IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Arduino Discussion :

Communication entre HC12 aléatoire ESP32-seeduinoXIAO [Arduino ESP32]


Sujet :

Arduino

  1. #1
    Membre éclairé
    Homme Profil pro
    bricoleur
    Inscrit en
    Octobre 2014
    Messages
    417
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Oise (Picardie)

    Informations professionnelles :
    Activité : bricoleur
    Secteur : Alimentation

    Informations forums :
    Inscription : Octobre 2014
    Messages : 417
    Par défaut Communication entre HC12 aléatoire ESP32-seeduinoXIAO
    Bonjour à tous
    J'ai un problème de transmission que seule la sensibilité humaine peut trouver, enfin je l'espère:

    J'envoie une commande d'un ESP32 équipé d'un HC12 vers une carte XIAO équipée aussi d'un HC12; puis j'attends un accusé réception.
    Le message de retour doit être stocké dans un tableau de caractères mRetour[20].
    Cela fonctionne dans la plupart des cas, mais, de temps en temps, l'accusé réception ne parvient pas (1 fois sur 5 ou 10), alors qu'il est bien émis de la carte distante.
    La routine d'envoi et de réception côté ESP32:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    //********************Emission / reception *******************************************
    bool xmit(char *cc){
      Serial.print("Msg envoye: "); 
      afficMsg(XMIT,false,10,20);                     //Transmission en cours
      uint8_t k=0; bool ok=false;                     //k->compteur de reception             
      //mRetour = "";                                   //message de retour sur question                                  //recepteur de catactere en code ASCII  
      /*while(Serial2.available()>0){
        char r=Serial2.read();                             //vidage du buffer de reception
      }*/
      Serial2.print(cc);                              //envoi de la commande
      Serial.println(cc);
      unsigned long tic=millis();                     //declenchement du chrono
      while((!Serial2.available()) && (millis()-tic < ATTENTE));  //attente de la reception
      if(Serial2.available()>0){
        do{ //*****tant que non ok et non timeout 5s *************************************
          int r = Serial2.read();                         //lecture d'un caractere (code ASCII)
            if(r >= 30 && r< 90 ) {                       //si r est compris entre 0 et Y de ASCII
              mRetour[k] = char(r);                       //charge le caractere
              k++;                         
              tic=millis();                             //redeclenche le timer
            }
            else if(r == 90){
              ok = true;        //le 'Z' (90) est le caractere de fin de message
              mRetour[k] = '\0';
            }
            //delai('m',20);                           //attend la reception d'autres caracteres
        }while(millis()-tic < ATTENTE && !ok);         //tant que pas de timeout et non ok  
      }
      else{                             //pas de message recu - Timeout
        effaceMsg(false,true);          //efface la deuxieme ligne , garde la premiere avec la commande
        afficMsg(TIMOUT,true,10,20);    //affiche Timeout sur la deuxieme ligne
      }  
      return (ok)? true: false;
    }
    Une des routines de réception côté ESP32:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
          //**************demande l'etat des equipements d'une carte */
          case 'E': {
            if(iParam==0 && (numCarte > NB_VOLET || nbCarSaisis != 5)){   //Erreur commande etat des volets
              sprintf(buf,"%s",ERCOM);
              afficMsg(buf,true,10,20);
              return;
            }
            else if(iParam==0){   //Exy00  demande etat d'un volet
              sprintf(buf,"Etat %s %d demande?", VOL,numCarte);
              afficMsg(buf);
              if(xmit(carCom)){
                int adresse = 0;
                int posVol = 0;
                bool ok=true;
                if (sscanf(mRetour, "K%d=%dE", &adresse, &posVol) == 2) {
                  Serial.printf("Retour: adresse = %d Valeur: %d\n", adresse,posVol);
                }
                else{
                  Serial.printf("%s\n",ERFMT);
                  ok=false;
                }
                if(ok){
                  if(posVol == 100){
                    sprintf(buf,"%s %d est en %s totale",VOL,numCarte,OUV);
                  }
                  else if(posVol == 0){
                    sprintf(buf,"Etat %s %d en %s totale",VOL,numCarte,FER);
                  }
                  else
                    sprintf(buf,"%s %d est %s de %d%c",VOL,numCarte,OUV,posVol,'%');
                  //if ok
                  effaceMsg(false,true);
                  afficMsg(buf,true,10,20);
                }
              }
              else{
                Serial.println(ERACK);
                afficMsg(ERACK,true,10,20);  
              }
            }
          }
          break;
    Message envoyé côté XIAO:
    Msg reçu: E1200
    Reponse: K12=0EZ
    Message reçu côté ESP32 lorsque cela fonctionne:
    Msg envoye: E1200Z
    ....Retour: adresse = 12 Valeur: 0
    Lorsque cela ne fonctionne pas:
    Msg envoye: E1200Z
    Erreur d'acquitement
    Si quelqu'un a une idée?

  2. #2
    Expert confirmé

    Homme Profil pro
    mad scientist :)
    Inscrit en
    Septembre 2019
    Messages
    2 938
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Etats-Unis

    Informations professionnelles :
    Activité : mad scientist :)

    Informations forums :
    Inscription : Septembre 2019
    Messages : 2 938
    Par défaut
    ce serait bon d'afficher ce que vous recevez vraiment et de mettre aussi un marqueur de début de message pour permettre la synchronisation propre du code qui attend l'ack.

    Imaginez que vous fassiez un timeout parce que le Z n'est pas arrivé à temps, lorsque vous revenez dans la boucle la fois suivante, il y a le Z dans le buffer et donc vous lisez tout de suite un message vide.

    Sinon e HC12 présente aussi des limites d’émission : la taille utile d’un message ne doit pas dépasser environ 60 octets, et le module impose un délai variable avant de repasser en réception, pouvant atteindre plusieurs dizaines de millisecondes selon le mode. Si la carte distante répond trop vite, une partie du message peut être perdue.

  3. #3
    Membre éclairé
    Homme Profil pro
    bricoleur
    Inscrit en
    Octobre 2014
    Messages
    417
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Oise (Picardie)

    Informations professionnelles :
    Activité : bricoleur
    Secteur : Alimentation

    Informations forums :
    Inscription : Octobre 2014
    Messages : 417
    Par défaut
    Bonsoir Jay M et merci pour votre réponse qui m'a fait prendre conscience que des caratères parasites pouvaient s'immicer dans le dialogue.
    J'ai donc valider les lignes commentaires où je vidais le buffer de réception avant l'envoi et supprimé les 'Serial.println' parfois inutiles.
    Ces changements ont notablement amélioré mon système avec un seul timeout pour une vingtaine de transmissions.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    //********************Emission / reception *******************************************
    bool xmit(char *cc){  
      afficMsg(XMIT,false,10,20);                     //Transmission en cours
      uint8_t k=0; bool ok=false;                     //k->compteur de reception                                               //recepteur de catactere en code ASCII  
      while(Serial2.available()>0){
        char r=Serial2.read();                       //vidage du buffer de reception
      }
      Serial2.print(cc);                              //envoi de la commande
      Serial.print("Msg envoye: "); Serial.println(cc);
      unsigned long tic=millis();                     //declenchement du chrono
      while((!Serial2.available()) && (millis()-tic < ATTENTE));  //attente de la reception
      if(Serial2.available()>0){
        do{ //*****tant que non ok et non timeout 5s *************************************
          int r = Serial2.read();                         //lecture d'un caractere (code ASCII)
            if(r >= 30 && r< 90 ) {                       //si r est compris entre 0 et Y de ASCII
              mRetour[k] = char(r);                       //charge le caractere
              k++;                         
              tic=millis();                             //redeclenche le timer
            }
            else if(r == 90){
              ok = true;        //le 'Z' (90) est le caractere de fin de message
              mRetour[k] = '\0';
            }
            delaiM(20);                                 //attend 20 ms la reception d'autres caracteres
        }while(millis()-tic < ATTENTE && !ok);          //tant que pas de timeout et non ok  
      }
      else{                             //pas de message recu - Timeout
        effaceMsg(false,true);          //efface la deuxieme ligne , garde la premiere avec la commande
        afficMsg(TIMOUT,true,10,20);    //affiche Timeout sur la deuxieme ligne
      }  
      return (ok)? true: false;
    }
    Sinon je teste partout les entêtes d'acquitement (le caractère K) et leur fin (caractère Z).

    Si vous avez une idée pour parfaire la solution; peut-être une ré-émission en cas d'échec?

  4. #4
    Expert confirmé

    Homme Profil pro
    mad scientist :)
    Inscrit en
    Septembre 2019
    Messages
    2 938
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Etats-Unis

    Informations professionnelles :
    Activité : mad scientist :)

    Informations forums :
    Inscription : Septembre 2019
    Messages : 2 938
    Par défaut
    vous ne pouvez pas compter sur le delay de 20ms pour que votre boucle
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
      if(Serial2.available()>0){
        do{ //*****tant que non ok et non timeout 5s *************************************
          int r = Serial2.read();                         //lecture d'un caractere (code ASCII)
         ...
    soit fonctionnelle il ne faut faire read() que quand available() vous dit qu'il y a quelque chose à lire (ou si vous faites un read et que c'est -1 c'est qu'il n'y avait rien à lire)

  5. #5
    Expert confirmé
    Avatar de fred1599
    Homme Profil pro
    Lead Dev Python
    Inscrit en
    Juillet 2006
    Messages
    4 754
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Meurthe et Moselle (Lorraine)

    Informations professionnelles :
    Activité : Lead Dev Python
    Secteur : Arts - Culture

    Informations forums :
    Inscription : Juillet 2006
    Messages : 4 754
    Par défaut
    Hello,

    @mormic, je pense qu'on a besoin de précisions

    1. Quel est le mode de fonctionnement exact (FU1, FU2, ou FU3/FU4) et le débit (baud rate) des deux modules HC12 ? (Exemple : Mode FU3 à 9600 bps)
    2. Quelle est la valeur de la constante ATTENTE (en millisecondes) ?
    3. Pouvez-vous confirmer que la carte XIAO distante envoie sa réponse immédiatement après avoir traité la commande, ou y a-t-il un délai (delay()) introduit volontairement ?
    4. Quel est le code de réception/réponse côté XIAO ? (Ceci est crucial pour vérifier la vitesse de réponse et la cohérence de l'émission).
    5. Avez-vous des informations sur la qualité du signal (distance, obstacles, interférences) ?
    Celui qui trouve sans chercher est celui qui a longtemps cherché sans trouver.(Bachelard)
    La connaissance s'acquiert par l'expérience, tout le reste n'est que de l'information.(Einstein)

  6. #6
    Membre éclairé
    Homme Profil pro
    bricoleur
    Inscrit en
    Octobre 2014
    Messages
    417
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Oise (Picardie)

    Informations professionnelles :
    Activité : bricoleur
    Secteur : Alimentation

    Informations forums :
    Inscription : Octobre 2014
    Messages : 417
    Par défaut
    Merci Fred1599 pour ces questions,

    1. J'utilise les HC12 tel que déballés du colis et j'ai choisi 9600 bits/s.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    //*****************parametrage general***************************************
    void setup(){
      Serial.begin(9600);
      delaiS(1);
      Serial.println("Demarrage");
      Serial2.begin(9600,SERIAL_8N1,16,17); //liaison HC12
      pinMode(TFT_LED, OUTPUT);
      digitalWrite(TFT_LED, LED_ON);
      tft.begin();
      tft.setRotation(ROTATION);
      //touch_calibrate();              // a utiliser la premiere fois
      if (!cal[1]) calibrate_touch();
      tft.setTouch(cal);
      tft.fillScreen(TFT_BLACK);        // on efface tout (fond noir)
      tft.setFreeFont(&FreeSans9pt7b);  // police de caractere
      initWiFi();
      delaiS(1);
      miseALHeure();
      afficDateHeure();
      delaiS(1);        //1 seconde  creationClavier();
      clavierOn=true;
      top=millis();
    }
    2. ATTENTE vaut actuellement 3000ms mais j'ai essayé d'autres valeurs sans changement.

    3. Non il n'y a pas de delay() et j'ai utilisé millis() entre la réception de la demande et le retour; résultat pour une demande d'état: 800ms
    Monitoring XIAO:
    Msg reçu: E1200
    Reponse: K12=56EZ en: 800ms
    4.
    Emission domotique: E1200 -> demande état carte 12, 00 pour le volet
    Réception carte distante d'adresse 12: K12=56EZ -> K:début de réponse, 12: adresse, =: indique la valeur, E: fin de la valeur, Z: fin du message
    5.
    Actuellement le récepteur se trouve dans mon atelier(4 m environ) mais je l'ai essayé à 15m avec obstacle: toujours bonne réception.

  7. #7
    Expert confirmé
    Avatar de fred1599
    Homme Profil pro
    Lead Dev Python
    Inscrit en
    Juillet 2006
    Messages
    4 754
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Meurthe et Moselle (Lorraine)

    Informations professionnelles :
    Activité : Lead Dev Python
    Secteur : Arts - Culture

    Informations forums :
    Inscription : Juillet 2006
    Messages : 4 754
    Par défaut
    Hmm, j'ai deux hypothèses, dont une qui est celle de @Jay M, on va vérifier si il y a une amélioration...

    Remplacez votre fonction xmit() actuelle par celle-ci. J'ai réécrit la logique de lecture pour qu'elle soit non bloquante, plus rapide et plus robuste, en suivant la suggestion de Jay M

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    //********************Emission / reception *******************************************
    bool xmit(char *cc) {
      afficMsg(XMIT, false, 10, 20);      // Transmission en cours
      uint8_t k = 0;
      bool ok = false;
     
      // 1. Vider le buffer de réception avant d'envoyer (bonne pratique)
      while (Serial2.available() > 0) {
        Serial2.read();
      }
     
      // 2. Envoi de la commande
      Serial2.print(cc);
      Serial.print("Msg envoye: "); Serial.println(cc);
     
      // 3. Boucle d'attente de réponse
      unsigned long tic = millis();
     
      // On ne vide plus mRetour, on le prépare
      memset(mRetour, 0, 20); // Nettoie le buffer de retour (mieux que mRetour = "")
     
      while (millis() - tic < ATTENTE && !ok) {
     
        // Y a-t-il un octet à lire ?
        if (Serial2.available() > 0) {
     
          int r = Serial2.read();
     
          // On ne re-déclenche le timer QUE si on reçoit un caractère valide.
          // Cela évite de maintenir la boucle "en vie" avec des parasites.
     
          if (r >= 30 && r < 90) {      // Caractère valide (0-Y)
            if (k < 19) { // Sécurité pour ne pas dépasser la taille du tableau (20-1)
              mRetour[k] = char(r);
              k++;
              tic = millis(); // On réinitialise le timeout à chaque caractère reçu
            }
          } 
          else if (r == 90) {           // 'Z' (90) = Fin de message
            mRetour[k] = '\0'; // Termine la chaîne C
            ok = true;
            // On ne sort pas de la boucle tout de suite, 'ok = true' s'en charge
          }
     
          // Tous les autres caractères (parasites, < 30 ou > 90) sont ignorés.
        }
        // Pas de delay() ici ! On laisse la boucle tourner rapidement.
      }
     
      // 4. Analyse du résultat (Timeout ou Message reçu)
      if (!ok) {                             //pas de message recu (ou pas de 'Z') - Timeout
        effaceMsg(false, true);
        afficMsg(TIMOUT, true, 10, 20);
        Serial.println("Timeout ou message incomplet.");
      }
     
      return ok;
    }
    Vérifiez que le taux d'échec tombe à 0.
    Celui qui trouve sans chercher est celui qui a longtemps cherché sans trouver.(Bachelard)
    La connaissance s'acquiert par l'expérience, tout le reste n'est que de l'information.(Einstein)

  8. #8
    Membre éclairé
    Homme Profil pro
    bricoleur
    Inscrit en
    Octobre 2014
    Messages
    417
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Oise (Picardie)

    Informations professionnelles :
    Activité : bricoleur
    Secteur : Alimentation

    Informations forums :
    Inscription : Octobre 2014
    Messages : 417
    Par défaut
    En effet cela paraît beaucoup plus sain, malgré un seul timeout pour une quarantaine d'envois; ce qui est acceptable puisque l'utilisateur est averti d'un échec.
    De plus je pourrais prévoir une procédure de renvoi.
    J'ai aussi modifié la procédure de réponse côté XIAO, car j'ai bien répondu qu'il n'y avait pas de delay(), mais il y avait une petite procédure de 2 clignotements de led intercalée entre réception et émission, raison des 800ms. Après cette modif, le temps de réponse=0ms!
    Procédure côté XIAO:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    case 'E': case 'e': { //********envoi de l'état du volet*********************************
                char buf[20];
                int etatActuel = etat*100;
                sprintf(buf,"K%d=%d%c%c",cAdd,etatActuel,'E','Z');            
                Serial1.println(buf);     //envoi sur HC12
                uint32_t tap=millis();
                cligne(2,LEDPIN);
                Serial.print("Reponse: "); Serial.print(buf);
                Serial.println(" en: " + String(tap-top) + "ms");
            }
    Encore merci à JayM pour ses analyses pertinentes et à fred1599 pour cette solution.

  9. #9
    Expert confirmé
    Avatar de fred1599
    Homme Profil pro
    Lead Dev Python
    Inscrit en
    Juillet 2006
    Messages
    4 754
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Meurthe et Moselle (Lorraine)

    Informations professionnelles :
    Activité : Lead Dev Python
    Secteur : Arts - Culture

    Informations forums :
    Inscription : Juillet 2006
    Messages : 4 754
    Par défaut
    Vous devez faire deux choses :

    • Côté XIAO : Introduire le délai de sécurité avant l'envoi.
    • Côté ESP32 : Utiliser la boucle de renvoi (retry) que vous avez mentionnée pour gérer les aléas restants.



    Remplacez votre case 'E' par celui-ci. J'ai déplacé le clignotement (remplacé par un delay propre) et changé le println en print.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    // Côté XIAO
    case 'E': case 'e': { //********envoi de l'état du volet*********************************
        char buf[20];
        int etatActuel = etat*100;
        sprintf(buf,"K%d=%d%c%c",cAdd,etatActuel,'E','Z');
     
        // TEMPS DE RÉPONSE (DEBUG)
        uint32_t tap = millis();
        Serial.print("Reponse preparee: "); Serial.print(buf);
        Serial.println(" en: " + String(tap-top) + "ms"); // Devrait être ~0ms
     
        // DÉLAI DE SÉCURITÉ CRUCIAL
        // Laisse le temps à l'ESP32 de repasser en mode RX
        delay(50); 
     
        // ENVOI HC12
        Serial1.print(buf);     // Utiliser print() au lieu de println()
     
        // Feedback visuel (si vous le souhaitez, mais après l'envoi)
        // cligne(2,LEDPIN); 
    }
    break; // N'oubliez pas le break !
    et

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    // Côté ESP32
    case 'E': {
      if (iParam == 0 && (numCarte > NB_VOLET || nbCarSaisis != 5)) {
        sprintf(buf, "%s", ERCOM);
        afficMsg(buf, true, 10, 20);
        return;
      } 
      else if (iParam == 0) { //Exy00 demande etat d'un volet
     
        #define MAX_RETRY 3
        bool transmission_ok = false;
        int retries = 0;
     
        // --- DÉBUT DE LA BOUCLE DE RENVOI ---
        while (retries < MAX_RETRY && !transmission_ok) {
     
          if (retries > 0) {
            Serial.printf("Echec, tentative %d/%d...\n", retries + 1, MAX_RETRY);
            delay(100); // Petit délai avant de renvoyer
          }
     
          sprintf(buf, "Etat %s %d demande?", VOL, numCarte);
          afficMsg(buf); // Affiche la demande (ex: "Etat Volet 12 demande?")
     
          // 1. Appel de xmit()
          if (xmit(carCom)) {
     
            // 2. SUCCÈS: xmit() a reçu un 'Z'. Vérifions le contenu de mRetour
            int adresse = 0;
            int posVol = 0;
     
            // mRetour contient "K12=56E" (le 'Z' a été remplacé par '\0')
            // Votre sscanf d'origine était donc correct !
            if (sscanf(mRetour, "K%d=%dE", &adresse, &posVol) == 2) {
     
              Serial.printf("Retour OK: adresse = %d Valeur: %d\n", adresse, posVol);
              transmission_ok = true; // C'est bon, on sort de la boucle while
     
              // --- Début de votre code d'affichage ---
              if (posVol == 100) {
                sprintf(buf, "%s %d est en %s totale", VOL, numCarte, OUV);
              } else if (posVol == 0) {
                sprintf(buf, "Etat %s %d en %s totale", VOL, numCarte, FER);
              } else {
                sprintf(buf, "%s %d est %s de %d%c", VOL, numCarte, OUV, posVol, '%');
              }
              effaceMsg(false, true);
              afficMsg(buf, true, 10, 20);
              // --- Fin de votre code d'affichage ---
     
            } else {
              // Erreur de format: on a reçu un 'Z' mais le message est corrompu
              Serial.printf("Erreur format (mRetour): %s\n", mRetour);
              afficMsg("Reponse CORROMPUE", true, 10, 20);
              // On laisse transmission_ok = false, la boucle va recommencer
            }
     
          } else {
            // ECHEC: xmit() a eu un TIMEOUT (le 'Z' n'est jamais arrivé)
            // (xmit s'occupe déjà d'afficher TIMOUT)
            Serial.println(ERACK); 
            // On laisse transmission_ok = false, la boucle va recommencer
          }
     
          retries++; // On passe à l'essai suivant
     
        } // --- FIN DE LA BOUCLE DE RENVOI ---
     
        // 3. Vérification finale
        if (!transmission_ok) {
          Serial.println("Echec final apres 3 tentatives.");
          effaceMsg(false, true); // Efface la ligne du bas
          afficMsg("ECHEC COM. FINAL", true, 10, 20); // Affiche l'échec final
        }
      }
    }
    break;
    Celui qui trouve sans chercher est celui qui a longtemps cherché sans trouver.(Bachelard)
    La connaissance s'acquiert par l'expérience, tout le reste n'est que de l'information.(Einstein)

  10. #10
    Membre éclairé
    Homme Profil pro
    bricoleur
    Inscrit en
    Octobre 2014
    Messages
    417
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Oise (Picardie)

    Informations professionnelles :
    Activité : bricoleur
    Secteur : Alimentation

    Informations forums :
    Inscription : Octobre 2014
    Messages : 417
    Par défaut
    Merci fred1599 pour cette solution de 'retry' mais cela ne convient pas car ayant un paquet de transmissions à gérer, il est préférable d'inclure la procédure de 'retry' dans la routine 'xmit()'.
    Je m'y attèle de ce pas et n'hésiterai pas à revenir vers vous pour soumettre ma solution.

  11. #11
    Membre éclairé
    Homme Profil pro
    bricoleur
    Inscrit en
    Octobre 2014
    Messages
    417
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Oise (Picardie)

    Informations professionnelles :
    Activité : bricoleur
    Secteur : Alimentation

    Informations forums :
    Inscription : Octobre 2014
    Messages : 417
    Par défaut
    Voilà ma solution pour la routine xmit() avec répétition en cas de non accusé réception:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    //********************Emission / reception *******************************************
    bool xmit(char *cc){
      afficMsg(XMIT, false, 10, 20);      // Transmission en cours
      uint8_t k = 0;
      uint8_t nbXmit=0;
      unsigned long tic;
      bool ok = false;
      // 1. Vidage du buffer de réception avant d'envoyer
      while (Serial2.available() > 0) {
        Serial2.read();
      }
      do{   //boucle des tentatives d'envoi
        // 2. Envoi de la commande
        Serial2.print(cc);
        tic = millis();
        Serial.print("Msg envoye: "); Serial.println(cc); 
        // 3. Boucle d'attente de réponse
        // preparation de mRetour
        memset(mRetour, 0, 20); // Nettoie le buffer de retour (mieux que mRetour = "") 
        while (millis() - tic < ATTENTE && !ok) { 
          if (Serial2.available() > 0) {        // Y a-t-il un octet à lire ?
            int r = Serial2.read(); 
            // On ne re-déclenche le timer QUE si on reçoit un caractère valide.
            // Cela évite de maintenir la boucle "en vie" avec des parasites. 
            if (r >= 30 && r < 90) {      // Caractère valide (0-Y)
              if (k < 19) { // Sécurité pour ne pas dépasser la taille du tableau (20-1)
                mRetour[k] = char(r);
                k++;
                tic = millis(); // On réinitialise le timeout à chaque caractère valide reçu
              }
            } 
            else if (r == 90) {           // 'Z' (90) = Fin de message
              mRetour[k] = '\0';          // Termine la chaîne C
              ok = true;                  // Permet la sortie de la boucle
            }      
          }
        }
        nbXmit++;
        delaiS(1);   //1 seconde
      }while(!ok && nbXmit < MAX_RETRY);  //Fin de la boucle des tentatives d'envoi
      // 4. Analyse du résultat 
      if(!ok && nbXmit >= MAX_RETRY){  //nombre de retries depasse
        sprintf(mRetour,"%s %s; %s",ERRECV,VOL,cc);
      }
      return ok;
    }
    Le delai ATTENTE est de 500ms.
    Avec ce nouveau dispositif, il y a quelques fois encore une répétition du message que je vais devoir gérer côté XIAO; mais au final, c'est 100% des messages qui aboutissent.
    J'attends vos commentaires et merci encore pour vos aides.
    Cordialement
    Michel

  12. #12
    Expert confirmé
    Avatar de fred1599
    Homme Profil pro
    Lead Dev Python
    Inscrit en
    Juillet 2006
    Messages
    4 754
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Meurthe et Moselle (Lorraine)

    Informations professionnelles :
    Activité : Lead Dev Python
    Secteur : Arts - Culture

    Informations forums :
    Inscription : Juillet 2006
    Messages : 4 754
    Par défaut
    Je vois plusieurs erreurs,

    k n'est pas réinitialisé

    Vous initialisez uint8_t k = 0; une seule fois, à l'extérieur de votre boucle do-while de tentatives.

    Tentative 1 : Échec de transmission

    L'envoi échoue. xmit() reçoit K12= mais perd la fin. La variable k vaut maintenant 4. La boucle while(!ok...) se termine par un timeout.

    nbXmit passe à 1, puis delaiS(1) s'exécute.

    La boucle do-while recommence car !ok est vrai.

    Tentative 2 : Le piège

    memset(mRetour, 0, 20); → Le buffer est bien vidé, c'est parfait.

    Cependant, k vaut toujours 4.

    La nouvelle réponse K12=56EZ arrive.

    Le code if (r >= 30 && r < 90) va écrire le premier caractère 'K' dans mRetour[k], c'est-à-dire mRetour[4].

    Le buffer mRetour contiendra : [vide][vide][vide][vide]K12=56E\0

    Résultat

    Le sscanf(mRetour, "K%d=%dE", ...) dans le case 'E' échouera, car il s'attend à ce que mRetour[0] soit 'K'.

    La Correction : Vous devez réinitialiser k à zéro à chaque nouvelle tentative, en même temps que vous nettoyez mRetour.

    Mettez k = 0; juste après votre memset.

    Le delaiS(1) s'exécute tout le temps

    Vous avez placé delaiS(1); juste avant la fin de la boucle do-while.

    Cette ligne s'exécute à chaque passage dans la boucle, y compris lors d'une transmission réussie du premier coup.

    Tentative : Succès de transmission

    L'envoi réussit. La variable ok devient true.

    La boucle while (millis() - tic < ATTENTE && !ok) se termine.

    nbXmit++ s'exécute.

    delaiS(1); s'exécute, bloquant le programme pendant 1 seconde inutilement.

    Fin de la boucle

    La condition while(!ok && nbXmit < MAX_RETRY) est testée. Comme !ok est faux, la boucle s'arrête.

    xmit() retourne true.

    Votre interface sera donc "lente" de 1 seconde à chaque commande. Ce délai ne doit s'appliquer que si la transmission a échoué, pour attendre avant la prochaine tentative.

    La Correction : Déplacez le délai pour qu'il ne s'exécute que si la tentative a échoué ET qu'on s'apprête à en refaire une.

    Voici une proposition de code,

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    bool xmit(char *cc) {
      afficMsg(XMIT, false, 10, 20);
      uint8_t k = 0;
      uint8_t nbXmit = 0;
      unsigned long tic;
      bool ok = false;
     
      while (Serial2.available() > 0) {
        Serial2.read();
      }
     
      do {
        Serial2.print(cc);
        tic = millis();
        Serial.print("Msg envoye (Essai "); Serial.print(nbXmit + 1); Serial.print("): "); Serial.println(cc); 
     
        memset(mRetour, 0, 20);
        k = 0;
        ok = false;
     
        while (millis() - tic < ATTENTE && !ok) { 
          if (Serial2.available() > 0) {
            int r = Serial2.read(); 
     
            if (r >= 30 && r < 90) {
              if (k < 19) { 
                mRetour[k] = char(r);
                k++;
                tic = millis();
              }
            } 
            else if (r == 90) {
              mRetour[k] = '\0';
              ok = true;
            }      
          }
        }
     
        nbXmit++;
     
        if (!ok && nbXmit < MAX_RETRY) {
          Serial.println("Timeout, nouvelle tentative dans 1s...");
          delaiS(1);
        }
      } while (!ok && nbXmit < MAX_RETRY);
     
      if (!ok && nbXmit >= MAX_RETRY) {
        sprintf(mRetour, "%s %s; %s", ERRECV, VOL, cc); 
      }
     
      if (!ok) {
        effaceMsg(false, true);
        afficMsg(TIMOUT, true, 10, 20);
      }
     
      return ok;
    }
    Puisque votre fonction xmit() gère maintenant elle-même les renvois, votre code case 'E' doit être simplifié. Il ne doit plus contenir de boucle while (retries < MAX_RETRY...).
    Celui qui trouve sans chercher est celui qui a longtemps cherché sans trouver.(Bachelard)
    La connaissance s'acquiert par l'expérience, tout le reste n'est que de l'information.(Einstein)

  13. #13
    Membre éclairé
    Homme Profil pro
    bricoleur
    Inscrit en
    Octobre 2014
    Messages
    417
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Oise (Picardie)

    Informations professionnelles :
    Activité : bricoleur
    Secteur : Alimentation

    Informations forums :
    Inscription : Octobre 2014
    Messages : 417
    Par défaut
    Je suis désolé de ces erreurs de débutant dues à un empressement à vouloir en finir avec les problèmes de transmission.
    J'ai donc corrigé les ignominies.
    Je n'ai pas mis le dernier paragraphe
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    if (!ok) {
        effaceMsg(false, true);
        afficMsg(TIMOUT, true, 10, 20);
      }
    car le retour est géré par la fonction appelante.
    J'ai inséré une lecture des réponses la réception des caractères
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    Serial.print("reception en "); Serial.print(millis()-tap); Serial.println(" ms");
    Pour une demande de fermeture volet j'obtiens par exemple:
    Msg envoye: (Essai 1): F1200Z
    reception en 87 ms
    reception en 88 ms
    reception en 89 ms
    reception en 90 ms
    reception en 91 ms
    K12F
    K12F = accusé réception adresse 12, F pour fermeture

    Pour une demande d'état du volet, j'obtiens par exemple:
    Msg envoye: (Essai 1): E1200Z
    reception en 127 ms
    reception en 128 ms
    reception en 129 ms
    reception en 130 ms
    reception en 131 ms
    reception en 132 ms
    reception en 255 ms
    reception en 255 ms
    Retour OK: adresse = 12 Valeur: 20
    Par contre j'ai aussi obtenu cela:
    Msg envoye: (Essai 1): E1200Z
    Timeout, nouvelle tentative dans 1 s
    Msg envoye: (Essai 2): E1200Z
    Timeout, nouvelle tentative dans 1 s
    Msg envoye: (Essai 3): E1200Z
    reception en 126 ms
    reception en 127 ms
    reception en 128 ms
    reception en 129 ms
    reception en 130 ms
    reception en 131 ms
    reception en 255 ms
    Retour OK: adresse = 12 Valeur: 0

  14. #14
    Expert confirmé
    Avatar de fred1599
    Homme Profil pro
    Lead Dev Python
    Inscrit en
    Juillet 2006
    Messages
    4 754
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Meurthe et Moselle (Lorraine)

    Informations professionnelles :
    Activité : Lead Dev Python
    Secteur : Arts - Culture

    Informations forums :
    Inscription : Juillet 2006
    Messages : 4 754
    Par défaut
    Avez-vous ajouté le delay(50); AVANT le Serial1.print(buf); sur votre carte XIAO ?
    Celui qui trouve sans chercher est celui qui a longtemps cherché sans trouver.(Bachelard)
    La connaissance s'acquiert par l'expérience, tout le reste n'est que de l'information.(Einstein)

  15. #15
    Membre éclairé
    Homme Profil pro
    bricoleur
    Inscrit en
    Octobre 2014
    Messages
    417
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Oise (Picardie)

    Informations professionnelles :
    Activité : bricoleur
    Secteur : Alimentation

    Informations forums :
    Inscription : Octobre 2014
    Messages : 417
    Par défaut
    Avez-vous ajouté le delay(50); AVANT le Serial1.print(buf); sur votre carte XIAO ?
    Oui
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    case 'E': case 'e': { //********envoi de l'état du volet*********************************
                char buf[20];
                int etatActuel = etat*100;
                sprintf(buf,"K%d=%d%c%c",cAdd,etatActuel,'E','Z');
                delai('m',50);  //50ms           
                Serial1.print(buf);     //envoi sur HC12
                //temps de reponse (debug)
                uint32_t tap=millis();
                Serial.print("Reponse: "); Serial.print(buf);
                Serial.println(" en: " + String(tap-top) + "ms");
                cligne(2,LEDPIN);
            }
            break;
    D'ailleurs le delai de renvoi de l'Ack est 50-51ms

  16. #16
    Expert confirmé
    Avatar de fred1599
    Homme Profil pro
    Lead Dev Python
    Inscrit en
    Juillet 2006
    Messages
    4 754
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Meurthe et Moselle (Lorraine)

    Informations professionnelles :
    Activité : Lead Dev Python
    Secteur : Arts - Culture

    Informations forums :
    Inscription : Juillet 2006
    Messages : 4 754
    Par défaut
    ok, alors je pense plus à un problème matériel, peut-être un pic de courant du WiFi.

    Peut-être qu'il faudrait lisser l'alimentation du module HC12 (côté ESP32) pour qu'il ne soit pas affecté par les pics de courant du WiFi.

    Faut surveiller les logs, et faire des analyses, voir si c'est récurrent...
    Celui qui trouve sans chercher est celui qui a longtemps cherché sans trouver.(Bachelard)
    La connaissance s'acquiert par l'expérience, tout le reste n'est que de l'information.(Einstein)

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Réponses: 2
    Dernier message: 19/10/2024, 18h48
  2. Lecture standard et communication entre processus!
    Par Tartar Ukid dans le forum C++Builder
    Réponses: 5
    Dernier message: 05/07/2003, 17h37
  3. Communication entre processus
    Par markopolo dans le forum C++Builder
    Réponses: 2
    Dernier message: 26/06/2003, 17h21
  4. Réponses: 5
    Dernier message: 25/03/2003, 20h43
  5. communication entre programmes
    Par jérôme dans le forum C
    Réponses: 12
    Dernier message: 16/04/2002, 09h05

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo