Plenty of embedded systems live or die on an analog input. A power-management board tracks a solar array. A motor controller reads back position or current from a moving shaft. A sensor node watches a 4–20 mA loop from the field. The firmware that interprets those signals is exactly the kind of code that passes every unit test and then misbehaves the first time the real waveform does something unexpected.
The problem in CI is that you can't put a solar array, a spinning motor, or a factory current loop on a test rig. So that code usually goes untested against realistic inputs. The BenchPod closes that gap from both directions: it can measure a real analog signal precisely, and it can reproduce one on demand.
A real analog front-end, not GPIO
The BenchPod's analog path is built for instrumentation, not bit-banging:
- A 16-bit ADC sampling at up to 1 MSPS, with a ±30 V input range through a compensated divider — so it can digitize a real bus rail or feedback line, not just a 0–3.3 V logic level.
- A 16-bit DAC with selectable output ranges (0–5 V, 0–3 V, and a bipolar ±14 V path) to drive the DUT's input.
- SMA connectors for clean analog I/O and screw terminals for 4–20 mA industrial current-loop interfaces.
- An on-board calibration relay that loops the DAC back into the ADC, so the pod can check its own output against its own measurement and stay trustworthy over time.
That combination is what makes record-and-replay possible: you measure with enough resolution to capture the real shape of a signal, then regenerate it with enough resolution that the DUT can't tell the difference.
Record once, replay forever
The workflow is the same regardless of the signal:
- Capture the real input on the bench with the ADC while the system runs normally — the solar panel under changing light, the motor moving through its range, the loop sensor over a shift.
- Store that trace as part of your test fixtures.
- Replay it through the DAC into the DUT on every CI run, so the firmware sees a faithful copy of the field signal without the field.
from embeddedci import benchpod
with benchpod.BenchPod("192.168.1.213") as bp:
# Capture the real-world signal on the bench
samples = bp.capture(samples=4096, sample_rate_mhz=1.0)
# ...later, in CI, drive the DUT's input with a waveform
bp.command({"cmd": "generate", "waveform": "sine",
"freq": 50.0, "amplitude": 3.0, "offset": 1.5})Now the DUT's ADC reads the same curve in a test runner that it would read in production — and you can assert on what the firmware decided to do about it.
Example: a solar array input
A photovoltaic input isn't a steady voltage; it's a curve that shifts with irradiance and load. An MPPT (maximum-power-point tracking) algorithm sweeps the operating point looking for the knee of that curve, and it has to keep tracking as clouds roll in and out.
Record the panel's output across a few real conditions — full sun, partial shade, a fast transient — and replay each one into the power board in CI. Then assert that the tracker actually lands near the maximum power point and re-converges after a step change. That's a test you simply cannot write against a bench power supply set to a fixed voltage.
Example: motor feedback
A stepper or BLDC controller reads an analog feedback signal — a current-sense line, a back-EMF measurement, a position potentiometer. Capture that signal from a real moving motor once, and you have a deterministic stand-in for the mechanics. Replay it and check that the controller's commutation, stall detection, or position estimate respond correctly — every run, identically, with no motor to wear out or get out of alignment.
The interesting part: inject faults
Replaying a clean signal proves the happy path. The DAC is also where you make the field misbehave on purpose — and that is where the bugs live:
- Out-of-range excursions — push the input above or below what the firmware expects and confirm it clamps, flags a fault, or shuts down safely instead of wrapping around or latching up.
- Dropouts and glitches — splice a momentary loss or a spike into the replayed trace and check the firmware rides through it (or fails safe) rather than acting on garbage.
- Drift and noise — add slow offset drift or superimposed noise and verify filtering and calibration logic still hold.
- Brown-outs and transients — combine the DAC with the pod's power control to sag the supply mid-measurement and watch what the ADC path does.
Each of these is a scenario your product will meet eventually. Injecting it deliberately, on every commit, turns "we think it handles a sensor fault" into a test that fails loudly the day someone breaks the handling.
From bench to CI
The point isn't to model the physics — it's to give the DUT a realistic input and a realistic bad input, on demand, with no analog lab around the test runner. Capture the signals that matter once, and every CI run gets to ask the question that used to need the whole rig: does the firmware do the right thing when the analog world does the wrong thing? For the digital side of the same bench, see Driving the BenchPod with Claude and the pytest framework.