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看执行的结果
    29

    1. 第一步会先连上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);