我盯着这个开源项目的资料看了很久,说实话现在能打动我的嵌入式作品不多了,但这个“墨水屏智能手表”确实值得认真聊聊。它不是一个玩具级的点灯项目,而是一个把“免费开源”“30 天长续航”“Arduino 编程”“自定义表盘”“蓝牙 Wi-Fi 双连接”这些关键词全部串起来的完整方案。对于想入门低功耗硬件、想搞懂电池续航到底是怎么算出来的朋友来说,它是一块非常合适的“磨刀石”。文章里我会把硬件选型、代码骨架、功耗优化思路、踩坑记录一次讲透,尽量把那些网上没人细说的细节也补上。
1. 这个项目到底在做什么:整体拆解与方案思路
1.1 为什么是墨水屏:30 天续航的第一块基石
很多人不理解,一块表凭什么能续航 30 天?答案的核心就藏在墨水屏里。墨水屏是一种双稳态显示技术,屏幕上每个黑白像素在电场驱动下翻转一次后,即使完全断电,画面也能一直保留。也就是说,墨水屏和传统 LED、OLED 屏幕的最大区别在于:它只在“刷新画面”的那一瞬间消耗明显电流,静态显示时几乎不耗电。
我拿常见的一块 1.54 英寸 200x200 黑白墨水屏来算:一次全屏刷新,大约消耗几毫焦耳的能量,折合成电池容量,大概只有 0.01mAh 到 0.02mAh。哪怕你一天刷新 100 次,在显示这一项上也只花掉 1 到 2mAh。反观 OLED 屏幕,如果像普通智能手表那样常亮显示,哪怕亮度调到很低,一天下来几十毫安时也是免不了的。这就是墨水屏手表能做到“月充”而普通屏幕很难做到的根本原因。
但墨水屏也有代价:刷新速度慢,通常一次全刷要一两秒,动画效果基本别想;同时刷新次数多了会有残影积累。所以这种屏适合信息变化频率低、以静态内容为主的设备。智能手表的表盘恰好就是“低频静态信息”的典型场景,时间、日期、天气、电池电量,这些内容本来就不需要每秒钟变化几十次。选墨水屏做手表,本质上是用“互动流畅度”换“续航”,对一个佩戴设备来说,这笔交易非常划算。
1.2 为什么主控是 ESP32:蓝牙 Wi-Fi 二合一,开发资料又全
项目标题里同时出现了“蓝牙”和“Wi-Fi”,这两条网络能力放在同一颗芯片上,当前最成熟、最便宜、最容易上手的方案就是乐鑫的 ESP32 系列。ESP32 内部集成了 2.4GHz Wi-Fi 和蓝牙 BLE,BLE 用于和手机 App 交互、接收配置或通知,Wi-Fi 用于拉取天气、同步时间、甚至日后做 OTA 固件升级,这些功能一颗芯片全搞定。
如果换成 STM32,蓝牙和 Wi-Fi 都要外挂模块,体积和成本都会增加;如果换成 nRF52840,BLE 做得很好,但 Wi-Fi 就得额外挂一颗芯片。而 ESP32 在 Arduino 生态里的资料丰富度,可以说是“遇到问题搜一下就有一堆解决方案”的程度。对于新手复刻这个项目,选 ESP32 能把硬件难度降到最低,也把可维护性拉到最高。
具体型号上,我建议优先考虑带 IPEX 天线或 PCB 天线的 ESP32-PICO 系列模块,体积小、集成度高,适合塞进手表壳里。ESP32-S3 模组也可以选,Flash 和 SRAM 都更大,GPIO 也更灵活。不过要注意,S3 的 Arduino 支持现在虽然已经很成熟,但部分老库针对原始 ESP32 优化更好,如果你完全是个新手,我反而推荐先用经典的 ESP32 DevKit 模块跑通代码,再考虑换小尺寸模组。
1.3 功能模块与完整硬件链路
这个项目看起来功能很多,但拆开看,其实每条功能链路的硬件开销都不复杂:
| 功能 | 关键硬件 | 数据来源 |
|---|---|---|
| 时间显示与智能闹钟 | 外部 RTC 时钟芯片 | I2C 总线读取,Wi-Fi 同步校准 |
| 指南针/方位角 | 磁力计传感器 | I2C 总线读取,软件校准 |
| 实时天气 | Wi-Fi + 免费天气 API | JSON 解析后绘制 |
| 番茄钟/倒计时 | 主控状态机 | 以 RTC 为基准计时 |
| 自定义表盘 | 按键/BLE 配置 | 存储中的布局参数 |
| 电池监测 | ADC 采样分压 | 定时读取并绘制电量图标 |
| 充电管理 | 锂电池充电板 | 独立模块,不影响主控逻辑 |
软件架构上,我强烈建议你不要在这块表上硬上复杂的 RTOS。这个项目本质上是一个由事件驱动的状态机:平时靠定时器唤醒,按键产生外部中断唤醒,蓝牙数据到达时按需处理。把“显示表盘”“处理菜单”“刷新天气”“番茄钟计时”这几个状态用最简单的 switch-case 管理起来,逻辑清晰,调试起来也快。如果你一开始就引入多线程调度,低功耗的时机把控反而会变得非常难。
2. 硬件选型细节:照着这个清单买,基本不会翻车
2.1 屏幕、主控模组与时间芯片的选型要点
屏幕部分,市面上最通用的是微雪或兼容的 1.54 英寸 200x200 黑白墨水屏,以及 2.13 英寸 250x122 的宽屏墨水屏。我个人更推荐 1.54 英寸,因为它的表盘形态更接近圆形手表的显示窗口,而且驱动板小、排线少。驱动库优先用 GxEPD2,它支持几十种墨水屏型号,底层封装得很干净,换屏时只需要改几行构造参数。
时间芯片是很多人会忽略的关键点。ESP32 内部虽然也有 RTC,但在深度睡眠状态下,内部 RTC 的时钟精度受温度影响很大,一个月走下来可能偏出十几分钟甚至更多。外挂一颗 DS3231 或者 RX8025T,就能把时间漂移控制在很低的水平。DS3231 带温补晶振,精度高、库多、资料全;RX8025T 同样带温补,功耗更低,很多成品开源手表都在用。新手我建议先试 DS3231,因为 Arduino 生态里关于它的示例代码实在太多了。
磁力计方面,开源方案里比较常用 QMC6310,中科微出品,价格便宜,性能也够用。HMC5883L 也仍然能买到,但假货多,稳定性不如 QMC6310 新品。指南针需要做校准,否则方位角完全没参考价值,这一点到代码部分我再细说。
2.2 电池、充电与电源管理
电池我用的是 502030 尺寸的聚合物锂电池,容量约 350mAh,厚度只有 3 毫米,能顺利放进打印好的表壳里。充电板用最常见的 TP4056 模块,带充放电保护,价格几块钱,接上 USB-C 就能充电。你可以在充电板的输出端加一个低功耗升压或 LDO 给主控供电,但我实测下来,如果电池电压在 3.3V 到 4.2V 之间,直接给 ESP32 的 3.3V 引脚供电也没有问题,只是要注意电量低于 3.4V 时要尽快关机,否则电池容易过放。
关于按键开关机,这里有个非常实用的技巧:用一颗 P-MOS 管做电源锁存。按下开机键时,MOS 管导通给整个系统供电,主控启动后把一个 GPIO 拉高保持住 MOS 管的导通状态;需要关机时,主控把 GPIO 拉低,切断电源。这样能实现真正的“完全断电休眠”,比让主控一直运行在浅睡眠里等按键要省电得多。这个方案在很多开源手表项目里都有现成电路,直接参考就行。
2.3 外壳、屏幕排线与传感器布局的实操建议
外壳部分最省事的方式是 3D 打印。很多开源手表项目发布时都会附带 STL 模型,你直接打印一个表壳,再买 22mm 的快拆表带就行。没有 3D 打印机的话,用亚克力板激光切割也行,甚至是用现成的运动手环表壳改造,只要给屏幕和按键开好孔就没问题。
屏幕排线是最容易折断的地方。墨水屏模块的 FPC 排线很脆,安装时一定要给排线留出弯折余量,不能直角弯折。我自己的做法是在排线背面贴一层薄透明胶带,再用海绵垫在排线折弯处,这样能显著降低排线断裂的概率。
传感器的布局也很关键,尤其是磁力计。手表表带如果有磁吸扣,会对指南针产生很大干扰,这点很多新手根本没意识到。我在调试时就踩过这个坑,表带是磁扣的,指南针方向偏得一塌糊涂。后来换了非磁性表扣,再经过校准,方位才恢复正常。所以做指南针功能时,先检查整机里有没有不该出现的磁性物体。
3. 核心代码实现:从表盘绘制到天气更新的完整骨架
3.1 表盘框架:如何画一个清晰又省电的表盘
表盘是用户每天看得最多的界面,设计的好坏直接决定这个项目值不值得做。墨水屏的表盘不需要复杂,关键是信息层级要清晰。我的表盘布局是:左上角显示日期和星期,中间用大号数字字体显示时和分,右上角放温度,底部放电池图标、蓝牙状态、闹钟标志。这样一屏能覆盖所有常用信息,又不拥挤。
代码层面,GxEPD2 库提供了基础绘图 API,你可以在一个画布上绘制文字、矩形、线条、位图。时间字体我建议用 DejaVu Sans Mono 之类的等宽数字字体,避免数字变化时文字宽度抖动。如果你要显示中文字符,比如“周一”“天气”这类,需要注意不要加载完整中文字库,否则 Flash 和内存都扛不住。推荐用字体提取工具,只把用到的汉字和符号转成 C 语言数组,再交给 Adafruit_GFX 或 U8g2 渲染。这样既保留了中文字形,又不会撑爆 ESP32 的资源。
墨水屏刷新策略上,我要特别强调:别每次都全刷。时间每分钟都会变,如果你每分钟全刷一次,虽然单次耗电不大,但残影问题会非常严重。正确做法是:分钟变化时,只对时间区域做局部刷新;每 30 分钟或 1 小时做一次全刷,清掉残影。GxEPD2 库提供了局部刷新接口,可以把刷新区域传入。这套策略在实测中既保证了显示清晰度,又减少了对墨水屏寿命的损耗。
3.2 天气获取:申请 API、解析 JSON、失败降级
天气功能需要连网,整体流程其实就三步:连接 Wi-Fi、请求天气 API、解析并缓存结果。市面上免费的天气 API 很多,选择一个免费额度足够、返回字段清晰的即可。申请好 API Key 之后,用 HTTP GET 请求对应 URL,返回的 JSON 里通常包含当前温度、天气代码、城市名等字段。
解析 JSON 我推荐 ArduinoJson 库,用法非常直白:
StaticJsonDocument<4096> doc; DeserializationError err = deserializeJson(doc, payload); if (!err) { float temp = doc["main"]["temp"]; int weatherId = doc["weather"][0]["id"]; String city = doc["name"].as<String>(); }这里有两个容易踩的坑。第一,ESP32 的 FreeRTOS 任务栈默认 8KB 左右,如果你的 JSON 文档开得太大,解析时可能会栈溢出,程序莫名其妙重启。遇到这种情况,要么把 StaticJsonDocument 的缓冲区放到全局变量里,要么在 menuconfig 中调大任务栈。第二,天气请求不是每次都保证成功,网络波动、API 限流都会导致失败,所以一定要把上次成功获取的天气数据缓存到 NVS 或 RTC 内存里,请求失败时直接显示旧数据,而不是显示一片空白。我在代码里是这样实现的:每天早上 7 点、中午 12 点、傍晚 6 点各更新一次天气,每次更新前先检查距上次更新时间是否超过 2 小时,没超过就直接用缓存。
3.3 番茄钟与闹钟:用简单状态机实现定时提醒
番茄钟和闹钟听起来像是“额外功能”,但实现起来并不复杂,因为我们可以把所有计时基准都放在外部 RTC 上。番茄钟的本质是一个倒计时状态:设置开始时间点,然后不断读取当前时间,计算差值,当差值达到设定分钟数时触发提醒。闹钟的本质是在某个设定时间点触发提醒。
这里的代码骨架可以抽象成几个状态:
enum TimerState { IDLE, FOCUS, BREAK, ALARM };每次唤醒后,先读取外部 RTC 的当前时间,再根据状态决定是否要切换到提醒状态。触发提醒时,可以驱动一个小蜂鸣器或震动马达,同时墨水屏上显示“番茄时间结束”之类的提示。因为墨水屏刷新慢,不该用动画去表现提醒,最直观的方式是全屏反色闪烁两次,配合蜂鸣器短响,效果就很明显。
要注意的是,番茄钟的计时过程中,主控不能一直醒着,否则续航就崩了。我采取的策略是:进入番茄钟状态后,手表仍然按正常节奏进入深度睡眠,但把定时唤醒间隔缩短到每 30 秒一次。每次醒来只读一次 RTC、判断一下时间是否到点,然后立刻继续睡。这样在 25 分钟的番茄周期里,一共只额外唤醒 50 次,每次工作几十毫秒,增加的电量消耗完全可以忽略。
3.4 蓝牙连接与手机 App 联动:先把“实时通知”这件事想明白
项目标题里的“支持蓝牙连接”很容易让人误以为能像主流智能手表那样实时接收手机消息。但我要先把话说清楚:在 30 天长续航的约束下,让 BLE 始终保持连接状态接收推送,是一个不太现实的方案。ESP32 在 BLE 连接状态下平均电流通常是几毫安到十几毫安,一天下来就要吃掉一两百毫安时,350mAh 的电池根本撑不到一周。
所以合理的方案有两种。第一种是“按需连接”:手表平时深度睡眠并关闭蓝牙,只有当用户按键进入蓝牙配置模式时,才打开 BLE,让手机 App 连接手表,进行表盘切换、天气刷新、时间校准等操作。用完后自动关蓝牙继续睡觉。第二种是“周期同步”:手表每 5 分钟或 10 分钟醒来一次,短暂打开 BLE 广播或与手机保持极低频的交互,把通知同步下来。第二种体验更好一些,但省电效果要差一点,具体取舍就看你是更在意续航还是更在意消息及时性。
我自己的选择是第一种,实现成本低,续航也最容易保证。BLE 的服务定义也很直接:在手机 App 里配置表盘主题时,通过 GATT 的写特征值把表盘的索引数字和背景图编号传给手表,手表收到后存入 NVS,下次启动时按配置绘制。这里建议定义两个 Characteristic,一个负责接收命令,一个负责返回手表状态,比如电量、固件版本、当前表盘编号。如果你会写一点 Android 或者 Flutter 的简单 BLE 调试工具,整个联动过程一周内就能跑通。不会写 App 也没关系,先用 nRF Connect 这个工具手动发送命令做验证,代码完全不受影响。
3.5 指南针功能的软件校准与显示
指南针的核心是读取磁力计三轴数据并计算方位角。公式并不复杂,核心代码如下:
float heading = atan2(my, mx) * 180.0 / PI; heading += declinationAngle; // 地磁偏角修正 if (heading < 0) heading += 360;但直接读出来的数据往往不能直接用,因为传感器存在零偏,而且手表本身可能有磁性干扰。所以一定要做校准:在手表上提供一个“画圆校准”菜单,让用户拿着手表在空中缓慢画 8 字,程序记录 X、Y、Z 三轴的最大值和最小值,算出偏移量后存到 NVS。以后每次读取都先减偏移再做反正切。这个流程虽然简单,但效果立竿见影。另外,如果你在室内靠近金属桌或钢筋墙壁,磁力计读数会剧烈跳变,这是正常的物理现象,不是代码 bug。
4. 30 天长续航是怎么算出来的:功耗实测与优化细节
4.1 先把账算明白:哪些地方才是真正的耗电大头
很多新手做低功耗项目时有个误区,以为屏幕是耗电大头。实际上在墨水屏方案里,屏幕耗电并不高,真正的耗电大头是无线连接。ESP32 打开 Wi-Fi 连接路由器并完成一次天气请求,瞬间电流可以达到 200 到 300mA,哪怕整个过程只有 3 秒,单次也要消耗 0.2 到 0.3mAh。如果一天更新 24 次天气,光 Wi-Fi 就吃掉六七毫安时,再加上其他开销,30 天续航就别想了。
所以,想要 30 天续航,必须控制联网频率。我实测过一组数据:350mAh 的电池,如果天气每小时更新一次,平均每天消耗约 12mAh,勉强能撑 29 天,但这还没有算上蓝牙和其他开销。如果把天气更新改成每 2 小时一次,并且把每次 Wi-Fi 连接时间压缩到 2 秒以内,每天无线开销大约降到 4mAh,续航余量就非常充裕了。
4.2 深度睡眠与唤醒策略:睡眠电流决定续航上限
整个低功耗设计的真正核心是深度睡眠。ESP32 官方资料里给出的深度睡眠电流在 5uA 左右,但那是芯片本身的裸值。实际项目里你要接外部 RTC、磁力计、墨水屏模块,这些外设接在总线上,如果不做断电控制,睡眠电流很容易飙升到几百微安。
正确做法是:把外设的供电引脚单独用 GPIO 控制,睡眠时彻底切断传感器和屏幕的电源,只给外部 RTC 保持供电。RTC 芯片工作在时间保持模式时,电流只有几微安,完全可以忽略。我实际测量过整个手表的深度睡眠电流,最终控制在 15uA 左右,这已经是相当不错的成绩。
唤醒策略也要分层:时间更新可以每隔 30 秒或 1 分钟唤醒一次,读取 RTC 后刷新表盘;天气更新可以每隔几小时唤醒一次,联网获取数据后继续睡;按键是通过 RTC GPIO 的外部中断唤醒,按下立即响应。ESP32 在 Arduino 里实现深度睡眠非常直接:
esp_sleep_enable_timer_wakeup(SLEEP_INTERVAL_US); esp_sleep_enable_ext0_wakeup(BUTTON_PIN, LOW); esp_deep_sleep_start();唤醒后,程序会从 setup() 重新执行,所以你需要根据esp_sleep_get_wakeup_cause()判断这次是因为定时器还是按键醒来,再决定要做哪些事。这套框架一旦搭好,后面加什么功能都往里面套就行。
4.3 墨水屏刷新策略:局部刷新和全刷的节奏要把握好
墨水屏的刷新功耗虽然不高,但刷新行为会直接影响两个东西:残影和显示寿命。显示时间这种低频变化内容,最理想的方式是只刷新数字所在的矩形区域。GxEPD2 支持局部刷新,代码里可以这样处理:
display.setPartialWindow(60, 80, 120, 40); display.firstPage(); do { display.fillScreen(GxEPD_WHITE); display.setCursor(60, 95); display.print(timeStr); } while (display.nextPage());每次分钟变化时调用这段代码,每秒甚至更频繁的刷新完全不需要。我建议每 10 到 15 次局部刷新之后,做一次全屏刷新来清掉残影。全刷虽然会轻微频闪,但低频次发生,用户感受不明显。还要注意,墨水屏在低温环境下刷新速度会变慢,冬天在户外如果发现屏幕变浅或者刷新中途卡住,别慌,把设备揣进口袋捂热再试,或者对屏幕重新初始化一次就好了。
4.4 一张可复算的功耗账本
我把整个系统的功耗做个估算表格,你可以拿这个账本去核对你的代码和硬件:
| 项目 | 平均电流 | 每日消耗 |
|---|---|---|
| 深度睡眠(含外部 RTC) | 15uA | 约 0.36mAh |
| 表盘分钟刷新(1440 次局部刷新) | 每次约 0.4mA 工作 100ms | 约 0.12mAh |
| 每 30 分钟一次全刷清残影(48 次) | 每次约 1.5mA 工作 1.2s | 约 0.04mAh |
| 天气更新(每天 4 次 Wi-Fi 连接) | 每次约 260mA 持续 2.5s | 约 0.72mAh |
| 蓝牙配置操作(每天 2 次,每次 30s) | 平均 80mA | 约 1.33mAh |
| NTP 时间校准(每天 1 次) | 约 260mA 持续 1.5s | 约 0.11mAh |
| 合计 | — | 约 2.68mAh/天 |
这个数字反推下来,350mAh 电池的理论续航是 130 天左右。但实际使用中,你会频繁按键、调试代码、连接 App,这部分开销完全不可控。我实测正常佩戴、每天看时间和偶尔玩一下指南针的情况下,实际续航在 35 到 50 天之间。所以说“30 天”是一个非常稳妥且保守的指标,只要你把深度睡眠电流压住了,达到这个数字并不难。
4.5 蓝牙常开与通知模式的选择
上一节讲到了 BLE 保持连接的代价,这里再展开一下。如果确实想要来电或消息通知功能,我的建议是把手表做成“通知接收端”,手机 App 在后台周期性经过 BLE 连接把未读通知写入手表,然后立刻断开。这种模式下,手表不需要一直保持连接,平均电流仍然很低。而为了做到这一点,配套手机 App 需要支持后台通知读取权限,在安卓和 iOS 上两者的实现难度不同,你在做之前一定要先评估清楚。我自己的经验是:先把手表端的 BLE 服务接口定义好,用手机调试工具模拟推送,验证没问题了,再写正规 App,这样能把风险降到最低。
5. 常见问题排查与避坑实录
5.1 墨水屏残影、白屏与低温刷新失败的应对
残影问题最常见的原因就是局部刷新次数过多却没有及时全刷。解决方案很简单:在代码里维护一个计数器,累计 N 次局部刷新后强制执行全刷。白屏问题则多半出在驱动时序上,墨水屏模块通常有 BUSY 引脚,刷新前一定要等待 BUSY 拉低再发送下一帧数据,否则会莫名其妙丢帧。还有,墨水屏的电源电压一般需要专用的负压驱动,模块供应商一般都会集成驱动电路,如果你用的是裸屏而不是模块,就需要额外加电平转换和驱动芯片,新手千万不要直接拿 ESP32 的 GPIO 去驱动裸墨水屏。
冬天户外使用墨水屏,刷新速度会明显变慢,这是墨水材料在低温下响应变慢的物理特性。遇到这种情况,要么减少户外刷新频率,要么在表壳内部垫一小片保温棉,帮助屏幕保持工作温度。实测下来,零度以下刷新时间可能从 1.5 秒延长到 3 秒,视觉上会有一点点卡顿,但功能不会受太大影响。
5.2 时间漂移与天气数据更新失败
如果你发现表盘时间过一段时间就偏了,优先检查外部 RTC 的 32.768kHz 晶振是否虚焊,以及是否用了带温补的芯片。DS3231 很少出现明显漂移,如果还是偏,多半是你每次开机时用 NTP 同步时间,而 NTP 的时区配置写错了。时区偏移要按你所在的时区填写,比如东八区就是configTime(8 * 3600, 0, "ntp.aliyun.com")。
天气更新失败则要检查几个点:API Key 是否有效、请求 URL 是否正确、JSON 解析缓冲是否够大、网络连接是否成功。我的排查顺序一般是先串口打印原始返回内容,看是服务端返回错误还是解析失败。如果是解析失败,就把 StaticJsonDocument 的容量调大;如果是网络问题,就检查路由器 2.4GHz 频段是否开启。ESP32 只支持 2.4GHz Wi-Fi,如果你的路由器只开 5GHz 频段,那手表根本扫描不到,这一点也是新手特别容易疏忽的。
5.3 蓝牙搜不到、连接后掉线
手表进入蓝牙模式后,如果手机扫不到,最常见的原因是广播窗口太短或者设备已从手机蓝牙缓存里“遗忘”。你需要在广播参数里把广播间隔调大,并延长广播持续时间。另外,ESP32 深度睡眠醒来后,如果你在 setup 里完成了蓝牙初始化,但马上又进入深度睡眠,手机端的连接可能还没建立成功就断开了。我的建议是:蓝牙配置模式下,暂时禁用深度睡眠,等蓝牙操作完成或超时后再恢复低功耗流程。
连接后掉线还可能是电源不稳导致的。BLE 连接瞬间电流会突然拉高,如果你的电池已经接近没电,或者供电路径上电阻过大,电压跌落会直接让 ESP32 重启。排查时用示波器或者高精度万用表观察电池端电压,看看连接瞬间电压跌落幅度,如果超过 200mV,就要检查电池内阻和连接导线接触电阻。
5.4 续航不达标时的排查思路
如果你的手表实际续航只有几天,不要急着怀疑电池和芯片,先用电流表实测各状态电流。最简单的方法是在电池正极串一个 10 欧姆采样电阻,用万用表测电阻两端电压再换算成电流,或者直接买一个带微安级精度的 USB 电流表。睡眠电流偏大主要查四个地方:墨水屏模块是否断电、磁力计是否还在供电、TP4056 充电板是否有反向漏电、ESP32 是否真的进入了深度睡眠而不是浅睡眠。
有一个非常隐蔽的坑是:ESP32 的 EN 引脚如果悬空,会受到干扰导致芯片偶尔重启;还有,如果你的代码里在 loop 里加了delay(100)这种长延时但忘了进入深度睡眠,整个系统会一直运行,电流轻松跑到几十毫安。这些只要用电流表一测就能定位。
提示:低功耗项目的调试顺序永远是“先硬件量电流,再软件查状态”。没有电流数据,一切代码层面的猜测都没有意义。
5.5 新手比较容易忽略的 3 个细节
第一,墨水屏模块一定要在睡觉前把 CS 引脚和供电引脚一起拉低,否则即使没在执行刷新,模块上的驱动芯片也可能通过 SPI 总线漏电。第二,ESP32 深度睡眠唤醒后,GPIO 状态不会自动恢复到睡眠前的配置,你需要在 setup 里重新初始化所有引脚方向,特别是一些没有上拉的下拉电阻的引脚,一旦悬空,可能导致外设误动作。第三,表盘上的电池图标尽量用模拟量显示,不要简单地按“高于 3.5V 就显示满电”,因为锂电池放电曲线在中段非常平缓,简单的电压阈值法会把电量显示得忽高忽低。正确做法是用查表法拟合放电曲线,或者用库仑计芯片,不过对 350mAh 小电池,查表法已经够用。
最后再分享一点我的个人体会
这个项目我前前后后改了三版,最大的收获不是最终做出了一个能用的手表,而是真正理解了“低功耗设计是一个系统能力”。它不是靠一颗芯片的魔法,也不是靠某个万能的库,而是靠把每一个电流路径都算明白:屏幕刷几次、Wi-Fi 连几次、睡眠压到多少微安、蓝牙什么时候开什么时候关。每一步都省一点,最终才能换来 30 天甚至更长的续航。如果你打算复刻这个项目,我建议你从最简单的时间表盘跑通硬件开始,不加天气也不加蓝牙,先实测睡眠电流,然后再逐步往上加功能。每加一个功能,就重新量一次电流,这样你会对自己手上每一毫安时的去向心里有数。这个开源项目给后来者留下的价值,也正在于此。硬件平台搭好之后,它还能继续扩展血氧、心率、GPS 轨迹记录,原理上都是往同一条 I2C 总线上挂新传感器的事,水到渠成。