液位传感器集成
液位传感器集成
本节介绍如何使用 HC-SR04 超声波传感器测量容器液位,并将数据整合到自动投料系统中。学习完成后,您将能够:
- 理解超声波测距原理和 HC-SR04 的使用方法
- 实现超声波传感器的驱动代码
- 将液位数据通过 MQTT 发送到 Node-RED
- 扩展多路液位监测
HC-SR04 工作原理
Section titled “HC-SR04 工作原理” ┌────────────────────────────────────┐ │ HC-SR04 超声波传感器 │ ├────────────────────────────────────┤ │ │ │ ┌──────────┐ │ │ │ 发射器 │ ←── 发出 40KHz 声波│ │ └──────────┘ │ │ ╲ ╱ │ │ ╳ 声波遇到水面反射 │ │ ╱ ╲ │ │ ┌──────────┐ │ │ │ 接收器 │ ←── 接收回波 │ │ └──────────┘ │ │ │ │ VCC ─── 5V │ │ GND ─── GND │ │ Trig ─── GPIO 2 (ESP32 发脉冲) │ │ Echo ─── GPIO 3 (ESP32 接收) │ └────────────────────────────────────┘
工作原理: 1. ESP32 向 Trig 引脚发送 10μs 高电平脉冲 2. HC-SR04 发射 8 个 40KHz 超声波脉冲 3. 声波遇到水面反射 4. HC-SR04 接收回波,Echo 引脚输出高电平 5. Echo 高电平持续时间 = 声波往返时间 6. 距离 = 时间 × 声速 ÷ 2
声速: 340 m/s (20°C) 最大量程: 4m 最小盲区: 2cm 精度: ±3mmUltrasonic Driver Code
Section titled “Ultrasonic Driver Code”Ultrasonic.h
Section titled “Ultrasonic.h”// Ultrasonic.h — 超声波传感器驱动#ifndef ULTRASONIC_H#define ULTRASONIC_H
// 测量距离 (单位: cm)// triggerPin: Trig 引脚// echoPin: Echo 引脚// 返回值: 距离值 (cm),超时返回 -1long measureDistance(int triggerPin, int echoPin) { // 确保 Trig 为低电平 digitalWrite(triggerPin, LOW); delayMicroseconds(2);
// 发送 10μs 高电平脉冲 digitalWrite(triggerPin, HIGH); delayMicroseconds(10); digitalWrite(triggerPin, LOW);
// 读取 Echo 高电平持续时间 (微秒) long duration = pulseIn(echoPin, HIGH, 30000); // 30ms 超时 (~5m)
if (duration == 0) { Serial.println("Ultrasonic: No echo received (timeout)"); return -1; // 超时 }
// 计算距离 (cm) // 声速 340m/s = 0.034 cm/μs // 往返时间 → 距离 = duration × 0.034 / 2 // 简化为: distance = duration / 58 long distance = duration / 58;
return distance;}
// 多次测量取平均 (减少噪声)long measureDistanceAverage(int triggerPin, int echoPin, int samples = 5) { long total = 0; int validSamples = 0;
for (int i = 0; i < samples; i++) { long d = measureDistance(triggerPin, echoPin); if (d > 0) { // 只累加有效值 total += d; validSamples++; } delay(10); // 测量间隔 }
if (validSamples == 0) return -1; return total / validSamples;}
#endif液位 = 容器高度 - 测量距离
Section titled “液位 = 容器高度 - 测量距离”// 液位计算// 容器总高度 (从传感器到容器底部)const int CONTAINER_HEIGHT = 24; // cm
// 将测量距离转换为液位百分比int distanceToLevelPercent(long distance, int containerHeight) { if (distance < 0) return -1; // 测量失败 if (distance >= containerHeight) return 0; // 空 if (distance <= 2) return 100; // 满 (传感器盲区)
int level = ((containerHeight - distance) * 100) / containerHeight; return constrain(level, 0, 100);}ESP32 Integration Code
Section titled “ESP32 Integration Code”在状态机中集成液位读取
Section titled “在状态机中集成液位读取”// 引脚定义#define TRIG_PIN 2#define ECHO_PIN 3#define RELAY_PUMP_PIN 20#define CONTAINER_HEIGHT 24 // cm
const int TRIG_PIN = 2;const int ECHO_PIN = 3;long distance1 = 0; // 1 号容器液位long distance2 = 0; // 2 号容器液位 (可选)
void handleGetData() { // 测量 1 号容器液位 (取平均) distance1 = measureDistanceAverage(TRIG_PIN, ECHO_PIN);
if (distance1 < 0) { // 测量失败 client.publish("esp32/dosing/status", "{\"error\":\"sensor_failure\"}"); currentState = STATE_TIME_FOR_SLEEP; return; }
// 计算液位百分比 int levelPercent = distanceToLevelPercent(distance1, CONTAINER_HEIGHT);
Serial.printf("Distance: %ld cm, Level: %d%%\n", distance1, levelPercent);
// 构建 JSON 发送到 Node-RED String payload = "{\"distance\":" + String(distance1) + ",\"level\":" + String(levelPercent) + ",\"container_height\":" + String(CONTAINER_HEIGHT) + ",\"state\":\"get_data\"}";
client.publish("esp32/dosing/info", payload.c_str());
// 等待 Node-RED 返回指令 currentState = STATE_WAIT; lastCommandTime = millis();}Multiple Level Sensors
Section titled “Multiple Level Sensors”// 扩展多路液位监测
// 引脚定义 (多路)#define TRIG1_PIN 2 // 容器 1 Trig#define ECHO1_PIN 3 // 容器 1 Echo#define TRIG2_PIN 4 // 容器 2 Trig#define ECHO2_PIN 5 // 容器 2 Echo
// 容器参数const int CONTAINER1_HEIGHT = 24;const int CONTAINER2_HEIGHT = 30;
void handleGetData() { // 同时读取多个传感器 distance1 = measureDistanceAverage(TRIG1_PIN, ECHO1_PIN, 3); distance2 = measureDistanceAverage(TRIG2_PIN, ECHO2_PIN, 3);
// 构建多容器 JSON String payload = "{\"distance1\":" + String(distance1) + ",\"distance2\":" + String(distance2) + ",\"level1\":" + String(distanceToLevelPercent(distance1, CONTAINER1_HEIGHT)) + ",\"level2\":" + String(distanceToLevelPercent(distance2, CONTAINER2_HEIGHT)) + ",\"state\":\"get_data\"}";
client.publish("esp32/dosing/info", payload.c_str()); currentState = STATE_WAIT;}Node-RED Data Parsing
Section titled “Node-RED Data Parsing”// Node-RED Function: 解析 ESP32 液位数据// 从 esp32/dosing/info Topic 接收
var data = msg.payload;
// data 格式: {"distance":16,"level":33,"container_height":24}
var distance = data.distance;var levelPercent = data.level;
// 计算剩余量 (升)// 假设容器截面为 20cm × 20cmvar containerVolume = 20 * 20 * data.container_height / 1000; // 升var remainingVolume = containerVolume * levelPercent / 100;
msg.payload = { distance: distance, level: levelPercent, remaining: remainingVolume.toFixed(1), unit: "liters", timestamp: Date.now()};
return msg;# 1. 测试传感器mosquitto_pub -t "esp32/dosing/command" -m "get_data"
# 2. 检查 MQTT 数据mosquitto_sub -t "esp32/dosing/info" -v# 输出: esp32/dosing/info {"distance":16,"level":33,...}
# 3. 用手遮挡传感器测试# 距离变小 → level 增加# 移开手 → level 减小
# 4. 传感器故障测试# 拔掉 Echo 线 → 应看到 {"error":"sensor_failure"}Common Customer Questions
Section titled “Common Customer Questions”Q1: 超声波传感器在泡沫或粉尘环境中可靠吗?
Section titled “Q1: 超声波传感器在泡沫或粉尘环境中可靠吗?”不推荐。泡沫会吸收超声波,粉尘会散射声波。推荐使用:
- 泡沫环境: 电容式液位传感器
- 粉尘环境: 雷达液位计
- 防腐蚀要求: 非接触式超声波(但需定期清洁传感器表面)
Q2: 容器形状不规则怎么计算液位?
Section titled “Q2: 容器形状不规则怎么计算液位?”对于不规则容器,可以在 Node-RED 中使用查表法:
// Node-RED Function: 不规则容器液位换算// 预定义的容器形状 → 液位映射表var lookupTable = [ {distance: 0, volume: 100}, {distance: 5, volume: 85}, {distance: 10, volume: 65}, {distance: 15, volume: 40}, {distance: 20, volume: 15}, {distance: 24, volume: 0}];
// 通过插值计算实际剩余量function interpolate(distance, table) { for (var i = 1; i < table.length; i++) { if (distance <= table[i].distance) { var ratio = (distance - table[i-1].distance) / (table[i].distance - table[i-1].distance); return table[i-1].volume - ratio * (table[i-1].volume - table[i].volume); } } return 0;}Q3: 如何减少液位波动引起的误判?
Section titled “Q3: 如何减少液位波动引起的误判?”- 软件: 多次采样取平均 (
measureDistanceAverage) - 软件: 移动平均滤波 (Node-RED 端)
- 硬件: 传感器安装固定支架,减少晃动
- 硬件: 在容器内加装导波管
✅ 推荐做法:
- 传感器距离液面至少 2cm(避免盲区)
- 多次采样取平均值(建议 5 次)
- 传感器表面保持清洁
- 液位数据附加上次投料时间一并存储
- 实现传感器故障检测和报警
❌ 避免做法:
- 传感器表面有冷凝水或结露时忽略校准
- 单次测量就作为最终液位值
- 容器内壁有附着物影响回波
- 忽略温度对声速的影响
Summary
Section titled “Summary”- HC-SR04: Trig + 10μs 脉冲 → Echo 测脉宽 → 距离 = 脉宽/58
- 液位计算: 容器高度 - 测量距离 = 液位高度
- 多次采样: 5 次取平均减少噪声
- 故障检测: 超时返回 -1,触发告警
- 多路扩展: 最多支持 8+ 超声波传感器