Vendor protocol reference

Note

This page covers boxes in their factory-default state, speaking the vendor’s own protocol rather than Modbus.

What we sell is Modbus — see that page. This page is for units obtained elsewhere, or when the Modbus page gets no reply.

Port settings

Value

Baud rate

9600

Data · stop · parity

8 · 1 · none

Works over

RS232 and RS485

Frame format

Always 7 bytes, both directions.

  0     1      2        3       4       5     6
┌────┬──────┬────────┬───────┬────────┬─────┬────┐
│ 01 │  51  │ folder │  00   │ volume │ XOR │ 02 │
└────┴──────┴────────┴───────┴────────┴─────┴────┘
  │     │        │        │        │      │     │
  │     │        │        │        │      │     └── end byte, fixed
  │     │        │        │        │      └──────── XOR of bytes 0–4
  │     │        │        │        └─────────────── 00–1C, i.e. 0–28
  │     │        │        └──────────────────────── unused, send 00
  │     │        └───────────────────────────────── 00–FF, i.e. 0–255
  │     └────────────────────────────────────────── 51 command · 52 reply
  └──────────────────────────────────────────────── start byte, fixed

Important

Folder and volume go in one frame, unlike Modbus which needs two. Shorter and faster.

Warning

🔴 There is no read command. This protocol is write-only. Your program must remember what it set.

After a restart there is no way to know what the box is playing, so send a stop as the first thing your program does, to reach a known state.

XOR

XOR bytes 0 through 4 together.

def xor8(data):
    v = 0
    for b in data:
        v ^= b
    return v

def frame(folder, volume):
    body = [0x01, 0x51, folder & 0xFF, 0x00, volume & 0xFF]
    return bytes(body + [xor8(body), 0x02])

Tip

XOR is much weaker than CRC16 — swap two bytes and the XOR is unchanged, so the error passes undetected.

On long or electrically noisy runs Modbus is more trustworthy. Order the Modbus variant if you have the choice.

Example frames

Generated by code, not typed. The first pair matches the manual byte for byte.

Action

Send

Box replies

Play AW002 at 28

01 51 02 00 1C 4E 02

01 52 02 00 1C 4D 02

Play AW005 at 28

01 51 05 00 1C 49 02

01 52 05 00 1C 4A 02

Stop

01 51 00 00 1C 4C 02

01 52 00 00 1C 4F 02

🔴 The echo trap

Danger

Command and reply differ by a single byte — 51 versus 52.

Some RS485 wiring echoes what you transmit. Code that only checks “did anything come back?” will treat its own frame as a reply, even with no speaker connected.

Check that byte 1 is 52. If it is 51, that is an echo, not a reply.

def is_real_reply(frame, sent_folder):
    if len(frame) != 7:
        return False
    if frame[1] != 0x52:          # 51 is our own command, echoed back
        return False
    if frame[2] != sent_folder:   # must match what we asked for
        return False
    return frame[5] == xor8(frame[:5])

Check before connecting hardware

Print the frames your code builds and compare them with the table above.

Warning

The wrong protocol gives total silence, with no error. If frames from this page get no reply, try the Modbus page before investigating the wiring.