# 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 ```bash ./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 ```bash python3 server.py # http://: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. ```bash # 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 ```python 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 ```bash 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 where `frm != RADIO_ADDR`. - **Frequency BCD is little-endian; level/meter BCD is big-endian** (`128 -> 01 28`). - **Mode FM = 0x05** confirmed empirically. `DV = 0x17` is 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 tone `0x1B/0x00`, tone on/off `0x16/0x42`. The tone wire field is **3 bytes** (`00` pad + 2-byte BCD; `00 08 85` = 88.5 Hz). **Offset-amount `0x0D` is 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. ```