MQTT with AWS IoT Core

AWS IoT Core is a managed MQTT broker. It is designed for large scale IoT deployments and supports many advanced features.

Prerequisites

We assume that you have set up your development environment as described in the IDE tutorial.

We also assume that you have flashed your device with Jaguar and that you are familiar with running Toit programs on it. If not, have a look at the Hello world tutorial.

While not necessary, we recommend to do the MQTT tutorial first.

Note that you can do this tutorial without a device. In that case, you need to use the -d host option whenever you invoke jag run. The program will then run on your computer instead of on a device.

Packages

The MQTT functionality is not part of the core libraries and must be imported as a package. See the packages tutorial for details.

We are using the mqtt package. To install it, run the following command:

jag pkg install github.com/toitware/mqtt@v2

When connecting to TLS secured services we will also use the certificate-roots package:

jag pkg install github.com/toitware/toit-cert-roots@v1

AWS IoT Core

Amazon has several tutorials on how to get started with their service. For example, see the Getting started guide.

We will not repeat all the steps here, but only give a brief overview.

  1. Create an AWS account, and sign in.
  2. Search for the IoT Core service.
  3. On the left click on "Connect one device".

This will start a wizard that will guide you through the process of creating a device and a certificate.

As instructed, make sure that you can ping your AWS broker. The URL is shown in the wizard. It should be something like <your_id>.iot.<your_region>.amazonaws.com. Remember this URL, we will need it later.

Select "Create a new thing" and give it a name.

The next screen asks you for the platform operating system and Device SDK. We are not using any of the SDKs, so you can keep the default settings.

Download the connection kit on the next page. This zip file contains the certificate and private key for your device. Unzip it and save the following files into your project directory:

  • <name>.cert.pem
  • <name>.private.key
  • <name>-Policy
  • <name>-public.key

Strictly speaking, we only need the certificate and private key, but these files aren't big, and for completeness sake we also save the policy and public key.

Code

AWS IoT Core uses TLS with client certificates to authenticate devices. This requires a few more steps than usual to establish a connection.

Create a new file aws.toit in your project directory, and watch it with Jaguar.

Add the following code to the file:

import certificate-roots
import mqtt
import net.x509
import tls

HOST ::= "<YOUR AMAZON HOST>"
PORT ::= 8883

CLIENT-ID ::= "sdk-nodejs-toit"
TOPIC ::= "sdk/test/js"

CLIENT-CERTIFICATE-DER ::= """
-----BEGIN CERTIFICATE-----
<YOUR CERTIFICATE>
-----END CERTIFICATE-----
"""

CLIENT-KEY-DER ::= """
-----BEGIN RSA PRIVATE KEY-----
<YOUR KEY>
-----END RSA PRIVATE KEY-----
"""

Here the HOST is the URL of your AWS broker. If you forgot it, you can find it in the "Settings" tab of the IoT Core console.

The CLIENT-ID and TOPIC are not arbitrary strings. They must comply with the policy restrictions for this device. These can be found in the <name>-Policy file you downloaded earlier. If you used the "Connect" wizard, it should, for example, have the following restrictions for the "iot:Connect" action:

        "arn:aws:iot:eu-west-1:915358716417:client/sdk-java",
        "arn:aws:iot:eu-west-1:915358716417:client/basicPubSub",
        "arn:aws:iot:eu-west-1:915358716417:client/sdk-nodejs-*"

The client ID, thus must be either sdk-java, basicPubSub, or sdk-nodejs-*, where * is a wildcard. The chosen sdk-nodejs-toit is thus a valid client ID.

Any other client ID will be rejected by the broker, and the connection will be cut (leading to an infinite "Attempting to (re)connect" loop).

Similarly, publishing to a topic that is not allowed by the policy will also lead to a disconnect.

The CLIENT-CERTIFICATE-DER and CLIENT-KEY-DER are the certificate and private key that you downloaded earlier. You can copy and paste them into the file.

Creating the client certificate

As mentioned above, AWS IoT Core uses TLS with client certificates to authenticate devices. Add the following function that creates the client certificate:

create-aws-certificate -> tls.Certificate:
  parsed := x509.Certificate.parse CLIENT-CERTIFICATE-DER
  return tls.Certificate parsed CLIENT-KEY-DER

Connecting to the broker

We can now connect to the broker. Add the following code to the main function:

main:
  certificate-roots.install-common-trusted-roots
  client := mqtt.Client.tls
      --host=HOST
      --certificate=create-aws-certificate
  options := mqtt.SessionOptions --client-id=CLIENT-ID
  client.start --options=options
  client.publish TOPIC "hello"
  client.close

If you still have the AWS IoT Core console open, you should see the message appear in the wizard.

It is not good practice to hard-code credentials in your program. See the secrets tutorial for details on how to handle secrets in Toit projects.

Troubleshooting

Amazon AWS disconnects client that don't satisfy the policy restrictions. Unfortunately, it does not provide any reasons for the disconnect in this case.

If you are seeing a message like Closing because of invalid packet kind: 0, then the connection was likely rejected by the broker.

In that case make sure that:

  • The client ID is correct, and accepted by the policy.
  • The certificate and private key are correct.
  • The topic is accepted by the policy. Topics typically start without a leading "/". If you have seen a debug log stating that the "Connection [was] established", immediately followed by a disconnect/reconnect, then a bad topic could be the problem.

Also remember that you can run Toit programs on your desktop machine. Sometimes this is easier for debugging.

Summary

In this tutorial we have seen how to connect to AWS IoT Core using TLS with client certificates. We have also seen how to use the mqtt package to publish a message to a topic.

AWS IoT Core supports many more features, such as device shadows, rules, and jobs. See the AWS IoT Core documentation for more details.