Delphi (ComPortLib) で azbil SDC/Rシリーズ RS-485 通信
azbil SDC/R シリーズの RS-485 (CPL / Modbus) 通信をやってみました。

使用した USB-RS485 変換ケーブルは、UTS-485TB-V2 (株式会社 ネットメカニズム http://www.cabling-ol.net/cabledirect/UTS-485TB-V2.php)
Windows7、8.1 では、USBポートに接続すると、自動でドライバーがインストールされます。
明示的に送信/受信を切り替える機能は無く、送信をするタイミング以外では、常に受信待機となります。
このため、RTS、DTSのコントロールは不要になり、プログラムはとても簡単なものになります。
※azbil C15、25/26、35/36 では終端抵抗を付けないので、本体内部 J1 の開放が必要です。
CPL の恩恵として、azbil ローダー SPL-C35 から 「CMC10L経由」を選択すると、接続機器との通信が可能になります。

unit azbilCPLUnit;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, CPort, CPortCtl;
type
TForm3 = class(TForm)
ComPort1: TComPort;
Button1: TButton;
Edit1: TEdit;
Edit2: TEdit;
ComLed1: TComLed;
Label1: TLabel;
Edit3: TEdit;
Button2: TButton;
Button3: TButton;
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
procedure Button3Click(Sender: TObject);
private
{ Private 宣言 }
public
{ Public 宣言 }
end;
var
Form3: TForm3;
implementation
{$R *.dfm}
//******************************
// CPL
// SetUp C64 = 0
//******************************
procedure TForm3.Button1Click(Sender: TObject);
const
STX = #$02;
EXT = #$03;
CRLF = #$0D#$0A;
var
cmd : string;
s : string;
i : integer;
sum : integer;
chksum : string;
res : string;
begin
Edit1.Text := '';
Edit1.Update;
ComPort1.Timeouts.ReadInterval := 20;
ComPort1.Timeouts.ReadTotalMultiplier := 0;
ComPort1.Timeouts.ReadTotalConstant := 500;
ComPort1.Timeouts.WriteTotalMultiplier := 0;
ComPort1.Timeouts.WriteTotalConstant := 500;
ComPort1.Port := 'COM7';
ComPort1.BaudRate := br19200;
ComPort1.Parity.Bits := prEven;
ComPort1.StopBits := sbOneStopBit;
ComPort1.DataBits := dbEight;
ComPort1.FlowControl.ControlDTR := dtrDisable;
ComPort1.FlowControl.ControlRTS := rtsDisable;
// セットアップダイアログを表示
// ComPort1.ShowSetupDialog;
try
// オープン
ComPort1.Open;
if ComPort1.Connected then begin
// 送信コマンド (9101 = PV読み出し)
cmd := 'RS,9101W,1';
s := STX; // 電文先頭
s := s + '01'; // 機器アドレス
s := s + '00'; // サブアドレス(未使用)
s := s + 'X' ; // デバイス区別コード 'X' または、'x'
s := s + cmd ; // コマンド
s := s + EXT ; // アプリケーション層の終了位置
// チェックサムの作成
// STX ~ ETX までを1バイトずつ加算
sum := 0;
for i := 1 to s.Length do
sum := sum + Ord(s[i]);
// 加算結果の下位1バイト
sum := sum and $000000FF;
// 2の補数
sum := (- sum and $000000FF);
// それを2バイトの ASCII コードに変換
chksum := IntToHex(sum, 2);
s := s + chksum; // チェックサム
s := s + CRLF; // 電文の最後
// 送信文字列
Edit1.Text := s;
Edit1.Update;
// 送信
ComPort1.WriteStr(s);
// 受信
ComPort1.ReadStr(res, 256);
// 受信文字列
Edit2.Text := res;
// PV
Edit3.Text := '';
if Copy(res, 7, 2) = '00' then begin
res := Copy(res, 10, res.Length - Pos(EXT, res) -1);
Edit3.Text := res;
end;
// クローズ
ComPort1.Close;
end
else begin
ShowMessage('ComPort 接続 失敗');
end;
except
ShowMessage('ComPort オープン エラー');
end;
end;
//******************************
// Modbus ASCII
// SetUp C64 = 1
//******************************
procedure TForm3.Button2Click(Sender: TObject);
const
CRLF = #$0D#$0A;
var
head : string;
s : string;
i : integer;
sum : integer;
chksum : string;
res : string;
begin
Edit1.Text := '';
Edit1.Update;
ComPort1.Timeouts.ReadInterval := 20;
ComPort1.Timeouts.ReadTotalMultiplier := 0;
ComPort1.Timeouts.ReadTotalConstant := 500;
ComPort1.Timeouts.WriteTotalMultiplier := 0;
ComPort1.Timeouts.WriteTotalConstant := 500;
ComPort1.Port := 'COM7';
ComPort1.BaudRate := br19200;
ComPort1.Parity.Bits := prEven;
ComPort1.StopBits := sbOneStopBit;
ComPort1.DataBits := dbEight;
ComPort1.FlowControl.ControlDTR := dtrDisable;
ComPort1.FlowControl.ControlRTS := rtsDisable;
try
// オープン
ComPort1.Open;
if ComPort1.Connected then begin
head := ':'; // 電文先頭
s := '01'; // 機器アドレス(2桁)
s := s + '03'; // 読み出しコマンド
s := s + '238D'; //読み出し先頭アドレス(16進4桁)
s := s + '0001'; //読み出し数(4桁)
// チェックサム(LRC)の作成
// 機器アドレスの先頭からチェックサムの直前まで
sum := 0;
// 16進2文字ずつを数値に変換し、加算する
for i := 1 to s.Length div 2 do
sum := sum + StrToInt('$' + Copy(s, i * 2 - 1, 2));
// 加算結果の下位1バイト
sum := sum and $000000FF;
// 2の補数
sum := (- sum and $000000FF);
// それを2バイトの ASCII コードに変換
chksum := IntToHex(sum, 2);
s := s + chksum; // チェックサム
s := s + CRLF; // 電文の最後
// 送信文字列
Edit1.Text := head + s;
Edit1.Update;
// 送信
ComPort1.WriteStr(head + s);
// 受信
ComPort1.ReadStr(res, 256);
// 受信文字列
Edit2.Text := res;
// PV表示
Edit3.Text := '';
// 送信した読み出しコマンド '03' を比較
if Copy(res, 4, 2) = '03' then begin
// 読み出しデータ数は、2倍されているので注意
// 1つ目の読み出しデータ(16進)
res := Copy(res, 8, 4);
// 16進を10進に変換
Edit3.Text := IntToStr(StrToInt('$' + res));
end;
// クローズ
ComPort1.Close;
end
else begin
ShowMessage('ComPort 接続 失敗');
end;
except
ShowMessage('ComPort オープン エラー');
end;
end;
//******************************
// Modbus RTU
// SetUp C64 = 2
//******************************
// azbil マニュアルのCRC計算を Delphi に変換
function CRC16(buf : array of Byte; BufLen: integer): word;
var
i, j : integer;
carry : word;
crc : word;
crcl : Byte;
begin
crc := $FFFF;
for i := 0 to BufLen - 1 do begin
crc := word(crc xor Ord(Buf[i]));
for j := 0 to 7 do begin
carry := crc and $0001;
crc := crc shr 1;
if carry = 1 then
crc := crc xor $A001;
end;
end;
crcl := (crc and $FF00) shr 8;
crc := crc shl 8;
crc := crc or crcl;
result := crc;
end;
procedure TForm3.Button3Click(Sender: TObject);
const
CRLF = #$0D#$0A;
var
s : string;
i : integer;
n : integer;
TxBuf : array [0..255] of Byte;
TxBufLen : integer;
crc : word;
RxBuf : array [0..255] of Byte;
res : string;
begin
Edit1.Text := '';
Edit1.Update;
ComPort1.Timeouts.ReadInterval := 20;
ComPort1.Timeouts.ReadTotalMultiplier := 0;
ComPort1.Timeouts.ReadTotalConstant := 500;
ComPort1.Timeouts.WriteTotalMultiplier := 0;
ComPort1.Timeouts.WriteTotalConstant := 500;
ComPort1.Port := 'COM7';
ComPort1.BaudRate := br19200;
ComPort1.Parity.Bits := prEven;
ComPort1.StopBits := sbOneStopBit;
ComPort1.DataBits := dbEight;
ComPort1.FlowControl.ControlDTR := dtrDisable;
ComPort1.FlowControl.ControlRTS := rtsDisable;
//ComPort1.DiscardNull := True;
try
// オープン
ComPort1.Open;
if ComPort1.Connected then begin
s := '01'; // 機器アドレス(2桁)
s := s + '03'; // 読み出しコマンド
s := s + '238D'; //読み出し先頭アドレス(16進4桁)
s := s + '0001'; //読み出し数(4桁)
TxBufLen := s.Length div 2;
for i := 1 to TxBufLen do
TxBuf[i-1] := StrToInt('$' + s[i * 2 - 1] + s[i * 2]);
// チェックサム(CRC)計算
crc := CRC16(TxBuf, TxBufLen);
Edit1.Text := s;
s := IntToHex(crc, 4);
// 文字列として表示
Edit1.Text := Edit1.Text + s;
// 送信データに CRC を追加
TxBuf[TxBufLen ] := StrToInt('$'+Copy(s, 1, 2));
TxBuf[TxBufLen + 1] := StrToInt('$'+Copy(s, 3, 2));
TxBufLen := TxBufLen + 2;
// 送信
// C80(LEDモニタ) = 2 (受信時点滅)にて確認
ComPort1.Write(TxBuf, TxBufLen);
// 受信
// C80(LEDモニタ) = 1 (送信時点滅)にて確認
n := ComPort1.Read(RxBuf, 256);
Edit2.Text := '';
Edit3.Text := '';
res := '';
if n > 0 then begin
// 受信データを文字列にする
for i := 0 to n - 1 do
res := res + IntToHex(RxBuf[i], 2);
// 受信文字列
Edit2.Text := res;
// PV表示
res := Copy(res, 7, 4);
// 16進を10進に変換
Edit3.Text := IntToStr(StrToInt('$' + res));
end;
// クローズ
ComPort1.Close;
end
else begin
ShowMessage('ComPort 接続 失敗');
end;
except
ShowMessage('ComPort オープン エラー');
end;
end;
end.