Reading data from energy meter through modbus protocol

Hi All,

Im trying to read holding register values of Elmeasure LG1129 and BM5140 energy meter using Modbus protocol through Arduino. I used RS485 to TTL converter between Energy meter and Arduino Mega but I haven’t succeeded. Please help me

This is the board i used for RS485 communication:
https://robokits.co.in/control-boards/interface-boards/max485-ttl-to-rs485-converter-module?gclid=EAIaIQobChMIhMDH_d2J5gIVizUrCh2MKwVHEAQYAiABEgKyZvD_BwE

Typical circuit diagram connection:

IMG_20191127_115511%20(1)

Some reference Code I got from forum and burned the same in Arduino:

#include <SimpleModbusMaster.h>

#define baud 9600
#define timeout 1000
#define polling 2000 // the scan rate
#define retry_count 10


#define TxEnablePin 2

#define TOTAL_NO_OF_REGISTERS 31


enum
{
  PACKET1,
  PACKET2,
  

  TOTAL_NO_OF_PACKETS // leave this last entry
};

// Create an array of Packets to be configured
Packet packets[TOTAL_NO_OF_PACKETS];

// Masters register array
unsigned int regs[TOTAL_NO_OF_REGISTERS];

void setup()
{
  // Initialize each packet
  Serial.begin(9600);
Serial1.begin(9600);
  
  
  modbus_construct(&packets[PACKET1], 1, READ_HOLDING_REGISTERS, 40133, 2, 10); 
  modbus_construct(&packets[PACKET2], 1, READ_HOLDING_REGISTERS, 40135, 2, 12); 
  
  modbus_configure(&Serial1, baud, SERIAL_8E1, timeout, polling, retry_count, TxEnablePin, packets, TOTAL_NO_OF_PACKETS, regs);
}

void loop()
{
  modbus_update();

  int A, B, C, D, E, F, G, H, I, J, K, L, M, N, O;

  A = regs[10];
  B = regs[12];

  
  Serial.print("A = ");
  Serial.println(A);


  Serial.print("B = ");
  Serial.println(B);

  Serial.println("============================================");
  delay(1000);

  
  Serial.print("successful_requests: ");
  Serial.println(packets[PACKET1].successful_requests);
  Serial.println("                        || ");
  Serial.print("failed_requests: ");
  Serial.print(packets[PACKET1].failed_requests);
  Serial.println("     || ");
  Serial.print("exception_errors: ");
  Serial.print(packets[PACKET1].exception_errors);
  Serial.println("     || ");
  Serial.print("connection: ");
  Serial.print(packets[PACKET1].connection);
  Serial.println("           || ");
 delay(1000);
}

Also Please clarify me this line of code what does number 10 represents although we have register number starting from 0 to 30

modbus_construct(&packets[PACKET1], 1, READ_HOLDING_REGISTERS, 40133, 2, 10); 

Hi This is a forum for Particle and its products and we really can’t offer much guidance on Arduino products. Can I suggest you register with this forum (or similar)

https://forum.arduino.cc

2 Likes

Thanks for your reply sir, But the post available in the link https://community.particle.io/t/rs-485-modbus-library/11981/52 made my day successful. I tried the available code in post no 52 to read data from energy meter and true it got working.

I need help in modifying code so as to read data from another modbus slave i.e I connected two Modbus devices in daisy chain fashion with Sl no 1 and Sl no 2. The code reads data from one slave and I want to read data from another device in same code. Can anybody help me pls

The code used in that post uses the ModbusMaster library which makes addressing individual devices straight forward.
If you look in post #51 in that thread you will find this

and with that the answer would be straight forward for any other slave ID.

However, since this is not an Arduino forum and hence has no affinity to the SimpleModbusMaster library you appear to be using it’s beyond our scope to make it fit a library currently not Particle user seems to be using.

Thanks for your reply sir.

I changed the code to modbusmaster library. I’m attaching the code i used to read data from energy meter

#include <ModbusMaster.h>

/*!
  We're using a MAX485-compatible RS485 Transceiver.
  Rx/Tx is hooked up to the hardware serial port at 'Serial'.
  The Data Enable and Receiver Enable pins are hooked up as follows:
*/
#define MAX485_DE      33
#define MAX485_RE_NEG  32

#define DEBUGON 1

// Transmission delay between modbus calls
#define XMITDELAY 1000

// 0 for 0-based, 1 for 1-based numbering
#define BASED_NUMBERING 1

/*********
* Retreives holding register data while handling device sleeping conditions
**********/

// instantiate ModbusMaster object
ModbusMaster node;
//#define reg_qty 11 (not used in this example)
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);
  // Init in receive mode
  digitalWrite(MAX485_RE_NEG, 0);
  digitalWrite(MAX485_DE, 0);

  // Modbus communication runs at 115200 baud
  Serial.begin(9600);
  Serial2.begin(9600, SERIAL_8E1);
  // Modbus slave ID 1, Serial1 on the Particle Photon
  node.begin(1, Serial2);
 
  // Callbacks allow us to configure the RS485 transceiver correctly
  node.preTransmission(preTransmission);
  node.postTransmission(postTransmission);
}

bool state = true; // (not used in this example)

void loop()
{
  uint16_t data[50];
 
 getHoldingRegisterData( 4301,  50,  data);  
 delay(1000);
 

}

bool getHoldingRegisterData(uint16_t registerAddress, uint16_t regSize, uint16_t* data){

uint8_t j, result;

if(DEBUGON){
  Serial.print(F("Reading register: "));
  Serial.print(registerAddress);
  Serial.print(F(" regSize: "));
  Serial.print(regSize);
  Serial.print(F(" sizeof(data): "));
  Serial.print(sizeof(&data));
  Serial.print(F(" XMITDELAY: "));
  Serial.println(XMITDELAY);
  Serial.println("");
}

// Delay and get register data.

result = node.readHoldingRegisters(registerAddress-BASED_NUMBERING, regSize);
delay(XMITDELAY);

// LT is sleeping, ping it a couple more times.

if(result ==node.ku8MBResponseTimedOut){

        if(DEBUGON){
          Serial.println(F("LT: Response timed out. Trying again. "));
        }

    int i =0;

    while(i < 50){

      result = node.readHoldingRegisters(registerAddress-BASED_NUMBERING, 50);
      delay(XMITDELAY);

   
      if(result == node.ku8MBResponseTimedOut){
        if(DEBUGON) Serial.println(F("LT: Failed. Response timed out. Adjust the XMITDELAY and/or ku8MBResponseTimeout? "));
      }
      else break;

      i++;

    }

}

if (result == node.ku8MBSuccess) {
  if(DEBUGON){
    Serial.print(F("LT: Success, Received data: "));
  }

  for (j = 0; j < 50; j++) {

    data[j] = node.getResponseBuffer(j);
   
    if(DEBUGON){
      Serial.print(data[j], HEX);
      Serial.print(F(" "));
    }
    
  }
  unsigned int W[] = {data[0], data[1]};
   unsigned int Rw[] = {data[2], data[3]};
   unsigned int Ry[] = {data[4], data[5]};
   unsigned int Rb[] = {data[6], data[7]};
   unsigned int PFavg[] = {data[8], data[9]};
   unsigned int PFr[] = {data[10], data[11]};
   unsigned int PFY[] = {data[12], data[13]};
   unsigned int PFB[] = {data[14], data[15]};
   unsigned int VLLavg[] = {data[16], data[17]};
   unsigned int Vry[] = {data[18], data[19]};
   unsigned int Vyb[] = {data[20], data[21]};
   unsigned int Vbr[] = {data[22], data[23]};
   unsigned int VLNavg[] = {data[24], data[25]};
   unsigned int VR[] = {data[26], data[27]};
   unsigned int VY[] = {data[28], data[29]};
   unsigned int VB[] = {data[30], data[31]};
   unsigned int Itotal[] = {data[32], data[33]};
   unsigned int IR[] = {data[34], data[35]};
   unsigned int IY[] = {data[36], data[37]}; 
   unsigned int IB[] = {data[38], data[39]};
   unsigned int F[] = {data[40], data[41]};
   unsigned int Wh[] = {data[42], data[43]};
   unsigned int RWh[] = {data[44], data[45]};
   unsigned int YWh[] = {data[46], data[47]};
   unsigned int BWh[] = {data[48], data[49]};
         

uint32_t a1 = (uint32_t)data[1] << 16 | data[0];
float b1 = *(float*)&a1;
Serial.print (b1, 3);
Serial.print(F(" "));

uint32_t a5 = (uint32_t)data[9] << 16 | data[8];
float b5 = *(float*)&a5;
Serial.print (b5, 3);
Serial.print(F(" "));

uint32_t a9 = (uint32_t)data[17] << 16 | data[16];
float b9 = *(float*)&a9;
Serial.print (b9, 3);
Serial.print(F(" "));

uint32_t a13 = (uint32_t)data[25] << 16 | data[24];
float b13 = *(float*)&a13;
Serial.print (b13, 3);
Serial.print(F(" "));

uint32_t a14 = (uint32_t)data[27] << 16 | data[26];
float b14 = *(float*)&a14;
Serial.print (b14, 3);
Serial.print(F(" "));

uint32_t a17 = (uint32_t)data[33] << 16 | data[32];
float b17 = *(float*)&a17;
Serial.print (b17, 3);
Serial.print(F(" "));

uint32_t a18 = (uint32_t)data[35] << 16 | data[34];
float b18 = *(float*)&a18;
Serial.print (b18, 3);
Serial.print(F(" "));

uint32_t a19 = (uint32_t)data[37] << 16 | data[36];
float b19 = *(float*)&a19;
Serial.print (b19, 3);
Serial.print(F(" "));

uint32_t a20 = (uint32_t)data[39] << 16 | data[38];
float b20 = *(float*)&a20;
Serial.print (b20, 3);
Serial.print(F(" "));

uint32_t a21 = (uint32_t)data[41] << 16 | data[40];
float b21 = *(float*)&a21;
Serial.print (b21, 3);
Serial.print(F(" "));

uint32_t a22 = (uint32_t)data[43] << 16 | data[42];
float b22 = *(float*)&a22;
Serial.print (b22, 3);
Serial.print(F(" "));


     
      
      
  if(DEBUGON){
    Serial.println("");
  }

  node.clearResponseBuffer();
  node.clearTransmitBuffer();
  return true;

}
else{
  if(DEBUGON){
  Serial.print(F("Failed, Response Code: "));
  Serial.println(result, HEX);
  }
}

node.clearResponseBuffer();
node.clearTransmitBuffer();
return false;
}

My requirement is, I want to read data from another energy meter too. My energy meters are
Elmeasure BM 5140 and LG+ 5310. How can I change above single code to read data from both the meters. Please help

I understand both the meters can be connected as shown below sharing single RS485 line but collecting data by setting different serial no to both the meters

image

If you want another meter (aka node) you’d also need two node objects (with an unique ID for each of them).
To keep the code changes minimal I’d do this

// ModbuusMaster node; // repalce with
ModbusMaster *actNode;
ModbusMaster node[2] = { ModbusMaster(Serial2, ID0), ModbusMaster(Serial2, ID1) }; 
...
void setup() {
  ...
  node[0].begin();
  node[1].begin();
  ...
}

Then replace all node.* calls with actNode->* and before requesting data from a specific node just set the pointer accordingly (e.g. actNode = &node[0]; or actNode = &node[1];).

While there is a lot more potential in streamlining your code above, this should get you unstuck.

thanks for your reply sir, FYI there exists a change in addressing the holding register of both the meters.

In case of BM5140, it has 4 channels for energy measurement, now I’m getting data from channel 3, its register address starts from 4301,
As far as LG+ 5130, it has only one channel of measurement and its holding register address starts from 40101, How this has to be included in void loop section
Can the below command can work in the way I specified?

void loop()
{
  uint16_t data[50];
 
 getHoldingRegisterData( 4301,  50,  data);  
 delay(1000);
 getHoldingRegisterData(40101, 50, data1);

}

Hi @ScruffR, we use that library with ESP32 to read up to 4 slaves, I think the code can be compiled with Particle photon just require to edit the IO accordingly.
here is full example --> ModbusMaster Multiple slaves
here is the library --> SensorModbusMaster

Hello All,

Back Again with some problem, I hope people here will help me to clarify the doubts in programming. As this forum only provides infor regarding Modbus communication.
Now I’m trying to collect data from energy meter by Nodemcu. As Nodemcu contains only one Hardware Serial port, I used Software serial to acquire data. I used the previously mentioned code to acquire data with little modifications but Serial monitor continuously prints "LT: Failed. Response timed out. Adjust the XMITDELAY and/or ku8MBResponseTimeout? "… What could be the reason? does software serial will not work with this code. Im attaching the code used for testing.


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

/*!
  We're using a MAX485-compatible RS485 Transceiver.
  Rx/Tx is hooked up to the hardware serial port at 'Serial'.
  The Data Enable and Receiver Enable pins are hooked up as follows:
*/
#define MAX485_DE      D4
#define MAX485_RE_NEG  D1

#define DEBUGON 1

// Transmission delay between modbus calls
#define XMITDELAY 1000

// 0 for 0-based, 1 for 1-based numbering
#define BASED_NUMBERING 1

/*********
* Retreives holding register data while handling device sleeping conditions
**********/

// instantiate ModbusMaster object
ModbusMaster node;
SoftwareSerial mySerial(D7,D8);


//#define reg_qty 11 (not used in this example)
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);
  // Init in receive mode
  digitalWrite(MAX485_RE_NEG, 0);
  digitalWrite(MAX485_DE, 0);

  // Modbus communication runs at 115200 baud
  Serial.begin(115200);
  mySerial.begin(9600);
  // Modbus slave ID 1, Serial1 on the Particle Photon
  node.begin(1, mySerial);
 
  // Callbacks allow us to configure the RS485 transceiver correctly
  node.preTransmission(preTransmission);
  node.postTransmission(postTransmission);
}

bool state = true; // (not used in this example)

void loop()
{
  uint16_t data[50];
 
 getHoldingRegisterData( 4301,  50,  data);  
 delay(1000);
 

}

bool getHoldingRegisterData(uint16_t registerAddress, uint16_t regSize, uint16_t* data){

uint8_t j, result;

if(DEBUGON){
  Serial.print(F("Reading register: "));
  Serial.print(registerAddress);
  Serial.print(F(" regSize: "));
  Serial.print(regSize);
  Serial.print(F(" sizeof(data): "));
  Serial.print(sizeof(&data));
  Serial.print(F(" XMITDELAY: "));
  Serial.println(XMITDELAY);
  Serial.println("");
}

// Delay and get register data.

result = node.readHoldingRegisters(registerAddress-BASED_NUMBERING, regSize);
delay(XMITDELAY);

// LT is sleeping, ping it a couple more times.

if(result ==node.ku8MBResponseTimedOut){

        if(DEBUGON){
          Serial.println(F("LT: Response timed out. Trying again. "));
        }

    int i =0;

    while(i < 50){

      result = node.readHoldingRegisters(registerAddress-BASED_NUMBERING, 50);
      delay(XMITDELAY);

   
      if(result == node.ku8MBResponseTimedOut){
        if(DEBUGON) Serial.println(F("LT: Failed. Response timed out. Adjust the XMITDELAY and/or ku8MBResponseTimeout? "));
      }
      else break;

      i++;

    }

}

if (result == node.ku8MBSuccess) {
  if(DEBUGON){
    Serial.print(F("LT: Success, Received data: "));
  }

  for (j = 0; j < 50; j++) {

    data[j] = node.getResponseBuffer(j);
   
    if(DEBUGON){
      Serial.print(data[j], HEX);
      Serial.print(F(" "));
    }
    
  }
  unsigned int W[] = {data[0], data[1]};
   unsigned int Rw[] = {data[2], data[3]};
   unsigned int Ry[] = {data[4], data[5]};
   unsigned int Rb[] = {data[6], data[7]};
   unsigned int PFavg[] = {data[8], data[9]};
   unsigned int PFr[] = {data[10], data[11]};
   unsigned int PFY[] = {data[12], data[13]};
   unsigned int PFB[] = {data[14], data[15]};
   unsigned int VLLavg[] = {data[16], data[17]};
   unsigned int Vry[] = {data[18], data[19]};
   unsigned int Vyb[] = {data[20], data[21]};
   unsigned int Vbr[] = {data[22], data[23]};
   unsigned int VLNavg[] = {data[24], data[25]};
   unsigned int VR[] = {data[26], data[27]};
   unsigned int VY[] = {data[28], data[29]};
   unsigned int VB[] = {data[30], data[31]};
   unsigned int Itotal[] = {data[32], data[33]};
   unsigned int IR[] = {data[34], data[35]};
   unsigned int IY[] = {data[36], data[37]}; 
   unsigned int IB[] = {data[38], data[39]};
   unsigned int F[] = {data[40], data[41]};
   unsigned int Wh[] = {data[42], data[43]};
   unsigned int RWh[] = {data[44], data[45]};
   unsigned int YWh[] = {data[46], data[47]};
   unsigned int BWh[] = {data[48], data[49]};
         

uint32_t a1 = (uint32_t)data[1] << 16 | data[0];
float b1 = *(float*)&a1;
Serial.print (b1, 3);
Serial.print(F(" "));

uint32_t a5 = (uint32_t)data[9] << 16 | data[8];
float b5 = *(float*)&a5;
Serial.print (b5, 3);
Serial.print(F(" "));

uint32_t a9 = (uint32_t)data[17] << 16 | data[16];
float b9 = *(float*)&a9;
Serial.print (b9, 3);
Serial.print(F(" "));

uint32_t a13 = (uint32_t)data[25] << 16 | data[24];
float b13 = *(float*)&a13;
Serial.print (b13, 3);
Serial.print(F(" "));

uint32_t a14 = (uint32_t)data[27] << 16 | data[26];
float b14 = *(float*)&a14;
Serial.print (b14, 3);
Serial.print(F(" "));

uint32_t a17 = (uint32_t)data[33] << 16 | data[32];
float b17 = *(float*)&a17;
Serial.print (b17, 3);
Serial.print(F(" "));

uint32_t a18 = (uint32_t)data[35] << 16 | data[34];
float b18 = *(float*)&a18;
Serial.print (b18, 3);
Serial.print(F(" "));

uint32_t a19 = (uint32_t)data[37] << 16 | data[36];
float b19 = *(float*)&a19;
Serial.print (b19, 3);
Serial.print(F(" "));

uint32_t a20 = (uint32_t)data[39] << 16 | data[38];
float b20 = *(float*)&a20;
Serial.print (b20, 3);
Serial.print(F(" "));

uint32_t a21 = (uint32_t)data[41] << 16 | data[40];
float b21 = *(float*)&a21;
Serial.print (b21, 3);
Serial.print(F(" "));

uint32_t a22 = (uint32_t)data[43] << 16 | data[42];
float b22 = *(float*)&a22;
Serial.print (b22, 3);
Serial.print(F(" "));


     
      
      
  if(DEBUGON){
    Serial.println("");
  }

  node.clearResponseBuffer();
  node.clearTransmitBuffer();
  return true;

}
else{
  if(DEBUGON){
  Serial.print(F("Failed, Response Code: "));
  Serial.println(result, HEX);
  }
}

node.clearResponseBuffer();
node.clearTransmitBuffer();
return false;
}

Meanwhile I tried the the code (mentioned in reply no 6) with ESP32 board (https://www.aliexpress.com/item/32814642296.html) which has 3 hardware serial ports. I used UART port no 2 (GPIO16 & GPIO17). Here too Serial monitor prints Time out as mentioned in case of ESP8266.

Also in your previous post, you shared library in which ESP32 reads upto 4 slaves. But in the code. it is been mentioned to swap the Serial Ports number for UART1 and UART2. I understand since the connection is made like that in the circuit board of Modbus box, but does it have to be followed for other ESP32 boards???

pls clarify whether DE and RE pin has to be shorted together and connected to pin4, since modbus master library uses individual digital pins for RE and DE pins…

Also please explain what does 0x04 specifies in this line of code

 if (modbus.int32FromRegister(0x04, reg_addr + Mb_offset , littleEndian))

Actually no, not ModBus.
However, as said earlier

So NodeMCU is not our focus but your question is very specific to that board and has nothing to do with Particle whatsoever.