Reference · Appendices A–E
Quick Reference
The book's appendices in one place: material to return to long after the first read.
A · Function Code Reference
The eight common codes do almost all everyday work; the rest appear mainly on specific devices. Codes are shown in decimal and hex, since manuals vary.
The common function codes
| Dec | Hex | Name | Acts on |
|---|---|---|---|
| 1 | 0x01 | Read Coils | Coils (bit, R) |
| 2 | 0x02 | Read Discrete Inputs | Discrete inputs (bit, R) |
| 3 | 0x03 | Read Holding Registers | Holding regs (R) |
| 4 | 0x04 | Read Input Registers | Input regs (R) |
| 5 | 0x05 | Write Single Coil | Coils (bit, W) |
| 6 | 0x06 | Write Single Register | Holding regs (W) |
| 15 | 0x0F | Write Multiple Coils | Coils (bit, W) |
| 16 | 0x10 | Write Multiple Registers | Holding regs (W) |
Less-common function codes
| Dec | Hex | Name |
|---|---|---|
| 7 | 0x07 | Read Exception Status (serial) |
| 8 | 0x08 | Diagnostics (serial) |
| 11 | 0x0B | Get Comm Event Counter (serial) |
| 12 | 0x0C | Get Comm Event Log (serial) |
| 17 | 0x11 | Report Server ID |
| 20 | 0x14 | Read File Record |
| 21 | 0x15 | Write File Record |
| 22 | 0x16 | Mask Write Register |
| 23 | 0x17 | Read/Write Multiple Registers |
| 24 | 0x18 | Read FIFO Queue |
Request and response data fields
For the common codes, the PDU is the function code byte followed by these fields (byte counts in parentheses). All multi-byte values are big-endian, high byte first.
| Code | Request data | Response data |
|---|---|---|
| 0x01 / 0x02 | start addr (2), qty (2) | byte count (1), packed bits |
| 0x03 / 0x04 | start addr (2), qty (2) | byte count (1), values (2×N) |
| 0x05 | addr (2), value (2) | echo of request |
| 0x06 | addr (2), value (2) | echo of request |
| 0x0F | start (2), qty (2), bytes (1), bits | start (2), qty (2) |
| 0x10 | start (2), qty (2), bytes (1), values | start (2), qty (2) |
For Write Single Coil (0x05), the value is 0xFF00 for ON and 0x0000 for OFF; no other value is valid. Coil reads return bits packed eight per byte, the first requested coil in the least significant bit.
Per-request limits
| Operation | Maximum per request |
|---|---|
| Read coils / discrete inputs | 2000 bits |
| Read holding / input registers | 125 registers |
| Write multiple coils | 1968 bits |
| Write multiple registers | 123 registers |
| PDU size | 253 bytes |
Frame sizes for a register read (FC03 / FC04, N registers)
| Transport | Request | Response | Wrapping |
|---|---|---|---|
| RTU | 8 bytes | 5 + 2N bytes | unit address + PDU + 2-byte CRC |
| TCP | 12 bytes | 9 + 2N bytes | 7-byte MBAP header + PDU, no CRC |
An exception response returns the function code with its high bit set (the original plus 0x80), followed by one exception code byte (Appendix B). A failed 0x03 returns 0x83.
B · Exception Code Reference
When a server understands a request but can't perform it, it returns the requested function code with its high bit set (code + 0x80), then one of these codes. A 0x03 request fails as 0x83, a 0x10 as 0x90.
| Code | Name | Meaning |
|---|---|---|
| 01 | Illegal Function | The device does not support this function code |
| 02 | Illegal Data Address | The address (or address range) does not exist here |
| 03 | Illegal Data Value | A value or quantity is outside the allowed range |
| 04 | Server Device Failure | An unrecoverable error occurred in the device |
| 05 | Acknowledge | Request accepted; long processing under way, poll later |
| 06 | Server Device Busy | The device is busy; resend the request later |
| 08 | Memory Parity Error | A memory error during a file-record access |
| 0A | Gateway Path Unavailable | A gateway is misconfigured or overloaded |
| 0B | Gateway No Response | The gateway reached the target, which did not reply |
Diagnosing the common three
- 02 Illegal Data Address: the most common, and almost always an addressing error: a forgotten 4xxxx reference-number conversion, a count running past the end of a table, or the wrong table entirely.
- 03 Illegal Data Value: a rule was broken, such as asking for more than 125 registers or writing a coil value other than
0xFF00/0x0000. It's about a number's validity for this request, not its meaning. - 01 Illegal Function: the device simply doesn't implement the function code you sent; check its manual for supported codes.
An exception is better news than silence: it proves the device received and understood the request. A request that draws no reply at all is a connection problem, not an exception; see Appendix D and Chapter 16.
C · pymodbus Quick Reference
The pymodbus patterns used in this course, for pymodbus version 3. This course pins 3.7.4:
pip install "pymodbus==3.7.4" pyserial
| pymodbus | The course's code |
|---|---|
| 3.6, 3.7 | Runs unchanged (the course pins 3.7.4) |
| 3.8 – 3.9 | zero_mode removed |
| 3.10+ | Renames: ModbusSlaveContext → ModbusDeviceContext, slaves= → devices=, slave= → device_id= |
| 3.15 | Datastore classes deprecated ahead of v4 |
When something fails, check your installed version with pip show pymodbus.
Connecting
from pymodbus.client import ModbusTcpClient
client = ModbusTcpClient("192.168.1.50",
port=502)
client.connect()
from pymodbus.client import ModbusSerialClient
client = ModbusSerialClient(
port="/dev/ttyUSB0", baudrate=9600,
parity="N", stopbits=1, bytesize=8)
client.connect()
Reading
rc = client.read_coils(0, count=8)
rd = client.read_discrete_inputs(0, count=8)
rh = client.read_holding_registers(0, count=4)
ri = client.read_input_registers(0, count=2)
if not rh.isError():
print(rh.registers) # list of ints
if not rc.isError():
print(rc.bits) # list of bools
On a serial bus, add the unit address to each call, e.g. read_holding_registers(0, count=4, slave=1).
Writing
client.write_coil(0, True)
client.write_register(0, 1500)
client.write_coils(0, [True, False, True])
client.write_registers(0, [10, 20, 30])
Decoding wide values
import struct
def to_signed16(v):
return v - 65536 if v >= 32768 else v
def regs_to_float(regs, order="big"):
if order == "little":
regs = [regs[1], regs[0]]
raw = struct.pack(">HH", regs[0], regs[1])
return struct.unpack(">f", raw)[0]
Here order is the word order: "big" means the high word is in the first register; "little" means the low word comes first.
A minimal server
from pymodbus.server import StartTcpServer
from pymodbus.datastore import (
ModbusSequentialDataBlock,
ModbusSlaveContext, ModbusServerContext)
block = ModbusSequentialDataBlock(0, [0]*100)
dev = ModbusSlaveContext(
di=block, co=block, hr=block, ir=block,
zero_mode=True)
ctx = ModbusServerContext(slaves=dev,
single=True)
StartTcpServer(context=ctx,
address=("0.0.0.0", 5020))
This sketch shares one block across all four tables for brevity, so a write to a holding register also shows up as an input register; give each table its own block (as in Chapter 12) when that matters. Always close a client with client.close() when finished, and check the result of client.connect(), which returns False if the server can't be reached.
D · Wiring and Settings Reference
The physical side of Modbus RTU over RS-485. The protocol logic is identical everywhere; these are the hardware details that change from site to site.
RS-485 wiring
- A two-wire RS-485 bus is a daisy chain, not a star: the pair runs device to device in one line.
- Connect A to A and B to B across all devices; join the signal grounds. If labels disagree between makers and you get silence, try swapping the two data wires.
- Place a termination resistor (typically 120 Ω) at each far end of the line on longer or faster runs; short bench links usually work without.
- Add bias resistors somewhere on the bus to hold the line in a known idle state between messages when devices behave erratically.
Serial settings
Both ends of a serial link must use identical settings. Modbus RTU always uses 8 data bits. Settings are written compactly: 9600 8-E-1 means 9600 baud, 8 data bits, even parity, 1 stop bit.
| Setting | Common values |
|---|---|
| Baud rate | 9600, 19200, 38400, 115200 |
| Data bits | 8 (always, for RTU) |
| Parity | None, Even, Odd (Even is the default) |
| Stop bits | 1, or 2 when parity is None |
Either way each byte is an 11-bit character (start + 8 data + parity + 1 stop, or start + 8 data + 2 stops). Devices that use 8N1 (10-bit characters) are common too; they simply must match.
USB-to-RS-485 adapters on Linux
- A plugged-in adapter usually appears as
/dev/ttyUSB0. Confirm withls /dev/ttyUSB*ordmesg | grep ttyUSB. - Serial-port access requires membership in the
dialoutgroup:sudo usermod -a -G dialout $USER, then log out and back in. - A "permission denied" on the port almost always means the
dialoutmembership is missing, not a wiring fault.
ls /dev/ttyUSB*
dmesg | grep ttyUSB
sudo usermod -a -G dialout $USER
Modbus TCP defaults
| Item | Value |
|---|---|
| Standard port | 502 |
| Practice port (this course) | 5020 (no admin rights needed) |
| Secure variant (Modbus/TCP Security) | TLS with certificate authentication, port 802 |
| Unit identifier | 1 or 0xFF for native devices; selects the target behind a gateway |
Quick silence checklist
When a bus returns nothing, check in order, most common first.
| Serial | TCP |
|---|---|
| 1. Port name | 1. IP address and reachability |
| 2. Port permission | 2. Port |
| 3. Serial settings (baud and parity) | 3. Firewall on 502 |
| 4. Unit address | 4. Unit identifier, if a gateway is involved |
| 5. Wiring (try an A/B swap) |
On Windows, Test-NetConnection <ip> -Port 502 checks TCP reachability and the port in one line.
E · Glossary
Concise definitions of the terms used in the course. For deeper treatment, see the chapter that introduces each: the data model in Part I, frames and transports in Part II, the hands-on code in Part III, and deployment in Part IV.
| Term | Definition |
|---|---|
| ADU | Application Data Unit: a complete frame, meaning the address/header, the PDU, and any error check. |
| ASCII (Modbus) | A serial form that sends each byte as two hex text characters, framed by a colon and a carriage return/line feed, checked by an LRC. |
| Baud rate | Serial speed in bits per second; must match at both ends. |
| Bias resistor | A resistor that holds an idle RS-485 line in a defined state between messages. |
| Big-endian | Byte order in which the most significant byte is sent first. Modbus uses it within every data field (the RTU CRC is the exception: low byte first). |
| Broadcast | A write to unit address 0, meant for all serial devices at once; draws no response. |
| Client (master) | The device that initiates requests; the only party that may start a conversation. |
| Coil | A single read/write bit, typically an output you can command on or off. |
| CRC | Cyclic redundancy check: the two-byte checksum on an RTU frame, sent low byte first. |
| Data block | In a server, the storage for one of the four tables (di, co, ir, hr). |
| Discrete input | A single read-only bit, typically a sensed status. |
| Exception response | A reply marking refusal: the function code with its high bit set, plus an exception code. |
| Float (IEEE 754) | A 32-bit real number spread across two registers; subject to word order. |
| Frame | One complete message as bytes on the wire. |
| Function code | The one-byte verb of a request, naming the operation (e.g. 0x03 read holding registers). |
| Gateway | A device bridging Modbus transports, commonly TCP to serial RTU; routes by unit ID. |
| Half-duplex | One pair of wires carrying traffic in both directions, one transmitter at a time (RS-485). |
| Holding register | A 16-bit read/write register, typically a setting or value you can change. |
| Input register | A 16-bit read-only register, typically a measured value. |
| LRC | Longitudinal redundancy check: the one-byte checksum used by Modbus ASCII. |
| MBAP header | The 7-byte Modbus TCP header: transaction ID, protocol ID, length, unit ID. |
| PDU | Protocol Data Unit: the function code plus data; the transport-independent core of a message. |
| Polling | Repeatedly sending requests in a loop to keep a picture of devices current. |
| Reference number | Conventional addressing (4xxxx, 3xxxx, etc.); strip the leading digit and subtract 1 for the protocol address. |
| Register | A 16-bit value slot (0–65535); the native unit of Modbus numeric data. |
| RS-485 | A differential two-wire serial standard, the usual physical layer for Modbus RTU. |
| RTU (Modbus) | The compact binary serial form, framed by silence and checked by a CRC. |
| SCADA | Supervisory Control and Data Acquisition: software that monitors and controls a process at scale. |
| Scaling | Storing a fractional value as a scaled integer (e.g. 235 for 23.5); the factor comes from the register map. |
| Server (slave) | The device that answers requests; never speaks unless asked. |
| Termination resistor | A resistor (≈120 Ω) at each end of an RS-485 line to prevent reflections. |
| Transaction ID | A number in the MBAP header that pairs a TCP response with its request. |
| Two's complement | The scheme for signed integers; a 16-bit value ≥ 32768 represents a negative number (subtract 65536). |
| Unit address / ID | The number identifying which device a request is for (the serial address; the TCP unit ID). |
| Word order | Which register holds the high half of a multi-register value; not universally agreed, a common source of garbled values. |
| Zero mode | A pymodbus server setting making client address N map directly to slot N (clean zero-based addressing). |
No terms match that filter.