Continuous Integration of Firmware

Note

The AWS implementation of Bifravst provides resources to continuously test the firmware using real hardware.

Overview

Every commit to the firmware repo will trigger a CI run. The CI run will

  1. create a new device and credentials on AWS IoT

  2. build a firmware that has the device ID hardcoded for the MQTT client ID

  3. create an AWS IoT job with the firmware and the credentials, which is picked up by a the Firmware CI runner (see below)

  4. observe the firmware CI run until it finishes

  5. download the log result from S3

  6. run assertions against the log result

The Firmware CI runner is running on a Raspberry Pi connected to AWS IoT where it receives jobs to execute:

  1. it flashes the firmware and optional credentials using the connected debugger to the connected nRF9160 DK or Thingy:91

  2. it then collects all log output until a. a timeout is reached b. or a stop condition is reached (wait for a log output to match a string)

  3. it uploads the logs to S3

Note

These devices connect to the existing instance of Bifravst, so the firmware tests will not set up a new blank Bifravst AWS environment for every test, but be run against the production environment. This is to ensure that firmware release will work against the existing, working solution. This approach is designed for trunk-based development.

Preparation

Enable the Firmware CI resources of Bifravst that allow GitHub Actions to create test devices, and the the Firmware CI runner to connect by enabling the context switch firmware-ci when deploying the stack (see Getting Started).

echo "firmware-ci=1" >> context.cfg
npx cdk deploy '*'

Print the AWS Key for the CI runner on GitHub Actions using this command:

node cli firmware-ci -s

Region: "<Region>"
Bucket name: "<Bucket name>"
Access Key ID: "<AWS Access Key ID>"
Secret Access Key: "<AWS Secret Access Key>"

Now you can create a new IoT Thing to be used for a Firmware CI runner (see below):

node cli firmware-ci -c

You can delete a device using this command:

node cli firmware-ci -r "<deviceId>"

Configure these as secrets on the firmware GitHub repository:

  • AWS_ACCESS_KEY_ID (as printed above)

  • AWS_SECRET_ACCESS_KEY (as printed above)

  • AWS_REGION (as printed above)

  • STACK_NAME (the stack name of your production environment, usually bifravst)

  • DEVICE_ID (the created Firmwer CI runner device, e.g. firmware-ci-3c431c57-e524-4010-b269-371cb53538b6)

Firmware CI runner setup

  1. Download JLink for your platform. Use the path to the folder (e.g. ~/JLink_Linux_V686_arm64/) further down.

  2. Install firmware-ci-runner-aws:

    git clone https://github.com/bifravst/firmware-ci-runner-aws.git
    cd firmware-ci-runner-aws
    npm ci
    npx tsc
    
  3. Now provide these environment variables:

    export AWS_ACCESS_KEY_ID="<AWS Access Key ID printed above>"
    export AWS_SECRET_ACCESS_KEY="<AWS Secret Access Key printed above>"
    export REGION="<Region printed above>"
    export BUCKET_NAME="<Bucket name printed above>"
    export PATH="<Path to JLINK>":$PATH
    

    The recommended workflow is to use a direnv plugin for your shell which will automatically export the environment variables it finds in a .envrc file in the project folder: Create a new file .envrc in the project folder and add the credentials that are presented to you after you have created the new user.

  4. Copy over the JSON file containing the certificate

  5. Run:

    node cli run "<device>" "<path to certificate.json>"
    

    <device> is the Linux file where the device is connected to, e.g. /dev/ttyACM0.

The Firmware CI will now process all schedule jobs one after another.