QtSerialPort and ReadyRead signal



  • Hi!

    I am working on a Qt data acquisition application which receives data from serialport and plots it on the gui in realtime (+some basic calculations). The data is collected at 40Hz by the hardware, which sends out the buffered data in every 500 ms (20 sample points = 166 bytes). My initial baudrate was 115200. So far so good, it is working flawlessly.

    Now, since 500ms plot refresh is kinda sluggish, I'd like to send the packets more often, at every 100ms which is 4 samplepoints (~30bytes). I modified the firmware accordingly however my Qt app started dropping packets. I changed the baudrate to 921600 to no avail.

    Before spawning a worker thread and all that misery I'd like to ask when the readyread signal is actually fired? At every received byte or is there any threshold to be set?

    Thank you.


  • Moderators

    You don't really need the signal if you are reading at constant time intervals... just use readAll() every time your QTimer fires (100 ms in your case). Should work. I've been using this with even shorter intervals and smaller baud rates - no problems.



  • This is called polling and considered best to avoid. What guarantees that your hardware is going to be in sync with QTimer? Furthermore, you need to implement additional packet handling delaing with partially received packets....The hardware part has low SRAM (256 bytes) thus buffering is not really an option.



  • JustGreg,

    About what hardware SRAM buffer there is a speech? If you mean the driver buffer (or the chip buffer) of serial port - that no problem with buffering will exist. Because the class automatically buffers all data at itself inside. And actually, having caused readAll() you will obtain data which are available in the class buffer, instead of the hardware buffer of serial port/driver.

    Thus you won't be able to lose any data at any options. Even if you don't call at readAll() - data won't be lost! You can read all accumulated data later.

    So, maybe you made something wrong. Please show your simple source project to reproduce problem.



  • Okay, I give a better explanation. The hardware is a wireless access point (WAP), consists of a proprieraty 2.4GHz transceiver chip, an MSP430 mcu and a USB-UART converter chip. It actually works as a 2-way bridge between the RF End Device (ED) and the Qt app.
    The ED transmits the packages at 40Hz. The WAP buffers some received data then transmits them in one big chunk to the PC. In my working setup, the WAP waits until 20 RF packets have been received then sends this accumulated data chunk to the PC. That means 2 packets per second or one packet per 500 ms. The WAP's MSP430 mcu has 256 bytes SRAM. Since 20 RF packet is 166 bytes, there is no space to buffer even 2 data chunks.

    My initial question was not about buffering and polling. I'd like to ask about the mechanism behing ReadyRead. When will it assert, for example? At every received bytes? In my experience in WIndows, the more frequent you assert your 'dataready' (or equivalent) event the more likely you will have dropouts.



  • bq. At every received bytes?

    Not necessarily through each byte.

    Everything depends on that, how many the byte managed to accept the driver of a serial port when triggered EV_RXCHAR event. All depends on driver, speed and so forth. If in the driver buffer for example 100 bytes are accepted, then ReadyRead() signal will be radiated through each 100 bytes (on the average).

    But once again I will repeat: in any case you won't lose data when reading because they are buffered in the class buffer (are transferred from the driver buffer to the class buffer at each EV_RXCHAR automatically).

    Also it silly to try to accept RealTime this through a serial port. Delivery of data isn't guaranteed in time. For RealTime you have to transfer data with use of some communication protocol of an exchange at which there are time stamps for pieces of data.

    In any case I don't understand your problem. Give an source code.



  • All right. So, if my hardware transmits 166 bytes (in a for loop, byte after byte) then readyread signal will be emitted most likely after all 166 bytes has been received but it is not guaranteed, right? It can be emitted like, after 100 bytes?

    I need to implement now some diagnostic functions to explore my problem behaviour better.

    Btw, every packet is timestamped for RF and UART as well. The access point counts and forward the number lost RF packets and timestamps the UART packets before sending them out. There is no retransmission protocol (yet).

    Real-time is obviously not 'hard' real-time here but something fast enough to acquire and display the data. I'm pretty experienced in what can be achieved in data acquisition at least in Windows (0.5-1kHz ECGs) so 40Hz with a callback mechanism should not be a problem for Linux - but I'm new to Linux programming so anything can happen.

    My code is here (relevant parts):

    openPort()

    @bool MainWindow::openPort(){

    bool status = false;
    
    QString pname = ui->le_portname->text();
    
    if (pname =="") return false;
    
    serial = new QSerialPort(this);
    serial->setPortName(pname);
    
    status = serial->open(QIODevice::ReadWrite);
    
    if (status){
        serial->setBaudRate(921600,QSerialPort::AllDirections);
        serial->setDataBits(QSerialPort::Data8);
        serial->setStopBits(QSerialPort::OneStop);
        serial->setFlowControl(QSerialPort::NoFlowControl);
        serial->setParity(QSerialPort::NoParity);
        ui->btn_start->setEnabled(true);
    
        QObject::connect(serial, SIGNAL(readyRead()), this, SLOT(readData()));
    }
    else{
        ui->btn_start->setEnabled(false);
        serial->close();
    }
    
    return status;
    

    }@

    readData()
    @void MainWindow::readData()
    {
    int buf;
    QByteArray data = serial->readAll();

    if (verifyCheckSum(data)){
    
        buf = checkLostUARTPackets(data);
        if (buf>0) writeMsgDisplay("LOST: "+QString::number(buf)+" lost UART packet has been detected.", QColor("red"));
        buf = checkLostRFPackets(data);
        if (buf>0) writeMsgDisplay("LOST: "+QString::number(buf)+" lost RF packet has been detected.", QColor("red"));
    
        getBatteryVoltage(data);
        ad.appendRFData(data);
        displayAxisData();
    }
    

    }@

    verifyChecksum()

    @bool MainWindow::verifyCheckSum(QByteArray msg){

    quint8 buf = 0;
    
    foreach (quint8 b, msg) buf +=b;
    
    if (buf) return false;
    
    return true;
    

    }@



  • bq. All right. So, if my hardware transmits 166 bytes (in a for loop, byte after byte) then readyread signal will be emitted most likely after all 166 bytes has been received but it is not guaranteed, right? It can be emitted like, after 100 bytes?

    Yes.

    The signal readyRead() is emitted when receiving at least one byte.

    But everything depends on driver realization or from load of CPU / Qt event-loop

    • Probably the driver can save up some quantity of bytes (for example 8), and then to establish a system event Rx of that that data are ready on a descriptor.

    • Probably from the notice moment a signal until reading passes some time during which some more bytes manage to arrive to the port.

    In any case, after a system Rx event is triggered, all data which managed to come to the UART driver buffer will be read and then the signal readyRead() will be emitted.


Log in to reply
 

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