Skip to content
Development
Skill

/sdr-receiver

Drive the Embedded AI Harness testbench's RTL-SDR receiver over `/api/sdr/*` — decode/analyze/power captures, the phased `acquire`, the interactive live rtl_433 console, "AI Sherlock" record→reverse-engineer, USB recovery, and the rtl_433 device database. Use this whenever the

BOOST
From plugin
embedded-ai-harness
18018 skills
Install
$ npx -y skills add SensorsIot/Embedded-AI-Harness --skill sdr-receiver --agent claude-code

How it fires

How this skill gets triggered: by you, by Claude, or both.

  • Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
  • You can call itInvoke it directly when you want it.
  • Slash command/sdr-receiver

Context preview

The summary Claude sees to decide when to auto-load this skill.

Drive the Embedded AI Harness testbench's RTL-SDR receiver over `/api/sdr/*` — decode/analyze/power captures, the phased `acquire`, the interactive live rtl_433 console, "AI Sherlock" record→reverse-engineer, USB recovery, and the rtl_433 device database. Use this whenever the

SKILL.md

sdr-receiver.SKILL.md
name: sdr-receiver
description: Drive the Embedded AI Harness testbench's RTL-SDR receiver over `/api/sdr/*` — decode/analyze/power captures, the phased `acquire`, the interactive live rtl_433 console, "AI Sherlock" record→reverse-engineer, USB recovery, and the rtl_433 device database. Use this whenever the user wants to receive, sniff, decode, analyze, or reverse-engineer an RF signal (433/315/868 MHz remotes, sensors, TPMS), read RSSI/pulse timing, recover a wedged dongle, or add a device to rtl_433 — even if they only say "rtl_433", "rtl-sdr", "sniff a remote", "what frequency is this", "signal too strong", "no signal", "AI Sherlock", or "add this remote". This is the receive-side counterpart to the transmit-only `signal-generator` skill. Always GET `/api/sdr/status` first to confirm the dongle is detected.

SDR Receiver (`/api/sdr/*`)

The testbench Pi has one RTL2832U dongle behind the `rtl_433` toolchain. Every receive operation goes through `/api/sdr/*`. It is the receive-side counterpart to the transmit-only `signal-generator` skill — never SSH in to run `rtl_433` yourself, drive the API.

**One dongle, one user.** Every capture (and the whole live console) holds a single-instance lock. While one is running, the others return `"SDR busy — a capture is already running"`. Stop the live console before a one-shot, and vice-versa.

---

Always check status first

**GET `/api/sdr/status`** before anything:

{"ok": true, "active": false, "mode": null, "freq_hz": 0,
 "hardware": {"rtl_433": true, "rtl_test": true, "device": true},
 "available": true}
  • `hardware.device: false` / `available: false` → the dongle isn't detected. Try

`POST /api/sdr/reset` (USB reset); if still absent, it's unplugged or the USB controller is wedged (see **Dongle recovery**).

  • `active: true` → something is already using the dongle; stop it first.

---

API summary

Every endpoint, with its request and response shape: [FSD Appendix D.10](../../../docs/Harness-FSD.md#d10-sdr-receiver).

Only the choice between them is this skill's: `capture` decodes, `analyze` gives raw pulse timing when nothing decodes, `power` measures a level, and `acquire` runs all three in sequence when you do not yet know what is on the air.

Common body fields: `freq_hz` (default 433.92 MHz), `duration_s`, `gain` (number dB, or omit for AGC), `sample_rate` (default 250 kHz — keep low, it's a Pi Zero 2 W), `flex` (an `-X` spec).

**A dongle plugged in after boot is picked up automatically** — status re-probes while the SDR is idle, so there is no need to restart the portal. If `device` stays `false`, the dongle really is absent or not enumerating.

**Pin `gain` on anything you will compare.** On AGC the tuner rescales from whatever it saw recently, so the same quiet band reads tens of dB apart between calls and a strong carrier compresses instead of standing clear. This bites hardest on `power`, where the whole point is comparing numbers.

**Is the dongle working at all?** `pytest pytest/ -k wt1909` transmits on 86.784 MHz (5th harmonic = 433.92 MHz) and requires the dongle to see a ≥15 dB lift, then a drop. It needs no DUT and no operator. Run it before debugging a "nothing decodes" complaint — it separates a dead receive path from a signal problem in one step.

---

The two big signal traps (learned the hard way)

1. **AGC saturates a too-close source.** With the tuner's default auto-gain, a strong near-field transmitter rails the input to full scale and fills an OOK signal's off-gaps — so a crisp remote reads as a continuous / misdetected **FSK** carrier and slices to all-zero codewords. Fix: **more distance** or a **fixed `gain`** (e.g. 33–40 dB). The tell is RSSI pegged near 0 dB regardless of distance, and `rtl_433` reporting FSK for a known-OOK remote.

2. **Decode mode is empty for unknown remotes.** Plain `decode` only emits for a *known* protocol. An unrecognised remote produces nothing — and no RSSI. Use `analyze` (or the live console, which runs `-A` in every mode) to see the signal regardless of decode. **Signal presence ≠ decodability.**

---

Live console (`/api/sdr/live/*`)

A persistent `rtl_433` whose merged output a reader thread fans into a sequence-numbered ring buffer the browser fast-polls (~500 ms; nothing dropped). `-A` runs in **every** mode, so the RSSI meter shows every burst's strength even when it doesn't decode.

`POST /api/sdr/live/start` body:

{"freqs": [433920000], "mode": "decode",
 "gain": 33.8, "sample_rate": 250000, "squelch": false,
 "hop_interval": 5, "flex": "n=r,m=OOK_PWM,s=340,l=2068,r=13936", "isolate": false}
  • `freqs`: one = locked, several = **hop** (`-H hop_interval`). Bands: 433.92 /

315 / 868. Hop to *find*, then lock to a single freq to *work* (hopping listens to each band ~1/3 of the time and misses momentary presses).

  • `mode`: `decode` (all built-ins) · `flex` (`-X`, `isolate:true` adds `-R 0`) ·

`analyze` (`-A` only).

  • `gain`: omit for AGC; a number sets a fixed tuner gain.
  • Poll `GET /api/sdr/live?since=<seq>`; each entry is `{seq, line, event}` where

`event` is the parsed JSON (or a synthetic `{rssi,snr,noise,analyzer:true}` from an analyzer `RSSI:` line). RSSI is **burst-driven** — it updates on a press, not continuously.

---

AI Sherlock — reverse-engineer an unknown remote

The record→analyze flow the console exposes as one toggle button:

1. **Start** (`/api/sdr/log/start`) — record the live stream (analyze+AGC). 2. Operator presses each key a few times. 3. **Stop** (`/api/sdr/log/stop`) — freeze the log. 4. `GET /api/sdr/log` → an assistant reads the bursts and derives: modulation/timing, the **constant preamble/device-ID**, and the **per-key varying field**, plus a decoder spec and button→code map.

`start_log` **clears** the previous recording (single in-memory buffer, lost on restart) — it's a record-analyze-now loop, not persistent storage.

---

Device database (make a

Read more
Ships withembedded-ai-harness

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.

Get the whole plugin
Stats
182
Stars
56
Forks
Maintained
Maintenance
Python
Language
MIT
License
1mo ago
Last commit
8mo ago
Created

Repo: SensorsIot/Embedded-AI-Harness

Other skills on embedded-ai-harness.