diff --git a/pixelprojektor/NeoPatterns.cpp b/pixelprojektor/NeoPatterns.cpp new file mode 100644 index 0000000..de2697b --- /dev/null +++ b/pixelprojektor/NeoPatterns.cpp @@ -0,0 +1,708 @@ +#include "NeoPatterns.h" + +NeoPatterns::NeoPatterns(uint16_t pixels, uint8_t pin, uint8_t type, void (*callback)()) : + Adafruit_NeoPixel(pixels, pin, type) +{ + OnComplete = callback; + //Allocate a zero initialized block of memory big enough to hold "pixels" uint8_t. + pixelR = ( uint8_t* ) calloc( pixels, sizeof( uint8_t ) ); + pixelG = ( uint8_t* ) calloc( pixels, sizeof( uint8_t ) ); + pixelB = ( uint8_t* ) calloc( pixels, sizeof( uint8_t ) ); + pixelR_buffer = ( uint8_t* ) calloc( pixels, sizeof( uint8_t ) ); + pixelG_buffer = ( uint8_t* ) calloc( pixels, sizeof( uint8_t ) ); + pixelB_buffer = ( uint8_t* ) calloc( pixels, sizeof( uint8_t ) ); +} + +void NeoPatterns::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; + case RANDOM_FADE: + RandomFadeUpdate(); + break; + case RANDOM_FADE_SINGLE: + RandomFadeSingleUpdate(); + break; + case SMOOTH: + SmoothUpdate(); + break; + case ICON: + IconUpdate(); + break; + case PLASMA: + PlasmaUpdate(); + break; + case FILL: + break; + case RANDOM: + break; + case NONE: + break; + default: + break; + } + } else { + delay(1); + } +} + +void NeoPatterns::Increment() +{ + if (Direction == FORWARD) + { + Index++; + if (Index >= TotalSteps) + { + Index = 0; + if (OnComplete != NULL) + { + OnComplete(); // call the completion callback + } + } + } + else // Direction == REVERSE + { + --Index; + if (Index <= 0) + { + Index = TotalSteps - 1; + if (OnComplete != NULL) + { + OnComplete(); // call the completion callback + } + } + } +} + +void NeoPatterns::Reverse() { + if (Direction == FORWARD) + { + Direction = REVERSE; + Index = TotalSteps - 1; + } + else + { + Direction = FORWARD; + Index = 0; + } +} + +void NeoPatterns::None(uint8_t interval) { + Interval = interval; + if (ActivePattern != NONE) { + clear(); + show(); + } + ActivePattern = NONE; +} + +/****************** Effects ******************/ + +void NeoPatterns::RainbowCycle(uint8_t interval, direction dir) { + ActivePattern = RAINBOW_CYCLE; + Interval = interval; + TotalSteps = 255; + Index = 0; + Direction = dir; +} + +void NeoPatterns::RainbowCycleUpdate() +{ + for (int i = 0; i < numPixels(); i++) + { + setPixelColor(i, Wheel(((i * 256 / numPixels()) + Index) & 255)); + } + show(); + Increment(); +} + +void NeoPatterns::TheaterChase(uint32_t color1, uint32_t color2, uint8_t interval, direction dir) { + ActivePattern = THEATER_CHASE; + Interval = interval; + TotalSteps = numPixels(); + Color1 = color1; + Color2 = color2; + Index = 0; + Direction = dir; +} +void NeoPatterns::TheaterChaseUpdate() { + for (int i = 0; i < numPixels(); i++) + { + if ((i + Index) % 3 == 0) + { + setPixelColor(i, Color1); + } + else + { + setPixelColor(i, Color2); + } + } + show(); + Increment(); +} + +void NeoPatterns::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 NeoPatterns::ColorWipeUpdate() +{ + setPixelColor(Index, Color1); + show(); + Increment(); +} + +// Initialize for a SCANNNER +void NeoPatterns::Scanner(uint32_t color1, uint8_t interval, bool colorful, bool spiral) +{ + ActivePattern = SCANNER; + Interval = interval; + TotalSteps = (numPixels() - 1) * 2; + Color1 = color1; + Index = 0; + wPos = 0; + this->colorful = colorful; + this->spiral = spiral; +} + +// Update the Scanner Pattern +void NeoPatterns::ScannerUpdate() +{ + if (colorful) { + Color1 = Wheel(wPos); + if (wPos >= 255) { + wPos = 0; + } + else { + wPos++; + } + } + for (int i = 0; i < numPixels(); i++) + { + int finalpos; + if (spiral) { + finalpos = numToSpiralPos(i); + } + else + { + finalpos = i; + } + if (i == Index) // Scan Pixel to the right + { + setPixelColor(finalpos, Color1); + } + else if (i == TotalSteps - Index) // Scan Pixel to the left + { + setPixelColor(finalpos, Color1); + } + else // Fading tail + { + setPixelColor(finalpos, DimColor(getPixelColor(finalpos))); + } + } + show(); + Increment(); + +} + +void NeoPatterns::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 NeoPatterns::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(); +} + +void NeoPatterns::RandomFade(uint8_t interval ) { + ActivePattern = RANDOM_FADE; + Interval = interval; + TotalSteps = 255; + Index = 0; +} + +void NeoPatterns::RandomFadeUpdate() { + ColorSet(Wheel(Index)); + Increment(); +} + +void NeoPatterns::RandomFadeSingle(uint8_t interval, uint8_t speed) { + ActivePattern = RANDOM_FADE_SINGLE; + Interval = interval; + TotalSteps = 255; + Index = 0; + WheelSpeed = speed; + RandomBuffer(); +} + +void NeoPatterns::RandomFadeSingleUpdate() { + for (int i = 0; i < numPixels(); i++) { + pixelR_buffer[i] += random(0, random(0, WheelSpeed + 1) + 1); //use buffer red channel for color wheel + setPixelColor(i, Wheel(pixelR_buffer[i])); + } + show(); + Increment(); +} + +void NeoPatterns::RandomBuffer() +{ + for (int i = 0; i < numPixels(); i++) { + uint32_t c = Wheel(random(0, 256)); + pixelR_buffer[i] = (uint8_t)(c >> 16); + pixelG_buffer[i] = (uint8_t)(c >> 8); + pixelB_buffer[i] = (uint8_t)c; + } +} + +void NeoPatterns::Random() +{ + None(); // Stop all other effects + ActivePattern = RANDOM; + for (int i = 0; i < numPixels(); i++) { + setPixelColor(i, Wheel(random(0, 256))); + } + show(); +} + +void NeoPatterns::Smooth(uint8_t wheelSpeed, uint8_t smoothing, uint8_t strength, uint8_t interval) { + ActivePattern = SMOOTH; + Interval = interval; + Index = 0; + WheelSpeed = wheelSpeed; + Smoothing = smoothing; + Strength = strength; + movingPoint_x = 3; + movingPoint_y = 3; + // Clear buffer (from previous or different effects) + for (int i = 0; i < numPixels(); i++) { + pixelR_buffer[i] = 0; + pixelG_buffer[i] = 0; + pixelB_buffer[i] = 0; + } +} + +void NeoPatterns::SmoothUpdate() { + uint32_t c = Wheel(wPos); + wPosSlow += WheelSpeed; + wPos = (wPos + (wPosSlow / 10) ) % 255; + wPosSlow = wPosSlow % 16; + + uint8_t r = (uint8_t)(c >> 16); + uint8_t g = (uint8_t)(c >> 8); + uint8_t b = (uint8_t)c; + + movingPoint_x = movingPoint_x + 8 + random(-random(0, 1 + 1), random(0, 1 + 1) + 1); + movingPoint_y = movingPoint_y + 8 + random(-random(0, 1 + 1), random(0, 1 + 1) + 1); + if (movingPoint_x < 8) { + movingPoint_x = 8 - movingPoint_x; + } else if (movingPoint_x >= 16) { + movingPoint_x = 22 - movingPoint_x; + } else { + movingPoint_x -= 8; + } + + if (movingPoint_y < 8) { + movingPoint_y = 8 - movingPoint_y; + } else if (movingPoint_y >= 16) { + movingPoint_y = 22 - movingPoint_y; + } else { + movingPoint_y -= 8; + } + uint8_t startx = movingPoint_x; + uint8_t starty = movingPoint_y; + + for (int i = 0; i < Strength; i++) { + + movingPoint_x = startx + 8 + random(-random(0, 2 + 1), random(0, 2 + 1) + 1); + movingPoint_y = starty + 8 + random(-random(0, 2 + 1), random(0, 2 + 1) + 1); + + if (movingPoint_x < 8) { + movingPoint_x = 8 - movingPoint_x; + } else if (movingPoint_x >= 16) { + movingPoint_x = 22 - movingPoint_x; + } else { + movingPoint_x -= 8; + } + + if (movingPoint_y < 8) { + movingPoint_y = 8 - movingPoint_y; + } else if (movingPoint_y >= 16) { + movingPoint_y = 22 - movingPoint_y; + } else { + movingPoint_y -= 8; + } + + if (pixelR[xyToPos(movingPoint_x, movingPoint_y)] < r) { + pixelR[xyToPos(movingPoint_x, movingPoint_y)]++; + } else if (pixelR[xyToPos(movingPoint_x, movingPoint_y)] > r) { + pixelR[xyToPos(movingPoint_x, movingPoint_y)]--; + } + if (pixelG[xyToPos(movingPoint_x, movingPoint_y)] < g) { + pixelG[xyToPos(movingPoint_x, movingPoint_y)]++; + } else if (pixelG[xyToPos(movingPoint_x, movingPoint_y)] > g) { + pixelG[xyToPos(movingPoint_x, movingPoint_y)]--; + } + if (pixelB[xyToPos(movingPoint_x, movingPoint_y)] < b) { + pixelB[xyToPos(movingPoint_x, movingPoint_y)]++; + } else if (pixelB[xyToPos(movingPoint_x, movingPoint_y)] > b) { + pixelB[xyToPos(movingPoint_x, movingPoint_y)]--; + } + } + + movingPoint_x = startx; + movingPoint_y = starty; + + for (int i = 0; i < numPixels(); i++) { + pixelR_buffer[i] = (Smoothing / 100.0) * pixelR[i] + (1.0 - (Smoothing / 100.0)) * getAverage(pixelR, i, 0, 0); + pixelG_buffer[i] = (Smoothing / 100.0) * pixelG[i] + (1.0 - (Smoothing / 100.0)) * getAverage(pixelG, i, 0, 0); + pixelB_buffer[i] = (Smoothing / 100.0) * pixelB[i] + (1.0 - (Smoothing / 100.0)) * getAverage(pixelB, i, 0, 0); + + } + + for (int i = 0; i < numPixels(); i++) { + pixelR[i] = pixelR_buffer[i]; + pixelG[i] = pixelG_buffer[i]; + pixelB[i] = pixelB_buffer[i]; + setPixelColor(i, pixelR[i], pixelG[i], pixelB[i]); + } + + show(); +} + + +/****************** Icon ******************/ + +void NeoPatterns::Icon(uint8_t fontchar, String iconcolor, uint8_t interval) +{ + // Save last effect, should be called after completion again + SavedPattern = ActivePattern; + SavedInterval = Interval; + SavedTotalSteps = TotalSteps; + SavedIndex = Index; + SavedColor1 = Color1; + SavedDirection = Direction; + SavedPlasmaPhase = PlasmaPhase; + SavedPlasmaPhaseIncrement = PlasmaPhaseIncrement; + SavedPlasmaColorStretch = PlasmaColorStretch; + ActivePattern = ICON; + Interval = interval; + TotalSteps = 80; + Index = 80; + Color1 = parseColor(iconcolor); + FontChar = fontchar; + Direction = REVERSE; +} + +void NeoPatterns::IconUpdate() +{ + for (int i = 0; i < numPixels(); i++) { + uint64_t mask = 1LL << (uint64_t)i; + + if ( (font[FontChar]&mask) == 0) { + setPixelColor(numToPos(i), Color(0, 0, 0)); //bit is 0 at pos i + } else { + float _brightness = 1.0 - ( (TotalSteps - Index) * 1.0 / TotalSteps ); + uint8_t _r = (uint8_t)(Color1 >> 16); + uint8_t _g = (uint8_t)(Color1 >> 8); + uint8_t _b = (uint8_t)Color1; + setPixelColor(numToPos(i), Color(_r * _brightness, _g * _brightness, _b * _brightness)); //bit is 1 at pos i + } + } + show(); + Increment(); +} + +void NeoPatterns::IconComplete() +{ + // Reload last effect + ActivePattern = SavedPattern; + Interval = SavedInterval; + TotalSteps = SavedTotalSteps; + Index = SavedIndex; + Color1 = SavedColor1; + Direction = SavedDirection; + PlasmaPhase = SavedPlasmaPhase; + PlasmaPhaseIncrement = SavedPlasmaPhaseIncrement; + PlasmaColorStretch = SavedPlasmaColorStretch; +} + +// Based upon https://github.com/johncarl81/neopixelplasma +void NeoPatterns::Plasma(float phase, float phaseIncrement, float colorStretch, uint8_t interval) +{ + ActivePattern = PLASMA; + Interval = interval; + PlasmaPhase = phase; + PlasmaPhaseIncrement = phaseIncrement; + PlasmaColorStretch = colorStretch; +} + +void NeoPatterns::PlasmaUpdate() +{ + PlasmaPhase += PlasmaPhaseIncrement; + int edge = (int)sqrt(numPixels()); + // The two points move along Lissajious curves, see: http://en.wikipedia.org/wiki/Lissajous_curve + // The sin() function returns values in the range of -1.0..1.0, so scale these to our desired ranges. + // The phase value is multiplied by various constants; I chose these semi-randomly, to produce a nice motion. + Point p1 = { (sin(PlasmaPhase * 1.000) + 1.0) * (edge / 2), (sin(PlasmaPhase * 1.310) + 1.0) * (edge / 2) }; + Point p2 = { (sin(PlasmaPhase * 1.770) + 1.0) * (edge / 2), (sin(PlasmaPhase * 2.865) + 1.0) * (edge / 2) }; + Point p3 = { (sin(PlasmaPhase * 0.250) + 1.0) * (edge / 2), (sin(PlasmaPhase * 0.750) + 1.0) * (edge / 2)}; + + byte row, col; + + // For each row... + for ( row = 0; row < edge; row++ ) { + float row_f = float(row); // Optimization: Keep a floating point value of the row number, instead of recasting it repeatedly. + + // For each column... + for ( col = 0; col < edge; col++ ) { + float col_f = float(col); // Optimization. + + // Calculate the distance between this LED, and p1. + Point dist1 = { col_f - p1.x, row_f - p1.y }; // The vector from p1 to this LED. + float distance1 = sqrt( dist1.x * dist1.x + dist1.y * dist1.y ); + + // Calculate the distance between this LED, and p2. + Point dist2 = { col_f - p2.x, row_f - p2.y }; // The vector from p2 to this LED. + float distance2 = sqrt( dist2.x * dist2.x + dist2.y * dist2.y ); + + // Calculate the distance between this LED, and p3. + Point dist3 = { col_f - p3.x, row_f - p3.y }; // The vector from p3 to this LED. + float distance3 = sqrt( dist3.x * dist3.x + dist3.y * dist3.y ); + + // Warp the distance with a sin() function. As the distance value increases, the LEDs will get light,dark,light,dark,etc... + // You can use a cos() for slightly different shading, or experiment with other functions. Go crazy! + float color_1 = distance1; // range: 0.0...1.0 + float color_2 = distance2; + float color_3 = distance3; + float color_4 = (sin( distance1 * distance2 * PlasmaColorStretch )) + 2.0 * 0.5; + + // Square the color_f value to weight it towards 0. The image will be darker and have higher contrast. + color_1 *= color_1 * color_4; + color_2 *= color_2 * color_4; + color_3 *= color_3 * color_4; + color_4 *= color_4; + + // Scale the color up to 0..7 . Max brightness is 7. + //strip.setPixelColor(col + (edge * row), strip.Color(color_4, 0, 0) ); + setPixelColor(col + (edge * row), Color(color_1, color_2, color_3)); + } + } + show(); +} + + +/****************** Helper functions ******************/ + +void NeoPatterns::SetColor1(uint32_t color) { + Color1 = color; +} +void NeoPatterns::SetColor2(uint32_t color) { + Color2 = color; +} + +// Calculate 50% dimmed version of a color (used by ScannerUpdate) +uint32_t NeoPatterns::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 NeoPatterns::ColorSet(uint32_t color) +{ + for (int i = 0; i < numPixels(); i++) + { + setPixelColor(i, color); + } + show(); +} + +void NeoPatterns::ColorSetParameters(String parameters) +{ + None(); + ActivePattern = FILL; + ColorSet(parseColor(parameters)); +} + +// Returns the Red component of a 32-bit color +uint8_t NeoPatterns::Red(uint32_t color) +{ + return (color >> 16) & 0xFF; +} + +// Returns the Green component of a 32-bit color +uint8_t NeoPatterns::Green(uint32_t color) +{ + return (color >> 8) & 0xFF; +} + +// Returns the Blue component of a 32-bit color +uint8_t NeoPatterns::Blue(uint32_t color) +{ + return color & 0xFF; +} + +// Input a value 0 to 255 to get a color value. +// The colors are a transition r - g - b - back to r. +uint32_t NeoPatterns::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); + } +} + +// Convert x y pixel position to matrix position +uint8_t NeoPatterns::xyToPos(int x, int y) { + if (y % 2 == 0) { + return (y * 8 + x); + } else { + return (y * 8 + (7 - x)); + } +} + +//convert pixel number to actual 8x8 matrix position +uint8_t NeoPatterns::numToPos(int num) { + int x = num % 8; + int y = num / 8; + return xyToPos(x, y); +} + +// Convert pixel number to actual 8x8 matrix position in a spiral +uint8_t NeoPatterns::numToSpiralPos(int num) { + int edge = (int)sqrt(numPixels()); + int findx = edge - 1; // 7 + int findy = 0; + int stepsize = edge - 1; // initial value (0..7) + int stepnumber = 0; // each "step" should be used twice + int count = -1; + int dir = 1; // direction: 0 = incX, 1=incY, 2=decX, 3=decY + if (num < edge) { + return num; // trivial + } + for (int i = edge; i <= num; i++) + { + count++; + if (count == stepsize) { + count = 0; + // Change direction + dir++; + stepnumber++; + if (stepnumber == 2) { + stepsize -= 1; + stepnumber = 0; + } + if (dir == 4) { + dir = 0; + } + } + switch (dir) { + case 0: + findx++; + break; + case 1: + findy++; + break; + case 2: + findx--; + break; + case 3: + findy--; + break; + } + } + return xyToPos(findx, findy); +} + +uint8_t NeoPatterns::getAverage(uint8_t array[], uint8_t i, int x, int y) +{ + // TODO: This currently works only with 8x8 (64 pixel)! + uint16_t sum = 0; + uint8_t count = 0; + if (i >= 8) { //up + sum += array[i - 8]; + count++; + } + if (i < (64 - 8)) { //down + sum += array[i + 8]; + count++; + } + if (i >= 1) { //left + sum += array[i - 1]; + count++; + } + if (i < (64 - 1)) { //right + sum += array[i + 1]; + count++; + } + return sum / count; +} + +uint32_t NeoPatterns::parseColor(String value) { + if (value.charAt(0) == '#') { //solid fill + String color = value.substring(1); + int number = (int) strtol( &color[0], NULL, 16); + // Split them up into r, g, b values + int r = number >> 16; + int g = number >> 8 & 0xFF; + int b = number & 0xFF; + return Color(r, g, b); + } + return 0; +} + + + + diff --git a/pixelprojektor/NeoPatterns.h b/pixelprojektor/NeoPatterns.h new file mode 100644 index 0000000..bd02db2 --- /dev/null +++ b/pixelprojektor/NeoPatterns.h @@ -0,0 +1,113 @@ +#include +#include "font.h" + +// Pattern types supported: +enum pattern { NONE, RAINBOW_CYCLE, THEATER_CHASE, COLOR_WIPE, SCANNER, FADE, RANDOM_FADE, SMOOTH, ICON, RANDOM_FADE_SINGLE, PLASMA, FILL, RANDOM }; +// Patern directions supported: +enum direction { FORWARD, REVERSE }; + +class NeoPatterns : public Adafruit_NeoPixel +{ + public: + NeoPatterns(uint16_t pixels, uint8_t pin, uint8_t type, void (*callback)()); + + void Update(); + + void Reverse(); + void None(uint8_t interval = 40); + void RainbowCycle(uint8_t interval, direction dir = FORWARD); + void RainbowCycleUpdate(); + void TheaterChase(uint32_t color1, uint32_t color2, uint8_t interval, direction dir = FORWARD); + void TheaterChaseUpdate(); + void ColorWipe(uint32_t color, uint8_t interval, direction dir = FORWARD); + void ColorWipeUpdate(); + void Scanner(uint32_t color1 = 16711680, uint8_t interval = 40, bool colorful = false, bool spiral = false); + void ScannerUpdate(); + void Fade(uint32_t color1, uint32_t color2, uint16_t steps, uint8_t interval, direction dir = FORWARD); + void FadeUpdate(); + void RandomFade(uint8_t interval = 100); + void RandomFadeUpdate(); + void RandomFadeSingle(uint8_t interval = 100, uint8_t speed = 5); + void RandomFadeSingleUpdate(); + void RandomBuffer(); + void Random(); + void Smooth(uint8_t wheelSpeed = 16, uint8_t smoothing = 80, uint8_t strength = 50, uint8_t interval = 40); + void SmoothUpdate(); + void Icon(uint8_t fontchar, String iconcolor = "#FFFFFF", uint8_t interval = 30); + void IconUpdate(); + void IconComplete(); + void Plasma(float phase = 0, float phaseIncrement = 0.03, float colorStretch = 0.3, uint8_t interval = 60); // 0.08 and 0.11 + void PlasmaUpdate(); + + void SetColor1(uint32_t color); + void SetColor2(uint32_t color); + //Utilities + void ColorSet(uint32_t color); + void ColorSetParameters(String parameters); + uint8_t Red(uint32_t color); + uint8_t Green(uint32_t color); + uint8_t Blue(uint32_t color); + uint32_t Wheel(byte WheelPos); + uint8_t numToSpiralPos(int num); + uint8_t xyToPos(int x, int y); + uint8_t numToPos(int num); + uint8_t getAverage(uint8_t array[], uint8_t i, int x, int y); + uint32_t parseColor(String value); + private: + + // Member Variables: + pattern ActivePattern; // which pattern is running + pattern SavedPattern; + direction Direction; // direction to run the pattern + direction SavedDirection; + + unsigned long Interval; // milliseconds between updates + unsigned long SavedInterval; + unsigned long lastUpdate; // last update of position + + uint32_t Color1, Color2; // What colors are in use + uint32_t SavedColor1; + uint16_t TotalSteps; // total number of steps in the pattern + uint16_t SavedTotalSteps; + uint16_t Index; // current step within the pattern + uint16_t SavedIndex; + uint8_t Every; // Turn every "Every" pixel in Color1/Color2 + + byte wPos; + bool colorful; + bool spiral; + uint8_t wPosSlow; + uint8_t WheelSpeed; + uint8_t Smoothing; + uint8_t Strength; + uint8_t movingPoint_x; + uint8_t movingPoint_y; + uint8_t *pixelR; + uint8_t *pixelG; + uint8_t *pixelB; + uint8_t *pixelR_buffer; + uint8_t *pixelG_buffer; + uint8_t *pixelB_buffer; + + uint8_t FontChar; + + float PlasmaPhase; + float SavedPlasmaPhase; + float PlasmaPhaseIncrement; + float SavedPlasmaPhaseIncrement; + float PlasmaColorStretch; + float SavedPlasmaColorStretch; + + + uint32_t DimColor(uint32_t color); + void Increment(); + void (*OnComplete)(); // Callback on completion of pattern + + // Convenient 2D point structure + struct Point { + float x; + float y; + }; + + +}; diff --git a/pixelprojektor/font.h b/pixelprojektor/font.h new file mode 100644 index 0000000..9b2094c --- /dev/null +++ b/pixelprojektor/font.h @@ -0,0 +1,150 @@ + +/* the values in this array are a 8x8 bitmap font for ascii characters */ +static uint64_t font[128] = { + /************************************************************************ + font.c + Copyright (C) Lisa Milne 2014 + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see + ************************************************************************/ + 0x7E7E7E7E7E7E0000, /* NUL */ + 0x7E7E7E7E7E7E0000, /* SOH */ + 0x7E7E7E7E7E7E0000, /* STX */ + 0x7E7E7E7E7E7E0000, /* ETX */ + 0x7E7E7E7E7E7E0000, /* EOT */ + 0x7E7E7E7E7E7E0000, /* ENQ */ + 0x7E7E7E7E7E7E0000, /* ACK */ + 0x7E7E7E7E7E7E0000, /* BEL */ + 0x7E7E7E7E7E7E0000, /* BS */ + 0x0, /* TAB */ + 0x7E7E7E7E7E7E0000, /* LF */ + 0x7E7E7E7E7E7E0000, /* VT */ + 0x7E7E7E7E7E7E0000, /* FF */ + 0x7E7E7E7E7E7E0000, /* CR */ + 0x7E7E7E7E7E7E0000, /* SO */ + 0x7E7E7E7E7E7E0000, /* SI */ + 0x7E7E7E7E7E7E0000, /* DLE */ + 0x7E7E7E7E7E7E0000, /* DC1 */ + 0x7E7E7E7E7E7E0000, /* DC2 */ + 0x7E7E7E7E7E7E0000, /* DC3 */ + 0x7E7E7E7E7E7E0000, /* DC4 */ + 0x7E7E7E7E7E7E0000, /* NAK */ + 0x7E7E7E7E7E7E0000, /* SYN */ + 0x7E7E7E7E7E7E0000, /* ETB */ + 0x7E7E7E7E7E7E0000, /* CAN */ + 0x7E7E7E7E7E7E0000, /* EM */ + 0x7E7E7E7E7E7E0000, /* SUB */ + 0x7E7E7E7E7E7E0000, /* ESC */ + 0x7E7E7E7E7E7E0000, /* FS */ + 0x7E7E7E7E7E7E0000, /* GS */ + 0x7E7E7E7E7E7E0000, /* RS */ + 0x7E7E7E7E7E7E0000, /* US */ + 0x0, /* (space) */ + 0x808080800080000, /* ! */ + 0x2828000000000000, /* " */ + 0x287C287C280000, /* # */ + 0x81E281C0A3C0800, /* $ */ + 0x6094681629060000, /* % */ + 0x1C20201926190000, /* & */ + 0x808000000000000, /* ' */ + 0x810202010080000, /* ( */ + 0x1008040408100000, /* ) */ + 0x2A1C3E1C2A000000, /* * */ + 0x8083E08080000, /* + */ + 0x81000, /* , */ + 0x3C00000000, /* - */ + 0x80000, /* . */ + 0x204081020400000, /* / */ + 0x1824424224180000, /* 0 */ + 0x8180808081C0000, /* 1 */ + 0x3C420418207E0000, /* 2 */ + 0x3C420418423C0000, /* 3 */ + 0x81828487C080000, /* 4 */ + 0x7E407C02423C0000, /* 5 */ + 0x3C407C42423C0000, /* 6 */ + 0x7E04081020400000, /* 7 */ + 0x3C423C42423C0000, /* 8 */ + 0x3C42423E023C0000, /* 9 */ + 0x80000080000, /* : */ + 0x80000081000, /* ; */ + 0x6186018060000, /* < */ + 0x7E007E000000, /* = */ + 0x60180618600000, /* > */ + 0x3844041800100000, /* ? */ + 0x3C449C945C201C, /* @ */ + 0x1818243C42420000, /* A */ + 0x7844784444780000, /* B */ + 0x3844808044380000, /* C */ + 0x7844444444780000, /* D */ + 0x7C407840407C0000, /* E */ + 0x7C40784040400000, /* F */ + 0x3844809C44380000, /* G */ + 0x42427E4242420000, /* H */ + 0x3E080808083E0000, /* I */ + 0x1C04040444380000, /* J */ + 0x4448507048440000, /* K */ + 0x40404040407E0000, /* L */ + 0x4163554941410000, /* M */ + 0x4262524A46420000, /* N */ + 0x1C222222221C0000, /* O */ + 0x7844784040400000, /* P */ + 0x1C222222221C0200, /* Q */ + 0x7844785048440000, /* R */ + 0x1C22100C221C0000, /* S */ + 0x7F08080808080000, /* T */ + 0x42424242423C0000, /* U */ + 0x8142422424180000, /* V */ + 0x4141495563410000, /* W */ + 0x4224181824420000, /* X */ + 0x4122140808080000, /* Y */ + 0x7E040810207E0000, /* Z */ + 0x3820202020380000, /* [ */ + 0x4020100804020000, /* */ + 0x3808080808380000, /* ] */ + 0x1028000000000000, /* ^ */ + 0x7E0000, /* _ */ + 0x1008000000000000, /* ` */ + 0x3C023E463A0000, /* a */ + 0x40407C42625C0000, /* b */ + 0x1C20201C0000, /* c */ + 0x2023E42463A0000, /* d */ + 0x3C427E403C0000, /* e */ + 0x18103810100000, /* f */ + 0x344C44340438, /* g */ + 0x2020382424240000, /* h */ + 0x800080808080000, /* i */ + 0x800180808080870, /* j */ + 0x20202428302C0000, /* k */ + 0x1010101010180000, /* l */ + 0x665A42420000, /* m */ + 0x2E3222220000, /* n */ + 0x3C42423C0000, /* o */ + 0x5C62427C4040, /* p */ + 0x3A46423E0202, /* q */ + 0x2C3220200000, /* r */ + 0x1C201804380000, /* s */ + 0x103C1010180000, /* t */ + 0x2222261A0000, /* u */ + 0x424224180000, /* v */ + 0x81815A660000, /* w */ + 0x422418660000, /* x */ + 0x422214081060, /* y */ + 0x3C08103C0000, /* z */ + 0x1C103030101C0000, /* { */ + 0x808080808080800, /* | */ + 0x38080C0C08380000, /* } */ + 0x324C000000, /* ~ */ + 0x7E7E7E7E7E7E0000 /* DEL */ +}; + diff --git a/pixelprojektor/pixelprojektor.ino b/pixelprojektor/pixelprojektor.ino index dbeb8fd..6079a5c 100644 --- a/pixelprojektor/pixelprojektor.ino +++ b/pixelprojektor/pixelprojektor.ino @@ -1,677 +1,230 @@ -#include -// homie lib from: https://github.com/marvinroger/homie-esp8266/ +#include #include +#include +#include "NeoPatterns.h" #ifdef __AVR__ - #include +#include #endif -#define PIN 2 //data pin for ws2812 - +#define PIN 2 //data pin for ws2812 (pixelprojektor @ ctdo: PIN 2) #define NUMPIXELS 64 -#define FPS 15 +NeoPatterns strip = NeoPatterns(NUMPIXELS, PIN, NEO_GRB + NEO_KHZ800, &StripComplete); -uint8_t effect=0; -#define EFFECT_NONE 0 -#define EFFECT_SMOOTH 1 -uint8_t movingPoint_x=3; -uint8_t movingPoint_y=3; -uint8_t wheelPos=0; -uint8_t wheelPosSlow=0; //for slower wheelPos increment than 1 -int wheelSpeed=16; //16=+1/frame -int smoothing=80; //0 to 100. 100=no change (ultrasmooth), 0=no smoothing. -int strength=50; //how much pixels to apply color to -#define EFFECT_SPIRAL 2 -#define EFFECT_RANDOMFADE 3 -int fadespeedmax=5; //1 to 255 +bool stopAfterCompletion; -int iconCountStart=0; //for percentage calculation -int iconCountdown=0; //0=off -uint8_t iconchar=0; //last displayed char -int32_t iconcolor=0; //last icon color - - - - - - - -/* the values in this array are a 8x8 bitmap font for ascii characters */ -static uint64_t font[128] = { - /************************************************************************ -* font.c -* Copyright (C) Lisa Milne 2014 -* -* This program is free software: you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation, either version 3 of the License, or -* (at your option) any later version. -* -* This program is distributed in the hope that it will be useful, but -* WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -* See the GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program. If not, see -************************************************************************/ - 0x7E7E7E7E7E7E0000, /* NUL */ - 0x7E7E7E7E7E7E0000, /* SOH */ - 0x7E7E7E7E7E7E0000, /* STX */ - 0x7E7E7E7E7E7E0000, /* ETX */ - 0x7E7E7E7E7E7E0000, /* EOT */ - 0x7E7E7E7E7E7E0000, /* ENQ */ - 0x7E7E7E7E7E7E0000, /* ACK */ - 0x7E7E7E7E7E7E0000, /* BEL */ - 0x7E7E7E7E7E7E0000, /* BS */ - 0x0, /* TAB */ - 0x7E7E7E7E7E7E0000, /* LF */ - 0x7E7E7E7E7E7E0000, /* VT */ - 0x7E7E7E7E7E7E0000, /* FF */ - 0x7E7E7E7E7E7E0000, /* CR */ - 0x7E7E7E7E7E7E0000, /* SO */ - 0x7E7E7E7E7E7E0000, /* SI */ - 0x7E7E7E7E7E7E0000, /* DLE */ - 0x7E7E7E7E7E7E0000, /* DC1 */ - 0x7E7E7E7E7E7E0000, /* DC2 */ - 0x7E7E7E7E7E7E0000, /* DC3 */ - 0x7E7E7E7E7E7E0000, /* DC4 */ - 0x7E7E7E7E7E7E0000, /* NAK */ - 0x7E7E7E7E7E7E0000, /* SYN */ - 0x7E7E7E7E7E7E0000, /* ETB */ - 0x7E7E7E7E7E7E0000, /* CAN */ - 0x7E7E7E7E7E7E0000, /* EM */ - 0x7E7E7E7E7E7E0000, /* SUB */ - 0x7E7E7E7E7E7E0000, /* ESC */ - 0x7E7E7E7E7E7E0000, /* FS */ - 0x7E7E7E7E7E7E0000, /* GS */ - 0x7E7E7E7E7E7E0000, /* RS */ - 0x7E7E7E7E7E7E0000, /* US */ - 0x0, /* (space) */ - 0x808080800080000, /* ! */ - 0x2828000000000000, /* " */ - 0x287C287C280000, /* # */ - 0x81E281C0A3C0800, /* $ */ - 0x6094681629060000, /* % */ - 0x1C20201926190000, /* & */ - 0x808000000000000, /* ' */ - 0x810202010080000, /* ( */ - 0x1008040408100000, /* ) */ - 0x2A1C3E1C2A000000, /* * */ - 0x8083E08080000, /* + */ - 0x81000, /* , */ - 0x3C00000000, /* - */ - 0x80000, /* . */ - 0x204081020400000, /* / */ - 0x1824424224180000, /* 0 */ - 0x8180808081C0000, /* 1 */ - 0x3C420418207E0000, /* 2 */ - 0x3C420418423C0000, /* 3 */ - 0x81828487C080000, /* 4 */ - 0x7E407C02423C0000, /* 5 */ - 0x3C407C42423C0000, /* 6 */ - 0x7E04081020400000, /* 7 */ - 0x3C423C42423C0000, /* 8 */ - 0x3C42423E023C0000, /* 9 */ - 0x80000080000, /* : */ - 0x80000081000, /* ; */ - 0x6186018060000, /* < */ - 0x7E007E000000, /* = */ - 0x60180618600000, /* > */ - 0x3844041800100000, /* ? */ - 0x3C449C945C201C, /* @ */ - 0x1818243C42420000, /* A */ - 0x7844784444780000, /* B */ - 0x3844808044380000, /* C */ - 0x7844444444780000, /* D */ - 0x7C407840407C0000, /* E */ - 0x7C40784040400000, /* F */ - 0x3844809C44380000, /* G */ - 0x42427E4242420000, /* H */ - 0x3E080808083E0000, /* I */ - 0x1C04040444380000, /* J */ - 0x4448507048440000, /* K */ - 0x40404040407E0000, /* L */ - 0x4163554941410000, /* M */ - 0x4262524A46420000, /* N */ - 0x1C222222221C0000, /* O */ - 0x7844784040400000, /* P */ - 0x1C222222221C0200, /* Q */ - 0x7844785048440000, /* R */ - 0x1C22100C221C0000, /* S */ - 0x7F08080808080000, /* T */ - 0x42424242423C0000, /* U */ - 0x8142422424180000, /* V */ - 0x4141495563410000, /* W */ - 0x4224181824420000, /* X */ - 0x4122140808080000, /* Y */ - 0x7E040810207E0000, /* Z */ - 0x3820202020380000, /* [ */ - 0x4020100804020000, /* */ - 0x3808080808380000, /* ] */ - 0x1028000000000000, /* ^ */ - 0x7E0000, /* _ */ - 0x1008000000000000, /* ` */ - 0x3C023E463A0000, /* a */ - 0x40407C42625C0000, /* b */ - 0x1C20201C0000, /* c */ - 0x2023E42463A0000, /* d */ - 0x3C427E403C0000, /* e */ - 0x18103810100000, /* f */ - 0x344C44340438, /* g */ - 0x2020382424240000, /* h */ - 0x800080808080000, /* i */ - 0x800180808080870, /* j */ - 0x20202428302C0000, /* k */ - 0x1010101010180000, /* l */ - 0x665A42420000, /* m */ - 0x2E3222220000, /* n */ - 0x3C42423C0000, /* o */ - 0x5C62427C4040, /* p */ - 0x3A46423E0202, /* q */ - 0x2C3220200000, /* r */ - 0x1C201804380000, /* s */ - 0x103C1010180000, /* t */ - 0x2222261A0000, /* u */ - 0x424224180000, /* v */ - 0x81815A660000, /* w */ - 0x422418660000, /* x */ - 0x422214081060, /* y */ - 0x3C08103C0000, /* z */ - 0x1C103030101C0000, /* { */ - 0x808080808080800, /* | */ - 0x38080C0C08380000, /* } */ - 0x324C000000, /* ~ */ - 0x7E7E7E7E7E7E0000 /* DEL */ -}; - - -Adafruit_NeoPixel strip = Adafruit_NeoPixel(NUMPIXELS, PIN, NEO_GRB + NEO_KHZ800); +void StripComplete() { + if (stopAfterCompletion) + { + strip.IconComplete(); + } + return; +} HomieNode homieNode("pixel", "commands"); -uint8_t pixelR[NUMPIXELS]; -uint8_t pixelG[NUMPIXELS]; -uint8_t pixelB[NUMPIXELS]; -//write to buffer, flip with showBuffer() -uint8_t pixelR_buffer[NUMPIXELS]; -uint8_t pixelG_buffer[NUMPIXELS]; -uint8_t pixelB_buffer[NUMPIXELS]; -long lastMillis=0; -long fpsdelay=1000/FPS; - -int xyToPos(int x, int y){ //convert x y pixel position to matrix position - if (y%2==0){ - return (y*8+x); - }else{ - return (y*8+(7-x)); - } +bool onSetColor(const HomieRange& range, const String& value) { + if (!range.isRange || range.index < 0 || range.index > 1) { + return false; + } + switch (range.index) { + case 0: + strip.SetColor1(value.toInt()); + break; + case 1: + strip.SetColor2(value.toInt()); + break; + } + homieNode.setProperty("color_" + String(range.index)).send(value); } -int numToPos(int num){ //convert pixel number to actual 8x8 matrix position - int x=num%8; - int y=num/8; - return xyToPos(x,y); -} - -uint32_t wheel(byte WheelPos) { - WheelPos = 255 - WheelPos; - if(WheelPos < 85) { - return strip.Color(255 - WheelPos * 3, 0, WheelPos * 3); +bool onSetPixel(const HomieRange& range, const String& value) { + if (!range.isRange) { + strip.None(); + strip.ColorSet(value.toInt()); + homieNode.setProperty("pixel").send(value); + return true; } - if(WheelPos < 170) { - WheelPos -= 85; - return strip.Color(0, WheelPos * 3, 255 - WheelPos * 3); - } - WheelPos -= 170; - return strip.Color(WheelPos * 3, 255 - WheelPos * 3, 0); -} - - -void led_fill(uint32_t c) -{ - for (int i=0; i < strip.numPixels(); i++) { - strip.setPixelColor(i, c); + if (range.index < 0 || range.index > strip.numPixels() - 1) { + return false; } + strip.None(); + strip.setPixelColor(range.index, value.toInt()); strip.show(); + homieNode.setProperty("pixel_" + String(range.index)).send(value); } -void led_random() -{ - for (int i=0; i < strip.numPixels(); i++) { - strip.setPixelColor(i, wheel(random(0,255))); +bool onSetBrightness(const HomieRange& range, const String& value) { + long brightness = value.toInt(); + if (brightness < 0 || brightness > 255) { + return false; } + strip.setBrightness(brightness); strip.show(); + homieNode.setProperty("brightness").send(value); } - -void showBuffer() -{ - for (int i=0; i < strip.numPixels(); i++) { - pixelR[i]=pixelR_buffer[i]; - pixelG[i]=pixelG_buffer[i]; - pixelB[i]=pixelB_buffer[i]; - strip.setPixelColor(i, pixelR[i], pixelG[i], pixelB[i]); +bool onSetEffect(const HomieRange& range, const String& value) { + stopAfterCompletion = false; + String effect = value; + effect.toLowerCase(); + if (effect == "scanner") { + strip.Scanner(strip.Color(255, 0, 0)); } + else if (effect == "randomscanner") { + strip.Scanner(strip.Color(255, 0, 0), 40, true); + } + else if (effect == "larsonspiral") { + strip.Scanner(strip.Color(255, 0, 0), 40, true, true); + } + else if (effect == "rainbowcycle") { + strip.RainbowCycle(50); + } + else if (effect == "theaterchase" || effect == "chase") { + strip.TheaterChase(strip.Color(255, 0, 0), strip.Color(0, 0, 255), 100); + } + else if (effect == "fade") { + strip.Fade(strip.Color(255, 0, 0), strip.Color(0, 0, 255), 200, 100); + } + else if (effect == "randomfade") { + strip.RandomFade(); + } + else if (effect == "random") { + strip.Random(); + } + else if (effect == "smooth") { //example: smooth|[wheelspeed]|[smoothing]|[strength] wheelspeed=1-255, smoothing=0-100, strength=1-255 + strip.Smooth(16, 80, 50, 40); + } + else if (effect == "plasma") { + strip.Plasma(); + } + else { + // Test whether command with parameters was sent + int sep = value.indexOf("|"); + String command = value.substring(0, sep); + String parameters = value.substring(sep + 1); + if (command.equals("fill")) { + strip.ColorSetParameters(parameters); + } + else if (command.equals("randomfade")) { + int sepparam = parameters.indexOf("|"); + int p1 = parameters.substring(0, sepparam).toInt(); + if (p1 <= 0) { + p1 = 5; + } + strip.RandomFadeSingle(p1); + } + else { + strip.None(); + } + } + homieNode.setProperty("effect").send(value); +} + +bool onSetIcon(const HomieRange& range, const String& value) { + stopAfterCompletion = true; + String _iconname = value; + if (value[0] == '#') { //color given + strip.Icon(value.substring(7)[0], value.substring(0, 6)); + } + else { + strip.Icon(value[0]); + } + homieNode.setProperty("icon").send(value); +} + +bool onSetClear(const HomieRange& range, const String& value) { + strip.None(); + strip.clear(); strip.show(); + homieNode.setProperty("clear").send(value); } -uint8_t getAverage(uint8_t array[NUMPIXELS], uint8_t i, int x, int y) -{ - uint16_t sum=0; - uint8_t count=0; - if (i>=8){ //up - sum+=array[i-8]; - count++; - } - if (i<(64-8)){ //down - sum+=array[i+8]; - count++; - } - if (i>=1){ //left - sum+=array[i-1]; - count++; - } - if (i<(64-1)){ //right - sum+=array[i+1]; - count++; - } - - /* - if (i>=(8+1)){ //up left - sum+=array[i-8-1]; - count++; - } - if (i<(64-8-1)){ //down left - sum+=array[i+8-1]; - count++; - } - if (i>=(8-1)){ //up right - sum+=array[i-8+1]; - count++; - } - if (i<(64-8+1)){ //down right - sum+=array[i+8+1]; - count++; - }*/ - return sum/count; -} - - -void led_smooth() -{ - for (int i=0; i < strip.numPixels(); i++) { - //uint8_t avgbrightness=pixelR_buffer[i]/3+pixelG_buffer[i]/3+pixelB_buffer[i]/3; - pixelR_buffer[i]=(smoothing/100.0)*pixelR[i] + (1.0-(smoothing/100.0))*getAverage(pixelR,i, 0,0); - pixelG_buffer[i]=(smoothing/100.0)*pixelG[i] + (1.0-(smoothing/100.0))*getAverage(pixelG,i, 0,0); - pixelB_buffer[i]=(smoothing/100.0)*pixelB[i] + (1.0-(smoothing/100.0))*getAverage(pixelB,i, 0,0); - - } - showBuffer(); -} - -void led_movingPoint() -{ - uint32_t c=wheel(wheelPos); - wheelPosSlow+=wheelSpeed; - wheelPos=(wheelPos+ (wheelPosSlow/10) )%255; - wheelPosSlow=wheelPosSlow%16; - - uint8_t r = (uint8_t)(c >> 16); - uint8_t g = (uint8_t)(c >> 8); - uint8_t b = (uint8_t)c; - - movingPoint_x=movingPoint_x+8+random(-random(0,1+1),random(0,1+1)+1); - movingPoint_y=movingPoint_y+8+random(-random(0,1+1),random(0,1+1)+1); - if (movingPoint_x<8){ - movingPoint_x=8-movingPoint_x; - }else if (movingPoint_x>=16){ - movingPoint_x=22-movingPoint_x; - }else{ - movingPoint_x-=8; - } - - if (movingPoint_y<8){ - movingPoint_y=8-movingPoint_y; - }else if (movingPoint_y>=16){ - movingPoint_y=22-movingPoint_y; - }else{ - movingPoint_y-=8; - } - uint8_t startx=movingPoint_x; - uint8_t starty=movingPoint_y; - - for (int i=0;i=16){ - movingPoint_x=22-movingPoint_x; - }else{ - movingPoint_x-=8; - } - - if (movingPoint_y<8){ - movingPoint_y=8-movingPoint_y; - }else if (movingPoint_y>=16){ - movingPoint_y=22-movingPoint_y; - }else{ - movingPoint_y-=8; - } - - - - if (pixelR[xyToPos(movingPoint_x,movingPoint_y)]r){ - pixelR[xyToPos(movingPoint_x,movingPoint_y)]--; - } - if (pixelG[xyToPos(movingPoint_x,movingPoint_y)]g){ - pixelG[xyToPos(movingPoint_x,movingPoint_y)]--; - } - if (pixelB[xyToPos(movingPoint_x,movingPoint_y)]b){ - pixelB[xyToPos(movingPoint_x,movingPoint_y)]--; - } - } - - - //pixelR[xyToPos(movingPoint_x,movingPoint_y)]=0.5*pixelR[xyToPos(movingPoint_x,movingPoint_y)]+0.5*r; - //pixelG[xyToPos(movingPoint_x,movingPoint_y)]=0.5*pixelG[xyToPos(movingPoint_x,movingPoint_y)]+0.5*g; - //pixelB[xyToPos(movingPoint_x,movingPoint_y)]=0.5*pixelB[xyToPos(movingPoint_x,movingPoint_y)]+0.5*b; - - movingPoint_x=startx; - movingPoint_y=starty; - -} -void bufferClear() -{ - for (int i=0; i < strip.numPixels(); i++) { - pixelR_buffer[i]=0; - pixelG_buffer[i]=0; - pixelB_buffer[i]=0; - } -} - -void led_spiral() -{ - wheelPos++; - for (int i=0; i < strip.numPixels(); i++) { - //strip.setPixelColor(i,wheel((wheelPos+i*5)%255)); - - - } +bool onSetLength(const HomieRange& range, const String& value) { + strip.None(); + strip.clear(); strip.show(); + int newLength = value.toInt(); + if (newLength > 0) { + strip.updateLength(newLength); + } + homieNode.setProperty("length").send(value); } -void led_randomfade() -{ - for (int i=0; i < strip.numPixels(); i++) { - pixelR_buffer[i]+=random(0,random(0,fadespeedmax+1)+1); //use buffer red channel for color wheel - strip.setPixelColor(i,wheel(pixelR_buffer[i])); - } - strip.show(); -} - -void led_icon(uint8_t fontchar, uint32_t iconcolor) -{ - for (int i=0; i < strip.numPixels(); i++) { - uint64_t mask=1LL<<(uint64_t)i; - - if ( (font[fontchar]&mask) == 0){ - strip.setPixelColor(numToPos(i),strip.Color(0, 0, 0)); //bit is 0 at pos i - }else{ - - float _brightness=1.0- ( (iconCountStart-iconCountdown)*1.0/iconCountStart ); - uint8_t _r=(uint8_t)(iconcolor >> 16); - uint8_t _g=(uint8_t)(iconcolor >> 8); - uint8_t _b=(uint8_t)iconcolor; - strip.setPixelColor(numToPos(i),strip.Color(_r*_brightness,_g*_brightness,_b*_brightness)); //bit is 1 at pos i - } - - } - strip.show(); -} - -void set_randomBuffer() -{ - for (int i=0; i < strip.numPixels(); i++) { - uint32_t c=wheel(random(0,256)); - pixelR_buffer[i]=(uint8_t)(c >> 16); - pixelG_buffer[i]=(uint8_t)(c >> 8); - pixelB_buffer[i]=(uint8_t)c; - } -} - - -uint32_t parseColor(String value){ - if (value.charAt(0)=='#'){ //solid fill - String color=value.substring(1); - int number = (int) strtol( &color[0], NULL, 16); - - - // Split them up into r, g, b values - int r = number >> 16; - int g = number >> 8 & 0xFF; - int b = number & 0xFF; - Homie.getLogger() << "r=" << r << " g=" << g << " b=" << b << endl; - //Serial.print("r=");Serial.print(r); - //Serial.print(" g=");Serial.print(g); - //Serial.print(" b=");Serial.println(b); - return strip.Color(r, g, b); - - } - return 0; -} - - -bool effectHandler(const HomieRange& range, const String& value) { - Homie.getLogger() << "-> " << value << endl; - int sep = value.indexOf("|"); - - String command=value.substring(0,sep); - String parameters=value.substring(sep+1); - Homie.getLogger() << "command=" << command << " parameters=" << parameters << endl; - - if (command.equals("fill")){ - effect=EFFECT_NONE; - led_fill(parseColor(parameters)); - }else if (command.equals("off")){ - effect=EFFECT_NONE; - led_fill(strip.Color(0, 0, 0)); - }else if (command.equals("random")){ - effect=EFFECT_NONE; - led_random(); - }else if (command.equals("set")){ //example: set|37#ff003a - effect=EFFECT_NONE; - int x=parameters.substring(0,1).toInt(); - int y=parameters.substring(1,2).toInt(); - String cstr=parameters.substring(2,9); - strip.setPixelColor(xyToPos(x,y), parseColor(cstr)); - strip.show(); - }else if (command.equals("smooth")){ //example: smooth|[wheelspeed]|[smoothing]|[strength] wheelspeed=1-255, smoothing=0-100, strength=1-255 - int sepparam = parameters.indexOf("|"); - int p1=parameters.substring(0,sepparam).toInt(); - String parameters_part2=parameters.substring(sepparam+1); - sepparam = parameters_part2.indexOf("|"); - int p2=parameters_part2.substring(0,sepparam).toInt(); - int p3=parameters_part2.substring(sepparam+1).toInt(); - wheelSpeed=16; //default, speed=+1 /frame - if (p1>0){ - wheelSpeed=p1; - } - smoothing=80; - if (p2>0){ - smoothing=p2; - } - strength=50; - if (p3>0){ - strength=p3; - } - Homie.getLogger() << "-- p1=" << p1 << " p2=" << p2 << " p3=" << p3 << endl; - effect=EFFECT_SMOOTH; - bufferClear(); - showBuffer(); - strip.show(); - }else if (command.equals("spiral")){ - effect=EFFECT_SPIRAL; - bufferClear(); - showBuffer(); - strip.show(); - }else if (command.equals("clearbuffer")){ - bufferClear(); - showBuffer(); - strip.show(); - }else if (command.equals("randomfade")){ //example: randomfade|5 - int sepparam = parameters.indexOf("|"); - int p1=parameters.substring(0,sepparam).toInt(); - fadespeedmax=5; - if (p1>0){ - fadespeedmax=p1; - } - effect=EFFECT_RANDOMFADE; - set_randomBuffer(); //initialize random - }else if (command.equals("randombuffer")){ - set_randomBuffer(); //set random - showBuffer(); - } - - - - - return true; -} - -bool pixelsHandler(const HomieRange& range, const String& value) { - - String remaining=value; - int i=0; - effect=EFFECT_NONE; - do{ - String current=remaining.substring(0,7); - Homie.getLogger() << i << ":" << current << endl; - uint32_t currentcolor=parseColor(current); - - strip.setPixelColor(i, currentcolor); - i++; - - remaining=remaining.substring(7); - - }while(remaining.length()>2 && (i0){ //icon effect active - - iconCountdown--; - led_icon(iconchar,iconcolor); - - - - }else{ - - switch(effect){ - case EFFECT_SMOOTH: - led_movingPoint(); - led_smooth(); - break; - case EFFECT_SPIRAL: - led_spiral(); - break; - case EFFECT_RANDOMFADE: - led_randomfade(); - break; - } - - } - - lastMillis=currentMillis; - } - + ArduinoOTA.handle(); } +/************ Old stuff ************/ +/* + bool pixelsHandler(const HomieRange& range, const String& value) { + String remaining = value; + int i = 0; + effect = EFFECT_NONE; + do { + String current = remaining.substring(0, 7); + Homie.getLogger() << i << ":" << current << endl; + uint32_t currentcolor = parseColor(current); + strip.setPixelColor(i, currentcolor); + i++; + remaining = remaining.substring(7); + } while (remaining.length() > 2 && (i < strip.numPixels())); + Homie.getLogger() << " filling rest with black" << endl; + while (i < strip.numPixels()) { + strip.setPixelColor(numToPos(i), strip.Color(0, 0, 0)); + i++; + } + + strip.show(); + + return true; + } +*/