build
Phase 3 of AI Closed-Loop Programming — the Build phase, and the driver of the whole loop:…
Drive the Embedded AI Harness testbench's RF signal generator over `/api/siggen/*` — continuous carrier, Morse/CW beacon, retune, PE4302 attenuation, and frequency listing. Use this skill whenever the user wants to emit, key, retune, or attenuate an RF signal from the testbench,
$ npx -y skills add SensorsIot/Embedded-AI-Harness --skill signal-generator --agent claude-codeHow it fires
How this skill gets triggered: by you, by Claude, or both.
/signal-generatorContext preview
The summary Claude sees to decide when to auto-load this skill.
Drive the Embedded AI Harness testbench's RF signal generator over `/api/siggen/*` — continuous carrier, Morse/CW beacon, retune, PE4302 attenuation, and frequency listing. Use this skill whenever the user wants to emit, key, retune, or attenuate an RF signal from the testbench,
name: signal-generator description: Drive the Embedded AI Harness testbench's RF signal generator over `/api/siggen/*` — continuous carrier, Morse/CW beacon, retune, PE4302 attenuation, and frequency listing. Use this skill whenever the user wants to emit, key, retune, or attenuate an RF signal from the testbench, even if they only say "CW beacon", "carrier", "Morse", "Si5351", "GPCLK", "PE4302 attenuator", "DF test", "direction finder", or "80m beacon" — those are all this one API. Always check `/api/siggen/status` first to see which backend (Si5351 vs GPCLK fallback) and attenuator are physically present before choosing a backend.
The testbench Pi has one signal-generator service. Two RF sources sit behind it (Si5351 on I²C, BCM2835 GPCLK on GPIO 5/6) and an optional PE4302 step attenuator can sit in the RF path. The endpoint is the same regardless of which backend is active — `/api/siggen/*` is the only entry point you should reach for.
This skill replaces the legacy `cw-beacon` skill, which only knew about GPCLK and led to wrong frequencies on testbenches that have an Si5351.
---
Before starting a carrier or recommending a backend, **GET `/api/siggen/status`**. The response tells you which hardware is detected:
{
"ok": true, "active": false, "backend": null,
"freq_hz": 0.0, "channel": null, "pin": null,
"atten_db": null, "morse": null,
"hardware": {"si5351": true, "gpclk": true, "pe4302": true}
}Why this matters:
Skipping the status check is the most common failure mode of this skill — pick the wrong backend and the carrier ends up tens of kHz off frequency, or you offer attenuation that doesn't exist.
---
Every endpoint, with its request and response shape: [FSD Appendix D.11](../../../docs/Harness-FSD.md#d11-signal-generator).
`freq` retunes an active carrier without restarting the Morse keyer — use it rather than stop/start when a test must not lose keying state.
{
"freq_hz": 3500000,
"backend": "auto",
"channel": 0,
"pin": 5,
"atten_db": 0,
"morse": {"message": "VVV DE TEST", "wpm": 15, "repeat": true}
}| Field | Type | Default | Notes | |-------|------|---------|-------| | `freq_hz` | number | required | Si5351 hits exactly; GPCLK snaps to nearest integer divider — read it back from the response | | `backend` | string | `"auto"` | `auto` / `si5351` / `gpclk`. Prefer `auto` unless you have a reason. | | `channel` | int | 0 | Si5351 output (0/1/2). Ignored by GPCLK. | | `pin` | int | 5 | GPCLK pin (5 or 6). Ignored by Si5351. | | `atten_db` | float | — | Initial PE4302 setting (0–31.5). Optional. | | `morse` | object | — | Omit → continuous carrier. Include → keyed beacon. |
`morse` shape: `{"message": str, "wpm": int|float = 15, "repeat": bool = true}`. WPM is PARIS-standard, 1–60.
The response echoes the *actual* state — always trust `freq_hz` from the response, not the request. Example:
{
"ok": true, "active": true, "backend": "si5351",
"freq_hz": 3500000.0000000005, "channel": 0, "pin": null,
"atten_db": 0.0,
"morse": {"message": "VVV DE TEST", "wpm": 15, "repeat": true}
}No body. Stops the carrier and disables the output. Idempotent.
{"freq_hz": 7100000, "channel": 0}Retunes the active carrier *without* tearing down the Morse keyer. Use this when sweeping or stepping through frequencies during a beacon transmission.
{"db": 12.5}Returns 4xx if PE4302 is not present. Range 0–31.5 dB in 0.5 dB steps. Pin sharing note: PE4302's LE line is GPIO 6, which is also GPCLK2 — when GPCLK is active on pin 6, attenuation control is unavailable. `Si5351 + PE4302` and `GPCLK on pin 5 + PE4302` are both safe.
Defaults: `low=3_500_000`, `high=4_000_000`, `backend=auto`. GPCLK returns discrete `{divider, freq_hz}` entries; Si5351 returns a single entry reporting the range as continuously tunable.
---
The Python driver mirrors the API one-for-one. Prefer these over raw curl when writing test scripts:
from testbench_driver import TestbenchDriver
wt = TestbenchDriver("$TESTBENCH_URL")
status = wt.siggen_status() # always first
print(status["hardware"])
wt.siggen_start(freq_hz=3_500_000) # continuous, auto backend
wt.siggen_freq(freq_hz=7_100_000) # retune in place
wt.siggen_atten(db=12.0) # PE4302
wt.siggen_start(freq_hz=3_571_000, # keyed beacon
morse={"message": "VVV DE TEST",
"wpm": 15, "repeat": True})
wt.siggen_stop()---
status = wt.siggen_status()
backend = "si5351" if status["hardware"]["si5351"] else "gpclk"
result = wt.siggen_start(freq_hz=3_500_000, backend=backend)
# On GPCLK, result["freq_hz"] may differ from 3_500_000 by ~25 kHz —
# tell the user the actual frequency.
print(f"Actual: {result['freq_hz']/1e6:.6f} MHz")wt.siggen_s
Spec to silicon, hands off. A horse is strong, fast, and willing — and useless for heavy loads until you harness it. The harness is not a part of the horse and not a part of the cart: it is the coupling that turns raw strength into pulled weight.
Phase 3 of AI Closed-Loop Programming — the Build phase, and the driver of the whole loop:…
Phase 2 of AI Closed-Loop Programming — Commissioning: prove the project's OWN…
Phase 0 of AI Closed-Loop Programming — Definition: engineers the WHAT the loop converges on.…
Complete ESP-IDF lifecycle: project setup, build, flash, monitor, and OTA. Automatically…
PlatformIO lifecycle for ESP32 firmware: platformio.ini, environment selection, build, upload…
Interview the user relentlessly about a plan or design until reaching shared understanding,…