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 :

ESPAsyncWebserver POST - Réceptionner un POST client et extraire les données. [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
    406
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Oise (Picardie)

    Informations professionnelles :
    Activité : bricoleur
    Secteur : Alimentation

    Informations forums :
    Inscription : Octobre 2014
    Messages : 406
    Par défaut
    Bonjour Jay M
    Depuis votre dernier Post et après des dizaines d'essais, je suis toujours en détresse.
    J'essaie de faire au plus simple et voilà où j'en suis:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     //********Réception d'un POST*********
      serveur.on("/GestionPost", HTTP_POST, reponseReception, nullptr, gestionReception);
    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
    //****Gestion réception des POST******************
    void gestionReception(AsyncWebServerRequest *req, uint8_t *data, size_t len, size_t index, size_t total) {
      if(index==0){
        bodyPret = false;
      }
      if(total >=tailleMaxBody) afficErreurs(MSG14);    //body too big
      else{
        memcpy(bufBody + index,data,len);
        if(index + len >= total){
          bufBody[total] = '\0';
          bodyPret=true;
          nbCarRecu=len;  
        }
      }
    }
    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
    void reponseReception(AsyncWebServerRequest *req) {
      if(bodyPret){
        afficInfo(MSG15);    //body received.
     
        //******************Analyse du post reçu pour la programmation********************
        char dest = bufBody[0];         //destination RAM ou ROM
        char nProg = bufBody[1];       //N° du programme horaire
        char nManip;
        uint8_t k=0;
        do{              //scan les 10 manoeuvres
          nManip=bufBody[2+k];          //N° de la manoeuvre sélectionnée     
          cmdCl[0] = bufBody[6+k];    //type action O/F
          cmdCl[1] = bufBody[3+k];    //adresse de l'équipement
          cmdCl[2] = bufBody[7+k];    //dizaine du taux
          cmdCl[3] = bufBody[8+k];    //unité du taux
          cmdCl[4] = bufBody[9+k];    //dizaine de l'heure
          cmdCl[5] = bufBody[10+k];   //unité de l'heure
          cmdCl[6] = bufBody[12+k];   //dizaine des minutes
          cmdCl[7] = bufBody[13+k];  //unité des minutes
     
          sprintf(buf0,"%i %c%c%c%c%c%c%c%c%c%c%c%c",nbCarRecu,dest,nProg,nManip,cmdCl[0],cmdCl[1],cmdCl[2],cmdCl[3],cmdCl[4],cmdCl[5],cmdCl[6],cmdCl[7],'\0');
          afficInfo(buf0);       //affichage 3 secondes
          k += 12;
        }while(k<122);
     
        if(dest=='O'){
          cmdCl[0] = '6';     //Code de commande ecriture en SPIFFS
          //cmdCl[1]  numéro du programme
          cmdCl[2] = '\0';
          commandeRecu = true;
        }
        req->send(200,"Content-type: text/plain", "Ok");
      }    
      else  req->send(413,"Content-type: text/plain", "NOk");
    }
    J'ai bien le message "Body reçu" -> donc "bodyPret=true;"
    Dans la boucle do while j'affiche bien les 8 premiers octets puis quelques fois les 8 suivants mais après le programme se plante et l'ESP32 se réinitialise!

    Je contrôle aussi que le nombre d'octets reçu est bien 122.

    J'ai l'impression que c'est un problème de timing?
    Dommage que je ne puisse pas utiliser Serial.print; je dois démonter l'ESP32 pour pouvoir le télécharger.
    Certainement dû à la fatigue ou à la luminosité de la nuit, en le remontant j'ai décalé le chip d'un cran et il n'a pas aimé du tout. Heureusement j'en avais un deuxième pour continuer mes tribulations!

  2. #2
    Expert confirmé

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

    Informations professionnelles :
    Activité : mad scientist :)

    Informations forums :
    Inscription : Septembre 2019
    Messages : 2 921
    Par défaut
    dans la première fonction vous faites
    ça c'est le nombre de caractère dans le dernier appel ce n'est pas la totalité des octets reçus, cette valeur est dans total
    il faudrait faire
    ---
    dans la seconde fonction la fonction sprintf() rajoute le caractère nul final donc ce n'est pas la peine de le prévoir.

    Comment est défini buf0 ?

    sinon quand vous faites k += 12; ce n'est pas bon car vous avez d'une part les 2 octets (destination RAM ou ROM et N° du programme horaire) à prendre en compte ce qui décale le compte et d'autre part il y a 13 octets et pas 12 entre le début des 2 trames

    par exemple, si j'analyse le message A6010VO02508:15120VO03008:16234LO10010:10348DO10011:00 et si je met cela en tableau avec l'index et en séparant les trames
    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
     
     0 A    ==> bufBody[0]  
     1 6    ==> bufBody[1]
    ----------
     2 0    ==> bufBody[2]  //N° de la manoeuvre sélectionnée
     3 1
     4 0
     5 V
     6 O
     7 0
     8 2
     9 5
    10 0
    11 8
    12 :
    13 1
    14 5
    ----------
    15 1    ==> bufBody[15]  
    16 2
    17 0
    18 V
    19 O
    20 0
    21 3
    22 0
    23 0
    24 8
    25 :
    26 1
    27 6
    ---------------
    28 2    ==> bufBody[28]  
    29 3
    30 4
    31 L
    32 O
    33 1
    34 0
    35 0
    36 1
    37 0
    38 :
    39 1
    40 0
    ---------------
    41 3    ==> bufBody[41]  
    42 4
    43 8
    44 D
    45 O
    46 1
    47 0
    48 0
    49 1
    50 1
    51 :
    52 0
    53 0
    on voit bien qu'il y a les 2 de décalage au début et ensuite on augment de 13 en 13



    je modifierais donc la fonction comme cela en commençant avec k = 2 et en modifiant les offsets quand vous ajoutez k et en faisant k += 13; au lieu de 12

    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
    void reponseReception(AsyncWebServerRequest *req) {
      if (bodyPret) {
        afficInfo(MSG15);    //body received.
     
        //******************Analyse du post reçu pour la programmation********************
        char dest = bufBody[0];         //destination RAM ou ROM
        char nProg = bufBody[1];       //N° du programme horaire
        char nManip;
        uint8_t k = 2;
        do {             //scan les 10 manoeuvres
          nManip = bufBody[k];      //N° de la manoeuvre sélectionnée
          cmdCl[0] = bufBody[4 + k];  //type action O/F
          cmdCl[1] = bufBody[1 + k];  //adresse de l'équipement
          cmdCl[2] = bufBody[5 + k];  //dizaine du taux
          cmdCl[3] = bufBody[6 + k];  //unité du taux
          cmdCl[4] = bufBody[7 + k];  //dizaine de l'heure
          cmdCl[5] = bufBody[8 + k]; //unité de l'heure
          cmdCl[6] = bufBody[10 + k]; //dizaine des minutes
          cmdCl[7] = bufBody[11 + k]; //unité des minutes
     
          sprintf(buf0, "%d %c%c%c%c%c%c%c%c%c%c%c", nbCarRecu, dest, nProg, nManip, cmdCl[0], cmdCl[1], cmdCl[2], cmdCl[3], cmdCl[4], cmdCl[5], cmdCl[6], cmdCl[7]);
          afficInfo(buf0);       //affichage 3 secondes
          k += 13;
        } while (k < 122);
     
        if (dest == 'O') {
          cmdCl[0] = '6';     //Code de commande ecriture en SPIFFS
          //cmdCl[1]  numéro du programme
          cmdCl[2] = '\0';
          commandeRecu = true;
        }
        req->send(200, "Content-type: text/plain", "Ok");
      }
      else  req->send(413, "Content-type: text/plain", "NOk");
    }

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

    Informations professionnelles :
    Activité : bricoleur
    Secteur : Alimentation

    Informations forums :
    Inscription : Octobre 2014
    Messages : 406
    Par défaut
    Comment est défini buf0 ?
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    char buf0[20], buf1[20];
    C'est pour l'affichage LCD sur 2 lignes de 16 caractères.

    sinon quand vous faites k += 12; ce n'est pas bon
    Je vous prie de m'excuser , j'ai modifié le champ "taux" non plus sur 3 caractères mais sur deux; en effet comme la valeur 100% n'est possible qu'une fois j'ai considéré que 00 était 100%. C'est d'ailleurs ainsi lorsqu'on manoeuvre à partir du clavier: O 3 00 -> ouverture du volet 3 de 100%. La fermeture quant à elle est toujours à 100%. Donc il y a bien 12 champs de commande. Vous avez raison d'initialiser k à 2 pour bien prendre en compte l'entête dest et prog.
    Voilà ce que cela donne avec les offsets adapté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
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    void reponseReception(AsyncWebServerRequest *req) {
      if(bodyPret){
        afficInfo(MSG15);    //body received.
     
        //******************Analyse du post reçu pour la programmation********************
        char dest = bufBody[0];         //destination RAM ou ROM
        char nProg = bufBody[1];       //N° du programme horaire
        char nManip;
        uint8_t k=2;
        do{              //scan les 10 manoeuvres
          nManip=bufBody[k];          //N° de la manoeuvre sélectionnée en position 2 puis 14
         // si c'est un volet
          cmdCl[0] = bufBody[4+k];    //type action O/F volet   en position 6 puis 
          cmdCl[1] = bufBody[1+k];    //adresse de l'équipement en position 3 puis 15
          cmdCl[2] = bufBody[5+k];    //dizaine du taux         en position 7 puis 19
          cmdCl[3] = bufBody[6+k];    //unité du taux           en position 8 puis 20
          cmdCl[4] = bufBody[7+k];    //dizaine de l'heure      en position 9 puis 21
          cmdCl[5] = bufBody[8+k];    //unité de l'heure        en position 10 puis 22
          cmdCl[6] = bufBody[10+k];   //dizaine des minutes     en position 12 puis 24
          cmdCl[7] = bufBody[11+k];  //unité des minutes        en position 13 puis 25
     
          sprintf(buf0,"%i %c%c%c%c%c%c%c%c%c%c%c%c",nbCarRecu,dest,nProg,nManip,cmdCl[0],cmdCl[1],cmdCl[2],cmdCl[3],cmdCl[4],cmdCl[5],cmdCl[6],cmdCl[7]);
          afficInfo(buf0);             //nbCarRecu correspond à size_t total
          k += 12;
        }while(k<122);
     
        if(dest=='O'){
          cmdCl[0] = '6';     //Code de commande ecriture en SPIFFS
          //cmdCl[1]  numéro du programme
          cmdCl[2] = '\0';
          commandeRecu = true;
        }
        req->send(200,"Content-type: text/plain", "Ok");
      }    
      else  req->send(413,"Content-type: text/plain", "NOk");
    }
    Voilà une trame avec saisie complète de la page HTML:
    Nom : captureAlert.JPG
Affichages : 120
Taille : 10,3 Ko

    Je relance le programme et j'affiche bien 122 pour total, ce qui est exact. Mais cela plante toujours.
    Alors j'ai remplacé afficInfo(buf0) par Serial.println(buf0), ce qui ne sert à rien puisque je ne peux pas utiliser la console du PC, mais qui a l'avantage de voir que ça ne plante plus. C'est donc ma routine d'affichage qui crée le problème:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    //***************affichage des messages d'information**************************
    void afficInfo(const char *msg){
      lcd.clear();
      lcd.backlight();
      lcd.setCursor(0,0); lcd.printf("%s",INF);
      lcd.setCursor(0,1); lcd.printf("%s",msg);
      delai('s',DURAFF);  //2 secondes
      lcd.clear(); 
    }
    Pourtant je l'utilise déjà à maintes reprises dans le programme ?
    ********************************
    Je viens de supprimer l'affichage. et cela fonctionne parfaitement. Ouf!
    Surtout que cette affichage dans l'utilisation normale ne sert à rien, puisque la commande est faite à distance.
    Je commençais vraiment à désespérer. Je vais pouvoir arrêter ce fil et continuer mes élucubrations.
    Je vous remercie encore pour le temps que vous avez consacré et vos conseils toujours avisés.
    Bien cordialement
    Michel

  4. #4
    Expert confirmé

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

    Informations professionnelles :
    Activité : mad scientist :)

    Informations forums :
    Inscription : Septembre 2019
    Messages : 2 921
    Par défaut
    je vois que votre fonction d'affichage faisait une attente active (de 2 secondes d'après le commentaire)
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
      delai('s',DURAFF);  //2 secondes
    comme vous appelez cela à chaque trame décodée dans la fonction reponseReception(), vous créez une attente de 20 secondes dans la boucle do {}*while()
    ça peut être trop long pour le callback et peut-être générer le plantage que vous voyez.

    Juste par curiosité, si vous conservez l'affichage mais enlevez le délai, est-ce que ça plante ?

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

    Informations professionnelles :
    Activité : bricoleur
    Secteur : Alimentation

    Informations forums :
    Inscription : Octobre 2014
    Messages : 406
    Par défaut
    Oui c'est exact, si je supprime ma fonction delai('s',DURAFF) de deux secondes cela fonctionne.
    J'ai essayé de mettre la fonction delay(2000) et c'est encore pire, même pas un affichage.
    Merci encore et à bientôt pour de nouvelles aventures....

  6. #6
    Expert confirmé

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

    Informations professionnelles :
    Activité : mad scientist :)

    Informations forums :
    Inscription : Septembre 2019
    Messages : 2 921
    Par défaut
    OK - c’est donc bien cela.

    La fonction est appelée dans le contexte de la construction de la réponse à la requête web - il faut que ce soit rapide, on ne peut pas bloquer le code.

    Bonne continuation !

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

    Informations professionnelles :
    Activité : bricoleur
    Secteur : Alimentation

    Informations forums :
    Inscription : Octobre 2014
    Messages : 406
    Par défaut
    Mon prochain delire:

    Le client demande à pouvoir modifier un programme horaire existant.
    Le serveur doit renvoyer la page HTML de saisie puis le contenu du programme existant.
    Le client effectue les modifications puis renvoie les données modifiées.
    Le serveur récupère les données et les traite.

    Les deux dernières lignes sont bien sûr déjà écrites.

  8. #8
    Expert confirmé

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

    Informations professionnelles :
    Activité : mad scientist :)

    Informations forums :
    Inscription : Septembre 2019
    Messages : 2 921
    Par défaut
    vous pourriez avoir une page générale qui montre la config en cours avec les champs modifiables
    si l'utilisateur modifie quelque chose et valide, ça envoie la requête de mise à jour

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

    Informations professionnelles :
    Activité : bricoleur
    Secteur : Alimentation

    Informations forums :
    Inscription : Octobre 2014
    Messages : 406
    Par défaut
    j''ai opté pour une solution relativement simple (pour moi):
    dans la page HTML de saisie d'un programme horaire, j'ai ajouté un bouton 'modif';
    après saisie du n° de programme à modifier, je lance un GET avec comme url "/API?Cmd=modif&prog=x" x étant le N° de prog;
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    function demandeProgramme(url){
        fetch(url, {
            method: 'GET',
            headers: {'Content-Type' : 'text/plain'}
        })
            .then(response => response.ok ? response.text() : Promise.reject("reponse non valide"))
            .then(text => chargeProgramme(text))
            .catch(error => alert("Erreur: ", error))
    }
    Le serveur qui a le programme dans un tableau char pg[10][81] 10 programmes de 0 à 9 10 manoeuvres de 8 octets (+ '\0') doit transmettre le tableau pg[x] dans la réponse.
    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
    serveur.on("/API", HTTP_GET, [](AsyncWebServerRequest *req){
        if(!clientConnected){
          req->send(408,"text/html", "Timeout");
          return;
        }
        String reponse = "";
        String commande[8], action[8];
        int nbArgs = req->args();
        for(int i=0;i<nbArgs;i++){
          commande[i] = req->argName(i); action[i]=req->arg(i);
        }
     //modification d'un programme horaire
        if(commande[0] == "Cmd" && action[0]=="modif"){
          uint8_t prg = action[1].toInt();
          reponse= String(pg[prg]);
          msgFinLoop = reponse;
          req->send(200,"text/plain", reponse);
        }
        }
    Ensuite, côté client, il me suffit avec javascript de remplir le formulaire de saisie, appliquer les modifs et transmettre par un POST.
    Afin de pouvoir visualiser des message sur LCD avec un délai, j'ai ajouté une routine qui affiche tranquillement en fin de Loop-->plus de plantage.

  10. #10
    Expert confirmé

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

    Informations professionnelles :
    Activité : mad scientist :)

    Informations forums :
    Inscription : Septembre 2019
    Messages : 2 921
    Par défaut
    idéalement vous auriez une page de configuration qui demande la configuration enregistrée côté arduino

    Avec cette liste vous mettez à jour les champs dans l'affichage

    un bouton permet de renvoyer la liste au serveur et vous faites un parsing pour mettre à jour la liste des configurations

+ Répondre à la discussion
Cette discussion est résolue.
Page 2 sur 2 PremièrePremière 12

Discussions similaires

  1. Appeler un programme sur le poste client avec <object>
    Par ouioui2000 dans le forum Balisage (X)HTML et validation W3C
    Réponses: 2
    Dernier message: 22/08/2005, 14h40
  2. [Forms5]ouvrir un executable sur un poste client
    Par anthony8 dans le forum Forms
    Réponses: 2
    Dernier message: 30/06/2005, 14h26
  3. [CR][VB6]PB sur poste Client
    Par Angusy dans le forum SDK
    Réponses: 8
    Dernier message: 16/04/2005, 20h20
  4. communication entre poste client windows et db mysql distant
    Par gabole dans le forum Bases de données
    Réponses: 3
    Dernier message: 24/08/2004, 16h59
  5. [CR10][ASP.NET]Impression sur le poste Client
    Par David.V dans le forum SAP Crystal Reports
    Réponses: 1
    Dernier message: 30/04/2004, 13h41

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