[Résolu] Afficher "é" sur la console

Bonjour,

Voici mon programme de test:

void setup()
{
  Serial.begin(115200);  // Pour pouvoir utiliser la console
  Serial.print(char(65)); // Affiche "A"
  Serial.print(char(195)); Serial.print(char(169)); // Affiche "é"
}

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

Je travaille avec une Uno, ma console est à 115200 bauds, j'utilise l'IDE 2.0.3 avec une console "Pas de fin de ligne".

Comme attendu, le setup() fait bien un affichage "Aé". Mon problème est que que le loop() ne fonctionne pas avec les caractères à deux ou trois octets. Si j'envoie "A", j'ai bien "A" qui s'affiche, mais pour "é" c'est "??"qui apparaît.

J'ai l'impression que les deux caractères partent bien mais que que le Serial.available() prend trop de temps et que la console est pressée, du coup elle affiche avent d'avoir reçu le second octet.

Si dans le setup') je mets:

Serial.print(char(195)); delayMicroseconds(1360); Serial.print(char(169)); // Affiche "é"

J'ai bien le "é" qui est affiché, mais si je mets:

Serial.print(char(195)); delayMicroseconds(1380); Serial.print(char(169)); // Affiche "é"

j'ai un double "?"
Pour 1370µs, j'ai de temps en temps "é", et de temps en temps "??"

Mon analyse est-elle juste? Y a-t-il un moyen simple de faire

  if (Serial.available())               // Si un caractère est arrivé
    Serial.print(char(Serial.read()));  // On l'affiche

mais qui fonctionne?

Cela fonctionnait bien avec l'IDE 1.truc.chose...

Il faudrait peut être afficher aussi les caractères en hexadécimal, pour voir si il anticipe les caractères et met par exemple deux caractères de valeur 0?

l'IDE utilise l'UTF8 qui utilise un nombre variable d'octets pour représenter un symbole. Certains sont sur 1 octet et dans ce cas un write avec le code fonctionne mais d'autres seront sur plusieurs octets.

par exemple ce code

void setup() {
  const char utf8[] = "€";
  Serial.begin(115200);
  Serial.println(strlen(utf8));

  for (byte i = 0; i < strlen(utf8); i++) {
    Serial.print("0x"); Serial.print((byte) utf8[i], HEX); Serial.write(' ');
  }
  Serial.println();
}

void loop() {}

où apparement votre chaîne de caractère ne contient qu'un seul symbole va afficher 3 car il faut 3 octets pour représenter le symbole € en UTF8 et vous verrez que ces octets sont 0xE2 0x82 0xAC

plus d'infos sur UTF-8 — Wikipédia

Description technique

Techniquement, il s'agit de coder les caractères Unicode sous forme de séquences de un à quatre codets d'un octet chacun. La norme Unicode définit entre autres un ensemble (ou répertoire) de caractères. Chaque caractère est repéré dans cet ensemble par un index entier aussi appelé « point de code ». Par exemple le caractère « € » (euro) est le 8365e caractère du répertoire Unicode, son index, ou point de code, est donc 8364 (0x20AC) (on commence à compter à partir de 0).

Le répertoire Unicode peut contenir plus d'un million de caractères, ce qui est bien trop grand pour être codé par un seul octet(limité à des valeurs entre 0 et 255). La norme Unicode définit donc des méthodes standardisées pour coder et stocker cet index sous forme de séquence d'octets : UTF-8 est l'une d'entre elles, avec UTF-16, UTF-32 et leurs différentes variantes.

La principale caractéristique d'UTF-8 est qu'elle est rétro-compatible avec le standard ASCII, c'est-à-dire que tout caractère ASCII se code en UTF-8 sous forme d'un unique octet, identique au code ASCII. Par exemple « A » (A majuscule) a pour code ASCII 65 (0x41) et se code en UTF-8 par l'octet 65. Chaque caractère dont le point de code est supérieur à 127 (0x7F) (caractère non ASCII) se code sur 2 à 4 octets. Le caractère « € » (euro) se code par exemple sur 3 octets : 226, 130, et 172 (0xE2, 0x82 et 0xAC).

le problème de la console est effectivement que s'il y a trop de temps entre deux données elle peut merder. J'avais vu cela dans les bugs listés faudrait retrouver

EDIT: cf Fix corruption of incoming UTF-8-encoded serial data by palazzol · Pull Request #1758 · arduino/arduino-ide · GitHub et Serial Monitor periodically corrupts post-ASCII output · Issue #589 · arduino/arduino-ide · GitHub

si vous voulez traiter cela dans votre code et que vous savez que vous ne recevez pas de binaire sur la voie série alors ce n'est pas très compliqué en lisant un octet de savoir si c'est le début d'un code UTF8 à 1, 2 3 ou 4 octets (la norme définit cela) et dans ce cas il faut bufferiser.

Son problème n'est pas vraiment le codage utf8, mais pourquoi de temps en temps l'affichage interprète le même caractère accentué correctement, des fois non.

C'est une conséquence du codage UTF8 et du nombre variable d'octets et une limitation du moniteur série

ma réponse était pour sa question

où on n'imprimerait pas tout ce suite, on collecterait tous les octets nécessaire à former un caractère UTF8 complet avant de l'envoyer d'un coup, sans pause entre les deux, vers le terminal

Pour fabriquer ces octets il faut comprendre comment on sait si dans le flux qui arrive on aura besoin de 1, 2, 3 ou 4 octets pour le caractère. donc il faut comprendre le codage utf8

Comme tu étais partis sur l'explication du codage et qu'il semblait déjà connaître cela, j'avoue que je n'ai pas tout lu.
Je peux supprimer mon message ?

pas la peine, votre question était pertinente pour mon post 3

donc en gros je testerais ce code

byte utf8[4];
byte n = 0;
byte attendu = 1;

void setup() {
  Serial.begin(115200);
  Serial.println("tapez UTF8");
}

void loop() {
  if (Serial.available()) {
    utf8[n] = Serial.read();
    if (n == 0) {
      if ((utf8[n] & 0b1000000) == 0) attendu = 1;                 // 0xxxxxxx
      else if ((utf8[n] & 0b11100000) == 0b11000000) attendu = 2;  // 110xxxxx
      else if ((utf8[n] & 0b11110000) == 0b11100000) attendu = 3;  // 1110xxxx
      else if ((utf8[n] & 0b11111000) == 0b11110000) attendu = 4;  // 11110xxx
      else attendu = 1;
    }
    if (++n == attendu) {
      Serial.print(attendu);
      Serial.print(" octet(s) =>"); Serial.write(utf8, attendu); Serial.println();
      n = 0;
    }
  }
}

(on peut mieux faire pour calculer le nombre d'octets attendus mais c'est pour que ce soit clair)

1 Like

Ok, encore un truc que je n'aime pas dans la version 2

Je crois avoir vu que le seul caractère sur 3 octets que l'on peut afficher avec la console c'est €, mais je n'en ai pas vu sur 4 octets.

Il me reste qu'à laisser tomber en attendant qu'ils allongent le temps . Sinon pour ne pas bloquer en cas de chaine tronquée de mémoriser tant que Serial.available() retourne true et de n'afficher que si cela retourne false. c'est peut être plus simple que d'analyser les octets.

Merci en tout cas. Je pensais avoir fait une erreur comme d'hab.

testez le bout de code que j'ai mis plus haut, ça devrait marcher
il y a plein de 3 octets qui fonctionnent essayez de copier coller cela

Aç€ắẮẫằἍ⁇⁈⁉⃔⃕↖↗↘↙∭◐◑♻⭅⭆⭂⭑⭤⭥⮑

vous aurez 1 octet pour le A, deux octets pour le ç et 3 octets pour les autres
effectivement les 4 octets ne sont pas implémentés dans l'IDE

EDIT: j'ai testé ça marche mieux sur l'ancien que le nouvel éditeur

c'est marrant de voir que je ne peux pas taper certains caractère dans la ligne d'entrée (un copier coller) mais qu'ils sont ensuite correctement affichés dans le moniteur

je suis d'accord avec vous que le nouveau terminal série de l'IDE 2.0 est à la ramasse...

En gros j'ai les mêmes, mais il y a des différences:

Et deux octets inconnus (deux points d'interrogation) pour trois octets. et le dernier caractère de la liste ne s'affiche pas chez moi.

En fait le € peut être intéressant les autres à trois octets nettement moins. Surtout si cela dépend de la version!

Et 3 octets pour afficher ⁇ alors que je peux en 2 octets afficher deux fois ? c'est un peu du luxe!

Mais vous aurez deux symboles au lieu d’un :wink:

Une bonne nouvelle apparemment

Fix corruption of multi-byte UTF-8 symbols in Serial Monitor

arduino/arduino-ide#1758, arduino/arduino-ide#589

Multi-byte characters (e.g., °) received by Serial Monitor were sometimes corrupted (appearing as ).

1 Like

Effectivement cela fonctionne avec la version 2.0.4

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.