commit e00f8e53e8cc823b15fca8264f88bfdac34f6061 Author: starcalc Date: Sun Apr 8 13:48:58 2018 +0200 Initial commit diff --git a/ESP8266-RGB5m.ino b/ESP8266-RGB5m.ino new file mode 100644 index 0000000..38819fa --- /dev/null +++ b/ESP8266-RGB5m.ino @@ -0,0 +1,265 @@ +#include + +#include +#include +#include +#define NUMPIXELS 5 * 60 // 60 Pixel per meter, it is being used within NeoPatterns.h +#include "NeoPatterns.h" +#include + +#define PIN D4 +#define PINLOW_RELAIS D2 + +NeoPatterns strip = NeoPatterns(NUMPIXELS, PIN, NEO_GRB + NEO_KHZ800, &StripComplete, &DebugOutput); + +bool stopAfterCompletion; +HomieNode homieNode("strip", "strip"); + +void StripComplete() { + if (stopAfterCompletion) + { + strip.IconComplete(); + } + return; +} + +void DebugOutput(String value) { + homieNode.setProperty("DEBUG").send(value); +} + + + +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 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 onSetBrightness(const HomieRange& range, const String& value) { + long brightness = value.toInt(); + if (brightness < 0 || brightness > 255) { + return false; + } + if (brightness > 160) { + brightness = 160; // Künstliche Begrenzung + } + strip.setBrightness(brightness); + strip.show(); + homieNode.setProperty("brightness").send(value); +} + +bool onSetEffect(const HomieRange& range, const String& value) { + + digitalWrite(PINLOW_RELAIS, LOW); // This relais: LOW = ON + 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), 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(PINLOW_RELAIS, HIGH); // This relais: HIGH = OFF + digitalWrite(PIN, LOW); // D4 ist auch gleichzeitig der LED-Pin, daher abschalten... (TODO: TEST: FIXME) + } + } + 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); +} + +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() { + strip.Update(); +} + +void setup() { + Serial.begin(115200); + + pinMode(PINLOW_RELAIS, OUTPUT); + digitalWrite(PINLOW_RELAIS, HIGH); // This relais: HIGH = OFF + + Homie_setFirmware("rgb5m", "1.0.4"); + Homie.setLoopFunction(loopHandler); + + 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); + homieNode.advertise("icon").settable(onSetIcon); + // Homie.disableLedFeedback(); + // Homie.setResetTrigger(D0, LOW, 2000); + + Homie.setup(); + + strip.begin(); + strip.clear(); + strip.setBrightness(160); // Künstliche Begrenzung, da nur 6A Netzteil + strip.show(); + stopAfterCompletion = false; // Default + + ArduinoOTA.setHostname("rgb5m"); + ArduinoOTA.onStart([]() { + strip.None(); + strip.clear(); + strip.setBrightness(25); + strip.show(); + }); + ArduinoOTA.onEnd([]() { + strip.clear(); + strip.show(); + }); + ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) { + Serial.print(progress); + Serial.print(" "); + Serial.print(total); + Serial.print(" "); + Serial.print(NUMPIXELS * (float)progress / (float)total); + + // strip.setPixelColor(total, strip.Color(255,0,0)); + // strip.setPixelColor(progress / (total / NUMPIXELS), strip.Color(255, 0, 0)); + for (int i = 0; i < NUMPIXELS; i++) + { + if (i > (NUMPIXELS * (float)progress / (float)total)) + { + // Higher: Default to off + strip.setPixelColor(i, strip.Color(0, 0, 0)); + } + else + { + // Lower/Same: Enable + strip.setPixelColor(i, strip.Color(255, 0, 0)); + } + } + strip.show(); + }); + ArduinoOTA.begin(); +} + +void loop() { + Homie.loop(); + ArduinoOTA.handle(); +} diff --git a/NeoPatterns.cpp b/NeoPatterns.cpp new file mode 100644 index 0000000..f86081b --- /dev/null +++ b/NeoPatterns.cpp @@ -0,0 +1,1177 @@ +#include "NeoPatterns.h" + +NeoPatterns::NeoPatterns(uint16_t pixels, uint8_t pin, uint8_t type, void (*callback)(), void (*callbackDebug)(String)) : + Adafruit_NeoPixel(pixels, pin, type) +{ + 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 + { + // 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 ICON: + IconUpdate(); + 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() +{ + // 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 = numPixels()-TotalSteps; + } +} + +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) % 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; +} + +// 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(); + +} + + +// 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; +} + +// 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(); +} + +/********** 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(); +} + + +/****************** 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); + } +} + +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/NeoPatterns.h b/NeoPatterns.h new file mode 100644 index 0000000..a0d8a66 --- /dev/null +++ b/NeoPatterns.h @@ -0,0 +1,182 @@ +#ifndef NEOPATTERNS_H +#define NEOPATTERNS_H + +#include +#include "font.h" +#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, SMOOTH, ICON, 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)(), void (*callbackDebug)(String)); + + + 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 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 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); + 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); + + +#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 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; + + private: + std::vector rocket_arr; + std::vector particle_arr; + + // 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/Particle.cpp b/Particle.cpp new file mode 100644 index 0000000..f30ad65 --- /dev/null +++ b/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/Particle.h b/Particle.h new file mode 100644 index 0000000..0cd229c --- /dev/null +++ b/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/Rocket.cpp b/Rocket.cpp new file mode 100644 index 0000000..9ed028e --- /dev/null +++ b/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/Rocket.h b/Rocket.h new file mode 100644 index 0000000..8d50fce --- /dev/null +++ b/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/font.h b/font.h new file mode 100644 index 0000000..54327cd --- /dev/null +++ b/font.h @@ -0,0 +1,153 @@ +#ifndef FONT_H +#define FONT_H + +/* 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 */ +}; + +#endif