I am trying to achieve a light sleep on ESP8266 with a GPIO wakeup. Everything is working fine except that if after wakeup the GPIO stays in a state opposite to the WAKEUP trigger level then the ESP crashes with a HW WDT reset. Need some help in the same.
What am I trying to achieve:
I have a ESP hooked up to a flow meter. The flow meter sends HIGH/LOW pulses when it turns. What I need is to be able to light sleep the ESP and wake up it when the flow starts again. For this I am connecting the flow meter output to the WAKE PIN on the ESP , so that the ESP wakes up when the flow starts. As I dont know if the flow meter output will be on LOW or HIGH when the sleep starts, I read the GPIO and then decide if to configure the WAKEUP trigger as GPIO_PIN_INTR_LOLEVEL
or GPIO_PIN_INTR_HILEVEL
What's the issue: If the ESP sleeps while GPIO is LOW then it has to wake up on a HIGH signal, which it does but if the flow meter stops at a HIGH signal then the ESP crashes after around 7 secs. This happens irrespective of how much time has elapsed since the wakeup. If for any reason the GPIO (connected to flow meter) will stop and remain at HIGH , the ESP will crash within 7 secs. The same applies if it sleeps on GPIO being HIGH, it then wakes up on a LOW and will crash with hardware Watchdog timer reset if the GPIO comes to LOW at any time. I read on a blog that the light sleep wakeup will work only on a pulsed trigger so if that's the case, this makes sense but I am still not able to accept the fact that this GPIO cannot be at a particular state at any time in the program after wakeup.
What have I tried: I tried a lot of things before I realized that this is the reason for crash so I wont go into those but will restrict it to this reason alone.
wifi_fpm_close();
after I wake up but doesnt solve the issue.output on crash
19:15:39.318 > ets Jan 8 2013,rst cause:4, boot mode:(3,7)
19:15:39.321 >
19:15:39.321 > wdt reset
19:15:39.321 > load 0x4010f000, len 3424, room 16
19:15:39.328 > tail 0
19:15:39.329 > chksum 0x2e
19:15:39.329 > load 0x3fff20b8, len 40, room 8
19:15:39.331 > tail 0
19:15:39.331 > chksum 0x2b
19:15:39.334 > csum 0x2b
19:15:39.334 > v00044c80
19:15:39.336 > ~ld
Code with all unrelated things stripped out:
#define DEBOUNCE_INTERVAL 10 //debouncing time in ms for interrupts
#define IDLE_TIME 15 //300 //idle time in sec beyond which the ESP goes to sleep , to be woken up only by a pulse from the meter
#define PULSE_PIN 4 // defines the pin to which the encoder is connected
#define WAKEUP_PIN 14 // defines the pin to which the wakeup signal is connected, in this case it is the same as the encoder pin
// ************ HASH DEFINES *******************
#include <Arduino.h>
#include "secrets.h"
#include "Debugutils.h"
#include <ESP8266WiFi.h>
#include <user_interface.h> // for RTC memory functions
// ************ GLOBAL OBJECTS/VARIABLES *******************
unsigned int pulses = 0; //stores the no of pulses detected by the sensor, it is reset after SENSOR_UPDATE_INTERVAL
unsigned long last_sleep_time = millis();//stores the no of ms since a message was last published , used to sleep the ESP beyond a certain value
unsigned long idle_time = millis();
volatile unsigned long lastMicros;
// ************ GLOBAL OBJECTS/VARIABLES *******************
void IRAM_ATTR pulseHandler() {
if((long)(micros() - lastMicros) >= DEBOUNCE_INTERVAL * 1000) { //note DEBOUNCE_INTERVAL is in ms , so multiply by 1000 for microsec
pulses += 1;
lastMicros = micros();
}
}
void light_sleep(){
WiFi.mode(WIFI_OFF); // you must turn the modem off; using disconnect won't work
// extern os_timer_t* timer_list;
// timer_list = nullptr; // stop (but don't disable) the 4 OS timers
wifi_fpm_set_sleep_type(LIGHT_SLEEP_T);
bool wakeup_pin_state = digitalRead(WAKEUP_PIN);
gpio_pin_wakeup_enable(GPIO_ID_PIN(WAKEUP_PIN), wakeup_pin_state? GPIO_PIN_INTR_LOLEVEL:GPIO_PIN_INTR_HILEVEL);// only LOLEVEL or HILEVEL interrupts work, no edge, that's a CPU limitation
Serial.printf("CPU going to sleep, pull WAKE_UP_PIN %s to wakeup",wakeup_pin_state?"LOW":"HIGH");Serial.flush();
wifi_fpm_open();
wifi_fpm_do_sleep(0xFFFFFFF); // only 0xFFFFFFF, any other value and it won't disconnect the RTC timer
delay(10); // it goes to sleep during this delay() and waits for an interrupt
Serial.println(F("Woke up!")); // the interrupt callback hits before this is executed*/
wifi_fpm_close(); // Disable force sleep mode
//sometimes the above statement gets printed before actually sleeping off, doesnt happen all the time though
//But the code works as expected, the ESP sleeps after printing Woke up
}
void setup() {
Serial.begin(115200);
pinMode(WAKEUP_PIN, INPUT_PULLUP);//INPUT_PULLUP
pinMode(PULSE_PIN, INPUT_PULLUP);
attachInterrupt(PULSE_PIN, pulseHandler, RISING);
Serial.println("Setup complete");
}
void loop() {
idle_time = millis() - last_sleep_time;
if(idle_time >= IDLE_TIME * 1000)
{
Serial.print("going to sleep after being idle for :"); Serial.print(idle_time/1000);Serial.println(" sec");Serial.flush();
light_sleep();
//now that we're awake , add the sleep time to the previous value
last_sleep_time = millis();
}
yield();
}
Got the solution on this via another forum. The key is to call the api gpio_pin_wakeup_disable();
after waking up to disable the GPIO wake function. The code works fine with this.
Fully working code for reference:
#define IDLE_TIME 15 //300 //idle time in sec beyond which the ESP goes to sleep , to be woken up only by a pulse from the meter
#define PULSE_PIN 4 // defines the pin to which the encoder is connected
#define WAKEUP_PIN 14 // defines the pin to which the wakeup signal is connected, in this case it is the same as the encoder pin
// ************ HASH DEFINES *******************
#include <Arduino.h>
#include "secrets.h"
#include "Debugutils.h"
#include <ESP8266WiFi.h>
#include <user_interface.h> // for RTC memory functions
// ************ GLOBAL OBJECTS/VARIABLES *******************
volatile unsigned int pulses = 0; //stores the no of pulses detected by the sensor, it is reset after SENSOR_UPDATE_INTERVAL
unsigned long last_sleep_time = millis();//stores the no of ms since a message was last published , used to sleep the ESP beyond a certain value
unsigned long idle_time = millis();
unsigned long lastMicros;
// ************ GLOBAL OBJECTS/VARIABLES *******************
void IRAM_ATTR pulseHandler() {
if((long)(micros() - lastMicros) >= DEBOUNCE_INTERVAL * 1000) { //note DEBOUNCE_INTERVAL is in ms , so multiply by 1000 for microsec
pulses += 1;
lastMicros = micros();
}
}
void light_sleep(){
WiFi.mode(WIFI_OFF); // you must turn the modem off; using disconnect won't work
// extern os_timer_t* timer_list;
// timer_list = nullptr; // stop (but don't disable) the 4 OS timers
wifi_fpm_set_sleep_type(LIGHT_SLEEP_T);
bool wakeup_pin_state = digitalRead(WAKEUP_PIN);
gpio_pin_wakeup_enable(GPIO_ID_PIN(WAKEUP_PIN), wakeup_pin_state? GPIO_PIN_INTR_LOLEVEL:GPIO_PIN_INTR_HILEVEL);// only LOLEVEL or HILEVEL interrupts work, no edge, that's a CPU limitation
Serial.printf("CPU going to sleep, pull WAKE_UP_PIN %s to wakeup",wakeup_pin_state?"LOW":"HIGH");Serial.flush();
wifi_fpm_open();
wifi_fpm_do_sleep(0xFFFFFFF); // only 0xFFFFFFF, any other value and it won't disconnect the RTC timer
delay(10); // it goes to sleep during this delay() and waits for an interrupt
Serial.println(F("Woke up!")); // the interrupt callback hits before this is executed*/
wifi_fpm_close(); // Disable force sleep mode
gpio_pin_wakeup_disable(); // Disable wakeup functionality for the GPIO pin
}
void setup() {
Serial.begin(115200);
pinMode(WAKEUP_PIN, INPUT_PULLUP);//INPUT_PULLUP
pinMode(PULSE_PIN, INPUT_PULLUP);
attachInterrupt(PULSE_PIN, pulseHandler, RISING);
Serial.println("Setup complete");
}
void loop() {
idle_time = millis() - last_sleep_time;
if(idle_time >= IDLE_TIME * 1000)
{
Serial.print("going to sleep after being idle for :"); Serial.print(idle_time/1000);Serial.println(" sec");Serial.flush();
light_sleep();
//now that we're awake , add the sleep time to the previous value
last_sleep_time = millis();
}
yield();
}