一、簡介
MQTT(消息隊列遙測傳輸)是ISO 標準(ISO/IEC PRF 20922)下基於發佈/訂閱範式的消息協議。它工作在 TCP/IP協議族上,是爲硬件性能低下的遠程設備以及網絡狀況糟糕的情況下而設計的發佈/訂閱型消息協議,爲此,它需要一個消息中間件 。
MQTT是一個基於客戶端-服務器的消息發佈/訂閱傳輸協議。MQTT協議是輕量、簡單、開放和易於實現的,這些特點使它適用範圍非常廣泛。在很多情況下,包括受限的環境中,如:機器與機器(M2M)通信和物聯網(IoT)。其在,通過衛星鏈路通信傳感器、偶爾撥號的醫療設備、智能家居、及一些小型化設備中已廣泛使用。
二、 開發插件
- 添加引用
- 添加MQTTNet,在NuGet搜索MQTTNet
- 添加Core,路徑:\IoTBrowser\src\app_x64\Core.dll
- 添加Infrastructure,路徑:\IoTBrowser\src\app_x64\Infrastructure.dll
- 添加Newtonsoft,路徑:\IoTBrowser\src\app_x64\Newtonsoft.Json.dll
- 開發MqttHostCom和MqttClientCom插件
- MqttHostCom 服務端 broker
using DDS.IoT.Com; using System; using System.Collections.Generic; using System.IO.Ports; using System.Linq; using System.Runtime.InteropServices; using System.Text; using System.Threading; using System.Threading.Tasks; namespace DDS.IoT.Mqtt { public class MqttHostCom : ComBase { public override string Type => "mqttHostCom"; public override string Name => "Mqtt主機"; private MqttHostService hostService; public override bool Init(int port, int baudRate = 9600, string extendData = null) { this.Port = port; hostService = new MqttHostService(); hostService.PushId = this.Id; hostService.StartAsync(extendData, OnPushData); Console.WriteLine("初始化MqttHostCom驅動程序成功!"); return true; } public override event PushData OnPushData; public override bool Open() { var b = false; try { b = true; IsOpen = true; } catch (Exception ex) { string msg = string.Format("MqttHostCom串口打開失敗:{0} ", ex.Message); Console.WriteLine(msg); } return b; } public override bool Close() { hostService.Dispose(); hostService = null; IsOpen = false; OnPushData = null; return true; } public override string Command(string name, string data) { var outData = string.Empty; return outData; } } }
2.MqttClientCom 客戶端
實現發佈和訂閱接口
using DDS.IoT.Com; using System; using System.Collections.Generic; using System.IO.Ports; using System.Linq; using System.Runtime.InteropServices; using System.Text; using System.Threading; using System.Threading.Tasks; namespace DDS.IoT.Mqtt { public class MqttClientCom : ComBase { public override string Type => "mqttClientCom"; public override string Name => "Mqtt客戶端"; private MqttClientService mqttClientService; public override bool Init(int port, int baudRate = 9600, string extendData = null) { mqttClientService = new MqttClientService(); mqttClientService.PushId = this.Id; this.Port = port; mqttClientService.MqttClientStart(extendData,this.OnPushData); Console.WriteLine("初始化MqttClientCom驅動程序成功!"); return true; } public override event PushData OnPushData; public override bool Open() { var b = false; try { mqttClientService.Open(); b = true; IsOpen = true; } catch (Exception ex) { string msg = string.Format("MqttClientCom串口打開失敗:{0} ", ex.Message); Console.WriteLine(msg); } return b; } public override bool Close() { mqttClientService.Close(); mqttClientService = null; IsOpen = false; OnPushData = null; return true; } public override string Command(string name, string data) { var outData = string.Empty; var dataObj = Newtonsoft.Json.JsonConvert.DeserializeObject<dynamic>(data); switch (name) { case "Publish": string topic = dataObj.topic; string payload = dataObj.data; int? level = dataObj.level; bool? retain = dataObj.retain; if (!level.HasValue) { level = 1; } if (!retain.HasValue) { retain = false; } outData =mqttClientService.Publish(topic, payload, level.Value, retain.Value).ToString(); break; case "Subscribe": topic = dataObj.topic; level = dataObj.level; if (!level.HasValue) { level = 0; } outData = mqttClientService.Subscribe(topic, level.Value).ToString(); break; } return outData; } } }
3.前端測試
<!DOCTYPE HTML PUBLIC "-//WAPFORUM//DTD XHTML Mobile 1.0//EN" "http://www.wapforum.org/DTD/xhtml-mobile10.dtd"> <html> <head lang="en"> <title>Mqtt</title> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width,initial-scale=1.0,maximum-scale=1.0,user-scalable=0"> <meta name="format-detection" content="telephone=no"> <!-- Set render engine for 360 browser --> <meta name="renderer" content="webkit"> <!-- No Baidu Siteapp--> <!--<meta http-equiv="Cache-Control" content="no-siteapp" />--> <META HTTP-EQUIV="pragma" CONTENT="no-cache"> <META HTTP-EQUIV="Cache-Control" CONTENT="no-cache, must-revalidate"> <META HTTP-EQUIV="expires" CONTENT="0"> <link rel="alternate icon" type="image/png" href="favicon.png"> <link rel="stylesheet" href="/scripts/amazeui/amazeui.min.css" /> <link rel="stylesheet" href="../css/main.css" /> <script src="/scripts/jquery-3.3.1.min.js"></script> <script src="/scripts/amazeui/amazeui.min.js"></script> <script src="/scripts/jquery.signalR-2.4.1.min.js"></script> <style> .list { width: 1000px !important; } .am-form { width: 100%; background: #fff; } .refresh-port { width: 80px !important; height: 30px !important; } #msg, #msgWrite { clear: both; } .am-u-sm-4 { padding: 3px; } </style> <script type="text/javascript"> var hostid;// 主機id var clientid;// 客戶端id function startHost() { var args = $('#txtHostArgs').val(); dds.iot.com.open({ type: 'mqttHostCom',//mqtt主機 port: 1, baudRate: 1, extendData: args, //extendData: JSON.stringify({ server: "*", port: 1883 }), onReceive: function (res) { addMsg('host:' + JSON.stringify(res.data)) console.log('host', res.data) }, onOpen: function (ar) { if (ar.Success) { hostid = ar.Data; addMsg('連接成功!') } else { alert(ar.Message) } } }) } function closeHost() { dds.iot.com.close(hostid) } function startClient() { var args = $('#txtClientArgs').val(); dds.iot.com.open({ type: 'mqttClientCom',//mqtt客戶端 port: 1, baudRate: 1, extendData: args, //extendData: JSON.stringify({ server: "localhost", port: 1883, clientid: "1", username: "", password:""}), onReceive: function (res) { addMsg('client:' + JSON.stringify(res.data)) console.log('client',res.data) }, onOpen: function (ar) { if (ar.Success) { clientid = ar.Data; addMsg('連接成功!') } else { alert(ar.Message) } } }) } function subscribe() { var topic = $('#txtTopic').val(); dds.iot.com.exeCommand({ id: clientid, name: "Subscribe", data: { topic: topic, level: 0 } }, function (ar) { if (ar.Success) { addMsg('訂閱成功!') } else { addMsg('操作失敗:' + ar.Message) } }) } function publish() { var topic = $('#txtTopic').val(); var contents = $('#txtContents').val(); dds.iot.com.exeCommand({ id: clientid, name: "Publish", data: { topic: topic, data: contents } }, function (ar) { if (ar.Success) { addMsg('發佈成功!') } else { addMsg('操作失敗:' + ar.Message) } }) } function closeClient() { dds.iot.com.close(clientid) } var $msg; function addMsg(msg) { $msg.val($msg.val()+"\n"+msg); } function clearLog() { $msg.val(''); } // 窗口初始化事件(操作窗口大小、標題) $(document).bind('dds.window.init', function (e, win) { $msg = $("#msg"); }) </script> </head> <body> <div class="fun_bd" style="padding:10px;"> <form class="am-form"> <h3>數據讀取</h3> <fieldset> <div class="am-form-group"> <label for="doc-ipt-email-1" class="am-u-sm-4">主機參數</label> <div class="am-u-sm-6"> <input id="txtHostArgs" type="text" value='{ server: "*", port: 1883 }' /> </div> <button onclick="startHost()" class="am-btn-primary" type="button">啓動主機</button> <button onclick="closeHost()" class="am-btn-danger" type="button">關閉主機</button> </div> <div class="am-form-group"> <label for="doc-ipt-email-1" class="am-u-sm-4">客戶端參數</label> <div class="am-u-sm-6"> <input id="txtClientArgs" type="text" value='{ server: "localhost", port: 1883, clientid: "1", username: "", password:""}' /> </div> <button onclick="startClient()" class="am-btn-primary" type="button">啓動客戶端</button> <button onclick="closeClient()" class="am-btn-danger" type="button">關閉客戶端</button> </div> <div class="am-form-group"> <label for="doc-ipt-email-1" class="am-u-sm-4">主題</label> <div class="am-u-sm-6"> <input id="txtTopic" type="text" value="/dds/iot/mqtt/test" /> </div> <button onclick="subscribe()" class="am-btn-primary" type="button">訂閱主題</button> </div> <div class="am-form-group"> <label for="doc-ipt-email-1" class="am-u-sm-4">主題內容</label> <div class="am-u-sm-6"> <input id="txtContents" type="text" value="測試測試" /> </div> <button onclick="publish()" class="am-btn-primary" type="button">發佈主題</button> </div> <!--<div id="msg"></div>--> <textarea id="msg" rows="18"></textarea> <div class="am-form-group"> <button onclick="clearLog()" class="am-btn-default" type="button">清除日誌</button> </div> </fieldset> </form> </div> </body> </html>
代碼地址:https://gitee.com/yizhuqing/IoTBrowser
基於Chromium內核使用H5快速開發工控系統界面,使用JS API前端人員既可以完成界面展示與硬件控制。系統自帶串口、RFID、電子秤等硬件協議支持,並且支持二次定製開發。可以用來開發人機界面(HMI)或數據採集與監督控制系統(SCADA) 。 使用H5或Vue可以本地打包離線應用,也可以在線加載Web網頁來控制設備硬件。