Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions .github/actions/build-step/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,10 @@ inputs:
type: boolean
required: false
default: false
mqtt:
type: boolean
required: false
default: false

runs:
using: "composite"
Expand All @@ -57,6 +61,11 @@ runs:
-d build \
-p --sysbuild \
-- -DEXTRA_CONF_FILE="overlay-debug-att.conf" 2>&1 | tee ../../artifacts/build_output_${{ inputs.short_board }}_debug.log
elif [[ "${{ inputs.mqtt }}" == "true" ]]; then
west build -b ${{ inputs.board }} \
-d build \
-p --sysbuild \
-- -DEXTRA_CONF_FILE="$(pwd)/../examples/modules/cloud/overlay-mqtt.conf" 2>&1 | tee ../../artifacts/build_output_${{ inputs.short_board }}_mqtt.log
else
west build -b ${{ inputs.board }} \
-d build \
Expand Down
11 changes: 11 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,17 @@ jobs:
path: asset-tracker-template/app
debug: true

# Asset Tracker Template firmware build with MQTT cloud moduel for Thingy91x
- name: Build thingy91x firmware with MQTT cloud module
if: ${{ github.event_name != 'pull_request' }}
uses: ./asset-tracker-template/.github/actions/build-step
with:
board: thingy91x/nrf9151/ns
short_board: thingy91x
version: ${{ env.VERSION }}-mqtt
path: asset-tracker-template/app
mqtt: true

# Asset Tracker Template firmware build with patches for Thingy91x
- name: Apply patches for Thingy91x firmware
if: ${{ github.event_name != 'pull_request' }}
Expand Down
5 changes: 1 addition & 4 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,4 @@
# This CMake file is picked by the Zephyr build system because it is defined
# as the module CMake entry point (see zephyr/module.yml).


zephyr_include_directories(include)

add_subdirectory(drivers)
add_subdirectory(examples/modules/cloud)
8 changes: 8 additions & 0 deletions Kconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# Copyright (c) 2025 Nordic Semiconductor ASA
# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
#
# This Kconfig file is picked by the Zephyr build system because it is defined
# as the module Kconfig entry point (see zephyr/module.yml). You can browse
# module options by going to Zephyr -> Modules in Kconfig.

rsource "examples/modules/cloud/Kconfig.cloud_mqtt"
8 changes: 4 additions & 4 deletions app/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,10 @@ target_include_directories(app PRIVATE src/common)
# Add main application source
target_sources(app PRIVATE src/main.c)

# Include mandatory module source folders
# Module source folders
add_subdirectory(src/modules/location)
add_subdirectory(src/modules/cloud)
add_subdirectory(src/modules/fota)
add_subdirectory(src/modules/network)
add_subdirectory(src/modules/button)
add_subdirectory(src/cbor)
Expand All @@ -24,6 +27,3 @@ add_subdirectory(src/cbor)
add_subdirectory_ifdef(CONFIG_APP_POWER src/modules/power)
add_subdirectory_ifdef(CONFIG_APP_ENVIRONMENTAL src/modules/environmental)
add_subdirectory_ifdef(CONFIG_APP_LED src/modules/led)
add_subdirectory_ifdef(CONFIG_APP_LOCATION src/modules/location)
add_subdirectory_ifdef(CONFIG_APP_CLOUD src/modules/cloud)
add_subdirectory_ifdef(CONFIG_APP_FOTA src/modules/fota)
2 changes: 1 addition & 1 deletion app/src/modules/cloud/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
#

target_sources(app PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/cloud.c)
target_sources_ifdef(CONFIG_APP_CLOUD app PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/cloud.c)
target_sources_ifdef(CONFIG_APP_CLOUD_SHELL app PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/cloud_shell.c)
target_include_directories(app PRIVATE .)

Expand Down
2 changes: 1 addition & 1 deletion app/src/modules/fota/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,5 @@
# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
#

target_sources(app PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/fota.c)
target_sources_ifdef(CONFIG_APP_FOTA app PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/fota.c)
target_include_directories(app PRIVATE .)
2 changes: 1 addition & 1 deletion app/src/modules/location/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,5 @@
# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
#

target_sources(app PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/location.c)
target_sources_ifdef(CONFIG_APP_LOCATION app PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/location.c)
target_include_directories(app PRIVATE .)
105 changes: 105 additions & 0 deletions docs/common/customization.md
Original file line number Diff line number Diff line change
Expand Up @@ -424,3 +424,108 @@ The dummy module is now ready to use. It provides the following functionality:
To test the module, send a `DUMMY_SAMPLE_REQUEST` message to its zbus channel. The module will respond with a `DUMMY_SAMPLE_RESPONSE` containing the incremented counter value.

This dummy module serves as a template that you can extend to implement more complex functionality. You can add additional message types, state variables, and processing logic as needed for your specific use case.

## Using a different Cloud

To connect to a generic MQTT server using the Asset Tracker Template, you can use the example cloud module provided under `examples/modules/cloud`. This module replaces the default nRF Cloud CoAP-based cloud integration with a flexible MQTT client implementation.

### Overview

- **Location and FOTA**: These features are deactivated when using the example MQTT module because they depend on nRF Cloud CoAP.
- **FOTA and LOCATION channels**: The MQTT cloud module provides stub channel declarations for FOTA and LOCATION to avoid build errors. You can implement your own FOTA and LOCATION modules based on your chosen cloud service if needed.
- **MQTT Client default configurations:**

- **Broker hostname:** mqtt.nordicsemi.academy
- **Port:** 8883
- **TLS:** Yes
- **Authentication:** Server only
- **CA:** modules/examples/cloud/creds/ca-cert.pem
- **Device ID** IMEI
- **Subscribed topic** imei/att-pub-topic
- **Publishing toptic** imei/att-sub-topic

The default configuration does not require/enable mutual authentication meaning that the device does not authenticate itself to the server.
That would require a device certificate/private key pair. However, the server is authenticated using the server certificate `ca-cert.pem`located in the module example folder.

### Configuration

Configurations for the MQTT stack can be set in the `overlay-mqtt.conf` file and Kconfig options defined in `examples/modules/cloud/Kconfig.cloud_mqtt`.
Some of the available options for controlling the MQTT module are:

- `CONFIG_APP_CLOUD_MQTT`
- `CONFIG_APP_CLOUD_MQTT_HOSTNAME`
- `CONFIG_APP_CLOD_MQTT_TOPIC_SIZE_MAX`
- `CONFIG_APP_CLOUD_MQTT_PUB_TOPIC`
- `CONFIG_APP_CLOUD_MQTT_SUB_TOPIC`
- `CONFIG_APP_CLOUD_MQTT_SEC_TAG`
- `CONFIG_APP_CLOUD_MQTT_SHELL`
- `CONFIG_APP_CLOUD_PAYLOAD_BUFFER_MAX_SIZE`
- `CONFIG_APP_CLOUD_SHADOW_RESPONSE_BUFFER_MAX_SIZE`
- `CONFIG_APP_CLOUD_BACKOFF_INITIAL_SECONDS`
- `CONFIG_APP_CLOUD_BACKOFF_TYPE_LINEAR`
- `CONFIG_APP_CLOUD_BACKOFF_TYPE_EXPONENTIAL`
- `CONFIG_APP_CLOUD_BACKOFF_TYPE_NONE`
- `CONFIG_APP_CLOUD_BACKOFF_LINEAR_INCREMENT_SECONDS`
- `CONFIG_APP_CLOUD_BACKOFF_MAX_SECONDS`
- `CONFIG_APP_CLOUD_THREAD_STACK_SIZE`
- `CONFIG_APP_CLOUD_MESSAGE_QUEUE_SIZE`
- `CONFIG_APP_CLOUD_WATCHDOG_TIMEOUT_SECONDS`
- `CONFIG_APP_CLOUD_MSG_PROCESSING_TIMEOUT_SECONDS`

### How to use the MQTT Cloud Example

1. **Build and flash with the MQTT overlay**

In the template's `app` folder, run:

```sh
west build -p -b thingy91x/nrf9151/ns -- -DEXTRA_CONF_FILE="$(PWD)/../examples/modules/cloud/overlay-mqtt.conf" && west flash --erase --skip-rebuild
```

2. **Observe that the device connects to the broker**

3. **Test using shell commands**

```bash
uart:~$ att_cloud_publish_mqtt test-payload
Sending on payload channel: "data":"test-payload","ts":1746534066186 (40 bytes)
[00:00:18.607,421] <dbg> cloud: on_cloud_payload_json: MQTT Publish Details:
[00:00:18.607,482] <dbg> cloud: on_cloud_payload_json: -Payload: "data":"test-payload","ts":1746534066186
[00:00:18.607,513] <dbg> cloud: on_cloud_payload_json: -Payload Length: 40
[00:00:18.607,543] <dbg> cloud: on_cloud_payload_json: -Topic: 359404230261381/att-pub-topic
[00:00:18.607,574] <dbg> cloud: on_cloud_payload_json: -Topic Size: 29
[00:00:18.607,635] <dbg> cloud: on_cloud_payload_json: -QoS: 1
[00:00:18.607,635] <dbg> cloud: on_cloud_payload_json: -Message ID: 1
[00:00:18.607,696] <dbg> mqtt_helper: mqtt_helper_publish: Publishing to topic: 359404230261381/att-pub-topic
[00:00:19.141,235] <dbg> mqtt_helper: mqtt_evt_handler: MQTT_EVT_PUBACK: id = 1 result = 0
[00:00:19.141,265] <dbg> cloud: on_mqtt_puback: Publish acknowledgment received, message id: 1
[00:00:19.141,296] <dbg> mqtt_helper: mqtt_helper_poll_loop: Polling on socket fd: 0
[00:00:48.653,503] <dbg> mqtt_helper: mqtt_helper_poll_loop: Polling on socket fd: 0
[00:00:49.587,463] <dbg> mqtt_helper: mqtt_evt_handler: MQTT_EVT_PINGRESP
[00:00:49.587,493] <dbg> mqtt_helper: mqtt_helper_poll_loop: Polling on socket fd: 0
[00:01:18.697,692] <dbg> mqtt_helper: mqtt_helper_poll_loop: Polling on socket fd: 0
[00:01:19.350,921] <dbg> mqtt_helper: mqtt_evt_handler: MQTT_EVT_PINGRESP
```

The MQTT cloud module includes a shell module/commands for testing cloud publishing much like the default CoAP configuration.
To implement custom cloud shell commands this module can be used `examples/modules/cloud/cloud_mqtt_shell.c`.

### **Module State Machine**

The cloud MQTT module uses a state machine to manage connection and reconnection logic. Below is a mermaid diagram representing the module's states:
```mermaid
stateDiagram-v2
[*] --> STATE_RUNNING
STATE_RUNNING --> STATE_DISCONNECTED : NETWORK_DISCONNECTED
STATE_DISCONNECTED --> STATE_CONNECTING : NETWORK_CONNECTED
STATE_CONNECTING --> STATE_CONNECTING_ATTEMPT
STATE_CONNECTING_ATTEMPT --> STATE_CONNECTING_BACKOFF : CLOUD_CONN_FAILED
STATE_CONNECTING_BACKOFF --> STATE_CONNECTING_ATTEMPT : CLOUD_BACKOFF_EXPIRED
STATE_CONNECTING_ATTEMPT --> STATE_CONNECTED : CLOUD_CONN_SUCCESS
STATE_CONNECTED --> STATE_DISCONNECTED : NETWORK_DISCONNECTED
STATE_CONNECTED --> STATE_CONNECTED : PAYLOAD_CHAN / send_data()
STATE_CONNECTED --> STATE_CONNECTED : <module>_SAMPLE_RESPONSE / send_<module>_data()
STATE_CONNECTED --> STATE_CONNECTED : NETWORK_CONNECTED / (noop)
STATE_CONNECTED --> STATE_DISCONNECTED : exit / mqtt_helper_disconnect()
```
6 changes: 3 additions & 3 deletions docs/patches/dummy-module.patch
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ index e1948cc..8d20bcd 100644
--- a/app/CMakeLists.txt
+++ b/app/CMakeLists.txt
@@ -27,3 +27,4 @@ add_subdirectory_ifdef(CONFIG_APP_LED src/modules/led)
add_subdirectory_ifdef(CONFIG_APP_LOCATION src/modules/location)
add_subdirectory_ifdef(CONFIG_APP_CLOUD src/modules/cloud)
add_subdirectory_ifdef(CONFIG_APP_FOTA src/modules/fota)
add_subdirectory_ifdef(CONFIG_APP_POWER src/modules/power)
add_subdirectory_ifdef(CONFIG_APP_ENVIRONMENTAL src/modules/environmental)
add_subdirectory_ifdef(CONFIG_APP_LED src/modules/led)
+add_subdirectory_ifdef(CONFIG_APP_DUMMY src/modules/dummy)
diff --git a/app/Kconfig b/app/Kconfig
index dad4070..684fe73 100644
Expand Down
21 changes: 21 additions & 0 deletions examples/modules/cloud/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
#
# Copyright (c) 2025 Nordic Semiconductor ASA
#
# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
#

# Prevent this example module from being included in images that doesn't enable the MQTT cloud module
if (NOT CONFIG_APP_CLOUD_MQTT)
return()
endif()

add_subdirectory(creds)

zephyr_sources(${CMAKE_CURRENT_SOURCE_DIR}/cloud_mqtt.c)
zephyr_sources(${CMAKE_CURRENT_SOURCE_DIR}/cloud_mqtt_shell.c)

if (CONFIG_NRF_CLOUD_COAP_SEC_TAG GREATER_EQUAL 2147483648 AND CONFIG_NRF_CLOUD_COAP_SEC_TAG LESS_EQUAL 2147483667)
message(WARNING "CONFIG_NRF_CLOUD_COAP_SEC_TAG is set to a developer security tag. "
"TLS traffic can now be decrypted with Nordic tools. "
"This should only be used during development and testing.")
endif()
142 changes: 142 additions & 0 deletions examples/modules/cloud/Kconfig.cloud_mqtt
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
#
# Copyright (c) 2025 Nordic Semiconductor
#
# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
#

menuconfig APP_CLOUD_MQTT
bool "Cloud module"
depends on MQTT_HELPER
depends on HW_ID_LIBRARY

if APP_CLOUD_MQTT

config APP_CLOUD_MQTT_HOSTNAME
string "MQTT broker hostname"
default "mqtt.nordicsemi.academy"
help
Hostname of the MQTT broker to connect to.

config APP_CLOD_MQTT_TOPIC_SIZE_MAX
int "MQTT topic size"
default 64
help
Maximum size of the MQTT topics used for publishing and subscribing to messages.

config APP_CLOUD_MQTT_PUB_TOPIC
string "MQTT publish topic"
default "att-pub-topic"
help
Topic used for publishing messages to the MQTT broker.

config APP_CLOUD_MQTT_SUB_TOPIC
string "MQTT subscribe topic"
default "att-sub-topic"
help
Topic used for subscribing to messages from the MQTT broker.

config APP_CLOUD_MQTT_SEC_TAG
int "MQTT security tag"
default 888
help
Security tag used to store the MQTT credentials in the modem using Modem Key Management API

config APP_CLOUD_MQTT_SHELL
bool "Enable cloud shell"
default y if SHELL
help
Enable cloud shell commands.

config APP_CLOUD_PAYLOAD_BUFFER_MAX_SIZE
int "Payload maximum buffer size"
default 128
help
Maximum size of the buffer sent over the payload channel when sending RAW JSON messages
to the cloud.

config APP_CLOUD_SHADOW_RESPONSE_BUFFER_MAX_SIZE
int "Payload maximum buffer size"
default 512
help
Maximum size of the buffer used to receive shadow responses from the cloud.

config APP_CLOUD_BACKOFF_INITIAL_SECONDS
int "Reconnection backoff time in seconds"
default 60
help
Time in between reconnection attempts to the MQTT server.
The timer starts after the last failed attempt.

choice APP_CLOUD_BACKOFF_TYPE
prompt "Reconnection backoff type"
default APP_CLOUD_BACKOFF_TYPE_LINEAR

config APP_CLOUD_BACKOFF_TYPE_EXPONENTIAL
bool "Exponential backoff"
help
Exponential backoff doubles the reconnection timeout after each failed attempt.
The maximum reconnection timeout is defined by APP_CLOUD_BACKOFF_MAX_SECONDS.

config APP_CLOUD_BACKOFF_TYPE_LINEAR
bool "Linear backoff"
help
Linear backoff adds a fixed amount of time to the reconnection timeout after each failed attempt,
as defined by APP_CLOUD_BACKOFF_LINEAR_INCREMENT_SECONDS.

config APP_CLOUD_BACKOFF_TYPE_NONE
bool "No backoff"
help
No backoff means that the reconnection timeout is constant at the value defined by
APP_CLOUD_BACKOFF_INITIAL_SECONDS.

endchoice

config APP_CLOUD_BACKOFF_LINEAR_INCREMENT_SECONDS
int "Reconnection backoff time increment"
default 60
help
Time added to the reconnection timeout after each failed attempt in seconds.
The maximum reconnection timeout is defined by APP_CLOUD_BACKOFF_MAX_SECONDS.

config APP_CLOUD_BACKOFF_MAX_SECONDS
int "Maximum reconnection timeout"
default 3600
help
Maximum reconnection backoff value in seconds.

config APP_CLOUD_THREAD_STACK_SIZE
int "Thread stack size"
default 3328

config APP_CLOUD_MESSAGE_QUEUE_SIZE
int "Message queue size"
default 5
help
ZBus subscriber message queue size.

config APP_CLOUD_WATCHDOG_TIMEOUT_SECONDS
int "Watchdog timeout"
default 180
help
Timeout in seconds for the cloud module watchdog.
The timeout given in this option covers both:
* Waiting for an incoming message in zbus_sub_wait_msg().
* Time spent processing the message, defined by
CONFIG_APP_CLOUD_MSG_PROCESSING_TIMEOUT_SECONDS.
Ensure that this value exceeds CONFIG_APP_CLOUD_MSG_PROCESSING_TIMEOUT_SECONDS.
A small difference between the two can mean more frequent watchdog feeds, which increases
power consumption.


config APP_CLOUD_MSG_PROCESSING_TIMEOUT_SECONDS
int "Maximum message processing time"
default 120
help
Maximum time allowed for processing a single message in the module's state machine.
The value must be smaller than CONFIG_APP_CLOUD_WATCHDOG_TIMEOUT_SECONDS.

module = APP_CLOUD_MQTT
module-str = Cloud MQTT
source "subsys/logging/Kconfig.template.log_config"

endif # APP_CLOUD_MQTT
Loading
Loading