The problem
Connected EVs can send raw battery data to the cloud or process it on board. Raw upload is costly and slow; on-board processing is limited by small MCUs. Choosing what runs where is usually ad hoc, and there is little measured evidence of the trade-offs on realistic embedded hardware.
Objectives
- Define a battery analytics pipeline: filtering, feature extraction, SOC estimation, anomaly detection and SOH trend.
- Implement each stage on the STM32 in fixed-point and on the PC in floating point.
- Run the pipeline at several split points, sending intermediate data over CAN and UART.
- Measure MCU cycles and memory, link bytes, end-to-end latency and analytic accuracy.
- Formulate and evaluate a partitioning rule or optimisation.
What you need
From the EV Embedded Development Kit
- Smart BMS unit STM32F103C8T6
- STM32F103C8T6 main controller
- 1S battery with connector and T-sense input
- Load section: three switchable resistive loads with LEDs
- CAN bus interface with screw terminal
- Serial UART interface
- SPI flash memory
Software
- STM32CubeIDE with HAL drivers
- Embedded C with CMSIS fixed-point routines
- FreeRTOS (optional, for timing isolation)
- Python 3 with NumPy, SciPy, pandas, scikit-learn and python-can
- Optional network emulation on the PC to add delay and loss
Extra parts
- USB-CAN adapter — The board has one CAN node; the adapter connects the PC cloud emulator to the vehicle-side CAN bus.
- INA219 I2C current and power monitor module — Current is needed for SOC and SOH analytics and is not listed on the kit.
This project uses the kit's lithium cell. Respect polarity, never short the terminals, and don't leave the cell charging unattended.
Key concepts
- Split point
- The stage in a processing pipeline after which data leaves the device. An early split sends raw data; a late split sends only results.
- Data reduction ratio
- How much smaller the output of a stage is than its input. Stages with high reduction and low compute cost are strong edge candidates.
- Cycle budget
- The CPU time available on the 72 MHz Cortex-M3 after control tasks; analytics must fit within it without disturbing protection functions.
- Replay testing
- Feeding recorded or public-dataset samples into the MCU over UART so every split point is evaluated on identical input.
- Pareto front
- The set of configurations where no metric can be improved without worsening another, used to present trade-offs honestly.
Step-by-step guide
- 1
Define the pipeline and metrics
Write the pipeline: anti-alias filter and downsample, feature extraction (mean, variance, voltage drop on load steps, temperature rise), SOC estimation (coulomb counting with OCV correction or an EKF), anomaly detection (EWMA residual test) and SOH trend (resistance estimate over time). For each stage define its output and an accuracy metric. Define system metrics: bytes per hour, latency to an anomaly alert, MCU cycles, RAM and flash.
Checkpoint: A pipeline diagram with input and output sizes at each stage and a metrics table.
- 2
Prepare datasets and replay
Record kit data under switchable loads with the INA219 providing current, following lithium safety rules. Prepare public datasets such as NASA PCoE and CALCE for longer and ageing scenarios. Write a UART replay protocol that pushes samples into the STM32 at real-time or accelerated rate, so the firmware cannot tell replayed data from live data. Document each dataset's licence and preprocessing.
Checkpoint: The STM32 processes a replayed file and produces the same feature values as a live run of the same test.
- 3
Implement stages on the STM32
Write each stage as a function with a fixed interface, in Q15 or Q31 fixed-point because the Cortex-M3 has no FPU. Measure cycles per call with the DWT cycle counter and static memory from the map file. Make the split point a runtime setting, so the firmware sends the output of stage k over the link and skips later stages.
Checkpoint: A table of cycles, RAM and flash per stage, measured on the board.
- 4
Implement the cloud side
Write the same stages in Python floating point as the cloud emulator. For each split point, the cloud receives stage k output and runs the rest. Ensure the fixed-point and floating-point versions agree within stated tolerances, and log any differences. Use the cloud side's results with the full raw data as the accuracy reference for every split point. Log processing time per stage on the PC as well, so the cost model can include cloud compute. Keep the interface for each stage identical in C and Python so the split point can move freely.
Checkpoint: End-to-end outputs at every split point agree with the all-cloud reference within tolerance.
- 5
Build the transport
Send split-point data over CAN (packed signals, 8-byte frames) through the USB-CAN adapter, and over UART for higher-volume early splits. Add sequence numbers and timestamps. Optionally emulate a cellular uplink on the PC by adding delay, jitter and loss before the cloud stage. Choose CAN signal layouts for features and results that fit 8-byte frames. Record the bus load percentage for each split point and check it stays within a sensible budget alongside other vehicle traffic, which the USB-CAN adapter can add as background load.
Checkpoint: Bus load on CAN and bytes on UART measured for each split point.
- 6
Run the split-point sweep
For each split point and dataset, run the full replay and record bytes sent, CAN bus load, MCU cycle usage, anomaly-alert latency, SOC error and SOH estimate error. Repeat with emulated uplink conditions. Keep the BMS protection task running throughout to check analytics do not delay it. Automate the sweep with a Python script so every configuration is run the same way and nothing is skipped. Repeat runs to measure variance.
Checkpoint: A complete results matrix with repeated runs and no missed protection deadlines.
- 7
Formulate the partitioning problem
Model each configuration's cost as a weighted sum or constrained optimisation: minimise bytes and latency subject to MCU cycle, RAM and accuracy constraints. Solve it by enumeration (the number of split points is small) or by integer programming if you allow per-stage placement. Compare the model's predictions with measured results. State all assumptions, such as cost per byte and acceptable latency, and test how sensitive the optimal choice is to them.
Checkpoint: The model ranks configurations in the same order as the measurements, or the discrepancies are explained.
- 8
Test adaptive partitioning
Implement a simple runtime policy that moves the split point when conditions change, for example sending only anomaly flags when the uplink is poor and features when it is good. Evaluate it against the best fixed split. Define the switching rule precisely, with hysteresis so the split point does not oscillate when conditions are near a threshold. Measure the cost of switching, such as lost samples or extra frames, and include it in the comparison with fixed splits under a scripted sequence of uplink conditions.
Checkpoint: The adaptive policy's metrics compared with every fixed split on the same scenario.
- 9
Analyse and present trade-offs
Plot the Pareto front of bytes against accuracy and latency against MCU load. Identify stages with high data reduction and low compute that should always run on the edge. Discuss sensitivity to dataset and uplink conditions. Present the measured per-stage cost table, since it is useful to others even without the optimisation. Show how the Pareto front shifts with uplink quality and dataset, and test whether the recommendations hold on both kit data and public data.
Checkpoint: Pareto plots and a written recommendation tied to measured numbers.
- 10
Write up and release
Document the testbed, pipeline, replay method, measurements, model and limitations (one small cell, emulated cloud, emulated uplink). Release firmware, scripts and kit data where possible. Organise the write-up around the research questions, and describe the replay method in enough detail for another group to use it on their own hardware. Discuss how the results might change on a more capable MCU or a real cellular link, and what experiments would test that.
Checkpoint: A thesis chapter or paper draft with every figure reproducible from released code.
Testing & validation
- Fixed-point and floating-point stages agree within stated tolerances.
- Replay and live runs produce matching outputs for the same test.
- BMS protection timing unaffected at every split point (measured with GPIO and DWT).
- Partitioning model predictions checked against measurements.
Expected results
A measured map of edge-versus-cloud trade-offs for battery analytics on a Cortex-M3, a validated partitioning model and an adaptive split policy evaluated against fixed splits.
Research angle for your PhD
Research questions: which battery analytics stages should run on a resource-constrained BMS, how does the optimal split shift with uplink quality and accuracy requirements, and can a lightweight runtime policy approach the best fixed split? Metrics: bytes per hour, alert latency, MCU cycles and memory, SOC and SOH error. Baselines: all-cloud and maximum-edge configurations. Datasets: kit data, NASA PCoE and CALCE replayed through the MCU. Publishable contributions include the measured cost table per stage, the partitioning formulation validated on hardware, and released replay tooling.
Take it further
- Add an ESP32 gateway to replace the emulated uplink with real Wi-Fi.
- Include energy consumption of the MCU and radio in the cost model.
- Extend to fleet level with several replayed vehicles sharing one cloud service.
Viva and review questions
Why use replay rather than only live data?
Replay gives identical input for every split point and allows long or ageing datasets that cannot be produced live on a 1S cell.
Why must analytics not disturb protection?
Protection is safety-critical; analytics run at lower priority and timing was measured to show protection deadlines were still met.
Which stages tend to belong on the edge?
Those that reduce data a lot for little compute, such as filtering, downsampling and feature extraction, which the measurements quantify.
How did you handle the lack of an FPU?
Edge stages use Q15 or Q31 fixed-point, checked against the Python floating-point versions.