Getting MODBUS data in correct format

Hello forum,

My previous experience with Arduino and Modbus is limited to getting wind directions from a wind vane. Now I’m trying to get values from a ultra-sonic water flow meter, like battery voltage, water velocity, flow, etc.

I used the same code that I used for the wind vane, and I’m able to get values from the different registers. But the values I’m getting does not correspond to anything that one would expect - battery voltage which is over 12.0V always returns a value of zero, and the other registers all return values that are clearly not actual values, since they’re always something like 512, 1024, 2048, 1536 (which is 512 + 1024.)

I’m using the ModbusMaster library (GitHub - syvic/ModbusMaster: Arduino class library for communicating with Modbus slaves over RS232/485), and from the example named RS_485HalfDuplex and changed the code a bit to work with my hardware: UNO with MAX485 (https://www.ebay.co.uk/itm/RS485-Module-MAX485-TTL-RS-485-for-Arduino-Raspberry-Pi-AA119/283989866584?hash=item421f1d7858:g:In4AAOSw-nRfRQb0)

#include <ModbusMaster.h>
#include <SoftwareSerial.h>

SoftwareSerial MAX485(6, 7);

#define MAX485_DE      4
#define MAX485_RE_NEG  5

ModbusMaster node;

void preTransmission()
{
  digitalWrite(MAX485_RE_NEG, 1);
  digitalWrite(MAX485_DE, 1);
}

void postTransmission()
{
  digitalWrite(MAX485_RE_NEG, 0);
  digitalWrite(MAX485_DE, 0);
}

void setup()
{
  pinMode(MAX485_RE_NEG, OUTPUT);
  pinMode(MAX485_DE, OUTPUT);

  digitalWrite(MAX485_RE_NEG, 0);
  digitalWrite(MAX485_DE, 0);


  Serial.begin(19200);
  MAX485.begin(9600);

  node.begin(1, MAX485);    // Modbus slave ID 

  node.preTransmission(preTransmission);
  node.postTransmission(postTransmission);
}


void loop()
{
  uint8_t resultMain;

  resultMain = node.readHoldingRegisters(0x0002, 1);
  
  if (resultMain == node.ku8MBSuccess) 
  {
    Serial.print("Result: ");
    Serial.println(node.getResponseBuffer(0x00));
  }  
  else
    Serial.println(F(">>>>>>  BAD RESULT")); 

  delay(500);
}

Sample output:

17:05:22.568 -> Result: 512
17:05:23.812 -> Result: 512
17:05:23.812 -> Result: 512
17:05:25.813 -> Result: 512
17:05:25.813 -> Result: 512

This code worked for my wind vane, since it gave decimal values of 0 to 15, each number corresponding to a wind direction. For the ultra-sonic sensor the decimal output is clearly not doing the trick, and I need to work out how to get the original hex values coming from the holding registers. If I can get the hex values I can work out the single precision floating value.

From the sensor manual:
7.3.2 Data format
(1) Instrument data storage format is:IEEE754 standard single-precision floating number
IEEE754 standard single-precision floating number consists of 1 sign bit+8 exponent +23 mantissa, use four hexadecimal digits. e.g. 124.75 use hexadecimal express as 42 F9 80 00. It’s calculated as: 24.75 Binary is 1111100.11
"

I don’t understand much about the internal working of this library, but I assume it reads the hex values coming from the sensor and converts it to decimal in a direct manner, without keeping the IEEE754 standard in mind. My question, how do I use this library to give me the data in the format I need so I can calculate the correct values?

Attached is the full manual.

Velocity Type Open Channel Flow Meter.pdf (678 KB)

You have to read at least 2 registers and combine them as described in the linked document. This document is missing a register description (at least I didn't find one), so you might have to post a link to the register table.

According to one of the engineers at the company where I bought the sensor, if I read register 6 (and this may mean register 6 and 7 as you menioned), if the actual flow result is 1,532.56 m3/h, the hex output from the unit will be 01 03 04 44 BF 91 EC B3 3A. (44 BF 91 EC is 1532.56m3/h)

Similarly, if I read register 0 to get battery voltage (probably 0 and 1) the result should be 01 03 04 44 BF 91 EC B3 3A if the actual value is 24.5V.

So back to my initial question, is there a way for me to get the code I used to output the HEX values instead of this decimal value I'm currently getting? A reminder that all the values I'm getting in this decimal format is some value which is 2 to the power n.

Another bit of info they gave me is that this is what the registers are supposed to hold:

0 battery voltage
2 water level
4 water velocity
6 water flow rate
8 accumulated flow

So I assume battery voltage would be register 0 and 1, water level register 2 and 3, etc.

My current output for these registers is like this:

resultMain = node.readHoldingRegisters(0, 1); -> 0
resultMain = node.readHoldingRegisters(0, 2); -> 16700

I just attached a charger to the battery that's supposed to have this value as voltage, lo and behold the value is climbing steadily. So I just need to figure out how to convert this decimal value into the more or less 12V value its supposed to be. Any ideas?

17:24:59.454 -> Result: 16700
17:24:59.985 -> Result: 16700
17:25:00.507 -> Result: 16700
17:25:01.041 -> Result: 16701
17:25:01.569 -> Result: 16702
17:25:02.098 -> Result: 16702
17:25:02.620 -> Result: 16703
17:25:03.145 -> Result: 16703
17:25:03.677 -> Result: 16702
17:25:04.199 -> Result: 16703
17:25:04.756 -> Result: 16704
17:25:05.293 -> Result: 16704
17:25:05.813 -> Result: 16704
17:25:06.339 -> Result: 16704
17:25:06.858 -> Result: 16704
17:25:07.386 -> Result: 16705
17:25:07.942 -> Result: 16705
17:25:08.473 -> Result: 16705
17:25:08.994 -> Result: 16705
17:25:09.519 -> Result: 16706
17:25:10.074 -> Result: 16706
17:25:10.597 -> Result: 16706
17:25:11.131 -> Result: 16706
17:25:11.653 -> Result: 16706
17:25:12.183 -> Result: 16706
17:25:12.710 -> Result: 16706
17:25:13.232 -> Result: 16707
17:25:13.763 -> Result: 16707
17:25:14.289 -> Result: 16707
17:25:14.846 -> Result: 16707
17:25:15.380 -> Result: 16707
17:25:15.903 -> Result: 16707
17:25:16.441 -> Result: 16707
17:25:16.959 -> Result: 16707
17:25:17.481 -> Result: 16708
17:25:18.038 -> Result: 16708
17:25:18.561 -> Result: 16708
17:25:19.086 -> Result: 16708
17:25:19.611 -> Result: 16708
17:25:20.131 -> Result: 16708
17:25:20.663 -> Result: 16708
17:25:21.226 -> Result: 16708
17:25:21.747 -> Result: 16708
17:25:22.288 -> Result: 16709
17:25:22.788 -> Result: 16709
17:25:23.322 -> Result: 16709
17:25:23.857 -> Result: 16709
17:25:24.390 -> Result: 16709

Three clues on how to convert this decimal value to actual values:

  1. A result of 17783 is shown on the sensor's onboard screen as 3955.7 m3
  2. A result of 16181 is shown on the sensor's onboard screen as 0.71 m
  3. A result of 16718 is shown on the sensor's onboard screen as 12.95V or therebouts.

Can anyone see how this conversion is made?

Again, according to the manual the outputs from the unit are float point number with single precision.

heinburgh:

  1. A result of 17783 is shown on the sensor's onboard screen as 3955.7 m3
  2. A result of 16181 is shown on the sensor's onboard screen as 0.71 m
  3. A result of 16718 is shown on the sensor's onboard screen as 12.95V or therebouts.

I found this website IEEE-754 Floating Point Converter that converts HEX to IEEE-754. I'm printing the decimal value that the sketch throws out with this line: Serial.println(node.getResponseBuffer(0x00), HEX); and when I enter this HEX output into the online converter it almost lines up. The issue is that I only get 2 bytes HEX from the unit, when I need 4 bytes to get the full number.

Example: reading register 8 I get HEX 45 77, which converts to an actual value of 3957.0. According to the unit's display it should be 3955.7, which I'm sure the missing 2 bytes would fill in.

Another example which confirms this is battery voltage. My uno throws out HEX 41 4F (00 00 added) which the online converter sets at 12.9375, and this is exactly what the unit's display shows. The example I received from the engineers was 41 C4 00 00, I'm assuming the 3rd and 4th bytes can always be 00 for this small number?

So my question is how do I get the 3rd and 4th bytes from the registers?

And the more important question, once I have all 4 bits, how to convert this to the actual value in Arduino.

Got the conversion here: https://forum.arduino.cc/index.php?topic=335109.0

Only item remaining is to get all four bytes from the modbus device.

Serial.print(node.getResponseBuffer(0x00), HEX);
Serial.println(node.getResponseBuffer(0x01), HEX);

Got it.