uasyncio

General discussions and questions abound development of code with MicroPython that is not hardware specific.
Target audience: MicroPython Users.
User avatar
pythoncoder
Posts: 5956
Joined: Fri Jul 18, 2014 8:01 am
Location: UK
Contact:

uasyncio

Post by pythoncoder » Sun Jan 01, 2017 5:02 pm

In recent weeks this has been radically improved. In particular it now offers millisecond level scheduling making it usable for asynchronous (non-blocking) device drivers. I've spent some time coding with it and documenting its use.

This is the outcome https://github.com/peterhinch/micropython-async.git.

It provides the following:
  • A brief description of the subset of asyncio currently available.
  • Debounced device drivers for switches and pushbuttons. The latter supports long-press and double-click events.
  • Several demo programs.
  • A set of simple synchronisation primitives: Lock, Event, Barrier and Semaphore classes.
  • A tutorial on using the uasyncio subset to interface hardware.
The tutorial is a work-in-progress, not least because uasyncio is itself under development.

Any comments, especially corrections of factual errors, welcome ;)
Peter Hinch
Index to my micropython libraries.

mkarliner
Posts: 8
Joined: Fri Sep 09, 2016 10:59 am

Re: uasyncio

Post by mkarliner » Tue Jan 10, 2017 6:23 pm

Very good late Christmas present!

Thanks Peter.

User avatar
pythoncoder
Posts: 5956
Joined: Fri Jul 18, 2014 8:01 am
Location: UK
Contact:

Re: uasyncio

Post by pythoncoder » Wed Jan 11, 2017 7:39 am

I'm impressed by the updated uasyncio :D I'm porting my applications to it in place of my old scheduler and have found the process remarkably painless. For example my touchscreen GUI https://github.com/peterhinch/micropython-tft-gui now runs under uasyncio with subjectively quick responsiveness. This makes fairly extensive use of asynchronous code so it's been a good practical test.

As I see it uasyncio still needs further development of IORead to better support devices such as UARTs and user-written drivers. But kudos to Paul for producing an effective asyncio subset with speed capable of handling hardware interfaces.
Peter Hinch
Index to my micropython libraries.

brett
Posts: 14
Joined: Sun Jan 15, 2017 3:17 am

Re: uasyncio

Post by brett » Fri Jan 20, 2017 2:24 pm

I read your information and I'm still trying to wrap my head around it. Would uasyncio help in this situation?

I'm a psychologist in a school system and we have several students with behavior issues that flip out (technical term) and get aggressive when their teacher calls the office for assistance. I've been trying to use a ESP8266 to create a box with a remote that she could discretely press to send a push notification to the office, myself, administration, etc. I want it to send a help request message with one press and a urgent help request with 3 presses in a certain time.

Thanks to help from you guys on here I got the button presses working but when I put the urequests post in there it locks the program up so it doesn't respond to additional presses and when it does they are so backed up that it crashes the program. I've been looking for a way to have the program send the request but not have to wait for it to complete before returning to the main loop. Is that possible with uasyncio on the ESP8266? I may have to utilize two different buttons on the remote, but I would rather the teacher not have to look at the remote to send the request. Thanks for your help.

--Brett

User avatar
pythoncoder
Posts: 5956
Joined: Fri Jul 18, 2014 8:01 am
Location: UK
Contact:

Re: uasyncio

Post by pythoncoder » Sat Jan 21, 2017 7:18 am

Unfortunately I'm not knowledgeable about the network programming side of things; I did some work with umqtt and found that it was difficult to integrate with asynchronous programming because it can block. In other words it can monopolise the CPU until a response is received. If urequests works in a similar way then you need to decide whether to send a request or an urgent request before you initiate the urequest. By that time the fact that it might block won't matter: the asynchronous timing stuff is complete.

In principle it should be easy. By definition a non-urgent request can be postponed until you're sure an urgent request is not intended.

The following comments relate to my aswitch.py module.

Create a Pushbutton instance and a Delay_ms timer. Your press_func callback triggers the timer if it's not already running, and sets a press_counter to 1; if the timer is running, it increments the counter. At the end of the time period you check the counter, clear it down and send a request or urgent request. If the sending process blocks for a period it then doesn't matter.

Rather than require three button presses have you considered a long press for an urgent request? It might be easier for users in a stressful situation and also possibly faster. How long should you wait to count three presses?

The Pushbutton class supports this. If the button is held down, after a configurable period a long_func callback is run. Start your Delay_ms timer in the button's press_func callback. Have the button's long_func callback set an emergency flag. When the Delay_ms timer times out, send the non-urgent message if emergency is False, otherwise clear it down and send the emergency message.
Peter Hinch
Index to my micropython libraries.

brett
Posts: 14
Joined: Sun Jan 15, 2017 3:17 am

Re: uasyncio

Post by brett » Mon Jan 23, 2017 2:21 am

Someday I will have to figure out how to install uasyncio and your aswitch modules. I think you are probably right about the long press, but I was struggling on how to get those modules working. So I figured out how to postpone the helpRequest function like you suggested. The program is working for the moment...well...if I can get the urequests post working correctly. Thanks for the help.

User avatar
pythoncoder
Posts: 5956
Joined: Fri Jul 18, 2014 8:01 am
Location: UK
Contact:

Tutorial update

Post by pythoncoder » Tue Apr 25, 2017 10:09 am

I've updated the tutorial https://github.com/peterhinch/micropython-async.git to reflect the current development status of uasyncio.

There is also an experimental modified version of uasyncio. This is intended for applications which have tasks requiring fast scheduling along with others which are less time critical. It provides a simple priority mechanism which can substantially improve the accuracy of time delays and reduce the scheduling latency (in the limit to ~300μs) where required.

Some benchmarks for testing scheduler performance are also provided.
Peter Hinch
Index to my micropython libraries.

thetreerat
Posts: 15
Joined: Thu Apr 27, 2017 6:40 pm

Re: uasyncio

Post by thetreerat » Thu Apr 27, 2017 7:01 pm

Peter,

I want to say thanks for your help files supplied with uasyncio. They have been very helpful over the last week.

everyone,

I have a project that I am using a hall sensor and I had it work with irq call backs. It was working with global varible and a standard loop. however things like the server had issues that it would hang the loop waiting for someone to connect. So I used your examples to build the server and the button, and pressure sensor to use coroutines. everything was work good till i tried to bring back the hall flow sensor.

but I don't seem to be able to access the global variable from inside the uasyncio loop. but I know that I get data in it as I can print it from the callback.

so is there a way to make a task can sense the rise of the sense pin in the hall sensor and increment a variable or event, and then have a monitor task can clean the count and calculate flow data? and do I have to worry about missing pin raises if it in a uasync loop if a task is slow to yield?

if you want to see the code please see git repo on github: http://github.com/thetreerat/WaterPump

thanks for any help
Hal

User avatar
pythoncoder
Posts: 5956
Joined: Fri Jul 18, 2014 8:01 am
Location: UK
Contact:

Re: uasyncio

Post by pythoncoder » Fri Apr 28, 2017 6:17 am

It would help if you could provide a simple test case to illustrate your problem, or point us to the points in your code where you're having trouble. You've written a lot of code!

I'm unaware of any issue with coroutines and callbacks accessing globals: I've used the technique numerous times. Take a look at https://github.com/peterhinch/micropyth ... latency.py for an example.

There is a potential issue where the latency implicit in cooperative scheduling causes events to be missed. If you're using interrupts you need to be aware of this in your application design.

Take a common case where a hardware interrupt sets a flag which is tested and cleared by a coroutine. If the scheduling latency exceeds the interval between successive interrupts the coro will detect one event where two occurred. There are ways to address this, but they require an understanding of the application. In some cases the interrupt service routine could increment a counter or put data into a (pre-allocated) queue. You may need to consider concurrency issues here: the interrupt could occur while the coro is part-way through processing the data. This is where realtime programming gets interesting ;)
Peter Hinch
Index to my micropython libraries.

thetreerat
Posts: 15
Joined: Thu Apr 27, 2017 6:40 pm

Re: uasyncio

Post by thetreerat » Sat Apr 29, 2017 1:44 am

Peter,

I created a new main.py that just uses just the flowmeter.py and uasyncio
full code is at https://github.com/thetreerat/WaterPump

=== main irc code ===
flowCount = 0

def callbackflow(p):
"""Add on to Counter """
global flowCount
flowCount += 1
print("""callback count: %s""" % (flowCount))

#register callback for flowmeter
mainFlowMeter.counterPin.irq(trigger=mainFlowMeter.counterPin.IRQ_RISING, handler=callbackflow)
=== end ====
=== Monitor flow coro ===
async def monitorFlowMeter(self):
"""coroutine for monitoring flow"""
global flowCount
while True:
print("""MonitroFlowMeter count: %s""" % (flowCount))
await asyncio.sleep(2)
=== end ===

I get a this when I reboot the controller. I tried it with my image with frozen bitcode last pull micropython 3 days ago, 1.8.7 seems to not work with the current uasyncio, and esp8266-20170428-v1.8.7-673-g49de9b6.bin
==== error on 1.8.7-673 ====
Traceback (most recent call last):
File "main.py", line 29, in <module>
File "/flash/lib/uasyncio/core.py", line 129, in run_forever
File "/flash/lib/uasyncio/core.py", line 87, in run_forever
File "WaterPumps/flowMeters.py", line 75, in monitorFlowMeter
NameError: name 'flowCount' is not defined
==== end error =====

I know it something I am missing or don't understand.

per your comment on irq and application design.

Flow sensor is just a hall affect sensor, so I think that it needs to be a irq callback that just increases the count on every hit.

I am assuming that way I do not loose pluses.

if in the process of setting the var, if I have the monitor coroutine reset 0 when it reads, a interrupt comes in but that would be only one pulse that gets lost, and not cause errors(right?). This would cause the flow rate to be off (low).

would it be better to let counter just grow, and keep track of last read and current read in monitor. This way the coroutines only read the var. I was worried that this would cause the program to crash when the var gets to big, or dose micropython handle the var when it grows too big.

The thought I had last night was that I could build a driver board that would do the counting, caluating the flow rate and conncect this to the board with ic2 but I have to do lots of research and might be over kill for just running to pumps for home.

sorry for the long reply.
thanks for any help
Hal

Post Reply