Modbus Addressing: 0-Based vs 1-Based (The 40001 Trap)

If you have ever typed 40001 into QModMaster’s Start Address field and gotten nothing back — no data, a timeout, or an “illegal data address” exception — you have hit the most common addressing mistake in Modbus. It is not a bug in QModMaster. It is two numbering systems colliding, and it bites almost every beginner.

QModMaster is a free, open-source (LGPLv3) Modbus RTU/TCP master simulator for Windows and Linux — grab it from the download page if you haven’t already. Its Start Address field always expects the protocol address, which is 0-based. Most device manuals print something else entirely.

Protocol address vs. register number

Modbus has two ways to refer to the same register. Confusing them is the entire trap:

  • Protocol address (0-based): the number that actually goes on the wire inside the request frame. The first register of each type is address 0.
  • Register number (1-based): the number printed in device manuals, following the old Modicon convention. The first holding register is 40001.

The Modicon scheme tags each register type with a leading digit:

Register typeNumber rangeRead withWrite with
Coils00001–09999FC 01FC 05 / 15
Discrete inputs10001–19999FC 02— (read-only)
Input registers30001–39999FC 04— (read-only)
Holding registers40001–49999FC 03FC 06 / 16

QModMaster uses 0-based addresses

The Start Address field in QModMaster always takes the protocol address. When your manual says “oil temperature is in holding register 40001“, you enter 0 — not 40001.

Manual saysEnter in Start AddressMath
Holding Register 40001040001 − 40001
Holding Register 40002140002 − 40001
Holding Register 40010940010 − 40001
Input Register 30001030001 − 30001 (use FC 04)
Coil 0000101 − 1 (use FC 01)
Discrete Input 10001010001 − 10001 (use FC 02)

Some manuals — PLCs especially — use 6-digit numbering (400001 instead of 40001). Same rule: strip the leading type digit(s) and subtract, so 400001 becomes address 0.

The ±1 offset fix

Even with the conversion right, some devices are genuinely off by one — their firmware numbers the first register as 1, or the manual’s table is shifted. If reads return the wrong values, or fail with an illegal-data-address exception, do this:

  • Pick one register whose value you know — a device ID, serial number, or firmware version straight from the manual.
  • Read it at the converted address. If it fails or the value is wrong, add 1 to Start Address and read again.
  • Still wrong? Subtract 1 from the original instead.
  • The moment the known register reads correctly, the offset is confirmed — apply it to every address on that device.

This works identically on Modbus RTU and Modbus TCP. Addressing lives in the Modbus application layer; the transport only changes the framing.

Frequently asked questions

Why does my manual say register 40001 but QModMaster reads nothing at address 40001?

Because 40001 is the register number, not the protocol address. On the wire, holding registers live at addresses 0–9998, so a request for address 40001 points far outside the valid block. The device answers with exception code 02 (illegal data address) or simply times out. Enter 0 instead.

My manual uses 6-digit numbers like 400001. What do I enter?

The same rule with one more digit: protocol address = register number − 400001. So 400001 becomes 0, 400002 becomes 1. This extended numbering is common on PLCs with large register maps.

I get values back, but every value is shifted by one register. Why?

Classic off-by-one: the device’s first register is address 1 in its firmware, or the manual’s table starts counting at 1. Shift your Start Address by ±1 and re-read a known register until the values line up, then keep that offset for the whole device.

Does the 0-based vs 1-based issue apply to Modbus TCP too?

Yes. Addressing is part of the Modbus protocol itself, not the serial line. RTU and TCP use identical protocol addresses — only the framing differs (raw serial bytes vs. the MBAP header).

Related Guides

Similar Posts