View all lessons
Part 1
Fundamentals
Part 2
Modbus RTU
- Lesson 5RS485 explained: A, B, common and termination20 min
- Lesson 6Baud rate and parity: why 8E1 is the default16 min
- Lesson 7The Modbus RTU frame byte by byte, plus CRC18 min
- Lesson 8Multiple Modbus devices on one RS485 bus16 min
- Lesson 9Read your first Modbus device with mbpoll22 min
- Lesson 10Modbus RTU troubleshooting: symptom to cause20 min
Part 3
Modbus TCP
Part 4
Advanced
- Lesson 16Writing to a Modbus device without breaking it18 min
- Lesson 17Word order and floats: same bytes, other value22 min
- Lesson 18Calculate your poll interval and bus load18 min
- Lesson 19Modbus security: the protocol will not help16 min
- Lesson 20Modbus integration: PLC, Home Assistant, cloud20 min
- Lesson 21Modbus commissioning checklist and cheat sheet18 min
Modbus TCP troubleshooting with Wireshark
Wireshark shows a Modbus TCP transaction up close. Filter on exceptions, pair every request with its response and recognise a gateway that quietly gives up.
What this lesson covers
- A capture that holds only Modbus traffic, and where to measure it
- Pairing a request with its response through the transaction id
- One filter for every exception, and Decode As on another port
Read first: Port 502, the MBAP header and the unit id, Practise Modbus TCP with a free simulator
Wireshark shows you the bytes that actually crossed the network, which is the fastest way to settle whether a Modbus TCP problem is yours, the device's or the gateway's. A capture turns "it does not work" into a frame you can read field by field. After this lesson you can record only Modbus traffic, pair request with response through the transaction id, pull out every exception with one filter, and attach the dissector to a port other than 502.
Capturing only Modbus traffic
Set a capture filter before you press start, not afterwards.
tcp port 502
Against the simulator from the lesson on practising with a simulator that becomes tcp port 5020. The filter discards packets at the moment of capture, so on a busy site you keep a few hundred frames instead of a few hundred thousand.
Where you measure decides what you see at all. Run Wireshark on the polling machine itself. A random switch port gives you nothing, because a switch forwards traffic only to the ports involved, so watching two other devices talk needs a mirror port or a tap.
A capture separates three fault pictures that look identical from the client side.
What the packet pane actually shows
Wireshark splits a Modbus frame into two layers you can open separately: the MBAP header and the PDU underneath. Here is a read of three holding registers, twelve bytes on the wire: 00 01 00 00 00 06 FF 03 00 6B 00 03.
| Bytes | Field | What it says |
|---|---|---|
00 01 | Transaction identifier | 1, the client picks it |
00 00 | Protocol identifier | 0, always |
00 06 | Length | 6 bytes follow: 1 unit id plus 5 PDU |
FF | Unit identifier | 255, device is on the network itself |
03 | Function code | FC03, read holding registers |
00 6B | Start address | PDU address 107, datasheet number 40108 |
00 03 | Quantity | Three registers |
The answer is fifteen bytes: 00 01 00 00 00 09 FF 03 06 02 2B 00 00 00 64. Six of the seven header bytes come straight back unchanged. Only Length goes from 6 to 9: the server alone knows how much it is about to send, 1 unit id plus 8 PDU bytes.
No CRC: Ethernet and TCP already check integrity
The rule this whole lesson leans on: Length counts the unit id plus the PDU, so the complete frame is always Length plus six.
Three filters you always use
Capture filters and display filters are different languages; the one you type in the bar during analysis is the display filter. Three of them cover most of the work.
| Filter | What you get |
|---|---|
mbtcp | Every Modbus TCP frame in the capture |
modbus.func_code == 3 | Only the reads with FC03 |
modbus.exception_code | Only the frames carrying an exception |
The third one is worth memorising. It holds no comparison at all: naming a field on its own matches every packet that has it, so it becomes your "what is going wrong here" filter. Which devices speak Modbus in the first place is a different question, and a Modbus network scan answers that faster than any capture.
Tying request and response together
mbtcp.trans_id == 5 puts one transaction on screen, question and answer together, and that is how you tell a missing reply from a slow one.
The server copies the transaction id out of the request into the response unchanged, so the pairing stays exact even with several requests in flight on one connection. Filter on the number and count what you get: two frames means the transaction completed, one frame means nothing answered. The time column tells you how long the device took, which settles whether your timeout is too short. The field itself is covered in the lesson on the MBAP header and the unit id.
Attaching the dissector to another port
The Modbus dissector is bound to port 502, so on any other port Wireshark shows plain TCP with an uninterpreted data blob, and mbtcp matches nothing. Right click a packet, choose Decode As, set the TCP port to Modbus/TCP, and the same packets are dissected from then on.
You need this more often than you would expect. The pymodbus simulator listens on 5020 because a port below 1024 needs administrator rights, and SolarEdge inverters commissioned with SetApp default to 1502. The specification says port 502 must stay available even when a second port is configured, and not every manufacturer honours that.
What a capture tells you about the gateway
Two exception codes only ever come from a gateway, and both are good news: they prove your network side works. Here is that same read refused, assembled from the rules in the specification: 00 01 00 00 00 03 01 83 0B.
| Bytes | Field | Meaning |
|---|---|---|
00 01 | Transaction id | Copied from the request |
00 00 | Protocol id | 0 |
00 03 | Length | 3, so 1 unit id plus 2 PDU bytes |
01 | Unit id | The server address on the RS485 side |
83 | Function code | 0x03 plus 0x80, so an exception |
0B | Exception code | Gateway target device failed to respond |
That reply came back fast, and from the gateway rather than the meter. The gateway put your request on the RS485 bus and nothing answered on address 1, so you check the server address, the baud rate, the parity and the wiring instead of the firewall. Exception 0A, gateway path unavailable, points at the gateway itself: misconfigured or overloaded. Configuring both sides is in the Modbus TCP gateway setup guide.
If nothing comes back at all, you still saw your request leave: the gateway is waiting out its own serial timeout, or the device has run out of connections. SolarEdge, for one, supports a single connection.
When Wireshark will not help you
On RS485 there is nothing for Wireshark to listen to: it captures from network interfaces, and a differential pair is not one. You can bridge a serial port in with extra tooling, but in daily work you use your client's poll statistics and the FC08 diagnostic counters, the way the RTU troubleshooting lesson does.
The second limit is meaning. A capture proves that datasheet number 40108 returned 02 2B, which is 555 decimal. Whether that is 55.5 amps, 555 watts or a status code is a datasheet question. It is also this readable because plain Modbus TCP has no authentication and no encryption, which is why Modbus TCP explained treats segmentation as part of the design.
There is a practical limit as well: on a managed workstation you cannot always install Wireshark, or you are not allowed to. The Protocol Analyzer in ModbusCloud Diagnostics then shows the frames of its own connection, colour coded per field and with a counter for transactions and errors. Watching the traffic of another client still needs Wireshark and a mirror port.
Common mistakes
-
Starting without a capture filter. On a live network you collect hundreds of thousands of packets in minutes, the ring buffer wraps, and the frames you needed are gone. Type
tcp port 502before you press start. -
Concluding that Wireshark cannot see the simulator. On port 5020 the dissector is not attached, so you see plain TCP and assume nothing is being captured. Clear the display filter first: if TCP packets are there, the problem is interpretation and Decode As fixes it.
-
Measuring on an ordinary switch port. You plug your laptop into a free port, capture nothing, and conclude the device is dead. Capture on the polling machine itself, or set up a mirror port.
-
Taking a missing response for a network problem. You see the request leave and nothing come back, and you go open firewall rules. Check the connection limit and the gateway first: a gateway waiting out a serial timeout produces silence, and so does a unit id the device ignores.
Get hands-on
You capture two transactions yourself, one that succeeds and one that fails, and find each back with its own filter.
- 1
Start the simulator and the capture
Run the pymodbus simulator on port 5020, then start Wireshark on the loopback interface with capture filter
tcp port 5020. Both are free to use, including commercially. - 2
Read one register
Fire off a read with your client. If you see only plain TCP, right click a packet, choose Decode As, and map TCP port 5020 to Modbus/TCP.
- 3
Name the seven MBAP bytes
Open the request and walk through transaction id, protocol id, Length and unit id. Note the transaction id, and check that Length equals 1 plus the number of PDU bytes.
- 4
Put the transaction back together
Set the display filter to
mbtcp.trans_id ==followed by your number. Two frames means the transaction completed. - 5
Provoke an exception
Read a PDU address far past the end of the map, then filter on
modbus.exception_codeand you are left with that one frame. - 6
Compare the two answers
The exception has Length 0x0003 and a function code above
0x80, the successful read has a byte count and register data behind it. Finish the byte table for the exception frame yourself, the way it is laid out earlier in this lesson.
The read in step two from the command line, one holding register at PDU address 3 on unit id 1:
mbpoll -m tcp -p 5020 -a 1 -t 4 -r 3 -c 1 -0 -1 127.0.0.1
With real equipment. Swap port 5020 for 502 and capture on the polling machine or a mirror port. Behind a gateway, put the RS485 server address in the unit id, then try a wrong one on purpose: exception 0B is what you are after.
Expected result: one capture holding a successful transaction and an exception, each found again with its own filter.
Summary
- A capture filter such as
tcp port 502decides what you keep at the moment of capture, which is the difference between a few hundred frames and a few hundred thousand. - The transaction id pairs question with answer, so
mbtcp.trans_idshows at a glance whether a response was missing or merely late. - The filter
modbus.exception_codenames a field without a comparison, which is why it pulls every failed request out at once. - On a port other than 502 the dissector is not attached, so Decode As is routine and not a workaround.
- Exception 0B comes from the gateway and proves your network side works, so the search moves to the RS485 bus.
Check yourself
Four questions about this lesson. Every answer comes with an explanation.
Question 1 of 4
Want to see how it works?
The ModbusCloud Gateway reads the devices from this course without you programming a single register.