I’ve been spending more time (and money) on ESP related projects, and recently solved the “how do I stop sensors from using battery power during deep sleep”
The impact of it may vary, but for a particular adafruit sensor module, which somereason has an led in it also, uses more energy (additional ~3ma) than I’d like. Shutting the sensor down should save about 3.2 milliamps of draw. For a 2500 mAh 18650 battery, that means 780 hours.
|ESP8266 (70mA) + Sensor (3.2mA)||73.2 mA||34 hours|
|ESP8266 Sleep (20 µA) + Sensor||3.22 mA||776 hours|
|ESP8266 Sleep + Sensor Off||0.02 mA||125,000 hours|
Now granted I wake up for 15 seconds every minute, thats 75% of the time.
The greater the sleep to run ratio, the greater the impact. For instance sleeping for 9:45 minutes, with 15 second runs.
That’s 268% - almost 3X the lifespan!
drainto GND on ESP/powersource
gateto ESP’s GPIO pin ***
*** This is where I wasted a lot of time. The ESP8266 has only 2 pins that will work (GPIO4/5).
As for power, you can literally wire an 18650 + directly to ESP’s
VIN and (-) to ESP’s
GND I used rubber band to hold the wire on so i can swap out battery while I charge. You can also buy lovely plastic battery holders pre-wired for like $.50.
Many thanks to Tatham Oddie’s post on using battery power deep sleep on ESP8266. His basic approach uses a script that acts as a custom
loop called on boot, and also calling sleep.
**The really cool thing with Tatham’s approach is each loop checks with Home Assistant to see if it should stay awake for OTA’s instead of sleeping.
His post opened my eyes to taking a little more control of the esp’s life cycle without any ‘code’ per se! We can easily control wheter our esp device is running or awaiting updates, and great for remote deployments.
I modified his approach (which was switching a light) to focus on reading sensor data. Rather than calling the loop on boot, I opted for more of an event driven approach. THe only time we loop is if we’re waiting for OTA. Additionally we allow a long and a slow loop depending on my needs.
The key aspects of ESPHome’s features are
on_value of the sensor components.
Sometimes we wake up quickly, others days we tend to drag. Seems the same for my ESP boards. This means the required
run_duration varies widely, so we dont use it. In addition to being event driven, the script now no longer loops or calls itself, since the above events will ensure we act as needed if something changes.
This means we can explicitly decide excactly how long we need to sleep, if at all, and make that call ourselves. If we don’t the chip just hums along happily.
This part is very simple. Once wired to the relay/mosfet, just declare the pin and attach it to the
on_start events. This ensures we’rew only feeding power to the auxilary devices onces the chip boots. We set the priority to ensure it occurs just before ESPHome tries to load sensor code.
I have noted the 3 tricks used in my code below
esphome: name: the-super-saver-sensor on_boot: priority: 601 #enable power before esp enables sensor then: - output.turn_on: gpio_d1 #enable sensor (trick 3) on_shutdown: priority: 710 then: - output.turn_off: gpio_d1 #sleep sensor (trick 3) # # Trick #1 - dynamic sleep duration # # Since startup, wifi, sensor, and HA sync take anywhere from 2 seconds to 12. # So to achieve a 60s or 600s loop we dynamically adjust how long to sleep # globals: - id: delay_int type: int restore_value: no initial_value: '60' # we can rely on HA time since we only use it after sensor succesfully publishes (successfully talked to HA) time: - platform: homeassistant id: homeassistant_time # bus for sensor i2c: sda: D5 scl: D2 scan: true id: bus_a sensor: - platform: bh1750 name: "Tent Lux" address: 0x23 update_interval: 5s #10 minute is longest interval on_value: then: - delay: 5s #time to send to HA, i actually dont think we ned this anymore - script.execute: consider_deep_sleep deep_sleep: id: deep_sleep_control sleep_duration: 45s #We override this on call # # # Trick # 2 - allow flag in home assistant to block sleep # # Rather than a simple binary though, I opted for a select_helper to give 3 run modes # - no sleep - 'ota' # - on the minute - '60s' # - on the hour - '600s' # just realized i missed great chance for horrible UI (ota,otm, oth, lol) text_sensor: - platform: homeassistant name: Lux Sensor Run Mode entity_id: input_select.lux_run_mode id: run_mode_select # Trick 3 - Control sensor power via GPIO connected to mosfet output: - platform: gpio pin: D1 id: gpio_d1 # # Main logic, not a loop. # # Called from successful sensor updates to HA. # Relies on sensor update timing to act as loop triogger for OTA mode # script: - id: consider_deep_sleep mode: queued then: - if: # path 1, we dont have connection to HA yet, so just chill for a bit condition: lambda: return id(run_mode_select).state.empty(); then: - logger.log: 'Unknown Setting, do nothing..' #will try again after next sensor read in ~5 seconds else: - if: # path 2 - we have value from HA, and it's OTA mode - no sleep! (also do nothing but diff log) condition: lambda: return strcmp("ota" , id(run_mode_select).state.c_str() ) == 0; then: - logger.log: 'Home Assistant Helper set to OTA, do not sleep.' else: - if: # path 3a - okay to loop, set delay to 60s condition: lambda: return strcmp( id(run_mode_select).state.c_str(), "60s") == 0; then: - lambda: | id(delay_int) = 60; - logger.log: 'Delay set to 60s' else: - if: # path 3b - okauy to loop, set interval to 600s condition: lambda: return strcmp( id(run_mode_select).state.c_str(), "600s") == 0; then: - lambda: | id(delay_int) = 600; - logger.log: 'Delay set to 10 min' # path 3 (a&b) - compensate for wifi/HA delays by reducing interval to next even minute/10minute - lambda: | auto now = id(homeassistant_time).now().timestamp; auto timefuture = now + id(delay_int); timefuture -= (timefuture % 60); ESP_LOGI("UGH", "Next target runtime: %d", timefuture); id(delay_int) = timefuture - now ; - logger.log: 'Entering sleep' - deep_sleep.enter: id: deep_sleep_control sleep_duration: !lambda return id(delay_int) * 1000; #lambdas should return value as ms