====== Neopixel-Feuereffekt für das SilentBase 802 - Programmversion 4 ====== Nachdem in [[arduino:silentbase_802_neopixel:programmversion_3|Programmversion 3]] der Fokus auf der Beschleunigung des Codes lag, spielt Version 4 mit der Möglichkeit der FastLED-Bibliothek, Farbpaletten hinzuzufügen. // Beleuchtung BeQuiet SilentBase 802 // Arduino Nano (Every) //------------------------- Eingebundene Bibliotheken ---------------------// #include // Bibliothek für die Kommunikation über OneWire #include // Bibliothek für die digitalen Temperatursensoren DS18B20 #include // Bibliothek für die NeoPixel // Debug-Level //#define DEBUG_EFFECT // Ausgabe sehr vieler Daten an die serielle Schnittstelle //#define DEBUG_SENSOR // Ausgabe der Sensordaten und Betriebszustände an die serielle Schnittstelle //------------------------- Definition der Inputs und Outputs ---------------------// #define NEOPIN1 2 // NeoPixel-Strip rechte Seite #define NEOPIN2 4 // NeoPixel-Strip linke Seite #define POWERPIN 3 // Schaltet über den MOSFET die NeoPixel-Streifen ein #define LEDPIN1 6 // LED 1 #define LEDPIN2 8 // LED 2 #define ONE_WIRE_BUS 11 // Datenleitung für die Temperatursensoren DS18B20 //------------------------- Definition der NeoPixel-Streifen ---------------------// #define COLOR_ORDER GRB #define CHIPSET WS2812B #define NUM_LEDS 71 #define BRIGHTNESS 200 //-------------------------- Definition der Auflösung der Temperatursensoren ----// // 9 bit resolution: 0,5°C increments, takes 94 ms for A/D conversion // 10 bit resolution: 0,25°C increments, takes 188 ms for A/D conversion // 11 bit resolution: 0,125°C increments, takes 375 ms for A/D conversion // 12 bit resolution: 0,0625°C increments, takes 750 ms for A/D conversion #define TEMPERATURE_PRECISION 11 // Setup a oneWire instance to communicate with any OneWire devices (not just Maxim/Dallas temperature ICs) OneWire oneWire(ONE_WIRE_BUS); // Pass our oneWire reference to Dallas Temperature. DallasTemperature sensors(&oneWire); // arrays to hold device addresses DeviceAddress sensor0, sensor1, sensor2; // Definiert die Variablen bool gReverseDirection = true; // Kehrt den Effekt um, wenn wahr float temperature; // Die höchste an den drei Sensoren gemessene Temperatur float temperature_min = 20; // Konfiguriert den unteren Grenzwert der Gehäusetemperatur float temperature_max = 40; // Konfiguriert den oberen Grenzwert der Gehäusetemperatur boolean LEDstate; boolean POWERstate = 0; // Wird "1" sobald der Loop aufgerufen wird int cooling; // Variable für Beleuchtungseffekt Flammen int sparking; // Variable für Beleuchtungseffekt Flammen static byte heat[71]; // Ein Array für die Temperaturwerte // Definiert die NeoPixel-Strips CRGB strip[NUM_LEDS]; // Fire2012 with programmable Color Palette // // This code is the same fire simulation as the original "Fire2012", // but each heat cell's temperature is translated to color through a FastLED // programmable color palette, instead of through the "HeatColor(...)" function. // // Four different static color palettes are provided here, plus one dynamic one. // // The three static ones are: // 1. the FastLED built-in HeatColors_p -- this is the default, and it looks // pretty much exactly like the original Fire2012. // // To use any of the other palettes below, just "uncomment" the corresponding code. // // 2. a gradient from black to red to yellow to white, which is // visually similar to the HeatColors_p, and helps to illustrate // what the 'heat colors' palette is actually doing, // 3. a similar gradient, but in blue colors rather than red ones, // i.e. from black to blue to aqua to white, which results in // an "icy blue" fire effect, // 4. a simplified three-step gradient, from black to red to white, just to show // that these gradients need not have four components; two or // three are possible, too, even if they don't look quite as nice for fire. // // The dynamic palette shows how you can change the basic 'hue' of the // color palette every time through the loop, producing "rainbow fire". CRGBPalette16 gPal; // Definiert die Tracking-Variablen für die IF-Abfragen unsigned long previousMillisSensors = 0; unsigned long previousMillisLED = 0; unsigned long previousMillisEffect = 0; unsigned long previousMillisSerialPrint = 0; // Definiert die Intervalle für die IF-Abfragen int intervalSensors = 1000; // Delay für Auslesen der Temperatursensoren int intervalLED = 500; // Delay für die Ausgabe der Temperatur als Blinkfrequenz der LED2 int intervalEffect = 15; // Delay für Effekte int intervalSerialPrint = 1000; // Delay für serielle Ausgabe //-------------------------- Setup ------------------------------------------------------------------// void setup() { digitalWrite(POWERPIN, LOW); // Schaltet den MOSFET aus Serial.begin(115200); // Initialisiere die Output-Pins pinMode(NEOPIN1, OUTPUT); pinMode(NEOPIN2, OUTPUT); pinMode(POWERPIN, OUTPUT); pinMode(LEDPIN1, OUTPUT); pinMode(LEDPIN2, OUTPUT); // Initialisiere die NeoPixel-Strips digitalWrite(POWERPIN, HIGH); // Schaltet den MOSFET ein FastLED.addLeds(strip, NUM_LEDS).setCorrection( TypicalLEDStrip ); FastLED.addLeds(strip, NUM_LEDS).setCorrection( TypicalLEDStrip ); FastLED.setBrightness( BRIGHTNESS ); // This first palette is the basic 'black body radiation' colors, // which run from black to red to bright yellow to white. gPal = HeatColors_p; // These are other ways to set up the color palette for the 'fire'. // First, a gradient from black to red to yellow to white -- similar to HeatColors_p // gPal = CRGBPalette16( CRGB::Black, CRGB::Red, CRGB::Yellow, CRGB::White); // Second, this palette is like the heat colors, but blue/aqua instead of red/yellow // gPal = CRGBPalette16( CRGB::Black, CRGB::Blue, CRGB::Aqua, CRGB::White); // Third, here's a simpler, three-step gradient, from black to red to white // gPal = CRGBPalette16( CRGB::Black, CRGB::Red, CRGB::White); // Start up the sensor library sensors.begin(); // locate devices on the bus Serial.print("Locating devices..."); Serial.print("Found "); Serial.print(sensors.getDeviceCount(), DEC); Serial.println(" devices."); // Search for devices on the bus and assign based on an index. Ideally, // you would do this to initially discover addresses on the bus and then // use those addresses and manually assign them (see above) once you know // the devices on your bus (and assuming they don't change). // // method 1: by index if (!sensors.getAddress(sensor0, 0)) Serial.println("Unable to find address for Device 0"); if (!sensors.getAddress(sensor1, 1)) Serial.println("Unable to find address for Device 1"); if (!sensors.getAddress(sensor2, 2)) Serial.println("Unable to find address for Device 2"); // show the addresses we found on the bus Serial.print("Device 0 Address: "); printAddress(sensor0); Serial.println(); Serial.print("Device 1 Address: "); printAddress(sensor1); Serial.println(); Serial.print("Device 2 Address: "); printAddress(sensor2); Serial.println(); // set the resolution to 11 bit per device sensors.setResolution(sensor0, TEMPERATURE_PRECISION); sensors.setResolution(sensor1, TEMPERATURE_PRECISION); sensors.setResolution(sensor2, TEMPERATURE_PRECISION); #ifdef DEBUG_SENSOR Serial.print("Device 0 Resolution: "); Serial.println(sensors.getResolution(sensor0), DEC); Serial.print("Device 1 Resolution: "); Serial.println(sensors.getResolution(sensor1), DEC); Serial.print("Device 2 Resolution: "); Serial.println(sensors.getResolution(sensor2), DEC); #endif delay (2000); } //-------------------------- Loop -------------------------------------------------------------------// void loop() { // Aktuelle Zeit abfragen unsigned long currentMillis = millis(); // Schaltet die NeoPixel ein und initialisiert die NeoPixel if(POWERstate == 0) { POWERstate = 1; digitalWrite(LEDPIN1, HIGH); // Schaltet LED1 ein } // Auslesen der Temperatursensoren und Berechnen einiger Variablen zur Beeinflussung des Effekts if ((unsigned long)(currentMillis - previousMillisSensors) >= intervalSensors) { // Request to all devices on the bus //temperature conversion - non-blocking / async unsigned long start = micros(); sensors.setWaitForConversion(false); // makes it async sensors.requestTemperatures(); sensors.setWaitForConversion(true); unsigned long stop = micros(); #ifdef DEBUG_SENSOR Serial.print("Time used for reading sensors: "); Serial.print(stop - start); Serial.println(" microseconds"); // print the device information printData(sensor0); printData(sensor1); printData(sensor2); #endif // Berechnen der höchsten Temperatur an den verfügbaren Sensoren float tempSensor0 = sensors.getTempC(sensor0); float tempSensor1 = sensors.getTempC(sensor1); float tempSensor2 = sensors.getTempC(sensor2); temperature = max(tempSensor0, tempSensor1); temperature = max(temperature, tempSensor2); #ifdef DEBUG_SENSOR Serial.print("Highest temperature: "); Serial.print(temperature); Serial.println(" °C"); #endif // Berechenen der Blinkfrequenz von LED2 constrain(temperature, temperature_min, temperature_max); intervalLED = map(temperature, temperature_min, temperature_max, 500, 100); #ifdef DEBUG_SENSOR Serial.print("Blink frequency LED2: "); Serial.print(intervalLED); Serial.println(" ms"); #endif // Berechnung von Cooling für den Effekt Fire 2012: Legt fest, wie stark die aufsteigenden Flammen abkühlen. // Weniger Cooling = höhere Flammen, mehr Cooling = kürzere Flammen. // Werte zwischen 20 und 100 sind empfohlen, ein guter Standard ist 55. cooling = map(temperature, temperature_min, temperature_max, 100, 40); // Berechnung von Sparking für den Effekt Fire2012: Legt fest, wie oft ein Funke auflohdert. // Höhere Werte = stürmisches Feuer, niedrige Werte = ruhigeres Feuer. // Werte zwischen 50 und 200 sind empfohlen, ein guter Standard ist 120. sparking = map(temperature, temperature_min, temperature_max, 30, 100); #ifdef DEBUG_SENSOR //Serial.print("cooling: "); Serial.print(cooling); Serial.print("\t"); //Serial.print("sparkling: "); Serial.println(sparking); #endif //Speichere die aktuelle Zeit in die zughörige Variable previousMillisSensors = currentMillis; } // Ausgabe der Temperatur als Blinkfrequenz der LED1 if ((unsigned long)(currentMillis - previousMillisLED) >= intervalLED) { if(LEDstate == 0) { digitalWrite(LEDPIN2, HIGH); //Serial.println("LED an"); LEDstate = 1; } else if(LEDstate == 1) { digitalWrite(LEDPIN2, LOW); //Serial.println("LED aus"); LEDstate = 0; } //Speichere die aktuelle Zeit in die zughörige Variable previousMillisLED = currentMillis; } // Feuer-Effekt: Fire2012 by Mark Kriegsman, July 2012 if ((unsigned long)(currentMillis - previousMillisEffect) >= intervalEffect) { #ifdef DEBUG_EFFECT unsigned long start = micros(); // Zeitstempel Start #endif // Add entropy to random number generator; we use a lot of it. random16_add_entropy( random()); // This basic one-dimensional 'fire' simulation works roughly as follows: // There's a underlying array of 'heat' cells, that model the temperature // at each point along the line. Every cycle through the simulation, // four steps are performed: // 1) All cells cool down a little bit, losing heat to the air // 2) The heat from each cell drifts 'up' and diffuses a little // 3) Sometimes randomly new 'sparks' of heat are added at the bottom // 4) The heat from each cell is rendered as a color into the leds array // The heat-to-color mapping uses a black-body radiation approximation. // Temperature is in arbitrary units from 0 (cold black) to 255 (white hot). // Step 1. Cool down every cell a little for( int i = 0; i < NUM_LEDS; i++) { heat[i] = qsub8( heat[i], random8(0, ((cooling * 10) / NUM_LEDS) + 2)); } // Step 2. Heat from each cell drifts 'up' and diffuses a little for( int k= NUM_LEDS - 1; k >= 2; k--) { heat[k] = (heat[k - 1] + heat[k - 2] + heat[k - 2] ) / 3; } // Step 3. Randomly ignite new 'sparks' of heat near the bottom if( random8() < sparking ) { int y = random8(7); heat[y] = qadd8( heat[y], random8(160,255) ); } // Step 4. Map from heat cells to LED colors for( int j = 0; j < NUM_LEDS; j++) { // Scale the heat value from 0-255 down to 0-240 // for best results with color palettes. uint8_t colorindex = scale8( heat[j], 240); CRGB color = ColorFromPalette( gPal, colorindex); int pixelnumber; if( gReverseDirection ) { pixelnumber = (NUM_LEDS-1) - j; } else { pixelnumber = j; } strip[pixelnumber] = color; //strip2[pixelnumber] = color; } FastLED.show(); // display this frame #ifdef DEBUG_EFFECT unsigned long stop = micros(); // Zeitstempel Stopp // Ausgabe der Zeit in Microsekunden, die für die Berechnung des Effekts benötigt wird //Serial.print("Time used for calculating effect: "); Serial.println(stop - start); //Serial.println(" microseconds"); #endif //Speichere die aktuelle Zeit in die zughörige Variable previousMillisEffect = currentMillis; } } //-------------------------- Funktionen --------------------------------------// // function to print a device address void printAddress(DeviceAddress deviceAddress) { for (uint8_t i = 0; i < 8; i++) { // zero pad the address if necessary if (deviceAddress[i] < 16) Serial.print("0"); Serial.print(deviceAddress[i], HEX); } } // function to print the temperature for a device void printTemperature(DeviceAddress deviceAddress) { float tempC = sensors.getTempC(deviceAddress); Serial.print("Temp C: "); Serial.print(tempC); } // function to print a device's resolution void printResolution(DeviceAddress deviceAddress) { Serial.print("Resolution: "); Serial.print(sensors.getResolution(deviceAddress)); Serial.println(); } // main function to print information about a device void printData(DeviceAddress deviceAddress) { Serial.print("Device Address: "); printAddress(deviceAddress); Serial.print(" "); printTemperature(deviceAddress); Serial.println(); } {{tag>Arduino Neopixel PC}}