FlyTerminal icon FlyTerminal

Debugging Modbus RTU Frames in a Serial Terminal

A raw serial terminal is the fastest way to confirm a Modbus RTU frame is well-formed before blaming the device or the master.

Modbus RTU remains the default register-map protocol on a large share of industrial controllers, drives, and sensor modules. When a register read returns nothing or a value looks wrong, the fastest path to the cause is a raw serial terminal that shows every byte on the wire in both directions.

This article walks through how to recognize a well-formed RTU frame, why the timing gaps matter as much as the bytes, and how to use a terminal's hex view to confirm the CRC and the framing instead of guessing which layer is at fault.

Anatomy of a Modbus RTU frame

Every RTU message opens with a slave address byte. On a multidrop bus only the addressed slave should answer, so the first byte you see in either direction tells you which device is involved. The frame then carries a single function code, the data field, and a two-byte CRC trailer.

The function code states what the master asked for. 0x03 reads holding registers and 0x10 writes multiple registers; both are the everyday workhorses for configuration and telemetry. An exception reply is not a separate frame type: the slave echoes the function code with its high bit set (function | 0x80) followed by one exception code byte.

  • Address (1 byte). Slave node ID; confirms you are talking to the intended device on a shared bus.
  • Function code (1 byte). The operation requested; 0x03 for read, 0x10 for write multiple.
  • Data field. Register address, count, and payload; length varies with the function.
  • CRC-16 (2 bytes). Trailer that proves the frame arrived intact.

Why the silent gaps matter

RTU has no header that declares the frame length. The bus is idle most of the time, and the protocol reuses silence to mark frame boundaries. After a complete frame there must be a quiet period of at least 3.5 character times before the next frame may begin, and a gap longer than 1.5 character times inside a frame is treated as a break.

On a busy bus a terminal that only shows received bytes is not enough: if two frames arrive back to back with less than 3.5 character times between them, a slow or buffered capture can mis-split them. Watch the timing, not just the hex.

Reading the CRC-16

The two trailing bytes are a CRC-16 computed over the address, function code, and data. The common implementation uses polynomial 0xA001 (the bit-reversed form of 0x8005) with an initial value of 0xFFFF, and the checksum is transmitted low byte first.

When the frame ends in bytes that look random, that is expected: the CRC is data-dependent. The check is that the two bytes match what your terminal's CRC calculator produces for the preceding bytes. A mismatch almost always means a dropped or flipped byte earlier in the frame, not a CRC bug in the device.

  • Copy the address through the data bytes exactly as shown in hex.
  • Run them through a CRC-16/Modbus calculator (poly 0xA001, init 0xFFFF).
  • Compare the low byte, then the high byte, against the frame trailer.
  • If they differ, re-capture: the error is usually in capture, not in the device.

Mistakes that waste an afternoon

Most RTU debugging dead ends come from port settings, not protocol logic. The parity choice and the framing need to match the device before any byte is trustworthy.

  • Wrong parity setting. Some devices use even parity (8E1), others none (8N1). A mismatched parity bit corrupts every byte, including the CRC, so the frame 'fails CRC' for a reason upstream of the checksum.
  • Assuming 8N1 is universal. The choice between 8E1 and 8N1 depends on the device and the Modbus variant it follows; check the device manual rather than guessing.
  • Reading ASCII by reflex. RTU is binary; viewing it as text turns register values into gibberish and hides the structure.

Tip: Keep the terminal in a hex or mixed view. The human-readable translation of register values belongs in your register map, not in the raw capture.

A capture workflow that scales

Open the port at the device's stated baud, data bits, parity, and stop bits. Start the master polling or send one request by hand, and capture both directions. In the hex view, split the capture at the 3.5-character silences, label each frame with its slave address and function, and verify the CRC of one good frame end to end.

Once one frame checks out, the failures become obvious: a timeout means no slave answered (wrong address or wiring), a CRC error means a byte was lost (parity or capture timing), and an exception reply means the request was well-formed but rejected (wrong register range). Each points at a different layer, and you fix them in order.

Start from a HEX view of the raw traffic, confirm your port framing in the baud-rate troubleshooting guide, and review RS-485 bus settings before assuming the device is at fault.

Try it on your own files

Download the free trial and see how it behaves on real work.

Download More articles

Common questions

What does a Modbus RTU exception reply look like?

The slave returns the original function code with its high bit set (function | 0x80) plus one exception code byte. A failed read holding registers, for example, comes back as 0x83 with a reason code you then look up.

Why do I see garbage instead of register values?

RTU frames are binary. If your terminal is in text mode the bytes render as nonsense. Switch to a hex or mixed view and parse the structure with your register map.

What parity should I use, 8E1 or 8N1?

It depends on the device and which Modbus variant it implements; both appear in the field. Match the setting to the device manual, because a wrong parity corrupts every byte including the CRC.

How long is the inter-frame silence in milliseconds?

It is 3.5 character times. At 9600 baud a character is about 1 ms, so the gap is roughly 3.5 ms at that speed; the duration scales with the baud rate.

My CRC never matches. Is the device broken?

Usually not. The most common cause is a dropped or flipped byte during capture, or a parity or framing mismatch that altered a byte before the CRC. Verify capture integrity and port settings first.

Keep reading