← Back to list

MQTT Relay Control HiveMq

HiveMQ Public

Rivaldi valdi · 2025-11-26 02:20 · 0 claps · 8.1 min read
#dashboard #iot #mqtt #data #webpage
Open on Medium ↗
Wiki topics: 📟 · Gadgets & IoT 🎬 · Film & Television

MQTT Relay Control HiveMq

HiveMQ Public

Dashboard Menggunakan HiveMq Mqtt

Dashboard Menggunakan HiveMq Mqtt

Program Ke ESP8266 NodeMcu

Program Ke ESP8266 NodeMcu

Berikut Coding Untuk WebPage Nya :v

<!DOCTYPE html>
<html lang="id">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Dashboard IoT Realtime (MQTT - tkj3_topic)</title>
    <!-- Load Tailwind CSS -->
    <script src="https://cdn.tailwindcss.com"></script>
    <script>
        tailwind.config = {
            theme: {
                extend: {
                    fontFamily: {
                        sans: ['Inter', 'sans-serif'],
                    },
                    colors: {
                        'primary': '#4f46e5',
                        'secondary': '#10b981',
                        'danger': '#ef4444',
                        'card-bg': '#f8fafc',
                    }
                }
            }
        }
    </script>
    <style>
        /* Impor font Inter */
        @import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;600;700&display=swap');
    </style>
</head>
<body class="bg-gray-100 min-h-screen p-4 font-sans antialiased">

    <!-- Header -->
    <header class="text-center mb-8">
        <h1 class="text-3xl font-bold text-gray-800">Dashboard Kontrol IoT (MQTT)</h1>
        <p class="text-gray-500">Monitoring Suhu, Kelembapan, dan Kontrol Relay 1 & 2 melalui MQTT</p>
    </header>

    <main class="max-w-4xl mx-auto space-y-6">

        <!-- Monitoring Card Section -->
        <section class="grid grid-cols-1 sm:grid-cols-2 gap-6">

            <!-- Suhu (Temperature) Card -->
            <div id="suhu-card" class="bg-card-bg p-6 rounded-xl shadow-lg border-l-4 border-primary transition duration-300 hover:shadow-xl">
                <div class="flex items-center justify-between">
                    <h2 class="text-xl font-semibold text-gray-700 flex items-center">
                        <svg class="w-6 h-6 mr-2 text-primary" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 3v1m0 16v1m9-9h-1M4 12H3m15.364 6.364l-.707-.707M6.343 6.343l-.707-.707m12.728 0l-.707.707M6.343 17.657l-.707.707M16 12a4 4 0 11-8 0 4 4 0 018 0z"></path></svg>
                        Suhu
                    </h2>
                    <span id="suhu-value" class="text-4xl font-bold text-primary animate-pulse">-- °C</span>
                </div>
            </div>

            <!-- Kelembapan (Humidity) Card -->
            <div id="kelembapan-card" class="bg-card-bg p-6 rounded-xl shadow-lg border-l-4 border-secondary transition duration-300 hover:shadow-xl">
                <div class="flex items-center justify-between">
                    <h2 class="text-xl font-semibold text-gray-700 flex items-center">
                        <svg class="w-6 h-6 mr-2 text-secondary" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M14 10l-2 1m0 0l-2-1m2 1v5M20 20H4a2 2 0 01-2-2V8a2 2 0 012-2h4l2-2h4l2 2h4a2 2 0 012 2v10a2 2 0 01-2 2z"></path></svg>
                        Kelembapan
                    </h2>
                    <span id="kelembapan-value" class="text-4xl font-bold text-secondary animate-pulse">-- %</span>
                </div>
            </div>
        </section>

        <!-- Control Card Section -->
        <section class="bg-white p-6 rounded-xl shadow-2xl">
            <h2 class="text-2xl font-bold text-gray-800 mb-4 border-b pb-2">Kontrol Relay</h2>
            <div class="grid grid-cols-1 md:grid-cols-2 gap-6">

                <!-- Relay 1 Control -->
                <div class="p-4 border rounded-lg flex items-center justify-between">
                    <span class="text-lg font-medium text-gray-700">Relay 1</span>
                    <button id="relay1-btn" onclick="toggleRelay('relay1')"
                        class="px-6 py-3 rounded-full text-white font-semibold shadow-md transition transform hover:scale-105 focus:outline-none focus:ring-4 focus:ring-offset-2 w-32"
                        data-status="false">
                        <span id="relay1-status-text">Memuat...</span>
                    </button>
                </div>

                <!-- Relay 2 Control -->
                <div class="p-4 border rounded-lg flex items-center justify-between">
                    <span class="text-lg font-medium text-gray-700">Relay 2</span>
                    <button id="relay2-btn" onclick="toggleRelay('relay2')"
                        class="px-6 py-3 rounded-full text-white font-semibold shadow-md transition transform hover:scale-105 focus:outline-none focus:ring-4 focus:ring-offset-2 w-32"
                        data-status="false">
                        <span id="relay2-status-text">Memuat...</span>
                    </button>
                </div>
            </div>
        </section>

        <!-- Authentication and Debug Info -->
        <footer class="text-center pt-4 text-sm text-gray-500">
            <p>Status Koneksi MQTT: <span id="auth-status" class="font-medium text-yellow-600">Menghubungkan...</span></p>
            <p>Broker: **broker.hivemq.com** (WSS Port: 8884) | Topik Kontrol/Data: **tkj3_topic/control & tkj3_topic/data**</p>
            <p>ID Klien: <span id="user-id" class="text-xs break-all">N/A</span></p>
            <p class="mt-2">Pastikan perangkat Anda (mis. ESP32/ESP8266) terhubung ke topik yang sama.</p>
        </footer>

    </main>

    <!-- Paho MQTT Library -->
    <script src="https://unpkg.com/paho-mqtt@1.1.0/paho-mqtt.js"></script> 

    <script>
        // --- Konfigurasi MQTT (Menggunakan WSS Port 8884) ---
        const MQTT_HOST = "broker.hivemq.com";
        const MQTT_PORT = 8884; // TLS Websocket Port
        const MQTT_PATH = "/mqtt"; // Path default untuk koneksi WSS
        // TOPIC SUDAH DIUBAH KE tkj3_topic
        const MQTT_TOPIC_DATA = "tkj3_topic/data"; // Topik untuk menerima data sensor dan status
        const MQTT_TOPIC_CONTROL = "tkj3_topic/control"; // Topik untuk mengirim perintah kontrol
        // ID Klien unik untuk menghindari konflik sesi
        const CLIENT_ID = 'web_' + Math.random().toString(16).substr(2, 8); 

        // --- Globals ---
        let client;

        // Mendapatkan elemen UI
        const authStatusEl = document.getElementById('auth-status');
        const userIdEl = document.getElementById('user-id');
        const suhuEl = document.getElementById('suhu-value');
        const kelembapanEl = document.getElementById('kelembapan-value');

        // Memperbarui ID Klien di UI
        userIdEl.textContent = CLIENT_ID;

        // 1. Fungsi untuk memperbarui UI tombol relay
        function updateRelayButton(relayKey, status) {
            const btn = document.getElementById(`${relayKey}-btn`);
            const textSpan = document.getElementById(`${relayKey}-status-text`);

            if (!btn || !textSpan) return;

            // Hapus kelas status yang ada
            btn.classList.remove('bg-secondary', 'bg-danger', 'ring-secondary', 'ring-danger', 'cursor-not-allowed', 'opacity-50', 'bg-gray-400');

            // Cek status: bisa berupa boolean (true/false) atau integer (1/0)
            const isOn = (status === 1 || status === true || status === 'ON');

            if (isOn) {
                // Relay ON
                btn.classList.add('bg-secondary', 'ring-secondary');
                textSpan.textContent = 'ON';
                btn.dataset.status = 'true';
                btn.disabled = false;
            } else if (status === 0 || status === false || status === 'OFF') {
                // Relay OFF
                btn.classList.add('bg-danger', 'ring-danger');
                textSpan.textContent = 'OFF';
                btn.dataset.status = 'false';
                btn.disabled = false;
            } else {
                // Loading/Error state
                btn.classList.add('bg-gray-400', 'cursor-not-allowed', 'opacity-50');
                textSpan.textContent = 'Memuat...';
                btn.disabled = true;
                btn.dataset.status = 'loading';
            }
        }

        // 2. Fungsi inisialisasi koneksi MQTT
        function initializeMQTT() {
            authStatusEl.textContent = 'Menghubungkan...';
            authStatusEl.classList.remove('text-secondary', 'text-danger');
            authStatusEl.classList.add('text-yellow-600');

            // Membuat instansi Paho Client dengan WSS Path
            client = new Paho.Client(MQTT_HOST, MQTT_PORT, MQTT_PATH, CLIENT_ID);

            // Menetapkan handler event
            client.onConnectionLost = onConnectionLost;
            client.onMessageArrived = onMessageArrived;

            // Melakukan koneksi
            client.connect({
                onSuccess: onConnectSuccess,
                onFailure: onConnectFailure,
                cleanSession: true,
                useSSL: true, // Menggunakan WSS (Secure WebSocket)
            });

            console.log(`Mencoba koneksi ke wss://${MQTT_HOST}:${MQTT_PORT}${MQTT_PATH}`);
        }

        // 3. Handler saat koneksi berhasil
        function onConnectSuccess() {
            console.log("MQTT Terhubung");
            authStatusEl.textContent = 'Tersambung';
            authStatusEl.classList.remove('text-yellow-600', 'text-danger');
            authStatusEl.classList.add('text-secondary');

            // Langganan (Subscribe) ke topik data
            client.subscribe(MQTT_TOPIC_DATA, { qos: 0 });
            console.log(`Berhasil subscribe ke topik: ${MQTT_TOPIC_DATA}`);
        }

        // 4. Handler saat koneksi gagal
        function onConnectFailure(responseObject) {
            console.error("Koneksi MQTT Gagal:", responseObject.errorMessage);
            authStatusEl.textContent = `Gagal: ${responseObject.errorMessage}`;
            authStatusEl.classList.remove('text-yellow-600', 'text-secondary');
            authStatusEl.classList.add('text-danger');
        }

        // 5. Handler saat koneksi terputus
        function onConnectionLost(responseObject) {
            if (responseObject.errorCode !== 0) {
                console.error("Koneksi MQTT Terputus:", responseObject.errorMessage);
                authStatusEl.textContent = 'Terputus';
                authStatusEl.classList.remove('text-secondary', 'text-yellow-600');
                authStatusEl.classList.add('text-danger');
                // Coba sambung ulang setelah 5 detik
                setTimeout(initializeMQTT, 5000); 
            }
        }

        // 6. Handler saat pesan diterima
        function onMessageArrived(message) {
            console.log(`Pesan diterima dari ${message.destinationName}: ${message.payloadString}`);

            if (message.destinationName === MQTT_TOPIC_DATA) {
                try {
                    // Pesan diharapkan dalam format JSON
                    const data = JSON.parse(message.payloadString);

                    // Update Suhu (Temperature)
                    const suhu = data.suhu !== undefined ? data.suhu : '--';
                    suhuEl.textContent = `${suhu} °C`;
                    suhuEl.classList.remove('animate-pulse'); 

                    // Update Kelembapan (Humidity)
                    const kelembapan = data.kelembapan !== undefined ? data.kelembapan : '--';
                    kelembapanEl.textContent = `${kelembapan} %`;
                    kelembapanEl.classList.remove('animate-pulse'); 

                    // Update Relay UI berdasarkan status yang diterima dari perangkat
                    updateRelayButton('relay1', data.relay1);
                    updateRelayButton('relay2', data.relay2);

                } catch (e) {
                    console.error("Error parsing JSON message:", e);
                }
            }
        }

        // 7. Fungsi Kontrol Relay (Tersedia secara global)
        window.toggleRelay = (relayKey) => {
            if (!client || !client.isConnected()) {
                console.warn("MQTT belum terhubung atau siap.");
                // NOTE: Menggunakan console.warn sebagai pengganti alert()
                console.warn("Koneksi MQTT belum siap. Coba lagi.");
                return;
            }

            const btn = document.getElementById(`${relayKey}-btn`);
            // Status saat ini di HTML adalah string 'true'/'false'
            const currentStatus = btn.dataset.status === 'true'; 
            // Nilai yang dikirimkan ke perangkat: 1 untuk ON, 0 untuk OFF
            const newStatusValue = currentStatus ? 0 : 1; 

            // Perintah yang akan dikirim (JSON)
            const controlPayload = JSON.stringify({
                [relayKey]: newStatusValue 
            });

            // Persiapan pesan
            const message = new Paho.Message(controlPayload);
            message.destinationName = MQTT_TOPIC_CONTROL;
            message.qos = 0;

            // Kirim pesan
            try {
                // Set tombol ke status 'memuat' sementara menunggu balasan/perubahan
                updateRelayButton(relayKey, 'loading'); 
                client.send(message);
                console.log(`Perintah dikirim ke ${MQTT_TOPIC_CONTROL}: ${controlPayload}`);
            } catch (error) {
                console.error("Gagal mengirim pesan MQTT:", error);
                // NOTE: Menggunakan console.error sebagai pengganti alert()
                console.error(`Gagal mengirim perintah: ${error.message}`);
                // Kembalikan tombol ke status sebelumnya jika gagal
                updateRelayButton(relayKey, currentStatus); 
            }
        };

        // Inisialisasi saat halaman dimuat
        initializeMQTT();
    </script>

</body>
</html>

Berikut Config dan Coding Pada Nodemcu Nya

#include <ESP8266WiFi.h>
#include <PubSubClient.h>         // Library untuk MQTT
#include <ArduinoJson.h>          // Library untuk memproses data JSON (control command)
#include <DHT.h>           
#include <LiquidCrystal_I2C.h>    

// --- KONFIGURASI WIFI & MQTT ---
#define WIFI_SSID "LINKZWIRE"       // Ganti dengan nama WiFi anda
#define WIFI_PASSWORD "qwerty123" // Ganti dengan password WiFi anda

// Konfigurasi Broker HiveMQ Public
#define MQTT_SERVER "broker.hivemq.com"
#define MQTT_PORT 1883                    // Port TCP standar untuk perangkat keras
#define MQTT_CLIENT_ID "NodeMCU_TKJ3_ID"  // ID Klien unik untuk NodeMCU ini (DIUBAH ke TKJ3)

// Topik Komunikasi (DIUBAH ke tkj3_topic)
#define MQTT_TOPIC_DATA "tkj3_topic/data"     // NodeMCU PUBLISH data sensor dan status relay ke sini
#define MQTT_TOPIC_CONTROL "tkj3_topic/control" // NodeMCU SUBSCRIBE untuk menerima perintah dari web

// --- KONFIGURASI PERANGKAT KERAS ---
#define DHT_PIN D7
#define DHT_TYPE DHT11

#define RELAY1_PIN D5
#define RELAY2_PIN D6

LiquidCrystal_I2C lcd(0x27, 16, 2); 
DHT dht(DHT_PIN, DHT_TYPE);

// --- VARIABEL GLOBAL ---
WiFiClient espClient;
PubSubClient client(espClient); // Inisialisasi MQTT client

bool relay1Status = false; // true = ON (LOW), false = OFF (HIGH)
bool relay2Status = false; // true = ON (LOW), false = OFF (HIGH)

unsigned long lastSensorRead = 0;
const long sensorInterval = 5000; // Baca dan kirim data setiap 5 detik

// --- PROTOTYPE FUNGSI ---
void connectWiFi();
void reconnectMQTT();
void callback(char* topic, byte* payload, unsigned int length);
void controlRelay();
void sendSensorData(float t, float h);

void setup() {
  Serial.begin(115200);

  // Inisialisasi Relay (Aktif LOW)
  pinMode(RELAY1_PIN, OUTPUT);
  pinMode(RELAY2_PIN, OUTPUT);
  digitalWrite(RELAY1_PIN, HIGH); 
  digitalWrite(RELAY2_PIN, HIGH);

  dht.begin();

  // Inisialisasi LCD
  lcd.init();
  lcd.backlight();
  lcd.print("Memulai MQTT...");

  connectWiFi();

  // Konfigurasi MQTT Server dan Callback
  client.setServer(MQTT_SERVER, MQTT_PORT);
  client.setCallback(callback);
}

// ----------------------------------------------------------------
// FUNGSI UTAMA
// ----------------------------------------------------------------

void connectWiFi() {
  lcd.clear();
  lcd.print("Menghubungkan WiFi");
  WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
  Serial.print("Menghubungkan ke WiFi: ");
  Serial.println(WIFI_SSID);

  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }

  Serial.println("\nWiFi Terhubung.");
  Serial.print("IP Address: ");
  Serial.println(WiFi.localIP());

  lcd.clear();
  lcd.print("WiFi Terhubung!");
  lcd.setCursor(0, 1);
  lcd.print(WiFi.localIP());
  delay(2000);
}

// Menangani pesan MQTT yang masuk (Perintah Kontrol)
void callback(char* topic, byte* payload, unsigned int length) {
  Serial.print("Pesan diterima di [");
  Serial.print(topic);
  Serial.print("]: ");

  // Buat string dari payload
  String message = "";
  for (int i = 0; i < length; i++) {
    message += (char)payload[i];
  }
  Serial.println(message);

  if (String(topic) == MQTT_TOPIC_CONTROL) {
    // Gunakan ArduinoJson untuk parsing
    StaticJsonDocument<100> doc;
    DeserializationError error = deserializeJson(doc, message);

    if (error) {
      Serial.print("deserializeJson() failed: ");
      Serial.println(error.c_str());
      return;
    }

    // Cek dan update status Relay 1
    if (doc.containsKey("relay1")) {
      // 1=ON, 0=OFF
      relay1Status = doc["relay1"].as<int>() == 1;
      Serial.print("Relay 1 diatur: ");
      Serial.println(relay1Status ? "ON" : "OFF");
    }

    // Cek dan update status Relay 2
    if (doc.containsKey("relay2")) {
      // 1=ON, 0=OFF
      relay2Status = doc["relay2"].as<int>() == 1;
      Serial.print("Relay 2 diatur: ");
      Serial.println(relay2Status ? "ON" : "OFF");
    }

    // Terapkan perubahan status ke pin
    controlRelay();
  }
}

// Mengatur kondisi pin Relay (Aktif LOW)
void controlRelay() {
  // Jika relay1Status TRUE (ON), pin LOW. Jika FALSE (OFF), pin HIGH.
  digitalWrite(RELAY1_PIN, relay1Status ? LOW : HIGH);
  digitalWrite(RELAY2_PIN, relay2Status ? LOW : HIGH);
}

// Melakukan koneksi ulang ke broker MQTT
void reconnectMQTT() {
  // Loop hingga terhubung
  while (!client.connected()) {
    Serial.print("Mencoba koneksi MQTT...");
    lcd.clear();
    lcd.print("Menghubungkan MQTT");

    // Mencoba koneksi dengan ID Klien unik
    if (client.connect(MQTT_CLIENT_ID)) { 
      Serial.println("terhubung");
      lcd.clear();
      lcd.print("MQTT Terhubung!");
      lcd.setCursor(0, 1);
      lcd.print(MQTT_TOPIC_CONTROL);

      // Setelah terhubung, langganan ke topik kontrol
      client.subscribe(MQTT_TOPIC_CONTROL);
      Serial.print("Subscribe ke topik: ");
      Serial.println(MQTT_TOPIC_CONTROL);

      // Kirim status awal relay setelah terhubung
      sendSensorData(dht.readTemperature(), dht.readHumidity());

    } else {
      Serial.print("gagal, rc=");
      Serial.print(client.state());
      Serial.println(" coba lagi dalam 5 detik");
      // Tunggu 5 detik sebelum mencoba lagi
      delay(5000);
    }
  }
}

// Mengirim data sensor dan status relay ke dashboard web
void sendSensorData(float t, float h) {
  // Membuat dokumen JSON untuk dikirim
  StaticJsonDocument<200> doc;

  doc["suhu"] = t;
  doc["kelembapan"] = h;
  // Kirim status relay sebagai 1 atau 0 (sesuai format JSON yang diharapkan web)
  doc["relay1"] = relay1Status ? 1 : 0; 
  doc["relay2"] = relay2Status ? 1 : 0;

  // Serialisasi JSON ke string
  char jsonBuffer[200];
  serializeJson(doc, jsonBuffer);

  // Publikasikan ke topik data
  if (client.publish(MQTT_TOPIC_DATA, jsonBuffer)) {
    Serial.print("Data terkirim ke ");
    Serial.print(MQTT_TOPIC_DATA);
    Serial.print(": ");
    Serial.println(jsonBuffer);
  } else {
    Serial.println("Gagal publikasi MQTT.");
  }

  // Update LCD
  lcd.clear();
  lcd.print("Suhu: ");
  lcd.print(t, 1);
  lcd.print("C");
  lcd.setCursor(0, 1);
  lcd.print("Kelembapan: ");
  lcd.print(h, 1);
  lcd.print("%");
}

void loop() {
  // Pastikan koneksi WiFi dan MQTT tetap terhubung
  if (WiFi.status() != WL_CONNECTED) {
    connectWiFi();
  }

  if (!client.connected()) {
    reconnectMQTT();
  }

  // Mempertahankan koneksi MQTT dan memproses pesan masuk
  client.loop(); 

  unsigned long currentTime = millis();

  // Membaca sensor dan mengirim data secara berkala
  if (currentTime - lastSensorRead >= sensorInterval) {
    float h = dht.readHumidity();
    float t = dht.readTemperature();

    if (isnan(h) || isnan(t)) {
      Serial.println("Gagal membaca dari sensor DHT!");
    } else {
      sendSensorData(t, h);
    }
    lastSensorRead = currentTime;
  }

  delay(10);
}

Rubah Credensial dan Wifi Mengunakan Data Masing Masing


메타데이터
post_id
5575d2ca2e49
slug
mqtt-relay-control-hivemq-5575d2ca2e49
url
https://medium.com/@rvaldi26/mqtt-relay-control-hivemq-5575d2ca2e49
canonical_url
https://medium.com/@rvaldi26/mqtt-relay-control-hivemq-5575d2ca2e49
author_url
https://medium.com/@rvaldi26
status
ok
fetched_at
2026-06-27 10:07:59