I design and manufacture Raspberry Pi HATs, and for too long every board that shipped was tested by hand: wire it up, toggle outputs, repeat. This is the test bench that replaced me. The interesting part isn't any single test, it's that the bench's capabilities are defined entirely by which HATs are stacked on the Pi. Swap the HAT combination and the.robot file, and the same bench tests completely different hardware. No dedicated fixture electronics, no separate PC: the Pi drives the instruments, runs the tests, flashes firmware, and writes the reports.
Full disclosure: the I/O HATs used here are boards I design and sell, and the DUT is one of my own products. The architecture works with any I/O boards.
The problemTwo pains pushed me into building this, and they turn out to be the same problem wearing different hats :).
End-of-line testing. Every manufactured board needs a functional test before shipping. Manual EOL testing is slow and, worse, inconsistent. The human checking board #100 of the day is not the human who checked board #2.
Firmware regression. Every firmware change should re-run the full functional suite against real hardware, and nobody does regression testing by hand more than twice.
Both reduce to: stimulate the DUT's inputs, observe its outputs, compare against expectations. So one bench does both jobs.
The core ideaA Raspberry Pi 4 sits in the middle. On top of it: whichever I2C I/O HATs match the DUT. Need to read the DUT's outputs? Stack a digital-input HAT. Need to drive its inputs? Stack a relay HAT or some other type of output HAT. I'm using I2C HATs, so each HAT has a configurable I2C address, they all share one bus, and everything goes through one Python library. Robot Framework sits on top and turns the Python calls into plain-English test cases and outputs nice, detailed HTML reports.
Contrast with a typical commercial functional test fixture: built for exactly one product, rebuilt from scratch when the product changes. Here, the Pi and HAT stack stay put. Need more I/O for the next product? Stack another HAT, give it an address. In practice, only the.robot file changes.
A lucky twist in my case: since my DUTs are themselves Pi HATs, they connect the same way the bench HATs do, so the stack plus wiring to terminal blocks *is* the fixture. No bed of nails. For any other product you'd put a jig (pogo pins, harness, clamp) between the terminal blocks and the DUT; the test logic doesn't care.
What's on the benchCurrent DUT: a board with 6 isolated digital inputs and 6 relay outputs (DI6acDQ6rly I2C HAT), which conveniently exercises everything in one suite: input reading, relay switching, interrupts, and boot/address jumpers.
The bench stack:
Two details I'm fond of:
1. Jumpers emulated by relays. The DUT's boot and address jumpers are each replaced by a relay contact, so the suite can short pins programmatically: activate the boot "jumper, " reset the DUT over I2C, scan the bus, assert it enumerated at the bootloader address. The address-jumper test is a Robot Framework template, a four-row table of jumper index vs. expected address. A broken jumper solder-joint/pad/trace on a manufactured board fails instantly, by name.
2. A deliberately marginal supply. The DUT inputs are driven from a separate 3V supply chosen to sit right at the minimum high-level threshold. Every input test therefore validates the input circuit's *sensitivity*, not just its logic. The inputs also accept both wiring polarities, so a dedicated relay HAT(DQ5rly I2C-HAT) flips the supply and the whole input matrix runs again reversed.
Block diagram+---------- TEST BENCH -----------+
| | I2C bus + power
| +------------------------------------+
| | | |
| +----------------------+ | +--------------------+
| | Test Bench HAT stack | | | DUT |
| | | | | |
| | DQ10rly @ 0x5E |------------>| boot/addr jumpers |
| | DI16ac @ 0x40 |<------------| DUT relay outputs |
| | DQ10rly @ 0x5F |---+-------->| DUT digital inputs |
| | DQ5rly @ 0x5D |---+ | | |
| +----------+-----------+ | +--------------------+
| | I2C bus + power |
| +----------v----------+ |
| | Raspberry Pi 4 | |
| +---------------------+ |
+---------------------------------+Why Robot FrameworkNo deep selection process: the HAT libraries are Python, I had Robot Framework experience from a previous job, done. pytest would work just as well. The real value is in the Python layer underneath, not the runner. What Robot gives for free: readable keyword-style test cases, HTML report + log per run, and a clean split between test logic and hardware access. The glue layer exposing the HAT library as Robot keywords is thin and linked below.
The suite, briefly- Suite setup creates a handle per bench HAT and resets every output, so each test starts from a known state. Test #1 connects to the DUT, checks name/firmware version, verifies all I/O idle, since there's no point continuing otherwise.
- Input tests run per-channel on each COM pin, then all channels at once as a bitmask read in both polarities. That single-pass bitmask check is exactly what an EOL run wants: fast, all channels, one verdict.
- Interrupt tests are three order-dependent cases: pulse with IRQs disabled does nothing; enabling rising-edge detection makes a pulse assert the active-low IRQ pin; clearing the capture register releases it. The pulse is 15 ms: long enough for relays that simulate it to actually make contact, short enough to look like a real event. Every 'Sleep' in the suite exists because a relay, a bus, or a firmware state machine demanded it.
- Relay tests close each DUT relay and read the corresponding monitor input.
- The one manual step: LEDs. The suite chases the channel LEDs and uses Robot's Dialogs library to ask the operator for a verdict. This step is living on borrowed time, a camera is next!
During firmware development the suite is a regression net: flash, run, know in minutes whether the change broke inputs, IRQ handling, or address selection. At end-of-line the *identical* suite is the production gate: jumpers, both polarities, interrupts, relays, LEDs, consistent verdicts across hundreds of boards, failing channel named in the report. One set of test logic, maintained once.
Hard-earned warnings!- Trim bench wiring to length and hot-glue the connectors. A bench sees hundreds of insertion cycles, and loose wires produce the most maddening class of intermittent failure.
- Do NOT write Robot's output files (report.html, log.html, output.xml) to the Pi's SD card if the bench runs hundreds of cycles. A power loss mid-write will eventually corrupt the card. Network share or external storage from day one.
- Camera-based LED verification (kills the last manual step; write-up coming as a project log)
- GitLab CI runner on the Pi, so every firmware commit gets tested on real hardware





Comments