To create a wireless, battery-powered environmental sensor, optimized for extremely low power consumption. The primary goal is to achieve a theoretical battery life exceeding 10 years on a single Li-SOCl₂ (e.g., FANSO ER18505M 3.6V, 3500mAh) battery.
This project achieves this goal through a unique "Zero Standby" architecture, where the microcontroller is completely disconnected from power during its idle state. The duty cycle is managed by an external, nano-power system timer.
-
Ultra-Low Electronic Quiescent Current: The total current consumed by the electronics in the power-off state has been meticulously optimized to be under 100 nA. This value is the sum of the quiescent currents (Iq) of all components permanently connected to the battery:
- TPS63900 Buck-Boost (in shutdown): ~60 nA
- TPL5111 System Timer: ~35 nA
- SiP32431 Load Switch (in off-state): ~0.01 nA (practically zero)
- Total Electronic Iq ≈ 95 nA
This successfully achieves the design goal of making the electronic footprint in the standby state almost immeasurable. It ensures that the theoretical battery life is maximized, being limited almost exclusively by the energy consumed during the brief active cycles.
-
Power Gating Architecture: Instead of using Deep Sleep mode, the TPL5111 completely enables and disables the power supply for the entire system (MCU + sensor), eliminating any MCU-related leakage currents.
-
Ultra-Fast On-Time: The entire active cycle (boot, measure, transmit, shutdown) is highly optimized to complete in well under 100 milliseconds (typically ~80ms), which is critical for minimizing energy consumption.
-
Optimized Operational Sequence: The firmware uses advanced techniques like latency hiding, where long physical processes (like battery measurement stabilization) are executed in the background during other necessary initializations (like the Wi-Fi stack), effectively reducing their time cost to zero.
-
ESP-NOW Communication: Utilizes the lightweight and fast ESP-NOW protocol, configured for Long Range (LR) mode, for robust and low-power data transmission.
-
Professional Component-Based Architecture: The code is structured into fully independent, reusable components (e.g.,
bme280_driver,espnow_sender,sensor_manager) in line with the best practices for ESP-IDF, ensuring clean separation of concerns and easy maintenance.
| Component | Model/Value | Role |
|---|---|---|
| Microcontroller | Espressif ESP32-C3-MINI-1 | Main processing unit, RF communication |
| System Timer | Texas Instruments TPL5111 | External watchdog and duty cycle manager (Power Gating) |
| Buck-Boost Converter | Texas Instruments TPS63900 | High-efficiency 3.3V voltage regulation |
| Environmental Sensor | Bosch BME280 | Measures temperature, humidity, and pressure (I²C) |
| Battery | FANSO ER18505M (Li-SOCl₂, 3.6V, 3500mAh) or similar | Primary power source |
| Load Switch | Vishay SiP32431 | Switched voltage divider for V_BATT measurement |
The duty cycle is fast, repeatable, and designed for maximum efficiency. The order of operations is critical.
-
START (Cold Boot): The TPL5111 activates the power converter, supplying power to the system.
app_mainbegins execution. -
IMMEDIATE CRITICAL ACTIONS:
- Initialize Critical Drivers: The bare minimum of drivers needed for immediate measurements are initialized (
bme280_driver,battery_monitor,power_manager). - Start Battery Measurement: The firmware immediately enables the external voltage divider. This starts the ~25ms physical process of charging the filter capacitor. This process now runs in the background.
- Perform BME280 Measurement: With the battery measurement stabilizing in the background, the CPU immediately performs the BME280 sensor reading. This is done as early as possible to get a temperature reading before the CPU core begins to self-heat.
- Initialize Critical Drivers: The bare minimum of drivers needed for immediate measurements are initialized (
-
LATENCY HIDING & NETWORK INIT:
- While the battery measurement capacitor is charging, the firmware productively uses this time to initialize the time-consuming network stack (
wifi_serviceandespnow_sender). This hides the 25ms delay, effectively making the battery stabilization time "free".
- While the battery measurement capacitor is charging, the firmware productively uses this time to initialize the time-consuming network stack (
-
FINALIZE & TRANSMIT:
- Control is passed to the
sensor_manager. - It finalizes the battery measurement by reading the now-stable voltage from the ADC.
- It assembles all data (BME280, battery) into a payload, calculates a CRC, and hands it to the
espnow_sender. - The
espnow_sendertransmits the packet and waits for a hardware-level acknowledgement (ACK) from the receiver.
- Control is passed to the
-
SHUTDOWN:
- Upon completion of the transmission (successful or not), a HIGH pulse is generated on the
DONEpin. - The TPL5111 receives the signal and immediately cuts off power to the system.
- A fail-safe mechanism is in place: if the shutdown signal fails, the MCU enters its deepest possible sleep state to conserve energy until the TPL5111's own watchdog timer performs a hard power cycle.
- Upon completion of the transmission (successful or not), a HIGH pulse is generated on the
This project is designed for rapid setup using the provided configuration files, while still requiring user-specific network details.
This repository includes a pre-configured sdkconfig file containing all the performance optimizations listed below. This saves you the time of setting them manually.
However, you MUST configure your own network-specific parameters before building and flashing.
To do this, run idf.py menuconfig and navigate to the following menu:
---> ESP32C3 Zero Standby Sensor Configuration
Inside this menu, you must set the following values to match your receiver/gateway:
Gateway MAC Address: The MAC address of your ESP-NOW receiver.ESPNOW primary master key (PMK): A 16-character string shared with the receiver.ESPNOW local master key (LMK): A 16-character string shared with the receiver for this specific device.ESPNOW Channel: The Wi-Fi channel (1-13) on which both devices will communicate.I2C Pin Configuration: Specify the GPIO pins used by the I2C master for the BME280.
For reference, here is a detailed list of the critical optimizations applied in the provided sdkconfig to achieve an ultra-fast boot time (cold boot to shutdown in < 100ms).
The following options, configured via idf.py menuconfig, are essential for minimizing boot time and runtime power consumption.
For maximum performance, both the main application and the bootloader should be compiled with -O2 optimization.
-
Application Optimization Level:
Compiler options ---> Optimization Level ---> Optimize for performance (-O2)
-
Bootloader Optimization Level:
Bootloader config ---> Bootloader optimization level ---> Optimize for performance (-O2)
-
Disable Console Output (CRITICAL): Disables the UART console driver entirely. This removes the initialization overhead of the UART peripheral and converts all logging macros to no-ops, significantly speeding up boot and execution.
Component config ---> ESP System Settings ---> Channel for console output (None)
-
Skip Image Validation: This is a major optimization. It instructs the bootloader to skip the time-consuming SHA-256 validation of the application image on every boot.
Bootloader config ---> [*] Skip image validation always
-
Disable Bootloader Log: Drastically speeds up the initial boot phase.
Bootloader config ---> Log ---> Bootloader log verbosity ---> No output
-
Disable Application Log: Frees up CPU cycles and reduces on-time. Set to "Info" for debugging and "None" for production.
Component config ---> Log ---> Log Level ---> Default log verbosity ---> No output
-
FreeRTOS: The project is configured for a single core and a 1000Hz tick rate.
Component config ---> FreeRTOS ---> Kernel ---> configTICK_RATE_HZ (1000)
-
Place ADC ISR in IRAM: Improves performance of ADC reads by running the code from faster RAM instead of flash.
Component config ---> ADC ---> [*] Place ISR version ADC oneshot mode read function into IRAM
-
Skip RTC Clock Calibration: Reduces boot time by skipping a time-consuming calibration step.
Component config ---> Hardware Settings ---> RTC Clock Config ---> (0) Number of cycles for RTC_SLOW_CLK calibration
The goal is to strip down the network stack to the bare minimum required for ESP-NOW.
-
Reduce Max TX Power: A critical setting to reduce peak current draw and stay within the limits of the power supply and battery. 18dBm is a safe starting point.
Component config ---> Wi-Fi ---> (18) Max WiFi TX power (dBm)
-
Enable Brownout Power Reduction: A fail-safe mechanism that reduces Tx power on the next boot if a brownout occurs.
Component config ---> PHY ---> [*] Reduce PHY TX power when brownout reset
-
Disable Unused Wi-Fi Features: ESP-NOW does not require SoftAP, WPA3, Enterprise security, or OWE. Disabling these significantly reduces the final binary size and memory footprint.
Component config ---> Wi-Fi ---> [ ] WiFi SoftAP SupportComponent config ---> Wi-Fi ---> Security features ---> [ ] Enable WPA3-Personal- And other similar options...
-
Disable LWIP (TCP/IP Stack): ESP-NOW operates at the MAC layer and does not use the TCP/IP stack (LWIP). Disabling IPv6 and DHCP server further reduces code size and memory usage.
Component config ---> LWIP ---> [ ] Enable IPv6Component config ---> LWIP ---> [ ] DHCPS: Enable IPv4 Dynamic Host Configuration Protocol Server (DHCPS)
-
SPI Flash Configuration: Using the fastest possible mode and frequency reduces the time spent loading the application from flash.
Serial flasher config ---> Flash SPI mode ---> QIOSerial flasher config ---> Flash SPI speed ---> 80 MHz
-
Disable PHY Calibration: Skips Wi-Fi/Bluetooth PHY calibration on boot, saving a significant amount of time. This is safe as calibration data is stored in NVS and loaded on first boot.
Component config ---> PHY ---> [*] Store phy calibration data in NVSComponent config ---> PHY ---> Calibration mode (Calibration none)
By default, the ESP32-C3's internal Boot ROM prints diagnostic messages to the UART during the very first moments of startup. This adds a significant delay. Disabling this is a one-way, irreversible operation that requires burning eFuses.
Warning: Burning eFuses is permanent. Proceed with caution.
To achieve the fastest possible boot time, connect the device and run the following commands using espefuse.py:
# Disable ROM logs for UART0
espefuse.py --port YOUR_PORT burn_efuse UART_PRINT_CONTROL 3
# For ESP32-C3 with built-in USB-JTAG, also disable its ROM logs
espefuse.py --port YOUR_PORT burn_efuse DIS_USB_SERIAL_JTAG_ROM_PRINT 1
The theoretical optimizations have been verified using a Nordic Power Profiler Kit II (PPK2). The chart below captures a single, complete active cycle — from cold boot, through sensor reading and Wi-Fi initialization, to ESP-NOW transmission and final power-off.
(Fig. 1: Complete active cycle profile captured with PPK2 @ 3.6V)
| Metric | Measured Value | Significance |
|---|---|---|
| Total On-Time | ~80 ms | Surpasses the goal of < 100ms. Minimizes the time the battery is under load. |
| Total Charge | ~3.05 mC | Extremely low energy cost per transmission. 1 mC ≈ 0.278 µAh, so one cycle costs just ~0.85 µAh. |
| Average Current | ~38 mA | Efficient operation during the active phase. |
| Peak Current | ~430 mA | Represents the short Wi-Fi TX burst. Handled by input capacitors to protect the high-impedance battery. |
Conclusion: With a total charge of 3.05 mC per cycle and a 10-minute interval, the average current consumption of the electronics is approximately 0.25 µA (plus the ~200nA quiescent current). This confirms that the system's battery life is strictly limited by the battery's self-discharge rate, not the device's power consumption.