How to use Asyncio to instantly leave a for loop in circuitp
Moderators: adafruit_support_bill, adafruit
Please be positive and constructive with your questions and comments.
- kazana
- Posts: 1
- Joined: Mon Jul 04, 2022 9:54 am
How to use Asyncio to instantly leave a for loop in circuitp
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?
- neradoc
- Posts: 542
- Joined: Wed Apr 27, 2016 2:38 pm
Re: How to use Asyncio to instantly leave a for loop in circ
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.
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.
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:
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.
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
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())
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
You can share your code to see how we can implement what you need.
Please be positive and constructive with your questions and comments.