ArduinoModbus
ArduinoModbus copied to clipboard
Modbus RTU library Problem of time consuming when multiple slave on same bus
Hi
I had problem of time consuming of pool()
method in my design using this library.
- when the master is not polling any devices on modbus : 0 to 2ms maximum => Fine for me
- when the master pool the arduino device : 36-37ms => fine for me
- when the master pool another address :
ms: 7
ms: 500
ms: 7
ms: 500
ms: 8
ms: 500
ms: 8
ms: 500
So here there is a problem because it block my program for half a second when the master interrogate another device. Do i need to configure something for to solve this ? Is the library capable of doing 1 master with several slaves ? I supposed it was possible.
I used an Arduino Nano Every, but now I tried simple way :
- Regular Arduino MEGA
- example software "Modbus RTU Server Kitchen Sink" with just adding few lines to measure the time of the
Pool()
method and sent the time toSerial2
for logging purpose - Arduino mega is connected without RS485, direct on a RS232 TTL to a computer.
- Arduino Master RTU is a Computer software used : QModMaster 9600bauds 1stop bits, parity none Modbus address is 42.
When sending to device 42
[RTU]>Tx > 13:36:22:648 - 2A 03 00 00 00 01 82 11
[RTU]>Rx > 13:36:22:673 - 2A 03 02 00 00 9C 42
Everything fine, pool
method using 15ms of time.
When sending to device 41 ( who do not exist )
[RTU]>Tx > 13:36:32:289 - 29 03 00 00 00 01 82 22
Nobody reply, so normal but the pool()
method on the arduino adr42 take 7ms and then 500ms.
So this 500ms block the rest of the program in the arduino.
So there is a problem in this library. That's so strange that nobody had this issue before, it is so simple to encounter.
Hope someone can give me directions because.
/*
Modbus RTU Server Kitchen Sink
This sketch creates a Modbus RTU Server and demostrates
how to use various Modbus Server APIs.
Circuit:
- MKR board
- MKR 485 shield
- ISO GND connected to GND of the Modbus RTU server
- Y connected to A/Y of the Modbus RTU client
- Z connected to B/Z of the Modbus RTU client
- Jumper positions
- FULL set to OFF
- Z \/\/ Y set to OFF
created 18 July 2018
by Sandeep Mistry
*/
#include <ArduinoRS485.h> // ArduinoModbus depends on the ArduinoRS485 library
#include <ArduinoModbus.h>
unsigned int cycle;
const int numCoils = 10;
const int numDiscreteInputs = 10;
const int numHoldingRegisters = 10;
const int numInputRegisters = 10;
void setup() {
Serial.begin(9600);
while (!Serial);
Serial2.begin(115200);
Serial2.println("Modbus RTU Server Kitchen Sink");
// start the Modbus RTU server, with (slave) id 42
if (!ModbusRTUServer.begin(42, 9600)) {
Serial2.println("Failed to start Modbus RTU Server!");
while (1);
}
// configure coils at address 0x00
ModbusRTUServer.configureCoils(0x00, numCoils);
// configure discrete inputs at address 0x00
ModbusRTUServer.configureDiscreteInputs(0x00, numDiscreteInputs);
// configure holding registers at address 0x00
ModbusRTUServer.configureHoldingRegisters(0x00, numHoldingRegisters);
// configure input registers at address 0x00
ModbusRTUServer.configureInputRegisters(0x00, numInputRegisters);
}
void loop() {
cycle = millis();
// Pool for modbus requests
ModbusRTUServer.poll();
cycle = millis() - cycle ;
if ( cycle > 2 )
{
Serial2.print("ms: ");
Serial2.println(cycle);
}
// map the coil values to the discrete input values
for (int i = 0; i < numCoils; i++) {
int coilValue = ModbusRTUServer.coilRead(i);
ModbusRTUServer.discreteInputWrite(i, coilValue);
}
// map the holiding register values to the input register values
for (int i = 0; i < numHoldingRegisters; i++) {
long holdingRegisterValue = ModbusRTUServer.holdingRegisterRead(i);
ModbusRTUServer.inputRegisterWrite(i, holdingRegisterValue);
}
}
Not sure if I have the same issue, but the effect is the same...
Actual only one single Modbus RTU server is created with this library and Arduino. If this server is polled to fast from client (one request every second), the complete arduino program stops working. If the Modbus connection from Client is stoped, arduino program continious running...
If the polling is changed to one request each 3 seconds it much more stabel and effects only in some read errors (~5%) and does not block the arduino program. But also this seems to be a issue.
As Client I'm actual using the software Modbuspoll with USB/RS485 converter.
I have an issue which I think it's related to this one: I have two slaves (Nano 33 IOT) on RS485 bus.
When I poll slave A from the master, slave B get stuck (I added a blikning LED in the main loop and it won't blink while other slave is being accessed) and vice-versa. It keeps blinking while serving requests addressed to itself. To solve the issue (apart from putting only 1 device per bus), I had to add lots of wait states on master side which is not good for my application.
I printed some debug on _modbus_rtu_receive
function in modbus-rtu.cpp
and got:
12:58:51.501 -> Request for slave 2 ignored (not 1)
12:58:51.501 -> _modbus_receive_msg Returned 0, The next expected message is a confirmation to ignore
12:58:52.093 -> Confirmation to ignore
12:58:52.684 -> Request for slave 2 ignored (not 1)
12:58:52.684 -> _modbus_receive_msg Returned 0, The next expected message is a confirmation to ignore
12:58:53.408 -> Request for slave 4 ignored (not 1)
12:58:53.408 -> _modbus_receive_msg Returned 0 on a confirmation to ignore
12:58:53.408 -> Confirmation to ignore
12:58:53.408 -> Request for slave 15 ignored (not 1)
12:58:53.408 -> _modbus_receive_msg Returned 0, The next expected message is a confirmation to ignore
12:58:55.494 -> Confirmation to ignore
seems like some byte got lost or there is a framing error, so an intermediate byte is interpreted as address (no one is accessing slave 4 or 15, just 1 and 2)... with this code:
static int _modbus_rtu_receive(modbus_t *ctx, uint8_t *req)
{
int rc;
#ifdef ARDUINO
modbus_rtu_t *ctx_rtu = (modbus_rtu_t*)ctx->backend_data;
#else
modbus_rtu_t *ctx_rtu = ctx->backend_data;
#endif
if (ctx_rtu->confirmation_to_ignore) {
rc = _modbus_receive_msg(ctx, req, MSG_CONFIRMATION);
if (rc == 0) {
if (ctx->debug) {
Serial.println("_modbus_receive_msg Returned 0 on a confirmation to ignore");
}
}
/* Ignore errors and reset the flag */
ctx_rtu->confirmation_to_ignore = FALSE;
rc = 0;
if (ctx->debug) {
printf("Confirmation to ignore\n");
Serial.println("Confirmation to ignore");
}
} else {
rc = _modbus_receive_msg(ctx, req, MSG_INDICATION);
if (rc == 0) {
/* The next expected message is a confirmation to ignore */
if (ctx->debug) {
Serial.println("_modbus_receive_msg Returned 0, The next expected message is a confirmation to ignore");
}
ctx_rtu->confirmation_to_ignore = TRUE;
}
}
return rc;
}
Sorry for previous post. Looks like it needs very frequent polling to avoid data loss and consequent framing errors. Every slave should process all Modbus data to avoid framing error /next header to be taken in the middle of the message.
Slave A should be able to process all slave B data (both directions) to keep aligned on the FSM which parse message bytes. Same for slave B on slave A data. Master will just wait for addressed slave, not the other ones.
I changed polling speed according to baud rate to avoid any issue and it seems okay now.
I also came across this issue and I think I understand what and why it is happening...
As @mktime stated in the initial report, the problem occurs if the slave doesn't exist and this is also the situation I have here. From what I can see, the code as it stands is programmed to ignore the next message if the previous message was not addressed to i. This check is in the _modbus_rtu_check_integrity
function -:
/* Filter on the Modbus unit identifier (slave) in RTU mode to avoid useless
* CRC computing. */
if (slave != ctx->slave && slave != MODBUS_BROADCAST_ADDRESS)
{
Serial1.print(millis());
Serial1.println(" - Request from incorrect slave");
if (ctx->debug)
{
printf("Request for slave %d ignored (not %d)\n", slave, ctx->slave);
}
/* Following call to check_confirmation handles this error */
return 0;
}
Then in modbus-rtu.cpp
we have the following check in _modbus_rtu_receive
-:
rc = _modbus_receive_msg(ctx, req, MSG_INDICATION);
if (rc == 0)
{
/* The next expected message is a confirmation to ignore */
ctx_rtu->confirmation_to_ignore = TRUE;
}
_modbus_receive_msg
will return 0 if the message is not addressed to the local slave ID or broadcast.
II believe (or assume) it does this to ignore the assumed response from the other slave, but in the event that the other slave doesn't exist, this times out and results in the 1/2 second delay in execution.
IMHO, I don't see a need for this check. If the other slave responds, then the message should also be ignored since it won'\t be addressed to the local ID (ie it will likely be addressed to the master) and if the other slave doesn't exist, then we shouldn't wait for a response that may not come - or may arrive after the 500ms timeout...
I've not yet tested this since I only have a single slave in my test system, but for me the fix is to remove the check that sets confirmation_to_ignore
to TRUE
. In fact as this seems to be the only reason that confirmation_to_ignore
can be true (from what I can see), it seems the code around this can be completely removed and simplified.
Maybe I'm missing something - I'm not a Modbus expert, but in most other protocols I've worked on, just ignoring any message not addressed to the local device should be sufficient. I'm happy to hear if others see a need for this check...
@cdjackson Hi Thanks for your advice My project was a for my own usage and since then i didn't had time to correct the problem, so my system work but bad. As i'm not a modbus expert too, i didn't touch the library for now.
But thanks for your advice, i will try as soon as i can.
As @mktime stated in the initial report, the problem occurs if the slave doesn't exist and this is also the situation I have here. From what I can see, the code as it stands is programmed to ignore the next message if the previous message was not addressed to i. This check is in the
_modbus_rtu_check_integrity
function -:/* Filter on the Modbus unit identifier (slave) in RTU mode to avoid useless * CRC computing. */ if (slave != ctx->slave && slave != MODBUS_BROADCAST_ADDRESS) { Serial1.print(millis()); Serial1.println(" - Request from incorrect slave"); if (ctx->debug) { printf("Request for slave %d ignored (not %d)\n", slave, ctx->slave); } /* Following call to check_confirmation handles this error */ return 0; }
Then in
modbus-rtu.cpp
we have the following check in_modbus_rtu_receive
-:rc = _modbus_receive_msg(ctx, req, MSG_INDICATION); if (rc == 0) { /* The next expected message is a confirmation to ignore */ ctx_rtu->confirmation_to_ignore = TRUE; }
_modbus_receive_msg
will return 0 if the message is not addressed to the local slave ID or broadcast.II believe (or assume) it does this to ignore the assumed response from the other slave, but in the event that the other slave doesn't exist, this times out and results in the 1/2 second delay in execution.
IMHO, I don't see a need for this check. If the other slave responds, then the message should also be ignored since it won'\t be addressed to the local ID (ie it will likely be addressed to the master) and if the other slave doesn't exist, then we shouldn't wait for a response that may not come - or may arrive after the 500ms timeout...
I've not yet tested this since I only have a single slave in my test system, but for me the fix is to remove the check that sets
confirmation_to_ignore
toTRUE
. In fact as this seems to be the only reason thatconfirmation_to_ignore
can be true (from what I can see), it seems the code around this can be completely removed and simplified.Maybe I'm missing something - I'm not a Modbus expert, but in most other protocols I've worked on, just ignoring any message not addressed to the local device should be sufficient. I'm happy to hear if others see a need for this check...
Thank you for your information! I have come across similar problem. I search 500 in ArduinoModbus/src
file folder, and find this in modbus-private.h
:
/* Timeouts in microsecond (0.5 s) */
#define _RESPONSE_TIMEOUT 500000
#define _BYTE_TIMEOUT 500000
The doesn't exist over time is defined here and you can change this time to 000000 to cancel the doesn't exist over time or change to other proper value.
By the way, A modbus node use the response to judge whether last-turn communication is over, so I think it is better to comply with the MODBUS Protocol.
By the way, A modbus node use the response to judge whether last-turn communication is over, so I think it is better to comply with the MODBUS Protocol.
Can you please provide a reference to this since I'm not sure what timeout you mean here. I think this is fine (and necessary) for the master since it is initiating the transactions, but a slave doesn't need to know if "last-turn communication is over" - it just needs to respond to any messages that get sent to it.
By the way, A modbus node use the response to judge whether last-turn communication is over, so I think it is better to comply with the MODBUS Protocol.
Can you please provide a reference to this since I'm not sure what timeout you mean here. I think this is fine (and necessary) for the master since it is initiating the transactions, but a slave doesn't need to know if "last-turn communication is over" - it just needs to respond to any messages that get sent to it.
When a slave receive an "ask" not sent to it, it would wait for the "answer" until timeout time is reached. I'm not a specialist of modbus, but have found this phenomenon: If I send a slave an "ask" for other slave directly, it would wait a _RESPONSE_TIMEOUT, but if I send a slave an "answer" sent by other slave directly, it would not wait a _RESPONSE_TIMEOUT, it wait a _BYTE_TIMEOUT. It seems that in this modbus lib, the slave has different state to deal with ”ask“ and ”answer“, so it need to deal with every "ask" and "answer" to filter out the "ask" sent to itself. A good news is I have some other modbus equipments, they always work well as you imagine. So I think this is depend on equipment, not by the modbus protocol.
When a slave receive an "ask" not sent to it, it would wait for the "answer" until timeout time is reached.
Thanks, but can you provide the protocol reference please? To me this makes no sense since the slave will never send a request anyway, so what is this timeout actually for? All it does is block the slave from doing anything - if the master sends a request, then the slave cannot respond. I'd just like to read the exact wording in the protocol doc and I can't find this at the moment.
Thanks.