久しぶりにNASにログインしたら
・DISK4が認識していません。
・DISK1が壊れそうです。
とのエラー表示。
| メーカー | 型番 | 内容 | 購入先 | 個数 | 値段 |
| 東芝 | MQ04ABD200 | 2.5インチHDD 2TB | ノジマオンライン | 1個 | 6,045円(税込) |
久しぶりにNASにログインしたら
・DISK4が認識していません。
・DISK1が壊れそうです。
とのエラー表示。
| メーカー | 型番 | 内容 | 購入先 | 個数 | 値段 |
| 東芝 | MQ04ABD200 | 2.5インチHDD 2TB | ノジマオンライン | 1個 | 6,045円(税込) |
自宅にはWxBeacon2とSwitchBotの温湿度計が各々1台ずつあるのだが、 先日のスクリプトだと1台のセンサから情報取得するのに毎回5秒かかるので1分間には最大12個しか取れない。クラス定義のscanを読むとタイムアウトまでの時間で見つかったデバイス全てに処理をしているようだったので、チェックしたいデバイスのMacアドレスをリストで渡すと、その中で見つかったセンサのデータを返すようにしてみた。
センサからのデータを成形したりbluepyを使う部分は前回同様に @c60evaporatorさんの記事のコードそのまま。
WxBeacon2のブロードキャストモードへの変更やSwitchBotのMACアドレスの取得、周辺モジュールのインストールや設定は上記の記事を参照の事。
「env_broadcast.py」、「bluetoothsensor.py」を同一ディレクトリに書くのすれば動くはず。
「env_broadcast.py」は、リスト型で調査対象のMACアドレスを一覧で受け取る。スキャン時に一致するMACアドレスが見つかるとWxBeacon2かSwitchBotの温湿度計かを自動判別してセンサ情報を取得する。取得したデータはMACアドレスをキーにしたdict型(バリューも更にdict型)に格納する。同じMACアドレスで2度取れてしまった際は上書きしている。
env_broadcast.py
from bluepy import btle
import struct
class ScanDelegate(btle.DefaultDelegate):
#コンストラクタ
def __init__(self, macaddrs):
btle.DefaultDelegate.__init__(self)
#センサデータ保持用変数
self.macaddr = macaddrs
self.sensorValue = dict()
for mac in macaddrs:
self.sensorValue[mac] = {'SensorType':'None'}
# スキャンハンドラー
def handleDiscovery(self, dev, isNewDev, isNewData):
# 新しいデバイスが見つかったら
if isNewDev or isNewData:
# ターゲットのMACアドレス(環境センサ)であるか
# print('dev.addr in self.macaddr ->', dev.addr, self.macaddr, dev.addr in self.macaddr)
if dev.addr in self.macaddr:
# アドバタイズデータを取り出し
for (adtype, desc, value) in dev.getScanData():
# 環境センサのとき、データ取り出しを実行
# まずWxBeacon2/Omronの場合
if desc == 'Manufacturer' and value[0:4] == 'd502':
#センサの種類(EP or IM)を取り出し
sensorType = dev.scanData[dev.SHORT_LOCAL_NAME].decode(encoding='utf-8')
#EPのときのセンサデータ取り出し
if sensorType == 'EP':
self.decodeSensorData_EP(value, dev.addr)
#IMのときのセンサデータ取り出し
if sensorType == 'IM':
self.decodeSensorData_IM(value, dev.addr)
# SwitchBotの温湿度計の場合
else:
for (adtype, desc, value) in dev.getScanData():
#環境センサのとき、データ取り出しを実行
if desc == '16b Service Data':
#センサデータ取り出し
self._decodeSensorData(value, dev.addr)
# センサデータを取り出してdict形式に変換(EPモード時)
def decodeSensorData_EP(self, valueStr, macaddr):
#文字列からセンサデータ(6文字目以降)のみ取り出し、バイナリに変換
valueBinary = bytes.fromhex(valueStr[6:])
#バイナリ形式のセンサデータを整数型Tapleに変換
(temp, humid, light, uv, press, noise, discomf, wbgt, rfu, batt) = struct.unpack('<hhhhhhhhhB', valueBinary)
#単位変換した上でdict型に格納
self.sensorValue[macaddr] = {
'SensorType': 'EP',
'Temperature': temp / 100,
'Humidity': humid / 100,
'Light': light,
'UV': uv / 100,
'Pressure': press / 10,
'Noise': noise / 100,
'Discomfort': discomf / 100,
'WBGT': wbgt / 100,
'BatteryVoltage': (batt + 100) / 100
}
# センサデータを取り出してdict形式に変換(IMモード時)
def decodeSensorData_IM(self, valueStr, macaddr):
#文字列からセンサデータ(6文字目以降)のみ取り出し、バイナリに変換
valueBinary = bytes.fromhex(valueStr[6:])
#バイナリ形式のセンサデータを整数型Tapleに変換
(temp, humid, light, uv, press, noise, accelX, accelY, accelZ, batt) = struct.unpack('<hhhhhhhhhB', valueBinary)
#単位変換した上でdict型に格納
self.sensorValue[macaddr] = {
'SensorType': 'IM',
'Temperature': temp / 100,
'Humidity': humid / 100,
'Light': light,
'UV': uv / 100,
'Pressure': press / 10,
'Noise': noise / 100,
'AccelerationX': accelX / 10,
'AccelerationY': accelY / 10,
'AccelerationZ': accelZ / 10,
'BatteryVoltage': (batt + 100) / 100
}
# センサデータを取り出してdict形式に変換
def _decodeSensorData(self, valueStr, macaddr):
#文字列からセンサデータ(4文字目以降)のみ取り出し、バイナリに変換
valueBinary = bytes.fromhex(valueStr[4:])
#バイナリ形式のセンサデータを数値に変換
batt = valueBinary[2] & 0b01111111
isTemperatureAboveFreezing = valueBinary[4] & 0b10000000
temp = ( valueBinary[3] & 0b00001111 ) / 10 + ( valueBinary[4] & 0b01111111 )
if not isTemperatureAboveFreezing:
temp = -temp
humid = valueBinary[5] & 0b01111111
#dict型に格納
self.sensorValue[macaddr] = {
'SensorType': 'SwitchBot',
'Temperature': temp,
'Humidity': humid,
'BatteryVoltage': batt
}
#!/usr/bin/python3
from bluepy import btle
from env_broadcast import ScanDelegate
import time
while True:
# 収集したいセンサのMACアドレスをリスト型で列挙(MACアドレスは小文字で記載)
sensor_mac_list = ["c9:43:c3:XX:XX:XX","c5:d7:b1:XX:XX:XX"]
# センサ値取得デリゲートを、スキャン時実行に設定
scanner = btle.Scanner().withDelegate(ScanDelegate(sensor_mac_list))
#スキャンしてセンサ値取得(タイムアウト10秒。10秒間で見つかったbluetoothデバイス全てにチェックを実施)
scanner.scan(10.0)
# 取得した値一覧
# print(scanner.delegate.sensorValue)
if scanner.delegate.sensorValue is not None:
for macaddr in sensor_mac_list:
if scanner.delegate.sensorValue[macaddr]['SensorType'] == 'EP':
print("update /XXXX/RRD/wxbeacon2.rrd "
+ str(int(time.time())) + ':'
+ str(scanner.delegate.sensorValue[macaddr]['BatteryVoltage']) + ':'
+ str(scanner.delegate.sensorValue[macaddr]['Temperature']) + ':'
+ str(scanner.delegate.sensorValue[macaddr]['Humidity']) + ':'
+ str(scanner.delegate.sensorValue[macaddr]['Light']) + ':'
+ str(scanner.delegate.sensorValue[macaddr]['UV']) + ':'
+ str(scanner.delegate.sensorValue[macaddr]['Pressure']) + ':'
+ str(scanner.delegate.sensorValue[macaddr]['Noise']) + ':'
+ str(scanner.delegate.sensorValue[macaddr]['Discomfort']) + ':'
+ str(scanner.delegate.sensorValue[macaddr]['WBGT']), flush=True)
elif scanner.delegate.sensorValue[macaddr]['SensorType'] == 'IM':
print("update /XXXX/NoName.rrd "
+ str(int(time.time())) + ':'
+ str(scanner.delegate.sensorValue[macaddr]['BatteryVoltage']) + ':'
+ str(scanner.delegate.sensorValue[macaddr]['Temperature']) + ':'
+ str(scanner.delegate.sensorValue[macaddr]['Humidity']) + ':'
+ str(scanner.delegate.sensorValue[macaddr]['Light']) + ':'
+ str(scanner.delegate.sensorValue[macaddr]['UV']) + ':'
+ str(scanner.delegate.sensorValue[macaddr]['Pressure']) + ':'
+ str(scanner.delegate.sensorValue[macaddr]['Noise']) + ':'
+ str(scanner.delegate.sensorValue[macaddr]['AccelerationX']) + ':'
+ str(scanner.delegate.sensorValue[macaddr]['AccelerationY']) + ':'
+ str(scanner.delegate.sensorValue[macaddr]['AccelerationZ']) + ':'
+ str(scanner.delegate.sensorValue[macaddr]['AccelerationX']), flush=True)
elif scanner.delegate.sensorValue[macaddr]['SensorType'] == 'SwitchBot':
print('update /XXXX/RRD/swbot.rrd '
+ str(int(time.time())) + ':'
+ str(scanner.delegate.sensorValue[macaddr]['Temperature']) + ':'
+ str(scanner.delegate.sensorValue[macaddr]['Humidity']) + ':'
+ str(scanner.delegate.sensorValue[macaddr]['BatteryVoltage']), flush=True)
elif scanner.delegate.sensorValue.get[macaddr]['SensorType'] == 'None':
# 時間内にデバイスが見つからなかった
pass
else:
# 想定外
print(scanner.delegate.sensorValue)
time.sleep(50)
実行結果は下記の感じなので、rrdtoolが動いているサーバには「./bluetoothsensor.py |nc XXX.XXX.XXX.XXX 42217 >/dev/null &」な感じで飛ばしている。
$ ./bluetoothsensor.py update /XXXX/RRD/swbot.rrd 1629028238:26.4:66:100 update /XXXX/RRD/wxbeacon2.rrd 1629028238:2.91:19.63:100.0:0:0.01:1010.1:31.9:67.33:23.03
(8/15)追記
この記事では1回のSCANで1デバイスから取得しているが1回のSCANで複数のデバイスからデータ取得できるのを記事にした(URL)。
--------------
「Bluetooth気温計のデータをラズパイで取得しrrdtoolで描画できるようにした。」のセンサ接続とrrdtoolに繋げた部分について
@c60evaporatorさんの記事
を参考にして作成。
まずはラズパイ3の1台に各記事ごとに設定して動作の確認。
「2」の方の記事ではまったのは、
sudo install libglib2.0-dev
との記載は「sudo apt-get install libglib2.0-dev」 かと。てっきりripが抜けているのかとか悩んだ(linux,Python初心者なので勘がきかない)。
あとメインスクリプトの部分でMacアドレスを渡さないといけないのと、Macアドレスを小文字で書かないと駄目なのにはまる。
同一ディレクトリに、「1」の記事の「omron_env_broadcast.py」と「2」の記事の「switchbot.py」を放り込んで、両記事のメインスクリプトを合体させ、scanして見つからなかった際の処理を追加して出力をrrdtoolに都合の良いものに書き換えたbluetoothsensor.pyを放り込む。
(scanを1回で両方のデータを取れるようにできそうだけど、後日頑張ろう)
bluetoothsensor.py
#!/usr/bin/python3
from bluepy import btle
from omron_env_broadcast import ScanDelegate
from switchbot import SwitchbotScanDelegate
import datetime
import time
while True:
dt_now = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
sb_meter = { "kitchen":"c9:43:c3:xx:xx:xx"}
#omron_env_broadcast.pyのセンサ値取得デリゲートを、スキャン時実行に設定
scanner = btle.Scanner().withDelegate(ScanDelegate())
#スキャンしてセンサ値取得(タイムアウト5秒)
scanner.scan(5.0)
if scanner.delegate.sensorValue is not None:
#print(dt_now + "," + "OM, " + "update /home/XXXX/RRD/wxbeacon2.rrd "
print("update /home/XXXX/RRD/wxbeacon2.rrd "
+ str(int(time.time())) + ":"
+ str(scanner.delegate.sensorValue['BatteryVoltage']) + ":"
+ str(scanner.delegate.sensorValue['Temperature']) + ":"
+ str(scanner.delegate.sensorValue['Humidity']) + ":"
+ str(scanner.delegate.sensorValue['Light']) + ":"
+ str(scanner.delegate.sensorValue['UV']) + ":"
+ str(scanner.delegate.sensorValue['Pressure']) + ":"
+ str(scanner.delegate.sensorValue['Noise']) + ":"
+ str(scanner.delegate.sensorValue['Discomfort']) + ":"
+ str(scanner.delegate.sensorValue['WBGT']), flush=True)
######SwitchBotの値取得######
for key in sb_meter:
#switchbot.pyのセンサ値取得デリゲートを、スキャン時実行に設定
scanner = btle.Scanner().withDelegate(SwitchbotScanDelegate(sb_meter[key]))
#スキャンしてセンサ値取得(タイムアウト5秒)
scanner.scan(5.0)
if scanner.delegate.sensorValue is not None:
#print(dt_now + "," + "SW, " + "update /home/XXXXX/RRD/swbot.rrd "
print("update /home/XXXX/RRD/swbot.rrd "
+ str(int(time.time())) + ":"
+ str(scanner.delegate.sensorValue['Temperature']) + ":"
+ str(scanner.delegate.sensorValue['Humidity']) + ":"
+ str(scanner.delegate.sensorValue['BatteryVoltage']), flush=True)
time.sleep(50)
$ ./bluetoothsensor.py update /home/xxxxx/RRD/wxbeacon2.rrd 1628919666:2.92:21.5:99.0:76:0.02:999.7:35.89:70.63:24.46 update /home/xxxxx/RRD/swbot.rrd 1628919671:27.0:59:100 update /home/xxxxx/RRD/wxbeacon2.rrd 1628919726:2.92:21.5:99.03:81:0.02:999.8:31.58:70.63:24.46 update /home/xxxxx/RRD/swbot.rrd 1628919731:27.0:58:100
のようなものが1分ごとに出てきます。ラズパイ側にデータをためるつもりはないのでデータ保存兼画像作成のサーバ側でrrdcachedを立ち上げラズパイから渡します。
サーバはNetBSD 9.2なので、「pkgin install rrdtool 」して「usr/pkg/bin/rrdcached -p /var/run/rrdcached/rrdcached.pid -l xxx.xxx.xxx.xxx:42217 -U rrdcached -w 60」(-lの後のxxxの部分は自サーバのIFについているIPv4アドレス)で立ち上げる。
ラズパイ側から疎通確認のために「telnet xxx.xxx.xxx.xxx 42217」で繋いで、「HELP」とか打ち込んで、
$ telnet XXX.XXX.XXX.XXX 42217 Trying XXX.XXX.XXX.XXX... Connected to XXX.XXX.XXX.XXXX. Escape character is '^]'. HELP 22 Command overview UPDATE <filename> <values> [<values> ...] FLUSH <filename> FLUSHALL PENDING <filename> FORGET <filename> ....と出れば接続OK.QUITで抜ける。あとはラズパイ側でNC(NetCat)を利用して、「./bluetoothsensor.py |nc xxx.xxx.xxx.xxx 42217 &」とかで直接投げ込めば更新完了。 うまく行かない際はサーバ側のRRDファイルのパーミッションがユーザ「rrdcached」での書き込み権限があるか確認。 サーバ側で更新されているか確認は「rrdtool last RRDファイル名」などで最終更新日を確認する。以下にrrdtoolでのRRDの作成と画像のパラーメータを載せておく。 細かいオプションは「RRDtool 1.2系を使う」を参照の事。
rrdtool create wxbeacon2.rrd --start "00:00 07/01/2021" \
--step 60 \
--no-overwrite \
DS:Battery:GAUGE:600:0:U \
DS:Temperature:GAUGE:600:U:U \
DS:Humidity:GAUGE:600:0:100 \
DS:Light:GAUGE:600:0:U \
DS:UV:GAUGE:600:0:U \
DS:Pressure:GAUGE:600:0:U \
DS:Noise:GAUGE:600:0:U \
DS:Discomfort:GAUGE:600:U:U \
DS:HeatStrokeRisk:GAUGE:600:U:U \
RRA:MAX:0.5:1:5256000
rrdtool create swbot.rrd --start "00:00 07/01/2021" \
--step 60 \
--no-overwrite \
DS:Temperature:GAUGE:600:U:U \
DS:Humidity:GAUGE:600:0:100 \
DS:BatteryVoltage:GAUGE:600:0:U \
RRA:MAX:0.5:1:5256000
/usr/pkg/bin/rrdtool graph temp-2week.png -h 300 -w 500 -s 'now - 2weeks' \
DEF:a=/XXXX/RRD/usbrh.rrd:Temperature:MAX \
DEF:b=/XXXX/RRD/wxbeacon2.rrd:Temperature:MAX \
DEF:c=/XXXX/RRD/swbot.rrd:Temperature:MAX \
LINE2:a#FF0000:"usbrh(室内)\n" \
LINE2:b#00FF00:"wx2(庭)\n" \
LINE2:c#00FFFF:"SWbot(台所)" -t "気温" -v "degC"
/usr/pkg/bin/rrdtool graph temp-2day.png -h 300 -w 500 -s 'now - 172800' \
DEF:a=/XXXX/RRD/usbrh.rrd:Temperature:MAX \
DEF:b=/XXXX/RRD/wxbeacon2.rrd:Temperature:MAX \
DEF:c=/XXXX/RRD/swbot.rrd:Temperature:MAX \
LINE2:a#FF0000:"usbrh(室内)\n" \
LINE2:b#00FF00:"wx2(庭)\n" \
LINE2:c#00FFFF:"SWbot(台所)" -t "気温" -v "degC"
/usr/pkg/bin/rrdtool graph temp-60min.png -h 300 -w 500 -s 'now - 3600' \
DEF:a=/XXXX/RRD/usbrh.rrd:Temperature:MAX \
DEF:b=/XXXX/RRD/wxbeacon2.rrd:Temperature:MAX \
DEF:c=/XXXX/RRD/swbot.rrd:Temperature:MAX \
LINE2:a#FF0000:"usbrh(室内)\n" \
LINE2:b#00FF00:"wx2(庭)\n" \
LINE2:c#00FFFF:"SWbot(台所)" -t "気温" -v "degC"
「Bluetooth気温計のデータをラズパイで取得しrrdtoolで描画できるようにした。」のパゴダの作成部分の記事。
パゴダは10年前に一度作成していて勝手は分かっている。前回は軒下につるしたところ、直射日光が壁に当たり、その温まった空気がパコダに入り込むという問題があった。USBでセンサを繋いでいたので遠くに置けないこともあり、あきらめていたがbluetooth接続のWxBeacon2が手に入ったので庭に設置の前提で再度作り直してみた。
以前参照していたページもナチュラル研究所と引っ越していました。
材料費
最初は5号皿4枚で作成した。
直射日光が当たると数字がおかしい感じがしたので、対策を実施。「Bluetooth気温計のデータをラズパイで取得しrrdtoolで描画できるようにした。」のusbrhのコードを再度書き直したについて
10年前にUSB温度・湿度計モジュール(以下usbrh)を連続的にとれるコードを書いていたが、サーバのHDDが壊れるとともに消えてしまった模様。
しょうがいないのでNetBSDのvmstatのコードを参考にしつつ連続的に取得するコードを追加。後日、GitHubにでも置いてみるかなぁ。使ったことないからよくわからん。
コマンドの使い方はこんな感じ
./usbrh -h usage: usbrh [-v] [-f device] [-c count] [-w wait] [-t timeformat] timeformat: unix=unix time, at=at time, default:RFC3339 ./usbrh -w 60 -t unix 1628922942 28.72 63.7
rrdtoolに入れる際はこんな感じ
./usbrh -w 60 -t unix | awk '{ print "update /XXXX/usbrh.rrd " $1 ":" $2 ":" $3; fflush()}' | rrdtool - &
ちなみにRRDファイルの作成は以下のコマンドで実施
rrdtool create usbrh.rrd --start "00:00 07/01/2021" \
--step 60 \
--no-overwrite \
DS:Temperature:GAUGE:600:U:U \
DS:Humidity:GAUGE:600:0:100 \
RRA:MAX:0.5:1:5256000
GitHubにおいてないので、いったんにここに書くが、まずは
USBRH on *BSDからtar玉をダウンロードしてmain.cを下記と入れ替えてコンパイルする。もとのaoyamaさんのコードがBSDライセンスなので、自分で書いた部分のライセンスをBSDライセンスの文言でさらに追加すればいいのかしら。。。/*
* $Id: main.c,v 1.4 2007/01/10 13:11:30 aoyama Exp $
*
* Copyright (c) 2006 Kenji AOYAMA <aoyama@nk-home.net>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <sys/ioctl.h>
#include <sys/param.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <time.h>
#include <dev/usb/usb.h>
#include <dev/usb/usbhid.h>
/* prototypes */
void usage(void);
int main(int, char *[]);
int check_device(char *);
#define USBRH_VENDOR 0x1774
#define USBRH_PRODUCT 0x1001
#define DEFAULT_DEVICE "/dev/uhid0"
extern char *__progname;
extern char *optarg;
extern int optind;
extern float calc_temp(unsigned int);
extern float calc_humid(unsigned int, float);
int vflag = 0;
int
main(int argc, char *argv[])
{
struct usb_ctl_report ucr;
struct timespec interval;
int ch, fd, ret;
int reps;
float t, h;
u_char buf[8];
char date[64];
char devname[MAXPATHLEN];
time_t ti;
int uflag, aflag, rflag;
reps = 0;
uflag=aflag=rflag=0;
interval.tv_sec = 0;
interval.tv_nsec = 0;
strlcpy(devname, DEFAULT_DEVICE, sizeof(devname));
while ((ch = getopt(argc, argv, "c:f:ht:vw:")) != -1) {
switch(ch) {
case 'c':
reps = atoi(optarg);
break;
case 'f':
strlcpy(devname, optarg, sizeof(devname));
break;
case 'h':
usage();
break;
case 't':
if(strcmp(optarg,"unix")==0){
uflag = 1;
}else if(strcmp(optarg,"at")==0){
aflag = 1;
}else{
rflag = 1;
}
break;
case 'v':
vflag = 1;
break;
case 'w':
interval.tv_sec = atol(optarg);
break;
default:
usage();
/* NOTREACHED */
}
}
argc -= optind;
argv += optind;
if (check_device(devname) != 1) {
fprintf(stderr, "%s: can not find USBRH device on %s\n",
__progname, devname);
exit(1);
}
if ((fd = open(devname, O_RDWR)) < 0) {
perror(devname);
exit(1);
}
/*
* XXX: If we issue SET_REPORT only once, read(2) sometimes
* blocks. So we issue SET_REPORT twice, with 1 second interval,
* that seems OK.
*/
ucr.ucr_report = UHID_OUTPUT_REPORT;
if (ioctl(fd, USB_SET_REPORT, &ucr) < 0) {
perror("USB_SET_REPORT");
exit(1);
}
sleep(1);
for(;;){
if (interval.tv_sec) {
if (!reps)
reps = -1;
} else if (reps){
interval.tv_sec = 1;
}
ucr.ucr_report = UHID_OUTPUT_REPORT;
if (ioctl(fd, USB_SET_REPORT, &ucr) < 0) {
perror("USB_SET_REPORT");
exit(1);
}
if ((ret = read(fd, buf, 7)) == -1) {
fprintf(stderr, "read error\n");
exit(1);
}
// close(fd);
/*
* Now we got 7 bytes data from the device.
*
* buf[0] Humidity raw data (high byte)
* buf[1] Humidity raw data (low byte)
* buf[2] Temperature raw data (high byte)
* buf[3] Temperature raw data (low byte)
* buf[4] ???
* buf[5] ???
* buf[6] ???
*/
/* Calculate the real values */
t = calc_temp(buf[2] * 256 + buf[3]);
h = calc_humid(buf[0] * 256 + buf[1], t);
ti = time(NULL);
if(uflag){
sprintf(date,"%ld", ti);
}else if(aflag){
strftime(date, sizeof(date), "%H:%M %m/%d/%Y", localtime(&ti));
}else if(rflag){
strftime(date, sizeof(date), "%Y-%m-%dT%H:%M:%S%z", localtime(&ti));
}
if (vflag) {
printf("Date\tTemperature\tHumidity\n");
}
printf("%s %.2f %.1f\n", date, t, h);
fflush(stdout);
if (reps >= 0 && --reps <=0)
break;
(void)nanosleep(&interval, NULL);
}
return 0;
}
void
usage(void)
{
fprintf(stderr, "usage: %s [-v] [-f device] [-c count] [-w wait] [-t timeformat]\n", __progname);
fprintf(stderr, "timeformat: unix=unix time, at=at time, default:RFC3339\n");
exit(1);
}
/*
* Check USBRH vendor ID and product ID on specified device.
* Return 1 if matches.
*/
int
check_device(char *devname)
{
int fd;
struct usb_device_info udi;
#if defined(__NetBSD__) || defined(__OpenBSD__)
/* On NetBSD/OpenBSD, /dev/uhid? can accept USB_GET_DEVICEINFO ioctl. */
if ((fd = open(devname, O_RDWR)) < 0) {
perror(devname);
exit(1);
}
if (ioctl(fd, USB_GET_DEVICEINFO, &udi) < 0) {
perror("USB_GET_DEVICEINFO");
exit(1);
}
close(fd);
/* vendor and product ID check */
if ((udi.udi_vendorNo == USBRH_VENDOR) &&
(udi.udi_productNo == USBRH_PRODUCT)) {
return 1;
}
return 0;
#else
/* Otherwise, walk through from /dev/usb? devices. */
int cnt, addr;
char buf1[MAXPATHLEN], buf2[MAXPATHLEN];
for (cnt = 0; cnt < 10; cnt++) { /* XXX: is this enough? */
snprintf(buf1, sizeof(buf1), "/dev/usb%d", cnt);
fd = open(buf1, O_RDONLY);
if (fd < 0)
continue;
for (addr = 1; addr < USB_MAX_DEVICES; addr++) {
udi.udi_addr = addr;
if (ioctl(fd, USB_DEVICEINFO, &udi) < 0)
continue;
strlcpy(buf2, "/dev/", sizeof(buf2));
strlcat(buf2, udi.udi_devnames[0], sizeof(buf2));
#ifdef DEBUG
printf("%s Addr %d, Vendor 0x%04x, Product 0x%04x, "
"%s, %s, %s, %s, %s\n",
buf1, addr, udi.udi_vendorNo, udi.udi_productNo,
udi.udi_devnames[0], udi.udi_devnames[1],
udi.udi_devnames[2], udi.udi_devnames[3],
buf2);
#endif
if ((strcmp(devname, buf2) == 0) &&
(udi.udi_vendorNo == USBRH_VENDOR) &&
(udi.udi_productNo == USBRH_PRODUCT)) {
return 1;
}
}
close(fd);
}
return 0;
#endif
}
Bluetooth経由で温度が取れるセンサーをRaspberry Pi 3 Model Bで取得してrrdtoolで描画するようにしてみました。
用意したもの
構成は下記の感じ。
下記は別記事にした。
以前、作った滑り台の台を段ボールにしていたが、だんだんとボロボロになってきたのでちゃんとした台を作ることにした。
参考にしたのは「2×4材(6F)1本で出来る踏み台♪」とか「1×6材で子供用2段ステップを手作り 1」とか。
オリンピック4連休の3日目にホームセンターで木材を買っておいて、最終日に作成した。この4連休はめちゃくちゃ暑かった。
踏み台の要件は2段あるタイプで、底面のサイズが30 cm x 30 cmで収まることにした。
踏むところが1x6か2x6で悩んだ。1(=19mm)だけだとたわむだろうと思い倍の厚さにするか横を補強するように渡すか。結果2(=38mm)の厚さだと持ち運ぶ際に重いのとねじ止めが面倒で、ネジも長いのを追加購入する必要があると思い板は増えるが1x6 3フィートを3枚で作ることにする。
設計図
自分で鋸を使って精度よく切れないので、カットはホームセンターに依頼してしまった。価格は下記。あら、3か月で1割以上の値上がりかな。
滑り台を載せても落ちないように彫刻刀で引っ掛けるところを掘って完成。
結構、次男が椅子にして遊んでいる。