MQTT Relay Control HiveMq
HiveMQ Public
MQTT Relay Control HiveMq
HiveMQ Public

Dashboard Menggunakan HiveMq Mqtt

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