View all lessons
Part 1
Fundamentals
Part 2
Modbus RTU
- Lesson 5RS485 explained: A, B, common and termination20 min
- Lesson 6Baud rate and parity: why 8E1 is the default16 min
- Lesson 7The Modbus RTU frame byte by byte, plus CRC18 min
- Lesson 8Multiple Modbus devices on one RS485 bus16 min
- Lesson 9Read your first Modbus device with mbpoll22 min
- Lesson 10Modbus RTU troubleshooting: symptom to cause20 min
Part 3
Modbus TCP
Part 4
Advanced
- Lesson 16Writing to a Modbus device without breaking it18 min
- Lesson 17Word order and floats: same bytes, other value22 min
- Lesson 18Calculate your poll interval and bus load18 min
- Lesson 19Modbus security: the protocol will not help16 min
- Lesson 20Modbus integration: PLC, Home Assistant, cloud20 min
- Lesson 21Modbus commissioning checklist and cheat sheet18 min
How to read a Modbus datasheet, step by step
Reading a Modbus datasheet is a skill. One line like 30001 float 2 registers hides four decisions, and this lesson teaches you to make all four on purpose.
What this lesson covers
- The four decisions in a line like 30001 float 2 registers
- From raw register value to volts, with scaling and sign
- Numbers that are not a measurement but a sentinel
Read first: Modbus addressing: from 40001 to the wire, Reading exception codes and choosing a function code
Reading a Modbus datasheet is the skill of turning one table row into four decisions: which data block, which numbering, how many registers wide, and which scaling factor. The protocol carries none of that, so the vendor document is the only place it is written down. After this lesson you can read any register map and know, before you poll, whether the number coming back can make sense.
Four decisions hidden in one datasheet line
One line from the protocol document of an Eastron SDM630, the three-phase meter behind many Home Assistant energy dashboards: datasheet number 30001, type float, 2 registers, unit V.
Everything you need to poll it is in there, none of it spelled out. As a request the row becomes the PDU 04 00 00 00 02.
| Bytes | Value | Meaning |
|---|---|---|
| 1 | 04 | FC04, read input registers |
| 2-3 | 00 00 | start address, PDU address 0 |
| 4-5 | 00 02 | quantity, two registers |
RTU adds a server address and a CRC around those five bytes, TCP an MBAP header. The PDU stays the same. The answer is 04 04 43 66 80 00.
| Bytes | Value | Meaning |
|---|---|---|
| 1 | 04 | same function code, no exception |
| 2 | 04 | byte count, four data bytes |
| 3-4 | 43 66 | first register, high word |
| 5-6 | 80 00 | second register, low word |
Glue the four data bytes into 0x43668000 and IEEE 754 reads 230.5, a mains voltage, so all four decisions were right. The rule behind it: a register is 16 bits and the protocol gives it no meaning. Block, numbering, width and scale live in the datasheet, and what a Modbus register map contains, block by block is the reference version.
Which block, and therefore which function code?
The leading digit of a five-digit datasheet number names the block, and the block names the function code: 3xxxx is an input register you read with FC04, 4xxxx a holding register you read with FC03. That digit never travels: the function code already states the block. Many vendors skip the notation and head the table "Input registers, function code 04" instead, as Eastron does.
If the map says nothing, do not guess twice. Send FC03. Exception 01, illegal function, means the device does not implement that function code, so try FC04. There are no other options for registers. Exception 02, illegal data address, means the function code was right and the address was not, which is the next section. See the full list of function codes with their limits and what each exception code tells you.
Which numbering does this vendor use?
A vendor tells you in one of three ways, and the third one is silence.
- In a sentence. Fronius writes that registers start at 1 and that you use address 40000 (
0x9C40) to read register 40001, so the address sent is always one lower. Daikin is shorter: "Register Base 0." - In two columns. SolarEdge prints base 0 and base 1 side by side, and the question disappears. Eastron does the same in one table, heading a column "Modbus Protocol Start Address" with
0x0000on the row that carries the number 30001. - Not at all. Then you test it yourself.
The rule underneath is single: the PDU address is the data model number minus 1, so datasheet number 40108 is PDU address 107, hex 0x006B. Home Assistant, pymodbus and libmodbus all expect that PDU address, while many meter, drive and heat pump datasheets print the 1-based number.
Testing beats calling the supplier: read a register whose value you can guess, then one address lower and one higher. Get the neighbouring value or exception 02 and you are off by exactly one. The reading that matches your expectation settles the numbering for the whole map, and the two ways Modbus addresses are counted has the conversion.
How many registers wide is this value?
Count from the type, not from the address: a 16-bit type takes one register, a 32-bit type two, a string as many as it needs at two ASCII characters per register.
Reading half a 32-bit value is not an error a device can catch. You get 0x4366 on its own, which as a 16-bit integer is 17254, a number with no meaning. Which register comes first is the next trap, with its own lesson in how ABCD, CDAB, BADC and DCBA make four numbers out of the same bytes.
More per-brand rules: comparison of Modbus energy meters.
From raw value to real unit
There are two families of scaling, and a second register tells you which one you are in.
Family one is a fixed factor in the datasheet. Daikin returns temperatures as a signed 16-bit value you divide by 100, so a raw 3512 is 35.12 degrees C. NIBE uses a divisor of 10, so a raw -47 is minus 4.7 degrees C. Same quantity, same type, different divisor.
Family two puts the scale in a register of its own: a SunSpec device publishes an integer plus a signed exponent beside it, and the real value is the raw value times ten to that power.
That same SMA map adds two warnings. DC power at datasheet number 40217 has scale factor 2, so a raw 57 is 5700 W, while the internal temperature at 40219 has no scale factor at all. Sign belongs here too: an int16 read as a uint16 turns minus 5.0 degrees C, raw -50, into 65486, which after the divide by 10 reads 6548.6 degrees C in January. The SunSpec model layout is the reference for that map.
When the number is not a measurement
Before you scale, check whether the value is a sentinel: a fixed pattern meaning "nothing here". SunSpec devices use 0x8000 for an int16, 0x80000000 for an int32, 0xFFFF for a uint16 and 0x7FC00000 for a float32. Daikin uses readable decimals: 32767 means unsupported, 32766 unavailable in this configuration, 32765 not loaded yet. A Daikin register returning 32767 is not 327.67 degrees C, it is a sensor the unit does not have.
The five questions you ask every map
Same five questions, every device, in this order.
- Which block? The table heading or the leading digit decides between
FC03andFC04. - Which numbering? A datasheet number is 1-based unless the vendor says otherwise, and the PDU address is that number minus 1.
- How wide? The data type says one register, two, or more, and you read them in one request.
- Which scale and which sign? A fixed divisor, a scale factor register next door, or neither.
- Is it a sentinel? Check the not-implemented values of that brand before you convert.
Sometimes the answer to question three is that the value is not a number at all. Daikin puts two ASCII characters in one register with its text16 type: 21816 is 0x5538, 0x55 is the letter U and 0x38 the digit 8, so the fault code is U8. Read that same register as a number and it means nothing.
With no map at all, the Register Scanner in ModbusCloud Diagnostics sweeps an address range and shows which addresses answer. It finds addresses, not meaning, so these five questions still apply.
Common mistakes
Forgetting the scaling factor. A register holding 2305 with a documented unit of 0.1 V is 230.5 V, not 2305 V. Nothing errors and nothing warns, so the value stays stable and wrong for months. Always verify against a quantity you can predict.
Reading a 32-bit value as two separate registers. You get two numbers that mean nothing and go hunting for a communication fault. The register count column is not decoration.
Mixing signed and unsigned. An outdoor temperature of minus 5 arrives as 65531 when you read it as unsigned. The pattern is values just below 65536 on a quantity that can go negative.
Taking a sentinel for a measurement. An SMA inverter returns 0x80000000 at night. Push that into a time series and you get a spike of more than two billion watts, plus an alarm nobody can explain.
Get hands-on
Apply the procedure to a real map and check your interpretation against reality.
- 1
Pick one line
Take the register map of your own device, or the Eastron SDM630 protocol document. One line is enough.
- 2
Choose a value you can predict
A mains voltage around 230 or a frequency around 50. A quantity you cannot predict verifies nothing.
- 3
Answer the four questions on paper
Block, numbering, width, scale, written down before you read anything.
- 4
Read the raw registers
Use mbpoll, free to use and also commercially. The
:hexsuffix prints what the device sent instead of what the tool thinks it means. The digit after-tfollows the block digit from the map, so-t 3for 3xxxx and-t 4for 4xxxx:mbpoll -m rtu -a 1 -b 9600 -P none -t 3:hex -r 1 -c 2 -1 /dev/ttyUSB0 - 5
Convert by hand and trace the difference
Glue the registers together, apply the scaling factor and compare with what you wrote down. A wrong order of magnitude means the fault is in one of your four decisions, not in the measurement. A factor of 10 or 100 points at the scale, a neighbouring value at the numbering, and pure nonsense at the width or the word order.
Without hardware. The pymodbus simulator is free to use, also commercially. Install it with pip install "pymodbus[serial,simulator]==3.15.0" and start pymodbus.simulator --modbus_server server --modbus_device device, which listens on Modbus TCP port 5020. PDU address 4 holds a uint32 across two registers: 9 times 65536 is 589824, plus 27177 makes 617001. PDU address 16 holds a string across five registers. Run the same four questions over both.
Expected result: four written answers plus a reading that matches your expectation, or a difference you can trace to one of those four decisions.
Summary
- A datasheet line is not an address but four decisions: which block, which numbering, how many registers wide, and which scaling factor.
- The block digit in a number such as 30001 never goes on the wire, because the function code already states the block:
FC04for input registers,FC03for holding registers. - A vendor states its numbering in a sentence, in two columns, or not at all, and in that third case you settle it on a value whose order of magnitude you know.
- Scaling is either a fixed divisor in the datasheet or a separate scale factor register beside the value as SunSpec does, and you check for sentinels before you apply it, because
0x8000in an int16 and 32767 on a Daikin register mean "no measurement".
Check yourself
Four questions about this lesson. Every answer comes with an explanation.
Question 1 of 4
Want to see how it works?
The ModbusCloud Gateway reads the devices from this course without you programming a single register.