Ardu? No!Initiation ≫ Le type char

Le type char

Dans cette partie d'initiation à l'Arduino, nous n'allons pas utiliser de matériel comme on le fait pour la plupart des projets. Usuellement les entrées d'informations pour la carte Arduino sont des poussoirs, des capteurs. N'en ayant pas pour la partie initiation, nous allons nous contenter d'utiliser le clavier de l'ordinateur quand la carte est connectée. Cela arrive très rarement pour un projet fini (sauf dans ce cas dialogue avec un autre dispositif série). C'est toutefois pratique en développement car cela permet par exemple de modifier un paramètre sans avoir à modifier le programme ou sans matériel de dialogue.

Le but avoué est aussi de découvrir et de comprendre un type de variable: le char. Cela vient de "character", caractère en français. char est un type entier un peu particulier.

Nous avons utilisé la voie série avec la console grâce à l'instruction Serial.print() ou Serial.println(). Pour envoyer des caractères sur la voie série (câble USB), il suffit simplement d'appeler les fonctions en donnant les bons paramètres. Pour lire un caractère (ou un octet), c'est un peu plus compliqué, car une lecture par Serial.read() nous donne bien le caractère à lire, mais si il n'y a pas de caractère reçu, la fonction retourne le nombre -1. Dans tous les cas elle retourne quelque chose. Pour éviter de lire un caractère quand il n'y en a pas, on utilise en principe la fonction Serial.available() qui retourne le nombre de caractères prêts à être lus. On pourrait aussi tester si la valeur lue est -1, mais cela se voit très peu.

Cela peut paraître un peu inhabituel d'avoir reçus de caractères et de les avoir mis en attente. En fait dès qu'un caractère arrive le processeur interrompt son travail, range le caractère, puis retourne à sa précédente tâche. Le processeur n'a pas les moyens de savoir ce que nous comptons faire de ce caractère car c'est nous qui allons utiliser ce renseignement dans le programme. Il ne peut que les ranger en attendant que nous venions les chercher.

Intéressons nous d'abord à cette fonction.

Serial.available()

Pour voir ce que retourne la fonction, faisons un programme qui ne fait que d'afficher ce que retourne la fonction:

void setup() {
  Serial.begin(115200); // Pour pouvoir utiliser la console
}

void loop() {
  Serial.println(Serial.available());
}

Quand on lance le programme, une suite de 0 s'affichent indiquant qu'il n'y a pas de caractères disponibles. Pour envoyer une lettre, il faut l'écrire dans la barre de saisie en haut (mettre le curseur dans la barre de saisie, et taper au clavier). Mettons par exemple la lettre A. Tant que l'on ne valide pas (touche "entrée") rien ne se passe. Après l'avoir envoyé, la fonction nous informe que deux caractères sont prêts à être lus. Le premier est la lettre "A", le deuxième est le caractère "fin de ligne".

Le choix du caractère de "fin de ligne" peut se choisir en bas de la console, à gauche du choix de la vitesse. Voici les choix possibles:
• "Pas de fin de ligne": aucun caractère n'est envoyé en plus; si on a écrit "A", on aura un seul caractère à lire
• "Nouvelle ligne": le caractère 10 est envoyé en fin de chaîne; si on a écrit "A", on aura deux caractère à lire
• "Retour chariot": le caractère 13 est envoyé en fin de chaîne; si on a écrit "A", on aura deux caractère à lire
• "Les deux, NL et CR": les caractères 10 et 13 sont envoyés en fin de chaîne; si on a écrit "A", on aura trois caractère à lire
Positionnons nous sur "Pas de fin de ligne". Le ou les caractères de fin de ligne ne sont pas envoyés, mais il faut quand même valider la saisie pour procéder à l'envoi.

Amusons-nous à envoyer plusieurs lettres. La fonction va nous informer qu'il y a bien plusieurs caractères en attente de lecture. Il y a une limite. Si nous envoyons trop de caractères, seuls les 63 peuvent être mis de côté, les suivants sont perdus.

Je vous demande aussi d'observer que si on envoie une lettre non accentuée majuscule ou minuscule ou bien un chiffre on a un caractère qui arrive. Il en est de même pour certains caractères spéciaux (;.?#*...) Quand on envoie par contre une lettre accentuée cela se traduit par deux places dans le tampon d'attente de lecture. Le plus curieux est le caractère € qui va utiliser trois emplacements (je crois qu'il est le seul à faire cela, mais je n'en suis pas sûr).

Serial.read()

Intéressons nous à ce que va retourner Serial.read(). Pour cela faisons un programme qui affiche le caractère reçu... si il y en a un qui est arrivé:

void setup() {
  Serial.begin(115200); // Pour pouvoir utiliser la console
}

void loop() {
  if (Serial.available()) // Si un caractère est arrivé
    Serial.println(Serial.read()); // On l'affiche
}

Envoyez la lettre A. Que se passe-t-il? C'est le nombre 65 qui s'affiche. On s'aperçoit ainsi que Serial.read() ne renvoie pas un caractère, mais un nombre. On aurait pu s'en douter car j'ai dit que cette fonction renvoyait -1 si aucun caractère n'était disponible, et -1 ne peut pas être un caractère. En regardant le code source, on peut voir que Serial.read() renvoie un int.

Profitons en pour relever quelques correspondances entre le caractère envoyé par la console et la valeur numérique affichée. Vérifiez:
A -> 65
B -> 66
C -> 67
a -> 97
b -> 98
c -> 99
é -> 195 + 169
è -> 195 + 168
€ -> 226 + 130 + 172

char

Pour pouvoir afficher notre nombre non pas sous forme numérique, mais sous forme normale, nous allons transtyper le nombre en caractère. Le type caractère est défini par le mot char. Par exemple pour définir un entier et un caractère, la définition peut être:

int nombreEntier;
char caractere;

Pour afficher le caractère à la place du nombre, on peut transtyper comme on l'avait fait en mettant le nom du nouveau type suivi de la valeur entre parenthèse. Le programme devient:

void setup() {
  Serial.begin(115200); // Pour pouvoir utiliser la console
}

void loop() {
  if (Serial.available()) // Si un caractère est arrivé
    Serial.println(char(Serial.read())); // On l'affiche
}

Envoyez les lettres A, B, C, a, b, c, quelques chiffres, des symboles. Cela doit fonctionner.

Envoyez maintenant la lettre é. Vous devez avoir afficher ? deux fois au lieu de notre lettre. Le caractère é étant défini par les deux nombres 195 et 169, on envoie à la console d'abord le nombre 195, puis le 169. Il semblerait que Serial.available() prends trop de temps et que la concole, impatiente, ne voyant pas encore venir le deuxième caractère imprime le premier seul puis ensuite le second.

ASCII

Revenons un peu sur le codage que nous avons observé:
A -> 65
B -> 66
C -> 67
a -> 97
b -> 98
c -> 99

65 n'est pas le caractère A. C'est un code numérique qui lui est associé. Effectivement dans une Arduino, on ne peur mettre dans les cases mémoire que des nombres. On va donc choisir pour chaque lettre un code. Au tout début de l'informatique, a été choisi un codage sur 7 bits soit avec 128 possibilités. Cela était suffisant pour représenter les 26 lettres minuscules, les 26 lettres majuscules, les 10 chiffres, 29 signes de ponctuation et divers, et quelques caractères de contrôle (en particulier 10 pour nouvelle ligne, 13 pour retour chariot). Bien entendu, comme cela se passait aux US, les accents étaient oubliés. Le code était appelé ASCII (American Standard Code for Information Interchange). En principe ce code est donné non pas avec des valeurs décimales, mais avec des valeurs hexadécimales:

0123456789A BCDEF
0xNULSOHSTXETXEOT ENQACKBELBSHTLFVTFF CRSOSI
1xDLEDC1 DC2DC3DC4NAKSYNETBCANEM SUBESCFSGSRSUS
2xespace!"#$%&' ()*+,-./
3x0123456789:; <=>?
4x@ABCDEFGHIJK LNO
5xPQRSTUVWXYZ[ \]^_
6x`abcdefghijk lmno
7xpqrstuvwxyz{ |}~ DEL

Cette table se lit ainsi: Pour avoir le code de A, à gauche de la ligne on a le premier chiffre 4x, le x se lit en haut sur la colonne, c'est 1. Le code est donc 4116 ce qui fait 65 en décimal (4*16+1*1).

La raison d'un code sur 7 bits était qu'il était utilisé pour des transmissions série, et que cela va plus vite de transmettre 7 bits que 8.

Bien entendu, cela a posé des problèmes pour les français qui ont des lettres accentuées. Par la suite le code ASCII a été étendu sur 8 bits pour avoir 128 caractères supplémentaires. Mais ce codage dépendait beaucoup du pays. Actuellement, on utilise de plus en plus le codage utf-8, notamment Arduino, qui est un standard mondial. 128 caractères tiennent sur un seul octet et sont ceux du code ascii. Les lettres accentuées sont codées sur 2 octets. Les caractères peuvent être codés sur 1, 2, 3 ou 4 octets, Bien sûr tous les codes de l'utf-8 ne sont pas repris car il y en a trop.

On trouvera sur internet beaucoup de sites expliquant le utf-8. Le site www.jchr.be nous permet un aperçu des caractères codés.

Pour souffler un peu

On a bien observé le codage des caractères. Amusons nous un peu. Écrivons un programme qui affiche les 26 lettres de l'alphabet.

On se rappellera que le type char est un type entier 8 bits signé. Sa vraie particularité est que cela affiche un caractère avec la fonction Serial.print() ou Serial.println(). On peut donc parfaitement l'utiliser pour faire des boucles for. On se rappellera que c'est la boucle à choisir si on connaît le nombre de boucles à l'avance. La boucle à faire est la suivante:

for (char c = 'A'; c <= 'Z'; c++) Serial.println(c);

Vous avez sans doute remarqué que pour affecter à c la valeur 65, j'ai utilisé 'A' qui veut dire "caractère A". C'est pareil, mais 'A' est plus lisible.

Écrivez donc le programme qui affiche les 26 majuscules. Comme on ne le fait qu'une fois, la ligne ci-dessus peut être mise dans setup, et du coup loop() sera vide. Aide?void setup() { Serial.begin(115200); // Pour pouvoir utiliser la console for (char c = 'A'; c <= 'Z'; c++) Serial.println(c); // L'alphabet } void loop() { }