Modbus RTU question

I've started to play with the Modbus stuff and managed to get it working in my own fashion with reading and searching on the net.

At the moment I've one master reads an analogue channel from one slave, I would like to add more slaves (another 3), The part I'm not sure is how to get the master to read from the other 3 slaves.

I've searched and can only seem to find details on a master and slave type of thing.

Is it possible to add to the master to read from 3 other slaves by adding to the master code below or have I got it wrong or looking at totally wrong, My thinking is some sort of counter the display the incoming data.

Master:

#include <SimpleModbusMaster.h>
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd(0x27, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE);  // Set the LCD I2C address

//////////////////// Port information ///////////////////
#define baud 115200
#define timeout 1000
#define polling 200 // the scan rate
#define retry_count 10

// used to toggle the receive/transmit pin on the driver
#define TxEnablePin 13
#define SlaveID 1
#define LED 11

// The total amount of available memory on the master to store data
#define TOTAL_NO_OF_REGISTERS 1

// This is the easiest way to create new packets
// Add as many as you want. TOTAL_NO_OF_PACKETS
// is automatically updated.
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()
{
  Serial.begin(115200);
  lcd.begin(20, 4);
  // Initialize each packet
  modbus_construct(&packets[PACKET1], 1, READ_HOLDING_REGISTERS, 0, 1, 0); //(packet,SlaveId,Function,holdingregAdress,data,locanstartadress)
  //modbus_construct(&packets[PACKET2], 1, PRESET_MULTIPLE_REGISTERS, 1, 1, 0);

  // Initialize the Modbus Finite State Machine
  modbus_configure(&Serial, baud, SERIAL_8N2, timeout, polling, retry_count, TxEnablePin, packets, TOTAL_NO_OF_PACKETS, regs);

  pinMode(LED, OUTPUT);
}

void loop()
{
  lcd.setCursor(0, 0);
  lcd.print("MODBUS RTU");
  modbus_update();

  lcd.setCursor(0, 1);
  lcd.print(regs[0]);
  lcd.print("   ");
}

Slave:

#include <SimpleModbusSlave.h>

//#define  LED 3
#define TxEnablePin 7
#define SlaveID 1

unsigned int holdingRegs[1]; // function 3 and 16 register array
////////////////////////////////////////////////////////////

void setup()
{
  Serial.begin(115200);
  modbus_configure(&Serial, 90600, SERIAL_8N2, SlaveID, TxEnablePin , 1, holdingRegs);

  // modbus_update_comms(baud, byteFormat, id) is not needed but allows for easy update of the
  // port variables and slave id dynamically in any function.
  modbus_update_comms(115200, SERIAL_8N2, 1);

  //pinMode(LED, OUTPUT);
}

void loop()
{
  // modbus_update() is the only method used in loop(). It returns the total error
  // count since the slave started. You don't have to use it but it's useful
  // for fault finding by the modbus master.

  modbus_update();

  holdingRegs[0] = analogRead(A0); // update data to be read by the master to adjust the PWM
}

Any ideas or pointers would be grateful

Thanks

Steve

SimpleModbusMaster is not the best choice for your task. The ModbusMaster library by Doc Walker (in the IDE library manager) is a better match here as you have an interface where you can explicitly read and write registers.

Ok thanks I will look into that

I think I have got this all wrong and Modbus may not be suitable, I was hoping/thinking that I could use an Arduino as a master with an LCD/TFT screen then 4 slaves just to read 1a single voltage and send the data to master so that can display the readings and also send the data to an SD card using openlog.

My idea is I want to monitor 4 X 6V batteries that are wired in series to give 24V output, The slaves would be isolated from each other and ran from there own power supply and then send the data over the RS485 Modbus to the master but reliable commutation with out losing data.

I've been learning how to create your own Modbus RS485 Master and Slave Device using the Arduino Uno Development System from udemy and have the followed it and got them working as shown in the videos and documentation.

But I cant find any sample code for just reading one single channel with more than one salve for the master and salve other than the code I posted.

Are there other ways to do this I wonder, I did consisder using op-amps set up in the differential mode means that there all connected in series but not sure if this would work. I have seen posts on here using relays and stuff but ideally I want to avoid this way and thought that Modbus would be the way to do it ?

Thanks

Steve

I have been playing with some more code, This seems to work loads better.

Master:

// include the library code:
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <ModbusMaster.h>
// initialize the library with the numbers of the interface pins
LiquidCrystal_I2C lcd(0x27, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE);  // Set the LCD I2C address
// instantiate ModbusMaster object
ModbusMaster node;

// variable
int Battery1_modbus;

void setup() {
  lcd.begin(20, 4);   // init display
  // init vars
  Battery1_modbus = 0;
  DisplayCurrentValues();
  // use Serial (port 0); initialize Modbus communication baud rate
  Serial.begin(9600);
  // communicate with Modbus slave ID 10 over Serial (port 0)
  node.begin(10, Serial);
}

void loop() {
  // read modbus data
  static uint32_t i;
  uint8_t j, result;
  uint16_t data[6];
  i++;

  // set word 0 of TX buffer to least-significant word of counter (bits 15..0)
  node.setTransmitBuffer(0, lowWord(i));
  // set word 1 of TX buffer to most-significant word of counter (bits 31..16)
  node.setTransmitBuffer(1, highWord(i));
  result = node.readInputRegisters(0, 1);
  if (result == node.ku8MBSuccess)
  {
    Battery1_modbus = node.getResponseBuffer(0);
  }

  DisplayCurrentValues();
}

void DisplayCurrentValues() {

  String tmpstr;
  lcd.setCursor(0, 0);
  tmpstr = "Battery 1: " + String(Battery1_modbus);
  lcd.print(tmpstr);

}

Slave:

#include <ModbusRtu.h>

// registers in the slave
uint16_t au16data[16] = {
  3, 1415, 9265, 4, 2, 7182, 28182, 8, 0, 0, 0, 0, 0, 0, 1, -1
};

Modbus slave(10, 0, 0); // this is slave @1 and RS-232 or USB-FTDI

void setup() {
  slave.begin( 9600 ); // baud-rate at 9600
}

void loop() {
  au16data[0] = analogRead(A0); // update data to be read by the master to adjust the PWM
  slave.poll( au16data, 16 );

}

But I still need to find out/learn how to add second slave to the master, Once I've got this working I then can add the other slaves.

I've tried the code below, Which I've found out it's not the correct way as it runs really slow, Not sure how to correct this or get it to read both slaves as fast as it does with the master code above.

" Master code test:

// include the library code:
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <ModbusMaster.h>
// initialize the library with the numbers of the interface pins
LiquidCrystal_I2C lcd(0x27, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE);  // Set the LCD I2C address
// instantiate ModbusMaster object
ModbusMaster node;
ModbusMaster node1;


// variable

int Battery1_modbus;
int Battery2_modbus;

void setup() {
  lcd.begin(20, 4);   // init display

  // init vars
  Battery1_modbus = 0;
  Battery2_modbus = 0;

  DisplayCurrentValues();

  // use Serial (port 0); initialize Modbus communication baud rate
  Serial.begin(9600);

  // communicate with Modbus slave ID 10 over Serial (port 0)
  node.begin(10, Serial);
  node1.begin(11, Serial);

}

void loop() {

  // read modbus data

  static uint32_t i;
  uint8_t j, result;
  uint8_t j1, result1;
  uint16_t data[6];

  i++;

  // set word 0 of TX buffer to least-significant word of counter (bits 15..0)
  node.setTransmitBuffer(0, lowWord(i));
  node1.setTransmitBuffer(0, lowWord(i));

  // set word 1 of TX buffer to most-significant word of counter (bits 31..16)
  node.setTransmitBuffer(1, highWord(i));
  node1.setTransmitBuffer(1, highWord(i));

  result = node.readInputRegisters(0, 1);
  if (result == node.ku8MBSuccess)
  {
    Battery1_modbus = node.getResponseBuffer(0);
  }
  result1 = node1.readInputRegisters(0, 1);
  if (result1 == node1.ku8MBSuccess)
  {
    Battery2_modbus = node1.getResponseBuffer(0);
  }

  // display data on LCD
  // CheckForDataChange();
  DisplayCurrentValues();
}



void DisplayCurrentValues() {

  String tmpstr;
  String tmpstr1;

  //lcd.clear();        // clear display
  lcd.setCursor(0, 0);
  tmpstr = "Battery 1: " + String(Battery1_modbus);

  lcd.print(tmpstr);
  lcd.setCursor(0, 1);
  tmpstr1 = "Battery 2: " + String(Battery2_modbus);

  lcd.print(tmpstr1);

}

Steve

I've tried the code below, Which I've found out it's not the correct way as it runs really slow, Not sure how to correct this or get it to read both slaves as fast as it does with the master code above.

What does "runs really slow" mean? I don't see what in the code should make that really slow except that you don't have two slaves with the addressed id on the bus as then it's waiting until it times out.

You don't need the setTransmitBuffer() calls, these are only used for writing to registers.

pylon:
What does "runs really slow" mean? I don't see what in the code should make that really slow except that you don't have two slaves with the addressed id on the bus as then it's waiting until it times out.

You don't need the setTransmitBuffer() calls, these are only used for writing to registers.

Matser code test file is the one that runs slow, Like it takes about a second to update the LCD.

I presume that this is because of having 2 of these node.begin(10, Serial) & node.begin1(11, Serial) as it's not the correct way.
I've got this code counting from 1 t0 4, Just now trying to figure out how to add it to my code so that it changes the slave address to read each slave.

Counting test code:

#define PAYLOAD_SIZE 1 // how many bytes to expect from each I2C salve node
#define NODE_MAX 4 // maximum number of slave nodes (I2C addresses) to probe
#define START_NODE 1 // The starting I2C address of slave nodes
#define NODE_READ_DELAY 5000 // Some delay between I2C node reads

int nodePayload[PAYLOAD_SIZE];

void setup()
{
  Serial.begin(9600);  
  Serial.println("MASTER READER NODE");
  Serial.print("Maximum Slave Nodes: ");
  Serial.println(NODE_MAX);
  Serial.print("Payload size: ");
  Serial.println(PAYLOAD_SIZE);
  Serial.println("***********************");
  
  
}

void loop()
{
  for (int nodeAddress = START_NODE; nodeAddress <= NODE_MAX; nodeAddress++) { // we are starting from Node address 2
        for (int j = 0; j < PAYLOAD_SIZE; j++) Serial.println(nodeAddress);   // print nodes data   
      Serial.println("*************************");      
      }
   
    delay(NODE_READ_DELAY);
}

Ignore last message, I think it's working and updating the LCD better now, Silly me I only connected 1 salve and was just changing the slave address, Now I've got the other unit built it seems to be working with This code.

Working code:

// include the library code:
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <ModbusMaster.h>
// initialize the library with the numbers of the interface pins
LiquidCrystal_I2C lcd(0x27, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE);  // Set the LCD I2C address
// instantiate ModbusMaster object
ModbusMaster node;
ModbusMaster node1;


// variable

int Battery1_modbus;
int Battery2_modbus;

void setup() {
  lcd.begin(20, 4);   // init display
  lcd.clear();        // clear display
  // init vars
  Battery1_modbus = 0;
  Battery2_modbus = 0;

  DisplayCurrentValues();

  // use Serial (port 0); initialize Modbus communication baud rate
  Serial.begin(9600);

  // communicate with Modbus slave ID 10 over Serial (port 0)
  node.begin(10, Serial);
  node1.begin(11, Serial);

}

void loop() {

  // read modbus data

  // static uint32_t i;
  uint8_t j, result;
  uint8_t j1, result1;
  //i++;

  result = node.readInputRegisters(0, 1);
  if (result == node.ku8MBSuccess)
  {
    Battery1_modbus = node.getResponseBuffer(0);
  }
  delay(50);
  result1 = node1.readInputRegisters(0, 1);
  if (result1 == node1.ku8MBSuccess)
  {
    Battery2_modbus = node1.getResponseBuffer(0);
  }

  DisplayCurrentValues();
}



void DisplayCurrentValues() {


  lcd.setCursor(0, 0);
  lcd.print(Battery1_modbus);
  lcd.print("    ");
  lcd.setCursor(0, 1);
  lcd.print(Battery2_modbus);
  lcd.print("    ");


}

Not sure if there is a better method or my code could be improved in any way

Thanks

Steve

Not sure if there is a better method or my code could be improved in any way

If you're planning to connect up to 4 of these devices, start converting your code to use arrays.

pylon:
If you're planning to connect up to 4 of these devices, start converting your code to use arrays.

Thanks Pylon,

Do you mean something like this code below ?

It complies ok but I've not tried it as the hardware is at home I was just playing with the code at work, but will try it once I get home.

// include the library code:
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <ModbusMaster.h>
// initialize the library with the numbers of the interface pins
LiquidCrystal_I2C lcd(0x3f, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE);  // Set the LCD I2C address
// instantiate ModbusMaster object
ModbusMaster node; // slave ID10
ModbusMaster node1;// slave ID11



// variable
float input_voltage1 = 0.0;
float input_voltage2 = 0.0;
int Battery_modbus[1]; // only 2 slave nodes at the moment

void setup() {
  lcd.begin(20, 4);   // init display
  lcd.clear();        // clear display
  // init vars
  Battery_modbus[0] = 0; // slave 10 battery
  Battery_modbus[1] = 0; // slave 11 battery

  DisplayCurrentValues();

  // use Serial (port 0); initialize Modbus communication baud rate
  Serial.begin(9600);

  // communicate with Modbus slave ID 10 over Serial (port 0)
  node.begin(10, Serial);
  node1.begin(11, Serial);

}

void loop() {

  // read modbus data

  // static uint32_t i;
  uint8_t j, result;
  uint8_t j1, result1;
  //i++;

  result = node.readInputRegisters(0, 1);
  if (result == node.ku8MBSuccess)
  {
    Battery_modbus[0] = node.getResponseBuffer(0);
    delay(30);
    Battery_modbus[1] = node1.getResponseBuffer(0);

  }

  input_voltage1 = (Battery_modbus[0] * 5.0) / 1024.0;
  input_voltage2 = (Battery_modbus[1] * 5.0) / 1024.0;
  DisplayCurrentValues();
}



void DisplayCurrentValues() {


  lcd.setCursor(0, 0);
  lcd.print(Battery_modbus[0]);
  lcd.print("    ");
  lcd.setCursor(0, 1);
  lcd.print(Battery_modbus[1]);
  lcd.print("    ");
  lcd.setCursor(0, 2);
  lcd.print(input_voltage1);
  lcd.print("    ");
  lcd.setCursor(0, 3);
  lcd.print(input_voltage2);
  lcd.print("    ");


}
int Battery_modbus[1]; // only 2 slave nodes at the moment

That defines an array with 1 element, not what you expected I think.

I would make the node and input_voltage variables an array too. Then use a loop to read and calculate these variables.

I thought that it starts off with 0 first then 1 which makes 2, I may have miss understood the array. Has I watched a couple of videos on array’s.

I’ve tried to create an array for the node but I got an error which I can’t Remember what it was now and not close to my pc to double check. This is the part that I did not know how to approach for the node & node1

I will check the error that I got for the node array, the other array complies but not tested yet

Thanks
Steve

I thought that it starts off with 0 first then 1 which makes 2, I may have miss understood the array.

The index starts at 0 but in the definition of the array you have to specify how many elements it should contain.