ESP32 web server asynch pour commande neopixel. Pb passage paramètre en live

Bonjour,

Je bosse avec :
ESP32 vroom
neopixel
Ide Arduino

Je débute un peu mais à force A force de lecture de tuto j'arrive à un server web à partir duquel je veux commander les effets lumineux d'un ruban de led adressables (ws2812)

Mes scénarios lumineux sont codés dans des fonctions et durent plusieurs minutes. Je souhaite les lancer en cliquant à partir de bouton sur une page web.

Mon pb : lorque je clique sur un bouton, l'effet se déroule bien, mais tant que la fonction n'est pas terminée je ne peux pas reprendre la main...
Je voudrais qu'à chaque clic sur un bouton, la fonction se déclenche aussitôt.

#include "SPIFFS.h"
#include "WiFi.h"
#include "ESPAsyncWebServer.h"
#include <Adafruit_NeoPixel.h>
#include <ESPmDNS.h>
 

const char* ssid= "toto";
// Set password
const char* password = "tata";

// Set Access Point IP
IPAddress   apIP(10, 10, 10, 1);
int max_connection =1;
int compteur = 0;
int pos1; int pos2; int pos3; int pos4; int pos5; // position led sur une même bande

#define PIN3 27 // pin connecté

#define NUMPIXELS 37 // Nb de Led

Adafruit_NeoPixel pixels = Adafruit_NeoPixel(NUMPIXELS, PIN, NEO_GRB + NEO_KHZ800);


int delayval = 10; // delay for half a second   
int prog =3; // numero du scenario lumière activé par défaut

 
AsyncWebServer server(80);
 
void setup(){
  Serial.begin(115200);
  pixels.begin(); // This initializes the NeoPixel library.

 
  if(!SPIFFS.begin()){
        Serial.println("Erreur de récupération des fichiers sur SPIFFS");
        return;
  }
  
 // WiFi.begin(ssid, password);
 // Set Access Point configuration
    WiFi.softAPConfig(apIP, apIP, IPAddress(255, 255, 255, 0));   // ss réseau FF FF FF 00
    WiFi.softAP(ssid, password, 1, 0, max_connection);
 
//  while (WiFi.status() != WL_CONNECTED) {
//    delay(1000);
//    Serial.println("Connecting to WiFi..");
//  }

  Serial.println("Connecting to WiFi..");
  Serial.println(WiFi.localIP());


  server.on("/html", HTTP_GET, [](AsyncWebServerRequest *request){ // Page de menu par défaut
    request->send(SPIFFS, "/vib.html", "text/html");
  });


  server.on("/UN", HTTP_GET, [](AsyncWebServerRequest *request){ // page UN
    request->send(SPIFFS, "/vib1.html", "text/html");
      prog=1;
  });
 
   server.on("/DE", HTTP_GET, [](AsyncWebServerRequest *request){ // page DE
    request->send(SPIFFS, "/vib2.html", "text/html");
      prog=2;
  }); 
   server.on("/TR", HTTP_GET, [](AsyncWebServerRequest *request){ // page DE
    request->send(SPIFFS, "/vib.html", "text/html");
      prog=3;
  }); 
  
  server.begin();
}
 
void loop(){


scenario(prog);


}// end loop

//---------------------------------------- Fonctions ----------------------------------------------
//-------------------------------------------------------------------------------------------------

    // choix du scénario lumière
    
    void scenario(int progLocal) {
Serial.println("programme dans boucle");  
Serial.println(progLocal);  


    switch (progLocal) {
      case 1:

        turquoise (0,255,255,0,delayval ); ...

 
        break;
      case 2:

        turquoise (0,255,255,255,delayval ); 
         retour(NUMPIXELS,0,0,0, 10); ...
        break;
      default:

                turquoise (0,255,0,0,100);   ...
        break;
    }// end switch
      
} // end scenario      

    
    void turquoise (int i, int r, int g, int b, int delayLocal){
  for(i ;i<NUMPIXELS;i++){
    // pixels.Color takes RGB values, from 0,0,0 up to 255,255,255
    pixels.setPixelColor(i, pixels.Color(r,g,b)); // Moderately bright green color.
    pixels.show(); // This sends the updated pixel color to the hardware.
    delay(delayLocal); // Delay for a period of time (in milliseconds).
  }
}
void retour (int i, int r, int g, int b, int delayLocal ){
    for(i;i>=0;i--){
    // pixels.Color takes RGB values, from 0,0,0 up to 255,255,255
    pixels.setPixelColor(i, pixels.Color(r,g,b)); // Moderately bright green color.
    pixels.show(); // This sends the updated pixel color to the hardware.
    delay(delayLocal); // Delay for a period of time (in milliseconds).
  }  
}

Je vous mets mon code. Mon pb est que une fois que la fct scenario est lancée elle ne prend plus en compte le changement d'argument envoyé par le client web... et là j'ai atteind mes limites de débutant
Merci d'avance

.

Bonjour,

Le problème vient de la structure de votre programme.
Vous ne pouvez pas savoir que la valeur de la variable globale 'prog' à été modifiée durant votre animation puisqu'à aucun moment vous ne regardez si sa valeur à changée.
De plus, dans le cas où vous voulez recommencer la même animation, il faudra être capable, pour la même valeur, de détecter sa mise à jour.

Edit: Dans ce genre de structure, il est préférable d'utiliser l'instruction millis() pour gérer une chronologie d'action plutôt que de travailler dans une boucle qui utilise la fonction delay() qui est bloquante.

Zlika a raison, mais ça impose de revoir en profondeur l'architecture du code. Les animations sont faites avec des boucles qui contiennent des delay (instructions bloquantes) et on ne sort de chaque animation qu'à la fin de cette boucle.

Pour éviter d'avoir à tout réécrire, il doit être possible de changer les boucles for par des boucles while en mettant un compteur de boucle soit en static soit en variable globale, et en testant le bouton dans la boucle. Si le bouton est enfoncé, on sort de l'animation par un return, sinon la boucle continue.

Pas simple à expliquer, voici en pseudocode la structure d'une animation:

static compteur = 0
while (compteur < valeur_max) {
 changement des leds
 attente avec millis {
   test du bouton
     si bouton enfoncé : return
  }
  compteur ++
}

L' "attente avec millis" c'est ce qui remplace le delay. Tu crées un chrono que tu lances au début de l'attente et tu vérifies l'état du bouton tant que la durée du delay n'est pas passée :

unsigned long chrono = millis();
while (millis()-chrono < delayLocal) {
  if (digitalRead(bouton) == LOW) return;
}

(en supposant un LOW si le bouton est enfoncé (INPUT_PULLUP)). Tu peux tester plusieurs boutons de la même manière.

Je pense que ça devrait marcher et permettre d'adapter le code à peu de frais. A tester cependant.

Oui, c'est une bonne idée pour ne pas trop changer la structure d'origine.
Par contre, l'événement ne vient pas d'un bouton mais du serveur Web asynchrone qui réagi à une requête et modifie une variable globale :confused:.
Je ne sais pas trop à quel moment cela va être traité. C'est possible sans tourner dans la loop pendant la durée de l'animation ? Sachant qu'il n'y a pas de handle, c'est un mystère pour moi.

Merci pour vos réponses. Tout nest pas super clair, mais je vais creuser et tester cela et je vous posterais le résultat

sinon il faudrait reprendre le code sur la base de machines à états, de sorte à quand même executer la loop régulièrement

voir ce tres bon cours sur la question

et éventuellement une librairie pour faciliter la mise en oeuvre

Finalement, on en revient à créer une base de temps avec millis() dans la loop, qui cadence l'appel à une fonction qui:

  • calcul ou définir les couleurs des leds du ruban en fonction de l'effet choisi
    (pas de boucle for ou while mais des variables que l'on incrémente ou diminue avec détection de fin plage)
  • envoie la trame dans le ruban
  • retourne le temps à attendre pour le prochain appel de la fonction

On peut éventuellement faire l' envoi de la trame au retour de l'appel pour simplifier la structure.

Oui, c'est ce que j'évoquais en disant

mais ça impose de revoir en profondeur l'architecture du code