diff --git a/esp-wemos-schild/NeoPatterns.cpp b/esp-wemos-schild/NeoPatterns.cpp index 91cea44..ad625db 100644 --- a/esp-wemos-schild/NeoPatterns.cpp +++ b/esp-wemos-schild/NeoPatterns.cpp @@ -1,293 +1,1115 @@ #include "NeoPatterns.h" -NeoPatterns::NeoPatterns(uint16_t pixels, uint8_t pin, uint8_t type, void (*callback)()) : - Adafruit_NeoPixel(pixels, pin, type) +NeoPatterns::NeoPatterns(uint16_t pixels, uint8_t pin, uint8_t type, void (*callback)(), void (*callbackDebug)(String)) : + Adafruit_NeoPixel(pixels, pin, type) { - OnComplete = callback; + OnComplete = callback; + OnDebugOutput = callbackDebug; + //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 ) ); + + // Max. MAX_DROPS Drops: Location + drop = ( uint8_t* ) calloc (MAX_DROPS, sizeof( uint8_t ) ); + // Max. MAX_DROPS Drops: Brightness (Default to 0) + dropBrightness = ( uint8_t* ) calloc (MAX_DROPS, sizeof( uint8_t ) ); + + + // Max. MAX_RINGS Rings: Location + ring = ( uint8_t* ) calloc (MAX_RINGS, sizeof( uint8_t ) ); + // Max. MAX_RINGS Rings: Brightness (Default to 0) + ringBrightness = ( uint8_t* ) calloc (MAX_RINGS, sizeof( uint8_t ) ); + ringDistance = ( uint8_t* ) calloc (MAX_RINGS, 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 NONE: - break; - default: - break; - } - } +void NeoPatterns::Update() { + if ((millis() - lastUpdate) > Interval) // time to update + { + // OnDebugOutput(String("Updating at " ) + String(millis())); + lastUpdate = millis(); + switch (ActivePattern) + { + case RAINBOW_CYCLE: + RainbowCycleUpdate(); + break; + case THEATER_CHASE: + TheaterChaseUpdate(); + break; + case BVB: + BVBChaseUpdate(); + 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 PLASMA: + PlasmaUpdate(); + break; + case FILL: + break; + case RANDOM: + break; + case FIRE: + FireUpdate(); + break; + case FIREWORKS: + FireworksUpdate(); + break; + case DROP: + DropUpdate(); + break; + case RINGS: + RingsUpdate(); + break; + case SCANNER_RANDOM: + ScannerRandomUpdate(); + break; + case NONE: + break; + default: + break; + } + } else { + delay(1); + // Serial.print("."); + } } void NeoPatterns::Increment() { - if (Direction == FORWARD) - { - Index++; - if (Index >= TotalSteps) - { - Index = 0; - if (OnComplete != NULL) - { - OnComplete(); // call the comlpetion callback - } - } - } - else // Direction == REVERSE - { - --Index; - if (Index <= 0) - { - Index = TotalSteps-1; - if (OnComplete != NULL) - { - OnComplete(); // call the comlpetion callback - } - } - } + // OnDebugOutput(String(Index) + " / " + String(TotalSteps)); + 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::Reverse() { + if (Direction == FORWARD) + { + Direction = REVERSE; + // Index = TotalSteps - 1; + } + else + { + Direction = FORWARD; + // Index = numPixels()-TotalSteps; + } } -void NeoPatterns::None(){ - if(ActivePattern != NONE) { - clear(); - show(); - } - ActivePattern = NONE; +void NeoPatterns::None(uint8_t interval) { + Interval = interval; + if (ActivePattern != NONE) { + clear(); + show(); + } + ActivePattern = NONE; } -void NeoPatterns::RainbowCycle(uint8_t interval, direction dir){ - ActivePattern = RAINBOW_CYCLE; - Interval = interval; - TotalSteps = 255; - Index = 0; - Direction = dir; +/****************** 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(); + 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::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::TheaterChaseUpdate() { + for (int i = 0; i < numPixels(); i++) + { + if ((i + Index) % 8 == 0) + { + setPixelColor(i, Color1); + } + else + { + // Reduce brightness for the base pixels + float _brightness = 0.2; + uint8_t _r = (uint8_t)(Color2 >> 16); + uint8_t _g = (uint8_t)(Color2 >> 8); + uint8_t _b = (uint8_t)Color2; + setPixelColor(i, Color(_r * _brightness, _g * _brightness, _b * _brightness)); + } + } + show(); + Increment(); } +void NeoPatterns::BVBChase(uint32_t color1, uint32_t color2, uint8_t interval, direction dir) { + ActivePattern = BVB; + Interval = interval; + TotalSteps = numPixels(); + Color1 = color1; + Color2 = color2; + Index = 0; + Direction = dir; +} +void NeoPatterns::BVBChaseUpdate() { + for (int i = 0; i < numPixels(); i++) + { + if ((i + Index) % 8 == 0) + { + setPixelColor(i, Color1); + } + else + { + // Reduce brightness for the base pixels + float _brightness = 0.2; + uint8_t _r = (uint8_t)(Color2 >> 16); + uint8_t _g = (uint8_t)(Color2 >> 8); + uint8_t _b = (uint8_t)Color2; + setPixelColor(i, Color(_r * _brightness, _g * _brightness, _b * _brightness)); + } + } + 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; + 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(); + setPixelColor(Index, Color1); + show(); + Increment(); } // Initialize for a SCANNNER -void NeoPatterns::Scanner(uint32_t color1, uint8_t interval, bool colorful) +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; + 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++) - { - 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(); + 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(); } + +// Initialize for a SCANNNER_RANDOM +void NeoPatterns::ScannerRandom(uint32_t color1, uint8_t interval, bool colorful, bool spiral) +{ + ActivePattern = SCANNER_RANDOM; + Interval = interval; + TotalSteps = (numPixels() - 1) * 2; + Color1 = color1; + Index = numPixels() / 2; + wPos = 0; + this->colorful = colorful; + this->spiral = spiral; +} + +// Update the Scanner Pattern +void NeoPatterns::ScannerRandomUpdate() +{ + if (colorful) { + Color1 = Wheel(wPos); + if (wPos >= 255) { + wPos = 0; + } + else { + wPos++; + } + } + if (random(0, 1000) < 5) + { + Reverse(); + } + 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; + 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; + // 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(); + 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::RandomFade(uint8_t interval ) { + ActivePattern = RANDOM_FADE; + Interval = interval; + TotalSteps = 255; + Index = 0; } -void NeoPatterns::SetColor1(uint32_t color){ - Color1 = color; +void NeoPatterns::RandomFadeUpdate() { + ColorSet(Wheel(Index)); + Increment(); } -void NeoPatterns::SetColor2(uint32_t color){ - Color2 = color; + +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(); +} + +/********** FIRE ********/ + +void NeoPatterns::Fire(uint8_t interval) +{ + ActivePattern = FIRE; + Interval = interval; + TotalSteps = 255; + Index = 0; +} + +void NeoPatterns::FireUpdate() +{ + int r = 255; + int g = r - 140; + int b = 0; + + for (int i = 0; i < numPixels(); i++) { + int flicker = random(0, 70); + int r1 = r - flicker; + int g1 = g - flicker; + int b1 = b - flicker; + if (g1 < 0) g1 = 0; + if (r1 < 0) r1 = 0; + if (b1 < 0) b1 = 0; + setPixelColor(i, r1, g1, b1); + } + show(); + Interval = random(50, 150); +} + +/********** FIRE END ****/ + +/********** FIREWORKS ********/ + +// Manchmal noch instabil und lässt den ESP abstürzen. Müsste mal mit Serial mal gedebuggt werden... +void NeoPatterns::Fireworks() +{ + ActivePattern = FIREWORKS; + Interval = 20; // 12ms ist so ziemlich die untere Grenze, durch die Berechnugen und Speicherzugriffe. + // Calculate "good" explosion speed + // 60 LED Strip: 50, 100, 0.985 is a good choice (with Interval = 25) + // Start 0, with maximum speed (100), the rocket should explode at the LATEST at position 50 (of 60). (Which is 10 pixels before maximum) + // rocket_speed_max should not be >100, as this would skip LEDs. + explosion_speed = 0.25f; + rocket_speed_max = 100; + rocket_slowdown = pow(explosion_speed, (float)((float)1/(float)(2*numPixels()-10))); // 0.985f; + rocket_speed_min = int(log(explosion_speed)/((numPixels()/4) * log(rocket_slowdown)))+1; + if (rocket_speed_min/100 < explosion_speed) + { + rocket_speed_min += explosion_speed*100; + } + // OnDebugOutput(String(rocket_slowdown, 6)); + // OnDebugOutput(String(rocket_speed_min)); +} + +/** Debug Output +haus/RGB5m/strip/DEBUG Start: 0: Speed: 0.960 Pos: 0 +haus/RGB5m/strip/DEBUG Explode 0: Speed 0.249 Pos: 149 Iterations: 282 // Die Anzahl der Iterationen ist gut berechnet (0.96 -- 282 von 290 max). Nur wurde der Slowdown nicht mit einberechnet + */ + +void NeoPatterns::explosion(int pos, float rocketspeed) +{ + uint8_t hue = random(0, 256); + uint8_t explosionsize = random(EXPLOSION_SIZE_MIN, EXPLOSION_SIZE_MAX + 1); + for (int i = 0; i < explosionsize; i++) + { + particle_arr.push_back(Particle(this, pos + i - 3, (float)(((float)random(-50, 50)) / 100) + rocketspeed / 2, hue, 1, 0.99f)); + } + +} + +void NeoPatterns::FireworksUpdate() +{ + if (millis() > currentRocketMillis + rocketTimeout) + { + // Start a new rocket + if (random(0, 2) == 0) + { + Rocket tmpr = Rocket(this, 0, (float)(((float)random(rocket_speed_min, rocket_speed_max)) / 100), rocket_slowdown); + // OnDebugOutput(String("Start: ") + String(tmpr.id()) + String(": Speed: ") + String(tmpr.rocketspeed(), 3) + String(" Pos: 0")); + rocket_arr.push_back(tmpr); + } + else + { + Rocket tmpr = Rocket(this, numPixels(), -(float)(((float)random(rocket_speed_min, rocket_speed_max)) / 100), rocket_slowdown); + // OnDebugOutput(String("Start: ") + String(tmpr.id()) + String(": Speed: ") + String(tmpr.rocketspeed(), 3) + String(" Pos: ") + String(numPixels())); + rocket_arr.push_back(tmpr); + } + rocketTimeout = random(ROCKET_LAUNCH_TIMEOUT_MIN, ROCKET_LAUNCH_TIMEOUT_MAX+1); + currentRocketMillis = millis(); + } + + clear(); + + // Iterate through all particles + for (std::vector::iterator it = particle_arr.begin(); it != particle_arr.end(); ++it) + { + Particle & p = *it; + p.update(); + // Erase Particles which are too dark + if (p.brightness() < 0.1) + { + it = particle_arr.erase(it); // After erasing, it is now pointing the next element. + --it; + } + } + + // Iterate through all rockets + for (std::vector::iterator it = rocket_arr.begin(); it != rocket_arr.end(); ++it) + { + Rocket & r = *it; + // Create Trail on old position + particle_arr.push_back(Particle(this, r.pos(), 0, 20, 0.3)); + + r.update(); + if ((r.rocketspeed() <= explosion_speed) && (r.rocketspeed() >= -explosion_speed)) + { + // OnDebugOutput(String("Explode ") + String(r.id()) + String(": Speed ") + String(r.rocketspeed(), 3) + String(" Pos: ") + String(r.pos()) + String(" Iterations: ") + String(r.iteration())); + explosion( r.pos(), r.rocketspeed()); + it = rocket_arr.erase(it); // After erasing, it is now pointing the next element. + --it; + } + } + show(); +} + +/********** FIREWORKS END ****/ + +/********** DROP ********/ + +void NeoPatterns::Drop(uint8_t interval) +{ + ActivePattern = DROP; + Interval = interval; + TotalSteps = 255; + Index = 0; + for (int i = 0; i < 10; i++) { + drop[i] = 0; + dropBrightness[i] = 0; + } + clear(); +} + +void NeoPatterns::DropUpdate() +{ + // Generate new drop? + if (random(0, 100) > 50) + { + Serial.println("Will generate a new drop"); + // New drop + // Find first free drop and discard, if no free place + for (int i = 0; i < MAX_DROPS; i++) { + if (drop[i] == 0) + { + Serial.print("Found a free position for a drop: "); + // Random position + drop[i] = random(0, numPixels()); + dropBrightness[i] = 255; // Initial brightness + Serial.print(i); + Serial.print(" pos "); + Serial.println(drop[i]); + break; + } + } + } + + // Work for all other drops + for (int i = 0; i < MAX_DROPS; i++) { + if (drop[i] > 0) + { + Serial.print("Updating drop on "); + Serial.println(i); + // Current drop + // dropBrightness[i] = dropBrightness[i]>>1; + dropBrightness[i] *= 0.9; + if (dropBrightness[i] <= 8) + { + // Brightness to zero for all neighbours + dropBrightness[i] = 0; + } + setPixelColor(drop[i], 0, 0, dropBrightness[i]); // TODO: Other colors? + // Set neighbouring drops + int nBright; + for (int neighbour = 1; neighbour < 5; neighbour++) { + //nBright = dropBrightness[i] >> neighbour; + nBright = dropBrightness[i]; + for (int j = 1; j < neighbour; j++) + { + nBright *= 0.6; + } + Serial.print(neighbour); + Serial.print(": "); + Serial.println(nBright); + if ((drop[i] - neighbour) >= 0) + { + setPixelColor(drop[i] - neighbour, 0, 0, nBright); + } + if ((drop[i] + neighbour) <= numPixels()) + { + setPixelColor(drop[i] + neighbour, 0, 0, nBright); + } + } + if (dropBrightness[i] <= 8) + { + // Disable this drop + drop[i] = 0; + } + + } + } + show(); +} + +/********** DROP END ****/ + +/********** RINGS ********/ + +void NeoPatterns::Rings(uint8_t interval) +{ + ActivePattern = RINGS; + Interval = interval; + TotalSteps = 255; + Index = 0; + for (int i = 0; i < 10; i++) { + ring[i] = 0; + ringBrightness[i] = 0; + ringDistance[i] = 0; + } + clear(); +} + +void NeoPatterns::RingsUpdate() +{ + // Generate new ring? + if (random(0, 100) > 50) + { + Serial.println("Will generate a new ring"); + // New ring + // Find first free ring and discard, if no free place + for (int i = 0; i < MAX_RINGS; i++) { + if (ring[i] == 0) + { + Serial.print("Found a free position for a ring: "); + // Random position + ring[i] = random(0, numPixels()); + ringBrightness[i] = 255 << 1; // Initial brightness + ringDistance[i] = 0; + Serial.print(i); + Serial.print(" pos "); + Serial.println(ring[i]); + break; + } + } + } + + // Work for all other rings + for (int i = 0; i < MAX_RINGS; i++) { + if (ring[i] > 0) + { + Serial.print("Updating ring on "); + Serial.println(i); + // Center of the ring + ringBrightness[i] *= 0.9; + if (ringBrightness[i] <= 8) + { + // Brightness to zero for the middle + ringBrightness[i] = 0; + } + setPixelColor(ring[i], 0, 0, ringBrightness[i]); // TODO: Other colors? + // Set neighbouring rings + int nBright; + // Maximum distance for rings is 10 + // General idea: Start with the middle (max brightness), continue left and right with brightness * 0.9 + // For each step, dim current brightness for ALL pixels simply by 0.7, below thershold -> off + ringDistance[i]++; + // Neighbours: Color of middle, dimmed by 0.9 to max distance + for (int neighbour = 1; neighbour < ringDistance[i]; neighbour++) + { + Serial.print("Neighbour "); + Serial.print(neighbour); + nBright = 255; + if (ringBrightness[i] == 0) + { + nBright = 0; + } + else + { + for (int j = 0; j < ringDistance[i] - neighbour; j++) + { + nBright *= 0.8; + } + nBright *= (1 - 0.1 * ringDistance[i]); + } + + if (nBright < 10) { + nBright = 0; + } + Serial.print(" brightness: "); + Serial.println(nBright); + if ((ring[i] - neighbour) >= 0) + { + setPixelColor(ring[i] - neighbour, 0, 0, nBright); + } + if ((ring[i] + neighbour) <= numPixels()) + { + setPixelColor(ring[i] + neighbour, 0, 0, nBright); + } + + } + + if (ringBrightness[i] <= 8) + { + // Disable this ring + ring[i] = 0; + } + + } + } + show(); +} + +/********** RINGS END ****/ + + +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(); +} + + + +// 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; + // 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(); + 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; + return (color >> 16) & 0xFF; } // Returns the Green component of a 32-bit color uint8_t NeoPatterns::Green(uint32_t color) { - return (color >> 8) & 0xFF; + return (color >> 8) & 0xFF; } // Returns the Blue component of a 32-bit color uint8_t NeoPatterns::Blue(uint32_t color) { - return color & 0xFF; + 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. +// 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); - } + 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); + } +} + +uint32_t NeoPatterns::Wheel(byte WheelPos, float brightness) { + WheelPos = 255 - WheelPos; + // OnDebugOutput(String("Value ") + String (WheelPos * 3) + String(" converted by brightness ") + String(brightness, 6) + String(" to ") + String(int((float)(WheelPos * 3) * brightness))); + if (WheelPos < 85) { + return Color(int((float)(255 - WheelPos * 3) * brightness), 0, int((float)(WheelPos * 3) * brightness)); + } + if (WheelPos < 170) { + WheelPos -= 85; + return Color(0, int((float)(WheelPos * 3) * brightness), int((float)(255 - WheelPos * 3) * brightness)); + } + WheelPos -= 170; + return Color(int((float)(WheelPos * 3) * brightness), int((float)(255 - WheelPos * 3) * brightness), 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/esp-wemos-schild/NeoPatterns.h b/esp-wemos-schild/NeoPatterns.h index 68e470b..bfe0317 100644 --- a/esp-wemos-schild/NeoPatterns.h +++ b/esp-wemos-schild/NeoPatterns.h @@ -1,59 +1,178 @@ +#ifndef NEOPATTERNS_H +#define NEOPATTERNS_H + #include +#include +#include +#include // std::remove +#include "Rocket.h" +#include "Particle.h" +// class Rocket; +// class Particle; + +// Ideas +// Drop (Middle high, than to both sides diming out) +#define MAX_DROPS 10 +#define MAX_RINGS 1 +// Two or more chasers +// Chaser changing direction randomly // Pattern types supported: -enum pattern { NONE, RAINBOW_CYCLE, THEATER_CHASE, COLOR_WIPE, SCANNER, FADE, RANDOM_FADE }; +enum pattern { NONE, RAINBOW_CYCLE, THEATER_CHASE, COLOR_WIPE, SCANNER, FADE, RANDOM_FADE, SMOOTH, RANDOM_FADE_SINGLE, PLASMA, FILL, RANDOM, FIRE, FIREWORKS, DROP, RINGS, SCANNER_RANDOM, BVB }; // 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)()); + public: + NeoPatterns(uint16_t pixels, uint8_t pin, uint8_t type, void (*callback)(), void (*callbackDebug)(String)); -void Update(); -void Reverse(); -void None(); -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, uint8_t interval = 40,bool colorful = 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 Update(); -void SetColor1(uint32_t color); -void SetColor2(uint32_t color); -//Utilities -void ColorSet(uint32_t color); -uint8_t Red(uint32_t color); -uint8_t Green(uint32_t color); -uint8_t Blue(uint32_t color); -uint32_t Wheel(byte WheelPos); + 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 BVBChase(uint32_t color1, uint32_t color2, uint8_t interval, direction dir = FORWARD); + void BVBChaseUpdate(); + 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 ScannerRandom(uint32_t color1 = 16711680, uint8_t interval = 40, bool colorful = false, bool spiral = false); + void ScannerRandomUpdate(); + 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 Fire(uint8_t interval = 100); + void FireUpdate(); + void Fireworks(); + void FireworksUpdate(); + void explosion(int pos, float rocketspeed); + void Drop(uint8_t interval = 100); + void DropUpdate(); + void Rings(uint8_t interval = 100); + void RingsUpdate(); + 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 Plasma(float phase = 0, float phaseIncrement = 0.03, float colorStretch = 0.3, uint8_t interval = 60); // 0.08 and 0.11 + void PlasmaUpdate(); -private: + 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); + uint32_t Wheel(byte WheelPos, float brightness); + 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); -// Member Variables: -pattern ActivePattern; // which pattern is running -direction Direction; // direction to run the pattern -unsigned long Interval; // milliseconds between updates -unsigned long lastUpdate; // last update of position +#define EXPLOSION_SIZE_MIN 5 +#define EXPLOSION_SIZE_MAX 10 + // 60 LED Strip: 50, 100, 0.985 is a good choice (with Interval = 25) + // Start 0, with maximum speed (100), the rocket should explode at the LATEST at position 50 (of 60). (Which is 10 pixels before maximum) + // #define EXPLOSION_SPEED 0.25f + // #define ROCKET_SPEED_MIN 50 + // ROCKET_SPEED_MAX should not be >100, as this would skip LEDs. + // #define ROCKET_SPEED_MAX 100 + // #define ROCKET_SLOWDOWN 0.985f +#define ROCKET_LAUNCH_TIMEOUT_MIN 1000 +#define ROCKET_LAUNCH_TIMEOUT_MAX 3000 -uint32_t Color1, Color2; // What colors are in use -uint16_t TotalSteps; // total number of steps in the pattern -uint16_t Index; // current step within the pattern + uint32_t maxRocketID = 0; + uint32_t maxParticleID = 0; + uint32_t currentRocketMillis = 0; + uint32_t rocketTimeout; + float explosion_speed = 0.25f; + uint8_t rocket_speed_min = 50; + uint8_t rocket_speed_max = 100; + double rocket_slowdown = 0.985f; -byte wPos; -bool colorful; + private: + std::vector rocket_arr; + std::vector particle_arr; -uint32_t DimColor(uint32_t color); -void Increment(); -void (*OnComplete)(); // Callback on completion of pattern + // 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; + + // Drops + uint8_t *drop; + uint8_t *dropBrightness; + + // Rings + uint8_t *ring; + uint8_t *ringBrightness; + uint8_t *ringDistance; + + 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 + void (*OnDebugOutput)(String); // Callback on completion of pattern + + // Convenient 2D point structure + struct Point { + float x; + float y; + }; }; + + +#endif diff --git a/esp-wemos-schild/Particle.cpp b/esp-wemos-schild/Particle.cpp new file mode 100644 index 0000000..f30ad65 --- /dev/null +++ b/esp-wemos-schild/Particle.cpp @@ -0,0 +1,48 @@ +#include "Particle.h" +#include "NeoPatterns.h" + + +Particle::Particle() // Particle::Particle(NeoPatterns * parent) +{ + _pos = 0; + // _id = parent->maxParticleID; + // parent->maxParticleID++; +} //Default constructor. + +Particle::Particle(NeoPatterns * parent, float pos, float speed, uint8_t hue, float brightness, float decay ) +{ + _id = parent->maxParticleID; + parent->maxParticleID++; + _pos = pos; + _speed = speed; + _hue = hue; + _brightness = brightness; + _decay = decay; + _parent = parent; +} + +bool Particle::operator==(const Particle &p) const { + return (p._id == _id); +} + +void Particle::update() +{ + _pos += _speed; + _speed *= 0.96; + _brightness *= _decay; + + if (_pos > _parent->numPixels()) { + _pos = 0; + } + _parent->setPixelColor((int)_pos, _parent->Wheel(_hue, _brightness)); +} + +float Particle::brightness() +{ + return _brightness; +} + + + + + diff --git a/esp-wemos-schild/Particle.h b/esp-wemos-schild/Particle.h new file mode 100644 index 0000000..0cd229c --- /dev/null +++ b/esp-wemos-schild/Particle.h @@ -0,0 +1,25 @@ +#ifndef PARTICLE_H +#define PARTICLE_H + +#include +class NeoPatterns; // Forward declaration + +class Particle +{ + public: + Particle(NeoPatterns * parent, float pos, float speed, uint8_t hue, float brightness, float decay = 0.95); + Particle(); + bool operator==(const Particle &p) const; + void update(); + int _id; + float brightness(); + private: + float _pos; + float _speed; + float _brightness; + float _decay; + uint8_t _hue; + NeoPatterns * _parent; +}; + +#endif diff --git a/esp-wemos-schild/Rocket.cpp b/esp-wemos-schild/Rocket.cpp new file mode 100644 index 0000000..9ed028e --- /dev/null +++ b/esp-wemos-schild/Rocket.cpp @@ -0,0 +1,62 @@ +#include "Rocket.h" +#include "NeoPatterns.h" + +Rocket::Rocket() +{ + // _id = maxRocketID; + // maxRocketID++; + _pos = 0; + _speed = 1; + _lastbright = 1; +} + +Rocket::Rocket(NeoPatterns *parent, float pos, float rocketspeed, float rocket_slowdown) +{ + _parent = parent; + _id = _parent->maxRocketID; + _parent->maxRocketID++; + _rocket_slowdown = rocket_slowdown; + _iteration = 0; + Serial.print("Rocket: "); + Serial.print(_id); + Serial.print(" "); + Serial.print(pos); + Serial.print(" "); + Serial.println(rocketspeed); + _pos = pos; + _speed = rocketspeed; + +} + +bool Rocket::operator==(const Rocket &r) const { + return (r._id == _id); +} + +void Rocket::update() +{ + _iteration++; + _pos += _speed; + _speed *= _rocket_slowdown; // 0.97 + _parent->setPixelColor(_pos, _parent->Color(50, 32, 0)); +} +// Schweif mit Sparkle +int Rocket::pos() +{ + return _pos; +} + +float Rocket::rocketspeed() +{ + return _speed; +} + +int Rocket::id() +{ + return _id; +} + +uint16_t Rocket::iteration() +{ + return _iteration; +} + diff --git a/esp-wemos-schild/Rocket.h b/esp-wemos-schild/Rocket.h new file mode 100644 index 0000000..8d50fce --- /dev/null +++ b/esp-wemos-schild/Rocket.h @@ -0,0 +1,29 @@ +#ifndef ROCKET_H +#define ROCKET_H + +#include +class NeoPatterns; // Forward declaration + +class Rocket +{ + public: + int _id; + Rocket(NeoPatterns *parent, float pos, float rocketspeed, float rocket_slowdown); + Rocket(); + bool operator==(const Rocket &r) const; + void update(); + int pos(); + float rocketspeed(); + int id(); + uint16_t iteration(); + private: + float _pos; + float _speed; + int _lastbright; + float _rocket_slowdown; + uint16_t _iteration; + NeoPatterns * _parent; + +}; + +#endif diff --git a/esp-wemos-schild/esp-wemos-schild.ino b/esp-wemos-schild/esp-wemos-schild.ino index 07f6ad7..5c40d50 100644 --- a/esp-wemos-schild/esp-wemos-schild.ino +++ b/esp-wemos-schild/esp-wemos-schild.ino @@ -4,148 +4,211 @@ #include #include #include "NeoPatterns.h" +#include #define PIN D1 -#define NUMPIXELS 80 +#define NUMPIXELS 30 #define FW_NAME "esp-schild" -#define FW_VERSION "1.0.1" +#define FW_VERSION "1.0.3" -void StripComplete(){ - return; +HomieNode homieNode("strip", "strip"); + +void StripComplete() { + return; } -NeoPatterns pixels = NeoPatterns(NUMPIXELS, PIN, NEO_GRB + NEO_KHZ800,&StripComplete); - -HomieNode stripNode("strip", "strip"); - -bool onSetColor(const HomieRange& range, const String& value){ - if (!range.isRange || range.index < 0 || range.index > 1) { - return false; - } - switch(range.index) { - case 0: - pixels.SetColor1(value.toInt()); - break; - case 1: - pixels.SetColor2(value.toInt()); - break; - } - stripNode.setProperty("color_" + String(range.index)).send(value); +void DebugOutput(String value) { + homieNode.setProperty("DEBUG").send(value); } -bool onSetPixel(const HomieRange& range, const String& value){ - if(!range.isRange) { - pixels.None(); - pixels.ColorSet(value.toInt()); - stripNode.setProperty("pixel").send(value); - return true; - } - if (range.index < 0 || range.index > pixels.numPixels()-1) { - return false; - } - pixels.None(); - pixels.setPixelColor(range.index, value.toInt()); - pixels.show(); - stripNode.setProperty("pixel_" + String(range.index)).send(value); +NeoPatterns strip = NeoPatterns(NUMPIXELS, PIN, NEO_GRB + NEO_KHZ800, &StripComplete, &DebugOutput); + +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); } -bool onSetBrightness(const HomieRange& range, const String& value){ - long brightness= value.toInt(); - if (brightness < 0 || brightness > 255) { - return false; - } - pixels.setBrightness(brightness); - pixels.show(); - stripNode.setProperty("brightness").send(value); +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 (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); } -bool onSetEffect(const HomieRange& range, const String& value){ - String effect = value; - effect.toLowerCase(); - if(effect == "scanner") { - pixels.Scanner(pixels.Color(255, 0, 0)); - } - else if(effect == "randomscanner") { - pixels.Scanner(pixels.Color(255, 0, 0), 40, true); - } - else if(effect == "rainbowcycle") { - pixels.RainbowCycle(50); - } - else if(effect == "theaterchase") { - pixels.TheaterChase(pixels.Color(255, 0, 0), pixels.Color(0,0,255), 100); - } - else if(effect == "fade") { - pixels.Fade(pixels.Color(255, 0, 0), pixels.Color(0,0,255), 200, 100); - } - else if(effect == "randomfade") { - pixels.RandomFade(); - } - else { - pixels.None(); - } - stripNode.setProperty("effect").send(value); +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); } -bool onSetClear(const HomieRange& range, const String& value){ - pixels.None(); - pixels.clear(); - pixels.show(); - stripNode.setProperty("clear").send(value); +bool onSetEffect(const HomieRange& range, const String& value) { + 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), 4, 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), 50); + } + else if (effect == "bvb") { + strip.BVBChase(strip.Color(255, 185, 0), strip.Color(0, 0, 0), 50); + } + 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 if (effect == "fire") { + strip.Fire(); + } + else if (effect == "fireworks") { + strip.Fireworks(); + } + else if (effect == "drop") { + strip.Drop(); + } + else if (effect == "scannerrandom") { + strip.ScannerRandom(strip.Color(255, 0, 0), 4, true); + } + else if (effect == "ring") { + strip.Rings(); + } 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 if (command.equals("randomscanner")) { + int sepparam = parameters.indexOf("|"); + int p1 = parameters.substring(0, sepparam).toInt(); + if (p1 <= 0) { + p1 = 5; + } + homieNode.setProperty("effect").send(String(p1)); + strip.Scanner(strip.Color(255, 0, 0), p1, true); + } + else { + strip.None(); + digitalWrite(PIN, LOW); // D4 ist auch gleichzeitig der LED-Pin, daher abschalten... (TODO: TEST: FIXME) + } + } + homieNode.setProperty("effect").send(value); } -bool onSetLength(const HomieRange& range, const String& value){ - pixels.None(); - pixels.clear(); - pixels.show(); - int newLength = value.toInt(); - if(newLength > 0) { - pixels.updateLength(newLength); - } - stripNode.setProperty("length").send(value); + +bool onSetClear(const HomieRange& range, const String& value) { + strip.None(); + strip.clear(); + strip.show(); + homieNode.setProperty("clear").send(value); +} + +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 loopHandler() { - pixels.Update(); + strip.Update(); } void setup() { - Serial.begin(115200); + Serial.begin(115200); - Homie_setFirmware(FW_NAME, FW_VERSION); - Homie_setBrand(FW_NAME); - Homie.setLoopFunction(loopHandler); + Homie_setFirmware(FW_NAME, FW_VERSION); + Homie_setBrand(FW_NAME); + Homie.setLoopFunction(loopHandler); - stripNode.advertiseRange("pixel", 0, NUMPIXELS-1).settable(onSetPixel); - stripNode.advertiseRange("color", 0, 1).settable(onSetColor); - stripNode.advertise("brightness").settable(onSetBrightness); - stripNode.advertise("effect").settable(onSetEffect); - stripNode.advertise("clear").settable(onSetClear); - stripNode.advertise("length").settable(onSetLength); + homieNode.advertiseRange("pixel", 0, NUMPIXELS - 1).settable(onSetPixel); + homieNode.advertiseRange("color", 0, 1).settable(onSetColor); + homieNode.advertise("brightness").settable(onSetBrightness); + homieNode.advertise("effect").settable(onSetEffect); + homieNode.advertise("clear").settable(onSetClear); + homieNode.advertise("length").settable(onSetLength); - pixels.begin(); - pixels.clear(); - pixels.setBrightness(64); - pixels.show(); + strip.begin(); + strip.clear(); + strip.setBrightness(30); + strip.show(); - Homie.setup(); + Homie.setup(); - ArduinoOTA.setHostname(Homie.getConfiguration().deviceId); -// ArduinoOTA.setPassword((const char *)"ctdo2342"); - ArduinoOTA.onStart([]() { - pixels.clear(); - }); - ArduinoOTA.onEnd([]() { - pixels.clear(); - }); - ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) { - pixels.setPixelColor(progress / (total / NUMPIXELS), pixels.Color(100, 0, 0)); - pixels.show(); - }); - ArduinoOTA.begin(); + + ArduinoOTA.setHostname(Homie.getConfiguration().deviceId); + ArduinoOTA.begin(); + ArduinoOTA.onStart([]() { + strip.clear(); + }); + ArduinoOTA.onEnd([]() { + strip.clear(); + }); + ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) { + strip.setPixelColor(progress / (total / NUMPIXELS), strip.Color(100, 0, 0)); + strip.show(); + }); + ArduinoOTA.begin(); } void loop() { - Homie.loop(); - ArduinoOTA.handle(); + Homie.loop(); + ArduinoOTA.handle(); }