เนื้อหาในบทความ
ทำไมต้อง Deep Sleep Mode?
เมื่อสร้างอุปกรณ์ IoT ที่ทำงานด้วยแบตเตอรี่ การประหยัดพลังงานเป็นสิ่งสำคัญที่สุด ESP32 ในโหมดปกติใช้กระแสไฟประมาณ 80-240 mA ซึ่งทำให้แบตเตอรี่หมดภายในไม่กี่ชั่วโมง แต่ด้วย Deep Sleep Mode คุณสามารถลดการใช้พลังงานลงเหลือเพียง 10-100 µA ทำให้อุปกรณ์ทำงานได้นานเป็นเดือนหรือเป็นปี!
💡 เปรียบเทียบการใช้พลังงาน
- • Active Mode: ~240 mA (100% ใช้งาน)
- • Light Sleep: ~0.8 mA (WiFi เปิดอยู่)
- • Deep Sleep: ~10-100 µA (เกือบปิดทั้งหมด)
- • Hibernation: ~2.5 µA (ปิดสนิท)
ใครควรใช้ Deep Sleep?
- ✅ เซ็นเซอร์วัดอุณหภูมิ/ความชื้นที่ส่งข้อมูลทุก 10-15 นาที
- ✅ โปรเจกต์ติดตามสัตว์เลี้ยง (Pet Tracker)
- ✅ ระบบเตือนภัยไฟไหม้/แก๊สรั่ว
- ✅ เครื่องวัดปริมาณน้ำฝน/ระดับน้ำ
- ✅ Remote control ที่ใช้แบตเตอรี่
โหมดประหยัดพลังงานของ ESP32
ESP32 มีหลายโหมดสำหรับประหยัดพลังงาน เลือกใช้ตามความเหมาะสมของโปรเจกต์
1. Modem Sleep
WiFi ยังเชื่อมต่ออยู่ แต่ CPU ลดการทำงาน ใช้ ~15 mA
2. Light Sleep
CPU หยุดทำงาน แต่ WiFi/Bluetooth พร้อมปลุกได้ทันที ใช้ ~0.8 mA
3. Deep Sleep ⭐
CPU, WiFi, Bluetooth ปิดหมด เหลือแต่ RTC memory ใช้ ~10-100 µA
4. Hibernation
ปิดทุกอย่างรวม RTC memory ใช้ ~2.5 µA (ต้อง reboot ใหม่)
พื้นฐานของ Deep Sleep Mode
เมื่อ ESP32 เข้าสู่ Deep Sleep Mode สิ่งที่เกิดขึ้นมีดังนี้:
❌ สิ่งที่หยุดทำงาน
- • CPU cores ทั้ง 2 cores
- • WiFi และ Bluetooth
- • ส่วนใหญ่ของ peripherals
- • RAM ส่วนใหญ่จะหายไป
✅ สิ่งที่ยังทำงาน
- • RTC (Real-Time Clock)
- • RTC Memory (8 KB)
- • RTC GPIO pins (บาง pins)
- • ULP Coprocessor (ถ้าใช้)
⚠️ ข้อควรระวังสำคัญ
- • ตัวแปรใน RAM ทั่วไปจะหายไปหมด (ต้องเก็บใน RTC memory)
- • WiFi connection จะหลุด ต้องเชื่อมต่อใหม่เมื่อตื่น
- • โค้ดจะเริ่มทำงานใหม่จาก setup() เสมอ
- • ต้องเลือก wake source ก่อนเข้า deep sleep
วิธีการปลุก ESP32 จาก Deep Sleep
ESP32 สามารถตื่นจาก Deep Sleep ได้หลายวิธี ขึ้นอยู่กับ application ของคุณ
1. Timer (ตั้งเวลา)
ตื่นเมื่อครบเวลาที่กำหนด เหมาะกับเซ็นเซอร์ที่ส่งข้อมูลเป็นระยะ
esp_sleep_enable_timer_wakeup(60 * 1000000); // 60 วินาที2. Touch Pad (สัมผัส)
ตื่นเมื่อมีการสัมผัสที่ pin ที่กำหนด เหมาะกับปุ่มกด
esp_sleep_enable_touchpad_wakeup();3. Ext0 (External Wakeup)
ตื่นเมื่อ GPIO pin เปลี่ยนสถานะ เหมาะกับเซ็นเซอร์หรือปุ่ม
esp_sleep_enable_ext0_wakeup(GPIO_NUM_4, 0); // Pin 4, LOW4. Ext1 (Multiple GPIOs)
ตื่นเมื่อ GPIO pins ใดก็ได้เปลี่ยนสถานะ (สูงสุด 6 pins)
esp_sleep_enable_ext1_wakeup(GPIO_SEL_4 | GPIO_SEL_5, ESP_EXT1_WAKEUP_ANY_HIGH);5. ULP Coprocessor
ใช้ ULP ประมวลผลข้อมูลเซ็นเซอร์ขณะหลับ ตื่นเมื่อเงื่อนไขตรง
esp_sleep_enable_ulp_wakeup();ตัวอย่างการใช้งาน Deep Sleep แบบพื้นฐาน
มาเริ่มต้นด้วยตัวอย่างง่ายๆ ที่ใช้ Timer Wakeup เพื่อให้ ESP32 ตื่นทุกๆ 30 วินาที
#include <WiFi.h>
#include <esp_sleep.h>
// กำหนดค่า WiFi
const char* ssid = "YOUR_SSID";
const char* password = "YOUR_PASSWORD";
// ตั้งค่าเวลาหลับ (เป็นไมโครวินาที)
#define uS_TO_S_FACTOR 1000000 /* แปลงไมโครวินาทีเป็นวินาที */
#define TIME_TO_SLEEP 30 /* หลับ 30 วินาที */
// ตัวแปร RTC memory (จะไม่หายเมื่อหลับ)
RTC_DATA_ATTR int bootCount = 0;
void setup() {
Serial.begin(115200);
// นับจำนวนครั้งที่ boot
bootCount++;
Serial.println("Boot number: " + String(bootCount));
// พิมพ์สาเหตุที่ตื่น
print_wakeup_reason();
// เชื่อมต่อ WiFi
WiFi.begin(ssid, password);
Serial.print("Connecting to WiFi");
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("\nWiFi connected!");
Serial.println("IP address: ");
Serial.println(WiFi.localIP());
// ====== ทำงานของคุณที่นี่ ======
// อ่านเซ็นเซอร์ ส่งข้อมูล ฯลฯ
Serial.println("Doing work...");
// ตัดการเชื่อมต่อ WiFi
WiFi.disconnect(true);
WiFi.mode(WIFI_OFF);
// ====== เข้าสู่ Deep Sleep ======
Serial.println("Going to sleep now");
Serial.flush();
// ตั้งเวลาปลุก
esp_sleep_enable_timer_wakeup(TIME_TO_SLEEP * uS_TO_S_FACTOR);
// เข้าสู่ deep sleep
esp_deep_sleep_start();
}
void loop() {
// ไม่มีอะไรที่นี่ เพราะ deep sleep จะรีเซ็ตตัวเอง
}
// ฟังก์ชันตรวจสอบสาเหตุที่ตื่น
void print_wakeup_reason() {
esp_sleep_wakeup_cause_t wakeup_reason;
wakeup_reason = esp_sleep_get_wakeup_cause();
switch(wakeup_reason) {
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_TIMER:
Serial.println("Wakeup caused by timer");
break;
case ESP_SLEEP_WAKEUP_TOUCHPAD:
Serial.println("Wakeup caused by touchpad");
break;
case ESP_SLEEP_WAKEUP_ULP:
Serial.println("Wakeup caused by ULP program");
break;
default:
Serial.println("Wakeup was not caused by deep sleep");
break;
}
}✅ จุดสำคัญของโค้ดนี้
- •
RTC_DATA_ATTRใช้เก็บตัวแปรที่ไม่หายเมื่อหลับ - •
esp_sleep_enable_timer_wakeup()ตั้งเวลาปลุก - •
esp_deep_sleep_start()เริ่มเข้าสู่ deep sleep - •
loop()จะไม่ถูกเรียก เพราะโค้ดรีเซ็ตใหม่เสมอ
เทคนิคขั้นสูง: ประหยัดพลังงานสูงสุด
1. ปิด WiFi และ Bluetooth ทันทีที่ใช้เสร็จ
// เปิด WiFi
WiFi.mode(WIFI_STA);
WiFi.begin(ssid, password);
// ทำงานที่ต้องใช้ WiFi...
// ปิด WiFi ทันที
WiFi.disconnect(true);
WiFi.mode(WIFI_OFF);
btStop(); // ปิด Bluetooth ถ้ามี2. ลด CPU Frequency
#include <esp_wifi.h>
// ลดความเร็ว CPU จาก 240 MHz เป็น 80 MHz
setCpuFrequencyMhz(80);
// ลดความเร็ว WiFi กินกระแสน้อยลง
esp_wifi_set_ps(WIFI_PS_MAX_MODEM);3. ใช้ RTC Memory อย่างมีประสิทธิภาพ
// ตัวแปรใน RTC memory (8 KB)
RTC_DATA_ATTR int bootCount = 0;
RTC_DATA_ATTR float lastTemperature = 0;
RTC_DATA_ATTR char deviceID[16] = "ESP32_001";
// struct ซับซ้อนก็ได้
struct SensorData {
float temp;
float humidity;
int batteryLevel;
};
RTC_DATA_ATTR SensorData lastReading;4. External Wakeup กับเซ็นเซอร์
// ตื่นเมื่อมี motion จาก PIR sensor
#define PIR_SENSOR_PIN 4
void setup() {
pinMode(PIR_SENSOR_PIN, INPUT_PULLUP);
// ตั้งค่าให้ตื่นเมื่อ PIR เป็น HIGH
esp_sleep_enable_ext0_wakeup(
(gpio_num_t)PIR_SENSOR_PIN,
1 // 1 = HIGH, 0 = LOW
);
// ทำงาน...
esp_deep_sleep_start();
}5. ใช้ ULP Coprocessor (ขั้นสูง)
ULP สามารถอ่านเซ็นเซอร์และประมวลผลขณะที่ main CPU หลับอยู่ ประหยัดพลังงานสูงสุด
Note: การเขียน ULP code ต้องใช้ Assembly พิเศษ และมีความซับซ้อนสูง แนะนำให้ศึกษาจาก Espressif ULP Documentation
คำนวณอายุแบตเตอรี่
มาคำนวณกันว่าโปรเจกต์ของคุณทำงานได้นานแค่ไหน
📊 สูตรคำนวณ
กระแสเฉลี่ย (mA) = (ActiveCurrent × ActiveTime + SleepCurrent × SleepTime) / TotalCycle
อายุแบตเตอรี่ (ชั่วโมง) = BatteryCapacity / AverageCurrent
อายุแบตเตอรี่ (วัน) = BatteryHours / 24
ตัวอย่างการคำนวณ
สถานการณ์: เซ็นเซอร์อุณหภูมิ
- • แบตเตอรี่: 2000 mAh (Li-ion 18650)
- • Active time: 5 วินาที (connect WiFi + send data)
- • Sleep time: 300 วินาที (5 นาที)
- • Active current: 150 mA (เฉลี่ย)
- • Sleep current: 20 µA = 0.02 mA
// กระแสเฉลี่ยต่อรอบ (305 วินาที)
= (150 mA × 5s + 0.02 mA × 300s) / 305s
= (750 + 6) / 305
= 2.48 mA
// อายุแบตเตอรี่
= 2000 mAh / 2.48 mA
= 806 ชั่วโมง
= 33.6 วัน ≈ 1 เดือน✨ เคล็ดลับ: ยืดอายุแบตเตอรี่
- • เพิ่มระยะเวลาหลับ (จาก 5 นาที → 15 นาที = 3 เท่ายิ่งดี)
- • ลด Active time (disconnect WiFi ไวๆ)
- • เลือกบอร์ดที่มี sleep current ต่ำ (ESP32-S2, S3, C3)
- • ใช้แบตเตอรี่ขนาดใหญ่ขึ้น
- • เพิ่ม solar panel เพื่อ charge
เปรียบเทียบอายุแบตเตอรี่ (2000 mAh)
| รอบการส่งข้อมูล | กระแสเฉลี่ย | อายุแบตเตอรี่ |
|---|---|---|
| ทุก 1 นาที | ~5 mA | ~16 วัน |
| ทุก 5 นาที | ~2.5 mA | ~33 วัน |
| ทุก 15 นาที | ~0.8 mA | ~104 วัน (3.5 เดือน) |
| ทุก 1 ชั่วโมง | ~0.2 mA | ~416 วัน (13.8 เดือน) |
เฮาร์ดแวร์ Tips เพื่อประหยัดพลังงาน
1. เลือกบอร์ดที่เหมาะสม
- • ESP32-S2, S3: Sleep current ต่ำกว่า
- • ESP32-C3: ราคาถูก, กินไฟน้อย
- • หลีกเลี่ยงบอร์ดที่มี LED ติดอยู่
- • เลือกบอร์ดที่มี voltage regulator ดี
2. ตัดอุปกรณ์ที่ไม่ใช้
- • ถอด USB-to-UART chip ออก (ถ้าได้)
- • ต่อเซ็นเซอร์ผ่าน MOSFET เพื่อตัดไฟ
- • ใช้ pull-up/pull-down แทนการเปิด IO
- • เลือกเซ็นเซอร์ที่กินไฟน้อย
3. จ่ายไฟอย่างมีประสิทธิภาพ
- • ใช้ LDO ที่มี quiescent current ต่ำ
- • หลีกเลี่ยง linear regulator เมื่อต่างศักย์มาก
- • ใช้ buck converter เมื่อเหมาะสม
- • ตัด LED บนบอร์ดออก (ถ้าได้)
4. เลือกแบตเตอรี่ที่เหมาะสม
- • Li-ion/LiPo: 3.7V, high capacity
- • LiFePO4: 3.2V, 3000+ cycles
- • AA Alkaline: ต่อกัน series
- • พิจารณาใช้ solar + supercap
⚡ วัดกระแสไฟจริง
ใช้ multimeter หรือ power monitor เพื่อวัดกระแสจริงของโปรเจกต์คุณ เพราะค่าที่คำนวณอาจต่างจากจริงมาก
ปัญหาที่พบบ่อยและวิธีแก้ไข
❌ ปัญหา: ตื่นแล้ว WiFi เชื่อมต่อไม่ได้
สาเหตุ: WiFi ยังเปิดอยู่ขณะเข้า deep sleep ทำให้ state ผิดพลาด
วิธีแก้: ต้อง WiFi.disconnect(true) และ WiFi.mode(WIFI_OFF) ก่อนเข้า deep sleep เสมอ
❌ ปัญหา: ตัวแปรหายหมดเมื่อตื่น
สาเหตุ: เก็บตัวแปรใน RAM ปกติ ซึ่งจะหายเมื่อ deep sleep
วิธีแก้: ใช้ RTC_DATA_ATTR สำหรับตัวแปรที่ต้องเก็บไว้
❌ ปัญหา: ตื่นแล้ว setup() ไม่ทำงาน
สาเหตุ: ผิด ในความเป็นจริง setup() จะถูกเรียกทุกครั้งที่ตื่น
วิธีแก้: ตรวจสอบ Serial Monitor และเพิ่ม Serial.flush() ก่อน sleep
❌ ปัญหา: แบตเตอรี่หมดเร็วมาก
สาเหตุ: อาจมีอุปกรณ์อื่นกินไฟ เช่น LED, regulator, หรือเซ็นเซอร์
วิธีแก้: วัดกระแสไฟแต่ละส่วน และตัดสิ่งที่ไม่จำเป็นออก
❌ ปัญหา: Ext0 wakeup ไม่ทำงาน
สาเหตุ: ใช้ GPIO pin ที่ไม่รองรับ RTC wakeup
วิธีแก้: ต้องใช้ RTC_GPIO pins ดูรายการจาก datasheet ESP32
❌ ปัญหา: หลับแล้วไม่ยอมตื่น
สาเหตุ: ไม่ได้ตั้งค่า wake source ก่อนเข้า deep sleep
วิธีแก้: ต้องเรียก esp_sleep_enable_xxx_wakeup() ก่อน esp_deep_sleep_start() เสมอ
สรุป
Deep Sleep Mode เป็นเครื่องมือที่ทรงพลังสำหรับสร้าง IoT device ที่ทำงานด้วยแบตเตอรี่ได้นาน ด้วยการลดการใช้พลังงานจาก 240 mA เหลือเพียง 10-100 µA คุณสามารถสร้างอุปกรณ์ที่ทำงานได้เป็นเดือนหรือเป็นปี!
สิ่งที่คุณได้เรียนรู้:
- ✅ ความแตกต่างระหว่างโหมดประหยัดพลังงานต่างๆ
- ✅ วิธีการตั้งค่า Deep Sleep และ Wake sources
- ✅ การใช้ RTC Memory เพื่อเก็บข้อมูลข้ามรอบ sleep
- ✅ เทคนิคขั้นสูงเพื่อลดการใช้พลังงาน
- ✅ การคำนวณอายุแบตเตอรี่
- ✅ การแก้ปัญหาที่พบบ่อย
🎯 ถัดไป
- • เปรียบเทียบ Sleep Modes ของ Arduino
- • ศึกษา บทความเกี่ยวกับ IoT sensors อื่นๆ
- • ลองสร้าง Weather Station ที่ทำงานด้วยแบตเตอรี่
- • ศึกษา ULP Coprocessor สำหรับ application ขั้นสูง