Getting started with WiFi on Raspberry Pi Pico W board

Raspberry Pi Trading announced the Raspberry Pi Pico W board basically based on the same design as the original Raspberry Pi Pico board with RP2040 dual-core Cortex-M0+ microcontroller but adding a wireless module with WiFi 4 and Bluetooth LE 5.2, although the latter is not enabled on the board at this time.

The company sent me a sample for review/evaluation, and I’ll focus on the WiFi part since the Raspberry Pi Pico W supports the same MicroPython and C/C++ SDKs as for the Raspberry Pi Pico board plus additional APIs for wireless connectivity.

Raspberry Pi Pico W unboxing

Getting Started Raspberry Pi Pico W

The board I received was cut from a 480-unit reel, and I also got a one-meter long micro USB to USB cable, which should probably not be included by default for people ordering the $6 board.

Raspberry Pi Pico W Review

Just like its predecessor, the board is tiny, and

Raspberry Pi Pico W PCB markings

The pinout is the same as the first RP2040 board, and clearly marked on the bottom side of the board.

Connecting Raspberry Pi Pico W to your computer

Raspberry Pi Pico W Getting Started with WiFi

Once we connect the board to the host computer it should show up as the RPI-RP2 drive in your computer. My laptop is running Ubuntu 20.04, but it will be the same on Windows or macOS.

RPI-RP2 folder

There are two files in the drive with INFO_UF2.txt with some information about the UF2 bootloader version and board model, and INDEX.HTM that redirects to the Pico documentation on the Raspberry Pi website. So nothing has changed here.

Here’s the output from kernel log for reference:

WiFi with MicroPython

Raspberry Pi Pico and Pico W share the same bootloader, but the MicroPython firmware is different, possibly due to small hardware differences (e.g. user LED connection), but also because it would not make sense to add the WiFi stack to Raspberry Pi Pico and waste precious storage and memory on a resources-constrained microcontroller.

We’ll find the right firmware file on Raspberry Pi website or we can download it directly as follows:


Once we’ve downloaded it, simply copy rp2-pico-w-latest.uf2 to the RPI-RP2 drive…
rp2-pico-w-latest.uf2

… and soon the drive will disappear, and the Raspberry Pi Pico W will now show as a serial device:


Which we can also detect in BootTerm (or other tools like Putty, minicom, tio, etc…):


If we connect to the serial device, we’ll enter a REPL interface where we can type some commands to list the 2.4 GHz access points:


Great! It works. There’s only one visible access point detected because I live in the countryside.  The other value (”) is for a hidden SSID open on my Xiaomi router. According to MicroPython API documentation, the five other values are for the bssid, channel number, RSSI, authmode, and hidden status, but the last two numbers are out of range:

There are five values for security:

  • 0 – open
  • 1 – WEP
  • 2 – WPA-PSK
  • 3 – WPA2-PSK
  • 4 – WPA/WPA2-PSK

and two for hidden:

  • 0 – visible
  • 1 – hidden

To go further, we’ll look at the “Connecting to the Internet” documentation for the board. The easiest way to update the code it to install Thonny editor first:


We can run the previous MicroPython program in Thonny IDE without any issue after going to Run->Select Interpreter and selecting “MicroPython (Raspberry Pi Pico)”

Thonny IDE Raspberry Pi Pico W

Let’s connect to CNX_Software_Xiaomi SSID with the following program:


Raspberry Pi Pico W MicroPython WiFi connection

My Raspberry Pi Pico W got an IP address and I can ping it successfully:


For reference, it shows as PYPB on the network:


The firmware ships with safe, global settings, but you may enable extra channels by setting up the country:


This will be especially useful if your board fails to connect to your router due to unavailable channels.

A typical use case for this type of board is to have a web interface to control LEDs or GPIOs. Let’s run a web server to control the user LED on the board:


The code is a little long as it includes some error handling as well. It works fine as you can see from the short demo video below.

YouTube video player

The code could be easily modified to control relays or GPIOs.

It’s also possible to install and run the popular iperf3 tool on the Raspberry Pi Pico W:


The code is rather short because we’ve skipped error handling for the connection. Anyway here’s the output from the network benchmark test:


That looks fairly slow to me, even though performance is not always the main requirement on this type of platform. It may also be typical (for this benchmark) on this class of hardware as, for reference, Damien George (MicroPython’s main developer) tested it on the Pyboard D-series board and achieved around 7 Mbits/sec.

If the announcement we were told the Raspberry Pi Pico W can also be used as an access point with up to four clients. I did not find any tutorial for this in MicroPython, but there are instructions for ESP8266 and ESP32, so let’s try to setup a CNX-PICO SSID and connect from a phone:


The good news is that my Raspberry Pi Pico W is now indeed an access point, but the less good news is that the custom ssid configuration did not work, and instead the board will automatically generate an access point name (PICO349B) with an open network.

Setup access point Raspberry Pi Pico W

So the API must be different, or custom access point names are not supported on Raspberry Pi Pico W MicroPython firmware just yet.

Using WiFi on Raspberry Pi Pico W with C programming

Let’s try to reproduce the code samples above with the C/C++ SDK. If you haven’t done so already, you’ll need to set up the SDK as we did for the Raspberry Pi Pico board and get the samples in a Linux computer or Raspberry Pi board:


There’s now a new directory for Pico W:


Here’s the WiFi scan sample code:


That’s a bit more complex than our MicroPython sample above. Let’s prepare the system to build the C samples:


You’d obviously need to change the SSID and password to match the ones in your network Here’s the output from the command for reference:


We can now build the wifi_scan sample:


The output is fairly long but should start and end as follows:


The build will have generated a bunch of files including the UF2 file we’ll use to copy to the board:


Since we’ve already installed the MicroPython firmware on the board, We’ll need to press the BOOT button and power cycle the board to enter mass storage mode and copy picow_wifi_scan_poll.uf2 file to the RPI-RP2 drive.
RPI-RP2 wifi scan program

The drive will disappear to reboot and automatically start the program, but I noticed the board will not show as a serial device anymore. That’s because we need to access the serial console through the UART pins of the Raspberry Pi Pico W with pin 1 Tx, pin 2 Rx, and pin 3 GND. So I soldered a short header to the relevant pins on Raspberry Pi Pico W and I used a USB to TTL board connected to my laptop, but if you’re programming the Pico W board with a Raspberry Pi SBC you could use the UART from the 40-pin GPIO header instead.

Raspberry Pi Pico W UART serial console

Now I can use BootTerm and see CNX_Software_Xiaomi SSID and the hidden SSID from my router properly detected:


So scanning works (although it shows the same SSID several times), and we can now try to connect to our WiFi router using the following sample (from Pico W documentation) in wifi_connect/wifi_connect.c:


Just make sure you change the ssid and pass values, as well as the country (CYW43_COUNTRY_??). We’ll also need a CMakeLists.txt file:


We’ll also need to copy some extra files to your working directory:


You should now have four files in the folder:


We can now build the program as we did before:


If there is any issue during the cmake step, simply correct the CMakeLists.txt file and delete all files in the build folder before running the cmake command again.

Since I’m in Thailand, I first assumed CYW43_COUNTRY_TH would do. Wrong!:


The available countries are available in pico-sdk/lib/cyw43-driver/src/cyw43_country.h. Here’s an extract:


I see. I have to use CYW43_COUNTRY_THAILAND instead and the build could complete. We have the required UF2 file:


Let’s flash it to the board with the usual method, and see if we can indeed connect to the router:


That does not tell us much, but at least the program runs without error. Working in C with the Raspberry Pi Pico W will require some work both in terms of learning the new low-level CYW43 API, LWiP library, and even playing with FreeRTOS for more complex examples.

There’s no sample to run a webpage to control an LED, so let’s check out the iperf sample. Let’s go into the /pico-examples/build/pico_w/iperf folder, and build the sample:


In that case, we have two UF2 files:


If we look at the code this is selected through PICO_CYW43_ARCH_POLL:

  1. PICO_CYW43_ARCH_POLL = 1 –  if you are using pico_cyw43_arch_poll, then you must poll periodically from your main loop (not from a timer) to check for WiFi driver or lwIP work that needs to be done.
  2. PICO_CYW43_ARCH_POLL != 1 – if you are not using pico_cyw43_arch_poll, then WiFI driver and lwIP work is done via interrupt in the background. This sleep is just an example of some (blocking)

Those are just two different programming methods for the program flow either using polling or interrupt. The latter is normally more efficient, but more complex. I’ll just copy picow_iperf_server_background.uf2 to the board for testing. Note the implementation is based on iperf2, not iperf3 as with the MicroPython example. The iperf server is running on the Raspberry Pi Pico W:


Let’s run iperf client on the laptop:


8 Mbps that a bit better than with MicroPython, but still in the same ballpark. The result is also shown in the Pico W serial console:


If you want to run the iperf sample as a client on Pico W you’ll need to add the following two lines after the headers:


This “Getting Started Guide” is getting long and has a bit more details than I initially expected, so I’ll skip that part.

Let’s complete the WiFi tutorial with the access point demo for Raspberry Pi Pico W. Before building the sample, we’ll want to have a look at the code in pico-examples/pico_w/access_point/picow_access_point.c:


This part is in the main() function. We can see that by default the SSID (access point name0 is picow_test, the password is password (with WPA2 security), the subnet will be 192.168.4.0, and the program will also start a DHCP server so that the client can get an IP address. If you set the password to NULL, it should become an open network. Let’s changed the ap_name to picow_cnxsoft, and the password to 123456…


Let’s build the sample and copy picow_access_point_poll.uf2 or picow_access_point_background.uf2 to the board as usual. Here’s the output from the terminal:


The server on port 80 is not a web server, but instead, a basic TCP server that I suppose is meant to work with the TCP client sample installed on another Raspberry Pi Pico.

Nevertheless, I could connect my phone to the picow_cnxsoft access point…

Raspberry Pi Pico W access point Android client

as well as my Ubuntu laptop

Raspberry Pi Pico W access point Ubuntu client

Both devices also show up in Raspberry Pi Pico W’s serial terminal:


Raspberry Pi Pico W’s documentation is pretty good to get started with WiFi, but not necessarily complete, as for example, at this time there’s no example to set up an access point in MicroPython and be prepared to look into the code, for example, I had to look into the code in the C/C++ SDK, as opposed as to the documentation, to find country settings. Most people should probably get started with MicroPython, as while the C/C++ SDK is more flexible, the learning curve should be much steeper.

Share this:
FacebookTwitterHacker NewsSlashdotRedditLinkedInPinterestFlipboardMeWeLineEmailShare

Support CNX Software! Donate via cryptocurrencies, become a Patron on Patreon, or purchase goods on Amazon or Aliexpress

ROCK 5 ITX RK3588 mini-ITX motherboard

12 Replies to “Getting started with WiFi on Raspberry Pi Pico W board”

  1. Are the 3x same ssids maybe 2.4 and 5ghz and iot radio of your Xiaomi router?

    1. I was confused by that as well with the C wifi scan sample. The MicroPython WiFi scan sample does not have this issue. I don’t think the Pico W module supports 5.0 GHz at all, so I don’t have an explanation for now.

  2. Love the post, helped me get my Pico W online! The solution to the SSID not setting as you experienced is to call config on the ap before setting it as active.

    1. > The solution to the SSID not setting as you experienced is to call config on the ap before setting it as active
      I thought I had tried that, but I’ll check it again a bit later.

      1. I had a similar issue on esp32, the solution in my case was to make sure the ssid was at least 8 characters long.

  3. Hi Jean-Luc @ CNX
    will there be a followup about
    PICO W
    but using
    Circuit Python?

    i got mine just 2 days ago and today find your good article
    PICO W use MicroPython OR C++SDK
    will start to dive in.
    b.r. KLL

  4. For everybody who wants to start with PlatformIO and the Pico W: to make everything work smoothly, your platformio.ini should look like this:

  5. I was looking for a guide on how to set up the PicoW as a webserver, host a simple webpage to control stuff, and I wanted the Pico to act as an accesspoint at the same time.

    Finally, by looking at various examples I now managed to put together a script that does this. One of the scripts I learned from also did read and displayed the Pico temp sensor at the webpage, so I have made this script include that as well.

    The script below works, but I would like to improve the html layout later. Like how to make that temperaure to apear on a separate line, and also what to do if I want to have some more buttons to control multiple outputs.
    If anyone have any suggestions on how to do that, please feel free to comment.

    1. I did some minor changes to add another set of buttons, and made them now control the GP0 and GP1 outputs instead of the onboard LED. The button pairs are now put side by side instead of on top of each other.
      I still need to figure out how to split the stateis information into multiple lines so that it can show one line for each IO and one line for Temperature.
      import rp2
      import network
      import machine
      import socket
      from time import sleep_ms
      from picozero import pico_temp_sensor, pico_led

      ssid = “pico_w_ap”
      password = “12345678”

      ap = network.WLAN(network.AP_IF)
      ap.config(essid=ssid, password=password)
      ap.active(True)

      # Select the onboard LED
      led1 = machine.Pin(0, machine.Pin.OUT)
      led2 = machine.Pin(1, machine.Pin.OUT)

      html = “””<!DOCTYPE html>
      <html>
       <head> <title>Pico W AccessPoint test page</title> </head>
       <body> <h1>Pico W AccessPoint test page</h1>
         <p><a href=/light1/on><input type=button value=’Light 1 ON’></a>
         <a href=/light1/off><input type=button value=’Light 1 OFF’></p>
         <p><a href=/light2/on><input type=button value=’Light 2 ON’></a>
         <a href=/light2/off><input type=button value=’Light 2 OFF’></p>
         <p>%s</p>
       </body>
      </html>
      “””

      # Wait for connect or fail
      max_wait = 10
      while max_wait > 0:
       if ap.status() < 0 or ap.status() >= 3:
         break
       max_wait -= 1
       print(‘waiting for connection…’)
       time.sleep(1)

      # Handle connection error
      if ap.status() != 3:
       raise RuntimeError(‘network connection failed’)
      else:
       print(‘connected’)
       status = ap.ifconfig()
       print( ‘ip = ‘ + status[0] )

      # Open socket
      addr = socket.getaddrinfo(‘0.0.0.0’, 80)[0][-1]
      s = socket.socket()
      s.bind(addr)
      s.listen(1)

      print(‘listening on’, addr)
      stateis = “Use buttons to toggle LED”

      # Listen for connections
      while True:
       try:
         cl, addr = s.accept()
         print(‘client connected from’, addr)
         request = cl.recv(1024)
         print(request)
         request = str(request)
         led1_on = request.find(‘/light1/on’)
         led1_off = request.find(‘/light1/off’)
         print( ‘led1 on = ‘ + str(led1_on))
         print( ‘led1 off = ‘ + str(led1_off))
         led2_on = request.find(‘/light2/on’)
         led2_off = request.find(‘/light2/off’)
         print( ‘led2 on = ‘ + str(led2_on))
         print( ‘led2 off = ‘ + str(led2_off))
         temperature = pico_temp_sensor.temp
         
         if led1_on == 6:
           print(“led1 on”)
           led1.value(1)
           stateis = “LED1 is ON and the Pico Temperature is {}”.format(temperature)

         if led1_off == 6:
           print(“led1 off”)
           led1.value(0)
           stateis = “LED1 is OFF and the Pico Temperature is {}”.format(temperature)

         if led2_on == 6:
           print(“led2 on”)
           led2.value(1)
           stateis = “LED2 is ON and the Pico Temperature is {}”.format(temperature)

         if led2_off == 6:
           print(“led2 off”)
           led2.value(0)
           stateis = “LED2 is OFF and the Pico Temperature is {}”.format(temperature)

         response = html % stateis
         cl.send(‘HTTP/1.0 200 OK\r\nContent-type: text/html\r\n\r\n’)
         cl.send(response)
         cl.close()
         sleep_ms(100)
         
       except OSError as e:
         cl.close()
         print(‘connection closed’)

Comments are closed.

Boardcon Rockchip and Allwinner SoM and SBC products
Boardcon Rockchip and Allwinner SoM and SBC products