Connection to multiple rs485 modbus rtu slaves

connection to multiple rs485 modbus rtu slaves

I have a groov epic with two GRV-CSERI-4 modules, in the second module, on a single channel I have wired two identical drives in serial RS485 MODBUS RTU, slave 1 and slave 2.

Communication Works correctly.

In the code you can see that I change the address of the slave with a for, which goes from 1 to the number of connected devices.

The initial record is 100, and the return is 10 records, in the table ch1_nt03HoldingRegAsIntReturnValues the data from slave 1 and 2 arrive and are overwritten, for this I make the move with the MoveNumTableToNumTable function from ch1_nt03HoldingRegAsIntReturnValues to ch1_ntDatastoreModbus, moving the index with the variables “( ch1_indexScanDevice-1)*ch1_returnNregister;”
that is, in the ch1_ntDatastoreModbus table from index 0 to index 9 is slave 1 and from index 10 to 19 is slave 2;
The problem is that after a while the data of the slaves are exchanged, index 0-9 is slave 2 and index 10-19 is slave 1, it is as if the response was delayed and that of slave 1 arrived when the response was already requested of slave 2.

place a variable that when equal to 1 closes and open the communication port, after doing this, the indices return to their normal state, index 0- 9 slave 1 and index 10-19 is slave 2, but after a while the same thing happens again.

note:
delayMsec(ch1_msDelayScan); // = 100
ch1_fMasterReadDelay = 0.5;

also use:

delayMsec(ch1_msDelayScan); // = 1000
ch1_fMasterReadDelay = 1;
But the problem continues.

What can cause this problem?

I think @philip will have some better advice, but straight way I see a fixed delay in there and that’s generally a bad idea with mulitdrop serial.
It would certainly explain what you are seeing where after a while things get out of sync and so the data gets merged.
You need to ensure a more strict poll-response where you wait for each reply, not just throw it out there and move on after the fixed time is up because each device will have a different response time. That time will vary based on both the distance from the EPIC but also the work load that each device is under (which will change from moment to moment and it can vary based on the software itself is running).

When you say “fixed delay” which one are you referring to?
In total I think there are 3:

  1. ch1_nt03AsIntParameters[2] = 1000; // ms timeOut

  2. ch1_fMasterReadDelay = 0.5 or 1;// Sec

  3. delayMsec(ch1_msDelayScan);// 100 or 1000 msec

I don’t understand how it could happen, since the request to the slave is made with the ch1_indexScanDevice variable, and with this same variable the movement is made in ch1_ntDatastoreModbus, therefore, how is it possible for the response to be exchanged?

unless, in the response, it does not take into account the id of the slave that responds.

I don’t see any issues with your delays, though the ch1_msDelayScan can be a lot shorter, 5-10ms will be fine and your timeout probably needs to be longer based on the behavior I think is happening.

What I suspect is happening is occasionally the slaves are taking longer to respond than the timeout set in ch1_nt03AsIntParameters[2] so when you make the next call, the receive buffer already has data in it.

The good news is, I think your code is fine! The issue is with the Opto22 modbus kit due to two reasons:

@Beno - bug report below

  1. The subroutines don’t check if there is already data in the receive buffer before it transmits so that it can clear it out first.

  2. The subroutine doesn’t verify the slave Id in the response so it can respond with an error status if there is a mismatch - it just takes whatever comes. The kit already has an error code for this, it just never does the check.

To fix the first issue, change the “Open” block in the subroutine:

if (GetNumCharsWaiting(chCommHandle) < 0 or not IsCommunicationOpen(chCommHandle)) then
  nStatus = OpenOutgoingCommunication(chCommHandle);
else
  ClearCommunicationReceiveBuffer(chCommHandle);
  nStatus = 0;
endif

To fix the second issue and validate the response Slave Id, change the Receive block in the subroutine and replace the code in the case 0 block with the following:

        ReceiveNChars(sTempString2, 3, chCommHandle);

        //Check for correct slave Id in response
        if(ntParameters[1] <> GetNthCharacter(sTempString2, 0)) then
              nStatus = -212;//Wrong Slave Address
        elseif (3 <> GetNthCharacter(sTempString2, 1)) then //exception code - should really check CRC here too!
              nStatus = GetNthCharacter(sTempString2, 2);
              ClearCommunicationReceiveBuffer(chCommHandle);
        else
              ReceiveNChars(sTempString1, GetNthCharacter(sTempString2, 2), chCommHandle);
              sTempString2 += sTempString1;
              nTemp2 = GenerateReverseCrc16OnString(-1, sTempString2);
              ReceiveNChars(sTempString1, 2, chCommHandle);                                                      
              if (nTemp2 <> (GetNthCharacter(sTempString1, 1) <<8) + GetNthCharacter(sTempString1, 0)) then
                    nStatus = -211;//CRC Error                
              endif   
        endif

It will look like this:

Let me know if that second one works okay, because I back ported my own modbus code to the kit right now to help you and did not test it.

1 Like

If Opto22 decides to fix the kit, they should also validate the function code in the response.

1 Like

hi philip,

Thank you very much for the help, I made the changes in the subroutine and started executing the strategy, I even added one more slave, so far everything is going well.
Is it correct to state that: even with the changes, it may return error -212 many times, but this can be solved by only adjusting the timeOut of nt03AsIntParameters[2] ?

Yes, I believe that is correct, I would recommend increasing the timeout since it seems you are getting valid responses, they are just taking longer than 1000ms sometimes.