วิธีต่อ MQTT กับ ESP32 ให้เสถียรและประหยัดแบตเตอรี่ สำหรับโปรเจกต์ IoT จริง
เตรียมของให้พร้อม!
โปรเจคนี้ต้องใช้: เซ็นเซอร์วัดแสง LUX Sensor Light Meter Interface I2C IIC VEML7700 TSL2591 Arduino ESP32
บทนำ: ทำไม MQTT บน ESP32 ถึงมีปัญหาบ่อย
เวลาทำโปรเจกต์ IoT ที่ใช้แบตเตอรี่ คนส่วนใหญ่เจอปัญหาเดียวกันคือ ESP32 ต่อ MQTT ได้แค่ 2-3 วันก็หมดแล้ว หรือไม่ก็ MQTT หลุดบ่อยจนระบบทำงานไม่ได้ นี่ไม่ใช่เพราะ ESP32 แย่ แต่เป็นเพราะวิธีใช้งานที่ไม่เหมาะสม
บทความนี้จะอธิบาย วิธีต่อ MQTT กับ ESP32 ให้เสถียรและประหยัดแบตเตอรี่จริงๆ โดยผมจะเล่าจากประสบการณ์ที่ใช้งานจริงในหลายโปรเจกต์ ไม่ใช่ copy จาก docs แล้วมาต่อกัน
[image: ผลเปรียบเทียบการใช้พลังงานของ ESP32 ระหว่างโหมดต่างๆ]
อุปกรณ์และซอฟต์แวร์ที่ต้องเตรียม
ก่อนเริ่มต้น เตรียมอุปกรณ์ดังนี้:
- บอร์ด ESP32 (แนะนำ ESP32-WROOM หรือ ESP32-C3 สำหรับประหยัดไฟ)
- แหล่งจ่ายไฟ (ถ้าใช้แบตเตอรี่ แนะนำ 18650 LiPo หรือ LiFePO4)
- MQTT Broker (Mosquitto บน server, หรือใช้ cloud service อย่าง HiveMQ Cloud ก็ได้)
- Arduino IDE หรือ PlatformIO
- ไลบรารี PubSubClient สำหรับ MQTT
ถ้าใครยังไม่คุ้นกับ ESP32 แนะนำอ่านบทความ ESP32 คืออะไร? เจาะลึกบอร์ด IoT ยอดนิยมที่แรงกว่า Arduino ก่อนนะครับ
การตั้งค่า MQTT Broker สำหรับ ESP32
ก่อนเขียนโค้ดบน ESP32 ต้องตั้งค่า broker ให้รองรับการเชื่อมต่อแบบประหยัดพลังงานก่อน
# mosquitto.conf สำหรับ IoT ประหยัดไฟ
max_keepalive 300
persistent_client_expiration 2h
max_connections 500
จุดสำคัญคือ max_keepalive ห้ามตั้งต่ำเกินไป ถ้าตั้ง 60 วินาที ESP32 ที่ sleep 10 นาทีจะถูกตัดทุกครั้ง แนะนำ 300 วินาทีขึ้นไป และ persistent_client_expiration ให้เผื่อไว้นานกว่า cycle ส่งข้อมูลสูงสุด 2-3 เท่า
[image: หน้าจอ config Mosquitto broker สำหรับ IoT]
โค้ด MQTT พื้นฐานบน ESP32 ที่ใช้งานได้จริง
#include <WiFi.h>
#include <PubSubClient.h>
// ตั้งค่า WiFi
const char* ssid = "YOUR_WIFI_SSID";
const char* password = "YOUR_WIFI_PASSWORD";
// ตั้งค่า MQTT
const char* mqtt_server = "192.168.1.100";
const int mqtt_port = 1883;
const char* mqtt_client_id = "esp32_sensor_01"; // ต้องคงที่!
const char* mqtt_user = "mqtt_user";
const char* mqtt_pass = "mqtt_pass";
WiFiClient espClient;
PubSubClient client(espClient);
void setup_wifi() {
delay(10);
WiFi.mode(WIFI_STA);
WiFi.begin(ssid, password);
int retry = 0;
while (WiFi.status() != WL_CONNECTED && retry < 20) {
delay(500);
retry++;
}
if (WiFi.status() != WL_CONNECTED) {
ESP.restart(); // ถ้าต่อไม่ได้ restart ใหม่ดีกว่ารอ
}
}
void reconnect_mqtt() {
while (!client.connected()) {
if (client.connect(mqtt_client_id, mqtt_user, mqtt_pass,
"esp32/offline", 1, true, "disconnected")) {
client.publish("esp32/online", "connected");
} else {
delay(2000); // รอก่อน retry
}
}
}
void setup() {
setup_wifi();
client.setServer(mqtt_server, mqtt_port);
reconnect_mqtt();
}
void loop() {
if (!client.connected()) {
reconnect_mqtt();
}
client.loop();
}
สิ่งที่ต้องสังเกต:
- ใช้
client_idคงที่เสมอ เพื่อให้ broker จำ session ได้ - ถ้าต่อ WiFi ไม่ได้ใน 20 วินาที ให้ restart แทนที่จะรอต่อไป เพราะจะทำให้ drain แบตเตอรี่เร็วมาก
- ส่ง “online” กับ “offline” topic ด้วย เพื่อตรวจสอบสถานะได้ง่าย
[image: ภาพ Serial Monitor แสดงสถานะการเชื่อมต่อ MQTT ของ ESP32]
Deep Sleep: กุญแจสำคัญของการประหยัดแบตเตอรี่
ESP32 กินไฟประมาณ 80-120mA เวลาทำงาน แต่ Deep Sleep กินแค่ 10-15µA ถ้าใช้วัดข้อมูลทุก 10 นาที แล้ว sleep ที่เหลือ จะประหยัดได้มากกว่า 99%
#define uS_TO_S_FACTOR 1000000 // ไมโครวินาที to วินาที
#define TIME_TO_SLEEP 600 // sleep 10 นาที
void goToSleep() {
// ส่ง offline ก่อน sleep
client.publish("esp32/offline", "sleeping");
client.disconnect(); // สำคัญมาก! ต้อง disconnect ก่อน
delay(100);
esp_sleep_enable_timer_wakeup(TIME_TO_SLEEP * uS_TO_S_FACTOR);
esp_deep_sleep_start();
}
ข้อผิดพลาดที่พบบ่อย: หลายคนลืม client.disconnect() ก่อน deep sleep ทำให้ broker รอ timeout นานมาก และ ESP32 ก็ยังกินไฟในโหมด sleep อยู่
การตื่นขึ้นมา reconnect อย่างรวดเร็ว
void setup() {
setup_wifi();
client.setServer(mqtt_server, mqtt_port);
reconnect_mqtt();
// อ่านค่าเซ็นเซอร์และส่ง
float temp = readTemperature();
char payload[50];
snprintf(payload, 50, "{\"temp\": %.2f, \"bat\": %d}", temp, getBattery());
client.publish("sensor/data", payload);
delay(100);
goToSleep();
}
ถ้าใช้เซ็นเซอร์วัดแสงด้วย แนะนำ เซ็นเซอร์วัดแสง LUX Sensor VEML7700 หรือ TSL2591 ซึ่งใช้ I2C กับ ESP32 ได้เลย ให้ค่าที่แม่นยำและกินไฟน้อย
เทคนิคเพิ่มเติมสำหรับความเสถียร
1. WiFi Reconnect Handler
void WiFiEvent(WiFiEvent_t event) {
switch(event) {
case SYSTEM_EVENT_STA_DISCONNECTED:
WiFi.reconnect();
break;
}
}
void setup() {
WiFi.onEvent(WiFiEvent);
// ...
}
2. Watchdog Timer
void loop() {
if (!client.connected()) {
reconnect_mqtt();
}
client.loop();
// ถ้า loop ทำงานนานเกินไปโดยไม่มี response ให้ restart
if (millis() - lastActivity > 60000) {
ESP.restart();
}
}
3. Keep Alive ที่เหมาะสม
client.setKeepAlive(120); // 120 วินาที สำหรับ cycle 10 นาที
keep-alive ควรสั้นกว่า sleep time แต่ไม่สั้นเกินไปจนเปลืองพลังงาน ถ้า sleep 10 นาที ใช้ 120 วินาที (2 นาที) กำลังดี
[image: กราฟเปรียบเทียบการใช้ไฟระหว่าง ESP32 ที่ใช้ Deep Sleep กับไม่ใช้]
การใช้ Dashboard ตรวจสอบสถานะ
เมื่อตั้งค่า MQTT บน ESP32 เรียบร้อยแล้ว อยากดูข้อมูลแบบ real-time แนะนำตั้ง Dashboard ดูได้เลย มีบทความ คู่มือเริ่มต้น Dashboard กราฟเรียลไทม์ ที่อธิบายวิธีต่อ ESP32 กับ Dashboard แบบละเอียด
สำหรับคนที่อยากลองทำ weather station มีโปรเจกต์ตัวอย่าง WIoT-2 Weather Station ที่ใช้ MQTT กับ ESP32 อยู่แล้ว
สรุป
การทำให้ MQTT บน ESP32 เสถียรและประหยัดไฟไม่ใช่เรื่องยาก แค่ต้องรู้จัก:
- Disconnect ก่อน deep sleep - ทุกครั้ง
- ใช้ client ID คงที่ - เพื่อให้ broker จำ session
- ตั้ง keep-alive ให้เหมาะสม - สั้นกว่า sleep time
- Restart เมื่อ WiFi ต่อไม่ได้ - ไม่งอมิงีรอ
- ส่ง online/offline status - เพื่อตรวจสอบง่าย
ถ้าทำตามนี้ ESP32 ต่อแบตเตอรี่ LiFePO4 ขนาด 3000mAh ส่งข้อมูลทุก 10 นาที อยู่ได้ประมาณ 3-6 เดือน ไม่ใช่ 2-3 วันแล้ว
อยากทำโปรเจคแบบนี้?
รับทำโปรเจค Arduino / IoT จบงานไว ส่งงานครบ พร้อมสอน
หากต้องการ รับทำโปรเจคอาดูโน่ หรือระบบ IoT แบบเร่งด่วน สามารถดูรายละเอียดบริการได้ที่หน้าแรก
จ้างทำโปรเจคเลย