🔋 ทำไมต้อง Deep Sleep?
เมื่อสร้างโปรเจกต์ IoT ที่ใช้แบตเตอรี่เป็นแหล่งจ่ายไฟ การประหยัดพลังงานเป็นสิ่งสำคัญที่สุด ESP32 ในโหมดปกติใช้กระแสไฟประมาณ 150-240 mA เมื่อเชื่อมต่อ WiFi ซึ่งถ้าใช้แบตเตอรี่ 2000mAh จะใช้งานได้เพียง 8-13 ชั่วโมง เท่านั้น
💡 แต่ด้วย Deep Sleep Mode กระแสไฟจะลดลงเหลือเพียง 10-100 µA (0.01-0.1 mA) ทำให้แบตเตอรี่เดิมใช้งานได้ 6-12 เดือน หรือมากกว่านั้น!
สถานการณ์ที่เหมาะกับ Deep Sleep:
- 🌡️ เซ็นเซอร์วัดอุณหภูมิ/ความชื้น ที่อ่านค่าทุก 5-15 นาที
- 🌾 โปรเจกต์ Smart Agriculture ที่ต้องการตรวจสอบค่าน้อยครั้ง
- 📦 ตัวติดตามสินค้า (Asset Tracker) ที่ส่งข้อมูลเป็นระยะ
- 🏠 Remote Control ที่รอรับสัญญาณจากผู้ใช้
- 🔔 ระบบแจ้งเตือนที่ทำงานเมื่อเกิดเหตุการณ์เฉพาะ
📊 เข้าใจ ESP32 Sleep Modes
ESP32 มีหลายโหมดการประหยัดพลังงาน แต่ละโหมดเหมาะกับ使用งานที่แตกต่างกัน:
| โหมด | กระแสไฟ | เวลาตื่น | การใช้งาน |
|---|---|---|---|
| Active (WiFi On) | 150-240 mA | - | ทำงานปกติ |
| Modem Sleep | 15-30 mA | < 1 ms | CPU ทำงาน, WiFi สลับ |
| Light Sleep | 0.8-15 mA | < 10 ms | CPU หยุด, RAM เก็บ |
| Deep Sleep | 10-100 µA | 2-50 ms | CPU/RAM ปิด, RTC เท่านั้น |
| Hibernation | 2.5-5 µA | 2-50 ms | ทุกอย่างปิด |
ℹ️ Deep Sleep คือโหมดที่นิยมที่สุดสำหรับโปรเจกต์แบตเตอรี่ เพราะสมดุลระหว่างกระแสไฟต่ำและความสามารถในการรักษาข้อมูลใน RTC Memory
⚡ การเตรียมฮาร์ดแวร์สำหรับ Low Power
ก่อนเริ่มเขียนโค้ด ต้องเตรียมฮาร์ดแวร์ให้พร้อมก่อน นี่คือสิ่งสำคัญที่หลายคนมองข้าม!
1. การเชื่อมต่อแบตเตอรี่ที่ถูกต้อง
⚠️ อย่าใช้ USB ขณะทดสอบกระแส Deep Sleep! USB จะจ่ายไฟเพิ่มทำให้วัดกระแสไฟไม่ได้ ใช้แบตเตอรี่หรือ Power Supply ภายนอกแทน
วิธีเชื่อมต่อแบตเตอรี่ Li-Ion/Li-Po:
- ขา BAT: เชื่อมต่อกับขาบวกของแบตเตอรี่ (3.7V-4.2V)
- ขา GND: เชื่อมต่อกับขาลบของแบตเตอรี่
- ขา 3V3: ใช้เพื่อจ่ายไฟให้เซ็นเซอร์ (หลีกเลี่ยงถ้าต้องการประหยัดสูงสุด)
2. ตัด LED บนบอร์ด (ตัวเลือก)
หลายบอร์ด ESP32 มี Power LED และ Status LED ที่ใช้กระแส 2-20 mA ซึ่งมากกว่า Deep Sleep เสียอีก!
- ถอนตัวต้านทาน LED ออก (สำหรับบอร์ดที่มี jumper)
- หรือใช้โค้ดปิด LED ก่อนเข้า Deep Sleep
3. ใช้ LDO ที่มีประสิทธิภาพสูง
บอร์ดบางอย่างใช้ LDO ที่ใช้กระแสไฟสูง (quiescent current) เลือกบอร์ดที่ใช้:
- AMS1117: ~5-10 mA (ไม่เหมาะกับแบตเตอรี่)
- ME6211: ~50-80 µA (ดีกว่า)
- HT7833: ~5-10 µA (ดีที่สุดสำหรับแบตเตอรี่)
💻 Deep Sleep แบบพื้นฐาน: ตื่นด้วย Timer
เริ่มต้นด้วยตัวอย่างง่ายที่สุด: ESP32 จะหลับด้วย Deep Sleep เป็นเวลาที่กำหนด แล้วตื่นขึ้นมาทำงาน
#include "esp_sleep.h"
// ตั้งค่าเวลานอน (เป็นไมโครวินาที)
// ตัวอย่าง: นอน 60 วินาที
#define SLEEP_DURATION_US 60 * 1000000
RTC_DATA_ATTR int bootCount = 0; // เก็บค่าใน RTC Memory (ไม่หายเมื่อ Deep Sleep)
void setup() {
Serial.begin(115200);
// เพิ่มจำนวนครั้งที่บูต
bootCount++;
Serial.println("Boot number: " + String(bootCount));
// แสดงสาเหตุการตื่น
esp_sleep_wakeup_cause_t wakeup_reason = esp_sleep_get_wakeup_cause();
switch(wakeup_reason) {
case ESP_SLEEP_WAKEUP_TIMER:
Serial.println("ตื่นเพราะ Timer");
break;
case ESP_SLEEP_WAKEUP_EXT0:
Serial.println("ตื่นเพราะ GPIO (EXT0)");
break;
default:
Serial.println("ไม่ใช่การตื่นจาก Deep Sleep");
break;
}
// ทำงานของคุณที่นี่ (อ่านเซ็นเซอร์, ส่งข้อมูล, ฯลฯ)
Serial.println("กำลังทำงาน...");
delay(2000); // จำลองการทำงาน 2 วินาที
Serial.println("เสร็จสิ้น กำลังเข้า Deep Sleep...");
// ตั้งค่า Timer และเข้า Deep Sleep
esp_sleep_enable_timer_wakeup(SLEEP_DURATION_US);
Serial.println("Zzz...");
Serial.flush();
// เข้า Deep Sleep
esp_deep_sleep_start();
}
void loop() {
// ไม่มีอะไรที่นี่ เพราะ Deep Sleep จะรีเซ็ตเมื่อตื่น
}✅ RTC_DATA_ATTR คือคีย์เวิร์ดสำคัญ! ตัวแปรที่ประกาศด้วยจะเก็บใน RTC Memory และไม่หายเมื่อ Deep Sleep เหมาะสำหรับเก็บ boot count, settings, หรือข้อมูลเล็กๆ
⏰ ตัวเลือกการตื่นจาก Deep Sleep
ESP32 รองรับหลายวิธีในการตื่นจาก Deep Sleep เลือกให้เหมาะกับโปรเจกต์ของคุณ:
1. Timer Wake-up (ตื่นตามเวลา)
เหมาะสำหรับเซ็นเซอร์ที่อ่านค่าเป็นระยะ เช่น อุณหภูมิทุก 10 นาที
esp_sleep_enable_timer_wakeup(60 * 1000000); // 60 วินาที2. EXT0 Wake-up (ตื่นด้วย GPIO 1 ขา)
เหมาะสำหรับปุ่มกด, เซ็นเซอร์ PIR, สวิตช์แม่เหล็ก
esp_sleep_enable_ext0_wakeup(GPIO_NUM_4, 1); // ขา 4, ตื่นเมื่อ HIGH⚠️ รองรับเพียง 1 ขา และต้องเป็น RTC IO pin เท่านั้น (0, 2, 4, 12-15, 25-27, 32-39)
3. EXT1 Wake-up (ตื่นด้วย GPIO หลายขา)
เหมาะสำหรับหลายเซ็นเซอร์ ตื่นเมื่อขาใดขาหนึ่ง ACTIVE
uint64_t mask = (1ULL << GPIO_NUM_4) | (1ULL << GPIO_NUM_5);
esp_sleep_enable_ext1_wakeup(mask, ESP_EXT1_WAKEUP_ANY_HIGH);4. Touch Wake-up (ตื่นด้วย Touch Sensor)
เหมาะสำหรับปุ่มสัมผัส, แผงควบคุมแบบ Touch
esp_sleep_enable_touchpad_wakeup();5. ULP Co-processor (ตื่นเมื่อเงื่อนไขตรง)
เหมาะสำหรับตรวจสอบเซ็นเซอร์โดยไม่ต้องตื่น CPU ประหยัดสุด!
esp_sleep_enable_ulp_wakeup();🚀 เทคนิคขั้นสูง: ประหยัดไฟสุดๆ
หลังจากใช้ Deep Sleep แล้ว เรายังสามารถลดกระแสไฟได้อีกด้วยเทคนิคเหล่านี้:
1. ปิด WiFi และ Bluetooth ทันทีที่ไม่ใช้
// ปิด WiFi
WiFi.mode(WIFI_OFF);
btStop(); // ปิด Bluetooth
// หรือใช้วิธีล้างค่า WiFi
esp_wifi_stop();
esp_wifi_deinit();
esp_bt_controller_disable();
esp_bt_controller_deinit();2. ปิด Peripherals ที่ไม่ใช้
// ปิด ADC, DAC, Sensor ที่ไม่ใช้
adc_power_off(); // ปิด ADC
dac_output_disable(DAC_CHANNEL_1);
dac_output_disable(DAC_CHANNEL_2);
// ลดความถี่ CPU (สำหรับ Light Sleep)
setCpuFrequencyMhz(80); // ลดจาก 240 MHz เป็น 80 MHz3. ใช้ ULP Co-processor แทน CPU
ULP (Ultra Low Power) Co-processor สามารถทำงานแบบง่ายๆ ได้โดยใช้กระแสเพียง ~10-150 µA เหมาะสำหรับ:
- อ่านค่าเซ็นเซอร์ ADC เป็นระยะ
- ตรวจสอบ Touch Pad
- เปรียบเทียบค่าและตัดสินใจตื่น CPU เมื่อจำเป็น
💡 ตัวอย่าง: ULP สามารถอ่านค่าอุณหภูมิทุก 10 นาที และตื่น CPU เมื่อเกิน 40°C เท่านั้น ทำให้ประหยัดแบตเตอรี่ได้มาก!
4. ปิดเซ็นเซอร์ด้วย MOSFET
หลายเซ็นเซอร์ใช้กระแสไฟมากเวลา Standby ใช้ MOSFET ตัดไฟเมื่อไม่ใช้:
// ก่อนเข้า Deep Sleep
digitalWrite(SENSOR_POWER_PIN, LOW); // ปิดเซ็นเซอร์
esp_deep_sleep_start();
// เมื่อตื่น
digitalWrite(SENSOR_POWER_PIN, HIGH); // เปิดเซ็นเซอร์
delay(100); // รอให้เซ็นเซอร์พร้อม
// อ่านค่าเซ็นเซอร์...🧮 คำนวณอายุการใช้งานแบตเตอรี่
มาคำนวณกันว่าแบตเตอรี่จะใช้งานได้นานแค่ไหน:
สูตรคำนวณ:
Battery Life (hours) = Battery Capacity (mAh) / Average Current (mA)
ตัวอย่าง 1: ไม่ใช้ Deep Sleep
- 📊 แบตเตอรี่: 2000 mAh (แบตเตอรี่ 18650)
- ⚡ กระแสเฉลี่ย: 150 mA (WiFi เปิดตลอด)
- 🕐 อายุการใช้งาน: 2000 / 150 = 13.3 ชั่วโมง
ตัวอย่าง 2: ใช้ Deep Sleep ดีๆ
📊 สถานการณ์:
- แบตเตอรี่: 2000 mAh
- ตื่นทำงาน: 10 วินาที ทุก 10 นาที (100 mA ขณะตื่น)
- Deep Sleep: 9 นาที 50 วินาที (50 µA)
🧮 คำนวณ:
- กระแสเฉลี่ย = (10s × 100mA + 590s × 0.05mA) / 600s
- กระแสเฉลี่ย = (1000 + 29.5) / 600 = 1.72 mA
- อายุการใช้งาน = 2000 / 1.72 = 1,163 ชั่วโมง ≈ 48 วัน
⚠️ ข้อควรระวัง: นี่คือคำนวณทางทฤษฎี ในความเป็นจริงต้องคิดแบตเตอรี่เสื่อม (ประมาณ 80% capacity), self-discharge, และอุณหภูมิ
🔧 ปัญหาที่พบบ่อยและวิธีแก้ไข
❌ ปัญหา: Deep Sleep ใช้กระแสไฟ 10-20 mA (เยอะเกินไป)
สาเหตุ: LDO ใช้กระแสสูง, อุปกรณ์ต่อพ่วงใช้ไฟเยอะ
วิธีแก้:
- ตรวจสอบว่าถอน USB แล้ว
- ปิด LED บนบอร์ด
- ตรวจสอบว่าเซ็นเซอร์ปิดอยู่จริง
- เปลี่ยนบอร์ดที่ใช้ LDO ประหยัดไฟ
❌ ปัญหา: ESP32 ไม่ยอมตื่นจาก Deep Sleep
สาเหตุ: ตั้งเวลาผิด, ใช้ขาที่ไม่รองรับ, แบตเตอรี่หมด
วิธีแก้:
- ตรวจสอบว่าใช้ RTC IO pin (สำหรับ EXT0/EXT1)
- ตรวจสอบแรงดันแบตเตอรี่ (ต่ำกว่า 2.3V อาจตื่นไม่ได้)
- ใช้ Serial Monitor เพื่อ Debug
❌ ปัญหา: WiFi เชื่อมต่อไม่ได้หลังตื่น
สาเหตุ: WiFi state ไม่ได้รักษาไว้ใน Deep Sleep
วิธีแก้:
- ต้องเชื่อมต่อ WiFi ใหม่ทุกครั้งที่ตื่น
- ใช้ WiFi.persistent(false) เพื่อประหยัด Flash
- พิจารณาใช้ Light Sleep แทนถ้าต้องการรักษา WiFi
❌ ปัญหา: ข้อมูลหายเมื่อตื่น
สาเหตุ: ตัวแปรปกติจะรีเซ็ตเมื่อ Deep Sleep
วิธีแก้:
- ใช้ RTC_DATA_ATTR สำหรับตัวแปรที่ต้องเก็บ
- ใช้ Preferences.h (SPIFFS) สำหรับข้อมูลขนาดใหญ่
🌍 โปรเจกต์จริง: เครื่องวัดอุณหภูมิไร้สาย
มาดูตัวอย่างโปรเจกต์จริงที่ใช้ Deep Sleep สร้างเครื่องวัดอุณหภูมิที่ส่งข้อมูลไปยัง CynoIoT Platform:
#include <WiFi.h>
#include <HTTPClient.h>
#include "esp_sleep.h"
#include "DHT.h"
// WiFi credentials
const char* ssid = "your_SSID";
const char* password = "your_PASSWORD";
// CynoIoT API endpoint
const char* apiUrl = "https://api.cynoiot.com/sensor/data";
// Sensor config
#define DHTPIN 4
#define DHTTYPE DHT22
DHT dht(DHTPIN, DHTTYPE);
// Deep sleep config
#define SLEEP_INTERVAL_MS 10 * 60 * 1000 // 10 นาที
// RTC memory variables
RTC_DATA_ATTR int bootCount = 0;
RTC_DATA_ATTR float lastTemp = 0;
RTC_DATA_ATTR float lastHum = 0;
void setup() {
Serial.begin(115200);
bootCount++;
Serial.println("=== Boot #" + String(bootCount) + " ===");
// อ่านค่าเซ็นเซอร์
dht.begin();
float temp = dht.readTemperature();
float hum = dht.readHumidity();
if (isnan(temp) || isnan(hum)) {
Serial.println("Failed to read from DHT sensor!");
temp = lastTemp;
hum = lastHum;
} else {
lastTemp = temp;
lastHum = hum;
}
Serial.printf("Temperature: %.2f°C\n", temp);
Serial.printf("Humidity: %.2f%%\n", hum);
// เชื่อมต่อ WiFi
Serial.print("Connecting to WiFi...");
WiFi.begin(ssid, password);
unsigned long startAttempt = millis();
while (WiFi.status() != WL_CONNECTED &&
millis() - startAttempt < 20000) {
delay(500);
Serial.print(".");
}
if (WiFi.status() == WL_CONNECTED) {
Serial.println(" connected!");
Serial.printf("IP: %s\n", WiFi.localIP().toString().c_str());
// ส่งข้อมูลไป CynoIoT
sendToCynoIoT(temp, hum);
// ปิด WiFi
WiFi.disconnect(true);
WiFi.mode(WIFI_OFF);
} else {
Serial.println(" failed! Will retry next time.");
}
// เตรียมเข้า Deep Sleep
Serial.printf("Entering deep sleep for %d ms...\n", SLEEP_INTERVAL_MS);
Serial.flush();
esp_sleep_enable_timer_wakeup(SLEEP_INTERVAL_MS * 1000);
esp_deep_sleep_start();
}
void loop() {
// Nothing here - deep sleep resets on wake
}
void sendToCynoIoT(float temp, float hum) {
HTTPClient http;
http.begin(apiUrl);
http.addHeader("Content-Type", "application/json");
// สร้าง JSON payload
String payload = "{\"temperature\":" + String(temp) +
",\"humidity\":" + String(hum) +
",\"device_id\":\"esp32_temp_sensor\"}";
Serial.print("Sending data to CynoIoT...");
int httpCode = http.POST(payload);
if (httpCode > 0) {
Serial.printf(" success! Code: %d\n", httpCode);
String response = http.getString();
Serial.println("Response: " + response);
} else {
Serial.printf(" failed! Error: %s\n", http.errorToString(httpCode).c_str());
}
http.end();
}📊 ผลลัพธ์: ด้วยแบตเตอรี่ 2000 mAh, โปรเจกต์นี้สามารถทำงานได้ 4-6 เดือน โดยไม่ต้องเปลี่ยนแบตเตอรี่!
🎯 สรุป
Deep Sleep ลดกระแสไฟจาก 150 mA เหลือ 10-100 µA ทำให้อายุแบตเตอรี่ยาวขึ้น 100-1000 เท่า
เลือก Wake-up Source ให้เหมาะกับโปรเจกต์: Timer, GPIO, Touch, หรือ ULP
ปิด WiFi, Bluetooth, และเซ็นเซอร์ที่ไม่ใช้ ทันที เพื่อประหยัดพลังงาน
ใช้ RTC_DATA_ATTR เพื่อรักษาข้อมูลข้ามรอบ Deep Sleep
คำนวณอายุแบตเตอรี่ก่อนทำโปรเจกต์จริง เพื่อตั้งค่าคาดหวังที่เป็นจริง