前阵子朋友送了一块开发板,型号叫ESP32-2432S028R,网上管它叫"CheapYellowDisplay",简称CYD。板子上自带一块2.8寸触摸屏,我看网上都是用Arduino或MicroPython玩的,想着能不能用Rust搞点有意思的。于是就写了这个电子宠物
前阵子朋友送了一块开发板,型号叫 ESP32-2432S028R,网上管它叫"Cheap Yellow Display",简称 CYD。
板子上自带一块 2.8 寸触摸屏,我看网上都是用 Arduino 或 MicroPython 玩的,想着能不能用 Rust 搞点有意思的。
于是就写了这个电子宠物 —— 一个会动的像素机器人。

说实话,一开始我也没想用 Rust,毕竟 C/C++ 才是嵌入式的主流。但之前用 C 写过几个嵌入式项目,被内存问题折磨过几次后,就想换个口味试试。
Rust 在嵌入式这块其实挺成熟的,esp-rs 社区把 ESP-IDF 的绑定做得很好,基本上 C 能用的功能 Rust 都能调。而且编译器能帮你拦住一大堆问题,烧录上去就能跑,调试起来省心不少。
你需要的东西很简单:
板子自带 WiFi、蓝牙、触摸屏,基本上是个完整的小终端了。
这块其实是最劝退的部分,我第一次配环境花了一阵,主要是查资料时间。
下面简单说下步骤:
装 Rust 工具链
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
装 espflash
这是一个专门给 ESP32 烧录程序的工具,类似 Arduino 的上传功能:
cargo install espflash
环境配好后就可以写代码了,编译烧录用 espflash:
espflash flash --monitor
第一次编译会比较慢,因为要下载 ESP-IDF 的 SDK,耐心等就行。
其实嵌入式开发,大部分代码都是在配置引脚和初始化外设,真正业务逻辑没多少。
初始化屏幕
屏幕用的是 SPI 协议通信,需要配置时钟线、数据线、片选这些:
let lcd_spi_driver = esp_idf_hal::spi::SpiDriver::new(
peripherals.spi2,
peripherals.pins.gpio14, // 时钟
peripherals.pins.gpio13, // 数据
None, // 不需要 MISO
&SpiDriverConfig::default(),
)?;
显示图片
用了一个叫 mipidsi 的库,专门驱动这种小屏幕:
let mut disp = Builder::new(mipidsi::models::ST7789, di)
.display_size(240, 320)
.color_order(ColorOrder::Bgr)
.init(&mut delay)?;
触摸检测
触摸芯片是 XPT2046,也是 SPI 通信。检测到点击后判断位置是否在机器人身上:
if tx >= pet.x && tx < pet.x + SPRITE_SIZE
&& ty >= pet.y && ty < pet.y + SPRITE_SIZE {
pet.random_interaction(); // 随机触发一个动作
}
宠物的状态系统
宠物有 6 种状态:发呆、说话、跳舞、睡觉、被摸、吃东西。每个状态对应一套动画帧:
pub enum PetState {
Idle, Talking, Dancing, Sleeping, Petting, Feeding,
}
还有三个数值属性:饥饿度、快乐度、能量。会随时间慢慢下降,你得去戳它陪它玩。
动画其实就是快速切换图片。我画了一套像素风的机器人,每种状态 4 帧,循环播放:
pub fn get_sprite(&self) -> &'static [u16; 16384] {
match self.state {
PetState::Dancing => match self.frame {
0 => &ROBOT_DANCE_0,
1 => &ROBOT_DANCE_1,
// ...
},
// ...
}
}
帧率控制在 30 FPS,每 33 毫秒刷新一次屏幕,肉眼看着就很流畅了。
图片是用 Python 脚本从 PNG 转成 Rust 代码的,放在 robo_sprites.rs 里,一个图片数组大概 32KB。
你可以通过 USB 串口给机器人发消息,它会显示在屏幕上:
python tools/send_msg.py "Hello! Robot!"
这样你就可以写个脚本,把 AI 的回复实时显示在板子上,还挺有意思的。
触摸坐标不准
一开始触摸位置老是对不上,后来发现是需要校准。官方文档给了参考值,X 轴 200-3700,Y 轴 240-3800,然后映射到屏幕坐标:
let tx = map_range(p.x, 200, 3700, 0, 240);
let ty = map_range(p.y, 240, 3800, 0, 320);
屏幕闪烁
刚开始直接清屏再画,闪烁得厉害。后来改成只刷新变化的部分就好了。其实嵌入式开发里这种"脏区域刷新"是很常见的优化手段。
SPI 引脚冲突
这块板子 LCD 和触摸用了两个不同的 SPI 控制器(SPI2 和 SPI3),一开始我给弄混了,花了好久排查。画了张引脚图才搞清楚。
整个项目 600 多行 Rust 代码,功能虽简单,但麻雀虽小五脏俱全——有动画、有交互、有状态管理,是学习嵌入式开发的好入口。
比起用 C,Rust 的体验确实好不少。不用担心空指针、不用担心内存泄漏,编译过了大概率就能跑。虽然生态还不如 C 那么完善,但日常玩玩已经够用了。
有问题欢迎留言,看到都会回。
封面图就是我做的那个像素机器人,挺可爱的吧?
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!