2.2a My Modbus-a-like interface
This is not the protocol I'm using...
After a bit of flip-flopping about using a full modbus implementation, I decided not to write my own protocol, but to use an implimentation of the modbus protocol.
This is retained as a reference to see what my thoughts were along the way.
Overview of protocol:
- No collision detection is preformed, only one device can talk on the bus at a time.
- Base serial protocol is async 19.2k baud, 1 stop, no parity.
- Slaves (RTU's) can only respond to the master, and can not initiate transfers
- RTU's are accessed as a number of registers, bit-wise, byte-wise or in 16 bit words.
- All data is sent as binary data, 16 bit words are sent big-endian, ie: MSB first.
- A 'pass through' function is provided for output devices such as LCD's.
- Maximum frame length is 16 bytes, including checksum.
- A frame starts with the reception of the first byte, and ends after a delay of 3x the character time after the last byte recieved. (~2ms @19.2k)
- After reception of a frame by at the RTU, the checksum is verified. If a frame is not valid the RTU does not respond, the master should detect a timeout after 1 second, and retry or error out as appropriate.
- The addressing scheme of the protocol allows for accessing 64 bytes of registers, mapped as:
- 64x byte wide registers
- 256x bit wise registers (Mapped to lower 32 byte wise registers)
- 32x 16 bit wide registers (Mapped big-endian ie: MSB first)
- Each RTU will keep 16 bit counters of frames received, valid frames (addressed to itself) and envalid frames.
- Counters are accessible as 16 bit wordwise registers above address 31, the top of the 'register' addresses.
Frame format:
|Address | Function | Payload | chksum |
----------------------------------------
| 1 Byte | 1 Byte | X Bytes | 1 Byte |
- Address = slave address destination or source (master has no address)
- Function = The Function code defines what type of data is in the Payload for this frame.
- Payload = 0 or more bytes of data, as per the Function reference below.
- Payload is prefixed with a register address for general read and write functions.
- chksum = bite wide simple checksum used to validate frame
Response from slave is in the same format, details in the notes for each function linked below.
Device Addresses The device addresses can be in the range 1 to 199, device 0 is not valid, and 200-255 are reserved for special 'broadcast' functions.
Address Function
------- --------
0 Invalid
1-199 Device addresses
200 Broadcast
222 Address Config Broadcast
Data
All numerical data is sent as 'Big-Endian' 16 bit words. ie: most significant Byte first.
Single Byte data is sent in the second data Byte of the data word, and the first Byte is ignored.
Bitwise data is sent as all ones or all zeros in the high order byte, the low order byte is ignored. ie 0xff?? or 0xff??. This might seem odd, but it's as per the modbus standard.
eg's:
- 36541 Decimal is sent as 0x8e - 0xDB
- 12 Decimal is sent as 0x00 - 0x0C
- '1' for a bitwise read/Write is send as 0xff - 0x??
Register Addresses
Registers 0-127 are device specific. Registers 128 through 255 are reserved for special purposes.
Register Function
-------- --------
128 16bit - Input frame count - Serial frames recieved for this device (inc. broadcast)
129 16bit - Output frame count - Serial frames sent from this device
130 16bit - Frame error count - Number of partial or failed checksum frames
131 16bit - command error count - Invalid register or command fields
An example register map for an 8 bit digital I/O board might be:
Register Function
-------- --------
0 Input Port 0 read-only 8-bit register
1 Output Port 0, read/write 8 bit register
0x10 Output Port 0, bit 0 timer read/write register
......
0x17 Output Port 0, bit 7 timer read/write register
Function Code
The function code is a single byte between 1 and 255.
1-127 = master to slave instructions
128-255 = slave to master responses.
Function code summary:
code Function
---- --------
0 Invalid
1 Read register
2 Write register
3 Read single input (bit wise read)
4 Write single output (bit wise write)
8 Write String (For ASCII output devices)
64 Respond with ID - Used as broadcast, devices delay before responding
delay based on ID. ie: ID 1 has short delay, ID 200 has a long one.
65 Retrieve ID string - Returns ASCII string to master with device Description.
66 Reset Counters
128 Failed command - Used in return from slaves to master.
The success of a command is implicit in the reply packet to the master. If it succeeds the function code is repeated in the reply packet. If it fails the function code is '128' and the error is in the frame payload.
The size of the data payload in a given frame is determined by the function code. See detail of each function below.
Function 1 - Read register
The payload for command 1 is a single byte containing the register address, and the return is the register number and the 16 bit number from the register contents, . Byte and bit-wise registers are converted as per the data rules above.
e.g.: The master reads address 0x02 on device 0x12 and gets 0x3d4e back...
|addr| Fn |reg | CRC |
Master out: |0x12|0x01|0x02|0x??,0x??|
|addr| Fn | data | CRC |
Slave reply: |0x12|0x01|0x3d|0x4e|0x??,0x??|
Function 2 - Write register
The payload for Write register is 3 bytes. The address of the register, and the 16 bit value to write. Byte and Bit-wise registers are expanded as per the data comment above.
The response from the device is to essentially return the frame un-modified if there was no error.
e.g.: The master writes 0xef into byte-sized register 0x02 of device 0x06
|addr| Fn |reg | data | CRC |
Master out: |0x06|0x02|0x02|0x00,0xef|0x??,0x??|
|addr| Fn |reg | data | CRC |
Slave reply:|0x06|0x02|0x02|0x00,0xef|0x??,0x??|
Function 3 - Read single input
The payload for Read single input 2 bytes. The address of the register, and the bit position you wish to read.
The response from the device is to return the bit packed into a 16bit word as per the data comment above.
e.g.: The master reads the 6th bit of register 0x05 of device 0x06 which is true/on.
|addr| Fn |reg |bit | CRC |
Master out: |0x06|0x03|0x02|0x06|0x??,0x??|
|addr| Fn | data | CRC |
Slave reply:|0x06|0x03|0xff,0x00|0x??,0x??|
Function 4 - Write single output
The payload for Write single output is 4 bytes. The address of the register, the bit position, and the bit data to be written packed into a 16 bit value as per the data comment above.
The response from the device is to essentially return the frame un-modified if there was no error.
e.g.: The master writes a 0/false value into bit 9 of register 0x02 of device 0x06
|addr| Fn |reg |bit | data | CRC |
Master out: |0x06|0x04|0x02|0x09|0x00,0x00|0x??,0x??|
|addr| Fn |reg |bit | data | CRC |
Slave reply:|0x06|0x04|0x02|0x09|0x00,0x00|0x??,0x??|
Function 8 - Write String
The write-string command is for devices such as LCD screens that require ASCII data to be passed from the bus to them un-modified.
The string is null-terminated, so the maximum string length per frame is 26 characters, given the overall maximum of 28 bytes per frame, and the first byte of the data is the 'register' to which the data is to be sent, allowing for multiple character outputs per slave device.
The response from the slave is the frame less the data payload.
e.g.: The master sends "Hello" to register 5 on device 8
|addr| Fn |reg | Data | CRC |
Master out: |0x08|0x08|0x05|'H','e','l','l','o',0x00|0x??,0x??|
|addr| Fn | CRC |
Slave reply: |0x12|0x08|0x??,0x??|
Function 64 - Respond with ID
Respond with ID has no data payload, and is sent to the broadcast address of 200.
The device responds with it's ID.
e.g.: The master sends out a Respond with ID request and slave 0x34 responds
|addr| Fn | CRC |
Master out: |0xc8|0x40|0x??,0x??|
|addr| Fn | CRC |
Slave reply: |0x34|0x40|0x??,0x??|
This function is to allow the Master to map out the devices connected to it. Every connected device responds to this after a delay based on it's ID on the bus.
The delay is 10 character times on the bus multiplied by the ID of the device. In the case of the one above at 9600baud that would be 10.4ms
Function 65 - Retrieve ID String
Function 65 allows the master to find out what sort of device something is. Typically after function 64 has been used to identify what ID's are in use on the bus.
There is no data payload in the request for this function.
The device responds with a null terminated string describing what it is.
e.g.: Master queries the ID string of device 0x23
|addr| Fn | CRC |
Master out: |0x23|0x65|0x??,0x??|
|addr| Fn | data | CRC |
Slave reply: |0x23|0x65|"8bit Digital I/O",0x00|0x??,0x??|
Function 66 - Reset Counters
The reset counters function resets the frame and error counters in the slave device specified.
The is no payload data for function 66, and the device just responds with the same packet back as confirmation.
e.g.: the master resets the counters in device 0x23
|addr| Fn | CRC |
Master out: |0x23|0x66|0x??,0x??|
|addr| Fn | CRC |
Slave reply:|0x23|0x66|0x??,0x??|