-1

There is a problem in writing the MODBUS RTU code using Arduino.

First, the communication was successful. However, communication is performed in the order of request -> request -> response -> request -> request -> response.

Please refer to the video below. https://youtu.be/Z8tkmY7l-oo

I don't know what's wrong with my code.

help me..

my code is...

#include <Crc16.h>
Crc16 crc;

#define EN0 2

uint8_t n8ID = 1;
uint8_t FunctionCode = 3;

const int DIP_1 = 6;
const int DIP_2 = 7;
const int DIP_3 = 8;
const int DIP_4 = 9;

uint8_t MODBUS_Request[10];

uint8_t Data[12] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};

void setup() 
{
  pinMode(EN0, OUTPUT);

  pinMode(DIP_1, INPUT_PULLUP);
  pinMode(DIP_2, INPUT_PULLUP);
  pinMode(DIP_3, INPUT_PULLUP);
  pinMode(DIP_4, INPUT_PULLUP);
  
  digitalWrite(EN0, LOW);
  
  Serial.begin(9600);

}

void loop() 
{
  digitalWrite(EN0, LOW);

  n8ID = 0;
  n8ID += (!digitalRead(DIP_1)) << 0;
  n8ID += (!digitalRead(DIP_2)) << 1;
  n8ID += (!digitalRead(DIP_3)) << 2;
  n8ID += (!digitalRead(DIP_4)) << 3;

  if (Serial.available())
  {
    uint8_t leng = Serial.readBytes(MODBUS_Request, 8);    
    
    if(MODBUS_Request[0] == n8ID && MODBUS_Request[1] == FunctionCode && MODBUS_Request[2] == 0 && MODBUS_Request[3] == 0)
    {
      uint8_t Request_dataBuff[6];

      for (int i = 0; i < 6; i++)
      {
        Request_dataBuff[i] = MODBUS_Request[i];
      }
      
      unsigned short RequestCRC = (unsigned short)(MODBUS_Request[7] << 8 | MODBUS_Request[6]);
      crc.clearCrc();

      for (uint8_t i = 0; i < 6; i++)
        crc.updateCrc(Request_dataBuff[i]);

      unsigned short CRC_Check = crc.getCrc();
      CRC_Check = crc.Modbus(Request_dataBuff, 0, 6);

      if(RequestCRC == CRC_Check)
      {
        uint8_t send_dataBuff[15] = {n8ID, FunctionCode, 12, Data[0], Data[1], Data[2], Data[3], Data[4], Data[5], Data[6], Data[7], Data[8], Data[9], Data[10], Data[11]};

        crc.clearCrc();
        
        for (uint8_t i = 0; i < 15; i++)
          crc.updateCrc(send_dataBuff[i]);

        unsigned short ResponseCRC = crc.getCrc();
        ResponseCRC = crc.Modbus(send_dataBuff, 0, 15);

        uint8_t Response_CRC_H = ResponseCRC >> 8;
        uint8_t Response_CRC_L = ResponseCRC & 0xFF;

        uint8_t MODBUS_Response[17] = {n8ID, FunctionCode, 12, Data[0], Data[1], Data[2], Data[3], Data[4], Data[5], Data[6], Data[7], Data[8], Data[9], Data[10], Data[11], Response_CRC_L, Response_CRC_H};
        
        digitalWrite(EN0, HIGH);
        
        for(int i = 0; i < 17; i++)
        {
          Serial.write(MODBUS_Response[i]);
          Serial.flush();
        }
        digitalWrite(EN0, LOW);
      }
    }
  }
}

4 Answers4

0

Your code is adding most of the delay. You are introducing a huge overhead by writing to the port one byte at a time.

Maybe you can try replacing this part:

for(int i = 0; i < 17; i++)
    {
      Serial.write(MODBUS_Response[i]);
      Serial.flush();
    }

With something simpler:

Serial.write(MODBUS_Response,17);
Serial.flush();

which just writes the whole Modbus frame in one go.

If that change alone is not fixing your issues you might need to give more details on your question about the other side of the link.

Marcos G.
  • 3,371
  • 2
  • 8
  • 16
  • Thank you for your answers. However, even if you change it as you said, the symptoms are the same. Currently, the connection of my device is Master : 1, Slave : 1, and PC software acts as a master. PC software is a simulator that can serve as a MODBUS RTU master. If you search for 'MODSCAN64' on Google, you will be able to get information about the software. So there must be a problem with my code somewhere. – Hyunwoo Choi Dec 05 '21 at 00:15
0

You cannot send two requests in a row.

What will happen if you send two requests to two slaves and they both respond at the same time?

Serial communication does not have any mechanism to avoid collisions.

It must be the master that synchronizes following a strict order: request -> response -> request -> response ... etc.

You cannot send a new request before you have received the response from the previous one.

from56
  • 3,976
  • 2
  • 13
  • 23
  • I don't understand that there are two requests. Currently, the connection of my device is Master : 1, Slave : 1, and PC software acts as a master. PC software is a simulator that can serve as a MODBUS RTU master. If you search for 'MODSCAN64' on Google, you will be able to get information about the software. – Hyunwoo Choi Dec 05 '21 at 00:17
0

First

Try to add a little delay before TXEN line goes low,

        digitalWrite(EN0, HIGH);
        
        for(int i = 0; i < 17; i++)
        {
          Serial.write(MODBUS_Response[i]);
          Serial.flush();
        }
        delay(1)  # <-- add a little delay to allow transmission finish 
        digitalWrite(EN0, LOW);

May need to increase the delay if the baudrate is really low.

Second

There may be some '0's or random/noise values preceeding with the actual request bytes received by your code, so you cannot just read in 8 bytes in one batch and assume it as the actual request. Instead, you may need to read one by one and figure out where is the start byte of request.

See the following case,

0 0 0 0 0 1 3 x x x x c c   ...   1 3 x x x x c c
^ first read  ^ ^ second read     ^ third read  ^  
| (discard)   | | (timeout)       | (success)   |

balun
  • 1,191
  • 1
  • 7
  • 12
0

The cause seems to have been found, but it does not seem to be a fundamental solution.

arduino's default Timeout value is 1000 ms, and the master asked Slave for data every 1000 ms.

I thought I'd move on to the next code when the receive buffer 8byte came in from the code below, but I think I'm waiting for a timeout time. Serial.readBytes(MODBUS_Request, 8);

i used the Serial.setTimeout() function as a solution to reduce the timeout time, but this doesn't seem to be a fundamental solution.

even if you use that function, the code will stop for as long as I set it. I want to use the Serial.readBytesUntil() function, but the last data to be received is CRC, so the last data cannot be predicted.

When data is received, how can I move on to the next code?