# Module 4: Function Codes

> Reads, writes, diagnostics, and the codes you'll actually meet in the field.

**Phase:** Foundations  
**Estimated time:** 8 min  
**Release status:** Published  
**Content version:** 1.0.0  
**Source:** https://learnmodbus.studioseventeen.io/lesson/function-codes

## What you'll be able to do
- Recite the four read codes and the four write codes from memory.
- Pick FC 03 vs FC 04 vs FC 06 vs FC 16 for a given task.
- Parse an exception PDU into function code + exception code.

## Three things people get wrong
1. **Using FC 06 for two registers.** FC 06 writes one register only. FC 16 writes a contiguous block in one request, but device-level atomic behavior still requires vendor documentation or verified lab evidence.
2. **Sending FC 02 to a holding-register map.** Wrong table. Use FC 03 (holding) or FC 04 (input).
3. **Ignoring the 0x80 bit on the response FC.** Any returned FC ≥ 0x80 is an exception — read the next byte for the cause.

## From the field
**The device that lied about its function codes**

A cheap meter advertised support for FC 03 and FC 04 but actually mirrored the same registers behind both. Integrators kept asking why the 'input' values matched the 'holding' values. The capture showed identical byte sequences on either FC. Trust the bus, not the brochure.

## References
- [Modbus Application Protocol V1.1b3](https://www.modbus.org/file/secure/modbusprotocolspecification.pdf) — Function code definitions — section 6

## Lesson

# Module 4 Lesson: Function Codes
## Learner Outcome

By the end of this module, learners can select and interpret the most common public Modbus function codes for reading and writing coils and registers, recognize function-code exception behavior, and explain why write echoes are not physical proof.

## Key Concepts

- Function codes are one-byte operation selectors.
- Common read functions target a table and a quantity.
- Common write functions target writable coils or holding registers.
- Devices do not have to support every public function code.
- Exception responses use the requested function code plus `0x80`.
- A successful write response proves protocol acceptance, not the intended physical process state.

Related foundations:

- [Module 2: The Modbus Message Model](https://learnmodbus.studioseventeen.io/lesson/message-model)
- [Module 3: Data Tables](https://learnmodbus.studioseventeen.io/lesson/data-tables)

## Key Terms

- Public function code: a standardized operation code defined for common Modbus actions.
- Read function: a function that requests coils, discrete inputs, input registers, or holding registers.
- Write function: a function that requests a server to change coil or register state.
- Write echo: the normal response pattern for many writes; useful protocol evidence but not physical process proof.
- Quantity limit: the maximum number of coils or registers a specific function can request.
- Safety boundary: the course rule that write-capable examples stay simulator-only or formally reviewed before real-device use.

## Source-Grounded Explanation

### Common Read Functions

| Decimal | Hex | Name | Logical Table | Request Shape | Normal Response Shape |
|---:|---:|---|---|---|---|
| 1 | `0x01` | Read Coils | Coils | starting address + quantity | function + byte count + packed bits |
| 2 | `0x02` | Read Discrete Inputs | Discrete inputs | starting address + quantity | function + byte count + packed bits |
| 3 | `0x03` | Read Holding Registers | Holding registers | starting address + quantity | function + byte count + register bytes |
| 4 | `0x04` | Read Input Registers | Input registers | starting address + quantity | function + byte count + register bytes |

The function code selects the logical table. The PDU address selects the offset inside that table. A value being an "input" to a dashboard does not mean function `04` is correct; the source map must identify the table.

### Common Write Functions

| Decimal | Hex | Name | Logical Table | Request Shape | Normal Response Shape |
|---:|---:|---|---|---|---|
| 5 | `0x05` | Write Single Coil | Coils | address + output value | echo function + address + value |
| 6 | `0x06` | Write Single Register | Holding registers | address + register value | echo function + address + value |
| 15 | `0x0F` | Write Multiple Coils | Coils | start + quantity + byte count + packed values | echo function + start + quantity |
| 16 | `0x10` | Write Multiple Registers | Holding registers | start + quantity + byte count + register values | echo function + start + quantity |

Advanced holding-register write-capable functions such as mask write register `22` and read/write multiple registers `23` exist, but they are not the core Module 4 write set. Treat them as advanced and safety-relevant until covered with specific source evidence.

### Exception Behavior

If a server returns an exception response, the response function byte is the requested function code plus `0x80`.

| Requested Function | Exception Function Byte |
|---:|---:|
| `01` | `81` |
| `03` | `83` |
| `06` | `86` |
| `10` | `90` |

Useful early exceptions:

- `01` Illegal Function: the server does not support the requested function in that context.
- `02` Illegal Data Address: the requested address or range is not available for that function/context.
- `03` Illegal Data Value: a value, quantity, byte count, or request structure is not acceptable.
- `04` Server Device Failure: the server failed while attempting the requested action.

Timeout/no usable response is not an exception response. It means the client did not receive a valid Modbus response.

### Common Quantity Limits

Common quantity limits come from the official application protocol and the 253-byte PDU size:

| Function | Maximum Quantity |
|---|---:|
| Read Coils `01` | 2000 bits |
| Read Discrete Inputs `02` | 2000 bits |
| Read Holding Registers `03` | 125 registers |
| Read Input Registers `04` | 125 registers |
| Write Multiple Coils `15` | 1968 bits |
| Write Multiple Registers `16` | 123 registers |

Learners should use the quick reference rather than diagnosing oversized requests as network problems.

Advanced/deferred note: Read/Write Multiple Registers `23` has official limits of 125 registers read and 121 registers written, but it is not part of the core Module 4 function-code set.

### Write Safety Boundary

A write response echo means the server accepted the Modbus write transaction. It does not prove that a pump started, a drive changed speed, a relay moved, or an interlock allowed the command to affect the process.

Real-device write behavior can depend on local/remote mode, interlocks, operating state, reserved bits, write-enable registers, command confirmation patterns, or separate status feedback. Module 4 introduces that boundary; Module 15 handles write safety in depth.

## Practical Example: Simulator-Only Setpoint Write

Synthetic teaching fixture:

| Example Field | Value |
|---|---|
| Source label | Synthetic teaching fixture |
| Transport | PDU-only worksheet or simulator after toolchain verification |
| Function code | `06` Write Single Register |
| Documented address | `40020` |
| PDU address | `0x0013` |
| Quantity | One 16-bit register |
| Data type | Unsigned 16-bit |
| Scale/unit | RPM in this fixture |
| Desired value | `215` RPM |
| PDU request | `06 00 13 00 D7` |
| Expected normal response | Echo of `06 00 13 00 D7` |
| Likely exception | `86 02` for illegal data address; `86 03` for unacceptable value |
| Required confirmation | Separate read or status feedback in simulator; no real-device write in this module |

Read/write comparison:

```text
Read holding register:
03 00 13 00 01

Write one holding register:
06 00 13 00 D7
```

The address is the same because both operations target the holding-register table. The function code changes the operation.

Simulator confirmation step:

```text
Read back the same simulator holding register after the write.
Record the returned raw value and compare it to the requested value.
```

This confirms simulator state for the exercise. It still does not authorize real-device writes.

## Common Pitfalls

- Choosing function `04` because a value is an input to software instead of checking the source map.
- Treating `83 02` as a normal response from function `83`.
- Assuming exception `02` means the device is dead. It means a responder returned illegal data address.
- Treating timeout/no response as an exception.
- Assuming support for `16` because `06` works.
- Writing to a holding-register row just because holding registers are write-capable as a table.
- Trusting write echo as physical process proof.

## Check-Your-Understanding

1. Which function reads holding registers?
2. Which function writes one coil?
3. Which function writes one holding register?
4. If function `03` receives an exception response, what function byte appears?
5. What is the difference between exception `83 02` and a timeout?
6. Why does a write lab use a simulator before real equipment?

## Source Notes

- Official basis: Modbus Application Protocol Specification for function-code ranges, common public function behavior, normal response shape, exception response shape, and common quantity limits.
- Official citation targets: [official-citation-map.md](https://learnmodbus.studioseventeen.io/resource/official-citation-map) rows for public function-code categories, maximum Modbus PDU size, read quantity limits, write single coil/register shape and echo boundary, write multiple coils/registers quantity limits, mask write register, read/write multiple registers, normal response vs exception response, and exception response PDU shape.
- Local course spine: `modbus-wiki.md` sections "Function Codes", "Common Quantity Limits", "Exception Responses", and "Write Semantics and Control Registers".
- Safety boundary: `release/write-function-risk-reference.md` and Module 15 expand real-device write controls.
- Synthetic basis: request/response examples are course-authored fixtures unless later verified against a simulator.

## Completion Checkpoint

Before moving on, learners should choose the likely function code for a read or write scenario, label the expected normal response shape, and explain why a write response is not evidence that the physical process reached the intended state.

## Reusable Assets

- [Function-code quick reference](https://learnmodbus.studioseventeen.io/resource/function-code-quick-reference)
- [Exception-code quick reference](https://learnmodbus.studioseventeen.io/resource/exception-code-quick-reference)
- [Write-function risk reference](https://learnmodbus.studioseventeen.io/resource/write-function-risk-reference)
- [Module 4 function-code lab](https://learnmodbus.studioseventeen.io/lesson/function-codes?page=lab-1)
- [Module 4 quiz](https://learnmodbus.studioseventeen.io/lesson/function-codes?page=quiz)

## Diagram source

```mermaid
flowchart LR
  R["Reads"] --> R1["01 Read Coils"]
  R --> R2["02 Read Discrete Inputs"]
  R --> R3["03 Read Holding Regs"]
  R --> R4["04 Read Input Regs"]
  W["Writes"] --> W1["05 Write Single Coil"]
  W --> W2["06 Write Single Reg"]
  W --> W3["15 Write Multi Coils"]
  W --> W4["16 Write Multi Regs"]
```

## Labs

### Lab 1

# Lab 16: Function-Code Decode Drill
Supporting modules: 2, 3, 14, 15
Estimated time: 40-60 minutes
Release state: draft; course-owned localhost simulator smoke test passed for write echo/readback; third-party tool, final source, and safety review pending

## Learner Outcome

Learners identify common read/write function codes, classify normal and exception responses, and separate write echo from independent state confirmation.

## Prerequisites

- Module 2 response/exception labeling.
- Module 3 data-table classification.
- [Function-code quick reference](https://learnmodbus.studioseventeen.io/resource/function-code-quick-reference)
- [Exception-code quick reference](https://learnmodbus.studioseventeen.io/resource/exception-code-quick-reference)
- [Write-function risk reference](https://learnmodbus.studioseventeen.io/resource/write-function-risk-reference)

## Safety And Scope

This is a no-hardware/simulator-first lab. Do not write to real devices. Write examples are synthetic fixtures and safety prompts. The course-owned smoke simulator verifies the local write echo/readback path, but that does not authorize any real-device write.

## Decode Cards

### Card A: Read Holding Register

```text
03 00 13 00 01
```

| Field | Expected answer |
|---|---|
| Source label | Synthetic teaching fixture |
| Operation | Read Holding Registers |
| Function | `03` |
| Table | Holding registers |
| PDU address | `0x0013` |
| Quantity | `1` register |
| Expected normal response | `03` + byte count + two data bytes |

### Card B: Write Single Register

```text
06 00 13 00 D7
```

| Field | Expected answer |
|---|---|
| Source label | Synthetic write fixture |
| Operation | Write Single Register |
| Function | `06` |
| Table | Holding registers |
| PDU address | `0x0013` |
| Value | `0x00D7`, decimal `215` |
| Expected normal response | Echo of `06 00 13 00 D7` |
| Safety boundary | Write echo is not physical state proof |

### Card C: Exception To Read Holding Register

```text
83 02
```

| Field | Expected answer |
|---|---|
| Source label | Synthetic teaching fixture |
| Original function | `03` |
| Response type | Exception response |
| Exception code | `02`, illegal data address |
| Meaning | A responder parsed enough to reject the requested address/range |

### Card D: Write Single Coil

```text
05 00 00 FF 00
```

| Field | Expected answer |
|---|---|
| Source label | Synthetic write fixture |
| Operation | Write Single Coil |
| Function | `05` |
| Table | Coils |
| PDU address | `0x0000` |
| Value | `FF 00` means ON for this standard write-coil request shape |
| Safety boundary | Simulator only; real commands require Module 15 review |

### Card E: Timeout Observation

The client sends function `03` and receives no usable response.

| Field | Expected answer |
|---|---|
| Response type | Timeout/no usable response |
| Is it exception `83 02`? | No |
| First checks | Path, wrapper, serial settings, server address, Unit Identifier/gateway route, malformed frame, silent discard |

### Card F: Simulator Readback Confirmation

After Card B, the learner performs a separate simulator readback:

```text
03 00 13 00 01
```

The simulator response returns raw value `00 D7`.

| Field | Expected answer |
|---|---|
| Source label | Synthetic simulator-planned fixture |
| Purpose | Confirm simulator state after a write |
| Function | `03` Read Holding Registers |
| Readback value | `0x00D7`, decimal `215` |
| Boundary | Simulator readback confirms this exercise only; it does not authorize or prove real-device physical state |

Course-owned smoke transcript:

```text
write_request_pdu: 06 00 13 00 D7
write_response_pdu: 06 00 13 00 D7
readback_request_pdu: 03 00 13 00 01
readback_response_pdu: 03 02 00 D7
```

Learner-style command evidence is available through:

```bash
python3 tools/modbus_tcp_smoke.py cli-transcript
```

## Procedure

1. Identify each function code.
2. Name the target table.
3. Label request fields: address, quantity, value, byte count, or exception code.
4. Classify response cases as normal, exception, or timeout/no usable response.
5. Mark any write case as simulator-only until safety review.
6. Answer the check questions.

## Expected Observations

- Read requests identify table and quantity through the function code plus address/count fields.
- Write single-register and write single-coil normal responses echo the request fields, but that echo is not physical process proof.
- Exception cards prove that a responder parsed enough of the request to reject it with a Modbus exception.
- Timeout/no usable response remains separate from exception handling and should move learners into transport/path troubleshooting.
- The simulator readback fixture confirms only the synthetic simulator state for this exercise.

## Troubleshooting Notes

- If learners treat a write echo as final state evidence, require a separate readback and an explicit simulator-only boundary.
- If learners choose function `04` for all measurements, redirect them to the source map and table evidence.
- If a timeout occurs, verify path, wrapper, server address, Unit Identifier, framing, and checksum before changing the function code.
- If an exception appears, check requested table, address range, quantity, and function support before diagnosing byte order or scale.

## Check Questions

1. Which cards are write-capable operations?
2. Which card proves a responder returned a Modbus exception?
3. Which card proves no usable response arrived but does not prove exception `02`?
4. What separate evidence would confirm a simulator write changed state?
5. Why is function `04` not automatically correct for every measurement?

## Instructor Notes

- Keep this lab at PDU/function-code level; transport checks belong to Modules 11 and 12.
- Use Card B and Card D to preview Module 15 write-safety stops.
- Require learners to say "write echo" rather than "the machine changed."
- Use Card F to distinguish simulator readback evidence from real-device physical proof.
- If learners diagnose timeout as wrong address first, redirect them to Module 14's no-response branch.

## Source Notes

- Official protocol basis: Modbus Application Protocol Specification for function-code behavior, response shape, and exception response shape.
- Synthetic basis: all byte strings and the localhost smoke simulator path are course-authored fixtures.
- Safety basis: real-device writes are out of scope until Module 15 safety review.

## Knowledge check

# Module 4 Quiz: Function Codes
## Questions

1. Which function reads holding registers?
   A. `01`
   B. `02`
   C. `03`
   D. `05`

2. Which function writes one coil?
   A. `04`
   B. `05`
   C. `06`
   D. `16`

3. Which function writes one holding register?
   A. `02`
   B. `03`
   C. `05`
   D. `06`

4. A function `03` request receives `83 02`. What does `83` mean?
   A. A normal function `83` response.
   B. Exception response to function `03`.
   C. TCP port 83.
   D. A write confirmation.

5. What does exception code `02` usually indicate?
   A. Illegal function.
   B. Illegal data address.
   C. Server device busy.
   D. A successful write.

6. The client receives no usable response before timeout. Which statement is correct?
   A. The server returned exception `02`.
   B. Timeout is a client observation, not a Modbus exception response.
   C. The value should be scaled differently.
   D. Function `04` should always be used next.

7. A write single register response echoes the requested value. What does that prove?
   A. The physical machine definitely changed state.
   B. The server accepted the Modbus write transaction at the protocol level.
   C. The value is safe on any real device.
   D. The register map is no longer needed.

8. Why should real-device writes not be part of Module 4?
   A. Modbus has no write functions.
   B. Write behavior can affect physical processes and needs Module 15 safety review, source evidence, and independent feedback.
   C. Function codes do not apply to writes.
   D. Holding registers are always read-only.

## Answer Key

1. C. Function `03` reads holding registers. `01` and `02` read one-bit tables, and `05` is a write function for a single coil.
2. B. Function `05` writes one coil. `04` reads input registers, `06` writes one holding register, and `16` writes multiple holding registers.
3. D. Function `06` writes one holding register. `03` reads holding registers, while `02` and `05` target different tables or operations.
4. B. Exception responses add `0x80` to the requested function, so `03` becomes `83`. It is not a normal function `83`, a TCP port, or a write confirmation.
5. B. Exception `02` is illegal data address. Illegal function is `01`, and a successful write would use a normal write response rather than an exception.
6. B. Timeout/no usable response is a client observation, not a Modbus exception response. Exception `02` would require a parseable exception response such as `83 02`.
7. B. A write echo proves the server accepted the Modbus write transaction at the protocol level. It does not prove physical state, safety, or that the register map can be ignored.
8. B. Real writes can affect physical processes and require Module 15 safety review, source evidence, and independent feedback. Modbus does have write functions, but Module 4 keeps them simulator-only.

## Distractor Review Notes

| Question | Correct | Why The Distractors Are Wrong |
|---:|---|---|
| 1 | C | A/B read one-bit tables, and D is a single-coil write function rather than a holding-register read. |
| 2 | B | A reads input registers, C writes one holding register, and D writes multiple holding registers. |
| 3 | D | A reads discrete inputs, B reads holding registers, and C writes one coil. |
| 4 | B | A invents a normal function `83`, C confuses the byte with a port number, and D treats an exception as a write confirmation. |
| 5 | B | A is exception `01`, C is a different exception code, and D would use a normal write response rather than exception `02`. |
| 6 | B | A requires a valid exception response, C jumps to value interpretation, and D applies a read function without evidence. |
| 7 | B | A/C/D overstate what the protocol echo proves and skip physical-state, safety, and source-map checks. |
| 8 | B | A/C deny Modbus write functions, and D invents a universal read-only rule for holding registers. |

## Instructor Notes

- Use questions 6 and 7 to separate troubleshooting branches and write-safety boundaries.
- Questions 1-3 should be fast recall; questions 4-8 test application.

## Source Notes

- Official citation targets: [official-citation-map.md](https://learnmodbus.studioseventeen.io/resource/official-citation-map) rows for public function-code categories, read quantity limits, write single coil/register shape and echo boundary, write multiple coils/registers quantity limits, and exception codes and exception response PDU shape.
- Real-device write questions remain safety-gated; normal write echoes are protocol evidence, not proof of physical state.

---
_Learn Modbus · learnmodbus.studioseventeen.io · Module 4/16 · v1.0.0 · Exported for offline / agent use._
