Thursday, December 16, 2010

Boost serial ports and new C++0x (C++1x) features

I recently spent some time writing my first small application on Linux (Ubuntu 10.10 using GCC 4.5 and the Boost libraries). The plan is to try writing some cross-platform, portable C++, while trying out the new C++0x features that have been implemented in GCC 4.5.

This application uses the asio (asynchronous I/O) features in the Boost libraries along with the Boost serial_port implementation to read and display input from a serial port (e.g. GPS NMEA data). I also used experimental C++0x threading libraries and lambdas and tried the Boost implementation of the observer pattern - sockets and signals. I have yet to test this code on any other platform.

main.cpp

#include "headers.h"
#include "SerialPort.h"
using namespace std;
using namespace boost::asio;

int main()
{
        //allow inter-mixing of wcout and cout calls (apparently non-standard)
        ios::sync_with_stdio(false);

        string portName = "/dev/ttyUSB1";

        io_service ioService;
        io_service::work work(ioService);
        thread ioThread([&ioService] () { ioService.run(); });

        SerialPort port(portName, 4800, ioService);
        port.LineReceived.connect([] (std::vector<char> const & line)
            {
                cout << &line[0] << endl;
            });

        wcout << L"Listening on port: " << endl;
        cout << portName << endl;
        wcout << L"Press enter to stop listening..." << endl;
        wstring result;
        getline(wcin, result);
        wcout << L"Exiting..." << endl;

        ioService.stop();
        ioThread.join();
}

headers.h

Separated for precompilation.
#include <boost/asio.hpp>
#include <boost/signal.hpp>
#include <iostream>
#include <vector>
#include <thread>
#include <functional>

SerialPort.h

#ifndef SERIALPORT_H
#define SERIALPORT_H

class SerialPort
{
public:
    SerialPort(std::string const & portName, int portSpeed, boost::asio::io_service & ioService);

    boost::signal<void(std::vector<char> const &)> LineReceived;

private:
    void OnBytesRead(boost::system::error_code const &error, size_t bytesReceived);
    void BeginListening();

    std::vector<char> _readBuffer;
    std::vector<char> _currentLine;
    boost::asio::serial_port _port;
};

#endif // SERIALPORT_H

SerialPort.cpp

#include "headers.h"
#include "SerialPort.h"
using namespace std;
using namespace boost::asio;

SerialPort::SerialPort(string const & portName, int const portSpeed, io_service & ioService)
    : _readBuffer(1000),
      _port(ioService, portName)

{
    _port.set_option(serial_port_base::baud_rate(portSpeed));

    BeginListening();
}

void SerialPort::BeginListening()
{
    _port.async_read_some(buffer(&_readBuffer[0], _readBuffer.size()),
        [this](boost::system::error_code const &error, size_t bytesReceived)
        {
            this->OnBytesRead(error, bytesReceived);
        });
}

void SerialPort::OnBytesRead(boost::system::error_code const &error, size_t bytesReceived)
{
    if(error)
    {
        return;
    }

    for(size_t i = 0; i != bytesReceived; ++i)
    {
        char currentChar = _readBuffer[i];
        switch(currentChar)
        {
        case '\r':
            break;
        case '\n':
            LineReceived(_currentLine);
            _currentLine.clear();
            break;
        default:
            _currentLine.push_back(currentChar);
        }
    }

    BeginListening();
}

0 comments:

Post a Comment