SHT31 Smart Gadget Development Kitに保存されている温度データを取ろうとしてみる
2019年5月に買って以来、ずっと謎だった温度ログを取る部分をwiresharkで見て、あわよくば自動でどこかに保存する仕組みを作りたい。このデバイスは随分前のものですが、たとえばRSコンポーネンツには在庫があるようです。ですが、全ての仕様やファームウェアがオープンソースになっているので、学習に使うには都合が良さそうです。
もうすぐ消えるようなメッセージが出てましたが、このアプリで温度計本体に保存されているログデータを取り出す部分をwiresharkで見てみます。
温度、湿度はだいたい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,)
温度計本体のデータを取り出す処理を行ったときの最初のデータはhandleが0x0032
でvalueが01000000cdcc7242e17a6a420ad76b4214ae6e42
です。右の方のデータを見ると'xxxxxx42'というのが4回繰り返しています。湿度データが4つ入っているようだと分かります。連続した湿度データを並べると、左のデータは全体で何番目からのデータという意味になりそうです。
01000000cdcc7242e17a6a420ad76b4214ae6e42 # 1回目: 1
05000000ec5164425c8f69427b1471427b147242 # 2回目: 5
09000000e17a7442295c7242a4707342c3f56f42 # 3回目: 9
0d0000007b147642b81e764266667942c3f57842 # 4回目: 13
温度計も同様になっていました。最後の方は4で割り切れない場合は、valueの長さが短くなることも分かりました。
ということで、パケットから温度と湿度を次のように得ることができます。アプリから取り出したデータと比べると新しい順にデータは送られていることも分かりました。
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)
bluetoohのやり取りはESPr® Developer 32を使います。
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.