Session 2: Non-Blocking Network Workflows
Synopsis
Shows how to integrate network activity into the event loop without freezing local device behavior, including retries, waits, and responsive status reporting.
Session Content
Session 2: Non-Blocking Network Workflows
Session Overview
In this session, learners explore how to build responsive network-enabled MicroPython programs on the Raspberry Pi Pico 2 W using non-blocking techniques. The focus is on avoiding long pauses in code while connecting to Wi-Fi, performing HTTP requests, and integrating networking with hardware tasks such as LED blinking and sensor polling.
Learning Objectives
By the end of this session, learners will be able to:
- Explain why blocking network calls reduce responsiveness
- Use uasyncio to coordinate Wi-Fi, timers, and I/O tasks
- Write non-blocking MicroPython code for network-connected devices
- Connect Pico 2 W to Wi-Fi and perform periodic HTTP requests
- Combine network workflows with hardware behavior such as LEDs and button monitoring
Prerequisites
- Basic Python knowledge
- Completed or understood Session 1 concepts: MicroPython setup and basic GPIO
- Raspberry Pi Pico 2 W with USB cable
- Thonny IDE installed
- Wi-Fi access credentials
- Optional: LED, resistor, button, and jumper wires
Development Environment Setup
Thonny Setup
- Install Thonny from https://thonny.org/
- Connect the Raspberry Pi Pico 2 W to the computer via USB
- Open Thonny
- In Thonny, select:
- Tools > Options > Interpreter
- Choose MicroPython (Raspberry Pi Pico)
- Select the correct COM/serial port
- Save files directly to the Pico filesystem when prompted
Helpful Files on the Pico
boot.pyfor startup configurationmain.pyfor the main application- Optional modules such as
wifi.pyornet_utils.py
Theory
1. Why Blocking Network Code is a Problem
Traditional synchronous networking can pause the CPU while: - waiting for Wi-Fi connection - waiting for DNS resolution - waiting for HTTP responses - retrying after a timeout
During these pauses: - LEDs may stop blinking - buttons may not be checked - sensors may not be read on time - the device appears unresponsive
2. Non-Blocking Design with uasyncio
uasyncio allows multiple tasks to cooperate by yielding control using await. This makes it possible to:
- blink an LED while connecting to Wi-Fi
- poll sensors at intervals
- send network requests without freezing the main loop
- handle timeouts more gracefully
3. Typical Async Workflow on Pico 2 W
A practical asynchronous IoT application often includes: - a Wi-Fi connection task - a status indicator task - a sensor reading task - a network publishing task - a retry or recovery task
4. MicroPython Networking Notes
MicroPython on the Pico 2 W supports:
- network.WLAN(network.STA_IF) for station mode
- socket-based networking
- uasyncio for cooperative multitasking
- urequests in some builds, but sockets are often more reliable for learning core concepts
Session Structure
Part 1: Theory and Demo (15 minutes)
- Explain blocking vs non-blocking execution
- Show a simple blinking LED program
- Demonstrate how blocking Wi-Fi connection can pause LED updates
- Introduce
uasyncioand cooperative tasks
Part 2: Guided Hands-On Exercise 1 (15 minutes)
- Connect Pico 2 W to Wi-Fi asynchronously
- Blink the onboard LED while connection is in progress
- Display connection status in the Thonny shell
Part 3: Guided Hands-On Exercise 2 (10 minutes)
- Perform a non-blocking HTTP GET request to a test endpoint
- Print the server response
- Keep a heartbeat LED running during network activity
Part 4: Wrap-Up and Review (5 minutes)
- Review key concepts
- Discuss common failure points
- Preview how these concepts apply to cloud IoT dashboards and MQTT
Hands-On Exercise 1: Async Wi-Fi Connection with LED Status
Goal
Connect the Pico 2 W to Wi-Fi while keeping the onboard LED blinking to prove the program remains responsive.
Hardware
- Raspberry Pi Pico 2 W
- No external hardware required
- Uses onboard LED
Code: main.py
import uasyncio as asyncio
import network
import time
from machine import Pin
# -----------------------------
# Configuration
# -----------------------------
SSID = "YOUR_WIFI_SSID"
PASSWORD = "YOUR_WIFI_PASSWORD"
# Pico 2 W onboard LED
led = Pin("LED", Pin.OUT)
# -----------------------------
# Wi-Fi setup
# -----------------------------
wlan = network.WLAN(network.STA_IF)
wlan.active(True)
async def blink_led():
"""Blink the onboard LED to show the system is alive."""
while True:
led.toggle()
await asyncio.sleep_ms(250)
async def connect_wifi():
"""Connect to Wi-Fi without freezing the rest of the program."""
if wlan.isconnected():
print("Already connected:", wlan.ifconfig())
return
print("Connecting to Wi-Fi...")
wlan.connect(SSID, PASSWORD)
# Wait asynchronously until connected or timeout
timeout_ms = 15000
start = time.ticks_ms()
while not wlan.isconnected():
elapsed = time.ticks_diff(time.ticks_ms(), start)
if elapsed > timeout_ms:
print("Wi-Fi connection timed out")
return
print("Waiting for connection...")
await asyncio.sleep(1)
print("Connected!")
print("Network config:", wlan.ifconfig())
async def main():
# Run blink task in the background
blink_task = asyncio.create_task(blink_led())
# Connect to Wi-Fi while blinking continues
await connect_wifi()
# Keep the program running
await blink_task
try:
asyncio.run(main())
finally:
asyncio.new_event_loop()
Expected Output
Connecting to Wi-Fi...
Waiting for connection...
Waiting for connection...
Connected!
Network config: ('192.168.1.50', '255.255.255.0', '192.168.1.1', '8.8.8.8')
Discussion Points
await asyncio.sleep(1)allows other tasks to run- The LED continues blinking during Wi-Fi setup
- A timeout prevents endless waiting
wlan.connect()starts the connection process; it does not immediately block forever
Mini-Challenge
Modify the LED blink rate: - fast blink while connecting - slow blink after connection
Hands-On Exercise 2: Non-Blocking HTTP GET Request
Goal
Fetch data from a web endpoint while keeping the device responsive.
Example Use Case
- Read a simple online status page
- Query a public API endpoint
- Simulate a sensor upload workflow
Code: main.py
import uasyncio as asyncio
import socket
from machine import Pin
# -----------------------------
# Configuration
# -----------------------------
LED_PIN = "LED"
led = Pin(LED_PIN, Pin.OUT)
async def heartbeat():
"""Heartbeat task to show the device is still responsive."""
while True:
led.on()
await asyncio.sleep_ms(100)
led.off()
await asyncio.sleep_ms(900)
async def http_get(host, path):
"""
Perform a simple HTTP GET request using sockets.
This keeps the example focused on core networking concepts.
"""
print("Resolving host...")
addr_info = socket.getaddrinfo(host, 80)[0][-1]
print("Connecting to server...")
s = socket.socket()
s.settimeout(5)
s.connect(addr_info)
request = (
"GET {} HTTP/1.0\r\n"
"Host: {}\r\n"
"User-Agent: Pico2W\r\n"
"Connection: close\r\n"
"\r\n"
).format(path, host)
print("Sending request...")
s.send(request.encode())
print("Reading response...")
response = b""
while True:
try:
chunk = s.recv(128)
if not chunk:
break
response += chunk
await asyncio.sleep_ms(10)
except OSError:
break
s.close()
# Convert to text and show a small portion
text = response.decode(errors="ignore")
print("Response received:")
print(text[:400])
async def main():
hb = asyncio.create_task(heartbeat())
await asyncio.sleep(1)
# Example endpoint that returns plain text
await http_get("example.com", "/")
# Keep running so heartbeat continues
await hb
try:
asyncio.run(main())
finally:
asyncio.new_event_loop()
Expected Output
Resolving host...
Connecting to server...
Sending request...
Reading response...
Response received:
HTTP/1.0 200 OK
Content-Type: text/html
...
Discussion Points
- Socket operations may still be partially blocking, but
awaitpoints improve responsiveness await asyncio.sleep_ms(10)yields control between chunks- A heartbeat LED proves the system remains active
Mini-Challenge
Change the endpoint to fetch a different page and print only: - status line - content-type header
Guided Exercise 3: Combine Wi-Fi, LED, and Periodic Network Polling
Goal
Create a small async application that: - connects to Wi-Fi - blinks an LED - performs periodic HTTP requests - prints the result every 30 seconds
Code: main.py
import uasyncio as asyncio
import network
import socket
from machine import Pin
SSID = "YOUR_WIFI_SSID"
PASSWORD = "YOUR_WIFI_PASSWORD"
led = Pin("LED", Pin.OUT)
wlan = network.WLAN(network.STA_IF)
wlan.active(True)
async def blink_led():
while True:
led.toggle()
await asyncio.sleep_ms(300)
async def wait_for_wifi():
if wlan.isconnected():
return
print("Connecting to Wi-Fi...")
wlan.connect(SSID, PASSWORD)
while not wlan.isconnected():
print("Waiting for Wi-Fi...")
await asyncio.sleep(1)
print("Wi-Fi connected:", wlan.ifconfig())
async def fetch_status():
host = "example.com"
path = "/"
try:
addr = socket.getaddrinfo(host, 80)[0][-1]
s = socket.socket()
s.settimeout(5)
s.connect(addr)
req = (
"GET {} HTTP/1.0\r\n"
"Host: {}\r\n"
"Connection: close\r\n"
"\r\n"
).format(path, host)
s.send(req.encode())
data = b""
while True:
chunk = s.recv(128)
if not chunk:
break
data += chunk
await asyncio.sleep_ms(5)
s.close()
text = data.decode(errors="ignore")
first_line = text.split("\r\n")[0]
print("HTTP status:", first_line)
except Exception as e:
print("Fetch error:", e)
async def periodic_poll():
while True:
await fetch_status()
print("Next check in 30 seconds")
await asyncio.sleep(30)
async def main():
await wait_for_wifi()
asyncio.create_task(blink_led())
await periodic_poll()
try:
asyncio.run(main())
finally:
asyncio.new_event_loop()
Expected Output
Connecting to Wi-Fi...
Waiting for Wi-Fi...
Wi-Fi connected: ('192.168.1.50', '255.255.255.0', '192.168.1.1', '8.8.8.8')
HTTP status: HTTP/1.0 200 OK
Next check in 30 seconds
Discussion Points
- Separate tasks keep the code organized
- Network polling runs periodically without blocking LED blinking
- Exceptions are caught to avoid crashing the entire application
Common Problems and Fixes
1. Wi-Fi Does Not Connect
Possible causes: - wrong SSID or password - weak signal - Pico 2 W not in station mode - router restrictions
Fixes:
- verify credentials
- move closer to the router
- print wlan.status() while debugging
- power-cycle the board
2. Code Freezes or Becomes Unresponsive
Possible causes:
- using time.sleep() inside async code
- long loops without await
- large blocking socket operations
Fixes:
- replace time.sleep() with await asyncio.sleep()
- insert yield points in loops
- structure work into tasks
3. DNS or HTTP Request Fails
Possible causes: - no internet access - incorrect host name - endpoint unavailable
Fixes:
- test with example.com
- add timeouts
- retry after delay
Best Practices
- Use
uasyncio.sleep()instead oftime.sleep()in async programs - Keep network tasks short and retryable
- Add timeouts for connection attempts
- Catch exceptions around network operations
- Use clear task names and small functions
- Test one task at a time before combining them
Review Questions
- Why is blocking code a problem in IoT applications?
- What does
awaitdo inuasyncio? - Why is a heartbeat LED useful during development?
- How can timeouts improve reliability?
- What is the benefit of separating Wi-Fi connection and polling into different tasks?
Suggested Extension Activity
Create a simple network monitor that: - connects to Wi-Fi - checks a website every minute - flashes the LED faster if the request fails - prints a success or failure counter
Session Summary
- Blocking network code can make a Pico 2 W appear frozen
uasyncioenables cooperative multitasking- Wi-Fi setup, LED blinking, and HTTP requests can run together
- Timeouts, exception handling, and task separation improve robustness
- These patterns are foundational for IoT dashboards, telemetry, and cloud-connected devices
Back to Chapter | Back to Master Plan | Previous Session | Next Session