Simple Mbed Cloud Client application using features of K64 & K66

Connect to Mbed Cloud!

This example was customized a bit for FRDM-K66 and FRDM-K64F.

It depends on having an SD Card plugged in for storage of credentials. It could be changed later to use a SPI flash or other storage on a shield or wired in.

The app keeps track of how many times switch 2 (SW2) is pressed. The value can be retrieved via a GET request to Mbed Cloud.

Also, it will blink a pattern based on milisecond (ms) timing values that can be sent from Mbed Cloud. The pattern can be sent with a PUT request and the blinking sequence can be triggered by a POST request.

Revision:
0:e13a8a944e25
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/combine_bootloader_with_app.py	Tue Feb 13 10:07:23 2018 +0000
@@ -0,0 +1,253 @@
+#!/usr/bin/env python
+
+## ----------------------------------------------------------------------------
+## 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.
+## ----------------------------------------------------------------------------
+
+from os import path
+import json
+import hashlib, zlib, struct
+import time
+import sys
+
+'''
+define FIRMWARE_HEADER_MAGIC   0x5a51b3d4UL
+define FIRMWARE_HEADER_VERSION 2
+define ARM_UC_SHA512_SIZE     (512/8)
+define ARM_UC_GUID_SIZE       (128/8)
+typedef struct _arm_uc_internal_header_t
+{
+    /* Metadata-header specific magic code */
+    uint32_t headerMagic;
+
+    /* Revision number for metadata header. */
+    uint32_t headerVersion;
+
+    /* Version number accompanying the firmware. Larger numbers imply more
+       recent and preferred versions. This is used for determining the
+       selection order when multiple versions are available. For downloaded
+       firmware the manifest timestamp is used as the firmware version.
+    */
+    uint64_t firmwareVersion;
+
+    /* Total space (in bytes) occupied by the firmware BLOB. */
+    uint64_t firmwareSize;
+
+    /* Firmware hash calculated over the firmware size. Should match the hash
+       generated by standard command line tools, e.g., shasum on Linux/Mac.
+    */
+    uint8_t firmwareHash[ARM_UC_SHA512_SIZE];
+
+    /* The ID for the update campaign that resulted in the firmware update.
+    */
+    uint8_t campaign[ARM_UC_GUID_SIZE];
+
+    /* Size of the firmware signature. Must be 0 if no signature is supplied. */
+    uint32_t firmwareSignatureSize;
+
+    /* Header 32 bit CRC. Calculated over the entire header, including the CRC
+       field, but with the CRC set to zero.
+    */
+    uint32_t headerCRC;
+
+    /* Optional firmware signature. Hashing algorithm should be the same as the
+       one used for the firmware hash. The firmwareSignatureSize must be set.
+    */
+    uint8_t firmwareSignature[0];
+} arm_uc_internal_header_t;
+'''
+
+# define defaults to go into the metadata header
+SIZEOF_SHA512     = int(512/8)
+SIZEOF_GUID       = int(128/8)
+FIRMWARE_HEADER_MAGIC = 0x5a51b3d4
+FIRMWARE_HEADER_VERSION = 2
+header_format = ">2I2Q{}s{}s2I".format(SIZEOF_SHA512, SIZEOF_GUID)
+
+if sys.version_info < (3,):
+    def b(x):
+        return bytearray(x)
+else:
+    def b(x):
+        return x
+
+def create_header(app_blob, firmwareVersion):
+    # calculate the hash of the application
+    firmwareHash = hashlib.sha256(app_blob).digest()
+
+    # calculate the total size which is defined as the application size + metadata header
+    firmwareSize = len(app_blob)
+
+    # set campaign GUID to 0
+    campaign = b'\00'
+
+    # signature not supported, set size to 0
+    signatureSize = 0
+
+    print ('imageSize:    {}'.format(firmwareSize))
+    print ('imageHash:    {}'.format(''.join(['{:0>2x}'.format(c) for c in b(firmwareHash)])))
+    print ('imageversion: {}'.format(firmwareVersion))
+
+    # construct struct for CRC calculation
+    headerCRC = 0
+    FirmwareHeader = struct.pack(header_format,
+                                 FIRMWARE_HEADER_MAGIC,
+                                 FIRMWARE_HEADER_VERSION,
+                                 firmwareVersion,
+                                 firmwareSize,
+                                 firmwareHash,
+                                 campaign,
+                                 signatureSize,
+                                 headerCRC)
+
+    # calculate checksum over header, including signatureSize but without headerCRC
+    headerCRC = zlib.crc32(FirmwareHeader[:-4]) & 0xffffffff
+
+    # Pack the data into a binary blob
+    FirmwareHeader = struct.pack(header_format,
+                                 FIRMWARE_HEADER_MAGIC,
+                                 FIRMWARE_HEADER_VERSION,
+                                 firmwareVersion,
+                                 firmwareSize,
+                                 firmwareHash,
+                                 campaign,
+                                 signatureSize,
+                                 headerCRC)
+
+    return FirmwareHeader
+
+
+def combine(bootloader_blob, app_blob, app_offset, hdr_offset, output, version):
+
+    # create header to go with the application binary
+    FirmwareHeader = create_header(app_blob, version)
+
+    # write the bootloader first
+    offset = 0
+    output.write(bootloader_blob)
+    offset += len(bootloader_blob)
+
+    # write the padding between bootloader and firmware header
+    output.write(b'\00' * (hdr_offset - offset))
+    offset += (hdr_offset - offset)
+
+    # write firmware header
+    output.write(FirmwareHeader)
+    offset += len(FirmwareHeader)
+
+    # write padding between header and application
+    output.write(b'\00' * (app_offset - offset))
+
+    # write the application
+    output.write(app_blob)
+
+
+if __name__ == '__main__':
+    import argparse
+
+    parser = argparse.ArgumentParser(
+        description='Combine bootloader with application adding metadata header.')
+
+    def offset_arg(s):
+        if s.startswith('0x'):
+            return int(s, 16)
+        else:
+            return int(s)
+
+    bin_map = {
+        'k64f': {
+            'mem_start': '0x0'
+        },
+        'ublox_evk_odin_w2': {
+            'mem_start': '0x08000000'
+        },
+        'nucleo_f429zi': {
+            'mem_start': '0x08000000'
+        }
+    }
+
+    curdir = path.dirname(path.abspath(__file__))
+
+    def parse_mbed_app_offset(mcu, key):
+        mem_start = bin_map[mcu]["mem_start"]
+        with open(path.join(curdir, "..", "mbed_app.json")) as fd:
+            mbed_json = json.load(fd)
+            offset = mbed_json["target_overrides"][mcu.upper()][key]
+            return offset_arg(offset) - offset_arg(mem_start)
+
+    # specify arguments
+    parser.add_argument('-m', '--mcu', type=lambda s : s.lower().replace("-","_"), required=False,
+                        help='mcu', choices=bin_map.keys())
+    parser.add_argument('-b', '--bootloader',    type=argparse.FileType('rb'), required=False,
+                        help='path to the bootloader binary')
+    parser.add_argument('-a', '--app',           type=argparse.FileType('rb'), required=True,
+                        help='path to application binary')
+    parser.add_argument('-c', '--app-offset',    type=offset_arg,              required=False,
+                        help='offset of the application')
+    parser.add_argument('-d', '--header-offset', type=offset_arg,              required=False,
+                        help='offset of the firmware metadata header')
+    parser.add_argument('-o', '--output',        type=argparse.FileType('wb'), required=True,
+                        help='output combined file path')
+    parser.add_argument('-s', '--set-version',   type=int,                     required=False,
+                        help='set version number', default=int(time.time()))
+
+    # workaround for http://bugs.python.org/issue9694
+    parser._optionals.title = "arguments"
+
+    # get and validate arguments
+    args = parser.parse_args()
+
+    if(not (args.mcu or args.bootloader)):
+        print("Please specify bootloader location or MCU")
+        exit(-1)
+
+    fname = ''
+    if args.mcu:
+        fname = path.join(curdir, 'mbed-bootloader-' + args.mcu.replace('_', '-')+'.bin')
+        if not path.exists(fname):
+            print("Specified MCU does not have a binary in this location")
+            exit(-1)
+
+    # Use specified bootloader or default if none is provided
+    bootloader = args.bootloader or open(fname, 'rb')
+
+    # read the contents of bootloader and application binaries into buffers
+    bootloader_blob = bootloader.read()
+    bootloader.close()
+    app_blob = args.app.read()
+    args.app.close()
+
+    # Use specified offsets or default if none are provided
+    if(not (args.mcu or args.app_offset)):
+        print("Please specify app offset or MCU")
+        exit(-1)
+    app_offset = args.app_offset or parse_mbed_app_offset(args.mcu, "target.mbed_app_start")
+
+    if(not (args.mcu or args.header_offset)):
+        print("Please specify header offset or MCU")
+        exit(-1)
+    header_offset = args.header_offset or parse_mbed_app_offset(args.mcu, "update-client.application-details")
+
+    # combine application and bootloader adding metadata info
+    combine(bootloader_blob, app_blob, app_offset, header_offset, args.output, args.set_version)
+
+    # close output file
+    output_fn = path.abspath(args.output.name)
+    args.output.close()
+
+    # print the output file path
+    print('Combined binary:' + output_fn)