uasyncio parralel threading seems to not work

Discussion about programs, libraries and tools that work with MicroPython. Mostly these are provided by a third party.
Target audience: All users and developers of MicroPython.
zaord
Posts: 96
Joined: Fri Jan 31, 2020 3:56 pm

uasyncio parralel threading seems to not work

Post by zaord » Fri Aug 07, 2020 3:26 pm

Hi here,

I try to use two routine I wants to be exectued in the same time continulsy, but Only the first one is running...

Where I am wrong ?

Code: Select all

 async def screen(self):
        Writer.set_textpos(ssd, 0, 0)  # In case previous tests have altered it
        wri = Writer(ssd, small, verbose=False)
        wri.set_clip(False, False, False)

        nfields = []
        dy = small.height() + 6
        y = 2
        col = 50
        width = wri.stringlen('990.99')
        for txt in ('Press:', 'Y:', 'Z:'):
            nfields.append(Label(wri, y, col, width, bdcolor=None))  # Draw border
            y += dy
          
    
        while True :
            Label(wri, 2, 2, ' Press ', True)
            Label(wri, 22, 2, ' Flow   ', True)
            Label(wri, 42, 2, ' Temp ', True)
            i=0    
            for field in nfields:
                value = (self.bmp.pressure-self.p0,self.sfm3000.Mes_flow(),self.bmp.temperature)
                #print(value[i]) # print sensors values
                field.value('{:5.2f}'.format(value[i]))
                i=i+1
            refresh(ssd)
            utime.sleep_ms(200)
        
    async def InvPress(self):
        utime.sleep_ms(1000)
        print('Sw2',self.SW2.value())
        while True:
            print(self.SW2.value())
            if self.SW2.value():
                if self.PressPos == 1:
                    self.stepper.steps(-1600,2600)
                    self.PressPos = not self.PressPos
                else :
                    self.stepper.steps(+1600,2600)
                    self.PressPos = not self.PressPos
            utime.sleep_ms(120)


    async def main(self):
        uasyncio.create_task(self.InvPress())
        uasyncio.create_task(self.screen())       
        await uasyncio.sleep(10)

zaord
Posts: 96
Joined: Fri Jan 31, 2020 3:56 pm

Re: uasyncio parralel threading seems to not work

Post by zaord » Sat Aug 08, 2020 11:21 am

Hi here,

It's seems that I did not put Any await so only one routine could be run and never the others.

Here I put the code updated :

Code: Select all

   uasyncio.run(self.main())
        
    async def screen(self):
        await uasyncio.sleep_ms(100)
        Writer.set_textpos(ssd, 0, 0)  # In case previous tests have altered it
        wri = Writer(ssd, small, verbose=False)
        wri.set_clip(False, False, False)

        nfields = []
        dy = small.height() + 6
        y = 2
        col = 50
        width = wri.stringlen('990.99')
        for txt in ('Press:', 'Y:', 'Z:'):
            nfields.append(Label(wri, y, col, width, bdcolor=None))  # Draw border
            y += dy
          
    
        while True :
            Label(wri, 2, 2, ' Press ', True)
            Label(wri, 22, 2, ' Flow   ', True)
            Label(wri, 42, 2, ' Temp ', True)
            i=0    
            for field in nfields:
                value = (self.bmp.pressure-self.p0,self.sfm3000.Mes_flow(),self.bmp.temperature)
                #print(value[i]) # print sensors values
                field.value('{:5.2f}'.format(value[i]))
                i=i+1
            refresh(ssd)
            await uasyncio.sleep_ms(100)
        
    async def InvPress(self):
        await uasyncio.sleep_ms(1000)
        print('Sw2',self.SW2.value())
        while True:
            print(self.SW2.value())
            if self.SW2.value():
                if self.PressPos == 1:
                    self.stepper.steps(-1600,2600)
                    self.PressPos = not self.PressPos
                else :
                    self.stepper.steps(+1600,2600)
                    self.PressPos = not self.PressPos
            await uasyncio.sleep_ms(120)


    async def main(self):
        uasyncio.create_task(self.InvPress())
        uasyncio.create_task(self.screen())       
        while True:
            await uasyncio.sleep(1000)
Now my question is that I use a Class Stepper for controlling a stepper motor driver and on this class that I call In the InvPress uasyncio routine, I tried to replace the commun utime.sleep() by await uasyncio.sleep_us(20), but this not seems to works sinc when the step() routine below is running, the main code is frost...

Any idea to solve this ?

Code: Select all

 async def steps(self, step_count, speed = 1000):
        """Rotate stepper for given steps."""
        print(self.step_time)
        self.calc_pulse_per_sec(speed)
        
        self.dir.value(0 if step_count > 0 else 1)
        
        for i in range(abs(step_count)):
            self.stp.high()
            await uasyncio.sleep_us(20)
            self.stp.low()
            await uasyncio.sleep_us(self.step_time)
        self.current_position += step_count

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

Re: uasyncio parralel threading seems to not work

Post by pythoncoder » Sat Aug 08, 2020 12:20 pm

I think your code is throwing an exception which you may be missing. uasyncio does not have a sleep_us function:

Code: Select all

>>> 'sleep_us' in dir(uasyncio)
False
It is easy to miss exceptions in running uasyncio programs because by default the task stops but the others continue, typically producing print output and causing the error to scroll out of view. You need a global exception handler during development.

Microsecond level timing is beyond uasyncio. When a task yields control (e.g. await asyncio.sleep_ms(1)) all other pending tasks get to run. The actual delay depends on the design of those tasks but will typically be tens of ms.
Peter Hinch
Index to my micropython libraries.

zaord
Posts: 96
Joined: Fri Jan 31, 2020 3:56 pm

Re: uasyncio parralel threading seems to not work

Post by zaord » Sat Aug 08, 2020 2:36 pm

Hi,

I found maybe a mistake in your tutorial

async def main():
loop = asyncio.get_event_loop()

Isn't be uasyncio.get_ instead ?

I try and come back if I have more questions !

zaord
Posts: 96
Joined: Fri Jan 31, 2020 3:56 pm

Re: uasyncio parralel threading seems to not work

Post by zaord » Sat Aug 08, 2020 2:49 pm

Okay, I get several error that I could fix with the _exception_handler, but my code is actually still frozen during my stepper function execution.

So I have some question :

1) Is it possible to call await uasyncio.sleep_ms(120) in the function stepper from the function InvPress which is called by the uasyncio main(self) if my function stepper is not difined in one of the uasyncio.create_task() from the main function ?


2) If a want refresh a oled screen each 200 ms, is it better to put a await uasyncio.sleep_ms(200) on begining, on end or to split this into 4 await uasyncio.sleep_ms(50) dispatched in the begining, middle and end of a routine ?

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

Re: uasyncio parralel threading seems to not work

Post by pythoncoder » Sun Aug 09, 2020 7:59 am

zaord wrote:
Sat Aug 08, 2020 2:36 pm
Hi,

I found maybe a mistake in your tutorial

async def main():
loop = asyncio.get_event_loop()

Isn't be uasyncio.get_ instead ?

I try and come back if I have more questions !
In my tutorial I advocate importing uasyncio as follows

Code: Select all

import uasyncio as asyncio
This facilitates writing code which is compatible with CPython. If you later decide to port your code, you can change the import to

Code: Select all

try:
    import asyncio
except ImportError:
    import uasyncio as asyncio
with no further changes normally needed.
Peter Hinch
Index to my micropython libraries.

zaord
Posts: 96
Joined: Fri Jan 31, 2020 3:56 pm

Re: uasyncio parralel threading seems to not work

Post by zaord » Sun Aug 09, 2020 8:02 am

Hmmm Good to know :)

Thanks a lot Peter !

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

Re: uasyncio parralel threading seems to not work

Post by pythoncoder » Sun Aug 09, 2020 8:12 am

I'm unclear about your other queries. If a function or method is defined with async def it is always valid for it to call uasyncio.sleep_ms(120). I think you need to explain the exact nature of your problem.

Re OLED refresh I can think of only one circumstance where you might want to split a delay, and that is where you have a computationally intensive routine which might hog the processor and starve other coroutines of execution. In these cases you could equally have a delay of zero as that will give every other task some execution:

Code: Select all

async def refresh_oled():
    while True:
        foo()  # Maybe takes 20ms
        await asyncio.sleep(0)
        bar()  # Takes another 20ms
        await asyncio.sleep(0)
        refresh_oled()  # Takes 20ms
        await asyncio.sleep(200)
In the above example no other task is blocked for more than 20ms (assuming there is no other heavy duty task in progress).
Peter Hinch
Index to my micropython libraries.

zaord
Posts: 96
Joined: Fri Jan 31, 2020 3:56 pm

Re: uasyncio parralel threading seems to not work

Post by zaord » Sun Aug 09, 2020 9:28 pm

My question is that the stepper routine I post above Have a sleep to vary the speed of rotation of my stepper, and this await seems to not allow the other coroutine to run since the oled refresh function is like so slow that is freeze the screen.

So I wonder if it is normal of if it's due to the fact that this function is in another class that my main function and maybe the await from this stepper class don't 'know' the other uasyncio routine of my main class where I call uasyncio.run(self.main())...

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

Re: uasyncio parralel threading seems to not work

Post by pythoncoder » Mon Aug 10, 2020 5:11 am

It's hard to comment without seeing the code but in my experience uasyncio V3 "just works". The scheduler will schedule every task which has been started, regardless of the module or the class that they are in.

Slowdowns of the type you describe are a common problem with asynchronous programming. They can usually be overcome with careful design. What is almost certainly happening is that some tasks are hogging the CPU. Do you ever call time.sleep()? This hogs the CPU for the duration. In my view it should never be used - time.sleep_us() can be necessary if there is a maximum delay which can be tolerated but you need to yield to the scheduler with asyncio.sleep_ms() ASAP. So code like the following will be a CPU hog:

Code: Select all

    async def foo(self):
        while True:
            for _ in range 1000:  # Greedily grab 50ms of CPU time
               time.sleep_us(50)
               # do something
           await asyncio.sleep_ms(0)
Driving stepper motors without hogging CPU is difficult. You can buy stepper motor controllers which do all the fast stuff onboard. You tell the controller where to move to and the controller handles acceleration and deceleration, telling you when it has got there. That would be my approach.
Peter Hinch
Index to my micropython libraries.

Post Reply