คำนวณ VPD ด้วย ESP32 แบบไม่ต้องพึ่ง Cloud

ความชื้นสัมพัทธ์ 70% ตอนเช้ากับตอนบ่าย ไม่ได้แปลว่าพืชรู้สึกเหมือนกัน บทความนี้อธิบายค่า VPD สูตรที่ใช้คำนวณ และวิธีให้ ESP32 ตัดสินใจเปิดพ่นหมอกเองโดยไม่ต้องรออินเทอร์เน็ต

ช่วงแรกที่ทำระบบพ่นหมอกในโรงเรือน ผมตั้งเงื่อนไขง่าย ๆ ว่า “ถ้าความชื้นต่ำกว่า 60% ให้พ่น” ฟังดูสมเหตุสมผล แต่ใช้จริงแล้วไม่ค่อยตรงกับอาการของพืช บางวันพ่นถี่จนใบเปียกตลอด บางวันอากาศร้อนจัดแต่ระบบกลับไม่ทำงาน เพราะตัวเลขความชื้นยังไม่ถึงเกณฑ์

ปัญหาคือ ความชื้นสัมพัทธ์ (RH) เป็นค่าเปรียบเทียบ มันบอกว่าอากาศอุ้มน้ำไว้กี่เปอร์เซ็นต์ของที่อุ้มได้สูงสุด แต่ “ที่อุ้มได้สูงสุด” เปลี่ยนไปตามอุณหภูมิ อากาศ 35°C อุ้มน้ำได้มากกว่าอากาศ 25°C เกือบเท่าตัว RH เท่ากันจึงไม่ได้แปลว่าพืชคายน้ำเท่ากัน

VPD คืออะไร

VPD ย่อมาจาก Vapor Pressure Deficit หรือ “ความขาดไอน้ำของอากาศ” คิดง่าย ๆ ว่าเป็นค่า ความกระหายน้ำของอากาศ ยิ่ง VPD สูง อากาศยิ่งดึงน้ำออกจากใบเร็ว พืชต้องคายน้ำมากและอาจปิดปากใบเพื่อป้องกันตัว ส่วน VPD ต่ำเกินไป อากาศแทบไม่ดึงน้ำ พืชดูดธาตุอาหารได้น้อยลง และเชื้อราชอบสภาพแบบนี้

หน่วยที่ใช้คือ kPa (กิโลปาสกาล) และต้องใช้แค่สองค่าที่เซนเซอร์ราคาไม่แพงวัดได้อยู่แล้ว คืออุณหภูมิกับความชื้นสัมพัทธ์

สูตรที่ใช้

ขั้นแรกหาความดันไอน้ำอิ่มตัว (SVP) จากอุณหภูมิด้วยสมการ Tetens จากนั้นหักส่วนที่อากาศมีไอน้ำอยู่แล้วออก

SVP = 0.6108 × e(17.27 × T) ÷ (T + 237.3)
VPD = SVP × (1 − RH ÷ 100)
T = อุณหภูมิอากาศ (°C) · RH = ความชื้นสัมพัทธ์ (%) · ผลลัพธ์เป็น kPa

ลองแทนค่าดูสามสถานการณ์ จะเห็นว่า RH อย่างเดียวหลอกตาแค่ไหน

สถานการณ์T (°C)RH (%)SVP (kPa)VPD (kPa)
เช้ามืด ชื้น25803.170.63
สาย อากาศอุ่น30704.241.27
บ่าย แดดจัด35505.622.81

แถวที่สองกับแถวที่สาม RH ต่างกันแค่ 20 จุด แต่ VPD ต่างกันมากกว่าสองเท่า ถ้าตั้งเกณฑ์ด้วย RH อย่างเดียว ระบบจะมองไม่เห็นความต่างนี้

ช่วงที่เหมาะสม

ตัวเลขด้านล่างเป็นช่วงที่ใช้กันทั่วไปสำหรับผักและพืชในโรงเรือน ใช้เป็นจุดเริ่มต้น แล้วปรับตามชนิดพืชและสิ่งที่เห็นจริงในแปลง

ระยะของพืชVPD (kPa)สังเกตได้จาก
ต่ำเกินไป< 0.4ใบชื้นนาน เสี่ยงเชื้อรา โตช้า
ต้นกล้า / ปักชำ0.4 – 0.8ต้นอ่อนยังไม่มีรากมาก ต้องการอากาศชื้น
ช่วงเจริญเติบโต0.8 – 1.2ใบเต่ง สีสม่ำเสมอ
ออกดอก / ติดผล1.2 – 1.6คายน้ำดี ดึงธาตุอาหารได้ดี
สูงเกินไป> 1.6ใบเหี่ยวตอนบ่าย ขอบใบไหม้

อุปกรณ์ที่ใช้

  • ESP32 DevKit หนึ่งบอร์ด รุ่นไหนก็ได้ที่มี Wi-Fi
  • เซนเซอร์ SHT31 (I²C) วัดอุณหภูมิและความชื้นได้แม่นกว่า DHT22 และมีฮีตเตอร์ในตัวไว้ไล่ความชื้นที่เกาะเซนเซอร์
  • โมดูลรีเลย์ 1 ช่อง แบบมี optocoupler สำหรับสั่งปั๊มพ่นหมอก
  • กล่องกันแดดสำหรับเซนเซอร์ ทำเองจากจานรองกระถางสีขาวซ้อนกันได้
SHT31ESP32รีเลย์ESP32
VIN3V3VCC5V (VIN)
GNDGNDGNDGND
SDAGPIO 21INGPIO 26
SCLGPIO 22

ฝั่งไฟ 220V ของปั๊มให้ต่อผ่านหน้าสัมผัสรีเลย์ และควรมีเบรกเกอร์กันดูดคั่นไว้เสมอ โรงเรือนมีทั้งน้ำและความชื้น อย่าประหยัดตรงนี้

โค้ด

หัวใจของโค้ดมีสองส่วน คือฟังก์ชันคำนวณ VPD กับการตัดสินใจเปิด–ปิดแบบมี hysteresis เปิดเมื่อ VPD เกินเกณฑ์บน และปิดเมื่อลงมาต่ำกว่าเกณฑ์ล่าง รีเลย์จะได้ไม่ตัดต่อถี่ ๆ ตอนค่าอยู่ใกล้เส้น

vpd_fogger.ino
#include <Wire.h>
#include <Adafruit_SHT31.h>

const int   RELAY_PIN   = 26;
const float VPD_ON      = 1.40;   // เปิดพ่นหมอกเมื่อ VPD สูงกว่านี้ (kPa)
const float VPD_OFF     = 1.10;   // ปิดเมื่อ VPD ลดลงต่ำกว่านี้
const unsigned long MIN_ON_MS = 20UL * 1000;  // พ่นอย่างน้อย 20 วินาที
const unsigned long MAX_ON_MS = 3UL * 60 * 1000; // ไม่เกิน 3 นาทีต่อรอบ
const unsigned long REST_MS   = 2UL * 60 * 1000; // พักอย่างน้อย 2 นาที

Adafruit_SHT31 sht31;
bool fogging = false;
unsigned long changedAt = 0;

float calcVPD(float t, float rh) {
  float svp = 0.6108 * exp((17.27 * t) / (t + 237.3));
  return svp * (1.0 - rh / 100.0);
}

void setFog(bool on) {
  fogging = on;
  changedAt = millis();
  digitalWrite(RELAY_PIN, on ? HIGH : LOW);
}

void setup() {
  Serial.begin(115200);
  pinMode(RELAY_PIN, OUTPUT);
  setFog(false);
  if (!sht31.begin(0x44)) {
    Serial.println("ไม่พบ SHT31 — ตรวจสาย SDA/SCL");
  }
}

void loop() {
  float t  = sht31.readTemperature();
  float rh = sht31.readHumidity();

  if (isnan(t) || isnan(rh)) {      // อ่านค่าไม่ได้ = ปิดไว้ก่อน
    if (fogging) setFog(false);
    delay(5000);
    return;
  }

  float vpd = calcVPD(t, rh);
  unsigned long elapsed = millis() - changedAt;

  if (!fogging && vpd > VPD_ON && elapsed > REST_MS) {
    setFog(true);
  } else if (fogging && elapsed > MIN_ON_MS &&
             (vpd < VPD_OFF || elapsed > MAX_ON_MS)) {
    setFog(false);
  }

  Serial.printf("T=%.1fC RH=%.0f%% VPD=%.2f kPa fog=%d\n",
                t, rh, vpd, fogging);
  delay(5000);
}

ใช้ไลบรารี Adafruit SHT31 ติดตั้งได้จาก Library Manager ใน Arduino IDE ตัวเลข VPD_ON และ VPD_OFF ตั้งไว้สำหรับผักช่วงโต ถ้าเป็นต้นกล้าให้ลดลงมาอยู่แถว 0.8 และ 0.6

ทำไมต้องคิดบนบอร์ด

ระบบแบบ “ส่งค่าขึ้น Cloud แล้วรอคำสั่งกลับมา” ทำได้และดูทันสมัย แต่ในแปลงจริง อินเทอร์เน็ตหลุดได้เสมอ ทั้งฝนตกหนัก ไฟดับ หรือซิมหมดแพ็กเกจ ถ้าการตัดสินใจอยู่บน Cloud วันที่เน็ตล่มคือวันที่ผักไม่มีใครดูแล

ผมจึงแบ่งหน้าที่ชัดเจน ESP32 ตัดสินใจเองทุกอย่างที่เกี่ยวกับชีวิตพืช ส่วนเครือข่ายมีไว้ส่งค่าผ่าน MQTT ไปเก็บใน InfluxDB เพื่อดูกราฟย้อนหลังและแจ้งเตือนเข้า LINE เท่านั้น เน็ตจะหลุดกี่ชั่วโมง โรงเรือนก็ยังทำงานตามปกติ

ระบบอัตโนมัติที่ดี ควรทำงานได้แม้ในวันที่เราติดต่อมันไม่ได้

ข้อควรระวังจากการใช้งานจริง

  • ตำแหน่งเซนเซอร์สำคัญกว่าราคาเซนเซอร์ ติดระดับเดียวกับทรงพุ่ม ห้ามโดนแดดตรงและห้ามอยู่ในละอองหมอก ไม่อย่างนั้นค่าจะเพี้ยนทั้งอุณหภูมิและความชื้น
  • อุณหภูมิใบต่างจากอุณหภูมิอากาศ ใบที่โดนแดดอาจร้อนกว่าอากาศ 1–3°C สูตรในบทความนี้เป็น VPD ของอากาศ ถ้าอยากละเอียดขึ้นให้ใช้เซนเซอร์อินฟราเรดวัดอุณหภูมิใบแยก
  • ความชื้นใกล้ 100% ทำให้เซนเซอร์อิ่มตัว ค่าจะค้างสูงหลังพ่นหมอก SHT31 มีฮีตเตอร์ในตัว เปิดสั้น ๆ วันละครั้งช่วยไล่ไอน้ำได้
  • ตั้งเพดานเวลาพ่นเสมอ ถ้าเซนเซอร์เสียแล้วรายงานค่าแห้งตลอด อย่าให้ปั๊มทำงานไม่หยุดจนน้ำท่วมแปลง โค้ดด้านบนจำกัดไว้ที่ 3 นาทีต่อรอบ

สรุป

เปลี่ยนจากการดูความชื้นอย่างเดียวมาดู VPD ใช้แค่สูตรบรรทัดเดียวกับเซนเซอร์ตัวเดิม แต่ทำให้ระบบตัดสินใจใกล้เคียงกับที่พืชรู้สึกจริงมากขึ้น และเมื่อให้บอร์ดคิดเอง โรงเรือนก็ไม่ต้องฝากชีวิตไว้กับสัญญาณอินเทอร์เน็ต

ระบบโรงเรือนตัวเต็มที่ต่อยอดจากโค้ดนี้ มีวัดความชื้นดินหลายระดับความลึกและส่งข้อมูลผ่าน LoRa ดูได้ที่หน้า นวัตกรรม IoT เกษตร