引言
为什么选择 ESP-IDF ?
使用的系统环境
- 操作系统: macOS 15.7.4
- 开发版: 正点原子 ESP32 AI BOX1
我们以后将频繁参考的正点原子官方资料 http://www.openedv.com/docs/boards/esp32/ATK-DNESP32S3BVXX.html
配置开发环境
安装 Rust
官方更推荐使用 Rustup 安装 Rust, 而非通过第三方包管理工具 brew.
ESP-IDF-Template 也更推荐使用 Rustup 进行安装 [^1]
https://rust-lang.org/tools/install/
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh随后运行 source "$HOME/.cargo/env" 或重启你的 Shell, 校验安装:
rustup update给 Rust 包管理工具 Cargo 换源
国内特殊的网络环境需要换源才能实现高速的依赖下载, 笔者这里推荐 北京外国语大学开源软件镜像站, 详见:
安装 ESP-IDF 及其工具链
根据 ESP-IDF-Template 中 Prerequisites 部分
In particular, do NOT clone, install and activate the ESP-IDF which is mentioned further down this page, as it is not necessary at all (though supported, but not for a beginner setup)
作为初学者的我们应让 ESP-IDF-SYS 来管理 ESP-IDF, 而非自己手动安装
安装系统级依赖
brew install libgcrypt glib pixman sdl2 libslirp dfu-util cmake安装 Python
笔者电脑里面有一套 Python 开发环境, 所以使用 pyenv 来管理 Python 环境来避免环境紊乱
brew install pyenv若重启 Shell 后无法检测到 pyenv 命令, 请参考 PyENV 官方文档
pyenv install -l # 查看最新版本pyenv install 3.14 # 安装目前的最新版本pyenv global 3.14pyenv rehashpython --version # 校验安装python --version 版本不对? 看看 pyenv versions 命令结果输出的版本由何设置, 或尝试 rm ~/.python-version.
安装 ESP Rust 相关工具
根据 ESP-IDF-Template 中 Prerequisites 部分
cargo install cargo-generatecargo install ldproxycargo install espup # ESP32-S3 属于 Xtensa,要用 espup 来装 Espressif Rust 工具链cargo install espflashcargo install cargo-espflash根据 ESP-RS, 仅安装最小依赖
espup install --targets esp32s3 --std. "$HOME/export-esp.sh"推荐将环境加载持久化进 Shell, 将 ~/.zshrc 改成你的 Shell 用户配置文件
echo '. "$HOME/export-esp.sh"' >> ~/.zshrc安装 VSCode 插件
在安装 VSCode 插件之前, 笔者更推荐安装以下的 Rust 的额外 lint 工具 和 官方格式化工具
rustup component add clippy rustfmt运行以下命令或手动安装以下两个 VSCode 插件
code --install-extension rust-lang.rust-analyzercode --install-extension Slint.slint推荐添加以下两个 VSCode 配置, 它做了两件事
- 将检查命令从
cargo check改为cargo clippy - 在保存时自动格式化代码
{ "rust-analyzer.check.command": "clippy", "[rust]": { "editor.formatOnSave": true }}搭建最小 Bootstrap
创建 ESP-IDF 项目
用官方模板生成项目, 在项目文件夹中运行如下命令, 根据 ESP-IDF-TEMPLATE
cargo generate esp-rs/esp-idf-template cargo我选择的选项如下
➜ CLionProjects cargo generate esp-rs/esp-idf-template cargo⚠️ Favorite `esp-rs/esp-idf-template` not found in config, using it as a git repository: https://github.com/esp-rs/esp-idf-template.git🤷 Project Name: esp32-s3-slint-rs🔧 Destination: /Users/xiaoyouchr/CLionProjects/esp32-s3-slint-rs ...🔧 project-name: esp32-s3-slint-rs ...🔧 Generating template ...✔ 🤷 Which MCU to target? · esp32s3✔ 🤷 Configure advanced template options? · true✔ 🤷 ESP-IDF version (master = UNSTABLE) · v5.5.3 # 不建议选择 master 分支, 不稳定✔ 🤷 Use latest GIT versions of the esp-idf-* crates instead of released crates.io versions? · false # 不建议从 Git 仓库拉取最新版本, 不稳定✔ 🤷 Installation location of managed ESP-IDF · workspace✔ 🤷 Configure project to use Dev Containers (VS Code and GitHub Codespaces)? · false✔ 🤷 Configure project to support Wokwi simulation with Wokwi VS Code extension? · false # Wokwi is World's most advanced ESP32 simulator✔ 🤷 Add CI files for GitHub Action? · true # GitHub 自动构建工作流安装 Slint 依赖
根据 https://docs.slint.dev/latest/docs/rust/slint/docs/mcu/
cargo add slint@1.16 --no-default-features --features "compat-1-2 unsafe-single-threaded libm renderer-software"cargo add --build slint-build@1.16最小 Slint UI
根据 https://docs.slint.dev/latest/docs/rust/slint/docs/mcu/
ui/app.slint
export component App inherits Window { width: 320px; height: 240px;
Rectangle { background: #1f2937;
Text { x: 16px; y: 16px; text: "ESP32-S3 + Slint + Rust"; color: white; font-size: 18px; }
Text { x: 16px; y: 48px; text: "compile-only skeleton"; color: #cbd5e1; font-size: 14px; } }}然后编辑 build.rs, 将 ui/app.slint 生成成 Rust 代码, 供 slint::include_modules!(); 引入.
build.rs
fn main() { embuild::espidf::sysenv::output();
let config = slint_build::CompilerConfiguration::new() .with_style("fluent".into()) .embed_resources(slint_build::EmbedResourcesKind::EmbedForSoftwareRenderer); slint_build::compile_with_config("ui/app.slint", config).unwrap();}随后运行 cargo build, 编译一下 Slint 文件, 防止在编辑 ui/main.rs 时报错.
最小主函数
根据 https://docs.slint.dev/latest/docs/rust/slint/docs/mcu/#the-platform-trait
ui/main.rs
slint::include_modules!();
use std::boxed::Box;use std::rc::Rc;use std::time::{Duration, Instant};
use slint::platform::software_renderer::MinimalSoftwareWindow;use slint::platform::{Platform, PlatformError};
struct EspPlatform { window: Rc<MinimalSoftwareWindow>, start: Instant,}
impl Platform for EspPlatform { fn create_window_adapter( &self, ) -> Result<Rc<dyn slint::platform::WindowAdapter>, PlatformError> { // Since on MCUs, there can be only one window, just return a clone of self.window. // We'll also use the same window in the event loop. Ok(self.window.clone()) }
fn duration_since_start(&self) -> Duration { self.start.elapsed() }}
fn main() { // It is necessary to call this function once. Otherwise, some patches to the runtime // implemented by esp-idf-sys might not link properly. See https://github.com/esp-rs/esp-idf-template/issues/71 esp_idf_svc::sys::link_patches();
// Bind the log crate to the ESP Logging facilities esp_idf_svc::log::EspLogger::initialize_default();
log::info!("Booting...");
let window = MinimalSoftwareWindow::new(Default::default()); window.set_size(slint::PhysicalSize::new(320, 240));
slint::platform::set_platform(Box::new(EspPlatform { window: window.clone(), start: Instant::now(), })) .expect("failed to set Slint platform");
let _ui = App::new().expect("failed to create Slint UI");
log::info!("Slint initialized");
loop { slint::platform::update_timers_and_animations(); window.draw_if_needed(|_renderer| { // TODO: render to LCD }); std::thread::sleep(Duration::from_millis(16)); }}第一次烧录
插入 ESP32-S3-BOX 后运行 cargo run, 查看控制台日志, 发现
I (480) esp32_s3_slint_rs: Booting...I (490) esp32_s3_slint_rs: Slint initialized烧录成功~
正式点亮屏幕
//
驱动 Xl9555
驱动屏幕
加入触屏功能
增强 Xl9555 驱动
驱动触摸
在 Slint 中处理触摸信号
优化性能
超频 I80 总线
边渲染边上屏
异步处理触摸信号
驱动 Wi-Fi
增强 Xl9555 以驱动 I²C 总线
扫描 Wi-Fi
连接 Wi-Fi
其他说明
提高波特率以加快烧录速度
提高栈大小以及如何避免栈溢出
Windows 下长路径问题
部分信息可能已经过时