模拟传感器读取
模拟传感器读取
本节介绍使用模拟液位传感器(如电位器式浮球液位计、压力传感器、电容式传感器等)替代超声波传感器的方案。学习完成后,您将能够:
- 理解 ESP32 的 ADC 功能和模拟传感器接口
- 实现模拟传感器读取和校准
- 根据不同液位传感器类型选择合适的方案
- 对比数字传感器和模拟传感器的优劣
ESP32 ADC Overview
Section titled “ESP32 ADC Overview”ESP32 内置两个 12 位 SAR ADC 模块:
| 参数 | 规格 |
|---|---|
| 分辨率 | 12 位 (0-4095) |
| 输入电压 | 0 - 3.3V |
| 采样率 | 最高 200ksps (单通道) |
| 可用通道 | ADC1: 8 通道 (GPIO 32-39) |
| ADC2: 10 通道 (共用 Wi-Fi) | |
| 衰减配置 | 0dB, 2.5dB, 6dB, 11dB |
⚠️ 注意: ADC2 在与 Wi-Fi 共用时会降低精度。推荐始终使用 ADC1 (GPIO 32-39) 读取模拟传感器。
Analog Level Sensor Options
Section titled “Analog Level Sensor Options”| 传感器类型 | 原理 | 输出信号 | 精度 | 适用场景 | 成本 |
|---|---|---|---|---|---|
| 电位器浮球 | 浮球带动电位器 | 0-3.3V | ±5% | 清水/非腐蚀液体 | $ |
| 压力传感器 | 液体压力 → 电压 | 0.5-4.5V | ±1% | 密闭容器底部 | $$ |
| 电容式 | 液位 → 电容 → 电压 | I2C/模拟 | ±2% | 腐蚀性液体/泡沫 | $$$ |
| 电阻式探针 | 液体导电率 | 0-3.3V | ±10% | 导电液体 | $ |
电位器浮球传感器接线
Section titled “电位器浮球传感器接线” 3.3V │ ┌┴┐ │R│ 10kΩ 电位器 (可选) └┬┘ │ ┌─────────┼─────────┐ │ │ │ ┌─┴─┐ ┌──┴──┐ │ │VCC│ │ OUT │ GND │ │ │ │ │ │浮球│ └──┬──┘ │ │ │ │ │ └───┘ │ │ │ │ GPIO 34 GND (ADC1_CH6)ADC Reading Implementation
Section titled “ADC Reading Implementation”// 模拟传感器读取#define LEVEL_SENSOR_PIN 34 // ADC1_CH6 (GPIO 34)
void setupADC() { // 配置 ADC 衰减 (扩大输入范围) // ADC_ATTEN_DB_0: 0 - 1.1V (100mV 分辨率) // ADC_ATTEN_DB_2_5: 0 - 1.5V // ADC_ATTEN_DB_6: 0 - 2.2V // ADC_ATTEN_DB_11: 0 - 3.3V (推荐) analogReadResolution(12); // 12 位精度 analogSetAttenuation(ADC_ATTEN_DB_11); // 0-3.3V}
// 读取模拟液位传感器// 返回值: 0-4095 (原始 ADC 值)int readAnalogLevel() { int rawValue = analogRead(LEVEL_SENSOR_PIN); return rawValue;}
// 转换为电压值 (mV)float rawToVoltage(int rawValue) { return rawValue * (3300.0 / 4095.0);}
// 转换为液位百分比 (需校准)// rawEmpty: 空容器时的 ADC 值// rawFull: 满容器时的 ADC 值int rawToLevelPercent(int rawValue, int rawEmpty, int rawFull) { if (rawValue <= rawEmpty) return 0; if (rawValue >= rawFull) return 100;
int level = ((rawValue - rawEmpty) * 100) / (rawFull - rawEmpty); return constrain(level, 0, 100);}Calibration Procedure
Section titled “Calibration Procedure”// 校准流程 (通常只需首次执行)struct CalibrationData { int rawEmpty; // 空容器 ADC 值 int rawFull; // 满容器 ADC 值 int sampleCount; // 采样次数};
CalibrationData cal = {0, 0, 50};
// 校准函数CalibrationData calibrateSensor(int pin) { CalibrationData result;
Serial.println("=== Calibration: Empty container ==="); Serial.println("Please ensure container is EMPTY, then press any key"); while (!Serial.available()); // 等待用户确认 Serial.read();
// 采样空容器值 long emptyTotal = 0; for (int i = 0; i < result.sampleCount; i++) { emptyTotal += analogRead(pin); delay(20); } result.rawEmpty = emptyTotal / result.sampleCount; Serial.printf("Empty value: %d\n", result.rawEmpty);
// === 满容器校准 === Serial.println("=== Calibration: Full container ==="); Serial.println("Please fill container to FULL, then press any key"); while (!Serial.available()); Serial.read();
long fullTotal = 0; for (int i = 0; i < result.sampleCount; i++) { fullTotal += analogRead(pin); delay(20); } result.rawFull = fullTotal / result.sampleCount; Serial.printf("Full value: %d\n", result.rawFull);
Serial.println("Calibration complete!"); return result;}Noise Filtering
Section titled “Noise Filtering”// 移动平均滤波const int FILTER_SIZE = 10;int filterBuffer[FILTER_SIZE];int filterIndex = 0;
void initFilter() { for (int i = 0; i < FILTER_SIZE; i++) { filterBuffer[i] = 0; }}
int movingAverageFilter(int newValue) { filterBuffer[filterIndex] = newValue; filterIndex = (filterIndex + 1) % FILTER_SIZE;
long sum = 0; for (int i = 0; i < FILTER_SIZE; i++) { sum += filterBuffer[i]; } return sum / FILTER_SIZE;}
// 使用示例int rawValue = analogRead(LEVEL_SENSOR_PIN);int filteredValue = movingAverageFilter(rawValue);int levelPercent = rawToLevelPercent(filteredValue, cal.rawEmpty, cal.rawFull);ESP32 Integration
Section titled “ESP32 Integration”void handleGetData() { // 读取模拟传感器 int rawValue = analogRead(LEVEL_SENSOR_PIN); int filteredValue = movingAverageFilter(rawValue); float voltage = rawToVoltage(filteredValue);
// 转换为液位百分比 int levelPercent = rawToLevelPercent(filteredValue, cal.rawEmpty, cal.rawFull);
Serial.printf("ADC: %d, Voltage: %.2fV, Level: %d%%\n", filteredValue, voltage / 1000, levelPercent);
// 发送到 Node-RED String payload = "{\"sensor_type\":\"analog\"," + String("\"adc_value\":") + String(filteredValue) + ",\"voltage\":" + String(voltage / 1000, 2) + ",\"level\":" + String(levelPercent) + ",\"state\":\"get_data\"}";
client.publish("esp32/dosing/info", payload.c_str()); currentState = STATE_WAIT;}Analog vs Digital Comparison
Section titled “Analog vs Digital Comparison”| 对比维度 | 模拟传感器 (ADC) | 超声波传感器 (GPIO) |
|---|---|---|
| 成本 | $1-5 | $2-3 |
| 精度 | ±5-10% (受 ADC 噪声影响) | ±3mm |
| 抗干扰 | 易受电源噪声影响 | 受泡沫/粉尘影响 |
| 安装要求 | 需接触液体 | 非接触式 |
| 维护 | 定期校准 | 清洁传感器表面 |
| 防腐蚀 | 需选用耐腐蚀材料 | 不需要接触液体 |
| 多路扩展 | 占用 ADC 通道 (有限) | 占用普通 GPIO (较多) |
Common Customer Questions
Section titled “Common Customer Questions”Q1: ESP32 ADC 精度不足怎么办?
Section titled “Q1: ESP32 ADC 精度不足怎么办?”ESP32 ADC 线性度约 ±2%,可通过以下方式改善:
- 使用外部 ADC 模块(如 ADS1115, 16 位 I2C)
- 软件校准(多点校准 + 插值)
- 电源增加 LC 滤波
// 外部 ADS1115 ADC (I2C)#include <Adafruit_ADS1X15.h>Adafruit_ADS1115 ads;
void setup() { ads.setGain(GAIN_ONE); // ±4.096V ads.begin(0x48);}
int readADS1115() { int16_t value = ads.readADC_SingleEnded(0); // 15 位分辨率 (0-32767) float voltage = ads.computeVolts(value); return map(value, 0, 32767, 0, 4095); // 映射到 12 位兼容}Q2: 模拟传感器需要定期校准吗?
Section titled “Q2: 模拟传感器需要定期校准吗?”建议每 3-6 个月或更换液体后重新校准。可以在 Node-RED 中实现远程校准:
// Node-RED Function: 远程校准命令msg.topic = "esp32/dosing/command";msg.payload = "calibrate_empty"; // 或 "calibrate_full"return msg;✅ 推荐做法:
- 始终使用 ADC1 (GPIO 32-39) 避免 Wi-Fi 干扰
- 开启 11dB 衰减以覆盖 0-3.3V 输入范围
- 使用移动平均滤波(10 次采样)
- 定期校准补偿传感器漂移
- 电源增加 100nF + 10μF 去耦电容
❌ 避免做法:
- 长距离 (>30cm) 传输模拟信号无屏蔽
- ADC2 与 Wi-Fi 同时使用
- 忽略 ADC 输入管脚的 RC 滤波
- 单次采样即作为最终结果
Summary
Section titled “Summary”- ESP32 ADC: 12 位分辨率, 0-3.3V 输入, 推荐 ADC1 (GPIO 32-39)
- 传感器类型: 浮球/压力/电容式液位传感器
- 校准: 空/满容器两点校准 + 软件插值
- 滤波: 移动平均减少 ADC 噪声
- 外部 ADC: ADS1115 提供 16 位精度