Ameba Arduino: [RTL8195] 連線Microsoft Azure雲服務

材料準備

  • Ameba RTL8195 x 1
  • Microsoft Azure 帳號

範例說明

Microsoft Azure IoT是一套雲端服務,如同官網的描述:
仰賴 Microsoft Azure IoT 中心,便可輕鬆且安全地連接物聯網 (IoT) 資產。使用裝置到雲端遙測資料來了解裝置及資產的狀態,並在裝置需要您的關注時隨時準備好採取行動。在雲端到裝置的訊息中,可靠地將命令及通知傳送至連接的裝置,並透過通知回條來追蹤訊息傳遞。裝置訊息是以耐用持久的方式傳送,以便配合間歇性連接的裝置。(From https://azure.microsoft.com/zh-tw/services/iot-hub/)
以下是本範例中會使用到的Azure IoT裝置監控平台架構圖
1上圖左邊的Devices即代表Ameba RTL8195,他使用共用的存取簽章(SAS)與Azure IoT Hub建立起連接,其意指若要在無需提供帳戶金鑰的情況下,將儲存體帳戶中物件的限制存取授與其他用戶端,則使用共用存取簽章 (SAS) 會是個佷有效的方式。

  • 要使用Microsoft Azure,首先需要註冊帳號,你可以在這邊找到登入與註冊的訊息:https://azure.microsoft.com/en-us/free/
    開通服務需要填信用卡號來確認使用者,並且現在(2016/10/5)開通服務有200塊美金的免費流量。詳細情形請參考網站內容說明。
  • 帳號註冊開通之後即可登入Azure portal,進入之後如下圖選取 New > Internet of Things > Azure IoT Hub
    2
  • 之後會出現欲新增IoT Hub所需輸入的必要資訊,輸入完畢後點擊Create鍵,即建立Azure IoT Hub服務,在這裡IoT Hub Name我們輸入為”AmebaGO”
    3
  • 建立Azure IoT Suite - Remote Monitoring,Remote Monitoring為一圖形化的裝置感測控制台,綜合了圖表分析及GPS地理位置座標地圖參照,您可以參考來自Microsoft的教學頁面
    4
  • 在上述教學步驟中我們新增了一個新的device,device id為”AmebaGO1”,為非模擬裝置,並且此裝置尚未啟用
  • 接著需要取得共用存取簽章SAS token,來作為Azure IoT Hub提供授權讓Ameba RTL8195能夠使用Azure IoT Hub服務
  • 回到主控台首頁,左邊功能清單點選All Resources,接著可看到剛新增好的Azure IoT Hub連結
  • 點選Settings > Shared access policies > registryReadWrite,取得Primary key以及其對應的Connection string
    5
  • 新增 SAS token,如下步驟:
    1. 至https://github.com/Azure/azure-iot-sdks/releases下載SetupDeviceExplorer.msi
    2. 安裝完成後,開啟Device Explorer,將剛在Azure IoT Hub主控台得到的Connection string填入connection information裡,然後點擊Update按鈕,如下圖藍框處:
    6
    3. Device Explorer底下的Shared Access Signature資訊應會自動更新完成,接著點選上面的Management頁籤,如果先前已在Azure IoT Suite - Remote Monitoring完成設定並且新增裝置,Management頁籤裡就會列出在Remote Monitoring裡新增的所有裝置,接著按下右上角的SAS權杖按鈕(SAS Token),在 [SASTokenForm] 的 [DeviceID] 下拉式清單中,選取您的裝置(AmebaGO1)且設定您的 TTL,最後按一下 [產生] 來建立您的權杖,如需要更進一步訊息可參考Azure教學: https://azure.microsoft.com/zh-tw/documentation/articles/iot-hub-mqtt-support/
    4. 產生的 SAS 權證看起來如下:HostName={your hub name}.azure-devices.net;DeviceId=javadevice;SharedAccessSignature=SharedAccessSignature sr={your hub name}.azure-devices.net%2fdevices%2fMyDevice01&sig=vSgHBMUG.....Ntg%3d&se=1456481802。
    5. 此項目在MQTT的 [密碼] 欄位中使用 MQTT 連線時所使用的部分是︰SharedAccessSignature sr={your hub name}.azure-devices.net%2fdevices%2fyDevice01&sig=vSgHBMUG.....Ntg%3d&se=1456481802g%3d&se=1456481802。
  • 設定Ameba RTL8195
    我們打開範例 “File” -> “Examples” -> “AmebaMQTTClient” -> “azure_iot_hub_basic”
    首先需要先填入Ameba RTL8195要連上的AP的ssid與password
    7接著設定Azure MQTT相關參數
    mqttServer設定為: " [IoT Hub Name].azure-devices.net",範例中的設定為"AmebaGO.azure-devices.net"
    mqttPort設定為:8883
    mqttClientName設定為: Remote Monitoring裡新增的裝置命名,範例中的設定為AmebaGO1
    mqttUsername設定為: " [IoT Hub Name].azure-devices.net/[Dvice ID] ",範例中的設定為"AmebaGO.azure-devices.net/AmebaGO1"
    mqttPassword設定為: 上述從Device Explorer中產生的SAS Token,如:SharedAccessSignature sr={ IoT Hub Name }.azure-devices.net%2fdevices%2fyDevice01&sig=vSgHBMUG.....Ntg%3d&se=1456481802g%3d&se=1456481802
    mqttTopic設定為: "devices/[Dvice ID]/messages/devicebound/#",範例中的設定為"devices/AmebaGO1/messages/devicebound/#"
    mqttPublishEvents設定為: "devices/[Dvice ID]/messages/events/",範例中的設定為"devices/AmebaGO1/messages/events/"
    8
    設定Ameba RTL8195 DeviceInfo publish message:
    在Remote Monitoring裡新增的device一開始預設為pending(未啟用狀態),需經由publish DeviceInfo message來啟用pending中的device,DeviceInfo 訊息如以下格式:
    9

    而本範例中另外在DeviceProperties裡新增了Manufacturer及地理位置信息,因此範例中需要設定:
    manufacturer範例中的設定為Realtek
    latitude範例中的設定為43.7184039 (Realtek的緯度座標)
    longitude範例中的設定為-79.5181426 (Realtek的經度座標)
    10
    設定Ameba RTL8195 sensor data publish message:
    本範例中在每次的loop程序裡會publish 4種參數,分別是DeviceId, Temperature, Humidity及ExternalTemperature,溫濕度裡的數值都可以依照實際需要而進行修改
    11

  • 編譯並執行
    我們將範例編譯並上傳至Ameba RTL8195之後,按下reset按鈕,打開serial monitor看執行的結果
    12
    1. 第一步會先連上AP並取得IP Address
    2. 接著會嘗試連上MQTT Broker server,這步會花比較久的時間。
    3. 連線上之後,Ameba RTL8195會印出Devinfo的內容,並將之publish至Azure IoT Hub,成功publish之後,即進入loop迴圈,開始publish溫濕度資料
    13
    4.此時可以至Remote Monitoring頁面,Remote Monitoring提供的圖形化監控界面可以同時掌握Ameba RTL8195的sensor data及地理位置訊息
    14
    - 從Remote Monitoring傳送命令至Ameba RTL8195
    遠端監視解決方案中的儀表板,可讓您透過 IoT 中樞傳送命令至裝置。例如,在遠端監視解決方案中,您可以傳送命令來設定裝置的內部溫度。
    在遠端監視解決方案的儀表板中,按一下左面板中的 [裝置] 瀏覽至 [裝置清單]。
    在 [裝置清單] 中,按一下裝置的 [裝置識別碼]。
    在 [裝置詳細資料] 面板中,按一下 [命令]。
    15
    在 [命令] 下拉式清單中選取 [SetTemperature],然後在 [溫度] 輸入新的溫度值。按一下 [傳送命令] 將命令傳送至裝置。
    16
    此時在Ameba RTL8195端接收到的訊息應會顯示如下:
    Message arrived: [topic]
    [Messages from Remote Monitoring]
    以上在Remote Monitoring傳送命令的進一步訊息請參考Azure教學:
    https://azure.microsoft.com/zh-tw/documentation/articles/iot-suite-connecting-devices/

程式碼說明

整份程式碼使用了基本的MQTT架構,除了Azure IoT Hub的MQTT Server與SAS Token設定可以參考前面的說明之外,程式碼說明如下
本範例中使用ArduinoJson,對於需要以JSON格式包裝的devinfo及sensor data messages字串,ArduinoJson提供了更容易使用的方式來將欲發送的data包裝起來,在開始將資料publish之前利用parseObject(devinfoStr)方法,可以將既成的JSON format字串自動解析,並且將sensor data自動內化為key–value的json object:

JsonObject& devinfo = devinfoBuffer.parseObject(devicePropertiesStr);

之後如有需要變更key裡的value,如下所示即可進行變更

deviceProperties["DeviceID"] = mqttClientName;
deviceProperties["Manufacturer"] = manufacturer;
deviceProperties["Latitude"] = double_with_n_digits(latitude, 7);
deviceProperties["Longitude"] = double_with_n_digits(longitude, 7);

最後在開始publish devinfo data之前可以將欲送出的devinfo內容,整理成JSON格式輸至出console:

devinfo.prettyPrintTo(Serial);

一開始連線至AP,這部份是一般的wifi連線程式碼,沒有不同的地方

setupWiFi();

設定certificate: 這部份就有點不同,其中我們的wifiClient的型態是 WiFiSSLClient wifiSSLClient;,WiFiSSLClient繼承了Client,所以也可以當作PubSublicant的constructor參數,

PubSubClient mqttClient(wifiSSLClient);

接著MQTT PubClient設定MQTT Broker server為QOS1及傾聽的callback() function並且進行連線

mqttClient.setServer(mqttServer, mqttPort);
mqttClient.setPublishQos(MQTTQOS1);
mqttClient.setCallback(callback);

連線成功之後,註冊要傾聽的topic

mqttClient.subscribe(mqttTopic);

傾聽topic,當使用者在Remote Monitoring傳送命令至Ameba RTL8195時,做出回應

void callback(char* topic, byte* payload, unsigned int length) {  
  char buf[MQTT_MAX_PACKET_SIZE];  
  strncpy(buf, (const char *)payload, length);
  buf[length] = '\0';
  
  Serial.print(F("Message arrived: "));
  Serial.println(topic);
  Serial.println( buf);  
}