Dashboard & Cloud

Firebase Realtime Database untuk IoT

Tutorial lengkap Firebase Realtime Database untuk IoT. Pelajari data structure, security rules, offline persistence, dan Cloud Functions untuk proyek IoT.

1. Apa Itu Firebase Realtime Database?

Firebase Realtime Database (RTDB) adalah cloud-hosted NoSQL database yang menyimpan data dalam format JSON dan menyinkronkan data secara real-time ke setiap client yang terhubung. Setiap kali data berubah, semua connected client akan menerima update secara otomatis — tanpa perlu polling atau WebSocket manual.

Untuk proyek IoT, Firebase RTDB sangat menarik karena kesederhanaannya. Kamu bisa dengan mudah mengirim data sensor dari ESP32 ke database, dan data tersebut langsung muncul di web dashboard dan mobile app secara real-time. Firebase juga menyediakan SDK untuk web, iOS, Android, dan bahkan Arduino/ESP32 melalui library pihak ketiga.

💡 Tips

Firebase menyediakan Spark Plan (gratis) yang cukup untuk development dan proyek kecil: 1 GB storage, 10 GB/bulan bandwidth, 100 koneksi simultan. Untuk produksi, kamu perlu Blaze Plan (pay-as-you-go).

Firebase RTDB vs Firestore

AspekRealtime DatabaseCloud Firestore
Data ModelJSON tree besarCollections & documents
QuerySorting/filtering terbatasCompound queries
Real-timeYa (seluruh subtree)Ya (per query)
Offline SupportYa (Android/iOS)Ya (Android/iOS/Web)
ScalabilitySingle databaseMulti-region, auto-scaling
PricingBerbasis bandwidthBerbasis operasi baca/tulis
Cocok untuk IoTYa (lebih sederhana)Ya (lebih scalable)

2. Setup Firebase Project

Langkah 1: Buat Firebase Project

  1. Buka Firebase Console
  2. Klik "Add project" → beri nama: iot-monitoring
  3. Nonaktifkan Google Analytics (opsional) → Create Project
  4. Setelah project dibuat, klik BuildRealtime Database
  5. Klik "Create Database" → pilih lokasi server terdekat
  6. Pilih "Start in test mode" (untuk development)

Langkah 2: Dapatkan Service Account Key

# Untuk ESP32/server-side:
# 1. Buka Project Settings → Service Accounts
# 2. Klik "Generate new private key"
# 3. Simpan file JSON dengan aman
# 4. Dari file JSON, ambil:
#    - database_url: https://your-project.firebaseio.com
#    - api_key: dari Project Settings → General → Web API Key

# Untuk ESP32, kamu perlu:
# - Firebase Host (database URL)
# - Firebase Auth Token (API key atau custom token)

Langkah 3: Install Library ESP32

# Arduino IDE: Install via Library Manager
# - Firebase ESP Client by mobizt
# - ArduinoJson by Benoit Blanchon

# PlatformIO: platformio.ini
[env:esp32]
platform = espressif32
board = esp32dev
framework = arduino
lib_deps =
    mobizt/Firebase ESP Client@^4.4.0
    bblanchon/ArduinoJson@^7.0.0

3. Data Structure untuk IoT

Desain data structure yang baik sangat penting di Firebase RTDB karena tidak adanya schema. Flat structure lebih disukai daripada deeply nested data karena mempengaruhi performa query dan security rules.

Struktur Data yang Direkomendasikan

{
  "devices": {
    "sensor-01": {
      "info": {
        "name": "Sensor Suhu Ruang Server",
        "location": "Building A, Floor 2",
        "type": "DHT22",
        "firmware": "2.1.0",
        "last_seen": 1719600000
      },
      "telemetry": {
        "1719600000": { "temp": 25.6, "humidity": 65 },
        "1719600060": { "temp": 25.8, "humidity": 64 },
        "1719600120": { "temp": 26.1, "humidity": 63 }
      },
      "latest": {
        "temp": 26.1,
        "humidity": 63,
        "battery": 87,
        "timestamp": 1719600120
      },
      "config": {
        "report_interval": 60,
        "temp_threshold": 40,
        "alert_enabled": true
      }
    }
  },
  "alerts": {
    "alert-001": {
      "device": "sensor-01",
      "type": "HIGH_TEMPERATURE",
      "value": 41.2,
      "threshold": 40,
      "timestamp": 1719600120,
      "status": "active"
    }
  },
  "users": {
    "user-uid-001": {
      "name": "Admin",
      "email": "admin@example.com",
      "devices": {
        "sensor-01": true
      }
    }
  }
}
⚠️ Data Structure Best Practices
  • Flat structure: Hindari nesting lebih dari 3 level
  • Denormalize: Duplikasi data jika perlu untuk query yang lebih cepat
  • Separate telemetry: Simpan time-series data terpisah dari metadata device
  • Timestamp as key: Gunakan timestamp Unix sebagai key untuk ordering otomatis
  • Limits: Node terbesar yang di-download sekaligus tidak boleh lebih dari 256MB

4. ESP32 + Firebase Integration

Koneksi ESP32 ke Firebase

// ESP32 + Firebase Realtime Database
#include <WiFi.h>
#include <Firebase_ESP_Client.h>
#include "addons/TokenHelper.h"
#include "addons/RTDBHelper.h"

#define WIFI_SSID "your-wifi"
#define WIFI_PASS "your-password"
#define API_KEY "your-firebase-api-key"
#define DATABASE_URL "https://your-project-default-rtdb.firebaseio.com"
#define USER_EMAIL "device@iot.com"
#define USER_PASS "device-password"

FirebaseData fbdo;
FirebaseAuth auth;
FirebaseConfig config;

void setup() {
  Serial.begin(115200);
  WiFi.begin(WIFI_SSID, WIFI_PASS);
  while (WiFi.status() != WL_CONNECTED) delay(500);
  
  config.api_key = API_KEY;
  config.database_url = DATABASE_URL;
  auth.user.email = USER_EMAIL;
  auth.user.password = USER_PASS;
  
  Firebase.begin(&config, &auth);
  Firebase.reconnectWiFi(true);
}

void sendTelemetry(float temp, float humidity) {
  String path = "/devices/sensor-01/latest";
  
  Firebase.RTDB.setFloat(&fbdo, path + "/temp", temp);
  Firebase.RTDB.setFloat(&fbdo, path + "/humidity", humidity);
  Firebase.RTDB.setInt(&fbdo, path + "/timestamp", (int)time(NULL));
  Firebase.RTDB.setInt(&fbdo, path + "/battery", readBattery());
  
  // Simpan ke history
  String historyPath = "/devices/sensor-01/telemetry/" + String((int)time(NULL));
  Firebase.RTDB.setFloat(&fbdo, historyPath + "/temp", temp);
  Firebase.RTDB.setFloat(&fbdo, historyPath + "/humidity", humidity);
  
  Serial.println("Data sent to Firebase!");
}

void readConfig() {
  if (Firebase.RTDB.getInt(&fbdo, "/devices/sensor-01/config/report_interval")) {
    int interval = fbdo.intData();
    Serial.printf("Report interval: %d seconds\n", interval);
  }
}

void loop() {
  static unsigned long lastSend = 0;
  if (millis() - lastSend > 60000) {
    float temp = dht.readTemperature();
    float hum = dht.readHumidity();
    sendTelemetry(temp, hum);
    readConfig();
    lastSend = millis();
  }
}

Streaming Data (Real-time Listener)

// Stream perubahan config dari Firebase ke ESP32
void streamCallback(FirebaseStream data) {
  Serial.printf("Stream path: %s\n", data.streamPath().c_str());
  Serial.printf("Data: %s\n", data.stringData().c_str());
  
  if (data.streamPath() == "/config/report_interval") {
    int newInterval = data.intData();
    updateReportInterval(newInterval);
  }
}

void streamTimeoutCallback(bool timeout) {
  if (timeout) Serial.println("Stream timeout, reconnecting...");
}

void setup() {
  // ... setelah Firebase.begin()
  Firebase.RTDB.beginStream(&fbdo, "/devices/sensor-01/config");
  Firebase.RTDB.setStreamCallback(&fbdo, streamCallback, streamTimeoutCallback);
}

void loop() {
  // Stream berjalan di background
  // Tidak perlu polling manual
}

5. Security Rules

Security rules sangat penting untuk melindungi data IoT kamu. Tanpa rules yang benar, siapa saja bisa membaca atau mengubah data device kamu.

Contoh Security Rules

{
  "rules": {
    "devices": {
      "$device_id": {
        // Device bisa tulis telemetry sendiri
        "latest": {
          ".read": "auth != null",
          ".write": "auth != null && auth.uid == $device_id"
        },
        "telemetry": {
          ".read": "auth != null",
          ".write": "auth != null && auth.uid == $device_id",
          // Validasi format data
          "$timestamp": {
            ".validate": "newData.hasChildren(['temp', 'humidity'])",
            "temp": {
              ".validate": "newData.isNumber() && newData.val() >= -40 && newData.val() <= 80"
            },
            "humidity": {
              ".validate": "newData.isNumber() && newData.val() >= 0 && newData.val() <= 100"
            }
          }
        },
        "config": {
          ".read": "auth != null",
          ".write": "root.child('users/' + auth.uid + '/devices/' + $device_id).exists()"
        },
        "info": {
          ".read": "auth != null",
          ".write": "false"
        }
      }
    },
    "alerts": {
      ".read": "auth != null",
      ".write": "false"
    },
    "users": {
      "$uid": {
        ".read": "auth != null && auth.uid == $uid",
        ".write": "auth != null && auth.uid == $uid"
      }
    }
  }
}
💡 Tips Security
  • Selalu autentikasi device menggunakan custom token atau email/password
  • Validasi data type dan range di security rules
  • Jangan pernah menggunakan ".read": true atau ".write": true di produksi
  • Gunakan Firebase Admin SDK di server untuk operasi admin
  • Test rules menggunakan Firebase Rules Playground

6. Offline Persistence & Sync

Firebase RTDB mendukung offline persistence di platform mobile (Android/iOS) dan web (dengan SDK web v9+). Ketika client kehilangan koneksi, data tetap tersimpan secara lokal dan disinkronkan otomatis saat koneksi pulih.

Web SDK: Enable Persistence

// Firebase Web SDK v9+
import { initializeApp } from "firebase/app";
import { getDatabase, ref, onValue, enableIndexedDbPersistence } from "firebase/database";

const app = initializeApp(firebaseConfig);
const db = getDatabase(app);

// Enable offline persistence
enableIndexedDbPersistence(db).catch((err) => {
  if (err.code == 'failed-precondition') {
    console.log('Multiple tabs open, persistence only works in one tab');
  } else if (err.code == 'unimplemented') {
    console.log('Browser does not support persistence');
  }
});

// Listen to sensor data (works offline too)
const sensorRef = ref(db, 'devices/sensor-01/latest');
onValue(sensorRef, (snapshot) => {
  const data = snapshot.val();
  updateDashboard(data.temp, data.humidity);
  // Data ini akan di-cache dan tersedia offline
});

Monitoring Connection Status

// Monitor koneksi Firebase
const connectedRef = ref(db, '.info/connected');
onValue(connectedRef, (snap) => {
  if (snap.val() === true) {
    console.log("Connected to Firebase");
    document.getElementById('status').textContent = '🟢 Online';
  } else {
    console.log("Disconnected from Firebase");
    document.getElementById('status').textContent = '🔴 Offline';
  }
});

7. Cloud Functions untuk IoT Automation

Firebase Cloud Functions memungkinkan kamu menjalankan kode server-side secara otomatis saat data berubah di RTDB. Ini sangat berguna untuk IoT automation seperti alerting, data processing, dan integrasi dengan layanan lain.

Contoh Cloud Functions

// functions/index.js
const functions = require("firebase-functions");
const admin = require("firebase-admin");
const nodemailer = require("nodemailer");
admin.initializeApp();

// Trigger: Kirim alert saat suhu melebihi threshold
exports.onTemperatureAlert = functions.database
  .ref("/devices/{deviceId}/latest/temp")
  .onWrite(async (change, context) => {
    const newTemp = change.after.val();
    const deviceId = context.params.deviceId;
    const threshold = 40;

    if (newTemp > threshold) {
      // Simpan alert
      const alertRef = admin.database().ref("/alerts").push();
      await alertRef.set({
        device: deviceId,
        type: "HIGH_TEMPERATURE",
        value: newTemp,
        threshold: threshold,
        timestamp: admin.database.ServerValue.TIMESTAMP,
        status: "active"
      });

      // Kirim notifikasi (contoh: FCM)
      const payload = {
        notification: {
          title: "⚠️ Suhu Tinggi!",
          body: `Device ${deviceId}: ${newTemp}°C (threshold: ${threshold}°C)`
        },
        topic: "iot-alerts"
      };
      await admin.messaging().send(payload);
      
      console.log(`Alert created for ${deviceId}: ${newTemp}°C`);
    }
  });

// Scheduled: Cleanup telemetry data lebih dari 30 hari
exports.cleanupTelemetry = functions.pubsub
  .schedule("every 24 hours")
  .onRun(async (context) => {
    const db = admin.database();
    const devices = await db.ref("/devices").once("value");
    const thirtyDaysAgo = Date.now() - (30 * 24 * 60 * 60 * 1000);
    
    devices.forEach((device) => {
      const deviceId = device.key();
      const telemetry = device.child("telemetry");
      telemetry.forEach((entry) => {
        const timestamp = parseInt(entry.key) * 1000;
        if (timestamp < thirtyDaysAgo) {
          db.ref(`/devices/${deviceId}/telemetry/${entry.key}`).remove();
        }
      });
    });
  });

// HTTP: API endpoint untuk external integration
exports.getDeviceStatus = functions.https.onRequest(async (req, res) => {
  const deviceId = req.query.device;
  const snapshot = await admin.database()
    .ref(`/devices/${deviceId}/latest`).once("value");
  res.json(snapshot.val());
});

8. Web Dashboard dengan Firebase

Membuat web dashboard real-time dengan Firebase sangat mudah menggunakan Firebase Web SDK. Data sensor akan update otomatis tanpa perlu refresh halaman.

<!-- Web Dashboard sederhana -->
<div id="dashboard">
  <div class="sensor-card">
    <h3>🌡️ Temperature</h3>
    <span id="temp-value">--</span>°C
  </div>
  <div class="sensor-card">
    <h3>💧 Humidity</h3>
    <span id="humidity-value">--</span>%
  </div>
  <div class="status-bar">
    Status: <span id="connection-status">🔴</span>
    Last update: <span id="last-update">--</span>
  </div>
</div>

<script type="module">
import { initializeApp } from "https://www.gstatic.com/firebasejs/10.12.0/firebase-app.js";
import { getDatabase, ref, onValue } from "https://www.gstatic.com/firebasejs/10.12.0/firebase-database.js";

const app = initializeApp({ databaseURL: "https://your-project.firebaseio.com" });
const db = getDatabase(app);

// Real-time listener
onValue(ref(db, 'devices/sensor-01/latest'), (snapshot) => {
  const data = snapshot.val();
  document.getElementById('temp-value').textContent = data.temp.toFixed(1);
  document.getElementById('humidity-value').textContent = data.humidity.toFixed(0);
  document.getElementById('last-update').textContent = new Date(data.timestamp * 1000).toLocaleString();
});

// Connection status
onValue(ref(db, '.info/connected'), (snap) => {
  document.getElementById('connection-status').textContent = snap.val() ? '🟢 Online' : '🔴 Offline';
});
</script>

9. Quiz: Uji Pemahamanmu!

Setelah membaca tutorial di atas, jawablah 5 pertanyaan berikut untuk menguji pemahamanmu tentang Firebase RTDB untuk IoT:

Pertanyaan 1: Apa format penyimpanan data di Firebase Realtime Database?

a) Relational (SQL tables)
b) JSON tree
c) Key-Value pairs
d) XML documents

Pertanyaan 2: Mengapa flat structure lebih disukai daripada deeply nested di RTDB?

a) Karena nested structure tidak didukung
b) Karena download sekaligus subtree besar mempengaruhi performa dan security rules
c) Karena flat structure menggunakan lebih sedikit bandwidth
d) Tidak ada perbedaan performa

Pertanyaan 3: Apa yang terjadi saat Firebase client kehilangan koneksi internet?

a) Semua data hilang
b) Data ter-cache lokal dan disinkronkan otomatis saat koneksi pulih
c) Client harus reconnect manual
d) Database menolak semua operasi

Pertanyaan 4: Firebase Cloud Functions dapat di-trigger oleh...

a) Hanya HTTP request
b) Hanya perubahan data di RTDB
c) Perubahan data, HTTP request, pubsub schedule, authentication events, dll
d) Hanya cron job terjadwal

Pertanyaan 5: Untuk device ESP32, apa yang perlu dikonfigurasi agar bisa menulis data ke RTDB?

a) Hanya database URL
b) API Key, Database URL, dan Authentication (email/password atau custom token)
c) Hanya IP address server Firebase
d) Tidak perlu konfigurasi apapun
← Sebelumnya ThingsBoard IoT Platform Selanjutnya → AWS IoT Core
🔍 Zoom
100%
🎨 Tema