November 17, 2018

micropython-uasyncio

Asynchronous I/O isn’t new in Python. Since Python 3.4, asyncio is part of the standard library. From A simple introduction to Python’s asyncio and I quote here:

Asyncio is all about writing asynchronous programs in Python. Asyncio is a beautiful symphony between an Event loop, Tasks and Coroutines all coming together so perfectly.

I agree and I love the pluggable event loop model. Asynchronous I/O isn’t unique to Python. I have been using libuv recently, a multi-platform C library focusing on Asynchronous I/O, and it has a very similar concept. I am really excited to find uasyncio libraries in the micrypython-lib GitHub repository. More interestingly, uasyncio is installed by default in the MicroPython official ESP8266 and ESP32 firmware as frozen modules. Here is a very simple example on MicroPython uasyncio to get started. Peter Hinch has a more complete tutorial.

Build MicroPython binary

You don’t really need a micro controller to start using MicroPython. It has Unix and Windows ports that can run natively on you systems but you might have to build it yourself. You don’t need to build MicroPython Unix port yourself if your system supports Snap apps (see below).

micropython-src/ports/unix$ make axtls  # needed until v1.9.4
micropython-src/ports/unix$ make

If everything goes well, you will find a single micropython binary in the same folder. You can start it from there or copy it to your system path.

Install micropython-uasyncio library

The Unix port has a handy package management utility upip installed by default.

$ micropython -m upip install micropython-uasyncio

Library files will be copied to $PATH/.micropython/lib. Alternatively, you can download them directly from the micrypython-lib GitHub repository.

Create asynchronous tasks

Python asyncio are built on cooperative multi-tasking and generators. MicroPython implements the entire Python 3.4 syntax with additionaly async / await keywords from Python 3.5.

async def sleep_5sec():
    while True:
        print('sleep 5 seconds')
        await uasyncio.sleep(5)

We can create other tasks like sleeping longer or making requests to backend REST APIs. It feels more natural to me to program in Asynchronous I/O model. Each task is a relatively small piece of code to finish one job and we hook them together in an event loop.

Wire tasks in an event loop

You can get the singleton event loop instance from uasyncio, attach tasks, and call run_forever to give control to the scheduler.

import uasyncio

async def sleep_5sec():
    while True:
        print('sleep 5 seconds')
        await uasyncio.sleep(5)

async def sleep_7sec():
    while True:
        print('sleep 7 seconds')
        await uasyncio.sleep(7)

if __name__ == '__main__':
    loop = uasyncio.get_event_loop()
    loop.create_task(sleep_5sec())  # schedule asap
    loop.create_task(sleep_7sec())
    loop.run_forever()

This example is trivial and might not be very interesting. Peter Hinch has more useful demo programs in his tutorial. In this PyCon AU 2018, Matt Trentini also had a quick 20-minute talk on Asyncio in (Micro)Python to explain the basic concepts.

Asyncio in (Micro)Python

Updated Nov 24, 2018

I have created an unofficial MicroPython Unix port Snap app. To test the latest successful build on your device, please run:

$ sudo snap install micropython

Get it from the Snap Store

© 2015-2019 Jiawei Huang

Powered by Hugo & Kiss.