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