大数跨境

使用 ESP32 和 Firebase 的智能水泵控制器(物联网项目)

使用 ESP32 和 Firebase 的智能水泵控制器(物联网项目) 索引目录
2025-10-22
1
导读:关注「索引目录」公众号,获取更多干货。有没有过这样的经历:忘了关水泵,回来后发现露台上竟然有个迷你游泳池?

关注「索引目录」公众号,获取更多干货。

有没有过这样的经历:忘了关水泵,回来后发现露台上竟然有个迷你游泳池?我有过。所以,我用 ESP32、Firebase 和一个简单的 Web 仪表盘构建了这个智能水泵控制器。现在,只需一个浏览器,我就可以打开或关闭水泵、检测漏水,甚至计算用水量。没错,它听起来就很酷。

无论你是想深入研究物联网,还是只是厌倦了忘记关掉水泵,这都是一个有趣且实用的项目,值得一试。它还能让你轻松体验云连接硬件,避免不知所措。

🔍 为什么要做这个项目?

在每家每户,总有人喊着“嘿,关掉马达!”,而另一个人则回答说“哦不,我忘了!”🤦🏼‍♂️

这种日常的混乱启发了我,让我构建了一个更智能的解决方案——一个可以让我通过手机或笔记本电脑远程控制电机的系统。它简单实用,而且实时运行的效果也让我惊喜不已。

所以我想,为什么不创建一个控制器呢:

  • 可在任何网络浏览器上运行
  • 使用 Firebase 进行实时数据同步
  • 不需要昂贵的组件
  • 让我免于每天在家打水仗
  • 甚至计算用水费用

这是一个了解物联网 + 云 + Web UI 的绝佳入门项目。

此外,由于 ESP32 自带 Wi-Fi,非常适合这类项目。搭配 Firebase 实时数据库,即可实现即时更新,无需自定义后端。双赢!

🛠️ 你需要什么

在开始构建之前,请确保您已收集所有必要的硬件和软件工具

硬件

以下是我使用的方法:

  • ESP32 开发板 – 您的项目的大脑,内置 Wi-Fi。
  • 5V 继电器模块——充当控制泵的电子开关。
  • 两个 YF-S201 水流量传感器 - 测量水箱输入和房屋使用的水流量。
  • 直流潜水泵(3V–5V)——用于测试的基本微型泵。
  • 面包板和跳线——方便接线和原型制作。
  • 5V 电源或电池——独立于 ESP32 为泵供电。
  • 透明 PVC 软水管 – 用于连接传感器和泵。



软件

你不需要复杂的开发设置。只需要以下基本配置:

  • Arduino IDE – 用于编写 ESP32
  • Firebase 控制台 – 您的云端数据库
  • Web 浏览器 – 在本地运行 UI 或在线托管
  • (可选)VS Code – 如果您喜欢使用高级编辑器来调整 HTML/JS

如果这是你的第一个 Firebase 项目,不用担心。它实际上非常适合初学者,我会一步一步指导你。

🛠️ 分步教程

让我们分解一下。你可以一次看一个部分。

✅ 1.硬件连接

让我们连接所有组件并使系统运行起来。

  • 查看下面的电路图以了解连接概况。



  • 将 ESP32 牢固地安装在面包板上。



  • 连接电源:
    • 使用红线将 ESP32 的 3V3 引脚连接到面包板上的正极(电源)轨。
    • 使用绿线将 ESP32 的 GND 引脚连接到负极(接地)轨。




  • 连接水流传感器 1:
    • 红线→电源轨(+3.3V)
    • 黑线→地线(GND)
    • 黄线→ESP32上的GPIO D18(信号引脚)




  • 连接水流传感器 2:
    • 红线→电源轨(+3.3V)
    • 黑线→地线(GND)
    • 黄线→ESP32上的GPIO D19




  • 连接继电器模块:
    • 红线(VCC)→电源轨(+3.3V 或 +5V,取决于您的继电器模块)
    • 绿线(GND)→接地轨
    • 黑线(IN)→ESP32上的GPIO D23




  • 连接泵和电源:
    • 将电池的正极连接到继电器上的 COM(公共)针脚。
    • 将电池的负极端子连接到水泵的负极端子。
    • 最后,将泵的正极连接到继电器的 NC(常闭)引脚。




就这样,你的硬件连接就完成了!
在接通电路电源之前,请仔细检查线路,确保所有连接牢固正确。

✅ 2. 设置 Firebase

  • 前往 Firebase 控制台并点击“开始”。
  • 创建一个新项目 - 将其命名为“AquaFlowproj”。



  • 关闭 Gemini 和 Google Analytics,然后单击创建项目。
  • 准备就绪后,转到左侧边栏的“构建”部分并选择“实时数据库”。
  • 单击创建数据库,选择您最近的地区(我使用了亚洲新加坡),然后选择“以测试模式启动”并单击启用。
  • 在数据库的根路径下,添加以下键:data flow1 flow2 pump



  • 接下来,打开“规则”选项卡,用下面给出的自定义规则替换现有规则,然后单击“发布”。
{
  "rules": {
    ".read": true,
    ".write": true,
    "data": {
      ".read": true,
      ".write": true
    },
    "pump": {
      ".read": true,
      ".write": true
    },
    "flow1": {
      ".read": true,
      ".write": true
    },
    "flow2": {
      ".read": true,
      ".write": true
    }
  }
}



  • 现在单击齿轮图标⚙️→项目设置。



  • 在“常规”选项卡下,滚动到“您的应用程序”部分并选择“Web”。
  • 添加应用程序昵称,单击注册应用程序,然后单击继续到控制台。
  • 现在您将看到所有 Firebase 配置详细信息,例如 apiKey、authDomain、databaseURL 等。将它们复制到记事本中以供日后使用。



  • 接下来,转到构建→身份验证。
  • 单击“开始”,然后选择“电子邮件/密码”作为登录方式,然后单击“启用”→“保存”。
  • 在“用户”选项卡下,点击“添加用户”。例如:
    • 电子邮件:project@user.com
    • 密码:project123




  • 将这些登录凭据与您的 Firebase 详细信息一起保存,我们很快就会在网站代码中需要它们。

✅ 3. 设置 Arduino IDE

  • 打开 Arduino IDE 并粘贴此 ESP32 代码。
/*
AquaFlow - By Yugesh

This code connects an ESP32 to Firebase and monitors
two water flow sensors (YF-S401) along with a relay 
for pump control. Data (flow1 & flow2) is sent to Firebase
every second, and pump commands (ON/OFF/AUTO) are received
from the database in real time.

Before running this code:

1. Replace Wi-Fi and Firebase credentials with your own.
2. Ensure your Firebase Realtime Database structure (contains nodes: /pump, /flow1, /flow2)
3. Connect components according to the pin config below.
*/

#include <WiFi.h>
#include <Firebase_ESP_Client.h>
#include "addons/TokenHelper.h"

// Wi-Fi Configuration – CHANGE THESE VALUES

#define WIFI_SSID "Your_WiFi_Name"         // Replace with your Wi-Fi name
#define WIFI_PASSWORD "Your_WiFi_Password" // Replace with your Wi-Fi password

// Firebase Configuration – CHANGE THESE VALUES

#define API_KEY "Your_Firebase_API_Key"                          // Get from Firebase project settings
#define DATABASE_URL "https://your-database-url.firebaseio.com/" // Your Firebase RTDB URL
#define USER_EMAIL "your_email@example.com"                      // Must be a registered Firebase user
#define USER_PASSWORD "your_password"                            // Corresponding password


// Firebase Objects
FirebaseData fbdo;
FirebaseAuth auth;
FirebaseConfig config;

// Pin Configuration (ESP32 GPIO pins)
// You can change these if your wiring differs.
const int relayPin = 23;      // Relay control pin (Active LOW)
const int flowSensor1 = 18;   // Flow sensor 1 input pin (YF-S401)
const int flowSensor2 = 19;   // Flow sensor 2 input pin (YF-S401)

// Flow Measurement Variables
volatile int pulseCount1 = 0;
volatile int pulseCount2 = 0;
unsigned long lastSendTime = 0;

// Interrupt Service Routines for Flow Sensors
void IRAM_ATTR pulseCounter1() 

pulseCount1++; 
}
void IRAM_ATTR pulseCounter2() 

pulseCount2++; 
}

// Setup Function
void setup() 
{
Serial.begin(115200);

// Relay setup
pinMode(relayPin, OUTPUT);
digitalWrite(relayPin, HIGH); // Relay OFF initially (active LOW)

// Flow sensor setup
pinMode(flowSensor1, INPUT_PULLUP);
pinMode(flowSensor2, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(flowSensor1), pulseCounter1, FALLING);
attachInterrupt(digitalPinToInterrupt(flowSensor2), pulseCounter2, FALLING);

// Wi-Fi connection
WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
Serial.print("Connecting to Wi-Fi");
while (WiFi.status() != WL_CONNECTED) {
Serial.print(".");
delay(300);
}
Serial.println("\nWi-Fi Connected");

// Firebase setup
config.api_key = API_KEY;
config.database_url = DATABASE_URL;
auth.user.email = USER_EMAIL;
auth.user.password = USER_PASSWORD;
config.token_status_callback = tokenStatusCallback;

Firebase.begin(&config, &auth);
Firebase.reconnectWiFi(true);
Serial.println("Firebase Initialized");
}

// Loop Function
void loop() 
{
if (Firebase.ready()) {

// Read pump status from Firebase
if (Firebase.RTDB.getString(&fbdo, "/pump")) {  // Path: /pump (do not change unless needed)
String command = fbdo.to<String>();
Serial.print("Firebase command: ");
Serial.println(command);

if (command == "ON") 
{
digitalWrite(relayPin, LOW);   // Turn ON pump

else if (command == "OFF") 
{
digitalWrite(relayPin, HIGH);  // Turn OFF pump
}
else if (command == "AUTO") {
// Optional: Add automation logic based on sensor data
}
} else 
{
Serial.print("Failed to read pump: ");
Serial.println(fbdo.errorReason());
}

// Send flow data every 1 second
if (millis() - lastSendTime > 1000) {
detachInterrupt(digitalPinToInterrupt(flowSensor1));
detachInterrupt(digitalPinToInterrupt(flowSensor2));

// Convert pulse counts to flow rate (L/min)
float flowRate1 = (pulseCount1 / 7.5);
float flowRate2 = (pulseCount2 / 7.5);

pulseCount1 = 0;
pulseCount2 = 0;
lastSendTime = millis();

attachInterrupt(digitalPinToInterrupt(flowSensor1), pulseCounter1, FALLING);
attachInterrupt(digitalPinToInterrupt(flowSensor2), pulseCounter2, FALLING);
Serial.printf("Flow1: %.2f L/min | Flow2: %.2f L/min\n", flowRate1, flowRate2);

// Send data to Firebase
bool success1 = Firebase.RTDB.setFloat(&fbdo, "/flow1", flowRate1); // Path: /flow1
if (success1) 
{
Serial.println("flow1 sent to Firebase");

else 
{
Serial.print("flow1 failed: ");
Serial.println(fbdo.errorReason());
}
bool success2 = Firebase.RTDB.setFloat(&fbdo, "/flow2", flowRate2); // Path: /flow2
if (success2) 
{
Serial.println("flow2 sent to Firebase");

else
{
Serial.print("flow2 failed: ");
Serial.println(fbdo.errorReason());
}
}
}
delay(100); // Keep loop responsive for accurate flow measurement
}

/* 
Notes:
- Wi-Fi and Firebase credentials must be updated before upload.
- Ensure you’ve installed the “Firebase ESP Client” library by Mobizt.
- Flow sensor calibration constant (7.5) is for YF-S401; adjust if using a different model.
- Database paths (/pump, /flow1, /flow2) should exist in your Firebase RTDB.
*/
  • 使用您之前保存的 Firebase 详细信息更新占位符(API 密钥、Auth 域等)。
  • 通过 USB 将您的 ESP32 开发板连接到您的计算机。
  • 打开库管理器(Sketch → Include Library → Manage Libraries)并安装这两个库:
    • Mobizt 为 ESP8266 和 ESP32 开发的 Firebase Arduino 客户端库
    • Mobizt 的 Firebase ESP32 客户端




  • 将您的草图(文件 → 另存为)保存在新文件夹中,例如命名为 AquaFlow。
  • 在同一文件夹中,创建一个名为 TokenHelper.h 的新文件并粘贴下面给出的代码。
#ifndef TOKEN_HELPER_H
#define TOKEN_HELPER_H

// Provide the token generation process info
void tokenStatusCallback(TokenInfo info){
  Serial.printf("Token info: type = %s, status = %s\n",
                getTokenType(info).c_str(),
                getTokenStatus(info).c_str());
}

#endif



  • 转到工具 → 开发板 → ESP32 → ESP32 开发模块。



  • 然后转到工具→端口并选择正确的 COM 端口(例如,COM3)。
  • 单击上传(→)图标将代码刷入您的 ESP32。
  • 上传完成后,打开串行监视器确认 ESP32 已成功连接到 Wi-Fi。
  • 如果您看到您的设备已连接,那么恭喜您,您的 ESP 现在正在与 Firebase 通信!

✅ 4. 设置 Web 仪表板

  • 打开 Visual Studio Code(或任何代码编辑器)。
  • 在文件夹中创建三个文件:



创建 index.html 文件并粘贴以下代码

<!DOCTYPE html>
<html lang="en">
<head>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.0/css/all.min.css">
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>AquaFlow Dashboard</title>
<link href="https://fonts.googleapis.com/css2?family=Poppins:wght@400;600;700&display=swap" rel="stylesheet">
<link rel="stylesheet" href="style.css">
</head>
<body>
<h1>AquaFlow</h1>
<div class="dashboard">

  <!-- MOTOR CONTROL -->
  <div class="section">
    <h2>Motor Control</h2>
    <div class="switches">
      <button id="onBtn" onclick="setPump('ON')">ON</button>
      <button id="offBtn" onclick="setPump('OFF')">OFF</button>
    </div>
  </div>

  <!-- SENSOR STATUS -->
  <div class="section">
    <h2>Sensor Status</h2>
    <div class="sensor-status">
      <div><p>S1</p><div id="s1Status" class="status-dot"></div></div>
      <div><p>S2</p><div id="s2Status" class="status-dot"></div></div>
    </div>
  </div>

  <!-- WATER USAGE -->
  <div class="section">
    <h2>Water Usage</h2>
    <div class="usage">
      <div class="card"><p><b>Live</b></p><div class="value">₹<span id="livePrice">0</span></div><p><span id="liveLiters">0</span>L</p></div>
      <div class="card"><p><b>Weekly</b></p><div class="value">₹<span id="weekPrice">0</span></div><p><span id="weekLiters">0</span>L</p></div>
      <div class="card"><p><b>Monthly</b></p><div class="value">₹<span id="monthPrice">0</span></div><p><span id="monthLiters">0</span>L</p></div>
    </div>
    <button id="resetBtn">Reset</button>
  </div>

  <!-- LIVE FLOW -->
  <div class="section">
    <h2>Sensor Flow</h2>
    <div class="flow">
      <div class="card"><p><b>S1</b></p><div class="value"><span id="flow1">0</span> ml/sec</div></div>
      <div class="card"><p><b>S2</b></p><div class="value"><span id="flow2">0</span> ml/sec</div></div>
    </div>
  </div>

 <!-- LEAKAGE DETECTION -->
<div class="section">
  <h2>Leakage Detection</h2>
  <div class="sensor-status">
    <div>
      <div id="leakStatus" class="status-dot"></div>
    </div>
  </div>
</div>

<!-- ABOUT SECTION -->
<div class="section">
  <p>Project by <b>Yugesh</b></p>
  <div class="social">
  <a href="https://www.linkedin.com/in/yugeshweb" target="_blank" title="LinkedIn">
    <i class="fa-brands fa-linkedin"></i>
  </a>
</div>
  </div>
</div>

<script type="module" src="script.js"></script>
</body>
</html>

创建 style.css 文件并粘贴以下代码

* { 
  margin: 0; 
  padding: 0; 
  box-sizing: border-box; 
  font-family: 'Poppins', sans-serif; 
}

html, body { 
  height: 100%; 
}

body {
  background: url('Aquabg.jpg') repeat center center/cover;
  display: grid; 
  grid-template-rows: 80px 1fr; 
  gap: 10px; 
  padding: 10px; 
  color: #ffffff
  overflow: hidden; 
  position: relative;
}

@media(max-width: 768px){
  body { 
    overflow: auto; 
    height: auto; 
  }
}

body::before {
  content: ''; 
  position: absolute; 
  top: 0; 
  left: 0; 
  right: 0; 
  bottom: 0;
  background: linear-gradient(135deg, rgba(102,126,234,0.1), rgba(118,75,162,0.1)); 
  z-index: 0;
}

h1 { 
  text-align: center; 
  font-size: 2.5rem; 
  font-weight: 700; 
  z-index: 1; 
}

.dashboard {
  display: grid; 
  grid-template-columns: repeat(3, 1fr); 
  grid-template-rows: repeat(2, 1fr); 
  gap: 10px;
  height: 100%; 
  z-index: 1;
}

@media(max-width: 900px){ 
  .dashboard { 
    grid-template-columns: repeat(2, 1fr); 
    grid-template-rows: repeat(3, 1fr); 
  } 
}

@media(max-width: 600px){ 
  .dashboard { 
    grid-template-columns: 1fr; 
    grid-template-rows: repeat(6, 1fr); 
  } 
}

.section {
  background: rgba(255,255,255,0.25); 
  border-radius: 20px; 
  backdrop-filter: blur(15px);
  padding: 15px; 
  display: flex; 
  flex-direction: column; 
  align-items: center; 
  justify-content: center;
  border: 1px solid rgba(255,255,255,0.4);
}

.section h2 { 
  margin-bottom: 10px; 
  font-size: 1.2rem; 
  font-weight: 600; 
  text-align: center; 
}

.switches { 
  display: flex; 
  gap: 10px; 
}

button {
  background: rgba(255,255,255,0.4); 
  border: 2px solid #000
  color: #000;
  font-weight: 600; 
  padding: 8px 20px; 
  border-radius: 25px; 
  cursor: pointer;
  transition: 0.3s;
}

button.active { 
  background: #000
  color: #fff
}

.sensor-status { 
  display: flex; 
  gap: 15px; 
  justify-content: center; 
}

.sensor-status > div { 
  display: flex; 
  flex-direction: column; 
  align-items: center; 
  gap: 5px;
  padding: 10px; 
  background: rgba(255,255,255,0.2); 
  border-radius: 15px; 
  min-width: 60px; 
  border: 1px solid rgba(255,255,255,0.3);
}

.status-dot { 
  width: 20px; 
  height: 20px; 
  border-radius: 50%; 
  background: #ff6b6b
  border: 2px solid rgba(0,0,0,0.2); 
  transition: 0.3s; 
}

.status-dot.active { 
  background: #51cf66
  animation: pulse 2s infinite; 
}

@keyframes pulse {
  0%, 100% { transform: scale(1); }
  50% { transform: scale(1.1); } 
}

.card { 
  background: rgba(255,255,255,0.3); 
  border-radius: 15px; 
  padding: 10px; 
  text-align: center; 
  width: 90px; 
  margin: 5px; 
}

.value { 
  font-size: 1.2rem; 
  font-weight: 700; 
}

.usage, .flow, .leakage { 
  display: flex; 
  justify-content: center; 
  gap: 10px; 
  flex-wrap: wrap; 
}

#resetBtn { 
  background: rgb(35, 6, 6); 
  color: #fff
  border: none; 
  padding: 6px 15px; 
  border-radius: 25px; 
  cursor: pointer; 
  margin-top: 5px; 
}

.social-links { 
  display: flex; 
  flex-direction: column; 
  gap: 5px; 
  align-items: center; 
  margin-top: 10px; 
}

.social-links a { 
  text-decoration: none; 
  color: #000
  font-weight: 600; 
  transition: 0.3s; 
}

.social-links a:hover { 
  color: #51cf66
  transform: scale(1.05); 
}

.social a {
  color: #c2c2c2;
  font-size: 30px;
  text-decoration: none;
}

.social a:hover {
  color: #000000;
}

创建 script.js 文件并粘贴以下代码

import { initializeApp } from "https://www.gstatic.com/firebasejs/11.0.1/firebase-app.js";
import { getDatabase, ref, onValue, set, get } from "https://www.gstatic.com/firebasejs/11.0.1/firebase-database.js";

// Replace these values with your own Firebase credentials
const firebaseConfig = {
  apiKey: "YOUR_API_KEY_HERE",
  authDomain: "YOUR_PROJECT_ID.firebaseapp.com",
  databaseURL: "https://YOUR_PROJECT_ID-default-rtdb.YOUR_REGION.firebasedatabase.app",
  projectId: "YOUR_PROJECT_ID",
  storageBucket: "YOUR_PROJECT_ID.appspot.com",
  messagingSenderId: "XXXXXX",
  appId: "1:XXXX:web:XXXX"
};

const app = initializeApp(firebaseConfig);
const db = getDatabase(app);

const flow1Ref = ref(db, "flow1");
const flow2Ref = ref(db, "flow2");
const pumpRef = ref(db, "pump");
const dataRef = ref(db, "data");

let totalLiters = 0, totalPrice = 0, lastF1 = 0, lastF2 = 0, lastFlow1 = 0, lastFlow2 = 0;

// Load permanent data once
async function loadData() {
  const snap = await get(dataRef);
  if (snap.exists()) {
    const d = snap.val();
    totalLiters = d.totalLiters || 0;
    totalPrice = d.totalPrice || 0;
    lastFlow1 = d.lastFlow1 || 0;
    lastFlow2 = d.lastFlow2 || 0;
    updateUsageDisplay();
  }
}
loadData();

// Save permanent data
function saveData() {
  set(dataRef, { totalLiters, totalPrice, lastFlow1, lastFlow2 });
}

const onBtn = document.getElementById("onBtn");
const offBtn = document.getElementById("offBtn");

window.setPump = (state) => {
  set(pumpRef, state);
  onBtn.classList.toggle('active', state === 'ON');
  offBtn.classList.toggle('active', state === 'OFF');
};

function updateUsageDisplay() {
  document.getElementById("liveLiters").innerText = totalLiters.toFixed(2);
  document.getElementById("livePrice").innerText = totalPrice.toFixed(2);
}

// Listen for flow updates
onValue(flow1Ref, snap => {
  const f1 = snap.val() || 0;
  document.getElementById("flow1").innerText = (f1 * 1000 / 60).toFixed(1);
  document.getElementById("s1Status").classList.toggle('active', f1 > 0);
  lastF1 = f1;
  checkLeak();
  if (f1 > lastFlow1) {
    lastFlow1 = f1;
    saveData();
  }
});

onValue(flow2Ref, snap => {
  const f2 = snap.val() || 0;
  document.getElementById("flow2").innerText = (f2 * 1000 / 60).toFixed(1);
  document.getElementById("s2Status").classList.toggle('active', f2 > 0);
  lastF2 = f2;
  checkLeak();

  if (f2 > lastFlow2) {
    const diff = f2 - lastFlow2;
    totalLiters += diff;
    totalPrice = totalLiters * 0.3; // Adjust pricing logic as needed
    lastFlow2 = f2;
    saveData();
    updateUsageDisplay();
  }
});

document.getElementById("resetBtn").addEventListener('click', () => {
  totalLiters = 0;
  totalPrice = 0;
  lastFlow1 = 0;
  lastFlow2 = 0;
  saveData();
  updateUsageDisplay();
});

function checkLeak() {
  const leakDot = document.getElementById("leakStatus");
  const isNormal = Math.abs(lastF1 - lastF2) < 0.05;
  leakDot.classList.toggle('active', isNormal);
  leakDot.style.background = isNormal ? '#51cf66' : '#ff6b6b';
}
  • 在您的 script.js 中,更新 Firebase 配置值:
    • api密钥
    • 授权域
    • 数据库URL
    • 项目编号
    • 存储桶


(使用您之前从 Firebase 保存的相同详细信息。)

  • 一切设置完成后,启动网站(您可以直接在浏览器中打开index.html)。



  • 您现在可以从网络仪表板控制泵,实时打开或关闭它,并通过 Firebase 立即同步数据。

轰!您已经成功构建了自己的智能水泵控制器。

✅工作模型



  • 从网站控制



🧩 最后说明

由于这个项目还只是个原型模型,所以水量的测量精度并不完美。我还没有校准流量传感器以获得精确的读数。

您还会注意到,即使刷新页面,计算出的价格和升数也不会重置。这是因为数据是持久存储的,只能使用“重置”按钮进行重置。

您可能还会注意到泄漏检测指示灯变红。这是因为两个传感器之间的流速不匹配。在实际系统中,这表示可能存在泄漏,但在这个小规模模型中,泄漏主要是由于水流的压力和高度差异造成的,而不是实际的泄漏。


GitHub链接:https://github.com/yugeshweb/AquaFlow


关注「索引目录」公众号,获取更多干货。


【声明】内容源于网络
0
0
索引目录
索引目录是一家专注于医疗、技术开发、物联网应用等领域的创新型公司。我们致力于为客户提供高质量的服务和解决方案,推动技术与行业发展。
内容 444
粉丝 0
索引目录 索引目录是一家专注于医疗、技术开发、物联网应用等领域的创新型公司。我们致力于为客户提供高质量的服务和解决方案,推动技术与行业发展。
总阅读12
粉丝0
内容444