การประยุกต์ใช้ปัญญาประดิษฐ์ด้วยเทคนิค Decision Tree สำหรับระบบรดน้ำอัจฉริยะใน Smart Farm
Application of Artificial Intelligence Using Decision Tree for an Intelligent Irrigation System in Smart Farming
ที่มาและความสำคัญของปัญหา
ภาคการเกษตรมีความจำเป็นต้องใช้น้ำอย่างเหมาะสมเพื่อให้พืชสามารถเจริญเติบโตและให้ผลผลิตได้อย่างมีประสิทธิภาพ อย่างไรก็ตาม การให้น้ำในระบบเกษตรทั่วไปมักอาศัยการกำหนดเวลา การสังเกตสภาพแวดล้อม หรือการตัดสินใจของผู้ปฏิบัติงาน ซึ่งอาจทำให้เกิดปัญหาการให้น้ำมากหรือน้อยเกินความต้องการของพืช โดยเฉพาะเมื่อสภาพแวดล้อมมีการเปลี่ยนแปลงตลอดเวลา เช่น อุณหภูมิ ความชื้น ความเข้มแสง และปริมาณก๊าซคาร์บอนไดออกไซด์ ส่งผลต่อการเจริญเติบโตและความต้องการน้ำของพืช การพัฒนาระบบที่สามารถตรวจวัดข้อมูลสภาพแวดล้อมและนำข้อมูลดังกล่าวมาใช้ประกอบการตัดสินใจให้น้ำจึงเป็นแนวทางสำคัญในการเพิ่มประสิทธิภาพการจัดการฟาร์ม
แนวคิด Smart Farm เป็นการประยุกต์ใช้เทคโนโลยีดิจิทัล ระบบ Internet of Things (IoT) และระบบอัตโนมัติเข้ามาช่วยในการตรวจวัด วิเคราะห์ และควบคุมกระบวนการทางการเกษตร โดยอุปกรณ์ไมโครคอนโทรลเลอร์ เช่น Arduino และ ESP32 สามารถเชื่อมต่อกับเซนเซอร์เพื่อเก็บข้อมูลสภาพแวดล้อมและควบคุมอุปกรณ์ต่าง ๆ ได้แบบอัตโนมัติ อย่างไรก็ตาม การตรวจวัดข้อมูลเพียงอย่างเดียวอาจยังไม่เพียงพอสำหรับการตัดสินใจที่เหมาะสม เนื่องจากข้อมูลจากเซนเซอร์หลายชนิดสามารถมีความสัมพันธ์กันและส่งผลต่อการตัดสินใจเปิดหรือปิดระบบให้น้ำ
ปัญญาประดิษฐ์ (Artificial Intelligence: AI) และเทคนิคการเรียนรู้ของเครื่อง (Machine Learning) จึงสามารถนำมาใช้เพื่อวิเคราะห์รูปแบบของข้อมูลและสร้างแบบจำลองสำหรับการตัดสินใจได้ โดยเฉพาะเทคนิค Decision Tree ซึ่งมีลักษณะเป็นโครงสร้างแบบต้นไม้ที่สามารถแบ่งข้อมูลตามเงื่อนไขของตัวแปรนำเข้า และนำไปสู่ผลลัพธ์ของการตัดสินใจ เทคนิคดังกล่าวมีข้อดีคือสามารถอธิบายกระบวนการตัดสินใจได้ง่าย สามารถแปลงผลลัพธ์จากแบบจำลองให้อยู่ในรูปแบบกฎ IF–THEN และนำกฎที่ได้ไปประยุกต์ใช้กับไมโครคอนโทรลเลอร์เพื่อควบคุมอุปกรณ์จริงได้โดยไม่จำเป็นต้องประมวลผลแบบจำลอง Machine Learning ใหม่ทุกครั้งบนอุปกรณ์ปลายทาง
ดังนั้น งานวิจัยนี้จึงมุ่งประยุกต์ใช้เทคนิค Decision Tree เพื่อสร้างแบบจำลองการตัดสินใจสำหรับระบบรดน้ำอัจฉริยะ โดยใช้ข้อมูลสภาพแวดล้อม ได้แก่ อุณหภูมิ ความชื้น ความเข้มแสง และปริมาณก๊าซคาร์บอนไดออกไซด์เป็นข้อมูลนำเข้า และกำหนดการเปิดหรือปิดระบบให้น้ำเป็นผลลัพธ์ของการตัดสินใจ จากนั้นนำกฎที่ได้จาก Decision Tree มาพัฒนาเป็นโปรแกรมบนบอร์ดไมโครคอนโทรลเลอร์ Arduino/ESP32 เพื่อควบคุมรีเลย์และปั๊มน้ำโดยอัตโนมัติ แนวทางดังกล่าวแสดงให้เห็นถึงการบูรณาการระหว่าง Artificial Intelligence, Machine Learning, IoT และระบบควบคุมอัตโนมัติ เพื่อสนับสนุนการจัดการน้ำใน Smart Farm และเป็นแนวทางสำหรับการพัฒนาระบบเกษตรอัจฉริยะที่สามารถนำข้อมูลมาใช้ประกอบการตัดสินใจได้อย่างเป็นระบบ
2. วัตถุประสงค์
- เพื่อพัฒนาแบบจำลองการตัดสินใจด้วยเทคนิค Decision Tree สำหรับจำแนกสถานะการเปิดหรือปิดระบบรดน้ำจากข้อมูลสภาพแวดล้อม ได้แก่ อุณหภูมิ ความชื้น ความเข้มแสง และปริมาณก๊าซคาร์บอนไดออกไซด์
- เพื่อศึกษากระบวนการสร้างกฎการตัดสินใจจาก Decision Tree โดยพิจารณาค่าความไม่แน่นอน (Entropy) และค่าการลดความไม่แน่นอน (Information Gain) สำหรับกำหนดลำดับของโหนดในการตัดสินใจ
- เพื่อพัฒนาระบบรดน้ำอัจฉริยะด้วย Arduino/ESP32 ที่สามารถรับข้อมูลจากเซนเซอร์ ประมวลผลตามกฎของ Decision Tree และควบคุมการเปิด–ปิดปั๊มน้ำโดยอัตโนมัติ
- เพื่อประเมินประสิทธิภาพของระบบรดน้ำอัจฉริยะ จากความถูกต้องของการตัดสินใจเปิด–ปิดน้ำตามแบบจำลอง Decision Tree และการตอบสนองของระบบควบคุม
1. Decision Tree คืออะไร?
Decision Tree หรือต้นไม้ตัดสินใจ เป็น Machine Learning Algorithm ประเภท Supervised Learning ที่ใช้ข้อมูลที่มีคำตอบอยู่แล้ว (Labeled Data) เพื่อเรียนรู้กฎสำหรับการจำแนกหรือทำนายข้อมูลใหม่
ในตัวอย่างนี้ เราต้องการสร้างระบบ AI ที่ตอบคำถามว่า
“จากอุณหภูมิ ความชื้น ความเข้มแสง และ CO₂ ระบบควรเปิดหรือปิดน้ำ?”
ดังนั้น
Input / Features
- อุณหภูมิ
- ความชื้น
- ความเข้มแสง
- ปริมาณ CO₂
Output / Target
- เปิดน้ำ
- ปิดน้ำ

3. Dataset
Dataset ที่เราใช้มีทั้งหมด 100 records
| Column | ความหมาย | ประเภท |
| Record | หมายเลขข้อมูล | ID |
| อุณหภูมิ (°C) | อุณหภูมิ | Feature |
| ความชื้น (%) | ความชื้น | Feature |
| ความเข้มแสง (Lux) | ปริมาณแสง | Feature |
| ปริมาณ CO₂ (ppm) | ปริมาณ CO₂ | Feature |
| การเปิด-ปิดน้ำ | ผลลัพธ์ | Target |
ตัวอย่าง
| Record | Temp | Humidity | Light | CO₂ | Water |
| 1 | 21 | 41 | 920 | 691 | ปิด |
| 2 | 31 | 55 | 994 | 1474 | ปิด |
| 3 | 33 | 71 | 548 | 1413 | ปิด |
| 4 | 37 | 86 | 550 | 640 | ปิด |
| 5 | 32 | 39 | 524 | 936 | เปิด |
| 6 | 24 | 54 | 316 | 1380 | ปิด |
| 7 | 23 | 41 | 496 | 483 | เปิด |
| 8 | 39 | 58 | 141 | 478 | เปิด |
| 9 | 20 | 44 | 355 | 850 | เปิด |
| 10 | 35 | 75 | 922 | 514 | เปิด |
4. ขั้นตอนการสร้าง Decision Tree
กระบวนการหลักมี 5 ขั้นตอน
1. เตรียม Dataset
https://github.com/zdysys/dataset/blob/main/Decision_Tree_Smart_Irrigation_100_Numeric.csv
2. คำนวณ Entropy
3. ทดลองแบ่งข้อมูลด้วย Feature ต่าง ๆ
4. คำนวณ Information Gain
5. เลือก Gain สูงสุด → สร้าง Node
6. ทำซ้ำกับข้อมูลย่อย
7. ได้ Decision Tree
5. ขั้นตอนที่ 1: ดู Class ของข้อมูล
จาก Dataset 100 records
| การเปิด-ปิดน้ำ | จำนวน |
| เปิด | 45 |
| ปิด | 55 |
| รวม | 100 |
ดังนั้น


6. ขั้นตอนที่ 2: คำนวณ Entropy ของ Dataset
Entropy ใช้วัดว่า Dataset มีความ “ปะปน” หรือ “ไม่แน่นอน” มากแค่ไหน
สูตร

สำหรับ 2 Class

แทนค่า

คำนวณ


ดังนั้น



ดังนั้น Dataset เริ่มต้นมี

7. ทำไม Entropy จึงเกือบเท่ากับ 1?
เพราะข้อมูลมี
เปิด = 45
ปิด = 55
ซึ่งค่อนข้างใกล้เคียงกับ 50:50
ดังนั้นข้อมูลจึงยังมีความไม่แน่นอนสูง
ถ้าเป็น
เปิด = 100
ปิด = 0
Entropy จะเป็น

เพราะเรารู้คำตอบแน่นอน
8. จุดสำคัญ: Dataset ของเราเป็นข้อมูลตัวเลข
ตรงนี้เป็นจุดที่แตกต่างจากตัวอย่าง Play Golf

https://python.plainenglish.io/decision-tree-clearly-explained-7c74f40dae9c?gi=6db5d3e2c8ea
Play Golf ใช้
Sunny
Rainy
Overcast
ซึ่งเป็น Categorical Data
แต่ Dataset ของเราเป็น
Temperature = 21, 22, 23, …
Humidity = 41, 55, 71, …
Light = 920, 994, …
CO₂ = 691, 1474, …
เป็น Numerical / Continuous Data
ดังนั้น Decision Tree ต้องหา จุดแบ่ง (Threshold)
ตัวอย่าง

และ

9. Threshold คืออะไร?
สมมติเรามีข้อมูล
| Humidity | Water |
| 41 | เปิด |
| 44 | เปิด |
| 49 | เปิด |
| 50 | ปิด |
| 55 | ปิด |
| 60 | ปิด |
Decision Tree สามารถทดลองแบ่งระหว่าง
49 และ 50
ดังนั้น Threshold คือ

จึงได้
Humidity ≤ 49.5
Humidity > 49.5
นี่คือแนวคิดสำคัญที่นักศึกษาควรเข้าใจ
Decision Tree ไม่ได้จำเป็นต้องใช้คำว่า ต่ำ/กลาง/สูง แต่สามารถค้นหา Threshold จากข้อมูลตัวเลขได้
10. ทดลองเลือก Root Node
เรามี Features 4 ตัว
- อุณหภูมิ
- ความชื้น
- ความเข้มแสง
- CO₂
เราต้องตอบคำถามว่า
Feature ไหนสามารถแบ่งข้อมูลให้ “บริสุทธิ์” ได้ดีที่สุด?
จึงทดลองทุก Feature
11. ทดลองแบ่งด้วยอุณหภูมิ
Decision Tree ทดลอง Threshold ต่าง ๆ เช่น
Temperature ≤ 34.5
Temperature > 34.5
สำหรับ Dataset นี้ จุดที่ให้ Information Gain สูงสุดของอุณหภูมิประมาณ

12. ทดลองแบ่งด้วยความชื้น
Decision Tree ทดลอง Threshold หลายค่า
เช่น
Humidity ≤ 45.5
Humidity ≤ 46.5
Humidity ≤ 47.5
Humidity ≤ 48.5
Humidity ≤ 49.5
Humidity ≤ 50.5
…
พบว่า Threshold ที่ดีที่สุดคือ

แบ่งเป็น

13. คำนวณ Entropy ของแต่ละกลุ่ม
หลังจากแบ่งที่

จะได้
กลุ่มที่ 1

มีทั้งหมด 28 records
| Class | จำนวน |
| เปิด | 26 |
| ปิด | 2 |
ดังนั้น

ได้

สังเกตว่าเกือบเป็นกลุ่มบริสุทธิ์แล้ว
เปิด ██████████████████████████ 26
ปิด ██ 2
14. กลุ่มที่ 2

มีทั้งหมด

records
| Class | จำนวน |
| เปิด | 19 |
| ปิด | 53 |
Entropy

ได้

15. Weighted Entropy
เราไม่สามารถเอา

มาบวกกันตรง ๆ ได้
เพราะแต่ละกลุ่มมีจำนวนข้อมูลไม่เท่ากัน
ดังนั้นต้องใช้ Weighted Entropy
สูตร

แทนค่า



16. คำนวณ Information Gain
สูตร

ดังนั้น


นี่เป็นค่าที่สำคัญมาก
17. ทดลอง Feature อื่น
เมื่อทำกระบวนการเดียวกันกับทุก Feature ได้ผลโดยประมาณดังนี้
| Feature | Best Threshold | Information Gain |
| ความชื้น | 49.5% | 0.289 |
| CO₂ | 1293 ppm | 0.112 |
| อุณหภูมิ | 34.5°C | 0.051 |
| ความเข้มแสง | 597.5 Lux | 0.025 |
ดังนั้น

มีค่า Information Gain สูงที่สุด
18. ดังนั้น Root Node คืออะไร?
คำตอบคือ
ความชื้น (Humidity)
และ Threshold คือ
49.5%
19. ทำไมต้องเลือก Gain สูงสุด?
ลองนึกภาพว่าเรามีคำถาม 4 ข้อ
ถามอุณหภูมิ?
ถามความชื้น?
ถามแสง?
ถาม CO₂?
เราต้องการเลือกคำถามที่ทำให้ข้อมูลแยกเป็นกลุ่มได้ชัดเจนที่สุด
ดังนั้น
Information Gain สูง = ลดความสับสนได้มาก
ใน Dataset นี้
ก่อนแบ่ง
Entropy = 0.993
แบ่งด้วย Humidity ≤ 49.5
หลังแบ่ง
Entropy = 0.703
ลดลง

20. สร้าง Node ถัดไป
หลังจาก Root Node
เราต้องดูแต่ละกิ่ง
กิ่งซ้าย
Humidity ≤ 49.5
มี
เปิด = 26
ปิด = 2
ยังไม่บริสุทธิ์ 100%
ดังนั้นต้องแบ่งต่อ
21. ทดลอง Feature ที่เหลือ
สำหรับกลุ่ม
Humidity ≤ 49.5
เราจะไม่เอา Humidity มาพิจารณาซ้ำในขั้นนี้ แต่ทดลอง
- Temperature
- Light
- CO₂
ใน Dataset นี้ Decision Tree พบว่า อุณหภูมิ เป็นตัวแบ่งที่เหมาะสม
จึงได้
Humidity ≤ 49.5
│
↓
Temperature
22. กิ่งขวา
สำหรับ
Humidity > 49.5
มี
เปิด = 19
ปิด = 53
ยังมีทั้งสอง Class
จึงต้องแบ่งต่อ
จาก Dataset นี้ได้โครงสร้างในระดับต้น ๆ เป็น

จากนั้น Tree จะค้นหา Feature และ Threshold ที่ช่วยแยกข้อมูลต่อ
23. ตัวอย่าง Decision Tree ที่ได้
เมื่อให้ Algorithm เรียนรู้จาก Dataset ทั้งหมด จะได้ต้นไม้ประมาณนี้


หมายเหตุ: ต้นไม้สุดท้ายที่ใช้จริงขึ้นอยู่กับเกณฑ์หยุดการแตก Node เช่น max_depth, min_samples_split, min_samples_leaf เป็นต้น
24. ตัวอย่างการใช้ Decision Tree ทำนาย
สมมติ Sensor อ่านค่าได้
Temperature = 32°C
Humidity = 42%
Light = 600 Lux
CO₂ = 900 ppm
เริ่มที่ Root
Humidity ≤ 49.5 ?
42 ≤ 49.5
ดังนั้น
Humidity
|
≤ 49.5
↓
Temperature
จากนั้นตรวจสอบ Temperature
32 > 29.5
ไปยัง Leaf
เปิด
ดังนั้น AI ตัดสินใจ

25. ตัวอย่างที่ 2
Sensor อ่านได้
Temperature = 28°C
Humidity = 85%
Light = 500 Lux
CO₂ = 700 ppm
เริ่ม
Humidity ≤ 49.5 ?
85 > 49.5
ไปทางขวา
Humidity > 49.5
จากนั้น Tree ตรวจสอบต่อ
และหากเข้าเงื่อนไข Leaf ที่เป็น ปิด
ผลลัพธ์คือ

26. สรุป Algorithm

27. ความสัมพันธ์ระหว่าง Entropy กับ Information Gain
นักศึกษามักสับสนสองคำนี้
| คำ | ตอบคำถามว่า |
| Entropy | ตอนนี้ข้อมูลสับสนแค่ไหน? |
| Information Gain | การแบ่งข้อมูลครั้งนี้ช่วยลดความสับสนได้เท่าไร? |
จำง่าย ๆ:
Entropy = ความสับสน
Information Gain = ความสับสนที่ลดลง
28. ตัวอย่างเปรียบเทียบ
ก่อนแบ่ง

แบ่งด้วย Humidity

ดังนั้น


แปลว่า
การแบ่งด้วยความชื้นสามารถลดความไม่แน่นอนของข้อมูลได้ประมาณ 0.289
29. ทำไม Dataset นี้จึงเหมาะกับการสอน?
Dataset นี้สามารถสอนนักศึกษาได้ครบหลายแนวคิด
ระดับที่ 1
Classification
เปิด / ปิด
ระดับที่ 2
Entropy
วัดความไม่แน่นอน
ระดับที่ 3
Information Gain
เลือก Feature ที่ดีที่สุด
ระดับที่ 4
Continuous Data
Temperature = 32
Humidity = 42
Light = 600
CO₂ = 900
ระดับที่ 5
Threshold
Humidity ≤ 49.5
ระดับที่ 6
Decision Tree
Sensor
↓
Decision Tree
↓
เปิด / ปิด
ระดับที่ 7
นำไปใช้กับ IoT

สรุป Rule: ทั้งหมด
| Rule | เงื่อนไขหลัก | ผลลัพธ์ |
|---|---|---|
| 1 | Humidity ≤ 49.5 AND Temperature ≤ 28.5 AND Light ≤ 887 | เปิดน้ำ |
| 2 | Humidity ≤ 49.5 AND Temperature ≤ 28.5 AND Light > 887 | ปิดน้ำ |
| 3 | Humidity ≤ 49.5 AND 28.5 < Temperature ≤ 29.5 | ปิดน้ำ |
| 4 | Humidity ≤ 49.5 AND Temperature > 29.5 | เปิดน้ำ |
| 5 | 49.5 < Humidity ≤ 75.5 AND Temperature ≤ 33.5 AND CO₂ ≤ 1274.5 AND Light ≤ 775.5 AND CO₂ ≤ 472 | ปิดน้ำ |
| 6 | 49.5 < Humidity ≤ 75.5 AND Temperature ≤ 33.5 AND 472 < CO₂ ≤ 1274.5 AND Light ≤ 380.5 AND Humidity ≤ 65.5 AND CO₂ ≤ 531 | เปิดน้ำ |
| 7 | 49.5 < Humidity ≤ 75.5 AND Temperature ≤ 33.5 AND 472 < CO₂ ≤ 1274.5 AND Light ≤ 380.5 AND Humidity ≤ 65.5 AND CO₂ > 531 | ปิดน้ำ |
| 8 | 49.5 < Humidity ≤ 75.5 AND Temperature ≤ 33.5 AND 472 < CO₂ ≤ 1274.5 AND Light ≤ 380.5 AND Humidity > 65.5 | เปิดน้ำ |
| 9 | 49.5 < Humidity ≤ 75.5 AND Temperature ≤ 33.5 AND 472 < CO₂ ≤ 1274.5 AND 380.5 < Light ≤ 775.5 AND Temperature ≤ 21.5 | เปิดน้ำ |
| 10 | 49.5 < Humidity ≤ 75.5 AND Temperature ≤ 33.5 AND 472 < CO₂ ≤ 1274.5 AND 380.5 < Light ≤ 775.5 AND Temperature > 21.5 | ปิดน้ำ |
| 11 | 49.5 < Humidity ≤ 75.5 AND Temperature ≤ 33.5 AND CO₂ ≤ 1274.5 AND Light > 775.5 | เปิดน้ำ |
| 12 | 49.5 < Humidity ≤ 75.5 AND Temperature ≤ 33.5 AND CO₂ > 1274.5 | ปิดน้ำ |
| 13 | 49.5 < Humidity ≤ 75.5 AND Temperature > 33.5 AND Humidity ≤ 60 | เปิดน้ำ |
| 14 | 49.5 < Humidity ≤ 75.5 AND Temperature > 33.5 AND Humidity > 60 AND Light ≤ 629.5 AND CO₂ ≤ 509 | เปิดน้ำ |
| 15 | 49.5 < Humidity ≤ 75.5 AND Temperature > 33.5 AND Humidity > 60 AND Light ≤ 629.5 AND 509 < CO₂ ≤ 1344.5 | ปิดน้ำ |
| 16 | 49.5 < Humidity ≤ 75.5 AND Temperature > 33.5 AND Humidity > 60 AND Light ≤ 629.5 AND 1344.5 < CO₂ ≤ 1366.5 | เปิดน้ำ |
| 17 | 49.5 < Humidity ≤ 75.5 AND Temperature > 33.5 AND Humidity > 60 AND Light ≤ 629.5 AND CO₂ > 1366.5 | ปิดน้ำ |
| 18 | 49.5 < Humidity ≤ 75.5 AND Temperature > 33.5 AND Humidity > 60 AND Light > 629.5 | เปิดน้ำ |
| 19 | Humidity > 75.5 | ปิดน้ำ |

int decisionTree(float temperature, float humidity, float light, float co2)
{
// Rule 1-4
if (humidity <= 49.5) {
if (temperature <= 29.5) {
if (temperature <= 28.5) {
if (light <= 887.0) {
return 1; // Rule 1: เปิดน้ำ
} else {
return 0; // Rule 2: ปิดน้ำ
}
} else {
return 0; // Rule 3: ปิดน้ำ
}
} else {
return 1; // Rule 4: เปิดน้ำ
}
} else {
// Rule 5-19
if (humidity <= 75.5) {
if (temperature <= 33.5) {
if (co2 <= 1274.5) {
if (light <= 775.5) {
if (co2 <= 472.0) {
return 0; // Rule 5: ปิดน้ำ
} else {
if (light <= 380.5) {
if (humidity <= 65.5) {
if (co2 <= 531.0) {
return 1; // Rule 6: เปิดน้ำ
} else {
return 0; // Rule 7: ปิดน้ำ
}
} else {
return 1; // Rule 8: เปิดน้ำ
}
} else {
if (temperature <= 21.5) {
return 1; // Rule 9: เปิดน้ำ
} else {
return 0; // Rule 10: ปิดน้ำ
}
}
}
} else {
return 1; // Rule 11: เปิดน้ำ
}
} else {
return 0; // Rule 12: ปิดน้ำ
}
} else {
if (humidity <= 60.0) {
return 1; // Rule 13: เปิดน้ำ
} else {
if (light <= 629.5) {
if (co2 <= 509.0) {
return 1; // Rule 14: เปิดน้ำ
} else {
if (co2 <= 1344.5) {
return 0; // Rule 15: ปิดน้ำ
} else {
if (co2 <= 1366.5) {
return 1; // Rule 16: เปิดน้ำ
} else {
return 0; // Rule 17: ปิดน้ำ
}
}
}
} else {
return 1; // Rule 18: เปิดน้ำ
}
}
}
} else {
return 0; // Rule 19: ปิดน้ำ
}
}
}
ตัวอย่างโปรเจ็ค
#include <Arduino.h>
#include <DHT.h>
// =====================================================
// ESP32 SMART IRRIGATION - DECISION TREE
// Board: ESP32 DevKit 30-pin
//
// DHT22 -> Temperature + Humidity
// POT 1 -> Light (Analog)
// POT 2 -> CO2 (Analog)
// Relay -> Water Pump
// LED -> Pump Status
//
// Decision Tree:
// Root Node = Humidity
// =====================================================
// ---------------- PIN CONFIGURATION ------------------
#define DHT_PIN 4
#define DHT_TYPE DHT22
#define LIGHT_PIN 34
#define CO2_PIN 35
#define RELAY_PIN 26
#define PUMP_LED_PIN 27
// Relay Module ส่วนใหญ่เป็น Active LOW
#define RELAY_ON LOW
#define RELAY_OFF HIGH
DHT dht(DHT_PIN, DHT_TYPE);
// =====================================================
// SETUP
// =====================================================
void setup() {
Serial.begin(115200);
delay(1000);
Serial.println();
Serial.println("==========================================");
Serial.println(" ESP32 SMART IRRIGATION");
Serial.println(" Decision Tree Controller");
Serial.println("==========================================");
// DHT22
dht.begin();
// ESP32 ADC 12-bit
// ค่า ADC = 0 - 4095
analogReadResolution(12);
// Relay
pinMode(RELAY_PIN, OUTPUT);
digitalWrite(RELAY_PIN, RELAY_OFF);
// LED
pinMode(PUMP_LED_PIN, OUTPUT);
digitalWrite(PUMP_LED_PIN, LOW);
Serial.println("System Ready");
}
// =====================================================
// PUMP CONTROL
// =====================================================
void pumpON() {
digitalWrite(RELAY_PIN, RELAY_ON);
digitalWrite(PUMP_LED_PIN, HIGH);
}
void pumpOFF() {
digitalWrite(RELAY_PIN, RELAY_OFF);
digitalWrite(PUMP_LED_PIN, LOW);
}
// =====================================================
// ADC -> LIGHT
// ADC 0-4095 -> 100-1000 Lux
// =====================================================
float convertLight(int adcValue) {
return 100.0 + ((float)adcValue / 4095.0) * 900.0;
}
// =====================================================
// ADC -> CO2
// ADC 0-4095 -> 350-1500 ppm
// =====================================================
float convertCO2(int adcValue) {
return 350.0 + ((float)adcValue / 4095.0) * 1150.0;
}
// =====================================================
// DECISION TREE
//
// Root Node = Humidity
//
// Input:
// Temperature
// Humidity
// Light
// CO2
//
// Output:
// true = OPEN WATER
// false = CLOSE WATER
// =====================================================
bool decisionTree(float temperature, float humidity, float light, float co2) {
if (humidity <= 49.5) {
if (temperature <= 29.5) {
if (temperature <= 28.5) {
if (light <= 887.0) {
return true;
} else {
return false;
}
} else {
return false;
}
} else {
return true;
}
} else {
if (humidity <= 75.5) {
if (temperature <= 33.5) {
if (co2 <= 1274.5) {
if (light <= 775.5) {
if (co2 <= 472.0) {
return false;
} else {
if (light <= 380.5) {
if (humidity <= 65.5) {
if (co2 <= 531.0) {
return true;
} else {
return false;
}
} else {
return true;
}
} else {
if (temperature <= 21.5) {
return true;
} else {
return false;
}
}
}
} else {
return true;
}
} else {
return false;
}
} else {
if (humidity <= 60.0) {
return true;
} else {
if (light <= 629.5) {
if (co2 <= 509.0) {
return true;
} else {
if (co2 <= 1344.5) {
return false;
} else {
if (co2 <= 1366.5) {
return true;
} else {
return false;
}
}
}
} else {
return true;
}
}
}
} else {
return false;
}
}
}
// =====================================================
// LOOP
// =====================================================
void loop() {
// ---------------------------------------------------
// READ DHT22
// ---------------------------------------------------
float temperature = dht.readTemperature();
float humidity = dht.readHumidity();
if (isnan(temperature) || isnan(humidity)) {
Serial.println("ERROR: Cannot read DHT22");
pumpOFF();
delay(2000);
return;
}
// ---------------------------------------------------
// READ ANALOG INPUT
// ---------------------------------------------------
int lightADC = analogRead(LIGHT_PIN);
int co2ADC = analogRead(CO2_PIN);
// ---------------------------------------------------
// CONVERT ADC TO SENSOR VALUE
// ---------------------------------------------------
float light = convertLight(lightADC);
float co2 = convertCO2(co2ADC);
// ---------------------------------------------------
// CALCULATE VOLTAGE
// ---------------------------------------------------
float lightVoltage = (lightADC / 4095.0) * 3.3;
float co2Voltage = (co2ADC / 4095.0) * 3.3;
// ===================================================
// DISPLAY SENSOR DATA
// ===================================================
Serial.println();
Serial.println("==========================================");
Serial.println(" SENSOR DATA");
Serial.println("==========================================");
Serial.print("Temperature : ");
Serial.print(temperature, 1);
Serial.println(" °C");
Serial.print("Humidity : ");
Serial.print(humidity, 1);
Serial.println(" %");
Serial.println("------------------------------------------");
Serial.print("Light ADC : ");
Serial.println(lightADC);
Serial.print("Light Volt : ");
Serial.print(lightVoltage, 2);
Serial.println(" V");
Serial.print("Light : ");
Serial.print(light, 1);
Serial.println(" Lux");
Serial.println("------------------------------------------");
Serial.print("CO2 ADC : ");
Serial.println(co2ADC);
Serial.print("CO2 Volt : ");
Serial.print(co2Voltage, 2);
Serial.println(" V");
Serial.print("CO2 : ");
Serial.print(co2, 1);
Serial.println(" ppm");
// ===================================================
// DECISION TREE
// ===================================================
bool water = decisionTree(
temperature,
humidity,
light,
co2
);
Serial.println("------------------------------------------");
// ===================================================
// CONTROL WATER PUMP
// ===================================================
if (water) {
Serial.println("DECISION : OPEN WATER");
pumpON();
Serial.println("PUMP : ON");
} else {
Serial.println("DECISION : CLOSE WATER");
pumpOFF();
Serial.println("PUMP : OFF");
}
Serial.println("==========================================");
// อ่านค่าใหม่ทุก 3 วินาที
delay(3000);
}
ตารางสรุปการต่อวงจร
| ลำดับ | อุปกรณ์ | หน้าที่ | GPIO ESP32 | ประเภทขา | การต่อ |
|---|---|---|---|---|---|
| 1 | DHT22 | วัดอุณหภูมิและความชื้น | GPIO 4 | Digital Input | DATA → GPIO 4 |
| 2 | Potentiometer #1 | จำลองค่า Light | GPIO 34 | Analog Input | Wiper → GPIO 34 |
| 3 | Potentiometer #2 | จำลองค่า CO₂ | GPIO 35 | Analog Input | Wiper → GPIO 35 |
| 4 | Relay Module | ควบคุมปั๊มน้ำ | GPIO 26 | Digital Output | IN → GPIO 26 |
| 5 | LED | แสดงสถานะปั๊มน้ำ | GPIO 27 | Digital Output | Anode → GPIO 27 ผ่าน R |
| 6 | Water Pump | รดน้ำต้นไม้ | ผ่าน Relay | — | Relay → Pump |

