Battery deep-sleep sensor
Wake every five minutes, read a DS18B20 and the battery voltage, then go back to deep sleep. A boot counter survives in RTC memory.
Wiring
| From | To board | Note |
|---|---|---|
| DS18B20 VDD (red) | 3V3 | |
| DS18B20 GND (black) | GND | |
| DS18B20 DATA (yellow) | GPIO 4 | 4.7 kΩ pull-up from DATA to 3V3 |
| Battery + through 100 kΩ | GPIO 35 | Divider top. Never connect the battery straight to a GPIO |
| 100 kΩ from GPIO 35 | GND | Divider bottom: the pin sees half the battery voltage |
Tested on this board
Not run on this board yet. Treat the wiring and code as a first draft.
Firmware, Arduino sketch for ESP32 Dev Module
#include <OneWire.h>
#include <DallasTemperature.h>
const int PROBE_PIN = 4;
const int BAT_PIN = 35; // ADC1 pin at the middle of a 100k/100k divider
const uint64_t SLEEP_MINUTES = 5;
// RTC memory keeps these through deep sleep. Losing power or flashing clears them.
RTC_DATA_ATTR uint32_t boots = 0;
RTC_DATA_ATTR float lastC = NAN;
OneWire oneWire(PROBE_PIN);
DallasTemperature probes(&oneWire);
void setup() {
Serial.begin(115200);
boots++;
bool timer = esp_sleep_get_wakeup_cause() == ESP_SLEEP_WAKEUP_TIMER;
Serial.printf("boot %lu, woke by %s\n", (unsigned long)boots, timer ? "timer" : "power-on or reset");
probes.begin();
probes.requestTemperatures(); // about 750 ms at full resolution
float c = probes.getTempCByIndex(0);
if (c == DEVICE_DISCONNECTED_C) {
Serial.println("no probe: check the data wire and the 4.7 kOhm pull-up");
} else {
Serial.printf("temp %.2f C, last time %.2f C\n", c, lastC);
lastC = c;
}
uint32_t mv = 0;
for (int i = 0; i < 8; i++) mv += analogReadMilliVolts(BAT_PIN);
float battery = mv / 8.0f * 2.0f / 1000.0f; // the divider halves the voltage
Serial.printf("battery %.2f V\n", battery);
Serial.printf("awake %lu ms, sleeping %llu min\n", millis(), (unsigned long long)SLEEP_MINUTES);
Serial.flush();
esp_sleep_enable_timer_wakeup(SLEEP_MINUTES * 60ULL * 1000000ULL);
esp_deep_sleep_start();
}
void loop() {} // never reached: every wake starts again at setup()Wiring
| From | To board | Note |
|---|---|---|
| DS18B20 VDD (red) | 3V3 | |
| DS18B20 GND (black) | GND | |
| DS18B20 DATA (yellow) | GPIO 4 | 4.7 kΩ pull-up from DATA to 3V3 |
| Battery + through 100 kΩ | GPIO 7 | Divider top. Never connect the battery straight to a GPIO |
| 100 kΩ from GPIO 7 | GND | Divider bottom: the pin sees half the battery voltage |
Tested on this board
Not run on this board yet. Treat the wiring and code as a first draft.
Firmware, Arduino sketch for ESP32S3 Dev Module
#include <OneWire.h>
#include <DallasTemperature.h>
const int PROBE_PIN = 4;
const int BAT_PIN = 7; // ADC1 pin at the middle of a 100k/100k divider
const uint64_t SLEEP_MINUTES = 5;
// RTC memory keeps these through deep sleep. Losing power or flashing clears them.
RTC_DATA_ATTR uint32_t boots = 0;
RTC_DATA_ATTR float lastC = NAN;
OneWire oneWire(PROBE_PIN);
DallasTemperature probes(&oneWire);
void setup() {
Serial.begin(115200);
boots++;
bool timer = esp_sleep_get_wakeup_cause() == ESP_SLEEP_WAKEUP_TIMER;
Serial.printf("boot %lu, woke by %s\n", (unsigned long)boots, timer ? "timer" : "power-on or reset");
probes.begin();
probes.requestTemperatures(); // about 750 ms at full resolution
float c = probes.getTempCByIndex(0);
if (c == DEVICE_DISCONNECTED_C) {
Serial.println("no probe: check the data wire and the 4.7 kOhm pull-up");
} else {
Serial.printf("temp %.2f C, last time %.2f C\n", c, lastC);
lastC = c;
}
uint32_t mv = 0;
for (int i = 0; i < 8; i++) mv += analogReadMilliVolts(BAT_PIN);
float battery = mv / 8.0f * 2.0f / 1000.0f; // the divider halves the voltage
Serial.printf("battery %.2f V\n", battery);
Serial.printf("awake %lu ms, sleeping %llu min\n", millis(), (unsigned long long)SLEEP_MINUTES);
Serial.flush();
esp_sleep_enable_timer_wakeup(SLEEP_MINUTES * 60ULL * 1000000ULL);
esp_deep_sleep_start();
}
void loop() {} // never reached: every wake starts again at setup()Wiring
| From | To board | Note |
|---|---|---|
| DS18B20 VDD (red) | 3V3 | |
| DS18B20 GND (black) | GND | |
| DS18B20 DATA (yellow) | GPIO 4 | 4.7 kΩ pull-up from DATA to 3V3 |
| Battery + through 100 kΩ | GPIO 3 | Divider top. Never connect the battery straight to a GPIO |
| 100 kΩ from GPIO 3 | GND | Divider bottom: the pin sees half the battery voltage |
Tested on this board
Not run on this board yet. Treat the wiring and code as a first draft.
Firmware, Arduino sketch for ESP32C3 Dev Module
#include <OneWire.h>
#include <DallasTemperature.h>
const int PROBE_PIN = 4;
const int BAT_PIN = 3; // ADC1 pin at the middle of a 100k/100k divider
const uint64_t SLEEP_MINUTES = 5;
// RTC memory keeps these through deep sleep. Losing power or flashing clears them.
RTC_DATA_ATTR uint32_t boots = 0;
RTC_DATA_ATTR float lastC = NAN;
OneWire oneWire(PROBE_PIN);
DallasTemperature probes(&oneWire);
void setup() {
Serial.begin(115200);
boots++;
bool timer = esp_sleep_get_wakeup_cause() == ESP_SLEEP_WAKEUP_TIMER;
Serial.printf("boot %lu, woke by %s\n", (unsigned long)boots, timer ? "timer" : "power-on or reset");
probes.begin();
probes.requestTemperatures(); // about 750 ms at full resolution
float c = probes.getTempCByIndex(0);
if (c == DEVICE_DISCONNECTED_C) {
Serial.println("no probe: check the data wire and the 4.7 kOhm pull-up");
} else {
Serial.printf("temp %.2f C, last time %.2f C\n", c, lastC);
lastC = c;
}
uint32_t mv = 0;
for (int i = 0; i < 8; i++) mv += analogReadMilliVolts(BAT_PIN);
float battery = mv / 8.0f * 2.0f / 1000.0f; // the divider halves the voltage
Serial.printf("battery %.2f V\n", battery);
Serial.printf("awake %lu ms, sleeping %llu min\n", millis(), (unsigned long long)SLEEP_MINUTES);
Serial.flush();
esp_sleep_enable_timer_wakeup(SLEEP_MINUTES * 60ULL * 1000000ULL);
esp_deep_sleep_start();
}
void loop() {} // never reached: every wake starts again at setup()Parts
| Quantity | Part | Where to buy |
|---|---|---|
| 1× | ESP32 dev board | Buy at Amazon |
| 1× | DS18B20 waterproof probe | Buy at Adafruit |
| 1× | 4.7 kΩ resistor, pull-up for the probe data line | Buy at Amazon |
| 2× | 100 kΩ resistors, battery divider | Buy at Adafruit |
| 1× | Single-cell Li-ion or LiPo battery with protection, and a way to get 3.3 V from it |
Amazon buy links may include an Associates tag, and this site may earn a commission from qualifying Amazon purchases at no extra cost to you. Adafruit links are direct.
Libraries
OneWire, DallasTemperature
Load it
- In the Arduino IDE, install the esp32 board package and pick the board named above the sketch.
- Install the libraries listed above from the Library Manager.
- Paste the sketch, choose the board's port and upload. Hold BOOT if the upload stalls at Connecting.
- Close the IDE's serial monitor, then open the bench here to watch the output.
Field notes
- A dev board is the wrong thing to run from a battery. Its voltage regulator, USB serial chip and power LED keep drawing current in deep sleep, typically milliamps rather than the microamps the chip itself needs. Measure your own board with a meter in series with the battery before trusting a battery-life estimate.
- Espressif's datasheets put the chip alone at around 5 to 10 µA in deep sleep with the RTC timer running, depending on the chip. Getting near that needs a board built for low power, or a bare module with a low-quiescent regulator.
- A full Li-ion cell is 4.2 V, too high for the 3V3 pin. Use a board with a battery connector, or a low-dropout 3.3 V regulator with a small quiescent current. The 5V pin's regulator on most dev boards drops too much voltage to run from one cell for long.
- The 100k/100k divider draws about 20 µA from the battery all the time. For long battery life, switch it with a MOSFET or use larger resistors with a 100 nF capacitor from the pin to GND.
- The battery pin must be an ADC1 pin: ADC2 cannot be read while Wi-Fi is on, which matters as soon as you add a Wi-Fi upload.
- On the S3 and C3 with the native USB port, the serial port disappears during deep sleep and the bench loses the connection on every sleep. Use the UART USB port, or reconnect after each wake.
Not tested yet
- Not tested on hardware yet, and no current draw has been measured. Compiles for every board listed with Arduino-ESP32 core 3.3.12.
Built it? Send a build report with your board and what the serial port printed. Working reports become the "Tested on this board" entries above.