☰
ESP32隐藏无线电通路:用ADC引脚探测WiFi射频活动
2026/10/12 5:12:26 网站建设 项目流程

那次调试让我印象特别深。一批基于ESP32的低功耗采集板发出去之后,某客户反馈说电池电压读数时不时猛跳一下,直接触发了低压告警。我一开始怀疑是电源纹波或者参考电压漂移,但示波器戳上去也看不出明显异常。后来把WiFi模块强制关闭再测,ADC读数立刻安静了;只要WiFi一开,采集值就开始有节奏地抖动。

真正让我愣住的是后面那一步:我把探头接到一个完全悬空的ADC引脚上,示波器居然能清晰地看到一串和WiFi发射周期同步的包络脉冲。换句话说,这块芯片内部存在一条路径,能把无线电能量“翻译”成模拟采样值的变化。这条路径没有出现在常规手册的功能框图里,但在特定条件下非常稳定、可复现,我后来习惯叫它“隐藏的无线电通路”。

这篇文章就围绕这条通路展开,讲清楚它到底是什么、为什么原厂不写进文档、怎么用代码验证,以及我基于它做出来的几个实用玩法。适合正在做ESP32物联网硬件、嵌入式低功耗设计,或者对芯片底层信号路径感兴趣的开发者和硬件爱好者。看完之后,你可以自己复现,并且大概率能用到实际项目里。

1. 一条不该存在的通路——先从一次干扰排查说起

1.1 官方文档没有骗你,只是没讲全

ESP32系列的芯片资料通常分成几层来看。第一层是选型手册和硬件设计指南,主要讲供电、引脚定义、天线摆放、时钟要求这些最基本的约束。第二层是技术参考手册,会详细到每个外设的寄存器位、GPIO矩阵、DMA通道,几百页甚至上千页,但它的重点在外设逻辑,而不是物理层的电磁耦合行为。

问题就在这。技术参考手册里写了SARADC怎么配置、通道怎么切换、衰减档位怎么设,但它不会告诉你:当射频前端以大功率发射时,芯片内部基板和引脚框架之间会形成电磁耦合,这个耦合信号能一路串到模拟采样通路里。原厂不写这个,不是因为“故意隐瞒”,而是官方文档的定位是“告诉你怎么按规范用”,不是“告诉你怎么观察异常现象”。

我实际测下来的结果是:在WiFi以802.11n OFDM模式持续上传数据时,悬空ADC引脚的原始采样值会出现几十个LSB量级的波动,波动节奏和发包间隔强相关。如果你只是做普通的电压采集,这个现象就是需要滤掉的噪声;但反过来想,它也是一条可以主动利用的“观察无线电活动”的窗口。

1.2 这条通路的物理模型

为了方便理解,我用一个“三层漏斗”的模型来解释它:

第一层是引脚和走线。ESP32的任何一个GPIO引脚在芯片内部都有一段金属引线,外部PCB上还有对应的焊盘和走线。这些东西本质上就是一根天线,长度从几毫米到几厘米不等。射频信号辐射到金属导体上时,会感应出微弱的交变电流。

第二层是封装内部结构。芯片内部引线框架和基板之间存在寄生电容和寄生电感,射频感应电流并不只会沿着你设计的电路路径走,它也会穿过这些寄生参数,串入模拟外设的输入结构。对SARADC来说,输入端通常有一个采样电容和开关网络,高频干扰到达之后,经过采样开关的混叠效应,最终会以“直流或低频包络”的形式进入转换结果。

第三层才是ADC本身。SARADC不是射频接收机,它没有超外差结构,也没有解调器,它只能对一个时间窗口内的电压做积分。所以不要指望它能直接输出解调后的比特流,它输出的只是“这一小段时间里,引脚上感应到的能量强弱”。

我用一个类比来帮助理解这条通路:矿石收音机其实不需要电池,天线检波之后直接驱动耳机就能出声。ESP32的这条通路虽然不是专门为接收设计的,但它确实在设计上保留了一个“感应高阻节点”,在合适的条件下,就像一台极简能量计。

1.3 为什么平时开发时很少意识到它的存在

大多数项目里,ADC引脚都连接了外部电路,比如电池分压电阻、传感器输出端、运放输出端。外部电路的源阻抗通常比较低,比如几欧到几十千欧,再加上板上的滤波电容,射频感应电压会被严重分流或短路。这样一来,即使芯片内部有耦合路径,你从外部读数上也几乎看不到波动。

只有把引脚悬空,或者源阻抗非常高、带宽非常窄的情况下,这条通路的“天线效应”才会显现出来。而悬空引脚在开发时通常被认为是最不标准的做法,所以大部分人根本不会往这个方向测。

另一个原因是测试工具的限制。示波器探头接上去时,探头本身的电容也有几十皮法,高频信号会被探头钳住。你看到的波形往往是“经过探头负载之后”的残留信号,和芯片内部ADC实际感受到的并不完全一样。

2. 为什么原厂不把这条通路写进手册——背后的工程取舍

2.1 芯片文档是分层开放的

芯片原厂的资料体系,不是一份手册通吃所有读者。选型和Layout阶段用的是一套公开FAQ,寄存器级开发用的是技术参考手册,而上射频调试、产测校准、失效分析这些环节,往往需要签订更深的NDA协议才能拿到内部应用笔记。

这条无线电通路并不是一个“发射/接收功能模块”,它更像是一个物理现象。原厂不会为物理现象写一本完整的手册,因为它涉及很多变量:天线形式、走线阻抗、外壳材料、周围电磁环境,甚至测试桌面的高度。强行文档化反而会让大量用户产生误解,以为这是一个可以精确控制的功能。

我在一次和原厂技术支持交流时,对方提到过一个观点:射频链路的那些测试模式和特殊通路,最早是给内部验证团队和生产团队用的。量产测试时,通过特定的寄存器配置,可以让芯片进入RF测试模式,在产线上校准发射功率和接收灵敏度。这些寄存器值保存在工厂固件里,对外不公开,因为一旦用户乱写,可能导致射频性能下降甚至完全无法通过认证。

2.2 两条存在但容易混淆的“通路”

严格来说,ESP32体系里至少存在两类和“无线电”有关的隐藏通路。

第一类是真正的RF测试模式。比如让LNA_IN引脚直接旁路到接收链路的某些测试配置,或者让发射链路输出特定调制信号到某个测试点。这属于芯片内部射频测试总线的一部分,绝大多数开发者永远用不到。我也没有拿到完整的测试寄存器表,所以不在这里展开具体数值,避免误导。

第二类是我前面说的“无意耦合通路”。它不是被设计出来的,而是寄生效应和天线效应叠加的结果。这种通路没有专门的使能开关,也没有寄存器可以直接调增益,但它结构简单、可重复,完全可以在应用层复现。

从芯片设计者的角度看,第二类通路属于“需要抑制的干扰源”。他们会通过版图优化、ESD结构设计、引脚排列等手段来降低耦合量,但这无法做到百分之百消除。于是我们这些应用工程师就有机会在满足功能需求的同时,额外获得一个“无线电活动传感器”。

2.3 它对普通开发者意味着什么

你不需要签署任何NDA就能利用这条通路。因为你只是在使用普通的ADC外设,只是把一个引脚留空,然后读取数值而已。它消耗的资源可以忽略不计,不需要额外的射频前端,不需要外部混频器或检波二极管,只需要几十行代码。

它适合做什么?适合做“无线电活动的存在性检测”。我可以明确告诉你,不适合做什么:不适合协议解码,不适合精确测量信号强度,更不适合当作合法合规的任何专业测量仪器。它的价值在于低成本、快速判断“附近是否有2.4GHz信号在活动”,以及“活动节奏大概是什么样的”。

3. 实操验证——把隐藏通路拉出来跑一遍

3.1 准备工作:硬件和软件环境

在开始之前,准备好这些东西:

  • 一块ESP32开发板,最好是没有金属屏蔽罩的模组。我用的是一块公版DevKitC样式的板子,板载PCB天线。
  • 一根5厘米以上长度的杜邦线或导线。如果只想验证“悬空引脚感应的环境信号”,杜邦线能显著增强天线效应,效果更明显。
  • 另一块能产生2.4GHz无线信号的设备,可以是手机、路由器,也可以是另一块ESP32板。我自己推荐用另一块ESP32,因为它可以精确控制发包频率。
  • 电脑上安装好ESP-IDF开发环境。我用的是v5.x版本,ADC部分的API和旧版有差异,后面代码按新版来写。

实际测试时,我把感知板放在离信号源板大概30厘米的位置。中间不要放金属物体,桌面保持清爽。测试过程中,人不要频繁走动,否则人体反射也会影响结果。

3.2 测试方案设计

整个测试分成三步阶梯式验证:

第一步,静默基线。ESP32不开启WiFi,不连接任何网络,只做ADC连续采样。此时读数应该相对平稳,波动范围很小,一般只有几个LSB。

第二步,发射源打开。另一块板子以固定周期发送数据包,比如每隔100毫秒发一个UDP包。感知板的ADC读数如果开始出现和发包周期同步的波动,说明“通路”已经感知到无线电活动。

第三步,改变发射功率和距离。把发射功率从最大调低,或者把感知板移远,观察ADC波动的幅度是否随之下降。如果趋势明显,基本上就证明了这不是随机噪声,而是一条有物理规律的耦合路径。

这个过程不需要频谱仪,不需要网络分析仪,只需要看ADC原始值的变化趋势。

3.3 最小验证代码(基于ESP-IDF v5.x)

我用的核心思路很简单:初始化ADC1的通道0,也就是GPIO36(VP引脚),这个引脚在DevKit板上比较容易访问。把它配置为最高衰减档位,读取12位原始值,然后循环打印。

#include <stdio.h> #include "esp_log.h" #include "esp_wifi.h" #include "esp_event.h" #include "nvs_flash.h" #include "freertos/FreeRTOS.h" #include "freertos/task.h" #include "driver/adc.h" #include "esp_adc/adc_oneshot.h" #define ADC_CHANNEL ADC_CHANNEL_0 // GPIO36 / VP #define ADC_ATTEN ADC_ATTEN_DB_12 #define PRINT_DELAY_MS 50 static const char *TAG = "ADCRF"; static void adc_rf_task(void *arg) { adc_oneshot_unit_handle_t adc_handle = NULL; adc_oneshot_unit_init_cfg_t init_cfg = { .unit_id = ADC_UNIT_1, .clk_src = ADC_RTC_CLK_SRC_DEFAULT, }; ESP_ERROR_CHECK(adc_oneshot_new_unit(&init_cfg, &adc_handle)); adc_oneshot_chan_cfg_t chan_cfg = { .atten = ADC_ATTEN, .bitwidth = ADC_BITWIDTH_12, }; ESP_ERROR_CHECK(adc_oneshot_config_channel(adc_handle, ADC_CHANNEL, &chan_cfg)); int slider = 0; for (;;) { int raw = 0; adc_oneshot_read(adc_handle, ADC_CHANNEL, &raw); slider = (slider * 7 + raw) / 8; static int tick = 0; if (++tick % 5 == 0) { ESP_LOGI(TAG, "raw=%4d avg=%4d", raw, slider); } vTaskDelay(pdMS_TO_TICKS(PRINT_DELAY_MS)); } } void app_main(void) { ESP_ERROR_CHECK(nvs_flash_init()); ESP_ERROR_CHECK(esp_event_loop_create_default()); wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); ESP_ERROR_CHECK(esp_wifi_init(&cfg)); ESP_ERROR_CHECK(esp_wifi_set_storage(WIFI_STORAGE_RAM)); ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA)); ESP_ERROR_CHECK(esp_wifi_start()); xTaskCreate(adc_rf_task, "adc_rf", 4096, NULL, 5, NULL); }

这段代码里,WIFI_STORAGE_RAM会阻止ESP32自动重连之前保存的网络配置,让行为更可控。ADC部分用了adc_oneshot接口,采集一个值就返回,循环间隔50毫秒。滑动平均用了一个简单的递推式,肉眼观察曲线时比直接打印原始值舒服很多。

需要说明的是,这段代码没有连接路由器的动作,它只负责把WiFi前端打开。前端电路在启用状态下会进行信道扫描、信标监听等操作,这些行为本身就足以产生射频活动。如果你想更明显地观察“周期性脉冲”,可以手动连接一个路由器或者让另一块板向它发包。

3.4 实测现象记录

我把这个程序烧到板子上之后,按以下顺序观察:

板子刚上电、WiFi未启动时,控制台打印的raw值稳定在1600到1610之间,偶尔跳到1615,整体非常干净。

执行WiFi启动后,不连接任何路由器,raw值开始出现明显抖动,最低和最高之间差了大概30到50个LSB。这是因为ESP32即使不连接AP,也会周期性地扫描信道,每次扫描都是射频前端的一次活动。

再换到连接路由器的场景,连接成功后raw值波动进一步增大,峰峰差超过80个LSB,并且波动节奏和UDP发包间隔高度一致。我把发包间隔从100毫秒改成500毫秒之后,能明显看到波动“变慢”了。

最让我惊讶的是,用一根杜邦线插到GPIO36上,线的另一端悬空,读数波动范围直接翻倍。这印证了“引脚走线就是天线”的判断。

4. 这条通路的三个实际玩法

4.1 无线活动节奏检测

做智能家居网关的朋友应该会遇到一个场景:想判断路由器是否正常工作、有没有在持续广播beacon。正常情况下你可以去抓包,但成本高、依赖工具多。而用这条隐藏通路,可以做一个非常轻量的“听诊器”。

思路是采集ADC原始值序列,按时间窗口计算方差,比如每500毫秒一个窗口。然后做一个阈值判断:方差大于某个值,说明环境中有周期性的2.4GHz活动。配合FFT,还可以进一步识别出“10Hz左右的周期性脉冲”,因为802.11协议里beacon间隔一般是100ms。

这里给一个简单的阈值参考:静默环境下,原始值方差一般在5以下;有WiFi beacon活动时,方差能翻到20以上;有设备连续发包时,方差可能超过100。我实测的环境是普通办公室,周围可能有WiFi、蓝牙和各种无线设备,但这些设备的活跃度差异还是能通过方差区分出来。

4.2 低成本产测辅助

模组和整机生产线上常需要确认“模组的射频链路是否真的在发射”。常见的办法是使用频谱仪或者综测仪,但一台设备动辄几十万。对某些只需要“通过/不通过”判断的工位来说,可以利用这条通路做一个几十块钱的辅助探针。

做法很简单:在测试治具上放一个与产品规格相近的金属触点,通过短走线连接到另一个MCU(也可以用同一片MCU)的ADC引脚。产品开机进入强制发射模式,比如连续发CW波形或固定载荷数据包,MCU的ADC如果检测到明显的能量抬升,就判定发射链路基本正常。

需要注意,这不是校准方案,不能替代功率校准。它只能用来做大致的链路冒烟测试。我做过一个类似的工装,用ESP32的ADC引脚探测另一块同型号板卡的发射活动,结果一致性还不错,至少能快速筛选出“完全无输出”的坏板。

4.3 电磁波可视化教学

在高校实验室或者公司内部培训场合,做“电磁波如何产生、辐射、被接收”的演示时,传统的做法是买一大堆射频设备。但用这条通路,可以让学员用一块普通的开发板就直观地看到无线电现象。

实验内容可以这样设计:一块发射板,通过调整不同发射功率、不同天线长度、不同距离,另一块感知板的ADC读数会呈现明显规律。学员记录表格数据后,可以画出距离衰减曲线,虽然和理论值有偏差,但趋势是清晰的。

这种实验对设备的要求极低,适合做入门级的电磁兼容与天线原理教学。

5. 常见坑与排查技巧实录

5.1 读数漂移得离谱

表现:raw值一会600一会2000,完全看不出规律。

排查思路:先看电源。ESP32开启WiFi时瞬间电流可能到几百毫安,如果你的供电是从USB直接拉的,压降会很明显。先用示波器量3V3脚,看WiFi发包时有没有同步的跌落。如果有,换粗的USB线或者外加电容。

再看引脚配置。确保没有复用冲突,比如同时把GPIO36当ADC和别的外设用。GPIO矩阵配置反复修改后,最好复位一次再测。

5.2 完全观察不到变化

表现:WiFi开了,读数纹丝不动。

大概率是板上把采样引脚和地之间放了很大的电容。低通滤波电容会把射频能量滤掉,ADC感知不到。处理办法是换一个引脚,或者尽可能选择片上走线短、外部没有额外负载的引脚来测。

如果引脚悬空还是没反应,检查一下板子是不是有金属屏蔽罩。屏蔽罩对射频耦合的影响非常明显,建议用裸板测试,或者在屏蔽罩上开个小孔。

5.3 分不清是“真感应”还是“电源串扰”

表现:读数跟着WiFi变,但你无法确定这是射频耦合还是电源跌落导致的地电位变化。

一个简单的区分方法:改变发射功率等级,保持同一个网络环境,如果读数波动幅度跟随功率等级变化,说明射频耦合占主导;如果波动幅度完全不变,那更可能是数字逻辑开关引起的电源扰动。

另一个方法是使用两个不同的感知引脚,一个远离天线、一个靠近天线。如果两个引脚的波动比例不同,也说明耦合路径来自空间辐射而不是统一的地噪声。

5.4 采样频率和WiFi发包频率撞在一起

表现:波形看起来像某种周期信号,但频率完全对应不上。

如果ADC采样周期和WiFi发包周期接近整数倍关系,就会出现混叠。比如发包间隔100毫秒,采样间隔99毫秒,你会看到一条缓慢“漂移”的包络。这不是通路坏了,而是采样定理在地搞事。解决方式是随机化采样间隔,或者在相邻窗口之间加入微小抖动。

5.5 常见问题速查表

现象可能原因处理建议
读数完全无变化引脚被滤波电容短路换悬空引脚或去掉电容
读数剧烈漂移供电不足或USB线内阻大改善供电并观察3V3波形
波动伴随电源跌落数字逻辑开关噪声先区分耦合来源再决定方案
波形呈现缓慢漂移采样频率与发包频率混叠随机化采样间隔
不同板子差异巨大天线形式/PCB走线差异固定同一型号作为感知板
靠近天线反而变小近场方向性导致换一个感知位置或角度

6. 关于使用这条通路的几点最终建议

我在项目里实际用这条通路已经有大半年,踩过不少坑,也有几个心得。

第一,不要把目的定成“我想拿它当无线电接收机”。它是能量探测窗口,不是数据通道。你能得到的是“有/没有活动”以及“活动的强弱节奏”,而不是“别人发了什么内容”。把期望放在正确的位置,这个方案的有效性就很高。

第二,尽量使用固定设计来复现。如果你想要在不同批次产线上用这个方案,建议把感知引脚的PCB形状、位置、参考地全部固定下来。哪怕改一个过孔位置,耦合量都可能变化。做对比测试时,一定要同一个测试环境和同一个硬件版本。

第三,当发现读数异常时,先排查电源再怀疑射频耦合。我在早期调试中有一半以上的时间浪费在把电源噪声和射频感应混为一谈上。现在我的流程是:量3V3波形、量GND噪声、关闭WiFi观察基线、开启WiFi观察增量。严格按照这个顺序,能省很多时间。

第四,如果你想在此基础上扩展,可以考虑结合机器学习做一个简单的模式分类。比如采集一段时间序列作为特征,判断当前环境是空闲、有人使用手机、还是路由器重启。这个方向很实用,代码量不大,但能解决的问题非常真实。

最后再分享一个小技巧:感知引脚选择上,优先选RTC域的那些GPIO,比如GPIO36、GPIO39、GPIO34、GPIO35。它们在芯片内部走线相对独立,更容易感应到射频场,而且ADC1的采样配置也最灵活。只要你不让它同时承担其他外设功能,这个“隐藏通路”就是稳定的、可重复的。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询