ESPHomeESP32DIY

Battery reed door sensor

An ultra-low-power ESPHome door/window sensor on an ESP32-C3 Super Mini — reed switch, deep sleep with GPIO wakeup, and a battery-voltage entity so you know when to recharge.

Difficulty · IntermediateSetup · 45–90 minutesHA · current stable (native API)ESPHome · current stable
Get the pack · from $9Have us set it up

Some hardware links on this page are affiliate links. As an Amazon Associate I earn from qualifying purchases. We may earn a commission if you buy through them, at no extra cost to you. See the affiliate disclosure.

Mains-powered contacts are easy. Battery contacts on ESP32 are easy to get wrong: the radio stays up, the reed sits on a pin that cannot wake the chip, and the cell is dead in two days. This pack is the sleepy pattern: wake on open or close, publish once, sleep again, and report battery voltage on a slow backup timer.

Wi-Fi credentials stay in secrets.yaml. Never paste SSIDs, PSKs, or API keys into a published YAML.

What problem it solves

You want a door or window contact in a shed, attic hatch, or interior door where pulling 5 V is annoying — without a Zigbee hub if you are already on ESPHome, and without a sensor that needs weekly charging.

How it works

  1. Reed on a wakeup GPIO — magnet present = closed (or the reverse; the pack inverts in YAML). The pin is an input with pull-up, and it is also the deep-sleep wakeup pin. The chip wakes on one pin level, so the YAML sets wakeup_pin_mode: INVERT_WAKEUP: if the door is already open when the chip goes to sleep, ESPHome flips the wake level so the next close wakes it. Open and close both report.
  2. Short run window — on boot, ESPHome connects, publishes the contact binary sensor and battery voltage, then deep_sleep.enter after a few seconds (long enough for HA to see the state).
  3. GPIO wakeup — open or close the door, the pin wakes the ESP32, it reports the new state, and it sleeps again.
  4. Timed wakeup — a backup sleep_duration (hours, not minutes) so a missed edge still refreshes battery voltage and an “I’m alive” timestamp.
  5. Low battery — ADC on a divider (or the board’s VBAT pin if it has one) with a binary sensor at a cutoff you pick.

This is not a lock. It is a contact. Treat it that way.

Hardware notes

  • Board. An ESP32-C3 Super Mini is small enough for a door jamb box. On the ESP32-C3, only GPIO0–GPIO5 can wake the chip from deep sleep. The pack uses GPIO4 and lists the pins to avoid: GPIO2, GPIO8, and GPIO9 (boot straps) and GPIO18/GPIO19 (USB).
  • Reed. A glass reed + magnet is enough. Mount so the magnet is next to the reed when the door is shut; leave slack in the wires so a slam does not rip the header.
  • Power. A small LiPo with a charge board is the usual path. Measure after the divider; a raw LiPo will exceed 3.3 V ADC range.
  • Antenna. A metal door frame eats 2.4 GHz. Offset the board into the jamb cavity, not behind a strike plate.

Snippet

Trimmed from the pack YAML. Names and pins are substitutions, not a live house:

substitutions:
  friendly_name: Shed door
  reed_pin: GPIO4 # ESP32-C3: only GPIO0–GPIO5 can wake from deep sleep
  battery_pin: GPIO1
  divider_ratio: "2.0"

esp32:
  board: esp32-c3-devkitm-1
  framework:
    type: esp-idf

binary_sensor:
  - platform: gpio
    pin:
      number: ${reed_pin}
      mode:
        input: true
        pullup: true
      inverted: true
      allow_other_uses: true
    name: Contact
    id: reed_contact
    device_class: door
    filters:
      - delayed_on_off: 20ms

sensor:
  - platform: adc
    pin: ${battery_pin}
    id: battery_voltage
    name: Battery voltage
    attenuation: 12db
    filters:
      - multiply: ${divider_ratio}

deep_sleep:
  id: sleeper
  run_duration: 15s
  sleep_duration: 6h
  wakeup_pin:
    number: ${reed_pin}
    allow_other_uses: true
    inverted: true
    mode:
      input: true
      pullup: true
  # Wake on open AND close: if the pin is already at the wake level when the
  # chip sleeps (door left open), flip the level so the next close wakes it.
  wakeup_pin_mode: INVERT_WAKEUP
# Wi-Fi, API, on-connect report, and the stay-awake-for-OTA helper: full YAML in the pack

Setup outline

  1. Wire reed to the wakeup GPIO and GND (or 3V3, matching the pull). Confirm open/close in a USB-powered ESPHome log before enabling deep sleep.
  2. Add the ADC divider, flash the pack YAML, adopt in Home Assistant.
  3. Enable deep sleep, disconnect USB, run on battery. Open and close the door: HA should see a brief online blip and a stable contact state.
  4. Leave it overnight. Battery voltage should tick on the timed wakeup; contact changes should still report within a couple of seconds of the edge.

Troubleshooting

  • Never wakes on the door. That GPIO is not a deep-sleep wakeup pin on this ESP32, or inverted / wakeup_pin_mode does not match the reed idle level. Test the pin with a jumper to ground before blaming the magnet.
  • Wakes once, then sleeps forever. run_duration is shorter than Wi-Fi + API connect. Raise it, or enter sleep only from on_connect after the contact is published.
  • Never sleeps (dead overnight). A sensor or logger is holding the loop, or USB-CDC is keeping the chip awake. Disable verbose logger on battery builds; do not leave power_save_mode off.
  • Battery reads 0.0 or 3.3 forever. Wrong ADC pin, missing divider, or attenuation too low. Measure with a meter at the pad the YAML names.

Credits

Based on the ESPHome deep sleep component and the community thread Reed door switch with deep sleep. This write-up is an original Super Mini recipe, not a paste of those pages.

Changelog

v1.0 · Oct 15, 2026: First publish.