UART using async mode on Thingy:91

Hello,

I am trying to establish communication between the nRF9160 SoC and the nRF52840 on the Thingy:91. I have two arrays: one of 774 bytes and another of 8 bytes. Currently, I am only sending the first array from the nRF52840 to the nRF9160. However, I have observed that the entire 774 bytes are not sent at once. Sometimes, it sends 700 bytes followed by 74 bytes, and on the other side (nRF9160) it receives sometimes 256 bytes three times and then 10 bytes.

The issue is that I need a solution to send the 774 bytes in one chunk and to receive it correctly on the other side, as I have no way to define the end of my array. For reference, my application is based on the lpuart sample, but I am using uart1 instead of lpuart.

Here's my app of nRF9160 side :

#include "uart_nrf91_to_nrf52.h"
#include <zephyr/kernel.h>
#include <zephyr/device.h>
#include <zephyr/devicetree.h>
#include <zephyr/sys/ring_buffer.h>
#include <zephyr/drivers/gpio.h>
#include <zephyr/drivers/uart.h>
#include <string.h>
#include <stdio.h>
#include <zephyr/logging/log.h>
#include <date_time.h>

LOG_MODULE_REGISTER(NRF52_LOG,4);
#define UART_BUF_SIZE		776
#define UART_TX_TIMEOUT_MS	100
#define UART_RX_TIMEOUT_MS	100

K_SEM_DEFINE(tx_done, 1, 1);
K_SEM_DEFINE(rx_disabled, 0, 1);
K_SEM_DEFINE(connected_lte, 0, 1);

#define UART_TX_BUF_SIZE  		256
#define UART_RX_MSG_QUEUE_SIZE	8
#define TOTAL_DATA_RX_SIZE		778
struct uart_msg_queue_item {
	uint8_t bytes[UART_BUF_SIZE];
	uint32_t length;
};

uint8_t ts_bytes[9]; // Timestamp bytes

// UART TX fifo
RING_BUF_DECLARE(app_tx_fifo, UART_TX_BUF_SIZE);
volatile int bytes_claimed;

// UART RX primary buffers
uint8_t uart_double_buffer[2][UART_BUF_SIZE];
uint8_t *uart_buf_next = uart_double_buffer[1];

// UART RX message queue
K_MSGQ_DEFINE(uart_rx_msgq, sizeof(struct uart_msg_queue_item), UART_RX_MSG_QUEUE_SIZE, 4);

static const struct device *dev_uart;

void app_uart_async_callback(const struct device *uart_dev,
							 struct uart_event *evt, void *user_data)
{
	static struct uart_msg_queue_item new_message;

	switch (evt->type) {
		case UART_TX_DONE:
			// Free up the written bytes in the TX FIFO
			ring_buf_get_finish(&app_tx_fifo, bytes_claimed);
            printk("number of bytes sent is : %d\n",evt->data.tx.len);
			// If there is more data in the TX fifo, start the transmission
			if(uart_tx_get_from_queue() == 0) {
				// Or release the semaphore if the TX fifo is empty
                printk("ALL data is transmitted\n");
				k_sem_give(&tx_done);
			}
			break;
		
		case UART_RX_RDY:
            // LOG_DBG("message received : length %d\n", evt->data.rx.len);
			memcpy(new_message.bytes, evt->data.rx.buf + evt->data.rx.offset, evt->data.rx.len);
			new_message.length = evt->data.rx.len;
			if(k_msgq_put(&uart_rx_msgq, &new_message, K_NO_WAIT) != 0){
				printk("Error: Uart RX message queue full!\n");
			}
			break;
		
		case UART_RX_BUF_REQUEST:
			uart_rx_buf_rsp(dev_uart, uart_buf_next, UART_BUF_SIZE);
			break;

		case UART_RX_BUF_RELEASED:
			uart_buf_next = evt->data.rx_buf.buf;
			break;

		case UART_RX_DISABLED:
			k_sem_give(&rx_disabled);
			break;
		
		default:
			break;
	}
}

void app_uart_init(void)
{
	dev_uart = DEVICE_DT_GET(DT_NODELABEL(uart1));

    if (!dev_uart) {
        printk("Impossible de trouver le périphérique UART\n");
        return;
    }
    if(device_is_ready(dev_uart) == false)
    {
        printk("Périphérique UART non prêt\n");
        return -1;
    }

	uart_callback_set(dev_uart, app_uart_async_callback, NULL);
	uart_rx_enable(dev_uart, uart_double_buffer[0], UART_BUF_SIZE, UART_RX_TIMEOUT_MS);
}

void uart_get_rx(void *p1, void *p2, void *p3){
    struct uart_msg_queue_item incoming_message;
	static uint8_t string_buffer[TOTAL_DATA_RX_SIZE + 2] = {0};
    int buffer_length = 0;
    uint16_t size = 0;
	while (1) {
        // printk("Waiting for new message\n");
		// This function will not return until a new message is ready
		k_msgq_get(&uart_rx_msgq, &incoming_message, K_FOREVER);
		// printk("New message received\n");
        // Copy incoming_message.bytes to string_buffer
        for (int i = 0; i < incoming_message.length; i++) {
            string_buffer[buffer_length] = incoming_message.bytes[i];
            if(buffer_length == 0){
                if(string_buffer[buffer_length] == 255){
                size = 775;
                }else{
                    size = 778;
                }
                printk("size = %d, string_buffer[0] = %d \n",size, string_buffer[buffer_length]);
            }
            buffer_length++;
            if (buffer_length == size) {
                // End of message detected
                // buffer_length -= 3; // Remove the end marker
                string_buffer[buffer_length] = '\0'; // Null-terminate the string
                printk("length of string_buffer : %d\n", buffer_length);
                // printk("RX : %s\n", string_buffer);
                memset(string_buffer, 0, sizeof(string_buffer)); // Clear the buffer
                buffer_length = 0; // Reset buffer length
                break;
            }
        }
	}
}

K_THREAD_DEFINE(uart_rx_get_from_nrf52, 1024, uart_get_rx, NULL, NULL, NULL, 5, 0, 0);

The app of nRF52840 side :

void receive_data(const uint8_t *data, size_t length) {
	uint8_t all_data[TOTAL_DATA_TEMP_SIZE];
	// Si la taille des données reçues est égale à 7, alors c'est le capteur de courant
	if(length == TOTAL_DATA_CURRENT_SIZE){
		// Capteur de courant 
		printk("Current sensor data received\n");
	} 
	// Sinon, c'est le capteur de température 
	else{
		if ((data_received_len + length) > TOTAL_DATA_TEMP_SIZE) {
				// Gérer l'erreur : données reçues dépassent la taille du tableau
				printk("Erreur : dépassement de capacité du tableau\n");
				return;
    	}

		// Copier les données reçues dans le tableau global
		memcpy(&all_data[data_received_len], data, length);

		// Mettre à jour l'index
		data_received_len += length;
		// printk("data_received_len : %d\n", data_received_len);
		// Si toutes les données ont été reçues
		if (data_received_len == TOTAL_DATA_TEMP_SIZE) {
			printk("%d received \n", data_received_len);
			send_data_to_nrf91(255, 1); // Envoie la taille des données reçues à la carte nRF91
			// send_data_in_chunks(all_data, TOTAL_DATA_TEMP_SIZE);
			send_data_to_nrf91(all_data, TOTAL_DATA_TEMP_SIZE); // Envoie les données reçues à la carte nRF91
			// Réinitialiser l'index si vous attendez une nouvelle série de données
			data_received_len = 0;
			/*for(int i = 0; i < 24; i++){
				for(int j = 0; j < 32; j++){
					printk("%d , ", all_data[i*32+j]);
				}*/
				// printk("\n");
			// send_data_to_nrf91(END_OF_ARRAY_MARKER, sizeof(END_OF_ARRAY_MARKER)); // Envoie le caractère de fin de message à la carte nRF91
			// k_sem_give(&send_uart_data); // Donne le semaphore pour débloquer la fonction app_uart_send
			}
		}	
		// printk("\n--------------------\n");
}


#define UART_BUF_SIZE		23
#define UART_TX_TIMEOUT_MS	100
#define UART_RX_TIMEOUT_MS	100

K_SEM_DEFINE(tx_done, 1, 1);
K_SEM_DEFINE(rx_disabled, 0, 1);

K_SEM_DEFINE(send_uart_data, 0, 1);

#define UART_TX_BUF_SIZE  		800
#define UART_RX_MSG_QUEUE_SIZE	8
struct uart_msg_queue_item {
	uint8_t bytes[UART_BUF_SIZE];
	uint32_t length;
};

// UART TX fifo
RING_BUF_DECLARE(app_tx_fifo, UART_TX_BUF_SIZE);
volatile int bytes_claimed;

// UART RX primary buffers
uint8_t uart_double_buffer[2][UART_BUF_SIZE];
uint8_t *uart_buf_next = uart_double_buffer[1];

// UART RX message queue
K_MSGQ_DEFINE(uart_rx_msgq, sizeof(struct uart_msg_queue_item), UART_RX_MSG_QUEUE_SIZE, 4);

static const struct device *dev_uart;

static int uart_tx_get_from_queue(void)
{
	uint8_t *data_ptr;
	// Try to claim any available bytes in the FIFO
	bytes_claimed = ring_buf_get_claim(&app_tx_fifo, &data_ptr, UART_TX_BUF_SIZE);

	if(bytes_claimed > 0) {
		// Start a UART transmission based on the number of available bytes
		uart_tx(dev_uart, data_ptr, bytes_claimed, SYS_FOREVER_MS);
	}
	return bytes_claimed;
}

void app_uart_async_callback(const struct device *uart_dev,
							 struct uart_event *evt, void *user_data)
{
	static struct uart_msg_queue_item new_message;

	switch (evt->type) {
		case UART_TX_DONE:
			// Free up the written bytes in the TX FIFO
			ring_buf_get_finish(&app_tx_fifo, bytes_claimed);
			LOG_DBG("TX done , %d bytes written\n", evt->data.tx.len);
			// If there is more data in the TX fifo, start the transmission
			if(uart_tx_get_from_queue() == 0) {
				// Or release the semaphore if the TX fifo is empty
				k_sem_give(&tx_done);
			}
			break;
		
		case UART_RX_RDY:
			memcpy(new_message.bytes, evt->data.rx.buf + evt->data.rx.offset, evt->data.rx.len);
			new_message.length = evt->data.rx.len;
            LOG_DBG("new_message.length : %d\n",new_message.length);
			if(k_msgq_put(&uart_rx_msgq, &new_message, K_NO_WAIT) != 0){
				printk("Error: Uart RX message queue full!\n");
			}
			break;
		
		case UART_RX_BUF_REQUEST:
			uart_rx_buf_rsp(dev_uart, uart_buf_next, UART_BUF_SIZE);
			break;

		case UART_RX_BUF_RELEASED:
			uart_buf_next = evt->data.rx_buf.buf;
			break;

		case UART_RX_DISABLED:
			k_sem_give(&rx_disabled);
			break;
		
		default:
			break;
	}
}

static void app_uart_init(void)
{
	dev_uart = DEVICE_DT_GET(DT_NODELABEL(uart1));

    if (!dev_uart) {
        LOG_ERR("Impossible de trouver le périphérique UART\n");
        return;
    }
    if(device_is_ready(dev_uart) == false)
    {
        LOG_ERR("Périphérique UART non prêt\n");
        return -1;
    }

	uart_callback_set(dev_uart, app_uart_async_callback, NULL);
	uart_rx_enable(dev_uart, uart_double_buffer[0], UART_BUF_SIZE, UART_RX_TIMEOUT_MS);
}

void send_data_to_nrf91(const uint8_t * data_ptr, uint32_t data_len)
{
    uint32_t written_to_buf = ring_buf_put(&app_tx_fifo, data_ptr, data_len);
		
    // In case the UART TX is idle, start transmission
    if(k_sem_take(&tx_done, K_NO_WAIT) == 0) {
        LOG_DBG("Transmission started\n");
        uart_tx_get_from_queue();
    }	
    
}

Best Regards

Youssef

Related