Perform Over the Air Updates using Bluetooth Low Energy - FreeRTOS

Perform Over the Air Updates using Bluetooth Low Energy


You can use the FreeRTOS Bluetooth Low Energy (BLE) and Over the Air Updates (OTA) libraries to perform a firmware update to a microcontroller device over Bluetooth Low Energy via an MQTT proxy running on a companion device. The update is performed using AWS IoT OTA jobs. The companion device connects to AWS IoT using Amazon Cognito credentials entered in a demo app. An authorized operator initiates the OTA update from the cloud. When the device connects through the demo app, the OTA update is initiated and the firmware is updated on the device.


  1. Port the Over The Air Updates agent to each microcontroller device:

  2. Port the Bluetooth Low Energy library to each microcontroller:

  3. Set up an AWS account if you don't already have one (the Free Tier is sufficient).

  4. You must have access to an Android phone as the companion device with Android v 6.0 or later and Bluetooth version 4.2 or later.

  5. Set up your development platform with:

    • Android Studio.

    • The AWS CLI installed.

    • Python3 installed.

    • The boto3 AWS Software Developer Kit (SDK) for Python.


The following is an overview of the steps required for setup. You can skip steps 1 and 2 if you have already done these to test Over The Air Updates over WiFi or Ethernet.

  1. Configure storage— Create an S3 bucket and policies and configure an IAM user that can perform updates.

  2. Create a code-signing certificate— Create a signing certificate and allow the IAM user to sign firmware updates.

  3. Configure Amazon Cognito authentication— Create a credential provider, user pool, and application access to the user pool.

  4. Configure Amazon FreeRTOS— Set up Bluetooth Low Energy, client credentials, and the code-signing public certificate.

  5. Configure an Android app— Set up the credential provider and user pool, then deploy the application to an Android device.

Step 1: Configure storage

You can skip these steps by launching the AWS CloudFormation template.

  1. Create an S3 bucket with versioning enabled to hold the firmware images.

  2. Create an OTA update service role and add the following managed policies to the role:

    • AWSIotLogging

    • AWSIotRuleActions

    • AWSIotThingsRegistration

    • AWSFreeRTOSOTAUpdate

  3. Add an inline policy to the role to allow it to perform AWS IoT actions and allow access to the S3 bucket that you created.

  4. Create an IAM user that can perform OTA updates. This user can sign and deploy firmware updates to AWS IoT devices in the account, and has access to do OTA updates on all devices. Access should be limited to trusted entities.

  5. Attach permissions with an OTA user policy.

  6. Create an inline policy that allows the IAM user you created to sign the firmware.

Step 2: Create the code-signing certificate

Create a code-signing certificate that can be used to sign the firmware. Note the certificate ARN when the certificate is imported.

aws acm import-certificate --profile=ota-update-user --certificate file://ecdsasigner.crt --private-key file://ecdsasigner.key { "CertificateArn": "arn:aws:acm:region:account:certificate/certid" }

The ARN will be used later to create a signing profile. If desired, the profile can be created using the following command at this point:

aws signer put-signing-profile --profile=ota-update-user --profile-name myOTAProfile --signing-material certificateArn=arn:aws:acm:region:account:certificate/certid --platform AmazonFreeRTOS-Default --signing-parameters certname=/cert.pem { "arn": "arn:aws:signer::account:/signing-profiles/myOTAProfile" }

Step 3: Configure the Amazon Cognito Authentication

AWS IoT configuration

First, set up the AWS IoT thing and policy. Because we are using the MQTT proxy on the Android phone which will be authenticated using Amazon Cognito, the device does not need the AWS IoT certificates.

Before you create the AWS IoT Policy, you need to know your AWS region and AWS account number.

Step 3a: Create an AWS IoT Policy

  1. Sign in to the AWS IoT console.

  2. In the upper-right corner, choose your account and under My Account make a note of your 12-digit account ID.

  3. In the left navigation pane, choose Settings. Under Device data endpoint, make a note of the endpoint value. The endpoint should be something like "". In this example, the AWS region is "us-west-2".

  4. In the left navigation pane, choose Secure, choose Policies, and then choose Create.

  5. If you do not have a policy created in your account, you will see the message "You don’t have any policies yet" and you should choose Create a policy.

  6. Enter a name for your policy (for example, "mqtt_proxy_iot_policy").

  7. In the Add statements section, choose Advanced mode. Copy and paste the following JSON into the policy editor window. Replace "aws-account-id" with your account ID (step 2). Replace "aws-region" with your region, for example "us-west-2" (step 3).

    { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": "iot:Connect", "Resource": "arn:aws:iot:region:account-id:*" }, { "Effect": "Allow", "Action": "iot:Publish", "Resource": "arn:aws:iot:region:account-id:*" }, { "Effect": "Allow", "Action": "iot:Subscribe", "Resource": "arn:aws:iot:region:account-id:*" }, { "Effect": "Allow", "Action": "iot:Receive", "Resource": "arn:aws:iot:region:account-id:*" } ] }
  8. Choose Create.

Step 3b: Create an AWS IoT thing

  1. Sign in to the AWS IoT console.

  2. In the left navigation pane, choose Manage, and then choose Things. In the top-right corner, choose Create.

  3. If you do not have any IoT things registered in your account, the message "You don’t have any things yet" is displayed and you should choose Register a thing.

  4. On the Creating AWS IoT things page, choose Create a single thing.

  5. On the Add your device to the thing registry page, enter a name for your thing (for example, "my_thing"). Only alphanumeric characters, hyphen ("-") and underscore ("_") are allowed. Choose Next.

  6. On the Add a certificate for your thing page, under Skip certificate and create thing, choose Create thing without certificate.

    Because we are using the BLE proxy mobile app that uses an Amazon Cognito credential for authentication and authorization, no device certificate is required.

Amazon Cognito configuration

Amazon Cognito is required for authentication of the MQTT proxy mobile app. An IAM policy is attached to the authenticated identity to allow the principal to attach the AWS IoT policy to the credential.

Step 3c: Set up an Amazon Cognito user pool

  1. Sign in to the Amazon Cognito console.

  2. In the right top navigation area, choose Create a user pool.

  3. Enter the pool name (for example, "mqtt_proxy_user_pool").

  4. Choose Review defaults.

  5. Next to App Clients, click Add app client..., and then choose Add an app client.

  6. Enter the app client name (for example "mqtt_app_client").

  7. Make sure Generate client secret is selected.

  8. Choose Create app client.

  9. Choose Return to pool details.

  10. On the Review page of the user pool, choose Create pool.

  11. You should see a message that says "Your user pool was created successfully."

  12. Make a note of the "Pool ID".

  13. In the left navigation pane, choose App clients.

  14. Click Show Details.

  15. Make a note of the "App client ID" and the "App client secret".

Step 3d: Amazon Cognito Identity Pool

  1. Sign in to the Amazon Cognito console.

  2. Choose Create new identity pool.

  3. Enter a name for the identity pool (for example, "mqtt_proxy_identity_pool").

  4. Expand Authentication providers.

  5. Choose the Cognito tab.

  6. Enter the User pool ID and App client ID that you saved from "Step 3c: Set up an Amazon Cognito user pool".

  7. Choose Create Pool.

  8. On the next page, choose Allow to create new roles for authenticated and unauthenticated identities.

  9. Make a note of the Identity pool ID, which has the format "us-east-1:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx".

Next, we attach an IAM policy to the authenticated identity so that the credential can attach the IoT policy to it.

Step 3e: Attach IAM policy to the authenticated identity

  1. Open the Amazon Cognito console.

  2. Choose the identity pool that you just created (for example, "mqtt_proxy_identity_pool").

  3. Choose Edit identity pool.

  4. Make a note of the IAM Role assigned to the Authenticated role (for example, "Cognito_mqtt_proxy_identity_poolAuth_Role").

  5. Open the IAM console.

  6. In the navigation pane, choose Roles.

  7. Search for the role (for example, "Cognito_mqtt_proxy_identity_poolAuth_Role"), and then choose it.

  8. Choose Add inline policy, and then choose JSON.

  9. Enter the following policy:

    { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "iot:AttachPolicy", "iot:AttachPrincipalPolicy", "iot:Connect", "iot:Publish", "iot:Subscribe", "iot:Receive" ], "Resource": "*" } ] }
  10. Choose Review Policy.

  11. Enter a policy name (for example, "mqttProxyCognitoPolicy").

  12. Choose Create policy.

Step 4: Configure FreeRTOS

Enable the OTA update demo as follows:


Only one demo can be enabled at a time in the Demo Runner.

  1. Open vendors/vendor/boards/board/aws_demos/config_files/aws_demo_config.h.



  2. Open demos/include/aws_clientcredential.h:

    • Change the endpoint URL in clientcredentialMQTT_BROKER_ENDPOINT[].

    • Change the thing name to the name of your thing (for example, "my_thing") in clientcredentialIOT_THING_NAME.


      Certificates don't have to be added when you use Amazon Cognito credentials.

  3. Open vendors/vendor/boards/board/aws_demos/common/config_files/aws_iot_network_config.h.


  4. Open ota_demo_config.h:

    • Change otapalconfigCODE_SIGNING_CERTIFICATE to refer to the certificate to be used to sign the firmware binary file.

    The application should start up and print the demo version:

    11 13498 [iot_thread] [INFO ][DEMO][134980] Successfully initialized the demo. Network type for the demo: 2 12 13498 [iot_thread] [INFO ][MQTT][134980] MQTT library successfully initialized. 13 13498 [iot_thread] OTA demo version 0.9.20 14 13498 [iot_thread] Creating MQTT Client...

Step 5: Configure the Android app

Download the Android Bluetooth Low Energy SDK and a sample app from the amazon-freertos-ble-android-sdk GitHub repo.

Make the following changes:

  1. Modify the file app/src/main/res/raw/awsconfiguration.json:

    Fill in the "PoolId", "Region", "AppClientId", and "AppClientSecret" fields using the instructions in the following sample JSON.

    { "UserAgent": "MobileHub/1.0", "Version": "1.0", "CredentialsProvider": { "CognitoIdentity": { "Default": { "PoolId": "Cognito->Manage Identity Pools->Federated Identities->mqtt_proxy_identity_pool->Edit Identity Pool->Identity Pool ID", "Region": "Your region (for example us-east-1)" } } }, "IdentityManager": { "Default": {} }, "CognitoUserPool": { "Default": { "PoolId": "Cognito-> Manage User Pools -> mqtt_proxy_user_pool -> General Settings -> PoolId", "AppClientId": "Cognito-> Manage User Pools -> mqtt_proxy_user_pool -> General Settings -> App clients ->Show Details", "AppClientSecret": "Cognito-> Manage User Pools -> mqtt_proxy_user_pool -> General Settings -> App clients ->Show Details", "Region": "Your region (for example us-east-1)" } } }
  2. Modify the file app/src/main/java/software/amazon/freertos/

    • Specify the policy name (for example, "mqtt_proxy_iot_policy") that you created earlier.

    • Set the Region (for example, "us-east-1").

  3. Build and install the demo app:

    1. In Android Studio, choose Build, Make Module app.

    2. Choose Run, Run app. You can go to the logcat window pane in Android Studio to monitor log messages.

    3. On the Android device, create an account from the login screen.

    4. Create a user. If a user already exists, enter the user's credentials.

    5. Allow the FreeRTOS Demo to access the device's location.

    6. Scan for Bluetooth Low Energy devices.

    7. Move the slider for the device found to On.

    8. Press 'y' on the serial port debug console.

    9. Choose Pair & Connect.

The More… link becomes active after the connection. You should see the connection state change to BLE_CONNECTED in the Android device logcat when the connection is complete:

2019-06-06 20:11:32.160 23484-23497/ I/FRD: BLE connection state changed: 0; new state: BLE_CONNECTED

Before the messages can be transmitted, the FreeRTOS device and the Android device must negotiate the MTU. You should see the following printout in logcat:

2019-06-06 20:11:46.720 23484-23497/ I/FRD: onMTUChanged : 512 status: Success

The device connects to the app and starts sending MQTT messages using the MQTT proxy. To confirm that the device can communicate, make sure that you can see the MQTT_CONTROL characteristic data value change to 01:

2019-06-06 20:12:28.752 23484-23496/ D/FRD: <-<-<- Writing to characteristic: MQTT_CONTROL with data: 01 2019-06-06 20:12:28.839 23484-23496/ D/FRD: onCharacteristicWrite for:

Complete pairing on microcontroller console

You will be prompted to press 'y' on the console when the device is paired with the Android device. The demo will not function until this step is performed.

E (135538) BT_GATT: GATT_INSUF_AUTHENTICATION: MITM Required W (135638) BT_L2CAP: l2cble_start_conn_update, the last connection update command still pending. E (135908) BT_SMP: Value for numeric comparison = 391840 15 13588 [InputTask] Numeric comparison:391840 16 13589 [InputTask] Press 'y' to confirm 17 14078 [InputTask] Key accepted W (146348) BT_SMP: FOR LE SC LTK IS USED INSTEAD OF STK 18 16298 [iot_thread] Connecting to broker... 19 16298 [iot_thread] [INFO ][MQTT][162980] Establishing new MQTT connection. 20 16298 [iot_thread] [INFO ][MQTT][162980] (MQTT connection 0x3ffd5754, CONNECT operation 0x3ffd586c) Waiting for operation completion. 21 16446 [iot_thread] [INFO ][MQTT][164450] (MQTT connection 0x3ffd5754, CONNECT operation 0x3ffd586c) Wait complete with result SUCCESS. 22 16446 [iot_thread] [INFO ][MQTT][164460] New MQTT connection 0x3ffc0ccc established. 23 16446 [iot_thread] Connected to broker.


  1. To install the prerequisites, run the following commands:

    pip3 install boto3 pip3 install pathlib
  2. Download the python script.

  3. Bump up the FreeRTOS application version in demos/include/aws_application_version.h and build a new binary file.

  4. To get help, run the following command in a terminal window:

    python3 -h
    usage: [-h] --profile PROFILE [--region REGION] [--account ACCOUNT] [--devicetype DEVICETYPE] --name NAME --role ROLE --s3bucket S3BUCKET --otasigningprofile OTASIGNINGPROFILE --signingcertificateid SIGNINGCERTIFICATEID [--codelocation CODELOCATION] Script to start OTA update optional arguments: -h, --help show this help message and exit --profile PROFILE Profile name created using aws configure --region REGION Region --account ACCOUNT Account ID --devicetype DEVICETYPE thing|group --name NAME Name of thing/group --role ROLE Role for OTA updates --s3bucket S3BUCKET S3 bucket to store firmware updates --otasigningprofile OTASIGNINGPROFILE Signing profile to be created or used --signingcertificateid SIGNINGCERTIFICATEID certificate id (not arn) to be used --codelocation CODELOCATION base folder location (can be relative)
  5. If you used the provided AWS CloudFormation template to create resources, here's an example run:

    python3 --profile otausercf --name my_thing --role ota_ble_iot_role-sample --s3bucket afr-ble-ota-update-bucket-sample --otasigningprofile abcd --signingcertificateid certificateid


  1. From the console, the logs show that the OTA update has started and the file block download is in progress:

    38 2462 [OTA Task] [prvParseJobDoc] Job was accepted. Attempting to start transfer. --- 49 2867 [OTA Task] [prvIngestDataBlock] Received file block 1, size 1024 50 2867 [OTA Task] [prvIngestDataBlock] Remaining: 1290 51 2894 [OTA Task] [prvIngestDataBlock] Received file block 2, size 1024 52 2894 [OTA Task] [prvIngestDataBlock] Remaining: 1289 53 2921 [OTA Task] [prvIngestDataBlock] Received file block 3, size 1024 54 2921 [OTA Task] [prvIngestDataBlock] Remaining: 1288 55 2952 [OTA Task] [prvIngestDataBlock] Received file block 4, size 1024 56 2953 [OTA Task] [prvIngestDataBlock] Remaining: 1287 57 2959 [iot_thread] State: Active Received: 5 Queued: 5 Processed: 5 Dropped: 0
  2. When the OTA update is complete, the device restarts with the updated firmware, connects again to the Android app, and then performs a self test.

  3. If the self test succeeds, the updated firmware is marked as active and you should see the updated version in the console:

    13 13498 [iot_thread] OTA demo version 0.9.21