ESP32 LiPo/Reading the battery/12. Read the cell on IO36
Reading the battery · 12 of 13

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

FromTo
CellShield LIPO BTT, red wire to +
Shield TO ESP32The board's battery socket, with a cable from the box
BATTERY MONITOR padBridged with solder
Shield switchON

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

Read the cell on battery, with the radio quiet
on USB
The cell, resting3.70 V
When the sketch reads it
Sketch reads
3.85 V
Off by
+0.15 V
That is the charger, not the cell. With USB in, the charger pushes current in and holds the cell above its resting voltage, up to 4.2 V. A reading taken now tells you the charger is working.

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

read_cell.ino

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

It prints 0.00 V

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.

It says about 4.2 V whatever the cell is

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.

It is 0.1 V away from my meter

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.

The number jumps around

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.

Where this goes next

Four failures, and the order to check each in.

When it will not run on battery

Edit this page — content/books/esplipo/read-the-cell.mdx

Community

Questions about this product

See what other owners have asked, and read their solutions.

Ask a question ↗

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.

Browse Modules and blocks on the forum