UART communication between nrf9160 and nrf52840 on Thingy91

I am having problems setting up UART communication between nrf9160 and nrf52840 on Thingy91. 

My set up consist of a Thingy52 and a Thingy91. I am using ncs version 2.0.1.

The Thingy52 is currently running the peripheral_uart sample and seems to work as intended. 

The Thingy91 is currently running the mqtt_simple sample on nrf9160 and the central_uart sample on nrf52840.

The peripheral_uart sample is built with the correct boardfile: thingy52_nrf52832.

The central_uart sample was not compatible with the correct boardfile for the nrf52-chip on Thingy91 (thingy91_nrf52840). However I got it to build with the nrf52840 DK boardfile (nrf52840dk_nrf52840). When flashing the sample built with this boardfile, it seems to work fine. The two Thingy's manage to establish a bluetooth connection and are able to send messages back and forth. 

The mqtt_simple sample is built with thingy91_nrf9160_ns, which is the correct boardfile for my case. It connects successfully to our MQTT broker and is able to send and receive messages. 

The missing link in my data flow is the internal communication between the nrf52840 and the nrf9160 on the Thingy91. I am trying to set up the uart0-peripheral on both chips based on the schematics. I am using the following pins as RX and TX:

On nrf9160:

RX ---> P0.23 = MCU_IF5

TX ---> P0.22 = MCU_IF4

on nrf52840:

TX ---> P0.25 = MCU_IF5

RX ---> P1.00 = MCU_IF4

This is implemented with the following overlay files:

mqtt_simple/boards/thingy91_nrf9160_ns.overlay

&uart0 {
	compatible = "nordic,nrf-uarte";
	current-speed = <115200>;
	status = "okay";
	pinctrl-0 = <&uart0_default>;
	pinctrl-1 = <&uart0_sleep>;
	pinctrl-names = "default", "sleep";
};

&pinctrl {
	uart0_default: uart0_default {
		group1 {
			psels = <NRF_PSEL(UART_TX, 0, 22)>,
			 	<NRF_PSEL(UART_RX, 0, 23)>;
		};
	};

	uart0_sleep: uart0_sleep {
		group1 {
			psels = <NRF_PSEL(UART_TX, 0, 22)>,
				<NRF_PSEL(UART_RX, 0, 23)>;
		};
	};
};


central_uart/boards/nrf52840dk_nrf52840.overlay

&uart0 {
	compatible = "nordic,nrf-uarte";
	status = "okay";
	current-speed = <115200>;
	pinctrl-0 = <&uart0_default>;
	pinctrl-1 = <&uart0_sleep>;
	pinctrl-names = "default", "sleep";
};

&pinctrl {
	uart0_default: uart0_default {
		group1 {
			psels = <NRF_PSEL(UART_TX, 0, 25)>,
				<NRF_PSEL(UART_RX, 1, 0)>;
		};
	};

	uart0_sleep: uart0_sleep {
		group1 {
			psels = <NRF_PSEL(UART_TX, 0, 25)>,
				<NRF_PSEL(UART_RX, 1, 0)>;
		};
	};
};

I have not made any major changes to the central_uart sample, as it already implements the UART async api. The device is linked to uart0. 

In the mqtt_simple sample I have tried to implement async uart similar to how it is implemented in the central_uart_sample. The init rutine succeeds and so does the uart_tx, but none of the messages are received by the other part. 


Here is my UART module, added to the mqtt_simple sample:

#include "uart.h"
#include <zephyr/kernel.h>
#include <zephyr/device.h>
#include <zephyr/devicetree.h>
#include <zephyr/logging/log.h>
#include <zephyr/drivers/uart.h>

#define LOG_MODULE_NAME uart
LOG_MODULE_REGISTER(LOG_MODULE_NAME);

/* UART payload buffer element size. */
#define UART_BUF_SIZE 20
#define UART_WAIT_FOR_BUF_DELAY K_MSEC(50)
#define UART_RX_TIMEOUT 50

static const struct device *uart; // = DEVICE_DT_GET(DT_NODELABEL(uart0));
static struct k_work_delayable uart_work;

static void uart_work_handler(struct k_work *item);
static void uart_cb(const struct device *dev, struct uart_event *evt, void *user_data);



struct uart_data_t {
	void *fifo_reserved;
	uint8_t  data[UART_BUF_SIZE];
	uint16_t len;
};

static K_FIFO_DEFINE(fifo_uart_tx_data);
static K_FIFO_DEFINE(fifo_uart_rx_data);

static struct k_fifo * p_fifo_uart_tx_data;
static struct k_fifo * p_fifo_uart_rx_data;

int uart_init(struct k_fifo * p_tx_fifo, struct k_fifo * p_rx_fifo)
{
	int err;

    p_fifo_uart_tx_data = p_tx_fifo;
    p_fifo_uart_rx_data = p_rx_fifo;

	struct uart_data_t *rx;

    uart = device_get_binding("UART_0");

	if (!device_is_ready(uart)) {
		LOG_ERR("UART device not ready");
		return -ENODEV;
	}
    
	LOG_INF("UART device ready");

	rx = k_malloc(sizeof(*rx));
	if (rx) {
		rx->len = 0;
	} else {
		return -ENOMEM;
	}

	k_work_init_delayable(&uart_work, uart_work_handler);

	err = uart_callback_set(uart, uart_cb, NULL);
	if (err) {
		return err;
	}

	return uart_rx_enable(uart, rx->data, sizeof(rx->data),
			      UART_RX_TIMEOUT);

}

static void uart_work_handler(struct k_work *item)
{
	struct uart_data_t *buf;

	buf = k_malloc(sizeof(*buf));
	if (buf) {
		buf->len = 0;
	} else {
		LOG_WRN("Not able to allocate UART receive buffer");
		k_work_reschedule(&uart_work, UART_WAIT_FOR_BUF_DELAY);
		return;
	}

	uart_rx_enable(uart, buf->data, sizeof(buf->data), UART_RX_TIMEOUT);
}

void uart_tx_data_received(const uint8_t *data, uint16_t len)
{

	int err;

    LOG_INF("In uart_tx_data_received()");

	for (uint16_t pos = 0; pos != len;) {
		struct uart_data_t *tx = k_malloc(sizeof(*tx));

		if (!tx) {
			LOG_WRN("Not able to allocate UART send data buffer");
		}

		/* Keep the last byte of TX buffer for potential LF char. */
		size_t tx_data_size = sizeof(tx->data) - 1;

		if ((len - pos) > tx_data_size) {
			tx->len = tx_data_size;
		} else {
			tx->len = (len - pos);
		}

		memcpy(tx->data, &data[pos], tx->len);

		pos += tx->len;

		/* Append the LF character when the CR character triggered
		 * transmission from the peer.
		 */
		if ((pos == len) && (data[len - 1] == '\r')) {
			tx->data[tx->len] = '\n';
			tx->len++;
		}   

        LOG_INF("Transmitting UART data!");
		err = uart_tx(uart, tx->data, tx->len, SYS_FOREVER_MS);
		if (err) {
            LOG_WRN("uart_tx() failed");
			k_fifo_put(&fifo_uart_tx_data, tx);
		}
	}

}


static void uart_cb(const struct device *dev, struct uart_event *evt, void *user_data)
{
	ARG_UNUSED(dev);

	static size_t aborted_len;
	struct uart_data_t *buf;
	static uint8_t *aborted_buf;
	static bool disable_req;

	switch (evt->type) {
	case UART_TX_DONE:
		LOG_DBG("UART_TX_DONE");
		if ((evt->data.tx.len == 0) ||
		    (!evt->data.tx.buf)) {
			return;
		}

		if (aborted_buf) {
			buf = CONTAINER_OF(aborted_buf, struct uart_data_t,
					   data);
			aborted_buf = NULL;
			aborted_len = 0;
		} else {
			buf = CONTAINER_OF(evt->data.tx.buf,
					   struct uart_data_t,
					   data);
		}

		k_free(buf);

		buf = k_fifo_get(&fifo_uart_tx_data, K_NO_WAIT);
		if (!buf) {
			return;
		}

		if (uart_tx(uart, buf->data, buf->len, SYS_FOREVER_MS)) {
			LOG_WRN("Failed to send data over UART");
		}

		break;

	case UART_RX_RDY:
		LOG_DBG("UART_RX_RDY");
		buf = CONTAINER_OF(evt->data.rx.buf, struct uart_data_t, data);
		buf->len += evt->data.rx.len;

		if (disable_req) {
			return;
		}

		if ((evt->data.rx.buf[buf->len - 1] == '\n') ||
		    (evt->data.rx.buf[buf->len - 1] == '\r')) {
			disable_req = true;
			uart_rx_disable(uart);
		}

		break;

	case UART_RX_DISABLED:
		LOG_DBG("UART_RX_DISABLED");
		disable_req = false;

		buf = k_malloc(sizeof(*buf));
		if (buf) {
			buf->len = 0;
		} else {
			LOG_WRN("Not able to allocate UART receive buffer");
			k_work_reschedule(&uart_work, UART_WAIT_FOR_BUF_DELAY);
			return;
		}

		uart_rx_enable(uart, buf->data, sizeof(buf->data),
			       UART_RX_TIMEOUT);

		break;

	case UART_RX_BUF_REQUEST:
		LOG_DBG("UART_RX_BUF_REQUEST");
		buf = k_malloc(sizeof(*buf));
		if (buf) {
			buf->len = 0;
			uart_rx_buf_rsp(uart, buf->data, sizeof(buf->data));
		} else {
			LOG_WRN("Not able to allocate UART receive buffer");
		}

		break;

	case UART_RX_BUF_RELEASED:
		LOG_DBG("UART_RX_BUF_RELEASED");
		buf = CONTAINER_OF(evt->data.rx_buf.buf, struct uart_data_t,
				   data);

		if (buf->len > 0) {
			k_fifo_put(&fifo_uart_rx_data, buf);
		} else {
			k_free(buf);
		}

		break;

	case UART_TX_ABORTED:
		LOG_DBG("UART_TX_ABORTED");
		if (!aborted_buf) {
			aborted_buf = (uint8_t *)evt->data.tx.buf;
		}

		aborted_len += evt->data.tx.len;
		buf = CONTAINER_OF(aborted_buf, struct uart_data_t,
				   data);

		uart_tx(uart, &buf->data[aborted_len],
			buf->len - aborted_len, SYS_FOREVER_MS);

		break;

	default:
		break;
	}
}


I am also attaching both of my pr.conf in case there is something missing there. 

prj.conf on nrf9160 (with mqtt_simple):

#
# Copyright (c) 2020 Nordic Semiconductor ASA
#
# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
#

# Networking
CONFIG_NETWORKING=y
CONFIG_NET_NATIVE=n
CONFIG_NET_SOCKETS_OFFLOAD=y
CONFIG_NET_SOCKETS=y
CONFIG_NET_SOCKETS_POSIX_NAMES=y

# LTE link control
CONFIG_LTE_LINK_CONTROL=y
CONFIG_LTE_AUTO_INIT_AND_CONNECT=n

# Modem library
CONFIG_NRF_MODEM_LIB=y

# MQTT
CONFIG_MQTT_LIB=y
CONFIG_MQTT_LIB_TLS=n
CONFIG_MODEM_KEY_MGMT=y
CONFIG_MQTT_CLEAN_SESSION=y

# Application
CONFIG_MQTT_PUB_TOPIC="CriticAlert/DevText"
CONFIG_MQTT_SUB_TOPIC="CriticAlert/WebText"
CONFIG_MQTT_CLIENT_ID="T91-U1"
CONFIG_MQTT_BROKER_HOSTNAME="62.92.148.234"
CONFIG_MQTT_BROKER_PORT=1883
CONFIG_BUTTON_EVENT_PUBLISH_MSG="Hello from nrf9160"
CONFIG_MQTT_RECONNECT_DELAY_S=30
CONFIG_LTE_CONNECT_RETRY_DELAY_S=10

# Button support
CONFIG_DK_LIBRARY=y

# Enable logging
CONFIG_LOG=y
CONFIG_MQTT_SIMPLE_LOG_LEVEL_DBG=y

# Memory
CONFIG_MAIN_STACK_SIZE=4096
CONFIG_HEAP_MEM_POOL_SIZE=2048

# NewLib C
CONFIG_NEWLIB_LIBC=y

# Config logger
CONFIG_LOG=y
CONFIG_USE_SEGGER_RTT=y
CONFIG_LOG_BACKEND_RTT=y
CONFIG_LOG_BACKEND_UART=n

# CONFIG_NET_LOG=y
# CONFIG_MQTT_LOG_LEVEL_DBG=y

# Enable the UART driver
CONFIG_UART_ASYNC_API=y
CONFIG_NRFX_UARTE0=y
CONFIG_SERIAL=y
CONFIG_CONSOLE=y
CONFIG_UART_CONSOLE=y

CONFIG_UART_LOG_LEVEL_DBG=y


prj.conf on nrf52840 (with central_uart):

#
# Copyright (c) 2018 Nordic Semiconductor
#
# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
#

# Enable the UART driver
CONFIG_UART_ASYNC_API=y
CONFIG_NRFX_UARTE0=y
CONFIG_SERIAL=y
CONFIG_CONSOLE=y
CONFIG_UART_CONSOLE=y

# Enable the BLE stack with GATT Client configuration
CONFIG_BT=y
CONFIG_BT_CENTRAL=y
CONFIG_BT_SMP=y
CONFIG_BT_GATT_CLIENT=y

# Enable the BLE modules from NCS
CONFIG_BT_NUS_CLIENT=y
CONFIG_BT_SCAN=y
CONFIG_BT_SCAN_FILTER_ENABLE=y
CONFIG_BT_SCAN_UUID_CNT=1
CONFIG_BT_GATT_DM=y
CONFIG_HEAP_MEM_POOL_SIZE=2048

# This example requires more workqueue stack
CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=2048

# Enable bonding
CONFIG_BT_SETTINGS=y
CONFIG_FLASH=y
CONFIG_FLASH_PAGE_LAYOUT=y
CONFIG_FLASH_MAP=y
CONFIG_NVS=y
CONFIG_SETTINGS=y

# Config logger
CONFIG_LOG=y
CONFIG_USE_SEGGER_RTT=y
CONFIG_LOG_BACKEND_RTT=y
CONFIG_LOG_BACKEND_UART=n

CONFIG_ASSERT=y

CONFIG_UART_LOG_LEVEL_DBG=y

I really hope you can help me out here! Let me know if there is any more information you would like me to provide. 



Best regards,
Tonja

  • To clarify, what happens when I am trying to send a message over UART is that the uart_tx()-function returns successfully, but I do not see anything on the recipient side. This is the case in both directions. 

    Also attaching the schematic I have been looking at when deciding the pins to use for RX/TX. 

    PCA20035_Schematic_And_PCB.pdf

  • Hi,

    From the schematic you link:

    I suggest that you connect an oscilloscope or logic analyzator to SB8 and SB9 and see if you can see any traffic on the actual lines.

    That way you can figure out if the error is in the nRF9160 or nRF52840.

    Also, I recommend having a look at our Asset Tracker v2 and Connectivity bridge samples, as they also do UART communication between the 9160 and 52840 on the Thingy91.

    Regards,
    Sigurd Hellesvik

  • Hi,

    Thanks for the answer. I actually just manages to make it work! :)

    The solution was to also configure the CTS and RTS pins in both overlay files. These were set up in the original board files to pins that are not connected in my case. This is probably why there were no data flow on my UART lines. I also changed the uart peripheral to uart1 from uart0. Not sure if this was necessary, but I can test it and report what I find. 

    Here are my new overlay files.

    nrf52840dk_nrf52840.overlay

    &uart1 {
    	current-speed = <115200>;
    	status = "okay";
    	pinctrl-0 = <&uart1_default>;
    	pinctrl-1 = <&uart1_sleep>;
    	pinctrl-names = "default", "sleep";
    };
    
    
    &pinctrl {
    
        uart1_default: uart1_default {
    		group1 {
    			psels = <NRF_PSEL(UART_TX, 0, 25)>,
    				<NRF_PSEL(UART_RX, 1, 0)>,
    				<NRF_PSEL(UART_RTS, 0, 22)>,
    				<NRF_PSEL(UART_CTS, 0, 19)>;
    		};
    	};
    
    	uart1_sleep: uart1_sleep {
    		group1 {
    			psels = <NRF_PSEL(UART_TX, 0, 25)>,
    				<NRF_PSEL(UART_RX, 1, 0)>,
    				<NRF_PSEL(UART_RTS, 0, 22)>,
    				<NRF_PSEL(UART_CTS, 0, 19)>;
    			low-power-enable;
    		};
    	};
        
    
    };
    
    
    

    thingy91_nrf9160_ns.overlay

    &uart1 {
    	current-speed = <115200>;
    	status = "okay";
    	pinctrl-0 = <&uart1_default>;
    	pinctrl-1 = <&uart1_sleep>;
    	pinctrl-names = "default", "sleep";
    };
    
    
    &pinctrl {
    
    	uart1_default: uart1_default {
    		group1 {
    			psels = <NRF_PSEL(UART_TX, 0, 22)>,
    				<NRF_PSEL(UART_RX, 0, 23)>,
    				<NRF_PSEL(UART_RTS, 0, 24)>,
    				<NRF_PSEL(UART_CTS, 0, 25)>;
    		};
    	};
    
    	uart1_sleep: uart1_sleep {
    		group1 {
    			psels = <NRF_PSEL(UART_TX, 0, 22)>,
    				<NRF_PSEL(UART_RX, 0, 23)>,
    				<NRF_PSEL(UART_RTS, 0, 24)>,
    				<NRF_PSEL(UART_CTS, 0, 25)>;
    			low-power-enable;
    		};
    	};
    
    };
    

Related