Solved Modbus timeout errors with Modbus Master (Client)
-
Hello, I am having problem with the communication using the Modbus plugin, I am getting the Timeout error if more than one variable is being added to read cycle. What is interesting there can be e.g. 2 Modbus requests sent in the one packet what probably leads to Timeout error (you can see it in the Wireshark capture file - link). Is there a way to avoid something like this? From my experience every request should be sent in separate packet, I am not sure why does this happen.
Saying more about my code I am setting the cyclic read requests by checking the timer timouts for every variable like this:
for (auto it = category_vector.begin(); it != category_vector.end(); ++it) { ... additional functions ... if(!sender) { QTimer* timer_refresh = new QTimer(this); timer_refresh->setInterval(refresh_rate); timer_refresh->start(); connect(timer_refresh,&QTimer::timeout, [=]() {(receiveVariable(name,table,is_subname)); }); timers_.push_back(timer_refresh); } ... additional functions ... }
And the 'receiveVariable':
void MainWindow::receiveVariable(const QString& key, QTableWidget* table, bool is_subname) { if (modbus_device_->state() != QModbusDevice::ConnectedState) return; QModbusDataUnit readUnit = modbus_variables_.getQModbusDataUnit(key); if (auto *reply = modbus_device_->sendReadRequest(readUnit, 1)) { if (!reply->isFinished()) connect(reply,&QModbusReply::finished, [=]() {(readReady(reply,key,table,is_subname)); }); else delete reply; // broadcast replies return immediately } else { QMessageBox messageBox(QMessageBox::Critical, "Error", "Read error: " + modbus_device_->errorString()); messageBox.exec(); } }
Basicaly in the 'readReady' function I am filling given table in specific way, if my defined variable is_subname is true or false.
The whole code runs fine if I have only one variable, but I am getting the errors with 2 variables on more.
-
Ok, I got some more experience and done a bit of reading.
My new code which I am trying to use is:
void MainWindow::sendAllVariables() { if (modbus_device_->state() != QModbusDevice::ConnectedState) return; for (auto const& element : modbus_variables_->getCategoriesMap()) { auto category = element.second; if (category->getFunction() != Function::Sender) continue; auto write_unit = category->getDataUnitWhole(); if(!modbus_device_->sendWriteRequest(write_unit, 1)) { QMessageBox messageBox(QMessageBox::Critical, "Error", "Write error: " + modbus_device_->errorString()); messageBox.exec(); } uint delay = qrand() % 50 + 100; QThread::msleep(delay); } }
And the problem is as previously, that the requests are send in one burst, as presented in the attached Wireshark file (on the beginning you can see the stacked write requests and afterwards read requests which are working properly). This stacked request is not supported by my device and not read correctly - as you can see it also sends response just for first write request. If I am sending write requests individually, again everything works fine.
What I found is that the QModbusClient uses the queuing, which effectively prevents me from being able to send to the device as I would like to do it (link). Is there a way, some trick to avoid this queuing? As you can see in the code I am trying to sleep the thread, but it is not of much help, as everything is working in sequence (as I assume). I could just get one big request which would cover up all the registers, but I guess there must be something better...
-
Hi,
From the looks of it, I'd say you can’t influence that currently. An alternative would be to use the QModbusReply::finished signal to trigger the next command. That way you ensure that your device should be able to receive the next request.
-
-
Thank you guys. For now I solved it with semaphore, and a queue to which I am putting all the variables, which gonna be sent. This is not the nicest design, but it works as it should. Maybe I will try to do it with local QEventLoop as you recommended @beecksche, when I will get more understanding of it.
For now, problem solved :)