行业资讯

基于ESP32与蓝牙的手机可控POV显示器:从原理到实践

发布时间:2026/7/28 15:46:10
基于ESP32与蓝牙的手机可控POV显示器:从原理到实践 1. 项目概述当POV遇上手机遥控几年前我第一次在Maker Faire上看到POV视觉暂留显示器时就被那种在空中“凭空”画出图像和文字的效果迷住了。传统的POV设备比如风扇或摇臂式的往往需要一个独立的控制器调试起来相当麻烦要么得连着电脑要么得按一堆物理按键。我当时就在想能不能让这玩意儿变得更“现代”一点用我们口袋里都有的手机来控制它于是就有了这个“FireBeetle手机可控POV显示器”的项目。简单来说这个项目就是用一块DFRobot的FireBeetle ESP32开发板作为核心驱动一圈LED灯高速旋转。利用人眼的视觉暂留效应这些快速闪动的LED就会在空中形成稳定的图案或文字。最关键的一步是通过蓝牙让手机和FireBeetle连接这样你就能在手机上轻松地编辑想要显示的内容、调整显示效果然后一键发送到设备上让它实时显示出来。它解决了传统POV设备交互不便、内容更新繁琐的核心痛点把编程和控制的门槛降到了最低。无论你是电子爱好者想做个酷炫的桌面摆件还是创客教育老师寻找一个融合了单片机、传感器和无线通信的综合实践项目甚至是极客想给自己做个个性化的信息提示器这个项目都非常适合。它涉及了Arduino编程、蓝牙通信协议、POV显示原理以及简单的机械结构是一个能玩又能学的综合性作品。2. 核心硬件选型与设计思路做一个POV显示器硬件是骨架。选对核心部件项目就成功了一半。我的设计思路很明确核心主控要足够强大且集成无线功能LED灯带要亮度高、响应快供电要稳定结构要简单可靠。2.1 为什么是FireBeetle ESP32主控芯片的选择是重中之重。市面上常见的Arduino Uno/Nano虽然经典但性能有限且需要额外加装蓝牙模块增加了复杂度和成本。而FireBeetle ESP32几乎是这个项目的“天选之子”。首先ESP32双核处理器提供了充沛的计算能力。POV显示本质上是在极短的时间窗口内通常是微秒级精确控制每个LED的亮灭。ESP32的主频高达240MHz远高于传统AVR芯片的16MHz这意味着它有足够的能力来运行复杂的显示逻辑和蓝牙协议栈而不会导致显示闪烁或卡顿。其次集成蓝牙与Wi-Fi。这是实现手机控制的关键。ESP32原生支持蓝牙4.2 BR/EDR和BLE我们可以利用BLE进行低功耗、快速的数据通信。手机APP通过BLE向ESP32发送要显示的图像数据或控制指令ESP32接收后立即在内存中更新显示缓冲区整个过程几乎无感延迟。如果未来想扩展功能比如通过Wi-Fi从网络获取天气、新闻来显示ESP32也能轻松胜任。再者FireBeetle系列板载了锂电池充电管理电路。这意味着你可以直接接上一块3.7V的锂电池供电板子会自动充电并实现稳定的5V/3.3V输出完美解决了移动设备的供电问题。对于需要旋转的POV设备来说无线供电是必须的FireBeetle的这个特性省去了外接充电模块的麻烦。最后DFRobot的FireBeetle板设计紧凑引脚布局清晰周边生态丰富文档和社区支持都很好对于开发者非常友好。注意ESP32的GPIO引脚虽然多但并非所有都适合驱动高速LED。务必查阅手册选择支持高速输出的引脚如GPIO4, GPIO5, GPIO12-19, GPIO21-23, GPIO25-27。避免使用一些有特殊启动功能的引脚如GPIO0, GPIO2, GPIO15等以免造成上电异常。2.2 LED灯珠与灯带的选择LED是显示效果的最终执行者它的选型直接决定了显示质量。WS2812B智能RGB LED灯珠是我的首选也就是大家常说的“NeoPixel”。它最大的优点是“单线控制”。每个WS2812B内部都集成了驱动芯片和RGB芯片只需要一根数据线DATA串联所有灯珠就能实现每个灯珠独立的全彩控制。这极大地简化了硬件连接和程序编写——我们只需要FireBeetle的一个GPIO引脚就能控制几十甚至上百个灯珠。对于POV应用需要特别关注两个参数刷新率WS2812B的通信速率是800Kbps这意味着控制一个灯珠的时间约为1.25µs。对于由16个灯珠组成的圆环刷新一整圈数据大约需要20µs。在高速旋转下例如每秒20转这个速度完全跟得上能保证图像的稳定性。亮度因为POV是在环境光下显示所以需要高亮度的LED。建议选择5V供电、亮度在1000-1500mcd以上的WS2812B灯带。贴片式如5050封装的灯带比圆头灯珠更适合因为它发光角度更广可视范围更大。灯珠的数量取决于你想要的显示分辨率和圆盘尺寸。常见的配置有8、12、16、24个。数量太少显示的文字笔画粗糙数量太多则对旋转速度和数据处理能力要求更高。对于入门和大多数应用场景16个灯珠是一个平衡点既能显示清晰的英文字母和简单图标又不会给控制器带来太大压力。2.3 供电与机械结构设计供电系统如前所述使用一块3.7V、容量在1000mAh以上的锂电池是最佳方案。它重量轻、能量密度高可以直接粘贴在旋转圆盘背面作为配重一举两得。通过FireBeetle的USB口或专用的锂电池接口为其充电。务必在电池和主板之间加入一个电源开关方便控制。机械结构这是保证显示稳定的物理基础。核心是一个由电机驱动的旋转平台。电机推荐使用直流减速电机。普通直流电机转速太快且不易控制舵机行程有限。减速电机扭矩大、转速稳定且可调通过改变电压非常适合带动有一定重量的圆盘。转速通常在60-300 RPM每秒1-5转之间可调这个范围的POV效果比较舒适。连接与固定需要设计一个圆盘可以用亚克力板、3D打印件或甚至一个旧光盘来固定LED灯带和FireBeetle主板。电机轴和圆盘之间需要一个联轴器来传递扭矩。整个结构电机、圆盘需要固定在一个坚固的底座上以防止振动。无线数据传输由于整体在旋转无法使用有线方式上传数据。因此蓝牙是唯一可行的实时通信方案。ESP32的蓝牙天线性能不错但在高速旋转的金属部件附近可能会受干扰。尽量将FireBeetle板固定在旋转中心附近并让天线部分板载PCB天线朝向底座外侧以减少屏蔽。3. 系统工作原理与软件架构理解了硬件我们再来拆解整个系统是如何协同工作的。这就像导演一部电影硬件是演员和舞台软件就是剧本和调度。3.1 POV显示的核心原理视觉暂留人眼的视网膜在光信号消失后视觉印象并不会立即消失这种生理现象被称为“视觉暂留”持续时间大约为1/24秒。POV技术正是利用了这一特性。当一圈LED灯高速旋转时在任何一个固定的空间位置上灯珠只会瞬间经过。如果我们能让灯珠在经过每个特定位置时按照预设的图案精确地亮起那么由于视觉暂留人眼就会将这一系列瞬间亮起的点“连接”起来感知为一幅完整的、静止在空中的图像。关键在于同步。我们必须知道每一个LED灯珠在何时经过了何处。通常我们在旋转轴上安装一个霍尔传感器或光电传感器在底座固定一个磁铁或遮光片。每旋转一圈传感器就会产生一个脉冲信号我们称之为“索引脉冲”或“零位信号”。这个脉冲告诉控制器“新的一圈开始了” 控制器以此脉冲为基准结合已知的电机转速和灯珠的物理位置就能计算出每个灯珠在每一时刻应该显示什么颜色。3.2 软件架构与数据流整个系统的软件可以划分为三大模块手机APP、FireBeetle固件、以及它们之间的蓝牙通信协议。1. 手机APP发送端 它的核心功能是内容编辑和蓝牙通信。你可以用手机输入文字、绘制简单图形或者选择预设的动画。APP需要将这些内容转换成一个二维的位图数据帧。例如对于16个灯珠的圆环我们可以将显示区域想象成一个360x16像素的圆柱面展开图。APP的工作就是生成这个位图然后通过蓝牙BLE协议将其拆分成适合传输的数据包发送给FireBeetle。2. 蓝牙通信协议通道 我们使用BLE的“特征值”Characteristic进行通信。在FireBeetle上我们创建一个BLE服务其中包含两个关键特征值一个“写”特征值用于手机向FireBeetle发送显示数据或控制命令如“显示文本‘Hello’”、“切换动画模式”、“调节亮度”。一个“通知”特征值可选用于FireBeetle向手机反馈状态如“数据接收完成”、“电池电量低”。 数据包格式需要自定义一个简单的协议。例如第一个字节作为指令头0x01代表文本0x02代表图像后续字节为有效载荷。为了可靠传输大图像还需要实现简单的分包和校验机制。3. FireBeetle固件接收与执行端 这是最复杂的部分其程序逻辑是一个典型的中断驱动型状态机。主循环负责处理蓝牙数据接收。当收到手机发来的新数据包时将其解析并更新到内存中的显示缓冲区。这个缓冲区存储了当前要显示的完整帧数据。定时器中断以极高的频率例如每10微秒触发。在中断服务程序中它根据当前时间从索引脉冲开始计算的微秒数和电机转速计算出当前哪个LED灯珠应该处于哪个显示列然后从显示缓冲区中查找对应的颜色值。WS2812B驱动将计算得到的颜色值按照WS2812B的严格时序要求0码和1码的高低电平时间通过操作GPIO引脚输出出去控制LED灯珠亮灭。这个“索引脉冲中断 - 定时器精确计时 - 实时查表输出”的流程是保证POV图像稳定不抖动的关键。任何在中断服务程序中的延迟都可能导致显示错位因此中断服务程序必须极其高效只做最必要的计算和IO操作。4. 从零开始的详细搭建步骤理论说得再多不如动手做一遍。下面我就带你一步步把这个项目搭建起来。我会尽量详述每个环节特别是容易踩坑的地方。4.1 硬件组装与焊接材料清单FireBeetle ESP32 开发板 x1WS2812B RGB LED灯带16灯珠 x13.7V锂电池1000mAh以上 x1直流减速电机6V 60-150RPM x1霍尔传感器如A3144 x1 或 槽型光电传感器 x1小磁铁 x1配合霍尔传感器亚克力圆盘直径约15cm x1电机固定支架、联轴器 x1套导线、开关、电阻220Ω、洞洞板或定制PCB若干步骤制作旋转圆盘在亚克力圆盘边缘等间距地标记出16个点将WS2812B灯带沿着边缘粘贴或焊接固定。确保所有灯珠的数据流向一致灯带上通常有箭头指示。将FireBeetle主板用螺丝或尼龙柱固定在圆盘的中心位置。连接电路LED连接将灯带的VCC接FireBeetle的5V引脚GND接GNDDIN数据输入接FireBeetle的一个GPIO引脚例如GPIO4。强烈建议在数据线DIN上串联一个220Ω的电阻靠近FireBeetle端以保护LED芯片。传感器连接以霍尔传感器A3144为例。其VCC接3.3VGND接GNDOUTPUT引脚接FireBeetle的另一个GPIO例如GPIO5并上拉一个10KΩ电阻到3.3V。将小磁铁固定在底座上对准旋转圆盘下方确保每转一圈传感器都能感应到。电机连接电机通过一个电机驱动模块如L298N、TB6612连接到FireBeetle。将电机的电源6V接驱动模块的电机电源口电机的两根线接驱动模块的输出端。驱动模块的控制引脚IN1, IN2用于控制方向PWM用于控制速度接FireBeetle的GPIO。电池连接锂电池的正负极直接接入FireBeetle板载的锂电池接口通常标有“Bat”和“Bat-”。固定整体结构将电机牢固地安装在底座上通过联轴器与圆盘中心连接。将传感器固定在底座上并调整磁铁位置使其与传感器间隙在1-5mm内确保信号可靠。整理好所有导线可以用扎带固定避免旋转时缠绕。实操心得焊接WS2812B灯带时温度不要过高建议350°C左右停留时间要短否则极易烫坏灯珠内部的IC。可以先在废料上练习。给旋转部分供电和传数据的所有线缆最好从旋转中心电机轴走线或者使用滑环但滑环会增加复杂性和成本。对于低速demo精心整理的软导线也可以工作。4.2 Arduino固件编写与核心代码解析接下来是给FireBeetle“注入灵魂”。我们使用Arduino IDE进行开发。环境配置在Arduino IDE中安装ESP32开发板支持。打开“文件”-“首选项”在“附加开发板管理器网址”中添加https://raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_index.json。然后在“工具”-“开发板”-“开发板管理器”中搜索安装“ESP32”。安装必要库FastLED用于高效驱动WS2812B。这是必装库比Adafruit_NeoPixel性能更高。BLEESP32已内置BLE库通常无需额外安装。核心代码结构#include FastLED.h #include BLEDevice.h #include BLEUtils.h #include BLEServer.h #define LED_PIN 4 #define NUM_LEDS 16 #define SENSOR_PIN 5 CRGB leds[NUM_LEDS]; volatile bool newFrameReady false; // 新数据标志 volatile unsigned long rotationStartTime 0; // 每圈开始时间 float rpm 120.0; // 电机转速假设120转/分 float usPerDegree; // 每度所需微秒数 int displayBuffer[360][NUM_LEDS]; // 显示缓冲区360度 x 16灯珠 // BLE相关定义 BLECharacteristic *pTxCharacteristic; BLECharacteristic *pRxCharacteristic; #define SERVICE_UUID 4fafc201-1fb5-459e-8fcc-c5c9c331914b #define CHARACTERISTIC_UUID_RX beb5483e-36e1-4688-b7f5-ea07361b26a8 #define CHARACTERISTIC_UUID_TX beb5483e-36e1-4688-b7f5-ea07361b26a9 // 索引脉冲中断服务程序 void IRAM_ATTR sensorISR() { rotationStartTime micros(); // 记录一圈开始的精确时间 } void setup() { Serial.begin(115200); FastLED.addLedsWS2812B, LED_PIN, GRB(leds, NUM_LEDS); FastLED.setBrightness(50); pinMode(SENSOR_PIN, INPUT_PULLUP); attachInterrupt(digitalPinToInterrupt(SENSOR_PIN), sensorISR, FALLING); // 下降沿触发 // 计算时间参数 usPerDegree (60.0 * 1e6) / (rpm * 360.0); // 每度微秒数 (60秒*10^6微秒) / (转速*360度) // 初始化显示缓冲区为全黑 memset(displayBuffer, 0, sizeof(displayBuffer)); // 初始化BLE initBLE(); // 初始化定时器中断用于精确控制LED刷新 hw_timer_t *timer timerBegin(0, 80, true); // 使用定时器0预分频80APB时钟80MHz分频后1MHz即1微秒计数一次 timerAttachInterrupt(timer, timerISR, true); timerAlarmWrite(timer, 10, true); // 每10微秒触发一次中断 timerAlarmEnable(timer); } void loop() { // 主循环主要处理蓝牙数据接收和命令解析 // 当收到手机发来的新图像数据时解析并更新 displayBuffer // 以及处理其他非实时性任务 if (newFrameReady) { // 将接收到的数据写入显示缓冲区 // 这里需要根据你的蓝牙数据协议来解析 newFrameReady false; } // 其他逻辑如读取电池电压等 } // 定时器中断服务程序核心显示引擎 void IRAM_ATTR timerISR() { if (rotationStartTime 0) return; // 尚未收到索引脉冲不显示 unsigned long currentTime micros(); unsigned long timeSinceStart currentTime - rotationStartTime; // 计算当前角度 (0-359度) int currentAngle (int)(timeSinceStart / usPerDegree) % 360; // 根据当前角度从显示缓冲区中取出这一列16个像素的数据 for (int ledIndex 0; ledIndex NUM_LEDS; ledIndex) { int colorValue displayBuffer[currentAngle][ledIndex]; // 将颜色值转换为CRGB格式并设置到LED数组这里需要根据你的缓冲区格式调整 // leds[ledIndex] CRGB(...); } // 批量发送数据到WS2812B // 注意FastLED.show() 函数耗时较长不能在中断中调用 // 我们需要一种更底层的方式。这里仅示意实际需用并行输出或DMA。 // 更常见的做法是在中断里只准备数据在主循环中调用show()。 // 但对于高速POV这可能造成拖影。高级做法是使用ESP32的RMT外设或I2S来驱动WS2812B这可以实现真正的硬件级实时输出。 } void initBLE() { BLEDevice::init(FireBeetle_POV_Display); BLEServer *pServer BLEDevice::createServer(); BLEService *pService pServer-createService(SERVICE_UUID); pRxCharacteristic pService-createCharacteristic( CHARACTERISTIC_UUID_RX, BLECharacteristic::PROPERTY_WRITE ); pTxCharacteristic pService-createCharacteristic( CHARACTERISTIC_UUID_TX, BLECharacteristic::PROPERTY_NOTIFY ); pRxCharacteristic-setCallbacks(new MyCallbacks()); // 设置回调处理手机发来的数据 pService-start(); BLEAdvertising *pAdvertising pServer-getAdvertising(); pAdvertising-start(); } class MyCallbacks: public BLECharacteristicCallbacks { void onWrite(BLECharacteristic *pCharacteristic) { std::string rxValue pCharacteristic-getValue(); // 解析rxValue更新显示缓冲区 newFrameReady true; } };代码关键点解析中断的使用sensorISR用于捕获每圈开始的时刻必须声明为IRAM_ATTR并尽可能简短。定时器中断这是显示同步的心脏。我们设置了一个高精度定时器每10微秒在中断中根据当前时间计算角度并查找显示数据。显示缓冲区displayBuffer[360][16]是一个二维数组模拟了360度方向上每一列对应一个角度的16个LED的颜色。手机发来的图像需要被转换并填充到这个缓冲区。BLE回调在MyCallbacks::onWrite中接收手机数据。实际项目中你需要在这里实现一个完整的协议解析器处理文本、图像、命令等不同数据类型。驱动WS2812B的挑战如注释所述在高速中断中调用FastLED.show()是不现实的。对于要求高的项目必须使用ESP32的RMT远程控制外设或I2S来生成WS2812B的精确时序信号这需要更底层的编程。FastLED库本身支持RMT驱动你可以在初始化时指定FastLED.addLedsWS2812B, LED_PIN, GRB(leds, NUM_LEDS).setCorrection(TypicalLEDStrip);并确保在setup()中正确配置了RMT通道。这样FastLED.show()就能非阻塞地通过硬件发送数据。4.3 手机APP端简易实现方案对于手机端我们不需要开发一个功能完整的APP可以先用现成的工具快速验证。这里提供两个思路方案一使用通用蓝牙调试APP在手机应用商店搜索“蓝牙串口”或“BLE调试器”这类APP很多。它们可以扫描并连接我们的FireBeetle设备然后以十六进制或文本格式发送数据。我们可以在Arduino代码中预先定义几种显示模式比如固定图案A、B、C然后手机发送简单的指令如发送“A”来切换。这是最快验证蓝牙通信和基本控制功能的方法。方案二使用MIT App Inventor或Blynk快速搭建如果你想有一个更友好的界面可以用图形化编程工具。MIT App Inventor完全在线、图形化编程适合初学者。你可以拖拽按钮、输入框、列表等组件设计一个界面然后编写逻辑当按钮点击时通过蓝牙组件向FireBeetle发送特定的字符串命令。Blynk一个非常流行的IoT平台它提供了现成的手机APP界面控件按钮、滑块、数据展示等你只需要在Arduino代码中集成Blynk库并定义虚拟引脚Virtual Pins来关联控件和数据。Blynk负责通信和界面你只需关注设备端逻辑。这对于实现亮度调节、模式切换等交互非常方便。自定义数据协议示例 假设我们想从手机发送“HELLO”这个单词来显示。在手机APP端将“HELLO”转换为5x7的点阵字模每个字母5列宽我们取7行高。对于16灯珠的环我们可能只显示一行字母那么“HELLO”总共需要5*525列数据。定义协议数据包第一个字节为命令字0x01代表文本第二个字节为文本长度0x05后面跟25个字节每个字节代表一列16个LED的亮灭可以用位表示1亮0灭但这里简化假设每个字节只是一个灰度值。手机APP按此格式组包并发送。FireBeetle在onWrite回调中解析这个包将25列数据填入displayBuffer的对应位置比如从角度0开始填充。5. 调试、优化与问题排查实录项目搭建和编码完成通电测试往往不会一帆风顺。下面是我在多次实践中总结的常见问题及其解决方法。5.1 图像闪烁、抖动或错位这是POV项目中最常见的问题根源几乎都是同步时序不准。症状图像不稳定上下跳动或左右漂移。排查与解决检查索引脉冲这是整个显示的“心跳”。首先用示波器或者简单的Arduino串口打印查看传感器引脚在旋转时是否产生了干净、稳定的方波脉冲。确保磁铁和传感器距离合适信号没有抖动。可以在传感器输出端加一个施密特触发器如74HC14或一个小的RC滤波电路来整形信号。校准转速代码中rpm变量的值必须尽可能接近电机实际转速。一个简单的校准方法是让设备旋转用手机慢动作视频拍摄数出10秒钟转了多少圈计算实际RPM然后更新到代码中。更精确的方法是在代码中测量连续两个索引脉冲的时间间隔来计算实时转速。中断响应时间确保索引脉冲中断服务程序sensorISR极其简短只做记录时间这一件事。避免在中断中做复杂计算或调用millis()/micros()虽然这里用了但它是原子操作相对安全。定时器中断频率usPerDegree的计算和定时器中断的频率决定了角度计算的精度。如果定时器中断间隔如10µs相对于每度所需时间例如在120RPM下每度约139µs来说太粗糙就会产生量化误差导致图像轻微抖动。可以尝试提高定时器中断频率或者采用在索引脉冲中断中重置一个硬件定时器/计数器在主循环中查询该计数器值的方式来获取更精确的当前角度。5.2 LED显示颜色异常或部分不亮症状LED颜色不对比如显示白色时偏红或者整条灯带从某个灯珠后都不亮了。排查与解决电源问题这是首要怀疑对象。WS2812B在全白亮起时电流巨大每个灯珠可达60mA。16个灯珠就是960mA。确保你的锂电池能提供持续的1A以上电流并且连接线足够粗减少压降。在FireBeetle的5V输出和灯带VCC之间最好并接一个470-1000µF的电解电容以缓冲瞬间大电流需求。数据信号问题数据线DIN上的信号质量至关重要。长导线会衰减和畸变信号。务必在FireBeetle输出端串联一个220-470Ω的电阻。如果灯带较长可以在第一个灯珠的DOUT和第二个灯珠的DIN之间也加一个小电阻如100Ω。接地问题确保FireBeetle的GND和灯带的GND是一点接地且连接良好。糟糕的接地回路会引起信号干扰。灯珠损坏焊接时过热可能损坏WS2812B。如果某个灯珠后的所有灯珠都不工作很可能是这个灯珠的数据通路坏了。可以尝试跳过它将数据线直接焊到下一个灯珠的DIN上测试。5.3 蓝牙连接不稳定或无法连接症状手机搜不到设备或者连接频繁断开。排查与解决供电干扰电机是巨大的噪声源。在电机电源线上并联一个0.1µF的瓷片电容和一个100µF的电解电容尽可能靠近电机引脚以滤除电刷火花产生的高频噪声。也可以尝试用独立的电池给电机供电与逻辑电路电源分离。天线位置尽量让FireBeetle的板载天线部分远离金属底座和电机。如果放在金属盒内信号会极大衰减。代码问题检查BLE设备名称是否唯一避免冲突。确保在setup()中初始化BLE后没有阻塞操作。如果连接后很快断开可能是ESP32进入了深度睡眠检查你的电源管理设置。手机端问题尝试重启手机蓝牙或者换一部手机测试。某些手机品牌对BLE连接有更严格的超时限制。5.4 图像模糊或有拖影症状显示的字符边缘不锐利有重影。排查与解决LED响应时间WS2812B的响应时间足够快通常不是问题。拖影更可能是视觉暂留时间与人眼跟踪造成的。如果旋转速度太慢人眼会不自觉地跟踪移动的灯珠导致图像模糊。适当提高电机转速如到180-250 RPM可以改善。亮度与背景光在明亮环境下LED亮度不足会导致图像难以看清。提高FastLED.setBrightness()的值但注意电流会随之增大。最好在较暗的环境下观看POV效果。软件消影在显示一列数据后立即将所有LED设置为黑色CRGB::Black等待极短时间如几微秒再显示下一列。这可以确保LED在非目标位置时是熄灭的减少拖影。但这需要非常精确的时间控制对代码时序要求极高。最后一个小技巧在正式组装前务必先在一个静止的状态下测试所有功能用杜邦线连接好所有部件不接电机用USB供电。测试每个LED是否能正确显示颜色测试蓝牙是否能连接并接收数据。这能排除大部分电气和代码逻辑问题避免旋转起来后问题复杂化。