
1. 项目缘起与整体设计思路1.1 为什么要做一块“伪”智能手表手上有一块吃灰多年的卡西欧F-91W经典到不能再经典表壳磨花、表带老化但机芯还走得挺准。某天翻零件盒看到几块ESP32-C3开发板突然冒出一个念头能不能把这块老表改造成一块能显示通知、看时间、测温度的“伪智能手表”注意我强调的是“伪”智能——它不需要跑Android Wear不需要触屏不需要心率血氧只要能通过蓝牙接收手机推送、在OLED小屏上显示关键信息、顺便当个手表用就足够了。这个项目的核心定位很清晰用最低的成本、最少的零件、最傻瓜的焊接方式把一块传统电子表改造成具备基础智能功能的可穿戴设备。ESP32-C3自带蓝牙5.0和Wi-FiRISC-V单核160MHz主频深度睡眠功耗可以压到几十微安配合一块小容量锂电池续航做到两三天完全可行。而且它支持Arduino IDE和ESP-IDF两套开发框架对新手友好对老手也够用。适合谁来参考如果你玩过51单片机、会点Arduino、手里有烙铁和万用表这个项目就是为你准备的。如果你完全零基础也没关系我会把每一步拆到“拧螺丝”的粒度包括怎么选电池、怎么焊排针、怎么用Arduino IDE烧录ESP32、怎么调蓝牙功耗。整个项目BOM成本控制在60元以内不含手表本体比市面上任何一款智能手表都便宜一个数量级。1.2 硬件选型背后的取舍逻辑选ESP32-C3而不是ESP32-S3或者ESP32-C6理由很实在。C3是RISC-V架构单核160MHz内置400KB SRAM支持BLE 5.0和Wi-Fi 4关键是它的QFN封装尺寸只有5x5mm开发板普遍做得小巧。S3双核虽然性能强但功耗高、价格贵对于一块只显示文字的小屏幕来说完全是杀鸡用牛刀。C6支持Zigbee和Thread但我暂时用不上而且C6的Arduino生态还不如C3成熟。屏幕选0.96寸SSD1306 OLEDI2C接口128x64分辨率白光或蓝光都行。为什么不用TFT彩屏因为OLED自发光、对比度高、功耗低而且SSD1306的驱动库在Arduino上极其成熟Adafruit_SSD1306和U8g2两个库随便挑一个都能跑。TFT彩屏虽然好看但背光常亮功耗大刷新率要求高对电池不友好。电池选402030规格的锂聚合物电池容量150mAh尺寸4mmx20mmx30mm刚好能塞进F-91W的表壳内部空间。为什么不用更大的电池因为表壳内部空间有限而且ESP32-C3在深度睡眠蓝牙广播模式下的平均电流可以做到1mA左右150mAh理论上能撑6天实际因为蓝牙连接和屏幕刷新两天半到三天是合理预期。充电方案用TP4056模块带保护板的那种输入5V通过Micro USB或Type-C接口输出接电池。TP4056便宜到离谱一块钱一个而且自带过充过放保护。唯一需要注意的是它的充电电流默认是1A对于150mAh电池来说太大了需要把PROG电阻换成2.2kΩ把充电电流降到约500mA或者换成10kΩ降到100mA这样对小电池更友好。1.3 软件架构的极简主义软件层面我坚持一个原则能跑Arduino就不碰ESP-IDF能用现成库就不自己写驱动。主程序基于Arduino框架核心库只有三个Adafruit_SSD1306负责屏幕、NimBLE-Arduino负责蓝牙低功耗、AceButton负责按键消抖。NimBLE比ESP32自带的Bluedroid协议栈轻量得多编译出来固件小一半内存占用也低对于C3这种内存有限的芯片来说非常关键。蓝牙部分实现两个功能一是作为BLE外设暴露一个自定义的GATT服务手机端用nRF Connect或者自己写的App往特征值里写字符串手表收到后显示在屏幕上二是作为BLE HID设备模拟成蓝牙键盘这样可以直接触发手机上的快捷操作比如翻页、播放暂停。不过HID部分我后来砍掉了因为和通知接收功能有冲突而且实际使用频率极低。电源管理是重头戏。ESP32-C3支持多种睡眠模式我用的是esp_deep_sleep_start()配合GPIO唤醒。屏幕在无操作30秒后自动关闭蓝牙在无连接时进入低占空比广播整体平均电流实测在0.8mA到1.2mA之间。这里有个坑Arduino框架下默认的蓝牙库在深度睡眠后无法自动恢复需要手动重新初始化我后面会详细讲怎么处理。2. 核心细节解析与实操要点2.1 卡西欧F-91W的拆解与空间评估拆F-91W只需要一把小号十字螺丝刀。背面四颗螺丝拧下来后盖一撬就开。内部结构比想象中简单一颗CR2016纽扣电池、一块黑色环氧树脂封装的机芯、一个压电蜂鸣片、四个金属按键触点。机芯和LCD是通过斑马条连接的拆的时候要小心斑马条容易移位。关键问题是ESP32-C3开发板能不能塞进去我量了一下内部空间长宽大约32mmx22mm厚度约6mm。市面上最小的ESP32-C3开发板是合宙的C3迷你版尺寸22.5mmx16mm厚度3mm刚好能放进去。但天线部分需要净空区不能紧贴金属表壳否则蓝牙信号衰减严重。我的做法是在表壳内侧贴一层铜箔胶带做屏蔽然后把开发板的天线区域朝向表壳的塑料部分F-91W的表壳是塑料的只有表圈是金属。屏幕怎么装原装的LCD和机芯全部拆掉只保留表壳和按键。0.96寸OLED的模块尺寸是27mmx27mm比表壳内宽还大所以要么换更小的0.91寸OLED128x32分辨率要么把OLED的PCB掰掉一部分。我选的是0.91寸版本尺寸26mmx15mm刚好能横着放进去。分辨率降到128x32后显示内容要精简一行最多显示16个英文字符中文只能显示8个。按键怎么处理F-91W有四个按键左上、左下、右上、右下。原装按键是金属弹片接触PCB上的触点我直接把ESP32的GPIO焊到按键触点上用内部上拉电阻按下时拉低。四个按键分别映射为左上电源开关、左下屏幕切换、右上确认/清除、右下蓝牙配对。注意按键触点和表壳之间要绝缘否则会短路。2.2 ESP32-C3的GPIO分配与外围电路ESP32-C3可用GPIO不多我列一下实际用到的引脚功能GPIO说明OLED SDAGPIO5I2C数据线接4.7kΩ上拉到3.3VOLED SCLGPIO6I2C时钟线接4.7kΩ上拉到3.3V按键左上GPIO0内部上拉按下拉低按键左下GPIO1内部上拉按下拉低按键右上GPIO2内部上拉按下拉低按键右下GPIO3内部上拉按下拉低电池电压检测GPIO4通过100kΩ100kΩ分压蓝牙状态LEDGPIO10板载LED低电平点亮注意GPIO0和GPIO1在ESP32-C3上是 strapping 引脚上电时的电平会影响启动模式。GPIO0拉低会进入下载模式所以按键左上不能在上电时被按住否则芯片不启动。我的解决办法是在GPIO0和按键之间串一个10kΩ电阻同时并联一个0.1uF电容到地这样上电瞬间GPIO0被电容拉高不会误触发下载模式。电池电压检测用两个100kΩ电阻分压因为ESP32-C3的ADC参考电压是1.1V而锂电池满电是4.2V分压后是2.1V超过了参考电压。所以实际用的是100kΩ33kΩ分压分压比是0.2484.2V分压后是1.04V刚好在量程内。ADC读取后用analogReadMilliVolts()转换成毫伏再乘以分压比的倒数就是电池电压。I2C上拉电阻不能省。SSD1306模块上通常自带4.7kΩ上拉但有些廉价模块为了省成本会省略。我实测过没有上拉电阻时I2C通信时好时坏尤其是电池电压降到3.3V以下时屏幕会花屏。所以我在SDA和SCL上各焊了一个4.7kΩ贴片电阻到3.3V问题彻底解决。2.3 蓝牙通知接收的实现细节BLE通知接收的核心是定义一个自定义GATT服务。我用NimBLE-Arduino库定义一个UUID为0000ffe0-0000-1000-8000-00805f9b34fb的服务里面包含一个可写特征值0000ffe1-...。手机端往这个特征值写字符串ESP32收到后触发回调函数把字符串存到一个全局缓冲区主循环再刷新到屏幕上。这里有个关键点BLE回调函数运行在蓝牙协议栈的任务里不能在里面做耗时操作比如刷屏幕、写Flash。我的做法是在回调里只做一件事把数据拷贝到缓冲区然后设置一个标志位。主循环检测到标志位后关闭蓝牙广播、刷新屏幕、再重新打开广播。这样虽然增加了延迟大约200ms但避免了协议栈崩溃。字符串编码要注意。手机端发过来的可能是UTF-8而SSD1306库默认只支持ASCII。中文会显示成乱码。解决办法是用U8g2库代替Adafruit_SSD1306U8g2支持UTF-8和中文点阵字体但代价是固件体积增加约30KB。ESP32-C3的Flash通常是4MB完全放得下。我用的是U8g2的u8g2_font_wqy12_t_gb2312字体12像素高能显示简体中文。蓝牙连接参数也要调。默认的连接间隔是30ms对于通知接收来说太频繁了功耗高。我在手机端nRF Connect把连接间隔改成200ms从机延迟设为4这样ESP32大部分时间在睡眠只有每200ms醒来一次收数据。实测平均电流从2.1mA降到了0.9mA。2.4 电源管理与深度睡眠的坑ESP32-C3的深度睡眠电流理论值是5uA但实际做出来很难低于50uA因为开发板上还有LDO、USB转串口芯片、LED等外围器件在耗电。我的做法是直接给ESP32-C3模组供电绕过开发板上的LDO和USB芯片。具体操作是把开发板上的3.3V LDO输出脚和模组VDD脚之间的走线割断然后从TP4056的输出直接接一个低静态电流的LDO比如HT7333静态电流4uA给模组供电。深度睡眠唤醒源用GPIO0左上按键配置为低电平唤醒。唤醒后需要重新初始化屏幕和蓝牙因为深度睡眠会关闭所有外设电源。这里有个坑Arduino框架下esp_deep_sleep_start()之后程序会从头开始执行setup()但蓝牙协议栈的初始化需要先调用nimble_port_deinit()清理否则第二次初始化会失败。我的做法是在setup()开头判断esp_sleep_get_wakeup_cause()如果是深度睡眠唤醒先执行一次蓝牙反初始化。屏幕关闭用SSD1306的ssd1306_command(SSD1306_DISPLAYOFF)电流从20mA降到10uA。但注意关闭显示后I2C通信仍然可用下次唤醒时发SSD1306_DISPLAYON即可。不要用display.clearDisplay()再display.display()那样只是清空显存屏幕背光还在耗电。3. 实操过程与核心环节实现3.1 硬件焊接与组装步骤第一步拆表。拧下F-91W后盖四颗螺丝取出机芯和电池保留表壳、表圈、四个按键和表带。用酒精棉签清理表壳内部残留的胶水和灰尘。第二步改造表壳。在表壳内侧底部贴一层0.5mm厚的绝缘胶带聚酰亚胺胶带防止PCB短路。然后在表壳侧面用2mm钻头打一个孔位置选在表壳右下角用于走充电线。孔打完后用圆锉修整边缘穿入一根30AWG硅胶线作为充电正负极。第三步焊接ESP32-C3模组。我用的是合宙C3迷你版直接焊在洞洞板上。洞洞板裁成22mmx16mm大小刚好放进表壳。焊接顺序先焊模组再焊OLED排针再焊按键飞线最后焊电池和充电线。注意OLED的排针要弯成90度否则高度超了盖不上后盖。第四步连接按键。F-91W的按键触点在PCB上用万用表蜂鸣档找出每个按键对应的两个焊盘。从焊盘飞线到ESP32的GPIO0-GPIO3线长控制在3cm以内避免引入干扰。每个按键线串一个100Ω电阻防止静电击穿GPIO。第五步安装电池。402030电池用双面胶固定在表壳背面注意不要压在按键排线上。电池正负极焊到TP4056模块的B和B-TP4056的OUT和OUT-接到LDO输入。TP4056模块太小放不进表壳我把它外置在一个小塑料盒里通过充电线连接手表。第六步合盖测试。先不拧螺丝用杜邦线临时连接电池上电看屏幕是否点亮。如果屏幕不亮先查I2C地址SSD1306默认地址是0x3C用I2C扫描程序确认。如果地址对但屏幕不亮查上拉电阻和供电电压。3.2 Arduino IDE环境配置与烧录Arduino IDE版本用2.3.2太老的版本对ESP32-C3支持不好。在“首选项”的“附加开发板管理器网址”里填入ESP32的板管理器地址然后在“开发板管理器”里搜索“esp32”安装最新版我写这篇文章时是3.0.7。安装完成后在“工具”-“开发板”里选“ESP32C3 Dev Module”。关键配置项USB CDC On Boot: Enabled这样串口监视器才能看到输出CPU Frequency: 160MHzFlash Mode: QIO 80MHzFlash Size: 4MBPartition Scheme: Default 4MB with spiffsUpload Speed: 921600烧录时如果遇到“Failed to connect to ESP32-C3: Timed out waiting for packet header”按住BOOT键再点上传看到“Connecting...”后松开。或者检查GPIO0是否被按键拉低如果是先断开按键再烧录。离线包的问题如果你在无网络环境可以用arduino-cli提前下载好ESP32核心包。命令是arduino-cli core install esp32:esp32下载完成后整个包在~/.arduino15/packages/esp32目录下拷贝到目标机器同样位置即可。PlatformIO用户可以用pio pkg install --platform espressif32离线包在.platformio/packages里。3.3 核心代码解析先看主程序框架#include U8g2lib.h #include NimBLEDevice.h #include AceButton.h U8G2_SSD1306_128X32_UNIVISION_F_HW_I2C u8g2(U8G2_R0, U8X8_PIN_NONE); NimBLEServer *pServer; NimBLECharacteristic *pCharacteristic; char notifyBuf[64] {0}; volatile bool newNotify false; void setup() { esp_sleep_wakeup_cause_t cause esp_sleep_get_wakeup_cause(); if (cause ESP_SLEEP_WAKEUP_GPIO) { // 从深度睡眠唤醒先反初始化蓝牙 nimble_port_deinit(); } u8g2.begin(); u8g2.setFont(u8g2_font_wqy12_t_gb2312); initBLE(); initButtons(); initPowerManagement(); } void loop() { if (newNotify) { u8g2.clearBuffer(); u8g2.drawUTF8(0, 12, notifyBuf); u8g2.sendBuffer(); newNotify false; } checkButtons(); checkBattery(); if (idleTime 30000) { enterDeepSleep(); } }BLE回调部分class NotifyCallback : public NimBLECharacteristicCallbacks { void onWrite(NimBLECharacteristic *pChar) { std::string value pChar-getValue(); if (value.length() 0 value.length() 64) { strncpy(notifyBuf, value.c_str(), 63); newNotify true; } } };深度睡眠函数void enterDeepSleep() { u8g2.clearBuffer(); u8g2.sendBuffer(); u8g2.setPowerSave(1); esp_sleep_enable_gpio_wakeup(GPIO_NUM_0, ESP_GPIO_WAKEUP_GPIO_LOW); esp_deep_sleep_start(); }按键处理用AceButton库配置如下AceButton btn1(GPIO_NUM_0); AceButton btn2(GPIO_NUM_1); AceButton btn3(GPIO_NUM_2); AceButton btn4(GPIO_NUM_3); void initButtons() { btn1.setEventHandler(handleButton); btn2.setEventHandler(handleButton); btn3.setEventHandler(handleButton); btn4.setEventHandler(handleButton); pinMode(GPIO_NUM_0, INPUT_PULLUP); pinMode(GPIO_NUM_1, INPUT_PULLUP); pinMode(GPIO_NUM_2, INPUT_PULLUP); pinMode(GPIO_NUM_3, INPUT_PULLUP); }电池电压检测float readBatteryVoltage() { int raw analogRead(GPIO_NUM_4); float voltage analogReadMilliVolts(GPIO_NUM_4) / 1000.0; return voltage * (100.0 33.0) / 33.0; }3.4 手机端配置与联调手机端我用的是nRF Connect安卓和LightBlueiOS。打开App扫描BLE设备应该能看到名为“CASIO-ESP32”的设备。连接后找到UUID为0000ffe0-...的服务点开里面的0000ffe1-...特征值点“写”图标输入要显示的文本点发送。手表屏幕上应该立刻显示出来。如果要实现自动通知转发需要装一个叫“Notification Forwarder”的App安卓它可以把微信、短信、来电等通知自动转发到指定的BLE特征值。配置方法在App里添加一个BLE设备选择CASIO-ESP32选择服务UUID和特征值UUID然后勾选要转发的App。iOS因为系统限制第三方App无法直接读取其他App的通知所以只能用快捷指令手动触发。联调时常见问题手机搜不到设备。先确认ESP32的蓝牙是否在广播用串口打印NimBLEDevice::getAdvertising()-isAdvertising()。如果没在广播检查initBLE()里是否调用了pServer-getAdvertising()-start()。如果广播了但搜不到可能是天线问题把手机贴近手表再试。4. 常见问题与排查技巧实录4.1 屏幕不亮或花屏这是最高频的问题。排查顺序如下现象可能原因解决方法完全不亮供电不足万用表测OLED VCC脚应为3.3V完全不亮I2C地址错误跑I2C扫描程序确认地址是0x3C还是0x3D花屏上拉电阻缺失SDA/SCL各加4.7kΩ上拉到3.3V花屏线太长I2C线缩短到5cm以内显示一半显存溢出检查u8g2.clearBuffer()是否在sendBuffer()前调用闪烁刷新率过高降低刷新频率或改用u8g2.setPowerSave(0)我踩过最坑的一次是屏幕偶尔亮偶尔不亮查了半天发现是OLED排针虚焊。用放大镜看焊点表面有裂纹重新补焊后问题消失。所以焊接完一定要用万用表蜂鸣档测每个引脚的通断。4.2 蓝牙连接不稳定蓝牙断连通常有三个原因天线净空不够、电源纹波大、连接参数太激进。天线净空ESP32-C3模组的天线区域下方和周围不能有金属。我把模组放在表壳最底部天线朝向表壳的塑料后盖中间隔了2mm的绝缘胶带。实测信号强度从-85dBm提升到-65dBm。电源纹波锂电池直接供电时纹波很小但经过TP4056和LDO后可能引入噪声。我在LDO输出端并了一个10uF钽电容和一个0.1uF陶瓷电容蓝牙断连次数从每小时3次降到几乎为零。连接参数手机端默认的连接间隔可能是7.5ms太快了。在nRF Connect里把Connection Interval改成200msSlave Latency改成4Supervision Timeout改成4000ms。这样ESP32每200ms才需要响应一次功耗和稳定性都大幅改善。4.3 深度睡眠后无法唤醒深度睡眠唤醒失败通常是GPIO配置问题。ESP32-C3的GPIO唤醒需要满足两个条件一是GPIO在睡眠前配置为输入并使能上拉二是唤醒电平要匹配。我用的是低电平唤醒配置如下pinMode(GPIO_NUM_0, INPUT_PULLUP); esp_deep_sleep_enable_gpio_wakeup(1ULL GPIO_NUM_0, ESP_GPIO_WAKEUP_GPIO_LOW); esp_deep_sleep_start();注意esp_deep_sleep_enable_gpio_wakeup的第一个参数是位掩码1ULL GPIO_NUM_0表示只使能GPIO0。如果写成GPIO_NUM_0值为0掩码就是1也能工作但可读性差。另一个坑是如果GPIO0在睡眠期间被持续拉低比如按键卡住ESP32会反复唤醒又立即进入睡眠电池很快耗尽。我的做法是在唤醒后检查按键是否仍然按下如果是延时500ms再进入睡眠给用户松开按键的时间。4.4 电池续航不达预期标称150mAh电池理论续航6天实际只有2天差了三倍。用万用表串联测电流发现平均电流是3.1mA而不是预期的1mA。排查发现三个耗电大户第一OLED屏幕虽然调用了setPowerSave(1)但SSD1306的电荷泵仍在工作电流约0.5mA。解决办法是直接发SSD1306_DISPLAYOFF命令电流降到10uA。第二蓝牙广播间隔默认是100ms改成1000ms后平均电流从1.2mA降到0.3mA。广播间隔在NimBLEDevice::getAdvertising()-setMinInterval(1600)里设置单位是0.625ms1600对应1000ms。第三LDO静态电流。我一开始用的AMS1117静态电流5mA比整个系统还高。换成HT7333后静态电流4uA续航直接翻倍。所以选LDO一定要看静态电流参数不要只看输出电流。4.5 烧录失败与下载模式ESP32-C3烧录失败最常见的原因是没进入下载模式。正常烧录时Arduino IDE会通过DTR和RTS自动控制GPIO0和EN脚但有些USB转串口芯片比如CH340不支持自动复位需要手动操作。手动进入下载模式按住BOOT键GPIO0拉低点一下RST键EN拉低再拉高然后松开BOOT键。此时芯片进入下载模式串口会打印“waiting for download”。然后点Arduino IDE的上传按钮烧录完成后按RST键重启。如果串口一直显示“Connecting...”检查USB线是否支持数据传输有些线只能充电。换一根线试试或者换一个USB口。Windows用户如果遇到“无法识别的USB设备”装一下CP210x或CH340的驱动。4.6 常见问题速查表问题排查第一步排查第二步排查第三步屏幕不亮测VCC电压跑I2C扫描检查上拉电阻蓝牙搜不到串口看广播状态检查天线净空换手机测试续航短串联测电流查LDO静态电流查广播间隔烧录失败手动进下载模式换USB线检查GPIO0电平按键失灵万用表测通断检查上拉配置检查串阻中文乱码换U8g2库确认UTF-8编码检查字体设置深度睡眠不唤醒查GPIO掩码查上拉配置查唤醒电平电池检测不准查分压电阻查ADC参考电压校准analogReadMilliVolts5. 进阶玩法与扩展思路5.1 加入温湿度传感器ESP32-C3没有内置温湿度传感器但可以外接SHT30或AHT20都是I2C接口和OLED共用总线。SHT30精度±0.3°CAHT20精度±0.3°C价格都在5块钱以内。接线很简单VCC接3.3VGND接地SDA和SCL分别并联到OLED的SDA和SCL。I2C地址不冲突SHT30是0x44AHT20是0x38OLED是0x3C。代码上用SHT31库或者AHT20库每10秒读一次显示在屏幕第二行。注意读传感器时要关闭屏幕的电荷泵否则I2C总线上的噪声会影响传感器读数。我的做法是读传感器前先setPowerSave(1)读完再setPowerSave(0)。5.2 蓝牙HID快捷控制虽然我砍掉了HID功能但如果你需要可以这样实现用ESP32-BLE-Keyboard库把ESP32模拟成蓝牙键盘。按键映射为左上音量加、左下音量减、右上播放暂停、右下下一曲。这样手表就变成了一个蓝牙遥控器控制手机音乐播放。注意HID和自定义GATT服务不能同时存在因为BLE协议栈只允许一个HID服务。解决办法是分时复用长按某个按键切换模式HID模式下不接收通知通知模式下不发送HID。切换时需要重新初始化BLE协议栈耗时约500ms。5.3 低功耗优化到极致如果你想把续航做到一周以上可以试试这些招第一把CPU频率从160MHz降到80MHz。在Arduino IDE里选“80MHz”功耗降低约30%性能足够跑OLED和BLE。第二关闭Wi-Fi。ESP32-C3的Wi-Fi和蓝牙共用射频即使不连接Wi-Fi射频前端也在耗电。在setup()里调用WiFi.mode(WIFI_OFF)电流降低约0.2mA。第三用RTC定时器唤醒代替GPIO唤醒。RTC定时器可以在深度睡眠期间以极低功耗运行每60秒唤醒一次刷新屏幕其他时间完全断电。这样平均电流可以压到0.1mA以下150mAh电池能撑两个月。但代价是蓝牙会断开需要重新连接。第四换用ESP32-C6的802.15.4模式。C6支持Thread和Zigbee在睡眠模式下的功耗比BLE更低。不过C6的Arduino支持还不完善需要等生态成熟。5.4 3D打印表壳原装F-91W表壳空间太局促如果你有3D打印机可以自己设计一个表壳把ESP32-C3、OLED、电池、充电模块全部集成进去。我用Fusion 360画了一个尺寸40mmx35mmx12mm比原装表壳大一圈但内部空间充裕可以放200mAh电池和0.96寸OLED。打印材料用PETG耐温70°C比PLA更适合可穿戴设备。表带用20mm宽的硅胶带用弹簧棒连接表壳。屏幕窗口用1mm厚的亚克力板用UV胶粘在表壳上。按键用6mmx6mm的轻触开关焊在PCB上从表壳侧面伸出。这个方案的好处是不用破坏原装手表所有零件都是标准件坏了随时换。缺点是体积大戴在手上像个小砖头。所以我的建议是如果你追求便携就用原装表壳如果你追求功能和可维护性就3D打印。5.5 接入Home AssistantESP32-C3可以通过Wi-Fi接入Home Assistant把手表变成智能家居的遥控器。用ESPHome框架配置一个二进制传感器读取按键状态一个传感器读取电池电压一个文本传感器显示通知。然后在Home Assistant里写自动化按左上键开灯、按左下键关灯、按右上键切换场景。ESPHome的配置很简单esphome: name: casio-watch esp32: board: esp32-c3-devkitm-1 framework: type: arduino binary_sensor: - platform: gpio pin: GPIO0 name: Button Top Left sensor: - platform: adc pin: GPIO4 name: Battery Voltage attenuation: 11db烧录ESPHome固件后手表会自动出现在Home Assistant的集成里。注意ESPHome和Arduino代码不能共存需要二选一。如果你主要用Home Assistant就刷ESPHome如果你主要用蓝牙通知就刷Arduino。6. 个人实操心得与避坑清单6.1 焊接方面的经验焊ESP32-C3模组时烙铁温度调到320°C用0.5mm含铅焊锡不要用无铅的无铅熔点高容易烫坏模组。焊之前先在焊盘上涂一点助焊膏焊完用洗板水清洗否则残留的助焊膏会吸潮导致漏电。飞线用30AWG硅胶线比漆包线好焊而且耐弯折。线长控制在5cm以内太长会引入天线效应影响蓝牙信号。每根线焊完后用热熔胶固定在PCB上防止拉扯时焊盘脱落。OLED排针不要直接焊在PCB上先用排母焊在PCB上再把OLED插上去。这样如果OLED坏了可以直接拔下来换不用重新焊接。6.2 电池方面的经验锂电池不要买太便宜的我买过一批3块钱的402030标称150mAh实测只有80mAh。后来换了一家卖7块钱的实测容量145mAh一致性也好很多。电池买回来先用充电器充到4.2V再放电到3.0V循环三次激活一下。TP4056的PROG电阻一定要换。默认1.2kΩ对应1000mA充电电流对150mAh电池来说太大了电池会发热甚至鼓包。换成10kΩ对应100mA充电时间约1.5小时电池温温的很安全。电池保护板不能省。有些电池自带保护板有些没有。如果没有一定要在TP4056输出和电池之间加一个DW018205A的保护板防止过放。过放一次锂电池就废了内阻变大容量减半。6.3 软件方面的经验Arduino IDE的串口监视器波特率设成115200和代码里的Serial.begin(115200)一致。如果看到乱码检查波特率是否匹配。ESP32-C3的USB CDC串口在Arduino IDE 2.x里有时会抽风换用1.8.19版本更稳定。NimBLE-Arduino库的版本很重要。我用的是1.4.1比较稳定。2.x版本API变了NimBLEDevice::init()要改成NimBLEDevice::init()而且内存占用更大。如果你用2.x遇到编译错误降级到1.4.1试试。U8g2库的字体选择要权衡。u8g2_font_wqy12_t_gb2312支持中文但占用约30KB Flash。如果你只用英文用u8g2_font_6x10_tf占用不到2KB。Flash空间紧张的话可以用u8g2_font_unifont_t_symbols支持部分符号和拉丁字母。6.4 结构方面的经验表壳内部所有金属部分都要绝缘。F-91W的表圈是金属的如果PCB碰到表圈会短路。我在表圈内侧贴了一圈绝缘胶带PCB用双面胶固定在胶带上。屏幕和表壳之间要加一层黑色泡棉防止漏光。OLED的视角有限如果不加泡棉从侧面看会有光晕很难看。泡棉厚度1mm用双面胶粘在屏幕边缘。充电接口用磁吸的更方便。我在表壳侧面焊了两个铜触点配合一个磁吸充电线不用每次插拔Micro USB。磁吸充电线的正负极不要接反接反了会烧TP4056。我在触点上刻了“”和“-”标记防止搞错。6.5 最后的避坑清单不要用ESP32-S3做这个项目功耗太高电池撑不住。不要用AMS1117静态电流太大换HT7333或XC6206。不要省略I2C上拉电阻否则屏幕会随机花屏。不要在BLE回调里刷屏幕会死机。不要在深度睡眠前不反初始化蓝牙第二次初始化会失败。不要用太长的飞线蓝牙信号会变差。不要买太便宜的电池容量虚标严重。不要忘记在GPIO0上串电阻否则上电会进下载模式。不要用无铅焊锡焊模组熔点太高容易烫坏。不要忘记在表壳内侧贴绝缘胶带短路会烧芯片。这个项目我从起念头到做完用了三个周末中间烧了一块ESP32-C3GPIO0没串电阻上电进下载模式我以为坏了其实是正常的废了两块OLED排针焊反了通电就冒烟换了三次电池前两次容量虚标。但最后做出来的东西确实能用戴在手上别人以为是卡西欧出了新款智能表。这种“伪”智能的乐趣在于你知道它里面是什么但别人不知道这种信息差本身就是一种乐趣。