Probleme de lecture Serial de Processing vers Arduino (pertes de données)

Bonjour !

je me fais un projet qui me tient a coeur, que beaucoup ont surement déjà fait mais ça m'entraine et c'est plutot amusant.

enfin bref j'ai des ecrans de type 16x02 et 20x04 (nombre de caractere x nombre de ligne) et je fais un programme sur processing qui affiche sur le PC l'equivalent de l'ecran 16x02, et sur lequel on peut "dessiner" dessus. Ensuite, en appuyant sur un bouton, ça envoi le "dessin" sur l'ecran 16x02/20x04.

j'en suis a ... allez.. 98 % ? x)

je pensais que mon code avait des soucis a plusieurs endroits car j'avais des resultats bizarre a l'affichage sur l'ecran, mais en realité, apres avoir regardé les valeurs qu'Arduino récupere de la liaison Série de Processing vers Arduino, j'ai tout de suite compris que le coeur du probleme etait la.

le fonctionnement de mon programme processing est simple :
on "dessine" sur les pixels et cela stocke dans un tableau a 2 dimensions si c'est un 1 ou un 0 pour tel ou tel pixel.

ensuite, quand on veut envoyer le dessin sur lecran, une boucle met toute ces valeurs dans un tableau de string sous la forme :

< 031 016 000 000 031 000 000 001 >

la communication série doit envoyer 32 string de ce genre (chacune de ces string represente un seul caractere de lecran, et chacune des valeur (031 par exemple) represente une "ligne de pixel" d'un caractere.

enfin bref, le calcul de ces valeurs se déroule parfaitement dans Processing, et j'affiche avant l'envoi le tableau entier que voici :

< 000 000 000 000 000 000 000 000 >
< 000 000 000 000 001 002 006 004 >
< 000 000 007 024 016 000 000 000 >
< 000 015 016 000 000 000 000 000 >
< 000 023 000 000 000 000 000 000 >
< 000 031 000 000 000 000 000 000 >
< 007 028 000 000 000 000 000 000 >
< 031 000 000 000 000 000 000 000 >
< 031 000 000 000 000 000 000 000 >
< 031 000 000 000 000 000 000 000 >
< 016 015 000 000 000 000 000 000 >
< 000 028 003 000 000 000 000 000 >
< 000 000 024 007 000 000 000 000 >
< 000 000 000 016 014 001 000 000 >
< 000 000 000 000 000 024 012 006 >
< 000 000 000 000 000 000 000 000 >
< 000 000 000 000 000 000 000 000 >
< 004 004 004 006 002 001 000 000 >
< 000 000 000 000 000 028 007 000 >
< 000 000 000 000 000 000 024 007 >
< 000 000 000 000 000 000 000 031 >
< 000 000 000 000 000 000 007 024 >
< 000 000 000 000 000 000 031 000 >
< 000 000 000 000 000 000 030 001 >
< 000 000 000 000 000 000 000 031 >
< 000 000 000 000 000 000 000 031 >
< 000 000 000 000 000 000 000 031 >
< 000 000 000 000 000 000 000 029 >
< 000 000 000 000 000 000 005 024 >
< 000 000 000 000 000 000 031 000 >
< 002 002 006 004 004 024 016 000 >
< 000 000 000 000 000 000 000 000 >

le code d'envoi Processing vers Arduino de ces valeurs est le suivant :

for(int g=0;g<caractere*ligne;g++) // caractere*ligne = 32 (pour 32 caracteres a envoyer)
    {
      //println(theStg[g]); // code donnant l'affichage precedant
      port.write(theStg[g]);
    }

avec cette ligne dans le setup :

port = new Serial(this, "COM5", 9600);

------------ Partie Arduino :

dans Arduino, la réception est gérée comme cela :

String chaine[32];
void loop() 
{
  
  if (Serial.available()) // si des données sont en attente
  {
    while (Serial.available()) //tant que des données sont en attente
    {
      for(int i=0;i<nbcar*ligne;i++) // recupere caractere par caractere l'affichage
      {
        for(int j=0;j<35;j++) // parcours toute la chaine du "<" j'usqu'au ">" de fin du caractere en cours
        {
          char c = Serial.read(); // l’Arduino lit le caractère
          chaine[i] += String(c); // et l’ajoute à l’objet reception
          delay(10); //petite attente (ces 3 lignes sont sugérées par Openclassroom)
        }
      }
    }
  }

je "televerse" le programme arduino avec l'arduino et l'ecran branché sur le port COM5, puis j'execute le code Processing, je fais mon petit dessin, et j'envoi le resultat vers l'ecran.

voici ce que ça fait (a gauche, ce qu'envoie Processing, et a droite, ce que recois Arduino)

la première ligne ainsi que la seconde, tout est OK, la structure de la string est identique, les données aussi... et puis viens la troisième string... tout part en sucette, et viens ensuite un ocean de ÿ venus d'on ne sait ou..

le problème est donc la transmission des données via le Serial, mais comment résoudre ça ?
j'ai cherché, et certains conseillent de deplacer le "While(Serial.available())" ou encore de mettre un "While(!Serial.available())" avant, mais ça ne résoud pas le probleme

j'ai essayé d'enlever le "delay(10)", ou de mettre d'autres valeurs dedans, ça change effectivement le resultat, mais pas en mieux..

j'ai essayé de mettre aussi un "delay(10)" du coté de processing, pareil, ça n'ameliore pas.
j'ai essayé de changer la valeur de "bauds", qui est a 9600 de base. ça ne change pas grand chose et ça n'ameliore rien.

j'ai essayé autrement, plutot que d'envoyer plusieurs string dans une boucle, j'ai fait une seule string qui contient toutes les valeurs, ce qui permet donc de faire un seul write depuis Processing et un seul read depuis arduino, en me disant que du coup, si il le passe, ca serait forcement en entier... Le probleme surviens aussi, la string deviens totalement alterée apres environ une dizaine de caracteres...

je suis perdu, c'est le seul probleme que j'ai, tout les reste fonctionne et je suis a 2 doigts de reussir. si vous avez une solution ou un conseil, je suis preneur ! merci à l'avance

ça part en sucette parce que vous lisez des données qui n'existent pas

  if (Serial.available()) // si [color=red]des[/color] données sont en attente[color=red]--> non UNE donnée[/color]
  {
    while (Serial.available()) //tant que [color=red]des[/color] données sont en attente[color=red]non UNE donnée[/color]
    {
      for(int i=0;i<nbcar*ligne;i++) // recupere caractere par caractere l'affichage
      {
        for(int j=0;j<35;j++) // parcours toute la chaine du "<" j'usqu'au ">" de fin du caractere en cours
        {
          char c = Serial.read(); // l'Arduino lit [color=red]le[/color] caractère 
// -->[color=red]NON IL LIT PLEIN DE DONNEES A CAUSE DES 2 BOUCLES FOR, MÊME SI ELLES NE SONT PAS LA[/color]
          chaine[i] += String(c); // et l'ajoute à l'objet reception

--> vous videz le buffer Série plus vite que les données n'arrivent. faut revoir toute cette partie du code . Vous ne pouvez faire un read() que si available() vous dit qu'il y a quelque chose de dispo.

étudiez Serial Input Basics

passez la communication à 115200 bauds

Ne mettez pas de delay()

(et ne pas utiliser de String --> lisez les fonctions C classiques stdlib.h et string.h)

Merci pour votre reponse

je dois bien avouer ne pas etre tres habile avec cette partie "communication", j'ai un arduino depuis seulement 2 semaines et ce genre de "lisaison série" est assez floue pour moi

je me suis basé sur un tuto openclassroom, et la partie qui lis les char, pour les inclure dans une string, le tout dans un for, fais justement partie de ce tuto, je pensais donc que je pouvais faire pareil.. d'ailleurs, les commentaires sur ce que vous commentez sont d'eux ^^

mais ce que je ne comprend pas trop, c'est que, meme si Serial.available ne permet de savoir que si "UNE" donnée est dispo, dans ce cas, mettre le "While(Serial.Available())" juste avant le "char c=Serial.Read();" devrais suffire non ? car dans ce cas, on serait deja dans la boucle, et on ne lirais rien tant qu'il n'y a pas de données disponible ?

ensuite, je ne vois pas trop comment adopter une vitesse de lecture adaptée par rapport a la vitesse a laquelle les données arrivent, pourriez vous me donner une voie pour "revoir toute cette partie du code" ?

merci

Voilà un petit exemple
Réglez votre console série correctement (1) et (2)

puis (3) entrez un texte

le code est relativement simple, dans la boucle on demande de vérifier si on a reçu une "phrase" entière. On définit une phrase comme étant une ligne terminée par un caractère particulier, ici j'attends un '\n' que la console envoie automatiquement si vous le lui dites. tant que je n'ai pas reçu ce caractère j'ajoute les caractères reçus dans un tableau de caractère que je termine par un caractère nul '\0' qui dénote en langage C la fin d'une chaîne de caractères (nécessaire pour utiliser les fonctions traitant de chaînes, comme print par exemple).

Une fois que j'ai reçu le caractère de fin, ma fonction recevoirMessage() répond "vrai" et j'affiche le message puis remets mon buffer à l'état initial et je suis prêt à recommencer.

En procédant comme cela vous êtes totalement indépendant de la vitesse d'arrivée des caractères, la chaîne se construit au fur et à mesure de l'arrivée des caractères.

OK ?

const byte tailleMax = 50;
char messageRecu[tailleMax + 1]; // +1 pour stocker un '\0' à la fin de la chaîne
byte index;

boolean recevoirMessage(const char finDeChaine)
{
  boolean fin = false;

  if (Serial.available()) { // si on a au moins un charactère en attente
    int recu = Serial.read(); // -1 si erreur, sinon notre charactère
    if (recu != -1) {
      if (recu == finDeChaine) fin = true;
      else {
        if (recu != '\r') { // on ignore CR
          messageRecu[index++] = (char) recu; // on stocke le caractère et passe à l'Index suivant
          messageRecu[index] = '\0'; // on marque la fin de chaîne
          if (index >= tailleMax) index = tailleMax - 1; // évite de déborder
        }
      }
    }
  }
  return fin;
}

void setup() {
  Serial.begin(115200);
  // pas nécessaire car le compilateur le fait pour nous avec des variables globales
  // juste pour vous rappeller qu'il est bon de définir les bonnes conditions initiales
  index = 0;
  messageRecu[0] = '\0';
}

void loop() {
  if (recevoirMessage('\n')) { // on lit jusqu'à recevoir un caractère de fin de message (ce que la console appelle NL)
    Serial.print("J'ai recu [");
    Serial.print(messageRecu);
    Serial.println("]");
    // On re-initalise notre buffer
    index = 0;
    messageRecu[0] = '\0';
  }
}

sinon vous dites:

Fabriziooo:
< 031 016 000 000 031 000 000 001 >

Pourquoi le 0 en début de nombre? quand on met un zéro devant généralement on veut dire que c'est une représentation en base 8 (octal). est-ce le cas?

J-M-L:
sinon vous dites:
Pourquoi le 0 en début de nombre? quand on met un zéro devant généralement on veut dire que c'est une représentation en base 8 (octal). est-ce le cas?

je me posais la meme question, et je viens d'en trouver la cause.

dans mon code pour passer de mon tableau de 256 entiers (tout les characteres, avec les valeurs constituant les 8 lignes de chacun des 32 caractere), a un tableau de char de 32 valeur, constitué comme ça :

< 031 000 000 000 000 000 000 000 >

eh bien j'avais utilisé " nf(montab[val],3) "... j'avais trouvé ça sur internet, sans vraiment en comprendre la logique. j'ai mis 2 a la place de 3, ça prendra moins de place, meme si ça reviens quasiment au meme

je vais essayer le code que vous m'avez donné pour tester, ainsi que des exemples du 1er lien, merci encore

je rencontre un nouveau problème que je ne comprend pas :

j'ai utilisé une des méthodes proposée dans votre 1er lien, et effectivement, il recupere bien toutes les données (en tout cas j'en ai l'impression).

cependant, le code de cette personne se découpe en 2 fonctions. Une qui recupere les données, l'autre qui affiche.

j'ai un int que j’incrémente a chaque fois que j'affiche ce qui est lu, et j'utilisais Serial.println() afin de voir dans la console si j'atteignais bien 255.

seulement, lorsque je fais cela, j'atteins environ 230 et quelques... et si j'ajoute une opération en dessous, a savoir affecter la valeur trouvée dans un tableau, et que je l'affiche un espace après mon compteur, alors ce compteur n'arrive plus qu'a 170 !

et la, je viens d'essayer d'afficher ce compteur seul, sans sauter de ligne (Serial.print), et la, magie, on atteint bien 255 !

est-ce parce que le code de cette personne lis les données dans une fonction qui est appelée depuis le Loop, et que l'affichage est lui aussi appelé depuis le Loop, et donc qu'il peut y avoir un conflit entre l'écriture et la lecture ?

EDIT : c'etait bien ça, j'ai enlevé les print, et j'ai utilisé sa fonction uniquement pour affecter dans un tableau les valeurs trouvées, comme ça pas de conflit, et j'arrive bien a lire, une fois que la recuperation Serial est finie, toutes les données dans un tableau d'entier. Je vais essayer de faire marcher le tout desormais !

C'est mieux de poster le code dont vous parlez quand vous voulez de l'aide...