nRF7002dk UDP Client Power Consumption

Dear Devzone-Team,

I am working on an nRF7002 based IoT Sensor using the nRF7002DK. I managed to set up the WiFi in STA mode and connect to Raspberry Pi working as AP. The nRF7002 should act as a UDP Client and send data to the AP.  I managed to set up the socket and the communication to the Raspberry is working. 

Now the problem:
When I look at the measurements with the Power Profiler Kit II there is too much power consumption in my opinion. Without UDP transmissions I have roughly the power consumption mentioned in Operating in power save modes and Target Wake Time on the nRF7002 DK for the normal DTIM Mode.
However, it seems that for the UDP transmissions it takes a long time until the sleep mode is activated again (~60mA for around 150ms). Please look at the attached figure from the PPK2, where you can see the UDP transmissions every 1s. The narrow spikes are the DTIM and the broader ones (~150ms) are when the UDP transmission is started.

Maybe you can help me to reduce the power of the UDP transmissions to the absolute minimum. (Maybe the nRF7002 waits for an response by the AP?)

Here is the main file:

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

/** @file
 * @brief WiFi station sample
 */

#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(sta, CONFIG_LOG_DEFAULT_LEVEL);


#include <nrfx_clock.h>
#include <zephyr/kernel.h>
#include <stdio.h>
#include <stdlib.h>
#include <zephyr/shell/shell.h>
#include <zephyr/sys/printk.h>
#include <zephyr/init.h>

#include <zephyr/net/net_if.h>
#include <zephyr/net/wifi_mgmt.h>
#include <zephyr/net/net_event.h>
#include <zephyr/drivers/gpio.h>
#include <zephyr/net/wifi.h>

#include <qspi_if.h>

#include <zephyr/net/socket.h>
#include <unistd.h> 

#include "net_private.h"

#define WIFI_SHELL_MODULE "wifi"

#define WIFI_SHELL_MGMT_EVENTS (NET_EVENT_WIFI_CONNECT_RESULT |		\
				NET_EVENT_WIFI_DISCONNECT_RESULT)

#define MAX_SSID_LEN        32
#define DHCP_TIMEOUT        70
#define CONNECTION_TIMEOUT  100
#define STATUS_POLLING_MS   300

/* 1000 msec = 1 sec */
#define LED_SLEEP_TIME_MS   100

/* The devicetree node identifier for the "led0" alias. */
#define LED0_NODE DT_ALIAS(led0)

#define UDP_CLIENT_PORT 4242


// Tasks:

//! Wifi thread priority level
#define WIFI_STACK_SIZE 4096
//! Wifi thread priority level
#define WIFI_PRIORITY 4

//! WiFi stack definition
K_THREAD_STACK_DEFINE(WIFI_STACK, WIFI_STACK_SIZE);
//! Variable to identify the Wifi thread
static struct k_thread wifiThread;



#define SENSOR_DATA_LENGTH 1000

struct sensorData
{
	uint16_t frame_number;
	uint8_t data[SENSOR_DATA_LENGTH];
};

struct sensorData transmit_data;


void fillArrayWithRandom(uint8_t *array, uint16_t size) {
    for (uint16_t i = 0; i < size; i++) {
        array[i] = (uint8_t)rand(); // Fill each element with a random value
    }
}


/*
 * A build error on this line means your board is unsupported.
 * See the sample documentation for information on how to fix this.
 */
static const struct gpio_dt_spec led = GPIO_DT_SPEC_GET(LED0_NODE, gpios);

static struct net_mgmt_event_callback wifi_shell_mgmt_cb;
static struct net_mgmt_event_callback net_shell_mgmt_cb;

static struct {
	const struct shell *sh;
	union {
		struct {
			uint8_t connected	: 1;
			uint8_t connect_result	: 1;
			uint8_t disconnect_requested	: 1;
			uint8_t _unused		: 5;
		};
		uint8_t all;
	};
} context;

void toggle_led(void)
{
	int ret;

	if (!device_is_ready(led.port)) {
		LOG_ERR("LED device is not ready");
		return;
	}

	ret = gpio_pin_configure_dt(&led, GPIO_OUTPUT_ACTIVE);
	if (ret < 0) {
		LOG_ERR("Error %d: failed to configure LED pin", ret);
		return;
	}

	while (1) {
		if (context.connected) {
			gpio_pin_toggle_dt(&led);
			k_msleep(LED_SLEEP_TIME_MS);
		} else {
			gpio_pin_set_dt(&led, 0);
			k_msleep(LED_SLEEP_TIME_MS);
		}
	}
}

K_THREAD_DEFINE(led_thread_id, 1024, toggle_led, NULL, NULL, NULL,
		7, 0, 0);

static int cmd_wifi_status(void)
{
	struct net_if *iface = net_if_get_default();
	struct wifi_iface_status status = { 0 };

	if (net_mgmt(NET_REQUEST_WIFI_IFACE_STATUS, iface, &status,
				sizeof(struct wifi_iface_status))) {
		LOG_INF("Status request failed");

		return -ENOEXEC;
	}

	LOG_INF("==================");
	LOG_INF("State: %s", wifi_state_txt(status.state));

	if (status.state >= WIFI_STATE_ASSOCIATED) {
		uint8_t mac_string_buf[sizeof("xx:xx:xx:xx:xx:xx")];

		LOG_INF("Interface Mode: %s",
		       wifi_mode_txt(status.iface_mode));
		LOG_INF("Link Mode: %s",
		       wifi_link_mode_txt(status.link_mode));
		LOG_INF("SSID: %-32s", status.ssid);
		LOG_INF("BSSID: %s",
		       net_sprint_ll_addr_buf(
				status.bssid, WIFI_MAC_ADDR_LEN,
				mac_string_buf, sizeof(mac_string_buf)));
		LOG_INF("Band: %s", wifi_band_txt(status.band));
		LOG_INF("Channel: %d", status.channel);
		LOG_INF("Security: %s", wifi_security_txt(status.security));
		LOG_INF("MFP: %s", wifi_mfp_txt(status.mfp));
		LOG_INF("RSSI: %d", status.rssi);
	}
	return 0;
}

static void handle_wifi_connect_result(struct net_mgmt_event_callback *cb)
{
	const struct wifi_status *status =
		(const struct wifi_status *) cb->info;

	if (context.connected) {
		return;
	}

	if (status->status) {
		LOG_ERR("Connection failed (%d)", status->status);
	} else {
		LOG_INF("Connected");
		context.connected = true;
	}

	context.connect_result = true;
}

static void handle_wifi_disconnect_result(struct net_mgmt_event_callback *cb)
{
	const struct wifi_status *status =
		(const struct wifi_status *) cb->info;

	if (!context.connected) {
		return;
	}

	if (context.disconnect_requested) {
		LOG_INF("Disconnection request %s (%d)",
			 status->status ? "failed" : "done",
					status->status);
		context.disconnect_requested = false;
	} else {
		LOG_INF("Received Disconnected");
		context.connected = false;
	}

	cmd_wifi_status();
}

static void wifi_mgmt_event_handler(struct net_mgmt_event_callback *cb,
				     uint32_t mgmt_event, struct net_if *iface)
{
	switch (mgmt_event) {
	case NET_EVENT_WIFI_CONNECT_RESULT:
		handle_wifi_connect_result(cb);
		break;
	case NET_EVENT_WIFI_DISCONNECT_RESULT:
		handle_wifi_disconnect_result(cb);
		break;
	default:
		break;
	}
}

static void print_dhcp_ip(struct net_mgmt_event_callback *cb)
{
	/* Get DHCP info from struct net_if_dhcpv4 and print */
	const struct net_if_dhcpv4 *dhcpv4 = cb->info;
	const struct in_addr *addr = &dhcpv4->requested_ip;
	char dhcp_info[128];

	net_addr_ntop(AF_INET, addr, dhcp_info, sizeof(dhcp_info));

	LOG_INF("DHCP IP address: %s", dhcp_info);
}
static void net_mgmt_event_handler(struct net_mgmt_event_callback *cb,
				    uint32_t mgmt_event, struct net_if *iface)
{
	switch (mgmt_event) {
	case NET_EVENT_IPV4_DHCP_BOUND:
		print_dhcp_ip(cb);
		break;
	default:
		break;
	}
}

static int __wifi_args_to_params(struct wifi_connect_req_params *params)
{
	params->timeout = SYS_FOREVER_MS;

	/* SSID */
	params->ssid = CONFIG_STA_SAMPLE_SSID;
	params->ssid_length = strlen(params->ssid);

#if defined(CONFIG_STA_KEY_MGMT_WPA2)
	params->security = 1;
#elif defined(CONFIG_STA_KEY_MGMT_WPA2_256)
	params->security = 2;
#elif defined(CONFIG_STA_KEY_MGMT_WPA3)
	params->security = 3;
#else
	params->security = 0;
#endif

#if !defined(CONFIG_STA_KEY_MGMT_NONE)
	params->psk = CONFIG_STA_SAMPLE_PASSWORD;
	params->psk_length = strlen(params->psk);
#endif
	params->channel = WIFI_CHANNEL_ANY;

	/* MFP (optional) */
	params->mfp = WIFI_MFP_OPTIONAL;

	return 0;
}

static int wifi_connect(void)
{
	struct net_if *iface = net_if_get_default();
	static struct wifi_connect_req_params cnx_params;

	context.connected = false;
	context.connect_result = false;
	__wifi_args_to_params(&cnx_params);


	if (net_mgmt(NET_REQUEST_WIFI_CONNECT, iface,
		     &cnx_params, sizeof(struct wifi_connect_req_params))) {
		LOG_ERR("Connection request failed");

		return -ENOEXEC;
	}

	LOG_INF("Connection requested");

	return 0;
}

int bytes_from_str(const char *str, uint8_t *bytes, size_t bytes_len)
{
	size_t i;
	char byte_str[3];

	if (strlen(str) != bytes_len * 2) {
		LOG_ERR("Invalid string length: %zu (expected: %d)\n",
			strlen(str), bytes_len * 2);
		return -EINVAL;
	}

	for (i = 0; i < bytes_len; i++) {
		memcpy(byte_str, str + i * 2, 2);
		byte_str[2] = '\0';
		bytes[i] = strtol(byte_str, NULL, 16);
	}

	return 0;
}



int Wifi_Stationing(void)
{
	int i;
	memset(&context, 0, sizeof(context));

	net_mgmt_init_event_callback(&wifi_shell_mgmt_cb, wifi_mgmt_event_handler,WIFI_SHELL_MGMT_EVENTS);

	net_mgmt_add_event_callback(&wifi_shell_mgmt_cb);


	net_mgmt_init_event_callback(&net_shell_mgmt_cb,net_mgmt_event_handler,NET_EVENT_IPV4_DHCP_BOUND);

	net_mgmt_add_event_callback(&net_shell_mgmt_cb);

	LOG_INF("Starting %s with CPU frequency: %d MHz", CONFIG_BOARD, SystemCoreClock/MHZ(1));
	k_sleep(K_SECONDS(1));


	LOG_INF("Static IP address (overridable): %s/%s -> %s",
		CONFIG_NET_CONFIG_MY_IPV4_ADDR,
		CONFIG_NET_CONFIG_MY_IPV4_NETMASK,
		CONFIG_NET_CONFIG_MY_IPV4_GW);

	while (1) {
		wifi_connect();

		for (i = 0; i < CONNECTION_TIMEOUT; i++) {
			k_sleep(K_MSEC(STATUS_POLLING_MS));
			cmd_wifi_status();
			if (context.connect_result) {
				break;
			}
		}
		if (context.connected) {
			k_sleep(K_FOREVER);
		} else if (!context.connect_result) {
			LOG_ERR("Connection Timed Out");
		}
	}

	return 0;
}

int udpClientSocket;
struct sockaddr_in serverAddress;
void UDP_Client(uint8_t* connected_flag) {

	
	int connectionResult;
	int sentBytes = 0;

	
	// Starve the thread until a DHCP IP is assigned to the board 
    while( !context.connected ){
		k_msleep( 100 );
	}
	// Server IPV4 address configuration 
    serverAddress.sin_family = AF_INET;
    serverAddress.sin_port = htons( UDP_CLIENT_PORT );
    zsock_inet_pton( 																\
			AF_INET, 														\
			"192.168.1.1", 								\
			&serverAddress.sin_addr );

	// Client socket creation 
	udpClientSocket = zsock_socket( 												\
							serverAddress.sin_family, 						\
							SOCK_DGRAM, 									\
							IPPROTO_UDP );									\

	if ( udpClientSocket < 0 ) {
		LOG_ERR( "UDP Client error: socket: %d\n", errno );
		k_sleep( K_FOREVER );
	}




	// Connection to the server. 
	connectionResult = connect( 											\
							udpClientSocket, 								\
							( struct sockaddr * )&serverAddress, 			\
							sizeof( serverAddress ));

	if ( connectionResult < 0 ) {
		LOG_ERR( "UDP Client error: connect: %d\n", errno );
		k_sleep( K_FOREVER );
	}
	LOG_INF( "UDP Client connected correctly" );

	*connected_flag = 1;	

}


int main(void)
{

	// START THE WIFI CONNECTION THREAD
	k_thread_create	(											\
		&wifiThread,										    \
		WIFI_STACK,										        \
		WIFI_STACK_SIZE,									    \
		(k_thread_entry_t)Wifi_Stationing,						\
		NULL,													\
		NULL,													\
		NULL,													\
		WIFI_PRIORITY,									        \
		0,														\
		K_NO_WAIT);	

	 k_thread_name_set(&wifiThread, "wifiStationing");
	 k_thread_start(&wifiThread);

	while( !context.connected ){
		k_msleep( 1000 );
	}
	LOG_INF("Connection Worked");

	uint8_t client_connected = 0;
	UDP_Client(&client_connected);

	LOG_INF("wait until client is connected");
	while(client_connected == 0){
		k_msleep( 500 );
	}
	LOG_INF("Client is connected -> start transmitting data");

	// struct wifi_ps_params params = { 0 };
	// params.wakeup_mode = WIFI_PS_WAKEUP_MODE_LISTEN_INTERVAL;
	// params.type = WIFI_PS_PARAM_WAKEUP_MODE;
	// struct net_if *iface = net_if_get_default();
	// net_mgmt(NET_REQUEST_WIFI_PS, iface, &params, sizeof(params));

	uint32_t counter = 0;
	while(1){
		transmit_data.frame_number = counter;
		fillArrayWithRandom(transmit_data.data, SENSOR_DATA_LENGTH); 

		send(udpClientSocket, (uint8_t* ) &transmit_data, sizeof(transmit_data), 0);
		//sendto(udpClientSocket, (uint8_t* ) &transmit_data, sizeof(transmit_data), 0,( struct sockaddr * )&serverAddress, sizeof( serverAddress ));
		counter++;
		k_msleep( 1000 );
	}

}

and the prj.conf

#
# Copyright (c) 2022 Nordic Semiconductor ASA
#
# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
#
CONFIG_WIFI=y
CONFIG_WIFI_NRF700X=y

# WPA supplicant
CONFIG_WPA_SUPP=y

# Below configs need to be modified based on security
# CONFIG_STA_KEY_MGMT_NONE=y
CONFIG_STA_KEY_MGMT_WPA2=y
# CONFIG_STA_KEY_MGMT_WPA2_256=y
# CONFIG_STA_KEY_MGMT_WPA3=y
CONFIG_STA_SAMPLE_SSID="RSPILAN"
CONFIG_STA_SAMPLE_PASSWORD="silicon123"



# System settings
CONFIG_NEWLIB_LIBC=y
CONFIG_NEWLIB_LIBC_NANO=n

# Networking
CONFIG_NETWORKING=y
CONFIG_NET_SOCKETS=y
CONFIG_NET_LOG=y
CONFIG_NET_IPV4=y
CONFIG_NET_UDP=y
CONFIG_NET_TCP=y
CONFIG_NET_DHCPV4=y

CONFIG_NET_PKT_RX_COUNT=8
CONFIG_NET_PKT_TX_COUNT=8

# Below section is the primary contributor to SRAM and is currently
# tuned for performance, but this will be revisited in the future.
CONFIG_NET_BUF_RX_COUNT=16
CONFIG_NET_BUF_TX_COUNT=16
CONFIG_NET_BUF_DATA_SIZE=128
CONFIG_HEAP_MEM_POOL_SIZE=153600
CONFIG_NET_TC_TX_COUNT=0

CONFIG_NET_IF_UNICAST_IPV4_ADDR_COUNT=1
CONFIG_NET_MAX_CONTEXTS=5
CONFIG_NET_CONTEXT_SYNC_RECV=y

CONFIG_INIT_STACKS=y

CONFIG_NET_L2_ETHERNET=y

CONFIG_NET_CONFIG_SETTINGS=y

CONFIG_NET_SOCKETS_POLL_MAX=4

# Memories
CONFIG_MAIN_STACK_SIZE=4096
CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=2048
CONFIG_NET_TX_STACK_SIZE=4096
CONFIG_NET_RX_STACK_SIZE=4096

# Debugging
CONFIG_STACK_SENTINEL=y
CONFIG_DEBUG_COREDUMP=y
CONFIG_DEBUG_COREDUMP_BACKEND_LOGGING=y
CONFIG_DEBUG_COREDUMP_MEMORY_DUMP_MIN=y
CONFIG_SHELL_CMDS_RESIZE=n


# Kernel options
CONFIG_ENTROPY_GENERATOR=y

# Logging
CONFIG_LOG=y
CONFIG_LOG_BUFFER_SIZE=2048
CONFIG_POSIX_CLOCK=y

CONFIG_PM=y

CONFIG_NET_CONFIG_MY_IPV4_ADDR="192.168.1.99"
CONFIG_NET_CONFIG_MY_IPV4_NETMASK="255.255.255.0"
CONFIG_NET_CONFIG_MY_IPV4_GW="192.168.1.1"

# printing of scan results puts pressure on queues in new locking
# design in net_mgmt. So, use a higher timeout for a crowded
# environment.
CONFIG_NET_MGMT_EVENT_QUEUE_TIMEOUT=5000

CONFIG_NET_SOCKETS_POSIX_NAMES=y


CONFIG_POSIX_MAX_FDS=9 

I am using nrf connect sdk v2.4.1 currently.

Thanks in advance

Julian

  • Hi Julian,

    I saw these additional spikes as well when testing yesterday. However, after I updated the Power Profiler app to v4.0.0-beta3 today and I no longer see the spikes when testing. I know there was a fix related to noise in the latest PPK2 firmware, but I do not know if it is related to this or not.

    Can you try updating as well and see if there is a difference? Please note that you will also have to update the firmware of the PPK2. You should get a prompt to update when selecting the PPK2 in the drop-down menu.

    I also saw spikes similar to yours shortly after the UDP transmission, but this was related to Power-Save poll sent from the device to AP and address resolution protocol (ARP) packet from AP to the device. All of this should show up in the Wireshark capture though, so it does not seem to be the case in your test since there are no packets.

    Best regards,
    Marte

  • Hello Marte,

    I updated the PPK2 but I have the same/similar results:

    I am pretty sure that where the marker starts is the start of the UDP transmission and the ACK is directly after that. But I measure nothing after which would explain the second plateau.

    I also observed these ARP messages from your measurement with "who has ...." but not in the one I sent to you. And they are not very frequent.
    Anyway, there is still the chance that I am missing something crucial or doing something wrong in the measurements.

    Additionally, I just saw that there is a (new?) example in the SDK "Wi-Wi: Raw TX packet". Do you think that if I try to create raw UDP packets with this sample I can achieve small transmission peaks without this long time until the device goes to sleep - or would I end up with the same result?

    br
    Julian

  • Hi Julian,

    Have you tested with a different network or different server?

    I am not able to reproduce the behavior you are seeing. I am testing with the Wi-Fi shell sample and sending with net udp send. When sending to an echo server I get two plateaus shortly after each other, and I could verify that the second was due to Power-Save poll and receiving response from the server based on the timing. When testing with a server that did not respond I only saw one plateau for the UDP and ACK.

    JulianKaroliny said:
    Do you think that if I try to create raw UDP packets with this sample I can achieve small transmission peaks without this long time until the device goes to sleep - or would I end up with the same result?

    I do not think this sample will help. By default power saving is turned off in the sample (CONFIG_NRF_WIFI_LOW_POWER=n), so it is not intended to be used with power saving.
    The sample sends raw 802.11 packets, so the fields of the MAC header are manually set in the code and you would have to set the fields of the NWK layer as well for the UDP packet.

    Best regards,
    Marte

  • Hey Marte,

    I will try to use a different server (and maybe also a different AP) and check if the problem is on that end.
    Thanks again for your fast replies and for your help. If I find anything new I will reach out here again or give an update. 

    best,
    Julian

  • Hi Julian,

    Please keep me posted if you find anything! I will do the same.

    I will start my vacation after tomorrow, and will be out of office until 2nd of January. If you have critical issues you can create a new ticket which will be assigned to one of the engineers working next week. Please note that due to reduced staffing there will be an increase in response time during the holidays.

    Happy holidays! Slight smile

    Best regards,
    Marte

Related