💡 划重点

  • 用 MAX17048 电量计配合 ESP32-S3,实时估算锂电池剩余电量与可用时间。
  • 0.96 寸 OLED 显示电量百分比、电压、预测续航,两个按键切换页面与重置预测。
  • 极客手表形态,支持串口日志与蓝牙/WiFi 上报,方便二次开发。
  • 整体物料成本控制在 95 元以内,适合个人 DIY 与嵌入式学习。

需求洞察

目标用户是嵌入式爱好者、极客手表玩家、需要长时间佩戴小型电池设备的人。真实痛点是:普通手表或 DIY 设备用电量显示往往靠电压估算,锂电池放电曲线平坦,电量会突然从 40% 跳到 10%,甚至关机前才报警。用户真正想知道的是“还能用多久”,而不是一个不准的百分比。MAX17048 内置 ModelGauge 算法,无需完整充放电学习,能提供较准的 SOC,配合 ESP32-S3 的计算与联网能力,可以记录放电曲线并做短期预测。这个项目值得做,因为它把电量计、显示、按键、低功耗与预测算法串成一个完整闭环,既能当手表,也能当电池监测平台。

方案设计

感知层:MAX17048 通过 I2C 采集电池电压、SOC、充放电速率;两个按键作为用户输入,用于切换显示和重置预测。

控制层:ESP32-S3 负责 I2C 轮询、数据滤波、续航预测和任务调度。预测采用简单滑动窗口:记录最近若干次 SOC 变化,计算平均放电速率,剩余时间等于当前 SOC 除以速率。

执行层:OLED 显示电量、电压、剩余小时数、充电状态图标;按键触发页面切换。

日志/联网层:串口输出调试信息;可选 BLE 或 WiFi 将电量日志上报到手机或服务器,便于长期分析。低功耗时进入深度睡眠,按键唤醒,定时唤醒采样。

物料清单

物料 数量 用途 估算单价 合计
ESP32-S3 开发板 1 主控与联网 35 元 35 元
MAX17048 模块 1 电量计与电压采集 18 元 18 元
0.96 寸 I2C OLED 1 显示电量与预测 12 元 12 元
轻触按键 2 页面切换与重置 0.5 元 1 元
3.7V 500mAh 锂电池 1 供电 15 元 15 元
表壳与表带 1 套 佩戴结构 10 元 10 元
杜邦线/小 PCB 1 套 连接与固定 4 元 4 元
合计 95 元

接线与结构

I2C 总线共用:MAX17048 与 OLED 都接 ESP32-S3 的 SDA=8、SCL=9,两者地址不同,不会冲突。MAX17048 默认地址 0x36,OLED 常见地址 0x3C。按键 KEY1 接 GPIO3,KEY2 接 GPIO4,另一端接 GND,启用内部上拉,按下为低电平。电池正负极接 MAX17048 的 BAT 与 GND,ESP32-S3 由电池经低压差稳压器供电。结构上,OLED 放在表盘正面,按键放在侧边,MAX17048 与主控叠放在表壳内部,电池贴底。若需要更稳的 I2C,可在 SDA/SCL 上加 4.7k 上拉电阻。

核心代码

以下为 Arduino 骨架,可编译上传。需安装 Adafruit_MAX17048 与 U8g2 库。


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

Adafruit_MAX17048 maxlipo;

#define KEY1 3
#define KEY2 4

float lastSoc = 0;
unsigned long lastMs = 0;
float rate = 0; // 每秒 SOC 变化

void setup() {
  Serial.begin(115200);
  Wire.begin(8, 9);
  u8g2.begin();
  if (!maxlipo.begin()) {
    while (1);
  }
  pinMode(KEY1, INPUT_PULLUP);
  pinMode(KEY2, INPUT_PULLUP);
  lastSoc = maxlipo.cellPercent();
  lastMs = millis();
}

void loop() {
  float soc = maxlipo.cellPercent();
  float volt = maxlipo.cellVoltage();
  unsigned long now = millis();
  float dt = (now - lastMs) / 1000.0;
  if (dt > 0) {
    rate = (lastSoc - soc) / dt; // 正值为放电
    lastSoc = soc;
    lastMs = now;
  }
  float remainH = 0;
  if (rate > 0.0001) remainH = soc / rate / 3600.0;

  u8g2.clearBuffer();
  u8g2.setCursor(0, 30);
  u8g2.print(soc, 1); u8g2.print(" %");
  u8g2.setCursor(0, 46);
  u8g2.setCursor(70, 46);
  u8g2.sendBuffer();

  if (digitalRead(KEY1) == LOW) { delay(200); }
  if (digitalRead(KEY2) == LOW) { rate = 0; delay(200); }

  Serial.print("SOC="); Serial.print(soc);
  Serial.print(" V="); Serial.print(volt);
  Serial.print(" rate="); Serial.println(rate);
  delay(1000);
}

调试步骤

  1. 上电后先扫描 I2C,确认 0x36 与 0x3C 都能发现。
  2. 串口打印 SOC 与电压,用万用表对比电池电压,误差应在 20mV 内。
  3. 接上 OLED,检查显示是否正常,调整对比度或字体。
  4. 按下 KEY1 与 KEY2,确认按键触发电平变化。
  5. 做一次完整放电测试,记录 SOC 与时间,修正预测系数。
  6. 开启深度睡眠,测试按键唤醒与定时采样功耗。
  7. 装入表壳,连续佩戴测试续航与显示可读性。

设计图

电池电量实时预测手表结构flow
1
感知输入
→
2
控制处理
→
3
执行反馈
→
4
数据上报