anytone778

This is the notes I made reverse engineering the serial commuication between the vendor software and the AnyTone 778UV radio. Driver support in chirp as of build #872 on 20200521

TODO

how to capture with wireshark and dissect

programming flow

n.b. tx and rx connected together in the serial adaptor - the host sees everything it sends on rx, that's not shown here

Memory map

    +------+---------------------------+
    | addr | Contents                  |
    +------+---------------------------+
    | 0000 | Memory 0 - bytes  0 - 15  |
    | 0010 | Memory 0 - bytes 16 - 32  |
    +------+---------------------------+
    | 0020 | Memory 1                  |
    | 0030 |                           |
    +------+---------------------------+
    | ...  |         ...               |
    +------+---------------------------+
    | 18e0 | Memory 199                |
    | 18f0 | 0x18e0/32 = 199           |
    +------+---------------------------+
    | 1900 | VF01                      |
    | ...  | just another memory       |
    |      | don't set name - it       |
    | 1910 | overwrites freq disp.     |
    +------+---------------------------+
    | 1920 | VF02                      |
    | ...  | just another memory       |
    |      | don't set name - it       |
    | 1930 | overwrites freq disp.     |
    +------+---------------------------+
    | 1940 | bit field of mem occupied |
    | 1950 |                           |
    +------+---------------------------+
    | 1960 | bit field of scan add     |
    | 1970 |                           |
    +------+---------------------------+
    | 1980 | switch on message is here |
    |      | bytes  0-6                |
    +------+---------------------------+
    | 1990 | this block is all 0xff    |
    | ...  | from the factory          |
    | 1ac0 |                           |
    +------+---------------------------+
    | 1ad0 | unknown                   |
    | ...  | there's some stuff here   |
    | 1c70 | but don't know what it is |
    +------+---------------------------+
    | 1c80 | unknown                   |
    | ...  | there's some stuff here   |
    | 1c60 | but don't know what it is |
    +------+---------------------------+
    | 1c70 | this block is all 0x00    |
    | ...  | from the factory          |
    | 30d0 |                           |
    +------+---------------------------+
    | 30e0 | unknown                   |
    | ...  | there's some stuff here   |
    | 3270 | but don't know what it is |
    +------+---------------------------+
    | 3280 | TRF                       |
    | 3290 | just another memory       |
    +------+---------------------------+
    | 320a | Don't know what's here,   |
    |      | and the vendor s/w doesn't|
    | 3b00 | read it. Here be dragons? |
    +------+---------------------------+
    | 3b10 | Don't know what this is   |
    | ...  | vendor s/w reads it before|
    |      | writing memories.  Looks  |
    | 3b20 | like maybe a bitfield.    |
    +------+---------------------------+

Spot addresses:

    +--------+--------+---------------------------------------+
    |  Addr  | Length | Description                           |
    +--------+--------+---------------------------------------+
    | 0x320b |    1   | Active VFO 00=A, 01=B                 |
    +--------+--------+---------------------------------------+
    | 0x3260 |    1   | Current channel VF0 A                 |
    +--------+--------+---------------------------------------+
    | 0x3267 |    1   | current scan channel                  |
    +--------+--------+---------------------------------------+
    | 0x3268 |    1   |  bit 3 set for scan active            |
    +--------+--------+---------------------------------------+
    | 0x326d |    1   | Band Settings - see band limits table |
    +--------+--------+---------------------------------------+

Messages and Flow

Enter Programming Mode and Identify

       host                         radio
       |                            |
       +------Program-------------->|   Host will send PROGRAM three times
       |                            |   before giving up.
       |<-----QX\x06----------------+
       |                            |
       +------\x02----------------->|  Check radio version
       |                            |
       |<-----..AT778UV.V200.....---+  Model and version
       |                            |

The identify message has the following format:

    +-------+--------+---------+----------------------+
    | Start | Length | Name    | Desc                 |
    | Byte  | Byte   |         |                      |
    +-------+--------+---------+----------------------+
    | 0x00  |    1   | Header  | Always 'I' 0x49      |
    +-------+--------+---------+----------------------+
    | 0x01  |    7   | Model   | C-string, model name |
    +-------+--------+---------+----------------------+
    | 0x08  |    1   | Band    | Always 0x00-0x02     |
    +-------+--------+---------+----------------------+
    | 0x09  |    6   | Version | C-string, version    |
    +-------+--------+---------+----------------------+

Band settings limit the transmit/receive frequency ranges

    +------+---------------------------------+
    | Band |       Band Limits [Hz]          |
    +------+----------------+----------------+
    | Byte |       VHF      |       UHF      |
    +------+----------------+----------------+
    | 0x00 | (144e6, 148e6) | (430e6, 440e6) |
    | 0x01 | (134e6, 174e6) | (400e6, 490e6) |
    | 0x02 | (144e6, 146e6) | (430e6, 440e6) |
    +------+----------------+----------------+

Known radio models and versions:

    +---------+------------+--------------+----------------+
    | Vendor  | Model Name | Model String | Version String |
    +---------+------------+--------------+----------------+
    | AnyTone |   778UV    | AT778UV      | V200           |
    | Retevis |    RT95    | RT95         | V100           |
    | CRT     | Micron UV  | MICRON       | V100           |
    | Midland | DBR2500    | DBR2500      | V100           |
    +---------+------------+--------------+----------------+

Model and version can also be found in the first few bytes of a save file from the vendor software.

Exit Programming Mode

       host                         radio
       |                            |
       +------END------------------>|
       |                            |
       |<-----\x06------------------+
       |                            |

Programming Flow - Read Device Memory

       host                         radio
       |                            |
     +--------------------------------+
     | Enter programming mode         |
     | and identify                   |
     +--------------------------------+
       |                            |
       +----Read 0x0000 16 bytes--->| Request the first half of memory 0
       |                            |
       |<---Write 0x0000 16 bytes---+ First half of memory 0
       |                            |
       +----Read 0x0010 16 bytes--->| Request the second half of memory 0
       |                            |
       |<---Write 0x0010 16 bytes---+ Second half of memory 0
       |                            |
                    ...               Repeat for addresses 0x20-0x3aff
       |                            |
       +----Read 0x3b00 16 bytes--->| Read last address in memory map
       |                            |
       |<---Write 0x0000 16 bytes---+ 
       |                            |
     +--------------------------------+
     |  Exit programming mode         |
     |                                |
     +--------------------------------+
       |                            |

Programming Flow - Write Device Memory

       host                         radio
       |                            |
     +--------------------------------+
     | Enter programming mode         |
     | and identify                   |
     +--------------------------------+
       |                            |
       +----Read 0x3b10 16 bytes--->|
       |                            |
       |<---Write 0x3b10 16 bytes---+  Data is 02ffffff 00000000 00000000 00000000
       |                            |  but I don't know what it means yet.
       |                            |  Also note that it's off the bottom of
       |                            |  the memory map. XXX 32 bytes
       |                            |
       |                            |
       +---Write 0x0000 16 bytes--->| Write the first half of memory 0
       |                            |
       |<----------\x06-------------+ Ack
       |                            |
       +---Write 0x0010 16 bytes--->| Write the second half of memory 0
       |                            |
       |<----------\x06-------------+ Ack
       |                            |
                    ...               Repeat for addresses 0x20-0x3aff
       |                            |
       +----Write 0x3b00 16 bytes-->| Write last address in memory map
       |                            |
       |<----------\x06-------------+ Ack
       |                            |
     +--------------------------------+
     |  Exit programming mode         |
     |                                |
     +--------------------------------+
       |                            |

Note however, that it's not necessary to read or write the whole memory, the radio seems to support arbitary length reads at any address; and definitely supports partial writes of radio memories - e.g. writing only memory 0.

Also note, that per-memory scan status, and a memory occupied flag, are stored in bit fields and may also need to be updated (see addresses 0x1940 and 0x1960 in the memory map). These bitfields are indexed by the memory index 0-199 starting with the bitfield address. E.g. memory zero is at bit 7 of byte 0; memory 10 at bit 3 of byte 1 etc.

Read and Write Messages

There's a really simple format to the messages sent and received by the radio in program mode, there are two message types: read and write. If the PC sends a read then it's a request with no data, the radio responds with a write message of the requested data. If the PC sends a write message then it includes the data, and the radio sends only an ACK, 0x06 in reply. If, for example, the checksum of a write message is wrong the radio can reply with 0x0a - NACK.

Message fields:

+-------+-------------------------------------+
| Start | Length | Content                    | 
| Byte  | Bytes  |                            | 
+-------+--------+----------------------------+
| 0x00  |    1   | Message Type:              |
|       |        | * 0x57 ('W') Write message |
|       |        | * 0x52 ('R') Read message  |
+-------+--------+----------------------------+
| 0x01  |    2   | Memory Read/Write Address  |
+-------+--------+----------------------------+
| 0x03  |    1   | Data Length (Bytes)        |
|       |        | typically (always?) 0x10   |
+-------+--------+----------------------------+
| 0x04  | Length | Data Bytes to read/write   |
+-------+--------+----------------------------+
| -0x02 |    1   | [checksum](##Checksum)     |
|       |        | over bytes 0x01 to -0x03   |
+-------+--------+----------------------------+
| -0x01 |    1   | End of message, always     |
|       |        | 0x06                       |
+-------+--------+----------------------------+

Message format, and a typical message writing the first 16 bytes of memory 49. This is also the format of the response from the radio if the host requests to read memory 49.

+------+---------+--------+-------------------------------------+----------+-----+
| Type | Address | Length | Data                                | Checksum | EoM |
+------+---------+--------+-------------------------------------+----------+-----+
|   57 |    0620 |    10  | 14500000 00100000 00010004 33001100 |    f3    | 06  |
+------+---------+--------+-------------------------------------+----------+-----+

Message format, and a typical message from the host asking to read the first 16 bytes of memory 49.

+------+---------+--------+----------+-----+
| Type | Address | Length | Checksum | EoM |
+------+---------+--------+----------+-----+
|   52 |    0620 |    10  |    f3    | 06  |
+------+---------+--------+----------+-----+

Memory Format

+------+---+--------------+----------------------------+
| 0x00 | 4 | frequency    | big endian bcd             |
|      |   |              | frequency 10s of Hz        |
+------+---+--------------+----------------------------+
| 0x04 | 4 | offset       | big endian bcd             |
|      |   |              | frequency 10s of Hz        |
+------+---+--------------+----------------------------+
| 0x08 | 1 | unknown1     | default 00                 |
+------+---+--------------+----------------------------+
| 0x09 | 1 |              | OR of tx power and split   |
|      |   | tx power     | tx power high : 00001000   |
|      |   |              | tx power med  : 00000100   |
|      |   | and          | tx power low  : 00000000   |
|      |   |              |                            |
|      |   | split        | split +ve : 00000001       |
|      |   |              | split -ve : 00000010       |
|      |   | flags1       | no split : 00000000        |
|      |   |              |                            |
|      |   |              | talk around: 0x80          |
|      |   |              | scramble : 0x40            |
+------+---+--------------+----------------------------+
| 0x0a | 1 | chan spacing | bit:                       |
|      |   |              |   7 -                      |
|      |   | and          |   6                        |
|      |   |              |   5                        |
|      |   | params       |   4                        |
|      |   |              |   3 - channel width 3,2    |
|      |   | flags 2      |   2 - 10=25kHz,01=20kHz,   |
|      |   |              |       00=12.5kHz           | 
|      |   |              |   1 - reverse              |
|      |   |              |   0 - tx off               |
+------+---+--------------+----------------------------+
| 0x0b | 1 | ctcss enable | bit:                       |
|      |   |              |   3 - dcs dec enable       |
|      |   |              |   2 - ctcss dec enable     |
|      |   | flags3       |   1 - dcs enc enable       |
|      |   |              |   0 - ctcss enc enable     |
+------+---+--------------+----------------------------+
| 0x0c | 1 |ctcss dec tone| ctcss enumeration          |
+------+---+--------------+----------------------------+
| 0x0d | 1 |ctcss enc tone| ctcss enumeration          |
+------+---+--------------+----------------------------+
| 0x0e | 1 | dcs dec code | =int(tone, 8) bits 7:0     |
+------+---+--------------+----------------------------+
| 0x0f | 1 | unknown3     | default 0x00               |
|      |   |              |   1 - DCS dec invert en    |
|      |   |              |   0 - DCS dec code bit 8   |
+------+---+--------------+----------------------------+
| 0x10 | 1 | dcs enc code | =int(tone, 8) bits 7:0     |
+------+---+--------------+----------------------------+
| 0x11 | 1 | unknown5     | default 0x00               |
|      |   |              |   1 - DCS enc invert en    |
|      |   |              |   0 - DCS enc code bit 8   |
+------+---+--------------+----------------------------+
| 0x12 | 1 | params       | bit                        |
|      |   |              | 1,0 - busy chn lockout     |
|      |   |              |       10 = 'busy'          |
|      |   | flags4       |       01 = 'repeater'      |
|      |   |              |       00 = 'off'           |
+------+---+--------------+----------------------------+
| 0x13 | 1 | unknown6     | default 0x00               |
+------+---+--------------+----------------------------+
| 0x14 | 1 | params       | bit                        |
|      |   | flags5       |   0 - ctcss squelch enable |
+------+---+--------------+----------------------------+
| 0x15 | 1 | unknown7     | default 0x00               |
+------+---+--------------+----------------------------+
| 0x16 | 1 | unknown8     | default 0x00               |
+------+---+--------------+----------------------------+
| 0x17 | 1 | unknown9     | default 0x00               |
+------+---+--------------+----------------------------+
| 0x18 | 1 | unknown10    | default 0x00               |
+------+---+--------------+----------------------------+
| 0x19 | 5 | name string  |                            |
+------+---+--------------+----------------------------+
| 0x1e | 1 | custom ctcss | custom ctcss value as      |
|      |   | low byte     |  Hz * 10, litte endian     |
+------+---+--------------+ e.g. 0xae08 = 222.2Hz      |
| 0x1f | 1 | custom ctcss | see 'define' value in      |
|      |   | high byte    | ctcss enumeration          |
|      |   |              |                            |
+------+---+--------------+----------------------------+

ctcss enumeration:

+------+-----------++------+------------++------+-----------++------+------------+
| Val  | Tone [Hz] || Val  | Tone [Hz]  || Val  | Tone [Hz] || Val  | Tone [Hz]  |
+------+-----------++------+------------++------+-----------++------+------------+
| 0x00 | 62.5      || 0x0d | 100.0      || 0x1a | 156.7     || 0x27 | 196.6      |
| 0x01 | 67.0      || 0x0e | 103.5      || 0x1b | 159.8     || 0x28 | 199.5      |
| 0x02 | 69.3      || 0x0f | 107.2      || 0x1c | 162.2     || 0x29 | 203.5      |
| 0x03 | 71.9      || 0x10 | 110.9      || 0x1d | 165.5     || 0x2a | 206.5      |
| 0x04 | 74.4      || 0x11 | 114.8      || 0x1e | 167.9     || 0x2b | 210.7      |
| 0x05 | 77.0      || 0x12 | 118.8      || 0x1f | 171.3     || 0x2c | 218.1      |
| 0x06 | 79.7      || 0x13 | 123.0      || 0x20 | 173.8     || 0x2d | 225.7      |
| 0x07 | 82.5      || 0x14 | 127.3      || 0x21 | 177.3     || 0x2e | 229.1      |
| 0x08 | 85.4      || 0x15 | 131.8      || 0x22 | 179.9     || 0x2f | 233.6      |
| 0x09 | 88.5      || 0x16 | 136.5      || 0x23 | 183.5     || 0x30 | 241.8      |
| 0x0a | 91.5      || 0x17 | 141.3      || 0x24 | 186.2     || 0x31 | 250.3      |
| 0x0b | 94.8      || 0x18 | 146.2      || 0x25 | 189.9     || 0x32 | 254.1      |
| 0x0c | 97.4      || 0x19 | 151.4      || 0x26 | 192.8     || 0x33 | define     |
+------+-----------++------+------------++------+-----------++------+------------+

Checksum

def checksum(message_bytes):
        mask = 0xFF
        checksum = 0
        for b in message_bytes:
                checksum = (checksum + b) & mask
        return checksum

#radio