Read the cell on IO36
With the pad bridged, a dozen lines read the cell: average sixteen calibrated readings from GPIO 36, double them, and correct by one number you get from a meter. Read it on battery with the radio quiet, or you are measuring the charger or the burst instead of the cell.
What goes where
| From | To |
|---|---|
| Cell | Shield LIPO BTT, red wire to + |
| Shield TO ESP32 | The board's battery socket, with a cable from the box |
| BATTERY MONITOR pad | Bridged with solder |
| Shield switch | ON |
Upload it, and set CAL
Upload with USB in and open the Serial Monitor at 115200 baud. With USB in, the
number is not the cell's resting voltage — the charger is holding it up — but it
is what a meter across the cell sees at the same moment, and that is all
calibration needs. Touch the meter's probes to the two metal pins at the back of
the LIPO BTT socket, compare, set CAL, and upload again.
Then unplug USB. The Serial Monitor has nothing to talk to on battery, which is why the sketch has a second way to tell you: the LED on GPIO 22 comes on when the cell falls below 3.4 V.
Why not raw counts
The version of this sketch that circulates reads analogRead(), divides by 4095
and multiplies by 3.3. That assumes an ADC that runs in a straight line from
0 V at count 0 to 3.3 V at count 4095. The ESP32's does not: at its widest
setting it is accurate from about 150 mV to 2450 mV, flattens above that, and
each chip differs from the next by several percent.
analogReadMilliVolts() does the conversion with the calibration data Espressif
stores in each chip, and returns millivolts. After that, Espressif's datasheet
allows ±60 mV at the pin. The sketch doubles that along with the reading, so
±0.12 V at the cell — which is the reason for CAL.
When to read it
Only one of those is the cell. With USB in, the charger is pushing current into the cell and holding it up. During a Wi-Fi burst, the cell sags under 240 mA. On battery with the radio quiet, almost nothing flows and the reading is the cell's own voltage.
In a sketch with Wi-Fi, read the cell in setup() before WiFi.begin(), or
between transmissions — never in the middle of sending.
What to do with the number
Print volts, not a percentage. A lithium cell spends most of its charge between about 3.9 V and 3.6 V, so a few hundredths of a volt of error moves a percentage by a lot. Voltage is a poor fuel gauge and a good alarm.
Use it as the alarm: when it reads under 3.4 V, save what matters, turn the radio off, and go into deep sleep for good. From the cell to 3.3 V is why 3.4.
The code
Reads the cell every two seconds, prints it while USB is connected, and lights the LED on GPIO 22 when the cell falls below 3.4 V — the part you can see on battery. Needs the board on a shield with the BATTERY MONITOR pad bridged.
// Lonely Binary ESP32 LiPo board: read the cell on IO36.
//
// Wiring (board on a shield, antenna at the switch end):
// cell -> shield LIPO BTT, red wire to +
// shield TO ESP32 -> board battery socket, with a cable from the box
// BATTERY MONITOR pad on the shield bridged with solder
// shield switch -> ON
//
// Arduino IDE:
// Tools > Board > esp32 > ESP32 Dev Module
// Tools > Upload Speed > 921600, or 115200 if the upload stops partway
// Serial Monitor at 115200 baud
const int BATT = 36; // VP. The pad joins the divider to this pin
const int LED = 22; // on-board LED, LOW lights it
const float DIVIDER = 2.0; // two equal resistors: the pin sees half
const float CAL = 1.0; // yours, from a meter. See the note
const int SAMPLES = 16;
const float LOW_V = 3.40; // finish up before the cell's protection does
float readCell() {
uint32_t sum = 0;
for (int i = 0; i < SAMPLES; i++) {
sum += analogReadMilliVolts(BATT); // calibrated millivolts, not counts
delay(2);
}
return sum / (float)SAMPLES / 1000.0f * DIVIDER * CAL;
}
void setup() {
Serial.begin(115200);
pinMode(LED, OUTPUT);
digitalWrite(LED, HIGH); // off
analogSetPinAttenuation(BATT, ADC_11db); // the widest range
}
void loop() {
float v = readCell();
bool low = v < LOW_V;
digitalWrite(LED, low ? LOW : HIGH); // the alarm you can see on battery
Serial.printf("cell %.2f V%s\n", v, low ? " LOW: finish up" : "");
delay(2000);
}CAL is the number to change. Start at 1.0, measure the cell with a meter at the same moment the sketch prints, and set CAL to the meter's reading divided by the sketch's. Nobody else's CAL is right for your board: it corrects your ADC and your resistors.
When it does not work
GPIO 36 is not connected to the divider. The pad is not bridged, the switch is OFF, or the board is seated turned round — then the shield's 36 socket holds GPIO 13. Check them in that order.
USB is plugged in and the charger is holding the cell up, which is exactly what it should do. The reading that matters is the one taken on battery, and on battery the LED on GPIO 22 is what reports it.
That is what CAL is for. The ADC's calibrated error at the pin is up to ±60 mV, which the doubling turns into ±0.12 V at the cell, and the two resistors add their own tolerance. Set CAL from your meter and it stays right for that board.
Raise SAMPLES to 64, and make sure the reading is not being taken while Wi-Fi transmits. A reading during a burst measures the cell sagging under load.
Edit this page — content/books/esplipo/read-the-cell.mdx
Questions about this product
See what other owners have asked, and read their solutions.
ESP32 LiPo Dev Kit: 3 Boards with LiPo Charger + 3 Shields
Loading discussions…
Discuss this article
Ask about this page. The answer stays here, on the page it belongs to, for whoever hits the same wall next.