📑 เนื้อหาในบทความ
📖 ภาพรวมโปรเจกต์
บทความนี้จะแนะนำวิธีสร้าง IoT Sensor Node ที่ทำงานด้วยแบตเตอรี่ได้นานหลายเดือน โดยใช้ ESP32 Deep Sleep Mode ร่วมกับ MQTT Protocol เพื่อส่งข้อมูลเซนเซอร์ไปยัง Server อย่างมีประสิทธิภาพ
🌟 จุดเด่นของโปรเจกต์นี้
- • ประหยัดพลังงานด้วย Deep Sleep Mode (10μA)
- • ตื่นจากการนอนเพื่อวัดค่าและส่งข้อมูลผ่าน MQTT
- • คำนวณอายุการใช้งานแบตเตอรี่ได้แม่นยำ
- • เข้ากันได้กับ Home Assistant, Node-RED และ CynoIoT Platform
- • ปรับแต่งระยะเวลาการส่งข้อมูลได้ตามต้องการ
สิ่งที่คุณจะได้เรียนรู้
- ✅ การทำความเข้าใจ Deep Sleep Mode ของ ESP32
- ✅ การเชื่อมต่อ ESP32 กับ MQTT Broker
- ✅ การตั้งค่า Timer Wake-up และ External Wake-up
- ✅ การคำนวณพลังงานและอายุการใช้งานแบตเตอรี่
- ✅ การเลือกเซนเซอร์ที่เหมาะสมสำหรับ Low Power
- ✅ เทคนิคการปรับแต่งให้ใช้พลังงานน้อยที่สุด
⚡ ทำไมต้อง Deep Sleep?
เมื่อสร้าง IoT Sensor Node ที่ทำงานด้วยแบตเตอรี่ การจัดการพลังงานเป็นสิ่งสำคัญที่สุด Deep Sleep Mode ของ ESP32 ช่วยลดการใช้พลังงานลงอย่างมหาศาล
เปรียบเทียบโหมดการทำงาน
| โหมด | กระแสไฟ | คำอธิบาย |
|---|---|---|
| Active Mode | 160-260 mA | WiFi เปิด, CPU ทำงานเต็มสูบ |
| Light Sleep | 0.8 - 15 mA | CPU หยุดทำงาน แต่ WiFi ยังเชื่อมต่อ |
| Deep Sleep | ~10 μA | CPU หยุด, RAM สูญหาย (RTC memory เหลือ) |
| Hibernation | ~2.5 μA | ปิดทุกอย่าง ยกเว้น RTC |
⚠️ ข้อควรระวัง
- • Deep Sleep จะรีเซ็ต ESP32 เมื่อตื่นขึ้นมา (เหมือนกดรีเซ็ต)
- • ข้อมูลใน RAM จะหายไป ใช้ RTC memory ในการเก็บข้อมูลสำคัญ
- • การเชื่อมต่อ WiFi ใหม่ทุกครั้งใช้เวลา ~2-3 วินาที
- • ต้องเขียนโค้ดให้รองรับการรีเซ็ตทุกครั้งที่ตื่น
📡 พื้นฐาน MQTT Protocol
MQTT (Message Queuing Telemetry Transport) เป็นโปรโตคอลการสื่อสารแบบ Lightweight ที่เหมาะสำหรับ IoT Devices ที่มีทรัพยากรจำกัด
ทำไม MQTT เหมาะกับ Deep Sleep?
✅ ข้อดี
- • ขนาด packet เล็ก (Header เพียง 2 bytes)
- • ใช้ bandwidth น้อยมาก
- • เชื่อมต่อและส่งข้อมูลเร็ว
- • รองรับ QoS (Quality of Service)
- • สามารถรักษา connection ได้ (Keep-alive)
📊 MQTT Architecture
- • Broker: ฝ่ายกลางที่รับส่งข้อมูล
- • Publisher: ผู้ส่งข้อมูล (ESP32)
- • Subscriber: ผู้รับข้อมูล
- • Topic: ช่องทางของข้อมูล
- • Message: ข้อมูลที่ส่ง
💡 เคล็ดลับสำหรับ Deep Sleep + MQTT
สำหรับ Sensor Node ที่ใช้ Deep Sleep ควรใช้ MQTT QoS 1 (At least once) เพื่อให้มั่นใจว่าข้อมูลถูกส่งถึง Broker แม้ว่าจะมีการเชื่อมต่อที่ไม่เสถียร แต่อย่าใช้ QoS 2 เพราะจะใช้พลังงานและเวลามากขึ้น
🛠️ อุปกรณ์ที่ต้องใช้
Hardware
ESP32 Board (DevKitC, NodeMCU, ฯลฯ)
บอร์ดหลักสำหรับโปรเจกต์ (~฿80-150)
DHT11/DHT22 หรือ DS18B20 Temperature Sensor
เซนเซอร์วัดอุณหภูมิ (~฿20-60)
Battery (Li-Ion 18650 หรือ Li-Po)
แหล่งจ่ายพลังงาน (~฿80-150)
TP4056 USB Charging Module หรือ DC-DC Boost Converter
ชาร์จและจัดการแบตเตอรี่ (~฿15-40)
Jumper Wires (Male-to-Female)
สายเชื่อมต่อ (~฿40-60)
Software
- • Arduino IDE 2.x - ดาวน์โหลดจาก arduino.cc
- • ESP32 Board Package - ติดตั้งผ่าน Board Manager
- • MQTT Broker - ใช้ฟรีจาก HiveMQ, EMQX หรือติดตั้งเอง
- • MQTT Client - MQTT Explorer สำหรับทดสอบ
📦 Libraries ที่ต้องติดตั้ง
• WiFi.h (built-in)
• PubSubClient (by Nick O'Leary)
• DHT sensor library (by Adafruit)
• OneWire (ใช้กับ DS18B20)
🔌 การต่อวงจร Hardware
ในตัวอย่างนี้เราจะใช้ DHT22 Temperature & Humidity Sensor เพราะใช้งานง่ายและให้ข้อมูลที่แม่นยำ
Pinout การต่อสาย
| DHT22 Pin | ESP32 Pin | หมายเหตุ |
|---|---|---|
| VCC (Pin 1) | 3.3V | จ่ายไฟ 3.3V |
| DATA (Pin 2) | GPIO 4 | เชื่อมต่อพร้อม Pull-up 10KΩ |
| NC (Pin 3) | - | ไม่ต้องต่อ |
| GND (Pin 4) | GND | กราวด์ |
⚠️ ข้อควรระวังเรื่อง Pin
- • หลีกเลี่ยงการใช้ GPIO 0, 2, 12, 15 เพราะอาจมีผลต่อการ Boot
- • GPIO 34-39 เป็น Input-only และไม่มี Pull-up/Pull-down ภายใน
- • ใช้ GPIO 4 หรือ GPIO 5 สำหรับ DHT22 เพื่อความปลอดภัย
- • DHT22 ต้องการ Pull-up resistor 10KΩ บนขา DATA
วงจรแบตเตอรี่
สำหรับโปรเจกต์นี้ สามารถใช้แบตเตอรี่ในรูปแบบต่างๆ ได้:
- Li-Ion 18650 - ความจุ 2000-3500mAh, ถูกและหาง่าย
- Li-Po ธรรมดา - ความจุ 500-2000mAh, เบาและบาง
- Battery Pack 4x AA - 6V, ใช้งานง่ายแต่อายุสั้น
💡 เคล็ดลับ
ใช้ TP4056 Module สำหรับชาร์จ Li-Ion/Li-Po และต่อเข้ากับ DC-DC Boost Converter เพื่อแปลง 3.7V เป็น 5V หรือ 3.3V สำหรับ ESP32 อย่าลืมใส่ Diode ป้องกันการไหลย้อนของกระแสไฟ
💻 โค้ดโปรแกรม
โค้ดต่อไปนี้จะทำให้ ESP32 ตื่นขึ้นมาเชื่อมต่อ WiFi, อ่านค่าเซนเซอร์, ส่งข้อมูลผ่าน MQTT, แล้วกลับเข้า Deep Sleep อีกครั้ง
/*
* ESP32 Deep Sleep + MQTT Temperature Sensor
*
* โค้ดนี้ทำให้ ESP32 ทำงานแบบ Battery-powered Sensor Node
* โดยจะตื่นขึ้นมาทุกๆ ช่วงเวลาที่กำหนด เพื่อส่งข้อมูลผ่าน MQTT
*
* Hardware: ESP32 + DHT22
* Author: CynoIoT Team
* Date: March 2026
*/
#include <WiFi.h>
#include <PubSubClient.h>
#include <DHT.h>
// ========== WiFi Settings ==========
const char* ssid = "YOUR_WIFI_SSID"; // ชื่อ WiFi
const char* password = "YOUR_WIFI_PASSWORD"; // รหัสผ่าน WiFi
// ========== MQTT Settings ==========
const char* mqtt_server = "broker.hivemq.com"; // MQTT Broker
const int mqtt_port = 1883; // MQTT Port
const char* mqtt_user = ""; // Username (ถ้ามี)
const char* mqtt_password = ""; // Password (ถ้ามี)
// ========== Topics ==========
const char* temp_topic = "cynoiot/sensor/temperature";
const char* hum_topic = "cynoiot/sensor/humidity";
const char* status_topic = "cynoiot/sensor/status";
// ========== Sensor Settings ==========
#define DHTPIN 4 // GPIO 4 สำหรับ DHT22
#define DHTTYPE DHT22 // รุ่น DHT22
DHT dht(DHTPIN, DHTTYPE);
// ========== Deep Sleep Settings ==========
#define uS_TO_S_FACTOR 1000000 /* แปลง microseconds เป็น seconds */
#define TIME_TO_SLEEP 300 /* เวลานอน 300 วินาที (5 นาที) */
// ========== Global Variables ==========
WiFiClient espClient;
PubSubClient client(espClient);
RTC_DATA_ATTR int bootCount = 0; // เก็บจำนวนครั้งที่บูตใน RTC memory
// ========== Function Prototypes ==========
void setupWiFi();
void reconnectMQTT();
void readAndSendSensorData();
void goToDeepSleep();
void setup() {
Serial.begin(115200);
// นับจำนวนครั้งที่ตื่นจาก Deep Sleep
++bootCount;
Serial.println("Boot number: " + String(bootCount));
// แสดงสาเหตุที่ตื่น
esp_sleep_wakeup_cause_t wakeup_reason;
wakeup_reason = esp_sleep_get_wakeup_cause();
switch(wakeup_reason) {
case ESP_SLEEP_WAKEUP_TIMER:
Serial.println("Wakeup caused by timer");
break;
case ESP_SLEEP_WAKEUP_EXT0:
Serial.println("Wakeup caused by external signal using RTC_IO");
break;
case ESP_SLEEP_WAKEUP_EXT1:
Serial.println("Wakeup caused by external signal using RTC_CNTL");
break;
case ESP_SLEEP_WAKEUP_TOUCHPAD:
Serial.println("Wakeup caused by touchpad");
break;
default:
Serial.println("Wakeup was not caused by deep sleep");
break;
}
// เริ่มต้นเซนเซอร์
dht.begin();
// เชื่อมต่อ WiFi
setupWiFi();
// เชื่อมต่อ MQTT
client.setServer(mqtt_server, mqtt_port);
reconnectMQTT();
// อ่านและส่งข้อมูลเซนเซอร์
readAndSendSensorData();
// ส่งสถานะและเข้า Deep Sleep
client.publish(status_topic, "Going to sleep...", true);
delay(100); // รอให้ส่งข้อมูลเสร็จ
goToDeepSleep();
}
void loop() {
// ไม่ต้องใช้ loop เพราะจะเข้า Deep Sleep ใน setup
}
// ========== WiFi Setup ==========
void setupWiFi() {
Serial.print("Connecting to 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("\nWiFi connected!");
Serial.print("IP address: ");
Serial.println(WiFi.localIP());
} else {
Serial.println("\nWiFi connection failed!");
Serial.println("Going to deep sleep anyway...");
}
}
// ========== MQTT Reconnect ==========
void reconnectMQTT() {
int attempts = 0;
while (!client.connected() && attempts < 3) {
Serial.print("Attempting MQTT connection...");
// สร้าง Client ID แบบสุ่ม
String clientId = "ESP32Client-";
clientId += String(random(0xffff), HEX);
if (client.connect(clientId.c_str(), mqtt_user, mqtt_password)) {
Serial.println("connected");
return;
} else {
Serial.print("failed, rc=");
Serial.print(client.state());
Serial.println(" retrying...");
delay(500);
attempts++;
}
}
}
// ========== Read and Send Sensor Data ==========
void readAndSendSensorData() {
// อ่านค่าอุณหภูมิและความชื้น
float humidity = dht.readHumidity();
float temperature = dht.readTemperature(); // ค่า Celsius
// ตรวจสอบว่าอ่านค่าได้หรือไม่
if (isnan(humidity) || isnan(temperature)) {
Serial.println("Failed to read from DHT sensor!");
client.publish(status_topic, "Sensor reading failed!", true);
return;
}
// แสดงค่าที่ได้ใน Serial Monitor
Serial.println("Temperature: " + String(temperature) + " °C");
Serial.println("Humidity: " + String(humidity) + " %");
// แปลงค่าเป็น String
char tempStr[10];
char humStr[10];
dtostrf(temperature, 2, 2, tempStr);
dtostrf(humidity, 2, 2, humStr);
// ส่งข้อมูลผ่าน MQTT
if (client.publish(temp_topic, tempStr, true)) {
Serial.println("Temperature data sent!");
} else {
Serial.println("Failed to send temperature data");
}
if (client.publish(hum_topic, humStr, true)) {
Serial.println("Humidity data sent!");
} else {
Serial.println("Failed to send humidity data");
}
}
// ========== Go to Deep Sleep ==========
void goToDeepSleep() {
Serial.println("Going to deep sleep for " + String(TIME_TO_SLEEP) + " seconds");
// ตั้งค่าเวลานอน
esp_sleep_enable_timer_wakeup(TIME_TO_SLEEP * uS_TO_S_FACTOR);
// เข้าสู่โหมด Deep Sleep
Serial.println("Entering deep sleep mode...");
Serial.flush();
esp_deep_sleep_start();
}✨ จุดสำคัญในโค้ด
- • RTC_DATA_ATTR: เก็บตัวแปรใน RTC memory จะไม่หายเมื่อ Deep Sleep
- • esp_sleep_enable_timer_wakeup(): ตั้งเวลาตื่นด้วย Timer
- • esp_deep_sleep_start(): เข้าสู่ Deep Sleep ทันที
- • MQTT QoS 1 (true): รับประกันว่าข้อมูลจะถูกส่งถึง
วิธีอัปโหลดโค้ด
- 1. เปิด Arduino IDE และสร้าง Sketch ใหม่
- 2. Copy โค้ดด้านบนและวางลงไป
- 3. แก้ไข WiFi credentials และ MQTT settings
- 4. เลือกบอร์ดและ port ที่ถูกต้อง
- 5. กด Upload ไปยัง ESP32
- 6. เปิด Serial Monitor (115200 baud) เพื่อดูผล
🔋 คำนวณอายุการใช้งานแบตเตอรี่
การคำนวณอายุการใช้งานแบตเตอรี่ช่วยให้เราวางแผนการบำรุงรักษาและทำนายว่าเซนเซอร์โหนดจะทำงานได้นานแค่ไหน
สูตรการคำนวณ
อายุแบต (วัน) = ความจุแบต (mAh) / (กระแสเฉลี่ยรายวัน (mAh/วัน))
กระแสเฉลี่ยรายวัน = กระแส Active × เวลา Active + กระแส Deep Sleep × เวลา Deep Sleep
ตัวอย่างการคำนวณ
📊 สมมติสถานการณ์
- • แบตเตอรี่: 2000 mAh (Li-Ion 18650)
- • เวลา Active: ~10 วินาทีต่อครั้ง
- • เวลา Deep Sleep: 290 วินาที (5 นาทีรวม)
- • กระแส Active: ~150 mA (เฉลี่ย)
- • กระแส Deep Sleep: ~10 μA = 0.01 mA
🧮 การคำนวณ
รอบต่อวัน: 86400 วินาที / 300 วินาที = 288 รอบ/วัน
กระแส Active ต่อวัน: 150 mA × 10 วินาที × 288 รอบ / 3600 = 120 mAh/วัน
กระแส Deep Sleep ต่อวัน: 0.01 mA × 290 วินาที × 288 รอบ / 3600 = 0.008 mAh/วัน
กระแสรวมต่อวัน: 120.008 ≈ 120 mAh/วัน
🎯 อายุแบต: 2000 / 120 = 16.67 วัน
เคล็ดลับการยืดอายุแบตเตอรี่
✅ ทำให้ใช้พลังน้อยลง
- • เพิ่มระยะเวลา Deep Sleep (เช่น 10-15 นาที)
- • ลดเวลาเชื่อมต่อ WiFi
- • ปิด Serial หลังจาก Debug เสร็จ
- • ใช้เซนเซอร์ที่ประหยัดพลังงาน
- • ปิด LED บนบอร์ด (ถ้ามี)
❌ สิ่งที่ควรหลีกเลี่ยง
- • ใช้ delay() มากเกินไปใน Active mode
- • ไม่ปิด WiFi หลังจากใช้งาน
- • ใช้ Power Bank ที่มี Auto-off
- • เซนเซอร์ที่ใช้พลังมากเกินไป
- • ส่งข้อมูลถี่เกินไป
⚠️ ข้อควรระวัง
การคำนวณเป็นเพียงค่าประมาณ อายุแบตจริงอาจต่างออกไปขึ้นอยู่กับอุณหภูมิ, คุณภาพแบต, และสภาพแวดล้อม แนะนำให้ทดสอบจริงและเพิ่ม Safety Margin 20-30%
🧪 การทดสอบ
ทดสอบด้วย MQTT Explorer
- 1
ดาวน์โหลดและติดตั้ง MQTT Explorer
ได้ที่ mqtt-explorer.com
- 2
สร้าง Connection ใหม่
Host: broker.hivemq.com, Port: 1883
- 3
Subscribe Topics
cynoiot/sensor/#
- 4
รอดูข้อมูล
ESP32 จะส่งข้อมูลทุกๆ 5 นาที (ตามที่ตั้งค่า)
ตรวจสอบ Serial Monitor
Boot number: 1
Wakeup caused by timer
Connecting to WiFi.....
WiFi connected!
IP address: 192.168.1.100
Attempting MQTT connection...connected
Temperature: 28.50 °C
Humidity: 65.20 %
Temperature data sent!
Humidity data sent!
Going to deep sleep for 300 seconds
Entering deep sleep mode...ทดสอบอายุแบตเตอรี่
หากต้องการทดสอบอายุแบตจริง สามารถใช้โค้ดนี้ในการ Track การใช้พลังงาน:
// เพิ่มใน setup() หลังจากตื่น
Serial.print("Boot count: ");
Serial.println(bootCount);
// คำนวณเวลาที่ผ่านไป (ถ้าต้องการ)
// ใช้ RTC memory ในการเก็บ timestamp ก่อนนอน🔧 การแก้ปัญหา
ESP32 ตื่นแล้วไม่ทำงาน
อาการ: ตื่นจาก Deep Sleep แต่ไม่เชื่อมต่อ WiFi หรือไม่ส่งข้อมูล
วิธีแก้: เพิ่ม delay() หลังจาก dht.begin() ให้เซนเซอร์พร้อมทำงาน, ตรวจสอบว่าตัวแปรทุกตัวถูกกำหนดค่าใหม่ใน setup()
MQTT เชื่อมต่อไม่ได้
อาการ: แสดง "failed, rc=-2" ใน Serial Monitor
วิธีแก้: ตรวจสอบว่า WiFi เชื่อมต่อสำเร็จ, ลองเปลี่ยน MQTT Broker, เพิ่มเวลา Timeout ในการเชื่อมต่อ
อ่านค่า DHT ไม่ได้
อาการ: แสดง "Failed to read from DHT sensor!"
วิธีแก้: ตรวจสอบการต่อสาย (VCC, GND, DATA), เพิ่ม delay() 2-3 วินาทีหลังจากเริ่มเซนเซอร์, ลองใช้ Pull-up resistor ภายนอก
แบตเตอรี่หมดเร็วเกินไป
อาการ: แบตหมดภายใน 2-3 วัน
วิธีแก้: เพิ่มเวลา Deep Sleep, ลดเวลา Active, ตรวจสอบว่าปิด WiFi หลังใช้งาน, ใช้เซนเซอร์ที่ประหยัดพลังงานกว่า
อัปโหลดโค้ดไม่ได้ขณะ Deep Sleep
อาการ: ไม่สามารถอัปโหลดโค้ดใหม่ได้
วิธีแก้: กดปุ่ม BOOT ค้างระหว่างอัปโหลด, หรือรีเซ็ต ESP32 ก่อนอัปโหลด, หรือต่อสาย GPIO 0 เข้ากับ GND ระหว่างอัปโหลด
🚀 ขั้นตอนถัดไป
ยินดีด้วย! ตอนนี้คุณมี IoT Sensor Node ที่ประหยัดพลังงานแล้ว ต่อไปนี้คือสิ่งที่คุณสามารถพัฒนาต่อได้:
📊 เชื่อมต่อกับ CynoIoT Platform
นำข้อมูลเซนเซอร์ไปแสดงบน Dashboard ของ CynoIoT พร้อมกราฟและแจ้งเตือน
🏠 ใช้กับ Home Assistant
เพิ่ม MQTT Sensor ใน Home Assistant เพื่อสร้าง Automation อัจฉริยะ
📡 เพิ่มเซนเซอร์หลายตัว
เพิ่ม Soil Moisture, Light Sensor หรือ Motion Sensor ลงในโหนดเดียว
🔋 OTA Updates
เพิ่มความสามารถอัปเดตฟาร์มแวร์ผ่าน WiFi โดยไม่ต้องถอดแบต