[趣味] ESP32 スマート噴水冷却システム
プロジェクトのハイライト
- [x] ウェブ操作:デバイスのIPにアクセスするだけで、温度と自動化状態をリアルタイムで確認できます
- [x] 手動/自動デュアルモード:ウェブからいつでも給水のオン/オフが可能(手動モードでは一時的に自動化を解除し、水の無駄を防ぎます)
- [x] スマート温控:自動モードでは、温度 ≥30℃ で自動給水開始、<30℃ で自動停止
- [ ] ウェブにはまだCSSが追加されておらず、機能デモのみです。必要に応じて自由に美化できます
きっかけ:エアコンが故障、問題は室外機にあった
2024年の湖北の夏は特に厳しく、家のエアコンの冷房効果が極めて悪く、ほぼ機能していない状態でした。
試行錯誤の末、 制御変数法 で項目ごとに調査し、最終的に真犯人を特定——室外機背面パネルの放熱不良(キャビネット板に遮られ、熱が排出できない)。
応急処置として思いついた解決策はシンプルかつ強引:ポンプで室外機に水を噴射し、強制冷却。
TBでポンプ、リレーなどのモジュールを購入し、電源を接続、動作確認成功!第一歩クリア!

新たな問題:ずっと水を噴射するのは無駄が多すぎる
手動スイッチは面倒なだけでなく、不在時には空焚きか水の無駄遣いになる。
システムをどうやって温度に応じて自動で起動/停止。
させるか考え始めた。ふと昨年ロケットの姿勢制御に使った MPU6050 六軸センサーを思い出した。これにはNTC温度モジュールが内蔵されている。
本来はチップ温度の検出用だが、MPU6050自体の発熱は極めて小さいため、環境温度とほぼみなせる。
そこでロケットから「借用」して、 ESP32-C3 と組み合わせて温控を実現。
ハードウェア配線
MPU6050とESP32-C3の配線は非常に簡単(左:MPU6050、右:ESP32-C3):
| MPU6050 | ESP32-C3 |
|---|---|
| VCC | 3V3 |
| GND | GND |
| SDA | P8 (SDA) |
| SCL | P9 (SCL) |

ソフトウェア設計
開発環境:Arduino IDE
コアの考え方:ESP32-C3が軽量Webサーバーを起動し、スマホやPCで接続するとリアルタイム温度と給水状態が確認でき、ボタンで手動制御または自動化を有効にできます。
主な機能:
- ルートパスにアクセス
/制御ページを返す /dhtインターフェースがセンサーデータを返す(リアルタイム温度、給水状態、自動状態)/setインターフェースで散水スイッチを切り替え(同時に自動モードをオフ)/autoインターフェースで自動モードを切り替え- 自動化ロジック:温度 ≥30℃ で水を出す、<30℃ で水を止める
以下が完全なコードです:
#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);
}
}
