Mbed OS and Pelion Device Management example for FRDM-K64F and FRDM-K66F boards

Dependencies:   FXAS21002 FXOS8700Q

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

  • FRDM-K64F - onboard Ethernet and onboard SD card holder.
  • FRDM-K66F - onboard Ethernet and onboard SD card holder.

Follow the Quick-Start instructions: https://cloud.mbed.com/quick-start

FRDM-K64F FRDM-K66F

Example functionality

This example showcases the following device functionality:

  • Read onboard FXOS8700Q accelerometer and magnetometer, and report the values as Pelion LWM2M resources (see image below).
  • (FRDM-K66F only) Read onboard FXAS21002 gyroscope and report the values as Pelion LWM2M resources.
  • On user button click, increment Pelion LWM2M button resource.
  • Allow the user to change the state of the board LED from Pelion LWM2M led_state resource and PUT request.

/media/uploads/screamer/pelion_st_humidity_reading.png?v=2

Use this example with Mbed CLI

1. Import the application into your desktop:

mbed import https://os.mbed.com/teams/NXP/code/pelion-example-frdm

cd pelion-example-frdm

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.9.x or above.

4. Compile and program:

mbed compile -t <toolchain> -m <TARGET_BOARD>

(supported toolchains : GCC_ARM / ARM / IAR)

5. You can connect on a virtual terminal/COM port to the platform using:

mbed sterm -b 115200

This should give you an output similar to:

[BOOT] Mbed Bootloader
[BOOT] ARM: 00000000000000000000
[BOOT] OEM: 00000000000000000000
[BOOT] Layout: 0 8374
[BOOT] Active firmware integrity check:
[BOOT] SHA256: 411F422DE8FF545E2D3C373E87D5328162A129314A655AEC32B9A167DE29177E
[BOOT] Version: 1553606618
[BOOT] Slot 0 is empty
[BOOT] Active firmware up-to-date
[BOOT] Application's start address: 0x10400
[BOOT] Application's jump address: 0x112C1
[BOOT] Application's stack address: 0x20030000
[BOOT] Forwarding to application...


Starting Simple Pelion Device Management Client example
You can hold the user button during boot to format the storage and change the device identity.

Sensors configuration:
FXOS8700Q accelerometer = 0xC7
FXOS8700Q magnetometer  = 0xC7
FXAS21002 gyroscope     = 0xB7

Connecting to the network using the default network interface...
Connected to the network successfully. IP address: 10.2.202.65
Initializing Pelion Device Management Client...
Initialized Pelion Device Management Client. Registering...
Press the user button to increment the LwM2M resource value...

FXOS8700Q mag:    0.312 x,   0.039 y,  -1.625 z [gauss]
FXOS8700Q acc:    0.139 x,  -0.123 y,   4.260 z [g]
FXAS21002 gryo:  13.089 x,  13.089 y,  13.089 z [dps]
Committer:
screamer
Date:
Mon Mar 25 17:18:30 2019 +0000
Revision:
1:42d51cf7cebe
Parent:
0:a9d53048f0b6
Child:
2:fba2c7066355
Add support for FXOS8700Q onboard sensor and report it to Pelion

Who changed what in which revision?

UserRevisionLine numberNew contents of line
screamer 0:a9d53048f0b6 1 // ----------------------------------------------------------------------------
screamer 0:a9d53048f0b6 2 // Copyright 2016-2018 ARM Ltd.
screamer 0:a9d53048f0b6 3 //
screamer 0:a9d53048f0b6 4 // SPDX-License-Identifier: Apache-2.0
screamer 0:a9d53048f0b6 5 //
screamer 0:a9d53048f0b6 6 // Licensed under the Apache License, Version 2.0 (the "License");
screamer 0:a9d53048f0b6 7 // you may not use this file except in compliance with the License.
screamer 0:a9d53048f0b6 8 // You may obtain a copy of the License at
screamer 0:a9d53048f0b6 9 //
screamer 0:a9d53048f0b6 10 // http://www.apache.org/licenses/LICENSE-2.0
screamer 0:a9d53048f0b6 11 //
screamer 0:a9d53048f0b6 12 // Unless required by applicable law or agreed to in writing, software
screamer 0:a9d53048f0b6 13 // distributed under the License is distributed on an "AS IS" BASIS,
screamer 0:a9d53048f0b6 14 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
screamer 0:a9d53048f0b6 15 // See the License for the specific language governing permissions and
screamer 0:a9d53048f0b6 16 // limitations under the License.
screamer 0:a9d53048f0b6 17 // ----------------------------------------------------------------------------
screamer 0:a9d53048f0b6 18 #ifndef MBED_TEST_MODE
screamer 0:a9d53048f0b6 19
screamer 0:a9d53048f0b6 20 #include "mbed.h"
screamer 0:a9d53048f0b6 21 #include "simple-mbed-cloud-client.h"
screamer 0:a9d53048f0b6 22 #include "FATFileSystem.h"
screamer 0:a9d53048f0b6 23 #include "LittleFileSystem.h"
screamer 1:42d51cf7cebe 24 #include "FXOS8700Q.h"
screamer 1:42d51cf7cebe 25
screamer 1:42d51cf7cebe 26 I2C sen_i2c(PTE25, PTE24);
screamer 1:42d51cf7cebe 27 FXOS8700QAccelerometer sen_acc(sen_i2c, FXOS8700CQ_SLAVE_ADDR1); // Configured for the FRDM-K64F with onboard sensors
screamer 1:42d51cf7cebe 28 FXOS8700QMagnetometer sen_mag(sen_i2c, FXOS8700CQ_SLAVE_ADDR1);
screamer 1:42d51cf7cebe 29
screamer 1:42d51cf7cebe 30 #define SENSORS_POLL_INTERVAL 1.0
screamer 1:42d51cf7cebe 31 #define SEND_ALL_SENSORS
screamer 0:a9d53048f0b6 32
screamer 0:a9d53048f0b6 33 // Default network interface object. Don't forget to change the WiFi SSID/password in mbed_app.json if you're using WiFi.
screamer 0:a9d53048f0b6 34 NetworkInterface *net = NetworkInterface::get_default_instance();
screamer 0:a9d53048f0b6 35
screamer 0:a9d53048f0b6 36 // Default block device available on the target board
screamer 0:a9d53048f0b6 37 BlockDevice *bd = BlockDevice::get_default_instance();
screamer 0:a9d53048f0b6 38
screamer 0:a9d53048f0b6 39 #if COMPONENT_SD || COMPONENT_NUSD
screamer 0:a9d53048f0b6 40 // Use FATFileSystem for SD card type blockdevices
screamer 0:a9d53048f0b6 41 FATFileSystem fs("fs", bd);
screamer 0:a9d53048f0b6 42 #else
screamer 0:a9d53048f0b6 43 // Use LittleFileSystem for non-SD block devices to enable wear leveling and other functions
screamer 0:a9d53048f0b6 44 LittleFileSystem fs("fs", bd);
screamer 0:a9d53048f0b6 45 #endif
screamer 0:a9d53048f0b6 46
screamer 0:a9d53048f0b6 47 #if USE_BUTTON == 1
screamer 0:a9d53048f0b6 48 InterruptIn button(BUTTON1);
screamer 0:a9d53048f0b6 49 #endif /* USE_BUTTON */
screamer 0:a9d53048f0b6 50
screamer 0:a9d53048f0b6 51 // Default LED to use for PUT/POST example
screamer 0:a9d53048f0b6 52 DigitalOut led(LED1);
screamer 0:a9d53048f0b6 53
screamer 0:a9d53048f0b6 54 // Declaring pointers for access to Pelion Device Management Client resources outside of main()
screamer 0:a9d53048f0b6 55 MbedCloudClientResource *button_res;
screamer 0:a9d53048f0b6 56 MbedCloudClientResource *led_res;
screamer 0:a9d53048f0b6 57 MbedCloudClientResource *post_res;
screamer 1:42d51cf7cebe 58 #ifdef SEND_ALL_SENSORS
screamer 1:42d51cf7cebe 59 MbedCloudClientResource *res_magnometer_x;
screamer 1:42d51cf7cebe 60 MbedCloudClientResource *res_magnometer_y;
screamer 1:42d51cf7cebe 61 MbedCloudClientResource *res_magnometer_z;
screamer 1:42d51cf7cebe 62 MbedCloudClientResource *res_accelerometer_x;
screamer 1:42d51cf7cebe 63 MbedCloudClientResource *res_accelerometer_y;
screamer 1:42d51cf7cebe 64 MbedCloudClientResource *res_accelerometer_z;
screamer 1:42d51cf7cebe 65 #endif /* SEND_ALL_SENSORS */
screamer 0:a9d53048f0b6 66
screamer 0:a9d53048f0b6 67 // An event queue is a very useful structure to debounce information between contexts (e.g. ISR and normal threads)
screamer 0:a9d53048f0b6 68 // This is great because things such as network operations are illegal in ISR, so updating a resource in a button's fall() function is not allowed
screamer 0:a9d53048f0b6 69 EventQueue eventQueue;
screamer 0:a9d53048f0b6 70
screamer 1:42d51cf7cebe 71 // When the device is registered, this variable will be used to access various useful information, like device ID etc.
screamer 1:42d51cf7cebe 72 static const ConnectorClientEndpointInfo* endpointInfo;
screamer 1:42d51cf7cebe 73
screamer 0:a9d53048f0b6 74 /**
screamer 0:a9d53048f0b6 75 * PUT handler - sets the value of the built-in LED
screamer 0:a9d53048f0b6 76 * @param resource The resource that triggered the callback
screamer 0:a9d53048f0b6 77 * @param newValue Updated value for the resource
screamer 0:a9d53048f0b6 78 */
screamer 0:a9d53048f0b6 79 void put_callback(MbedCloudClientResource *resource, m2m::String newValue) {
screamer 0:a9d53048f0b6 80 printf("PUT received. New value: %s\n", newValue.c_str());
screamer 0:a9d53048f0b6 81 led = atoi(newValue.c_str());
screamer 0:a9d53048f0b6 82 }
screamer 0:a9d53048f0b6 83
screamer 0:a9d53048f0b6 84 /**
screamer 0:a9d53048f0b6 85 * POST handler - prints the content of the payload
screamer 0:a9d53048f0b6 86 * @param resource The resource that triggered the callback
screamer 0:a9d53048f0b6 87 * @param buffer If a body was passed to the POST function, this contains the data.
screamer 0:a9d53048f0b6 88 * Note that the buffer is deallocated after leaving this function, so copy it if you need it longer.
screamer 0:a9d53048f0b6 89 * @param size Size of the body
screamer 0:a9d53048f0b6 90 */
screamer 0:a9d53048f0b6 91 void post_callback(MbedCloudClientResource *resource, const uint8_t *buffer, uint16_t size) {
screamer 0:a9d53048f0b6 92 printf("POST received (length %u). Payload: ", size);
screamer 0:a9d53048f0b6 93 for (size_t ix = 0; ix < size; ix++) {
screamer 0:a9d53048f0b6 94 printf("%02x ", buffer[ix]);
screamer 0:a9d53048f0b6 95 }
screamer 0:a9d53048f0b6 96 printf("\n");
screamer 0:a9d53048f0b6 97 }
screamer 0:a9d53048f0b6 98
screamer 0:a9d53048f0b6 99 /**
screamer 0:a9d53048f0b6 100 * Button handler
screamer 0:a9d53048f0b6 101 * This function will be triggered either by a physical button press or by a ticker every 5 seconds (see below)
screamer 0:a9d53048f0b6 102 */
screamer 0:a9d53048f0b6 103 void button_press() {
screamer 0:a9d53048f0b6 104 int v = button_res->get_value_int() + 1;
screamer 0:a9d53048f0b6 105 button_res->set_value(v);
screamer 0:a9d53048f0b6 106 printf("Button clicked %d times\n", v);
screamer 0:a9d53048f0b6 107 }
screamer 0:a9d53048f0b6 108
screamer 0:a9d53048f0b6 109 /**
screamer 0:a9d53048f0b6 110 * Notification callback handler
screamer 0:a9d53048f0b6 111 * @param resource The resource that triggered the callback
screamer 0:a9d53048f0b6 112 * @param status The delivery status of the notification
screamer 0:a9d53048f0b6 113 */
screamer 0:a9d53048f0b6 114 void button_callback(MbedCloudClientResource *resource, const NoticationDeliveryStatus status) {
screamer 0:a9d53048f0b6 115 printf("Button notification, status %s (%d)\n", MbedCloudClientResource::delivery_status_to_string(status), status);
screamer 0:a9d53048f0b6 116 }
screamer 0:a9d53048f0b6 117
screamer 0:a9d53048f0b6 118 /**
screamer 0:a9d53048f0b6 119 * Registration callback handler
screamer 0:a9d53048f0b6 120 * @param endpoint Information about the registered endpoint such as the name (so you can find it back in portal)
screamer 0:a9d53048f0b6 121 */
screamer 0:a9d53048f0b6 122 void registered(const ConnectorClientEndpointInfo *endpoint) {
screamer 0:a9d53048f0b6 123 printf("Registered to Pelion Device Management. Endpoint Name: %s\n", endpoint->internal_endpoint_name.c_str());
screamer 0:a9d53048f0b6 124 }
screamer 0:a9d53048f0b6 125
screamer 1:42d51cf7cebe 126 /**
screamer 1:42d51cf7cebe 127 * Initialize sensors
screamer 1:42d51cf7cebe 128 */
screamer 1:42d51cf7cebe 129 void sensors_init() {
screamer 1:42d51cf7cebe 130 printf ("\nSensors configuration:\n");
screamer 1:42d51cf7cebe 131
screamer 1:42d51cf7cebe 132 sen_acc.enable();
screamer 1:42d51cf7cebe 133 sen_mag.enable();
screamer 1:42d51cf7cebe 134
screamer 1:42d51cf7cebe 135 printf("FXOS8700Q accelerometer = 0x%X\n", sen_acc.whoAmI());
screamer 1:42d51cf7cebe 136 printf("FXOS8700Q magnetometer = 0x%X\n", sen_mag.whoAmI());
screamer 1:42d51cf7cebe 137
screamer 1:42d51cf7cebe 138 printf("\n"); ;
screamer 1:42d51cf7cebe 139 }
screamer 1:42d51cf7cebe 140
screamer 1:42d51cf7cebe 141 /**
screamer 1:42d51cf7cebe 142 * Update sensors and report their values.
screamer 1:42d51cf7cebe 143 * This function is called periodically.
screamer 1:42d51cf7cebe 144 */
screamer 1:42d51cf7cebe 145 void sensors_update() {
screamer 1:42d51cf7cebe 146 motion_data_counts_t acc_raw, mag_raw;
screamer 1:42d51cf7cebe 147
screamer 1:42d51cf7cebe 148 printf(" \n");
screamer 1:42d51cf7cebe 149
screamer 1:42d51cf7cebe 150 sen_acc.getAxis(acc_raw);
screamer 1:42d51cf7cebe 151 sen_mag.getAxis(mag_raw);
screamer 1:42d51cf7cebe 152
screamer 1:42d51cf7cebe 153 printf("FXOS8700Q mag: %7ld x, %7ld y, %7ld z [mgauss] \n", mag_raw.x, mag_raw.y, mag_raw.z);
screamer 1:42d51cf7cebe 154 printf("FXOS8700Q acc: %7ld x, %7ld y, %7ld z [mg] \n", acc_raw.x, acc_raw.y, acc_raw.z);
screamer 1:42d51cf7cebe 155
screamer 1:42d51cf7cebe 156 printf("\r\033[3A");
screamer 1:42d51cf7cebe 157
screamer 1:42d51cf7cebe 158 if (endpointInfo) {
screamer 1:42d51cf7cebe 159 #ifdef SEND_ALL_SENSORS
screamer 1:42d51cf7cebe 160 res_magnometer_x->set_value((int)mag_raw.x);
screamer 1:42d51cf7cebe 161 res_magnometer_y->set_value((int)mag_raw.y);
screamer 1:42d51cf7cebe 162 res_magnometer_z->set_value((int)mag_raw.z);
screamer 1:42d51cf7cebe 163 res_accelerometer_x->set_value((int)acc_raw.x);
screamer 1:42d51cf7cebe 164 res_accelerometer_y->set_value((int)acc_raw.y);
screamer 1:42d51cf7cebe 165 res_accelerometer_z->set_value((int)acc_raw.z);
screamer 1:42d51cf7cebe 166 #endif /* SEND_ALL_SENSORS */
screamer 1:42d51cf7cebe 167 }
screamer 1:42d51cf7cebe 168 }
screamer 1:42d51cf7cebe 169
screamer 0:a9d53048f0b6 170 int main(void) {
screamer 0:a9d53048f0b6 171 printf("\nStarting Simple Pelion Device Management Client example\n");
screamer 0:a9d53048f0b6 172
screamer 0:a9d53048f0b6 173 #if USE_BUTTON == 1
screamer 0:a9d53048f0b6 174 // If the User button is pressed ons start, then format storage.
screamer 0:a9d53048f0b6 175 if (button.read() == MBED_CONF_APP_BUTTON_PRESSED_STATE) {
screamer 0:a9d53048f0b6 176 printf("User button is pushed on start. Formatting the storage...\n");
screamer 0:a9d53048f0b6 177 int storage_status = StorageHelper::format(&fs, bd);
screamer 0:a9d53048f0b6 178 if (storage_status != 0) {
screamer 0:a9d53048f0b6 179 printf("ERROR: Failed to reformat the storage (%d).\n", storage_status);
screamer 0:a9d53048f0b6 180 }
screamer 0:a9d53048f0b6 181 } else {
screamer 0:a9d53048f0b6 182 printf("You can hold the user button during boot to format the storage and change the device identity.\n");
screamer 0:a9d53048f0b6 183 }
screamer 0:a9d53048f0b6 184 #endif /* USE_BUTTON */
screamer 0:a9d53048f0b6 185
screamer 1:42d51cf7cebe 186 sensors_init();
screamer 1:42d51cf7cebe 187
screamer 0:a9d53048f0b6 188 // Connect to the Internet (DHCP is expected to be on)
screamer 0:a9d53048f0b6 189 printf("Connecting to the network using the default network interface...\n");
screamer 0:a9d53048f0b6 190 net = NetworkInterface::get_default_instance();
screamer 0:a9d53048f0b6 191
screamer 0:a9d53048f0b6 192 nsapi_error_t net_status = NSAPI_ERROR_NO_CONNECTION;
screamer 0:a9d53048f0b6 193 while ((net_status = net->connect()) != NSAPI_ERROR_OK) {
screamer 0:a9d53048f0b6 194 printf("Unable to connect to network (%d). Retrying...\n", net_status);
screamer 0:a9d53048f0b6 195 }
screamer 0:a9d53048f0b6 196
screamer 0:a9d53048f0b6 197 printf("Connected to the network successfully. IP address: %s\n", net->get_ip_address());
screamer 0:a9d53048f0b6 198
screamer 0:a9d53048f0b6 199 printf("Initializing Pelion Device Management Client...\n");
screamer 0:a9d53048f0b6 200
screamer 0:a9d53048f0b6 201 // SimpleMbedCloudClient handles registering over LwM2M to Pelion Device Management
screamer 0:a9d53048f0b6 202 SimpleMbedCloudClient client(net, bd, &fs);
screamer 0:a9d53048f0b6 203 int client_status = client.init();
screamer 0:a9d53048f0b6 204 if (client_status != 0) {
screamer 0:a9d53048f0b6 205 printf("Pelion Client initialization failed (%d)\n", client_status);
screamer 0:a9d53048f0b6 206 return -1;
screamer 0:a9d53048f0b6 207 }
screamer 0:a9d53048f0b6 208
screamer 0:a9d53048f0b6 209 // Creating resources, which can be written or read from the cloud
screamer 0:a9d53048f0b6 210 button_res = client.create_resource("3200/0/5501", "button_count");
screamer 0:a9d53048f0b6 211 button_res->set_value(0);
screamer 0:a9d53048f0b6 212 button_res->methods(M2MMethod::GET);
screamer 0:a9d53048f0b6 213 button_res->observable(true);
screamer 0:a9d53048f0b6 214 button_res->attach_notification_callback(button_callback);
screamer 0:a9d53048f0b6 215
screamer 0:a9d53048f0b6 216 led_res = client.create_resource("3201/0/5853", "led_state");
screamer 0:a9d53048f0b6 217 led_res->set_value(led.read());
screamer 0:a9d53048f0b6 218 led_res->methods(M2MMethod::GET | M2MMethod::PUT);
screamer 0:a9d53048f0b6 219 led_res->attach_put_callback(put_callback);
screamer 0:a9d53048f0b6 220
screamer 0:a9d53048f0b6 221 post_res = client.create_resource("3300/0/5605", "execute_function");
screamer 0:a9d53048f0b6 222 post_res->methods(M2MMethod::POST);
screamer 0:a9d53048f0b6 223 post_res->attach_post_callback(post_callback);
screamer 0:a9d53048f0b6 224
screamer 0:a9d53048f0b6 225 printf("Initialized Pelion Device Management Client. Registering...\n");
screamer 0:a9d53048f0b6 226
screamer 0:a9d53048f0b6 227 // Callback that fires when registering is complete
screamer 0:a9d53048f0b6 228 client.on_registered(&registered);
screamer 0:a9d53048f0b6 229
screamer 0:a9d53048f0b6 230 // Register with Pelion DM
screamer 0:a9d53048f0b6 231 client.register_and_connect();
screamer 0:a9d53048f0b6 232
screamer 0:a9d53048f0b6 233 // The button fires on an interrupt context, but debounces it to the eventqueue, so it's safe to do network operations
screamer 0:a9d53048f0b6 234 button.fall(eventQueue.event(&button_press));
screamer 0:a9d53048f0b6 235 printf("Press the user button to increment the LwM2M resource value...\n");
screamer 1:42d51cf7cebe 236
screamer 0:a9d53048f0b6 237 // The timer fires on an interrupt context, but debounces it to the eventqueue, so it's safe to do network operations
screamer 0:a9d53048f0b6 238 Ticker timer;
screamer 1:42d51cf7cebe 239 timer.attach(eventQueue.event(&sensors_update), SENSORS_POLL_INTERVAL);
screamer 0:a9d53048f0b6 240
screamer 0:a9d53048f0b6 241 // You can easily run the eventQueue in a separate thread if required
screamer 0:a9d53048f0b6 242 eventQueue.dispatch_forever();
screamer 0:a9d53048f0b6 243 }
screamer 0:a9d53048f0b6 244
screamer 0:a9d53048f0b6 245 #endif /* MBED_TEST_MODE */