Apologies if posted in the wrong place, first time.
I'm trying to read a voltage value in RapidScada from Arduino via Modbus RTU but I only get integers on the SCADA side when the Arduino is outputting floats.
I'm very new to Modbus, any help would be greatly appreciated
Uno R3 board as slave/server, Rapidscada as master/client
#include <modbus.h> // MODBUS protocol
#include <modbusDevice.h> // devices
#include <modbusRegBank.h> // register bank
#include <modbusSlave.h> // slave addresses
modbusDevice regBank; // object - stores data
modbusSlave slave; // modbus slave protocol handler
// variables defined area
void setup() {
// put your setup code here, to run once:
Serial.begin(9600); // begin serial monitor at 9600 baudrate
regBank.setId(5); // set slave device id (can be any number) for arduino to talk to modbus master device
regBank.add(30001); // analog input device 1 (30000 modbus register/address)
regBank.add(2); // digital output for led
regBank.add(10003); // digital input for pushbutton (10000 modbus register/address)
slave._device = ®Bank; // assign modbus slave device to the protocol handler
slave.setBaud(9600); // initialise serial port for comms at 9600 baudrate
pinMode(3, INPUT); // set pin 3 to input - pushbutton
}
void loop() {
// put your main code here, to run repeatedly:
float sensor1 = analogRead(0); // variable to store analogIn pin value - sensor 1 (pot)
delay(20); // 20ms delay between sensor readings
boolean digitalInput3 = digitalRead(3); // variable to store if digital pin input is true/false - pushbutton
int digitalOutput2 = regBank.get(2); // variable to store digital pin 2 data/state given from scada - led
float sensor1read = (sensor1 * 5.0) / 1024.0; // analogIn pin value (0-1023) multiplied by 5V and divide by 1024 to convert value into readable voltage (0-5V) for 10bit resolution ADC
// test to serial monitor
//Serial.println(sensor1read);
//Serial.println(digitalInput3);
if (digitalOutput2 >= 1 && digitalRead(2) == LOW) // if scada button pressed & led is off - turn on
digitalWrite(2, HIGH);
if (digitalOutput2 <= 0 && digitalRead(2) == HIGH) // if scada button pressed & led is on - turn off
digitalWrite(2, LOW);
// analog values
regBank.set(30001, sensor1read); // regBank function to write data on rtu protocol addresses - analog input values
// digital inputs
regBank.set(10003, digitalInput3); // regBank function to write data on rtu protocol addresses - digital input values
delay(100); // delay to ensure enough time for values to be sent
slave.run(); // function to ensure running slave device
}`
The Modbus protocol can only transport 1bit coil and 16bit register values. Some manufacturers use two holding registers (2 x 16bit = 32 bit) to encode a single precision float value but that has to be documented because it isn't covered by the standard.
You should post a link to the library you're using.
What Arduino board are you using?
Most PLCs use such high numbers to distinguish between the register types but actually use lower numbered registers. It depends on the manufacturer which method it implements but the manual should tell you that.
Any help or insights is very appreciated. Modbus is new territory but I'm trying to learn.
Trying to read analogue inputs from a power factor correction circuit I'm designing for a project, so I need to read floating points for voltage and current values. The code posted is just a mockup for the Modbus testing, trying to get a crude framework I can build on.
As I wrote, that's not a question of the Arduino library but of the other side (the Rapidscada in your case). Consult the manual of that system about how it expects a float to be transferred by 16 bit integers. You might also post a link to that manual so we may help you find that out.
Often these values are transferred by integers as you can define the unit, it doesn't have to be V, it can be mV.
I will try to find the information on the Rapidscada side for the correct method for receiving floats.
I understand what you mean about transferring integers and using mV, unfortunately my range exceeds this in certain circumstances and I'm looking for a high degree of accuracy but I will take the advice into consideration. It may become useful.
Do you know any good sources for teaching myself Modbus protocol? I have found various sources but I thought I would ask incase you know of more robust sources of information.
Thanks again for taking the time to reply. It has been very helpful
Thankyou for that. That gives me some solid information to work with. I see what you mean about two holding registers to transfer the information.
I wasn't aware that accuracy would be lost during the change from integer to float but that's great you mentioned that. Information like that is very valuable at this stage.
I apologise for the lack of technical knowledge on my part. My skillset is very much electrical/electronic. I'm trying to expand it into software through the project.
Thanks for taking the time to help, I appreciate it.
I type-cast a float pointer into a pointer to a 32 bit integer and de-reference it to get an exact copy of the float bits in an integer format. That works because a float is also represented by 32 bits (the bits just have another meaning). Once I have the bits of the float in an integer it's much easier to split that up into two 16 bit values.
This worked perfectly. Thankyou pylon. Really helped. I would have replied sooner but I've been studying C++ alot to get better. Thanks again.
// store voltage value in input register and read
//------------------------------------------------------------------------------------------//
// float voltage value cast to 32bit int and split between 2x 16bit ints
uint32_t dualValue1 = *((uint32_t *)&voltage);
uint16_t register1 = dualValue1 >> 16; // right shift >> by 16bits
uint16_t register2 = dualValue1 & register1;
// write values to input registers for voltage value
modbusTCPServer.inputRegisterWrite(0x00, register1);
modbusTCPServer.inputRegisterWrite(0x01, register2);
// read values from input registers for voltage value
modbusTCPServer.inputRegisterRead(0x00);
modbusTCPServer.inputRegisterRead(0x01);
//------------------------------------------------------------------------------------------//