センサ・シリーズ 温湿度①SHT31 その4 BLE
SHT31で測定した温湿度をBLEで通信できるようにします。ラズパイはサーバの立場のペリフェラル動作です。ラズパイで使えるpythonのBLEライブラリはさほど多くはありません。
使用環境
|
使用するpyblenoは、内部で使用するhci関連が管理者権限で動くので、次のようにインストールします。
sudo pip3 install pybleno
ソースや事例は、https://github.com/Adam-Langley/pyblenoにあります。
●examples/batteryservice/をベースに拡張する
サンプルは三つのファイルに分かれていますが、一つにします。
関数sht31()で、SHT31の読み取りはCRCは省略しました。戻り値は、温度と湿度の値(実数)のリストです。
関数sht31Service()は、サービスを定義します。UUIDは任意な文字列を使いました。Characteristics(
以下、キャラ)などでは最後の数字を変更しています。
関数sht31_temp_Characteristic()は、サービスsht31Service()の内部の定義です。read属性だけがあります。onReadRequest()は、セントラル側がread要求を出すと呼ばれます。内容は、list(struct.pack('>f', sht31()[0])) で、温度を浮動小数点形式で送ります。
起動は、sudo python3 xxx.pyです。実行を終了するのには、何らかのキーをターミナル内で打ちます。
from pybleno import* import time import sys def sht31(): import smbus i2c = smbus.SMBus(1) i2c.write_byte_data(0x44, 0x23, 0x34) time.sleep(0.015) dataTmsb, dataTlsb, dataTcrc, dataHmsb, dataHlsb, dataTcrc = i2c.read_i2c_block_data(0x44, 0xff, 6) rawT = dataTmsb << 8 | dataTlsb rawR = dataHmsb << 8 | dataHlsb temp = -45 + rawT * 175 / 65535 RH = 100 * rawR / 65535 # print(' temp= {0:.1f} `C, humi= {1:.1f} RH%'.format(temp, RH)) return [temp, RH] print(sht31()[0], sht31()[1]) class sht31Service(BlenoPrimaryService): def __init__(self): BlenoPrimaryService.__init__(self, { 'uuid':'1234-5678-1234-5678-1234-5678-9abc-def0', 'characteristics':[sht31_temp_Characteristic()] } ) class sht31_temp_Characteristic(Characteristic): def __init__(self): Characteristic.__init__(self,{ 'uuid':'1234-5678-1234-5678-1234-5678-9abc-def7', 'properties':['read'], 'value':None } ) def onReadRequest(self, offset, callback): callback(Characteristic.RESULT_SUCCESS, array.array('B', list(struct.pack('>f', sht31()[0])) )) # main bleno = Bleno() primaryService = sht31Service(); def onStateChange(state): print('on->stateChange:'+state); if(state=='poweredOn'): bleno.startAdvertising('SHT31',[primaryService.uuid]); else: bleno.stopAdvertising(); bleno.on('stateChange',onStateChange) def onAdvertisingStart(error): print('on -> advertisingStart: ' + ('error ' + error if error else 'success')); if not error: def on_setServiceError(error): print('setServices: %s' % ('error ' + error if error else 'success')) bleno.setServices([ primaryService ], on_setServiceError) bleno.on('advertisingStart', onAdvertisingStart) bleno.start() print('Hittodisconnect') if(sys.version_info>(3,0)): input() else: raw_input() bleno.stopAdvertising() bleno.disconnect() print('terminated.') sys.exit(1)
●確認
動作中のターミナルの様子です。
オン・セミコンダクターのRSL10-USB001GEVK: RSL10 USB DongleとそのツールRSL10 Bluetooth Low Enaergy Exploerをつかってセントラル側の動作をします。
local NameがSHT31なので、見つけたらConnectします。この時点でAdvertisingが止まります。Discover Serviceでサービスを表示します。UUIDの1234-5678-1234-5678-1234-5678-9abc-def7が温度データです。
読み取った0x41acd91dは浮動小数点形式です。struct.pack('>f', はビッグ・エンディアンの実数を指定しています。
Floating Point to Hex Converterのサイトに行き、この数値を入力し、変換します。21.606でした。正しく届いているようです。
●温度に加えて湿度を送る
温度のキャラsht31_temp_Characteristic(Characteristic)と同様に湿度のキャラを作ります。そしてサービスにそのキャラを追加します。
from pybleno import * import time import sys def sht31(): import smbus i2c = smbus.SMBus(1) i2c.write_byte_data(0x44, 0x23, 0x34) time.sleep(0.015) dataTmsb, dataTlsb, dataTcrc, dataHmsb, dataHlsb, dataTcrc = i2c.read_i2c_block_data(0x44, 0xff, 6) rawT = dataTmsb << 8 | dataTlsb rawR = dataHmsb << 8 | dataHlsb temp = -45 + rawT * 175 / 65535 RH = 100 * rawR / 65535 return [temp, RH] print(sht31()[0], sht31()[1]) class sht31Service(BlenoPrimaryService): def __init__(self): BlenoPrimaryService.__init__(self, { 'uuid':'1234-5678-1234-5678-1234-5678-9abc-def0', 'characteristics':[sht31_temp_Characteristic(), sht31_humi_Characteristic()] } ) class sht31_temp_Characteristic(Characteristic): def __init__(self): Characteristic.__init__(self, { 'uuid':'1234-5678-1234-5678-1234-5678-9abc-def7', 'properties':['read'], 'value':None } ) def onReadRequest(self, offset, callback): callback(Characteristic.RESULT_SUCCESS, array.array('B', list(struct.pack('>f', sht31()[0])) )) class sht31_humi_Characteristic(Characteristic): def __init__(self): Characteristic.__init__(self,{ 'uuid':'1234-5678-1234-5678-1234-5678-9abc-def8', 'properties':['read'], 'value':None } ) def onReadRequest(self, offset, callback): callback(Characteristic.RESULT_SUCCESS, array.array('B', list(struct.pack('>f', sht31()[1])) )) # main bleno = Bleno() primaryService = sht31Service() def onStateChange(state): print('on->stateChange:'+state) if(state=='poweredOn'): bleno.startAdvertising('SHT31 temp humi',[primaryService.uuid]) else: bleno.stopAdvertising() bleno.on('stateChange', onStateChange) def onAdvertisingStart(error): print('on -> advertisingStart: ' + ('error ' + error if error else 'success')); if not error: def on_setServiceError(error): print('setServices: %s' % ('error ' + error if error else 'success')) bleno.setServices([ primaryService ], on_setServiceError) bleno.on('advertisingStart', onAdvertisingStart) bleno.start() print('Hittodisconnect') if(sys.version_info > (3,0)): input() else: raw_input() bleno.stopAdvertising() bleno.disconnect() print('terminated.') sys.exit(1) |
セントラル側の情報です。
●UARTでデータを送る
ラズパイのBLEでUARTを使うメリットはわかっていません。ノルディックBLEデバイスなどを使ったボードではよく使われます。物理的にUART用のピンに何かをつなぐという利用方法があるかもしれませんが、実験していません。
UARTのUUIDは決まっています。
UARTサービスUUID "6e400001-b5a3-f393-e0a9-e50e24dcca9e" 送信キャラUUID "6e400002-b5a3-f393-e0a9-e50e24dcca9e" 受信キャラUUID "6e400003-b5a3-f393-e0a9-e50e24dcca9e" |
sht31()関数では、温度と湿度を文字列のリストにしました。
onReadRequest()関数では、温度と湿度の区切りを';'にし、バイト列で送ります。
from pybleno import * import time import sys def sht31(): import smbus i2c = smbus.SMBus(1) i2c.write_byte_data(0x44, 0x23, 0x34) time.sleep(0.015) dataTmsb, dataTlsb, dataTcrc, dataHmsb, dataHlsb, dataTcrc = i2c.read_i2c_block_data(0x44, 0xff, 6) rawT = dataTmsb << 8 | dataTlsb rawR = dataHmsb << 8 | dataHlsb temp = -45 + rawT * 175 / 65535 RH = 100 * rawR / 65535 #print(str(temp)) #print(str(RH)) return [str(round(temp,2)),str(round(RH,1))] class sht31Service(BlenoPrimaryService): def __init__(self): BlenoPrimaryService.__init__(self, { 'uuid':'6e400001-b5a3-f393-e0a9-e50e24dcca9e', 'characteristics':[sht31_Characteristic()] } ) class sht31_Characteristic(Characteristic): def __init__(self): Characteristic.__init__(self, { 'uuid':'6e400002-b5a3-f393-e0a9-e50e24dcca9e', 'properties':['read'], 'value':None } ) def onReadRequest(self, offset, callback): callback(Characteristic.RESULT_SUCCESS, (sht31()[0]+ ';' +sht31()[1]).encode('utf-8')) # main bleno = Bleno() primaryService = sht31Service() def onStateChange(state): print('on->stateChange:'+state) if(state=='poweredOn'): bleno.startAdvertising('SHT31 UART',[primaryService.uuid]) else: bleno.stopAdvertising() bleno.on('stateChange',onStateChange) def onAdvertisingStart(error): print('on -> advertisingStart: ' + ('error ' + error if error else 'success')); if not error: def on_setServiceError(error): print('setServices: %s' % ('error ' + error if error else 'success')) bleno.setServices([ primaryService ], on_setServiceError) bleno.on('advertisingStart', onAdvertisingStart) bleno.start() print('Hittodisconnect') if(sys.version_info > (3,0)): input() else: raw_input() bleno.stopAdvertising() bleno.disconnect() print('terminated.') sys.exit(1) |
セントラル側です。UTF8の文字列でみると、そのまま数字で読み取れます。