本文環境基於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 可執行文件,嘗試執行
報錯 ,正常 因爲沒有配置正確終端節點及證書
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目錄下
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 配置成功
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!");
也就是
以上代碼則是打開遺言消息。
消息topic 爲AWSIOTLWTTEST
消息內容爲AndyLi test my awsiot device last will message!
重新編譯並執行程序
測試在AWSIOT 控制檯 測試一欄 訂閱AWSIOTLWTTEST 消息:
點擊訂閱主題後, 執行 ./subscribe_publish_sample 啓動成功後 殺掉進程,可以看到後臺收到了對應遺言消息
這裏遺言消息功能就測試完畢了。
3 設備影子使用遺言實現離線檢測功能
因爲遺言機制是基於MQTT的本身協議機制實現,而通常用於管理設備狀態都是用於設備影子(shadow)實現,所以很自然的想到,當設備異常離線後,如果能通過遺言消息去更新設備影子中的某個字段來表示是否離線。而AWS 官方文檔中也提出了該種方案,但描述不夠具體且不能通配所有設備,給使用者帶來一定困擾,需要進行一定程度改造才能使用。
鏈接:https://docs.aws.amazon.com/zh_cn/iot/latest/developerguide/device-shadow-data-flow.html
在設備影子中使用遺言更新shadow標誌位流程如下:
主要流程分爲三步:
- 定義一個shadow屬性作爲設備離線標誌位,每次代碼運行時做出響應更新
- 修改AWS IOT aws-iot-device-sdk-embedded-C lib源碼以支持shadow 可傳入遺言消息參數。通過控制檯監聽遺言消息判斷是否遺言消息發送成功。
- 配置AWSIOT規則引擎,當收到遺言消息重新發布shadow更新請求。
3.1 在事物影子中定義一個字段表示狀態
在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控制檯選擇行動,點擊創建規則
填入規則名及描述,填入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()}
添加好的規則如下圖:
添加好,保存規則即可開始測試。
測試當你殺掉IOT 程序時,離線標誌位connected就會被更新爲false。達到預期效果。
3.4 測試
我們在控制檯訂閱MQTT消息看 殺掉程序是否收到遺言消息:
good 收到了 我們先要的遺言消息,既然收到了這個遺言消息,如果規則引擎有效就會將該消息轉換成更新shadow的消息 $aws/things/ThingName/shadow/update, 這裏訂閱下這個消息嘗試一下:
同樣也收到了對應的消息,最後看下shadow的行爲。先將shadow中connect 更改爲true。(PS:這其實也是IOT的行爲,當程序啓動時應該更新shadow 中的connected 屬性爲true)。
運行程序,然後殺掉程序。可以看到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 (#.#)