In this lesson

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.

As printed in the Eastron SDM630 datasheet30001FLOAT322 registersVFour decisions in one line1
Which block, so which function code?
3xxxx is an input register, so FC04
2
Does this sheet count from 1 or from 0?
30001 is data model number 1, so PDU address 0
3
How wide, and in which word order?
float32 over 2 registers, high word first
4
Which scaling factor and which unit?
No factor listed, the float is already in volts
A datasheet row is not an address but a set of four decisions. Skip one of them and you get a number that looks plausible and is still wrong.

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.

BytesValueMeaning
104FC04, read input registers
2-300 00start address, PDU address 0
4-500 02quantity, 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.

BytesValueMeaning
104same function code, no exception
204byte count, four data bytes
3-443 66first register, high word
5-680 00second 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.

  1. 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."
  2. 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 0x0000 on the row that carries the number 30001.
  3. 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.

40200550×10^402011=5500 W
40200
raw value, active power
40201
scale factor, W_SF
5500 W
real value
SunSpec keeps the scale in a register of its own, right next to the measurement. Read only the first one and you are a factor of ten out.

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.

SMA power register, int32 over two registers☀4200 W☾0x8000000021474836480 W
4200 W
Daytime, inverter awake
0x80000000
At night, inverter asleep
0 W
Not a measurement, store nothing
Treat that sentinel as a reading and your dashboard shows more than two billion watts at midnight. Filter it in the mapping, not in the graph.

The five questions you ask every map

Same five questions, every device, in this order.

  1. Which block? The table heading or the leading digit decides between FC03 and FC04.
  2. Which numbering? A datasheet number is 1-based unless the vendor says otherwise, and the PDU address is that number minus 1.
  3. How wide? The data type says one register, two, or more, and you read them in one request.
  4. Which scale and which sign? A fixed divisor, a scale factor register next door, or neither.
  5. 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. 1

    Pick one line

    Take the register map of your own device, or the Eastron SDM630 protocol document. One line is enough.

  2. 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. 3

    Answer the four questions on paper

    Block, numbering, width, scale, written down before you read anything.

  4. 4

    Read the raw registers

    Use mbpoll, free to use and also commercially. The :hex suffix prints what the device sent instead of what the tool thinks it means. The digit after -t follows the block digit from the map, so -t 3 for 3xxxx and -t 4 for 4xxxx:

    mbpoll -m rtu -a 1 -b 9600 -P none -t 3:hex -r 1 -c 2 -1 /dev/ttyUSB0
    
  5. 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: FC04 for input registers, FC03 for 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 0x8000 in 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
The datasheet says: 30001, float, 2 registers, Volts. Which function code do you send, and which start address?
A register holds the value 2305 and the datasheet says the unit is 0.1 V. What do you read, and what is your check?
You read an outdoor temperature and get 65531 back while it is freezing outside. What is going on?
At night an SMA inverter returns 0x80000000 in its power register. What do you do with that in your monitoring?

Stuck on a project?

Describe what you see and where it goes wrong. We will look at it and tell you what we would do.

Further reading in the knowledge base