ESP32でstdなRust開発入門 その1 環境構築

概要

そろそろRustを勉強してみようと思って、いろいろなボードを探してみたのでうがやっぱり使い慣れたESP32で環境を立ち上げてみたいと思います。

環境

ESP32ボード

なんでもよかったのですが、技適付きのキットでマニュアルがしっかりしているFreenoveさんのボードを利用してみたいと思います。付属品を使ってみたいだけなので他のESP32ボードでも問題なく動くと思います。

開発環境

Windows上のWSL2にUbuntu22.04で構築してみました。ただしWSL環境ですとシリアル転送がかんたんに利用できないので、転送だけはWindows側で自動実行する仕組みも作りました。

UbuntuやMacなどであればそのまま転送できるので、もっと簡単に環境構築ができるはずです。

環境構築

Windows側にPython3を導入

こちらは書きません。他のサイトにいろいろ情報があるので調べて構築してみてください。

Windows側にesptoolを導入

pip install esptool

ESP32への転送にはWindows側のesptoolを利用するのでセットアップしておきます。

WSLにUbuntu22.04を導入

こちらは書きません。他のサイトにいろいろ情報があるので調べて構築してみてください。以下すべてWSLでの作業となります。

WSLのUbuntuを最新版に更新

sudo apt update
sudo apt upgrade

とりあえず更新をします。

必須パッケージの追加

sudo apt install git python3 python3-pip gcc build-essential curl pkg-config libudev-dev libtinfo5

上記サイトからの設定になります。

Rustupの導入

curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh

Rustの環境を整えてくれるツールを入れます。通常のRust環境はこちらで導入されますが、マイコンなどの組み込みで利用する環境は別となります。

再ログイン

source "$HOME/.cargo/env"

上記コマンドで環境変数を読み込み直してもいいのですが、安全のためコンソールを閉じてからログインし直します。

ldproxy導入

cargo install ldproxy

Rustのリンカーのためのプロキシーツールを入れます。

espup導入

cargo install espup

ESP32用のRustupツールを入れます。

ESP32用Rust環境構築

espup install

ESP32用のRust環境をセットアップします。

ldconfig実行

sudo ldconfig

動的ライブラリの依存関係を更新します。ldproxyあたりで実行しろと書いてあった気がします。とくに実行してデメリットがないので実行しておきます。

cargo-generate導入

cargo install cargo-generate

テンプレートからプロジェクトを作成するツールを導入します。

サンプルプロジェクト作成

環境変数設定

. ~/export-esp.sh

こちらはターミナルで接続したたびに実行する必要があります。Rustでしか使わない環境であれば自動実行させることも可能だと思います。これを実行し忘れるとビルドでエラーが出るので注意してください。

プロジェクト用のフォルダ作成

cd
mkdir prog
cd prog

Homeディレクトリにprogフォルダを作成して、この中でプログラムをいれていきたいと思います。場所はどこでも構いませんが、WSLだとデフォルトがWidnows側のPathになっていることが多いので、WSL側のPathにしたほうが早く動くはずです。

サンプルプロジェクト作成

cargo generate --vcs none --git https://github.com/esp-rs/esp-idf-template cargo

上記のコマンドでプロジェクトを作成できます。一般的にはvcsオプションは付けないと思います。デフォルトの状態だとgit initまでされてしまうのでgitを使わない場合にはつけるか、あとでフォルダごと消すか、そのまま残すかでもよいと思います。

実行すると対話型で以下の項目を設定します。ただし、出てくる順番は実行ごとにランダムで変わるので注意してください。

Project Name

作成するプロジェクト名を設定します。

ESP-IDF native build version

利用するESP-IDFのバージョンを選択します。通常は一番上にある最新安定版を利用します。ちなみにv4.4と指定すると、v4.4系の最新であるv4.4.3が現在利用されていました。

STD support

Rustには標準ライブラリを利用するstdと、利用しないno-stdがあります。ESP32は標準ライブラリが利用でき、利用してもそれほどサイズが大きくならないのでstdを利用したほうがよいと思います。他のホードですとstdが利用できず、no-std環境のみサポートしているものが多いです。

ちなみにno-stdを使いたい場合には「https://github.com/esp-rs/esp-template」というテンプレートが別にあり、こちらを使ったほうがよいと思います。

Configure project to use Dev Containers

ビルドにコンテナを利用するかの設定です。espupにてビルド環境をWSL上に構築済みのためfalseを選択します。

MCU

利用しているボードを選択します。今回は無印のesp32を選択します。RISC-Vであるesp32c3がよいのかなと最初思ったのですが、どのMCUでも同じように使えるようです。

プロジェクトフォルダに移動

cd testproj

先程作成したプロジェクトのフォルダに移動します。

転送用スクリプトを導入

curl -O https://gist.githubusercontent.com/tanakamasayuki/502de4d5f51597d38264475bae42bd52/raw/wsltool.sh
chmod +x wsltool.sh

私が作成した転送用のスクリプトをダウンロードして、実行権限を追加します。中身はシンプルなのであとで確認してみるとよいと思います。

実行設定変更

vi .cargo/config.toml

プロジェクトの中にある設定ファイルを編集して、実行する際に先程の転送用スクリプトを実行するように変更します。

[target.xtensa-esp32-espidf]
linker = "ldproxy"
#runner = "espflash --monitor"
runner = "./wsltool.sh esp32 COM4 921600"

編集する場所は上記のesp32のespflashで転送しているコマンドになります。

説明
./wsltool.sh転送で利用するスクリプト。プロジェクト最上位で実行することを想定
esp32対象のMCU
COM4Windowsで認識しているシリアルポート番号。autoで自動判定
921600転送で利用する速度。1500000にすると早いかも

上記のような説明になっています。シリアルポート番号は環境により異なるので自分の環境で認識しているものに変更してください。転送速度もボードにより異なります。大抵のボードは1500000でも転送できると思います。(逆に921600だと転送できないM5StickCなどもあります)

ビルド

cargo build

まずはビルドをしてみて、エラーがないかを確認します。ESP-IDF(esp-idf-sys)は巨大なので初回ビルドには5分以上かかる場合があります。とくにアンチウイルスやファイアウォールなどが入っていると通信に時間がかかって、なかなかビルドが進みません。

ビルドに失敗した場合には「. ~/export-esp.sh」を実行していない可能性が高いです。

    Finished dev [optimized + debuginfo] target(s) in 1m 56s

上記のようにFinishedが表示されていれば成功です。

実行してみる

cargo run

config.tomlの設定が正しければ転送して、シリアルモニタが起動するはずです。

上記のような表示がでて「Hello, world!」が最後の行にあれば成功です。

IDF Monitorを終了する

シリアルコンソールの表示にはIDF Monitorというツールを利用しています。ちょっと操作が特殊なツールになります。これ以外のツールでもよいのですがWindows側で動かしているため、標準的なシリアル表示ツールがありません。wsltool.shを編集してPuTTYとかに変更しても問題ありません。

ちなみに終了するコマンドはCtrl+]になります。あとCdtl+T, Rでリセットがかかったりします。

上手く動かないとき、Tips

ビルドに失敗する

. ~/export-esp.sh

上記を実行し忘れていませんか? 本当によく実行し忘れます。profileなどのログイン時に自動実行する設定にしてもよいのかもしれません。

なんか古い

sudo apt update
sudo apt upgrade
rustup self update
rustup update
cargo install -f espup
espup update

上記のコマンドで関連しているものが更新されます。特にespupは公式Rustにパッチを当てて環境を構築してくれるのですが、公式側の設定変更とかでビルド失敗する場合があります。その場合にはespup自体を更新しないと「espup update」が成功しないので注意してください。

sudo apt install libssl-dev
cargo install cargo-update

個人的には上記のcargo-updateを導入して、cargo全体を一括更新できるようにしたほうがおすすめです。

sudo apt update
sudo apt upgrade
rustup self update
rustup update
cargo install-update --all
espup update

上記で一括更新されます。「cargo install -f espup」だと更新がない場合でも毎回ビルドしなおしてしまうので非常に遅いです。cargo-updateは更新があったときだけビルドをしてくれるので軽いです。ただし、依存として「libssl-dev」が必要ですのでaptで入れておきます。

wsltoolがきもい

#!/bin/bash

CHIP_NAME=$1
COM_NAME=$2
COM_SPEED=$3
ELF_FILE=$4

PROJECT_NAME=`basename $ELF_FILE`
BUILD_PATH=`dirname $ELF_FILE`
if [ `basename $BUILD_PATH` = "examples" ]; then
    BUILD_PATH=`dirname $BUILD_PATH`
fi

ESP_IDF=$(ls .embuild/espressif/esp-idf/ | tail -1)
ESP_IDF_SYS=`ls $BUILD_PATH/build/ | grep esp-idf-sys | tail -1`

if [ $CHIP_NAME = "esp32" ] || [ $CHIP_NAME = "esp32s2" ]; then
    BOOT_ADR=0x1000
else
    BOOT_ADR=0x0
fi

echo "WSL Rust TOOL"

echo "PROJECT_NAME  : $PROJECT_NAME"
echo "ELF_FILE      : $ELF_FILE"
echo "BUILD_PATH    : $BUILD_PATH"
echo "ESP-IDF       : $ESP_IDF"
echo "ESP_IDF_SYS   : $ESP_IDF_SYS"
echo "CHIP_NAME     : $CHIP_NAME"
echo "BOOT_ADR      : $BOOT_ADR"
echo "COM_NAME      : $COM_NAME"
echo "COM_SPEED     : $COM_SPEED"

cp -f $ELF_FILE .embuild/elf
cp -f $BUILD_PATH/build/$ESP_IDF_SYS/out/build/bootloader/bootloader.bin .embuild/
cp -f $BUILD_PATH/build/$ESP_IDF_SYS/out/build/partition_table/partition-table.bin .embuild/

powershell.exe -Command python -m esptool --chip $CHIP_NAME elf2image .embuild\\elf
powershell.exe -Command python -m esptool --chip $CHIP_NAME --port $COM_NAME --baud $COM_SPEED --before default_reset --after hard_reset write_flash -z $BOOT_ADR .embuild/bootloader.bin 0x8000 .embuild/partition-table.bin 0x10000 .embuild/elf.bin 
powershell.exe -Command python .embuild\\espressif\\esp-idf\\$ESP_IDF\\tools\\idf_monitor.py --disable-address-decoding --port $COM_NAME .embuild/elf

更新している可能性がありますが、上記のようなスクリプトになっています。

mt@pc:~/prog/esp32-test$ cargo run
    Finished dev [optimized + debuginfo] target(s) in 0.11s
     Running `/home/mt/prog/esp32-test/./wsltool.sh esp32 COM7 921600 target/xtensa-esp32-espidf/debug/esp32-test`
WSL Rust TOOL
PROJECT_NAME  : esp32-test
ELF_FILE      : target/xtensa-esp32-espidf/debug/esp32-test
BUILD_PATH    : target/xtensa-esp32-espidf/debug
ESP-IDF       : release-v4.4
ESP_IDF_SYS   : esp-idf-sys-1057fae825b7c998
CHIP_NAME     : esp32
BOOT_ADR      : 0x1000
COM_NAME      : COM7
COM_SPEED     : 921600

esp32-testプロジェクトで実行した結果です。cargo runでconfig.tomlに設定したコマンドの最後にELFファイルのpathも渡して実行されています。

そこからELFからelf2imageを使って転送に使うbinファイルを生成。あと同時に転送する必要があるbootloader.binとpartition-table.binをビルドPathから探してきて、Windows側のesptoolを使ってボードに転送します。

その後にidf_monitor.pyを使ってシリアルコンソールを開くスクリプトとなります。ちなみにWindows側からWSLは「\\wsl$」などのpathでアクセスすることができますが、コマンドプロンプトだとサポートしていないのでpowershell経由で呼び出しています。

エディタをどうすればいいの?

Windows側にVSCodeを入れて使うのが一般的です。以下の拡張機能を入れるのがおすすめです。

Japanese Language Pack for Visual Studio Code

日本語化。必須ではありません。

rust-analyzer

標準的なRust用拡張機能です。

WSL

WSLのプロジェクトフォルダのTOPで「code .」と実行することでVSCodeが起動します。この機能は非常に便利です。

crates

こちらは似たようなツールがいろいろありますが、Rustのライブラリにあたるクレートの管理に便利なツールです。

上記のように依存しているバージョン設定で、現在の最新バージョンを教えてくれます。この辺のツールはローカルではなくWSL側にインストールする必要があります。

上記のような表記が拡張機能にあった場合には押してインストールしてあげてください。

VSCodeから実行したい

がんばればできるはずです。。。ただシリアルコンソールが見にくいと思いますので別窓でcargo runを実行するのが便利じゃないかなと思っています。。。

Rustがわからない

大丈夫です。私もわかりません。こんな手順を書くのに時間をかけているのでぜんぜん学習が進んでいません。

上記が公式の「the book」と呼ばれるチュートリアルになります。

非公式ですが、公式からもリンクされている日本語訳があります。まずはこの辺のドキュメントを読むことをおすすめします。

組み込みRustがわからない

基礎から学ぶ 組込みRust
シーアンドアール研究所
¥4,202(2024/12/03 20:52時点)

上記の本が非常にわかりやすいです。とはいえ、「Wio Terminal」向けの本なので半分ぐらいはESP32には関係ないと思います。ですが、マイコン向けの開発ではこの本でないと書いていないようなことがたくさんあります。

ディスクがもりもり減っていく

ESP32のRustではESP-IDFをダンロードしてビルドしています。その関係で1プロジェクトあたり1G以上のディスク容量が必要となります。最初にテンプレートからいろいろな設定でプロジェクトを作成して、ビルドをしていくとどんどんディスクが減っていきます。

おしてWSLは仮想ディスクを利用しているので、一度拡張した領域はファイルを消してもWindowsが利用できる容量は増えません。仮想ディスク領域を圧縮することも可能ですが、かなり面倒な作業になります。

まとめ

最小手順にするために、WSLの環境を消しては作ったりしているのでかなり時間がかかっています。転送部分もエラーチェックが甘いので転送失敗していてもコンソールを開いてしまうなど、作りが悪いところがありますが、まあ実用的な範囲までは作り込めたかなと思っています。

こんなもの書いているよりRustの勉強をしろってのはもっともなのですが、この記事で救われる人がいるはず。。。このへんは趣味で勉強している利点ですね。実際にRustを使う予定もないですし、、、

コメント