1. Apa Itu OTA Update?
OTA (Over-The-Air) adalah metode untuk mengupdate firmware atau software pada perangkat embedded secara wireless, tanpa perlu menghubungkan kabel USB atau mengakses fisik perangkat. Teknologi ini sangat penting dalam ekosistem IoT di mana perangkat sering dipasang di lokasi yang sulit dijangkau — seperti atap gedung, gudang, area pertanian, atau bahkan di dalam dinding.
Bayangkan Anda memiliki 50 unit ESP32 yang tersebar di berbagai lokasi untuk memantau suhu gudang. Tanpa OTA, setiap kali ada bug atau fitur baru, Anda harus mendatangi setiap perangkat satu per satu dengan laptop dan kabel USB. Dengan OTA, semua bisa diupdate dari satu lokasi pusat melalui jaringan WiFi.
OTA bukan sekadar kenyamanan — untuk banyak proyek IoT produksi, OTA adalah kebutuhan wajib. Tanpa OTA, biaya maintenance perangkat yang tersebar bisa sangat mahal dan tidak praktis.
Jenis-Jenis OTA pada ESP32
ESP32 mendukung beberapa metode OTA yang bisa dipilih sesuai kebutuhan:
| Metode OTA | Cara Kerja | Kompleksitas | Cocok Untuk |
|---|---|---|---|
| ArduinoOTA | Update via jaringan lokal menggunakan Arduino IDE | Mudah | Development & testing |
| Web-Based OTA | Upload firmware via browser ke web server di ESP32 | Sedang | Prototyping & demo |
| HTTP OTA | ESP32 mengunduh firmware dari server HTTP/HTTPS | Mahir | Produksi & fleet update |
Skema Partisi OTA ESP32
Untuk mendukung OTA, ESP32 menggunakan skema partisi khusus yang membagi flash memory menjadi beberapa bagian. Paling tidak, diperlukan dua partisi aplikasi — satu yang sedang berjalan (aktif) dan satu lagi untuk menampung firmware baru yang diupload.
┌──────────────────────────────────────────────────┐ │ Flash 4 MB │ ├──────────────┬────────────────────────────────────┤ │ nvs (20KB) │ Penyimpanan data konfigurasi │ ├──────────────┼────────────────────────────────────┤ │ otadata (8KB)│ Menandai partisi mana yang aktif │ ├──────────────┼────────────────────────────────────┤ │ app0 (1.5MB) │ ← Partisi firmware A (aktif) │ ├──────────────┼────────────────────────────────────┤ │ app1 (1.5MB) │ ← Partisi firmware B (OTA target) │ ├──────────────┼────────────────────────────────────┤ │ spiffs (1MB) │ Filesystem untuk data tambahan │ └──────────────┴────────────────────────────────────┘ Saat OTA: firmware baru ditulis ke partisi yang tidak aktif, lalu bootloader mengarahkan boot ke partisi baru setelah reset.
Pastikan ukuran firmware Anda tidak melebihi kapasitas partisi. Dengan skema default "Default 4MB with spiffs", masing-masing partisi app0 dan app1 berukuran sekitar 1.5 MB. Jika firmware Anda terlalu besar, Anda perlu membuat custom partition scheme.
2. Setup ArduinoOTA
ArduinoOTA adalah metode OTA paling sederhana yang tersedia di ekosistem Arduino. Dengan ArduinoOTA, Anda bisa mengupload firmware baru langsung dari Arduino IDE tanpa perlu kabel USB, asalkan ESP32 dan komputer Anda berada di jaringan WiFi yang sama.
Langkah 1: Instal Library ArduinoOTA
Library ArduinoOTA sudah termasuk dalam package ESP32 untuk Arduino IDE, jadi jika Anda sudah menginstal board ESP32 (seperti di tutorial ESP32 Fundamentals), Anda tidak perlu menginstal library tambahan.
Langkah 2: Program ESP32 dengan ArduinoOTA
Berikut adalah kode dasar yang mengaktifkan ArduinoOTA pada ESP32:
// ============================================
// OTA Basic - ESP32 ArduinoOTA
// BeebaneLabs - https://beebanelabs.pages.dev
// ============================================
#include <WiFi.h>
#include <ArduinoOTA.h>
// Konfigurasi WiFi
const char* ssid = "WIFI_KAMU";
const char* password = "PASSWORD_WIFI";
void setup() {
Serial.begin(115200);
Serial.println("Memulai ESP32 dengan ArduinoOTA...");
// Koneksi ke WiFi
WiFi.mode(WIFI_STA);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("");
Serial.print("Terhubung ke WiFi! IP: ");
Serial.println(WiFi.localIP());
// Inisialisasi ArduinoOTA
ArduinoOTA.setHostname("esp32-ota-demo");
ArduinoOTA.onStart([]() {
String type;
if (ArduinoOTA.getCommand() == U_FLASH) {
type = "firmware";
} else {
type = "filesystem";
}
Serial.println("Mulai update: " + type);
});
ArduinoOTA.onEnd([]() {
Serial.println("\nUpdate selesai!");
});
ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) {
Serial.printf("Progress: %u%%\r", (progress / (total / 100)));
});
ArduinoOTA.onError([](ota_error_t error) {
Serial.printf("Error[%u]: ", error);
if (error == OTA_AUTH_ERROR) Serial.println("Auth Failed");
else if (error == OTA_BEGIN_ERROR) Serial.println("Begin Failed");
else if (error == OTA_CONNECT_ERROR) Serial.println("Connect Failed");
else if (error == OTA_RECEIVE_ERROR) Serial.println("Receive Failed");
else if (error == OTA_END_ERROR) Serial.println("End Failed");
});
ArduinoOTA.begin();
Serial.println("OTA siap! Upload dari Arduino IDE via Network Port.");
}
void loop() {
// Handle OTA request — WAJIB dipanggil di loop()
ArduinoOTA.handle();
// Program utama Anda di sini
// ...
}
Langkah 3: Upload Firmware via OTA
- Pertama-tama, upload kode di atas ke ESP32 via kabel USB (ini upload terakhir via kabel!)
- Buka Tools → Port, Anda akan melihat port baru bertuliskan "esp32-ota-demo at 192.168.x.x (ESP32)"
- Pilih port network tersebut
- Buat perubahan pada kode Anda (misalnya ubah pesan serial)
- Klik → Upload (Ctrl+U) seperti biasa
- Arduino IDE akan mengupload firmware melalui WiFi tanpa kabel!
Jika port network ESP32 tidak muncul di Arduino IDE, pastikan komputer dan ESP32 berada di jaringan yang sama dan firewall tidak memblokir port 3232 (port default ArduinoOTA). Coba matikan firewall sementara untuk testing.
Menambahkan Password Keamanan
Secara default, ArduinoOTA tidak memerlukan password — siapa saja di jaringan yang sama bisa mengupload firmware ke ESP32 Anda. Untuk keamanan, tambahkan password:
// Atur password untuk OTA
ArduinoOTA.setPassword("passwordOTA123");
// Atau gunakan hash MD5 (lebih aman):
// ArduinoOTA.setPasswordHash("d8578edf8458ce06fbc5bb76a58c5ca4");
Saat password OTA diaktifkan, Arduino IDE akan meminta password saat pertama kali upload via network. Masukkan password yang sama dengan yang Anda atur di kode. Password ini dikirim dalam bentuk MD5 hash, bukan plain text, tetapi bukan enkripsi end-to-end. Jangan gunakan password sensitif.
3. Web-Based OTA Update
Web-Based OTA memungkinkan Anda mengupdate firmware ESP32 melalui web browser. ESP32 menjalankan web server kecil yang menyediakan halaman HTML tempat Anda bisa memilih dan mengupload file firmware (.bin). Metode ini sangat fleksibel karena tidak memerlukan Arduino IDE — cukup buka browser dari perangkat manapun.
Mengapa Web-Based OTA?
- Tidak perlu Arduino IDE — cukup browser (Chrome, Firefox, dll)
- Visual dan intuitif — ada progress bar dan feedback yang jelas
- Bisa dari smartphone — praktis untuk debugging di lapangan
- Fleksibel — bisa diakses dari perangkat manapun di jaringan yang sama
Program Web-Based OTA
// ============================================
// Web-Based OTA Update - ESP32
// BeebaneLabs - https://beebanelabs.pages.dev
// ============================================
#include <WiFi.h>
#include <WebServer.h>
#include <Update.h>
const char* ssid = "WIFI_KAMU";
const char* password = "PASSWORD_WIFI";
WebServer server(80);
// Halaman HTML untuk upload
const char* loginIndex = R"rawliteral(
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>ESP32 OTA Update</title>
<style>
body { font-family: Arial; background: #1a1a2e; color: #eee;
display: flex; justify-content: center; align-items: center;
min-height: 100vh; margin: 0; }
.card { background: #16213e; padding: 40px; border-radius: 12px;
box-shadow: 0 4px 20px rgba(0,0,0,0.3); max-width: 450px; width: 90%; }
h1 { text-align: center; color: #00d2ff; margin-bottom: 24px; }
input[type=file] { margin: 16px 0; width: 100%; }
button { background: #00d2ff; color: #000; border: none; padding: 12px 24px;
border-radius: 8px; width: 100%; font-size: 16px; cursor: pointer;
font-weight: bold; }
button:hover { background: #00b4d8; }
#progress { display: none; margin-top: 16px; }
.bar { background: #0f3460; border-radius: 8px; overflow: hidden; height: 24px; }
.bar-fill { background: #00d2ff; height: 100%; width: 0%;
transition: width 0.3s; text-align: center; line-height: 24px;
font-size: 12px; color: #000; font-weight: bold; }
#status { margin-top: 12px; text-align: center; }
</style>
</head>
<body>
<div class="card">
<h1>🔄 ESP32 OTA Update</h1>
<form id="uploadForm">
<input type="file" name="firmware" accept=".bin" id="fileInput">
<button type="submit">Upload Firmware</button>
</form>
<div id="progress">
<div class="bar"><div class="bar-fill" id="barFill">0%</div></div>
</div>
<div id="status"></div>
</div>
<script>
document.getElementById('uploadForm')
.addEventListener('submit', function(e) {
e.preventDefault();
var file = document.getElementById('fileInput').files[0];
if (!file) { alert('Pilih file firmware!'); return; }
var data = new FormData();
data.append('firmware', file);
var xhr = new XMLHttpRequest();
xhr.open('POST', '/update', true);
document.getElementById('progress').style.display = 'block';
xhr.upload.addEventListener('progress', function(e) {
if (e.lengthComputable) {
var pct = Math.round((e.loaded / e.total) * 100);
document.getElementById('barFill').style.width = pct + '%';
document.getElementById('barFill').innerText = pct + '%';
}
});
xhr.onload = function() {
if (xhr.status === 200) {
document.getElementById('status').innerHTML =
'<p style="color:#00d2ff">✅ Update berhasil! ESP32 akan restart...</p>';
} else {
document.getElementById('status').innerHTML =
'<p style="color:#ff6b6b">❌ Update gagal!</p>';
}
};
xhr.send(data);
});
</script>
</body>
</html>
)rawliteral";
void setup() {
Serial.begin(115200);
// Koneksi WiFi
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("\nWiFi connected! IP: " + WiFi.localIP().toString());
// Route: halaman utama
server.on("/", HTTP_GET, []() {
server.send(200, "text/html", loginIndex);
});
// Route: handle upload firmware
server.on("/update", HTTP_POST, []() {
server.sendHeader("Connection", "close");
server.send(200, "text/plain", (Update.hasError()) ? "FAIL" : "OK");
delay(1000);
ESP.restart();
}, []() {
HTTPUpload& upload = server.upload();
if (upload.status == UPLOAD_FILE_START) {
Serial.printf("Mulai upload: %s\n", upload.filename.c_str());
if (!Update.begin(UPDATE_SIZE_UNKNOWN)) {
Update.printError(Serial);
}
} else if (upload.status == UPLOAD_FILE_WRITE) {
if (Update.write(upload.buf, upload.currentSize)
!= upload.currentSize) {
Update.printError(Serial);
}
} else if (upload.status == UPLOAD_FILE_END) {
if (Update.end(true)) {
Serial.printf("Update selesai: %u bytes\n",
upload.totalSize);
} else {
Update.printError(Serial);
}
}
});
server.begin();
Serial.println("Web OTA server berjalan!");
Serial.println("Buka http://" + WiFi.localIP().toString()
+ " di browser");
}
void loop() {
server.handleClient();
}
1. Upload kode di atas via kabel USB untuk pertama kali.
2. Buka Serial Monitor, catat IP address ESP32.
3. Buka browser, masukkan IP tersebut (contoh: http://192.168.1.105).
4. Pilih file .bin firmware baru, klik Upload.
5. Tunggu progress bar hingga 100%, ESP32 akan restart otomatis.
6. File .bin bisa ditemukan di C:\Users\<user>\AppData\Local\Temp\arduino_build_<id>\ setelah Verify/Compile di Arduino IDE.
4. HTTP OTA Update
HTTP OTA adalah metode yang paling powerful untuk update firmware di lingkungan produksi. Dengan HTTP OTA, ESP32 secara aktif mengunduh firmware dari server web (cloud atau lokal) dan menginstalnya sendiri. Ini memungkinkan Anda mengupdate ratusan perangkat sekaligus hanya dengan mengupload firmware ke satu server.
Arsitektur HTTP OTA
┌──────────┐ HTTPS GET ┌──────────────┐
│ ESP32 │ ───────────────→ │ Web Server │
│ Device │ ←─────────────── │ (GitHub/ │
│ │ firmware.bin │ S3/custom) │
└────┬─────┘ └──────────────┘
│ ↑
│ 1. Cek versi terbaru │
│ dari server │
│ 2. Jika ada update, │
│ download firmware.bin │
│ 3. Tulis ke partisi OTA │
│ 4. Set flag & restart │
▼
┌──────────┐
│ Boot │ → bootloader memilih
│ Loader │ partisi baru
└──────────┘
Program HTTP OTA dengan Version Check
// ============================================
// HTTP OTA Update - ESP32
// BeebaneLabs - https://beebanelabs.pages.dev
// ============================================
#include <WiFi.h>
#include <HTTPClient.h>
#include <HTTPUpdate.h>
#include <WiFiClientSecure.h>
const char* ssid = "WIFI_KAMU";
const char* password = "PASSWORD_WIFI";
// URL firmware di server (bisa GitHub raw, S3, server sendiri)
const char* firmwareURL =
"http://yourserver.com/esp32/firmware.bin";
// URL file versi (untuk cek update terbaru)
const char* versionURL =
"http://yourserver.com/esp32/version.txt";
// Versi firmware saat ini — naikkan setiap kali ada update
#define FIRMWARE_VERSION "1.0.0"
void setup() {
Serial.begin(115200);
Serial.println("Firmware v" + String(FIRMWARE_VERSION));
// Koneksi WiFi
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("\nWiFi connected!");
// Cek dan jalankan update jika tersedia
checkForUpdate();
}
void loop() {
// Cek update setiap 60 detik
static unsigned long lastCheck = 0;
if (millis() - lastCheck > 60000) {
checkForUpdate();
lastCheck = millis();
}
// Program utama...
}
void checkForUpdate() {
if (WiFi.status() != WL_CONNECTED) return;
HTTPClient http;
http.begin(versionURL);
int httpCode = http.GET();
if (httpCode == 200) {
String newVersion = http.getString();
newVersion.trim();
Serial.println("Versi server: " + newVersion);
Serial.println("Versi lokal: " + String(FIRMWARE_VERSION));
if (newVersion != String(FIRMWARE_VERSION)) {
Serial.println("Update tersedia! Memulai download...");
performOTA(firmwareURL);
} else {
Serial.println("Firmware sudah versi terbaru.");
}
} else {
Serial.println("Gagal cek versi: " + String(httpCode));
}
http.end();
}
void performOTA(const char* url) {
WiFiClient client;
Serial.println("Downloading firmware dari: " + String(url));
t_httpUpdate_return ret = httpUpdate.update(client, url);
switch (ret) {
case HTTP_UPDATE_FAILED:
Serial.printf("Update GAGAL! Error(%d): %s\n",
httpUpdate.getLastError(),
httpUpdate.getLastErrorString().c_str());
break;
case HTTP_UPDATE_NO_UPDATES:
Serial.println("Tidak ada update.");
break;
case HTTP_UPDATE_OK:
Serial.println("Update berhasil! Restart...");
// ESP akan restart otomatis
break;
}
}
Menyimpan Firmware di GitHub
Salah satu cara termudah untuk mendistribusikan firmware adalah menggunakan GitHub Releases. Upload file .bin sebagai release asset, lalu gunakan URL raw dari file tersebut di kode ESP32:
// URL firmware dari GitHub Release const char* firmwareURL = "https://github.com/user/repo/releases/latest/" "download/firmware.bin"; // Untuk HTTPS, gunakan WiFiClientSecure: WiFiClientSecure client; client.setInsecure(); // Skip SSL verification (dev only!) httpUpdate.update(client, firmwareURL);
Untuk koneksi HTTPS yang aman, gunakan sertifikat CA server yang valid, bukan setInsecure(). setInsecure() hanya untuk development — di produksi, ini rentan terhadap man-in-the-middle attack. Simpan root CA certificate di SPIFFS dan muat saat inisialisasi.
5. Pertimbangan Keamanan OTA
OTA adalah pedang bermata dua — memudahkan update, tetapi juga membuka potensi keamanan. Jika seseorang bisa mengintersep proses OTA dan mengirim firmware berbahaya, mereka bisa mengambil alih seluruh jaringan perangkat IoT Anda. Berikut adalah langkah-langkah keamanan yang wajib diterapkan:
- ❌ Firmware injection oleh attacker
- ❌ Intercept & modifikasi firmware
- ❌ Upload malware ke semua device
- ❌ Brick perangkat dengan firmware corrupt
- ❌ Pencurian data konfigurasi WiFi
- ✅ Firmware terenkripsi (AES-256)
- ✅ Signature verification (RSA/ECDSA)
- ✅ HTTPS dengan certificate pinning
- ✅ Rollback otomatis jika boot gagal
- ✅ Version downgrade protection
Best Practices Keamanan OTA
| Langkah | Deskripsi | Tingkat |
|---|---|---|
| Gunakan HTTPS | Enkripsi channel komunikasi untuk mencegah intercept | Wajib |
| Verifikasi Signature | Tanda tangani firmware dengan private key, verifikasi di ESP32 | Sangat Disarankan |
| Firmware Encryption | Enkripsi file firmware agar tidak bisa dibaca/dimodifikasi | Disarankan |
| Version Check | Pastikan versi baru > versi lama (cegah downgrade) | Wajib |
| Secure Boot | ESP32 hanya menjalankan firmware yang ditandatangani Espressif | Produksi |
| Flash Encryption | Enkripsi seluruh flash memory, firmware tidak bisa dibaca via JTAG | Produksi |
Jangan pernah menyimpan password WiFi atau API key secara plain text di firmware yang dikirim via OTA. Gunakan NVS (Non-Volatile Storage) ESP32 untuk menyimpan kredensial secara terpisah dari firmware, sehingga update firmware tidak menghapus atau mengekspos data sensitif.
Implementasi Rollback Otomatis
ESP32 memiliki fitur OTA rollback built-in yang mencegah boot loop jika firmware baru bermasalah. Jika ESP32 tidak bisa boot dengan firmware baru setelah N kali restart, bootloader akan otomatis kembali ke firmware lama:
#include <esp_ota_ops.h>
void setup() {
Serial.begin(115200);
// Cek apakah ini boot pertama setelah OTA
const esp_partition_t* running =
esp_ota_get_running_partition();
esp_ota_img_states_t state;
if (esp_ota_get_state_partition(running, &state) == ESP_OK) {
if (state == ESP_OTA_IMG_PENDING_VERIFY) {
// Firmware baru berjalan pertama kali
// Lakukan self-test di sini:
bool selfTestOK = runSelfTest();
if (selfTestOK) {
// Konfirmasi firmware valid
esp_ota_mark_app_valid_cancel_rollback();
Serial.println("Firmware baru dikonfirmasi!");
} else {
// Firmware bermasalah, rollback ke versi lama
Serial.println("Self-test gagal! Rollback...");
esp_ota_mark_app_invalid_and_rollback();
ESP.restart();
}
}
}
}
bool runSelfTest() {
// Test koneksi WiFi
// Test koneksi server
// Test sensor aktif
// Return true jika semua OK
return true;
}
6. Proyek Praktis: Remote Update Sensor Node
Sekarang kita akan menggabungkan semua yang telah dipelajari ke dalam satu proyek lengkap: sensor node yang bisa di-update dari jarak jauh. Proyek ini menggunakan ESP32 yang membaca sensor suhu DHT11, mengirim data ke MQTT broker, dan mendukung update firmware via HTTP OTA dari server.
Fitur Proyek
- Monitoring suhu dan kelembaban via MQTT
- Web dashboard untuk status perangkat
- HTTP OTA dengan version checking otomatis
- LED status: hijau (normal), kuning (updating), merah (error)
- Auto-reconnect WiFi dan MQTT
- Rollback otomatis jika firmware baru gagal
┌────────────┐ WiFi ┌──────────────┐ │ ESP32 │──────────────→│ MQTT Broker │ │ Sensor │ publish: │ (Mosquitto) │ │ Node │ sensor/suhu └──────────────┘ │ │ │ ┌──────┐ │ WiFi ┌──────────────┐ │ │DHT11│ │←──────────────│ OTA Server │ │ └──────┘ │ HTTP GET │ (version + │ │ ┌──────┐ │ firmware.bin │ firmware) │ │ │ LED │ │──────────────→│ │ │ └──────┘ │ └──────────────┘ └────────────┘
Kode Lengkap Proyek
// ============================================
// Proyek: Remote Update Sensor Node
// ESP32 + DHT11 + MQTT + HTTP OTA
// BeebaneLabs - https://beebanelabs.pages.dev
// ============================================
#include <WiFi.h>
#include <PubSubClient.h>
#include <HTTPClient.h>
#include <HTTPUpdate.h>
#include <DHT.h>
#include <esp_ota_ops.h>
// ====== KONFIGURASI ======
#define FIRMWARE_VERSION "1.2.0"
#define LED_STATUS_PIN 2
#define DHT_PIN 4
#define DHT_TYPE DHT11
#define OTA_CHECK_INTERVAL 300000 // 5 menit
const char* ssid = "WIFI_KAMU";
const char* password = "PASSWORD_WIFI";
const char* mqtt_server = "192.168.1.100";
const int mqtt_port = 1883;
const char* mqtt_topic = "iot/sensornode/data";
const char* mqtt_cmd_topic= "iot/sensornode/cmd";
const char* ota_version_url =
"http://yourserver.com/ota/version.txt";
const char* ota_firmware_url =
"http://yourserver.com/ota/firmware.bin";
// ====== OBJEK GLOBAL ======
WiFiClient espClient;
PubSubClient mqtt(espClient);
DHT dht(DHT_PIN, DHT_TYPE);
unsigned long lastOTA = 0;
unsigned long lastRead = 0;
// ====== LED STATUS ======
void setLedStatus(const char* mode) {
// Dipanggil dengan "ok", "ota", "error"
// Implementasi LED blink pattern bisa ditambah
Serial.printf("Status: %s\n", mode);
}
// ====== WIFI ======
void setupWiFi() {
WiFi.mode(WIFI_STA);
WiFi.begin(ssid, password);
Serial.print("Menghubungkan WiFi");
int attempts = 0;
while (WiFi.status() != WL_CONNECTED && attempts < 30) {
delay(500);
Serial.print(".");
attempts++;
}
if (WiFi.status() == WL_CONNECTED) {
Serial.println("\nWiFi OK! IP: " + WiFi.localIP().toString());
} else {
Serial.println("\nWiFi gagal! Restart...");
ESP.restart();
}
}
// ====== MQTT ======
void mqttCallback(char* topic, byte* payload, unsigned int length) {
String msg;
for (int i = 0; i < length; i++) {
msg += (char)payload[i];
}
Serial.printf("MQTT [%s]: %s\n", topic, msg.c_str());
if (msg == "check_update") {
checkForOTAUpdate();
} else if (msg == "restart") {
ESP.restart();
} else if (msg == "status") {
publishStatus();
}
}
void connectMQTT() {
while (!mqtt.connected()) {
Serial.print("MQTT connecting...");
if (mqtt.connect("esp32-sensor-node")) {
Serial.println("connected!");
mqtt.subscribe(mqtt_cmd_topic);
} else {
Serial.printf("failed (%d). Retry...\n", mqtt.state());
delay(3000);
}
}
}
void publishStatus() {
String status = "{";
status += "\"version\":\"" + String(FIRMWARE_VERSION) + "\",";
status += "\"ip\":\"" + WiFi.localIP().toString() + "\",";
status += "\"rssi\":" + String(WiFi.RSSI()) + ",";
status += "\"uptime\":" + String(millis() / 1000);
status += "}";
mqtt.publish(mqtt_topic, status.c_str());
}
// ====== OTA UPDATE ======
void checkForOTAUpdate() {
if (WiFi.status() != WL_CONNECTED) return;
Serial.println("Mengecek update OTA...");
setLedStatus("ota");
HTTPClient http;
http.begin(ota_version_url);
int code = http.GET();
if (code == 200) {
String serverVersion = http.getString();
serverVersion.trim();
if (serverVersion != String(FIRMWARE_VERSION)) {
Serial.println("Update tersedia: " + serverVersion);
performOTAUpdate();
} else {
Serial.println("Sudah versi terbaru.");
setLedStatus("ok");
}
} else {
Serial.println("Gagal cek versi: " + String(code));
setLedStatus("error");
}
http.end();
}
void performOTAUpdate() {
WiFiClient client;
t_httpUpdate_return ret =
httpUpdate.update(client, ota_firmware_url);
switch (ret) {
case HTTP_UPDATE_OK:
Serial.println("OTA berhasil! Restart...");
break;
case HTTP_UPDATE_FAILED:
Serial.printf("OTA gagal: %s\n",
httpUpdate.getLastErrorString().c_str());
setLedStatus("error");
break;
default:
break;
}
}
// ====== SETUP ======
void setup() {
Serial.begin(115200);
pinMode(LED_STATUS_PIN, OUTPUT);
dht.begin();
setupWiFi();
mqtt.setServer(mqtt_server, mqtt_port);
mqtt.setCallback(mqttCallback);
connectMQTT();
Serial.println("Sensor Node siap! v" + String(FIRMWARE_VERSION));
setLedStatus("ok");
}
// ====== LOOP ======
void loop() {
// Maintain connections
if (WiFi.status() != WL_CONNECTED) setupWiFi();
if (!mqtt.connected()) connectMQTT();
mqtt.loop();
// Baca sensor setiap 15 detik
if (millis() - lastRead > 15000) {
float suhu = dht.readTemperature();
float hum = dht.readHumidity();
if (!isnan(suhu) && !isnan(hum)) {
String payload = "{";
payload += "\"suhu\":" + String(suhu, 1) + ",";
payload += "\"kelembaban\":" + String(hum, 1) + ",";
payload += "\"versi\":\"" + String(FIRMWARE_VERSION) + "\"";
payload += "}";
mqtt.publish(mqtt_topic, payload.c_str());
Serial.printf("Suhu: %.1f°C, Humidity: %.1f%%\n",
suhu, hum);
}
lastRead = millis();
}
// Cek OTA setiap interval
if (millis() - lastOTA > OTA_CHECK_INTERVAL) {
checkForOTAUpdate();
lastOTA = millis();
}
}
1. Upload kode pertama kali via kabel USB.
2. Sensor suhu mulai dikirim ke MQTT setiap 15 detik.
3. Untuk update remote: naikkan versi di FIRMWARE_VERSION, compile, upload file .bin ke server, dan buat file version.txt berisi versi baru.
4. ESP32 akan otomatis mendeteksi update baru setiap 5 menit.
5. Atau kirim perintah check_update ke topik MQTT iot/sensornode/cmd untuk trigger manual.
7. Quiz: Uji Pemahamanmu!
Setelah membaca tutorial di atas, jawablah 5 pertanyaan berikut untuk menguji pemahamanmu tentang OTA Update pada ESP32: