Pendahuluan
Di artikel BME280 (#13) kita sudah membaca suhu, kelembaban, dan tekanan lewat I2C, lalu mengirimnya ke broker MQTT. Data itu hanya terlihat di Serial Monitor atau aplikasi subscriber — tidak ada feedback langsung di hardware.
Artikel ini menambahkan layar OLED SSD1306 0,96″ (128×64 piksel) ke node yang sama. Pembaca sensor bisa melihat angka di lokasi tanpa membuka laptop. Karena OLED juga memakai I2C, BME280 dan OLED berbagi bus SDA/SCL — hanya alamat perangkat yang berbeda.
Ini lanjutan Jalur A (hardware & sensor) setelah deep sleep (#11), NVS (#12), dan BME280 (#13).
Prasyarat: Sudah paham GPIO dasar (#3), DHT22 (#5), MQTT (#7), dan I2C + BME280 (#13). Untuk WiFi/NVS/broker auth, baca #12 dan #16.
Yang Kamu Butuhkan
- ESP32 DevKit
- Modul sensor BME280 (I2C) — dari artikel #13
- Modul OLED 0,96″ SSD1306 (I2C, 128×64, 3.3V) — ±Rp 20.000–35.000
- Breadboard + kabel jumper
- Broker MQTT — disarankan Mosquitto pribadi (#16) (boleh
test.mosquitto.orghanya untuk uji hardware/OLED)
Mengapa OLED SSD1306?
| Aspek | Serial Monitor saja | + OLED SSD1306 |
|---|---|---|
| Feedback di lapangan | Butuh laptop/USB | Langsung di modul |
| Konsumsi daya | Minimal | ±20–40 mA saat layar menyala |
| Protokol | UART | I2C — satu bus dengan BME280 |
| Use case | Debug development | Panel sensor dinding, greenhouse, gudang |
Catatan daya: Untuk node deep sleep baterai (#11), matikan backlight OLED saat tidur atau pakai layar hanya saat ada interaksi — OLED boros dibanding ESP32 tidur.
Dua Perangkat, Satu Bus I2C
I2C mendukung banyak slave di kabel yang sama. Setiap modul punya alamat unik:
- BME280 — biasanya
0x76atau0x77 - SSD1306 OLED — biasanya
0x3C(kadang0x3D)
Karena alamat berbeda, BME280 dan OLED bisa dirakit paralel ke GPIO 21 (SDA) dan GPIO 22 (SCL) tanpa konflik.
Komponen & Wiring
Hubungkan kedua modul ke ESP32 (paralel di breadboard):
ESP32 DevKit BME280 + OLED (paralel)
───────────── ─────────────────────
3.3V ─────── VCC (kedua modul)
GND ─────── GND (kedua modul)
GPIO 21 (SDA) ─────── SDA (kedua modul)
GPIO 22 (SCL) ─────── SCL (kedua modul)
- Pastikan modul OLED 3.3V — beberapa modul punya jumper VCC/3V3
- Pin RST OLED boleh tidak di-wire jika library pakai
OLED_RESET -1 - Panjang kabel prototype <30 cm biasanya aman; untuk kabel panjang pertimbangkan pull-up 4.7kΩ di SDA/SCL
Install Library
Pastikan Arduino IDE dan board ESP32 sudah terpasang — ikuti tutorial install Arduino IDE & Board Manager (#2) jika belum.
Arduino IDE → Sketch → Include Library → Manage Libraries:
- Adafruit SSD1306
- Adafruit GFX Library (dependency OLED)
- Adafruit BME280 Library + Adafruit Unified Sensor
- PubSubClient (Nick O'Leary)
- WiFiManager (tzapu)
Board: esp32 by Espressif (v3.x). Library Wire, Preferences, dan WiFi sudah built-in.
Topic MQTT (sama #13): kodingindonesia/esp32/bme280/data
Payload JSON: {"suhu":28.5,"kelembaban":65.2,"tekanan":1013.25}
Kode Lengkap: BME280 + OLED + WiFiManager + MQTT
Sketch membaca BME280, menggambar suhu/kelembaban/tekanan di OLED, lalu publish JSON ke broker — pola NVS seperti #12 dan #16.
#include <Wire.h>
#include <WiFi.h>
#include <WiFiManager.h>
#include <Preferences.h>
#include <PubSubClient.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_BME280.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#define I2C_SDA 21
#define I2C_SCL 22
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
#define OLED_RESET -1
#define SCREEN_ADDRESS 0x3C
const char* NS_KINDO = "kindo";
const int MQTT_PORT = 1883;
Adafruit_BME280 bme;
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
WiFiClient espClient;
PubSubClient mqttClient(espClient);
Preferences prefs;
String mqttHost, mqttUser, mqttPass, topicSensor;
WiFiManagerParameter pHost("mqtt_host", "MQTT broker IP", "192.168.1.50", 64);
WiFiManagerParameter pUser("mqtt_user", "MQTT username", "kindo_esp32", 32);
WiFiManagerParameter pPass("mqtt_pass", "MQTT password", "", 48);
WiFiManagerParameter pTopic("mqtt_topic", "MQTT topic", "kodingindonesia/esp32/bme280/data", 64);
bool initI2C() {
Wire.begin(I2C_SDA, I2C_SCL);
delay(50);
return true;
}
bool initBME280() {
if (bme.begin(0x76)) return true;
if (bme.begin(0x77)) return true;
Serial.println("BME280 tidak ditemukan");
return false;
}
bool initOLED() {
if (display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS)) return true;
if (display.begin(SSD1306_SWITCHCAPVCC, 0x3D)) return true;
Serial.println("OLED SSD1306 tidak ditemukan");
return false;
}
void tampilkanOLED(float suhu, float kelembaban, float tekanan) {
display.clearDisplay();
display.setTextSize(1);
display.setTextColor(SSD1306_WHITE);
display.setCursor(0, 0);
display.println(F("Koding Indonesia"));
display.println(F("BME280 + OLED"));
display.drawLine(0, 18, 128, 18, SSD1306_WHITE);
display.setCursor(0, 24);
display.printf("Suhu: %.1f C", suhu);
display.printf("\nRH: %.1f %%", kelembaban);
display.printf("\nTekanan: %.0f hPa", tekanan);
display.display();
}
void muatMqttDariNvs() {
prefs.begin(NS_KINDO, true);
mqttHost = prefs.getString("mqtt_host", "192.168.1.50");
mqttUser = prefs.getString("mqtt_user", "kindo_esp32");
mqttPass = prefs.getString("mqtt_pass", "");
topicSensor = prefs.getString("mqtt_topic", "kodingindonesia/esp32/bme280/data");
prefs.end();
}
void simpanMqttKeNvs() {
prefs.begin(NS_KINDO, false);
prefs.putString("mqtt_host", pHost.getValue());
prefs.putString("mqtt_user", pUser.getValue());
prefs.putString("mqtt_pass", pPass.getValue());
prefs.putString("mqtt_topic", pTopic.getValue());
prefs.end();
}
bool setupWiFiManager() {
WiFiManager wm;
wm.setConfigPortalTimeout(180);
wm.addParameter(&pHost);
wm.addParameter(&pUser);
wm.addParameter(&pPass);
wm.addParameter(&pTopic);
muatMqttDariNvs();
pHost.setValue(mqttHost.c_str(), 64);
pUser.setValue(mqttUser.c_str(), 32);
pPass.setValue(mqttPass.c_str(), 48);
pTopic.setValue(topicSensor.c_str(), 64);
if (!wm.autoConnect("KindoESP32-Setup")) return false;
simpanMqttKeNvs();
return true;
}
bool koneksiMQTT() {
mqttClient.setServer(mqttHost.c_str(), MQTT_PORT);
mqttClient.setBufferSize(512);
String clientId = "ESP32-OLED-" + String(random(0xffff), HEX);
if (!mqttClient.connect(clientId.c_str(), mqttUser.c_str(), mqttPass.c_str())) {
Serial.print("MQTT gagal, rc=");
Serial.println(mqttClient.state());
return false;
}
return true;
}
void bacaDanTampilkan() {
float suhu = bme.readTemperature();
float kelembaban = bme.readHumidity();
float tekanan = bme.readPressure() / 100.0F;
if (isnan(suhu) || isnan(kelembaban) || isnan(tekanan)) {
Serial.println("BME280 baca gagal");
return;
}
tampilkanOLED(suhu, kelembaban, tekanan);
char payload[128];
snprintf(payload, sizeof(payload),
"{\"suhu\":%.1f,\"kelembaban\":%.1f,\"tekanan\":%.2f}",
suhu, kelembaban, tekanan);
mqttClient.loop();
if (mqttClient.publish(topicSensor.c_str(), payload, false)) {
Serial.print("Publish OK → ");
Serial.println(payload);
} else {
Serial.println("Publish gagal");
}
}
void setup() {
Serial.begin(115200);
delay(500);
initI2C();
if (!initBME280() || !initOLED()) {
while (true) delay(1000);
}
tampilkanOLED(0, 0, 0);
display.clearDisplay();
display.setTextSize(1);
display.setTextColor(SSD1306_WHITE);
display.setCursor(0, 24);
display.println(F("Menghubungkan..."));
display.display();
if (!setupWiFiManager()) ESP.restart();
if (!koneksiMQTT()) ESP.restart();
bacaDanTampilkan();
}
void loop() {
mqttClient.loop();
delay(5000);
bacaDanTampilkan();
}
Penjelasan Bagian Kritis
Wire.begin(21, 22)sekali — BME280 dan OLED berbagi objekWireyang samadisplay.begin(SSD1306_SWITCHCAPVCC, 0x3C)— coba0x3Djika layar kosongdisplay.display()— wajib dipanggil setelah menggambar; tanpa ini layar tidak berubahdisplay.clearDisplay()— hapus buffer sebelum menggambar frame baru (hindari ghosting)- Urutan init —
Wire.begin→ BME280 → OLED; jika salah satu gagal, hentikan agar mudah debug mqttClient.loop()— tetap dipanggil diloop()meski fokus artikel ini adalah layar
Uji Coba (Step-by-Step)
- Rakit BME280 + OLED paralel di breadboard, upload sketch, Serial Monitor 115200
- Pastikan OLED menampilkan teks
Menghubungkan...lalu angka sensor - Portal
KindoESP32-Setup— isi WiFi + kredensial broker (#16) - Verifikasi angka di OLED ≈ Serial Monitor / MQTT Explorer
- Subscribe topic di laptop:
mosquitto_sub -h 192.168.1.50 -p 1883 \
-u kindo_esp32 -P 'KindoMQTT2026!' \
-t "kodingindonesia/esp32/bme280/data" -v
Pro tip: Untuk demo, tiup hangat ke BME280 — suhu di OLED naik dalam 2–3 detik. Topic unik per unit, misalnya
kodingindonesia/anton/esp32/bme280/data.
Scanner I2C (Opsional)
Jika salah satu modul tidak terdeteksi, jalankan sketch Scan I2C dari artikel #13. Kamu harus melihat dua alamat (misalnya 0x76 + 0x3C).
Tips & Troubleshooting
- Layar putih/kosong: Cek alamat
0x3Cvs0x3D, wiring 3.3V, dan panggilandisplay.display() - Hanya BME280 terdeteksi: OLED mungkin modul SPI — pastikan beli varian I2C (4 pin: GND VCC SCL SDA)
- Teks terpotong: Resolusi 128×64 — pakai
setTextSize(1); untuk font besar kurangi jumlah baris - Ghosting/berbayang: Selalu
clearDisplay()sebelum menggambar ulang - BME280 gagal setelah pasang OLED: Cek beban 3.3V — dua modul + ESP32; gunakan USB port yang stabil
- Portal WiFi tidak muncul:
wm.resetSettings()atau bukahttp://192.168.4.1— sama seperti #12 - MQTT gagal (rc=-2): Broker tidak terjangkau — cek IP, firewall port 1883, satu jaringan WiFi
- MQTT auth (rc=5): Lihat troubleshooting broker #16
- Compile error WiFiManager: Update library tzapu ke 2.x; pastikan board esp32 v3.x
- Compile error GFX: Install Adafruit GFX versi terbaru sebelum SSD1306
- WiFi 2.4 GHz: ESP32 tidak support jaringan WiFi 5 GHz saja
Keamanan & Produksi
- Jangan commit password MQTT ke Git — simpan lewat portal WiFiManager + NVS seperti artikel #12
- OLED menampilkan data sensor di lokasi fisik — hindari menampilkan kredensial atau token di layar
- Gunakan broker Mosquitto pribadi + auth (#16) — jangan andalkan
test.mosquitto.orguntuk data produksi
Langkah Selanjutnya (Seri 2)
- OTA update firmware — update tanpa kabel USB (butuh #12 WiFiManager)
- Gabung deep sleep (#11) + BME280 + OLED untuk node lapangan (matikan OLED saat tidur)
- Subscriber Python → MySQL (#18) untuk histori + OLED sebagai panel lokal
- Capstone greenhouse (#39) — sensor + layar + pompa relay
Dengan OLED di bus I2C yang sama, node sensormu punya antarmuka lokal selain MQTT cloud. Lanjutkan di halaman artikel Koding Indonesia.