プロが教えるわが家の防犯対策術!

初めて投稿します。

現在卒業研究で、RS232cで接続された2次元センサ(カメラで対象物をトラッキングし、2次元座標データをPCに送る計測器)のデータをPC(OSはXP)で読み込もうとしています。
以下のサイトを見つけ、
http://7ujm.net/C++/Rs232c.h.html
ソースファイルとヘッダーファイルからなるプログラムをつくり(プログラムの変更点は最後に明記しました)
、VC++とBORLANDC++で試してみたのですが
VC++ではプロジェクトのビルド時に
「fatal error C1010: プリコンパイル済みのヘッダーの検索中に予期しないEOFを検出しました。」
とエラーが出てしまいます。
_tmain()関数の引数を無記入にしてみてもエラーの内容は変わりません
でした。

また、BORLANDC++ではコンパイル時に
「エラー E2268 RS232.cpp 11: 未定義の関数'printf'を呼び出した(関数 tmai())」
「エラー E2268 RS232.cpp 13: 未定義の関数'gethar'を呼び出した(関数 tmai())」
となってしまい、ヘッダーファイルのみをコンパイルしても
「エラー E2141 RS232c.h 10:宣言の構文エラー」
というエラーが生じてしまいます。

念のためこちらも
http://okwave.jp/qa733184.html
参考に、BORLANDの設定(bcc32.cfg ファイルの確認など、最初の設定)も確認しましたが、独習Cなどの教本に載っているプログラムでは問題なく動作します。

なお計測機器のボーレートは115200bps、ターミネータはcr(キャリッジリターン)のみで(lf(ラインフィード)が入ってもかまわない)、送信コマンドはASCII文字となっています。

ハイパーターミナル、MATLABのM-fileでの通信は確認済みです。

もしこのような環境でRS232c通信が利用可能なC(またはC++)プログラムをご存知の方がいらっしゃれば、教えていただけないでしょうか?
よろしくお願いします。


「ソースファイル」
#include "RS232c.h" //このヘッダーを取り込みます。

int _tmain()
{
RS232c rs;
rs.Connect();
rs.Send("POS#?,,1 CR/LF");//文字列を送信 Send(送信文字列)
char w[100];
rs.Read(w,100); //文字列の受信 Read(char配列,読み込む文字数)
printf(w);
getchar();
return 0;
}

「ヘッダーファイル」
#ifndef _RS_232C_H_
#define _RS_232C_H_

#if _MSC_VER > 1000
#pragma once
#endif

#include <windows.h>
<中略>
bool RS232c::Connect(char* PortNmae="COM1",
int BaudRate = 115200,
int ByteSize = 8,
int Parity = NOPARITY,
int StopBits = ONESTOPBIT,
int RTS = RTS_CONTROL_DISABLE,
int DTR = DTR_CONTROL_DISABLE,
int ReadTimeOut =700,
int WriteTimeOut = 700
)
<中略>
{
RS232c::~RS232c(){
//ポートを閉じます
CloseHandle(m_hComm);
}
#endif // _RS_232C_H_

A 回答 (9件)

> の値を変更すると変わってくるので、ターミネータはうまく機能していないのかと思うのですが、



そのようですね。

また、WinRS のほうの 「Abnormal program termination」は、おそらくタイムアウトだと思います。

これから推定すると、計測器からは(crlf ではなく)cr しか帰ってきてないのではと思います。
WinRS *port = new WinRS(1, 115200, ifLine::cr, "8N1", false);
(3つめのパラメータが、ifLine::crlf → ifLine::cr に変更。これで、cr を受け取った時点で帰ってきます)

あるいは、port->talk("POS#?,,1 \0"); のレスポンスはすぐに帰ってくるのでしょうか?
port->listen() は、これを呼び出したときに受信データがないと、約6秒でタイムアウトします。
もしもレスポンスに時間がかかるのであれば、

port->talk("POS#?,,1");
while(! port->loc()); // レスポンス受信まで待つ
port->listen(buff, len);
としてみて下さい。

また、port->listen() の len は、呼び出し時には、受信できる最大バイト数を、返り時には、実際に受信したデータ数を持っています。
このため、talk() を複数行う場合は、
len = 1024;
port->talk("POS#?,,1");
のように毎回 len を設定して下さい。
これがないと、前回の受信バイト数をもって listen() を呼び出すので、前回のレスポンスより長いレスポンスが受け取れなくなります。

最後に、"POS#?,,1 \0" のような、最後の "\0" は不要です。
(あっても無害ですが)
"~" で囲まれた文字列の終端には、暗黙のうちに、"\0" が付加されますので。
    • good
    • 0
この回答へのお礼

お返事ありがとうございます。

シリアルポートのパラメータを
ifLine::crlf → ifLine::cr に変更した時点で、
レスポンスに時間がかからず受信データが送られてきました。
今後レスポンスに時間がかかるようになった場合は、教えていただいたwhile文を使いたいと思います。

また、talk() を複数行う場合は上記のことを気をつけて行いたいと思います。

>最後に、"POS#?,,1 \0" のような、最後の "\0" は不要です。
初歩的なご指摘までしていただきお恥ずかしい限りです。

ご丁寧なご説明のおかげで解決することができました。
本日は1日がかりで遅くまでご指導いただき、本当にありがとうございます。

お礼日時:2007/12/26 19:50

WinRS のほうは、unsigned int len;


の宣言を確認して下さい。
const char len; のままだとまずいです。
    • good
    • 0
この回答へのお礼

こちらはご指摘いただいた後すぐに修正したので、unsigned int len;と宣言しています。

お礼日時:2007/12/26 19:36

http://7ujm.net/C++/Rs232c.h.html
のソースであれば、
rs.Send("POS#?,,1 CR/LF");
が間違いです。CR/LF は、制御コードなので、
rs.Send("POS#?,,1\x0d\x0a");
で crlf つきの送付になります。(\x0d = cr \x0a = lf)
また、受信側も
rs.Read(w,100); ではなくて、
rs.Read_CRLF(w, 100); の方を使ってみて下さい。

この回答への補足

お返事ありがとうございます。

こちらのプログラムはご指導いただいた通りに変更したところ、目的のデータが返送されました!
本当に助かりました!

ただ、データ返送時間が
int ReadTimeOut =700,
int WriteTimeOut = 700
の値を変更すると変わってくるので、ターミネータはうまく機能していないのかと思うのですが、このような考えは誤った解釈なのでしょうか?

補足日時:2007/12/26 18:26
    • good
    • 0

> std::printf(len);


ここが間違っています。レスポンスは、buff に帰ってきており、len は帰ってきたバイト数です。
ですので、
std::printf(buff);
で表示できるはずです。
len は、unsigned int len; で正解です。
std::printf("mess = %s, and len = %d", buff, len);
とすれば、帰ってきたレスポンスと、その長さが表示されるでしょう。

あと、getchar() も、確かに、cstdio にありますから、std::getchar(); とすればOKです。
fscanf() はファイルからの読み出しですが?
std::scanf(); ならコンソールから読んでくれるはずです。

コンソールで画面を消さないためには、std::scanf("%s", buff);
をおけば、a (何か文字)+リターンキーを押すまで止まってくれるかなと思います。

この回答への補足

お返事ありがとうございます。ご指導いただいたとおりに試みたつもりですが、
以下の様な警告とエラーがでました。


警告 W8030 winrs.cpp 19: 'len' パラメータ(WinRS::listen(char *,unsigned int &)
)のために一時変数を使用する(関数 main() )
エラー E2272 winrs.cpp 21: 識別子が必要(関数 main() )
エラー E2379 winrs.cpp 21: ステートメントにセミコロン(;)がない(関数 main() )
*** 2 errors in Compile ***

>port->listen(buff, len);
19行目は上のようなプログラムで警告は出ますが、プログラムは動きました。

>getchar() も、確かに、cstdio にありますから、std::getchar(); とすればOKです。
上記の関数をコピー&ペーストでなく打ち込んでも以下の様なエラーが出てしまいます。
ですが、教えていただいたstd::scanf("%s", buff);を使えばコンパイルができました。


おかげさまでテストプログラムは動作し、目的のASCII文字も返ってきました。
ありがとうございます!

しかし、_tmain()関数内で
port->talk("DPW,,12,10 \0");//計測窓を計測器モニタに表示
port->talk("TRW,,12,10 \0");//計測窓を計測器モニタの対象物に追跡
port->talk("POS#?,,1 \0");//ある時刻の座標を1回表示
のようなコマンドを送信すると2行目のコマンドまでは認識するのですが、
下のような返信が帰ってきてしまいます。
「Abnormal program termination」
上記のターミネータの表記がまずいのでしょうか?

補足日時:2007/12/26 18:19
    • good
    • 0

No.3 です。

本題とは直接関係ないですが

#include "winrs.h"
#include "string.h"
#include <cstdio>
#include <cstring>
#include <windows.h>
#include <stdio.h>//2つのヘッダファイルを
#include "ifline.h"//追加しインクルード

ここで、
#include <stdio.h> と
#include <cstdio> は、
実質的に同じものをインクルードしています。
これは、C++の namespace の関連で導入されたもので、

#include <cstdio>
であれば、
printf() ではなくて、std::printf() でOKです。

あと、#include "string.h" も、Cの標準ヘッダであれば、
#icnlude <cstring> と同じものです。
こちらは、strcmp() strcpy() などのかわりに、std::strcmp() や std::strcpy() を使います。

この回答への補足

お返事ありがとうございます。

また、<stdio.h>ヘッダを<cstdio>ヘッダに変えるとgetchar関数が以下の様なエラーになってしまい、std::fscanf関数などにしてもエラーが消えませんでした。

エラー E2451 winrs.cpp 18: 未定義のシンボル _streams(関数 main() )
エラー E2268 winrs.cpp 18: 未定義の関数 '_fgetc' を呼び出した(関数 main() )

初歩的な質問で申し訳ありませんが、<cstdio>ヘッダで実行ファイルを走らせた後コンソール画面を表示させたままにさせるにはどのような関数を使えばいいのでしょうか?

補足日時:2007/12/26 16:49
    • good
    • 0

No.3 です。



それぞれ、
「error C2065: 'ort' : 定義されていない識別子です。」
port が間違って、 ort になってしまっています。

「error C2065: 'buff' : 定義されていない識別子です。」
char buff[1024]; が宣言されていません。
これは _tmain() の中で(というか、RS232C からデータを受けるところで)必要です。

「error C2065: 'len' : 定義されていない識別子です。」
unsigned int len = 1024; が宣言されていません。
これも、実際に受信をするところで必要です。

「error C2227: '->listen' : 左側がクラス、構造体、共用体へのポインタではありません。」
ort と同じで、port->listen() が ort->listen() になっています。

こんなところかと思います。

あと、最初のテストでは、RS232C 経由の送信は単純に、
port->talk("送信するデータ");
のほうがわかりやすいかと思います。

この回答への補足

お返事ありがとうございます。
初歩的なことを質問してしまい、申し訳ありません。
ご回答を元に以下の様な_tmain()関数を作成しコンパイルしてみました。

int _tmain()
{
WinRS *port = new WinRS(1, 115200, ifLine::crlf, "8N1", false);
char message[1024];
char buff[1024];
unsigned int len = 1024;
port->talk("TEST?,RS");
port->listen(buff, len);
std::printf(len);
getchar();
}

"TEST?,RS"というコマンドは今回使用している計測機器の通信試験を行うコマンドで、
通信ができていれば

"RS cr lf"

と返送してきます。
しかし

C:\BORLAND\SRC>bcc32 winrs.cpp
Borland C++ 5.5.1 for Win32 Copyright (c) 1993, 2000 Borland
winrs.cpp:
エラー E2034 winrs.cpp 17: 'unsigned int' 型は 'const char *' 型に変換できない(
関数 main() )
エラー E2342 winrs.cpp 17: パラメータ '__format' は const char * 型として定義さ
れているので unsigned int は渡せない(関数 main() )
エラー E2451 winrs.cpp 18: 未定義のシンボル _streams(関数 main() )
エラー E2268 winrs.cpp 18: 未定義の関数 '_fgetc' を呼び出した(関数 main() )
*** 4 errors in Compile ***

というエラーが出たため、
unsigned int len = 1024;
         ↓
const char *len = 1024;
としましたが、以下の様なエラーが出てしまったため、

C:\BORLAND\SRC>bcc32 winrs.cpp
Borland C++ 5.5.1 for Win32 Copyright (c) 1993, 2000 Borland
winrs.cpp:
エラー E2034 winrs.cpp 15: 'int' 型は 'const char *' 型に変換できない(関数 main(
) )
警告 W8030 winrs.cpp 17: 'len' パラメータ(WinRS::listen(char *,unsigned int &)
)のために一時変数を使用する(関数 main() )
エラー E2034 winrs.cpp 17: 'const char *' 型は 'unsigned int' 型に変換できない(
関数 main() )
エラー E2342 winrs.cpp 17: パラメータ 'len' は unsigned int & 型として定義されて
いるので const char * は渡せない(関数 main() )
*** 3 errors in Compile ***

const char *len = 1024;
         ↓
const char len = 1024;
のように変えたところ、以下の様な警告は出ましたがコンパイルはできました。

警告 W8030 winrs.cpp 17: 'len' パラメータ(WinRS::listen(char *,unsigned int &)
)のために一時変数を使用する(関数 main() )
Turbo Incremental Link 5.00 Copyright (c) 1997, 2000 Borland

そこで作成された実行ファイルを走らせてみたのですが、コンソール画面が表示されるのみで
データの取得はできませんでした。
equinox2さんやtanma3さんのおかげでコンパイルはできるようになった
http://7ujm.net/C++/Rs232c.h.html
のプログラムでも同様の状態になってしまうため、

これら2つのプログラムはDTE(データ端末装置)間の通信プログラムになっており、
リバースケーブルで結線された計測装置との通信プログラムになってないのではないかという疑問を持ちました。
このような疑問は見当違いなものなのでしょうか?

私ではプログラムから読み取ることができませんので、お手数ですがご確認お願いできないでしょうか。
よろしくお願いします。

補足日時:2007/12/26 17:09
    • good
    • 0

無保証ですが。




http://www.nest4.net/tec/ifline.h
http://www.nest4.net/tec/winrs.h
http://www.nest4.net/tec/winrs.cpp

なんかいかがでしょうか?

#include "winrs.h"

でインクルードすると、WinRS というクラスが使えるようになります。
詳細は、WinRS.h を読んで、解読してください。

一応、
WinRS *port = new WinRS(1, 115200, ifLine::crlf, "8N1", false);
で、COM1 を 115200bps, データ区切りは、crlf, 8bit non parity, stop 1,
フロー制御無しとして開きます。

これで、

char message[1024];
port->talk(message);
とすれば、message('\0' でターミネートされているもの)の 内容に、勝手に crlf
を付加して送信します。

char buff[1024];
unsigned int len = 1024; // これは、受信できる「最大」バイト数の設定
を使って、
port->listen(buff, len);
とすると、buff のなかに、crlf までのデータを(crlf をはじいて)持って帰ってきます。
帰ったときには、len の中に、実際に取得したバイト数が入っています。

あと、受信データの有無は、port->loc() が0か非ゼロかで判断できます
(0のとき受信データなし)
受信データがないときに、listen()を呼ぶと、6秒くらいでタイムアウトして例外を
送出します。

あと、もしかしたら、最近の USB タイプのシリアル変換コネクタとは相性が
良くないです。
一応、

port = new WinRS(1, 115200, ifLine::crlf, "8N1", false);
delete port;
port = new WinRS(1, 115200, ifLine::crlf, "8N1", false);
と1度 new して delete すると、その後動くようです。

この回答への補足

お返事ありがとうございます。

早速ヘッダファイル2つとソースファイル1つをダウンロードし、
VC++.NETで試してみましたが、プリプロセッサの定義が

「error C2065: 'ort' : 定義されていない識別子です。」
「error C2065: 'buff' : 定義されていない識別子です。」
「error C2065: 'len' : 定義されていない識別子です。」
「error C2227: '->listen' : 左側がクラス、構造体、共用体へのポインタではありません。」

というエラーが出てきてしまい、BORLAND C++で試しても上の3つの
エラーは残ってしまいます。

以下が、
winrs.cpp​
の先頭部分を書き換えたものです。

winrs.cpp​と別にソースファイルを作らなかったのは、
この後でMATLAB、SIMLINKのC-MEX S-Functionに組み込む際
ヘッダーファイルもまとめて、1つのファイルにしたかった
ためです。

#include "winrs.h"
というヘッダーファイルもインクルードしていてもエラーが出てしまうということは、このプログラムでは上記のような使い方はできないのでしょうか?

#include "winrs.h"
#include "string.h"
#include <cstdio>
#include <cstring>
#include <windows.h>
#include <stdio.h>//2つのヘッダファイルを
#include "ifline.h"//追加しインクルード

int _tmain()
{
WinRS *port = new WinRS(1, 115200, ifLine::crlf, "8N1", false);
char message[1024];
port->talk(message);
ort->listen(buff, len);
printf(len);
getchar();
return 0;
}
<以下winrs.cpp​と同じ>

補足日時:2007/12/26 13:03
    • good
    • 0

>VC++ではプロジェクトのビルド時に


>「fatal error C1010: プリコンパイル済みのヘッダーの検索中に予期>しないEOFを検出しました。」
>とエラーが出てしまいます。
上記の理由についてですが、たぶん使用しているのはVC++6.0ですよね。

理由としては、VCは stdafx.hファイルをコンパイル時間を短縮するため
あらかじめコンパイルしておくような設定になっていて、それをプリコンパイルヘッダーと呼びます。
stdafx.h がプリコンパイルヘッダーとして指定されていて、それがインクルードされてないですよ。といって怒ってます。

ですのでstdafx.h をインクルードしてやってください。

プリコンパイルヘッダーを使用しないように設定を変更するのもアリです。
その場合は、ファイルビューでファイル右クリックして設定を選んでください。ファイル毎にプリコンパイル済みヘッダを使うかどうかの指定ができるはずです。
<設定→C/C++→プリコンパイル済みヘッダ→プリコンパイルヘッダを使用しない>
とすればstdafx.hが無くてもコンパイルできるようになります。
    • good
    • 0
この回答へのお礼

早速のお返事、ありがとうございます。

VC++のバージョンが抜けていました、すみません。
使用しているのはVC++.NETですが、
「プリコンパイル済みヘッダを使用しない」設定したら、
ビルドはできました。

初心者にも分かりやすいアドバイスで助かりました。

お礼日時:2007/12/26 11:31

(1)printfやgetcharを使うなら、stdio.hをincludeしましょう。



(2)RS232c.hの10行目はどうなっていますか。
#肝心なところは省略しないでください

この回答への補足

早速のお返事ありがとうございます。

(1)
あまりにも基本的なミスですみません。
無事コンパイルできました。

(2)
ヘッダファイルの10行目は以下のクラス定義の最初の行になっています。

class RS232c{
private:
HANDLE m_hComm;
DWORD size;
DCBm_Dcb;
public:
//各種パラメーターの設定後に接続します。 返り値 TRUE = 成功 FALSE = 失敗
bool Connect(char* PortNmae, //ポート名を指定します COM1 COM2など、初期値はCOM1
int BaudRate,//ボーレートを指定します。初期値は 9600
int ByteSize,//1バイトのビット数を指定します。初期値は 8
int Parity,//パリティを指定します。パリティなし: NOPARITY
//偶数パリティ: EVENPARITY
//奇数パリティ: ODDPARITY
//初期値は、パリティなし: NOPARITY
int StopBits,//ストップビット数を指定します。
//1ビット: ONESTOPBIT
//1.5ビット: ONE5STOPBITS
//2ビット: TWOSTOPBITS
//初期値は 1ビット: ONESTOPBIT
int RTS,//RTSをON=RTS_CONTROL_ENABLE 初期値は無効です
int DTR,//DTRをON=DTR_CONTROL_ENABLE 初期値は無効です
int ReadTimeOut,//受信時のタイムアウト msで指定 初期値は 5000ms
int WriteTimeOut//送信時のタイムアウト msで指定 初期値は 20000ms
);
//文字列の受信 Read(char配列,読み込む文字数) 返り値 TRUE = 成功 FALSE = 失敗
bool Read(char* Buff,int NumberOfCharactersToRead);
//文字列の受信 Read_CRLF(char配列,バッファーサイズ) 返り値 TRUE = 成功 FALSE = 失敗
//CRLFをキャッチしたときに受信完了します、読み込み文字列がバッファーサイズをあふれた場合は
//その時点までの文字列しか受信できません
bool Read_CRLF(char* Buff,int NumberOfCharactersToRead);
//文字列を送信 Send(送信文字列) 返り値 TRUE = 成功 FALSE = 失敗
bool Send(char* word);
//接続の状態を取得します
bool RS232c::isLink();
//デストラクタ ポートを閉じます
~RS232c();
};

補足日時:2007/12/26 11:36
    • good
    • 0

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!