1. TinyBattery 库概述TinyBattery 是一个专为 Arduino 生态设计的轻量级电池管理库核心目标是在资源受限的微控制器平台上实现高精度、低开销的电池电压监测与荷电状态SOC估算。该库不依赖复杂电量计芯片如 MAX17043、BQ27441而是基于纯模拟前端 软件算法方案通过 ADC 采样分压网络电压结合指数移动平均EMA滤波与线性映射完成从原始 ADC 值到实际电池电压V、再到百分比形式荷电状态%的完整转换链。其“Tiny”特性体现在三方面代码体积极小全部逻辑封装在单个.h头文件中无.cpp实现文件编译后静态代码占用通常 800 字节AVR 平台运行时开销极低无动态内存分配无浮点运算可选整数模式关键函数GetVoltage()执行时间稳定在 12–18 µs16 MHz ATmega328P硬件依赖精简仅需一个 ADC 通道、两个精密电阻构成分压网络无需额外 I²C/SPI 外设或专用电量计 IC。该库并非通用 BMS电池管理系统不提供过压/欠压保护、温度补偿、库仑积分或电池老化建模功能。其定位明确为低功耗物联网节点、便携式传感器终端、教育类开发板等场景提供一种零外部芯片、零驱动移植成本、开箱即用的电池健康度可视化方案。典型应用包括LoRaWAN 终端电池续航监控、太阳能充电板电压反馈、手持设备电量图标驱动、Arduino Nano 智能小车电源告警等。2. 硬件接口与分压网络设计原理TinyBattery 的硬件基础是一个由 MCU 内置 ADC、参考电压源Vref及外部电阻分压网络构成的测量回路。其核心公式为$$ V_{bat} V_{adc} \times \frac{R_{VDD} R_{GND}}{R_{GND}} \times \frac{V_{ref}}{V_{adc_max}} $$其中$V_{bat}$待测电池实际电压单位V$V_{adc}$ADC 读取的原始数字值0–1023 for 10-bit, 0–4095 for 12-bit$R_{VDD}$上拉电阻连接电池正极与 ADC 引脚$R_{GND}$下拉电阻连接 ADC 引脚与 GND$V_{ref}$ADC 参考电压默认为 MCU 的 AVCC通常 5.0V 或 3.3V也可设为内部 1.1V 带隙基准$V_{adc_max}$ADC 最大数字值取决于分辨率2.1 分压比选择工程准则TinyBattery 要求用户显式传入RESISTOR_VDD和RESISTOR_GND参数这直接决定了系统量程与精度。设计时需遵循以下原则设计目标推荐方案工程依据量程匹配分压后电压必须 ≤ $V_{ref}$且留 ≥0.2V 余量防止 ADC 饱和失真避免非线性区信噪比优化分压网络总阻值建议 20kΩ–200kΩ过低阻值增加功耗$I V_{bat}/(R_{VDD}R_{GND})$过高阻值易受 PCB 漏电流与 ADC 输入阻抗ATmega328P 为 100MΩ影响精度保障使用 1% 精度金属膜电阻避免碳膜电阻碳膜电阻温漂大±500 ppm/°C导致温度变化时读数漂移以标称 24V 锂电池组工作范围 16.0–25.9V为例若采用 $V_{ref}5.035V$实测 AVCC 值则分压比需满足$$ \frac{R_{VDD} R_{GND}}{R_{GND}} \geq \frac{25.9}{5.035} \approx 5.14 $$取 $R_{GND} 47\text{k}\Omega$则 $R_{VDD} \geq (5.14-1) \times 47\text{k}\Omega \approx 194.6\text{k}\Omega$。文档示例中选用 210kΩ提供 15.4% 余量完全覆盖 25.9V 上限。2.2 参考电压VccREF校准必要性Arduino 默认使用 AVCC 作为 ADC 参考但其实际值受 USB 供电质量、LDO 输出纹波、负载电流波动影响。实测中 AVCC 可能在 4.95V–5.10V 间漂移若固守理论值 5.00V将引入 ±1% 系统误差。TinyBattery 要求用户传入实测VccREF如示例中的5.035其来源应为使用高精度万用表四位半测量 Arduino 板上 AVCC 引脚对 GND 电压或在空载、USB 供电稳定状态下用analogReference(DEFAULT)analogRead()读取已知基准源如 TL431 输出 2.5V反推 AVCC。此步骤是 TinyBattery 实现 ±0.02V 电压精度的前提不可省略。3. 核心 API 接口详解TinyBattery 以 C 类封装所有功能通过TinyBattery类实例调用。构造函数完成硬件参数初始化成员函数提供电压与电量查询。3.1 构造函数硬件参数绑定TinyBattery(uint8_t adcPin, float vccRef, uint32_t rVdd, uint32_t rGnd, float minVolt, float maxVolt);参数类型说明典型值注意事项adcPinuint8_tADC 通道引脚编号Arduino 模拟引脚号如A0A0必须为支持 ADC 的引脚AVR: A0–A7ESP32: 34–39, 32–35vccReffloat实测 ADC 参考电压V5.035精确到毫伏级直接影响电压绝对精度rVdduint32_t上拉电阻阻值Ω210000使用uint32_t支持 64kΩ 电阻rGnduint32_t下拉电阻阻值Ω47000同上minVoltfloat电池放电截止电压V16.00决定GetChargeLevel()的 0% 点maxVoltfloat电池满充电压V25.90决定GetChargeLevel()的 100% 点关键机制构造函数内预计算分压系数m_voltPerUnit每 ADC 单位对应的电池电压增量与m_minAdc对应minVolt的 ADC 值避免在loop()中重复浮点运算// 内部预计算简化版 m_voltPerUnit vccRef * (rVdd rGnd) / (rGnd * 1024.0); // 10-bit ADC m_minAdc minVolt / m_voltPerUnit; m_maxAdc maxVolt / m_voltPerUnit;3.2 GetVoltage()获取实时电池电压float GetVoltage();功能执行一次 ADC 采样 → EMA 滤波 → 电压换算返回平滑后的电池电压V。返回值float类型范围[minVolt, maxVolt]超出范围时自动钳位。内部流程调用analogRead(adcPin)获取原始 ADC 值输入至EMA.h库的EMA::update()函数进行一阶指数滤波时间常数由EMA.h的ALPHA宏定义默认 0.125将滤波后 ADC 值乘以预计算的m_voltPerUnit得到电压对结果执行constrain(voltage, minVolt, maxVolt)钳位。性能数据ATmega328P 16MHz单次执行耗时15.2 µs含analogRead13µs EMA 计算 2.2µs内存占用仅 4 字节静态变量滤波器状态3.3 GetChargeLevel()获取荷电状态百分比uint8_t GetChargeLevel();功能基于当前电压按线性插值计算 SOC 百分比0–100。返回值uint8_t0 表示电量耗尽≤minVolt100 表示满电≥maxVolt。计算公式$$ \text{SOC} \left\lfloor \frac{V_{bat} - V_{min}}{V_{max} - V_{min}} \times 100 \right\rfloor $$结果经constrain(level, 0, 100)保证有效性。注意此为简化线性模型适用于电压-SOC 曲线较平坦的电池如磷酸铁锂 LFP对钴酸锂LiCoO₂等曲线陡峭者需配合查表法见 5.2 节扩展。4. 依赖库深度解析EMA.h 与 ADC.hTinyBattery 的稳定性与精度高度依赖两个底层支撑库EMA.h指数移动平均滤波与ADC.hADC 配置抽象。二者均由同一作者维护设计哲学高度统一零依赖、头文件仅、宏配置驱动。4.1 EMA.h轻量级一阶 IIR 滤波器EMA.h实现标准一阶指数移动平均$$ y[n] \alpha \cdot x[n] (1-\alpha) \cdot y[n-1] $$其中 $\alpha$ 为平滑因子0 $\alpha$ ≤ 1决定响应速度与噪声抑制能力。API 核心class EMA { public: EMA(float alpha 0.125f); // 构造设置 alpha float update(float sample); // 更新输入新样本返回滤波值 void reset(float initial 0.0f); // 重置清空历史状态 private: float m_alpha; float m_state; };Alpha 选择指南场景推荐 Alpha效果高频噪声强电机干扰0.0625强滤波响应慢时间常数 ≈ 16 采样周期需快速跟踪电压变化如充电中0.25弱滤波响应快时间常数 ≈ 4 采样周期默认平衡点0.125折中时间常数 ≈ 8 采样周期TinyBattery 集成方式在TinyBattery.h中通过#include EMA.h引入并在类中声明私有成员EMA m_ema;。构造函数中m_ema EMA(0.125f);初始化。4.2 ADC.h跨平台 ADC 配置抽象层ADC.h解决 Arduino 各平台 ADC 配置差异问题AVR/ESP32/STM32提供统一接口。TinyBattery 依赖其两点分辨率设置通过ADC.setResolution(bits)统一设置AVR 仅支持 10-bitESP32 支持 9–12-bit参考电压切换通过ADC.setReference(type)切换DEFAULT/INTERNAL/EXTERNAL。关键宏定义位于ADC.h#define ADC_RESOLUTION 10 // 可在 platformio.ini 中覆写build_flags -DADC_RESOLUTION12 #define ADC_REFERENCE DEFAULT // 同样可覆写TinyBattery 初始化逻辑在TinyBattery::GetVoltage()首次调用时自动执行static bool adcConfigured false; if (!adcConfigured) { ADC.setResolution(ADC_RESOLUTION); ADC.setReference(ADC_REFERENCE); adcConfigured true; }此设计确保 ADC 配置仅执行一次且与平台无关。5. 工程实践与高级应用扩展5.1 低功耗休眠模式下的电压监测在电池供电的 IoT 设备中MCU 大部分时间处于SLEEP_MODE_PWR_DOWNAVR或deep sleepESP32。此时需在唤醒瞬间快速采样避免 ADC 模块未稳定。AVR 平台优化方案ATmega328P#include avr/sleep.h #include avr/power.h void setup() { // ...其他初始化 // 关闭未使用外设以省电 power_adc_disable(); // 休眠前关闭 ADC } void loop() { // 1. 唤醒后立即开启 ADC 并等待稳定 power_adc_enable(); delayMicroseconds(100); // 等待 ADC 基准稳定 // 2. 执行一次 TinyBattery 采样 float batV Battery.GetVoltage(); // 3. 进入休眠持续 60 秒 set_sleep_mode(SLEEP_MODE_PWR_DOWN); sleep_enable(); sleep_cpu(); sleep_disable(); power_adc_disable(); // 休眠后关闭 ADC }关键点delayMicroseconds(100)不可省略——AVR ADC 基准电容充电需约 50–100µs否则首次采样偏差可达 5%。5.2 非线性电池的 SOC 查表法扩展对于电压-SOC 曲线非线性的电池如三元锂 NCN线性插值误差大。可扩展TinyBattery支持查表// 在 TinyBattery.h 中添加 class TinyBattery { private: const float* m_socTable; // 指向电压-百分比查表数组 uint8_t m_tableSize; public: TinyBattery(..., const float* socTable, uint8_t tableSize); uint8_t GetChargeLevelLUT(); // 新增查表版 SOC }; // 使用示例为 24V 三元锂电池定义 11 点查表20V–29V步进 1V const float LTO_SOC_TABLE[11] { 0.0, 10.0, 25.0, 40.0, 55.0, 70.0, 82.0, 90.0, 95.0, 98.0, 100.0 }; TinyBattery Battery(A0, 5.035, 210000, 47000, 20.0, 29.0, LTO_SOC_TABLE, 11); // GetChargeLevelLUT() 内部实现二分查找 线性插值 uint8_t TinyBattery::GetChargeLevelLUT() { float v GetVoltage(); // 二分查找 v 在 m_socTable 对应的电压区间 // 假设电压点等距分布索引 i (v - minVolt) / step uint8_t idx constrain((v - m_minVolt) / 1.0, 0, m_tableSize-2); float frac (v - (m_minVolt idx * 1.0)) / 1.0; return constrain( m_socTable[idx] frac * (m_socTable[idx1] - m_socTable[idx]), 0, 100 ); }5.3 FreeRTOS 任务中安全调用在 FreeRTOS 环境下GetVoltage()可能被多任务并发调用。需添加临界区保护针对 AVR 平台#include freertos/FreeRTOS.h #include freertos/task.h // 修改 TinyBattery.h在 GetVoltage() 前后加临界区 float TinyBattery::GetVoltage() { taskENTER_CRITICAL(); // 进入临界区 uint16_t raw analogRead(m_adcPin); float filtered m_ema.update(raw * m_voltPerUnit); float voltage constrain(filtered, m_minVolt, m_maxVolt); taskEXIT_CRITICAL(); // 退出临界区 return voltage; }注意analogRead()在 AVR 上本质是原子操作单条IN指令但m_ema.update()涉及浮点运算与变量读写需保护。6. PlatformIO 集成实战配置PlatformIO 提供比 Arduino IDE 更灵活的依赖管理与编译控制。以下是生产环境推荐配置6.1 platformio.ini 完整配置[platformio] default_envs pro16MHzatmega328 [env:pro16MHzatmega328] platform atmelavr board pro16MHzatmega328 framework arduino lib_deps rafaelreyescarmona/TinyBattery^0.1 rafaelreyescarmona/EMA^0.2 ; 显式声明 EMA 依赖避免隐式版本冲突 build_flags -DADC_RESOLUTION10 -DADC_REFERENCEDEFAULT -DDEBUG_TINYBATTERY1 ; 启用调试串口输出 monitor_speed 56700 [env:LGT8F328] board LGT8F328P board_build.f_cpu 32000000 platform lgt8f framework arduino lib_deps rafaelreyescarmona/TinyBattery^0.1 rafaelreyescarmona/EMA^0.2 build_flags -DADC_RESOLUTION12 -DADC_REFERENCEINTERNAL1V1 ; LGT8F 内部 1.1V 基准更稳定6.2 调试宏增强DEBUG_TINYBATTERY启用DEBUG_TINYBATTERY后TinyBattery.h会输出关键中间值便于验证分压参数#ifdef DEBUG_TINYBATTERY Serial.print(Raw ADC: ); Serial.print(raw); Serial.print( | Filtered: ); Serial.print(filtered, 3); Serial.print( | Voltage: ); Serial.print(voltage, 3); Serial.println( V); #endif此功能在首次调试分压网络时至关重要——若Raw ADC值长期为 0 或 1023表明分压比错误或接线故障。7. 典型故障排查与精度验证7.1 常见问题速查表现象可能原因验证方法解决方案GetVoltage()恒为 0.00VADC 引脚未连接 /adcPin参数错误用万用表测A0对 GND 电压是否随电池变化检查硬件连线确认analogRead(A0)返回值非零读数比万用表低 0.5VvccRef值偏小如用了 5.00V 但实测 4.95V测 AVCC 电压代入公式反推理论读数重新测量 AVCC更新构造函数vccRef参数GetChargeLevel()在 20%–80% 区间跳变剧烈EMA 滤波未生效检查是否遗漏EMA.h依赖或m_ema未正确初始化确认lib_deps包含EMA检查构造函数中m_ema初始化语句编译报错‘ADC’ was not declared in this scopeADC.h未被正确包含或平台不支持查看lib_deps是否包含rafaelreyescarmona/ADC显式添加rafaelreyescarmona/ADC^0.1到lib_deps7.2 精度验证实验使用 Fluke 87V 万用表作为基准对 24V 电池组进行三点校准电池状态万用表读数VTinyBattery 读数V误差V误差%FS满电静置25.8225.80-0.02-0.08%半电负载 100mA23.4523.470.020.09%欠压放电截止16.0516.03-0.02-0.12%结论在合理分压网络与校准 VccREF 前提下TinyBattery 全量程精度优于 ±0.02V±0.1% FS满足工业级传感器节点需求。8. 开源协议与合规性说明TinyBattery 采用GNU GPL v3.0协议其核心约束如下衍生作品必须开源若修改TinyBattery.h并用于商业产品必须公开修改后的全部源代码链接例外不适用GPL v3.0 无 LGPL 的“动态链接例外”因此即使仅#include该库整个固件也视为衍生作品专利授权贡献者授予用户实施相关专利的权利防止专利诉讼免责条款明确声明“AS IS”不提供适销性与特定用途适用性担保。商业项目规避建议若需闭源可自行重写等效功能仅 200 行代码或采用 MIT 协议的替代库如BatterySense若接受开源需在产品文档中显著位置声明“本产品固件包含 TinyBattery 库遵循 GPL v3.0源代码可于 [URL] 获取”。该库的 GPL 属性与其“轻量、免费、教育友好”的定位一致鼓励社区协作改进而非商业封闭。