azbil SDC / R CPL 通信 (2019/03/09)
azbil の調節計 SDC/R ですが、ローダーケーブルを使うと RS-485 モデルでなくても SPL 通信ができます。
ローダーケーブルは、メーカー純正品だけでなく、UART - RS232C 変換 IC を使って自作(詳しくはこちら)も可能です。
以下は、Delphi を使った Windows 用のサンプルです。
将来、Android からワイヤレス(UART - Bluetooth 変換)で設定変更、手動操作をするためのたたき台です。

unit Unit1;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, OoMisc, AdPort, AdSelCom,
Vcl.ExtCtrls, Vcl.Buttons,
IniFiles;
type
TForm1 = class(TForm)
ApdComPort1: TApdComPort;
ComboBox1: TComboBox;
Timer1: TTimer;
Label1: TLabel;
Label2: TLabel;
Label3: TLabel;
Label4: TLabel;
Edit1: TEdit;
Edit2: TEdit;
Edit3: TEdit;
Edit4: TEdit;
Edit5: TEdit;
Edit6: TEdit;
Button3: TButton;
Button4: TButton;
Label6: TLabel;
Edit7: TEdit;
Edit8: TEdit;
SpeedButton1: TSpeedButton;
SpeedButton2: TSpeedButton;
SpeedButton3: TSpeedButton;
Label5: TLabel;
procedure ApdComPort1TriggerAvail(CP: TObject; Count: Word);
procedure FormCreate(Sender: TObject);
procedure Button3Click(Sender: TObject);
procedure Timer1Timer(Sender: TObject);
procedure SpeedButton1Click(Sender: TObject);
procedure Button4Click(Sender: TObject);
procedure SpeedButton3Click(Sender: TObject);
procedure Edit7KeyPress(Sender: TObject; var Key: Char);
procedure Edit8KeyPress(Sender: TObject; var Key: Char);
procedure FormDestroy(Sender: TObject);
private
{ Private 宣言 }
public
{ Public 宣言 }
rcvBuf : string;
rcvFlag : boolean;
cmdMode : integer;
decScale : Double;
decPt : integer;
procedure DispRcvData(const rcv : string);
procedure SendCmd(const cmd : string);
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
const
STX = #$02;
ETX = #$03;
CRLF = #$0D#$0A;
// n の k 乗 (Math ユニット不要)
function IntPower(n, k : integer):integer;
var
i : integer;
begin
result := 1;
for i := 1 to k do result := result * n;
end;
procedure TForm1.DispRcvData(const rcv : string);
// SDC からの受信データを表示
var
s : string;
fmt : string;
i : integer;
begin
try
if cmdMode = 100 then begin
if (Copy(rcv, 6, 2) = '00') and (Length(Copy(rcv, 8)) >= 28) then begin
for i := 0 to 6 do begin
s := Copy(rcv, 8 + i * 4, 4);
if i = 0 then begin
decPt := StrToIntDef('$' + s, 0);
decScale := 1 / IntPower(10, decPt);
end
else begin
fmt := '%.' + decPt.ToString + 'f';
case i of
// PV
1: Edit1.Text := Format(fmt, [StrToInt('$' + s) * decScale]);
// SP
2: begin
s := Format(fmt, [StrToInt('$' + s) * decScale]);
if (Edit2.Text = '') and (Edit7.Text = '') then Edit7.Text := s;
Edit2.Text := s;
end;
// OUT
3: Edit3.Text := Format('%.1f', [StrToInt('$' + s) * 0.1]);
// MFB
4: Edit4.Text := Format('%.1f', [StrToInt('$' + s) * 0.1]);
// Auto / Man
5: if StrToInt('$' + s) = 1 then begin
Edit5.Text := 'MANUAL';
Edit5.Font.Color := clRed;
Edit8.Font.Color := clRed;
end
else begin
Edit5.Text := 'AUTO';
Edit5.Font.Color := clGreen;
Edit8.Font.Color := clGreen;
end;
// MAN Out
6: begin
s := Format('%.1f', [StrToInt('$' + s) * 0.1]);
if Edit6.Text = '' then Edit8.Text := s;
Edit6.Text := s;
end;
end;
end;
end;
end;
end;
except
;
end;
end;
procedure TForm1.Edit7KeyPress(Sender: TObject; var Key: Char);
// SP
begin
if Key = #13 then begin
Key := #00;
SpeedButton1Click(SpeedButton1);
end;
end;
procedure TForm1.Edit8KeyPress(Sender: TObject; var Key: Char);
// MANUAL OUT
begin
if Key = #13 then begin
Key := #00;
SpeedButton1Click(SpeedButton2);
end;
end;
procedure TForm1.ApdComPort1TriggerAvail(CP: TObject; Count: Word);
// SDC より返信
var
i : Word;
c : AnsiChar;
begin
for i := 1 to Count do begin
c := ApdComPort1.GetChar;
if rcvFlag then begin
if c = ETX then begin
DispRcvData(string(rcvBuf));
// チェックサムは無視する
rcvFlag := False;
end;
if rcvFlag then rcvBuf := rcvBuf + string(c);
end;
if c = STX then rcvFlag := True;
end;
end;
procedure TForm1.Button3Click(Sender: TObject);
// 通信開始
var
s : string;
begin
with ApdComPort1 do begin
s := Copy(ComboBox1.Text, 4);
s := Copy(s, 1, Length(s) -1);
ComNumber := StrToIntDef(s, 4);
Baud := 19200;
DataBits := 8;
StopBits := 1;
Parity := TParity.pEven;
end;
// 係数
decScale := -1;
try
ApdComPort1.Open := True;
Timer1.Enabled := True;
except
end;
end;
procedure TForm1.Button4Click(Sender: TObject);
// 通信終了
begin
ApdComPort1.Open := False;
Timer1.Enabled := false;
end;
procedure TForm1.FormCreate(Sender: TObject);
// フォーム生成
var
i: integer;
edt : TEdit;
ini : TIniFile;
begin
AdSelCom.ShowPortsInUse := False;
for i := 0 to 32 do
if AdSelCom.IsPortAvailable(i) then
ComboBox1.Items.Add (AdPort.ComName(i) + '.');
for i := 1 to 8 do begin
edt := FindComponent('Edit' + IntToStr(i)) as TEdit;
edt.Text := '';
end;
Label5.Caption := '';
ini := TIniFile.Create(ChangeFileExt(ParamStr(0), '.ini'));
try
if ComboBox1.Items.Count > 0 then
ComboBox1.ItemIndex := ini.ReadInteger('Com', 'PortIndex', 0);
finally
ini.Free;
end;
end;
procedure TForm1.FormDestroy(Sender: TObject);
// フォーム破棄
var
ini : TIniFile;
begin
ini := TIniFile.Create(ChangeFileExt(ParamStr(0), '.ini'));
try
ini.WriteInteger('Com', 'PortIndex', ComboBox1.ItemIndex);
finally
ini.Free;
end;
end;
procedure TForm1.SendCmd(const cmd : string);
// SDC へコマンド送信
var
s : string;
i : integer;
sum : integer;
chksum : string;
begin
s := STX; // 電文先頭
s := s + '01'; // 機器アドレス
s := s + '00'; // サブアドレス(未使用)
s := s + 'X' ; // デバイス区別コード 'X' または、'x'
s := s + cmd ; // コマンド
s := s + ETX ; // アプリケーション層の終了位置
// チェックサムの作成
// 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; // 電文の最後
rcvBuf := '';
rcvFlag := False;
if ApdComport1.Open then
ApdComPort1.Output := AnsiString(s);
end;
procedure TForm1.SpeedButton1Click(Sender: TObject);
// SP / MANUAL 変更
var
cmd : string;
v : double;
begin
if ApdComport1.Open then begin
cmd := '';
Timer1.Enabled := False;
// SP
if Sender as TSpeedButton = SpeedButton1 then begin
v := StrToFloatDef(Edit7.Text, -999);
if v > - 999 then begin
cmd := 'WD238E' + IntToHex(Trunc(v * IntPower(10, decPt)), 4);
Edit7.Text := Format ('%.' + decPt.ToString + 'f',[StrToFloat(Edit7.Text)]);
end;
end
// Manual
else begin
if Edit5.Text = 'MANUAL' then begin
v := StrToFloatDef(Edit8.Text, -999);
if v > -999 then begin
cmd := 'WD3902' + IntToHex(Trunc(v * 10), 4);
Edit8.Text := Format ('%.1f',[StrToFloat(Edit8.Text)]);
end;
end;
end;
cmdMode := 0;
if cmd <> '' then SendCmd(cmd);
Timer1.Enabled := True;
end;
end;
procedure TForm1.SpeedButton3Click(Sender: TObject);
// Auto / Manual
var
cmd : string;
begin
if ApdComport1.Open then begin
Timer1.Enabled := False;
if Edit5.Text = 'AUTO' then begin
//cmd := 'WD3904' + '0001'
cmd := 'WU00'+ '3904' + '0001' + '3902' + IntToHex(Trunc(StrToFloatDef(Edit8.Text, 0) * 10), 4);
Edit8.Text := Format('%.1f',[StrToFloatDef(Edit8.Text, 0)]);
end
else cmd := 'WD3904' + '0000';
cmdMode := 0;
SendCmd(cmd);
Timer1.Enabled := True;
end;
end;
procedure TForm1.Timer1Timer(Sender: TObject);
var
cmd : string;
begin
cmdMode := 100;
if cmdMode = 100 then begin
// dp, PV, SP, OUT, MFB, AUT/MAN, MAN OUT
cmd := 'RU00' +'1454' + '3814' + '3815' + '3816' + '3851' + '3811' + '3902';
if ApdComport1.Open then begin
SendCmd(cmd);
if Label5.Caption <> '' then Label5.Caption := ''
else Label5.Caption := '■';
end;
end;
end;
end.