1. 项目概述当骰子遇见光一个桌面上的微型互动装置“Dice of Leds”直译过来就是“LED骰子”。乍一听你可能会觉得这不就是个带灯的骰子吗市面上确实有那种按一下亮个随机数的电子骰子。但今天我想聊的远不止于此。这是一个融合了硬件交互、随机逻辑与视觉反馈的创意电子项目它更像是一个可以放在你桌面上兼具实用性与观赏性的微型互动装置。核心玩法是你“摇动”它通过传感器模拟装置内部的LED灯阵会以某种炫酷的动画形式最终“掷”出一个1到6的点数。它解决的不仅仅是“随机数生成”这个基础需求更是为桌面游戏、决策时刻甚至是一个人的发呆时光增添一份有仪式感的、看得见的乐趣。这个项目非常适合对Arduino、树莓派Pico这类微控制器感兴趣并且希望从点亮LED进阶到实现完整互动逻辑的爱好者。无论你是想做一个独特的桌游配件一个送给朋友的Geek礼物还是一个锻炼自己嵌入式开发与电路设计能力的练手项目Dice of Leds都能提供一条清晰的学习路径。整个过程会涉及传感器数据采集、去抖动算法、随机数生成、状态机编程、LED动画驱动以及电源管理等多个核心环节。接下来我将以一个资深硬件折腾者的角度带你从设计思路到焊接收尾完整地拆解这个迷人的小项目。2. 核心设计思路与方案选型为什么是“摇动”而不是“按钮”做一个电子骰子最直接的想法可能是用一个按键按一下LED显示一个随机数。这当然可以但少了点“掷骰子”的灵魂。我们的目标是模拟真实的物理投掷过程因此“摇动”作为触发方式是更优解。这引出了我们第一个核心设计决策如何检测“摇动”动作2.1 运动传感器的选择加速度计 vs. 陀螺仪 vs. 六轴IMU检测动作我们首先想到的是运动传感器。常见的有三种加速度计测量线性加速度也就是“速度变化快慢”。静止时能测出重力方向晃动时能测出各个方向的加速度值。它非常适合检测“突然的晃动”或“敲击”。陀螺仪测量角速度也就是“旋转的快慢”。它能告诉你设备正在朝哪个方向转动但对于单纯的线性移动比如平移晃动不敏感。六轴IMU通常是加速度计和陀螺仪的结合体如MPU6050能提供更完整的运动数据。对于“掷骰子”这个动作它既有平移的晃动也可能有翻转。但我们的核心需求是检测一个明确的“启动信号”而不是精确复现骰子的运动轨迹。因此一个三轴加速度计例如ADXL345、MPU6050的加速度计部分就完全够用且成本更低数据处理更简单。我们通过监测加速度的矢量合成值即X, Y, Z轴加速度的平方和再开方是否超过某个阈值来判断是否发生了有效晃动。实操心得新手常犯的一个错误是直接读取单个轴的加速度值来判断晃动这会导致装置在某些特定角度下不灵敏。一定要使用三轴矢量和这样无论你怎么拿设备只要动了数值就有变化。2.2 主控芯片选型Arduino还是树莓派Pico这是另一个关键选择。两者都能胜任。Arduino Uno/Nano生态成熟资料极多入门友好。但性能有限如果后期想加入非常复杂的LED动画比如全彩流水、渐变可能会吃力。模拟输入引脚足够连接加速度计。树莓派Pico (RP2040)双核ARM Cortex-M0主频133MHz性能强劲价格甚至更便宜。原生支持PIO可编程IO驱动LED灯带如WS2812B有奇效几乎不占用CPU资源。但相对Arduino社区生态稍新一些库需要自己摸索。我的选择是树莓派Pico。原因有三第一性能冗余意味着我可以设计更流畅、更复杂的掷骰子动画未来扩展性强第二PIO对于驱动LED矩阵是“降维打击”让主核专心处理逻辑第三其小巧的尺寸更适合嵌入到最终成品中。2.3 显示方案LED点阵 vs. LED灯组如何显示1到6的点数传统骰子的点数布局是固定的。方案A7个独立LED按照骰子面孔布局放置7个LED对应6个点加中心点。成本低控制简单但显示效果单一无法做动画。方案B8x8 LED点阵可以显示点数甚至能在掷骰子过程中显示滚动动画、数字特效等。视觉效果炫酷可玩性高。方案CWS2812B LED灯环或矩阵例如一个16位的灯环或者一个小型8x8矩阵。每个LED可独立控制RGB颜色实现流光溢彩的动画效果是视觉表现力的天花板。为了最大化项目的趣味性和学习价值我推荐方案C使用一个共16颗WS2812B LED的灯环。16颗灯足以清晰地排布出1-6的点数图案我们可以用12颗灯围成外圈4颗灯在内圈构成骰子面富余的LED还可以用来做旋转、闪烁等过渡动画。2.4 供电与结构设计供电选择3.7V的锂电池如14500或小型锂聚合物电池配合充电管理模块如TP4056实现无线便携。结构上需要一个小盒子来容纳所有电路。可以使用3D打印外壳这是最定制化的方案也可以找一个现成的透明塑料盒进行改装。整体方案框图如下 树莓派Pico作为大脑通过I2C接口读取MPU6050加速度计的数据判断晃动。一旦检测到有效晃动Pico的一个PIO状态机开始驱动WS2812B灯环播放一段随机长度的“滚动动画”动画结束后根据随机数结果点亮对应的骰子点数图案。整个系统由锂电池供电通过一个拨动开关控制总电源。3. 硬件搭建与核心电路解析理论说完我们开始动手。这部分会详细列出物料清单并解释关键电路连接背后的原因。3.1 物料清单BOM元件名称规格/型号数量说明主控芯片树莓派Pico1核心处理器运动传感器MPU6050 (六轴IMU)1用于检测晃动LED灯环WS2812B, 16位1显示单元电池3.7V 锂聚合物电池 (500mAh)1供电充电模块TP4056 带保护板1充电及过放保护升压模块MT3608 (可选)1如电池电压驱动LED亮度不足时使用开关小型拨动开关1电源总开关电阻10kΩ, 470Ω各1I2C上拉、LED数据线限流电容100nF (104)2-3电源滤波杜邦线公对公、公对母若干连接用外壳3D打印或塑料盒1容纳所有部件3.2 电路连接详解与原理连接电路不是简单的“连上线就行”每一条线都有它的使命。1. 树莓派Pico与MPU6050的连接 (I2C通信)Pico VBUS (5V) - MPU6050 VCC为传感器供电。MPU6050工作电压为2.375V-3.46V但模块通常自带LDO稳压输入5V没问题。Pico GND - MPU6050 GND共地所有电路的基准零电位必须一致。Pico GP4 (I2C0 SDA) - MPU6050 SDA数据线。Pico GP5 (I2C0 SCL) - MPU6050 SCL时钟线。在SDA和SCL线上各接一个10kΩ电阻上拉到3.3V这是I2C总线标准要求。上拉电阻确保总线在空闲时为高电平提供驱动能力。Pico内部有弱上拉但为了长距离或更稳定通信外加上拉电阻是良好实践。注意事项务必确认MPU6050模块的IO电平。如果是5V电平的模块需要在数据线SDA, SCL上串联一个470Ω左右的电阻或者使用电平转换模块以防止5V信号损坏Pico的3.3V GPIO。现在市面上很多模块已经是3.3V兼容的了购买时需留意。2. 树莓派Pico与WS2812B灯环的连接Pico VBUS (5V) - 灯环 VCCWS2812B最佳工作电压是5V。在5V下LED颜色最正亮度最高。Pico GND - 灯环 GND再次强调共地至关重要LED的电流很大如果地线接触不良或回路阻抗大会导致信号紊乱LED乱闪。Pico GP0 (或任意IO) - 灯环 DIN数据输入。WS2812B使用单线归零码协议对时序要求极其严格。这就是为什么我们要用Pico的PIO来驱动它可以生成精确定时的信号不受主程序循环干扰。在数据线GP0和灯环DIN之间串联一个470Ω电阻这个电阻起到限流和缓冲作用可以削弱信号振铃提高通信稳定性是保护LED芯片和GPIO口的好习惯。在灯环的电源输入端VCC和GND之间并联一个100μF以上的电解电容WS2812B在全部点亮、尤其是显示白色时瞬时电流可能非常大16颗全白约1A。这个电容作为“能量水池”可以吸收瞬间的电流需求防止电源电压被拉低导致Pico重启或LED颜色异常。3. 供电系统连接锂电池正极 - TP4056模块 BAT锂电池负极 - TP4056模块 BAT-TP4056模块 OUT - 拨动开关一端拨动开关另一端 - 整个系统的VCC主线即Pico的VSYS MPU6050和灯环的VCC都从这条主线取电TP4056模块 OUT- - 整个系统的GND主线在系统VCC和GND主线之间靠近Pico处并联一个100nF的陶瓷电容用于滤除高频噪声保证主控芯片稳定运行。如果测试发现电池直接输出约3.7V-4.2V驱动灯环亮度不够可以在电池和系统VCC之间加入一个MT3608升压模块将其稳定输出设置在5V。但要注意升压模块有转换效率损耗会缩短续航。4. 软件逻辑与代码实现详解硬件是躯体软件是灵魂。Dice of Leds的软件核心是一个状态机它清晰地定义了装置在不同行为下的状态转换。4.1 程序状态机设计我们定义四个主要状态IDLE (空闲状态)装置静止LED显示一个温和的待机呼吸灯效果。程序持续读取MPU6050数据监测晃动。SHAKING (摇动检测状态)当加速度矢量超过阈值进入此状态。此时LED可以开始闪烁或变化给予用户“已识别动作”的反馈。为了防止一次晃动触发多次需要加入一个“去抖动”计时例如持续监测晃动超过200毫秒才认为有效。ROLLING (滚动动画状态)确认有效晃动后进入此状态。LED灯环播放一段随机时长的“滚动”动画例如LED快速顺时针/逆时针旋转。同时程序开始生成一个最终的随机数1-6。动画时长可以是0.5秒到2秒之间的一个随机值增加不确定性。RESULT (显示结果状态)滚动动画结束根据生成的随机数点亮灯环上对应的骰子点数图案并保持数秒。随后自动跳转回IDLE状态。4.2 关键代码模块解析我们使用MicroPython进行开发因为它对树莓派Pico支持好且代码更易读。这里不会贴出全部代码但会详解核心片段。1. MPU6050数据读取与晃动检测import machine, time import math # 初始化I2C i2c machine.I2C(0, sclmachine.Pin(5), sdamachine.Pin(4), freq400000) # ... (MPU6050初始化代码设置量程、滤波器等) def read_accel(): # 读取加速度计原始数据X, Y, Z轴 data i2c.readfrom_mem(MPU6050_ADDR, ACCEL_XOUT_H, 6) ax (data[0]8 | data[1]) ay (data[2]8 | data[3]) az (data[4]8 | data[5]) # 根据量程例如±2g转换为g值 accel_scale 16384.0 # for ±2g ax_g ax / accel_scale ay_g ay / accel_scale az_g az / accel_scale return ax_g, ay_g, az_g def get_accel_magnitude(): ax, ay, az read_accel() # 计算矢量幅度静止时约为1g重力 magnitude math.sqrt(ax*ax ay*ay az*az) return magnitude # 晃动检测逻辑 shake_threshold 1.5 # 幅度超过1.5g认为在晃动 idle_magnitude 1.0 # 静止时的参考值 debounce_time_ms 200 last_shake_time 0 def check_shake(): global last_shake_time mag get_accel_magnitude() current_time time.ticks_ms() if abs(mag - idle_magnitude) shake_threshold: if time.ticks_diff(current_time, last_shake_time) debounce_time_ms: last_shake_time current_time return True return False关键点shake_threshold需要根据实际安装位置和晃动力度进行校准。太敏感会误触发太迟钝则用户体验不好。可以通过串口打印出mag值观察静止和晃动时的数值来调整。2. WS2812B驱动与动画使用MicroPython的neopixel库非常方便但为了极致效果和低CPU占用我们可以用PIO汇编。不过对于16颗灯标准库性能足够。import neopixel from machine import Pin # 初始化灯环 num_leds 16 np neopixel.NeoPixel(Pin(0), num_leds) # 定义骰子1-6的点阵映射使用灯环索引 dice_patterns { 1: [7, 8], # 中心两个灯 2: [1, 14], # 对角 3: [1, 7, 14], # 对角加中心 4: [0, 5, 10, 15], # 四个角 5: [0, 5, 7, 10, 15], # 四角加中心 6: [0, 2, 5, 10, 13, 15] # 两排 } def show_dice_number(num, color(30, 30, 30)): # 默认白色亮度较低 np.fill((0,0,0)) # 清空 for led_index in dice_patterns[num]: np[led_index] color np.write() def rolling_animation(duration_ms): # 一个简单的旋转动画示例 start_time time.ticks_ms() while time.ticks_diff(time.ticks_ms(), start_time) duration_ms: for i in range(num_leds): np.fill((0,0,0)) np[i] (50, 0, 0) # 红色光点 np[(i8)%num_leds] (0, 50, 0) # 绿色光点 np.write() time.sleep_ms(30) # 控制旋转速度动画优化上面的rolling_animation是一个基础示例。你可以设计更复杂的动画比如彩虹渐变旋转、波浪效果等。关键是控制好帧率time.sleep_ms的值避免卡顿。3. 主循环与状态机实现import random STATE_IDLE 0 STATE_SHAKING 1 STATE_ROLLING 2 STATE_RESULT 3 current_state STATE_IDLE roll_result 0 animation_start_time 0 rolling_duration 0 def idle_state(): # 呼吸灯效果 # ... 呼吸灯代码 ... if check_shake(): return STATE_SHAKING return STATE_IDLE def shaking_state(): # 快速闪烁提示 for _ in range(3): np.fill((20,20,0)) # 黄色 np.write() time.sleep_ms(100) np.fill((0,0,0)) np.write() time.sleep_ms(100) # 确定进入滚动 return STATE_ROLLING def rolling_state(): global roll_result, rolling_duration, animation_start_time # 首次进入初始化结果和动画时间 if rolling_duration 0: roll_result random.randint(1, 6) rolling_duration random.randint(500, 2000) # 随机0.5到2秒 animation_start_time time.ticks_ms() # 播放动画 rolling_animation(50) # 播放一小段动画 # 检查是否超时 if time.ticks_diff(time.ticks_ms(), animation_start_time) rolling_duration: rolling_duration 0 # 重置 return STATE_RESULT return STATE_ROLLING def result_state(): show_dice_number(roll_result, (40, 40, 40)) # 显示结果稍亮 time.sleep(3) # 保持显示3秒 return STATE_IDLE # 主循环 while True: if current_state STATE_IDLE: current_state idle_state() elif current_state STATE_SHAKING: current_state shaking_state() elif current_state STATE_ROLLING: current_state rolling_state() elif current_state STATE_RESULT: current_state result_state() # 可以加入一个短延时降低CPU占用 time.sleep_ms(10)5. 组装调试与性能优化当所有代码烧录完毕硬件连接无误后就可以进行组装和最终调试了。5.1 结构组装要点固定与绝缘使用热熔胶或双面胶将Pico、MPU6050、TP4056模块牢固地固定在底壳内。确保所有焊点或杜邦线接头不会相互接触或与金属外壳短路。可以在PCB板背面贴一层绝缘胶带。传感器方向MPU6050的安装方向会影响加速度计读数。在代码中你需要定义哪个轴对应哪个方向。最简单的办法是让传感器的一个轴比如Z轴垂直向上然后在静止状态下校准idle_magnitude应该接近1g。如果安装方向是随意的你可能需要在代码里做一个坐标变换矩阵但这对于晃动检测来说不是必须的只要阈值设置合理即可。LED灯环布局将灯环用胶水固定在面盖内侧。确保其位置居中并且数据流入方向DIN正确连接到Pico。如果使用透明或磨砂面盖显示效果会更好。开关与充电口在壳体侧面开孔安装拨动开关和Micro USB充电口TP4056模块的接口方便操作。5.2 系统调试与校准电源测试先不接LED灯环上电测试Pico和MPU6050是否正常工作可通过串口打印信息。然后接上灯环测试LED是否能被控制。传感器校准将装置静置在水平面上通过串口观察get_accel_magnitude()的返回值。理论上应该接近1.01g。记录这个值作为idle_magnitude。然后用力晃动观察最大值将shake_threshold设置为一个介于静止值和最大晃动值之间的数比如1.3到1.8。动画流畅度观察滚动动画是否有卡顿。如果卡顿尝试减少单帧动画的LED操作数量或者增加time.sleep_ms的间隔。对于更复杂的动画可以考虑使用定时器中断来刷新LED解放主循环。功耗测试在IDLE状态下测量整机电流。一个优化良好的系统待机电流应小于20mA。如果过高检查是否有模块未进入睡眠如MPU6050可配置为低功耗模式或者LED在待机时是否完全熄灭np.fill((0,0,0))并np.write()。5.3 常见问题与排查技巧实录即使按照步骤操作你也可能会遇到一些“坑”。下面是我在多次制作中总结的常见问题速查表现象可能原因排查与解决思路上电后无任何反应1. 电池没电或接反。2. 电源开关损坏或未打开。3. Pico焊接或连接不良。1. 用万用表测电池电压检查正负极。2. 短接开关两端看是否上电。3. 检查Pico的VSYS/VBUS是否有5V电压。LED灯环乱闪或不亮1. 电源功率不足电池电量低或线阻大。2. 地线接触不良最常见。3. 数据线连接错误或接触不良。4. 未接滤波电容。1. 单独用5V USB给灯环供电测试。2.重点检查确保Pico的GND和灯环的GND是同一条粗线或同一个焊盘回路阻抗要小。3. 检查数据线是否接到DIN以及Pico的GPIO号是否正确。4. 在灯环电源端并联一个100-470μF的电解电容。晃动检测不灵敏或误触发1. 加速度计阈值shake_threshold设置不当。2. 传感器安装不牢产生振动。3. 去抖动时间设置太短或太长。1. 通过串口打印实时加速度幅度观察静止和晃动时的数值重新调整阈值。2. 牢固固定传感器模块。3. 调整debounce_time_ms通常200ms是个不错的起点。显示结果后无法返回空闲状态1. 状态机逻辑错误RESULT状态后未正确跳转。2. 在RESULT状态的time.sleep期间传感器读数积压导致误判。1. 仔细检查主循环中result_state()函数的返回值确保它返回STATE_IDLE。2. 避免在状态函数中使用长延时阻塞。可以考虑用非阻塞的时间判断方式重构状态机。电池续航极短1. LED亮度设置过高。2. 待机时未关闭LED或降低传感器功耗。3. 存在短路或元件漏电。1. 将LED显示亮度调低30-50的RGB值在暗环境下已足够清晰。2. 在IDLE状态确保LED全灭。研究MPU6050的低功耗模式并配置。3. 断电后用手触摸各芯片是否有异常发热。用万用表测待机电流。6. 进阶玩法与扩展思路一个基础版的Dice of Leds完成后你可以根据自己的兴趣进行无限扩展多种动画模式不止随机点数可以增加“抛硬币”显示两种图案、“抽签”显示1-20等模式通过双击或长按开关切换。声音反馈加入一个微型无源蜂鸣器在掷骰子、显示结果时发出不同的音效体验更沉浸。无线连接为Pico W版本添加Wi-Fi将掷出的结果同步到手机App或电脑上记录用于线上桌游。姿态感应利用MPU6050的陀螺仪实现更复杂的交互。比如将骰子“掷”向桌面检测到特定方向的加速度和撞击才触发结果模拟真实物理。外观艺术化使用半透明树脂进行浇筑将电路完全封装在内做成一个真正的“魔法骰子”或者设计成其他创意造型。这个项目的魅力在于它从一个简单的想法出发贯穿了电路设计、嵌入式编程、传感器应用、结构设计等多个硬件开发的核心技能点。每一个环节的调试和优化都是宝贵的经验。我最享受的时刻不是它第一次成功亮起而是当我把成品递给朋友他下意识地摇动它看到流光溢彩的动画和随机亮起的点数时脸上露出的那种惊喜的表情。那一刻你会觉得所有的调试和折腾都值了。希望这份详细的拆解能帮你打造出属于你自己的、独一无二的“Dice of Leds”。