news 2026/10/30 3:49:26

ESP32墨水屏手表DIY:Arduino低功耗设计实现30天续航

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
ESP32墨水屏手表DIY:Arduino低功耗设计实现30天续航

1. 为什么我要做一块墨水屏手表

先说说我做这块表的起因。手上戴过好几块智能手表,续航基本都是一天一充,出差忘带充电器就直接变砖。后来接触到墨水屏,发现这玩意儿的物理特性太适合做手表了——断电之后画面还在,只有刷新的时候才耗电。理论上只要把刷新频率压下来,续航可以做到按周甚至按月算。

这块表的核心方案是Arduino 编程 + ESP32 主控 + 墨水屏显示,定位是一块完全开源、可以自己改代码、自己换表盘的智能手表。功能上覆盖了自定义表盘、蓝牙连接、Wi-Fi 联网、实时天气、番茄闹钟、指南针这几项。适合谁看?如果你玩过 Arduino、手里有块 ESP32 开发板、对低功耗设备感兴趣,那这篇内容基本可以照着复现;如果你只是想了解墨水屏手表是怎么做出来的,也能从里面看到完整的选型思路和踩坑记录。

我前后折腾了大概两个月,中间换过屏、换过电源方案、重写过好几次刷新逻辑,最后实测在正常使用强度下(每天看几十次时间、每小时刷一次天气、番茄钟偶尔用)能撑到接近 30 天。这个数字不是实验室理想值,是戴着上班、通勤、偶尔淋点小雨的真实结果。

下面我把整个项目的设计思路、硬件选型、代码实现、功耗优化和踩过的坑全部摊开讲,尽量做到你拿着这份记录就能自己攒一块出来。

2. 整体方案设计与核心选型思路

2.1 为什么是 ESP32 而不是其他主控

选主控这件事我纠结了很久。一开始想用 STM32,毕竟低功耗做得成熟,但后来放弃了,原因有三个。

第一是无线连接。这块表要支持蓝牙和 Wi-Fi,STM32 要外挂模块,电路复杂度和成本都上去了。ESP32 自带 Wi-Fi 和经典蓝牙 + BLE,一颗芯片全搞定,省掉一堆外围器件。

第二是Arduino 生态。ESP32 在 Arduino IDE 里的支持已经非常成熟,装个开发板包就能用,库也多。我搜过arduino esp32开发板和arduino esp32-c3开发板包下载这类关键词,社区资料非常全,遇到问题基本都能搜到答案。相比之下 STM32 用 Arduino 框架虽然也能跑,但坑明显更多。

第三是算力和内存。ESP32 双核 240MHz,带 520KB SRAM,跑表盘渲染、天气解析、蓝牙通信绰绰有余。如果以后想加点边缘 AI 的东西(比如手势识别),ESP32 也能扛得住。

具体型号我用的是ESP32-S3-N16R8 mini 开发板,16MB Flash + 8MB PSRAM。选 S3 而不是普通 ESP32 的原因是 S3 支持更低的深睡功耗,而且 PSRAM 大,做复杂表盘的时候可以整帧缓存,刷新更流畅。mini 版尺寸小,塞进表壳里刚好。

2.2 墨水屏的选型与驱动方案

墨水屏我试过两种。一种是常见的 SPI 接口小屏,另一种是带驱动板的成品模块。最后定下来用的是1.54 寸、200x200 分辨率的 SPI 墨水屏,原因很简单:尺寸适合手表、分辨率够显示时间日期和简单图标、SPI 接口接线少。

这里要提一个搜索里经常出现的词epd51822墨水屏例程。EPD 系列是墨水屏驱动芯片的常见型号,不同尺寸的屏驱动 IC 不一样,买屏的时候一定要确认驱动型号,然后找对应的库。我用的是 Waveshare 的 e-Paper 库,Arduino 里直接装就行,支持局部刷新和全局刷新两种模式。

墨水屏有个关键特性必须理解:全局刷新会闪黑,局部刷新不闪但会留残影。手表这种场景,秒针或者时间变化用局部刷新,每隔一段时间(比如一小时)做一次全局刷新清残影。这个策略后面功耗部分会详细讲。

2.3 电源方案:30 天续航的关键

续航是这块表的灵魂。我一开始用 200mAh 的小锂电,结果三天就没电了。后来换成500mAh 的软包电池,配合深度睡眠策略,才做到接近 30 天。

电源管理上我做了几件事。一是用TP4056 充电模块做充电管理,带过充过放保护。二是加了一个MOS 管开关电路,在深度睡眠时切断墨水屏和传感器的供电,只留 ESP32 的 RTC 域工作。三是把 ESP32 的深睡电流压到了10uA 级别,这个后面代码部分会讲怎么配。

粗略算一下账:500mAh 电池,如果平均工作电流做到 0.7mA 左右,理论上能撑 700 小时,差不多 29 天。实际因为有刷新峰值和 Wi-Fi 联网的瞬时大电流,会打点折扣,所以最终落在 28 到 30 天之间。

2.4 功能模块的取舍

标题里列的功能我一个个说下实现方式。

自定义表盘:表盘数据存在 Flash 里,用数组描述每个元素的位置、类型、内容。换表盘就是换一组配置,甚至可以通过蓝牙从手机推过来。

蓝牙连接:用 BLE 做低功耗通信,主要用来同步时间、推送表盘、传天气数据。经典蓝牙功耗太高,手表场景不合适。

Wi-Fi 连接:只在需要更新天气的时候开,连上路由器拿完数据立刻关掉。常开 Wi-Fi 续航直接崩。

实时天气:走 HTTP 请求天气 API,解析 JSON 拿温度和天气状况,然后渲染到屏幕上。

番茄闹钟:用 ESP32 的定时器 + 震动马达实现,25 分钟一个周期,到点震动提醒。

指南针:用 QMC5883L 或 HMC5883L 磁力计,I2C 接口,读三轴磁场算方位角。

3. 硬件搭建与接线实操

3.1 物料清单与成本

先把清单列出来,方便你照着买。

物料型号/规格数量备注
主控ESP32-S3-N16R8 mini116MB Flash + 8MB PSRAM
墨水屏1.54寸 SPI 200x2001确认驱动 IC 型号
电池500mAh 软包锂电1带保护板
充电模块TP40561带 DW01 保护
磁力计QMC5883L1I2C 接口
震动马达扁平纽扣马达13V 驱动
MOS 管AO34011P 沟道,做电源开关
表壳3D 打印1自己建模
表带20mm 硅胶1通用款

整体成本控制在一百多块,比买成品表便宜,而且能随便改。

3.2 墨水屏接线与注意事项

墨水屏走 SPI,接线如下:

  • VCC 接 3.3V
  • GND 接 GND
  • DIN 接 GPIO 11(MOSI)
  • CLK 接 GPIO 12(SCK)
  • CS 接 GPIO 10
  • DC 接 GPIO 9
  • RST 接 GPIO 8
  • BUSY 接 GPIO 7

注意:墨水屏的 BUSY 引脚一定要接,刷新的时候要等它拉低才能进行下一步,否则会出现花屏或者刷新不完整。我一开始偷懒没接,结果屏幕偶尔显示一半就卡住,查了半天才发现是这个原因。

另外墨水屏的供电最好独立控制,用 MOS 管做开关。因为墨水屏在待机时虽然不刷新,但驱动板还是有微小漏电流,长期累积对续航有影响。

3.3 电源电路的关键细节

电源部分我画了个简单的框图思路:电池接 TP4056 充电,TP4056 输出接一个 AO3401 P 沟道 MOS 管,MOS 管的栅极由 ESP32 的一个 GPIO 控制。这样 ESP32 可以在进入深睡前把外设电源切断。

这里有个坑:MOS 管的栅极驱动电平要匹配。AO3401 是 P 沟道,栅极拉低才导通,所以要用一个 NPN 三极管或者 N 沟道 MOS 做电平转换,不能直接用 ESP32 的 GPIO 驱动。我第一版直接接 GPIO,结果 MOS 管一直半导通,电池一晚上就掉了一半。

还有个细节是ADC 测电池电压。用两个电阻分压,把电池电压降到 ESP32 ADC 能测的范围(0-3.3V),然后代码里换算成百分比显示在表盘上。分压电阻选 100K 和 100K,这样静态电流只有几微安,对续航影响可以忽略。

3.4 磁力计和震动马达

磁力计 QMC5883L 走 I2C:

  • VCC 接 3.3V
  • GND 接 GND
  • SDA 接 GPIO 4
  • SCL 接 GPIO 5

磁力计用之前一定要做校准,否则方位角偏差能到几十度。校准方法很简单:把表拿在手里,绕三个轴各转几圈,记录最大最小值,算出偏移量和缩放系数,写进代码里。

震动马达通过一个 NPN 三极管驱动,基极接 GPIO 6,集电极接马达,发射极接地。马达两端要并一个续流二极管,防止反向电动势打坏三极管。

4. 软件架构与核心代码实现

4.1 开发环境搭建

开发环境用 Arduino IDE。装的时候注意几点:

第一,去官网下载最新版 Arduino IDE,然后按arduino esp32开发板的教程装 ESP32 开发板包。国内下载慢的话,可以用离线包,搜arduino ide esp32离线包能找到不少资源。

第二,开发板包版本建议用 2.0.11 或更新的稳定版。我试过几个版本,2.0.11 在 S3 上最稳。装的时候如果卡在下载,可以手动把包放到~/.arduino15/packages/目录下。

第三,装库:e-Paper 库、ArduinoJson 库、QMC5883L 库、ESP32 BLE 库。这些在库管理器里直接搜名字装就行。

提示:如果你在 Windows 上编译 ESP32 项目觉得慢,可以关掉杀毒软件实时扫描,或者把项目目录加到白名单,编译速度能快不少。

4.2 墨水屏刷新策略与代码

墨水屏刷新的核心逻辑是局部刷新为主,全局刷新为辅。下面是我实际用的刷新函数简化版:

void updateTimeDisplay(int hour, int minute) { // 局部刷新,只更新时间区域 display.setPartialWindow(50, 80, 100, 40); display.firstPage(); do { display.fillRect(50, 80, 100, 40, GxEPD_WHITE); display.setCursor(50, 80); display.setTextSize(3); display.print(hour); display.print(":"); if (minute < 10) display.print("0"); display.print(minute); } while (display.nextPage()); } void fullRefresh() { // 全局刷新,清除残影 display.setFullWindow(); display.firstPage(); do { drawWatchFace(); } while (display.nextPage()); }

局部刷新的窗口要精确计算,只覆盖变化的区域。窗口开太大,刷新时间长、耗电多;开太小,内容显示不全。我一般留 2 到 4 个像素的余量。

全局刷新的频率我定的是每小时一次。实测下来,局部刷新连续做 60 次左右,残影会开始明显,这时候来一次全局刷新刚好清掉。

4.3 深度睡眠与唤醒逻辑

续航的核心在深睡。ESP32 深睡的代码大概长这样:

#define BUTTON_PIN 0 void enterDeepSleep() { // 关闭外设电源 digitalWrite(PERIPH_POWER_PIN, HIGH); // 配置唤醒源:按键唤醒 + 定时唤醒 esp_sleep_enable_ext0_wakeup(GPIO_NUM_0, 0); esp_sleep_enable_timer_wakeup(60 * 1000000); // 60秒 // 进入深睡 esp_deep_sleep_start(); }

这里有几个关键点。

唤醒源的选择:按键唤醒用 EXT0,定时唤醒用 timer。按键接 GPIO 0,按下拉低唤醒。定时器设 60 秒,意味着每分钟醒一次更新时间。如果你能接受更长的刷新间隔,比如 5 分钟醒一次,续航还能再拉长。

外设断电:进深睡前一定要把墨水屏、磁力计、马达的电源切断,否则它们会持续耗电。这就是前面 MOS 管电路的作用。

RTC 内存保存状态:深睡会丢失 RAM 里的数据,但 RTC 内存保留。可以把当前时间、番茄钟状态这些存在 RTC 内存里,醒来直接读。

实测这套逻辑跑下来,深睡电流在 10uA 左右,加上每分钟醒一次的瞬时功耗,平均电流能压到 0.7mA 上下。

4.4 蓝牙 BLE 通信实现

蓝牙用 BLE,主要做三件事:同步时间、推送表盘配置、传天气数据。我用的是一个自定义的 GATT 服务,手机端用 nRF Connect 这类通用工具就能调试。

#include <BLEDevice.h> #include <BLEServer.h> #include <BLEUtils.h> BLEServer *pServer; BLECharacteristic *pTimeChar; void setupBLE() { BLEDevice::init("InkWatch"); pServer = BLEDevice::createServer(); BLEService *pService = pServer->createService(SERVICE_UUID); pTimeChar = pService->createCharacteristic( TIME_CHAR_UUID, BLECharacteristic::PROPERTY_WRITE | BLECharacteristic::PROPERTY_READ ); pService->start(); BLEAdvertising *pAdvertising = BLEDevice::getAdvertising(); pAdvertising->addServiceUUID(SERVICE_UUID); pAdvertising->start(); }

注意:BLE 广播和连接期间功耗比深睡高很多,所以我的策略是平时关闭蓝牙,需要同步的时候长按按键开启,同步完自动关闭。如果蓝牙常开,续航直接砍半。

4.5 Wi-Fi 天气更新

天气更新走 HTTP,流程是:开 Wi-Fi → 连路由器 → 请求 API → 解析 JSON → 关 Wi-Fi → 刷新屏幕。

void updateWeather() { WiFi.begin(SSID, PASSWORD); while (WiFi.status() != WL_CONNECTED) { delay(500); } HTTPClient http; http.begin(WEATHER_API_URL); int httpCode = http.GET(); if (httpCode == 200) { String payload = http.getString(); DynamicJsonDocument doc(1024); deserializeJson(doc, payload); currentTemp = doc["temp"]; weatherCode = doc["code"]; } http.end(); WiFi.disconnect(true); WiFi.mode(WIFI_OFF); }

Wi-Fi 从开到关整个过程大概 5 到 8 秒,耗电比较集中。我设的频率是每小时更新一次天气,如果你对天气实时性要求不高,可以改成 3 小时一次,续航还能再涨几天。

5. 功耗优化与续航实测

5.1 各模块功耗拆解

我把各个模块的功耗实测数据整理成表,方便你判断哪里还能优化。

模块工作电流深睡电流占比
ESP32 主控80mA(活跃)10uA主要
墨水屏刷新15mA0(断电)中等
Wi-Fi 联网120mA0(关闭)峰值
BLE 广播30mA0(关闭)中等
磁力计0.1mA0(断电)极小
震动马达60mA0峰值

从表里能看出来,Wi-Fi 和 BLE 是耗电大户,所以策略就是能关就关。墨水屏刷新虽然电流不大,但刷新时间长,累积起来也不少,所以要控制刷新频率。

5.2 实测续航数据

我做了三组测试,都是 500mAh 电池。

第一组:每分钟刷新时间,每小时更新天气,蓝牙常关。实测续航28 天。

第二组:每 5 分钟刷新时间,每 3 小时更新天气,蓝牙常关。实测续航41 天。

第三组:每分钟刷新时间,每小时更新天气,蓝牙常开。实测续航9 天。

所以如果你追求极致续航,把刷新间隔拉长、蓝牙关掉,40 天以上完全没问题。标题里说的 30 天,是正常使用强度的保守值。

5.3 几个省电的骚操作

除了常规的深睡和断电,我还试了几个偏方。

降低 CPU 频率:ESP32 默认 240MHz,在活跃期间可以降到 80MHz,功耗能降三成左右。对表盘渲染这种任务,80MHz 完全够用。

关闭不用的外设时钟:ESP32 有很多外设,用不到的可以在代码里关掉时钟,能省一点是一点。

优化刷新窗口:局部刷新的窗口越小,刷新时间越短,耗电越少。我把时间显示的刷新窗口从 120x50 缩到 100x40,单次刷新时间从 800ms 降到 500ms。

用 RTC 内存代替 Flash 读写:Flash 读写功耗比 RTC 内存高,频繁存状态的话,用 RTC 内存更省。

6. 常见问题与排查实录

6.1 墨水屏花屏、刷新不完整

这是最常见的问题。原因通常有三个:BUSY 引脚没接或没等、SPI 速率太高、供电不稳。

排查顺序:先确认 BUSY 接了并且代码里等了;然后把 SPI 速率降到 4MHz 试试;最后检查电源,墨水屏刷新瞬间电流会跳一下,如果电池电量低或者供电线太细,电压会掉,导致刷新失败。

6.2 蓝牙连不上或频繁断开

搜hc05蓝牙模块连接不上和8852ae蓝牙搜索不到设备的人很多,虽然我用的是 ESP32 内置 BLE,但问题类似。常见原因:手机端缓存了旧的配对信息、BLE 广播间隔太长、服务 UUID 写错。

解决办法:手机端删掉旧配对重新搜;把广播间隔从 100ms 调到 50ms;用 nRF Connect 确认服务 UUID 和特征值 UUID 是否和代码一致。

6.3 深睡后唤醒不了

一般是唤醒源配置错了。EXT0 唤醒要求引脚电平在深睡期间保持稳定,如果按键电路有抖动或者上拉电阻太大,可能唤醒失败。建议按键加 100nF 电容做硬件消抖,上拉电阻用 10K。

6.4 电池电量显示不准

ADC 测电压受负载影响大,刷新屏幕的时候电压会瞬间掉,导致读数偏低。解决办法是在深睡前测电压,这时候负载最小,读数最准。另外分压电阻要用 1% 精度的,普通 5% 的误差太大。

6.5 编译报错和库冲突

Arduino 装多了库容易冲突,尤其是不同版本的 e-Paper 库。建议一个项目一个库版本,别混用。如果编译报错说找不到某个函数,先检查库版本,再检查开发板包版本。

7. 表盘自定义与扩展玩法

7.1 表盘数据结构设计

表盘我用一个结构体数组描述,每个元素代表一个显示项:

struct WatchElement { uint8_t type; // 0=文字 1=图标 2=进度条 uint8_t x, y; // 位置 uint8_t size; // 字号 uint8_t source; // 数据源:0=时间 1=日期 2=电量 3=天气 }; WatchElement watchFace[] = { {0, 50, 80, 3, 0}, // 时间 {0, 60, 130, 1, 1}, // 日期 {2, 150, 180, 1, 2}, // 电量条 {0, 20, 20, 1, 3}, // 天气 };

换表盘就是换这个数组,甚至可以通过蓝牙从手机推过来,存到 Flash 里,重启后加载。

7.2 后续可以加的功能

这块表还有很大扩展空间。比如加个温湿度传感器,做成环境监测手表;加个加速度计,做计步和抬腕亮屏;甚至可以用 ESP32 的边缘 AI 能力做手势识别,挥挥手就切表盘。

如果你玩过arduino智能小车或者arduino控制舵机,这套代码框架也能直接迁移过去,ESP32 的 GPIO 和定时器用法是通用的。

8. 我踩过的几个大坑

第一个坑是墨水屏买错驱动型号。第一次买屏没注意驱动 IC,结果库不兼容,折腾了一周才发现是屏的问题。买屏一定要问清楚驱动型号,然后找对应的例程。

第二个坑是电源电路设计错误。前面提过,MOS 管栅极驱动没做电平转换,导致漏电。这个坑让我白白损失了两块电池的续航测试数据。

第三个坑是深睡唤醒源冲突。我同时用了 EXT0 和 timer 唤醒,结果发现 EXT0 配置会覆盖 timer 配置,导致定时唤醒失效。后来改成只用 timer 唤醒,按键用轮询方式在醒来后检测。

第四个坑是Wi-Fi 连接超时没处理。有次路由器挂了,代码卡在while (WiFi.status() != WL_CONNECTED)死循环里,电池一晚上耗光。后来加了超时机制,连不上就放弃,直接进深睡。

第五个坑是Flash 读写寿命。我一开始把番茄钟状态频繁写 Flash,后来查资料发现 Flash 有擦写次数限制,改成写 RTC 内存才解决。

9. 关于开源和复现的建议

这块表我打算把代码和硬件设计都开源出去。如果你要复现,我的建议是先跑通最小系统:只接 ESP32 和墨水屏,把时间显示跑起来,确认刷新逻辑没问题,再一步步加电池、加传感器、加蓝牙。别一上来就全接上,出了问题很难定位。

另外,墨水屏的刷新策略是这块表的灵魂,建议你多花点时间调这个参数。不同的屏、不同的使用习惯,最优刷新间隔都不一样。我给的 60 秒和 1 小时只是参考值,你可以根据自己的需求调。

最后分享一个我实测有效的小技巧:在墨水屏的 VCC 和 GND 之间并一个 100uF 的电容,能明显改善刷新时的电压波动,减少花屏概率。这个电容不贵,但效果立竿见影。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/30 3:49:24

CI-03烧录失败真相:免唤醒属性与协议握手深度解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/30 3:48:30

ST语言设备模板FB:一套代码覆盖8种机型的复用架构设计

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/30 3:48:11

ATT7022E三相电能计量芯片驱动开发实战:SPI通信与校表流程

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/30 3:48:01

英伟达AI+3D打印技术布局与工业4.0应用

1. 英伟达的3D打印战略布局解析英伟达近年来在3D打印领域的三笔关键投资&#xff0c;揭示了这家AI巨头对智能制造未来的深度布局。作为深耕计算机图形和人工智能的芯片制造商&#xff0c;英伟达正将其在GPU加速计算和AI模拟方面的技术优势&#xff0c;系统性地注入到增材制造这…

作者头像 李华
网站建设 2026/10/30 3:46:49

技术项目命名与版本控制最佳实践

1. 项目背景解析"dragonballz_e212-1"这个看似神秘的代码组合&#xff0c;实际上蕴含着典型的项目命名逻辑。在技术开发领域&#xff0c;这种命名方式通常由项目代号&#xff08;dragonballz&#xff09;和版本标识&#xff08;e212-1&#xff09;组成。就像汽车厂商…

作者头像 李华
网站建设 2026/10/30 3:46:31

C51独立式键盘接口设计:状态机消抖与非阻塞驱动实现

简介&#xff1a;本资源是南昌航空大学C51单片机课程实验三——独立式键盘接口实验的完整工程包&#xff0c;面向单片机初学者、高校电子类专业学生及嵌入式入门实践者&#xff0c;聚焦硬件接口设计与底层驱动编程能力训练。压缩包共16个文件&#xff08;49KB&#xff09;&…

作者头像 李华