ESP32开启WiFi时analogRead(A10)失效的固件修复方案咨询
解决ESP32 WiFi与ADC2引脚冲突的固件方案
你的问题是ESP32的ADC2引脚(GPIO4/A10)与WiFi硬件资源冲突导致读取失败,由于板子已量产无法更换引脚,完全可以通过固件修改来规避这个问题——核心思路是临时暂停WiFi射频模块,完成ADC读取后再恢复WiFi功能,以下是具体实现方案和代码修改示例:
冲突原因说明
ESP32的ADC2模块与WiFi/蓝牙射频共享同一组硬件总线,当WiFi处于活跃状态时,射频会独占这部分资源,导致ADC2无法正常采样,返回固定值0。这是硬件层面的资源限制,只能通过固件调度来规避。
可行的固件修改方案
1. 调整读取逻辑:中断标记+主任务处理
由于WiFi操作不能在中断上下文执行(会导致系统崩溃或死锁),需要把ADC读取的逻辑从定时器中断移到主任务中:
- 定时器中断仅负责标记需要读取电池电量
- 主loop中检测到标记后,执行WiFi暂停→ADC读取→WiFi恢复的流程
2. WiFi暂停与恢复的实现
使用WiFi.mode()函数临时关闭WiFi,读取完成后恢复之前的WiFi模式。需要注意保存当前WiFi状态,避免恢复后无法重连。
3. 代码修改示例
基于你提供的代码,修改后的完整代码如下(关键修改处已标注):
#include <WiFi.h> #define LED_BUILTIN 2 #define LEVEL1 25 // Level1 : GPIO 25 of ESP32 #define LEVEL2 26 // Level2 : GPIO 26 of ESP32 #define LEVEL3 27 // Level3 : GPIO 27 of ESP32 #define LEVEL4 14 // Level4 : GPIO 14 of ESP32 #define VBUS_DETECT 12 //GPIO 13 #define BATTERY_READ A10 //GPIO 4(A10) : Analog READ PIN for the battery voltage #define BATTERY_0 1950 // ADC value for Battery 0% - 1950 #define BATTERY_25 2303 // ADC value for Battery 25% - 2200 #define BATTERY_50 2400 // ADC value for Battery 50% - 2275 #define BATTERY_75 2410 // ADC value for Battery 75% - 2310 #define BATTERY_100 2850 // ADC value for Battery 100% 2850 int LEVELS[4] = {25, 26, 27, 14}; int batSensorValue = 0; // value read from the pot int full_charged = 0; hw_timer_t *Timer0_Cfg = NULL; int prevBatIndex = 0; int batTimerCount = 0; int gSensorValue = 0; int adjustedSensorValue = 0; int functionalSensorValue = 0; uint8_t sensorBuffer = 4; bool chargeFlashFlag = 0; bool needReadBattery = false; // 新增:标记需要读取电池电量 wifi_mode_t prevWiFiMode; // 新增:保存暂停前的WiFi模式 int getLevelIndex(int value) { switch (value) { case 0 ... BATTERY_25 - 1: return 1; case BATTERY_25 ... BATTERY_50 - 1: return 2; case BATTERY_50 ... BATTERY_75 - 1: return 3; case BATTERY_75 ... BATTERY_100 - 1: return 4; default: return 5; } } void IRAM_ATTR Timer0_ISR() { batTimerCount++; // 仅标记需要读取,不再在中断中执行ADC读取 if(batTimerCount >= 10) { needReadBattery = true; batTimerCount = 0; } } // 新增:电池电量读取与处理函数 void readAndProcessBattery() { // 保存当前WiFi模式 prevWiFiMode = WiFi.getMode(); // 暂停WiFi WiFi.mode(WIFI_OFF); // 多次读取取平均值(原逻辑的10次采样) gSensorValue = 0; for(int i=0; i<10; i++) { batSensorValue = analogRead(BATTERY_READ); gSensorValue += batSensorValue; delay(10); // 短延迟保证采样稳定 } // 恢复WiFi模式 WiFi.mode(prevWiFiMode); // 如果之前是STA模式,尝试重连(防止暂停导致断开) if(prevWiFiMode == WIFI_STA && !WiFi.isConnected()) { WiFi.reconnect(); } // 原有的数据处理逻辑 int vbusStatus = digitalRead(VBUS_DETECT); adjustedSensorValue = (vbusStatus == 1 ? ((gSensorValue/10)-58) : (gSensorValue/10)); if ( (BATTERY_0 - sensorBuffer) <= adjustedSensorValue && adjustedSensorValue <= (BATTERY_0 + sensorBuffer) || (BATTERY_25 - sensorBuffer) <= adjustedSensorValue && adjustedSensorValue <= (BATTERY_25 + sensorBuffer) || (BATTERY_50 - sensorBuffer) <= adjustedSensorValue && adjustedSensorValue <= (BATTERY_50 + sensorBuffer) || (BATTERY_75 - sensorBuffer) <= adjustedSensorValue && adjustedSensorValue <= (BATTERY_75 + sensorBuffer) || (BATTERY_100 - sensorBuffer) <= adjustedSensorValue && adjustedSensorValue <= (BATTERY_100 + sensorBuffer) ) { functionalSensorValue = adjustedSensorValue + sensorBuffer + 1; } else functionalSensorValue = adjustedSensorValue; int levelIndex = getLevelIndex(functionalSensorValue); full_charged = (levelIndex == 5) ? 1 : 0; Serial.print("levelIndex = "); Serial.println(levelIndex); Serial.print("sensor = "); Serial.println(adjustedSensorValue); // LED控制逻辑 if(prevBatIndex > vbusStatus) { prevBatIndex = 0; return; } for(int i = 0; i < 4; i++) { digitalWrite(LEVELS[i], i < levelIndex ? HIGH : LOW); } if(full_charged == 0 && vbusStatus == 1 && chargeFlashFlag == 0) { digitalWrite(LEVELS[levelIndex-1], !digitalRead(LEVELS[levelIndex-1])); } if (vbusStatus == 1) chargeFlashFlag = (chargeFlashFlag+1)%2; prevBatIndex = vbusStatus; gSensorValue = 0; } void setup() { Serial.begin(115200); WiFi.begin(); pinMode(LEVEL1, OUTPUT); pinMode(LEVEL2, OUTPUT); pinMode(LEVEL3, OUTPUT); pinMode(LEVEL4, OUTPUT); pinMode(VBUS_DETECT, INPUT); Timer0_Cfg = timerBegin(0, 80, true); timerAttachInterrupt(Timer0_Cfg, &Timer0_ISR, true); timerAlarmWrite(Timer0_Cfg, 100000, true); // 100ms触发一次 timerAlarmEnable(Timer0_Cfg); } void loop() { // 检查是否需要读取电池电量 if(needReadBattery) { needReadBattery = false; readAndProcessBattery(); } delay(100); }
关键注意事项
- WiFi暂停时间:整个读取过程仅需几十毫秒,对WiFi连接的影响极小,用户几乎感知不到
- 重连逻辑:如果WiFi在暂停时处于连接状态,恢复后需要调用
WiFi.reconnect()确保重新连接 - 采样时机:如果你的设备有大量WiFi数据传输,可以选择在传输间隙读取,进一步降低影响
- 备选方案:也可以尝试使用ESP-IDF原生的
adc2_get_raw()函数,该函数会尝试获取ADC2使用权,失败时返回错误,但这种方式在WiFi活跃时成功率较低,不如暂停WiFi可靠
内容的提问来源于stack exchange,提问作者Dylan
相关产品推荐
相关产品推荐

