ESP8266 interrupts definition causing reboot loop

Hardware/Software used:

  • NodeMCU ESP8266 Module
  • Blynk version: 1.2.0
  • ESP Library version: 3.1.1

Hi, I am using this “weather station” code that I did a while ago, and it used to work just fine on the old blynk app. Since it is now discontinued I needed to rebuild this project for the new blynk environment. But I am having infinite reboots caused by the interrupts definition. I determined that if I take out these two lines of code the issue goes away. Does somebody know what might be causing this issue?

attachInterrupt(digitalPinToInterrupt(reed_switch_pin_pluviometro), callbackPluviometro, FALLING);
attachInterrupt(digitalPinToInterrupt(reed_switch_pin_anemometro), callbackAnemometro, FALLING);

I enter on a re-boot loop and the error message says something like this:

10:55:59.916 -> --------------- CUT HERE FOR EXCEPTION DECODER ---------------
10:55:59.949 -> 
10:55:59.949 ->  ets Jan  8 2013,rst cause:2, boot mode:(3,7)
10:55:59.949 -> 
10:55:59.949 -> load 0x4010f000, len 3424, room 16 
10:55:59.949 -> tail 0
10:55:59.949 -> chksum 0x2e
10:55:59.949 -> load 0x3fff20b8, len 40, room 8 
10:55:59.949 -> tail 0
10:55:59.949 -> chksum 0x2b
10:55:59.949 -> csum 0x2b
10:55:59.949 -> v00048650
10:55:59.949 -> ~ld
10:56:00.029 -> ISR not in IRAM!
10:56:00.029 -> 
10:56:00.029 -> User exception (panic/abort/assert)
10:56:00.029 -> --------------- CUT HERE FOR EXCEPTION DECODER ---------------
10:56:00.029 -> 
10:56:00.029 -> Abort called
10:56:00.029 -> 
10:56:00.029 -> >>>stack>>>
10:56:00.029 -> 
10:56:00.029 -> ctx: cont
10:56:00.029 -> sp: 3fffff00 end: 3fffffd0 offset: 0000
10:56:00.029 -> 3fffff00:  3ffeffbc 00000020 3fffff70 3ffeffbc  
10:56:00.029 -> 3fffff10:  000000fe 00000000 00000000 00000000  
10:56:00.064 -> 3fffff20:  00000000 00000000 00000000 402071ba  
10:56:00.064 -> 3fffff30:  3fffefd0 000000fe 00000000 00ff0000  
10:56:00.064 -> 3fffff40:  5ffffe00 5ffffe00 ffffffff 3ffef060  
10:56:00.064 -> 3fffff50:  00000000 00000002 0000000e 40206322  
10:56:00.064 -> 3fffff60:  4010055d 00000001 3ffeeed8 40206334  
10:56:00.064 -> 3fffff70:  00000000 0012001f 0000000e 40206825  
10:56:00.064 -> 3fffff80:  0001c200 0000001c 00000000 40206428  
10:56:00.104 -> 3fffff90:  3fffdad0 00000000 3ffef034 4020257e  
10:56:00.104 -> 3fffffa0:  feefeffe feefeffe feefeffe feefeffe  
10:56:00.104 -> 3fffffb0:  feefeffe feefeffe feefeffe 40205e8c  
10:56:00.104 -> 3fffffc0:  feefeffe feefeffe 3fffdab0 40100f91  
10:56:00.104 -> <<<stack<<<
10:56:00.104 -> 
10:56:00.104 -> --------------- CUT HERE FOR EXCEPTION DECODER ---------------

// BLYNK -> V4 (lluvia en milimetros)
//       -> V5 (lluvia en milimetros dividido 3)
//       -> V6 (anemometro promedio 10secs)
//       -> V0 (reseteo de lluvia)
//       -> V1 (fecha de reseteo lluvia)
//       -> V2 (anemometro)

#define BLYNK_TEMPLATE_ID "THASH"
#define BLYNK_TEMPLATE_NAME "T1"
#define BLYNK_AUTH_TOKEN "TKN"

#define BLYNK_FIRMWARE_VERSION        "0.1.0"

#define EEPROM_SIZE 512

#include <ESP8266WiFi.h>
#include <BlynkSimpleEsp8266.h>
#include <EEPROM.h>
#include "Time.h"
#include <NTPtimeESP.h>

//** SENSOR PINS
const int reed_switch_pin_pluviometro = 14;      // GPI014, D5
const int reed_switch_pin_anemometro = 12;      // GPIO12, D6

//** DIRECCIONES EEPROM STORAGE
const int eeDir_contador = 100;
const int eeDir_lluvia_milimetros = 150;
const int eeDir_lluvia_milimetros_div3 = 250;
const int eeDir_fecha_reset = 200;
const float rev_rad_conv = 6.28;
const float radio_anemometro = 0.06; // en metros (son 6cm)
const int milimetros_por_cuenta = 1;

// Define NTP Client to get time
NTPtime NTPch("pool.ntp.org");

// Set password to "" for open networks.
char ssid[] = "ssid";		                                        //local wifi network SSID
char pass[] = "passwd";			                                //local network password

BlynkTimer timer;                                                   //config timer

// anemometro variables
int contador_pluviometro = 0;
float lluvia_milimetros = 0;
float lluvia_milimetros_div3 = 0;

// pluviometro variables
int contador_anemometro = 0;
float velocidad_viento = 0;
float velocidad_viento_promedio = 0;
float contador_medicion_viento = 0;
bool procesar_pluviometro = false;

void procesarLluvia() {
  contador_pluviometro += 1;
  lluvia_milimetros = milimetros_por_cuenta * contador_pluviometro;
  lluvia_milimetros_div3 = int(milimetros_por_cuenta * contador_pluviometro / 3);
  grabarEeprom(eeDir_contador, contador_pluviometro);
  grabarEeprom(eeDir_lluvia_milimetros, lluvia_milimetros);
  Blynk.virtualWrite(V4, lluvia_milimetros);
  Blynk.virtualWrite(V5, lluvia_milimetros_div3);
  procesar_pluviometro = false;
}

void procesarViento() {
  contador_medicion_viento += 1;
  velocidad_viento_promedio = (velocidad_viento_promedio + velocidad_viento) / contador_medicion_viento; // para pasar a km/h
  velocidad_viento = contador_anemometro * rev_rad_conv * radio_anemometro; // en m/s
  velocidad_viento = velocidad_viento * 3.6; // para pasar a km/h
  if (contador_medicion_viento >= 10) {
    contador_medicion_viento = 0;
    velocidad_viento_promedio = 0;
  }
  Blynk.virtualWrite(V2, velocidad_viento);
  Blynk.virtualWrite(V6, velocidad_viento_promedio);
  contador_anemometro = 0;
}

void setup() {
  EEPROM.begin(EEPROM_SIZE);
  delay(10);
  iniciarEeprom();
  Serial.begin(115200);
  
  // interrupts
  pinMode(reed_switch_pin_pluviometro, INPUT_PULLUP);
  pinMode(reed_switch_pin_anemometro, INPUT_PULLUP);
  attachInterrupt(digitalPinToInterrupt(reed_switch_pin_pluviometro), callbackPluviometro, FALLING);
  attachInterrupt(digitalPinToInterrupt(reed_switch_pin_anemometro), callbackAnemometro, FALLING);
  timer.setInterval(1000, procesarViento);

  delay(10);
  printWifiStatus();
}

void printWifiStatus() {
  Serial.println();
  Serial.println("Connecting to " + String(ssid));      // Connected to WiFi network
  Blynk.begin(BLYNK_AUTH_TOKEN, ssid, pass);
  delay(20);
  Serial.println(WiFi.localIP());								     //this is local IP for this board
  Serial.println("WiFi connected");
}

void callbackPluviometro() {
  Serial.println("llamo lluvia");
  procesar_pluviometro = true;
}

void callbackAnemometro() {
  Serial.println("llamo anemometro");
  contador_anemometro += 1;
}

void loop() {
  Blynk.run();
  timer.run();
  if (procesar_pluviometro) {
    procesarLluvia();
  }
}

// HELPER FUNCTIONS AND BLYNK TERMINAL

// PARA PODER RESETEAR LA LLUVIA
BLYNK_WRITE(V0) {
  int state = param.asInt();
  strDateTime dateTime = NTPch.getNTPtime(-3.0, 0);
  if (state == 1)
  {
    contador_pluviometro = 0;
    lluvia_milimetros = 0;
    grabarEeprom(eeDir_contador, contador_pluviometro);
    grabarEeprom(eeDir_lluvia_milimetros, lluvia_milimetros);
    grabarEeprom(eeDir_lluvia_milimetros_div3, lluvia_milimetros);
    Blynk.virtualWrite(V4, lluvia_milimetros);
    Blynk.virtualWrite(V5, lluvia_milimetros);
    
    do {
      Serial.println("entro al dowhile");
      dateTime = NTPch.getNTPtime(-3.0, 0);
      delay(50);
    } while (!dateTime.valid);
    if (dateTime.valid) {
      String now =  String(dateTime.day) + "/" + \
                    String(dateTime.month) + "/" + \
                    String(dateTime.year) + " " + \
                    String(dateTime.hour) + ":" + String(dateTime.minute) + ":" + String(dateTime.second);

          Serial.println(now);
          writeStringToEEPROM(eeDir_fecha_reset, now);
          Blynk.virtualWrite(V1, now);
    }
  }
}

void iniciarEeprom() {
  int valor;
  int lluvia_milimetros_lo = EEPROM.read(eeDir_lluvia_milimetros);
  int lluvia_milimetros_hi = EEPROM.read(eeDir_lluvia_milimetros+1);
  valor = (lluvia_milimetros_hi << 8) | lluvia_milimetros_lo;
  lluvia_milimetros = valor;
  Blynk.virtualWrite(V4, lluvia_milimetros);

  int valor_div3;
  int lluvia_milimetros_lo_div3 = EEPROM.read(eeDir_lluvia_milimetros_div3);
  int lluvia_milimetros_hi_div3 = EEPROM.read(eeDir_lluvia_milimetros_div3+1);
  valor_div3 = (lluvia_milimetros_hi_div3 << 8) | lluvia_milimetros_lo_div3;
  lluvia_milimetros_div3 = valor_div3;
  Blynk.virtualWrite(V5, lluvia_milimetros_div3);

  int contador_lo = EEPROM.read(eeDir_contador);
  int contador_hi = EEPROM.read(eeDir_contador+1);
  valor = (contador_hi << 8) | contador_lo;
  contador_pluviometro = valor;

  String now = readStringFromEEPROM(eeDir_fecha_reset);
  Blynk.virtualWrite(V1, now);
}

void grabarEeprom(int dir, int val) {
  byte val_lo = val;      // It automatically stores the lower bits of x
  byte val_hi = val >> 8; // It stores the upper bits of x
  EEPROM.write(dir, val_lo);
  EEPROM.write(dir+1, val_hi);
  EEPROM.commit();
}

void writeStringToEEPROM(int addrOffset, const String &strToWrite) {
  byte len = strToWrite.length();
  EEPROM.write(addrOffset, len);
  for (int i = 0; i < len; i++)
  {
    EEPROM.write(addrOffset + 1 + i, strToWrite[i]);
  }
  EEPROM.commit();
}

String readStringFromEEPROM(int addrOffset) {
  int newStrLen = EEPROM.read(addrOffset);
  char data[newStrLen + 1];
  for (int i = 0; i < newStrLen; i++)
  {
    data[i] = EEPROM.read(addrOffset + 1 + i);
  }
  data[newStrLen] = '\0'; // !!! NOTE !!! Remove the space between the slash "/" and "0" (I've added a space because otherwise there is a display bug)
  return String(data);
}


This is the cause of your issue.

Earlier versions of the ESP8266 core weren’t too fussy about proper ISR definitions, but that has been tightened-up in later releases.

void callbackPluviometro()

and

void callbackAnemometro()

need to be changed to…

IRAM_ATTR void callbackPluviometro()

and

IRAM_ATTR callbackAnemometro()

You also need to move these functions so that they appear before your void setup, or add function prototypes for these ISR handlers near the top of your code.

Also, procesar_pluviometro and contador_anemometro should be volatile variables, and sling serial prints in ISRs isn’t recommended.

You should also restructure your code so that this…

isn’t part of your void loop.

Pete.

Wow you are a life saver! Thanks so much! And according to your recommendations I changed the code to look like this:

// BLYNK -> V4 (lluvia en milimetros)
//       -> V5 (lluvia en milimetros dividido 3)
//       -> V6 (anemometro promedio 10secs)
//       -> V0 (reseteo de lluvia)
//       -> V1 (fecha de reseteo lluvia)
//       -> V2 (anemometro)

#define BLYNK_TEMPLATE_ID "THASH"
#define BLYNK_TEMPLATE_NAME "T1"
#define BLYNK_AUTH_TOKEN "TKN"

#define BLYNK_FIRMWARE_VERSION        "0.1.0"

#define EEPROM_SIZE 512

#include <ESP8266WiFi.h>
#include <BlynkSimpleEsp8266.h>
#include <EEPROM.h>
#include "Time.h"
#include <NTPtimeESP.h>

//** SENSOR PINS
const int reed_switch_pin_pluviometro = 14;      // GPI014, D5
const int reed_switch_pin_anemometro = 12;      // GPIO12, D6

//** DIRECCIONES EEPROM STORAGE
const int eeDir_contador = 100;
const int eeDir_lluvia_milimetros = 150;
const int eeDir_lluvia_milimetros_div3 = 250;
const int eeDir_fecha_reset = 200;
const float rev_rad_conv = 6.28;
const float radio_anemometro = 0.06; // en metros (son 6cm)
const int milimetros_por_cuenta = 1;

// Define NTP Client to get time
NTPtime NTPch("pool.ntp.org");

// Set password to "" for open networks.
char ssid[] = "ssid";		                                        //local wifi network SSID
char pass[] = "passwd";			                                //local network password

BlynkTimer timer;                                                   //config timer

// anemometro variables
volatile int contador_pluviometro = 0;
volatile float lluvia_milimetros = 0;
volatile int lluvia_milimetros_div3 = 0;

// pluviometro variables
int contador_anemometro = 0;
float velocidad_viento = 0;
float velocidad_viento_promedio = 0;
volatile float contador_medicion_viento = 0;

IRAM_ATTR void callbackPluviometro() {
  Serial.println("llamo pluviometro");
  contador_pluviometro += 1;
  lluvia_milimetros = milimetros_por_cuenta * contador_pluviometro;
  lluvia_milimetros_div3 = int(milimetros_por_cuenta * contador_pluviometro / 3);
  grabarEeprom(eeDir_contador, contador_pluviometro);
  grabarEeprom(eeDir_lluvia_milimetros, lluvia_milimetros);
  Blynk.virtualWrite(V4, lluvia_milimetros);
  Blynk.virtualWrite(V5, lluvia_milimetros_div3);
}

void callbackAnemometro() {
  Serial.println("llamo anemometro");
  contador_anemometro += 1;
}

void procesarViento() {
  contador_medicion_viento += 1;
  velocidad_viento_promedio = (velocidad_viento_promedio + velocidad_viento) / contador_medicion_viento; // para pasar a km/h
  velocidad_viento = contador_anemometro * rev_rad_conv * radio_anemometro; // en m/s
  velocidad_viento = velocidad_viento * 3.6; // para pasar a km/h
  if (contador_medicion_viento >= 10) {
    contador_medicion_viento = 0;
    velocidad_viento_promedio = 0;
  }
  Blynk.virtualWrite(V2, velocidad_viento);
  Blynk.virtualWrite(V6, velocidad_viento_promedio);
  contador_anemometro = 0;
}

void printWifiStatus() {
  Serial.println();
  Serial.println("Connecting to " + String(ssid));      // Connected to WiFi network
  Blynk.begin(BLYNK_AUTH_TOKEN, ssid, pass);
  delay(20);
  Serial.println(WiFi.localIP());								     //this is local IP for this board
  Serial.println("WiFi connected");
}

// HELPER FUNCTIONS AND BLYNK TERMINAL

// PARA PODER RESETEAR LA LLUVIA
BLYNK_WRITE(V0) {
  int state = param.asInt();
  strDateTime dateTime = NTPch.getNTPtime(-3.0, 0);
  if (state == 1)
  {
    contador_pluviometro = 0;
    lluvia_milimetros = 0;
    grabarEeprom(eeDir_contador, contador_pluviometro);
    grabarEeprom(eeDir_lluvia_milimetros, lluvia_milimetros);
    grabarEeprom(eeDir_lluvia_milimetros_div3, lluvia_milimetros);
    Blynk.virtualWrite(V4, lluvia_milimetros);
    Blynk.virtualWrite(V5, lluvia_milimetros);
    
    do {
      Serial.println("entro al dowhile");
      dateTime = NTPch.getNTPtime(-3.0, 0);
      delay(50);
    } while (!dateTime.valid);
    if (dateTime.valid) {
      String now =  String(dateTime.day) + "/" + \
                    String(dateTime.month) + "/" + \
                    String(dateTime.year) + " " + \
                    String(dateTime.hour) + ":" + String(dateTime.minute) + ":" + String(dateTime.second);

          Serial.println(now);
          writeStringToEEPROM(eeDir_fecha_reset, now);
          Blynk.virtualWrite(V1, now);
    }
  }
}

void iniciarEeprom() {
  int valor;
  int lluvia_milimetros_lo = EEPROM.read(eeDir_lluvia_milimetros);
  int lluvia_milimetros_hi = EEPROM.read(eeDir_lluvia_milimetros+1);
  valor = (lluvia_milimetros_hi << 8) | lluvia_milimetros_lo;
  lluvia_milimetros = valor;
  Blynk.virtualWrite(V4, lluvia_milimetros);

  int valor_div3;
  int lluvia_milimetros_lo_div3 = EEPROM.read(eeDir_lluvia_milimetros_div3);
  int lluvia_milimetros_hi_div3 = EEPROM.read(eeDir_lluvia_milimetros_div3+1);
  valor_div3 = (lluvia_milimetros_hi_div3 << 8) | lluvia_milimetros_lo_div3;
  lluvia_milimetros_div3 = valor_div3;
  Blynk.virtualWrite(V5, lluvia_milimetros_div3);

  int contador_lo = EEPROM.read(eeDir_contador);
  int contador_hi = EEPROM.read(eeDir_contador+1);
  valor = (contador_hi << 8) | contador_lo;
  contador_pluviometro = valor;

  String now = readStringFromEEPROM(eeDir_fecha_reset);
  Blynk.virtualWrite(V1, now);
}

void grabarEeprom(int dir, int val) {
  byte val_lo = val;      // It automatically stores the lower bits of x
  byte val_hi = val >> 8; // It stores the upper bits of x
  EEPROM.write(dir, val_lo);
  EEPROM.write(dir+1, val_hi);
  EEPROM.commit();
}

void writeStringToEEPROM(int addrOffset, const String &strToWrite) {
  byte len = strToWrite.length();
  EEPROM.write(addrOffset, len);
  for (int i = 0; i < len; i++)
  {
    EEPROM.write(addrOffset + 1 + i, strToWrite[i]);
  }
  EEPROM.commit();
}

String readStringFromEEPROM(int addrOffset) {
  int newStrLen = EEPROM.read(addrOffset);
  char data[newStrLen + 1];
  for (int i = 0; i < newStrLen; i++)
  {
    data[i] = EEPROM.read(addrOffset + 1 + i);
  }
  data[newStrLen] = '\0'; // !!! NOTE !!! Remove the space between the slash "/" and "0" (I've added a space because otherwise there is a display bug)
  return String(data);
}

void setup() {
  EEPROM.begin(EEPROM_SIZE);
  delay(10);
  iniciarEeprom();
  Serial.begin(115200);
  
  // interrupts
  pinMode(reed_switch_pin_pluviometro, INPUT_PULLUP);
  pinMode(reed_switch_pin_anemometro, INPUT_PULLUP);
  attachInterrupt(digitalPinToInterrupt(reed_switch_pin_pluviometro), callbackPluviometro, FALLING);
  attachInterrupt(digitalPinToInterrupt(reed_switch_pin_anemometro), callbackAnemometro, FALLING);
  timer.setInterval(1000, procesarViento);

  delay(10);
  printWifiStatus();
}

void loop() {
  Blynk.run();
  timer.run();
}


Please let me know if you see someting strange or worth mentioning about it! Thank you so much again Pete!

ISRs should be as short and to the point as possible. This has gone from having one unnecessary serial print to being totally bloated.

I also forgot to mention that I wouldn’t use EDPROM if I were you, it’s outdated and unreliable, and will eventually kill your hardware’s NVRAM.
it was replaced by SPIFFS, which has since been replaced by LittleFS.
I’d swap to using LittleFS if I were you.

Pete.

Perfect then, I’ve updated the functions callbackPluviometro behaves just like the other ISR function and I handle the logic in a similar way as procesarViento
I include the new code just with these relevant changes.

IRAM_ATTR void callbackPluviometro() {
  contador_pluviometro += 1;
}

void setup() {
  ...
  timer.setInterval(1000, procesarViento);
  timer.setInterval(1000, procesarLluvia);
}

void procesarLluvia() {
  Serial.println("procesar lluvia");
  // si sucede que cambio este valor ahi recien grabamos los valores
  if (lluvia_milimetros != milimetros_por_cuenta * contador_pluviometro) {
    lluvia_milimetros = milimetros_por_cuenta * contador_pluviometro;
    lluvia_milimetros_div3 = int(milimetros_por_cuenta * contador_pluviometro / 3);
    grabarEeprom(eeDir_contador, contador_pluviometro);
    grabarEeprom(eeDir_lluvia_milimetros, lluvia_milimetros);
    Blynk.virtualWrite(V4, lluvia_milimetros);
    Blynk.virtualWrite(V5, lluvia_milimetros_div3);
  }
}

Regarding to LittleFS I didn’t know about that either, I will need to keep investigating about it’s usage since I need to store float, int and String variables.

Regards!