ESP32-C3 で esp-def-svc を使って、WiFi通信する

ESP32-C3 は WiFI に対応した RISC-V MCU を積んだモジュールで、Rust を使って開発することもできる。通信部分のサンプルコードが、ESP32-C3 をベースに書かれていて、とりあえず始めるはならば ESP32-C3 が良さそうである。

写真は私の作ったESP32-C3 の開発ボード(キットとしてboothで販売中)。

74th.booth.pm

しかし、この部分のライブラリ、まだ途上で、The Rust on ESP Book からたどれるサンプルコードが、現在の最新のライブラリでは動作しないようになっている。

esp-rs.github.io

サンプルコードのリポジトリ

github.com

一方古いバージョンのライブラリを使い続けられるかというと、このライブラリは ESP-IDF(ESP IoT Development Framework)の Rust のラッパであり、ESP-IDF のバージョンと密接になっており、この辺りのバージョンを適切に併せて上げないとビルドが通らなくなる。

そこで、現状の最新でWiFi で通信できるところまでを確立できたので記しておきます。

利用したバージョン

ESP-IDF v4.4

  • embedded-hal : Peripherals の抽象例や
  • esp-idf-hal : embedded-hal の esp-idf のバインディング
  • esp-dev-svc : WiFi、通信周りを担うサービス
  • embedded-svc : ESP32 に依存しない HTTP Client 等の実装
# Cargo.toml
[dependencies]
anyhow = "1"
array-macro = "2.1.5"
embedded-hal = "1.0.0-alpha.9"
embedded-svc = { version = "0.24.0", features = ["experimental"] }
esp-backtrace = { version = "0.4.0", features = ["esp32c3"] }
esp-idf-hal = "0.40.1"
esp-idf-svc = { version = "0.45.0", features = ["experimental"] }
esp-idf-sys = { version = "0.32.1", features = ["binstart"] }

WiFI に接続する

まず、接続部分について。このサイトの記述が、ほぼそのまま使うことができました。

medium.com

use esp_idf_svc;
use esp_idf_sys;

fn main() -> anyhow::Result<()> {
    println!("start");
    unsafe {
        esp_idf_sys::nvs_flash_init();
    }

    let sysloop = esp_idf_svc::eventloop::EspSystemEventLoop::take().unwrap();
    let nvs = esp_idf_svc::nvs::EspDefaultNvsPartition::take().unwrap();

    esp_idf_sys::link_patches();
    let peripherals = Peripherals::take().unwrap();

    let mut wifi_driver =
        esp_idf_svc::wifi::EspWifi::new(peripherals.modem, sysloop, Some(nvs)).unwrap();
    wifi_driver
        .set_configuration(&embedded_svc::wifi::Configuration::Client(
            embedded_svc::wifi::ClientConfiguration {
                ssid: "ssid-ssid".into(),
                password: "test-test".into(),
                ..Default::default()
            },
        ))
        .unwrap();
    wifi_driver.start().unwrap();
    wifi_driver.connect().unwrap();
    while !wifi_driver.is_connected().unwrap() {
        let config = wifi_driver.get_configuration().unwrap();
        println!("Waiting for station {:?}", config);
    }
    println!("Should be connected now");

    sleep(Duration::new(1, 0));

    println!(
        "IP info: {:?} {:?}",
        wifi_driver.sta_netif().get_ip_info().unwrap(),
        wifi_driver.is_connected(),
    );
}

これで、SSIDとそのパスワードを与えれば、WiFIに接続するところまでできました。

HTTP 通信をする

http 周りの実装は、embedded-svcリファクタリングが最近入り、他で使われている実装例が使えなくなくなっていました。

しかし、esp-idf-svcGitHub には example が用意されており、これからHTTP 通信を実現することができました。

github.com

use embedded_svc::{
    http::{client::Client as HttpClient, Method, Status},
    io::Write,
    utils::io,
};

pub fn main() -> anyhow::Result<()> {
    // 略

    let mut client = HttpClient::wrap(EspHttpConnection::new(&HttpConfiguration {
        crt_bundle_attach: Some(esp_idf_sys::esp_crt_bundle_attach), // Needed for HTTPS support
        ..Default::default()
    })?);
    let url = "http://192.168.1.182:30500/api/ip";
    let request = client.request(Method::Get, &url, &[])?;
    println!("-> GET {}", url);
    let mut response = request.submit()?;
    // Process response
    let status = response.status();
    println!("<- {}", status);
    println!();
}

まとめ

最新のライブラリで様相が変わっているのに辛みを感じましたが、なんとか動くコードを見つけて実装することができました。

これで好きにRust で通信させられそうです。