How to use Asyncio to instantly leave a for loop in circuitp

CircuitPython on hardware including Adafruit's boards, and CircuitPython libraries using Blinka on host computers.

Moderators: adafruit_support_bill, adafruit

Please be positive and constructive with your questions and comments.
Locked
User avatar
kazana
 
Posts: 1
Joined: Mon Jul 04, 2022 9:54 am

How to use Asyncio to instantly leave a for loop in circuitp

Post by kazana »

I am trying to wrap my ahead around how to use asyncio to instantly leave a function based on the state of a hall effect. My function calls a nested for loop to control a strand of neopixels in a chasing manner. I am using the hall effect as a switch. Without asyncio, I can exit the for loop, but it won't leave the loop until the function has finished which might be several seconds. I want to instantly leave the loop when the magnetic field leaves. Typically this would be an interrupt function, but I keep getting referred to use asyncio. Is there a simple example anyone has seen to help walk me through this?

User avatar
neradoc
 
Posts: 542
Joined: Wed Apr 27, 2016 2:38 pm

Re: How to use Asyncio to instantly leave a for loop in circ

Post by neradoc »

Hi, typically an animation is not done with a loop, but with a function that displays a single frame of the animation, and is itself called in a loop.
That means you are never "stuck" in the animation loop, you are always in the main loop, so interrupting an animation does not require leaving a loop, only not calling the next animation frame. So you can read from the hall sensor between frames and make a decision without having to wait for the animation.

This is how the adafruit_led_animation works for example. https://learn.adafruit.com/circuitpython-led-animations
In concept that would be something like that. Here read_sensor() stands for how you read your sensor (returning True if the condition for the animation is met). And animation is an animation from the adafruit_led_animation library.

Code: Select all

animating = False
while True:
    if animating:
        animation.animate()
    if read_sensor():
        must_animate = True
    else:
        must_animate= False
This does not require asyncio.

You could implement it with asyncio. Stopping an animation loop while it's running can be a little tricky with asyncio (or interrupts for that matter) since you want a part of your code to stop another. Asyncio allows stopping a task, but you have to deal with exceptions and stuff.
Another method is to simply check a variable and stop the loop based on it like this.

Code: Select all

import asyncio
animating = False

async def animate_leds():
    while animating:
        animation.animate()
        await asyncio.sleep(0.1)

async def main()
    global animating
    while True:
        if read_sensor() is True and not animating:
            animating = True
            asyncio.create_task(animate_leds())
        else:
            animating = False
        await asyncio.sleep(0.5)

asyncio.run(main())
You can also code your own animation here instead of calling animation.animate() but you must make sure to call asyncio.sleep regularly to let the other task run and maybe add checks like if not animating: return (or break) regularly if the loop doesn't loop too often. Something like that:

Code: Select all

async def animate_leds():
    while animating:
        # some custom rainbow animation
        for x in range(256):
            pixels.fill(colorwheel(x))
            await asyncio.sleep(0.05)
            # test in the for loop, or it will be too long
            if not animating:
                return
I don't have an example with cancelling the task (rather than using a flag), I haven't looked into that kind of things too much.
You can share your code to see how we can implement what you need.

Locked
Please be positive and constructive with your questions and comments.

Return to “Adafruit CircuitPython”