Protocol Deep Dive - icom-lan
Description: Python library for controlling Icom transceivers over LAN

Skip to content

# Protocol Deep Dive

The Icom LAN protocol is a proprietary UDP-based protocol used by Icom transceivers for remote control over Ethernet/WiFi. It was reverse-engineered by the wfview project.

## Packet Structure

Every UDP packet starts with a fixed 16-byte header:

    Offset  Size  Endian  Field
    0x00    4     LE      Packet length (total, including header)
    0x04    2     LE      Packet type
    0x06    2     LE      Sequence number
    0x08    4     LE      Sender ID
    0x0C    4     LE      Receiver ID

## Packet Types

Type | Name | Size | Description
--- | --- | --- | ---
`0x00` | DATA | Variable | Carries CI-V commands, audio, etc.
`0x01` | CONTROL | 0x10+ | Retransmit requests
`0x03` | ARE_YOU_THERE | 0x10 | Discovery request (broadcast)
`0x04` | I_AM_HERE | 0x10 | Discovery response
`0x05` | DISCONNECT | 0x10 | Disconnect notification
`0x06` | ARE_YOU_READY | 0x10 | Ready handshake
`0x07` | PING | 0x15 | Keep-alive ping/pong

## Port Architecture

Port | Name | Purpose
--- | --- | ---
50001 | Control | Authentication, session management, keep-alive
50002 | CI-V | CI-V serial command exchange
50003 | Audio | Opus audio streaming

The CI-V and audio port numbers are negotiated — they're reported in the status packet (0x50 bytes) after the conninfo exchange. However, in practice they are almost always control_port+1 and control_port+2.

## Discovery

    Client → Radio:  ARE_YOU_THERE (0x03), 16 bytes
    Radio → Client:  I_AM_HERE (0x04), 16 bytes, includes radio's sender_id
    Client → Radio:  ARE_YOU_READY (0x06), 16 bytes
    Radio → Client:  ARE_YOU_READY (0x06), 16 bytes (echo/ack)

This can also be broadcast (UDP broadcast to port 50001) for autodiscovery.

## Authentication

### Login Packet (0x80 bytes)

    Offset  Size  Field
    0x00    4     Packet length (0x80)
    0x04    2     Type (0x00 = DATA)
    0x06    2     Sequence
    0x08    4     Sender ID
    0x0C    4     Receiver ID
    0x10    4     Payload size (0x70, big-endian)
    0x14    1     Request/reply (0x01)
    0x15    1     Request type (0x00 = login)
    0x16    2     Inner sequence (big-endian)
    0x1A    2     Token request ID
    0x40    16    Encoded username
    0x50    16    Encoded password
    0x60    16    Computer name (ASCII)

### Credential Encoding

Icom uses a position-dependent substitution cipher:

1. For each character at position `i`: compute index = `ord(char) + i`
2. If index > 126: index = `32 + (index % 127)`
3. Look up in the substitution table (128-byte constant)

This is obfuscation, not encryption. The substitution table is publicly known.

### Auth Response (0x60 bytes)

    Offset  Size  Field
    0x1A    2     Token request ID (echoed)
    0x1C    4     Session token
    0x30    4     Error code (0 = success, 0xFEFFFFFF/0xFFFFFFFF = failure)
    0x40    16    Connection type string

### Token Ack (0x40 bytes)

    Offset  Size  Endian  Field
    0x10    4     BE      Payload size
    0x14    1             Request/reply (0x01)
    0x15    1             Request type (0x02 = token ack)
    0x16    2     BE      Inner sequence
    0x1A    2     LE      Token request ID
    0x1C    4     LE      Token
    0x24    2     BE      Reset capability (0x0798)

## Conninfo Exchange

### Conninfo Packet (0x90 bytes)

Sent by both radio and client. Contains:

    Offset  Size  Field
    0x15    1     Request type (0x03 = conninfo)
    0x1C    4     Token
    0x20    16    GUID / MAC area
    0x40    32    Device name
    0x60    16    Encoded username
    0x70    1     RX enable
    0x71    1     TX enable
    0x72    1     RX codec
    0x73    1     TX codec
    0x74    4     RX sample rate (BE)
    0x78    4     TX sample rate (BE)
    0x7C    4     CI-V local port (BE)
    0x80    4     Audio local port (BE)
    0x84    4     TX buffer latency ms (BE)

Critical: The client must echo the radio's GUID (0x20–0x2F from the radio's conninfo) in its own conninfo. Otherwise, the status packet will report CI-V port = 0.

### Status Packet (0x50 bytes)

Sent by the radio after receiving the client's conninfo:

    Offset  Size  Endian  Field
    0x30    4     LE      Error code
    0x40    1             Disconnect flag
    0x42    2     BE      CI-V port
    0x46    2     BE      Audio port

## CI-V Over UDP

### CI-V Data Packet

CI-V frames are wrapped in UDP data packets on port 50002:

    Offset  Size  Field
    0x00    4     Total packet length
    0x04    2     Type (0x00 = DATA)
    0x06    2     Sequence
    0x08    4     Sender ID
    0x0C    4     Receiver ID
    0x10    1     Reply marker (0xC1 for CI-V data)
    0x11    2     CI-V frame length (LE)
    0x13    2     Send sequence (BE)
    0x15    ...   CI-V frame data

The CI-V header is 0x15 bytes (not 0x10 — there's a 5-byte sub-header after the standard packet header).

### OpenClose Packet (0x16 bytes)

Sent to start/stop the CI-V data stream:

    Offset  Size  Field
    0x10    2     Data (0x01C0)
    0x13    2     Send sequence (BE)
    0x15    1     Magic (0x04 = open, 0x00 = close)

## Keep-Alive

### Ping Packet (0x15 bytes)

    Offset  Size  Field
    0x00    4     Length (0x15)
    0x04    2     Type (0x07 = PING)
    0x06    2     Ping sequence
    0x08    4     Sender ID
    0x0C    4     Receiver ID
    0x10    1     Flag (0x00 = request, 0x01 = reply)
    0x11    4     Timestamp (LE, milliseconds)

Pings are sent every 500ms. The radio drops connections that stop pinging.

## Retransmit

If a sequence gap is detected, the receiver sends a retransmit request:

- Single missing: 0x10-byte control packet with type=0x01 and the missing seq in the seq field
- Multiple missing: 0x10+ byte packet with type=0x01, followed by pairs of (seq, seq) for each missing packet

## CI-V Frame Format

Standard Icom CI-V serial protocol, encapsulated in UDP:

    FE FE <to> <from> <cmd> [<sub>] [<data>...] FD

- `FE FE` — preamble
- `<to>` — destination address (radio = 0x98 for IC-7610)
- `<from>` — source address (controller = 0xE0)
- `<cmd>` — command byte
- `<sub>` — sub-command (optional, depends on command)
- `<data>` — payload (variable length)
- `FD` — terminator

### BCD Frequency Encoding

Frequencies are encoded as 5-byte little-endian BCD:

    14,074,000 Hz → 00 40 07 14 00

    Byte 0: 00 → digits 0,0 (1 Hz, 10 Hz)
    Byte 1: 40 → digits 4,0 (100 Hz, 1 kHz)
    Byte 2: 07 → digits 0,7 (10 kHz, 100 kHz)
    Byte 3: 14 → digits 1,4 (1 MHz, 10 MHz)
    Byte 4: 00 → digits 0,0 (100 MHz, 1 GHz)

## References

- wfview — open-source Icom remote control software (GPLv3)
- `packettypes.h` — packet structure definitions
- `icomudpbase.cpp` — base UDP handling
- `icomudphandler.cpp` — authentication and handshake
- `icomudpcivdata.cpp` — CI-V data flow
- `icomcommander.cpp` — CI-V command encoding

## Legal Note

Protocol reverse engineering for interoperability is protected by:

- EU Directive 2009/24/EC (Software Directive, Article 6)
- US DMCA interoperability exception (17 U.S.C. § 1201(f))

This library is a clean-room implementation based on protocol understanding, not a derivative of wfview's GPLv3 code.