跳转到内容

模拟传感器读取

模拟传感器读取

本节介绍使用模拟液位传感器(如电位器式浮球液位计、压力传感器、电容式传感器等)替代超声波传感器的方案。学习完成后,您将能够:

  • 理解 ESP32 的 ADC 功能和模拟传感器接口
  • 实现模拟传感器读取和校准
  • 根据不同液位传感器类型选择合适的方案
  • 对比数字传感器和模拟传感器的优劣

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) 读取模拟传感器。

传感器类型原理输出信号精度适用场景成本
电位器浮球浮球带动电位器0-3.3V±5%清水/非腐蚀液体$
压力传感器液体压力 → 电压0.5-4.5V±1%密闭容器底部$$
电容式液位 → 电容 → 电压I2C/模拟±2%腐蚀性液体/泡沫$$$
电阻式探针液体导电率0-3.3V±10%导电液体$
3.3V
┌┴┐
│R│ 10kΩ 电位器 (可选)
└┬┘
┌─────────┼─────────┐
│ │ │
┌─┴─┐ ┌──┴──┐ │
│VCC│ │ OUT │ GND
│ │ │ │ │
│浮球│ └──┬──┘ │
│ │ │ │
└───┘ │ │
│ │
GPIO 34 GND
(ADC1_CH6)
// 模拟传感器读取
#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);
}
// 校准流程 (通常只需首次执行)
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;
}
// 移动平均滤波
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);
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;
}
对比维度模拟传感器 (ADC)超声波传感器 (GPIO)
成本$1-5$2-3
精度±5-10% (受 ADC 噪声影响)±3mm
抗干扰易受电源噪声影响受泡沫/粉尘影响
安装要求需接触液体非接触式
维护定期校准清洁传感器表面
防腐蚀需选用耐腐蚀材料不需要接触液体
多路扩展占用 ADC 通道 (有限)占用普通 GPIO (较多)

ESP32 ADC 线性度约 ±2%,可通过以下方式改善:

  1. 使用外部 ADC 模块(如 ADS1115, 16 位 I2C)
  2. 软件校准(多点校准 + 插值)
  3. 电源增加 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 滤波
  • 单次采样即作为最终结果
  1. ESP32 ADC: 12 位分辨率, 0-3.3V 输入, 推荐 ADC1 (GPIO 32-39)
  2. 传感器类型: 浮球/压力/电容式液位传感器
  3. 校准: 空/满容器两点校准 + 软件插值
  4. 滤波: 移动平均减少 ADC 噪声
  5. 外部 ADC: ADS1115 提供 16 位精度