The 19 function codes and diagnostics
In this tutorial you run every function code in one headless simulator. It’s configured as an RTU server but never connected, so you need no serial port. RTU lets you run the serial-line-only functions (07, 08, 11, 12 and 17); in a TCP configuration they return exception 01.
Every result is a local result of execute. To send the same PDUs to a real device, use request on a client.
- Prepare a model with the four tables, file records, a FIFO queue and device identification objects.
- Check the request and response bytes of all 19 function codes.
- See the diagnostics counters, listen-only mode and recovery, broadcasts and requests for other units.
- Tell apart requests that cause exceptions 01, 02 and 03.
What you need
Section titled “What you need”- The PDComm Modbus SDK and the
MODBUS_SDKvariable (see the quickstart) - One terminal
1. Prepare the model
Section titled “1. Prepare the model”Start the headless simulator:
"$MODBUS_SDK/bin/pdcomm-modbus-headless"Configure an RTU server and replace the whole model. Enter each command on one line:
{"action":"configure","config":{"transport":"rtu","role":"server","address":"127.0.0.1:0","port":"","unit":1,"baud":19200,"parity":"even","stop_bits":1,"timeout_ms":1000}}{"action":"model_set","model":{"coils":{"start":0,"values":[0,0,0,0,0,0,0,0]},"discrete_inputs":{"start":0,"values":[1,0,1,0,0,0,0,0]},"holding_registers":{"start":0,"values":[42,4660,0,0]},"input_registers":{"start":0,"values":[123,0,0,0]},"files":{"1":[0,0,0,0]},"fifos":{"0":[18,52]},"objects":{"0":[80,68,67],"1":[77,66],"2":[49]},"server_id":[1,255],"exception_status":5,"individual_access":true}}model_set returns {"ok":true,"result":{"updated":true}}. The model contains:
| Item | Value |
|---|---|
| Coils 0–7 | All 0 |
| Discrete inputs 0–7 | 1, 0, 1, 0, 0, 0, 0, 0 |
| Holding registers 0–3 | 42, 4660 (0x1234), 0, 0 |
| Input registers 0–3 | 123, 0, 0, 0 |
| File 1 | Records 0–3, all 0 |
| FIFO 0 | 18, 52 |
| Device identification objects | 0 = PDC, 1 = MB, 2 = 1 |
| Server ID | 01 FF (ID 1, running) |
| Exception status | 5 |
model_setneeds every member; a missing one fails with an error such asmissing field `discrete_inputs`.- A value such as 2 in a bit table fails with
Bit table values must be zero or one. - Device identification objects 0, 1 and 2 are mandatory, and the reserved range 7–127 isn’t allowed. Violations fail with
Invalid file, FIFO or device identification model. - A failed
model_setleaves the existing model unchanged.
2. Run the 19 function codes
Section titled “2. Run the 19 function codes”Run the requests in this table from top to bottom, each as {"action":"execute","pdu":"<request PDU>"}. Responses are arrays of decimal bytes; run them in order to get the same values.
| Step | Function | Request PDU | response |
Notes |
|---|---|---|---|---|
| 1 | 01 Read Coils | 0100000001 |
[1,1,0] |
Coil 0 is off. |
| 2 | 02 Read Discrete Inputs | 0200000003 |
[2,1,5] |
1, 0, 1 packed as 0b101. |
| 3 | 03 Read Holding Registers | 0300000001 |
[3,2,0,42] |
|
| 4 | 04 Read Input Registers | 0400000001 |
[4,2,0,123] |
|
| 5 | 05 Write Single Coil | 050000FF00 |
[5,0,0,255,0] |
Echoes the request. |
| 6 | 01 Read Coils | 0100000001 |
[1,1,1] |
Coil 0 is on now. |
| 7 | 06 Write Single Register | 0600000009 |
[6,0,0,0,9] |
|
| 8 | 03 Read Holding Registers | 0300000001 |
[3,2,0,9] |
|
| 9 | 07 Read Exception Status | 07 |
[7,5] |
The model’s exception_status |
| 10 | 08 Diagnostics, sub-function 0 | 0800001234 |
[8,0,0,18,52] |
Echoes the data. |
| 11 | 11 Get Comm Event Counter | 0B |
[11,0,0,0,9] |
Status 0000 (not busy), event count 9 |
| 12 | 12 Get Comm Event Log | 0C |
[12,29,0,0,0,9,0,12,128,64,...] |
Byte count, status, event count, message count, recent events |
| 13 | 15 Write Multiple Coils | 0F000000010100 |
[15,0,0,0,1] |
Writes 0 to coil 0. |
| 14 | 16 Write Multiple Registers | 100000000102000A |
[16,0,0,0,1] |
Writes 10 to holding register 0. |
| 15 | 17 Report Server ID | 11 |
[17,2,1,255] |
Byte count 2, the model’s server_id |
| 16 | 20 Read File Record | 140706000100000001 |
[20,4,3,6,0,0] |
File 1, record 0, one word |
| 17 | 21 Write File Record | 1509060001000000010009 |
Same as the request | Writes 9 to file 1, record 0. |
| 18 | 20 Read File Record | 140706000100000001 |
[20,4,3,6,0,9] |
The written value |
| 19 | 22 Mask Write Register | 160000FF000055 |
Same as the request | (10 AND FF00) OR (0055 AND NOT FF00) = 85 |
| 20 | 03 Read Holding Registers | 0300000001 |
[3,2,0,85] |
|
| 21 | 23 Read/Write Multiple Registers | 170000000200000001020009 |
[23,4,0,9,18,52] |
Writes 9 to register 0, then reads 0–1. |
| 22 | 24 Read FIFO Queue | 180000 |
[24,0,6,0,2,0,18,0,52] |
Byte count 6, count 2, values 18 and 52. Reading leaves the queue intact. |
| 23 | 43/14 Read Device Identification | 2B0E0100 |
[43,14,1,129,0,0,3,0,3,80,68,67,1,2,77,66,2,1,49] |
Basic objects 0–2 |
| 24 | 43/14, individual access | 2B0E0401 |
[43,14,4,129,0,0,1,1,2,77,66] |
Object 1 only |
Two responses decoded:
- 11 Get Comm Event Counter: the event count is the number of successfully processed messages other than the diagnostics management functions (08, 11, 12): the nine earlier requests other than 08.
- 43/14:
14 01is MEI 14, read code 1.129(0x81) is the conformity level: basic level 1 plus the individual-access bit0x80.0 0means no further page and the next object ID, and3is the number of objects. Each object follows as ID, length and value.
3. Read the diagnostics counters
Section titled “3. Read the diagnostics counters”Put the test value 0x1234 into the diagnostic register, then run the sub-functions:
{"action":"diagnostics_set","register":4660,"busy":false}| Request PDU | Response | Notes |
|---|---|---|
0800020000 |
[8,0,2,18,52] |
Diagnostic register 0x1234 |
08000B0000 |
[8,0,11,0,27] |
Bus message count; depends on the requests in this session |
08000E0000 |
[8,0,14,0,28] |
Messages addressed to this server |
08000D0000 |
[8,0,13,0,1] |
Exception responses sent |
08000A0000 |
[8,0,10,0,0] |
Clears the counters and the diagnostic register. |
08000B0000 |
[8,0,11,0,1] |
Only this request has been counted since the clear. |
0800030A00 |
[8,0,3,10,0] |
Sets the ASCII end character to LF (0x0A). The response still uses the previous one. |
0800140000 |
[8,0,20,0,0] |
Clears the overrun counter and flag. |
The counters are 16 bits wide and wrap from 65535 to 0. Their values depend on the requests processed, so check increments and resets rather than fixed numbers. {"action":"diagnostics"} returns all counters and the event log.
4. Enter and leave listen-only mode
Section titled “4. Enter and leave listen-only mode”{"action":"execute","pdu":"0800040000"}{"action":"execute","pdu":"060000002A"}{"action":"diagnostics"}{"ok":true,"result":{"response":null,"source":"local","status":"no_response"}}{"ok":true,"result":{"response":null,"source":"local","status":"no_response"}}{"ok":true,"result":{"counters":{...,"no_responses":2,...},...,"listen_only":true,"register":0}}- The request that enters listen-only mode gets no response (
no_response). - The following write also gets no response and isn’t applied.
Recover with a restart request, which gets no response either:
{"action":"execute","pdu":"0800010000"}{"action":"diagnostics"}{"action":"execute","pdu":"0300000001"}{"ok":true,"result":{"response":null,"source":"local","status":"no_response"}}{"ok":true,"result":{"counters":{"bus_messages":0,...},...,"listen_only":false,"register":0}}{"ok":true,"result":{"response":[3,2,0,9],"source":"local","status":"success"}}listen_only is false again and the counters are cleared. The 42 sent during listen-only mode wasn’t applied, so the value is 9. On a real serial line the recovery request gets no response either, so don’t treat its timeout as a failed recovery: confirm with a read.
5. Send a broadcast and a request for another unit
Section titled “5. Send a broadcast and a request for another unit”{"action":"execute","unit":0,"pdu":"060000002A"}{"action":"execute","pdu":"0300000001"}{"action":"execute","unit":5,"pdu":"0300000001"}{"ok":true,"result":{"response":null,"source":"local","status":"no_response"}}{"ok":true,"result":{"response":[3,2,0,42],"source":"local","status":"success"}}{"ok":true,"result":{"response":null,"source":"local","status":"no_response"}}- A write to unit 0 is a broadcast: no response, but the value changed.
- Unit 5 isn’t this server’s address (unit 1), so it doesn’t answer, just as it ignores requests for other servers on a serial bus.
- A broadcast read can’t return a value; broadcasts are for writes only. An RTU or ASCII client refuses to send a read to unit 0 and returns the
InvalidConfigerror.
6. Tell exception responses apart
Section titled “6. Tell exception responses apart”| Request PDU | Response | Exception | Reason |
|---|---|---|---|
0300000000 |
[131,3] |
03 | Quantity 0 is outside 1–125. |
0F0000000101 |
[143,3] |
03 | The byte count says 1, but the data byte is missing. |
03FFFF0001 |
[131,2] |
02 | Address 65535 isn’t in the model. |
1800FF |
[152,2] |
02 | FIFO 255 isn’t in the model. |
2B0D00 |
[171,1] |
01 | The default server doesn’t handle MEI 13. |
5A |
[218,1] |
01 | Function code 90 isn’t supported. |
Finally, run a diagnostics request in a TCP configuration:
{"action":"disconnect"}{"action":"configure","config":{"transport":"tcp","role":"server","address":"127.0.0.1:0","port":"","unit":1,"baud":19200,"parity":"even","stop_bits":1,"timeout_ms":1000}}{"action":"execute","pdu":"0800001234"}{"ok":true,"result":{"response":[136,1],"source":"local","status":"exception"}}A TCP server answers serial-line-only functions with exception 01. To test them, go back to an RTU or ASCII configuration.
7. Clean up
Section titled “7. Clean up”Enter {"action":"shutdown"}. None of these commands caused a service error, so the exit code is 0. To start again with the same model, enter the two commands of step 1 in a new session, or save them as a project file.
Next steps
Section titled “Next steps”- In RTU and ASCII communication, send the same requests over a serial line.
- PDU, ADU and function codes lists the limits and specification sections of each function code.