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
- Download and install Thonny from the official site.
- Open Thonny.
- Connect the Raspberry Pi Pico 2 W via USB.
2.2 Flash MicroPython to Pico 2 W
- Hold the BOOTSEL button while plugging the Pico into USB.
- It appears as a USB drive.
- Copy the MicroPython
.uf2firmware for Pico 2 W to the drive. - The board reboots automatically.
2.3 Configure Thonny
- Open Tools → Options → Interpreter
- Select:
- MicroPython (Raspberry Pi Pico)
- Choose the correct port.
- 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:
- Boot device
- Initialize hardware
- Connect to Wi-Fi
- Start sensor polling task
- Start status/heartbeat task
- Start telemetry publishing task
- Handle failures gracefully
- 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 Trueloops withoutawait - 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
8.3 LED Does Not Blink
- 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
- Why is async useful in IoT applications?
- What problem does
await asyncio.sleep()solve? - Why should tasks be small and focused?
- What is the difference between blocking and cooperative multitasking?
- 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
uasyncioenables 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