💡 划重点
- 主控:ESP32-S3,兼具 Wi-Fi 与 BLE,满足 MQTT 联网与未来 OTA 需求。
- 核心监测:电阻式土壤湿度传感器模拟输出,配合 DHT22 温湿度补偿,提升浇水判断准确率。
- 交互与反馈:0.96 寸 OLED 实时显示数据,本地灯光/蜂鸣器提醒,MQTT 推送至手机。
- 低功耗策略:非浇水期进入深度睡眠,每小时唤醒一次采集并上报,单节 18650 可续航约两个月。
- 成本控制:总物料费控制在 60 元以内,全部使用常见模块,可自行焊接组装。
需求洞察
目标用户:阳台面积有限、喜欢种植香草、花卉或小番茄的都市园艺爱好者,尤其是经常出差或忘记浇水的上班族。
真实痛点:
- 浇水全凭感觉:盆土表面干了但底部仍湿,反复过量浇水导致烂根,是阳台盆栽死亡的第一大原因。
- 时间碎片化:用户无法每天定时巡查每一盆植物,出差或假期时只能拜托邻居,结果往往不理想。
- 缺乏长期数据:不知道盆土湿度在一天中的变化规律,难以优化浇水时间和水量,无法积累种植经验。
- 市售方案太贵:成品智能花盆或灌溉套件均价 150 元以上,且多为封闭系统,无法按自己需求扩展。
为什么值得做:
- 该项目以极低成本解决高频刚需,核心感知链路清晰,是学习物联网硬件开发的理想入门作品。
- ESP32-S3 提供了丰富的片上资源,项目后期可平滑扩展为多盆联动、自动补水,甚至结合天气预报决策。
- 阳台场景贴近生活,用户通过自己动手能获得极强的成就感,同时真正改善家居种植体验。
方案设计
感知层
- 土壤湿度:电阻式湿度传感器(镀金探头),模拟电压输出接 ESP32-S3 的 GPIO34(ADC1_CH6)。非浇水期土壤越干阻值越大,输出电压越高,程序中设定上下阈值。
- 环境温湿度:DHT22(AM2302)单总线数字传感器,连接 GPIO15。用于温度校正与露点粗略参考,辅助判断土壤水分蒸发速率。
控制层
- 主控 ESP32-S3:双核 Xtensa LX7,内置 2.4 GHz Wi-Fi,支持低功耗协处理器。在 loop 中完成采集、显示、判断与数据推送,随后进入 Light Sleep,由 RTC 定时器唤醒。
- 低功耗管理:深度睡眠时将外设(传感器、OLED)供电经由 GPIO 控制的高侧 PMOS 切断,仅保留 RTC 唤醒源,平均待机电流可降至 1.6 mA 左右。
执行层
- OLED 显示:0.96 寸 128×64 I²C SSD1306,地址 0x3C,接 GPIO21(SDA)、GPIO22(SCL)。实时轮显湿度百分比、温度、浇水建议及电池电压。
- 本地告警:板载 LED (GPIO2) 在湿度低于临界值时以 1 Hz 闪烁;预留有源蜂鸣器控制接口(GPIO5),可通过跳线帽开启声音提示。
日志/联网层
- MQTT 数据上传:通过家庭 Wi-Fi 连接至本地 MQTT Broker(如 Mosquitto 或 EMQX),上报 JSON 结构体
{"moisture":56,"temp":24.5,"bat":3.91}。 - 远程提醒与数据存储:手机端可使用 MQTT Dash 或 Node-RED 可视化,并可对接 InfluxDB 存储历史曲线。
- OTA 固件更新:利用 ArduinoOTA 库或基于 ESP-IDF 的 app_update 组件,实现远程固件升级,长期维护无需拆机。
整体工作流程:每小时唤醒→依次开启传感器与 OLED 供电→采集 3 次取中值→OLED 显示并判断是否需要浇水→连接 Wi-Fi→MQTT 上报→关闭外设供电→进入深度睡眠。
物料清单
| 物料名称 | 数量 | 用途 | 估算单价(元) | 小计(元) |
|---|---|---|---|---|
| ESP32-S3 开发板 (Mini N16R8) | 1 | 主控与无线通信 | 19.90 | 19.90 |
| 电阻式土壤湿度传感器 | 1 | 测量盆土容积含水率 | 3.50 | 3.50 |
| DHT22 温湿度模块 | 1 | 环境温湿度采集 | 5.80 | 5.80 |
| 0.96 寸 OLED (I²C, SSD1306) | 1 | 本地数据显示 | 8.90 | 8.90 |
| 18650 电池盒 + 保护板 | 1 | 供电与电池保护 | 3.00 | 3.00 |
| TP4056 充电模块 | 1 | USB 锂电池充电 | 1.80 | 1.80 |
| AO3401 PMOS 模块 + 电阻 | 1 | 外设电源控制 | 0.80 | 0.80 |
| 洞洞板、排针、杜邦线、热缩管 | 若干 | 结构连接 | 4.00 | 4.00 |
| 3D 打印外壳(或防水盒改装) | 1 | IP54 级防护 | 6.00 | 6.00 |
| 合计 | 53.70 |
总成本 53.70 元,预留 6.30 元用于运费或价格浮动,确保在 60 元目标内。
接线与结构
依据给定建议并微调后的接线如下:
- 土壤湿度传感器:VCC 经 PMOS 受控 3.3 V 供电,GND 接公共地,模拟输出 → GPIO34(ADC1_CH6)。说明:使用 ADC1 通道在 Wi-Fi 开启时仍能正常工作,避免 ADC2 冲突。
- DHT22:VCC 同样来自受控 3.3 V,DATA → GPIO15,并上拉 10 kΩ 电阻至 VCC。
- OLED:VCC 接受控 3.3 V,GND 公共地,SDA → GPIO21,SCL → GPIO22(ESP32-S3 的 I²C 外设 0 默认引脚)。
- 外设供电控制 PMOS:GPIO7 经 1 kΩ 限流电阻接到 PMOS 栅极,源极接电池正极,漏极输出 V_PERI(外设 3.3 V)通过 LDO 或直接从电池降压模块获得,此处使用开发板 3.3 V 引脚总输出。
- 蜂鸣器(选焊):GPIO5 经过 NPN S8050 驱动有源蜂鸣器。
- 唤醒按键:轻触开关一端接 GPIO0,另一端接地,同时作为烧录按钮,长按即可从深睡眠唤醒并进入下载模式。
结构说明:
- 将 ESP32-S3 开发板与充电、供电模块焊接在洞洞板下层;OLED、传感器接口和设置按钮固定在上层或外壳面板。
- 探头插入花盆侧壁中部位置,信号线用热缩管防水处理后从花盆底部排水孔引出。
- 外壳可 3D 打印或选用 IP54 仪表防水盒,底部设计探头引线孔,并打胶密封。
核心代码
以下为基于 Arduino 框架的可编译骨架,使用 PlatformIO 环境,开发板选择 esp32-s3-devkitc-1。
#include <WiFi.h>
#include <PubSubClient.h>
#include <Adafruit_Sensor.h>
#include <DHT.h>
#include <U8g2lib.h>
// ---------- 引脚与常量 ----------
#define PIN_MOISTURE 34 // ADC1_CH6
#define PIN_DHT 15
#define PIN_PERI_EN 7 // 外设供电使能
#define PIN_BUZZER 5
#define PIN_LED 2
#define DHT_TYPE DHT22
DHT dht(PIN_DHT, DHT_TYPE);
U8G2_SSD1306_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0, /* reset=*/ U8X8_PIN_NONE);
// WiFi 与 MQTT
const char* ssid = "Your_SSID";
const char* password = "Your_PASSWORD";
const char* mqtt_server = "192.168.1.100";
WiFiClient espClient;
// 湿度范围参数(需根据探头在空气中/水中的读数标定)
const int DRY_AIR = 3200; // 空气中 ADC 原始值
const int WET_WATER = 1400; // 水中 ADC 原始值
// 唤醒间隔(秒)
const uint64_t SLEEP_SEC = 3600; // 每小时
void setup() {
Serial.begin(115200);
pinMode(PIN_PERI_EN, OUTPUT);
pinMode(PIN_BUZZER, OUTPUT);
pinMode(PIN_LED, OUTPUT);
// 上电开启外设供电
delay(100); // 等待电源稳定
dht.begin();
u8g2.begin();
u8g2.clearBuffer();
u8g2.sendBuffer();
// 采集并处理数据
float moisture = readMoisture();
float temp = dht.readTemperature();
float hum = dht.readHumidity();
// 显示
displayData(moisture, temp);
// 判断是否需要本地告警
if (moisture < 30.0) {
digitalWrite(PIN_LED, HIGH);
tone(PIN_BUZZER, 1000, 300);
delay(300);
digitalWrite(PIN_LED, LOW);
}
// 联网上报(若电池电压较低可跳过)
if (connectWiFi()) {
if (client.connect("BalconyPlanter")) {
String payload = "{\"moisture\":" + String(moisture, 1) +
",\"temp\":" + String(temp, 1) +
",\"humidity\":" + String(hum, 1) + "}";
client.publish("planter/data", payload.c_str());
client.disconnect();
}
WiFi.disconnect(true);
}
// 关闭外设供电,准备休眠
esp_sleep_enable_timer_wakeup(SLEEP_SEC * 1000000ULL);
esp_deep_sleep_start();
}
void loop() {
// 主逻辑均在 setup 中完成,深睡后重启执行
}
// ---------- 读取土壤湿度百分比 ----------
float readMoisture() {
const int samples = 3;
long sum = 0;
for (int i = 0; i < samples; i++) {
sum += analogRead(PIN_MOISTURE);
delay(10);
}
int avg = sum / samples;
// 反向线性映射:干=0%,湿=100%
float percent = map(avg, DRY_AIR, WET_WATER, 0, 100);
if (percent < 0) percent = 0;
if (percent > 100) percent = 100;
return percent;
}
// ---------- OLED 显示 ----------
void displayData(float moisture, float temp) {
u8g2.clearBuffer();
u8g2.drawStr(0, 10, ("Soil: " + String(moisture, 0) + "%").c_str());
u8g2.drawStr(0, 25, ("Temp: " + String(temp, 1) + " C").c_str());
if (moisture < 30) {
u8g2.drawStr(0, 45, ">>> Water me! <<<");
} else {
}
u8g2.sendBuffer();
}
// ---------- 连接 Wi-Fi(带超时) ----------
bool connectWiFi() {
WiFi.begin(ssid, password);
int retries = 0;
while (WiFi.status() != WL_CONNECTED && retries < 20) {
delay(500);
retries++;
}
return (WiFi.status() == WL_CONNECTED);
}
代码说明:
- 未使用 RTOS,所有任务在 setup 中顺序执行,结束时进入深度睡眠,醒来后芯片复位重新运行 setup,结构简单可靠。
- ADC 反向映射为 0~100% 湿度,实际阈值需在烧录后首次调试标定。
- MQTT 采用简单 JSON 字符串拼接,避免引入 ArduinoJson 库,减小固件体积。
调试步骤
- 供电检查:未焊接探头前,仅给系统供电,测量 PMOS 输出 V_PERI 是否为稳定的 3.3 V,按下按钮能正常唤醒。
- 传感器校准:将湿度探头完全浸入水中,读取串口打印的 ADC 值(约 1400~1600);擦拭后放置干燥纸巾中,读取空气值(约 3000~3300)。将这两个值替换到 DRY_AIR 和 WET_WATER 宏定义里。
- OLED 测试:先单独测试 SSD1306 示例程序,确认 I²C 地址为 0x3C,显示正常无残缺。
- Wi-Fi 与 MQTT 验证:设置正确的 SSID 和密码,在电脑上运行 MQTT 客户端订阅
planter/data,观察串口是否打印连接成功,消息是否送达。 - 低功耗验证:在 PMOS 关闭前,用万用表 μA 档串联在电池总回路,观察 deep sleep 电流,应小于 2 mA。若偏高,检查 IO 状态和上拉电阻。
- 联调:将探头插入花盆正常浇水后的土壤中,静置 1 小时后观察湿度百分比是否在经验“湿润”区间(如 60%~85%),待土壤自然干燥至 30% 以下时验证 LED 和蜂鸣器动作。
设计图
阳台盆栽土壤湿度守护结构flow
1
感知输入
→
2
控制处理
→
3
执行反馈
→
4
数据上报