Results 1 to 5 of 5

Thread: QtSerialPort--how to recognize end of message

  1. #1
    Join Date
    Jun 2012
    Posts
    219
    Qt products
    Qt4
    Platforms
    Windows
    Thanks
    28
    Thanked 3 Times in 3 Posts

    Default QtSerialPort--how to recognize end of message

    I've been trying out the SerialPort class. It seems easy to use, but I'm struggling with how to deal with a protocol I'm stuck with, without using some kind of time-out mechanism.

    The protocol consists of a variable number of fixed length (sixteen byte) records. Each record has an stx (0x02) in the first byte and a etx (0x03) in the last byte. The last record in the message will have an ascii "X" in byte 7.

    My Qt C++ code needs to receive these messages and update widgets based on the message content. Messages will be sent to my code asynchronously and infrequently, typically, a set of about ten, sixteen byte records every couple of minutes.

    I've implemented code in Tcl that works well, but uses a blocking read with a time-out mechanism. The time out is set to several seconds so that the data that's returned contains all the records for the message. A check for the "X" in byte 7 of the last record is done, but no recovery is attempted if it's something else. It seems to always be correct.

    There's no GUI in the Tcl implementation, so I don't have to worry about it becoming non-responsive during the blocking read.

    It seems that doing a blocking read is highly undesirable in Qt, so I'm wondering if there is a better approach? I could keep checking SerialPort::bytesAvailable() and do a read, append byte data, and start a timer each time data is read. When the timer expires, the message is assumed to be complete and a check could be done for the "X" character like I do in Tcl.

    Does this seem like a reasonable approach? Do I need to do something like "update" in the loop where I'm checking bytesAvailable to keep the GUI responsive?

    Thanks,

    Dave Thomas

  2. #2
    Join Date
    Mar 2008
    Location
    Kraków, Poland
    Posts
    1,540
    Qt products
    Qt4
    Platforms
    Unix/X11 Windows
    Thanked 284 Times in 279 Posts

    Default Re: QtSerialPort--how to recognize end of message

    This is classical multi-layer transport. Serial port and QtSerialPort library is a physical layer with stream of bytes. You need to create a second layer that is turning the stream of bytes to the list of packages received. Each package can generate a ready signal to the presentation layer. The presentation layer uses the finished packages. In a case like yours (one-way transmission) the first two layers can be placed in a separate thread.

  3. #3
    Join Date
    Jan 2009
    Location
    Russia
    Posts
    309
    Qt products
    Qt4
    Platforms
    Unix/X11 Windows
    Thanks
    2
    Thanked 43 Times in 42 Posts

    Default Re: QtSerialPort--how to recognize end of message

    There is a no bad description of the principle of the development of protocols:

    http://www.eventhelix.com/realtimema...ocol_layer.htm

    but it is not tied to the signals / slots.

    As others have said Lesiok good practice to use ISO / OSI model, for example
    can be implement this (pseudo code):

    Custom protocol:

    packet[<header><body><trailer>]

    where:

    header[<start><length>]

    - start is 1 byte = 0x02
    - length is 1 byte from 0 to 255

    body[<data>]

    - data is data array from 0 to 255 bytes

    trailer[<end>]

    - end is 1 byte = 0x03
    Code:
    Qt Code:
    1. class Layer : public QIODevice
    2. {
    3. Q_OBJECT
    4. public:
    5. Layer() : lower(0), upper(0) {}
    6. protected:
    7. Layer *lower;
    8. Layer *upper;
    9. };
    10.  
    11. PresentationLayer : public Layer
    12. {
    13. Q_OBJECT
    14. signals:
    15. void timeout();
    16.  
    17. public:
    18. PresentationLayer() : process(false) {
    19. lower = new SerialPort();
    20. connect(lower, SIGNAL(readyRead()), this, SLOT(onFromLower());
    21. timer = new QTimer(100); // 100 msec waiting
    22. timer->setSingleShot(true);
    23. connect(timer, SIGNAL(timeout()), this, SLOT(onWaitPacketTimeOut());
    24. }
    25. protected:
    26. virtual quint64 readData() {
    27. return data.size();
    28. }
    29. private slots:
    30. void onFromLower() {
    31. if (!process)
    32. timer->start();
    33.  
    34. queue.append(lower->readAll());
    35.  
    36. while (queue.size() > 0) {
    37. char start = queue.at(0);
    38. if (start != START_CONSTANT) {
    39. queue.removeFirst();
    40. continue;
    41. }
    42.  
    43. char length = queue.at(1);
    44. if (queue.size() < (1 + 1 + length + 1)) // start + length + data + end
    45. // not the whole package was received.
    46. break;
    47.  
    48. char end = queue.at(1 + length);
    49. if (end != END_CONSTANT) {
    50. queue.removeFirst();
    51. continue;
    52. }
    53.  
    54. // ok, all checks success, packed received,
    55. // need take data and transfer to upper Level
    56. timer->stop();
    57. queue.removeFirst(); // remove start field
    58. queue.removeFirst(); // remove length field
    59. queue.removeLast(); // remove end field
    60. // done
    61. data = queue.toQByteArray();
    62. queue.clear();
    63. emit readyRead();
    64. }
    65. }
    66.  
    67. void onWaitPacketTimeOut() {
    68. process = false;
    69. queue.clear();
    70. emit timeout();
    71. }
    72.  
    73. QQueue<char> queue;
    74. QTimer *timer;
    75. bool process;
    76. QByteArray data;
    77. }
    To copy to clipboard, switch view to plain text mode 

    Or something else to come up with ...
    Last edited by kuzulis; 28th November 2012 at 08:24.

  4. #4
    Join Date
    Jun 2012
    Posts
    219
    Qt products
    Qt4
    Platforms
    Windows
    Thanks
    28
    Thanked 3 Times in 3 Posts

    Default Re: QtSerialPort--how to recognize end of message

    Thanks for the replies! I didn't mean to ignore them for so long. I didn't get email notification I expected, so I thought no one had bothered to respnd.

    I don't think this a classical multi-layer transport, but a little more background is needed, I think.

    I left out the "stickler" in the protocol. Each record includes a "block check character", which is a checksum of the preceeding fields in the record. Since this "bcc" (block check character) field is binary and could contain either the STX or ETX character (and probably WILL about every 256 records), I don't think I can use either the STX or ETX field to figure out record boundaries. So, I think I have to do "strange and unnatural acts" to process the message.

    I think the only way I can know the entire message has been sent is to reset a timer after each character is received. When the timer expires, I'll process all the bytes received since the last valid message and check if the new bytes appear be a valid message.

    The check for valid message would consist of

    1) integral number of 15 byte records in the message (message bye length modulo 15 is zero)
    2) first byte of every 15 byte record is STX
    3) last byte of every 15 byte record is ETX
    4) bcc (14th byte of each record) is checksum


    I expect to seem some junk data due to cable plug/unplug, but otherwise there probably will be no checksum errors.

    So, back to my question. I still think I need something like a blocking read that times out after a second or two to trigger processing of the bytes received. So, I'd think I need to reset the single shot timer in "onFromLower" handler.

    I think the it be more straight forward to do something like this in the readyRead handler:

    time_out= QTime::currentTime().addSecs(1);
    while( QTime::currentTime() < time_out )
    {

    msg.append(serial->readAll);
    // process events (forgot the Qt call)
    }

    Or, is there a reason using a slot for the single shot timer is better?

    Thanks!

    Dave Thomas
    Last edited by davethomaspilot; 3rd December 2012 at 13:51.

  5. #5
    Join Date
    Jan 2009
    Location
    Russia
    Posts
    309
    Qt products
    Qt4
    Platforms
    Unix/X11 Windows
    Thanks
    2
    Thanked 43 Times in 42 Posts

    Default Re: QtSerialPort--how to recognize end of message

    Or, is there a reason using a slot for the single shot timer is better?
    I do not know, try it.

    There is another way: use blocking I / O in a separate thread, see new examples BlockingMaster/BlockingSlave.

Similar Threads

  1. Adding QtSerialPort as a library -
    By HSPalm in forum Newbie
    Replies: 14
    Last Post: 26th June 2014, 16:16
  2. QtSerialPort synchronous approach
    By codein in forum Newbie
    Replies: 2
    Last Post: 4th September 2012, 09:45
  3. Replies: 4
    Last Post: 24th July 2012, 10:31
  4. cannot build qtserialport
    By banlinhtienphong in forum Qt Programming
    Replies: 1
    Last Post: 16th December 2011, 05:56
  5. How do I get Qt to recognize the oci driver.
    By yleesun in forum Qt Programming
    Replies: 11
    Last Post: 19th January 2009, 03:50

Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  
Qt is a trademark of The Qt Company.