implementation of interrupt : ISR-Handler-Asyncio...

All ESP8266 boards running MicroPython.
Official boards are the Adafruit Huzzah and Feather boards.
Target audience: MicroPython users with an ESP8266 board.
User avatar
thierryp
Posts: 12
Joined: Mon Jan 31, 2022 5:55 pm
Location: Paris - France

Re: implementation of interrupt : ISR-Handler-Asyncio...

Post by thierryp » Mon Feb 14, 2022 8:59 am

Hello Peter,

Your example seems to be the best way for my pushbutton problem.
I have understood the mechanism to pass func and argument.

I have an another question :
The 3 func function (func1, func2, func3) which can be long task must be "async def func" ==> normal
But you must add "await" in each func.
  • What is the best value in "await asyncio.sleep_ms( )" ==> ((0) or other)
  • What is the best place to put it ?
For example if func1 seems like that :

Code: Select all

# rainbow 
def rainbow_cycle(wait):
  for j in range(255):
    for i in range(n):
      rc_index = (i * 256 // n) + j
      np[i] = wheel(rc_index & 255)
    np.write()
    time.sleep_ms(wait)
Thanks,

Thierry.

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

Re: implementation of interrupt : ISR-Handler-Asyncio...

Post by pythoncoder » Mon Feb 14, 2022 2:21 pm

A direct conversion to an asynchronous function would be

Code: Select all

import uasyncio as asyncio
async def rainbow_cycle(wait):
  for j in range(255):
    for i in range(n):
      rc_index = (i * 256 // n) + j
      np[i] = wheel(rc_index & 255)
    np.write()
    await asyncio.sleep_ms(wait)
In certain circumstances you might want to add

Code: Select all

await asyncio.sleep_ms(0)
in the inner loop. You would do this if the time taken to run the inner loop was so long that it interfered with the proper running of other coroutines. Doing this does bring complications in that, during the sleep_ms(0) other coroutines run, slowing the inner loop. You might need to measure the time taken and adjust the value of wait to achieve the desired timing.
Peter Hinch
Index to my micropython libraries.

User avatar
thierryp
Posts: 12
Joined: Mon Jan 31, 2022 5:55 pm
Location: Paris - France

Re: implementation of interrupt : ISR-Handler-Asyncio...

Post by thierryp » Mon Feb 14, 2022 3:44 pm

Peter,

Thanks you very much for your help.
I 'll use all your recommandations soon to transform the initial program :
https://randomnerdtutorials.com/micropy ... 2-esp8266/
with classic handler (pushbuttons set as interrupts) to async pushbuttons.

Thierry.

User avatar
thierryp
Posts: 12
Joined: Mon Jan 31, 2022 5:55 pm
Location: Paris - France

Re: implementation of interrupt : ISR-Handler-Asyncio...

Post by thierryp » Fri Feb 18, 2022 5:49 pm

hello Peter,

I have copied the primitives directory to the ESP8266 board.
Question : is it obligatory to copy on board ?
my code no.

I have modified with your help the code to async like this :

Code: Select all

# essai de test en aynchrone correspondant à des push button
from machine import Pin
import machine, neopixel, time
# Rajout
import uasyncio as asyncio
from primitives.pushbutton import Pushbutton

##
task = None  # Reference to currently running task
# bouton 1 ==> func clear et wait = 0?
pb1 = Pushbutton(Pin(15, Pin.IN))
pb1.press_func(start, (clear,0,))
# bouton 2 ==> func bounce et wait = 70ms
pb2 = Pushbutton(Pin(14, Pin.IN))
pb2.press_func(start, (bounce, 70,))
# bouton 3 ==> func cycle et wait = 50ms
pb3 = Pushbutton(Pin(12, Pin.IN))
pb3.press_func(start, (cycle, 50,))
# bouton 4 ==> func rainbow_cycle et wait = 1ms
pb4 = Pushbutton(Pin(13, Pin.IN))
pb4.press_func(start, (rainbow_cycle, 1,))
##
# LED strip configuration
# number of pixels
n = 48
# strip control gpio
p = 5 
np = neopixel.NeoPixel(machine.Pin(p), n)

## tache principale
async def start(func, var):
    global task
    if task is not None:
        task.cancel()
        await asyncio.sleep_ms(0)  # Allow cancellation to occur
    task = asyncio.create_task(func, var)

# FUNCTIONS FOR LIGHTING EFFECTS
#
# Bouton 1 ==> clear (var) ne sert à rien
async def clear(var):
    # turn off all pixels
    for i in range(n):
        np[i] = (0, 0, 0)
        np.write()

# Bouton 2 ==> bounce
async def bounce(wait):
# les couleurs à utiliser
    r = 23 
    g = 210
    b = 15
    for i in range(2 * n):
        for j in range(n):
            np[j] = (r, g, b)
        if (i // n) % 2 == 0:
            np[i % n] = (0, 0, 0)
        else:
            np[n - 1 - (i % n)] = (0, 0, 0)
        np.write()
        await asyncio.sleep_ms(wait)

# Bouton 3 ==> cycle
async def cycle(wait):
# les couleurs à utiliser
    r = 123 
    g = 0
    b = 154
    for i in range(n):
        for j in range(n):
            np[j] = (0, 0, 0)
            np[i % n] = (r, g, b)
            np.write()
        await asyncio.sleep_ms(wait)

# Bouton 4 ==> rainbow
async def rainbow_cycle(wait):
  for j in range(255):
    for i in range(n):
      rc_index = (i * 256 // n) + j
      np[i] = wheel(rc_index & 255)
    np.write()
    await asyncio.sleep_ms(wait)

# function to go through all colors 
async def weel(pos):
# Input a value 0 to 255 to get a color value.
# The colours are a transition r - g - b - back to r.
    if pos < 0 or pos > 255:
        return (0, 0, 0)
    if pos < 85:
        return (255 - pos * 3, pos * 3, 0)
    if pos < 170:
        pos -= 85
        return (0, 255 - pos * 3, pos * 3)
    pos -= 170
    return (pos * 3, 0, 255 - pos * 3)
    #await asyncio.sleep_ms(1)
But i have an error in Thonny IDE :

Code: Select all

>>> %Run -c $EDITOR_CONTENT
Traceback (most recent call last):
  File "<stdin>", line 12, in <module>
NameError: name 'start' isn't defined
There is no main routine ?

And is the code the last routine weel(pos) must be in async or not ?

Thanks,

Thierry.

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

Re: implementation of interrupt : ISR-Handler-Asyncio...

Post by pythoncoder » Sat Feb 19, 2022 12:16 pm

The following gets rid of the error messages:

Code: Select all

# essai de test en aynchrone correspondant à des push button
from machine import Pin
import machine, neopixel, time
# Rajout
import uasyncio as asyncio
from primitives.pushbutton import Pushbutton

##
task = None  # Reference to currently running task
# bouton 1 ==> func clear et wait = 0?
pb1 = Pushbutton(Pin(15, Pin.IN))
# bouton 2 ==> func bounce et wait = 70ms
pb2 = Pushbutton(Pin(14, Pin.IN))
# bouton 3 ==> func cycle et wait = 50ms
pb3 = Pushbutton(Pin(12, Pin.IN))
# bouton 4 ==> func rainbow_cycle et wait = 1ms
pb4 = Pushbutton(Pin(13, Pin.IN))
##
# LED strip configuration
# number of pixels
n = 48
# strip control gpio
p = 5 
np = neopixel.NeoPixel(machine.Pin(p), n)

## tache principale
async def start(func, var):
    global task
    if task is not None:
        task.cancel()
        await asyncio.sleep_ms(0)  # Allow cancellation to occur
    task = asyncio.create_task(func, var)

# FUNCTIONS FOR LIGHTING EFFECTS
#
# Bouton 1 ==> clear (var) ne sert à rien
async def clear(var):
    # turn off all pixels
    for i in range(n):
        np[i] = (0, 0, 0)
        np.write()

# Bouton 2 ==> bounce
async def bounce(wait):
# les couleurs à utiliser
    r = 23 
    g = 210
    b = 15
    for i in range(2 * n):
        for j in range(n):
            np[j] = (r, g, b)
        if (i // n) % 2 == 0:
            np[i % n] = (0, 0, 0)
        else:
            np[n - 1 - (i % n)] = (0, 0, 0)
        np.write()
        await asyncio.sleep_ms(wait)

# Bouton 3 ==> cycle
async def cycle(wait):
# les couleurs à utiliser
    r = 123 
    g = 0
    b = 154
    for i in range(n):
        for j in range(n):
            np[j] = (0, 0, 0)
            np[i % n] = (r, g, b)
            np.write()
        await asyncio.sleep_ms(wait)

# Bouton 4 ==> rainbow
async def rainbow_cycle(wait):
  for j in range(255):
    for i in range(n):
      rc_index = (i * 256 // n) + j
      np[i] = wheel(rc_index & 255)
    np.write()
    await asyncio.sleep_ms(wait)

# function to go through all colors 
async def weel(pos):
# Input a value 0 to 255 to get a color value.
# The colours are a transition r - g - b - back to r.
    if pos < 0 or pos > 255:
        return (0, 0, 0)
    if pos < 85:
        return (255 - pos * 3, pos * 3, 0)
    if pos < 170:
        pos -= 85
        return (0, 255 - pos * 3, pos * 3)
    pos -= 170
    return (pos * 3, 0, 255 - pos * 3)
    #await asyncio.sleep_ms(1)

async def main():
    pb1.press_func(start, (clear,0,))
    pb2.press_func(start, (bounce, 70,))
    pb3.press_func(start, (cycle, 50,))
    pb4.press_func(start, (rainbow_cycle, 1,))
    while True:
        await asyncio.sleep(1)  # Replace with your code
        print('Running')

asyncio.run(main())
In your version you were assigning press_func routines before they were created. You can't write

Code: Select all

x = y
y = 42
because at the time you assign y to x, y doesn't exist. Also you never actually start the scheduler.

My version should load error-free but it won't actually work because I have no idea what weel is supposed to do.

As a general point, writing a large program all in one go is fine for experienced coders. But if you're learning (or are an experienced programmer learning a new technique) the best way is to start small and simple and gradually add functionality.
Peter Hinch
Index to my micropython libraries.

User avatar
thierryp
Posts: 12
Joined: Mon Jan 31, 2022 5:55 pm
Location: Paris - France

Re: implementation of interrupt : ISR-Handler-Asyncio...

Post by thierryp » Sat Feb 19, 2022 7:28 pm

Hello Peter,

I test your new code but I have an other error in Thonny when i push any button ...
>>> %Run -c $EDITOR_CONTENT
Running
Running
Task exception wasn't retrieved
future: <Task> coro= <generator object 'start' at 3fff0240>
Traceback (most recent call last):
File "uasyncio/core.py", line 1, in run_until_complete
File "<stdin>", line 32, in start
TypeError: function takes 1 positional arguments but 2 were given
Running
Running
Running

I don't understand your next lines in your reply : (I don't have x and y variable in code)
In your version you were assigning press_func routines before they were created. You can't write

Code: Select all

x = y
y = 42
because at the time you assign y to x, y doesn't exist. Also you never actually start the scheduler.

After that you have right :
==> the best way is to start small and simple and gradually add functionality.

Thanks,

Thierry.

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

Re: implementation of interrupt : ISR-Handler-Asyncio...

Post by pythoncoder » Mon Feb 21, 2022 9:34 am

Sorry, I hadn't spotted that error in your original code. The line

Code: Select all

    task = asyncio.create_task(func, var)
should read

Code: Select all

    task = asyncio.create_task(func(var))
My point about x and y was an illustration to show that you can't use an object that doesn't yet exist. Your original code was assigning a function to press_func before that function was defined.
Peter Hinch
Index to my micropython libraries.

User avatar
thierryp
Posts: 12
Joined: Mon Jan 31, 2022 5:55 pm
Location: Paris - France

Re: implementation of interrupt : ISR-Handler-Asyncio...

Post by thierryp » Mon Feb 21, 2022 10:17 am

Hello Peter,

Thanks you,

I'll test the code with your modification :

Code: Select all

 task = asyncio.create_task(func(var))
And ok i understand now your explanation about my first code...

Thierry.

User avatar
thierryp
Posts: 12
Joined: Mon Jan 31, 2022 5:55 pm
Location: Paris - France

Re: implementation of interrupt : ISR-Handler-Asyncio...

Post by thierryp » Tue Feb 22, 2022 1:54 pm

Hello Peter,

The code is working.
All tests seems to be good.
I have now a real interrupt handler button via asyncio...

Thanks very much for your help and patience.

Thierry.

Post Reply