Ameba Arduino: [RTL8195] 使用Amazon AWS IoT Shadow Service

材料準備

  • Ameba x 1
  • Led x 1

範例說明

  • 簡介
    Amazon AWS IoT是一套雲端服務,如同Amazon網站上描述:
    AWS IoT 是一種讓您能夠將裝置連接至 AWS 服務和其他裝置、保護資料和互動安全、處理和對裝置資料採取動作,以及即使離線也能讓應用程式與裝置互動的平台。(from https://aws.amazon.com/tw/iot/how-it-works/)

    底下是AWS IoT的架構圖:
    1
    (Picture from http://docs.aws.amazon.com/iot/latest/developerguide/aws-iot-how-it-works.html)

    圖中左上方的Things可以想成是Ameba,它與MQTT Message Broker建立起TLS加密的Channel之後,經由MQTT Protocol溝通。在Message Broker背後有Thing Shadows,可以讓Ameba離線時,仍能讓控制端留下訊息,在下一次Ameba連線時,Thing Shadow會將訊息傳出。
    架構裡的Rules Engine可以針對Thing的行為做限制,也可以接上Amazon的其它服務,這部份不在這次的範例討論裡。

  • 使用AWS IoT主控台
    要使用AWS IoT,首先需要開通AWS IoT的服務,你可以在這邊找到登入與註冊的訊息:https://aws.amazon.com/
    開通服務需要填信用卡號來確認使用者,並且現在(2016/06/27)開通服務會在第一年享有優惠,在某個流量以內不收費,或是可以設置流量警示避免繳交不預期的費用。詳細情形請參考網站內容說明。
    登入之後,會進入Amazon Management Console,可以看到有很多的Service,點選AWS IoT
    2

    會進入AWS IoT的首頁,如果是第一次使用,會看到底下的簡介畫面。Amazon的service為了效率考量分了好幾個區域,讓使用者可以選較近的server使用,為了確保使用品質,我們先點右上角使用者名稱旁邊的地區選項:
    3

    接著選擇離自己近的區域
    4

    選好之後,我們再點選 “Get started”
    5

    “Get started” 預設的行為是 “Create a thing”,我們填入 thing的名稱為 “ameba”,並且點選 “Add attributes” 來新增屬性
    6

    屬性可以用來定義 ameba的狀態,ameba可以更新這些狀態,控制端也可以嘗試要求ameba切換到控制端想要的狀態。這裡我們填入屬性名稱為led,值填入0,讓ameba可以更新一個led的狀態。設定完成之後,點選 “Create”

    7

    這樣我們就新增完成 thing。接著我們點選 “Create a policy”。
    8

    Policy的用途是限制thing的功能,可以限制thing可以執行的MQTT動作,或是限制特定的topic,更多policy的用法可以參考:http://docs.aws.amazon.com/iot/latest/developerguide/authorization.html
    這裡我們先不作限制,讓整個流程成功之後再調整這部份。我們將policy的名稱填入 “amebaPolicy”,Action填入 “iot:*”,Resources填入 “*”,並在後面的 “Allow”打勾。填完之後點選 “Add statement”。
    9

    接著按下Create,就完成Policy的設定:
    10

    完成之後會在下面看到已經有了 thing與 policy兩個圖案。
    接著我們要設定TLS所需的certificate,點選 “Create a certificate”
    11

    這裡可以選擇自己定義的certificate,或是網站幫你產生。我們點選 “1-Click certificate create”讓網站幫我們產生。
    12

    接著會產生三個連結,讓我們可以下載 public key,private key,以及certificate。我們將這三個檔案下載。
    13

    此時下面有三個圖案,由左到右分別是certificate,policy,以及thing。這三個彼此目前沒有任何關聯,我們要將policy與thing繫結到certificate上。我們點選certificate:
    14

    然後在右邊的Actions下拉選單裡,點選 “Attach a policy”
    15

    輸入之前新增的policy名稱,這邊我們填入 “amebaPolicy”,然後點 “Attach”
    16

    接著我們再點右邊的 Actions下拉選單,點選 “Attach a thing”
    17

    然後輸入之前新增的 thing名稱,這邊我們填入 “ameba”,然後點 “Attach”
    18

    此時我們重新整理一下頁面,並點選 certificate,可以看到右方的資訊欄裡已經多了 ameba與amebaPolicy
    19

    接著我們要啟用certificate,點選certificate之後,在右邊的Actions下拉選單裡,點選 “Activate”
    20

    接著我們需要查看thing的一些資訊,我們點ameba的圖案,右邊會出現一些資訊:
    -- REST API endpoint: 裡面的值是 “https://a2zweh2b7yb784.iot.ap-southeast-1.amazonaws.com/things/ameba/shadow”,其中 “a2zweh2b7yb784.iot.ap-southeast-1.amazonaws.com”就是我們可以使用的 MQTT Broker server的位址
    -- MQTT topic:裡面的值是 “$aws/things/ameba/shadow/update”,代表如果我們想使用AWS IoT Shadow的服務,我們的MQTT topic就得填這個值。但如果我們只想用MQTT的功能,則可以使用其它值。這邊我們建議使用 “$aws/things/ameba/shadow/update”

    21

  • 設定Ameba
    我們打開範例 “File” -> “Examples” -> “AmebaMQTTClient” -> “amazon_awsiot_basic”
    首先需要先填入Ameba要連上的AP的ssid與password
    22接著填入thing的名稱,這邊我們填入 “ameba”,這個名稱會自動帶入與thing名稱有關的字串裡。
    23接著填入MQTT Broker server的位址,這個位址可以在AWS IoT主控台裡找到,它會在我們新增的thing的資訊欄裡面,這裡我們填入"a2zweh2b7yb784.iot.ap-southeast-1.amazonaws.com"
    24

    接著我們填入TLS會用到的root CA,root CA與我們在AWS IoT主控台新增的certificate不同,它可以在這裡找到:
    25

    我們可以下載確認一下root CA是否與sketch的root CA相同
    26

    接著我們要填入我們在AWS IoT主控台新增的certificate(通常稱之為 client certificate),檔名結尾為 “-certificate.pem.crt” (Ex. “efae24a533-certificate.pem.crt”),我們可以用文字編輯器打開這個檔案,會發現它與sketch的內容不太一樣,我們需要將它調整成字串格式:
    - 每行後面需要加上換行符號 \n
    - 每行前後需要加上雙引號
    - 為了將字串串接,在行尾加上 \
    - 最後一行以分號結束

    27

    我們在AWS IoT主控台新增certificate時,還有private key的資訊,我們將它填入,一樣要轉成字串格式:
    28

    到這邊就設定完成了

  • 編譯並執行
    我們將sketch編譯並上傳至Ameba之後,按下reset按鈕,打開serial monitor看執行的結果
    291. 第一步會先連上AP並取得IP Address
    2. 接著會嘗試連上MQTT Broker server,這步會花比較久的時間。會看到log裡出現驗證certificate的資訊,最後建立TLS連線成功,顯示 “connected”
    3. 連線上之後,Ameba會publish目前的狀態,topic 為 “$aws/things/ameba/shadow/update”,而payload為AWS IoT Shadow的JSON格式。
    AWS IoT Shadow的格式可以參考這裡:http://docs.aws.amazon.com/iot/latest/developerguide/thing-shadow-document-syntax.html這裡Ameba只有led的狀態,這個狀態與我們在AWS IoT主控台在新增thing時所添加的狀態是一樣的名稱。這裡我們將 “led” 的狀態填入1,代表將led點亮。
    此時我們可以在 AWS IoT主控台的ameba thing右方資訊欄看到資訊已更新,會看到 “Last update”的地方會是最近幾分鐘的時間,並且 “Shadow status” 的內容裡會看到ameba上傳的資訊。
    30

    如果我們想控制ameba讓led關掉,可以點選右上方的 “Update Shadow”
    31

    點選之後,會出現 “Shadow state” 的文字方塊,裡面已經有預設的內容
    32

    我們將文字方塊的內容改成:

    {
      "desired": {
        "led": 0
      }
    }
    

    代表我們希望將 “led” 的狀態改為 0,改完之後點選 “Update shadow”
    33

    回到右上方的 “Detail” 頁籤,等一會兒會看到 “led” 的相關狀態都變成 0了,並且 “Last update” 又有一筆新的更新
    34

    此時回到 Serial Monitor會看到新的訊息
    1. 進來了一筆標題為 “$aws/things/ameba/shadow/update/accepted” 的訊息,內容有剛剛我們在AWS IoT主控台填寫的內容
    2. Ameba收到前一筆訊息時,解析它的內容,發現 “led” 狀態變成0,於是將led關掉,並且publish新的狀態上去
    3. 同時進來另外一筆標題為 “$aws/things/ameba/shadow/update/delta”的內容
    4. 最後MQTT Broker又送一筆標題 “$aws/things/ameba/shadow/update/accepted”的訊息,回應ameba最新一筆的publish
    35

    這個過程到這邊結束,我們總共使用了 AWS IoT主控台,設定了TLS加密所需的certificate,也設定了MQTT Brokder,最後使用AWS IoT Shadow操作與更新ameba的狀態。Amazon提供了詳細的文件,可以在這裡找到更多相關資訊:http://docs.aws.amazon.com/iot/latest/developerguide/

程式碼說明

整份程式碼使用了基本的MQTT架構,除了AWS IoT的MQTT Broker Server與TLS certificate設定可以參考前面的說明之外,程式碼說明如下

  • 設定led狀態
    這部份是一般的GPIO應用,預設led_pin為10,led_state為1

     pinMode(led_pin, OUTPUT);
    digitalWrite(led_pin, led_state);
    
  • 連線至AP
    這部份是一般的wifi連線程式碼,沒有不同的地方
  • 設定certificate
    這部份就有點不同,其中我們的wifiClient的型態是

    WiFiSSLClient wifiClient;
    

    WiFiSSLClient繼承了Client,所以也可以當作PubSublicant的constructor參數
    在連線之前,我們設定TLS相關的certificate

    wifiClient.setRootCA((unsigned char*)rootCABuff);
    wifiClient.setClientCertificate((unsigned char*)certificateBuff, (unsigned char*)privateKeyBuff);
    
  • 設定MQTT Broker server
    接著MQTT PubClient設定MQTT Broker server並且連線

    client.setServer(mqttServer, 8883);
    client.setCallback(callback);
    

    注意到這邊的port是8883,一般來說,如果MQTT底層走的是TLS協定,使用的port會是8883

  • 連線至MQTT Broker server
    進入loop()之後,會呼叫reconnect()並且嘗試連線,這邊也是log裡出現驗證certificate的地方:

    while (!client.connected()) {
    
  • Subscribe & Publish
    連線成功之後,註冊要傾聽的topic

    for (int i=0; i<5; i++) {
      client.subscribe(subscribeTopic[i]);
    }
    

    常用的topic有這些
    "$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
    註冊完之後,我們publish目前的狀態

    sprintf(publishPayload, "{\"state\":{\"reported\":{\"led\":%d}},\"clientToken\":\"%s\"}", led_state, clientId);
    client.publish(publishTopic, publishPayload);
    
  • 傾聽topic並做出回應
    我們在callback裡傾聽先前註冊的5個topic,並且檢查是否有 "/shadow/get/accepted"

    if (strstr(topic, "/shadow/get/accepted") != NULL) {
    

    如果有的話,代表控制端送了訊息過來,我們解析裡面的內容,如果led狀態與現在不同,則publish新的狀態

    updateLedState(desired_led_state);