4
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Rust で ESP32 を動かす

4
Posted at

はじめに

これまで、マイコンは Raspberry Pi Pico 2 W を使ってきましたが、別のマイコンも試してみたくなり、ESP32 を触ってみることにしました。

また、普段は MicroPython で書いていますが、Rust に結構興味があるので、今回は Rust で動かしてみます。

ボード上の LED で L チカするところまでやってみます。

ESP32 を開発している Espressif が Rust での book を公開しているので、それに沿ってやっていきます。

環境

build target について

ESP32 シリーズは、チップによって CPU のアーキテクチャが違います。

  • Xtensa: The ESP32 and ESP32-S series are based on the Xtensa architecture.
  • RISC-V: The ESP32-C and ESP32-H series are based on the RISC-V architecture.

今回使う ESP32-WROOM-32E は、データシートに

Built around ESP32 series of SoCs, Xtensa® dual-core 32-bit LX6 microprocessor

とあるので、中身は無印の ESP32(Xtensa)だとわかります。

RISC-V のチップであれば、

$ rustup target add riscv32imc-unknown-none-elf # ESP32-C2, ESP32-C3
$ rustup target add riscv32imac-unknown-none-elf # ESP32-C6, ESP32-H2

のように、通常の Rust のツールチェーンに target を追加するだけでビルドできます。
どちらも Tier 2 の target になっています。

Xtensa の場合は Rust(というよりもバックエンドの LLVM)で、まだ十分にサポートされていないので、Espressif が fork した Rust のコンパイラを使う必要があります。

これをインストールするためのツールとして、espup が用意されています。

espup によって、

がインストールされます。

やってみる

ツールチェーンのインストール

まずは、上で説明した通り Xtensa に対応するために espup をインストールします。

$ cargo install espup --locked
$ espup install --targets esp32

[info]: Installing the Espressif Rust ecosystem
[info]: Checking Rust installation
[info]: Installing Xtensa Rust 1.97.0.0 toolchain
[info]: Installing GCC (xtensa-esp-elf)
[info]: Installing Xtensa LLVM

--targets を指定しない場合は、すべてのチップ向けのツールチェーンがインストールされるので、今回は esp32 だけにしています。

インストールが終わると、esp という名前のツールチェーンが追加されます。

$ rustup toolchain list
stable-aarch64-apple-darwin (active, default)
esp

また、espup install を実行すると、ホームディレクトリに export-esp.sh が作られます。

export-esp.sh
export LIBCLANG_PATH="/Users/tsuzuki-takaaki/.rustup/toolchains/esp/xtensa-esp32-elf-clang/esp-21.1.3_20260408/esp-clang/lib"
export PATH="/Users/tsuzuki-takaaki/.rustup/toolchains/esp/xtensa-esp-elf/esp-16.1.0_20260609/xtensa-esp-elf/bin:$PATH"

rustc や cargo は、rustup が rust-toolchain.toml を見て esp ツールチェーンのものを呼び出してくれますが、リンクに使う GCC に関しては、rustup が切り替えてくれないので、このスクリプトを読んで、PATH を通す必要があります。

このファイルは、ターミナルを開くたびに読み込む必要があります。

ツールのインストール

Rust での ESP32 の開発が簡単になるように、ツールが用意されているので、それをインストールします。

$ cargo install esp-generate --locked
$ cargo install espflash --locked

プロジェクトの作成

esp-generate を使ってプロジェクトを作ります。

$ esp-generate --headless -o esp32 -o esp32-wroom-32e blinky

esp-generate は、何も指定しないと TUI でオプションを選ぶ形になりますが、今回は --headless で直接指定しています。
-o esp32 でチップを、-o esp32-wroom-32e でモジュールを指定しています。

オプションの一覧は、

$ esp-generate list-options

でみることができます。

blinky % tree
.
├── build.rs
├── Cargo.toml
├── rust-toolchain.toml
└── src
    ├── bin
    │   └── main.rs
    └── lib.rs

3 directories, 5 files

rust-toolchain.toml で、esp ツールチェーンを使うように指定されています。

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

そのため、このディレクトリの中では esp ツールチェーンが使われます。

$ rustup toolchain list
stable-aarch64-apple-darwin (default)
esp (active)

.cargo/config.toml では、build target と、cargo run したときに実行するコマンドが指定されています。

.cargo/config.toml
# Keep compilation settings in this file and user/runtime settings in `esp-config.toml`.
include = ["esp-config.toml"]

[target.xtensa-esp32-none-elf]
runner = "espflash flash --monitor --chip esp32"

[build]
rustflags = [
  "-C", "link-arg=-nostartfiles",
]

target = "xtensa-esp32-none-elf"

[unstable]
build-std = ["core"]

runner に espflash flash --monitor が指定されているので、cargo run でビルドから書き込みまででき、書き込んだあとはそのままシリアルモニタになります。

L チカする

esp-generate によって、以下のファイルが生成されます。

src/bin/main.rs
#![no_std]
#![no_main]
#![deny(
    clippy::mem_forget,
    reason = "mem::forget is generally not safe to do with esp_hal types, especially those \
    holding buffers for the duration of a data transfer."
)]
#![deny(clippy::large_stack_frames)]

use esp_hal::clock::CpuClock;
use esp_hal::main;
use esp_hal::time::{Duration, Instant};

#[panic_handler]
fn panic(_: &core::panic::PanicInfo) -> ! {
    loop {}
}

// This creates a default app-descriptor required by the esp-idf bootloader.
// For more information see: <https://docs.espressif.com/projects/esp-idf/en/stable/esp32/api-reference/system/app_image_format.html#application-description>
esp_bootloader_esp_idf::esp_app_desc!();

#[allow(
    clippy::large_stack_frames,
    reason = "it's not unusual to allocate larger buffers etc. in main"
)]
#[main]
fn main() -> ! {
    // generator version: 2.0.0
    // generator parameters: -o esp32 -o esp32-wroom-32e

    let config = esp_hal::Config::default().with_cpu_clock(CpuClock::max());
    let peripherals = esp_hal::init(config);

    // The following pins are used to bootstrap the chip. They are available
    // for use, but check the datasheet of the module for more information on them.
    // - GPIO0
    // - GPIO2
    // - GPIO5
    // - GPIO12
    // - GPIO15
    // These GPIO pins are in use by some feature of the module and should not be used.
    let _gpio6 = peripherals.GPIO6;
    let _gpio7 = peripherals.GPIO7;
    let _gpio8 = peripherals.GPIO8;
    let _gpio9 = peripherals.GPIO9;
    let _gpio10 = peripherals.GPIO10;
    let _gpio11 = peripherals.GPIO11;
    let _gpio16 = peripherals.GPIO16;
    let _gpio20 = peripherals.GPIO20;

    loop {
        let delay_start = Instant::now();
        while delay_start.elapsed() < Duration::from_millis(500) {}
    }

    // for inspiration have a look at the examples at https://github.com/esp-rs/esp-hal/tree/esp-hal-v1.2.2/examples
}

ピンの配置図を見ると、LED は、

  • LED TX
  • LED RX
  • LED IO2

の 3 つが用意されていることがわかります。

今回は、LED IO2 を光らせます。

esp-hal でだいぶ抽象化されているので、Output とピンを指定するだけで LED を光らせることができます。

src/bin/main.rs
#![no_std]
#![no_main]

use esp_hal::clock::CpuClock;
use esp_hal::delay::Delay;
use esp_hal::gpio::{Level, Output, OutputConfig};
use esp_hal::main;

#[panic_handler]
fn panic(_: &core::panic::PanicInfo) -> ! {
    loop {}
}

// This creates a default app-descriptor required by the esp-idf bootloader.
// For more information see: <https://docs.espressif.com/projects/esp-idf/en/stable/esp32/api-reference/system/app_image_format.html#application-description>
esp_bootloader_esp_idf::esp_app_desc!();

#[main]
fn main() -> ! {
    let config = esp_hal::Config::default().with_cpu_clock(CpuClock::max());
    let peripherals = esp_hal::init(config);

    let mut led_io2 = Output::new(peripherals.GPIO2, Level::Low, OutputConfig::default());
    let delay = Delay::new();

    loop {
        led_io2.toggle();
        delay.delay_millis(500);
    }
}
$ cargo run
4
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?