In this exercise, we will learn how to connect to Wi-Fi using the Network Management API. Throughout this exercise, we will learn how to include support for the Network Management API and how to configure the necessary callbacks for Wi-Fi events. First, we will go through how to configure the necessary Wi-Fi parameters directly in the application to request a connection. Then, how to add CLI support, to provision the Wi-Fi credentials more securely.
Exercise steps
0. Prepare the project and build and flash it to your board.
0.1 If you haven’t already done so, clone the GitHub repository for this course.
Copy the link to the repository and use VS Code’s Command Palette to clone the repository.

1.2 In the nRF Connect extension in VS Code, select Open an existing application, and navigate to wifi-fund/l2, click on the folder called l2_e2 and select Open.
1. Enable the necessary configurations.
1.1 Enable Wi-Fi in Sysbuild.
Add the following line to the sysbuild.conf file
SB_CONFIG_WIFI_NRF70=yKconfig1.2 Enable the Wi-Fi-relevant configurations.
Add the following lines to your prj.conf file
CONFIG_WIFI=y
CONFIG_WIFI_NM_WPA_SUPPLICANT=yKconfig1.3 Optimize Wi-Fi stack to save memory.
To optimize the performance and memory usage of the Wi-Fi stack, we will reduce some of the default parameters that are unnecessarily large for our simple application.
CONFIG_NRF70_RX_NUM_BUFS specifies the number of RX buffers that can be used by the nRF Wi-Fi driver. The number of buffers must be enough to keep up with the RX traffic, otherwise packets might be dropped. We will set this to 16, which can handle moderate traffic without excessive memory usage.
CONFIG_NRF70_MAX_TX_AGGREGATION specifies the maximum number of frames that can be coalesced into a single Wi-Fi frame. More frames imply more coalescing opportunities but can add latency to the TX path as we wait for more frames to arrive. We will set this to 4 which provides moderature frame aggregation without excessive latency.
CONFIG_NRF70_RX_NUM_BUFS=16
CONFIG_NRF70_MAX_TX_AGGREGATION=4KconfigCONFIG_NRF70_RX_NUM_BUFS: Number of RX buffers, default value 48
CONFIG_NRF70_MAX_TX_AGGREGATION: Maximum number of TX packets to aggregate, default value 12
1.4 Enable the Network Management relevant configurations.
Add the following lines to your prj.conf file
CONFIG_NET_MGMT=y
CONFIG_NET_MGMT_EVENT=y
CONFIG_NET_MGMT_EVENT_INFO=yKconfigCONFIG_NET_MGMT: Adds the Network Management APICONFIG_NET_MGMT_EVENT: Adds support for runtime network event notificationsCONFIG_NET_MGMT_EVENT_INFO: Adds supports for passing information along with an event
1.5 Enable the Connection Manager and L2 connectivity.
Enable the Connection Manager to listen to the network interface and raise L4 events connected and disconnected when we are connected and increase the connectivity monitoring stack. Set the L2 connectivity to Wi-Fi.
We will also disable the auto connection feature by disabling CONFIG_L2_WIFI_CONNECTIVITY_AUTO_CONNECT since we want to establish the connection manually in this exercise for educational purposes.
Add the following lines to your prj.conf file.
CONFIG_NET_CONNECTION_MANAGER=y
CONFIG_NET_CONNECTION_MANAGER_MONITOR_STACK_SIZE=5200
CONFIG_L2_WIFI_CONNECTIVITY=y
CONFIG_L2_WIFI_CONNECTIVITY_AUTO_CONNECT=nKconfig2. Configure the Wi-Fi credentials.
To statically add the Wi-Fi network configuration to the application, we will use Kconfigs available in the Wi-Fi credentials library. Please note that this is not recommended outside of the development phase.
Add the following lines to your prj.conf file
CONFIG_WIFI_CREDENTIALS=y
CONFIG_WIFI_CREDENTIALS_STATIC=y
CONFIG_WIFI_CREDENTIALS_STATIC_SSID="<your_network_SSID>"
CONFIG_WIFI_CREDENTIALS_STATIC_PASSWORD="<your_network_password>"KconfigCONFIG_WIFI_CREDENTIALS– enables the Wi-Fi credentials management subsystemCONFIG_WIFI_CREDENTIALS_STATIC– enables the static Wi-Fi network configurationCONFIG_WIFI_CREDENTIALS_STATIC_SSID– SSID of the statically configured Wi-Fi networkCONFIG_WIFI_CREDENTIALS_STATIC_PASSWORD– Password of the statically configured Wi-FI network
3. Include the necessary header files.
Include header files for the Network Management API, the Wi-Fi management API and the Wi-Fi Credentials library.
Add the following lines to the main.c file
#include <zephyr/net/net_mgmt.h>
#include <zephyr/net/wifi_mgmt.h>
#include <net/wifi_credentials.h>C4. Define a macro for the relevant network events.
Define a bit mask of the relevant events to pass to the event handler.
NET_EVENT_L4_CONNECTED is raised by the Connection Manager when there is at least one network interface ready.
NET_EVENT_L4_DISCONNECTED is raised by the Connection Manager when there are no longer any ready network interfaces left.
#define EVENT_MASK (NET_EVENT_L4_CONNECTED | NET_EVENT_L4_DISCONNECTED)C5. Declare the callback structure for Wi-Fi events.
Declare the callback structure mgmt_cb of type struct net_mgmt_event_callback to handle the network events.
static struct net_mgmt_event_callback mgmt_cb;C6. Define the callback function for network events.
6.1 Define the boolean connected and the semaphore run_app.
Define connected to keep track of the current connection status, and the semaphore run_app to ensure the application waits for a Wi-Fi connection.
static bool connected;
static K_SEM_DEFINE(run_app, 0, 1);C6.2 Define the callback function net_mgmt_event_handler() to handle the connection and disconnection to Wi-Fi. We want LED1 on the board to reflect our current Wi-Fi connection status.
static void net_mgmt_event_handler(struct net_mgmt_event_callback *cb,
uint32_t mgmt_event, struct net_if *iface)
{
if ((mgmt_event & EVENT_MASK) != mgmt_event) {
return;
}
if (mgmt_event == NET_EVENT_L4_CONNECTED) {
LOG_INF("Network connected");
connected = true;
dk_set_led_on(DK_LED1);
k_sem_give(&run_app);
return;
}
if (mgmt_event == NET_EVENT_L4_DISCONNECTED) {
if (connected == false) {
LOG_INF("Waiting for network to be connected");
} else {
dk_set_led_off(DK_LED1);
LOG_INF("Network disconnected");
connected = false;
}
k_sem_reset(&run_app);
return;
}
}C7. Initialize and add the callback function for network events.
Initialize the callback structure mgmt_cb with the handler function net_mgmt_event_handler, then add the callback structure.
net_mgmt_init_event_callback(&mgmt_cb, net_mgmt_event_handler, EVENT_MASK);
net_mgmt_add_event_callback(&mgmt_cb);C8. Define the function wifi_args_to_params() to populate the Wi-Fi credential parameters.
Define a function that takes the parameter struct wifi_connect_req_params and assigns the relevant Wi-Fi network configuration information.
8.1 Populate the SSID and password.
The SSID and password of your network have been set in the Kconfigs CONFIG_WIFI_CREDENTIALS_STATIC_SSID and CONFIG_WIFI_CREDENTIALS_STATIC_PASSWORD.
Add the following code snippet to your application
params->ssid = CONFIG_WIFI_CREDENTIALS_STATIC_SSID;
params->ssid_length = strlen(params->ssid);
params->psk = CONFIG_WIFI_CREDENTIALS_STATIC_PASSWORD;
params->psk_length = strlen(params->psk);C8.2 Populate the remaining parameters
Add the following code snippet
params->channel = WIFI_CHANNEL_ANY;
params->security = WIFI_SECURITY_TYPE_PSK;
params->mfp = WIFI_MFP_OPTIONAL;
params->timeout = SYS_FOREVER_MS;
params->band = WIFI_FREQ_BAND_UNKNOWN;
memset(params->bssid, 0, sizeof(params->bssid));CThe last line is necessary to clear the BSSID parameter. If it is not cleared and happens to have a non-zero value, the Wi-Fi stack will assume it is a valid request and only connect to an AP matching this value.
9. Declare the variables for the network configuration parameters and interface.
9.1 Declare the variable for the network configuration parameters.
Declare the variable cnx_params of type struct wifi_connect_req_params in main()
struct wifi_connect_req_params cnx_params;C9.2 Get the network interface.
Define the pointer struct iface, and use the helper function net_if_get_first_wifi() to assign the default network interface. Return an error if the result is NULL.
struct net_if *iface = net_if_get_first_wifi();
if (iface == NULL) {
LOG_ERR("Returned network interface is NULL");
return -1;
}C10. Populate cnx_params with the network configuration.
Call wifi_args_to_params() to populate cnx_params that was declared in the previous step.
wifi_args_to_params(&cnx_params);C11. Call net_mgmt() to request the Wi-Fi connection.
Now that the necessary parameters are populated, call net_mgmt() with NET_REQUEST_WIFI_CONNECT to specify the management procedure being requested. Then pass the parameters iface and cnx_params to specify the network interface and network configuration parameters.
int err = net_mgmt(NET_REQUEST_WIFI_CONNECT, iface, &cnx_params, sizeof(struct wifi_connect_req_params));
if (err) {
LOG_ERR("Connecting to Wi-Fi failed, err: %d", err);
return ENOEXEC;
}C11. Build and flash the application to your board.
If the connection was successful, LED1 on your board should light, and you should see the following log output
*** Booting nRF Connect SDK v2.9.0-7787b2649840 ***
*** Using Zephyr OS v3.7.99-1f8f3dc29142 ***
[00:00:00.667,907] <inf> Lesson2_Exercise2: Initializing Wi-Fi driver
[00:00:00.668,243] <inf> wifi_supplicant: wpa_supplicant initialized
[00:00:01.472,503] <inf> Lesson2_Exercise2: Connecting to Wi-Fi
[00:00:07.082,397] <inf> Lesson2_Exercise2: Network connectedTerminalHowever, provisioning the Wi-Fi device by adding all the necessary information directly to the firmware is not secure nor good practice. Instead, we want to add support for shell commands in the application, so that we can enter the Wi-Fi credentials through the command line instead, just like we did in Exercise 1.
12. Enable Wi-Fi credentials storage backend.
As we covered in Wi-Fi Provisioning, the Wi-Fi credentials library provides two different backend options for credential storage: Zephyr’s settings subsystem and PSA Protected Storage. PSA is a part of the TF-M architecture, so it should be used when building with TF-M, while Zephyr’s settings subsystem can be used when building without TF-M.
Let’s create a .conf overlay file for each build target so that our sample will build regardless of the board target.
12.1 Disable the static Wi-Fi network configuration and remove the Kconfigs storing your SSID and password
CONFIG_WIFI_CREDENTIALS_STATIC=n
#CONFIG_WIFI_CREDENTIALS_STATIC_SSID="<your_network_SSID>"
#CONFIG_WIFI_CREDENTIALS_STATIC_PASSWORD="<your_network_password>"Kconfig12.2 Configure the board-specific .conf file for the board target with TF-M.
In the directory called boards in the base code exercise, open the .conf file corresponding to your board target with TF-M, e.g nrf7002dk_nrf5340_cpuapp_ns.conf or nrf5340dk_nrf5340_cpuapp_ns.conf, depending on which hardware you are using for this course.
Add the following lines to this file
CONFIG_WIFI_CREDENTIALS_BACKEND_PSA=y
CONFIG_TFM_PROFILE_TYPE_MEDIUM=y
CONFIG_PM_PARTITION_SIZE_TFM_SRAM=0x18000KconfigCONFIG_WIFI_CREDENTIALS_BACKEND_PSA: Enables the PSA backend API.CONFIG_TFM_PROFILE_TYPE_MEDIUM: Sets the desired profile for the TrustedFirmware-M (TF-M) implementation. A TF-M profile, in this context, is a set of configurations that sets the level of security and features to be included in the build. Our choice of Small profile is to achieve a balance between the included security level and the resources used.CONFIG_PM_PARTITION_SIZE_TFM_SRAM=0x18000: Sets the memory partition allocated to the TF-M to 98.3 KB. This is the memory space we recommend allocating to the TF-M to ensure it operates correctly.
12.3 Configure the board-specific .conf file for the board target without TF-M.
Since the PSA backend requires TF-M which is only included when building with TF-M (_ns board target), building without TF-M would result in build errors.
Open the .conf file in the boards directory corresponding to the board target without TF-M, e.g. nrf7002dk_nrf5340_cpuapp.conf or nrf5340dk_nrf5340_cpuapp.conf, depending on which hardware you are using for this course.
Add the following lines to this file
CONFIG_WIFI_CREDENTIALS_BACKEND_SETTINGS=y
CONFIG_FLASH=y
CONFIG_FLASH_PAGE_LAYOUT=y
CONFIG_FLASH_MAP=y
CONFIG_NVS=y
CONFIG_SETTINGS=y
CONFIG_SETTINGS_NVS=yKconfigCONFIG_WIFI_CREDENTIALS_BACKEND_SETTINGS: Enables the Zephyr settings subsystem backend.CONFIG_FLASH: Enables the flash driversCONFIG_FLASH_PAGE_LAYOUT: API for retrieving the layout of pagesCONFIG_FLASH_MAP: Enables the flash map abstraction moduleCONFIG_NVS: Enables support for non-volatile storageCONFIG_SETTINGS: Enables the settings subsystemCONFIG_SETTINGS_NVS: Enables NVS storage support
13. Enable support for shell commands in the application.
Let’s enable support for issuing commands over shell in the application
Add the following lines in prj.conf
CONFIG_SHELL=y
CONFIG_NET_L2_WIFI_SHELL=y
CONFIG_WIFI_CREDENTIALS_SHELL=y
CONFIG_SHELL_STACK_SIZE=5200KconfigCONFIG_SHELL– Enables shell commandsCONFIG_NET_L2_WIFI_SHELL– Shell commands for Wi-Fi,wifiCONFIG_WIFI_CREDENTIALS_SHELL– Shell commands for Wi-Fi credentials,wifi_credCONFIG_SHELL_STACK_SIZE– Increase the stack size allocated to shell subsystem
14. Enable the auto connect feature of the L2 Wi-Fi connectivity.
Remove the following Kconfig from the prj.conf file
#CONFIG_L2_WIFI_CONNECTIVITY_AUTO_CONNECT=nKconfigCONFIG_L2_WIFI_CONNECTIVITY automatically selects the following Kconfig
CONFIG_L2_WIFI_CONNECTIVITY_AUTO_CONNECT– When enabled, connect will automatically be called after the network interface has been brought up.
This will call Wi-Fi connect with any stored credentials on the device right after the network interface is ready.
15. Note the #ifdef directives around code relying on CONFIG_WIFI_CREDENTIALS_STATIC.
Notice in src/main.c that the code that relies on CONFIG_WIFI_CREDENTIALS_STATIC contains #ifdef directives conditional on the Kconfig. So the wifi_args_to_params() function that we defined in step 8, as well as steps 9.1, 9.2, 10 and 11 in main() will not be included in the build because we have disabled CONFIG_WIFI_CREDENTIALS_STATIC.
#ifdef CONFIG_WIFI_CREDENTIALS_STATIC
/* Code to only be included when CONFIG_WIFI_CREDENTIALS_STATIC is enabled, e.g steps 8, 9, 10 and 11 */
#endif //CONFIG_WIFI_CREDENTIALS_STATICCThis is because we will be issuing credentials and connecting using the Shell interface instead.
16. Build the application, and erase and flash to your board.
Since we have enabled both backends depending on the board target, you can choose if you would like to build with or without TF-M, depending on the security requirements of your application.
We recommend building with TF-M, for a secure application.
| Board | Build without TF-M | Build with TF-M | Extra CMake arguments |
|---|---|---|---|
| nRF7002 DK | nrf7002dk/nrf5340/cpuapp | nrf7002dk/nrf5340/cpuapp/ns | N/A |
| nRF5340 DK + nRF7002 EK | nrf5340dk/nrf5340/cpuapp | nrf5340dk/nrf5340/cpuapp/ns | -DSHIELD=nrf7002ek |
Provisioning and connecting to Wi-Fi using shell commands is the connection method we will be using in the other exercises in this course. With the exception of Lesson 3 Exercise 2, all the following exercises will use the PSA backend for storing the credentials.
When flashing, use Erase and Flash to Board to make sure any stored credentials are removed.

17. Connect to a Wi-Fi network.
Now that we have enabled shell commands, we will connect to a network through the terminal.
17.1 Open a terminal and issue the following command to store the credentials
wifi_cred add -s "<your_network_SSID>" -p "<your_network_password>" -k <key_mgmt>For the last command, refer to the output from wifi scan
<key-mgmt>: 0: None, 1: WPA2-PSK, 2: WPA2-PSK-256, 3: SAE-HNP, 4: SAE-H2E, 5: SAE-AUTO, 6: WAPI, 7: EAP-TLS, 8: WEP, 9 : WPA-PSK, 10 : WPA-Auto-Personal, 11: DPP
17.2 Issue the following command to automatically connect to the network that is stored
wifi_cred auto_connectIf the connection was successful, you should see the following log output.
*** Booting nRF Connect SDK ***
*** Using Zephyr OS ***
[00:00:00.667,907] <inf> Lesson2_Exercise2: Initializing Wi-Fi driver
[00:00:00.668,243] <inf> wifi_supplicant: wpa_supplicant initialized
uart: wifi_cred add -s "<your_network_SSID>" -p "<your_network_password>" -k <key_mgmt>
wifi_cred add -s "<your_network_SSID>" -p "<your_network_password>" -k <key_mgmt>
uart: wifi_cred auto_connect
wifi_cred auto_connect
[00:01:47.411,926] <inf> wifi_mgmt_ext: Connection requested
Connected
[00:00:38.871,398] <inf> Lesson2_Exercise2: Network connectedTerminal17.3 Reset your device and observe the auto connect feature is enabled.
To confirm that the automatic connection feature has been enabled, reset your device by pressing the RESET button.
Observe that the connection request is sent automatically by the Wi-Fi management extension library after the WPA supplicant has been initialized. And the application logs that the network is connected.
*** Booting nRF Connect SDK ***
*** Using Zephyr OS ***
[00:00:00.525,451] <inf> Lesson2_Exercise2: Initializing Wi-Fi driver
[00:00:00.525,817] <inf> wifi_supplicant: wpa_supplicant initialized
[00:00:12.086,975] <inf> wifi_mgmt_ext: Connection requested
Connected
[00:00:16.433,776] <inf> Lesson2_Exercise2: Network connectedTerminalNote
The credentials used in this exercise will be stored in the following exercises, as long as you do not Erase and Flash to Board. To issue new credentials, send
wifi_cred delete "<your_network_SSID>"
then
wifi_cred add -s "<your_network_SSID>" -p "<your_network_password>" -k <key_mgmt>
with the new credentials.