Solaris de USB温度計
自宅サーバを運用していると、家の温度を測ってグラフ化したくなるというニーズはそれなりにあるようで、多くのブログ記事等がヒットします。 情報を集めてみると、PCsensor社のTEMPerというシリーズのUSB温度計が、1000円くらいから入手できてよく使われているようです。
USB温度計! USB thermometer-528018
- 出版社/メーカー: OEM
- メディア: エレクトロニクス
- 購入: 5人 クリック: 48回
- この商品を含むブログ (2件) を見る
安いのでとりあえずAmazonで購入してみました。私が購入したのは Gold TEMPer という製品のようです。中国語混じりのちょっと怪しげなパッケージです。 ドライバCDが付いていますが、Windows用のソフトウェアしかないので無視することにします。
検索すると、ラズパイやLinuxサーバでの動作成功例が見つけられましたが、私の家の自宅サーバはSolarisなので、当然Solarisでやってみたくなります。
USBポートに挿してみて、cfgadmで見てみたところ、TEMPerV1.4 としてOSには正しく認識されているようです。これはいけそうな気がしてきました。
$ cfgadm -v Ap_Id Receptacle Occupant Condition Information When Type Busy Phys_Id ... usb2/1.6 connected configured ok Mfg: RDing Product: TEMPerV1.4 NConfigs: 1 Config: 0 <no cfg str descr> unavailable usb-device n /devices/pci@0,0/pci8086,2036@1d/hub@1:1.6 ...
prtconfで見ると、VendorID:0c45, ProductID:7401 のデバイスであることがわかります。
$ prtconf -v System Configuration: Oracle Corporation i86pc Memory size: 8106 Megabytes System Peripherals (Software Nodes): ... name='usb-product-name' type=string items=1 value='TEMPerV1.4' name='usb-vendor-name' type=string items=1 value='RDing' ... name='usb-product-id' type=int items=1 value=00007401 name='usb-vendor-id' type=int items=1 value=00000c45 ...
メーカーサイトではWindows用のソフトウェアしか公開されていないようですが、USBプロトコルアナライザで解析して作られたと思われるLinux用のドライバを作っている方がいました。
https://relavak.wordpress.com/2009/10/17/temper-temperature-sensor-linux-driver/
このブログのレスの中にある pcsensor-0.0.1 は、 0c45:7401 のデバイスに対応しているようなので、試してみることにしました。Linux用ですが、カーネルモジュールではなく、ユーザモードで動作するlibusbというライブラリを使って作られているので、Linuxでなくても動作すると期待できます。実際、FreeBSDでの動作成功例も見つけることができました。
Solarisでとりあえずmakeしてみましたが、だめでした。
$ tar zxvf pcsensor-0.0.1.tar.gz $ cd pcsensor-0.0.1 $ CC=gcc make gcc -DUNIT_TEST -o pcsensor pcsensor.c -lusb pcsensor.c: In function 'interrupt_read': pcsensor.c:235:5: warning: incompatible implicit declaration of built-in function 'bzero' pcsensor.c: In function 'interrupt_read_temperatura': pcsensor.c:254:5: warning: incompatible implicit declaration of built-in function 'bzero' Undefined first referenced symbol in file usb_detach_kernel_driver_np /var/tmp//ccUZxvwd.o ld: fatal: symbol referencing errors collect2: ld returned 1 exit status make: *** [pcsensor] Error 1
libusbのドキュメントを見たところ、usb_detach_kernel_driver_np はLinux専用の関数のようなので、コメントアウトしてみたところ、makeは通るようになりました。
http://www.electric-spoon.com/doc/libusb-dev/html/function.usbdetachkerneldrivernp.html
$ diff pcsensor.c.orig pcsensor.c 77c77 < ret = usb_detach_kernel_driver_np(lvr_winusb, iInterface); --- > ret = 1; //usb_detach_kernel_driver_np(lvr_winusb, iInterface);
実行してみたところ、デバイスを見つけてはいるようなのですが、claimのところでうまくいってないようでした。(後述のmodunloadしてもだめでした)
$ sudo ./pcsensor -v ... lvr_winusb with Vendor Id: c45 and Product Id: 7401 found. usb_open: device ptr is 0x8065f68 usb_open: pindex = 0 Detach failed: Error 0[0] Continuing anyway Detach failed: Error 0[0] Continuing anyway Could not claim interface
libusbのバージョンには0.1系と1.0系があり、このドライバは0.1系を使っていました。libusb-1.0のドキュメントを見ると、Solarisを正式サポートしているようなので、1.0系に対応すれば動作するかもしれないと思いました。
Linux用のドライバ(pcsensorまたはtemper)は派生版がたくさんあるようでしたが、libusb-1.0に対応しているものは見つけられなかったので、対応する意味はそれなりにありそうです。
libusb-0.1系と1.0系ではAPIが異なっており、互換性がありません。とはいえ、関数名はだいたい先頭を usb から libusb に置き換えたものが同等の機能のようなので、ドキュメントを眺めつつなんとなく似た名前の関数に置き換えていきました。libusbの使い方がちょっとだけわかったような気がします。
丸1日ほど格闘した結果、なんとなく動くようになったのでGithubに上げました。
Solaris 11 と RHEL 6(CentOS 6)で正常に動作することを確認しています。いくつかの派生版で、複数デバイスに対応する機能追加が行われていたので、それも取り込んでいます。
Solaris 11 だと↓のような感じでビルドできます。事前にgccとlibusb-1を入れておいてください。
$ sudo pkg install gcc libusb-1 ... $ git clone https://github.com/shakemid/pcsensor-temper $ cd pcsensor-temper $ make ... $ sudo ./pcsensor.sh ...
複数デバイスがある場合は、↓のような感じで列挙されます。1つのデバイスに2つのセンサが付いているものにも対応していますが、私のものは2つ目のセンサ(external)はないので異常な値になっています。
$ sudo ./pcsensor 2017/08/31 19:00:35 Temperature (0:internal) 81.28F 27.38C Temperature (0:external) 214.60F 101.45C 2017/08/31 19:00:35 Temperature (1:internal) 81.16F 27.31C Temperature (1:external) 214.60F 101.45C ...
また、Muninでグラフ化できるようにプラグインも作ってみました。Githubの同じリポジトリの中に置いてあります。
MuninのRRDにはキャリブレーションしていない生の値を記録し、CDEF機能で補正するような作りにしてあります。補正した値をRRDに格納してもよいですが、CDEF機能を使うと、後でキャリブレーションをやり直しても以前に取得した値も補正できる利点があります。 CDEF機能はとっつきづらいですが、逆ポーランド記法(RPN)でRRDに格納された値に様々な計算を加えることができるRRDtoolの機能で、いろいろと応用が利きます。
上記のプラグインの場合は、たとえば↓のように env.cdef オプションで比率とオフセット値を設定できるようになっています。
/path/to/munin/etc/plugin-conf.d/temper ---- [temper] user root env.pcsensor /usr/local/bin/pcsensor env.cdef temperature,1.0287,*,0.85,-
TEMPerのキャリブレーションについては↓の記事がわかりやすいと思います。
Muninでグラフ化すると↓のような感じになります。さすがに8月の室内は30度を超えて暑いですね。
詳細は不明なのですが、どうも16回データを取得すると、17回目にデータを取得できなくなるような事象に遭遇しました。
$ sudo pcsensor 2017/09/01 19:14:57 Temperature (0:internal) 89.60F 32.00C Temperature (0:external) 214.60F 101.45C 16回繰り返す $ sudo pcsensor USB read failed: 0 USB interrupt read: File descriptor in bad state Fatal error> USB read failed
↓で議論されているのと同じ事象のように見えますが、どうも解決していないようです。 github.com
Linuxだと、もう一度実行すると温度を取得できるようなのですが、Solarisだと何度実行しても取得できないままでした。 この状態のときは、何かがUSBデバイスをロックしているように見えたので、HIDドライバをmodunloadしてみたところ、温度を取得できるようになりました。
$ sudo cat /dev/usb/c45.7401/0/if0in1 cat: /dev/usb/c45.7401/0/if0in1: Device busy $ modinfo | grep hid 168 fffffffff7e5d000 5858 20 1 hid (USB HID Client Driver) ... $ sudo modunload -i 168 ...
これはちょっとあんまりな気がするのでもっといい方法があるとよいのですが、今のところ他に方法を見つけられていないので、HIDドライバをmodunloadしてpcsensorコマンドを実行するラッパをpcsensor.shとして公開しています。
というわけで、SolarisでもUSB温度計を使用できるようになりました。めでたしめでたし。
2017/9/14追記
Device::USB::TEMPer1F - search.cpan.org
cpanに登録されているのでcpanmでインストールできます。
$ sudo cpanm -v Device::USB::TEMPer1F ...
↓のようなサンプルスクリプトで温度を取得できました。
#!/usr/bin/perl use strict; use warnings; use Device::USB::TEMPer1F; my $temper = Device::USB::TEMPer1F->new or die $@; print $temper->fetch, "\n";
動作はしたのですが、どうもfetchメソッドでexternalのセンサーの値を読んでいるようなので、internalのセンサーの値を読むように変更しました。これは作者にフィードバックしておこうと思います。
--- TEMPer1F.pm.orig 2017-09-14 10:21:37.907791130 +0900 +++ TEMPer1F.pm 2017-09-14 10:21:48.015163258 +0900 @@ -79,7 +79,7 @@ ) || die "Cannot read the the temperature!\n"; my $r = [unpack "C8", $self->{buffer}]; - return sprintf "%0.2f", $r->[4] + $r->[5]/256; + return sprintf "%0.2f", $r->[2] + $r->[3]/256; } ############################ private methods ##############################
実行例 $ sudo perl temper.pl 33.69
このモジュールだと16回以上取得してもエラーにならないようです。しかし、最初にmodunloadする必要はあるようでした。上記のCプログラムも、このPerlモジュールに倣って、不要そうなini_control_transferのあたりを削除したらエラーが出なくなりました。
ずいぶんシンプルになりました。今までの苦労はいったい。でもだいぶ勉強になった気がしました。
2017/9/28追記
eBayで、センサーが2つ付いたTEMPer2と、湿度計が付いたTEMPerHUMも買って対応してみました。 なんだかTEMPer工場みたいになってしまいました。はじめは部屋の温度を記録したかっただけなのにすっかり手段と目的が入れ替わってます。 USBハブに直接挿すと熱が伝わってしまうので、100円ショップのUSB延長ケーブルを使っています。
eBayで買うとだいたい10日~2週間くらいで届きました。ちょっと納期はかかりますが値段は日本のAmazonで買うより安めです。 stores.ebay.com
ソースコードはGithubで公開しています。 github.com
温度と湿度の計算式はこちらをだいぶ参考にしました。 github.com
かなり車輪の再発明感はありますが、より簡単にビルドできて、Solarisでも動きます。Solarisでも動きます。(大事なことなので2回言いました)