A TP4056 module and an 18650 cell turn a D1 mini into a wireless sensor you can leave in the garden or on the terrace. The cell connects to the charger’s B+/B−, and the charger’s OUT+/OUT− power the D1 mini through its 5V pin and regulator. Charging happens through the TP4056’s own USB socket.
The D1 mini has a neat trick for checking the cell: its A0 divider is already on the board, so one extra 100 kΩ resistor from OUT+ to A0 is all it takes to read up to 4.2 V. The sketch reads the voltage, then deep-sleeps for five minutes (D0 jumpered to RST) to make the cell last.
Wiring
| Wemos D1 mini | TP4056 Li-ion charger module (with DW01A protection) | Wire | Notes |
|---|---|---|---|
| 18650 Li-ion cell (protected) · + | B+ | red | |
| 18650 Li-ion cell (protected) · − | B- | black | |
| 5V | OUT+ | red | The cell (3.0–4.2 V) feeds the D1 mini’s 3.3 V regulator through the 5V pin. Never connect OUT+ to 3V3. |
| GND | OUT- | black | |
| A0 | OUT+ | orange | Via 100 kΩ — Adds to the board’s 220 kΩ / 100 kΩ divider: A0 then reads 0–4.2 V. |
5 V input (4.5–5.5 V, USB) part, 3.3 V board. No logic pins. OUT+ is the raw cell voltage, 3.0–4.2 V: feed it to an ESP32 or Pico through a low-dropout 3.3 V regulator (HT7333, MCP1700) or the board’s VSYS/VIN pin, and to a 5 V board through a boost converter (MT3608). Never connect it straight to a 3.3 V pin.
Board quirks
- The D1 mini’s A0 already has a 220 kΩ / 100 kΩ divider (about 3.2 V full scale). One extra 100 kΩ in series makes it (100 + 220 + 100) / 100 × 1.0 V = 4.2 V full scale — exactly a full Li-ion cell — with no other parts. It draws about 10 µA from the cell all the time.
- OUT+ goes to the 5V pin so the cell passes through the board’s 3.3 V regulator. On 3V3 directly, a fully charged cell at 4.2 V would exceed the ESP8266’s 3.6 V limit.
- The regulator needs some headroom: below about 3.4–3.5 V the 3.3 V rail starts to sag and Wi-Fi can brown out. The sketch stops waking up below 3.3 V; the TP4056’s protection chip cuts off much lower (about 2.5 V) to protect the cell itself.
- Don’t plug the D1 mini’s USB in while the cell is connected unless you know your board has a diode between USB 5 V and the 5V pin. Without one, USB 5 V is pushed into OUT+ and the cell. Charge through the TP4056’s own USB socket.
- Use a module with the protection chip (DW01 + 8205, labelled OUT+/OUT−) and a protected or branded cell. Keep the charger off flammable surfaces; Li-ion cells left charging in a hot room in May deserve respect.
- Resistor tolerance (±5 %) shifts the reading. Measure the cell with a multimeter once and set
CALto correct it.
Code
// Battery-powered Wemos D1 mini (ESP8266) from a TP4056 charger module + one Li-ion cell,
// measuring the cell voltage on A0 and deep-sleeping between readings.
// OUT+ → 5V (through the board's 3.3 V regulator), OUT- → GND,
// OUT+ → 100 kΩ → A0 (with the board's 220k/100k divider this makes A0 read 0-4.2 V),
// D0 → RST for deep-sleep wake. Never connect OUT+ to the 3V3 pin.
#include <ESP8266WiFi.h>
const float FULL_SCALE_V = 4.2; // (100k + 220k + 100k) / 100k x 1.0 V
const float CAL = 1.00; // multimeter reading / printed reading, to trim resistor tolerance
const float LOW_CUTOFF_V = 3.30; // below this, stop waking up
const uint32_t SLEEP_SECONDS = 300;
float batteryVolts() {
long sum = 0;
for (int i = 0; i < 16; i++) {
sum += analogRead(A0);
delay(2);
}
return (sum / 16.0f) * FULL_SCALE_V / 1023.0f * CAL;
}
void setup() {
WiFi.mode(WIFI_OFF);
Serial.begin(115200);
Serial.println();
float v = batteryVolts();
// Rough guide only: a Li-ion cell's voltage is not linear with charge.
int pct = constrain((int)((v - 3.3f) / (4.2f - 3.3f) * 100), 0, 100);
Serial.printf("Battery %.2f V (about %d %%)\n", v, pct);
if (v < LOW_CUTOFF_V) {
Serial.println("Battery low - sleeping until reset. Charge it via the TP4056's USB.");
ESP.deepSleep(0); // 0 = sleep until RST; draws only the sleep current
}
Serial.printf("Sleeping %lu s\n", (unsigned long)SLEEP_SECONDS);
ESP.deepSleep(SLEEP_SECONDS * 1000000ULL);
}
void loop() {}// Battery-powered Wemos D1 mini (ESP8266) from a TP4056 charger module + one Li-ion cell,
// measuring the cell voltage on A0 and deep-sleeping between readings.
// OUT+ → 5V (through the board's 3.3 V regulator), OUT- → GND,
// OUT+ → 100 kΩ → A0 (with the board's 220k/100k divider this makes A0 read 0-4.2 V),
// D0 → RST for deep-sleep wake. Never connect OUT+ to the 3V3 pin.
#include <ESP8266WiFi.h>
const float FULL_SCALE_V = 4.2; // (100k + 220k + 100k) / 100k x 1.0 V
const float CAL = 1.00; // multimeter reading / printed reading, to trim resistor tolerance
const float LOW_CUTOFF_V = 3.30; // below this, stop waking up
const uint32_t SLEEP_SECONDS = 300;
float batteryVolts() {
long sum = 0;
for (int i = 0; i < 16; i++) {
sum += analogRead(A0);
delay(2);
}
return (sum / 16.0f) * FULL_SCALE_V / 1023.0f * CAL;
}
void setup() {
WiFi.mode(WIFI_OFF);
Serial.begin(115200);
Serial.println();
float v = batteryVolts();
// Rough guide only: a Li-ion cell's voltage is not linear with charge.
int pct = constrain((int)((v - 3.3f) / (4.2f - 3.3f) * 100), 0, 100);
Serial.printf("Battery %.2f V (about %d %%)\n", v, pct);
if (v < LOW_CUTOFF_V) {
Serial.println("Battery low - sleeping until reset. Charge it via the TP4056's USB.");
ESP.deepSleep(0); // 0 = sleep until RST; draws only the sleep current
}
Serial.printf("Sleeping %lu s\n", (unsigned long)SLEEP_SECONDS);
ESP.deepSleep(SLEEP_SECONDS * 1000000ULL);
}
void loop() {}Expected serial output
Battery 3.97 V (about 74 %)
Sleeping 300 sBattery 3.97 V (about 74 %) Sleeping 300 s
Common errors
Reading is about 30 % too high
The 100 kΩ series resistor is missing (or shorted), so the sketch’s 4.2 V scale is applied to a 3.2 V divider. Check the resistor between OUT+ and A0.
Board never wakes after the first reading
D0 must be wired to RST for deep sleep. Full fix.
Board resets as soon as Wi-Fi starts on battery
The cell is low and the regulator can’t supply the Wi-Fi current peaks. Charge the cell, and add a 470 µF capacitor between 3V3 and GND.