以前、某所でC++による RS232C 通信ソフトをを紹介されてしまい(してしまい?)ました。
そのときには、単に、ソースの場所を開示しただけなのですが、さすがに、ソースを示しただけではちょっと問題かなと、少々解説も書いてみることにしました。
なお、このソフトは、ほとんど趣味で書かれたもので、無保証です。
動作については、一切の保証をいたしかねます。
間違っても、量産品に使ったりしないでください。
ただし、ソースを示していますので、変更はご自由に行っていただいて結構です。
MS-Windows 環境で、RS232C (というか、COM ポート)をコントロールするためのC++のソースです。
もともと、計測器などのコントロールを目的としており、行単位のコマンドを送信して、行単位のレスポンスを受信するという使い方を想定しています。
C++のクラスになっているので、複数のポートを独立してコントロールするような用途にも対応できます。
動作実績としては、Windows 98/2000/XP までは確認しています。
また、処理系としては、Borland の C++ Builder (5, 6, 2006) と、Micorosoft の Visual C++ 6 で動作したという話があります。
現在のところ、以下のような障害が確認されています。
いずれも、Borand C++ builder の(BDS の)2006 で発生しており、もしかしたら、処理系依存の障害かもしれません。
また、いずれも、USB で RS232C をエミュレートするタイプの機器で発生しております。
必要なファイルは、
winrs.h(ヘッダファイル)
winrs.cpp(プログラム本体)
ifline.h(インターフェイスクラスの定義ファイル)
の3つです。
このファイルは、もともと、RS232C だけではなく、同じ形で、GPIB をコントロールするために設計されており、GPIB のアクセスにあわせた形で、スーパークラスを作り、それから派生させた形になっています。
GPIB, RS232C のそれぞれに対応した計測器に対して、同じようにコントロールすることを目的にしています。
このため、コマンドの送信が、talk() だったり、レスポンスの受信が listen() だったりしています(いずれも、GPIB の用語です)
#include "winrs.h" .... WinRS *port = new WinRS(1, 9600, ifLine::cr, "8N1", false);
で、ポートをオープンします。
引数の指定は以下のようになります。
WinRS(int addr = 1, int bps = 9600, ifLine::delim delim = ifLine::crlf, char *mode = "8N1", bool rsFlow = false) throw(portDuplex);
不要になったら、
delete port;
で、ポートをクローズします。
前項で説明したオープンの成功可否判断(というか、WinRS のインスタンスが正常に生成されたかどうか)は、以下のようにチェックできます。
WinRS *port = new WinRS(1, 9600); if ((*port) 正常にオープンできたときの処理 else オープンに失敗したときの処理
または、
if (port->isValid()) 正常にオープンできたときの処理 else オープンに失敗したときの処理
いずれも内部的には同じことをやっています。
前項でオープンした port に対して、
port->talk("文字列");
で、指定した文字列+行ターミネータを送信します。
文字列は、'\0' でターミネートされた普通のC文字列です。
1バイト単位での送信は、
port->putc1(文字);
で実行可能です。
受信データの有無は、以下のようにチェックできます。
if (port->loc()) .... (受信データあり) if (! port->loc()) .... (受信データなし) while(! port->loc()); .... (受信データ発生するまで待つ・セミコロンの位置に注意)
なお、このパッケージの受信プログラムは、データがない場合、約6秒で、タイムアウトし、例外を発生します。
データ待ちが予想される場合、受信を行う前に、loc() でデータ有無を確認してください。
1行データの受信には2つの引数が必要です。
unsigned int len; char buff[16]; len = 16; port->listen(buff, len);
のように使用します。
listen() の実行時点の len の値が、受信できる最大長になります。
また、listen() の実行後、len には実際に受信したデータの数がセットされます。
これは、1行単位のデータ受信を行うもので、オープン時に指定したターミネータが現れるまで、データを受信します。
ただし、len で指定した数以上のデータは捨てられます。
また、受信データが存在しない場合、データ待ちとなりますが、約6秒でタイムアウトし、例外を送出します。
なお、データ長の設定は、参照型になっているので、port->listen(buff, 16) のような定数による指定は望ましくありません(が、受信したデータ数がかえってこないだけで、それ以上の実害はありません)
c = port->getc1();
で、1バイトのデータを受信できます。
このときも、受信データが存在しなければ、データ待ちとなり、約6秒でタイムアウトします。