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)

Committer:
MACRUM
Date:
Thu Dec 12 10:26:06 2019 +0900
Revision:
0:9f917a7bf2da
Initial commit.

Who changed what in which revision?

UserRevisionLine numberNew contents of line
MACRUM 0:9f917a7bf2da 1 """
MACRUM 0:9f917a7bf2da 2 Copyright 2019 ARM Limited
MACRUM 0:9f917a7bf2da 3 Licensed under the Apache License, Version 2.0 (the "License");
MACRUM 0:9f917a7bf2da 4 you may not use this file except in compliance with the License.
MACRUM 0:9f917a7bf2da 5 You may obtain a copy of the License at
MACRUM 0:9f917a7bf2da 6
MACRUM 0:9f917a7bf2da 7 http://www.apache.org/licenses/LICENSE-2.0
MACRUM 0:9f917a7bf2da 8
MACRUM 0:9f917a7bf2da 9 Unless required by applicable law or agreed to in writing, software
MACRUM 0:9f917a7bf2da 10 distributed under the License is distributed on an "AS IS" BASIS,
MACRUM 0:9f917a7bf2da 11 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
MACRUM 0:9f917a7bf2da 12 See the License for the specific language governing permissions and
MACRUM 0:9f917a7bf2da 13 limitations under the License.
MACRUM 0:9f917a7bf2da 14 """
MACRUM 0:9f917a7bf2da 15
MACRUM 0:9f917a7bf2da 16 # pylint: disable=missing-docstring,too-many-instance-attributes
MACRUM 0:9f917a7bf2da 17 # pylint: disable=line-too-long,method-hidden
MACRUM 0:9f917a7bf2da 18
MACRUM 0:9f917a7bf2da 19 import json
MACRUM 0:9f917a7bf2da 20 import os
MACRUM 0:9f917a7bf2da 21 import uuid
MACRUM 0:9f917a7bf2da 22 import requests
MACRUM 0:9f917a7bf2da 23 from mbed_cloud import AccountManagementAPI
MACRUM 0:9f917a7bf2da 24 from mbed_cloud import ConnectAPI
MACRUM 0:9f917a7bf2da 25 from mbed_cloud import DeviceDirectoryAPI
MACRUM 0:9f917a7bf2da 26 from mbed_cloud import UpdateAPI
MACRUM 0:9f917a7bf2da 27 from icetea_lib.bench import Bench
MACRUM 0:9f917a7bf2da 28 from icetea_lib.bench import TestStepFail
MACRUM 0:9f917a7bf2da 29
MACRUM 0:9f917a7bf2da 30
MACRUM 0:9f917a7bf2da 31 class PelionBase(Bench):
MACRUM 0:9f917a7bf2da 32 """
MACRUM 0:9f917a7bf2da 33 Base class containing common implementation shared by tests
MACRUM 0:9f917a7bf2da 34 """
MACRUM 0:9f917a7bf2da 35
MACRUM 0:9f917a7bf2da 36 def __init__(self, **kwargs):
MACRUM 0:9f917a7bf2da 37 Bench.__init__(self, **kwargs)
MACRUM 0:9f917a7bf2da 38 self.test_config = None
MACRUM 0:9f917a7bf2da 39 self.device_id = None
MACRUM 0:9f917a7bf2da 40 self.manifest_id = None
MACRUM 0:9f917a7bf2da 41 self.account_api = None
MACRUM 0:9f917a7bf2da 42 self.connect_api = None
MACRUM 0:9f917a7bf2da 43 self.device_api = None
MACRUM 0:9f917a7bf2da 44 self.update_api = None
MACRUM 0:9f917a7bf2da 45 self.rest_headers = None
MACRUM 0:9f917a7bf2da 46 self.rest_address = None
MACRUM 0:9f917a7bf2da 47 self.pattern_value1 = "1000:1000:1000:1000"
MACRUM 0:9f917a7bf2da 48 self.pattern_value2 = "2000:1000:2000:1000"
MACRUM 0:9f917a7bf2da 49 self.pattern_value3 = "3000:1000:3000:1000"
MACRUM 0:9f917a7bf2da 50 self.notified_value = ""
MACRUM 0:9f917a7bf2da 51
MACRUM 0:9f917a7bf2da 52 def setup(self):
MACRUM 0:9f917a7bf2da 53 # Check if API key is in environmental vars
MACRUM 0:9f917a7bf2da 54 if 'MBED_CLOUD_SDK_API_KEY' in os.environ:
MACRUM 0:9f917a7bf2da 55 api_key = (os.environ[str('MBED_CLOUD_SDK_API_KEY')])
MACRUM 0:9f917a7bf2da 56 else:
MACRUM 0:9f917a7bf2da 57 api_key = self.config.get("api_key")
MACRUM 0:9f917a7bf2da 58
MACRUM 0:9f917a7bf2da 59 if not api_key.startswith('ak_'):
MACRUM 0:9f917a7bf2da 60 raise TestStepFail("No API key in MBED_CLOUD_SDK_API_KEY or in pelion.tc_cfg")
MACRUM 0:9f917a7bf2da 61
MACRUM 0:9f917a7bf2da 62 self.device_id = self.config.get("device_id")
MACRUM 0:9f917a7bf2da 63 host = self.config.get("host")
MACRUM 0:9f917a7bf2da 64 self.test_config = {"api_key": api_key, "host": host}
MACRUM 0:9f917a7bf2da 65 self.account_api = AccountManagementAPI(self.test_config)
MACRUM 0:9f917a7bf2da 66 self.connect_api = ConnectAPI(self.test_config)
MACRUM 0:9f917a7bf2da 67 self.device_api = DeviceDirectoryAPI(self.test_config)
MACRUM 0:9f917a7bf2da 68
MACRUM 0:9f917a7bf2da 69 # Additional parameters for handling REST requests without SDK
MACRUM 0:9f917a7bf2da 70 self.rest_headers = {'Authorization': 'Bearer ' + api_key}
MACRUM 0:9f917a7bf2da 71 self.rest_address = self.config.get("host")
MACRUM 0:9f917a7bf2da 72
MACRUM 0:9f917a7bf2da 73 # Init delay due to internal usage of PULL notification in SDK. Notications might be lost between
MACRUM 0:9f917a7bf2da 74 # tests.
MACRUM 0:9f917a7bf2da 75 # TODO: Remove workaround after limitation in SDK has been fixed.
MACRUM 0:9f917a7bf2da 76 self.delay(5)
MACRUM 0:9f917a7bf2da 77
MACRUM 0:9f917a7bf2da 78 def setup_update(self):
MACRUM 0:9f917a7bf2da 79 self.manifest_id = self.config.get("manifest_id")
MACRUM 0:9f917a7bf2da 80 self.update_api = UpdateAPI(self.test_config)
MACRUM 0:9f917a7bf2da 81
MACRUM 0:9f917a7bf2da 82 def _callback_fn(self, device_id, path, value):
MACRUM 0:9f917a7bf2da 83 string = value.decode("utf-8")
MACRUM 0:9f917a7bf2da 84 self.logger.info("Notification for %s received: %r value: %r", device_id, path, string)
MACRUM 0:9f917a7bf2da 85 self.notified_value = string
MACRUM 0:9f917a7bf2da 86
MACRUM 0:9f917a7bf2da 87 def verify_registration(self, expected_state):
MACRUM 0:9f917a7bf2da 88 self.logger.info("Verify device to in state %s", expected_state)
MACRUM 0:9f917a7bf2da 89 device = self.device_api.get_device(self.device_id)
MACRUM 0:9f917a7bf2da 90 if device is None:
MACRUM 0:9f917a7bf2da 91 raise TestStepFail("device_id %s does not exist/is not listed in Device Directory" % self.device_id)
MACRUM 0:9f917a7bf2da 92 else:
MACRUM 0:9f917a7bf2da 93 if device.state == expected_state:
MACRUM 0:9f917a7bf2da 94 self.logger.info("Endpoint %s is in state: %s", self.device_id, expected_state)
MACRUM 0:9f917a7bf2da 95 else:
MACRUM 0:9f917a7bf2da 96 raise TestStepFail("Endpoint %s is in state %s, expected %s" % (self.device_id, device.state, expected_state))
MACRUM 0:9f917a7bf2da 97
MACRUM 0:9f917a7bf2da 98 def prepare_campaign_filter(self, state):
MACRUM 0:9f917a7bf2da 99 # Create filter for the update campaign. Here we only update one endpoint.
MACRUM 0:9f917a7bf2da 100 payload = {}
MACRUM 0:9f917a7bf2da 101 payload["device_filter"] = "id=" + self.device_id
MACRUM 0:9f917a7bf2da 102 payload["root_manifest_id"] = self.manifest_id
MACRUM 0:9f917a7bf2da 103 payload["name"] = str(uuid.uuid4())
MACRUM 0:9f917a7bf2da 104 payload["state"] = state
MACRUM 0:9f917a7bf2da 105 return payload
MACRUM 0:9f917a7bf2da 106
MACRUM 0:9f917a7bf2da 107 ## Function for starting a update campaign
MACRUM 0:9f917a7bf2da 108 ## TODO: Replace with Python SDK implementation
MACRUM 0:9f917a7bf2da 109 def post_campaign(self, payload):
MACRUM 0:9f917a7bf2da 110 req_address = self.rest_address + '/v3/update-campaigns'
MACRUM 0:9f917a7bf2da 111 res = requests.post(req_address, json=payload, headers=self.rest_headers)
MACRUM 0:9f917a7bf2da 112 if res.status_code == 409:
MACRUM 0:9f917a7bf2da 113 # Endpoint can be targeted by only one active campaign. The older campaign must be deleted/stopped.
MACRUM 0:9f917a7bf2da 114 raise TestStepFail("Campaign already exists for device %s" % self.device_id)
MACRUM 0:9f917a7bf2da 115 elif res.status_code != 201:
MACRUM 0:9f917a7bf2da 116 raise TestStepFail("Campaign creation failed with %d" % res.status_code)
MACRUM 0:9f917a7bf2da 117
MACRUM 0:9f917a7bf2da 118 data = res.json()
MACRUM 0:9f917a7bf2da 119 campaign_id = data["id"]
MACRUM 0:9f917a7bf2da 120 return campaign_id
MACRUM 0:9f917a7bf2da 121
MACRUM 0:9f917a7bf2da 122 ## Function for checking firmware update status from Cloud
MACRUM 0:9f917a7bf2da 123 ## TODO: Replace with Python SDK implementation
MACRUM 0:9f917a7bf2da 124 def check_campaign_state(self, campaign_id):
MACRUM 0:9f917a7bf2da 125 results = []
MACRUM 0:9f917a7bf2da 126 base_url = self.rest_address + "/v3/update-campaigns"
MACRUM 0:9f917a7bf2da 127 campaign_url = base_url + "/" + campaign_id
MACRUM 0:9f917a7bf2da 128 metadata_url = campaign_url + "/" + "campaign-device-metadata"
MACRUM 0:9f917a7bf2da 129
MACRUM 0:9f917a7bf2da 130 # Short wait until campaign has been initialized properly.
MACRUM 0:9f917a7bf2da 131 self.delay(5)
MACRUM 0:9f917a7bf2da 132
MACRUM 0:9f917a7bf2da 133 # Fetch basic campaign information
MACRUM 0:9f917a7bf2da 134 response = requests.get(campaign_url, headers=self.rest_headers)
MACRUM 0:9f917a7bf2da 135 if response.status_code != 200:
MACRUM 0:9f917a7bf2da 136 raise TestStepFail("Get Campaign state returned %d" % response.status_code)
MACRUM 0:9f917a7bf2da 137
MACRUM 0:9f917a7bf2da 138 resp = json.loads(response.content)
MACRUM 0:9f917a7bf2da 139 results.append(resp["state"])
MACRUM 0:9f917a7bf2da 140
MACRUM 0:9f917a7bf2da 141 # Fetch campaign metadata information
MACRUM 0:9f917a7bf2da 142 response = requests.get(metadata_url, headers=self.rest_headers)
MACRUM 0:9f917a7bf2da 143 if response.status_code != 200:
MACRUM 0:9f917a7bf2da 144 raise TestStepFail("Get Campaign metadata returned %d" % response.status_code)
MACRUM 0:9f917a7bf2da 145
MACRUM 0:9f917a7bf2da 146 resp = json.loads(response.content)
MACRUM 0:9f917a7bf2da 147 if 'data' in resp and resp['data'][0]['deployment_state']:
MACRUM 0:9f917a7bf2da 148 meta_data = resp['data']
MACRUM 0:9f917a7bf2da 149 device_state = meta_data[0]['deployment_state']
MACRUM 0:9f917a7bf2da 150 results.append(device_state)
MACRUM 0:9f917a7bf2da 151 else:
MACRUM 0:9f917a7bf2da 152 raise TestStepFail("No metadata for %d" % self.device_id)
MACRUM 0:9f917a7bf2da 153
MACRUM 0:9f917a7bf2da 154 return results
MACRUM 0:9f917a7bf2da 155
MACRUM 0:9f917a7bf2da 156 restTimeout = 60