Pico W First Blink: Why the LED Lives on the CYW43 (Not GPIO25)

3 views 0 likes 0 comments 19 minutesOriginalTutorials

Walk Raspberry Pi's official pico-examples blink on classic Pico W: the user LED is on the CYW43 wireless chip (CYW43_WL_GPIO_LED_PIN), not RP2040 GPIO25 — so you must build with -DPICO_BOARD=pico_w.

#Raspberry Pi #Pico W #pico-sdk #pico-examples #CYW43 #RP2040 #Microcontroller #Embedded #Affiliate
Pico W First Blink: Why the LED Lives on the CYW43 (Not GPIO25)

Pico W First Blink: Why the LED Lives on the CYW43 (Not GPIO25)

The quickest honest test of a new Raspberry Pi Pico W is the blink example that ships inside Raspberry Pi's own SDK. It is also where a lot of "my board is dead" tickets start, because the on-board LED is not an RP2040 GPIO. On the classic Pico W it lives on the CYW43439 wireless chip, and you only reach it after the CYW43 driver is initialized.

Everything below comes from open-source, Raspberry Pi–maintained sources:

I verified the board header and blink sources against upstream copies fetched for this write-up (pico_w.h, blink/blink.c, blink/CMakeLists.txt, pico_w/wifi/blink/picow_blink.c). I did not flash hardware for this post, and I did not run a full pico-sdk compile on the machine I wrote on, so treat the steps as source-verified, not as a flash log.

This post is about the classic Pico W (RP2040 + CYW43439). It is not the Pico 2 W (PICO_BOARD=pico2_w). If your silkscreen says Pico 2 W, use that board definition instead.

Disclosure: further down I link one eBay listing for the board. That link is an eBay affiliate link (customid=blog01); if you buy through it I may earn a commission at no extra cost to you. Check the listing yourself before you pay.

The two facts that matter before you flash anything

1. There is no PICO_DEFAULT_LED_PIN on Pico W. The board header says so in plain text:

c 复制代码
// --- LED ---
// no PICO_DEFAULT_LED_PIN - LED is on Wireless chip
// no PICO_DEFAULT_WS2812_PIN

Instead it defines:

c 复制代码
#ifndef CYW43_WL_GPIO_LED_PIN
#define CYW43_WL_GPIO_LED_PIN 0
#endif

and sets PICO_CYW43_SUPPORTED. The LED is WL_GPIO0 on the wireless chip — not RP2040 GPIO25.

2. Building as plain pico leaves the LED dark. The non-wireless Pico does put the LED on GPIO25. On Pico W, GPIO25 is reused as the CYW43 SPI chip-select (CYW43_DEFAULT_PIN_WL_CS is 25u in the same header). If you configure with the wrong board, the classic gpio_put(25, …) path either never compiles into the CYW43 branch, or you are poking the Wi-Fi CS line — neither lights the user LED.

Official examples/blink/blink.c branches on the macros:

c 复制代码
#if defined(PICO_DEFAULT_LED_PIN)
    gpio_init(PICO_DEFAULT_LED_PIN);
    gpio_set_dir(PICO_DEFAULT_LED_PIN, GPIO_OUT);
#elif defined(CYW43_WL_GPIO_LED_PIN)
    return cyw43_arch_init();
#endif

and later:

c 复制代码
cyw43_arch_gpio_put(CYW43_WL_GPIO_LED_PIN, led_on);

The blink CMakeLists.txt only links the driver when the board supports it:

cmake 复制代码
if (PICO_CYW43_SUPPORTED)
    target_link_libraries(blink pico_cyw43_arch_none)
endif()

So the fix is not "try a different GPIO number." It is -DPICO_BOARD=pico_w so the CYW43 path is compiled in.

Classic Pico W vs Pico / Pico 2 W

Board CMake board User LED path
Pico (no wireless) pico PICO_DEFAULT_LED_PIN = GPIO25
Pico W (this post) pico_w CYW43_WL_GPIO_LED_PIN = 0 via cyw43_arch_*
Pico 2 W pico2_w Also CYW43, different board header — not covered here

If the listing photos show a green RP2040 board with a separate wireless module can, you almost certainly have classic Pico W. Confirm the silkscreen before you pick a PICO_BOARD value.

The board I'd point you to

If you still need a board, this is the listing I checked while writing: NEW Raspberry Pi Pico W (Wireless) - RP2040 on eBay (affiliate link).

When I opened it on Nov 7, 2026, the title was NEW Raspberry Pi Pico W (Wireless) - RP2040, condition was New, Buy It Now at US $23.01 with Free USPS Ground Advantage shipping from Chattanooga, Tennessee, United States. The seller (FindersKeepersTN) showed about 99.7% positive feedback from roughly 6,164 ratings, and returns were accepted for 30 days with the seller paying return shipping. Photos show two boards in clear factory packaging (green PCB + wireless module) — classic Pico W packaging, not Pico 2 W.

Whichever seller you buy from, check two things on arrival: the silkscreen says Pico W (not Pico 2 W), and you are willing to build with PICO_BOARD=pico_w so the CYW43 LED path is active.

1) Install the Pico SDK toolchain

Follow Raspberry Pi's Getting started with the Pico SDK for your OS (ARM GCC + CMake + the SDK). A typical layout:

bash 复制代码
## clone SDK (once)
git clone https://github.com/raspberrypi/pico-sdk.git
cd pico-sdk && git submodule update --init && cd ..
export PICO_SDK_PATH="$PWD/pico-sdk"

You also need a Pico-capable ARM toolchain (arm-none-eabi-gcc) and CMake. When setup is done, arm-none-eabi-gcc --version and cmake --version should both print something useful. Use a pico-sdk release that includes the pico_w board header.

2) Clone pico-examples and configure for Pico W

bash 复制代码
git clone https://github.com/raspberrypi/pico-examples.git
cd pico-examples
mkdir -p build && cd build
cmake -DPICO_BOARD=pico_w ..

-DPICO_BOARD=pico_w is the critical line. It pulls in boards/pico_w.h, defines PICO_CYW43_SUPPORTED, and leaves PICO_DEFAULT_LED_PIN undefined so blink takes the CYW43 branch.

bash 复制代码
cmake --build . --target blink

That builds examples/blink, which auto-selects CYW43 when the board header defines CYW43_WL_GPIO_LED_PIN.

There is also an explicit Wi-Fi-folder example that does the same thing without the #ifdef dance:

bash 复制代码
cmake --build . --target picow_blink

Upstream pico_w/wifi/blink/picow_blink.c is short on purpose: cyw43_arch_init(), then cyw43_arch_gpio_put(CYW43_WL_GPIO_LED_PIN, 1/0) in a loop, linking pico_cyw43_arch_none (Wi-Fi stack not required just to toggle the LED GPIO on the wireless chip).

Either target is fine for a first LED check. Prefer blink if you want one tree that also works for plain Pico; prefer picow_blink if you want the Pico W path spelled out with no macros.

4) Flash via BOOTSEL UF2

Hold BOOTSEL, plug the Pico W into USB, release BOOTSEL. A mass-storage volume named something like RPI-RP2 should appear. Copy the UF2 onto it:

bash 复制代码
## Linux example — adjust the mount path / volume name for your OS
cp blink/blink.uf2 /media/$USER/RPI-RP2/
## or, for the dedicated example:
## cp pico_w/wifi/blink/picow_blink.uf2 /media/$USER/RPI-RP2/

The board reboots on its own. If the CYW43 path is active, the on-board LED should blink at 250 ms on / 250 ms off (the delay used in both official examples).

Use a data USB cable. Charge-only cables are still the most common fake "dead board," and they never present the BOOTSEL drive.

Troubleshooting in one table

Symptom Most likely cause Fix
UF2 flashes, LED stays dark Built with PICO_BOARD=pico (GPIO25 path) Reconfigure with -DPICO_BOARD=pico_w and rebuild
Link / undefined CYW43 symbols Board is pico_w but CYW43 lib not linked Ensure PICO_CYW43_SUPPORTED path links pico_cyw43_arch_none (blink's CMake already does this)
cyw43_arch_init() fails at runtime Driver / firmware init issue, or wrong board Confirm silkscreen is Pico W; rebuild from a clean build/ with pico_w
No RPI-RP2 drive Charge-only cable, bad port, or BOOTSEL not held Swap cable; hold BOOTSEL before plug-in
Thought GPIO25 was the LED That is true on plain Pico; on Pico W GPIO25 is WL CS Stop toggling 25; use cyw43_arch_gpio_put(CYW43_WL_GPIO_LED_PIN, …)
Board is Pico 2 W Wrong board define for this post Use PICO_BOARD=pico2_w and the matching board header — not pico_w

Takeaway

When a new Pico W seems dead after a "GPIO25 blink," do not start by swapping pin numbers the way you would on a plain Pico. On the classic Pico W the official answer is already in pico-sdk / pico-examples: there is no PICO_DEFAULT_LED_PIN, the LED is CYW43_WL_GPIO_LED_PIN (0) on the wireless chip, and you must build with -DPICO_BOARD=pico_w so cyw43_arch_init() / cyw43_arch_gpio_put actually run.

Affiliate disclosure: the eBay link above is an affiliate link (customid=blog01). Prices, stock and seller details change; I describe the listing as I saw it on Nov 7, 2026.

Last Updated:2026-11-07 10:32:04

Comments (0)

Post Comment

Loading...
0/500

No comments yet, be the first to comment!