ラトックシステムの 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;