💡 划重点
- 用 ESP32-S3 做主控,电容式土壤湿度传感器做核心感知,DHT22 补充空气温湿度。
- 本地 OLED 实时显示,MQTT 上报手机或 Home Assistant,出门也能看。
- 土壤偏干才启动微型水泵滴灌,带滞回区间,避免反复启停。
- 深睡眠周期唤醒,适合阳台无插座场景,整机成本控制在 60 元内。
需求洞察
目标用户是城市阳台园艺爱好者,尤其是养多肉、薄荷、番茄、草莓的上班族。他们通常只有两三盆到十几盆植物,空间有限,浇水靠记忆和手感。真实痛点有三个:一是忘记浇水,夏季阳台蒸发快,一天就能让叶片发蔫;二是浇水过量,托盘积水导致烂根;三是出差或加班几天,回家发现植物已经救不回来。市售智能花盆价格高、体积固定、不适合已有花盆改造,而专业灌溉控制器又偏贵、偏工程化。这个项目值得做,是因为土壤湿度是盆栽最关键的变量,用几十元就能把“凭感觉浇水”变成“按湿度补水”,而且 ESP32-S3 自带 Wi-Fi,能把数据接到现有智能家居里,扩展成本很低。
方案设计
感知层由电容式土壤湿度传感器和 DHT22 组成。电容式探头比电阻式更耐腐蚀,适合长期插在土里;DHT22 提供空气温湿度,用于判断环境蒸发趋势。控制层是 ESP32-S3,负责 ADC 采样、滑动平均滤波、阈值判断和低功耗调度。执行层是微型直流水泵加继电器或 MOS 管,通过硅胶滴灌管把水送到盆土,每次只运行数秒,防止积水。日志与联网层包括 0.96 寸 OLED 和 MQTT:OLED 显示湿度百分比、温度和运行状态;MQTT 把数据发到本地 Broker 或云平台,同时可接收远程手动浇水指令。低功耗策略是默认深睡眠,每 10 分钟唤醒一次采样,湿度低于阈值才启动水泵并上报,其余时间休眠。
物料清单
| 物料 | 数量 | 用途 | 估算单价 | 合计 |
|---|---|---|---|---|
| ESP32-S3 开发板 | 1 | 主控与 Wi-Fi | 24 元 | 24 元 |
| 电容式土壤湿度传感器 | 1 | 测土壤含水率 | 5 元 | 29 元 |
| DHT22 温湿度模块 | 1 | 测空气温湿度 | 9 元 | 38 元 |
| 0.96 寸 I2C OLED | 1 | 本地显示 | 8 元 | 46 元 |
| 微型直流水泵与硅胶管 | 1 | 滴灌执行 | 7 元 | 53 元 |
| 继电器模块 | 1 | 控制水泵通断 | 3 元 | 56 元 |
| 杜邦线与面包板 | 1 套 | 连接与固定 | 2 元 | 58 元 |
总成本约 58 元,低于 60 元目标。水泵可用 5V 电源独立供电,避免电机启动拉低主控电压。
接线与结构
题目给定的土壤湿度模拟脚 A0=34,但 ESP32-S3 的 ADC1 通道集中在 GPIO1 到 GPIO10,GPIO34 并不适合直接做 ADC 输入。因此微调为 GPIO4,对应 ADC1_CH3,采样更稳定。DHT22 保持 GPIO15,OLED 保持 GPIO21/22,继电器新增 GPIO5。
| 模块 | 引脚 | ESP32-S3 |
|---|---|---|
| 土壤湿度 | AO | GPIO4 |
| 土壤湿度 | VCC/GND | 3.3V/GND |
| DHT22 | DATA | GPIO15 |
| DHT22 | VCC/GND | 3.3V/GND |
| OLED | SDA | GPIO21 |
| OLED | SCL | GPIO22 |
| 继电器 | IN | GPIO5 |
| 继电器 | VCC/GND | 5V/GND |
| 水泵 | 电源 | 5V 独立供电,经继电器常开触点 |
结构上,ESP32-S3 和 OLED 放进小防水盒,土壤探头插入盆土中段,DHT22 固定在盒外通风处,水泵放在水瓶旁,滴灌管插入盆边。
核心代码
以下为 Arduino 骨架,使用 PubSubClient、DHT 和 Adafruit_SSD1306 库。
#include <WiFi.h>
#include <PubSubClient.h>
#include <DHT.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#define SOIL_PIN 4
#define DHT_PIN 15
#define RELAY_PIN 5
#define DHT_TYPE DHT22
Adafruit_SSD1306 oled(128, 64, &Wire;, -1);
DHT dht(DHT_PIN, DHT_TYPE);
WiFiClient espClient;
PubSubClient mqtt(espClient);
const char* SSID = "your-ssid";
const char* PASS = "your-pass";
const char* BROKER = "192.168.1.10";
const int DRY_RAW = 3200;
const int WET_RAW = 1200;
const int WATER_ON = 35;
const int WATER_OFF = 50;
int readSoilPercent() {
int raw = analogRead(SOIL_PIN);
int pct = map(raw, DRY_RAW, WET_RAW, 0, 100);
return constrain(pct, 0, 100);
}
void waterPump(int seconds) {
digitalWrite(RELAY_PIN, HIGH);
delay(seconds * 1000);
digitalWrite(RELAY_PIN, LOW);
}
void setup() {
Serial.begin(115200);
pinMode(RELAY_PIN, OUTPUT);
digitalWrite(RELAY_PIN, LOW);
Wire.begin(21, 22);
oled.clearDisplay();
dht.begin();
WiFi.begin(SSID, PASS);
mqtt.setServer(BROKER, 1883);
delay(3000);
}
void loop() {
int soil = readSoilPercent();
float t = dht.readTemperature();
float h = dht.readHumidity();
oled.clearDisplay();
oled.setTextSize(1);
oled.setCursor(0, 0);
oled.printf("Soil:%d%%\nT:%.1fC H:%.0f%%", soil, t, h);
oled.display();
if (soil < WATER_ON) {
waterPump(3);
} else if (soil > WATER_OFF) {
digitalWrite(RELAY_PIN, LOW);
}
if (mqtt.connect("plant-guard")) {
char payload[64];
snprintf(payload, sizeof(payload), "{\"soil\":%d,\"t\":%.1f,\"h\":%.0f}", soil, t, h);
mqtt.publish("balcony/plant/state", payload);
mqtt.disconnect();
}
esp_sleep_enable_timer_wakeup(10ULL * 60ULL * 1000000ULL);
esp_deep_sleep_start();
}
调试步骤
- 先只接土壤传感器,串口打印原始 ADC 值,记录空气中、刚浇透后的数值,填入 DRY_RAW 和 WET_RAW。
- 扫描 OLED 地址,确认 0x3C 能显示,再接入 DHT22 检查读数。
- 用 Mosquitto 或 Home Assistant 测试 MQTT 主题,确认 JSON 能收到。
- 继电器先接 LED 测试,确认逻辑为高电平触发,再接水泵,避免空转。
- 测深睡眠电流,若偏高,检查 OLED 和 DHT 供电,必要时加 MOS 管断电。
- 设定 10 分钟唤醒周期,观察一天内的湿度曲线,再调整阈值。