m5stack UnitV2用のOpenCVプログラムをRustでクロスコンパイルする方法

作りたいもの
- m5stack UnitV2で動くプログラム
- OpenCVを使ってカメラ画像取得(できればストリーミング)
開発環境
- 言語:Rust
- 開発するPC:Ubuntu
この記事
開発に必要なもののメモからサンプルプログラムを動かすところまでを記事として残す
Rustの開発環境は構築済みとする

m5stack UnitV2
Linuxが動いていて、マイクやカメラを搭載してる小型マイコンだと思えば良さそう。
チュートリアル から
- Built-in recognition function use tutorial
- Jupyter Notebook Development Tutorial/Example
- SSH connection & WIFI configuration
あたりを試しておく
チュートリアルのBuit-inプログラムについて
UnitV2は起動時にホームディレクトリにあるWebアプリのサーバプログラムを自動で実行するようになっている。
Ethernet over USBによりUSBケーブル越しにIP通信ができるので、PCからブラウザでWebアプリを起動したり、Jupyterが使えるようになっている。
Webアプリではあらかじめ保存されているBuiltinプログラムを試せる
Builtinプログラムのコードはここ https://github.com/m5stack/UnitV2Framework
m5stack特有のライブラリなどがあるわけではなく、ただ単にOpenCVをC++で使ったり、arecordでロク0音したりしているので、プログラム自体は普通のLinuxとして書けば良さそう。

ファイルとしては
ホームディレクトリの/home/m5stack
以下にpayload
というディレクトリがあり、その下にWebアプリ用のファイルやプログラムがまとめられている
server.py
が起動プログラム、bin
にBuilt-inプログラムがまとめられている
server.py
自体は/etc/init.d/S85runpayload
で起動している

m5stackは組み込み用Linuxが入っているので、/etc/init.d関連がUbuntuとかの普通のLinuxとは設定方法が微妙に異なるっぽい。
https://buildroot.org/downloads/manual/manual.html の6.3に説明がある。

あまり詳しい説明はないが、組み込み用のため、ランレベルで切り替えたり、サービスのon/off切り替えのような機能(それを実現するためのスクリプト)など使わないっぽい。
単純に/etc/init.d以下にある、スクリプトファイルを順に実行するだけなので、S85runpayload
のファイル名を変える(S以外の文字で始める)か、削除してしまうかがよさげ

OpenCV
UnitV2にはOpenCVのライブラリが入っているので、今回はOpenCV自体のクロスコンパイルは不要か?
まずは、UnitV2にインストール済みのOpenCVを使うプログラムをクロスコンパイルしてみる

Rustのクロスコンパイル環境
ここを参考に構築してみる
rustup target list
でクロスコンパイルのターゲット一覧を確認
armv7関連は下記のものがある。
armv7-linux-androideabi
armv7-unknown-linux-gnueabi
armv7-unknown-linux-gnueabihf
armv7-unknown-linux-musleabi
armv7-unknown-linux-musleabihf
armv7a-none-eabi
armv7r-none-eabi
armv7r-none-eabihf
Builtinプログラムのコンパイル方法とかから類推でarmv7-unknown-linux-gnueabihfをインストール
rustup target add armv7-unknown-linux-gnueabihf
sudo apt install g++-arm-linux-gnueabihf # リンカのインストール

このあと、リンカの設定がいる
${HOME}/.cargo/configというファイルを作り、下記内容を書いておく
[target.armv7-unknown-linux-gnueabihf]
linker = "arm-linux-gnueabihf-gcc"

まずはOpenCVを使わないhelloworldをクロスコンパイルしてみる
下記コマンドでビルド
cargo build --target=armv7-unknown-linux-gnueabihf
target/armv7-unknown-linux-gnueabihf/debug/m5unitv2_cv_camera
という実行バイナリができる

プログラムのコピー
UnitV2ではsshができるように設定されているが、scpは難あり。
/dev/nullのパーミッションエラーでscpができない。/dev/nullのパーミッションを変えている記事もあったが、ここではrootでscpすることにした。
scp後、所有者変更と実行権限付与
chown m5stack:10 m5unitv2_cv_camera
chmod 755 m5unitv2_cv_camera

GLIBCエラー
実行したところGLIBCがないというエラーが出た
unitv2% ./m5unitv2_cv_camera
./m5unitv2_cv_camera: /lib/libc.so.6: version `GLIBC_2.33' not found (required by ./m5unitv2_cv_camera)
./m5unitv2_cv_camera: /lib/libc.so.6: version `GLIBC_2.32' not found (required by ./m5unitv2_cv_camera)
./m5unitv2_cv_camera: /lib/libc.so.6: version `GLIBC_2.34' not found (required by ./m5unitv2_cv_camera)

開発PCのglibcのバージョンがターゲットのバージョンより新しいと、このエラーが出るらしい
ターゲットをgnuではなく、muslにするとライブラリ非依存のバイナリを作れるらしいので、試してみる。
muslは対応するg++のリンカはなく、rust-lldを使う
rustup target add armv7-unknown-linux-musleabihf
.cargo/configに以下を追記
[target.armv7-unknown-linux-musleabihf]
linker = "rust-lld"

ターゲットを変えてビルド
cargo build --target=armv7-unknown-linux-musleabihf
ビルドしたら、再度rootのscpでUnitV2にコピー。この時、ファイルを上書きしたのに、なぜか所有者や権限はそのままだった。。。

無事helloworldがUnitV2で起動できた。
ファイルサイズは4.6M。glibc使う方は3.9Mなので、多少大きくなっているが許容範囲。

本題と関係ないがUnitV2はWiFi APにできるのだが、初期状態ではうまくいかない。ほかの記事でも設定できず断念している記事があったが、下記の方法でAP化に成功したので、メモしておく
WiFiのAPとしての設定ファイルが/etc/hostapd.confにある。このファイルを編集することで設定を変更できるのだが、ここにssidの指定がない。
ssid=M5UV2_XXX
と、ssidを追記すると、PCなどからSSIDが見えるようになり、ログインできる。
パスワードはUnitV2のドキュメントに書いてあるが、このファイルを編集して変えることもできると思う。

OpenCVのクレートは https://github.com/twistedfall/opencv-rust を使う
cargo add opencv
でCargo.tomlにクレートを追記できる
公式のReadmeに従って、必要なパッケージをインストール。(Ubuntu用コマンド)
apt install libopencv-dev clang libclang-dev

このままだとclangがうまく設定?できておらず、ビルドに失敗する。
コンパイルの途中で、clangがヘッダファイルに対して not found というエラーを出す。
調べたところ、clang++の設定がうまくいっていないらしい。clang++単体で試してもコンパイルに失敗した。

ライブラリ周りの設定ができていないようだが、開発環境の設定を頑張ってもクロスコンパイルでは意味がないので、クロスコンパイルを試してみる。
clang++ --target=arm-linux-gnueabihf a.cpp
でテスト用のhelloworldはコンパイルできたが、UnitV2に持っていくとrustのときと同様glibcのバージョンエラーが出た。
clangはmuslに対応しておらず、musl用のコンパイルは大変そう。結局glibcのバージョン問題を解決する必要がある。

m5stack unitv2の公式Built-inのgithubにgnu toolchainの情報があるので、ダウンロードページから直接ダウンロードしてみる
執筆時点のバージョン情報は
gcc-arm-10.2-2020.11-x86_64-arm-none-linux-gnueabihf.tar.xz