I am using an MKR RS485 shield with Arduino MKR 1310 board. I am communicating with an energy meter via RS485, however, the data I read is inaccurate. For instance, the voltage on the meter is 230V for a phase voltage but on the Serial Monitor I read 247 to 250V. The data frame for reading one phase voltage is: 0x01, 0x03, 0x00, 0xF3, 0x00, 0x01, 0x74, 0x39. The sketch I used is given below:
#include <ArduinoRS485.h>
#include <ArduinoModbus.h>
void setup() {
Serial.begin(115200);
if (!ModbusRTUClient.begin(9600)) {
Serial.println("Failed to start Modbus RTU Client!");
while (1);
}
}
void loop() {
float volt = readVoltage();
Serial.println(volt);
delay(5000);
}
float readVoltage() {
float volt = 0.;
if (!ModbusRTUClient.requestFrom(0x01, HOLDING_REGISTERS, 0x00F3, 1)) { //make the call to the register
Serial.print("failed to read voltage! ");
Serial.println(ModbusRTUClient.lastError()); //error handler
} else {
uint8_t word1 = ModbusRTUClient.read(); //read data from the buffer
volt = word1;
}
return volt;
}
May I know what could be the possible reason for reading the inaccurate data? Thank you.
Have you ever set up a Campbell Scientific data logger as a Modbus slave and discovered that your data was not arriving at your SCADA system as you expected? You may have quickly realized two things: troubleshooting the communication problem is not an easy task, and there are many different approaches you can take. MyPrepaidCenter
I need little assistance in my sketch. I want to read three values i.e. voltage, current, and then power from the energy meter. Firstly, the voltage value is inaccurate sometimes. But at least I read a value near to the value on shown on the meter. However, when I turned on a load of 1.8 kW the meter shows the same voltage around 230 volts but on the Serial Monitor I see around 180 V.
The current value for my load on the meter is 8.6 Amps but the Serial Monitor shows around 60 Amps and varies.
The most important for me is the power value because that is going to be used in the program later on. The power value for my load is around 1.8 kW but I see 100 W and then it starts decreasing to around 80 W or so. The attached file shows the columns as: Address, Name, Type, Note, Word. Voltage and Current are one word while power is 2 words. In the power section, I tried to print the value of word1 and word2, so the word1 prints 0, while word2 shows 100W and the decreasing value until around 80W. Can someone please tell me what and where am I doing wrong? Thank you very much.
#include <ArduinoRS485.h>
#include <ArduinoModbus.h>
void setup() {
Serial.begin(115200);
if (!ModbusRTUClient.begin(9600)) {
Serial.println("Failed to start Modbus RTU Client!");
while (1);
}
}
void loop() {
float volt = readVoltage();
Serial.print(volt, 1);
Serial.println(" V");
delay(2000);
float amp = readCurrent();
Serial.print(amp, 1);
Serial.println(" A");
delay(2000);
float watt = readPower();
Serial.print(watt, 1);
Serial.println(" W");
delay(5000);
}
float readVoltage() {
float volt = 0.;
if (!ModbusRTUClient.requestFrom(0x01, HOLDING_REGISTERS, 0x00F3, 1)) { // make the call to the register
Serial.print("failed to read voltage! ");
Serial.println(ModbusRTUClient.lastError()); // error handler
} else {
uint8_t word1 = ModbusRTUClient.read(); // read data from the buffer
volt = word1;
}
return volt;
}
float readCurrent() {
float ampere = 0;
if (!ModbusRTUClient.requestFrom(0x01, HOLDING_REGISTERS, 0x00F9, 1)) { // make the call to the register
Serial.print("failed to read current! ");
Serial.println(ModbusRTUClient.lastError()); // error handler
} else {
uint8_t word1 = ModbusRTUClient.read(); // read data from the buffer
ampere = word1;
}
return ampere;
}
float readPower() {
float watt = 0;
if (!ModbusRTUClient.requestFrom(0x01, HOLDING_REGISTERS, 0x00FD, 2)) { // make the call to the register
Serial.print("failed to read power! ");
Serial.println(ModbusRTUClient.lastError()); // error handler
} else {
int8_t word1 = ModbusRTUClient.read(); // read data from the buffer
int8_t word2 = ModbusRTUClient.read();
int16_t totalWatt = word1 << 8 | word2; // bit math
watt = totalWatt;
}
return watt;
}
uint8_t word1 = ModbusRTUClient.read(); //read data from the buffer
Register size of Modbus is 16bit, so storing that information into an 8bit value looses information. You even call the variable word, a word is 16bits.
May I know what could be the possible reason for reading the inaccurate data? Thank you.
Most probably this isn't related to the software you wrote.
The attached file shows the columns as: Address, Name, Type, Note, Word.
Post the complete manual, any information in an other part of the document may be important!
Thank you for the response pylon. Right now I am using the sketch I posted here. The voltage I read is 245 to 250V while on meter it is 228V without any load connected. After connecting an electric kettle, the voltage dropped to 202V, while the voltage on the energy meter was 225V. The current for the load I measure on the meter is around 8.7 Amps, while on the Serial Monitor I see 75 Amps or less. The active power on the meter is around 1900 watts while on Serial monitor it shows around 1600 watts.
I am attaching the manuals I received for the energy meter. This is the link to the meter I am using. The name written on the meter is Acrel ACR10R-D16TE4 with 3 CTs. The manuals are for ACR10R and ACR10RH. I followed the ACR10RH though.
#include <ArduinoRS485.h>
#include <ArduinoModbus.h>
void setup() {
Serial.begin(9600);
if (!ModbusRTUClient.begin(9600)) {
Serial.println("Failed to start Modbus RTU Client!");
while (1);
}
}
void loop() {
float volt = readVoltage();
Serial.print(volt, 1);
Serial.println(" V");
delay(2000);
float amp = readCurrent();
Serial.print(amp, 1);
Serial.println(" A");
delay(2000);
double watt = readPower();
Serial.print(watt, 1);
Serial.println(" W");
delay(2000);
}
float readVoltage() {
float volt = 0.;
if (!ModbusRTUClient.requestFrom(0x01, HOLDING_REGISTERS, 0x00F3, 1)) { // make the call to the register
Serial.print("failed to read voltage! ");
Serial.println(ModbusRTUClient.lastError()); // error handler
} else {
uint8_t word1 = ModbusRTUClient.read(); // read data from the buffer
volt = word1;
}
return volt;
}
float readCurrent() {
float ampere = 0;
if (!ModbusRTUClient.requestFrom(0x01, HOLDING_REGISTERS, 0x00F9, 1)) { // make the call to the register
Serial.print("failed to read current! ");
Serial.println(ModbusRTUClient.lastError()); // error handler
} else {
uint8_t word1 = ModbusRTUClient.read(); // read data from the buffer
ampere = word1;
}
return ampere;
}
float readPower() {
float watt = 0;
if (!ModbusRTUClient.requestFrom(0x01, HOLDING_REGISTERS, 0xFD, 2)) { // make the call to the register
Serial.print("failed to read power! ");
Serial.println(ModbusRTUClient.lastError()); // error handler
} else {
uint16_t word1 = ModbusRTUClient.read(); // read data from the buffer
uint16_t word2 = ModbusRTUClient.read();
uint32_t WATT = word1 << 16 | word2; // bit math
watt = WATT;
}
return watt;
}
The two manuals are contradictory. The register description in section 7.5 of the first document doesn't match the the one in 7.4.1 of the second document.
Your code still uses the wrong types, at least for voltage and current. Did you read my last post?
The voltage I read is 245 to 250V while on meter it is 228V without any load connected. After connecting an electric kettle, the voltage dropped to 202V, while the voltage on the energy meter was 225V. The current for the load I measure on the meter is around 8.7 Amps, while on the Serial Monitor I see 75 Amps or less. The active power on the meter is around 1900 watts while on Serial monitor it shows around 1600 watts.
This value mismatch is definitely not from a wrong read-out. The Modbus protocol includes CRC codes to check for wrong transmissions. As you don't get transmission errors the values you got are exactly what the meter sent. If that doesn't match what the display says at the same time it may be time to ask the vendor about the discrepancy.
pylon:
The two manuals are contradictory. The register description in section 7.5 of the first document doesn't match the the one in 7.4.1 of the second document.
You are right. I actually followed the 10RH manual. I need to confirm from the seller regarding the correct manual for my device.
pylon:
Your code still uses the wrong types, at least for voltage and current. Did you read my last post?
Oh sorry. Should I do it like:
uint16_t word1 = ModbusRTUClient.read();
for current and voltage? The variable I declared is word1 not word, so how is going to affect?
for current and voltage. The voltage value now I read is exactly the same as on the meter but the current is still less. On meter the current value is 8.9A but on the serial monitor I read only 7.5A. Similarly, the power value on the meter is around 2.1kW while I read around 1.65kW. It seems like I am not reading the registers correctly.
Try it this way (my interpretation of the manual):
uint32_t word1 = ModbusRTUClient.read(); // read data from the buffer
uint32_t word2 = ModbusRTUClient.read();
uint32_t WATT = word1 << 16 | word2; // bit math
watt = WATT / 100.0;
and for the current:
uint16_t word1 = ModbusRTUClient.read(); // read data from the buffer
ampere = word1/1000.0;
I don't know if this gives you more accurate outputs but at least that seems to be the correct way to read that stuff. Are you sure you're looking at the same values on the display as you read out over Modbus?
pylon:
Are you sure you're looking at the same values on the display as you read out over Modbus?
I think I am doing it right as can be seen in sketch.
After your changes, I now read the current as 0 A and 5.6 watts for a 2.9 A and 700 watts load.
For the test, I calculated the reduced factors of the amps and watts of the load I connect. And then I multiplied them before print to correct it. For instance, my load was 2100 watts on the meter while serial monitor it displayed 1750, so 2100/1750 = 1.185. Likewise for the current. I tested it for three different loads and the values on serial monitor are matching the values on the meter. I think I am missing to read that factor.
pylon:
It might be that I misinterpreted the manual.
They might use binary digits for the floating point.
But that would mean this code should return correct value:
uint16_t word1 = ModbusRTUClient.read(); // read data from the buffer
volt = word1 / 2.0;
Voltage is already giving correct value so I didn't change it. I mean I didn't divide the value of word1 by 2.
pylon:
uint16_t word1 = ModbusRTUClient.read(); // read data from the buffer
ampere = word1/8.0;
Ok, so current is now near to the current value on meter. On the meter, I saw the current value of 8.7 A while the Serial Monitor was show 9.1 A.
pylon:
uint32_t word1 = ModbusRTUClient.read(); // read data from the buffer
uint32_t word2 = ModbusRTUClient.read();
uint32_t WATT = word1 << 16 | word2; // bit math
watt = WATT / 4.0;
For power, when I had the WATT/4.0, the Serial Monitor was showing very less value compared to the meter. Therefore, I took 4.0. But again there was discrepancy. The power value on meter was 1985 Watts while Serial Monitor showed 1639 Watts. With smaller load the value is close but with bigger load the difference is more.
For the FREQUENCY; I wrote the sketch like this:
float readFrequency() {
float freq = 0.;
if (!ModbusRTUClient.requestFrom(0x01, HOLDING_REGISTERS, 0x00FC, 1)) { // make the call to the register
Serial.print("failed to read frequency! ");
Serial.println(ModbusRTUClient.lastError()); // error handler
} else {
uint16_t word1 = ModbusRTUClient.read(); // read data from the buffer
freq = word1 / 100.0;
}
return freq;
}
To get the correct value, I had to divide word1 by 100.
Ok, so current is now near to the current value on meter. On the meter, I saw the current value of 8.7 A while the Serial Monitor was show 9.1 A.
Excuse me, but a near value isn't what we're looking for. The serial interface should return exactly the same value as you see on the display, else the meter isn't working correctly.
To get the correct value, I had to divide word1 by 100.
In that case my first interpretation was correct and that strange "Point digital: 2" means dividing by a factor 100. But that also means that the power value also must be divided by 100 and the current value by 1000. If that doesn't return the exactly same value as on the display, list in that "manual" is probably not correct and you should read another register to get the correct value.
Ask your vendor to get a manual that actually matches the device you bought.
Agree. The voltage is point digital 1, so it is divided by 10 and gives correct value. Frequency is point digital 2 and divided by 100 to give correct value. Power is also point digital 2 and should be divided by 100 and current by 1000 but unfortunately both of them are not returning correct value. I will talk to them and see if I could get some help.
@pylon, so finally the vendor replied today regarding my query. According to them, I need to use the formula as given in the protocol. If you have the protocol, in section 7.5.1 there is a table of formula to calculate the voltage, frequency, power factor and current. I am attaching a snap of it. In the formula there are few terms like Ue, PI, PU that are required to calculate the final value. They need to be read. Ue lies on address 4, PU on 6 and PI on 7. I tried reading them and calculate the final value but I am getting error in reading the Ue value, similarly PI value also gives an error, no idea why? I am giving the snippet of the sketch below.
float v = 0.;
uint16_t word1, Ue, PU, Pi;
if (!ModbusRTUClient.requestFrom(0x01, HOLDING_REGISTERS, 0x00F3, 1)) { // make the call to the register
Serial.print("failed to read voltage! ");
Serial.println(ModbusRTUClient.lastError()); // error handler
} else {
word1 = ModbusRTUClient.read(); // read data from the buffer
}
if (!ModbusRTUClient.requestFrom(0x01, HOLDING_REGISTERS, 0x0004, 1)) { // make the call to the register
Serial.print("failed to read Ue! ");
Serial.println(ModbusRTUClient.lastError()); // error handler
} else {
Ue = ModbusRTUClient.read(); // read data from the buffer
}
if (!ModbusRTUClient.requestFrom(0x01, HOLDING_REGISTERS, 0x0006, 1)) { // make the call to the register
Serial.print("failed to read PU! ");
Serial.println(ModbusRTUClient.lastError()); // error handler
} else {
PU = ModbusRTUClient.read(); // read data from the buffer
}
if (!ModbusRTUClient.requestFrom(0x01, HOLDING_REGISTERS, 0x0007, 1)) { // make the call to the register
Serial.print("failed to read PI! ");
Serial.println(ModbusRTUClient.lastError()); // error handler
} else {
Pi = ModbusRTUClient.read(); // read data from the buffer
}
v = (word1 * PU) / Ue;
return v;