homieiot / homie-esp8266

πŸ’‘ ESP8266 framework for Homie, a lightweight MQTT convention for the IoT
http://homieiot.github.io/homie-esp8266
MIT License
1.36k stars 306 forks source link

Homie and light sleep - How it is done #412

Open luebbe opened 6 years ago

luebbe commented 6 years ago

Hi Folks,

Has anyone tried to get his ESP into light sleep using the homie framework?

I'd like to send data to/from an MQTT server using homie. The device which is used to send the data is running on batteries. Deep sleep for a predefined time span is not an option, because the device has to wake up on a button press at any time. Wiring the button to the reset pin is also not an option, because:

  1. it's the only button and
  2. the button serves different functions (wake up and turn on display, send data when awake) so the ESP can not reboot every time the button is pressed.

So I thought light sleep with gpio wakeup would be an option. There is some documentation on this subject. I'm trying to use the code in https://github.com/esp8266/Arduino/issues/1381#issuecomment-258621247

Right now I'm trying to disconnect the ESP cleanly and send it to light sleep, by calling Homie.prepareToSleep() and then sleepnow() from the example code.

Triggering the wakeup() code after some time (e.g. 10 seconds) or via gpio seems to work, but homie never reconnects to WiFi/MQTT. Where's my mistake? Is this possible at all with Homie?

A little serial logging:

πŸ’‘ Firmware device-connect (1.0.0)
πŸ”Œ Booting into normal mode πŸ”Œ
{} Stored configuration
  β€’ Hardware device ID: a020a6117237
  β€’ Device ID: a020a6117237
  β€’ Name: Develop
  β€’ Wi-Fi:
    β—¦ SSID: xxxxxxxx
    β—¦ Password not shown
    β—¦ IP: 192.168.0.xxx
    β—¦ Mask: 255.255.255.0
    β—¦ Gateway: 192.168.0.1
  β€’ MQTT:
    β—¦ Host: 192.168.0.10
    β—¦ Port: 1883
    β—¦ Base topic: homie/
    β—¦ Auth? no
  β€’ OTA:
    β—¦ Enabled? no
β€’ ADC measurement:
  β—¦ Reading interval: 300 s
↕ Attempting to connect to Wi-Fi...
βœ” Wi-Fi connected, IP: 192.168.0.xxx
Triggering WIFI_CONNECTED event...
↕ Attempting to connect to MQTT...
Sending initial information...
βœ” MQTT ready
Triggering MQTT_READY event...
Calling setup function...
γ€½ Sending statistics...
  β€’ Wi-Fi signal quality: 100%
  β€’ Uptime: 10s
Going to sleep                                 <-- happens after 30 seconds of inactivity. Homie.prepareToSleep is called before
βœ– Wi-Fi disconnected
Triggering WIFI_DISCONNECTED event...
Who woke me?                                   <-- device wakes up on button press/interrupt
Device in preparation to sleep...              <-- Why does homie say this now?
γ€½ Sending statistics...
  β€’ Wi-Fi signal quality: 100%
  β€’ Uptime: 31s
βœ” Wi-Fi connected, IP: 192.168.0.xxx
Triggering WIFI_CONNECTED event...
↕ Attempting to connect to MQTT...
βœ– MQTT disconnected
Triggering MQTT_DISCONNECTED event...
Triggering READY_TO_SLEEP event...             <-- Why does the event trigger "long" after the device has been woken up?
Ready to sleep
luebbe commented 6 years ago

Some Info in between: I managed to get my ESP into light sleep (and wake it up successfully) using the code linked above in combination with the Homie framework. There's no need to call Homie.prepareToSleep() In fact it gets into the way, because there's no easy way in Homie to reset the readyForSleep flag back to normal. You also have to be very careful with the order in which you execute commands to sleep/wake up properly.

luebbe commented 6 years ago

Some more information hoping it will be useful to others. Here are the two functions I used to go to sleep and wake up again.

sleepNow() is called from loop() after a certain inactivity period. wakeupNow() is passed as a callback and is triggered when one (of the two in my example) GPIO pins is pulled low. It is important to know that after wakeupNow(), loop() resumes exactly after the line where sleepNow() was called.

void sleepNow()
{
  Homie.getLogger() << "? Going to sleep" << endl;
  _sleepState = GOING_TO_SLEEP;

  wifi_station_disconnect();
  wifi_set_opmode(NULL_MODE);
  wifi_fpm_set_sleep_type(LIGHT_SLEEP_T); //light sleep mode

  // Wake up when MENU button is pressed on the ESP
  gpio_pin_wakeup_enable(PIN_MENUSELECT, GPIO_PIN_INTR_LOLEVEL);

  // Wake up when READY signal is received
  gpio_pin_wakeup_enable(PIN_DIG_READY, GPIO_PIN_INTR_LOLEVEL);

  wifi_fpm_open();
  delay(100);
  wifi_fpm_set_wakeup_cb(wakeupNow); //wakeup callback

  // Maximum light-sleep duration: 268.435.455 uS (~268 Seconds) according to Expressif docs
  wifi_fpm_do_sleep(0xFFFFFFF);
  delay(100);
}
void wakeupNow()
{
  // Asynchronous Callback, which is triggered by the wakeup event
  // Don't do anything time consuming in here. Just set the necessary flags and exit.
  // Code is resumed in loop() exactly after the line where sleepNow() was called!!!

  Homie.getLogger() << "? Waking from sleep" << endl;

  // Release the GPIOs used for waking up. Essential if you want to use them normally afterwards!!!
  gpio_pin_wakeup_disable();

  wifi_fpm_close();
  wifi_set_opmode(STATION_MODE);
  wifi_station_connect();

  // Just set the flag(s). Evaluate and recover in loop()
  _sleepState = WAKING_UP;
  _lastAction = millis();
}
timpur commented 6 years ago

I also think it would be nice go from standalone mode and normal at will. This would also allow you to do light sleep ect. Ill look into the viability of this.

igrowing commented 1 year ago

Hi @luebbe, long time :) And still trying to use ESP8266. Based on your code, I have strange behavior of ESP. Hoping you might have some insights. Light sleep --> wait for trigger --> awake --> reboot. I use only 1 wake trigger (not 2) and I don't use the wake up callback.

  Homie.getLogger() << "? Going to sleep" << endl;
  wifi_station_disconnect();
  wifi_set_opmode(NULL_MODE);
  wifi_fpm_set_sleep_type(LIGHT_SLEEP_T); //light sleep mode
  Homie.getLogger() << "3";

  // PIN_FLOW = 12
  gpio_pin_wakeup_enable(PIN_FLOW, GPIO_PIN_INTR_HILEVEL);
//  gpio_pin_wakeup_enable(PIN_FLOW, GPIO_PIN_INTR_ANYEDGE);  // LOLEVEL, POSEDGE, NEGEDGE
  Homie.getLogger() << "2";

  wifi_fpm_open();
  delay(100);
  // wifi_fpm_set_wakeup_cb(wakeupNow); //wakeup callback
  Homie.getLogger() << "1" << endl;

  // Maximum light-sleep duration: 268.435.455 uS (~268 Seconds) according to Expressif docs
  wifi_fpm_do_sleep(0xFFFFFFF);
  delay(100);

  gpio_pin_wakeup_disable();
  Serial.println(">> wake up");
  Homie.reset();

In all scenarios the GPIO12 is always connected to +3.3V, no changes intentionally for testing. I expect ESP to sleep 268 seconds, awake and restart. However, it restarts in 100ms or so but with different output.

When trigger is GPIO_PIN_INTR_HILEVEL the print is:

? Going to sleep
3
 ets Jan  8 2013,rst cause:4, boot mode:(3,0)

wdt reset
load 0x4010f000, len 3584, room 16
tail 0
chksum 0xb0
csum 0xb0
v2843a5ac
~ld

In this case WDT casues the reset.

When trigger is GPIO_PIN_INTR_LOLEVEL or GPIO_PIN_INTR_POSEDGE or GPIO_PIN_INTR_NEGEDGE or GPIO_PIN_INTR_ANYEDGE the print is:

? Going to sleep
321

--------------- CUT HERE FOR EXCEPTION DECODER ---------------

Soft WDT reset

>>>stack>>>

ctx: sys
sp: 3fffed20 end: 3fffffb0 offset: 01a0
3fffeec0:  00000001 3fff08a8 3ffeff8c 4020e4d0

I.e. in these cases it prints more before light sleep but crashes. In this case Soft WDT causes the reset.

UPDATE: All the problem happens because WiFi activity. It cannot be stopped (I can't find a way to stop it) under Homie 2.0.0. Under WifiManager lib the light sleep with the given recipe works. However, exit from it doesn't: the WiFi won't restore. Restart is required.

Among all tested interrupt events only GPIO_PIN_INTR_LOLEVEL works fine.