master
id5100 — CI-V control for the ICOM ID-5100
Software control layer for the ID-5100 mobile transceiver over CI-V, via the RT Systems USB-29A cable (FTDI) plugged into the radio's [SP2] jack.
- Verified link:
/dev/ttyUSB0, 9600 baud, radio CI-V address 0x8C. - No dependencies — pure stdlib (
termios), no pyserial.
Layers
| File | Role | Tested by |
|---|---|---|
civ.py |
Pure CI-V codec: BCD freq/level encode-decode, frame build/parse | test_civ.py (11 unit tests) |
radio.py |
Serial transport + high-level Radio class (get/set freq, mode, vol, sql, smeter) |
live-radio integration |
cli.py |
Command-line front end | live-radio integration |
server.py |
Web control-panel server (stdlib http.server + JSON API) |
live-radio integration |
panel.html |
Instrument-panel web UI (amber VFD, S-meter, mode/level controls) | headless screenshot |
civ_probe.py |
One-shot link probe (read VFO frequency) | — |
CLI
./cli.py status # freq, mode, volume, squelch, s-meter at a glance
./cli.py freq # read VFO (MHz)
./cli.py freq 146.52 # set VFO to 146.520 MHz
./cli.py mode fm # set mode: lsb/usb/am/cw/fm/dv
./cli.py vol 30 # set AF volume 0-255 (read with no arg)
./cli.py sql 60 # set squelch level 0-255
./cli.py smeter # read S-meter 0-255
# global: --port /dev/ttyUSB0 --baud 9600
Web panel
python3 server.py # http://<host>:8550/ (binds 0.0.0.0 -> phone-reachable)
# --port 8550 --serial /dev/ttyUSB0 --baud 9600
Holds the serial port for its lifetime, so don't run cli.py while the server
is up (one process owns /dev/ttyUSB0). The page polls /api/status ~1/s for
live frequency, mode, S-meter, squelch, and levels; controls POST to
/api/{freq,mode,vol,sql}.
Repeaters (RepeaterBook CSV)
The panel's Repeaters card lists nearby repeaters from a local repeaters.json,
in RepeaterBook's proximity order, and tunes the radio (freq + duplex + tone) on tap.
# refresh the data: drop a new CSV into repeaterbook/, then re-ingest
python3 repeaters.py ingest repeaterbook/RB_2606141409.csv repeaters.json
- Data source: a RepeaterBook CSV proximity export (download from your account for your area). It has no lat/long, but it's sorted nearest-first, so row order is the rank.
- Filter: keeps FM + D-STAR on 2m/70cm (drops DMR/P25/NXDN-only). ~1073 of 1258 rows.
- API:
GET /api/repeaters?q=&limit=(search + nearest-first list);POST /api/tune-repeater(body = one record) → per-step result{ok, steps:{freq,duplex,tone_freq,tone_on}, errors}. - Offset: the offset amount is not set over CI-V (the ID-5100 rejects it); only the duplex direction (DUP±) is sent, and the radio applies its own configured offset (standard 0.6/5 MHz). Non-standard offsets are set on the radio head.
- D-STAR: DV rows tune freq/direction only — call routing (URCALL/RPT1/RPT2) is the separate RS-MS1A protocol, set on the head.
Library
from radio import Radio
with Radio("/dev/ttyUSB0", 9600) as r:
r.get_frequency() # -> Hz
r.set_frequency(146_520_000)
r.get_mode() # -> ("FM", 0x05)
r.set_volume(30)
Tests
python3 -m unittest test_civ -v
Gotchas / verified facts
- [SP2] is the CI-V port on the ID-5100 (doubles as 2nd speaker jack).
- Bus echo: the radio echoes your TX frame before replying;
transact()filters frames wherefrm != RADIO_ADDR. - Frequency BCD is little-endian; level/meter BCD is big-endian (
128 -> 01 28). - Mode FM = 0x05 confirmed empirically.
DV = 0x17is from the generic ICOM table and not yet verified on this radio — confirm before relying on it. - Repeater commands (verified live 2026-06-14): duplex direction
0x0F(0x10 simplex / 0x11 DUP− / 0x12 DUP+), repeater tone0x1B/0x00, tone on/off0x16/0x42. The tone wire field is 3 bytes (00pad + 2-byte BCD;00 08 85= 88.5 Hz). Offset-amount0x0Dis rejected (NG) by the ID-5100 — not supported. - Scope is CI-V (operate the radio). D-STAR DR routing / callsign entry live in the separate RS-MS1A data-jack protocol, not here.
Description
Languages
Python
68.1%
HTML
31.9%