はじめに
Inkbird IBS-TH1 PLUSの値をBeagleBone Blackでスプレッドシートへ記録を元にBeagleBone Black+python3 からESP-WROOM-32+micropythonに変更した時の記録です。
使用機材
- Inkbird IBS-TH1PLUS( https://amzn.to/3P15mLc )
- ESP32-DevKitC-32E ( https://amzn.to/3Oa1I1b )
- micropython esp32 v1.20.0 ( https://micropython.org/resources/firmware/esp32-20230426-v1.20.0.bin )
1.ESP32に Micropython を焼きます
本家 https://docs.micropython.org/en/latest/esp32/tutorial/intro.html#esp32-intro
pip insttall esptool
ESP32をPCに繋いだら/dev/ttyUSB0に出てくるのを確認。書き込み権限も確認。
BOOTボタン押しながらENボタン押してFirmware downloadモードにします
esptool.py --port /dev/ttyUSB0 erase_flash
esptool.py --chip esp32 --port /dev/ttyUSB0 write_flash -z 0x1000 esp32-20230426-v1.20.0.bin
1.5 シリアルプロンプト
sudo apt install picocom
picocom /dev/ttyUSB0 -b115200
これでmicropythonのプロンプトが見えたらOKです。
2. adfruit-ampy インストール
ファイル転送のためだけにインストールします。
pip install adafruit-ampy
ファイル転送のコマンドは以下の通り
ampy -p /dev/ttyUSB0 put main.py
3. ESP32 のBLEでInkbirdのセンサ値を取得
bluepyはmicropythonにないため、ubluetoothで作成します。BLE Central RoleとGATT Clientを実装。BLE advertiser と BLE scanner ,GATT Serverはしない方針です。
処理の流れは以下の通り。
- BLE ON
- BLE Connect
- BLE READ(GATT Client)
- BLE disconnect
- BLE OFF
# config
PERIPHERAL_MAC_ADDRESS=b'\xFF\xFF\xFF\xFF\xFF\xFF'
##
import ubluetooth
import utime
import ustruct
## event codes は https://docs.micropython.org/en/latest/library/bluetooth.html#event-handling から必要なものだけ
_IRQ_PERIPHERAL_CONNECT = (7)
_IRQ_PERIPHERAL_DISCONNECT = (8)
_IRQ_GATTC_READ_RESULT = (15)
_IRQ_GATTC_READ_DONE = (16)
## 1.BLE ON
ble=ubluetooth.BLE()
ble.active(True)
# setting IRQ
def bt_irq(event, data):
if event == _IRQ_PERIPHERAL_CONNECT:
# A successful gap_connect().
conn_handle, addr_type, addr = data
global conn_state
conn_state = 0
global handle
handle=conn_handle
elif event == _IRQ_PERIPHERAL_DISCONNECT:
# Connected peripheral has disconnected.
conn_handle, addr_type, addr = data
global conn_state
conn_state = -2
elif event == _IRQ_GATTC_READ_RESULT:
# A gattc_read() has completed.
conn_handle, value_handle, char_data = data
global temp
global humid
(temp, humid, unknown1, unknown2, unknown3) = ustruct.unpack('<hhBBB', char_data)
elif event == _IRQ_GATTC_READ_DONE:
# A gattc_read() has completed.
# Note: Status will be zero on success, implementation-specific value otherwise.
conn_handle, value_handle, status = data
global rstatus
rstatus=status
ble.irq(bt_irq)
## 2. BLE connect
addr_type=0
conn_time = 30
# conn_state 0:connected , -1:connecting , -2:disconnected
conn_state=-1
ble.gap_connect(addr_type, PERIPHERAL_MAC_ADDRESS)
while( conn_state != 0 ):
print("BLE connecting ..",conn_time)
if( conn_state == -2):
conn_state =-1
ble.gap_connect(addr_type, PERIPHERAL_MAC_ADDRESS)
conn_time = conn_time -1
if(conn_time < 0):
break
utime.sleep_ms(1000)
if( conn_state == 0):
### 3. BLE read ( GATT Client )
value_handle=0x2d
rstatus=-1
ble.gattc_read(handle,value_handle)
### wait read
while(rstatus!=0):
utime.sleep_ms(2000)
### output
print(temp/100 , humid/100)
## 4.BLE disconnect
ble.gap_disconnect(handle)
# 5. BLE OFF
ble.active(False)
Inkbird IBS-TH1PLUS のvalue_handle は0x2d です。
PERIPHERAL_MAC_ADDRESS へ InkbirdのMACアドレスを入力した後,ファイル転送&実行してInkbird の温度と同じであれば動作確認完了です。
$ ampy -p /dev/ttyUSB0 put main.py
$ picocom /dev/ttyUSB0 -b115200
>>> (Ctrl-Dで soft reboot)
27.24 48.11
MicroPython v1.20.0 on 2023-04-26; ESP32 module with ESP32
Type "help()" for more information.
>>>
4.ESP32 から WLAN経由でスプレッドシートにセンサ値を書き込み
スプレッドシートにセンサ値を書き込むGASスクリプトをWebAppとして公開
前回の記事のスクリプトをmicropythonでクエリパラメータをPOSTする方法が分からなかったのでクエリーパラメータからJSONパラメータへ変更する。
var book = SpreadsheetApp.getActiveSpreadsheet();
//PostされたJSONデータを受け取り
function doPost(e){
var parameter = JSON.parse(e.postData.getDataAsString());
var data = [
parameter.Date_Master, // マスター日時
parameter.Date, // 測定日時
parameter.Temperature, // 気温
parameter.Humidity, // 湿度
parameter.Light, // 照度
parameter.UV, // UV
parameter.Pressure, // 圧力
parameter.Noise, // 騒音
parameter.BatteryVoltage, // 電池電圧
new Date(), // アップロード終了時刻
];
//取得したデータをログに記載
Logger.log(new Date());
//スプレッドシートへのデータ行書き込み
addData(parameter.DeviceName, data);
//返り値
var output = ContentService.createTextOutput();
output.setMimeType(ContentService.MimeType.JSON);
output.setContent(JSON.stringify({ message: "success!" }));
return output;
}
//スプレッドシートへのデータ行書き込み
function addData(sheetName, data){
var sheet = book.getSheetByName(sheetName);
sheet.appendRow(data);
}
WebAppへPOSTするスクリプトの作成
WLANの接続処理を追加する。リファレンスそのままです。
全体の処理の流れは以下の通り。
- BLE ON
- BLE Connect
- BLE READ(GATT Client)
- BLE disconnect
- BLE OFF
- WLAN connect
- POST data
- WLAN disconnect
# config
PERIPHERAL_MAC_ADDRESS=b'\xFF\xFF\xFF\xFF\xFF\xFF'
WEB_APP_URL='https://script.google.com/macros/s/******/exec'
DEVICE_NAME='****'
WLAN_SSID='******'
WLAN_PASSWD='******'
##
import ubluetooth
import utime
import ustruct
## event codes は https://docs.micropython.org/en/latest/library/bluetooth.html#event-handling から必要なものだけ
_IRQ_PERIPHERAL_CONNECT = (7)
_IRQ_PERIPHERAL_DISCONNECT = (8)
_IRQ_GATTC_READ_RESULT = (15)
_IRQ_GATTC_READ_DONE = (16)
## 1.BLE ON
ble=ubluetooth.BLE()
ble.active(True)
# setting IRQ
def bt_irq(event, data):
if event == _IRQ_PERIPHERAL_CONNECT:
# A successful gap_connect().
conn_handle, addr_type, addr = data
global conn_state
conn_state = 0
global handle
handle=conn_handle
elif event == _IRQ_PERIPHERAL_DISCONNECT:
# Connected peripheral has disconnected.
conn_handle, addr_type, addr = data
global conn_state
conn_state = -2
elif event == _IRQ_GATTC_READ_RESULT:
# A gattc_read() has completed.
conn_handle, value_handle, char_data = data
global temp
global humid
(temp, humid, unknown1, unknown2, unknown3) = ustruct.unpack('<hhBBB', char_data)
elif event == _IRQ_GATTC_READ_DONE:
# A gattc_read() has completed.
# Note: Status will be zero on success, implementation-specific value otherwise.
conn_handle, value_handle, status = data
global rstatus
rstatus=status
ble.irq(bt_irq)
## 2. BLE connect
addr_type=0
conn_time = 30
# conn_state 0:connected , -1:connecting , -2:disconnected
conn_state=-1
ble.gap_connect(addr_type, PERIPHERAL_MAC_ADDRESS)
while( conn_state != 0 ):
print("BLE connecting ..",conn_time)
if( conn_state == -2):
conn_state =-1
ble.gap_connect(addr_type, PERIPHERAL_MAC_ADDRESS)
conn_time = conn_time -1
if(conn_time < 0):
break
utime.sleep_ms(1000)
if( conn_state == 0):
### 3. BLE read ( GATT Client )
value_handle=0x2d
rstatus=-1
ble.gattc_read(handle,value_handle)
### wait read
while(rstatus!=0):
utime.sleep_ms(2000)
### output
print(temp/100 , humid/100)
## 4.BLE disconnect
ble.gap_disconnect(handle)
# 5. BLE OFF
ble.active(False)
###################################
# 6.WLAN connect
def do_connect():
import network
wlan = network.WLAN(network.STA_IF)
wlan.active(True)
if not wlan.isconnected():
print('connecting to network..')
wlan.connect( WLAN_SSID, WLAN_PASSWD)
while not wlan.isconnected():
pass
print('network config:', wlan.ifconfig())
do_connect()
# 7. POST data
import urequests
import ujson
# upload to the spreadsheet
data = {
'DeviceName': DEVICE_NAME,
'Date_Master': '',
'Date': '',
'SensorType': '' ,
'Temperature': str(temp/100),
'Humidity': str(humid/100),
'Light': '',
'UV': '',
'Pressure': '',
'Noise': '',
'BatteryVoltage': ''
}
response = urequests.post( WEB_APP_URL, data=ujson.dumps(data))
response.close()
# 8.WLAN disconnect
wlan.disconnect()
wlan.active(False)
PERIPHERAL_MAC_ADDRESS へ InkbirdのMACアドレスを、
WEB_APP_URL へ GASのWebAPPのUrl を、
deviceName へ Googleスプレッドシートのシート名を
WLAN_SSID へ ローカルWifiのSSIDを、
WLAN_PASSWDへ ローカルWifiのパスワードを、それぞれ入力します。
その後、以下のコマンドを実行してスプレッドシートに値が書き込まれていたら動作確認完了です。
$ ampy -p /dev/ttyUSB0 put main.py
$ picocom /dev/ttyUSB0 -b115200
>>> (Ctrl-Dで soft reboot)
27.75 48.71
connecting to network..
network config: ('192.168.10.115', '255.255.255.0', '192.168.10.1', '192.168.10.1')
MicroPython v1.20.0 on 2023-04-26; ESP32 module with ESP32
Type "help()" for more information.
>>>
5. ESP32 でスケジュール実行
cronは無いので while loop とsleep で実装
(2023-08-20 追記) while loop だと urequests.py がエラーでおちるため、毎回再起動させる方針へ変更
- BLE ON
- BLE Connect
- BLE READ(GATT Client)
- BLE disconnect
- BLE OFF
- WLAN connect
- POST data
- WLAN disconnect
- Sleep
- reset
# config
PERIPHERAL_MAC_ADDRESS=b'\xFF\xFF\xFF\xFF\xFF\xFF'
WEB_APP_URL='https://script.google.com/macros/s/******/exec'
DEVICE_NAME='****'
WLAN_SSID='******'
WLAN_PASSWD='******'
##
import ubluetooth
import utime
import ustruct
## event codes は https://docs.micropython.org/en/latest/library/bluetooth.html#event-handling から必要なものだけ
_IRQ_PERIPHERAL_CONNECT = (7)
_IRQ_PERIPHERAL_DISCONNECT = (8)
_IRQ_GATTC_READ_RESULT = (15)
_IRQ_GATTC_READ_DONE = (16)
## 1.BLE ON
ble=ubluetooth.BLE()
ble.active(True)
# setting IRQ
def bt_irq(event, data):
if event == _IRQ_PERIPHERAL_CONNECT:
# A successful gap_connect().
conn_handle, addr_type, addr = data
global conn_state
conn_state = 0
global handle
handle=conn_handle
elif event == _IRQ_PERIPHERAL_DISCONNECT:
# Connected peripheral has disconnected.
conn_handle, addr_type, addr = data
global conn_state
conn_state = -2
elif event == _IRQ_GATTC_READ_RESULT:
# A gattc_read() has completed.
conn_handle, value_handle, char_data = data
global temp
global humid
(temp, humid, unknown1, unknown2, unknown3) = ustruct.unpack('<hhBBB', char_data)
elif event == _IRQ_GATTC_READ_DONE:
# A gattc_read() has completed.
# Note: Status will be zero on success, implementation-specific value otherwise.
conn_handle, value_handle, status = data
global rstatus
rstatus=status
ble.irq(bt_irq)
## 2. BLE connect
addr_type=0
conn_time = 30
# conn_state 0:connected , -1:connecting , -2:disconnected
conn_state=-1
ble.gap_connect(addr_type, PERIPHERAL_MAC_ADDRESS)
while( conn_state != 0 ):
print("BLE connecting ..",conn_time)
if( conn_state == -2):
conn_state =-1
ble.gap_connect(addr_type, PERIPHERAL_MAC_ADDRESS)
conn_time = conn_time -1
if(conn_time < 0):
break
utime.sleep_ms(1000)
if( conn_state == 0):
### 3. BLE read ( GATT Client )
value_handle=0x2d
rstatus=-1
ble.gattc_read(handle,value_handle)
### wait read
while(rstatus!=0):
utime.sleep_ms(2000)
### output
print(temp/100 , humid/100)
## 4.BLE disconnect
ble.gap_disconnect(handle)
# 5. BLE OFF
ble.active(False)
###################################
# 6.WLAN connect
def do_connect():
import network
wlan = network.WLAN(network.STA_IF)
wlan.active(True)
if not wlan.isconnected():
print('connecting to network..')
wlan.connect( WLAN_SSID, WLAN_PASSWD)
while not wlan.isconnected():
pass
print('network config:', wlan.ifconfig())
do_connect()
# 7. POST data
import urequests
import ujson
# upload to the spreadsheet
data = {
'DeviceName': DEVICE_NAME,
'Date_Master': '',
'Date': '',
'SensorType': '' ,
'Temperature': str(temp/100),
'Humidity': str(humid/100),
'Light': '',
'UV': '',
'Pressure': '',
'Noise': '',
'BatteryVoltage': ''
}
response = urequests.post( WEB_APP_URL, data=ujson.dumps(data))
response.close()
# 8.WLAN disconnect
wlan.disconnect()
wlan.active(False)
# 9.Sleep
sleep_time= 30*60 #30 min
while(sleep_time > 0):
print(sleep_time)
sleep_time=sleep_time-1
utime.sleep(1) # 1sec
# 10.reset
import machine
machine.reset()
その他
micropython のexampleが動かなくなっていたため、調査しながら作ったら、とてもスクリプトっぽいコードになってしまいました。
参考