The Raspberry PI Pico W added a WiFi connection to the microcontroller from RPI Foundation, enabling our projects to connect to the internet world
This tutorial will show you how to use WiFi with Raspberry PI Pico.
With the release of Raspberry PI Pico W, adding the Infineon CYW4343 module to the RP2040-based microcontroller enabled us to get connected to 2.4GHz WiFi networks. It has been a big step, as before this evolution only a few microcontrollers were able to get connected with a WiFi connection and this makes the Pico usable in smart things projects.
What We Need
As usual, I suggest adding from now to your favourite e-commerce shopping cart all needed hardware, so that at the end you will be able to evaluate overall costs and decide if continue with the project or remove them from the shopping cart. So, hardware will be only:
- A common computer (maybe with Windows, Linux or Mac). It can also be a Raspberry PI Computer board
- Raspberry PI Pico W microcontroller (with a common micro USB cable)
Check hardware prices with the following links:
Install MicroPython Firmware
Before starting with examples, you need to install the MicroPython firmware in your Raspberry PI Pico W.
Here there’s the first warning note: at the time of this post (July 2022), Raspberry PI Foundation had to publish a specific firmware in order to get the WiFi working on Raspberry PI Pico W. They promised that the firmware capabilities will be joined to official MicroPython firmware soon. But, for the moment, running the networking code with official MicroPython firmware will result in the following error when trying to import the network library:
Traceback (most recent call last): File "<stdin>", line 1, in <module> ImportError: no module named 'network'
Anyway, you can also refer to my First steps with Raspberry PI Pico for Beginners tutorial, as it also shows how to use Thonny (the same IDE I’m going to use for this tutorial). This will allow you to test the official MicroPython firmware before switching to the RPI Foundation fork.
So, if you tested that the official firmware gets the above error, please install the firmware provided by Raspberry PI Foundation from their documentation page (please put attention to selecting the firmware for the Pico W) or you can get a copy from my download page:
Briefly, you can install it by:
- pressing the BOOTSEL button on RPI Pico W
- then plugging the USB cable into your PC
- releasing the BOOTSEL button
- copying the downloaded firmware (“.uf2” file) in the storage appearing on your computer (usually labelled as “RPI-RPI2”)
- once the copy is finished, the RPI Pico will automatically reboot and get ready with the new firmware
The very first operation is checking that your WiFI network has enough signal to connect. Please remember that Raspberry PI Pico W can use only 2.4 GHz networks.
The code to perform this operation is simple and short, so I’ve added in-code comments that explain the row task:
import network #import required module wlan = network.WLAN(network.STA_IF) #initialize the wlan object wlan.active(True) #activates the wlan interface accessPoints = wlan.scan() #perform a WiFi Access Points scan for ap in accessPoints: #this loop prints each AP found in a single row on shell print(ap)
The result will be similar to the following (where I’ve used the “*” chars to hide my personal network for privacy):
>>> %Run -c $EDITOR_CONTENT (b'****er-1', b'\xac\x9e\x17k\xd6@', 2, -47, 5, 5) (b'*********G3P4', b'\xc8Z\x9f\xdc\xa47', 11, -81, 5, 3) (b'*********TWEB', b'\xcaZ\x9f\xdc\xa47', 11, -80, 5, 2)
For each discovered access point, the following info SHOULD be reported:
- The access point name (SSID)
- The basic service set identifier (BSSID)
- The channel that the access point is using
- the Received Signal Strength Indicator (RSSI): a measurement of how well your device can hear a signal from an access point or router (signals from “-70” are usable with common WiFi)
- The security protocol : 0 –> open; 1 –> WEP; 2 –> WPA-PSK; 3 –> WPA2-PSK; 4 –> WPA/WPA2-PSK)
- The info if the Access Point is hidden; 0 –> visible; 1 –> hidden
I typed “SHOULD” as the last 2 values resulting from my local scan don’t seem to be compliant with the official MicroPython WLAN docs… I don’t know if it is an implementation bug or if I’m reading these values in the wrong way, but the Access Point name and signal strength are the most important and their values seem to be reliable.
Connect to WiFi
Once scanned the WiFi networks and found that the signal is enough for reliable communication, we can now start to get connected to our home router.
The code is the following (you will have to change the red parts):
import network, rp2, time # set your WiFi Country rp2.country('IT') wlan = network.WLAN(network.STA_IF) wlan.active(True) # set power mode to get WiFi power-saving off (if needed) wlan.config(pm = 0xa11140) wlan.connect('YourHomeSSID', 'YourSSIDPassword') while not wlan.isconnected() and wlan.status() >= 0: print("Waiting to connect:") time.sleep(1) print(wlan.ifconfig())
In the MicroPython code, the “rp2.country(‘IT’)” sets the WiFi country code (mine one is IT as I’m in Italy). Change it to your 2-letter country code in order to have your WiFi using the channels allowed in your country.
The “wlan.config(pm = 0xa11140)” sets the WiFi power mode for your Raspberry PI Pico W. By default, the Pico W puts the WiFi into an energy-saving mode that makes it less responsive after a while that the connection is not used. If you need a prompt response even after time, use this to keep the WiFi power always on.
The wlan.connect() is where your WiFi credentials are saved. To say the truth, if you are going to use a Raspberry PI Pico W in a public space it’s not secure having the WiFi password exposed in plain code and you will need more advanced connection protection (like, for example, a specific network protected from the router side). For home projects, usually, it’s not a big problem. Moreover, the wlan.connect() initializes the WiFi connection.
The while loop waits for the connection to be completed. The check is also made by verifying the wlan.status() codes, that can be interpreted with the following schema:
- -3 -> CYW43_LINK_BADAUTH
- -2 -> CYW43_LINK_NONET
- -1 -> CYW43_LINK_FAIL
- 0 -> CYW43_LINK_DOWN
- 1 -> CYW43_LINK_JOIN
- 2 -> CYW43_LINK_NOIP
- 3 -> CYW43_LINK_UP
Finally, we print the IP configuration get from the router (below I show mine, but your ones should be similar):
>>> %Run -c $EDITOR_CONTENT Waiting to connect: Waiting to connect: Waiting to connect: Waiting to connect: ('192.168.1.43', '255.255.255.0', '192.168.1.1', '220.127.116.11')
The result shows:
- ‘192.168.1.43’ -> the IP address get from Raspberry PI Pico W. This is the address that you will use to reach your Raspberry PI Pico W
- ‘255.255.255.0’, -> the subnet mask
- ‘192.168.1.1’, -> the default gateway (usually your home router IP)
- ‘18.104.22.168’ -> the DNS address
Our Raspberry PI Pico W is not connected to the WiFi network and it can interact with the world.
Getting Internet pages/content with Urequests
The first example is getting info from internet pages or services. There’re different ways to get this goal, but the simpler one is using the urequests library.
This example shows how to download a generic file, but you can use the results in your MicroPython code to manage the text/json files received in order to filter and adapt whatever input.
The code is the following:
import network import urequests # set your WiFi Country rp2.country('IT') wlan = network.WLAN(network.STA_IF) wlan.active(True) # set power mode to get WiFi power-saving off (if needed) wlan.config(pm = 0xa11140) wlan.connect('YourHomeSSID', 'YourSSIDPassword') # Make GET request r = urequests.get("https://google.com") print(r.content) r.close()
Please note that you can’t download pages too big in size as to the limit of Raspberry PI Pico W memory (256KB of RAM and 2MB of Flash Memory). When this limit is exceeded, you will get an error similar to the following:
Traceback (most recent call last): File "<stdin>", line 18, in <module> File "urequests.py", line 20, in content MemoryError: memory allocation failed, allocating 116480 bytes
The above script will return the HTML code for the linked page.
Exposing HTML pages
This example shows the opposite case compared to the previous one: our Raspberry PI Pico W will publish a simple HTML page. Moreover, from this HTML page, we’ll be able to control the board LED. In this case, we’ll use the socket library, that is included in the RPI Foundation firmware too.
Please start downloading the following files and moving them to your Raspberry PI Pico W storage:
The netman.py code includes a very simple library to manage the WiFi connection for your Raspberry PI Pico W. It includes a connectWiFi() function that requires as inputs your home SSID, your WiFi password and your 2-letter country code. It returns the connection status: this is an array having at its first element the IP Address get from your home Router. Moreover, it adds better connection management with error handling, as suggested in Raspberry PI Foundation documentation.
Using such custom function, linked from an external file, makes your code far cleaner and easier to understand.
The webled.py code is the MicroPython script to run in your Raspberry PI Pico W. Of course the print() statements will show in your Thonny shell what is happening and can be removed or commented once you don’t need them.
At the start, the required modules are imported:
import netman import socket from machine import Pin
The network and rp2 modules are not required as they are imported by netman module.
After creating the LED object, that will drive the Raspberry PI Pico W board LED:
led = Pin("LED", Pin.OUT)
we set the WiFi connection credentials and country. We use variables in order to have a single place where to change them:
country = 'IT' ssid = 'YourHomeSSID' password = 'YourSSIDPassword'
We now use the netman module to initialize WiFi connection and we’ll store the connection status in a “wifi_connection” variable:
wifi_connection = netman.connectWiFi(ssid,password,country)
The next part stores the HTML code for our page to show on remote computers. You can note that we’re using the “%s” marker to make it possible to add later the LED status. Moreover, we’re also using the “wifi_connection” value (that is the IP address get from Wlan connection) to create an internal link to the same page:
html = """<!DOCTYPE html> <html> <head> <title>Pico W</title> </head> <body> <h1>Pico W</h1> <p>Current status: %s</p> <p><a href="http://"""+wifi_connection+"""/light/on">Turn ON</a></p> <p><a href="http://"""+wifi_connection+"""/light/off">Turn OFF</a></p> <p>by <a href="https://peppe8o.com">peppe8o.com</a></p> </body> </html> """
The socket is initialized and configured to bind at “0.0.0.0”: this means that our webserver will answer any request from external computers:
addr = socket.getaddrinfo('0.0.0.0', 80)[-1] s = socket.socket() s.bind(addr) s.listen(1) print('listening on', addr)
At very the first page load, the LED status may be inconsistent. So initializing it to a default value is a good way to have a controlled initial configuration:
led.value(0) stateis = "LED is OFF"
The main loop will wait for external connections. Once a generic connection comes from a computer, the shell will print the request (also useful for troubleshooting):
while True: try: cl, addr = s.accept() print('client connected from', addr) request = cl.recv(1024) print(request)
This webserver can answer to serve 3 different pages (URLs). The URLs will start always with “http://YourRpiPicoAddress” (where “YourRpiPicoAddress” is your Pico W IP address, mine one for this example will be 192.168.1.43). The root URL will show the LED status, while the URLs including “/light/on” or “/light/off” will control the LED, besides showing the same page output and the current LED status. Making it simpler, with my IP address:
- http://192.168.1.43 -> this will show the LED status, without changing it
- http://192.168.1.43/light/on -> this will power on the LED and show the LED status
- http://192.168.1.43/light/off -> this will power off the LED and show the LED status
We’ll find the URL that the computer is asking for by reading the request content and looking for the related strings. Pease note that the request string has been cut for the first 50 chars as moving from LED ON page and LED OFF page it will track the previous page with the “Referer” query and may lead to not correct status:
request = str(request)[0:50] # The [0:50] avoids getting the url directory from referer led_status = request.find('GET / HTTP') led_on = request.find('/light/on') led_off = request.find('/light/off') print( 'led on = ' + str(led_on)) print( 'led off = ' + str(led_off))
If the find function doesn’t find the required string, it will return a “-1” value. If this finds the string, it will return the related position as an integer. With this in mind, the following if statements will set the LED on/off and he related state message string:
if led_status >0: print("LED status request") # No LED action if led_on >0: print("led on") led.value(1) stateis = "LED is ON" if led_off >0: print("led off") led.value(0) stateis = "LED is OFF"
Finally, the web page is server and we also close the socket session:
response = html % stateis cl.send('HTTP/1.0 200 OK\r\nContent-type: text/html\r\n\r\n') cl.send(response) cl.close()
Moreover, in line with the “try:” statement, the final “exception” will handle errors:
except OSError as e: cl.close() print('connection closed')
The resulting page will be the following (please, remember to use your Raspberry PI Pico W address instead of mine). And yes, you can configure the HTML code as you prefer, like I added the peppe8o.com link. This will require a bit of HTML knowledge.
The official documentation to get your Raspberry PI Pico W connected is available from the Raspberry PI Foundation website at https://datasheets.raspberrypi.com/picow/connecting-to-the-internet-with-pico-w.pdf. From this documentation, I’ve freely changed some code parts and I hope you will enjoy the changes.
Interested to do more with your Raspberry PI Pico? Try to look at my Raspberry PI Pico tutorials for useful and funny projects!
We are sorry that this post was not useful for you!
Let us improve this post!
Tell us how we can improve this post?