Contents — 26 sections
Project Overview
Watches every cell in a battery pack — voltage, temperature and balance — so weak cells and thermal danger are caught before they turn a storage battery into a hazard.
A battery pack is only as safe and healthy as its weakest cell, and the whole-pack voltage that a simple gauge shows hides everything that matters. A pack reading a healthy total can contain one cell drifting low, one running hot, or a growing imbalance that will slowly destroy the pack and, in lithium chemistries, can lead to overcharge, over-discharge or thermal events. Serious battery systems therefore monitor each cell individually. This project builds that monitor: it measures per-cell voltage, pack temperature(s) and current, tracks the balance between cells, and warns of the conditions — a weak cell, an imbalance, a thermal excursion — that precede failure, so a storage battery stays safe and lasts.
The core is per-cell voltage sensing, because cells in a series pack must be kept within a safe window individually: over-charging or over-discharging any single cell damages it and, for lithium, is a safety risk, and the pack must be managed to the most extreme cell, not the average. The monitor measures each cell, watches for any cell straying toward its limits, and tracks the spread between the highest and lowest cell — a growing spread (imbalance) is an early sign of a weak or ageing cell and of a pack that needs balancing. Temperature is watched because heat is the enemy of batteries and the herald of danger: an abnormally hot cell, or a rising rate of temperature, is a warning that must be heeded. Current (charge/discharge) lets it estimate state of charge and enforce safe limits.
On top of monitoring, it supports balancing — bleeding charge from the highest cells so the pack charges evenly and no cell runs ahead — which extends pack life and keeps cells matched. It logs and trends every cell so degradation is visible, and it alarms on any unsafe condition. This is safety-critical work, so the project is clear: a battery management system that actually protects a pack must reliably cut off charge/discharge on fault, and lithium packs in particular demand a proper, ideally purpose-built or certified, BMS — a homebrew monitor is an educational build of the sensing and logic and a supplement, not a replacement for protection that a real pack's safety depends on. But as a per-cell voltage/temperature/balance monitor, it teaches and demonstrates exactly what keeps a battery pack safe: watch every cell, catch the weak one and the hot one early, and keep the pack in balance.
What this project does
- Measures per-cell voltage across a series pack
- Monitors pack temperature(s) and charge/discharge current
- Tracks cell imbalance (highest-to-lowest spread)
- Warns of weak cells, over/under-voltage and thermal danger
- Supports balancing to keep cells matched and extend life
- Estimates state of charge and enforces safe limits
- Logs and trends every cell so degradation is visible
Real-World Applications
| Setting | How it is used |
|---|---|
| Home/solar storage batteries | Keeping a storage pack safe and healthy with per-cell visibility and balancing. |
| DIY / repurposed packs | Monitoring 18650/LiFePO4 packs for imbalance and thermal safety. |
| E-mobility / robotics packs | Watching cells in EV/robot batteries for weak cells and heat. |
| Battery education / testing | Learning cell balancing, SoC and thermal management. |
Deployment contexts where a build of this kind earns its keep.
Features & Capabilities
- Per-cell voltage monitoring (manage to the worst cell)
- Imbalance tracking for early weak-cell detection
- Temperature and rate-of-rise thermal warning
- Cell balancing support
- State-of-charge estimation and safe-limit enforcement
- Per-cell trending for degradation
- Honest: monitoring/logic, not a replacement for protective BMS
Difficulty, Time & Required Skills
| Attribute | Value |
|---|---|
| Difficulty level | Advanced |
| Estimated completion time | 14–22 hours |
| Indicative build cost | ₹3,500 – ₹6,500 |
| Primary discipline | Energy |
| Reference platform | ESP32 DevKit V1 (ESP-WROOM-32) |
Skills you should have (or will pick up)
- Per-cell voltage measurement (mux/isolation)
- Temperature and current sensing
- Imbalance and state-of-charge estimation
- Cell balancing
- Safe-limit enforcement and alarming
Bill of Materials
Every part below is commonly available from Indian and international hobby-electronics suppliers. Prices are indicative 2026 retail figures in Indian rupees and will drift — treat them as a budgeting guide, not a quotation.
| Component | Key specification | Qty | Approx. cost |
|---|---|---|---|
| ESP32 DevKit V1 (ESP-WROOM-32) Wi-Fi transmit bursts peak near 500 mA — size the regulator accordingly. | Dual-core Xtensa LX6 @ 240 MHz, 520 KB SRAM, 4 MB flash, Wi-Fi 802.11 b/g/n + BLE 4.2, 34 GPIO, 18× 12-bit ADC, 2× 8-bit DAC | 1 | ₹450 |
| INA219 high-side DC current/power monitor Reports bus voltage, shunt voltage, current and power directly — no maths needed. | 0–26 V bus, ±3.2 A with 0.1 Ω shunt, 12-bit, ±0.5 % gain error | 1 | ₹260 |
| DS18B20 waterproof temperature probe Dozens can share one GPIO — you address them by ROM code. | −55 to +125 °C, ±0.5 °C from −10 to +85 °C, 9–12-bit resolution, unique 64-bit ROM ID | 1 | ₹160 |
| ACS712 hall-effect current sensor (20 A) Zero-offset drifts with temperature — re-zero at boot with no load. | ±20 A, 100 mV/A, 80 kHz bandwidth, 1.2 mΩ internal resistance, 2.1 kV isolation | 1 | ₹180 |
| 0.96″ SSD1306 OLED display Static images burn in — invert or scroll the screen periodically. | 128 × 64 monochrome, 1.3–3.3 V logic, 100 kHz–400 kHz I²C | 1 | ₹250 |
| microSD card 32 GB A1 class For 24/7 loggers buy a high-endurance card — normal cards die in months. | A1 rated, 10 MB/s random write, UHS-I, endurance-grade recommended | 1 | ₹450 |
| 5 V 3 A regulated SMPS adapter Measure the real output — many "3 A" adapters sag below 4.7 V at 2 A. | 100–240 VAC in, 5 V ±5 % out, 3 A, short-circuit and over-voltage protection | 1 | ₹350 |
| Per-cell voltage sensing A dedicated multi-cell monitor IC is safest/most accurate | Isolated/mux per-cell taps or a battery-monitor IC (e.g. multi-cell AFE) | 1 | ₹1,200 |
| Balancing circuitry | Passive (bleed resistors + FETs) or active balancing per cell | 1 | ₹800 |
| Pack temperature sensors | Multiple DS18B20/thermistors across the pack | 2 | ₹400 |
| Protection (for a real BMS) A monitor alone does not protect; protection cuts off on fault | Charge/discharge cutoff (FETs/contactor) — required for protection | 1 | ₹1,000 |
Estimated total: ₹5,500, excluding tools, shipping and consumables.
Tools and consumables
- Soldering iron (temperature controlled, 350 °C) with 0.8 mm 60/40 or lead-free solder
- Digital multimeter — continuity, DC volts and current ranges
- Wire strippers, flush cutters and a small set of precision screwdrivers
- Heat-shrink tubing and a heat gun (or a lighter, carefully)
- A laptop with a USB port and the toolchain listed above
Hardware Specifications
| Part | Specification | Supply | Interface | Reference |
|---|---|---|---|---|
| ESP32 DevKit V1 (ESP-WROOM-32) | Dual-core Xtensa LX6 @ 240 MHz, 520 KB SRAM, 4 MB flash, Wi-Fi 802.11 b/g/n + BLE 4.2, 34 GPIO, 18× 12-bit ADC, 2× 8-bit DAC | 3.3 V logic / 5 V USB | UART, SPI, I²C, I²S, CAN, PWM | Datasheet |
| INA219 high-side DC current/power monitor | 0–26 V bus, ±3.2 A with 0.1 Ω shunt, 12-bit, ±0.5 % gain error | 3–5.5 V | I²C (0x40–0x4F) | Datasheet |
| DS18B20 waterproof temperature probe | −55 to +125 °C, ±0.5 °C from −10 to +85 °C, 9–12-bit resolution, unique 64-bit ROM ID | 3.0–5.5 V | 1-Wire (multi-drop) | Datasheet |
| ACS712 hall-effect current sensor (20 A) | ±20 A, 100 mV/A, 80 kHz bandwidth, 1.2 mΩ internal resistance, 2.1 kV isolation | 5 V | Analogue | Datasheet |
| 0.96″ SSD1306 OLED display | 128 × 64 monochrome, 1.3–3.3 V logic, 100 kHz–400 kHz I²C | 3.3–5 V | I²C (0x3C) | Datasheet |
| microSD card 32 GB A1 class | A1 rated, 10 MB/s random write, UHS-I, endurance-grade recommended | 3.3 V | SDIO / SPI | Datasheet |
| 5 V 3 A regulated SMPS adapter | 100–240 VAC in, 5 V ±5 % out, 3 A, short-circuit and over-voltage protection | 5 V | DC barrel / USB | Datasheet |
Consolidated electrical and interface specifications for every active part in the build.
Power Budget & Supply Sizing
Add up the typical active current of every part, then size the supply with at least 50 % headroom so transmit bursts and motor inrush never brown out the controller.
| Load | Supply rail | Typical current (mA) | Notes |
|---|---|---|---|
| ESP32 DevKit V1 (ESP-WROOM-32) | 3.3 V logic / 5 V USB | 160 | Wi-Fi transmit bursts peak near 500 mA — size the regulator accordingly. |
| INA219 high-side DC current/power monitor | 3–5.5 V | 1 | Reports bus voltage, shunt voltage, current and power directly — no maths needed. |
| DS18B20 waterproof temperature probe | 3.0–5.5 V | 1.5 | Dozens can share one GPIO — you address them by ROM code. |
| ACS712 hall-effect current sensor (20 A) | 5 V | 10 | Zero-offset drifts with temperature — re-zero at boot with no load. |
| 0.96″ SSD1306 OLED display | 3.3–5 V | 20 | Static images burn in — invert or scroll the screen periodically. |
| microSD card 32 GB A1 class | 3.3 V | 100 | For 24/7 loggers buy a high-endurance card — normal cards die in months. |
| 5 V 3 A regulated SMPS adapter | 5 V | 3000 | Measure the real output — many "3 A" adapters sag below 4.7 V at 2 A. |
Summed typical draw is 3292.5 mA. With a 1.5× design margin the supply should deliver at least 5000 mA continuously at the stated rail voltage.
Software Requirements & Development Environment
Reference toolchain: Arduino IDE 2.3.x with the ESP32 board package 3.x (or PlatformIO on VS Code). Anything newer normally works; anything older may lack the board definitions used here.
- Install the Arduino IDE 2.3.x (or PlatformIO if you prefer a real editor and dependency locking).
- Add
https://espressif.github.io/arduino-esp32/package_esp32_index.jsonunder File → Preferences → Additional Board Manager URLs, then install esp32 from the Boards Manager. - Set the correct port under Tools → Port. On Linux add yourself to the
dialoutgroup:sudo usermod -aG dialout $USERand log out and back in. - Open the Serial Monitor at 115200 baud — every sketch here logs its state there.
- Keep File → Preferences → Show verbose output during: compilation switched on while you are debugging build errors.
Required libraries
| Library | Why it is needed | Install |
|---|---|---|
| WiFi (ESP32 core) bundled | Station/AP connection management for the ESP32. | Bundled with the ESP32 Arduino core |
| PubSubClient 2.8 | Lightweight MQTT 3.1.1 client for constrained devices. | Library Manager → "PubSubClient" by Nick O'Leary |
| ina219lib | — | — |
| OneWire + DallasTemperature 2.3.x / 3.9.x | Bus enumeration and conversion commands for DS18B20 probes. | Library Manager → "DallasTemperature" (pulls OneWire) |
| Adafruit SSD1306 + GFX 2.5.x | Framebuffer and text/graphics primitives for the OLED. | Library Manager → "Adafruit SSD1306" |
| InfluxDB 2.x + Telegraf 2.7 | Time-series storage with retention policies and downsampling. | docker run -p 8086:8086 influxdb:2.7 |
| ArduinoJson 7.x | Zero-allocation JSON serialisation and parsing. | Library Manager → "ArduinoJson" by Benoit Blanchon |
Block Diagram
The block diagram shows the functional decomposition of the system — what senses, what decides, what acts, and where the data ends up.
Circuit Diagram & Wiring
Every signal line in the build is shown below, followed by a pin-by-pin connection table you can work through with a multimeter in hand.
| Peripheral | Peripheral pin | Controller pin | Signal |
|---|---|---|---|
| Per-cell taps / AFE | mux/SPI | ADC / bus | Per-cell voltage |
| INA219 (current) | SDA/SCL | GPIO 21/22 | Pack current (I²C) |
| Temp sensors | DQ | GPIO 4 | Pack temperature(s) |
| Balancing FETs | ctrl | GPIO | Bleed high cells |
| Protection cutoff | ctrl | GPIO 26 | Charge/discharge disable (real BMS) |
| OLED | SDA/SCL | GPIO 21/22 | Cell voltages/temp |
| Wi-Fi | on-chip | — | Dashboard |
Wire one row at a time and tick it off — most "it does not work" reports trace back to a single swapped pair.
Wiring explanation
- Sense each cell's voltage safely — a dedicated multi-cell monitor IC (analog front end) is the accurate, safe way; ad-hoc dividers on a high-voltage series stack are error-prone and risky.
- Manage the pack to the most extreme cell; a healthy total can hide an out-of-limit cell.
- Place multiple temperature sensors across the pack; a single sensor can miss a hot cell.
- For a protective BMS, include a charge/discharge cutoff (FETs/contactor) that disconnects on fault — a monitor that only warns does not protect.
- Battery work is hazardous — guard against shorts and high current, and test safely.
System Architecture
Read the stack from the bottom up: physical hardware, the firmware that drives it, the transport that moves data off the device, and the software a human actually looks at.
Working Principle
The defining principle of battery management is that a series pack must be managed to its worst cell, not its average, because the cells are in series and share the same current but are not identical. The whole-pack voltage — the only thing a simple gauge shows — is a sum that can look perfectly healthy while one cell inside is over-charged, another over-discharged, or one drifting weak. And in lithium chemistries these single-cell excursions are not merely inefficient; over-charging a cell can drive it into thermal instability and over-discharging can damage it irreversibly. So real battery management measures every cell individually and keeps each within its safe voltage window, treating the most extreme cell as the constraint. This per-cell visibility is the foundation everything else builds on.
From per-cell voltages comes the single most useful health signal: imbalance, the spread between the highest and lowest cell. A fresh, healthy pack's cells track each other closely; as the pack ages, or if a cell is weak, the cells drift apart — one reaches full or empty before the others, limiting the whole pack's usable capacity and, if unmanaged, being repeatedly pushed to its limits and degrading faster. A growing imbalance is therefore an early warning of a weak or ageing cell, and the cue for balancing: bleeding charge from the highest cells (passive balancing) or moving charge between cells (active balancing) so they finish charging together. Balancing keeps the pack matched, restores usable capacity, and protects the weak cell from being over-driven — one of the most important things a battery system does for longevity.
Temperature is the safety-critical channel, because heat is both the enemy of battery life and the signature of danger. Batteries age faster hot, and — crucially — an abnormally hot cell, or a rising rate of temperature, can be the leading edge of a thermal event (in the worst case, thermal runaway). So the monitor watches temperature at multiple points (a single sensor can miss a localised hot cell), warns on both absolute temperature and rate of rise, and treats a thermal excursion as an alarm that must halt charging/discharging. Alongside, current measurement enables state-of-charge estimation (by integrating charge in and out, refined by cell voltages) and the enforcement of safe charge/discharge limits.
The crucial honesty is the difference between a monitor and a protective battery management system. A monitor senses and warns; a BMS that actually keeps a pack safe must be able to act — to reliably cut off charge or discharge the moment any cell goes out of its safe window or the pack overheats, using FETs or a contactor, independent of any network or human. For lithium packs especially, where the consequences of an unmanaged fault include fire, this protective cutoff is not optional, and it is why serious systems use purpose-built or certified BMS hardware. This project builds the sensing and logic — per-cell voltage, temperature, imbalance, balancing, SoC — which is the heart of understanding battery management and a valuable supplementary monitor, but it is emphatic that a monitor which only warns does not protect, and that a real pack's safety depends on protection that disconnects on fault. Built and used with that clarity, it teaches and demonstrates exactly what keeps a battery pack safe and healthy: see every cell, catch the weak one and the hot one early, keep the pack in balance, and — in a true BMS — cut off before damage is done.
The maths behind it
Per-cell safe window + imbalance
Each cell must stay in [V_min, V_max] (chemistry-specific,
e.g. LiFePO4 ~2.5–3.65 V):
fault if any cell < V_min or > V_max
imbalance = max(cell) − min(cell)
balance when imbalance > threshold (during charge)
Manage to the WORST cell, not the pack average.
Thermal warning
Watch absolute temperature and rate of rise:
warn if T_cell > T_warn OR dT/dt > R_warn
fault/cutoff if T_cell > T_max
Rising temperature can precede a thermal event → halt
charge/discharge (in a real BMS).
State of charge (coulomb counting + voltage)
SoC by integrating current, anchored by voltage:
SoC += (I · dt) / capacity (coulomb counting)
correct near full/empty using cell voltages (OCV)
Enforce safe charge/discharge current and SoC limits.
Program Flowchart
The firmware is a single cooperative loop. Nothing blocks for long, so networking, sensing and the user interface all stay responsive.
Assembly Instructions
Build on a breadboard first and only commit to solder once the whole system has run for an hour without a fault.
Set up safe per-cell sensing
Use a dedicated multi-cell monitor IC (or carefully-isolated per-cell taps) to measure each cell's voltage accurately and safely, plus multiple temperature sensors and a current sensor.
Add balancing and (for a BMS) protection
Add balancing circuitry (passive bleed or active) per cell, and — for a protective BMS — a charge/discharge cutoff (FETs/contactor) that disconnects on any fault.
Set up logic and reporting
Compute imbalance, SoC and thermal state; enforce safe limits; log/trend per cell; and report/alarm.
Step-by-Step Implementation Guide
Work through these in order. Each step ends in something you can observe, so a failure is always localised to the step you just finished.
Evaluate cells, balance and enforce safety
Read all cells/temperatures/current, check each cell against its window and the thermal limits, balance during charge when imbalance is high, and (in a real BMS) cut off on fault.
cppbms-logic.ino#define V_MIN 2.5f #define V_MAX 3.65f // LiFePO4 example #define IMBAL_THRESH 0.05f // 50 mV spread triggers balancing #define T_WARN 45.0f #define T_MAX 60.0f #define NCELLS 8 const char* evaluate(float *v, float *t, int nT, bool charging){ float hi=-1, lo=9; int hiIdx=-1; for(int i=0;i<NCELLS;i++){ if (v[i] > V_MAX) return "cell over-voltage"; // manage to worst cell if (v[i] < V_MIN) return "cell under-voltage"; if (v[i]>hi){ hi=v[i]; hiIdx=i; } if (v[i]<lo) lo=v[i]; } for(int i=0;i<nT;i++){ if (t[i] > T_MAX) return "over-temperature (cutoff)"; // thermal fault if (t[i] > T_WARN) return "temperature high (warn)"; } float imbalance = hi - lo; if (charging && imbalance > IMBAL_THRESH) balanceCell(hiIdx); // bleed highest return nullptr; }if (v[i] > V_MAX) return "cell over-voltage"Every cell is checked against its safe window individually — an over- or under-voltage on any single cell is a fault, because the pack must be managed to its worst cell.if (t[i] > T_MAX) return "over-temperature (cutoff)"A cell exceeding its thermal limit is a fault that, in a protective BMS, must cut off charge/discharge — heat is the safety-critical channel.float imbalance = hi - loThe spread between the highest and lowest cell is the imbalance, the early-warning signal for a weak or ageing cell.if (charging && imbalance > IMBAL_THRESH) balanceCell(hiIdx)When charging and the imbalance is high, charge is bled from the highest cell so the cells finish together — balancing that protects the weak cell and restores capacity.Estimate SoC, log and report
Integrate current for state of charge (anchored by cell voltages), log and trend per cell for degradation, and report/alarm — cutting off on fault in a real BMS.
Complete Source Code
The listing below is complete and compiles as written — there are no elided sections. Read the annotations under each block before you upload it.
/* ═══════════════════════════════════════════════════════════════
Battery Management Monitor — ESP32 (sensing + logic; EDUCATIONAL)
Measures per-cell voltage, pack temperature(s) and current; tracks
imbalance and SoC; warns of weak/hot cells; supports balancing.
A real protective BMS must CUT OFF on fault — a monitor that only
warns is a supplement, not a replacement (lithium needs protection).
══════════════════════════════════════════════════════════════════ */
#include <WiFi.h>
#include <PubSubClient.h>
#include <Wire.h>
#include <Adafruit_INA219.h>
#include <OneWire.h>
#include <DallasTemperature.h>
#define NCELLS 8
#define NTEMP 2
#define V_MIN 2.5f
#define V_MAX 3.65f
#define IMBAL_THRESH 0.05f
#define T_WARN 45.0f
#define T_MAX 60.0f
#define PIN_CUTOFF 26 // charge/discharge disable (real BMS)
Adafruit_INA219 ina; OneWire ow(4); DallasTemperature temp(&ow);
WiFiClient net; PubSubClient mqtt(net);
float soc = 50.0f; uint32_t lastMs=0;
float readCell(int i){ /* mux/AFE read of cell i */ return readCellVoltage(i); }
void balanceCell(int i){ setBalanceFet(i, true); } // bleed highest cell
void setup(){
Serial.begin(115200);
pinMode(PIN_CUTOFF, OUTPUT); digitalWrite(PIN_CUTOFF, HIGH); // enabled
Wire.begin(21,22); ina.begin(); temp.begin();
WiFi.begin(WIFI_SSID,WIFI_PASS); mqtt.setServer(MQTT_HOST,1883);
lastMs = millis();
}
void loop(){
if(!mqtt.connected()&&WiFi.status()==WL_CONNECTED) mqtt.connect("bms-1");
mqtt.loop();
uint32_t now=millis(); float dtHr=(now-lastMs)/3600000.0f; lastMs=now;
float v[NCELLS], hi=-1, lo=9; int hiIdx=-1;
for(int i=0;i<NCELLS;i++){ v[i]=readCell(i);
if(v[i]>hi){hi=v[i];hiIdx=i;} if(v[i]<lo)lo=v[i]; }
float t[NTEMP]; temp.requestTemperatures();
for(int i=0;i<NTEMP;i++) t[i]=temp.getTempCByIndex(i);
float current = ina.getCurrent_mA()/1000.0f; // + charge / - discharge
soc += (current * dtHr) / PACK_AH * 100.0f; // coulomb counting
soc = constrain(soc, 0, 100);
// safety evaluation (manage to worst cell)
const char *fault=nullptr;
for(int i=0;i<NCELLS;i++){
if(v[i]>V_MAX){fault="cell over-voltage";} if(v[i]<V_MIN){fault="cell under-voltage";}
}
for(int i=0;i<NTEMP;i++) if(t[i]>T_MAX) fault="over-temperature";
if (fault){
digitalWrite(PIN_CUTOFF, LOW); // PROTECT: disconnect
mqtt.publish("bms/1/fault", fault);
} else {
digitalWrite(PIN_CUTOFF, HIGH);
float imbalance = hi - lo;
bool charging = current > 0.1f;
if (charging && imbalance > IMBAL_THRESH) balanceCell(hiIdx); // balance
if (t[0] > T_WARN) mqtt.publish("bms/1/warn","temperature high");
}
char m[260];
int n = snprintf(m,sizeof m,
"{\"soc\":%.0f,\"imbalance_mV\":%.0f,\"tmax\":%.1f,\"cells\":[",
soc,(hi-lo)*1000, t[0]);
for(int i=0;i<NCELLS;i++) n+=snprintf(m+n,sizeof m-n,"%.3f%s",v[i],i<NCELLS-1?",":"");
snprintf(m+n,sizeof m-n,"]}");
mqtt.publish("bms/1/cells", m);
delay(1000);
}
Configuration & Calibration
Configuration steps
- Set the chemistry-specific per-cell V_min/V_max, thermal limits and imbalance threshold.
- Configure per-cell sensing (AFE/mux), temperature/current sensing, balancing and (for a BMS) the cutoff.
- Set pack capacity for SoC and safe charge/discharge limits.
- Configure logging/trending and alarms.
Calibration procedure
An uncalibrated sensor produces confident, precise, wrong numbers. Do this once per physical unit and record the constants.
Cell voltage accuracy
Verify per-cell voltage readings against a reference meter; accuracy matters near the safe limits.
SoC
Calibrate coulomb counting against known full/empty points; anchor with cell voltages.
Thermal
Verify temperature sensors and set warn/max limits for the chemistry; test rate-of-rise warning.
Network Architecture & Connectivity
Communication protocol
Per-cell voltages, imbalance, temperatures, current and SoC publish continuously; faults publish immediately. Protective cutoff (real BMS) is local and independent of the network.
| Topic / endpoint | Direction | Payload |
|---|---|---|
bms/1/cells | node → dashboard | per-cell V, imbalance, temp, SoC |
bms/1/fault | node → owner | over/under-voltage, over-temp (cutoff) |
bms/1/warn | node → owner | temperature high / imbalance |
Message contract between the device and the broker.
Cloud platform configuration
A dashboard trends every cell for degradation, shows imbalance/temperature/SoC, and alarms on faults; protective cutoff stays local for safety.
Dashboard setup
Per-cell voltage bars and trends, imbalance, temperatures, current and SoC, with fault/warn markers.
Mobile app integration
Immediate fault alerts (over/under-voltage, over-temp) and warnings (imbalance, high temp).
Security considerations
- Keep protective cutoff local and independent of the network.
- Authenticate nodes; secure battery data.
- Alert on monitor silence — a blind pack is a risk.
Testing Procedure & Expected Output
Test from the bottom up. Confirm power, then each sensor in isolation, then the integrated loop — the first failing step tells you exactly where to look.
| Test | What you should see |
|---|---|
| Drive a cell toward V_max/V_min | Fault; (real BMS) cutoff; alarm — managed to worst cell |
| Create an imbalance during charge | Highest cell balanced (bled); imbalance reduces |
| Heat a cell | Temperature warn, then over-temp cutoff/alarm |
| Charge/discharge a known amount | SoC tracks; anchored by voltages near extremes |
| Trend cells over cycles | A weak/ageing cell drifts/sags — visible for action |
| Fault with a monitor-only build | Warns but cannot protect — confirming a real BMS needs cutoff |
Bench-test checklist. If a row fails, stop and fix it before moving on.
Expected output
The dashboard shows each cell's voltage, the imbalance, temperatures, current and SoC, with per-cell trends and alarms for weak/hot cells; a real BMS shows cutoff events.
{
"soc": 68,
"imbalance_mV": 72,
"tmax": 41.0,
"cells": [3.31, 3.33, 3.28, 3.34, 3.32, 3.33, 3.26, 3.33]
}
Cell 7 (3.26 V) is the lowest and the 72 mV imbalance flags a weak cell developing — visible per-cell before the pack total would ever reveal it, and the cue to balance and watch that cell.
Troubleshooting: Common Errors & Fixes
Performance Optimisation
- Read all cells each cycle; per-cell visibility is the whole point.
- Keep protective cutoff local and fast; never depend on the network for safety.
- Balance during charge when imbalance is high.
- Trend per cell for degradation.
- Replace every
delay()with amillis()comparison — blocking delays are the single most common cause of dropped readings. - Sample sensors on a fixed cadence and publish on a slower one; you almost never need to transmit at the sampling rate.
- Move networking into its own FreeRTOS task so a slow DNS lookup cannot stall the control loop.
- Use
uint8_t/uint16_twhere the range allows; on an 8-bit AVR a 32-bit add costs four times as much. - Batch several samples into one MQTT publish. Radio time, not CPU time, dominates the energy budget.
- Set the MQTT keep-alive to a value that matches your reporting interval so the broker does not churn reconnections.
- For battery builds use deep sleep between samples: an ESP32 drops from ~160 mA awake to about 10 µA asleep, which is the difference between days and months of runtime.
Safety Precautions
- A real protective BMS must cut off charge/discharge on fault; a monitor that only warns is a supplement, not a replacement — lithium packs require proper protection.
- Manage the pack to the worst cell; over/under-voltage or over-temperature of any cell is a fault.
- Battery work is hazardous (fire, shorts, high current) — build/test with appropriate safety and use proper multi-cell front ends.
- For real packs, use purpose-built/certified BMS hardware; this is educational sensing/logic.
- Wear eye protection when soldering or cutting, and solder in a ventilated space — rosin flux fumes are a respiratory irritant.
- Power the circuit through a bench supply with a current limit while you are testing. A 300 mA limit turns a wiring mistake into a beep instead of a dead board.
- Disconnect power before changing any wiring. Hot-plugging a sensor onto a live bus is the fastest way to lose a controller.
Maintenance
- Verify per-cell voltage/temperature calibration periodically.
- Act on imbalance/weak-cell trends; balance or replace cells.
- Test the protective cutoff (real BMS) regularly.
- Re-calibrate SoC/capacity as the pack ages.
- Re-check every screw terminal and header after the first week — thermal cycling loosens connections that felt tight on day one.
- Rotate the microSD card annually and keep an image of the working system. Cards used as loggers wear out silently.
- Keep the broker and dashboard containers patched, and rotate device credentials at least once a year.
- Recalibrate at the interval given in the calibration section, and keep the constants in a text file next to the firmware — not only in flash.
- Keep a short logbook of firmware versions and what changed. Six months later you will not remember why that constant is 1.083.
Future Improvements & Upgrades
A working v1 is a platform, not a finish line. These are the upgrades that add the most capability for the least rework.
- Add active balancing for efficiency.
- Add impedance/health estimation per cell.
- Integrate with charger/inverter for coordinated protection.
- Add state-of-health and remaining-life estimation.
- Design a proper PCB. Once the breadboard version has run for a month, moving to a two-layer board removes the intermittent-contact failures that dominate prototype faults.
- Add over-the-air firmware updates so you never have to physically reach a deployed node again.
- Add persistent local storage (microSD or the on-chip flash) so a network outage does not create a hole in your data.
- Move configuration out of the source: a captive-portal setup page or a JSON config file makes the build reusable without a recompile.
- Add a battery and solar option so the unit survives a power cut and can be sited away from a socket.
- Write a small test harness that feeds synthetic sensor values through the decision logic, so you can validate thresholds without physically triggering the event.
Frequently Asked Questions
References & Learning Resources
These are the primary sources worth reading in full. Manufacturer datasheets always outrank forum posts when the two disagree.
- Battery management system (BMS)Reference
- Cell balancingReference
- Lithium-ion safety and thermal runawayReference
- State of charge estimationReference
- INA219 current/power monitor (datasheet)TI