Go Down

Topic: String? Pas bien. Char[]? bien! (Read 654 times) previous topic - next topic

savoriano

Aug 08, 2019, 12:00 am Last Edit: Aug 18, 2019, 01:21 pm by savoriano
Je toujours utilisé pour mes programme l'objet String car je le trouve puissant.
Depuis que je me suis mis à l'arduino, je n'arrête pas d'entendre que il faut éviter d'utiliser String et privilégier char[].
OK, message reçu!
Mon code:
Code: [Select]

String sTrama;

void CapteursPresion()
{
  double  c;
  char cDato[6];
  String sHex;
  byte PosX = 1;
  //sTrama = "";
  while (Serial2.available() > 0)//il y a quelque chose que le BT veut nous transmettre?
  {
    //lit le BT
    sHex = String(Serial2.read(), HEX);//Attention: il ne met pas les 0 non significatifs
    if (sHex.length() == 1) sHex = "0" + sHex;//ajoute les 0 non significatifs
    sTrama += sHex; //Crée la trame

    if (sTrama == OkDisa1 || sTrama == OkDisa2 || sTrama == OkDisa3) sTrama = ""; // efface les "OK+DISA*"

    //Serial.println(sTrama);
    if (sTrama.endsWith("0d0a")) //fin de la trame
    {
      //PrintTFT(42, 182, "Od0a", 2);
      //Serial.println(sTrama);
      // Analise la trame
      if (sTrama.length() == 76)//le capteur transmet 2 differentes trames
      { //seulement la plus longue nous interesse
        if (sTrama.startsWith(MACCaptPressAv)) PosX = 140;//La trame vient elle du capteur avant?
        else if (sTrama.startsWith(MACCaptPressAr)) PosX = 0;//La trame vient elle du capteur arrière?
        else
        {
          sTrama = "";
          return;//on sort car la trame ne provient pas de nos capteurs
        }
        //PrintTFT(42, 182, String(PosX), 2);
        cDato[0] = sTrama.charAt(56);// Preds les char de la pression
        cDato[1] = sTrama.charAt(57);
        cDato[2] = sTrama.charAt(54);
        cDato[3] = sTrama.charAt(55);
        cDato[4] = sTrama.charAt(52);
        cDato[5] = sTrama.charAt(53);
        c = strtoul(cDato, 0, 16);//Converte HEX to DEC.

        if (c > 400000UL)//des fois il trouve de pressions incoerrentes!
        {
          sTrama = "";
          return;
        }

        if (c < 190000UL && tft.readPixel(PosX + 10 , 259) != RED)//pression insuffisante tft.readPixel(posxa + 6 , posya)
        { // PneuAV sert pour eviter qu'il colorie le pneu à chaque fois
          for (byte a = 41; a < 60; a++) tft.drawCircle(PosX + 60, 259, a, RED);//Colora le gomme in ROSSO
        }
        else if (c > 250001UL && tft.readPixel(PosX + 10 , 259) != BLUE)// Pression elevée
        {
          for (byte a = 41; a < 60; a++) tft.drawCircle(PosX + 60, 259, a, BLUE);//Colora le gomme in BLU
        }
        else if (c < 250000UL && c > 189000UL && tft.readPixel(PosX + 10 , 259) != GREEN) //pression optimale
        {
          for (byte a = 41; a < 60; a++) tft.drawCircle(PosX + 60, 259, a, GREEN);//Colora le gomme in VERDE
        }

        PrintTFT(42 + PosX, 180, String(float(c) / 100000.00), 2);//Ecrit la pression

        cDato[0] = sTrama.charAt(64);// Preds les char de la Temperature
        cDato[1] = sTrama.charAt(65);
        cDato[2] = sTrama.charAt(62);
        cDato[3] = sTrama.charAt(63);
        cDato[4] = sTrama.charAt(60);
        cDato[5] = sTrama.charAt(61);
        c = strtoul(cDato, 0, 16);//Converte HEX to DEC
        PrintTFT(112 + PosX, 300, String(int(c / 100)) + " ", 2);// Ecrit sur le TFT la Temperature
      }
      sTrama = "";//efface la trame
    }
  }

 
J'ai commencé avec la première partie:
Code: [Select]
 while (Serial2.available() > 0)//il y a quelque chose que le BT veut nous transmettre?
  {
    //lit le BT
    sHex = String(Serial2.read(), HEX);//Attention: il ne met pas les 0 non significatifs
    LenTrama = strlen(sTrama);
    if (strlen(sHex) == 1) //Ajoute le 0 non significatif
    {
      sTrama[LenTrama] = '0';
      sTrama[LenTrama +1] = sHex[0];
      sTrama[LenTrama +2] = '\0';
    }else
    {
      sTrama[LenTrama] = sHex[0];
      sTrama[LenTrama +1] = sHex[1];
      sTrama[LenTrama +2] = '\0';
    }

Je n'ai pas l'habitude d'utiliser char c'est pour cette raison que je vous demande si c'est bien ou pas.
Pardonnez moi pour mon français, ce n'ai pas ma langue maternelle.

J-M-L

Hello - Please do not PM me for help,  others will benefit as well if you post your question publicly on the forums.
Bonjour Pas de messages privés SVP, postez dans le forum directement pour que ça profite à tous

savoriano

#2
Aug 08, 2019, 08:45 am Last Edit: Aug 08, 2019, 09:08 am by savoriano
Trama
Message 18, j'ai juste supprimer les espaces entre chaque byte.
Pardonnez moi pour mon français, ce n'ai pas ma langue maternelle.

lesept

L'un des puissants outils à disposition en C++ pour créer des tableaux de caractères avec un format bien précis - disons créer des trames - c'est sprintf.

Tu crées un tableau vide, de longueur suffisante pour toutes les trames possibles. Disons que la longueur maximale d'une trame sera de 50 caractères, plus le \0 à la fin (du tableau) si nécessaire

Code: [Select]
char pastis[51];
Ensuite si tu veux y mettre des valeurs (séparées par des espaces) et un entête :
Code: [Select]
char entete[] = "AT+DISA: MAC(";
int val1, val2, val3;
sprintf (pastis,"%s%d %d %d\0",entete,val1,val2,val3);

Il faut connaitre les codes utilisés pour le format :
  • %s pour un tableau de caractères,
  • %d pour un entier,
  • %c pour un char unique,
  • %u pour un unsigned int,
  • %X pour un hexadécimal en majuscules...
On peut aussi ajouter des caractères spéciaux :
  • \n (newline)
  • \t (tab)
  • ...
Ensuite pour jouer avec plusieurs tableaux de caractères, il y a des fonctions sympas:
  • strcat : pour concaténer deux chaines
  • srtcpy : pour copier une chaîne dans une autre
  • strlen : pour connaitre la longueur d'une chaîne
Il existe aussi des fonctions pour convertir des chiffres en chaines de caractères et vice-versa : itoa, atoi, etc. Mais je pense que les fonctions précédentes sont suffisantes pour créer une trame.
A force d'essayer on finit par réussir... Donc, plus ça rate, plus on a de chances que ça marche (proverbe Sharduinok).

dbrion06

sprintf n'est pas spécifique à C++, mais est récupéré de C; awk utilise aussi, si besoin, sprintf , avec les mêmes règles de formatage que C (bien pratique, pour tester: là, j'ai verifié les tabulations, le newline et le format hexa)
Code: [Select]

echo "salut" |  awk '{a=sprintf ( "%s\tJupyter\tle\tHomardeux\n hex(85)=%x", $1,85); print a;}'
salut   Jupyter le      Homardeux
 hex(85)=55


savoriano

Code: [Select]
   sprintf(sHex,"%02x\0",Serial2.read());
    strcat(sTrama, sHex);

Ca peut fonctionner?
Pardonnez moi pour mon français, ce n'ai pas ma langue maternelle.

dbrion06

#6
Aug 08, 2019, 11:22 am Last Edit: Aug 08, 2019, 11:38 am by dbrion06
Oui:
j'ai testé, n'ayant pas d'arduino sous la main, avec mon PC (c'est plus facile: on n'a pas à s'ennuyer avec tout plein de prises...)
Code: [Select]

 gcc strcattest.c && ./a.exe
Trame:55
sh-4.1$ gcc strcattest.c && ./a.exe && cat strcattest.c
Trame:55

   #include <stdio.h> // arduino n'a pas besoin de ces 3 includes (les fournit d'office, IIRC)
   #include <stdint.h>
   #include <string.h>
            // char *strcat(char *DST, const char *SRC);
int main() {
   char sTrama[51] ="Trame:"; // dimensionné depuis post 3
   char sHex[11];
   // sert à dimensionner le resulatat d'une conversion : on pourra  convertir dexs int32_t confortablement (8 cars +\0)
  uint8_t serialRead='U'; // mon pc ne connaôt pas Serial2.read
   sprintf(sHex, "%02x\n\0", serialRead);
   strcat(sTrama, sHex);
   printf(sTrama);
   return 0;// nombre d'erreurs
}


Edité: ce code peut être un peu plus sûr (protège de debordements du tableau sTrama si, à la place de   strcat(sTrama, sHex); on mettait    strncat(sTrama, sHex,50);
(mais si debordement il y a, on n'en informe pas dans cette version naîve l"auteur).
En changeant l'invocation de gcc "gcc --pedantic -Wall", on peut voir quelques petits messages d'averissement sans conséquences.... (ceci ne peut être fait sur arduino....)

savoriano

Code: [Select]
char sTrama[100];
#define MACCaptPressAv "510110ca"
#define MACCaptPressAr "aa0340ca"
#define OkDisa1 "4f4b2b444953413a"
#define OkDisa2 "4f4b2b4449534153"
#define OkDisa3 "4f4b2b4449534345"

void CapteursPression()
{
  uint32_t  c;
  char cDato[6];
  char sHex[3];
  byte PosX = 1;
  int LenTrama;
  //sTrama = "";
  while (Serial2.available() > 0)//il y a quelque chose que le BT veut nous trasmettre?
  {
    //lit le BT
    sprintf(sHex, "%02x\0", Serial2.read());
    strcat(sTrama, sHex);

    if (strncmp(sTrama, OkDisa1, 16) == 0 || strncmp(sTrama, OkDisa2, 16) == 0 || strncmp(sTrama, OkDisa3, 16) == 0) sTrama[0] = '\0'; // efface les "OK+DISA*"

      //Serial.println(sTrama);
      char * pch;
      pch = strstr(sTrama, "0d0a");

      if (pch != NULL) //fin de la trame
      {
        //PrintTFT(42, 182, "Od0a", 2);
        //Serial.println(sTrama);
        // Analise la trame
        if (strlen(sTrama) == 76)//le capteur transmet 2 differentes trames
        { //seulement la plus longue nous interesse
          if (strncmp(sTrama, MACCaptPressAv, 8) == 0) PosX = 140;//La trame vient elle du capteur avant?
          else if (strncmp(sTrama, MACCaptPressAr, 8) == 0) PosX = 0;//La trame vient elle du capteur ariere?
          else
          {
            sTrama[0] = '\0';
            return;//on sort car la trame ne provient pas de nos capteurs
          }
          //PrintTFT(42, 182, String(PosX), 2);
          cDato[0] = sTrama[56];// Preds les char de la pression
          cDato[1] = sTrama[57];
          cDato[2] = sTrama[54];
          cDato[3] = sTrama[55];
          cDato[4] = sTrama[52];
          cDato[5] = sTrama[53];
          cDato[6] = '\0';
         c = strtoul(cDato, 0, 16);//Converte HEX to DEC.

          if (c > 400000UL)//des fois il trouve de pressions incoerrentes!
          {
            sTrama[0] = '\0';
            return;
          }

          if (c < 190000UL && tft.readPixel(PosX + 10 , 259) != RED)//pression insuffisante tft.readPixel(posxa + 6 , posya)
          { // PneuAV sert pour eviter qu'il colorie le pneu à chaque fois
            for (byte a = 41; a < 60; a++) tft.drawCircle(PosX + 60, 259, a, RED);//Colora le gomme in ROSSO
          }
          else if (c > 250001UL && tft.readPixel(PosX + 10 , 259) != BLUE)// Pression elevée
          {
            for (byte a = 41; a < 60; a++) tft.drawCircle(PosX + 60, 259, a, BLUE);//Colora le gomme in BLU
          }
          else if (c < 250000UL && c > 189000UL && tft.readPixel(PosX + 10 , 259) != GREEN) //pression optimale
          {
            for (byte a = 41; a < 60; a++) tft.drawCircle(PosX + 60, 259, a, GREEN);//Colora le gomme in VERDE
          }

          PrintTFT(42 + PosX, 180, String(float(c) / 100000.00), 2);//Ecrit la pression

          cDato[0] = sTrama[64];// Preds les char de la Temperature
          cDato[1] = sTrama[65];
          cDato[2] = sTrama[62];
          cDato[3] = sTrama[63];
          cDato[4] = sTrama[60];
          cDato[5] = sTrama[61];
          cDato[6] = '\0';
          c = strtoul(cDato, 0, 16);//Converte HEX to DEC
          PrintTFT(112 + PosX, 300, String(int(c / 100)) + " ", 2);// Ecrit sur le TFT la Temperature
        }
        sTrama[0] = '\0';//efface la trame
      }
  }

  if (TimerBT + 3000 < millis()) //relance tous les 3 sec la commande "AT+DISA?"
  {
    Serial2.write("AT+DISA?");
    TimerBT = millis();
  }
}

Je ne l'ai pas testé, juste compilé.
A ce rythme d'ici à 30/40 ans je vais devenir aussi fort que vous! :D
Pardonnez moi pour mon français, ce n'ai pas ma langue maternelle.

dbrion06

Excusez moi, mais je n'ai pas compris cette séquence d' instructions
Code: [Select]

    char * pch;
      pch = strstr(sTrama, "0d0a");


Il recherche la chaine de 4 caractères "0d0a"?

savoriano

oui. Il cherche si dans sTrama il y a "0d0a". Trouvé ici
Ces 4 caractères indiquent que la trama est finie.
Pardonnez moi pour mon français, ce n'ai pas ma langue maternelle.

hbachetti

Tu dois faire erreur :
0x0D ou 13 ou '\r' = retour chariot
0xOA ou 10 ou '\n' = retour à la ligne

Code: [Select]
      pch = strstr(sTrama, "\r\n");
Linux is like a wigwam: no Windows, no Gates, and an Apache inside ...

dbrion06

Vous confondez peut être une séquence de caractères avec leur représentation hexadecimale (horreur assez courante, surtout s'ils sont inimprimables....):
ex:"RU" a pour représentation hexadécimale 52 55 00

savoriano

#12
Aug 08, 2019, 04:04 pm Last Edit: Aug 08, 2019, 04:14 pm by savoriano
Avec le premier code la trame finissait avec 0d0a.
Ca veut dire que:

si serial.read() lit un 13 ou un 10
sprintf ne rajoute pas 0d ou 0a à la chaine?

P.S.
Code: [Select]
    sHex = String(Serial2.read(), HEX);//Attention: il ne met pas les 0 non significatifs
    if (sHex.length() == 1) sHex = "0" + sHex;//ajoute les 0 non significatifs
    sTrama += sHex; //Crée la trame

me rajoutait bien 0d et 0a à sTrama
Pardonnez moi pour mon français, ce n'ai pas ma langue maternelle.

kamill

Tu confonds le caractère '\x0d' ou '\r' et la chaine de caractères "0d"  qui est composée de deux caractères '0' et 'd'.

hbachetti

sprintf ajoute un '\0' à la chaîne, rien d'autre.

Quote
Avec le premier code la trame finissait avec 0d0a.
Plutôt "\x0d\x0a"

Tes caractères '\x0d' et '\x0a' provenaient forcément de la ligne série.
Si tu envoies tes chaînes avec un terminal ou le moniteur série ARDUINO c'est normal.

Tu peux régler le moniteur série pour envoyer CR, LF, les deux ou aucun.
Linux is like a wigwam: no Windows, no Gates, and an Apache inside ...

Go Up