ESP32-C3 は WiFI に対応した RISC-V MCU を積んだモジュールで、Rust を使って開発することもできる。通信部分のサンプルコードが、ESP32-C3 をベースに書かれていて、とりあえず始めるはならば ESP32-C3 が良さそうである。
写真は私の作ったESP32-C3 の開発ボード(キットとしてboothで販売中)。
しかし、この部分のライブラリ、まだ途上で、The Rust on ESP Book からたどれるサンプルコードが、現在の最新のライブラリでは動作しないようになっている。
サンプルコードのリポジトリ
一方古いバージョンのライブラリを使い続けられるかというと、このライブラリは 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 に接続する
まず、接続部分について。このサイトの記述が、ほぼそのまま使うことができました。
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-svc の GitHub には example が用意されており、これからHTTP 通信を実現することができました。
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 で通信させられそうです。