Cảm biến độ ẩm đất điện dung v1.2: khác loại điện trở, lỗi hay gặp, hiệu chuẩn bằng mẫu đất
Cảm biến độ ẩm đất điện dung v1.2: so với loại điện trở LM393, lỗi phần cứng hay gặp, vì sao hiệu chuẩn không khí và nước cho số sai, hiệu chuẩn bằng mẫu đất; code ESP32 bảng nhiều điểm.
Cảm biến độ ẩm đất điện dung (capacitive soil moisture sensor, bản v1.2 màu đen phổ biến) thay cho loại hai que kim loại vì không bị ăn mòn. Nhưng nhiều người dùng xong vẫn thắc mắc: cảm biến báo 60 % mà đất nhìn khá khô, hay số đọc không đổi khi tưới. Bài này giải thích cảm biến điện dung khác loại điện trở (module LM393) thế nào, các lỗi phần cứng hay gặp của bản v1.2, vì sao cách hiệu chuẩn "để ngoài không khí là 0 %, nhúng nước là 100 %" không cho ra lượng nước thật trong đất, và cách hiệu chuẩn bằng mẫu đất theo phương pháp cân. Có chương trình ESP32 dùng bảng hiệu chuẩn nhiều điểm lưu trong flash và script Python so sánh hai cách hiệu chuẩn.
1. Cảm biến điện dung và điện trở khác nhau thế nào

- Loại điện dung: hai dải đồng trong bo mạch, phủ kín bằng lớp sơn, tạo thành một tụ điện. Nước có hằng số điện môi khoảng 80, đất khô chỉ vài đơn vị, nên đất càng ướt điện dung càng lớn. Mạch dao động và mạch lọc trên bo đổi điện dung thành điện áp: đất ướt cho điện áp thấp, đất khô cho điện áp cao.
- Loại điện trở: dòng điện một chiều chạy qua đất giữa hai que. Que bị điện phân, ăn mòn sau vài tuần cắm liên tục; số đọc còn phụ thuộc mạnh vào lượng phân bón (muối) trong đất. Module LM393 có thêm ngõ ra số DO theo ngưỡng chỉnh bằng biến trở.
Nguyên lý các loại cảm biến độ ẩm có ở bài cảm biến độ ẩm: điện dung, điện trở.
2. Lỗi phần cứng hay gặp của bản v1.2

Cảm biến v1.2 được nhiều xưởng sản xuất với linh kiện khác nhau. Bản dùng chip 555 loại CMOS chạy được ở 3,3 V; bản dùng chip 555 lưỡng cực (NE555) cần khoảng 4,5 V trở lên. Một số bản nối thiếu điện trở xả của mạch lọc, điện áp ra không giảm kịp khi khô lại. Cách thử nhanh: cấp nguồn bằng đúng điện áp sẽ dùng, đo AOUT khi để ngoài không khí (khoảng 2,5–2,8 V) và khi nhúng vào cốc nước tới vạch (khoảng 1,1–1,4 V); rút ra khỏi nước, điện áp phải tăng lại trong vài giây.
3. Vì sao hiệu chuẩn "không khí và nước" cho số sai
Cách thường thấy: ghi điện áp khi để ngoài không khí làm 0 %, khi nhúng nước làm 100 %, rồi đổi tuyến tính. Số ra là một chỉ số tương đối, không phải lượng nước trong đất, vì hai lý do: đất khô không phải không khí (đất có điện môi riêng), và quan hệ điện áp với lượng nước là đường cong, thay đổi nhanh khi đất khô và chậm dần khi đất ướt. Đại lượng nông nghiệp dùng là độ ẩm thể tích θv: phần trăm thể tích nước trong một thể tích đất. Đất vườn thường từ vài phần trăm (khô) tới 40–50 % (bão hòa), không bao giờ 100 %.
# PYWORLD.VN - Hiệu chuẩn cảm biến độ ẩm đất điện dung bằng mẫu đất: độ ẩm thể tích (θv) tính từ cân mẫu, so với điện áp cảm biến
# PYWORLD.VN - So sánh cách đổi "không khí = 0 %, nước = 100 %" với bảng hiệu chuẩn nhiều điểm (nội suy tuyến tính từng đoạn)
# PYWORLD.VN - Số liệu mẫu là giả lập theo dạng đường cong thường gặp; thay bằng số đo với đất của mình
"""θv = (khối lượng ướt − khối lượng khô) / thể tích mẫu (nước 1 g/cm³), nhân 100 ra %."""
from __future__ import annotations
V_KHONG_KHI, V_NUOC = 2650, 1180 # mV đo được khi để ngoài không khí và nhúng vào cốc nước
def mv_that(theta: float) -> float:
"""Dạng đường cong giả lập của cảm biến trong đất: điện áp giảm nhanh lúc đất khô, chậm dần khi đất ướt."""
return 1180 + 1290 / (1 + theta / 9.0)
# Mẫu hiệu chuẩn: (khối lượng khô g, khối lượng ướt g, thể tích mẫu cm³); số đo mV lấy từ mv_that() thay cho đo thật
MAU = [(130.0, 130.0, 100), (130.0, 136.5, 100), (130.0, 143.0, 100), (130.0, 152.0, 100), (130.0, 161.0, 100),
(130.0, 170.0, 100), (130.0, 177.0, 100)]
def theta_tu_can(kho: float, uot: float, the_tich: float) -> float:
return (uot - kho) / the_tich * 100
def bang_hieu_chuan() -> list[tuple[float, float]]:
"""(mV, θv %) sắp theo mV tăng dần để nội suy."""
return sorted((round(mv_that(theta_tu_can(*m))), theta_tu_can(*m)) for m in MAU)
def noi_suy(bang: list[tuple[float, float]], mv: float) -> float:
if mv <= bang[0][0]:
return bang[0][1]
if mv >= bang[-1][0]:
return bang[-1][1]
for (x0, y0), (x1, y1) in zip(bang, bang[1:]):
if x0 <= mv <= x1:
return y0 + (mv - x0) / (x1 - x0) * (y1 - y0)
return bang[-1][1]
def hai_diem(mv: float) -> float:
return (V_KHONG_KHI - mv) / (V_KHONG_KHI - V_NUOC) * 100
def main() -> None:
bang = bang_hieu_chuan()
print("Bang hieu chuan (mV -> theta_v %):", ", ".join(f"{mv:.0f}->{t:.1f}" for mv, t in bang))
print(" theta that mV 2 diem (khong khi/nuoc) bang nhieu diem")
for theta in (8, 15, 25, 35, 45):
mv = mv_that(theta)
print(f" {theta:5.1f} % {mv:5.0f} {hai_diem(mv):7.1f} % {noi_suy(bang, mv):6.1f} %")
if __name__ == "__main__":
main()

Với đường cong giả lập trong script, đất có 8 % nước thì cách 2 điểm báo khoảng 54 %, bảng 7 điểm báo 8,4 %. Dùng chỉ số tương đối vẫn tưới được (chọn ngưỡng bằng cách quan sát cây), nhưng không so sánh được giữa hai loại đất, và không đối chiếu được với số liệu khuyến cáo cho cây trồng.
4. Hiệu chuẩn bằng mẫu đất

Cần một cân nhà bếp (0,1 g hoặc 1 g), một cốc nhựa có thể tích biết trước, đất lấy đúng nơi sẽ trồng. Mỗi lần thêm nước thì trộn đều, nén đất vào cốc chặt như ngoài vườn, cắm cảm biến tới vạch, đo, rồi gõ lệnh vào chương trình ở mục 5. Năm đến bảy điểm từ khô tới sũng là đủ; chương trình nội suy tuyến tính giữa các điểm.
5. Chương trình ESP32 dùng bảng hiệu chuẩn
Nối AOUT vào GPIO34, VCC vào GPIO25, GND chung với ESP32 DevKit V1. ESP32 chỉ cấp nguồn cho cảm biến lúc đo (cảm biến ăn khoảng 5 mA), đỡ tốn pin và tránh làm ấm đất quanh cảm biến.
// PYWORLD.VN - Cảm biến độ ẩm đất điện dung v1.2 với ESP32: đổi mV ra độ ẩm thể tích theo bảng hiệu chuẩn nhiều điểm (nội suy)
// PYWORLD.VN - Lệnh Serial "c 25" ghi mV hiện tại ứng với 25 % (đo bằng cân mẫu đất); "x" xóa bảng; bảng lưu trong flash
// PYWORLD.VN - AOUT -> GPIO34; VCC lấy từ GPIO25 (chỉ cấp khi đo, cảm biến ăn khoảng 5 mA), GND chung
#include <Preferences.h>
const int CHAN_AO = 34, CHAN_NGUON = 25;
const int TOI_DA_DIEM = 10;
Preferences luu;
int soDiem = 0;
float bangMv[TOI_DA_DIEM], bangPt[TOI_DA_DIEM]; // sắp theo mV tăng dần
float docMv() {
digitalWrite(CHAN_NGUON, HIGH);
delay(150); // chờ mạch dao động và lọc trên cảm biến ổn định
uint32_t tong = 0;
for (int i = 0; i < 32; i++) tong += analogReadMilliVolts(CHAN_AO);
digitalWrite(CHAN_NGUON, LOW);
return tong / 32.0f;
}
float doiRaPhanTram(float mv) {
if (soDiem < 2) return NAN; // chưa đủ hai điểm hiệu chuẩn
if (mv <= bangMv[0]) return bangPt[0];
if (mv >= bangMv[soDiem - 1]) return bangPt[soDiem - 1];
for (int i = 1; i < soDiem; i++) {
if (mv <= bangMv[i]) {
float t = (mv - bangMv[i - 1]) / (bangMv[i] - bangMv[i - 1]);
return bangPt[i - 1] + t * (bangPt[i] - bangPt[i - 1]);
}
}
return NAN;
}
void luuBang() {
luu.putInt("n", soDiem);
luu.putBytes("mv", bangMv, sizeof(bangMv));
luu.putBytes("pt", bangPt, sizeof(bangPt));
}
void themDiem(float mv, float pt) {
if (soDiem >= TOI_DA_DIEM) return;
int i = soDiem++;
while (i > 0 && bangMv[i - 1] > mv) { // chèn giữ thứ tự mV tăng dần
bangMv[i] = bangMv[i - 1];
bangPt[i] = bangPt[i - 1];
i--;
}
bangMv[i] = mv;
bangPt[i] = pt;
luuBang();
Serial.printf("Them diem %.0f mV = %.1f %%, bang co %d diem\n", mv, pt, soDiem);
}
void setup() {
Serial.begin(115200);
pinMode(CHAN_NGUON, OUTPUT);
digitalWrite(CHAN_NGUON, LOW);
analogSetPinAttenuation(CHAN_AO, ADC_11db); // thang đo tới khoảng 3,1 V
luu.begin("do_am_dat", false);
soDiem = luu.getInt("n", 0);
luu.getBytes("mv", bangMv, sizeof(bangMv));
luu.getBytes("pt", bangPt, sizeof(bangPt));
Serial.printf("Bang hieu chuan: %d diem. Lenh: c <phan tram>, x\n", soDiem);
}
void loop() {
if (Serial.available()) {
String lenh = Serial.readStringUntil('\n');
lenh.trim();
if (lenh.startsWith("c ")) themDiem(docMv(), lenh.substring(2).toFloat());
else if (lenh == "x") {
soDiem = 0;
luuBang();
Serial.println("Da xoa bang");
}
}
static uint32_t lan = 0;
if (millis() - lan < 5000) return;
lan = millis();
float mv = docMv();
float pt = doiRaPhanTram(mv);
if (isnan(pt)) Serial.printf("%.0f mV (chua hieu chuan)\n", mv);
else Serial.printf("%.0f mV -> do am the tich %.1f %%\n", mv, pt);
}
- Gõ
c 0khi cảm biến trong đất khô,c 12.5khi đất có 12,5 % nước…; bảng tự sắp theo mV và lưu vào flash, mất điện không mất. - Gõ
xđể xóa bảng, làm lại từ đầu. - Chưa đủ hai điểm thì chương trình chỉ in mV.
- Bảng hiệu chuẩn chỉ đúng cho loại đất đã dùng để hiệu chuẩn; đổi đất, đổi giá thể thì hiệu chuẩn lại.
6. Lắp đặt ngoài vườn
- Phủ keo epoxy hoặc keo nến lên mép bo và phần linh kiện phía trên vạch; mép bo cắt hở là chỗ nước thấm vào lớp mạch.
- Cắm thẳng đứng, ép đất sát hai mặt cảm biến; khe hở không khí làm số đọc thấp hẳn.
- Đặt ở độ sâu vùng rễ, cách gốc cây và vòi tưới một khoảng để không đo đúng chỗ nước vừa chảy xuống.
- Bón phân, tưới nước có muối làm số đọc lệch; định kỳ đối chiếu bằng cách lấy mẫu đất cân.
Dùng cảm biến để tự tưới có ở bài tưới cây tự động với ESP32 và nhà kính mini với ESP32.
7. Lỗi hay gặp
| Hiện tượng | Nguyên nhân | Cách xử lý |
|---|---|---|
| Số đọc không đổi khi tưới | Chip 555 không chạy ở 3,3 V, cảm biến hỏng | Thử cấp 5 V (AOUT vẫn dưới 3,3 V), đổi cảm biến |
| Rút khỏi nước mà số giảm rất chậm | Thiếu điện trở xả mạch lọc | Kiểm tra mạch, chọn bản khác |
| Số trôi dần sau vài tuần | Nước thấm vào mép bo | Phủ keo, thay cảm biến |
| Hai cảm biến cùng loại báo khác nhau | Linh kiện khác nhau giữa các bản | Hiệu chuẩn riêng từng cái |
| Số % lớn bất thường | Dùng cách đổi 2 điểm không khí, nước | Hiệu chuẩn bằng mẫu đất |
Bài cùng cụm chủ đề: tưới vườn nhiều vùng bằng van điện từ; bơm và van điện từ điều khiển bằng MOSFET; hiệu chuẩn cảm biến bằng hồi quy.
Xem thêm