对于MQTT相信搞物联网的话,已经不是很陌生了。MQTT是基于TCP长连接下的,它的报文比较小,非常适合于物联网嵌入式设备使用。对于详细的MQTT协议可以参考网上其他文章,这里推荐一个github上的中文协议手册

MQTT协议中文版 : https://mcxiaoke.gitbooks.io/mqtt-cn/content/
<https://mcxiaoke.gitbooks.io/mqtt-cn/content/>

  在项目开发中,嵌入式端已经实现了MQTT对接百度天工物联(后期再写一篇paho.mqtt.embedded-c
<https://github.com/eclipse/paho.mqtt.embedded-c>
的移植教程),但是苦于没有一个web端控制。在网上也寻找过方案,包括用后端PHPMQTT实现,ASP.net也有对于的MQTT库。但是没有一个简单的快速实现方法,如果会一点html+javascript的话,我再想,是不是能用前端的技术来实现一个简单的MQTT。按这个思路去网上搜寻,还真有相关的库。

1 - MQTT.js 

<https://www.npmjs.com/package/mqtt>

 https://www.npmjs.com/package/mqtt <https://www.npmjs.com/package/mqtt>

MQTT.js is a client library for the MQTT <http://mqtt.org/> protocol, written
in JavaScript for node.js and the browser.

2 - Paho



http://www.eclipse.org/paho/downloads.php
<http://www.eclipse.org/paho/downloads.php>

Paho的库做的比较全,支持一下语言。




上表中看到,Paho有一个JavaScript的支持,居然我前面的STM32开发板中用的是Paho的Embedded版本移植,那就想尝试下Paho的JavaScript吧,好在前几年也自学过一些javascript语法。

    在网上找了下,关于Paho MQTT.js去对接百度天工物联的资料比较少,索性将自己的摸索过程记录下来。

MQTT系统结构

    本文的目的是 实现通过网页实时控制设备端的一个LED灯。

    本文主要讲述的内容是在已实现物联网设备的基础上,设计web页面实现网页和嵌入式物联网设备的数据通讯。整个系统结构如下:



   
 设备端启动后,连接百度IOT服务器,并订阅一个主题【mytopic01】。web页面端启动后,根据用户设置的百度IOt的账号密码进行连接,并在页面中添加2个button,用于推送主题消息。

开源库列表

 1-paho.mqtt.javascript <https://github.com/eclipse/paho.mqtt.javascript>  : 
https://github.com/eclipse/paho.mqtt.javascript
<https://github.com/eclipse/paho.mqtt.javascript>

 2-相关文档  :  http://www.eclipse.org/paho/files/jsdoc/index.html
<http://www.eclipse.org/paho/files/jsdoc/index.html>

 

html页面设计

这里只讲究功能,页面的美化就不考虑了,有能力的同学可以用Bootstrap对页面进行精心排版和美化

新建 index.html
<!doctype html> <html lang="en"> <head> <title>Paho MQTT.js </title> <!--
Required meta tags --> <meta charset="utf-8"> <meta name="viewport"
content="width=device-width, initial-scale=1, shrink-to-fit=no"> <!-- Bootstrap
CSS --> <link rel="stylesheet"
href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.2/css/bootstrap.min.css"
integrity="sha384-Smlep5jCw/wG7hdkwQ/Z5nLIefveQRIY9nfy6xoR1uRYBtpZgI6339F5dgvm/e9B"
crossorigin="anonymous"> <!-- 引用MQTT的CDN --> <script
src="https://cdnjs.cloudflare.com/ajax/libs/paho-mqtt/1.0.1/mqttws31.js"
type="text/javascript"></script> </head> <body> <div class="container"> <div
class="row"> <div class="col"> </div> <h1>Paho MQTT.js Test!</h1> <div
class="col"> </div> </div> <div class="row"> <div class="col-md"> </div> <div
class="col-md-5"> <div class="card border-success"> <div
class="card-img-top"><img class=""
src="https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1534998931421&di=9c27aaeef01a83dd530a40f7b06aae1f&imgtype=0&src=http%3A%2F%2Fpic.58pic.com%2F58pic%2F15%2F53%2F67%2F93a58PICrhF_1024.png"
width="50px" height="50px" alt=""></div> <div class="card-body"> <h4
class="card-title">LED control</h4> <p class="card-text"> <button type="button"
class="btn btn-primary" onclick='publish("ON");'>开灯</button> <button
type="button" class="btn btn-seconday" onclick='publish("OFF");'>关灯</button>
</p> </div> </div> </div> <div class="col-md"> </div> </div> </div> <!-- 相关业务代码
--> <script src="utility_paho.js"></script> </body> </html>
这是一个简单的HTML页面,添加了2个Button。关于MQTT 通信的相关代码在 `utility_paho.js` 文件内。

 

JS代码设计

新建 utility_paho.js 文件
var passWord = "baidu IOT 的设备实例密码"; var username = "设备实例用户名"; var hostname =
"XXXXX.mqtt.iot.gz.baidubce.com"; //替换成的你百度实例地址 var port = "8884";
//使用WSS协议的接口地址 var clientId = makeid(); var connected = false; var client = new
Paho.MQTT.Client(hostname, Number(port), "/mqtt", 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; var options = {
invocationContext: { host: hostname, port: port, clientId: clientId }, timeout:
5, keepAliveInterval: 60, cleanSession: true, useSSL: true, //reconnect: true,
onSuccess: onConnect, onFailure: onFail, mqttVersion: 4 }; options.userName =
username; options.password = passWord; client.connect(options); function
subscribe() { var topic = "mytopic01"; var qos = 0; logMessage("INFO",
"Subscribing to: [Topic: ", topic, ", QoS: ", qos, "]");
client.subscribe(topic, { qos: Number(qos) }); } function publish(ledState) {
var topic = "mytopic01"; var qos = 0; var message = ledState; var retain =
false; message = "{\"LED\":\"" + message + "\"} "; logMessage("INFO",
"Publishing Message: [Topic: ", topic, ", Payload: ", message, ", QoS: ", qos,
", Retain: ", retain, "]"); message = new Paho.MQTT.Message(message);
message.destinationName = topic; message.qos = Number(qos); message.retained =
retain; client.send(message); } function disconnect() { logMessage("INFO",
"Disconnecting from Server."); client.disconnect(); } // called when the client
loses its connection function onConnectionLost(responseObject) { if
(responseObject.errorCode !== 0) { logMessage("INFO", "Connection Lost. [Error
Message: ", responseObject.errorMessage, "]"); } connected = false; } // called
when a message arrives function onMessageArrived(message) { logMessage("INFO",
"Message Recieved: [Topic: ", message.destinationName, ", Payload: ",
message.payloadString, ", QoS: ", message.qos, ", Retained: ",
message.retained, ", Duplicate: ", message.duplicate, "]"); } // called when
the client connects function onConnect(context) { // Once a connection has been
made, make a subscription and send a message. var connectionString =
context.invocationContext.host + ":" + context.invocationContext.port +
context.invocationContext.path; logMessage("INFO", "Connection Success ",
"[URI: ", connectionString, ", ID: ", context.invocationContext.clientId, "]");
connected = true; } 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 makeid() { var text = ""; var possible =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; for (var i =
0; i < 15; i++) text += possible.charAt(Math.floor(Math.random() *
possible.length)); return text; } function logMessage(type, ...content) { var
date = new Date(); var timeString = date.toUTCString(); var logMessage =
timeString + " - " + type + " - " + content.join(""); if (type === "INFO") {
console.info(logMessage); } else if (type === "ERROR") {
console.error(logMessage); } else { console.log(logMessage); } }
对于Paho 相关API接口函数的使用,可以参考以上相关文档
<http://www.eclipse.org/paho/files/jsdoc/index.html>说明

运行页面

将以上的 index.html 和 utility_paho.js 放在同一目录下,使用浏览器(推荐chrome)打开index.html即可看到画面.



 

对于百度天工物联的实例添加和设置,请自行百度学习。

几个坑点

在实验过程中,本人也掉过接个坑,现总结下。

1. client.connect(options); 方法的 options参数名称必须要和文档中正确,不能有多余货错误参数,否者会出现如下错误:



2. paho javascript版本是使用WS协议通信的,所有不能使用百度天工的tcp端口了。使用如下图片中的第三个,并使用SSL (useSSL :
true)  

这一点也要感谢 QQ群好友 明宝(23072790XX) 的提醒,对于一个搞嵌入式硬件的,一开始还真不知道paho 的javascript
版本使用的ws协议接口,一直在1883端口下试来试去一直报错。


var options = { invocationContext: { host: hostname, port: port, clientId:
clientId }, timeout: 5, keepAliveInterval: 60, cleanSession: true, useSSL:
true, onSuccess: onConnect, onFailure: onFail, mqttVersion: 4 };