Contents
- サンワサプライ社製 ワイヤレス温湿度ロガーUNI-01-B002
- 1. 目的
- 2. メーカー推奨の接続方法での動作確認
- 3. iphoneの汎用BLEツール(LightBlue)で接続
- 4. pythonのBleakライブラリで接続
- 4-1. UNI-01-B002をスキャンで検索
- 4-2. UNI-01-B002のUUIDを調査
- 5.データの解析
- 5.1.温度
- 5.2.湿度
- 6.PDHでの温湿度読取り
- 6-1.notifyデータの読取り
- 6-2.Node-REDでのダッシュボード表示
- 7. UNI-01-B002のBLE接続に関する補足事項
- Appendix.1 温度の直線近似プログラム
- Appendix.2 湿度の直線近似プログラム
- Appendix.3 BLE接続で温湿度を読み取るプログラム
サンワサプライ社製 ワイヤレス温湿度ロガーUNI-01-B002
1. 目的
サンワサプライ(株)のワイヤレス温湿度ロガー UNI-01-B002は、BLE(Bluetooth Low Energy)を搭載し、温度と湿度を定期的に送信してくる。このセンサをR-CPSの拡張センサとして使用し、温度と湿度のデータを取得する方法を検討する。
2. メーカー推奨の接続方法での動作確認
apple storeから無償のワイヤレス温湿度計を取得する(左上の図)。起動し、センサ検索を行い、“TH Sensor”に接続すると、右の図のように温度と湿度が表示される。座標表示を選ぶと下の図のように2次元グラフで表示される。送信周期は2秒、10秒、30秒、60秒(1分)、300秒(5分)が選べる。
3. iphoneの汎用BLEツール(LightBlue)で接続
LightBlueで検索を掛けるとTH Sensorとして検出される。
⑥ をクリックするとバッテリーレベルのRead/Notifyで、⑦をクリックすると温度/湿度のNotifyと予想される。⑦は4秒おきに更新されるため。
4. pythonのBleakライブラリで接続
BLEで接続するためには、BLE機器のMACアドレスが必要です。MACアドレスは、パッケージにも記載されています(F0:AB:54:4B:A8:8C)。
取得方法の詳細は、R-CPS-HP 第6巻 5-1. BLE機器との接続の1-3. BLE接続に必要な情報を参照ください。
4-1. UNI-01-B002をスキャンで検索
ターミナル画面で以下のコマンドを実行し、“TH Sensor”が表示されるかを調べます。
$ python3 scan_ble_devices.py
結果を以下に示します。上記のMACアドレスと同じアドレスで“TH Sensor”が表示されています。
4-2. UNI-01-B002のUUIDを調査
次に、ターミナル画面で以下のコマンドを実行し、UUIDを調べます。
$ python3 get_device_uuids.py F0:AB:54:4B:AB:BC
結果を以下に示します。Light Blueの画面の⑦の[‘read’,’notify’]サービスのUUIDが見つかります。
5.データの解析
iphoneのアプリ「無線温湿度」に表示される温湿度と「LightBlue」の画面の⑦のUUIDを呼んだ時の値の相関を検討します。
「無線温湿度」と「LightBlue」は同時には読めないので、交互に値を呼んで記録した結果をもとに、直線近似で直線を求めます。
5.1.温度
これらのデータから、まず、温度に関して、「無線温湿度」と「LightBlue」の相関が分かるデータを選び出します。
Pythonで直線近似をさせて、傾きと切片を求めます。Pythonの直線近似のプログラムは、Appendix.1に示しますので参考にしてください。
■ pythonでの直線近似の結果
傾き:m= 0.002681276582012078
切片:c= -46.850169942798054
■ 直線近似用に準備したデータと直線近似での値
早朝の窓を開けた低い温度と昼間の暖房を掛けた時の温度を使用して直線近似の精度を上げています。
■ 直線近似の結果のグラフ
5.2.湿度
湿度も同様に、「無線温湿度」と「LightBlue」の相関が分かるデータを選び出します。読取れた値からPythonで直線近似をさせて、傾きと切片を求めます。
湿度も早朝の気温が低い時の湿度が高いデータと昼間のエアコンが効いている湿度が低いデータを組み合わせています。
Pythonの直線近似のプログラムは、Appendix.2に示しますので参考にしてください。
■ pythonでの直線近似の結果
傾き:m= 0.0019073492524788495
切片:c= -6.000100924682605
■ 直線近似用に準備したデータと直線近似での値
■ 直線近似の結果のグラフ
6.PDHでの温湿度読取り
前項での直線近似を使って、PDHで温湿度ロガー“UNI-01-B002”からnotifyされる温湿度を読取り、ダッシュボードに表示させます。ダッシュボードへの表示結果を以下に示します。
6-1.notifyデータの読取り
notifyのデータを読み取るために、UNI-01-B002にBLE(Bluetooth Low Energy)で接続する必要があります。
“4. pythonのBleakライブラリで接続”で調べたMACアドレスとサービスのuuidを使用します。
MACアドレス F0:AB:54:4B:A8:8C
サービスのUUID f7eeaa21-276e-4165-aa69-7e3de7fc627e
読取ったデータを、“5.データの解析”で求めた直線近似の傾きと切片を使って、温度、湿度に変換して、mqttでlocalhostへpubuhishするまでのフローを次に示します。プログラムはpythonで記載しています。コードはAppendix.3を参照ください。
6-2.Node-REDでのダッシュボード表示
次にNode-REDのフロー画面を示します。デバッグ画面に読み取った温度(temp)と湿度(humid)が表示されています。
① mqtt inで受け取って、
② changeノードで温湿度のデータを振り分け
③ dashboardのノードでゲージとチャートを表示しています。
ダッシュボードの画面は章の初めをご覧ください。
7. UNI-01-B002のBLE接続に関する補足事項
温湿度ロガー“UNI-01-V002”はBLEの接続がうまく行かないことが多いです。
その場合には,下の図の④スイッチを長押しして下さい(「温湿度ロガー(超小型・無線・iPhone専用・Bluetooth) UNI-01-B002」の「取扱説明書」参照)。
説明の“接続② コネクトモードの時”にあるように存在通知の時間を10秒毎から1秒毎に変更できます。これによって、接続がかなりうまくできるようになります。
ただし、pythonのプログラムからはTimeout Errorが良く出て、接続できないことがあります。その場合は、上の長押しをやって、何度もトライしてみてください。
Appendix.1 温度の直線近似プログラム
import numpy as np
import matplotlib.pyplot as plt
# データ
Y = [3.77, 3.86, 3.94, 4.03, 4.12, 4.20, 4.29, 4.37, 4.46, 4.54, 4.63, 4.72, 4.80, 4.89, 4.97, 5.06, 5.15, 5.23, 21.45, 21.53, 21.62, 21.70, 21.79, 21.88, 21.96, 22.05, 22.13, 22.22, 22.31, 22.39, 22.48, 22.56, 22.65, 22.73, 22.82, 22.91, 22.99, 23.08, 16.47, 24.02]
X_hex = ['0x49C0', '0x49E0', '0x4A00', '0x4A20', '0x4A40', '0x4A60', '0x4A80', '0x4AA0', '0x4AC0', '0x4AE0', '0x4B00', '0x4B20', '0x4B40', '0x4B60', '0x4B80', '0x4BA0', '0x4BC0', '0x4BE0', '0x6380', '0x63A0', '0x63C0', '0x63E0', '0x6400', '0x6420', '0x6440', '0x6460', '0x6480', '0x64A0', '0x64C0', '0x64E0', '0x6500', '0x6520', '0x6540', '0x6560', '0x6580', '0x65A0', '0x65C0', '0x65E0', '0x5C40', '0x6740']
# 16進数を10進数に変換
X = [int(x, 16) for x in X_hex]
# 近似直線を求める
A = np.vstack([X, np.ones(len(X))]).T
m, c = np.linalg.lstsq(A, Y, rcond=None)[0]
print("m= ",m,"c=",c);
#近似直線で計算した結果を表示する
out = np.round(m * np.array(X) + c, 2)
print("out=",out)
# プロット
plt.plot(X, Y, 'o', label='Original data', markersize=10)
plt.plot(X, m*np.array(X) + c, 'r', label='Fitted line')
plt.legend()
plt.show()
Appendix.2 湿度の直線近似プログラム
基本、プログラムは温度と同じです。データが異なるだけです。
import numpy as np
import matplotlib.pyplot as plt
# データ
Y = [34.95, 36.85, 36.91, 37.09, 37.15, 37.33, 37.64, 37.70, 37.88, 38.01, 38.13, 38.19, 38.25, 38.74, 38.98, 39.04, 39.17, 39.41, 39.78, 39.84, 39.96, 40.14, 40.20, 40.33, 40.39, 40.75, 40.81, 40.88, 67.19, 67.24, 67.36, 65.17, 65.23, 65.96, 66.63, 69.07, 69.38, 70.05, 70.11, 70.84, 71.27]
X_hex = ['0x53E0', '0x57C0', '0x57E0', '0x5840', '0x5860', '0x58C0', '0x5960', '0x5980', '0x59E0', '0x5A20', '0x5A60', '0x5A80', '0x5AA0', '0x5BA0', '0x5C20', '0x5C40', '0x5C80', '0x5D00', '0x5DC0', '0x5DE0', '0x5E20', '0x5E80', '0x5EA0', '0x5EE0', '0x5F00', '0x5FC0', '0x5FE0', '0x6000', '0x95E0', '0x9600', '0x9640', '0x91C0', '0x91E0', '0x9360', '0x94C0', '0x99C0', '0x9A60', '0x9BC0', '0x9BE0', '0x9D60', '0x9E40']
# 16進数を10進数に変換
X = [int(x, 16) for x in X_hex]
# 近似直線を求める
A = np.vstack([X, np.ones(len(X))]).T
m, c = np.linalg.lstsq(A, Y, rcond=None)[0]
print("m= ",m,"c=",c);
#近似直線で計算した結果を表示する
out = np.round(m * np.array(X) + c, 2)
print("out=",out)
# プロット
plt.plot(X, Y, 'o', label='Original data', markersize=10)
plt.plot(X, m*np.array(X) + c, 'r', label='Fitted line')
plt.legend()
plt.show()
Appendix.3 BLE接続で温湿度を読み取るプログラム
# -*- coding: utf-8 -*-
'''
temp-humid01.py : Read temperature and humidity data from UNI-01-B002
Rev.0.01: 2024/02/08
'''
import sys
import asyncio
from bleak import BleakClient
import paho.mqtt.client as mqtt
import time
import json
import struct
# setting for BLE
ADDRESS = ( "F0:AB:54:4B:A8:8C" )
# UUID for UNI-01-B002
CHARACTERISTIC_UUID = "f7eeaa21-276e-4165-aa69-7e3de7fc627e" # read & notify
# MQTT ブローカーの設定
broker_address = "localhost"
port = 1883
topic = "temp-humid"
# MQTT クライアントを作成
mqtt_client = mqtt.Client()
sdata = { # 辞書型データ
'sensor': 'UNI-01-B002',
'time' : 'time',
'temp' : 20.00,
'humid' : 40.00
}
#温度データの係数
mt = 0.002681276582012078
ct = -46.850169942798054
#湿度データの係数
mh = 0.0019073492524788495
ch = -6.000100924682605
def publish(sdata):
# Publish on MQTT
json_data = json.dumps(sdata)
mqtt_client.publish(topic, json_data)
def bytes_to_hex(data):
# bytearrayをバイト単位で16進数に変換します
data1 = ''.join(format(x, '02x') for x in data)
# 4バイトごとの文字列に分けます
X_hex = [data1[i:i+4] for i in range(0, len(data1), 4)]
# 16進数を10進数に変換
X = [int(x, 16) for x in X_hex]
return X
def notification_handler(sender, data: bytearray):
data1 = bytes_to_hex(data)
temp = round(data1[0] * mt + ct, 2)
humid = round(data1[1] * mh + ch, 2)
sdata['sensor'] = 'UNI-01-B002'
sdata['time'] = time.time()
sdata['temp'] = temp
sdata['humid'] = humid
print(data1)
print(sdata)
publish(sdata)
async def main(address):
char_uuid = CHARACTERISTIC_UUID
# MQTT ブローカーに接続
mqtt_client.connect(broker_address, port)
print(f"MQTT connected: {broker_address}")
print(address, char_uuid)
async with BleakClient(address) as client:
print(f"Connected: {client.is_connected}")
await client.start_notify(char_uuid, notification_handler)
try:
while True:
await asyncio.sleep(1.0)
except KeyboardInterrupt:
print("Stop!! Key board Interrupt!!");
await client.stop_notify(char_uuid)
finally:
await client.stop_notify(char_uuid)
print("Disconnected")
# MQTT クライアントを切断
mqtt_client.disconnect()
print(f"MQTT disconnected: {broker_address}")
if __name__ == "__main__":
asyncio.run(
main(
sys.argv[1] if len(sys.argv) > 1 else ADDRESS,
)
)