Multitasking
Moderators: adafruit_support_bill, adafruit
Please be positive and constructive with your questions and comments.
- Rcayot
- Posts: 321
- Joined: Sat Feb 08, 2020 6:48 pm
Multitasking
There is a great series by Bill Earl called multitasking on arduino. Is there something similar for circuitpython?
I have done a bit of beginner arduino programming, and would like to move to CP because of all of the great products and supporting libraries at Adafruit. I have yet to find, however, a good guide to using classes for multitasking using CP. and the new years eve drop ball example does not help me, it is more of a state machine example.
Also, any other resources on that subject would be helpful. Most online python resources do not address aspects of physical computing.
Thanks,
Roger Ayotte
I have done a bit of beginner arduino programming, and would like to move to CP because of all of the great products and supporting libraries at Adafruit. I have yet to find, however, a good guide to using classes for multitasking using CP. and the new years eve drop ball example does not help me, it is more of a state machine example.
Also, any other resources on that subject would be helpful. Most online python resources do not address aspects of physical computing.
Thanks,
Roger Ayotte
- adafruit_support_bill
- Posts: 88093
- Joined: Sat Feb 07, 2009 10:11 am
Re: Multitasking
State machines are the key to multitasking in a purely single-threaded environment. The state machine classes in Dave's ball-drop tutorial are structured a little differently from the state machines in my Multitasking the Arduino series. But overall effect is similar. The main processing loop just needs to call the update function.the new years eve drop ball example does not help me, it is more of a state machine example.
To make it multitask, you create multiple state machines and call update on all of them.
- tannewt
- Posts: 3304
- Joined: Thu Oct 06, 2016 8:48 pm
Re: Multitasking
Hi Roger,
CircuitPython doesn't support any of Python's concurrency mechanics. There is a long issue about it here if you are interested: https://github.com/adafruit/circuitpython/issues/1380
~Scott
CircuitPython doesn't support any of Python's concurrency mechanics. There is a long issue about it here if you are interested: https://github.com/adafruit/circuitpython/issues/1380
~Scott
- AndyWilx
- Posts: 8
- Joined: Mon Aug 10, 2020 8:12 am
Re: Multitasking
Hi all
I hope someone can help.
I'm an artist working on a project that requires two NeoPixel strips running two animations simultaneously.
Bill's Multitasking sketch is perfect and I've been able to change Rings to Strips, remove the button functions and play with the patterns included.
However, I need a new pattern integrated into the code and have no idea how to make it work.
I'm not a coder and am struggling.
Below is the pulse code I want to assign to one of the strips.
Can anyone help or point me in the right direction?
It would be very much appreciated
Thanks Guys
Andy Wilx
#include <Adafruit_NeoPixel.h>
#define PIN 6
#define NUMPIXELS 12
Adafruit_NeoPixel strip = Adafruit_NeoPixel(NUMPIXELS, PIN, NEO_GRB + NEO_KHZ800);
int dir = 1;
int bright = 50;
void setup() {
strip.begin();
strip.show();
}
void loop() {
uint32_t blueFade = strip.ColorHSV(43650,255,bright);
strip.fill(blueFade);
strip.show();
bright = bright + dir;
if(bright > 100 || bright < 10) dir = dir * -1;
delay(25);
}
I hope someone can help.
I'm an artist working on a project that requires two NeoPixel strips running two animations simultaneously.
Bill's Multitasking sketch is perfect and I've been able to change Rings to Strips, remove the button functions and play with the patterns included.
However, I need a new pattern integrated into the code and have no idea how to make it work.
I'm not a coder and am struggling.
Below is the pulse code I want to assign to one of the strips.
Can anyone help or point me in the right direction?
It would be very much appreciated
Thanks Guys
Andy Wilx
#include <Adafruit_NeoPixel.h>
#define PIN 6
#define NUMPIXELS 12
Adafruit_NeoPixel strip = Adafruit_NeoPixel(NUMPIXELS, PIN, NEO_GRB + NEO_KHZ800);
int dir = 1;
int bright = 50;
void setup() {
strip.begin();
strip.show();
}
void loop() {
uint32_t blueFade = strip.ColorHSV(43650,255,bright);
strip.fill(blueFade);
strip.show();
bright = bright + dir;
if(bright > 100 || bright < 10) dir = dir * -1;
delay(25);
}
- adafruit_support_bill
- Posts: 88093
- Joined: Sat Feb 07, 2009 10:11 am
Re: Multitasking
You can accomplish that effect using the Fade pattern from the guide: https://learn.adafruit.com/multi-taskin ... rt-3/fader
Set color1 to be your brightest blue color and color2 to be the dimmest and use an interval of 25.
Set color1 to be your brightest blue color and color2 to be the dimmest and use an interval of 25.
- AndyWilx
- Posts: 8
- Joined: Mon Aug 10, 2020 8:12 am
Re: Multitasking
Thanks for coming back to me.
I have done that and it obviously works as a fade but not a pulse.
It's not quite the same effect as it fades off, then pops on.
This is more subtle as the end point isn't quite off so looks like a pulse not a fade.
Also how do I keep it to one colour?
I'm after a single colour heartbeat.
I guess where I struggle with this type of code, is the lack of tangible variables. A string of words rather than percentages and values.
I'll clearly never be a coder. ;o)
Any help will be very much appreciated.
Andy
I have done that and it obviously works as a fade but not a pulse.
It's not quite the same effect as it fades off, then pops on.
This is more subtle as the end point isn't quite off so looks like a pulse not a fade.
Also how do I keep it to one colour?
I'm after a single colour heartbeat.
I guess where I struggle with this type of code, is the lack of tangible variables. A string of words rather than percentages and values.
I'll clearly never be a coder. ;o)
Any help will be very much appreciated.
Andy
- adafruit_support_bill
- Posts: 88093
- Joined: Sat Feb 07, 2009 10:11 am
Re: Multitasking
The fade lets you set the end point to any arbitrary color. color2 does not need to be black. If you want more of a 'breathing' effect, just reverse the direction on completion.This is more subtle as the end point isn't quite off
You specify the start color (color1) and the end color (color2). They can be different shades of the same color.Also how do I keep it to one colour?
- AndyWilx
- Posts: 8
- Joined: Mon Aug 10, 2020 8:12 am
Re: Multitasking
Thanks again for the reply.
I've got it to fix to one color, (simply turn off the random update).
But this isn't the same animation. This is a fade not a pulse.
Fade: 3-2-1-off-3-2-1-off
Pulse: 3-2-1-off-1-2-3-2-1-off-1-2-3
See my original code.
Apologies if I'm not able to see it in your code.
I'm not a coder.
I'm so close I can smell it but my lack of knowledge is frustrating.
Help would be very welcome.
Andy
I've got it to fix to one color, (simply turn off the random update).
But this isn't the same animation. This is a fade not a pulse.
Fade: 3-2-1-off-3-2-1-off
Pulse: 3-2-1-off-1-2-3-2-1-off-1-2-3
See my original code.
Apologies if I'm not able to see it in your code.
I'm not a coder.
I'm so close I can smell it but my lack of knowledge is frustrating.
Help would be very welcome.
Andy
- adafruit_support_bill
- Posts: 88093
- Joined: Sat Feb 07, 2009 10:11 am
Re: Multitasking
It's hard to offer specific advice since you haven't posted your code.
https://learn.adafruit.com/multi-taskin ... -1167790-9
There is nothing random in the fade pattern. It does what you program it to do.I've got it to fix to one color, (simply turn off the random update).
AS mentioned in my previous post, If you want a breathing effect, simply reverse the fade on completion.Fade: 3-2-1-off-3-2-1-off
Pulse: 3-2-1-off-1-2-3-2-1-off-1-2-3
https://learn.adafruit.com/multi-taskin ... -1167790-9
Code: Select all
if (strip.Direction == FORWARD)
{
strip.Direction = REVERSE;
}
else
{
strip.Direction = FORWARD;
}
- AndyWilx
- Posts: 8
- Joined: Mon Aug 10, 2020 8:12 am
Re: Multitasking
I'm after:
Strip 1 Rainbow Cycle (I'll want to add audio responsive code next ;o)
Strip 2 Pulsing heart beat in one colour.
P.S.
I didn't mean your code was random, I meant removing the 'Random' Call Back seemed to work for me.
"Stick.Color1 = Stick.Wheel(random(255));"
Many Many thanks for your help.
Andy
Code: Select all
#include <Adafruit_NeoPixel.h>
// Pattern types supported:
enum pattern { NONE, RAINBOW_CYCLE, THEATER_CHASE, COLOR_WIPE, SCANNER, FADE };
// Patern directions supported:
enum direction { FORWARD, REVERSE };
// NeoPattern Class - derived from the Adafruit_NeoPixel class
class NeoPatterns : public Adafruit_NeoPixel
{
public:
// Member Variables:
pattern ActivePattern; // which pattern is running
direction Direction; // direction to run the pattern
unsigned long Interval; // milliseconds between updates
unsigned long lastUpdate; // last update of position
uint32_t Color1, Color2; // What colors are in use
uint16_t TotalSteps; // total number of steps in the pattern
uint16_t Index; // current step within the pattern
void (*OnComplete)(); // Callback on completion of pattern
// Constructor - calls base-class constructor to initialize strip
NeoPatterns(uint16_t pixels, uint8_t pin, uint8_t type, void (*callback)())
:Adafruit_NeoPixel(pixels, pin, type)
{
OnComplete = callback;
}
// Update the pattern
void Update()
{
if((millis() - lastUpdate) > Interval) // time to update
{
lastUpdate = millis();
switch(ActivePattern)
{
case RAINBOW_CYCLE:
RainbowCycleUpdate();
break;
case THEATER_CHASE:
TheaterChaseUpdate();
break;
case COLOR_WIPE:
ColorWipeUpdate();
break;
case SCANNER:
ScannerUpdate();
break;
case FADE:
FadeUpdate();
break;
default:
break;
}
}
}
// Increment the Index and reset at the end
void Increment()
{
if (Direction == FORWARD)
{
Index++;
if (Index >= TotalSteps)
{
Index = 0;
if (OnComplete != NULL)
{
OnComplete(); // call the comlpetion callback
}
}
}
else // Direction == REVERSE
{
--Index;
if (Index <= 0)
{
Index = TotalSteps-1;
if (OnComplete != NULL)
{
OnComplete(); // call the comlpetion callback
}
}
}
}
// Reverse pattern direction
void Reverse()
{
if (Direction == FORWARD)
{
Direction = REVERSE;
Index = TotalSteps-1;
}
else
{
Direction = FORWARD;
Index = 0;
}
}
// Initialize for a RainbowCycle
void RainbowCycle(uint8_t interval, direction dir = FORWARD)
{
ActivePattern = RAINBOW_CYCLE;
Interval = interval;
TotalSteps = 255;
Index = 0;
Direction = dir;
}
// Update the Rainbow Cycle Pattern
void RainbowCycleUpdate()
{
for(int i=0; i< numPixels(); i++)
{
setPixelColor(i, Wheel(((i * 256 / numPixels()) + Index) & 255));
}
show();
Increment();
}
// Initialize for a Theater Chase
void TheaterChase(uint32_t color1, uint32_t color2, uint8_t interval, direction dir = FORWARD)
{
ActivePattern = THEATER_CHASE;
Interval = interval;
TotalSteps = numPixels();
Color1 = color1;
Color2 = color2;
Index = 0;
Direction = dir;
}
// Update the Theater Chase Pattern
void TheaterChaseUpdate()
{
for(int i=0; i< numPixels(); i++)
{
if ((i + Index) % 3 == 0)
{
setPixelColor(i, Color1);
}
else
{
setPixelColor(i, Color2);
}
}
show();
Increment();
}
// Initialize for a ColorWipe
void ColorWipe(uint32_t color, uint8_t interval, direction dir = FORWARD)
{
ActivePattern = COLOR_WIPE;
Interval = interval;
TotalSteps = numPixels();
Color1 = color;
Index = 0;
Direction = dir;
}
// Update the Color Wipe Pattern
void ColorWipeUpdate()
{
setPixelColor(Index, Color1);
show();
Increment();
}
// Initialize for a SCANNNER
void Scanner(uint32_t color1, uint8_t interval)
{
ActivePattern = SCANNER;
Interval = interval;
TotalSteps = (numPixels() - 1) * 2;
Color1 = color1;
Index = 0;
}
// Update the Scanner Pattern
void ScannerUpdate()
{
for (int i = 0; i < numPixels(); i++)
{
if (i == Index) // Scan Pixel to the right
{
setPixelColor(i, Color1);
}
else if (i == TotalSteps - Index) // Scan Pixel to the left
{
setPixelColor(i, Color1);
}
else // Fading tail
{
setPixelColor(i, DimColor(getPixelColor(i)));
}
}
show();
Increment();
}
// Initialize for a Fade
void Fade(uint32_t color1, uint32_t color2, uint16_t steps, uint8_t interval, direction dir = FORWARD)
{
ActivePattern = FADE;
Interval = interval;
TotalSteps = steps;
Color1 = color1;
Color2 = color2;
Index = 0;
Direction = dir;
}
// Update the Fade Pattern
void FadeUpdate()
{
// Calculate linear interpolation between Color1 and Color2
// Optimise order of operations to minimize truncation error
uint8_t red = ((Red(Color1) * (TotalSteps - Index)) + (Red(Color2) * Index)) / TotalSteps;
uint8_t green = ((Green(Color1) * (TotalSteps - Index)) + (Green(Color2) * Index)) / TotalSteps;
uint8_t blue = ((Blue(Color1) * (TotalSteps - Index)) + (Blue(Color2) * Index)) / TotalSteps;
ColorSet(Color(red, green, blue));
show();
Increment();
}
// Calculate 50% dimmed version of a color (used by ScannerUpdate)
uint32_t DimColor(uint32_t color)
{
// Shift R, G and B components one bit to the right
uint32_t dimColor = Color(Red(color) >> 1, Green(color) >> 1, Blue(color) >> 1);
return dimColor;
}
// Set all pixels to a color (synchronously)
void ColorSet(uint32_t color)
{
for (int i = 0; i < numPixels(); i++)
{
setPixelColor(i, color);
}
show();
}
// Returns the Red component of a 32-bit color
uint8_t Red(uint32_t color)
{
return (color >> 16) & 0xFF;
}
// Returns the Green component of a 32-bit color
uint8_t Green(uint32_t color)
{
return (color >> 8) & 0xFF;
}
// Returns the Blue component of a 32-bit color
uint8_t Blue(uint32_t color)
{
return color & 0xFF;
}
// Input a value 0 to 255 to get a color value.
// The colours are a transition r - g - b - back to r.
uint32_t Wheel(byte WheelPos)
{
WheelPos = 255 - WheelPos;
if(WheelPos < 85)
{
return Color(255 - WheelPos * 3, 0, WheelPos * 3);
}
else if(WheelPos < 170)
{
WheelPos -= 85;
return Color(0, WheelPos * 3, 255 - WheelPos * 3);
}
else
{
WheelPos -= 170;
return Color(WheelPos * 3, 255 - WheelPos * 3, 0);
}
}
};
void Strip1Complete();
void Strip2Complete();
NeoPatterns Strip1(12, 5, NEO_GRB + NEO_KHZ800, &Strip1Complete);
NeoPatterns Strip2(12, 6, NEO_GRB + NEO_KHZ800, &Strip2Complete);
void setup()
{
Strip1.begin();
Strip2.begin();
// Kick off a pattern
//THEATERCHASE
//Strip2.TheaterChase(Strip1.Color(255,255,0), Strip2.Color(0,0,50), 100);
//RAINBOWCYCLE
Strip1.RainbowCycle(10);
Strip1.Color1 = Strip1.Color1;
//SCANNER
//Strip2.Scanner(Strip2.Color(50,0,255), 255);
//FADE
Strip2.Scanner(Strip2.Color(50,0,255), 255);
Strip2.ActivePattern = FADE;
Strip2.Interval = 100;
}
// Main loop
void loop()
{
// Update the Strips.
Strip1.Update();
Strip2.Update();
}
// Strip1 Completion Callback
void Strip1Complete()
{
}
//Strip2 Completion Callback
void Strip2Complete()
{
if (Strip2.Direction == FORWARD)
{
Strip2.Direction = REVERSE;
}
else
{
Strip2.Direction = FORWARD;
}
}
Last edited by adafruit_support_bill on Wed Aug 12, 2020 8:46 am, edited 1 time in total.
Reason: added code in-line
Reason: added code in-line
- adafruit_support_bill
- Posts: 88093
- Joined: Sat Feb 07, 2009 10:11 am
Re: Multitasking
Code: Select all
//FADE
Strip2.Scanner(Strip2.Color(50,0,255), 255);
Strip2.ActivePattern = FADE;
Strip2.Interval = 100;
Code: Select all
//FADE - bright blue to dim blue - 100 steps - 10 ms per step
Strip2.Fade(Strip2.Color(50,0,255), Strip2.Color(0,0,25), 100, 10, FORWARD);
- AndyWilx
- Posts: 8
- Joined: Mon Aug 10, 2020 8:12 am
Re: Multitasking
That's great thank you.
And does the:
if (Strip2.Direction == FORWARD)
{
Strip2.Direction = REVERSE;
}
else
{
Strip2.Direction = FORWARD;
}
Code go into the Completion Callback?
I've put it there and then just cycles in reverse.
I had a fiddle with defining the reverse in the Callback, which I can see is ugly and not good code but it worked properly for one cycle. It then just loops in reverse.
//Strip2 Completion Callback
void Strip2Complete()
{
if (Strip2.Color(0,0,25))
{
// Strip2.Direction = REVERSE;
Strip2.Fade(Strip2.Color(0,0,25), Strip2.Color(0,0,255), 100, 10);
}
else
{
Strip2.Direction = FORWARD;
}
}
I'm assuming I'm not defining what FORWARD is in the main loop?
And does the:
if (Strip2.Direction == FORWARD)
{
Strip2.Direction = REVERSE;
}
else
{
Strip2.Direction = FORWARD;
}
Code go into the Completion Callback?
I've put it there and then just cycles in reverse.
I had a fiddle with defining the reverse in the Callback, which I can see is ugly and not good code but it worked properly for one cycle. It then just loops in reverse.
//Strip2 Completion Callback
void Strip2Complete()
{
if (Strip2.Color(0,0,25))
{
// Strip2.Direction = REVERSE;
Strip2.Fade(Strip2.Color(0,0,25), Strip2.Color(0,0,255), 100, 10);
}
else
{
Strip2.Direction = FORWARD;
}
}
I'm assuming I'm not defining what FORWARD is in the main loop?
- Attachments
-
[The extension ino has been deactivated and can no longer be displayed.]
- adafruit_support_bill
- Posts: 88093
- Joined: Sat Feb 07, 2009 10:11 am
Re: Multitasking
This should work:
Code: Select all
//Strip2 Completion Callback
void Strip2Complete()
{
if (Strip2.Direction == FORWARD)
{
Strip2.Direction = REVERSE;
}
else
{
Strip2.Direction = FORWARD;
}
}
- AndyWilx
- Posts: 8
- Joined: Mon Aug 10, 2020 8:12 am
Re: Multitasking
I tried that first and it doesn't.
this is the result:
3-2-1-flicker-1-2-3
1-2-3
1-2
1-2
1-2-3
1-2-3
1-2
1-2
If I add
void Strip2Complete()
{
if (Strip2.Direction == FORWARD)
{
Strip2.Direction = REVERSE;
Strip2.Fade(Strip2.Color(0,0,25), Strip2.Color(0,0,255), 100, 10);
}
else
{
Strip2.Direction = FORWARD;
Strip2.Fade(Strip2.Color(0,0,255), Strip2.Color(0,0,25), 100, 10);
}
}
I get:
3-2-1-2-3
1-2-3
1-2-3
1-2-3
Frustratingly close.
this is the result:
3-2-1-flicker-1-2-3
1-2-3
1-2
1-2
1-2-3
1-2-3
1-2
1-2
If I add
void Strip2Complete()
{
if (Strip2.Direction == FORWARD)
{
Strip2.Direction = REVERSE;
Strip2.Fade(Strip2.Color(0,0,25), Strip2.Color(0,0,255), 100, 10);
}
else
{
Strip2.Direction = FORWARD;
Strip2.Fade(Strip2.Color(0,0,255), Strip2.Color(0,0,25), 100, 10);
}
}
I get:
3-2-1-2-3
1-2-3
1-2-3
1-2-3
Frustratingly close.
- adafruit_support_bill
- Posts: 88093
- Joined: Sat Feb 07, 2009 10:11 am
Re: Multitasking
Try this:
Code: Select all
//Strip2 Completion Callback
void Strip2Complete()
{
if (Strip2.Direction == FORWARD)
{
Strip2.Direction = REVERSE;
Strip2.Index = Strip2.TotalSteps-1;
}
else
{
Strip2.Direction = FORWARD;
Strip2.Index = 0;
}
}
Please be positive and constructive with your questions and comments.