diff --git a/samples/net/secure_mqtt_sensor_actuator/CMakeLists.txt b/samples/net/secure_mqtt_sensor_actuator/CMakeLists.txt
new file mode 100644
index 00000000000..af4feece2dc
--- /dev/null
+++ b/samples/net/secure_mqtt_sensor_actuator/CMakeLists.txt
@@ -0,0 +1,11 @@
+# SPDX-License-Identifier: Apache-2.0
+
+cmake_minimum_required(VERSION 3.20.0)
+
+find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
+project(secure_mqtt_sensor_actuator)
+
+FILE(GLOB app_sources src/*.c)
+target_sources(app PRIVATE ${app_sources})
+
+zephyr_include_directories(${APPLICATION_SOURCE_DIR}/src/tls_config)
diff --git a/samples/net/secure_mqtt_sensor_actuator/Kconfig b/samples/net/secure_mqtt_sensor_actuator/Kconfig
new file mode 100644
index 00000000000..19d15187acf
--- /dev/null
+++ b/samples/net/secure_mqtt_sensor_actuator/Kconfig
@@ -0,0 +1,58 @@
+# Copyright (c) 2024 Analog Devices, Inc.
+# SPDX-License-Identifier: Apache-2.0
+
+mainmenu "Secure MQTT Sensor Actuator Sample Application"
+
+config NET_SAMPLE_MQTT_BROKER_HOSTNAME
+ string "Hostname of MQTT broker"
+ default "test.mosquitto.org"
+ help
+ MQTT broker's hostname or IP address.
+
+config NET_SAMPLE_MQTT_BROKER_PORT
+ string "MQTT Broker Connection Port"
+ default "8883"
+ help
+ Port through which the application should connect to the MQTT broker.
+ Secure MQTT uses port 8883.
+
+config NET_SAMPLE_MQTT_PUB_TOPIC
+ string "The MQTT topic the application should publish data to"
+ default "zephyr_sample/sensor"
+
+config NET_SAMPLE_MQTT_SUB_TOPIC_CMD
+ string "The MQTT topic the application will receive commands on"
+ default "zephyr_sample/command"
+
+config NET_SAMPLE_MQTT_PUBLISH_INTERVAL
+ int "Interval between MQTT publishes (in seconds)"
+ default 3
+ help
+ This config determines the frequency at which MQTT publishes occur.
+
+choice NET_SAMPLE_MQTT_QOS
+ prompt "Quality of Service level used for MQTT publish and subscribe"
+ default NET_SAMPLE_MQTT_QOS_1_AT_LEAST_ONCE
+
+config NET_SAMPLE_MQTT_QOS_0_AT_MOST_ONCE
+ bool "QoS 0 / At most once delivery"
+ help
+ No acknowledgment needed for published message.
+
+config NET_SAMPLE_MQTT_QOS_1_AT_LEAST_ONCE
+ bool "QoS 1 / At least once delivery"
+ help
+ If acknowledgment expected for published message, duplicate messages permitted.
+
+config NET_SAMPLE_MQTT_QOS_2_EXACTLY_ONCE
+ bool "QoS 2 / Exactly once delivery"
+ help
+ Acknowledgment expected and message shall be published only once.
+
+endchoice
+
+config NET_SAMPLE_MQTT_PAYLOAD_SIZE
+ int "Size of MQTT payload in bytes"
+ default 128
+
+source "Kconfig.zephyr"
diff --git a/samples/net/secure_mqtt_sensor_actuator/README.rst b/samples/net/secure_mqtt_sensor_actuator/README.rst
new file mode 100644
index 00000000000..b2ff6a1573c
--- /dev/null
+++ b/samples/net/secure_mqtt_sensor_actuator/README.rst
@@ -0,0 +1,219 @@
+.. zephyr:code-sample:: secure-mqtt-sensor-actuator
+ :name: Secure MQTT Sensor/Actuator
+ :relevant-api: mqtt_socket sensor_interface
+
+ Implement an MQTT-based IoT sensor/actuator device
+
+Overview
+********
+
+This sample demonstrates the implementation of an IoT sensor/actuator device.
+The application uses the MQTT protocol to securely send sensor data
+to a remote MQTT broker, while responding to commands received over MQTT.
+
+Features:
+
+- Establishing network connectivity using a DHCPv4 lease
+- Establishing a secure MQTT connection (using TLS 1.2) to MQTT broker
+- Publishing temperature sensor data in JSON format to the MQTT broker at a user-defined interval
+- Subscribing to user-defined topic(s) on MQTT broker
+- Responding to commands received over the network (LED control)
+- Handling of MQTT connection, re-connecting and keep-alive
+- Network status LED
+
+Requirements
+************
+- Board with network capability (tested with adi_eval_adin1110ebz)
+- `Eclipse Mosquitto`_ MQTT broker
+- DHCP server
+- Network connection between the board and Mosquitto broker
+
+Build and Running
+*****************
+This application relies on an network connection between the board and an MQTT broker.
+This broker can exist locally (e.g. on a host PC) or a publicly available MQTT broker
+ can be used.
+For quick sampling/testing, a configuration is provided to connect to a local MQTT broker
+without security, using a static IP address.
+
+Hardware Setup
+==============
+If using Ethernet, connect the board to the MQTT broker. This may be your host PC
+(for locally hosted Mosquitto broker) or your internet router
+(to connect to the public Mosquitto broker).
+If required, connect a temperature sensor to the board.
+
+Software Setup
+==============
+The temperature sensor should be aliased in devicetree as ``ambient-temp0``.
+If a board does not include an on-board temperature sensor, one can be connected externally
+and a board overlay file used to add the sensor and alias:
+
+.. code-block:: devicetree
+
+ / {
+ aliases {
+ ambient-temp0 = &adt7420;
+ };
+ };
+ };
+
+It is possible to use other types of sensors, by adding them in devicetree and by changing
+``SENSOR_CHAN`` :file:`in device.c` to match the desired sensor type.
+
+There are a few ways to configure the application:
+
+.. list-table::
+
+ * - :file:`prj.conf`
+ - Default config: Secure MQTT, dynamic IP address (DHCP)
+
+ * - :file:`overlay-static.conf`
+ - Secure MQTT, static IP address
+
+ * - :file:`overlay-static-insecure.conf`
+ - Unsecure MQTT, static IP address
+
+**Default Config:**
+
+Using the default config, the application will use DHCP to acquire an IP address and attempt
+to securely connect to an MQTT broker using TLS 1.2.
+
+- The MQTT broker to which the board will connect is specified by
+ ``CONFIG_NET_SAMPLE_MQTT_BROKER_HOSTNAME``.
+ By default, this is set to test.mosquitto.org.
+- Connecting securely using TLS, requires the inclusion of the broker's CA certificate
+ in the application.
+- Download the CA certificate in DER or PEM format from https://test.mosquitto.org
+- In :file:`tls_config/cert.h`, set ``ca_certificate[]`` to the contents of the cert.
+- By connecting the board to your internet router, it should automatically be assigned
+ an IPv4 address using DHCP.
+- The application will then attempt to connect to the public Mosquitto broker
+ and begin publishing data.
+- It is also possible to connect securely to a locally hosted MQTT broker.
+ This will require provisioning of certificates.
+ The CA cert should be included in the build as described above.
+ ``CONFIG_NET_SAMPLE_MQTT_BROKER_HOSTNAME`` should be configured to match the
+ local broker hostname/IP address.
+ Depending on the CA cert being used, additional MbedTLS config options may need to be enabled.
+ This can be done using Kconfig or using a custom MbedTLS config file
+ (see modules/mbedtls/Kconfig).
+ See https://mosquitto.org/man/mosquitto-tls-7.html for more info on setting up
+ TLS support for Mosquitto locally.
+- A DHCP server can be installed on the host PC to handle assigning an IP to the board
+ e.g. dnsmasq (Linux) or DHCP Server for Windows (Windows).
+- Build the sample with default config:
+
+.. zephyr-app-commands::
+ :zephyr-app: samples/net/secure_mqtt_sensor_actuator
+ :board: adi_eval_adin1110ebz
+ :goals: build
+ :compact:
+
+**Static IP Config:**
+
+Use the :file:`overlay-static.conf` Kconfig overlay to disable DHCP and use
+a static IP address config.
+The device, gateway, and DNS server IP addresses should be set according to
+your local network configuration.
+
+.. zephyr-app-commands::
+ :zephyr-app: samples/net/secure_mqtt_sensor_actuator
+ :board: adi_eval_adin1110ebz
+ :conf: "prj.conf overlay-static.conf"
+ :goals: build
+ :compact:
+
+**Static IP/Unsecure MQTT Config:**
+
+Use the :file:`overlay-static-insecure.conf` Kconfig overlay to disable TLS and DHCP.
+This config requires connecting to a locally hosted Mosquitto MQTT broker.
+
+- In :file:`overlay-static-insecure.conf`, set the IP address of the board and the Mosquitto
+ broker (i.e. IP address of Ethernet port on host PC). These addresses should be in the
+ same subnet e.g. 192.0.2.1 and 192.0.2.2.
+- On your host PC, install Mosquitto.
+- Create a file called ``unsecure.conf`` with the following content:
+
+.. code-block:: console
+
+ listener 1883 0.0.0.0
+ allow_anonymous true
+
+
+- Start a Mosquitto broker using the configuration file:
+
+.. code-block:: console
+
+ $ sudo mosquitto -v -c unsecure.conf
+
+- Build the sample with quick test config:
+
+.. zephyr-app-commands::
+ :zephyr-app: samples/net/secure_mqtt_sensor_actuator
+ :board: adi_eval_adin1110ebz
+ :conf: "prj.conf overlay-static-insecure.conf"
+ :goals: build
+ :compact:
+
+Using the Sample
+================
+- Once the board establishes an MQTT connection with the Mosquitto broker, the network
+ LED will turn on and the board will begin publishing sensor readings in JSON format
+ at a regular interval determined by ``CONFIG_NET_SAMPLE_MQTT_PUBLISH_INTERVAL``.
+
+- Use Mosquitto to subscribe to the sensor data being sent from the board:
+
+.. code-block:: console
+
+ $ mosquitto_sub -d -h -t zephyr_sample/sensor
+
+- The application will subscribe to a topic determined by ``CONFIG_NET_SAMPLE_MQTT_SUB_TOPIC_CMD``.
+ If a supported command string is received by the board on this topic, the board will execute
+ an associated command handler function.
+ Supported commands (defined in :file:`device.c`):
+
+ - ``led_on``, turn on board LED
+ - ``led_off``, turn off board LED
+
+- Use Mosquitto to publish these commands to the MQTT broker:
+
+.. code-block:: console
+
+ $ mosquitto_pub -d -h --cafile -t zephyr_sample/command -m "led_on"
+
+- The Quality of Service (QoS) level that is used for MQTT publishing and
+ subscriptions can be configured using Kconfig.
+
+Sample output
+=============
+
+.. code-block:: console
+
+ *** Booting Zephyr OS build v3.6.0-2212-g2c9c4f3733e9 ***
+ [00:00:00.181,000] app_device: Device adt7420@48 is ready
+ [00:00:00.181,000] app_device: Device leds is ready
+ [00:00:00.181,000] app_main: MAC Address: 00:E0:FE:FE:DA:C8
+ [00:00:00.181,000] app_main: Bringing up network..
+ [00:00:00.801,000] net_dhcpv4: Received: 192.168.1.17
+ [00:00:00.801,000] app_main: Network connectivity up!
+ [00:00:00.818,000] app_mqtt: Connecting to MQTT broker @ 91.121.93.94
+ [00:00:01.154,000] net_mqtt: Connect completed
+ [00:00:01.197,000] app_mqtt: Connected to MQTT broker!
+ [00:00:01.197,000] app_mqtt: Hostname: test.mosquitto.org
+ [00:00:01.198,000] app_mqtt: Client ID: adi_eval_adin1110ebz_9a
+ [00:00:01.198,000] app_mqtt: Port: 8883
+ [00:00:01.198,000] app_mqtt: TLS: Enabled
+ [00:00:01.198,000] app_mqtt: Subscribing to 1 topic(s)
+ [00:00:01.238,000] app_mqtt: SUBACK packet ID: 5841
+ [00:00:04.200,000] app_mqtt: Published to topic 'zephyr_sample/sensor', QoS 1
+ [00:00:04.319,000] app_mqtt: PUBACK packet ID: 1
+ [00:00:07.202,000] app_mqtt: Published to topic 'zephyr_sample/sensor', QoS 1
+ [00:00:07.323,000] app_mqtt: PUBACK packet ID: 2
+ [00:00:10.204,000] app_mqtt: Published to topic 'zephyr_sample/sensor', QoS 1
+ [00:00:10.322,000] app_mqtt: PUBACK packet ID: 3
+ [00:00:12.769,000] app_mqtt: MQTT payload received!
+ [00:00:12.769,000] app_mqtt: topic: 'zephyr_sample/command', payload: led_on
+ [00:00:12.770,000] app_device: Executing device command: led_on
+
+.. _Eclipse Mosquitto: https://mosquitto.org/download/
diff --git a/samples/net/secure_mqtt_sensor_actuator/boards/adi_eval_adin1110ebz.overlay b/samples/net/secure_mqtt_sensor_actuator/boards/adi_eval_adin1110ebz.overlay
new file mode 100644
index 00000000000..8182bb8b230
--- /dev/null
+++ b/samples/net/secure_mqtt_sensor_actuator/boards/adi_eval_adin1110ebz.overlay
@@ -0,0 +1,15 @@
+/*
+ * Copyright (c) 2024 Analog Devices, Inc.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+&adin1110 {
+ mdio {
+ /* Enable link status LED */
+ ethernet-phy@1 {
+ led0-en;
+ led1-en;
+ };
+ };
+};
diff --git a/samples/net/secure_mqtt_sensor_actuator/overlay-static-insecure.conf b/samples/net/secure_mqtt_sensor_actuator/overlay-static-insecure.conf
new file mode 100644
index 00000000000..33655bfe4e1
--- /dev/null
+++ b/samples/net/secure_mqtt_sensor_actuator/overlay-static-insecure.conf
@@ -0,0 +1,18 @@
+# Config to disable TLS and DHCPv4, allowing insecure MQTT and a static IP address.
+# This allows quick testing of the application without need for a DHCP server or secure MQTT broker set up.
+# Only recommended for quick sampling/testing.
+# Usage: west build -b -- -DOVERLAY_CONFIG=overlay-static-insecure.conf
+
+# Disable MQTT with TLS
+CONFIG_MQTT_LIB_TLS=n
+
+# Disable DHCP
+CONFIG_NET_DHCPV4=n
+
+# Insecure MQTT uses port 1883
+CONFIG_NET_SAMPLE_MQTT_BROKER_PORT="1883"
+
+# Static IP address config
+CONFIG_NET_CONFIG_SETTINGS=y
+CONFIG_NET_CONFIG_MY_IPV4_ADDR="192.0.2.1"
+CONFIG_NET_SAMPLE_MQTT_BROKER_HOSTNAME="192.0.2.2"
diff --git a/samples/net/secure_mqtt_sensor_actuator/overlay-static.conf b/samples/net/secure_mqtt_sensor_actuator/overlay-static.conf
new file mode 100644
index 00000000000..69737f64078
--- /dev/null
+++ b/samples/net/secure_mqtt_sensor_actuator/overlay-static.conf
@@ -0,0 +1,8 @@
+CONFIG_NET_DHCPV4=n
+CONFIG_NET_CONFIG_SETTINGS=y
+CONFIG_NET_CONFIG_NEED_IPV4=y
+CONFIG_NET_CONFIG_MY_IPV4_ADDR="192.0.2.1"
+CONFIG_NET_CONFIG_MY_IPV4_GW="192.0.2.2"
+CONFIG_DNS_RESOLVER=y
+CONFIG_DNS_SERVER_IP_ADDRESSES=y
+CONFIG_DNS_SERVER1="192.0.2.2"
diff --git a/samples/net/secure_mqtt_sensor_actuator/prj.conf b/samples/net/secure_mqtt_sensor_actuator/prj.conf
new file mode 100644
index 00000000000..b87eaa2c348
--- /dev/null
+++ b/samples/net/secure_mqtt_sensor_actuator/prj.conf
@@ -0,0 +1,59 @@
+# Enable network stack
+CONFIG_NETWORKING=y
+CONFIG_NET_LOG=y
+
+# Enable IPv4
+CONFIG_NET_IPV4=y
+
+# Enable IPv6
+CONFIG_NET_IPV6=y
+
+# Enable TCP
+CONFIG_NET_TCP=y
+
+# Enable DHCP
+CONFIG_NET_DHCPV4=y
+
+# Enable Sockets (used by MQTT lib)
+CONFIG_NET_SOCKETS=y
+CONFIG_NET_SOCKETS_SOCKOPT_TLS=y
+
+# Enable MQTT
+CONFIG_MQTT_LIB=y
+CONFIG_MQTT_LIB_TLS=y
+
+# Enable Mbed TLS
+CONFIG_MBEDTLS=y
+CONFIG_MBEDTLS_BUILTIN=y
+CONFIG_MBEDTLS_ENABLE_HEAP=y
+CONFIG_MBEDTLS_HEAP_SIZE=60000
+CONFIG_MBEDTLS_SSL_MAX_CONTENT_LEN=16384
+CONFIG_MBEDTLS_PEM_CERTIFICATE_FORMAT=y
+CONFIG_MBEDTLS_SERVER_NAME_INDICATION=y
+
+# Enable JSON
+CONFIG_JSON_LIBRARY=y
+
+# Enable net conn manager
+CONFIG_NET_CONNECTION_MANAGER=y
+
+# Enable device hostname
+CONFIG_NET_HOSTNAME_ENABLE=y
+
+# Enable Posix API functionality
+CONFIG_POSIX_API=y
+
+# Enable sensor API
+CONFIG_SENSOR=y
+
+# Enable LED API
+CONFIG_LED=y
+
+# Main stack size
+CONFIG_MAIN_STACK_SIZE=2048
+
+# System work queue stack size
+CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=2048
+
+# Increase Rx net buffers
+CONFIG_NET_BUF_RX_COUNT=100
diff --git a/samples/net/secure_mqtt_sensor_actuator/sample.yaml b/samples/net/secure_mqtt_sensor_actuator/sample.yaml
new file mode 100644
index 00000000000..44b9a4d7d2a
--- /dev/null
+++ b/samples/net/secure_mqtt_sensor_actuator/sample.yaml
@@ -0,0 +1,33 @@
+sample:
+ name: Secure MQTT Sensor/Actuator Sample
+tests:
+ sample.net.secure_mqtt_sensor_actuator:
+ harness: net
+ tags:
+ - net
+ - mqtt
+ - sensors
+ filter: dt_alias_exists("ambient-temp0")
+ integration_platforms:
+ - adi_eval_adin1110ebz
+ sample.net.secure_mqtt_sensor_actuator.staticip:
+ harness: net
+ extra_args: OVERLAY_CONFIG="overlay-static.conf"
+ tags:
+ - net
+ - mqtt
+ - sensors
+ filter: dt_alias_exists("ambient-temp0")
+ integration_platforms:
+ - native_sim
+ sample.net.secure_mqtt_sensor_actuator.staticip_insecure:
+ harness: net
+ extra_args: OVERLAY_CONFIG="overlay-static-insecure.conf"
+ tags:
+ - net
+ - mqtt
+ - sensors
+ filter: dt_alias_exists("ambient-temp0")
+ integration_platforms:
+ - adi_eval_adin1110ebz
+ - native_sim
diff --git a/samples/net/secure_mqtt_sensor_actuator/src/device.c b/samples/net/secure_mqtt_sensor_actuator/src/device.c
new file mode 100644
index 00000000000..7ac800e43e2
--- /dev/null
+++ b/samples/net/secure_mqtt_sensor_actuator/src/device.c
@@ -0,0 +1,138 @@
+/*
+ * Copyright (c) 2024 Analog Devices, Inc.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#include
+LOG_MODULE_REGISTER(app_device, LOG_LEVEL_DBG);
+
+#include
+#include
+#include
+#include
+
+#include "device.h"
+
+#define SENSOR_CHAN SENSOR_CHAN_AMBIENT_TEMP
+#define SENSOR_UNIT "Celsius"
+
+/* Devices */
+static const struct device *sensor = DEVICE_DT_GET_OR_NULL(DT_ALIAS(ambient_temp0));
+static const struct device *leds = DEVICE_DT_GET_OR_NULL(DT_INST(0, gpio_leds));
+
+/* Command handlers */
+static void led_on_handler(void)
+{
+ device_write_led(LED_USER, LED_ON);
+}
+
+static void led_off_handler(void)
+{
+ device_write_led(LED_USER, LED_OFF);
+}
+
+/* Supported device commands */
+struct device_cmd device_commands[] = {
+ {"led_on", led_on_handler},
+ {"led_off", led_off_handler}
+};
+
+const size_t num_device_commands = ARRAY_SIZE(device_commands);
+
+/* Command dispatcher */
+void device_command_handler(uint8_t *command)
+{
+ for (int i = 0; i < num_device_commands; i++) {
+ if (strcmp(command, device_commands[i].command) == 0) {
+ LOG_INF("Executing device command: %s", device_commands[i].command);
+ return device_commands[i].handler();
+ }
+ }
+ LOG_ERR("Unknown command: %s", command);
+}
+
+int device_read_sensor(struct sensor_sample *sample)
+{
+ int rc;
+ struct sensor_value sensor_val;
+
+ /* Read sample only if a real sensor device is present
+ * otherwise return a dummy value
+ */
+ if (sensor == NULL) {
+ sample->unit = SENSOR_UNIT;
+ sample->value = 20.0 + (double)sys_rand32_get() / UINT32_MAX * 5.0;
+ return 0;
+ }
+
+ rc = sensor_sample_fetch(sensor);
+ if (rc) {
+ LOG_ERR("Failed to fetch sensor sample [%d]", rc);
+ return rc;
+ }
+
+ rc = sensor_channel_get(sensor, SENSOR_CHAN, &sensor_val);
+ if (rc) {
+ LOG_ERR("Failed to get sensor channel [%d]", rc);
+ return rc;
+ }
+
+ sample->unit = SENSOR_UNIT;
+ sample->value = sensor_value_to_double(&sensor_val);
+ return rc;
+}
+
+int device_write_led(enum led_id led_idx, enum led_state state)
+{
+ int rc;
+
+ switch (state) {
+ case LED_OFF:
+ if (leds == NULL) {
+ LOG_INF("LED %d OFF", led_idx);
+ break;
+ }
+ led_off(leds, led_idx);
+ break;
+ case LED_ON:
+ if (leds == NULL) {
+ LOG_INF("LED %d ON", led_idx);
+ break;
+ }
+ led_on(leds, led_idx);
+ break;
+ default:
+ LOG_ERR("Invalid LED state setting");
+ rc = -EINVAL;
+ break;
+ }
+
+ return rc;
+}
+
+bool devices_ready(void)
+{
+ bool rc = true;
+
+ /* Check readiness only if a real sensor device is present */
+ if (sensor != NULL) {
+ if (!device_is_ready(sensor)) {
+ LOG_ERR("Device %s is not ready", sensor->name);
+ rc = false;
+ } else {
+ LOG_INF("Device %s is ready", sensor->name);
+ }
+ }
+
+ if (leds != NULL) {
+ if (!device_is_ready(leds)) {
+ LOG_ERR("Device %s is not ready", leds->name);
+ rc = false;
+ } else {
+ LOG_INF("Device %s is ready", leds->name);
+ }
+ }
+
+ return rc;
+}
diff --git a/samples/net/secure_mqtt_sensor_actuator/src/device.h b/samples/net/secure_mqtt_sensor_actuator/src/device.h
new file mode 100644
index 00000000000..ad09b9d3efa
--- /dev/null
+++ b/samples/net/secure_mqtt_sensor_actuator/src/device.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2024 Analog Devices, Inc.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#ifndef __DEVICE_H__
+#define __DEVICE_H__
+
+/** @brief Sensor sample structure */
+struct sensor_sample {
+ const char *unit;
+ int value;
+};
+
+/** @brief Available board LEDs */
+enum led_id {
+ LED_NET, /* Network status LED*/
+ LED_USER /* User LED */
+};
+
+/** @brief LED states */
+enum led_state {
+ LED_OFF,
+ LED_ON
+};
+
+/** @brief Device command consisting of a command string
+ * and associated handler function pointer
+ */
+struct device_cmd {
+ const char *command;
+ void (*handler)();
+};
+
+/**
+ * @brief Check if all devices are ready
+ */
+bool devices_ready(void);
+
+/**
+ * @brief Read sample from the sensor
+ */
+int device_read_sensor(struct sensor_sample *sample);
+
+/**
+ * @brief Write to a board LED
+ */
+int device_write_led(enum led_id led_idx, enum led_state state);
+
+/**
+ * @brief Handler function for commands received over MQTT
+ */
+void device_command_handler(uint8_t *command);
+
+#endif /* __DEVICE_H__ */
diff --git a/samples/net/secure_mqtt_sensor_actuator/src/main.c b/samples/net/secure_mqtt_sensor_actuator/src/main.c
new file mode 100644
index 00000000000..64993139e90
--- /dev/null
+++ b/samples/net/secure_mqtt_sensor_actuator/src/main.c
@@ -0,0 +1,140 @@
+/*
+ * Copyright (c) 2024 Analog Devices, Inc.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#include
+LOG_MODULE_REGISTER(app_main, LOG_LEVEL_DBG);
+
+#include
+#include
+#include
+#include
+#include
+
+#include "mqtt_client.h"
+#include "device.h"
+
+#define NET_L4_EVENT_MASK (NET_EVENT_L4_CONNECTED | NET_EVENT_L4_DISCONNECTED)
+
+/* MQTT client struct */
+static struct mqtt_client client_ctx;
+
+/* MQTT publish work item */
+struct k_work_delayable mqtt_publish_work;
+
+static struct net_mgmt_event_callback net_l4_mgmt_cb;
+
+/* Network connection semaphore */
+K_SEM_DEFINE(net_conn_sem, 0, 1);
+
+static void net_l4_evt_handler(struct net_mgmt_event_callback *cb,
+ uint32_t mgmt_event, struct net_if *iface)
+{
+ switch (mgmt_event) {
+ case NET_EVENT_L4_CONNECTED:
+ k_sem_give(&net_conn_sem);
+ LOG_INF("Network connectivity up!");
+ break;
+ case NET_EVENT_L4_DISCONNECTED:
+ LOG_INF("Network connectivity down!");
+ break;
+ default:
+ break;
+ }
+}
+
+/** Print the device's MAC address to console */
+void log_mac_addr(struct net_if *iface)
+{
+ struct net_linkaddr *mac;
+
+ mac = net_if_get_link_addr(iface);
+
+ LOG_INF("MAC Address: %02X:%02X:%02X:%02X:%02X:%02X",
+ mac->addr[0], mac->addr[1], mac->addr[3],
+ mac->addr[3], mac->addr[4], mac->addr[5]);
+}
+
+/** The system work queue is used to handle periodic MQTT publishing.
+ * Work queuing begins when the MQTT connection is established.
+ * Use CONFIG_NET_SAMPLE_MQTT_PUBLISH_INTERVAL to set the publish frequency.
+ */
+
+static void publish_work_handler(struct k_work *work)
+{
+ int rc;
+
+ if (mqtt_connected) {
+ rc = app_mqtt_publish(&client_ctx);
+ if (rc != 0) {
+ LOG_INF("MQTT Publish failed [%d]", rc);
+ }
+ k_work_reschedule(&mqtt_publish_work,
+ K_SECONDS(CONFIG_NET_SAMPLE_MQTT_PUBLISH_INTERVAL));
+ } else {
+ k_work_cancel_delayable(&mqtt_publish_work);
+ }
+}
+
+int main(void)
+{
+ int rc;
+ struct net_if *iface;
+
+ devices_ready();
+
+ iface = net_if_get_default();
+ if (iface == NULL) {
+ LOG_ERR("No network interface configured");
+ return -ENETDOWN;
+ }
+
+ log_mac_addr(iface);
+
+ /* Register callbacks for L4 events */
+ net_mgmt_init_event_callback(&net_l4_mgmt_cb, &net_l4_evt_handler, NET_L4_EVENT_MASK);
+ net_mgmt_add_event_callback(&net_l4_mgmt_cb);
+
+ LOG_INF("Bringing up network..");
+
+#if defined(CONFIG_NET_DHCPV4)
+ net_dhcpv4_start(iface);
+#else
+ /* If using static IP, L4 Connect callback will happen,
+ * before conn mgr is initialised, so resend events here
+ * to check for connectivity
+ */
+ conn_mgr_mon_resend_status();
+#endif
+
+ /* Wait for network to come up */
+ while (k_sem_take(&net_conn_sem, K_MSEC(MSECS_NET_POLL_TIMEOUT)) != 0) {
+ LOG_INF("Waiting for network connection..");
+ }
+
+ rc = app_mqtt_init(&client_ctx);
+ if (rc != 0) {
+ LOG_ERR("MQTT Init failed [%d]", rc);
+ return rc;
+ }
+
+ /* Initialise MQTT publish work item */
+ k_work_init_delayable(&mqtt_publish_work, publish_work_handler);
+
+ /* Thread main loop */
+ while (1) {
+ /* Block until MQTT connection is up */
+ app_mqtt_connect(&client_ctx);
+
+ /* We are now connected, begin queueing periodic MQTT publishes */
+ k_work_reschedule(&mqtt_publish_work,
+ K_SECONDS(CONFIG_NET_SAMPLE_MQTT_PUBLISH_INTERVAL));
+
+ /* Handle MQTT inputs and connection */
+ app_mqtt_run(&client_ctx);
+ }
+
+ return rc;
+}
diff --git a/samples/net/secure_mqtt_sensor_actuator/src/mqtt_client.c b/samples/net/secure_mqtt_sensor_actuator/src/mqtt_client.c
new file mode 100644
index 00000000000..3fdb6f3abf4
--- /dev/null
+++ b/samples/net/secure_mqtt_sensor_actuator/src/mqtt_client.c
@@ -0,0 +1,510 @@
+/*
+ * Copyright (c) 2024 Analog Devices, Inc.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#include
+LOG_MODULE_REGISTER(app_mqtt, LOG_LEVEL_DBG);
+
+#include
+#include
+#include
+#include
+#include
+
+#include "mqtt_client.h"
+#include "device.h"
+
+/* Buffers for MQTT client */
+static uint8_t rx_buffer[CONFIG_NET_SAMPLE_MQTT_PAYLOAD_SIZE];
+static uint8_t tx_buffer[CONFIG_NET_SAMPLE_MQTT_PAYLOAD_SIZE];
+
+/* MQTT payload buffer */
+static uint8_t payload_buf[CONFIG_NET_SAMPLE_MQTT_PAYLOAD_SIZE];
+
+/* MQTT broker details */
+static struct sockaddr_storage broker;
+
+/* Socket descriptor */
+static struct zsock_pollfd fds[1];
+static int nfds;
+
+/* JSON payload format */
+static const struct json_obj_descr sensor_sample_descr[] = {
+ JSON_OBJ_DESCR_PRIM(struct sensor_sample, unit, JSON_TOK_STRING),
+ JSON_OBJ_DESCR_PRIM(struct sensor_sample, value, JSON_TOK_NUMBER),
+};
+
+/* MQTT connectivity status flag */
+bool mqtt_connected;
+
+/* MQTT client ID buffer */
+static uint8_t client_id[50];
+
+#if defined(CONFIG_MQTT_LIB_TLS)
+#include "tls_config/cert.h"
+
+/* This should match the CN field in the server's CA cert */
+#define TLS_SNI_HOSTNAME CONFIG_NET_SAMPLE_MQTT_BROKER_HOSTNAME
+#define APP_CA_CERT_TAG 1
+
+static const sec_tag_t m_sec_tags[] = {
+ APP_CA_CERT_TAG,
+};
+
+/** Register CA certificate for TLS */
+static int tls_init(void)
+{
+ int rc;
+
+ rc = tls_credential_add(APP_CA_CERT_TAG, TLS_CREDENTIAL_CA_CERTIFICATE,
+ ca_certificate, sizeof(ca_certificate));
+ if (rc < 0) {
+ LOG_ERR("Failed to register public certificate: %d", rc);
+ return rc;
+ }
+
+ return rc;
+}
+#endif
+
+static void prepare_fds(struct mqtt_client *client)
+{
+ if (client->transport.type == MQTT_TRANSPORT_NON_SECURE) {
+ fds[0].fd = client->transport.tcp.sock;
+ }
+#if defined(CONFIG_MQTT_LIB_TLS)
+ else if (client->transport.type == MQTT_TRANSPORT_SECURE) {
+ fds[0].fd = client->transport.tls.sock;
+ }
+#endif
+
+ fds[0].events = ZSOCK_POLLIN;
+ nfds = 1;
+}
+
+static void clear_fds(void)
+{
+ nfds = 0;
+}
+
+/** Initialise the MQTT client ID as the board name with random hex postfix */
+static void init_mqtt_client_id(void)
+{
+ snprintk(client_id, sizeof(client_id), CONFIG_BOARD"_%x", (uint8_t)sys_rand32_get());
+}
+
+static inline void on_mqtt_connect(void)
+{
+ mqtt_connected = true;
+ device_write_led(LED_NET, LED_ON);
+ LOG_INF("Connected to MQTT broker!");
+ LOG_INF("Hostname: %s", CONFIG_NET_SAMPLE_MQTT_BROKER_HOSTNAME);
+ LOG_INF("Client ID: %s", client_id);
+ LOG_INF("Port: %s", CONFIG_NET_SAMPLE_MQTT_BROKER_PORT);
+ LOG_INF("TLS: %s",
+ IS_ENABLED(CONFIG_MQTT_LIB_TLS) ? "Enabled" : "Disabled");
+}
+
+static inline void on_mqtt_disconnect(void)
+{
+ mqtt_connected = false;
+ clear_fds();
+ device_write_led(LED_NET, LED_OFF);
+ LOG_INF("Disconnected from MQTT broker");
+}
+
+/** Called when an MQTT payload is received.
+ * Reads the payload and calls the commands
+ * handler if a payloads is received on the
+ * command topic
+ */
+static void on_mqtt_publish(struct mqtt_client *const client, const struct mqtt_evt *evt)
+{
+ int rc;
+ uint8_t payload[CONFIG_NET_SAMPLE_MQTT_PAYLOAD_SIZE];
+
+ rc = mqtt_read_publish_payload(client, payload,
+ CONFIG_NET_SAMPLE_MQTT_PAYLOAD_SIZE);
+ if (rc < 0) {
+ LOG_ERR("Failed to read received MQTT payload [%d]", rc);
+ return;
+ }
+ /* Place null terminator at end of payload buffer */
+ payload[rc] = '\0';
+
+ LOG_INF("MQTT payload received!");
+ LOG_INF("topic: '%s', payload: %s",
+ evt->param.publish.message.topic.topic.utf8, payload);
+
+ /* If the topic is a command, call the command handler */
+ if (strcmp(evt->param.publish.message.topic.topic.utf8,
+ CONFIG_NET_SAMPLE_MQTT_SUB_TOPIC_CMD) == 0) {
+ device_command_handler(payload);
+ }
+}
+
+/** Handler for asynchronous MQTT events */
+static void mqtt_event_handler(struct mqtt_client *const client, const struct mqtt_evt *evt)
+{
+ switch (evt->type) {
+ case MQTT_EVT_CONNACK:
+ if (evt->result != 0) {
+ LOG_ERR("MQTT Event Connect failed [%d]", evt->result);
+ break;
+ }
+ on_mqtt_connect();
+ break;
+
+ case MQTT_EVT_DISCONNECT:
+ on_mqtt_disconnect();
+ break;
+
+ case MQTT_EVT_PINGRESP:
+ LOG_INF("PINGRESP packet");
+ 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_PUBREC:
+ if (evt->result != 0) {
+ LOG_ERR("MQTT PUBREC error [%d]", evt->result);
+ break;
+ }
+
+ LOG_INF("PUBREC packet ID: %u", evt->param.pubrec.message_id);
+
+ const struct mqtt_pubrel_param rel_param = {
+ .message_id = evt->param.pubrec.message_id
+ };
+
+ mqtt_publish_qos2_release(client, &rel_param);
+ break;
+
+ case MQTT_EVT_PUBREL:
+ if (evt->result != 0) {
+ LOG_ERR("MQTT PUBREL error [%d]", evt->result);
+ break;
+ }
+
+ LOG_INF("PUBREL packet ID: %u", evt->param.pubrel.message_id);
+
+ const struct mqtt_pubcomp_param rec_param = {
+ .message_id = evt->param.pubrel.message_id
+ };
+
+ mqtt_publish_qos2_complete(client, &rec_param);
+ break;
+
+ case MQTT_EVT_PUBCOMP:
+ if (evt->result != 0) {
+ LOG_ERR("MQTT PUBCOMP error %d", evt->result);
+ break;
+ }
+
+ LOG_INF("PUBCOMP packet ID: %u", evt->param.pubcomp.message_id);
+ break;
+
+ case MQTT_EVT_SUBACK:
+ if (evt->result == 0x80) {
+ LOG_ERR("MQTT SUBACK error [%d]", evt->result);
+ break;
+ }
+
+ LOG_INF("SUBACK packet ID: %d", evt->param.suback.message_id);
+ break;
+
+ case MQTT_EVT_PUBLISH:
+ const struct mqtt_publish_param *p = &evt->param.publish;
+
+ if (p->message.topic.qos == MQTT_QOS_1_AT_LEAST_ONCE) {
+ const struct mqtt_puback_param ack_param = {
+ .message_id = p->message_id
+ };
+ mqtt_publish_qos1_ack(client, &ack_param);
+ } else if (p->message.topic.qos == MQTT_QOS_2_EXACTLY_ONCE) {
+ const struct mqtt_pubrec_param rec_param = {
+ .message_id = p->message_id
+ };
+ mqtt_publish_qos2_receive(client, &rec_param);
+ }
+
+ on_mqtt_publish(client, evt);
+
+ default:
+ break;
+ }
+}
+
+/** Poll the MQTT socket for received data */
+static int poll_mqtt_socket(struct mqtt_client *client, int timeout)
+{
+ int rc;
+
+ prepare_fds(client);
+
+ if (nfds <= 0) {
+ return -EINVAL;
+ }
+
+ rc = zsock_poll(fds, nfds, timeout);
+ if (rc < 0) {
+ LOG_ERR("Socket poll error [%d]", rc);
+ }
+
+ return rc;
+}
+
+/** Retrieves a sensor sample and encodes it in JSON format */
+static int get_mqtt_payload(struct mqtt_binstr *payload)
+{
+ int rc;
+ struct sensor_sample sample;
+
+ rc = device_read_sensor(&sample);
+ if (rc != 0) {
+ LOG_ERR("Failed to get sensor sample [%d]", rc);
+ return rc;
+ }
+
+ rc = json_obj_encode_buf(sensor_sample_descr, ARRAY_SIZE(sensor_sample_descr),
+ &sample, payload_buf, CONFIG_NET_SAMPLE_MQTT_PAYLOAD_SIZE);
+ if (rc != 0) {
+ LOG_ERR("Failed to encode JSON object [%d]", rc);
+ return rc;
+ }
+
+ payload->data = payload_buf;
+ payload->len = strlen(payload->data);
+
+ return rc;
+}
+
+int app_mqtt_publish(struct mqtt_client *client)
+{
+ int rc;
+ struct mqtt_publish_param param;
+ struct mqtt_binstr payload;
+ static uint16_t msg_id = 1;
+ struct mqtt_topic topic = {
+ .topic = {
+ .utf8 = CONFIG_NET_SAMPLE_MQTT_PUB_TOPIC,
+ .size = strlen(topic.topic.utf8)
+ },
+ .qos = IS_ENABLED(CONFIG_NET_SAMPLE_MQTT_QOS_0_AT_MOST_ONCE) ? 0 :
+ (IS_ENABLED(CONFIG_NET_SAMPLE_MQTT_QOS_1_AT_LEAST_ONCE) ? 1 : 2)
+ };
+
+ rc = get_mqtt_payload(&payload);
+ if (rc != 0) {
+ LOG_ERR("Failed to get MQTT payload [%d]", rc);
+ }
+
+ param.message.topic = topic;
+ param.message.payload = payload;
+ param.message_id = msg_id++;
+ param.dup_flag = 0;
+ param.retain_flag = 0;
+
+ rc = mqtt_publish(client, ¶m);
+ if (rc != 0) {
+ LOG_ERR("MQTT Publish failed [%d]", rc);
+ }
+
+ LOG_INF("Published to topic '%s', QoS %d",
+ param.message.topic.topic.utf8,
+ param.message.topic.qos);
+
+ return rc;
+}
+
+int app_mqtt_subscribe(struct mqtt_client *client)
+{
+ int rc;
+ struct mqtt_topic sub_topics[] = {
+ {
+ .topic = {
+ .utf8 = CONFIG_NET_SAMPLE_MQTT_SUB_TOPIC_CMD,
+ .size = strlen(sub_topics->topic.utf8)
+ },
+ .qos = IS_ENABLED(CONFIG_NET_SAMPLE_MQTT_QOS_0_AT_MOST_ONCE) ? 0 :
+ (IS_ENABLED(CONFIG_NET_SAMPLE_MQTT_QOS_1_AT_LEAST_ONCE) ? 1 : 2)
+ }
+ };
+ const struct mqtt_subscription_list sub_list = {
+ .list = sub_topics,
+ .list_count = ARRAY_SIZE(sub_topics),
+ .message_id = 5841u
+ };
+
+ LOG_INF("Subscribing to %d topic(s)", sub_list.list_count);
+
+ rc = mqtt_subscribe(client, &sub_list);
+ if (rc != 0) {
+ LOG_ERR("MQTT Subscribe failed [%d]", rc);
+ }
+
+ return rc;
+}
+
+/** Process incoming MQTT data and keep the connection alive*/
+int app_mqtt_process(struct mqtt_client *client)
+{
+ int rc;
+
+ rc = poll_mqtt_socket(client, mqtt_keepalive_time_left(client));
+ if (rc != 0) {
+ if (fds[0].revents & ZSOCK_POLLIN) {
+ /* MQTT data received */
+ rc = mqtt_input(client);
+ if (rc != 0) {
+ LOG_ERR("MQTT Input failed [%d]", rc);
+ return rc;
+ }
+ /* Socket error */
+ if (fds[0].revents & (ZSOCK_POLLHUP | ZSOCK_POLLERR)) {
+ LOG_ERR("MQTT socket closed / error");
+ return -ENOTCONN;
+ }
+ }
+ } else {
+ /* Socket poll timed out, time to call mqtt_live() */
+ rc = mqtt_live(client);
+ if (rc != 0) {
+ LOG_ERR("MQTT Live failed [%d]", rc);
+ return rc;
+ }
+ }
+
+ return 0;
+}
+
+void app_mqtt_run(struct mqtt_client *client)
+{
+ int rc;
+
+ /* Subscribe to MQTT topics */
+ app_mqtt_subscribe(client);
+
+ /* Thread will primarily remain in this loop */
+ while (mqtt_connected) {
+ rc = app_mqtt_process(client);
+ if (rc != 0) {
+ break;
+ }
+ }
+ /* Gracefully close connection */
+ mqtt_disconnect(client);
+}
+
+void app_mqtt_connect(struct mqtt_client *client)
+{
+ int rc = 0;
+
+ mqtt_connected = false;
+
+ /* Block until MQTT CONNACK event callback occurs */
+ while (!mqtt_connected) {
+ rc = mqtt_connect(client);
+ if (rc != 0) {
+ LOG_ERR("MQTT Connect failed [%d]", rc);
+ k_msleep(MSECS_WAIT_RECONNECT);
+ continue;
+ }
+
+ /* Poll MQTT socket for response */
+ rc = poll_mqtt_socket(client, MSECS_NET_POLL_TIMEOUT);
+ if (rc > 0) {
+ mqtt_input(client);
+ }
+
+ if (!mqtt_connected) {
+ mqtt_abort(client);
+ }
+ }
+}
+
+int app_mqtt_init(struct mqtt_client *client)
+{
+ int rc;
+ uint8_t broker_ip[NET_IPV4_ADDR_LEN];
+ struct sockaddr_in *broker4;
+ struct addrinfo *result;
+ const struct addrinfo hints = {
+ .ai_family = AF_INET,
+ .ai_socktype = SOCK_STREAM
+ };
+
+ /* Resolve IP address of MQTT broker */
+ rc = getaddrinfo(CONFIG_NET_SAMPLE_MQTT_BROKER_HOSTNAME,
+ CONFIG_NET_SAMPLE_MQTT_BROKER_PORT, &hints, &result);
+ if (rc != 0) {
+ LOG_ERR("Failed to resolve broker hostname [%s]", gai_strerror(rc));
+ return -EIO;
+ }
+ if (result == NULL) {
+ LOG_ERR("Broker address not found");
+ return -ENOENT;
+ }
+
+ broker4 = (struct sockaddr_in *)&broker;
+ broker4->sin_addr.s_addr = ((struct sockaddr_in *)result->ai_addr)->sin_addr.s_addr;
+ broker4->sin_family = AF_INET;
+ broker4->sin_port = ((struct sockaddr_in *)result->ai_addr)->sin_port;
+ freeaddrinfo(result);
+
+ /* Log resolved IP address */
+ inet_ntop(AF_INET, &broker4->sin_addr.s_addr, broker_ip, sizeof(broker_ip));
+ LOG_INF("Connecting to MQTT broker @ %s", broker_ip);
+
+ /* MQTT client configuration */
+ init_mqtt_client_id();
+ mqtt_client_init(client);
+ client->broker = &broker;
+ client->evt_cb = mqtt_event_handler;
+ client->client_id.utf8 = client_id;
+ 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_config;
+
+ client->transport.type = MQTT_TRANSPORT_SECURE;
+
+ rc = tls_init();
+ if (rc != 0) {
+ LOG_ERR("TLS init error");
+ return rc;
+ }
+
+ tls_config = &client->transport.tls.config;
+ tls_config->peer_verify = TLS_PEER_VERIFY_REQUIRED;
+ tls_config->cipher_list = NULL;
+ tls_config->sec_tag_list = m_sec_tags;
+ tls_config->sec_tag_count = ARRAY_SIZE(m_sec_tags);
+#if defined(CONFIG_MBEDTLS_SERVER_NAME_INDICATION)
+ tls_config->hostname = TLS_SNI_HOSTNAME;
+#else
+ tls_config->hostname = NULL;
+#endif /* CONFIG_MBEDTLS_SERVER_NAME_INDICATION */
+#endif /* CONFIG_MQTT_LIB_TLS */
+
+ return rc;
+}
diff --git a/samples/net/secure_mqtt_sensor_actuator/src/mqtt_client.h b/samples/net/secure_mqtt_sensor_actuator/src/mqtt_client.h
new file mode 100644
index 00000000000..d78c2b05d34
--- /dev/null
+++ b/samples/net/secure_mqtt_sensor_actuator/src/mqtt_client.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2024 Analog Devices, Inc.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#ifndef __MQTT_CLIENT_H__
+#define __MQTT_CLIENT_H__
+
+/** MQTT connection timeouts */
+#define MSECS_NET_POLL_TIMEOUT 5000
+#define MSECS_WAIT_RECONNECT 1000
+
+/** MQTT connection status flag */
+extern bool mqtt_connected;
+
+/**
+ * @brief Initialise the MQTT client & broker configuration
+ */
+int app_mqtt_init(struct mqtt_client *client);
+
+/**
+ * @brief Blocking function that establishes connectivity to the MQTT broker
+ */
+void app_mqtt_connect(struct mqtt_client *client);
+
+/**
+ * @brief Subscribes to user-defined MQTT topics and continuously
+ * processes incoming data while the MQTT connection is active
+ */
+void app_mqtt_run(struct mqtt_client *client);
+
+/**
+ * @brief Subscribe to user-defined MQTT topics
+ */
+int app_mqtt_subscribe(struct mqtt_client *client);
+
+/**
+ * @brief Publish MQTT payload
+ */
+int app_mqtt_publish(struct mqtt_client *client);
+
+#endif /* __MQTT_CLIENT_H__ */
diff --git a/samples/net/secure_mqtt_sensor_actuator/src/tls_config/cert.h b/samples/net/secure_mqtt_sensor_actuator/src/tls_config/cert.h
new file mode 100644
index 00000000000..e044f465b9e
--- /dev/null
+++ b/samples/net/secure_mqtt_sensor_actuator/src/tls_config/cert.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2024 Analog Devices, Inc.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#ifndef __CERT_H__
+#define __CERT_H__
+
+/* The CA certficate of the MQTT broker should be included here
+ * The certificate can either be in DER or PEM format.
+ * A DER certificate can be converted to a byte array using
+ * "cat ca.crt | sed -e '1d;$d' | base64 -d |xxd -i"
+ * If using a PEM certifificate, each line should be wrapped in "\r\n"
+ */
+
+/* CA certificate for Mosquitto public broker
+ * (available from test.mosquitto.org, valid at time of development)
+ */
+static const unsigned char ca_certificate[] = "-----BEGIN CERTIFICATE-----\r\n"
+"MIIEAzCCAuugAwIBAgIUBY1hlCGvdj4NhBXkZ/uLUZNILAwwDQYJKoZIhvcNAQEL\r\n"
+"BQAwgZAxCzAJBgNVBAYTAkdCMRcwFQYDVQQIDA5Vbml0ZWQgS2luZ2RvbTEOMAwG\r\n"
+"A1UEBwwFRGVyYnkxEjAQBgNVBAoMCU1vc3F1aXR0bzELMAkGA1UECwwCQ0ExFjAU\r\n"
+"BgNVBAMMDW1vc3F1aXR0by5vcmcxHzAdBgkqhkiG9w0BCQEWEHJvZ2VyQGF0Y2hv\r\n"
+"by5vcmcwHhcNMjAwNjA5MTEwNjM5WhcNMzAwNjA3MTEwNjM5WjCBkDELMAkGA1UE\r\n"
+"BhMCR0IxFzAVBgNVBAgMDlVuaXRlZCBLaW5nZG9tMQ4wDAYDVQQHDAVEZXJieTES\r\n"
+"MBAGA1UECgwJTW9zcXVpdHRvMQswCQYDVQQLDAJDQTEWMBQGA1UEAwwNbW9zcXVp\r\n"
+"dHRvLm9yZzEfMB0GCSqGSIb3DQEJARYQcm9nZXJAYXRjaG9vLm9yZzCCASIwDQYJ\r\n"
+"KoZIhvcNAQEBBQADggEPADCCAQoCggEBAME0HKmIzfTOwkKLT3THHe+ObdizamPg\r\n"
+"UZmD64Tf3zJdNeYGYn4CEXbyP6fy3tWc8S2boW6dzrH8SdFf9uo320GJA9B7U1FW\r\n"
+"Te3xda/Lm3JFfaHjkWw7jBwcauQZjpGINHapHRlpiCZsquAthOgxW9SgDgYlGzEA\r\n"
+"s06pkEFiMw+qDfLo/sxFKB6vQlFekMeCymjLCbNwPJyqyhFmPWwio/PDMruBTzPH\r\n"
+"3cioBnrJWKXc3OjXdLGFJOfj7pP0j/dr2LH72eSvv3PQQFl90CZPFhrCUcRHSSxo\r\n"
+"E6yjGOdnz7f6PveLIB574kQORwt8ePn0yidrTC1ictikED3nHYhMUOUCAwEAAaNT\r\n"
+"MFEwHQYDVR0OBBYEFPVV6xBUFPiGKDyo5V3+Hbh4N9YSMB8GA1UdIwQYMBaAFPVV\r\n"
+"6xBUFPiGKDyo5V3+Hbh4N9YSMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEL\r\n"
+"BQADggEBAGa9kS21N70ThM6/Hj9D7mbVxKLBjVWe2TPsGfbl3rEDfZ+OKRZ2j6AC\r\n"
+"6r7jb4TZO3dzF2p6dgbrlU71Y/4K0TdzIjRj3cQ3KSm41JvUQ0hZ/c04iGDg/xWf\r\n"
+"+pp58nfPAYwuerruPNWmlStWAXf0UTqRtg4hQDWBuUFDJTuWuuBvEXudz74eh/wK\r\n"
+"sMwfu1HFvjy5Z0iMDU8PUDepjVolOCue9ashlS4EB5IECdSR2TItnAIiIwimx839\r\n"
+"LdUdRudafMu5T5Xma182OC0/u/xRlEm+tvKGGmfFcN0piqVl8OrSPBgIlb+1IKJE\r\n"
+"m/XriWr/Cq4h/JfB7NTsezVslgkBaoU=\r\n"
+"-----END CERTIFICATE-----\r\n";
+
+#endif /* __CERT_H__ */