Created
August 11, 2012 10:58
-
-
Save yoggy/3323808 to your computer and use it in GitHub Desktop.
SerialPort class sample using boost::asio::serial_port
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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; | |
} | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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); | |
}; |
perfect!
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
Quick question on your reply. In this bit:
then
Am I right in thinking that thread_ is a member variable of the class?