1.Rust嵌入式入门——esp32C3连接wifi
ESP32-C3是一款集成了Wi-Fi和蓝牙的RISC-V微控制器,适用于物联网设备和智能硬件项目。它支持2.4 GHz Wi-Fi、低功耗蓝牙、高性能32位RISC-V单核处理器以及多种外设,本文将带领大家一窥,rust嵌入式的精彩。
1.开发环境搭建
cargo install espup
espup是用于安装和维护乐鑫 Rust 生态系统的工具。
接下来安装工具链
espup install
如果是linux/macos用户还需要添加环境变量
cat $HOME/export-esp.sh >> ~/.bashrc
详细请参考: The Rust on ESP Book
安装cargo-generate
cargo-generate是一个用于初始化新的Rust项目的命令行工具。它允许你从现成的模板(通常是Git仓库)创建新的Rust项目。
cargo install cargo-generate
安装Espressif 工具链
cargo install cargo-espflash espflash ldproxy
cargo-espflash- 上传固件到微控制器,打开串口监视器,Cargo 集成espflash- 上传固件到微控制器,打开串口监视器ldproxy- Espressif 构建工具链的依赖
2.新建项目
拉取官方模版创建项目
cargo generate esp-rs/esp-idf-template cargo
cargo run
直接运行该项目,会自动将固件写入到esp32c3开发版。
注意:
第一次编译时会下载esp-idf编译链。如果网络不好,或者访问不到github就无法进行编译。
可以通过修改项目目录下.cargo/config.toml新增内容如下:
[build]
target = "riscv32imc-esp-espidf"
[target.riscv32imc-esp-espidf]
linker = "ldproxy"
runner = "espflash flash --monitor" # Select this runner for espflash v3.x.x
rustflags = [
"--cfg",
"espidf_time64",
] # Extending time_t for ESP IDF 5: https://github.com/esp-rs/rust/issues/110
[unstable]
build-std = ["std", "panic_abort"]
[env]
MCU = "esp32c3"
# Note: this variable is not used by the pio builder (`cargo build --features pio`)
ESP_IDF_VERSION = "v5.2.2"
# Workaround for https://github.com/esp-rs/esp-idf-template/issues/174
CRATE_CC_NO_DEFAULTS = "1"
# 添加如下内容
# ESP-IDF 框架工具的安装位置。(这里使用的全局,后面项目就不用再下载依赖了)
ESP_IDF_TOOLS_INSTALL_DIR = { value = "global" }
# ESP-IDF 框架的 Git 仓库地址。(这里使用了代理)
ESP_IDF_REPOSITORY = "https://mirror.ghproxy.com/https://github.com/espressif/esp-idf.git"
同时下载一些tools也需要访问GitHub,可以设置环境来解决
export IDF_GITHUB_ASSETS="dl.espressif.cn/github_assets"
可以使用下面命令,查看构建详情
cargo build -vv
详情请参考
编辑器提示异常
如果vscode提示异常可以查看下面几个issure
3.wifi连接网络
安装依赖
cargo add anyhow toml-cfg
删除main.rs,修改cargo.toml,将[[bin]]内容删除或者注释。
[package]
name = "rust-embedded-study"
version = "0.1.0"
authors = ["yexiyue <yexiyue666@qq.com>"]
edition = "2021"
resolver = "2"
rust-version = "1.77"
# [[bin]]
# name = "rust-embedded-study"
# harness = false # do not use the built in cargo test harness -> resolve rust-analyzer errors
[profile.release]
opt-level = "s"
[profile.dev]
debug = true # Symbols are nice and they don't increase the size on Flash
opt-level = "z"
[features]
default = ["std", "embassy", "esp-idf-svc/native"]
pio = ["esp-idf-svc/pio"]
std = ["alloc", "esp-idf-svc/binstart", "esp-idf-svc/std"]
alloc = ["esp-idf-svc/alloc"]
nightly = ["esp-idf-svc/nightly"]
experimental = ["esp-idf-svc/experimental"]
embassy = [
"esp-idf-svc/embassy-sync",
"esp-idf-svc/critical-section",
"esp-idf-svc/embassy-time-driver",
]
[dependencies]
log = { version = "0.4", default-features = false }
esp-idf-svc = { version = "0.49", default-features = false }
anyhow = "1.0.86"
toml-cfg = "0.2.0"
[build-dependencies]
embuild = "0.32.0"
在src目录下新建lib.rs,wifi.rs和bin/connect_wifi.rs
lib.rs
use anyhow::Result;
use esp_idf_svc::eventloop::EspSystemEventLoop;
use esp_idf_svc::hal::peripherals::Peripherals;
use esp_idf_svc::nvs::EspDefaultNvsPartition;
// 导入与WiFi相关的模块,用于后续的WiFi配置和服务。
pub mod wifi;
/**
* 系统初始化函数。
*
* 该函数负责初始化系统级的服务和硬件外设,为应用程序提供基础的运行环境。
*
* @return Result<(EspSystemEventLoop, Peripherals, EspDefaultNvsPartition)> 初始化完成后的系统事件循环、外设句柄和默认NVS分区。
*/
pub fn init() -> Result<(EspSystemEventLoop, Peripherals, EspDefaultNvsPartition)> {
// 链接SDK中的补丁,以修正某些功能的兼容性问题。
esp_idf_svc::sys::link_patches();
// 初始化日志系统,为后续的调试和错误追踪提供支持。
esp_idf_svc::log::EspLogger::initialize_default();
// 获取系统事件循环实例,用于处理系统级别的事件。
let sysloop = esp_idf_svc::eventloop::EspSystemEventLoop::take()?;
// 获取外设句柄,用于访问和控制硬件资源。
let peripherals = Peripherals::take()?;
// 获取默认的NVS分区,用于存储配置数据和运行时信息。
let nvs = EspDefaultNvsPartition::take()?;
// 返回初始化完成的系统事件循环、外设句柄和默认NVS分区。
Ok((sysloop, peripherals, nvs))
}
wifi.rs
use anyhow::Result;
use esp_idf_svc::eventloop::EspSystemEventLoop;
use esp_idf_svc::hal::peripheral::Peripheral;
use esp_idf_svc::nvs::EspDefaultNvsPartition;
use esp_idf_svc::wifi::{AuthMethod, BlockingWifi, ClientConfiguration, Configuration, EspWifi};
/**
* 连接到指定的Wi-Fi网络。
*
* 此函数初始化Wi-Fi模块,配置连接参数,并尝试连接到指定的Wi-Fi网络。它还负责打印扫描到的Wi-Fi网络信息,
* 以及在成功连接后打印设备的IP地址信息。
*
* @param ssid Wi-Fi网络的SSID。
* @param psk Wi-Fi网络的预共享密钥(PSK)。
* @param modem 用于与Wi-Fi模块通信的外设接口。
* @param sysloop 系统事件循环,用于处理Wi-Fi相关的事件。
* @param nvs NVS(Non-Volatile Storage)分区,用于存储Wi-Fi配置等信息。
* @return 返回一个封装了Wi-Fi模块的Box<EspWifi>实例,表示连接成功;如果连接失败,则返回错误。
*/
pub fn connect_wifi(
ssid: &str,
psk: &str,
modem: impl Peripheral<P = esp_idf_svc::hal::modem::Modem> + 'static,
sysloop: EspSystemEventLoop,
nvs: EspDefaultNvsPartition,
) -> Result<Box<EspWifi<'static>>> {
// 初始化EspWifi实例。
let mut esp_wifi = EspWifi::new(modem, sysloop.clone(), Some(nvs))?;
// 将EspWifi封装为BlockingWifi,以便可以使用阻塞模式的API。
let mut wifi = BlockingWifi::wrap(&mut esp_wifi, sysloop)?;
// 配置Wi-Fi连接参数,包括SSID、认证方法和密码。
let configuration = Configuration::Client(ClientConfiguration {
ssid: ssid.try_into().unwrap(),
auth_method: AuthMethod::WPA2Personal,
password: psk.try_into().unwrap(),
..Default::default()
});
// 应用配置。
wifi.set_configuration(&configuration)?;
// 启动Wi-Fi模块。
log::info!("启动Wi-Fi");
wifi.start()?;
// 扫描可用的Wi-Fi网络。
log::info!("扫描Wi-Fi");
let access_point_infos = wifi.scan()?;
// 打印扫描结果。
log::info!("扫描到的Wi-Fi数量: {}", access_point_infos.len());
access_point_infos
.into_iter()
.for_each(|info| println!("{:#?}", info));
// 尝试连接到配置的Wi-Fi网络。
log::info!("连接Wi-Fi");
wifi.connect()?;
// 确认Wi-Fi连接已建立。
log::info!("Wi-Fi已连接");
// 等待网络接口启动。
wifi.wait_netif_up()?;
// 获取连接后的IP地址信息。
let ip_info = wifi.wifi().sta_netif().get_ip_info()?;
// 打印IP地址信息。
log::info!("IP信息: {:?}", ip_info);
// 返回封装了Wi-Fi模块的实例。
Ok(Box::new(esp_wifi))
}
connect_wifi.rs
#[toml_cfg::toml_config]
#[derive(Debug)]
/// `Config`结构体用于存储配置信息,主要是WiFi的SSID和PSK。
///
/// # Attributes
///
/// `wifi_ssid` - WiFi的SSID,默认值为"Wokwi-GUEST"。
/// `wifi_psk` - WiFi的预共享密钥(PSK),默认为空字符串。
pub struct Config {
#[default("Wokwi-GUEST")]
wifi_ssid: &'static str,
#[default("")]
wifi_psk: &'static str,
}
fn main() -> anyhow::Result<()> {
// 初始化系统循环、外设和NVS闪存。
let (sysloop, peripherals, nvs) = rust_embedded_study::init()?;
// 使用配置文件中的WiFi SSID和PSK连接到WiFi。
// 这里使用了`rust_embedded_study`库提供的`connect_wifi`函数。
let _wifi = rust_embedded_study::wifi::connect_wifi(
CONFIG.wifi_ssid,
&CONFIG.wifi_psk,
peripherals.modem,
sysloop,
nvs,
)?;
// 返回成功结果结束程序。
Ok(())
}
最后在根目录下新建cfg.toml用于保持wifi相关信息
[rust-embedded-study] # 该名称必须与package name一致
wifi_ssid = "iQOO Z1"
wifi_psk = "yexiyue666"
运行
cargo run
由于.cargo/config.toml配置了runner,所以只需要一行命令就能编译并写入到微控制器。
最后
🎉恭喜您完成了环境配置,这是学习Rust嵌入式开发的第一步,也是最具挑战性的一步。接下来,我将根据官方教程继续更新“Rust嵌入式”系列,并同步代码到仓库yexiyue/rust-embedded-study 。
如果您觉得不错,请点个关注或者收藏,感谢您的支持。