ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

ESP32-S3打造会眨眼的电子眼睛挂坠:硬件选型、图形编程与3D打印全解析

2026/10/7 7:44:49 拓冰建站 浏览量
ESP32-S3打造会眨眼的电子眼睛挂坠:硬件选型、图形编程与3D打印全解析 1. 项目缘起与整体设计思路1.1 这个挂坠到底是个什么东西Eye Pendant V2直译过来就是“眼睛挂坠第二代”。第一代我没做过但看到V2这个后缀就知道这玩意儿肯定是被迭代过的——要么是初代太丑要么是功能太弱要么是焊接太反人类。我拿到这个项目标题的时候脑子里第一反应是一个能戴在身上的、带显示屏的小挂坠屏幕上显示一只眼睛而且这只眼睛会动、会眨、会盯着人看。事实也确实如此。这个项目的核心构成就三块一块ESP32-S3开发板负责算力和驱动、一块小尺寸TFT屏幕负责显示那只眼睛、一个SPDT拨动开关负责电源切换或者模式切换。外壳用3D打印做代码用Arduino IDE写图形库用TFT_eSPI。整套东西做下来体积大概跟一个打火机差不多挂在脖子上或者当钥匙扣都行。它解决的是什么问题说实话它不解决什么实际问题。它就是一个电子饰品一个能让你在朋友聚会上掏出来说“你看这玩意儿会眨眼”的小玩具。但恰恰是这种没有实际用途的东西最能检验一个人的嵌入式功底——因为你要在极小的体积里塞进主控、屏幕、电池、开关还要保证它能稳定运行几个小时不崩这比做个温湿度传感器难多了。适合谁来参考如果你已经玩过Arduino的基础例程点过LED、读过按键、驱动过OLED想找一个综合性强的项目来练手那这个项目非常合适。它涉及SPI通信、电源管理、3D建模、图形编程、低功耗设计基本上把创客技能树上的主要分支都摸了一遍。纯小白也能做但建议先把Arduino IDE的环境搭明白不然光一个库的配置就能卡你两天。1.2 为什么选ESP32-S3而不是别的选主控这件事我踩过的坑比吃过的盐还多。最早我想用Arduino Nano便宜、小巧、资料多但一算内存就傻了——Nano的ATmega328P只有2KB SRAM而我要在屏幕上做动画一帧全屏缓冲就要几十KB根本跑不动。后来考虑STM32性能够了但开发环境配置太折腾而且TFT_eSPI对STM32的支持不如对ESP系列友好。最后锁定ESP32-S3理由有三条。第一内存够大。ESP32-S3内置512KB SRAM还支持外接PSRAM做全屏动画缓冲绰绰有余。第二主频够高。双核240MHz跑图形刷新完全无压力实测刷一帧240x240的图只要几毫秒。第三生态成熟。Arduino IDE对ESP32-S3的支持已经很完善了TFT_eSPI库直接就有现成的配置模板省去了大量底层调试的时间。还有一个隐藏优势ESP32-S3支持USB OTG可以直接模拟成HID设备。这意味着你后续想扩展功能比如让挂坠变成一个体感鼠标或者自定义快捷键硬件上完全可行。虽然V2版本没做这个功能但留了这个口子后续升级空间很大。1.3 屏幕和图形库的搭配逻辑屏幕我选的是1.28寸圆形TFT分辨率240x240驱动芯片是GC9A01。为什么选圆形因为眼睛是圆的用方形屏幕显示圆形眼睛四个角要么留黑边要么被外壳遮住浪费面积。圆形屏幕配合3D打印的圆形外壳整体感强很多。驱动库用TFT_eSPI这是目前Arduino生态里性能最好的TFT驱动库之一。它支持DMA传输刷屏的时候CPU可以同时干别的事。而且它有一个Sprite功能可以在内存里创建一个离屏画布先把整帧画好再一次性推送到屏幕避免闪烁。这个功能对做动画来说太重要了没有它的话你画一笔屏幕闪一下眼睛看起来就像在抽搐。配置TFT_eSPI的时候有个坑它的引脚定义不在代码里而在库文件夹下的User_Setup.h文件里。很多人第一次用的时候改了代码里的引脚发现没反应就是因为没改这个头文件。具体配置我后面会详细说。1.4 电源方案与开关选型电源部分我用的是3.7V锂聚合物电池容量200mAh体积刚好能塞进外壳。ESP32-S3的工作电压是3.3V所以需要一颗LDO稳压芯片我用的是ME6211C33静态电流只有几十微安对续航帮助很大。开关选SPDT单刀双掷型号是SS12D00。为什么不用普通的自锁开关因为SPDT有三个脚公共端、常开端、常闭端。我可以把电池正极接公共端常开端接LDO输入常闭端接一个充电模块。这样拨到一边是开机拨到另一边是充电一个开关搞定两种状态省了一个接口的位置。这个设计思路是从老式收音机上学的简单可靠。注意锂电池的正负极千万不能接反接反的瞬间LDO和ESP32-S3都会烧掉。我第一次做的时候就是晚上焊的迷迷糊糊把红线黑线搞反了上电一股焦味三十块钱的板子直接报废。后来我养成了一个习惯焊电池线之前先用万用表量三遍极性。1.5 3D打印外壳的设计考量外壳用Fusion 360建模分前后两片用卡扣合拢。前片开一个圆形窗口露出屏幕后片留一个充电口和开关拨柄的槽。壁厚设1.2mm这个厚度在强度和体积之间比较平衡——太薄了容易断太厚了显得笨重。打印材料用PLA就行便宜、好打、颜色多。层高0.12mm填充率20%打印出来表面质量足够。如果你想要更好的手感可以用PETG韧性更好但打印温度要高一些而且容易拉丝需要调好回抽参数。有一个细节屏幕和外壳之间要垫一圈黑色泡棉胶一是遮光防止屏幕边缘漏光二是缓冲防止屏幕被外壳压裂。这个泡棉胶用1mm厚的就行剪成圆环状内径比屏幕可视区大1mm外径比屏幕外形小1mm。2. 核心细节解析与实操要点2.1 Arduino IDE环境搭建的完整流程Arduino IDE的安装本身不难官网下载安装包一路下一步就行。难的是ESP32-S3的板级支持包。很多人卡在这一步打开开发板管理器搜esp32发现搜不到或者装完了编译报错。正确的流程是这样的打开Arduino IDE点“文件”-“首选项”在“附加开发板管理器网址”里填入ESP32的板级支持包地址。这个地址在乐鑫的官方文档里有我这里不贴具体链接你搜“ESP32 Arduino core”就能找到。填完之后点“工具”-“开发板”-“开发板管理器”搜索esp32安装最新版本。安装过程可能要十几分钟取决于你的网络速度。如果一直卡在“下载中”可以试试离线包——在GitHub上找到esp32-arduino的release页面下载zip包然后手动解压到Arduino的硬件目录下。具体路径是文档/Arduino/hardware/espressif/esp32没有目录就自己建。装完之后在开发板列表里选“ESP32S3 Dev Module”。有几个参数要改Flash Size选4MBPSRAM选OPI PSRAMUSB CDC On Boot选Enabled。最后这个选项很重要不选的话串口监视器看不到输出。实操心得Mac用户注意Arduino IDE在Mac上第一次打开可能会提示“无法验证开发者”去“系统偏好设置”-“安全性与隐私”里点“仍要打开”就行。另外Mac上串口驱动有时候会抽风如果开发板插上电脑没反应换一根数据线试试——很多Micro USB线只能充电不能传数据这个坑我踩过不止一次。2.2 TFT_eSPI库的配置与引脚映射TFT_eSPI的配置是新手最容易翻车的地方。库安装完之后不要急着写代码先找到库文件夹里的User_Setup.h。在Arduino IDE里可以通过“文件”-“示例”-“TFT_eSPI”-“User_Setup”快速定位。打开User_Setup.h你需要做三件事。第一注释掉所有其他驱动芯片的定义只保留GC9A01。第二定义引脚。ESP32-S3的SPI引脚是可以重映射的我用的配置是SCLK接GPIO12MOSI接GPIO11CS接GPIO10DC接GPIO9RST接GPIO8BLK接GPIO7。第三设置屏幕尺寸把TFT_WIDTH和TFT_HEIGHT都改成240。改完之后保存然后编译一个示例程序测试。如果屏幕白屏或者花屏先检查引脚有没有写错再检查SPI频率是不是太高。GC9A01的SPI时钟建议先设27MHz稳定之后再往上调。我实测调到40MHz也能跑但线太长的话可能会出问题。还有一个隐藏选项#define SPI_FREQUENCY和#define SPI_READ_FREQUENCY。写频率和读频率要分开设读频率一般比写频率低因为读操作对时序要求更严格。2.3 眼睛动画的图形编程思路眼睛动画的核心是帧缓冲加局部刷新。如果你每帧都全屏重绘240x240的分辨率16位色深一帧就是115KB的数据量SPI传输再快也扛不住高帧率。所以要用TFT_eSPI的Sprite功能在内存里建一个240x240的16位色Sprite所有绘图操作都在Sprite上进行画完之后只把变化区域推送到屏幕。眼睛的绘制分三层眼白、虹膜、瞳孔。眼白就是一个填充的白色圆虹膜是一个带径向渐变的蓝色圆瞳孔是一个黑色小圆。眨眼动画就是改变眼白和虹膜之间的垂直距离——上眼睑往下压下眼睑往上抬中间露出的部分就是眼睛。虹膜的径向渐变用TFT_eSPI的fillCircle逐层画圆来实现。从外到内颜色从深蓝渐变到浅蓝每层半径递减1像素颜色值按比例插值。这个方法效率不高但240x240的屏幕上虹膜直径也就100像素左右画100个圆对ESP32-S3来说毫无压力。瞳孔的位置可以随机偏移模拟眼睛看不同方向的效果。偏移范围控制在虹膜半径的30%以内不然瞳孔会跑到虹膜外面去看起来像脱窗了。2.4 SPDT开关的接线与模式切换逻辑SPDT开关的三个脚我用万用表蜂鸣档测过中间是公共端两边分别是常开和常闭。接线方案是电池正极接公共端常开端接LDO的VIN常闭端接TP4056充电模块的BAT。这样拨到常开端时电池给LDO供电系统开机拨到常闭端时电池和充电模块连通插USB就能充电。这里有个细节TP4056的BAT和OUT是两回事。BAT是接电池的OUT是给负载供电的。如果你把负载接在OUT上充电的时候负载也会通电但TP4056的放电保护能力有限大电流负载可能会触发保护。所以我的方案是负载直接接电池充电模块只负责充电不负责放电路径。模式切换的逻辑在代码里实现。我定义了两个模式待机模式和互动模式。待机模式下眼睛缓慢眨眼帧率降到15fps省电互动模式下眼睛快速转动帧率30fps响应按键或触摸。模式切换通过一个额外的轻触按键实现不是SPDT开关——SPDT只管电源不管模式。2.5 3D打印外壳的建模要点Fusion 360建模的时候先画屏幕的轮廓。GC9A01的模块尺寸是直径36mm厚度2.5mm。外壳前片的窗口直径开34mm留1mm的边压住屏幕边缘。屏幕后面做一个台阶深度2.5mm刚好卡住屏幕。后片要留三个开口充电口用Type-C母座开孔尺寸8.5mm x 3.5mm开关槽开在侧面宽度3mm长度5mm让拨柄能伸出来挂绳孔开在顶部直径2mm穿挂绳用。卡扣设计用悬臂梁结构前片做凸起后片做凹槽合拢的时候靠塑料的弹性卡住。悬臂梁的厚度设0.8mm长度4mm这个尺寸的弹性刚好太厚了卡不进去太薄了容易断。打印技巧PLA打印悬臂梁的时候层高设0.1mm打印速度降到30mm/s冷却风扇开最大。这样打出来的悬臂梁韧性最好不容易在装配的时候折断。我第一批打的外壳悬臂梁用0.2mm层高打的装的时候一按就断了后来改成0.1mm层高就没再断过。3. 实操过程与核心环节实现3.1 硬件焊接与组装顺序焊接顺序很重要搞错了会很难受。我的顺序是先焊ESP32-S3模块的排针再焊TFT的排线然后焊LDO和充电模块最后焊电池线。ESP32-S3模块的排针要焊两排每排20个脚。焊的时候先把排针插在面包板上模块放在排针上这样排针不会歪。焊完一排检查一排用放大镜看有没有虚焊。虚焊是嵌入式项目最大的隐形杀手有时候你代码调了半天最后发现是某个引脚没焊好。TFT的排线是8pin的FPC座焊的时候温度不要太高350度就够了高了会把座子烫变形。焊完用万用表量一下每个脚对地的阻值确认没有短路。LDO和充电模块都是贴片元件手焊的话用尖头烙铁先在一个焊盘上上锡用镊子夹住元件放上去焊固定再焊另一边。ME6211C33的封装是SOT-23-5引脚间距0.95mm手焊有点挑战但多练几次就熟了。电池线最后焊焊之前用万用表确认电池电压在3.7V到4.2V之间。如果低于3.0V电池可能已经过放了先充电再焊。3.2 代码框架与关键函数实现代码框架分四个部分初始化、动画循环、模式管理、电源管理。初始化部分负责配置TFT、创建Sprite、设置引脚模式。关键代码是#include TFT_eSPI.h TFT_eSPI tft TFT_eSPI(); TFT_eSprite eyeSprite TFT_eSprite(tft); void setup() { tft.init(); tft.setRotation(0); eyeSprite.createSprite(240, 240); eyeSprite.setSwapBytes(true); pinMode(BUTTON_PIN, INPUT_PULLUP); }setSwapBytes(true)这行很关键不设的话颜色会变成反的——红色变蓝色蓝色变红色。这是因为TFT_eSPI默认使用大端序而ESP32是小端序需要交换字节。动画循环部分用millis()做时间管理不用delay()。delay()会阻塞整个循环导致按键响应迟钝。正确的做法是记录上一次刷新的时间如果距离现在超过帧间隔就刷新一帧。unsigned long lastFrame 0; int frameInterval 33; // 30fps void loop() { unsigned long now millis(); if (now - lastFrame frameInterval) { lastFrame now; drawEye(); eyeSprite.pushSprite(0, 0); } checkButton(); }drawEye()函数负责在Sprite上画眼睛。眨眼动画用一个状态机实现眼睛睁开、正在闭合、完全闭合、正在睁开。每个状态持续不同的帧数闭合和睁开的过程用插值计算眼睑位置。3.3 眼睛动画的参数调优眼睛动画看起来自不自然关键在眨眼速度和瞳孔移动的节奏。眨眼速度太快像抽搐太慢像犯困。我试过好几组参数最后定下来的是闭合过程4帧完全闭合保持2帧睁开过程6帧。按30fps算一次眨眼大概0.4秒跟真人眨眼速度差不多。瞳孔移动用随机游走算法。每帧给瞳孔位置加一个随机偏移量偏移量限制在±1像素以内然后限制瞳孔位置在虹膜范围内。这样瞳孔会缓慢地飘来飘去看起来像在扫视周围。虹膜颜色我调了三个版本纯蓝色、蓝绿色、琥珀色。最后选了琥珀色因为琥珀色在圆形屏幕上对比度最高远看像一只猫眼。颜色值用RGB565格式琥珀色大概是0xFD20。调参心得不要一次改太多参数改一个测一个。我一开始同时改了眨眼速度和瞳孔移动范围结果眼睛看起来像得了帕金森根本分不清是哪个参数的问题。后来学乖了每次只调一个参数用串口打印当前参数值方便对比。3.4 电源管理与续航实测续航是这类穿戴设备的核心指标。我实测了几种配置配置帧率平均电流200mAh电池续航全亮全速30fps85mA约2.3小时降亮度30fps62mA约3.2小时降亮度降帧率15fps41mA约4.9小时待机模式5fps18mA约11小时降亮度是通过TFT的BLK引脚输出PWM实现的。ESP32-S3的LEDC外设可以输出PWM频率设5000Hz占空比从100%降到40%屏幕亮度明显降低但还能看清电流从85mA降到62mA。待机模式是检测到10秒没有按键操作后自动进入帧率降到5fps只做缓慢眨眼。按一下按键唤醒到互动模式。还有一个省电技巧不用屏幕的时候把屏幕关掉。TFT_eSPI没有直接的关屏函数但可以通过写GC9A01的寄存器实现。发送命令0x28Display Off就能关屏发送0x29Display On开屏。关屏后电流降到5mA以下。3.5 外壳组装与最终调试组装顺序先把屏幕用泡棉胶粘在前片内侧然后把ESP32-S3模块用双面胶固定在后片内侧电池用泡棉胶粘在模块上面。所有线整理好用细扎带绑一下防止合壳的时候压断。合壳之前先上电测试确认屏幕显示正常、按键响应正常、开关切换正常。测试通过后再合壳合的时候从一侧开始压听到“咔”一声就是卡住了。最终成品尺寸是42mm x 42mm x 14mm重量38克含电池。挂在脖子上有点分量但不算累赘。如果换成更薄的电池厚度可以压到10mm以内。4. 常见问题与排查技巧实录4.1 屏幕不亮或白屏的排查流程屏幕不亮是最常见的问题排查要按顺序来不要跳步。第一步量电压。用万用表量LDO的输出应该是稳定的3.3V。如果电压不对检查电池电压和LDO焊接。LDO的输入输出不能接反接反了输出就是0V。第二步量背光。BLK引脚应该有PWM信号或者高电平。如果没有检查代码里有没有配置BLK引脚为输出模式。有些TFT模块的BLK是低电平点亮的这种情况要输出低电平。第三步检查SPI通信。用逻辑分析仪或者示波器看SCLK和MOSI有没有波形。如果没有波形检查User_Setup.h里的引脚定义和实际接线是否一致。我遇到过好几次代码里写GPIO12实际焊的是GPIO13的情况查了半天。第四步检查初始化序列。GC9A01的初始化序列比较长如果某个寄存器写错了屏幕可能白屏。TFT_eSPI库自带的GC9A01驱动是验证过的一般不会有问题但如果你改过User_Setup.h里的参数可能会影响初始化。4.2 颜色显示异常的解决方法颜色不对通常有三种表现红蓝互换、颜色发灰、颜色断层。红蓝互换就是前面说的字节序问题在Sprite上设setSwapBytes(true)就能解决。如果设了还是不对检查TFT的初始化代码里有没有设TFT_RGB_ORDER有些屏幕是BGR排列的需要改成TFT_BGR。颜色发灰一般是色深设置不对。TFT_eSPI默认是16位色RGB565如果你设成了8位色颜色就会发灰。检查User_Setup.h里的#define COLOR_DEPTH确保是16。颜色断层是渐变算法的问题。用fillCircle逐层画圆做渐变的时候如果颜色插值步长太大就会看到明显的色带。解决办法是增加渐变层数或者用抖动算法在相邻像素间随机分配颜色值。4.3 动画卡顿与帧率不稳的优化动画卡顿的原因通常有三个SPI频率太低、Sprite太大、代码里有阻塞操作。SPI频率先检查User_Setup.h里的SPI_FREQUENCY默认可能是27MHz可以试着调到40MHz。但要注意频率太高可能导致通信不稳定如果调高之后出现花屏就降回来。Sprite太大也会影响性能。240x240的16位Sprite占115KB内存ESP32-S3的SRAM有512KB放一个Sprite没问题但如果你同时创建多个Sprite内存就不够了。解决办法是复用Sprite不要重复创建。代码里的阻塞操作最常见的是delay()和while循环等待。把所有delay()换成millis()时间管理把while等待换成状态机。我重构过一次代码把三个delay(100)去掉之后帧率从18fps提到了28fps。4.4 电池续航不达预期的排查续航短的原因可能是漏电流大、屏幕亮度高、帧率过高。漏电流用万用表电流档测。把万用表串在电池和LDO之间关机状态下测静态电流。正常应该在几十微安以内。如果超过1mA检查有没有引脚悬空或者上拉电阻太小。ESP32-S3的未使用引脚建议设成输入模式不要悬空。屏幕亮度是最大的耗电户。把BLK的PWM占空比从100%降到40%电流能降20mA左右。如果还是不够考虑换低功耗屏幕或者用OLED——OLED黑色不发光显示眼睛这种大部分是黑色的画面功耗比TFT低很多。帧率对功耗的影响是线性的。30fps降到15fps功耗差不多减半。但帧率太低眼睛看起来会卡15fps是底线再低就不流畅了。4.5 常见问题速查表现象可能原因排查方法解决方案屏幕不亮电源问题量LDO输出电压检查电池和LDO焊接屏幕白屏SPI通信失败示波器看SCLK波形检查引脚定义和接线颜色反了字节序错误显示纯红色测试设setSwapBytes(true)颜色发灰色深设置错误检查COLOR_DEPTH改为16位色动画卡顿SPI频率低测帧率提高SPI_FREQUENCY续航短漏电流大测静态电流检查悬空引脚按键不灵上拉电阻问题量按键引脚电压启用内部上拉充电不进去充电模块故障量BAT电压检查TP4056焊接外壳合不上卡扣尺寸不对量卡扣厚度调整建模参数屏幕压裂外壳公差太小检查屏幕台阶增加0.2mm余量独家避坑焊接FPC座的时候千万不要用焊锡膏。焊锡膏的残留物会渗进FPC座的触点之间造成短路。用松香或者免洗助焊剂就行。我有一次用了焊锡膏屏幕死活不亮洗了三遍板子才恢复正常。4.6 进阶扩展思路V2版本做完之后我琢磨了几个扩展方向。第一加陀螺仪。用MPU6050检测挂坠的倾斜角度眼睛跟着重力方向看像真眼睛一样。第二加蓝牙。用ESP32-S3自带的蓝牙手机连上之后可以改眼睛颜色、眨眼频率。第三加触摸。用TTP223触摸模块摸一下挂坠眼睛就眨一下互动感更强。这些扩展都不难硬件上留了足够的引脚和空间。但每加一个功能功耗就上去一点续航就下来一点。怎么平衡功能和续航是这个项目永恒的课题。我个人在实际操作中的体会是做这类小玩意儿完成比完美重要。第一版能亮、能眨眼、能戴就已经成功了。颜色不够好看、帧率不够高、外壳有层纹这些都是可以后续迭代的。最怕的是追求一步到位结果卡在某个细节上项目烂尾。先做出来再做好这是我做了几十个创客项目之后最深的感悟。