เนื้อหาในบทความ
ทำไมต้อง Deep Sleep?
เคยเจอปัญหาโปรเจกต์ IoT ที่ใช้แบตเตอรี่หมดภายในวันเดียวไหม? ESP32 ที่ทำงานปกติใช้กระแสไฟ ~240mA เมื่อเปิด WiFi แบบเต็มสูบ แบตเตอรี่ 2000mAh จะหมดภายใน ~8 ชั่วโมง เท่านั้น!
แต่ด้วย Deep Sleep Mode กระแสไฟจะลดลงเหลือเพียง ~10µA (0.01mA) ทำให้แบตเตอรี่เดิมใช้งานได้ 6-12 เดือน หรือมากกว่านั้น!
📊 เปรียบเทียบการใช้พลังงาน
Active Mode (WiFi เปิด)
~240mA
แบต 2000mAh = 8 ชม.
Deep Sleep Mode
~10µA (0.01mA)
แบต 2000mAh = 6-12 เดือน
โหมดประหยัดไฟของ ESP32
ESP32 มีหลายโหมดสำหรับประหยัดพลังงาน เลือกใช้ตามความเหมาะสมกับโปรเจกต์:
1. Modem Sleep
ปิด WiFi แต่ CPU ยังทำงาน
- • กระแส: ~20-70mA
- • ใช้เมื่อ: ไม่ต้องใช้ WiFi ต่อเนื่อง
- • Wake-up: ทันที
2. Light Sleep
CPU หยุดทำงาน แต่ RAM ยังเก็บข้อมูล
- • กระแส: ~0.8-15mA
- • ใช้เมื่อ: ต้องการตื่นเร็วๆ
- • Wake-up: ไม่กี่มิลลิวินาที
3. Deep Sleep ⭐
แนะนำCPU และ WiFi ปิดทั้งหมด เก็บข้อมูลเฉพาะ RTC Memory
- • กระแส: ~10µA (0.01mA)
- • ใช้เมื่อ: ต้องการประหยัดพลังงานสูงสุด
- • Wake-up: ต้อง Restart โปรแกรมใหม่
4. Hibernation
ปิดทุกอย่าง รวมถึง RTC Memory
- • กระแส: ~2.5µA
- • ใช้เมื่อ: ประหยัดพลังงานสูงสุด ไม่สนข้อมูล
- • Wake-up: ต้องเริ่มต้นใหม่ทั้งหมด
เริ่มต้น Deep Sleep ง่ายๆ
มาเริ่มต้นด้วยตัวอย่างพื้นฐาน: ให้ ESP32 นอน Deep Sleep เป็นเวลา 30 วินาที แล้วตื่นมาทำงานอีกครั้ง
/*
* ESP32 Deep Sleep Basic Example
* ตัวอย่างการใช้งาน Deep Sleep Mode เบื้องต้น
* โดยให้ ESP32 นอน 30 วินาที แล้วตื่นมาทำงานอีกครั้ง
*/
#define uS_TO_S_FACTOR 1000000 // แปลงไมโครวินาทีเป็นวินาที
#define TIME_TO_SLEEP 30 // เวลานอน (วินาที)
void setup() {
Serial.begin(115200);
// รอเวลาสักครู่เพื่อให้ Serial เตรียมพร้อม
delay(1000);
// พิมพ์ข้อความเริ่มต้น
Serial.println("ESP32 เริ่มต้นทำงาน!");
Serial.print("จะเข้า Deep Sleep เป็นเวลา ");
Serial.print(TIME_TO_SLEEP);
Serial.println(" วินาที");
// ตั้งค่าเวลานอน
esp_sleep_enable_timer_wakeup(TIME_TO_SLEEP * uS_TO_S_FACTOR);
// เข้าสู่ Deep Sleep Mode
Serial.println("กำลังเข้า Deep Sleep Mode...");
esp_deep_sleep_start();
}
void loop() {
// ใน Deep Sleep Mode จะไม่มีการทำงานใน loop()
// เพราะโปรแกรมจะเริ่มใหม่ที่ setup() เสมอเมื่อตื่น
}💡 สิ่งสำคัญที่ต้องรู้
- • เมื่อ ESP32 ตื่นจาก Deep Sleep โปรแกรมจะเริ่มใหม่ที่
setup()เสมอ - • ข้อมูลในตัวแปรปกติจะหายไป ต้องใช้ RTC Memory ถ้าต้องการเก็บข้อมูล
- • Deep Sleep ใช้กระแสไฟเพียง ~10µA ประหยัดมาก!
- • สามารถตั้งเวลานอนได้นานสุด ~58 ปี (ในทางทฤษฎี)
วิธีปลุก ESP32 จาก Deep Sleep
ESP32 สามารถตื่นจาก Deep Sleep ได้หลายวิธี เลือกใช้ตามความเหมาะสมกับโปรเจกต์:
⏰ Timer Wake-up
ตั้งเวลาปลุก เช่น ทุกๆ 10 นาที
เหมาะกับ: เซ็นเซอร์ที่วัดค่าเป็นระยะ
👆 Touch Wake-up
สัมผัสที่ขา Touch Pin
เหมาะกับ: ปุ่มสัมผัสแบบประหยัดไฟ
⚡ External Wake-up
สัญญาณจากภายนอก เช่น PIR Sensor
เหมาะกับ: ตรวจจับความเคลื่อนไหว
🌐 ULP Co-processor
ใช้ ULP ตรวจสอบเซ็นเซอร์ขณะนอน
เหมาะกับ: ตรวจสอบเซ็นเซอร์แบบละเอียด
ตัวอย่าง: External Wake-up ด้วย PIR Sensor
ตัวอย่างนี้ใช้ PIR Motion Sensor เพื่อปลุก ESP32 เมื่อตรวจจับความเคลื่อนไหว
/*
* ESP32 PIR Motion Sensor Wake-up
* ปลุก ESP32 เมื่อตรวจจับความเคลื่อนไหวด้วย PIR Sensor
*
* วิธีต่อสาย:
* PIR VCC -> 3.3V
* PIR GND -> GND
* PIR OUT -> GPIO 4
*/
#define RTC_DATA_ATTR // บันทึกข้อมูลใน RTC Memory (ไม่หายเมื่อ Deep Sleep)
RTC_DATA_ATTR int bootCount = 0; // นับจำนวนครั้งที่ Boot
const int PIR_SENSOR_PIN = 4; // ขาที่ต่อกับ PIR Sensor
void setup() {
Serial.begin(115200);
delay(1000);
// เพิ่มจำนวนครั้งที่ Boot
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_EXT0:
Serial.println("ตื่นเพราะตรวจจับความเคลื่อนไหว (PIR Sensor)!");
break;
case ESP_SLEEP_WAKEUP_TIMER:
Serial.println("ตื่นเพราะหมดเวลา Timer");
break;
default:
Serial.println("ตื่นเพราะสาเหตุอื่น");
break;
}
// ทำงานที่ต้องการ เช่น ส่งข้อมูลไป Server
doSomething();
// ตั้งค่าให้ตื่นเมื่อมีสัญญาณจาก PIR Sensor (EXT0)
esp_sleep_enable_ext0_wakeup(
GPIO_NUM_4, // ขาที่ต่อกับ PIR Sensor
1 // 1 = HIGH, 0 = LOW
);
Serial.println("กำลังเข้า Deep Sleep Mode...");
Serial.println("รอจนกว่าจะตรวจจับความเคลื่อนไหว...");
esp_deep_sleep_start();
}
void loop() {
// ไม่มีการทำงานใน loop เมื่อใช้ Deep Sleep
}
void doSomething() {
Serial.println("กำลังทำงาน...");
// เพิ่มโค้ดของคุณที่นี่ เช่น:
// - อ่านค่าเซ็นเซอร์
// - ส่งข้อมูลผ่าน WiFi
// - บันทึกข้อมูล
delay(2000);
Serial.println("ทำงานเสร็จสิ้น");
}วัดกระแสไฟจริง
เพื่อคำนวณอายุแบตเตอรี่ที่แม่นยำ ต้องวัดกระแสไฟจริงของโปรเจกต์คุณเอง
วิธีวัดกระแสไฟ
อุปกรณ์ที่ต้องใช้
- • Multimeter ที่วัดกระแสไฟ µA ได้
- • แหล่งจ่ายไฟ เช่น แบตเตอรี่หรือ Power Supply
- • ตัวต้านทาน 10Ω (สำหรับวัดกระแส Active Mode)
ขั้นตอนการวัด
- 1ต่อ Multimeter แบบ Series (ต่อเรียง) ระหว่างแบตเตอรี่และ ESP32
- 2ตั้งค่า Multimeter ที่ 200µA DC หรือ 200mA DC (ขึ้นกับโหมด)
- 3วัดกระแสในแต่ละโหมด: Active, Light Sleep, Deep Sleep
- 4บันทึกค่าและคำนวณอายุแบตเตอรี่
⚠️ ข้อควรระวัง
- • อย่าวัดกระแส Active Mode โดยตรง (อาจเกินขอบเขตของ Multimeter)
- • ใช้ตัวต้านทาน 10Ω และวัดแรงดันข้ามตัวต้านทานแทน
- • ระวังอย่าให้ขาสั้น (Short Circuit)
- • USB Programmer บางตัวมีผลต่อการวัดกระแส Deep Sleep
เทคนิคขั้นสูง
นอกจาก Deep Sleep แล้ว ยังมีเทคนิคอื่นๆ เพื่อประหยัดพลังงานเพิ่มเติม:
1. ปิด WiFi และ Bluetooth เมื่อไม่ได้ใช้
WiFi และ Bluetooth ใช้พลังงานมาก ปิดทันทีที่ไม่ได้ใช้
// ปิด WiFi
WiFi.mode(WIFI_OFF);
// ปิด Bluetooth
btStop();
// เปิดใหม่เมื่อต้องใช้
WiFi.begin(ssid, password);2. ลดความเร็ว CPU
ลดความเร็ว CPU เมื่อไม่ต้องการประมวลผลเยอะ
// ลดความเร็ว CPU เป็น 80MHz (จาก 240MHz)
setCpuFrequencyMhz(80);
// กลับสู่ความเร็วปกติ
setCpuFrequencyMhz(240);ประหยัด ~20-30% พลังงาน แต่ทำงานช้าลง
3. ใช้ RTC Memory เก็บข้อมูล
ข้อมูลใน RTC Memory จะไม่หายเมื่อ Deep Sleep
// ประกาศตัวแปรใน RTC Memory
RTC_DATA_ATTR int bootCount = 0;
RTC_DATA_ATTR float sensorData[10];
// ข้อมูลจะยังอยู่หลังจากตื่นจาก Deep Sleep4. ใช้ ULP Co-processor
ULP สามารถอ่านเซ็นเซอร์ได้ขณะที่ ESP32 นอน Deep Sleep
เหมาะสำหรับ: ตรวจสอบเซ็นเซอร์ที่ต้องการความแม่นยำสูง
5. เลือกใช้บอร์ดที่ประหยัดไฟ
บอร์ด ESP32 บางรุ่นมีวงจรประหยัดไฟในตัว
- • ESP32-WROOM-32 มี LDO ในตัว (กระแส Deep Sleep ~10µA)
- • ESP32-WROVER มี PSRAM (กระแส Deep Sleep ~20µA)
- • บอร์ดบางรุ่นมี LED ที่ติดอยู่ ทำให้ใช้ไฟมากขึ้น
คำนวณอายุแบตเตอรี่
สูตรคำนวณอายุแบตเตอรี่:
อายุแบตเตอรี่ (ชั่วโมง) =
ความจุแบต (mAh) ÷ กระแสเฉลี่ย (mA)
ตัวอย่างการคำนวณ
โปรเจกต์: เซ็นเซอร์วัดอุณหภูมิทุก 10 นาที
- • แบตเตอรี่: 2000mAh (พวง AA 2 ก้อน)
- • Active Mode: 200mA นาน 5 วินาที (เชื่อมต่อ WiFi ส่งข้อมูล)
- • Deep Sleep: 0.01mA นาน 595 วินาที (รอ 10 นาที)
กระแสเฉลี่ยต่อรอบ (10 นาที):
(200mA × 5s + 0.01mA × 595s) ÷ 600s
= (1000 + 5.95) ÷ 600
= 1.67mA
อายุแบตเตอรี่:
2000mAh ÷ 1.67mA = 1197 ชั่วโมง ≈ 50 วัน
💡 เคล็ดลับเพิ่มอายุแบตเตอรี่
- • เพิ่มระยะห่างระหว่างการอ่านเซ็นเซอร์ (เช่น ทุก 30 นาที แทน 10 นาที)
- • ลดเวลาที่ใช้ WiFi (ส่งข้อมูลเร็วๆ แล้วปิดทันที)
- • ใช้แบตเตอรี่ Li-Ion 18650 (2000-3500mAh) แทน AA
- • เพิ่มแผงโซลาร์เซลล์ชาร์จแบตเตอรี่
- • ใช้ LDO ที่มีประสิทธิภาพสูง (เช่น HT7333, ME6211)
ตัวอย่างจริง: เซ็นเซอร์โหนดวัดอุณหภูมิและความชื้น
มาดูตัวอย่างเต็มๆ ของเซ็นเซอร์โหนดที่ใช้ DHT22 วัดอุณหภูมิและความชื้น และส่งข้อมูลไปยัง CynoIoT Platform ทุกๆ 15 นาที
/*
* ESP32 Low Power Sensor Node with DHT22
* เซ็นเซอร์โหนดวัดอุณหภูมิและความชื้น ประหยัดไฟ
* ส่งข้อมูลไป CynoIoT ทุกๆ 15 นาที
*
* อุปกรณ์:
* - ESP32 Board
* - DHT22 Sensor (ต่อที่ GPIO 4)
* - แบตเตอรี่ Li-Ion 18650
*
* คาดว่าอายุแบตเตอรี่: 6-12 เดือน
*/
#include "DHT.h"
#include <WiFi.h>
#include <HTTPClient.h>
// ===== WiFi Credentials =====
const char* ssid = "YOUR_SSID";
const char* password = "YOUR_PASSWORD";
// ===== CynoIoT Settings =====
const char* cynoiot_server = "api.cynoiot.com";
const char* device_id = "YOUR_DEVICE_ID";
const char* api_key = "YOUR_API_KEY";
// ===== Sensor Settings =====
#define DHTPIN 4 // ขาที่ต่อกับ DHT22
#define DHTTYPE DHT22
DHT dht(DHTPIN, DHTTYPE);
// ===== Sleep Settings =====
#define uS_TO_S_FACTOR 1000000 // แปลงไมโครวินาทีเป็นวินาที
#define TIME_TO_SLEEP 900 // เวลานอน (15 นาที = 900 วินาที)
// ===== RTC Memory (เก็บข้อมูลขณะ Deep Sleep) =====
RTC_DATA_ATTR int bootCount = 0;
RTC_DATA_ATTR float avgTemp = 0;
RTC_DATA_ATTR float avgHum = 0;
RTC_DATA_ATTR int readingsCount = 0;
// ===== Function Prototypes =====
void readSensor();
void sendData(float temp, float hum);
void enterDeepSleep();
void setup() {
// เพิ่มจำนวนครั้งที่ Boot
bootCount++;
Serial.begin(115200);
delay(1000);
Serial.println("=================================");
Serial.println("ESP32 Sensor Node - Boot #" + String(bootCount));
Serial.println("=================================");
// เริ่มต้นใช้งานเซ็นเซอร์
dht.begin();
// อ่านค่าเซ็นเซอร์
readSensor();
// เข้าสู่ Deep Sleep Mode
enterDeepSleep();
}
void loop() {
// ไม่มีการทำงานใน loop เมื่อใช้ Deep Sleep
}
void readSensor() {
Serial.println("กำลังอ่านค่าเซ็นเซอร์ DHT22...");
// อ่านค่าอุณหภูมิและความชื้น
float h = dht.readHumidity();
float t = dht.readTemperature();
// ตรวจสอบว่าอ่านค่าสำเร็จหรือไม่
if (isnan(h) || isnan(t)) {
Serial.println("Failed to read from DHT sensor!");
return;
}
Serial.print("อุณหภูมิ: ");
Serial.print(t);
Serial.print("°C | ความชื้น: ");
Serial.print(h);
Serial.println("%");
// คำนวณค่าเฉลี่ย
if (readingsCount == 0) {
avgTemp = t;
avgHum = h;
} else {
avgTemp = (avgTemp * readingsCount + t) / (readingsCount + 1);
avgHum = (avgHum * readingsCount + h) / (readingsCount + 1);
}
readingsCount++;
// ส่งข้อมูลไป CynoIoT (เฉพาะครั้งที่ 5 เพื่อประหยัดแบต)
if (bootCount % 5 == 0) {
sendData(avgTemp, avgHum);
// Reset averages
avgTemp = 0;
avgHum = 0;
readingsCount = 0;
}
}
void sendData(float temp, float hum) {
Serial.println("กำลังเชื่อมต่อ WiFi...");
// เชื่อมต่อ WiFi
WiFi.begin(ssid, password);
unsigned long startTime = millis();
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
// Timeout หลังจาก 10 วินาที
if (millis() - startTime > 10000) {
Serial.println("\nเชื่อมต่อ WiFi ไม่สำเร็จ!");
return;
}
}
Serial.println("\nWiFi เชื่อมต่อสำเร็จ!");
Serial.print("IP address: ");
Serial.println(WiFi.localIP());
// ส่งข้อมูลไป CynoIoT
HTTPClient http;
String url = "https://" + String(cynoiot_server) + "/api/v1/data";
http.begin(url);
http.addHeader("Content-Type", "application/json");
http.addHeader("X-API-Key", api_key);
// สร้าง JSON payload
String payload = "{\"device_id\":\"" + String(device_id) +
"\",\"temperature\":" + String(temp) +
",\"humidity\":" + String(hum) + "}";
Serial.println("กำลังส่งข้อมูล...");
int httpResponseCode = http.POST(payload);
if (httpResponseCode > 0) {
Serial.print("HTTP Response code: ");
Serial.println(httpResponseCode);
String response = http.getString();
Serial.println("Response: " + response);
} else {
Serial.print("Error code: ");
Serial.println(httpResponseCode);
}
http.end();
// ปิด WiFi เพื่อประหยัดพลังงาน
WiFi.mode(WIFI_OFF);
Serial.println("ปิด WiFi เรียบร้อย");
}
void enterDeepSleep() {
Serial.println("=================================");
Serial.print("Boot count: ");
Serial.println(bootCount);
Serial.print("เฉลี่ยอุณหภูมิ: ");
Serial.print(avgTemp);
Serial.println("°C");
Serial.print("เฉลี่ยความชื้น: ");
Serial.print(avgHum);
Serial.println("%");
// ตั้งค่าเวลานอน
esp_sleep_enable_timer_wakeup(TIME_TO_SLEEP * uS_TO_S_FACTOR);
Serial.println("กำลังเข้า Deep Sleep Mode...");
Serial.print("จะตื่นอีกครั้งใน ");
Serial.print(TIME_TO_SLEEP / 60);
Serial.println(" นาที");
Serial.println("=================================\n");
// เข้าสู่ Deep Sleep Mode
esp_deep_sleep_start();
}📊 สรุปสมรรถนะ
อายุแบตเตอรี่โดยประมาณ
6-12 เดือน
ระยะห่างการส่งข้อมูล
ทุก 75 นาที
กระแสไฟเฉลี่ย
~0.5mA
ค่าใช้จ่ายต่อเดือน
~0.05 บาท
สรุป
Deep Sleep Mode เป็นเทคนิคที่ทรงพลังที่สุดสำหรับประหยัดพลังงานในโปรเจกต์ ESP32 สามารถยืดอายุแบตเตอรี่จากวันเดียวเป็นหลายเดือนได้
ในบทความนี้เราได้เรียนรู้:
- • โหมดประหยัดไฟของ ESP32 ทั้งหมด 4 โหมด
- • วิธีใช้งาน Deep Sleep พื้นฐานและขั้นสูง
- • วิธีปลุก ESP32 จาก Deep Sleep (Timer, Touch, External)
- • เทคนิคขั้นสูงในการประหยัดพลังงานเพิ่มเติม
- • การคำนวณอายุแบตเตอรี่ที่แม่นยำ
- • ตัวอย่างเซ็นเซอร์โหนดจริงที่ใช้งานได้
หวังว่าบทความนี้จะช่วยให้คุณสร้างโปรเจกต์ IoT ที่ประหยัดพลังงานและใช้งานได้นานขึ้น!