.. _lib_aws_iot:

AWS IoT
#######

.. contents::
   :local:
   :depth: 2

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)

.. _aws_setup_and_configuration:

Setup and configuration
***********************

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

1. :ref:`aws_setup_and_permissions`
#. :ref:`generating_and_provisioning_certificates`
#. :ref:`creating_a_policy`
#. :ref:`creating_a_thing`
#. :ref:`Configuring the library <configuring_the_library>`

.. rst-class:: numbered-step

.. _aws_setup_and_permissions:

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 :file:`nrf/scripts/requirements-extra.txt` file must be installed.

.. rst-class:: numbered-step

.. _generating_and_provisioning_certificates:

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.

.. tabs::

   .. tab:: nRF91: Keys generated on device

      .. 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 :ref:`at_client_sample` 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:

         .. code-block:: console

            nrfcredstore <serial port> list

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

      #. Select a security tag that is not yet in use.
         This security tag must match the value set in the :kconfig:option:`CONFIG_MQTT_HELPER_SEC_TAG` Kconfig option.

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

         .. code-block:: console

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

         |serial_port_sec_tag|

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

         .. code-block:: console

            openssl req -inform DER -in device_cert.csr.der -outform PEM -out device_cert.csr.pem

      #. Obtain a signed certificate using the following command:

         .. code-block:: console

            aws iot create-certificate-from-csr --certificate-signing-request file://device_cert.csr.pem --certificate-pem-outfile device_cert.pem --set-as-active --no-cli-pager --query certificateArn

      #. Take note of the certificate ARN, as it will be required later.
      #. Provision the certificate using the following command:

         .. code-block:: console

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

         |serial_port_sec_tag|

      #. Download the `Amazon Root CA 1`_ PEM file.
      #. Provision the certificate using the following command:

         .. code-block:: console

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

         |serial_port_sec_tag|

   .. tab:: nRF91: Keys generated by AWS

      .. warning::
         This option is not recommended for production scenarios, since the private key leaves the device.

      .. important::
         Program the :ref:`at_client_sample` sample to your device before following this guide.

      To obtain a key pair and certificate generated by AWS, and to provision them to the modem, complete the following steps:

      1. Generate the key pair and certificate using the following command:

         .. code-block:: console

            aws iot create-keys-and-certificate --set-as-active --certificate-pem-outfile device_cert.pem --public-key-outfile pub_key.pem --private-key-outfile priv_key.pem --no-cli-pager --query certificateArn

      #. Take note of the certificate ARN, as it will be required later.
      #. Obtain a list of installed keys using the following command:

         .. code-block:: console

            nrfcredstore <serial port> list

         where ``<serial port>`` corresponds to the serial port of your device.

      #. Select a security tag that is not yet in use.
         This security tag must match the value set in :kconfig:option:`CONFIG_MQTT_HELPER_SEC_TAG` Kconfig option.

      #. Provision the client certificate using the following command:

         .. code-block:: console

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

         |serial_port_sec_tag|

      #. Provision the client key using the following command:

         .. code-block:: console

            nrfcredstore <serial port> write <sec tag> CLIENT_KEY priv_key.pem

         |serial_port_sec_tag|

      #. Download the `Amazon Root CA 1`_ PEM file.
      #. Provision the certificate using the following command:

         .. code-block:: console

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

         |serial_port_sec_tag|

   .. tab:: nRF70: Keys generated by AWS

      To obtain a key pair and certificate generated by AWS, complete the following steps:

      1. Generate the key pair and certificate using the following command:

         .. code-block:: console

            aws iot create-keys-and-certificate --set-as-active --certificate-pem-outfile client-cert.pem --public-key-outfile public-key.pem --private-key-outfile private-key.pem --no-cli-pager --query certificateArn

      #. Take note of the certificate ARN, as it will be required later.
      #. Download the `Amazon Root CA 1`_ PEM file as :file:`ca-cert.pem`.
      #. Place the PEM files into the folder path specified by the :kconfig:option:`CONFIG_MQTT_HELPER_CERTIFICATES_FOLDER` option, default is :file:`<app_src_dir>/certs/`.
         Ensure that the :kconfig:option:`CONFIG_MQTT_HELPER_PROVISION_CERTIFICATES` option is set.
      #. If the files are placed correctly, the :ref:`lib_mqtt_helper` library finds the certificates and provisions them to the Mbed TLS stack when connecting to AWS IoT.

.. rst-class:: numbered-step

.. _creating_a_policy:

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 :file:`policy.json` with the following content:

   .. code-block:: javascript

      {
         "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`_.

#. Create the policy using the following command:

   .. code-block:: console

      aws iot create-policy --policy-name my-policy --policy-document file://policy.json

#. Attach the policy to the previously registered certificate using the following command:

   .. code-block:: console

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

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

.. rst-class:: numbered-step

.. _creating_a_thing:

Creating a Thing
================

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

1. Create a *Thing* using the following command:

   .. code-block:: console

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

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

#. Attach the certificate to the *Thing* using the following command:

   .. code-block:: console

      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*.

.. rst-class:: numbered-step

.. _configuring_the_library:

Configuring the library
=======================

Complete the following steps to set the required library options:

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

   .. code-block:: console

      aws iot describe-endpoint --endpoint-type iot:Data-ATS

#. Set the :kconfig:option:`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 :ref:`lib_set_aws_hostname`.
#. Set the :kconfig:option:`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 :ref:`lib_set_client_id`.
#. Set the :kconfig:option:`CONFIG_MQTT_HELPER_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:

* :kconfig:option:`CONFIG_AWS_IOT_TOPIC_GET_ACCEPTED_SUBSCRIBE`
* :kconfig:option:`CONFIG_AWS_IOT_TOPIC_GET_REJECTED_SUBSCRIBE`
* :kconfig:option:`CONFIG_AWS_IOT_TOPIC_UPDATE_ACCEPTED_SUBSCRIBE`
* :kconfig:option:`CONFIG_AWS_IOT_TOPIC_UPDATE_REJECTED_SUBSCRIBE`
* :kconfig:option:`CONFIG_AWS_IOT_TOPIC_UPDATE_DELTA_SUBSCRIBE`
* :kconfig:option:`CONFIG_AWS_IOT_TOPIC_DELETE_ACCEPTED_SUBSCRIBE`
* :kconfig:option:`CONFIG_AWS_IOT_TOPIC_DELETE_REJECTED_SUBSCRIBE`

Other options:

* :kconfig:option:`CONFIG_AWS_IOT_BROKER_HOST_NAME`
* :kconfig:option:`CONFIG_AWS_IOT_CLIENT_ID_STATIC`
* :kconfig:option:`CONFIG_AWS_IOT_CLIENT_ID_MAX_LEN`
* :kconfig:option:`CONFIG_AWS_IOT_CONNECT_TIMEOUT_SECONDS`
* :kconfig:option:`CONFIG_AWS_IOT_AUTO_DEVICE_SHADOW_REQUEST`

MQTT helper library specific options:

* :kconfig:option:`CONFIG_MQTT_HELPER_SEND_TIMEOUT`
* :kconfig:option:`CONFIG_MQTT_HELPER_SEND_TIMEOUT_SEC`
* :kconfig:option:`CONFIG_MQTT_HELPER_SEC_TAG`
* :kconfig:option:`CONFIG_MQTT_HELPER_SECONDARY_SEC_TAG`
* :kconfig:option:`CONFIG_MQTT_HELPER_PORT`
* :kconfig:option:`CONFIG_MQTT_HELPER_RX_TX_BUFFER_SIZE`
* :kconfig:option:`CONFIG_MQTT_HELPER_PAYLOAD_BUFFER_LEN`
* :kconfig:option:`CONFIG_MQTT_HELPER_STACK_SIZE`
* :kconfig:option:`CONFIG_MQTT_HELPER_NATIVE_TLS`
* :kconfig:option:`CONFIG_MQTT_HELPER_LAST_WILL`
* :kconfig:option:`CONFIG_MQTT_HELPER_LAST_WILL_TOPIC`
* :kconfig:option:`CONFIG_MQTT_HELPER_LAST_WILL_MESSAGE`

.. note::
   If you are using a longer client ID that is either set by the option :kconfig:option:`CONFIG_AWS_IOT_CLIENT_ID_STATIC` or passed in during connect, it might be required to increase the value of the option :kconfig:option:`CONFIG_AWS_IOT_CLIENT_ID_MAX_LEN` to reserve enough space for the client ID string.

.. _aws_iot_usage:

Usage
*****

The :ref:`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 :ref:`aws_iot_sample_server_setup` and :ref:`aws_iot_sample_building_and_running`.

Initializing the library
========================

The library is initialized by calling the :c:func:`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 MQTT broker
=====================================

After the initialization, the :c:func:`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 :c:func:`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 :c:enumerator:`AWS_IOT_EVT_CONNECTED` event to signify that the library is connected and ready to be used.

Subscribing to non-AWS specific topics
======================================

To subscribe to non-AWS specific topics, pass a list containing the topics using the :c:func:`aws_iot_application_topics_set` function before calling the :c:func:`aws_iot_connect` function.

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

.. code-block:: c

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

	static const struct mqtt_topic topic_list[] = {
		{
			.topic.utf8 = MY_CUSTOM_TOPIC_1,
			.topic.size = strlen(MY_CUSTOM_TOPIC_1),
			.qos = MQTT_QOS_1_AT_LEAST_ONCE,
		},
		{
			.topic.utf8 = MY_CUSTOM_TOPIC_2,
			.topic.size = strlen(MY_CUSTOM_TOPIC_2),
			.qos = MQTT_QOS_1_AT_LEAST_ONCE,
		}
	};

	err = aws_iot_application_topics_set(topic_list, ARRAY_SIZE(topic_list));
	if (err) {
		LOG_ERR("aws_iot_application_topics_set, error: %d", err);
		FATAL_ERROR();
		return err;
	}

Publishing to non-AWS specific topics
=====================================

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

* Populate a :c:struct:`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 :c:struct:`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 :c:struct:`aws_iot_data`.
  This structure is then passed into the :c:func:`aws_iot_send` function.

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

.. code-block:: c

	#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;
	}

.. _lib_set_client_id:

Setting client ID at run-time
=============================

The library supports passing in the client ID at runtime.
To use this feature, set the ``client_id`` entry in the :c:struct:`aws_iot_config` structure that is passed in the :c:func:`aws_iot_connect` function when connecting.
The ``client_id`` entry must be a null-terminated string.

.. _lib_set_aws_hostname:

Setting the AWS host name at runtime
====================================

The library supports passing in the endpoint URL at runtime.
To use this feature, set the ``host_name`` entry in the :c:struct:`aws_iot_config` structure that is passed in the :c:func:`aws_iot_connect` function when connecting.
The ``client_id`` entry must be a null-terminated string.

.. _aws_iot_testing_and_debugging:

Testing and debugging
=====================

For general information about testing and debugging, see :ref:`testing`.

Topic monitoring
----------------

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

.. _aws_iot_troubleshooting:

Troubleshooting
===============

For issues related to the library and |NCS| in general, refer to :ref:`known_issues`.

* If you are experiencing unexpected disconnects from AWS IoT, try decreasing the value of the :kconfig:option:`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 :kconfig: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 :kconfig:option:`CONFIG_MQTT_HELPER_RX_TX_BUFFER_SIZE` option.
* For nRF91 Series devices, 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 supports FOTA using the :ref:`lib_aws_fota` library.
This library can be enabled by setting the :kconfig:option:`CONFIG_AWS_FOTA` Kconfig option.
To create a FOTA job, refer to the :ref:`lib_aws_fota` documentation.

API documentation
*****************

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

.. doxygengroup:: aws_iot
   :project: nrf
   :members:
