├── .github ├── ISSUE_TEMPLATE │ └── bug_report.md └── workflows │ └── main.yml ├── CMakeLists.txt ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── aws-cert-converter.py ├── aws_credentials.h ├── demo_mqtt.cpp ├── demo_shadow.cpp ├── main.cpp ├── mbed-client-for-aws.lib ├── mbed-os.lib ├── mbed_app.json ├── resources └── official_armmbed_example_badge.png ├── tests ├── README.md └── aws.log └── wifi-ism43362.lib /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: 'type: bug' 6 | assignees: '' 7 | 8 | --- 9 | 10 | 28 | 29 | ### Description of defect 30 | 31 | 35 | 36 | 37 | #### Target(s) affected by this defect ? 38 | 39 | 40 | #### Toolchain(s) (name and version) displaying this defect ? 41 | 42 | 43 | #### What version of Mbed-os are you using (tag or sha) ? 44 | 53 | 54 | 55 | #### What version(s) of tools are you using. List all that apply (E.g. mbed-cli) 56 | 57 | 58 | #### How is this defect reproduced ? 59 | -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: Build example application 2 | 3 | on: 4 | pull_request: 5 | push: 6 | 7 | jobs: 8 | build-cli-v1: 9 | container: 10 | image: ghcr.io/armmbed/mbed-os-env:master-latest 11 | 12 | runs-on: ubuntu-latest 13 | 14 | strategy: 15 | matrix: 16 | target: [K64F, DISCO_L475VG_IOT01A] 17 | profile: [release, debug, develop] 18 | 19 | 20 | steps: 21 | - 22 | name: Checkout 23 | uses: actions/checkout@v2 24 | 25 | - 26 | name: build-example 27 | run: | 28 | set -e 29 | mbed deploy 30 | mbed compile -t GCC_ARM -m ${{ matrix.target }} --profile ${{ matrix.profile }} 31 | 32 | 33 | build-cli-v2: 34 | container: 35 | image: ghcr.io/armmbed/mbed-os-env:master-latest 36 | 37 | runs-on: ubuntu-latest 38 | 39 | strategy: 40 | matrix: 41 | target: [K64F, DISCO_L475VG_IOT01A] 42 | profile: [release, debug, develop] 43 | 44 | 45 | steps: 46 | - 47 | name: Checkout 48 | uses: actions/checkout@v2 49 | 50 | - 51 | name: build-example-application 52 | run: | 53 | set -e 54 | mbed-tools deploy 55 | mbed-tools compile -t GCC_ARM -m ${{ matrix.target }} --profile ${{ matrix.profile }} 56 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2020 ARM Limited. All rights reserved. 2 | # SPDX-License-Identifier: Apache-2.0 3 | 4 | cmake_minimum_required(VERSION 3.19.0 FATAL_ERROR) 5 | 6 | set(MBED_PATH ${CMAKE_CURRENT_SOURCE_DIR}/mbed-os CACHE INTERNAL "") 7 | set(MBED_CONFIG_PATH ${CMAKE_CURRENT_BINARY_DIR} CACHE INTERNAL "") 8 | set(APP_TARGET mbed-os-example-for-aws) 9 | 10 | include(${MBED_PATH}/tools/cmake/app.cmake) 11 | 12 | project(${APP_TARGET}) 13 | 14 | add_subdirectory(${MBED_PATH}) 15 | add_subdirectory(mbed-client-for-aws) 16 | 17 | if("-DMBED_CONF_ISM43362_PROVIDE_DEFAULT=1" IN_LIST MBED_CONFIG_DEFINITIONS) 18 | add_subdirectory(wifi-ism43362) 19 | endif() 20 | 21 | add_executable(${APP_TARGET}) 22 | 23 | mbed_configure_app_target(${APP_TARGET}) 24 | 25 | target_include_directories(${APP_TARGET} 26 | PRIVATE 27 | . 28 | ) 29 | 30 | target_sources(${APP_TARGET} 31 | PRIVATE 32 | main.cpp 33 | demo_mqtt.cpp 34 | demo_shadow.cpp 35 | ) 36 | 37 | if("-DMBED_CONF_ISM43362_PROVIDE_DEFAULT=1" IN_LIST MBED_CONFIG_DEFINITIONS) 38 | target_link_libraries(${APP_TARGET} 39 | PRIVATE 40 | wifi-ism43362 41 | ) 42 | endif() 43 | 44 | target_link_libraries(${APP_TARGET} 45 | PRIVATE 46 | mbed-os 47 | mbed-client-for-aws 48 | ) 49 | 50 | mbed_set_post_build(${APP_TARGET}) 51 | 52 | option(VERBOSE_BUILD "Have a verbose build process") 53 | if(VERBOSE_BUILD) 54 | set(CMAKE_VERBOSE_MAKEFILE ON) 55 | endif() 56 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to Mbed OS 2 | 3 | Mbed OS is an open-source, device software platform for the Internet of Things. Contributions are an important part of the platform, and our goal is to make it as simple as possible to become a contributor. 4 | 5 | To encourage productive collaboration, as well as robust, consistent and maintainable code, we have a set of guidelines for [contributing to Mbed OS](https://os.mbed.com/docs/mbed-os/latest/contributing/index.html). 6 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![](./resources/official_armmbed_example_badge.png) 2 | 3 | # Mbed OS example for AWS cloud 4 | 5 | The example project is part of the [Arm Mbed OS Official Examples](https://os.mbed.com/code/). It contains two demos: 6 | * MQTT (default): publishes a message every 1 second for 10 messages or until a message is received. 7 | * Device Shadow service: retrieves a desired value from the cloud, then reports a string value and then an integer value to the cloud. 8 | 9 | You can build the project with all supported [Mbed OS build tools](https://os.mbed.com/docs/mbed-os/latest/tools/index.html). However, this example project specifically refers to the command-line interface tool [Arm Mbed CLI](https://github.com/ARMmbed/mbed-cli#installing-mbed-cli). 10 | (Note: To see a rendered example you can import into the Arm Online Compiler, please see our [import quick start](https://os.mbed.com/docs/mbed-os/latest/quick-start/online-with-the-online-compiler.html#importing-the-code).) 11 | 12 | ## Mbed OS build tools 13 | 14 | ### Mbed CLI 2 15 | Starting with version 6.5, Mbed OS uses Mbed CLI 2. It uses Ninja as a build system, and CMake to generate the build environment and manage the build process in a compiler-independent manner. If you are working with Mbed OS version prior to 6.5 then check the section [Mbed CLI 1](#mbed-cli-1). 16 | 1. [Install Mbed CLI 2](https://os.mbed.com/docs/mbed-os/latest/build-tools/install-or-upgrade.html). 17 | 1. From the command-line, import the example: `mbed-tools import mbed-os-example-for-aws` 18 | 1. Change the current directory to where the project was imported. 19 | 20 | ### Mbed CLI 1 21 | 1. [Install Mbed CLI 1](https://os.mbed.com/docs/mbed-os/latest/quick-start/offline-with-mbed-cli.html). 22 | 1. From the command-line, import the example: `mbed import mbed-os-example-for-aws` 23 | 1. Change the current directory to where the project was imported. 24 | 25 | ## Configuring the AWS IoT Core service 26 | 27 | 1. Create an AWS account if you don't have one, and login to it. 28 | 29 | __NOTE:__ If you have an admin for your AWS account, please contact them to add a user to the account. You should obtain your login credentials from your admin in this case. 30 | 31 | 1. Set up device credentials and policy via the AWS IoT console. You can refer to the AWS documentation [here](https://docs.aws.amazon.com/iot/latest/developerguide/iot-gs.html). Follow the steps there to 32 | 33 | * create a thing 34 | * generate a device certificate and keys 35 | * create an IoT policy and attach that policy to your device. 36 | 37 | Also download "Amazon Root CA 1" from [here](https://docs.aws.amazon.com/iot/latest/developerguide/server-authentication.html#server-authentication-certs). 38 | 39 | Once you have downloaded the credentials, you will need to place them in the [`aws_credentials.h`](./aws_credentials.h) file of this example. 40 | 41 | The example includes a python script to automate converting the credentials you downloaded from AWS into C-compatible arrays/strings. First, create a new folder in the project to store your credential files, eg: `mkdir aws-credentials`. Copy the previously-downloaded credential files into this subdirectory. 42 | 43 | Then, you can run the script to automatically generate the necessary code from the credentials: 44 | 45 | ``` 46 | python aws-cert-converter.py aws-credentials 47 | ``` 48 | 49 | For more details on how to use the convert script, simply pass in the `-h` flag to print the help documentation. 50 | 51 | The above command will read your credential files and place them into a C header file for you: `aws_credentials.h` 52 | 53 | 1. Once you have created the "thing", you will need to obtain the custom endpoint URL from the console. At the time of writing this document, you can find it under "Settings" in the IoT console. 54 | 55 | In [`mbed_app.json`](./mbed_app.json) file, set `aws-endpoint.value` to be that of the custom endpoint. 56 | 57 | 1. Set a topic that both your device and the cloud can publish messages to. 58 | 59 | In [`mbed_app.json`](./mbed_app.json) file, set `aws-mqtt-topic.value` to a name you prefer, or use the default one. On the AWS console, you will then need to subscribe to the same topic name. At the time of writing this document, you can find this under "Test" on the console. 60 | 61 | 1. Give your device a name by setting `aws-client-identifier` in [`mbed_app.json`](./mbed_app.json). 62 | 63 | ## Building and running 64 | 65 | ### MQTT demo (default) 66 | 67 | 1. If using WiFi (e.g. on DISCO_L475VG_IOT01A), enter your network's SSID and password in [`mbed_app.json`](./mbed_app.json) (see [here](https://github.com/ARMmbed/mbed-os-example-wifi/blob/master/README.md#getting-started)). Keep any existing `\"`s. (If you use a different WiFi-enabled target, you may need to manually import its WiFi driver as described [here](https://github.com/ARMmbed/mbed-os-example-wifi#supported-hardware).) 68 | 69 | 1. For Ethernet (e.g on K64F), connect a cable to the port. 70 | 71 | 1. Connect a USB cable between the USB port on the board and the host computer. 72 | 73 | 1. Run the following command to build the example project, program the microcontroller flash memory, and open a serial terminal: 74 | 75 | * Mbed CLI 2 76 | 77 | ```bash 78 | $ mbed-tools compile -m -t --flash --sterm --baudrate=115200 79 | ``` 80 | 81 | * Mbed CLI 1 82 | 83 | ```bash 84 | $ mbed compile -m -t --flash --sterm --baudrate=115200 85 | ``` 86 | 87 | Alternatively, you can manually copy the binary to the board, which you mount on the host computer over USB. 88 | The binary is located at: 89 | * **Mbed CLI 2** - `./cmake_build////mbed-os-example-for-aws.bin`
90 | * **Mbed CLI 1** - `./BUILD///mbed-os-example-for-aws.bin` 91 | 92 | Depending on the target, you can build the example project with the `GCC_ARM` or `ARM` toolchain. Run the command below to determine which toolchain supports your target: 93 | 94 | ``` 95 | $ mbed compile -S 96 | ``` 97 | 98 | ### Device Shadow demo 99 | 100 | Before running this demo, go to the AWS IoT Core console. In _Test_ -> _Subscribe to a topic_, subscribe to `$aws/things//shadow/#` (the `#` is a wildcard) on the AWS to monitor all Device Shadow updates. Then in _Publish to a topic_, publish to `$aws/things//shadow/update` the following payload: 101 | 102 | ```json 103 | { 104 | "state": { 105 | "desired": { 106 | "DemoNumber": 200 107 | } 108 | } 109 | } 110 | ``` 111 | 112 | Build with the same steps as the MQTT demo, but also set `aws-client.shadow` to `true` in [`mbed_app.json`](./mbed_app.json) before compilation. 113 | 114 | ## Expected output 115 | 116 | ### MQTT demo (default) 117 | 118 | Once the example starts to run, you can [monitor a serial terminal](https://os.mbed.com/docs/mbed-os/v6.0/tutorials/serial-comm.html) to see that the device connects to your network, exchanges some TLS handshakes, connects to AWS and publishes to the topic you just subscribed. This can be seen on the AWS console as incoming messages. 119 | 120 | The application publishes a message every second for 10 seconds, or until it receives a message from the cloud: 121 | ``` 122 | [INFO][Main]: Publishing "10 messages left to send, or until we receive a reply" to topic 123 | [INFO][Main]: Message sent successfully 124 | [INFO][Main]: Publishing "9 messages left to send, or until we receive a reply" to topic 125 | [INFO][Main]: Message sent successfully 126 | [INFO][Main]: Publishing "8 messages left to send, or until we receive a reply" to topic 127 | [INFO][Main]: Message sent successfully 128 | ``` 129 | 130 | You can send a message to your device via the AWS IoT Core console (_Test_ -> _Publish to a topic_). Use the same topic name you set in `aws-mqtt-topic.value` in [`mbed_app.json`](./mbed_app.json). Use the existing JSON structure and set "message" to one you want to send. 131 | 132 | On receipt of a message, the application displays on the console the message you sent via the AWS IoT Core console. 133 | 134 | ### Device Shadow demo 135 | 136 | The following will be printed to the serial: 137 | ``` 138 | [INFO][Main]: Device Shadow document downloaded 139 | [INFO][Main]: Desired value of DemoNumber: 140 | [INFO][Main]: Device Shadow reported string value published 141 | [INFO][Main]: Device Shadow reported integer value published 142 | ``` 143 | 144 | ## Troubleshooting 145 | If you have problems, you can review the [documentation](https://os.mbed.com/docs/latest/tutorials/debugging.html) for suggestions on what could be wrong and how to fix it. 146 | 147 | ## Related Links 148 | 149 | * [Mbed client for AWS IoT Core](https://github.com/ARMmbed/mbed-client-for-aws) 150 | * [AWS IoT Core](https://aws.amazon.com/iot-core/) 151 | * [Mbed OS Configuration](https://os.mbed.com/docs/latest/reference/configuration.html). 152 | * [Mbed OS Serial Communication](https://os.mbed.com/docs/latest/tutorials/serial-communication.html). 153 | * [Mbed boards](https://os.mbed.com/platforms/). 154 | 155 | ### License and contributions 156 | 157 | The software is provided under Apache-2.0 license. Contributions to this project are accepted under the same license. Please see contributing.md for more info. 158 | 159 | This project contains code from other projects. The original license text is included in those source files. They must comply with our license guide. 160 | -------------------------------------------------------------------------------- /aws-cert-converter.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import argparse 4 | from os import walk 5 | 6 | # If modifying the template below, 7 | # make sure to double escape newlines you want in the C-string (ie: '\\n') 8 | # and to double-up brackets ('{' and '}' become '{{' and '}}') or else 9 | # formatting of the template string will fail 10 | header_template = ( 11 | """ 12 | // AWS Certificates 13 | 14 | #ifndef AWS_CREDENTIALS_H 15 | #define AWS_CREDENTIALS_H 16 | 17 | namespace aws {{ 18 | namespace credentials {{ 19 | /* 20 | * PEM-encoded root CA certificate 21 | */ 22 | const char rootCA[] = 23 | {0}; 24 | 25 | /* 26 | * PEM-encoded client certificate 27 | */ 28 | const char clientCrt[] = 29 | {1}; 30 | 31 | /* 32 | * PEM-encoded client private key. 33 | */ 34 | const char clientKey[] = 35 | {2}; 36 | 37 | }} 38 | }} 39 | 40 | #endif 41 | """ 42 | ) 43 | 44 | default_out_file_name = "aws_credentials.h" 45 | 46 | # Formats the given file into a C-string with newlines 47 | def format_file(file_path): 48 | out = '' 49 | with open(file_path, 'r') as f: 50 | lines = f.readlines() 51 | for l in lines: 52 | # Do not add a newline to the last line 53 | line_format = '"{}\\n"\n' 54 | if l is lines[-1]: 55 | line_format = '"{}"\n' 56 | out = out + line_format.format(l.strip()) 57 | return out 58 | 59 | def main(): 60 | parser = argparse.ArgumentParser(description='Convert AWS credential files to a compatible C-style header format') 61 | parser.add_argument('certs_directory', metavar='d', help='Path to directory containing credential files downloaded from AWS IoT Console') 62 | global default_out_file_name 63 | parser.add_argument('--output-file', help='Output file name (optional, defaults to: {}'.format(default_out_file_name)) 64 | args = parser.parse_args() 65 | 66 | # Walk the given directory to find the cert files we need 67 | f = [] 68 | for (dirpath, dirnames, filenames) in walk(args.certs_directory): 69 | f.extend(filenames) 70 | break 71 | 72 | # These are the files we need, make sure they exist 73 | root_ca_file = None 74 | client_crt_file = None 75 | client_key_file = None 76 | for cert_file in f: 77 | if "root" in cert_file.lower(): 78 | root_ca_file = args.certs_directory + "/" + cert_file 79 | elif ".pem.crt" in cert_file: 80 | client_crt_file = args.certs_directory + "/" + cert_file 81 | elif "private.pem.key" in cert_file: 82 | client_key_file = args.certs_directory + "/" + cert_file 83 | 84 | if root_ca_file: 85 | print("Root CA file found: {}".format(root_ca_file)) 86 | else: 87 | print("Could not find Root CA file (eg: AmazonRootCA1.pem)!") 88 | exit() 89 | 90 | if client_crt_file: 91 | print("Client certificate file found: {}".format(client_crt_file)) 92 | else: 93 | print("Could not find client certificate file (eg: xxxxxxxxxx-certificate.pem.crt)!") 94 | exit() 95 | 96 | if client_key_file: 97 | print("Client key file found: {}".format(client_key_file)) 98 | else: 99 | print("Could not find client private key file (eg: xxxxxxxxxx-private.pem.key)!") 100 | exit() 101 | 102 | print("Generating C-style header from given credentials...") 103 | 104 | global header_template 105 | out_file_name = default_out_file_name 106 | if args.output_file: 107 | out_file_name = args.output_file 108 | 109 | with open(out_file_name, 'w+') as out: 110 | out.write(header_template.format(format_file(root_ca_file),\ 111 | format_file(client_crt_file),\ 112 | format_file(client_key_file))) 113 | 114 | print("Saved generated credentials header to {}!".format(out_file_name)) 115 | 116 | if __name__ == "__main__": 117 | main() 118 | -------------------------------------------------------------------------------- /aws_credentials.h: -------------------------------------------------------------------------------- 1 | /* 2 | * AWS Certificates 3 | * Copyright (c) 2020 Arm Limited 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | 8 | #ifndef AWS_CREDENTIALS_H 9 | #define AWS_CREDENTIALS_H 10 | 11 | namespace aws { 12 | namespace credentials { 13 | /* 14 | * PEM-encoded root CA certificate 15 | * 16 | * Must include the PEM header and footer, 17 | * and every line of the body needs to be quoted and end with \n: 18 | * "-----BEGIN CERTIFICATE-----\n" 19 | * "...base64 data...\n" 20 | * "-----END CERTIFICATE-----"; 21 | */ 22 | const char rootCA[] = "-----BEGIN CERTIFICATE-----\n" 23 | "...\n" 24 | "...\n" 25 | "...\n" 26 | "-----END CERTIFICATE-----"; 27 | 28 | /* 29 | * PEM-encoded client certificate 30 | * 31 | * Must include the PEM header and footer, 32 | * and every line of the body needs to be quoted and end with \n: 33 | * "-----BEGIN CERTIFICATE-----\n" 34 | * "...base64 data...\n" 35 | * "-----END CERTIFICATE-----"; 36 | */ 37 | const char clientCrt[] = "-----BEGIN CERTIFICATE-----\n" 38 | "...\n" 39 | "...\n" 40 | "...\n" 41 | "-----END CERTIFICATE-----"; 42 | 43 | /* 44 | * PEM-encoded client private key. 45 | * 46 | * Must include the PEM header and footer, 47 | * and every line of the body needs to be quoted and end with \n: 48 | * "-----BEGIN RSA PRIVATE KEY-----\n" 49 | * "...base64 data...\n" 50 | * "-----END RSA PRIVATE KEY-----"; 51 | */ 52 | const char clientKey[] = "-----BEGIN RSA PRIVATE KEY-----\n" 53 | "...\n" 54 | "...\n" 55 | "...\n" 56 | "-----END RSA PRIVATE KEY-----"; 57 | 58 | } 59 | } 60 | 61 | #endif 62 | -------------------------------------------------------------------------------- /demo_mqtt.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020-2021 Arm Limited 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | #if !MBED_CONF_AWS_CLIENT_SHADOW 7 | 8 | #include "mbed.h" 9 | #include "mbed-trace/mbed_trace.h" 10 | #include "rtos/ThisThread.h" 11 | #include "AWSClient/AWSClient.h" 12 | 13 | extern "C" { 14 | #include "core_json.h" 15 | } 16 | 17 | #define TRACE_GROUP "Main" 18 | 19 | static bool reply_received = false; 20 | 21 | // Callback when a MQTT message has been added to the topic 22 | void on_message_callback( 23 | const char *topic, 24 | uint16_t topic_length, 25 | const void *payload, 26 | size_t payload_length) 27 | { 28 | char *json_value; 29 | size_t value_length; 30 | auto ret = JSON_Search((char *)payload, payload_length, "sender", strlen("sender"), &json_value, &value_length); 31 | if (ret == JSONSuccess && (strncmp(json_value, "device", strlen("device")) == 0)) { 32 | tr_info("Message sent successfully"); 33 | } else { 34 | ret = JSON_Search((char *)payload, payload_length, "message", strlen("message"), &json_value, &value_length); 35 | if (ret == JSONSuccess) { 36 | reply_received = true; 37 | tr_info("Message received from the cloud: \"%.*s\"", value_length, json_value); 38 | } else { 39 | tr_error("Failed to extract message from the payload: \"%.*s\"", payload_length, (const char *) payload); 40 | } 41 | } 42 | } 43 | 44 | void demo() 45 | { 46 | AWSClient &client = AWSClient::getInstance(); 47 | 48 | // Subscribe to the topic 49 | const char topic[] = MBED_CONF_APP_AWS_MQTT_TOPIC; 50 | int ret = client.subscribe(topic, strlen(topic)); 51 | if (ret != MBED_SUCCESS) { 52 | tr_error("AWSClient::subscribe() failed"); 53 | return; 54 | } 55 | 56 | // Send ten message to the cloud (one per second) 57 | // Stop when we receive a cloud-to-device message 58 | char payload[128]; 59 | for (int i = 0; i < 10; i++) { 60 | if (reply_received) { 61 | // If we have received a message from the cloud, don't send more messeges 62 | break; 63 | } 64 | 65 | // The MQTT protocol does not distinguish between senders, 66 | // so we add a "sender" attribute to the payload 67 | const char base_message[] = "messages left to send, or until we receive a reply"; 68 | sprintf(payload, "{\n" 69 | " \"sender\": \"device\",\n" 70 | " \"message\": \"%d %s\"\n" 71 | "}", 72 | 10 - i, base_message); 73 | tr_info("Publishing \"%d %s\" to topic \"%s\"", 10 - i, base_message, topic); 74 | ret = client.publish( 75 | topic, 76 | strlen(topic), 77 | payload, 78 | strlen(payload) 79 | ); 80 | if (ret != MBED_SUCCESS) { 81 | tr_error("AWSClient::publish() failed"); 82 | goto unsubscribe; 83 | } 84 | 85 | rtos::ThisThread::sleep_for(1s); 86 | } 87 | 88 | // If the user didn't manage to send a cloud-to-device message earlier, 89 | // let's wait until we receive one 90 | while (!reply_received) { 91 | // Continue to receive messages in the communication thread 92 | // which is internally created and maintained by the Azure SDK. 93 | sleep(); 94 | } 95 | 96 | unsubscribe: 97 | // Unsubscribe from the topic 98 | ret = client.unsubscribe(topic, strlen(topic)); 99 | if (ret != MBED_SUCCESS) { 100 | tr_error("AWSClient::unsubscribe() failed"); 101 | } 102 | } 103 | 104 | #endif // !MBED_CONF_AWS_CLIENT_SHADOW -------------------------------------------------------------------------------- /demo_shadow.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020-2021 Arm Limited 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | #if MBED_CONF_AWS_CLIENT_SHADOW 7 | 8 | #include "mbed.h" 9 | #include "mbed-trace/mbed_trace.h" 10 | #include "AWSClient/AWSClient.h" 11 | 12 | extern "C" { 13 | #include "core_json.h" 14 | } 15 | 16 | #define TRACE_GROUP "Main" 17 | 18 | // This should not be called for the Device Shadow demo - 19 | // Device Shadow messages are internally consumed by the client 20 | void on_message_callback( 21 | const char *topic, 22 | uint16_t topic_length, 23 | const void *payload, 24 | size_t payload_length) 25 | { 26 | tr_warning( 27 | "Message received on non-shadow topic: %.*s payload: %.*s", 28 | topic_length, 29 | topic, 30 | (const char *) payload_length, 31 | payload 32 | ); 33 | } 34 | 35 | void demo() 36 | { 37 | AWSClient &client = AWSClient::getInstance(); 38 | 39 | // Download shadow document 40 | auto ret = client.downloadShadowDocument(); 41 | if (ret == MBED_SUCCESS) { 42 | tr_info("Device Shadow document downloaded"); 43 | } else { 44 | tr_error("AWSClient::downloadShadowDocument() failed: %d", ret); 45 | return; // cannot continue without downloadShadowDocument() 46 | } 47 | 48 | // Get a desired shadow value 49 | const char shadow_demo_int_key[] = "DemoNumber"; 50 | const int shadow_demo_int_value = 100; 51 | char *shadow_value; 52 | size_t shadow_value_length; 53 | ret = client.getShadowDesiredValue( 54 | shadow_demo_int_key, 55 | strlen(shadow_demo_int_key), 56 | &shadow_value, 57 | &shadow_value_length 58 | ); 59 | if (ret == MBED_SUCCESS) { 60 | tr_info("Desired value of %s: %.*s", shadow_demo_int_key, shadow_value_length, shadow_value); 61 | } else { 62 | tr_error( 63 | "AWSClient::getShadowDesiredValue() failed: %d, " 64 | "please ensure you have set a desired value for %s (e.g. using the AWS Console)", 65 | ret, 66 | shadow_demo_int_key 67 | ); 68 | } 69 | 70 | // Report a string shadow value 71 | const char shadow_demo_string_key[] = "DemoName"; 72 | const char shadow_demo_string_value[] = "mbed-os-example-for-aws"; 73 | ret = client.publishShadowReportedValue( 74 | shadow_demo_string_key, 75 | strlen(shadow_demo_string_key), 76 | shadow_demo_string_value, 77 | strlen(shadow_demo_string_value) 78 | ); 79 | if (ret == MBED_SUCCESS) { 80 | tr_info("Device Shadow reported string value published"); 81 | } else { 82 | tr_error("AWSClient::publishShadowReportedValue() failed: %d", ret); 83 | } 84 | 85 | // Report an integer shadow value 86 | ret = client.publishShadowReportedValue( 87 | shadow_demo_int_key, 88 | strlen(shadow_demo_int_key), 89 | shadow_demo_int_value 90 | ); 91 | if (ret == MBED_SUCCESS) { 92 | tr_info("Device Shadow reported integer value published"); 93 | } else { 94 | tr_error("AWSClient::publishShadowReportedValue() failed: %d", ret); 95 | } 96 | } 97 | 98 | #endif // MBED_CONF_AWS_CLIENT_SHADOW -------------------------------------------------------------------------------- /main.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020-2021 Arm Limited 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | #include "mbed.h" 7 | #include "mbed-trace/mbed_trace.h" 8 | #include "rtos/Mutex.h" 9 | #include "rtos/Thread.h" 10 | #include "rtos/ThisThread.h" 11 | #include "AWSClient/AWSClient.h" 12 | #include "aws_credentials.h" 13 | 14 | extern "C" { 15 | #include "core_json.h" 16 | } 17 | 18 | #define TRACE_GROUP "Main" 19 | 20 | // Implemented by the two demos 21 | void on_message_callback( 22 | const char *topic, 23 | uint16_t topic_length, 24 | const void *payload, 25 | size_t payload_length 26 | ); 27 | void demo(); 28 | 29 | rtos::Mutex connection_mutex; 30 | 31 | // Task to process MQTT responses at a regular interval 32 | static void process_responses() 33 | { 34 | AWSClient &client = AWSClient::getInstance(); 35 | while (true) { 36 | connection_mutex.lock(); // avoid AWSClient::disconnect() while processing 37 | if (!client.isConnected()) { 38 | connection_mutex.unlock(); 39 | break; 40 | } 41 | if (client.processResponses() != MBED_SUCCESS) { 42 | tr_error("AWSClient::processResponses() failed"); 43 | } 44 | connection_mutex.unlock(); 45 | rtos::ThisThread::sleep_for(10ms); 46 | } 47 | } 48 | 49 | int main() 50 | { 51 | // "goto" requires early initialization of variables 52 | AWSClient &client = AWSClient::getInstance(); 53 | rtos::Thread process_thread; 54 | AWSClient::TLSCredentials_t credentials; 55 | int ret; 56 | 57 | mbed_trace_init(); 58 | tr_info("Connecting to the network..."); 59 | auto network = NetworkInterface::get_default_instance(); 60 | if (network == NULL) { 61 | tr_error("No network interface found"); 62 | goto end; 63 | } 64 | ret = network->connect(); 65 | if (ret != 0) { 66 | tr_error("Connection error: %x", ret); 67 | goto end; 68 | } 69 | tr_info("MAC: %s", network->get_mac_address()); 70 | tr_info("Connection Success"); 71 | 72 | // Set credentials 73 | credentials.clientCrt = aws::credentials::clientCrt; 74 | credentials.clientCrtLen = sizeof(aws::credentials::clientCrt); 75 | credentials.clientKey = aws::credentials::clientKey; 76 | credentials.clientKeyLen = sizeof(aws::credentials::clientKey); 77 | credentials.rootCrtMain = aws::credentials::rootCA; 78 | credentials.rootCrtMainLen = sizeof(aws::credentials::rootCA); 79 | 80 | // Initialize client 81 | ret = client.init( 82 | on_message_callback, 83 | credentials 84 | ); 85 | if (ret != MBED_SUCCESS) { 86 | tr_error("AWSClient::init() failed"); 87 | goto disconnect; 88 | } 89 | 90 | // Connect to AWS IoT Core 91 | ret = client.connect( 92 | network, 93 | credentials, 94 | MBED_CONF_APP_AWS_ENDPOINT, 95 | MBED_CONF_APP_AWS_CLIENT_IDENTIFIER 96 | ); 97 | if (ret != MBED_SUCCESS) { 98 | tr_error("AWSClient::connect() failed"); 99 | goto disconnect; 100 | } 101 | 102 | // Start a background thread to process MQTT 103 | ret = process_thread.start(process_responses); 104 | if (ret != osOK) { 105 | tr_error("Failed to start thread to process MQTT"); 106 | goto disconnect; 107 | } 108 | 109 | // Run a demo depending on the configuration in mbed_app.json: 110 | // * demo_mqtt.cpp if aws-client.shadow is unset or false 111 | // * demo_shadow.cpp if aws-client.shadow is true 112 | demo(); 113 | 114 | disconnect: 115 | if (client.isConnected()) { 116 | connection_mutex.lock(); 117 | ret = client.disconnect(); 118 | connection_mutex.unlock(); 119 | if (ret != MBED_SUCCESS) { 120 | tr_error("AWS::disconnect() failed"); 121 | } 122 | } 123 | 124 | // The process thread should finish on disconnection 125 | ret = process_thread.join(); 126 | if (ret != osOK) { 127 | tr_error("Failed to join thread"); 128 | } 129 | 130 | ret = network->disconnect(); 131 | if (ret != MBED_SUCCESS) { 132 | tr_error("NetworkInterface::disconnect() failed"); 133 | } 134 | 135 | end: 136 | tr_info("End of the demo"); 137 | } -------------------------------------------------------------------------------- /mbed-client-for-aws.lib: -------------------------------------------------------------------------------- 1 | https://github.com/ARMmbed/mbed-client-for-aws.git/#f7f8ad1ed18f42f2da7b0c89f6d2e8803e985894 2 | -------------------------------------------------------------------------------- /mbed-os.lib: -------------------------------------------------------------------------------- 1 | https://github.com/ARMmbed/mbed-os/#17dc3dc2e6e2817a8bd3df62f38583319f0e4fed 2 | -------------------------------------------------------------------------------- /mbed_app.json: -------------------------------------------------------------------------------- 1 | { 2 | "config": { 3 | "aws-endpoint": { 4 | "help": "Endpoint URL", 5 | "value": "\"… .iot. … .amazonaws.com\"" 6 | }, 7 | "aws-mqtt-topic": { 8 | "help": "Topic to subscribe & publish to", 9 | "value": "\"sdkTest/sub\"" 10 | }, 11 | "aws-client-identifier": { 12 | "help": "Unique identifier for the thing", 13 | "value": "\"...\"" 14 | } 15 | }, 16 | "target_overrides": { 17 | "*": { 18 | "mbed-trace.enable": true, 19 | "mbed-trace.max-level": "TRACE_LEVEL_INFO", 20 | "platform.error-filename-capture-enabled": true, 21 | "platform.stdio-convert-newlines": true, 22 | "platform.stdio-baud-rate": 115200, 23 | "aws-client.shadow": false, 24 | "aws-client.aws-sdk-trace": false 25 | }, 26 | "DISCO_L475VG_IOT01A": { 27 | "target.network-default-interface-type": "WIFI", 28 | "nsapi.default-wifi-security": "WPA_WPA2", 29 | "nsapi.default-wifi-ssid": "\"SSID\"", 30 | "nsapi.default-wifi-password": "\"PASSWORD\"" 31 | }, 32 | "EP_AGORA": { 33 | "platform.stdio-buffered-serial" : true, 34 | "platform.stdio-flush-at-exit" : true, 35 | "drivers.uart-serial-rxbuf-size" : 1024, 36 | "drivers.uart-serial-txbuf-size" : 1024, 37 | "lwip.ipv4-enabled" : true, 38 | "lwip.ipv6-enabled" : true, 39 | "lwip.ppp-enabled" : true, 40 | "lwip.tcp-enabled" : true, 41 | "lwip.ethernet-enabled" : false, 42 | "lwip.mem-size" : 22000, 43 | "lwip.tcpip-thread-stacksize" : 2000, 44 | "nsapi.dns-response-wait-time" : 30000, 45 | "nsapi.default-cellular-apn" : "\"phone\"", 46 | "lwip.use-mbed-trace" : true, 47 | "lwip.debug-enabled" : false 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /resources/official_armmbed_example_badge.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ARMmbed/mbed-os-example-for-aws/fcabf3e5065e1989d3c9646607bbf994c24e7c51/resources/official_armmbed_example_badge.png -------------------------------------------------------------------------------- /tests/README.md: -------------------------------------------------------------------------------- 1 | # Testing examples 2 | 3 | Examples are tested using tool [htrun](https://github.com/ARMmbed/mbed-os-tools/tree/master/packages/mbed-host-tests) and templated print log. 4 | 5 | To run the test, use following command after you build the example: 6 | ``` 7 | mbedhtrun -d D: -p COM4 -m K64F -f .\BUILD\K64F\GCC_ARM\mbed-os-example-for-aws.bin --compare-log tests\aws.log --baud-rate=115200 --sync=0 8 | ``` 9 | 10 | 11 | More details about `htrun` are [here](https://github.com/ARMmbed/mbed-os-tools/tree/master/packages/mbed-host-tests#testing-mbed-os-examples). 12 | -------------------------------------------------------------------------------- /tests/aws.log: -------------------------------------------------------------------------------- 1 | Connecting to the network... 2 | Connection Success 3 | Certificate verification passed 4 | Message sent successfully 5 | Message received from the cloud 6 | 7 | -------------------------------------------------------------------------------- /wifi-ism43362.lib: -------------------------------------------------------------------------------- 1 | https://github.com/ARMmbed/wifi-ism43362/#3813a4bb8623cc9b0525978748581f60d47142fa 2 | --------------------------------------------------------------------------------