MQTT - 使用Amazon AWS IoT Shadow 服務
材料準備
- AmebaD [ AMB23 / AMB21 / AMB22 / BW16 / AW-CU488 Thing Plus ] x 1
範例說明
Amazon AWS IoT是一個雲端IoT服務平台:
AWS IoT是一個平台使您可以將設備連接到AWS Services和其他設備,保護數據,處理設備數據並對其進行操作,並使應用程序即使在離線狀態下也可以與設備進行互動。
AWS IoT的服務架構:(圖片來自 http://docs.aws.amazon.com/iot/latest/developerguide/aws-iot-how-it-works.html )
在該結構中,Ameba屬於左上角的“Things”。 將在“Things”和MQTT Message Broker之間建立一個TLS安全通道。 接著,“Things”和“Message Broker”通過此安全通道使用MQTT協議進行通信。 在“Message Broker”後面,“Thing Shadows”在Ameba離線時暫時保留消息,並在下次連接時將控制消息發送給Ameba。 通過“Rules Engine”,您可以限制事物的行為或將事物連接到Amazon的其他服務。
AWS管理控制台
首先,創建一個帳戶並註冊AWS IoT服務:https://aws.amazon.com/
然後,登錄到Amazon 管理控制台,然後單擊services -> Internet of Things下的 “IoT Core”。
然後,您將進入AWS IoT的主頁。 為了提供最佳服務,Amazon在不同地區提供服務器供用戶選擇。
點擊右上角的區域下拉菜單:
選擇附近的區域。
在Services頁面中,選擇Onboard。
進入AWS IoT的主頁並且點擊“Get started”
點擊“Create single thing”。
我們在名稱欄位中填寫“ameba”。 屬性代表ameba的狀態。
屬性的值可以直接由ameba或由控制端更新,並且控制端可以請求ameba將屬性設置為所需值。
在這裡,我們添加一個名為“led”的屬性,其值為 0”,然後單擊“Next”。
點擊“Skip creating a certificate at this time”,接著點擊“Create thing”。
點擊“Policies”,然後選擇“Create a policy”
策略用於限制“thing”可以執行的功能,它可以限制MQTT操作或可以執行的特定主題。 詳細了解政策:
http://docs.aws.amazon.com/iot/latest/developerguide/authorization.html
在這裡,我們不對ameba設置政策。 在名稱欄位中填寫“amebaPolicy”,在操作欄位中填寫“iot:*”,在資源欄位中填寫“*”。 然後檢查“Allow”。 最後單擊“Create”。
接下來,我們必須設置TLS憑證。 可以選擇自定義或是AWS IoT產出的憑證。點擊“Create Certificate”來生成TLS憑證。
然後,您可以看到4個鏈接。 請單擊4個鏈接中的每個鏈接,以下載“public key”,“private key”,“certificate”和“ rootCA”。
下載完4個文件後,單擊“Done”,然後返回到憑證主頁。
在“Actions”下拉菜單中單擊“Attach a policy”。
選擇“ AmebaPolicy”,然後點擊“Attach”
然後返回到憑證首頁右上方的“Actions”下拉菜單,單擊“Attach thing”,選擇出現在下面的窗口時剛創建的內容“ameba”,然後單擊“Attach”
然後啟動憑證。 返回憑證主頁並單擊憑證,然後在“Actions”下拉菜單中單擊“Activate”。
返回到左側的文件,選擇“Manage”->“Things”,然後單擊我們創建的ameba項目。
進入ameba頁面,在左側選擇“Interact”。 查找用於設置Amazon Alexa的Rest API端點的信息:
— REST API endpoint: “https://a1a7oo4baosgyy.iot.us-east-1.amazonaws.com/things/ameba/shadow”, “a1a7oo4baosgyy.iot.us-east-1.amazonaws.com” 為 MQTT Broker 服務器的地址。
— MQTT topic:”$aws/things/ameba/shadow/update” 表示我們將在AWS IoT Shadow服務中使用的MQTT主題(如果僅使用MQTT,而沒有AWS IoT Shadow服務,則可以指定其他主題名稱)。 建議使用”$aws/things/ameba/shadow/update”。
Ameba 設定
打開 “File” -> “Examples” -> “AmebaMQTTClient” -> “Amazon_AWS_IoT_Basic”
在範例代碼中,修改黃色的程式碼部分以符合您的WiFi網路設置。
填入”thing”名稱為”ameba”
以及我們先前在AWS IoT中找到的MQTT Broker服務器地址。
接下來,填寫TLS中使用的root CA。 下載並確保root CA內容正確。
接著填寫我們在AWS IoT控制台中創建的憑證(即客戶端憑證),通常其文件名會以“ -certificate.pem.crt”(例如“ efae24a533-certificate.pem.crt”)結尾。 使用文字編輯器打開憑證,並如下調整其格式:
– 在每行末尾添加新的行字元“\n”。
– 在每行的開頭和結尾處添加雙引號。
– 要將每行連接為字串,請在每行末尾添加“\”。
– 最後一行以分號結尾。
以相同的方式調整私鑰的格式,並將其添加到privateKeyBuff。
編譯並執行
上傳程式碼並在上傳完成後按Ameba上的重置按鈕。
在Arduino IDE中打開串行端口,觀察Ameba連接到AWS IoT服務器的情況。
備擇方案
Ameba還可以從AWS shadow中檢索當前的LED狀態。 通過向“shadow/get”主題發送消息來完成此操作。 有關更多信息,請參閱Amazon_AWS_IoT_with_ACK範例代碼。
程式碼說明
更改led狀態:
在這個例子中,我們使用GPIO接口來控制led。 在範例代碼中,我們默認將led_pin設置為10,將led_state設置為1。
pinMode(led_pin, OUTPUT); digitalWrite(led_pin, led_state);
設置憑證:
注意我們是使用wifiClient的WiFiSSLClient類型。
WiFiSSLClient wifiClient;
WiFiSSLClient繼承Client,因此可以將其作為PubSubClient構造函數的參數進行傳遞。
接下來,設置連接所需的TLS憑證。
wifiClient.setRootCA((unsigned char*)rootCABuff); wifiClient.setClientCertificate((unsigned char*)certificateBuff, (unsigned char*)privateKeyBuff);
配置MQTT Broker服務器
然後,MQTT PubClient將MQTT Broker服務器設置成連接
client.setServer(mqttServer, 8883); client.setCallback(callback);
連接到MQTT Broker服務器:
在loop()中,調用reconnect()函數並嘗試連接到MQTT Broker服務器並進行憑證驗證。
while (!client.connected()) {
訂閱並發布
接下來,訂閱主題。
for (int i=0; i<5; i++) { client.subscribe(subscribeTopic[i]); }
有一些共同的主題:
“$aws/things/ameba/shadow/update/accepted”,
“$aws/things/ameba/shadow/update/rejected”,
“$aws/things/ameba/shadow/update/delta”,
“$aws/things/ameba/shadow/get/accepted”,
“$aws/things/ameba/shadow/get/rejected”
相關文件:
http://docs.aws.amazon.com/iot/latest/developerguide/thing-shadow-data-flow.html
然後發布當前狀態:
sprintf(publishPayload, "{\"state\":{\"reported\":{\"led\":%d}},\"clientToken\":\"%s\"}", led_state, clientId); client.publish(publishTopic, publishPayload);
檢查主題並做出回應:
在回調函數中,我們檢查5個訂閱的主題,並檢查是否有“/shadow/get/accepted”消息:
if (strstr(topic, "/shadow/get/accepted") != NULL) {
如果存在,則消息來自控制端。 如果消息中的屬性狀態不同於當前狀態,請發布新狀態。
updateLedState(desired_led_state);