Raspberry Pi Pico W WiFi Resiliency

, updated 13 September 2024 🔖 iot ⏲️ 3 minutes to read

I no longer believe the original conclusions of this post to be accurate, because:

  1. Things started behaving correctly once I removed my use of the experimental threading library in MicroPython
  2. I believe having Thonny or another debugger attached does influence the behaviour of the code, especially when (1) is also a factor

I have since modified my WiFi connection code to include a connection timeout, and this seems to be all that is needed to ensure a stable WiFi connection.

See wifi.py for the updated code. The original post is reproduced below.

Original Post

The Raspberry Pi Pico W is a great little device for running Python code - I am using four of them as IoT sensor devices in my home, which you can read about here:

  1. Raspberry Pi Pico Home Assistant Motion & Temperature Sensor
  2. Raspberry Pi Pico Carbon Dioxide Sensor

However, there is one problem with the Pico W: the W. Between the wireless chip, its drivers, or Micropython there are problems. Just google "raspberry pi pico wifi issue" to see the spectrum of issues. So while my original post has a simple bit of code to manage the connection, this wasn’t enough to prevent the sensors from losing connectivity for hours at a time.

I found that this hanging was not solved by a machine.reset() either (see watchdog.py). Powering the boards off and then on again fixed it - which means my issue at least was very likely caused by some bad state somewhere (maybe, the WiFi SOC?).

I didn't have the time to debug the various levels of abstraction, so I set about trying to make my own code more robust. Let's analyse my first attempt to see where the pitfalls are:

wlan = network.WLAN(network.STA_IF)
wlan.active(True)
wlan.config(pm = 0xa11140, hostname = "sensorpico")

while True:
  if not wlan.isconnected():
    # wlan.connect doesn't clear out the WiFi state
    # Subsequent reconnects may not work
    wlan.connect("my SSID", "my password")
    # This loop may never exit, because the
    # WiFi chip has no connection timeout!
    while not wlan.isconnected():
      utime.sleep(1)

Let's try to improve this a bit:

wlan = network.WLAN(network.STA_IF)
- wlan.active(True)
wlan.config(pm = 0xa11140, hostname = "sensorpico")

while True:
  if not wlan.isconnected():
+   wlan.active(False)
+   wlan.active(True)
    wlan.connect("my SSID", "my password")
+   started_connect_time = utime.ticks_ms()
    while not wlan.isconnected():
+     if utime.ticks_diff(utime.ticks_ms(), started_connect_time) > 20_000:
+       break
      utime.sleep(1)

The tweaks solved the hanging problem since we now give the WiFi chip 20 seconds to finish connecting, and if it doesn't, we abort and try the whole thing again. But this doesn't clear out the WiFi state, and means that sometimes, the code can keep spinning around but never actually connecting.

It turns out that wlan.active(False) isn't doing enough for us here. Maybe it's telling the chip to power down, but it's not resetting any state on the chip, and that's what we need to do. Sure enough, there is a method which can help:

wlan = network.WLAN(network.STA_IF)
wlan.config(pm = 0xa11140, hostname = "sensorpico")

while True:
  if not wlan.isconnected():
+   wlan.deinit()
    wlan.active(True)
    wlan.connect("my SSID", "my password")
    started_connect_time = utime.ticks_ms()
    while not wlan.isconnected():
      if utime.ticks_diff(utime.ticks_ms(), started_connect_time) > 20_000:
        break # (or, raise exception)
      utime.sleep(1)

The wlan.deinit() method appears to completely wipe the WiFi chip state. At least, I think it does, because at the time of writing the documentation doesn't actually list this method. I found the method by inspecting the wlan object's methods and testing it. But, it seems to do exactly as I want.

So there you have it - resilient WiFi connection code. I decided to wrap this up in a class too (wifi.py), so that my application code only needs to make the following calls in its initialization/main loop:

wlan = wifi.WiFi("sensorpico", "my SSID", "my password")

while True:
  try:
    wlan.ensure_connected()
    # Do other stuff
  except Exception as e:
    # Do something with exception
  utime.sleep(0.1)

This was all very difficult to arrive at and took a lot of time (in fact, most of the time on this sensor project has been the WiFi debugging/tweaking/testing), but I'm happy to have cracked this particular nut - at least for now!

🏷️ wifi code connect pico raspberry pi sensor chip state issue enough micropython seems details summary

⬅️ Previous post: Raspberry Pi Pico Carbon Dioxide Sensor

➡️ Next post: Optimising Rocket Chat Content Delivery with CloudFront

🎲 Random post: Pushing to Public AWS Container Registry with GitHub Actions

Comments

Please click here to load comments.