- Mengapa Deep Sleep? Perbandingan Daya & Umur Baterai
- Mode Daya ESP32 (Active, Modem, Light Sleep, Deep Sleep, Hibernate)
- Deep Sleep dengan Timer Wakeup
- Deep Sleep dengan GPIO Wakeup (Touch/Button)
- Deep Sleep dengan ULP Coprocessor
- Mengukur Konsumsi Daya (Multimeter & INA219)
- Mengirim Data Sebelum Tidur (WiFi Flush & MQTT Disconnect)
- Proyek: Sensor Suhu Battery-Powered 1 Tahun
- Quiz Pemahaman
1. Mengapa Deep Sleep? Perbandingan Daya & Umur Baterai
ESP32 adalah mikrokontroler yang sangat powerful dengan WiFi dan Bluetooth built-in, namun kemampuan tersebut datang dengan konsumsi daya yang relatif besar jika dibandingkan mikrokontroler sederhana seperti ATtiny85. Saat mode aktif penuh dengan WiFi aktif, ESP32 bisa mengonsumsi hingga 240 mA β jumlah yang cukup untuk menghabiskan baterai AAA dalam waktu kurang dari dua hari.
Inilah mengapa ESP32 menyediakan fitur Deep Sleep: mode tidur ultra-rendah daya yang hanya mengonsumsi sekitar 5-10 Β΅A. Selisihnya luar biasa β hampir 50.000 kali lebih hemat daya dibanding mode aktif!
| Skenario | Konsumsi Daya | Umur Baterai 1000mAh |
|---|---|---|
| WiFi aktif (RX/TX) | 240 mA | ~4 jam |
| WiFi idle, aktif | 150 mA | ~6.7 jam |
| Modem Sleep (CPU on) | 20 mA | ~50 jam |
| Light Sleep | 0.8 mA | ~52 hari |
| Deep Sleep | 0.005 mA (5 Β΅A) | ~22 tahun* |
| Hibernate | 0.0025 mA (2.5 Β΅A) | ~45 tahun* |
*Umur baterai pada Deep Sleep dihitung secara teoretis tanpa memperhitungkan self-discharge baterai (sekitar 5-20% per tahun), arus bocor regulator tegangan, dan arus sensor yang terpasang. Umur baterai nyata biasanya 1-3 tahun untuk proyek IoT sederhana yang bangun setiap 5-15 menit sekali.
Bayangkan ESP32 seperti manusia. Mode aktif adalah saat berlari maraton (boros energi). Deep Sleep adalah tidur nyenyak di malam hari β tubuh tetap hidup tapi konsumsi energi sangat minimal. Setiap kali terbangun, ESP32 perlu "pemanasan" sebentar sebelum bisa beraktivitas kembali.
2. Mode Daya ESP32
ESP32 memiliki lima mode operasi utama, masing-masing menawarkan keseimbangan berbeda antara fungsionalitas dan efisiensi daya. Memahami perbedaan masing-masing mode adalah kunci untuk merancang proyek IoT hemat daya yang efektif.
Konsumsi Daya (mA) β β β Active Mode (WiFi ON) β 240 mA β ββββββββββββββββββββββββββββββββββββββββββββββββββββββ β β β Active Mode (WiFi OFF) β 24 mA β ββββββ β β β Modem Sleep β 20 mA β βββββ β β β Light Sleep β 0.8 mA β β β β β Deep Sleep β 0.005 mA (5 Β΅A) β Β· β β β Hibernate β 0.0025 mA (2.5 Β΅A) β Β· βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
2.1 Active Mode (Mode Aktif)
Dalam mode aktif, kedua core CPU berjalan penuh. Modul WiFi dan/atau Bluetooth bisa aktif atau dimatikan. Mode ini cocok untuk perhitungan berat atau komunikasi real-time, tetapi paling boros daya. Gunakan hanya saat benar-benar diperlukan.
2.2 Modem Sleep
CPU tetap berjalan tetapi modul WiFi/Bluetooth dimatikan saat tidak digunakan. ESP32 akan mematikan modem secara otomatis saat tidak ada paket data yang ditransmisikan. Mode ini sangat berguna untuk aplikasi yang memproses data secara lokal tanpa komunikasi konstan.
2.3 Light Sleep
CPU berhenti sementara clock dimatikan, namun semua RAM tetap terisi. ESP32 bisa terbangun dari timer, GPIO, atau peripheral lainnya. Kekurangannya: waktu wake-up relatif cepat dan semua state tetap tersimpan, tetapi konsumsi daya masih terlalu tinggi untuk baterai kecil.
2.4 Deep Sleep
Mode yang paling banyak digunakan untuk proyek hemat daya. CPU, WiFi, Bluetooth, dan sebagian besar peripheral dimatikan. Yang tetap aktif hanyalah:
- RTC (Real-Time Clock) β untuk timer wakeup
- RTC Memory β hingga 8 KB data bisa disimpan
- ULP Coprocessor β bisa menjalankan program sederhana
- GPIO wake pin β untuk membangunkan dari tombol/touch
Saat masuk Deep Sleep, semua isi SRAM hilang. Program akan dimulai ulang dari awal (seperti reboot). Inilah sebabnya kita perlu menyimpan data penting di RTC Memory atau EEPROM/SPIFFS sebelum tidur.
2.5 Hibernate
Mode paling hemat daya. Hampir semua komponen dimatikan termasuk RTC. Hanya skema wake-up eksternal yang tetap aktif. ESP32 harus boot ulang dari awal setiap kali terbangun β tidak ada RTC memory yang tersimpan. Cocok untuk skenario wake-up eksternal yang sangat jarang.
| Mode | CPU | WiFi/BT | RAM | Wake Source |
|---|---|---|---|---|
| Active | β Running | β Bisa ON/OFF | β Full | - |
| Modem Sleep | β Running | β Dimatikan | β Full | - |
| Light Sleep | β Dijeda | β Dimatikan | β Full | Timer, GPIO, UART |
| Deep Sleep | β Mati | β Mati | β οΈ RTC only | Timer, GPIO, Touch, ULP |
| Hibernate | β Mati | β Mati | β Hilang | GPIO eksternal |
3. Deep Sleep dengan Timer Wakeup
Mode wakeup paling sederhana dan paling sering digunakan. ESP32 akan bangun secara otomatis setelah interval waktu tertentu yang ditentukan. Ini cocok untuk aplikasi seperti sensor lingkungan yang membaca data secara berkala.
Cara Kerja Timer Wakeup
βββββββββββ ββββββββββββ βββββββββββββββ
β BOOT ββββββΆβ Bekerja ββββββΆβ Deep Sleep β
β (reset) β β (baca & β β (tunggu β
β βββββββ kirim) βββββββ timer) β
βββββββββββ ββββββββββββ βββββββββββββββ
β² β
β Waktu: T wake β
β Waktu: T sleep β
ββββββββββββββββββββββββββββββββββββ
Rasio duty cycle = T_sleep / (T_wake + T_sleep)
Contoh: 300 detik sleep, 5 detik wake = 98.3% duty cycle
Library ESP32 Arduino menyediakan fungsi bawaan yang sangat mudah digunakan:
// ESP32 Deep Sleep dengan Timer Wakeup
// IoTHub - https://iothub.id
#include <Arduino.h>
// Definisikan waktu tidur dalam mikrosekon
// 1 detik = 1000000 Β΅s
// 5 menit = 300000000 Β΅s
// 15 menit = 900000000 Β΅s
#define TIME_TO_SLEEP 300 // 300 detik = 5 menit
RTC_DATA_ATTR int jumlahBangun = 0;
void setup() {
Serial.begin(115200);
Serial.flush();
// Tambah jumlah wake counter di RTC Memory
jumlahBangun++;
Serial.println("=== ESP32 Deep Sleep Timer ===");
Serial.print("Wake-up ke-: ");
Serial.println(jumlahBangun);
Serial.print("Timestamp: ");
Serial.println(millis());
Serial.print("Menghabiskan waktu selama: ");
Serial.print(TIME_TO_SLEEP);
Serial.println(" detik...");
// --- Lakukan pekerjaan di sini ---
// Baca sensor, kirim data, kontrol LED, dll.
// Nyalakan LED indikator selama 1 detik
pinMode(LED_BUILTIN, OUTPUT);
digitalWrite(LED_BUILTIN, HIGH);
delay(1000);
digitalWrite(LED_BUILTIN, LOW);
// Atur waktu sleep dan masuk Deep Sleep
esp_sleep_enable_timer_wakeup(TIME_TO_SLEEP * 1000000ULL);
Serial.println("Masuk Deep Sleep...");
Serial.flush(); // Pastikan serial selesai terkirim
esp_deep_sleep_start();
// Kode di bawah TIDAK akan pernah dieksekusi
}
void loop() {
// Tidak digunakan karena ESP32 bangun dari setup()
}
RTC_DATA_ATTR adalah atribut yang menandai variabel disimpan di RTC Memory, bukan SRAM biasa. RTC Memory tetap terisi meskipun ESP32 dalam Deep Sleep, sehingga variabel jumlahBangun tidak akan hilang meskipun ESP32 berulang kali masuk dan keluar Deep Sleep. Maksimal 8 KB data bisa disimpan di RTC Memory.
4. Deep Sleep dengan GPIO Wakeup (Touch/Button)
Selain timer, ESP32 juga bisa dibangunkan dari Deep Sleep menggunakan perubahan sinyal pada pin GPIO tertentu. Ini sangat berguna untuk aplikasi interaktif β misalnya menekan tombol untuk mengaktifkan display, atau touch sensor untuk menyalakan lampu.
4.1 GPIO Wakeup dengan Tombol Push-Button
ESP32 Tombol
βββββββββββ ββββββββββ
β β β β
β 3.3V βββββββ¬ββββββ€ Pin 1 β
β β β β β
β GPIO 33 βββββββ€ βββββ¬βββββ
β (EXT0) β [10kΞ©] β
β β β βββββ
β GND βββββββ΄ββββββββββ
β β
βββββββββββ
Catatan: Tombol menghubungkan GPIO ke GND saat ditekan.
Resistor pull-up 10kΞ© memastikan GPIO HIGH saat tombol tidak ditekan.
GPIO 33 mendukung wake-up dari Deep Sleep (EXT0).
// ESP32 Deep Sleep dengan GPIO Wakeup (Tombol)
// IoTHub - https://iothub.id
#include <Arduino.h>
#define BUTTON_PIN 33 // Pin tombol untuk wake up
#define LED_PIN 2 // LED indikator
#define TIMEOUT_WAKE 300 // Auto wake setelah 5 menit
RTC_DATA_ATTR int wakeCount = 0;
RTC_DATA_ATTR int buttonWakes = 0;
void setup() {
Serial.begin(115200);
Serial.flush();
wakeCount++;
// Tentukan alasan bangun
esp_sleep_wakeup_cause_t penyebab = esp_sleep_get_wakeup_cause();
switch (penyebab) {
case ESP_SLEEP_WAKEUP_EXT0:
Serial.println("Bangun karena: Tombol ditekan (EXT0)!");
buttonWakes++;
break;
case ESP_SLEEP_WAKEUP_TIMER:
Serial.println("Bangun karena: Timer timeout");
break;
default:
Serial.println("Bangun karena: Boot pertama kali / reset");
break;
}
Serial.print("Total wake-up: ");
Serial.println(wakeCount);
Serial.print("Bangun dari tombol: ");
Serial.println(buttonWakes);
// --- Lakukan pekerjaan ---
pinMode(LED_PIN, OUTPUT);
digitalWrite(LED_PIN, HIGH);
delay(2000); // Nyalakan LED selama 2 detik
digitalWrite(LED_PIN, LOW);
// Konfigurasi 2 wake-up source:
// 1. GPIO 33 (tombol) β EXT0
// 2. Timer sebagai cadangan (timeout)
esp_sleep_enable_ext0_wakeup((gpio_num_t)BUTTON_PIN, 0);
esp_sleep_enable_timer_wakeup(TIMEOUT_WAKE * 1000000ULL);
Serial.println("Masuk Deep Sleep... (tekan tombol untuk bangun)");
Serial.flush();
esp_deep_sleep_start();
}
void loop() {
// Tidak digunakan
}
4.2 GPIO Wakeup dengan Touch Pad
ESP32 memiliki 10 pin touch yang bisa mendeteksi sentuhan manusia tanpa komponen mekanis apapun. Pin touch dapat membangunkan ESP32 dari Deep Sleep saat jari menyentuh elektroda tembaga yang terhubung ke pin touch.
// ESP32 Deep Sleep dengan Touch Wakeup
// IoTHub - https://iothub.id
#include <Arduino.h>
// Pin touch yang mendukung wake-up dari Deep Sleep
#define TOUCH_PIN T0 // GPIO 4 β Touch channel 0
// Threshold sensitivitas touch
#define TOUCH_THRESHOLD 40
RTC_DATA_ATTR int wakeCount = 0;
void setup() {
Serial.begin(115200);
Serial.flush();
wakeCount++;
esp_sleep_wakeup_cause_t penyebab = esp_sleep_get_wakeup_cause();
if (penyebab == ESP_SLEEP_WAKEUP_TOUCHPAD) {
Serial.println("Bangun karena: Sentuhan pada pad!");
// Tampilkan channel touch yang memicu wake
touch_pad_t touchPad = esp_sleep_get_touchpad_wakeup_status();
Serial.print("Channel: T");
Serial.println((int)touchPad);
} else {
Serial.println("Bangun karena: Boot pertama kali");
}
Serial.print("Total wake: ");
Serial.println(wakeCount);
// Tampilkan nilai pembacaan touch
uint32_t nilaiTouch;
touch_pad_read(TOUCH_PIN, &nilaiTouch);
Serial.print("Nilai touch saat ini: ");
Serial.println(nilaiTouch);
// Aktifkan touch wakeup
touch_pad_init();
touch_pad_set_meas_time(TOUCH_PAD_MEAS_TIME_DEFAULT,
TOUCH_PAD_MEAS_TIME_DEFAULT);
touch_pad_config(TOUCH_PIN, TOUCH_THRESHOLD);
esp_sleep_enable_touchpad_wakeup();
Serial.println("Masuk Deep Sleep... (sentuh pad untuk bangun)");
Serial.flush();
esp_deep_sleep_start();
}
void loop() {
// Tidak digunakan
}
Untuk elektroda sentuh, cukup hubungkan sepotong kawat tembaga atau foil ke pin GPIO yang diinginkan. Semakin luas area elektroda, semakin sensitif deteksinya. Hindari elektroda yang terlalu besar karena bisa meningkatkan false trigger.
5. Deep Sleep dengan ULP Coprocessor
ULP (Ultra Low Power) Coprocessor adalah prosesor kecil 32-bit yang tetap aktif bahkan saat ESP32 dalam Deep Sleep. ULP bisa menjalankan program sederhana, membaca sensor analog (ADC), dan membangunkan prosesor utama saat kondisi tertentu terpenuhi.
Keunggulan utama ULP: menghemat daya secara signifikan karena prosesor utama tetap tidur, sementara ULP hanya mengonsumsi sekitar 5 Β΅A tambahan saat menjalankan program.
Kapan Menggunakan ULP?
- Memantau sensor analog secara periodik tanpa membangunkan CPU utama
- Menunggu nilai sensor melewati threshold tertentu baru membangunkan ESP32
- Membaca sensor I2C sederhana dari peripheral yang tetap aktif
- Menghitung event sederhana dan membangunkan ESP32 hanya setelah N event
// ESP32 Deep Sleep dengan ULP Coprocessor
// Membaca sensor analog dari ADC dan membangunkan
// ESP32 saat nilai melampaui threshold
// IoTHub - https://iothub.id
#include <Arduino.h>
#include "soc/rtc_io.h"
#include "esp_sleep.h"
// Pin sensor analog
#define SENSOR_PIN 36 // GPIO 36 (VP) β ADC1_CH0
#define THRESHOLD 2000 // Threshold ADC (0-4095)
// Deklarasi fungsi assembly ULP
extern const uint8_t ulp_machine_start[] asm("_binary_ulp_program_bin_start");
extern const uint8_t ulp_machine_end[] asm("_binary_ulp_program_bin_end");
RTC_DATA_ATTR int wakeCount = 0;
void setup() {
Serial.begin(115200);
Serial.flush();
wakeCount++;
// Cek alasan wake-up
esp_sleep_wakeup_cause_t penyebab = esp_sleep_get_wakeup_cause();
if (penyebab == ESP_SLEEP_WAKEUP_ULP) {
Serial.println("Bangun karena: ULP Coprocessor!");
// Baca nilai sensor terakhir dari RTC memory
uint32_t nilaiSensor;
rtc_gpio_isolate(GPIO_NUM_36); // Isolasi pin setelah bangun
Serial.println("Kondisi threshold tercapai!");
} else {
Serial.println("Bangun karena: Boot / Reset");
}
Serial.print("Wake-up ke-: ");
Serial.println(wakeCount);
// --- Lakukan pekerjaan utama ---
int bacaSensor = analogRead(SENSOR_PIN);
Serial.print("Nilai sensor: ");
Serial.println(bacaSensor);
// Konfigurasi ULP
initUlpSensor();
// Masuk Deep Sleep
Serial.println("Masuk Deep Sleep (ULP aktif)...");
Serial.flush();
esp_deep_sleep_start();
}
void initUlpSensor() {
// Load program ULP dari binary yang dikompilasi
size_t ukuran = ulp_machine_end - ulp_machine_start;
esp_err_t err = ulp_load_binary(0, ulp_machine_start, ukuran);
if (err != ESP_OK) {
Serial.print("Error loading ULP program: ");
Serial.println(esp_err_to_name(err));
return;
}
// Konfigurasi ULP untuk membaca ADC
ulp_set_wakeup_period(0, 5000000); // Bangun setiap 5 detik
// Set threshold di RTC memory
// Posisi RTC memory yang digunakan oleh ULP
// untuk menyimpan hasil pembacaan
// Mulai ULP
err = ulp_run(&ulp_machine_start - (uint32_t *)rtc_get_address());
if (err != ESP_OK) {
Serial.print("Error starting ULP: ");
Serial.println(esp_err_to_name(err));
}
}
void loop() {
// Tidak digunakan
}
Program ULP ditulis dalam bahasa assembly khusus ULP dan perlu dikompilasi terpisah menggunakan toolchain ulp_as dari ESP-IDF. Dalam contoh di atas, binary ULP dimuat dari file ulp_program.bin yang sudah dikompilasi sebelumnya. Proses ini lebih kompleks dan memerlukan pemahaman ESP-IDF di luar Arduino IDE.
6. Mengukur Konsumsi Daya (Multimeter & INA219)
Tidak ada gunanya menulis kode hemat daya jika Anda tidak bisa membuktikan hasilnya. Mengukur konsumsi daya aktual adalah langkah penting untuk memvalidasi optimasi yang dilakukan.
6.1 Menggunakan Multimeter Digital
Multimeter (mode Amperemeter)
ββββββββββββββββ
β 200mA / β
β 20mA / β
Baterai (+) βββΆβ 2mA range ββββΆ ESP32 VIN (+)
3.7V LiPo β β
β β οΈ Series! β
ββββββββββββββββ
Baterai (-) βββββββββββββββββββββΆ ESP32 GND (-)
β οΈ PENTING: Multimeter dipasang SERIAL (putuskan kabel VCC
dan hubungkan melalui multimeter). JANGAN paralel!
Langkah-langkah pengukuran:
- Putuskan kabel positif (+) dari baterai ke ESP32
- Hubungkan probe merah multimeter ke kabel dari baterai (+)
- Hubungkan probe hitam multimeter ke pin VIN ESP32
- Atur multimeter ke range yang sesuai (mulai dari 200mA, turunkan jika perlu)
- Pastikan kabel probe terpasang ke terminal COM dan mA (bukan 10A!)
6.2 Menggunakan Modul INA219
Modul INA219 adalah sensor arus/t tegangan I2C yang populer dan murah (sekitar Rp 15-25rb). Modul ini bisa mengukur tegangan bus (0-26V), arus (Β±3.2A), dan menghitung daya secara real-time β cocok untuk monitoring konsumsi daya proyek IoT.
Baterai INA219 Modul ESP32
3.7V LiPo βββββββββββββββ βββββββββββ
β VIN+ ββββββββΌββββββββββ€ VIN β
(+) ββββββββββ€ VCC ββββββββΌβββββ¬βββββ€ β
β GND ββββββββΌβββββΌβββββ€ GND β
(-) ββββββββββ€ SDA ββββββββΌβββββ€ β β
β SCL ββββββββΌβββββ β β
β IN- β β GPIO 21 β (SDA)
β IN+ β β GPIO 22 β (SCL)
β SD0/ADR β β β
βββββββββββββββ βββββββββββ
INA219 dihubungkan secara SERIAL pada jalur power
untuk mengukur arus yang mengalir ke ESP32
// Membaca konsumsi daya ESP32 menggunakan INA219
// IoTHub - https://iothub.id
#include <Wire.h>
#include <Adafruit_INA219.h>
Adafruit_INA219 ina219;
void setup() {
Serial.begin(115200);
Serial.println("=== INA219 Power Monitor ===");
if (!ina219.begin()) {
Serial.println("ERROR: INA219 tidak terdeteksi!");
while (1) { delay(1000); }
}
Serial.println("INA219 siap!");
Serial.println();
}
void loop() {
// Baca tegangan bus (tegangan baterai)
float teganganBus = ina219.getBusVoltage_V();
// Baca tegangan shunt (untuk hitung arus)
float teganganShunt = ina219.getShuntVoltage_mV();
// Baca arus dalam mA
float arus = ina219.getCurrent_mA();
// Hitung daya dalam mW
float daya = ina219.getPower_mW();
// Tampilkan hasil
Serial.println("--- Pembacaan Daya ---");
Serial.print("Tegangan Bus : ");
Serial.print(teganganBus, 3);
Serial.println(" V");
Serial.print("Tegangan Shunt: ");
Serial.print(teganganShunt, 3);
Serial.println(" mV");
Serial.print("Arus : ");
Serial.print(arus, 1);
Serial.println(" mA");
Serial.print("Daya : ");
Serial.print(daya, 1);
Serial.println(" mW");
Serial.println();
delay(2000);
}
Untuk pengukuran Deep Sleep yang akurat, pastikan modul USB-to-serial (CH340/CP2102) dilepas atau dinonaktifkan karena modul tersebut bisa menarik arus 20-30 mA. Gunakan baterai langsung untuk pengukuran yang representatif terhadap kondisi operasi nyata.
7. Mengirim Data Sebelum Tidur (WiFi Flush & MQTT Disconnect)
Salah satu kesalahan paling umum dalam proyek Deep Sleep adalah masuk sleep tanpa memastikan semua data sudah terkirim. Jika ESP32 masuk Deep Sleep saat WiFi atau MQTT masih dalam proses pengiriman, data bisa hilang atau koneksi broker menjadi ghost connection.
Urutan Shutdown yang Benar
ββββββββββββββββββββββββββββββββββββββββββββββββ β 1. Pastikan semua data terkirim β β (MQTT publish, HTTP response, dll) β β β β β βΌ β β 2. Tunggu WiFi TX buffer kosong β β (WiFi.flush() atau delay 500ms) β β β β β βΌ β β 3. Putuskan koneksi MQTT / HTTP β β (client.disconnect()) β β β β β βΌ β β 4. Matikan WiFi β β (WiFi.disconnect() + WiFi.mode(WIFI_OFF))β β β β β βΌ β β 5. Putuskan WiFi radio β β (esp_wifi_disconnect()) β β β β β βΌ β β 6. Flush serial buffer β β (Serial.flush()) β β β β β βΌ β β 7. Masuk Deep Sleep β β (esp_deep_sleep_start()) β ββββββββββββββββββββββββββββββββββββββββββββββββ
// Fungsi shutdown yang benar untuk WiFi & MQTT
// IoTHub - https://iothub.id
#include <WiFi.h>
#include <PubSubClient.h>
// Konfigurasi WiFi
const char* ssid = "WiFi_Indonesia";
const char* password = "password123";
// Konfigurasi MQTT
const char* mqttServer = "broker.hivemq.com";
const int mqttPort = 1883;
const char* mqttTopic = "iothub/sensor/suhu";
WiFiClient wifiClient;
PubSubClient mqttClient(wifiClient);
// --- Fungsi Terhubung WiFi ---
void sambungWifi() {
Serial.print("Menghubungkan WiFi");
WiFi.begin(ssid, password);
int percobaan = 0;
while (WiFi.status() != WL_CONNECTED && percobaan < 20) {
delay(500);
Serial.print(".");
percobaan++;
}
if (WiFi.status() == WL_CONNECTED) {
Serial.println("\nWiFi terhubung!");
Serial.print("IP: ");
Serial.println(WiFi.localIP());
} else {
Serial.println("\nGagal terhubung WiFi!");
}
}
// --- Fungsi Terhubung MQTT ---
void sambungMQTT() {
mqttClient.setServer(mqttServer, mqttPort);
int percobaan = 0;
while (!mqttClient.connected() && percobaan < 3) {
Serial.print("Menghubungkan MQTT...");
if (mqttClient.connect("ESP32DeepSleep")) {
Serial.println("OK!");
} else {
Serial.print("Gagal, rc=");
Serial.println(mqttClient.state());
delay(1000);
percobaan++;
}
}
}
// --- Fungsi Kirim Data ---
bool kirimData(float suhu, float kelembaban) {
if (!mqttClient.connected()) return false;
// Format data sebagai JSON
char payload[128];
snprintf(payload, sizeof(payload),
"{\"suhu\":%.1f,\"kelembaban\":%.1f,\"uptime\":%lu}",
suhu, kelembaban, millis());
// Kirim data
bool berhasil = mqttClient.publish(mqttTopic, payload);
if (berhasil) {
Serial.print("Data terkirim: ");
Serial.println(payload);
} else {
Serial.println("Gagal mengirim data!");
}
return berhasil;
}
// --- Fungsi Shutdown Aman ---
void shutdownAman() {
Serial.println("=== Memulai Shutdown ===");
// Langkah 1: Pastikan semua paket MQTT terkirim
Serial.println("[1/6] Flush MQTT...");
mqttClient.loop(); // Proses callback & pending
delay(500); // Tunggu TX buffer kosong
// Langkah 2: Putuskan MQTT
Serial.println("[2/6] Disconnect MQTT...");
if (mqttClient.connected()) {
mqttClient.disconnect();
delay(100);
}
// Langkah 3: Disconnect WiFi
Serial.println("[3/6] Disconnect WiFi...");
WiFi.disconnect(true);
delay(100);
// Langkah 4: Matikan modul WiFi
Serial.println("[4/6] WiFi OFF...");
WiFi.mode(WIFI_OFF);
delay(100);
// Langkah 5: Putuskan radio WiFi
Serial.println("[5/6] WiFi radio OFF...");
esp_wifi_disconnect();
delay(100);
// Langkah 6: Flush serial
Serial.println("[6/6] Serial flush...");
Serial.flush();
Serial.println("=== Shutdown Selesai ===");
delay(100);
}
Selalu panggil shutdownAman() sebelum esp_deep_sleep_start(). Tanpa fungsi ini, data yang dikirim via MQTT bisa hilang, atau WiFi connection bisa menjadi "ghost" yang memboroskan daya karena broker tetap mencoba mengirim data ke perangkat yang sudah tidur.
8. Proyek: Sensor Suhu Battery-Powered 1 Tahun
Sekarang kita akan menggabungkan semua konsep yang sudah dipelajari menjadi proyek nyata: sensor suhu yang bisa bertahan hampir setahun menggunakan satu baterai LiPo 18650 (3400mAh).
Spesifikasi Proyek
| Parameter | Nilai |
|---|---|
| Sensor | DHT22 / DS18B20 (akurat & hemat daya) |
| Interval pembacaan | 15 menit |
| Protokol kirim data | MQTT ke broker HiveMQ / Mosquitto |
| Baterai | LiPo 18650 β 3.7V 3400mAh |
| Target umur baterai | Minimal 10 bulan |
| Metode hemat daya | Deep Sleep + Timer Wakeup |
Kalkulasi Daya
βββββββββββββββββββββββββββββββββββββββββββββββ β KALKULASI UMUR BATERAI β βββββββββββββββββββββββββββββββββββββββββββββββ€ β β β Siklus satu pembacaan: β β βββββββββββββββββββββ β β Boot + WiFi Connect : 3 detik @ 150mA β β MQTT Publish : 1 detik @ 180mA β β Total waktu aktif : ~5 detik β β Energi aktif : 5s Γ 150mA β β = 750 mAΒ·detik β β β β Siklus Deep Sleep: β β βββββββββββββββββ β β Waktu tidur : 900 detik (15m) β β Arus tidur : 0.005 mA β β Energi tidur : 900 Γ 0.005 β β = 4.5 mAΒ·detik β β β β Total per siklus : 754.5 mAΒ·detik β β β β Siklus per hari : 96 (15 menit) β β Konsumsi per hari : 754.5 Γ 96 β β = 72,432 mAΒ·detik β β = 20.12 mAΒ·jam β β = 20.12 mAh/hari β β β β Umur baterai : 3400 mAh β β Γ· 20.12 mAh/hari β β β 169 hari β β β 5.6 bulan β β β β Dengan self-discharge : ~5 bulan β β (baterai turun 20%/thn) β β β β β Gunakan baterai lebih besar atau β β interval lebih lama untuk 1 tahun! β βββββββββββββββββββββββββββββββββββββββββββββββ
Untuk mencapai target 1 tahun, kita bisa menggunakan baterai ganda (2Γ 18650 paralel = 6800mAh) atau interval 30 menit. Dengan 6800mAh dan interval 15 menit, baterai bertahan sekitar 338 hari β cukup untuk mencapai target 1 tahun.
Kode Program Lengkap
// Sensor Suhu Battery-Powered 1 Tahun
// ESP32 Deep Sleep + MQTT + DHT22
// IoTHub - https://iothub.id
#include <WiFi.h>
#include <PubSubClient.h>
#include <DHT.h>
// ===== KONFIGURASI =====
#define SLEEP_DURATION 900LL // 15 menit (dalam detik)
#define DHT_PIN 4 // GPIO4
#define DHT_TYPE DHT22 // Gunakan DHT22 untuk akurasi
#define LED_INDICATOR 2 // GPIO2 (LED internal)
#define BAT_ADC_PIN 34 // GPIO34 untuk baca tegangan baterai
#define BAT_VOLT_DIV 2.0 // Rasio voltage divider
// WiFi credentials
const char* WIFI_SSID = "WiFi_Indonesia";
const char* WIFI_PASS = "password123";
// MQTT credentials
const char* MQTT_SERVER = "broker.hivemq.com";
const int MQTT_PORT = 1883;
const char* MQTT_USER = "";
const char* MQTT_PASS_MQTT = "";
const char* MQTT_TOPIC = "iothub/suhu/esp32-01";
const char* MQTT_CLIENT = "ESP32-Suhu-01";
// ===== OBJEK GLOBAL =====
DHT dht(DHT_PIN, DHT_TYPE);
WiFiClient wifiClient;
PubSubClient mqtt(wifiClient);
// ===== RTC MEMORY =====
RTC_DATA_ATTR int wakeCount = 0;
RTC_DATA_ATTR int errConnect = 0;
RTC_DATA_ATTR float lastSuhu = 0.0;
RTC_DATA_ATTR float lastHumidity = 0.0;
// ===== FUNGSI SETUP =====
void setup() {
Serial.begin(115200);
Serial.flush();
wakeCount++;
// LED indikator
pinMode(LED_INDICATOR, OUTPUT);
digitalWrite(LED_INDICATOR, HIGH);
Serial.println();
Serial.println("ββββββββββββββββββββββββββββββββββββββββ");
Serial.println("β Sensor Suhu Battery-Powered 1 Tahun β");
Serial.println("β ESP32 Deep Sleep + MQTT β");
Serial.println("ββββββββββββββββββββββββββββββββββββββββ");
Serial.print("Wake-up ke-: ");
Serial.println(wakeCount);
// Baca tegangan baterai
float teganganBaterai = bacaTeganganBaterai();
Serial.print("Tegangan baterai: ");
Serial.print(teganganBaterai, 2);
Serial.println(" V");
// Cek apakah baterai masih cukup (minimum 3.3V)
if (teganganBaterai < 3.3) {
Serial.println("PERINGATAN: Baterai lemah! Interval diperpanjang.");
// Jika baterai lemah, sleep lebih lama (30 menit)
sleepDanBangun(SLEEP_DURATION * 2);
}
// Baca sensor suhu
dht.begin();
delay(2000); // Tunggu sensor stabil
float suhu = dht.readTemperature();
float kelembaban = dht.readHumidity();
if (isnan(suhu) || isnan(kelembaban)) {
Serial.println("ERROR: Gagal baca sensor DHT22!");
// Gunakan data terakhir dari RTC memory
suhu = lastSuhu;
kelembaban = lastHumidity;
Serial.println("Menggunakan data terakhir dari RTC memory.");
} else {
Serial.print("Suhu : ");
Serial.print(suhu, 1);
Serial.println(" Β°C");
Serial.print("Kelembaban: ");
Serial.print(kelembaban, 1);
Serial.println(" %");
lastSuhu = suhu;
lastHumidity = kelembaban;
}
// Kirim data via MQTT
if (suhu != 0.0 || kelembaban != 0.0) {
bool terkirim = kirimDataMQTT(suhu, kelembaban, teganganBaterai);
if (terkirim) {
Serial.println("Data berhasil dikirim!");
} else {
Serial.println("Gagal mengirim data.");
errConnect++;
}
}
// Shutdown dan masuk Deep Sleep
shutdownAman();
Serial.print("Tidur selama ");
Serial.print(SLEEP_DURATION);
Serial.println(" detik...");
sleepDanBangun(SLEEP_DURATION);
}
// ===== FUNGSI LOOP =====
void loop() {
// Tidak digunakan β ESP32 selalu bangun dari setup()
}
// ===== FUNGSI-FUNGSI PEMBANTU =====
float bacaTeganganBaterai() {
// Baca ADC dan konversi ke tegangan
int adcValue = analogRead(BAT_ADC_PIN);
float tegangan = (adcValue / 4095.0) * 3.3 * BAT_VOLT_DIV;
return tegangan;
}
bool kirimDataMQTT(float suhu, float kelembaban, float tegangan) {
// Sambung WiFi
Serial.print("Menghubungkan WiFi...");
WiFi.begin(WIFI_SSID, WIFI_PASS);
int coba = 0;
while (WiFi.status() != WL_CONNECTED && coba < 20) {
delay(500);
Serial.print(".");
coba++;
}
if (WiFi.status() != WL_CONNECTED) {
Serial.println("\nWiFi gagal!");
return false;
}
Serial.println(" OK!");
// Sambung MQTT
mqtt.setServer(MQTT_SERVER, MQTT_PORT);
coba = 0;
while (!mqtt.connected() && coba < 3) {
Serial.print("MQTT...");
if (mqtt.connect(MQTT_CLIENT, MQTT_USER, MQTT_PASS_MQTT)) {
Serial.println("OK!");
} else {
Serial.print("Gagal (");
Serial.print(mqtt.state());
Serial.println(")");
delay(1000);
coba++;
}
}
if (!mqtt.connected()) {
Serial.println("MQTT tidak terhubung!");
return false;
}
// Siapkan payload JSON
char payload[256];
snprintf(payload, sizeof(payload),
"{"
"\"device\":\"ESP32-01\","
"\"suhu\":%.1f,"
"\"kelembaban\":%.1f,"
"\"baterai\":%.2f,"
"\"wake\":%d,"
"\"uptime\":%lu"
"}",
suhu, kelembaban, tegangan, wakeCount, millis()
);
// Publish
bool hasil = mqtt.publish(MQTT_TOPIC, payload);
mqtt.loop();
delay(200);
return hasil;
}
void shutdownAman() {
Serial.println("Shutdown...");
mqtt.loop();
delay(500);
if (mqtt.connected()) mqtt.disconnect();
delay(100);
WiFi.disconnect(true);
delay(100);
WiFi.mode(WIFI_OFF);
delay(100);
Serial.flush();
digitalWrite(LED_INDICATOR, LOW);
delay(100);
}
void sleepDanBangun(long long detikTidur) {
esp_sleep_enable_timer_wakeup(detikTidur * 1000000ULL);
esp_deep_sleep_start();
}
Rangkaian Fisik
ββββββββββββββββββββββββββββββββββββββββββββββββββββ β β β LiPo 18650 ESP32 DevKit DHT22 β β 3.7V 3400mAh ββββββββββββββββ ββββββββββ β β β β β VCC β β β (+)ββββ¬ββββββββββββ€ VIN β β βββββββ€ β β β β β β [10kΞ©]β β β βββ[10kΞ©]ββββ€ GPIO34 β β β β β β β β ββββ€ DATA β β β (-)ββββΌββββββββββββ€ GPIO4 ββββββββ β β β β β β β ββββ€ β β β β β β GND β β GND β β β β ββββββββββββββββ ββββββββββ β β β β β β Voltage Divider β β βββ[10kΞ©]βββ¬ββ[10kΞ©]ββ GND β β β β β βββββ GPIO34 (Baca V_batt) β β β ββββββββββββββββββββββββββββββββββββββββββββββββββββ
- Baterai lebih besar = umur lebih lama. Gunakan 2Γ 18650 paralel untuk 6800mAh.
- Interval lebih lama = menghemat daya signifikan. 30 menit masih cukup untuk monitoring suhu ruangan.
- Gunakan DS18B20 sebagai alternatif DHT22 β lebih hemat daya saat pembacaan.
- Matikan semua LED pada modul selain LED indikator sesaat.
- Hapus voltage regulator dan jalankan ESP32 langsung dari 3.7V LiPo (lihat datasheet untuk batas tegangan).
9. Quiz: Uji Pemahamanmu!
Setelah membaca tutorial di atas, jawablah 5 pertanyaan berikut untuk menguji pemahamanmu tentang ESP32 Deep Sleep: