samples: net: add secure MQTT sensor/actuator device sample
This sample demonstrates the implementation of an (industrial) 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 the MQTT connection. Signed-off-by: Jason Murphy <jason.murphy@analog.com>
This commit is contained in:
parent
fac04490a4
commit
55d6e4cb10
14 changed files with 1353 additions and 0 deletions
11
samples/net/secure_mqtt_sensor_actuator/CMakeLists.txt
Normal file
11
samples/net/secure_mqtt_sensor_actuator/CMakeLists.txt
Normal file
|
@ -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)
|
58
samples/net/secure_mqtt_sensor_actuator/Kconfig
Normal file
58
samples/net/secure_mqtt_sensor_actuator/Kconfig
Normal file
|
@ -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"
|
219
samples/net/secure_mqtt_sensor_actuator/README.rst
Normal file
219
samples/net/secure_mqtt_sensor_actuator/README.rst
Normal file
|
@ -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
|
||||||
|
<https://test.mosquitto.org/> 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 <test.mosquitto.org/local broker IP> -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 <test.mosquitto.org/local broker IP> --cafile <path/to/ca.crt> -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] <inf> app_device: Device adt7420@48 is ready
|
||||||
|
[00:00:00.181,000] <inf> app_device: Device leds is ready
|
||||||
|
[00:00:00.181,000] <inf> app_main: MAC Address: 00:E0:FE:FE:DA:C8
|
||||||
|
[00:00:00.181,000] <inf> app_main: Bringing up network..
|
||||||
|
[00:00:00.801,000] <inf> net_dhcpv4: Received: 192.168.1.17
|
||||||
|
[00:00:00.801,000] <inf> app_main: Network connectivity up!
|
||||||
|
[00:00:00.818,000] <inf> app_mqtt: Connecting to MQTT broker @ 91.121.93.94
|
||||||
|
[00:00:01.154,000] <inf> net_mqtt: Connect completed
|
||||||
|
[00:00:01.197,000] <inf> app_mqtt: Connected to MQTT broker!
|
||||||
|
[00:00:01.197,000] <inf> app_mqtt: Hostname: test.mosquitto.org
|
||||||
|
[00:00:01.198,000] <inf> app_mqtt: Client ID: adi_eval_adin1110ebz_9a
|
||||||
|
[00:00:01.198,000] <inf> app_mqtt: Port: 8883
|
||||||
|
[00:00:01.198,000] <inf> app_mqtt: TLS: Enabled
|
||||||
|
[00:00:01.198,000] <inf> app_mqtt: Subscribing to 1 topic(s)
|
||||||
|
[00:00:01.238,000] <inf> app_mqtt: SUBACK packet ID: 5841
|
||||||
|
[00:00:04.200,000] <inf> app_mqtt: Published to topic 'zephyr_sample/sensor', QoS 1
|
||||||
|
[00:00:04.319,000] <inf> app_mqtt: PUBACK packet ID: 1
|
||||||
|
[00:00:07.202,000] <inf> app_mqtt: Published to topic 'zephyr_sample/sensor', QoS 1
|
||||||
|
[00:00:07.323,000] <inf> app_mqtt: PUBACK packet ID: 2
|
||||||
|
[00:00:10.204,000] <inf> app_mqtt: Published to topic 'zephyr_sample/sensor', QoS 1
|
||||||
|
[00:00:10.322,000] <inf> app_mqtt: PUBACK packet ID: 3
|
||||||
|
[00:00:12.769,000] <inf> app_mqtt: MQTT payload received!
|
||||||
|
[00:00:12.769,000] <inf> app_mqtt: topic: 'zephyr_sample/command', payload: led_on
|
||||||
|
[00:00:12.770,000] <inf> app_device: Executing device command: led_on
|
||||||
|
|
||||||
|
.. _Eclipse Mosquitto: https://mosquitto.org/download/
|
|
@ -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;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
|
@ -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 <board> -- -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"
|
|
@ -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"
|
59
samples/net/secure_mqtt_sensor_actuator/prj.conf
Normal file
59
samples/net/secure_mqtt_sensor_actuator/prj.conf
Normal file
|
@ -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
|
33
samples/net/secure_mqtt_sensor_actuator/sample.yaml
Normal file
33
samples/net/secure_mqtt_sensor_actuator/sample.yaml
Normal file
|
@ -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
|
138
samples/net/secure_mqtt_sensor_actuator/src/device.c
Normal file
138
samples/net/secure_mqtt_sensor_actuator/src/device.c
Normal file
|
@ -0,0 +1,138 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2024 Analog Devices, Inc.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <zephyr/logging/log.h>
|
||||||
|
LOG_MODULE_REGISTER(app_device, LOG_LEVEL_DBG);
|
||||||
|
|
||||||
|
#include <zephyr/kernel.h>
|
||||||
|
#include <zephyr/drivers/sensor.h>
|
||||||
|
#include <zephyr/drivers/led.h>
|
||||||
|
#include <zephyr/random/random.h>
|
||||||
|
|
||||||
|
#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;
|
||||||
|
}
|
56
samples/net/secure_mqtt_sensor_actuator/src/device.h
Normal file
56
samples/net/secure_mqtt_sensor_actuator/src/device.h
Normal file
|
@ -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__ */
|
140
samples/net/secure_mqtt_sensor_actuator/src/main.c
Normal file
140
samples/net/secure_mqtt_sensor_actuator/src/main.c
Normal file
|
@ -0,0 +1,140 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2024 Analog Devices, Inc.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <zephyr/logging/log.h>
|
||||||
|
LOG_MODULE_REGISTER(app_main, LOG_LEVEL_DBG);
|
||||||
|
|
||||||
|
#include <zephyr/kernel.h>
|
||||||
|
#include <zephyr/net/mqtt.h>
|
||||||
|
#include <zephyr/net/net_if.h>
|
||||||
|
#include <zephyr/net/net_mgmt.h>
|
||||||
|
#include <zephyr/net/conn_mgr_monitor.h>
|
||||||
|
|
||||||
|
#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;
|
||||||
|
}
|
510
samples/net/secure_mqtt_sensor_actuator/src/mqtt_client.c
Normal file
510
samples/net/secure_mqtt_sensor_actuator/src/mqtt_client.c
Normal file
|
@ -0,0 +1,510 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2024 Analog Devices, Inc.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <zephyr/logging/log.h>
|
||||||
|
LOG_MODULE_REGISTER(app_mqtt, LOG_LEVEL_DBG);
|
||||||
|
|
||||||
|
#include <zephyr/kernel.h>
|
||||||
|
#include <zephyr/net/socket.h>
|
||||||
|
#include <zephyr/net/mqtt.h>
|
||||||
|
#include <zephyr/data/json.h>
|
||||||
|
#include <zephyr/random/random.h>
|
||||||
|
|
||||||
|
#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;
|
||||||
|
}
|
43
samples/net/secure_mqtt_sensor_actuator/src/mqtt_client.h
Normal file
43
samples/net/secure_mqtt_sensor_actuator/src/mqtt_client.h
Normal file
|
@ -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__ */
|
|
@ -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__ */
|
Loading…
Add table
Add a link
Reference in a new issue