项目亮点
- [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 服务器,手机或电脑连上后即可看到实时温度与浇水状态,并可通过按钮手动控制或开启自动化。
主要功能:
- 访问根路径
/返回控制页面 /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);
}
}
评论
暂无评论