หน้าหลัก รายวิชาคอมพิวเตอร์ 31910-2012 อินเทอร์เน็ตเพื่อสรรพสิ่ง (ปวส.)

31910-2012 อินเทอร์เน็ตเพื่อสรรพสิ่ง (IoT & Arduino)

หลักสูตรประกาศนียบัตรวิชาชีพชั้นสูง (ปวส.) สาขาวิชาเทคโนโลยีสารสนเทศ / อิเล็กทรอนิกส์ สำนักงานคณะกรรมการการอาชีวศึกษา (สอศ.) รหัสวิชา 31910-2012 จำนวน 3.0 หน่วยกิต ปูพื้นฐานสถาปัตยกรรมระบบสมองกลฝังตัว Arduino & ESP32, การประมวลผลสัญญาณเซนเซอร์, ระบบขับเร้า, โปรโตคอล MQTT / LINE Notify / Web Server และคลาวด์ไอโอที พร้อมเอกสารวิชาการส่งงาน ว.PA ครบวงจร

สมรรถนะวิชาชีพ หลักสูตร ปวส. (สอศ.)
4 หน่วยการเรียนรู้หลัก
16 บทเรียนเจาะลึก
16 โค้ดตัวอย่าง (.ino)
8 ใบงาน & รูบริกส์ ว.PA
4
หน่วยการเรียนรู้เข้มข้น
16
บทเรียนพร้อมคำอธิบาย & โค้ด
8
ใบงานฝึกปฏิบัติการ & รูบริกส์
100%
ตรงตามสมรรถนะ ปวส. (สอศ.)
ศูนย์รวมสื่อการสอนวิชาการและเอกสารส่งงาน (ว.PA / สมรรถนะ ปวส.)
รหัสวิชา 31910-2012 ปวส.
ประมวลรายวิชา (Syllabus)
แผนการจัดการเรียนรู้ 18 สัปดาห์ ครอบคลุม 4 หน่วยการเรียนรู้ วัตถุประสงค์เชิงพฤติกรรม K-P-A เกณฑ์การวัดและประเมินผลตามมาตรฐาน สอศ.
ชุดใบงาน & รูบริกส์ 8 ชุด
ชุดใบงานฝึกปฏิบัติการทดลองวงจร IoT, Arduino, เซนเซอร์ และระบบคลาวด์ พร้อมตารางประเมินผลรูบริกส์ (Rubrics) สำหรับใช้บันทึกผลงานลงแฟ้มสะสมงาน ว.PA
ซอร์สโค้ด Arduino (.ino)
รวมไฟล์สเก็ตช์โค้ดภาษา C/C++ ของทั้ง 16 บทเรียน บีบอัดเป็นไฟล์ ZIP พร้อมนำเข้าสู่โปรแกรม Arduino IDE 2.x ทดสอบได้ทันที
รายการอุปกรณ์ฮาร์ดแวร์ (BOM)
ตารางอุปกรณ์ บอร์ดไมโครคอนโทรลเลอร์ เซนเซอร์ และอุปกรณ์ขับเร้าที่ต้องใช้ประกอบการเรียนการสอน สำหรับครูผู้สอนจัดซื้อและเตรียมห้องแล็บ

1 หน่วยที่ 1: สถาปัตยกรรมไมโครคอนโทรลเลอร์และระบบนิเวศ Arduino

เจาะลึกฮาร์ดแวร์ไมโครคอนโทรลเลอร์ ATmega328P และ ESP32, โครงสร้างโค้ด C/C++, ระบบเวลา Multitasking, การจัดการสัญญาณ Digital/Analog

หน่วยที่ 1 • บทที่ 1

1. สถาปัตยกรรมไมโครคอนโทรลเลอร์และระบบเวลา Non-blocking

Microcontroller Architecture & Non-blocking Multitasking (millis vs delay)
3 คาบ (135 นาที)
K: อธิบายความแตกต่างของ delay() และ millis() P: เขียนโปรแกรมควบคุมจังหวะเวลาแบบ Multitasking A: ความรอบคอบในการป้องกันปัญหาเวลาโอเวอร์โฟลว์
สาระการเรียนรู้เชิงวิชาการ: ไมโครคอนโทรลเลอร์สมัยใหม่เช่น ESP32 (Xtensa Dual-Core 32-bit 240MHz) หรือ ATmega328P (8-bit 16MHz) ทำงานตามวงรอบคำสั่งของนาฬิกา ในการเขียนโปรแกรมควบคุมอุปกรณ์ภายนอก การเรียกใช้ฟังก์ชัน delay(ms) จะทำให้ CPU ถูกกักขังอยู่ในวงรอบว่าง (Busy-wait loop) ไม่สามารถตอบสนองต่อเซนเซอร์หรือเครือข่ายได้ ในระบบงานจริงระดับ ปวส. จึงต้องเปลี่ยนมาใช้แนวคิด State Machine และฟังก์ชัน millis() ซึ่งคืนค่าจำนวนมิลลิวินาทีที่เปิดเครื่องขึ้นมา ทำให้สามารถจัดการงานหลายอย่างพร้อมกัน (Non-blocking Co-operative Multitasking)
  • โครงสร้างหลัก: setup() ทำงานเพียงรอบเดียวเพื่อกำหนดโหมดขาพอร์ต และ loop() ทำงานวนรอบซ้ำอย่างต่อเนื่อง
  • ชนิดข้อมูลเวลา: ต้องประกาศตัวแปรเก็บค่าเวลาเป็น unsigned long (32-bit) เพื่อรองรับเวลาสะสมต่อเนื่องได้นานถึง ~49.7 วันก่อนที่ค่าจะวนกลับมาเป็น 0
  • สูตรการเปรียบเทียบ: if (currentMillis - previousMillis >= interval) ป้องกันปัญหา Rollover ได้อย่างสมบูรณ์
01_led_blink_timing.ino (Arduino C++)
// โค้ดตัวอย่างที่ 1: ควบคุมไฟกระพริบโดยไม่บล็อกซีพียู (Non-blocking Blink)
const int LED_PIN = 2;               // ขา LED บนบอร์ด (ESP32 คือ GPIO2, Uno คือขา 13)
const unsigned long INTERVAL = 1000; // รอบเวลา 1,000 มิลลิวินาที (1 วินาที)

unsigned long previousMillis = 0;    // ตัวแปรจำเวลาครั้งล่าสุด
int ledState = LOW;                  // สถานะหลอดไฟ

void setup() {
  pinMode(LED_PIN, OUTPUT);
  Serial.begin(115200);
  Serial.println("System Ready: Non-blocking LED Blinker");
}

void loop() {
  unsigned long currentMillis = millis();

  // ตรวจสอบว่าเวลาผ่านไปครบช่วงที่กำหนดหรือยัง
  if (currentMillis - previousMillis >= INTERVAL) {
    previousMillis = currentMillis;

    // สลับสถานะหลอดไฟ (Toggle)
    ledState = (ledState == LOW) ? HIGH : LOW;
    digitalWrite(LED_PIN, ledState);

    Serial.print("Timestamp: ");
    Serial.print(currentMillis);
    Serial.print(" ms | LED State: ");
    Serial.println(ledState == HIGH ? "ON" : "OFF");
  }

  // ซีพียูสามารถประมวลผลงานอื่น เช่น อ่านค่าเซนเซอร์ หรือรับข้อมูลเครือข่าย ในลูปนี้ได้ทันที
}
Serial Monitor Output (115200 baud)
System Ready: Non-blocking LED Blinker
Timestamp: 1000 ms | LED State: ON
Timestamp: 2000 ms | LED State: OFF
Timestamp: 3000 ms | LED State: ON
Timestamp: 4000 ms | LED State: OFF
Pro-Tip สำหรับวิศวกร IoT: หลีกเลี่ยงการใช้ long (Signed) ในการเก็บเวลา เพราะเมื่อค่าเวลาเกิน 2,147,483,647 ms (~24.8 วัน) จะเกิดเครื่องหมายลบ ส่งผลให้เงื่อนไข if เกิดข้อผิดพลาดและอุปกรณ์หยุดทำงานทันที!
ใบงานท้าทาย (Lab Challenge 1)
ให้นักศึกษาเพิ่ม LED สีเขียวที่ขา GPIO4 ให้กะพริบทุกๆ 350 ms และ LED สีแดงที่ขา GPIO2 ให้กะพริบทุกๆ 1000 ms พร้อมส่งสัญญาณ Heartbeat ผ่าน Serial ทุกๆ 5 วินาที โดยทั้งหมดต้องทำงานพร้อมกันใน loop() เดียวกันโดยไม่ใช้ delay()
unsigned long prevRed = 0, prevGreen = 0;
const unsigned long INT_RED = 1000, INT_GREEN = 350;
int stateRed = LOW, stateGreen = LOW;

void loop() {
  unsigned long now = millis();
  if (now - prevRed >= INT_RED) {
    prevRed = now;
    stateRed = !stateRed;
    digitalWrite(2, stateRed);
  }
  if (now - prevGreen >= INT_GREEN) {
    prevGreen = now;
    stateGreen = !stateGreen;
    digitalWrite(4, stateGreen);
  }
}
หน่วยที่ 1 • บทที่ 2

2. การสื่อสารพอร์ตโฟนอนุกรม และตัวถอดรหัสคำสั่ง

UART Serial Communication & String Command Parser
3 คาบ (135 นาที)
K: หลักการบัฟเฟอร์ข้อมูล UART และ Baud Rate P: เขียนโค้ด Tokenize และแยกคำสั่งควบคุม Relay A: ความถูกต้องของการตอบรับสัญญาณยืนยัน (ACK)
สาระการเรียนรู้เชิงวิชาการ: UART (Universal Asynchronous Receiver-Transmitter) เป็นมาตรฐานการสื่อสารแบบอนุกรมสองทาง (Full-Duplex) ระหว่างคอมพิวเตอร์และไมโครคอนโทรลเลอร์ ข้อมูลที่ส่งเข้ามาจะถูกพักไว้ใน Hardware Serial Ring Buffer (ขนาด 64 ไบต์) การประมวลผลคำสั่งจำเป็นต้องอ่านข้อมูลจนถึงจุดสิ้นสุดของข้อความ (End-of-line: \n หรือ \r) และตัดอักขระส่วนเกินด้วย trim() ก่อนส่งเข้าสู่ฟังก์ชันตรวจสอบเงื่อนไข
02_serial_comm_parser.ino (Arduino C++)
const int RELAY_PIN = 4;

void setup() {
  pinMode(RELAY_PIN, OUTPUT);
  digitalWrite(RELAY_PIN, LOW);
  
  Serial.begin(115200);
  while (!Serial) { ; }
  
  Serial.println("=========================================");
  Serial.println("  IoT Serial Command Parser System Ready ");
  Serial.println("  Commands: LED:ON | LED:OFF | STATUS    ");
  Serial.println("=========================================");
}

void loop() {
  if (Serial.available() > 0) {
    String cmd = Serial.readStringUntil('\n');
    cmd.trim(); // กำจัด \r หรือช่องว่างหัวท้าย
    
    if (cmd.equalsIgnoreCase("LED:ON")) {
      digitalWrite(RELAY_PIN, HIGH);
      Serial.println("[ACK] Relay Output is now ACTIVATED (ON)");
    } 
    else if (cmd.equalsIgnoreCase("LED:OFF")) {
      digitalWrite(RELAY_PIN, LOW);
      Serial.println("[ACK] Relay Output is now DEACTIVATED (OFF)");
    } 
    else if (cmd.equalsIgnoreCase("STATUS")) {
      int st = digitalRead(RELAY_PIN);
      Serial.print("[INFO] Current Relay State is: ");
      Serial.println(st == HIGH ? "ON" : "OFF");
      Serial.print("[INFO] System Uptime: ");
      Serial.print(millis() / 1000);
      Serial.println(" seconds");
    } 
    else {
      Serial.print("[ERR] Unknown Command: ");
      Serial.println(cmd);
    }
  }
}
Serial Interaction
[USER SENDS]: LED:ON
[ACK] Relay Output is now ACTIVATED (ON)
[USER SENDS]: STATUS
[INFO] Current Relay State is: ON
[INFO] System Uptime: 45 seconds
ใบงานท้าทาย (Lab Challenge 2)
พัฒนาระบบรับคำสั่งแบบมีพารามิเตอร์ เช่น SET:INTERVAL:500 หรือ SET:PWM:180 โดยใช้ฟังก์ชัน cmd.indexOf(':') และ cmd.substring() เพื่อแยกตัวเลขพารามิเตอร์ไปใช้งานจริง
if (cmd.startsWith("SET:PWM:")) {
  int val = cmd.substring(8).toInt();
  val = constrain(val, 0, 255);
  ledcWrite(0, val);
  Serial.printf("[ACK] PWM set to %d\n", val);
}
หน่วยที่ 1 • บทที่ 3

3. ดิจิทัลอินพุต-เอาต์พุต และการกำจัดสัญญาณกระดอน

Digital I/O, Internal Pull-up & Software Debouncing
3 คาบ (135 นาที)
K: วงจร Pull-up ภายในไมโครคอนโทรลเลอร์ P: ต่อวงจรสวิตช์ปุ่มกดและเขียนซอฟต์แวร์ Debounce A: ความตระหนักในปรากฏการณ์ Contact Bouncing
สาระการเรียนรู้เชิงวิชาการ: สวิตช์กลไก (Mechanical Tactile Switch) เมื่อถูกกด หน้าสัมผัสโลหะจะไม่แตะกันสนิทในทันที แต่จะเกิดการเด้งกระดอน (Bouncing) ในระดับ 5 - 50 มิลลิวินาที ทำให้ไมโครคอนโทรลเลอร์ที่มีความเร็วสูงอ่านค่าลอจิกเป็นกดซ้ำหลายสิบครั้ง การแก้ปัญหาทำได้ 2 แนวทางคือ ฮาร์ดแวร์ (RC Low-pass Filter) หรือ ซอฟต์แวร์ Debouncing โดยใช้ตัวแปรตรวจจับเวลาความนิ่งของสัญญาณก่อนยอมรับสถานะใหม่
03_digital_io_pullup_debounce.ino (Arduino C++)
const int BUTTON_PIN = 14; // สวิตช์ต่อกับขา GPIO14 และอีกขาลง GND
const int LED_PIN = 2;

int ledState = LOW;
int lastButtonReading = HIGH;
int buttonState = HIGH;
unsigned long lastDebounceTime = 0;
const unsigned long DEBOUNCE_DELAY = 50; // หน่วงเวลาตรวจสอบ 50ms

void setup() {
  pinMode(BUTTON_PIN, INPUT_PULLUP); // ใช้งานตัวต้านทานพูลอัปภายในชิป
  pinMode(LED_PIN, OUTPUT);
  digitalWrite(LED_PIN, ledState);
  Serial.begin(115200);
  Serial.println("Debounce Push Button System Ready");
}

void loop() {
  int reading = digitalRead(BUTTON_PIN);

  // เมื่อตรวจพบการเปลี่ยนแปลงของระดับสัญญาณ
  if (reading != lastButtonReading) {
    lastDebounceTime = millis();
  }

  // หากสัญญาณคงที่นานเกินค่า DEBOUNCE_DELAY ให้ยอมรับค่านั้น
  if ((millis() - lastDebounceTime) > DEBOUNCE_DELAY) {
    if (reading != buttonState) {
      buttonState = reading;
      
      // เมื่อปุ่มถูกกดลงจริง (Active LOW)
      if (buttonState == LOW) {
        ledState = !ledState;
        digitalWrite(LED_PIN, ledState);
        Serial.print("Valid Press Detected! Toggle State: ");
        Serial.println(ledState ? "HIGH" : "LOW");
      }
    }
  }

  lastButtonReading = reading;
}
ข้อควรรู้เกี่ยวกับขาพอร์ต ESP32: ขา GPIO 34, 35, 36, 39 บนบอร์ด ESP32 เป็นขาแบบ Input Only และไม่มีตัวต้านทาน Internal Pull-up/Pull-down หากต้องการต่อสวิตช์ที่ขาเหล่านี้ จะต้องต่อตัวต้านทาน 10k โอห์มภายนอกวงจรเสมอ
หน่วยที่ 1 • บทที่ 4

4. การอ่านสัญญาณแอนะล็อก และการขับสัญญาณ PWM

Analog-to-Digital Converter (ADC 12-bit) & Pulse-Width Modulation
3 คาบ (135 นาที)
K: ความละเอียด ADC 10-bit (Uno) vs 12-bit (ESP32) P: เทียบสเกลแรงดันและสร้างสัญญาณ PWM หรี่ไฟ LED A: ความแม่นยำในการเทียบเคียงสัดส่วนด้วยฟังก์ชัน map()
สาระการเรียนรู้เชิงวิชาการ: ตัวแปลงสัญญาณแอนะล็อกเป็นดิจิทัล (ADC) ทำหน้าที่แปลงแรงดันไฟฟ้าต่อเนื่อง (0 ถึง 3.3V บน ESP32) ให้เป็นตัวเลขดิจิทัล 12-bit (ค่า 0 ถึง 4095) ในทางกลับกัน ไมโครคอนโทรลเลอร์ไม่มีตัวแปลง DAC ทุกขา จึงใช้เทคนิค PWM (Pulse-Width Modulation) เพื่อปรับสัดส่วน Duty Cycle ของพัลส์ความถี่สูง จำลองเป็นแรงดันเฉลี่ยในการหรี่แสงหลอดไฟหรือควบคุมความเร็วมอเตอร์
04_analog_adc_pwm_dimmer.ino (Arduino C++)
const int POT_PIN = 34; // ขาต่อตัวต้านทานปรับค่าได้ (ADC1_CH6)
const int PWM_PIN = 18; // ขาขับสัญญาณ PWM ไปยัง LED

// การตั้งค่าฮาร์ดแวร์ PWM บน ESP32
const int PWM_FREQ = 5000;       // ความถี่ 5 kHz
const int PWM_CHANNEL = 0;       // ช่องสัญญาณหมายเลข 0
const int PWM_RESOLUTION = 8;    // ความละเอียด 8-bit (ค่า 0-255)

void setup() {
  Serial.begin(115200);
  ledcSetup(PWM_CHANNEL, PWM_FREQ, PWM_RESOLUTION);
  ledcAttachPin(PWM_PIN, PWM_CHANNEL);
  Serial.println("Analog ADC & PWM Dimmer System Initialized");
}

void loop() {
  int rawADC = analogRead(POT_PIN);         // อ่านค่า ADC (0 - 4095)
  float voltage = (rawADC / 4095.0) * 3.3;  // คำนวณเป็นแรงดันโวลต์

  // แปลงช่วงข้อมูล 0-4095 ให้เป็น 0-255 สำหรับ PWM Duty Cycle
  int dutyCycle = map(rawADC, 0, 4095, 0, 255);
  ledcWrite(PWM_CHANNEL, dutyCycle);

  Serial.print("Raw ADC: ");
  Serial.print(rawADC);
  Serial.print(" | Voltage: ");
  Serial.print(voltage, 2);
  Serial.print(" V | PWM Duty: ");
  Serial.println(dutyCycle);

  delay(100);
}

2 หน่วยที่ 2: เซนเซอร์ อุปกรณ์ขับเร้า และการแสดงผล (Sensors & Actuators)

การเชื่อมต่อทรานสดิวเซอร์ตรวจวัดสภาพแวดล้อมจริง การขับโหลดไฟฟ้ากำลัง และการสื่อสารผ่านบัส I2C แสดงผลผ่านกราฟิก OLED

หน่วยที่ 2 • บทที่ 5

5. เซนเซอร์วัดอุณหภูมิและความชื้นสัมพัทธ์ในอากาศ

Temperature & Relative Humidity Sensors (DHT11/DHT22)
3 คาบ (135 นาที)
K: โครงสร้างเซนเซอร์สารกึ่งตัวนำ และการส่งข้อมูล Single-bus P: อ่านค่าอุณหภูมิ/ความชื้น และคำนวณดัชนีความร้อน A: การจัดการข้อผิดพลาดข้อมูลสูญหาย (NaN Validation)
สาระการเรียนรู้เชิงวิชาการ: เซนเซอร์ตระกูล DHT ประกอบด้วยตัวต้านทานวัดความชื้นชนิดโพลีเมอร์ (Capacitive Humidity Sensor) และเทอร์มิสเตอร์ NTC วัดอุณหภูมิ มีไอซีขนาดเล็กแปลงสัญญาณส่งออกมาเป็นแพ็กเกจดิจิทัล 40 บิตผ่านสายเส้นเดียว (Single-Wire Two-way Communication) โดยรุ่น DHT22 มีความละเอียดและความเที่ยงตรงสูงกว่า DHT11 มาก (ความชื้น ±2-5%, อุณหภูมิ ±0.5°C)
05_dht_temp_humidity.ino (Arduino C++)
#include "DHT.h"

#define DHTPIN 4       // ขาสัญญาณ Data ต่อกับ GPIO4
#define DHTTYPE DHT22  // ชนิดเซนเซอร์ DHT22 (AM2302)

DHT dht(DHTPIN, DHTTYPE);

void setup() {
  Serial.begin(115200);
  Serial.println("DHT22 Temperature & Humidity Sensor Test");
  dht.begin();
}

void loop() {
  delay(2000); // DHT22 ต้องการช่วงเวลาอ่านค่าอย่างน้อย 2 วินาที

  float humidity = dht.readHumidity();
  float temperature = dht.readTemperature(); // องศาเซลเซียส

  // ตรวจสอบความถูกต้องของข้อมูล (ป้องกันค่าว่างหรือ NaN)
  if (isnan(humidity) || isnan(temperature)) {
    Serial.println("[ERROR] Failed to read from DHT sensor! Check wiring.");
    return;
  }

  // คำนวณ Heat Index (ดัชนีความร้อนที่ร่างกายสัมผัสได้จริง)
  float hic = dht.computeHeatIndex(temperature, humidity, false);

  Serial.print("ความชื้น: ");
  Serial.print(humidity, 1);
  Serial.print(" %RH | อุณหภูมิ: ");
  Serial.print(temperature, 1);
  Serial.print(" *C | ดัชนีความร้อน: ");
  Serial.print(hic, 1);
  Serial.println(" *C");
}
หน่วยที่ 2 • บทที่ 6

6. การวัดระยะทางด้วยคลื่นเสียงสะท้อนอัลตราโซนิก

Ultrasonic Distance Sensor (HC-SR04) & Buzzer Alarm
3 คาบ (135 นาที)
สาระการเรียนรู้เชิงวิชาการ: เซนเซอร์ HC-SR04 ส่งคลื่นเสียงความถี่สูง 40 kHz ออกไปทางลำโพงส่ง (Trigger) แล้วจับเวลาจนกระทั่งคลื่นสะท้อนกลับมาเข้าสู่ไมโครโฟนรับ (Echo) ด้วยความเร็วเสียงในอากาศประมาณ 340 เมตร/วินาที (หรือ 0.034 เซนติเมตร/ไมโครวินาที) เมื่อนำระยะเวลาไป-กลับมาหาร 2 จะได้ระยะห่างของวัตถุได้อย่างแม่นยำ
06_ultrasonic_distance_hcsr04.ino (Arduino C++)
const int TRIG_PIN = 5;
const int ECHO_PIN = 18;
const int BUZZER_PIN = 19;

void setup() {
  Serial.begin(115200);
  pinMode(TRIG_PIN, OUTPUT);
  pinMode(ECHO_PIN, INPUT);
  pinMode(BUZZER_PIN, OUTPUT);
  Serial.println("Ultrasonic Radar & Proximity Alarm Ready");
}

void loop() {
  // ยิงพัลส์ Trigger กว้าง 10 ไมโครวินาที
  digitalWrite(TRIG_PIN, LOW);
  delayMicroseconds(2);
  digitalWrite(TRIG_PIN, HIGH);
  delayMicroseconds(10);
  digitalWrite(TRIG_PIN, LOW);

  // วัดความกว้างพัลส์สะท้อนกลับ Echo (หน่วยไมโครวินาที)
  long duration = pulseIn(ECHO_PIN, HIGH);
  float distanceCm = (duration * 0.034) / 2.0;

  Serial.print("ระยะห่างของวัตถุ: ");
  Serial.print(distanceCm, 1);
  Serial.println(" cm");

  // ระบบแจ้งเตือนเมื่อวัตถุเข้ามาใกล้ต่ำกว่า 15 ซม.
  if (distanceCm > 0 && distanceCm < 15.0) {
    digitalWrite(BUZZER_PIN, HIGH);
  } else {
    digitalWrite(BUZZER_PIN, LOW);
  }

  delay(200);
}
หน่วยที่ 2 • บทที่ 7

7. การควบคุมโหลดไฟฟ้ากระแสสลับด้วยโมดูลรีเลย์

AC 220V Load Control via Opto-isolated Relay Module
3 คาบ (135 นาที)
สาระการเรียนรู้เชิงวิชาการ: ในระบบ IoT อุตสาหกรรม การสั่งการเครื่องใช้ไฟฟ้าหรือมอเตอร์ 220V ต้องใช้ Optocoupler Relay Module ซึ่งใช้แสง (Photodiode + Phototransistor) ในการตัดแยกสัญญาณวงจรแรงดันต่ำ (3.3V/5V) ออกจากวงจรแรงดันสูง เพื่อป้องกันแรงดันย้อนกลับ (Back EMF หรือ Surge Voltage) มาทำลายชิปไมโครคอนโทรลเลอร์
07_relay_ac_load_control.ino (Arduino C++)
const int RELAY_PIN = 23;

// โมดูลรีเลย์ส่วนใหญ่เป็นวงจร Active LOW
#define RELAY_ON  LOW
#define RELAY_OFF HIGH

void setup() {
  pinMode(RELAY_PIN, OUTPUT);
  digitalWrite(RELAY_PIN, RELAY_OFF); // สถานะเริ่มต้น ปิดรีเลย์เสมอเพื่อความปลอดภัย
  Serial.begin(115200);
  Serial.println("Optocoupler Relay Controller Online");
}

void loop() {
  Serial.println("[ACTION] สั่งเปิดโหลดไฟฟ้า (Relay ON)");
  digitalWrite(RELAY_PIN, RELAY_ON);
  delay(5000);

  Serial.println("[ACTION] สั่งปิดโหลดไฟฟ้า (Relay OFF)");
  digitalWrite(RELAY_PIN, RELAY_OFF);
  delay(5000);
}
คำเตือนความปลอดภัยทางไฟฟ้า: การต่อวงจรไฟ AC 220V ต้องตัดเบรกเกอร์ต้นทางทุกครั้ง ใช้ไขควงวัดไฟตรวจสอบก่อนสัมผัส และห้ามทดลองคนเดียวโดยไม่มีครูผู้สอนหรืออาจารย์ประจำห้องปฏิบัติการดูแล
หน่วยที่ 2 • บทที่ 8

8. การสื่อสารบัส I2C และแดชบอร์ดจอแสดงผล OLED

I2C Communication Protocol & SSD1306 OLED Graphic Dashboard
3 คาบ (135 นาที)
สาระการเรียนรู้เชิงวิชาการ: I2C (Inter-Integrated Circuit) เป็นบัสสื่อสารความเร็วสูงแบบซิงโครนัสที่ใช้สายเพียง 2 เส้น คือ SDA (Serial Data) และ SCL (Serial Clock) รองรับการเชื่อมต่ออุปกรณ์ Master และ Slave หลายตัวในเส้นทางเดียวกันผ่านระบบแอดเดรส 7-bit (จอ OLED SSD1306 มักใช้แอดเดรส 0x3C หรือ 0x3D)
08_oled_i2c_dashboard.ino (Arduino C++)
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
#define OLED_RESET    -1
#define SCREEN_ADDRESS 0x3C

Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);

void setup() {
  Serial.begin(115200);
  if(!display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS)) {
    Serial.println(F("OLED SSD1306 allocation failed"));
    for(;;);
  }
  display.clearDisplay();
  display.setTextColor(SSD1306_WHITE);
}

void loop() {
  float fakeTemp = 28.5 + (random(0, 50) / 10.0);
  float fakeHum = 65.0 + (random(0, 100) / 10.0);

  display.clearDisplay();

  // วาดหัวข้อ Dashboard
  display.setTextSize(1);
  display.setCursor(10, 0);
  display.println(F("--- KRUBAS IoT NODE ---"));
  display.drawLine(0, 10, 128, 10, SSD1306_WHITE);

  // อุณหภูมิ
  display.setCursor(0, 20);
  display.print(F("TEMP: "));
  display.setTextSize(2);
  display.setCursor(42, 16);
  display.print(fakeTemp, 1);
  display.setTextSize(1);
  display.print(F(" C"));

  // ความชื้น
  display.setCursor(0, 42);
  display.print(F("HUM : "));
  display.setTextSize(2);
  display.setCursor(42, 38);
  display.print(fakeHum, 1);
  display.setTextSize(1);
  display.print(F(" %"));

  display.drawLine(0, 55, 128, 55, SSD1306_WHITE);
  display.setCursor(15, 57);
  display.print(F("STATUS: ONLINE [OK]"));

  display.display();
  delay(1000);
}

3 หน่วยที่ 3: ระบบฝังตัวสื่อสารไร้สายและโปรโตคอล IoT (Wireless & IoT Protocols)

โครงข่าย Wi-Fi 802.11 b/g/n, สถาปัตยกรรม RESTful API, การแจ้งเตือน LINE Notify, โปรโตคอล MQTT และ Web Server ฝังในชิป

หน่วยที่ 3 • บทที่ 9

9. การเชื่อมต่อโครงข่ายไร้สาย Wi-Fi และระบบ Auto-reconnect

ESP32 Wi-Fi Station Mode, DHCP, RSSI & Fault-Tolerant Reconnection
3 คาบ (135 นาที)
สาระการเรียนรู้เชิงวิชาการ: ชิป ESP32 มีโมเดม Wi-Fi และสแต็กโพรโทคอล TCP/IP แบบฮาร์ดแวร์ในตัว สามารถทำงานได้ 3 โหมด: WIFI_STA (Station) เชื่อมต่อกับ Access Point เหมือนสมาร์ตโฟน, WIFI_AP (SoftAP) กระจายสัญญาณเป็นเร้าเตอร์เอง, และ WIFI_AP_STA (Dual Mode) ในระบบ IoT อุตสาหกรรม การเชื่อมต่ออาจหลุดได้จากคลื่นรบกวน จึงจำเป็นต้องมีกลไก Auto-reconnect เพื่อกู้คืนสถานะการเชื่อมต่อโดยอัตโนมัติ
09_esp32_wifi_station_mode.ino (ESP32 C++)
#include <WiFi.h>

const char* ssid = "YOUR_WIFI_SSID";
const char* password = "YOUR_WIFI_PASSWORD";

void connectToWiFi() {
  Serial.print("Connecting to Wi-Fi: ");
  Serial.println(ssid);
  WiFi.mode(WIFI_STA);
  WiFi.begin(ssid, password);

  int attempts = 0;
  while (WiFi.status() != WL_CONNECTED && attempts < 25) {
    delay(500);
    Serial.print(".");
    attempts++;
  }

  if (WiFi.status() == WL_CONNECTED) {
    Serial.println("\n>>> Wi-Fi Connected Successfully! <<<");
    Serial.print("IP Address: ");
    Serial.println(WiFi.localIP());
    Serial.print("Subnet Mask: ");
    Serial.println(WiFi.subnetMask());
    Serial.print("Gateway IP: ");
    Serial.println(WiFi.gatewayIP());
    Serial.print("Signal (RSSI): ");
    Serial.print(WiFi.RSSI());
    Serial.println(" dBm");
  } else {
    Serial.println("\n[ERROR] Connection Timeout!");
  }
}

void setup() {
  Serial.begin(115200);
  connectToWiFi();
}

void loop() {
  // ตรวจจับการหลุดเชื่อมต่อเพื่อต่อใหม่โดยอัตโนมัติ
  if (WiFi.status() != WL_CONNECTED) {
    Serial.println("Wi-Fi connection lost. Attempting reconnect...");
    WiFi.disconnect();
    WiFi.reconnect();
    delay(5000);
  }
  delay(1000);
}
หน่วยที่ 3 • บทที่ 10

10. ระบบส่งการแจ้งเตือนความผิดปกติเข้า LINE Notify

Emergency Alert System via LINE Notify REST API (HTTPS POST)
3 คาบ (135 นาที)
สาระการเรียนรู้เชิงวิชาการ: LINE Notify เป็นบริการ Webhook API ที่เปิดให้ระบบภายนอกส่งข้อความเข้ากลุ่ม LINE ได้โดยตรงผ่านโปรโตคอล HTTPS (พอร์ต 443) ด้วยเมธอด POST โดยใช้มาตรฐาน Bearer Token สำหรับการยืนยันตัวตน บน ESP32 เราใช้คลาส HTTPClient ร่วมกับ WiFiClientSecure เพื่อเข้ารหัสความปลอดภัย SSL/TLS
10_iot_line_notify_alert.ino (ESP32 C++)
#include <WiFi.h>
#include <HTTPClient.h>
#include <WiFiClientSecure.h>

const char* ssid = "YOUR_WIFI_SSID";
const char* password = "YOUR_WIFI_PASSWORD";
const char* LINE_TOKEN = "YOUR_LINE_NOTIFY_TOKEN";

void sendLineAlert(String message) {
  if (WiFi.status() == WL_CONNECTED) {
    WiFiClientSecure client;
    client.setInsecure(); // ละเว้นการตรวจสอบ Root CA ในการทดลอง

    HTTPClient https;
    if (https.begin(client, "https://notify-api.line.me/api/notify")) {
      https.addHeader("Content-Type", "application/x-www-form-urlencoded");
      https.addHeader("Authorization", String("Bearer ") + LINE_TOKEN);

      String postData = "message=" + message;
      int httpCode = https.POST(postData);

      if (httpCode > 0) {
        Serial.print("[LINE] Response HTTP Code: ");
        Serial.println(httpCode);
      } else {
        Serial.print("[LINE] Error: ");
        Serial.println(https.errorToString(httpCode).c_str());
      }
      https.end();
    }
  }
}

void setup() {
  Serial.begin(115200);
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); }
  Serial.println("\nWiFi Connected!");

  sendLineAlert("🔥 ระบบเตือนภัย IoT ปวส.: ตรวจพบอุณหภูมิห้องเซิร์ฟเวอร์สูงผิดปกติ 43.5 *C!");
}

void loop() {
  delay(60000);
}
หน่วยที่ 3 • บทที่ 11

11. การสื่อสาร Telemetry ด้วยโปรโตคอล MQTT

MQTT Protocol (Publish / Subscribe) via HiveMQ Public Broker
3 คาบ (135 นาที)
สาระการเรียนรู้เชิงวิชาการ: MQTT (Message Queuing Telemetry Transport) เป็นโปรโตคอลการสื่อสารข้อมูลแบบน้ำหนักเบา (Lightweight) บนมาตรฐาน ISO/IEC 20922 ที่ออกแบบมาเพื่ออุปกรณ์ที่มีทรัพยากรจำกัดและเครือข่ายความเร็วต่ำ ใช้รูปแบบ Publish/Subscribe ผ่าน Broker ศูนย์กลาง มีขนาด Header เริ่มต้นเพียง 2 ไบต์ ช่วยประหยัดแบนด์วิดท์และพลังงานได้ดีกว่า HTTP ถึง 10-50 เท่า
11_mqtt_pubsub_hivemq.ino (ESP32 C++)
#include <WiFi.h>
#include <PubSubClient.h>

const char* ssid = "YOUR_WIFI_SSID";
const char* password = "YOUR_WIFI_PASSWORD";
const char* mqtt_server = "broker.hivemq.com";
const int mqtt_port = 1883;

const char* TOPIC_DATA = "krubas/iot/sensor/data";
const char* TOPIC_CONTROL = "krubas/iot/control/relay";

WiFiClient espClient;
PubSubClient client(espClient);

void callback(char* topic, byte* payload, unsigned int length) {
  String msg = "";
  for (int i = 0; i < length; i++) { msg += (char)payload[i]; }
  Serial.print("Message arrived [");
  Serial.print(topic);
  Serial.print("]: ");
  Serial.println(msg);

  if (String(topic) == TOPIC_CONTROL) {
    if (msg == "ON") {
      digitalWrite(2, HIGH);
      Serial.println("[MQTT ACTUATOR] Turned ON");
    } else if (msg == "OFF") {
      digitalWrite(2, LOW);
      Serial.println("[MQTT ACTUATOR] Turned OFF");
    }
  }
}

void reconnect() {
  while (!client.connected()) {
    Serial.print("Attempting MQTT connection...");
    String clientId = "ESP32Client-" + String(random(0xffff), HEX);
    if (client.connect(clientId.c_str())) {
      Serial.println("connected!");
      client.subscribe(TOPIC_CONTROL);
    } else {
      Serial.print("failed, rc=");
      Serial.print(client.state());
      delay(5000);
    }
  }
}

void setup() {
  pinMode(2, OUTPUT);
  Serial.begin(115200);
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); }
  client.setServer(mqtt_server, mqtt_port);
  client.setCallback(callback);
}

void loop() {
  if (!client.connected()) { reconnect(); }
  client.loop();

  static unsigned long lastMsg = 0;
  if (millis() - lastMsg > 5000) {
    lastMsg = millis();
    float temp = 29.2;
    String payload = "{\"temp\":" + String(temp) + ",\"status\":\"ONLINE\"}";
    client.publish(TOPIC_DATA, payload.c_str());
    Serial.println("Published to MQTT: " + payload);
  }
}
หน่วยที่ 3 • บทที่ 12

12. การสร้างเว็บเซิร์ฟเวอร์แบบอะซิงโครนัสบน ESP32

ESP32 Asynchronous Web Server & Interactive Browser UI
3 คาบ (135 นาที)
สาระการเรียนรู้เชิงวิชาการ: ESPAsyncWebServer รองรับการเชื่อมต่อแบบมัลติคอนเนกชัน (Multi-connections) โดยไม่ทำให้ระบบสะดุด เหมาะสำหรับการส่งหน้าเว็บ HTML/CSS/JavaScript ให้แก่เบราว์เซอร์ และรับคำสั่งสลับการทำงานของพอร์ตแบบ Real-time โดยไม่ต้องรีเฟรชหน้าจอ
12_esp32_async_webserver.ino (ESP32 C++)
#include <WiFi.h>
#include <ESPAsyncWebServer.h>

const char* ssid = "YOUR_WIFI_SSID";
const char* password = "YOUR_WIFI_PASSWORD";

const int RELAY_PIN = 4;
bool relayState = false;

AsyncWebServer server(80);

const char index_html[] PROGMEM = R"rawliteral(
<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <title>ESP32 IoT Dashboard</title>
  <style>
    body { font-family: Arial; text-align: center; margin-top: 50px; background: #0f172a; color: #fff; }
    .btn { display: inline-block; padding: 15px 30px; font-size: 20px; border-radius: 8px; cursor: pointer; text-decoration: none; font-weight: bold; }
    .btn-on { background-color: #22c55e; color: white; }
    .btn-off { background-color: #ef4444; color: white; }
  </style>
</head>
<body>
  <h2>ESP32 Web Server Relay Controller</h2>
  <p>Status: %STATE%</p>
  <a href="/toggle" class="btn %BTN_CLASS%">TOGGLE POWER</a>
</body>
</html>
)rawliteral";

String processor(const String& var){
  if(var == "STATE"){ return relayState ? "ACTIVATED (ON)" : "DEACTIVATED (OFF)"; }
  if(var == "BTN_CLASS"){ return relayState ? "btn-off" : "btn-on"; }
  return String();
}

void setup() {
  Serial.begin(115200);
  pinMode(RELAY_PIN, OUTPUT);
  digitalWrite(RELAY_PIN, LOW);

  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); }
  Serial.println("\nServer IP: " + WiFi.localIP().toString());

  server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){
    request->send_P(200, "text/html", index_html, processor);
  });

  server.on("/toggle", HTTP_GET, [](AsyncWebServerRequest *request){
    relayState = !relayState;
    digitalWrite(RELAY_PIN, relayState ? HIGH : LOW);
    request->redirect("/");
  });

  server.begin();
}

void loop() { }

4 หน่วยที่ 4: แพลตฟอร์มคลาวด์ แดชบอร์ด และโครงงาน IoT ประยุกต์

การบูรณาการระบบกับคลาวด์ชั้นนำ (Blynk, NETPIE, ThingSpeak) และโครงงานพัฒนาระบบฟาร์มอัจฉริยะอัตโนมัติเต็มรูปแบบ

หน่วยที่ 4 • บทที่ 13

13. การพัฒนาแอปมือถือสั่งงานด้วย Blynk IoT

Blynk IoT Platform, Virtual Pins & Mobile Dashboard Development
3 คาบ (135 นาที)
สาระการเรียนรู้เชิงวิชาการ: Blynk IoT เป็นแพลตฟอร์ม Low-code สำหรับสร้างแดชบอร์ดบนสมาร์ตโฟน (iOS/Android) และเว็บเบราว์เซอร์ โดยมีแกนหลักคือ Virtual Pins (V0 - V255) ทำหน้าที่เป็นช่องทางส่งตัวแปรระหว่างอุปกรณ์ฮาร์ดแวร์และวิดเจ็ตบนมือถือ ช่วยให้ไม่ต้องเขียนแอปพลิเคชันมือถือตั้งแต่ต้น
13_blynk_iot_virtual_pins.ino (ESP32 C++)
#define BLYNK_TEMPLATE_ID   "TMPLxxxxxx"
#define BLYNK_TEMPLATE_NAME "Smart Farm"
#define BLYNK_AUTH_TOKEN    "YOUR_BLYNK_TOKEN"

#include <WiFi.h>
#include <WiFiClient.h>
#include <BlynkSimpleEsp32.h>

char ssid[] = "YOUR_WIFI_SSID";
char pass[] = "YOUR_WIFI_PASSWORD";

const int PUMP_PIN = 5;
BlynkTimer timer;

// รับค่าจากปุ่มสวิตช์บนแอป Blynk (Virtual Pin V1)
BLYNK_WRITE(V1) {
  int value = param.asInt();
  digitalWrite(PUMP_PIN, value == 1 ? HIGH : LOW);
  Serial.println(value == 1 ? "[BLYNK] Pump ON" : "[BLYNK] Pump OFF");
}

// ฟังก์ชันส่งค่าเซนเซอร์ขึ้นคลาวด์ทุก 2 วินาที
void sendSensorData() {
  float soilMoisture = 68.5; // ตัวอย่างค่าเซนเซอร์
  Blynk.virtualWrite(V2, soilMoisture);
}

void setup() {
  pinMode(PUMP_PIN, OUTPUT);
  Serial.begin(115200);
  Blynk.begin(BLYNK_AUTH_TOKEN, ssid, pass);
  timer.setInterval(2000L, sendSensorData);
}

void loop() {
  Blynk.run();
  timer.run();
}
หน่วยที่ 4 • บทที่ 14

14. แพลตฟอร์มคลาวด์อุตสาหกรรม NETPIE 2020

Industrial IoT Platform: NETPIE 2020, Device Shadow & Data Feed
3 คาบ (135 นาที)
สาระการเรียนรู้เชิงวิชาการ: NETPIE (Network Platform for Internet of Everything) พัฒนาโดย NECTEC สวทช. เป็นแพลตฟอร์ม IoT มาตรฐานอุตสาหกรรม รองรับแนวคิด Device Shadow (สำเนาสถานะอุปกรณ์ในระบบคลาวด์) ทำให้สามารถอ่านและสั่งงานอุปกรณ์ได้แม้ในช่วงที่อุปกรณ์ขาดการเชื่อมต่อไปชั่วคราว
14_netpie2020_microgear.ino (ESP32 C++)
#include <WiFi.h>
#include <PubSubClient.h>

const char* ssid = "YOUR_WIFI_SSID";
const char* password = "YOUR_WIFI_PASSWORD";

const char* mqtt_server = "broker.netpie.io";
const int mqtt_port = 1883;
const char* client_id = "YOUR_DEVICE_ID";
const char* token = "YOUR_DEVICE_TOKEN";
const char* secret = "YOUR_DEVICE_SECRET";

WiFiClient espClient;
PubSubClient client(espClient);

void setup() {
  Serial.begin(115200);
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); }
  client.setServer(mqtt_server, mqtt_port);
}

void loop() {
  if (!client.connected()) {
    while (!client.connected()) {
      Serial.print("Connecting to NETPIE 2020...");
      if (client.connect(client_id, token, secret)) {
        Serial.println("connected!");
      } else {
        delay(3000);
      }
    }
  }
  client.loop();

  // อัปเดตข้อมูลสถานะเข้าสู่ Shadow
  static unsigned long lastTime = 0;
  if (millis() - lastTime > 10000) {
    lastTime = millis();
    String payload = "{\"data\":{\"temp\":31.4,\"humidity\":72.0}}";
    client.publish("@shadow/data/update", payload.c_str());
    Serial.println("Sent telemetry to NETPIE Shadow");
  }
}
หน่วยที่ 4 • บทที่ 15

15. ระบบบันทึกประวัติข้อมูลและพล็อตกราฟ ThingSpeak

Time-Series Data Logging, Analytics & Visualization with ThingSpeak
3 คาบ (135 นาที)
สาระการเรียนรู้เชิงวิชาการ: ThingSpeak ให้บริการจัดเก็บข้อมูลชนิด Time-series Data และเชื่อมโยงกับการวิเคราะห์ข้อมูลด้วย MATLAB เหมาะสำหรับการเก็บประวัติสภาพอากาศ คุณภาพน้ำ หรือการใช้พลังงานย้อนหลังเพื่อคำนวณค่าเฉลี่ย สถิติ และพยากรณ์แนวโน้ม
15_thingspeak_data_logger.ino (ESP32 C++)
#include <WiFi.h>
#include <HTTPClient.h>

const char* ssid = "YOUR_WIFI_SSID";
const char* password = "YOUR_WIFI_PASSWORD";
const char* thingspeak_api_key = "YOUR_WRITE_API_KEY";
const char* server = "http://api.thingspeak.com/update";

void setup() {
  Serial.begin(115200);
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); }
  Serial.println("WiFi Connected for ThingSpeak Logger");
}

void loop() {
  if (WiFi.status() == WL_CONNECTED) {
    HTTPClient http;
    float temp = 27.8;
    float lightLux = 850;

    String url = String(server) + "?api_key=" + thingspeak_api_key + 
                 "&field1=" + String(temp) + "&field2=" + String(lightLux);

    http.begin(url);
    int httpResponseCode = http.GET();

    if (httpResponseCode > 0) {
      Serial.print("ThingSpeak Update HTTP Code: ");
      Serial.println(httpResponseCode);
    } else {
      Serial.print("HTTP Error: ");
      Serial.println(httpResponseCode);
    }
    http.end();
  }
  delay(20000); // ส่งข้อมูลทุก 20 วินาทีตามข้อกำหนด ThingSpeak
}
หน่วยที่ 4 • บทที่ 16

16. โครงงานบูรณาการระบบโรงเรือนอัจฉริยะแบบอัตโนมัติ

Capstone IoT Project: Smart Greenhouse Automation & Multi-platform Integration
6 คาบ (270 นาที)
สาระการเรียนรู้เชิงวิชาการ: โครงงานปลายภาคเรียนบูรณาการความรู้ทั้ง 4 หน่วยการเรียนรู้ ได้แก่ วงจรอ่านค่าความชื้นในดินและอุณหภูมิ, วงจรขับปั๊มน้ำด้วยรีเลย์, การแสดงผลกราฟิกบนจอ OLED, การรายงานข้อมูลผ่าน MQTT Broker และการแจ้งเตือนภัยผ่าน LINE Notify เมื่อระดับน้ำต่ำกว่าเกณฑ์
16_capstone_smart_greenhouse.ino (ESP32 C++)
#include <WiFi.h>
#include <PubSubClient.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

const char* ssid = "YOUR_WIFI_SSID";
const char* password = "YOUR_WIFI_PASSWORD";

const int RELAY_WATER_PUMP = 16;
const int RELAY_MIST_FAN   = 17;
const int SOIL_SENSOR_PIN  = 35;

void setup() {
  Serial.begin(115200);
  pinMode(RELAY_WATER_PUMP, OUTPUT);
  pinMode(RELAY_MIST_FAN, OUTPUT);
  digitalWrite(RELAY_WATER_PUMP, HIGH); // ปิดปั๊มน้ำ
  digitalWrite(RELAY_MIST_FAN, HIGH);   // ปิดพัดลมหมอก

  WiFi.begin(ssid, password);
  Serial.println("Smart Greenhouse System Starting...");
}

void loop() {
  // อ่านค่าความชื้นในดินและแปลงเป็นเปอร์เซ็นต์
  int soilRaw = analogRead(SOIL_SENSOR_PIN);
  int soilPercent = map(soilRaw, 4095, 1500, 0, 100);
  soilPercent = constrain(soilPercent, 0, 100);

  Serial.print("Soil Moisture: ");
  Serial.print(soilPercent);
  Serial.println("%");

  // ตรรกะควบคุมอัตโนมัติ (Automated Closed-loop Control)
  if (soilPercent < 40) {
    digitalWrite(RELAY_WATER_PUMP, LOW); // รดน้ำอัตโนมัติ
    Serial.println("[AUTO ACTION] Soil dry: Water Pump ACTIVATED");
  } else {
    digitalWrite(RELAY_WATER_PUMP, HIGH);
  }

  delay(2000);
}
กิจกรรมประเมินผลโครงงาน (Capstone Project Submission)
ให้นักศึกษาจัดทำรายงานโครงงานฉบับสมบูรณ์ (5 บท) พร้อมคลิปวิดีโอสาธิตการทำงานจริงของระบบโรงเรือนอัจฉริยะ นำเสนอต่อคณะกรรมการเพื่อประเมินสมรรถนะรายวิชา 31910-2012 ปวส. ตามแบบฟอร์มการประเมินรูบริกส์

ข้อมูลประมวลรายวิชา 31910-2012 อินเทอร์เน็ตเพื่อสรรพสิ่ง (ปวส.)

ชื่อรายวิชา: อินเทอร์เน็ตเพื่อสรรพสิ่ง (Internet of Things - IoT)

รหัสวิชา: 31910-2012 ระดับประกาศนียบัตรวิชาชีพชั้นสูง (ปวส.) 3.0 หน่วยกิต (2-3-3)

คำอธิบายรายวิชา: ศึกษาและปฏิบัติเกี่ยวกับสถาปัตยกรรมระบบสมองกลฝังตัว ไมโครคอนโทรลเลอร์ Arduino และ ESP32 การเชื่อมต่อเซนเซอร์ อุปกรณ์ขับเร้า การประมวลผลข้อมูล โปรโตคอลการสื่อสารไร้สาย Wi-Fi, MQTT, HTTP การเชื่อมต่อ LINE Notify และคลาวด์แพลตฟอร์ม IoT

สัดส่วนการวัดผลประเมินผล:
  • จิตพิสัยและการเข้าชั้นเรียน: 20 คะแนน
  • ใบงานการทดลองและแบบฝึกหัด (16 บท): 30 คะแนน
  • การทดสอบปฏิบัติการกลางภาค: 10 คะแนน
  • โครงงานไอโอทีต้นแบบ (Capstone Project): 20 คะแนน
  • การประเมินผลปลายภาคเรียน: 20 คะแนน

บัญชีรายการอุปกรณ์ฮาร์ดแวร์สำหรับห้องปฏิบัติการ (BOM List)

รายการอุปกรณ์ขั้นต่ำสำหรับนักศึกษา 1 ชุดการทดลอง ในรายวิชา 31910-2012 ปวส.

ที่ รายการอุปกรณ์ สเปกแนะนำ จำนวน
1 บอร์ดไมโครคอนโทรลเลอร์ ESP32 NodeMCU-32S (38 pins) 1 บอร์ด
2 เซนเซอร์วัดอุณหภูมิและความชื้น DHT22 (AM2302) Module 1 ตัว
3 เซนเซอร์วัดระยะทางอัลตราโซนิก HC-SR04 1 ตัว
4 จอแสดงผลกราฟิก OLED 0.96 นิ้ว I2C 128x64 SSD1306 1 จอ
5 โมดูลรีเลย์แยกทางแสง 1-Channel / 2-Channel 5V Optocoupler 1 โมดูล
6 เซนเซอร์วัดความชื้นในดิน Capacitive Soil Moisture Sensor v1.2 1 ตัว
7 เบรดบอร์ดและสายจัมเปอร์ Breadboard 830 รู + สายต่อ ผู้-ผู้/ผู้-เมีย 1 ชุด
คัดลอกโค้ดเรียบร้อยแล้ว