Basically my problem was not starting the io_service
thread after async_read_some
in the same function. Can you blame me? This stuff is not very clear cut. Here's my code in case anyone wants it (INFO and ERROR come from boost logging, see one of my other questions on it):
serial_comm.hpp
#ifndef __SERIAL_COMM_HPP
#define __SERIAL_COMM_HPP
#include <boost/asio.hpp>
#include <boost/asio/serial_port.hpp>
#include <boost/system/error_code.hpp>
#include <boost/system/system_error.hpp>
#include <boost/bind.hpp>
#include <boost/thread.hpp>
#include <string>
#include <atomic>
#include "logging.hpp" // Boost logging
#define MAX_RESPONSE_SIZE 256
class serial_comm
{
public:
void open_serial_port (std::string device_path, unsigned int baud_rate);
void close_serial_port (void);
void async_read_some (void);
std::string serial_read_data;
std::atomic <bool> serial_data_read_complete{false};
private:
// functions
void data_received (const boost::system::error_code& ec, size_t bytes_transferred);
// variables
boost::mutex mutex;
boost::system::error_code error;
boost::asio::io_service io_service;
typedef boost::shared_ptr<boost::asio::serial_port> serial_port_ptr;
serial_port_ptr serial_port;
char read_buffer[MAX_RESPONSE_SIZE];
};
#endif // __SERIAL_COMM_HPP
serial_comm.cpp
#include "../include/serial_comm.hpp"
void serial_comm::open_serial_port (std::string device_path, unsigned int baud_rate)
{
INFO << "started";
try
{
serial_port.reset();
serial_port = serial_port_ptr(new boost::asio::serial_port(io_service));
serial_port->open(device_path, error);
if (error)
{
ERROR << "error.message() >> " << error.message().c_str();
throw -3;
}
// set options
serial_port->set_option(boost::asio::serial_port_base::baud_rate(baud_rate));
serial_port->set_option(boost::asio::serial_port_base::character_size(8));
serial_port->set_option(boost::asio::serial_port_base::stop_bits(boost::asio::serial_port_base::stop_bits::one));
serial_port->set_option(boost::asio::serial_port_base::parity(boost::asio::serial_port_base::parity::none));
serial_port->set_option(boost::asio::serial_port_base::flow_control(boost::asio::serial_port_base::flow_control::none));
}
catch (int error)
{
ERROR << "error = " << error;
throw -1;
}
catch (const std::exception &e)
{
ERROR << "e.what() = " << e.what();
throw -2;
}
INFO << device_path << " opened correctly";
INFO << "ended";
return;
}
void serial_comm::close_serial_port()
{
boost::mutex::scoped_lock lock(mutex); // prevent multiple thread access
INFO << "started";
try
{
if (serial_port)
{
serial_port->cancel();
serial_port->close();
serial_port.reset();
}
else
{
WARNING << "serial port is not open";
}
io_service.stop();
io_service.reset();
}
catch (const std::exception &e)
{
ERROR << "e.what() = " << e.what();
throw -1;
}
INFO << "ended";
return;
}
void serial_comm::async_read_some (void)
{
boost::mutex::scoped_lock lock (mutex); // prevent multiple threads
INFO << "started";
std::string data;
try
{
if (serial_port.get() == NULL || !serial_port->is_open())
{
WARNING << "serial port is not open";
throw -2;
}
serial_port->async_read_some(
boost::asio::buffer(read_buffer, MAX_RESPONSE_SIZE),
boost::bind(
&serial_comm::data_received,
this, boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred
)
);
// start io_service run thread after giving it work
boost::thread t(boost::bind(&boost::asio::io_service::run, &io_service));
}
catch (const std::exception &e)
{
ERROR << "e.what() = " << e.what();
throw -1;
}
INFO << "ended";
return;
}
void serial_comm::data_received(const boost::system::error_code& error, size_t bytes_transferred)
{
boost::mutex::scoped_lock lock(mutex); // prevent multiple thread access
INFO << "started";
try
{
if (serial_port.get() == NULL || !serial_port->is_open())
{
WARNING << "serial port is not open";
throw -2;
}
if (error)
{
ERROR << "error.message() >> " << error.message().c_str();
throw -3;
}
for (unsigned int i = 0; i < bytes_transferred; ++i) {
serial_read_data += read_buffer[i];
}
INFO << "bytes_transferred = " << bytes_transferred << "; serial_read_data = " << serial_read_data;
serial_data_read_complete = true;
}
catch (const std::exception &e)
{
ERROR << "e.what() = " << e.what();
throw -1;
}
// prevent io_service from returning due to lack of work
serial_port->async_read_some(
boost::asio::buffer(read_buffer, MAX_RESPONSE_SIZE),
boost::bind(
&serial_comm::data_received,
this, boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred
)
);
INFO << "ended";
return;
}
main.cpp
#include "../include/serial_comm.hpp"
int main(void)
{
serial_comm _serial_comm;
try
{
_serial_comm.open_serial_port("/dev/ttyS0", 9600);
_serial_comm.async_read_some(); // this function will always check for data
loop:
while (!_serial_comm.serial_data_read_complete)
{
sleep(1);
}
INFO << "_serial_comm.serial_read_data = " << _serial_comm.serial_read_data;
_serial_comm.serial_read_data.clear();
_serial_comm.serial_data_read_complete = false;
goto loop;
}
catch (int error)
{
ERROR << "error >> " << error;
return;
}
FATAL << "main ended";
return;
}