Boost logo

Boost Users :

Subject: [Boost-users] LibBoost - Asio - SerialPort - set_option throw an exception
From: Mikael Plouhinec (miplouhinec_at_[hidden])
Date: 2014-12-01 08:20:51


Hello,

I have some problem using the libboost asio to communicate with serial port.

I want to open my serial port with :

m_Balance.open(BalancePort, (unsigned int)9600,
boost::asio::serial_port_base::parity(boost::asio::serial_port_base::parity::even),
boost::asio::serial_port_base::character_size((unsigned int)7),
boost::asio::serial_port_base::flow_control(
boost::asio::serial_port_base::flow_control::hardware),
boost::asio::serial_port_base::stop_bits(boost::asio::serial_port_base::stop_bits::one));

but I have an exception with the character_size : Invalid Argument

I put some log in the open method and I have found that my problem is on
the set_option of the character size.

Can someone help me?

Thanks a lot.

Mike

Libboost is on Debian Wheezy : version 1.49

I use the following Serial Class (cpp) :

/*
 * File: Serial.cpp
 * Author: Terraneo Federico
 * Distributed under the Boost Software License, Version 1.0.
 * Created on September 12, 2009, 3:47 PM
 *
 * v1.05: Fixed a bug regarding reading after a timeout (again).
 *
 * v1.04: Fixed bug with timeout set to zero
 *
 * v1.03: Fix for Mac OS X, now fully working on Mac.
 *
 * v1.02: Code cleanup, speed improvements, bug fixes.
 *
 * v1.01: Fixed a bug that caused errors while reading after a timeout.
 *
 * v1.00: First release.
 */

#include "Serial.h"
#include <string>
#include <algorithm>
#include <iostream>
#include <boost/bind.hpp>

using namespace std;
using namespace boost;

CSerial::CSerial(): io(), port(io), timer(io),
        timeout(posix_time::seconds(0)) {}

CSerial::CSerial(const std::string& devname, unsigned int baud_rate,
        asio::serial_port_base::parity opt_parity,
        asio::serial_port_base::character_size opt_csize,
        asio::serial_port_base::flow_control opt_flow,
        asio::serial_port_base::stop_bits opt_stop)
        : io(), port(io), timer(io), timeout(posix_time::seconds(0))
{
    open(devname,baud_rate,opt_parity,opt_csize,opt_flow,opt_stop);
}

void CSerial::open(const std::string& devname, unsigned int baud_rate,
        asio::serial_port_base::parity opt_parity,
        asio::serial_port_base::character_size opt_csize,
        asio::serial_port_base::flow_control opt_flow,
        asio::serial_port_base::stop_bits opt_stop)
{
    if(isOpen()) close();
    port.open(devname);
Log.AddLine("set 1", LOG_DEBUG);
    port.set_option(asio::serial_port_base::baud_rate(baud_rate));
Log.AddLine("set 2", LOG_DEBUG);
    port.set_option(opt_parity);
Log.AddLine("set 3", LOG_DEBUG);
    port.set_option(opt_csize);
Log.AddLine("set 4", LOG_DEBUG);
    port.set_option(opt_flow);
Log.AddLine("set 5", LOG_DEBUG);
    port.set_option(opt_stop);
Log.AddLine("set 6", LOG_DEBUG);
}

bool CSerial::isOpen() const
{
    return port.is_open();
}

void CSerial::close()
{
    if(isOpen()==false) return;
    port.close();
}

void CSerial::setTimeout(const posix_time::time_duration& t)
{
    timeout=t;
}

void CSerial::write(const char *data, size_t size)
{
    asio::write(port,asio::buffer(data,size));
}

void CSerial::write(const std::vector<char>& data)
{
    asio::write(port,asio::buffer(&data[0],data.size()));
}

void CSerial::writeString(const std::string& s)
{
    asio::write(port,asio::buffer(s.c_str(),s.size()));
}

void CSerial::read(char *data, size_t size)
{
    if(readData.size()>0)//If there is some data from a previous read
    {
        istream is(&readData);
        size_t toRead=min(readData.size(),size);//How many bytes to read?
        is.read(data,toRead);
        data+=toRead;
        size-=toRead;
        if(size==0) return;//If read data was enough, just return
    }

    setupParameters=ReadSetupParameters(data,size);
    performReadSetup(setupParameters);

    //For this code to work, there should always be a timeout, so the
    //request for no timeout is translated into a very long timeout
    if(timeout!=posix_time::seconds(0)) timer.expires_from_now(timeout);
    else timer.expires_from_now(posix_time::hours(100000));

    timer.async_wait(boost::bind(&CSerial::timeoutExpired,this,
                asio::placeholders::error));

    result=resultInProgress;
    bytesTransferred=0;
    for(;;)
    {
        io.run_one();
        switch(result)
        {
            case resultSuccess:
                timer.cancel();
                return;
            case resultTimeoutExpired:
                port.cancel();
                throw(timeout_exception("Timeout expired"));
            case resultError:
                timer.cancel();
                port.cancel();

throw(boost::system::system_error(boost::system::error_code(),
                        "Error while reading"));
            //if resultInProgress remain in the loop
        }
    }
}

std::vector<char> CSerial::read(size_t size)
{
    vector<char> result(size,'\0');//Allocate a vector with the desired size
    read(&result[0],size);//Fill it with values
    return result;
}

std::string CSerial::readString(size_t size)
{
    string result(size,'\0');//Allocate a string with the desired size
    read(&result[0],size);//Fill it with values
    return result;
}

std::string CSerial::readStringUntil(const std::string& delim)
{
    // Note: if readData contains some previously read data, the call to
    // async_read_until (which is done in performReadSetup) correctly
handles
    // it. If the data is enough it will also immediately call
readCompleted()
    setupParameters=ReadSetupParameters(delim);
    performReadSetup(setupParameters);

    //For this code to work, there should always be a timeout, so the
    //request for no timeout is translated into a very long timeout
    if(timeout!=posix_time::seconds(0)) timer.expires_from_now(timeout);
    else timer.expires_from_now(posix_time::hours(100000));

    timer.async_wait(boost::bind(&CSerial::timeoutExpired,this,
                asio::placeholders::error));

    result=resultInProgress;
    bytesTransferred=0;
    for(;;)
    {
        io.run_one();
        switch(result)
        {
            case resultSuccess:
                {
                    timer.cancel();
                    bytesTransferred-=delim.size();//Don't count delim
                    istream is(&readData);
                    string result(bytesTransferred,'\0');//Alloc string
                    is.read(&result[0],bytesTransferred);//Fill values
                    is.ignore(delim.size());//Remove delimiter from stream
                    return result;
                }
            case resultTimeoutExpired:
                port.cancel();
                throw(timeout_exception("Timeout expired"));
            case resultError:
                timer.cancel();
                port.cancel();

throw(boost::system::system_error(boost::system::error_code(),
                        "Error while reading"));
            //if resultInProgress remain in the loop
        }
    }
}

CSerial::~CSerial() {}

void CSerial::performReadSetup(const ReadSetupParameters& param)
{
    if(param.fixedSize)
    {

asio::async_read(port,asio::buffer(param.data,param.size),boost::bind(
                &CSerial::readCompleted,this,asio::placeholders::error,
                asio::placeholders::bytes_transferred));
    } else {
        asio::async_read_until(port,readData,param.delim,boost::bind(
                &CSerial::readCompleted,this,asio::placeholders::error,
                asio::placeholders::bytes_transferred));
    }
}

void CSerial::timeoutExpired(const boost::system::error_code& error)
{
     if(!error && result==resultInProgress) result=resultTimeoutExpired;
}

void CSerial::readCompleted(const boost::system::error_code& error,
        const size_t bytesTransferred)
{
    if(!error)
    {
        result=resultSuccess;
        this->bytesTransferred=bytesTransferred;
        return;
    }

    //In case a asynchronous operation is cancelled due to a timeout,
    //each OS seems to have its way to react.
    #ifdef _WIN32
    if(error.value()==995) return; //Windows spits out error 995
    #elif defined(__APPLE__)
    if(error.value()==45)
    {
        //Bug on OS X, it might be necessary to repeat the setup
        //http://osdir.com/ml/lib.boost.asio.user/2008-08/msg00004.html
        performReadSetup(setupParameters);
        return;
    }
    #else //Linux
    if(error.value()==125) return; //Linux outputs error 125
    #endif

    result=resultError;
}

Serial.h

/*
 * File: Serial.h
 * Author: Terraneo Federico
 * Distributed under the Boost Software License, Version 1.0.
 *
 * Created on September 12, 2009, 3:47 PM
 */

#ifndef Serial_H
#define Serial_H

#include <stdexcept>
#include <boost/utility.hpp>
#include <boost/asio.hpp>
#include "Log.h"

/**
 * Thrown if timeout occurs
 */
class timeout_exception: public std::runtime_error
{
public:
    timeout_exception(const std::string& arg): runtime_error(arg) {}
};

/**
 * Serial port class, with timeout on read operations.
 */
class CSerial: private boost::noncopyable
{
public:
    CSerial();

    /**
     * Opens a serial device. By default timeout is disabled.
     * \param devname serial device name, example "/dev/ttyS0" or "COM1"
     * \param baud_rate serial baud rate
     * \param opt_parity serial parity, default none
     * \param opt_csize serial character size, default 8bit
     * \param opt_flow serial flow control, default none
     * \param opt_stop serial stop bits, default 1
     * \throws boost::system::system_error if cannot open the
     * serial device
     */
    CSerial(const std::string& devname, unsigned int baud_rate,
        boost::asio::serial_port_base::parity opt_parity=
            boost::asio::serial_port_base::parity(
                boost::asio::serial_port_base::parity::none),
        boost::asio::serial_port_base::character_size opt_csize=
            boost::asio::serial_port_base::character_size(8),
        boost::asio::serial_port_base::flow_control opt_flow=
            boost::asio::serial_port_base::flow_control(
                boost::asio::serial_port_base::flow_control::none),
        boost::asio::serial_port_base::stop_bits opt_stop=
            boost::asio::serial_port_base::stop_bits(
                boost::asio::serial_port_base::stop_bits::one));

    /**
     * Opens a serial device.
     * \param devname serial device name, example "/dev/ttyS0" or "COM1"
     * \param baud_rate serial baud rate
     * \param opt_parity serial parity, default none
     * \param opt_csize serial character size, default 8bit
     * \param opt_flow serial flow control, default none
     * \param opt_stop serial stop bits, default 1
     * \throws boost::system::system_error if cannot open the
     * serial device
     */
    void open(const std::string& devname, unsigned int baud_rate,
        boost::asio::serial_port_base::parity opt_parity=
            boost::asio::serial_port_base::parity(
                boost::asio::serial_port_base::parity::none),
        boost::asio::serial_port_base::character_size opt_csize=
            boost::asio::serial_port_base::character_size(8),
        boost::asio::serial_port_base::flow_control opt_flow=
            boost::asio::serial_port_base::flow_control(
                boost::asio::serial_port_base::flow_control::none),
        boost::asio::serial_port_base::stop_bits opt_stop=
            boost::asio::serial_port_base::stop_bits(
                boost::asio::serial_port_base::stop_bits::one));

    /**
     * \return true if serial device is open
     */
    bool isOpen() const;

    /**
     * Close the serial device
     * \throws boost::system::system_error if any error
     */
    void close();

    /**
     * Set the timeout on read/write operations.
     * To disable the timeout, call
setTimeout(boost::posix_time::seconds(0));
     */
    void setTimeout(const boost::posix_time::time_duration& t);

    /**
     * Write data
     * \param data array of char to be sent through the serial device
     * \param size array size
     * \throws boost::system::system_error if any error
     */
    void write(const char *data, size_t size);

     /**
     * Write data
     * \param data to be sent through the serial device
     * \throws boost::system::system_error if any error
     */
    void write(const std::vector<char>& data);

    /**
    * Write a string. Can be used to send ASCII data to the serial device.
    * To send binary data, use write()
    * \param s string to send
    * \throws boost::system::system_error if any error
    */
    void writeString(const std::string& s);

    /**
     * Read some data, blocking
     * \param data array of char to be read through the serial device
     * \param size array size
     * \return numbr of character actually read 0<=return<=size
     * \throws boost::system::system_error if any error
     * \throws timeout_exception in case of timeout
     */
    void read(char *data, size_t size);

    /**
     * Read some data, blocking
     * \param size how much data to read
     * \return the receive buffer. It iempty if no data is available
     * \throws boost::system::system_error if any error
     * \throws timeout_exception in case of timeout
     */
    std::vector<char> read(size_t size);

    /**
     * Read a string, blocking
     * Can only be used if the user is sure that the serial device will not
     * send binary data. For binary data read, use read()
     * The returned string is empty if no data has arrived
     * \param size hw much data to read
     * \return a string with the received data.
     * \throws boost::system::system_error if any error
     * \throws timeout_exception in case of timeout
     */
    std::string readString(size_t size);

    /**
     * Read a line, blocking
     * Can only be used if the user is sure that the serial device will not
     * send binary data. For binary data read, use read()
     * The returned string is empty if the line delimiter has not yet
arrived.
     * \param delimiter line delimiter, default="\n"
     * \return a string with the received data. The delimiter is removed
from
     * the string.
     * \throws boost::system::system_error if any error
     * \throws timeout_exception in case of timeout
     */
    std::string readStringUntil(const std::string& delim="\n");

    ~CSerial();

private:

    /**
     * Parameters of performReadSetup.
     * Just wrapper class, no encapsulation provided
     */
    class ReadSetupParameters
    {
    public:
        ReadSetupParameters(): fixedSize(false), delim(""), data(0),
size(0) {}

        explicit ReadSetupParameters(const std::string& delim):
                fixedSize(false), delim(delim), data(0), size(0) { }

        ReadSetupParameters(char *data, size_t size): fixedSize(true),
                delim(""), data(data), size(size) { }

        //Using default copy constructor, operator=

        bool fixedSize; ///< True if need to read a fixed number of
parameters
        std::string delim; ///< String end delimiter (valid if
fixedSize=false)
        char *data; ///< Pointer to data array (valid if fixedSize=true)
        size_t size; ///< Array size (valid if fixedSize=true)
    };

    /**
     * This member function sets up a read operation, both reading a
specified
     * number of characters and reading until a delimiter string.
     */
    void performReadSetup(const ReadSetupParameters& param);

    /**
     * Callack called either when the read timeout is expired or canceled.
     * If called because timeout expired, sets result to
resultTimeoutExpired
     */
    void timeoutExpired(const boost::system::error_code& error);

    /**
     * Callback called either if a read complete or read error occurs
     * If called because of read complete, sets result to resultSuccess
     * If called because read error, sets result to resultError
     */
    void readCompleted(const boost::system::error_code& error,
            const size_t bytesTransferred);

    /**
     * Possible outcome of a read. Set by callbacks, read from main code
     */
    enum ReadResult
    {
        resultInProgress,
        resultSuccess,
        resultError,
        resultTimeoutExpired
    };

    boost::asio::io_service io; ///< Io service object
    boost::asio::serial_port port; ///< Serial port object
    boost::asio::deadline_timer timer; ///< Timer for timeout
    boost::posix_time::time_duration timeout; ///< Read/write timeout
    boost::asio::streambuf readData; ///< Holds eventual read but not
consumed
    enum ReadResult result; ///< Used by read with timeout
    size_t bytesTransferred; ///< Used by async read callback
    ReadSetupParameters setupParameters; ///< Global because used in the
OSX fix
};

#endif //CSerial_H

--
[logo Aston] 	
MIKAEL PLOUHINEC
miplouhinec_at_[hidden]
Tél : +33(0) 2 51 79 77 40 - Mob : +33(0) 6 80 23 21 28
www.sqli.com - www.aston-ecole.com


Boost-users list run by williamkempf at hotmail.com, kalb at libertysoft.com, bjorn.karlsson at readsoft.com, gregod at cs.rpi.edu, wekempf at cox.net