Ameba Arduino: [RTL8195] 连线Microsoft Azure云服务

材料准备

  • Ameba 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);  
}