ラトックシステムの REX-WF60 (Wi-Fi -> RS-232C 変換アダプター) を使ってみた (2016/10/24 update)
※2016/10/24 文字列書き込み、読み込みコードを追加しました。
・iOS, Android, Windows から Wi-Fi 経由で、RS-232C 通信が行える REX-WF60 を使って、「電子天びん」のデータ受信、PLC
のデータ送信、受信を行ってみました。
同じようにワイヤレスで RS-232C 機器が使える REX-BT60 (Bluetooth -> RS-232C 変換アダプター)は、iOS には対応していませんが、
こちらは、iOS にも対応しています。
・iOS, Android, Windows とも、アクセスポイントモード(WF60 と直接通信する)と、インフラストラクチャーモード(アクセスポイントを経由して通信を行う)に対応しています。
※アクセスポイントモードでは、WF60との通信専用になり、インターネット、他のPCとの通信等は行えなくなります。
※こちらの環境 (Windows)では、簡単設定ツールで WF60 が検出できず、設定時のみファイアーウォールを一時的に無効にする必要がありました。
iPhone の場合は、そのままでOKでした。
※Wi-Fi エリアが重複している場合、手動で Wi-Fi の接続先を切り換える必要があります。
シリアル通信(仮想 COM ポート)は、ComPortLibrary。TCP 通信は、IndyTCPClient コンポーネントを使用しています。
※仮想 COM ポートは、Windows 用で、PC ごとにインストールが必要です。また、通常の RS-232C 通信プログラム同様の通信設定が必要になります。
TCP 通信の場合は、WF60 の IP アドレスとポートを指定するだけになり、プログラムが簡単になります。
■「電子天びん」のデータを受信してみました。
接続ケーブルは、ストレート(SD, RD, SG のみ)を使用。
"*HELLO*"以降が、「電子天びん」からの受信データです。
■PLCと通信してみました。
PLCのデータを読み書きするために、シリアル通信の設定を WF60 に合わせます。
※設定変更、書き込み後は、PLC をリセットする必要があります。←たまに、忘れます。
接続ケーブルは、クロスケーブル(SD, RD, SG のみ)を使用。
PLC への送信コマンド、レスポンスの文字列は、TCP 接続であっても通常のシリアル通信とまったく同じです。
// IOHandler.WriteLnRFC() を IOHandler.WriteLn() に変更しました (2017/ 1/ 6) unit WF60Unit; interface uses Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, CPort, IdBaseComponent, IdComponent, IdTCPConnection, IdTCPClient; type TForm3 = class(TForm) ComPort1: TComPort; Button1: TButton; IdTCPClient1: TIdTCPClient; Button2: TButton; Edit1: TEdit; Edit2: TEdit; Button3: TButton; Button4: TButton; procedure Button1Click(Sender: TObject); procedure Button2Click(Sender: TObject); procedure Button3Click(Sender: TObject); procedure Button4Click(Sender: TObject); private { Private 宣言 } public { Public 宣言 } end; var Form3: TForm3; implementation {$R *.dfm} // REX-WF60 へは、アクセスポイントモードで接続 // 事前に Wi-Fi を切り替えておく必要がある // 仮想 COM ポート で「電子天びん」のデータを受信 // ストレートケーブルで接続(接続は、SD, RD, SG のみでOK) procedure TForm3.Button1Click(Sender: TObject); var s:string; begin ComPort1.Port := 'COM8'; ComPort1.BaudRate := br9600; ComPort1.DataBits := dbEight; ComPort1.StopBits := sbOneStopBit; ComPort1.Parity.Bits := prNone; ComPort1.Timeouts.ReadInterval := 100; // デフォルト=-1; ComPort1.Timeouts.WriteTotalConstant := 500; ComPort1.Timeouts.ReadTotalConstant := 500; // オープン ComPort1.Open; // 実際の機器に接続されているかは、不明 if Comport1.Connected then begin // 受信 // 受信文字列:'*HELLO*'で始まる文字列(異常であっても) ComPort1.ReadStr(s, 256); Edit1.Text := s; end; // クローズ ComPort1.Close; end; // TCP で「電子天びん」のデータを受信 // ストレートケーブルで接続(接続は、SD, RD, SG のみでOK) procedure TForm3.Button2Click(Sender: TObject); begin IdTCPClient1.Host := '1.2.3.4'; IdTCPClient1.Port := 2000; IdTCPClient1.ReadTimeout := 500; // 接続 IdTCPClient1.Connect; if IdTCPClient1.Connected then begin // 受信 // 受信異常:''; // 受信正常:'*HELLO*' で始まる文字列 Edit2.Text := IdTCPClient1.IOHandler.ReadLn() ; end; // 切断 IdTCPClient1.Disconnect; end; // 仮想 COM ポート で PLC(FX3S + FX3G-232-BD) 送受信 // クロスケーブルで接続(接続は、SD, RD, SG のみでOK) procedure TForm3.Button3Click(Sender: TObject); const STX = #$02; // テキスト開始 ETX = #$03; // テキスト終了 EOT = #$04; // テキスト終結 ENQ = #$05; // 問合せ ACK = #$06; // 肯定応答 CR = #$0D; // キャリッジリターン LF = #$0A; // 改行 CL = #$0C; // クリア NAK = #$15; // 否定応答 var cmd, res : string; Ticks : Cardinal; begin // MC プロトコル形式4(CR,LF あり) cmd := ENQ; cmd := cmd + '00'; // 局番号(PLC側の設定による) cmd := cmd + 'FF'; // PC番号(FXの場合、固定) cmd := cmd + 'BR'; // コマンド(ビット読み出し) cmd := cmd + '0'; // 伝文ウェイト('0'~'F' = 0~150ms) cmd := cmd + 'X0000'; // 5桁で指示:X0000から cmd := cmd + '16'; // 16個読み出し cmd := cmd + CR + LF; Edit1.Text := cmd; Edit2.Text := ''; ComPort1.Port := 'COM8'; ComPort1.BaudRate := br9600; ComPort1.DataBits := dbEight; ComPort1.StopBits := sbOneStopBit; ComPort1.Parity.Bits := prNone; ComPort1.Timeouts.ReadInterval := 100; // デフォルト=-1; ComPort1.Timeouts.WriteTotalConstant := 500; ComPort1.Timeouts.ReadTotalConstant := 500; Ticks := GetTickCount; // オープン ComPort1.Open; // 実際の機器に接続されているかは、不明 if Comport1.Connected then begin // コマンド送信 Comport1.WriteStr(cmd); // レスポンス受信 // 受信文字列:'*HELLO*'で始まる文字列(異常であっても) ComPort1.ReadStr(res, 1024); Edit2.Text := res; end; // クローズ ComPort1.Close; Caption := 'COM:' + IntToStr(GetTickCount()-Ticks); end; // TCP でPLC(FX3S + FX3G-232-BD) 送受信 // クロスケーブルで接続(接続は、SD, RD, SG のみでOK) procedure TForm3.Button4Click(Sender: TObject); const STX = #$02; // テキスト開始 ETX = #$03; // テキスト終了 EOT = #$04; // テキスト終結 ENQ = #$05; // 問合せ ACK = #$06; // 肯定応答 CR = #$0D; // キャリッジリターン LF = #$0A; // 改行 CL = #$0C; // クリア NAK = #$15; // 否定応答 var cmd, res : string; Ticks : Cardinal; begin // MC プロトコル形式4(CR,LF あり) cmd := ENQ; cmd := cmd + '00'; // 局番号(PLC側の設定による) cmd := cmd + 'FF'; // PC番号(FXの場合、固定) cmd := cmd + 'BR'; // コマンド(ビット読み出し) cmd := cmd + '0'; // 伝文ウェイト('0'~'F' = 0~150ms) cmd := cmd + 'X0000'; // 5桁で指示:X0000から cmd := cmd + '16'; // 16個読み出し cmd := cmd + CR + LF; Edit1.Text := cmd; Edit2.Text := ''; // アクセスポイントモードの時、IP アドレスは WF60 から自動で割り付けられる IdTCPClient1.Host := '1.2.3.4'; // ポート:2000 固定 IdTCPClient1.Port := 2000; IdTCPClient1.ReadTimeout := 500; Ticks := GetTickCount; // 接続 IdTCPClient1.Connect; if IdTCPClient1.Connected then begin // コマンド送信 IdTCPClient1.IOHandler.WriteLn(cmd); // while IdTCPClient1.IOHandler.CheckForDataOnSource(10) do ; // レスポンス受信 // 受信異常:''; // 受信正常:'*HELLO*' で始まる文字列 res := IdTCPClient1.IOHandler.ReadLn() ; Edit2.Text := res; end; // 切断 IdTCPClient1.Disconnect; Caption := 'TCP:' + IntToStr(GetTickCount()-Ticks); end; end. // ***************************************************************************** // 以下、2016/10/24追加分 REX-WF60 TCP->FX3S RS232C 交信 // ***************************************************************************** // REX-WF60 ワードデバイス 文字列書き込み // 1ワード= 16ビット= 2バイト= 2 文字 procedure TForm1.Button10Click(Sender: TObject); var cmd, res : string; strTExt : string; srcText : string; sndText : string; sndLen : integer; i : integer; begin // 転送文字列 srcText := Edit11.Text;//'ABCDEFG'; // 文字列をASCIIコードに変換し、それを16進2桁の文字列にする // 文字列は、上位、下位で格納される for i := 1 to srcText.Length do sndText := sndText + IntToHex(Ord(srcText[i]), 2); // 書き込みワード数=必要ワード数 sndLen := srcText.Length div 2; if srcText.Length mod 2 > 0 then begin // 1文字余った時 sndText := sndText + '00'; Inc(sndLen); end; // MC プロトコル形式4(CR,LF あり) cmd := ENQ; cmd := cmd + '00'; // 局番号(PLC側の設定による) cmd := cmd + 'FF'; // PC番号(FXの場合、固定) cmd := cmd + 'WW'; // コマンド(ワード書き込み) cmd := cmd + '1'; // 伝文ウェイト('0'~'F' = 0~150ms) cmd := cmd + 'D0000'; // 5桁で指示:Y0000から cmd := cmd + IntToHex(sndLen, 2); // 書き込みワード数 2桁16進数 // 文字列が'AB'の時、D0 の上位に'B'、下位に'A' が転送される cmd := cmd + sndText; // 書き込み文字列のASCIIコード cmd := cmd + CR + LF; // #13#10 Edit8.Text := cmd; Edit9.Text := ''; // アクセスポイントモードの時、IP アドレスは WF60 から自動で割り付けられる IdTCPClient1.Host := Edit6.Text;//'1.2.3.4'; // ポート:2000 固定 IdTCPClient1.Port := StrToIntDef(Edit7.Text, 2000); IdTCPClient1.ConnectTimeout := 1000; IdTCPClient1.ReadTimeout := 500; try // 接続 if not IdTCPClient1.Connected then IdTCPClient1.Connect; if IdTCPClient1.Connected then begin // コマンド送信 IdTCPClient1.IOHandler.WriteLn(cmd); while IdTCPClient1.IOHandler.CheckForDataOnSource(10) do ; // レスポンス受信 // 受信異常:''; // 受信正常:'*HELLO*' で始まる文字列 // ReadLn:CRLFは含まれない res := IdTCPClient1.IOHandler.ReadLn() ; Edit9.Text := res; { 正常終了時: '*HELLO*'+ACK(#06)+ '00FF';// CRLF(#13#10)は含まない 異常終了時: '*HELLO*'NAK(#$15) + '00FF' + ERRORCODE(2文字);// + CRLF(#13#10)は含まない } if Pos('*HELLO*', res) = 1 then begin res := Copy(res, 8); if (Copy(res, 1, 1) = ACK) and (Length(res) = 5) then begin // 正常終了 Edit10.Text := 'OK'; end else if (Copy(res, 1, 1) = NAK) and (Length(res) = 7) then begin // エラーコード Edit10.Text := 'ErrorCode = ' + Copy(res, 6, 2); end; end else Edit10.Text := 'NoRes'; end; // 切断 IdTCPClient1.Disconnect; except // エラーメッセージ on E: Exception do begin strTExt := E.ClassName + sLineBreak + E.Message; Application.MessageBox(PChar(strTExt), '情報', MB_ICONINFORMATION); end; end; end; // REX-WF60 ワードデバイス 文字列読み込み // 1ワード= 16ビット= 2バイト= 2 文字 procedure TForm1.Button11Click(Sender: TObject); var cmd, res : string; strTExt : string; wordsCount : integer; readLen : integer; readText : string; s : string; i : integer; begin // 読み込み文字列数 readLen := Length(Edit11.Text); // 読み込みワード数 wordsCount := readLen div 2; if readLen mod 2 > 0 then Inc(wordsCount); // MC プロトコル形式4(CR,LF あり) cmd := ENQ; cmd := cmd + '00'; // 局番号(PLC側の設定による) cmd := cmd + 'FF'; // PC番号(FXの場合、固定) cmd := cmd + 'WR'; // コマンド(ワード読み出し) cmd := cmd + '1'; // 伝文ウェイト('0'~'F' = 0~150ms) cmd := cmd + 'D0000'; // 5桁で指示:Y0000から cmd := cmd + IntToHex(wordsCount, 2); // '06'; // 読み出し数2桁 cmd := cmd + CR + LF; Edit8.Text := cmd; Edit9.Text := ''; // アクセスポイントモードの時、IP アドレスは WF60 から自動で割り付けられる IdTCPClient1.Host := Edit6.Text; //'1.2.3.4'; // ポート:2000 固定 IdTCPClient1.Port := StrToIntDef(Edit7.Text, 2000); IdTCPClient1.ConnectTimeout := 1000; IdTCPClient1.ReadTimeout := 500; try // 接続 if not IdTCPClient1.Connected then IdTCPClient1.Connect; if IdTCPClient1.Connected then begin // コマンド送信 IdTCPClient1.IOHandler.WriteLn(cmd); while IdTCPClient1.IOHandler.CheckForDataOnSource(10) do ; // レスポンス受信 // 受信異常:''; // 受信正常:'*HELLO*' で始まる文字列 // ReadLn:CRLFは含まれない res := IdTCPClient1.IOHandler.ReadLn() ; Edit9.Text := res; { 正常終了時: '*HELLO*' + STX(#02)+'00FF'+ RESULT(4文字×読み出し数) + ETX(#03);// +CRLF(#13#10) 異常終了時: '*HELLO*' + NAK(#$15) + '00FF' + ERRORCODE(2文字);// + CRLF(#13#10) } if Pos('*HELLO*', res) = 1 then begin res := Copy(res, 8); if (Copy(res, 1, 1) = STX) and (Length(res) >= 8 - 2 + wordsCount*4) then begin // ワードデバイスの状態 // 左から若い順 s := Copy(res, 6, wordsCount*4); for i := 1 to s.Length div 2 do // ASCIIコードを文字列に変換 readText := readText + Char(StrToInt('$' + s[i * 2 - 1] + s[i * 2])); Edit10.Text := readText; end else if (Copy(res, 1, 1) = NAK) and (Length(res) = 9 - 2) then begin // エラーコード Edit10.Text := 'ErrorCode = ' + Copy(res, 6, 2); end; end else Edit10.Text := 'NoRes'; end; // 切断 IdTCPClient1.Disconnect; except // エラーメッセージ on E: Exception do begin strTExt := E.ClassName + sLineBreak + E.Message; Application.MessageBox(PChar(strTExt), '情報', MB_ICONINFORMATION); end; end; end;