ESP32(Xtensa)向けRust開発環境構築手順

## 1. 前提

これは、Rust + ESP32(Xtensaアーキテクチャ) + VSCodeを使用する前提の開発環境構築手順になります。

https://qiita.com/yuyakato/items/c11de0a43d14dea6c9dd#6-esp32%E3%81%B8%E3%81%AE%E8%BB%A2%E9%80%81

こちらでもESP32+Rustの紹介がされていますが、環境が大きく変わりましたので昔のやり方となります。
私は試したことはありませんが、もし興味のある方はリンク先をご覧ください。

本記事は、構築中やビルド中に発生したエラーと解決方法も紹介しているので、
もし、エラーで行き詰っている方の参考になれば幸いです。

**2022/12/2 追記**
この記事を書いた当初はWindowsのみでの開発でしたが、非常に不便(静的解析やビルドが遅い)だったため、現在は次第にWSLやMacへ開発環境を移しました。
この記事の内容の表現がWindows寄りになっていますが、そこは適宜、自分の環境に置き換えてください。

あとでWSLでの開発環境構築の方法も記事にしようと思っています。(催促されたら早く記事にします)

### TODO

- パーティションの変更方法
- GPIOやADC、I2Cなどの使用方法
- 同じI2Cバスを複数のオブジェクトから使用する方法
- MQTT通信で送受信までの手順

### ビルドとフラッシュができることを確認している環境(2022/12/2 追記)

- Windows
- Windows + WSL2 + Ubuntu ※Win10だと、ESPにフラッシュするには独自にビルドしたカーネルを使用する必要あり。Win11はいらないっぽい？未確認
- Ubuntu(ビルドのみ。フラッシュは未確認。多分できる。この中で一番楽かもしれない。)
- Macbook Air M2 2022(Ventura 13.0.1)

### この記事で使用するESP

ESP32-C3以降だと、Risc-Vアーキテクチャになるので、[2.6節](#2.6.-rust-buildのインストールスクリプトを実行)あたりでインストールするビルドツールが異なります。
恐らく、当記事の2.5節 ~ 2.8節までが[esp-rs/rust-build #riscv-installation](https://github.com/esp-rs/rust-build#riscv-installation)で済むと思います。(多分。自己責任でお願いします。)

私が使用しているのは、無印のESP32ですが、ESP32-Sシリーズも同様にできると思います。(Xtensaアーキなため)

### 私の環境の詳細(Windows)

2022年9月あたりの環境

使用しているPC: i7-10510U, メモリ24GB, Windows10

```powershell
> rustup show
Default host: x86_64-pc-windows-msvc
rustup home:  C:\Users\<User Name>\.rustup

installed toolchains
--------------------

stable-x86_64-pc-windows-msvc
nightly-x86_64-pc-windows-msvc
esp (default)
esp-1.60.0.1

active toolchain
----------------

esp (default)
rustc 1.60.0-nightly (69346c12c 2022-04-19)


> rustup --version
rustup 1.24.3 (ce5817a94 2021-05-31)

> rustc --version
rustc 1.60.0-nightly (69346c12c 2022-04-19)

> cargo --version
cargo 1.63.0-nightly (a4c1cd0eb 2022-05-18)

> .\Install-RustToolchain.ps1
Processing configuration:
-InstalltationMode    = install
-LlvmVersion          = esp-14.0.0-20220415
-ToolchainVersion     = 1.60.0.1
```

ちょくちょく、`rustup update`をしてバイナリをコピーしていたため(2.8節で説明)、nightlyのみversionが進んでいます。

## 2. Rust + ESP32向けツールチェインのインストール

### 2.1. Rustを公式サイトからDL、インストール

- rust-buildのインストールスクリプトでもRustはインストールされるが、パスが自動では通らないため、公式のインストーラーを最初に使用するほうが私は楽。

### 2.2. Pythonを公式サイトからDL、インストール

- pyenv使用者への注意事項
  - pyenvのみだと、[pyenvの仕組みとRustからのPython実行の方法](https://github.com/esp-rs/esp-idf-sys/issues/95#issuecomment-1129829621)から正常に実行されない。
  - `cargo build`時に`Error: Could not install esp-idf`とエラーがでる。
  - 他の仮想環境の場合はわからない。
- Python3.10以上

### 2.3. 一度、使用しているターミナルを全て終了する

- RustとPythonのパスをターミナルに反映させる。

### 2.4. 管理者権限のPowerShellを起動する

- 管理者権限でないと、次からのインストールに失敗するため注意。
- ※コマンドプロンプトでも可能かどうか、吐き出す変数の形式が違うかなどの挙動の違いなどは確認していない。

### 2.5. rust-buildの前提条件をインストール

~~- [esp-rs/rust-build #windows-x64](https://github.com/esp-rs/rust-build#windows-x64)に書いてある通り、Chocolateyをインストールした後、Chocolateyで依存関係の諸々をインストールする。~~
~~- <ユーザディレクトリ>/.rustup/toolchains下に`esp`というディレクトリが生成される。~~

**2022/12/2 追記**

>Warning
>Install scripts from this repository will now be feature freeze. New features will be added to espup, a Rust version of the install scripts.

rust-buildリポジトリから、rust-buildをインストール・アップデート・アンインストールをコマンドでやってくれる「[espup](https://github.com/esp-rs/espup)」というバイナリクレートが新しく開発されたようです。

これによって、簡単にインストールできる様になりました。

https://github.com/esp-rs/espup#requirements

Linux以外はほぼ準備することないです。

### 2.6. rust-buildのインストールスクリプトを実行

~~- [esp-rs/rust-build #Installation commands for PowerShell](https://github.com/esp-rs/rust-build#installation-commands-for-powershell)のコマンドを実行して、rust-buildをインストール。~~
~~- インストールが完了すると、最後に環境変数を設定するコマンドが出力されるため、メモしておく。~~
~~- もし、メモする前にターミナルを消してしまった場合、`.\Install-RustToolchain.ps1 -InstallationMode export`で再度出力できる。~~
~~- もし、インストールに失敗しスクリプトを再度実行した際に、既にインストールされている旨のメッセージが表示された場合、`<ユーザディレクトリ>/.rustup/toolchains/esp`のディレクトリを削除することで再度インストールをおこなうことができる。~~

**2022/12/2 追記**

https://github.com/esp-rs/espup#installation

https://github.com/esp-rs/espup#quickstart

espupのバイナリクレートをインストールした後、`espup install`コマンドが使用できるようになるので、
`--esp-idf-version <ESP_IDF_VERSION>`をつけてインストールしてください。
指定するESP_IDF_VERSIONは、現時点(2022/12/2)で[esp-idf-template](https://github.com/esp-rs/esp-idf-template)などで主に使用されているv4.4で良いと思っています。
[esp-idf-sys](https://github.com/esp-rs/esp-idf-sys)と関係するため、これの対応次第。
v5とかビルドできるのかわかっていないので、もし試したことがある方は教えてください。

```
cargo install espup

espup install --esp-idf-version <ESP_IDF_VERSION>
```

また、環境変数はespup installを実行したディレクトリに「export-esp.ps1(Linuxとかだと.sh)」というファイルで生成されます。
次の項で使用します。

WindowsのWSL環境(後で記事化する予定です。)とMac環境でなら動作できることを確認しています。


### 2.6.1. Windowsのespupでのインストールの注意

が、Windows**上**での動作確認はできていないため、実際に動くかどうかはわからないです。。。(動いたら教えてください><)

https://github.com/esp-rs/espup#known-issues-or-limitations

なんかエラーがでるっぽいです。
もし無理な場合、取り消し線で消している前の方法でインストールしてください。

### 2.7. インストールスクリプトの最後に書かれた環境変数をユーザ環境変数に追加

- スクリプトの最後で出力された値、もしくは、生成されたexport-esp.ps1(.sh)を参考に、
    - Windows
        - PATH、LIBCLANG_PATH、PIP_USERの3つの環境変数をWindowsのユーザ環境変数として設定する。
    - Ubuntu(WSL)
        - T.B.D.(あとで確認します。。。)
    - Mac
        - LIBCLANG_PATH, PATHを~/.zshrcに追加する。
- PowerShellのプロファイルに追記しターミナル実行時のみの環境変数とすると、VSCodeの拡張機能がターミナルの環境変数を拾わず`cargo check`や`cargo build`が正常に動作しないため注意。
- 環境変数を設定していない時に出るエラー

  ```powershell
  thread 'main' panicked at 'Unable to find libclang: "the libclang shared library at C:\msys64\mingw64\bin\libclang.dll could not be opened: LoadLibraryExW failed
  ```

### 2.8. <ユーザディレクトリ>/.rustup/toolchains/esp/binに<ユーザディレクトリ>/.rustup/toolchains/nightly-x86_64-pc-windows-msvc/bin内の実行ファイルをコピーする (Windowsのみ)

- そのままだとrust-buildのインストールによってespのtoolchainにcargo.exeが入らないため、cargoコマンドを複数実行するサブコマンドなどでエラーが発生する。

  ```powershell
  >cargo build
  error: unable to unlink old fallback exe: アクセスが拒否されました。 (os error 5)
  ```

  - <https://github.com/rust-lang/rustup/issues/2889>

- `cargo`, `cargo-clippy`, `cargo-fmt`, `clippy-driver`, `rustfmt`をコピーする。
  - これはとりあえず、esp/binに入っていないバイナリをコピーしている。
  - cargoだけでもエラーは発生しなくなるが、cargo-fmtがないとVSCode拡張機能のrust-analyzerでのフォーマットが動作しないなど、エラーがでるため私は諸々コピーしている。
- 私は、`rustup update`などでnightlyのツールチェインが更新されたら、同じようにコピーしてespのツールチェインに配置している。 ~~いつビルドできなくなるかわからんが...~~

※ Windows以外のWSLのUbuntu、Macは必要ないです。

### 2.9. ESP32向けのツールチェインの適用

ビルドをおこなう前に、ビルドなどで使用するツールチェインをespに設定する必要があります。

設定方法は3つあります。

### 2.10. ビルド時などで使用するツールチェインを選択する

`cargo +esp build`

のように、cargoのあとに`+esp`を付けると、espのツールチェインを使用してビルドできます。

ただ、これだとVSCodeの拡張機能で`cargo 〇〇`を実行された時にエラーがでて正常に動作しないため、
次の2つの方法をオススメします。

#### 2.10.1. Rustのデフォルトのツールチェインをespに変更する

`rustup default esp`

普段RustでESP32向けアプリ以外のアプリを開発しない人はこちらを選んでも良いと思います。
ただ、windows向けのアプリも開発するし、ESP32向けのアプリも開発するという方は、
これだとプロジェクトごとに毎回切り替えなければならなく面倒なため、次の設定方法をオススメします。

#### 2.10.2. プロジェクトディレクトリのルートに設定ファイルを置く

```toml:rust-toolchain.toml
[toolchain]
channel = "esp"
```

この`rust-toolchain.toml`をプロジェクトルートに置くことで、
ESP32向けのプロジェクトディレクトリ下で`cargo build`などを実行する時は自動でespのツールチェインを使用するようになります。
これで普段はwindows向けツールチェインに設定しておくことができます。

### 2.11. ビルド&フラッシュ&モニター

※espのツールチェインに設定されている前提

- `cargo check`
でコンパイルのみ

- `cargo build`
でビルドのみ

- `cargo espflash --target=xtensa-esp32-espidf COM〇`
でビルドとフラッシュ。

- `cargo espflash --target=xtensa-esp32-espidf COM〇 --monitor`
`--monitor`を付けると、フラッシュ後にESP32とのシリアルモニターが起動する。

## 3. Rust + ESP32向けツール(バイナリクレート)のインストール

バイナリクレートとは、Cargoを通してインストールできるRust産のツール、実行ファイルのこと。

Cargoを使用してESP32向けプログラムを開発する際に必要、もしくはあったら便利なバイナリクレートを紹介する。
`cargo install --list`で取得したため、バージョンもついている。記録用。

これらは、
`cargo install cargo-espflash`のように、`cargo install <クレート名>`でインストールできる。

### 3.1. 必須

- cargo-espflash v1.5.1:
  - ビルドツールをインストールする際に勝手にインストールされる。
  - これでESP32にフラッシュする。
- ldproxy v0.3.0:
  - ビルドツールをインストールする際に勝手にインストールされる。
  - よく理解はしていないが、Cライブラリをリンクする際に必要なものだと認識。

### 3.2. 推奨

- cargo-generate v0.13.1:
  - プロジェクトのテンプレートを生成するもの。
  - [ESP32用のテンプレート](https://github.com/esp-rs/esp-idf-template)があるため、これを利用して生成するのが便利。
- sccache v0.3.0:
  - `cargo check`や`cargo build`でコンパイルしたクレートをキャッシュする。
  - 変更のないクレートは再コンパイルしないため、ビルド時間が短くなる。
  - `cargo install sccache`でインストールができるが、環境変数`RUSTC_WRAPPER`にsccacheのパスを追加する必要がある。
- ~~cargo-edit v0.9.1:~~
  - ~~cargo add で依存クレートをCargo.tomlへ追加できて便利。cuiでのnpmやpipのように使用できる。~~
  - (2022/07/01 追記)
  - https://github.com/rust-lang/rust/releases/tag/1.62.0
  - Rust1.62.0になり、デフォルトで`cargo add`コマンドが組み込まれ、ほぼ同様のことができるようになった。
  - このバイナリクレートをインストールする必要性がなくなったかも...。

### 3.3. 便利

- cargo-tree v0.29.0:
  - 各クレートごとに依存しているクレートをツリーで表示する。
- cargo-update v8.1.4:
  - `cargo install`を使用してインストールしたバイナリクレートをアップデートできる。
  - `cargo install-update --all`でインストールされている全てのバイナリクレートをアップデートする。
- cargo-espmonitor v0.8.1:
  - シリアルポートでESP32と通信できる。(送信は未確認)
  - `cargo espmonitor COM〇`

### 3.4. オススメバイナリクレートの参考

https://qiita.com/sinkuu/items/3ea25a942d80fce74a90#cargo-outdated-cargo-outdated

## 4. VSCodeの拡張機能のオススメ

- [rust-analyzer](https://marketplace.visualstudio.com/items?itemName=rust-lang.rust-analyzer)
  - Rust公式が推奨している構文解析ツール
  - `Rust`という拡張機能があるが、公式は`Rust`の拡張機能のサポートをやめ、こちらの拡張機能をサポートすることになった。
- [Better TOML](https://marketplace.visualstudio.com/items?itemName=bungcip.better-toml)
  - RustのプロジェクトファイルであるCargo.tomlが見やすくなる。
  - Cargo.tomlは良く編集するため、あったほうが良い。
- [crates](https://marketplace.visualstudio.com/items?itemName=serayuzgur.crates)
  - Cargo.tomlを開いた時に、`dependencies`に記述している依存クレートのバージョンをチェックし、存在するバージョンの一覧を確認することができる。
  - クレートのgithubを見にいかなくても存在するバージョンを確認できたり、各バージョンのdocを開くことができたりと、とても便利。

## 5. 注意事項

- プロジェクトを作成した後の最初のビルドはとても遅い
  - esp-idf-sysのビルドでしばらく進まない。
  - ビルドが完了するまで6m13s掛かった。(i7-10510U, 8コア, 24GB)
  - 多コアほどビルド早いかも(私比、多分)
- プロジェクト単位で数GBするため、ストレージを持っていかれる
  - 例え、`cargo generate`で生成したHello Worldでも、
    - embuildクレートでesp-idfのDL、ビルドが走る。(ビルド後1.26GB)
    - ビルド後のプロジェクトのtargetディレクトリは1.01GB
  - 合計2.28GB持っていかれる。
- プロジェクトのパスはできるだけ短くする
  - `fatal: No names found, cannot describe anything.`
    `has 〇〇〇 characters.  The maximum full path to an object file is 250 characters (see CMAKE_OBJECT_PATH_MAX).  Object file`
    などのエラーがでたら、パスの長さを疑うこと。
  - ドライブレターやスラッシュを含んで49文字でエラー、38文字は通ることを確認してはいるが、環境によるかどうかは不明。

- プロジェクトディレクトリの名前と`Cargo.toml`の`[package]`に書かれている`name`は同じ名前にする。
    - `ninja: build stopped: subcommand failed`
    - `ninja: error: remove(esp-idf\freertos\CMakeFiles\__idf_freertos.dir\tasks.c.obj.d): Permission denied`
    - `thread 'main' panicked at`
    - などと書かれたエラーが発生する。
- プロジェクトディレクトリを別の場所へ移動したら(パスが変わったら)`cargo clean`を行う。
    - ビルドしたことのあるプロジェクトディレクトリを別ディレクトリへ移動してビルドをしたところ、`ifd-esp-sys`のビルド時に`指定されたパスが見つかりません。 (os error 3)`と表示された。

## 6. 個人的なつぶやき

RustもESP32も初めて使用しましたが、Rustも覚えること多いし、Rust+ESP32の環境構築も覚えること多いしネットに情報は少ないしで実際に動かすまでとても時間が掛かりました。。。

でも、実際にモダンに書けるRustでマイコン用ファームが書けるとなると、ワクワクがすごいですね！
一度、動いてしまえばあとは簡単なので、C++に慣れていない私にとってはとても快適な環境です。Rust楽しい...！

ただ、辛かったのは、環境構築の時に何度やってもesp-idf-sysクレートのビルドが失敗するので、
原因を探るために設定を変えてビルドして、8分後にビルド失敗して設定値変えてまた8分待って・・・というのを繰り返していました(笑)
しかもビルド中はCPU8コア全て100%に張り付いて何もできないという...(泣)


これから、Rustが盛り上がっていくのも楽しみですし、RustでESP32を動かす界隈も盛り上がっていって欲しいなと思います。

この記事が誰かのRust+ESP32開発への手助けとなれば幸いです。