Windows11 WSL2上のUbuntuでesp-idfを使う(2025/01/04更新)

Windows11のWSL2を使う機会が増えたので、自分への覚書として関係するリンクと内容を簡単にまとめます。

## 開発環境
* Windows11: Pro 24H2
* WSL: v2.3.26.0
* Ubuntu: 22.04.5 LTS

## Linuxでのツールチェーンのセットアップ
https://docs.espressif.com/projects/esp-idf/en/stable/esp32/get-started/linux-macos-setup.html

まずは、上記の手順に従い、ESP-IDFのツールチェーンをセットアップしていきます。

### 1. 必要なパッケージを導入する
ESP-IDFを使用してコンパイルするために、次のコマンドでパッケージを導入します。
```:Ubuntu
$ sudo apt-get install git wget flex bison gperf python3 python3-pip python3-venv cmake ninja-build ccache libffi-dev libssl-dev dfu-util libusb-1.0-0
```

### 2. ESP-IDFを取得する
ESP-IDFをインストールするディレクトリに移動し、次の手順でリポジトリをクローンします。
以下の例の設定
* インストールディレクトリ: ~/esp/
* バージョン: v5.4 (2025/01/04時点の最新版)
ESP-IDFのバージョンについては[こちら](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/)を参照してください。
```:Ububtu
$ mkdir -p ~/esp
$ cd ~/esp
$ git clone -b v5.4 --recursive https://github.com/espressif/esp-idf.git
```
ESP-IDFは **~/esp/esp-idf** にダウンロードされます。

### 3. 必要なツールを設定する
ESP32をサポートするプロジェクトでは、ESP-IDFの他に、コンパイラ、デバッガー、Python パッケージなど、ESP-IDFで使用されるツールもインストールする必要があります。
以下のコマンドを参考にターゲットとするチップをリストしてスクリプトを実行します。
必要なツールはLinux 上のユーザーホームディレクトリ( **~/.espressif** )にインストールされます。
```:Ubuntu
$ cd ~/esp/esp-idf
$ ./install.sh esp32,esp32s3
```
サポートされているすべてのターゲット用のツールをインストールするには、次のコマンドを実行してください。
```:Ubuntu
$ cd ~/esp/esp-idf
$ ./install.sh all
```

### 4. 環境変数を設定する
ツールをコマンドラインから使用するためには、いくつかの環境変数を設定する必要があります。ESP-IDFには、それを実行するスクリプトが用意されていますので、次のコマンドを実行します。
```:Ubuntu
$ . ~/esp/esp-idf/export.sh
```
※先頭のドットとパスの間のスペースに注意してください。
以下のメッセージが表示されたら、設定完了です。
```:Ubuntu
$ . ~/esp/esp-idf/export.sh
Checking "python3" ...
Python 3.10.12
"python3" has been detected
Activating ESP-IDF 5.4
* Checking python version ... 3.10.12
* Checking python dependencies ... OK
* Deactivating the current ESP-IDF environment (if any) ... OK
* Establishing a new ESP-IDF environment ... OK
* Identifying shell ... bash
* Detecting outdated tools in system ... OK - no outdated tools found
* Shell completion ... Autocompletion code generated

Done! You can now compile ESP-IDF projects.
Go to the project directory and run:

  idf.py build
```

## サンプルプロジェクトで動作確認をする
ESP-IDFのプロジェクトを開始する準備が完了しましたので、実際にサンプルプロジェクトをビルドしてターゲット上で実行してみます。

### 1. サンプルプロジェクトをコピーする
ESP-IDFのexamplesディレクトリからhello_worldプロジェクトをコピーして実行してみます。
get-started/hello_world を~/espにコピーします。
```:Ubuntu
$ cd ~/esp
$ cp -r ~/esp/esp-idf/examples/get-started/hello_world ./
```

### 2. WSL2でターゲットボードを接続する
次に、ターゲットボードをコンピューターに接続し、どのシリアルポートでボードが認識されているかを確認します。
USBデバイスの接続のサポートはWSL2ではネイティブに使用できないので、オープンソースの [usbipd-win](https://github.com/dorssel/usbipd-win/releases)プロジェクトをインストールする必要があります。
以下の記事を参考に、USBに接続されたデバイスをWSL2上のUbuntuで認識させます。

##### WSLでUSB認識をしよう
https://qiita.com/KMNMKT/items/ae0e76a3e420b3ca16e6
##### USB デバイスを接続する
https://learn.microsoft.com/ja-jp/windows/wsl/connect-usb

まずPowerShellを開きWindows11にusbipdをインストールします。
```:PowerShell
> winget install --interactive --exact dorssel.usbipd-win
```

次にWSL2上のUbuntuでusbipdの設定をします。Linuxのカーネルバージョンを確認します。
```:Ubuntu
$ uname -r
5.15.167.4-microsoft-standard-WSL2
```

筆者の環境では5.15ですので、linux-tools-5.15のインストール可能なバージョンを確認します。
```:Ubuntu
$ sudo apt update
$ sudo apt upgrade
$ sudo apt-cache search linux-tools-5.15.*-generic
～省略～
linux-tools-5.15.0-125-generic - Linux kernel version specific tools for version 5.15.0-125
linux-tools-5.15.0-126-generic - Linux kernel version specific tools for version 5.15.0-126
linux-tools-5.15.0-127-generic - Linux kernel version specific tools for version 5.15.0-127
linux-tools-5.15.0-128-generic - Linux kernel version specific tools for version 5.15.0-128
linux-tools-5.15.0-130-generic - Linux kernel version specific tools for version 5.15.0-130
～省略～
```

今回は一番新しい **linux-tools-5.15.0-130-generic** をインストールします。
```:Ubuntu
$ sudo apt install linux-tools-5.15.0-130-generic hwdata
$ sudo update-alternatives --install /usr/local/bin/usbip usbip /usr/lib/linux-tools/5.15.0-130-generic/usbip 20
```

次にPowerShellからUSBデバイスの接続をします。PowerShellを管理者モードで開き、現在接続されているUSBデバイスを確認します。
```:PowerShell
> usbipd list
Connected:
BUSID  VID:PID    DEVICE                                                        STATE
1-5    303a:1001  USB シリアル デバイス (COM10), USB JTAG/serial debug unit     Not shared
```

今回はWindows11のCOM10で認識されていますので、コマンド **usbipd bind** を使ってデバイスを共有し、WSLに接続できるようにします。
以下のコマンドを実行した後、もう一度 **usbipd list** でデバイスが共有されていることを確認します。
```:PowerShell
> usbipd bind --busid 1-5
> usbipd  list
Connected:
BUSID  VID:PID    DEVICE                                                        STATE
1-5    303a:1001  USB シリアル デバイス (COM10), USB JTAG/serial debug unit     Shared
```

このデバイスのBUSIDである1-5をWSLに認識させます。
```:PowerShell
> usbipd attach --wsl --busid 1-5
usbipd: info: Using WSL distribution 'Ubuntu-22.04' to attach; the device will be available in all WSL 2 distributions.
usbipd: info: Using IP address 172.19.48.1 to reach the host.
```

Ubuntuで接続された USB デバイスの一覧を表示し、認識されたポート名を確認します。
```:Ubuntu
$ lsusb
Bus 002 Device 001: ID 1d6b:0003 Linux Foundation 3.0 root hub
Bus 001 Device 002: ID 303a:1001 Espressif USB JTAG/serial debug unit
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
$ dmesg | grep tty
[ 7830.505535] cdc_acm 1-1:1.0: ttyACM0: USB ACM device
```

先ほど接続したデバイスが表示され、Ubuntu上で **ttyACM0** として認識されているのがわかりました。
これにアクセス権限を与えておきます。
```:Ubuntu
$ sudo chmod 777 /dev/ttyACM0
```
なお、WSLでデバイスを使い終わったら、USBデバイスの接続を物理的に解除するか、PowerShellから次のコマンドを実行して切断します。
```:PowerShell
usbipd detach --busid <busid>
```

### 3. プロジェクトを構成する
hello_worldディレクトリに移動し **ESP32S3** をターゲットとして設定し、プロジェクト構成ユーティリティ(menuconfig)を実行します。
```:Ubuntu
$ cd ~/esp/hello_world
$ idf.py set-target esp32s3
Adding "set-target"'s dependency "fullclean" to list of commands with default set of options.
Executing action: fullclean
Build directory '/home/***/esp/hello_world/build' not found. Nothing to clean.
Executing action: set-target
Set Target to: esp32s3, new sdkconfig will be created.
～中略～
-- Configuring done
-- Generating done
-- Build files have been written to: /home/***/esp/hello_world/build
$ idf.py menuconfig
```

この手順が正しく実行された場合、次のメニューが表示されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/120903/b3a66c1a-a374-790f-bef0-a232ff4c4787.png)

このメニューを使用して、Wi-Fi ネットワーク名とパスワード、プロセッサ速度、フラッシュの容量などのプロジェクト固有の変数を設定します。今回のプロジェクト(hello_world)はデフォルト構成で実行しますので、デフォルトのまま[Q]で終了します。

### 4. プロジェクトをビルドする
次のコマンドを実行してプロジェクトをビルドします。このコマンドは、アプリケーションとすべてのESP-IDFコンポーネントをコンパイルし、ブートローダ、パーティション テーブル、およびアプリケーションバイナリを生成します。
```:Ubuntu
$ idf.py build
Executing action: all (aliases: build)
Running ninja in directory /home/***/esp/hello_world/build
Executing "ninja all"...
[0/1] Re-running CMake...
-- git rev-parse returned 'fatal: not a git repository (or any of the parent directories): .git'
-- Could not use 'git describe' to determine PROJECT_VER.
-- Building ESP-IDF components for target esp32s3

～中略～

[1033/1034] Generating binary image from built executable
esptool.py v4.8.1
Creating esp32s3 image...
Merged 2 ELF sections
Successfully created esp32s3 image.
Generated /home/tomorrow56/esp/hello_world/build/hello_world.bin
[1034/1034] cd /home/tomorrow56/esp/hello_world/build/esp-idf/espt...n-table.bin /home/tomorrow56/esp/hello_world/build/hello_world.bin
hello_world.bin binary size 0x327f0 bytes. Smallest app partition is 0x100000 bytes. 0xcd810 bytes (80%) free.

Project build complete. To flash, run:
 idf.py flash
or
 idf.py -p PORT flash
or
 python -m esptool --chip esp32s3 -b 460800 --before default_reset --after hard_reset write_flash --flash_mode dio --flash_size 2MB --flash_freq 80m 0x0 build/bootloader/bootloader.bin 0x8000 build/partition_table/partition-table.bin 0x10000 build/hello_world.bin
or from the "/home/tomorrow56/esp/hello_world/build" directory
 python -m esptool --chip esp32s3 -b 460800 --before default_reset --after hard_reset write_flash "@flash_args"
```
エラーがなければ、ファームウェアのバイナリ(.bin)ファイルが生成されてビルドが終了します。

### 5. デバイスにフラッシュする
ESP32S3用にビルドしたバイナリを次のコマンドを実行してデバイスのポート(/dev/ttyACM0)に書き込みます。
```:Ubuntu
$ idf.py -p /dev/ttyACM0 flash
Executing action: flash
Running ninja in directory /home/tomorrow56/esp/hello_world/build
Executing "ninja flash"...

～中略～

Compressed 3072 bytes to 103...
Wrote 3072 bytes (103 compressed) at 0x00008000 in 0.0 seconds (effective 559.3 kbit/s)...
Hash of data verified.

Leaving...
Hard resetting via RTS pin...
Done
```
終了までに問題がなければ、ボードが再起動し **hello_world** アプリケーションが起動します。

### 6. シリアル出力をモニターする
**hello_world** が実際に実行されているかどうかを確認するために、IDFモニターアプリケーションを起動します。
```:Ubuntu
$ idf.py -p /dev/ttyACM0 monitor
Executing action: monitor
Running idf_monitor in directory /home/tomorrow56/esp/hello_world
Executing "/home/tomorrow56/.espressif/python_env/idf5.4_py3.10_env/bin/python /home/tomorrow56/esp/esp-idf/tools/idf_monitor.py -p /dev/ttyACM0 -b 115200 --toolchain-prefix xtensa-esp32s3-elf- --target esp32s3 --revision 0 /home/tomorrow56/esp/hello_world/build/hello_world.elf -m '/home/tomorrow56/.espressif/python_env/idf5.4_py3.10_env/bin/python' '/home/tomorrow56/esp/esp-idf/tools/idf.py' '-p' '/dev/ttyACM0'"...
--- esp-idf-monitor 1.5.0 on /dev/ttyACM0 115200

～中略～

Hello world!
This is esp32s3 chip with 2 CPU core(s), WiFi/BLE, silicon revision v0.2, 2MB external flash
Minimum free heap size: 390340 bytes
Restarting in 10 seconds...
Restarting in 9 seconds...
```
ログに"Hello world!"と表示されていれば問題なくプログラムが動いています。
IDFモニターを終了するには、ショートカット(Ctrl+])を使用します。

## その他の操作
### フラッシュ消去
フラッシュを消去することも可能です。フラッシュ メモリ全体を消去するには、次のコマンドを実行します。
```:Ubuntu
idf.py -p <PORT> erase-flash
```

OTA データが存在する場合は、消去には次のコマンドを実行します。
```:Ubuntu
idf.py -p <PORT> erase-otadata
```

## 上記以外での参考情報(リンク)
### Build System
idf.pyのオプションの説明があります。

https://docs.espressif.com/projects/esp-idf/en/v4.3.4/esp32/api-guides/build-system.html

### m5stack のソフトウェアを esp-idf 環境で開発して動かす
5GFXライブラリを導入しサンプルを動かす方法が説明されています。

https://zenn.dev/mai/articles/72519e289973c6

### エクスプローラでWSL2（ubuntu）のディレクトリを開く方法
https://snowsystem.net/other/windows/wsl2-ubuntu-explorer/

### VSCodeのESP-IDF拡張機能「Espressif IDF」を使ってみる
https://developer.mamezou-tech.com/blogs/2023/02/19/esp-idf-vsc-extension/

## 最終的にやりたいこと
### M5Stack NESをM5 Core2に移植する
https://qiita.com/HidetoKimura/items/86ad39f6aa5427d7c1e5

### a Nintendo Entertainment System emulator for M5Stack
https://github.com/m5stack/M5Stack-nesemu