M5Stack用MicroPythonのビルドとカスタマイズ

# はじめに

M5StackでMicroPythonを使った開発を行うためのMicroPythonのファームウェアのビルド済みイメージ・ファイルは、M5Stackから提供されています。
これらのファームウェアは [M5Burner](http://res.m5stack.com/M5Burner/M5Burner.zip) に含まれており、M5Burnerを使ってM5Stackに書き込んで使うことができます。

M5Stackの公式MicroPythonファームウェアは、M5Cloudや [UI.Flow](http://flow.m5stack.com/) を使って開発することを目的として調整されています。そのため、ベースとなったMicroPythonの実装にある機能がすべて有効になっているわけではありません。
こういった機能を有効化するなどのカスタマイズを行うためには、MicroPythonを自分でビルドする必要があります。

M5Stackで使われているカスタム版MicroPythonのソースコードは、GitHubの [リポジトリ](https://github.com/m5stack/M5Stack_MicroPython) から入手可能です。

# カスタマイズとビルド

## Windowsでのビルド環境の構築

### ESP-IDFを使う環境

ESP32用MicroPythonはESP-IDFでビルドできるように作られているので、基本的に WindowsでのESP-IDFの開発環境を構築する手順と同様です。
ESP-IDFの公式ドキュメントに [セットアップ手順](https://docs.espressif.com/projects/esp-idf/en/latest/get-started/windows-setup.html) が記載されているので、この手順でセットアップを行います。

ドキュメントにあるリンクをたどって、Espressifの公式ページからWindows用 all-in-one パッケージをダウンロードします。執筆時点では　 https://dl.espressif.com/dl/esp32_win32_msys2_environment_and_toolchain-20181001.zip です。

ダウンロードしたら適当な場所に展開しておきます。パスにスペースや日本語を含まない場所が無難です。

### シンボリック・リンクを扱うための設定

MicroPythonのリポジトリでシンボリック・リンクを使っているため、シンボリック・リンクを使えるように設定する必要があります。

Windows 10 April 2018 Update (1803) 以降は、デフォルトでシンボリック・リンクを作成する権限の設定が行われているようですが、それ以前のバージョンでは権限の設定が必要になるようです。筆者は1803でしか試していないので、この作業は行っていません。
検索すると手順を説明している記事がでてきますので、そちらを参考に設定してください。

ビルド環境である `MSYS2` はデフォルトではWindowsのシンボリック・リンク機能を使わない設定になっていますので、設定を変更します。
ESP-IDFのall-in-oneパッケージを展開したディレクトリにある `mingw32.ini` をエディタで開き、`MSYS=winsymlinks:nativestrict`を設定している行のコメント・アウトを解除します。

```ini
MSYS=winsymlinks:nativestrict
#MSYS=error_start:mingw64/bin/qtcreator.exe|-debug|<process-id>
#CHERE_INVOKING=1
#MSYS2_PATH_TYPE=inherit
MSYSTEM=MINGW32
```

また、Windows用のGit自体もシンボリック・リンクを使わない設定になっているため、設定を変更する必要があります。

```bash
git config --global core.symlinks true
```

## ソースコードの取得と設定

GitHub上に M5Stack_MicroPython としてM5Stack用MicroPythonがアップロードされています。

公式のMicroPythonのBoris Lovosevic氏によるESP32向けカスタム版として MicroPython_ESP32_psRAM_LoBo があります。
M5Stack_MicroPython は MicroPython_ESP32_psRAM_LoBo フォークしてM5Stack向けの機能を追加したものになっています。

ESP-IDFのall-in-oneパッケージを展開したディレクトリにある `mingw32.exe` を実行して開いたターミナルで適当なディレクトリに移動して M5Stack_MicroPython リポジトリを clone します。

```bash
git clone https://github.com/m5stack/M5Stack_MicroPython.git
```

ビルド用のスクリプトが `MicroPython_BUILD/BUILD.sh` にあるので、`MicroPython_BUILD` ディレクトリに移動して以下のとおり呼び出して、MicroPythonの設定を行います。

```bash
cd MicroPython_BUILD
./BUILD.sh menuconfig
```

しばらくすると、ESP-IDFのプロジェクト設定の画面が表示されます。

<img width="455" alt="ESP-IDFのプロジェクト設定" src="https://qiita-image-store.s3.amazonaws.com/0/58388/f7971855-5a54-a4c0-1663-7a6123590a94.png">

`MicroPython` エントリの下に、MicroPythonの各種設定項目が含まれています。

<img width="455" alt="MicroPythonの設定項目" src="https://qiita-image-store.s3.amazonaws.com/0/58388/1a635692-72f7-388b-6cad-3f4b485a2d3e.png">

さすがに全設定項目は説明しきれませんので、筆者がよく設定する項目についてのみ説明します。選択式の設定項目の場合の設定値は、`Y` が選択、`N` が非選択を意味することとします。

| グループ | 名前 | 設定値 | 内容 |
|:---|:---|:---|:---|:---|
| MicroPython/System settings | Enable Ftp Server | Y | FTPサーバ機能を有効にする |
| MicroPython/Modules | Use Curl module | Y | curlモジュールを有効にする |
| Compiler options | Optimization Level | Release (-Os) | リリース用に最適化を行う |
| Component config/ESP32-specific | Support for external, SPI-connected RAM | Y | PSRAMを有効にする |
| Component config/ESP32-specific/SPI RAM config | Ignore PSRAM when not found | Y | PSRAMが見つからなかってもpanicせずにPSRAMを無効化して起動する |
| Component config/ESP32-specific | Panic handler behavior | Invoke GDBStub | panic時にGDB Stubを起動する |

* MicroPython/System settings/Enable Ftp Server

    MicroPython_ESP32_psRAM_LoBo に実装されている [FTPサーバ機能](https://github.com/loboris/MicroPython_ESP32_psRAM_LoBo/wiki/ftpserver) を有効化します。 `network.ftp` 以下のメンバーからFTPサーバ機能を操作できるようになります。

* MicroPython/Modules/Use Curl module

    MicroPython_ESP32_psRAM_LoBo に実装されている libcurlによるHTTP/FTP/SMTPを使ったネットワーク・アクセス機能を有効化します。
    `import curl`として[curlモジュール](https://github.com/loboris/MicroPython_ESP32_psRAM_LoBo/wiki/curl) を読み込んで使います。

* Compiler options/Optimization Level

    コンパイル時の最適化レベルを設定します。デバッグ中は `-Og`にしておくとバグなどでMicroPython自体がクラッシュしたときに原因を探りやすくなります。リリース時は `-Os` にしておくと、MicroPythonのファームウェアのサイズが小さくなり、動作速度を改善する各種最適化が有効になります。
    MicroPythonユーザーとしては `-Os` にしておくのがよいでしょう。

* Component config/ESP32-specific/Support for external, SPI-connected RAM
* Component config/ESP32-specific/SPI RAM config/Ignore PSRAM when not found

    M5Stack FIREなどに搭載されている外付けSRAM (PSRAM) を有効にするための設定です。通常PSRAMを有効化している場合にPSRAMが見つからなければ、その時点でpanicします。`Ignore PSRAM when not found` を入れておくことにより、PSRAMが見つからない場合もpanicせずにPSRAMが無効なものとして起動します。

* Component config/ESP32-specific/Panic handler behavior

    ファームウェアがpanicしたときの挙動を設定します。デフォルトはデバッグ出力にpanic時のレジスタの値やスタック・トレースを出力して動作を停止します。panic時にデバッガを接続できる状態にしたい場合は、`Invoke GDBStub`を選択しておきます。この場合panic時にGDBを接続するためのデバッグ・スタブが実行され、シリアルポート経由でGDBを接続してpanic時の状態を調査することができます。

## ビルドと書き込み

MicroPythonの設定が終わったら、ビルドを行います。

```bash
./BUILD.sh all -j9 -v
```

`-j9`の部分は`make`コマンドと同様ビルドの並列実行数を指定するオプションです。ビルド環境のPCの性能に応じて適宜数字を増減してください。

`-v`オプションを指定すると、ビルド中のメッセージがコンソールに出力されます。`-v`を付けないと、一切メッセージが表示されずビルドの進捗状況がわからないので、つけておくことをお勧めします。

ビルドが終わったら、`flash`コマンドと`flashfs`コマンドを実行してファームウェアとSPIFFSのイメージを書き込みます。

```bash
ESPPORT=COM6 ./BUILD.sh flash
ESPPORT=COM6 ./BUILD.sh flashfs
```

`ESPPORT`環境変数で書き込み先のM5Stackが接続されているシリアル・ポートの名前を指定します。例ではCOM6に接続されている場合として`ESPPORT=COM6` を指定しています。

書き込み完了後はブートローダ起動状態のままとなりますので、リセットボタンを押すかシリアルポートを開くなどしてリセットを行います。

以下の例では、ESP-IDFに付属している IDF monitorを起動してリセットとデバッグ・メッセージの表示を行います。

```bash
ESPPORT=COM6 ./BUILD.sh monitor
```

# スクリプトの転送

カスタムビルド版のMicroPythonでは、M5GOやM5Stack FIREの出荷時イメージのように、起動直後に実行されるスクリプトが含まれていません。
このため、スクリプトの転送には M5Cloud や UI.Flow を使うことができません。
また、Wi-Fi APとして起動するようにもなっていないので、どうにかしてWi-Fiの接続設定を行う必要があります。

イメージを書き込んだ直後は、自動実行されるスクリプトが含まれていないため、MicroPythonがREPLの状態になります。この状態でユーザはプログラムを手動で入力して`network`モジュールを操作してWi-Fi接続を行うことは可能です。

ただし、さすがに毎回手動で設定するのは面倒ですので、アクセスポイントへの接続とFTPサーバの起動までを行うスクリプトを用意しておき、REPLのペーストモードで入力して実行します。
スクリプトは[Gist](https://gist.github.com/ciniml/200f05eada40ab7c067d760bc69779de)に置いてあります。 

REPLのプロンプト `>>>` が出ている状態で `Ctrl+E` を押すとペーストモードになります。この状態でクリップボードからコピー(TeraTermの場合はマウス右クリック) を行って、コピーが終わったら `Ctrl+D` を押してペーストモードを抜けます。これにより、複数行のスクリプトを効率よく入力して実行することができます。

![起動とWi-Fi接続](https://qiita-image-store.s3.amazonaws.com/0/58388/75ac2a05-62bf-c2ca-ae65-c39c8dd41e38.gif)

Wi-Fi APに接続後、割り当てられたIPアドレスを表示し、FTPサーバを起動しています。

あとはWinSCPなどのFTPクライアントを使って実行したいスクリプトを転送します。

![WinSCPでのスクリプトの転送](https://qiita-image-store.s3.amazonaws.com/0/58388/bf50aa4e-1fb1-2dc4-32a5-d5b4a081fe0c.gif)

転送したスクリプトのうち、`appconfig.py`にWi-Fiの接続設定などを書いてあるため、リセット後は自動的にアクセスポイントに接続し、動作を開始します。

![リセット後の動作](https://qiita-image-store.s3.amazonaws.com/0/58388/1a243356-6a25-e89b-56c1-b3dcf8bc5cad.gif)

# ファームウェア書き込み時にスクリプトを書き込む方法

`BUILD.sh flashfs` で書き込まれるSPIFFSのイメージの内容は `MicroPython_BUILD/components/internalfs_image/image` ディレクトリの内容です。初回書き込み時からあらかじめ入れておきたいスクリプトやデータはここに置いておくと便利です。

# コンパイル済みスクリプトの追加

`MicroPython_BUILD/components/micropython/esp32/modules` に含まれているPythonスクリプトは、MicroPythonのビルド時にバイトコードに変換された状態で組み込まれます。このため、滅多に変更しないスクリプトやサイズの大きいスクリプトはここに置いておくことにより、ESP32上でのバイトコードへの変換を行わずに済みます。

同様に、`mpy-cross`ツールを手動で呼び出すことにより、任意のMicroPythonスクリプトをホスト上でバイトコードに変換しておくことができます。
以下の例では、`main.py`をバイトコードにコンパイルして `main.mpy` を生成します。


```bash
MicroPython_BUILD/components/micropython/mpy-cross/mpy-cross.exe main.py
```

コンパイルした`*.mpy` ファイルは`*.py`と同様にimportできますので、FTP経由などでM5Stackにコピーして実行できます。

# おわりに

以上でM5Stack用MicroPythonをカスタマイズしたものをM5Stackで動作させる手順は終わりです。

MicroPythonはいろいろカスタマイズの余地がありますので、設定を変更したり、パフォーマンスが足りない部分をC言語で記述したりして有効活用していければと思います。