diff options
| -rw-r--r-- | .gitignore | 6 | ||||
| -rw-r--r-- | CMakeLists.txt | 14 | ||||
| -rw-r--r-- | Kconfig | 78 | ||||
| -rw-r--r-- | README.rst | 188 | ||||
| -rw-r--r-- | overlay-qemu-x86-tls.conf | 22 | ||||
| -rw-r--r-- | overlay-tls.conf | 13 | ||||
| -rw-r--r-- | prj.conf | 49 | ||||
| -rw-r--r-- | prj_qemu_x86.conf | 43 | ||||
| -rw-r--r-- | sample.yaml | 9 | ||||
| -rw-r--r-- | src/certificates.h | 38 | ||||
| -rw-r--r-- | src/main.c | 603 | ||||
| -rw-r--r-- | west.yml | 13 |
12 files changed, 1076 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..635a99b --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +# editors +*.swp +*~ + +# build +/build*/ diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..8fe58a9 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,14 @@ +# +# Copyright (c) 2018 Nordic Semiconductor ASA +# +# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause +# + +cmake_minimum_required(VERSION 3.20.0) + +find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) +project(mqtt-simple) + +# NORDIC SDK APP START +target_sources(app PRIVATE src/main.c) +# NORDIC SDK APP END @@ -0,0 +1,78 @@ +# +# Copyright (c) 2020 Nordic Semiconductor ASA +# +# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause +# + +menu "MQTT simple sample" +config MQTT_PUB_TOPIC + string "MQTT publish topic" + default "my/publish/topic" + +config MQTT_SUB_TOPIC + string "MQTT subscribe topic" + default "my/subscribe/topic" + +config MQTT_CLIENT_ID + string "MQTT Client ID" + help + Use a custom Client ID string. If not set, the client ID will be + generated based on IMEI number (for nRF9160 based targets) or + randomly (for other platforms). + default "" + +config MQTT_BROKER_HOSTNAME + string "MQTT broker hostname" + default "test.mosquitto.org" + +config MQTT_BROKER_PORT + int "MQTT broker port" + default 1883 + +config MQTT_MESSAGE_BUFFER_SIZE + int "MQTT message buffer size" + default 128 + +config MQTT_PAYLOAD_BUFFER_SIZE + int "MQTT payload buffer size" + default 128 + +config BUTTON_EVENT_PUBLISH_MSG + string "The message to publish on a button event" + default "Hello from nRF91 MQTT Simple Sample" + +config BUTTON_EVENT_BTN_NUM + int "The button number" + default 1 + +config MQTT_RECONNECT_DELAY_S + int "Seconds to delay before attempting to reconnect to the broker" + default 60 + +config LTE_CONNECT_RETRY_DELAY_S + int "Seconds to delay before attempting to retry LTE connection" + default 120 + +config MQTT_TLS_SEC_TAG + int "TLS credentials security tag" + default 24 + +config MQTT_TLS_SESSION_CACHING + bool "Enable TLS session caching" + +config MQTT_TLS_PEER_VERIFY + int "Set peer verification level" + default 2 + help + Set to 0 for VERIFY_NONE, 1 for VERIFY_OPTIONAL, and 2 for + VERIFY_REQUIRED. + +endmenu + +menu "Zephyr Kernel" +source "Kconfig.zephyr" +endmenu + +module = MQTT_SIMPLE +module-str = MQTT Simple +source "${ZEPHYR_BASE}/subsys/logging/Kconfig.template.log_config" diff --git a/README.rst b/README.rst new file mode 100644 index 0000000..e10c3dd --- /dev/null +++ b/README.rst @@ -0,0 +1,188 @@ +.. _mqtt_simple_sample: + +nRF9160: Simple MQTT +#################### + +.. contents:: + :local: + :depth: 2 + +The Simple MQTT sample demonstrates how to easily connect an nRF9160 SiP to an MQTT broker and send and receive data. + +Requirements +************ + +The sample supports the following development kit: + +.. table-from-sample-yaml:: + +Additionally, the sample supports :ref:`qemu_x86`. + +.. include:: /includes/tfm.txt + +Overview +********* + +The sample connects to an MQTT broker and publishes the data it receives on the configured subscribe topic to the configured publish topic. +On a button press event or typing ``mqtt publish`` in the terminal emulator, the sample publishes the configured message to the configured publish topic. +By default, the sample can establish a secure (TLS) connection or a non-secure connection to the configured MQTT broker. +The sample disables power saving modes (PSM and eDRX) so that network events are processed as soon as possible. + +Configuration +************* + +|config| + +Configuration options +===================== + +Check and configure the following configuration options for the sample: + +.. _CONFIG_MQTT_BROKER_HOSTNAME: + +CONFIG_MQTT_BROKER_HOSTNAME - MQTT Broker host name + This configuration option defines the MQTT Broker host name. + +.. _CONFIG_MQTT_CLIENT_ID: + +CONFIG_MQTT_CLIENT_ID - MQTT Client ID + This configuration option specifies the MQTT Client ID. + +.. _CONFIG_MQTT_SUB_TOPIC: + +CONFIG_MQTT_SUB_TOPIC - MQTT Subscribe topic + This configuration option sets the MQTT Subscribe topic. + +.. _CONFIG_MQTT_PUB_TOPIC: + +CONFIG_MQTT_PUB_TOPIC - MQTT Publish topic + This configuration option sets the MQTT Publish topic. + +.. _CONFIG_MQTT_BROKER_PORT: + +CONFIG_MQTT_BROKER_PORT - MQTT Broker Port + This configuration option specifies the port number associated with the MQTT broker. + +.. _CONFIG_BUTTON_EVENT_PUBLISH_MSG: + +CONFIG_BUTTON_EVENT_PUBLISH_MSG - Button event publish message + This configuration option specifies the message text which is published on a button press. + +.. _CONFIG_BUTTON_EVENT_BTN_NUM: + +CONFIG_BUTTON_EVENT_BTN_NUM - Button number for publish + This configuration option specifies the button number which, when pressed, will publish an MQTT message. + +.. _CONFIG_MQTT_RECONNECT_DELAY_S: + +CONFIG_MQTT_RECONNECT_DELAY_S - MQTT broker reconnect delay + This configuration option specifies the delay (in seconds) before attempting to reconnect to the broker. + +.. _CONFIG_LTE_CONNECT_RETRY_DELAY_S: + +CONFIG_LTE_CONNECT_RETRY_DELAY_S - LTE connection retry delay + This configuration option specifies delay (in seconds) before attempting to retry LTE connection. + +Configuration files +===================== + +The sample provides the following predefined configuration files for the following development kits: + +* :file:`prj.conf` - For nRF9160 DK +* :file:`prj_qemu_x86.conf` - For x86 Emulation (QEMU) + +In addition, the sample provides overlay configuration files, which are used to enable additional features in the sample: + +* :file:`overlay-tls.conf` - TLS overlay configuration file for nRF9160 DK +* :file:`overlay-qemu-x86-tls.conf` - TLS overlay configuration file for x86 Emulation (QEMU) + +They are located in :file:`samples/nrf9160/mqtt_simple` folder. + + +To add a specific overlay configuration file to the build, add the ``-- -DOVERLAY_CONFIG=<overlay_config_file>`` flag to your build. + +See :ref:`cmake_options` for instructions on how to add this option to your build. +For example, when building on the command line, you can build the sample with the TLS configuration for nRF9160 DK as follows: + + .. code-block:: console + + west build -b nrf9160dk_nrf9160_ns -- -DOVERLAY_CONFIG=overlay-tls.conf + +.. note:: + The CA certificate for the default MQTT broker is included in the project and automatically provisioned after boot if the sample is built with the TLS configuration. + +Building and running +******************** + +.. |sample path| replace:: :file:`samples/nrf9160/mqtt_simple` + +.. include:: /includes/build_and_run_ns.txt + +Testing +======= + +|test_sample| + +1. |connect_kit| +#. |connect_terminal| +#. Reset your nRF9160 DK. +#. Observe that the sample displays the following information in the terminal:: + + The MQTT simple sample started +#. Observe that the development kit connects to the configured MQTT broker (:ref:`CONFIG_MQTT_BROKER_HOSTNAME <CONFIG_MQTT_BROKER_HOSTNAME>`) after it gets the LTE connection. + At this stage, the development kit is ready to echo the data sent to it on the configured subscribe topic (:ref:`CONFIG_MQTT_SUB_TOPIC <CONFIG_MQTT_SUB_TOPIC>`). +#. Use an MQTT client like `Mosquitto`_ to subscribe to and publish data to the broker. + Observe that the development kit publishes all the data that you publish to :ref:`CONFIG_MQTT_SUB_TOPIC <CONFIG_MQTT_SUB_TOPIC>` on :ref:`CONFIG_MQTT_PUB_TOPIC <CONFIG_MQTT_PUB_TOPIC>`. + +Sample output +============= + +The following serial UART output is displayed in the terminal emulator: + +.. code-block:: console + + *** Booting Zephyr OS build v2.4.0-ncs1-rc1-6-g45f2d5cf8ea4 *** + <inf> mqtt_simple: The MQTT simple sample started + <inf> mqtt_simple: LTE Link Connecting... + <inf> mqtt_simple: LTE Link Connected! + <inf> mqtt_simple: Disabling PSM and eDRX + <inf> mqtt_simple: IPv4 Address found 137.135.83.217 + <inf> mqtt_simple: MQTT client connected + <inf> mqtt_simple: Subscribing to: my/subscribe/topic len 18 + <inf> mqtt_simple: SUBACK packet id: 1234 + <inf> mqtt_simple: Publishing: Hello from nRF91 MQTT Simple Sample + <inf> mqtt_simple: to topic: my/publish/topic len: 16 + <inf> mqtt_simple: PUBACK packet id: 51700 + + + +Troubleshooting +=============== + +Public MQTT brokers might be unstable. +If you experience problems connecting to the MQTT broker, try switching to another MQTT broker by changing the value of the :ref:`CONFIG_MQTT_BROKER_HOSTNAME <CONFIG_MQTT_BROKER_HOSTNAME>` configuration option. + +.. note:: + If the :ref:`CONFIG_MQTT_BROKER_HOSTNAME <CONFIG_MQTT_BROKER_HOSTNAME>` configuration option is changed and the overlay TLS configuration is used, the included CA certificate must be updated with the CA certificate for + the newly configurated MQTT broker. + + +Dependencies +************ + +This sample uses the following |NCS| library: + +* :ref:`lte_lc_readme` + +It uses the following `sdk-nrfxlib`_ library: + +* :ref:`nrfxlib:nrf_modem` + +It uses the following Zephyr library: + +* :ref:`MQTT <zephyr:networking_api>` +* :ref:`zephyr:shell_api` + +In addition, it uses the following secure firmware component: + +* :ref:`Trusted Firmware-M <ug_tfm>` diff --git a/overlay-qemu-x86-tls.conf b/overlay-qemu-x86-tls.conf new file mode 100644 index 0000000..1e17556 --- /dev/null +++ b/overlay-qemu-x86-tls.conf @@ -0,0 +1,22 @@ +# +# Copyright (c) 2020 Nordic Semiconductor ASA +# +# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause +# + +# MQTT TLS +CONFIG_MQTT_LIB_TLS=y +CONFIG_MQTT_BROKER_PORT=8883 + +# MBED TLS +CONFIG_MBEDTLS=y +CONFIG_MBEDTLS_BUILTIN=y +CONFIG_MBEDTLS_ENABLE_HEAP=y +CONFIG_MBEDTLS_HEAP_SIZE=120000 +CONFIG_MBEDTLS_SSL_MAX_CONTENT_LEN=16384 +CONFIG_MBEDTLS_PEM_CERTIFICATE_FORMAT=y +CONFIG_MBEDTLS_KEY_EXCHANGE_RSA_ENABLED=y +CONFIG_MBEDTLS_MAC_SHA256_ENABLED=y + +# NET SOCKETS +CONFIG_NET_SOCKETS_SOCKOPT_TLS=y diff --git a/overlay-tls.conf b/overlay-tls.conf new file mode 100644 index 0000000..9a58e44 --- /dev/null +++ b/overlay-tls.conf @@ -0,0 +1,13 @@ +# +# Copyright (c) 2020 Nordic Semiconductor ASA +# +# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause +# + +# MQTT TLS +CONFIG_MQTT_LIB_TLS=y +CONFIG_MQTT_BROKER_PORT=8886 +CONFIG_MQTT_KEEPALIVE=30 + +# Modem key management, for provisioning certificates +CONFIG_MODEM_KEY_MGMT=y diff --git a/prj.conf b/prj.conf new file mode 100644 index 0000000..d992cc6 --- /dev/null +++ b/prj.conf @@ -0,0 +1,49 @@ +# +# Copyright (c) 2020 Nordic Semiconductor ASA +# +# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause +# + +# Networking +CONFIG_NETWORKING=y +CONFIG_NET_NATIVE=n +CONFIG_NET_SOCKETS_OFFLOAD=y +CONFIG_NET_SOCKETS=y +CONFIG_NET_SOCKETS_POSIX_NAMES=y + +# LTE link control +CONFIG_LTE_LINK_CONTROL=y +CONFIG_LTE_AUTO_INIT_AND_CONNECT=n + +# Modem library +CONFIG_NRF_MODEM_LIB=y + +# MQTT +CONFIG_MQTT_LIB=y +CONFIG_MQTT_LIB_TLS=n +CONFIG_MQTT_CLEAN_SESSION=y + +# Application +# CONFIG_MQTT_PUB_TOPIC="my/publish/topic" +# CONFIG_MQTT_SUB_TOPIC="my/subscribe/topic" +# CONFIG_MQTT_CLIENT_ID="my-client-id" +# CONFIG_MQTT_BROKER_HOSTNAME="test.mosquitto.org" +# CONFIG_MQTT_BROKER_PORT=1883 + +# Button support +CONFIG_DK_LIBRARY=y + +# Enable logging +CONFIG_LOG=y +CONFIG_MQTT_SIMPLE_LOG_LEVEL_DBG=y + +# Memory +CONFIG_MAIN_STACK_SIZE=4096 +CONFIG_HEAP_MEM_POOL_SIZE=2048 + +# NewLib C +CONFIG_NEWLIB_LIBC=y + +# Shell support +CONFIG_SHELL=y +CONFIG_HW_ID_LIBRARY=y diff --git a/prj_qemu_x86.conf b/prj_qemu_x86.conf new file mode 100644 index 0000000..cf5863e --- /dev/null +++ b/prj_qemu_x86.conf @@ -0,0 +1,43 @@ +# +# Copyright (c) 2020 Nordic Semiconductor ASA +# +# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause +# + +# General config +CONFIG_TEST_RANDOM_GENERATOR=y +CONFIG_NEWLIB_LIBC=y +CONFIG_LOG=y + +# MQTT +CONFIG_MQTT_LIB=y +CONFIG_MQTT_LIB_TLS=n + +# Networking config +CONFIG_NETWORKING=y +CONFIG_NET_IPV4=y +CONFIG_NET_TCP=y +CONFIG_NET_SOCKETS=y +CONFIG_NET_SOCKETS_POSIX_NAMES=y + +CONFIG_DNS_RESOLVER=y +CONFIG_DNS_SERVER_IP_ADDRESSES=y +CONFIG_DNS_SERVER1="8.8.8.8" + +# Network address config +CONFIG_NET_CONFIG_SETTINGS=y +CONFIG_NET_CONFIG_NEED_IPV4=y +CONFIG_NET_CONFIG_MY_IPV4_ADDR="192.0.2.1" +CONFIG_NET_CONFIG_PEER_IPV4_ADDR="192.0.2.2" +CONFIG_NET_CONFIG_MY_IPV4_GW="192.0.2.2" + +# Application +#CONFIG_MQTT_PUB_TOPIC="/my/publish/topic" +#CONFIG_MQTT_SUB_TOPIC="/my/subscribe/topic" +#CONFIG_MQTT_CLIENT_ID="my-client-id" +#CONFIG_MQTT_BROKER_HOSTNAME="mqtt.eclipseprojects.io" +#CONFIG_MQTT_BROKER_PORT=1883 + +# Memory +CONFIG_HEAP_MEM_POOL_SIZE=16384 +CONFIG_MAIN_STACK_SIZE=16384 diff --git a/sample.yaml b/sample.yaml new file mode 100644 index 0000000..1f8a578 --- /dev/null +++ b/sample.yaml @@ -0,0 +1,9 @@ +sample: + name: mqtt simple sample +tests: + sample.nrf9160.mqtt_simple: + build_only: true + integration_platforms: + - nrf9160dk_nrf9160_ns + platform_allow: nrf9160dk_nrf9160_ns + tags: ci_build diff --git a/src/certificates.h b/src/certificates.h new file mode 100644 index 0000000..aebb1e8 --- /dev/null +++ b/src/certificates.h @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2020 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause + */ + +/** CA certificate for test.mosquitto.org, Let’s Encrypt R3 */ +#define CA_CERTIFICATE \ + "-----BEGIN CERTIFICATE-----\n" \ + "MIIFFjCCAv6gAwIBAgIRAJErCErPDBinU/bWLiWnX1owDQYJKoZIhvcNAQELBQAw\n" \ + "TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh\n" \ + "cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMjAwOTA0MDAwMDAw\n" \ + "WhcNMjUwOTE1MTYwMDAwWjAyMQswCQYDVQQGEwJVUzEWMBQGA1UEChMNTGV0J3Mg\n" \ + "RW5jcnlwdDELMAkGA1UEAxMCUjMwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK\n" \ + "AoIBAQC7AhUozPaglNMPEuyNVZLD+ILxmaZ6QoinXSaqtSu5xUyxr45r+XXIo9cP\n" \ + "R5QUVTVXjJ6oojkZ9YI8QqlObvU7wy7bjcCwXPNZOOftz2nwWgsbvsCUJCWH+jdx\n" \ + "sxPnHKzhm+/b5DtFUkWWqcFTzjTIUu61ru2P3mBw4qVUq7ZtDpelQDRrK9O8Zutm\n" \ + "NHz6a4uPVymZ+DAXXbpyb/uBxa3Shlg9F8fnCbvxK/eG3MHacV3URuPMrSXBiLxg\n" \ + "Z3Vms/EY96Jc5lP/Ooi2R6X/ExjqmAl3P51T+c8B5fWmcBcUr2Ok/5mzk53cU6cG\n" \ + "/kiFHaFpriV1uxPMUgP17VGhi9sVAgMBAAGjggEIMIIBBDAOBgNVHQ8BAf8EBAMC\n" \ + "AYYwHQYDVR0lBBYwFAYIKwYBBQUHAwIGCCsGAQUFBwMBMBIGA1UdEwEB/wQIMAYB\n" \ + "Af8CAQAwHQYDVR0OBBYEFBQusxe3WFbLrlAJQOYfr52LFMLGMB8GA1UdIwQYMBaA\n" \ + "FHm0WeZ7tuXkAXOACIjIGlj26ZtuMDIGCCsGAQUFBwEBBCYwJDAiBggrBgEFBQcw\n" \ + "AoYWaHR0cDovL3gxLmkubGVuY3Iub3JnLzAnBgNVHR8EIDAeMBygGqAYhhZodHRw\n" \ + "Oi8veDEuYy5sZW5jci5vcmcvMCIGA1UdIAQbMBkwCAYGZ4EMAQIBMA0GCysGAQQB\n" \ + "gt8TAQEBMA0GCSqGSIb3DQEBCwUAA4ICAQCFyk5HPqP3hUSFvNVneLKYY611TR6W\n" \ + "PTNlclQtgaDqw+34IL9fzLdwALduO/ZelN7kIJ+m74uyA+eitRY8kc607TkC53wl\n" \ + "ikfmZW4/RvTZ8M6UK+5UzhK8jCdLuMGYL6KvzXGRSgi3yLgjewQtCPkIVz6D2QQz\n" \ + "CkcheAmCJ8MqyJu5zlzyZMjAvnnAT45tRAxekrsu94sQ4egdRCnbWSDtY7kh+BIm\n" \ + "lJNXoB1lBMEKIq4QDUOXoRgffuDghje1WrG9ML+Hbisq/yFOGwXD9RiX8F6sw6W4\n" \ + "avAuvDszue5L3sz85K+EC4Y/wFVDNvZo4TYXao6Z0f+lQKc0t8DQYzk1OXVu8rp2\n" \ + "yJMC6alLbBfODALZvYH7n7do1AZls4I9d1P4jnkDrQoxB3UqQ9hVl3LEKQ73xF1O\n" \ + "yK5GhDDX8oVfGKF5u+decIsH4YaTw7mP3GFxJSqv3+0lUFJoi5Lc5da149p90Ids\n" \ + "hCExroL1+7mryIkXPeFM5TgO9r0rvZaBFOvV2z0gp35Z0+L4WPlbuEjN/lxPFin+\n" \ + "HlUjr8gRsI3qfJOQFy/9rKIJR0Y/8Omwt/8oTWgy1mdeHmmjk7j1nYsvC9JSQ6Zv\n" \ + "MldlTTKB3zhThV1+XWYp6rjd5JW1zbVWEkLNxE7GJThEUG3szgBVGP7pSWTUTsqX\n" \ + "nLRbwHOoq7hHwg==\n" \ + "-----END CERTIFICATE-----" diff --git a/src/main.c b/src/main.c new file mode 100644 index 0000000..570c01d --- /dev/null +++ b/src/main.c @@ -0,0 +1,603 @@ +/* + * Copyright (c) 2018 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause + */ + +#include <zephyr/kernel.h> +#include <stdio.h> +#include <zephyr/drivers/uart.h> +#include <string.h> +#include <zephyr/random/rand32.h> +#include <zephyr/net/mqtt.h> +#include <zephyr/net/socket.h> +#include <zephyr/shell/shell.h> +#include <hw_id.h> +#if defined(CONFIG_NRF_MODEM_LIB) +#include <nrf_modem_at.h> +#endif /* CONFIG_NRF_MODEM_LIB */ +#include <modem/lte_lc.h> +#include <zephyr/logging/log.h> +#if defined(CONFIG_MODEM_KEY_MGMT) +#include <modem/modem_key_mgmt.h> +#endif +#include <dk_buttons_and_leds.h> + +#include "certificates.h" + +LOG_MODULE_REGISTER(mqtt_simple, CONFIG_MQTT_SIMPLE_LOG_LEVEL); + +/* Buffers for MQTT client. */ +static uint8_t rx_buffer[CONFIG_MQTT_MESSAGE_BUFFER_SIZE]; +static uint8_t tx_buffer[CONFIG_MQTT_MESSAGE_BUFFER_SIZE]; +static uint8_t payload_buf[CONFIG_MQTT_PAYLOAD_BUFFER_SIZE]; + +/* The mqtt client struct */ +static struct mqtt_client client; + +/* MQTT Broker details. */ +static struct sockaddr_storage broker; + +/* File descriptor */ +static struct pollfd fds; + +#if defined(CONFIG_MQTT_LIB_TLS) +static int certificates_provision(void) +{ + int err = 0; + + LOG_INF("Provisioning certificates"); + +#if defined(CONFIG_NRF_MODEM_LIB) && defined(CONFIG_MODEM_KEY_MGMT) + + err = modem_key_mgmt_write(CONFIG_MQTT_TLS_SEC_TAG, + MODEM_KEY_MGMT_CRED_TYPE_CA_CHAIN, + CA_CERTIFICATE, + strlen(CA_CERTIFICATE)); + if (err) { + LOG_ERR("Failed to provision CA certificate: %d", err); + return err; + } + +#elif defined(CONFIG_BOARD_QEMU_X86) && defined(CONFIG_NET_SOCKETS_SOCKOPT_TLS) + + err = tls_credential_add(CONFIG_MQTT_TLS_SEC_TAG, + TLS_CREDENTIAL_CA_CERTIFICATE, + CA_CERTIFICATE, + sizeof(CA_CERTIFICATE)); + if (err) { + LOG_ERR("Failed to register CA certificate: %d", err); + return err; + } + +#endif + + return err; +} +#endif /* defined(CONFIG_MQTT_LIB_TLS) */ + +/**@brief Function to print strings without null-termination + */ +static void data_print(uint8_t *prefix, uint8_t *data, size_t len) +{ + char buf[len + 1]; + + memcpy(buf, data, len); + buf[len] = 0; + LOG_INF("%s%s", (char *)prefix, (char *)buf); +} + +/**@brief Function to publish data on the configured topic + */ +static int data_publish(struct mqtt_client *c, enum mqtt_qos qos, + uint8_t *data, size_t len) +{ + struct mqtt_publish_param param; + + param.message.topic.qos = qos; + param.message.topic.topic.utf8 = CONFIG_MQTT_PUB_TOPIC; + param.message.topic.topic.size = strlen(CONFIG_MQTT_PUB_TOPIC); + param.message.payload.data = data; + param.message.payload.len = len; + param.message_id = sys_rand32_get(); + param.dup_flag = 0; + param.retain_flag = 0; + + data_print("Publishing: ", data, len); + LOG_INF("to topic: %s len: %u", + CONFIG_MQTT_PUB_TOPIC, + (unsigned int)strlen(CONFIG_MQTT_PUB_TOPIC)); + + return mqtt_publish(c, ¶m); +} + +/**@brief Function to subscribe to the configured topic + */ +static int subscribe(void) +{ + struct mqtt_topic subscribe_topic = { + .topic = { + .utf8 = CONFIG_MQTT_SUB_TOPIC, + .size = strlen(CONFIG_MQTT_SUB_TOPIC) + }, + .qos = MQTT_QOS_1_AT_LEAST_ONCE + }; + + const struct mqtt_subscription_list subscription_list = { + .list = &subscribe_topic, + .list_count = 1, + .message_id = 1234 + }; + + LOG_INF("Subscribing to: %s len %u", CONFIG_MQTT_SUB_TOPIC, + (unsigned int)strlen(CONFIG_MQTT_SUB_TOPIC)); + + return mqtt_subscribe(&client, &subscription_list); +} + +/**@brief Function to read the published payload. + */ +static int publish_get_payload(struct mqtt_client *c, size_t length) +{ + int ret; + int err = 0; + + /* Return an error if the payload is larger than the payload buffer. + * Note: To allow new messages, we have to read the payload before returning. + */ + if (length > sizeof(payload_buf)) { + err = -EMSGSIZE; + } + + /* Truncate payload until it fits in the payload buffer. */ + while (length > sizeof(payload_buf)) { + ret = mqtt_read_publish_payload_blocking( + c, payload_buf, (length - sizeof(payload_buf))); + if (ret == 0) { + return -EIO; + } else if (ret < 0) { + return ret; + } + + length -= ret; + } + + ret = mqtt_readall_publish_payload(c, payload_buf, length); + if (ret) { + return ret; + } + + return err; +} + +/**@brief MQTT client event handler + */ +void mqtt_evt_handler(struct mqtt_client *const c, + const struct mqtt_evt *evt) +{ + int err; + + switch (evt->type) { + case MQTT_EVT_CONNACK: + if (evt->result != 0) { + LOG_ERR("MQTT connect failed: %d", evt->result); + break; + } + + LOG_INF("MQTT client connected"); + subscribe(); + break; + + case MQTT_EVT_DISCONNECT: + LOG_INF("MQTT client disconnected: %d", evt->result); + break; + + case MQTT_EVT_PUBLISH: { + const struct mqtt_publish_param *p = &evt->param.publish; + + LOG_INF("MQTT PUBLISH result=%d len=%d", + evt->result, p->message.payload.len); + err = publish_get_payload(c, p->message.payload.len); + + if (p->message.topic.qos == MQTT_QOS_1_AT_LEAST_ONCE) { + const struct mqtt_puback_param ack = { + .message_id = p->message_id + }; + + /* Send acknowledgment. */ + mqtt_publish_qos1_ack(&client, &ack); + } + + if (err >= 0) { + data_print("Received: ", payload_buf, + p->message.payload.len); + /* Echo back received data */ + data_publish(&client, MQTT_QOS_1_AT_LEAST_ONCE, + payload_buf, p->message.payload.len); + } else if (err == -EMSGSIZE) { + LOG_ERR("Received payload (%d bytes) is larger than the payload buffer " + "size (%d bytes).", + p->message.payload.len, sizeof(payload_buf)); + } else { + LOG_ERR("publish_get_payload failed: %d", err); + LOG_INF("Disconnecting MQTT client..."); + + err = mqtt_disconnect(c); + if (err) { + LOG_ERR("Could not disconnect: %d", err); + } + } + } break; + + case MQTT_EVT_PUBACK: + if (evt->result != 0) { + LOG_ERR("MQTT PUBACK error: %d", evt->result); + break; + } + + LOG_INF("PUBACK packet id: %u", evt->param.puback.message_id); + break; + + case MQTT_EVT_SUBACK: + if (evt->result != 0) { + LOG_ERR("MQTT SUBACK error: %d", evt->result); + break; + } + + LOG_INF("SUBACK packet id: %u", evt->param.suback.message_id); + break; + + case MQTT_EVT_PINGRESP: + if (evt->result != 0) { + LOG_ERR("MQTT PINGRESP error: %d", evt->result); + } + break; + + default: + LOG_INF("Unhandled MQTT event type: %d", evt->type); + break; + } +} + +/**@brief Resolves the configured hostname and + * initializes the MQTT broker structure + */ +static int broker_init(void) +{ + int err; + struct addrinfo *result; + struct addrinfo *addr; + struct addrinfo hints = { + .ai_family = AF_INET, + .ai_socktype = SOCK_STREAM + }; + + err = getaddrinfo(CONFIG_MQTT_BROKER_HOSTNAME, NULL, &hints, &result); + if (err) { + LOG_ERR("getaddrinfo failed: %d", err); + return -ECHILD; + } + + addr = result; + + /* Look for address of the broker. */ + while (addr != NULL) { + /* IPv4 Address. */ + if (addr->ai_addrlen == sizeof(struct sockaddr_in)) { + struct sockaddr_in *broker4 = + ((struct sockaddr_in *)&broker); + char ipv4_addr[NET_IPV4_ADDR_LEN]; + + broker4->sin_addr.s_addr = + ((struct sockaddr_in *)addr->ai_addr) + ->sin_addr.s_addr; + broker4->sin_family = AF_INET; + broker4->sin_port = htons(CONFIG_MQTT_BROKER_PORT); + + inet_ntop(AF_INET, &broker4->sin_addr.s_addr, + ipv4_addr, sizeof(ipv4_addr)); + LOG_INF("IPv4 Address found %s", ipv4_addr); + + break; + } else { + LOG_ERR("ai_addrlen = %u should be %u or %u", + (unsigned int)addr->ai_addrlen, + (unsigned int)sizeof(struct sockaddr_in), + (unsigned int)sizeof(struct sockaddr_in6)); + } + + addr = addr->ai_next; + } + + /* Free the address. */ + freeaddrinfo(result); + + return err; +} + +#define RANDOM_LEN 10 +#define CLIENT_ID_LEN sizeof(CONFIG_BOARD) + 1 + RANDOM_LEN + +/* Function to get the client id */ +static const uint8_t* client_id_get(void) +{ + static uint8_t client_id[MAX(sizeof(CONFIG_MQTT_CLIENT_ID), + CLIENT_ID_LEN)]; + + if (strlen(CONFIG_MQTT_CLIENT_ID) > 0) { + snprintf(client_id, sizeof(client_id), "%s", + CONFIG_MQTT_CLIENT_ID); + goto exit; + } + + char hw_id_buf[HW_ID_LEN] = {0}; + + int err = hw_id_get(hw_id_buf, ARRAY_SIZE(hw_id_buf)); + + if (!err) { + snprintf(client_id, sizeof(client_id), "nrf-%s", + hw_id_buf); + goto exit; + } + + LOG_ERR("failed to retrieve HW ID, err: %d", err); + + uint32_t id = sys_rand32_get(); + snprintf(client_id, sizeof(client_id), "%s-%010u", CONFIG_BOARD, id); + +exit: + LOG_DBG("client_id = %s", (char *)client_id); + + return client_id; +} + +/**@brief Initialize the MQTT client structure + */ +static int client_init(struct mqtt_client *client) +{ + int err; + + mqtt_client_init(client); + + err = broker_init(); + if (err) { + LOG_ERR("Failed to initialize broker connection"); + return err; + } + + /* MQTT client configuration */ + client->broker = &broker; + client->evt_cb = mqtt_evt_handler; + client->client_id.utf8 = client_id_get(); + client->client_id.size = strlen(client->client_id.utf8); + client->password = NULL; + client->user_name = NULL; + client->protocol_version = MQTT_VERSION_3_1_1; + + /* MQTT buffers configuration */ + client->rx_buf = rx_buffer; + client->rx_buf_size = sizeof(rx_buffer); + client->tx_buf = tx_buffer; + client->tx_buf_size = sizeof(tx_buffer); + + /* MQTT transport configuration */ +#if defined(CONFIG_MQTT_LIB_TLS) + struct mqtt_sec_config *tls_cfg = &(client->transport).tls.config; + static sec_tag_t sec_tag_list[] = { CONFIG_MQTT_TLS_SEC_TAG }; + + LOG_INF("TLS enabled"); + client->transport.type = MQTT_TRANSPORT_SECURE; + + tls_cfg->peer_verify = CONFIG_MQTT_TLS_PEER_VERIFY; + tls_cfg->cipher_count = 0; + tls_cfg->cipher_list = NULL; + tls_cfg->sec_tag_count = ARRAY_SIZE(sec_tag_list); + tls_cfg->sec_tag_list = sec_tag_list; + tls_cfg->hostname = CONFIG_MQTT_BROKER_HOSTNAME; + +#if defined(CONFIG_NRF_MODEM_LIB) + tls_cfg->session_cache = IS_ENABLED(CONFIG_MQTT_TLS_SESSION_CACHING) ? + TLS_SESSION_CACHE_ENABLED : + TLS_SESSION_CACHE_DISABLED; +#else + /* TLS session caching is not supported by the Zephyr network stack */ + tls_cfg->session_cache = TLS_SESSION_CACHE_DISABLED; + +#endif + +#else + client->transport.type = MQTT_TRANSPORT_NON_SECURE; +#endif + + return err; +} + +/**@brief Initialize the file descriptor structure used by poll. + */ +static int fds_init(struct mqtt_client *c) +{ + if (c->transport.type == MQTT_TRANSPORT_NON_SECURE) { + fds.fd = c->transport.tcp.sock; + } else { +#if defined(CONFIG_MQTT_LIB_TLS) + fds.fd = c->transport.tls.sock; +#else + return -ENOTSUP; +#endif + } + + fds.events = POLLIN; + + return 0; +} + +#if defined(CONFIG_DK_LIBRARY) +static void button_handler(uint32_t button_states, uint32_t has_changed) +{ + if (has_changed & button_states & + BIT(CONFIG_BUTTON_EVENT_BTN_NUM - 1)) { + int ret; + + ret = data_publish(&client, + MQTT_QOS_1_AT_LEAST_ONCE, + CONFIG_BUTTON_EVENT_PUBLISH_MSG, + sizeof(CONFIG_BUTTON_EVENT_PUBLISH_MSG)-1); + if (ret) { + LOG_ERR("Publish failed: %d", ret); + } + } +} +#endif + +static int shell_mqtt_publish(const struct shell *shell, size_t argc, char **argv) +{ + ARG_UNUSED(argc); + ARG_UNUSED(argv); + + int ret; + + ret = data_publish(&client, + MQTT_QOS_1_AT_LEAST_ONCE, + CONFIG_BUTTON_EVENT_PUBLISH_MSG, + sizeof(CONFIG_BUTTON_EVENT_PUBLISH_MSG) - 1); + if (ret) { + LOG_ERR("Publish failed: %d", ret); + } + + return ret; +} + +SHELL_STATIC_SUBCMD_SET_CREATE(mqtt_sub, + SHELL_CMD(publish, NULL, "Publish data to configured publish topic", + shell_mqtt_publish), + SHELL_SUBCMD_SET_END /* Array terminated. */ +); +SHELL_CMD_REGISTER(mqtt, &mqtt_sub, "MQTT operations", NULL); + +/**@brief Configures modem to provide LTE link. Blocks until link is + * successfully established. + */ +static int modem_configure(void) +{ +#if defined(CONFIG_LTE_LINK_CONTROL) + /* Turn off LTE power saving features for a more responsive demo. Also, + * request power saving features before network registration. Some + * networks rejects timer updates after the device has registered to the + * LTE network. + */ + LOG_INF("Disabling PSM and eDRX"); + lte_lc_psm_req(false); + lte_lc_edrx_req(false); + + if (IS_ENABLED(CONFIG_LTE_AUTO_INIT_AND_CONNECT)) { + /* Do nothing, modem is already turned on + * and connected. + */ + } else { + int err; + + LOG_INF("LTE Link Connecting..."); + err = lte_lc_init_and_connect(); + if (err) { + LOG_INF("Failed to establish LTE connection: %d", err); + return err; + } + LOG_INF("LTE Link Connected!"); + } +#endif /* defined(CONFIG_LTE_LINK_CONTROL) */ + + return 0; +} + +void main(void) +{ + int err; + uint32_t connect_attempt = 0; + + LOG_INF("The MQTT simple sample started"); + +#if defined(CONFIG_MQTT_LIB_TLS) + err = certificates_provision(); + if (err != 0) { + LOG_ERR("Failed to provision certificates"); + return; + } +#endif /* defined(CONFIG_MQTT_LIB_TLS) */ + + do { + err = modem_configure(); + if (err) { + LOG_INF("Retrying in %d seconds", + CONFIG_LTE_CONNECT_RETRY_DELAY_S); + k_sleep(K_SECONDS(CONFIG_LTE_CONNECT_RETRY_DELAY_S)); + } + } while (err); + + err = client_init(&client); + if (err != 0) { + LOG_ERR("client_init: %d", err); + return; + } + +#if defined(CONFIG_DK_LIBRARY) + dk_buttons_init(button_handler); +#endif + +do_connect: + if (connect_attempt++ > 0) { + LOG_INF("Reconnecting in %d seconds...", + CONFIG_MQTT_RECONNECT_DELAY_S); + k_sleep(K_SECONDS(CONFIG_MQTT_RECONNECT_DELAY_S)); + } + err = mqtt_connect(&client); + if (err != 0) { + LOG_ERR("mqtt_connect %d", err); + goto do_connect; + } + + err = fds_init(&client); + if (err != 0) { + LOG_ERR("fds_init: %d", err); + return; + } + + while (1) { + err = poll(&fds, 1, mqtt_keepalive_time_left(&client)); + if (err < 0) { + LOG_ERR("poll: %d", errno); + break; + } + + err = mqtt_live(&client); + if ((err != 0) && (err != -EAGAIN)) { + LOG_ERR("ERROR: mqtt_live: %d", err); + break; + } + + if ((fds.revents & POLLIN) == POLLIN) { + err = mqtt_input(&client); + if (err != 0) { + LOG_ERR("mqtt_input: %d", err); + break; + } + } + + if ((fds.revents & POLLERR) == POLLERR) { + LOG_ERR("POLLERR"); + break; + } + + if ((fds.revents & POLLNVAL) == POLLNVAL) { + LOG_ERR("POLLNVAL"); + break; + } + } + + LOG_INF("Disconnecting MQTT client..."); + + err = mqtt_disconnect(&client); + if (err) { + LOG_ERR("Could not disconnect MQTT client: %d", err); + } + goto do_connect; +} diff --git a/west.yml b/west.yml new file mode 100644 index 0000000..7fd440b --- /dev/null +++ b/west.yml @@ -0,0 +1,13 @@ +manifest: + remotes: + - name: ncs + url-base: https://github.com/nrfconnect + projects: + - name: sdk-nrf + remote: ncs + path: nrf + revision: v2.2.0 + import: true + clone-depth: 1 + self: + path: mqtt_simple |
