feat: optimize neopattern example
This commit is contained in:
376
examples/neopattern/NeoPattern.cpp
Normal file
376
examples/neopattern/NeoPattern.cpp
Normal file
@@ -0,0 +1,376 @@
|
|||||||
|
#include "NeoPattern.h"
|
||||||
|
|
||||||
|
// Constructor - calls base-class constructor to initialize strip
|
||||||
|
NeoPattern::NeoPattern(uint16_t pixels, uint8_t pin, uint8_t type, void (*callback)(int))
|
||||||
|
: Adafruit_NeoPixel(pixels, pin, type)
|
||||||
|
{
|
||||||
|
frameBuffer = (uint8_t *)malloc(768);
|
||||||
|
OnComplete = callback;
|
||||||
|
TotalSteps = numPixels();
|
||||||
|
begin();
|
||||||
|
}
|
||||||
|
|
||||||
|
NeoPattern::NeoPattern(uint16_t pixels, uint8_t pin, uint8_t type)
|
||||||
|
: Adafruit_NeoPixel(pixels, pin, type)
|
||||||
|
{
|
||||||
|
frameBuffer = (uint8_t *)malloc(768);
|
||||||
|
TotalSteps = numPixels();
|
||||||
|
begin();
|
||||||
|
}
|
||||||
|
|
||||||
|
NeoPattern::~NeoPattern() {
|
||||||
|
if (frameBuffer) {
|
||||||
|
free(frameBuffer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void NeoPattern::handleStream(uint8_t *data, size_t len)
|
||||||
|
{
|
||||||
|
//const uint16_t *data16 = (uint16_t *)data;
|
||||||
|
bufferSize = len;
|
||||||
|
memcpy(frameBuffer, data, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
void NeoPattern::drawFrameBuffer(int w, uint8_t *frame, int length)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < length; i++)
|
||||||
|
{
|
||||||
|
uint8_t r = frame[i];
|
||||||
|
uint8_t g = frame[i + 1];
|
||||||
|
uint8_t b = frame[i + 2];
|
||||||
|
setPixelColor(i, r, g, b);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void NeoPattern::onCompleteDefault(int pixels)
|
||||||
|
{
|
||||||
|
//Serial.println("onCompleteDefault");
|
||||||
|
// FIXME no specific code
|
||||||
|
if (ActivePattern == THEATER_CHASE)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Reverse();
|
||||||
|
//Serial.println("pattern completed");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Increment the Index and reset at the end
|
||||||
|
void NeoPattern::Increment()
|
||||||
|
{
|
||||||
|
completed = 0;
|
||||||
|
if (Direction == FORWARD)
|
||||||
|
{
|
||||||
|
Index++;
|
||||||
|
if (Index >= TotalSteps)
|
||||||
|
{
|
||||||
|
Index = 0;
|
||||||
|
completed = 1;
|
||||||
|
if (OnComplete != NULL)
|
||||||
|
{
|
||||||
|
OnComplete(numPixels()); // call the comlpetion callback
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
onCompleteDefault(numPixels());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else // Direction == REVERSE
|
||||||
|
{
|
||||||
|
--Index;
|
||||||
|
if (Index <= 0)
|
||||||
|
{
|
||||||
|
Index = TotalSteps - 1;
|
||||||
|
completed = 1;
|
||||||
|
if (OnComplete != NULL)
|
||||||
|
{
|
||||||
|
OnComplete(numPixels()); // call the comlpetion callback
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
onCompleteDefault(numPixels());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reverse pattern direction
|
||||||
|
void NeoPattern::Reverse()
|
||||||
|
{
|
||||||
|
if (Direction == FORWARD)
|
||||||
|
{
|
||||||
|
Direction = REVERSE;
|
||||||
|
Index = TotalSteps - 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Direction = FORWARD;
|
||||||
|
Index = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize for a RainbowCycle
|
||||||
|
void NeoPattern::RainbowCycle(uint8_t interval, direction dir)
|
||||||
|
{
|
||||||
|
ActivePattern = RAINBOW_CYCLE;
|
||||||
|
Interval = interval;
|
||||||
|
TotalSteps = 255;
|
||||||
|
Index = 0;
|
||||||
|
Direction = dir;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update the Rainbow Cycle Pattern
|
||||||
|
void NeoPattern::RainbowCycleUpdate()
|
||||||
|
{
|
||||||
|
for (int i = 0; i < numPixels(); i++)
|
||||||
|
{
|
||||||
|
setPixelColor(i, Wheel(((i * 256 / numPixels()) + Index) & 255));
|
||||||
|
}
|
||||||
|
show();
|
||||||
|
Increment();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize for a Theater Chase
|
||||||
|
void NeoPattern::TheaterChase(uint32_t color1, uint32_t color2, uint16_t interval, direction dir)
|
||||||
|
{
|
||||||
|
ActivePattern = THEATER_CHASE;
|
||||||
|
Interval = interval;
|
||||||
|
TotalSteps = numPixels();
|
||||||
|
Color1 = color1;
|
||||||
|
Color2 = color2;
|
||||||
|
Index = 0;
|
||||||
|
Direction = dir;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update the Theater Chase Pattern
|
||||||
|
void NeoPattern::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 NeoPattern::ColorWipe(uint32_t color, uint8_t interval, direction dir)
|
||||||
|
{
|
||||||
|
ActivePattern = COLOR_WIPE;
|
||||||
|
Interval = interval;
|
||||||
|
TotalSteps = numPixels();
|
||||||
|
Color1 = color;
|
||||||
|
Index = 0;
|
||||||
|
Direction = dir;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update the Color Wipe Pattern
|
||||||
|
void NeoPattern::ColorWipeUpdate()
|
||||||
|
{
|
||||||
|
setPixelColor(Index, Color1);
|
||||||
|
show();
|
||||||
|
Increment();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize for a SCANNNER
|
||||||
|
void NeoPattern::Scanner(uint32_t color1, uint8_t interval)
|
||||||
|
{
|
||||||
|
ActivePattern = SCANNER;
|
||||||
|
Interval = interval;
|
||||||
|
TotalSteps = (numPixels() - 1) * 2;
|
||||||
|
Color1 = color1;
|
||||||
|
Index = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update the Scanner Pattern
|
||||||
|
void NeoPattern::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 NeoPattern::Fade(uint32_t color1, uint32_t color2, uint16_t steps, uint8_t interval, direction dir)
|
||||||
|
{
|
||||||
|
ActivePattern = FADE;
|
||||||
|
Interval = interval;
|
||||||
|
TotalSteps = steps;
|
||||||
|
Color1 = color1;
|
||||||
|
Color2 = color2;
|
||||||
|
Index = 0;
|
||||||
|
Direction = dir;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update the Fade Pattern
|
||||||
|
void NeoPattern::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 NeoPattern::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 NeoPattern::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 NeoPattern::Red(uint32_t color)
|
||||||
|
{
|
||||||
|
return (color >> 16) & 0xFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns the Green component of a 32-bit color
|
||||||
|
uint8_t NeoPattern::Green(uint32_t color)
|
||||||
|
{
|
||||||
|
return (color >> 8) & 0xFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns the Blue component of a 32-bit color
|
||||||
|
uint8_t NeoPattern::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 NeoPattern::Wheel(uint8_t WheelPos)
|
||||||
|
{
|
||||||
|
//if(WheelPos == 0) return Color(0,0,0);
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Effects from https://www.tweaking4all.com/hardware/arduino/adruino-led-strip-effects/
|
||||||
|
*/
|
||||||
|
void NeoPattern::Fire(int Cooling, int Sparking)
|
||||||
|
{
|
||||||
|
uint8_t heat[numPixels()];
|
||||||
|
int cooldown;
|
||||||
|
|
||||||
|
// Step 1. Cool down every cell a little
|
||||||
|
for (int i = 0; i < numPixels(); i++)
|
||||||
|
{
|
||||||
|
cooldown = random(0, ((Cooling * 10) / numPixels()) + 2);
|
||||||
|
|
||||||
|
if (cooldown > heat[i])
|
||||||
|
{
|
||||||
|
heat[i] = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
heat[i] = heat[i] - cooldown;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step 2. Heat from each cell drifts 'up' and diffuses a little
|
||||||
|
for (int k = numPixels() - 1; k >= 2; k--)
|
||||||
|
{
|
||||||
|
heat[k] = (heat[k - 1] + heat[k - 2] + heat[k - 2]) / 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step 3. Randomly ignite new 'sparks' near the bottom
|
||||||
|
if (random(255) < Sparking)
|
||||||
|
{
|
||||||
|
int y = random(7);
|
||||||
|
heat[y] = heat[y] + random(160, 255);
|
||||||
|
//heat[y] = random(160,255);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step 4. Convert heat to LED colors
|
||||||
|
for (int j = 0; j < numPixels(); j++)
|
||||||
|
{
|
||||||
|
setPixelHeatColor(j, heat[j]);
|
||||||
|
}
|
||||||
|
|
||||||
|
showStrip();
|
||||||
|
}
|
||||||
|
|
||||||
|
void NeoPattern::setPixelHeatColor(int Pixel, uint8_t temperature)
|
||||||
|
{
|
||||||
|
// Scale 'heat' down from 0-255 to 0-191
|
||||||
|
uint8_t t192 = round((temperature / 255.0) * 191);
|
||||||
|
|
||||||
|
// calculate ramp up from
|
||||||
|
uint8_t heatramp = t192 & 0x3F; // 0..63
|
||||||
|
heatramp <<= 2; // scale up to 0..252
|
||||||
|
|
||||||
|
// figure out which third of the spectrum we're in:
|
||||||
|
if (t192 > 0x80)
|
||||||
|
{ // hottest
|
||||||
|
setPixel(Pixel, 255, 255, heatramp);
|
||||||
|
}
|
||||||
|
else if (t192 > 0x40)
|
||||||
|
{ // middle
|
||||||
|
setPixel(Pixel, 255, heatramp, 0);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{ // coolest
|
||||||
|
setPixel(Pixel, heatramp, 0, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void NeoPattern::setPixel(int Pixel, uint8_t red, uint8_t green, uint8_t blue)
|
||||||
|
{
|
||||||
|
setPixelColor(Pixel, Color(red, green, blue));
|
||||||
|
}
|
||||||
|
|
||||||
|
void NeoPattern::showStrip()
|
||||||
|
{
|
||||||
|
show();
|
||||||
|
}
|
||||||
@@ -56,424 +56,54 @@ class NeoPattern : public Adafruit_NeoPixel
|
|||||||
int bufferSize = 0;
|
int bufferSize = 0;
|
||||||
|
|
||||||
// Constructor - calls base-class constructor to initialize strip
|
// Constructor - calls base-class constructor to initialize strip
|
||||||
NeoPattern(uint16_t pixels, uint8_t pin, uint8_t type, void (*callback)(int))
|
NeoPattern(uint16_t pixels, uint8_t pin, uint8_t type, void (*callback)(int));
|
||||||
: Adafruit_NeoPixel(pixels, pin, type)
|
NeoPattern(uint16_t pixels, uint8_t pin, uint8_t type);
|
||||||
{
|
~NeoPattern();
|
||||||
frameBuffer = (uint8_t *)malloc(768);
|
|
||||||
OnComplete = callback;
|
|
||||||
TotalSteps = numPixels();
|
|
||||||
begin();
|
|
||||||
}
|
|
||||||
|
|
||||||
NeoPattern(uint16_t pixels, uint8_t pin, uint8_t type)
|
// Stream handling
|
||||||
: Adafruit_NeoPixel(pixels, pin, type)
|
void handleStream(uint8_t *data, size_t len);
|
||||||
{
|
void drawFrameBuffer(int w, uint8_t *frame, int length);
|
||||||
frameBuffer = (uint8_t *)malloc(768);
|
|
||||||
TotalSteps = numPixels();
|
|
||||||
begin();
|
|
||||||
}
|
|
||||||
|
|
||||||
~NeoPattern() {
|
// Pattern completion
|
||||||
if (frameBuffer) {
|
void onCompleteDefault(int pixels);
|
||||||
free(frameBuffer);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
// Pattern control
|
||||||
|
void Increment();
|
||||||
|
void Reverse();
|
||||||
|
|
||||||
// Implementation starts here (inline)
|
// Rainbow Cycle
|
||||||
void handleStream(uint8_t *data, size_t len)
|
void RainbowCycle(uint8_t interval, direction dir = FORWARD);
|
||||||
{
|
void RainbowCycleUpdate();
|
||||||
//const uint16_t *data16 = (uint16_t *)data;
|
|
||||||
bufferSize = len;
|
|
||||||
memcpy(frameBuffer, data, len);
|
|
||||||
}
|
|
||||||
|
|
||||||
void drawFrameBuffer(int w, uint8_t *frame, int length)
|
// Theater Chase
|
||||||
{
|
void TheaterChase(uint32_t color1, uint32_t color2, uint16_t interval, direction dir = FORWARD);
|
||||||
for (int i = 0; i < length; i++)
|
void TheaterChaseUpdate();
|
||||||
{
|
|
||||||
uint8_t r = frame[i];
|
|
||||||
uint8_t g = frame[i + 1];
|
|
||||||
uint8_t b = frame[i + 2];
|
|
||||||
setPixelColor(i, r, g, b);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void onCompleteDefault(int pixels)
|
// Color Wipe
|
||||||
{
|
void ColorWipe(uint32_t color, uint8_t interval, direction dir = FORWARD);
|
||||||
//Serial.println("onCompleteDefault");
|
void ColorWipeUpdate();
|
||||||
// FIXME no specific code
|
|
||||||
if (ActivePattern == THEATER_CHASE)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
Reverse();
|
|
||||||
//Serial.println("pattern completed");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update the pattern
|
// Scanner
|
||||||
void Update()
|
void Scanner(uint32_t color1, uint8_t interval);
|
||||||
{
|
void ScannerUpdate();
|
||||||
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;
|
|
||||||
case FIRE:
|
|
||||||
Fire(50, 120);
|
|
||||||
break;
|
|
||||||
case NONE:
|
|
||||||
// For NONE pattern, just maintain current state
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
if (bufferSize > 0)
|
|
||||||
{
|
|
||||||
drawFrameBuffer(TotalSteps, frameBuffer, bufferSize);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void UpdateScheduled()
|
// Fade
|
||||||
{
|
void Fade(uint32_t color1, uint32_t color2, uint16_t steps, uint8_t interval, direction dir = FORWARD);
|
||||||
if ((millis() - lastUpdate) > Interval) // time to update
|
void FadeUpdate();
|
||||||
{
|
|
||||||
lastUpdate = millis();
|
|
||||||
Update();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Increment the Index and reset at the end
|
// Fire effect
|
||||||
void Increment()
|
void Fire(int Cooling, int Sparking);
|
||||||
{
|
void setPixelHeatColor(int Pixel, uint8_t temperature);
|
||||||
completed = 0;
|
void setPixel(int Pixel, uint8_t red, uint8_t green, uint8_t blue);
|
||||||
if (Direction == FORWARD)
|
void showStrip();
|
||||||
{
|
|
||||||
Index++;
|
|
||||||
if (Index >= TotalSteps)
|
|
||||||
{
|
|
||||||
Index = 0;
|
|
||||||
completed = 1;
|
|
||||||
if (OnComplete != NULL)
|
|
||||||
{
|
|
||||||
OnComplete(numPixels()); // call the comlpetion callback
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
onCompleteDefault(numPixels());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else // Direction == REVERSE
|
|
||||||
{
|
|
||||||
--Index;
|
|
||||||
if (Index <= 0)
|
|
||||||
{
|
|
||||||
Index = TotalSteps - 1;
|
|
||||||
completed = 1;
|
|
||||||
if (OnComplete != NULL)
|
|
||||||
{
|
|
||||||
OnComplete(numPixels()); // call the comlpetion callback
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
onCompleteDefault(numPixels());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Reverse pattern direction
|
// Utility methods
|
||||||
void Reverse()
|
uint32_t DimColor(uint32_t color);
|
||||||
{
|
void ColorSet(uint32_t color);
|
||||||
if (Direction == FORWARD)
|
uint8_t Red(uint32_t color);
|
||||||
{
|
uint8_t Green(uint32_t color);
|
||||||
Direction = REVERSE;
|
uint8_t Blue(uint32_t color);
|
||||||
Index = TotalSteps - 1;
|
uint32_t Wheel(uint8_t WheelPos);
|
||||||
}
|
|
||||||
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, uint16_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(uint8_t WheelPos)
|
|
||||||
{
|
|
||||||
//if(WheelPos == 0) return Color(0,0,0);
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Effects from https://www.tweaking4all.com/hardware/arduino/adruino-led-strip-effects/
|
|
||||||
*/
|
|
||||||
void Fire(int Cooling, int Sparking)
|
|
||||||
{
|
|
||||||
uint8_t heat[numPixels()];
|
|
||||||
int cooldown;
|
|
||||||
|
|
||||||
// Step 1. Cool down every cell a little
|
|
||||||
for (int i = 0; i < numPixels(); i++)
|
|
||||||
{
|
|
||||||
cooldown = random(0, ((Cooling * 10) / numPixels()) + 2);
|
|
||||||
|
|
||||||
if (cooldown > heat[i])
|
|
||||||
{
|
|
||||||
heat[i] = 0;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
heat[i] = heat[i] - cooldown;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Step 2. Heat from each cell drifts 'up' and diffuses a little
|
|
||||||
for (int k = numPixels() - 1; k >= 2; k--)
|
|
||||||
{
|
|
||||||
heat[k] = (heat[k - 1] + heat[k - 2] + heat[k - 2]) / 3;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Step 3. Randomly ignite new 'sparks' near the bottom
|
|
||||||
if (random(255) < Sparking)
|
|
||||||
{
|
|
||||||
int y = random(7);
|
|
||||||
heat[y] = heat[y] + random(160, 255);
|
|
||||||
//heat[y] = random(160,255);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Step 4. Convert heat to LED colors
|
|
||||||
for (int j = 0; j < numPixels(); j++)
|
|
||||||
{
|
|
||||||
setPixelHeatColor(j, heat[j]);
|
|
||||||
}
|
|
||||||
|
|
||||||
showStrip();
|
|
||||||
}
|
|
||||||
|
|
||||||
void setPixelHeatColor(int Pixel, uint8_t temperature)
|
|
||||||
{
|
|
||||||
// Scale 'heat' down from 0-255 to 0-191
|
|
||||||
uint8_t t192 = round((temperature / 255.0) * 191);
|
|
||||||
|
|
||||||
// calculate ramp up from
|
|
||||||
uint8_t heatramp = t192 & 0x3F; // 0..63
|
|
||||||
heatramp <<= 2; // scale up to 0..252
|
|
||||||
|
|
||||||
// figure out which third of the spectrum we're in:
|
|
||||||
if (t192 > 0x80)
|
|
||||||
{ // hottest
|
|
||||||
setPixel(Pixel, 255, 255, heatramp);
|
|
||||||
}
|
|
||||||
else if (t192 > 0x40)
|
|
||||||
{ // middle
|
|
||||||
setPixel(Pixel, 255, heatramp, 0);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{ // coolest
|
|
||||||
setPixel(Pixel, heatramp, 0, 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void setPixel(int Pixel, uint8_t red, uint8_t green, uint8_t blue)
|
|
||||||
{
|
|
||||||
setPixelColor(Pixel, Color(red, green, blue));
|
|
||||||
}
|
|
||||||
|
|
||||||
void showStrip()
|
|
||||||
{
|
|
||||||
show();
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
@@ -86,6 +86,12 @@ void NeoPatternService::handleStatusRequest(AsyncWebServerRequest* request) {
|
|||||||
doc["interval"] = updateIntervalMs;
|
doc["interval"] = updateIntervalMs;
|
||||||
doc["active"] = initialized;
|
doc["active"] = initialized;
|
||||||
|
|
||||||
|
// Add pattern metadata
|
||||||
|
String currentPattern = currentPatternName();
|
||||||
|
doc["pattern_description"] = getPatternDescription(currentPattern);
|
||||||
|
doc["pattern_requires_color2"] = patternRequiresColor2(currentPattern);
|
||||||
|
doc["pattern_supports_direction"] = patternSupportsDirection(currentPattern);
|
||||||
|
|
||||||
String json;
|
String json;
|
||||||
serializeJson(doc, json);
|
serializeJson(doc, json);
|
||||||
request->send(200, "application/json", json);
|
request->send(200, "application/json", json);
|
||||||
@@ -94,8 +100,16 @@ void NeoPatternService::handleStatusRequest(AsyncWebServerRequest* request) {
|
|||||||
void NeoPatternService::handlePatternsRequest(AsyncWebServerRequest* request) {
|
void NeoPatternService::handlePatternsRequest(AsyncWebServerRequest* request) {
|
||||||
JsonDocument doc;
|
JsonDocument doc;
|
||||||
JsonArray arr = doc.to<JsonArray>();
|
JsonArray arr = doc.to<JsonArray>();
|
||||||
for (const auto& kv : patternUpdaters) {
|
|
||||||
arr.add(kv.first);
|
// Get all patterns from registry and include metadata
|
||||||
|
auto patterns = patternRegistry.getAllPatterns();
|
||||||
|
for (const auto& pattern : patterns) {
|
||||||
|
JsonObject patternObj = arr.add<JsonObject>();
|
||||||
|
patternObj["name"] = pattern.name;
|
||||||
|
patternObj["type"] = pattern.type;
|
||||||
|
patternObj["description"] = pattern.description;
|
||||||
|
patternObj["requires_color2"] = pattern.requiresColor2;
|
||||||
|
patternObj["supports_direction"] = pattern.supportsDirection;
|
||||||
}
|
}
|
||||||
|
|
||||||
String json;
|
String json;
|
||||||
@@ -108,8 +122,13 @@ void NeoPatternService::handleControlRequest(AsyncWebServerRequest* request) {
|
|||||||
|
|
||||||
if (request->hasParam("pattern", true)) {
|
if (request->hasParam("pattern", true)) {
|
||||||
String name = request->getParam("pattern", true)->value();
|
String name = request->getParam("pattern", true)->value();
|
||||||
setPatternByName(name);
|
if (isValidPattern(name)) {
|
||||||
updated = true;
|
setPatternByName(name);
|
||||||
|
updated = true;
|
||||||
|
} else {
|
||||||
|
// Invalid pattern name - could add error handling here
|
||||||
|
LOG_WARN("NeoPattern", "Invalid pattern name: " + name);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (request->hasParam("color", true)) {
|
if (request->hasParam("color", true)) {
|
||||||
@@ -195,6 +214,9 @@ void NeoPatternService::setPattern(NeoPatternType pattern) {
|
|||||||
currentState.pattern = static_cast<uint>(pattern);
|
currentState.pattern = static_cast<uint>(pattern);
|
||||||
neoPattern->ActivePattern = static_cast<::pattern>(pattern);
|
neoPattern->ActivePattern = static_cast<::pattern>(pattern);
|
||||||
resetStateForPattern(pattern);
|
resetStateForPattern(pattern);
|
||||||
|
|
||||||
|
// Initialize the pattern using the registry
|
||||||
|
patternRegistry.initializePattern(static_cast<uint8_t>(pattern));
|
||||||
}
|
}
|
||||||
|
|
||||||
void NeoPatternService::setPatternByName(const String& name) {
|
void NeoPatternService::setPatternByName(const String& name) {
|
||||||
@@ -263,46 +285,89 @@ void NeoPatternService::registerTasks() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void NeoPatternService::registerPatterns() {
|
void NeoPatternService::registerPatterns() {
|
||||||
// Register pattern updaters
|
// Register all patterns with their metadata and callbacks
|
||||||
patternUpdaters["none"] = [this]() { updateNone(); };
|
patternRegistry.registerPattern(
|
||||||
patternUpdaters["rainbow_cycle"] = [this]() { updateRainbowCycle(); };
|
"none",
|
||||||
patternUpdaters["theater_chase"] = [this]() { updateTheaterChase(); };
|
static_cast<uint8_t>(NeoPatternType::NONE),
|
||||||
patternUpdaters["color_wipe"] = [this]() { updateColorWipe(); };
|
"No pattern - solid color",
|
||||||
patternUpdaters["scanner"] = [this]() { updateScanner(); };
|
[this]() { /* No initialization needed */ },
|
||||||
patternUpdaters["fade"] = [this]() { updateFade(); };
|
[this]() { updateNone(); },
|
||||||
patternUpdaters["fire"] = [this]() { updateFire(); };
|
false, // doesn't require color2
|
||||||
|
false // doesn't support direction
|
||||||
|
);
|
||||||
|
|
||||||
// Register name to pattern mapping
|
patternRegistry.registerPattern(
|
||||||
nameToPatternMap["none"] = NeoPatternType::NONE;
|
"rainbow_cycle",
|
||||||
nameToPatternMap["rainbow_cycle"] = NeoPatternType::RAINBOW_CYCLE;
|
static_cast<uint8_t>(NeoPatternType::RAINBOW_CYCLE),
|
||||||
nameToPatternMap["theater_chase"] = NeoPatternType::THEATER_CHASE;
|
"Rainbow cycle pattern",
|
||||||
nameToPatternMap["color_wipe"] = NeoPatternType::COLOR_WIPE;
|
[this]() { neoPattern->RainbowCycle(updateIntervalMs, static_cast<::direction>(direction)); },
|
||||||
nameToPatternMap["scanner"] = NeoPatternType::SCANNER;
|
[this]() { updateRainbowCycle(); },
|
||||||
nameToPatternMap["fade"] = NeoPatternType::FADE;
|
false, // doesn't require color2
|
||||||
nameToPatternMap["fire"] = NeoPatternType::FIRE;
|
true // supports direction
|
||||||
|
);
|
||||||
|
|
||||||
|
patternRegistry.registerPattern(
|
||||||
|
"theater_chase",
|
||||||
|
static_cast<uint8_t>(NeoPatternType::THEATER_CHASE),
|
||||||
|
"Theater chase pattern",
|
||||||
|
[this]() { neoPattern->TheaterChase(currentState.color, currentState.color2, updateIntervalMs, static_cast<::direction>(direction)); },
|
||||||
|
[this]() { updateTheaterChase(); },
|
||||||
|
true, // requires color2
|
||||||
|
true // supports direction
|
||||||
|
);
|
||||||
|
|
||||||
|
patternRegistry.registerPattern(
|
||||||
|
"color_wipe",
|
||||||
|
static_cast<uint8_t>(NeoPatternType::COLOR_WIPE),
|
||||||
|
"Color wipe pattern",
|
||||||
|
[this]() { neoPattern->ColorWipe(currentState.color, updateIntervalMs, static_cast<::direction>(direction)); },
|
||||||
|
[this]() { updateColorWipe(); },
|
||||||
|
false, // doesn't require color2
|
||||||
|
true // supports direction
|
||||||
|
);
|
||||||
|
|
||||||
|
patternRegistry.registerPattern(
|
||||||
|
"scanner",
|
||||||
|
static_cast<uint8_t>(NeoPatternType::SCANNER),
|
||||||
|
"Scanner pattern",
|
||||||
|
[this]() { neoPattern->Scanner(currentState.color, updateIntervalMs); },
|
||||||
|
[this]() { updateScanner(); },
|
||||||
|
false, // doesn't require color2
|
||||||
|
false // doesn't support direction
|
||||||
|
);
|
||||||
|
|
||||||
|
patternRegistry.registerPattern(
|
||||||
|
"fade",
|
||||||
|
static_cast<uint8_t>(NeoPatternType::FADE),
|
||||||
|
"Fade pattern",
|
||||||
|
[this]() { neoPattern->Fade(currentState.color, currentState.color2, currentState.totalSteps, updateIntervalMs, static_cast<::direction>(direction)); },
|
||||||
|
[this]() { updateFade(); },
|
||||||
|
true, // requires color2
|
||||||
|
true // supports direction
|
||||||
|
);
|
||||||
|
|
||||||
|
patternRegistry.registerPattern(
|
||||||
|
"fire",
|
||||||
|
static_cast<uint8_t>(NeoPatternType::FIRE),
|
||||||
|
"Fire effect pattern",
|
||||||
|
[this]() { neoPattern->Fire(50, 120); },
|
||||||
|
[this]() { updateFire(); },
|
||||||
|
false, // doesn't require color2
|
||||||
|
false // doesn't support direction
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<String> NeoPatternService::patternNamesVector() const {
|
std::vector<String> NeoPatternService::patternNamesVector() const {
|
||||||
std::vector<String> names;
|
return patternRegistry.getAllPatternNames();
|
||||||
names.reserve(patternUpdaters.size());
|
|
||||||
for (const auto& kv : patternUpdaters) {
|
|
||||||
names.push_back(kv.first);
|
|
||||||
}
|
|
||||||
return names;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
String NeoPatternService::currentPatternName() const {
|
String NeoPatternService::currentPatternName() const {
|
||||||
for (const auto& kv : nameToPatternMap) {
|
return patternRegistry.getPatternName(static_cast<uint8_t>(activePattern));
|
||||||
if (kv.second == activePattern) {
|
|
||||||
return kv.first;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return "none";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
NeoPatternService::NeoPatternType NeoPatternService::nameToPattern(const String& name) const {
|
NeoPatternService::NeoPatternType NeoPatternService::nameToPattern(const String& name) const {
|
||||||
auto it = nameToPatternMap.find(name);
|
uint8_t type = patternRegistry.getPatternType(name);
|
||||||
return (it != nameToPatternMap.end()) ? it->second : NeoPatternType::NONE;
|
return static_cast<NeoPatternType>(type);
|
||||||
}
|
}
|
||||||
|
|
||||||
void NeoPatternService::resetStateForPattern(NeoPatternType pattern) {
|
void NeoPatternService::resetStateForPattern(NeoPatternType pattern) {
|
||||||
@@ -322,6 +387,29 @@ uint32_t NeoPatternService::parseColor(const String& colorStr) const {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool NeoPatternService::isValidPattern(const String& name) const {
|
||||||
|
return patternRegistry.isValidPattern(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool NeoPatternService::isValidPattern(NeoPatternType type) const {
|
||||||
|
return patternRegistry.isValidPattern(static_cast<uint8_t>(type));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool NeoPatternService::patternRequiresColor2(const String& name) const {
|
||||||
|
const PatternInfo* info = patternRegistry.getPattern(name);
|
||||||
|
return info ? info->requiresColor2 : false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool NeoPatternService::patternSupportsDirection(const String& name) const {
|
||||||
|
const PatternInfo* info = patternRegistry.getPattern(name);
|
||||||
|
return info ? info->supportsDirection : false;
|
||||||
|
}
|
||||||
|
|
||||||
|
String NeoPatternService::getPatternDescription(const String& name) const {
|
||||||
|
const PatternInfo* info = patternRegistry.getPattern(name);
|
||||||
|
return info ? info->description : "";
|
||||||
|
}
|
||||||
|
|
||||||
void NeoPatternService::update() {
|
void NeoPatternService::update() {
|
||||||
if (!initialized) return;
|
if (!initialized) return;
|
||||||
|
|
||||||
@@ -329,13 +417,8 @@ void NeoPatternService::update() {
|
|||||||
//if (now - lastUpdateMs < updateIntervalMs) return;
|
//if (now - lastUpdateMs < updateIntervalMs) return;
|
||||||
//lastUpdateMs = now;
|
//lastUpdateMs = now;
|
||||||
|
|
||||||
const String name = currentPatternName();
|
// Use pattern registry to execute the current pattern
|
||||||
auto it = patternUpdaters.find(name);
|
patternRegistry.executePattern(static_cast<uint8_t>(activePattern));
|
||||||
if (it != patternUpdaters.end()) {
|
|
||||||
it->second();
|
|
||||||
} else {
|
|
||||||
updateNone();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void NeoPatternService::updateRainbowCycle() {
|
void NeoPatternService::updateRainbowCycle() {
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
#include "NeoPattern.h"
|
#include "NeoPattern.h"
|
||||||
#include "NeoPatternState.h"
|
#include "NeoPatternState.h"
|
||||||
#include "NeoPixelConfig.h"
|
#include "NeoPixelConfig.h"
|
||||||
|
#include "PatternRegistry.h"
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
|
|
||||||
@@ -71,13 +72,20 @@ private:
|
|||||||
void resetStateForPattern(NeoPatternType pattern);
|
void resetStateForPattern(NeoPatternType pattern);
|
||||||
uint32_t parseColor(const String& colorStr) const;
|
uint32_t parseColor(const String& colorStr) const;
|
||||||
|
|
||||||
|
// Pattern validation methods
|
||||||
|
bool isValidPattern(const String& name) const;
|
||||||
|
bool isValidPattern(NeoPatternType type) const;
|
||||||
|
bool patternRequiresColor2(const String& name) const;
|
||||||
|
bool patternSupportsDirection(const String& name) const;
|
||||||
|
String getPatternDescription(const String& name) const;
|
||||||
|
|
||||||
TaskManager& taskManager;
|
TaskManager& taskManager;
|
||||||
NeoPattern* neoPattern;
|
NeoPattern* neoPattern;
|
||||||
NeoPixelConfig config;
|
NeoPixelConfig config;
|
||||||
NeoPatternState currentState;
|
NeoPatternState currentState;
|
||||||
|
|
||||||
std::map<String, std::function<void()>> patternUpdaters;
|
// Pattern registry for centralized pattern management
|
||||||
std::map<String, NeoPatternType> nameToPatternMap;
|
PatternRegistry patternRegistry;
|
||||||
|
|
||||||
NeoPatternType activePattern;
|
NeoPatternType activePattern;
|
||||||
NeoDirection direction;
|
NeoDirection direction;
|
||||||
|
|||||||
101
examples/neopattern/PatternRegistry.cpp
Normal file
101
examples/neopattern/PatternRegistry.cpp
Normal file
@@ -0,0 +1,101 @@
|
|||||||
|
#include "PatternRegistry.h"
|
||||||
|
|
||||||
|
PatternRegistry::PatternRegistry() {
|
||||||
|
// Constructor - patterns will be registered by the service
|
||||||
|
}
|
||||||
|
|
||||||
|
void PatternRegistry::registerPattern(const PatternInfo& pattern) {
|
||||||
|
patternMap_[pattern.name] = pattern;
|
||||||
|
typeToNameMap_[pattern.type] = pattern.name;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PatternRegistry::registerPattern(const String& name, uint8_t type, const String& description,
|
||||||
|
std::function<void()> initializer, std::function<void()> updater,
|
||||||
|
bool requiresColor2, bool supportsDirection) {
|
||||||
|
PatternInfo info(name, type, description, initializer, updater, requiresColor2, supportsDirection);
|
||||||
|
registerPattern(info);
|
||||||
|
}
|
||||||
|
|
||||||
|
const PatternInfo* PatternRegistry::getPattern(const String& name) const {
|
||||||
|
auto it = patternMap_.find(name);
|
||||||
|
return (it != patternMap_.end()) ? &it->second : nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
const PatternInfo* PatternRegistry::getPattern(uint8_t type) const {
|
||||||
|
auto typeIt = typeToNameMap_.find(type);
|
||||||
|
if (typeIt == typeToNameMap_.end()) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
return getPattern(typeIt->second);
|
||||||
|
}
|
||||||
|
|
||||||
|
String PatternRegistry::getPatternName(uint8_t type) const {
|
||||||
|
auto it = typeToNameMap_.find(type);
|
||||||
|
return (it != typeToNameMap_.end()) ? it->second : "";
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t PatternRegistry::getPatternType(const String& name) const {
|
||||||
|
const PatternInfo* info = getPattern(name);
|
||||||
|
return info ? info->type : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<String> PatternRegistry::getAllPatternNames() const {
|
||||||
|
std::vector<String> names;
|
||||||
|
names.reserve(patternMap_.size());
|
||||||
|
for (const auto& pair : patternMap_) {
|
||||||
|
names.push_back(pair.first);
|
||||||
|
}
|
||||||
|
return names;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<PatternInfo> PatternRegistry::getAllPatterns() const {
|
||||||
|
std::vector<PatternInfo> patterns;
|
||||||
|
patterns.reserve(patternMap_.size());
|
||||||
|
for (const auto& pair : patternMap_) {
|
||||||
|
patterns.push_back(pair.second);
|
||||||
|
}
|
||||||
|
return patterns;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PatternRegistry::isValidPattern(const String& name) const {
|
||||||
|
return patternMap_.find(name) != patternMap_.end();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PatternRegistry::isValidPattern(uint8_t type) const {
|
||||||
|
return typeToNameMap_.find(type) != typeToNameMap_.end();
|
||||||
|
}
|
||||||
|
|
||||||
|
void PatternRegistry::executePattern(const String& name) const {
|
||||||
|
const PatternInfo* info = getPattern(name);
|
||||||
|
if (info && info->updater) {
|
||||||
|
info->updater();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PatternRegistry::executePattern(uint8_t type) const {
|
||||||
|
const PatternInfo* info = getPattern(type);
|
||||||
|
if (info && info->updater) {
|
||||||
|
info->updater();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PatternRegistry::initializePattern(const String& name) const {
|
||||||
|
const PatternInfo* info = getPattern(name);
|
||||||
|
if (info && info->initializer) {
|
||||||
|
info->initializer();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PatternRegistry::initializePattern(uint8_t type) const {
|
||||||
|
const PatternInfo* info = getPattern(type);
|
||||||
|
if (info && info->initializer) {
|
||||||
|
info->initializer();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PatternRegistry::updateTypeMap() {
|
||||||
|
typeToNameMap_.clear();
|
||||||
|
for (const auto& pair : patternMap_) {
|
||||||
|
typeToNameMap_[pair.second.type] = pair.first;
|
||||||
|
}
|
||||||
|
}
|
||||||
73
examples/neopattern/PatternRegistry.h
Normal file
73
examples/neopattern/PatternRegistry.h
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <map>
|
||||||
|
#include <functional>
|
||||||
|
#include <vector>
|
||||||
|
#include <Arduino.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PatternInfo structure containing all information needed for a pattern
|
||||||
|
*/
|
||||||
|
struct PatternInfo {
|
||||||
|
String name;
|
||||||
|
uint8_t type;
|
||||||
|
String description;
|
||||||
|
std::function<void()> updater;
|
||||||
|
std::function<void()> initializer;
|
||||||
|
bool requiresColor2;
|
||||||
|
bool supportsDirection;
|
||||||
|
|
||||||
|
// Default constructor for std::map compatibility
|
||||||
|
PatternInfo() : type(0), requiresColor2(false), supportsDirection(false) {}
|
||||||
|
|
||||||
|
// Parameterized constructor
|
||||||
|
PatternInfo(const String& n, uint8_t t, const String& desc,
|
||||||
|
std::function<void()> initFunc, std::function<void()> updateFunc = nullptr,
|
||||||
|
bool needsColor2 = false, bool supportsDir = true)
|
||||||
|
: name(n), type(t), description(desc), updater(updateFunc),
|
||||||
|
initializer(initFunc), requiresColor2(needsColor2), supportsDirection(supportsDir) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PatternRegistry class for centralized pattern management
|
||||||
|
*/
|
||||||
|
class PatternRegistry {
|
||||||
|
public:
|
||||||
|
using PatternMap = std::map<String, PatternInfo>;
|
||||||
|
using PatternTypeMap = std::map<uint8_t, String>;
|
||||||
|
|
||||||
|
PatternRegistry();
|
||||||
|
~PatternRegistry() = default;
|
||||||
|
|
||||||
|
// Pattern registration
|
||||||
|
void registerPattern(const PatternInfo& pattern);
|
||||||
|
void registerPattern(const String& name, uint8_t type, const String& description,
|
||||||
|
std::function<void()> initializer, std::function<void()> updater = nullptr,
|
||||||
|
bool requiresColor2 = false, bool supportsDirection = true);
|
||||||
|
|
||||||
|
// Pattern lookup
|
||||||
|
const PatternInfo* getPattern(const String& name) const;
|
||||||
|
const PatternInfo* getPattern(uint8_t type) const;
|
||||||
|
String getPatternName(uint8_t type) const;
|
||||||
|
uint8_t getPatternType(const String& name) const;
|
||||||
|
|
||||||
|
// Pattern enumeration
|
||||||
|
std::vector<String> getAllPatternNames() const;
|
||||||
|
std::vector<PatternInfo> getAllPatterns() const;
|
||||||
|
const PatternMap& getPatternMap() const { return patternMap_; }
|
||||||
|
|
||||||
|
// Pattern validation
|
||||||
|
bool isValidPattern(const String& name) const;
|
||||||
|
bool isValidPattern(uint8_t type) const;
|
||||||
|
|
||||||
|
// Pattern execution
|
||||||
|
void executePattern(const String& name) const;
|
||||||
|
void executePattern(uint8_t type) const;
|
||||||
|
void initializePattern(const String& name) const;
|
||||||
|
void initializePattern(uint8_t type) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
PatternMap patternMap_;
|
||||||
|
PatternTypeMap typeToNameMap_;
|
||||||
|
|
||||||
|
void updateTypeMap();
|
||||||
|
};
|
||||||
@@ -18,13 +18,14 @@ This example demonstrates how to integrate a NeoPixel pattern service with the S
|
|||||||
|
|
||||||
## Configuration
|
## Configuration
|
||||||
|
|
||||||
Edit `config.h` to configure your setup:
|
Edit `NeoPixelConfig.h` to configure your setup:
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
#define NEOPIXEL_PIN 4 // GPIO pin connected to LED strip
|
static constexpr int DEFAULT_PIN = 2;
|
||||||
#define NEOPIXEL_LENGTH 8 // Number of LEDs in your strip
|
static constexpr int DEFAULT_LENGTH = 8;
|
||||||
#define NEOPIXEL_BRIGHTNESS 100 // Initial brightness (0-255)
|
static constexpr int DEFAULT_BRIGHTNESS = 100;
|
||||||
#define NEOPIXEL_UPDATE_INTERVAL 100 // Update interval in milliseconds
|
static constexpr int DEFAULT_UPDATE_INTERVAL = 100;
|
||||||
|
static constexpr int DEFAULT_COLOR = 0xFF0000; // Red
|
||||||
```
|
```
|
||||||
|
|
||||||
## API Endpoints
|
## API Endpoints
|
||||||
|
|||||||
@@ -1,22 +0,0 @@
|
|||||||
#ifndef __NPX_CONFIG__
|
|
||||||
#define __NPX_CONFIG__
|
|
||||||
|
|
||||||
// NeoPixel Configuration
|
|
||||||
#define NEOPIXEL_PIN 2
|
|
||||||
#define NEOPIXEL_LENGTH 4
|
|
||||||
#define NEOPIXEL_BRIGHTNESS 100
|
|
||||||
#define NEOPIXEL_UPDATE_INTERVAL 100
|
|
||||||
|
|
||||||
// Spore Framework Configuration
|
|
||||||
#define SPORE_APP_NAME "neopattern"
|
|
||||||
#define SPORE_DEVICE_TYPE "led_strip"
|
|
||||||
|
|
||||||
// Network Configuration (if needed)
|
|
||||||
#define WIFI_SSID "your_wifi_ssid"
|
|
||||||
#define WIFI_PASSWORD "your_wifi_password"
|
|
||||||
|
|
||||||
// Debug Configuration
|
|
||||||
#define DEBUG_SERIAL_BAUD 115200
|
|
||||||
#define DEBUG_ENABLED true
|
|
||||||
|
|
||||||
#endif
|
|
||||||
Reference in New Issue
Block a user