Skip to content

Latest commit

 

History

3 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

EV ADAS System — STM32F103C8T6

build platform language flash licence

A real-time Advanced Driver Assistance System for an electric vehicle, built on an STM32F103C8T6 "Blue Pill" in bare-metal Embedded C with the STM32 HAL. It runs a deterministic vehicle state machine, computes time-to-collision from three HC-SR04 ultrasonic sensors, drives a 20 kHz motor PWM with hardware fault cut-out, and streams binary telemetry over UART to a live Python instrument cluster.

Built during the Embedded Systems Internship at Emertxe Information Technologies, Bangalore (June – July 2026), as the capstone project against a formal requirements and design specification.

📺 Project walkthrough video · 📄 Engineering report · 🔧 Run it yourself in PICSimLab

Dashboard — nominal


What it does

Subsystem Behaviour
EV dynamics Physics-based inertia model — torque → acceleration → speed, with aerodynamic drag, mechanical braking and regenerative braking
Energy model SOC by energy integration (depletes under load, recovers under regen); range estimated from SOC and drive-mode efficiency
Forward collision TTC from front distance and speed; WARNING <50 cm or TTC <3 s, CRITICAL <20 cm or TTC <1.5 s
Blind-spot Left/right detection under 30 cm, gated above 20 km/h
Parking assist Below 10 km/h, a progressive 0–100 proximity score from the nearest of three sensors
State machine PARKED → READY → DRIVING → REGEN → FAULT, exit from FAULT only by explicit command
Fault management Over-temperature, low SOC and collision-critical latch a hard fault and cut motor PWM within one loop cycle; sensor and comms timeouts degrade gracefully as warnings
Alarms Four-level priority (P0–P3) driving LEDs and a passive buzzer with distinct tone/pattern per level
Telemetry Four binary packet types at 10 Hz over DMA-backed UART
Shell 15 commands for live inspection and fault injection over the same link
Watchdog IWDG resets the system if the main loop stalls beyond 1 s

Dashboard — collision critical


Architecture

flowchart LR
    subgraph Sensing
        POT["4x Potentiometer<br/>PA0-PA3"]
        US["3x HC-SR04<br/>PB0-PB5"]
    end

    subgraph MCU["STM32F103C8T6 @ 72 MHz"]
        direction TB
        SCHED["main.c<br/>TIM3 10 ms tick<br/>÷10 → 100 ms"]
        EV["ev_control.c<br/>speed · SOC · torque · range"]
        ULT["ultrasonic.c<br/>TIM2 µs echo timing"]
        ADAS["adas.c<br/>TTC · FCW · BSD · parking"]
        FLT["fault.c<br/>flag latch · safe state"]
        BUZ["buzzer.c<br/>TIM4 tone PWM"]
        TLM["telemetry.c<br/>packet serialiser"]
        SH["uart_shell.c<br/>ring buffer · parser"]

        SCHED --> EV
        SCHED --> ULT
        ULT --> ADAS
        EV --> ADAS
        ADAS --> FLT
        EV --> FLT
        ADAS --> BUZ
        EV --> TLM
        ADAS --> TLM
        FLT --> TLM
        SH --> EV
        SH --> ADAS
        SH --> FLT
    end

    subgraph Actuation
        PWM["Motor PWM<br/>PA8 · TIM1 · 20 kHz"]
        LED["4x Status LED<br/>PB8-PB11"]
        SPK["Buzzer<br/>PB6"]
    end

    HOST["Python Dashboard<br/>matplotlib + pyserial"]

    POT --> SCHED
    US --> ULT
    EV --> PWM
    ADAS --> LED
    FLT --> LED
    BUZ --> SPK
    TLM -->|"USART1 DMA<br/>115200 8N1"| HOST
    HOST -->|"shell commands"| SH
Loading

Scheduling

A single TIM3 interrupt at 100 Hz is the only periodic source. The main loop drains owed ticks from a counter rather than a flag, so if a job overruns the missed ticks are caught up and the physics integrator still receives exactly one dt per 10 ms of real time.

Job Period Work
10 ms 100 Hz ADC sampling, EV model, motor PWM duty, buzzer sequencer, shell, UART pump, watchdog refresh
100 ms 10 Hz Ultrasonic scan (all 3), ADAS evaluation, fault check, telemetry publish

Nothing in the main loop blocks. The buzzer patterns, the alarm test sequence and all UART transmission are tick-driven state machines rather than HAL_Delay() calls.


Repository layout

firmware/           STM32 project (STM32CubeIDE-compatible)
  Core/Inc          module headers
  Core/Src          module sources
  Drivers/          STM32F1 HAL + CMSIS
  Makefile          portable build — no IDE required
  ev_dash.ioc       CubeMX project file
dashboard/
  dashboard.py      live instrument cluster
  test_protocol.py  protocol round-trip tests
  make_preview.py   regenerates the README screenshots
docs/
  PROJECT_REPORT.md    full engineering report
  SIMULATION_GUIDE.md  run it in PICSimLab, no hardware
  PROTOCOL.md          UART wire format
  TRACEABILITY.md      requirement → code map, deviations, defects fixed
  WIRING.md            pin assignment and PICSimLab setup
  TEST_PLAN.md         manual test procedures
.github/workflows/
  build.yml            CI: builds both configs, runs protocol tests

Firmware modules

File Responsibility
main.c Peripheral init, scheduler, ISR callbacks
ev_control.c/.h Speed, SOC, torque, power, range, drive modes, motor PWM
adas.c/.h TTC, collision, blind-spot, parking assist, alarm priority
ultrasonic.c/.h HC-SR04 trigger/echo timing, distance conversion
fault.c/.h Fault evaluation, latching, safe-state enforcement
buzzer.c/.h TIM4 tone synthesis, non-blocking beep patterns
telemetry.c/.h Packet serialisation, CRC, DMA ring buffer
uart_shell.c/.h RX ring buffer, command parser
common.h Shared types, pin aliases, fault bitmask

Build

With the Makefile (no IDE)

Requires arm-none-eabi-gcc and make.

cd firmware
make

Produces build/ev_adas.elf, .hex and .bin. If your toolchain is not on PATH (for example the one bundled with STM32CubeIDE):

make GCC_PATH=/c/ST/STM32CubeIDE_1.19.0/STM32CubeIDE/plugins/com.st.stm32cube.ide.mcu.externaltools.gnu-tools-for-stm32.13.3.rel1.win32_1.0.0.202411081344/tools/bin

Build for size with make DEBUG=0 (-Os). Current footprint — comfortable headroom on 64 KB flash / 20 KB SRAM:

              text    data     bss     dec
debug  -Og   43236     476    4340   48052
release -Os  40196     476    4332   45004

Both configurations build clean under -Wall -Wextra.

With STM32CubeIDE

File → Import → Existing Projects into Workspace → select firmware/. Build normally; the CubeMX .ioc is included for regenerating peripheral init.

Flash

st-flash write firmware/build/ev_adas.bin 0x8000000

or use ST-Link via CubeIDE / STM32CubeProgrammer.


Run the dashboard

pip install pyserial matplotlib numpy
python dashboard/dashboard.py --port COM3      # hardware or PICSimLab bridge
python dashboard/dashboard.py --demo           # no hardware needed

The decoder handles both the binary packets and the legacy ASCII lines on the same link, so no configuration is needed.

Run the protocol tests:

python dashboard/test_protocol.py

UART shell

115200 8N1. Type help for the list.

mode <eco|normal|sport>       drive mode / torque scale
speed set <kmh>               inject vehicle speed
soc set <pct>                 override state of charge
temp set <degC>               override motor temperature
obstacle <cm>                 inject front distance
obstacle clear                release front sensor
blindspot <on|off>            simulate side vehicles
sim clear                     release ALL injections
fault inject <motor|soc|col>  force a fault condition
fault clear                   FAULT -> PARKED
stream <on|off>               telemetry stream
format <bin|ascii>            telemetry encoding
alarm test                    cycle buzzer P3 -> P1
status                        full system state
reset                         software reset via IWDG

Example — reproduce the collision-critical path:

speed set 72
obstacle 15
status
fault clear

Simulation

The project targets PICSimLab with a Blue Pill board. See docs/WIRING.md for the component list, pin connections and bring-up order. A virtual COM bridge (VSPE or similar) exposes PICSimLab's UART to the host so dashboard.py can attach.


Documentation

  • docs/PROJECT_REPORT.md — the full engineering report: architecture, design decisions, how the three contradictions in the specification were resolved, verification results, resource budget and limitations. Start here if you want the engineering story rather than the code.
  • docs/SIMULATION_GUIDE.md — run the whole system with no hardware: PICSimLab setup, part-by-part wiring, the UART bridge, an 11-step demo sequence and a troubleshooting table.
  • docs/TRACEABILITY.md — every requirement mapped to the code that implements it, the twelve places the implementation deliberately departs from the specification and why, and the twenty defects found and fixed.
  • docs/PROTOCOL.md — UART frame format and packet layouts.
  • docs/WIRING.md — pin map, timer allocation, PICSimLab and real-hardware notes.
  • docs/TEST_PLAN.md — 18 manual test procedures covering all five use cases and the acceptance criteria.

The requirements document contains a small number of internal contradictions — most notably TIM1 being assigned both a 20 kHz PWM carrier and a 100 Hz interrupt, and PB0 being listed as both the buzzer and the front sensor TRIG. Each is resolved explicitly and recorded in TRACEABILITY.md rather than silently.


Tech stack

Embedded C · STM32 HAL · STM32CubeIDE · CubeMX · ARM Cortex-M3 · GPIO / ADC / Timers / PWM / UART / DMA / IWDG · HC-SR04 · PICSimLab · Python · matplotlib · pyserial


Licence

MIT

About

Real-time EV ADAS on an STM32F103 Blue Pill: physics-based vehicle model, HC-SR04 time-to-collision, deterministic safety state machine, 20 kHz motor PWM with fault cut-out, DMA-backed binary telemetry, and a live Python dashboard. Bare-metal Embedded C.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages