Open7

SHT31 Smart Gadget Development Kitに保存されている温度データを取ろうとしてみる

nabeyangnabeyang

2019年5月に買って以来、ずっと謎だった温度ログを取る部分をwiresharkで見て、あわよくば自動でどこかに保存する仕組みを作りたい。このデバイスは随分前のものですが、たとえばRSコンポーネンツには在庫があるようです。ですが、全ての仕様やファームウェアがオープンソースになっているので、学習に使うには都合が良さそうです。

https://www.sensirion.com/jp/environmental-sensors/humidity-sensors/development-kit/

nabeyangnabeyang

温度、湿度はだいたい1秒に1回ずつ送られているので(正確には分からなくても、アプリの表示が頻繁に変わる)、適当にそれっぽいパケットのvalueを取ってみます(ファームウェアからドキュメントまで公開されているので、そっちを見たほうが良いかもしれません)。handleが0x0032でvalueが295c6942とかhandleが0x0037でvalueが6666dc41がセントラルデバイスに飛んでいるのが分かりました。
そしてこれらがそれぞれ温度、湿度がそのまま入っていることが分かります。

import struct
print(struct.unpack('<f', bytes.fromhex('295c6942'))) #=>(58.34000015258789,)
print(struct.unpack('<f', bytes.fromhex('6666dc41'))) #=> (27.549999237060547,)
nabeyangnabeyang

温度計本体のデータを取り出す処理を行ったときの最初のデータはhandleが0x0032でvalueが01000000cdcc7242e17a6a420ad76b4214ae6e42です。右の方のデータを見ると'xxxxxx42'というのが4回繰り返しています。湿度データが4つ入っているようだと分かります。連続した湿度データを並べると、左のデータは全体で何番目からのデータという意味になりそうです。

01000000cdcc7242e17a6a420ad76b4214ae6e42 # 1回目: 1
05000000ec5164425c8f69427b1471427b147242 # 2回目: 5
09000000e17a7442295c7242a4707342c3f56f42 # 3回目: 9
0d0000007b147642b81e764266667942c3f57842 # 4回目: 13

温度計も同様になっていました。最後の方は4で割り切れない場合は、valueの長さが短くなることも分かりました。

nabeyangnabeyang

ということで、パケットから温度と湿度を次のように得ることができます。アプリから取り出したデータと比べると新しい順にデータは送られていることも分かりました。

def extract_data(raws):
    a = []
    for raw in raws:
        b = bytes.fromhex(raw['btatt.value'].replace(':', ''))
        n = len(b) - 4
        assert n % 4 == 0
        d = struct.unpack(f"<i{'f' * (n // 4)}", b)
        assert len(a) + 1 == d[0]
        a += d[1:]
    return a
with open('sht31.json', 'r') as f:
        packets = json.load(f)
packets[0]['_source']['layers']['btatt']['btatt.opcode']
atts = [p['_source']['layers']['btatt'] for p in packets if  'btatt.value' in p['_source']['layers']['btatt'].keys()  ]
raw_humidities = [att for att in atts if att['btatt.handle'] == '0x00000032' and len(att['btatt.value']) >= 23]
raw_temperatures = [att for att in atts if att['btatt.handle'] == '0x00000037' and len(att['btatt.value']) >= 23]

assert len(raw_humidities) == len(raw_temperatures)

humidities = extract_data(raw_humidities)
temperatures = extract_data(raw_temperatures)
nabeyangnabeyang

screenだと何も表示されないことが多いのですが、picocomだと大丈夫。

  • picocomの終わり方: Ctrl+a, Ctrl+x
  • おかしくなったら、マイコンのResetボタンを押すとリブートする
    次のように出たら、エンタキー押せばPythonのREPLが使える。
rst:0x1 (POWERON_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT)
configsip: 0, SPIWP:0xee
clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00
mode:DIO, clock div:2
load:0x3fff0030,len:4
load:0x3fff0034,len:5636
load:0x40078000,len:12696
load:0x40080400,len:4292
entry 0x400806b0
MicroPython v1.15 on 2021-04-18; ESP32 module with ESP32
Type "help()" for more information.