The 3xxxx and 4xxxx labels describe memory areas; the function code describes the wire operation. A 4xxxx holding-register tag reads with code 03 and writes with 06 or 16, a 3xxxx input-register tag reads with 04, and a wrong value usually means the panel's address entry needs the one-based form.
An operator panel on a Modbus RTU link reads the wrong register, or the controller rejects every request, and the first suspect is the function code. The mismatch is usually the notation. The 3xxxx and 4xxxx labels say which memory area a tag points at; the function codes 01, 02, 03, 04, 05, 06 and 16 say what travels on the wire. A 4xxxx holding-register tag reads with function code 03 and writes with 06 or 16. A 3xxxx input-register tag reads with 04 and never writes. Once the two schemes are separated, the wrong-value cases that remain usually come down to an address entered one number below the wire offset. This article gives the mapping, the symptom each fault produces, a worked byte-level example and the check order.
Why does my panel send function code 04 when I selected a write?
It is not sending 04 as a write: function code 04 is the read input registers request, and the panel picks its function code from the memory area you tagged, so a 3xxxx tag reads with 04 and a 4xxxx tag reads with 03.
A Modbus write is never code 04. The MODBUS Application Protocol Specification V1.1b3 lists the writes as 05 (write single coil), 06 (write single register), 15 (write multiple coils) and 16 (write multiple registers), and the reads as 01, 02, 03 and 04. The pattern that looks like a swap, a read-only display sending 04 where you expected 03 and the reverse on the next tag, is the two numbering schemes crossing. Set each tag's memory type from the controller's register table, and the requests come out right: the 4xxxx tag reads with 03 and, when the operator changes an editable value, writes it with 16.
What is the difference between a 4xxxx register and function code 04?
The 4xxxx prefix names a memory area, the holding registers; function code 04 names an operation, read input registers; the two numbering schemes cross over, which is why a 4xxxx read travels as code 03 and a 3xxxx read travels as code 04.
The prefix notation is a documentation convention inherited from legacy controller register tables: 0xxxx coils, 1xxxx discrete inputs, 3xxxx input registers, 4xxxx holding registers. The five-digit reference 40001 names the first holding register, and 40101 names the 101st. A function code is a field inside the request frame itself. The MODBUS Application Protocol Specification V1.1b3 defines the operations: 01 read coils, 02 read discrete inputs, 03 read holding registers, 04 read input registers, 05 write single coil, 06 write single register, 16 write multiple registers. The 3xxxx and 4xxxx data word reference shows how the same registers carry both kinds of label in vendor tables.
Which function code reads and writes each Modbus register area?
Four areas cover the common cases: coils read with 01 and write with 05 or 15, discrete inputs read with 02 and never write, input registers read with 04 and never write, holding registers read with 03 and write with 06 or 16.
| Area | Table label | Read code | Write code | Typical content |
|---|---|---|---|---|
| Coils | 0xxxx | 01 | 05 or 15 | bits an operator toggles |
| Discrete inputs | 1xxxx | 02 | none | limit switches, run feedback |
| Input registers | 3xxxx | 04 | none | meter and counter values |
| Holding registers | 4xxxx | 03 | 06 or 16 | setpoints and editable values |
Read/write register in a panel manual describes the holding-register area, which accepts both operations; code 03 itself is only ever a read. A numeric display set read-only sends one read, 03 for a 4xxxx tag or 04 for a 3xxxx tag. An editable value sends 06 or 16 at the moment the operator commits a new number, and the controller's table tells you whether it accepts that write at that address.
Why is the value wrong even with the right function code?
Count on an off-by-one address convention: the same register is 40001 in one-based notation and offset 0 on the wire, so a panel that expects the reference form needs the entry one above the wire address, as in 1001 to reach register 1000.
The request carries a 16-bit starting address, so the wire counts from 0 to 65535, while the printed reference counts from 1: register 40001 is wire address 0, and 40101 minus 40001 leaves wire address 100. Some editors want the printed reference, some want the bare offset, and the guide to mapping 3xxxx and 4xxxx registers shows both conventions against one table. An entry one off reads the neighboring register, so the display shows a plausible value that belongs to another variable, or the controller answers exception 02, illegal data address, when the offset lands in a gap. To reach wire address 1000, a panel that counts from 1 needs the entry 1001. The invalid HMI address fix covers the same class of error when tagging controller memory.
Which symptom points to which Modbus problem?
Read the controller's response, because each failure mode answers a different question: exception 01 means the operation is wrong for that area, exception 02 means the address is outside the map, a plausible but wrong number means the offset is off by one, and silence means the serial link.
- Exception code 01, illegal function. The operation is wrong for the area: a write aimed at the 3xxxx input-register area, or a 04 aimed at holding registers. Correct the tag's memory type in the panel.
- Exception code 02, illegal data address. The starting address, or address plus quantity, falls outside the controller's map. Check the offset convention and the register's range.
- A plausible number from the wrong variable. The off-by-one entry. Add one to the panel's address and retest.
- Digits that decode to nonsense on a healthy link. The data format is the suspect: signed read as unsigned, BCD read as binary, or two registers combined in the wrong word order. The BCD analog register fix walks that case.
- Silence, CRC errors or framing garbage. The serial link is the suspect: baud, parity, stop bits, station number, wiring, covered in the last section.
The MODBUS specification defines these exception codes: 01 illegal function, 02 illegal data address, 03 illegal data value. Exception 03 says a field in the request, such as the quantity, is out of range while the address itself was valid.
Worked example: what bytes travel for register 1000?
Worked scenario: the controller's map lists register 1000 holding average line current, so the panel entry is 1001, the request carries function code 03, starting address 1000 and quantity 1 register, and the reply's two bytes 0x05C3 decode to 14.75 A.
Say the controller's register table lists: 1000, average line current, 16-bit unsigned, 0.01 A per count. That map entry is an example; your table prints the real reference and scale. The tag gets memory type 4xxxx (holding registers) and quantity 1 register. The panel's address field counts from 1, so the entry is 1001.
The request on the two-wire RS-485 link is: function code 03, starting address 1000 (hex 0x03E8), quantity 1. The reply is: byte count 2, data 0x05 0xC3. Decode it: 0x05C3 = 5 × 256 + 12 × 16 + 3 = 1475 counts, and 1475 × 0.01 A = 14.75 A on the display.
Enter 1000 in that panel and the request reads wire address 999 instead: the display shows whatever the neighbor holds, or the controller returns exception 02 when 999 lands in a gap. The decision: capture the request once, and when the starting address is one below the register you wanted, add one to the entry.
How do I check the request my panel actually sends?
Capture one request and compare three fields: the function code byte, the 16-bit starting address and the quantity, because that comparison settles the memory type, the offset convention and the range in a single read of a register whose value you already know.
- Copy the exact register reference and description from the controller's register table, and mark whether the register is input (3xxxx) or holding (4xxxx).
- Set the panel tag's memory type from that table: 3xxxx reads with 04, 4xxxx reads with 03.
- Set the display object read-only unless operators should change the value.
- Read one register whose value you know, and capture the request: function byte, 16-bit starting address, quantity.
- If the captured starting address sits one below the register you wanted, add one to the panel's entry and retest.
- Only after the read returns the known value, test a write on a spare holding register: the panel sends 06 or 16, and the controller's table confirms it accepts that code there.
Recommendation: Before changing any wiring or serial setting, set the tag's memory type from the controller's table (4xxxx reads with code 03, 3xxxx with 04), capture one read of a register whose value you already know, and add one to the address entry when the captured starting address is one below the register you wanted. Change the function code only when the capture shows the wrong operation for the area.
When is it the RS-485 wiring instead of the register numbers?
Silence, CRC errors and framing garbage are wiring, not addressing: check that baud rate, parity and stop bits match on both ends, that station numbers are unique on the link, and that the two-wire RS-485 pair is continuous with termination at both ends.
A controller that receives a valid request and answers with an exception has a working link, so the fault is in the request and the cable can wait. 9600 baud with even parity and one stop bit is the serial default you meet most often on Modbus links. The Modbus RTU no-reply and address-error fix covers the silent link, and wiring an HMI to a VFD over Modbus RTU shows a complete settings table for a serial link.
The decisions, case by case: a request that carries 04 when you meant a write means the tag sits in the input-register area, so move it to holding registers and the write goes out as 06 or 16. Exception 01: correct the memory type. Exception 02 or a plausible value from the wrong variable: add one to the address entry. Silence: fix the serial link before touching another register.
Questions
How many registers can one Modbus read request return?
The MODBUS specification caps function codes 03 and 04 at 125 registers per request, written 0x007D in the protocol fields. The response carries one byte count followed by two bytes per register, so a full read returns 250 data bytes. A numeric display usually asks for 1 register and gets 2 bytes, and a 32-bit value needs two adjacent registers read together in one request.
Does the 3xxxx and 4xxxx notation apply over Modbus TCP too?
Yes. The notation and the function codes belong to the protocol's data model, which the specification defines independently of the transport underneath it. Function code 03 reads holding registers over Modbus RTU on two-wire RS-485 and over Modbus TCP on Ethernet alike, and the one-based versus zero-based address trap behaves the same way on both.
What does Modbus exception code 03 tell me?
Exception 03 is illegal data value: the request reached a valid address, but a field in it is out of range for the operation. A quantity of 0 or more than 125 registers on a read is the classic case. The address entry is right in that case, so check the panel's quantity or data-format setting before touching anything else.
Planning information, not legal or engineering advice. Rules differ by place: check codes and permits with your local authority and your licensed trades.