零知IDE——基于ESP32的ADS1115 多通道数据采集系统:从差分测量到Web实时监控
✔零知开源(零知IDE)是一个专为电子初学者/电子兴趣爱好者设计的开源软硬件平台,在硬件上提供超高性价比STM32系列开发板、物联网控制板。取消了Bootloader程序烧录,让开发重心从 “配置环境” 转移到 “创意实现”,极大降低了技术门槛。零知IDE编程软件,内置上千个覆盖多场景的示例代码,支持项目源码一键下载,项目文章在线浏览。零知开源(零知IDE)平台通过软硬件协同创新,让你的创意快速转化为实物,来动手试试吧!
✔访问零知实验室,获取更多实战项目和教程资源吧!
www.lingzhilab.com
目录
项目概述
本项目基于零知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 初始化配置
// 增益设置: 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 多模式数据采集
// --- 变量存储 --- 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数据接口
// --- 全局对象 --- 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 前端数据可视化
<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>系统流程图

差分测量模式深度解析
// 读取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