Modbus Decoder and Frame Builder — RTU and TCP, Online Free

By | November 21, 2025

Two jobs on one page. Decode takes bytes off the wire and tells you what they say. Build goes the other way: pick a function code, fill in the fields, get a frame with a valid CRC you can paste into a serial terminal or a test client.

The decoder handles RTU and TCP, and it works out which one it is looking at on its own.

Transport
Register format
Request frame optional — gives the decoder the start address and quantity
Response frame
Start address used when no request is given
Load an example:
Paste a response frame and press Decode. Add the request frame to get real point addresses.

Paste the request as well as the response

The response frame alone cannot tell you which registers you are looking at.

A read holding registers response is a function code, a byte count and the data. That is all. The start address lives in the request, and the slave never repeats it. So a response of 01 03 04 00 7B 00 C8 means “here are two registers”, and nothing more. Which two is anyone’s guess.

Paste the request in the top field and the decoder carries the start address and quantity across. Registers then come back with their real addresses instead of starting at zero. It also cross-checks the pair: a request to slave 1 with an answer from slave 3, or a request for function 3 answered by function 4, gets flagged.

If you only have the response, type the start address in the field below it and the table numbers itself from there.

How the transport is detected

RTU and TCP carry the same PDU. What differs is the wrapper.

RTUTCP
Before the function code1 address octet7-octet MBAP header
After the data2 CRC octetsnothing
Frame boundary3.5 character silencelength field in the MBAP header
Addressingslave address 1 to 247unit ID, often 255 or 0 through a gateway

Auto-detect checks the CRC first. If the last two octets are a valid CRC-16 over everything before them, it is RTU. Only if that fails does it look for the MBAP shape.

That order matters. An ordinary read request, 01 03 00 00 00 02 C4 0B, happens to look like a valid MBAP header: protocol ID zero, length two, eight octets total. Tools that test for TCP first will call it a TCP frame and decode garbage. Testing the CRC first avoids the whole class of mistake, and a corrupted RTU frame then reports a CRC mismatch instead of being quietly relabelled.

You can override the choice if you know what you have.

Reading the byte map

Every frame is drawn as coloured segments with the field name under each one.

Blue is the framing: slave address and CRC on RTU, the MBAP fields on TCP. Teal is the PDU: function code and the fields that belong to it. Amber is data. Red is trouble — a failed CRC, a wrong length field, or octets left over after the function’s fields are accounted for.

Those leftover octets are worth attention. If a read response says byte count 4 and carries six data octets, something upstream is padding or the frame is two frames run together. The map shows exactly where the extra bytes start.

Word order, the thing that wastes the most time

Modbus moves 16-bit registers. A 32-bit float needs two of them, and the standard does not say which register goes first. Every vendor picked something.

The decoder gives you all four orders under names people actually use:

NameAlso calledRegister orderByte order in each register
ABCDbig-endianhigh word firsthigh byte first
CDABword swap, mid-littlelow word firsthigh byte first
BADCbyte swap, mid-bighigh word firstlow byte first
DCBAlittle-endianlow word firstlow byte first

Switch the selector and the table redraws without re-pasting. The raw hex column stays next to each value, so you can see which arrangement produces a sensible number. A temperature reading of 74.2 in one order and 1.8e+32 in another tells you which one the device uses in about three seconds.

The same selector handles 16-bit signed and unsigned, hex, binary, ASCII, and 32 and 64-bit integers and doubles.

Got the register values but not the number
Drop the decoded registers into the Modbus Float Converter to see all four word orders side by side. It resolves FLOAT32, INT32, and INT64, applies a scale factor, and encodes a value back to registers when you need to write one.

Exception responses

When a slave cannot serve a request it sets the top bit of the function code and returns one data octet. Function 3 becomes 0x83, and the octet after it says why.

CodeNameWhat to check
1Illegal functionThe slave does not implement that function code at all
2Illegal data addressThe address does not exist, or the range runs past the end of the block
3Illegal data valueA value in the request is out of range. Often a quantity above the limit
4Slave device failureSomething failed inside the device while handling the request
5AcknowledgeAccepted, but it will take time. Poll for the result
6Slave device busyRetry later
7Negative acknowledgeThe programming function cannot be performed
8Memory parity errorExtended file record area failed its check
10Gateway path unavailableThe gateway has no route to that unit ID
11Gateway target failed to respondThe route exists, the device behind it does not answer

Codes 10 and 11 only come from a gateway, and they are the difference between “wrong unit ID” and “the serial device is offline”. Worth knowing which one you got before you drive to site.

The decoder names the base function alongside the exception, because 0x83 on its own is easy to misread as an unknown function code.

Quantity limits the frame will not tell you about

Reaching the limit does not corrupt anything. It just produces an exception 3 that some clients report as a timeout.

FunctionLimit per request
1, 2 — read coils or discrete inputs2000 bits
3, 4 — read holding or input registers125 registers
15 — write multiple coils1968 coils
16 — write multiple registers123 registers

The decoder checks the quantity against these, and the builder refuses to generate a request above them.

Building a frame

Pick RTU or TCP, set the slave address and function code, fill the fields, press Build. You get the frame in hex with the CRC already appended, or the MBAP header already sized.

The builder writes master requests, which is what you need for a bench test. Addresses go out as they appear on the wire, so a holding register documented as 40001 is address 0 here. That offset is the single most common reason a test read comes back as exception 2.

Send to decoder pushes the frame you just built into the decode tab and runs it. Useful for confirming a frame reads back the way you intended before you send it to a live device.

FAQ

Does anything leave my browser?

No. The decoding and the CRC run in the page. There is no server call and nothing is stored.

Which CRC does Modbus RTU use?

CRC-16 with polynomial 0xA001, initial value 0xFFFF, no final inversion, transmitted low byte first. It covers the slave address, the function code and all data, and it is not used at all in Modbus TCP, where TCP’s own checksum does the job.

Why does my frame decode as TCP when it is serial?

It should not, since the CRC is tested first. If it happens, the CRC is failing. Set the transport by hand and look at the CRC line: if the calculated value differs from the one in the frame, you have a real error, not a detection problem.

Can it decode Modbus ASCII?

Not yet. ASCII frames start with a colon, carry hex digits as text and end with an LRC and CRLF. Convert the frame to binary and the PDU part decodes normally.

How do I know if a frame is a request or a response?

For functions 5, 6 and 22 you cannot. The response is a byte-for-byte echo of the request. The tool says “request or echoed response” rather than guessing. For functions 1 and 2 a four-octet payload is also ambiguous — it could be start address and quantity, or byte count 3 with three data octets. Pasting both halves of the exchange resolves it.

What unit ID should I use over TCP?

For a native TCP device the unit ID is usually ignored, and 255 or 1 both work. Through a gateway to a serial line it is the serial slave address, and it matters. Exception 11 means the gateway tried and got nothing back.

Two devices answer the same poll. Is that visible here?

Not from a single frame. It shows up as CRC errors on a serial capture, because the two responses overlap on the wire. Consistent CRC failures on a multi-drop line with clean wiring usually means a duplicate address.

Does the register table handle strings?

Yes. Choose 16-bit ASCII and each register shows as two characters. Device name and firmware version blocks read straight out.

Author: Zakaria El Intissar

I've spent 13 years in power system automation, electrical protection, and SCADA communication, as an automation and industrial computing engineer. ScadaProtocols.com is where I turn what I've learned on site into plain guides and working tools — so other engineers can decode, analyze, and troubleshoot industrial communication protocols without the guesswork.

Leave a Reply

Your email address will not be published. Required fields are marked *