Go Down

Topic: Comment chercher une sous-chaîne spécifique ? (Read 295 times) previous topic - next topic

Niconnexion

May 17, 2017, 02:31 pm Last Edit: May 17, 2017, 04:04 pm by Niconnexion
Bonjour à tous,

Comment puis-je vérifier la présence d'une sous-chaîne spécifique dans une chaîne ?
J'ai bien trouvé la méthode substring(index,index2) mais dans mon cas je ne sais pas à quel endroit de la chaîne se trouve ma sous-chaîne...

J'ai bien une idée :

-Parcourir la chaîne.
-Comparer le caractère(c-ch) à l'indice courant(x-ch) de la chaîne au caractère(c-s-ch) à l'indice (x-s-ch) de la sous-chaîne recherchée.
   -S'il est identique on compare c-ch de x+1 à c-s-ch de x-s-ch+1.
   -S'il est différent on compare c-ch de x+2 à c-s-ch de x-s-ch.
       -S'il est identique on compare c-ch de x+2 à c-s-ch de x-s-ch+1.
       -S'il est différent ...
           -S'il est identique ...
           -S'il est différent ...
               -S'il est identique et que c'est la fin de la sous chaîne on retourne "sous-chaîne trouvée".
               -S'il est différent et que c'est la fin de la chaîne on retourne "sous-chaîne non trouvée".

Quelqu'un peut m'expliquer comment faire ça en Arduino et me dire si c'est une bonne méthode ?  :D

Elle est surement mal rédigée mais je pense avoir l'idée...

En gros tant que les caractères sont identiques on avance sur la chaîne et sur la sous-chaîne et dès qu'ils sont différents on avance sur la chaîne mais on retourne au début de la sous-chaîne.






_pepe_

Bonjour

Arduino étant basé sur le langage C, pour rechercher une sous-chaîne dans une chaîne on peut utiliser la fonction standard strstr(), ou encore strcasechr() pour faire une recherche en ignorant la casse. Ces fonctions retournent un pointeur sur le début de la première sous-chaîne correspondante trouvée, ou NULL en cas d'échec.

Niconnexion


_pepe_

#3
May 17, 2017, 04:20 pm Last Edit: May 17, 2017, 04:24 pm by _pepe_
Sinon, la méthode que tu présentes peut convenir, même si ce n'est pas forcément la plus optimisée.


Code: [Select]

char *trouve_sous_chaine(char *chaine, char *motif)
{
  // pointe sur le début de la chaîne
  char *p = chaine;

  // tant que la fin de la chaîne n'a pas été atteinte
  while (*p != '\0') {
    // compare la sous-chaîne testée au motif recherché :

    // pointe sur le début du motif recherché
    char *q = motif;

    // pointe sur le début de la sous-chaîne testée
    char *r = p;

    // tant que la fin de la chaîne n'a pas été atteinte
    // et que les caractères comparés sont identiques
    while ( (*r != '\0') && (*r == *q) ) {

      // pointe sur les caractères à comparer suivants
      r++;
      q++;

      // si la fin du motif recherché a été atteinte
      if (*q == '\0')
        // le motif a été trouvé
        // retourne son adresse dans la chaîne
        return p;
    }

    // pointe sur le début de la sous-chaîne testée suivante
    p++;
  }

  // le motif n'a pas été trouvé
  return NULL;
}

Niconnexion

Merci pour ce code bien détaillé !

De mon coté j'ai essayé de faire cela :

Code: [Select]

char Stringbuffer[inputStringGSM.length()+1];
String souschaine = "AT+CREG=1\r\r\nOK";
const int taille = souschaine.length()+1;
char SubstringBuffer[taille];
char *ret;
int cas;

void setup() {//[...]}

void loop() {
    //[...]
    switch(cas){
    case 0 :
      //retourne inputStringGSM (type String).
      readFromGsm();

      //Pour utiliser strstr il faut donc caster 'inputStringGSM'.
      inputStringGSM.toCharArray(Stringbuffer, inputStringGSM.length()+1);
      //Il faut également caster 'souschaine', bien que j'aurais pu la déclarer directement en char array.
      souschaine.toCharArray(SubstringBufferss, souschaine.length()+1);
     
      // La sous-chaîne est-elle dans la chaîne ?
      ret = strstr(Stringbuffer, SubstringBuffer);
      if (ret != NULL){
        Serial.println("Pas dans la chaîne");
        cas = 1;
        break;
      }else {
        Serial.println("dans la chaîne");
        cas = 2;
        break;
      }
    case 1:
    //[...]
    }
    //[...]
}


Mais j'ai ce message d'erreur : array bound is not an integer constant before ']' token.
Relatif à cette ligne : char Stringbuffer[inputStringGSM.length()+1];

Pourtant j'ai vu plein d'exemple avec cette méthode...
En attendant je vais essayer de comprendre ton code, pas encore  tout pigé x)

J-M-L

N'utilisez pas la classe String... restez au niveau des tableaux de caracteres
Please do not PM me for help,  others will benefit as well if you post your question publicly on the forums
Pas de messages privés SVP

Niconnexion

D'accord,
Mais ça n'explique pas l'erreur !

kamill

Bonjour,

Ce serait mieux si tu nous donnais tous les éléments avec le programme complet, mais je suppose que inputStringGSM.length() n'est pas une constante
d'ou l'erreur dans char Stringbuffer[inputStringGSM.length()+1];

J-M-L

#8
May 19, 2017, 10:09 am Last Edit: May 19, 2017, 10:12 am by J-M-L
Si si ;)

Et si vous utilisez des instances de la classe String dans ce cas utilisez les méthodes associées d'extraction (indexOf) et comparaison (==), ne fabriquez pas de nouveaux buffers

Mais cette classe n'est pas idéale pour des microcontroleurs avec peu de ram, le mieux donc étant de faire les choses plus près du hardware avec les fonctions C
Please do not PM me for help,  others will benefit as well if you post your question publicly on the forums
Pas de messages privés SVP

Niconnexion

La taille de inputStringGSM varie, oui. Mais même si c'était un tableau de caractères elle varierait.

Elle varie en fonction de la réponse que me retourne le GSM donc oui elle varie tout le temps...

A quoi sert strstr() si on ne peut pas chercher une sous-chaîne dans une chaîne qui varie.

Comment j'utilise strstr si ma chaîne varie ?

Je sais bien que String n'est pas adapté aux microcontrôleurs mais c'est étrange cette facilité que j'ai à l'utiliser... Tout le monde rabâche qu'il faut utiliser des tableaux de caractères mais personnellement j'ai toujours pas réussi à lire un seul caractère sur mon bus série et à l'afficher. J'ai même pas trouvé un exemple sur internet !! Il y en a pas un pareil ! Alors qu'avec String ça marche du premier coup...

Si quelqu'un à assez de courage pour m'expliquer comment changer mes habitudes de String pour passer à du 100% tableau de char je suis preneur.

Il faut un buffer, ok. Comment je fais un buffer dynamique ? comme ça : char mybuffer[] =""; ?
Il faut un index, ok. J'imagine que ça c'est bon :  int index =0; ?
Il faut un endroit pour stocker le caractère courant, ok. Comme ceci :  char c; ?

Bon, tant qu'il y a des caractère on read ?
Du coup : While(Serial.available > 0){c=Serial.read();} ?

Je pense que j'ai compris tout ça mais pourquoi je n'arrive toujours pas à lire un caractère.
Code: [Select]

char mybuffer[]="";
int i = 0;
char c;

void setup() {
Serial.begin(9600);

while(Serial.available() > 0){
    c = Serial.read();
    mybuffer[i] = c;
    i++;
  }
Serial.println(mybuffer);

}
void loop() {
}



Pouvez vous me faire un exemple simple (très simple) pour accompagner cette douloureuse transition de String à tableau de char que je m'apprête à faire ?  :smiley-cry:

J-M-L

#10
May 19, 2017, 11:09 am Last Edit: May 19, 2017, 11:45 am by J-M-L
Quote
Tout le monde rabâche qu'il faut utiliser des tableaux de caractères mais personnellement j'ai toujours pas réussi à lire un seul caractère sur mon bus série et à l'afficher. J'ai même pas trouvé un exemple sur internet !! Il y en a pas un pareil ! Alors qu'avec String ça marche du premier coup...
lire Serial Input Basics (Example 2 - Receive with an end-marker ) et connaître les fonctions des librairies standards stdlib.h et
string.h

Quote
Comment je fais un buffer dynamique ?
En utilisant malloc() et free() par exemple --> mais ne le faites pas.

Vous en prenez un assez grand pour stocker la réponse la plus longue que vous attendez jusqu'à la réception d'un marqueur de fin de réponse (souvent un retour à la ligne) le plus souvent ce qui vous intéresse à une longueur connue - ou alors vous pouvez traiter par petits bouts la réponse quand elle arrive avec un parser.

Utiliser de la mémoire dynamique est ce qui morcelle votre mémoire et qui - si c'est mal géré - au final transforme le tas en gruyère et vous ne pourrez plus allouer un bloc contigu de mémoire à un moment et votre programme va crasher sans que vous puissiez identifier pourquoi. (cf par exemple ce post)
Please do not PM me for help,  others will benefit as well if you post your question publicly on the forums
Pas de messages privés SVP

Niconnexion

Merci J-M-L.

Voici donc ma nouvelle façon de faire :
Code: [Select]

#define SIM800_RESET_PIN 77
#define PWKEY 6
const byte nbChars = 64;
char receivedChars[nbChars];
boolean newData = false;

void setup() {
  Serial.begin(9600);
  Serial1.begin(9600);

  pinMode(SIM800_RESET_PIN, OUTPUT);
  pinMode(PWKEY, OUTPUT);
  digitalWrite(PWKEY, HIGH);
  delay(1000);
  digitalWrite(SIM800_RESET_PIN, LOW);
  delay(100);
  digitalWrite(SIM800_RESET_PIN, HIGH);
  delay(2000);
}

void loop() {
   
      delay(2000);
     
      // Network registration
      commandStringGSM = "AT+CREG=1";
      Serial1.println(commandStringGSM);
      delay(50);
      readFromGsm();     
      showNewData();
      delay(500);
     
      // Set text mode
      commandStringGSM = "AT+CMGF=1";
      Serial1.println(commandStringGSM);
      delay(50);
      readFromGsm();
      showNewData();
      delay(500);

}



ma fonction ReadFromGsm :
Code: [Select]

void readFromGsm(){

    static byte ndx = 0;
    char endMarker = '\n';
    char rc;
   
    while(Serial1.available() > 0 && newData == false){
   
        rc = Serial1.read();
        if (rc != endMarker){
          receivedChars[ndx] = rc;
          ndx++;
          if (ndx >= nbChars) {
            ndx = nbChars -1;
          }
        }else {
            receivedChars[ndx] = '\0'; // terminate the string
            ndx = 0;
            newData = true;
        }
    }
}


et ma fonction showNewData :
Code: [Select]

void showNewData(){
    if (newData == true) {
        Serial.print("Received stuff : ");
        Serial.println(receivedChars);
        newData = false;
    }
}


J'ai bien les réponses du GSM mais c'est pas encore la grande forme quand on regarde le moniteur série :

Code: [Select]

Received stuff : AT+CMGF=1    //ici c'est bien la réponse attendue...


Received stuff : OK                 //...suivie de "OK" c'est parfait pour vérifier la réponse ([b]1[/b])

Received stuff :

Received stuff : +CPIN: READY

Received stuff :

Received stuff : SMS ReaAT+CREG=1


Received stuff : OK

Received stuff :
Received stuff : ERROR
AT+CMGF=1


Received stuff : Call ReAT+CMGF=   //ici ça va pas ! On a "Call re" ([b]2[/b]) puis "AT+CMGF="
Received stuff : OK                        //pas d'\r\r\n avant le OK ça va pas !





La réponse attendue est : "AT+CREG=1\r\r\nOK" et la suite importe peu.


(1) J'aurais voulu vérifier la réponse en la comparant à la réponse attendue. Simplement :
if(receivedChars == "AT+CREG=1\r\r\nOK"){//bonne réponse}else{//mauvaise réponse}

(2) "Call re" est en fait l'URC "Call ready" qui a été mangé je ne sais comment, et il manque le 1 à "AT+CMGF=" ! et il n'y a pas d'\r\r\n avant le "OK" comme au niveau du (1).

Il y a d'autres URC qui viennent parasiter la lecture comme :
"SMS ready" ou "+CPIN: READY" ou encore "RDY" et j'en passe...


Cette idée de lire tant qu'on est pas au caractère de fin de chaîne prédéfinit n'est peut-être pas adaptée pour la vérification que je souhaite effectuer.


Il faut impérativement que je reçoive "AT+CREG=1\r\r\nOK" pour vérifier que la commande à bien été appliquée. Après il est possible de vérifier "AT+CREG=1\r\r\n" dans un premier temps puis lors d'une seconde lecture vérifier "OK" mais le cas (2) montre bien que c'est pas gagné...

Comment pourrait-je vérifier la réponse de mon GSM ?  :smiley-confuse:

J-M-L

je ne sais pas ce qu'est commandStringGSM c'est défini où?

Vous avez encore un petit problème de compréhension sur le port série

il faudrait que la fonction readFromGsm soit appelée tant que vous n'avez pas reçu le marqueur de fin de ligne / réponse surtout en ayant une communication à 9600 bauds

en effet votre while va vider le buffer du port série plus vide que les données n'arrivent et donc à un moment while(Serial1.available() > 0 sera faux, aucune donnée ne sera dispo, mais ce n'est pas parce que vous avez tout reçu, c'est parce qu'il faut attendre un peu pour recevoir la fin et donc vous recevez une réponse tronquée (et la prochaine fois que vous appelez la fonction vous allez recevoir ce qu'il vous manque ce qui peut vous induire en erreur, par exemple lire à ce moment là le OK qui devait être reçu avant)

le fonctionnement (si vous regardez le tuto) c'est d'appeler dans la boucle leur fonction recvWithEndMarker() et tant que newData n'est pas vrai, il y a encore des choses qui doivent arriver.

Please do not PM me for help,  others will benefit as well if you post your question publicly on the forums
Pas de messages privés SVP

Niconnexion

Bonjour,

commandStringGSM est de type String et est définie avant le void setup() comme suit :
String commandStringGSM = "";

Je ne l'ai pas encore changé en tableau de caractères mais est-ce la cause de mon problème ?
Je m'en sert juste pour écrire sur le bus série de mon module GSM, pour lui envoyer les commandes.

Je dois définir '\0' comme étant mon caractère de fin de chaîne (endMarker) ?

Je dois appeler readFromGsm tant que je n'ai pas reçu le marqueur de fin de ligne ? Dans l'exemple du tuto il appelle simplement recvWithEndMarker() dans le void loop().

Je dois mettre un délai à quel endroit ? Il n'y a pas de délai dans le tuto, et la communication est à 9600 bauds, comme pour moi.

Si on prend cette ligne du moniteur série :
Received stuff : SMS ReaAT+CREG=1

"Rea" est en fait "Ready", tronqué, mais je n'ai jamais reçu "dy" par la suite.

J'appelle ma fonction dans la boucle et tant que newData n'est pas vrai, comme dans le tuto, je ne comprend pas votre dernière phrase.

Merci encore, je teste tout ça à 9h.





J-M-L

Bonjour,

Quote
commandStringGSM est de type String et est définie avant le void setup() comme suit : ...
On dirait que le code que vous postez n'est pas celui que vous utilisez... (et si elle est déclarée dans le setup elle ne peut pas être utilisée ailleurs...)

Difficile de vous aider si on ne voit pas votre vrai code


- pas besoin de delay dans la gestion du port série si vous attendez tranquillement la réception du caractère de fin le ligne / message
 

Please do not PM me for help,  others will benefit as well if you post your question publicly on the forums
Pas de messages privés SVP

Go Up