Adafruit EyeLights LED眼镜编程实战:火焰、眨眼与BMP动画全解析
1. 项目概述:用代码点亮你的眼镜
如果你玩过微控制器,比如树莓派Pico或者Adafruit的各类板子,那你肯定对LED点阵屏不陌生。但把LED矩阵和环形灯带直接集成到一副眼镜上,还能用CircuitPython这种对新手极其友好的语言来编程,这事儿就有点意思了。Adafruit的EyeLights LED眼镜就是这么个玩意儿,它把15x5的RGB LED矩阵和两个各24颗LED的环形灯带塞进了眼镜腿,让你能直接在眼前编程显示各种动画。
这不仅仅是“让灯闪起来”那么简单。它的核心挑战在于,如何在资源极其有限的微控制器上(通常只有几百KB内存,主频几十到几百MHz),实现流畅、生动且不重复的动画效果。这就像让你用一台老式功能手机去渲染一段游戏CG,你得在算法和创意上动足脑筋。本文要拆解的,就是三个极具代表性的案例:一个经典的8位机风格的火焰模拟,一个拟人化的眨眼眼睛动画,以及一个利用预渲染BMP位图来播放复杂动画的“偷懒”大法。我会带你从硬件接线、库安装,一直深入到每一行关键代码的逻辑,并分享我在调试这些效果时踩过的坑和总结的技巧。无论你是刚接触CircuitPython的新手,还是想为你的可穿戴项目寻找灵感的资深玩家,这里都有你能直接拿去用的干货。
2. 硬件准备与开发环境搭建
2.1 核心硬件:Adafruit EyeLights LED眼镜与驱动板
项目的主角是Adafruit EyeLights LED Glasses套件。它主要包含两部分:一副嵌入了LED的眼镜,和一块对应的IS31FL3741 LED驱动板。眼镜本身包含的LED分为两个区域:位于镜片前方的是一块15列 x 5行的RGB LED矩阵,用于显示主要的图形和文字;位于眼镜框两侧的是两个独立的环形LED阵列,每个环由24颗LED组成,可以用来显示辅助动画、进度条或者装饰性光效。
为什么需要单独的驱动板?因为眼镜上的LED数量不少(矩阵75颗 + 环形48颗 = 123颗RGB LED,即369个独立的LED通道),直接使用微控制器的GPIO引脚驱动是不现实的,无论是引脚数量还是电流都远远不够。IS31FL3741是一颗专业的LED矩阵驱动芯片,它通过I2C总线与主控MCU通信,接收指令后,自己负责所有LED的PWM调光和刷新,极大地减轻了主控的负担。我们写的CircuitPython代码,本质就是在通过I2C向这颗驱动芯片发送数据。
注意:在连接硬件时,务必确保使用足够粗的导线连接驱动板的电源输入端(通常是
5V和GND)。123颗LED全亮白色时,瞬时电流可能超过1A,劣质或过细的导线会产生压降,导致LED亮度不均甚至主控板重启。
2.2 软件环境:CircuitPython与必备库
你需要一块支持CircuitPython的开发板,例如Adafruit的ItsyBitsy RP2040、Feather RP2040,或者树莓派Pico。首先,去CircuitPython官网下载对应板型的最新版本UF2固件,按住板子上的BOOT(或BUF)按钮的同时连接USB,将出现的U盘中的旧固件删除,把下载的UF2文件拖进去,板子会自动重启并变成一个名为CIRCUITPY的U盘。
接下来是安装库文件。对于Adafruit的项目,最省事的方法是使用“项目捆绑包(Project Bundle)”。在每个示例的页面,通常都有一个“Download Project Bundle”按钮。点击下载后,你会得到一个.zip文件。解压后,你会看到lib文件夹和code.py文件。将lib文件夹内的所有.mpy或.py库文件,全部复制到你的CIRCUITPY盘的lib目录下(如果不存在就新建一个)。然后,用项目提供的code.py覆盖掉CIRCUITPY根目录下原有的code.py。这样,库和代码就一次性配置好了。
对于本文涉及的三个项目,核心库是adafruit_is31fl3741及其子模块adafruit_ledglasses。对于BMP动画项目,还需要adafruit_imageload和displayio库,这些通常也会包含在项目捆绑包中。
2.3 基础代码结构解析
无论实现哪种动画,与眼镜硬件交互的初始化代码都是类似的。我们来看一下这段几乎每个项目都有的“样板代码”:
import board from busio import I2C import adafruit_is31fl3741 from adafruit_is31fl3741.adafruit_ledglasses import LED_Glasses # 手动声明I2C总线,并提升频率至1MHz以获得更快的刷新率 i2c = I2C(board.SCL, board.SDA, frequency=1000000) # 初始化LED眼镜驱动,启用缓冲模式以获得更平滑的动画 glasses = LED_Glasses(i2c, allocate=adafruit_is31fl3741.MUST_BUFFER) glasses.show() # 清空显示,避免启动时的残留 glasses.global_current = 20 # 设置全局电流,控制亮度(范围0-255)这里有几个关键点:
- 手动声明I2C:我们没有使用
board.I2C(),而是手动实例化busio.I2C对象。这样做的主要目的是为了将I2C频率设置为1000000(1MHz)。默认频率可能较低,提高频率可以加快数据传输,让动画更流畅,减少闪烁感。 - 缓冲模式(MUST_BUFFER):这是至关重要的一个参数。启用缓冲后,我们对LED颜色的所有设置操作(如
glasses.pixel(x, y, color))都是在内存中的一个缓冲区里进行的。只有当我们调用glasses.show()时,所有更改才会被一次性发送到驱动芯片。这避免了在逐像素设置过程中屏幕出现撕裂或闪烁,是实现平滑动画的基础。 - 全局电流(global_current):这个值控制所有LED的最大亮度。设置为20是一个中等偏保守的亮度,既保证了效果,又避免了电流过大。你可以根据环境光和个人喜好调整,但请注意,调高会显著增加功耗和发热。
3. 火焰效果:在像素限制中模拟自然
3.1 算法思路:来自8位机时代的智慧
火焰效果是一个经典的“障眼法”。它并不模拟真实的流体力学,而是用一个巧妙的迭代算法,在内存和计算力都捉襟见肘的环境下(比如古老的8位微机),制造出足以乱真的视觉印象。其核心思想可以概括为“向上传播并衰减的随机噪声”。
想象一个比实际LED矩阵多一行、左右各多一列的虚拟网格(data)。在每一帧动画的开始,我们在最底部那一行(虚拟的、屏幕外的行)注入随机强度的“热量”(噪声)。然后,从下往上,计算每一行像素的新值:每个像素的“热度”主要来源于它正下方那个像素,并少量混合其左下和右下像素的热度,最后乘以一个小于1的衰减系数。这样,底部的随机热源就会向上“飘散”,同时亮度逐渐减弱,形成火焰升腾并消失的效果。数学上很粗糙,但人眼却很容易接受。
3.2 代码逐行解析与实现
让我们深入火焰效果的核心循环代码,看看这个思想是如何落地的。
首先,我们初始化一个二维数组data作为我们的“热度图”:
# 虚拟网格:高度比LED矩阵多1行(用于底部噪声),宽度左右各多1列(避免边界检查) data = [[0] * (glasses.width + 2) for _ in range(glasses.height + 1)] # glasses.width是18,height是5。所以data是6行 x 20列的网格。接着,我们创建一个颜色查找表colormap,将0-31的热度值映射为从黑到红,再到黄,最后到白的渐变色。这里使用了伽马校正(gamma = 2.6),因为人眼对亮度的感知不是线性的,伽马校正能让亮度变化看起来更均匀自然。
主循环while True内的try块是动画发生的地方:
注入噪声:在每一帧,更新虚拟网格最底部(第5行,索引从0开始)的数据。这里用了一个小技巧:新值 = 旧值 * 0.33 + 随机值 * 0.67 * 85。保留三分之一旧值是为了让噪声变化不那么突兀和闪烁,乘以85是一个经验值,用来将随机数范围适配到颜色表的范围附近。
for x in range(1, 19): # 遍历第5行,避开左右边界列 data[5][x] = 0.33 * data[5][x] + 0.67 * random.random() * 85热度传播与衰减:从下往上(
for y in range(5)),计算新一帧的热度。对于data[y][x](屏幕上的第y行,第x列),它的新值来源于其下方一行y1 = data[y+1]的三个像素:正下方y1[x],左下方y1[x-1]和右下方y1[x+1]。正下方像素的权重最高(直接相加),左右下方像素权重较低(乘以0.33后再相加)。总和最后乘以衰减系数0.35。这个0.35是关键,它确保了火焰在上升过程中快速变暗。data[y][x] = (y1[x] + ((y1[x - 1] + y1[x + 1]) * 0.33)) * 0.35映射到LED:计算出的热度值(一个浮点数)被转换为0-31的整数索引,然后通过
colormap查找表获取对应的24位RGB颜色,并设置到眼镜矩阵的实际像素上。glasses.pixel(x - 1, y, colormap[min(31, int(data[y][x]))]) # x-1是因为data有左右边界处理环形LED:矩阵两侧的环形LED并不与像素网格对齐。代码采用了一种折中但高效的方法:在矩阵的特定边缘像素(如左上角、右上角等)取样热度值,然后在环形LED的对应弧段上进行线性插值(
interp函数),从而让环形灯带也呈现出与矩阵火焰衔接的渐变色。刷新显示:最后调用
glasses.show(),将缓冲区中的所有更改推送到硬件。
实操心得:火焰效果的“味道”很大程度上由几个参数决定:底部噪声的混合权重(0.33和0.67)、衰减系数(0.35)以及颜色映射表。你可以尝试调整这些值。例如,将衰减系数改为0.5,火焰会升得更高、更持久;改为0.2,火焰则会短促而明亮。调整颜色映射表
colormap的生成逻辑,可以创造出“冷火”、“鬼火”等不同色调的火焰效果。
3.3 错误处理与稳定性优化
你可能注意到了代码被包裹在一个try...except OSError块中。这是因为I2C通信在物理上比较脆弱,线缆稍有松动或干扰,就可能引发通信错误。如果捕获到OSError,代码会打印“Restarting”并调用supervisor.reload()来软重启整个CircuitPython程序。这是一种简单粗暴但有效的容错机制,能确保在绝大多数情况下,动画在短暂中断后能自动恢复,而不是完全死机。
对于需要长时间稳定运行的项目(比如穿戴展示),我建议除了这种重启机制,还可以考虑加入看门狗定时器(WatchDog Timer),并在主循环中增加一些状态心跳指示,以便更好地监控程序健康状态。
4. 眨眼动画:赋予像素以生命
4.1 设计哲学:拟人化与平滑运动
眨眼动画的目标是让两个简单的圆形“瞳孔”看起来像有生命的眼睛。这涉及到两个核心挑战:一是如何在小尺寸、低分辨率的5x9像素区域内(每只眼睛)让圆形边缘看起来平滑(抗锯齿);二是如何让眼球的移动和眨眼的动作看起来自然,而不是机械的跳变。
项目代码通过两个关键技术解决了这些问题:
3倍超采样(3X Space):在内存中创建一个分辨率是实际LED矩阵3倍的虚拟画布(对于眼睛区域,就是18x15像素)。在这个高分辨率画布上绘制圆形或椭圆,然后通过求平均的方式“下采样”到实际的LED网格。例如,虚拟画布上3x3的9个像素点,对应实际LED矩阵的1个像素。如果这9个点中有5个被点亮,那么实际像素的亮度就是5/9。这本质上是一种软件实现的抗锯齿,让圆形边缘呈现出灰度过渡,避免了生硬的锯齿感。
物理模拟与缓动函数:眼球的移动不是直接从A点跳到B点,而是模拟了“挤压和拉伸”的弹性效果。代码中,眼球被建模为一个椭圆,由两个焦点(
p1和p2)定义。在移动时,前焦点p1先快速向目标移动,后焦点p2稍晚启动,这样在移动过程中,眼球会因两个焦点的分离而被拉长,到达目标后再合并为圆形,产生了非常生动的弹性效果。移动过程使用了三次缓动函数(3*e^2-2*e^3),这使得启动和停止都不是线性的,而是有加速和减速的过程,更符合真实物体的运动规律。
4.2 核心代码模块拆解
眨眼动画的代码比火焰效果更复杂,因为它包含了状态机(控制眨眼和移动的时序)、椭圆光栅化算法和抗锯齿下采样。
1. 椭圆光栅化(rasterize函数)这个函数是数学密集型。它根据两个焦点和给定的“半径”(当焦点重合时形成标准圆的半径),利用椭圆定义(到两焦点的距离之和为常数)来判定虚拟画布上的每个像素点是否在椭圆内部。虽然作者自嘲这是“蛮力”方法,对每个像素计算平方根,但对于这么小的区域(18x15)来说,在CircuitPython上完全可行。计算出的perimeter(周长)就是那根“绳子”的长度,用于判断点是否在椭圆内。
2. 抗锯齿下采样(smooth方法)这是Eye类的一个方法。它接收高分辨率的bitmap(虚拟画布)和一个影响区域rect。函数的核心是一个三重循环:对于输出LED矩阵的每一个像素(比如眼睛区域的6x5),它累加对应虚拟画布上3x3共9个像素的值(0或1),然后将累加和(0-9)作为索引,去预计算的colormap中查找最终的颜色。这个colormap已经根据eye_color(瞳孔颜色)和伽马校正预先生成好了。
3. 动画状态机主循环中的时间逻辑控制着一切:
- 眨眼:
blink_state有0(静止)、1(闭合中)、2(睁开中)三个状态。持续时间是随机的(random.uniform),闭合快,睁开慢,中间有随机长度的停顿,这使得眨眼看起来不那么规律和机械。 - 移动:
in_motion标志控制眼球是否在移动。移动的持续时间、方向和距离都是随机的。移动时,通过ratio(经过时间占总时间的比例)和缓动函数e来计算两个焦点p1和p2的实时位置,实现弹性移动效果。
4. 环形LED的眨眼配合当眼睛眨眼时,环形LED需要模拟“眼睑”扫过的效果。代码预计算了环形上每个LED在虚拟画布空间中的Y坐标(y_pos)。在眨眼时,根据上下眼睑的位置(upper,lower),计算每个LED被眼睑覆盖的比例(ratio),然后在这个比例上,将环形的基础颜色(ring_open_color)和眨眼时的颜色(ring_blink_color)进行插值混合,从而让环形LED的颜色随着眼睑的开合平滑变化。
注意事项:代码中有一个重要的细节,即两只眼睛的瞳孔有轻微的“对焦”偏移(
x_offset)。左眼的瞳孔中心在3X空间里向右偏移+2,右眼向左偏移-2。这使得两只眼睛的瞳孔并非完全对称地落在像素网格的同一点上,避免了因像素对齐而产生的“对视”或“斗鸡眼”等不自然感,这是一个非常细腻的设计。
5. BMP动画:用图像预渲染解放CPU
5.1 原理与优势:空间换时间的艺术
当你需要播放一段复杂、逐帧绘制非常困难的动画(比如一段文字滚动、一个旋转的Logo或一段小故事)时,逐帧实时计算可能会耗尽MCU的资源。BMP动画方案提供了另一种思路:将动画预先在电脑上绘制好,保存为位图文件,然后在设备上只需按顺序读取和显示这些帧。
这本质上是“空间换时间”。我们消耗了额外的存储空间(Flash或SD卡)来存放图像数据,但换来了极低的CPU占用率。播放动画变成了简单的文件读取和内存拷贝操作,MCU可以轻松达到很高的帧率,甚至同时处理其他任务(如读取传感器)。
项目使用了两种不同的位图布局来分别驱动矩阵和环形LED,这是为了复用Adafruit已有的教程代码,降低学习成本:
- 矩阵动画:采用“精灵图(Sprite Sheet)”布局。多帧动画纵向或横向排列在一张大图里。例如,一个18像素宽、5像素高的动画,如果有10帧,可以做成18x50像素的BMP(10帧纵向排列),或者180x5像素(10帧横向排列)。代码会根据帧索引计算出当前帧在精灵图中的位置。
- 环形动画:采用“波形图”布局。图像宽度代表时间(帧数),高度固定为48像素(对应左右两个环各24颗LED)。播放动画就是从左到右(或反之)扫描图像的每一列,将这一列48个像素的颜色分别赋给48颗环形LED。
5.2 EyeLightsAnim库详解
eyelights_anim.py这个库文件封装了所有处理BMP的细节,让主程序变得极其简洁。
1. 初始化与图像加载在__init__中,库使用adafruit_imageload.load加载指定的BMP文件。这里要求BMP必须是索引颜色模式(4位或8位),即包含一个调色板。加载后,库会对调色板中的所有颜色应用伽马校正(gamma_adjust函数),确保显示亮度与在电脑上观看时一致。
2. 帧绘制逻辑
draw_matrix(matrix_frame): 如果传入特定帧号,就显示该帧;否则播放下一帧(自动循环)。它根据帧号计算出在精灵图中的切片位置(xoffset,yoffset),然后将这个18x5的切片数据逐个像素地拷贝到LED眼镜的矩阵缓冲区。它还会检查调色板中该索引是否被标记为透明色(is_transparent),如果是则跳过,这允许我们创建带有透明背景的动画。draw_rings(ring_frame): 逻辑类似,但更简单。它直接读取位图中指定列(ring_frame)的48个像素值,前24个给左环,后24个给右环。frame(matrix_frame, ring_frame): 这是给用户调用的主接口。它根据初始化时设定的rings_on_top参数,决定先画矩阵还是先画环形LED,以解决两者共享像素时的覆盖问题。
3. 主程序中的使用主程序code.py的使用简单到令人发指:
anim = EyeLightsAnim(glasses, "matrix.bmp", "rings.bmp") while True: anim.frame() # 播放下一帧 glasses.show() time.sleep(0.02) # 控制帧率,约50FPS你可以通过anim.frame(matrix_index, ring_index)来分别控制矩阵和环形的帧,实现更复杂的同步效果,比如让环形动画在矩阵动画播放到一半时才开始。
5.3 制作动画BMP的技巧与工具
如何制作符合要求的BMP文件?
- 图像尺寸:
- 矩阵动画:宽度必须是18的倍数,高度必须是5的倍数。总帧数 = (宽度/18) * (高度/5)。
- 环形动画:高度必须为48像素,宽度即总帧数。
- 颜色模式:务必保存为索引颜色(Indexed Color)的BMP。在Photoshop、GIMP或免费的在线转换器中,在保存时选择“BMP”格式,然后通常会有一个选项让你选择“颜色深度”或“模式”,选择“8位”或“索引色”。颜色数最多256色。
- 透明色:你可以指定一种颜色作为透明色。在调色板中,将该颜色的索引标记为透明。在EyeLightsAnim库中,绘制时会跳过透明索引的像素。这可以用来创建非矩形的动画元素。
- 工具推荐:
- Aseprite:一款优秀的像素画和精灵动画软件,非常适合制作这种小尺寸的逐帧动画,能直接导出精灵图。
- GIMP:免费开源,功能强大。你可以创建一个多图层的文件,每一层是一帧,然后使用“将图层导出为文件”的脚本,或者手动拼接成精灵图。
- 代码生成:对于规律性强的动画(如几何图形变换),完全可以写一个Python脚本,用PIL(Pillow)库来生成序列帧并拼接成BMP,这是最灵活的方式。
踩坑记录:最常见的错误就是保存成了24位真彩色BMP。
adafruit_imageload在加载真彩色BMP时行为可能不一致,或者内存占用激增。务必确认是索引色。另一个坑是图像尺寸不对,导致代码计算切片时数组越界。在制作时,最好先用一个纯色测试图来验证整个流程是否通畅。
6. 性能优化与深度调试技巧
6.1 内存与帧率监控
在资源受限的微控制器上编程,必须对资源使用心中有数。CircuitPython提供了gc(垃圾回收)和sys模块来帮助监控。
你可以在代码中定期打印内存使用情况:
import gc import sys frames = 0 start_time = time.monotonic() while True: # ... 你的动画主循环 ... frames += 1 if frames % 100 == 0: # 每100帧打印一次 elapsed = time.monotonic() - start_time print(f"FPS: {frames/elapsed:.1f}, Mem Free: {gc.mem_free()}")如果发现帧率(FPS)远低于预期(比如低于30),或者空闲内存(gc.mem_free())在持续下降,就说明可能存在性能瓶颈或内存泄漏。火焰和眨眼动画因为涉及浮点运算和对象创建(如bytearray),会比纯BMP播放更耗资源。
6.2 提升动画流畅度的关键
使用
time.monotonic()而非time.sleep()进行节拍控制:在主循环末尾使用固定的time.sleep(0.02)确实简单,但这无法保证每一帧的计算时间相同。如果某一帧计算量突然变大,睡眠时间不变,就会导致这一帧显示时间变长,动画卡顿。更专业的做法是记录每一帧开始的时间,计算该帧耗时,然后动态调整等待时间,以维持固定的帧间隔。frame_duration = 1.0 / 50 # 目标帧率50FPS,每帧0.02秒 next_frame_time = time.monotonic() while True: # 执行动画计算和glasses.show() next_frame_time += frame_duration sleep_time = next_frame_time - time.monotonic() if sleep_time > 0: time.sleep(sleep_time) else: pass # 我们已经落后于计划,跳过等待,尽快开始下一帧预计算与查找表:这是嵌入式图形编程的黄金法则。眨眼动画中预计算的
colormap、y_pos和eyelid扫描线就是典范。任何可以提前算好、避免在循环中重复计算的值,都应该被预计算并存储起来。例如,火焰效果中的伽马校正颜色表、正弦/余弦值等。精简循环与避免动态内存分配:在
while True主循环内部,尽量避免创建新的列表、字典等对象。像火焰效果中的data数组是在循环外初始化的,每次只是修改其值。眨眼动画中,bitmap = [bytearray(6 * 3) for _ in range(5 * 3)]这行在循环内创建了新的bytearray,对于RP2040这类性能较强的MCU问题不大,但在更弱的芯片上,可以考虑将其移到循环外,然后用bytearray的fill(0)或切片赋值来清空。
6.3 常见问题排查速查表
| 问题现象 | 可能原因 | 排查步骤与解决方案 |
|---|---|---|
| LED完全不亮 | 1. 电源问题 2. I2C连接错误 3. 库未正确安装 | 1. 检查5V和GND连接是否牢固,用万用表测量电压。 2. 确认SCL、SDA线是否接反,是否接触不良。尝试降低I2C频率(如400kHz)测试。 3. 确认 CIRCUITPY/lib目录下存在adafruit_is31fl3741等库文件。 |
| 只有部分LED亮或颜色错乱 | 1. 缓冲区未正确更新 2. 像素坐标设置错误 3. 颜色格式错误 | 1. 确保在修改像素后调用了glasses.show()。2. 矩阵像素坐标是 (x, y),其中0 <= x < 18,0 <= y < 5。环形LED索引是0-23。检查是否越界。3. 颜色应为24位RGB整数,如 0xFF0000表示红色。确保你的颜色值计算正确。 |
| 动画闪烁、撕裂 | 1. 未使用缓冲模式 2. 帧率不稳定或太低 3. I2C通信错误 | 1. 初始化LED_Glasses时务必传入allocate=adafruit_is31fl3741.MUST_BUFFER。2. 使用上述帧率控制方法,并打印FPS监控。优化代码,减少单帧计算量。 3. 检查接线,确保I2C上拉电阻已启用(很多板子内置),或外接4.7K上拉电阻到3.3V。 |
| BMP动画无法加载或显示花屏 | 1. BMP文件格式错误 2. 文件路径或名称错误 3. 内存不足 | 1. 确认BMP是索引颜色(8位或4位),并已复制到CIRCUITPY根目录。用电脑图片查看器确认。2. 检查 EyeLightsAnim初始化时的文件名字符串是否与磁盘上的文件完全一致(包括后缀)。3. 如果BMP文件太大,可能导致内存不足。尝试减小动画尺寸或帧数。检查 gc.mem_free()。 |
| 程序运行一段时间后崩溃重启 | 1. 内存泄漏 2. 电源不稳定 3. 看门狗触发 | 1. 监控内存使用。确保没有在循环内无限创建对象(如列表)。 2. LED全亮时电流很大,可能导致电压跌落。使用质量好的USB线或外部5V电源。 3. 如果启用了看门狗,确保在主循环中定期喂狗。 |
6.4 创意扩展与项目思路
掌握了这三个核心模式后,你可以将它们组合,创造出更丰富的互动体验:
- 传感器驱动:使用眼镜上或外接的传感器(如加速度计、光线传感器、麦克风)来影响动画。例如,根据头部动作(加速度计)让火焰摇摆,根据环境光调整亮度,或者让眼睛随着声音节奏眨动。
- 混合模式:用BMP动画播放一个背景(如星空),同时在上面用代码实时绘制前景(如根据传感器数据变化的指示器)。注意处理好图层叠加顺序。
- 无线控制:通过蓝牙(如Adafruit的Bluefruit LE模块)或Wi-Fi接收来自手机或电脑的指令,实时切换动画模式、颜色或播放特定的BMP序列。
- 省电优化:对于电池供电的项目,在不活动时自动调暗亮度(
glasses.global_current)或进入低帧率的待机动画,可以大幅延长续航。
最后,调试这类项目,一个串口终端(如Mu编辑器、Thonny或VS Code的串口监视器)是你的最佳伙伴。善用print()语句输出变量状态、帧率和内存信息,能帮你快速定位问题所在。硬件上,一把好用的万用表和一套可靠的杜邦线也同样重要。
