In my last post on the Nordic Semiconductor Thingy:91 X IoT prototyping platform, I outlined the features and architecture of the device. The combination of wireless protocols on the Thingy:91 X (Bluetooth LE, LTE-M, Wi-Fi) make it a compelling foundation for a wide variety of applications. However, there are a few intricacies to the protocol support.
Namely, the primary stated purpose of the Wi-Fi support is for network-based positioning (i.e. scan for local access points, send them to a server, get approximate location back). This leverages the nRF7002’s radio, but uses it in scan mode, consuming less resources on the host MCU. This is important, particularly if the nRF9151 is acting as the host MCU rather than the nRF5340, as the former has constrained RAM (256 KB) and does not support external execute-in-place (XIP). External XIP allows a device to relocate some of its application code to an external flash memory component, then include the external flash in its memory map in order to execute the relocated code. The nRF5340 supports external XIP, allowing it to accommodate applications with larger code size despite having the same amount of internal flash memory (1 MB) as the nRF9151. It also has 512 KB of RAM compared to the nRF9151’s 256 KB.
There are two aspects of Wi-Fi support on the nRF70 series companion ICs that
impact resource consumption on the host MCU: patches and the driver. The
firmware patches are loaded onto the nRF70 device by the host MCU and add
functionality to the base ROM. They are included in the host MCUs firmware by
default as part of the Wi-Fi driver it leverages to communicate with the nRF70
device. This increases the code size, which, as previously mentioned, can be
partially mitigated if the host supports external
XIP
(SB_CONFIG_WIFI_PATCHES_EXT_FLASH_XIP
). The nRF70 series also supports
loading patches from non-XIP QSPI
flash
(SB_CONFIG_WIFI_PATCHES_EXT_FLASH_STORE
). In this case the Wi-Fi driver on the
host loads the patch into RAM, then transfers it to the nRF70 device. As one
might expect, the
patch
that only enables scan mode is significantly smaller (32 KB) than the
patch
that enables full functionality (77.8 KB).
The second, and potentially more impactful, contributor to resource consumption on the host MCU is the flash and RAM consumption of the driver itself. Specifically, to use an nRF70 device in station mode, WPA supplicant is required, which can add anywhere from 180 KB to 250 KB to code size (flash). The total driver RAM consumption also increases from around 30 KB to around 130 KB. Attempting to fit anything of consequence alongside it into 256 KB of RAM on the nRF9151 would be challenging, which is likely why the current documentation does not indicate station mode support for nRF91 series devices (all of which have 256 KB of RAM).
However, from a raw resource perspective, the simplest station mode application should be able to run on the nRF9151. I noticed that the example included support for the nRF52840 DK (with the nRF7002 EK shield), and the nRF52840 SoC matches the nRF9151 in RAM (256 KB) and flash size (1 MB). Surely we could get it working on the nRF9151 as well.
To enable Wi-Fi on the nRF9151, an overlay file that includes the
thingy91x_wifi.dtsi
devictree file is necessary.
thingy91x_nrf9151_ns.overlay
#include <thingy91x_wifi.dtsi>
This serves to enable the nRF7002 and its dedicated power management IC (PMIC) (nPM6001), as well as the short range (Bluetooth, Wi-Fi) capabilities of the RF front end.
thingy91x_wifi.dtsi
/*
* Copyright (c) 2024 Nordic Semiconductor
* SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
*/
/* Enable short range RF */
&ldsw_rf_fe_sr_en {
/delete-property/ output-low;
output-high;
};
/* Set pmic_wifi enable signal */
&ldsw_nPM6001_en {
/delete-property/ regulator-boot-off;
regulator-boot-on;
};
/* Enable pmic_wifi */
&pmic_wifi {
status = "okay";
regulators {
status = "okay";
};
};
/* enable nRF70 */
&nrf70 {
status = "okay";
};
If we look at the schematic, both the Wi-Fi PMIC (ldsw_nPM6001_en
) and the RF
front end (ldsw_rf_fe_sr_en
) enable signals are connected to the main PMIC
(nPM1300).

ldsw_nPM6001_en
is enabled via the regulator-boot-on
label in the included
devicetree file, which will cause it to be turned on when the nPM1300 driver is
initialized.
The RF frontend is not enabled until later alongside the nPM6001 regulators.
When CONFIG_WIFI_NRF70=y
, the nrf70_support.c
“library”
is included in the build via the Thingy:91 X board
CMakeLists.txt
.
CMakeLists.txt
if(CONFIG_WIFI_NRF70)
if(CONFIG_BOARD_THINGY91X_NRF9151 OR CONFIG_BOARD_THINGY91X_NRF9151_NS)
zephyr_library()
zephyr_library_sources(nrf70_support.c)
endif()
endif()
When the Wi-Fi interface is brought up, the nrf_wifi_if_zep_start_board
function is invoked.
nrf70_support.c
static const struct device *wifi_regulator = DEVICE_DT_GET(DT_NODELABEL(reg_wifi));
static const struct gpio_dt_spec ldsw_rf_fe_sr_en = {
.port = DEVICE_DT_GET(DT_PARENT(DT_NODELABEL(ldsw_rf_fe_sr_en))),
.pin = 1,
.dt_flags = GPIO_ACTIVE_LOW | GPIO_OPEN_DRAIN | GPIO_PULL_UP,
};
/* Enable the regulator and set the GPIO pin to enable the RF frontend */
int nrf_wifi_if_zep_start_board(const struct device *dev)
{
ARG_UNUSED(dev);
int ret;
ret = regulator_enable(wifi_regulator);
if (ret) {
LOG_ERR("Cannot turn on regulator %s (%d)", wifi_regulator->name, ret);
return ret;
}
ret = regulator_set_mode(wifi_regulator, NPM6001_MODE_PWM);
if (ret) {
LOG_ERR("Cannot set mode for regulator %s (%d)", wifi_regulator->name, ret);
return ret;
}
ret = gpio_pin_set_dt(&ldsw_rf_fe_sr_en, 1);
if (ret) {
LOG_ERR("Cannot set GPIO %s (%d)", ldsw_rf_fe_sr_en.port->name, ret);
return ret;
}
/* Wait for the load switch to settle on operating voltage. */
k_sleep(K_USEC(300));
return 0;
}
The regulator_enable
and regulator_set_mode
calls utilitze the nPM6001
driver to configure the reg_wifi
regulator via the I2C connection to the
nPM6001. Then the RF front end short range capabilities are enabled via the
gpio_pin_set_dt
call. Because the port
of ldsw_rf_fe_sr_en
is set to the
parent of the devicetree node label, the GPIO is pulled high by talking to the
nPM1300 over I2C. The generated devicetree file makes these relationships plain.
zephyr.dts
i2c2: i2c@a000 {
compatible = "nordic,nrf-twim";
#address-cells = < 0x1 >;
#size-cells = < 0x0 >;
reg = < 0xa000 0x1000 >;
interrupts = < 0xa 0x1 >;
easydma-maxcnt-bits = < 0xd >;
status = "okay";
zephyr,pm-device-runtime-auto;
clock-frequency = < 0x186a0 >;
pinctrl-0 = < &i2c2_default >;
pinctrl-1 = < &i2c2_sleep >;
pinctrl-names = "default", "sleep";
bme680: bme680@76 {
status = "disabled";
compatible = "bosch,bme680";
reg = < 0x76 >;
};
pmic_main: npm1300@6b {
compatible = "nordic,npm1300";
status = "okay";
pmic-int-pin = < 0x3 >;
reg = < 0x6b >;
host-int-gpios = < &gpio0 0x2 0x0 >;
gpios_pmic: npm1300_gpios {
compatible = "nordic,npm1300-gpio";
status = "okay";
gpio-controller;
#gpio-cells = < 0x2 >;
ngpios = < 0x5 >;
phandle = < 0x9 >;
npm13_button: GPIO0 {
gpio-hog;
gpios = < 0x0 0x0 >;
};
ldsw_rf_fe_sr_en: GPIO1 {
gpio-hog;
gpios = < 0x1 0x17 >;
output-high;
};
power_switch: GPIO2 {
gpio-hog;
gpios = < 0x2 0x0 >;
};
npm6001_ready: GPIO4 {
gpio-hog;
gpios = < 0x4 0x0 >;
};
};
regulators {
compatible = "nordic,npm1300-regulator";
status = "okay";
reg_3v3: BUCK2 {
regulator-min-microvolt = < 0x325aa0 >;
regulator-max-microvolt = < 0x325aa0 >;
enable-gpios = < &gpios_pmic 0x2 0x0 >;
};
ldsw_nPM6001_en: LDO1 {
regulator-initial-mode = < 0x3 >;
regulator-allowed-modes = < 0x3 >;
regulator-boot-on;
};
ldsw_sensors: LDO2 {
regulator-initial-mode = < 0x3 >;
regulator-allowed-modes = < 0x3 >;
regulator-boot-on;
};
};
npm1300_charger: charger {
compatible = "nordic,npm1300-charger";
status = "okay";
vbus-limit-microamp = < 0x7a120 >;
term-microvolt = < 0x401640 >;
current-microamp = < 0xa4cb8 >;
dischg-limit-microamp = < 0x147260 >;
thermistor-cold-millidegrees = < 0x0 >;
thermistor-cool-millidegrees = < 0x0 >;
thermistor-warm-millidegrees = < 0xafc8 >;
thermistor-hot-millidegrees = < 0xafc8 >;
thermistor-ohms = < 0x2710 >;
thermistor-beta = < 0xd6b >;
charging-enable;
};
};
pmic_wifi: npm6001@70 {
status = "okay";
compatible = "nordic,npm6001";
reg = < 0x70 >;
regulators {
compatible = "nordic,npm6001-regulator";
status = "okay";
pmic_wifi_buck0: BUCK0 {
regulator-boot-off;
};
pmic_wifi_buck1: BUCK1 {
regulator-boot-off;
};
pmic_wifi_buck2: BUCK2 {
regulator-boot-off;
};
reg_wifi: BUCK3 {
regulator-min-microvolt = < 0x325aa0 >;
regulator-max-microvolt = < 0x325aa0 >;
regulator-initial-mode = < 0x0 >;
regulator-boot-on;
};
};
};
accel: accelerometer_lp: adxl367@1d {
status = "disabled";
compatible = "adi,adxl367";
odr = < 0x3 >;
reg = < 0x1d >;
int1-gpios = < &gpio0 0xb 0x0 >;
};
magnetometer: bmm350@14 {
status = "disabled";
compatible = "bosch,bmm350";
reg = < 0x14 >;
drdy-gpios = < &gpio0 0x7 0x1 >;
};
};
Because this setup is just enabling the hardware necessary to use Wi-Fi
capabilities, it is the same whether using the nRF7002 in scan or station mode.
Kconfig is used to determine the firmware support, including what patch is
included and loaded. By default, CONFIG_NRF70_SYSTEM_MODE=y
is selected when
using the nRF7002, which results in the default
patch being included via the
nrf_wifi
driver.
CMakeLists.txt
if (CONFIG_NRF_WIFI_PATCHES_BUILTIN)
zephyr_blobs_verify(MODULE nrf_wifi REQUIRED)
# RPU FW patch binaries based on the selected configuration
if(CONFIG_NRF70_SYSTEM_MODE)
set(NRF70_PATCH ${FW_BINS_BASE}/default/nrf70.bin)
elseif(CONFIG_NRF70_RADIO_TEST)
set(NRF70_PATCH ${FW_BINS_BASE}/radio_test/nrf70.bin)
elseif(CONFIG_NRF70_SCAN_ONLY)
set(NRF70_PATCH ${FW_BINS_BASE}/scan_only/nrf70.bin)
elseif (CONFIG_NRF70_SYSTEM_WITH_RAW_MODES)
set(NRF70_PATCH ${FW_BINS_BASE}/system_with_raw/nrf70.bin)
elseif(CONFIG_NRF70_OFFLOADED_RAW_TX)
set(NRF70_PATCH ${FW_BINS_BASE}/offloaded_raw_tx/nrf70.bin)
else()
# Error
message(FATAL_ERROR "Unsupported nRF70 patch configuration")
endif()
set(gen_inc_dir ${ZEPHYR_BINARY_DIR}/misc/generated)
zephyr_include_directories(${gen_inc_dir})
set(gen_dir ${gen_inc_dir}/nrf70_fw_patch)
generate_inc_file_for_target(
nrf_wifi
${NRF70_PATCH}
${gen_dir}/nrf70.bin.inc
)
endif()
The station example provides the configuration for this setup in the
prj.conf
.
The application can be built for the nRF9151 on the Thingy:91 X with the
following command.
west build -p -b thingy91x/nrf9151/ns .
“Barely” in the title of this post might be an understatement. The build is
successful, but utilies 99.11%
of available RAM.
[501/501] Linking C executable zephyr/zephyr.elf
Memory region Used Size Region Size %age Used
FLASH: 542612 B 800 KB 66.24%
RAM: 201600 B 203416 B 99.11%
IDT_LIST: 0 GB 32 KB 0.00%
I had set my Wi-Fi SSID (CONFIG_WIFI_CREDENTIALS_STATIC_SSID
) and password
(CONFIG_WIFI_CREDENTIALS_STATIC_PASSWORD
) in the prj.conf
, but on my first
attempt of programming the Thingy:91 X with west flash --recover
, I observed
the following output.
*** Booting nRF Connect SDK v2.9.0-7787b2649840 ***
*** Using Zephyr OS v3.7.99-1f8f3dc29142 ***
[00:00:00.565,917] <inf> net_config: Initializing network
[00:00:00.565,948] <inf> net_config: Waiting interface 1 (0x2000f5a0) to be up...
[00:00:00.566,131] <inf> net_config: IPv4 address: 192.168.1.99
[00:00:00.566,192] <inf> net_config: Running dhcpv4 client...
[00:00:00.566,833] <inf> sta: Starting thingy91x with CPU frequency: 64 MHz
[00:00:00.568,542] <inf> wifi_supplicant: wpa_supplicant initialized
[00:00:01.566,986] <inf> sta: Static IP address (overridable): 192.168.1.99/255.255.255.0 -> 192.168.1.1
[00:00:01.567,138] <err> wifi_mgmt_ext: Connection request failed
[00:00:01.567,138] <err> sta: Connection request failed
[00:00:01.567,169] <inf> sta: Status request failed
[00:00:01.620,666] <err> wifi_supplicant: Failed to add iface wlan0
The error was orginating from the following line in the application.
main.c
if (net_mgmt(NET_REQUEST_WIFI_CONNECT_STORED, iface, NULL, 0)) {
LOG_ERR("Connection request failed");
return -ENOEXEC;
}
The log line from wifi_mgmt_ext
provided a hint that this network
management
command was being handled by Nordic’s extended Wi-Fi command
library.
wifi_mgmt_ext.c
static int wifi_ext_command(uint32_t mgmt_request, struct net_if *iface, void *data, size_t len)
{
int ret = 0;
ret = add_static_network_config(iface);
if (ret) {
return ret;
}
wifi_credentials_for_each_ssid(add_stored_network, iface);
return ret;
};
NET_MGMT_REGISTER_REQUEST_HANDLER(NET_REQUEST_WIFI_CONNECT_STORED, wifi_ext_command);
To quickly diagnose the problem, I used west attach
to connect with GDB and
step through the call stack. I started by setting a breakpoint on
wifi_ext_command
and resetting the target.
(gdb) b wifi_ext_command
Breakpoint 1 at 0x6c8e0
(gdb) monitor reset
Resetting target
Shortly after boot, the application hit the breakpoint.
Breakpoint 1, wifi_ext_command (mgmt_request=mgmt_request@entry=1364590702, iface=0x2000f5a0 <__net_if_dts_ord_126_0>, data=data@entry=0x0, len=len@entry=0)
The iface
being passed matches the devicetree number of the wlan0
interface
accessed via SPI on the nRF7002.
* 126 /soc/peripheral@40000000/spi@b000/wifi@1/wlan0
Stepping through add_static_network_config
, it returns with a call to
add_network_from_credentials_struct_personal
,
which invokes the NET_REQUEST_WIFI_CONNECT
command.
wifi_mgmt_ext.c
if (net_mgmt(NET_REQUEST_WIFI_CONNECT, iface, &cnx_params,
sizeof(struct wifi_connect_req_params))) {
LOG_ERR("Connection request failed\n");
return -ENOEXEC;
}
The connect command is handled by Zephyr’s L2 network management library.
wifi_mgmt.c
static int wifi_connect(uint32_t mgmt_request, struct net_if *iface,
void *data, size_t len)
{
struct wifi_connect_req_params *params =
(struct wifi_connect_req_params *)data;
const struct device *dev = net_if_get_device(iface);
const struct wifi_mgmt_ops *const wifi_mgmt_api = get_wifi_api(iface);
if (wifi_mgmt_api == NULL || wifi_mgmt_api->connect == NULL) {
return -ENOTSUP;
}
if (!net_if_is_admin_up(iface)) {
return -ENETDOWN;
}
LOG_HEXDUMP_DBG(params->ssid, params->ssid_length, "ssid");
LOG_HEXDUMP_DBG(params->psk, params->psk_length, "psk");
if (params->sae_password) {
LOG_HEXDUMP_DBG(params->sae_password, params->sae_password_length, "sae");
}
NET_DBG("ch %u sec %u", params->channel, params->security);
if ((params->security > WIFI_SECURITY_TYPE_MAX) ||
(params->ssid_length > WIFI_SSID_MAX_LEN) ||
(params->ssid_length == 0U) ||
((params->security == WIFI_SECURITY_TYPE_PSK ||
params->security == WIFI_SECURITY_TYPE_WPA_PSK ||
params->security == WIFI_SECURITY_TYPE_PSK_SHA256 ||
params->security == WIFI_SECURITY_TYPE_WPA_AUTO_PERSONAL) &&
((params->psk_length < 8) || (params->psk_length > 64) ||
(params->psk_length == 0U) || !params->psk)) ||
((params->security == WIFI_SECURITY_TYPE_SAE_HNP ||
params->security == WIFI_SECURITY_TYPE_SAE_H2E ||
params->security == WIFI_SECURITY_TYPE_SAE_AUTO) &&
((params->psk_length == 0U) || !params->psk) &&
((params->sae_password_length == 0U) || !params->sae_password)) ||
((params->channel != WIFI_CHANNEL_ANY) &&
(params->channel > WIFI_CHANNEL_MAX)) ||
!params->ssid) {
return -EINVAL;
}
#ifdef CONFIG_WIFI_NM_WPA_SUPPLICANT_ROAMING
memset(&roaming_params, 0x0, sizeof(roaming_params));
roaming_params.is_11r_used = params->ft_used;
#endif
return wifi_mgmt_api->connect(dev, params);
}
NET_MGMT_REGISTER_REQUEST_HANDLER(NET_REQUEST_WIFI_CONNECT, wifi_connect);
The failure was occurring at get_wifi_api
, which returned a wifi_mgmt_api
,
but the connect
callback was NULL
.
(gdb) p *wifi_mgmt_api
$15 = {scan = 0x68a75 <nrf_wifi_disp_scan_zep>, connect = 0x0, disconnect = 0x0, ap_enable = 0x0, ap_disable = 0x0, ap_sta_disconnect = 0x0, iface_status = 0x0,
set_power_save = 0x68f91 <nrf_wifi_set_power_save>, set_twt = 0x6934d <nrf_wifi_set_twt>, get_power_save_config = 0x69119 <nrf_wifi_get_power_save_config>, reg_domain = 0x67ab5 <nrf_wifi_reg_domain>,
filter = 0x0, mode = 0x0, channel = 0x0, btm_query = 0x0, get_version = 0x0, get_conn_params = 0x0, set_rts_threshold = 0x69831 <nrf_wifi_set_rts_threshold>, ap_config_params = 0x0, pmksa_flush = 0x0,
get_rts_threshold = 0x69959 <nrf_wifi_get_rts_threshold>, wps_config = 0x0}
The connect
callback should be provided by the “glue
code”
that ties wpa_supplicant
into Zephyr’s networking stack.
supp_main.c
static const struct wifi_mgmt_ops mgmt_ops = {
.get_version = supplicant_get_version,
.scan = supplicant_scan,
.connect = supplicant_connect,
.disconnect = supplicant_disconnect,
...
The wpa_supplicant
error log was a good hint as to why that may not be
happening.
[00:00:01.620,666] <err> wifi_supplicant: Failed to add iface wlan0
The log originates in the
wpa_supplicant_add_iface
function when attempting to initialize the interface.
wpa_supplicant.c
if (wpa_supplicant_init_iface(wpa_s, &t_iface)) {
wpa_printf(MSG_DEBUG, "Failed to add interface %s",
iface->ifname);
wpa_supplicant_deinit_iface(wpa_s, 0, 0);
return NULL;
}
I went further down the rabbit hole of the steps required for initialization than I have space to enumerate here, but the issue became obvious after setting a breakpoint and stepping through the call stack.
(gdb) where
#0 l2_packet_init (ifname=ifname@entry=0x2002ca78 <kheap.system_heap+48680> "wlan0", own_addr=<optimized out>, protocol=protocol@entry=34958, rx_callback=0x4a781 <wpa_supplicant_rx_eapol>,
rx_callback_ctx=rx_callback_ctx@entry=0x2002ca28 <kheap.system_heap+48600>, l2_hdr=l2_hdr@entry=0)
#1 0x0004ae3a in wpa_supplicant_update_mac_addr (wpa_s=wpa_s@entry=0x2002ca28 <kheap.system_heap+48600>)
#2 0x0004b358 in wpa_supplicant_driver_init (wpa_s=wpa_s@entry=0x2002ca28 <kheap.system_heap+48600>)
#3 0x0004bdf0 in wpa_supplicant_init_iface (iface=<synthetic pointer>, wpa_s=0x2002ca28 <kheap.system_heap+48600>)
#4 wpa_supplicant_add_iface (global=global@entry=0x20028668 <kheap.system_heap+31256>, iface=iface@entry=0x20017af0 <supplicant_thread_stack+6808>, parent=parent@entry=0x0)
l2_packet_init
is where we return back to Zephyr
land,
eventually
attempting
to create a socket
.
l2_packet_zephyr.c
l2->fd = socket(AF_PACKET, l2_hdr ? SOCK_RAW : SOCK_DGRAM,
htons(protocol));
if (l2->fd < 0) {
wpa_printf(MSG_ERROR,
"Failed to open socket: %s, proto: %d, af: %d",
strerror(errno), htons(protocol), AF_PACKET);
goto fail;
}
Stepping through the socket call, we can see that the creation handler is being
provided by the nrf_modem_lib
. In other words, we are attempting to create an
offloaded socket on the nRF9151 modem, then use it for Wi-Fi via the nRF7002.
Breakpoint 10, socket (family=3, type=2, proto=36488)
394 return zsock_socket(family, type, proto);
(gdb) s
zsock_socket (proto=36488, type=2, family=3)
60 return z_impl_zsock_socket(family, type, proto);
(gdb) s
z_impl_zsock_socket (family=3, type=2, proto=36488)
109 STRUCT_SECTION_FOREACH(net_socket_register, sock_family) {
(gdb) s
112 if (sock_family->family != family &&
(gdb)
119 if (!sock_family->is_supported(family, type, proto)) {
(gdb)
nrf9x_socket_is_supported (family=3, type=2, proto=36488)
1045 if (offload_disabled) {
...
0x00057afe in z_impl_zsock_socket (family=3, type=2, proto=36488)
123 errno = 0;
(gdb) s
124 ret = sock_family->handler(family, type, proto);
(gdb)
nrf9x_socket_create (family=3, type=2, proto=36488)
1079 if (type & SOCK_NATIVE) {
(gdb) s
1090 sd = nrf9x_socket_offload_socket(family, type, proto);
(gdb) s
nrf9x_socket_offload_socket (proto=36488, type=2, family=3)
336 retval = nrf_socket(family, type, proto);
(gdb) s
nrf9x_socket_create (proto=36488, type=2, family=3)
1091 if (sd < 0) {
nrf9x_socket_create (proto=36488, type=<optimized out>, family=3)
1093 return -1;
The small fix here is to disable the nrf_modem_lib
, which is enabled by
default on the nRF9151. This can be accomplished by adding a board-specific
.conf
file for the Thingy:91 X.
thingy91x_nrf9151_ns.conf
CONFIG_NRF_MODEM_LIB=n
Building and flashing again results in the expected output, and successful connection to the local Wi-Fi network.
*** Booting nRF Connect SDK v2.9.0-7787b2649840 ***
*** Using Zephyr OS v3.7.99-1f8f3dc29142 ***
[00:00:00.562,683] <inf> net_config: Initializing network
[00:00:00.562,713] <inf> net_config: Waiting interface 1 (0x2000aff0) to be up...
[00:00:00.562,896] <inf> net_config: IPv4 address: 192.168.1.99
[00:00:00.562,957] <inf> net_config: Running dhcpv4 client...
[00:00:00.564,208] <inf> sta: Starting thingy91x with CPU frequency: 64 MHz
[00:00:00.566,162] <inf> wifi_supplicant: wpa_supplicant initialized
[00:00:01.564,483] <inf> sta: Static IP address (overridable): 1��.168.1.99/255.255.255.0 -> 192.168.1.1
[00:00:01.564,514] <inf> sta: Waiting for Wi-Fi to be ready
[00:00:03.163,818] <inf> wifi_mgmt_ext: Connection requested
[00:00:03.163,848] <inf> sta: Connection requested
[00:00:03.163,940] <inf> sta: ==================
[00:00:03.163,970] <inf> sta: State: SCANNING
[00:00:03.464,111] <inf> sta: ==================
[00:00:03.464,141] <inf> sta: State: SCANNING
[00:00:03.764,312] <inf> sta: ==================
[00:00:03.764,343] <inf> sta: State: SCANNING
[00:00:04.064,514] <inf> sta: ==================
[00:00:04.064,544] <inf> sta: State: SCANNING
[00:00:04.364,715] <inf> sta: ==================
[00:00:04.364,746] <inf> sta: State: SCANNING
[00:00:04.664,916] <inf> sta: ==================
[00:00:04.664,947] <inf> sta: State: SCANNING
[00:00:04.965,118] <inf> sta: ==================
[00:00:04.965,148] <inf> sta: State: SCANNING
[00:00:05.265,319] <inf> sta: ==================
[00:00:05.265,350] <inf> sta: State: SCANNING
[00:00:05.565,521] <inf> sta: ==================
[00:00:05.565,551] <inf> sta: State: SCANNING
[00:00:05.865,722] <inf> sta: ==================
[00:00:05.865,753] <inf> sta: State: SCANNING
[00:00:06.165,924] <inf> sta: ==================
[00:00:06.165,954] <inf> sta: State: SCANNING
[00:00:06.466,125] <inf> sta: ==================
[00:00:06.466,156] <inf> sta: State: SCANNING
[00:00:06.766,326] <inf> sta: ==================
[00:00:06.766,357] <inf> sta: State: SCANNING
[00:00:07.066,528] <inf> sta: ==================
[00:00:07.066,558] <inf> sta: State: SCANNING
[00:00:07.366,729] <inf> sta: ==================
[00:00:07.366,760] <inf> sta: State: SCANNING
[00:00:07.666,931] <inf> sta: ==================
[00:00:07.666,961] <inf> sta: State: SCANNING
[00:00:07.967,132] <inf> sta: ==================
[00:00:07.967,163] <inf> sta: State: AUTHENTICATING
[00:00:08.242,462] <inf> sta: Connected
[00:00:08.262,725] <inf> net_dhcpv4: Received: 192.168.1.238
[00:00:08.263,000] <inf> net_config: IPv4 address: 192.168.1.238
[00:00:08.263,000] <inf> net_config: Lease time: 43200 seconds
[00:00:08.263,061] <inf> net_config: Subnet: 255.255.255.0
[00:00:08.263,092] <inf> net_config: Router: 192.168.1.1
[00:00:08.263,214] <inf> sta: DHCP IP address: 192.168.1.238
[00:00:08.275,146] <inf> sta: ==================
[00:00:08.275,177] <inf> sta: State: COMPLETED
[00:00:08.275,207] <inf> sta: Interface Mode: STATION
[00:00:08.275,238] <inf> sta: Link Mode: WIFI 6 (802.11ax/HE)
[00:00:08.275,268] <inf> sta: SSID: <REDACTED>
[00:00:08.275,329] <inf> sta: BSSID: <REDACTED>
[00:00:08.275,329] <inf> sta: Band: 5GHz
[00:00:08.275,360] <inf> sta: Channel: 44
[00:00:08.275,360] <inf> sta: Security: WPA2-PSK
[00:00:08.275,390] <inf> sta: MFP: Optional
[00:00:08.275,390] <inf> sta: RSSI: -52
We also free up some RAM, though we’re stll operating on the margin.
Memory region Used Size Region Size %age Used
FLASH: 530044 B 800 KB 64.70%
RAM: 205120 B 216 KB 92.74%
IDT_LIST: 0 GB 32 KB 0.00%
Stepping through again, we can now see that the socket
implementation is
provided
by
zpacket_socket
.
0x00057bfe in z_impl_zsock_socket (family=3, type=2, proto=36488)
123 errno = 0;
(gdb) s
124 ret = sock_family->handler(family, type, proto);
(gdb) s
zpacket_socket (family=3, type=2, proto=36488)
49 fd = zvfs_reserve_fd();
There is still more to explore to determine the capabilities and limitations of using a resource constrained MCU host with nRF70 series devices, but knowing that it is possible opens up the door for some interesting multi-protocol designs. If interested in trying it out for yourself, I opened a PR to the nRF Connect SDK add ing support for using the nRF9151 on the Thingy:91 X as Wi-Fi host.