走心攻略:用“美人鱼手链”当礼物之前,先看这篇技术拆解与 DIY 方案
每到节日、纪念日或者某个特别的日子,“送什么礼物”就成了很多人的头号难题。市面上的礼品选择非常多,但真正能让人记住的礼物,往往不是最贵的,而是那种花了心思、带入了个人表达的东西。最近“美人鱼手链”这类概念很火,比如 Davena 的款式,主打精致颜值和浪漫寓意。但作为技术公众号的读者,我建议你先别急着下单,因为还有一个更值得考虑的选项:运用嵌入式硬件、蓝牙设计和 3D 建模,亲手做一块独一无二的“美人鱼主题智能手链”。
这篇文章不是劝退你买礼物,而是给你一条更有差异化的思路。我们会从电子 DIY 的角度出发,把“美人鱼手链表”拆成一个完完整整的硬件项目:元器件选型、核心电路设计、固件代码结构、蓝牙交互、屏幕动画、防水续航,再到测试验证和外观组装。哪怕你完全没接触过嵌入式开发,也可以照着文章一步步跑通,最终拿出一个能在朋友面前展示成果的实体作品。
1. 为什么“手链礼物”值得做成一个技术项目
如果只是把成品买回来送给对方,礼物本身是完整的,但过程只发生在下单页。而如果是自己动手做一块智能手链,整个过程会变成一趟完整的工程探索:你会在硬件选型时琢磨怎么平衡外观和体积,会在写代码时反复调节动画和交互逻辑,会在测试中发现蓝牙连接不稳定然后回头检查天线布局。
这些努力最后会直接体现在成品上。一块手工打造、具备自定义动画、闪烁呼吸灯甚至计步功能的“美人鱼手链”,和货架上批量化生产的饰品相比,最大的区别是它带有真实的技术信息。对于程序员、工程师这类身份的人来说,这种礼物本身就带着职业痕迹和个人表达。
从技术学习的角度看,这个项目恰好覆盖了嵌入式开发里最典型的几条路线:单片机最小系统设计、I2C/SPI 外设驱动、低功耗蓝牙通信、锂电池充放电管理、结构外壳设计。换句话说,它不是一个为了有趣而硬造出来的玩具,而是一套可以迁移到智能手表、智能戒指、穿戴式医疗设备等真实产品中的技术栈。
再说得更直白一点:当你自己做过一块这样的手链,再去观察市面上那些智能手环、儿童手表、运动腕带,你看到的不再是“黑盒子”,而是一块 PCB、一颗芯片、一串固件代码,以及一个完整的硬件生命周期。
2. 核心概念与整体技术方案
2.1 什么是“美人鱼手链表”的本质
礼物消费场景里,“美人鱼手链表”指的是带美人鱼元素设计、颜值优先的腕带饰品。但从硬件技术角度看,一个真正可落地的“智能手链表”应当包含以下模块:
- 主控芯片,负责运行逻辑和驱动外设
- 显示屏,用来展示时间、动画或自定义文字
- 蓝牙模块,用于和手机 App 通信
- 电池管理系统,负责充电、放电和保护
- 外壳结构,用于保护内部元器件并提供佩戴形态
这其实就是一个功能裁剪到极致的智能手表。
2.2 技术路线选择
在设计智能手链表时,存在两种主流切入方式:一是使用现成的蓝牙 SoC,二是用“MCU + 独立蓝牙芯片”组合。下表对比了两者的差异:
| 对比维度 | 单芯片蓝牙 SoC | MCU + 蓝牙芯片 |
|---|---|---|
| 典型芯片 | nRF52832、ESP32-C3、DA14531 | STM32 + CC2541 等 |
| 开发难度 | 中等,环境配置稍多 | 较低,MCU 开发基础可复用 |
| BOM 成本 | 较低 | 中等 |
| 体积控制 | 更容易做小 | 印制板面积更大 |
| 学习收益 | 更高,理解完整蓝牙协议栈 | 偏传统 MCU 外设开发 |
对于第一次做可穿戴设备的开发者,我更推荐先选“MCU + 蓝牙模块”路线,比如常见的 STM32 最小系统板,外加一个透传蓝牙模块。这套组合的资料非常多,出错时容易排查,而且 MCU 侧的逻辑和普通单片机开发几乎没有区别。
2.3 显示方案的选型
美人鱼主题离不开视觉呈现。可选方案主要有三种:
- OLED 显示屏:显示效果清晰,对比度高,适合显示图案和文字,功耗适中
- 电子墨水屏:功耗极低,但刷新率低,不适合动态动画,且颜色选择受限
- LED 灯珠矩阵:适合做呼吸灯和闪烁效果,但无法表达复杂图形
从“美人鱼”这个主题方发,OLED 是最平衡的选择。0.96 英寸、128x64 分辨率的 I2C 接口 OLED 模块,既不占太多电路板空间,又能显示鱼鳞纹理、水波纹、爱心等像素图形。再加上 SSD1306 驱动芯片,在 STM32、ESP32、Arduino 上都有非常成熟的库支持,驱动难点基本为零。
2.4 为什么要关注功耗
可穿戴设备的佩戴体验和续航强相关。如果一块手链每天都需要充电,用户很快就会放弃使用。因此在硬件设计阶段,就要考虑三处省电设计:
- 蓝牙广播间隔,影响连接速度和功耗
- 屏幕亮度与刷新率,OLED 在显示大面积高亮内容时功耗明显上升
- MCU 睡眠模式,没有操作时应当进入低功耗状态
市面上成熟手环的待机时间通常以“天”计,而不是以“小时”计,所以功耗优化不是后期加分项,而是决定项目能否真正日常使用的关键。
3. 环境准备与硬件选型清单
在动手之前,建议先准备好一套完整工具链。对于纯软件背景的读者,这里的清单会比你平时的工作环境多出一些硬件烧录设备,但这些都是成熟、容易获得的工具。
3.1 硬件清单
| 部件 | 选型建议 | 数量 | 说明 |
|---|---|---|---|
| 主控 MCU | STM32F103C8T6 或 ESP32-C3 | 1 | 推荐 ESP32-C3,自带蓝牙 |
| 显示屏 | 0.96 英寸 SSD1306 OLED | 1 | I2C 接口 |
| 蓝牙模块 | 如果 MCU 无蓝牙,选择 HC-08 或 CC2541 | 1 | 串口透传即可 |
| 锂电池 | 3.7V 聚合物电池,容量 150mAh 到 300mAh | 1 | 体积优先 |
| 充电管理 | TP4056 模块 | 1 | 支持 USB 充电 |
| 震动马达 | 微型扁平马达 | 1 | 可选,用于提醒 |
| 外壳 | 3D 打印树脂或亚克力切割 | 1 | 根据结构设计 |
| 表带 | 硅胶或编织带 | 1 | 成品手链底座 |
3.2 软件工具清单
| 工具 | 用途 |
|---|---|
| VS Code + PlatformIO | 写固件程序,推荐,跨平台体验好 |
| Arduino IDE | 适合入门,ESP32 支持很成熟 |
| STM32CubeMX | 用于 STM32 的引脚初始化和时钟树配置 |
| Altium Designer 或立创 EDA | 原理图和 PCB 设计 |
| FreeCAD 或 Fusion 360 | 外壳三维建模 |
| 手机 App | 可使用现有的 BLE 调试助手,先不用自研 App |
3.3 开发环境建议
从降低上手门槛考虑,我建议第一次尝试就选用 Arduino IDE 加 ESP32-C3,原因有三个:一是 ESP32-C3 内置 Wi-Fi 和蓝牙,省掉了外挂蓝牙芯片的配对问题;二是 Arduino 生态对 SSD1306 OLED、锂电池电压检测等模块的支持非常成熟;三是烧录和串口监视器使用简单,不需要额外的仿真器。
如果坚持使用 STM32,会让你在底层寄存器、时钟树配置上花更多时间,这是好事,前提是给自己留出足够的学习周期。
4. 核心流程拆解
整个项目可以拆成六个阶段,每个阶段都有独立的产出物和验收标准。
4.1 原理图搭建
先把大系统的各个模块画在一张原理图里。重点要关注电源树,这一点对新手来说最容易出错。充电模块的 USB 输入来自外部,输出接到电池正极;电池同时给系统供电,但 MCU 的工作电压是 3.3V,因此电池电压需要经过 LDO 稳压器转换。切忌把 3.7V 电池直接接到 3.3V 供电引脚上,这会击穿芯片。
4.2 PCB 布局与尺寸控制
手链的体积大约两三厘米见方,因此 PCB 布局要紧凑。显示屏通过排针或 FPC 座连接,MCU 尽量放在板子中心。蓝牙天线区域要保持净空,不能覆盖铜皮和元件,否则信号衰减会让连接距离直接缩短。
4.3 固件功能划分
固件代码需要处理三类任务:显示时间或动画、处理蓝牙命令、管理电池电量和低功耗状态。在代码结构上,可以把显示、蓝牙、电源管理分成独立模块,保持文件清晰,不要写成一个上千行的主文件。
4.4 交互逻辑定义
把“美人鱼”主题转化为具体的交互方式。常见做法是:抬手点亮屏幕;短按按键切换时间、动画、表白文字三种模式;长按关机;蓝牙连接成功后,显示一个爱心动画作为反馈。
4.5 结构件和外观制作
外观是这个项目的灵魂。你可以用建模软件设计一个贝壳形或鱼尾形的外壳,正面留出 OLED 窗口,侧面留一个按键孔和充电口。如果不想学建模,也可以选用市售的圆形表壳作为底板,把 PCB 固定在里面。
4.6 整机测试
把硬件、固件、外观组装到一起后,进行完整的功能测试。测试内容包括显示是否正常、蓝牙指令是否有响应、充电时指示灯是否亮起、待机电流是否符合预期。每一项测试都需要记录结果,形成一张验收表。
5. 完整示例代码实现
下面给出三个核心代码片段,分别对应显示驱动、蓝牙控制和低功耗处理。为了方便实战,示例采用 Arduino 环境加 ESP32-C3 的写法,库文件从 Arduino 库管理器安装即可。
5.1 OLED 显示美人鱼动画帧
文件路径:src/main.cpp
#include <Wire.h> #include <Adafruit_GFX.h> #include <Adafruit_SSD1306.h> #define SCREEN_WIDTH 128 #define SCREEN_HEIGHT 64 #define OLED_RESET -1 Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET); // 简单的鱼鳞图案,用像素点方式绘制 void drawScale(int offsetX, int offsetY, int size) { for (int x = 0; x < size; x++) { for (int y = 0; y < size; y++) { int dist = (x - size / 2) * (x - size / 2) + (y - size / 2) * (y - size / 2); if (dist > (size / 2 - 1) * (size / 2 - 1) && dist < (size / 2 + 1) * (size / 2 + 1)) { display.drawPixel(offsetX + x, offsetY + y, SSD1306_WHITE); } } } } void drawMermaidPattern(int frame) { display.clearDisplay(); // 随着 frame 变化制造动态效果 for (int i = 0; i < 3; i++) { int yOffset = (frame * 2 + i * 12) % 48; drawScale(20 + i * 30, 8 + yOffset, 12); } display.setTextSize(1); display.setTextColor(SSD1306_WHITE); display.setCursor(20, 52); display.println("Mermaid Tail"); display.display(); } void setup() { if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) { // I2C 地址确认 Serial.println("SSD1306 allocation failed"); for (;;) ; } display.clearDisplay(); } void loop() { for (int frame = 0; frame < 10; frame++) { drawMermaidPattern(frame); delay(120); } }这段代码画出了三个随时间上下浮动的鳞片轮廓,模拟水面波动的感觉。真正做外观时,可以把这些图案替换成更精细的位图数组,直接在代码里用const unsigned char定义图片数据。
5.2 蓝牙透传命令解析
如果你采用“MCU + 蓝牙模块”方案,串口透传是最简单的方式。手机端通过 BLE 调试助手向模块发送字符串,MCU 收到后做命令匹配。以下代码演示了如何解析字符指令:
文件路径:src/ble_handler.cpp
#include <Arduino.h> String receiveBuffer = ""; void processCommand(String cmd) { cmd.trim(); if (cmd == "TIME") { // 发送时间数据到显示模块 Serial.println("CMD: TIME"); } else if (cmd == "LOVE") { // 显示爱心动画 Serial.println("CMD: LOVE"); } else if (cmd == "OFF") { // 进入低功耗模式 Serial.println("CMD: OFF"); } else { Serial.println("CMD: UNKNOWN"); } } void setupBLE() { Serial.begin(115200); } void loopBLE() { while (Serial.available() > 0) { char c = Serial.read(); if (c == '\n') { processCommand(receiveBuffer); receiveBuffer = ""; } else { receiveBuffer += c; } } }这里的关键点是把“接收数据”和“解析命令”分开。接收数据时只做字符累积,等到收到换行符再整体处理,这样可以避免由于串口分包导致的命令不完整问题。
5.3 低功耗模式与电池电压检测
ESP32-C3 的深度睡眠模式可以显著降低待机功耗。以下代码演示了按键事件唤醒和电池电压读取的基本思路:
文件路径:src/power_manage.cpp
#include <driver/adc.h> #include <esp_sleep.h> #define WAKEUP_PIN GPIO_NUM_2 #define ADC_BATTERY_PIN ADC1_CHANNEL_0 void setupPower() { // 配置触摸或 GPIO 唤醒 esp_sleep_enable_ext0_wakeup(WAKEUP_PIN, 0); } float readBatteryVoltage() { int raw = analogRead(ADC_BATTERY_PIN); // 根据分压电阻计算实际电池电压,这里默认分压比 2:1 float voltage = raw * 3.3 / 4095.0 * 2.0; return voltage; } void enterSleep() { Serial.println("Entering deep sleep"); esp_deep_sleep_start(); } void loopPower() { float bat = readBatteryVoltage(); if (bat < 3.2) { enterSleep(); } delay(5000); }实际使用中,ADC 引脚的电压范围、分压电阻的精度都会影响电池电量的读数,建议在硬件阶段给电池电压加上分压电路并留出校准点。
5.4 按键输入与多状态切换
为了让交互更自然,这里再给出一个简单的状态机示例,用来切换“时间显示 / 动画显示 / 表白文字”三种模式:
文件路径:src/ui_state.cpp
enum UIState { UI_TIME, UI_ANIMATION, UI_MESSAGE }; UIState currentState = UI_TIME; void switchState() { if (currentState == UI_TIME) { currentState = UI_ANIMATION; } else if (currentState == UI_ANIMATION) { currentState = UI_MESSAGE; } else { currentState = UI_TIME; } refreshScreen(); } void refreshScreen() { switch (currentState) { case UI_TIME: // 调时间显示函数 break; case UI_ANIMATION: // 调动画函数 break; case UI_MESSAGE: // 显示自定义表白文字 break; } }这种状态机写法看起来简单,但在嵌入式 UI 里非常实用。后续要增加计步、来电提醒等功能,只需要在枚举里增加状态值,再补充对应的refreshScreen分支即可。
5.5 构建与烧录命令
在 PlatformIO 中,项目目录下的platformio.ini可以这样配置:
文件路径:platformio.ini
[env:esp32-c3] platform = espressif32 board = esp32-c3-devkitm-1 framework = arduino monitor_speed = 115200 upload_speed = 921600然后运行命令:
pio run -t upload pio device monitor如果烧录失败,先检查开发板是否处于下载模式,以及 Windows 下的串口驱动是否正确安装。
6. 运行结果与效果验证
烧录完成后,OLED 屏幕如果出现卡通美人鱼动画并且能循环播放,说明 I2C 通信和显示库已经正常跑通。接着可以验证蓝牙:
- 打开手机上的 BLE 调试工具,搜索附近的蓝牙设备。
- 连接名称中包含设备 MAC 后四位字符的设备。
- 在调试工具的“写数据”窗口发送
LOVE\n,屏幕应当切换到爱心动画。 - 发送
OFF\n,设备进入睡眠状态,电流明显下降。
使用万用表串联在电池供电回路中,可以测试休眠电流。如果休眠电流明显偏高,通常是板载稳压芯片或传感器的静态功耗过高,建议从这些外设逐个排查。
有一个细节值得留意:OLED 在显示全屏动画时会消耗较多电流,如果电池容量只有 150mAh,持续播放动画可能只能坚持两小时左右。因此在正式使用中,建议把动画模式做成“短按触发,10 秒后自动退出”,不要长时间停留。
7. 常见问题与排查思路
在实际 DIY 过程中,往往会碰到五花八门的问题。我整理了几个高频问题,以及对应的排查路径:
| 问题现象 | 可能原因 | 排查方式 | 解决方案 |
|---|---|---|---|
| 屏幕不亮 | I2C 地址不对或接线反了 | 扫描 I2C 总线,确认设备地址 | 把接线改为 SDA/SCL 正确引脚,修改库初始化地址 |
| 蓝牙连接不上 | 广播间隔过长或天线被遮挡 | 检查广播日志和 RSSI 值 | 缩短广播间隔,重新规划 PCB 天线净空 |
| 电池续航太短 | 传感器持续上电或代码未进入睡眠 | 使用万用表测工作模式和休眠电流 | 睡眠前关掉不必要的外设电源,降低屏幕刷新频率 |
| 代码烧录失败 | 串口被占用或未进入下载模式 | 检查设备管理器和串口占用情况 | 关闭串口监视器,按住 BOOT 键再上电 |
| 手机接收不到心跳数据 | 固件没有周期发送通知 | 查看 notify 配置代码 | 确认服务端已使能 CCCD,并配置通知回调 |
这里最容易被忽视的是静电问题。在干燥环境下,手直接接触天线区域或者裸露引脚,可能导致蓝牙连接瞬间断开。最终外壳组装时,注意让充电接口和按键区域有防护,避免频繁触摸内部电路。
8. 最佳实践与工程建议
8.1 使用模块化代码架构
主程序文件不要堆成一个 2000 行的“大泥球”。按照显示屏、电源、蓝牙、按键、状态管理建立独立文件和接口,这样后期修改每一个模块时不需要理解所有历史逻辑。
8.2 建立测试清单
做完一版硬件之后,第一时间要做完整测试,不要只测“屏幕能亮”就收工。测试项至少包括:
- 电压范围测试:3.3V 到 4.2V 之间,系统是否稳定
- 蓝牙连接距离测试:无障碍物 5 米内是否稳定
- 充电测试:充电过程中是否可以正常开机
- 待机电流测试:是否低于预期阈值
- 跌落测试:外壳是否保护内部元器件
8.3 预留调试接口
PCB 上一定要留出至少一个串口或 SWD 调试触点。不要因为追求美观而砍掉调试接口,否则一旦出现故障,只能整机报废重做。在产品原型阶段,调试能力比体积更重要。
8.4 关于安全与合规
如果这个项目只是自己使用,问题不大。但如果要送给他人,请注意锂电池的使用安全:
- 不要使用破损、鼓包的电池
- 充电时使用带过充保护的 TP4056 模块
- 外壳应留有小型散热孔,避免锂电池在封闭高温环境下工作
- 不要尝试把充电口接反电源
在 3D 打印外壳或手工制作时,还要避免金属部件直接接触电池正负极,防止短路起火。
8.5 外观和佩戴体验优先级不低于硬件
很多技术背景的开发者容易把注意力全放在 PCB 和代码上,结果做出来的外壳方方正正,佩戴体验很差。实际上,手链毕竟是日常戴在手腕上的物品,边缘必须倒角打磨,重量需要控制在合理范围内,按键不要设计得过硬。外观设计与硬件设计应该并行启动,不要等电路板做完了再临时找外观方案。
9. 从礼物到产品:值得继续深入的方向
当你照着文章把第一版手链做出来之后,可以明显感觉到,真正的技术挑战不在某一个模块,而在整体系统的协同:电池容量与体积的矛盾、显示效果与功耗的取舍、佩戴舒适度和内部电路的冲突,这些才是可穿戴设备设计师每天都在面对的问题。
后续值得深入的方向有三个。第一个是自研手机 App,通过蓝牙协议给手链发送自定义文字或照片,这样对方可以随时在手链上看到你编辑的“今日彩蛋”。第二个是引入更多传感器,比如加速度计,用来实现抬腕亮屏和计步。第三个是完善整体结构设计,使用倒扣式卡扣取代螺丝固定,让外壳更有饰品质感。
但这篇文章最终想说的,其实是一个更朴素的观点:技术能力不应该只停留在简历和项目文档里,它完全可以变成一份有温度的实物礼物。一块由你亲手设计电路、编写固件、三次打样后终于组装成功的手链,它的价值远不止于屏幕上的图案,更在于你投入在里面的时间与思考。这才是“让 TA 感受你真心与浪漫”的最有力证明。