Reboot ESP8266 NodeMCU

Bonjour à tous,

Je souhaite construire un distributeur automatique de croquettes, avec un ESP8266 NodeMCU pour pouvoir l'utiliser avec mon assistant vocal.
Pour cela, je me sers de l'outil Sinric, qui me permet de faire cette communication en Alexa et mon ESP8266 relativement facilement.

J'ai d'abord commencé par réaliser un test avec une simple LED, que j'active grâce à Alexa. Jusqu'à là aucun problème, tout fonctionne.

J'ai ensuite souhaité que l'activation de cette LED enclenche la rotation de mon stepper. Et c'est là que ça se complique... Lorsque j'active ma broche, le moteur se lance. Cependant celui-ci s'arrête au bout d'environ 2sec et la carte reboot en me laissant ce message dans le moniteur série :

16:33:39.156 -> Soft WDT reset
16:33:39.156 -> 
16:33:39.156 -> >>>stack>>>
16:33:39.156 -> 
16:33:39.156 -> ctx: cont
16:33:39.192 -> sp: 3ffffb50 end: 3fffffc0 offset: 01b0
16:33:39.192 -> 3ffffd00:  3ffee970 000004ce 3ffeea84 402065db  
16:33:39.192 -> 3ffffd10:  3ffee970 3ffeeab0 3ffeea84 4020113e  
16:33:39.192 -> 3ffffd20:  3ffee970 3ffeeb2c 3fff05a4 40202b0b  
16:33:39.192 -> 3ffffd30:  00000005 3fff065c 3fff065c 3fff0a2c  
16:33:39.192 -> 3ffffd40:  3fff0a5c 3fff0a4c 3fff0a2c 40101620  
16:33:39.192 -> 3ffffd50:  3fff0334 0018001f 00fecad0 40004e4f  
16:33:39.192 -> 3ffffd60:  4010219f 82fecad0 3fff05fc 000d000f  
16:33:39.227 -> 3ffffd70:  000012d0 3fff03ec 0018001f 00100790  
16:33:39.227 -> 3ffffd80:  3fff05a4 00000000 00000000 00000000  
16:33:39.227 -> 3ffffd90:  3fff0504 3fff05a4 3ffee994 4020c1cc  
16:33:39.227 -> 3ffffda0:  3ffe98c0 7fffffff 00000000 4020c500  
16:33:39.227 -> 3ffffdb0:  00000000 4bc6a7f0 2d0e5604 402054e1  
16:33:39.227 -> 3ffffdc0:  007a1200 73b727a2 4bc6a700 40204f10  
16:33:39.227 -> 3ffffdd0:  3ffe98cc 00000000 40100294 00005b59  
16:33:39.227 -> 3ffffde0:  00000000 00000000 00000000 00000000  
16:33:39.263 -> 3ffffdf0:  00000001 3ffee994 3ffffe50 4020c4d4  
16:33:39.263 -> 3ffffe00:  00001370 00000002 3ffee994 401000e3  
16:33:39.263 -> 3ffffe10:  3fff05a4 3ffee994 3ffffe50 402051d5  
16:33:39.263 -> 3ffffe20:  00005b59 0000004d 00000020 40100a50  
16:33:39.263 -> 3ffffe30:  00418937 0164d0af 00000010 0000004d  
16:33:39.263 -> 3ffffe40:  3fff05a4 3ffee970 3ffee994 402053e8  
16:33:39.263 -> 3ffffe50:  3fff0034 73b709df 40204ae4 4020c4b0  
16:33:39.263 -> 3ffffe60:  00000000 3ffeecf4 40100294 00005b59  
16:33:39.298 -> 3ffffe70:  00000000 00000000 00000000 00000000  
16:33:39.298 -> 3ffffe80:  00000001 3ffee994 3ffffee0 4020544d  
16:33:39.298 -> 3ffffe90:  00000001 3ffee994 3ffeec5c 401000e3  
16:33:39.298 -> 3ffffea0:  3ffee9fb 3ffee994 3ffffee0 402051d5  
16:33:39.298 -> 3ffffeb0:  00005b59 00000002 00000000 fffffffe  
16:33:39.298 -> 3ffffec0:  ffffffff 3fffc6fc 0000000c 00000000  
16:33:39.298 -> 3ffffed0:  3ffee970 00000002 3ffee994 40205253  
16:33:39.298 -> 3ffffee0:  3fff0124 3fffdad0 40204a98 40205434  
16:33:39.333 -> 3ffffef0:  3ffee9fb 00000002 3ffeec5c 00000030  
16:33:39.333 -> 3fffff00:  00000000 4bc6a7f0 3d2f1a9f 3ffeec5c  
16:33:39.333 -> 3fffff10:  3ffee994 3ffee970 3ffee994 4020540d  
16:33:39.333 -> 3fffff20:  3ffee9d4 3ffee970 40100294 00005b58  
16:33:39.333 -> 3fffff30:  00000000 4bc6a7f0 424dd2f1 00000000  
16:33:39.333 -> 3fffff40:  00000000 00000000 4bc6a7f0 3ffeec5c  
16:33:39.333 -> 3fffff50:  3ffee994 3ffee994 3ffee970 4020542d  
16:33:39.333 -> 3fffff60:  007a1200 73b607b7 3ffee900 4020606d  
16:33:39.369 -> 3fffff70:  3ffee994 3ffee970 3ffee994 4020c524  
16:33:39.369 -> 3fffff80:  00000000 00000001 3ffee970 4020622f  
16:33:39.369 -> 3fffff90:  3fffdad0 00000000 3ffee970 40201353  
16:33:39.369 -> 3fffffa0:  3fffdad0 00000000 3ffeec1c 402082a8  
16:33:39.369 -> 3fffffb0:  feefeffe feefeffe 3ffe8534 40100cdd  
16:33:39.369 -> <<<stack<<<
16:33:39.405 -> 
16:33:39.405 ->  ets Jan  8 2013,rst cause:2, boot mode:(3,6)
16:33:39.405 -> 
16:33:39.405 -> load 0x4010f000, len 1392, room 16 
16:33:39.405 -> tail 0
16:33:39.405 -> chksum 0xd0
16:33:39.405 -> csum 0xd0
16:33:39.405 -> v3d128e5c
16:33:39.405 -> ~ld

Ce message m'indique donc que la raison de rst est la "cause 2". Après quelques recherches, il semblerait que ceci soit dû à une action "trop longue", qui imposerait le reboot de la carte. J'ai aussi lu que l'utilisation de la fonction yield() pouvait éventuellement résoudre le problème.

Seulement après de nombreux essais, je ne parviens pas à trouver la solution. Compte tenu du fait que le problème ne se produit qu'avec le moteur, et que les seules lignes différentes entre avec et sans le stepper font référence à la bibliothèque Stepper.h, j'ai pensé que le yield() devrait plutôt être placé directement dans cette bibliothèque. Mais mais petites connaissances en code ne m'ont pas pas permises de savoir où le placer dans le fichier .cpp ...

Je m'en remets donc à vous pour m'aiguiller sur ce petit problème. Je vous place aussi ci dessous l'extrait de code en question.

Bonne journée

  if (deviceId == "5e85e5e8ce60582b5f8872f2") // Device ID of first device
  {     
    Serial.print("Turn on device id: ");
    Serial.println(deviceId);
    tempo=millis();
    while (millis()-tempo<1000){
      digitalWrite(14, HIGH);
      myStepper.setSpeed(motorSpeed); // Ligne ajoutée pour le stepper
      myStepper.step(2048);  // Ligne ajoutée pour le stepper
      
    }
    digitalWrite(14, LOW);
  }    
}

Bonjour

ajouter yield() dans le boucle while() ci dessus. sans cela le code, dans la boucle, accapare totalement l'ESP8266 sans laisser de temps aux tâches d'arrière plan.

S'assurer que libairie utilisée est compatible ESP8266, ce qui semble être le cas au vu des tutoriels rencontrés

Bonjour al1fch,

Merci pour votre réponse.

J'ai essayé de placer le yield() à tous les endroits possibles dans la boucle while() mais rien n'y fait...
J'ai aussi essayé de le placer dans le .cpp, mais soit je ne l'ai pas mis au bon endroit, soit ce n'est pas ici qu'il faut le mettre.

Quoiqu'il en soit, je sèche !

Ne le mets pas dans la bibliothèque. Mets le ici :

while (millis()-tempo<1000){ yield();

Si ça ne marche pas il y a peut-être un autre endroit dans ton code qui accapare le processeur : poste ton code entier

Bonsoir lesept,

Toujours pas.

Voici le code ci-dessous. Pour info, il s'agit d'un code récupéré ici, et pour être honnête, une bonne partie m'est abstraite ::slight_smile: Cependant il est fonctionnel lorsque je l'utilise tel qu'il a été écrit. C'est seulement depuis l'ajout de la boucle while() que le reboot survient.

#include <Arduino.h>
#include <ESP8266WiFi.h>
#include <ESP8266WiFiMulti.h>
#include <WebSocketsClient.h> //  https://github.com/kakopappa/sinric/wiki/How-to-add-dependency-libraries
#include <ArduinoJson.h> // https://github.com/kakopappa/sinric/wiki/How-to-add-dependency-libraries
#include <StreamString.h>
#include <Stepper.h>

#include <AceButton.h> // https://github.com/bxparks/AceButton
using namespace ace_button;

double tempo;
const int stepsPerRevolution = 32*64;  
int motorSpeed = 10;
Stepper myStepper(stepsPerRevolution, 5, 0, 4, 2);
ESP8266WiFiMulti WiFiMulti;
WebSocketsClient webSocket;
WiFiClient client;

#define MyApiKey "cf1ec************a80eaa184" 
#define MySSID "********" 
#define MyWifiPassword "*********" 

#define HEARTBEAT_INTERVAL 300000 // 5 Minutes 

uint64_t heartbeatTimestamp = 0;
bool isConnected = false;

// Switch
const int BUTTON_PIN = D1; //TODO:  pin number attached to the button.
AceButton button(BUTTON_PIN);
 
void handleEvent(AceButton*, uint8_t, uint8_t);
void setPowerStateOnServer(String deviceId, String value);

// deviceId is the ID assgined to your smart-home-device in sinric.com dashboard. Copy it from dashboard and paste it here

void turnOn(String deviceId) {

  if (deviceId == "5e85e5e8ce60582b5f8872f2") // Device ID of first device
  {     
    Serial.print("Turn on device id: ");
    Serial.println(deviceId);
    tempo=millis();
    while (millis()-tempo<10000){ yield();
      digitalWrite(14, HIGH); 
      myStepper.setSpeed(motorSpeed);
      myStepper.step(2048);
    }
    digitalWrite(14, LOW);
  }    
}

void turnOff(String deviceId) {
   if (deviceId == "5e85e5e8ce60582b5f8872f2") // Device ID of first device
   {  
     Serial.print("Turn off Device ID: ");
     Serial.println(deviceId);
   }  
}

void webSocketEvent(WStype_t type, uint8_t * payload, size_t length) {
  switch(type) {
    case WStype_DISCONNECTED:
      isConnected = false;    
      Serial.printf("[WSc] Webservice disconnected from sinric.com!\n");
      break;
    case WStype_CONNECTED: {
      isConnected = true;
      Serial.printf("[WSc] Service connected to sinric.com at url: %s\n", payload);
      Serial.printf("Waiting for commands from sinric.com ...\n");        
      }
      break;
    case WStype_TEXT: {
        Serial.printf("[WSc] get text: %s\n", payload);
        // Example payloads

        // For Switch or Light device types
        // {"deviceId": xxxx, "action": "setPowerState", value: "ON"} // https://developer.amazon.com/docs/device-apis/alexa-powercontroller.html

        // For Light device type
        // Look at the light example in github
          
#if ARDUINOJSON_VERSION_MAJOR == 5
        DynamicJsonBuffer jsonBuffer;
        JsonObject& json = jsonBuffer.parseObject((char*)payload);
#endif
#if ARDUINOJSON_VERSION_MAJOR == 6        
        DynamicJsonDocument json(1024);
        deserializeJson(json, (char*) payload);      
#endif        
        String deviceId = json ["deviceId"];     
        String action = json ["action"];
        
        if(action == "setPowerState") { // Switch or Light
            String value = json ["value"];
            if(value == "ON") {
                turnOn(deviceId);
            } else {
                turnOff(deviceId);
            }
        }
        else if (action == "test") {
            Serial.println("[WSc] received test command from sinric.com");
        }
      }
      break;
    case WStype_BIN:
      Serial.printf("[WSc] get binary length: %u\n", length);
      break;
  }
}

void setup() {
  Serial.begin(115200);
  pinMode(14, OUTPUT);
  pinMode(5, OUTPUT);
  pinMode(4, OUTPUT);
  pinMode(0, OUTPUT);
  pinMode(2, OUTPUT);

   
  WiFiMulti.addAP(MySSID, MyWifiPassword);
  Serial.println();
  Serial.print("Connecting to Wifi: ");
  Serial.println(MySSID);  

  // Waiting for Wifi connect
  while(WiFiMulti.run() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  if(WiFiMulti.run() == WL_CONNECTED) {
    Serial.println("");
    Serial.print("WiFi connected. ");
    Serial.print("IP address: ");
    Serial.println(WiFi.localIP());
  }

  // Setup button
  // Button uses the built-in pull up register.
  pinMode(BUTTON_PIN, INPUT_PULLUP); // INPUT_PULLUP so no need a 10K resistor

  button.init(BUTTON_PIN);
  button.setEventHandler(handleEvent);

  // server address, port and URL
  webSocket.begin("iot.sinric.com", 80, "/");

  // event handler
  webSocket.onEvent(webSocketEvent);
  webSocket.setAuthorization("apikey", MyApiKey);
  
  // try again every 5000ms if connection has failed
  webSocket.setReconnectInterval(5000);   // If you see 'class WebSocketsClient' has no member named 'setReconnectInterval' error update arduinoWebSockets
}

void loop() {
  webSocket.loop();
  button.check();

  if(isConnected) {
      uint64_t now = millis();
      
      // Send heartbeat in order to avoid disconnections during ISP resetting IPs over night. Thanks @MacSass
      if((now - heartbeatTimestamp) > HEARTBEAT_INTERVAL) {
          heartbeatTimestamp = now;
          webSocket.sendTXT("H");          
      }
  }   
}
 
void setPowerStateOnServer(String deviceId, String value) {
#if ARDUINOJSON_VERSION_MAJOR == 5
  DynamicJsonBuffer jsonBuffer;
  JsonObject& root = jsonBuffer.createObject();
#endif
#if ARDUINOJSON_VERSION_MAJOR == 6        
  DynamicJsonDocument root(1024);
#endif        

  root["deviceId"] = deviceId;
  root["action"] = "setPowerState";
  root["value"] = value;
  StreamString databuf;
#if ARDUINOJSON_VERSION_MAJOR == 5
  root.printTo(databuf);
#endif
#if ARDUINOJSON_VERSION_MAJOR == 6        
  serializeJson(root, databuf);
#endif  
  webSocket.sendTXT(databuf);
}

void handleEvent(AceButton* /* button */, uint8_t eventType,
    uint8_t /* buttonState */) {
  switch (eventType) {
    case AceButton::kEventPressed:
      Serial.println("kEventPressed");
      setPowerStateOnServer("<device id>", "ON");
      break;
    case AceButton::kEventReleased:
      Serial.println("kEventReleased");
      break;
  }
}

Merci pour votre aide !

Et si tu changes

double tempo;

en

unsigned long tempo;

Toujours pas... Même en modifiant la position du yield()

gugutz_96:
J'ai ensuite souhaité que l'activation de cette LED enclenche la rotation de mon stepper. Et c'est là que ça se complique... Lorsque j'active ma broche, le moteur se lance. Cependant celui-ci s'arrête au bout d'environ 2sec et la carte reboot

Une alimentation séparée pour le moteur ?

achess:
Une alimentation séparée pour le moteur ?

Oui je récupère les 5V d'une arduino uno

Il faudrait tester un code plus simple :

#include <Arduino.h>
#include <Stepper.h>


unsigned long tempo;
const int stepsPerRevolution = 32 * 64;
int motorSpeed = 10;
Stepper myStepper(stepsPerRevolution, 5, 0, 4, 2);
bool isConnected = false;
// Switch
const int BUTTON_PIN = D1; //TODO:  pin number attached to the button.

void turnOn(String deviceId) {
  Serial.print("Turn on device id: ");
  Serial.println(deviceId);
  tempo = millis();
  while (millis() - tempo < 10000) {
    yield();
    digitalWrite(14, HIGH);
    myStepper.setSpeed(motorSpeed);
    myStepper.step(2048);
  }
  digitalWrite(14, LOW);
}

void turnOff(String deviceId) {
  Serial.print("Turn off Device ID: ");
  Serial.println(deviceId);
}

void setup() {
  Serial.begin(115200);
  pinMode(14, OUTPUT);
  pinMode(5, OUTPUT);
  pinMode(4, OUTPUT);
  pinMode(0, OUTPUT);
  pinMode(2, OUTPUT);

  // Setup button
  // Button uses the built-in pull up register.
  pinMode(BUTTON_PIN, INPUT_PULLUP); // INPUT_PULLUP so no need a 10K resistor

  turnOn("device");
  delay(2000);
  turnOff("device");
}

void loop() {
}

(pas vérifié)

Au passage, ceci me parait douteux :
pinMode(0, OUTPUT);

L'ESP8266 a des pins dédiées et il faut faire attention à celles qu'on utilise. Est-ce toi qui les as choisies ou bien elles étaient déjà comme ça ?

Je viens de tester avec un code simplifié (un BP qui déclenche le stepper) et le résultat est le même, reboot au bout d'environ 3sec.

#include <Arduino.h>
#include <Stepper.h>


unsigned long tempo;
const int stepsPerRevolution = 32 * 64;
int motorSpeed = 10;
Stepper myStepper(stepsPerRevolution, 14, 13, 12, 15);

void turnOn() {
  Serial.print("Turn on device");
  tempo = millis();
  while (millis() - tempo < 10000) {
    yield();
    digitalWrite(4, HIGH);
    myStepper.setSpeed(motorSpeed);
    myStepper.step(2048);
  }
  digitalWrite(4, LOW);
}

/*void turnOff(String deviceId) {
  Serial.print("Turn off Device ID: ");
  Serial.println(deviceId);
}
*/
void setup() {
  Serial.begin(115200);
  pinMode(14, OUTPUT); //IN1 sur D5
  pinMode(13, OUTPUT); //IN2 sur D6
  pinMode(12, OUTPUT); //IN3 sur D7
  pinMode(15, OUTPUT); //IN4 sur D8
  pinMode(4, OUTPUT); //LED
  pinMode(5, INPUT_PULLUP);

}


void loop() {
  if (digitalRead(5)==LOW){
    turnOn();
  }
}

J'ai ensuite re-simplifié le code en mettant en commentaire les deux lignes myStepper de la boucle While() et en gardant le yield(), et là aucun reboot, ma LED reste bien allumée pendant 10sec puis s'éteint. Il semblerait donc que ce soit ces deux lignes là qui m'embêtent, mais je ne sais pas quoi modifier...

lesept:
Au passage, ceci me parait douteux :
pinMode(0, OUTPUT);

L'ESP8266 a des pins dédiées et il faut faire attention à celles qu'on utilise. Est-ce toi qui les as choisies ou bien elles étaient déjà comme ça ?

C'est moi qui les ai choisies. Comme j'ai branché les 4 fils de mon moteur sur les broches D1, D2, D3 et D4, j'ai utilisé les équivalences, à savoir D1=5, D2=4, D3=0, et D4=2. Ce n'est pas correct ?
J'ai refait le montage avec les pins D5, D6, D7 et D8.

Le problème peut venir de là:

  myStepper.step(2048);

Si ça fait avancer le moteur de 2048 pas, tu peux essayer pas à pas avec un yield() entre chaque pas

for (int i=0;i<2048;i++) { myStepper.step(1); yield(); }

C'est quoi comme moteur ? As-tu cherché des exemples de mise en oeuvre de ce moteur avec un NodeMCU, pour voir quelles pins sont utilisées ? Par exemple ici

Un grand merci cela fonctionne ! ;D

En revanche, pensez-vous qu'il soit possible d'introduire le yield() directement dans la bibliothèque pour s'affranchir de cette boucle for() et que celà devienne transparent dans le code ?

Pourquoi pas?

En revanche, pensez-vous qu'il soit possible d'introduire le yield() directement dans la bibliothèque pour s'affranchir de cette boucle for() et que celà devienne transparent dans le code ?

L'inconvénient est mineur.
La modification d'une librairie entraîne la perte de la modification en cas de mise à jour de celle-ci.

laisser un yield() bien visible permet de garder a l'esprit un besoin de l'EDP8266.