2017-08

スポンサーサイト

上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。

boost asio でシリアル通信

C++でシリアルポートを扱うのって面倒ですよね。
ってなわけで、Boosを使ってシリアル通信クラスを書いてみた。

ヘッダファイル

#pragma once

#include
#include
#include

namespace boost{ namespace system{ class error_code; }; };

namespace serial
{
/**
* @brief:シリアルポートのオプションを設定するためのEnumクラス群
*/
// ボーレート設定用
class BaudRate {
public:
enum {
B2400 = 2400,
B4800 = 4800,
B9600 = 9600,
B19200 = 19200,
B38400 = 38400,
B115200 = 115200
};
};

// パリティ設定用
class Parity {
public:
enum {
None=0,
Odd,
Even
};
};

// ストップビット設定用
class StopBits {
public:
enum {
One=0,
OnePointFive,
Two
};
};

// データのバイトサイズ設定用
class ByteSize{
public:
enum{
B7 = 7,
B8 = 8
};
};

// フローコントロール用の設定
class FlowControl{
public:
enum{
None=0,
Hardware,
Software
};
};

class SerialPort;
/**
/* @brief : observerを使ってコールバックを実装するためのインターフェースクラス
*/
class SerialObserver
{
friend SerialPort;
public:
SerialObserver(){}
virtual ~SerialObserver(){}

protected:
virtual void notify( const std::string& str )=0;

};


/**
* @brief : boostを使ったシリアル通信クラス
* イベントで動くようにしたのため
*  通信に余分なリソースをさく必要がなくなります。
*/
class SerialPort
{
// コンストラクタ、デストラクタ------------------------
public:
SerialPort();
virtual ~SerialPort();


// 操作------------------------------------------------
public:
/*
* @brief : ポートのオープン
* @param[in] : comポート
* @param[in] : 1バイトのビット数
* @param[in] : パリティを指定
* @param[in] : ストップビット指定
* @return : 成功判定
*/
bool open(
const std::string& com = "COM1",
int baudrate = BaudRate::B115200,
int bytesize = ByteSize::B8,
int parity = Parity::None,
int stopbits = StopBits::One,
int flowcontrol = FlowControl::None
);

/**
* @brief : ポートのクローズ
* @return : 成功判定
*/
bool close();

/**
* @brief : 文字列を送信する関数
* @param[in] : 送信する文字列
*/
bool send( const std::string& s );

/**
* @brief : 文字列を送信する関数
* @param[in] : 送信する文字
*/
bool send( char c );

/**
* @brief : 文字列を送信する関数
* @param[in] : 送信する文字列
* @param[in] : 文字列のサイズ
*/
bool send( const char *c, int size );

/**
* @brief : observerを追加する関数
* @param[in] : observerクラス
* @return : 成功判定
*/
bool attach( SerialObserver* ob );

/**
/* @brief : observerを削除する関数
/* @pram[in] : observerクラス
/* @return : 成功判定
*/
bool detach( SerialObserver* ob );

/**
/* @brief : データを受信する関数
/* @param[out] : 取得データ
/* @return : 残りのデータ数
*/
bool receive( std::string& str, double timeout );

/**
* @brief : 接続確認
* @return : 接続状況
*/
bool isConnect() const { return is_connect_; }



private:
// 更新関数
virtual void notifyAll( const std::string& str );

// データ書き込み
bool write( const char* str, int n );


// 属性------------------------------------------------
private:
class serial_impl;
boost::shared_ptr impl;

// 受信用バッファ
char rBuffer[1024];
int readSize;

void read_ok( const boost::system::error_code& e, size_t size );
bool is_connect_;

// 最新版の受信データ
std::string readData;
};

};


ソースファイル

#include
#include
#include
#include

#include
#include
#include
#include
#include
#include

#include "SerialPort.h"

using namespace serial;
using namespace boost::asio;
using namespace std;

///
/// boostのインターフェースを隠蔽するためのクラス
///

class SerialPort::serial_impl
{
// コンストラクタ、デストラクタ----------------------------
public:
serial_impl()
: serial_port( NULL )
, br( serial_port_base::baud_rate(9600) )
, cs( serial_port_base::character_size( 8 ) )
, fc( serial_port_base::flow_control::none )
, parity( serial_port_base::parity::none )
, sb( serial_port_base::stop_bits::one )
, com( "COM1" )
{}

virtual ~serial_impl()
{
if( serial_port ) delete serial_port; serial_port = NULL;
}


// 属性----------------------------------------------------
public:
// shared_ptr
std::vector< SerialObserver* > ptrList;

// thread
boost::thread ioThread;
boost::condition recv_condition;
boost::mutex recv_sync;

// シリアルの設定系統
io_service io;
std::string com;
serial_port *serial_port;
serial_port_base::baud_rate br;
serial_port_base::character_size cs;
serial_port_base::flow_control::type fc;
serial_port_base::parity::type parity;
serial_port_base::stop_bits::type sb;
};


/**
* @brief コンストラクタ
*/
SerialPort::SerialPort()
: impl( new serial_impl() )
, is_connect_( false )
{}


/**
* @brief : デストラクタ
*/
SerialPort::~SerialPort() {
close();
}


/**
* @brief : ポートのオープン
* @param[in] : comポート
* @param[in] : 1バイトのビット数
* @param[in] : パリティを指定
* @param[in] : ストップビット指定
* @return : 成功判定
*/
bool SerialPort::open(
const std::string &com,
int baudrate,
int cs,
int parity,
int stopbits,
int flow
)
{
if( is_connect_ ) return false;

boost::system::error_code ret;

// ポートのオープン
impl->serial_port = new serial_port( impl->io );
impl->serial_port->open( com, ret );

if( ret ){
std::cerr << "resial_port open() error " << ret << std::endl;
return false;
}

// 接続フラグ
is_connect_ = true;

// パリティ値の設定
serial_port_base::parity::type parity_t = serial_port_base::parity::none;
if( parity == Parity::Even ) parity_t = serial_port_base::parity::even;
else if( parity == Parity::Odd ) parity_t = serial_port_base::parity::odd;

// Stop Bists
serial_port_base::stop_bits::type stopbit_t = serial_port_base::stop_bits::one;
if( stopbits == StopBits::Two ) stopbit_t = serial_port_base::stop_bits::two;
else if( stopbits == StopBits::OnePointFive ) stopbit_t = serial_port_base::stop_bits::onepointfive;

// flow control
serial_port_base::flow_control::type flow_t = serial_port_base::flow_control::none;
if( flow == FlowControl::Hardware ) flow_t = serial_port_base::flow_control::hardware;
else if( flow == FlowControl::Software ) flow_t = serial_port_base::flow_control::software;

// 設定値の取得
impl->com = com;
impl->br = serial_port_base::baud_rate( baudrate );
impl->cs = serial_port_base::character_size( cs );
impl->parity = parity_t;
impl->sb = stopbit_t;
impl->fc = flow_t;

impl->serial_port->set_option( impl->br );
impl->serial_port->set_option( serial_port_base::parity(parity_t) );
impl->serial_port->set_option( serial_port_base::character_size( cs ) );
impl->serial_port->set_option( serial_port_base::stop_bits(stopbit_t) );
impl->serial_port->set_option( serial_port_base::flow_control(flow_t) );

// 読み込み用の関数を設定
impl->serial_port->async_read_some(
boost::asio::buffer(rBuffer, 1024),
boost::bind(&SerialPort::read_ok, this, _1, _2) );

// IOサービスの開始
impl->ioThread = boost::thread( boost::bind(&boost::asio::io_service::run, &impl->io) );

return true;
}


/**
* @brier : オブジェクトの登録を行う
* @param[in] : 登録を行うオブジェクト
* @return : 成功判定
*/
bool SerialPort::attach(SerialObserver *ob) {
std::vector::iterator it = std::find( impl->ptrList.begin(), impl->ptrList.end(), ob );

// 登録されていなかったら、オブザーバーを登録
if( it != impl->ptrList.end() ) return false;
impl->ptrList.push_back(ob);
return true;
}


/**
* @brier : オブジェクトの破棄を行う
* @param[in] : 破棄を行うオブジェクト
* @return : 成功判定
*/
bool SerialPort::detach(SerialObserver *ob) {
std::vector::iterator it = std::find( impl->ptrList.begin(), impl->ptrList.end(), ob );

// 登録されていなかったら、オブザーバーを登録
if( it == impl->ptrList.end() ) return false;
impl->ptrList.erase( it );
return true;
}


/**
* @brief : 状態の更新を通知する
* @param[in]: 受信文字列
*/
void SerialPort::notifyAll( const std::string& str ) {
// 全てのオブザーバーに通知
BOOST_FOREACH( SerialObserver* ob, impl->ptrList ) ob->notify(str);

// コンディション解除
boost::mutex::scoped_lock lk( impl->recv_sync );
readData = str;
impl->recv_condition.notify_all();
}


/**
* @brief : ポートのクローズ
* @return : 成功判定
*/
bool SerialPort::close()
{
if( !is_connect_ ) return false;
impl->recv_condition.notify_all();
impl->serial_port->close();
is_connect_ = false;
return true;
}


/*
* @brief : データリード
* @return : 成功判定
* @param[out]: 受信データ
* @param[in] : タイムアウト[ms]
*/
bool SerialPort::receive( std::string& str, double timeout ) {

// 接続判定
if( !is_connect_ ) return false;

boost::mutex::scoped_lock lk( impl->recv_sync );

// 受信待ち
boost::xtime xt;
boost::xtime_get(&xt, boost::TIME_UTC);
xt.nsec += static_cast(timeout*1000.0*1000.0);

// 受信待ち失敗
if( !impl->recv_condition.timed_wait(lk,xt) ) return false;

// 受信文字列を格納
str = this->readData;
return true;
}


/**
/* @brief : 文字列の送信関数
/* @return : 成功判定
*/
bool SerialPort::send( const std::string& s ) {
return write( s.c_str(), s.size() );
}

bool SerialPort::send( char c ) {
return write( &c, 1 );
}

bool SerialPort::send( const char* c, int size ) {
return write( c, size );
}

bool SerialPort::write( const char* str, int n ) {
boost::system::error_code ret;
if( !is_connect_ ) return false;

impl->serial_port->write_some( boost::asio::buffer( str, n ), ret );

if( ret ) {
std::cerr << "serial_port::write_some() return = " << ret << std::endl;
return false;
}

return true;
}

void SerialPort::read_ok( const boost::system::error_code& e, size_t size ) {
if( e ) {
std::cerr << "read_some() Error = " << e << std::endl;
return;
}

// 受信処理
std::string str( rBuffer, rBuffer+size );
//readQue.push_back( str );

// 更新処理
notifyAll( str );

// 読み込みが完了したので、再度設定
impl->serial_port->async_read_some(
boost::asio::buffer(rBuffer, 1024),
boost::bind(&SerialPort::read_ok, this, _1, _2) );
}


送受信サンプル

#include
#include
#include "SerialPort.h"

int main() {
serial::SerialPort send;
serial::SerialPort recv;

// ポートオープン
recv.open( "COM3", serial::BaudRate::B115200 );
send.open( "COM1", serial::BaudRate::B115200 );

std::string str;
for(;;) {
send.send( "test\n" );

// タイムアウト1秒でデータ取得
if( recv.receive( str, 1000 ) ) {
std::cerr << str << std::endl;
} else {
std::cerr << "receive error" << std::endl;
}
}

return 0;
}


非同期通信サンプル
boost使ってコールバックを作るのがめんどくさかったので、
オブザーバーパターンで作成。

#include
#include
#include "SerialPort.h"

using namespace serial;

class Observer : public SerialObserver
{
public:
Observer(){}
virtual ~Observer()
{}

protected:
void notify( const std::string& str ) {
std::cerr << str << std::endl;
}
};


int main()
{
serial::SerialPort send;
serial::SerialPort recv;

recv.open( "COM3", serial::BaudRate::B115200 );
send.open( "COM1", serial::BaudRate::B115200 );

// オブザーバー登録
Observer ob;
recv.attach( &ob );

// データ送信
for( int i=0; i<100; ++i ) {
send.send("tset\n");
}

// オブザーバー登録解除
recv.detach( &ob );

return 0;
}


非同期も簡単にできるようになりました。
これでC++で簡単にシリアルポートを使える。

● COMMENT FORM ●

承認待ちコメント

このコメントは管理者の承認待ちです


管理者にだけ表示を許可する

トラックバック

http://chicklab.blog84.fc2.com/tb.php/29-bd86a112
この記事にトラックバックする(FC2ブログユーザー)

NEW ENTRY «  | BLOG TOP |  » OLD ENTRY

カレンダー

07 | 2017/08 | 09
- - 1 2 3 4 5
6 7 8 9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30 31 - -

FC2カウンター

最新記事

最新コメント

最新トラックバック

月別アーカイブ

カテゴリ

未分類 (7)
日記 (7)
wiiremote (1)
google (1)
回路 (3)
psp (1)
programing (15)
linux (0)
OpenWRT (3)
kinect (1)
Raspberry Pi (8)

検索フォーム

RSSリンクの表示

リンク

このブログをリンクに追加する

google adsense

上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。