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.