Skip to content

Session 1: Designing End-to-End Async IoT Applications

Synopsis

Brings together hardware, networking, concurrency, and application structure to plan complete Pico 2 W solutions with clear reliability goals.

Session Content

Session 1: Designing End-to-End Async IoT Applications

Session Duration

~45 minutes

Audience

Python developers with basic programming knowledge who are new to Raspberry Pi Pico 2 W and MicroPython asynchronous programming.

Session Goals

By the end of this session, learners will be able to: - Explain why asynchronous programming is useful in IoT systems - Set up a MicroPython development workflow with Thonny - Write basic async code using uasyncio on Raspberry Pi Pico 2 W - Design an end-to-end IoT application flow using sensors, indicators, and network communication - Build a small async IoT demo that reads a sensor and performs concurrent tasks


1. Session Outline

1.1 Theory: Why Async for IoT? (10 minutes)

  • What makes IoT applications different from desktop/server apps
  • Common Pico tasks that should run concurrently:
  • reading sensors
  • blinking LEDs/status indicators
  • handling button input
  • connecting to Wi-Fi
  • sending telemetry to a server or cloud endpoint
  • Problems with blocking code:
  • missed sensor events
  • unresponsive buttons
  • delayed network retries
  • Benefits of uasyncio:
  • cooperative multitasking
  • cleaner task separation
  • better responsiveness
  • End-to-end IoT architecture:
  • hardware layer
  • device logic layer
  • communication layer
  • optional cloud/backend layer

1.2 Setup: Development Environment (5 minutes)

  • Install Thonny
  • Select MicroPython interpreter for Raspberry Pi Pico
  • Flash MicroPython firmware for Pico 2 W
  • Test board connection
  • Recommended Thonny settings:
  • interpreter: MicroPython (Raspberry Pi Pico)
  • correct COM/serial port
  • stop/restart backend after flashing

1.3 Guided Demo: Async Building Blocks (10 minutes)

  • Import and use uasyncio
  • Define async tasks with async def
  • Use await asyncio.sleep()
  • Run multiple tasks with asyncio.create_task()
  • Use asyncio.gather() for coordinated startup

1.4 Hands-on Exercise: Concurrent LED + Sensor Simulation (15 minutes)

  • Implement:
  • a blinking status LED task
  • a simulated sensor reading task
  • a console logger task
  • Observe how tasks run together without blocking
  • Modify timing values to see scheduling effects

1.5 Wrap-up and Review (5 minutes)

  • Recap of async design principles
  • Discuss how this pattern scales to real sensors and Wi-Fi
  • Preview of next session: async GPIO and sensor polling patterns

2. Development Environment Setup

2.1 Install Thonny

  1. Download and install Thonny from the official site.
  2. Open Thonny.
  3. Connect the Raspberry Pi Pico 2 W via USB.

2.2 Flash MicroPython to Pico 2 W

  1. Hold the BOOTSEL button while plugging the Pico into USB.
  2. It appears as a USB drive.
  3. Copy the MicroPython .uf2 firmware for Pico 2 W to the drive.
  4. The board reboots automatically.

2.3 Configure Thonny

  1. Open Tools → Options → Interpreter
  2. Select:
  3. MicroPython (Raspberry Pi Pico)
  4. Choose the correct port.
  5. Verify the REPL shows the MicroPython prompt.

2.4 Quick Test

Run this in Thonny:

print("Hello from Pico 2 W")

Expected output:

Hello from Pico 2 W

3. Theory Notes: Designing End-to-End Async IoT Systems

3.1 Typical IoT Workload on Pico 2 W

A Pico 2 W IoT device often needs to: - boot quickly - connect to Wi-Fi - gather readings from sensors - publish data periodically - react to events such as button presses or alarms - keep a heartbeat indicator running

If these are handled in a single blocking loop, one slow operation can delay everything else.

3.2 Async Design Pattern

A good async design usually separates the application into tasks:

  • Task 1: Status LED heartbeat
  • Task 2: Sensor polling
  • Task 3: Network publishing
  • Task 4: Command/alert handling

Each task: - does one small job - yields control frequently - avoids long blocking loops

3.3 Key Principle

Use: - await for cooperative pauses - short-running functions - shared state only when necessary - clear task responsibilities


4. Hands-on Exercise: Concurrent Tasks with uasyncio

4.1 Learning Objective

Create a simple async application with: - a blinking onboard LED - a simulated sensor task - a periodic status message

This exercise focuses on async structure before adding real hardware or networking.

4.2 Code

Create a new file in Thonny and save it as main.py on the Pico:

import uasyncio as asyncio
from machine import Pin

# Onboard LED on Raspberry Pi Pico 2 W
led = Pin("LED", Pin.OUT)

# Shared state for the simulated sensor value
sensor_value = 0


async def blink_led():
    """Blink the onboard LED every 500 ms."""
    while True:
        led.on()
        print("LED ON")
        await asyncio.sleep(0.5)

        led.off()
        print("LED OFF")
        await asyncio.sleep(0.5)


async def read_sensor_simulated():
    """
    Simulate a sensor reading every 2 seconds.

    In a real project, this would read from a temperature, humidity,
    light, or motion sensor.
    """
    global sensor_value

    while True:
        sensor_value += 1
        print("Sensor reading:", sensor_value)
        await asyncio.sleep(2)


async def report_status():
    """Print a periodic status message every 3 seconds."""
    while True:
        print("System status: running")
        await asyncio.sleep(3)


async def main():
    """Create and coordinate all application tasks."""
    asyncio.create_task(blink_led())
    asyncio.create_task(read_sensor_simulated())
    asyncio.create_task(report_status())

    # Keep the main coroutine alive
    while True:
        await asyncio.sleep(1)


try:
    asyncio.run(main())
finally:
    asyncio.new_event_loop()

4.3 Example Output

LED ON
LED OFF
Sensor reading: 1
System status: running
LED ON
LED OFF
LED ON
Sensor reading: 2
LED OFF
System status: running
...

4.4 What to Observe

  • The LED blinks continuously
  • Sensor messages appear less often
  • Status messages appear independently
  • No task blocks the others

5. Hands-on Exercise: Replace Simulation with a Real Input

5.1 Learning Objective

Use a button as a simple event source while keeping the async structure.

5.2 Wiring

  • Connect a pushbutton between GP14 and GND
  • Use the internal pull-up resistor in software

5.3 Code

import uasyncio as asyncio
from machine import Pin

led = Pin("LED", Pin.OUT)
button = Pin(14, Pin.IN, Pin.PULL_UP)


async def blink_led():
    """Blink the onboard LED every second."""
    while True:
        led.toggle()
        print("Heartbeat:", "ON" if led.value() else "OFF")
        await asyncio.sleep(1)


async def monitor_button():
    """
    Check the button state periodically.

    Button is active-low because we use the internal pull-up resistor.
    """
    last_state = button.value()

    while True:
        current_state = button.value()

        if current_state != last_state:
            if current_state == 0:
                print("Button pressed")
            else:
                print("Button released")
            last_state = current_state

        await asyncio.sleep(0.05)


async def main():
    asyncio.create_task(blink_led())
    asyncio.create_task(monitor_button())

    while True:
        await asyncio.sleep(1)


try:
    asyncio.run(main())
finally:
    asyncio.new_event_loop()

5.4 Example Output

Heartbeat: ON
Heartbeat: OFF
Button pressed
Button released
Heartbeat: ON
Heartbeat: OFF

6. End-to-End IoT Design Discussion

6.1 Application Flow

A complete async IoT application on Pico 2 W often follows this sequence:

  1. Boot device
  2. Initialize hardware
  3. Connect to Wi-Fi
  4. Start sensor polling task
  5. Start status/heartbeat task
  6. Start telemetry publishing task
  7. Handle failures gracefully
  8. Continue running indefinitely

6.2 Task Separation Example

  • Wi-Fi task: connects and reconnects
  • Sensor task: gathers data every N seconds
  • Publisher task: sends latest data to backend
  • Indicator task: shows connection status
  • Control task: handles user input or alarms

6.3 Best Practices

  • Avoid long while True loops without await
  • Keep each task focused on one responsibility
  • Use small sleep intervals for responsive behavior
  • Prefer non-blocking periodic checks
  • Include exception handling around network operations

7. Mini Lab: Design Your Own Async IoT Device

7.1 Scenario

Design a smart environmental monitor with: - a heartbeat LED - a button for user interaction - periodic telemetry reporting - a placeholder for a temperature sensor

7.2 Questions to Answer

  • Which tasks are needed?
  • Which tasks run continuously?
  • Which tasks run periodically?
  • What should happen if Wi-Fi disconnects?
  • How often should sensor data be sampled?

7.3 Suggested Task Map

  • Task 1: LED heartbeat
  • Task 2: button monitor
  • Task 3: sensor read
  • Task 4: network publish
  • Task 5: connection supervision

8. Troubleshooting

8.1 Thonny Cannot Connect

  • Verify USB cable supports data
  • Recheck interpreter selection
  • Confirm correct serial port
  • Restart Thonny after flashing firmware

8.2 ImportError: no module named uasyncio

  • Ensure MicroPython is running on the Pico
  • Confirm you are not using standard desktop Python
  • Reflash firmware if needed
  • Confirm you are using Pin("LED", Pin.OUT)
  • Restart the board
  • Check that the script is saved as main.py

8.4 Button Reads Incorrectly

  • Verify wiring to GND
  • Use Pin.PULL_UP
  • Remember the input is active-low

9. Knowledge Check

  1. Why is async useful in IoT applications?
  2. What problem does await asyncio.sleep() solve?
  3. Why should tasks be small and focused?
  4. What is the difference between blocking and cooperative multitasking?
  5. How does uasyncio.create_task() help with concurrent behavior?

10. Session Summary

  • IoT devices often need multiple actions to happen “at the same time”
  • Blocking code can make devices feel slow or unreliable
  • uasyncio enables cooperative multitasking on the Pico 2 W
  • A clean async design separates sensor, indicator, and networking logic
  • The same pattern scales from simple demos to real cloud-connected devices

11. Suggested Next Session

  • Async GPIO input handling and debouncing
  • Reading a real sensor asynchronously
  • Connecting Pico 2 W to Wi-Fi and sending data to an HTTP endpoint

Back to Chapter | Back to Master Plan | Next Session