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.
In the GitHub repository for this course, go to the base code for this exercise, found in lesson2/wififund_less2_exer2
.
1. Enable the necessary configurations.
1.1 Enable the Wi-Fi-relevant configurations.
Add the following lines to your prj.conf
file
CONFIG_WIFI=y
CONFIG_WIFI_NRF700X=y
CONFIG_WPA_SUPP=y
Kconfig1.2 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=y
CONFIG_NET_MGMT_EVENT_STACK_SIZE=4096
CONFIG_NET_CONNECTION_MANAGER=y
CONFIG_WIFI_MGMT_EXT=y
Kconfig2. 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 network3. Include the necessary header files.
Add the following lines to the main.c
file
#include <zephyr/net/wifi.h>
#include <zephyr/net/wifi_mgmt.h>
#include <zephyr/net/net_mgmt.h>
#include <net/wifi_mgmt_ext.h>
C4. Define a macro for the relevant network events.
Define a bit mask of the relevant events to pass to the event handler.
#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. 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.
7.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);
C7.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;
#if NCS_VERSION_NUMBER > 0x20600
memset(params->bssid, 0, sizeof(params->bssid));
#endif
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. However, this parameter does not exist in the wifi_connect_req_params
until nRF Connect SDK v2.6.1, hence the if-endif conditional.
8. Declare the variables for the network configuration parameters and interface.
8.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;
C8.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;
}
C9. 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);
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 2.6.1-3758bcbfa5cd ***
[00:00:01.472,503] <inf> Lesson2_Exercise2: Connecting to Wi-Fi
[00:00:07.082,397] <inf> Lesson2_Exercise2: Network connected
TerminalProvisioning 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 backend depending on the board target
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 prj.conf
overlay file for each build configuration 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 Create a board-specific .conf
file for the board target with TF-M.
Create a directory called boards
in the base code exercise, and create a file called 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=0x18000
CONFIG_MBEDTLS_HEAP_SIZE=16384
KconfigCONFIG_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 Medium 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. CONFIG_MBEDTLS_HEAP_SIZE=16384
: Configures the heap memory size allocated to the mbedTLS library, which is used for TLS-related operations. 12.3 Create a 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 (“non-secure” board target), building without TF-M (“secure” board target) would result in build errors.
Create another file in the boards
for the board target, called 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=y
KconfigCONFIG_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 support12.4 Include the header file for the Wi-Fi credentials library in main.c
#include <net/wifi_credentials.h>
C13. 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_WIFI_CREDENTIALS_SHELL=y
CONFIG_SHELL_STACK_SIZE=4400
KconfigCONFIG_SHELL
– Enables shell commandsCONFIG_WIFI_CREDENTIALS_SHELL
– Shell commands to manage Wi-Fi credentialsCONFIG_SHELL_STACK_SIZE
– Increase the stack size allocated to shell subsystem13. Comment out the unnecessary code in main.c
.
To avoid build errors and warnings, let’s comment out step 7 (the function wifi_args_to_params()
), step 8.1, 8.2, as well as step 10 and 11.
14. Build and flash the application 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.
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 |
15. Connect to a Wi-Fi network.
Now that we have enabled shell commands, we will connect to a network through the terminal.
15.1 Open a terminal and issue the following command to store the credentials
wifi_cred add "<your_network_SSID>" WPA2-PSK "<your_network_password>"
15.2 Issue the following command to automatically connect to the network that is stored
wifi_cred auto_connect
If the connection was successful, you should see the following log output.
*** Booting nRF Connect SDK 2.6.1-3758bcbfa5cd ***
uart: wifi_cred add "<your_network_SSID>" WPA2-PSK "<your_network_password>"
wifi_cred add "<your_network_SSID>" WPA2-PSK "<your_network_password>"
uart: wifi_cred auto_connect
wifi_cred auto_connect
[00:00:38.871,398] <inf> Lesson2_Exercise2: Network connected[1B][0m
TerminalProvisioning 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.