RS422 Device Bus Protocol

[ introduction | generic protocol | devices | controller protocol ]


1. Introduction

This document describes the protocol used on the RS422 bus that connects various devices around the house. It also lists the (known) devices and their supported commands.


2. Generic Protocol

The following figure shows the general structure of a packet on the RS422 bus:

general packet structure

The first byte is the length of the packet, inclusive of itself and the checksum byte at the end of the packet. The checksum is the sum (modulo 256) of all the preceeding bytes in the packet.

The LUN is the device logical unit number (abbreviation shamelessly lifted from SCSI terminology). There are two special LUN values: 0x00 is the LUN of the bus-master for a particular bus (typically the PC via RS232/RS422 bridge); 0xff is the broadcast LUN.

Broadcast packets contain the generic protocol described here. Packets directed at a specific LUN are considered to be device-specific. Packets sent to the bus-master LUN are nearly always reply packets, from either the generic or device-specific protocols.

Generic protocol packets have the following structure:

generic protocol packet structure

The following table lists the currently supported values for cmd:

0x00device discovery, used to find new devices
0x01device soft reset, used to reset devices on the bus
0x80device address assign, used to assign LUN values
0x81device ping, used to ping devices
0x82device identity, used to retrieve the identity of a device
0x83device raw command, used to control a device without a LUN

To make device programming slightly simpler, any generic command that has the high-bit set requires a device-ID match. That device-ID is contained in the 4 bytes following the command byte.

2.0.1. Packet Padding

As of December 2009, packets may be padded before and after with 0x00 bytes.

2.1. Device Discovery

The device-discover command (0x00) is used to locate new devices (or existing devices without a LUN set). The packet sent has the following structure:

generic protocol discovery packet

Devices respond after a reasonably variable delay, based on their device-ID). The response sent by a device is similar to that of the device-identify command; in this case the response has the structure:

generic protocol discovery response packet

2.2. Device Soft-Reset

The soft-reset command (0x01) is used to reset all devices on the bus. This is only good for devices that are still responding to the system, however. The packet sent has the following structure:

generic protocol soft-reset packet

2.3. Device Address-Assign

The device-address-assign command (0x80) is used to assign LUN values to devices. The packet sent has the following structure:

generic protocol address-assignment packet

The device-ID must match the device's own ID. The response sent by a device is largely the same, except that the (first) LUN value will be 0x00 and not 0xff. The response is largely an acknowledgement to ensure that a device's LUN has indeed been changed.

2.4. Device Ping

The device-ping command (0x81) is used to test the existence of a device. The packet sent has the following structure:

generic protocol ping packet

The device-ID must match the device's own ID. The response sent by a device is largely the same, except that the LUN value will be 0x00 and not 0xff.

2.5. Device Identity

The device-identify command (0x82) is used to retrieve the device identity for a device:

generic protocol device-id packet

The response from a device has the following format:

generic protocol device-id response packet

2.6. Device Raw Command

The device-raw-command command (0x83) is used to control a device that does not have a LUN set:

generic protocol device control packet


3. Devices

Various supported devices and IDs:

0x80010000test unit
0x80020001temperature sensor
0x80020002temperature sensor
0x80020003temperature sensor
0x80020004temperature sensor
0x80020005temperature sensor
0x80030000remote IR remote
0x80040000AVR programmer
0x80050001pulse sensor (electicity and gas monitoring)
0x80060001general I/O device
0x800700014-character display
0x800800018-character display
0x80090001bus power manager
0x8010000116-character display
0x80110001transmitter test device
0x80120001train controller device
0x80140001RF transceiver device
0x801500019V H-bridge device
0x80180001remote train

3.1. Test Unit Commands

CommandResponseDescription
00reset counter
01xxset counter to xx
02enable periodic messages
03disable periodic messages

3.2. Temperature Sensor Commands

CommandResponseDescription
00set normal operation
01calibrate high (30 ^C) value
02calibrate low (0 ^C) value
03nn03xxxxyyread temperature and aux counters, originating LUN in nn, temperature count in xxxx, auxillary count in yy
04nn04set normal operation and acknowledge, originating LUN in nn
05nn05calibrate high (30 ^C) value and acknowledge, originating LUN in nn
06nn06calibrate low (0 ^C) value and acknowledge, originating LUN in nn

3.3. Remote IR Remote Commands

CommandResponseDescription
00xxyytransmit RC5 codeset xx (0-31), command yy (0-63)
01xxset RC5 transmit frequency constant to xx (default is 6f)
02nn02xxget RC5 transmit frequency constant, originating LUN in nn, frequency value in xx

3.4. AVR Programmer Commands

CommandResponseDescription
00initialise programmer
01shut-down programmer
03nn03xxyyread aux counters, originating LUN in nn, aux1 count in xx, aux2 count in yy
to be completed

3.5. Pulse Sensor Commands

CommandResponseDescription
00clear counters
01nn01xxxxxxyyyyyyread and clear counters, originating LUN in nn, counter 1 value in xxxxxx and counter 2 value in yyyyyy

3.6. General I/O Device Commands

CommandResponseDescription
00xxoutput xx on the 8-bit port
01nn01xxread 8-bit port data, originating LUN in nn, data in xx
to be completed

3.7. 4-character Display Commands

CommandResponseDescription
00clear display
01aabbcc...set display text (max. 44 chars)
02enable scrolling
03disable scrolling
04xxset scroll speed to xx
05xxset display offset to xx

3.8. 8-character Display Commands

CommandResponseDescription
00clear display
01aabbcc...set display text (max. 44 chars)
02enable scrolling
03disable scrolling
04xxset scroll speed to xx
05xxset display offset to xx

3.9. Bus Power Manager Commands

CommandResponseDescription
0000ssssget device status
010101turn on socket A
010201turn off socket A
010301disable socket A cold-start
0104xx01enable socket A cold-start after (15 * xx) seconds
020102turn on socket B
020202turn off socket B
020302disable socket B cold-start
0204xx02enable socket B cold-start after (15 * xx) seconds
0301power-cycle bus
0304xx03enable bus cold-start after xx seconds

3.10. 16-character Display Commands

The ATMega8 device that this currently runs on has plenty of internal memory (1k SRAM). The maximum supported message size is around 496 bytes. To get the whole message in, it must be uploaded in 64-byte chunks, which makes the protocol handling a little odd.

CommandResponseDescription
00clear display
01nn aabbcc...set text segment nn (0-7) to text aabbcc.. (max 64 chars at a time)
02enable scrolling
03disable scrolling
04xxset scroll speed to xx
05xxset display offset to xx
06xxset high display offset to xx
07hhllroll display to offset hh ll
08xxset scroll start offset to xx (0-15)

3.11. Transmitter Test Device Commands

This device doesn't respond to any particular commands: it just transmits a general message to the host (about once per second). It will respond correctly to generic commands, to have its LUN set, etc.

3.12. Train Controller Commands

This device has (in theory) 4x 8-bit output ports and 4x 8-bit input ports, wired up to various sensors and indicators.

CommandResponseDescription
00disable input polling
01enable input polling
02 xxset polling speed to xx
03 pp vvset output port pp to vv
04 pp vv ww hhll jjkktimed port output, after hhll ms set port pp to vv, hold for jjkk ms then set to ww
05 ppppvvccread input port pp, value returned in vv, changed bits indicated in cc
06cancel any in-progress timed port output
07 pp vv ww mm hhll jjkkmasked timed port output, after hhll ms set port pp bits mm to vv, hold for jjkk ms then set to ww mask mm; unmasked bits are not affected
08aabbccdd vvwwxxyyread all input ports (reported in aabbccdd), without clearing change bits (reported in vvwwxxyy)
09clear all input port change bits
0a pp vv mmset output port pp to vv with mask mm

3.13. RF Transceiver Device

This device is used to send packets over RF (433 MHz) using an RF650 (RF-solutions). The RF packet size is essentially fixed, but can be configured in software here for different modules. Receiving RF650s automatically pad output packets to the appropriate size.

CommandResponseDescription
00 xx yyset RF650 transmit (xx) and receive (yy) packet sizes. Note that this does not reconfigure the RF650.
01 aabbcc...transmit the specified packet of data aabbcc.. over the RF link.

3.14. 9V H-Bridge Device

This device is used to control a number of 9V H-bridge devices. Bridge control bits/values are 00 = disable (floating/coast), 01 = enable forwards, 02 = enable reverse, 03 = enable brake. Bridge identifiers (bit mask) are 01 = A, 02 = B, 04 = C, 08 = D, 10 = E, 20 = F, 40 = G, 80 = H.

CommandResponseDescription
00disable all bridges.
01 xx aaset bridge(s) xx to state aa.
02 xx aa jjkk bbset bridge(s) xx to state aa for jjkk milliseconds then to state bb.
03 xx aa jjkk bb llmmpulse mode: set bridge(s) xx to state aa for jjkk milliseconds then to state bb for llmm milliseconds, then loop (until changed).

3.15. Remove Train Device

This device is not connected up to the bus directly, but can be accessed via the RF transceiver. A 9-byte packet is sent to the device, with the following format:

Byte012345678
Desc.ID byte 0ID byte 1ID byte 2ID byte 30xfbbridge 1 modebridge 1 powerbridge 2 modebridge 2 power

The 4 ID bytes must match the ones coded into the device (e.g. 0x80, 0x18, 0x00, 0x01), which allows multiple devices to be signalled but only one to respond.

The bridge mode is split into 2 nibbles. The low-order nibble specifies how current flows through the bridge: 0x00 = float; 0x01 = forward; 0x02 = reverse; 0x03 = brake. The high-order nibble controls higher-level properties: 0x10 = timed output (for 1.5s); 0x20 = ramp power up/down until target is reached.

The bridge power controls chopping. 0x00 means no power, 0x7f is half power (50% duty cycle), and 0xff is full power.


4. Controller Protocol

There is a piece of software (sermon2) which controls access to the RS422 bus via a RS232 port. The various client utilities connect to this via a Unix socket (/tmp/sermon2 by default) or over an IPv4 socket (on TCP port 3005 by default). A small library of C functions generalises this, allowing clients to connect locally or over the internet fairly transparently. This documents the protocol that is used over that connection.

A C structure defines the packet that clients and the server use to interact. Integers are not byte-order/endianness converted, but as long as clients and the server are all on the same (usually a Linux PC) this shouldn't be a problem — i.e. we assume little-endian.

    #define SM_PACKET_MAX (128)

    typedef struct {
        int code;                              /* op-code/response-code */
        int lun;                               /* logical unit to address (if relevant) */
        unsigned char data[SM_PACKET_MAX];     /* data payload */
        int length;                            /* length of data */
    } sm_packet;

The packet size sent over the Unix/IPv4 socket is fixed at 140 bytes.

The various operation/response codes are defined as follows:

    #define SM_PARTIAL     0x10000
    #define SM_RESPONSE    0x08000

    #define SM_CODEMASK    0x07fff
    #define SM_NOP         0x00001
    #define SM_SCAN        0x00002
    #define SM_RESET       0x00003
    #define SM_PING        0x00004
    #define SM_DEVID       0x00005
    #define SM_LIST        0x00006
    #define SM_RAW         0x00007
    #define SM_ASYNCMSG    0x00008
    #define SM_FINDDEV     0x00009

    #define SM_DISCONNECT  0x01001

The SM_PARTIAL flag is used to indicate that the packet being sent/received is only part of the data required. For example, when a SM_LIST command is sent, the response comes back in multiple packets, all but the last having SM_PARTIAL set.

The SM_RESPONSE flag is used to indicate that a response is expected (to server), or that the packet is a response (from server).

For certain commands, the lun field is relevant, for most not. In most cases, the contents of the data and its length are relevant however. The following sub-sections briefly describe each of the commands. For talking to actual devices on the RS422 bus, the SM_RAW command is likely to be the most useful — a brief example of this is given later.

4.1. NOP

This is (can be) used to test connectivity with the server. If the SM_RESPONSE flag is set, the server will send a response back. The lun, data and length fields are unchanged.

4.2. SCAN

This is used to scan for devices on the RS422 bus. If the SM_RESPONSE flag is set, a response is sent back once the device scan has been initiated (but probably before any new devices have been identified). The presence of new devices can be detected by sending a SM_LIST packet.

4.3. RESET

This triggers a soft reset of all devices on the bus. If the SM_RESPONSE flag is set, a response is sent back once the reset has been initiated. Note: this will not help to reset "stuck" devices: that requires a power-cycle of the bus.

4.4. PING

Not currently supported — use a RAW ping command.

4.5. DEVID

Not currently supported — use a RAW device-ID command.

4.6. LIST

This is used to list devices on the RS422 bus. If the SM_RESPONSE flag is not set, this command is ignored. Each response packet returned to the client will contain information about a single device. The data field contains a C structure that looks like:

    typedef struct {
        unsigned char lun;                   /* logical unit number */
        unsigned char devid[4];              /* device ID (4 bytes) */
        void *dummy1;                        /* ignore */
        unsigned char fwmaj;                 /* firmware major version */
        unsigned char fwmin;                 /* firmware minor version */
        void *dummy2;                        /* ignore */
    } rs422dev_t;

The two pointer fields usually contain useful information, but should be ignored when this structure is returned over the socket. The device identifier (up to 16 bytes of meaningful name) can be found after this structure (from byte 15) in the data field. The length of the response packet indirectly indicates how many bytes of device-name there are (length - 15).

If there are more responses still to come (i.e. more devices) then the SM_PARTIAL flag will be set.

4.7. RAW

This is the most useful (and frequently used) mechanism to communicate with devices on the RS422 bus. The lun field is actually ignored, as this information is expected to be contained within the data field, depending on what is being sent. The data and its length is expected to fill the "LUN and data" bits of the generic protocol. That is, when the packet goes onto the wire, the actual packet length is prepended and the checksum appended. Thus, this can be used to send any kind of command to any kind of device.

If the SM_RESPONSE flag is set (in the request) then a response is expected from the device. The default timeout for getting a response is 1 second. If a valid response is received from the device within this time, it is returned to the client. If no response is received, a packet is sent back to the client of type SM_ASYNCMSG (along with a short message stating that a timeout happened). For all kinds of response sent back to the client, the SM_RESPONSE flag will be set.

4.8. ASYNCMSG

This is used to tell clients about events that happen outside of the normal flow of communication. Typically timeouts to device responses and other error conditions.

4.9. FINDDEV

This is used to find a particular device. A response is always generated, regardless of whether SM_RESPONSE is set or not. The data and length of the outgoing packet should contain an ID string that is used to locate the LUN of a particular device. If multiple devices match, only the first will be returned. The LUN of a matching device will be set in the lun field of the response packet (with other contents unchanged). If the device is not found, the lun field will be set to 255.

To search for (and find) multiple instances of a particular device, the LIST command should be used.

4.10. Example

This section briefly explains the steps required to communicate with RS422 bus devices via the server software (sermon2). As an example, we'll assume we want to make a model train with ID 0x80180001 go forwards at 75% power (ish) for 1.5 seconds.

  1. Connect to the server through its Unix socket or over the internet.

  2. Locate the LUN of the RF transceiver device (whose descriptive name is "rftransmit"). To do this, fill a sm_packet structure with the appropriate contents:

    		sm_packet pkt;
    
    		pkt.code = SM_FINDDEV | SM_RESPONSE;
    		pkt.length = sprintf (pkt.data, "rftransmit");
    
  3. Send the whole packet (sizeof(pkt) bytes) to the server.

  4. Wait for a reply from the server — and do some basic sanity checks:

    		sm_packet pkt;
    
    		... read whole packet (140 bytes) from socket
    
    		if ((pkt.code & SM_CODEMASK) != SM_FINDDEV) {
    			/* error.. */
    			exit (1);
    		}
    
    		if (pkt.lun == 255) {
    			/* device not found, error.. */
    			exit (2);
    		}
    		/* ... else pkt.lun contains the LUN we're after */
    
  5. Build a packet to send to the train via the RF transceiver, and send it to the server:

    		sm_packet pkt;
    
    		pkt.code = SM_RAW;
    		pkt.data[0] = rflun;		/* LUN of RF transceiver */
    		pkt.data[1] = 0x01;		/* RF transceiver command to transmit data */
    		pkt.data[2] = 0x80;		/* train ID bytes */
    		pkt.data[3] = 0x18;
    		pkt.data[4] = 0x00;
    		pkt.data[5] = 0x01;
    		pkt.data[6] = 0xfb;		/* as per train protocol */
    		pkt.data[7] = 0x11;		/* bridge 1 forward, timed */
    		pkt.data[8] = 0xbf;		/* 75% power (roughly) */
    		pkt.data[9] = 0x00;		/* bridge 2 float */
    		pkt.data[10] = 0x00;		/* bridge 2 zero power */
    		pkt.length = 11;
    
    		... send packet to server
    
  6. Disconnect from the server.


Last modified 17th October 2010 by Fred Barnes (http://frmb.org/).