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

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

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

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

www.lingzhilab.com

目录

一、系统接线部分

1.1 硬件清单

1.2 接线方案表

1.3 接线示意图

1.4 实物连接图

二、安装与使用部分

三、代码讲解部分

3.1 初始化配置

3.2 多模式数据采集

3.3 Web数据接口

3.4 前端数据可视化

四、项目结果演示

4.1 操作流程

4.2 视频演示

五、ADS1115技术讲解

5.1 寄存器配置

5.2 I2C 通信协议

六、常见问题解答(FAQ)

Q1:测量负电压的原理?

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

Q3: I2C地址冲突怎么办?


项目概述

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

项目难点及解决方案

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

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

一、系统接线部分

1.1 硬件清单

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

1.2 接线方案表

        根据代码设计,接线配置如下:
ESP32引脚ADS1115引脚连接说明备注
GPIO21SDAI2C数据线模块内置上拉电阻
GPIO22SCLI2C时钟线模块内置上拉电阻
3.3VVDD电源正极电源5V供电,避免模块损坏
GNDGND电源地共地很重要
电位器1AIN0通道0输入差分正输入端
电位器2AIN1通道1输入差分负输入端
GNDAIN2通道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

Read more

前端实现图片防盗链技术详解 - 原理分析与SpringBoot解决方案

前端实现图片防盗链技术详解 - 原理分析与SpringBoot解决方案

🌷 古之立大事者,不惟有超世之才,亦必有坚忍不拔之志 🎐 个人CSND主页——Micro麦可乐的博客 🐥《Docker实操教程》专栏以最新的Centos版本为基础进行Docker实操教程,入门到实战 🌺《RabbitMQ》专栏19年编写主要介绍使用JAVA开发RabbitMQ的系列教程,从基础知识到项目实战 🌸《设计模式》专栏以实际的生活场景为案例进行讲解,让大家对设计模式有一个更清晰的理解 🌛《开源项目》本专栏主要介绍目前热门的开源项目,带大家快速了解并轻松上手使用 🍎《前端技术》专栏以实战为主介绍日常开发中前端应用的一些功能以及技巧,均附有完整的代码示例 ✨《开发技巧》本专栏包含了各种系统的设计原理以及注意事项,并分享一些日常开发的功能小技巧 💕《Jenkins实战》专栏主要介绍Jenkins+Docker的实战教程,让你快速掌握项目CI/CD,是2024年最新的实战教程 🌞《Spring Boot》专栏主要介绍我们日常工作项目中经常应用到的功能以及技巧,代码样例完整 👍《Spring Security》专栏中我们将逐步深入Spring Security的各个技

速通前端篇 —— HTML

速通前端篇 —— HTML

找往期文章包括但不限于本期文章中不懂的知识点: 个人主页:我要学编程程(ಥ_ಥ)-ZEEKLOG博客 所属专栏:速通前端 目录 HTML的介绍 如何创建HTML文件 HTML 文件基本结构 HTML常用标签 title标签   标题标签 h1-h6  段落标签 p 换行标签 br 图片标签 img  超链接 a 表格标签 table 表单标签 input 标签 form 标签  select 标签 textarea 标签  无语义标签 div&span 列表标签  综合练习:用户登录  由于我们Java是属于后端开发的,因此对于前端部分,我们只需要简单了解,达到认识与编写基本的代码即可。  HTML的介绍 HTML(Hyper

ofa_image-caption代码实例:扩展支持WebP格式与EXIF元数据保留功能

ofa_image-caption代码实例:扩展支持WebP格式与EXIF元数据保留功能 1. 引言 你有没有遇到过这种情况?从手机或相机里导出一堆照片,想快速整理归档,却要一张张手动写描述,费时又费力。或者,在做内容创作时,需要为大量图片配上精准的英文说明,人工处理效率极低。 今天要介绍的这个工具,就是来解决这个痛点的。它叫 ofa_image-caption,是一个纯本地运行的图像描述生成工具。简单来说,你给它一张图,它就能用英文告诉你这张图里有什么。 这个工具的核心是基于一个叫 OFA 的模型,这个模型在图像描述生成领域表现很不错。我们之前发布的版本已经能很好地处理 JPG、PNG 这些常见格式了。但最近,越来越多的用户开始使用 WebP 这种更高效的图片格式,同时,很多摄影师和内容创作者也希望生成的描述能保留图片拍摄时的原始信息(比如拍摄时间、相机型号)。 所以,我们对这个工具进行了一次重要的升级。这篇文章,我就带你手把手看看,我们是如何在原有代码基础上,扩展了对 WebP 格式的支持,并实现了 EXIF 元数据的保留功能。

前端检查内存泄露

前言 前端应用的内存泄露,指不再使用内存未被释放,导致页面占用内存持续增长,轻则引发页面卡顿,加载缓慢,重则导致浏览器崩溃, 尤其在单页应用SPA中,路由切换频繁但内存不回收,问题会被无限放大,比如用户长时间使用某后台管理系统,可能出现操作响应式延迟,甚至需要强制刷新才能恢复,这很可能是内存泄露在"作祟" 一. 前端常见的内存泄露场景 1. 意外的全局变量:未声明的变量(如a = 10而非let a = 10)会挂载到window上,页面不刷新就不会释放; 2. 闭包滥用:闭包会保留对外部作用域的引用,若长期持有 DOM 或大型对象,会导致内存无法回收(如未清理的事件监听回调) ; 3. 未清理的 DOM 引用:删除 DOM 节点后,仍保留其引用(如let el =       document.getElementById('test&