Neopixel-Feuereffekt für das SilentBase 802 - Programmversion 1

Diesem Programm liegt der Fire2012-Effekt von Mark Kriegsman aus der FastLED-Bibliothek zugrunde, der allerdings so umgeschrieben wurde, dass er mit der Adafruit NeoPixel-Bibliothek funktioniert.

// Beleuchtung BeQuiet SilentBase 802
// Arduino Nano (Every)

//------------------------- Eingebundene Bibliotheken ---------------------//
#include <OneWire.h>                         // Bibliothek für die Kommunikation über OneWire
#include <DallasTemperature.h>               // Bibliothek für die digitalen Temperatursensoren DS18B20
#include <Adafruit_NeoPixel.h>               // 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 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
int numPixels = 71;           // Anzahl der NeoPixel
float temperature;            // Die höchste an den drei Sensoren gemessene Temperatur
int load;                     // Variable repäsentiert später die Differenz zwischen minimaler und maximaler Netzteillast in Prozent

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 sparkling;                // Variable für Beleuchtungseffekt Flammen
int cooldown;
static byte heat[71];         // Ein Array für die Temperaturwerte
byte t192;                    // Variable für Beleuchtungseffekt Flammen
  
// Definiert die NeoPixel-Strips
Adafruit_NeoPixel strip1 = Adafruit_NeoPixel(numPixels, NEOPIN1, NEO_GRB + NEO_KHZ800);
Adafruit_NeoPixel strip2 = Adafruit_NeoPixel(numPixels, NEOPIN2, NEO_GRB + 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 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 = 5;         // Delay für Effekte
int intervalSerialPrint = 1000;  // Delay für serielle Ausgabe


void setup() {
  digitalWrite(POWERPIN, LOW);                  // Schaltet den MOSFET aus
  Serial.begin(115200);
  
  // Initialisiere die NeoPixel-Pins
  pinMode(NEOPIN1, OUTPUT);
  pinMode(NEOPIN2, OUTPUT);
  pinMode(POWERPIN, OUTPUT);
  pinMode(LEDPIN1, OUTPUT);
  pinMode(LEDPIN2, OUTPUT);
  
 
  // Initialisiere die NeoPixel-Strips
  digitalWrite(POWERPIN, HIGH);
  
  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
  
  // Start up the 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);

  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);

delay (2000);
}

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
    /*
    digitalWrite(POWERPIN, HIGH);                  // Schaltet den MOSFET durch
    
    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
    */
  }

// 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: ");
    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 = 0;
    temperature = tempSensor0;
    if(tempSensor1 > temperature) {
      temperature = tempSensor1;
    }
    if(tempSensor2 > temperature) {
      temperature = tempSensor2;
    }
    #ifdef DEBUG_SENSOR
    Serial.print("Temperature: ");
    Serial.print(temperature);
    Serial.println(" °C");
    #endif
    
    // Berechenen der Blinkfrequenz von LED2
    if(temperature < temperature_min) {
      temperature = temperature_min;
    }
    if(temperature > temperature_max) {
      temperature = temperature_max;
    }
    intervalLED = map(temperature, temperature_min, temperature_max, 1000, 250);
    #ifdef DEBUG_SENSOR
    Serial.print("Blink frequency LED2: "); Serial.println(intervalLED); Serial.print(" ms");
    #endif
    
    // Berechnung der Netzteillast in Prozent: Bei ruhendem Desktop soll der Wert 0 sein, bei maximaler Auslastung 100
    load = map(temperature, temperature_min, temperature_max, 0, 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 = map(temperature, temperature_min, temperature_max, 100, 20);

    // 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 = map(temperature, temperature_min, temperature_max, 30, 200);

    #ifdef DEBUG_EFFECT
    //Serial.print("load: ");
    Serial.print(load); Serial.print("\t");
    //Serial.print("cooling: ");
    Serial.print(cooling); Serial.print("\t");
    //Serial.print("sparkling: ");
    Serial.println(sparkling);
    #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
      if ((unsigned long)(currentMillis - previousMillisEffect) >= intervalEffect) {

      // 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
      if( t192 > 0x80) {                                                // hottest
        for(int k = numPixels - 1; k >= 2; k--) {
          heat[k] = (heat[k - 1] + heat[k - 2] + heat[k - 2]) / 3;      // muss noch angepasst werden
        }
          } else if( t192 > 0x40 ) {                                    // middle
          for(int k = numPixels - 1; k >= 2; k--) {
          heat[k] = (heat[k - 1] + heat[k - 2] + heat[k - 2]) / 3;      // muss noch angepasst werden
        }
          } else {                                                      // coolest
          for(int k = numPixels - 1; k >= 2; k--) { 
          heat[k] = (heat[k - 1] + heat[k - 2] + heat[k - 2]) / 3;      // muss noch angepasst werden
        }
          } 
    
    // Step 3.  Randomly ignite new 'sparks' near the bottom
      if(random(255) < sparkling) {
        int 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
        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(map(j, 0, numPixels, numPixels, 0), 255, 255, heatramp);
          strip2.setPixelColor(map(j, 0, numPixels, numPixels, 0), 255, 255, heatramp);
          } else if( t192 > 0x40 ) {             // middle
          strip1.setPixelColor(map(j, 0, numPixels, numPixels, 0), 255, heatramp, 0);
          strip2.setPixelColor(map(j, 0, numPixels, numPixels, 0), 255, heatramp, 0);
          } else {                               // coolest
          strip1.setPixelColor(map(j, 0, numPixels, numPixels, 0), heatramp, 0, 0);
          strip2.setPixelColor(map(j, 0, numPixels, numPixels, 0), heatramp, 0, 0);  
          }
        }
      strip1.show();
      strip2.show();
      
    
    //Speichere die aktuelle Zeit in die zughörige Variable
    previousMillisEffect = currentMillis;
    }
}


// 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();
}