The problem
Understanding how a battery is used, how deep it is cycled, at what loads and at what temperatures, is the basis for range estimates, warranty decisions and BMS tuning. Engineers need clean, timestamped usage data and the skills to turn it into conclusions.
Objectives
- Log cell voltage, cell temperature and load configuration with RTC timestamps.
- Estimate discharge current from measured voltage and the known load resistance, and state its error.
- Export sessions as CSV and analyse them with pandas and matplotlib.
- Compare discharge behaviour under one, two and three loads.
What you need
From the EV Embedded Development Kit
- 1S battery with connector and T-sense input
- Load section: three switchable resistive loads with LEDs
- Charge/discharge control relay
- Battery charging unit with its own switch
- RTC with CR2032 backup cell
- Serial UART interface
- Smart BMS unit STM32F103C8T6
- BMS power switch, OLED and six navigation buttons
Software
- STM32CubeIDE with HAL drivers
- Embedded C
- Python 3 with pyserial, pandas, NumPy and matplotlib
- Jupyter notebook for the analysis
Extra parts
- Digital multimeter (lab equipment) — Needed once to measure each load resistance with the cell disconnected and to calibrate the ADC voltage reading, which the current estimate depends on.
This project uses the kit's lithium cell. Respect polarity, never short the terminals, and don't leave the cell charging unattended.
Key concepts
- Current from Ohm's law
- With a known load resistance R and measured terminal voltage V, current is roughly V/R. Errors come from resistance tolerance, wiring and relay contact resistance, and the resistors warming up.
- Discharge curve
- Terminal voltage plotted against time or charge removed. Its shape shows the plateau and the steep fall near empty that a BMS must protect against.
- Energy and charge throughput
- Charge is the integral of current over time (Ah) and energy is the integral of voltage times current (Wh). Both are computed by summing samples.
- Timestamping with an RTC
- A battery-backed clock gives each sample real date and time, so sessions recorded on different days can be compared.
Step-by-step guide
- 1
Plan the logging and safety rules
Decide signals (cell voltage, cell temperature, which loads are on, relay state), a 1 s sample rate and the session format. Set protection limits before any test: stop discharge at a safe cut-off voltage from the cell's datasheet, stop on high temperature, and never leave the cell charging or discharging unattended. Check polarity on the battery connector and never short the terminals.
Checkpoint: A signal table and a written safety sheet with cut-off voltage and temperature limits, signed off by the team.
- 2
Measure the load resistances
With the battery removed and power off, measure each of the three load resistors with a multimeter, including the path through the load switch if possible. Record values and tolerances. Also note the multimeter's accuracy. These numbers drive every current estimate, so measure carefully and repeat each reading three times. Measure again after the loads have been running for ten minutes and cooled, because resistor values drift with temperature. Use the mean value in firmware and the spread in the uncertainty analysis later.
Checkpoint: A table of resistance values with mean and spread for each load.
- 3
Configure the BMS unit
This project runs on the Smart BMS unit, which owns the battery, loads and relay (confirm the pin mapping from the kit schematic). In CubeMX enable ADC1 channels for cell voltage and the T-sense input, GPIO outputs for the load switches and the charge/discharge relay, the RTC, and a USART for the PC. Set the DIP switches for the battery and load section. Use the OLED to show live values and the buttons to select loads.
Checkpoint: The OLED shows live cell voltage and temperature, and the buttons switch each load and its LED.
- 4
Calibrate voltage and temperature
Convert ADC counts using the divider ratio and the 3.3 V reference, then compare with the multimeter at three cell voltages and fit a gain and offset. Convert the T-sense reading with the sensor curve from the kit documentation and sanity-check it at room temperature. Average 16 samples per reading to reduce noise. Store calibration constants in code. Repeat the check after a day to see whether the calibration holds.
Checkpoint: Voltage error after calibration is within a stated tolerance across the range, and the temperature reading is plausible.
- 5
Implement the logger
Every second read the RTC, the calibrated voltage and temperature, and the load bitmask, then send a CSV line over UART: date-time, mV, 0.1 °C, loads, relay state. Compute estimated current on the board as V divided by the parallel resistance of the active loads, in integer mA, and include it. Add a session header line with calibration constants and resistances so files are self-describing.
Checkpoint: A terminal shows one line per second with timestamps that match the PC clock within the RTC's drift.
- 6
Add protection
Open the discharge relay and switch off all loads if voltage falls below the cut-off or temperature exceeds the limit, and log the reason. Show the event on the OLED. Test each limit safely by temporarily raising the cut-off threshold in software rather than over-discharging the cell. Make protection independent of the logging code, so a UART problem can never delay it. Once tripped, require a button press on the BMS unit to re-enable discharge, and show the trip reason on the OLED until then.
Checkpoint: Raising the software cut-off above the present voltage stops discharge immediately and logs the event.
- 7
Record discharge sessions
With the cell fully charged (charging supervised), record discharge sessions at one, two and three loads, stopping at the cut-off. Rest the cell between sessions and record ambient temperature. Keep each session in its own file named by date and load count. Record each session in the same ambient conditions, starting from the same charge state, with a rest of at least 30 minutes after charging so the cell voltage settles. Never leave the setup running unattended; one team member stays with the bench and checks temperature regularly.
Checkpoint: Three complete discharge files, each ending with a protection stop at the cut-off voltage.
- 8
Analyse in Python
Load sessions with pandas, parse timestamps and compute charge (Ah) and energy (Wh) by summing current and power over time. Plot voltage against charge removed for all three loads on one chart, and temperature against time. Compare delivered capacity and average voltage between loads and discuss why heavier loads give lower voltage and less delivered energy. Resample all sessions to a common time base before comparing them.
Checkpoint: A notebook producing overlay plots and a table of Ah, Wh and mean voltage per session.
- 9
Quantify the current estimate error
Propagate the uncertainties: resistance tolerance, voltage calibration error and resistor heating. Estimate the combined uncertainty on current and on delivered capacity. If the team can borrow a current meter, compare one session against it; otherwise state the estimated bounds clearly. Write the error propagation as a formula: for I = V/R, the relative uncertainty in I is roughly the root-sum-square of the relative uncertainties in V and R. Apply it per sample and carry it through the capacity sum, and plot the resulting error band on the discharge curves.
Checkpoint: An uncertainty table for current and capacity with the assumptions listed.
- 10
Report the findings
Write up the setup, calibration, safety rules, logged sessions, analysis and uncertainty. Include the notebook and raw CSV files so the results can be checked. Include the safety sheet, the resistance and calibration tables, the overlay plots and the uncertainty table. Discuss what the data says about how load affects usable capacity, and what a production BMS would measure directly, such as current through a shunt with a dedicated amplifier, to remove the main uncertainty in this method.
Checkpoint: A report whose figures can be regenerated from the archived CSV files with one notebook run.
Testing & validation
- Calibrated voltage agrees with the multimeter within the stated tolerance at three points.
- Protection opens the relay at the configured limits.
- Charge and energy sums are checked against a hand calculation on a short segment.
- Timestamps are continuous and monotonic in every session.
Expected results
A timestamped battery usage dataset across three load levels, with discharge curves, delivered capacity and energy, and an honest uncertainty estimate for the voltage-derived current.
Take it further
- Fit an OCV–SOC curve from rest periods between load steps.
- Add an INA219 current monitor and compare measured with estimated current.
- Log to SPI flash for unattended (but supervised) long sessions.
Viva and review questions
How do you get current without a current sensor?
From the measured terminal voltage divided by the known resistance of the active loads, measured beforehand with a multimeter.
What are the main error sources in that estimate?
Resistor tolerance, voltage calibration error, contact and wiring resistance, and the resistors' value changing as they heat up.
Why does delivered capacity fall with heavier load?
Higher current gives a larger drop across the cell's internal resistance, so the cut-off voltage is reached earlier.
Why use the RTC rather than a counter?
It gives real dates and times, so sessions across days and resets can be compared and aligned.