Carglglz / asyncmd

Tools for MicroPython Async Development
7 stars 2 forks source link
async asyncio debugging htop logging micropython mqtt profiling systemd

Tools for MicroPython Async Development

Inspired by aiorepl, asyncmd is an asyncio based set of tools to help with the development of asynchronous applications implemented in MicroPython.

Asyncio is ideal for running multiple tasks concurrently^1, however an easy way to interactively inspect running tasks in the event loop was not available until the introduction of aiorepl, an asynchronous MicroPython REPL.

This set of tools builds upon this aiorepl capacity to interact with tasks running in the event loop.

asyncmd is intended to be flexible and extensible i.e. minimum requirement is aioctl.py and then every script builds upon aioctl functionality.

Features

Install

Manual

For aioctl.py, aioschedule.py, aiolog.py, aioservice.py and aioclass.py just upload the scripts to the device[^3]

For aioservices/services make the directories first and then upload aioservices/services/__init__.py (or directly sync aioservices)

Then to install a service upload it to this directory.

Using MIP

See MIP

Note that Network-capable boards must be already connected to WiFi/LAN and have internet access

To install asyncmd using mip

>>> import mip
>>> mip.install("github:Carglglz/asyncmd", target=".")

For simple installation .i.e only aioctl.py

>>> import mip
>>> mip.install("github:Carglglz/asyncmd/aioctl.py")

To install services using mip

>>> import mip
>>> mip.install("github:Carglglz/asyncmd/services", target=".")

# or only network (core network, wpa_supplicant)

>>> mip.install("github:Carglglz/asyncmd/services/network", target=".")

# or develop (watcher, mip, unittest)

>>> mip.install("github:Carglglz/asyncmd/services/develop", target=".")

# or ble 

>>> mip.install("github:Carglglz/asyncmd/services/ble", target=".")

Note that this set of tools (with exception of aioservices/services) can be frozen in the firmware too which will be the best option for saving memory. To freeze services, see frz_services. To learn more about freezing packages/modules see MicroPython manifest files

Example

This basic example demonstrates how to use @aioctl.aiotask decorator to create a traceable task

e.g. async_blink.py

from machine import Pin
import uasyncio as asyncio
import aiorepl
import aioctl

# Define a task

@aioctl.aiotask
async def blink(pin, sleep=5):
    led = Pin(pin, Pin.OUT)
    while True:
        led.on()
        await asyncio.sleep_ms(500)
        led.off()
        await asyncio.sleep(sleep)

async def main():
    print("Starting tasks...")
    # Add tasks
    aioctl.add(blink, 2, sleep=5)
    aioctl.add(aiorepl.task, name="repl")
    # await tasks
    await asyncio.gather(*aioctl.tasks())

asyncio.run(main())

To run, copy and paste in paste mode or upload the script to the device then

>>> import async_blink
Starting tasks...
Starting asyncio REPL
--> import aioctl
--> aioctl.status()
● repl: status: running since 2023-03-16 11:27:11; 38 s ago
● blink: status: running since 2023-03-16 11:27:11; 38 s ago

# Enable aioctl debug mode
--> aioctl.debug()
debug mode: True
--> aioctl.status()
● repl: status: running since 2023-03-16 11:27:11; 00:01:01 ago
    Task: <Taskctl object at 2000c9d0>
    ┗━► args: ()
    ┗━► kwargs: {}
● blink: status: running since 2023-03-16 11:27:11; 00:01:01 ago
    Task: <Taskctl object at 2000c7d0>
    ┗━► args: (2,)
    ┗━► kwargs: { 'sleep': 5 }

# Stop blink task
--> aioctl.stop("blink")
True
--> aioctl.status()
● repl: status: running since 2023-03-16 11:27:11; 00:01:25 ago
    Task: <Taskctl object at 2000c9d0>
    ┗━► args: ()
    ┗━► kwargs: {}
● blink: status: stopped @ 2023-03-16 11:28:33; 3 s ago --> result:
    Task: <Taskctl object at 2000c7d0>
    ┗━► runtime: 00:01:22
    ┗━► args: (2,)
    ┗━► kwargs: { 'sleep': 5 }

# Change sleep kwarg
--> aioctl.group().tasks["blink"].kwargs.update(sleep=3)

# Start again
--> aioctl.start("blink")
True
--> aioctl.status()
● repl: status: running since 2023-03-16 11:27:11; 00:06:35 ago
    Task: <Taskctl object at 2000c9d0>
    ┗━► args: ()
    ┗━► kwargs: {}
● blink: status: running since 2023-03-16 11:33:43; 3 s ago
    Task: <Taskctl object at 20016110>
    ┗━► args: (2,)
    ┗━► kwargs: { 'sleep': 3 }

# Add another blink task
--> aioctl.add(blink, 3, sleep=6)
--> aioctl.status()
● blink@1: status: running since 2023-03-16 11:40:56; 11 s ago
    Task: <Taskctl object at 20015350>
    ┗━► args: (3,)
    ┗━► kwargs: { 'sleep': 6 }
● repl: status: running since 2023-03-16 11:27:11; 00:13:56 ago
    Task: <Taskctl object at 2000c9d0>
    ┗━► args: ()
    ┗━► kwargs: {}
● blink: status: running since 2023-03-16 11:37:48; 00:03:19 ago
    Task: <Taskctl object at 20010070>
    ┗━► args: (2,)
    ┗━► kwargs: { 'sleep': 3 }

See more examples to know how to add "async" logging, callbacks, debugging errors, get results, scheduling and finally some examples of aioservice implementation.

Docs

aioctl

aiolog

aioschedule

aioservice

aioservices

app

async_modules

Examples

Set of examples of increasing complexity to show the capabilities of these tools.

Use cases for aioctl

Use cases for aioservice

Tutorial

See a tutorial for unix port in develop example using MQTT, asyncmd CLI and the following services:

See a tutorial for esp32 port in develop example using MQTT, asyncmd CLI and the following services:

asyncmd CLI

asyncmd cli

Tested on ports:

Notes

multiple tasks concurrently where timing precision is only needed to be held up to a certain degree which can vary with the number of tasks running , the amount of time they take to run and how frequent they are scheduled

[^2]: Inspiration comes from Linux systemd specially systemctl and journalctl.

[^3]: Better if compiled to .mpy using mpy-cross to save memory, see mpy-cross