SORACOM meets UIFlow2 〜ビジュアルプログラミングでもSORACOMを使いたい！〜

:::note info
この記事は[SORACOM Advent Calendar 2024](https://qiita.com/advent-calendar/2024/soracom)の20日目の記事です。
:::

## はじめに
ごきげんよう、ソラコムCREの岡田（hisaya）です。こういう記事を書くときはツカミに何を書こうか毎回迷っているのですが、今年は同じチームの[kanuの記事](https://qiita.com/kanuu0504/items/b33acdd1144b807e8229)の衝撃的なツカミには勝てそうにないので、聞かれてもいない好きな寿司ネタを発表します。ビンチョウマグロ🍣

ひと滑りしたところで本題に入ると、MicroPythonで `network.PPP` モジュールがちゃんと使えるようになってPPPoS接続できるようになっていたことと、UIFlow版MicroPythonがオープンソースになっていたことが個人的な今年の大きな発見として挙げられるのですが、そのときに勢いで書いたQiitaの記事では `Execute mpy code` ブロックにPPPoS接続のためのコードを（半ば無理やり）書いていました。

https://qiita.com/Plemling138/items/3f6541505f730a7b3796

せっかくのビジュアルプログラミング環境でコードを手書きしないといけないのはメリットが半減するよなぁ…と思っていたのですが、その後[M5 Japan Tour 2024 Autumn](https://m5stack.connpass.com/event/329550/)でBlock Designerの話を聞いて、これを使ってPPPoS接続のセットアップやSORACOMの各種サービスのエントリポイントへのリクエストをカスタムブロック化すれば、UIFlowのビジュアルプログラミングだけで完結できるようになるのでは？と思いました。

この記事では実際にBlock Designerを使ってカスタムブロックを作ったことと、作成した「SORACOMをUIFlowで有効活用するためのカスタムブロック」について紹介します。なお、この記事で実際に作成したカスタムブロックは下記リポジトリで公開しています。

https://github.com/sayacom/UIFlow2_SORACOM

:::note warn
UIFlowのカスタムブロックおよびBlock Designerは機能自体がアルファ版です（2024-12-20時点）。
また、上記カスタムブロックも個人として開発したものであり、SORACOMとして提供・サポートするものではありません。
リポジトリの注意事項を確認のうえ、自己責任で使用してください。
:::

## Block Designer

カスタムブロックは前述の[Block Designer](https://block-designer.m5stack.com/)を使用して作成します。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/182576/493612f8-2e63-ef3f-ec4f-f2a3a1faf20d.png" width="50%" />

UIFlow2の画面左下 `Custom (Alpha)` をクリックしてファイルインポート画面を表示すると、モーダル右上に[Block Designer]のボタンが出てきます。
これをクリックすると下記のようなGUIで関数ブロックや引数を構築する画面が出てきます。

![スクリーンショット 2024-12-18 22.53.45.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/182576/4df063ce-6890-3017-899c-8459ee334722.png)

操作自体はおおむね直感的で、とりあえず必要そうな関数をポチポチ作り、戻り値があるかないかでTypeを変え、ラベルと引数部分の変数の型やドロップダウンリストのアイテムを追加していきます。
GUI操作の裏ではPythonコードが作られていて、上部 `Python` タブをクリックすると、エディタ上にGUIで構築した関数とそれを定義するためのYAMLのようなコメントが追加されたコードが出てきます。

![スクリーンショット 2024-12-18 23.04.03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/182576/5a72cedd-39ce-7ad9-8765-7e9ecfea5556.png)

Pythonコードからブロックを起こしたり、エディタ側で加えた変更をブロックに反映することもできますが（Update Blocksボタン）、引数の順番やデフォルト値がうまく反映されない場合がありました。私は一旦Block Designer側で必要なカスタムブロックの外装を構築してからUIFlowのコードエディタで検証した処理をBlock Designerに戻し…ということを繰り返してコードブロックを構築しました。

## Soracom カスタムブロック

ここからは作成したSoracomカスタムブロックについて説明していきます。

### 全体
![all_functions.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/182576/3f6ac2aa-0bcd-dc93-f550-9652ae051948.png)

おもな特徴は下記のとおりです。
- PPPoS接続に必要な設定のコードブロック（認証設定、接続開始・終了、状態管理など）を提供します
- SORACOMのデータ転送サービス（Beam, Funnel, Funk, Harvest および Unified Endpoint）やメタデータサービスへのアクセスを1ブロックで完結するコードブロックを提供します
- 実装における特定の通信モジュールへの依存を最小限にし、通信モジュールとのやりとりはUIFlow側で生成したインスタンスを使用する実装になっています（UnitCatM+GNSS での動作を確認しています）

### PPP 接続用コードブロック
`network.PPP` を使用してPPPoS接続を開始するコードブロック群です。
![ppp_functions.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/182576/353a5b7b-9a34-b4e0-7c84-32b9174cb1f6.png)

PPPoS接続を開始するためには事前に通信モジュールがダイヤルアップモードに遷移している必要がありますが、これを開始するためには必然的に通信モジュールに適したATコマンド操作が必要になります。このため単純にPPPインスタンスを保持するだけではなく、通信モジュールに対してアクセス可能なインスタンスも保持する必要があります。

M5が販売している通信モジュールのユニットはUIFlowで使用可能なコードブロックが存在するので、このコードブロックで設定可能な内容はそちらで設定してSoracomインスタンスに `Set UART/Modem interface...` コードブロックを介して引き回す形をとっています。
それ以外の通信モジュールを使用する場合はUARTインスタンスを渡して、UIFlow版MicroPythonで提供している [`driver.simcom.common`](https://github.com/m5stack/uiflow-micropython/blob/master/m5stack/libs/driver/simcom/common.py) パッケージ内の`Modem`クラス（基底クラス）を内部的にインスタンス化して保持し、適宜ATコマンドを送受信できるようにしています。ゆえに、Soracomクラスは実質的にModemとPPPのラッパーのような働きをします。

なお、前述のとおり一度PPPoS接続を開始してしまえば通信を使用する関数はセルラー通信を利用することになるので、ユニット独自の関数や後述のデータ転送サービス用コードブロックを使用しなくても、ビルトインされたソケット通信やHTTPコードブロックを使っても問題ありません。
また通信モジュールの初期化や設定と切り離して管理しているので、原理的には適切な初期設定（APNなど）を実施したうえで通信モジュールのインスタンスをSoracomインスタンスに渡し、PPPoSの認証情報の指定を変えれば他キャリアのSIMも使えるかもしれません（未検証）。

### SORACOM用コードブロック

SORACOMの各種データ転送・蓄積サービスにデータを送信するコードブロックです。内部的にはHTTPコードブロックのような `requests` をラップしているだけではあるのですが、サービスないしエントリポイントを選択してペイロードとなるMapオブジェクトを渡すだけでデータを送信できるので、UIFlowのブロックが少しすっきりします。
![soracom_functions.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/182576/29a0fd23-d223-0283-9062-dabf2f9cc617.png)

対応するサービスは下記のとおりです

- `Send data as JSON by HTTP` コードブロック
    - [Unified Endpoint](https://users.soracom.io/ja-jp/docs/unified-endpoint/)
    - [SORACOM Harvest Data](https://users.soracom.io/ja-jp/docs/harvest/)
    - [SORACOM Funnel](https://users.soracom.io/ja-jp/docs/funnel/)
    - [SORACOM Funk](https://users.soracom.io/ja-jp/docs/funk/)
- `Send data as JSON by HTTP to SORACOM Beam` コードブロック
    - [HTTP エントリポイント](https://users.soracom.io/ja-jp/docs/beam/http/)
    - [Web サイトエントリポイント](https://users.soracom.io/ja-jp/docs/beam/website/)
- [メタデータサービス](https://users.soracom.io/ja-jp/docs/air/use-metadata/)
    - [SIMタグ](https://users.soracom.io/ja-jp/docs/air/set-tag/) （取得・更新・削除）
    - ユーザーデータ

これらのコードブロックを使用したレスポンスは、`Get latest HTTP response...` コードブロックを介して取得できます。

## 実装例

実際にSoracomカスタムブロックを使用したUIFlowのプロジェクトの全体を示します（画像は拡大してください）。
この例ではsetup関数内でUnitCatMモジュールの初期化とPPP接続を確立したうえで、ダミーのセンサーデータをMapオブジェクトに格納し、ペイロードに渡してUnified Endpoint経由やBeam経由でデータを送信するような関数群を定義しています（サンプル実装集を兼ねているので呼び出していない関数もあります）。
ご覧のとおり、プロジェクト内でMicroPythonのコードを扱うことなく（`Execute mpy code` ブロックを一切使用せず）すっきりした形でプログラムを構成できることがお分かりいただけると思います。
![examples.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/182576/8ce98b1a-365f-e692-70fe-5d93dc3942f1.png)

:::note info
M5Stack Core 2向けに作成したプロジェクトファイルはリポジトリ内にあります。
カスタムブロック自体はUIFlow版MicroPythonが動作する環境であれば基本的にはどれでも動作すると思います。
https://github.com/sayacom/UIFlow2_SORACOM/blob/main/sample_projects/SORACOM_CORE2.m5f2
:::

:::note warn
カスタムブロックをプロジェクトにインポートしてもブロックとして表示されない場合があります。
その場合はリポジトリに記載の方法を参考にプロジェクトファイルをローカル側で修正するか、実装例のプロジェクトを読み込ませて変更する形でお試しください。
:::

### おまけ: UIFlowでも通信量チューニングができるかも？

ちょっと話は変わりますが、いわゆるArduino環境でセルラー通信するスケッチを開発する際には[TinyGSM](https://github.com/vshymanskyy/TinyGSM)ライブラリを使用することが多いと思います。このライブラリは対応する通信モジュールに搭載されたプロトコルスタックをATコマンド経由で使用する方式のため、通信量の観点ではUDP通信をサポートしていないことで最低でもTCP通信のオーバーヘッドが常に発生するといったデメリットがあります。

PPPoS接続のメリットは通信モジュール側のプロトコルスタックに依存することなく、MicroPythonにビルトインされた各種プロトコルをそのまま取り扱えることです。
あえてSoracomカスタムブロックには入れていませんが、PPP接続を開始した状態で下記のようなソケット通信のブロックを使用するとTCP/UDPのペイロード部分をそのままSORACOMに送信できます。ビルトインされたソケット通信ブロックだけで最小限の通信量に抑えて通信できるようになり、バイナリパーサーと組み合わせることで任意のWebアプリケーションやFaaSなどで取り扱いやすい形式に変換してデータを転送できます。
![examples_socket.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/182576/9f80ff56-f310-681f-e8eb-34d15898fb0f.png)
この例では[バイナリパーサー](https://users.soracom.io/ja-jp/docs/binary-parser/)で `@json` （事前定義済みフォーマット）を指定する想定で、JSONペイロードをそのままUnified Endpointに送信しています。これだけでもHTTPのリクエストヘッダ分の通信量が削減でき、UDPの場合はさらにTCPハンドシェイク分の通信量も削減できます。バイナリパーサーの[カスタムフォーマット](https://users.soracom.io/ja-jp/docs/binary-parser/custom-format/)を使用することでさらに削る余地もあると思います。

## おわりに

この記事ではBlock Designerを使用してカスタムブロックを作成する方法と、実際に作成したSoracomカスタムブロックについて紹介しました。
Soracomカスタムブロックを使用すると、Wi-Fi前提で通信を使用するUIFlowプロジェクトを開発していた方は、セルラー通信に対応するユニットを導入して少しコードブロックを修正するだけでデバイスを屋外やWi-Fiのない環境でも利用できるようになるかもしれません。
また、UIFlowはM5Stackが取り扱うセンサやユニットを簡単に扱えるので、手軽なIoTプロジェクトに向いています。ぜひSORACOMと組み合わせて、独自のIoTデバイス開発にチャレンジしてみてください。