The Pico In MicroPython: Asyncio Client
Written by Mike James & Harry Fairhead   
Wednesday, 30 July 2025
Article Index
The Pico In MicroPython: Asyncio Client
Using uasyncio
Async Networking

Async Networking

The asyncio module is primarily designed to work with asynchronous network connections. It doesn’t provide high-level networking facilities. There is no asynchronous download of an HTML page, for example. However, it provides a class that caters for high-level clients and another for server objects which make working with general TCP connections very easy.

Communication with both client and server objects is via streams which are modeled on files. Both client and server objects return streams to allow the TCP connection to be used. The MicroPython implementation of uasyncio uses a single Stream object as a reader and a writer.

The supported read methods are:

  • read(n = -1) Reads up to n bytes as a bytes object
    The default, n = -1, is to read until the end of the file signal (EOF) is received and return all read bytes

  • readline() Reads a sequence of bytes ending with \n
    If EOF is received and \n was not found, the method returns partially read data. If EOF is received and the internal buffer is empty, returns an empty bytes object

  • readexactly(n) Reads exactly n bytes and raises an EOF error if EOF is reached before n can be read

  • readinto(buf) Reads up to len(buf) bytes into buf

Notice that all of the reading methods are coroutines as there may not be enough data ready to satisfy the call. In this case the coroutine is suspended and the main thread is freed. That is, calls to functions that read data are asynchronous coroutines. Also notice that while there are references to using EOF to signal the end of a transaction, in general EOF isn’t particularly useful when dealing with sockets. Sockets tend to be left open until they are no longer required and data is usually sent in some sort of format that lets you work out when you have read a useful chunk of data that can be processed. Generally, if you wait for an EOF you will wait a long time until the server times out and closes the socket.

The only write method is:

  • write(buf) Attempts to write the buf to the stream
    The data is only written following a call to the drain() method

This is not a coroutine and always returns immediately. However, the drain() coroutine, which waits until it is appropriate to resume writing to the stream, should be called after each write operation, for example:

stream.write(data)
await stream.drain()

The close() method closes both the stream and the underlying socket used in the TCP connection and should be used along with the wait_closed() coroutine:

stream.close()
await stream.wait_closed()

The logic is that there is no point in carrying on until the stream has been closed and so you might as well free the main thread. You can also use is_closing() to test whether the stream is closed or is in the process of closing.

Downloading A Web Page

We have already used the urequest module to download a web page and we have used sockets to do the same job asynchronously with non-blocking sockets. An alternative approach is to use uasyncio to do the job as part of an overall asynchronous system.

The coroutine:

uasyncio.open_connection(host,port)

uses sockets to open a connection to the host, specified as an IP address or a URL and a port. If successful this returns a (reader, writer) tuple which can be used to communicate with the server. The reader and writer are actually the same stream object, but for clarity we will make use of each one appropriately. For example, to connect to www.example.com, as we did in the previous chapter, you would use client.py:

import uasyncio
from time import sleep_ms
from machine import Pin, Timer
import rp2
import network
def setup(country, ssid, key):
... async def main(): reader,writer = await uasyncio.open_connection(
"www.example.com",80) request = b"GET /index.html HTTP/1.1\r\n
Host:example.org\r\n\r\n" writer.write(request) await writer.drain() print((await reader.read(512)).decode("utf-8")) reader.close() wifi = setup(country, ssid, key) uasyncio.run(main())

What is the advantage of this approach?

The simple answer is that it makes it easier to overlap downloads. For example, if you convert the download actions into a function:

async def getPage(url):
   reader,writer = await uasyncio.open_connection(url,80)
   request = b"GET /index.html HTTP/1.1\r\n
Host:example.org\r\n\r\n" writer.write(request) await writer.drain() page = await reader.read(512) reader.close() return page

you can now call it sequentially or concurrently.

If you call it sequentially:

    results = await getPage('www.example.com')
    results = await getPage('www.example.com')

it takes 800ms to download the page twice. However, if you call it concurrently:

results = await uasyncio.gather( 
getPage('www.example.com'),
getPage('www.example.com'))

it takes only 400ms, which is only a little more than the 350ms it takes to download the page once. The improvement is due to the fact that while the getPage coroutine is waiting for the download, it releases the thread and the other coroutine can execute.

In chapter but not in this extract

  • Server
  • A Web Server

Best Practice

How best to implement a server?

Although the uasyncio approach is attractive from a theoretical point of view, it is built using non-blocking sockets. If you really need to serve multiple clients at the same time then directly using non-blocking sockets is probably the best way to do the job from the point of not having additional overhead and having more control over timing in a polling loop.

Notice that uasyncio doesn’t include any support for events and this is true of the full asyncio module. If the task loop could be modified to include event handlers then it might be more useful in an IoT context. The Pico has the hardware to work with events and it should be easy to add this to uasyncio by adding an event handling Task when an event occurs.

There is also a lot to be said for using a client to deliver data to a server via a PUT or POST request. This would save a lot of effort in implementing an asynchronous system and makes timing easier to deal with.

Summary

  • The asyncio module provides single-threaded multi-tasking.

  • A coroutine is a function that can be suspended and resumed by the use of the await instruction.

  • A coroutine has to be run in conjunction with an event loop. asyncio.run creates an event loop and runs a coroutine as a task using it.

  • A Task is a coroutine with some additional methods and it is what is added to the event loop’s queue using asyncio.create_task. The Task is run when the thread becomes free.

  • When you await a coroutine it starts running to completion.

  • When you await a Task, i.e. a coroutine already on the task loop, it only starts running if it isn’t already completed.

  • The await always returns the result of the Task, including any exceptions that might have occurred.

  • You can use wait_for as a version of await with a timeout.

  • Task coroutines can be executed in sequential order by awaiting each one in turn. They can be run concurrently by adding them to the queue or by using the gather coroutine.

  • A Task can be canceled, but is up to you to handle the exception.

  • A Task returns any exceptions to the awaiting coroutine – these can be raised or processed.

  • Locks are less useful for coroutines because unless the thread is released they are atomic. If a race condition can occur there are asynchronous equivalents of some of the standard synchronization objects.

  • The asyncio module makes network connections easy and asynchronous.

  • Implementing a web client is easy, but there is no high-level function which downloads an HTML page. You have to work with the HTTP protocol.

  • Creating a web server is only slightly more difficult in that you have to support multiple potential clients.

Programming the Raspberry Pi Pico/W In MicroPython Third Edition

By Harry Fairhead & Mike James

PicoPython3E360

Buy from Amazon.

Contents

  • Preface
  • Chapter 1 The Raspberry Pi Pico – Before We Begin
  • Chapter 2 Getting Started
  • Chapter 3 Getting Started With The GPIO
  • Chapter 4 Simple Output
  • Chapter 5 Some Electronics
  • Chapter 6 Simple Input
             Extract: Simple Input ****
  • Chapter 7 Advanced Input – Events and Interrupts
  • Chapter 8 Pulse Width Modulation
             Extract: PWM  ****
  • Chapter 9 Controlling Motors And Servos
             Extract: DC Motors ****
  • Chapter 10 Getting Started With The SPI Bus
  • Chapter 11 A-To-D and The SPI Bus  ****
  • Chapter 12 Using The I2C Bus
  • Chapter 13 Using The PIO   
  • Chapter 14 The DHT22 Sensor Implementing A Custom Protocol
             Extract: A PIO Driver For The DHT22   ****
  • Chapter 15 The 1‑Wire Bus And The DS1820
  • Chapter 16 The Serial Port
  • Chapter 17 Using The Pico W - WiFi
             Extract: HTTP Client  ****     
  • Chapter 18 Sockets
             Extract: Sockets ****
  • Chapter 19 Asyncio And Servers
             Extract: Asyncio Client  ***NEW!!!
  • Chapter 20 Advanced Hardware
  • Chapter 21 Direct To The Hardware
             Extract: Direct To The Hardware ****

Also of interest:

Raspberry Pico File System

**** articles from the second edition not yet updated.

<ASIN:B0BR8LWYMZ>

<ASIN:B0BL1HS3QD>

 

To be informed about new articles on I Programmer, sign up for our weekly newsletter, subscribe to the RSS feed and follow us on Twitter, Facebook or Linkedin.

Banner


With MCP Docs Servers You'll Never Run Out Of Fresh Documentation
14/08/2025

MCP has changed the way you interact with your tools overnight. Now it targets your documentation. Wouldn't be great to have the latest and updated code samples and documentation of your favorite fram [ ... ]



Record Level Of Interest In Google Summer of Code 2025
15/08/2025

Google Summer of Code 1025 is well underway with 1280 contributors from 68 countries coding for 185 mentoring Organizations. Figures from Google show a record level of interest in the progra [ ... ]


More News

pico book

 

Comments




or email your comment to: comments@i-programmer.info



Last Updated ( Wednesday, 30 July 2025 )