All tools

Free tool

Modbus RTU frame builder

Builds a Modbus RTU request with a correct CRC-16, draws every byte, shows the reply to expect and decodes the reply you get back. The Tstep-087 stepper drive and TIO-0808 I/O card register maps are built in, taken from their firmware.

Request

Device

Firmware 2.00. Addresses 1–16 and 19200 or 115200 baud, both set by switches. Reads up to 8 registers, writes up to 4.

Bus
  • S4 off
  • S5 off
  • S6 off
  • S7 off
  • S8 off

Switches for address 1 and 19200 baud. On means closed. Read at power-up.

Function and register

Hex with 0x, or decimal.

registers
Register
0x0000 (0), Product ID
Access
Read only
Size
16-bit
Value
0x0087, fixed by the firmware
CRC-16/MODBUS, as sent 44 09low byte first
CRC value
0x0944
Request
8 bytes
Expected reply
13 bytes
3.5 characters at 19200
1.82 ms

Request frame

  • 01addr
  • 03fn
  • 00start hi
  • 00start lo
  • 00qty hi
  • 04qty lo
  • 44CRC lo
  • 09CRC hi
  • Address
  • Function
  • Data
  • CRC

Unit 1: read 4 holding registers, 0x0000 to 0x0003: Product ID, Firmware version, Unit address, Capabilities.

Copy the frame

Hex

01 03 00 00 00 04 44 09

C array

const uint8_t frame[8] = { 0x01, 0x03, 0x00, 0x00, 0x00, 0x04, 0x44, 0x09 };

Python bytes

b'\x01\x03\x00\x00\x00\x04\x44\x09'

Expected reply

  • 01addr
  • 03fn
  • 08bytes
  • 000000 hi
  • 870000 lo
  • 020001 hi
  • 000001 lo
  • 000002 hi
  • 010002 lo
  • 000003 hi
  • 070003 lo
  • 73CRC lo
  • 3FCRC hi

A normal reply, 13 bytes: address, function, byte count 8, 4 registers high byte first, CRC.

RegisterNameRawValue
0x0000Product ID00870x0087, a Tstep-087
0x0001Firmware version02002.00
0x0002Unit address00011
0x0003Capabilities00070x0007: motion, potentiometer, IN1/IN2

Every value here is fixed by the firmware, except the unit address, which is the address entered above.

Timing on the wire

Baud1 character3.5 charactersSpec gapThis requestIts reply
192000.521 ms1.82 ms1.82 ms4.17 ms6.77 ms
1152000.0868 ms0.304 ms1.75 ms0.694 ms1.13 ms

A character is 10 bits: start, 8 data bits, stop, the 8N1 both firmwares use. Above 19200 baud the Modbus specification fixes the gap at 1.75 ms. The firmware ends a frame after 3 ms of silence, so leave at least 5 ms between requests.

Decode a reply

Read against the request above: unit 1, function 03, 4 registers from 0x0000. Spaces, commas and 0x are ignored.

CRC correct: 73 3F

A normal reply to function 03: 4 registers from 0x0000.

RegisterNameRawValue
0x0000Product ID00870x0087, a Tstep-087
0x0001Firmware version02002.00
0x0002Unit address00011
0x0003Capabilities00070x0007: motion, potentiometer, IN1/IN2

How the frame is built

A Modbus RTU frame is the unit address, a function code, the data and a CRC. Nothing marks where a frame starts or ends: the silence between frames does that. Every multi-byte field is big-endian, high byte first, except the CRC, which goes low byte first.

FunctionRequestBytesNormal replyBytes
03address, 03, start (2), quantity (2), CRC (2)8address, 03, byte count, 2 bytes per register, CRC5 + 2 × quantity
04address, 04, start (2), quantity (2), CRC (2)8address, 04, byte count, 2 bytes per register, CRC5 + 2 × quantity
06address, 06, register (2), value (2), CRC (2)8the request, echoed8
10 (16)address, 10, start (2), quantity (2), byte count, 2 bytes per register, CRC (2)9 + 2 × quantityaddress, 10, start (2), quantity (2), CRC8
any, refused--address, function + 0x80, exception code, CRC5

The Tstep-087 and TIO-0808 implement exactly these four functions, and answer anything else with exception 01. Function 04 runs the same code as 03 on both, so the two read the same registers.

The CRC, worked through

CRC-16/MODBUS starts at 0xFFFF. Each byte is XORed into the low eight bits, then the register is shifted right eight times; whenever the bit shifted out is 1, the register is XORed with 0xA001, the polynomial 0x8005 reflected. There is no final XOR, and the result goes on the wire low byte first.

crc = 0xFFFF
for each byte:
    crc = crc XOR byte
    repeat 8 times:
        if (crc AND 1) = 1:  crc = (crc >> 1) XOR 0xA001
        else:                crc = crc >> 1
send  crc AND 0xFF,  then  crc >> 8

Worked through for 01 03 00 00 00 01: read one register, the Tstep-087’s product ID, from unit 1.

ByteCRC after it
(start)0xFFFF
010x807E
030x2140
000xF020
000xD8F1
000x8419
010x0A84

The CRC is 0x0A84, sent as 84 0A, so the frame is 01 03 00 00 00 01 84 0A. A Tstep-087 at address 1 answers 01 03 02 00 87 F8 26. Two more to check an implementation against: 01 03 00 00 00 0A ends C5 CD, and the nine ASCII characters 123456789 give 0x4B37.

Byte order

Writing cruise speed 2000 and move distance 6000 to a Tstep-087 at address 1 in one frame: 01 10 02 04 00 04 08 00 00 07 D0 00 00 17 70 8E 86. The drive acknowledges with 01 10 02 04 00 04 81 B3.

Limits, silences and exceptions

CodeNameWhen the Tstep-087 and TIO-0808 return it
01Illegal functionAny function other than 03, 04, 06 and 10.
02Illegal data addressA register that is not mapped, a range that runs into one, or a write to a read-only register.
03Illegal data valueA quantity of 0 or over the limit, a byte count that contradicts the quantity, a TIO-0808 I/O value out of range, an unknown command, or a command refused in the current state.
04Server device failureDefined in the protocol code, never returned.

Timing

RTU frames are separated by at least 3.5 characters of silence. Both products run their serial port at 8 data bits, no parity and 1 stop bit, so one character is 10 bits.

1 character  = 10 / baud seconds
t3.5         = 3.5 × 10 / baud

19200 baud:   t3.5 = 35 / 19200  = 1.82 ms
115200 baud:  t3.5 = 35 / 115200 = 0.304 ms   (the Modbus specification fixes 1.75 ms above 19200)

A pause that long inside a frame splits it in two and both halves fail their CRC, so send each frame in a single write. The firmware decides a frame has ended after 3 ms of silence at either baud rate, so leave at least 5 ms between the end of one request and the start of the next. A device on an 8E1 or 8N2 line has 11 bits to a character instead.

The TIO-0808 map moved at firmware 3.00

From firmware 3.00 the TIO-0808 drives a step/direction axis, and its motion registers sit at the same addresses, with the same meanings, as the Tstep-087’s. The card’s own I/O moved to make room.

BlockFirmware 2.00Firmware 3.00
Card control: DAC, PWM, outputs, LED0x0200–02060x0400–0406
Card status: inputs, analogue, temperature0x0300–03030x0500–0503
Motion parameters and command-0x0200–020B
Motion status-0x0300–0305

The failure is silent. A master written for 2.00 that polls 0x0300 for the input port now reads the low word of the current speed, and both are small, plausible numbers. Read register 0x0001 first: 0x0300 or higher means the 3.00 map this tool uses. The output port also shrank from eight bits to six, because OUT7 and OUT8 became the step and direction pins.

Register maps

Tstep-087, firmware 2.00

AddressDecimalNameAccessSize and unitsRange and notes
Identity
0x00000Product IDR16-bit0x0087, fixed
0x00011Firmware versionR16-bit0x0200, fixed
0x00022Unit addressR16-bit1 to 16. As set on switches S4 to S7.
0x00033CapabilitiesR16-bit, bits0x0007, fixed: bit 0 motion, bit 1 potentiometer, bit 2 IN1/IN2
Motion parameters
0x0200–0201512–513AccelerationR/W32-bit, steps/s²1 to 1000000 steps/s², clamped
0x0202–0203514–515Start speedR/W32-bit, steps/s1 to 100000 steps/s, clamped
0x0204–0205516–517Cruise speedR/W32-bit, steps/s1 to 100000 steps/s, clamped
0x0206–0207518–519Move distanceR/W32-bit, steps0 to 2147483647 steps, clamped
0x0208–0209520–521DwellR/W32-bit, ms1 to 2147483647 ms, clamped. Stored, but nothing in the firmware acts on it.
0x020A522CommandR/W16-bitSee the commands below. Writing it executes; reads back the last command accepted.
0x020B523Command sequenceR/W16-bit0 to 65535. Reads back the sequence of the last command executed.
Status
0x0300–0301768–769Current speedR32-bit, steps/s
0x0302–0303770–771PositionR32-bit signed, stepsTwo’s complement across the pair.
0x0304772State wordR16-bit, bits0x0001 moving, 0x0002 clockwise, 0x0004 waiting (never set), 0x0008 potentiometer enabled, 0x0010 driver fault latched. A fault reads 0x0010 on its own.
0x0305773InputsR16-bit, bitsbit 0 IN1, bit 1 IN2

TIO-0808, firmware 3.00

AddressDecimalNameAccessSize and unitsRange and notes
Identity
0x00000Product IDR16-bit0x0808, fixed
0x00011Firmware versionR16-bit0x0300, fixed. Read this first.
0x00022Unit addressR16-bit17 to 24. As set on switches AD0 to AD2.
0x00033CapabilitiesR16-bit, bits0x001F, fixed: bit 0 DAC, bit 1 PWM, bit 2 ADC, bit 3 8 inputs and 6 outputs, bit 4 step/direction axis
Motion parameters, as the Tstep-087
0x0200–0201512–513AccelerationR/W32-bit, steps/s²1 to 1000000 steps/s², clamped. 1000 at start-up until saved.
0x0202–0203514–515Start speedR/W32-bit, steps/s1 to 100000 steps/s, clamped. 100 at start-up until saved.
0x0204–0205516–517Cruise speedR/W32-bit, steps/s1 to 100000 steps/s, clamped. 1000 at start-up until saved.
0x0206–0207518–519Move distanceR/W32-bit, steps0 to 2147483647 steps, clamped
0x0208–0209520–521DwellR/W32-bit, ms1 to 2147483647 ms, clamped. Reserved, never acted on.
0x020A522CommandR/W16-bitSee the commands below.
0x020B523Command sequenceR/W16-bit0 to 65535
Motion status, as the Tstep-087
0x0300–0301768–769Current speedR32-bit, steps/s
0x0302–0303770–771PositionR32-bit signed, stepsTwo’s complement across the pair.
0x0304772State wordR16-bit, bits0x0001 moving, 0x0002 clockwise, 0x0008 pot mode on. 0x0004 and 0x0010 are never set on this card.
0x0305773Seek inputsR16-bit, bitsbit 0 IN1, bit 1 IN2
Card control
0x04001024DAC levelR/W16-bit, %0 to 100 %, else exception 03
0x04011025DAC enableR/W16-bit0 or 1, else exception 03
0x04021026PWM frequencyR/W16-bit, Hz100 to 10000 Hz, else exception 03
0x04031027PWM dutyR/W16-bit, per mille1 to 999 per mille (0.1 to 99.9 %), else exception 03
0x04041028PWM enableR/W16-bit0 or 1, else exception 03
0x04051029Output portR/W16-bit, bits OUT1 to OUT60 to 63, else exception 03. OUT7 and OUT8 are the step and direction pins.
0x04061030Status LEDR/W16-bit, %0 to 100 %, else exception 03
Card status
0x05001280Input portR16-bit, bits IN1 to IN8A bit is 1 while its input is pulled low.
0x05011281Output port readbackR16-bit, bits OUT1 to OUT6
0x05021282Analogue inputR16-bit, mV
0x05031283CPU temperatureR16-bit, °C

Commands, written to 0x020A

ValueCommandTstep-087TIO-0808
0StopAlways acceptedAlways accepted
1Move clockwise by the staged distanceException 03 if the move cannot startException 03 if the move cannot start
2Move anticlockwise by the staged distanceException 03 if the move cannot startException 03 if the move cannot start
3Run clockwise until stopped; reverses a run already goingException 03 if the run or reversal cannot startException 03 if the run or reversal cannot start
4Run anticlockwise until stopped; reverses a run already goingException 03 if the run or reversal cannot startException 03 if the run or reversal cannot start
5Seek input 1Exception 03 if the seek cannot startException 03 if the seek cannot start
6Seek input 2Exception 03 if the seek cannot startException 03 if the seek cannot start
7Reset the position counter to zeroAlways acceptedAlways accepted
8Save parameters to flashException 03 while the motor is movingException 03 while the axis is moving, or if the flash write fails
13Potentiometer enable (pot mode on)Always acceptedAlways accepted
14Potentiometer disable (pot mode off)Always acceptedAlways accepted
17Drive enableAlways acceptedRefused, exception 03
18Drive disableAlways acceptedRefused, exception 03
19Clear a latched driver faultException 03 while the fault is still liveRefused, exception 03
20Apply the staged cruise speedException 03 while the potentiometer is enabledException 03 while pot mode is on

Any other value is refused with exception 03. A command sent with a sequence number the device has already executed is acknowledged and not carried out again.

Switches and wiring

Related Tiny Controls products