Multiple Modbus RTU slaves

Hi
I need some help to convert this code so it can handle two or more slaves on the Kamstrup Modbus network.

Status on the code/projekt:

  • Layout, see attached image.
  • Code "master _v91" can read and print, on LCD, from SDM 1 and 2. It can also at te same time read and print value one and two from slave 3 om the Kamstrup network.

Code in the Modbus Master _v91.

#include <SPI.h>
#include <Wire.h>
#include <SoftwareSerial.h>                                                   
#include <SDM630KAM382.h>    
#include <LiquidCrystal_I2C.h>
#include <ModbusRtu.h>

SoftwareSerial swSerSDM(6, 7);  
  
SDM sdm(swSerSDM, 9600, 5);   

#define TXEN  4 

// data array for modbus network sharing
uint16_t au16data[16];
uint8_t u8state;

volatile unsigned long counter = 0;
volatile unsigned long lastImpulse = 0;
volatile unsigned long diffImpulse = 0;
float currentWatt = 0;
float LcdcurrentWatt = 0;
float RD_S_ID3_VAL1_1 = 0;

volatile unsigned long counter2 = 0;
volatile unsigned long lastImpulse2 = 0;
volatile unsigned long diffImpulse2 = 0;
float currentWatt2 = 0;
float LcdcurrentWatt2 = 0;
float RD_S_ID3_VAL2_1 = 0;

LiquidCrystal_I2C lcd(0x3f, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE);

Modbus master(0,0,TXEN); 

modbus_t telegram;

unsigned long u32wait;

void setup() {
  
  sdm.begin();  
  
  master.begin( 19200 );
  master.setTimeOut( 2000 );
  u32wait = millis() + 1000;
  u8state = 0; 

    lcd.begin(20,4);

    lcd.setCursor(6,0);
    lcd.print("Home");
    lcd.setCursor(4,1);
    lcd.print("Power");
    lcd.setCursor(2,2);
    lcd.print("BY: Mr.Y");
    lcd.setCursor(0,3);
    lcd.print("--------------------");
    
    delay(5000);
    lcd.clear();
  
}

void loop() {
  switch( u8state ) {
  case 0: 
    if (millis() > u32wait) u8state++; // wait state
    break;
  case 1: 
    telegram.u8id = 3; // slave address
    telegram.u8fct = 4; // function code
    telegram.u16RegAdd = 1; // start address in slave
    telegram.u16CoilsNo = 2; // number of elements (coils or registers) to read
    telegram.au16reg = au16data; // pointer to a memory array in the Arduino
    RD_S_ID3_VAL1_1 = au16data[0];
    RD_S_ID3_VAL2_1 = au16data[1];
    
    master.query( telegram ); // send query (only once)
    u8state++;
    break;
  case 2:
    master.poll(); // check incoming messages
    if (master.getState() == COM_IDLE) {
      u8state = 0;
      u32wait = millis() + 100; 
    }
    break;
  }
     lcd.setCursor(0,0);
     lcd.print("KamEL-1(W):");
     lcd.setCursor(11,0);
     lcd.print(RD_S_ID3_VAL1_1);
     lcd.print("   ");

     lcd.setCursor(0,1);
     lcd.print("KamEL-2(W):");
     lcd.setCursor(11,1);
     lcd.print(RD_S_ID3_VAL2_1);
     lcd.print("   ");   

     lcd.setCursor(0,2);
     lcd.print ("SDM-ID1(W):");
     lcd.setCursor(11,2);
     lcd.print(sdm.readVal(SDM630_POWERTOTAL, 0x01), 2);
     lcd.print("  ");

     lcd.setCursor(0,3);
     lcd.print ("SDM-ID2(W):");
     lcd.setCursor(11,3);
     lcd.print(sdm.readVal(SDM630_POWERTOTAL, 0x02), 2);
     lcd.print("  ");
}

I have tried my self but no luck. Here is my contribution.

#include <SPI.h>
#include <Wire.h>
#include <SoftwareSerial.h>                                              
#include <SDM630KAM382.h>    
#include <LiquidCrystal_I2C.h>
#include <ModbusRtu.h>

SoftwareSerial swSerSDM(6, 7);  
  
SDM sdm(swSerSDM, 9600, 5);   

#define TXEN  4 

// data array for modbus network sharing
uint16_t au16data[4];
uint16_t au16data2[4];
uint8_t u8state;
uint8_t u8query;

volatile unsigned long counter = 0;
volatile unsigned long lastImpulse = 0;
volatile unsigned long diffImpulse = 0;
float currentWatt = 0;
float LcdcurrentWatt = 0;
float RD_S_ID3_VAL1_1 = 0;
float RD_S_ID4_VAL1_1 = 0;

volatile unsigned long counter2 = 0;
volatile unsigned long lastImpulse2 = 0;
volatile unsigned long diffImpulse2 = 0;
float currentWatt2 = 0;
float LcdcurrentWatt2 = 0;
float RD_S_ID3_VAL2_1 = 0;
float RD_S_ID4_VAL2_1 = 0;

LiquidCrystal_I2C lcd(0x3f, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE);

Modbus master(0,0,TXEN); 

modbus_t telegram[2];

unsigned long u32wait;

void setup() {
  
  sdm.begin();  
  
  master.begin( 19200 );
  master.setTimeOut( 5000 );
  u32wait = millis() + 1000;
  // u8state = 0; 
  u8state = u8query = 0; 
  
    lcd.begin(20,4);

    lcd.setCursor(6,0);
    lcd.print("Rekasta 8");
    lcd.setCursor(4,1);
    lcd.print("Energikollen");
    lcd.setCursor(2,2);
    lcd.print("Stefan Eriksson");
    lcd.setCursor(0,3);
    lcd.print("--------------------");
    
    delay(5000);
    lcd.clear();

    telegram[0].u8id = 4; // slave address
    telegram[0].u8fct = 4; // function code
    telegram[0].u16RegAdd = 1; // start address in slave
    telegram[0].u16CoilsNo = 2; // number of elements (coils or registers) to read
    telegram[0].au16reg = au16data; // pointer to a memory array in the Arduino
    
    telegram[1].u8id = 3; // slave address
    telegram[1].u8fct = 4; // function code
    telegram[1].u16RegAdd = 1; // start address in slave
    telegram[1].u16CoilsNo = 2; // number of elements (coils or registers) to read
    telegram[1].au16reg = au16data2; // pointer to a memory array in the Arduino
  
}

void loop() {
  switch( u8state ) {
  case 0: 
    if (millis() > u32wait) u8state++; // wait state
    break;
  case 1: 
    
    master.query( telegram[0] ); // send query (only once)
    u8state++;
    
  case 2: 
    if (millis() > u32wait) u8state++; // wait state
    break;
  case 3: 
    
    master.query( telegram[1] ); // send query (only once)
    u8state++;
    
    break;
  case 4:
    master.poll(); // check incoming messages

    RD_S_ID3_VAL1_1 = au16data[1];
    RD_S_ID3_VAL2_1 = au16data[0];
    RD_S_ID4_VAL1_1 = au16data2[0];
    RD_S_ID4_VAL2_1 = au16data2[1];
    
    if (master.getState() == COM_IDLE) {
      u8state = 0;
      u32wait = millis() + 100; 
    }
    
    break;
  }
     lcd.setCursor(0,0);
     lcd.print("KamEL-1(W):");
     lcd.setCursor(11,0);
     lcd.print(RD_S_ID3_VAL2_1);
     lcd.print("   ");

     lcd.setCursor(0,1);
     lcd.print("KamEL-4(W):");
     lcd.setCursor(11,1);
     lcd.print(RD_S_ID4_VAL2_1);
     lcd.print("   ");   

     lcd.setCursor(0,2);
     lcd.print ("SDM-ID1(W):");
     lcd.setCursor(11,2);
     lcd.print(sdm.readVal(SDM630_POWERTOTAL, 0x01), 2);
     lcd.print("  ");

     lcd.setCursor(0,3);
     lcd.print ("SDM-ID2(W):");
     lcd.setCursor(11,3);
     lcd.print(sdm.readVal(SDM630_POWERTOTAL, 0x02), 2);
     lcd.print("  ");
}

Code for Modbus Slave 03 (_v6.1) and 04 (_v6.2).
See attached file.

Appreciate all suggestions for general improvements and the solution to my problem reading from several Modbus slaves, on the Kamstrup Modbus network.

// Vind89

SDM630KAM382.zip (3.87 KB)

Modbus-Master-Slave-for-Arduino-master.zip (200 KB)

Slave_KAM382v61.ino (2.53 KB)

Slave_KAM382v62.ino (2.72 KB)

Managed to make it work, see code below.

But now i use a "delay (200);" between asking for data/receiving data from slave ID:3 and asking for data/receiving data from slave ID:4.

Is there any other way to do replace the "delay (200);". As an example checking if the serial port are available/free?

#include <SPI.h>
#include <Wire.h>
#include <SoftwareSerial.h>                                              
#include <SDM630KAM382.h>    
#include <LiquidCrystal_I2C.h>
#include <ModbusRtu.h>

SoftwareSerial swSerSDM(6, 7);  
  
SDM sdm(swSerSDM, 9600, 5);   

#define TXEN  4 

uint16_t au16data[4];
uint16_t au16data2[4];
uint8_t u8state;
uint8_t u8query;

volatile unsigned long counter = 0;
volatile unsigned long lastImpulse = 0;
volatile unsigned long diffImpulse = 0;
float currentWatt = 0;
float LcdcurrentWatt = 0;
float RD_S_ID3_VAL1_1 = 0;
float RD_S_ID4_VAL1_1 = 0;

volatile unsigned long counter2 = 0;
volatile unsigned long lastImpulse2 = 0;
volatile unsigned long diffImpulse2 = 0;
float currentWatt2 = 0;
float LcdcurrentWatt2 = 0;
float RD_S_ID3_VAL2_1 = 0;
float RD_S_ID4_VAL2_1 = 0;

LiquidCrystal_I2C lcd(0x3f, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE);

Modbus master(0,0,TXEN); 

modbus_t telegram[2];

unsigned long u32wait;

void setup() {
  
  sdm.begin();  
  
  master.begin( 19200 );
  master.setTimeOut( 5000 );
  u32wait = millis() + 1000;
  // u8state = 0; 
  u8state = u8query = 0; 
  
    lcd.begin(20,4);

    lcd.setCursor(6,0);
    lcd.print("Rekasta 8");
    lcd.setCursor(4,1);
    lcd.print("Energikollen");
    lcd.setCursor(2,2);
    lcd.print("Stefan Eriksson");
    lcd.setCursor(0,3);
    lcd.print("--------------------");
    
    delay(5000);
    lcd.clear();

    telegram[0].u8id = 3;                              // slave address
    telegram[0].u8fct = 4;                             // function code
    telegram[0].u16RegAdd = 1;                    // start address in slave
    telegram[0].u16CoilsNo = 2;                    // number of elements (coils or registers) to read
    telegram[0].au16reg = au16data;             // pointer to a memory array in the Arduino
    
    telegram[1].u8id = 4;                              // slave address
    telegram[1].u8fct = 4;                             // function code
    telegram[1].u16RegAdd = 1;                    // start address in slave
    telegram[1].u16CoilsNo = 2;                    // number of elements (coils or registers) to read
    telegram[1].au16reg = au16data2;           // pointer to a memory array in the Arduino

}

void loop() {

    master.query( telegram[0] );                
    
   
    master.poll();                                           // check incoming messages

      RD_S_ID3_VAL1_1 = au16data[0]; 
      RD_S_ID3_VAL2_1 = au16data[1];  
    
 delay(200);                                                 // Is there any other way to do this?
 
    master.query( telegram[1] );
  
    master.poll();

      RD_S_ID4_VAL1_1 = au16data2[0]; 
      RD_S_ID4_VAL2_1 = au16data2[1];  
 
     lcd.setCursor(0,0);
     lcd.print("KamEL-3(W):");
     lcd.setCursor(11,0);
     lcd.print(RD_S_ID3_VAL1_1);
     lcd.print("   ");

     lcd.setCursor(0,1);
     lcd.print("KamEL-3(W):");
     lcd.setCursor(11,1);
     lcd.print(RD_S_ID3_VAL2_1);
     lcd.print("   ");   

     lcd.setCursor(0,2);
     lcd.print ("KamEL-4(W):");
     lcd.setCursor(11,2);
     lcd.print(RD_S_ID4_VAL1_1);
     lcd.print("  ");    

     lcd.setCursor(0,3);
     lcd.print ("KamEL-4(W):");
     lcd.setCursor(11,3);
     lcd.print(RD_S_ID4_VAL2_1);
     lcd.print("  ");   
/*
     lcd.setCursor(0,2);
     lcd.print ("SDM-ID1(W):");
     lcd.setCursor(11,2);
     lcd.print(sdm.readVal(SDM630_POWERTOTAL, 0x01), 2);
     lcd.print("  ");

     lcd.setCursor(0,3);
     lcd.print ("SDM-ID2(W):");
     lcd.setCursor(11,3);
     lcd.print(sdm.readVal(SDM630_POWERTOTAL, 0x02), 2);
     lcd.print("  ");
 */
}

Is there any other way to do replace the "delay (200);"

Yes, wait until master.getState() returns COM_IDLE(). The slave acknowledges the message and you have to wait for that answer to arrive before you can send the next message.