樹莓派 ( Raspberry Pi OS / Raspbian / Ubuntu) 安裝 mosquitto 作為 MQTT Broker時,如果同時開啟 websocket 通訊,可以利用 mqttws31.js 以及簡易的 javascript 程式,即可利用直接瀏覽器完成 MQTT client 所有操作
安裝 mosquitto :
sudo apt-get update
sudo apt-get install mosquitto
開啟 websocket 功能:

sudo nano /etc/mosquitto/mosquitto.conf
檔案最後增加(此處以 MQTT TCP 1883,MQTT websocket 9001 為例)
port 1883
protocol mqtt
# Websockets
listener 9001
protocol websockets

重新啟動即可
sudo service mosquitto restart

 

線上展示 : http://www.icdt.com.tw/mqtt.html

範例程式:

<!doctype html>
<html xmlns='http://www.w3.org/1999/xhtml' xml:lang='en' lang='en'><head>
<title>MQTT Websocket</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/paho-mqtt/1.0.1/mqttws31.js" type="text/javascript"></script>
<script type="text/javascript" src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<style type='text/css'>
html,body{font-family:Verdana,sans-serif;font-size:15px;margin:0px; background-color: white; height: 100%; overflow:hidden;}
input{margin-top: 4px;margin-left: 4px;height:20px;text-align:center;font-size:13px;border-radius:2px;border: 1px outset #c5dbec}
button{margin-top: 4px;margin-left: 10px;height:22px;text-align:center;font-size:13px;border-radius:2px;border: 1px outset #c5dbec}
label{margin-top: 4px;margin-left: 20px;height:20px}
#container{background-color:#f8f8f8;width:100%;overflow:hidden;position:absolute;top:0px;bottom:0px;height:auto;padding:10px 10px;}
#messagearea{margin:10px 10px;overflow-y:auto;height:calc(100% - 160px);width:calc(100% - 60px);background-color:black;color:white;font-size:16px;padding:10px 10px 10px 10px;}
</style>
<script type="text/javascript">
var client;
var mqttError =[
"0 No Error",
"1 Connection Refused: Unacceptable protocol version",
"2 Connection Refused: Identifier rejected",
"3 Connection Refused: Server Unavailable",
"4 Connection Refused: Bad username or password",
"5 Connection Refused: Authorization error",
"6 Connection lost or bad",
"7 Timeout waiting for Length bytes",
"8 Timeout waiting for Payload",
"9 Timeout waiting for CONNACK",
"10 Timeout waiting for SUBACK",
"11 Timeout waiting for UNSUBACK",
"12 Timeout waiting for PINGRESP",
"13 Malformed Remaining Length",
"14 Problem with the underlying communication port",
"15 Address could not be parsed",
"16 Malformed received MQTT packet",
"17 Subscription failure",
"18 Payload decoding failure",
"19 Failed to compile a Decoder",
"20 The received MQTT packet type is not supported on this client"];

function makeid(length) {
var result = '';
var characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
var charactersLength = characters.length;
for ( var i = 0; i < length; i++ ) {
result += characters.charAt(Math.floor(Math.random() * charactersLength));
}
return result;
}

function doConnect() {
todo = $("#Connect").text();
if(todo=="Connect")
{
hostip = $("#hostip").val();
hostport = $("#hostport").val() *1;
client = new Paho.MQTT.Client(hostip, hostport, "wsc" + makeid(20));
client.onConnect = onConnect;
client.onMessageArrived = onMessageArrived;
client.onConnectionLost = onConnectionLost;
client.connect({onSuccess:onConnect});
}
else
{
client.disconnect();
appendmessage("System : Disonnect MQTT");
$("#Connect").text("Connect");
}
checkpublish();
}

function doSubscribe(subscribe="#") {
client.subscribe(subscribe);
}

function doPublish() {
topic = $("#topic").val();
payload=$("#payload").val();
if((topic.trim()!="") && (payload.trim()!=""))
{
message = new Paho.MQTT.Message(payload.trim());
message.destinationName = topic.trim();
client.send(message);
}
}

function onConnect() {
$("#Connect").text("Disonnect");
subscribe = $("#subscribe").val();
doSubscribe(subscribe);
checkpublish();
}

function onConnectionLost(responseObject) {
$("#Connect").text("Connect");
checkpublish();
if(responseObject.errorCode !== 0)
{
if (responseObject.errorCode < mqttError.length)
appendmessage("Error :" + mqttError[responseObject.errorCode]);
else
appendmessage("Error Code :" +responseObject.errorCode);
}
}

function now()
{
var dt = new Date();
return (dt.getMonth()+1).toString().padStart(2, '0') + "/" + dt.getDate().toString().padStart(2, '0') + " " +
dt.getHours().toString().padStart(2, '0') + ":" + dt.getMinutes().toString().padStart(2, '0') + ":" + dt.getSeconds().toString().padStart(2, '0') + "&nbsp;&nbsp;&nbsp;&nbsp;";
}

function appendmessage($data) {
$("#messagearea").append(now() + $data.replace(/(?:\r\n|\r|\n)/g, '<br>')+'<br>');
$("#messagearea").scrollTop( $("#messagearea").prop("scrollHeight"));
}
function onMessageArrived(message) {
appendmessage(message.destinationName + " : " + message.payloadString);
}

function checkpublish()
{
topic = $("#topic").val();
payload=$("#payload").val();
Connect=$("#Connect").text();
$bool = (topic.trim()=="") || (payload.trim()=="") || (Connect == "Connect");
$("#Publish").prop("disabled", $bool);
}

</script>
</head>

<body>
<div id="container">
<label style="margin-left: 4px;">Host : </label><input id="hostip" type="text" value="icdt.sytes.net" maxlength="64" style="width:160px"/>
<label>Port : </label><input type="number" id="hostport" name="port" min="1" max="65535" value="9001" style="width:80px">
<label>Subscribe : </label><input id="subscribe" type="text" value="/mqtt/#" maxlength="128" style="width:300px">
<button onClick="doConnect()" id="Connect">Connect</button>
<div id="messagearea"></div>
<label style="margin-left: 4px;width:20px">Payload : </label><input type="text" id="payload" maxlength="128" style="width:calc(98% - 550px);text-align:left" onchange="checkpublish()">
<label style="width:20px">Topic : </label><input type="text" value="/mqtt" id="topic" maxlength="64" style="width:280px" onchange="checkpublish()">
<button id="Publish" onclick="doPublish()" style="width:60px" disabled>Publish</button>
</div>
</body>
</html>