Tưới cây tự động với ESP32: cảm biến độ ẩm đất, relay và bơm mini, ngưỡng có trễ
Làm hệ thống tưới cây tự động với ESP32: cảm biến độ ẩm đất điện dung, hiệu chỉnh khô ướt, relay điều khiển bơm mini, ngưỡng có trễ, giới hạn thời gian bơm và thí nghiệm STEM.
Hệ thống tưới cây tự động là đề tài STEM gần gũi với học sinh Việt Nam: chậu rau trên ban công, vườn trường, mô hình nông nghiệp thông minh trong các cuộc thi khoa học kỹ thuật. ESP32 đọc cảm biến độ ẩm đất; khi đất khô thì bật relay cho bơm mini tưới, đủ ẩm thì tắt.
Bài viết hướng dẫn chọn cảm biến, hiệu chỉnh mức khô và ướt, nối dây qua relay, chương trình có ngưỡng trễ, giới hạn thời gian bơm, thời gian chờ nước thấm, và gợi ý thí nghiệm STEM.
1. Cảm biến độ ẩm đất kiểu điện dung
- Cảm biến loại điện dung (thường ghi "Capacitive Soil Moisture Sensor v1.2") đo theo điện dung của đất quanh hai bản cực đã được phủ kín. Đất càng ẩm, điện dung càng lớn, điện áp ra ở chân AOUT càng thấp.
- Loại hai chân kim loại trần (kiểu điện trở) rẻ hơn nhưng có dòng điện chạy trong đất nên bị ăn mòn sau vài tuần. Loại điện dung bền hơn nhiều.
- Cấp cho cảm biến 3,3 V từ ESP32 để điện áp ra không vượt quá 3,3 V.
- Giá trị đọc được phụ thuộc loại đất, độ nén và từng cảm biến, nên phải hiệu chỉnh: đo một lần khi đất khô, một lần khi nhúng vào nước.

2. Linh kiện cần chuẩn bị
- ESP32 DevKit V1 30 chân.
- Cảm biến độ ẩm đất kiểu điện dung.
- Module relay 1 kênh 5 V.
- Bơm chìm mini 3–6 V, ống dẫn nước, cốc hoặc chai làm bể chứa.
- Nguồn 5 V từ 1 A: cốc sạc điện thoại, hoặc khay pin 18650 qua mạch hạ áp LM2596.
- Dây cắm test board.
3. Sơ đồ nối dây

- Bơm chạy bằng nguồn 5 V qua tiếp điểm COM–NO của relay. ESP32 chỉ điều khiển cuộn relay, không cấp dòng cho bơm.
- Module relay có loại kích mức thấp và loại kích mức cao. Đặt hằng số
RELAY_ACTIVE_LOWcho đúng, như trong bài ESP32 điều khiển relay. - Bơm là tải cảm. Hàn thêm một diode 1N4007 song song ngược với bơm (vạch của diode về phía cực +) để giảm xung điện áp khi ngắt.
- Đặt mạch điện cao hơn chậu và bể nước, che kín khỏi nước. Phần cảm biến trên vạch giới hạn và mạch điện của cảm biến không được ngập nước.
4. Chương trình
/*
PYWORLD.VN - Tưới cây tự động: cảm biến độ ẩm đất điện dung + relay + bơm mini (ESP32)
Cảm biến: AOUT -> GPIO34, VCC -> 3V3, GND. Relay 1 kênh: IN -> GPIO26, VCC 5 V, GND. Bơm 5 V qua tiếp điểm COM-NO
Lệnh (115200, New Line): kho = lưu mức khô (cắm cảm biến vào đất khô), uot = lưu mức ướt (nhúng vào nước), n = tưới ngay
*/
#include <Preferences.h>
#define CAM_BIEN_PIN 34
#define RELAY_PIN 26
// true: module kích mức thấp (IN = LOW thì relay đóng); false: module kích mức cao
const bool RELAY_ACTIVE_LOW = true;
const int NGUONG_BAT = 35; // % độ ẩm: thấp hơn thì bắt đầu tưới
const int NGUONG_TAT = 60; // % độ ẩm: đạt mức này thì ngừng tưới
const unsigned long BOM_TOI_DA_MS = 10000; // mỗi lần tưới không quá 10 giây
const unsigned long CHO_THAM_MS = 60000; // sau mỗi lần tưới chờ 60 giây cho nước thấm
int kho = 2800, uot = 1300; // giá trị ADC khi khô và khi ướt, sửa bằng lệnh kho, uot
bool dangBom = false;
unsigned long tBatBom = 0, tTatBom = 0;
Preferences boNho;
void bom(bool bat) {
bool muc = RELAY_ACTIVE_LOW ? !bat : bat;
digitalWrite(RELAY_PIN, muc ? HIGH : LOW);
if (bat && !dangBom) tBatBom = millis();
if (!bat && dangBom) tTatBom = millis();
dangBom = bat;
}
int docTho() {
long tong = 0;
for (int i = 0; i < 16; i++) { tong += analogRead(CAM_BIEN_PIN); delay(2); }
return tong / 16;
}
// Cảm biến điện dung: đất càng khô, giá trị ADC càng lớn
int doAm(int raw) {
if (kho == uot) return 0;
return constrain((long)(kho - raw) * 100 / (kho - uot), 0, 100);
}
void xuLyLenh() {
String s = Serial.readStringUntil('\n');
s.trim();
s.toLowerCase();
if (s == "kho") { kho = docTho(); boNho.putInt("kho", kho); Serial.printf("Da luu muc kho = %d\n", kho); }
else if (s == "uot") { uot = docTho(); boNho.putInt("uot", uot); Serial.printf("Da luu muc uot = %d\n", uot); }
else if (s == "n") { bom(true); Serial.println("Tuoi ngay"); }
}
void setup() {
Serial.begin(115200);
pinMode(RELAY_PIN, OUTPUT);
bom(false);
tTatBom = millis() - CHO_THAM_MS; // cho phép tưới ngay khi vừa bật nguồn
boNho.begin("tuoicay", false);
kho = boNho.getInt("kho", kho);
uot = boNho.getInt("uot", uot);
Serial.printf("Muc kho %d, muc uot %d. Lenh: kho, uot, n\n", kho, uot);
}
void loop() {
if (Serial.available()) xuLyLenh();
static unsigned long tDoTruoc = 0;
if (millis() - tDoTruoc < 2000 && !dangBom) return; // khi đang bơm thì đo liên tục
tDoTruoc = millis();
int raw = docTho();
int pt = doAm(raw);
if (dangBom) {
if (pt >= NGUONG_TAT || millis() - tBatBom >= BOM_TOI_DA_MS) {
bom(false);
Serial.printf("Do am %d %% -> tat bom, cho %lu giay cho nuoc tham\n", pt, CHO_THAM_MS / 1000);
}
} else if (pt < NGUONG_BAT && millis() - tTatBom >= CHO_THAM_MS) {
bom(true);
Serial.printf("Do am %d %% (raw %d) -> bat bom\n", pt, raw);
} else {
Serial.printf("Do am %d %% (raw %d)\n", pt, raw);
}
}
Hiệu chỉnh lần đầu (Serial Monitor, 115200 baud, kiểu xuống dòng "New Line"):
- Cắm cảm biến vào đất đã khô, gõ
kho. - Nhúng phần cảm biến (dưới vạch giới hạn) vào cốc nước, gõ
uot. - Cắm lại cảm biến vào chậu. Hai giá trị được lưu vào flash, lần sau bật nguồn không phải làm lại.
Gõ n để bơm thử ngay một lần.
5. Giải thích cách điều khiển

- Ngưỡng có khoảng trễ: dưới 35 % thì bật bơm, đạt 60 % mới tắt. Nếu bật và tắt cùng một ngưỡng, bơm sẽ bật tắt liên tục khi độ ẩm dao động quanh ngưỡng đó.
- Giới hạn 10 giây mỗi lần bơm: nếu cảm biến tuột khỏi chậu hay hỏng, bơm cũng không chạy mãi làm ngập chậu.
- Chờ 60 giây: nước cần thời gian thấm xuống tới chỗ cảm biến. Không chờ thì chương trình tưới tiếp khi đất ở dưới còn chưa kịp ẩm.
- Đọc 16 lần lấy trung bình để số đo ổn định. Mỗi 2 giây đo một lần; khi đang bơm thì đo liên tục để tắt đúng lúc.
6. Gợi ý dùng trong giờ học STEM
Mục tiêu: học sinh biết dùng cảm biến để ra quyết định, hiểu vì sao cần hiệu chỉnh và khoảng trễ, và gắn kiến thức điện tử với sinh học, nông nghiệp.
- Hiệu chỉnh nhiều loại đất. Đo giá trị khô và ướt với đất thịt, đất cát, đất trộn xơ dừa. Lập bảng và nhận xét: vì sao mỗi loại đất cần hiệu chỉnh riêng?
- Đo tốc độ khô của đất. Ghi độ ẩm mỗi 30 phút trong một ngày, ở trong nhà và ngoài nắng. Vẽ đồ thị, tìm thời điểm đất khô nhanh nhất.
- Chọn ngưỡng theo loại cây. Tìm hiểu cây xương rồng, rau muống, cây cảnh cần đất ẩm bao nhiêu, rồi chỉnh
NGUONG_BAT,NGUONG_TATcho phù hợp. - Tiết kiệm nước. Đo lượng nước bơm ra mỗi giây (hứng vào cốc có vạch chia), tính lượng nước dùng mỗi ngày. So sánh với tưới bằng tay.
- Mở rộng IoT. Gửi độ ẩm lên điện thoại qua Wi-Fi hoặc MQTT, như các bài web server và MQTT với ESP32 trên pyworld.vn.
7. Xử lý lỗi thường gặp
| Hiện tượng | Nguyên nhân thường gặp | Cách xử lý |
|---|---|---|
| Độ ẩm luôn 0 % hoặc 100 % | Chưa hiệu chỉnh; nhầm thứ tự khô, ướt | Gõ lại kho khi đất khô, uot khi nhúng nước |
| Relay kêu nhưng bơm không chạy | Dây bơm nối COM–NC thay vì COM–NO; nguồn bơm yếu | Chuyển sang NO; kiểm tra nguồn 5 V |
| Vừa bật nguồn bơm đã chạy, lệnh ngược | Sai loại relay kích mức cao hay thấp | Đổi RELAY_ACTIVE_LOW |
| ESP32 khởi động lại khi bơm chạy | Bơm và ESP32 dùng chung nguồn yếu, nhiễu | Nguồn từ 1 A; diode song song với bơm; tụ 470 µF ở nguồn 5 V |
| Số đo nhảy nhiều | Dây cảm biến dài, gần dây bơm; đất chưa nén | Dây ngắn, tách xa dây bơm; cắm cảm biến chặt vào đất |
8. Xem thêm
- ESP32 điều khiển module relay an toàn
- Đọc ADC trên ESP32
- MQTT với ESP32: gửi dữ liệu và điều khiển relay
- Deep Sleep trên ESP32: tiết kiệm pin (cho hệ thống chạy pin ngoài vườn)
Xem thêm