parminder singh

Jun 02, 2025 • 6 min read

Connecting ESP8266/ESP32 with Node.js Using HiveMQ MQTT Broker

Connecting ESP8266/ESP32 with Node.js Using HiveMQ MQTT Broker

Introduction

In this article, I'll show you how to connect your ESP8266 or ESP32 microcontroller to a Node.js application using HiveMQ's public MQTT broker. HiveMQ offers a reliable cloud-based MQTT broker that's perfect for IoT projects, providing better reliability than public test brokers for your applications.

Why HiveMQ?

HiveMQ provides:

  • A free public broker for testing

  • High reliability and uptime

  • WebSocket support for browser-based clients

  • Scalability for when your project grows

What You'll Need

  • ESP8266 or ESP32 development board

  • Arduino IDE (for programming the ESP)

  • Node.js installed on your computer

  • Basic knowledge of JavaScript and Arduino programming

Step 1: HiveMQ Connection Details

For the HiveMQ public broker, use these connection details:

  • Broker URL: broker.hivemq.com

  • Port: 1883 (MQTT) or 8883 (MQTT over TLS)

  • WebSocket: 8000 (ws) or 8884 (wss)

No authentication is required for the public broker, but consider this for production:

const client = mqtt.connect('mqtt://broker.hivemq.com:1883', {
  clientId: 'your-unique-client-id',
  // username: 'your-username', // For authenticated brokers
  // password: 'your-password'  // For authenticated brokers
});

Step 2: Programming the ESP8266/ESP32

Install the PubSubClient library in your Arduino IDE if you haven't already.

Here's the updated ESP code for HiveMQ:

#include <ESP8266WiFi.h>  // Use #include <WiFi.h> for ESP32
#include <PubSubClient.h>

const char* ssid = "your_wifi_ssid";
const char* password = "your_wifi_password";
const char* mqtt_server = "broker.hivemq.com";

WiFiClient espClient;
PubSubClient client(espClient);

// Generate a unique client ID
String clientId = "ESP8266Client-";
unsigned long lastMsg = 0;
#define MSG_BUFFER_SIZE (50)
char msg[MSG_BUFFER_SIZE];

void setup_wifi() {
  delay(10);
  Serial.println();
  Serial.print("Connecting to ");
  Serial.println(ssid);

  WiFi.begin(ssid, password);

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

  randomSeed(micros());

  Serial.println("");
  Serial.println("WiFi connected");
  Serial.println("IP address: ");
  Serial.println(WiFi.localIP());
}

void callback(char* topic, byte* payload, unsigned int length) {
  Serial.print("Message arrived [");
  Serial.print(topic);
  Serial.print("] ");
  
  String message;
  for (int i = 0; i < length; i++) {
    message += (char)payload[i];
  }
  Serial.println(message);

  // Handle different commands
  if (message == "LED_ON") {
    digitalWrite(LED_BUILTIN, LOW);
    client.publish("esp8266/status", "LED is ON");
  } else if (message == "LED_OFF") {
    digitalWrite(LED_BUILTIN, HIGH);
    client.publish("esp8266/status", "LED is OFF");
  }
}

void reconnect() {
  while (!client.connected()) {
    Serial.print("Attempting MQTT connection...");
    
    // Create a random client ID
    clientId += String(random(0xffff), HEX);
    
    if (client.connect(clientId.c_str())) {
      Serial.println("connected");
      client.subscribe("esp8266/commands"); // Subscribe to commands topic
    } else {
      Serial.print("failed, rc=");
      Serial.print(client.state());
      Serial.println(" try again in 5 seconds");
      delay(5000);
    }
  }
}

void setup() {
  pinMode(LED_BUILTIN, OUTPUT);
  Serial.begin(115200);
  setup_wifi();
  client.setServer(mqtt_server, 1883);
  client.setCallback(callback);
}

void loop() {
  if (!client.connected()) {
    reconnect();
  }
  client.loop();

  // Publish a heartbeat message every 10 seconds
  unsigned long now = millis();
  if (now - lastMsg > 10000) {
    lastMsg = now;
    snprintf(msg, MSG_BUFFER_SIZE, "heartbeat #%ld", lastMsg);
    Serial.print("Publish message: ");
    Serial.println(msg);
    client.publish("esp8266/heartbeat", msg);
  }
}

Step 3: Node.js Client for HiveMQ

Create a new Node.js project:

npm init -y
npm install mqtt express

Here's an enhanced Node.js server that connects to HiveMQ:

const mqtt = require('mqtt');
const express = require('express');
const app = express();
const path = require('path');

// Connect to HiveMQ broker
const client = mqtt.connect('mqtt://broker.hivemq.com:1883', {
  clientId: 'nodejs-client-' + Math.random().toString(16).substr(2, 8),
  clean: true,
  reconnectPeriod: 1000
});

// Middleware
app.use(express.json());
app.use(express.static(path.join(__dirname, 'public')));

// MQTT Connection handlers
client.on('connect', () => {
  console.log('Connected to HiveMQ broker');
  
  // Subscribe to ESP topics
  client.subscribe('esp8266/status', (err) => {
    if (!err) console.log('Subscribed to esp8266/status');
  });
  
  client.subscribe('esp8266/heartbeat', (err) => {
    if (!err) console.log('Subscribed to esp8266/heartbeat');
  });
});

// Handle incoming messages
client.on('message', (topic, message) => {
  console.log(`Received message on ${topic}: ${message.toString()}`);
  
  // You could store this in a database or process it further
});

// API endpoint to send commands
app.post('/api/command', (req, res) => {
  const { command } = req.body;
  
  if (!command) {
    return res.status(400).json({ error: 'Command is required' });
  }

  client.publish('esp8266/commands', command, { qos: 1 }, (err) => {
    if (err) {
      return res.status(500).json({ error: 'Failed to send command' });
    }
    res.json({ status: 'Command sent', command });
  });
});

// Web interface
app.get('/', (req, res) => {
  res.sendFile(path.join(__dirname, 'public', 'index.html'));
});

// Start server
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
  console.log(`Server running on http://localhost:${PORT}`);
});

// Error handling
client.on('error', (err) => {
  console.error('MQTT error:', err);
});

process.on('SIGINT', () => {
  client.end();
  process.exit();
});

Step 4: Enhanced Web Interface

Create a public/index.html file with a more interactive UI:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>ESP Control via HiveMQ</title>
    <style>
        body {
            font-family: Arial, sans-serif;
            max-width: 800px;
            margin: 0 auto;
            padding: 20px;
        }
        .control-panel {
            display: flex;
            gap: 10px;
            margin-bottom: 20px;
        }
        button {
            padding: 12px 24px;
            font-size: 16px;
            cursor: pointer;
            border: none;
            border-radius: 4px;
        }
        #led-on {
            background-color: #4CAF50;
            color: white;
        }
        #led-off {
            background-color: #f44336;
            color: white;
        }
        #toggle-led {
            background-color: #2196F3;
            color: white;
        }
        #status {
            padding: 15px;
            background-color: #f8f8f8;
            border-radius: 4px;
            margin-top: 20px;
        }
        .logs {
            margin-top: 30px;
            border: 1px solid #ddd;
            padding: 10px;
            height: 200px;
            overflow-y: auto;
            background-color: #f9f9f9;
        }
    </style>
</head>
<body>
    <h1>ESP8266/ESP32 Control Panel</h1>
    <p>Connected via HiveMQ MQTT Broker</p>
    
    <div class="control-panel">
        <button id="led-on">Turn LED ON</button>
        <button id="led-off">Turn LED OFF</button>
        <button id="toggle-led">Toggle LED</button>
    </div>
    
    <div id="status">Status: Waiting for updates...</div>
    
    <h2>Event Log</h2>
    <div class="logs" id="event-log"></div>

    <script>
        // DOM elements
        const ledOnBtn = document.getElementById('led-on');
        const ledOffBtn = document.getElementById('led-off');
        const toggleBtn = document.getElementById('toggle-led');
        const statusDiv = document.getElementById('status');
        const eventLog = document.getElementById('event-log');
        
        // Add timestamp to logs
        function getTimestamp() {
            return new Date().toLocaleTimeString();
        }
        
        // Add message to event log
        function addLog(message) {
            const logEntry = document.createElement('div');
            logEntry.textContent = `[${getTimestamp()}] ${message}`;
            eventLog.appendChild(logEntry);
            eventLog.scrollTop = eventLog.scrollHeight;
        }
        
        // Send command to ESP
        async function sendCommand(command) {
            try {
                const response = await fetch('/api/command', {
                    method: 'POST',
                    headers: {
                        'Content-Type': 'application/json',
                    },
                    body: JSON.stringify({ command })
                });
                
                const data = await response.json();
                addLog(`Command sent: ${data.command}`);
            } catch (err) {
                addLog(`Error: ${err.message}`);
                console.error('Error:', err);
            }
        }
        
        // Button event listeners
        ledOnBtn.addEventListener('click', () => sendCommand('LED_ON'));
        ledOffBtn.addEventListener('click', () => sendCommand('LED_OFF'));
        toggleBtn.addEventListener('click', () => sendCommand('TOGGLE_LED'));
        
        // Connect to MQTT via WebSocket for real-time updates
        // Note: This requires the server to support WebSocket proxying or CORS
        // Alternatively, you could use Server-Sent Events or polling
        const eventSource = new EventSource('/api/events');
        
        eventSource.onmessage = (event) => {
            const data = JSON.parse(event.data);
            if (data.topic === 'esp8266/status') {
                statusDiv.textContent = `Status: ${data.message}`;
                addLog(`Status update: ${data.message}`);
            } else if (data.topic === 'esp8266/heartbeat') {
                addLog(`Heartbeat: ${data.message}`);
            }
        };
        
        eventSource.onerror = (err) => {
            addLog('EventSource error:', err);
        };
    </script>
</body>
</html>

Step 5: Adding Server-Sent Events (SSE)

To make the web interface truly real-time without polling, add SSE support to your Node.js server:

// Add this near the top with other requires
const { EventEmitter } = require('events');
const mqttEvents = new EventEmitter();

// Modify the message handler to emit events
client.on('message', (topic, message) => {
  console.log(`Received message on ${topic}: ${message.toString()}`);
  mqttEvents.emit('mqtt_message', { topic, message: message.toString() });
});

// Add SSE endpoint
app.get('/api/events', (req, res) => {
  res.setHeader('Content-Type', 'text/event-stream');
  res.setHeader('Cache-Control', 'no-cache');
  res.setHeader('Connection', 'keep-alive');
  
  const sendEvent = (data) => {
    res.write(`data: ${JSON.stringify(data)}\n\n`);
  };
  
  mqttEvents.on('mqtt_message', sendEvent);
  
  req.on('close', () => {
    mqttEvents.off('mqtt_message', sendEvent);
  });
});

Security Considerations with HiveMQ

While the public HiveMQ broker is great for testing, remember:

  1. No authentication: Anyone can subscribe to your topics

  2. No encryption: Data is sent in clear text (use port 8883 for TLS)

  3. Unique topics: Use unique, complex topic names to avoid collisions

  4. Client IDs: Ensure your client IDs are unique

For production, consider:

  • Using HiveMQ Cloud with authentication

  • Implementing your own MQTT broker with proper security

  • Adding TLS encryption

  • Implementing application-level security

Next Steps

This implementation gives you a solid foundation. You could extend it by:

  1. Adding sensor data collection from the ESP

  2. Implementing a database to store historical data

  3. Creating user authentication for the web interface

  4. Building a mobile app that connects to the same MQTT topics

  5. Adding OTA (Over-The-Air) updates for your ESP

Conclusion

You've now created a complete system where:

  • Your ESP device connects to HiveMQ's public broker

  • A Node.js server also connects to the same broker

  • You have a web interface to control the ESP

  • Status updates are shown in real-time

This architecture is scalable and can be the foundation for more complex IoT applications. The use of HiveMQ ensures reliable message delivery, and the separation of components makes your system more maintainable

Join parminder on Peerlist!

Join amazing folks like parminder and thousands of other people in tech.

Create Profile

Join with parminder’s personal invite link.

1

12

0