Reading from serial port with Boost Asio
Asked Answered
F

2

9

I'm want to check for incoming data packages on the serial port, using boost.asio. Each data packet will start with a header that is one byte long, and will specify what type of the message has been sent. Each different type of message has its own length. The function I want to write should listen for new incoming messages continually, and when it finds one it should read it, and call some other function to parse it. My current code is as follows:

void check_for_incoming_messages()
{
    boost::asio::streambuf response;
    boost::system::error_code error;
    std::string s1, s2;
    if (boost::asio::read(port, response, boost::asio::transfer_at_least(0), error)) {
        s1 = streambuf_to_string(response);
        int msg_code = s1[0];
        if (msg_code < 0 || msg_code >= NUM_MESSAGES) {
            // Handle error, invalid message header
        }
        if (boost::asio::read(port, response, boost::asio::transfer_at_least(message_lengths[msg_code]-s1.length()), error)) {
            s2 = streambuf_to_string(response);
            // Handle the content of s1 and s2
        }
        else if (error != boost::asio::error::eof) {
            throw boost::system::system_error(error);
        }
    }
    else if (error != boost::asio::error::eof) {
        throw boost::system::system_error(error);
    }
}

Is boost::asio::streambuf the right tool to use? And how do I extract the data from it so I can parse the message? I also want to know if I need to have a separate thread which only calls this function, so that it gets called more often. Should I be worried about losing data between two calls to the function because of high traffic and serial port's buffer running out? I'm using Qt's libraries for GUI and I don't really know how much time it takes to process all the events.

Edit: The interesting question is: how can I check if there is any incoming data at the serial port? If there is no incoming data, I don't want the function to block...

Factor answered 10/5, 2010 at 23:11 Comment(0)
C
27

This article is helpful in understanding how ASIO can be used asynchronously with serial ports:

UPDATE (2019-03):

The original article I had linked to is no longer available and is difficult to find even in Internet Archive. (Here is a snapshot.). There are now newer articles on using ASIO for serial I/O found easily by searching, but this older article is still very useful. I'm putting it in a public gist so that it doesn't get lost:

The code described in the article appears to have been copied here:

The author seems to have updated it for C++11. I believe the article was originally written by fede.tft.

Cavie answered 6/6, 2010 at 21:50 Comment(4)
Thank you! :) That was what I found I had to do too.Factor
I found out that I had to read asynchronously in order not to disrupt the program flow. Before, I was using the read function, while async_read was the correct function. The same goes for writing, async_write should be used to prevent the program from freezing the program if the read by some reason makes a pause.Factor
@Factor could you share withe me the source code from the webpage of fede? The site is now being moved to Internet Archive and cannot clone anymore. Thank you!Purely
@ShawnLe I expect the original source will be available for cloning soon on Internet Archive. However, the source from the original article also appears to be available on Github: github.com/fedetft/serial-portCavie
K
3

Jason,

If it is suitable for your application, I'd highly recommend implementing a callback-based asynchronous serial RX. How do I perform a nonblocking read using asio? has a great little example of how to implement asynch serial with a timeout. As you recognised, it will require a multi-threaded implementation to get the performance advantages, so you will need to put some thought where your recieved data will be buffered to make sure you aren't doing a lot of copying.

As far as the boost::streambuff stuff goes, I personally prefer just to block out some memory as a char array - char m_RXBuffer[m_RXBuffSize] and use boost::asio::buffer(m_RXBuffer, m_RXBuffSize) to pass the target buffer into async_read_some. In particular for RS232, I have always found the fact that the underlying data is a stream of bytes naturally maps a lot better onto a simple char array than any of the more complex data structures.

Good Luck!

Keynote answered 12/3, 2012 at 5:57 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.