Documentation

Modbus parser: how to decode a Modbus frame

A Modbus frame is a device address (the unit ID), a function code and data. Modbus TCP, RTU and ASCII wrap those same three parts differently, and parsing a frame means reading the fields in order and checking the checksum. Paste your hex below to see it done.

Modbus frame decoder

The decoder needs JavaScript, which is turned off in this browser. The guide below it works without JavaScript.

What is inside a Modbus frame

Every Modbus message has the same core, the PDU (protocol data unit): a one-byte function code followed by data whose layout depends on that function. The transport adds what it needs around the PDU. Modbus TCP puts a 7-byte header in front of the PDU. Modbus RTU puts the unit ID in front and a CRC-16 behind. Modbus ASCII does the same as RTU but spells every byte as two text characters and uses an LRC instead of the CRC. The sections below show each wrapper with one worked example, and every example reads the same 3 registers, so you can compare them. The function code table lists what each code does.

All multi-byte numbers in the PDU are sent high byte first. The one exception is the RTU CRC, which is sent low byte first.

Modbus TCP frame structure

A Modbus TCP frame starts with the MBAP header (Modbus Application Protocol header): a 2-byte transaction ID, a 2-byte protocol ID that must be 0, a 2-byte length and the 1-byte unit ID. The length counts the bytes after it, so it is the unit ID plus the PDU. TCP already protects the data in transit, so the frame has no CRC. Here is a request for 3 holding registers starting at address 16, and the reply.

Modbus TCP request: read 3 holding registers from address 16, unit 1
FieldHexMeaning
Transaction ID00 011. The client picks it; the device copies it into the reply so the two can be matched.
Protocol ID00 00Always 0 for Modbus.
Length00 066 bytes follow: unit ID (1) + function code (1) + 4 bytes of data.
Unit ID01Device address. Devices reached directly by IP address often ignore it.
Function code03Read Holding Registers.
Start address00 1016, counted from 0.
Quantity00 033 registers.
Modbus TCP response to that request
FieldHexMeaning
Transaction ID00 01Same as in the request.
Protocol ID00 000.
Length00 099 bytes follow: unit ID, function code, byte count and 6 data bytes.
Unit ID01Same as in the request.
Function code03Same as in the request. The high bit is not set, so this is not an exception.
Byte count066 data bytes follow.
Register 100 FA250.
Register 200 011.
Register 313 885000.

To parse a TCP frame, check that the protocol ID is 0 and that the length field equals the number of bytes that follow it. A TCP stream can deliver several frames back to back, or half a frame, and the length field is how you find the border between them.

Modbus RTU frame example

A Modbus RTU frame has no header and no length field. It is the unit ID, the function code, the data and a 2-byte CRC-16 calculated over everything before it. The receiver finds the end of the frame from a silence of at least 3.5 character times on the line: at 9600 baud with 11 bits per character that is about 4 ms, and above 19200 baud the specification fixes it at 1.75 ms. A pause of more than 1.5 character times inside a frame marks it as broken.

Modbus RTU request: the same read, 8 bytes
FieldHexMeaning
Unit ID01Device address 1.
Function code03Read Holding Registers.
Start address00 1016.
Quantity00 033 registers.
CRC-1604 0EThe CRC value is 0x0E04, sent low byte first.
Modbus RTU response: 11 bytes
FieldHexMeaning
Unit ID01Device address 1.
Function code03Read Holding Registers.
Byte count066 data bytes.
Registers00 FA 00 01 13 88250, 1 and 5000.
CRC-16A5 F7The CRC value is 0xF7A5, sent low byte first.

The CRC starts at 0xFFFF, folds in each byte with XOR and shifts right eight times per byte, applying the polynomial 0xA001 whenever the lowest bit is set. The result is sent low byte first, which is why the value 0x0E04 appears as 04 0E on the wire. When a frame fails its CRC check, first try swapping the two CRC bytes; if that helps, the tool that built the frame has the order wrong. On RS-485, a converter that echoes your request puts those bytes in front of the reply, which is covered in RS-485 echo and RTS.

Modbus ASCII frame

Modbus ASCII is used on some older serial devices. The frame starts with a colon, writes every byte as two hex characters, ends with an LRC (longitudinal redundancy check) and finishes with a carriage return and a line feed. The LRC is the sum of the data bytes, negated and cut to one byte. Every byte takes two characters, so the 7 bytes of this frame (unit ID, function, 4 data bytes and the LRC) become 14 characters, plus the colon and the line end.

Modbus ASCII request: the same read, sent as text (:010300100003E9)
FieldHexMeaning
Start3AA colon.
Unit ID30 31The characters 01.
Function and data30 33 30 30 31 30 30 30 30 33The characters 0300100003: function 03, address 0010, quantity 0003.
LRC45 39The characters E9. The bytes 01 03 00 10 00 03 add up to 0x17, and the negated sum is 0xE9.
End0D 0ACarriage return and line feed.

Exception responses

When a device understands a request but cannot carry it out, it answers with the request's function code plus 0x80, which sets the highest bit, followed by a single exception code. A read with function 03 is answered with 83, and a write with function 16 with 90. The PDU of an exception is always 2 bytes. It also proves that the device is alive and the frame was valid, which makes it a different problem from a timeout. As an example, a read of one register at address 36864 (0x9000) is sent as 01 03 90 00 00 01 A9 0A, and a device with nothing at that address replies:

Modbus RTU exception response to function 03
FieldHexMeaning
Unit ID01Same as in the request.
Function code8303 + 80: the exception response to function 03.
Exception code02Illegal Data Address: the device has nothing at that address.
CRC-16C0 F1The CRC value is 0xF1C0, sent low byte first.
Common Modbus exception codes
CodeNameUsual meaning
01Illegal FunctionThe device does not support that function code.
02Illegal Data AddressThe address, or the address plus quantity, is outside what the device offers.
03Illegal Data ValueA value or the quantity is not allowed, for example a byte count that does not fit.
04Server Device FailureThe device tried the request and failed.
06Server Device BusyThe device is busy; try again later.
0AGateway Path UnavailableA gateway could not route the request.
0BGateway Target Device Failed to RespondA gateway got no answer from the device behind it.

The full list with causes and fixes is on the Modbus exception codes page.

Reading the data field

After the function code, the layout depends on the function and on the direction. The common ones are in the table. A response to a read starts with a byte count, then the values: two bytes per register, high byte first. Function 06 is echoed unchanged, and the reply to function 16 repeats the address and the quantity but not the values. Addresses in the PDU count from 0; see Modbus register addressing if a manual lists 40001-style numbers.

Data field after the function code
FunctionRequestResponse
03 and 04Start address (2 bytes), quantity (2 bytes).Byte count (1 byte), then 2 bytes per register, high byte first.
06Register address (2), value (2).An exact copy of the request.
10 (16)Start address (2), quantity (2), byte count (1), then 2 bytes per register.Start address (2) and quantity (2), without the values.

A register has only 16 bits, so a 32-bit integer or a float uses two registers, and devices differ on which register holds the high word and on the byte order inside each word. The frame cannot tell you; the device manual does. The Modbus register poller page explains how to show the same registers in all four byte and word orders.

How to check a Modbus frame

  1. Length. Is the frame long enough for its function, and on TCP does the length field match the bytes that follow it?

  2. Protocol ID. On TCP it must be 0. Anything else means the data is not Modbus TCP.

  3. Unit ID. Does it match the device you addressed? A reply from another unit means a second device or a gateway is answering.

  4. Function code. Is the high bit set? If so, the next byte is an exception code and the data checks below do not apply.

  5. Byte count against quantity. For registers the byte count is twice the quantity; for coils it is the quantity divided by 8, rounded up.

  6. CRC or LRC. Recalculate it over the right bytes and compare. TCP frames have none.

Reading frames in Busscope

The traffic panel in Busscope Master (F4) lists every frame that was sent and received, as hex with a time stamp and a Tx or Rx marker. It shows the complete frame as it went over the link: the MBAP header on TCP, the CRC on RTU, and on ASCII the colon, the characters and the line end as hex bytes starting with 3A. The panel shows raw bytes and does not split them into fields. To read a frame, copy its hex into the decoder above. The decoder is a free helper on this website, not a part of the Busscope program. The Modbus TCP tester and Modbus RTU tester pages describe the program itself.

Frequently asked questions

What is a Modbus parser?

A Modbus parser reads the bytes of a Modbus frame and splits them into fields: the unit ID (device address), the function code and the data, plus the header or checksum that the transport adds. The decoder on this page does that in your browser for Modbus TCP, RTU and ASCII frames.

How do I decode a Modbus RTU frame by hand?

Read the first byte as the unit ID and the second as the function code. The last two bytes are the CRC-16, sent low byte first, and everything between the function code and the CRC is the data. Calculate the CRC over all bytes before it and compare.

Why does the CRC of my Modbus RTU frame not match?

Common causes are swapped CRC bytes (the low byte is sent first), a CRC calculated over the wrong range (it covers unit ID, function code and data, but not itself), a frame that was cut off or merged with the next one, and an RS-485 echo that puts your own request in front of the reply. Modbus TCP frames have no CRC.

Can I tell a request from a response by looking at the bytes?

Not always. The function code is the same in both directions. For most functions the length and the byte count give the direction away, but responses to functions 05, 06 and 22 repeat the request exactly, and a few request and response layouts can match by chance. Choose the direction in the decoder when auto-detect is ambiguous.

Does the decoder upload my frames?

No. The decoder is JavaScript that runs in your browser. It makes no network requests, stores nothing and has no tracking, so the bytes you paste stay on your computer.

Try Busscope

Install Busscope and run it without a license key to see whether it fits your work. Evaluation mode is for trying the tool, not for production or commercial use.

Download the evaluation All documentation