test

Dependencies:   MQTT

Revision:
0:9d5f28595388
diff -r 000000000000 -r 9d5f28595388 README.md
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/README.md	Wed Mar 14 03:03:16 2018 +0000
@@ -0,0 +1,334 @@
+# Example for Connection with AWS IoT thru MQTT/HTTPS on Mbed OS
+
+This is an example to demonstrate connection with [AWS IoT](https://aws.amazon.com/iot)
+on Nuvoton Mbed-enabled boards.
+
+## Supported platforms
+On Mbed OS, connection with AWS IoT requires Mbed TLS. It requires more than 64 KB RAM.
+Currently, the following Nuvoton Mbed-enalbed boards can afford such memory footprint:
+- [NuMaker-PFM-NUC472](https://developer.mbed.org/platforms/Nuvoton-NUC472/)
+- [NuMaker-PFM-M487](https://developer.mbed.org/platforms/NUMAKER-PFM-M487/)
+
+## Access and manage AWS IoT Service
+To run the example, you need to register one [AWS account](https://aws.amazon.com/)
+to access and manage AWS IoT Service for your device to connect with.
+This [link](https://docs.aws.amazon.com/iot/latest/developerguide/what-is-aws-iot.html) gives detailed
+information about it.
+
+1. Sign in to [AWS Management Console](https://aws.amazon.com/console/).
+1. Enter AWS IoT Service.
+1. In AWS IoT Service, create a thing.
+The Console may prompt you to also create a certificate and a policy. Skip for creating them later.
+1. In AWS IoT Service, create a policy. A workable example would be below.
+Note that you need to replace **REGION** and **ACCOUNT** to match your case.
+
+    <pre>
+    {
+        "Version": "2012-10-17",
+        "Statement": [
+            {
+                "Effect": "Allow",
+                "Action": "iot:Connect",
+                "Resource": "arn:aws:iot:<b>REGION</b>:<b>ACCOUNT</b>:client/*"
+            },
+            {
+                "Effect": "Allow",
+                "Action": "iot:Subscribe",
+                "Resource": ["arn:aws:iot:<b>REGION</b>:<b>ACCOUNT</b>:topicfilter/*"]
+            },
+            {
+                "Effect": "Allow",
+                "Action": ["iot:Publish", "iot:Receive"],
+                "Resource": "arn:aws:iot:<b>REGION</b>:<b>ACCOUNT</b>:topic/*"
+            },
+            {
+                "Effect": "Allow",
+                "Action": ["iot:UpdateThingShadow", "iot:GetThingShadow", "iot:DeleteThingShadow"],
+                "Resource": "arn:aws:iot:<b>REGION</b>:<b>ACCOUNT</b>:thing/*"
+            }
+        ]
+    }
+    </pre>
+
+1. In AWS IoT Service, create a certificate. You would get 4 security credential files from it.
+   Download them for later use.
+   - AWS IoT's CA certificate
+   - User certificate
+   - User private key
+   - User public key
+   
+   After creating the certificate, do:
+   1. Activate the certificate
+   1. Attach the thing created above to the certificate
+   1. Attach the policy created above to the certificate
+
+## Configure your device with AWS IoT
+Before connecting your device with AWS IoT, you need to configure security credential and
+protocol dependent parameters into your device. These configurations are all centralized in `main.cpp`.
+
+### Configure certificate into your device
+From above, you've got 4 security credential files: CA certificate and user certificate/private key/public key.
+Configure CA certificate, user certificate, and user private key into your device.
+User public key has been included in user certificate and is not used here.
+1. Replace CA certificate with downloaded from the Console.
+    ```
+    const char SSL_CA_CERT_PEM[] = "-----BEGIN CERTIFICATE-----\n"
+        "MIIE0zCCA7ugAwIBAgIQGNrRniZ96LtKIVjNzGs7SjANBgkqhkiG9w0BAQUFADCB\n"
+        "yjELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQL\n"
+    ```
+
+1. Replace user certificate with downloaded from the Console.
+    ```
+    const char SSL_USER_CERT_PEM[] = "-----BEGIN CERTIFICATE-----\n"
+        "MIIDWjCCAkKgAwIBAgIVALN/H7tr8cgpl2zwg0JjEE106XilMA0GCSqGSIb3DQEB\n"
+        "CwUAME0xSzBJBgNVBAsMQkFtYXpvbiBXZWIgU2VydmljZXMgTz1BbWF6b24uY29t\n"
+    ```
+
+1. Replace user private key with downloaded from the Console.
+    ```
+    const char SSL_USER_PRIV_KEY_PEM[] = "-----BEGIN RSA PRIVATE KEY-----\n"
+    ```
+
+**NOTE:** The credential hard-coded in source code is deactivated or deleted.
+          Use your own credential for connection with AWS IoT.
+
+### Connect through MQTT
+To connect your device with AWS IoT through MQTT, you need to configure the following parameters.
+
+1. Enable connection through MQTT.
+    ```
+    #define AWS_IOT_MQTT_TEST       1
+    ```
+
+1. Replace server name (endpoint). **Endpoint** has the following format and you just 
+   need to modify **IDENTIFIER** and **REGION** to match your case.
+    <pre>
+    #define AWS_IOT_MQTT_SERVER_NAME                "<b>IDENTIFIER</b>.iot.<b>REGION</b>.amazonaws.com"
+    </pre>
+   
+1. Server port number is fixed. Don't change it.
+    ```
+    #define AWS_IOT_MQTT_SERVER_PORT                8883
+    ```
+    
+1. Replace **THINGNAME** to match your case. The **THINGNAME** is just the name of the thing you've created above.
+    <pre>
+    #define AWS_IOT_MQTT_THINGNAME                  "<b>THINGNAME</b>"
+    </pre>
+    
+1. Replace **CLIENTNAME** to match your case. If you adopt the example policy above,
+   you can modify it arbitrarily because the policy permits any client name bound to your account.
+    <pre>
+    #define AWS_IOT_MQTT_CLIENTNAME                 "<b>CLIENTNAME</b>"
+    </pre>
+
+AWS IoT MQTT protocol supports topic subscribe/publish. The example demonstrates:
+- Subscribe/publish with user topic
+- Subscribe/publish with reserved topic (starting with $) to:
+    - Update thing shadow
+    - Get thing shadow
+    - Delete thing shadow
+
+### Connect through HTTPS
+To connect your device with AWS IoT through HTTPS, you need to configure the following parameters.
+
+1. Enable connection through HTTPS.
+    ```
+    #define AWS_IOT_HTTPS_TEST      1
+    ```
+
+1. Replace server name (endpoint). **Endpoint** has the following format and you just 
+   need to modify **IDENTIFIER** and **REGION** to match your case.
+    <pre>
+    #define AWS_IOT_HTTPS_SERVER_NAME               "<b>IDENTIFIER</b>.iot.<b>REGION</b>.amazonaws.com"
+    </pre>
+   
+1. Server port number is fixed. Don't change it.
+    ```
+    #define AWS_IOT_HTTPS_SERVER_PORT               8443
+    ```
+    
+1. Replace **THINGNAME** to match your case. The **THINGNAME** is just the name of the thing you've created above.
+    <pre>
+    #define AWS_IOT_HTTPS_THINGNAME                 "<b>THINGNAME</b>"
+    </pre>
+
+AWS IoT HTTPS protocol supports topic publish-only and RESTful API. The example demonstrates:
+- Publish to user topic
+- Publish to reserved topic (starting with $) to:
+    - Update thing shadow
+    - Get thing shadow
+    - Delete thing shadow
+- RESTful API to:
+    - Update thing shadow RESTfully through HTTPS/POST method
+    - Get thing shadow RESTfully through HTTPS/GET method
+    - Delete thing shadow RESTfully through HTTPS/DELETE method
+
+## Patch MQTT library
+Currently, MQTT library has one issue with unsubscribe from topic multiple times. Fix it:
+
+In `MQTT/MQTTClient.h` > `MQTT::Client<Network, Timer, MAX_MQTT_PACKET_SIZE, b>::cycle`,
+<pre>
+switch (packet_type)
+    {
+        default:
+            // no more data to read, unrecoverable. Or read packet fails due to unexpected network error
+            rc = packet_type;
+            goto exit;
+        case 0: // timed out reading packet
+            break;
+        case CONNACK:
+        case PUBACK:
+        case SUBACK:
+        <b>
+        case UNSUBACK:
+        </b>
+            break;
+        case PUBLISH:
+        {
+</pre>
+
+## Monitor the application
+If you configure your terminal program with **9600/8-N-1**, you would see output similar to:
+
+**NOTE:** Make sure that the network is functional before running the application.
+
+<pre>
+Starting AWS IoT test
+Using Mbed OS 5.7.1
+[EasyConnect] IPv4 mode
+Connecting with a1fbcwaqfqeozo.iot.us-east-1.amazonaws.com:8883
+Connecting to a1fbcwaqfqeozo.iot.us-east-1.amazonaws.com:8883
+</pre>
+
+If you get here successfully, it means configurations with security credential are correct.
+<pre>
+Starting the TLS handshake...
+TLS connection to a1fbcwaqfqeozo.iot.us-east-1.amazonaws.com:8883 established
+Server certificate:
+    cert. version     : 3
+    serial number     : 3C:AC:B3:D3:3E:D8:6A:C9:2B:EF:D2:C5:B1:DC:BF:66
+    issuer name       : C=US, O=Symantec Corporation, OU=Symantec Trust Network, CN=Symantec Class 3 ECC 256 bit SSL CA - G2
+    subject name      : C=US, ST=Washington, L=Seattle, O=Amazon.com, Inc., CN=*.iot.us-east-1.amazonaws.com
+    issued  on        : 2017-03-07 00:00:00
+    expires on        : 2018-03-08 23:59:59
+    signed using      : ECDSA with SHA256
+    EC key size       : 256 bits
+    basic constraints : CA=false
+    subject alt name  : iot.us-east-1.amazonaws.com, *.iot.us-east-1.amazonaws.com
+    key usage         : Digital Signature
+    ext key usage     : TLS Web Server Authentication, TLS Web Client Authentication
+Certificate verification passed
+
+Connects with a1fbcwaqfqeozo.iot.us-east-1.amazonaws.com:8883 OK
+</pre>
+
+MQTT handshake goes:
+<pre>
+MQTT connects OK
+
+Subscribing/publishing user topic
+MQTT subscribes to Nuvoton/Mbed/+ OK
+Message to publish:
+{ "message": "Hello from Nuvoton Mbed device" }
+MQTT publishes message to Nuvoton/Mbed/D001 OK
+Message arrived: qos 1, retained 0, dup 0, packetid 1
+Payload:
+{ "message": "Hello from Nuvoton Mbed device" }
+
+MQTT unsubscribes from Nuvoton/Mbed/+ OK
+Subscribes/publishes user topic OK
+
+Subscribing/publishing UpdateThingShadow topic
+MQTT subscribes to $aws/things/Nuvoton-Mbed-D001/shadow/update/accepted OK
+MQTT subscribes to $aws/things/Nuvoton-Mbed-D001/shadow/update/rejected OK
+Message to publish:
+{ "state": { "reported": { "attribute1": 3, "attribute2": "1" } } }
+MQTT publishes message to $aws/things/Nuvoton-Mbed-D001/shadow/update OK
+Message arrived: qos 1, retained 0, dup 0, packetid 1
+Payload:
+{"state":{"reported":{"attribute1":3,"attribute2":"1"}},"metadata":{"reported":{"attribute1":{"timestamp":1514962195},"attribute2":{"timestamp":1514962195}}},"version":77,"timestamp":1514962195}
+
+MQTT unsubscribes from $aws/things/Nuvoton-Mbed-D001/shadow/update/accepted OK
+MQTT unsubscribes from $aws/things/Nuvoton-Mbed-D001/shadow/update/rejected OK
+Subscribes/publishes UpdateThingShadow topic OK
+
+Subscribing/publishing GetThingShadow topic
+MQTT subscribes to $aws/things/Nuvoton-Mbed-D001/shadow/get/accepted OK
+MQTT subscribes to $aws/things/Nuvoton-Mbed-D001/shadow/get/rejected OK
+Message to publish:
+
+MQTT publishes message to $aws/things/Nuvoton-Mbed-D001/shadow/get OK
+Message arrived: qos 1, retained 0, dup 0, packetid 1
+Payload:
+{"state":{"reported":{"attribute1":3,"attribute2":"1"}},"metadata":{"reported":{"attribute1":{"timestamp":1514962195},"attribute2":{"timestamp":1514962195}}},"version":77,"timestamp":1514962198}
+
+MQTT unsubscribes from $aws/things/Nuvoton-Mbed-D001/shadow/get/accepted OK
+MQTT unsubscribes from $aws/things/Nuvoton-Mbed-D001/shadow/get/rejected OK
+Subscribes/publishes GetThingShadow topic OK
+
+Subscribing/publishing DeleteThingShadow topic
+MQTT subscribes to $aws/things/Nuvoton-Mbed-D001/shadow/delete/accepted OK
+MQTT subscribes to $aws/things/Nuvoton-Mbed-D001/shadow/delete/rejected OK
+Message to publish:
+
+MQTT publishes message to $aws/things/Nuvoton-Mbed-D001/shadow/delete OK
+Message arrived: qos 1, retained 0, dup 0, packetid 1
+Payload:
+{"version":77,"timestamp":1514962202}
+
+MQTT unsubscribes from $aws/things/Nuvoton-Mbed-D001/shadow/delete/accepted OK
+MQTT unsubscribes from $aws/things/Nuvoton-Mbed-D001/shadow/delete/rejected OK
+Subscribes/publishes DeleteThingShadow topic OK
+
+MQTT disconnects OK
+</pre>
+
+Dynamic memory footprint (heap) is output below.
+Static memory footprint (global/stack) could be obtained by inspecting MAP file.
+You could get total memory footprint by adding these two together.
+<pre>
+Current heap size: 1351
+Max heap size: 63022
+</pre>
+
+## Trouble-shooting
+- Over ESP8266 WiFi,
+  if you make a loop test like below (`main.cpp`), you may always meet errors in the following loops
+  after some network error has happened in the previous one.
+    <pre>
+    <b>while (true) {</b>
+        #if AWS_IOT_MQTT_TEST
+            AWS_IoT_MQTT_Test *mqtt_test = new AWS_IoT_MQTT_Test(AWS_IOT_MQTT_SERVER_NAME, AWS_IOT_MQTT_SERVER_PORT, network);
+            mqtt_test->start_test();
+            delete mqtt_test;
+        #endif  // End of AWS_IOT_MQTT_TEST
+    
+        #if AWS_IOT_HTTPS_TEST
+            AWS_IoT_HTTPS_Test *https_test = new AWS_IoT_HTTPS_Test(AWS_IOT_HTTPS_SERVER_NAME, AWS_IOT_HTTPS_SERVER_PORT, network);
+            https_test->start_test();
+            delete https_test;
+        #endif  // End of AWS_IOT_HTTPS_TEST
+    <b>}</b>
+    </pre>
+    This issue would be caused by failure of ESP8266 AT commands **CLOSE**/**DISCONNECT**
+    because ESP8266 F/W is still busy in handling previous unfinished network transfer
+    due to bad network status and fails these commands.
+    These commands must be OK for ESP8266 F/W to reset connection state correctly.
+    If that happens, try enlarging [ESP8266 driver's](https://github.com/ARMmbed/esp8266-driver) timeout configuration.
+    For example, enlarge `ESP8266_SEND_TIMEOUT`/`ESP8266_RECV_TIMEOUT`/`ESP8266_MISC_TIMEOUT` (defined in
+    [ESP8266Interface.cpp](https://github.com/ARMmbed/esp8266-driver/blob/master/ESP8266Interface.cpp))
+    to 5000/5000/5000 ms respectively (through `mbed_app.json`).
+    <pre>
+    {
+        "macros": [
+            "MBED_CONF_APP_MAIN_STACK_SIZE=4096",
+            "MBEDTLS_USER_CONFIG_FILE=\"mbedtls_user_config.h\"",
+            "MBED_HEAP_STATS_ENABLED=1",
+            "MBED_MEM_TRACING_ENABLED=1",
+            <b>"ESP8266_SEND_TIMEOUT=5000",</b>
+            <b>"ESP8266_RECV_TIMEOUT=5000",</b>
+            <b>"ESP8266_MISC_TIMEOUT=5000"</b>
+        ],
+        "config": {
+    </pre>
\ No newline at end of file