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
Reading exception codes and choosing a function code
A Modbus exception code tells you exactly where the fault sits. Learn which function code to send, and what 0x83 with exception 02 says about your address.
What this lesson covers
- Which function code belongs to which data block and bit width
- Reading 0x83 with exception 02, and where to look next
- Why a CRC error gives you silence instead of an exception
Read first: Modbus addressing: from 40001 to the wire
A Modbus exception code is one byte in which a device tells you which of your assumptions was wrong. It is not a crash and not a broken cable: the frame arrived intact, the device understood it, and refused it on purpose. After this lesson you can pick the right function code for a read, spot an exception in a hex dump without counting bytes, and tell from the code alone where to go looking.
Which question does which code ask?
The function code is the verb of your request: it names the data block you want and whether you read it or write it. Here is a complete read against an energy meter at server address 1 on an RS485 bus.
01 03 00 6B 00 03 74 17
| Byte | Value | Field | What it says |
|---|---|---|---|
| 1 | 01 | server address | talk to the device at address 1 |
| 2 | 03 | function code | FC03, read holding registers |
| 3 to 4 | 00 6B | data, starting address | PDU address 107, which is 40108 in 1-based datasheet numbering |
| 5 to 6 | 00 03 | data, quantity | three registers |
| 7 to 8 | 74 17 | error check | CRC-16, low byte first |
The meter answers:
01 03 06 02 2B 00 00 00 64 05 7A
| Byte | Value | Field | What it says |
|---|---|---|---|
| 1 | 01 | server address | the same device |
| 2 | 03 | function code | your code, echoed back unchanged |
| 3 | 06 | data, byte count | three registers times two bytes |
| 4 to 9 | 02 2B 00 00 00 64 | data, register values | 555, 0 and 100 |
| 10 to 11 | 05 7A | error check | CRC-16 over this frame |
Now the rule: the data block and the operation together decide the function code, and a normal response repeats that code.
Eight codes cover almost all day to day work: FC01 and FC02 read bits, FC03 and FC04 read 16 bit registers, FC05 and FC06 write a single item, FC15 and FC16 write a block. The rest of the public codes are diagnostics, file records or serial line only, and the complete Modbus function code reference lists them all. FC05, FC06 and FC16 write, so they can break something; the routine that keeps them safe is in the lesson on writing to a device.
Why two codes read almost the same thing
FC03 and FC04 do the same job in different blocks: FC03 reads holding registers, which can be written as well, FC04 reads input registers, which are read only. On the wire the two requests have the same shape and differ in one field, the function code. Here is the FC04 example from the standard:
01 04 00 08 00 01 B0 08
Same shape as the FC03 request above, 04 instead of 03, so this device looks in its input registers and returns one register from PDU address 8. Change byte 2 of that FC03 request to 04 and you read the same addresses in the other block, with a freshly calculated CRC. Which block your meter or inverter uses is a manufacturer decision, not a protocol rule. Plenty of energy meters publish measurements as input registers, many solar inverters expose the same values as holding registers, and some devices implement only one of the two codes. The register map of the device settles it, and guessing costs you an exception.
How to spot an exception without counting
Look at the second byte of an RTU frame, or at the first byte after the MBAP header on Modbus TCP. Is it 0x80 or higher, then you are looking at an exception. Every normal function code sits below 0x80, and a server answering with an exception sets the most significant bit of the code you sent.
01 83 02 C0 F1
0x03 plus 0x80 is 0x83, so this device is answering an FC03 request. The byte after it, 02, is the exception code. An exception PDU is always exactly two bytes, so the whole RTU frame is five bytes: address, code, exception code, and two CRC bytes. In a log full of long register responses that length alone gives it away.
The nine exception codes, grouped by cause
Standard V1.1b3 defines nine exception codes, and they fall into three groups that each point at a different place to look.
Codes 01, 02 and 03 say your request was wrong, and you fix those from your own side. Codes 04, 05, 06 and 08 say the request was fine but the device could not execute it right then. Codes 0A and 0B come from a gateway, not from the end device: 0B means the gateway forwarded your request to the RS485 side and that server address stayed silent, so check the address, wiring, baud rate and parity behind the gateway.
There is no exception 07 and no exception 09. You still meet 07 in old Modicon documentation, but the current standard does not have it.
What exception 02 actually tells you
Exception 02, Illegal Data Address, is about the starting address and the quantity together, not about the starting address alone. The standard spells it out with a device that has 100 registers, addressed in the PDU as 0 up to 99. A request that starts at register 96 for 4 registers works, because it touches 96, 97, 98 and 99. The same request for 5 registers fails with exception 02, because it would also touch register 100, which does not exist.
That is why exception 02 is the code you meet most often. Two causes dominate: the off-by-one between the 1-based number on the datasheet and the 0-based PDU address, worked through in Modbus addressing: from 40001 to the wire, and a block read that starts inside a table and runs past its end.
Why silence means something else than an exception
A request has four possible outcomes, and only one of them is an exception.
- The device receives the frame without error and can handle it: normal response.
- The device never receives the frame: no response, you time out.
- The device receives the frame but detects a parity or CRC error: no response, another timeout.
- The device receives the frame cleanly but cannot execute it: exception response.
A CRC error produces silence, never an exception, and that fixes your search order.
What FC43 tells you about an unknown device
FC43 with MEI type 14, Read Device Identification, asks a device to name itself. Three objects are mandatory in every implementation: VendorName, ProductCode and MajorMinorRevision. The request PDU is four bytes, 2B 0E 01 00: the function code, the MEI type, Read Device ID code 01 for the basic identification, and object id 0x00.
That is worth remembering in front of an existing installation with an unlabelled box in the cabinet, because it gives you brand, type and revision without a datasheet. It pairs well with scanning a Modbus network for live addresses. Not every device implements FC43. If yours does not, you get 0xAB back with exception 01, which is an answer too.
A full identification does not always fit in one 253 byte PDU. The response therefore carries More Follows and Next Object Id, and you fetch the rest in a second request.
Common mistakes
Swapping FC03 and FC04. People assume every measurement lives in holding registers and send FC03 to a meter that publishes input registers. The result is exception 01 if the device does not implement the code, or exception 02 if the block is smaller than assumed. Look up which block the device uses before you send anything.
Reading an exception as a wiring fault. This is the expensive one. An engineer sees an error, walks to the cabinet and starts pulling on terminals. An exception means your frame arrived without a single flipped bit, so the cable, the termination, the baud rate, the parity and the server address are all fine.
Expecting exception 03 to be about a register value. The standard says explicitly that it is not. Exception 03 is about the structure of your request, such as a quantity outside the allowed range. A setpoint that is too high for the application does not produce exception 03.
Waiting for exception 07 or 09. Neither exists in V1.1b3. The nine codes are 01 to 06, 08, 0A and 0B.
Try it yourself
The goal is to provoke a real exception on purpose and see the frame, because that is the difference between knowing exceptions exist and recognising one.
- 1
Start the simulator
pymodbus is free to use, including commercially. Install it and start the bundled example device:
python -m pip install "pymodbus[serial,simulator]==3.15.0" pymodbus.simulator --modbus_server server --modbus_device deviceThe server listens on port 5020, the web interface on
http://127.0.0.1:8081/. - 2
Read a register that exists
Point a client at PDU address 3 and ask for one register:
from pymodbus.client import ModbusTcpClient client = ModbusTcpClient("127.0.0.1", port=5020) client.connect() print(client.read_holding_registers(address=3, count=1, device_id=1).registers)That prints
[17001]. - 3
Provoke a real exception
Change the address to 1 and print the whole result instead of
.registers. The simulator answersExceptionResponse(dev_id=1, function_code=131, exception_code=2). - 4
Do the arithmetic yourself
131 decimal is 128 plus 3, so
0x83. Work out from that which function code your client sent, and why this is an exception and not a normal response. - 5
Push past a limit
Now ask for 130 registers with FC03. The standard allows 1 to 125, so 130 is strictly a data value problem and belongs to exception 03. Many devices check the address first and answer 02 instead. Note what yours does.
With hardware. Ask your own device for a register the datasheet does not list. Almost every device answers 0x83 with exception 02.
Expected result: an exception response on screen, and proof that the path to the device is intact.
Summary
- A function code names the data block and the operation, and a normal response echoes that code back.
- An exception response carries your function code plus
0x80and one exception code byte, so0x03becomes0x83. - Standard V1.1b3 defines nine exception codes: 01 to 06, 08, 0A and 0B. There is no 07 and no 09.
- Exception 02 is about the starting address and the quantity together, so a read that runs one register past a table triggers it.
- A transmission error gives silence and a timeout, never an exception, so an exception proves the wiring, the parameters and the address are good.
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.