Each project has something you can poke. The behaviour is taken from my own measurements and code.
A small car with a real car's wiring
A Raspberry Pi sees the lane, traffic lights and stop signs. It never touches a motor. Three STM32 ECUs do the sensing, driving and display, and they all talk over one CAN bus, the way a real vehicle splits its electronics.
4 nodes on one bus
500 kbps CAN, 11-bit IDs
500 ms safety watchdog
0.36 ms mean command/feedback round trip over 100 tests
Real screenshots from the robot's perception window. Pick what's in front of the camera.
DRIVING
Try to break the CAN bus
A live model of my four-node network, slowed down 4× so you can see the frames. The log uses the same IDs and byte layout as my firmware.
Motor: RUNNINGLeft 198 rpmRight 196 rpm
Time since last safety frame (0x100), limit 500 ms
[FSM] DRIVING
Rules the Motor ECU follows: an unsafe 0x100 stops the motors at once. No 0x100 for 500 ms also stops them. After recovery it waits for a fresh 0x200 command, so an old command is never replayed.
On the test track.Top view: Pi, camera mast and STM32 ECUs.Waiting at a red light.My desktop dashboard decoding the bus live.Raw candump: 0x100 safety, 0x200 command, 0x300 feedback.
Encoder feedback is used for RPM monitoring and fault checks. A closed-loop speed controller is the next step.
How this network was specified and tested before and after the boards were wired: HIL with CANoe.
Proved in CANoe, then on real ECUs
My In-Vehicle Networking capstone. The robot's CAN network was specified and tested in Vector CANoe first, then the same tests were run against real ECUs on a physical bus.
Four ECUs, one safety rule: the Motor ECU obeys a command only while the Sensor ECU's latest status is SAFE and less than 500 ms old. I built the whole network in CANoe before writing any firmware, wrote 19 automated tests against 13 requirements, and then reran those 19 tests unchanged on two STM32 boards and a laptop.
19 / 19 tests pass in simulation and on hardware
0 error frames in the final hardware regressions
0.002 ms SD on the real ECUs' 100 ms cycles
15 to 67 ms from a stimulus to UNSAFE on the real bus
Choose the components and sketch the hardware layout with the team.
Prove it in CANoe
A DBC with 6 messages and 20 signals, four CAPL ECU models, three live panels and a 19-case CAPL test module.
19 / 19 pass in real-time simulation
Run it on hardware
Two STM32F103 ECUs and a laptop ADAS ECU on a real bus, with CANoe on a VN5620A. Same DBC, same tests.
19 / 19 pass, 0 error frames
What's real on the bench
Tap any box to see what runs for real and what CANoe injects.
Scroll the diagram sideways to see the whole bench.
Real hardware on the busCAPL node inside CANoe
Run the regression
The 19 test cases from my CAPL test modules. Pick where to run them, then tap any test to see the requirement it checks and what was measured.
Find the bug
Real problems from bringing the bench up. Most of the time went into bus faults, not logic faults. Guess the cause, then see what it was.
The interlock, in CAPL
From my Motor_ECU.can model. The STM32 firmware follows the same rules.
on message Safety_Status /* 0x100: refresh watchdog, stop if UNSAFE */
{
sensorSafety = this.Safety_State;
watchdogOk = 1;
cancelTimer(tWatchdog);
setTimer(tWatchdog, WATCHDOG_MS);
if (sensorSafety != 0) StopMotors(ST_SAFETY);
}
on timer tWatchdog /* no 0x100 for 500 ms */
{
watchdogOk = 0;
StopMotors(ST_WATCHDOG);
setTimer(tWatchdog, WATCHDOG_MS);
}
on message Motor_Command /* 0x200: execute only if SAFE and fresh */
{
if ((sensorSafety == 0) && (watchdogOk == 1))
{
appliedL = Clamp100((int)this.Left_Cmd.phys);
appliedR = Clamp100((int)this.Right_Cmd.phys);
motorState = ST_NORMAL;
}
else { appliedL = 0; appliedR = 0; }
}
A SAFE status never restarts the motors on its own. Every stop is latched, and the ECU also starts latched, so the wheels move only on a new command received while SAFE.
The bench: CANoe, the VN5620A and the robot.The laptop ADAS ECU next to the robot.The panels during a US2 obstacle fault: UNSAFE, both wheels at 0 rpm.Trace, all six IDs decoded from my DBC.Simulation test report, 19 / 19, with the TC06 cycle-time check.
On the bench, sensor values come from CANoe and wheel speed comes from a model in the Motor ECU firmware. The Display ECU is still a CAPL node, and the ADAS ECU is a Python process on a laptop. Next: real sensor drivers and encoders, a Raspberry Pi and a TFT Display ECU, each checked with the same 19 tests.
12 V in, 5 V out, and it has to stay 5 V
I built a synchronous buck converter from a gate driver, two MOSFETs and an STM32, then wrote two controllers for it. A textbook dual-loop PI, and a sliding mode controller whose surface includes the inductor resistance and capacitor ESR.
100 kHz PWM from TIM1
10 kHz current loop, 1 kHz voltage loop in ISRs
±50 mV with SMC under load and input steps
STM32F103, IR2104, INA240, MATLAB/Simulink
Kick the converter
Choose a controller, then disturb it. The response is shaped on what I measured on the bench: PI dipped about 1 V on a load step, SMC stayed within about 50 mV.
CH11.00 V/divDual-loop PI5.00 V
Worst deviation since last disturbance: 0 mV
The bench build.Switch node at 99.85 kHz.SMC output, zoomed: all within about ±50 mV.
A safety controller, designed before it was coded
A bidirectional collision-avoidance system built in Simulink, Stateflow and Simscape. Ultrasonic distances drive a state machine that slows or stops two modelled DC motors, and Simulink Coder turns it into C.
342 blocks
100% decision coverage
Model Advisor warnings 29 → 1
C99 code generated with Simulink Coder
Move the obstacle
The front controller, using the thresholds from my generated C code.
NORMALdistance > 20: user PWM
EMERGENCY10 to 20: PWM held at 0.3
STOPdistance < 10: PWM 0
Motor PWM (user asks for 0.8)
status_msgf = "FORWARD:SAFE DRIVING"
The front Stateflow chart.Coverage: 100% decision on every subsystem.
A motor drive that protects itself
A closed-loop DC motor drive on an Arduino Uno, built for my Power System Protection Techniques course. An encoder gives position and speed. The controller also measures supply voltage and motor current, and acts like a protection relay: detect, trip, lock out, and reset only when it is safe.
4 trips recorded, each caught in 0.1 to 0.2 s
Trips at 8.0 V, 14.5 V and 0.15 A
PID position and PI speed control, 20 ms loop
Arduino Uno, ACS712, H-bridge, C++, Python
Run the drive like the real thing
One controller, as in my project. Choose a mode, press START, move the target, then push the supply voltage or the load current past a limit. The behaviour follows my recordings and firmware: position lags the target by about 0.2 s and takes the shortest way round, speed is ramped at 200 rpm per second, a fault must last 100 to 150 ms to trip, the first cause is kept, RESET is refused while the cause is still there, and after a reset the motor waits for START.
STOPmotor off
RUNclosed loop active
HOMEreturning to 0 degrees
TRIPfault latched
Target 0Actual 0Error 0Drive 0 %
Yellow: target. Blue: actual. Red band: motor tripped. Last 10 seconds.
power-up: STOP
Over-current trip on the bench: banner TRIPPED, drive 0 %.Load added by hand to raise the motor current.Over-voltage trip. RESET is refused until the supply is back in range.Over-current event: load applied, current rises, drive is cut.
Code, data and the full report are on GitHub. Emergency stop, stall and the PC-link watchdog are in the firmware and are still to be tested on the bench.
My pinout
Every pin is a peripheral I've actually used. Hover or tap one to see where.
Languages: Embedded C, C++, Python, MATLAB
Boards: STM32F1 (Cortex-M3), Arduino Uno (ATmega328P), ESP32, Raspberry Pi