Skip to main content
emobility.academy
B.Tech / BEDashboard & HMIIntermediate About 5 weeksKit only

Fault-Code Viewer and Service Menu

Build a fault-code system that records diagnostic trouble codes with freeze-frame data in SPI flash, and a PIN-protected service menu on the LCD and keypad to view, explain and clear them. The design follows the logic of status flags used in vehicle diagnostics.

Uses: GPIO · SPI

The problem

When an EV develops a fault, a technician needs to know what happened, when and under what conditions, even if the fault has since cleared. Diagnostic trouble codes with freeze-frame snapshots and status flags give that history. Storing them reliably and presenting them clearly on a simple display is a core part of service-friendly firmware.

Objectives

  • Define a set of fault codes with detection conditions, debounce and status flags
  • Store codes, occurrence counts and freeze-frame data in SPI flash with wear-aware, power-safe writes
  • Build a service menu on the 16×2 LCD and keypad to browse, explain and clear codes
  • Protect clearing and settings with a service PIN

What you need

From the EV Embedded Development Kit

  • STM32F103C8T6 microcontroller unit (main controller)
  • SPI flash memory
  • 16×2 LCD
  • 4×3 matrix keypad
  • Analog potentiometer input
  • BMP280 pressure and temperature sensor
  • RTC with CR2032 backup cell
  • RGB LEDs and a buzzer

Software

  • STM32CubeIDE with HAL drivers
  • Embedded C
  • Python (optional, for decoding a flash dump over UART)

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

Diagnostic trouble code
An identifier for a specific fault, such as sensor out of range, stored when the fault is confirmed.
Status flags
Bits that track a code's history, such as failed now, failed this cycle, pending and confirmed, similar in spirit to the status byte used in UDS diagnostics.
Freeze frame
A snapshot of key values, such as time, speed, temperature and voltage, taken at the moment a fault is confirmed.
Debounce counter
A counter that rises on failed checks and falls on passed checks; a fault is confirmed only when it crosses a threshold, which filters glitches.

Step-by-step guide

  1. 1

    Define the fault list

    Choose eight to twelve faults the kit can actually detect: potentiometer reading out of range (simulating a throttle fault), BMP280 not responding, temperature out of range, RTC oscillator stopped, flash CRC error, keypad stuck key and supply voltage low if measurable. For each, write the detection condition, debounce thresholds, the freeze-frame values and a short text explanation for the menu.

    Checkpoint: A fault table with code, condition, thresholds, freeze-frame and text for each fault.

  2. 2

    Board setup

    Enable the DIP switches for SPI flash, LCD, keypad, potentiometer, BMP280, RTC, LEDs and buzzer. In CubeMX set SYSCLK to 72 MHz, configure SPI with separate chip-selects for the BMP280 and flash, I2C for the RTC, ADC for the potentiometer, keypad and LCD GPIOs and a 10 ms tick. Check the BMP280 chip ID and the flash JEDEC ID at start-up.

    Checkpoint: Both device IDs read correctly and are shown on the LCD at start-up.

  3. 3

    Fault monitors

    Write a monitor function for each fault that runs at a fixed rate and reports pass or fail. Feed results into debounce counters. Update status flags on each result: failed now, failed since last clear, pending after the first failure and confirmed when the counter crosses its threshold. Count occurrences. Run each monitor at a rate suited to its signal, and make sure one failed sensor cannot stop the other monitors from running.

    Checkpoint: Turning the potentiometer to an end set as out of range creates a pending, then confirmed, code shown over UART.

  4. 4

    Freeze-frame capture

    When a code is confirmed, capture the freeze frame: RTC date and time, potentiometer value, temperature, pressure and the code's occurrence count. Keep only the first occurrence's frame and the latest, so the record size is fixed. Take the snapshot in the same task that confirms the fault, so the values belong to the same moment. Keep freeze-frame fields in a fixed-size structure so storage stays simple and predictable, and test that a fault raised just after start-up still gets a valid timestamp.

    Checkpoint: A confirmed code has a freeze frame with the correct time and values.

  5. 5

    Flash storage

    Store the fault table in SPI flash as fixed-size records with a sequence number and CRC. Write records in a log across two sectors and erase the older sector only when the newer is full, which spreads wear and keeps a valid copy if power fails mid-write. At start-up, rebuild the table from the latest valid records. Measure how many writes a typical day causes to estimate flash life.

    Checkpoint: Codes survive a power cycle, and cutting power during a write still restores a valid table.

  6. 6

    Service menu

    Build a menu: a code list showing code and status, a detail view with the text explanation, a freeze-frame view across several screens and a clear action. Navigate with keypad keys such as 2 and 8 for up and down, '#' to enter and '*' to go back. Show the number of confirmed codes on the main screen with a warning LED.

    Checkpoint: A technician can browse every stored code, read its explanation and freeze frame and return to the main screen.

  7. 7

    Service PIN and clearing

    Protect clear and settings actions with a service PIN entered on the keypad, stored as a salted hash in flash. Clearing a code resets its flags and counters but writes a log entry recording when it was cleared. Add a 'clear all' with a confirmation step. Lock the service menu again after a period of inactivity so it is not left open, and count failed service PIN attempts with a delay after several failures, as for any PIN.

    Checkpoint: Clearing without the PIN is refused, and clearing with it removes the code and records the action.

  8. 8

    UART dump tool

    Add a UART command to dump all records. Write a Python script that decodes the dump into a readable table and CSV, with code texts. This acts as a simple service tool on a laptop. Add a version field to the record format so that the tool can still read logs written by older firmware. Use the same code-text table in firmware and tool, generated from one source, so a code always means the same fault in both.

    Checkpoint: The Python tool prints every stored code with its freeze frame.

  9. 9

    Test and report

    Trigger every fault deliberately, for example by disabling the BMP280 DIP switch or setting thresholds in a test build. Check confirmation, freeze frames, persistence and clearing. Document the fault table, the storage format and test results. Include the flash record format with byte offsets, and describe how a technician would use the menu to diagnose an intermittent fault from its occurrence count and freeze frames.

    Checkpoint: A test table covering every fault and the storage power-cut test is in the report.

Testing & validation

  • Every defined fault triggered and confirmed with the correct freeze frame
  • Short glitches filtered by the debounce counters
  • Codes and clear history persist across power cycles and power cuts during writes
  • Clearing blocked without the service PIN

Expected results

A fault-management module with debounced monitors, status flags, freeze frames in power-safe SPI flash storage, and a PIN-protected service menu plus a laptop dump tool.

Take it further

  • Read and clear codes over CAN with a simplified diagnostic request and response
  • Add ageing of confirmed codes that clear after several good cycles
  • Show fault statistics such as occurrences per day
  • Add codes from the BMS unit received over the link between controllers

Viva and review questions

What is the difference between pending and confirmed?

Pending means the fault has been seen at least once; confirmed means the debounce counter has crossed its threshold, so the fault is real and stored with a freeze frame.

Why log records across two sectors?

Flash must be erased in sectors; alternating sectors means a valid copy always exists if power fails during an erase or write, and it spreads wear.

Why protect clearing with a PIN?

Clearing removes evidence a technician may need, so only authorised service staff should be able to do it.

Related EV projects

All 100 EV projects