Skip to content

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

  1. Install Thonny from https://thonny.org/
  2. Connect the Raspberry Pi Pico 2 W to the computer via USB
  3. Open Thonny
  4. In Thonny, select:
  5. Tools > Options > Interpreter
  6. Choose MicroPython (Raspberry Pi Pico)
  7. Select the correct COM/serial port
  8. Save files directly to the Pico filesystem when prompted

Helpful Files on the Pico

  • boot.py for startup configuration
  • main.py for the main application
  • Optional modules such as wifi.py or net_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 uasyncio and 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 await points 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 of time.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

  1. Why is blocking code a problem in IoT applications?
  2. What does await do in uasyncio?
  3. Why is a heartbeat LED useful during development?
  4. How can timeouts improve reliability?
  5. 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
  • uasyncio enables 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