nRF9160: nRF Cloud MQTT multi-service

This sample is a minimal, error tolerant, integrated demonstration of the nRF Cloud, Location, and AT Host libraries. It demonstrates how you can use these libraries together to support Firmware-Over-The-Air (FOTA), Location Services, periodic sensor samples, and more in your nRF Cloud-enabled application. It also demonstrates how to implement error tolerance in your cellular applications without relying on reboot loops.


The sample supports the following development kits:

Hardware platforms


Board name

Build target





nRF9160 DK




When built for an _ns build target, the sample is configured to compile and run as a non-secure application with Cortex-M Security Extensions enabled. Therefore, it automatically includes Trusted Firmware-M that prepares the required peripherals and secure services to be available for the application.

External flash

To use the external flash memory on the nRF9160 DK v0.14.0 or later versions, the board controller firmware must be of version v2.0.1. This is the factory firmware version. If you need to program the board controller firmware again, complete the following steps:

  1. Download the nRF9160 DK board controller firmware from the nRF9160 DK downloads page.

  2. Make sure the PROG/DEBUG SW10 switch on the nRF9160 DK is set to nRF52.

  3. Program the board controller firmware (nrf9160_dk_board_controller_fw_2.0.1.hex) using the Programmer app in nRF Connect for Desktop.


The board controller firmware version must be v2.0.1 or higher, which enables the pin routing to external flash.

See Board controller for more details.


This sample implements or demonstrates the following features:

Structure and theory of operation

This sample is separated into a number of smaller functional units. The top level functional unit and entry point for the sample is the src/main.c file. This file starts three primary threads, each with a distinct function:

src/main.c also optionally starts a fourth thread, the led_thread, which animates any onboard LEDs if LED status indication is enabled.

Connection management loop

The connection management loop performs the following four primary tasks:

  1. Configures and activates the cellular modem.

  2. Starts a connection to the LTE network.

  3. Maintains a connection to nRF Cloud.

  4. Re-establishes or retries all lost or failed connections.

The modem only needs to be set up and connected to LTE once. If LTE is lost, the modem automatically works to re-establish it.

The connection to nRF Cloud must be maintained manually by the application. A significant portion of the src/connection.c file is dedicated to this task.

This file also maintains a number of Zephyr Events tracking the state of the LTE and nRF Cloud connections. The connection management loop relies on these events to control its progression.

The connection management loop must conform to the nRF Cloud connection process. This is mostly handled by the nRF Cloud library, with exception to the following behavior: When a device is first being associated with an nRF Cloud account, the nRF Cloud MQTT API will send a user association request notification to the device. Upon receiving this notification, the device must wait for a user association success notification, and then manually disconnect from and reconnect to nRF Cloud. Notifications of user association success are sent for every subsequent connection after this as well, so the device must only disconnect and reconnect if it previously received a user association request notification. This behavior is handled by the NRF_CLOUD_EVT_USER_ASSOCIATION_REQUEST and NRF_CLOUD_EVT_USER_ASSOCIATED cases inside the cloud_event_handler() function in the src/connection.c file.

The application must also manually reset its connection to nRF Cloud whenever LTE is lost. Otherwise, the nRF Cloud library will deadlock. The LTE_LC_EVT_NW_REG_STATUS case inside the lte_event_handler() function performs this task.

Upon startup, the connection management loop also updates the Device Shadow. This is performed in the update_shadow() function of the src/connection.c file.

Device message queue

Any thread may submit device messages to the device message queue, where they are stored until a working connection to nRF Cloud is established. Once this happens, the message thread transmits all enqueued device messages, one at a time and in fast succession, to nRF Cloud. If an enqueued message fails to send, it will be sent back to the queue and tried again later. If more than CONFIG_MAX_CONSECUTIVE_SEND_FAILURES messages fail to send in a row, the connection to nRF Cloud is reset and re-established after a short delay. The transmission pauses again whenever connection to nRF Cloud is lost. Management of the device message queue is implemented entirely in the src/connection.c file.

Application thread and main application loop

The application thread is implemented in the src/application.c file, and is responsible for the high-level behavior of this sample. It performs the following major tasks:


Periodic location tracking is handled by the Location library once it has been requested, whereas temperature samples are individually requested by the Main Application Loop.


The FOTA update support is almost entirely implemented by enabling the CONFIG_NRF_CLOUD_FOTA option (which is implicitly enabled by CONFIG_NRF_CLOUD_MQTT).

However, even with CONFIG_NRF_CLOUD_FOTA enabled, applications must still reboot themselves manually after FOTA download completion, and must still update their Device Shadow to reflect FOTA support.

Reboot after download completion is handled by the src/fota_support.c file, triggered by a call from the src/connection.c file.

Device Shadow updates are performed in the src/connection.c file.

In a real-world setting, these two behaviors could be directly implemented in the src/connection.c file. In this sample, they are separated for clarity.

This sample supports full modem FOTA for the nRF9160 development kit version 0.14.0 and higher. To enable full modem FOTA, add the following parameter to your build command:


Also, specify your development kit version by appending it to the board name. For example, if your development kit version is 1.0.1, use the following board name in your build command:


This sample also supports placement of the MCUboot secondary partition in external flash for the nRF9160 development kit version 0.14.0 and higher. To enable this, add the following parameter to your build command:


Then specify your development kit version as described earlier.

Temperature sensing

Temperature sensing is mostly implemented in the src/temperature.c file. This includes generation of false temperature readings on the Nordic nRF9160 DK, which does not have a built-in temperature sensor.

Using the built-in temperature sensor of the Nordic Thingy:91 requires a devicetree overlay file, namely the boards/thingy91_nrf9160_ns.overlay file, as well as enabling the Kconfig options CONFIG_SENSOR and CONFIG_BME680. The devicetree overlay file is automatically applied during compilation whenever the thingy91_nrf9160_ns target is selected. The required Kconfig options are implicitly enabled by CONFIG_TEMP_DATA_USE_SENSOR.


For temperature readings to be visible in the nRF Cloud portal, they must be marked as enabled in the Device Shadow. This is performed by the src/connection.c file.

Location tracking

All matters concerning location tracking are handled in the src/location_tracking.c file. This involves setting up a periodic location request, and then passing the results to a callback configured by the src/application.c file.

For location readings to be visible in the nRF Cloud portal, they must be marked as enabled in the Device Shadow. This is performed by the src/connection.c file.

Each enabled location method is tried in the following order until one succeeds: GNSS, Wi-Fi, then cellular.

The GNSS and cellular location tracking methods are enabled by default and will work on both Thingy:91 and nRF9160 DK targets.

The Wi-Fi location tracking method is not enabled by default and requires the nRF7002 Wi-Fi companion chip.

When enabled, this location method scans the MAC addresses of nearby access points and submits them to nRF Cloud to obtain a location estimate.

See Building with nRF7002 EK Wi-Fi scanning support (for nRF9160 DK) for details on how to enable Wi-Fi location tracking.

This sample supports placing P-GPS data in external flash for the nRF9160 development kit version 0.14.0 and later. To enable this, add the following parameter to your build command:


Also, specify your development kit version by appending it to the board name. For example, if your development kit version is 1.0.1, use the following board name in your build command:


Remote execution of modem AT commands

If the CONFIG_AT_CMD_REQUESTS Kconfig option is enabled, you can remotely execute modem AT commands on your device by sending a device message with appId MODEM, messageType CMD, and the data key set to the command you would like to execute.

The application executes the command stored in the data key, and responds with a device message containing either an error code or the response from the modem to the AT command.

For example, if you send the following device message to a device running this sample with CONFIG_AT_CMD_REQUESTS enabled:

{"appId":"MODEM", "messageType":"CMD", "data":"AT+CGMR"}

It executes the modem AT command AT+CGMR and sends a device message similar to the following back to nRF Cloud:

   "appId": "MODEM",
   "messageType": "DATA",
   "ts": 1669244834095,
   "data": "mfw_nrf9160_1.3.2\r\nOK"

To do this in the nRF Cloud portal, write {“appId”:”MODEM”, “messageType”:”CMD”, “data”:”AT+CGMR”} into the Send a message box of the Terminal card and click Send.

LED status indication

On boards that support LED status indication, this sample can indicate its current status with any on-board LEDs.

This is performed by a background thread implemented in the src/led_control.c file.

Other threads may request either a temporary or indefinite LED pattern. This wakes up the led_thread, which begins animating the requested pattern, sleeping for 100 milliseconds at a time between animation frames, until the requested pattern has completed (if it is temporary), or until a new pattern is requested in its place.

This feature is enabled by default for the thingy91_nrf9160_ns (Thingy:91) and nrf9160dk_nrf9160_ns (nRF9160 DK) targets.

The patterns displayed, the states they describe, and the options required for them to appear are as follows:



nRF9160 DK


Trying to connect to nRF Cloud (for the first time)

Pulsating orange LED

Single LED lit, spinning around the square of LEDs

LED status indication is enabled

Connection to nRF Cloud lost, reconnecting

Pulsating orange LED

Single LED lit, spinning around the square of LEDs


Fatal error

Blinking red LED, 75% duty cycle

All four LEDs blinking, 75% duty cycle

LED status indication is enabled

Device message sent successfully

Blinking green LED, 25% duty cycle

Alternating checkerboard pattern (two LEDs are lit at a time, either LED1 and LED4, or LED2 and LED3)



LED pulsating between blue and cyan

Single LED lit, bouncing between opposite corners (LED1 and LED4)


Under all other circumstances, on-board LEDs are turned off.



See Customizing LED status indication for details on customizing the LED behavior.

See Configuring LED status indication for third-party boards for details on configuring LED status indication on third-party boards.

Test counter

You can enable a test counter by enabling the CONFIG_TEST_COUNTER option. Every time a sensor sample is sent, this counter is incremented by one, and its current value is sent to nRF Cloud. A plot of the value of the counter over time is automatically shown in the nRF Cloud portal. This plot is useful for tracking, visualizing, and debugging connection loss, resets, and re-establishment behavior.

Device message formatting

This sample constructs JSON-based device messages using cJSON.

While any valid JSON string can be sent as a device message, and accepted and stored by nRF Cloud, there are some pre-designed message structures, known as schemas. The nRF Cloud portal knows how to interpret these schemas. These schemas are described in nRF Cloud application protocols for long-range devices. The device messages constructed in the src/application.c file all adhere to the general message schema.

GNSS and temperature data device messages conform to the gnss and temp deviceToCloud schemas respectively.


See Configuring your application for information about how to permanently or temporarily change the configuration.

Disabling key features

The following key features of this sample may be independently disabled:

If you disable both GNSS and cellular-based location tracking, location tracking is completely disabled.


MQTT should only be used with applications that need to stay connected constantly or transfer data frequently. While this sample does allow its core features to be slowed or completely disabled, in real-world applications, you should carefully consider your data throughput and whether MQTT is an appropriate solution. If you want to disable or excessively slow all of these features for a real-world application, other solutions, such as the nRF Cloud Rest API, may be more appropriate.

Customizing GNSS antenna configuration

This sample uses the Modem antenna library, which is enabled by default for builds targeting either the nrf9160dk_nrf9160_ns or thingy91_nrf9160_ns board names.

If you are using a different board or build target, or would like to use a custom or external GNSS antenna, see the Modem antenna library documentation for configuration instructions.

Enable CONFIG_MODEM_ANTENNA_GNSS_EXTERNAL to use an external antenna.

Customizing LED status indication

To disable LED status indication (other than the selected idle behavior) after a connection to nRF Cloud has been established at least once, disable CONFIG_LED_VERBOSE_INDICATION.

To turn the LED off while the sample is idle (rather than show an idle pattern), disable CONFIG_LED_CONTINUOUS_INDICATION.

If you disable both of these options together, the status indicator LED remains off after a connection to nRF Cloud has been established at least once.

Configuring LED status indication for third-party boards

This sample assumes that the target board either has a single RGB LED with PWM support, or four discrete LEDs available.

For third-party boards, you can select the RGB LED option by enabling both the CONFIG_LED_INDICATION_PWM and CONFIG_LED_INDICATION_RGB options. In this case, the board must have a devicetree entry marked as compatible with the Zephyr pwm-leds driver.

Otherwise, the four-LED option (CONFIG_LED_INDICATION_GPIO and CONFIG_LED_INDICATOR_4LED) is selected by default as long as there is a devicetree entry compatible with the Zephyr gpio-leds driver.

The four-LED option should work even if there are not four LEDs available, as long as an appropriate devicetree entry exists. However, if fewer than four LEDs are available, the patterns may be difficult to identify.

To add your own LED indication implementations, you can add values to the LED_INDICATOR Kconfig choice and modify the src/led_control.c file accordingly.

To disable LED indication, enable the CONFIG_LED_INDICATION_DISABLED option.

For examples of how to set up devicetree entries compatible with the Zephyr gpio-leds and pwm-leds drivers, see the files zephyr/boards/arm/nrf9160dk_nrf9160/nrf9160dk_nrf9160_common.dts and zephyr/boards/arm/thingy91_nrf9160/thingy91_nrf9160_common.dts. Search for nodes with compatible = "gpio-leds"; and compatible = "pwm-leds"; respectively.

Useful debugging options

To see all debug output for this sample, enable the CONFIG_MQTT_MULTI_SERVICE_LOG_LEVEL_DBG option.

To monitor the GNSS module (for instance, to see whether A-GPS or P-GPS assistance data is being consumed), enable the CONFIG_NRF_CLOUD_GPS_LOG_LEVEL_DBG option.

See also the Test counter.

Configuration options

Set the following configuration options for the sample:


Sets the log level for this sample to debug.


Requests Power Saving Mode from cellular network when enabled.

CONFIG_LTE_INIT_RETRY_TIMEOUT_SECONDS - LTE initialization retry timeout

Sets the number of seconds between each LTE modem initialization retry.

CONFIG_CLOUD_CONNECTION_RETRY_TIMEOUT_SECONDS - Cloud connection retry timeout (seconds)

Sets the cloud connection retry timeout in seconds.

CONFIG_CLOUD_CONNECTION_REESTABLISH_DELAY_SECONDS - Cloud connection re-establishment delay (seconds)

Sets the connection re-establishment delay in seconds. When the connection to nRF Cloud has been reset, wait for this amount of time before a new attempt to connect.

CONFIG_CLOUD_READY_TIMEOUT_SECONDS - Cloud readiness timeout (seconds)

Sets the cloud readiness timeout in seconds. If the connection to nRF Cloud does not become ready within this timeout, the sample will reset its connection and try again.

CONFIG_DATE_TIME_ESTABLISHMENT_TIMEOUT_SECONDS - Modem date and time establishment timeout (seconds)

Sets the timeout for modem date and time establishment (in seconds). The sample waits for this number of seconds for the modem to determine the current date and time before giving up and moving on.

CONFIG_APPLICATION_THREAD_STACK_SIZE - Application Thread Stack Size (bytes)

Sets the stack size (in bytes) for the application thread of the sample.

CONFIG_CONNECTION_THREAD_STACK_SIZE - Connection Thread Stack Size (bytes)

Sets the stack size (in bytes) for the connection thread of the sample.

CONFIG_MESSAGE_THREAD_STACK_SIZE - Message Queue Thread Stack Size (bytes)

Sets the stack size (in bytes) for the message queue processing thread of the sample.

CONFIG_MAX_OUTGOING_MESSAGES - Outgoing message maximum

Sets the maximum number of messages that can be in the outgoing message queue. Messages submitted past this limit will not be enqueued.

CONFIG_MAX_CONSECUTIVE_SEND_FAILURES - Max outgoing consecutive send failures

Sets the maximum number of device messages which may fail to send before a connection reset and cooldown is triggered.

CONFIG_CONSECUTIVE_SEND_FAILURE_COOLDOWN_SECONDS - Cooldown after max consecutive send failures exceeded (seconds)

Sets the cooldown time (in seconds) after the maximum number of consecutive send failures is exceeded. If a connection reset is triggered by too many failed device messages, the sample waits for this long (in seconds) before trying again.

CONFIG_SENSOR_SAMPLE_INTERVAL_SECONDS - Sensor sampling interval (seconds)

Sets the time to wait between each temperature sensor sample.


Decreasing the sensor sampling interval too much leads to more frequent use of the LTE connection, which can prevent GNSS from obtaining a fix. This is because GNSS can operate only when the LTE connection is not active. Every time a sensor sample is sent, it interrupts any attempted GNSS fix. The exact time required to obtain a GNSS fix will vary depending on satellite visibility, time since last fix, the type of antenna used, and other environmental factors. In good conditions, and with A-GPS data, one minute is a reasonable interval for reliably getting a location estimate. This allows using long enough value for CONFIG_GNSS_FIX_TIMEOUT_SECONDS, while still leaving enough time for falling back to cellular positioning if needed.

The default sensor sampling interval, 60 seconds, will quickly consume cellular data, and should not be used in a production environment. Instead, consider using a less frequent interval, and if necessary, combining multiple sensor samples into a single device message , or combining multiple device messages using the d2c/bulk device message topic.

If you significantly increase the sampling interval, you must keep the CONFIG_MQTT_KEEPALIVE short to avoid the carrier silently closing the MQTT connection due to inactivity, which could result in dropped device messages. The exact maximum value depends on your carrier. In general, a few minutes or less should work well.


Sets the GNSS fix timeout in seconds. On each location sample, try for this long to achieve a GNSS fix before falling back to cellular positioning.


Sets the location sampling interval in seconds.


Enables GNSS location tracking. Disable all location tracking methods to completely disable location tracking. Defaults to enabled.

CONFIG_LOCATION_TRACKING_CELLULAR - Cellular location tracking

Enables cellular location tracking. Disable all location tracking methods to completely disable location tracking. Defaults to enabled.


Enables Wi-Fi location tracking. Disable all location tracking methods to completely disable location tracking. Requires the use of an nRF7002 companion chip. Defaults to disabled.

CONFIG_TEMP_DATA_USE_SENSOR - Use genuine temperature data

Sets whether to take genuine temperature measurements from a connected BME680 sensor, or just simulate sensor data.

CONFIG_TEMP_TRACKING - Track temperature data

Enables tracking and reporting of temperature data to nRF Cloud. Defaults to enabled.


Use the Zephyr pwm-leds driver for LED indication. Defaults to enabled on the Thingy:91.


Use the Zephyr gpio-leds driver for LED indication. Defaults to enabled if there is a compatible devicetree entry, and the Thingy:91 is not the target. Defaults to enabled on the nRF9160DK.

CONFIG_LED_INDICATION_DISABLED - Completely disable LED indication

Defaults to enabled if both CONFIG_LED_INDICATION_PWM and CONFIG_LED_INDICATION_GPIO are disabled.


Use an on-board RGB LED for status indication. Defaults to enabled on the Thingy:91.

CONFIG_LED_INDICATOR_4LED - Four-LED status indication

Use four discrete LEDs for status indication. Defaults to enabled if CONFIG_LED_INDICATOR_RGB is disabled and LED indication is not disabled.

CONFIG_LED_VERBOSE_INDICATION - Verbose LED status indication

Show more detailed LED status updates. Show a pattern when device messages are successfully sent, and when the initial connection to nRF Cloud is lost. Defaults to enabled if LED indication is enabled.

CONFIG_LED_CONTINUOUS_INDICATION - Continuous LED status indication

Show an idle pattern, rather than turn the LEDs off, when the sample is idle. Defaults to enabled if LED indication is enabled.

CONFIG_TEST_COUNTER - Enable test counter

Enables the test counter.

CONFIG_AT_CMD_REQUESTS - Enable AT command requests

Allow remote execution of modem AT commands, requested using application-specific device messages. See Remote execution of modem AT commands for details.

CONFIG_AT_CMD_REQUEST_RESPONSE_BUFFER_LENGTH - Length of AT command request response buffer (bytes)

Sets the size of the buffer for storing responses to modem AT commands before they are forwarded to the cloud. Modem responses longer than this length will be replaced with an error code message (-NRF_E2BIG). Cannot be less than 40 bytes.

Building and running

This sample can be found under samples/nrf9160/nrf_cloud_mqtt_multi_service in the nRF Connect SDK folder structure.

When built as firmware image for the _ns build target, the sample has Cortex-M Security Extensions (CMSE) enabled and separates the firmware between Non-Secure Processing Environment (NSPE) and Secure Processing Environment (SPE). Because of this, it automatically includes the Trusted Firmware-M (TF-M). To read more about CMSE, see Processing environments.

To build the sample with Visual Studio Code, follow the steps listed on the How to build an application page in the nRF Connect for VS Code extension documentation. See Building and programming an application for other building and programming scenarios and Testing and debugging an application for general information about testing and debugging in the nRF Connect SDK.

Building with nRF7002 EK Wi-Fi scanning support (for nRF9160 DK)

To build the sample with nRF7002 EK Wi-Fi scanning support, use the -DSHIELD=nrf7002ek, -DDTC_OVERLAY_FILE=nrf9160dk_with_nrf7002ek.overlay and -DOVERLAY_CONFIG=overlay-nrf7002ek-wifi-scan-only.conf options.

This enables the Wi-Fi location tracking method automatically.

For example:

west build -p -b nrf9160dk_nrf9160_ns -- -DSHIELD=nrf7002ek -DDTC_OVERLAY_FILE="nrf9160dk_with_nrf7002ek.overlay" -DOVERLAY_CONFIG="overlay-nrf7002ek-wifi-scan-only.conf"

This is only supported on the Nordic nRF9160 DK with an attached nRF7002 EK.

See also the paragraphs on the Wi-Fi location tracking method.


This sample uses the following nRF Connect SDK libraries and drivers:

It uses the following sdk-nrfxlib library:

In addition, it uses the following secure firmware component: