Modbus Address Converter
Translate 0-based, 1-based and 4xxxx Modbus register notations.
This tool translates between the three ways a Modbus register gets named: data-model notation (40011), 1-based addressing (11) and the 0-based protocol address that actually travels on the wire (10). It understands all four entity tables — coils, discrete inputs, input registers and holding registers — in both 5- and 6-digit forms.
How it works
The Modbus protocol transmits a plain 0-based offset; which of the four data tables it indexes is implied by the function code, not the address. Documentation, however, traditionally names points with a table prefix and a 1-based index: 4 for holding registers, 3 for input registers, 1 for discrete inputs, 0 for coils. Both conventions describe the same register, shifted by one and dressed differently — and mixing them silently produces the infamous off-by-one errors.
Paste a documented address like 40011 to get the wire offset, or go the other way from a captured request back to the documentation name. Offsets beyond 9998 only exist in the 6-digit notation, which the tool selects automatically.
Worked example
30001 → input register · protocol offset 0
100005 → discrete input · protocol offset 4
Parameters
| Parameter | Value | Notes |
|---|---|---|
| 0xxxx | coils | read/write bits, FC 01/05/15 |
| 1xxxx | discrete inputs | read-only bits, FC 02 |
| 3xxxx | input registers | read-only 16-bit, FC 04 |
| 4xxxx | holding registers | read/write 16-bit, FC 03/06/16 |
| Wire format | 0-based offset | 0 … 65535, table via function code |
Python implementation
# Data-model notation -> (entity, 0-based protocol address)
TABLES = {0: "coil", 1: "discrete input", 3: "input register", 4: "holding register"}
def parse_data_model(addr: str):
table, offset = int(addr[0]), int(addr[1:])
return TABLES[table], offset - 1 # 1-based -> 0-based
assert parse_data_model("40011") == ("holding register", 10)
assert parse_data_model("30001") == ("input register", 0)FAQ
▸Why does register 40011 go on the wire as address 10?
40011 is data-model notation: the leading 4 identifies a holding register and the remaining digits are a 1-based index (the 11th register). The protocol itself transmits a 0-based offset with the entity implied by the function code — so 40011 → offset 11−1 = 10, i.e. 0x000A in the request.
▸What do the leading digits 0, 1, 3 and 4 mean?
They encode the entity table: 0xxxx coils (read/write bits), 1xxxx discrete inputs (read-only bits), 3xxxx input registers (read-only 16-bit), 4xxxx holding registers (read/write 16-bit). The digit is a naming convention only — it never appears in the frame.
▸Why do 5-digit and 6-digit notations both exist?
5-digit notation (40001–49999) can only name 9999 registers, but the protocol allows 65536. Modern documentation therefore uses 6 digits (400001–465536). The tool shows both, and marks the 5-digit form unavailable when the offset exceeds 9998.
▸My device documentation and my master are off by one — which side is wrong?
Neither: one speaks 1-based (data model) and the other 0-based (protocol). Off-by-one is the single most common Modbus commissioning problem. Convert the documented address here and compare the 0-based value with what your master actually transmits.
▸Is my data uploaded?
No. Conversion happens locally in your browser.