ModBusRTU ESP32 with RPi 4B Communication protocol idea

Thread Starter

Vignesh_S_1997

Joined Nov 7, 2023
8
Hi,
I have been working with ModBusRTU library using ESP32 by following the steps given in the tutorial. I am able to make simple Modbus communication between Two ESP32 devices using RS485 modules by making one of them as Master and another as Slave.
As the author of the above tutorial, I am also planning to make a Pick-To-Light device architecture. I am able to send values from Master to Slave device using the above library. But I don't have idea to get the Acknowledge message from the Slave device to the Master.
I have some questions:
1) How can I get the Acknowledge message from the Slave device after pressing a button from Slave device to Master?
2) What is the maximum number of Slave devices, I can connect to a Single Master for perfect communication?
3) Is there any problem if I use Raspberry Pi 4B as a Master instead of ESP32?If it's possible, What library suits best for this purpose?

Eagerly waiting for ideas from fellow enthusiasts.
 

Ya’akov

Joined Jan 27, 2019
8,973
Welcome to AAC.

ModBus is a strictly master-solve protocol so only the master can initiate communications. This means you will have to store the button press status locally in the slave and have the master track outstanding transactions, periodically polling with a request to read the register storing the status on the slave.
 

Thread Starter

Vignesh_S_1997

Joined Nov 7, 2023
8
Welcome to AAC.

ModBus is a strictly master-solve protocol so only the master can initiate communications. This means you will have to store the button press status locally in the slave and have the master track outstanding transactions, periodically polling with a request to read the register storing the status on the slave.
Thanks for your reply Ya'akov. On following your idea, I have created the Slave program which will store the value 1 of Hreg=11, when the Push button is pressed. On the master side, I continuously checking that Hreg=11 from the Slave after sending the value. Now arrives some more questions:
1) Even I am able to get the ACK msg from the Hreg of Slave device, I have to check for that Hreg value continuously. Is there other ways to check only when that Hreg value changes like CallBack method?
2) I am not able to find a way to get the ACK msg from other Slave devices with different addresses. I have to connected a maximum of 30 devices with a Master. Can you suggest another method to get the ACK msg from all the devices, Once the Push button is pressed?
 

strantor

Joined Oct 3, 2010
6,778
You can use PyModbus on Pi. Modbus supports up to 247 devices per port but the data is exchanged with each device one at a time. You cannot get acknowledgement from all 247 (or even just 30) devices at the same time, it must be one by one. To speed up the process you could split up the slave devices onto separate busses and make your script on the Pi multi-threaded, accessing each bus simultaneously on multiple ports.
 

Thread Starter

Vignesh_S_1997

Joined Nov 7, 2023
8
Thankyou for your reply.
You cannot get acknowledgement from all 247 (or even just 30) devices at the same time, it must be one by one. To speed up the process you could split up the slave devices onto separate busses and make your script on the Pi multi-threaded, accessing each bus simultaneously on multiple ports.
As you said, it is not necessary to get the ACK messages simultaneously. If the master can receive the ACK messages One by One from the Slaves, it's more than enough.
But my major question is, How can I get the ACK messages from the all the Slaves (Eg: 30 Slaves.) ?
If I have to send the message to any of the 30 Slaves, I can send that particular Slave using the Address. I don't have any procedure to get the ACK messages from the Slaves along with their Addresses.
Can you suggest me with Pi Multi-Threaded access for RS485 communication protocol?
 

Ya’akov

Joined Jan 27, 2019
8,973
Thankyou for your reply.

As you said, it is not necessary to get the ACK messages simultaneously. If the master can receive the ACK messages One by One from the Slaves, it's more than enough.
But my major question is, How can I get the ACK messages from the all the Slaves (Eg: 30 Slaves.) ?
If I have to send the message to any of the 30 Slaves, I can send that particular Slave using the Address. I don't have any procedure to get the ACK messages from the Slaves along with their Addresses.
Can you suggest me with Pi Multi-Threaded access for RS485 communication protocol?
You have to poll. One or one thousand—it works the same way. Any time the master has sent a message that needs a reply, it it is added to a list of slaves that are expected to reply. The master then periodically polls by iterating through that list, looking for the value in the register that indicates a response.

For purposes of tracking exceptions, I would have a register that indicates the state of the light and the state of the button. If the master commands the slave to light the light, it does—and it sets the light on register to true. Once the button is pressed it sets the button pressed register to true.

When it polls, the master checks the button pressed register and the light on register, if it is button pressed is true, it commands the slave to turn off the light and reset the light register to false. But, if light is false it is an exception since there would be no pending response from the slave if the light isn’t on, and that would require handling as an error.

This last part is to ensure that undetected race conditions or other behaviors can‘t go undetected.
 

Thread Starter

Vignesh_S_1997

Joined Nov 7, 2023
8
Thankyou Ya'akov. Now I can understand the process clearly from your points. I am really new to this Modbus protocol. Can you suggest some example programs for the Polling based approach of Modbus protocol. I am using the ModbusRTU library. But I have to implemet Raspberry Pi 4 as my Master. It would be great, if you suggest me examples from Python or Arduino code.
 

Thread Starter

Vignesh_S_1997

Joined Nov 7, 2023
8
I am able to make a Basic Master Slave structure to send message from Master and receive ACK message from Slave when the push button is pressed by following your points. But, my approach is very much limited to only one Slave device. How can I extend this approach for a maximum of 30 devices. I will attach my Master and Slave codes for you reference.
Master.cpp:
void loop() {

  mb.task();
  //yield();
  delay(10);

  if (Serial.available())
  {
    String str = Serial.readStringUntil ('\n');
    char msg[10];
    char buf[30];

    str.toCharArray(buf, 20);
    int i = 0;


    char* token = strtok(buf, " , ");

    // Keep printing tokens while one of the
    // delimiters present in str[].
    while (token != NULL)
    {
      if (i == 0)
      {
        // to_ID = (uint8_t)atoi(token);
        to_ID = (int)atoi(token);
      }
      else if (i == 1)
      {
        data = (uint16_t)atoi(token);
        // String to_msg_str = String(token);
        // to_msg_str.toCharArray(msg, 10);
      }
      token = strtok(NULL, " , ");
      i++;
    }
    // rs485.send(to_ID, (uint8_t *)msg, strlen(msg));
    if (!mb.slave())
    {
      // mb.writeHreg(1, REG1, 99,  cbWrite);
      // uint16_t data = 3;
      Serial.print("Data: ");
      Serial.println(data);

      // mb.writeHreg(1, REG1, data,  cbWrite);
      mb.writeHreg(to_ID, REG1, data,  cbWrite);
      data_sent = true;
      delay(50);
    }
  }

  if (!mb.slave() && (data_sent == true))
  {
    mb.readHreg(1, REG2, &storing_data,1, cbWrite);
    if(storing_data==1)//THIS NEVER PRINTS 55 ?
    {
      Serial.println("ACK from 1");
      data_sent = false;
    }
  }
}
Slave.cpp:
void loop()
{
  button.loop(); // MUST call the loop() function first
  mb.task();
  delay(200);
  if(mb.Hreg(REG1) != 1000)
  {
    // IF RECEIVED MESSAGE IS 99, DO SOME TASKS HERE AND THEN REPLY BACK TO MASTER BY PUTTING ANOTHER VALUE TO REG1
    Serial.println(mb.Hreg(REG1));

    uint16_t m = mb.Hreg(REG1);
      
    if (m != 0)
    {
      tm1637.displayNumber(m);
      digitalWrite(ACK_LED, HIGH);
    }
    mb.Hreg(REG1,1000);    //PUT 55 IN REG1
    mb.Hreg(REG2, 0);
  }

  if (button.isPressed())
  {
    Serial.println("Button Pressed!");
    tm1637.clearTMDisplay();
    digitalWrite(ACK_LED, LOW);
    mb.Hreg(REG2, 1);
  }
}

My Master and Slave devices Serial Monitor Output Screen:

Master_Slave_Serial_Monitor.png

Can you help me with this?
 

Thread Starter

Vignesh_S_1997

Joined Nov 7, 2023
8
Hello Guys...I am eagerly waiting for reply from fellow members in order to proceed my project....If any of the members have any idea please reply guys....
 

Ya’akov

Joined Jan 27, 2019
8,973
This is a description of basic program flow and logic. I can’t guarantee I didn’t miss something but it is surely sufficient to make clear the kind of approach you can take. You should use a state machine strategy, along with work queues (the arrays, below) and interrupts so that time critical processing isn’t adversely affected.

Write the code in a function-oriented way, so that you can make the main loop simply an orderly list of function calls. Be careful with your variable names to ensure they are functionally normalized, and are storing only one piece of information per scalar value. Don’t store two things in a single scalar that will require parsing later.

So, if you read through this and understand it you should be able to write a program that does everything you need and can be easily extended as required.

You will have to work out some values for POLL_INTERVAL, CHECK_INTERVAL, etc based on the time-cost for each polling operation, the load from other tasks, etc. I can’t predict what they should be a priori.

Create a constant for POLL_INTERVAL to store the number of milliseconds that must elapse before the pendingNodes array (below) is checked.

Create a constant for CHECK_INTERVAL to store the number of milliseconds between times when a particular node is checked when the program is in the pollNodes loop.

Create a constant for MAX_RETRIES to store the number of attempts to poll a node without receiving an ACK before marking that node as an exception.

Create a set of constants such as ACK_TIMEOUT, NODE_FAILED (if the node responds with an explicit NACK), and any other values for the exceptionNodes array (below) status variable that stores the type of exception.

Create an array named something like pendingNodes that can store three values for each node.

Create an array that can store three values per node called something like exceptionNodes to store nodes that have entered an exception state.

Each time a node is sent a command that requires an ACK add it to the array along with a timestamp indicating when it was sent, and a retries counter set to 0.

Using a state machine, iterate over the list using an pollNodes function every POLL_INTERVAL, polling each node with a timestamp old enough to qualify.

If you find an ACK, send the node a reset light command (or whatever strategy you want to use), then delete it from the array.

If there is no ACK, check the retries value in the to see if it is greater than MAX_RETRIESif it is, respond to this exception (e.g.: send a command to flash the light to get operator attention, or send a supervisory notification, or whatever your workflow calls for.

Add the node to the exceptionNodes* array, along with a timestamp and status (using the constants defined above) that indicates the nature of the exception

*the processing of this array is a matter for you to determine—how will you handle nodes that fail in one way or another. For example if the operator at that node fails to press the button, you might choose to flash the light, or command an audible signal, etc.

The use of a state machine and interrupts will allow your program to run continuously, and you can choose to set priorities for processing exceptions, for example, to ensure good response times.
 

Thread Starter

Vignesh_S_1997

Joined Nov 7, 2023
8
Thankyou for your detailed explanation Ya'akov. I will try to implement the steps given by you. Once I get the output, I will definitely make you to know that. Thankyou very much.
 
Top