Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/carglglz/asyncmd
Tools for MicroPython Async Development
https://github.com/carglglz/asyncmd
async asyncio debugging htop logging micropython mqtt profiling systemd
Last synced: about 2 months ago
JSON representation
Tools for MicroPython Async Development
- Host: GitHub
- URL: https://github.com/carglglz/asyncmd
- Owner: Carglglz
- Created: 2023-01-27T17:38:01.000Z (almost 2 years ago)
- Default Branch: main
- Last Pushed: 2024-03-20T01:24:31.000Z (9 months ago)
- Last Synced: 2024-10-17T21:35:36.218Z (2 months ago)
- Topics: async, asyncio, debugging, htop, logging, micropython, mqtt, profiling, systemd
- Language: Python
- Homepage:
- Size: 1.35 MB
- Stars: 7
- Watchers: 1
- Forks: 2
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
Awesome Lists containing this project
README
# Tools for MicroPython Async Development
Inspired by [aiorepl](https://github.com/micropython/micropython-lib/tree/master/micropython/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](https://github.com/micropython/micropython-lib/tree/master/micropython/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
* Create async **traceable tasks** that can be controlled, tracked, managed, debugged or profiled --> `aioctl.py`
Create a task that can be stopped or restarted, inspect its result/return value,
internal error, traceback message, know how long it has been running or how long it has been done.
Also allows to add a custom name/id to the task, or run a callback function when the task is stopped
or throws an error.
e.g. ``● hello_task: status: running since 2023-03-16 00:00:19; 4 s ago``* Asynchronous **RAM logging** --> `aiolog.py`
Create a "ring buffer stream" to log tasks output indefinitely. It
allocates a predefined amount of RAM and rotates automatically so it never allocates
more RAM than the one predefined.
Also allows to interactively inspect its content the same way as using `cat`, `cat | grep` or `tail -F `, `tail -F | grep`.
e.g. `2023-03-16 00:00:19 [pyb] [INFO] [hello_task] LED 3 toggled!`* Asynchronous **scheduling** --> `aioschedule.py`
Create a task and run it in `s` seconds (*from event loop start or task creation*) or at a time `(timetuple)` and repeat every `n` seconds.
This has a "scheduler task loop" that checks every second its schedule and runs a scheduled task when the time is due.e.g.
```
● world_task: status: done @ 2023-03-16 00:12:44; 48 s ago --> result:
┗━► schedule: last @ 2023-03-16 00:12:39 --> next in 37 s @ 2023-03-16 00:13:16
```* Asynchronous **services**[^2] --> `aioservice.py`, `aioclass.py`, `aioservices/services`, `services.config`
Create a service that can have one or more tasks, install/list/get status of services, load/unload, config services
as enabled or disabled, config service main task args and keyword args, get the traceback of services that failed to load, init/boot services
following a priority depending of another service dependency...e.g.
```
[ OK ] Service: hello_core.service from ./aioservices/services/hello_core_service.py loaded
[ OK ] Service: hello_low.service from ./aioservices/services/hello_low_service.py loaded
[ OK ] Service: world.service from ./aioservices/services/world_service.mpy loaded
[ OK ] Service: hello.service from ./aioservices/services/hello_service.mpy loaded
[ OK ] Service: watcher.service from ./aioservices/services/watcher_service.mpy loaded
[ ERROR ] Service: dofail.service from ./aioservices/services/dofail_service.mpy not loaded: Error: ZeroDivisionError
```
Finally to inspect a task or service enabling debug mode in `aioctl` gives the full service/task status
```
● hello.service - Hello example runner v1.0
Loaded: Service: hello.service from ./aioservices/services/hello_service.mpy
Active: (active) running since 2023-03-16 00:35:00; 00:02:31 ago
Type: runtime.service
Docs: https://github.com/Carglglz/asyncmd/blob/main/README.md
Stats: Temp: 25.79095 C Loop info: exec time: 5 ms; # loops: 186
Task:
┗━► args: (Service: hello.service from ./aioservices/services/hello_service.mpy, 3, 10)
┗━► kwargs: { 'on_error': ,
'_id': 'hello.service',
'log': ,
'on_stop': }
2023-03-16 00:35:10 [pyb] [INFO] [hello.service] LED 3 toggled!
2023-03-16 00:35:20 [pyb] [INFO] [hello.service] LED 2 toggled!
```## 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](https://docs.micropython.org/en/latest/reference/packages.html)*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](https://docs.micropython.org/en/latest/reference/manifest.html#manifest)## Example
This basic example demonstrates how to use `@aioctl.aiotask` decorator to create
a traceable taske.g. `async_blink.py`
```python
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:
┗━► args: ()
┗━► kwargs: {}
● blink: status: running since 2023-03-16 11:27:11; 00:01:01 ago
Task:
┗━► 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:
┗━► args: ()
┗━► kwargs: {}
● blink: status: stopped @ 2023-03-16 11:28:33; 3 s ago --> result:
Task:
┗━► 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:
┗━► args: ()
┗━► kwargs: {}
● blink: status: running since 2023-03-16 11:33:43; 3 s ago
Task:
┗━► 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:
┗━► args: (3,)
┗━► kwargs: { 'sleep': 6 }
● repl: status: running since 2023-03-16 11:27:11; 00:13:56 ago
Task:
┗━► args: ()
┗━► kwargs: {}
● blink: status: running since 2023-03-16 11:37:48; 00:03:19 ago
Task:
┗━► args: (2,)
┗━► kwargs: { 'sleep': 3 }```
See more [examples](examples/) to know how to add *"async" logging*,
callbacks, debugging errors, get results, scheduling and finally some [examples](lib-aioservices/) of `aioservice`
implementation.## Docs
### [aioctl](docs/aioctl.md)
### [aiolog](docs/aiolog.md)
### [aioschedule](docs/aioschedule.md)
### [aioservice](docs/aioservice.md)
### [aioservices](docs/aioservices.md)
### [app](app/)
### [async_modules](async_modules/)
## Examples
Set of examples of increasing complexity to show the capabilities
of these tools.- aiotasks --> [examples](examples)
- aioservices --> [example-aioservices](lib-aioservices)
#### Use cases for **aioctl**
- Building block for *asyncio* based applications that makes tasks
traceable, manageable and easily debugged#### Use cases for **aioservice**
- Debug, develop and manage complex *asyncio* applications
- Create reproducible/repeatable builds that can be fine tuned for each device using config files
- Long term running and modular applications that need to be resilient and remotely:
- monitored
- debugged
- updated## Tutorial
See a tutorial for `unix` port in [develop example](develop/unix) using *MQTT*, *asyncmd* CLI and the following services:
- `aiomqtt.service` --> control/debug and get/check OTA updates notifications over MQTT
- `aiomqtt_sensor_bme280.service` --> sensor (simulated) reports Temperature,Humidity,Pressure over MQTT
- `mip.service` --> check and update mip installable packages
- `unittest.service` --> check and run new tests regularly.
- `watcher.service` --> watch services tasks to add resiliency and enable watchdog to secure
device continuous operation with minimum downtimeSee a tutorial for `esp32` port in [develop example](develop/esp32) using *MQTT*, *asyncmd* CLI and the following services:
- `aiomqtt.service` --> control/debug and get/check OTA updates notifications over MQTT
- `aiomqtt_sensor_bme280.service` --> sensor (simulated) reports Temperature,Humidity,Pressure over MQTT
- `ota.service` --> async OTA firmware upgrades using `asyncmd` CLI
- `network.service` --> enable WiFi connection and WebREPL (optional)
- `wpa_supplicant.service` --> check WiFi connection and reconnects if disconnected
- `watcher.service` --> watch services tasks to add resiliency and enable watchdog to secure
device continuous operation with minimum downtime## asyncmd CLI
### [asyncmd cli](cli/)
## Tested on ports:
- esp32 (WROOM ESP32-D0WDQ6 revision v1.0)
- stm32 (pyboard) (STM32F405RG)
- unix (see [develop/unix](develop/unix))### Notes
[^1]: *Runnnig*
*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](https://github.com/systemd/systemd) specially `systemctl` and `journalctl`.*
[^3]: *Better if compiled to `.mpy` using `mpy-cross` to save memory, see [mpy-cross](https://docs.micropython.org/en/latest/reference/glossary.html#term-cross-compiler)*