NeoPixel-Beleuchtung für SilentBase 800 Programmversion 0.9
Der Glitzereffekt in Programmversion 0.7 war für mich bereits eine Herausforderung. (Der Feuereffekt Nr. 2 (Programmmodus 5) aus Programmversion 0.8 ist ein Kind des Zufalls.) Den bekannten Fire 2012-Effekt aus der FastLED-Bibliothek nachzubauen war eine Herausforderung für mich. Sehr hilfreich war der Code von Tweaking4all, der allerdings fehlerbehaftet ist. Aber letzendlich habe ich es geschafft.
Eine gute Beschreibung der Auswirkung der Parameter „Cooling“ und „Sparkling“ (inklusive Videos) gibt es hier.
Hilfreiche Links:
- Sehr gute Beschreibung des Codes: https://github.com/krzychb/EspFire2012/blob/master/readme.md
- Simulation Fire-Effekt: http://dougalcampbell.github.io/LEDStrip/
- Fire-Effekt in JS: https://github.com/dougalcampbell/LEDStrip/blob/master/fire.js
ToDos:
- Bei dem Fire2012-Effekt müsste die Temperatur eines Pixels die Bewegungsgeschwindigkeit beeinflussen.
- Die Variable „buttonpressed“ könnte auch als boolean definiert werden.
- Das array „byte invert“ könnte man sich mit dem Befehl map sparen.
Der Sketch enthält Code von Baldengineer, Scynd, http://www.walltech.cc und http://www.tweaking4all.com.
// Dieser Sketch enthält Code von // Baldengineer (https://www.baldengineer.com), // Scynd (http://www.scynd.de) und // Hans Luijten (http://www.tweaking4all.com). // Bibliotheken einbinden #include <EEPROM.h> #include <ResponsiveAnalogRead.h> #include <Adafruit_NeoPixel.h> // Definiert die Pins #define buttonPin 7 // Taster #define loadPin A0 // analoge Spannungsmessung #define neoPin1 11 // Neopixel-Strip rechte Seite #define neoPin2 12 // Neopixel-Strip linke Seite // Definiert eine Adresse im EEPROM int addr = 0; // An dieser Adresse wird später der ausgewählte Programmmodus gespeichert // Definiert ein ResponsiveAnalogRead Objekt ResponsiveAnalogRead rload(loadPin, true); // Definiert die Variablen int numPixels = 64; // Anzahl der NeoPixel int load; // Variable repäsentiert später die Differenz zwischen minimaler und maximaler Netzteillast in Prozent int buttonstate = HIGH; // aktuelles Signal vom Tasterpin int buttonpressed = 0; // abfragen ob Taster gedrückt war int debouncetime = 50; // Zeit für Entprellung ggf. anpassen float rloadmin = 350; // Messwert am loadPin bei minimaler Netzteillast (muss abgelesen werden) float rloadmax = 650; // Messwert am loadPin bei maximaler Netzteillast (muss abgelesen werden) float x = 0; // Zählwert für verschiedene Beleuchtungseffekte boolean sw1 = true; // Schaltvariable für verschiedene Beleuchtungseffekte boolean sw2 = true; // Schaltvariable für verschiedene Beleuchtungseffekte int i; // Zählwert für verschiedene Beleuchtungseffekte int j; // Zählwert für verschiedene Beleuchtungseffekte int k; // Zählwert für verschiedene Beleuchtungseffekte int l; // Zählwert für verschiedene Beleuchtungseffekte int cooling; // Variable für Beleuchtungseffekt Flammen int sparkling; // Variable für Beleuchtungseffekt Flammen int cooldown; static byte heat[64]; // Ein Array für die Temperaturwerte byte invert[] = {63, 62, 61, 60, 59, 58, 57, 56, 55, 54, // Ein Array zur Umrechnung 53, 52, 51, 50, 49, 48, 47, 46, 45, 44, 43, 42, 41, 40, 39, 38, 37, 36, 35, 34, 33, 32, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 }; byte programmode = 0; // Programmmodus // Definiert die NeoPixel-Strips Adafruit_NeoPixel strip1 = Adafruit_NeoPixel(numPixels, neoPin1, NEO_GRBW + NEO_KHZ800); Adafruit_NeoPixel strip2 = Adafruit_NeoPixel(numPixels, neoPin2, NEO_GRBW + NEO_KHZ800); // Definiert die globalen RGBW-Werte byte r = 0; byte g = 0; byte b = 0; byte w = 0; // Definiert die Tracking-Variablen für die IF-Abfragen unsigned long previousMillisCalcLoad = 0; unsigned long previousMillisFPS20 = 0; unsigned long previousMillisFPS60 = 0; unsigned long previousMillisSerialPrint = 0; unsigned long buttontime = 0; unsigned long sparkle1on = 0; unsigned long sparkle1off = 0; unsigned long sparkle1ondelay = 100; unsigned long sparkle1offdelay = 500; unsigned long sparkle2on = 0; unsigned long sparkle2off = 0; unsigned long sparkle2ondelay = 100; unsigned long sparkle2offdelay = 500; // Definiert die Intervalle für die IF-Abfragen int intervalCalcLoad = 500; // Delay für Berechnung der Last int intervalFPS20 = 50; // Delay für Effekte mit 20 FPS int intervalFPS60 = 16; // Delay für Effekte mit 60 FPS int intervalSerialPrint = 1000; // Delay für serielle Ausgabe void setup() { // Initialisiere den Button-Pin pinMode(buttonPin, INPUT_PULLUP); // Initialisiere die NeoPixel-Pins pinMode(neoPin1, OUTPUT); pinMode(neoPin2, OUTPUT); // Initialisiere den analogen Pin pinMode(loadPin, INPUT); // Initialisiere die NeoPixel-Strips strip1.begin(); // Initialisiert das Neopixel strip1.show(); // Macht das NeoPixel sichtbar //strip1.clear(); // Macht das NeoPixel aus strip2.begin(); // Initialisiert das Neopixel strip2.show(); // Macht das NeoPixel sichtbar //strip2.clear(); // Macht das NeoPixel aus // Lese den abgespeicherten Wert für den Programmmodus aus dem EEPROM programmode = EEPROM.read(addr); // Initialisiere die serialle Schnittstelle Serial.begin(57600); delay (2000); } void loop() { // Lesen und entprellen des Tasters buttonstate = digitalRead(buttonPin); // Wenn der Taster gedrückt ist... if (buttonstate == LOW) { buttontime = millis(); // aktualisiere tasterZeit buttonpressed = 1; // speichert, dass Taster gedrückt wurde } // Wenn die gewählte entprellZeit vergangen ist und der Taster gedrückt war... if ((millis() - buttontime > debouncetime) && buttonpressed == 1) { buttonpressed = 0; // setzt gedrückten Taster zurück programmode++; // Programmmodus wird um +1 erhöht EEPROM.write(addr, programmode); // Schreibt den Programmmodus ins EEPROM r = 0; // setzt den globalen Farbwert zurück g = 0; // setzt den globalen Farbwert zurück b = 0; // setzt den globalen Farbwert zurück w = 0; // setzt den globalen Farbwert zurück strip1.clear(); // setzt die NeoPixel zurück strip2.clear(); // setzt die NeoPixel zurück } // Aktuelle Zeit abfragen unsigned long currentMillis = millis(); // Messen der Spannung am analogen Eingangspin loadPin (A0) und Berechnen einiger Variablen zur Beeinflussung verschiedener Effekte if ((unsigned long)(currentMillis - previousMillisCalcLoad) >= intervalCalcLoad) { // Auslesen des analogen Eingangs (Last Netzteil) rload.update(); // Berechnung der Netzteillast in Prozent: Bei ruhendem Desktop soll der Wert 0 sein, bei maximaler Auslastung 100 load = 100 - ((rloadmax - rload.getValue()) / (rloadmax - rloadmin)) * 100; // Berechnung von Cooling für den Effekt Fire 2012: Legt fest, wie stark die aufsteigenden Flammen abkühlen // Werte zwischen 20 und 100 sollen am hübschesten sein, ein guter Standard ist 50 cooling = 100 - (load / 0.8); // Berechnung von Sparkling für den Effekt Fire2012: Legt fest, wie oft ein Funke auflohdert //Werte zwischen 50 und 200 sollen am hübschesten sein, ein guter Standard ist 120 sparkling = 50 + (load * 1.5); //Speichere die aktuelle Zeit in die zughörige Variable previousMillisCalcLoad = currentMillis; } // Steuerung der NeoPixel-Strips // Beleuchtungseffekt für Programmmodus 0 (Atmen rot) if (programmode == 0) { if ((unsigned long)(currentMillis - previousMillisFPS20) >= intervalFPS20) { if(load < 10) { // Variable soll für diesen Effekt nicht kleiner als 10 werden load = 10; } // Ansteuerung der NeoPixel-Strips x = x + (0.05 * (load/10)); if (x >= 6.28) { x = 0; } // fade led mit x r = ((exp(sin(x)) - 0.36787944) * 108.492061351); for (int i = 0; i < numPixels; i++) { strip1.setPixelColor(i, r, g, b, w); strip2.setPixelColor(i, r, g, b, w); } strip1.show(); strip2.show(); //Speichere die aktuelle Zeit in die zughörige Variable previousMillisFPS20 = currentMillis; } } //Beleuchtungseffekt für Programmmodus 1 (Atmen weiß) else if (programmode == 1) { if ((unsigned long)(currentMillis - previousMillisFPS20) >= intervalFPS20) { if(load < 10) { // Variable soll für diesen Effekt nicht kleiner als 10 werden load = 10; } // Ansteuerung der NeoPixel-Strips x = x + (0.05 * (load/10)); if (x >= 6.28) { x = 0; } // fade led mit x w = ((exp(sin(x)) - 0.36787944) * 108.492061351); for (int i = 0; i < numPixels; i++) { strip1.setPixelColor(i, r, g, b, w); strip2.setPixelColor(i, r, g, b, w); } strip1.show(); strip2.show(); //Speichere die aktuelle Zeit in die zughörige Variable previousMillisFPS20 = currentMillis; } } //Beleuchtungseffekt für Programmmodus 2 (Stabthermometer) else if (programmode == 2) { if ((unsigned long)(currentMillis - previousMillisFPS20) >= intervalFPS20) { if(load >= numPixels) { // Variable soll für diesen Effekt nicht größer als NeoPixel vorhanden load = 63; } // Weißer Bereich for (int i = 0; i < numPixels; i++) { strip1.setPixelColor(i, r, g, b, 127); strip2.setPixelColor(i, r, g, b, 127); } // Roter Bereich for (int i = 63; i > (63 - load); i--) { strip1.setPixelColor(i, 127, g, b, w); strip2.setPixelColor(i, 127, g, b, w); } strip1.show(); strip2.show(); //Speichere die aktuelle Zeit in die zughörige Variable previousMillisFPS20 = currentMillis; } } //Beleuchtungseffekt für Programmmodus 3 (Stabthermometer mit Glitzer) else if (programmode == 3) { if ((unsigned long)(currentMillis - previousMillisFPS20) >= intervalFPS20) { if(load >= numPixels) { // Variable soll für diesen Effekt nicht größer als NeoPixel vorhanden load = 63; } // Blauer Bereich for (int i = 0; i < numPixels; i++) { strip1.setPixelColor(i, r, g, 127, w); strip2.setPixelColor(i, r, g, 127, w); } // Roter Bereich for (int i = 63; i > (63 - load); i--) { strip1.setPixelColor(i, 127, g, b, w); strip2.setPixelColor(i, 127, g, b, w); } // Sparkle NeoPixel-Strip 1 if(sw1) { strip1.setPixelColor(j, r, g, b, 255); if(millis() - sparkle1on >= sparkle1ondelay) { sparkle1offdelay = random (100, 1000); sparkle1off = millis(); sw1 = !sw1; } } if(!sw1) { if(millis() - sparkle1off >= sparkle1offdelay) { j = random(0, (numPixels -1)); sparkle1on = millis(); sw1 = !sw1; } } // Sparkle NeoPixel-Strip 2 if(sw2) { strip2.setPixelColor(k, r, g, b, 255); if(millis() - sparkle2on >= sparkle2ondelay) { sparkle2offdelay = random (100, 1000); sparkle2off = millis(); sw2 = !sw2; } } if(!sw2) { if(millis() - sparkle2off >= sparkle2offdelay) { k = random(0, (numPixels - 1)); sparkle2on = millis(); sw2 = !sw2; } } // Sende die Daten an die Neopixel strip1.show(); strip2.show(); //Speichere die aktuelle Zeit in die zughörige Variable previousMillisFPS20 = currentMillis; } } //Beleuchtungseffekt für Programmmodus 4 (Feuer 1) else if (programmode == 4) { if ((unsigned long)(currentMillis - previousMillisFPS20) >= intervalFPS20) { for (int i = 0; i < numPixels; i++) { int flicker = random (0, 150); int r = 255; int g = r-100; int b = 30; r = r - flicker; g = g - flicker; b = b - flicker; if(r < 0) r = 0; if(g < 0) g = 0; if(b < 0) b = 0; strip1.setPixelColor(i, r, g, b, 0); strip2.setPixelColor(i, r, g, b, 0); } // Sende die Daten an die Neopixel strip1.show(); strip2.show(); //Speichere die aktuelle Zeit in die zughörige Variable previousMillisFPS20 = currentMillis; } } //Beleuchtungseffekt für Programmmodus 5 (Feuer 2) else if (programmode == 5) { if ((unsigned long)(currentMillis - previousMillisFPS20) >= intervalFPS20) { if(load >= numPixels) { // Variable soll für diesen Effekt nicht größer als NeiPoxel vorhanden load = 63; } int r = 255; int g = 130; int b = 30; int w = 63; for (int i = (numPixels -1); i > 0; i--) { int flicker = random (0, (42 - (load/2))); r = r - flicker; g = g - flicker; b = b - flicker; w = w - (flicker * 3); if(r < 0) r = 0; if(g < 0) g = 0; if(b < 0) b = 0; if(w < 0) w = 0; strip1.setPixelColor(i, r, g, b, w); strip2.setPixelColor(i, r, g, b, w); } // Sende die Daten an die Neopixel strip1.show(); strip2.show(); //Speichere die aktuelle Zeit in die zughörige Variable previousMillisFPS20 = currentMillis; } } //Beleuchtungseffekt für Programmmodus 6 (Fire2012)) else if (programmode == 6) { if ((unsigned long)(currentMillis - previousMillisFPS60) >= intervalFPS60) { // Step 1. Cool down every cell a little for(int i = 0; i < numPixels; i++) { cooldown = random(0, ((cooling * 10) / numPixels) + 2); if(cooldown > heat[i]) { heat[i] = 0; } else { heat[i] = heat[i] - cooldown; } } // Step 2. Heat from each cell drifts 'up' and diffuses a little for(int k = numPixels - 1; k >= 2; k--) { heat[k] = (heat[k - 1] + heat[k - 2] + heat[k - 2]) / 3; } // Step 3. Randomly ignite new 'sparks' near the bottom if(random(255) < sparkling) { l = random(7); heat[l] = heat[l] + random(160,255); //heat[l] = random(160,255); } // Step 4. Convert heat to LED colors for(int j = numPixels -1; j >= 0; j--) { // Scale 'heat' down from 0-255 to 0-191 byte t192 = round((heat[j] /255.0) * 191); // calculate ramp up from byte heatramp = t192 & 0x3F; // 0..63 heatramp <<= 2; // scale up to 0..252 // figure out which third of the spectrum we're in: if( t192 > 0x80) { // hottest strip1.setPixelColor(invert[j], 255, 255, heatramp, 0); strip2.setPixelColor(invert[j], 255, 255, heatramp, 0); } else if( t192 > 0x40 ) { // middle strip1.setPixelColor(invert[j], 255, heatramp, 0, 0); strip2.setPixelColor(invert[j], 255, heatramp, 0, 0); } else { // coolest strip1.setPixelColor(invert[j], heatramp, 0, 0, 0); strip2.setPixelColor(invert[j], heatramp, 0, 0, 0); } } strip1.show(); strip2.show(); //Speichere die aktuelle Zeit in die zughörige Variable previousMillisFPS60 = currentMillis; } } // Wenn der Programmodus auf einen höheren Wert sprngt, wird er zurück auf 0 gesetzt und beginnt von vorne else { programmode = 0; } // Ausgabe an die serielle Schnittstelle if ((unsigned long)(currentMillis - previousMillisSerialPrint) >= intervalSerialPrint) { //Ausgabe der berechneten Netzteillast an die sereille Schnittstelle Serial.print("Last (berechnet): "); Serial.print(load); Serial.println(" %"); Serial.print("Cooling: "); Serial.println(cooling); Serial.print("Sparkling: "); Serial.println(sparkling); //Ausgabe der Werte von ResponsiveAnalogRead Serial.print("Last (geglaettet): "); Serial.println(rload.getValue()); // if the repsonsive value has change, print out 'changed' if(rload.hasChanged()) { Serial.println("\tchanged"); } // Ausgabe Programmzähler Serial.print("Programm Nr.: "); Serial.println(programmode); //Speichere die aktuelle Zeit in die zughörige Variable previousMillisSerialPrint = currentMillis; } }
Der Sketch verwendet 10.912 Bytes (33%) des Programmspeicherplatzes. Das Maximum sind 32.256 Bytes. Globale Variablen verwenden 567 Bytes (27%) des dynamischen Speichers, 1.481 Bytes für lokale Variablen verbleiben. Das Maximum sind 2.048 Bytes.
Tags: #Arduino #NeoPixel