OpenHTF plug for driving an EmbeddedCI BenchPod directly over TCP or serial (no cloud)
Project description
embeddedci-openhtf
⚠️ Experimental. This package is new and its API may change between releases. Pin a version and expect breaking changes before 1.0.
Drive an EmbeddedCI BenchPod from OpenHTF — Google's open-source hardware test framework — connecting directly to the pod over a TCP socket or serial port. No EmbeddedCI cloud account, OIDC, or web UI is required: this package is for teams who want OpenHTF's test sequencing, limits, records, and station GUI while talking straight to a pod on their own bench.
It's a thin wrapper over the embeddedci BenchPod SDK: a single
plug plus a few phase helpers. The dependency direction is strictly
embeddedci-openhtf → embeddedci.
pip install embeddedci-openhtf # pulls in embeddedci + openhtf
The plug
BenchPodPlug opens a BenchPod when a test starts and closes it at teardown.
Bind the connection inline with benchpod_plug(...), or leave it unbound and
supply it through OpenHTF config / the BENCHPOD_CONNECTION env var.
import openhtf as htf
from embeddedci_openhtf import benchpod_plug
bench = benchpod_plug("192.168.1.50:8080") # TCP — or benchpod_plug("/dev/ttyACM0")
@htf.plug(bench=bench)
def power_up(test, bench):
bench.power_on() # methods proxy to the BenchPod SDK client
bench.pod.capture_uart(...) # or reach the full client via .pod
Connection forms (all direct, never cloud):
| Form | Example |
|---|---|
TCP host[:port] |
benchpod_plug("192.168.1.50:8080") |
| Serial device path | benchpod_plug("/dev/ttyACM0") / benchpod_plug("COM5") |
BENCHPOD_CONNECTION env |
benchpod_plug() (unbound) |
| OpenHTF config | htf.conf.load(benchpod_connection="...") then @htf.plug(bench=BenchPodPlug) |
Phase helpers
Ready-made, fully-decorated phases for the common steps:
from embeddedci_openhtf import benchpod_plug, flash_phase, boot_banner_phase
bench = benchpod_plug("192.168.1.50:8080")
test = htf.Test(
flash_phase(bench, file="fw.elf", target="target/stm32f4x.cfg",
swclk=11, swdio=12, nreset=3), # records flash_ok, attaches openocd.log
boot_banner_phase(bench, rx=1, tx=2, expect="APP_OK"), # records boot_ok, attaches uart.txt
)
test.execute(test_start=lambda: "SN-0001")
LA channels are 1-12 (the pod has 12 generic logic-analyzer channels and no fixed-role pins — wire any DUT signal to any channel and name it here).
For anything custom, write a normal phase and use the recorders in
embeddedci_openhtf.measurements (record_flash, record_uart,
record_samples) to map SDK results onto measurements and attachments.
flash_phase needs openocd on PATH (the pod is the CMSIS-DAP probe; OpenOCD
runs the flash algorithm from the target= config, so every OpenOCD-supported
MCU works unchanged).
Analog steps
The pod's DAC output and ADC input are exposed as analog phases (and low-level helpers). These are TCP-only — they need the JSON/sample channel that the serial console doesn't provide.
from embeddedci_openhtf import (
benchpod_plug, signal_generate_phase, adc_capture_phase, loopback_measure_phase,
)
bench = benchpod_plug("192.168.1.50:8080")
test = htf.Test(
# drive the DAC and capture the ADC together (loopback), assert the round trip
loopback_measure_phase(bench, waveform="sine", freq=10_000, amplitude=120,
pp_range=(180, 255), mean_range=(110, 150)),
# or: free-run a waveform, then snapshot the ADC separately
signal_generate_phase(bench, waveform="square", freq=1_000, amplitude=100),
adc_capture_phase(bench, samples=4096, pp_range=(150, 255)),
)
Each capture/measure phase records <prefix>_min / _max / _mean / _pp
(peak-to-peak) measurements — pass any as (low, high) to make it a limit — and
attaches the raw samples as adc.json. The low-level helpers signal_generate,
signal_stop, and measure are available for custom phases.
Station mode (persistent connection)
By default the plug opens a connection per Test.execute() and closes it at
teardown. On a station cycling many DUTs back-to-back, pass persistent=True to
keep one connection open across executions (re-checked with a ping each run,
reconnected if it dropped). Reuse the same plug class for every execution, and
close it once at the end:
from embeddedci_openhtf import benchpod_plug, close_persistent_benchpods
from openhtf.plugs import user_input
bench = benchpod_plug("192.168.1.50:8080", persistent=True)
test = htf.Test(power_phase(bench, on=True), boot_banner_phase(bench, rx=1, tx=2, expect="APP_OK"))
try:
while test.execute(test_start=user_input.prompt_for_test_start()):
pass # next DUT — same pod connection
finally:
close_persistent_benchpods() # also runs automatically at process exit
Examples
examples/flash_and_boot.py— flash over SWD then assert the boot banner, over a direct TCP connection.examples/serial_smoke.py— a no-flash power + UART smoke test with a parsed measurement, over a direct serial connection.examples/analog_loopback.py— DAC→ADC loopback signal-path self-test, over a direct TCP connection.examples/station.py— a station loop testing many DUTs over one persistent connection.
Development
# from the repo root
pip install -e "packages/embeddedci[dev]"
pip install -e "packages/embeddedci-openhtf[dev]"
pytest packages/embeddedci-openhtf
The test suite runs the real OpenHTF executor against an in-memory fake transport
(tests/_fake.py), so it needs no pod and no OpenOCD.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file embeddedci_openhtf-0.1.0.tar.gz.
File metadata
- Download URL: embeddedci_openhtf-0.1.0.tar.gz
- Upload date:
- Size: 17.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
1ac31d34740c22f1353be4b87a4ea27469a4cd8892b5838608fbd108f21511f4
|
|
| MD5 |
dc3a680743f481d7ff02f7c8ddc7868f
|
|
| BLAKE2b-256 |
337ce18c942e044da7356041271da42f29dcec22a0e9a1432356885403edb26f
|
Provenance
The following attestation bundles were made for embeddedci_openhtf-0.1.0.tar.gz:
Publisher:
publish.yml on embeddedci-com/embeddedci-python
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
embeddedci_openhtf-0.1.0.tar.gz -
Subject digest:
1ac31d34740c22f1353be4b87a4ea27469a4cd8892b5838608fbd108f21511f4 - Sigstore transparency entry: 2040170522
- Sigstore integration time:
-
Permalink:
embeddedci-com/embeddedci-python@5f10ce059ea8840787bec3248b3598ae7bd4b088 -
Branch / Tag:
refs/tags/embeddedci-openhtf-v0.1.0 - Owner: https://github.com/embeddedci-com
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@5f10ce059ea8840787bec3248b3598ae7bd4b088 -
Trigger Event:
push
-
Statement type:
File details
Details for the file embeddedci_openhtf-0.1.0-py3-none-any.whl.
File metadata
- Download URL: embeddedci_openhtf-0.1.0-py3-none-any.whl
- Upload date:
- Size: 14.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
244b73e7deaceb3a90973a61e86b5e87f820fb0e5d3f0ae486498c9c62f5cde8
|
|
| MD5 |
40a894ed0cde6fbbabed211a7ec66ae5
|
|
| BLAKE2b-256 |
070efec888d0bf525e6d286c9e3093d1e54464a1dae47d8eb5fb0d492114078e
|
Provenance
The following attestation bundles were made for embeddedci_openhtf-0.1.0-py3-none-any.whl:
Publisher:
publish.yml on embeddedci-com/embeddedci-python
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
embeddedci_openhtf-0.1.0-py3-none-any.whl -
Subject digest:
244b73e7deaceb3a90973a61e86b5e87f820fb0e5d3f0ae486498c9c62f5cde8 - Sigstore transparency entry: 2040170604
- Sigstore integration time:
-
Permalink:
embeddedci-com/embeddedci-python@5f10ce059ea8840787bec3248b3598ae7bd4b088 -
Branch / Tag:
refs/tags/embeddedci-openhtf-v0.1.0 - Owner: https://github.com/embeddedci-com
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@5f10ce059ea8840787bec3248b3598ae7bd4b088 -
Trigger Event:
push
-
Statement type: