The Pico In MicroPython: Asyncio Client |
Written by Mike James & Harry Fairhead | ||||
Wednesday, 30 July 2025 | ||||
Page 3 of 3
Async NetworkingThe 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:
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:
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 PageWe 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):
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
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(
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
Best PracticeHow 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
Programming the Raspberry Pi Pico/W In MicroPython Third EditionBy Harry Fairhead & Mike JamesBuy from Amazon. Contents
Also of interest: **** 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.
Comments
or email your comment to: comments@i-programmer.info |
||||
Last Updated ( Wednesday, 30 July 2025 ) |