การประยุกต์ใช้ปัญญาประดิษฐ์ด้วยเทคนิค Decision Tree สำหรับระบบรดน้ำอัจฉริยะใน Smart Farm

การประยุกต์ใช้ปัญญาประดิษฐ์ด้วยเทคนิค 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. วัตถุประสงค์

  1. เพื่อพัฒนาแบบจำลองการตัดสินใจด้วยเทคนิค Decision Tree สำหรับจำแนกสถานะการเปิดหรือปิดระบบรดน้ำจากข้อมูลสภาพแวดล้อม ได้แก่ อุณหภูมิ ความชื้น ความเข้มแสง และปริมาณก๊าซคาร์บอนไดออกไซด์
  2. เพื่อศึกษากระบวนการสร้างกฎการตัดสินใจจาก Decision Tree โดยพิจารณาค่าความไม่แน่นอน (Entropy) และค่าการลดความไม่แน่นอน (Information Gain) สำหรับกำหนดลำดับของโหนดในการตัดสินใจ
  3. เพื่อพัฒนาระบบรดน้ำอัจฉริยะด้วย Arduino/ESP32 ที่สามารถรับข้อมูลจากเซนเซอร์ ประมวลผลตามกฎของ Decision Tree และควบคุมการเปิด–ปิดปั๊มน้ำโดยอัตโนมัติ
  4. เพื่อประเมินประสิทธิภาพของระบบรดน้ำอัจฉริยะ จากความถูกต้องของการตัดสินใจเปิด–ปิดน้ำตามแบบจำลอง 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

ตัวอย่าง

RecordTempHumidityLightCO₂Water
12141920691ปิด
231559941474ปิด
333715481413ปิด
43786550640ปิด
53239524936เปิด
624543161380ปิด
72341496483เปิด
83958141478เปิด
92044355850เปิด
103575922514เปิด

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 คืออะไร?

สมมติเรามีข้อมูล

HumidityWater
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 ตัว

  1. อุณหภูมิ
  2. ความชื้น
  3. ความเข้มแสง
  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 ได้ผลโดยประมาณดังนี้

FeatureBest ThresholdInformation Gain
ความชื้น49.5%0.289
CO₂1293 ppm0.112
อุณหภูมิ34.5°C0.051
ความเข้มแสง597.5 Lux0.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เงื่อนไขหลักผลลัพธ์
1Humidity ≤ 49.5 AND Temperature ≤ 28.5 AND Light ≤ 887เปิดน้ำ
2Humidity ≤ 49.5 AND Temperature ≤ 28.5 AND Light > 887ปิดน้ำ
3Humidity ≤ 49.5 AND 28.5 < Temperature ≤ 29.5ปิดน้ำ
4Humidity ≤ 49.5 AND Temperature > 29.5เปิดน้ำ
549.5 < Humidity ≤ 75.5 AND Temperature ≤ 33.5 AND CO₂ ≤ 1274.5 AND Light ≤ 775.5 AND CO₂ ≤ 472ปิดน้ำ
649.5 < Humidity ≤ 75.5 AND Temperature ≤ 33.5 AND 472 < CO₂ ≤ 1274.5 AND Light ≤ 380.5 AND Humidity ≤ 65.5 AND CO₂ ≤ 531เปิดน้ำ
749.5 < Humidity ≤ 75.5 AND Temperature ≤ 33.5 AND 472 < CO₂ ≤ 1274.5 AND Light ≤ 380.5 AND Humidity ≤ 65.5 AND CO₂ > 531ปิดน้ำ
849.5 < Humidity ≤ 75.5 AND Temperature ≤ 33.5 AND 472 < CO₂ ≤ 1274.5 AND Light ≤ 380.5 AND Humidity > 65.5เปิดน้ำ
949.5 < Humidity ≤ 75.5 AND Temperature ≤ 33.5 AND 472 < CO₂ ≤ 1274.5 AND 380.5 < Light ≤ 775.5 AND Temperature ≤ 21.5เปิดน้ำ
1049.5 < Humidity ≤ 75.5 AND Temperature ≤ 33.5 AND 472 < CO₂ ≤ 1274.5 AND 380.5 < Light ≤ 775.5 AND Temperature > 21.5ปิดน้ำ
1149.5 < Humidity ≤ 75.5 AND Temperature ≤ 33.5 AND CO₂ ≤ 1274.5 AND Light > 775.5เปิดน้ำ
1249.5 < Humidity ≤ 75.5 AND Temperature ≤ 33.5 AND CO₂ > 1274.5ปิดน้ำ
1349.5 < Humidity ≤ 75.5 AND Temperature > 33.5 AND Humidity ≤ 60เปิดน้ำ
1449.5 < Humidity ≤ 75.5 AND Temperature > 33.5 AND Humidity > 60 AND Light ≤ 629.5 AND CO₂ ≤ 509เปิดน้ำ
1549.5 < Humidity ≤ 75.5 AND Temperature > 33.5 AND Humidity > 60 AND Light ≤ 629.5 AND 509 < CO₂ ≤ 1344.5ปิดน้ำ
1649.5 < Humidity ≤ 75.5 AND Temperature > 33.5 AND Humidity > 60 AND Light ≤ 629.5 AND 1344.5 < CO₂ ≤ 1366.5เปิดน้ำ
1749.5 < Humidity ≤ 75.5 AND Temperature > 33.5 AND Humidity > 60 AND Light ≤ 629.5 AND CO₂ > 1366.5ปิดน้ำ
1849.5 < Humidity ≤ 75.5 AND Temperature > 33.5 AND Humidity > 60 AND Light > 629.5เปิดน้ำ
19Humidity > 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ประเภทขาการต่อ
1DHT22วัดอุณหภูมิและความชื้นGPIO 4Digital InputDATA → GPIO 4
2Potentiometer #1จำลองค่า LightGPIO 34Analog InputWiper → GPIO 34
3Potentiometer #2จำลองค่า CO₂GPIO 35Analog InputWiper → GPIO 35
4Relay Moduleควบคุมปั๊มน้ำGPIO 26Digital OutputIN → GPIO 26
5LEDแสดงสถานะปั๊มน้ำGPIO 27Digital OutputAnode → GPIO 27 ผ่าน R
6Water Pumpรดน้ำต้นไม้ผ่าน RelayRelay → Pump