AWS IOT 離線檢測功能(MQTT 遺言)

本文環境基於AWSIOT 嵌入式C lib:
https://github.com/aws/aws-iot-device-sdk-embedded-C

環境:ubuntu
語言 : C

關於AWS IOT 基礎操作可參見:https://blog.csdn.net/m0_37263637/article/details/80989986
關於AWS IOT 嵌入式lib基礎使用:https://blog.csdn.net/m0_37263637/article/details/81113614

aws_iot_shadow_yield(device->mqttClient, 5);//接受消息的回調不return 這裏就會阻塞住<br />影子的回調均在這個函數中觸發

1 MQTT 遺言

MQTT遺言消息是指:一般是連接着代理程序的客戶端預定義好 LWT(Last Will and Testament)的。如果客戶端異常地斷開連接,代理程序(the broker)將會廣播 LWT 消息到所有訂閱者的客戶端中。也就是當設備異常斷線後,server就會發布MQTT連接到server註冊的遺言消息。而我們使用這個功能就可以用來判斷IOT 是否異常離線。

2 AWS IOT 遺言消息

AWS IOT 下層實質上爲MQTT實現,所以同樣支持mqtt遺言機制,在sample 我們可以做出相應修改進行測試。

2.1 下載相關源碼並編譯

2.1.1 先到github 下載aws-iot-c源碼:

https://github.com/aws/aws-iot-device-sdk-embedded-C

2.1.2 準備mbedTLS

https://tls.mbed.org/download/start/mbedtls-2.11.0-apache.tgz
下載 mbedTLS lib,解壓並將mbedTLS中內容放到aws-iot-device-sdk-embedded-C/external_libs/mbedTLS目錄下

2.1.3 準備 CppUTest

https://github.com/cpputest/cpputest/releases/tag/v3.6
下載 tar包,解壓並CppUTest中內容放到aws-iot-device-sdk-embedded-C/external_libs/CppUTest目錄下

2.1.4 測試編譯

cd samples/linux/subscribe_publish_sample
sudo make

編譯後生成 subscribe_publish_sample 可執行文件,嘗試執行
image.png
報錯 ,正常 因爲沒有配置正確終端節點及證書

2.2 修改源碼配置事務及證書

在AWS IOT 控制檯創建事物並獲取相應事物證書並配置權限,可參見:https://blog.csdn.net/m0_37263637/article/details/80989986
創建好事物和證書後,注意這CA證書要中國區的

2.2.1 配置證書

將aws iot後臺創建的設備時下載的證書及對應root證書複製到aws-iot-device-sdk-embedded-C/certs目錄下
image.png

2.2.2 修改aws sample config

到aws-iot-device-sdk-embedded-C/samples/linux/subscribe_publish_sample目錄下修改配置aws_iot_config.h 配置相應事物信息,找到如下字段

#define AWS_IOT_MQTT_HOST              "" ///< Customer specific MQTT HOST. The same will be used for Thing Shadow
#define AWS_IOT_MQTT_PORT              443 ///< default port for MQTT/S
#define AWS_IOT_MQTT_CLIENT_ID         "c-sdk-client-id" ///< MQTT client ID should be unique for every device
#define AWS_IOT_MY_THING_NAME          "AWS-IoT-C-SDK" ///< Thing Name of the Shadow this device is associated with
#define AWS_IOT_ROOT_CA_FILENAME       "rootCA.crt" ///< Root CA file name
#define AWS_IOT_CERTIFICATE_FILENAME   "cert.pem" ///< device signed certificate file name
#define AWS_IOT_PRIVATE_KEY_FILENAME   "privkey.pem" ///< Device private key filename

//說明
//AWS_IOT_MQTT_HOST:終端節點,AWSIOT 控制檯 - 設置- 專有終端節點 可以看到終端節點域名
// AWS_IOT_MQTT_CLIENT_ID:clientID ,AWS後臺-具體某個事物-事物 ARN
// AWS_IOT_MY_THING_NAME:事物名,AWS後臺-具體某個事物-事物
// AWS_IOT_ROOT_CA_FILENAME : CA證書名,即1中拷貝進去的CA證書文件名
// AWS_IOT_CERTIFICATE_FILENAME: IOT 設備證書,即1 中拷貝進去的設備證書文件名
// AWS_IOT_PRIVATE_KEY_FILENAME:IOT設備私匙,即1 中拷貝進去的設備私匙文件名

//配置完成後可能是這樣
// =================================================
#define AWS_IOT_MQTT_HOST              "xxxxxxxxxxxxx.ats.iot.cn-north-1.amazonaws.com.cn" ///< Customer specific MQTT HOST. The same will be used for Thing Shadow
#define AWS_IOT_MQTT_PORT              443 ///< default port for MQTT/S
#define AWS_IOT_MQTT_CLIENT_ID         "AWSIOTTESTLWT" ///< MQTT client ID should be unique for every device
#define AWS_IOT_MY_THING_NAME 		     "AWSIOTTESTLWT" ///< Thing Name of the Shadow this device is associated with
#define AWS_IOT_ROOT_CA_FILENAME       "root-CA.crt" ///< Root CA file name
#define AWS_IOT_CERTIFICATE_FILENAME   "1e44fc1711-certificate.pem.crt" ///< device signed certificate file name
#define AWS_IOT_PRIVATE_KEY_FILENAME   "1e44fc1711-private.pem.key" ///< Device private key filename
// =================================================

配置好了只有可以測試sample是否可用

2.2.3 測試sample 是否可用

執行
./subscribe_publish_sample

出現如下結果即AWS IOT 配置成功
image.png

2.3 修改源碼添加遺言消息

修改samples/linux/subscribe_publish_sample/subscribe_publish_sample.c 185行
屏蔽 connectParams.isWillMsgPresent = false;
添加以下內容

  connectParams.isWillMsgPresent = true;  // This needs to be set to true so that server will continue parsing the packet and look for last will configuration
  connectParams.will = iotMqttWillOptionsDefault; // This also configure the last will to be QoS0
  connectParams.will.pTopicName = "AWSIOTLWTTEST";
  connectParams.will.topicNameLen = strlen("AWSIOTLWTTEST");
  connectParams.will.pMessage = "AndyLi test  my  awsiot device last will message!";
  connectParams.will.msgLen = strlen("AndyLi test  my  awsiot device last will message!");

也就是
image.png
以上代碼則是打開遺言消息。
消息topic 爲AWSIOTLWTTEST
消息內容爲AndyLi test my awsiot device last will message!

重新編譯並執行程序

測試在AWSIOT 控制檯 測試一欄 訂閱AWSIOTLWTTEST 消息:
image.png
點擊訂閱主題後, 執行 ./subscribe_publish_sample 啓動成功後 殺掉進程,可以看到後臺收到了對應遺言消息
image.png
這裏遺言消息功能就測試完畢了。

3 設備影子使用遺言實現離線檢測功能

因爲遺言機制是基於MQTT的本身協議機制實現,而通常用於管理設備狀態都是用於設備影子(shadow)實現,所以很自然的想到,當設備異常離線後,如果能通過遺言消息去更新設備影子中的某個字段來表示是否離線。而AWS 官方文檔中也提出了該種方案,但描述不夠具體且不能通配所有設備,給使用者帶來一定困擾,需要進行一定程度改造才能使用。

鏈接:https://docs.aws.amazon.com/zh_cn/iot/latest/developerguide/device-shadow-data-flow.html

在設備影子中使用遺言更新shadow標誌位流程如下:
主要流程分爲三步:

  1. 定義一個shadow屬性作爲設備離線標誌位,每次代碼運行時做出響應更新
  2. 修改AWS IOT aws-iot-device-sdk-embedded-C lib源碼以支持shadow 可傳入遺言消息參數。通過控制檯監聽遺言消息判斷是否遺言消息發送成功。
  3. 配置AWSIOT規則引擎,當收到遺言消息重新發布shadow更新請求。

3.1 在事物影子中定義一個字段表示狀態

image.png
在AWS IOT Thing 後臺中定義一個connected 屬性標識設備在線/離線狀態

{
  "reported": {
    "connected": "false"
  }
}

3.2 修改aws-iot-device-sdk-embedded-C 支持LWT消息

如2.2節中 的內容修改LWT 遺言消息 來觸發規則引擎,
這裏通2.2節 但這裏有所不同消息內容需要設置爲特定的內容,通過規則引擎配置的規則後即會更新到設備影子中的connect字段

{
  "state": {
    "reported": {
      "connected": "false"
    }
  }
}

所以修改內容如下,這裏用到CJSON 來封裝上面這種消息格式:

 IOT_INFO("Connecting ...");

  cJSON *monitor1 = cJSON_CreateObject();
	cJSON *state1 = cJSON_CreateObject();
  cJSON *reported1 = cJSON_CreateObject();
	cJSON_AddStringToObject(reported1, "connected", "false");
	cJSON_AddItemToObject(state1, "reported", reported1);
	cJSON_AddItemToObject(monitor1, "state", state1);

	char* stringCJson = cJSON_PrintUnformatted(monitor1);//生成JSON PrintUnformatted函數爲不格式化生成,生成一個緊湊的json字串
   if (stringCJson == NULL) {
        fprintf(stderr, "Failed to print monitor.\n");
   }

	int str_len = strlen(stringCJson);
  
  char* thingName = "AWSIOTTESTLWT";
  connectParams.isWillMsgPresent = true;  // This needs to be set to true so that server will continue parsing the packet and look for last will configuration
  connectParams.will = iotMqttWillOptionsDefault; // This also configure the last will to be QoS0
 
  connectParams.will.pTopicName  = (char *)malloc(50+22);  //開闢一個空間存儲消息
  sprintf(connectParams.will.pTopicName , "/things/%s/shadow/update", thingName);
  connectParams.will.topicNameLen = strlen(connectParams.will.pTopicName);
  
	connectParams.will.pMessage = malloc(str_len);
  strcpy(connectParams.will.pMessage, stringCJson);
  connectParams.will.msgLen = strlen(connectParams.will.pMessage);
	
  cJSON_free(stringCJson);
  cJSON_Delete(monitor1);

3.3 配置AWS IOT規則引擎

在AWS IOT控制檯選擇行動,點擊創建規則image.png

填入規則名及描述,填入SQL語句篩選出相應LWT消息,比如按照上面流程來的SQL 語句如下

SELECT * FROM '/things/+/shadow/update'

我們需要篩選出3.2中我們發送的的LWT消息。即/thing/ThingName/shadow/update 消息,上訴代碼使用+號進行通配。篩選出符合動態ThingName的消息,而不是固定的ThingName。

配置篩選出相應LWT消息SQL語句後,應該爲觸發這消息添加相關的後續處理,添加下面的添加操作,選擇將消息重新發布到 AWS IoT主題,去更新對應的設備shadow中的字段。這裏主題需要修改該爲 更新shadow的消息(發送$aws/things/ThingName/shadow/update)則會進行更新對應事物shadow,請填入下面字段中的內容,這裏可能需要相應的權限,需要去IAM中創建。

$$aws${topic()}

添加好的規則如下圖:
image.png
添加好,保存規則即可開始測試。
測試當你殺掉IOT 程序時,離線標誌位connected就會被更新爲false。達到預期效果。

3.4 測試

我們在控制檯訂閱MQTT消息看 殺掉程序是否收到遺言消息:
image.png
good 收到了 我們先要的遺言消息,既然收到了這個遺言消息,如果規則引擎有效就會將該消息轉換成更新shadow的消息 $aws/things/ThingName/shadow/update, 這裏訂閱下這個消息嘗試一下:
image.png同樣也收到了對應的消息,最後看下shadow的行爲。先將shadow中connect 更改爲true。(PS:這其實也是IOT的行爲,當程序啓動時應該更新shadow 中的connected 屬性爲true)。
image.png
運行程序,然後殺掉程序。可以看到connected 從true 編程false。到這裏我們就完成了AWS IOT 利用MQTT消息實現離線檢測功能。

3.5 遺言消息的行爲

我們在配置MQTT時,其中在下層有一個屬性keepAliveIntervalInSec

ConnectParams->keepAliveIntervalInSec = 600; // NOTE: Temporary fix

如果是手動殺掉會立刻觸發遺言消息,而如果是斷網,設備掉電,則則會更根據這個屬性來觸發遺言消息。
比如默認設置是600s。 即設備異常10分鐘後纔會觸發遺言消息。

4 測試code

環境:X86 Linux gcc
github: https://github.com/CollapsarLi/aws_iot_c_shadow_lwt_sample.git
把github中的代碼放到samples/linux/subscribe_publish_sample 目錄下覆蓋原sample 並參照2.2.2 中配置aws_iot_config 及 AWSIOT 控制檯規則引擎。進行測試。
好用請start (#.#)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章