||11 months ago|
|docs||1 year ago|
|perthensis||2 years ago|
|util/project-skeleton||11 months ago|
|.gitignore||1 year ago|
|LICENSE||2 years ago|
|README.md||1 year ago|
Perthensis: an asynchronous framework for MicroPython.
But it's not just about the async stuff. Additionally, it should be a kind of framework or toolset to make everyday MicroPython tasks easier.
Example: Heartbeat and counter
from perthensis import Scheduler, Heartbeat, DebouncedRotary, TimerDebounce # This class manages your background tasks. sch = Scheduler() # Perthensis comes with a convenient class to blink LEDs in a heartbeat rhythm. # Initialize it and tell it to blink on pin 33. hb = Heartbeat(33) # Then tell the scheduler to run its "beat" method as a background task. sch.create_task(hb.beat) # Let's write a simple background task that prints ever increasing numbers. async def counter(scheduler): x = 1 while True: print(x) # The scheduler will always be passed as first argument to a task and # provides access to some convenience methods. await scheduler.sleep(1) x += 1 # Launch that task, too. This is a shorthand way of calling create_task(): sch(counter) # Next, let's define a variable to be manipulated via a rotary encoder. n = 0 def rotary_callback(modify): global n n += modify # The rotary is connected to pins 4 and 5 and they need a pull-up. # Pin inversion (via `invert=True`) and reversing the direction # (via `reverse=True`) are available, too. rotary = DebouncedRotary(4, 5, rotary_callback, Pin.PULL_UP) # The TimerDebounce class can debounce multiple pins with just one # hardware timer. Here, we use timer 3. The timer will be started and stopped # dynamically on demand (only when debouncing) to save CPU cycles. tdb = TimerDebounce(3) # When the button on pin 36 is pushed, print the value of our rotary variable. def push_callback(pin_id, pin_value): global n print(n) # You can modify the time the button needs to have a constant value by passing # the `threshold` parameter (in milliseconds). tdb.add_pin(36, push_callback, Pin.PULL_UP) # Give control to the scheduler. This method will never return. sch.run_forever()
How does it work?
In version 3.5, Python started supporting
await syntax for coroutines.
And MicroPython has a subset of that functionality available in version 1.13 and newer.
It's important to know that these are not threads.
They do not actually run in parallel.
Instead, every time an
async function (in our case, that's usually a background task) calls
await, its state will be frozen and it will be paused until the function specified after
During that time, other functions can do stuff.
This is what's called cooperative multitasking:
Functions voluntarily pause themselves, so that other functions can run.
So, when you want to make an LED blink, instead of switching it on, then blocking the processor for some time, then switching it off and blocking again, you instead switch it on and tell Python that you're not interested in running for the next 100 milliseconds. Then, when that time has passed, your next line of code will run.
You can of course also wait for incoming network connections, changes in the value of input pins etc.
- Make asynchronous MicroPython easy to use.
- Provide convenience functionality for everyday requirements (keep network connections alive, debounce buttons, sync NTP time regularly, etc).
- Preserve the freedom of the library's user as much as feasible.
- Be modular.
- Generate synergies between these modules, but try not to make them depend on one another.
- MicroPython ≥ 1.13
Project template and build utility
Check out the
util/project-skeleton directory for a template you can use to set up your own MicroPython projects.
It comes with a very handy build script that makes downloading and flashing MicroPython as well as syncing code from different directories (e.g. your own code and Perthensis and/or other libraries) to the board a breeze.
The scheduler can create new tasks (but not cancel them yet), there is an LED heartbeat class, a pin debouncer, a class to read rotary encoders and one to react on a value staying the same for some time (e.g. when holding down a button). More documentation for them is planned, but the source code is commented.
Also, the package's module loading deals gracefully with module files not being present. This means you only have to copy those modules to your board you actually want to use.
I have, from a previous proof-of-concept version of this library, a bunch of other functionality that just requires cleanup before it can be published here. Since I'll be working on a paid project that involves this library, expect that functionality to arrive in the coming days.
Can I use Perthensis on all board types supported by MicroPython?
Probably. At least that’s the goal.
If you find something that doesn’t work with all types of boards, please open an issue about it!
Can I use Perthensis with CircuitPython?
Honestly, I don't know, because I haven't used CircuitPython yet, but I'd like to support it. Try it and let me know! CircuitPython doesn't seem to have async support yet, though, and that is (and probably will continue to be) a requirement for most of Perthensis. You can track the dicussion and implementation process in CircuitPython's #1380.
Nevertheless, issues and pull requests about CircuitPython compatibility are encouraged.
Can it run on a "normal" Python implementation, e.g. on a Raspberry Pi?
Running MicroPython locally on your laptop or Pi is way harder than it should be, some might even say it's impossible. There's the Unix port of MicroPython, but it has its quirks.
I'd say: Try it, but don't expect too much. It's more of a problem with MicroPython itself than with this library.
I have a feature request!
That's not a question.
Would you be interested in my feature request?
Yes. Feel free to open an issue.