物聯網時代-跟著Thingsboard學IOT架構-MQTT裝置協議

三升水發表於2019-07-24

Thingsboard的MQTT裝置協議

thingsboard官網: https://thingsboard.io/

thingsboard GitHub: https://github.com/thingsboard/thingsboard

thingsboard提供的體驗地址: http://demo.thingsboard.io/

 

BY Thingsboard team

以下內容是在原文基礎上演繹的譯文。除非另行註明,頁面上所有內容採用知識共享-署名(CC BY 2.5 AU)協議共享。

原文地址: ThingsBoard API參考:MQTT裝置API


 

MQTT基礎知識

MQTT是一種輕量級的釋出 - 訂閱訊息傳遞協議,可能使其最適合各種物聯網裝置。您可以在此處找到有關MQTT的更多資訊。

ThingsBoard伺服器節點充當MQTT Broker,支援QoS級別0(最多一次)和1(至少一次)以及一組預定義主題。


 

客戶端庫設定

您可以在Web上找到大量MQTT客戶端庫。本文中的示例將基於Mosquitto,MQTT.jsPaho,要設定其中一個工具。

 

鍵值格式

預設情況下,ThingsBoard支援JSON中的鍵值內容。Key始終是一個字串,而value可以是string,boolean,double或long。也可以使用自定義二進位制格式或某些序列化框架。有關詳細資訊,請參閱物模型。例如:

 
{"stringKey":"value1", "booleanKey":true, "doubleKey":42.0, "longKey":73}

 

 

遙測上傳API

為了將遙測資料釋出到ThingsBoard伺服器節點,請將PUBLISH訊息傳送到以下主題:

 
v1/devices/me/telemetry

 

最簡單的支援資料格式是:

 
{"key1":"value1", "key2":"value2"}

 

要麼

 [{"key1":"value1"}, {"key2":"value2"}]

 

請注意,在這種情況下,伺服器端時間戳將分配給上傳的資料!

如果您的裝置能夠獲取客戶端時間戳,您可以使用以下格式:

 {"ts":1451649600512, "values":{"key1":"value1", "key2":"value2"}}

 

在上面的示例中,我們假設“1451649600512”是具有毫秒精度的unix時間戳。例如,值'1451649600512'對應於'Fri,2016年1月1日12:00:00.512 GMT'

 

屬性API

ThingsBoard屬性API允許裝置

  • 客戶端裝置屬性上載到伺服器。

將屬性更新發布到伺服器

要將客戶端裝置屬性發布到ThingsBoard伺服器節點,請將PUBLISH訊息傳送到以下主題:

 v1/devices/me/attributes

 

 

更多請看上文給出的連線。


 

Thingsboard的MQTT傳輸協議架構

因為Thingsboard最新release,是基於微服務架構,不利用單獨理解程式碼。

Thingsboard原始碼: https://github.com/thingsboard/thingsboard/tree/release-2.0/transport/mqtt

本文基於上面原始碼後,剔除相關的安全驗證和處理之後搭建簡易的講解專案:

https://github.com/sanshengshui/IOT-Technical-Guide/tree/master/IOT-Guide-MQTT

 


 

MQTT框架

因為Thingsboard是一個JVM技術棧的PaaS平臺,所以使用的是基於Java通訊框架的Netty,如果有對Netty不太熟悉的同學,可以參考我之前搭建的Netty實踐學習案例: https://github.com/sanshengshui/netty-learning-example

 

專案結構

 .
 ├── IOT-Guide-MQTT.iml
 ├── pom.xml
 └── src
     └── main
         └── java
             └── com
                 └── sanshengshui
                     └── mqtt
                         ├── adapter
                         │   └── JsonMqttAdaptor.java // MQTT json轉換器,在跟Thingsboard學習IOT-物模型有所講解
                         ├── IOTMqttServer.java // MQTT服務
                         ├── MqttTopicMatcher.java
                         ├── MqttTopics.java 
                         ├── MqttTransportHandler.java //MQTT處理類
                         └── MqttTransportServerInitializer.java

 


 

 

專案程式碼講解

 

IOTMqttServer

 
 1 private static final int PORT = 1884;
 2      private static final String leakDetectorLevel = "DISABLED";
 3      private static final Integer bossGroupThreadCount = 1;
 4      private static final Integer workerGroupThreadCount = 12;
 5      private static final Integer maxPayloadSize = 65536;
 6  7      public static void main(String[] args) throws Exception {
 8          ResourceLeakDetector.setLevel(ResourceLeakDetector.Level.valueOf(leakDetectorLevel.toUpperCase()));
 9 10          EventLoopGroup bossGroup = new NioEventLoopGroup(bossGroupThreadCount);
11          EventLoopGroup workerGroup = new NioEventLoopGroup(workerGroupThreadCount);
12 13          try {
14 15              ServerBootstrap b = new ServerBootstrap();
16              b.group(bossGroup,workerGroup)
17                      .channel(NioServerSocketChannel.class)
18                      .handler(new LoggingHandler(LogLevel.INFO))
19                      .childHandler(new MqttTransportServerInitializer(maxPayloadSize));
20              ChannelFuture f = b.bind(PORT);
21              f.channel().closeFuture().sync();
22          } finally {
23              bossGroup.shutdownGracefully();
24              workerGroup.shutdownGracefully();
25          }
26      }

 

第8行,設定服務端Netty記憶體讀寫洩漏級別,預設條件下為:DISABLED

第10行和第11行,設定boss執行緒組和work執行緒組的執行緒數量。預設情況下,boss執行緒組的執行緒數量為1,work執行緒組的數量為執行服務機器核心數量的2倍。

第15行,通過建立ServerBootstrap物件,在第16行設定使用EventLoopGroup

在17和19行,設定要被例項化的NioServerSockerChannel類,並設定最大的負載內容數量。

最後我們通過shutdowGracefully()函式優雅的關閉bossGroup和workGroup。


 

MqttTransportHandler#processMqttMsg()

 
 1 private void processMqttMsg(ChannelHandlerContext ctx, MqttMessage msg) {
 2          address = (InetSocketAddress) ctx.channel().remoteAddress();
 3          if (msg.fixedHeader() == null) {
 4              processDisconnect(ctx);
 5              return;
 6          }
 7  8          switch (msg.fixedHeader().messageType()) {
 9              case CONNECT:
10                  processConnect(ctx, (MqttConnectMessage) msg);
11                  break;
12              case PUBLISH:
13                  processPublish(ctx, (MqttPublishMessage) msg);
14                  break;
15              case SUBSCRIBE:
16                  processSubscribe(ctx, (MqttSubscribeMessage) msg);
17                  break;
18              case UNSUBSCRIBE:
19                  processUnsubscribe(ctx, (MqttUnsubscribeMessage) msg);
20                  break;
21              case PINGREQ:
22                  if (checkConnected(ctx)) {
23                      ctx.writeAndFlush(new MqttMessage(new MqttFixedHeader(PINGRESP,false,AT_MOST_ONCE, false, 0)));
24                  }
25                  break;
26              case DISCONNECT:
27                  if (checkConnected(ctx)) {
28                      processDisconnect(ctx);
29                  }
30                  break;
31              default:
32                  break;
33 34          }
35      }
36

 

第3行,通過判斷訊息的固定頭部是否為空,如果空;則通過processDisconnect(ctx)將裝置連線關閉。

processDisconnect(channelHandlerContext ctx)

 
private void processDisconnect(ChannelHandlerContext ctx) {
         ctx.close();  // 關閉socket通道
     }
 ​

 

第8行,通過判斷固定頭部的MQTT訊息型別,針對不同訊息做相應的處理。


 

MqttTransportHandler#PublishDevicePublish

以下是對釋出訊息進行相關的解讀,更多訊息型別的處理類,大家請參考我上面的IOT-Guide-MQTT進行閱讀。

 
private void processDevicePublish(ChannelHandlerContext ctx, MqttPublishMessage mqttMsg, String topicName, int msgId) {
         try {
             if (topicName.equals(MqttTopics.DEVICE_TELEMETRY_TOPIC)) { //如果主題為v1/devices/me/attributes
                 JsonMqttAdaptor.convertToMsg(POST_TELEMETRY_REQUEST, mqttMsg);
             } else if(topicName.equals(DEVICE_ATTRIBUTES_TOPIC)) {
                 JsonMqttAdaptor.convertToMsg(POST_ATTRIBUTES_REQUEST, mqttMsg);
             } else if(topicName.equals(MqttTopics.DEVICE_ATTRIBUTES_REQUEST_TOPIC_PREFIX)) {
                 JsonMqttAdaptor.convertToMsg(GET_ATTRIBUTES_REQUEST, mqttMsg);
             }
         } catch (AdaptorException e) {
 ​
         }
 ​
     }
 ​

 

我上面的程式碼僅是對訊息的主題進行判斷,然後對主題內的內容進行物模型的解析,得到相關屬性或者遙測資料的獲得。


 

演示效果

我們通過Paho或者MQTT.js和服務進行連線,釋出訊息到以下主題:

 v1/devices/me/telemetry
 

簡易的資料格式如下:

 {"key1":"value1", "key2":"value2"}

 

Paho圖示:

伺服器控制檯列印資料:

 七月 24, 2019 1:37:18 下午 io.netty.handler.logging.LoggingHandler channelRegistered
 資訊: [id: 0xf2bfb3a8] REGISTERED
 七月 24, 2019 1:37:18 下午 io.netty.handler.logging.LoggingHandler bind
 資訊: [id: 0xf2bfb3a8] BIND: 0.0.0.0/0.0.0.0:1884
 七月 24, 2019 1:37:18 下午 io.netty.handler.logging.LoggingHandler channelActive
 資訊: [id: 0xf2bfb3a8, L:/0:0:0:0:0:0:0:0:1884] ACTIVE
 七月 24, 2019 1:37:22 下午 io.netty.handler.logging.LoggingHandler channelRead
 資訊: [id: 0xf2bfb3a8, L:/0:0:0:0:0:0:0:0:1884] RECEIVED: [id: 0xe08abd12, L:/127.0.0.1:1884 - R:/127.0.0.1:48816]
 key= 1563946708305
 屬性名=temperature 屬性值=38
 屬性名=humidity 屬性值=60

 

如上所示,希望大家對Thingsboard的IOT架構-MQTT裝置協議這塊有所瞭解!

 

相關文章