Modbus RTU: readyRead() not called



  • I'm trying to "port" the example of Modbus master to my application.
    Here some relevant code:

    QModbusClient *_modbus;
    
    bool ModbusMaster::open(QString port)
    {
        _modbus = new QModbusRtuSerialMaster(this);
    
        _modbus->setConnectionParameter(QModbusDevice::SerialPortNameParameter, port);
        _modbus->setConnectionParameter(QModbusDevice::SerialParityParameter, QSerialPort::NoParity);
        _modbus->setConnectionParameter(QModbusDevice::SerialBaudRateParameter, QSerialPort::Baud57600);
        _modbus->setConnectionParameter(QModbusDevice::SerialDataBitsParameter, QSerialPort::Data8);
        _modbus->setConnectionParameter(QModbusDevice::SerialStopBitsParameter, QSerialPort::OneStop);
    
        _modbus->setTimeout(2000);
        _modbus->setNumberOfRetries(3);
    
        return _modbus->connectDevice();
    }
    
    bool ModbusMaster::read(QModbusDataUnit::RegisterType type, int startAddress, quint16 count)
    {
        if (!_modbus) return false;
        if (_modbus->state() != QModbusDevice::ConnectedState) return false;
    
        qDebug() << "Read" << startAddress;
    
        QModbusDataUnit req(type, startAddress, count);    
        if (auto *reply = _modbus->sendReadRequest(req, 1))
        {
            qDebug() << reply->isFinished();
            if (!reply->isFinished()) connect(reply, &QModbusReply::finished, this, &ModbusMaster::readyRead);
            else readReady();
            return true;
        }
        return false;
    }
    
    void ModbusMaster::readyRead ()
    {
        qDebug() << "readyRead executed!";
        auto reply = qobject_cast<QModbusReply *>(sender());
        if (!reply) return;
        reply->deleteLater();
    
        if (reply->error() == QModbusDevice::NoError)
        {
            const QModbusDataUnit unit = reply->result();
            // do something
        }
        else if (reply->error() == QModbusDevice::ProtocolError)  qDebug() << reply->errorString();
        else qDebug() << reply->errorString();
    
        // call next reading
        emit readFinished();
    }
    

    but it seems even if received data readyRead() is not called, example:

    Debug: read 495
    Debug: false 
    Debug: (RTU client) Sent Serial PDU: 0x0301ef0001
    Debug: (RTU client) Sent Serial ADU: 0x010301ef0001b403 
    Debug: (RTU client) Response buffer: "0103020000b844" 
    Debug: (RTU client) Received ADU: "0103020000b844" 
    Debug: (RTU client) Send successful: 0x0301ef0001 
    

    after few seconds I got a timeout from my timer. As far as I understand the response is actually received, but the readyRead() slot is not called.

    Is there something obvious I'm missing?



  • What is the parent class of your ModbusMaster implementation? What does it derive from?

    readyRead is a signal. You are implementing it as a slot. You should probalby connect the modbus readyRead signal to a reader slot in your constructor. I don't believe you want to overide readyRead, but only use it to cause a reader slot to be executed.


  • Qt Champions 2018

    @Mark81 You should connect a slot to https://doc.qt.io/qt-5/qmodbusreply.html#errorOccurred and see whether this one is emitted and if so what the error is.


  • Qt Champions 2018

    @Kent-Dorfman No, reply is https://doc.qt.io/qt-5/qmodbusreply.html, it does not have readyRead signal, but it has finished() signal.



  • @Kent-Dorfman the parent class is QObject. readyRead() is my own slot as jsulm said. The signal is finished().

    @jsulm unfortunately there are no errors:

        QModbusDataUnit req(type, startAddress, count);    
        if (auto *reply = _modbus->sendReadRequest(req, _id))
        {
            connect(reply, &QModbusReply::errorOccurred, this, [reply]()
            {
                qDebug() << reply->errorString();
            });
            
            if (!reply->isFinished()) connect(reply, &QModbusReply::finished, this, &ModbusMaster::readReady);
            // ...
    

    but the output is the same.
    After a lot of trials I noticed that, seldom, the slot is called. But it's about 1 to 10.
    Given that the hardware is ok because other Modbus master applications work fine, what I don't understand is why if the answer is received (Received ADU is correct) the signal/slot mechanism doesn't work. This code seems very similar to the Qt5 example. The only code that is not shown here (but I've commented out to try it) is a QTimer that after 5000 ms fires a timeout if no answers were received.



  • @jsulm said in Modbus RTU: readyRead() not called:

    @Mark81 You should connect a slot to https://doc.qt.io/qt-5/qmodbusreply.html#errorOccurred and see whether this one is emitted and if so what the error is.

    Actually, there's no need to connect to that slot. According to the documentation:

    void QModbusReply::finished()
    This signal is emitted when the reply has finished processing. The reply may still have returned with an error. [...]
    

    Because I check for errors in my slot it should make no difference at all.


  • Qt Champions 2018

    @Mark81

    The documentation also states: https://doc.qt.io/qt-5/qmodbusreply.html#errorOccurred

    The finished() signal will probably follow.

    So it might nevertheless be a good idea to connect to errorOccurred.



  • @aha_1980 yes, as reported in the previous reply I added such a connection but it doesn't print anything, so it seems no errors are thrown. Still I can't receive replies in my slot.

    By the way, to me both sentences are in contradiction but perhaps is my poor English that suggests that:

    This signal is emitted when the reply has finished processing. The reply may still have returned with an error.

    The finished() signal will probably follow.


  • Qt Champions 2018

    @Mark81 I fully agree that "may still have" and "will probably follow" are very poor documentation statements - and can most likely be improved.

    But for now,that just means you should check every error source you can, if you encounter problems.


  • Moderators

    @Mark81

    the QModbusRtuSerialMaster has also errors that can be emitted. And those differ from the reply, check those also.

    connect(_modbus, & QModbusRtuSerialMaster::errorOccurred, this, [](QModbusDevice::Error error)->void{ qDebug() << error;});
    


  • @J.Hilk You're right. Added and tried. Whenever the "hang" happens also this slot shows nothing, unfortunately.


  • Moderators

    @Mark81 how much time passes between open and the first read request ?

    I noticed, on Windows, that when I call immediately after opening the device the first data , then most of the time nothing happens at all.



  • @J.Hilk adding a small delay seems to drastically improve the situation. Occasionally it still happens that the debug messages show a received answer but the slot is not called. That's weird because I can understand if some reasons the answer cannot be received, but this is not the case...



  • UPDATE

    I think (not sure, though) I noticed something interesting.
    When it works, the debug output from qt.modbus is like this:

    qt.modbus: (RTU client) Sent Serial PDU: 0x0304270001
    qt.modbus.lowlevel: (RTU client) Sent Serial ADU: 0x0103042700013531
    qt.modbus: (RTU client) Send successful: 0x0304270001
    qt.modbus.lowlevel: (RTU client) Response buffer: "01030200017984"
    qt.modbus: (RTU client) Received ADU: "01030200017984"
    

    and my slot is correctly called. Instead, when the problem arises the output is like the one reported in the first post or even this one:

    qt.modbus: (RTU client) Sent Serial PDU: 0x0304180002
    qt.modbus.lowlevel: (RTU client) Sent Serial ADU: 0x010304180002453c
    qt.modbus.lowlevel: (RTU client) Response buffer: "01030400004080ca53"
    qt.modbus: (RTU client) Received ADU: "01030400004080ca53"
    qt.modbus: (RTU client) Send successful: 0x0304180002
    qt.modbus: (RTU client) Receive timeout: 0x0304180002
    qt.modbus: (RTU client) Sent Serial PDU: 0x0304180002
    qt.modbus.lowlevel: (RTU client) Sent Serial ADU: 0x010304180002453c
    qt.modbus.lowlevel: (RTU client) Response buffer: "01030400004080ca53"
    qt.modbus: (RTU client) Received ADU: "01030400004080ca53"
    qt.modbus: (RTU client) Send successful: 0x0304180002
    qt.modbus: (RTU client) Receive timeout: 0x0304180002
    

    Here my slot is not called.

    I see that every time this happens the "Response buffer" line comes before the "Send successful" one.
    I'm looking at the code but I don't understand if this might be a problem.

    (Sorry for the outdated link but I have difficult to find the actual source code of Qt5 - btw I'm using Qt5.11.1)



  • Perhaps I found it here https://code.woboq.org/qt5/qtserialbus/src/serialbus/qmodbusrtuserialmaster_p.h.html#154:

    if (m_state != State::Receive) {
        qCDebug(QT_MODBUS) << "(RTU server) Ignoring response due to non receive state";
        return;
    }
    

    but https://code.woboq.org/qt5/qtserialbus/src/serialbus/qmodbusrtuserialmaster_p.h.html#351:

    qCDebug(QT_MODBUS) << "(RTU client) Send successful:" << m_current.requestPdu;
    m_state = Receive;
    

    So if the state is not set to Receive it discard the response. Anyway I didn't see the debug output "Ignoring response...".


  • Qt Champions 2018

    @Mark81 said in Modbus RTU: readyRead() not called:

    (Sorry for the outdated link but I have difficult to find the actual source code of Qt5 - btw I'm using Qt5.11.1)

    Then you should really test with a recent version 5.12.x. There has been quite some fixes last year.



  • Update to Qt5.12.2. Now with the very same code and data after a while it crashes with this error:

    ASSERT failure in processQueue: "response timer active", file qmodbusrtuserialmaster_p.h, line 302

    What should one do in such a case?


  • Moderators

    @Mark81 vote for the issue and pray, like I did

    https://bugreports.qt.io/browse/QTBUG-73965

    also leave a comment, it helps when people state that they have the same issue.



  • @J.Hilk said in Modbus RTU: readyRead() not called:

    @Mark81 vote for the issue and pray, like I did

    I'll try to suggest to my customer to pray and wait a fix too.


  • Moderators

    @Mark81
    you can download the source code and in qmodbusrtumaster comment that line

    QObject::connect(m_serialPort, &QSerialPort;::bytesWritten, q,
    [this](qint64 bytes) {
    m_current.bytesWritten += bytes;
    if (m_state == Send && (m_current.bytesWritten ==m_current.adu.size()) && !m_current.reply.isNull()) {
           // the if conditions above are copied from processQueue()
            qCDebug(QT_MODBUS) << "(RTU client) Send successful(quick):" << m_current.requestPdu;
            m_state = Receive;
            m_sendTimer.stop();
    //        m_responseTimer.start(m_responseTimeoutDuration); comment this line
    }
    });
    

    thats the quick and dirty fix I made, fixes the unexpected state issue


Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.