New: the AI firewall watches the whole platform and explains each incident in plain words. See how it works

Reading a panel meter over Modbus RTU: how do 43 66 99 9A become 230.6 V?

Reading a panel meter over Modbus RTU: how do 43 66 99 9A become 230.6 V?

Solved 6 5

24 years running plants and I have never had to decode a Modbus frame by hand, so here we are.

One of our maintenance techs built a small logger: Arduino Uno with a MAX485 board reading the panel meter on line 4 (Modbus RTU, 9600 8N1, slave address 1). The Uno passes the values over serial to a Raspberry Pi that publishes them to Hermetry. He left for a new job before finishing the decoding part.

The Uno sends this request and gets a reply every time, so the wiring works:

TX: 01 03 00 00 00 02 C4 0B
RX: 01 03 04 43 66 99 9A 3A 1E

The panel itself shows 230.6 V on L1. I can’t see how those bytes turn into 230.6. What part of the reply is the value, and how do I turn it into a number I can send?

♥ 2

Is 43 66 the voltage in hex? 0x4366 is 17254, so maybe divide by something?

Ask me about 4–20 mA.
♥ 0

Which meter is it? The manual’s register map says whether a value is a float or a scaled integer.

♥ 2

Found the manual in the panel door. “Voltage L1, address 0x0000, float, 2 registers.”

♥ 1

Then the 2 registers are one 32-bit float. Splitting your reply:

01          slave address
03          function (read holding registers)
04          byte count, 4 bytes follow
43 66 99 9A data: register 0 = 0x4366, register 1 = 0x999A
3A 1E       CRC

Put the four data bytes together as 0x4366999A and read them as an IEEE 754 float: sign 0, exponent gives 2^7 = 128, mantissa about 1.8016, and 128 x 1.8016 = 230.6. So Wilson’s 0x4366 is the first half of the float, which is why dividing never comes out right.

On the Uno the bytes arrive most significant first but the AVR is little endian, so reverse them before copying into a float:

uint8_t b[4] = { rx[6], rx[5], rx[4], rx[3] };   // 9A 99 66 43
float v;
memcpy(&v, b, 4);
Serial.print("{"l1.voltage":");
Serial.print(v, 1);
Serial.println("}");

Some meters send the two registers the other way round (word swapped). If you ever get something like 1.2e-23 instead of a voltage, swap rx[3..4] with rx[5..6]. Check the CRC before trusting a reply too.

Timestamps in UTC or it didn't happen.
Accepted solution♥ 6

Worth sending an error counter next to the readings, modbus.crc_errors or similar. On our sites that counter going up was always the first sign of a loose A/B wire.

QoS 1 and a lot of patience.
♥ 1

230.6 on the dashboard, matching the panel. I passed this thread to the tech who took over and he added the other phases and the CRC check in an afternoon. Wilson, I tried your divide idea first too, so you’re in good company.

♥ 1
Viewing 7 posts – 1 through 7 (of 7 total)
  • You must be logged in to reply to this topic.

Join the conversation

Sign in to ask and answer, like helpful posts and mark the reply that solved your problem. Every contribution earns points and levels.

Share