AWS IoT

The Amazon Web Services Internet-of-Things (AWS IoT) library enables applications to connect to, and exchange messages with the AWS IoT Core service. The library supports the following technologies:

  • TLS secured MQTT transmission protocol

  • Firmware-Over-The-Air (FOTA)

Setup and configuration

To connect a device to AWS IOT Core, complete the following steps:

  1. Setting up AWS and configuring permissions

  2. Generating and provisioning certificates

  3. Creating a policy

  4. Creating a Thing

  5. Configuring the library

Setting up AWS and configuring permissions

The initial AWS account setup required for using AWS IoT Core is described in Set up your AWS account.

For development purposes, the AWS managed policies AWSIoTConfigAccess and AWSIoTDataAccess provide sufficient permissions to manage AWS IoT. If you want to use AWS FOTA, the AmazonS3FullAccess policy can be used to obtain access to AWS S3.

Note

These policies provide a large number of permissions to the user. Though this can be acceptable for development purposes, you should operate on a least-privilege principle whenever possible.

To complete the steps described in this document, make sure that the following prerequisites are met:

  • Install AWS Command Line Interface on your system and login as a user with appropriate permissions.

  • To use the nrfcredstore tool, the dependencies in the nrf/scripts/requirements-extra.txt file must be installed.

Generating and provisioning certificates

Things in AWS IoT are typically authenticated using device certificates. There are multiple ways to generate and register these certificates:

  • The device key pair and certificate are generated by AWS and downloaded onto the device.

  • The device generates the key pair and a Certificate Signing Request (CSR). This request is uploaded to AWS to obtain a device certificate and is used to generate a self-signed device certificate.

Note

Generating a key pair on device requires an nRF91 Series device. If you are using an nRF9160 DK, modem version v1.3.x or later is required.

Important

Program the Cellular: AT Client sample to your device before following this guide.

Complete the following steps to generate a key pair and CSR on the modem, which is then used to obtain a device certificate signed by AWS:

  1. Obtain a list of installed keys using the following command:

    nrfcredstore <serial port> list
    

    where <serial port> is the serial port of your device.

  2. Select a security tag that is not yet in use. This security tag must match the value set in the CONFIG_AWS_IOT_SEC_TAG Kconfig option.

  3. Generate a key pair and obtain a CSR using the following command:

    nrfcredstore <serial port> generate <sec tag> device_cert.csr.der
    

    where <serial port> is the serial port of your device and <sec tag> is the previously chosen unused security tag.

  4. Convert the CSR from DER format to PEM format using the following command:

    openssl req -inform DER -in device_cert.csr.der -outform PEM -out device_cert.csr.pem
    
  5. Obtain a signed certificate using the following command:

    aws iot create-certificate-from-csr --certificate-signing-request device_cert.csr.pem --certificate-pem-outfile device_cert.pem --set-as-active --no-cli-pager --query certificateArn
    
  6. Take note of the certificate ARN, as it will be required later.

  7. Provision the certificate using the following command:

    nrfcredstore <serial port> write <sec tag> CLIENT_CERT device_cert.pem
    

    where <serial port> is the serial port of your device and <sec tag> is the previously chosen unused security tag.

  8. Download the Amazon Root CA 1 PEM file.

  9. Provision the certificate using the following command:

    nrfcredstore <serial port> write <sec tag> ROOT_CA_CERT AmazonRootCA1.pem
    

    where <serial port> is the serial port of your device and <sec tag> is the previously chosen unused security tag.

Creating a policy

AWS IoT Core policies determine which permissions a Thing has and are required to connect to the AWS IoT data plane. To create a policy, complete these steps:

  1. Create a file policy.json with the following content:

    {
       "Version": "2012-10-17",
       "Statement": [
           {
             "Effect": "Allow",
             "Action": "iot:*",
             "Resource": "*"
           }
        ]
     }
    

    Note

    This policy example is only intended for development environments. Make sure to update this to a more restrictive policy before you go into production. For more information, refer to the example policies listed in AWS IoT Core policy examples and Security best practices in AWS IoT Core.

  2. Create the policy using the following command:

    aws iot create-policy --policy-name my-policy --policy-document file://policy.json
    
  3. Attach the policy to the previously registered certificate using the following command:

    aws iot attach-policy --target <certificate arn> --policy-name my-policy
    

    where <certificate arn> is the ARN of the previously generated device certificate.

Creating a Thing

Create a Thing in AWS IoT core by completing the following steps:

  1. Create a Thing using the following command:

    aws iot create-thing --thing-name <thing name>
    

    where <thing name> is the desired name for the Thing, for example, my-thing.

  2. Attach the certificate to the Thing using the following command:

    aws iot attach-thing-principal --principal <certificate arn> --thing-name <thing name>
    

    where <certificate arn> is the ARN of the previously generated device certificate and <thing name> is the previously chosen name of the Thing.

Configuring the library

Complete the following steps to set the required library options:

  1. Obtain the AWS IoT broker endpoint using the following command:

    aws iot describe-endpoint --endpoint-type iot:Data-ATS
    
  2. Set the CONFIG_AWS_IOT_BROKER_HOST_NAME Kconfig option to the obtained endpoint value. For information on how to set this value at runtime, refer to Setting the AWS host name at runtime.

  3. Set the CONFIG_AWS_IOT_CLIENT_ID_STATIC Kconfig option to the name of the Thing created earlier. For information on how to set this value at runtime, refer to Setting client ID at run-time.

  4. Set the CONFIG_AWS_IOT_SEC_TAG to the security tag for which the key and certificate were provisioned earlier.

Optional library options

To subscribe to the various AWS IoT Device Shadow Topics , set the following options:

Other options:

Note

If you are using a longer device ID that is either set by the option CONFIG_AWS_IOT_CLIENT_ID_STATIC or passed in during initialization, it might be required to increase the value of the option CONFIG_AWS_IOT_CLIENT_ID_MAX_LEN for proper initialization of the library.

Usage

The AWS IoT sample showcases the use of this library and can be used to verify a connection to AWS IoT. To configure and run the sample, complete the steps described in Setup and Building and running.

Initializing the library

The library is initialized by calling the aws_iot_init() function. If this API call fails, the application must not make any other API calls to the library.

Connecting to the AWS IoT message broker

After the initialization, the aws_iot_connect() function must be called to connect to the AWS IoT broker. If this API call fails, the application must retry the connection by calling aws_iot_connect() again.

Note

The connection attempt can fail due to several reasons related to the network. Due to this its recommended to implement a routine that tries to reconnect the device upon a disconnect.

During an attempt to connect to the AWS IoT broker, the library tries to establish a connection using a TLS handshake, which usually spans a few seconds. When the library has established a connection and subscribed to all the configured and passed-in topics, it will propagate the AWS_IOT_EVT_READY event to signify that the library is ready to be used.

Subscribing to non-AWS specific topics

To subscribe to non-AWS specific topics, complete the following steps:

The following code example shows how to subscribe to non-AWS specific topics:

#define CUSTOM_TOPIC_1  "my-custom-topic/example"
#define CUSTOM_TOPIC_2  "my-custom-topic/example2"

const struct aws_iot_topic_data topics_list[2] = {
        [0].str = CUSTOM_TOPIC_1,
        [0].len = strlen(CUSTOM_TOPIC_1),
        [1].str = CUSTOM_TOPIC_2,
        [1].len = strlen(CUSTOM_TOPIC_2)
};

err = aws_iot_subscription_topics_add(topics_list, ARRAY_SIZE(topics_list));
if (err) {
        LOG_ERR("aws_iot_subscription_topics_add, error: %d", err);
        return err;
}

err = aws_iot_init(NULL, aws_iot_event_handler);
if (err) {
        LOG_ERR("AWS IoT library could not be initialized, error: %d", err);
        return err;
}

Publishing to non-AWS specific topics

To publish to a non-AWS specific topic, complete the following steps:

  • Populate a aws_iot_topic_data with the custom topics that you want to publish to. It is not necessary to set the topic type when populating the aws_iot_topic_data structure. This type is reserved for AWS IoT shadow topics.

  • Pass in the entry that corresponds to the topic that the payload is to be published to in the message structure aws_iot_data. This structure is then passed into the aws_iot_send() function.

The following code example shows how to publish to non-AWS specific topics:

#define MY_CUSTOM_TOPIC_1 "my-custom-topic/example"
#define MY_CUSTOM_TOPIC_1_IDX 0

static struct aws_iot_topic_data pub_topics[1] = {
        [MY_CUSTOM_TOPIC_1_IDX].str = MY_CUSTOM_TOPIC_1,
        [MY_CUSTOM_TOPIC_1_IDX].len = strlen(MY_CUSTOM_TOPIC_1),
};

struct aws_iot_data msg = {
        /* Pointer to payload */
        .ptr = buf,

        /* Length of payload */
        .len = len,

         /* Message ID , if not set it will be provided by the AWS IoT library */
        .message_id = id,

        /* Quality of Service level */
        .qos = MQTT_QOS_0_AT_MOST_ONCE,

        /* "my-custom-topic/example" */
        .topic = pub_topics[MY_CUSTOM_TOPIC_1_IDX]
};

err = aws_iot_send(&msg);
if (err) {
        LOG_ERR("aws_iot_send, error: %d", err);
        return err;
}

Setting client ID at run-time

The AWS IoT library also supports passing in the client ID at run time. To enable this feature, set the client_id entry in the aws_iot_config structure that is passed in the aws_iot_init() function when initializing the library, and set the CONFIG_AWS_IOT_CLIENT_ID_APP Kconfig option.

Setting the AWS host name at runtime

The AWS IoT library also supports passing the endpoint address at runtime by setting the CONFIG_AWS_IOT_BROKER_HOST_NAME_APP option. If this option is set, the host_name and host_name_len must be set in the aws_iot_config structure before it is passed into the aws_iot_init() function. The length of your AWS host name (CONFIG_AWS_IOT_BROKER_HOST_NAME) must be shorter than the default value of CONFIG_AWS_IOT_BROKER_HOST_NAME_MAX_LEN, for proper initialization of the library.

Testing and debugging

For general information about testing and debugging, see Testing and debugging an application.

Topic monitoring

To observe incoming messages, navigate to the AWS IoT console and click MQTT test client. Subscribe to the topic that you want to monitor, or use the wild card token # to monitor all topics.

Troubleshooting

For issues related to the library and nRF Connect SDK in general, refer to Known issues.

  • If you are experiencing unexpected disconnects from AWS IoT, try decreasing the value of the CONFIG_MQTT_KEEPALIVE option or publishing data more frequently. AWS IoT specifies a maximum allowed keepalive of 1200 seconds (20 minutes), however in certain LTE networks, the Network Address Translation (NAT) timeout can be considerably lower. As a recommendation to prevent the likelihood of unexpected disconnects, set the option CONFIG_MQTT_KEEPALIVE to the highest value of the network NAT and maximum allowed MQTT keepalive.

  • If publishing larger payloads fails, you might need to increase the value of the CONFIG_AWS_IOT_MQTT_RX_TX_BUFFER_LEN option.

  • For nRF9160-based boards, the size of incoming messages cannot exceed approximately 2 kB. This is due to a limitation of the modem’s internal TLS buffers. Messages that exceed this limitation will be dropped.

AWS FOTA

The library automatically includes and enables support for FOTA using the AWS FOTA library. To create a FOTA job, refer to the AWS FOTA documentation.

API documentation

Header file: include/net/aws_iot.h
Source files: subsys/net/lib/aws_iot/src/
group aws_iot

Library to connect the device to the AWS IoT message broker.

Typedefs

typedef void (*aws_iot_evt_handler_t)(const struct aws_iot_evt *evt)

AWS IoT library asynchronous event handler.

Param evt:

[in] The event and any associated parameters.

Enums

enum aws_iot_shadow_topic_type

AWS IoT shadow topics, used in messages to specify which shadow topic that will be published to.

Values:

enumerator AWS_IOT_SHADOW_TOPIC_NONE

Unused default value.

enumerator AWS_IOT_SHADOW_TOPIC_GET

This topic type corresponds to $aws/things/<thing-name>/shadow/get, publishing an empty message to this topic requests the device shadow document.

enumerator AWS_IOT_SHADOW_TOPIC_UPDATE

This topic type corresponds to $aws/things/<thing-name>/shadow/update, publishing data to this topic updates the device shadow document.

enumerator AWS_IOT_SHADOW_TOPIC_DELETE

This topic type corresponds to $aws/things/<thing-name>/shadow/delete, publishing an empty message to this topic deletes the device Shadow document.

enum aws_disconnect_result

@ AWS broker disconnect results.

Values:

enumerator AWS_IOT_DISCONNECT_USER_REQUEST
enumerator AWS_IOT_DISCONNECT_CLOSED_BY_REMOTE
enumerator AWS_IOT_DISCONNECT_INVALID_REQUEST
enumerator AWS_IOT_DISCONNECT_MISC
enumerator AWS_IOT_DISCONNECT_COUNT
enum aws_connect_result

AWS broker connect results.

Values:

enumerator AWS_IOT_CONNECT_RES_SUCCESS
enumerator AWS_IOT_CONNECT_RES_ERR_NOT_INITD
enumerator AWS_IOT_CONNECT_RES_ERR_INVALID_PARAM
enumerator AWS_IOT_CONNECT_RES_ERR_NETWORK
enumerator AWS_IOT_CONNECT_RES_ERR_BACKEND
enumerator AWS_IOT_CONNECT_RES_ERR_MISC
enumerator AWS_IOT_CONNECT_RES_ERR_NO_MEM
enumerator AWS_IOT_CONNECT_RES_ERR_PRV_KEY
enumerator AWS_IOT_CONNECT_RES_ERR_CERT
enumerator AWS_IOT_CONNECT_RES_ERR_CERT_MISC
enumerator AWS_IOT_CONNECT_RES_ERR_TIMEOUT_NO_DATA
enumerator AWS_IOT_CONNECT_RES_ERR_ALREADY_CONNECTED
enum aws_iot_evt_type

AWS IoT notification event types, used to signal the application.

Values:

enumerator AWS_IOT_EVT_CONNECTING

Connecting to AWS IoT broker.

enumerator AWS_IOT_EVT_CONNECTED

Connected to AWS IoT broker.

enumerator AWS_IOT_EVT_READY

AWS IoT library has subscribed to all configured topics.

enumerator AWS_IOT_EVT_DISCONNECTED

Disconnected to AWS IoT broker.

enumerator AWS_IOT_EVT_DATA_RECEIVED

Data received from AWS message broker.

enumerator AWS_IOT_EVT_PUBACK

Acknowledgment for data sent to AWS IoT.

enumerator AWS_IOT_EVT_PINGRESP

Acknowledgment for pings sent to AWS IoT.

enumerator AWS_IOT_EVT_FOTA_START

FOTA update start.

enumerator AWS_IOT_EVT_FOTA_DONE

FOTA done. Payload of type dfu_target_image_type (image).

    If the image parameter type is of type DFU_TARGET_IMAGE_TYPE_MCUBOOT the device needs to
    reboot to apply the new application image.

    If the image parameter type is of type DFU_TARGET_IMAGE_TYPE_MODEM_DELTA the modem
    needs to be reinitialized to apply the new modem image.

enumerator AWS_IOT_EVT_FOTA_ERASE_PENDING

FOTA erase pending.

enumerator AWS_IOT_EVT_FOTA_ERASE_DONE

FOTA erase done.

enumerator AWS_IOT_EVT_FOTA_DL_PROGRESS

FOTA progress notification.

enumerator AWS_IOT_EVT_FOTA_ERROR

FOTA error. Used to propagate FOTA-related errors to the application. This is to distinguish between AWS_IOT irrecoverable errors and FOTA errors, so they can be handled differently.

enumerator AWS_IOT_EVT_ERROR

AWS IoT library irrecoverable error.

Functions

int aws_iot_init(const struct aws_iot_config *const config, aws_iot_evt_handler_t event_handler)

Initialize the module.

Note

This API must be called exactly once, and it must return successfully.

Parameters:
  • config[in] Pointer to struct containing connection parameters.

  • event_handler[in] Pointer to event handler to receive AWS IoT module events.

Returns:

0 If successful. Otherwise, a (negative) error code is returned.

int aws_iot_connect(struct aws_iot_config *const config)

Connect to the AWS IoT broker.

This function exposes the MQTT socket to main so that it can be polled on.

Parameters:
  • config[out] Pointer to struct containing connection parameters, the MQTT connection socket number will be copied to the socket entry of the struct.

Returns:

0 If successful. Otherwise, a (negative) error code is returned.

int aws_iot_disconnect(void)

Disconnect from the AWS IoT broker.

Returns:

0 If successful. Otherwise, a (negative) error code is returned.

int aws_iot_send(const struct aws_iot_data *const tx_data)

Send data to AWS IoT broker.

Parameters:
  • tx_data[in] Pointer to struct containing data to be transmitted to the AWS IoT broker.

Returns:

0 If successful. Otherwise, a (negative) error code is returned.

int aws_iot_input(void)

Get data from AWS IoT broker.

Returns:

0 If successful. Otherwise, a (negative) error code is returned.

int aws_iot_ping(void)

Ping AWS IoT broker. Must be called periodically to keep connection to broker alive.

Returns:

0 If successful. Otherwise, a (negative) error code is returned.

int aws_iot_subscription_topics_add(const struct aws_iot_topic_data *const topic_list, size_t list_count)

Add a list of application specific topics that will be subscribed to upon connection to AWS IoT broker.

Parameters:
  • topic_list[in] Pointer to list of topics.

  • list_count[in] Number of entries in the list.

Returns:

0 If successful. Otherwise, a (negative) error code is returned.

struct aws_iot_topic_data
#include <aws_iot.h>

AWS IoT topic data.

Public Members

enum aws_iot_shadow_topic_type type

Optional: type of shadow topic that will be published to. When publishing to a shadow topic this can be set instead of the application specific topic below.

const char *str

Pointer to string of application specific topic.

size_t len

Length of application specific topic.

struct aws_iot_app_topic_data
#include <aws_iot.h>

Structure used to declare a list of application specific topics passed in by the application.

Public Members

struct mqtt_topic list[CONFIG_AWS_IOT_APP_SUBSCRIPTION_LIST_COUNT]

List of application specific topics.

size_t list_count

Number of entries in topic list.

struct aws_iot_data
#include <aws_iot.h>

AWS IoT transmission data.

Public Members

struct aws_iot_topic_data topic

Topic data is sent/received on.

char *ptr

Pointer to data sent/received from the AWS IoT broker.

size_t len

Length of data.

enum mqtt_qos qos

Quality of Service of the message.

uint16_t message_id

Message id, used to match acknowledgments.

uint8_t dup_flag

Duplicate flag. 1 indicates the message is a retransmission, Usually triggered by missing publication acknowledgment.

uint8_t retain_flag

Retain flag. 1 indicates to AWS IoT that the message should be stored persistently.

struct aws_iot_evt
#include <aws_iot.h>

Struct with data received from AWS IoT broker.

Public Members

enum aws_iot_evt_type type

Type of event.

int fota_progress

FOTA progress in percentage.

struct aws_iot_config
#include <aws_iot.h>

Structure for AWS IoT broker connection parameters.

Public Members

int socket

Socket for AWS IoT broker connection

char *client_id

Client id for AWS IoT broker connection, used when CONFIG_AWS_IOT_CLIENT_ID_APP is set. If not set an internal configurable static client id is used.

size_t client_id_len

Length of client_id string.

char *host_name

AWS IoT endpoint host name for broker connection, used when CONFIG_AWS_IOT_BROKER_HOST_NAME_APP is set. If not the static AWS_IOT_BROKER_HOST_NAME is used.

size_t host_name_len

Length of host_name string.