はじめに
M5Stackシリーズのビジュアル開発環境であるUIFlowは、実行環境としてMicroPythonを利用しています。また、LTE-M接続用のユニットとして、SIM7080Gを搭載したUNIT CatM+GNSSなども販売されています。
一方で、現在のUIFlowのUNIT CatM+GNSS向けのブロックでは、通信モジュールに搭載されたプロトコルスタックを利用する形でしか通信ができません。このため使用できるHTTPメソッドに一部制約があったり、UIFlowにビルトインされているrequestsを使用するHTTPブロックやsocketを使用するTCP/UDPブロックを直接扱うことができず、Wi-Fiを前提としたプログラムをそのまま動作させることが困難です。
しかし、MicroPythonのnetwork.PPP
モジュールを利用すると、PPPoS接続でセルラー通信網に接続できるようになります。PPP接続に起因するオーバーヘッドなどのトレードオフはありますが、先述のような通信モジュール側のプロトコルスタック仕様に依存することで生じる制約を回避できます。
この記事では、UIFlow 2.0を利用してMicroPythonを書き込んだM5Stackからセルラー通信セッションを確立し、PPP接続をセットアップする手順を紹介します。
この方法を使用するとUIFlowにビルトインされた通信関連のブロック(すなわちMicroPython側にビルトインされたプロトコルスタック)をそのまま利用できるようになるため、Wi-Fiを前提としたプログラムのロジック部分も大きな変更を加えることなくセルラー通信経由で利用できるようになります。
network.PPP
モジュール自体は以前からMicroPythonに実装されていましたが、私が2022年末頃に試した限りでは初代UIFlowやプレーンなESP32向けMicroPython v1.19.1をインストールしたM5Stackでは正常に動作しなかったため諦めていた方法です。
この記事で紹介する方法は、ここ1,2年のうちにMicroPythonに取り込まれた変更とUIFlow 2.0のオープンソース化でモジュールの構造や実装が確認できるようになったことで具体的に実現可能になったものです。
準備するもの・前提条件
今回は以下の環境で検証しました。
ハードウェア
- M5Stack Core2
- UNIT CatM+GNSS
- セルラー通信のためのSIMカード
- 今回はSORACOM Air for セルラー(plan-D)のSIMを使用しました
実行環境
- M5stack/uiflow-micropython v2.0.5
- UIFlow 2.0 (V2.0.5)
なお、以下の点はすでに完了しているものとし、本記事の説明対象外とします。
- uiflow-micropythonがM5Stackに書き込まれていること
- セルラー通信のために必要な設定が完了していること
- UIFlowの利用に必要な環境が整っていること
今回紹介する方法は最低限のセットアップ手順と使用方法の例のみです。必要に応じてエラーハンドリングや定期的に切断・再接続するような機構を追加してください。
また、筆者はこの機構を利用することによって生じるいかなる損害の責任も負いません。実装を参考にする場合は自身の責任において実施してください。
セットアップ
PPP接続関連のコードはおもにセットアップ時点で実行する必要がありますが、逆に言えばそれ以降のロジック部分はほとんど変更する必要がありません。
インポート文の追加
下記のモジュールをインポートします(requests
は必要に応じて追加してください)。driver.simcom.*
はUIFlow 2.0.5以降で利用できるようです。
import machine
import time
import network
import requests
from driver.simcom.sim7080 import SIM7080
from driver.simcom.common import AT_CMD
UIFlowの関数ブロックを利用する場合は下記のような形になります。
通信モジュールのセットアップ
通信モジュールのためのラッパークラスにUARTインスタンスを渡し、APN設定などを実施後、セルラー通信網にアタッチします(実行時に既存のセッションを明示的に切断しています)。
def setup_modem():
global MODEM, uart1, CELLULAR_STATUS
print("Setting up MODEM...")
MODEM = SIM7080(uart1)
MODEM.modem_debug = True
MODEM.set_gprs_network_state(0) # Detach network
MODEM.set_pdp_context("soracom.io")
MODEM.set_gprs_network_state(1) # Attach network
CELLULAR_STATUS = MODEM.get_gprs_network_status() == 1
if CELLULAR_STATUS == False:
print("Failed to attach cellular network.")
sys.exit(-1)
print("Attached cellular network")
UIFlow V2.0.6 からUNIT CatM+GNSSのブロックが追加されたため、基本的なセットアップは定義済みのコードブロックで構成できるかもしれません。
ここではUNIT CatM+GNSS(SIM7080G)を使用する想定でユーティリティ関数を利用してセットアップしています。キャリア選択や通信方式などより細かい設定項目もユーティリティ関数に定義があるようなので、必要に応じて指定してください。
https://github.com/m5stack/uiflow-micropython/tree/master/m5stack/libs/driver/simcom
なお、UNIT CatM+GNSS以外のユニットや通信モジュールを利用する場合は関数に定義のないコマンドを送信する必要があると思います。その場合は基底クラスにある Modem#execute_at_command関数を利用するとよさそうです。
PPP接続の確立
通信モジュール側でダイヤルアップモードに遷移し、network.PPP
モジュールをセットアップして接続を確立します。接続を確立すると、セルラー通信網を利用して通信できるようになります。
なお、disconnect_ppp_and_cellular関数はREPL(コマンドインタプリタ)から呼び出すことを想定しています。
def setup_ppp():
global MODEM, CELLULAR_STATUS, PPP, PPP_STATUS
print("Setup PPP connection...")
MODEM.execute_at_command(AT_CMD("ATD*99#", "CONNECT 150000000", 100000))
PPP = network.PPP(uart1)
PPP.active(True)
PPP.connect(authmode=PPP.AUTH_CHAP, username="sora", password="sora")
time.sleep_ms(3000)
PPP_STATUS = PPP.isconnected()
if PPP_STATUS == False:
print("Failed to establish PPP")
sys.exit(-1)
_ipaddr = PPP.ifconfig()
print(f"Established PPP connection, IP addr is {_ipaddr[0]}")
def disconnect_ppp_and_cellular():
global MODEM, PPP
print("Disconnecting...")
PPP.active(False)
time.sleep_ms(3000)
MODEM.set_gprs_network_state(0)
setup()関数の記述例
ここまでの関数ブロックを使ってsetup()
関数を構成してみます。
def setup():
global MODEM, CELLULAR_STATUS, PPP, PPP_STATUS, uart1
MODEM = None
CELLULAR_STATUS = None
PPP = None
PPP_STATUS = None
M5.begin()
uart1 = UART(1, baudrate=115200, bits=8, parity=None, stop=1, tx=14, rx=13, txbuf=256, rxbuf=256, timeout=0, timeout_char=0, invert=0, flow=0)
setup_modem()
setup_ppp()
(例)HTTPリクエスト
あとはWi-Fiのときと同じようにビルトインされているHTTPブロックなどを使用してインターネット側のリソースにアクセスするだけです。UnitCatMの専用ブロックを使用する必要はありません。
下記ではインターネット側のリソースにアクセスする例として米Yahoo!FinanceのAPIから147A(ソラコム)の株価データを取得する例を示しています。
def get_stock_info_147A():
http_req = requests.get('https://query2.finance.yahoo.com/v8/finance/chart/147A.T', headers={})
status_code = http_req.status_code
print((str('Response stock info: status ') + str(status_code)))
response_json = http_req.json()
return response_json
おわりに
セットアップは少々煩雑ですが、それ以外のロジック部分は基本的にはUIFlowのビジュアルプログラミングで実装を完結できるようになります。
また、ちょっとしたセットアップは必要ですがSORACOM Airを利用している場合はSORACOM Napter経由でリモートからMicroPythonのWebREPLにアクセスしてコマンド操作できるなど、UnitCatMモジュールの活用幅が飛躍的に広がります。ぜひお試しください!