LearnSCADA

Part II · Chapter 8

Function Codes in Depth

A function code is the verb of a Modbus request. Eight of them do nearly all the everyday work, and they follow a pattern simple enough that you never need to memorize them as a list.

By the end of this chapter you can

  • Pick the right function code for any read or write, straight from the data model.
  • Lay out the request and response for the register reads, the bit reads, and the single and multiple writes.
  • Unpack coil bits from a response byte, and use the 0xFF00 / 0x0000 rule for single coils.
  • Recognize the rarer codes when a device manual mentions them.

The eight that matter

The working set falls into an obvious pattern: four codes read the four data tables, and four write the two writable tables. Every readable table has a read function, and every writable table has both a single-item and a multiple-item write. Each code is just "read this table" or "write that table," and its data field follows from the data model of Chapter 2.

CodeNameActs onMax per request
0x01Read CoilsCoils (bits, read)2000 bits
0x02Read Discrete InputsDiscrete inputs (bits, read)2000 bits
0x03Read Holding RegistersHolding registers (read)125 registers
0x04Read Input RegistersInput registers (read)125 registers
0x05Write Single CoilOne coil (bit, write)1 coil
0x06Write Single RegisterOne holding register (write)1 register
0x0FWrite Multiple CoilsMany coils (bits, write)1968 bits
0x10Write Multiple RegistersMany holding registers (write)123 registers

The per-request limits come from the 253-byte PDU ceiling of Chapter 4. A client that needs more simply sends several requests. You'll also see 0x0F and 0x10 written in decimal as FC15 and FC16.

The eight core function codes mapped onto the four data tables. Coils are read with 0x01 and written with 0x05 and 0x0F. Discrete inputs are read with 0x02 and cannot be written. Input registers are read with 0x04 and cannot be written. Holding registers are read with 0x03 and written with 0x06 and 0x10. Each row lights up in turn. READ DATA TABLE WRITE single many Coils 1 bit · read / write Discrete inputs 1 bit · read-only Input registers 16 bits · read-only Holding registers 16 bits · read / write 0x01 0x02 0x04 0x03 0x05 0x0F 0x06 0x10 read-only: no write code read-only: no write code
Figure 8.1 · The eight core codes on the four tables. Each readable table has a read code; each writable table has a single-write and a multiple-write code. Note that 0x03 reads holding registers and 0x04 reads input registers, not the other way round.

Reading registers (0x03 and 0x04)

Read Holding Registers (0x03) and Read Input Registers (0x04) are the codes you'll use most. They're identical in shape and differ only in the table they touch. After the function code, the request carries a two-byte starting address and a two-byte quantity, up to 125 registers.

The response echoes the function code, then a one-byte byte count, then the values, two bytes per register in big-endian order. The byte count is always twice the number of registers. Here is the running example, four holding registers from address 0:

DirectionBytesField
Request03Function code
Request00 00Starting address
Request00 04Quantity (4 registers)
Response03Function code (echoed)
Response08Byte count (4 × 2)
Responsexx xx xx xx xx xx xx xxFour register values

Wrapped in an RTU frame for server 1, with the registers holding 100, 200, 300 and 400:

Address Function code Data CRC
01server 1 03read holding 00 00start 0 00 04qty 4 44 09CRC
01server 1 03echo 08byte count 00 64= 100 00 C8= 200 01 2C= 300 01 90= 400 90 08CRC
Read Holding Registers. The client sends 01 03 00 00 00 04 44 09 asking server 1 for four registers from address 0. Registers 0 to 3 light up in the server, holding 100, 200, 300 and 400. The response 01 03 08 00 64 00 C8 01 2C 01 90 90 08 carries a byte count of 8 and the four values, high byte first. Client asks for 0–3 Server 1 address value 00064 · 100 100C8 · 200 2012C · 300 30190 · 400 40000 · 0 01 03 00 00 00 04 44 09 01 03 08 00 64 00 C8 01 2C 01 90 90 08 08 = byte count: 4 registers × 2 bytes 00 64 → 100 · 00 C8 → 200 · 01 2C → 300 · 01 90 → 400
Figure 8.2 · Read Holding Registers. The request gives a starting address and a quantity; the response echoes the function code, states a byte count, and returns each register as two big-endian bytes.

Notice that the response doesn't repeat the starting address. The client has to remember what it asked for and match the values up itself. The Modbus specification's own example reads three registers from server 17 (0x11), starting at address 107 (reference 40108):

11server 17 03read holding 00 6Bstart 107 00 03qty 3 76 87CRC
11server 17 03echo 06byte count AE 41reg 107 56 52reg 108 43 40reg 109 49 ADCRC

Change the 03 to 04 and the same exchange reads input registers instead. Same shape, different table.

Reading bits (0x01 and 0x02)

Read Coils (0x01) and Read Discrete Inputs (0x02) use the same request shape: function code, starting address, and a quantity, this time a count of bits up to 2000. The response again echoes the function code and gives a byte count, but the bits are packed eight to a byte rather than wasting seven-eighths of the space sending each bit in its own byte.

The packing rule surprises newcomers: within each byte, the first requested coil is the least significant bit. Read eight coils with the first, third, and fourth ON, and the byte is not what you'd write left to right. It's 0x0D, because bits 0, 2, and 3 are set. Bytes fill from the low coil upward: coils 0–7 in the first byte, 8–15 in the next, and unused high bits of the last byte are sent as zero.

Packing coil states into a byte. Eight coils, 0 to 7, are shown left to right; coils 0, 2 and 3 are on. One by one each coil's state drops into the response byte, coil 0 into bit 0 at the right-hand end and coil 7 into bit 7 at the left. The paths cross, and the finished byte reads 0000 1101, which is 0x0D. 8 coils in the order requested (coil 0 first) coil 0coil 1 coil 2coil 3 coil 4coil 5 coil 6coil 7 ONoff ONON offoff offoff The response byte (MSB on the left) bit 7bit 6 bit 5bit 4 bit 3bit 2 bit 1bit 0 1 0 1 1 0 0 0 0 Byte = 0000 1101 = 0x0D
Figure 8.3 · Packing coil states into a byte. The first coil read lands in the least significant bit, so the paths cross. With more than eight coils, the next byte starts again at bit 0, and leftover high bits in the last byte are zero.

The book's example as complete RTU frames, reading coils 0–7 from server 1:

01server 1 01read coils 00 00start 0 00 08qty 8 3D CCCRC
01server 1 01echo 01byte count 0Dcoils 0–7 90 4DCRC

When the count isn't a multiple of eight, the last byte is padded. Reading 10 coils from address 19 needs two bytes: CD holds coils 19–26 and 01 holds coils 27–28 in its two low bits, with bits 2–7 sent as zero.

01server 1 01read coils 00 13start 19 00 0Aqty 10 4D C8CRC
01server 1 01echo 02byte count CDcoils 19–26 01coils 27–28 2C ACCRC

CD is 1100 1101. Read right to left: coil 19 ON, 20 off, 21 and 22 ON, 23 and 24 off, 25 and 26 ON. Read Discrete Inputs (0x02) packs its bits exactly the same way.

Writing one thing (0x05 and 0x06)

To change a single value, use Write Single Coil (0x05) or Write Single Register (0x06). The request carries the function code, the two-byte address of the item, and a two-byte value. Both have a pleasing property: the response is an exact echo of the request, confirming the precise value was written.

Write Single Register is the obvious one: the value bytes are the 16-bit number to store. Write Single Coil has a quirk. A coil is one bit, but the value field is two bytes, and rather than 0x0001 for on, the protocol uses 0xFF00 for ON and 0x0000 for OFF. No other value is valid; a server answers anything else with exception 03, Illegal Data Value. Turning coil 5 on:

DirectionBytesField
Request05Function code
Request00 05Coil address (5)
RequestFF 00Value = ON
Response05 00 05 FF 00Exact echo of the request
01server 1 05write coil 00 05coil 5 FF 00ON 9C 3BCRC
Write Single Coil. The client sends 01 05 00 05 FF 00 9C 3B. When it arrives, coil 5 in the server switches on, and the server sends back exactly the same eight bytes as confirmation. Client coil 5 → ON Server 1 coils 4 5 6 ON 01 05 00 05 FF 00 9C 3B 01 05 00 05 FF 00 9C 3B request response FF 00 = ON · response = byte-for-byte echo ✓
Figure 8.4 · Write Single Coil. The value is 0xFF00 for ON or 0x0000 for OFF, nothing else. The response echoes the request exactly, confirming the write.

Write Single Register works the same way. Setting register 1 to 3 on server 1, which the server echoes back unchanged:

01server 1 06write register 00 01address 1 00 03value 3 98 0BCRC

Writing many things (0x0F and 0x10)

To set a run of items in one shot, use Write Multiple Coils (0x0F) or Write Multiple Registers (0x10). After the function code come the starting address and the quantity, then a one-byte byte count, then the values: packed bits for coils, two bytes per register for registers. The response is short: it echoes the function code, starting address and quantity, confirming how many items were written without repeating the data.

Write Multiple Registers is the one you'll reach for constantly, both to set several parameters at once and, as Chapter 14 shows, to write values too wide for one register. Writing 0x000A and 0x0102 starting at address 16:

DirectionBytesField
Request10Function code
Request00 10Starting address (16)
Request00 02Quantity (2 registers)
Request04Byte count (2 × 2)
Request00 0A 01 02The two values
Response10 00 10 00 02Echo: function, address, quantity
01server 1 10write multiple 00 10start 16 00 02qty 2 04byte count 00 0A= 10 01 02= 258 52 F0CRC
01server 1 10echo 00 10start 16 00 02qty 2 40 0DCRC
Write Multiple Registers. The long request 01 10 00 10 00 02 04 00 0A 01 02 52 F0 travels to server 1. Registers 16 and 17 change from 0000 to 000A and 0102. The short response 01 10 00 10 00 02 40 0D comes back, echoing only the function, starting address and quantity. Client write 16–17 Server 1 address value 150000 16 0000 000A 17 0000 0102 180000 01 10 00 10 00 02 04 00 0A 01 02 52 F0 01 10 00 10 00 02 40 0D 04 = byte count, then the values response: function, start, quantity, no data
Figure 8.5 · Write Multiple Registers. The request carries the starting address, the quantity, a byte count, and the values; the response echoes only the function code, address, and quantity.

Write Multiple Coils (0x0F) has the same shape, with the bits packed exactly as 0x01 returns them. Writing the ten coil states from the read example above back to coils 19–28:

01server 1 0Fwrite coils 00 13start 19 00 0Aqty 10 02byte count CDcoils 19–26 01coils 27–28 72 CBCRC
01server 1 0Fecho 00 13start 19 00 0Aqty 10 24 09CRC
0x06 or 0x10? Some devices accept only 0x10, even for a single register, and some older ones accept only 0x06. If a write comes back with exception 01 (Illegal Function), try the other. Any value wider than 16 bits should be written with 0x10 so both halves change in the same transaction.

A note on the rarer codes

The specification defines a handful of less common codes you may meet occasionally. Treat them as special cases: when you need one, the device manual will show its exact layout, and by then reading a layout will be second nature. Appendix A lists them all.

CodeNameWhat it does
0x07Read Exception StatusReturns one device-defined status byte (serial only; Chapter 9)
0x08DiagnosticsSerial-line tests and counters, chosen by a sub-function (Chapter 9)
0x0BGet Comm Event CounterA status word and a count of successful messages (serial)
0x0CGet Comm Event LogThe counter plus a short log of recent events (serial)
0x11Report Server IDDevice-specific identification and run status (serial)
0x14 / 0x15Read / Write File RecordAccess to grouped "file" records of registers
0x16Mask Write RegisterChanges selected bits of one holding register in place, using AND and OR masks
0x17Read/Write Multiple RegistersA write and a read in one transaction (the write is performed first)
0x18Read FIFO QueueReads a queue of registers from a device FIFO
0x2B / 0x0ERead Device IdentificationVendor name, product code, and revision, as text objects

These can be set aside while learning because the great majority of real integration work (reading measurements, checking status bits, changing settings, commanding outputs) is fully served by the eight core codes.

Request builder

Address: a zero-based number (107, 0x6B) or a 5- or 6-digit reference (40108, 400108, 00012), which is converted for you.

    PDU (function code + data)

    RTU request frame

    Expected response

    Check your understanding

    1. A setpoint is listed at reference 40021. Which function code and address read it?

    4xxxx means holding register, read with 0x03. Drop the leading 4 and subtract one: 40021 → address 20.

    2. You read 8 coils from address 0 and the data byte is 0x0D. Which coils are ON?

    0x0D = 0000 1101. Bits 0, 2 and 3 are set, and the first coil requested sits in bit 0.

    3. A Write Single Coil request carries the value 00 01. What does a compliant server do?

    Only 0xFF00 (ON) and 0x0000 (OFF) are valid for 0x05. Anything else breaks the request's rules, which is exactly what exception 03 reports.

    4. What does a successful Write Multiple Registers (0x10) response contain?

    Multiple writes get a short echo confirming where and how many items were written; the data isn't repeated.