The problem
When several faults happen at once, a cluster that flashes everything overwhelms the rider, and one that shows the wrong warning first can hide a critical problem. Production clusters use priority rules, colour conventions such as red for danger and amber for caution in line with ISO 2575 symbol practice, acknowledgement and minimum display times. This arbitration logic is a classic embedded design problem.
Objectives
- Define a warning catalogue with priorities, colours, sound patterns and acknowledgement rules
- Implement an arbitration engine that selects what to show when several warnings are active
- Drive RGB tell-tales with PWM, buzzer patterns and LCD messages from the engine
- Test the engine with scripted multi-fault scenarios
What you need
From the EV Embedded Development Kit
- STM32F103C8T6 microcontroller unit (main controller)
- RGB LEDs and a buzzer
- 16×2 LCD
- 4×3 matrix keypad
- Analog potentiometer input
- 3-axis accelerometer and gyroscope (MPU6050, I2C)
- Serial UART interface
Software
- STM32CubeIDE with HAL drivers
- Embedded C
- Python (optional, for scripted fault scenarios over UART)
Extra parts
None. Everything this project needs is on the kit.
Key concepts
- Tell-tale
- An indicator that shows a function or fault state, such as a battery or temperature warning, with standard colours: red for danger, amber for caution and green or blue for information.
- Priority arbitration
- Choosing which active warning to show by priority, then by time of activation, with critical warnings able to interrupt lower ones.
- Acknowledgement and re-arm
- Some warnings can be acknowledged to silence the buzzer while the tell-tale stays lit; if the condition clears and returns, the warning re-arms.
- PWM colour and blinking
- Timer PWM channels set the brightness of each LED colour; a slower timer pattern gives steady, slow-blink or fast-blink states.
Step-by-step guide
- 1
Warning catalogue
Define eight to ten warnings, such as battery critically low, battery low, high temperature, tilt or fall detected, over-speed, sensor fault and seat-belt or stand reminder as an information item. For each, set priority, colour, blink pattern, buzzer pattern, LCD text, whether it can be acknowledged and a minimum display time. Base the colour scheme on common tell-tale practice and explain it.
Checkpoint: A catalogue table reviewed by the guide, with a short reason for each priority.
- 2
Board setup
Enable the DIP switches for the RGB LEDs, buzzer, LCD, keypad, potentiometer, MPU6050 and UART. In CubeMX set SYSCLK to 72 MHz, put the RGB LED pins on timer PWM channels if the schematic allows, otherwise drive them as GPIO with software blinking, configure the buzzer on a timer for tones, the ADC for the potentiometer, I2C for the MPU6050 and a 10 ms tick.
Checkpoint: Each LED colour can be dimmed and the buzzer plays a test tone.
- 3
Output drivers
Write an LED driver that takes a colour and a pattern (off, steady, slow blink, fast blink) and a buzzer driver that plays patterns such as a single chime, a repeated beep and a continuous tone. Patterns run from the 10 ms tick so they never block. Write an LCD message routine for a two-line warning. Keep drivers free of warning logic so they can be reused.
Checkpoint: Every pattern runs at the right rate while the main loop stays responsive.
- 4
Warning sources
Create the conditions that raise warnings: the potentiometer as emulated speed for over-speed and as an emulated battery level for low-battery warnings, the MPU6050 for tilt beyond a set angle, and keypad keys or UART commands to inject other faults. Add debouncing and hysteresis so warnings do not flicker at thresholds. Keep each source in its own function that only reports whether its condition is present, so sources can be added or tested without touching the arbitration engine.
Checkpoint: Each warning can be raised and cleared, with no chatter near thresholds.
- 5
Arbitration engine
Keep a table of warning states: inactive, active, acknowledged. Every 10 ms, select the active warning with the highest priority, breaking ties by activation time. A higher-priority warning interrupts a lower one at once, but a lower one waits for the current one's minimum display time. Rotate between equally high warnings. Keep all active tell-tales lit even when another message is shown on the LCD.
Checkpoint: With three warnings active, the LCD shows the highest priority and all three tell-tale states are correct.
- 6
Acknowledgement
Map a keypad key to acknowledge. Acknowledging silences the buzzer and moves the LCD to the next warning, but the tell-tale stays lit while the condition remains. Critical warnings cannot be acknowledged. If an acknowledged warning clears and returns, it re-arms with sound. Log each acknowledgement with the warning ID and time. Decide and document what happens if the rider presses acknowledge with no warning active, and if a new warning arrives at the same moment as the key press.
Checkpoint: Acknowledged warnings stay silent until they clear and come back, and critical warnings refuse acknowledgement.
- 7
Scripted scenario tests
Write test scenarios as timed sequences of fault injections sent over UART from Python or stored in firmware. Include a critical warning arriving during a low one, two equal warnings, rapid on-off flicker and acknowledgement during rotation. Log the engine's decisions with timestamps over UART. Re-run the full set after every change to the engine, so a fix for one scenario does not break another. Keep scenarios as files so they can be shared and reviewed.
Checkpoint: The log for each scenario matches the expected decision sequence.
- 8
Timing and resource check
Measure the time from a condition being raised to the LCD, LED and buzzer responding, and the CPU time of the engine per tick. Check RAM use of the state table. Show that the worst case meets a target response time you set at the start. Also check behaviour with every warning active at once, which is the worst case for both CPU time and the clarity of what the rider sees, and note any warnings that become hard to distinguish.
Checkpoint: A timing table shows the worst-case response within the target.
- 9
Report
Document the catalogue, the arbitration rules, the drivers, the scenarios and the results. Include a state diagram of a warning's life cycle and photographs or short videos of key scenarios. Explain how each priority was chosen and what you would change after testing, and describe how the engine could be reused in a real cluster receiving its warning conditions from other control units over CAN.
Checkpoint: A complete report with the catalogue, state diagram and scenario results.
Testing & validation
- Scripted multi-fault scenarios matched against expected decision sequences
- Critical warnings interrupt lower ones immediately
- No flicker at thresholds thanks to hysteresis
- Worst-case response time measured against the target
Expected results
A warning manager that arbitrates simultaneous alerts by priority and drives RGB tell-tales, buzzer patterns and LCD messages, verified with scripted scenarios and timing measurements.
Take it further
- Receive warning conditions over CAN from other nodes
- Log every warning with RTC timestamps to SPI flash
- Add a configurable catalogue loaded from flash
- Study rider response times with different patterns
Viva and review questions
Why keep tell-tales lit when another message is on the LCD?
The rider must still know every active condition; the LCD shows one message at a time, but the tell-tales summarise all of them.
Why have a minimum display time?
Without it, rapidly changing warnings could flash messages too quickly to read; critical warnings still interrupt at once.
Why can't critical warnings be acknowledged?
They need immediate action, so silencing them could let the rider ignore a dangerous condition.