Improve this doc

AWS IoT Integration

balena aws iot

The Amazon Web Services Internet-of-Things (AWS IoT) service enables bi-directional communication between Internet-connected things, such as sensors, embedded devices, or appliances, and other services on the AWS cloud, such as cloud servers, databases, analytics and more. This document provides an overview how to use AWS IoT component with balena to deploy IoT devices on the AWS IoT platform.

Configuring AWS IoT

Sign up for an AWS account or log into your account at the AWS Console. Once logged in, navigate to the AWS IoT console from AWS services dashboard.

For more details about the following steps, please check the AWS Documentation, and in particular the AWS IoT Documentation. Most tasks are available both in the web interface and through the AWS Command Line Interface (CLI).

Create a thing

Physical devices that are sending and/or receiving data are called "things" on AWS IoT. They can connect to the platform using certificates, and have to have rules defined to give the device ability to communicate with AWS services. Optionally they are grouped with a thing type to make configuration of similar devices easier.

From the AWS IoT Core page, go to Manage -> Things. Then on the page, go ahead and register a thing, then on the next page select Create a single thing.

create thing

create thing

For this example project, we will create a thing called balena_project. All you need to do is to insert the name and click Next.

generate certs

Each device or thing, must have its own certificates that will be used to authenticate with AWS IoT, so let’s use the One-click certificate creation option.

generate certs

In order to authenticate with the service, you will first need to download all four certificates from the dashboard to your computer (make sure you also download the root CA for AWS IoT).

download certs

Create a policy

Now it’s time to create some policies to allow our devices to communicate with the platform. Go back to the IoT Core, open Secure -> Policies and click on Create a policy.

create policy

For this project create a policy called balena_control_policy, and add the statement as shown below:

Action iot:*
Resource ARN *
Effect Allow

create policy

Go to *Secure -> Certificates. Select the recently created certificate and attach both the thing and policy to it.

The policy previously created enables all devices (things) to connect to our AWS IoT broker, but for security reasons, when you add the *thing to the certificate, it guarantees that only those with matching security keys will be able to connect to the server.

create policy

The last step in configuring the AWS IoT is to get the endpoint URL to connect to the service. Simply go to AWS IoT and click on Settings. There you will find the endpoint. Save it as we will need it later on.

endpoint

At this point everything is ready on the AWS side, so let’s go ahead and configure our device to communicate with it using balenaCloud.

Flashing the Raspberry Pi and deploying code

Step 1 - Set up the balenaCloud application

If you don’t have one already, sign up for a balenaCloud account. The first thing we need to do is to create a new application, for that click on Create application, give it a name and select a device type (on this example we will create a project called aws-iot to run on a Raspberry Pi 3).

With the application created, click on Add device and select the latest recommended balenaOS version, choose the network connection you desire, setup its credentials and download the balenaOS to your computer.

add device

Step 2 - Flash your device

Use balenaEtcher to flash your Raspberry Pi with the downloaded OS image from the previous section. Insert the SD card into your computer, select the balenaOS image file, select the SD Card and click Flash!

etcher

After flashing is done, insert the SD card into your device and turn it on. After a few seconds, it should connect to the internet and show up on the balenaCloud dashboard.

etcher

Step 3 - Push the app code

Once your device is showing up on the dashboard, it is time to push the code to balenaCloud, after which it will automatically distribute it to all of the devices in your application. For that, we will download the source code from GitHub and push the project to the device using the balena CLI tools. I’ve summarised the process for this below, but if you need more information we have a detailed deployment guide available in our docs.

First of all, download the app from the GitHub project repository, and clone or download it to your computer.

github

Then, after installing the balena CLI tools on your computer, from the project directory, execute balena push <appName> where <appName> is the name of the application you created within the balenaCloud dashboard earlier. For this example project, we will use then balena push aws-iot.

If all went well you’ll see the balena unicorn mascot and the code you’ve just pushed will automatically be distributed to the devices in your application.

balena push

With your hardware provisioned and the code deployed, it’s time to configure the device so it can connect to the Amazon servers.

Converting the certificates to base64

When configuring your device to communicate with AWS IoT, each device must contain its own certificates. The issue with the certificate files is that you can’t and shouldn’t add them to the project directory as it would create a security issue for the whole project. Instead, we will deploy all the devices with the same source-code and configure individual certificates from the balenaCloud dashboard, making use of environment variables.

The method we will apply is to convert the cert files we previously downloaded into base64 strings and paste them into our device’s variables.

You can generate the base64 encoded files from the terminal with: openssl base64 -in <in file> -out <out file>

For this project, you will need to convert the root CA root-CA.crt, the thing certificate xxx.cert.pem and the private key xxx.private.key. Then you will paste the content of the files into our balenaDash environment variables as described in the next session.

Add Environment Variables

To add the environment variables for the device, on the device dashboard page, go to D(x) Device Variables and add the following variables with the values from the conversion in the previous step.

ENV VAR Value
AWS_ENDPOINT data.iot.us-west-2.amazonaws.com
AWS_PRIVATE_CERT Base64 string of xxx.cert.pem
AWS_ROOT_CERT Base64 string of root-CA.crt
AWS_THING_CERT Base64 string of xxx.private.key

You should now have something similar to:

end vars

Using Node.js

The previous part of the documentation describes how to create a sample project with Python. In case you want to create a Node.js application, the AWS Javascript SDK package is capable of both working with the AWS IoT resources and the data communication on the IoT Data Plane. Thus it can be used to implement both the provisioning and the device side of the application. However for security reasons it isn't encouraged to use the AWS Javascript SDK on devices in the field, it is better instead to just use the AWS IoT device SDK doesn't have resource management capabilities. Therefore for this example, we have split the code into two parts. balena-aws-lambda is responsible the resource provisioning and balena-aws-device only handles data communication.

For a complete Node.js example, please see the pair of balena-aws-lambda and balena-aws-device repositories!

Here are a few notes using the AWS IoT device SDK with balena devices. Using Dockerfile templates, start from the balena default Node.js images, for example:

FROM balenalib/%%BALENA_MACHINE_NAME%%-node:latest

Add the aws-iot-device-sdk dependency in your package.json in your application's folder:

npm install --save aws-iot-device-sdk

Later in your Dockerfile.template you can then configure the node modules installation as:

COPY package.json ./
RUN JOBS=MAX npm i --unsafe-perm --production && npm cache clean

Then in your application you can access the environmental variables through process.env.VARIABLE, and send messages through the SDK. A simple example is shown below (where new-lines in the relevant environment variables were escaped base64 encoded strings):

var awsIot = require('aws-iot-device-sdk');
var Chance = require('chance'); // used to randomise bool values
var chance = new Chance();

var device = awsIot.device({
privateKey: new Buffer(process.env.AWS_PRIVATE_KEY, 'base64'),
clientCert: new Buffer(process.env.AWS_CERT, 'base64'),
    caCert: new Buffer(process.env.AWS_ROOT_CA, 'base64'),
  clientId: process.env.BALENA_DEVICE_UUID,
    region: process.env.AWS_REGION
});

device.on('connect', function() {
  console.log('connect');
  device.subscribe('sensor');
  // publish data
  setInterval(function () {
    var reading = chance.floating({min: 0, max: 200});
        device.publish('sensor', JSON.stringify({ reading: reading }));
  }, process.env.INTERVAL || 3000);
});

device.on('message', function(topic, payload) {
  console.log('message', topic, payload.toString());
});

General

You can create base64 encoded strings from key files to be use with environment variables for example in Python as:

import base64

filename = "KEYFILE"
with open(filename, "rb") as key_file:
    key = key_file.read()
    encoded_key = base64.b64encode(key)
    print(encoded_key.decode("ascii") )

or in Linux as:

cat KEYFILE | base64 -w 0

where you need to replace KEYFILE with the relevant filename (such as xxxxxxxxxx-certificate.pem.crt or xxxxxxxxxx-private.pem.key).

Further information

Shortcuts:

Sample Apps

A few sample apps to get started: