📑 เนื้อหาในบทความ
🌙 ภาพรวม Deep Sleep Mode
คุณเคยสงสัยไหมว่าเซ็นเซอร์ IoT บางตัวทำงานด้วยแบตเตอรี่ได้นานหลายเดือนได้อย่างไร? คำตอบคือ Deep Sleep Mode!
ESP32 มีโหมดพลังงานต่ำหลายแบบ แต่ Deep Sleep เป็นโหมดที่ประหยัดที่สุด เมื่อเข้าสู่ Deep Sleep:
- CPU และ WiFi หยุดทำงาน
- ใช้พลังงานเพียง 10-20 µA (เทียบกับโหมดปกติ 150-200 mA)
- เก็บข้อมูลใน RTC Memory ไว้ได้
- ตื่นด้วย Timer, External Wakeup, หรือ Touchpad
💡 เกร็ดความรู้: ใน Deep Sleep Mode ESP32 ใช้พลังงานน้อยกว่าโหมดปกติประมาณ 10,000 เท่า! ทำให้แบตเตอรี่ขนาด 2000mAh ทำงานได้นานเป็นเดือน
📦 สิ่งที่ต้องเตรียม
Hardware:
- ESP32 Dev Board (เช่น ESP32-WROOM) 1 บอร์ด - ฿80-150
- เซ็นเซอร์ (เช่น DHT11/DHT22) - ฿40-80
- Jumper Wires - ฿20
- Breadboard (ถ้าจำเป็น) - ฿30
- แบตเตอรี่ (Li-ion 18650 หรือ Power Bank) - ฿100-200
Software:
- Arduino IDE พร้อม ESP32 Board Package
- Library: PubSubClient (สำหรับ MQTT)
- MQTT Broker (ใช้ฟรีจาก HiveMQ, EMQX หรือติดตั้งเอง)
- MQTT Client (สำหรับทดสอบ เช่n MQTT Explorer)
📌 ระดับความยาก: กลาง - ควรมีพื้นฐาน ESP32 และ C++ พื้นฐาน
🔌 การต่อ Hardware
สำหรับตัวอย่างนี้ เราจะใช้ DHT22 (วัดอุณหภูมิและความชื้น) เชื่อมต่อกับ ESP32:
การต่อสาย:
| DHT22 Pin | ESP32 Pin |
|---|---|
| VCC (Pin 1) | 3.3V |
| DATA (Pin 2) | GPIO 4 |
| GND (Pin 4) | GND |
⚠️ ข้อควรระวัง: อย่าลืมต่อ Resistor 10kΩ ระหว่าง VCC และ DATA pin เพื่อ Pull-up!
📡 พื้นฐาน MQTT Protocol
MQTT (Message Queuing Telemetry Transport) เป็น protocol ที่เหมาะสำหรับ IoT เพราะ:
- เบาเครื่อง - ใช้ bandwidth น้อย
- รวดเร็ว - latency ต่ำ
- Publish/Subscribe - ส่งข้อมูลได้หลายจุด
- QoS Levels - รับประกันการส่งข้อมูล
โครงสร้าง MQTT:
- 📢 Broker - คือเซิร์ฟเวอร์กลาง (เช่n HiveMQ Cloud)
- 📤 Publisher - อุปกรณ์ที่ส่งข้อมูล (ESP32)
- 📥 Subscriber - อุปกรณ์ที่รับข้อมูล (Dashboard, Database)
- 📂 Topic - ป้ายกำกับข้อมูล (เช่n
sensors/esp32-01/temperature)
✅ ใช้ MQTT Broker ฟรี:HiveMQ Cloud หรือ EMQX Cloud เหมาะสำหรับการทดสอบ
💻 ส่วนที่ 1: Basic Deep Sleep
เริ่มต้นด้วย Deep Sleep แบบพื้นฐานก่อน เพื่อให้เข้าใจหลักการ:
#include <Arduino.h>
// กำหนดเวลานอน (microseconds)
#define uS_TO_S_FACTOR 1000000 // แปลง microseconds เป็น seconds
#define TIME_TO_SLEEP 10 // นอน 10 วินาที
// Pin สำหรับ LED (สำหรับแสดงสถานะ)
const int ledPin = 2;
// ตัวแปรเก็บใน RTC Memory (จะไม่หายเมื่อเข้า Deep Sleep)
RTC_DATA_ATTR int bootCount = 0;
void setup() {
Serial.begin(115200);
pinMode(ledPin, OUTPUT);
// นับจำนวนครั้งที่บูต
bootCount++;
Serial.println("Boot number: " + String(bootCount));
// กระพริบ LED แสดงว่าตื่นแล้ว
for(int i = 0; i < 3; i++) {
digitalWrite(ledPin, HIGH);
delay(100);
digitalWrite(ledPin, LOW);
delay(100);
}
// แสดงข้อมูล
Serial.println("เข้าสู่ Deep Sleep Mode สำหรับ " +
String(TIME_TO_SLEEP) +
" วินาที...");
Serial.println("จำไว้ว่า ESP32 จะตื่นอีกครั้งใน " +
String(TIME_TO_SLEEP) +
" วินาที");
delay(1000);
// เข้าสู่ Deep Sleep
esp_sleep_enable_timer_wakeup(TIME_TO_SLEEP * uS_TO_S_FACTOR);
esp_deep_sleep_start();
// โค้ดบรรทัดถัดไปจะไม่ถูก执行 (ESP32 จะรีสตาร์ทเมื่อตื่น)
}
void loop() {
// ใน Deep Sleep mode ไม่มี loop
}อธิบายโค้ด:
RTC_DATA_ATTR- เก็บตัวแปรใน RTC Memory ไม่หายเมื่อนอนesp_sleep_enable_timer_wakeup()- ตั้งเวลาตื่นesp_deep_sleep_start()- เข้าสู่ Deep Sleep
🔍 ลองอัปโหลด: อัปโหลดโค้ดนี้แล้วเปิด Serial Monitor (115200 baud) จะเห็นว่า ESP32 บูตทุกๆ 10 วินาที
💻 ส่วนที่ 2: เพิ่ม MQTT สำหรับส่งข้อมูล
ตอนนี้เราจะเพิ่ม MQTT เพื่อส่งข้อมูลเซ็นเซอร์ไปยัง Broker:
#include <WiFi.h>
#include <PubSubClient.h>
#include "DHT.h"
// ========== WiFi Settings ==========
const char* ssid = "YOUR_WIFI_SSID";
const char* password = "YOUR_WIFI_PASSWORD";
// ========== MQTT Settings ==========
const char* mqtt_server = "YOUR_MQTT_BROKER"; // เช่n "broker.hivemq.com"
const int mqtt_port = 1883;
const char* mqtt_topic = "cynoiot/sensors/esp32-01";
// ========== Sensor Settings ==========
#define DHTPIN 4
#define DHTTYPE DHT22
DHT dht(DHTPIN, DHTTYPE);
// ========== Deep Sleep Settings ==========
#define uS_TO_S_FACTOR 1000000
#define TIME_TO_SLEEP 60 // นอน 60 วินาที
// ========== Global Variables ==========
RTC_DATA_ATTR int bootCount = 0;
WiFiClient espClient;
PubSubClient client(espClient);
// ========== WiFi Connection ==========
void setupWiFi() {
Serial.println("กำลังเชื่อมต่อ WiFi: " + String(ssid));
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.println("IP address: " + WiFi.localIP().toString());
} else {
Serial.println("\n❌ WiFi เชื่อมต่อไม่สำเร็จ");
}
}
// ========== MQTT Connection ==========
void reconnectMQTT() {
while (!client.connected()) {
Serial.print("กำลังเชื่อมต่อ MQTT...");
String clientId = "ESP32Client-" + String(random(0xffff), HEX);
if (client.connect(clientId.c_str())) {
Serial.println("✅ เชื่อมต่อสำเร็จ!");
} else {
Serial.print("❌ ล้มเหลว, rc=");
Serial.print(client.state());
Serial.println(" ลองใหม่ใน 5 วินาที");
delay(5000);
}
}
}
// ========== Send Sensor Data ==========
void sendSensorData() {
// อ่านค่าจากเซ็นเซอร์
float h = dht.readHumidity();
float t = dht.readTemperature();
// ตรวจสอบว่าอ่านค่าได้หรือไม่
if (isnan(h) || isnan(t)) {
Serial.println("❌ ไม่สามารถอ่านค่าจาก DHT ได้!");
return;
}
// สร้าง JSON payload
String payload = "{";
payload += "\"temperature\":" + String(t, 1) + ",";
payload += "\"humidity\":" + String(h, 1) + ",";
payload += "\"bootCount\":" + String(bootCount) + ",";
payload += "\"wifiRSSI\":" + String(WiFi.RSSI());
payload += "}";
// ส่งข้อมูลไปยัง MQTT
Serial.println("ส่งข้อมูล: " + payload);
if (client.publish(mqtt_topic, payload.c_str())) {
Serial.println("✅ ส่งข้อมูลสำเร็จ!");
} else {
Serial.println("❌ ส่งข้อมูลล้มเหลว");
}
}
void setup() {
Serial.begin(115200);
dht.begin();
bootCount++;
Serial.println("\n=== Boot #" + String(bootCount) + " ===");
// เชื่อมต่อ WiFi
setupWiFi();
if (WiFi.status() == WL_CONNECTED) {
// ตั้งค่า MQTT
client.setServer(mqtt_server, mqtt_port);
// เชื่อมต่อ MQTT
reconnectMQTT();
if (client.connected()) {
// ส่งข้อมูลเซ็นเซอร์
sendSensorData();
// รอสักครู่ให้มั่นใจว่าข้อมูลถูกส่ง
delay(1000);
client.disconnect();
}
}
// ปิด WiFi เพื่อประหยัดพลังงาน
WiFi.disconnect(true);
delay(100);
Serial.println("เข้าสู่ Deep Sleep สำหรับ " + String(TIME_TO_SLEEP) + " วินาที...");
// เข้าสู่ Deep Sleep
esp_sleep_enable_timer_wakeup(TIME_TO_SLEEP * uS_TO_S_FACTOR);
esp_deep_sleep_start();
}
void loop() {
// ไม่มี loop ใน Deep Sleep
}🔧 แก้ไขค่า: อย่าลืมเปลี่ยน YOUR_WIFI_SSID, YOUR_WIFI_PASSWORD, และ YOUR_MQTT_BROKER
ทดสอบด้วย MQTT Explorer:
- ดาวน์โหลดและติดตั้ง MQTT Explorer
- เชื่อมต่อไปที่ Broker ของคุณ
- Subscribe topic:
cynoiot/sensors/esp32-01 - อัปโหลดโค้ดไปยัง ESP32
- รอดูข้อมูลทุกๆ 60 วินาที!
💻 ส่วนที่ 3: รุ่นปรับปรุง (Complete Version)
รุ่นสมบูรณ์พร้อมฟีเจอร์เพิ่มเติม:
- ✅ ตรวจสอบสถานะแบตเตอรี่
- ✅ Adaptive Sleep (ปรับเวลานอนตามเหตุการณ์)
- ✅ Error Handling ที่ดีขึ้น
- ✅ ส่งข้อมูลล่วงหน้าหากแบตใกล้หมด
#include <WiFi.h>
#include <PubSubClient.h>
#include "DHT.h"
// ========== CONFIGURATION ==========
// WiFi
const char* ssid = "YOUR_WIFI_SSID";
const char* password = "YOUR_WIFI_PASSWORD";
// MQTT
const char* mqtt_server = "broker.hivemq.com";
const int mqtt_port = 1883;
const char* mqtt_topic_temp = "cynoiot/sensors/esp32-01/temperature";
const char* mqtt_topic_hum = "cynoiot/sensors/esp32-01/humidity";
const char* mqtt_topic_status = "cynoiot/sensors/esp32-01/status";
// Sensor
#define DHTPIN 4
#define DHTTYPE DHT22
DHT dht(DHTPIN, DHTTYPE);
// Battery Monitoring
#define BAT_PIN 35 // GPIO35 สำหรับอ่านแรงดันแบต
// Deep Sleep
#define uS_TO_S_FACTOR 1000000
#define NORMAL_SLEEP 300 // 5 นาที (300 วินาที)
#define LOW_BATT_SLEEP 3600 // 1 ชั่วโมง (3600 วินาที)
// Thresholds
#define LOW_BATT_THRESHOLD 3.3 // แรงดันต่ำกว่า 3.3V = แบตใกล้หมด
// ========== GLOBAL VARIABLES ==========
RTC_DATA_ATTR int bootCount = 0;
RTC_DATA_ATTR float batteryVoltage = 0.0;
RTC_DATA_ATTR bool lowBatteryMode = false;
WiFiClient espClient;
PubSubClient client(espClient);
// ========== HELPER FUNCTIONS ==========
// อ่านแรงดันแบตเตอรี่
float readBattery() {
int adcValue = analogRead(BAT_PIN);
// แปลง ADC เป็นแรงดัน (ESP32 ADC: 0-4095 = 0-3.3V)
// ถ้าใช้ voltage divider ปรับค่าตรงนี้
float voltage = (adcValue / 4095.0) * 3.3 * 2; // *2 เพราะ voltage divider
return voltage;
}
// เชื่อมต่อ WiFi (พร้อม timeout)
bool connectWiFi() {
Serial.println("กำลังเชื่อมต่อ WiFi: " + String(ssid));
WiFi.mode(WIFI_STA);
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.println("IP: " + WiFi.localIP().toString());
Serial.println("RSSI: " + String(WiFi.RSSI()) + " dBm");
return true;
} else {
Serial.println("\n❌ WiFi เชื่อมต่อล้มเหลว");
return false;
}
}
// เชื่อมต่อ MQTT
bool connectMQTT() {
client.setServer(mqtt_server, mqtt_port);
String clientId = "ESP32Client-" + String(random(0xffff), HEX);
if (client.connect(clientId.c_str())) {
Serial.println("✅ MQTT เชื่อมต่อสำเร็จ!");
return true;
} else {
Serial.println("❌ MQTT เชื่อมต่อล้มเหลว, rc=" + String(client.state()));
return false;
}
}
// อ่านค่าเซ็นเซอร์
bool readSensor(float* temp, float* hum) {
*temp = dht.readTemperature();
*hum = dht.readHumidity();
if (isnan(*temp) || isnan(*hum)) {
Serial.println("❌ อ่านค่า DHT ล้มเหลว!");
return false;
}
Serial.println("🌡️ อุณหภูมิ: " + String(*temp, 1) + "°C");
Serial.println("💧 ความชื้น: " + String(*hum, 1) + "%");
return true;
}
// ส่งข้อมูลไป MQTT
void publishSensorData(float temp, float hum, float batt) {
char msg[50];
// ส่งอุณหภูมิ
snprintf(msg, 50, "%.1f", temp);
client.publish(mqtt_topic_temp, msg);
// ส่งความชื้น
snprintf(msg, 50, "%.1f", hum);
client.publish(mqtt_topic_hum, msg);
// ส่งสถานะระบบ
String status = "{";
status += "\"bootCount\":" + String(bootCount) + ",";
status += "\"battery\":" + String(batt, 2) + ",";
status += "\"rssi\":" + String(WiFi.RSSI()) + ",";
status += "\"lowBatt\":" + String(lowBatteryMode ? "true" : "false");
status += "}";
client.publish(mqtt_topic_status, status.c_str());
Serial.println("✅ ส่งข้อมูล MQTT เรียบร้อย");
}
// ========== MAIN SETUP ==========
void setup() {
Serial.begin(115200);
dht.begin();
pinMode(BAT_PIN, INPUT);
bootCount++;
Serial.println("\n========== BOOT #" + String(bootCount) + " ==========");
// อ่านแรงดันแบต
batteryVoltage = readBattery();
Serial.println("🔋 แรงดันแบต: " + String(batteryVoltage, 2) + "V");
// ตรวจสอบแบตต่ำ
if (batteryVoltage < LOW_BATT_THRESHOLD) {
lowBatteryMode = true;
Serial.println("⚠️ แบตใกล้หมด! ปรับเวลานอนเป็น 1 ชั่วโมง");
}
// เชื่อมต่อ WiFi
if (connectWiFi()) {
// เชื่อมต่อ MQTT
if (connectMQTT()) {
// อ่านค่าเซ็นเซอร์
float temp, hum;
if (readSensor(&temp, &hum)) {
// ส่งข้อมูล
publishSensorData(temp, hum, batteryVoltage);
delay(1000); // รอให้ส่งเสร็จ
}
}
// ตัดการเชื่อมต่อ
client.disconnect();
WiFi.disconnect(true);
delay(100);
}
// คำนวณเวลานอน
uint64_t sleepTime = lowBatteryMode ? LOW_BATT_SLEEP : NORMAL_SLEEP;
Serial.println("😴 เข้า Deep Sleep " + String(sleepTime) + " วินาที...");
Serial.println("======================================\n");
// เข้า Deep Sleep
esp_sleep_enable_timer_wakeup(sleepTime * uS_TO_S_FACTOR);
esp_deep_sleep_start();
}
void loop() {
// ไม่มี loop
}🎯 ฟีเจอร์พิเศษ: รุ่นนี้จะปรับเวลานอนเป็น 1 ชั่วโมงเมื่อแบตใกล้หมด (< 3.3V) เพื่อยืดอายุการใช้งาน!
⚡ วัดการใช้พลังงาน
มาวิเคราะห์กันว่า Deep Sleep ช่วยประหยัดแบตได้มากแค่ไหน:
สถานการณ์: ส่งข้อมูลทุก 5 นาที
| สถานะ | กระแส | เวลา | พลังงาน |
|---|---|---|---|
| Deep Sleep | 20 µA | 298 วินาที | 1.67 mAh |
| ตื่น + WiFi | 150 mA | 2 วินาที | 0.083 mAh |
| รวม/รอบ | - | 300 วินาที | 1.75 mAh |
คำนวณอายุแบต:
- แบต 2000mAh ÷ 1.75 mAh/รอบ = 1,143 รอบ
- 1,143 รอบ × 5 นาที = 5,715 นาที ≈ 95 ชั่วโมง ≈ 4 วัน
- ถ้าเพิ่มเป็นทุก 15 นาที → 12 วัน
- ถ้าเพิ่มเป็นทุก 1 ชั่วโมง → 48 วัน!
💡 เคล็ดลับ: ใช้แบต Li-ion 18650 (2000-3500mAh) เพื่ออายุการใช้งานนานขึ้น และเพิ่มช่วงเวลานอนตามความเหมาะสมกับแอปพลิเคชัน
🔧 ปัญหาที่พบบ่อยและวิธีแก้ไข
❓ ปัญหา: ESP32 ตื่นทุกครั้งที่บูต ไม่ยอมนอน
สาเหตุ: ใช้ delay() มากเกินไปใน setup()
วิธีแก้: ลด delay และเรียก esp_deep_sleep_start() ทันทีหลังจากทำงานเสร็จ
❓ ปัญหา: MQTT ส่งข้อมูลไม่ได้
สาเหตุ: ตัดการเชื่อมต่อไปเร็วเกินไปก่อนที่ข้อมูลจะส่งเสร็จ
วิธีแก้: เพิ่ม delay(1000-2000) หลังจาก client.publish()
❓ ปัญหา: DHT อ่านค่าไม่ได้ตลอด
สาเหตุ: DHT ต้องใช้เวลา 2 วินาทีระหว่างการอ่าน
วิธีแก้: เพิ่ม dht.begin() และ delay(2000) หลังจากตื่น
❓ ปัญหา: แบตหมดเร็วเกินไป
สาเหตุ: นอนน้อยเกินไป หรือใช้ WiFi นานเกินไป
วิธีแก้: เพิ่มเวลานอนเป็น 10-15 นาที หรือใช้ WiFi.setSleep(true)
❓ ปัญหา: RTC Memory หายเมื่อบูต
สาเหตุ: ไม่ได้ใช้ RTC_DATA_ATTR
วิธีแก้: ประกาศตัวแปรด้วย RTC_DATA_ATTR int variable = 0;
🎉 สรุป
ในบทความนี้คุณได้เรียนรู้:
- ✅ หลักการ Deep Sleep Mode บน ESP32
- ✅ การเชื่อมต่อ MQTT และส่งข้อมูล
- ✅ การประหยัดพลังงานด้วย Adaptive Sleep
- ✅ การวัดและคำนวณอายุแบตเตอรี่
- ✅ การแก้ปัญหาที่พบบ่อย
🚀 ถัดไป:
- เชื่อมต่อกับ CynoIoT Platform เพื่อเก็บข้อมูลใน Database
- สร้าง Dashboard ด้วย Node-RED หรือ Grafana
- ใช้ External Wakeup (ตัวตรวจจับการเคลื่อนไหว)
- ทำ OTA Updates ผ่าน MQTT
🎯 บทความที่เกี่ยวข้อง:
• เปรียบเทียบ ESP32 vs ESP8266
🌟 เริ่มต้นสร้าง IoT Sensor ประหยัดพลังงาน
สมัครใช้งาน CynoIoT Platform ฟรีวันนี้ เพื่อเก็บข้อมูลเซ็นเซอร์ สร้าง Dashboard และตรวจสอบสถานะอุปกรณ์ได้ทุกที่