Skip to content

IC-7610 USB Serial Backend Setup (macOS)

This guide shows how to control the IC-7610 via USB serial CI-V + USB audio devices instead of the default LAN backend.

Why Use the Serial Backend?

  • No network required — direct USB connection
  • Lower latency — no UDP/network overhead
  • Simpler setup — no IP config, username, or password
  • Field operation — works without WiFi/Ethernet

Hardware Requirements

  • IC-7610 transceiver
  • USB A-to-B cable (typically included with the radio)
  • macOS computer (tested on Ventura+ arm64/Intel)

Radio Configuration

Critical Setup Step

On the IC-7610, navigate to Menu → Set → Connectors → CI-V → CI-V USB Port and set it to Link to [CI-V], NOT [REMOTE].

  • Link to [CI-V] — serial CI-V commands work (required for icom-lan serial backend)
  • [REMOTE] — RS-BA1 mode, serial CI-V is blocked

This finding was confirmed with live hardware validation in issue #146.

Setting Value Why
CI-V USB Port Link to [CI-V] ✅ Required — enables serial CI-V
CI-V USB Baud Rate 115200 Recommended for scope/waterfall
CI-V Address 0x98 (default) Library auto-detects, but confirm if changed
USB Audio TX Enabled Allows browser/WSJT-X TX via USB audio
USB Audio RX Enabled Exports RX audio to computer

Baud Rate

  • 115200 baud is recommended for scope/waterfall capability
  • Lower baud rates (19200, 9600) work for basic control (freq, mode, PTT) but scope/waterfall is disabled by a guardrail due to high packet rate
  • CI-V baud rate IS significant on IC-7610 — it must match between radio and library

macOS Setup

1. Install icom-lan

# Core install — includes serial CI-V (pyserial), USB audio RX/TX,
# audio-device listing (sounddevice + numpy + opuslib).
pip install icom-lan

Note

Since v0.19 the audio-bridge stack (sounddevice, numpy, opuslib) is part of the core install. The legacy [bridge] and [audio] extras still resolve but are now no-op aliases.

2. Connect the Radio

  1. Power on the IC-7610
  2. Connect USB A-to-B cable from Mac to IC-7610 USB (B) port (rear panel, square connector)
  3. Wait ~5 seconds for macOS to enumerate the device

3. Find the Serial Device

# List serial devices
ls -l /dev/cu.usbserial-*

# Example output:
# /dev/cu.usbserial-111120

The IC-7610 typically appears as /dev/cu.usbserial-XXXXXX where XXXXXX is the radio's serial number.

You can also use icom-lan discover --serial-only to find USB-connected radios automatically:

icom-lan discover --serial-only
IC-7610:
  • Serial: /dev/cu.usbserial-111120 (19200 baud)

4. Find USB Audio Devices

# List available audio devices
icom-lan --list-audio-devices

Audio-device listing requires sounddevice, which ships with the core install since v0.19 (pip install icom-lan).

Example output:

4 audio device(s):
  [0] IC-7610 USB Audio  (in=2, out=2)
  [1] Built-in Microphone  (in=2, out=0)
  [2] Built-in Output  (in=0, out=2)
  [3] BlackHole 2ch  (in=2, out=2)

The IC-7610 USB audio device is typically named IC-7610 USB Audio or similar.

JSON Output

icom-lan --list-audio-devices --json
Returns structured JSON for scripting.

Multi-Radio: Automatic USB Audio Resolution

When two or more Icom radios are connected via USB simultaneously (e.g. IC-7300 + IC-7610), each exposes an identically named "USB Audio CODEC" device. Specifying --rx-device by name is ambiguous in this situation.

The serial backend automatically resolves the correct audio device using USB topology matching (macOS only): it reads each device's USB hub location from IORegistry and matches the audio device that shares the same USB hub as the serial CI-V port. No manual index specification is required.

If topology resolution fails (e.g. on Linux), the driver falls back to selecting the first matching USB Audio CODEC device by name, which may be incorrect for multi-radio setups. In that case, specify explicit device indices with --rx-device and --tx-device.

5. Test the Connection

# Set environment variables
export ICOM_SERIAL_DEVICE=/dev/cu.usbserial-111120
export ICOM_SERIAL_BAUDRATE=115200

# Test basic control
icom-lan --backend serial status
icom-lan --backend serial freq
icom-lan --backend serial mode

Expected output:

Frequency:    14,074,000 Hz  (14.074000 MHz)
Mode:         USB
S-meter:      42
Power:        50

6. Test Audio (Optional)

# Capture 10 seconds of RX audio to WAV
icom-lan --backend serial \
    --rx-device "IC-7610 USB Audio" \
    audio rx --out test_rx.wav --seconds 10

# Audio devices are auto-detected if not specified

Usage Examples

CLI

# Status check
icom-lan --backend serial status

# Set frequency
icom-lan --backend serial freq 7.074m

# Set mode
icom-lan --backend serial mode USB

# PTT on/off
icom-lan --backend serial ptt on
icom-lan --backend serial ptt off

# CW keying
icom-lan --backend serial cw "CQ CQ DE KN4KYD K"

# Attenuator (uses Command29 for IC-7610)
icom-lan --backend serial att 18
icom-lan --backend serial att 0

# Preamp
icom-lan --backend serial preamp 1
icom-lan --backend serial preamp 0

Python API

import asyncio
from icom_lan.backends.factory import create_radio
from icom_lan.backends.config import SerialBackendConfig

async def main():
    config = SerialBackendConfig(
        device="/dev/cu.usbserial-111120",
        baudrate=115200,
        radio_addr=0x98,
        rx_device="IC-7610 USB Audio",  # or None for auto-detect
        tx_device="IC-7610 USB Audio",  # or None for auto-detect
    )

    radio = create_radio(config)

    async with radio:
        # Control
        freq = await radio.get_frequency()
        print(f"Frequency: {freq/1e6:.3f} MHz")

        await radio.set_frequency(14_074_000)
        await radio.set_mode("USB")

        # Meters
        s = await radio.get_s_meter()
        print(f"S-meter: {s}")

        # Audio (if audio_capable)
        from icom_lan.radio_protocol import AudioCapable
        if isinstance(radio, AudioCapable):
            def on_audio(packet):
                print(f"RX audio: {len(packet.data)} bytes")

            await radio.start_audio_rx_opus(on_audio)
            await asyncio.sleep(10)
            await radio.stop_audio_rx_opus()

asyncio.run(main())

Web UI with Serial Backend

# Start web UI on serial backend
icom-lan --backend serial \
    --rx-device "IC-7610 USB Audio" \
    --tx-device "IC-7610 USB Audio" \
    web

# Then open http://localhost:8080

The web UI will show: - Frequency/mode control - Meters (S-meter, power, SWR during TX) - RX audio streaming to browser - TX audio from browser microphone (if USB audio TX is enabled)

rigctld with Serial Backend

# Start rigctld server on serial backend
icom-lan --backend serial serve

# Then configure WSJT-X:
# Radio: Hamlib NET rigctl
# Network Server: localhost:4532

Capability Differences: LAN vs Serial

Feature LAN Backend Serial Backend
Control (freq/mode/PTT) ✅ Full ✅ Full
Meters (S/SWR/ALC) ✅ Full ✅ Full
Audio RX ✅ Opus/PCM over UDP ✅ USB audio device
Audio TX ✅ Opus/PCM over UDP ✅ USB audio device
Scope/Waterfall ✅ Full (~225 pkt/s) ⚠️ Requires ≥115200 baud*
Dual Receiver ✅ Command29 ✅ Command29
Remote Access ✅ Over LAN/VPN ❌ USB only
Discovery ✅ UDP broadcast ✅ CI-V auto-probe

* Scope guardrail: Serial backend enforces a minimum 115200 baud for scope/waterfall operations due to high CI-V packet rate. Lower baud rates risk command timeout/starvation. Override is possible via allow_low_baud_scope=True or ICOM_SERIAL_SCOPE_ALLOW_LOW_BAUD=1 (use with caution).

Troubleshooting

"No such file or directory: /dev/cu.usbserial-..."

Cause: USB cable not connected, or radio not powered on.

Fix: 1. Check USB cable connection (rear panel USB (B) port on IC-7610) 2. Power-cycle the radio 3. Wait 5-10 seconds after power-on 4. Run ls -l /dev/cu.usbserial-* again

"Permission denied: /dev/cu.usbserial-..."

Cause: User lacks permissions to access serial device.

Fix (macOS typically doesn't need this, but if it happens):

# Add your user to the dialout group (Linux)
sudo usermod -a -G dialout $USER

# Logout and login again

On macOS, if you see permissions issues, try:

# Check ownership
ls -l /dev/cu.usbserial-*

# Typically owned by root:wheel with mode 0666 (world read/write)
# If not, contact system admin or check USB security settings

"Audio device 'IC-7610 USB Audio' not found"

Cause: USB audio not exported by radio, or wrong device name.

Fix: 1. Verify USB audio is enabled in radio settings: - Menu → Set → Connectors → USB Audio → Enabled 2. List available devices:

icom-lan --list-audio-devices
3. Use exact device name from the list (case-sensitive) 4. If still not visible, disconnect/reconnect USB cable

"Scope over serial requires baudrate >= 115200"

Symptom: CommandError when calling enable_scope() or capture_scope_frame() with baud rate < 115200.

Cause: Scope/waterfall CI-V traffic is high-rate (~225 packets/sec on LAN). Lower serial baud rates cannot sustain this rate without starving command responses.

Fix: 1. Recommended: Set CI-V USB Baud Rate to 115200 in radio settings 2. Override (use with caution):

config = SerialBackendConfig(..., allow_low_baud_scope=True)
or
export ICOM_SERIAL_SCOPE_ALLOW_LOW_BAUD=1
Library will log a warning about timeout risk.

"CI-V response timed out" on serial

Causes: 1. Wrong baud rate — radio and library must match 2. Wrong CI-V USB Port setting — must be Link to [CI-V], not [REMOTE] 3. Serial port busy — another app (wfview, hamlib, etc.) is using the serial device

Fix: 1. Check radio baud rate: Menu → Set → Connectors → CI-V → CI-V USB Baud Rate 2. Check CI-V USB Port setting (see "Critical Setup Step" above) 3. Close other apps using the serial port:

# Check what's using the port (macOS)
lsof | grep cu.usbserial

Wrong USB audio device selected with two radios connected

Symptom: Audio from the wrong radio when IC-7300 and IC-7610 are both connected via USB.

Cause: Two identical "USB Audio CODEC" devices appear in the system; name-based selection picks the first one regardless of which radio's serial port is in use.

Fix: On macOS, this is resolved automatically — the library reads USB hub topology via IORegistry and selects the audio device on the same USB hub as the serial CI-V port. Check the log for a line like:

usb-audio-resolve: /dev/cu.usbserial-201410 → prefix 0x2014 → RX device [2], TX device [3]

If you see "topology resolution not supported" (Linux), specify device indices explicitly:

icom-lan --backend serial --rx-device 2 --tx-device 3 status

Use icom-lan --list-audio-devices to find the correct indices.

Audio TX not working

Cause: Radio USB Audio TX not enabled.

Fix: 1. Menu → Set → Connectors → USB Audio → USB Audio TXEnabled 2. Check PTT is active before transmitting audio 3. Verify --tx-device matches the USB audio device name

Environment Variables

For convenience, set these in your shell profile:

# ~/.bashrc or ~/.zshrc
export ICOM_SERIAL_DEVICE=/dev/cu.usbserial-111120
export ICOM_SERIAL_BAUDRATE=115200
export ICOM_USB_RX_DEVICE="IC-7610 USB Audio"
export ICOM_USB_TX_DEVICE="IC-7610 USB Audio"

# Then simply:
icom-lan --backend serial status

Migration from LAN to Serial

If you're currently using the LAN backend and want to switch to serial:

  1. No code changes needed — use create_radio(config) factory with typed config:

    # Before (LAN)
    from icom_lan.backends.config import LanBackendConfig
    config = LanBackendConfig(host="192.168.1.100", ...)
    
    # After (Serial)
    from icom_lan.backends.config import SerialBackendConfig
    config = SerialBackendConfig(device="/dev/cu.usbserial-111120", ...)
    
    # Same factory call
    radio = create_radio(config)
    

  2. CLI: add --backend serial flag:

    # Before (LAN, default)
    icom-lan status
    
    # After (Serial)
    icom-lan --backend serial status
    

  3. Capability check: scope/waterfall requires ≥115200 baud (see table above)

  4. Audio device selection: LAN uses Opus/PCM over UDP, serial uses USB audio devices (explicit device names or auto-detect)

Next Steps


Hardware validated: 2026-03-06, IC-7610 over USB on macOS (Darwin arm64), Python 3.11.14, pytest 9.0.2 (issue #146)