2026/9/2 23:39:10

基于ESP32的智能手表DIY:从硬件选型到代码实现的完整开源教程

基于ESP32的智能手表DIY:从硬件选型到代码实现的完整开源教程 最近在折腾物联网项目时发现很多朋友对用ESP32这类低成本、高性能的Wi-Fi/蓝牙双模芯片制作智能穿戴设备很感兴趣。市面上的智能手表功能虽多但底层逻辑和二次开发空间有限。本文将分享一个基于ESP32的多功能手表开源项目并附上详细的复刻教程。从硬件选型、环境搭建、核心代码解析到功能调试手把手带你打造一款属于自己的智能手表涵盖时间显示、心率监测、天气获取等实用功能。无论你是物联网初学者还是想深入了解ESP32开发的爱好者都能从本文获得一套完整、可落地的解决方案。1. 项目背景与核心概念1.1 什么是ESP32ESP32是乐鑫Espressif推出的一款高度集成的低功耗、低成本Wi-Fi和蓝牙双模系统级芯片SoC。它内置两个高性能的Xtensa® 32位LX6微处理器核心主频高达240MHz拥有丰富的外设接口如GPIO、ADC、DAC、I2C、I2S、SPI、UART等。其强大的无线连接能力、充足的运算资源以及活跃的开源社区使其成为物联网IoT项目尤其是DIY智能硬件的热门选择。1.2 为什么用ESP32做手表传统智能手表方案如Nordic nRF系列成本较高且开发门槛不低。ESP32则提供了极具性价比的替代方案成本极低核心开发板价格仅二三十元极大降低了DIY门槛。功能强大双核处理器性能足以流畅驱动显示屏、处理传感器数据并运行轻量级图形界面GUI。连接便利内置Wi-Fi可轻松连接网络获取天气、同步时间NTP蓝牙BLE可用于连接手机、传输健康数据或充当遥控器。生态丰富支持Arduino框架、ESP-IDF乐鑫官方IoT开发框架有海量的库和教程支持开发效率高。可扩展性强丰富的IO口便于连接心率传感器、加速度计、GPS模块、SD卡等实现功能定制。1.3 项目目标与功能概览本项目旨在打造一款开源、可定制、功能丰富的ESP32智能手表。核心功能设计如下基础功能高精度时间显示支持网络对时、电池电量监测。健康监测集成心率血氧传感器实现实时心率监测。环境感知通过Wi-Fi获取当地天气信息温度、湿度、天气状况。信息提醒通过蓝牙接收手机通知需配套App。扩展接口预留SD卡接口用于存储音乐、图片支持振动马达、按键控制。OTA升级支持通过网络进行固件无线升级方便后续功能迭代。2. 硬件准备与物料清单复刻本项目你需要准备以下硬件。大部分模块在主流电商平台均可购得。2.1 核心控制器ESP32开发板推荐使用ESP32-S3或ESP32-WROOM-32E模组的开发板。ESP32-S3性能更强IO更多更适合图形应用。本文以通用的ESP32开发板为例。关键参数需支持Wi-Fi、蓝牙具备足够的GPIO至少20个可用。2.2 显示模块TFT液晶显示屏这是手表的“脸”。推荐使用带驱动IC如ST7789、ILI9341的SPI接口屏幕尺寸在1.3寸至1.8寸之间分辨率240x240或240x320。型号示例1.54英寸 IPS TFT LCD (ST7789V, 240x240)。接口SPI节省IO口。2.3 传感器模块心率血氧传感器用于健康监测。MAX30102是一款常用的集成式传感器通过I2C接口通信可测量心率和血氧饱和度。加速度计/陀螺仪用于计步、抬腕亮屏等。MPU6050六轴或MPU9250九轴是常见选择通过I2C通信。2.4 电源与结构锂电池3.7V聚合物锂电池容量建议在500mAh以上确保续航。充电管理模块TP4056等单节锂电池充电模块负责给电池安全充电。升压稳压模块由于ESP32和屏幕需要3.3V供电而电池电压在3.7V-4.2V之间波动需要一个稳定的3.3V输出。可使用AMS1117-3.3等LDO或具有更高效率的DC-DC降压模块。振动马达小型扁平振动马达用于消息提醒。表壳与表带可使用3D打印自制或购买现成的DIY手表外壳套件。其他杜邦线、面包板用于前期测试、焊接工具、万用表。2.5 完整物料清单参考模块推荐型号接口备注主控ESP32 DevKit V1-核心处理单元屏幕1.54″ IPS (ST7789)SPI显示界面心率MAX30102I2C监测心率和血氧运动MPU6050I2C检测运动和姿态电池3.7V 600mAh锂电池-供电充电TP4056模块-充电管理稳压AMS1117-3.3模块-提供稳定3.3V马达1020振动马达GPIO触觉反馈按键轻触开关 x2GPIO功能控制3. 软件开发环境搭建我们将使用Arduino IDE进行开发因为它对初学者友好库生态丰富。当然你也可以使用PlatformIOVSCode插件或乐鑫官方的ESP-IDF。3.1 安装Arduino IDE访问 Arduino官网 下载并安装最新版Arduino IDE。启动Arduino IDE打开“文件” - “首选项”。在“附加开发板管理器网址”中添加以下网址如果已有其他用逗号分隔https://espressif.github.io/arduino-esp32/package_esp32_index.json点击“确定”保存。3.2 安装ESP32开发板支持打开“工具” - “开发板” - “开发板管理器”。在搜索框中输入“esp32”。找到由“Espressif Systems”发布的“esp32”开发板包点击“安装”。安装过程可能需要几分钟并会下载相关工具链。3.3 安装必要的库文件本项目需要依赖多个第三方库来驱动硬件和实现功能。打开“项目” - “加载库” - “管理库”搜索并安装以下库TFT_eSPI用于驱动ST7789等TFT屏幕的强大图形库。Adafruit MAX30102 library用于驱动MAX30102心率血氧传感器。Adafruit MPU6050用于驱动MPU6050加速度计陀螺仪。NTPClient用于从网络时间协议NTP服务器获取时间。ArduinoJson用于解析从网络API获取的JSON格式天气数据。WiFi和HTTPClientESP32内置用于网络连接和HTTP请求。安装完成后需要特别配置TFT_eSPI库在Arduino的库安装目录下例如C:\Users\你的用户名\Documents\Arduino\libraries找到TFT_eSPI文件夹。进入该文件夹找到User_Setup.h文件用文本编辑器打开。根据你的屏幕型号和接线注释掉不用的驱动并取消注释你所用驱动的定义。例如对于ST7789 240x240屏幕你可能需要确保以下行被启用#define ST7789_DRIVER并正确设置屏幕的宽高、引脚等。库中通常有多个User_Setup_Select.h示例文件你可以直接复制对应示例的内容来替换User_Setup.h这是最稳妥的方法。4. 硬件电路连接详解在将模块焊接或装入表壳前强烈建议先在面包板上完成所有连接和测试。以下是基于常见模块的接线示意图GPIO号以ESP32 DevKit V1为例请根据你的板子调整。4.1 显示屏 (ST7789 SPI) 连接屏幕引脚ESP32 GPIO功能说明VCC3.3V电源正极GNDGND电源地SCL/SCKGPIO 18SPI时钟线SDA/MOSIGPIO 23SPI数据线主出从入RES/RSTGPIO 4复位引脚可选可接ESP32 RSTDC/RS/A0GPIO 2数据/命令选择引脚CSGPIO 15片选引脚低电平有效BL3.3V (或通过GPIO控制)背光控制常接3.3V常亮4.2 传感器 (MAX30102 MPU6050 I2C) 连接MAX30102和MPU6050可以共享I2C总线。传感器引脚ESP32 GPIO功能说明公共I2C总线SCLGPIO 22I2C时钟线SDAGPIO 21I2C数据线MAX30102VIN3.3V电源注意有些模块需3.3VGNDGND电源地MPU6050VCC3.3V电源GNDGND电源地(INT)未连接或GPIO中断引脚本项目暂未使用4.3 电源系统连接锂电池正极 - TP4056模块 BAT 锂电池负极 - TP4056模块 BAT- 及 系统GND TP4056模块 OUT - AMS1117-3.3模块 IN TP4056模块 OUT- - 系统GND AMS1117-3.3模块 OUT - ESP32 3.3V、屏幕VCC、传感器VCC AMS1117-3.3模块 GND - 系统GND注意TP4056的OUT是电池电压~4.2V-3.7V不能直接给ESP32供电必须经过降压稳压到3.3V。4.4 其他外设连接振动马达一端接GPIO如GPIO 25另一端接GND。需要通过一个三极管或MOS管驱动因为GPIO驱动电流有限。按键一端接GPIO如GPIO 34, 35另一端接GND。GPIO需设置为上拉输入模式按键按下时读到低电平。5. 核心软件功能实现与代码解析我们将分模块讲解核心代码逻辑。请先在Arduino IDE中创建一个新项目。5.1 基础工程结构与头文件首先包含所有必要的库并定义引脚。// ESP32_MultiWatch.ino #include TFT_eSPI.h #include SPI.h #include Wire.h #include “MAX30105.h” // 注意Adafruit库中MAX30102用的是MAX30105.h #include “heartRate.h” #include Adafruit_MPU6050.h #include Adafruit_Sensor.h #include WiFi.h #include HTTPClient.h #include ArduinoJson.h #include NTPClient.h #include WiFiUdp.h // 引脚定义 #define TFT_CS 15 #define TFT_DC 2 #define TFT_RST 4 #define TFT_BL 13 // 背光控制 #define VIBRATE_PIN 25 #define BUTTON_A 34 #define BUTTON_B 35 // 全局对象 TFT_eSPI tft TFT_eSPI(); MAX30105 particleSensor; Adafruit_MPU6050 mpu; WiFiUDP ntpUDP; NTPClient timeClient(ntpUDP, “ntp1.aliyun.com”, 8*3600, 60000); // 使用阿里云NTP东八区 // 全局变量 char ssid[] “Your_WiFi_SSID”; // 你的Wi-Fi名称 char pass[] “Your_WiFi_Password”; // 你的Wi-Fi密码 int heartRate 0; bool heartRateDetected false; float temperature 0.0; String weatherText “Sunny”; void setup() { Serial.begin(115200); initDisplay(); initSensors(); connectWiFi(); initTime(); // 其他初始化... } void loop() { updateTimeDisplay(); updateHeartRate(); updateWeatherPeriodically(); // 每隔一段时间更新 handleButtons(); // 主循环逻辑... }5.2 显示屏驱动与UI框架TFT_eSPI库功能强大我们先初始化屏幕并绘制一个简单的静态界面框架。void initDisplay() { pinMode(TFT_BL, OUTPUT); digitalWrite(TFT_BL, HIGH); // 打开背光 tft.init(); tft.setRotation(1); // 根据屏幕方向调整0-3 tft.fillScreen(TFT_BLACK); tft.setTextColor(TFT_WHITE, TFT_BLACK); // 绘制顶部状态栏时间、电量 tft.fillRect(0, 0, 240, 20, TFT_NAVY); tft.setTextDatum(TC_DATUM); // 文本对齐方式顶部居中 tft.drawString(“--:--”, 120, 2, 4); // 时间占位符 // 绘制主内容区 // 心率区域 tft.drawRect(10, 30, 100, 60, TFT_GREEN); tft.drawString(“HR”, 60, 35, 2); tft.drawString(“—“, 60, 55, 4); // 天气区域 tft.drawRect(130, 30, 100, 60, TFT_CYAN); tft.drawString(“Weather”, 180, 35, 2); tft.drawString(“N/A”, 180, 55, 2); } void updateTimeDisplay() { timeClient.update(); String formattedTime timeClient.getFormattedTime(); tft.setTextDatum(TC_DATUM); tft.setTextColor(TFT_WHITE, TFT_NAVY); tft.drawString(formattedTime, 120, 2, 4); }5.3 心率监测功能实现MAX30102传感器需要先进行配置然后在循环中持续读取数据并计算心率。void initSensors() { // 初始化I2C Wire.begin(21, 22); // SDA, SCL // 初始化MAX30102 if (!particleSensor.begin(Wire, I2C_SPEED_FAST)) { Serial.println(“MAX30102 not found. Check wiring/power.“); while (1); } // 配置传感器 particleSensor.setup(); particleSensor.setPulseAmplitudeRed(0x0A); // 设置红光LED亮度 particleSensor.setPulseAmplitudeIR(0x0A); // 设置红外LED亮度 // 初始化MPU6050 if (!mpu.begin()) { Serial.println(“MPU6050 not found. Check wiring/power.“); while (1); } mpu.setAccelerometerRange(MPU6050_RANGE_2_G); Serial.println(“All sensors initialized.”); } void updateHeartRate() { long irValue particleSensor.getIR(); if (checkForBeat(irValue) true) { // 检测到心跳 long delta millis() - lastBeat; lastBeat millis(); heartRate 60 / (delta / 1000.0); // 计算BPM if (heartRate 255 heartRate 20) { // 合理范围过滤 heartRateDetected true; // 更新显示 tft.setTextDatum(TC_DATUM); tft.setTextColor(TFT_GREEN, TFT_BLACK); tft.drawNumber(heartRate, 60, 55, 4); Serial.print(“BPM: “); Serial.println(heartRate); } } // 可选检测手指是否放置 if (irValue 50000) { tft.drawString(“Place Finger”, 60, 55, 2); heartRateDetected false; } }注意心率算法 (checkForBeat) 来自库中的示例实际应用中可能需要更复杂的滤波和算法来保证准确性。5.4 网络时间与天气获取通过Wi-Fi连接网络同步时间并获取天气信息。这里以心知天气Seniverse的免费API为例你需要先去其官网注册获取API密钥。void connectWiFi() { WiFi.begin(ssid, pass); tft.setTextDatum(TC_DATUM); tft.setTextColor(TFT_YELLOW, TFT_BLACK); tft.drawString(“Connecting WiFi...”, 120, 100, 2); while (WiFi.status() ! WL_CONNECTED) { delay(500); Serial.print(“.”); } tft.drawString(“WiFi Connected!”, 120, 100, 2); Serial.println(“\nWiFi Connected.”); delay(1000); } void initTime() { timeClient.begin(); timeClient.update(); } void updateWeather() { if (WiFi.status() WL_CONNECTED) { HTTPClient http; // 替换YOUR_API_KEY和YOUR_CITY为你的信息 String url “https://api.seniverse.com/v3/weather/now.json?keyYOUR_API_KEYlocationbeijinglanguagezh-Hansunitc”; http.begin(url); int httpCode http.GET(); if (httpCode HTTP_CODE_OK) { String payload http.getString(); DynamicJsonDocument doc(1024); deserializeJson(doc, payload); JsonObject now doc[“results”][0][“now”]; weatherText now[“text”].asString(); temperature now[“temperature”].asfloat(); // 更新显示 tft.setTextDatum(TC_DATUM); tft.setTextColor(TFT_CYAN, TFT_BLACK); String weatherStr weatherText ” ” String(temperature) “C”; tft.drawString(weatherStr, 180, 55, 2); Serial.println(“Weather updated: ” weatherStr); } else { Serial.println(“HTTP request failed.”); } http.end(); } } // 在loop中可以设置一个定时器每10分钟更新一次天气 unsigned long lastWeatherUpdate 0; const long weatherInterval 600000; // 10分钟 void updateWeatherPeriodically() { unsigned long currentMillis millis(); if (currentMillis - lastWeatherUpdate weatherInterval) { lastWeatherUpdate currentMillis; updateWeather(); } }5.5 低功耗与电源管理对于手表功耗至关重要。ESP32提供了深度睡眠Deep Sleep模式。void enterDeepSleep(int seconds) { // 在进入睡眠前关闭外设电源或将其置于低功耗模式 digitalWrite(TFT_BL, LOW); // 关闭屏幕背光 tft.writecommand(ST7789_SLPIN); // 让屏幕进入睡眠模式需查屏幕驱动IC手册 // 配置唤醒源例如外部引脚RTC GPIO或定时器 esp_sleep_enable_timer_wakeup(seconds * 1000000ULL); // 微秒 Serial.println(“Entering deep sleep for ” String(seconds) ” seconds.”); delay(100); esp_deep_sleep_start(); } // 在loop中可以根据按键或加速度计判断无操作后进入睡眠 // 例如通过MPU6050检测到手表放下静止一段时间后注意深度睡眠时大部分RAM数据会丢失程序会从setup()重新开始。需要结合RTC存储器或非易失性存储NVS来保存状态。6. 系统整合与烧录测试将上述代码模块整合到一个完整的.ino文件中。在烧录前请务必检查接线确保所有连接正确特别是电源正负极。选择开发板在Arduino IDE的“工具” - “开发板”中选择你的ESP32型号如“ESP32 Dev Module”。选择端口将ESP32通过USB线连接电脑在“工具” - “端口”中选择对应的COM口。修改配置将代码中的Wi-Fi SSID、密码、天气API密钥替换为你自己的。编译上传点击“上传”按钮。首次上传可能需要按住ESP32板上的“BOOT”按钮。上传成功后打开串口监视器波特率115200观察初始化日志。如果一切正常屏幕将点亮显示时间、心率需佩戴和天气信息。7. 常见问题与排查思路在复刻过程中你可能会遇到以下问题问题现象可能原因排查思路与解决方案上传代码失败1. 驱动未安装CH340/CP21022. 端口选择错误3. ESP32未进入下载模式1. 安装对应的USB转串口驱动。2. 在设备管理器中确认COM口。3. 上传时尝试先按住“BOOT”键再按一下“EN/RST”键然后松开“BOOT”键。屏幕白屏或花屏1. 接线错误特别是SCL/SDA, MOSI/MISO2. 电源功率不足3.User_Setup.h配置错误1. 用万用表检查接线对照引脚图。2. 确保3.3V电源能提供足够电流500mA可尝试单独给屏幕供电测试。3. 仔细检查TFT_eSPI库中的引脚配置确保与你的接线一致。Wi-Fi连接失败1. SSID/密码错误2. 路由器设置了MAC过滤或隐藏SSID3. 信号太弱1. 确认密码注意大小写。2. 检查路由器设置或尝试用手机热点测试。3. 在串口监视器查看Wi-Fi连接状态码。心率读数不稳定或为01. 手指未贴合传感器2. 环境光干扰3. 传感器初始化失败1. 确保手指完全覆盖传感器窗口保持静止。2. 避免强光直射传感器。3. 检查I2C地址MAX30102通常是0x57用Wire扫描工具确认。天气获取失败1. API密钥无效或过期2. 城市名称错误3. 网络连接超时1. 重新申请API密钥并替换。2. 使用API支持的城市代码如“beijing”。3. 增加HTTP超时时间检查网络连通性。功耗过高续航短1. 屏幕背光常亮2. Wi-Fi/蓝牙一直开启3. 未使用睡眠模式1. 通过PWM控制背光亮度无操作时调暗或关闭。2. 在不需要时断开Wi-Fi连接 (WiFi.disconnect(true))。3. 实现抬腕检测用MPU6050在静止时让ESP32进入轻睡眠或深度睡眠。8. 项目优化与扩展思路完成基础版本后你可以从以下方向进行优化和功能扩展打造更完善的手表8.1 软件优化更高效的UI框架考虑使用LVGL等嵌入式图形库实现更流畅的动画和更复杂的界面。本地RTC添加DS3231等高精度实时时钟芯片即使在ESP32深度睡眠时也能保持计时减少网络对时依赖。数据存储与回顾将心率、步数数据存入SPIFFS或SD卡并设计历史数据查看界面。蓝牙通知开发一个简单的手机App可用MIT App Inventor或Flutter通过BLE向手表推送来电、短信等通知。OTA升级实现完整的空中升级功能方便后续固件更新无需连接数据线。8.2 硬件扩展GPS模块添加NEO-6M等GPS模块实现轨迹记录、速度显示。SD卡模块存储更多资源如壁纸、音乐配合I2S DAC播放。触摸屏更换为电容/电阻触摸屏实现更直观的交互。更多传感器添加BME280温湿度气压或紫外线传感器丰富环境监测功能。8.3 结构与电源优化定制PCB将面包板上的连线整合到一块定制PCB上大大缩小体积提高可靠性。优化电源路径使用高效率的同步降压DC-DC芯片如TPS63020替代LDO延长电池续航。充电与保护集成完整的锂电池充电、保护过充、过放、短路电路。8.4 生产注意事项静电防护焊接和组装时注意防静电尤其是敏感的传感器芯片。防水设计如果希望日常佩戴表壳需要做防水处理选择防水按键和密封圈。固件稳定性增加看门狗定时器防止程序跑飞。对关键功能如心率读取进行异常处理和超时判断。版本管理使用Git对代码进行版本控制清晰地记录每次功能更新和Bug修复。这个ESP32多功能手表项目不仅是一个有趣的DIY作品更是一个涵盖硬件设计、嵌入式编程、无线通信、电源管理和UI设计的综合性学习平台。从点亮第一块屏幕到成功读取心率、获取网络天气每一步的调试和成功都能带来巨大的成就感。希望这份详细的教程能帮助你顺利复刻并激发你更多的创意。如果在实践中遇到新问题欢迎在开源社区分享和讨论共同完善这个项目。