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
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