Mbed OS example of Pelion device management client

This example is known to work great on the following platforms:

https://os.mbed.com/media/cache/platforms/M46B_mbed.png.250x250_q85.jpg

To use this example, connect ESP8266 Wi-Fi module (Grove UART WiFi) to the AdBun-M46B board as below:

ESP8266 Wi-FiAdBun-M46B
TXPL1 (CN4-22)
RXPL2 (CN4-24)
VINVCC3.3 (CN11-5)
GNDGND (CN11-6)

https://dl.dropboxusercontent.com/s/5j4glkwhjg5m5ay/IMG_7457.jpg

Example functionality

  • Initialize, connect and register to Pelion DM
  • Interact with the user through the serial port (115200 bauds)
    • Press enter through putty/minicom to simulate button
    • Press i to print endpoint name
    • Press Ctrl-C to to unregister
    • Press r to reset storage and reboot (warning: it generates a new device ID!)

Instructions to use this program with Mbed CLI


1. Import the application into your desktop:

mbed import https://os.mbed.com/teams/Toshiba/code/mbed-os-example-pelion/
cd mbed-os-example-pelion


2. Install the CLOUD_SDK_API_KEY

mbed config -G CLOUD_SDK_API_KEY <PELION_DM_API_KEY>

For instructions on how to generate your API key, please see the documentation.

3. Initialize firmware credentials (done once per repository). You can use the following command:

mbed dm init -d "<your company name in Pelion DM>" --model-name "<product model identifier>" -q --force

If above command do not work for your Mbed CLI, please consider upgrading Mbed CLI to version 1.8.x or above.

4. Connect Pins used with Wifi shield (ESP8266) and SD card shield:

5. Edit mbed_app.json to specify Wi-Fi access point information

            "nsapi.default-wifi-ssid"                   : "\"SSID\"",
            "nsapi.default-wifi-password"               : "\"PASSWORD\""

6. Compile and program:

mbed compile -t <toolchain> -m TMPM46B

(supported toolchains : GCC_ARM / ARM / IAR)

Files at this revision

API Documentation at this revision

Comitter:
MACRUM
Date:
Thu Dec 12 10:26:06 2019 +0900
Commit message:
Initial commit.

Changed in this revision

LICENSE Show annotated file Show diff for this revision Revisions of this file
README.md Show annotated file Show diff for this revision Revisions of this file
TESTS/README.md Show annotated file Show diff for this revision Revisions of this file
TESTS/__init__.py Show annotated file Show diff for this revision Revisions of this file
TESTS/basic_os_tests.json Show annotated file Show diff for this revision Revisions of this file
TESTS/basic_tests.json Show annotated file Show diff for this revision Revisions of this file
TESTS/deregister.py Show annotated file Show diff for this revision Revisions of this file
TESTS/device_update.py Show annotated file Show diff for this revision Revisions of this file
TESTS/full_os_tests.json Show annotated file Show diff for this revision Revisions of this file
TESTS/full_tests.json Show annotated file Show diff for this revision Revisions of this file
TESTS/get.py Show annotated file Show diff for this revision Revisions of this file
TESTS/observation.py Show annotated file Show diff for this revision Revisions of this file
TESTS/pelion.tc_cfg Show annotated file Show diff for this revision Revisions of this file
TESTS/pelion_helper.py Show annotated file Show diff for this revision Revisions of this file
TESTS/post.py Show annotated file Show diff for this revision Revisions of this file
TESTS/put.py Show annotated file Show diff for this revision Revisions of this file
TESTS/register.py Show annotated file Show diff for this revision Revisions of this file
TESTS/register_and_read_id.py Show annotated file Show diff for this revision Revisions of this file
bootloader/mbed-bootloader-disco_l475vg_iot01a-external_kvstore-qspif.bin Show annotated file Show diff for this revision Revisions of this file
bootloader/mbed-bootloader-k64f-block_device-kvstore-v4.0.0.bin Show annotated file Show diff for this revision Revisions of this file
bootloader/mbed-bootloader-k64f-internal_flash-no_rot-v4.0.1.bin Show annotated file Show diff for this revision Revisions of this file
bootloader/mbed-bootloader-k66f-internal_flash-no_rot-v4.0.0.bin Show annotated file Show diff for this revision Revisions of this file
bootloader/mbed-bootloader-nucleo_f411re-block_device-kvstore-v4.0.0.bin Show annotated file Show diff for this revision Revisions of this file
bootloader/mbed-bootloader-nucleo_f429zi-internal_flash-no_rot-v4.0.0.bin Show annotated file Show diff for this revision Revisions of this file
bootloader/mbed-bootloader-tmpm46b-internal_flash-no_rot-v4.0.1.bin Show annotated file Show diff for this revision Revisions of this file
bootloader/mbed-bootloader-ublox_evk_odin_w2-block_device-kvstore-v4.0.0.bin Show annotated file Show diff for this revision Revisions of this file
drivers/COMPONENT_WIFI_IDW01M1.lib Show annotated file Show diff for this revision Revisions of this file
drivers/COMPONENT_WIFI_ISM43362.lib Show annotated file Show diff for this revision Revisions of this file
main.cpp Show annotated file Show diff for this revision Revisions of this file
mbed-cloud-client.lib Show annotated file Show diff for this revision Revisions of this file
mbed-os.lib Show annotated file Show diff for this revision Revisions of this file
mbed_app.json Show annotated file Show diff for this revision Revisions of this file
mbed_cloud_client_user_config.h Show annotated file Show diff for this revision Revisions of this file
mbed_cloud_dev_credentials.c Show annotated file Show diff for this revision Revisions of this file
update_default_resources.c Show annotated file Show diff for this revision Revisions of this file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/LICENSE	Thu Dec 12 10:26:06 2019 +0900
@@ -0,0 +1,165 @@
+Apache License
+Version 2.0, January 2004
+http://www.apache.org/licenses/
+
+TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+1. Definitions.
+
+"License" shall mean the terms and conditions for use, reproduction, and
+distribution as defined by Sections 1 through 9 of this document.
+
+"Licensor" shall mean the copyright owner or entity authorized by the copyright
+owner that is granting the License.
+
+"Legal Entity" shall mean the union of the acting entity and all other entities
+that control, are controlled by, or are under common control with that entity.
+For the purposes of this definition, "control" means (i) the power, direct or
+indirect, to cause the direction or management of such entity, whether by
+contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the
+outstanding shares, or (iii) beneficial ownership of such entity.
+
+"You" (or "Your") shall mean an individual or Legal Entity exercising
+permissions granted by this License.
+
+"Source" form shall mean the preferred form for making modifications, including
+but not limited to software source code, documentation source, and configuration
+files.
+
+"Object" form shall mean any form resulting from mechanical transformation or
+translation of a Source form, including but not limited to compiled object code,
+generated documentation, and conversions to other media types.
+
+"Work" shall mean the work of authorship, whether in Source or Object form, made
+available under the License, as indicated by a copyright notice that is included
+in or attached to the work (an example is provided in the Appendix below).
+
+"Derivative Works" shall mean any work, whether in Source or Object form, that
+is based on (or derived from) the Work and for which the editorial revisions,
+annotations, elaborations, or other modifications represent, as a whole, an
+original work of authorship. For the purposes of this License, Derivative Works
+shall not include works that remain separable from, or merely link (or bind by
+name) to the interfaces of, the Work and Derivative Works thereof.
+
+"Contribution" shall mean any work of authorship, including the original version
+of the Work and any modifications or additions to that Work or Derivative Works
+thereof, that is intentionally submitted to Licensor for inclusion in the Work
+by the copyright owner or by an individual or Legal Entity authorized to submit
+on behalf of the copyright owner. For the purposes of this definition,
+"submitted" means any form of electronic, verbal, or written communication sent
+to the Licensor or its representatives, including but not limited to
+communication on electronic mailing lists, source code control systems, and
+issue tracking systems that are managed by, or on behalf of, the Licensor for
+the purpose of discussing and improving the Work, but excluding communication
+that is conspicuously marked or otherwise designated in writing by the copyright
+owner as "Not a Contribution."
+
+"Contributor" shall mean Licensor and any individual or Legal Entity on behalf
+of whom a Contribution has been received by Licensor and subsequently
+incorporated within the Work.
+
+2. Grant of Copyright License.
+
+Subject to the terms and conditions of this License, each Contributor hereby
+grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free,
+irrevocable copyright license to reproduce, prepare Derivative Works of,
+publicly display, publicly perform, sublicense, and distribute the Work and such
+Derivative Works in Source or Object form.
+
+3. Grant of Patent License.
+
+Subject to the terms and conditions of this License, each Contributor hereby
+grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free,
+irrevocable (except as stated in this section) patent license to make, have
+made, use, offer to sell, sell, import, and otherwise transfer the Work, where
+such license applies only to those patent claims licensable by such Contributor
+that are necessarily infringed by their Contribution(s) alone or by combination
+of their Contribution(s) with the Work to which such Contribution(s) was
+submitted. If You institute patent litigation against any entity (including a
+cross-claim or counterclaim in a lawsuit) alleging that the Work or a
+Contribution incorporated within the Work constitutes direct or contributory
+patent infringement, then any patent licenses granted to You under this License
+for that Work shall terminate as of the date such litigation is filed.
+
+4. Redistribution.
+
+You may reproduce and distribute copies of the Work or Derivative Works thereof
+in any medium, with or without modifications, and in Source or Object form,
+provided that You meet the following conditions:
+
+You must give any other recipients of the Work or Derivative Works a copy of
+this License; and
+You must cause any modified files to carry prominent notices stating that You
+changed the files; and
+You must retain, in the Source form of any Derivative Works that You distribute,
+all copyright, patent, trademark, and attribution notices from the Source form
+of the Work, excluding those notices that do not pertain to any part of the
+Derivative Works; and
+If the Work includes a "NOTICE" text file as part of its distribution, then any
+Derivative Works that You distribute must include a readable copy of the
+attribution notices contained within such NOTICE file, excluding those notices
+that do not pertain to any part of the Derivative Works, in at least one of the
+following places: within a NOTICE text file distributed as part of the
+Derivative Works; within the Source form or documentation, if provided along
+with the Derivative Works; or, within a display generated by the Derivative
+Works, if and wherever such third-party notices normally appear. The contents of
+the NOTICE file are for informational purposes only and do not modify the
+License. You may add Your own attribution notices within Derivative Works that
+You distribute, alongside or as an addendum to the NOTICE text from the Work,
+provided that such additional attribution notices cannot be construed as
+modifying the License.
+You may add Your own copyright statement to Your modifications and may provide
+additional or different license terms and conditions for use, reproduction, or
+distribution of Your modifications, or for any such Derivative Works as a whole,
+provided Your use, reproduction, and distribution of the Work otherwise complies
+with the conditions stated in this License.
+
+5. Submission of Contributions.
+
+Unless You explicitly state otherwise, any Contribution intentionally submitted
+for inclusion in the Work by You to the Licensor shall be under the terms and
+conditions of this License, without any additional terms or conditions.
+Notwithstanding the above, nothing herein shall supersede or modify the terms of
+any separate license agreement you may have executed with Licensor regarding
+such Contributions.
+
+6. Trademarks.
+
+This License does not grant permission to use the trade names, trademarks,
+service marks, or product names of the Licensor, except as required for
+reasonable and customary use in describing the origin of the Work and
+reproducing the content of the NOTICE file.
+
+7. Disclaimer of Warranty.
+
+Unless required by applicable law or agreed to in writing, Licensor provides the
+Work (and each Contributor provides its Contributions) on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied,
+including, without limitation, any warranties or conditions of TITLE,
+NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are
+solely responsible for determining the appropriateness of using or
+redistributing the Work and assume any risks associated with Your exercise of
+permissions under this License.
+
+8. Limitation of Liability.
+
+In no event and under no legal theory, whether in tort (including negligence),
+contract, or otherwise, unless required by applicable law (such as deliberate
+and grossly negligent acts) or agreed to in writing, shall any Contributor be
+liable to You for damages, including any direct, indirect, special, incidental,
+or consequential damages of any character arising as a result of this License or
+out of the use or inability to use the Work (including but not limited to
+damages for loss of goodwill, work stoppage, computer failure or malfunction, or
+any and all other commercial damages or losses), even if such Contributor has
+been advised of the possibility of such damages.
+
+9. Accepting Warranty or Additional Liability.
+
+While redistributing the Work or Derivative Works thereof, You may choose to
+offer, and charge a fee for, acceptance of support, warranty, indemnity, or
+other liability obligations and/or rights consistent with this License. However,
+in accepting such obligations, You may act only on Your own behalf and on Your
+sole responsibility, not on behalf of any other Contributor, and only if You
+agree to indemnify, defend, and hold each Contributor harmless for any liability
+incurred by, or claims asserted against, such Contributor by reason of your
+accepting any such warranty or additional liability.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/README.md	Thu Dec 12 10:26:06 2019 +0900
@@ -0,0 +1,274 @@
+# Pelion Client Mbed OS Example
+
+This is a simplified example with the following features:
+- Mbed OS 5.13 and Pelion Device Management Client 3.3.0 
+- Support for FW Update
+- 200 lines of code + credential sources
+
+Note this application is considered **alpha** and given early access to Mbed Partners.
+
+## Supported platforms
+
+This table shows a list of platforms that are supported.
+
+Platform                          |  Connectivity     | Storage for credentials  | Storage for FW candidate | Notes
+----------------------------------| ------------------| -------------------------| -----------------------  | --------------
+NXP K64F                          | Ethernet          | Internal Flash           |  Internal Flash          |
+NXP K66F                          | Ethernet          | Internal Flash           |  Internal Flash          |
+ST NUCLEO_F429ZI                  | Ethernet          | Internal Flash           |  Internal Flash          |
+ST NUCLEO_F411RE                  | WiFi IDW01M1      | SD card                  |  SD card                 |
+Ublox UBLOX_EVK_ODIN_W2           | WiFi              | SD card                  |  SD card                 |
+
+<span class="notes">**(*) Note**: the platforms require further testing</span>
+
+# Developer guide
+
+This section is intended for developers to get started, import the example application, compile and get it running on their device.
+
+## Requirements
+
+- Mbed CLI >= 1.10.0
+  
+  For instructions on installing and using Mbed CLI, please see our [documentation](https://os.mbed.com/docs/mbed-os/latest/tools/developing-mbed-cli.html).
+  
+- Install the `CLOUD_SDK_API_KEY`
+
+   `mbed config -G CLOUD_SDK_API_KEY ak_1MDE1...<snip>`
+
+   You should generate your own API key. Pelion Device Management is available for any Mbed developer. Create a [free trial](https://os.mbed.com/pelion-free-tier).
+
+   For instructions on how to generate your API key, please see our [documentation](https://cloud.mbed.com/docs/current/integrate-web-app/api-keys.html#generating-an-api-key). 
+
+## Deploying
+
+This repository is in the process of being updated and depends on few enhancements being deployed in mbed-cloud-client. In the meantime, follow these steps to import and apply the patches before compiling.
+
+    mbed import mbed-os-pelion-example
+    cd mbed-os-pelion-example
+
+## Compiling
+
+    mbed target K64F
+    mbed toolchain GCC_ARM
+    mbed device-management init -d arm.com --model-name example-app --force -q
+    mbed compile
+
+## Program Flow
+
+1. Initialize, connect and register to Pelion DM
+1. Interact with the user through the serial port (115200 bauds)
+   - Press enter through putty/minicom to simulate button
+   - Press 'i' to print endpoint name
+   - Press Ctrl-C to to unregister
+   - Press 'r' to reset storage and reboot (warning: it generates a new device ID!)
+ 
+## Further information and requirements
+
+Check the public tutorial for further information:
+
+  [https://www.pelion.com/docs/device-management/current/connecting/mbed-os.html](https://www.pelion.com/docs/device-management/current/connecting/mbed-os.html)
+
+## Troubleshooting
+
+- Device initializes but can't register to Pelion
+
+  Error: `client_error(3) -> Bootstrap server URL is not correctly formed`
+  
+  Solution: Format the the storage by pressing 'r' in the serial terminal.
+  
+# Porting process to add support for an Mbed Enabled board
+
+  There are many steps involved in this process. We generally recomend the following steps:
+  
+  1. Configure the application using `mbed_app.json`
+      - Configure the default connectivity
+      - Configure the KVSTORE area to store credentials (internal or external memory)
+      - Build the application, program the board and observe whether the application can connect to Pelion DM by using a serial terminal.
+  1. Configure the bootloader using `bootloader_app.json`
+      - Configure the KVSTORE area
+      - Configure the FW Candidate Storage
+      - Build bootloader application, program the board and observe whether this is able to boot.
+  1. Enable application with bootloader using `mbed_app.json`
+      - Enable the usage of the bootloader
+      - Ensure the KVSTORE addresses and FW Candidate storage addresses match with the bootloader configuration
+      - Build the application again (this time combined with bootloader) and check whether it can boot and connect to Pelion DM.
+      - Perform a FW Update and check whether the process can be completed succesfully.
+
+## 1. Application configuration
+
+<span class="notes">**Note**: consider allocating the credentials on internal flash to simplify the application setup process. In addition, consider the use of internal flash to store the firmware candidate image for the FW update process as this would remove the need to use external components. If there isn't enough space, you may need to enable external storage (SD Card, SPI, etc).</span>
+
+Mbed OS boards should have a default configuration for connectivity and storage in Mbed OS (`targets.json`).
+You can extend or override the default configuration using `mbed_app.json` in this application. Create a new entry under the target name for your device.
+
+### a. Connectivity
+
+  Specify the default IP connectivity type for your target. It's essential with targets that lack default connectivity set in `targets.json` or for targets that support multiple connectivity options. For example:
+   
+      "target.network-default-interface-type" : "ETHERNET",
+      
+  The possible options are `ETHERNET`, `WIFI` and `CELLULAR`.
+   
+  Depending on connectivity type, you might have to specify more configuration options. Review the [documentation](https://os.mbed.com/docs/mbed-os/latest/porting/porting-connectivity.html) for further information.
+
+### b. Storage for credentials
+
+  Start by getting familiar with the multiple [storage options](https://os.mbed.com/docs/mbed-os/latest/reference/storage.html) and configurations supported in Mbed OS.
+
+  Then start designing the system memory map, the location of components (whether they are on internal or external memory), and the corresponding base addresses and sizes. You may want to create a diagram similar to the one below to help you to make design decisions:
+ 
+    +--------------------------+
+    |                          |
+    |                          |
+    |                          |
+    |Firmware Candidate Storage|
+    |                          |
+    |                          |
+    |                          |
+    +--------------------------+ <-+ update-client.storage-address
+    |                          |
+    |         KVSTORE          |
+    |                          |
+    +--------------------------+ <-+ storage_tdb_internal.internal_base_address
+    |                          |
+    |        Free space        |
+    |                          |
+    +--------------------------+
+    |                          |
+    |                          |
+    |        Active App        |
+    |                          |
+    |                          |
+    |                          |
+    +--------------------------+ <-+ mbed-bootloader.application-start-address
+    |Active App Metadata Header|
+    +--------------------------+ <-+ update-client.application-details
+    |                          |
+    |        Bootloader        |
+    |                          |
+    +--------------------------+ <-+ 0
+
+  In cases where the MCU has two separate memory banks, it's appropiate to allocate the bootloader and base application in one bank, and KVSTORE storage at the begining of the second bank followed by a firmware candidate storage.
+
+  - **Option 1:** Allocating credentials in internal memory
+    
+    **This is the preferred option whenever possible**. Make sure `TDB_INTERNAL` is the type of storage selected in `mbed_app.json`. Specify the base address depending on the available memory in the system. The size of this section should be aligned with the flash erase sector. The value should be multiple of 4 with a minimum of 24KB and upwards depending on the use case (for example the usage of certificate chain will increase the need of storage to hold those certificates). An example of this configuration can be seen for the `NUCLEO_F429ZI` platform in this application.
+
+        "storage.storage_type"                      : "TDB_INTERNAL" 
+        "storage_tdb_internal.internal_base_address": "(MBED_ROM_START+1024*1024)",
+        "storage_tdb_internal.internal_size"        : "(128*1024)",
+
+  - **Option 2:** Allocating credentials in external memory:
+    
+    This is possible when the platform has an storage device wired to the MCU (could be on-board or external component). Make sure `FILESYSTEM` is specified as type of storage. The blockdevice and filesystem should be one of the supported in Mbed OS (see [docs](https://os.mbed.com/docs/mbed-os/latest/porting/blockdevice-port.html)).
+    
+    An example of this configuration can be seen for the `K64F` platform in the [mbed-cloud-client-example](https://github.com/ARMmbed/mbed-cloud-client-example/blob/master/mbed_app.json#L32)
+    
+        "storage.storage_type"                      : "FILESYSTEM",
+        "storage_filesystem.blockdevice"            : "SD",
+        "storage_filesystem.filesystem"             : "LITTLE",
+        "storage_filesystem.internal_base_address"  : "(32*1024)",
+        "storage_filesystem.rbp_internal_size"      : "(8*1024)",
+        "storage_filesystem.external_base_address"  : "(0x0)",
+        "storage_filesystem.external_size"          : "(1024*1024*64)",
+
+### c. Storage for firmware updates
+
+  Before enabling FW updates, it's recomended that the application is able to initialize the network and connect to Pelion DM.
+  
+  Once the connection is successfull, you can follow the steps below to enable the platform to receive FW updates. Note the configuration for the application in this section should match with the one on the bootloader - see section below.
+   
+  - Common configuration
+  
+    Regardless of where the firmware candidate is located (internal or external), there is a need to have a bootloader in place. The binary of the booloader can be specified with the `bootloader_img` option. The address and size of the bootloader determines the `application-details` and `bootloader-details` options. The value of `bootloader-details` can be obtained by running the binary on the target and observing the serial output.
+    
+    Review the [mbed-bootloader](https://github.com/ARMmbed/mbed-bootloader#configurations) guidelines on how these options should be selected. Review the [bootloader configuration](2.-Bootloader-configuration) section below for more information.
+    
+    Copy the compiled bootloader from `mbed-bootloader/BUILDS/<TARGET>/<TOOLCHAIN>-TINY/mbed-bootloader.bin` to `bootloader/mbed-bootloader-<TARGET>.bin`.
+
+    Edit `mbed-os-pelion-example/mbed_app.json` and modify the target configuration to match with the one in `bootloader_app.json`.
+
+   <span class="notes">**Note:**
+
+  - `update-client.application-details` should be identical in both `bootloader_app.json` and `mbed_app.json`.
+
+  - `target.app_offset` is relative offset to `flash-start-address` you specified in `mbed_app.json` and `bootloader_app.json`, and is the hex value of the offset specified by `application-start-address` in `bootloader_app.json`. For example,  `(MBED_CONF_APP_FLASH_START_ADDRESS+65*1024)` dec equals `0x10400` hex.
+
+  - `target.header_offset` is also relative offset to the `flash-start-address` you specified in the `bootloader_app.json`, and is the hex value of the offset specified by `update-client.application-details`. For example, `(MBED_CONF_APP_FLASH_START_ADDRESS+64*1024)` dec equals `0x10000` hex.</span>
+      
+  An example of this configuration can be seen for the `NUCLEO_F429ZI` platform.
+  
+        "update-client.application-details"         : "(MBED_ROM_START + MBED_BOOTLOADER_SIZE)",
+        "update-client.bootloader-details"          : "0x08007300",
+        "target.bootloader_img"                     : "bootloader/mbed-bootloader-<target>",
+        "target.header_offset"                      : "0x8000",
+        "target.app_offset"                         : "0x8400",
+    
+  - **Option 1:** Allocating the firmware update candidate in internal memory
+
+    **This is the preferred option whenever possible**. Make sure `ARM_UCP_FLASHIAP` is selected in `update-storage` in `mbed_app.json`. This area should be located at the end of the flash after the KVSTORE area. Specify the `storage-address`, `storage-size` and `storage-page` as required. The `application-details` option should point at the end of the bootloader area. An example of this configuration can be seen for the `NUCLEO_F429ZI` platform.
+    
+        "mbed-cloud-client.update-storage"          : "ARM_UCP_FLASHIAP",
+        "update-client.storage-address"             : "(MBED_CONF_STORAGE_TDB_INTERNAL_INTERNAL_BASE_ADDRESS+MBED_CONF_STORAGE_TDB_INTERNAL_INTERNAL_SIZE)",
+        "update-client.storage-size"                : "(1024*1024-MBED_CONF_STORAGE_TDB_INTERNAL_INTERNAL_SIZE)",
+        "update-client.storage-page"                : 1,
+
+  - **Option 2:** Allocating the firmware update candidate in external memory
+  
+  When using an external device to the MCU to store the firmware candidate, make sure `ARM_UCP_FLASHIAP_BLOCKDEVICE` is specified as type of `update-storage`. Specify the `storage-address`, `storage-size` and `storage-page` as required.
+    
+  An example of this configuration can be seen for the `K64F` platform in the [mbed-cloud-client-example](https://github.com/ARMmbed/mbed-cloud-client-example/blob/master/mbed_app.json#L32)
+    
+        "mbed-cloud-client.update-storage"          : "ARM_UCP_FLASHIAP_BLOCKDEVICE",
+        "update-client.storage-address"             : "(1024*1024*64)",
+        "update-client.storage-size"                : "((MBED_ROM_START + MBED_ROM_SIZE - APPLICATION_ADDR) * MBED_CONF_UPDATE_CLIENT_STORAGE_LOCATIONS)",
+   
+## 2. Bootloader configuration
+
+The bootloader is required to perform FW Updates. The steps below explain how to create a new configuration and binary for the bootloader.
+
+1. Import as a new application the [mbed-bootloader](https://github.com/ARMmbed/mbed-bootloader/) repository.
+
+1. Edit the bootloader application configuration in this example (`bootloader/bootloader_app.json`) and add a new target entry. An example of this configuration can be seen for the `NUCLEO_F429ZI` platform:
+
+       "update-client.firmware-header-version": "2",
+       "mbed-bootloader.use-kvstore-rot": 0,
+       "mbed-bootloader.bootloader-size": "APPLICATION_SIZE",
+       "update-client.application-details"        : "(MBED_ROM_START + MBED_BOOTLOADER_SIZE)",
+       "mbed-bootloader.application-start-address": "(MBED_CONF_UPDATE_CLIENT_APPLICATION_DETAILS + MBED_BOOTLOADER_ACTIVE_HEADER_REGION_SIZE)",
+       "mbed-bootloader.max-application-size"     : "(MBED_ROM_START + MBED_BOOTLOADER_FLASH_BANK_SIZE - MBED_CONF_MBED_BOOTLOADER_APPLICATION_START_ADDRESS)",
+       "update-client.storage-address"            : "(MBED_ROM_START + MBED_BOOTLOADER_FLASH_BANK_SIZE + KVSTORE_SIZE)",
+       "update-client.storage-size"               : "(MBED_BOOTLOADER_FLASH_BANK_SIZE - KVSTORE_SIZE)",
+       "update-client.storage-locations"          : 1,
+       "kvstore-size": "2*64*1024",
+       "update-client.storage-page": 1
+    
+1. Compile the bootloader using the `bootloader_app.json` configuration you've just edited:
+
+       mbed compile -t <TOOLCHAIN> -m <TARGET> --profile=tiny.json --app-config=.../mbed-os-pelion-example/bootloader/bootloader_app.json>
+
+<span class="notes">**Note:** `mbed-bootloader` is primarily optimized for `GCC_ARM`, so you may want to compile it with that toolchain.
+Before jumping to the next step, you should compile and flash the bootloader and then connect over the virtual serial port to ensure the bootloader is running correctly. You can ignore errors related to checksum verification or falure to jump to application - these are expected at this stage.</span>
+
+
+## Validation and testing
+
+In addition to having an example application succesfully connected to Pelion DM, it's required to ensure that the application is working correcly in multiple situations. This can be achived by running the following tests:
+
+- Mbed OS tests (as described in our [documentation](https://os.mbed.com/docs/mbed-os/latest/porting/testing.html))
+
+  `mbed test`
+
+- Mbed OS integration tests
+
+  See [mbed-os/TESTS/integration/README.md](https://github.com/ARMmbed/mbed-os/blob/sip-workshop/TESTS/integration/README.md) (sip-workshop branch)
+  
+  `mbed test -t <toolchain> -m <platform> -n *integration-* -DINTEGRATION_TESTS -v `
+
+- Pelion Client tests, including firmware update.
+
+  See the [testing](./TESTS/README.md) documentation to validate the configuration in this example.
+
+# Known-issues
+
+Please review existing issues on github and report any problem you may see.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/TESTS/README.md	Thu Dec 12 10:26:06 2019 +0900
@@ -0,0 +1,116 @@
+# Device Management E2E testing
+
+Device Management E2E tests are platform agnostic. They verify that a target platform can reliably perform basic critical operations.
+There are two different test sets:
+
+- Tests for verifying the basic functionality of a target platform.
+- Advanced tests for more complex use cases (for example `device_update` test case).
+
+## Requirements
+
+1. [IceTea](https://github.com/ARMmbed/icetea) v1.2.1 or later.
+1. [Mbed Cloud SDK for Python](https://cloud.mbed.com/docs/current/mbed-cloud-sdk-python/index.html) v2.0.5 or later.
+
+## Installation
+
+```
+pip install icetea mbed-cloud-sdk
+```
+
+## Basic usage for Mbed OS (serial dependency)
+
+These Mbed OS specific tests use the serial console to read the `device_id` automatically from console output.
+
+1. Define environmental variable `MBED_CLOUD_SDK_API_KEY` with your [API key](https://cloud.mbed.com/docs/current/integrate-web-app/api-keys.html) as the value. Alternatively add your API key to `TESTS/pelion.tc_cfg`.
+1. Verify with `mbed ls` that the device is connected via serial and mounted.
+
+### Running the test suite
+
+To run the test suite, use the following command:
+
+```
+icetea --suite basic_os_tests.json --suitedir TESTS/ --tcdir ./TESTS/ --tc_cfg TESTS/pelion.tc_cfg --reset
+```
+
+If you have prepared a manifest (see below the instructions for `device_update` testcase), you can run the full suite with:
+
+```
+icetea --suite full_os_tests.json --suitedir TESTS/ --tcdir ./TESTS/ --tc_cfg TESTS/pelion.tc_cfg --reset
+```
+
+## Basic usage
+
+These generic tests do not depend on console.
+
+1. Register your device to Device Management. For examples, see the [tutorials](https://cloud.mbed.com/docs/current/connecting/device-management-client-tutorials.html).
+1. Enter your [device_id](https://cloud.mbed.com/docs/current/connecting/device-identity.html) to `TESTS/pelion.tc_cfg`.
+1. Enter your [API key](https://cloud.mbed.com/docs/current/integrate-web-app/api-keys.html) to `TESTS/pelion.tc_cfg`.
+
+### Running the test suite
+
+To run the test suite, use the following command:
+
+```
+icetea --suite basic_tests.json --suitedir TESTS/ --tcdir ./TESTS/ --tc_cfg TESTS/pelion.tc_cfg
+```
+
+If you have prepared a manifest (see below the instructions for `device_update` testcase), you can run the full suite with:
+
+```
+icetea --suite full_tests.json --suitedir TESTS/ --tcdir ./TESTS/ --tc_cfg TESTS/pelion.tc_cfg
+```
+
+### Running a single test
+
+To run a single test, use the following command:
+
+```
+icetea --tc basic_get --tcdir ./TESTS/ --tc_cfg TESTS/pelion.tc_cfg
+```
+
+## Current tests
+
+| Test name        | Main functions                             | Notes                                 |
+| ---------------- | ------------------------------------------ | --------------------------------------|
+| `register`       | Verify that the device is registered.      |                                       |
+| `register_and_read_id` | Verify that the device is registered. | Uses serial to read the `device_id`. Mbed OS only. |
+| `get`            | Verify that the device responds to GET.    | Uses OMA resource `/3/0/0`            |
+| `put`            | Verify that the device responds to PUT.    | Uses custom resource `/3201/0/5853`   |
+| `post`           | Verify that the device responds to POST.   | Uses custom resource `/3201/0/5850`   |
+| `observation`    | Verify that the device can send notifications. | Uses custom resource `/3201/0/5853`   |
+| `deregister`     | Verify that the device can deregister.     | Uses custom resource `/5000/0/1`      |
+| `device_update`  | Performs the firmware update.              | This testcase verifies that the device can perform a firmware update. For this testcase, you need to manually generate an update image and a manifest. |
+
+### Executing `device_update` test case
+
+The test case does not automatically generate an update image or generate a manifest. Read [Update documentation](https://cloud.mbed.com/docs/current/updating-firmware/index.html) for more information.
+
+To run the test case:
+
+1. Compile a new update image.
+1. Upload the image to Device Management.
+1. Generate a manifest for the update image.
+1. Upload the manifest to Device Management.
+1. Add the `<manifest-id>` to `TESTS/pelion.tc_cfg`.
+
+#### Example for executing the `device_update` test case on Mbed OS platform
+
+To prepare the latest Device Management Client example on Mbed OS for testing, run the following commands:
+
+1. `mbed dm init -d arm.com --model-name example-app --force -q`
+1. `mbed compile -t <toolchain> -m <MCU>`
+1. `mbed dm update prepare`
+
+The last command uploads the `mbed-cloud-client-example_update.bin`, generates a new manifest and uploads the manifest to Device Management. Record the resulting Manifest ID and add it to `TESTS/pelion.tc_cfg`.
+
+```
+[INFO] 2018-12-12 15:06:42 - manifesttool.prepare - Manifest ID: <manifest-id>
+```
+
+To execute the test, use the following command:
+
+```
+icetea --tc device_update --tcdir ./TESTS/ --tc_cfg TESTS/pelion.tc_cfg
+```
+
+ <span class="notes">**Note:** After each successful update, you need to generate a new manifest for a new firmware update test. Otherwise, the test will pass without actually performing any device side updates. The update campaign ends automatically because the manifest has already been applied to the device.</span>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/TESTS/basic_os_tests.json	Thu Dec 12 10:26:06 2019 +0900
@@ -0,0 +1,10 @@
+{
+    "testcases": [
+        {"name" : "register_and_read_id"},
+        {"name" : "get"},
+        {"name" : "put"},
+        {"name" : "post"},
+        {"name" : "observation"},
+        {"name" : "deregister"}
+    ]
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/TESTS/basic_tests.json	Thu Dec 12 10:26:06 2019 +0900
@@ -0,0 +1,10 @@
+{
+    "testcases": [
+        {"name" : "register"},
+        {"name" : "get"},
+        {"name" : "put"},
+        {"name" : "post"},
+        {"name" : "observation"},
+        {"name" : "deregister"}
+    ]
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/TESTS/deregister.py	Thu Dec 12 10:26:06 2019 +0900
@@ -0,0 +1,53 @@
+"""
+Copyright 2019 ARM Limited
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+"""
+
+# pylint: disable=missing-docstring,useless-super-delegation
+# pylint: disable=line-too-long,method-hidden,relative-import
+
+from icetea_lib.bench import TestStepFail
+from mbed_cloud.exceptions import CloudApiException
+from pelion_helper import PelionBase
+
+
+class Testcase(PelionBase):
+    def __init__(self):
+        PelionBase.__init__(self,
+                            name="deregister",
+                            title="Example application can deregister",
+                            status="released",
+                            type="acceptance",
+                            component=["mbed_cloud_client_example"])
+
+    def setup(self):
+        super(Testcase, self).setup()
+
+    def case(self):
+        resource_path = '/5000/0/1'
+        # Deregister device via POST (/5000/0/1)
+        self.logger.info("Deregister via POST %s", resource_path)
+        try:
+            self.connect_api.execute_resource(device_id=self.device_id,
+                                              resource_path=resource_path,
+                                              timeout=self.restTimeout)
+        except CloudApiException as error:
+            raise TestStepFail("POST request failed with %d and msg %s" % (error.status, error.message))
+
+        # Verify client is deregistered
+        self.logger.info("POST done")
+
+        self.verify_registration("deregistered")
+
+    def teardown(self):
+        self.connect_api.stop_notifications()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/TESTS/device_update.py	Thu Dec 12 10:26:06 2019 +0900
@@ -0,0 +1,64 @@
+"""
+Copyright 2019 ARM Limited
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+"""
+
+# pylint: disable=missing-docstring
+# pylint: disable=line-too-long,method-hidden,relative-import
+
+import time
+from icetea_lib.bench import TestStepFail
+from pelion_helper import PelionBase
+
+
+class Testcase(PelionBase):
+    def __init__(self):
+        PelionBase.__init__(self,
+                            name="device_update",
+                            title="Example application can perform firmware update",
+                            status="released",
+                            type="acceptance",
+                            component=["mbed_cloud_client_example"])
+    def setup(self):
+        super(Testcase, self).setup()
+        super(Testcase, self).setup_update()
+
+    def case(self):
+        # Create filter for update campaign generation, with automatic startup
+        update_filter = self.prepare_campaign_filter(state="scheduled")
+        # Start the update campaign
+        campaign_id = self.post_campaign(update_filter)
+        # Wait for the campaign to reach autostopped state
+        timeout = 600
+        _endtime = time.time() + timeout
+
+        while True:
+            self.delay(10)
+            result = self.check_campaign_state(campaign_id)
+            # Update campaign has stopped
+            if result[0] == "autostopped":
+                break
+
+            if time.time() > _endtime:
+                self.logger.error("Campaign in state: %s, Device in state: %s", result[0], result[1])
+                raise TestStepFail("Campaign did not finish in %d" % timeout)
+
+        # Check for final state of the device
+        final_result = self.check_campaign_state(campaign_id)
+        if final_result[1] != "deployed":
+            raise TestStepFail("Device in %s state, expected deployed" % result[1])
+
+        self.logger.info("Firmware update finished successfully")
+
+    def teardown(self):
+        self.connect_api.stop_notifications()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/TESTS/full_os_tests.json	Thu Dec 12 10:26:06 2019 +0900
@@ -0,0 +1,11 @@
+{
+    "testcases": [
+        {"name" : "register_and_read_id"},
+        {"name" : "get"},
+        {"name" : "put"},
+        {"name" : "post"},
+        {"name" : "observation"},
+        {"name" : "device_update"},
+        {"name" : "deregister"}
+    ]
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/TESTS/full_tests.json	Thu Dec 12 10:26:06 2019 +0900
@@ -0,0 +1,11 @@
+{
+    "testcases": [
+        {"name" : "register"},
+        {"name" : "get"},
+        {"name" : "put"},
+        {"name" : "post"},
+        {"name" : "observation"},
+        {"name" : "device_update"},
+        {"name" : "deregister"}
+    ]
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/TESTS/get.py	Thu Dec 12 10:26:06 2019 +0900
@@ -0,0 +1,49 @@
+"""
+Copyright 2019 ARM Limited
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+"""
+
+# pylint: disable=missing-docstring,useless-super-delegation
+# pylint: disable=line-too-long,method-hidden,relative-import
+
+from icetea_lib.bench import TestStepFail
+from mbed_cloud.exceptions import CloudApiException
+from pelion_helper import PelionBase
+
+
+class Testcase(PelionBase):
+    def __init__(self):
+        PelionBase.__init__(self,
+                            name="get",
+                            title="Example application can perform basic CoAP operation (GET)",
+                            status="released",
+                            type="acceptance",
+                            component=["mbed_cloud_client_example"])
+
+    def setup(self):
+        super(Testcase, self).setup()
+
+    def case(self):
+        resource_path = '/3/0/0'
+        self.logger.info("Testing GET %s", resource_path)
+        try:
+            resource_manufacturer = self.connect_api.get_resource_value(device_id=self.device_id,
+                                                                        resource_path=resource_path,
+                                                                        timeout=self.restTimeout)
+        except CloudApiException as error:
+            raise TestStepFail("GET request failed with %d and msg %s" % (error.status, error.message))
+
+        self.logger.info("Received value %s for %s", resource_manufacturer, resource_path)
+
+    def teardown(self):
+        self.connect_api.stop_notifications()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/TESTS/observation.py	Thu Dec 12 10:26:06 2019 +0900
@@ -0,0 +1,110 @@
+"""
+Copyright 2019 ARM Limited
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+"""
+
+# pylint: disable=missing-docstring,useless-super-delegation
+# pylint: disable=line-too-long,method-hidden,relative-import
+
+import time
+from icetea_lib.bench import TestStepFail
+from mbed_cloud.exceptions import CloudApiException
+from pelion_helper import PelionBase
+
+
+class Testcase(PelionBase):
+    def __init__(self):
+        PelionBase.__init__(self,
+                            name="observation",
+                            title="Example application can respond to resource subscription)",
+                            status="released",
+                            type="acceptance",
+                            component=["mbed_cloud_client_example"])
+
+    def setup(self):
+        super(Testcase, self).setup()
+
+    def case(self):
+        # Test resource subscription
+        resource_path = '/3201/0/5853'
+
+        # 1) Add initial resource value
+        self.logger.info("PUT %s with value %s", resource_path, self.pattern_value1)
+        try:
+            self.connect_api.set_resource_value(device_id=self.device_id,
+                                                resource_path=resource_path,
+                                                resource_value=self.pattern_value1,
+                                                timeout=self.restTimeout)
+        except CloudApiException as error:
+            raise TestStepFail("PUT request failed with %d and msg %s" % (error.status, error.message))
+
+        # Create resource subscription for custom resource (/3201/0/5853)
+        self.logger.info("Testing subscription for %s", resource_path)
+        self.connect_api.start_notifications()
+
+        try:
+            self.connect_api.add_resource_subscription_async(device_id=self.device_id,
+                                                             resource_path=resource_path,
+                                                             callback_fn=self._callback_fn)
+        except CloudApiException as error:
+            raise TestStepFail("Subscription request failed with %d and msg %s" % (error.status, error.message))
+
+        # 2) Update resource value
+        self.logger.info("PUT %s with value %s", resource_path, self.pattern_value2)
+        try:
+            self.connect_api.set_resource_value(device_id=self.device_id,
+                                                resource_path=resource_path,
+                                                resource_value=self.pattern_value2,
+                                                timeout=self.restTimeout)
+        except CloudApiException as error:
+            raise TestStepFail("PUT request failed with %d and msg %s" % (error.status, error.message))
+
+        self.logger.info("Waiting for resource notification")
+        notif_timeout = time.time() + self.restTimeout
+
+        # 3) Wait until we receive some value or timeout
+        while time.time() < notif_timeout:
+            if self.notified_value != "":
+                break
+
+        if self.notified_value != self.pattern_value2:
+            self.logger.error("Web-application received %s as notification, expected %s", self.notified_value, self.pattern_value2)
+            raise TestStepFail("Incorrect/No notification received")
+        else:
+            self.logger.info("Web-application received %s as notification.", self.notified_value)
+
+        # 4) Remove subscription
+        try:
+            self.connect_api.delete_resource_subscription(device_id=self.device_id)
+        except CloudApiException as error:
+            raise TestStepFail("Subscription removal failed with %d and msg %s" % (error.status, error.message))
+
+        # 5) Update resource value to trigger reset from server
+        # This is needed to clean the client subscription status for any follow-up tests
+        self.logger.info("PUT %s with value %s", resource_path, self.pattern_value3)
+        try:
+            self.connect_api.set_resource_value(device_id=self.device_id,
+                                                resource_path=resource_path,
+                                                resource_value=self.pattern_value3,
+                                                timeout=self.restTimeout)
+        except CloudApiException as error:
+            raise TestStepFail("PUT request failed with %d and msg %s" % (error.status, error.message))
+
+    def teardown(self):
+        # Remove subscription
+        try:
+            self.connect_api.delete_resource_subscription(device_id=self.device_id)
+        except CloudApiException as error:
+            raise TestStepFail("Subscription removal failed with %d and msg %s" % (error.status, error.message))
+
+        self.connect_api.stop_notifications()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/TESTS/pelion.tc_cfg	Thu Dec 12 10:26:06 2019 +0900
@@ -0,0 +1,6 @@
+{
+    "device_id"  : "your-device-id",
+    "api_key"    : "your-api-key",
+    "host"       : "https://api.us-east-1.mbedcloud.com",
+    "manifest_id": "your-manifest-id"
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/TESTS/pelion_helper.py	Thu Dec 12 10:26:06 2019 +0900
@@ -0,0 +1,156 @@
+"""
+Copyright 2019 ARM Limited
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+"""
+
+# pylint: disable=missing-docstring,too-many-instance-attributes
+# pylint: disable=line-too-long,method-hidden
+
+import json
+import os
+import uuid
+import requests
+from mbed_cloud import AccountManagementAPI
+from mbed_cloud import ConnectAPI
+from mbed_cloud import DeviceDirectoryAPI
+from mbed_cloud import UpdateAPI
+from icetea_lib.bench import Bench
+from icetea_lib.bench import TestStepFail
+
+
+class PelionBase(Bench):
+    """
+    Base class containing common implementation shared by tests
+    """
+
+    def __init__(self, **kwargs):
+        Bench.__init__(self, **kwargs)
+        self.test_config = None
+        self.device_id = None
+        self.manifest_id = None
+        self.account_api = None
+        self.connect_api = None
+        self.device_api = None
+        self.update_api = None
+        self.rest_headers = None
+        self.rest_address = None
+        self.pattern_value1 = "1000:1000:1000:1000"
+        self.pattern_value2 = "2000:1000:2000:1000"
+        self.pattern_value3 = "3000:1000:3000:1000"
+        self.notified_value = ""
+
+    def setup(self):
+        # Check if API key is in environmental vars
+        if 'MBED_CLOUD_SDK_API_KEY' in os.environ:
+            api_key = (os.environ[str('MBED_CLOUD_SDK_API_KEY')])
+        else:
+            api_key = self.config.get("api_key")
+
+        if not api_key.startswith('ak_'):
+            raise TestStepFail("No API key in MBED_CLOUD_SDK_API_KEY or in pelion.tc_cfg")
+
+        self.device_id = self.config.get("device_id")
+        host = self.config.get("host")
+        self.test_config = {"api_key": api_key, "host": host}
+        self.account_api = AccountManagementAPI(self.test_config)
+        self.connect_api = ConnectAPI(self.test_config)
+        self.device_api = DeviceDirectoryAPI(self.test_config)
+
+        # Additional parameters for handling REST requests without SDK
+        self.rest_headers = {'Authorization': 'Bearer ' + api_key}
+        self.rest_address = self.config.get("host")
+
+        # Init delay due to internal usage of PULL notification in SDK. Notications might be lost between
+        # tests.
+        # TODO: Remove workaround after limitation in SDK has been fixed.
+        self.delay(5)
+
+    def setup_update(self):
+        self.manifest_id = self.config.get("manifest_id")
+        self.update_api = UpdateAPI(self.test_config)
+
+    def _callback_fn(self, device_id, path, value):
+        string = value.decode("utf-8")
+        self.logger.info("Notification for %s received: %r value: %r", device_id, path, string)
+        self.notified_value = string
+
+    def verify_registration(self, expected_state):
+        self.logger.info("Verify device to in state %s", expected_state)
+        device = self.device_api.get_device(self.device_id)
+        if device is None:
+            raise TestStepFail("device_id %s does not exist/is not listed in Device Directory" % self.device_id)
+        else:
+            if device.state == expected_state:
+                self.logger.info("Endpoint %s is in state: %s", self.device_id, expected_state)
+            else:
+                raise TestStepFail("Endpoint %s is in state %s, expected %s" % (self.device_id, device.state, expected_state))
+
+    def prepare_campaign_filter(self, state):
+        # Create filter for the update campaign. Here we only update one endpoint.
+        payload = {}
+        payload["device_filter"] = "id=" + self.device_id
+        payload["root_manifest_id"] = self.manifest_id
+        payload["name"] = str(uuid.uuid4())
+        payload["state"] = state
+        return payload
+
+    ## Function for starting a update campaign
+    ## TODO: Replace with Python SDK implementation
+    def post_campaign(self, payload):
+        req_address = self.rest_address + '/v3/update-campaigns'
+        res = requests.post(req_address, json=payload, headers=self.rest_headers)
+        if res.status_code == 409:
+            # Endpoint can be targeted by only one active campaign. The older campaign must be deleted/stopped.
+            raise TestStepFail("Campaign already exists for device %s" % self.device_id)
+        elif res.status_code != 201:
+            raise TestStepFail("Campaign creation failed with %d" % res.status_code)
+
+        data = res.json()
+        campaign_id = data["id"]
+        return campaign_id
+
+    ## Function for checking firmware update status from Cloud
+    ## TODO: Replace with Python SDK implementation
+    def check_campaign_state(self, campaign_id):
+        results = []
+        base_url = self.rest_address + "/v3/update-campaigns"
+        campaign_url = base_url + "/" + campaign_id
+        metadata_url = campaign_url + "/" + "campaign-device-metadata"
+
+        # Short wait until campaign has been initialized properly.
+        self.delay(5)
+
+        # Fetch basic campaign information
+        response = requests.get(campaign_url, headers=self.rest_headers)
+        if response.status_code != 200:
+            raise TestStepFail("Get Campaign state returned %d" % response.status_code)
+
+        resp = json.loads(response.content)
+        results.append(resp["state"])
+
+        # Fetch campaign metadata information
+        response = requests.get(metadata_url, headers=self.rest_headers)
+        if response.status_code != 200:
+            raise TestStepFail("Get Campaign metadata returned %d" % response.status_code)
+
+        resp = json.loads(response.content)
+        if 'data' in resp and resp['data'][0]['deployment_state']:
+            meta_data = resp['data']
+            device_state = meta_data[0]['deployment_state']
+            results.append(device_state)
+        else:
+            raise TestStepFail("No metadata for %d" % self.device_id)
+
+        return results
+
+    restTimeout = 60
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/TESTS/post.py	Thu Dec 12 10:26:06 2019 +0900
@@ -0,0 +1,51 @@
+"""
+Copyright 2019 ARM Limited
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+"""
+
+# pylint: disable=missing-docstring,useless-super-delegation
+# pylint: disable=line-too-long,method-hidden,relative-import
+
+from icetea_lib.bench import TestStepFail
+from mbed_cloud.exceptions import CloudApiException
+from pelion_helper import PelionBase
+
+
+class Testcase(PelionBase):
+    def __init__(self):
+        PelionBase.__init__(self,
+                            name="post",
+                            title="Example application can perform basic CoAP operation (POST)",
+                            status="released",
+                            type="acceptance",
+                            component=["mbed_cloud_client_example"])
+
+    def setup(self):
+        super(Testcase, self).setup()
+
+    def case(self):
+        resource_path = '/3201/0/5850'
+        # Test basic POST operation
+        # Post/Execute at custom resource (/3201/0/5850)
+        self.logger.info("Testing POST %s", resource_path)
+        try:
+            self.connect_api.execute_resource(device_id=self.device_id,
+                                              resource_path=resource_path,
+                                              timeout=self.restTimeout)
+        except CloudApiException as error:
+            raise TestStepFail("POST request failed with %d and msg %s" % (error.status, error.message))
+
+        self.logger.info("POST done")
+
+    def teardown(self):
+        self.connect_api.stop_notifications()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/TESTS/put.py	Thu Dec 12 10:26:06 2019 +0900
@@ -0,0 +1,63 @@
+"""
+Copyright 2019 ARM Limited
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+"""
+
+# pylint: disable=missing-docstring,useless-super-delegation
+# pylint: disable=line-too-long,method-hidden,relative-import
+
+from icetea_lib.bench import TestStepFail
+from mbed_cloud.exceptions import CloudApiException
+from pelion_helper import PelionBase
+
+
+class Testcase(PelionBase):
+    def __init__(self):
+        PelionBase.__init__(self,
+                            name="put",
+                            title="Example application can perform basic CoAP operation (PUT)",
+                            status="released",
+                            type="acceptance",
+                            component=["mbed_cloud_client_example"])
+
+    def setup(self):
+        super(Testcase, self).setup()
+
+    def case(self):
+        resource_path = '/3201/0/5853'
+        # Write new value to custom resource (/3201/0/5853)
+        self.logger.info("Testing PUT %s with value %s", self.pattern_value1, resource_path)
+        try:
+            self.connect_api.set_resource_value(device_id=self.device_id,
+                                                resource_path=resource_path,
+                                                resource_value=self.pattern_value1,
+                                                timeout=self.restTimeout)
+        except CloudApiException as error:
+            raise TestStepFail("PUT request failed with %d and msg %s" % (error.status, error.message))
+
+        # Read and verify that new value is in effect
+        try:
+            pattern_value_new = self.connect_api.get_resource_value(device_id=self.device_id,
+                                                                    resource_path=resource_path,
+                                                                    timeout=self.restTimeout)
+        except CloudApiException as error:
+            raise TestStepFail("GET request failed with %d and msg %s" % (error.status, error.message))
+
+        self.logger.info("Read back value %s for %s", pattern_value_new, resource_path)
+
+        if self.pattern_value1 != pattern_value_new:
+            raise TestStepFail("Pattern %s written does not match the pattern %s read back" % (self.pattern_value1,
+                                                                                               pattern_value_new))
+
+    def teardown(self):
+        self.connect_api.stop_notifications()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/TESTS/register.py	Thu Dec 12 10:26:06 2019 +0900
@@ -0,0 +1,37 @@
+"""
+Copyright 2019 ARM Limited
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+"""
+
+# pylint: disable=missing-docstring,useless-super-delegation
+# pylint: disable=method-hidden,relative-import
+
+from pelion_helper import PelionBase
+
+
+class Testcase(PelionBase):
+    def __init__(self):
+        PelionBase.__init__(self,
+                            name="register",
+                            title="Example application can perform basic CoAP operation (GET)",
+                            status="released",
+                            type="acceptance",
+                            component=["mbed_cloud_client_example"])
+    def setup(self):
+        super(Testcase, self).setup()
+
+    def case(self):
+        self.verify_registration("registered")
+
+    def teardown(self):
+        self.connect_api.stop_notifications()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/TESTS/register_and_read_id.py	Thu Dec 12 10:26:06 2019 +0900
@@ -0,0 +1,80 @@
+"""
+Copyright 2019 ARM Limited
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+"""
+
+# pylint: disable=missing-docstring,useless-super-delegation
+# pylint: disable=method-hidden,relative-import
+
+import time
+import json
+from pelion_helper import PelionBase
+
+
+class Testcase(PelionBase):
+    def __init__(self):
+        PelionBase.__init__(self,
+                            name="register_and_read_id",
+                            title="Example application can register and prints out its device ID",
+                            status="released",
+                            type="acceptance",
+                            component=["mbed_cloud_client_example"],
+                            requirements={
+                                "duts": {  # default requirements for all nodes
+                                    '*': {
+                                        "count": 1,
+                                        "type": "hardware",
+                                        "application": {
+                                            "init_cli_cmds": [],
+                                            "post_cli_cmds": []
+                                        }
+                                    }
+                                }
+                            })
+                                
+    def setup(self):
+        super(Testcase, self).setup()
+        self.__timeout = 120
+        self.__endtime = time.time() + self.__timeout
+
+
+    def case(self):
+
+        while True:
+            try:
+                self.verify_trace(1, ['Device ID'], True)
+                break
+            except:
+                if time.time() > self.__endtime:
+                    raise TestStepFail('Timeout: Did not find Endpoint Name within %d seconds' % self.__timeout)
+                else:
+                    pass
+
+        # Get the endpoint from the logs
+        self.logger.info("Reading device ID from console.")
+        dev_id_raw = ""
+        dev_id_raw = list(filter(lambda x: "Device ID" in x, self.duts[0].traces))
+        device_id = dev_id_raw[0].split()[2]
+        self.logger.info("Writing Device ID %s to pelion.tc_cfg", device_id)
+        # Store the Device ID to pelion.tc_cfg
+        with open("TESTS/pelion.tc_cfg") as stream:
+            data = json.load(stream)
+            data["device_id"] = device_id
+
+        with open("TESTS/pelion.tc_cfg", 'w') as stream:
+            json.dump(data, stream)
+
+        self.verify_registration("registered")
+
+    def teardown(self):
+        self.connect_api.stop_notifications()
Binary file bootloader/mbed-bootloader-disco_l475vg_iot01a-external_kvstore-qspif.bin has changed
Binary file bootloader/mbed-bootloader-k64f-block_device-kvstore-v4.0.0.bin has changed
Binary file bootloader/mbed-bootloader-k64f-internal_flash-no_rot-v4.0.1.bin has changed
Binary file bootloader/mbed-bootloader-k66f-internal_flash-no_rot-v4.0.0.bin has changed
Binary file bootloader/mbed-bootloader-nucleo_f411re-block_device-kvstore-v4.0.0.bin has changed
Binary file bootloader/mbed-bootloader-nucleo_f429zi-internal_flash-no_rot-v4.0.0.bin has changed
Binary file bootloader/mbed-bootloader-tmpm46b-internal_flash-no_rot-v4.0.1.bin has changed
Binary file bootloader/mbed-bootloader-ublox_evk_odin_w2-block_device-kvstore-v4.0.0.bin has changed
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/drivers/COMPONENT_WIFI_IDW01M1.lib	Thu Dec 12 10:26:06 2019 +0900
@@ -0,0 +1,1 @@
+https://github.com/ARMmbed/wifi-x-nucleo-idw01m1/#a00282000178bd75d57f67c161db11faa2b2ce74
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/drivers/COMPONENT_WIFI_ISM43362.lib	Thu Dec 12 10:26:06 2019 +0900
@@ -0,0 +1,1 @@
+https://github.com/ARMmbed/wifi-ism43362/#1c040113be0fe2a14c4b6107e1db3ec24e8b20b6
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main.cpp	Thu Dec 12 10:26:06 2019 +0900
@@ -0,0 +1,214 @@
+// ----------------------------------------------------------------------------
+// Copyright 2016-2019 ARM Ltd.
+//
+// SPDX-License-Identifier: Apache-2.0
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ----------------------------------------------------------------------------
+#ifndef MBED_TEST_MODE
+#include "mbed.h"
+#include "kv_config.h"
+#include "mbed-cloud-client/MbedCloudClient.h" // Required for new MbedCloudClient()
+#include "factory_configurator_client.h"       // Required for fcc_* functions and FCC_* defines
+#include "m2mresource.h"                       // Required for M2MResource
+
+#include "mbed-trace/mbed_trace.h"             // Required for mbed_trace_*
+
+// Pointers to the resources that will be created in main_application().
+static MbedCloudClient *cloud_client;
+static bool cloud_client_running = true;
+static NetworkInterface *network = NULL;
+
+// Fake entropy needed for non-TRNG boards. Suitable only for demo devices.
+const uint8_t MBED_CLOUD_DEV_ENTROPY[] = { 0xf6, 0xd6, 0xc0, 0x09, 0x9e, 0x6e, 0xf2, 0x37, 0xdc, 0x29, 0x88, 0xf1, 0x57, 0x32, 0x7d, 0xde, 0xac, 0xb3, 0x99, 0x8c, 0xb9, 0x11, 0x35, 0x18, 0xeb, 0x48, 0x29, 0x03, 0x6a, 0x94, 0x6d, 0xe8, 0x40, 0xc0, 0x28, 0xcc, 0xe4, 0x04, 0xc3, 0x1f, 0x4b, 0xc2, 0xe0, 0x68, 0xa0, 0x93, 0xe6, 0x3a };
+
+static M2MResource* m2m_get_res;
+static M2MResource* m2m_put_res;
+static M2MResource* m2m_post_res;
+static M2MResource* m2m_deregister_res;
+
+void print_client_ids(void)
+{
+    printf("Account ID: %s\n", cloud_client->endpoint_info()->account_id.c_str());
+    printf("Endpoint name: %s\n", cloud_client->endpoint_info()->internal_endpoint_name.c_str());
+    printf("Device Id: %s\n\n", cloud_client->endpoint_info()->endpoint_name.c_str());
+}
+
+void button_press(void)
+{
+    m2m_get_res->set_value(m2m_get_res->get_value_int() + 1);
+    printf("Counter %" PRIu64 "\n", m2m_get_res->get_value_int());
+}
+
+void put_update(const char* /*object_name*/)
+{
+    printf("PUT update %d\n", (int)m2m_put_res->get_value_int());
+}
+
+void execute_post(void* /*arguments*/)
+{
+    printf("POST executed\n");
+}
+
+void deregister_client(void)
+{
+    printf("Unregistering and disconnecting from the network.\n");
+    cloud_client->close();
+}
+
+void deregister(void* /*arguments*/)
+{
+    printf("POST deregister executed\n");
+    m2m_deregister_res->send_delayed_post_response();
+
+    deregister_client();
+}
+
+void client_registered(void)
+{
+    printf("Client registered.\n");
+    print_client_ids();
+}
+
+void client_unregistered(void)
+{
+    printf("Client unregistered.\n");
+    (void) network->disconnect();
+    cloud_client_running = false;
+}
+
+void client_error(int err)
+{
+    printf("client_error(%d) -> %s\n", err, cloud_client->error_description());
+}
+
+void update_progress(uint32_t progress, uint32_t total)
+{
+    uint8_t percent = (uint8_t)((uint64_t)progress * 100 / total);
+    printf("Update progress = %" PRIu8 "%%\n", percent);
+}
+
+int main(void)
+{
+    int status;
+
+    status = mbed_trace_init();
+    if (status != 0) {
+        printf("mbed_trace_init() failed with %d\n", status);
+        return -1;
+    }
+
+    // Mount default kvstore
+    printf("Application ready\n");
+    status = kv_init_storage_config();
+    if (status != MBED_SUCCESS) {
+        printf("kv_init_storage_config() - failed, status %d\n", status);
+        return -1;
+    }
+
+    // Connect with NetworkInterface
+    printf("Connect to network\n");
+    network = NetworkInterface::get_default_instance();
+    if (network == NULL) {
+        printf("Failed to get default NetworkInterface\n");
+        return -1;
+    }
+    status = network->connect();
+    if (status != NSAPI_ERROR_OK) {
+        printf("NetworkInterface failed to connect with %d\n", status);
+        return -1;
+    }
+
+    printf("Network initialized, connected with IP %s\n\n", network->get_ip_address());
+
+    // Run developer flow
+    printf("Start developer flow\n");
+    status = fcc_init();
+    if (status != FCC_STATUS_SUCCESS) {
+        printf("fcc_init() failed with %d\n", status);
+        return -1;
+    }
+
+    // Inject hardcoded entropy for the device. Suitable only for demo devices.
+    (void) fcc_entropy_set(MBED_CLOUD_DEV_ENTROPY, sizeof(MBED_CLOUD_DEV_ENTROPY));
+    status = fcc_developer_flow();
+    if (status != FCC_STATUS_SUCCESS && status != FCC_STATUS_KCM_FILE_EXIST_ERROR && status != FCC_STATUS_CA_ERROR) {
+        printf("fcc_developer_flow() failed with %d\n", status);
+        return -1;
+    }
+
+    printf("Create resources\n");
+    M2MObjectList m2m_obj_list;
+
+    // GET resource 3200/0/5501
+    m2m_get_res = M2MInterfaceFactory::create_resource(m2m_obj_list, 3200, 0, 5501, M2MResourceInstance::INTEGER, M2MBase::GET_ALLOWED);
+    if (m2m_get_res->set_value(0) != true) {
+        printf("m2m_get_res->set_value() failed\n");
+        return -1;
+    }
+
+    // PUT resource 3201/0/5853
+    m2m_put_res = M2MInterfaceFactory::create_resource(m2m_obj_list, 3201, 0, 5853, M2MResourceInstance::INTEGER, M2MBase::GET_PUT_ALLOWED);
+    if (m2m_put_res->set_value(0) != true) {
+        printf("m2m_led_res->set_value() failed\n");
+        return -1;
+    }
+    if (m2m_put_res->set_value_updated_function(put_update) != true) {
+        printf("m2m_put_res->set_value_updated_function() failed\n");
+        return -1;
+    }
+
+    // POST resource 3201/0/5850
+    m2m_post_res = M2MInterfaceFactory::create_resource(m2m_obj_list, 3201, 0, 5850, M2MResourceInstance::INTEGER, M2MBase::POST_ALLOWED);
+    if (m2m_post_res->set_execute_function(execute_post) != true) {
+        printf("m2m_post_res->set_execute_function() failed\n");
+        return -1;
+    }
+
+    // POST resource 5000/0/1 to trigger deregister.
+    m2m_deregister_res = M2MInterfaceFactory::create_resource(m2m_obj_list, 5000, 0, 1, M2MResourceInstance::INTEGER, M2MBase::POST_ALLOWED);
+
+    // Use delayed response
+    m2m_deregister_res->set_delayed_response(true);
+
+    if (m2m_deregister_res->set_execute_function(deregister) != true) {
+        printf("m2m_post_res->set_execute_function() failed\n");
+        return -1;
+    }
+
+    printf("Register Pelion Device Management Client\n\n");
+    cloud_client = new MbedCloudClient(client_registered, client_unregistered, client_error, NULL, update_progress);
+    cloud_client->add_objects(m2m_obj_list);
+    cloud_client->setup(network); // cloud_client->setup(NULL); -- https://jira.arm.com/browse/IOTCLT-3114
+
+    while(cloud_client_running) {
+        int in_char = getchar();
+        if (in_char == 'i') {
+            print_client_ids(); // When 'i' is pressed, print endpoint info
+            continue;
+        } else if (in_char == 'r') {
+            (void) fcc_storage_delete(); // When 'r' is pressed, erase storage and reboot the board.
+            printf("Storage erased, rebooting the device.\n\n");
+            wait(1);
+            NVIC_SystemReset();
+        } else if (in_char > 0 && in_char != 0x03) { // Ctrl+C is 0x03 in Mbed OS and Linux returns negative number
+            button_press(); // Simulate button press
+            continue;
+        }
+        deregister_client();
+        break;
+    }
+    return 0;
+}
+
+#endif /* MBED_TEST_MODE */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mbed-cloud-client.lib	Thu Dec 12 10:26:06 2019 +0900
@@ -0,0 +1,1 @@
+https://github.com/ARMmbed/mbed-cloud-client/#84bc0d4582472c07630777da9b8330e2d5ed098a
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mbed-os.lib	Thu Dec 12 10:26:06 2019 +0900
@@ -0,0 +1,1 @@
+https://github.com/ARMmbed/mbed-os/#b6e5a0a8afa34dec9dae8963778aebce0c82a54b
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mbed_app.json	Thu Dec 12 10:26:06 2019 +0900
@@ -0,0 +1,191 @@
+{
+    "macros": [
+        "MBED_TRACE_MAX_LEVEL=TRACE_LEVEL_INFO",
+        "MBED_CLIENT_USER_CONFIG_FILE=\"mbed_cloud_client_user_config.h\"",
+        "MBED_CLOUD_CLIENT_USER_CONFIG_FILE=\"mbed_cloud_client_user_config.h\"",
+        "ARM_UC_USE_PAL_BLOCKDEVICE=1",
+        "PAL_PLATFORM_DEFINED_CONFIGURATION=\"mbedOS_SST.h\"",
+        "PAL_USER_DEFINED_CONFIGURATION=\"mbedOS_SST.h\""
+    ],
+    "target_overrides": {
+        "*": {
+            "target.features_add"                       : ["BOOTLOADER", "STORAGE"],
+            "platform.stdio-baud-rate"                  : 115200,
+            "platform.stdio-convert-newlines"           : true,
+            "platform.stdio-buffered-serial"            : true,
+            "platform.stdio-flush-at-exit"              : true,
+            "rtos.main-thread-stack-size"               : 5120,
+            "update-client.storage-locations"           : 1,
+            "mbed-trace.enable"                         : null,
+            "events.shared-stacksize"                   : 2048,
+            "nsapi.default-wifi-security"               : "WPA_WPA2",
+            "nsapi.default-wifi-ssid"                   : "\"SSID\"",
+            "nsapi.default-wifi-password"               : "\"PASSWORD\""
+        },
+        "Freescale": {
+            "lwip.mem-size"                             : 12500
+        },
+        "STM_EMAC": {
+            "lwip.pbuf-pool-size"                       : 16,
+            "lwip.mem-size"                             : 12500
+        },
+        "K64F": {
+            "target.macros_add"                         : ["MBEDTLS_USER_CONFIG_FILE=\"mbedTLSConfig_mbedOS.h\""],
+            "target.network-default-interface-type"     : "ETHERNET",
+            "target.bootloader_img"                     : "bootloader/mbed-bootloader-k64f-internal_flash-no_rot-v4.0.1.bin",
+            "target.header_offset"                      : "0x8000",
+            "target.app_offset"                         : "0x8400",
+            "target.restrict_size"                      : "0x74000",
+            "update-client.bootloader-details"          : "0x4A6C",
+            "update-client.application-details"         : "(32*1024)",
+            "update-client.storage-address"             : "(MBED_CONF_STORAGE_TDB_INTERNAL_INTERNAL_BASE_ADDRESS+MBED_CONF_STORAGE_TDB_INTERNAL_INTERNAL_SIZE)",
+            "update-client.storage-size"                : "(512*1024-MBED_CONF_STORAGE_TDB_INTERNAL_INTERNAL_SIZE)",
+            "update-client.storage-locations"           : 1,
+            "update-client.storage-page"                : 8,
+            "mbed-cloud-client.update-storage"          : "ARM_UCP_FLASHIAP",
+            "storage_tdb_internal.internal_base_address": "(512*1024)",
+            "storage_tdb_internal.internal_size"        : "(48*1024)",
+            "storage.storage_type"                      : "TDB_INTERNAL"
+        },
+        "K66F": {
+            "target.macros_add"                         : ["MBEDTLS_USER_CONFIG_FILE=\"mbedTLSConfig_mbedOS.h\""],
+            "target.network-default-interface-type"     : "ETHERNET",
+            "target.bootloader_img"                     : "bootloader/mbed-bootloader-k66f-internal_flash-no_rot-v4.0.0.bin",
+            "target.header_offset"                      : "0x8000",
+            "target.app_offset"                         : "0x8400",
+            "update-client.bootloader-details"          : "0x4D58",
+            "update-client.application-details"         : "(32*1024)",
+            "update-client.storage-address"             : "(MBED_CONF_STORAGE_TDB_INTERNAL_INTERNAL_BASE_ADDRESS+MBED_CONF_STORAGE_TDB_INTERNAL_INTERNAL_SIZE)",
+            "update-client.storage-size"                : "(1024*1024-MBED_CONF_STORAGE_TDB_INTERNAL_INTERNAL_SIZE)",
+            "update-client.storage-page"                : 8,
+            "mbed-cloud-client.update-storage"          : "ARM_UCP_FLASHIAP",
+            "storage_tdb_internal.internal_base_address": "(1024*1024)",
+            "storage_tdb_internal.internal_size"        : "(96*1024)",
+            "storage.storage_type"                      : "TDB_INTERNAL"
+        },
+        "NUCLEO_F429ZI": {
+            "target.macros_add"                         : ["MBEDTLS_USER_CONFIG_FILE=\"mbedTLSConfig_mbedOS.h\""],
+            "target.network-default-interface-type"     : "ETHERNET",
+            "target.bootloader_img"                     : "bootloader/mbed-bootloader-nucleo_f429zi-internal_flash-no_rot-v4.0.0.bin",
+            "target.header_offset"                      : "0x8000",
+            "target.app_offset"                         : "0x8400",
+            "update-client.bootloader-details"          : "0x080078CC",
+            "update-client.application-details"         : "(MBED_ROM_START + MBED_BOOTLOADER_SIZE)",
+            "update-client.storage-address"             : "(MBED_CONF_STORAGE_TDB_INTERNAL_INTERNAL_BASE_ADDRESS+MBED_CONF_STORAGE_TDB_INTERNAL_INTERNAL_SIZE)",
+            "update-client.storage-size"                : "(1024*1024-MBED_CONF_STORAGE_TDB_INTERNAL_INTERNAL_SIZE)",
+            "update-client.storage-page"                : 1,
+            "mbed-cloud-client.update-storage"          : "ARM_UCP_FLASHIAP",
+            "storage_tdb_internal.internal_base_address": "(MBED_ROM_START+1024*1024)",
+            "storage_tdb_internal.internal_size"        : "(128*1024)",
+            "storage.storage_type"                      : "TDB_INTERNAL"
+        },
+        "UBLOX_EVK_ODIN_W2": {
+            "target.macros_add"                         : ["MBEDTLS_USER_CONFIG_FILE=\"mbedTLSConfig_mbedOS.h\""],
+            "target.network-default-interface-type"     : "WIFI",
+            "target.bootloader_img"                     : "bootloader/mbed-bootloader-ublox_evk_odin_w2-block_device-kvstore-v4.0.0.bin",
+            "target.header_offset"                      : "0x10000",
+            "target.app_offset"                         : "0x10400",
+            "target.components_add"                     : ["SD"],
+            "update-client.bootloader-details"          : "0x08007300",
+            "update-client.application-details"         : "(0x08000000+64*1024)",
+            "update-client.storage-address"             : "(1024*1024*64)",
+            "update-client.storage-size"                : "((MBED_ROM_START + MBED_ROM_SIZE - APPLICATION_ADDR) * MBED_CONF_UPDATE_CLIENT_STORAGE_LOCATIONS)",
+            "mbed-cloud-client.update-storage"          : "ARM_UCP_FLASHIAP_BLOCKDEVICE",
+            "storage_filesystem.internal_base_address"  : "(0x08000000+32*1024)",
+            "storage_filesystem.rbp_internal_size"      : "(32*1024)",
+            "storage_filesystem.external_base_address"  : "(0x0)",
+            "storage_filesystem.external_size"          : "(1024*1024*64)",
+            "storage.storage_type"                      : "FILESYSTEM",
+            "storage_filesystem.filesystem"             : "LITTLE",
+            "storage_filesystem.blockdevice"            : "SD",
+            "target.lse_available"                      : 0,
+            "target.macros_remove"                      : ["MBEDTLS_CONFIG_HW_SUPPORT"]
+        },
+        "NUCLEO_F411RE": {
+            "target.extra_labels_add"                   : ["PSA"],
+            "target.macros_add"                         : ["MBEDTLS_USER_CONFIG_FILE=\"mbedTLSConfig_mbedOS_SW_TRNG_PSA.h\"", "MBEDTLS_PSA_CRYPTO_C", "MBEDTLS_ENTROPY_NV_SEED", "PAL_USE_HW_TRNG=0"],
+            "target.network-default-interface-type"     : "WIFI",
+            "target.bootloader_img"                     : "bootloader/mbed-bootloader-nucleo_f411re-block_device-kvstore-v4.0.0.bin",
+            "target.header_offset"                      : "0x10000",
+            "target.app_offset"                         : "0x10400",
+            "target.components_add"                     : ["SD", "WIFI_IDW01M1"],
+            "idw0xx1.provide-default"                   : true,
+            "idw0xx1.tx"                                : "PA_9",
+            "idw0xx1.rx"                                : "PA_10",
+            "update-client.bootloader-details"          : "(0x08000000+30*1024)",
+            "update-client.application-details"         : "(0x08000000+64*1024)",
+            "update-client.storage-address"             : "(1024*1024*64)",
+            "update-client.storage-size"                : "((MBED_ROM_START + MBED_ROM_SIZE - APPLICATION_ADDR) * MBED_CONF_UPDATE_CLIENT_STORAGE_LOCATIONS)",
+            "mbed-cloud-client.update-storage"          : "ARM_UCP_FLASHIAP_BLOCKDEVICE",
+            "storage_filesystem.filesystem"             : "LITTLE",
+            "storage_filesystem.blockdevice"            : "SD",
+            "storage_filesystem.internal_base_address"  : "(0x08000000+32*1024)",
+            "storage_filesystem.rbp_internal_size"      : "(32*1024)",
+            "storage.storage_type"                      : "FILESYSTEM",
+            "storage_filesystem.external_base_address"  : "(0x0)",
+            "storage_filesystem.external_size"          : "(1024*1024*64)",
+            "events.shared-stacksize"                   : 2048,
+            "events.shared-eventsize"                   : 1024,
+            "drivers.uart-serial-rxbuf-size"            : 1024,
+            "drivers.uart-serial-txbuf-size"            : 1024,
+            "target.macros_remove"                      : ["MBEDTLS_CONFIG_HW_SUPPORT"],
+            "sd.SPI_MOSI"                               : "PC_3",
+            "sd.SPI_MISO"                               : "PC_2",
+            "sd.SPI_CLK"                                : "PC_7",
+            "sd.SPI_CS"                                 : "PB_9"
+        },
+        "DISCO_L475VG_IOT01A": {
+            "target.macros_add"                         : ["MBEDTLS_USER_CONFIG_FILE=\"mbedTLSConfig_mbedOS.h\""],
+            "target.network-default-interface-type"     : "WIFI",
+            "target.bootloader_img"                     : "bootloader/mbed-bootloader-disco_l475vg_iot01a-external_kvstore-qspif.bin",
+            "target.header_offset"                      : "0x11000",
+            "target.app_offset"                         : "0x11400",
+            "target.components_add"                     : ["QSPIF", "WIFI_ISM43362"],
+            "bootloader-size"                           : "(36*1024)",
+            "ism43362.read-thread-stack-size"           : 1024,
+            "mbed-client-pal.pal-max-frag-len"          : 1,
+            "mbed-client.sn-coap-max-blockwise-payload-size": 256,
+            "mbed-cloud-client.update-storage"          : "ARM_UCP_FLASHIAP_BLOCKDEVICE",
+            "storage.storage_type"                      : "FILESYSTEM",
+            "storage_filesystem.filesystem"             : "LITTLE",
+            "storage_filesystem.blockdevice"            : "QSPIF",
+            "storage_filesystem.external_size"          : "(1024 * 1024)",
+            "storage_filesystem.external_base_address"  : "(0)",
+            "storage_filesystem.rbp_internal_size"      : "(32 * 1024)",
+            "storage_filesystem.internal_base_address"  : "(MBED_ROM_START + MBED_BOOTLOADER_SIZE)",
+            "update-client.application-details"         : "(MBED_CONF_STORAGE_FILESYSTEM_INTERNAL_BASE_ADDRESS + MBED_CONF_STORAGE_FILESYSTEM_RBP_INTERNAL_SIZE)",
+            "update-client.bootloader-details"          : "0x800882c",
+            "update-client.firmware-header-version"     : "2",
+            "update-client.storage-address"             : "(MBED_CONF_STORAGE_FILESYSTEM_EXTERNAL_BASE_ADDRESS + MBED_CONF_STORAGE_FILESYSTEM_EXTERNAL_SIZE)",
+            "update-client.storage-locations"           : 1,
+            "update-client.storage-size"                : "((MBED_ROM_START + MBED_ROM_SIZE - APPLICATION_ADDR) * MBED_CONF_UPDATE_CLIENT_STORAGE_LOCATIONS)"
+        },
+        "TMPM46B": {
+            "target.macros_add"                         : ["MBEDTLS_USER_CONFIG_FILE=\"mbedTLSConfig_mbedOS.h\""],
+            "target.components_add"                     : ["FLASHIAP"],
+            "target.network-default-interface-type"     : "WIFI",
+            "esp8266.provide-default"                   : true,
+            "esp8266.tx"                                : "D1",
+            "esp8266.rx"                                : "D0",
+            "target.bootloader_img"                     : "bootloader/mbed-bootloader-tmpm46b-internal_flash-no_rot-v4.0.1.bin",
+            "target.header_offset"                      : "0x8000",
+            "target.app_offset"                         : "0x8400",
+            "target.restrict_size"                      : "0x70000",
+            "update-client.application-details"         : "(32*1024)",
+            "update-client.storage-address"             : "(MBED_CONF_STORAGE_TDB_INTERNAL_INTERNAL_BASE_ADDRESS+MBED_CONF_STORAGE_TDB_INTERNAL_INTERNAL_SIZE)",
+            "update-client.storage-size"                : "(512*1024-MBED_CONF_STORAGE_TDB_INTERNAL_INTERNAL_SIZE)",
+            "update-client.storage-locations"           : 1,
+            "mbed-cloud-client.update-storage"          : "ARM_UCP_FLASHIAP",
+            "storage_tdb_internal.internal_base_address": "(512*1024)",
+            "storage_tdb_internal.internal_size"        : "(64*1024)",
+            "storage.storage_type"                      : "TDB_INTERNAL"
+        }
+    },
+    "config": {
+        "bootloader-size": {
+            "help"      : "Helper macro to enable calculation of rom regions. target.header_offset and target.app_offset still needs to be calculated manually, though.",
+            "value"     : "(32*1024)",
+            "macro_name": "MBED_BOOTLOADER_SIZE"
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mbed_cloud_client_user_config.h	Thu Dec 12 10:26:06 2019 +0900
@@ -0,0 +1,41 @@
+// ----------------------------------------------------------------------------
+// Copyright 2019 ARM Ltd.
+//
+// SPDX-License-Identifier: Apache-2.0
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ----------------------------------------------------------------------------
+
+#ifndef MBED_CLOUD_CLIENT_USER_CONFIG_H
+#define MBED_CLOUD_CLIENT_USER_CONFIG_H
+
+#define MBED_CLOUD_CLIENT_ENDPOINT_TYPE         "default"
+#define MBED_CLOUD_CLIENT_TRANSPORT_MODE_TCP
+#define MBED_CLOUD_CLIENT_LIFETIME              3600
+
+#ifdef MBED_CONF_MBED_CLIENT_SN_COAP_MAX_BLOCKWISE_PAYLOAD_SIZE
+    #define SN_COAP_MAX_BLOCKWISE_PAYLOAD_SIZE    MBED_CONF_MBED_CLIENT_SN_COAP_MAX_BLOCKWISE_PAYLOAD_SIZE
+#else
+    #define SN_COAP_MAX_BLOCKWISE_PAYLOAD_SIZE      512
+#endif
+
+/* set flag to enable update support in mbed Cloud client */
+#define MBED_CLOUD_CLIENT_SUPPORT_UPDATE
+
+/* set download buffer size in bytes (min. 1024 bytes) */
+#define MBED_CLOUD_CLIENT_UPDATE_BUFFER          1024
+
+#define MBED_CLOUD_DEV_UPDATE_CERT
+#define MBED_CLOUD_DEV_UPDATE_ID
+
+#endif /* MBED_CLOUD_CLIENT_USER_CONFIG_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mbed_cloud_dev_credentials.c	Thu Dec 12 10:26:06 2019 +0900
@@ -0,0 +1,54 @@
+// ----------------------------------------------------------------------------
+// Copyright 2019 ARM Ltd.
+//
+// SPDX-License-Identifier: Apache-2.0
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ----------------------------------------------------------------------------
+
+#ifndef __MBED_CLOUD_DEV_CREDENTIALS_H__
+#define __MBED_CLOUD_DEV_CREDENTIALS_H__
+
+#error "Replace mbed_cloud_dev_credentials.c with your own developer cert."
+
+#include <inttypes.h>
+
+const char MBED_CLOUD_DEV_BOOTSTRAP_ENDPOINT_NAME[] = "";
+const char MBED_CLOUD_DEV_ACCOUNT_ID[] = "";
+const char MBED_CLOUD_DEV_BOOTSTRAP_SERVER_URI[] = "";
+
+const uint8_t MBED_CLOUD_DEV_BOOTSTRAP_DEVICE_CERTIFICATE[] =
+{ 0x0 };
+
+const uint8_t MBED_CLOUD_DEV_BOOTSTRAP_SERVER_ROOT_CA_CERTIFICATE[] =
+{ 0x0 };
+
+const uint8_t MBED_CLOUD_DEV_BOOTSTRAP_DEVICE_PRIVATE_KEY[] =
+{ 0x0 };
+
+const char MBED_CLOUD_DEV_MANUFACTURER[] = "dev_manufacturer";
+
+const char MBED_CLOUD_DEV_MODEL_NUMBER[] = "dev_model_num";
+
+const char MBED_CLOUD_DEV_SERIAL_NUMBER[] = "0";
+
+const char MBED_CLOUD_DEV_DEVICE_TYPE[] = "dev_device_type";
+
+const char MBED_CLOUD_DEV_HARDWARE_VERSION[] = "dev_hardware_version";
+
+const uint32_t MBED_CLOUD_DEV_MEMORY_TOTAL_KB = 0;
+const uint32_t MBED_CLOUD_DEV_BOOTSTRAP_DEVICE_CERTIFICATE_SIZE = sizeof(MBED_CLOUD_DEV_BOOTSTRAP_DEVICE_CERTIFICATE);
+const uint32_t MBED_CLOUD_DEV_BOOTSTRAP_SERVER_ROOT_CA_CERTIFICATE_SIZE = sizeof(MBED_CLOUD_DEV_BOOTSTRAP_SERVER_ROOT_CA_CERTIFICATE);
+const uint32_t MBED_CLOUD_DEV_BOOTSTRAP_DEVICE_PRIVATE_KEY_SIZE = sizeof(MBED_CLOUD_DEV_BOOTSTRAP_DEVICE_PRIVATE_KEY);
+
+#endif //__MBED_CLOUD_DEV_CREDENTIALS_H__
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/update_default_resources.c	Thu Dec 12 10:26:06 2019 +0900
@@ -0,0 +1,41 @@
+// ----------------------------------------------------------------------------
+// Copyright 2016-2017 ARM Ltd.
+//
+// SPDX-License-Identifier: Apache-2.0
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ----------------------------------------------------------------------------
+ 
+#ifdef MBED_CLOUD_CLIENT_USER_CONFIG_FILE
+#include MBED_CLOUD_CLIENT_USER_CONFIG_FILE
+#endif
+ 
+#include <stdint.h>
+ 
+#ifdef MBED_CLOUD_DEV_UPDATE_ID
+const uint8_t arm_uc_vendor_id[16] = { "dev_manufacturer" };
+const uint16_t arm_uc_vendor_id_size = sizeof(arm_uc_vendor_id);
+ 
+const uint8_t arm_uc_class_id[16]  = { "dev_model_number" };
+const uint16_t arm_uc_class_id_size = sizeof(arm_uc_class_id);
+#endif
+ 
+#ifdef MBED_CLOUD_DEV_UPDATE_CERT
+const uint8_t arm_uc_default_fingerprint[32] = { 0 };
+const uint16_t arm_uc_default_fingerprint_size =
+    sizeof(arm_uc_default_fingerprint);
+ 
+const uint8_t arm_uc_default_certificate[1] = { 0 };
+const uint16_t arm_uc_default_certificate_size =
+    sizeof(arm_uc_default_certificate);
+#endif
\ No newline at end of file