Community project

LiFePO4 Battery Monitor

ESP32
Photo of LiFePO4 Battery Monitor
Generated with AI

Shaikh Tajmul haque

Published September 13, 2026

This project builds a real-time battery monitor for a 1S LiFePO4 cell using an ESP32 microcontroller. The system measures voltage, current, and power consumption through an INA219 current sensor, displays readings on an OLED screen, and estimates charge percentage based on cell voltage characteristics. The guide includes a complete wiring diagram, parts list, and firmware ready to flash.

Builders will learn how to interface multiple I2C sensors with an ESP32, implement LiFePO4 state-of-charge estimation, and create a responsive display that shows real-time charging and discharging data. Assembly takes about an hour and requires basic soldering skills to connect the battery pack, charger module, current sensor, and display.

Wiring diagram

Wiring diagram for LiFePO4 Battery Monitor

Gather all the parts

QtyComponent
1

SSD1306 OLED

0.96 in, 128×64 I2C

0.96 inch 128x64 OLED display with I2C interface

1

Adafruit INA219 High-Side DC Current Sensor

I2C address 0x40

INA219 high-side current and bus-voltage monitor breakout. It is powered from 3.3V or 5V and communicates over I2C. Route the measured load current through VIN+ and VIN-; those shunt terminals are part of the power path, not MCU GPIO.

1

Adafruit bq25185 USB / DC / Solar Lithium Ion/Polymer Charger

Configured for 3.6 V LiFePO4

Multi-source LiPoly/LiIon/LiFePO4 charger based on the bq25185, accepting USB, DC, or solar input. Supports 250mA, 500mA, or 1A charge rates; good value upgrade from MCP73831-based boards.

1

3.3v Buck Boost

3.3 V output

TI TPS63030/TPS63031 high-efficiency single-inductor buck-boost converter family with 1A switches. Used to hold a regulated rail when battery voltage crosses above and below the target output.

1

Protected 1S LiFePO4 battery pack

3.2 V nominal, 3.6 V full, protected

A single-cell rechargeable lithium iron phosphate battery that stores power for the monitor and the connected load.

Assemble it in 4 steps

1. Keep the battery leads safe

Leave the LiFePO4 battery disconnected while building. Connect battery_1 BAT- to the common GND rail, then connect battery_1 BAT+ to ina219_1 VIN+. This routes every battery amp through the meter.

  • Use short, adequately thick wires for the two battery-positive connections.
  • Do not let the battery's red and black leads touch — that can heat the wires and damage the battery.

2. Connect the charger and 3.3 V supply

Connect ina219_1 VIN- to charger_1 BAT. Connect charger_1 GND and boost_1 GND to the common GND rail. Connect charger_1 LOAD to boost_1 VIN, then connect boost_1 VOUT to the 3V3 rail. Set the boost module to exactly 3.3 V with a multimeter before attaching the ESP-12E.

  • The charger USB-C socket is the input used to recharge the battery.
  • Do not connect a LiFePO4 cell to a charger unless its charge setting is confirmed for 3.6 V LiFePO4 cells — a 4.2 V lithium-polymer setting can damage the cell.
  • Do not exceed 3.3 V on the ESP-12E, OLED, or INA219 logic supply.

3. Wire the display and meter

Connect oled_1 VCC and ina219_1 VCC to 3V3 (power). Connect their GND pins to GND (ground). Connect both SDA pins to ESP-12E GPIO4/D2 (data), and both SCL pins to GPIO5/D1 (clock).

  • The OLED and INA219 deliberately share the two SDA and SCL wires.
  • Make sure VCC and GND are not swapped — swapped power can damage the OLED or sensor.

4. Power and program the ESP-12E

Connect the ESP-12E 3V3 and GND pins to the matching rails. Build the normal ESP-12E boot circuit: EN and RST held high with 10 kΩ resistors, GPIO0 held high with 10 kΩ, and GPIO15 held low with 10 kΩ. Use a 3.3 V USB-to-serial adapter for programming, temporarily holding GPIO0 at GND while resetting to enter programming mode.

  • Check the 3V3 rail with a multimeter before inserting or powering the ESP-12E.
  • A 5 V serial adapter connected to ESP-12E signal pins can permanently damage the module.
  • Do not attach the battery until the wiring has been checked for shorts.

Review all connections

1. Connections between "battery_1" and "ESP32"

Functionbattery_1ESP32
powerBAT+Adafruit INA219 High-Side DC Current Sensor VIN+EXT
groundBAT-GND

2. Connections between "ina219_1" and "ESP32"

Functionina219_1ESP32
dataVIN-Adafruit bq25185 USB / DC / Solar Lithium Ion/Polymer Charger BATEXT
powerVCC3V3
groundGNDGND
i2cSDAGPIO 4
i2cSCLGPIO 5

3. Connections between "charger_1" and "ESP32"

Functioncharger_1ESP32
groundGNDGND
powerLOAD3.3v Buck Boost VINEXT

4. Connections between "boost_1" and "ESP32"

Functionboost_1ESP32
groundGNDGND
powerVOUT3V3

5. Connections between "oled_1" and "ESP32"

Functionoled_1ESP32
powerVCC3V3
groundGNDGND
i2cSDAGPIO 4
i2cSCLGPIO 5

Deploy the firmware

#include <Arduino.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <Adafruit_INA219.h>


// Forward declarations
float lifepo4Percent(float volts);
void drawScreen(float volts, float milliamps, float milliwatts);

constexpr uint8_t I2C_SDA_PIN = 4;
constexpr uint8_t I2C_SCL_PIN = 5;
constexpr uint8_t SCREEN_WIDTH = 128;
constexpr uint8_t SCREEN_HEIGHT = 64;

Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);
Adafruit_INA219 ina219;

String lastScreen = "";
unsigned long lastSampleMs = 0;

float lifepo4Percent(float volts) {
  // Resting-voltage estimate for one LiFePO4 cell; load current affects this estimate.
  if (volts >= 3.60f) return 100.0f;
  if (volts >= 3.45f) return 95.0f;
  if (volts >= 3.35f) return 80.0f;
  if (volts >= 3.30f) return 60.0f;
  if (volts >= 3.25f) return 40.0f;
  if (volts >= 3.20f) return 20.0f;
  if (volts >= 3.10f) return 10.0f;
  return 0.0f;
}

void drawScreen(float volts, float milliamps, float milliwatts) {
  float pct = lifepo4Percent(volts);
  const char *direction = milliamps > 15.0f ? "DISCHARGE" : (milliamps < -15.0f ? "CHARGING" : "IDLE");
  float amps = fabs(milliamps) / 1000.0f;
  float watts = fabs(milliwatts) / 1000.0f;

  String screen = String(volts, 3) + "|" + String(milliamps, 0) + "|" + String(milliwatts, 0) + "|" + String((int)pct) + "|" + direction;
  if (screen == lastScreen) return;
  lastScreen = screen;

  display.clearDisplay();
  display.setTextColor(SSD1306_WHITE);
  display.setTextSize(1);
  display.setCursor(0, 0);
  display.print("LiFePO4 MONITOR");
  display.setCursor(0, 13);
  display.print("Bat: "); display.print(volts, 3); display.println(" V");
  display.setCursor(0, 25);
  display.print("Current: "); display.print(amps, 3); display.println(" A");
  display.setCursor(0, 37);
  display.print("Power: "); display.print(watts, 2); display.println(" W");
  display.setCursor(0, 49);
  display.print(direction); display.print("  "); display.print((int)pct); display.println("%");
  display.display();
}

void setup() {
  Wire.begin(I2C_SDA_PIN, I2C_SCL_PIN);
  display.begin(SSD1306_SWITCHCAPVCC, 0x3C);
  display.clearDisplay();
  display.setTextColor(SSD1306_WHITE);
  display.setTextSize(1);
  display.setCursor(0, 0);
  display.println("Starting meter...");
  display.display();

  if (!ina219.begin()) {
    display.setCursor(0, 16);
    display.println("INA219 not found");
    display.display();
    while (true) delay(100);
  }
  ina219.setCalibration_32V_2A();
}

void loop() {
  if (millis() - lastSampleMs < 1000) return;
  lastSampleMs = millis();

  float volts = ina219.getBusVoltage_V() + ina219.getShuntVoltage_mV() / 1000.0f;
  float milliamps = ina219.getCurrent_mA();
  float milliwatts = ina219.getPower_mW();
  drawScreen(volts, milliamps, milliwatts);
}

Remix this project

Make it yours in one click

Open a full copy of this project in your own Schematik workspace — diagram, code, parts, and assembly steps included. Swap the sensor, add features, or redesign the whole thing with AI. The author's original stays untouched.

Open in Schematik