[M5StickC+UIFlow] Tips  ※随時更新

# 検証環境
本体:
　M5StackC
ファームウェアバージョン:
　UIFlow-v1.3.2

# USBモードに変更したい
[こちらのページ](https://qiita.com/nagase/items/9df1f78a7a51047a3e65)を参考にしてください。

# USBポートに刺しても認識しない
Windows Update へのアクセスを制限されている環境（WSUS サーバ以外アクセス禁止等）で、公式サイトからダウンロードできる「[**CP210X Driver**](https://m5stack.com/pages/download)」では、M5StickC を刺しても COM ポートを認識してくれません。
「[**FTDI VCP Driver**](https://www.ftdichip.com/Drivers/VCP.htm)」をインストールしてください。

# USBモードでのポート名を知りたい。
Windows PC の場合は `ComX`、WSL環境では `/dev/ttySX` （X≒ComXの番号と同一）です。
※その他の環境は試してないので分かりません。

# NTPサーバと時刻同期するには
`UIFlow-v1.3.2` Firmware には時刻は設定できますが、NTPサーバと同期（ntp_sync）するモジュールが入っていません。
[こちらのページ](https://qiita.com/inasawa/items/a1830266c1eceb714884)で公開されている `ntptime.py` を使わせていただくと同期できるようになります。

ちなみにファイルをデバイスへコピーするには `ampy` を使いましょう。

```python:コーディング例
import ntptime

offset = 9 * 3600
ntptime.host = 'ntp.nict.jp'
ntptime.settime(offset)

now = utime.localtime() #現在時刻の取得
print(now) #(YYYY, MM, DD, HH, MM, SS, MS)
```

# VSCode から M5StickC にアクセスしたい
拡張機能 `vscode-m5stack-mpy` をインストールしましょう。
使い方については[こちらのページ](https://marketplace.visualstudio.com/items?itemName=curdeveryday.vscode-m5stack-mpy)の「Quick Start」に書かれています。

# ampy で M5StickC にアクセスできない
いざ ampy で M5StickC にアクセスしようとしても、次のエラーメッセージが表示されてアクセスできません。

```
ampy.pyboard.PyboardError: could not enter raw repl
```

ampy はデバイス側でREPL（対話モード）が使える状態になってないと利用できません。

そのような場合は、一旦 ターミナル（`TeraTerm`（Windows）や `screen`（Linux等））からデバイスにシリアル接続します。（baudrate=115200 ）
接続後エンターキーを押しても無反応なはずですので、そこで何度か `Ctrl` + `C` を押せば、見慣れた Python インタプリタのプロンプト（`>>>`）が表示されます。

最後にその状態からターミナルをデバイスから切断すれば、ampy でアクセスできるようになります。
※ screen の場合、デタッチのみではセッションが残ってしまいますので、確実にセッションも切断しましょう（`session kill PID`）

ちなみにデバイスの電源をOFF/ONしてしまうと、再度 ampy でアクセスできなくなります。
その場合はもう一度ターミナル接続してREPLが利用できる状態にする必要がありますので注意してください。

# 起動時にアプリを自動実行させたい

独自の `main.py` を作成、デバイスへ転送し、電源を再投入するけど自動実行されない。
大抵の方はそんな状態に陥ると思います。（私もそうなりました）

ネット上の情報では対応方法を見つけることができず、大抵は「**boot.py が実行された後、main.py が実行される**」とだけしか書かれていません。

当方で調査、検証した結果、独自アプリを自動起動させる手段は、以下の２通りがあるようです。

##（手段１）/flash/config.json を変更する

まずは独自アプリを `main.py` というファイル名で作成、デバイスにアップロード（`/flash/main.py`）します。

そして `/flash/config.json` を次のように変更します。
（`/flash/config.json` 内のキー `start` の値を `flow` から `app` に変更するだけです）

```json:config.json（変更前）
{"server": "Flow.m5stack.com", "start": "flow", "mode": "usb", "wifi": {"ssid": "", "password": ""}}
```

```json:config.json（変更後）
{"server": "Flow.m5stack.com", "start": "app", "mode": "usb", "wifi": {"ssid": "", "password": ""}}
```

変更後に電源を再投入すれば、UIFlow のロゴマークが表示された後に `main.py` が自動実行されます。

##（手段２）/flash/apps 下にアプリを配置する　※推奨

独自アプリを作成し、デバイスの `/flash/apps` 下にアップロードします。
※今回は `/flash/apps/hoge.py` というアプリを配置しました。LCD中央に「HOGE」と表示するだけのアプリです。
※各ボタンの配置がわからない時は[こちらの画像](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/94476/b671e312-453a-fd88-e2de-a3a7bb3652e2.jpeg)を参照してください。

一度電源をOFFし、**ボタンA**を押しながら**パワーボタン**を押して電源を投入します。
下図のように `Program` が表示されます。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/94476/148ba360-208a-9256-84ea-00d65d8f6f4f.jpeg" width="25%">

**ボタンB**を押して下図のように `APP.List` を表示します。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/94476/ad10d0a7-5cbb-a199-3076-ea5f0ba78cde.jpeg" width="25%">

**ボタンA**を押してアプリのリストを表示します。
更に**ボタンB**を押して自動実行したいアプリ（今回は `hoge`）を選択します。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/94476/8d123135-e257-d148-ab04-05f2e549bb5d.jpeg" width="25%">

アプリ選択後、**ボタンA**を押すと選択したアプリが実行されます。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/94476/91e4b5d8-e5e6-de3d-b1d7-54cd8c7ba6b9.jpeg" width="25%">

以上の操作で、電源再投入後も選択したアプリが自動実行します。

# AWS IoT Core と連携したい

**無理です、諦めてください。**

･･･というのは半分冗談で半分本気です。

実は執筆時点の UIFlow-v1.3.2 と M5StickC の組み合わせで SSL(TLS)接続を行うと、メモリ絡みのエラーが頻発します。（`mbedtls_ssl_handshake error` や、`Stack Overflow` でファームウェアがクラッシュ）

だからTSL接続が必須なIoTクラウドサービス（AWS IoT Core や Google Cloud IoT Core 等）に対しては、**直接連携することが出来ません**。
（将来のバージョンアップで出来るようになるかもしれませんが）

そこで**直接連携**出来ないのであれば、ブローカー的なものを間に挟めば良いのです。そう、**MQTTブローカー**です。
MQTTブローカーのオープンソース実装である「**Mosquitto**」をVMインスタンスやコンテナ上で稼働し、更に**ブリッジ機能**を利用することで、AWS IoT Core に接続し、メッセージ交換ができるようになります。

[**連携イメージ**]
![Mosquitto.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/94476/18f09282-86e0-da28-327f-14c14623c6a7.png)

## 環境構築

MQTTブローカーを使った AWS IoT へのブリッジ環境の構築方法は、クラメソさんの[このページ](https://dev.classmethod.jp/cloud/aws/mosquitto-bridge-to-awsiot/)が参考になると思います。


（実際の本番環境では MQTT ブローカー自身が SPOF にならないよう、K8s 上に実装する等の工夫が必要でしょう。）

## 更にもう一工夫
クラメソさんの記事では、`bridge.conf` 内のブリッジするトピックを、以下のように固定していました。
（`awsiot_to_localgateway`、`localgateway_to_awsiot`、`both_directions`）

```conf:/etc/mosquitto/conf.d/bridge.conf（一部抜粋）
# Specifying which topics are bridged
topic awsiot_to_localgateway in 1
topic localgateway_to_awsiot out 1
topic both_directions both 1
```

ブリッジするトピックの指定ですが、実はワイルドカード（`#`）を利用することでき、例えば publish されたトピック全てをブリッジする場合は次のように記述することができます。

```conf:/etc/mosquitto/conf.d/bridge.conf（一部抜粋）
# Specifying which topics are bridged
topic # both 1
```

### AWS IoT の 「$aws/things/～」 へ Publish する場合

AWS IoT の場合、デバイスから Publish するトピックに `$aws/things/～` を指定する場合もあります。（Shadow 等）
Mosquitto では Publish されたトピックに `$` 記号が含まれていると、正常に転送されない不具合（仕様？）があるようです。

そんな場合は次のように設定することで、 MQTT ブローカーから AWS IoT へ Publish するトピック全てに `$aws/` を先頭に付与することが出来ます。

```conf:/etc/mosquitto/conf.d/bridge.conf（一部抜粋）
# Specifying which topics are bridged
topic # out 1 "" $aws/
topic # in 1
```

デバイスからは `$aws/` を除いたトピック（`things/～`）へ Publish することで、目的のトピックへ転送されます。

### そもそも bridge.conf の内容が反映されない

`/etc/mosquitto/conf.d` 下に `bridge.conf` を置いただけでは、設定が反映されない場合があるようです。（手元にある CentOS7 がそうでした）

その場合は `/etc/mosquitto/mosquitto.conf` に、次の `include_dir` 行を追加してください。

```conf
include_dir /etc/mosquitto/conf.d
```

# Timer で OSError 261 が発生する

マルチスレッドの実行でよく使う `Timer` ですが、その際スクリプトは次のように書くと思います。

```python
from machine import Timer

def int_handler_timer_0(t):
    print('Timer0')

def int_handler_timer_1(t):
    print('Timer1')

t0 = Timer(0)
t0.init(period=10000, mode=Timer.PERIODIC, callback=int_handler_timer_0)
t1 = Timer(1)
t1.init(period=5000, mode=Timer.PERIODIC, callback=int_handler_timer_1)
```

一見問題ないように見えるスクリプトですが、いざ実行すると `OSError: 261` が発生し、`Timer(1)` 側がスタックしてしまいます。

検証したところ、M5StickC には Timer が2個しかなく、更にその2個は `-1` 、`0` を指定しなければスタックしてしまうようです。

なので上記スクリプトを実行するには、次のようにそれぞれ `Timer(-1)`、 `Timer(0)` とする必要があります。

```python
from machine import Timer

def int_handler_timer_0(t):
    print('Timer0')

def int_handler_timer_1(t):
    print('Timer1')

t0 = Timer(-1)
t0.init(period=10000, mode=Timer.PERIODIC, callback=int_handler_timer_0)
t1 = Timer(0)
t1.init(period=5000, mode=Timer.PERIODIC, callback=int_handler_timer_1)
```

また Timer は 2個しかありませんので、次のように 3つ目の Timer を追加すると `OSError: 261` が発生し、`Timer(1)` がスタックします。

```python
from machine import Timer

def int_handler_timer_0(t):
    print('Timer0')

def int_handler_timer_1(t):
    print('Timer1')

def int_handler_timer_2(t):
    print('Timer2')

t0 = Timer(-1)
t0.init(period=10000, mode=Timer.PERIODIC, callback=int_handler_timer_0)
t1 = Timer(0)
t1.init(period=5000, mode=Timer.PERIODIC, callback=int_handler_timer_1)
t2 = Timer(1)
t2.init(period=1000, mode=Timer.PERIODIC, callback=int_handler_timer_2)
```

#M5StickC向けのPythonリファレンスとかあるの？

UIFlow の Python ライブラリが GitHub リポジトリ [UIFlow-Code](https://github.com/m5stack/UIFlow-Code) に公開されており、そこの [Wiki ページ](https://github.com/m5stack/UIFlow-Code/wiki
) に M5Stack（M5StickC）向けリファレンスがあります。

また、UIFlow 自体は `MicroPython v1.11` をベースに開発されているとのこと（[参考](http://community.m5stack.com/topic/1556/micropython-version-1-12-released/5)）なので、[MicroPython v1.11 のリファレンス](http://docs.micropython.org/en/v1.11/esp32/quickref.html) も役立つと思います。（**ESP32 向けのリファレンス**を参照してください）