零知IDE——基于ESP32的ADS1115 多通道数据采集系统:从差分测量到Web实时监控

✔零知开源(零知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 Web数据接口](#3.3 Web数据接口)

[3.4 前端数据可视化](#3.4 前端数据可视化)

四、项目结果演示

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

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

五、ADS1115技术讲解

[5.1 寄存器配置](#5.1 寄存器配置)

[5.2 I2C 通信协议](#5.2 I2C 通信协议)

六、常见问题解答(FAQ)

Q1:测量负电压的原理?

[Q2:Web 页面能打开,但数值不更新 / 波形无变化?](#Q2:Web 页面能打开,但数值不更新 / 波形无变化?)

[Q3: I2C地址冲突怎么办?](#Q3: I2C地址冲突怎么办?)


项目概述

本项目基于零知ESP32 -WROOM-32主控板,结合TI ADS1115 16位高精度ADC模数转换器,实现了一个功能完整的多通道数据采集与Web监控系统。系统不仅支持传统的4通道单端电压测量,更实现了A0-A1差分电压测量功能,并通过现代化的Web界面实时展示数据变化趋势

项目难点及解决方案

问题描述:在单次转换模式下,每次测量都需要重新配置MUX通道选择寄存器。频繁切换差分和单端模式可能导致数据读取错误

**解决方案:**采集流程中先读取差分数据、再读取单端数据,每次读取前库自动完成寄存器重新配置,避免手动操作寄存器的出错

一、系统接线部分

1.1 硬件清单

组件 型号/规格 数量 备注
主控板 零知ESP32-WROOM-32 1 核心控制器
ADC模块 ADS1115 16位 1 高精度模数转换
杜邦线 公对公 若干 连接线
电位器 10kΩ 2 模拟信号源
电源 5V/2A 1 供电

1.2 接线方案表

根据代码设计,接线配置如下:

ESP32引脚 ADS1115引脚 连接说明 备注
GPIO21 SDA I2C数据线 模块内置上拉电阻
GPIO22 SCL I2C时钟线 模块内置上拉电阻
3.3V VDD 电源正极 电源5V供电,避免模块损坏
GND GND 电源地 共地很重要
电位器1 AIN0 通道0输入 差分正输入端
电位器2 AIN1 通道1输入 差分负输入端
GND AIN2 通道2输入 单端测量
- AIN3 通道3输入 单端测量

注意:GAIN_ONE模式输入电压必须在±4.096V范围内

1.3 接线示意图

PS:旋转电位器和ADS1115 ADC模块都采用系统3.3V供电

1.4 实物连接图

二、安装与使用部分

2.1 开源平台-输入"ADS1115 多通道数据采集 "并搜索-代码下载自动打开

2.2 连接-验证-上传

2.3 调试-串口监视器

三、代码讲解部分

代码文件结构

主程序文件,实现 WiFi 连接、ADS1115 数据采集、WebServer 搭建;头文件,存储 Web 页面的 HTML/CSS/JavaScript 代码

3.1 初始化配置

cpp 复制代码
// 增益设置: GAIN_ONE (+/- 4.096V)
// 1 bit = 0.125mV
const float multiplier = 0.000125; 

void setup() {
  Serial.begin(115200);

  // 1. 初始化 ADS1115
  if (!ads.begin()) {
    Serial.println("无法初始化 ADS1115,请检查连线!");
    while (1);
  }
  // 设置增益以匹配 3.3V 系统,同时保留一定的余量
  ads.setGain(GAIN_ONE); 

  // 2. 连接 WiFi
  WiFi.begin(ssid, password);
  Serial.print("正在连接 WiFi");
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println("\nWiFi 已连接");
  Serial.print("访问地址: http://");
  Serial.println(WiFi.localIP());

  // 3. 配置 Web 服务器路由
  // 首页
  server.on("/", HTTP_GET, []() {
    server.send_P(200, "text/html", index_html);
  });

  // API 接口:返回 JSON 数据
  server.on("/data", HTTP_GET, []() {
    String json = "{";
    json += "\"diff_raw\":" + String(raw_diff) + ",";
    json += "\"diff_v\":" + String(volt_diff, 4) + ",";
    json += "\"v0\":" + String(volt_a0, 4) + ",";
    json += "\"v1\":" + String(volt_a1, 4) + ",";
    json += "\"v2\":" + String(volt_a2, 4);
    json += "}";
    server.send(200, "application/json", json);
  });

  server.begin();
  Serial.println("HTTP 服务器已启动");
}

GAIN_ONE增益倍数1,满量程±4.096V;multiplier每个LSB对应的电压值(0.125mV)

3.2 多模式数据采集

cpp 复制代码
// --- 变量存储 ---
int16_t raw_diff; // 差分原始值
int16_t raw_a0, raw_a1, raw_a2; // 单端原始值
float volt_diff, volt_a0, volt_a1, volt_a2; // 转换后的电压值

void loop() {
  server.handleClient();

  // --- 关键步骤:切换模式读取数据 ---
  // 注意:频繁切换配置寄存器会消耗一点时间,但为了同时显示单端和差分是必须的。
  
  // 1. 读取 A0-A1 差分值 (Differential)
  // 如果 A0 > A1,值为正;如果 A1 > A0,值为负
  raw_diff = ads.readADC_Differential_0_1();
  volt_diff = raw_diff * multiplier;

  // 2. 读取单端值 (Single Ended) 用于参考
  raw_a0 = ads.readADC_SingleEnded(0);
  volt_a0 = raw_a0 * multiplier;

  raw_a1 = ads.readADC_SingleEnded(1);
  volt_a1 = raw_a1 * multiplier;

  raw_a2 = ads.readADC_SingleEnded(2); // 备用通道
  volt_a2 = raw_a2 * multiplier;

  // 简单的串口调试 (可选)
  // Serial.printf("Diff: %.4f V, A0: %.4f V, A1: %.4f V\n", volt_diff, volt_a0, volt_a1);
  
  delay(5); // 短暂延时,避免 CPU 过载
}

测量AIN0和AIN1之间的电压差、结果可正可负,代表相对极性、差分模式能抑制共模噪声,提高测量精度

3.3 Web数据接口

cpp 复制代码
// --- 全局对象 ---
WebServer server(80);

// 3. 配置 Web 服务器路由
// 首页
server.on("/", HTTP_GET, []() {
  server.send_P(200, "text/html", index_html);
});

// API 接口:返回 JSON 数据
server.on("/data", HTTP_GET, []() {
  String json = "{";
  json += "\"diff_raw\":" + String(raw_diff) + ",";
  json += "\"diff_v\":" + String(volt_diff, 4) + ",";
  json += "\"v0\":" + String(volt_a0, 4) + ",";
  json += "\"v1\":" + String(volt_a1, 4) + ",";
  json += "\"v2\":" + String(volt_a2, 4);
  json += "}";
  server.send(200, "application/json", json);
});

server.begin();

使用JSON格式便于前端解析、保留4位小数确保精度同时提供原始值和计算值

3.4 前端数据可视化

cpp 复制代码
<script>
    // --- Chart.js 初始化配置 ---
    const ctx = document.getElementById('voltageChart').getContext('2d');
    
    // 创建渐变填充
    let gradient = ctx.createLinearGradient(0, 0, 0, 400);
    gradient.addColorStop(0, 'rgba(102, 252, 241, 0.4)'); // 顶部颜色
    gradient.addColorStop(1, 'rgba(102, 252, 241, 0)');   // 底部透明

    const voltageChart = new Chart(ctx, {
      type: 'line',
      data: {
        labels: [], // 时间轴标签
        datasets: [{
          label: 'Differential Voltage (A0 - A1)',
          data: [],
          borderColor: '#66fcf1',
          backgroundColor: gradient,
          borderWidth: 2,
          pointRadius: 0, // 隐藏数据点,使线条更平滑
          fill: true,
          tension: 0.4 // 曲线平滑度
        }]
      },
      options: {
        responsive: true,
        maintainAspectRatio: false,
        scales: {
          x: { display: false }, // 隐藏X轴标签保持简洁
          y: {
            grid: { color: '#333' },
            ticks: { color: '#888' },
            suggestedMin: -4.0,
            suggestedMax: 4.0
          }
        },
        plugins: {
          legend: { labels: { color: '#c5c6c7' } }
        },
        animation: false // 禁用动画以提高实时性能
      }
    });

    // --- 数据获取与更新逻辑 ---
    const maxDataPoints = 100; // 图表保留的数据点数量

    function updateDashboard() {
      fetch('/data')
        .then(response => response.json())
        .then(data => {
          // 1. 更新卡片数值
          document.getElementById('valDiff').innerText = data.diff_v.toFixed(4);
          document.getElementById('val0').innerText = data.v0.toFixed(4);
          document.getElementById('val1').innerText = data.v1.toFixed(4);
          document.getElementById('val2').innerText = data.v2.toFixed(4);

          // 2. 更新图表
          // 获取当前时间戳作为简单的标签
          const now = new Date().toLocaleTimeString();

          // 添加新数据
          voltageChart.data.labels.push(now);
          voltageChart.data.datasets[0].data.push(data.diff_v);

          // 如果数据点过多,移除最早的数据
          if (voltageChart.data.labels.length > maxDataPoints) {
            voltageChart.data.labels.shift();
            voltageChart.data.datasets[0].data.shift();
          }

          voltageChart.update();
        })
        .catch(error => console.error('Error:', error));
    }

    // 设置刷新频率 (100ms = 10Hz刷新率)
    setInterval(updateDashboard, 100);

  </script>

系统流程图

差分测量模式深度解析

cpp 复制代码
// 读取A0-A1差分原始值,底层自动配置寄存器
raw_diff = ads.readADC_Differential_0_1();
// 转换为实际电压(有符号,支持正负电位差)
volt_diff = raw_diff * multiplier;

ADS1115 的差分测量是将两个输入引脚(A0/A1)的电位差作为输入信号,而非相对于 GND 的单端信号,核心优势是抑制共模干扰

差分测量原理图

当 A0 电压 > A1 电压时,raw_diff为正,volt_diff为正;当 A0 电压 < A1 电压时,raw_diff为负,volt_diff

差分量程与增益配置一致(GAIN_ONE 对应 ±4.096V),即 A0-A1 的电位差范围为 - 4.096V~+4.096V

四、项目结果演示

4.1 操作流程

按照接线方案表完成 ESP32 与 ADS1115 的连接,A0/A1 接电位器(模拟差分信号),A2 接备用传感器,修改主程序SSID和PASSWORD为实际 WiFi 路由信息

上电运行

给 ESP32 上电,打开串口监视器,设置波特率为 115200,等待 WiFi 连接成功,记录打印的 "访问地址,如http://192.168.x.x

数据查看

打开电脑 / 手机浏览器,输入上述访问地址,即可看到实时电压数值和差分电压波形

信号调试

调节电位器,观察 A0/A1 电压变化,以及差分电压的实时波形变化

4.2 视频演示

ESP32+ADS1115多通道采集:差分电压实时波形可视化

完整演示 ESP32 驱动 ADS1115 的多通道单端 / 差分采集流程,包含代码烧录、代码修改、WiFi 连接、Web 可视化全流程,直观展示差分电压随输入信号变化的实时波形,以及各通道电压的高精度显示效果

五、ADS1115技术讲解

ADS1115 具有 一个输入多路复用器 (MUX),可实现双路差分输入或 四路单端输入测量。兼容 I 2C 的 16 位低功耗精密模数转换器 (ADC)

Multiplexer 复用器

ADS1115包含两个差分输入,AIN0和AIN1可以与AIN3进行差分测量,多路复用器由配置寄存器中的位MUX [2:0]配置

5.1 寄存器配置

ADS1115 有 4 个寄存器,本项目核心用到配置寄存器(0x01) 和转换寄存器(0x00)

转换寄存器(0x00)

存储 ADC 转换后有符号的原始值,ESP32 读取该寄存器值后,乘以转换系数即可得到实际电压

配置寄存器(0x01)[15:0]

16位配置寄存器用于控制工作模式、输入选择、数据速率、满量程范围和比较器模式

①MUX位详解(输入选择):

本项目设置MUX[2:0]位为000,使用差分通道AIN0和AIN1

②PGA位详解(增益设置):

本项目设置PGA[2:0]位为001,采用GAIN_ONE增益

5.2 I2C 通信协议

① I2C地址选择

ADS1115有一个地址引脚 ADDR,用于配置器件的 I2C 地址。该引脚可连接至 GND、VDD、SDA 或 SCL,通过一个引脚即可选择四种不同的地址

② 向寄存器写入数据

要访问 ADS111x 中的特定寄存器,主机必须首先向地址指针寄存器中的寄存器地址指针位 P [1:0] 写入适当的值。

地址指针寄存器是在从机地址字节、低电平的读 / 写位以及从机成功应答之后直接写入,从机进行应答,主机发出停止条件或重复起始条件

③ 从寄存器读取数据

从 ADS111x 读取数据时,先前写入位 P [1:0] 的值决定了要读取的寄存器。要更改读取的寄存器,必须向 P [1:0] 写入新值

④ 数据格式

以二进制补码格式提供 16 位数据。正满量程(+FS)输入产生的输出代码为 7FFFh,负满量程(--FS)输入产生的输出代码为 8000h

对于超过满量程的信号,输出会钳位在这些代码上

电压计算原理

ADS1115的输出代码与输入电压的关系为:电压 = (ADC读数 × 满量程电压) / (2¹⁵ - 1)

在GAIN_ONE模式下:满量程电压 = 4.096V、分辨率 = 4.096V / 32767 ≈ 0.125mV

六、常见问题解答(FAQ)

Q1:测量负电压的原理?

*A: ADS1115本身不能直接测量负电压(相对GND)。但通过以下方法间接测量:*使用差分模式时V-接负电压,V+接GND、使用电平移位电路将负电压抬升到0-3.3V范围、使用双电源供电给ADS1115提供±2.5V电源

Q2:Web 页面能打开,但数值不更新 / 波形无变化?

*A:排查步骤:*检查 ESP32 是否仍连接 WiFi、检查浏览器控制台(F12)是否有报错、确认代码中setInterval(updateDashboard, 100)未被注释,刷新频率正常

Q3: I2C地址冲突怎么办?

A:ADS1115的I2C地址由ADDR引脚决定:默认ADDR接地0x48、ADDR接VDD地址为0x49、ADDR接SDA地址为0x4A、ADDR接SCL地址为0x4B。在代码Adafruit_ADS1115 ads(0x49); 中修改地址为0x49

项目资源整合:

ADS1115库文件:Adafruit_ADS1X15

ADS1115数据手册:ADS111x datasheet

相关推荐
Lxinccode3 小时前
python(70) : 网页IDE
开发语言·ide·python·网页ide
shishi52115 小时前
trae重装后,无法预览调试弹窗报错的解决方案
ide·计算机视觉·语言模型
小皮每天进步一点点18 小时前
IDEA找不到源码
java·ide·intellij-idea
LCG米19 小时前
开发环境搭建:告别Keil,用CLion+STM32CubeMX打造智能嵌入式IDE
ide·stm32·嵌入式硬件
用户405383693520 小时前
开源语音识别FunASR入门详解
ide·macos·xcode
何亚告1 天前
VScode引入claude+deepseek
ide·vscode·编辑器
dyxal1 天前
VSCode中,通过SFTP插件管理远程服务器文件
服务器·ide·vscode
俩个逗号。。1 天前
Android Studio build生成apk和run生成apk签名不一样
android·ide·android studio
儿歌八万首1 天前
Android Studio 调试指南:从入门到进阶
android·ide·android studio