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

Vue hybride

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

    Informations professionnelles :
    Activité : bricoleur
    Secteur : Alimentation

    Informations forums :
    Inscription : Octobre 2014
    Messages : 414
    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 935
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Etats-Unis

    Informations professionnelles :
    Activité : mad scientist :)

    Informations forums :
    Inscription : Septembre 2019
    Messages : 2 935
    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
    414
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Oise (Picardie)

    Informations professionnelles :
    Activité : bricoleur
    Secteur : Alimentation

    Informations forums :
    Inscription : Octobre 2014
    Messages : 414
    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 935
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Etats-Unis

    Informations professionnelles :
    Activité : mad scientist :)

    Informations forums :
    Inscription : Septembre 2019
    Messages : 2 935
    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 432
    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 432
    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
    414
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Oise (Picardie)

    Informations professionnelles :
    Activité : bricoleur
    Secteur : Alimentation

    Informations forums :
    Inscription : Octobre 2014
    Messages : 414
    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 432
    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 432
    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
    414
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Oise (Picardie)

    Informations professionnelles :
    Activité : bricoleur
    Secteur : Alimentation

    Informations forums :
    Inscription : Octobre 2014
    Messages : 414
    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.

+ 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