Programme de lecture de données Serie

Bonjour et merci d'avance pour votre aide,

C'est la première fois que je me lance dans un projet où des données sont envoyées d'un pc pour piloter un Arduino Uno. Je me suis donc penché sur les tuto et sujets qui parlent de ce genre de communication. Plus précisement j'essaye de m'inspirer de ce topic (surtout l'exemple 5). Mais je dois reconnaitre que ce genre de programme est plus compliqué que ce que j'ai l'habitude de faire.

Donc avant de passer du temps à essayer de comprendre chaque étape de ce programme je voudrais m'assurer qu'il n'y a pas plus simple. Dans mon projet, le pc doit envoyer des données (un entier entre 0 et 100) sur 3 variables différentes.

Je pensais faire en sorte que ces données aient le format suivant : <NomVariable+Entier>. L'Arduino n'aurait qu'à lire les 2 premières lettres pour connaitre le nom de la variable et ensuite stocker l'entier dans cette variable.

Est-ce que vous confirmez que le code ci-dessous est la façon la plus simple/efficace de s'y prendre ? (parce que ça va me demander un bon moment pour comprendre tous les morceaux de ce programme)

Si vous avez des exemples plus simples je suis preneur

Code de l'exemple 5 :

// Example 5 - Receive with start- and end-markers combined with parsing

const byte numChars = 32;
char receivedChars[numChars];
char tempChars[numChars];        // temporary array for use when parsing

      // variables to hold the parsed data
char messageFromPC[numChars] = {0};
int integerFromPC = 0;
float floatFromPC = 0.0;

boolean newData = false;

//============

void setup() {
    Serial.begin(9600);
    Serial.println("This demo expects 3 pieces of data - text, an integer and a floating point value");
    Serial.println("Enter data in this style <HelloWorld, 12, 24.7>  ");
    Serial.println();
}

//============

void loop() {
    recvWithStartEndMarkers();
    if (newData == true) {
        strcpy(tempChars, receivedChars);
            // this temporary copy is necessary to protect the original data
            //   because strtok() used in parseData() replaces the commas with \0
        parseData();
        showParsedData();
        newData = false;
    }
}

//============

void recvWithStartEndMarkers() {
    static boolean recvInProgress = false;
    static byte ndx = 0;
    char startMarker = '<';
    char endMarker = '>';
    char rc;

    while (Serial.available() > 0 && newData == false) {
        rc = Serial.read();

        if (recvInProgress == true) {
            if (rc != endMarker) {
                receivedChars[ndx] = rc;
                ndx++;
                if (ndx >= numChars) {
                    ndx = numChars - 1;
                }
            }
            else {
                receivedChars[ndx] = '\0'; // terminate the string
                recvInProgress = false;
                ndx = 0;
                newData = true;
            }
        }

        else if (rc == startMarker) {
            recvInProgress = true;
        }
    }
}

//============

void parseData() {      // split the data into its parts

    char * strtokIndx; // this is used by strtok() as an index

    strtokIndx = strtok(tempChars,",");      // get the first part - the string
    strcpy(messageFromPC, strtokIndx); // copy it to messageFromPC
 
    strtokIndx = strtok(NULL, ","); // this continues where the previous call left off
    integerFromPC = atoi(strtokIndx);     // convert this part to an integer

    strtokIndx = strtok(NULL, ",");
    floatFromPC = atof(strtokIndx);     // convert this part to a float

}

//============

void showParsedData() {
    Serial.print("Message ");
    Serial.println(messageFromPC);
    Serial.print("Integer ");
    Serial.println(integerFromPC);
    Serial.print("Float ");
    Serial.println(floatFromPC);
}

le post auquel vous faites référence est une bon source. ça peut sembler compliqué mais c'est important de bien comprendre comment traiter cela proprement

j'ai un petit tuto en français que vous pouvez lire qui pose les bases, peut-être que cela vous aidera ?

Je ne sais pas si c'est le plus simple, mais ça va marcher c'est sûr (même remarque que J-M-L).

Dans ton cas, si tu n'as qu'un nom de variable et un 'int', tu peux le simplifier puisque tu n'as pas besoin de lire le float. Cependant, pense à utiliser '<' au début de ton message, '>' à la fin et ',' pour séparer le nom et la valeur (pas le '+') :

<NomVariable, Entier>

Il semble que strtok puisse utiliser plusieurs séparateurs : donc on pourrait avoir :
    strtokIndx = strtok(tempChars,",+.-<> ");et utiliser des séparateurs comme ' ', '+', '-', ',', etc.

Quelque chose ici aussi sur le forum en français

fdufnews:
Quelque chose ici aussi sur le forum en français

L'archive (sous windows) semble corrompue.

Sur le premier post il y a 2 liens :

Le premier est en effet corrompu (sous GNU/Linux aussi). Le second (pièce attachée) fonctionne (sous GNU/Linux) même si les accents dans les noms de fichiers sont "spéciaux". Par contre je ne sais pas si c'est la dernière version de l'archive.

En effet, merci de la précision.

Ok donc après lecture et tentative de compréhension des sources proposées (merci les gars ^^) j'ai décidé de partir de l'exemple que je mentionnais dans mon premier message. Mais j'aimerais être sûr que je comprends bien la logique des blocs. Donc corrigez moi si je me trompe.

Dans le bloc ci-dessous,

void recvWithStartEndMarkers() {
    static boolean recvInProgress = false;
    static byte ndx = 0;
    char startMarker = '<';
    char endMarker = '>';
    char rc;

    while (Serial.available() > 0 && newData == false) {
        rc = Serial.read();

        if (recvInProgress == true) {
            if (rc != endMarker) {
                receivedChars[ndx] = rc;
                ndx++;
                if (ndx >= numChars) {
                    ndx = numChars - 1;
                }
            }
            else {
                receivedChars[ndx] = '\0'; // terminate the string
                recvInProgress = false;
                ndx = 0;
                newData = true;
            }
        }

        else if (rc == startMarker) {
            recvInProgress = true;
        }
    }
}

la logique serait :

  • Si le caractère reçu est "<", recvInProgress devient vrai. S'il est vrai et que le caractère suivant reçu n'est pas ">", chaque caractère reçu est stocké l'un après l'autre dans receivedChars (index [ndx]incrémenté de 1 à chaque fois).
  • Sinon, le caractère reçu est ">" et la dernière valeur stockée est "\0" qui indique la fin d'une ligne, recvInProgress redevient faux jusqu'à ce que le caractère reçu soit de nouveau "<" et l'index est remis à 0

Ca vous parait juste comme description ? J'ai encore un peu de mal à comprendre le rôle du
if (ndx >= numChars) {
ndx = numChars - 1;

De ce que je comprends, le numChars étant fixé à 32, si l'index dépasse ou égalise 32, l'index vaudra 32-1 ? A quoi cela sert-il ?

ça sert à laisser la place pour mettre le \0 qui indique la fin de la chaîne de caractères. C'est fait pour éviter un dépassement de tableau (la chaîne est un tableau de numChars caractères) donc une suite de numChars cases mémoire consécutives utilisées pour stocker ton message. Un buffer. Si on écrit un caractères au-delà de cet espace réservé, on risque des erreurs d'exécution.