UDP - Mancata ricezione pacchetti in NBIOT mode

While developing my project, I need to receive UDP packets with the NRF9151.
My configuration is as follows:
- 1nce SIM
- VPN between my UDP service and the 1nce network (to eliminate NAT issues)
- Roaming connection in Italy with Vodafone
Starting from the udp/sample example that cyclically sends UDP packets, I also added the reception part. I immediately noticed that the packet was received by the device only if sent within about 5 seconds. The 5-second period coincides with the time between send and "RCC Idle."

I further tested by eliminating transmission on the device and focusing on reception.
From the tests I performed, it appears that the same code receives packets regularly if the NRF9151 is configured in LTEM mode.
By modifying the firmware to operate in NBIOT mode, all packets are lost.
In reality, even in NBIOT, for each packet sent, the modem switches from the RCC Idle state to the RCC Connect state and vice versa, but the socket does not receive any data.

This is my main.c

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

#include <stdio.h>
#include <zephyr/kernel.h>
#include <zephyr/net/socket.h>
#include <modem/lte_lc.h>
#include <modem/nrf_modem_lib.h>

#define UDP_IP_HEADER_SIZE 28

static int client_fd;
static struct sockaddr_storage host_addr;
static struct k_work_delayable socket_transmission_work;
static int data_upload_iterations = CONFIG_UDP_DATA_UPLOAD_ITERATIONS;
static uint8_t recv_buf[CONFIG_UDP_DATA_UPLOAD_SIZE_BYTES];

struct sockaddr_in local_addr = {
			.sin_family = AF_INET,    /**< AF_INET      */
			.sin_port = htons(5683),      /**< Port number  */
			.sin_addr.s_addr = INADDR_ANY,      /**< IPv4 address */
};

K_SEM_DEFINE(lte_connected_sem, 0, 1);
K_SEM_DEFINE(modem_shutdown_sem, 0, 1);

static void socket_transmission_work_fn(struct k_work *work)
{
	int err;
	char buffer[CONFIG_UDP_DATA_UPLOAD_SIZE_BYTES] = {"Maurizio\0"};

	printk("Transmitting UDP/IP payload of %d bytes to the ",
	       CONFIG_UDP_DATA_UPLOAD_SIZE_BYTES + UDP_IP_HEADER_SIZE);
	printk("IP address %s, port number %d\n",
	       CONFIG_UDP_SERVER_ADDRESS_STATIC,
	       CONFIG_UDP_SERVER_PORT);

#if defined(CONFIG_UDP_RAI_LAST)
	/* Let the modem know that this is the last packet for now and we do not
	 * wait for a response.
	 */
	int rai = RAI_LAST;

	err = setsockopt(client_fd, SOL_SOCKET, SO_RAI, &rai, sizeof(rai));
	if (err) {
		printk("Failed to set socket option, error: %d\n", errno);
	}
#endif

#if defined(CONFIG_UDP_RAI_ONGOING)
	/* Let the modem know that we expect to keep the network up longer.
	 */
	int rai = RAI_ONGOING;

	err = setsockopt(client_fd, SOL_SOCKET, SO_RAI, &rai, sizeof(rai));
	if (err) {
		printk("Failed to set socket option, error: %d\n", errno);
	}
#endif

	//err = send(client_fd, buffer, sizeof(buffer), 0);
	err = sendto(client_fd, buffer, sizeof(buffer), 0, (struct sockaddr *)&host_addr, sizeof(host_addr));
	if (err < 0) {
		printk("Failed to transmit UDP packet, error: %d\n", errno);
	}

#if defined(CONFIG_UDP_RAI_NO_DATA)
	/* Let the modem know that there will be no upcoming data transmission anymore.
	 */
	int rai = RAI_NO_DATA;

	err = setsockopt(client_fd, SOL_SOCKET, SO_RAI, &rai, sizeof(rai));
	if (err) {
		printk("Failed to set socket option, error: %d\n", errno);
	}
#endif

	/* Transmit a limited number of times and then shutdown. */
	if (data_upload_iterations > 0) {
		data_upload_iterations--;
	} else if (data_upload_iterations == 0) {
		k_sem_give(&modem_shutdown_sem);
		/* No need to schedule work if we're shutting down. */
		return;
	}

	/* Schedule work if we're either transmitting indefinitely or
	 * there are more iterations left.
	 */
	k_work_schedule(&socket_transmission_work,
			K_SECONDS(CONFIG_UDP_DATA_UPLOAD_FREQUENCY_SECONDS));
}

static void work_init(void)
{
	k_work_init_delayable(&socket_transmission_work, socket_transmission_work_fn);
}

static void lte_handler(const struct lte_lc_evt *const evt)
{
	switch (evt->type) {
	case LTE_LC_EVT_NW_REG_STATUS:
		if ((evt->nw_reg_status != LTE_LC_NW_REG_REGISTERED_HOME) &&
		    (evt->nw_reg_status != LTE_LC_NW_REG_REGISTERED_ROAMING)) {
			break;
		}

		printk("Network registration status: %s\n",
		       evt->nw_reg_status == LTE_LC_NW_REG_REGISTERED_HOME ?
		       "Connected - home" : "Connected - roaming");
		k_sem_give(&lte_connected_sem);
		break;
	case LTE_LC_EVT_PSM_UPDATE:
		printk("PSM parameter update: TAU: %d s, Active time: %d s\n",
		       evt->psm_cfg.tau, evt->psm_cfg.active_time);
		break;
	case LTE_LC_EVT_EDRX_UPDATE:
		printk("eDRX parameter update: eDRX: %.2f s, PTW: %.2f s\n",
		       (double)evt->edrx_cfg.edrx, (double)evt->edrx_cfg.ptw);
		break;
	case LTE_LC_EVT_RRC_UPDATE:
		printk("RRC mode: %s\n",
		       evt->rrc_mode == LTE_LC_RRC_MODE_CONNECTED ? "Connected" : "Idle\n");
		break;
	case LTE_LC_EVT_CELL_UPDATE:
		printk("LTE cell changed: Cell ID: %d, Tracking area: %d\n",
		       evt->cell.id, evt->cell.tac);
		break;
	case LTE_LC_EVT_RAI_UPDATE:
		/* RAI notification is supported by modem firmware releases >= 2.0.2 */
		printk("RAI configuration update: "
		       "Cell ID: %d, MCC: %d, MNC: %d, AS-RAI: %d, CP-RAI: %d\n",
		       evt->rai_cfg.cell_id,
		       evt->rai_cfg.mcc,
		       evt->rai_cfg.mnc,
		       evt->rai_cfg.as_rai,
		       evt->rai_cfg.cp_rai);
		break;
	default:
		break;
	}
}

static int socket_connect(void)
{
	int err;
	struct sockaddr_in *server4 = ((struct sockaddr_in *)&host_addr);

	server4->sin_family = AF_INET;
	server4->sin_port = htons(CONFIG_UDP_SERVER_PORT);

	inet_pton(AF_INET, CONFIG_UDP_SERVER_ADDRESS_STATIC, &server4->sin_addr);

	client_fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
	if (client_fd < 0) {
		printk("Failed to create UDP socket, error: %d\n", errno);
		err = -errno;
		return err;
	}

	err = bind(client_fd, (struct sockaddr *)&local_addr,
		   sizeof(local_addr));
	if (err < 0) {
	   printk("Failed to bind socket, error: %d\n", err);
	   close(client_fd);
	   return err;
	}

/*  	err = connect(client_fd, (struct sockaddr *)&host_addr, sizeof(struct sockaddr_in));
	if (err < 0) {
		printk("Failed to connect socket, error: %d\n", errno);
		close(client_fd);
		return err;
	}  */

	return 0;
}

int main(void)
{
	int err;
	int received;

	printk("UDP sample has started\n");

	work_init();

	err = nrf_modem_lib_init();
	if (err) {
		printk("Failed to initialize modem library, error: %d\n", err);
		return -1;
	}
#if defined(CONFIG_UDP_RAI_ENABLE) && defined(CONFIG_SOC_NRF9160)
	/* Enable Access Stratum RAI support for nRF9160.
	 * Note: The 1.3.x modem firmware release is certified to be compliant with 3GPP Release 13.
	 * %REL14FEAT enables selected optional features from 3GPP Release 14. The 3GPP Release 14
	 * features are not GCF or PTCRB conformance certified by Nordic and must be certified
	 * by MNO before being used in commercial products.
	 * nRF9161 is certified to be compliant with 3GPP Release 14.
	 */
	err = nrf_modem_at_printf("AT%%REL14FEAT=0,1,0,0,0");
	if (err) {
		printk("Failed to enable Access Stratum RAI support, error: %d\n", err);
		return -1;
	}
#endif

	err = lte_lc_connect_async(lte_handler);
	if (err) {
		printk("Failed to connect to LTE network, error: %d\n", err);
		return -1;
	}

	k_sem_take(&lte_connected_sem, K_FOREVER);

	err = socket_connect();
	if (err) {
		return -1;
	}

	// Disable work scheduling for now, as we want to test the reception of UDP packets from the server first.
	//k_work_schedule(&socket_transmission_work, K_NO_WAIT);
    struct sockaddr_in client_addr;
    socklen_t client_addr_len = sizeof(client_addr);
    
	while (1) {
		printk("Waiting received data\n");
		//received = recv(client_fd, recv_buf, sizeof(recv_buf), 0);
		received = recvfrom(client_fd, recv_buf, sizeof(recv_buf), 0,(struct sockaddr *)&client_addr, &client_addr_len);

		//received = recv(client_fd, recv_buf, 8, 0);
		if (received < 0) {
			printk("Socket error: %d, exit\n", errno);
			break;
		} if (received == 0) {
			printk("Empty datagram\n");
			continue;
		}
		recv_buf[received] = '\0';
		printk("Received %s\n", recv_buf);
	}	


	k_sem_take(&modem_shutdown_sem, K_FOREVER);

	err = nrf_modem_lib_shutdown();
	if (err) {
		return -1;
	}

	return 0;
}

The prj.conf derive from usp/sample

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

# General config
CONFIG_PICOLIBC_IO_FLOAT=y
CONFIG_NCS_SAMPLES_DEFAULTS=y
CONFIG_SERIAL=y

# Network
CONFIG_NETWORKING=y
CONFIG_NET_NATIVE=n
CONFIG_NET_SOCKETS=y
CONFIG_NET_SOCKETS_OFFLOAD=y
CONFIG_POSIX_API=y

# LTE link control
CONFIG_LTE_LINK_CONTROL=y

# Modem library
CONFIG_NRF_MODEM_LIB=y

# Heap and stacks
CONFIG_HEAP_MEM_POOL_SIZE=1024
CONFIG_MAIN_STACK_SIZE=4096
CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=2048

# LTE parameters
## PSM
CONFIG_UDP_PSM_ENABLE=n
CONFIG_LTE_PSM_REQ_RPTAU="00100001"
CONFIG_LTE_PSM_REQ_RAT="00000000"

## eDRX
CONFIG_UDP_EDRX_ENABLE=n

## RAI
CONFIG_UDP_RAI_ENABLE=n

## Enable required LTE link control modules
CONFIG_LTE_LC_EDRX_MODULE=y
CONFIG_LTE_LC_PSM_MODULE=y
CONFIG_LTE_LC_RAI_MODULE=y

## Maurizio
CONFIG_UDP_DATA_UPLOAD_FREQUENCY_SECONDS=90

CONFIG_LTE_NETWORK_MODE_NBIOT=y
#CONFIG_LTE_NETWORK_MODE_LTE_M=y

CONFIG_UDP_SERVER_ADDRESS_STATIC="10.64.193.81"
CONFIG_UDP_SERVER_PORT=5683
#CONFIG_UDP_SERVER_PORT=10000
CONFIG_UDP_DATA_UPLOAD_SIZE_BYTES=30



# Enable Packet Data Network (PDN) library
CONFIG_PDN=y
CONFIG_PDN_DEFAULTS_OVERRIDE=y
# Set your custom APN
CONFIG_PDN_DEFAULT_APN="iot.1nce.net"
# Opzionale: imposta la famiglia IP (IPv4, IPv6 o IPv4V6)
CONFIG_PDN_DEFAULT_FAM_IPV4=y

# Abilita il blocco su un PLMN specifico
CONFIG_LTE_LOCK_PLMN=y

# Inserisci il codice MCCMNC dell'operatore (es. 22210 per VODAFONE IT)
CONFIG_LTE_LOCK_PLMN_STRING="22210"

#CONFIG_NRF_MODEM_LIB_TRACE=y

The serial output for NBIOT is:

LTE cell changed: Cell ID: 7593841, Tracking area: 17095
RRC mode: Connected
Network registration status: Connected - roaming
Waiting received data
PSM parameter update: TAU: 7200 s, Active time: -1 s
RRC mode: Idle

RRC mode: Connected
RRC mode: Idle

RRC mode: Connected
RRC mode: Idle

The same log with LTEM (whree the data are reeceived) is:

*** Booting nRF Connect SDK v3.2.4-4c3fc0d44534 ***
*** Using Zephyr OS v4.2.99-9673eec75908 ***
UDP sample has started
I: Trace thread ready
I: Trace level override: 2
LTE cell changed: Cell ID: 7593761, Tracking area: 13007
RRC mode: Connected
Network registration status: Connected - roaming
Waiting received data
PSM parameter update: TAU: 3600 s, Active time: -1 s
RRC mode: Idle

RRC mode: Connected
Received 12:19:53
Waiting received data
RRC mode: Idle

RRC mode: Connected
Received 12:20:14
Waiting received data
RRC mode: Idle

Using Cellular Monitor, I dumped modem activity in both the NB IoT and LTE cases:

Cellular-Monitor-NBIOT-device.pcapng

Cellular-Monitor-LTEM-device.pcapng

Please help me to solve the problem.

Parents
  • Hello  , apologies for the late reply. I will have a look at the wireshark traces that you have provided.

    VPN between my UDP service and the 1nce network (to eliminate NAT issues)

    Could you please elaborate more on what this means? Are you able to also provide the raw modem traces from Cellular Monitor?

    Thanks. 

    Kind regards,
    Øyvind

  • Hi Øyvind,

    I'll try to clarify how the 1NCE VPN works. When you purchase a 1NCE SIM card, you get the option to connect the server to a network using a VPN that can directly reach devices with fixed addresses for each SIM card. This way, packets to and from the server use the VPN to communicate with the devices. For the UDP reception issue I reported, I also contacted 1NCE, which monitored the UDP traffic and verified that outgoing messages from my server are correctly arriving on their network and are correctly routed to the IP address of the device being tested.

    I'm attaching the log file I received from 1NCE.

    2026-05-06_nbiot_dl_traffic_udp.pcapng

    The device notices the packet that has been sent to it because, systematically, after a few seconds (usually 1 to 10 seconds), it switches from the RCC Idle state to the RCC Connect state, but immediately afterwards it returns to the RCC Idle state without the socket receiving any data.

    1NCE has now collected the information and forwarded it to the roaming partner (Vodafone) for their analysis.

    Kind regards

Reply
  • Hi Øyvind,

    I'll try to clarify how the 1NCE VPN works. When you purchase a 1NCE SIM card, you get the option to connect the server to a network using a VPN that can directly reach devices with fixed addresses for each SIM card. This way, packets to and from the server use the VPN to communicate with the devices. For the UDP reception issue I reported, I also contacted 1NCE, which monitored the UDP traffic and verified that outgoing messages from my server are correctly arriving on their network and are correctly routed to the IP address of the device being tested.

    I'm attaching the log file I received from 1NCE.

    2026-05-06_nbiot_dl_traffic_udp.pcapng

    The device notices the packet that has been sent to it because, systematically, after a few seconds (usually 1 to 10 seconds), it switches from the RCC Idle state to the RCC Connect state, but immediately afterwards it returns to the RCC Idle state without the socket receiving any data.

    1NCE has now collected the information and forwarded it to the roaming partner (Vodafone) for their analysis.

    Kind regards

Children
Related