← Back to Plugins
Voice

Radio

richcannings By richcannings 👁 7 views ▲ 0 votes

Talk to OpenClaw over ham radio β€” ham radio voice channel plugin

GitHub

Install

npm install
npm

README

# openclaw-radio

### Turn any sound card into a radio channel.

**What if your AI assistant could talk on the radio?** openclaw-radio is an [OpenClaw](https://github.com/openclaw/openclaw) plugin that connects any USB sound card interface β€” [DigiRig](https://digirig.net/), AIOC, Signalink, or a CM108 dongle β€” to your AI assistant over ham radio. Plug in your interface, set your callsign, and you're on the air.

**Status:** First QSO completed on-air 2026-03-30. W6RGC/BOSS responding to voice transmissions on UHF FM. πŸ“»

---

## Features

- **Any audio interface** β€” DigiRig, AIOC, Signalink, CM108, or any USB sound card
- **Any PTT method** β€” serial (RTS/DTR), GPIO, VOX, or RX-only monitor mode
- **Dual-threshold carrier sense** β€” detects speech vs. squelch, handles mid-sentence pauses
- **Noise rejection** β€” sustained energy gate + post-STT word filter suppresses static hallucinations
- **FCC compliant** β€” callsign on every transmission, ID timer per Part 97.119
- **TX safety** β€” hardware watchdog prevents stuck transmissions
- **Ham radio personality** β€” AI knows how to talk on the radio (brevity, prowords, phonetics, signal reports)
- **Full observability** β€” JSONL logs with every TX/RX transcript and per-stage latency timing
- **Linux-first, Mac-supported** β€” ALSA/PulseAudio/PipeWire on Linux, CoreAudio via `sox` on macOS

---

## Quick Start

```bash
# 1. Install the plugin
openclaw plugins install /path/to/openclaw-radio

# 2. Set your callsign
openclaw config set channels.openclaw-radio.tx.callsign "W6RGC/BOSS"

# 3. Configure your interface (DigiRig example)
openclaw config set channels.openclaw-radio.audioInput "plughw:CARD=Device"
openclaw config set channels.openclaw-radio.audioOutput "plughw:CARD=Device"
openclaw config set channels.openclaw-radio.ptt.type serial
openclaw config set channels.openclaw-radio.ptt.port /dev/ttyUSB0
openclaw config set channels.openclaw-radio.ptt.lines '["RTS"]'

# 4. Enable the channel
openclaw config set channels.openclaw-radio '{"enabled":true}'

# 5. Restart gateway
openclaw gateway restart

# 6. Verify
/radio doctor
```

---

## Interface Configuration

### DigiRig
```
audioInput:  plughw:CARD=Device
audioOutput: plughw:CARD=Device
ptt.port:    /dev/ttyUSB0
ptt.lines:   ["RTS"]
```

### AIOC (All-In-One-Cable)
```
audioInput:  plughw:CARD=AllInOneCable
audioOutput: plughw:CARD=AllInOneCable
ptt.port:    /dev/ttyACM0
ptt.lines:   ["DTR"]
```

> ⚠️ AIOC firmware 1.00 is common in the wild. CM108 HID PTT requires upgrading to firmware 1.2.0+. Serial PTT (DTR) works on all firmware versions.

> ⚠️ **Serial PTT note:** The `serialport` npm `.set()` method is broken on some Linux kernels (notably Jetson + ttyACM). openclaw-radio uses a Python daemon with `ioctl(TIOCMSET)` instead β€” no action required, but `python3` must be in PATH.

---

## Commands

| Command | Description |
|---|---|
| `/radio tx <text>` | Manually transmit text over the air |
| `/radio doctor` | Diagnose audio devices, serial ports, STT config, and show full radio config |
| `/radio setup` | Same as doctor, plus shows config set commands to apply |
| `/radio status` | Current state and uptime |

---

## How It Works

```
Radio β†’ Sound Card β†’ arecord β†’ Carrier Sense β†’ WAV file
  β†’ OpenClaw STT β†’ Agent β†’ OpenClaw TTS β†’ aplay β†’ Sound Card β†’ Radio TX
                                    ↕
                              PTT daemon (serial)
```

1. **Listen** β€” `arecord` streams audio. Carrier sense monitors energy levels.
2. **Record** β€” when sustained speech is detected (3+ consecutive frames), audio accumulates until squelch closes.
3. **Transcribe** β€” utterance written to temp WAV, passed to OpenClaw's STT pipeline.
4. **Think** β€” transcribed text + ham radio persona + signal report routed to agent.
5. **Speak** β€” reply goes through OpenClaw's TTS pipeline β†’ raw PCM.
6. **Transmit** β€” PTT keyed, audio played via `aplay`, PTT released.

---

## Requirements

- [OpenClaw](https://github.com/openclaw/openclaw) with a configured STT provider (e.g., OpenAI Whisper API)
- Linux: `alsa-utils` (`arecord`/`aplay`)
- macOS: `sox` (`brew install sox`)
- `python3` in PATH (for serial PTT daemon)
- For serial PTT: USB-to-serial adapter

---

## Configuration Reference

All config lives under `channels.openclaw-radio` in `openclaw.json`.

| Key | Default | Description |
|---|---|---|
| `audioInput` | `plughw:0,0` | ALSA input device |
| `audioOutput` | `plughw:0,0` | ALSA output device |
| `sampleRate` | `16000` | Capture sample rate (Hz) |
| `ptt.type` | `none` | `none` / `serial` / `gpio` / `vox` |
| `ptt.port` | `/dev/ttyUSB0` | Serial port or GPIO path |
| `ptt.lines` | `["RTS"]` | Lines to assert: `["RTS"]`, `["DTR"]`, or `["RTS","DTR"]` |
| `ptt.leadMs` | `350` | Pre-audio delay after PTT key |
| `ptt.tailMs` | `120` | Post-audio delay before PTT release |
| `rx.speechThreshold` | `0.3` | RMS energy to trigger recording |
| `rx.carrierSenseThreshold` | `0.0008` | RMS floor for open squelch |
| `rx.maxSilenceMs` | `2000` | Silence before end-of-utterance |
| `rx.minSpeechFrames` | `3` | Consecutive frames required to start recording |
| `rx.minSpeechMs` | `200` | Anti-kerchunk minimum (ms) |
| `rx.cooldownMs` | `3000` | Post-RX cooldown |
| `tx.callsign` | `N0CALL/AI` | Your callsign, appended to every TX |
| `tx.policy` | `direct-only` | `direct-only` (callsign/alias required) or `proactive` |
| `tx.aliases` | `Boss,Overlord,Lord,Seven,7` | Trigger words for direct-only mode |
| `tx.maxTxMs` | `120000` | TX watchdog (ms) |
| `tx.postTxMuteMs` | `3000` | Post-TX mute window (ms) |
| `persona` | `true` | Inject ham radio personality prompt |

---

## Observability

Every exchange logged to `~/.openclaw/logs/radio-YYYY-MM-DD.log`:

```jsonl
{"summary":"RX: Boss this is W6RGC radio check","type":"RX","text":"Boss this is W6RGC radio check","rmsDb":-21.3,"peakDb":-1.2}
{"summary":"TX: W6RGC this is Boss, loud and clear. W6RGC/BOSS","type":"TX","text":"..."}
{"summary":"METRIC: sttMs=1568 dispatchMs=6148 responseTimeMs=2100","type":"METRIC","responseTimeMs":2100}
```

`responseTimeMs` is the key metric β€” dead air between squelch-close and PTT-key. Target: under 3 seconds.

---

## Development

```bash
git clone https://github.com/richcannings/openclaw-radio.git
cd openclaw-radio
npm install
npm test          # unit tests
npm run typecheck # TypeScript strict mode
```

See [docs/DESIGN.md](docs/DESIGN.md) for architecture, [docs/PRD.md](docs/PRD.md) for requirements status, and [docs/INSIGHTS.md](docs/INSIGHTS.md) for hard-won lessons from the first on-air tests.

---

## Links

- **OpenClaw** β€” [github.com/openclaw/openclaw](https://github.com/openclaw/openclaw)
- **DigiRig** β€” [digirig.net](https://digirig.net/)
- **AIOC** β€” [github.com/skuep/AIOC](https://github.com/skuep/AIOC)
- **w6rgc-ai** (predecessor) β€” [github.com/richcannings/w6rgc-ai](https://github.com/richcannings/w6rgc-ai)

## License

Apache 2.0

---

*73 de W6RGC/BOSS* πŸ“»
voice

Comments

Sign in to leave a comment

Loading comments...