// = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = //
// HAW Hamburg | DMI | Medientechnik //
// Technisches Projekt (TPV / TPD) WiSe 22/23 & SoSe 23 //
// haw_tpd_desklight.ino - Desklight, das sich anpassende Licht //
// Abgabe SoSe 23 //
// Authors: //
// Cedric Grote, 2689357 //
// Moses Marquis, 2684576 //
// = = = = = = = = = = = = = = = = = = = = = = = = = = = = = //
// Libraries
// - Importiert die benötigten Libraries
#include <LiquidCrystal_I2C.h> // Nutzen des Displays
#include <LinkedList.h> // Ermöglicht "dynamische Arrays"
#include <FastLED.h> // Ansteuerung der LED-Streifen
#include <EasyButton.h> // Einfache Handhabung und Entprellen der Buttons
// Pins
// - Deklarieren der verbundenen Pins
#define LDR_PIN 34 // Analog Pin des Lichtsensors
#define ENCODER_CLK 18 // „Serial Clock“ Pin des Drehencoders
#define ENCODER_DT 19 // Datenpin 2 des Drehencoders
#define ENCODER_SW 4 // Switch Pin des Drehencoders (Button)
#define DATA_PIN 5 // Datenpin für den LED-Streifen
// Timer
// - Variablen für getakteten Ablauf ohne Delay()
long timer_refreshRate; // Timer für Leuchtfunktion
long timestamp_refreshRate = 1;
long timer_partyTempo; // Explizieter Timer für Party-Leuchtfunktion
long timestamp_partyTempo = 100;
long timer_idle; // Timer für Inaktivität des Nutzenden
long timestamp_screensaver = 10000;
// LEDS
// - Deklariert die LEDs
#define NUM_LEDS 38
CRGB leds[NUM_LEDS];
// Enums
// - Alternative Namesgebung für bestimmte Werte
enum e_Type{ // Status des Menüs
e_Screensaver, // 0
e_Mode, // 1
e_Attribute, // 2
e_Exit // 3
};
// CustomChar
// - Array für eigene Zeichen
byte smiley1[8] = { // Normaler Smiley
0b00000,
0b01010,
0b01010,
0b01010,
0b00000,
0b10001,
0b01110,
0b00000
};
byte smiley2[8] = { // Zwinkender Smiley
0b00000,
0b00010,
0b01010,
0b00010,
0b00000,
0b10001,
0b01110,
0b00000
};
// Display
// - Instanziert ein Display mit Hilfe der Klasse "LiquidCrystal_I2C"
LiquidCrystal_I2C lcd(0x27, 20, 4);
//0x3f -> IRL
//0x27 -> Wokwi
// Klassen
// - Ab hier werden alle essentielle Klassen instanziert
// == Farbe ==
// Instanziert Objekte, welche Name und Farbinformationen
// vereinen und Platz für zusätzliche Funktionen bieten können
class Color {
public:
String _name; // Name der Farbe um diese im Menü anzuzeigen
CRGB _color; // Farbwerte um diese bearbeiten und verwenden zu können
Color(String name, uint8_t r, uint8_t g, uint8_t b) {
setName(name);
setColor(r,g,b);
}
void setName(String name) {_name = name;}
void setColor(uint8_t r, uint8_t g, uint8_t b) {_color = CRGB(r,g,b);}
String getName() {return _name;}
CRGB getColor() {return _color;}
};
// == Attribute ==
// Instanziert Objekte, welche Name und Werteinformationen
// vereinen und Platz für zusätzliche Funktionen bieten können.
// Bildet die Basis für Leuchtenfunktionen und die letzte
// Ebene des Menüs.
class Attribute {
public:
String _name; // Anzeigename des Attributes
String _suffix; // i.d.R. Einheit des Attributes
String _valuePrint; // Anzeigewert des Attributes
int _value; // Datenwert des Attributes
int _minValue; // Minimaler einzustellener Grenzwert
int _maxValue; // Maximal einzustellener Grenzwert
int _step; // Größe der Werteveränderung
e_Type _type = e_Attribute; // Legt fest ob es sich über ein echtes Attribut oder "Exit" handelt
Attribute(String name, String suffix, int value, int minValue, int maxValue, int step) {
setName(name);
setSuffix(suffix);
setValue(value);
setMinValue(minValue);
setMaxValue(maxValue);
setStep(step);
}
Attribute(String name, String suffix, byte value, byte minValue, byte maxValue, byte step, e_Type type) {
setName(name);
setSuffix(suffix);
setValue(value);
setMinValue(minValue);
setMaxValue(maxValue);
setStep(step);
setType(type);
}
void setName(String name) {_name = name;}
void setSuffix(String suffix) {_suffix = suffix;}
void setValue(int value) {_value = value;}
void setMinValue(int minValue) {_minValue = minValue;}
void setMaxValue(int maxValue) {_maxValue = maxValue;}
void setStep(int step) {_step = step;}
void setType(e_Type type) {_type = type;}
String getName() {return _name;}
String getSuffix() {return _suffix;}
int getValue() {return _value;}
int getMinValue() {return _minValue;}
int getMaxValue() {return _maxValue;}
int getStep() {return _step;}
e_Type getType() {return _type;}
void increase() { // Funktion die den Wert um den hinterlegten Wert erhöht
if (_value + _step <= _maxValue) {
_value = _value + _step;
}
}
void decrease() { // Funktion die den Wert um den hinterlegten Wert verringert
if (_value +- _step >= _minValue) {
_value = _value - _step;
}
}
};
// == Mode ==
// Instanziert Objekte, welches massiv für die Menüstruktur verantwortlich ist.
// Beinhaltet unter Anderem Name und "Untermenü Punkte".
// Beinhaltet außerdem die Möglichkeit direkt eine Funktion zugewiesen zu bekommen.
class Mode {
protected:
using CallBack = void (*)(); // "Pointer auf eine zugewiesene Funktion"
CallBack callBack = nullptr; // Instanziert Callback als Nullpointer
public:
String _name; // Anzeigename dess Modes
LinkedList<Attribute*> _attributeList; // Liste möglicher zugewiesener Attribute
LinkedList<Mode*> _modeList; // Liste möglicher zugewiesener Modi
e_Type _type = e_Mode; // Legt fest ob der Mode Untermodi oder Attribute enthält
Mode(String name) {
setName(name);
}
Mode(String name, e_Type type) {
setName(name);
setType(type);
}
void setName(String name) {_name = name;}
void setType(e_Type type) {_type = type;}
String getName() {return _name;}
e_Type getType() {return _type;}
void assignFunction(CallBack callBack){ // Weißt dem Mode seine Leuchtenfunktion zu
this->callBack = callBack;
}
void runMode(){ // Ruft die zugewiesene Leuchtenfunktion auf, solange vorhanden
if(callBack) callBack();
}
};
// == Menü ==
// Bildet das Ober-Objekt der Menüstruktur und beinhaltet
// alle wichtigen Funktionen und Variablen, welche für die Menüführung
// von Bedeutung sind.
class Menu {
public:
String _name; // Name des Menüs (Der gesamten Leuchte)
LinkedList<Mode*> _modeList; // Liste zugewiesener Modis
LinkedList<int> _menuHistory; // Ansatz für Menüverlauf
int _menuPosition = 0; // Aktuelle Menüposition
int _menuOffset = 0; // Aktuelles Menüoffset -> Da auf dem Display nur 3 Zeilen Platz ist
Mode* _mainMode; // Haupt und Startmode der Leuchte
Mode* _currentMode; // Aktueller Mode der Lampe
Mode* _runningMode; // Aktuell Laufender Mode
Attribute* _currentAttribute; // Aktuelles ausgewähltes Attribute
bool _waitingForInput = false; // Sagt aus, ob der Nutzer ein Attribute verändert oder nicht
Menu(String name) {
setName(name);
}
void setName(String name) {_name = name;}
void setMainMode(Mode* mainMode) {_mainMode = mainMode;}
void setRunningMode(Mode* runningMode) {
if (runningMode != _mainMode) {
_runningMode = runningMode;
}
}
void setCurrentMode(Mode* currentMode) {
setRunningMode(currentMode);
_currentMode = currentMode;
}
void setCurrentAttribute(Attribute* currentAttribute) {_currentAttribute = currentAttribute;}
String getName() {return _name;}
Mode getCurrentMode() {return *_currentMode;}
Attribute getCurrentAttribute() {return *_currentAttribute;}
void drawBootScreen() { // Zeichnet den Startladescreen
lcd.clear();
lcd.setCursor(0,0);
lcd.print("////////////////////");
lcd.setCursor(0,1);
lcd.print("//// DESK LIGHT ////");
lcd.setCursor(0,2);
lcd.print("//// Loading. ////");
lcd.setCursor(0,3);
lcd.print("////////////////////");
delay(1000);
lcd.setCursor(0,2);
lcd.print("//// Loading.. ////");
delay(1000);
lcd.setCursor(0,2);
lcd.print("//// Loading... ////");
delay(500);
}
void drawScreenSaver() { // Zeichnet den Bildschirmschoner
lcd.clear();
lcd.setCursor(0,0);
lcd.print("////////////////////");
lcd.setCursor(0,1);
lcd.print("//// DESK LIGHT ////");
lcd.setCursor(0,2);
lcd.print("////////////////////");
if (_runningMode->_name.length() <= 18) {
lcd.setCursor(((20-((_runningMode->_name).length()))/2)-1,2);
lcd.print(" ");
lcd.print(_runningMode->_name);
lcd.print(" ");
} else if (_runningMode->_name.length() <= 20) {
lcd.setCursor(((20-((_runningMode->_name).length()))/2),2);
lcd.print(_runningMode->_name);
} else {
lcd.setCursor(0,2);
for (int i = 0; i < 20; i++) {
lcd.print(_runningMode->_name[i]);
}
}
lcd.setCursor(0,3);
lcd.print("////////////////////");
}
void drawHeader(){ // Zeichnet die Kopfzeile des Menüs
if (_waitingForInput) {
lcd.setCursor(0,0);
lcd.print(_currentAttribute->getName());
} else if (!_waitingForInput) {
lcd.setCursor(0,0);
lcd.print(_currentMode->getName());
}
}
void drawContent(){ // Zeichnet den Inhalt des Menüs
switch (_currentMode->getType()) {
case 0: //Screensaver
break;
case 1: //Mode
for (int i = 0; i < 3; i++) {
if (i < _currentMode->_modeList.size()) {
lcd.setCursor(2,i + 1);
lcd.print(_currentMode->_modeList.get((i+_menuOffset))->getName());
}
}
break;
case 2: //Attribute
for (int i = 0; i < 3; i++) {
if (i < _currentMode->_attributeList.size()) {
lcd.setCursor(2,i + 1);
lcd.print(_currentMode->_attributeList.get((i+_menuOffset))->getName());
}
}
break;
case 3: //Exit
break;
}
}
void drawArrow(){ // Zeichnet die Pfeile, die die aktuelle Menüposition anzeigen
for (int i = 1; i < 4; i++){
lcd.setCursor(0,i);
lcd.print(" ");
}
lcd.setCursor(0,_menuPosition+1);
lcd.print(">");
}
void drawStatus(){ // Zeichnet den ersten Buchstaben des laufenden Modes
lcd.setCursor(17, 0);
lcd.print("|");
lcd.print((_runningMode->getName())[0]);
lcd.print("|");
}
void drawValue(){ // Zeichnet bei Nutzereingabe den aktuellen Wert mit Suffix
lcd.setCursor(0,2);
lcd.print("> ");
lcd.print(_currentAttribute->_valuePrint);
lcd.print(" ");
lcd.print(_currentAttribute->_suffix);
}
void updateMenu() { // Updated das Menu (Zusammenfassung einzelner obriger Funktionen)
lcd.clear();
drawHeader();
drawContent();
drawArrow();
drawStatus();
}
void updateInputScreen() { // Updatet den Bildschirm bei Eingabe durch die Nutzenden
lcd.clear();
drawHeader();
drawValue();
}
void menuDown() { // Überprüft ob die Nutzenden im Menü weiter nach Unten dürfen
int size;
switch(_currentMode->_type) {
case e_Mode: size = _currentMode->_modeList.size(); break;
case e_Attribute: size = _currentMode->_attributeList.size(); break;
}
if (_menuPosition + 1 < 3 && _menuPosition + 1 <= size - 1) {
_menuPosition++;
} else {
if (_menuPosition + _menuOffset < size - 1) {
_menuOffset++;
} else {
}
}
}
void menuUp() { // Überprüft ob die Nutzenden im Menü weiter nach Oben dürfen
int size;
switch(_currentMode->_type) {
case e_Mode: size = _currentMode->_modeList.size(); break;
case e_Attribute: size = _currentMode->_attributeList.size(); break;
}
if (_menuOffset > 0) {
_menuOffset--;
} else if (_menuPosition - 1 >= 0) {
_menuPosition--;
} else {
}
}
void menuEnter() { // Bestätigt die Eingabe der Nutzenden und handelt je nach Status
switch (_currentMode->getType()) {
case 0: // Screensaver
break;
case 1: // Mode
if (_currentMode->_modeList.get(_menuPosition+_menuOffset)->_type != e_Exit) {
setCurrentMode(_currentMode->_modeList.get(_menuPosition+_menuOffset));
_menuPosition = 0;
_menuOffset = 0;
_menuHistory.add(_menuPosition);
updateMenu();
} else if (_currentMode->_modeList.get(_menuPosition+_menuOffset)->_type == e_Exit) {
setCurrentMode(_mainMode);
_menuPosition = 0;
_menuOffset = 0;
updateMenu();
}
break;
case 2: // Attribute
if (_currentMode->_attributeList.get(_menuPosition+_menuOffset)->_type != e_Exit) {
setCurrentAttribute(_currentMode->_attributeList.get(_menuPosition+_menuOffset));
_menuPosition = 0;
_menuOffset = 0;
_menuHistory.add(_menuPosition);
_waitingForInput = true;
updateInputScreen();
} else if (_currentMode->_attributeList.get(_menuPosition+_menuOffset)->_type == e_Exit) {
setCurrentMode(_mainMode);
_menuPosition = 0;
_menuOffset = 0;
updateMenu();
}
break;
case 3: // Exit
setCurrentMode(_mainMode);
_menuPosition = 0;
_menuOffset = 0;
updateMenu();
break;
}
}
};
// Instanzierung des MainMenu Objektes
// Muss vor Encoder Klasse geschehen, da es im Folgenden verwendet wird.
Menu MainMenu("Main Menu");
// == Encoder ==
// Instanziert Objekte, welches für die Verarbeitung der Encoder Signale zuständig ist
// und eingehende Signale in Menüfunktionen übersetzt.
class Encoder {
private:
int _pinClk; //Clk Pin
int _pinDt; //Dt Pin
int _pinSw; //Sw Pin
int _newClk; //Zwischenspeicher Variable
int _dtValue; //Zwischenspeicher Variable
int _lastClk; //Zwischenspeicher Variable
public:
Encoder(int pinClk, int pinDt) {
setPins(pinClk, pinDt);
}
void setPins(int pinClk, int pinDt) {
_pinClk = pinClk;
_pinDt = pinDt;
}
void init() { // Initialisiert die Pins des Encoders
pinMode(_pinClk, INPUT_PULLUP);
pinMode(_pinDt, INPUT_PULLUP);
_lastClk = HIGH;
}
void read() { // Liest aktuelle Werteveränderungen aus und handelt dementsprechend
_newClk = digitalRead(_pinClk);
if (_newClk != _lastClk) {
_lastClk = _newClk;
int _dtValue = digitalRead(_pinDt);
if (_newClk == LOW && _dtValue == HIGH) { //Encoder dreht sich im Uhrzeigersinn
if (!MainMenu._waitingForInput) {
MainMenu.menuDown();
MainMenu.updateMenu();
} else if (MainMenu._waitingForInput) {
MainMenu._currentAttribute->increase();
MainMenu._currentMode->runMode();
MainMenu.updateInputScreen();
}
}
if (_newClk == LOW && _dtValue == LOW) { //Encoder dreht sich gegen den Uhrzeigersinn
if (!MainMenu._waitingForInput) {
MainMenu.menuUp();
MainMenu.updateMenu();
} else if (MainMenu._waitingForInput) {
MainMenu._currentAttribute->decrease();
MainMenu._currentMode->runMode();
MainMenu.updateInputScreen();
}
}
timer_idle = millis();
}
}
};
// Allgemeine Funktion, welche an den Button übergeben wird,
// und bei jedem Knopfdruck ausgeführt wird.
void onPressed() {
if (!MainMenu._waitingForInput) {
MainMenu.menuEnter();
} else if (MainMenu._waitingForInput) {
MainMenu._waitingForInput = false;
MainMenu.updateMenu();
}
timer_idle = millis();
}
// EasyButton(ButtonPin)
EasyButton button(ENCODER_SW); //erstellt einen Button für das Drücken des Encoders
// Encoder(int pinClk, int pinDt, int pinSw)
Encoder encoder(ENCODER_CLK,ENCODER_DT); //erstellt einen Encoder für das Drehen des Encoders
// Attribute(String title, String suffix, byte value, byte minValue, byte maxValue, byte step)
Attribute Static_Dim("Intensity", "%", 100, 0, 100, 10);
Attribute Static_MixColor("MixColor", "", 0, 0, 12, 1);
Attribute Static_CTC("ColorTemp", "K", 10, 0, 50, 1);
Attribute Static_Raw_r("Red Channel", "raw value", 255, 0, 255, 5);
Attribute Static_Raw_g("Green Channel", "raw value", 255, 0, 255, 5);
Attribute Static_Raw_b("Blue Channel", "raw value", 255, 0, 255, 5);
Attribute Dynamic_Dim("Max. Intensity", "%", 100, 0, 100, 10);
Attribute Dynamic_MinLimit("Min Limit", "raw value", 4000, 0, 4000, 100);
Attribute Dynamic_MaxLimit("Max Limit", "raw value", 1800, 0, 4000, 100);
Attribute Dynamic_MixColor("MixColor", "", 0, 0, 12, 1);
Attribute Dynamic_CTC("ColorTemp", "K", 10, 0, 50, 1);
Attribute Party_Dim("Intensity", "%", 100, 0, 100, 10);
Attribute Party_Tempo("Tempo", "%", 50, 0, 100, 10);
Attribute Party_MixColor_Low("MixColor Low", "", 0, 0, 12, 1);
Attribute Party_MixColor_High("MixColor High", "", 1, 0, 12, 1);
Attribute Attribute_Exit("Save & Quit", "", 0, 0, 0, 0, e_Exit);
// Liste, welche alle zukünftigen Color Elemente in einer "MixColor" Palette zusammenfasst
LinkedList<Color*> mixColorList;
// Color(String name, uint8_t r, uint8_t g, uint8_t b)
Color White("White",255,255,255);
Color Red("Red",255,0 ,0 );
Color Orange("Orange",255,70,0);
Color Yellow("Yellow",255,150,0);
Color FernGreen("Fern Green",128,255,0);
Color Green("Green",0,255,0);
Color SeaGreen("Sea Green",0,255,128);
Color Cyan("Cyan",0,255,255);
Color LightBlue("Light Blue",0,128,255);
Color Blue("Blue",0,0,255);
Color Violet("Violet",128,0,255);
Color Magenta("Magenta",255,0,255);
Color Pink("Pink",255,0,128);
// Mode(String name, e_Type type)
Mode Main("Main Menu", e_Mode);
Mode Static("Static", e_Mode);
Mode StaticMixColor("Static MixColor", e_Attribute);
Mode StaticCTC("Static CTC", e_Attribute);
Mode StaticRaw("Static RAW", e_Attribute);
Mode Dynamic("Dynamic", e_Mode);
Mode DynamicMixColor("Dynamic MixColor", e_Attribute);
Mode DynamicCTC("Dynamic CTC", e_Attribute);
Mode Party("Party", e_Mode);
Mode PartyRainbow("Party Rainbow", e_Attribute);
Mode PartyPolice("Party Police", e_Attribute);
Mode PartyColorChase("Party Color Chase", e_Attribute);
Mode Mode_Exit("<< Back", e_Exit);
// ================================================
// =========== RUNNING CODE STARTS HERE ===========
// ================================================
// =========== SETUP ============
// ================================================
void setup() {
Serial.begin(115200);
// Initialisieren der LEDs mit Korrekturfaktor
FastLED.addLeds<NEOPIXEL, DATA_PIN>(leds, NUM_LEDS).setCorrection(0Xffa0a0);
FastLED.setTemperature(Halogen);
// Initialisieren des LCD Displays
lcd.init();
lcd.backlight();
// Initialisieren des Buttons
button.begin();
button.onPressed(onPressed);
// Initialisieren des Encoders
encoder.init();
// Erstellen der Smileys
lcd.createChar(0, smiley1);
lcd.createChar(1, smiley2);
// Füllen der MixColor Palette
mixColorList.add(&White);
mixColorList.add(&Red);
mixColorList.add(&Orange);
mixColorList.add(&Yellow);
mixColorList.add(&FernGreen);
mixColorList.add(&Green);
mixColorList.add(&SeaGreen);
mixColorList.add(&Cyan);
mixColorList.add(&LightBlue);
mixColorList.add(&Blue);
mixColorList.add(&Violet);
mixColorList.add(&Magenta);
mixColorList.add(&Pink);
// Zuweisung des Start-Modis ("Default")
MainMenu.setMainMode(&Main);
MainMenu.setCurrentMode(&Main);
MainMenu.setRunningMode(&StaticMixColor);
// Erstellen der Menüstruktur
// == STATIC ==
Main._modeList.add(&Static); // Fügt dem Menü das 1. Obermenü "Statische-Menü" hinzu
// -> MixColor
StaticMixColor._attributeList.add(&Static_Dim);
StaticMixColor._attributeList.add(&Static_MixColor);
StaticMixColor._attributeList.add(&Attribute_Exit);
StaticMixColor.assignFunction(runStaticMixColor);
Static._modeList.add(&StaticMixColor);
// -> CTC
StaticCTC._attributeList.add(&Static_Dim);
StaticCTC._attributeList.add(&Static_CTC);
StaticCTC._attributeList.add(&Attribute_Exit);
StaticCTC.assignFunction(runStaticCTC);
Static._modeList.add(&StaticCTC);
// -> Raw
StaticRaw._attributeList.add(&Static_Dim);
StaticRaw._attributeList.add(&Static_Raw_r);
StaticRaw._attributeList.add(&Static_Raw_g);
StaticRaw._attributeList.add(&Static_Raw_b);
StaticRaw._attributeList.add(&Attribute_Exit);
StaticRaw.assignFunction(runStaticRaw);
Static._modeList.add(&StaticRaw);
Static._modeList.add(&Mode_Exit);
// == DYNAMIC ==
Main._modeList.add(&Dynamic); // Fügt dem Menü das 2. Obermenü "Statische-Menü" hinzu
// -> MixColor
DynamicMixColor._attributeList.add(&Dynamic_Dim);
DynamicMixColor._attributeList.add(&Dynamic_MinLimit);
DynamicMixColor._attributeList.add(&Dynamic_MaxLimit);
DynamicMixColor._attributeList.add(&Dynamic_MixColor);
DynamicMixColor._attributeList.add(&Attribute_Exit);
DynamicMixColor.assignFunction(runDynamicMixColor);
Dynamic._modeList.add(&DynamicMixColor);
// -> CTC
DynamicCTC._attributeList.add(&Dynamic_Dim);
DynamicCTC._attributeList.add(&Dynamic_MinLimit);
DynamicCTC._attributeList.add(&Dynamic_MaxLimit);
DynamicCTC._attributeList.add(&Dynamic_CTC);
DynamicCTC._attributeList.add(&Attribute_Exit);
DynamicCTC.assignFunction(runDynamicCTC);
Dynamic._modeList.add(&DynamicCTC);
Dynamic._modeList.add(&Mode_Exit);
// == PARTY ==
Main._modeList.add(&Party); // Fügt dem Menü das 3. Obermenü "Party-Menü" hinzu
// -> Rainbow
PartyRainbow._attributeList.add(&Party_Dim);
PartyRainbow._attributeList.add(&Party_Tempo);
PartyRainbow._attributeList.add(&Attribute_Exit);
PartyRainbow.assignFunction(runPartyRainbow);
Party._modeList.add(&PartyRainbow);
// -> Police
PartyPolice._attributeList.add(&Party_Dim);
PartyPolice._attributeList.add(&Party_Tempo);
PartyPolice._attributeList.add(&Attribute_Exit);
PartyPolice.assignFunction(runPartyPolice);
Party._modeList.add(&PartyPolice);
// -> Color Chase
PartyColorChase._attributeList.add(&Party_Dim);
PartyColorChase._attributeList.add(&Party_Tempo);
PartyColorChase._attributeList.add(&Party_MixColor_Low);
PartyColorChase._attributeList.add(&Party_MixColor_High);
PartyColorChase._attributeList.add(&Attribute_Exit);
PartyColorChase.assignFunction(runPartyColorChase);
Party._modeList.add(&PartyColorChase);
Party._modeList.add(&Mode_Exit);
// Startsequenz des Bildschirms
MainMenu.drawBootScreen();
MainMenu.updateMenu();
delay(100);
blink();
}
// ================================================
// =========== LOOP ===========
// ================================================
void loop() {
readInput(); // Überprüft Button und Encoder auf Updates
if (millis() > timestamp_refreshRate + timer_refreshRate ) { // Führt die aktuelle Leuchtenfunktion aus
timer_refreshRate = millis();
MainMenu._runningMode->runMode();
}
if (millis() > timestamp_screensaver + timer_idle ) { // Versetzt die Leuchte nach gewisser Inaktivität in Bildschirmschoner
EVERY_N_MILLISECONDS(timestamp_screensaver) {
MainMenu.drawScreenSaver();
}
}
}
// ================================================
// =========== Allgemeine ===========
// =========== Funktionen ===========
// ================================================
void readInput() { // Überprüft auf eingabe durch Nutzende
button.read();
encoder.read();
}
void blink(){ //lässt Smiley im Startmenü zwinkern
lcd.setCursor(18, 0);
lcd.write(byte(0));
delay(1000);
lcd.setCursor(18, 0);
lcd.write(byte(1));
delay(400);
lcd.setCursor(18, 0);
lcd.write(byte(0));
delay(1000);
}
// Leuchtenfunktionsbedingte Variablen
uint8_t hue = 0;
int SwitchStatus = 0;
int BlendAmount = 0;
// Hilfsfunktionen für Leuchtenfunktionen
int calcDynamicDim(int sensorPin) { // Rechnet ein Analogen Sensor Input in eine Dynamische Helligkeit um
int dynamicBrightness;
int input = analogRead(sensorPin);
int maxBrightness = map(Dynamic_Dim._value,Dynamic_Dim._minValue,Dynamic_Dim._maxValue,0,255);
if (input > Dynamic_MinLimit._value) {
dynamicBrightness = maxBrightness;
}
else if (input >= Dynamic_MaxLimit._value && Dynamic_MinLimit._value >= input) {
dynamicBrightness = map(input, Dynamic_MaxLimit._value, Dynamic_MinLimit._value, 1, maxBrightness);
}
else if (input < Dynamic_MaxLimit._value) {
dynamicBrightness = 0;
}
return dynamicBrightness ;
}
CRGB calcCTC(int i) { //Rechnet eine Laufvariable (i) in RGB Werte um, so dass näherungsweise eine Farbtemperatur entsteht
int r = 255;
int g = (1 * i + 90);
int b = (3 * i + 17);
if (r > 255) {r = 255;}
if (r < 0) {r = 0;}
if (g > 255) {g = 255;}
if (g < 0) {g = 0;}
if (b > 255) {b = 255;}
if (b < 0) {b = 0;}
return CRGB(r,g,b);
}
int mapRaw(Attribute* inputAttribute) { // Wandelt Attribute Werte in Werte von 0 - 255 um
return map(inputAttribute->_value,inputAttribute->_minValue,inputAttribute->_maxValue,0,255);
}
// Leuchtenfunktionen
void runStaticMixColor() { // Statische Farbe + Helligkeit
Static_Dim._valuePrint = Static_Dim._value; // Korrigiert den Anzeigewert für Intensity
Static_MixColor._valuePrint = mixColorList.get(Static_MixColor._value)->getName();// Korrigiert den Anzeigewert für die Farbe
fill_solid(leds, NUM_LEDS, mixColorList.get(Static_MixColor._value)->getColor()); // Setzt alle LEDs auf die gewünschte Farbe
FastLED.setBrightness(mapRaw(&Static_Dim)); // Setzt alle LEDs auf eine gewünschte Helligkeit
FastLED.show(); // Sendet die Information an die LEDs
}
void runStaticCTC() { // Statische Farbtemperatur + Helligkeit
Static_Dim._valuePrint = Static_Dim._value; // Korrigiert den Anzeigewert für Intensity
Static_CTC._valuePrint = Static_CTC._value * 100 + 1000; // Korrigiert den Anzeigewert der Farbtemperatur
fill_solid(leds, NUM_LEDS, calcCTC(Static_CTC._value)); // Setz alle LEDs mittels RGB-Mischung auf eine gewünschte Farbtemperatur
FastLED.setBrightness(mapRaw(&Static_Dim)); // Setzt alle LEDs auf eine gewünschte Helligkeit
FastLED.show(); // Sendet die Information an die LEDs
}
void runStaticRaw(){ // Statische Farbe nach Analogem Input + Helligkeit
Static_Dim._valuePrint = Static_Dim._value;
Static_Raw_r._valuePrint = Static_Raw_r._value;
Static_Raw_g._valuePrint = Static_Raw_g._value;
Static_Raw_b._valuePrint = Static_Raw_b._value;
fill_solid(leds, NUM_LEDS, CRGB(Static_Raw_r._value,Static_Raw_g._value,Static_Raw_b._value));
FastLED.setBrightness(mapRaw(&Static_Dim));
FastLED.show();
}
void runDynamicMixColor() { // Dynamische Helligkeit mit Statischer Farbe
int input = analogRead(LDR_PIN);
int maxBrightness = mapRaw(&Dynamic_Dim);
int brightness = 100;
if (input > Dynamic_MinLimit._value) {
brightness = maxBrightness;
}
else if (input >= Dynamic_MaxLimit._value && Dynamic_MinLimit._value >= input) {
brightness = map(input, Dynamic_MaxLimit._value, Dynamic_MinLimit._value, 1, maxBrightness);
}
else if (input < Dynamic_MaxLimit._value) {
brightness = 0;
}
Dynamic_Dim._valuePrint = Dynamic_Dim._value;
Dynamic_MaxLimit._valuePrint = Dynamic_MaxLimit._value;
Dynamic_MinLimit._valuePrint = Dynamic_MinLimit._value;
Dynamic_MixColor._valuePrint = mixColorList.get(Dynamic_MixColor._value)->getName();
fill_solid(leds, NUM_LEDS, mixColorList.get(Dynamic_MixColor._value)->getColor());
FastLED.setBrightness(brightness);
FastLED.show();
}
void runDynamicCTC() { // Dynamische Helligkeit mit Statischer Farbtemperatur
int input = analogRead(LDR_PIN); // Speichert den analogen Wert des Sensors zwischen.
int brightness; // Erstellt die Brightness Variable, welche im Folgen mit Information gefüllt wird
int maxBrightness = mapRaw(&Dynamic_Dim); // Berechnet die Max Helligkeit, welche über das Menü eingestellt wurde
// Falls das Umgebungslicht die "Dunkelschwelle" vollkommen überschritten hat
if (input > Dynamic_MinLimit._value) {
brightness = maxBrightness;
}
// Falls das Umgebungslicht zwischen der "Hell- und Dunkelschwelle" liegt
else if (input >= Dynamic_MaxLimit._value && Dynamic_MinLimit._value >= input) {
brightness = map(input, Dynamic_MaxLimit._value, Dynamic_MinLimit._value, 1, maxBrightness);
}
// Falls das Umgebungslicht über der "Helligkeitsgrenze" liegt
else if (input < Dynamic_MaxLimit._value) {
brightness = 0;
}
Dynamic_Dim._valuePrint = Dynamic_Dim._value; // Korrigiert den Anzeigewert für Intensity
Dynamic_MaxLimit._valuePrint = Dynamic_MaxLimit._value; // Korrigiert den Anzeigewert für "Helligkeitsgrenze"
Dynamic_MinLimit._valuePrint = Dynamic_MinLimit._value; // Korrigiert den Anzeigewert für "Dunkelschwelle"
Dynamic_CTC._valuePrint = Dynamic_CTC._value * 100 + 1000; // Korrigiert den Anzeigewert für Farbtemperatur
fill_solid(leds, NUM_LEDS, calcCTC(Dynamic_CTC._value)); // Setz alle LEDs mittels RGB-Mischung auf eine gewünschte Farbtemperatur
FastLED.setBrightness(brightness); // Setzt alle LEDs auf eine gewünschte Helligkeit
FastLED.show(); // Sendet die Information an die LEDs
}
void runPartyRainbow() { // Regenbogen Effekt mit Statischer Helligkeit
Party_Dim._valuePrint = Party_Dim._value;
Party_Tempo._valuePrint = Party_Tempo._value;
timestamp_partyTempo = (map(Party_Tempo._value,0,100,100,0));
if (millis() > timestamp_partyTempo + timer_partyTempo ) {
timer_partyTempo = millis();
for (int i = 0; i < NUM_LEDS; ++i) {
leds[i] = CHSV(hue + (i * 10), 255, 255);
}
hue++;
FastLED.setBrightness(mapRaw(&Party_Dim));
FastLED.show();
}
}
void runPartyPolice() { // Polizeisirenen Effekt mit Statischer Helligkeit
Party_Dim._valuePrint = Party_Dim._value;
Party_Tempo._valuePrint = Party_Tempo._value;
timestamp_partyTempo = (100 + map(Party_Tempo._value,0,100,1000,0));
if (millis() > timestamp_partyTempo + timer_partyTempo) {
timer_partyTempo = millis();
if (SwitchStatus == 0) {
for (int i = (NUM_LEDS/2); i < NUM_LEDS; ++i) {
leds[i] = Red.getColor();
}
for (int i = 0; i < (NUM_LEDS/2); ++i) {
leds[i] = Blue.getColor();
}
SwitchStatus = 1;
} else if (SwitchStatus == 1) {
for (int i = (NUM_LEDS/2); i < NUM_LEDS; ++i) {
leds[i] = Blue.getColor();
}
for (int i = 0; i < (NUM_LEDS/2); ++i) {
leds[i] = Red.getColor();
}
SwitchStatus = 0;
}
FastLED.setBrightness(mapRaw(&Party_Dim));
FastLED.show();
}
}
void runPartyColorChase () {
CRGB LowValue = mixColorList.get(Party_MixColor_Low._value)->getColor();
CRGB HighValue = mixColorList.get(Party_MixColor_High._value)->getColor();
CRGB CurrentColor;
timestamp_partyTempo = (10 + map(Party_Tempo._value,0,100,100,0));
Party_Dim._valuePrint = Party_Dim._value;
Party_Tempo._valuePrint = Party_Tempo._value;
Party_MixColor_Low._valuePrint = mixColorList.get(Party_MixColor_Low._value)->getName();
Party_MixColor_High._valuePrint = mixColorList.get(Party_MixColor_High._value)->getName();
if (millis() > timestamp_partyTempo + timer_partyTempo) {
timer_partyTempo = millis();
switch (SwitchStatus) {
case 0:
if ((BlendAmount + 5) > 255) {
SwitchStatus = 1;
} else {
BlendAmount = BlendAmount + 5;
}
CurrentColor = blend(LowValue,HighValue,BlendAmount);
break;
case 1:
if ((BlendAmount - 5) < 0) {
SwitchStatus = 0;
} else {
BlendAmount = BlendAmount - 5;
}
CurrentColor = blend(LowValue,HighValue,BlendAmount);
break;
}
fill_solid(leds, NUM_LEDS, CurrentColor);
FastLED.setBrightness(mapRaw(&Party_Dim));
FastLED.show();
}
}