Delphi XE5, 10.1 Berlin で Rotronic HC2 (ロトロニック社温湿度計) を、読む (2015/08/14, 2017/03/03 更新)
ロトロニック社 (http://www.rotronic.jp/index.html) の温湿度計を、簡単に Delphi から使うサンプルです。
2017/03/03 (追加)
--------------------------------------------------------------------
HC2 センサーの UART 信号を RS232C にレベル変換すると、とても簡単に読めました。DLL や VCP は不要です。
使用したレベル変換器(¥930) : http://www.mycomkits.com/SHOP/MK-205.html
RS232C の通信設定、送信するコマンド "{ 99RDD}"+#13 は、下記のサンプルと同じです。
MK205 にラトックシステムのWi-Fi - RS232C変換器 (REX-WF60)、Bluetooth - RS232C 変換器 (REX-BT60)
を接続すると、
ワイヤレスで、Windows, Android 端末から、温度、湿度、露点温度の計測が可能になります。
どちらの機器も 5V の電源が必要ですが、モバイルバッテリーでも使用できました。
--------------------------------------------------------------------
※専用のUSBケーブル(UART信号をUSB信号に変換)が必要で、ケーブルだけで ¥24,000. (定価)します。
ハンディタイプ温湿度計(HP22等)の場合、電源は、USBから給電されます。変換器(HF5等)では、無理でした。
USBドライバーのインストール手順 http://www.rotronic.jp/faq_005.html
Windows 8.1 の場合は、「Windows 8 (8.1) でPC接続したとき、正常にインストールできない。」 http://www.rotronic.jp/faq.html も参考に。
VB.NET、C#、VC++、Excel 等から使えるRo3xdrv.dll が公開されていますが、残念ながら、Delphi からでは使えないようです。
VB.NETのサンプルをダブルクリックで試してみたところ、簡単にビルドできました。.NET環境の方は、こちらのほうが良さそうです。

■VCPを使用する
USBドライバのVCP(バーチャルコムポート)機能を使うと、通常のRS-232C機器として、
Delphi からでも簡単に使えます。
「USB Serial Converter」 を右クリックし、「プロパティー」 をクリック。

「詳細設定」タブで、「VCPをロードする」にチェックを付け、[OK]ボタンをクリック。

「USB Serial Port (COM No)」 が追加されます。(追加されない場合は、再起動してみて下さい。)
これを右クリックし、プロパティーを表示します。

「ポートの設定」タブでボーレート等の設定を行います。

■Delphi + ComPort (CPort Lib) でバーチャル COM ポートを使う
送信コマンドは、'{ 99RDD}' + #13
※ '{' と '99RDD}' の間には半角スペースが必要です。要注意です。
50msecほどの時間をおいて受信すると、文字列が返ってきます。
これを、';' 区切りで読むと、計測値等の情報が取得できます。
■FTD2XX.dll をそのまま使う
VCP のチェック、設定は不要です。
専用のUSBケーブルを接続し、ドライバをインストールしただけで、使用できます。
送信文字列、受信文字列は、VCP を使う時とまったく同じです。
DLL を使うために、D2XXUnit2.pas (ソースコードを参照) が必要です。
FTD2XX.dll の日本語説明 PDF は、こちら((株)西日本常盤商行様)
サンプルEXE (現場での温湿度計測のための最少機能ツール)
・相対湿度、温度、露点温度(演算値) 表示
・トレンド表示(直近5分のみ)
・トレンドグラフのスクリーンショット保存
・直近5分間の測定データCSVファイル保存
ダウンロード(サンプルEXE本体のみ.ソースコードなし)
■スクリーンショット

サンプルEXE(マルチスレッドで、1秒に1回程度計測しています。)↓

■ソースコード
unit Rotronic232cUnit;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, CPort;
type
TForm2 = class(TForm)
ComPort1: TComPort;
Button1: TButton;
Edit1: TEdit;
Edit2: TEdit;
Edit3: TEdit;
Edit4: TEdit;
Label1: TLabel;
Label2: TLabel;
Label3: TLabel;
Button2: TButton;
procedure Button1Click(Sender: TObject);
procedure Edit1Change(Sender: TObject);
procedure Button2Click(Sender: TObject);
private
{ Private 宣言 }
public
{ Public 宣言 }
end;
var
Form2: TForm2;
implementation
{$R *.dfm}
uses D2XXUnit2;
// VCP(バーチャル COM ポート) 使用
// ComPort コンポーネント(CPort Lib)が必要です
procedure TForm2.Button1Click(Sender: TObject);
var
s : string;
begin
try
with ComPort1 do begin
Port := 'COM6';
BaudRate := br19200;
DataBits := dbEight;
StopBits := sbOneStopBit;
Parity.Bits := prNone;
Parity.Check := False;
FlowControl.FlowControl := fcNone;
end;
ComPort1.Open;
// コマンド送信
ComPort1.WriteStr('{ 99RDD}'+#13);
Sleep(100);
// レスポンス受信
ComPort1.ReadStr(s, 1024);
Edit1.Text := s;
ComPort1.Close;
except
ShowMessage('通信エラーです.');
end;
end;
// USB 使用(VCPを使わない場合は、こちら)
procedure TForm2.Button2Click(Sender: TObject);
var
res : string;
begin
// デバイスオープン
if OpenUsbDevice = FT_OK then begin
// 通信設定
SetUsbDeviceBaudRate;
// タイムアウト設定(必須)
SetUsbDeviceTimeOuts(500, 500);
// 読み込みコマンドを送信
if WriteHc2ReadCommand = FT_OK then
// レスポンス受信
if ReadHc2Result(res) = FT_OK then
Edit1.Text := res
else
ShowMessage('返信エラーです.');
// デバイスクローズ
CloseUsbDevice;
end
else begin
// USBケーブルが接続されていない
ShowMessage('通信エラーです.');
end;
end;
procedure TForm2.Edit1Change(Sender: TObject);
var
sl: TStringList;
s: string;
begin
sl := TStringList.Create;
try
s := StringReplace(Edit1.Text, ' ', '_', [rfReplaceAll]);
sl.Delimiter := ';';
sl.DelimitedText := s;
if sl.Count > 10 then begin
// 相対湿度
Edit2.Text := sl[1];
// 温度
Edit3.Text := sl[5];
// 演算値(露点温度)
Edit4.Text := sl[10];
end
else begin
Edit2.Text := '';
Edit3.Text := '';
Edit4.Text := '';
end;
finally
sl.Free;
end;
end;
end.
// VCP を使う場合は、不要です。
unit D2XXUnit2;
{
***************************************************
FTD2XX.DLL 通信ユニット for HygroClip2
f.izawa (http://www.izawa-web.com) 2015/08/07
***************************************************
参考:
・FTDI社 D2XXUnit.pas
・神栄テクノロジー株式会社 VB6 サンプル通信モジュール
・株式会社西日本常盤商行 Ftd2xxドライバー説明日本語訳
}
interface
Uses Windows,Forms,Dialogs, Messages, SysUtils, Variants, Classes, Graphics, Controls,
System.UITypes;
const
// Return codes
FT_OK = 0;
FT_INVALID_HANDLE = 1;
FT_DEVICE_NOT_FOUND = 2;
FT_DEVICE_NOT_OPENED = 3;
FT_IO_ERROR = 4;
FT_INSUFFICIENT_RESOURCES = 5;
FT_INVALID_PARAMETER = 6;
FT_INVALID_BAUD_RATE = 7;
FT_DEVICE_NOT_OPENED_FOR_ERASE = 8;
FT_DEVICE_NOT_OPENED_FOR_WRITE = 9;
FT_FAILED_TO_WRITE_DEVICE = 10;
FT_EEPROM_READ_FAILED = 11;
FT_EEPROM_WRITE_FAILED = 12;
FT_EEPROM_ERASE_FAILED = 13;
FT_EEPROM_NOT_PRESENT = 14;
FT_EEPROM_NOT_PROGRAMMED = 15;
FT_INVALID_ARGS = 16;
FT_NOT_SUPPORTED = 17;
FT_OTHER_ERROR = 18;
// Flags for FT_OpenEx
FT_OPEN_BY_SERIAL_NUMBER = 1;
FT_OPEN_BY_DESCRIPTION = 2;
// Flags for FT_ListDevices
FT_LIST_NUMBER_ONLY = $80000000;
FT_LIST_BY_INDEX = $40000000;
FT_LIST_ALL = $20000000;
FT_LIST_BY_INDEX_AND_NO = $40000001;
// RS232C
FT_BITS_8 = 8;
FT_BITS_7 = 7;
FT_STOP_BITS_1 = 0;
FT_STOP_BITS_2 = 2;
FT_PARITY_NONE = 0;
FT_PARITY_ODD = 1;
FT_PARITY_EVEN = 2;
FT_PARITY_MARK = 3;
FT_PARITY_SPACE = 4;
FT_FLOW_NONE = $0000;
FT_FLOW_RTS_CTS = $0100;
FT_FLOW_DTR_DSR = $0200;
FT_FLOW_XON_XOFF = $0400;
// Purge
FT_PURGE_RX = 1;
FT_PURGE_TX = 2;
function OpenUsbDevice: long;
function CloseUsbDevice: long;
function WriteHc2ReadCommand: long;
function ReadHc2Result(var res: string): long;
function ReadUsbDevice(var res :string; var ReadLen : long): long;
function SetUSBDeviceBaudRate: long;
function SetUSBDeviceTimeOuts(ReadTimeOut, WriteTimeOut: DWord) : long;
function GetUSBDeviceInfo(var DevType: DWord; var ID: DWord; var SerialNumber: array of AnsiChar; var Description: array of AnsiChar) : long;
implementation
var
FT_HANDLE : Long;
function FT_GetDeviceInfo(ftHandle: DWord; DevType,ID, SerNum,Desc,pvDummy:Pointer) :long; stdcall; external 'FTD2XX.DLL';
function FT_SetDataCharacteristics(ftHandle:Dword; WordLength,StopBits,Parity:Byte):Integer; stdcall; external 'FTD2XX.DLL' name 'FT_SetDataCharacteristics';
function FT_SetFlowControl(ftHandle:Dword; FlowControl:Word; XonChar,XoffChar:Byte):Integer; stdcall; external 'FTD2XX.DLL' name 'FT_SetFlowControl';
function FT_Open(Index:Integer; var ftHandle:Long): long ; stdcall; external 'FTD2XX.DLL' name 'FT_Open';
function FT_Read(ftHandle:Dword; FTInBuf:Pointer; BufferSize:LongInt; var ResultPtr: Long): Integer; stdcall; external 'FTD2XX.DLL' name 'FT_Read';
function FT_Write(ftHandle:Dword; FTOutBuf:Pointer; BufferSize:LongInt; var ResultPtr: Long): Integer; stdcall; external 'FTD2XX.DLL' name 'FT_Write';
function FT_ResetDevice(ftHandle:Dword):Integer; stdcall; external 'FTD2XX.DLL' name 'FT_ResetDevice';
function FT_Purge(ftHandle:Dword; Mask:Dword):Integer; stdcall; external 'FTD2XX.DLL' name 'FT_Purge';
function FT_Close(ftHandle:Dword):long; stdcall; external 'FTD2XX.DLL' name 'FT_Close';
function FT_SetBaudRate(ftHandle:Dword; BaudRate:DWord):Long; stdcall; external 'FTD2XX.DLL' name 'FT_SetBaudRate';
function FT_SetTimeouts(ftHandle:Dword; ReadTimeout,WriteTimeout:Dword):Long; stdcall; external 'FTD2XX.DLL' name 'FT_SetTimeouts';
function FT_GetStatus(ftHandle: DWord; RxBytes, TxBytes, EventStatus:Pointer):long; stdcall; external 'FTD2XX.DLL' name 'FT_GetStatus';
function FT_ListDevices(pvArg1: Dword; pvArg2:Pointer; dwFlags:Dword):Long; stdcall; external 'FTD2XX.DLL' name 'FT_ListDevices';
function FT_OpenEx(pvArg1: Pointer; dwFlags:Dword; var ftHandle: Long):Long; stdcall; external 'FTD2XX.DLL' name 'FT_OpenEx';
function FT_SetDivisor(ftHandle:Dword; Divisor:DWord):Long; stdcall; external 'FTD2XX.DLL' name 'FT_SetDivisor';
function OpenUsbDevice: long;
begin
result := FT_Open(0, FT_Handle);
end;
function CloseUsbDevice: long;
begin
result := FT_Close(FT_Handle);
end;
function WriteHc2ReadCommand: long;
var
WriteLen : long;
cmd : string;
i : integer;
Buffer : array [0.. 63] of AnsiChar;
begin
cmd := '{ 99RDD}' + #13;
for i := 0 to cmd.Length - 1 do
FT_Out_Buffer[i] := AnsiChar(cmd[i + 1]);
result := FT_Write(FT_Handle, @Buffer, 9, WriteLen);
end;
function ReadHc2Result(var res: string): long;
var
ReadLen : long;
begin
result := ReadUsbDevice(res, ReadLen);
end;
function ReadUsbDevice(var res :string; var ReadLen : long): long;
var
i : integer;
ret : Long;
c : AnsiChar;
begin
Result := -1;
for i := 0 to 255 do begin
Application.ProcessMessages;
ret := FT_Read(FT_Handle, @c, 1, ReadLen);
if ret = FT_OK then begin
if (i = 0) and (c <> '{') then Break;
res := res + String(c);
if c = #13 then begin
ReadLen := i;
Result := FT_OK;
break;
end;
end
else begin
Result := ret;
Break;
end;
end;
end;
function SetUsbDeviceBaudRate: Long;
begin
FT_Purge(FT_Handle, FT_PURGE_RX or FT_PURGE_TX);
FT_ResetDevice(FT_Handle);
Result := FT_SetBaudRate(FT_Handle, 19200);
FT_SetDataCharacteristics(FT_Handle, FT_BITS_8 ,FT_STOP_BITS_1, FT_PARITY_NONE);
FT_SetFlowControl(FT_Handle, FT_FLOW_NONE, $11, $13);
end;
function SetUsbDeviceTimeOuts(ReadTimeOut, WriteTimeOut: DWord) : long;
begin
Result := FT_SetTimeouts(FT_Handle, ReadTimeout, WriteTimeout);
end;
// 未使用
function GetUsbDeviceInfo(var DevType: DWord; var ID: DWord; var SerialNumber:array of AnsiChar; var Description: array of AnsiChar) : long;
begin
Result := FT_GetDeviceInfo(FT_Handle, @DevType, @ID, @SerialNumber, @Description, nil);
end;