Community project

Untitled Project

Hoki Huang

Published August 14, 2026

ESP32
Photo of Untitled ProjectGenerated with AI

This automatic plant watering system monitors soil moisture with a capacitive sensor and triggers a 12V water pump when the soil dries out. The ESP32 microcontroller reads moisture levels, controls the pump via a MOSFET switch, and sounds an alert buzzer when watering begins. Builders will receive a complete wiring diagram, parts list, firmware with calibration guidance, and step-by-step assembly instructions covering water line preparation, sensor and buzzer hookup, pump switching circuit, flyback diode protection, power supply verification, and initial moisture calibration.

The system uses an MP1584 buck converter to safely step down 12V to ESP32 logic levels, a logic-level MOSFET to switch the pump on demand, and a 1N4007 diode to protect against inductive kickback. Assembly follows a logical sequence from power isolation through electrical testing to final calibration, ensuring safe handling of both water and high-voltage components.

Wiring diagram

Interactive · read-only
Wiring diagram for Untitled Project

Pan and zoom to explore the wiring. Remix the project to edit it in your own workspace.

Parts list

Bill of materials
ComponentQtyNotes
Gravity: IP65 Capacitive Soil Moisture SensorSEN03081Capacitive soil moisture sensor with IP65 waterproof and corrosion-resistant construction. Compatible with Arduino, ESP32, and Raspberry Pi.
12V DC Submersible Mini Water Pump12 V1Two-wire brushed DC submersible water pump for small irrigation or circulation projects. It is an external load and must be driven through a relay, MOSFET, or motor driver; do not connect directly to an MCU GPIO.
IRLZ44N Logic-Level N-Channel MOSFETIRLZ44N1Logic-level N-channel MOSFET commonly used as a low-side switch for DC loads from 3.3V or 5V microcontroller GPIO. Use a gate resistor, a gate pulldown, and a flyback diode for inductive loads.
1N4007 Diode1N40071General-purpose silicon rectifier diode in DO-204AL (DO-41) axial through-hole package. Rated 1A average forward current and 1000V peak reverse voltage (VRRM). Forward voltage drop ~0.7V at typical load (up to 1.1V at full 1A). Used here in series with LM7805 Vout for reverse polarity protection: anode to LM7805 output, cathode to 5V rail, resulting in ~4.3V on the output rail. Not suitable for high-frequency switching (reverse recovery ~2µs–30µs); intended for DC or 50/60Hz rectification only.
Buzzer3.3 V active1Piezo buzzer for sound output
12V Barrel-Jack Adapter12 V / 2 A112 V / 2 A wall adapter with a 5.5 mm / 2.1 mm barrel jack. Used to power motor drivers, LED strips, or boards that need a higher rail.
MP1584 Buck ConverterSet to 5.0 V1Adjustable buck (step-down) DC-DC converter, 4.5-28 V in -> 0.8-20 V out, ~3 A. Configured to 5 V to step a 9 V / 12 V supply or battery pack down to the board's 5 V rail.
Resistor100 Ω1Through-hole resistor (current-limiting in series with an LED)
Resistor10 kΩ1Through-hole resistor (current-limiting in series with an LED)

Assembly

6 steps
  1. 断电并准备水路

    先不要连接 12V 适配器。将 pump_1 放入水箱,出水管固定在花盆土表面上方,避免水倒流到电子元件。

    • Tip: 先用清水短暂测试水泵和管路是否通畅。
    • Tip: 让水泵始终浸在水中再运行。
    • 电子元件、ESP32 和适配器必须放在远离漏水的位置。
    • 水泵不得干转。
  2. 连接湿度传感器和蜂鸣器

    soil_sensor_1 的 VCC 接 ESP32 3V3、GND 接 ESP32 GND、AOUT 接 GPIO34。buzzer_1 的 GND 接 GND,SIGNAL 接 GPIO25;若你的蜂鸣器模块另有 VCC 引脚,则接 ESP32 3V3。

    • Tip: 只将传感器探头部分插入土中,连接端保持干燥。
    • Tip: GPIO34 是仅输入的模拟脚,正适合传感器输出。
    • 传感器 AOUT 不可接 5V;本设计使用 3.3V 供电,确保 ESP32 模拟输入安全。
  3. 搭建水泵 MOSFET 开关

    pump_1 的 PUMP+ 接 adapter_1 的 +12V;PUMP- 接 mosfet_1 的 DRAIN;mosfet_1 的 SOURCE 接公共 GND。gate_resistor_1(100Ω)串联在 GPIO26 与 mosfet_1 的 GATE 之间。gate_pulldown_1(10kΩ)一端接 GATE、另一端接公共 GND。

    • Tip: IRLZ44N 面向你、引脚朝下时通常为 GATE、DRAIN、SOURCE;仍请以所购器件的数据手册/丝印为准。
    • Tip: 把 12V 适配器负极、MOSFET SOURCE、ESP32 GND 接到同一个公共地。
    • 绝不可把水泵接到 ESP32 GPIO 或 3.3V 引脚。
    • 确认 MOSFET 引脚顺序后才通电,接反可能损坏器件。
  4. 安装续流二极管

    flyback_diode_1 并联在 pump_1 两端:有色环的 Cathode 接泵 PUMP+ / +12V;无环的 Anode 接泵 PUMP- / MOSFET DRAIN。

    • Tip: 二极管应尽量靠近水泵接线端安装。
    • 二极管极性不能反:有色环的一端必须接 +12V。接反会造成电源短路。
  5. 连接降压供电并检查电压

    adapter_1 的 +12V 接 buck_1 VIN,负极接 buck_1 GND。先在未接 ESP32 时给适配器通电,用万用表调节 buck_1,使 VOUT 对 GND 为稳定的 5.0V;断电后再把 VOUT 接 ESP32 VIN、GND 接公共 GND。

    • Tip: ESP32 的 USB 连接也可用于上传和串口查看;不要在 USB 供电与 buck 输出接入 VIN 的情况下随意改线。
    • Tip: 确认所有地线已经相连后再接上电源。
    • MP1584 输出必须先调到 5.0V;高于 5V 可能损坏 ESP32 VIN 供电部分。
    • 不要将 12V 直接接入 ESP32 的 VIN 或 3V3。
  6. 首次上电与湿度校准

    通电后用 Schematik 的 Deploy 按钮烧录。观察串口输出的 Soil raw 值:记录探头在干土/空气中的值并替换 RAW_DRY,记录在充分湿润土壤中的值并替换 RAW_WET;保持 RAW_DRY 大于 RAW_WET。

    • Tip: 程序默认每 60 秒采样一次;湿度低于 20% 时蜂鸣 3 秒并让水泵运行 5 秒。
    • Tip: 每次浇水后程序至少等待 5 分钟才允许下一次启动水泵,避免过度浇水。
    • 首次测试时把出水管对准容器而非花盆,先确认水泵工作时间和流量合适。

Pin assignments

Board wiring reference
PinConnectionType
3V3soil_sensor_1 VCCpower
GNDsoil_sensor_1 GNDground
GPIO 34soil_sensor_1 AOUTanalog
GNDbuzzer_1 GNDground
GPIO 25buzzer_1 SIGNALdigital
EXTadapter_1 +12VMP1584 Buck Converter VINpower
VINbuck_1 VOUTpower
EXTpump_1 PUMP+12V Barrel-Jack Adapter +12Vdata
EXTpump_1 PUMP-IRLZ44N Logic-Level N-Channel MOSFET DRAINdata
GNDmosfet_1 SOURCEground
EXTflyback_diode_1 Cathode12V Barrel-Jack Adapter +12Vpower
EXTflyback_diode_1 AnodeIRLZ44N Logic-Level N-Channel MOSFET DRAINpower
GPIO 26gate_resistor_1 P1digital
EXTgate_resistor_1 P2IRLZ44N Logic-Level N-Channel MOSFET GATEdigital
EXTgate_pulldown_1 P1IRLZ44N Logic-Level N-Channel MOSFET GATEdigital
GNDgate_pulldown_1 P2ground
GNDadapter_1 GNDground
GNDbuck_1 GNDground

Firmware

ESP32
main.cppDeploy to device
#include <Arduino.h>


// Forward declarations
int readSoilRaw();
int rawToPercent(int raw);
void startAlert(unsigned long now);
void stopAlert();
void startPump(unsigned long now);
void stopPump(unsigned long now);

constexpr int SOIL_PIN = 34;
constexpr int BUZZER_PIN = 25;
constexpr int PUMP_PIN = 26;

// Calibrate these two readings for your own soil and sensor.
// RAW_DRY: probe in air/dry soil; RAW_WET: probe in fully wet soil.
constexpr int RAW_DRY = 3000;
constexpr int RAW_WET = 1500;
constexpr int DRY_THRESHOLD_PERCENT = 20;
constexpr unsigned long SAMPLE_INTERVAL_MS = 60000UL;
constexpr unsigned long PUMP_DURATION_MS = 5000UL;
constexpr unsigned long PUMP_COOLDOWN_MS = 300000UL;
constexpr unsigned long BUZZER_TOGGLE_MS = 150UL;
constexpr unsigned long ALERT_DURATION_MS = 3000UL;

unsigned long lastSampleMs = 0;
unsigned long pumpStartedMs = 0;
unsigned long lastPumpStoppedMs = 0;
unsigned long alertStartedMs = 0;
unsigned long lastBuzzerToggleMs = 0;
bool pumpRunning = false;
bool alertActive = false;
bool buzzerOn = false;

int readSoilRaw() {
  long total = 0;
  for (int i = 0; i < 16; ++i) {
    total += analogRead(SOIL_PIN);
    delay(5);
  }
  return total / 16;
}

int rawToPercent(int raw) {
  const long numerator = static_cast<long>(RAW_DRY - raw) * 100L;
  const long denominator = RAW_DRY - RAW_WET;
  if (denominator == 0) return 0;
  return constrain(static_cast<int>(numerator / denominator), 0, 100);
}

void startAlert(unsigned long now) {
  alertActive = true;
  alertStartedMs = now;
  lastBuzzerToggleMs = now;
  buzzerOn = true;
  digitalWrite(BUZZER_PIN, HIGH);
}

void stopAlert() {
  alertActive = false;
  buzzerOn = false;
  digitalWrite(BUZZER_PIN, LOW);
}

void startPump(unsigned long now) {
  pumpRunning = true;
  pumpStartedMs = now;
  digitalWrite(PUMP_PIN, HIGH);
  Serial.println("Pump ON");
}

void stopPump(unsigned long now) {
  pumpRunning = false;
  lastPumpStoppedMs = now;
  digitalWrite(PUMP_PIN, LOW);
  Serial.println("Pump OFF");
}

void setup() {
  Serial.begin(115200);
  analogReadResolution(12);
  pinMode(BUZZER_PIN, OUTPUT);
  pinMode(PUMP_PIN, OUTPUT);
  digitalWrite(BUZZER_PIN, LOW);
  digitalWrite(PUMP_PIN, LOW);
  delay(200);
  lastSampleMs = millis() - SAMPLE_INTERVAL_MS;
  Serial.println("Soil watering controller ready");
}

void loop() {
  const unsigned long now = millis();

  if (pumpRunning && now - pumpStartedMs >= PUMP_DURATION_MS) {
    stopPump(now);
  }

  if (alertActive) {
    if (now - alertStartedMs >= ALERT_DURATION_MS) {
      stopAlert();
    } else if (now - lastBuzzerToggleMs >= BUZZER_TOGGLE_MS) {
      lastBuzzerToggleMs = now;
      buzzerOn = !buzzerOn;
      digitalWrite(BUZZER_PIN, buzzerOn ? HIGH : LOW);
    }
  }

  if (now - lastSampleMs < SAMPLE_INTERVAL_MS) return;
  lastSampleMs = now;

  const int raw = readSoilRaw();
  const int moisture = rawToPercent(raw);
  Serial.printf("Soil raw=%d, moisture=%d%%\n", raw, moisture);

  if (moisture < DRY_THRESHOLD_PERCENT) {
    Serial.println("Soil is below 20%: watering request");
    startAlert(now);
    if (!pumpRunning && now - lastPumpStoppedMs >= PUMP_COOLDOWN_MS) {
      startPump(now);
    } else {
      Serial.println("Pump cooldown active; warning only");
    }
  }
}

“Deploy to device” opens this project in Schematik, where you can flash it to your board over USB.

Remix this project

Make it yours in one click

Open a full copy of this project in your own Schematik workspace — diagram, code, parts, and assembly steps included. Swap the sensor, add features, or redesign the whole thing with AI. The author's original stays untouched.

Open in Schematik