基于STM32的锂电池充电全程可视化监控系统DIY
发布时间:2026/10/22 6:28:34 锦皓数字建站

把充电这件事做成全程看得见是我折腾这套基于STM32单片机智能锂电池充电系统最大的动力。这套DIY项目我习惯叫它DIYS384核心功能一句话概括实时采集充电电压、电流和功率通过蓝牙无线APP在手机上看曲线通过WiFi上报云平台做远程监控旁边再挂一路摄像头视频监控让整个充电过程变得透明可控。这篇文章会把硬件选型、充电采样电路、STM32状态机代码、蓝牙/WiFi双通道协议、摄像头外挂方案、云平台数据链路以及我调试过程中烧掉三块板子换回来的教训全部写出来。如果你打算给航模电池、电动工具电池做一套智能充电监控或者想用一个项目同时练到单片机、传感器采样、无线通信、APP、云平台这套完整链路这篇可以直接当复现手册用。1. 七合一智能充电监控的定位与硬件选型思路1.1 这套系统到底解决了什么问题先把这个项目解决的问题讲透不然很容易做着做着变成为了堆功能而堆功能的大杂烩。普通充电器只有一颗指示灯锂聚合物电池充到一半电压是多少、电流掉到多少、功率有没有异常你根本不知道。稍微进阶一点的平衡充只能看数字人一离开电脑就没了监控更别提人在外面还想看一眼充电状态。DIYS384把用户体验分成四个层次本地实时数据STM32做主控通过专用采样芯片抓电压和电流实时计算功率精度比直接用单片机ADC高一个量级。近场交互蓝牙透传模块把数据帧发给手机APPAPP上绘制电压、电流曲线显示充电状态。远程监控WiFi模块把数据打包成JSON上报云平台人在外面也能看设备是否在线、充电是否完成、有没有告警。辅助视频ESP32-CAM独立提供视频画面确认电池区域的物理环境安全。这套设计适合两类人。一类是电池玩家手里有航模电池、电动工具电池、DIY电池包想加一个仪表盘看清楚充电过程另一类是嵌入式学习者这个项目一次覆盖了采样、状态机、无线通信、APP和云平台比单纯做个温湿度计学到的东西多得多。我自己就是属于后者入坑最后越做越深。1.2 硬件清单与模块拆分逻辑以下是这套系统我用到的关键硬件以及各自承担的角色模块型号/方案职责选型理由主控STM32F103C8T6充电状态机、数据采集、串口分发72MHz主频够用资料多成本低充电管理TP4056小电流/开关型充电方案大电流恒流恒压充电安全兜底内置CC-CV逻辑外围简单可靠电压电流采样INA226高精度采集电压、电流、功率I2C接口16位ADC寄存器直接读功率蓝牙HM-10BLE或HC-05近场数据透传串口透传联调简单手机BLE支持成熟WiFiESP8266ESP-01S远程HTTP上报AT指令操作便宜生态成熟视频ESP32-CAM独立视频流与OSD状态叠加自带WiFi和摄像头不占用STM32算力显示可选OLED 0.96寸本地查看关键数据I2C接口方便现场调试模块拆分的逻辑我的观点很明确别让一个模块扛所有事。STM32负责充电策略和采样蓝牙负责近场WiFi负责远场ESP32-CAM负责视频每个模块都是独立链路通过串口或GPIO解耦。这样做的最大好处是调试时可以单独验证每一条链路坏了也能单独换不用整板重焊。提示我强烈建议各模块的供电相互隔离至少WiFi和摄像头模块前面要加独立稳压。ESP8266和ESP32-CAM在发射瞬间电流会从几十毫安跳到300毫安以上如果和STM32共用一条电源线采样电压就会跟着跳充电电流读数出现明显毛刺排查起来非常头疼。2. 充电主回路与电压电流功率采样从原理到参数整定2.1 CC-CV充电曲线与充电芯片选型锂电池充电和铅酸电池、镍氢电池最大的区别在于必须遵守恒流恒压CC-CV两阶段规范。恒流阶段CC用设定电流持续充电电池电压慢慢爬升当电压到达截止电压——三元锂电池一般是4.2V高压版可能是4.35V——切换成恒压阶段CV电压保持不变电流自然下降当电流降到0.1C到0.08C之间时判定充满。这个流程听起来简单但不要自己去搭恒流恒压环。我早期试过用MOS管和运放搭线性恒流电路一个没调稳就可能把电池电压顶过4.25V非常危险。最稳妥的做法是用专用充电芯片。TP4056是入门首选价格便宜外围元件就几个电阻电容内置完整的CC-CV逻辑和温度检测引脚NTC单节锂电、充电电流1A以内完全够用。如果充电电流要求大比如要给并联电池组以2A以上充电TP4056线性方案的发热就很感人效率也低。这时候应该换开关型充电方案比如常见的降压型充电管理芯片输入电压可以更高充电效率做到85%以上发热小很多。代价是外围元件变多电感电容布局要仔细PCB走线也讲究地回路。我的选型建议是单节锂电、充电电流不超过1A用TP4056便宜又好调。两节或以上、充电电流2A以上直接上开关型方案省得散热问题反过来拖累项目进度。2.2 用INA226做高精度采样别把ADC当唯一出路很多新手看到测电压电流第一反应是STM32自带ADC分压电阻加采样电阻搞定。能用但精度很难看。原因有三个一是F103的12位ADC本身分辨率有限在3.3V基准下每个LSB约0.8mV换算到电池端电压就有十几毫伏级别的误差二是单片机ADC的参考电压一般直接取自3.3V而WiFi模块发射时会导致3.3V波动产生不稳定的测量噪声三是采样电阻两端的压降本来就很小直接进ADC还需要额外放大电路噪声一叠加读数就开始飘。我最终选INA226就是因为它专门干这个活。它是一颗I2C接口的电流、电压、功率监测芯片内部集成16位ADC和可编程增益放大。两个关键输入VBUS引脚直接接电池正负极测电压IN和IN-跨接在采样电阻两侧测压差芯片内部完成电流和功率计算寄存器直接读出数值不用自己在软件里做烦琐的比例换算。INA226的接线有三个注意点采样电阻放电池负极那一侧也就是低端采样。这么做共模电压低测量逻辑简单DIY圈最常见的接法。采样电阻阻值不能随便选。一般用10mΩ到50mΩ。阻值太大充电回路额外压降明显太小则信号微弱芯片分辨率发挥不出来。用20mΩ、满量程5A的话压降才0.1V完全可以接受。I2C地址由A0、A1引脚决定默认都接GND时地址是0x40。如果总线上还有OLED之类的I2C设备要检查地址冲突。2.3 采样校准与关键参数设定芯片精度高不代表出厂就能直接用PCB走线电阻、采样电阻误差、连接器接触电阻都会引入偏差。我的校准流程是这样的先校准电压用数字万用表同时测电池正负极读取INA226的母线电压寄存器两者差值记为电压偏移量代码里做加法校准。 再校准电流充电回路串入一个精密电流表读取寄存器电流值计算比例因子也就是实际电流除以读数值。 完整校准公式就是实际电压 寄存器电压 volt_offset 实际电流 寄存器电流 × current_gain功率寄存器是芯片内部用电压电流乘积算出来的电压电流校准准了功率自然准。注意INA226的配置寄存器里有ADC转换时间设置默认值比较快数据会有些抖动。如果读数波动明显可以把转换时间调长比如设置成8.2ms滤波效果立竿见影。我一开始为了追求刷新速度读数跳得厉害还以为是硬件噪声后来发现只是配置寄存器没有调整白忙活半天。3. STM32端的数据处理与充电状态机3.1 状态机设计预充、恒流、恒压、充满、故障STM32的主程序核心不是死循环读传感器而是一个清晰的状态机。我在代码里定义的状态大概是这样的typedef enum { CHG_IDLE 0, CHG_PRECHARGE, CHG_CC, CHG_CV, CHG_DONE, CHG_FAULT } ChargerState;IDLE未接电池或待机充电芯片不输出电流。PRECHARGE电池电压低于3.0V时先用0.1C左右的小电流预充防止大电流冲击深度过放的电池。CC电池电压正常后进入恒流阶段充电电流锁定设定值。CV电压到达4.2V阈值后切换恒压电压维持电流自然下降。DONE电流降到0.1C且持续一段时间判定充满。FAULT过压、过流、过温、反接等异常立即关断充电输出。状态机每100ms跑一次根据最新电压电流判断是否切换。代码骨架大致这样void charger_state_machine(uint16_t vbat_mv, uint16_t icharge_ma) { switch (state) { case CHG_IDLE: if (vbat_mv 2800) state CHG_PRECHARGE; break; case CHG_PRECHARGE: if (vbat_mv 3000) state CHG_CC; else if (precharge_timeout) enter_fault(FAULT_PRECHARGE_TIMEOUT); break; case CHG_CC: if (vbat_mv 4200) state CHG_CV; break; case CHG_CV: if (icharge_ma 100) state CHG_DONE; break; case CHG_DONE: // 保持充满状态靠充电芯片维持涓流 break; default: break; } }这个状态机看起来简单但它让后面的保护逻辑、日志记录、APP上报都变得顺理成章。每次状态切换都是一个事件可以打日志、可以推送给APP、可以触发云端通知。3.2 采集、滤波与功率/电量计算STM32每100ms读一次INA226读到的原始值还要做三级处理。第一级是软件滤波用滑动平均窗口比如取最近10次数据取平均消除充电过程中的短时波动。第二级是单位换算把寄存器的原始数值按照采样电阻和配置换算成mV、mA、mW。第三级是限幅判断如果某个值突然变化超过阈值比如电流从1A瞬间跳到2A判定为异常进入故障处理而不是盲目采信。功率和电量计算是标题里功率两个字的落点。充电功率等于电压乘以电流直接从INA226功率寄存器读取就行。累计电量我用安时积分法公式就是cap_mAh icharge_ma * dt_hour;每100ms把当前电流乘上对应的时间增量再累加最终得到这次充电充入了多少mAh。配合电池标称容量可以估算充电百分比。这个方法在充电过程中比开路电压法可靠因为充电时电池电压是虚高的开路电压法根本测不准。但也要注意安时积分法受温度、内阻影响只能做估算参考真正精确的SOC还是得靠专业电量计芯片。3.3 保护逻辑与本地事件记录STM32的职责不只是采集数据更重要的是做安全兜底。我实现过的保护项包括过压保护电压超过设定值比如4.25V持续3秒强制进入FAULT并关闭充电使能引脚。过流保护充电电流超过设定值20%以上立即断开。过温保护用NTC温度检测超过45℃先降流超过55℃直接停充。反接保护靠充电芯片本身实现软件检测到电压异常就报错。每次进入FAULT或DONESTM32会把事件按时间戳记录到片上Flash。别小看这个日志有一次我检查某块电池的充电异常翻了日志看到那次的电压曲线和故障码很快定位到是充电芯片虚焊导致的接触电阻变大。没有日志这种偶发问题几乎没法复现。4. 蓝牙与WiFi双通道上报模块接线、协议帧与APP联调4.1 蓝牙透传模块与数据帧格式设计蓝牙部分我用过HC-05也用HM-10。两者都是串口透传手机APP连上之后STM32往串口写什么APP就收到什么。区别在于HM-10是低功耗蓝牙BLE功耗低与手机连接稳定HC-05是经典蓝牙配对方式老旧但对某些场景更友好。做DIY我建议直接HM-10现在手机对BLE支持很完善。接线就是三根线模块TX接STM32的RXRX接TXGND共地。注意逻辑电平如果模块是3.3V就直接连如果是5V模块就需要电平转换或者分压电阻。通信一定要定义协议帧不能把原始数据直接往蓝牙里塞。串口是流式传输没有帧头帧尾的话APP端根本不知道一个数据包从哪里开始到哪里结束数据一多就全乱了。我用的格式是帧头(0xAA) 帧头(0x55) 长度(1字节) 功能码(1字节) 数据段(N字节) 异或校验(1字节)功能码用来区分数据类型0x01表示实时数据0x02表示告警事件0x03表示充电状态切换。实时数据里包含电压2字节、电流2字节、功率2字节、温度1字节、状态1字节全部按大端序排列。APP解析的时候先找AA 55再校验长度和异或值即使中途丢一个字节也不会整个崩掉。4.2 WiFi模块的HTTP上报链路WiFi部分用ESP8266通过串口发AT指令操作。STM32把实时数据拼成JSON通过HTTP POST到云端。ESP8266上电后的初始化序列比较关键的指令是AT ATCWMODE1 ATCWJAP你的SSID,你的密码 ATCIPSTARTTCP,云服务器域名或IP,80 ATCIPSEND POST /api/upload HTTP/1.1 Host: 你的服务器地址 Content-Type: application/json Content-Length: 51 {voltage:4.18,current:850,power:3.55}这里有几个坑。第一个是AT指令返回串的空间串口缓冲区至少要开到512字节以上否则返回内容过长会被截断。第二个是每条指令要加回车换行而且要等模块返回OK或ready再发下一条不能盲目用延时凑时序。第三个是HTTP上报用短连接每次上报完成后发CIPCLOSE关掉TCP连接不能一直挂着否则会被服务器踢掉也会占用ESP8266的连接资源。4.3 手机APP侧的实时展示与报警提示APP我的路线是先用现成的串口转蓝牙调试工具验证数据帧确认帧结构没问题之后再做界面。DIY项目用可视化APP构建工具就很合适不用写太多原生代码。界面设计我分了三个区块顶部实时数值电压、电流、功率、温度大字显示。中部曲线区电压曲线和电流曲线以时间为横轴能拖回看整个充电过程。底部状态栏显示当前处于恒流、恒压、充满还是故障状态以及告警事件滚动列表。蓝牙联调最容易出的问题就是波特率不匹配。STM32串口的波特率、蓝牙模块AT指令设置的波特率、手机APP里配置的波特率三处必须一致。我建议统一用115200并且先单独用PC串口工具把蓝牙模块参数确认好再焊到板子上不然排查起来很痛苦。WiFi链路和蓝牙链路是并行的APP既可以连蓝牙看本地实时数据也可以连云端账号看远程历史数据和告警消息两条通道互不干扰。这样一来本地调试用蓝牙远程管理用WiFi加云平台正好互补。5. 摄像头视频监控不要指望STM32跑视频流5.1 为什么要把视频任务外挂出去标题里带了摄像头视频监控很多人第一反应是给STM32接一个摄像头模块让单片机自己压缩视频推出去。这个方向对F103来说性价比极低摄像头输出RAW格式的数据量非常大F103即使跑到72MHz也只能勉强跑低分辨率低帧率的画面CPU基本被榨干还要外挂SRAM做帧缓存主板复杂度和成本都上去了充电监控的核心功能反而被拖累。所以我的做法是视频监控用独立模块实现让ESP32-CAM去扛摄像头和视频流这就是典型的让专业模块做专业的事。STM32只负责充电数据ESP32-CAM负责画面两个系统之间通过串口交换状态信息。这样的解耦设计让整个系统可靠性更高哪怕摄像头挂了充电监控和蓝牙上报依然不受影响。5.2 ESP32-CAM独立视频流与状态叠加ESP32-CAM模块自带摄像头支持WiFi刷入摄像头Web服务器示例程序后连上WiFi手机或电脑浏览器通过HTTP直接访问视频流。刷固件用常见的ESP32开发环境即可主要设置就是WiFi账号密码和摄像头型号。刷完接5V供电它就自动变成一个带视频流的局域网摄像头非常省事。视频流和充电数据的联动我提供一个很实用的方案ESP32-CAM的串口再接到STM32的一个UARTSTM32周期性发送一条状态字符串给ESP32-CAMESP32-CAM在代码里把收到的文字叠加到视频画面的OSD区域。这样你在浏览器或APP里看到的画面除了锂电池本体还会直接显示4.18V 850mA 3.55W CHG_CV这样的实时数据。实现方法不复杂在摄像头示例代码里加一个处理串口消息的任务更新OSD变量即可。注意ESP32-CAM对供电比较敏感摄像头上电瞬间电流很大。如果从主板LDO取电很容易随机重启我最终用独立的5V电源给ESP32-CAM供电并在电源输入端并联一个大容量电容视频流才彻底稳定。5.3 视频画面与充电数据的联动方案如果不折腾OSD叠加也可以简单很多。摄像头画面只负责看环境有没有人在旁边、电池有没有鼓包冒烟、线材温度有没有异常这些直观信息跟APP的数据是并行的两个通道。我实际使用最多的场景是给一组无人机电池充电摄像头斜着对准电池区域云平台上能看到数据曲线同时每隔10秒抓一张视频截图已经够用了。视频流的远程访问我的稳妥建议是先玩局域网内的视频流确认整个系统能跑通之后再考虑云平台侧的截图或视频接入。毕竟远程访问视频流涉及的端口映射、平台对接这些环节对纯小白有门槛放在项目第二阶段做更合理。6. 云平台数据链路最后一公里怎么打通6.1 上报方式选择定时上报加事件触发很多DIY项目喜欢把所有数据实时发到云端结果发现两个问题一是ESP8266长时间全速工作发热明显影响稳定性二是TCP连接频繁断开重连数据反而丢得更厉害。我的做法是定时加事件双轨制定时上报每10秒一次把电压、电流、功率、温度、SOC打包成JSON发上去。满足大多数监控需求也不会把流量和连接资源刷爆。 事件上报充电完成、进入恒压阶段、告警触发这些重要节点即时上报不等待定时周期。比如充电完成了云端和APP要立刻收到通知。JSON体大概长这样{ device_id: diy_charge_01, ts: 1730000000, voltage: 4.18, current: 850, power: 3.55, temperature: 30.2, state: 3, soc: 87 }字段全部用英文小写单位固定云端解析就少了歧义。6.2 云端规则与远程预警云平台层有两条路线可选。第一条是使用公共物联网平台在平台上创建产品和设备设备通过MQTT或HTTP接入平台自带数据面板、设备管理、规则引擎。第二条是自己搭一套轻量后端用开源MQTT broker做数据中转再加上Node-RED接数据库和看板网页。我的倾向是快速出成果用公共平台想深入理解物联网架构再自己搭MQTT。我在公共物联网平台上实现过最有用的功能是规则预警电压大于4.25V持续5秒触发过压告警。温度高于50℃触发高温告警。充电完成事件到达时触发充电完成通知。平台规则引擎把这些告警转成手机推送人不在现场也能掌上看到。这个远程预警能力是整个DIYS384里我认为最值钱的部分它把看到数据升级成数据帮我看着。6.3 断网、掉电、复电等异常场景处理设备端的网络异常处理决定整套系统的口碑。WiFi模块重启、路由器重启、云平台临时维护这些日常情况都会发生。我总结三个实用策略心跳保持定时上报本身就可以当心跳云端连续60秒收不到数据就判定设备离线APP端显示断线状态。断线数据缓存STM32的Flash记录最近一段时间未上报的数据点WiFi恢复后补传避免远程侧出现空白。上电自动恢复ESP8266初始化必须等待ready和联网成功再继续干活连续失败就软复位不能僵死。还有一个经验STM32和ESP8266之间的串口数据传输要尽量用DMA加环形缓冲避免在WiFi上报期间阻塞充电状态机的执行。我早期用一个简单的阻塞延时等AT返回导致充电状态检测卡顿差一点让电池过充。改成非阻塞解析之后彻底解决。7. DIY踩坑实录烧掉三块板子之后总结的教训7.1 电源与接插件的教训调试这套系统烧过三块板子第一块死在电源反接。当时图方便用杜邦线接5V电源正负极插反的一瞬间主控和蓝牙模块直接冒烟。之后我做了两个改进一是电源入口加防反接二极管或者用带反接保护的PMOS电路二是接线时先用万用表通断档确认正负极再上电。看起来很基础的习惯紧张调试时最容易犯。第二块板的教训来自给ESP8266供电。ESP-01S的额定电流余量要留足如果直接从主控开发板的3.3V稳压器取电发射瞬间电压猛掉模块反复重启串口完全乱掉。最终我在电源树里给ESP8266和ESP32-CAM各自单独一路稳压输入侧加了470uF以上的大电容做瞬态储能问题才消失。7.2 通信与调试的坑蓝牙和WiFi调试阶段我建议每个模块在接入系统之前先用USB转TTL单独验证。蓝牙模块用PC串口工具发AT指令确认波特率ESP8266用串口助手发AT确认能连上路由器。单独验证过的模块再接进系统出问题时排查范围会小很多。让我印象最深的坑是INA226的I2C地址冲突。早期在I2C总线上还挂了一个OLED显示屏地址也是0x40两个器件同时应答读回来的数据全是FF。后来把INA226的A0、A1引脚接到不同电平把地址改到0x41总线立刻清净。这个坑也提醒我接I2C设备之前一定先看数据手册地址配置不能想当然。7.3 锂电池的安全底线最后必须认真说锂电池的安全问题。做DIY充电系统安全底线绝对不能破充电管理必须用专用充电芯片不能自己搭简单的限压限流电路代替。不要充着电就睡觉或出门至少要在云端或APP设置告警。电池鼓包、异响、温度异常时第一时间断电并把电池移到防火环境。所有接线要可靠尽量焊接而不是杜邦线大电流路径的线径要足够。我自己的使用习惯是人不在家的时候只启动数据上报和视频轮询不启动充电等确认电池电压正常再远程开启充电。数据监测永远是辅助手段真正保障安全的始终是规范的充电管理电路和使用习惯。回头看这个项目最值钱的部分不是某一个模块而是把采样、状态机、双通道通信、视频、云端串成一条完整链路的过程。这个过程中踩过的每一个坑从I2C地址冲突到WiFi供电不足最后都变成了调试技巧。如果你接下来想继续深入可以在本地加一块OLED屏显示曲线把充电管理升级成带通信接口的电量计方案或者用实时操作系统把采集和上报任务彻底分层。动起手来比看一百篇教程都更有用。
锦
锦皓数字建站
深耕本土企业品牌数字化升级,专注原创端正雅致商务官网,从视觉设计到稳定运维全程保驾护航。