I have a project in mind where I monitor my mailbox door with a battery-powered ESP32 (using deep_sleep). My mailbox is too far away from my WiFi access point but I figure with ESPNow support I can connect the "mailbox" ESP32 to a nearby "office" ESP32 that is also running ESPHome. This way I can hand-off the door state and battery state to a device that is always on and ready. I'm using the ESP-NOW Packet Transport Platform - ESPHome - Smart Home Made Simple to handle the messaging.
Anyway, I've created this "mailbox" ESP32 build and now I'm running into a handshake problem. The mailbox door has a reed switch to a GPIO and on "open" the device wakes from deep_sleep and starts doing all the things. What I've found is that often, if the door is opened and closed in a normal time (<5s) the "office" device doesn't even get the "open" state data by then and the "mailbox" device is already issuing the close state. This means that Home Assistant never actually sees the mailbox door state as open.
So, I figured I need a handshake to happen between mailbox and office such that mailbox doesn't issue the "close" state until it gets notification that office has received the "open" state. I'm trying to set this up manually with a few binary_sensors and I'm now causing unhandled exceptions, I think, because I'm trying to wait for these sensors to be cleared before doing stuff in both cases.
I think I'm probably overcomplicating this whole thing.
Does anyone have an idea for implementing this so that I can pass the information from "mailbox" to "office" cleanly and without complexity?
For reference this is the logic I have currently loaded and it works pretty well when everything is slow and works (please ignore the delay statements, I have those in there just for debug and to manually slow down the process). The problem with this, I think, is that if I open and close the door fast the two while loops cause a circular dependency lockup condition and that I think causes an unhandled exception.
This is on the "mailbox" device:
binary_sensor:
- platform: template
id: inside_open_message_packet
- platform: template
id: inside_close_message_packet
- platform: packet_transport
provider: office
name: Keep On
id: keep_on
remote_id: keep_mailbox_on
internal: false
- platform: packet_transport
provider: office
name: Door Opened Response
id: door_opened_response
remote_id: door_opened_response
internal: false
- platform: packet_transport
provider: office
name: Door Closed Response
id: door_closed_response
remote_id: door_closed_response
internal: false
- platform: gpio
pin:
number: GPIO32
mode: INPUT_PULLUP
allow_other_uses : true
name: "Door"
id: door
device_class: door
filters:
- delayed_on_off: 250ms
internal: false
on_press:
then:
- while:
condition:
or:
- binary_sensor.is_on: inside_open_message_packet
- binary_sensor.is_on: inside_close_message_packet
then:
- logger.log:
format: "open: %s close: %s waiting for it to clear..."
args: [ 'ONOFF(id(inside_open_message_packet).state)', 'ONOFF(id(inside_close_message_packet).state)' ]
- delay: 1s
- logger.log: "Starting open message packet"
- binary_sensor.template.publish:
id: inside_open_message_packet
state: ON
- while:
condition:
binary_sensor.is_off: door_opened_response
then:
- logger.log: "Sent door opened and wait for response..."
# - espnow.send:
# address: "8C:AA:B5:8B:E3:F0"
# data: "door opened"
- delay: 1s
- logger.log: "States are good, door opened! Closing open message packet."
- delay: 5s
- binary_sensor.template.publish:
id: inside_open_message_packet
state: OFF
on_release:
then:
- while:
condition:
or:
- binary_sensor.is_on: inside_close_message_packet
- binary_sensor.is_on: inside_open_message_packet
then:
- logger.log:
format: "open: %s close: %s waiting for it to clear..."
args: [ 'ONOFF(id(inside_open_message_packet).state)', 'ONOFF(id(inside_close_message_packet).state)' ]
- delay: 1s
- logger.log: "Starting close message packet"
- binary_sensor.template.publish:
id: inside_close_message_packet
state: ON
- while:
condition:
binary_sensor.is_off: door_closed_response
then:
- logger.log: "Sent door closed and wait for response..."
- delay: 1s
- logger.log: "States are good, door closed! Closing close message packet."
- delay: 5s
- binary_sensor.template.publish:
id: inside_close_message_packet
state: OFF
- logger.log: "States are good, moving on with release"
- delay: 5s
- if:
condition:
lambda: 'return id(keep_on).state;'
then:
- logger.log: "Keep On is set, so we're not going to sleep."
else:
- logger.log: "Keep On is not set, so we're going to sleep in 20s."
- component.update: battery_voltage
- delay: 20s
- deep_sleep.enter:
id: deep_sleep_1
Hi! That is a very creative way to solve the handshake puzzle using while loops! However, managing those tight loops during rapid deep sleep cycles can easily cause unhandled exceptions or stack overflows in ESPHome.
You can actually achieve the exact same logic much more efficiently by using a built-in ESPHome filter called delayed_off. This acts as a "Pulse Extender", forcing the "Open" state to stay active for a fixed minimum time (like 10 seconds), regardless of how fast the mailman closes the mailbox.
This gives your ESP-NOW transmission and Home Assistant plenty of time to catch the event, without needing complex loops.
binary_sensor:
- platform: packet_transport
provider: office
name: Keep On
id: keep_on
remote_id: keep_mailbox_on
internal: false
- platform: gpio
pin:
number: GPIO32
mode: INPUT_PULLUP
allow_other_uses: true
name: "Door"
id: door
device_class: door
filters:
# 1. Debounce the physical switch
- delayed_on_off: 50ms
# 2. Force the OPEN state to stay alive for at least 10 seconds
- delayed_off: 10s
internal: false
on_press:
then:
- logger.log: "Door OPEN physically detected! Sending packet immediately..."
# Replace the line below with your actual ESP-NOW transmit action
- component.update: office
on_release:
then:
- logger.log: "10-second window expired. Sending CLOSED packet..."
# Replace the line below with your actual ESP-NOW transmit action
- component.update: office
- delay: 5s # Give the radio a moment to push the last packet out
- if:
condition:
lambda: 'return id(keep_on).state;'
then:
- logger.log: "Keep On is active, staying awake."
else:
- logger.log: "Going to sleep now..."
- component.update: battery_voltage
- delay: 2s
- deep_sleep.enter:
id: deep_sleep_1
I think you should simply detect wake up of mailbox and put it back to sleep when it has done necessary communication. Close action is not fundamental.
I thought of this, too... just single "edge trigger" since that's all I really want to know and it would simplify the whole operation. I think this combined with Boostma's suggestion of using extended delayed_off will make this actually work.
LOL, I just barely built the device yesterday and ran into implementation/technical issues too quickly to start with monitoring and optimizing for energy savings. This ESP32 is a completely generic 30-pin one that I had laying around. I've already destroyed the power-on LED, I plan to down-clock the CPU, reduce the sensor sample rates (maybe set to never and manually issue updates when sending the detail off to the office) and investigate if there are other things to try. I'm using a single 18650 cell with a buck converter to drop it to 3.3V and then just the reed switch going to a GPIO -- it's VERY simple.
Anyway, need to get the actual function to work, then look at the power/lifetime situation.
Esp is so power hungry that you don't want to use some fixed delays there. I'm not using espnow, but you likely have minimal feedback for successful transmission using on_sent automation and wait_for_sent parameter.
I asked as generations of hobbyists have tried using the ESP32 for exactly your scenario with a battery powered remote sensor, and have been deeply disenchanted by high current draw (measured in hours/days but years expected), and have found the challenge of trying to implement the temptation of sleep modes, high in promises but short on actual achievement, and hoped you may have found the Holy Grail.
Espressif have recognised this shortcoming in their product lineup and recently released new chips for low power operation, but it still appears that switching from using ESP32 chips to the nRF52840 series and not using WiFi may be an alternate option you may wish to explore that may save you some headscratching and a lot of wasted time.
Both chips supported by ESPHome, so all your groundwork with software planning and design should be able to be ported over quite quickly.
After reading about your hardware setup (the single 18650 cell, the buck converter, and clipping the power LED), I did some more thinking about your battery lifetime.
If maximizing that 18650 battery is your ultimate goal, those fixed 15-second delays in the script are actually your biggest bottleneck. Right now, the ESP32 is burning precious milliamps just waiting around for the timer to finish, even though the data was probably transmitted successfully in the first 2 seconds.
To get the absolute longest battery life (potentially extending it from months to over a year), I realized we can let the Office ESP tell the Mailbox ESP exactly when it’s safe to go to sleep.
By listening to the door_closed_response packet, your mailbox device can enter deep sleep instantly within 1.5 seconds total awake-time! If the office is offline, I added a 5-second safety timeout as a backup so your battery never drains.
binary_sensor:
- platform: packet_transport
provider: office
name: "Keep On"
id: keep_on
remote_id: keep_mailbox_on
internal: false
# This sensor triggers the moment the office confirms it received the CLOSED state
- platform: packet_transport
provider: office
remote_id: door_closed_response
internal: true
on_press:
then:
- logger.log: "Office confirmed CLOSE packet! Sleeping immediately..."
- script.execute: enter_deep_sleep
- platform: gpio
pin:
number: GPIO32
mode: INPUT_PULLUP
allow_other_uses: true
name: "Door"
id: door
device_class: door
filters:
- delayed_on_off: 50ms
internal: false
on_press:
then:
- logger.log: "Door OPENED! Transmitting..."
- component.update: office
- script.stop: safety_timeout_sleep
on_release:
then:
- logger.log: "Door CLOSED! Transmitting..."
- component.update: office
# Start the safety timeout (backup in case the office doesn't respond)
- script.execute: safety_timeout_sleep
script:
# SAFETY BACKUP: If the office is offline, force sleep after 5 seconds anyway
- id: safety_timeout_sleep
mode: restart
then:
- delay: 5s
- logger.log: "No response from office after 5s. Forcing sleep to save battery..."
- script.execute: enter_deep_sleep
# Centralized sleep controller
- id: enter_deep_sleep
then:
- if:
condition:
and:
- lambda: 'return !id(door).state;' # Only sleep if door is shut
- lambda: 'return !id(keep_on).state;' # Only sleep if Keep On is off
then:
- component.update: battery_voltage
- delay: 100ms # Brief moment to clear voltage component
- deep_sleep.enter: deep_sleep_1
The nRF52840 usually talks (sips) microamps, not milliamps.
Session establishment, data loss during sleep mode, and handshaking are the common challenges, and this thread is revisiting them. I was hoping for some breakthrough.
Changing your sensor end ESP32 to a nRF52840 (they are getting cheaper) and retaining your office end ESP32 and using a non WiFi network radio transport layer may be the option that can result in long battery life.
Not trying to discourage you so far down your track, just pointing you towards what others have tried once they have explored and found the ESP32 chips unsuitable.
You are completely right, those are exactly the classic challenges with these older ESP chips when waking up from deep sleep.
There is definitely no grand breakthrough here, but hopefully, switching to fast ESP-NOW packets and going to sleep immediately after the office device responds helps to keep the awake-time as short as possible for this specific hardware.
The classic window shuffle challenge, minimising it to reduce current draw, but keeping it open long enough to let the data through.
The ESPNOW protocol tweaks around the edges, tempting you. Finally frustration and realisation dawns - the chipset radios draw too much current - it is a hardware drawback, not a software coding challenge.
Ten buck hardware solution chip swap vs many hours of reading Espressif data sheets closely, or asking a hallucinating AI engine, you are welcome to rise to the challenge.
Having been along this path and watching others tread the same weary options, you always hope somebody has found the magic combo of commands. Sadly I have grown tired of trying and have jumped in to offer a diffetent path to consider that will provide a viable option of how to power a battery powered sensor and report the results to HomeAssistant cheaply and efficiently.
NRF52 can do it at 1% power draw compared to ESP (communicating continuously at max BLE advertising interval). Basically "always on" from that point of view, . Unfortunately any practical use of NRF52 is not on Esphome yet.
One lives in hope and patience. By the time it rolls out, the newer Espressif chips may yet again tempt me to go and explore. The draw of Thread, ZigBee and BLE/BlueTooth beckons. ESPHome is not always the optimum choice, and certainly the first few generations of ESP32's aren't either.
Hence my current mailbox sensor is a Telink chip inside a cheap PVVX firmware customised Xiaomi Mijea LYWSD03MMC temperature/humidity sensor with the added reed switch option soldered on. Two wires and a reed switch. Couldn't get much simpler.
Battery life now 16 months and counting.
Bonus is I now have an outdoor temperature and humidity sensor showing in HomeAssistant that can tell me how warm my letters are.
The older ESP32 I vainly tried to wrangle into battery operation is now doing well as a proxy relaying inbound BLE packets to WiFi, powered off an old phone charger.
Apologies to @SpikeyGG - we derailed your thread but hopefully saved you a lot of grief tilting at windmills. Yes, your rechargeable batteries can be recharged by fitting a small solar panel to your mailbox, but the futzing around to do that compared to playing with the cute tiny Xiaomi Mijea LYWSD03MMC is possibly irresistible, and cost effective.
Had you considered using a BLE door sensor and receiving that via a the "office" ESP32 running a BLE proxy? The door sensor will run for months or years on a coin cell or a couple of AAA batteries.