Contents
- BLE機器との接続
- 1.概要
- 1-1. 接続するセンサ機器
- 1-2. システム概要
- 1-3. BLE接続に必要な情報
- 2. センサ機器のBluetoothの情報を調べるPythonプログラム
- 2-2. NotifyコマンドのUUIDを調べるプログラム
- 3. OWON社製 Digital Multimeter B35T
- 3-1. BluetoothのMACアドレスの調査
- 3-2. NotifyコマンドのUUIDの調査
- 3-3. Notifyコマンドを使ってデータの取込プログラム
- 3-4. Node-REDのエディタ・プログラミング
- 3-4-1. Pythonプログラムの実行部
- 3-4-2. HTTPリクエストの受け部
- 3-4-3.ダッシュボード表示部
- 3-5. 実行結果
- 4. APERA Instruments社製 Smart Multiparameter Tester PC60-Z
- 4-1. BluetoothのMACアドレスの調査
- 4-2. NotifyコマンドのUUIDの調査
- 4-3. Notifyコマンドを使ってデータの解析
- 4-4. Node-REDのダッシュボードへの表示
- 4-4-1. Node-REDへの転送プログラム
- 4-4-2. Node-REDのエディタ・プログラミング
- 4-5. 実行結果
BLE機器との接続
1.概要
R-MSMとは異なる外部センサ機器でBLE(Bluetooth Low Energy)送信を行う機器をPDH(Physical Data Hub)に接続します。
1-1. 接続するセンサ機器
ここでは、以下の2機種をPDHに接続します。
1) OWON社製 Digital Multimeter B35T
2) APERA Instruments社製 Smart Multiparameter Tester PC60-Z
1-2. システム概要
センサ機器とPDHとの接続はBLEで行います。PDHでNode-REDから起動されたPython3(bleakライブラリ使用)でセンサの測定情報を取得します。
その後、取得したデータをNode-REDにHTTPで送信します。受信したNode-REDのデータをダッシュボードのチャートノードでグラフ化します。
1-3. BLE接続に必要な情報
1) センサ機器のBluetoothのMACアドレス、2) NotifyコマンドのUUIDです。これらは、センサ機器を起動しておいて、PDHでPython3のプログラムを実行して調べます。
参考HP: WT901BLECLにBLE接続してデータを読み取る
Github – hbldh/bleak
2. センサ機器のBluetoothの情報を調べるPythonプログラム
1) センサ機器のBluetoothのMACアドレス、2) NotifyコマンドのUUID を調べるPythonプログラムを示します。
ライブラリ bleakは、インストールが必要です。各自の環境に合わせてインストールしてください。
$ pip install bleak
2-1. BluetoothのMACアドレスを調べるプログラム
BluetoothのMACアドレスを調べるプログラムを以下に示します。BleakScanner.discover()で情報を取得します。
”scan_ble_devices.py”
import asyncio
from bleak import BleakScanner
async def run():
devices = await BleakScanner.discover()
for d in devices:
print(f"address: {d.address}, name: {d.name}, uuid: {d.metadata['uuids']}")
loop = asyncio.get_event_loop()
loop.run_until_complete(run())
2-2. NotifyコマンドのUUIDを調べるプログラム
上のコマンドで調べたMACアドレスを使って、サービスの情報を取得します。
addressをご自身の調べたいMACアドレスに変更してください。
get_device_uuids.py
import asyncio
from bleak import BleakClient
address = "EA:78:B5:4D:E3:21" # scan_ble_devices.pyで取得したMACアドレスを指定
async def run(address, loop):
async with BleakClient(address, loop=loop) as client:
x = client.is_connected
print("Connected: {0}".format(x))
for service in client.services:
print(f"{service.uuid}: {service.description}: {[f'{c.properties},{c.uuid}' for c in service.characteristics]}")
loop = asyncio.get_event_loop()
loop.run_until_complete(run(address, loop))
3. OWON社製 Digital Multimeter B35T
B35Tは、BLE送信対応のデジタルマルチメータです。BLEに対応していることで、電圧や電流を長期間モニタ出来ることを売りにしています。また、熱電対を取付けることで、温度の測定もできます。温度もBLEで送れます。
このデジタルマルチメータ B35TのデータをPDHに送り、ダッシュボードに結果を表示させます。
3-1. BluetoothのMACアドレスの調査
2-1項の”scan_ble_devices.py“を実行してBluetoothのMACアドレスを調べます。
実行結果を以下に示します。nameに“BDM”と出ているのが、B35Tになります。
従って、MACアドレスは、18:45:16:a4:89:1c です。
3-2. NotifyコマンドのUUIDの調査
2-2項の” get_device_uuids.py“を実行してNotifyコマンドのUUIDを調べます。
実行結果を以下に示します。大きく3つに分かれています。
1) Device Information:read コマンドのUUIDが9つあります。
2) Generic Attribute Progile:indicate コマンドのUUIDが1つあります。
3) Vendor specific:readコマンドが2つ、writeコマンドが1つ、read/writeコマンドが1つ、notifyコマンドが1つあります。それぞれにUUIDが記載されています。
ここでは、notifyコマンドのUUID “0000fff4-0000-1000-8000-00805f9b34fb”を使用します。
3-3. Notifyコマンドを使ってデータの取込プログラム
上で調べたMACアドレスとUUIDを使って、センサ機器からデータを読み出します。
そして読み出したデータをHTTP requestを使って、Node-REDのhttp in ノードに送ります。urlは、’http://localhost:1880/temperature’としました。また、送るデータはjsonフォーマットの形式としています。
http_b35t.py
# -*- coding: utf-8 -*-
import sys
import asyncio
from bleak import BleakClient
import time
import json
import urllib.request
# you can change these to match your device or override them from the command line
CHARACTERISTIC_UUID = "0000fff4-0000-1000-8000-00805f9b34fb" # notify
ADDRESS = (
"18:45:16:a4:89:1c" # BDM
)
url = 'http://localhost:1880/temperature'
sdata = { # 辞書型データ
'sensor': 'B35T',
'temp': 20.0,
'units':'deg'
}
def contact(data): # Node-REDとの通信
headers = {'Content-Type': 'application/json',}
req = urllib.request.Request(url, json.dumps(data).encode(), headers)
try:
with urllib.request.urlopen(req) as res:
body = res.read()
except urllib.error.HTTPError as err:
print(err.code)
except urllib.error.URLError as err:
print(err.reason)
print('code',res.getcode())
return(body)
def notification_handler(sender, data):
"""Simple notification handler which prints the data received."""
#print("{0}: {1}".format(sender, data))
sensor = sender
data1 = data.decode()
data2 = float(data1[1:5])
#sdata['sensor'] = sensor
sdata['temp'] = data2
print(sdata)
res = contact(sdata) # send temperature data
async def main(address, char_uuid):
async with BleakClient(address) as client:
print(f"Connected: {client.is_connected}")
await client.start_notify(char_uuid, notification_handler)
await asyncio.sleep(5.0)
await client.stop_notify(char_uuid)
if __name__ == "__main__":
asyncio.run(
main(
sys.argv[1] if len(sys.argv) > 1 else ADDRESS,
sys.argv[2] if len(sys.argv) > 2 else CHARACTERISTIC_UUID,
)
)
3-4. Node-REDのエディタ・プログラミング
B35Tから送られたデータがPythonプログラムで処理され、HTTP requestでNode-REDに送られます。それを受けて、ダッシュボードに表示するNode-REDのエディタプログラムを説明します。Node-REDは大きく3つのブロックに分かれます。
1) Pythonプログラムの実行部
2) HTTPリクエストの受け部
3) ダッシュボード表示部
の3つです。
3-4-1. Pythonプログラムの実行部
Pythonプログラムの実行部は、injectノード、execノード、debugノードの3つのノードで構成されます。中身の変更は、execノードのコマンド欄です。コマンド欄に、実行コマンドとプログラム名を記載します。
python3 /home/pi/source/python_pro/enable_b35t/http_b35t.py
3-4-2. HTTPリクエストの受け部
HTTPリクエストの受け部は、http inノード、http responseノード、changeノード、Date/Time Formatterノード、debugノードの5つのノードから構成されます。http inノードのURL欄に、pythonプログラムのurlと同じ“/temperature”を記載します。そして、メソッドには、POSTを選びます。また、changeノードは、payloadの下のデータの整理を行います。
3-4-3.ダッシュボード表示部
ダッシュボード表示部は、changeノードとchartノードで構成されます。changeノードで値をmsg.payloadに移します。また、charノードは、グループとタブの設定を行い、最大値/最小値の設定も行います。
3-5. 実行結果
B35Tの電源を入れ、Bluetoothボタンを押してBLEでのデータ送信状態にしたうえで、injectノードのボタンを押します。するとpython3のプログラムが実行され、データがhttpで送られてきます。送られてきたデータはデバッグノードに表示されるとともに、ダッシュボードのチャート画面に表示されます。
4. APERA Instruments社製 Smart Multiparameter Tester PC60-Z
PC6-Zは、PH値、伝導率、TDS、塩分を測定できる水質測定計です。Bluetoothでスマートフォンのアプリとやり取りを行うことができますので、そのデータを読み取ります。そして、海産物の養殖でBluetoothを使ってIoT用に使用できないかを検討します。
4-1. BluetoothのMACアドレスの調査
2-1項の”scan_ble_devices.py“を実行してBluetoothのMACアドレスを調べます。
実行結果を以下に示します。一番上の行に出ているのが、PC6-Zになります。
ポイントはPC6-Zの電源を入れる前に、一度スキャンし、電源投入後と比較することです。その短期間に増えたBluetoothのアドレスが対称機器の可能性が高いです。
MACアドレスは、 B9:12:B3:20:7F:F8 です。
4-2. NotifyコマンドのUUIDの調査
2-2項の” get_device_uuids.py“を実行してNotifyコマンドのUUIDを調べます。
実行結果を以下に示します。大きく2つに分かれています。
1) Generic Attribute Profile:indicate コマンドのUUIDが1つあります。
2) Vendor specific:同一UUIDに、write-without-responseコマンドが1つとwriteコマンドが1つ、それと別のUUIDにnotifyコマンドが1つあります。
ここでは、notifyコマンドのUUID “0000ffe4-0000-1000-8000-00805f9b34fb”を使用します。
4-3. Notifyコマンドを使ってデータの解析
上で調べたMACアドレスとUUIDを使って、センサ機器からデータを読み出します。
プログラムは、enable_pc6.pyです。
データは、Binary配列として読み出されます。PHと温度の値の変化とデータを見比べてデータがどこに埋め込まれているかを推測します。
次の結果が、PC6-Zの表示とBluetoothのデータを比較した結果になります。
赤字の部分がPHの変化でデータが変わる部分、青字の部分が温度の変化で変わる部分です。それぞれ10進に直したのが右端の結果です。
さらに温度を上げると、一番右端のByteが xff から x00に変わり、その左側のByteが 0 から 1 に変わるのが分かります。一番右側のByteとその左側のByteで、温度を表していると考えられます。ただし、左側のByteの‘0’は文字コードです。数字に直すと b’0’= 48 = 0x30、同様に、b’1’= 49 = 0x31です。単純に計算には使えません。
そこで、bytearray[5]がb’1’なら、bytearray[6]に255を足すようにします。このため、下の例で分かるように温度25.5℃の表現の仕方は、’b0\xff’と’b1\x00’の2通りがあります。
この結果から、PHと温度は、次の計算式で求まることが分かります。
PH値 = (bytearray[2:4]を10進化 - 6096) ÷ 100
温度 :もし bytearray[5] = b’0’ = 48 なら 温度 = bytearry[6]を10進化 ÷ 10
もし bytearray[5] = b’1’ = 49 なら 温度 = bytearry[6]を10進化 ÷ 10 +255
この計算式を組み込んで、プログラムを実行した結果を次に示します。5秒おきに5回測定しています。
測定器の表示結果を、PDHのPythonのプログラムの結果が一致しているのが分かります。
1) PH: 8.13pH Temp: 18.7℃
2) PH: 4.44pH Temp: 18.5℃
次にデータの解析に用いた、Pythonのプログラムを示します。
ADDRESSをそれぞれの機器に合わせて変更する必要があります。
enable_pc6.py
# -*- coding: utf-8 -*-
import sys
import asyncio
from bleak import BleakClient
CHARACTERISTIC_UUID = "0000ffe4-0000-1000-8000-00805f9b34fb" # notify
ADDRESS = (
"B9:12:B3:20:7F:F8" # PC60-Z
)
def notification_handler(sender, data: bytearray):
index = data[1]
ph = int.from_bytes(data[2:4], byteorder = 'big', signed=False)
ph = (ph - 6096) / 100
if (data[5]==49):
temp = 255 + data[6]
else:
temp = data[6]
temp = temp / 10
print(f"PH: {ph}, Temp: {temp}")
# print(f"PH: {ph}, Temp: {temp}, data: {data}")
async def main(address, char_uuid):
async with BleakClient(address) as client:
print(f"Connected: {client.is_connected}")
await client.start_notify(char_uuid, notification_handler)
await asyncio.sleep(5.0)
await client.stop_notify(char_uuid)
if __name__ == "__main__":
asyncio.run(
main(
sys.argv[1] if len(sys.argv) > 1 else ADDRESS,
sys.argv[2] if len(sys.argv) > 2 else CHARACTERISTIC_UUID,
)
)
4-4. Node-REDのダッシュボードへの表示
PC60-Zから送られたデータをPythonプログラムで処理し、HTTP requestでNode-REDに送ります。それを受けて、Node-REDのダッシュボードに表示する流れを、説明します。
4-4-1. Node-REDへの転送プログラム
Node-REDへは、HTTP requestで転送します。4-3の項で解析に使ったプログラムに、HTTP転送部(関数contact)を追加します。
http_pc60.py
# -*- coding: utf-8 -*-
import sys
import asyncio
from bleak import BleakClient
import time
import json
import urllib.request
CHARACTERISTIC_UUID = "0000ffe4-0000-1000-8000-00805f9b34fb" # notify
ADDRESS = (
"B9:12:B3:20:7F:F8" # PC60-Z
)
url = 'http://localhost:1880/pH'
sdata = { # 辞書型データ
'sensor': 'pc60-z',
'ph': 7.0,
'temp': 20.0
}
def contact(data): # Node-REDとの通信
headers = {'Content-Type': 'application/json',}
req = urllib.request.Request(url, json.dumps(data).encode(), headers)
try:
with urllib.request.urlopen(req) as res:
body = res.read()
except urllib.error.HTTPError as err:
print(err.code)
except urllib.error.URLError as err:
print(err.reason)
print('code',res.getcode())
return(body)
def notification_handler(sender, data):
ph = int.from_bytes(data[2:4], byteorder = 'big', signed=False)
ph = (ph - 6096) / 100
if (data[5]==49):
temp = 255 + data[6]
else:
temp = data[6]
temp = temp / 10
print(f"PH: {ph}, Temp: {temp}")
sdata['ph'] = ph
sdata['temp'] = temp
res = contact(sdata) # send measured data
async def main(address, char_uuid):
async with BleakClient(address) as client:
print(f"Connected: {client.is_connected}")
await client.start_notify(char_uuid, notification_handler)
await asyncio.sleep(5.0)
await client.stop_notify(char_uuid)
if __name__ == "__main__":
asyncio.run(
main(
sys.argv[1] if len(sys.argv) > 1 else ADDRESS,
sys.argv[2] if len(sys.argv) > 2 else CHARACTERISTIC_UUID,
)
)
4-4-2. Node-REDのエディタ・プログラミング
Node-REDは大きく3つのブロックに分かれます。
1) Pythonプログラムの実行部
2) HTTPリクエストの受け部
3) ダッシュボード表示部
の3つです。
ノード内部の説明は、「3-4. Node-REDのエディタ・プログラミング」を参照ください。基本的に同じです。
相違点は、
(1) 実行するプログラム名(http_pc60.py)
(2) HTTP リクエストのurl名(/pH)
(3) ダッシュボードのチャートが2つ になっている
ところです。
4-5. 実行結果
PC60-Zの電源を入れ、PH測定を開始します。その後、injectノードのボタンを押します。するとpython3のプログラムが実行され、データがhttpで送られてきます。送られてきたデータはデバッグノードに表示されるとともに、ダッシュボードのチャート画面に表示されます。