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.