📑 เนื้อหาในบทความ
🎯 ภาพรวมโปรเจกต์
โปรเจกต์นี้จะสอนวิธีสร้าง ระบบตั้งเวลาเปิด-ปิดอุปกรณ์ไฟฟ้า โดยใช้ ESP32 เป็นตัวควบคุม ผ่านหน้าเว็บที่ติดตั้งบนบอร์ดเอง ทำให้สามารถเข้าถึงและควบคุมได้จากอุปกรณ์ที่เชื่อมต่อ WiFi ในเครือข่ายเดียวกัน
✨ จุดเด่นของโปรเจกต์
- • ควบคุมได้ผ่าน Web Browser ทั้งบนมือถือและคอมพิวเตอร์
- • ตั้งเวลาเปิด-ปิดได้หลายช่วงเวลา
- • สถานะปัจจุบันแสดงผลแบบ Real-time
- • ใช้งานง่าย ไม่ต้องต่อ Internet
- • ประหยัดพลังงานด้วยการควบคุม Relay
การประยุกต์ใช้งาน
- ระบบรดน้ำอัตโนมัติ: ตั้งเวลารดน้ำตอนเช้าและเย็น
- ควบคุมไฟในบ้าน: เปิด-ปิดไฟตามช่วงเวลาที่กำหนด
- เครื่องใช้ไฟฟ้า: ตั้งเวลาเปิดปั๊มน้ำ, พัดลม, หรืออุปกรณ์อื่นๆ
- ระบบแจ้งเตือน: ใช้เป็นส่วนหนึ่งของระบบอัตโนมัติภายในบ้านอัจฉริยะ
🔧 อุปกรณ์ที่ต้องใช้
Hardware Components
| อุปกรณ์ | รายละเอียด | ประมาณการ |
|---|---|---|
| ESP32 DevKit | บอร์ดไมโครคอนโทรลเลอร์ (มี WiFi) | ฿100-150 |
| Relay Module 5V | 1-channel หรือ 2-channel | ฿20-40 |
| LED + Resistor | 220Ω สำหรับทดสอบ | ฿5 |
| Breadboard + Jumper | สายจัมเปอร์และบอร์ดทดลอง | ฿30-50 |
| USB Cable | Micro USB สำหรับ ESP32 | ฿20-30 |
💡 ข้อควรระวัง
- • หากต่อกับอุปกรณ์ไฟฟ้า 220V ให้ใช้ Relay ที่รองรับกระแสสูง
- • ต้องมีความรู้เรื่องไฟฟ้าก่อนต่อกับอุปกรณ์จริง
- • ใช้หลอด LED ทดสอบก่อนเพื่อความปลอดภัย
⚡ วงจรการต่อใช้งาน
การเชื่อมต่อ Relay Module กับ ESP32
รายการ Pinout:
- VCC → 3.3V หรือ 5V บน ESP32
- GND → GND บน ESP32
- IN1 → GPIO 26 หรือ GPIO ที่ต้องการ
- IN2 → GPIO 27 (ถ้าใช้ Relay 2-channel)
วงจรทดสอบด้วย LED
การเชื่อมต่อ LED:
- GPIO 26 → Resistor 220Ω → LED Anode (+)
- LED Cathode (-) → GND
การเชื่อมต่อ Relay:
- VCC → 5V
- GND → GND
- IN1 → GPIO 26
✅ หมายเหตุ
- • Relay Module บางรุ่นต้องการกระแสสูง ให้ต่อ VCC เข้ากับ 5V โดยตรง
- • หลีกเลี่ยงการต่ออุปกรณ์ที่ใช้กระแสสูงกว่า 10A โดยไม่มีการออกแบบวงจรเพิ่มเติม
💻 การเตรียมซอฟต์แวร์
1. ติดตั้ง Arduino IDE
ดาวน์โหลดและติดตั้ง Arduino IDE จาก arduino.cc
2. ติดตั้ง ESP32 Board
- เปิด Arduino IDE → File → Preferences
- ในช่อง "Additional Board Manager URLs" ใส่:
https://raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_index.json- ไปที่ Tools → Board → Boards Manager
- ค้นหา "ESP32" และติดตั้ง
3. เลือกบอร์ดและ Port
- Tools → Board → ESP32 Arduino → ESP32 Dev Module
- Tools → Port → เลือก COM port ของ ESP32
📝 โค้ดโปรแกรม
นี่คือโค้ดที่สมบูรณ์สำหรับระบบตั้งเวลาเปิด-ปิดอุปกรณ์:
#include <WiFi.h>
#include <WebServer.h>
#include <time.h>
// ==================== การตั้งค่า WiFi ====================
const char* ssid = "YOUR_WIFI_SSID"; // แก้ไขเป็น WiFi ของคุณ
const char* password = "YOUR_WIFI_PASSWORD"; // แก้ไขเป็นรหัสผ่าน WiFi
// ==================== การตั้งค่า Hardware ====================
const int RELAY_PIN = 26; // GPIO ที่ต่อกับ Relay
const int LED_PIN = 2; // ในบอร์ด LED (สำหรับทดสอบ)
// ==================== การตั้งค่า Web Server ====================
WebServer server(80);
// ==================== การตั้งค่า Timer ====================
struct Timer {
int hour;
int minute;
bool action; // true = เปิด, false = ปิด
bool enabled;
};
#define MAX_TIMERS 8
Timer timers[MAX_TIMERS] = {
{6, 0, true, true}, // เปิดเวลา 06:00
{18, 0, false, true}, // ปิดเวลา 18:00
{7, 30, true, false}, // Timer ที่ 3 (ปิดใช้งาน)
{0, 0, false, false},
{0, 0, false, false},
{0, 0, false, false},
{0, 0, false, false},
{0, 0, false, false}
};
bool relayState = false;
bool manualOverride = false;
unsigned long lastCheckTime = 0;
const unsigned long CHECK_INTERVAL = 60000; // ตรวจสอบทุก 1 นาที
// ==================== ฟังก์ชันตั้งค่าเริ่มต้น ====================
void setup() {
Serial.begin(115200);
delay(1000);
// ตั้งค่า Pin
pinMode(RELAY_PIN, OUTPUT);
pinMode(LED_PIN, OUTPUT);
digitalWrite(RELAY_PIN, LOW);
digitalWrite(LED_PIN, LOW);
// เชื่อมต่อ WiFi
Serial.println("\n\nเชื่อมต่อ WiFi...");
WiFi.begin(ssid, password);
int attempts = 0;
while (WiFi.status() != WL_CONNECTED && attempts < 20) {
delay(500);
Serial.print(".");
attempts++;
}
if (WiFi.status() == WL_CONNECTED) {
Serial.println("\n✅ WiFi เชื่อมต่อสำเร็จ!");
Serial.print("🌐 IP Address: ");
Serial.println(WiFi.localIP());
// แสดงใน Serial Monitor
Serial.println("🔗 เปิดหน้าเว็บ: http://" + WiFi.localIP().toString());
} else {
Serial.println("\n❌ ไม่สามารถเชื่อมต่อ WiFi ได้");
}
// ตั้งค่า NTP Time (เวลาอินเทอร์เน็ต)
configTime(7 * 3600, 0, "pool.ntp.org", "time.nist.gov");
Serial.println("⏰ กำลังตั้งเวลาจากเซิร์ฟเวอร์ NTP...");
// ตั้งค่า Web Server
setupWebServer();
Serial.println("✅ พร้อมใช้งาน!");
}
// ==================== ฟังก์ชันหลัก ====================
void loop() {
server.handleClient();
// ตรวจสอบ Timer ทุก 1 นาที
unsigned long currentTime = millis();
if (currentTime - lastCheckTime >= CHECK_INTERVAL) {
lastCheckTime = currentTime;
checkTimers();
}
delay(10);
}
// ==================== ฟังก์ชันตรวจสอบ Timer ====================
void checkTimers() {
struct tm timeinfo;
if (!getLocalTime(&timeinfo)) {
Serial.println("❌ ไม่สามารถรับเวลาได้");
return;
}
int currentHour = timeinfo.tm_hour;
int currentMinute = timeinfo.tm_min;
// ตรวจสอบทุก Timer
for (int i = 0; i < MAX_TIMERS; i++) {
if (timers[i].enabled &&
timers[i].hour == currentHour &&
timers[i].minute == currentMinute) {
// ทำการเปิด/ปิด Relay
setRelay(timers[i].action);
Serial.printf("⏰ Timer %d: %s\n", i + 1, timers[i].action ? "เปิด" : "ปิด");
}
}
}
// ==================== ฟังก์ชันควบคุม Relay ====================
void setRelay(bool state) {
relayState = state;
digitalWrite(RELAY_PIN, state ? HIGH : LOW);
digitalWrite(LED_PIN, state ? HIGH : LOW);
manualOverride = true;
}
// ==================== ฟังก์ชันตั้งค่า Web Server ====================
void setupWebServer() {
// หน้าหลัก
server.on("/", []() {
String html = generateWebPage();
server.send(200, "text/html", html);
});
// API: อัปเดต Timer
server.on("/api/timer", HTTP_POST, []() {
if (server.hasArg("index") && server.hasArg("hour") &&
server.hasArg("minute") && server.hasArg("action")) {
int index = server.arg("index").toInt();
timers[index].hour = server.arg("hour").toInt();
timers[index].minute = server.arg("minute").toInt();
timers[index].action = server.arg("action") == "1";
timers[index].enabled = server.hasArg("enabled") ?
server.arg("enabled") == "1" : true;
server.send(200, "application/json", "{\"status\":\"ok\"}");
} else {
server.send(400, "application/json", "{\"status\":\"error\"}");
}
});
// API: เปิด/ปิด Relay แบบ Manual
server.on("/api/relay", HTTP_POST, []() {
bool state = server.arg("state") == "1";
setRelay(state);
server.send(200, "application/json", "{\"status\":\"ok\"}");
});
// API: ดึงสถานะ
server.on("/api/status", HTTP_GET, []() {
struct tm timeinfo;
getLocalTime(&timeinfo);
String json = "{";
json += "\"relay\":" + String(relayState ? "true" : "false") + ",";
json += "\"hour\":" + String(timeinfo.tm_hour) + ",";
json += "\"minute\":" + String(timeinfo.tm_min) + ",";
json += "\"second\":" + String(timeinfo.tm_sec) + ",";
json += "\"timers\":[";
for (int i = 0; i < MAX_TIMERS; i++) {
if (i > 0) json += ",";
json += "{";
json += "\"hour\":" + String(timers[i].hour) + ",";
json += "\"minute\":" + String(timers[i].minute) + ",";
json += "\"action\":" + String(timers[i].action ? "true" : "false") + ",";
json += "\"enabled\":" + String(timers[i].enabled ? "true" : "false");
json += "}";
}
json += "]}";
server.send(200, "application/json", json);
});
server.begin();
Serial.println("🌐 Web Server เริ่มทำงานแล้ว");
}
// ==================== สร้างหน้าเว็บ ====================
String generateWebPage() {
struct tm timeinfo;
getLocalTime(&timeinfo);
String html = "<!DOCTYPE html>";
html += "<html lang='th'>";
html += "<head>";
html += "<meta charset='UTF-8'>";
html += "<meta name='viewport' content='width=device-width, initial-scale=1.0'>";
html += "<title>ESP32 Power Timer</title>";
html += "<style>";
html += "body{font-family:sarabun,sans-serif;background:linear-gradient(135deg,#667eea 0%,#764ba2 100%);min-height:100vh;margin:0;padding:20px}";
html += ".container{max-width:800px;margin:0 auto;background:white;border-radius:20px;box-shadow:0 20px 60px rgba(0,0,0,0.3);padding:30px}";
html += "h1{text-align:center;color:#333;margin-bottom:10px}";
html += ".status{text-align:center;font-size:24px;margin-bottom:30px;padding:15px;border-radius:10px}";
html += ".status.on{background:#4ade80;color:white}";
html += ".status.off{background:#f87171;color:white}";
html += ".time{text-align:center;font-size:18px;color:#666;margin-bottom:20px}";
html += ".timer{background:#f9fafb;padding:15px;border-radius:10px;margin-bottom:10px;border-left:4px solid #667eea}";
html += ".timer h3{margin:0 0 10px 0;color:#333}";
html += ".timer-controls{display:flex;gap:10px;align-items:center}";
html += "input[type='number']{width:60px;padding:8px;border:1px solid #ddd;border-radius:5px}";
html += "button{padding:10px 20px;border:none;border-radius:5px;cursor:pointer;font-weight:bold;transition:all 0.3s}";
html += ".btn-primary{background:#667eea;color:white}";
html += ".btn-primary:hover{background:#5568d3}";
html += ".btn-danger{background:#f87171;color:white}";
html += ".btn-success{background:#4ade80;color:white}";
html += ".toggle{position:relative;display:inline-block;width:50px;height:26px}";
html += ".toggle input{opacity:0;width:0;height:0}";
html += ".slider{position:absolute;cursor:pointer;top:0;left:0;right:0;bottom:0;background-color:#ccc;transition:.4s;border-radius:26px}";
html += ".slider:before{position:absolute;content:'';height:20px;width:20px;left:3px;bottom:3px;background-color:white;transition:.4s;border-radius:50%}";
html += "input:checked+.slider{background-color:#667eea}";
html += "input:checked+.slider:before{transform:translateX(24px)}";
html += "</style>";
html += "</head>";
html += "<body>";
html += "<div class='container'>";
html += "<h1>⏰ ระบบตั้งเวลา ESP32</h1>";
// สถานะ Relay
html += "<div class='status " + String(relayState ? "on" : "off") + "'>";
html += relayState ? "🔌 สถานะ: เปิดอยู่" : "🔌 สถานะ: ปิดอยู่";
html += "</div>";
// เวลาปัจจุบัน
html += "<div class='time'>";
html += "🕐 เวลา: " + String(timeinfo.tm_hour) + ":" +
String(timeinfo.tm_min) + ":" + String(timeinfo.tm_sec);
html += "</div>";
// ปุ่มเปิด/ปิด Manual
html += "<div style='text-align:center;margin-bottom:30px'>";
html += "<button class='btn-primary' onclick='toggleRelay()'>เปิด/ปิด แบบ Manual</button>";
html += "</div>";
// รายการ Timer
html += "<h2 style='color:#333;margin-bottom:20px'>📋 รายการ Timer</h2>";
for (int i = 0; i < MAX_TIMERS; i++) {
html += "<div class='timer'>";
html += "<h3>Timer " + String(i + 1) + "</h3>";
html += "<div class='timer-controls'>";
html += "<input type='number' id='hour" + String(i) + "' value='" +
String(timers[i].hour) + "' min='0' max='23' placeholder='ชม.'>";
html += "<span>:</span>";
html += "<input type='number' id='minute" + String(i) + "' value='" +
String(timers[i].minute) + "' min='0' max='59' placeholder='นาที'>";
html += "<label class='toggle'>";
html += "<input type='checkbox' id='enabled" + String(i) + "' " +
String(timers[i].enabled ? "checked" : "") + " onchange='toggleTimer(" + String(i) + ")'>";
html += "<span class='slider'></span>";
html += "</label>";
html += "<button class='btn-success' onclick='saveTimer(" + String(i) + ")'>บันทึก</button>";
html += "</div>";
html += "</div>";
}
html += "</div>";
html += "<script>";
html += "function toggleRelay(){";
html += " fetch('/api/relay', {method: 'POST', headers: {'Content-Type': 'application/x-www-form-urlencoded'}, body: 'state=' + (relayState ? '0' : '1')}).then(()=>location.reload());";
html += "}";
html += "function saveTimer(index){";
html += " const hour = document.getElementById('hour' + index).value;";
html += " const minute = document.getElementById('minute' + index).value;";
html += " const enabled = document.getElementById('enabled' + index).checked;";
html += " fetch('/api/timer', {method: 'POST', headers: {'Content-Type': 'application/x-www-form-urlencoded'}, body: 'index=' + index + '&hour=' + hour + '&minute=' + minute + '&action=1&enabled=' + (enabled ? '1' : '0')}).then(()=>location.reload());";
html += "}";
html += "setInterval(()=>location.reload(), 60000);"; // รีโหลดทุก 1 นาที
html += "</script>";
html += "</body>";
html += "</html>";
return html;
}📌 คำอธิบายโค้ด
- • โค้ดใช้ WebServer library สร้างหน้าเว็บแบบง่าย
- • มีการตั้งค่า Timer ได้สูงสุด 8 ช่วงเวลา
- • รับเวลาจาก NTP Server เพื่อความแม่นยำ
- • หน้าเว็บมีการตกแต่งและใช้งานง่าย
- • รองรับการควบคุม Manual ผ่านปุ่มบนหน้าเว็บ
🧪 การทดสอบระบบ
ขั้นตอนการทดสอบ
- อัปโหลดโค้ด: กดปุ่ม Upload ใน Arduino IDE รอจนกว่าจะขึ้น "Done uploading"
- เปิด Serial Monitor: กด Ctrl+Shift+M ตั้ง baud rate เป็น 115200
- ตรวจสอบ IP Address: ดูใน Serial Monitor จะขึ้น IP เช่น "192.168.1.100"
- เปิดหน้าเว็บ: พิมพ์ IP address ใน Web Browser บนมือถือหรือคอมพิวเตอร์
- ทดสอบ Relay: กดปุ่ม "เปิด/ปิด แบบ Manual" ควรได้ยินเสียงคลิ๊กจาก Relay
- ตั้งเวลา: ตั้ง Timer 1-2 ช่วงเวลา บันทึกและรอดูผล
✅ ผลลัพธ์ที่คาดหวัง
- • เห็นหน้าเว็บสีม่วงสวยงามพร้อมปุ่มควบคุม
- • กดปุ่ม Manual แล้ว Relay ทำงาน (LED ติด/ดับ)
- • ตั้งเวลาแล้ว Relay เปิด/ปิดตามเวลาที่กำหนด
- • หน้าเว็บรีโหลดอัตโนมัติทุก 1 นาที
🔧 การแก้ปัญหา
ปัญหา: ESP32 เชื่อมต่อ WiFi ไม่ได้
- • ตรวจสอบชื่อและรหัสผ่าน WiFi ในโค้ด
- • ลองรีเซ็ต ESP32 แล้วอัปโหลดใหม่
- • ตรวจสอบว่า Router ไม่ได้บล็อกอุปกรณ์
- • ลองเปลี่ยนช่องสัญญาณ WiFi (channel) เป็น 1, 6 หรือ 11
ปัญหา: Relay ไม่ทำงาน
- • ตรวจสอบการเชื่อมต่อสาย VCC, GND, IN1
- • ทดสอบด้วย LED ก่อนเพื่อให้แน่ใจว่าโค้ดทำงาน
- • บาง Relay Module ต้องใช้ 5V ไม่ใช่ 3.3V
- • ลองเปลี่ยน Pin GPIO เป็นตัวเลขอื่น
ปัญหา: หน้าเว็บเปิดไม่ได้
- • ตรวจสอบว่าอุปกรณ์เชื่อมต่อ WiFi อยู่ในเครือข่ายเดียวกัน
- • รอสักครู่ NTP Server อาจใช้เวลาในการซิงค์เวลา
- • ลองพิมพ์ IP address ใน Browser อื่น
- • เปิด Serial Monitor ดูว่ามีข้อผิดพลาดหรือไม่
ปัญหา: Timer ทำงานไม่ตรงเวลา
- • ตรวจสอบว่าได้ตั้งค่า timezone ถูกต้อง (บรรทัด configTime)
- • NTP Server อาจล่าช้า ให้รอ 2-3 นาทีหลังเปิดเครื่อง
- • ตรวจสอบว่าเวลาบนหน้าเว็บถูกต้องหรือไม่
🚀 ขยายความสามารถ
ไอเดียต่อยอด
- เพิ่มช่อง Relay: ใช้ Relay 2-channel 4-channel หรือ 8-channel เพื่อควบคุมหลายอุปกรณ์พร้อมกัน
- เชื่อมต่อ CynoIoT: ส่งข้อมูลเข้าสู่ CynoIoT Platform เพื่อบันทึกประวัติและควบคุมระยะไกลผ่าน Cloud
- เพิ่ม Sensor: ต่อเซ็นเซอร์วัดอุณหภูมิ ความชื้น DHT11/DHT22 เพื่อตั้งเวลาตามสภาพอากาศ
- ประหยัดพลังงาน: ใช้ Deep Sleep Mode เพื่อประหยัดแบตเตอรี่ในโปรเจกต์พกพา
- ระบบแจ้งเตือน: เพิ่ม Module Line Notify หรือ Telegram Bot เพื่อแจ้งเตือนเมื่อ Timer ทำงาน
- หน้าจอแสดงผล: เพิ่ม OLED LCD หรือ 7-segment display เพื่อแสดงสถานะโดยไม่ต้องเปิดเว็บ
💡 Tip: การเชื่อมต่อ CynoIoT
คุณสามารถใช้ CynoIoT SDK เพื่อส่งข้อมูลการเปิด-ปิด Relay เข้าสู่ระบบ Cloud ทำให้สามารถควบคุมและดูประวัติได้จากทุกที่ ไม่จำกัดอยู่ในเครือข่ายเดียวกัน
📝 สรุป
ในบทความนี้ คุณได้เรียนรู้วิธีสร้าง ระบบตั้งเวลาเปิด-ปิดอุปกรณ์ไฟฟ้า ด้วย ESP32 และ Web Server ที่สามารถควบคุมผ่านหน้าเว็บได้ง่ายๆ โปรเจกต์นี้เหมาะสำหรับการนำไปประยุกต์ใช้กับระบบอัตโนมัติภายในบ้าน การรดน้ำ หรือโปรเจกต์ IoT อื่นๆ
สิ่งที่คุณได้เรียนรู้
- ✅ การเชื่อมต่อ Relay Module กับ ESP32
- ✅ การสร้าง Web Server บน ESP32
- ✅ การใช้ NTP เพื่อรับเวลาจากอินเทอร์เน็ต
- ✅ การตั้งเวลาอัตโนมัติด้วย Timer
- ✅ การสร้างหน้าเว็บสวยงามและใช้งานง่าย