บทความ: สร้าง IoT Sensor ประหยัดพลังงานด้วย ESP32 Deep Sleep และ MQTT

เรียนรู้วิธีสร้างเซ็นเซอร์ IoT ที่ทำงานด้วยแบตเตอรี่นานเป็นเดือนด้วย ESP32 Deep Sleep Mode และ MQTT Protocol เหมาะสำหรับโปรเจกต์ Smart Home และ Agricultural Monitoring

📅 8 มีนาคม 2026⏱️ 15 นาที🎯 ระดับกลาง🔧 ESP32, MQTT, Deep Sleep

🌙 ภาพรวม 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:

  1. ดาวน์โหลดและติดตั้ง MQTT Explorer
  2. เชื่อมต่อไปที่ Broker ของคุณ
  3. Subscribe topic: cynoiot/sensors/esp32-01
  4. อัปโหลดโค้ดไปยัง ESP32
  5. รอดูข้อมูลทุกๆ 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 และตรวจสอบสถานะอุปกรณ์ได้ทุกที่