WiFi & Bluetooth on Pico 2 W
The Pico 2 W includes the CYW43439 wireless chip, adding WiFi and Bluetooth capabilities to your embedded projects. This opens up possibilities for IoT applications, web servers, network communication, and wireless sensor networks.
Understanding the CYW43439 Wireless Chip
The CYW43439 is a complete WiFi and Bluetooth solution that communicates with the RP2350 via SPI. The chip manages all wireless operations independently, and your Rust code communicates with it through the cyw43 driver.
Key Wireless Features
WiFi 802.11n
2.4GHz wireless networking with support for WPA2/WPA3 security. Connect to existing networks or create your own access point.
Bluetooth 5.2
Low-energy Bluetooth support for connecting to phones, sensors, and other Bluetooth devices.
Onboard PCB Antenna
Built-in antenna eliminates the need for external components, though range may be limited compared to external antennas.
Reserved GPIO Pins: The CYW43439 wireless chip uses GPIO 23, 24, 25, and 29 for communication with the RP2350. These pins are NOT available for general GPIO use on the Pico 2 W.
- •GPIO 23: Power control for the wireless chip
- •GPIO 24: SPI data line (MOSI/MISO)
- •GPIO 25: SPI chip select (also controls onboard LED)
- •GPIO 29: SPI clock line
Setting Up WiFi with Embassy
Embassy provides excellent support for the Pico 2 W through the embassy-rp and cyw43 crates. Here's how to initialize WiFi and connect to a network.
Required Dependencies
Add these to your Cargo.toml:
[dependencies]
embassy-executor = { version = "0.6.4", features = ["arch-cortex-m", "executor-thread"] }
embassy-rp = { version = "0.3.0", features = ["rp2350"] }
embassy-time = { version = "0.3.2", features = ["defmt"] }
embassy-net = { version = "0.5.0", features = ["tcp", "udp", "dhcpv4", "medium-ethernet"] }
cyw43 = { version = "0.2.0", features = ["defmt", "firmware-logs"] }
cyw43-pio = { version = "0.2.0", features = ["overclock"] }
static_cell = "2.1.0"
defmt = "0.3"
defmt-rtt = "0.4"
panic-probe = { version = "0.3", features = ["print-defmt"] }Complete WiFi Connection Example
This example shows how to initialize the WiFi chip and connect to your home network:
#![no_std]
#![no_main]
use cyw43_pio::PioSpi;
use defmt::*;
use embassy_executor::Spawner;
use embassy_net::{Stack, StackResources};
use embassy_rp::bind_interrupts;
use embassy_rp::gpio::{Level, Output};
use embassy_rp::peripherals::{DMA_CH0, PIO0};
use embassy_rp::pio::{InterruptHandler, Pio};
use embassy_time::{Duration, Timer};
use static_cell::StaticCell;
use {defmt_rtt as _, panic_probe as _};
bind_interrupts!(struct Irqs {
PIO0_IRQ_0 => InterruptHandler<PIO0>;
});
// Task to run the CYW43 network stack
#[embassy_executor::task]
async fn wifi_task(
runner: cyw43::Runner<'static, Output<'static>, PioSpi<'static, PIO0, 0, DMA_CH0>>
) -> ! {
runner.run().await
}
// Task to run the network stack
#[embassy_executor::task]
async fn net_task(stack: &'static Stack<cyw43::NetDriver<'static>>) -> ! {
stack.run().await
}
#[embassy_executor::main]
async fn main(spawner: Spawner) {
let p = embassy_rp::init(Default::default());
// Initialize PIO for CYW43 communication
let Pio { mut common, sm0, .. } = Pio::new(p.PIO0, Irqs);
// Set up CYW43 pins
let pwr = Output::new(p.PIN_23, Level::Low);
let cs = Output::new(p.PIN_25, Level::High);
let mut pio_spi = PioSpi::new(
&mut common,
sm0,
p.DMA_CH0,
p.PIN_24,
p.PIN_29,
p.PIN_25,
);
// Load CYW43 firmware
static STATE: StaticCell<cyw43::State> = StaticCell::new();
let state = STATE.init(cyw43::State::new());
let fw = include_bytes!("../../../firmware/43439A0.bin");
let clm = include_bytes!("../../../firmware/43439A0_clm.bin");
// Initialize CYW43 driver
let (net_device, mut control, runner) = cyw43::new(state, pwr, pio_spi, fw).await;
unwrap!(spawner.spawn(wifi_task(runner)));
// Initialize network stack
static STACK: StaticCell<Stack<cyw43::NetDriver<'static>>> = StaticCell::new();
static RESOURCES: StaticCell<StackResources<3>> = StaticCell::new();
let config = embassy_net::Config::dhcpv4(Default::default());
let seed = 0x1234_5678; // Random seed for network stack
let stack = &*STACK.init(Stack::new(
net_device,
config,
RESOURCES.init(StackResources::new()),
seed,
));
unwrap!(spawner.spawn(net_task(stack)));
// Connect to WiFi
control.init(clm).await;
control.set_power_management(cyw43::PowerManagementMode::PowerSave).await;
let wifi_network = "YOUR_WIFI_SSID";
let wifi_password = "YOUR_WIFI_PASSWORD";
info!("Connecting to WiFi...");
loop {
match control.join_wpa2(wifi_network, wifi_password).await {
Ok(_) => {
info!("WiFi connected!");
break;
}
Err(err) => {
warn!("Failed to connect to WiFi: {:?}", err);
Timer::after(Duration::from_secs(1)).await;
}
}
}
// Wait for DHCP
info!("Waiting for DHCP...");
while !stack.is_config_up() {
Timer::after_millis(100).await;
}
info!("DHCP configured!");
// Print IP address
if let Some(config) = stack.config_v4() {
info!("IP address: {:?}", config.address);
}
// Blink LED to show we're connected
loop {
control.gpio_set(0, true).await;
Timer::after_millis(100).await;
control.gpio_set(0, false).await;
Timer::after_millis(900).await;
}
}Firmware Files Required: The CYW43439 chip requires two firmware files to operate:
- •
43439A0.bin- Main firmware - •
43439A0_clm.bin- Country locale matcher (regulatory compliance)
These files are included in the Embassy repository under cyw43-firmware/.
Creating a Simple HTTP Server
Once WiFi is connected, you can create network applications like web servers. Here's a simple HTTP server example:
use embassy_net::tcp::TcpSocket;
use embassy_net::{Stack, StackResources};
#[embassy_executor::task]
async fn http_server_task(stack: &'static Stack<cyw43::NetDriver<'static>>) {
let mut rx_buffer = [0; 4096];
let mut tx_buffer = [0; 4096];
loop {
let mut socket = TcpSocket::new(stack, &mut rx_buffer, &mut tx_buffer);
socket.set_timeout(Some(Duration::from_secs(10)));
info!("Listening on TCP:80...");
if let Err(e) = socket.accept(80).await {
warn!("Accept error: {:?}", e);
continue;
}
info!("Received connection from {:?}", socket.remote_endpoint());
let mut buf = [0; 1024];
loop {
let n = match socket.read(&mut buf).await {
Ok(0) => {
warn!("Connection closed");
break;
}
Ok(n) => n,
Err(e) => {
warn!("Read error: {:?}", e);
break;
}
};
// Simple HTTP response
let response = b"HTTP/1.1 200 OK\r\n\
Content-Type: text/html\r\n\
Connection: close\r\n\
\r\n\
<html><body><h1>Hello from Pico 2 W!</h1></body></html>";
if let Err(e) = socket.write_all(response).await {
warn!("Write error: {:?}", e);
}
break;
}
}
}Bluetooth Support
The CYW43439 chip includes Bluetooth 5.2 support, but as of writing, full Bluetooth functionality in Embassy is still under development. WiFi support is mature and production-ready, while Bluetooth support is improving with each release.
WiFi Power Management
The CYW43 driver supports different power management modes to balance power consumption and responsiveness:
Power Management Modes
PowerSave (Recommended)
control.set_power_management(cyw43::PowerManagementMode::PowerSave).await;Balances power consumption with network performance. WiFi chip sleeps when idle.
Performance
control.set_power_management(cyw43::PowerManagementMode::Performance).await;WiFi chip stays active for lowest latency. Higher power consumption.
Aggressive
control.set_power_management(cyw43::PowerManagementMode::Aggressive).await;Maximum power savings. May affect connection stability.
Common WiFi Operations
Scanning for Networks
let scan_options = cyw43::ScanOptions::default();
let mut networks = [cyw43::ScanResult::default(); 10];
let count = control.scan(&scan_options, &mut networks).await;
for network in &networks[..count] {
info!("SSID: {:?}, RSSI: {}", network.ssid, network.rssi);
}Checking Connection Status
let status = control.status();
match status {
cyw43::Status::Connected => info!("WiFi connected"),
cyw43::Status::Disconnected => warn!("WiFi disconnected"),
_ => info!("WiFi status: {:?}", status),
}Disconnecting from Network
control.leave().await;
info!("Disconnected from WiFi");Troubleshooting WiFi
Common Issues
Connection Failures
- •Verify WiFi credentials (SSID and password)
- •Check that your network uses WPA2 or WPA3 (WEP is not supported)
- •Ensure you're within range of the access point
No IP Address (DHCP Issues)
- •Wait longer for DHCP to complete (can take 5-10 seconds)
- •Check that your router has DHCP enabled
- •Try using a static IP configuration instead
Firmware Loading Errors
- •Ensure firmware files are in the correct location
- •Check that PIO pins (23, 24, 25, 29) are correctly configured
- •Verify the Pico 2 W is properly powered (WiFi requires stable 3.3V)
Further Resources
- ▸Embassy Repository - Official examples and latest updates
- ▸Embassy-net Documentation - Network stack documentation
- ▸Pico 2 W Datasheet - Hardware specifications
You now have the foundation to build WiFi-enabled IoT projects with your Pico 2 W!