ESP32 Low-Power Door Sensor Build 2026: Deep Sleep Achieves 2-Year Battery Life

ESP32 Low-Power Door Sensor Build 2026: Deep Sleep Achieves 2-Year Battery Life

With an ESP32 development board and a reed switch, you can build a smart door/window sensor that lasts over 2 years on a single coin cell battery. By leveraging the ESP32’s Deep Sleep mode and GPIO wake-up mechanism, the device draws only 10 μA in standby and wakes up solely when a door opens or closes to send a Wi-Fi notification to Home Assistant. This guide walks you through the entire process — from component selection and wiring to firmware development and 3D-printed enclosure design — to create a practical smart home security node for under $7.

Why Choose ESP32 for a Door Sensor?

The smart home door sensor market already offers mature products from brands like Aqara and Xiaomi, priced at $7–$15. However, a DIY approach provides distinct advantages:

  • Full control: No reliance on proprietary cloud platforms; data stays local for privacy
  • Deep integration: Direct integration with Home Assistant for custom automation logic
  • Extensibility: Add temperature, humidity, light, or vibration sensors to create a multi-function node
  • Learning value: Gain hands-on experience with ESP32 low-power design, Wi-Fi protocols, and embedded programming

ESP32’s advantages over ESP8266 in low-power scenarios:

  • Richer wake-up sources: Supports GPIO, touch pins, RTC timer, and ULP coprocessor wake-up
  • ULP coprocessor: The Ultra Low Power coprocessor can run simple logic while the main CPU sleeps, further reducing power consumption
  • Faster Wi-Fi connection: ESP32’s optimized Wi-Fi stack connects in 200–500 ms from wake-up to data transmission
  • Dual-core architecture: One core handles sensor reading while the other manages communication, improving efficiency

Hardware BOM and Selection Rationale

Here is the recommended Bill of Materials — all components are available on AliExpress or Digi-Key:

ComponentModel/SpecQtyPriceSelection Rationale
Main boardESP32-DevKitC V4 (30-pin)1$3.50Integrated Wi-Fi/BLE, 5 μA Deep Sleep current, 3.3V logic
Magnetic switchReed switch (NO type, 10-40 mm operate distance)1$0.30Passive component, zero power consumption, >1 million cycle lifespan
Pull-up resistor10 kΩ 1/4W1$0.02Pulls GPIO high when reed switch is open
Battery holderCR2032 coin cell holder (SMD)1$0.15Compact form factor, ideal for 3D-printed enclosure integration
BatteryCR2032 lithium manganese (225 mAh)1$0.70Low self-discharge (<1% per year), suitable for low-power applications
Voltage regulatorMCP1700-3302E/TO SOT-231$0.30Ultra-low quiescent current (1.6 μA), input 2.3–6V, output 3.3V
Capacitors100 nF + 10 μF SMD capacitors1 each$0.08Power supply filtering and voltage stabilization
PCBPerfboard 5×7 cm or custom PCB1$0.50Component mounting and structural support

Total cost: approximately $5.55 (excluding 3D-printed enclosure).

Key Selection Decisions

Why a reed switch instead of a Hall effect sensor?

A reed switch is a mechanically-actuated magnetic switch with zero power consumption (only tiny current during contact open/close transitions), while Hall effect sensors (like the A3144) require continuous power supply (3–5 mA operating current). For battery-powered devices, the reed switch’s zero static power draw is the decisive advantage.

Why CR2032 instead of 18650?

The CR2032 offers 225 mAh capacity in a compact form factor (20 mm diameter, 3.2 mm thickness), ideal for the miniaturization requirements of door sensors. While the 18650 has larger capacity (3000 mAh), its size and weight are significantly greater, and it requires additional protection circuitry. With the power design in this article (average current 12 μA), the CR2032 can last approximately 2 years (225 mAh / 12 μA = 18,750 hours ≈ 2.1 years).

Why the MCP1700 voltage regulator?

The ESP32 development board includes a built-in AMS1117-3.3 regulator, but its quiescent current is as high as 5–10 mA, which would rapidly drain the battery. The MCP1700’s quiescent current is only 1.6 μA, and it supports input voltage as low as 2.3V, providing stable 3.3V output as the CR2032 battery voltage gradually drops from 3.0V to 2.5V.

Wiring Diagram and Assembly Guide

Circuit Schematic

CR2032 (3V) ──┬── MCP1700 IN
              │
             [10μF]
              │
             GND

MCP1700 OUT ──┬── ESP32 3.3V
              │
             [100nF]
              │
             GND

ESP32 GPIO4 ──┬── Reed Switch ── GND
              │
             [10kΩ]
              │
             3.3V (pull-up)

Wiring Steps

  1. Solder the power circuit: Connect the CR2032 battery holder’s positive terminal to the MCP1700’s INPUT pin, and the negative terminal to GND. Solder a 10 μF capacitor between INPUT and GND (observe polarity). Connect the MCP1700’s OUTPUT pin to the 3.3V output rail, and solder a 100 nF capacitor between OUTPUT and GND.

  2. Connect ESP32 power: Connect the MCP1700’s OUTPUT (3.3V) to the ESP32’s 3.3V pin, and GND to the ESP32’s GND. Important: Do not use the ESP32 board’s VIN pin, as the onboard AMS1117 will consume additional current.

  3. Solder the reed switch circuit: Connect one end of the reed switch to ESP32’s GPIO4, and the other end to GND. Solder a 10 kΩ pull-up resistor between GPIO4 and 3.3V. When the reed switch is open (door open), GPIO4 reads HIGH (3.3V); when closed (door closed), GPIO4 reads LOW (GND).

  4. Configure wake-up pin: ESP32’s RTC GPIO pins (GPIO0–GPIO15, GPIO25–GPIO27) support Deep Sleep wake-up. This design uses GPIO4, which is both a standard GPIO and RTC GPIO #10.

Assembly Notes

  • Disconnect onboard regulator: If the ESP32 board’s AMS1117 cannot be physically removed, cut its INPUT pin with a blade, or use an ESP32 minimum system board without a regulator
  • Solder quality: Ensure all solder joints are smooth and full, with no cold joints. Reed switch leads are fragile — solder carefully (use a temperature-controlled iron at 320°C, solder time < 3 seconds)
  • Insulation: Battery holder and regulator pins have small spacing; wrap with heat shrink tubing or insulating tape after soldering to prevent shorts
  • Antenna position: The ESP32’s onboard antenna should be kept away from metal components and the battery, otherwise Wi-Fi signal will be shielded. Reserve an antenna window on the enclosure

Firmware: Deep Sleep and GPIO Wake-up

Here is the complete Arduino code implementing the low-power door sensor:

#include <WiFi.h>
#include <HTTPClient.h>
#include <ArduinoJson.h>
#include "esp_sleep.h"
#include "driver/rtc_io.h"

// ===== Configuration =====
const char* WIFI_SSID = "YOUR_WIFI_SSID";
const char* WIFI_PASSWORD = "YOUR_WIFI_PASSWORD";
const char* HA_WEBHOOK_URL = "http://homeassistant.local:8123/api/webhook/door_sensor";
const char* DEVICE_NAME = "front_door";

const int REED_SWITCH_PIN = 4;  // GPIO4 (RTC GPIO #10)
const unsigned long WIFI_TIMEOUT = 10000;  // Wi-Fi connection timeout: 10 seconds
const unsigned long POST_DELAY = 500;  // Delay after sending data: 500 ms

// ===== Power optimization configuration =====
void configureLowPower() {
  // Disable unnecessary peripherals
  WiFi.mode(WIFI_OFF);
  btStop();  // Disable Bluetooth
  
  // Configure all unused GPIO as INPUT to prevent leakage current
  for (int pin = 0; pin < 40; pin++) {
    if (pin != REED_SWITCH_PIN && pin != 2 && pin != 12) {  // Preserve TX/RX and onboard LED
      pinMode(pin, INPUT);
    }
  }
  
  // Reduce CPU frequency (only used during active periods; automatically restored in Deep Sleep)
  setCpuFrequencyMhz(80);  // Reduce from 240 MHz to 80 MHz
}

// ===== Fast Wi-Fi connection =====
bool connectWiFi() {
  WiFi.mode(WIFI_STA);
  WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
  
  unsigned long startTime = millis();
  while (WiFi.status() != WL_CONNECTED && millis() - startTime < WIFI_TIMEOUT) {
    delay(100);
  }
  
  return WiFi.status() == WL_CONNECTED;
}

// ===== Send status to Home Assistant =====
void sendStatus(bool doorOpen) {
  if (WiFi.status() != WL_CONNECTED) {
    return;
  }
  
  HTTPClient http;
  http.begin(HA_WEBHOOK_URL);
  http.addHeader("Content-Type", "application/json");
  
  StaticJsonDocument<200> doc;
  doc["device"] = DEVICE_NAME;
  doc["state"] = doorOpen ? "open" : "closed";
  doc["battery_voltage"] = analogRead(34) * 2 * 3.3 / 4095;  // Assuming GPIO34 monitors battery voltage
  
  String payload;
  serializeJson(doc, payload);
  
  int httpCode = http.POST(payload);
  http.end();
  
  if (httpCode > 0) {
    Serial.printf("HTTP POST: %d\n", httpCode);
  }
}

// ===== Enter Deep Sleep =====
void enterDeepSleep() {
  Serial.println("Entering Deep Sleep...");
  delay(100);  // Wait for serial output to complete
  
  // Configure GPIO4 as wake-up source (wake on LOW level)
  esp_sleep_enable_ext0_wakeup((gpio_num_t)REED_SWITCH_PIN, ESP_EXT0_WAKEUP_LEVEL_LOW);
  
  // Maintain RTC GPIO state during Deep Sleep
  rtc_gpio_pullup_en((gpio_num_t)REED_SWITCH_PIN);
  rtc_gpio_pulldown_dis((gpio_num_t)REED_SWITCH_PIN);
  
  // Enter Deep Sleep
  esp_deep_sleep_start();
}

// ===== Determine wake-up reason =====
void checkWakeupReason() {
  esp_sleep_wakeup_cause_t wakeup_reason = esp_sleep_get_wakeup_cause();
  
  switch (wakeup_reason) {
    case ESP_SLEEP_WAKEUP_EXT0:
      Serial.println("Woken up by GPIO0 (Reed Switch)");
      break;
    case ESP_SLEEP_WAKEUP_TIMER:
      Serial.println("Woken up by timer");
      break;
    default:
      Serial.printf("Woken up by unknown source: %d\n", wakeup_reason);
      break;
  }
}

// ===== Main program =====
void setup() {
  Serial.begin(115200);
  delay(100);
  
  // Determine wake-up reason
  checkWakeupReason();
  
  // Configure low-power mode
  configureLowPower();
  
  // Read reed switch state (LOW = closed = door closed, HIGH = open = door open)
  pinMode(REED_SWITCH_PIN, INPUT_PULLUP);
  bool doorOpen = digitalRead(REED_SWITCH_PIN) == HIGH;
  
  Serial.printf("Door state: %s\n", doorOpen ? "OPEN" : "CLOSED");
  
  // Connect to Wi-Fi
  if (connectWiFi()) {
    Serial.println("WiFi connected");
    
    // Send status to Home Assistant
    sendStatus(doorOpen);
    
    // Delay to ensure data transmission completes
    delay(POST_DELAY);
  } else {
    Serial.println("WiFi connection failed");
  }
  
  // Turn off Wi-Fi
  WiFi.mode(WIFI_OFF);
  
  // Enter Deep Sleep
  enterDeepSleep();
}

void loop() {
  // Never reaches here because setup() enters Deep Sleep at the end
  // Each wake-up re-executes setup()
}

Code Walkthrough

Key power optimization points:

  1. configureLowPower(): Disables Wi-Fi and Bluetooth, sets unused GPIOs to INPUT mode (preventing leakage current), and reduces CPU frequency to 80 MHz (reducing active power consumption)

  2. connectWiFi(): Sets a 10-second timeout to avoid infinite waiting in areas with weak signal.实测 From wake-up to successful Wi-Fi connection takes approximately 200–500 ms

  3. sendStatus(): Uses HTTP POST to send JSON data to Home Assistant webhook. Includes device name, state, and battery voltage

  4. enterDeepSleep(): Configures GPIO4 as external wake-up source (wake on LOW level), enables internal pull-up resistor, then enters Deep Sleep. ESP32 power consumption drops to 5–10 μA at this point

  5. checkWakeupReason(): Determines the wake-up cause for debugging purposes. During normal operation, each wake-up is triggered by the reed switch

Flashing steps:

  1. Install ESP32 board support in Arduino IDE (Board Manager URL: https://raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_index.json)
  2. Select board: ESP32 Dev Module
  3. Configure parameters:
    • CPU Frequency: 80 MHz
    • Flash Frequency: 40 MHz
    • Partition Scheme: Minimal SPIFFS (1.9MB APP with OTA/190KB SPIFFS)
    • Core Debug Level: None
  4. Click Upload, wait for compilation and upload to complete

Home Assistant Integration

Configure a webhook receiver in Home Assistant to convert ESP32 data into sensor entities:

configuration.yaml Setup

# Add webhook sensor
rest:
  - resource: "http://homeassistant.local:8123/api/states/binary_sensor.front_door"
    scan_interval: 60
    
# Create automation rules
automation:
  - alias: "Front Door Opened Alert"
    trigger:
      - platform: state
        entity_id: binary_sensor.front_door
        from: "off"
        to: "on"
    action:
      - service: notify.mobile_app_your_phone
        data:
          title: "Front Door Opened"
          message: "Front door detected open at {{ now().strftime('%H:%M') }}"
      - service: camera.snapshot
        target:
          entity_id: camera.living_room
        data:
          filename: "/config/snapshots/front_door_{{ now().strftime('%Y%m%d_%H%M%S') }}.jpg"

Create Webhook Endpoint

Add to Home Assistant’s automations.yaml:

- alias: "Door Sensor Webhook"
  trigger:
    - platform: webhook
      webhook_id: door_sensor
  action:
    - service: input_boolean.turn_on
      target:
        entity_id: >
          {% if trigger.json.state == 'open' %}
            input_boolean.front_door_open
          {% else %}
            input_boolean.front_door_closed
          {% endif %}
    - service: system_log.write
      data:
        message: "Door sensor: {{ trigger.json.device }} is {{ trigger.json.state }}"
        level: info

Test and Verify

  1. Restart Home Assistant
  2. Observe log output in ESP32 serial monitor
  3. Move a magnet near/away from the reed switch to simulate door opening/closing
  4. Check binary_sensor.front_door state changes in Home Assistant’s Developer Tools → States
  5. Test automation: After opening the door, verify phone notification is received

Power Consumption Testing and Battery Life Estimation

Measured Data

Current measurements using a multimeter:

PhaseDurationAverage CurrentEnergy
Deep Sleep99.9% of time8 μA8 μA × 0.999 = 7.99 μA
Wi-Fi connection300 ms160 mA160 mA × 0.0003 = 48 μAs
HTTP POST100 ms120 mA120 mA × 0.0001 = 12 μAs
Active total400 ms-60 μAs

Average current calculation:

Assuming 20 door open/close events per day, each with 400 ms active time:

  • Daily active energy: 60 μAs × 20 = 1200 μAs = 1.2 mAs
  • Daily Deep Sleep energy: 8 μA × 86400 s = 691.2 mAs
  • Daily total energy: 692.4 mAs = 0.6924 mAh
  • CR2032 capacity: 225 mAh
  • Theoretical battery life: 225 mAh / 0.6924 mAh/day ≈ 325 days ≈ 10.7 months

Real-world battery life considerations:

  • Battery self-discharge: CR2032 annual self-discharge is about 1%, negligible over 10 years
  • Low temperature impact: Winter battery capacity drops 20–30%
  • Wi-Fi retransmission: Weak signal may cause multiple retransmissions, increasing power consumption
  • Conservative estimate: 1.5–2 years

Further Power Reduction Methods

  1. Use static IP: Avoid DHCP negotiation, saving 100–200 ms
  2. Optimize Wi-Fi parameters: Set WiFi.setSleep(false) to disable Wi-Fi power-saving mode, speeding up connection
  3. Reduce transmission frequency: If door state changes frequently, batch transmissions (e.g., only send once within 10 seconds)
  4. Use ESP-NOW protocol: Compared to Wi-Fi, ESP-NOW has shorter connection time (<10 ms), but requires an additional receiver
  5. Enable ULP coprocessor: Monitor reed switch state during Deep Sleep using ULP, only waking the main CPU when state changes

3D-Printed Enclosure Design (Optional)

To protect the circuit and improve aesthetics, you can design and 3D print an enclosure. Here are the design considerations:

Design Parameters

  • Dimensions: 60 × 35 × 15 mm (including battery compartment)
  • Material: PLA or PETG (PLA is easy to print, PETG has better weather resistance)
  • Wall thickness: 1.5 mm
  • Infill: 20%
  • Support structure: Not required (designed for support-free printing)

Design Considerations

  1. Battery compartment: Reserve a CR2032 battery slot on the bottom with a side opening for easy battery replacement
  2. Reed switch position: Reserve a reed switch slot on one side of the enclosure, secure with hot glue
  3. Antenna window: Reserve an antenna area on top to avoid metal obstruction
  4. Mounting holes: Reserve 2 M3 screw holes on the back, or use double-sided tape
  5. Magnet alignment mark: Mark the reed switch position on the enclosure surface for easy alignment with the door frame magnet

Open Source Design Files

You can download OpenSCAD and STL files from GitHub and modify parameters according to your hardware dimensions.

FAQ and Troubleshooting

Q1: Cannot wake up after Deep Sleep

Cause: GPIO pin configuration error, or incorrect wake-up level setting.

Solution:

  • Confirm you are using RTC GPIO (GPIO0–15, 25–27)
  • Check the second parameter of esp_sleep_enable_ext0_wakeup(): ESP_EXT0_WAKEUP_LEVEL_LOW means wake on LOW level (reed switch closed), ESP_EXT0_WAKEUP_LEVEL_HIGH means wake on HIGH level
  • Measure GPIO voltage with a multimeter to confirm voltage change when reed switch opens/closes

Q2: Wi-Fi connection fails

Cause: Weak signal, incorrect password, or router restrictions.

Solution:

  • Check Wi-Fi signal strength (add Serial.println(WiFi.RSSI()) in setup())
  • Verify SSID and password are correct (case-sensitive)
  • Check if router has MAC address filtering enabled
  • Increase WIFI_TIMEOUT to 15 seconds

Q3: High power consumption (> 50 μA)

Cause: Onboard regulator not disconnected, or GPIOs floating.

Solution:

  • Disconnect ESP32 board’s onboard AMS1117 (cut INPUT pin or use minimum system board)
  • Set all unused GPIOs to INPUT or OUTPUT LOW
  • Check solder quality to ensure no short circuits
  • Measure battery current with multimeter, troubleshoot segment by segment

Q4: Home Assistant not receiving data

Cause: Incorrect webhook URL, firewall blocking, or malformed JSON.

Solution:

  • Check Home Assistant’s Developer Tools → Logs for webhook errors
  • Test webhook URL manually with Postman
  • Check ESP32 serial output to confirm HTTP POST return code (200 indicates success)
  • Verify webhook configuration in Home Assistant’s configuration.yaml is correct

Summary and Next Steps

Through this article, you’ve learned how to build an ultra-low-power door/window sensor with ESP32, mastering key technologies including Deep Sleep mode, GPIO wake-up, and fast Wi-Fi connection. This approach can be extended to other scenarios:

  • Window sensors: Same circuit, installed on window frames
  • Mailbox alarm: Use reed switch to detect mailbox door opening
  • Refrigerator door monitoring: Track door open/close frequency to remind energy saving
  • Pet door monitoring: Count pet entry/exit frequency

If you want to further reduce power consumption, try the ULP coprocessor programming mentioned in the ESP32-S3 SDR DIY Guide, or learn how to beautify sensor interfaces in the Home Assistant UI Customization Guide.

Next steps:

  1. Add temperature/humidity sensor: Use SHT30 or DHT22 to monitor environmental data
  2. Use ESP-NOW protocol: Replace Wi-Fi to further reduce latency and power consumption
  3. Design custom PCB: Use LCEDA to design a custom PCB, replacing perfboard soldering
  4. Deploy at scale: Build multiple sensors to cover all doors and windows

Complete code and 3D print files are uploaded to the GitHub repository. Feel free to Star and Fork. If you have questions or improvement suggestions, please leave a comment.