资讯详情

资讯详情

ESP32-S3 4通道ADC采集与Modbus RTU从机实战:Modbus Poll调试教程

简介这份资源面向物联网开发初学者与嵌入式工程师围绕ESP32-S3的四通道ADC数据采集与Modbus RTU通信展开配套完整源码与Modbus Poll上位机软件帮助读者打通从模拟信号采集到PC端数据可视化的完整链路。压缩包共221个文件约14.55MB包含cpp、ino、h等源码文件a、o、d等编译中间产物以及bin固件、json配置、yml与ini工程文件另有exe格式的Modbus Poll工具目录结构清晰便于直接烧录与二次修改。资源中已集成Modbus-Arduino、Modbus-Serial等库文件并附带firmware.bin、bootloader.bin、partitions.bin等可烧录固件省去繁琐的环境搭建步骤。目前已有398人学习下载适合希望快速验证ADC采集与Modbus从站通信方案的开发者参考实践。1. 从一块 ESP32-S3 说起4 通道 ADC 采集到底难在哪很多人第一次用 ESP32-S3 做数据采集都会觉得这事没什么门槛芯片自带 ADC接上传感器读几个数串口一打印就完事了。真到要把它接到工业现场、让上位机通过 Modbus 轮询读取时问题就全冒出来了——读数跳得厉害、通道之间互相串扰、Modbus Poll 连上去一直超时、寄存器地址对不上、采回来的值跟万用表差了十万八千里。标题里说的「4 通道 ADC 数据采集 Modbus Poll 教程」本质上就是解决这条链路ESP32-S3 把 4 路模拟量采准再通过 Modbus RTU 从机协议暴露给上位机用 Modbus Poll 这类工具做验证和调试。这套方案适合谁一类是做小型数据采集盒、环境监测节点、设备状态监控的嵌入式开发者手上有 ESP32-S3 但不想上复杂的工业采集卡另一类是想学 Modbus 从机实现、又不想一上来就啃 PLC 手册的人。它不追求 24 位高精度胜在成本低、开发快、协议通用能把「模拟量 → 数字量 → 寄存器 → 上位机」这条链路完整跑通。下面我按自己实际搭过的顺序把选型、接线、代码、Modbus 映射和踩坑一次讲清楚。2. ESP32-S3 的 ADC 到底能不能用选型与硬件前提2.1 为什么是 ESP32-S3而不是经典 ESP32ESP32-S3 用的是两个 12 位 SAR ADCADC1 和 ADC2总共支持多路通道。相比经典 ESP32S3 在 GPIO 矩阵和外设映射上更灵活USB 原生支持也让调试方便不少。但要注意一个硬约束ADC2 在 Wi-Fi 工作时会被占用如果你打算边联网边采集4 个通道最好全部安排在 ADC1 上。ESP32-S3 的 ADC1 可用通道大致覆盖 GPIO1 到 GPIO10 这一段具体以你手上模组的 datasheet 为准不同封装引出不同选 4 个连续且没被 Strapping 或 USB 占用的脚是第一步。我一般会优先避开 GPIO0、GPIO45、GPIO46 这类 Strapping 脚也别碰 USB_D-/USB_D 对应的脚否则下载和复位会出玄学问题。选好脚之后硬件上还有两件事必须做一是每个模拟输入前加 RC 低通二是保证模拟地和数字地单点汇接。2.2 输入调理电路别把传感器直接怼到 ADC 脚上ESP32-S3 的 ADC 输入范围名义上是 03.3V但实际线性区大概在 0.1V3.1V 之间靠近两端非线性很明显。而且它的输入阻抗不算高直接接高阻信号源会导致分压误差。常见做法是加一级电压跟随器运放再串一个 1kΩ 电阻、并一个 100nF 电容到地构成 RC 滤波。元件参数作用串联电阻1kΩ限流、与电容构成低通对地电容100nF滤高频噪声截止约 1.6kHz运放轨到轨单电源阻抗变换隔离信号源参考内部 1.1V 或 VDD决定满量程如果你的信号本身低阻抗、变化又慢比如热敏电阻分压可以省掉运放但 RC 滤波建议保留。4 个通道的 RC 参数保持一致否则通道间的一致性会很差。2.3 衰减档位与量程一个必须提前定死的参数ESP32-S3 的 ADC 有衰减配置常见有 0dB、2.5dB、6dB、12dB 几档。衰减越大可测电压范围越大但分辨率折算到电压上会变粗。要测 03.3V一般用 12dB 档要测小信号用 0dB 或 2.5dB 更合适。这个参数在代码里通过analogSetAttenuation()设置必须和你的实际输入范围匹配否则要么削顶要么浪费分辨率。// 设置 ADC1 的衰减档位决定可测电压范围 // ADC_ATTEN_DB_12 对应约 0~3.1V 有效线性区 analogSetAttenuation(ADC_11db); // 老版本 API 写法 // 新版本 ESP-IDF / Arduino Core 3.x 用 // analogSetAttenuation(ADC_ATTEN_DB_12);这里有个血泪经验不同 Arduino-ESP32 核心版本对衰减常量的命名不一样2.x 用ADC_11db3.x 改成ADC_ATTEN_DB_12。编译报「未定义」时先查你装的核心版本别急着怀疑硬件。3. 把 4 路模拟量采稳代码结构与滤波策略3.1 最小可运行采集框架先给一个能跑起来的多通道采集骨架用 Arduino 框架写方便快速验证。核心是初始化 4 个 ADC 脚、设置衰减和分辨率、循环读取并做平均。#include Arduino.h // 4 个 ADC1 通道避开 Strapping 和 USB 脚 const int ADC_PINS[4] {1, 2, 3, 4}; const int SAMPLE_NUM 32; // 每通道采样次数用于平均 uint16_t adc_buf[4]; void setup() { Serial.begin(115200); // 12 位分辨率0~4095 analogReadResolution(12); // 统一衰减档匹配 0~3.3V 输入 analogSetAttenuation(ADC_11db); // 可选设置引脚衰减部分核心支持逐脚设置 for (int i 0; i 4; i) { pinMode(ADC_PINS[i], INPUT); } } // 对单通道做多次采样取平均抑制随机噪声 uint16_t readAverage(int pin) { uint32_t sum 0; for (int i 0; i SAMPLE_NUM; i) { sum analogRead(pin); delayMicroseconds(100); // 给采样保持电容一点恢复时间 } return (uint16_t)(sum / SAMPLE_NUM); } void loop() { for (int i 0; i 4; i) { adc_buf[i] readAverage(ADC_PINS[i]); } // 打印原始值便于和 Modbus 寄存器对照 Serial.printf(CH0%u CH1%u CH2%u CH3%u\n, adc_buf[0], adc_buf[1], adc_buf[2], adc_buf[3]); delay(200); }逻辑说明analogReadResolution(12)把返回值定在 04095readAverage做 32 次平均能明显压掉随机跳变每次采样之间加 100 微秒延时是因为 ESP32-S3 的 ADC 采样保持电容在通道切换后需要恢复时间连续猛读会让相邻通道互相污染。参数上SAMPLE_NUM越大越稳但越慢32 是响应速度和稳定性的折中delay(200)只是调试用真正接入 Modbus 后由主循环节奏决定。3.2 多通道串扰换通道后第一笔数据要丢掉上面代码里那个 100 微秒延时就是对付串扰的。ESP32-S3 的 ADC 只有一个采样保持电路多路复用时切换通道后的第一次转换会带着上一通道的残留电荷。稳妥做法是换通道后先读一次丢弃再读有效值。uint16_t readChannelClean(int pin) { analogRead(pin); // 丢弃第一次清残留 delayMicroseconds(50); uint32_t sum 0; for (int i 0; i SAMPLE_NUM; i) { sum analogRead(pin); delayMicroseconds(100); } return (uint16_t)(sum / SAMPLE_NUM); }这个「丢弃第一笔」的习惯是我调了几天才发现通道间总差几十个 LSB 的根因。别小看它4 通道轮询时它直接决定一致性。3.3 软件滤波滑动平均还是中值平均滤波对白噪声有效但遇到尖峰脉冲比如继电器动作干扰就压不住。现场更稳的是「中值 平均」两级先取 N 次采样排序取中值再对连续几次中值做滑动平均。// 简化版取 9 次采样排序后的中值 uint16_t readMedian(int pin) { uint16_t v[9]; for (int i 0; i 9; i) { v[i] analogRead(pin); delayMicroseconds(100); } // 冒泡排序9 个数开销可接受 for (int i 0; i 8; i) for (int j 0; j 8 - i; j) if (v[j] v[j1]) { uint16_t t v[j]; v[j] v[j1]; v[j1] t; } return v[4]; // 中值 }中值滤波对单点尖峰几乎免疫代价是要排序、耗时略高。4 通道、每通道 9 次采样在 240MHz 主频下完全无压力。我的习惯是慢变量温度、液位用中值 平均快变量振动、电流用平均 限幅。4. 用 Modbus RTU 从机把数据交出去寄存器映射与实现4.1 先定寄存器表再写代码Modbus 调试翻车十有八九是寄存器地址没对齐。动手前先把表定死4 个通道各占一个保持寄存器Holding Register功能码 03地址从 0 开始。原始 ADC 值 04095 放一个寄存器如果上位机要工程量再各加一个寄存器放换算后的值比如 ×100 的整数。寄存器地址名称类型说明0x0000CH0_RAWHolding通道0 原始值 0~40950x0001CH1_RAWHolding通道1 原始值0x0002CH2_RAWHolding通道2 原始值0x0003CH3_RAWHolding通道3 原始值0x0004CH0_VOLTHolding通道0 电压 ×1000 (mV)0x0005CH1_VOLTHolding通道1 电压 ×10000x0006CH2_VOLTHolding通道2 电压 ×10000x0007CH3_VOLTHolding通道3 电压 ×1000注意 Modbus 里「地址」和「编号」常差 1协议报文里写 0x0000很多上位机界面显示 40001。Modbus Poll 里要按你的设置填别一个填 0 一个填 1对不上就超时或读错。4.2 用现成库实现从机自己撸 Modbus RTU 帧不是不行但没必要。常见做法是用ModbusRTU或eModbus这类库把 ESP32-S3 当从机Slave串口接 RS485 收发器。下面给一个基于常见从机库的骨架重点看寄存器回调怎么把 ADC 值塞进去。#include ModbusRTU.h ModbusRTU mb; // 从机地址 1串口用 Serial2RS485 方向脚 5 #define SLAVE_ID 1 #define RS485_DE 5 uint16_t holdingRegs[8]; // 对应上面 8 个寄存器 // 库在收到读保持寄存器请求时回调这里把最新 ADC 值填进去 uint16_t cbReadHolding(TRegister* reg, uint16_t val) { uint16_t addr reg-address.address; if (addr 8) return holdingRegs[addr]; return 0; } void setup() { Serial.begin(115200); Serial2.begin(9600, SERIAL_8N1, 18, 17); // RX18 TX17 pinMode(RS485_DE, OUTPUT); digitalWrite(RS485_DE, LOW); mb.begin(Serial2, RS485_DE); mb.slave(SLAVE_ID); // 注册 0~7 共 8 个保持寄存器 mb.addHreg(0, 0, 8); mb.onGet(cbReadHolding); } void loop() { // 采集并更新寄存器 for (int i 0; i 4; i) { uint16_t raw readChannelClean(ADC_PINS[i]); holdingRegs[i] raw; // 电压换算raw/4095*3.3V再 ×1000 存成 mV 整数 holdingRegs[4 i] (uint16_t)((float)raw / 4095.0f * 3300.0f); } mb.task(); // 处理 Modbus 收发 delay(50); }逻辑说明mb.addHreg(0, 0, 8)声明从地址 0 起 8 个保持寄存器onGet回调在主机读时触发返回当前值。RS485 方向脚由库自动翻转前提是你把 DE 脚告诉它。参数上波特率 9600、8N1 是最通用的组合Modbus Poll 里要设成完全一致从机地址 1 要唯一总线上不能重复。4.3 电压换算与工程量标定上面把电压存成 mV 整数是为了避免浮点在寄存器里没法直接放。如果你要传温度、压力这类工程量建议在固件里做线性标定工程值 (raw - raw_min) / (raw_max - raw_min) * (eng_max - eng_min) eng_min再乘个系数存整数。标定用的 raw_min/raw_max 要实测别照抄理论值因为每片芯片的线性区略有差异。5. 用 Modbus Poll 验证连接、读值与排错5.1 Modbus Poll 侧的正确配置Modbus Poll 是上位机调试工具配置项必须和固件一一对应。打开后新建连接关键参数如下配置项值对应固件ModeRTU从机用 RTUPort你的 USB 转 485 口物理连接Baud9600Serial2.beginData/Parity/Stop8/N/1SERIAL_8N1Slave ID1SLAVE_IDFunction03 Holding RegisteraddHregAddress0起始地址Quantity8寄存器数量Scan Rate500ms主循环节奏配置完点连接正常的话表格里 8 行数据会以 500ms 刷新。如果一直显示 Timeout先查物理层A/B 线有没有接反、终端电阻有没有、共地没有。RS485 不共地是新手最常翻的车。5.2 读到的值和串口打印对不上怎么办先确认 Modbus Poll 的显示格式。默认可能是 Signed、Hex 或 Float而你存的是无符号整数。把显示切成 Unsigned 或 Decimal再和串口打印对照。如果还是差检查是不是地址偏移了一位——有些库的addHreg(0,...)实际映射到协议地址 0但 Modbus Poll 里如果勾了「PLC addresses (base 1)」界面显示会整体加 1。5.3 用轮询日志定位超时Modbus Poll 底部有 Traffic 窗口能看到实际发出的请求帧和从机回应。正常一问一答超时就是只有请求没有回应。这时回到固件侧mb.task()有没有被长时间阻塞采集里的delay是不是太长导致错过响应窗口我遇到过采集平均 32 次、每次 delay 100 微秒加上串口打印主循环一次超过 100ms主机 500ms 轮询虽然能撑住但一旦缩短扫描周期就丢包。解决办法是把采集和 Modbus 处理解耦或者压缩平均次数。6. 避坑与排查4 通道采集接 Modbus 最容易翻车的 5 个点现象一通道之间数值互相影响动一路其他路也跳。原因ADC 多路复用后采样保持电容残留换通道第一笔没丢弃。 解决换通道后先analogRead一次丢弃再延时 50 微秒读有效值如 3.2 节所示。现象二Modbus Poll 一直 Timeout串口打印却正常。原因RS485 收发方向脚没配对或 A/B 接反或没共地。 解决先用示波器或逻辑分析仪看 485 差分线上有没有回应确认 DE 脚由库控制A/B 对调试一次两端共地。现象三读到的寄存器值偶尔错位CH0 的值跑到 CH1。原因寄存器地址和上位机编号基准不一致或数组越界。 解决统一用协议地址 0 起Modbus Poll 里关掉 base 1 显示检查holdingRegs下标和addHreg范围一致。现象四靠近 0V 或 3.3V 时读数明显非线性。原因ADC 线性区有限衰减档没选对。 解决把输入信号限制在 0.13.1V选合适衰减档必要时做两点或多点标定用查表或分段线性修正。现象五Wi-Fi 一开ADC 值就乱跳。原因ADC2 被 Wi-Fi 占用或射频干扰耦合进模拟线。 解决4 通道全放 ADC1模拟走线远离天线和电源RC 滤波参数加大采集时暂时关 Wi-Fi 或降低发射功率。7. 进阶把采集精度和协议稳定性再往上抬一档前面跑通的是「能用」这一章说「用得住」。第一个技巧是用 ESP32-S3 的内部校准。芯片出厂时每个 ADC 有一组校准值存在 eFuse 里Arduino 核心的analogRead在部分版本已经自动应用但如果你用 ESP-IDF 的adc_cali接口可以显式拿到校准句柄把原始码值转成 mV比手算raw/4095*3300准得多。做法是初始化adc_cali_create_scheme_curve_fitting再对每次读数调adc_cali_raw_to_voltage。代价是多占一点 flash 和 RAM换来的是全温区误差明显收窄。第二个技巧是给 Modbus 加异常保护。从机不能假设主机永远规规矩矩。读超出范围的寄存器地址时库一般会自动回异常码 02非法数据地址但你要确认它真的回了而不是死等。可以在onGet里对越界地址返回 0 并打日志方便定位。另外串口长时间收不到完整帧要有超时复位避免半帧卡死状态机。第三个技巧是采集与通信分时。如果扫描周期要求很紧比如 100ms把 ADC 采集放到定时器中断或独立任务里主循环只负责mb.task()和搬运数据。ESP32-S3 双核可以把 Modbus 放 core 0、采集放 core 1用队列传值避免长延时阻塞响应。进阶项收益代价内部校准精度提升温漂小代码复杂度上升异常码保护总线更健壮需测试各异常分支双核分时响应快、不丢包需处理并发与队列最后说个验证方法拿一个可调直流源从 0.2V 到 3.0V 每隔 0.2V 打一个点同时记录万用表读数、串口原始值、Modbus Poll 读到的 mV 值做一张三点对照表。误差在 ±30mV 以内这套 4 通道采集就算合格超过 100mV回去查衰减档和校准。我自己调这套东西时最大的教训是别一上来就怀疑芯片九成问题出在接线、共地和地址对齐上。把这三样先排干净剩下的才是代码的事。希望帮到你。本文还有配套的精品资源点击获取
觉得有用,分享给同行:

为您的企业打造数字门面

稳重轻奢商务风格,端正雅致视觉,长效耐看不易过时。

立即咨询 →