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