💡 划重点

  • 本项目用 ESP32-S3 做主控,搭配 MAX17048 电量计芯片与 0.96 寸 OLED,做一块能实时预测剩余续航的极客手表。MAX17048 直接输出荷电状态与充放电速率,比单纯读电压准得多;按键切换页面,串口输出日志,可选 Wi-Fi 上报。整套物料控制在 95 元内,适合有焊接和 Arduino 基础的玩家。

需求洞察

目标用户是极客、嵌入式学习者和喜欢 DIY 手表的人。真实痛点有三个:一是普通手表用电量百分比,本质是电压查表,负载一变就跳,低电量突然关机;二是充电决策靠猜,不知道还能撑几小时;三是想学 I2C、低功耗、传感器融合,却找不到小而完整的项目。MAX17048 自带 ModelGauge 算法,能给出较稳的 SOC 和 CRATE,用它做“实时预测”比电压法更有说服力。手表形态又逼着开发者考虑功耗、显示刷新和按键交互,学习密度高,成品也有日常佩戴价值,所以值得做。

方案设计

感知层:MAX17048 通过 I2C 读取电池电压、SOC、CRATE、温度。CRATE 表示当前充放电速率,单位 %/h,用它算剩余时间最直接。控制层:ESP32-S3 每 2 秒采样一次,对 CRATE 做滑动平均,抑制负载突变造成的抖动;再用 SOC 除以平均放电速率,得到剩余小时数。执行层:OLED 显示电量、电压、剩余时间;两个按键分别切换页面和强制刷新。日志/联网层:串口打印 CSV,方便校准;可选 Wi-Fi 或 BLE 把数据发到手机,做趋势记录。低功耗上,屏幕常亮时用 20 秒自动熄屏,主控进入轻睡眠,按键中断唤醒。

物料清单

物料 数量 用途 估算单价 合计
ESP32-S3 开发板 1 主控、蓝牙/Wi-Fi 38 元 38 元
MAX17048 模块 1 电量计、SOC/CRATE 16 元 16 元
0.96 寸 OLED I2C 1 显示电量与预测 11 元 11 元
轻触按键 2 切页、刷新 0.5 元 1 元
3.7V 500mAh 锂电池 1 供电 13 元 13 元
TP4056 充电板 1 锂电充电保护 2.5 元 2.5 元
表壳与表带 1 佩戴结构 10 元 10 元
导线、热缩管 若干 连接与绝缘 2 元 2 元
合计 93.5 元

接线与结构

I2C 保留给定引脚:SDA=GPIO8,SCL=GPIO9,MAX17048 与 OLED 共用总线。MAX17048 默认地址 0x36,OLED 常用 0x3C,不冲突。按键原定 GPIO3/GPIO4,但 GPIO3 是 ESP32-S3 的启动模式 strapping 脚,上电被拉低可能进入下载模式,因此微调为 GPIO4 和 GPIO5,分别接按键一端,另一端接地,内部上拉。若坚持用 GPIO3,需加 RC 延迟并在上电时避免按下。MAX17048 的 BAT 接电池正极,GND 共地,VCC 接 3.3V;ALRT 可接 GPIO6 做低电量中断。结构上,OLED 开窗粘在表壳正面,按键放在右侧,电池和充电板叠在底层,用海绵双面胶固定。

核心代码

以下为 Arduino 骨架,需安装 Adafruit MAX17048 与 U8g2 库。


#include <Wire.h>
#include <Adafruit_MAX17048.h>
#include <U8g2lib.h>

Adafruit_MAX17048 maxl;
const int KEY_NEXT = 4, KEY_RATE = 5;
float rateBuf[8]; int idx = 0; bool showRate = false;

void setup() {
  Serial.begin(115200);
  Wire.begin(8, 9);
  oled.begin();
  if (!maxl.begin()) { Serial.println("MAX17048 not found"); while (1); }
}

float avgRate() {
  float s = 0; for (int i = 0; i < 8; i++) s += rateBuf[i];
  return s / 8.0;
}

void loop() {
  float soc = maxl.cellPercent();
  float v = maxl.cellVoltage();
  float rate = maxl.chargeRate();
  rateBuf[idx++ % 8] = rate;
  float ar = avgRate();
  float hours = (ar < -0.01) ? soc / -ar : 99.0;

  if (!digitalRead(KEY_NEXT)) { showRate = !showRate; delay(200); }
  if (!digitalRead(KEY_RATE)) { /* 强制刷新 */ delay(200); }

  oled.clearBuffer();
  oled.drawStr(0, 12, showRate ? "Rate page" : "Battery watch");
  oled.setCursor(0, 30); oled.print(soc, 1); oled.print(" %");
  oled.setCursor(0, 58);
  if (hours < 99) { oled.print(hours, 1); oled.print(" h left"); }
  else oled.print("charging / idle");
  oled.sendBuffer();
  delay(2000);
}

调试步骤

第一步,只接 MAX17048,跑 I2C 扫描,确认 0x36 出现;读电压应与万用表相差 20mV 内。第二步,接 OLED,确认 0x3C 出现且不花屏。第三步,测按键,按下时串口打印键值,检查上拉是否正常。第四步,接电池,观察 SOC 从空到满的充电曲线,若跳变明显,在代码里把 CRATE 滑动平均窗口从 8 调到 16。第五步,用已知负载放电,记录预测剩余时间与实测误差,误差超过 20% 就做线性修正。第六步,测整机电流,熄屏后应低于 5mA,否则关闭不用的外设并延长休眠。

设计图

电池电量实时预测手表结构flow
1
感知输入:MAX17048 电压、SOC、CRATE
→
2
控制处理:ESP32-S3 采样、滑动平均、续航预测
→
3
执行反馈:OLED 显示、按键切页
→
4
数据上报:串口日志、可选 Wi-Fi/BLE