Skip to content

Instantly share code, notes, and snippets.

@yoggy
Created August 11, 2012 10:58
Show Gist options
  • Save yoggy/3323808 to your computer and use it in GitHub Desktop.
Save yoggy/3323808 to your computer and use it in GitHub Desktop.
SerialPort class sample using boost::asio::serial_port
#include "stdafx.h"
#include "SerialPort.h"
int main(int argc, char* argv[])
{
bool rv;
SerialPort::print_devices();
std::string name = SerialPort::get_port_name(0);
SerialPort serial;
rv = serial.start(name.c_str(), 115200);
if (rv == false) {
return -1;
}
// initialize
serial.end_of_line_char(0x0d);
serial.write_some("BI010\r\n");
serial.write_some("PE011\r\n");
serial.write_some("RA01000\r\n");
// wait
Sleep(5 * 1000);
serial.stop();
return 0;
}
#include "StdAfx.h"
#include <Setupapi.h>
#pragma comment(lib, "Setupapi.lib")
#include "SerialPort.h"
SerialPort::SerialPort(void) : end_of_line_char_('\n')
{
}
SerialPort::~SerialPort(void)
{
stop();
}
char SerialPort::end_of_line_char() const
{
return this->end_of_line_char_;
}
void SerialPort::end_of_line_char(const char &c)
{
this->end_of_line_char_ = c;
}
std::vector<std::string> SerialPort::get_port_names()
{
std::vector<std::string> names;
BOOL rv;
DWORD size;
GUID guid[1];
HDEVINFO hdevinfo;
DWORD idx = 0;
SP_DEVINFO_DATA devinfo_data;
devinfo_data.cbSize = sizeof(SP_DEVINFO_DATA);
int count = 0;
rv = SetupDiClassGuidsFromName("Ports", (LPGUID)&guid, 1, &size) ;
if (!rv) {
std::cout << "error : SetupDiClassGuidsFromName() failed..." << std::endl;
return names;
}
hdevinfo = SetupDiGetClassDevs(&guid[0], NULL, NULL, DIGCF_PRESENT | DIGCF_PROFILE);
if (hdevinfo == INVALID_HANDLE_VALUE) {
std::cout << "error : SetupDiGetClassDevs() failed..." << std::endl;
return names;
}
while(SetupDiEnumDeviceInfo(hdevinfo, idx++, &devinfo_data)) {
char friendly_name[MAX_PATH];
char port_name[MAX_PATH];
DWORD prop_type;
DWORD type = REG_SZ;
HKEY hKey = NULL;
rv = ::SetupDiGetDeviceRegistryProperty(hdevinfo, &devinfo_data, SPDRP_FRIENDLYNAME, &prop_type,
(LPBYTE)friendly_name, sizeof(friendly_name), &size);
if (!rv) {
std::cout << "error : SetupDiGetDeviceRegistryProperty() failed..." << std::endl;
continue;
}
hKey = ::SetupDiOpenDevRegKey(hdevinfo, &devinfo_data, DICS_FLAG_GLOBAL, 0, DIREG_DEV, KEY_READ);
if (!hKey) continue;
size = sizeof(port_name);
rv = ::RegQueryValueEx(hKey, "PortName", 0, &type, (LPBYTE)&port_name, &size);
::RegCloseKey(hKey);
names.push_back(port_name);
}
SetupDiDestroyDeviceInfoList(hdevinfo);
return names;
}
int SerialPort::get_port_number()
{
std::vector<std::string> names = get_port_names();
return names.size();
}
std::string SerialPort::get_port_name(const unsigned int &idx)
{
std::vector<std::string> names = get_port_names();
if (idx >= names.size()) return std::string();
return names[idx];
}
void SerialPort::print_devices()
{
std::cout << "SerialPort::print_devices()" << std::endl;
int n = SerialPort::get_port_number();
for (int i = 0; i < n; ++i) {
std::string name = SerialPort::get_port_name(i);
std::cout << "\t" << name.c_str() << std::endl;
}
}
bool SerialPort::start(const char *com_port_name, int baud_rate)
{
boost::system::error_code ec;
if (port_) {
std::cout << "error : port is already opened..." << std::endl;
return false;
}
port_ = serial_port_ptr(new boost::asio::serial_port(io_service_));
port_->open(com_port_name, ec);
if (ec) {
std::cout << "error : port_->open() failed...com_port_name="
<< com_port_name << ", e=" << ec.message().c_str() << std::endl;
return false;
}
// option settings...
port_->set_option(boost::asio::serial_port_base::baud_rate(baud_rate));
port_->set_option(boost::asio::serial_port_base::character_size(8));
port_->set_option(boost::asio::serial_port_base::stop_bits(boost::asio::serial_port_base::stop_bits::one));
port_->set_option(boost::asio::serial_port_base::parity(boost::asio::serial_port_base::parity::none));
port_->set_option(boost::asio::serial_port_base::flow_control(boost::asio::serial_port_base::flow_control::none));
boost::thread t(boost::bind(&boost::asio::io_service::run, &io_service_));
async_read_some_();
return true;
}
void SerialPort::stop()
{
boost::mutex::scoped_lock look(mutex_);
if (port_) {
port_->cancel();
port_->close();
port_.reset();
}
io_service_.stop();
io_service_.reset();
}
int SerialPort::write_some(const std::string &buf)
{
return write_some(buf.c_str(), buf.size());
}
int SerialPort::write_some(const char *buf, const int &size)
{
boost::system::error_code ec;
if (!port_) return -1;
if (size == 0) return 0;
return port_->write_some(boost::asio::buffer(buf, size), ec);
}
void SerialPort::async_read_some_()
{
if (port_.get() == NULL || !port_->is_open()) return;
port_->async_read_some(
boost::asio::buffer(read_buf_raw_, SERIAL_PORT_READ_BUF_SIZE),
boost::bind(
&SerialPort::on_receive_,
this, boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
}
void SerialPort::on_receive_(const boost::system::error_code& ec, size_t bytes_transferred)
{
boost::mutex::scoped_lock look(mutex_);
if (port_.get() == NULL || !port_->is_open()) return;
if (ec) {
async_read_some_();
return;
}
for (unsigned int i = 0; i < bytes_transferred; ++i) {
char c = read_buf_raw_[i];
if (c == end_of_line_char_) {
this->on_receive_(read_buf_str_);
read_buf_str_.clear();
}
else {
read_buf_str_ += c;
}
}
async_read_some_();
}
void SerialPort::on_receive_(const std::string &data)
{
std::cout << "SerialPort::on_receive_() : " << data << std::endl;
}
#pragma once
#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 <vector>
typedef boost::shared_ptr<boost::asio::serial_port> serial_port_ptr;
#define SERIAL_PORT_READ_BUF_SIZE 256
class SerialPort
{
protected:
boost::asio::io_service io_service_;
serial_port_ptr port_;
boost::mutex mutex_;
char read_buf_raw_[SERIAL_PORT_READ_BUF_SIZE];
std::string read_buf_str_;
char end_of_line_char_;
private:
SerialPort(const SerialPort &p);
SerialPort &operator=(const SerialPort &p);
public:
SerialPort(void);
virtual ~SerialPort(void);
char end_of_line_char() const;
void end_of_line_char(const char &c);
virtual bool start(const char *com_port_name, int baud_rate=9600);
virtual void stop();
int write_some(const std::string &buf);
int write_some(const char *buf, const int &size);
static int get_port_number();
static std::string get_port_name(const unsigned int &idx);
static std::vector<std::string> get_port_names();
static void print_devices();
protected:
virtual void async_read_some_();
virtual void on_receive_(const boost::system::error_code& ec, size_t bytes_transferred);
virtual void on_receive_(const std::string &data);
};
@wlanowski
Copy link

Please use comments to show us, what your code is doing. thx.

@spinorkit
Copy link

spinorkit commented Apr 18, 2018

Hi, thanks for this example. I ran into a serious bug in start() which stopped it ever receiving data because io_service_.run() was returning before it was given any work to do. The fix is to call async_read_some_(); before creating the thread and calling run():

`bool SerialPort::start(const char *com_port_name, int baud_rate)
{
boost::system::error_code ec;

if (port_) {
	std::cout << "error : port is already opened..." << std::endl;
	return false;
}

port_ = serial_port_ptr(new boost::asio::serial_port(io_service_));
port_->open(com_port_name, ec);
if (ec) {
	std::cout << "error : port_->open() failed...com_port_name="
		<< com_port_name << ", e=" << ec.message().c_str() << std::endl; 
	return false;
}

// option settings...
port_->set_option(boost::asio::serial_port_base::baud_rate(baud_rate));
port_->set_option(boost::asio::serial_port_base::character_size(8));
port_->set_option(boost::asio::serial_port_base::stop_bits(boost::asio::serial_port_base::stop_bits::one));
port_->set_option(boost::asio::serial_port_base::parity(boost::asio::serial_port_base::parity::none));
port_->set_option(boost::asio::serial_port_base::flow_control(boost::asio::serial_port_base::flow_control::none));

async_read_some_();
std::thread t( [this] () {this->io_service_.run(); });
thread_.swap(t);

return true;

}

void SerialPort::stop()
{
std::unique_lock lock(mutex_);

if (port_) {
	port_->cancel();
	port_->close();
	port_.reset();
}

if (thread_.joinable())
thread_.join();

io_service_.stop();
io_service_.reset();

}
`

@jmccabe
Copy link

jmccabe commented Sep 9, 2019

Quick question on your reply. In this bit:

async_read_some_();
std::thread t( [this] () {this->io_service_.run(); });
thread_.swap(t);

then

if (thread_.joinable())
thread_.join();

Am I right in thinking that thread_ is a member variable of the class?

@ahmetserdar
Copy link

perfect!

@llallo
Copy link

llallo commented Aug 25, 2023

Thank you for this excelent example.
Just one minor suggestion: io_service_.restart() needs to be called in function SerialPort::start if there has been a previous call to SerialPort::stop, where io_service_.reset() is invoked. like this:

bool SerialPort::start(const char *com_port_name, int baud_rate)
{
	boost::system::error_code ec;

	if (port_) {
		std::cout << "error : port is already opened..." << std::endl;
		return false;
	}
        io_service_.restart();
	port_ = serial_port_ptr(new boost::asio::serial_port(io_service_));
	port_->open(com_port_name, ec);
        ...

otherwise, it stops working on subsequent calls to stop() / start().

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment