How To Implement Emonlib & Modbus Client

Hi All,

I am setting up an arduino running emonlib and need to send values via RS232 using modbus protocol to a PLC.
The arduino is pushing out data to its serial interface so far,need a push in the right direction regarding what serial modbus client library works well with emonlib and perhaps an example sketch to see how to implement this.
TIA

emonLib does not output serial data - it only produces the numbers as variables. You will need to write or modify your sketch to format those numbers as strings to pass to the Modbus API. It’s decades since I had any dealings with Modbus, so rusty isn’t the word. There is an Arduino Modbus library, I’d look there first.

2 Likes

Hi chaps,
Thanks Robert for the reply,
So far I have emonlib sending values every 5 seconds over serial and a solar panel voltage monitor on A3 using the sketch below 


   // These are from left to right: real power, apparent power, rms voltage, rms current and power factor.
   
   #include "EmonLib.h"              // Include Emon Library
   EnergyMonitor emon1;              // Create an instance
   #define NUM_SAMPLES 10
   int sum = 0;                    // sum of samples taken for DC solar volt meter
   unsigned char sample_count = 0; // current sample number
   float voltage = 0.0;            // calculated voltage

void setup() {
  
  Serial.begin(9600);
  emon1.voltage(5, 173.85, 1.7);  // Voltage: input pin, calibration, phase_shift
  emon1.current(4, 111.1);        // Current: input pin, calibration.
}

void loop() {
  
  emon1.calcVI(20,2000);          // Calculate all. No.of crossings, time-out
  //Serial.println ("Real Power,Apparent Power,RMS Voltage,RMS Current,Power Factor");
  //emon1.serialprint();            // Print out all variables
  Serial.print(" D4567 ");Serial.print(emon1.realPower);
  Serial.print(" D4567 ");Serial.print(emon1.apparentPower);
  Serial.print(" D2168 ");Serial.print(emon1.Vrms);
  Serial.print(" D2166 ");Serial.print(emon1.Irms);
  Serial.print(" D4567 ");Serial.print(emon1.powerFactor);
  
      while (sample_count < NUM_SAMPLES) {
        sum += analogRead(A3);
        sample_count++;
        delay(10);
      }        
             voltage = ((float)sum / (float)NUM_SAMPLES * 5.02) / 1024.0;
    Serial.print(" D2170 ");Serial.print(voltage * 11.211);
   
    Serial.println("   ");
    Serial.println("   ");
    sample_count = 0;
    sum = 0;
 
   // delay(5000);
}

I am trying to get voltage,current,power,etc values sent to different registers (eg D1544,D1548,D1554) to a PLC using a serial modbus protocol.

The following example works but I have had no success getting real time values from emonlib sent to the PLC using this library.

/*
  Modbus-Arduino Example - TempSensor (Modbus Serial)
  Copyright by André Sarmento Barbosa
  http://github.com/andresarmento/modbus-arduino
*/
 
#include  "Modbus.h"
#include  "ModbusSerial.h" 


//Modbus Registers Offsets (0-9999)
const int SENSOR_IREG = 100; 
//Used Pins
const int sensorPin = A0;

// ModbusSerial object
ModbusSerial mb;

long ts;

void setup() {
    // Config Modbus Serial (port, speed, byte format) 
    mb.config(&Serial, 38400, SERIAL_8N1);
    // Set the Slave ID (1-247)
    mb.setSlaveId(10);  

    // Add SENSOR_IREG register - Use addIreg() for analog Inputs
    mb.addIreg(SENSOR_IREG);
    
    ts = millis();
}

void loop() {
   //Call once inside loop() - all magic here
   mb.task();
   
   //Read each two seconds
   if (millis() > ts + 2000) {   
       ts = millis();
       //Setting raw value (0-1024)
       mb.Ireg(SENSOR_IREG, analogRead(sensorPin));
   } 
}

Sketch will not compile without const int SENSOR_IREG = 100; and mb.Ireg(SENSOR_IREG, analogRead(sensorPin)); commented out.

Any pointers on how to get emonlib values sent instead of arduino pin states,or an easier to configure modbus library,welcome.

Thanks
Dave

I’ve been looking at the “ModbusMaster” library (not “ModbusSerial”) and that appears (famous last words) to be fairly straightforward.
The source file:
https://raw.githubusercontent.com/4-20ma/ModbusMaster/master/src/ModbusMaster.cpp
Documentation: ModbusMaster: Main Page
A class object needs to be instantiated, a begin( ) method called, then there are a number of ways to set up the data and send it. You can also write a single register at a time by giving it an address and a value (writeSingleRegister( ) ), or a contiguous sequence of registers starting at an address (writeMultipleRegisters( ) ). Or you can send a block of data. The first two look the most useful to you.

You’d (probably) put all the setup stuff in setup( ), and the writes where you have the serial.prints in loop( ), naturally. But you might possibly want to start and shut down the modbus each time in loop( ).

emonhub was recently updated to support modbus see examples in /conf

Emonhub will post data from modus to MQTT & Emoncms. I have not tested this it was contributed by:

In a typical Modbus setup the Modbus Master (Client) send data request to one or more Modbus Slaves (Servers) and gets responses, The Slave can’t push data.
Not that difficult to do this with EmonTX Shield. e.g. EmonTX Shield + Ethernet Shield + Arduino Uno + Modbus This is an Modbus TCP version. You can adapt it for Modbus Serial.

Thanks a lot for the replies,will get the tcp modbus communicating then take it from there.
Regards
Dave

Have managed to integrate emonlib and a modbus slave arduino libraries so far.
When connected to a master simulator running on PC, values are sent,but only once,in other words,voltage,current,etc do not refresh,only the initial value read after the arduino is reset gets sent to the simulator.
If a manual reset is done on the arduino then new values get sent,it seems the code is not “looping” only reading once.
Please see code below ,thanks
Dave

New Work In Progress.ino (2.3 KB)

I don’t understand your “while(1) { 
}” inside loop( ).
That executes the code inside the curly braces forever, it never drops out, so loop( ) only executes once and never reaches its closing curly brace, so calcVI( ) is only ever executed once, just before the code enters the while( ) condition.

Remember, in the Atmel processor itself - not part of what you see, is code that calls setup( ) once and then calls loop( ) repeatedly.

What you have is bypassing that mechanism. I don’t think it’s what you wanted.

Thanks for the reply,
What you say makes perfect sense and explains why nothing gets updated.
However moving the "while(1) " up above the calcVI( ) or removing it altogether,causes an error on the simulator and the arduino does not reply to the simulator (the TX led never lights on uno)
Perhaps I should try another modbus slave library,can you recommend one perhaps or explain why "while(1) " is needed here ?
Regards
Dave

Not knowing the thinking of the people who wrote that code, no. But I suspect they didn’t have an Arduino in mind, and the idea was to run that chunk of code continuously, forever sending messages. Is that the behaviour you saw?

I haven’t checked the meaning of each of the library calls (that’s your job!), but it ought to work if you take out the line containing the while( ) (and its corresponding closing brace).

But what’s this about a slave library? As I remember it, the Modbus Master controls the bus. If the data is flowing from Arduino to PC, then if the PC is the master, the Arduino must either react to a request for data by running calcVI( ) and sending the data once it’s available (and the master needs to wait while that happens), or the Arduino puts the data into registers when it feels the need and on receipt of a request it returns the last set of readings.

What I think you need to do is find out what all those library calls do and how they work. If it expects the second option, then you still need to take out the while( ), because loop( ) does the equivalent, but you need to include calcVI in the loop only once every few seconds - and even then, you might have a problem because while it’s away calculating the power etc, it won’t reply to a Modbus request and the master might time out.

But if the rest of your system permits (are there other devices on the bus?), it should be a lot easier to have the Arduino as the Master. Then it can tell the slave (the PC) to accept some data and send the data when it feels like it, which is immediately after you’ve measured with calcVI( ), and then go to sleep for 5 or 10 s.

With a bit of inspired guesswork, I think I’ve found the library you are using.

slave.run( ) appears to poll the serial port looking for input, all the rest of the while( ) loop is putting the values there. Therefore, you have a choice of 2: either put calcVI( ) inside the while( ) loop, or remove the while( ) loop and rely on loop( ).
But in each case, you have the problem of the time taken by calcVI( ) to measure the voltage and current, and while that is happening (~ 200 ms), run( ) won’t be checking the serial input buffer and any requests from the master will be piling up - or worse, overflowing.

What I think you must do is wrap calcVI( ) inside a timer conditional (go through calcVI( ) once every 10 s, say), so that the voltages and currents are updated with that frequency, but execute run( ) every time, so that messages from the master are handled promptly. Even so, you will still have the problem that the slave won’t respond while it is running calcVI( ), so the master should poll the slave only once every 10 s - at the same rate as calcVI( ) is run.

Alternatively, it might be possible to create your own protocol around the outside of the Modbus library, by sending a message (setting a register) to command the slave to run calcVI( ), then returning (say) 250 ms later to retrieve the values. In that way, the Modbus master device is responsible for controlling when readings are taken and for fetching the values from the slave.

Hi Robert,
Removing the while(1) and wrapping calcVI inside a 10 second conditional timer and slowing the PC polling time down from every second to 5 seconds seems to have made the code pretty stable.
Will look into making the arduino the master and the PLC the slave,not sure if the PLC can be slave though.
Thank you for you input.
Regards
Dave