Pendahuluan

Di Seri 1 kita memakai DHT22 — sensor digital satu kabel data yang mudah untuk pemula. Di Seri 2, saatnya naik level ke I2C: bus dua kawat (SDA/SCL) yang bisa menghubungkan banyak perangkat sekaligus, dengan akurasi lebih baik.

Artikel ini fokus pada sensor BME280: mengukur suhu, kelembaban, dan tekanan udara dalam satu modul kecil. Kita baca datanya lewat I2C, lalu publish JSON ke broker MQTT — mengikuti pola NVS + WiFiManager (#12) dan broker pribadi (#16).

Ini lanjutan Jalur A (hardware & sensor) setelah deep sleep (#11) dan konfigurasi lapangan (#12).

Prasyarat: Sudah paham GPIO dasar (#3), DHT22 (#5), MQTT (#7). Untuk WiFi/NVS/broker auth, baca #12 dan #16.

Yang Kamu Butuhkan

  • ESP32 DevKit
  • Modul sensor BME280 (breakout I2C, 3.3V) — ±Rp 25.000–40.000 di marketplace lokal
  • Kabel jumper female–female atau breadboard
  • Broker MQTT — disarankan Mosquitto pribadi (boleh test.mosquitto.org hanya untuk uji hardware)

DHT22 vs BME280 — Kenapa Upgrade?

AspekDHT22 (Seri 1)BME280 (Seri 2)
ProtokolDigital 1-wire (GPIO)I2C (SDA + SCL)
SuhuCukup untuk hobbyStabil, cocok monitoring & cuaca
Kelembaban±2–5% RH±3% RH
Tekanan udaraTidak adaAda (hPa) — ketinggian, cuaca
Perangkat di satu bus1 pin per sensorBanyak (alamat I2C berbeda)

Apa itu I2C?

I2C (Inter-Integrated Circuit) memakai dua kabel data:

  • SDA — data (Serial Data)
  • SCL — clock (Serial Clock)

Setiap perangkat punya alamat 7-bit unik di bus yang sama. BME280 umumnya 0x76 atau 0x77 (tergantung jumper modul).

Analogi: I2C seperti lift apartemen — banyak penghuni (sensor), satu jalur (bus), setiap lantai punya nomor (alamat).

Komponen & Wiring I2C

Pin default I2C ESP32 DevKit (bisa diubah di kode):

ESP32 DevKit          BME280
─────────────         ──────
3.3V          ─────── VCC
GND           ─────── GND
GPIO 21 (SDA) ─────── SDA
GPIO 22 (SCL) ─────── SCL
  • Modul breakout BME280 biasanya sudah punya pull-up kecil di PCB
  • Pastikan modul 3.3V — jangan 5V langsung ke ESP32
  • Jika bme.begin() gagal, coba alamat 0x77 (beberapa modul pakai ini)

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 BME280 Library
  • Adafruit Unified Sensor (dependency)
  • PubSubClient (Nick O'Leary)
  • WiFiManager (tzapu) — provisioning WiFi + MQTT

Board: esp32 by Espressif (v3.x). Library Wire, Preferences, dan WiFi sudah built-in.

Broker & topic: publish ke broker kamu (lihat #16).

Topic default: kodingindonesia/esp32/bme280/data

Payload JSON: {"suhu":28.5,"kelembaban":65.2,"tekanan":1013.25} — tekanan dalam hPa.

Catatan topic Seri 2: DHT22 memakai .../dht22/data. BME280 punya subtopic sendiri .../bme280/data agar hierarki MQTT tetap rapi.

Kode Lengkap: BME280 + I2C + WiFiManager + MQTT Auth

Pola NVS sama artikel #16 — tanpa hardcode WiFi/MQTT di sketch.

#include <Wire.h>
#include <WiFi.h>
#include <WiFiManager.h>
#include <Preferences.h>
#include <PubSubClient.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_BME280.h>

#define I2C_SDA 21
#define I2C_SCL 22
const char* NS_KINDO = "kindo";
const int MQTT_PORT = 1883;

Adafruit_BME280 bme;
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 initBME280() {
  Wire.begin(I2C_SDA, I2C_SCL);
  if (bme.begin(0x76)) return true;
  if (bme.begin(0x77)) return true;
  Serial.println("BME280 tidak ditemukan — cek wiring I2C");
  return false;
}

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-BME280-" + 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 publishBME280() {
  float suhu = bme.readTemperature();
  float kelembaban = bme.readHumidity();
  float tekanan = bme.readPressure() / 100.0F; // Pa → hPa

  if (isnan(suhu) || isnan(kelembaban) || isnan(tekanan)) {
    Serial.println("BME280 baca gagal");
    return;
  }

  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);

  if (!initBME280()) {
    while (true) delay(1000);
  }
  delay(100);

  if (!setupWiFiManager()) ESP.restart();
  if (!koneksiMQTT()) ESP.restart();

  publishBME280();
}

void loop() {
  mqttClient.loop();
  delay(10000);
  publishBME280();
}

Penjelasan Bagian Kritis

  • Wire.begin(I2C_SDA, I2C_SCL) — inisialisasi bus I2C di pin 21/22
  • bme.begin(0x76) / 0x77 — coba kedua alamat umum modul breakout
  • readPressure() / 100.0F — library mengembalikan Pascal; dibagi 100 jadi hPa
  • setBufferSize(512) — payload JSON 3 field butuh buffer cukup besar
  • mqttClient.loop() — wajib sebelum publish()

Uji Coba (Step-by-Step)

  1. Rakit wiring I2C, upload sketch, Serial Monitor 115200
  2. Pastikan tidak ada error BME280 tidak ditemukan
  3. Portal KindoESP32-Setup — isi WiFi + kredensial broker (#16)
  4. Serial: Publish OK dengan JSON suhu, kelembaban, tekanan
  5. Di laptop — mosquitto_sub atau MQTT Explorer:
mosquitto_sub -h 192.168.1.50 -p 1883 \
  -u kindo_esp32 -P 'KindoMQTT2026!' \
  -t "kodingindonesia/esp32/bme280/data" -v

Pro tip: Topic unik per unit, misalnya kodingindonesia/anton/esp32/bme280/data.

Scanner I2C (Opsional)

Jika sensor tidak terdeteksi, upload sketch scanner untuk melihat alamat di bus:

#include <Wire.h>
void setup() {
  Serial.begin(115200);
  Wire.begin(21, 22);
  Serial.println("Scan I2C...");
  for (byte addr = 1; addr < 127; addr++) {
    Wire.beginTransmission(addr);
    if (Wire.endTransmission() == 0) {
      Serial.printf("Perangkat di 0x%02X\n", addr);
    }
  }
}
void loop() {}

Tips & Troubleshooting

  • BME280 tidak ditemukan: Cek 3.3V, SDA/SCL tidak tertukar, coba 0x77, jalankan scanner I2C
  • Nilai tekanan aneh: Normal sekitar 950–1050 hPa di permukaan laut; bandingkan dengan cuaca lokal
  • Portal WiFi tidak muncul: Reset WiFi tersimpan — tahan BOOT saat boot atau wm.resetSettings(); buka manual http://192.168.4.1 jika captive portal tidak redirect
  • Compile error WiFiManager: Update library tzapu ke 2.x; pastikan board esp32 v3.x
  • MQTT gagal (rc=-2): Broker tidak terjangkau — cek IP host, firewall port 1883, ESP32 dan broker satu jaringan
  • MQTT auth gagal (rc=5): Username/password salah — lihat troubleshooting broker #16
  • Panjang kabel I2C: Untuk prototype breadboard <30 cm biasanya aman; kabel panjang butuh pull-up lebih kuat
  • 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
  • Portal AP KindoESP32-Setup default tanpa password — untuk deploy lapangan, pertimbangkan password AP atau provisioning terbatas
  • Gunakan broker Mosquitto pribadi + auth (#16) — jangan andalkan test.mosquitto.org untuk data produksi

Langkah Selanjutnya (Seri 2)

  • Artikel #14: OLED SSD1306 — tampilkan suhu/tekanan di layar (bus I2C sama, alamat berbeda dari BME280)
  • Artikel #15: OTA update — update firmware tanpa kabel
  • Gabung dengan deep sleep (#11) untuk node sensor hemat baterai + BME280
  • Subscriber Python → MySQL (#18) untuk simpan histori tekanan & suhu

Dengan I2C dan BME280, hardware stack kamu siap untuk dashboard OLED dan capstone greenhouse Seri 2. Lanjutkan di halaman artikel Koding Indonesia.