Webpage using Paho JS to publish to subscriber

Hi All
Sorry to say I have been struggling to make a webpage connect to my HiveMQ server using Paho JS. The examples all don’t work for various reasons that I just don’t understand.
I’m trying to make the webpage “device agnostic”, so use the web version of Paho.

My HTML is:

    <!-- import jquery library -->
    <script src="https://code.jquery.com/jquery-3.1.0.min.js" type="text/javascript"></script>

    <!-- import paho MQTT library -->
    <script src="https://cdnjs.cloudflare.com/ajax/libs/paho-mqtt/1.0.1/mqttws31.min.js" type="text/javascript"></script>

My (limited) understanding of this is that it downloads the javascript engine from the web address. This seems to work as the javascript runs.

My javascript for connection is:

// Generate a new random MQTT client id on each page load
var MQTT_CLIENT_ID = "iot_web_"+Math.floor((1 + Math.random()) * 0x10000000000).toString(16);

// Create a MQTT client instance
//var MQTT_CLIENT = new Paho.MQTT.Client("iot.eclipse.com", 80, "/ws", MQTT_CLIENT_ID);
//var MQTT_CLIENT = new Paho.MQTT.Client("mqtt-dashboard.com", 8884, "/ws", MQTT_CLIENT_ID);
var MQTT_CLIENT = new Paho.MQTT.Client("<my alpha code>.s1.eu.hivemq.cloud", 8884, MQTT_CLIENT_ID);

// Tell the client instance to connect to the MQTT broker
MQTT_CLIENT.connect({
	userName : "<my username for HiveMQ>",
	password : "<my password for HiveMQ>",
	useSSL   : true,
	onSuccess: function(){
		console.log('Connected');
		client.subscribe("SquashScore");            // the topic I want to publish to
		message = new Paho.Message("Hello");
		client.send(message);
	}
});

console.log("attempting to connect");

// This is the function which handles button clicks
function myButtonWasClicked() {
  var mqttMessage = new Paho.MQTT.Message("Score:12-13");
  MQTT_CLIENT.send(mqttMessage);
}

The browser log shows “Attempting to connect” and then “Connected” which seems good. However, an error happens in the browser window when I try to publish by clicking the button.
The browser has a message of

Uncaught Error: AMQJS0011E Invalid state not connected.
    at k.send (mqttws31.min.js:34:175)
    at I.send (mqttws31.min.js:67:296)
    at myButtonWasClicked (app.js:35:15)
    at HTMLButtonElement.onclick (index.html:14:46)

which seems to mean that the page was never connected in the first place.

This should be easy - to me it looks like all the documentation is either out of date or just wrong.

Anybody got any help for what’s going wrong? Happy to provide other details if needed.
Thanks in advance.
Max

Certainly! Here’s the revised response:


Hello @village_idiot,

Welcome to the HiveMQ Community Forum. We’re delighted to see your interest in MQTT and HiveMQ Cloud.

You are correct; the error you encountered is due to the client not being connected.

To resolve this issue, please use the following code to establish a successful connection to HiveMQ Cloud: For more details, please refer to this post.

var MQTT_CLIENT = new Paho.MQTT.Client("wss://[hivemq cloud cluster url]:8884/mqtt", "your client id");

We hope this helps. If you have any further questions, please feel free to ask.

Best regards,
Sheetal from HiveMQ Team

Hi Sheetal
Thanks for the reply. I have already seen that post and tried the proposed solution with no success.
I have just tried it again and the console just says “attempting to connect”. Obviously, clicking the button produces the same error code.

My network is the normal home-type wired Ethernet that seems to be able to do everything else.

The concept I’m trying to achieve must have been done already but no-one has posted code which actually works.

If there is any more information I can provide, please let me know but this is so frustrating as you guys seem to think it works. I haven’t managed to get TSL working at all.

Thanks in advance.
Max

Hi
Just had a thought: is there a certificate issue here? Do I need certificates to enable this connection?

Thanks
Max

Hi @village_idiot

Please try my script and see if it works for you. Replace “broker.hivemq.com” with your own broker, and replace “MY_USER” and “MY_PASSWORD” with your own credentials.

index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>HiveMQ Cloud Example</title>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/paho-mqtt/1.0.1/mqttws31.js" type="text/javascript"></script>
</head>
<body>

<!-- Display received message -->
<div id="message"></div>

<script>
    // Create a client instance
    let client = null;
    let connected = false;
    let connectionAttempt = 1;

    logMessage(`INFO    Starting Eclipse Paho JavaScript Client.`);

    const clientId = "MY_UNIQUE_CLIENT";

    const hostname = "broker.hivemq.com";
    const port = 8884;
    const path = "/mqtt";
    const user = "MY_USER";
    const pass = "MY_PASSWORD";

    const keepAlive = 60;
    const timeout = 30;
    const tls = true;
    const cleanSession = false;
    const lastWillTopic = `Test/LastWills/${clientId}`;
    const lastWillQos = 0;
    const lastWillRetain = true;
    const lastWillMessageVal = `Last will of ${clientId}`;

    client = new Paho.MQTT.Client(hostname, Number(port), path, clientId);
    logMessage(`INFO    Connecting to Server: [Host: ${hostname}, Port: ${port}, Path: ${client.path}, ID: ${clientId}]`);

    // set callback handlers
    client.onConnectionLost = onConnectionLost;
    client.onMessageArrived = onMessageArrived;
    client.onConnected = onConnected;

    let lastWillMessage = new Paho.MQTT.Message(lastWillMessageVal);
    lastWillMessage.destinationName = lastWillTopic;
    lastWillMessage.qos = lastWillQos;
    lastWillMessage.retained = lastWillRetain;

    const connectOptions = {
        invocationContext: {
            host: hostname,
            port: port,
            path: client.path,
            clientId: clientId
        },
        timeout: timeout,
        keepAliveInterval: keepAlive,
        cleanSession: cleanSession,
        useSSL: tls,
        onSuccess: onConnect,
        onFailure: onFail,
        userName: user,
        password: pass,
        willMessage: lastWillMessage
    };
    // connect the client
    client.connect(connectOptions);


    function onConnect() {
        // Once a connection has been made, make a subscription and send a message.
        logMessage("INFO    onConnect");

        const subscribeTopicFilter = "Test/#";
        const qos = 1;
        logMessage(`INFO    Subscribing to: [Topic: ${subscribeTopicFilter}, QoS: ${qos}]`);
        client.subscribe(subscribeTopicFilter, { qos: Number(qos) });

        for (let i = 1; i <= 3; i++ ){
            const messageText = `Hello ${i}`;
            const publishTopic = `Test/${i}`
            const retain = false;
            logMessage(`INFO    Publishing Message: [Topic: ${publishTopic}, Payload: ${messageText}, QoS: ${qos}, Retain: ${retain}]`);
            let message = new Paho.MQTT.Message(messageText);
            message.destinationName = publishTopic;
            message.qos = Number(qos);
            message.retained = retain;
            client.send(message);
        }

        // Set a timer for 10 seconds to disconnect
        setTimeout(() => {
            logMessage("INFO    Disconnecting...");
            client.disconnect();
        }, 5000); // 5000 milliseconds = 5 seconds
    }
    function onConnected(reconnect, uri) {
        // Once a connection has been made, make a subscription and send a message.
        logMessage(`INFO    Client Has now connected: [Reconnected: ${reconnect}, URI: ${uri}]`);
        connected = true;
    }
    function onFail(context) {
        logMessage(`ERROR   Failed to connect. [Error Message: ${context.errorMessage}]`);
        connected = false;
    }

    function onConnectionLost(responseObject) {
        logMessage("INFO    onConnectionLost")
        if (responseObject.errorCode !== 0)
            logMessage(`ERROR    onConnectionLost: ${responseObject.errorMessage}, Code: ${responseObject.errorCode}`);

        // Set a timer for 2 seconds to reconnect
        const reconnectSeconds = 2 * connectionAttempt
        connectionAttempt += 1
        setTimeout(() => {
            logMessage(`INFO    Reconnecting in ${reconnectSeconds}...`);
            if (connectOptions.hasOwnProperty("mqttVersionExplicit")){
                delete connectOptions.mqttVersionExplicit
            }
            client.connect(connectOptions)
        }, reconnectSeconds*1000); // 1000 milliseconds = 1 second
    }
    function onMessageArrived(message) {
        logMessage(`Received message: ${safeTagsRegex(message.payloadString)}, Topic: ${message.destinationName}, QoS: ${message.qos}, Retained: ${message.retained}`)
    }
    // Just in case someone sends html
    function safeTagsRegex(str) {
        return str.replace(/&/g, "&amp;").replace(/</g, "&lt;").
        replace(/>/g, "&gt;");
    }

    function logMessage(message){
        document.getElementById("message").innerHTML += `${message}<br>`;
        console.log(message);
    }
</script>
</body>
</html>

Should you encounter any errors, please let us know.

Regards,
Dasha from the HiveMQ Team

Hello @village_idiot,

Thank you for your response. I am uploading a full example of the code for your reference. Please use the user cluster URL and credentials. Make sure you have the correct permissions set for the client so that the client can publish and subscribe.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>MQTT Web Client</title>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/paho-mqtt/1.0.1/mqttws31.min.js"></script>
    <script>
        
        // Create a MQTT client instance
        var MQTT_CLIENT = new Paho.MQTT.Client("wss://xxxxxxxxx.s1.eu.hivemq.cloud:8884/mqtt", "test1234");
        

        // Tell the client instance to connect to the MQTT broker
        MQTT_CLIENT.connect({
            userName: "xxxx",
            password: "xxxx",
            useSSL: true,
            onSuccess: function() {
                console.log('Connected');
                // Correctly subscribe to the topic
                MQTT_CLIENT.subscribe("#"); 
                // Create a message object and specify the topic
                var message = new Paho.MQTT.Message("Hello");
                message.destinationName = "World";
                MQTT_CLIENT.send(message);
            },
            onFailure: function(error) {
                console.log('Connection failed: ' + error.errorMessage);
            }
        });

        console.log("attempting to connect");

        // This is the function which handles button clicks
        function myButtonWasClicked() {
            var mqttMessage = new Paho.MQTT.Message("testingmessage");
            mqttMessage.destinationName = "World"; // Specify the topic
            MQTT_CLIENT.send(mqttMessage);
        }
    </script>
</head>
<body>
    <h1>MQTT Web Client</h1>
    <button onclick="myButtonWasClicked()">Send Score</button>
</body>
</html>


Please try and let us know if this helps.

Regards,
Sheetal from HiveMQ Team.

Hi Sheetal/Dasha

Thanks both of you for your help.

It looks like I was at fault for this. I am not sure where I went wrong but I couldn’t see messages appearing where I thought they should. Your code is working for me now and has helped immensely.

Thanks again.

If you have any help getting an Arduino-type board (specifically MatrixPortal M4 with ESP32S3 onboard) connected to port 8883 using TLS I would appreciate it.

Cheers
Max