QSerialport hardwarecontrol, do I get this rigth?



  • I'm working with an old PID controller (SCORBOT controller-a), which I'm not sure of if it is using CTS/RTS handshaking. Nothing was written about in the documentation regarding the controller.

    From the hardware side: I checked the cable, the pins required for CTS/RTS were soldered.

    From the software side I set the flow control to HardwareControl.

    Now, from my understanding, if the CTS/RTS handshake is there the following should happen:

    serialport->write("command1");
    serialport->write("command2");

    When the first command is being processed by the controller, it should not be possible to write the second command to the controller (CTS is low). So, No need to self-program anything regarding the pinoutSignals (CTS and RTS) the driver in the controller should do this automatically, right?

    My question to the forum is: Is my understanding about CTS/RTS in my case correct? Does this assure the controller isnt implementing the handshake?

    Br.



  • "...My question to the forum is: Is my understanding about CTS/RTS in my case correct? Does this assure the controller isnt implementing the handshake?"

    Yes, your understanding seems correct but your observations does not necessarily mean the handshaking isn't implemented.

    The RTS/CTS lines were a method used to prevent buffer overflow. If, for example, you have a slow dot matrix (serial) printer from the 1980's putting the data on paper was slower than transmitting by serial. When the RX buffer of the printer approached capacity it would set its RTS line low which would stop the source from transmitting. When the RX buffer is depleted enough it will return the RTS line high again and allow more data to be transmitted from the source.

    For your test senerio there are two issues that might make it seem as though it is not working:

    1. Sending single commands such as 'Command1' is too short and often a response is expected before the next command is sent. It is basically half duplex.

    2. The RX buffer on the receiving device is much bigger (could be virtual through a driver) and the device is likely much faster. You may not be able to send enough data at a rate that will even come close to overflowing a RX buffer of a modern device.

    If you have access to the inner workings of the receiving device you could modify it to read data very slowly. You might see the RTS lines change state in this case. On the receiving device if you have a driver between the application and the hardware likely the RX buffer is practically unlimited from the point of view of the application which means it doesn't mattern how slow you read the data (for a while at least). Depends on the OS of the receiving device and how it is setup.



  • Thank you very much for your reply, this cleared up some things.

    The PID controller is a pretty old one (1995), and the PC I'm sending data from is fairly new.

    So, I'm thinking that in my case when serialport->write("command1"); is written onto the buffer, the controller "wakes up" . But, at the time the controller wants to process the command and set his RTS line to low, the PC has already written serialport->write("command2"); and by sending the second command over the serial, he will overwrite or overflow the RX buffer of the controller.

    Working with a serialport->waitForReadyRead(500); works allright, but is definitely not the most practical way to go. Actually I never want to use a timer to solve any kind of problem..



  • This sequence:

    void Foo::send()
    {
        serialport->write("command1");
        serialport->write("command2");
    }
    

    will writes this commands set in one chunk: "command1command2".

    If you need to write it separatelly, then you should use bytesWritten(qint64 bytes) signal, and to count a number of written bytes (pseudo code):

    QList<QByteArray> commands << "command1" << "command2";
    qint64 counter = 0;
    QSerialPort port;
    connect(port, &QSerialPort::bytesWritten, this, &Foo::processWritten);
    
    void Foo::sendNextCommand()
    {
        port.write(commands .first());
    }
    
    void Foo::processWritten(qint64 bytes)
    {
        counter += bytes;
        QByteArray pendingcommand = commands.first();
        if (counter < pendingcommand.size())
            return; // do nothing
        
        counter  = 0;
        commands.takeFirst(); // clear a command
        
        sendNextCommand(); // write next command
    }
    

    or something like..



  • @kuzulis Thank you! The only thing with your solution is: the SIGNAL(byteswritten()) means a specific amount of data is being written to the device. The problem in my case is that the controller needs time to process the first command and probably clear his buffer and do some more stuff. When signal emits, and my program writes the next command, the PC will still be too fast. The buffer of the controller will be overwritten/overflowed.



  • Yes, I understand. Just try to use HW flow control then (but nobody tested this in QtSerialPort, because it is difficult to make without an specific hardware).

    Upd:

    You can alto try to sent byte-per byte.. In this case the device should be in time to trigger of a CTS (maybe). ;)

    However this does not need (I talk about byte-per-byte), because even if you do writing of 100500 bytes in one chunk, then a remote device just will set CTS when it can not be processed bytes anymore and then a serialport driver just paused of writing. But when a remote device will cleanup CTS, then a serialport driver will continue a remainder of source 100500 bytes. I think...

    Of course, your remote device should has some FIFO, and to setup CTS when a FIFO has (3/4) from the capacity.. or something like..



  • I don't know if this is part of the problem but there is a TX buffer in QSerialPort. It caused me some grief recently when sending large blocks of data.

    Basically when you write some data to the serial port it is actually put into a queue so you think it has been completely sent. The idea mentioned earlier by @kuzulis (using the bytesWritten signal) is a good idea so you can actually monitor what is sent.

    In my case I chose to block until the bytes were actually written. Blocking is fine in a separate thread if you are setup this way (don't put this in the GUI thread).

    result = d_serial_device->write(data,max_size);
    d_serial_device->waitForBytesWritten(-1);  // block until actually written
    

    This is an area that changed from the third party serial port library (used prior to Qt version 5). Using the new version I would send a large block of data, didn't realize it was in the process of sending (thought it was sent) and would report an error after not getting a response within an expected amount of time (it was still sending data at this point).

    Another issue I noticed is that data is buffered you do not want to close the QSerialPort until it is complete. Something like this will truncate what is sent:

    QSerialPort port;
    port.open(QIODevice::ReadWrite);
    port.write("some data sent out the serial port"):
    port.close();  // not done sending.  data lost here when closed
    


  • @Rondog @kuzulis I'm going to give the bytesWritten()signal a try, I will let you know how it worked out. I don't have a separate thread set up, but it's definitely worth the try! Big thanks for the reply's!


Log in to reply
 

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