เนื้อหาในบทความ
ภาพรวมโปรเจกต์
ทุกปัญหาของคนรักพืชคือ "รดน้ำมากไปหรือน้อยไป" ระบบเฝ้าสังเกตพืชอัจฉริยะ (Smart Plant Monitoring System) จะช่วยแก้ปัญหานี้ด้วย:
- วัดความชื้นในดิน - รู้ได้ทันทีว่าพืชต้องการน้ำ
- วัดอุณหภูมิและความชื้นอากาศ - เพื่อสภาพแวดล้อมที่เหมาะสม
- แจ้งเตือนอัตโนมัติ - ผ่าน LINE Notification เมื่อดินแห้ง
- บันทึกข้อมูล - ดูประวัติการเปลี่ยนแปลงได้ที่ CynoIoT Platform
- ประหยัดเงิน - เมื่อเทียบกับซื้อของสำเร็จรูป (฿500-800 บาท ในตลาด)
💡 ทำไมต้องทำเอง? ระบบทำเองปรับแต่งได้ตามต้องการ เพิ่มเซ็นเซอร์อื่นๆ ได้ และเรียนรู้การทำงานของ IoT จริงๆ นอกจากนี้ยังเชื่อมต่อกับ CynoIoT Platform ได้ฟรี!
อุปกรณ์ที่ต้องใช้
Hardware
| อุปกรณ์ | จำนวน | ราคาโดยประมาณ |
|---|---|---|
| ESP32 DevKit / NodeMCU ESP32 | 1 | ฿80-150 |
| Soil Moisture Sensor ( capacitive ) | 1 | ฿30-50 |
| DHT22 / AM2302 (Temp & Humidity) | 1 | ฿40-60 |
| Jumper Wires (female-to-female) | - | ฿20-30 |
| Breadboard หรือ PCB | 1 | ฿15-30 |
| USB Cable สำหรับ ESP32 | 1 | ฿15-25 |
Software
- Arduino IDE - สำหรับอัปโหลดโค้ด
- Library: DHT sensor library by Adafruit
- Library: WiFi Client & HTTPS (สำหรับ LINE Notify)
- Library: CynoIoT MQTT Client (ถ้าใช้ CynoIoT)
💡 เคล็ดลับ: ให้ใช้ Capacitive Soil Moisture Sensor แทน Resistive Sensor เพราะทนทานกว่าและไม่เกิดการกัดกร่อน (corrosion) ราคาใกล้เคียงกันแต่อายุการใช้งานยาวนานกว่ามาก
การต่อวงจร
การต่อวงจรทำได้ง่ายมาก เพราะใช้เซ็นเซอร์แบบดิจิทัลทั้งหมด ไม่ต้องกังวลเรื่องค่า analog
ต่อ Soil Moisture Sensor (Capacitive)
Soil Moisture Sensor → ESP32 VCC → 3.3V GND → GND OUT (Signal) → GPIO 34 (ADC1_CH6)
ต่อ DHT22 Sensor
DHT22 → ESP32 VCC (Pin 1) → 3.3V DATA (Pin 2) → GPIO 4 GND (Pin 4) → GND ต่อตัวต้านทาน 10kΩ ระหว่าง VCC และ DATA (pull-up)
⚠️ ข้อควรระวัง: GPIO 34-39 บน ESP32 เป็น input only ไม่สามารถใช้เป็น output ได้ ให้ใช้เฉพาะสำหรับอ่านค่าจากเซ็นเซอร์เท่านั้น
การเขียนโปรแกรม
โค้ดต่อไปนี้จะอ่านค่าจากเซ็นเซอร์ทั้งสองและส่งข้อมูลไปยัง CynoIoT Platform พร้อมแจ้งเตือนผ่าน LINE เมื่อดินแห้งเกินไป
#include <WiFi.h>
#include <HTTPClient.h>
#include <DHT.h>
#include <PubSubClient.h>
// ===== ตั้งค่า WiFi =====
const char* ssid = "YOUR_WIFI_SSID";
const char* password = "YOUR_WIFI_PASSWORD";
// ===== ตั้งค่า LINE Notify =====
const char* lineToken = "YOUR_LINE_NOTIFY_TOKEN";
// ===== ตั้งค่า CynoIoT MQTT =====
const char* mqttServer = "mqtt.cynoiot.com";
const int mqttPort = 1883;
const char* mqttUser = "YOUR_CYNOIOT_TOKEN";
const char* mqttPass = "";
const char* deviceId = "plant-monitor-001";
// ===== ตั้งค่าเซ็นเซอร์ =====
#define SOIL_PIN 34 // GPIO สำหรับ Soil Moisture
#define DHT_PIN 4 // GPIO สำหรับ DHT22
#define DHT_TYPE DHT22
DHT dht(DHT_PIN, DHT_TYPE);
WiFiClient espClient;
PubSubClient mqttClient(espClient);
// ตัวแปรสำหรับเก็บค่าเก่า
float lastSoilMoisture = 100;
bool notified = false;
void setup() {
Serial.begin(115200);
// เริ่มต้นเซ็นเซอร์
dht.begin();
// เริ่มต้น WiFi
WiFi.begin(ssid, password);
Serial.print("กำลังเชื่อมต่อ WiFi");
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("\nWiFi เชื่อมต่อแล้ว!");
Serial.print("IP address: ");
Serial.println(WiFi.localIP());
// ตั้งค่า MQTT
mqttClient.setServer(mqttServer, mqttPort);
delay(2000);
}
void loop() {
// ตรวจสอบการเชื่อมต่อ MQTT
if (!mqttClient.connected()) {
reconnectMQTT();
}
mqttClient.loop();
// อ่านค่าจากเซ็นเซอร์
float soilMoisture = readSoilMoisture();
float temperature = dht.readTemperature();
float humidity = dht.readHumidity();
// ตรวจสอบว่าอ่านค่าสำเร็จหรือไม่
if (isnan(temperature) || isnan(humidity)) {
Serial.println("ไม่สามารถอ่านค่าจาก DHT22 ได้!");
delay(2000);
return;
}
// แสดงค่าบน Serial Monitor
Serial.println("===== ข้อมูลเซ็นเซอร์ =====");
Serial.printf("ความชื้นในดิน: %.1f%%\n", soilMoisture);
Serial.printf("อุณหภูมิ: %.1f°C\n", temperature);
Serial.printf("ความชื้นอากาศ: %.1f%%\n", humidity);
Serial.println("=====================");
// ส่งข้อมูลไปยัง CynoIoT
sendToCynoIoT(soilMoisture, temperature, humidity);
// แจ้งเตือนผ่าน LINE ถ้าดินแห้งเกิน 30%
if (soilMoisture < 30 && !notified) {
sendLineAlert(soilMoisture, temperature, humidity);
notified = true;
}
// รีเซ็ตการแจ้งเตือนเมื่อดินชื้นกลับมา
if (soilMoisture > 50) {
notified = false;
}
delay(60000); // อ่านทุกๆ 1 นาที
}
// ฟังก์ชันอ่านค่าความชื้นในดิน
float readSoilMoisture() {
int rawValue = analogRead(SOIL_PIN);
// แปลงค่า ADC (0-4095) เป็นเปอร์เซ็นต์
// Capacitive sensor: แห้ง = ค่าต่ำ, ชื้น = ค่าสูง
float moisture = map(rawValue, 0, 4095, 0, 100);
return moisture;
}
// ฟังก์ชันส่งข้อมูลไปยัง CynoIoT
void sendToCynoIoT(float soil, float temp, float hum) {
String payload = "{";
payload += "\"soil_moisture\":" + String(soil) + ",";
payload += "\"temperature\":" + String(temp) + ",";
payload += "\"humidity\":" + String(hum);
payload += "}";
char topic[100];
sprintf(topic, "device/%s/data", deviceId);
mqttClient.publish(topic, payload.c_str());
Serial.println("ส่งข้อมูลไปยัง CynoIoT แล้ว");
}
// ฟังก์ชันแจ้งเตือนผ่าน LINE
void sendLineAlert(float soil, float temp, float hum) {
HTTPClient http;
String message = "\\n⚠️ แจ้งเตือน: ดินของพืขแห้งแล้ว!\\n";
message += String("━━━━━━━━━━━━━━━\\n");
message += String("🌱 ความชื้นในดิน: ") + soil + "%\\n";
message += String("🌡️ อุณหภูมิ: ") + temp + "°C\\n";
message += String("💧 ความชื้นอากาศ: ") + hum + "%\\n";
message += String("━━━━━━━━━━━━━━━\\n");
message += String("⏰ เวลา: ") + getTimeString();
http.begin("https://notify-api.line.me/api/notify");
http.addHeader("Content-Type", "application/x-www-form-urlencoded");
http.addHeader("Authorization", "Bearer " + String(lineToken));
String httpRequestData = "message=" + message;
int httpResponseCode = http.POST(httpRequestData);
if (httpResponseCode > 0) {
Serial.println("ส่งการแจ้งเตือน LINE สำเร็จ!");
} else {
Serial.print("Error ส่ง LINE: ");
Serial.println(httpResponseCode);
}
http.end();
}
// ฟังก์ชันเชื่อมต่อ MQTT ใหม่
void reconnectMQTT() {
while (!mqttClient.connected()) {
Serial.print("กำลังเชื่อมต่อ MQTT...");
if (mqttClient.connect(deviceId, mqttUser, mqttPass)) {
Serial.println("เชื่อมต่อสำเร็จ!");
} else {
Serial.print("ล้มเหลว, rc=");
Serial.print(mqttClient.state());
Serial.println(" ลองใหม่ใน 5 วินาที");
delay(5000);
}
}
}
// ฟังก์ชันเวลา (simple version)
String getTimeString() {
return "เมื่อสักครู่นี้";
}คำอธิบายโค้ด
- setup() - เชื่อมต่อ WiFi และเริ่มต้นเซ็นเซอร์
- loop() - อ่านค่าเซ็นเซอร์ทุกๆ 1 นาที
- readSoilMoisture() - แปลงค่า ADC เป็นเปอร์เซ็นต์
- sendToCynoIoT() - ส่งข้อมูลไปยัง Platform
- sendLineAlert() - แจ้งเตือนเมื่อดินแห้ง
💡 เคล็ดลับ: สามารถปรับค่า threshold (เดิม: 30%) ตามชนิดของพืช เช่น กระบองเพชรต้องการน้ำน้อยกว่าเฟิร์น
เชื่อมต่อกับ CynoIoT Platform
CynoIoT เป็น Platform ฟรีสำหรับบริหารจัดการอุปกรณ์ IoT พร้อม Dashboard สวยงามและการแจ้งเตือน
ขั้นตอนการลงทะเบียน
- ไปที่ cynoiot.com และสมัครสมาชิกฟรี
- สร้าง Device ใหม่ ในระบบ (ได้รับ Device Token)
- นำ Token มาใส่ในโค้ดตรง
mqttUser - อัปโหลดโค้ดไปยัง ESP32
- ดูข้อมูลบน Dashboard ได้ทันที!
✅ ข้อดีของ CynoIoT: ฟรีไม่จำกัด Device, Dashboard สวยงาม, รองรับ MQTT, และมี API สำหรับเชื่อมต่อกับระบบอื่นๆ
การแก้ปัญหาที่พบบ่อย
ปัญหา: อ่านค่า DHT22 ไม่ได้
สาเหตุ: ต่อสายผิด, ไม่มีตัวต้านทาน pull-up, หรือ GPIO ผิด
วิธีแก้: ตรวจสอบการต่อสายให้ถูกต้อง ใส่ตัวต้านทาน 10kΩ ระหว่าง VCC และ DATA
ปัญหา: ค่า Soil Moisture ผิดปกติ
สาเหตุ: ใช้ resistive sensor (กัดกร่อน), ต่อขาผิด
วิธีแก้: เปลี่ยนเป็น capacitive sensor หรือทำ calibration ใหม่
ปัญหา: ส่ง LINE Notify ไม่ได้
สาเหตุ: Token ผิด, ข้อความยาวเกินไป, หรือไม่ได้ต่อ internet
วิธีแก้: ตรวจสอบ Token ให้ถูกต้อง, ลดความยาวข้อความ, ตรวจสอบการเชื่อมต่อ WiFi
ปัญหา: MQTT เชื่อมต่อไม่ได้
สาเหตุ: Device Token ผิด, ไม่ได้เชื่อมต่อ internet
วิธีแก้: ตรวจสอบ Device Token จาก CynoIoT Dashboard, ทดสอบ WiFi connection
สรุปสิ่งที่เรียนรู้
- การต่อและใช้งาน Soil Moisture Sensor (Capacitive)
- การอ่านค่า DHT22 สำหรับวัดอุณหภูมิและความชื้น
- การส่งข้อมูลไปยัง CynoIoT Platform ผ่าน MQTT
- การแจ้งเตือนผ่าน LINE Notify
- การสร้างระบบ IoT ที่ใช้งานได้จริง
🎯 ถัดไป: ลองเพิ่มฟีเจอร์อื่นๆ เช่น ปั๊มน้ำอัตโนมัติ, จอแสดงผล, หรือเชื่อมต่อ Home Assistant