The problem
After a crash or a sudden fault, engineers need to know what the vehicle was doing in the seconds before and after. Continuous high-rate logging fills memory quickly, so event data recorders keep a short rolling window and save it only when a trigger occurs.
Objectives
- Sample the MPU6050 at 100 Hz and the BMP280 and battery signals at a lower rate into a RAM ring buffer.
- Detect crash-like events (acceleration magnitude above a threshold) and fault events (over-temperature, under-voltage).
- Save pre-trigger and post-trigger data with metadata to SPI flash, and lock events against overwrite.
- Read events out over UART and plot them on a PC.
What you need
From the EV Embedded Development Kit
- MPU6050 accelerometer and gyroscope
- BMP280 pressure and temperature sensor
- SPI flash memory
- RTC with CR2032 backup cell
- 1S battery with connector and T-sense input
- RGB LEDs
- Buzzer
- Serial UART interface
- STM32F103C8T6 main controller
Software
- STM32CubeIDE with HAL drivers
- Embedded C
- Python 3 with pyserial, NumPy and matplotlib
Extra parts
None. Everything this project needs is 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
- Pre-trigger buffer
- A circular buffer in RAM that always holds the most recent few seconds of data. When a trigger fires, the buffer already contains what happened before it.
- Trigger logic
- Rules that decide an event happened, such as acceleration magnitude above a set g level for a minimum duration, which rejects single-sample spikes.
- Event locking
- Saved events are marked so the normal logging process cannot overwrite them until a technician clears them.
- RAM budget
- The STM32F103C8T6 has 20 KB RAM, so the buffer size, sample format and rate must be chosen together; 2 s of six 16-bit IMU channels at 100 Hz is 2.4 KB.
Step-by-step guide
- 1
Define triggers and recording windows
Decide the event types and their rules: crash-like (acceleration magnitude above, say, 2 g for at least 20 ms; on a desk you will test with a firm tap or drop onto a cushion), tilt-over (angle above a threshold for 1 s), over-temperature and cell under-voltage. Choose a 2 s pre-trigger and 1 s post-trigger window. Calculate RAM use for each channel and rate, and keep a margin for the stack and drivers.
Checkpoint: A trigger table and a RAM budget showing the buffer fits within 20 KB with margin.
- 2
Configure peripherals
Set DIP switches to connect the MPU6050, BMP280, SPI flash, RTC, RGB LEDs and buzzer blocks. In CubeMX enable I2C at 400 kHz for the MPU6050 (and the BMP280 if it is on I2C on your board), SPI for the flash, the RTC, ADC for the battery-related inputs available to this MCU, a USART and a timer interrupt at 100 Hz to pace sampling.
Checkpoint: WHO_AM_I of the MPU6050 reads 0x68, the BMP280 chip ID reads 0x58, and the flash JEDEC ID prints correctly.
- 3
Write the sensor drivers
Configure the MPU6050 for ±8 g and ±500 °/s with the digital low-pass filter set, then read all six axes in one 14-byte burst from 0x3B. Read the BMP280 calibration constants and apply the datasheet compensation in integer form for pressure and temperature. Read cell voltage and temperature through the ADC (or from the BMS unit if that is where they are wired). Convert to engineering units in fixed-point.
Checkpoint: A UART stream of all channels at the right rate, with resting acceleration magnitude close to 1 g.
- 4
Build the ring buffer
Store IMU samples as 16-bit raw values in a ring buffer sized for the pre-trigger window. Keep a separate, slower ring buffer for pressure, temperature and voltage at 10 Hz. Use a write index that wraps with a mask (make the length a power of two) so the sampling interrupt stays short. Disable the interrupt only for the few instructions that copy the write index when the main loop reads the buffer, so the copy is consistent without losing samples.
Checkpoint: Sampling ISR time, measured with a GPIO toggle, stays well below the 10 ms period.
- 5
Implement trigger detection
Compute squared acceleration magnitude in integer maths (avoid square roots) and compare it with the squared threshold. Require the condition for a minimum number of samples. Check the slower channels for fault thresholds. On a trigger, record the trigger index, type and RTC time, then keep sampling for the post-trigger window before freezing the buffers. Ignore new triggers until the event is saved.
Checkpoint: Taps above the threshold trigger reliably, ordinary handling of the board does not, and each trigger prints its type and time.
- 6
Save events to SPI flash
Reserve a flash region for events with a directory sector listing slot number, type, time, sample count and lock flag. Copy the frozen buffers in chronological order (unwrap the ring) into page programs of 256 bytes, then write the directory entry last so a power cut mid-save leaves no half-valid event. Add a CRC over the event payload. Flash an RGB LED and sound a short buzzer pattern when an event is stored.
Checkpoint: After a trigger the event appears in the directory, its CRC verifies and it survives a reset.
- 7
Lock and manage events
Mark saved events as locked. When all slots are full, decide a policy (for example keep crash events, overwrite the oldest fault event) and document it. Add UART commands to list events, dump one, and clear all after a confirmation, as a service tool would. Store a running event counter in the directory so the order of events is known even if the RTC was wrong. Require a confirmation code on the clear command so it cannot happen by accident.
Checkpoint: Filling all slots behaves as the documented policy says and locked crash events are never overwritten.
- 8
Write the PC viewer
Write a Python script that downloads an event, checks the CRC, converts raw values to g, °/s, hPa, °C and volts, and plots every channel against time with the trigger at t = 0. Mark the pre-trigger and post-trigger regions. Add a summary line per event with type, RTC time, peak acceleration and the fault values at the trigger. Save every plot as an image alongside the decoded CSV, so events can be included directly in the report and compared across tests.
Checkpoint: A plot of a tap event shows the spike at t = 0 with clean data before and after.
- 9
Test and characterise
Run repeated trigger tests at several intensities and record detection rate and false triggers during normal handling. Measure the time from trigger to event saved. Cut power during a save to confirm the directory-last approach works. Check that the sampling period stays constant while flash is being written. Use the same tap height or drop height for repeated trials so results are comparable, and protect the board with a cushion; do not drop the kit with the battery fitted.
Checkpoint: A test table with detection and false-trigger counts, save time and power-cut results.
- 10
Document the recorder
Write up the trigger rules, memory layout, save sequence, slot policy and test results. Discuss how a production event data recorder would add tamper protection and standardised data elements. Explain how the trigger thresholds were chosen from the test data, and list the known false-trigger cases. Add the event record format so that a service tool written by another team could decode it, and describe the clearing procedure a technician would follow.
Checkpoint: The report includes the flash map, a timing diagram of a trigger and plots of real events.
Testing & validation
- Pre-trigger data in saved events covers the full configured window.
- False-trigger count during five minutes of normal handling is recorded and below the target set in step 1.
- Saved events survive resets and pass CRC checks.
- Sampling jitter stays within a stated bound during flash writes.
Expected results
A working event data recorder that captures and preserves the seconds around crash-like and fault events, with a PC viewer that plots each event for analysis.
Take it further
- Add a CAN frame that announces an event to other nodes (with a second kit or a USB-CAN adapter).
- Estimate the change in velocity during the event by integrating acceleration.
- Sign event records with a truncated MAC to show tamper evidence.
Viva and review questions
Why do you need a pre-trigger buffer?
The cause of an event happens before the trigger. A rolling buffer keeps the most recent seconds so they can be saved when the trigger fires.
Why compare squared magnitude?
It avoids a square root, which is slow without an FPU, and comparing against the squared threshold gives the same decision.
How do you make a save power-safe?
The payload and CRC are written first and the directory entry last, so an interrupted save never appears as a valid event.
How did you size the buffer?
From channels, sample width, rate and window length, checked against the 20 KB RAM with room for the stack and drivers.