ESP8266 keeps resetting .....Solved #8

This is the code for ESPNow two way controller. It reads some data from Serial and beams it to a slave which in turn sends some other by return. Code based on this discussion : ESP8266 with ESP-NOW as an alternative to nRF24L01+ - Exhibition / Gallery - Arduino Forum

This is the Error I am getting in Serial monitor :

19:56:10.510 -> --------------- CUT HERE FOR EXCEPTION DECODER ---------------
19:56:10.544 -> 
19:56:10.544 ->  ets Jan  8 2013,rst cause:2, boot mode:(3,6)
19:56:10.544 -> 
19:56:10.544 -> load 0x4010f000, len 3584, room 16 
19:56:10.544 -> tail 0
19:56:10.544 -> chksum 0xb0
19:56:10.544 -> csum 0xb0
19:56:10.544 -> v2843a5ac
19:56:10.544 -> ~ld
19:56:10.786 -> 0,0,0,0,0
19:56:10.786 -> 
19:56:10.786 -> User exception (panic/abort/assert)
19:56:10.786 -> --------------- CUT HERE FOR EXCEPTION DECODER ---------------
19:56:10.786 -> 
19:56:10.786 -> Panic core_esp8266_main.cpp:133 __yield
19:56:10.786 -> 
19:56:10.786 -> >>>stack>>>
19:56:10.786 -> 
19:56:10.786 -> ctx: sys
19:56:10.786 -> sp: 3fffed80 end: 3fffffb0 offset: 0000
19:56:10.786 -> 3fffed80:  00000000 00000000 00000020 40100154  
19:56:10.821 -> 3fffed90:  000000fe 00000000 00000000 00000000  
19:56:10.821 -> 3fffeda0:  00000000 00000000 00000000 3ffee630

Any reason why this is resetting ?

The full code is below :

#include <ESP8266WiFi.h>
#include <espnow.h>
#include "SSD1306Wire.h"

// This is the MAC Address of the connected slave
uint8_t remoteMac[] = {0x36, 0x33, 0x33, 0x33, 0x33, 0x33};

#define WIFI_CHANNEL 4

const byte numChars = 100;              // Adjust based on max number of char + null
char receivedChars[numChars];           // Holder for incoming data
bool started = false;                   //True: Message is started
bool ended   = false;                   //True: Message is finished
bool newData = false;
char incomingByte ;                     //Variable to store the incoming byte
byte charIndex ;                        //Index of received array

uint16_t txMsgCount ;

unsigned long serTimeOutMs = millis();
unsigned long serTimeOutIntvl = 2000;

struct DataStruct {                     // The controller will send DI and AI data
  uint16_t DIState;
  uint16_t AIVal[16];
};

struct RDataStruct {                    // The Controller will receive DO and AO data
  uint16_t DOState;
  uint16_t AOVal[4];
};

DataStruct sendingData;                 // Data to Slave

RDataStruct receivedData;               // Data from slave

unsigned long lastSentMillis;
unsigned long sendIntervalMillis = 500;  // This decides how often the data is sent. The Slave will folow this ...

unsigned long lastBlinkMillis;
unsigned long fastBlinkMillis = 100;
unsigned long slowBlinkMillis = 1000;
unsigned long blinkIntervalMillis = slowBlinkMillis;

byte ledPin = 14;

// Initialize the OLED display using Wire library
SSD1306Wire  display(0x3c, D1, D2);       //  SDA = D1 and SCL = D2

//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

void setup() {
  Serial.begin(115200);

  pinMode(ledPin, OUTPUT);

  WiFi.mode(WIFI_STA);
  WiFi.disconnect();

  display.init();
  display.flipScreenVertically();
  display.setTextAlignment(TEXT_ALIGN_LEFT);
  display.setFont(ArialMT_Plain_16);

  if (esp_now_init() != 0) {
    display.drawString(0, 10, "ESP-NOW INIT");
    display.drawString(0, 35, "FAILED...");
    display.display();
    while (true) {};
  }

  esp_now_set_self_role(ESP_NOW_ROLE_COMBO);
  esp_now_add_peer(remoteMac, ESP_NOW_ROLE_COMBO, WIFI_CHANNEL, NULL, 0);

  esp_now_register_send_cb(sendCallBackFunction);
  esp_now_register_recv_cb(receiveCallBackFunction);

  waitingForHostMsg();

}

//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

void loop() {
  readSerial();
  if ( millis() - serTimeOutMs > serTimeOutIntvl ) {
    waitingForHostMsg();
    txMsgCount = 0;
  } 
  blinkLed();
}

//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

//READ DI AND AI VALUES FROM MASTER SCREEN

void readSerial()                                  // Read from the LabVIEW code via Serial and prepare to Send it to Controller
{
  while (Serial.available() > 0)                  // Is there any data in Serial buffer ??
  {
    incomingByte = Serial.read();                 // Read the incoming byte .. this removes it from Serial buffer

    if (incomingByte == '<')                       // Wait for the start marker..
    {
      started = true;                              // Got the start marker. Set receive boolean to true..
      charIndex = 0;
      receivedChars[charIndex] = '\0';             // Throw away any incomplete characters
    }

    else if (incomingByte == '>')                  // Wait for the end marker
    {
      ended = true;                                // Got the end marker ...
      break;                                       // Stop reading - exit from while loop!
    }

    else                                           // Read the message from the Serial buffer !
    {
      if (charIndex < numChars)                    // Make sure there is room
      {
        receivedChars[charIndex] = incomingByte;   // Add char to array
        charIndex++;
        receivedChars[charIndex] = '\0';           // Add NULL to end.. keep on adding
      }
    }
  }

  if (started && ended)                           // All data read from the Serial buffer... process it.
  {
    receivedChars[charIndex] = '\0';
    parseData();                                  // Read and store the Voltage info in variables.
    charIndex = 0;
    started = false;
    ended = false;
    serTimeOutMs = millis();
  }
}

//**********************************

// PARSE THE DI AND AI VALUES & SEND TO SLAVE

void parseData() {                                  // split the data into its parts

  char * strtokIndx;                                // this is used by strtok() as an index

  strtokIndx = strtok(receivedChars, ",");          // get the first part - the string
  sendingData.DIState = atoi(strtokIndx);

  for ( byte count = 0; count < 16; count++) {
    strtokIndx = strtok(NULL, ",");                 // this continues where the previous call left off
    sendingData.AIVal[count] = atoi(strtokIndx);    // convert this part to an integer
  }

  sendDI_AIData();
}

//**********************************

// SEND THE DI AND AI VALUES TO SLAVE VIA WIFI

void sendDI_AIData() {                                 // Send the DI and AI values got via Serial to the Slave
  
  uint8_t byteArray[sizeof(sendingData)];
  memcpy(byteArray, &sendingData, sizeof(sendingData));
  esp_now_send(NULL, byteArray, sizeof(sendingData));  // NULL means send to all peers
  delay(2);                                            // A small delay for the Slave response to reach back
}

//**********************************

// GET THE DO AND AO VALUES FROM SLAVE VIA WIFI
// AND THEN PRINT IT TO THE MASTER SCREEN

void receiveCallBackFunction(uint8_t *senderMac, uint8_t *incomingData, uint8_t len) {
  memcpy(&receivedData, incomingData, sizeof(receivedData));
  // This data will be processed inside Send call back function..
}

//**********************************

//

void sendCallBackFunction(uint8_t* mac, uint8_t sendStatus) {
  char receiveBuffer[50];                           // Buffer for  the DO and AO state from controller
  if (sendStatus == 0) {
    blinkIntervalMillis = fastBlinkMillis;
    sprintf(receiveBuffer, "%u,%u,%u,%u,%u", receivedData.DOState, receivedData.AOVal[0], receivedData.AOVal[1], receivedData.AOVal[2], receivedData.AOVal[3]);
    Serial.println(receiveBuffer);                   // Update the Master with DO and AO values..
    char msg[10];
    display.clear();
    txMsgCount++ ;
    if (txMsgCount > 1000 ) txMsgCount = 0;
    sprintf(msg, "MsgCount : %5u", txMsgCount);
    display.drawString(0, 20, msg );
    display.display();
  }
 
  else {
    
    blinkIntervalMillis = slowBlinkMillis;
    sprintf(receiveBuffer, "%u,%u,%u,%u,%u", 0,0,0,0,0);
    Serial.println(receiveBuffer);                   // Update the Master with Zero for DO and AO values as Slave not on line..
    display.drawString(0, 20, "Link Fail." );
    display.display();
  }
}

//**********************************

// BLINK LED FAST IF SEND IS SUCCESS ELSE SLOW

void blinkLed() {
  if (millis() - lastBlinkMillis >= blinkIntervalMillis) {
    lastBlinkMillis += blinkIntervalMillis;
    digitalWrite(ledPin, ! digitalRead(ledPin));
  }
}

//**********************************

// HOST SERIAL IS OFF MESSAGE

void waitingForHostMsg() {
  display.clear();
  display.drawString(0, 10, "WAITING FOR");
  display.drawString(0, 35, "SERIAL HOST.");
  display.display();
}

could be the watchdog timer is reseting
in your loop try calling yeild() to allow other tasks to run

  if ( millis() - serTimeOutMs > serTimeOutIntvl ) {
    waitingForHostMsg();
    txMsgCount = 0;
    yeild();
  }

sp. "yield"

Have you thought about adding serial prints to your code to find the line that the error is happening?

a line of code
serial.println("1")
a line of code
serial.println("2")
a line of code
serial.println("3")

Me, I'm going to venture the issues is with the #include "SSD1306Wire.h" library.

strtokIndx = strtok(receivedChars, ",");          // get the first part - the string

I suspect it may be happening here, strtok() has a bug in the esp core as far as i know.
Although reset cause 2 is an external reset (and they may have fixed the bug by now)
Your code for serial reception appears to be based on Robin2 's Serial input basics, and when people use it on an ESP and experience a crash, it is usually strtok() that is to blame. You can create a function quite easily that more or less does what strtok() does in this context.
By the way, your readSerial() function should have a yield() inside the while loop. theoretically it could cause a wdt reset, though admittedly it would take quite a lot of data to get passed the 2.5s mark.

It is worth making sure the power supply to the esp8266 is good as I can't tell you how many times I have been stumped by strange errors/behaviours in my projects only to finally discover it was just down to power supply.

I tend to put a good sized smoothing capacitor on the power inputs to try and smooth out the spikes these units cause when using wifi.

when I get those it is usually because memory is being overwritten somehow, could be a data area is not large enough and something is corrupted or a list of say ws2811 lights is not matching the number in the string. check the listing for compiler warnings too.

A big thanks to all those who responded and pointed in all possible problem areas - memory issues / power supply issues and so on.

Finally it turns out that the SSD1306 & WiFi cannot co-exist in this particular board. ( Thanks IdahoWalker) I removed that and all issues were over and of course i inserted yield() within the Serial Read - just to be sure.

Just to prove the point I have another code as below that uses the same Serial read and print but does not use WiFi and its working good on the same module. This is a big learning !!

#include "SSD1306Wire.h"

const byte numChars = 100;               // Adjust based on max number of char + null
char receivedChars[numChars];            // Holder for incoming data
bool started = false;                    //True: Message is started
bool ended   = false;                    //True: Message is finished
bool newData = false;
char incomingByte ;                      //Variable to store the incoming byte
byte charIndex ;                         //Index of received array
bool serialLinkFlag ;

uint16_t rxMsgCount, txMsgCount ;

unsigned long serTimeOutMs = millis();
unsigned long serTimeOutIntvl = 2000;

struct DataStruct {                      // The controller will send DI and AI data
  uint16_t DIState;
  uint16_t AIVal[4];
};

DataStruct receivedData;                 // Data to Slave


// Initialize the OLED display using Wire library
SSD1306Wire  display(0x3c, D1, D2); //  SDA = D1 and SCL = D2

//$$$$$$$$$$$$$$$$$$$$$$$

void setup()
{
  Serial.begin(115200);

  pinMode(LED_BUILTIN, OUTPUT);               // Lights up for LOW ..
  digitalWrite(LED_BUILTIN, HIGH);

  display.init();
  display.flipScreenVertically();
  display.setTextAlignment(TEXT_ALIGN_LEFT);
  display.setFont(ArialMT_Plain_16);
  display.drawString(0, 10, "SERIAL_MCU");
  display.drawString(0, 35, "LV_CHK.INO.");
  display.display();
}

//$$$$$$$$$$$$$$$$$$$$$$$

void loop()
{
  readSerial();
  if ( millis() - serTimeOutMs > serTimeOutIntvl ) {
    display.clear();
    display.drawString(0, 10, "WAITING  FOR");
    display.drawString(0, 35, "SERIAL  HOST.");
    display.display();
    txMsgCount = 0; 
  }
}

//$$$$$$$$$$$$$$$$$$$$$$$

void readSerial()                                  // Read from the LabVIEW code via Serial and prepare to Send it to Controller
{
  while (Serial.available() > 0)                  // Is there any data in Serial buffer ??
  {
    display.clear();
    digitalWrite(LED_BUILTIN, LOW);
    incomingByte = Serial.read();                 // Read the incoming byte .. this removes it from Serial buffer

    if (incomingByte == '<')                       // Wait for the start marker..
    {
      started = true;                              // Got the start marker. Set receive boolean to true..
      charIndex = 0;
      receivedChars[charIndex] = '\0';             // Throw away any incomplete characters
    }

    else if (incomingByte == '>')                  // Wait for the end marker
    {
      ended = true;                                // Got the end marker ...
      break;                                       // Stop reading - exit from while loop!
    }

    else                                           // Read the message from the Serial buffer !
    {
      if (charIndex < numChars)                    // Make sure there is room
      {
        receivedChars[charIndex] = incomingByte;   // Add char to array
        charIndex++;
        receivedChars[charIndex] = '\0';           // Add NULL to end.. keep on adding
      }
    }
  }

  if (started && ended)                           // All data read from the Serial buffer... process it.
  {
    receivedChars[charIndex] = '\0';
    parseData();                                  // Read and store the Voltage info in variables.
    charIndex = 0;
    started = false;
    ended = false;
    serialLinkFlag = true;
    serTimeOutMs = millis();
  }
}

//============

void parseData() {                                  // split the data into its parts

  char * strtokIndx;                                // this is used by strtok() as an index

  strtokIndx = strtok(receivedChars, ",");          // get the first part - the string
  receivedData.DIState = atoi(strtokIndx);

  for ( byte count = 0; count < 4; count++) {
    strtokIndx = strtok(NULL, ",");                 // this continues where the previous call left off
    receivedData.AIVal[count] = atoi(strtokIndx);    // convert this part to an integer
  }
  writeSerial();
}

//============

void writeSerial() {
  char receiveBuffer[50];                          // Read the DO and AO state from controller
  sprintf(receiveBuffer, "%u,%u,%u,%u,%u", receivedData.DIState, receivedData.AIVal[0], receivedData.AIVal[1], receivedData.AIVal[2], receivedData.AIVal[3]);
  Serial.println(receiveBuffer);                   // Update the LV Test Code  with the DI and AI values..
  char msg[10];
  txMsgCount++ ;
  if (txMsgCount > 1000 ) txMsgCount = 0;
  sprintf(msg, "MsgCount : %5u", txMsgCount);
  display.drawString(0, 20, msg );
  display.display();
  digitalWrite(LED_BUILTIN, HIGH);
}