Float, modbus - How to send

I still do not get it.
I tried reading this thread THIS but I can not for the life of me figure out how it is do be done.

I have a INA219 current sensor…
WinTR Scada - Link - Link2
I have ethernet shield and modbus library for Arduino - Modbus .

Everything is setup and I can read any register from the Arduino and control holding registers adjust set points. All int, bytes and bools.

What I want to do is read the current sensor through modbus. So I need to write the value to two 16bit registers. I tried using low byte en high byte from here but does not work.

So what I do is multiply by 100 and then send the int over modbus, in the master I do linear scaling. It works, but not if the current is a negative number.

From the Simply Modbus website - Here gives example like…

Register 40108 could also be combined with 40109 to form any of these 32-bit data types:
A 32-bit unsigned integer (a number between 0 and 4,294,967,295)
40108,40109 = AE41 5652 = 2,923,517,522

So I will be testing… Mb.MbData[0],Mb.MbData[1] = 30.11; as soon as I get home today.

#include <Wire.h>
#include <Adafruit_INA219.h>
#include <SPI.h>
#include <Ethernet.h>
#include "MgsModbus.h"

MgsModbus Mb;
int inByte = 0; // incoming serial byte
byte mac[] = {0x90, 0xA2, 0xDA, 0x0E, 0x94, 0xB5 };
IPAddress ip(192, 168, 0, 10);
IPAddress subnet(255, 255, 255, 0);

Adafruit_INA219 ina219;

int Current_int;

void setup(void) 
{
  Ethernet.begin(mac, ip, subnet);
  uint32_t currentFrequency;
    
  ina219.begin();
  ina219.setCalibration_16V_400mA();


}

void loop(void) 
{
  float shuntvoltage = 0;
  float busvoltage = 0;
  float current_mA = 0;
  float loadvoltage = 0;

  shuntvoltage = ina219.getShuntVoltage_mV();
  busvoltage = ina219.getBusVoltage_V();
  current_mA = ina219.getCurrent_mA();
  loadvoltage = busvoltage + (shuntvoltage / 1000);
  
  current_int = current_mA * 100;  //This shifts the decimal point 2 places to the right
						           //9.40mA * 100 = 940 / In the master I just scale it.
  
  Mb.MbData[0] = current_mA; //Here is the problem
  Mb.MbData[1] = Current_int;  //mA can reach 3200, so 3200*100 overflows this data type


  Mb.MbsRun();
  delay(2000);
}

Remember all Modbus holding registers are 16 bits of unsigned data.

With your statement:
Mb.MbData[0] = current_mA; //Here is the problemYou are attempting to place 32 bits of float data into 16 bits of uint16_t.

What you need to do is to place half of the 32 bit float in one holding register and the other half of the 32 bit float in another holding register.

There are a few ways in which you can do this in code.
Then, the SCADA system needs to know to read in the two holding registers and interpret back to 32 bit float.

Another method, and the way I would do this is to not use the library you are using, but instead to know and understand the INA219 type of sensor. The library is giving you the float data, not the chip itself.

From the chip, the data is all 16 bit.
You can look inside that library and see how it works and pull it apart if you wish, or just address the chip itself and get the data directly, they are quite nice chips.

I use the INA226, with higher bus voltage capabilities, with this library:


Paul - VK7KPA

Thank You rockwallaby.

I have been trying to figure out how to divide a 32bit into two 16bit registers. So far the only solution I have found is to use something called a union. Was just wondering if there was an easier way.
I have also noticed that if I have a value of example -200 and I set my scada software to read that register as a signed 16bit then it displays correctly.

Using an union is probably the suggested method, otherwise you can do it with pointers, by pointing to the high order 16 bits of the float and then the low order 16 bits placing each into a holding register.

Yes, I suspect for low values you may well get the SCADA to read some values, though you would need to make that unsigned 16 bits. But, this is not the solution, you need to interpret the float as a float in the SCADA system.

As I mentioned, the other way to tackle this is to get the data directly from the INA219, I recall register 4 for two bytes, so 16 bits.
This is the raw ADC data and you then need to adjust this to meaningful values by factoring the calibrated shunt value.

You will see this in both the Adafruit library or the library I linked to.

You could also of course have the SCADA system hold the calibrated shunt value which is a constant and then have the SCADA use this to multiply the 16 bits of raw ADC INA219 data for current.

Another thing you might think on is the range of your expected current values.
For example, if for instance you are only reading a full scale range of 0 to 3 Amps and you want or need precision to two decimal places, then what is commonly done is to multiply the data by 100, so you have a value ranged from 0 to 300.
Then, in the SCADA system you simply take the value and divide by 100 to get a floating point value once again.
Using this method is simple and only needs a single 16 bit holding register to hold that data range of 0 to 300.
If you want precision to three decimal places, then use a factor of 1000, and it will still work for a holding register.

Hope this helps.


Paul - VK7KPA

rockwallaby:
Another thing you might think on is the range of your expected current values.
For example, if for instance you are only reading a full scale range of 0 to 3 Amps and you want or need precision to two decimal places, then what is commonly done is to multiply the data by 100, so you have a value ranged from 0 to 300.
Then, in the SCADA system you simply take the value and divide by 100 to get a floating point value once again.
Using this method is simple and only needs a single 16 bit holding register to hold that data range of 0 to 300.
If you want precision to three decimal places, then use a factor of 1000, and it will still work for a holding register.

Thank you. This is exactly what I am doing right now. The scada software has a scale feature. So the raw value is -300 to 300 - scaled -3.00 tot 3.00. It works.

The scada also have a feature to combine two registers, which is the reason why I wanted to split the 32bit reading. BUt any how, seems like it is more coding, value times 100 seems like a lot less complicated solution.

Thanks for your help, I will be reading up on pointers

Hello,
I am working on WinTr Scada actually. I need some help:
I want to read the arduino data which come from sensors on WinTr. I have an arduino mega and an Ethernet shield. I have installed Modbus Plc simulator like the tutorial.
I need to read data on different modbus adresses to draw diagram. I meet some problems, when I created more than one tag and I click on run there is always a constant graph.
How can I proceed to resolve this problem please?

Does your gauge reading change?
I have used the trend feature and it worked.

The register your reading from is probably not updating the value.

Hello,
The gauge changes value but only for a tag so one address.
I contacted the person in charge of answering WinTr scada's questions and according to him, Barcode reader should not be used. You have I should write a slave code in the arduino so that Modbus plc simulator can read my temperature and humidity values coming from the arduino on different addresses at the same time. My problem is that I can not read the data of the arduino card by USB port on Modbus PLC simulator. I also have an Ethernet shield and the cable.
If you can help me please it would help me a lot,
Thank you for your availability,

Yours Sincerely,

I am No expert on Wintr.
Are you using modbus rtu on your arduino?
Why are you using the Barcode reader? I have no idea how it works.

Wintr can read from arduino using modbus rtu or modbus tcp. I have used both with success.

In my first post on this thread I have linked to the modbus tcp library I am using currently. You will need ethernet shield. Then setup comms in wintr as tcp master.

You can code arduino to put temp in one register and humidity in another and then set wintr to look at both.

I have not used modbus simulators.

I have arduino program for modbus rtu somewhere and a wintr project spicific for that arduino code.
I will search for it and then send all to you so that you can play around with it if you want.

Hello Sir,
I do not use RTU. I use TCP / IP with my arduino. I have an ethernet shield with the cable. Because after having found the way to read the values of the arduino on WinTr, I should send the data on a cloud.
In the tutoriels of Wintr, they use modbus plc simulator thus I did the same thing but that doesn't work correctly.
Exactly, I don't know how can I manage to code of the arduino by using TCP to put in a register the temperature and in an other one the humidity and this for several values of temperatures and humidity.
I contacted the Mister of WinTr but he doesn't understand when I ask him for help to configure WinTr as Master. I don't know how can I proceed.

Thank you for your help and for your availability for me.

Yours sincerely,

There is the code I made, But it 's false I think :

MgsModbus_test_Slave1.ino (589 Bytes)

I have attached some code I am using. This is not all my own code but code that is currently working and giving values to Wintr. I have 12 other pages of code because I like to run sub routines like a plc would.
this attached code will probably not compile for you because of all the libraries and the sub routines. But you might find something useful. This forum has upload limit so I can not upload my Wintr project. Currently looking for a cloud service to use.

Home_Alarm_Automation_V7_digital_temps.ino (30.9 KB)

Hello Sir,

Thank you for your code. I think It is litle bit complicate to understand there is a lot of sub routines. I just want to communicate with WinTr scada via Modbus arduino code and be able to read my sensors values.
Thank you for you availibility,

O.k. Attached code should get you started. This modbus library does not support 32bit values. So to get temp to wintr you have to multiply by 100. In wintr you will have to create a new internal tag to convert back to float.

Change your Computer ip address to 172.22.2.100 for testing sake.

In wintr, go to Control panel. Select the tab Modbus Rtu/Tcp master.
Left most window should be Rtu Network /1 if not right click the Add.
Second window from left. Right Click Rtu Net. "COM1… then select Network and then properties.
Select Modbus Tcp IP
Modbus Tcp ip address 172.22.2.200 Port 502
poll interval 10
Time out 1500

Right click station then select Station then properties.

Station name - Station 1
Station address 1
fail repeat 2
Tick
Tick
32 Bit data word = Fisrt high

32 32
32 32

Under address in the first row type the register address. For the attached code use 30018.
tage name random
unsigned int 16

Click on Connect top write of active window.

You should now see a value between 0 and 300

registerd tag list is where you can create internal tags for scaling purpose.

I hope this helps.

Home_Alarm_Modified.ino (1.6 KB)

Dear Sir,

I tried to make the code with your example there is in attachment. I think it is false it doesn’t run.
Should I connect the Ethernet shield to communicate with WinTr?
My arduino sould support three code : one for the reception of NRF24L01 module, one to modbus to connect with WinTr as I tried just here and thingspeak to send data to the cloud.

Thank you for your help,

Yours Sincerely,

receveur.ino (1.11 KB)