Hardware-in-the-Loop Test Framework
Python, CAN bus, analog/digital I/O, CI/CD
The Problem
Pierce Manufacturing's Hardware-in-the-Loop (HIL) simulator for fire apparatus ECUs was designed for manual testing. An engineer would sit at the test bench with mechanical switches and indicator lights, verifying behavior by hand. The process was:
- Slow - a full regression test could take days
- Error-prone - each engineer ran the tests slightly differently
- Untraceable - no automated test log to show what passed or failed
The simulator hardware itself was capable. What was missing was a software layer that let engineers treat it like a programmable test system instead of a manual switchboard.
The Approach
I pushed for automated test execution and built a Python abstraction layer that sat between the engineer's test scripts and the HIL hardware API.
Test script (Python) -> Abstraction layer -> HIL hardware API -> ECU
The abstraction layer handled:
- CAN bus message injection and monitoring - send a message, wait for a response, verify the payload
- Analog and digital I/O control - set a voltage on a pin, read a sensor input, toggle a relay
- Timing and sequencing - orchestrate multi-step test scenarios with precise timing
- Pass/fail logic - compare observed behavior against expected behavior, with configurable tolerances
The key insight was that the abstraction layer should hide the hardware details but expose the operations that engineers actually cared about: "send this CAN message and verify that response," not "set register 0x34 to value 0x7F."
The Result
The framework became the standard for new product testing at Pierce. Engineers could write test scripts in a few hours that would have taken days to run manually. The automated test suite could run overnight and produce a clean pass/fail report by morning.
More importantly, the framework made testing repeatable - the same test run on two different days would produce identical results, which was impossible with manual testing. This made regression testing reliable enough to run before every release.
Technical Details
The HIL simulator was built around a CAN bus backbone with analog and digital I/O channels all routed to I/O pins on National Intruments io modules. The Python abstraction layer communicated with the simulator hardware over a the python's Versistand package, sending commands and receiving responses in a custom protocol.
Key capabilities exposed through the abstraction layer:
- CAN message send/receive with filtering and pattern matching
- Digital output - set a pin high or low
- Digital input - read a pin state
- Analog output - set a voltage level (e.g., 0-5V or 0-24V for truck systems)
- Analog input - read a voltage level
- Timed sequences - execute a series of operations with precise delays between them
- Logging - every operation and its result recorded with timestamps
What I Learned
The HIL framework was a reminder that the best engineering tools are the ones that make the operator's job easier. The hardware was perfectly capable - what was missing was someone to bridge the gap between what the hardware could do and what the engineers needed it to do.
The Python abstraction layer was not technically complex. What made it valuable was that it removed friction. Engineers could focus on what to test instead of how to drive the hardware.