El blog personal de un estudiante de secundaria

[Pasatiempo] Sistema inteligente de enfriamiento por aspersión de agua con ESP32

Aventuras diarias

Puntos destacados del proyecto

  • [x] Control por página web: Accede a la IP del dispositivo para ver en tiempo real la temperatura y el estado de automatización
  • [x] Modo dual manual/automático: Activa o desactiva el riego en cualquier momento desde la página web (el modo manual saldrá temporalmente de la automatización para evitar desperdiciar agua)
  • [x] Control inteligente de temperatura: En modo automático, cuando la temperatura ≥30℃ se activa el agua automáticamente, y cuando es <30℃ se detiene automáticamente
  • [ ] La página web aún no tiene CSS añadido, solo sirve como demostración funcional, se puede embellecer según sea necesario

Origen: El aire acondicionado dejó de funcionar, el problema estaba en la unidad exterior

El verano de 2024 en Hubei fue especialmente insoportable, el aire acondicionado de casa enfriaba muy mal, básicamente era inútil.
Después de mucho batallar, mediante el método de control de variables se revisó punto por punto, y finalmente se identificó al culpable——la disipación de calor deficiente del panel trasero de la unidad exterior(bloqueado por el panel del armario, el calor no podía salir).

La solución temporal que se me ocurrió fue simple y directa:rociar agua hacia la unidad exterior con una bomba, forzando el enfriamiento

Compré en TB la bomba, relés y otros módulos, conecté la alimentación, y funcionó, ¡primer paso exitoso!


Nuevo problema: rociar agua constantemente desperdicia demasiado

El interruptor manual no solo es molesto, sino que cuando no hay nadie en casa, o se quema en seco o se desperdicia agua.
Empecé a pensar en cómo hacer que el sistemase iniciara y detuviera automáticamente según la temperatura

De repente recordé el sensor de seis ejes MPU6050que quedó de cuando hice corrección de actitud de cohetes el año pasado, el cual tiene integrado un módulo de temperatura NTC.
Aunque originalmente se usaba para detectar la temperatura del chip, el MPU6050 genera muy poco calor propio, por lo que se puede aproximar perfectamente como temperatura ambiente.
Así que lo “tomé prestado” del cohete, y junto con el ESP32-C3 implementé el control de temperatura.


Conexiones de hardware

La conexión entre el MPU6050 y el ESP32-C3 es muy sencilla (izquierda: MPU6050, derecha: ESP32-C3):

MPU6050el ESP32-C3
VCC3V3
GNDGND
SDAP8 (SDA)
SCLP9 (SCL)

Diseño de software

Entorno de desarrollo:Arduino IDE
Idea central: El ESP32-C3 inicia un servidor web ligero, y al conectarse desde un móvil u ordenador se puede ver la temperatura en tiempo real y el estado de riego, y se puede controlar manualmente o activar la automatización mediante botones.

Funciones principales:

  • Acceder a la ruta raíz / Devuelve la página de control
  • /dht La interfaz devuelve los datos del sensor (temperatura en tiempo real, estado de riego, estado automático)
  • /set Interruptor de riego por conmutación de interfaz (desactiva simultáneamente el modo automático)
  • /auto Modo automático por conmutación de interfaz
  • Lógica de automatización: temperatura ≥30℃ activa el agua, <30℃ detiene el agua

A continuación el código completo:

#include <Adafruit_MPU6050.h>
#include <Adafruit_Sensor.h>
#include <WiFi.h>
#include <ESPAsyncWebServer.h>

AsyncWebServer server(80);   // 端口80,可直接通过IP访问

// 网页 HTML(存储于 Flash)
const char index_html[] PROGMEM = R"rawliteral(
<!DOCTYPE HTML>
<html>
<head>
    <meta charset="utf-8">
    <title>空调浇水控制</title>
</head>
<body>
    <h2>空调浇水控制</h2>
    <div id="dht"></div>
    <button onclick="set()">开启/关闭浇水</button>
    <button onclick="autoset()">开启/关闭自动</button>
</body>
<script>
    function set() {
        var xhr = new XMLHttpRequest();
        xhr.open("GET", "/set?value=ESP32", true);
        xhr.send();
    }
    function autoset() {
        var xhr = new XMLHttpRequest();
        xhr.open("GET", "/auto?value=ESP32", true);
        xhr.send();
    }
    // 每秒更新一次数据
    setInterval(function () {
        var xhttp = new XMLHttpRequest();
        xhttp.onreadystatechange = function () {
            if (this.readyState == 4 && this.status == 200) {
                document.getElementById("dht").innerHTML = this.responseText;
            }
        };
        xhttp.open("GET", "/dht", true);
        xhttp.send();
    }, 1000);
</script>
</html>
)rawliteral";

Adafruit_MPU6050 mpu;
#define MPU6050_INTERVAL 100       // 传感器读取间隔(毫秒)
unsigned long mpu6050Times = 0;
float mpu6050Temp = 0;

float xAcceleration, yAcceleration, zAcceleration;
float xAccele, yAccele, zAccele;
float xGyro = 0, yGyro = 0, zGyro = 0;
float gravity = 9.8;

bool iswater = false;  // 浇水状态
bool isauto = true;    // 自动模式默认开启

// 构造返回给网页的 HTML 数据
String Merge_Data(void) {
    String dataBuffer = "<p>";
    dataBuffer += "<h1>传感器数据</h1>";
    dataBuffer += "<b>温度: </b>";
    dataBuffer += String(mpu6050Temp, 1) + " ℃";
    dataBuffer += "<br />";
    dataBuffer += "<b>当前浇水状态: </b>";
    dataBuffer += iswater ? "开启" : "关闭";
    dataBuffer += "<br />";
    dataBuffer += "<b>当前自动状态: </b>";
    dataBuffer += isauto ? "开启" : "关闭";
    dataBuffer += "<br /></p>";
    return dataBuffer;
}

// 手动切换浇水(同时关闭自动模式)
void Config_Callback(AsyncWebServerRequest *request) {
    iswater = !iswater;
    isauto = false;
    request->send(200, "text/plain", "OK");
}

// 切换自动模式
void Auto_Callback(AsyncWebServerRequest *request) {
    isauto = !isauto;
    request->send(200, "text/plain", "OK");
}

void setup() {
    Serial.begin(115200);

    // 连接 WiFi(请替换为实际 SSID 和密码)
    WiFi.begin("你的WiFi名", "你的WiFi密码");
    while (WiFi.status() != WL_CONNECTED) {
        delay(500);
        Serial.println("正在连接WiFi...");
    }
    Serial.println("WiFi 连接成功!");
    Serial.print("IP 地址: ");
    Serial.println(WiFi.localIP());

    pinMode(1, OUTPUT);   // GPIO1 控制继电器(水泵)

    // 配置 Web 服务器路由
    server.on("/", HTTP_GET, [](AsyncWebServerRequest *request) {
        request->send_P(200, "text/html", index_html);
    });
    server.on("/dht", HTTP_GET, [](AsyncWebServerRequest *request) {
        request->send_P(200, "text/plain", Merge_Data().c_str());
    });
    server.on("/set", HTTP_GET, Config_Callback);
    server.on("/auto", HTTP_GET, Auto_Callback);
    server.begin();
    Serial.println("HTTP 服务器已启动");

    // 初始化 MPU6050
    if (!mpu.begin()) {
        Serial.println("未找到 MPU6050 芯片!");
        while (1) { delay(1000); }  // 停止运行
    }
    mpu.setAccelerometerRange(MPU6050_RANGE_16_G);
    mpu.setGyroRange(MPU6050_RANGE_250_DEG);
    mpu.setFilterBandwidth(MPU6050_BAND_21_HZ);
    Serial.println("MPU6050 初始化成功!");
}

void loop() {
    getMpu6050Data();   // 读取温度及姿态数据

    if (isauto) {
        // 自动模式:根据温度控制水泵
        if (mpu6050Temp >= 30.0) {
            digitalWrite(1, HIGH);
            iswater = true;
        } else {
            digitalWrite(1, LOW);
            iswater = false;
        }
    } else {
        // 手动模式:直接遵从按钮状态
        digitalWrite(1, iswater ? HIGH : LOW);
    }
}

void getMpu6050Data() {
    if (millis() - mpu6050Times >= MPU6050_INTERVAL) {
        mpu6050Times = millis();

        sensors_event_t a, g, temp;
        mpu.getEvent(&a, &g, &temp);

        mpu6050Temp = temp.temperature;

        xAcceleration = a.acceleration.x;
        yAcceleration = a.acceleration.y;
        zAcceleration = a.acceleration.z;

        xAccele = xAcceleration / gravity;   // 转换为 g 为单位
        yAccele = yAcceleration / gravity;
        zAccele = zAcceleration / gravity;

        xGyro = g.gyro.x;
        yGyro = g.gyro.y;
        zGyro = g.gyro.z;

        // 串口输出,便于调试(保留了姿态数据,方便后续扩展)
        Serial.print("温度: "); Serial.print(mpu6050Temp);
        Serial.print(" , x加速: "); Serial.print(xAccele);
        Serial.print(" , y加速: "); Serial.print(yAccele);
        Serial.print(" , z加速: "); Serial.print(zAccele);
        Serial.print(" , x角速度: "); Serial.print(xGyro);
        Serial.print(" , y角速度: "); Serial.print(yGyro);
        Serial.print(" , z角速度: "); Serial.println(zGyro);
    }
}