04 — ADDRESSES & REGISTERS

4 separate address spaces, all independent.

Estimated 7 minutes.

Four address spaces, not one.

Modbus separates its data into four independent pools: coils, discrete inputs, input registers, holding registers. Each has its own 16-bit address range. Coil 5 and holding register 5 are totally different storage.

The classic 1-based labels.

The 0xxxx / 1xxxx / 3xxxx / 4xxxx prefixes you see in vendor docs are a naming convention, not the wire address.

On the wire, addresses are 0-based 16-bit numbers.

The prefix in the docs (0/1/3/4) is just a hint about which space you mean. On the actual Modbus wire every address is a plain 0-based 16-bit integer, and the function code disambiguates which space you are talking to.

Human vs wire address.

Vendor says "Coil 1". On the wire you send 0x0000 in an FC1 request.

Holding register 40001 = wire address 0x0000.

When a vendor manual says "40001", it usually means: holding register space (the 4 in front) + 1-based index 1, which becomes wire address 0 when you actually transmit. The "4" never appears on the wire — only the function code (FC3) tells the slave it is a holding read.

Map the slave.

Probe the slave's address space. Pick a table (FC1/FC2/FC3/FC4) and click any of 32 addresses. Mapped addresses reveal what they hold; unmapped ones throw 0x02 illegal data address. Map at least one valid address in each of the four tables.

Each space is 65,536 entries.

The address field on the wire is 16 bits, so each space tops out at 65,536 entries — way more than any realistic project. You will run out of wall budget long before you run out of addresses.

Read register 40005.

A vendor doc says: "Read register 40005." What function code and wire address do you send?

  • FC3, address 40005
  • FC3, address 4
  • FC4, address 4
  • FC1, address 5

The spaces are independent.

Coil 0 and holding register 0 are entirely separate storage. Writing to one does not change the other. A slave can have any subset of the four spaces — many simple devices only expose holding registers, for example.

Write to holding, read from coil.

You write 0x0001 to holding register 0. Then you read coil 0. What do you get?

  • 1, because the value carries across
  • Whatever coil 0 was — the spaces are independent
  • An exception, because writing to a holding affects the coils

You can translate any vendor address.

  • Four spaces: coils (0xxxx), discrete inputs (1xxxx), input registers (3xxxx), holding registers (4xxxx).
  • On the wire: 0-based 16-bit address. The function code picks the space.
  • "40005" → FC3 at wire address 4.
  • Spaces are independent — overlap of indices does not mean overlap of storage.

Lesson 04 complete.

  • You can map any vendor-style label to the right function + wire address.
  • You know why the prefix never travels on the wire.
  • You can explain why "coil 0" and "holding register 0" are not related.