零知IDE——零知ESP32 + INA219电流传感器实现18650锂电池智能充放电监测系统

✔零知开源(零知IDE)是一个专为电子初学者/电子兴趣爱好者设计的开源软硬件平台,在硬件上提供超高性价比STM32系列开发板、物联网控制板。取消了Bootloader程序烧录,让开发重心从 "配置环境" 转移到 "创意实现",极大降低了技术门槛。零知IDE编程软件,内置上千个覆盖多场景的示例代码,支持项目源码一键下载,项目文章在线浏览。零知开源(零知IDE)平台通过软硬件协同创新,让你的创意快速转化为实物,来动手试试吧!

✔访问零知实验室,获取更多实战项目和教程资源吧!

www.lingzhilab.com

目录

一、系统接线部分

[1.1 硬件清单](#1.1 硬件清单)

[1.2 接线方案表](#1.2 接线方案表)

[1.3 具体接线图](#1.3 具体接线图)

[1.4 接线实物图](#1.4 接线实物图)

二、安装与使用部分

三、核心代码解析

[3.1 数据采集与滤波](#3.1 数据采集与滤波)

[3.2 模式自动识别](#3.2 模式自动识别)

[3.3 状态圈动态显示](#3.3 状态圈动态显示)

[3.4 JSON数据接口](#3.4 JSON数据接口)

[3.5 主程序接口](#3.5 主程序接口)

四、项目结果演示

[4.1 操作流程](#4.1 操作流程)

[4.2 视频演示](#4.2 视频演示)

五、INA219电流传感器知识点讲解

[5.1 寄存器映射](#5.1 寄存器映射)

[5.2 I2C总线通信原理](#5.2 I2C总线通信原理)

六、常见问题解答(FAQ)

Q1:为什么电流测量值跳动很大?

Q2:网页无法访问,或数据不刷新?


项目概述

本项目基于零知ESP32 开发板和INA219电流传感器,实现了一个功能完整的锂电池充放电智能监测系统。系统能够实时监测电池的电压、电流、功率等关键参数,并通过TFT显示屏可视化波形显示,同时支持Web远程监控和风扇智能控制

项目难点及解决方案

问题描述:仅读取电流数值无法区分充电 / 放电状态

**解决方案:**利用 INA219 分流电压的极性特性,结合总线电压变化趋势辅助验证

一、系统接线部分

1.1 硬件清单

组件 数量 参数/型号 说明
零知ESP32开发板 1 ESP32-WROOM-32 主控MCU
INA219电流传感器 1 I2C地址0x40 电流/电压监测
ST7789 TFT屏 1 320×240 SPI 显示屏
TP4056充电板 1 1A充电电流 锂电池充电管理
18650电池+座 1 3.7V 3500mAh 被测电池
直流风扇 1 L9110 风扇模块 放电负载
USB Type-C线 1 数据+电源 充电接口
杜邦线 若干 公对母/母对母 连接线

1.2 接线方案表

根据config.h文件定义的引脚,接线如下:

零知 ESP32引脚 连接模块 功能说明
GPIO21 INA219 SDA I²C数据线
GPIO22 INA219 SCL I²C时钟线
GPIO15 TFT CS 片选信号
GPIO13 TFT DC 数据/命令选择
GPIO4 TFT RST 复位信号
GPIO23 TFT SDA SPI数据输出
GPIO18 TFT SCL SPI时钟
GPIO25 风扇 INA PWM控制信号
3.3V 模块VCC 电源正极
GND 模块GND 电源地线

双向电流检测务必按照以下方式接线:

充电回路

INA219引脚 连接目标 说明
VIN+ 充电板输出 B+ 高侧电压检测点
VIN- 电池正极 低侧电压检测点

充电时:电流从充电器流出 -> 进入 VIN+ -> 流向 VIN- -> 进入电池。此时电流方向为正 (+),系统识别为充电。

放电回路

| INA219引脚 | 连接目标 | 说明 |
| VIN+ | 负载(风扇)正极 | 高侧电压检测点 |

VIN- 电池正极 低侧电压检测点

放电时:电流从电池流出 -> 进入 VIN- -> 流向 VIN+ -> 进入负载。此时电流方向为负 (-),系统识别为放电。

1.3 具体接线图

注意:电池负极、负载负极、充电器负极全部共地 (GND),INA219使用5V供电

1.4 接线实物图

二、安装与使用部分

2.1 开源平台-输入"INA219电流传感器"并搜索-代码下载自动打开

2.2 连接-验证-上传

2.3 调试-串口监视器

三、核心代码解析

项目文件结构

ESP32_Battery_Monitor/

├── ESP32_Battery_Monitor.ino # 主程序

├── config.h # 配置文件

├── BatteryMonitor.h/cpp # 电池监测类

├── DisplayHandler.h/cpp # 显示控制类

├── FanController.h/cpp # 风扇控制类

└── WebHandler_Battery.h/cpp # Web服务器类

3.1 数据采集与滤波

cpp 复制代码
void BatteryMonitor::updateSensorData() {
  float rawCurrent = ina219.getCurrent_mA();
  
  // 关键:滑动窗口平均滤波
  filteredCurrent = filterCurrentValue(rawCurrent);
  
  // 读取其他参数
  currentData.shuntVoltage = ina219.getShuntVoltage_mV();
  currentData.voltage = ina219.getBusVoltage_V();
  currentData.power = ina219.getPower_mW();
  
  // 保存绝对值用于显示
  currentData.current = abs(filteredCurrent);
}

float BatteryMonitor::filterCurrentValue(float newValue) {
  // 滑动窗口队列
  currentBuffer[filterIndex] = newValue;
  filterIndex = (filterIndex + 1) % FILTER_WINDOW_SIZE;
  
  // 计算平均值
  float sum = 0;
  for (int i = 0; i < FILTER_WINDOW_SIZE; i++) {
    sum += currentBuffer[i];
  }
  return sum / FILTER_WINDOW_SIZE;
}

滤波效果对比

情况 原始电流波动 滤波后波动 改善率
充电 800±50mA 800±10mA 80%
放电 -300±80mA -300±15mA 81%
待机 ±20mA ±5mA 75%

3.2 模式自动识别

cpp 复制代码
void BatteryMonitor::updateMode() {
  BatteryMode oldMode = currentMode;
  
  // 关键:基于滤波后电流判断
  if (filteredCurrent > CURRENT_THRESHOLD) {
    currentMode = MODE_CHARGING;    // 正值 = 充电
  } else if (filteredCurrent < -CURRENT_THRESHOLD) {
    currentMode = MODE_DISCHARGING; // 负值 = 放电
  } else {
    currentMode = MODE_STANDBY;     // 接近0 = 待机
  }
  
  // 模式切换处理
  if (currentMode != oldMode) {
    modeStartTime = millis();
    totalCapacity_mAh = 0;  // 重置累计容量
    smoothedTimeEst = -1;   // 重置时间预估
  }
}

电流范围在+15 ~ -15mA 内判断结果为MODE_STANDBY (死区)

3.3 状态圈动态显示

cpp 复制代码
void DisplayHandler::drawStatusCircle(BatteryMode mode) {
  int x = 220, y = 10, r = 8;
  
  // 清除旧圈
  tft.fillCircle(x, y, r + 2, COLOR_BACKGROUND);
  
  uint16_t c; 
  const char* t;
  
  // 根据模式选择颜色和文字
  if (mode == MODE_CHARGING) { 
    c = COLOR_CHARGE;        // 0x07E0 绿色
    t = "CHARGING"; 
  } else if (mode == MODE_DISCHARGING) { 
    c = COLOR_DISCHARGE;     // 0xF800 红色
    t = "DISCHARGE"; 
  } else { 
    c = COLOR_STANDBY;       // 0x8410 灰色
    t = "STANDBY"; 
  }
  
  tft.print(t); 
  tft.fillCircle(x, y, r, c); 
  tft.drawCircle(x, y, r, COLOR_TEXT);
}

采用RGB565颜色编码

RGB565格式:RRRRR GGGGGG BBBBB (5+6+5=16位)

比如,灰色 0x8410 = 10000 100000 10000 = (132, 132, 132)

3.4 JSON数据接口

cpp 复制代码
void WebHandlerBattery::handleData() {
  BatteryData data = Battery.getData(); // 单一数据源
  BatteryMode mode = Battery.getCurrentMode();
  
  String json = "{";
  json += "\"voltage\":" + String(data.voltage, 2);
  json += ",\"current\":" + String(data.current, 1);
  json += ",\"power\":" + String(abs(data.power), 1);
  json += ",\"battery\":" + String(data.batteryPercent, 1);
  json += ",\"capacity\":" + String(data.capacity, 1);
  json += ",\"mode\":" + String((int)mode);
  json += ",\"fan\":" + String(Fan.isRunning() ? "true" : "false");
  json += ",\"pwm\":" + String((int)((Fan.getCurrentDuty() / 255.0) * 100));
  
  float timeEst = (mode == MODE_CHARGING) ? Battery.estimateTimeToFull() : Battery.estimateTimeToEmpty();
  json += ",\"timeEst\":" + String(timeEst, 0);
  
  if (logBuffer.length() > 0) {
    json += ",\"log\":\"" + logBuffer + "\"";
    logBuffer = "";
  }
  
  json += "}";
  server.send(200, "application/json", json);
}

将电压、电流、状态等数据封装为 JSON 格式,实现WebSocket 主动推送数据到ESP32

3.5 主程序接口

cpp 复制代码
/**************************************************************************************
 * 文件: /ESP32_Battery_Monitor/ESP32_Battery_Monitor.ino
 * 作者:零知实验室(深圳市在芯间科技有限公司)
 * -^^- 零知实验室,让电子制作变得更简单! -^^-
 * 时间: 2026-1-13
 * 功能特性:
 * 单INA219监测: 0x40 (自动识别充放电)、TFT实时波形: 电压/电流/功率三条曲线动态显示 (防抖动优化)、Web网页监控: 修复ESP32 3.x兼容性问题
 * PWM循环控制: 风扇转速周期性变化、智能算法: 时间预估平滑处理,防止数值跳动

 * 访问方式:
 * 连接WiFi热点: ESP32_Battery_Monitor (密码: 12345678),浏览器打开: http://192.168.4.1
 * ***************************************************************************************/

#include "config.h"
#include "BatteryMonitor.h"
#include "DisplayHandler.h"
#include "FanController.h"
#include "WebHandler_Battery.h"

// 全局变量
unsigned long lastPrintTime = 0;
unsigned long lastLowVoltageCheck = 0;

void setup() {
  Serial.begin(DEBUG_BAUD_RATE);
  delay(1000);
  
  DEBUG_PRINTLN("\n\n");
  DEBUG_PRINTLN("╔═════════════════════╗");
  DEBUG_PRINTLN("║  ESP32 锂电池充放电监测系统              ║");
  DEBUG_PRINTLN("╚═════════════════════╝");
  DEBUG_PRINTLN();
  
  DEBUG_PRINTLN("=== 系统初始化开始 ===\n");
  
  // 1. 初始化TFT显示屏
  DEBUG_PRINTLN("[1/4] 初始化TFT显示屏...");
  Display.begin();
  
  // 2. 初始化INA219电池监测
  DEBUG_PRINTLN("\n[2/4] 初始化INA219监测模块...");
  if (!Battery.begin()) {
    DEBUG_PRINTLN("❌ INA219初始化失败,系统停止");
    Display.showLowVoltageWarning();
    while(1) { delay(100); }
  }
  
  // 3. 初始化PWM风扇控制
  DEBUG_PRINTLN("\n[3/4] 初始化PWM风扇控制...");
  Fan.begin();
  
  // 4. 初始化Web服务器
  DEBUG_PRINTLN("\n[4/4] 初始化Web服务器...");
  WebBattery.begin();
  
  DEBUG_PRINTLN("\n=== 系统初始化完成 ===");
  DEBUG_PRINTLN("=== 开始监测 ===\n");
  delay(1000);
}

void loop() {
  Battery.update();
  Fan.updateCycleMode();
  Display.update();
  WebBattery.loop();
  checkLowVoltage();
  printDebugInfo();
  delay(10);
}

void checkLowVoltage() {
  unsigned long now = millis();
  if (now - lastLowVoltageCheck >= 2000) {
    if (Battery.isLowVoltage()) {
      DEBUG_PRINTLN("\n⚠️⚠️⚠️ 低电压保护触发 ⚠️⚠️⚠️");
      Fan.stop();
      Display.showLowVoltageWarning();
      while(1) { delay(100); }
    }
    lastLowVoltageCheck = now;
  }
}

void printDebugInfo() {
  #if DEBUG_ENABLE
  unsigned long now = millis();
  if (now - lastPrintTime >= 3000) {
    BatteryMode mode = Battery.getCurrentMode();
    BatteryData data = Battery.getData();
    
    DEBUG_PRINTLN("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━");
    const char* modeStr[] = {"待机", "充电", "放电"};
    DEBUG_PRINTF("⚡ 模式: %s\n", modeStr[mode]);
    
    DEBUG_PRINTLN("\n📊 电池数据:");
    DEBUG_PRINTF("  电压: %.2f V\n", data.voltage);
    DEBUG_PRINTF("  电流: %.1f mA\n", data.current);
    DEBUG_PRINTF("  功率: %.1f mW\n", data.power);
    DEBUG_PRINTF("  电量: %.1f %%\n", data.batteryPercent);
    DEBUG_PRINTF("  容量: %.1f mAh\n", data.capacity);
    
    if (Fan.isRunning()) {
      float percent = (Fan.getCurrentDuty() / 255.0) * 100.0;
      DEBUG_PRINTF("\n🌀 风扇: 运行中 (PWM: %.0f%%)\n", percent);
    } else {
      DEBUG_PRINTLN("\n🌀 风扇: 停止");
    }
    
    if (mode == MODE_CHARGING || mode == MODE_DISCHARGING) {
      float timeEst = (mode == MODE_CHARGING) ? Battery.estimateTimeToFull() : Battery.estimateTimeToEmpty();
      if (timeEst > 0) {
        int hours = (int)(timeEst / 60);
        int mins = (int)(timeEst) % 60;
        DEBUG_PRINTF("\n⏱  预计时间: %dh %dm\n", hours, mins);
      }
    }
    
    DEBUG_PRINTF("\n💾 内存: %d KB 可用\n", ESP.getFreeHeap() / 1024);
    DEBUG_PRINTLN("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n");
    lastPrintTime = now;
  }
  #endif
}

/******************************************************************************
 * 深圳市在芯间科技有限公司
 * 淘宝店铺:在芯间科技零知板
 * 店铺网址:https://shop533070398.taobao.com
 * 版权说明:
 *  1.本代码的版权归【深圳市在芯间科技有限公司】所有,仅限个人非商业性学习使用。
 *  2.严禁将本代码或其衍生版本用于任何商业用途(包括但不限于产品开发、付费服务、企业内部使用等)。
 *  3.任何商业用途均需事先获得【深圳市在芯间科技有限公司】的书面授权,未经授权的商业使用行为将被视为侵权。
******************************************************************************/

系统流程图

容量积分计算

// 安时积分:电流 × 时间

totalCapacity_mAh += abs(filteredCurrent) * deltaTime_h;

计算公式

充放电分别累计,每次模式切换清零重新计算

四、项目结果演示

按接线表完成零知 ESP32、INA219、锂电池、TP4056充电板的接线,确认正负极无误

4.1 操作流程

①系统启动

上电后TFT显示启动画面2秒、自动连接INA219传感器、启动Web服务器(AP模式)

②功能验证

TFT屏显示实时波形和数据面板、连接手机WiFi热点:ESP32_Battery_Monitor和密码:12345678、浏览器访问:http://192.168.4.1

③测试场景

充电测试:连接充电器观察充电状态

放电测试:连接负载观察放电过程、风扇控制:Web界面控制风扇启停

在浏览器中输入串口输出的 ESP32 IP 地址(192.168.4.1),打开监测页面,断开负载,接锂电池充电器,电流变为正值,电压缓慢上升,状态显示 "Charging"

④数据导出

点击网页"导出数据"按钮,将充电中实时电压、电流和功率通过excel表格展示,"清空记录"按钮可以将数据清零,重新开始记录

4.2 视频演示

零知ESP32+INA219:锂电池智能监测系统全功能演示

零知ESP32和INA219电流传感器的锂电池充放电监测系统包括,系统硬件组成和接线详解、TFT显示屏实时波形显示效果、充电/放电状态自动识别演示、Web远程监控界面操作、PWM风扇智能控制功能、低电压保护触发测试、系统数据记录和导出功能

五、INA219电流传感器知识点讲解

INA219是一款基于I²C接口的零漂移、双向电流/功率监测传感器。其核心工作原理如下:

根据芯片手册,经过分流电阻N(采样电阻)后,能够采集到的最低有效电压LSB为10uV。

利用欧姆定律计算电流公式:

电流测量:根据Rshunt(0.1Ω)分流电阻两端的电压降计算;电压测量:直接测量总线电压(支持0-26V范围);功率计算:内部乘法器实时计算功率

5.1 寄存器映射

INA219内部有16个寄存器,本项目使用的主要寄存器:

地址 寄存器名称 功能 本项目配置
0x00 Configuration 配置寄存器 0x399F
0x01 Shunt Voltage 分流电压 只读
0x02 Bus Voltage 总线电压 只读
0x03 Power 功率 只读
0x04 Current 电流 只读
0x05 Calibration 校准寄存器 0x1000

校准寄存器(0x05)

校准值计算公式:

其中电流LSB = 最大预期电流 / 32768

Current_LSB=10010^-6=100uA=0.0001A

计算基准值:Cal=0.04096/(Current_LSB/R)=0.04096/(0.0001A0.1R)=4096=0x1000

校准寄存器与缩放

如果发现测量到的电流值有误,用电流表测到的实际值为0.290A,INA219测量结果为0.342A

采用Cal的校准公式(缩放校准后的)Cal=4096*0.290/0.3421 = 3472 = 0x0D90

5.2 I2C总线通信原理

INA219 采用标准 I2C 通信协议,SDA(数据)和 SCL(时钟)为双向引脚

1)串行总线地址

INA219 有两个地址引脚A0 和 A1都设置为GND,该从机地址为0x40

从机地址可通过模块上的 A0和A1 引脚短接修改(只短接A0 从机地址为0x41,只短接A1 从机地址为0x44,短接A0和A1 从机地址0x45)

2)软件I2C时序

总线上的所有从机在 SCL 的上升沿移入从机地址字节,其中最后一位指示要进行的是读操作还是写操作。在第九个时钟脉冲期间,被寻址的从机通过生成确认信号并将 SDA 拉至低电平来响应主机

六、常见问题解答(FAQ)

Q1:为什么电流测量值跳动很大?

*A:尝试以下解决方案:*增加FILTER_WINDOW_SIZE滤波窗口大小

Q2:网页无法访问,或数据不刷新?

*A:请解决:*串口查看 IP 地址是否获取成功,确保设备连接同一路由器,修改 WebSocket 端口;确保server.handleClient()和webSocket.loop()在主循环中执行

项目资源整合

INA219数据手册: INA219 DataSheet

INA219 库: RobTillaart/INA219

相关推荐
全栈前端老曹2 小时前
【包管理】npm最常见的10大问题故障和解决方案
前端·javascript·rust·npm·node.js·json·最佳实践
minglie12 小时前
VSCode 作为 Vivado RTL 编辑器的配置
ide·vscode·编辑器
区区一散修2 小时前
0.IntelliJ IDEA的安装和使用
java·ide·intellij-idea
独处东汉2 小时前
AI辅助Stm32l031项目开发调试板子printf
stm32·单片机·嵌入式硬件
2301_772204282 小时前
嵌入式——51单片机的基本知识
单片机·嵌入式硬件·51单片机
hopsky2 小时前
数据服务开源-SqlRest 1.6 idea中启动 (pg版)
java·ide·intellij-idea
韩师学子--小倪10 小时前
fastjson与gson的toString差异
java·json
上大科技蔡生12 小时前
CS5715:2.7V~26V宽输入,单节锂电池适用,最高36V输出,省掉电感电流检测电阻,软启动时间可调,异步升压DCDC控制器
单片机·嵌入式硬件·dcdc
CQ_YM13 小时前
51单片机(1)
单片机·嵌入式硬件·51单片机