ORANGE-ESPerで、MicroPython + FabGLを実現 ~ ESP-WROVER編

2021/05/01

 ①MicroPython + FabGLが実現できたが、ヒープが減少

FabGLを組み込む前は、99KB程度MicroPythonのヒープ空きサイズがありましたが、さすがにVGA表示メモリが必要になるので、22KBまで減ってしまっていました。WiFiや、Bluetoothなどが使用できる上にVGA表示とてんこ盛りしましたが、簡単なプログラムなら使えるのではないでしょうか。


そこで、ヒープを増やすため調査すると、幸いにも、SPIRAM搭載のESP-WROVERを搭載したESP32-DevKitC開発ボードを見つけましたので、これを使うことにしました。


②ビルドで"IRAM0 segment data does not fit."が発生

menuconfigで、SPIRAMを有効にし、ビルドオプションを指定してビルドを行いましたが、リンカーのASSERTに引っ掛かり、"IRAM0 segment data does not fit."となってしまいました。


下記は、リンカーが割り当てた、各セグメントの割り振りです。"iram0_0_seg"は、128KBしかなく、これが溢れたようです。この領域は、CPUが高速にアクセスできる高速処理を必要としている関数群に割り当てることになっていますが、MAPファイルを確認すると、SPIRAM関連のMicroPython・FabGLの関数でオーバーフローしたようです。

Memory Configuration

Name Origin Length Attributes
iram0_0_seg 0x0000000040080000 0x0000000000020000 xr
iram0_2_seg 0x00000000400d0020 0x000000000032ffe0 xr
dram0_0_seg 0x000000003ffbdb5c 0x000000000001e6a4 rw
drom0_0_seg 0x000000003f400020 0x00000000003fffe0 r
rtc_iram_seg 0x00000000400c0000 0x0000000000002000 xrw
rtc_data_seg 0x000000003ff80000 0x0000000000002000 rw
rtc_slow_seg 0x0000000050000200 0x0000000000000e00 rw
extern_ram_seg 0x000000003f800000 0x0000000000400000 xrw
*default* 0x0000000000000000 0xffffffffffffffff

"IRAM0 segment data does not fit."の対処方法を調査すると、不要と思われる関数(機能)を外すという消極的な対策程度しか確認できませんでした。半分あきらめ気味で、FabGL内で"IRAM_ATTR"属性が付けられている関数を確認すると、"displaycontroller.cpp"内は、それほど高速性でなくても行けそうな感じでした。


そこで、ESP-IDFの機能で、リンカーへのセグメント割り当てオプションができる"Linker Script"があることがわかりましたので、"linker_fragment_file.lf"を作成し、"CMakeLists.txt"に必要な定義を追加しました。これで、"IRAM0 segment data does not fit."が回避できました。


"displaycontroller.cpp"内の関数は、全てFlashROMに移動させました。実行時は、CPUのCacheメモリ経由でアクセスされますので、大きな問題はないと思われます。CPUクロック周波数を240MHz、Flashクロックを80MHzに指定して、特に表示のちらつきは、無いようです。


ESP-WROVERの恩恵で、無事MicroPythonの空きヒープサイズがが4MBになりましたので、かなりの大きさのプログラムでも問題無いはずです。


③ESP-WROVERでSDカードが使えない

ここまでなんとか来ましたが、SDカードの動作確認をすると、エラーが出て使えませんでした。ESP-WROVER内部とESP32-DevKitC開発ボードの回路図を確認すると、ORENGE-ESPerでSDカード信号に使用しているPin(17)とPin(16)は、SPIRAMで使用しているため、ESP32-DevKitC開発ボード上は、NCになっていました。


そこで、オプションのシリアルI/Fで使う、Pin(2)とPin(34)に変更しました。ジャンパ線が必要ですが、ESP-WROVERに依存していますので、下記のようにESP32-DevKitC開発ボード上でジャンパ線を追加しました。これで、ORENGE-ESPerは、ESP-WROVERでもESP-WROOM両者で使えます。


但し、GPIO2は、ESP32のプログラミング書き込みに使用されているため、浮遊容量が増え、不安定になる場合があるようですので、ESP32のプログラミング書き込みは、ORENGE-ESPerから外してESP32-DevKitC開発ボード単独で行うことをお勧めします。また、シリアルポート番号が変わることがありますので、注意ください。

Pin(17) <-> Pin(2)
Pin(16) <-> Pin(34)

下記は、ジャンパ線を飛ばした後のESP-WROVER版ESP32-DevKitC開発ボードです。


④おまけモジュールの追加

最後に、MicroPythonを使いやすくするために、下記のモジューを"micropython/port/esp32/modules"

に配置し、ビルドするとコンパイル済mpy形式でビルドイメージに組み込まれます。


・uwipye.py    軽量pythonエディター

・upysh.py     ファイル操作shellライクコマンド

・mount_sd.py  SDカードのマウント('/sd'にマウント)


⑤ソース・バイナリの公開

MicroPythonは、MITライセンス、FabGLは、GPLライセンスで公開されていますので、今回は、GPLライセンスに従い、ソースとバイナリをtarボールで公開します。変更箇所は、Winmergeなどで差分を確認できると思いますが、オリジナルを変更した部分には"jr2xzy"の記述を入れています。

ソース・バイナリのtgz

ファイルサイズの関係で、fullclean状態になりますので、MicroPythonのソースツリーの"micripython/port/esp32"に上書きし、menuconfig後、ビルドしてください。また、ESP-WROVERの場合、"linker_fragment_file.lf"を有効にしてください。

## ESP-WROOM for ESPer ##

cd ~/esp32/micropython/ports/esp32
idf.py menuconfig

Serial flasher config --->
Flash SPI mode (QIO) --->
Flash SPI speed (80 MHz) --->
Flash size (4 MB) --->

Driver configurations --->
RTCIO configuration --->
[*] Support array `rtc_gpio_desc` for ESP32

Component config --->
ESP32-specific --->
CPU frequency (240 MHz) --->
[ ] Support for external, SPI-connected RAM

idf.py build

## ESP-WROVER for ESPer ##
cd ~/esp32/micropython/ports/esp32
idf.py menuconfig

Serial flasher config --->
Flash SPI mode (QIO) --->
Flash SPI speed (80 MHz) --->
Flash size (4 MB) --->

Driver configurations --->
RTCIO configuration --->
[*] Support array `rtc_gpio_desc` for ESP32

Component config --->
ESP32-specific --->
CPU frequency (240 MHz) --->
[*] Support for external, SPI-connected RAM

SPI RAM config --->
Set RAM clock speed (80MHz clock speed) --->
[*] Initialize SPI RAM during startup (NEW)

idf.py -D MICROPY_BOARD=GENERIC_SPIRAM build

バイナリは、"micropython/port/esp32"配下の"ESP-WROOM"及び、"ESP-WROVER"に置いています。ORANGE-ESPer用の"esptool.exe"と"upload.batを適当に変更して使えば、ビルドしなくても焼くことができるはずです。


下記は、"import mount_sd"を実行後、MicroPythonの空きヒープサイズが"4095296"になっています。"from uwipye import pye"を実行後、pye()エディターのキー機能が表示されます。



以上、ESP32(ESP-IDF)には結構苦労しましたので、Arduinoの有難さがわかります。ESP32の能力をフルに出すためには、ESP-IDFが必要な感じです。今後、FabGLのMicroPythonインタフェースが追加できれば、用途が広がるかもしれません。いずれにしろ、ORENGE-ESPerを楽しんだり、ESP32のデバッグに役立ていただければと思います。

(再クロール更新:2022/12/22)

ORANGE-ESPerで、MicroPython + FabGLを実現 ~ FabGLのESP-IDFへのポーティング

2021/04/30

 ①ESP-IDFの"hello_world_main.c"にAnsi-Terminalを追加

まず、FabGLライブラリの配置です。ESP-IDFでは、ソースツリーに"components"というサブディレクトリを配置すると、自動的に"CMakeLists.txt"をスキャンしてビルドの対象にしてくれます。

  +hello_world
+components
| +fabgl
| +arduino ESP32用Arduinoライブラリ
| +cores Arduinoコアライブラリ
| +libraries Arduinoオプションライブラリ
| +variants ボード依存ヘッダー
| +src FabGLライブラリ
| +CMakeLists.txt ビルド用スクリプト
+main メインプログラム

次に、CMakeLists.txtの記述です。

Ansi-Terminalが必要とする最低限のモジュールを順番にビルド対象にしていきます。コンパイルン時にヘッダーファイルが見つからない場合は、ヘッダーパスを追加し、エラーを解消していきます。ただ、ESP-IDFのバージョンが異なるため、ヘッダーファイルの内容が変更されている部分があるため、未定義の型・マクロの定義部分を見つけてソースコードを変更していきます。


コンパイルエラーを解消後、リンカーで未定義変数・関数が出てきますので、対象ライブラリを追加すると、CMakeLists.txtが完成します。この段階で、必要に応じコンパイラ・リンカーのオプションを追加します。


リンクが完了し、バイナリイメージをESP32に焼いて実行できるようになりました。Arduinoと同じVGA画面が表示でき、ホットしたのもつかの間、設定変更のダイアログを終了させると、"Guru Meditation Error"で落ちてしまいます。Arduinoでは、何ら問題ありませんでした。

Guru Meditation Error: Core  0 panic'ed (LoadProhibited). Exception was unhandled.
Core 0 register dump:
PC : 0x400eb927 PS : 0x00060f30 A0 : 0x800ea9a2 A1 : 0x3ffebd90
A2 : 0xfefefefe A3 : 0x3ffebdc5 A4 : 0x00000000 A5 : 0x00000000
A6 : 0xffffffff A7 : 0x3ffed290 A8 : 0x80082d08 A9 : 0x3ffebd50
A10 : 0x3ffe3efc A11 : 0x00000000 A12 : 0x3ffb7028 A13 : 0x00000000
A14 : 0x00000000 A15 : 0x00060023 SAR : 0x00000008 EXCCAUSE: 0x0000001c
EXCVADDR: 0xfefeff3a LBEG : 0x4000c46c LEND : 0x4000c477 LCOUNT : 0x00000000

ELF file SHA256: 1e6c549e1a65be00

Backtrace: 0x400eb927:0x3ffebd90 0x400ea99f:0x3ffebdc0 0x400d3492:0x3ffebdf0
0x400d3616:0x3ffebe10 0x401163e9:0x3ffebe30 0x400f5b99:0x3ffebe50 0x40088bb6:0x3ffebe90

バックトレースアドレス・リンカーマップの確認、ログ追加で問題個所を特定はできました。通常問題なく通過していますが、問題がある場合、途中で落ちてしまいます。ESP32マイクロコントローラでは、命令RAMは、書き込み保護が掛かっているので、プログラムの破壊がなくても落ちてしまったわけです。


ESP32について調べていくと、例外が発生した場合には、対象アドレスが、EXCVADDRに表示される仕組みになっているので、NULLポインター・未確保ヒープ・解放済ヒープなのか切り分けができるようです。下記のオプションで有効になります。

Heap memory debugging  --->
Heap corruption detection (Comprehensive) --->


前記の例では、"EXCVADDR: 0xfefeff3a"でしたので、解放済ヒープでの値"0xfefefefe"に近い値で、このアドレスに対するオフセットで変数をアクセスしていることが想定できました。


ソースコードから、問題となったクラス変数ポインターが特定できました。そのクラスの生成・消滅を追っかけると、ダイアログの消去処理中に消滅が起こりえる部分を見つけました。わずかなタスク実行タイミングの差で発生する潜在バグでると考えられます。完全な修正ではないですが、回避できるタイミングで対象クラスの消滅を行うように変更しました。


これで、FabGLライブラリをESP-IDF上に移植できました。


②MicroPythonのソースツリーへの組込みとビルド

"hello_world"の場合と同様に、micropython/portの配下にFabGLライブラリを移行します。MicroPpythonのメインプログラム"main.c'から、MicroPpythonを開始する前にFabGLを初期化しておくようしました。MicroPpythonは、最大確保可能なヒープをMicroPpythonのヒープに割り当てますので、起動前に一時的なヒープの使用を終わらせておきます。"AnsiTerminal.ino.cpp"を変更し"fabgl_setup.cpp"を作成します。Ansi-Terminalの設定ダイアログを閉じると、MicroPpythonを起動させるシーケンスにします。

  +esp32
+components
| +fabgl
| +arduino ESP32用Arduinoライブラリ
| +cores Arduinoコアライブラリ
| +libraries Arduinoオプションライブラリ
| +variants ボード依存ヘッダー
| +src FabGLライブラリ
| +CMakeLists.txt ビルド用スクリプト
+main メインプログラム
 +modules 追加のMicroPythonモジュール

MicroPythonとFabGLの統合に合わせて"CMakeLists.txt"を変更して、ビルドしたところ、partitionのサイズが足らなくなったので、"partitions.csv"変更して、なんとか動作させることができました。但し、この段階では、MicroPpythonは、USBシリアル経由のみの動作です。


③MicroPythonの入出力をFabGL Ansi-Terminalに接続

MicroPythonにUSB-Serial(RX)とAnsi-Terminal(Keyboard)のどちらからでも入力できるようにします。MicroPythonからは、USB-Serial(TX)とAnsi-Terminal(VGA Display)へ出力させます。これで、マルチターミナルになり、どちらからも透過的に使用できます。TeraTermがなくても、Ansi-Terminalで、Micropythonパソコンになります。

USB-Serial(RX) -+-> MicroPython -+-> USB-Serial(TX)
| |
Ansi-Terminal(Keyboard) +-> Ansi-Terminal(VGA Display)

"mphalport.c"を変更して、上記を実現しました。ただ1点、スクリーンエディタなどで、Ansi-Terminalからのレポート情報を取得する"ESC [ 5 n"と"ESC [ 6 n"のエスケープシーケンスがあることがわかりました。両方にこのシーケンスを送ると、双方からレスポンスがあり、混信してしまいます。そのため、このエスケープシーケンスをUSB-Serial(TX)へ送らないようにブロックする機能を追加しました。


以上、全体をビルドしました。下記は起動画面です。FabGL Ansi-Terminalの設定ダイアログが出ますが、変更が無ければ、"Enter"でMicroPythonが起動します。



MicroPythonが起動した画面です。



かんぱぱさんのasciiartを動かしてみました。PC上のTeraTermとVGAモニタが同じ表示になります。


(再クロール更新:2022/12/22)

ORANGE-ESPerで、MicroPython + FabGLを実現 ~ 準備編

2021/04/29

 ①MicroPythonとFabGL

MicroPythonは、幅広い組込みマイコンで手軽にプログラミング可能な言語処理系です。今回ターゲットにしたESP32は、上海のEspressif Systems社から販売されている、米国のTensilica社の240MHz Xtensa LX6 dual CPUを搭載した普及価格のIoT用マイクロコントローラです。IoT用ですので、Bluetooth、WiFiなど機能も搭載されて、Micropythonから利用できます。


一方、ORANGE-ESPerは、ESP32-WROOM-32モジュールを搭載したESP32-DevKitC開発ボードを利用してVGAモニタ・キーボード・マウス・SDカードを接続でき、ESP32用Arudino上のFabGLライブラリを利用したライブラリ付属の懐かしいアプリケーションが楽しめます。

このFabGLライブラリは、イタリアのFabrizio Di Vittorioさんが開発し、SPI+DMAでVGAビデオ信号を生成させ、IoT用マイクロコントローラをパソコンのように使えるようにした点が光ります。


また、ORANGE-ESPer発売元のピコソフト社からORANGE-OSも公開されていますが、アプリケーションは、限られています。そこで、今回は、ORANGE-ESPerで、Micropythonパソコンにチャレンジすることにしました。PCのTeraTermとORANGE-ESPerのキーボード+VGAモニタ双方からMicroPythonを使えるようにしたいと考えています。


②Windows10上でMicroPythonのビルド

MicroPythonのビルドには、ESP-IDF開発環境が必要になります。まず ESP-IDF Tools Installer for Windows(V2.5)をインストール後、適当なディレクトリにMicroPythonをgithubからcloneします。ESP-IDFは、V4.2がインストールされ、MicroPythonは、V1.14でした。

早速、esp32用のビルドを行うと、Linuxのcatコマンドが無いため、ビルドエラーで先に進めることができませんでした。みなさん、Linuxでビルドしているみたいです。(涙)


Windows10で、VM上にLinuxをインストールしたことはありますが、今回のビルドのためにLinux全体の機能を管理するのも大変ですので、今回は、Linuxをお手軽なWindows Subsystem for LinuxのUbuntuを選択しました。利点は、Windows配下のファイルシステムを共用できるので、慣れたWindows用のツールが利用できる点が優れています。ただし、Windowsからファイルを書き込むと、Linuxのパーミッションがリセットされる点がありますので、注意が必要になります。


下記が、Windows Subsystem for LinuxのUbuntuインストール後の手順です。

sudo apt update && sudo apt upgrade
sudo apt update
sudo apt install \
gcc git wget make libncurses-dev flex bison gperf \
libffi-dev libssl-dev

cd
mkdir esp32
cd esp32
git clone --recursive https://github.com/micropython/micropython
git clone -b v4.2 --recursive https://github.com/espressif/esp-idf.git
git submodule update --init --recursive

cd /usr/bin
sudo ln -s /usr/bin/python3 python

cd
curl "https://bootstrap.pypa.io/get-pip.py" -o "get-pip.py"
python get-pip.py
sudo apt install cmake

cd ~/esp32/esp-idf
./install.sh

. ~/esp32/esp-idf/export.sh

cd ~/esp32/micropython/mpy-cross
make mpy-cross

cd ~/esp32/micropython/port/esp32
idf.py build

無事、MicroPythonのビルドに成功しました。WSLでは、CPUコアの割り当てが少ないようで、Windows上のESP-IDFに比べると遅い点が気になりますが、仕方がありません。


ORANGE-ESPerで、無事にMicroPythonのプロンプトが表示できました。ちなみに、MicroPythonの空きヒープサイズは、99KB程度でした。


③ESP32用のAruduino上で、FabGLのAnsi-Terminalサンプルプログラムをビルド

Arduino V1.8.10にesp32 V1.0.6ボードマネージャと、FabGL V1.0.2ライブラリを追加し、FabGL付属のAnsiTerminalサンプルプログラムをビルド後、無事、VGA画面が立ち上がりました。



次に、ESP-IDF V4.2上で、Ansi-Terminalサンプルプログラムをビルドできる必要があります。esp32 V1.0.6ボードマネージャは、ESP-IDF V3.3.5ベースのようですので、V4.2に適合させることができれば、FabGLライブラリをMicroPythonのソースコードと統合できるはずです。

(再クロール更新:2022/12/22)