Pico W First Blink: Why the LED Lives on the CYW43 (Not GPIO25)
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.

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:
- raspberrypi/pico-sdk, the official Pico SDK
- raspberrypi/pico-examples, including
examples/blinkandpico_w/wifi/blink - Board header
src/boards/include/boards/pico_w.h
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.
3) Build the blink example (and know about picow_blink)
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.