ロトロニック社温湿度プローブ HC2 / HC2A のロギング機能 (2018/04/02)
プローブ単体にロギング機能を内蔵しています。
・データ数: (温度+湿度) × 2000 データまで
・インターバル: 5 秒単位で任意
・時刻データは、ロギング開始時刻のみ記憶
データごとの時刻は、ロギング開始時刻 + データ数× 5 秒
・ロギング中でも、ロギングデータは読み込み可能
・ロギング中でも、現在値の取得は可能(コマンド:{ 99RDD})
・ロギングを開始すると、HC2 の電源を切っても ロギング状態、計測データ保持され、
次の通電時、前回までの計測データに追加されます。
HC2 の時刻記録は開始時刻だけなのでログ中断の時刻は記録されません。
・ロギング状態の確認
送信コマンド:{ 99LGC\}
返信文字列を';' 区切りで読みだすと、下記のデータが取得されます。( []内の数字は、0ベースのインデックス)
[1]:ログ状態 1: 実行中、0: 停止中
[2]:ログモード 1: スタートストップモード、2: ループモード
[3]:ロギングインターバル 1: 5秒 2: 10秒 ... 10: 50秒(5秒単位)
[4]:ロギング時刻の基準値(2000 年 1 月 1 日 00:00:00 からの経過秒数を 5 で割った整数値)
[5]:データ数 (状態チェックコマンド時のみ)
・ロギング開始
送信コマンド:{ 99LGC 1;1;1;12345678;}
1; ロギング開始
1; ロギングモード
1; ロギングインターバル
123..; ロギング基準時刻
・ロギング基準時刻
'2000/01/01 00:00:00 を基準として、経過秒数を 5 で割った整数値
・ロギング停止
送信コマンド:{ 99LGC 0;1;1;12345678;}
停止の場合、ロギング基準時刻は適当で良いようだが、ロギング状態を取得しその値をそのまま使う。
・ロギングデータの取り出し
1つの計測データに対して 3 バイトのデータが返ってきます。アドレスを、3 バイト進めると次の計測データになります。
返信伝文の長さは、9 + データ数×12 + 1(#13 = CR) 文字。2000 データの読みだしに 40 秒ほどかかります。
コマンド:{ 99ERD 0; + 読みだしアドレス + ; + 読みだしバイト数 + ;}
※ロギングデータの開始アドレスは、2176。読みだしデータ数ではなく、バイト数。
※「神栄テクノロジー株式会社 温湿度プローブ HC2 (HygroClip2) シリーズ 通信プロトコル」を参考にしました。
■サンプルコード
Delphi 10.2 Tokyo
unit Unit2; interface uses Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, System.DateUtils, Vcl.StdCtrls, AdPacket, OoMisc, AdPort,AdSelCom ; type TForm2 = class(TForm) ApdComPort1: TApdComPort; Button2: TButton; ComboBox1: TComboBox; Memo1: TMemo; Button3: TButton; Button4: TButton; Button5: TButton; Button6: TButton; Button1: TButton; procedure Button1Click(Sender: TObject); procedure Button2Click(Sender: TObject); procedure FormCreate(Sender: TObject); procedure Button3Click(Sender: TObject); procedure Button4Click(Sender: TObject); procedure Button5Click(Sender: TObject); procedure Button6Click(Sender: TObject); procedure ApdComPort1TriggerAvail(CP: TObject; Count: Word); private { Private 宣言 } public { Public 宣言 } // 応答伝文を区別するため CmdMode: integer; // 読み込みデータカウンター LogDataCounter : integer; // LGC コマンドにより取得したデータ数 LogDataCount : integer; // LGC コマンドにより取得したログ周期 LogInterval : integer; // LGC コマンドにより取得したログ開始時刻 LogTimeCount : integer; // LGC コマンドにより取得したログ開始時刻 LogDataStartTime : TDateTime; // ログモード LogMode : integer; // ロギング中 IntLogFlag : integer; LogDataEndTime : TDateTime; // データ読み込みのインデックス LogReadIndex : integer; // 読みだし時間計測用 Ticks : Cardinal; // 受信文字列 RcvBuffer : string; procedure DoLogCommand; end; var Form2: TForm2; implementation {$R *.dfm} // TDateTime を 2000/01/01 00:00:00.00 を基準にした 5秒単位のカウント数に変換 function AirChipTimeCount(ADate: TDateTime): Integer; // uses ... System.DateUtils; var sec : cardinal; begin sec := SecondsBetween(StrToDateTime('2000/01/01 00:00:00'), ADate); result := sec div 5; end; // ログ内の時間データ(2000/01/01 00:00:00.00 を基準にした 5秒単位のカウント数)を // TDateTime に変換 function AirChipTimeCountToDateTime(ACount : integer): TDateTime; begin result := StrToDateTime('2000/01/01 00:00:00') + (ACount * 5) / SecsPerDay; end; // ログデータ 3バイトの数値を温度、湿度データに変換 procedure AirChipDataTH(b1, b2, b3: Byte; var T: double; var H: double); var v : Cardinal; begin v := b1 + 256 * b2 + 65536 * b3; H := (v mod 1024) / 10; T := Trunc(v / 1024) / 20 -100; end; procedure TForm2.DoLogCommand; var i : integer; sl : TStringList; T, H : double; begin Memo1.Lines.BeginUpdate ; if CmdMode >= 100 then begin sl := TStringList.Create; try sl.Delimiter := ';'; sl.DelimitedText := RcvBuffer; if (CmdMode <> 101) and (sl.Count >= 6) then begin // ログ中ON IntLogFlag := (sl[1]).ToInteger; // ログモード LogMode := (sl[2]).ToInteger; // ログ間隔 LogInterval := (sl[3]).ToInteger * 5; // 5秒単位 // ログ開始日付時刻 LogTimeCount := (sl[4]).ToInteger; // それを TDateTime に変換 LogDataStartTime := AirChipTimeCountToDateTime(LogTimeCount); // ログデータ数 LogDataCount := (sl[5]).ToInteger; if LogDataCount = 0 then begin CmdMode := 0; Memo1.Lines.Add(RcvBuffer); Memo1.Lines.Add('LogCount = 0, Please retry'); end else begin // ログデータ読みだし if CmdMode = 100 then begin // 周期、開始時刻~終了時刻、データ数を表示 Memo1.Lines.Add( ' ロギング中=' + IntLogFlag.ToString + ' ログモード=' + LogMode.ToString + ' ログ周期=' + LogInterval.ToString + ' 開始時刻=' + FormatDateTime('yyyy/mm/dd hh:mm:ss', LogDataStartTime) + ' データ数=' + LogDataCount.ToString); // ログデータ読込コマンドを送信 CmdMode := 101; LogReadIndex := 0; // 次の 80 データ(240 バイト)を読む ApdComport1.PutString( '{ 99ERD 0;' + Format('%04d',[LogReadIndex + 2176]) + ';00240;}' + #13); LogReadIndex := LogReadIndex + 240; end // ログ停止 else if CmdMode = 110 then begin CmdMode := 0; Memo1.Lines.Add( ' ロギング中=' + IntLogFlag.ToString + ' ログモード=' + LogMode.ToString + ' ログ周期=' + LogInterval.ToString + ' 開始時刻=' + FormatDateTime('yyyy/mm/dd hh:mm:ss', LogDataStartTime) + ' データ数=' + LogDataCount.ToString); // LOG 停止 ApdComport1.PutString('{ 99LGC 0;1;'+ sl[3] + ';' + sl[4] +';}' + #13); CmdMode := 111; // この応答伝文を無視するためのダミー // 周期、開始時刻~終了時刻、データ数を表示 Memo1.LInes.Add('End Logging'); end // ログ開始 else if CmdMode = 120 then begin CmdMode := 0; Memo1.Lines.Add( ' ロギング中=' + IntLogFlag.ToString + ' ログモード=' + LogMode.ToString + ' ログ周期=' + LogInterval.ToString + ' 開始時刻=' + FormatDateTime('yyyy/mm/dd hh:mm:ss', LogDataStartTime)); // 周期、開始時刻を表示 Memo1.Lines.Add('Begin Logging'); end // ログ状態の確認 else if CmdMode = 130 then begin Memo1.Lines.Add( ' ロギング中=' + IntLogFlag.ToString + ' ログモード=' + LogMode.ToString + ' ログ周期=' + LogInterval.ToString + ' 開始時刻=' + FormatDateTime('yyyy/mm/dd hh:mm:ss', LogDataStartTime) + ' データ数=' + LogDataCount.ToString); end; end; end; // 240 + 2 if (CmdMode = 101) and (sl.Count >= 80 * 3) then begin for i := 0 to 80 -1 do begin Inc(LogDataCounter); AirChipDataTH( StrToInt(sl[i * 3 + 1]), StrToInt(sl[i * 3 + 2]), StrToInt(sl[i * 3 + 3]), T, H); // SecsPerDay = 24 * 60 * 60 Memo1.Lines.Add( Format('%04d: ', [LogDataCounter]) + FormatDateTime('yyyy/mm/dd hh:mm:ss', LogDataStartTime + ((LogDataCounter-1) * LogInterval) / SecsPerDay) + Format(' %.1f %.1f', [T, H])); if (LogDataCounter >= LogDataCount) or (LogDataCounter >= 2000) then begin LogDataEndTime := LogDataStartTime+ ((LogDataCounter-1) * LogInterval) / SecsPerDay; CmdMode := 0; Memo1.Lines.Add(Format('%.1f sec', [(GetTickCount - Ticks)/ 1000])); Break; end; end; if CmdMode = 101 then begin // 次の 80 データ(240 バイト)を読む ApdComport1.PutString( '{ 99ERD 0;' + Format('%04d',[LogReadIndex + 2176]) + ';00240;}' + #13); LogReadIndex := LogReadIndex + 240; end; end; finally // 受信文字列バッファをクリア RcvBuffer := ''; sl.Free; end; end; Memo1.Lines.EndUpdate; Memo1.Repaint; end; // HC2 からの受信伝文 procedure TForm2.ApdComPort1TriggerAvail(CP: TObject; Count: Word); var //i : integer; //c : AnsiChar; Flag : boolean; B: TBytes; begin { // 1文字ずつ取得 Flag := False; for i := 1 to Count do begin c := ApdComPort1.GetChar; if c = #13 then begin Flag := True; Break; end else RcvBuffer := RcvBuffer + string(c); end; } // ブロックごと取得(時間は変わらない) SetLength(B, Count); ZeroMemory(B, Count); ApdComPort1.GetBlock(B[0], Count); RcvBuffer := RcvBuffer + TEncoding.ANSI.GetString(B); Flag := B[Count -1] = Ord(#13); if Flag then DoLogCommand; end; // ロギング状態の確認 procedure TForm2.Button1Click(Sender: TObject); begin CmdMode := 130; ApdComport1.PutString('{ 99LGC\}' + #13); end; // OPEN procedure TForm2.Button2Click(Sender: TObject); var s : string; begin if ComboBox1.ItemIndex > 0 then begin // COM ポート番号 s :=(ComboBox1.Items[ComboBox1.ItemIndex]).Substring(4); ApdComport1.ComNumber := s.ToInteger; // HC2 の仕様 ApdComport1.Baud := 19200; ApdComport1.DataBits := 8; ApdComport1.StopBits := 1; ApdComport1.Parity := TParity.pNone; // HC2 の電源供給に使用のため、ON ApdComport1.RTS := True; try // すでにOpen していてもエラーにはならない ApdComPort1.Open := True; Memo1.Lines.BeginUpdate; Memo1.Lines.Add(' Connected'); Memo1.Lines.EndUpdate; except ShowMessage('オープン失敗'); end; end; end; // ポートクローズ procedure TForm2.Button3Click(Sender: TObject); begin // オープンされていない時は、エラーになる if ApdComPort1.Open then ApdComPort1.Open := False; end; // ログ停止 procedure TForm2.Button4Click(Sender: TObject); begin CmdMode := 110; ApdComport1.PutString('{ 99LGC\}' + #13); end; // ログ開始 procedure TForm2.Button5Click(Sender: TObject); var LogStartTimeCount : Integer; begin Cmdmode := 0; // ログ開始時刻 LogStartTimeCount := AirChipTimeCount(Now); // 一度ロギング 停止 // 前回のロギングデータのログ周期、ログ開始時刻は無意味になる // なので、時刻は適当でも良い ApdComport1.PutString('{ 99LGC 0;1;1;' + LogStartTimeCount.ToString +';}' + #13); Sleep(100); // ロギング 開始 ApdComport1.PutString('{ 99LGC 1;1;1;' + LogStartTimeCount.ToString +';}' + #13); Sleep(100); // ロギング状態の確認 CmdMode := 120; ApdComport1.PutString('{ 99LGC\}' + #13); end; // ログデータ読みだし procedure TForm2.Button6Click(Sender: TObject); begin // OPEN後、すぐだとデータ数が0で取得される // ログ周期、開始日付時刻を取得するため、ロギング状態を確認 CmdMode := 100; LogDataCounter := 0; Ticks := GetTickCount; ApdComport1.PutString('{ 99LGC\}' + #13); end; procedure TForm2.FormCreate(Sender: TObject); var i :integer; begin Memo1.Lines.Clear; RcvBuffer := ''; ApdComport1.AutoOpen := False; // 現在有効な COM ポート番号を列挙 for i := 1 to 32 do if IsPortAvailable(i) then ComboBox1.Items.Add ('COM ' + IntToStr (i)); end; end.