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



MIKAEL PLOUHINEC 

miplouhinec@sqli.com
Tél : +33(0) 2 51 79 77 40 - Mob : +33(0) 6 80 23 21 28
www.sqli.com - www.sqli-enterprise.com
Digital That Works