用 Rust 给 ESP32 写个电子宠物

  • King
  • 发布于 8小时前
  • 阅读 22

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

前阵子朋友送了一块开发板,型号叫 ESP32-2432S028R,网上管它叫"Cheap Yellow Display",简称 CYD。

板子上自带一块 2.8 寸触摸屏,我看网上都是用 Arduino 或 MicroPython 玩的,想着能不能用 Rust 搞点有意思的。

于是就写了这个电子宠物 —— 一个会动的像素机器人。

robot-on-esp32.png

先说下为啥要用 Rust

说实话,一开始我也没想用 Rust,毕竟 C/C++ 才是嵌入式的主流。但之前用 C 写过几个嵌入式项目,被内存问题折磨过几次后,就想换个口味试试。

Rust 在嵌入式这块其实挺成熟的,esp-rs 社区把 ESP-IDF 的绑定做得很好,基本上 C 能用的功能 Rust 都能调。而且编译器能帮你拦住一大堆问题,烧录上去就能跑,调试起来省心不少。

硬件准备

你需要的东西很简单:

  • 一块 CYD 板子(某宝搜 ESP32-2432S028R,几十块)
  • 一根 Micro USB 线(注意要能传数据的,不是那种只能充电的)
  • 电脑(Windows/Mac/Linux 都行)

板子自带 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 那么完善,但日常玩玩已经够用了。

有问题欢迎留言,看到都会回。


封面图就是我做的那个像素机器人,挺可爱的吧?

点赞 0
收藏 0
分享
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

0 条评论

请先 登录 后评论
King
King
0x56af...a0dd
擅长Rust/Solidity/FunC/Move开发