arduino:flaschenkuehler:programmversion_0.4
Unterschiede
Hier werden die Unterschiede zwischen zwei Versionen angezeigt.
Beide Seiten der vorigen RevisionVorhergehende ÜberarbeitungNächste Überarbeitung | Vorhergehende Überarbeitung | ||
arduino:flaschenkuehler:programmversion_0.4 [25.07.2017 16:32] – Frickelpiet | arduino:flaschenkuehler:programmversion_0.4 [18.05.2023 12:34] (aktuell) – Externe Bearbeitung 127.0.0.1 | ||
---|---|---|---|
Zeile 1: | Zeile 1: | ||
====== Flaschenkühler - Programmversion 0.4 ====== | ====== Flaschenkühler - Programmversion 0.4 ====== | ||
+ | Diese Programmversion dokumentiert den Wechsel von der experimentellen Verdrahtung auf dem Breadboard auf gelötete Platinen. Außerdem ist der experimentelle Aufbau der Hardware fertiggestellt. | ||
- | Die Programmversion wird vor allem die Regelung des Peltier-Elements einbinden. | + | ACHTUNG: Weil der AREF am Arduino mit dessen |
- | * Die [[arduino:flaschenkuehler: | + | |
- | * Die Solltemperatur der heißen Seite wird zunächst fest vorgegeben. Welcher Wert hier realistisch | + | |
- | * Der PID-Regler für den Lüfter muss so eingestellt werden, dass der Lüfter möglichst nie voll aufdrehen muss. | + | |
- | * Es macht vermutlich Sinn, die Solltemperatur der heißen Seite von der Umgebnungstemperatur abhängig zu machen, da die heiße Seite einerseits niemals unter Raumtemperatur gekühlt werden kann und andererseits möglichst kühl gehalten werden sollte. | + | |
- | * Ebenso kann der Höchstwert für die Solltemperatur für der kalten Seite von der Umgebungstemperatur abhängig gemacht werden, da die Solltemperatur nicht über der Umgebungstemperatur liegen kann. (Das Peltier-Element wird so gesteuert, dass es nur kühlen kann. | + | |
+ | < | ||
+ | analogReference(EXTERNAL); | ||
+ | </ | ||
- | Um die PID-Regler abstimmen zu können, sollen verschiedene Variablen grafisch am PC dargestellt werden. | + | Ohne diesen Befehl wird die interne Referenzspannung mit dem 3,3-Volt-Ausgang kurzgeschlossen, was den Arduino zerstören kann. |
+ | Es wurden die folgenden Anpassungen vorgenommen: | ||
+ | * Das Dispaly zeigt nun korrekt die Soll- und Isttemperatur des Kühlbechers an. | ||
+ | * Über die beiden Taster wird die Solltemperatur des Kühlbechers eingestellt. | ||
+ | * Die PID-Regler für den Lüfter und das Peltier-Element wurden angepasst. (Weitere Optimierung ist aber noch notwendig.) | ||
+ | * Die Solltemperatur für den Kühlkörper wird an die Umgebungstemperatur (+ 10 °C) angepasst. | ||
+ | * Die Auswertung des Tachosignals wurde verfeinert. Das Programm ignoriert jetzt nicht nur zu niedrige und zu hohe Frequenzen des Tachosignals, | ||
+ | * Mit der Freeware [[http:// | ||
+ | To Dos: | ||
+ | * Die Anzeige der Kühlleistung und der Lüfterdrehzahl sollte in Anzeigomodus 1 (erweitert) mittig ausgerichtet werden. | ||
+ | Mit der Version 0.4 errecht das Programm seinen finalen Entwicklungsstatus und ist damit der " | ||
- | http://www.serialcominstruments.com/serial.php | + | Evtl. könnte die Tabelle mit den Farbwerten für die farbliche Darstellung der Temperaturen auf dem Display in ein speicherschonendes Format gebracht oder gekürzt werden. So ließe sich Speicherplatz hinzugewinnen. |
- | http://www.serialcominstruments.com/instrument4.php | + | < |
+ | // Flaschenkühler - Programmversion 0.4 | ||
+ | // Diese Version des Programms | ||
+ | // ... regelt ein Peltier-Element und einen Lüfter mit jeweils einem PID-Regler ... | ||
+ | // ... misst dafür vier Thermistoren und berechnet die Temperaturen ... | ||
+ | // ... stellt die Solltemperatur des Kühlbechers über zwei Taster ein ... | ||
+ | // ... speichert die Solltemperatur im EEPROM | ||
+ | // ... misst das Tachosignal des Lüfters aus und berechnet die Drehzahl ... | ||
+ | // ... überwacht die Funktion des Lüfters ... | ||
+ | // ... zeigt verschiedene Werte auf einem OLED-Display an ... | ||
+ | // ... unterstützt verschiedene Anzeigemodi ... | ||
+ | // ... zählt die Betriebsstunden (viertelstundengenau) und speichert sie im EEPROM ... | ||
+ | // ... empfiehlt alle 100 Stunden eine Reinigung ... | ||
+ | // ... gibt Daten an die serielle Schnittstelle in einem Format uas, das mit SerialComInstruments interpretiert werden kann ... | ||
+ | // ... und ist damit eigentlich fertig. | ||
+ | // | ||
+ | #include < | ||
+ | #include < | ||
+ | #include < | ||
+ | #include < | ||
+ | #include < | ||
+ | #include < | ||
+ | #include < | ||
+ | #include < | ||
+ | #include < | ||
- | Tags: #Arduino #Peltier-Element | + | // |
+ | #define thermistor1Pin | ||
+ | #define thermistor2Pin | ||
+ | #define thermistor3Pin | ||
+ | #define thermistor4Pin | ||
+ | #define tachoPin | ||
+ | #define peltierPin | ||
+ | #define powerPin | ||
+ | #define dc 5 // | ||
+ | #define cs 6 // Chip Select | ||
+ | #define rst | ||
+ | #define button2Pin | ||
+ | #define fanPin | ||
+ | #define button1Pin | ||
+ | |||
+ | |||
+ | // | ||
+ | #define BLACK | ||
+ | #define BLUE 0x0418 | ||
+ | #define BLUEBERRY | ||
+ | #define RED | ||
+ | //#define GREEN | ||
+ | //#define CYAN 0x07FF | ||
+ | //#define MAGENTA | ||
+ | //#define YELLOW | ||
+ | #define WHITE | ||
+ | |||
+ | |||
+ | // | ||
+ | float thermistor1Value = 0; // Variable in der der Wert von Thermistor 1 gespeichert wird | ||
+ | float thermistor2Value = 0; // Variable in der der Wert von Thermistor 2 gespeichert wird | ||
+ | //float thermistor3Value = 0; // Variable in der der Wert von Thermistor 3 gespeichert wird | ||
+ | float thermistor4Value = 0; // Variable in der der Wert von Thermistor 4 gespeichert wird | ||
+ | |||
+ | // Temperaturen | ||
+ | float tempAmbient = 0; // Umgebungstemperatur | ||
+ | float tempDissipator = 0; // Kühlkörpertemperatur | ||
+ | //float tempCupbase = 0; // Kühlbecherboden | ||
+ | float tempCupedge = 0; // Kühlbecherrand | ||
+ | float previousTempCupedge = 0; // Wird verwendet um festzustellen, | ||
+ | float TargetTemp = 0; // Solltemperatur | ||
+ | |||
+ | // PWM Frequenzen | ||
+ | unsigned short frequencyFan = 25000; | ||
+ | unsigned short frequencyPeltier = 16000; | ||
+ | |||
+ | // Lüfter | ||
+ | bool tachoSignal; | ||
+ | unsigned long pulseOn = 0; // Ansteigende Signalflanke (Variablenformat nicht verändern!) | ||
+ | unsigned long pulseOff = 0; // Abfallende Signalflanke (Variablenformat nicht verändern!) | ||
+ | unsigned long duration; | ||
+ | bool high = false; | ||
+ | int rpm = 0; // Drehzahl des Lüfters in U/Min | ||
+ | int rpmArray[10]; | ||
+ | unsigned char a = 0; | ||
+ | int rpmMax = 0; // Maximale Drehzahl des Lüfter (aus den letzten 10 Wertens) | ||
+ | int previousRPMMax = 0; // Zur Bestimmung von Änderungen der Lüfterdrehzahl | ||
+ | bool fanAlert = false; | ||
+ | bool fanAlertState = true; | ||
+ | unsigned long fanAlertDelay = 2000; // Gibt die Verzögerung des Alarms " | ||
+ | unsigned long previousMillis = 0; | ||
+ | |||
+ | // Betriebsstundenzähler und Serviceintervall | ||
+ | float operatingTime; | ||
+ | long lastTime = 0; | ||
+ | float serviceInterval; | ||
+ | bool serviceIntervalReset = false; | ||
+ | |||
+ | // Adressen im EEPROM | ||
+ | unsigned char addrOperatingTime = 0; // Startadresse für eine Variable im Datentyp float (4 Byte!) | ||
+ | unsigned char addrTargetTemp = 4; // Startadresse für eine Variable im Datentyp double (8 Byte!) | ||
+ | unsigned char addrDisplayMode = 13; // Adresse für den Anzeigemodus (2 Byte reserviert) | ||
+ | unsigned char addrServiceInterval = 15; // Startadresse für eine Variable im Datentyp float (4 Byte!) | ||
+ | |||
+ | // Definiert das OLED | ||
+ | Adafruit_SSD1351 tft = Adafruit_SSD1351(cs, | ||
+ | |||
+ | // Definiert ein Array, aus dem Farbwerte ausgelesen werden | ||
+ | unsigned long colors[] = {0x07FF, 0x07DF, 0x077F, 0x06FF, 0x069F, 0x063F, 0x05DF, 0x057F, 0x053F, 0x049F, 0x043F, 0x03DF, 0x037F, 0x031F, 0x029F, 0x023F, 0x01DF, 0x017F, 0x011F, 0x009F, | ||
+ | 0x003F, 0x001F, 0x101F, 0x201F, 0x281F, 0x381F, 0x401F, | ||
+ | 0xF81F, 0xF81F, 0xF81D, 0xF81B, 0xF81A, 0xF818, 0xF817, 0xF815, 0xF813, 0xF812, 0xF810, 0xF80F, 0xF80D, 0xF80C, 0xF80A, 0xF808, 0xF807, 0xF805, 0xF804, 0xF802, 0xF800}; | ||
+ | |||
+ | // Informationsanzeige | ||
+ | char displayMode; | ||
+ | bool refreshPeltier = true; // Wird wahr, wenn der Anzeigebereich für das Peltier-Element aktualisiert werden muss | ||
+ | bool refreshTargettemp = true; // Wird wahr, wenn der Anzeigebereich für die Solltemperatur aktualisiert werden muss | ||
+ | bool refreshFan = true; // Wird wahr, wenn der Anzeigebereich für den Lüfter aktualisiert werden muss | ||
+ | unsigned char x = 0; | ||
+ | |||
+ | // Instantiiert ButtonEvents-Objekte | ||
+ | ButtonEvents button1; | ||
+ | ButtonEvents button2; | ||
+ | |||
+ | // Instantiiert ein RunningMedian-Objekt zur Bechnung von Medianwerten | ||
+ | RunningMedian medianDuration(5); | ||
+ | |||
+ | // Instantiirt ResponsiveAnalogRead-Objekte zur Glättung der analogen Inputs | ||
+ | ResponsiveAnalogRead thermistor1(thermistor1Pin, | ||
+ | ResponsiveAnalogRead thermistor2(thermistor2Pin, | ||
+ | // | ||
+ | ResponsiveAnalogRead thermistor4(thermistor4Pin, | ||
+ | |||
+ | // Definiert die PID-Regler | ||
+ | double setpointFan, | ||
+ | double fanP = 40, fanI = 1, fanD = 2; | ||
+ | PID fanPID(& | ||
+ | |||
+ | double setpointPeltier, | ||
+ | double peltierP = 30, peltierI = 0.5, peltierD = 0; | ||
+ | PID peltierPID(& | ||
+ | double previousOutputPeltier = 0; | ||
+ | |||
+ | |||
+ | // Definiert die Tracking-Variablen für die IF-Abfragen | ||
+ | unsigned long previousMillisControllers = 0; // Auslesen der analogen Inputs und Berechnung der Temperaturen | ||
+ | unsigned long previousMillisCalculateFanSpeed = 0; // Berechnung der Drehzahl des Lüfters | ||
+ | unsigned long previousMillisDisplayCoolingPower | ||
+ | unsigned long previousMillisDisplayFanSpeed = 0; // Ausgabe der gemittelten Lüfterdrehzahl an das OLED-Display | ||
+ | unsigned long previousMillisDisplayGraph = 0; // Ausgabe der gemittelten Lüfterdrehzahl an das OLED-Display | ||
+ | unsigned long previousMillisSerialPrint = 0; // Ausgabe an die serielle Schnittstelle | ||
+ | |||
+ | // Definiert die Intervalle für die IF-Abfragen in Millisekunden | ||
+ | const unsigned long intervalControllers = 100; // Auslesen der analogen Inputs und Berechnung der Temperaturen | ||
+ | const unsigned long intervalCalculateFanSpeed = 100; // Berechnung der Drehzahl des Lüfters | ||
+ | const unsigned long intervalDisplayCoolingPower = 1000;// Berechnung der PID-Regler (Lüfter und Peltier-Element) | ||
+ | const unsigned long intervalDisplayFanSpeed = 1000; // Ausgabe der gemittelten Lüfterdrehzahl an das OLED-Display | ||
+ | const unsigned long intervalDisplayGraph = 1000; // Ausgabe der gemittelten Lüfterdrehzahl an das OLED-Display | ||
+ | const unsigned long intervalSerialPrint = 500; // Ausgabe an die serielle Schnittstelle | ||
+ | |||
+ | int loopCounter = 0; | ||
+ | |||
+ | |||
+ | // | ||
+ | void setup() { | ||
+ | Serial.begin(115200); | ||
+ | |||
+ | // Initialisiert das OLED-Dispaly | ||
+ | tft.begin(); | ||
+ | |||
+ | // Definiert AREF als externe Referenzspannung | ||
+ | analogReference(EXTERNAL); | ||
+ | |||
+ | // Definiert die Pins | ||
+ | pinMode(tachoPin, | ||
+ | pinMode(powerPin, | ||
+ | pinMode(fanPin, | ||
+ | pinMode(peltierPin, | ||
+ | pinMode(button1Pin, | ||
+ | pinMode(button2Pin, | ||
+ | |||
+ | // Initialisiert die ButtonEvents-Objekte | ||
+ | button1.attach(button1Pin); | ||
+ | button1.debounceTime(10); | ||
+ | button1.doubleTapTime(60); | ||
+ | button1.holdTime(500); | ||
+ | button2.attach(button2Pin); | ||
+ | button2.debounceTime(10); | ||
+ | button2.doubleTapTime(60); | ||
+ | button2.holdTime(500); | ||
+ | |||
+ | // Initialisiert timer1 und timer2 (timer0 bleibt unberührt) | ||
+ | InitTimersSafe(); | ||
+ | |||
+ | // Definiert die Frequenzen für die angegebenen Pins | ||
+ | SetPinFrequencySafe(fanPin, | ||
+ | SetPinFrequencySafe(peltierPin, | ||
+ | |||
+ | /* | ||
+ | bool successFan = SetPinFrequencySafe(fanPin, | ||
+ | bool successPeltier = SetPinFrequencySafe(peltierPin, | ||
+ | |||
+ | //if the pin frequency was set successfully, | ||
+ | if(successFan) { | ||
+ | pinMode(13, OUTPUT); | ||
+ | digitalWrite(13, | ||
+ | Serial.print(" | ||
+ | } | ||
+ | if(successPeltier) { | ||
+ | Serial.print(" | ||
+ | } | ||
+ | */ | ||
+ | // Initialisiert die PID-Regler | ||
+ | fanPID.SetMode(AUTOMATIC); | ||
+ | peltierPID.SetMode(AUTOMATIC); | ||
+ | |||
+ | // Liest die im EEPROM gespeicherten Variablen aus | ||
+ | displayMode = EEPROM.read(addrDisplayMode); | ||
+ | operatingTime = EEPROM.readFloat(addrOperatingTime); | ||
+ | serviceInterval = EEPROM.readFloat(addrServiceInterval); | ||
+ | TargetTemp = EEPROM.readDouble(addrTargetTemp); | ||
+ | |||
+ | // | ||
+ | // | ||
+ | |||
+ | // Meldung "Klar zum Start!" | ||
+ | Serial.println("< | ||
+ | Serial.print("< | ||
+ | Serial.print("< | ||
+ | |||
+ | // Startbildschirm | ||
+ | tft.fillScreen(BLACK); | ||
+ | tft.setCursor(25, | ||
+ | tft.setTextColor(WHITE, | ||
+ | tft.setTextSize(0); | ||
+ | tft.print(" | ||
+ | tft.setCursor(20, | ||
+ | tft.print(" | ||
+ | tft.setCursor(45, | ||
+ | tft.setTextSize(2); | ||
+ | tft.print(" | ||
+ | if (serviceInterval >= 100.00) { | ||
+ | tft.setCursor(0, | ||
+ | tft.setTextColor(RED, | ||
+ | tft.setTextSize(0); | ||
+ | tft.print(" | ||
+ | } | ||
+ | tft.setCursor(10, | ||
+ | tft.setTextColor(WHITE, | ||
+ | tft.setTextSize(0); | ||
+ | tft.print(" | ||
+ | tft.setCursor(10, | ||
+ | tft.print(operatingTime); | ||
+ | |||
+ | delay(2000); | ||
+ | tft.fillScreen(BLACK); | ||
+ | } | ||
+ | |||
+ | |||
+ | // | ||
+ | void loop() { | ||
+ | // Aktuelle Zeit abfragen | ||
+ | unsigned long currentMillis = millis(); | ||
+ | |||
+ | loopCounter++; | ||
+ | |||
+ | |||
+ | // | ||
+ | // Lesen und entprellen des Tasters | ||
+ | button1.update(); | ||
+ | button2.update(); | ||
+ | |||
+ | |||
+ | if (button1.tapped() == true) { | ||
+ | TargetTemp = TargetTemp + 0.5; | ||
+ | / | ||
+ | if (outputPeltier >= 255) { | ||
+ | outputPeltier = 255; | ||
+ | } | ||
+ | */ | ||
+ | EEPROM.updateDouble(addrTargetTemp, | ||
+ | Serial.print(" | ||
+ | refreshTargettemp = true; | ||
+ | if (TargetTemp >= 30.00) { | ||
+ | TargetTemp = 30.00; | ||
+ | } | ||
+ | } | ||
+ | if (button1.held() == true) { | ||
+ | displayMode++; | ||
+ | if (displayMode > 2) { | ||
+ | displayMode = 0; | ||
+ | } | ||
+ | tft.fillScreen(BLACK); | ||
+ | EEPROM.update(addrDisplayMode, | ||
+ | // | ||
+ | refreshPeltier = true; | ||
+ | refreshTargettemp = true; | ||
+ | previousTempCupedge = 150; | ||
+ | refreshFan= true; | ||
+ | x = 0; | ||
+ | } | ||
+ | if (button1.doubleTapped() == true) { | ||
+ | serviceInterval = 0; | ||
+ | EEPROM.updateFloat(addrServiceInterval, | ||
+ | Serial.println(" | ||
+ | serviceIntervalReset = true; | ||
+ | } | ||
+ | |||
+ | if (button2.tapped() == true) { | ||
+ | TargetTemp = TargetTemp - 0.5; | ||
+ | / | ||
+ | if (outputPeltier <= 0.00) { | ||
+ | outputPeltier = 0.00; | ||
+ | } | ||
+ | */ | ||
+ | EEPROM.updateDouble(addrTargetTemp, | ||
+ | Serial.print(" | ||
+ | refreshTargettemp = true; | ||
+ | if (TargetTemp <= 0.00) { | ||
+ | TargetTemp = 0.00; | ||
+ | } | ||
+ | } | ||
+ | if (button2.held() == true) { | ||
+ | displayMode--; | ||
+ | if (displayMode < 0) { | ||
+ | displayMode = 2; | ||
+ | } | ||
+ | tft.fillScreen(BLACK); | ||
+ | EEPROM.update(addrDisplayMode, | ||
+ | // | ||
+ | refreshPeltier = true; | ||
+ | refreshTargettemp = true; | ||
+ | previousTempCupedge = 150; | ||
+ | refreshFan= true; | ||
+ | x = 0; | ||
+ | } | ||
+ | |||
+ | |||
+ | |||
+ | |||
+ | // | ||
+ | if ((unsigned long)(currentMillis - previousMillisControllers) >= intervalControllers) { | ||
+ | // Widerstandswerte | ||
+ | // R1 (A1) = 10000 | ||
+ | // R2 (A3) = 10500 | ||
+ | // R3 (A5) = 10400 | ||
+ | // R4 (A7) = 10300 | ||
+ | |||
+ | |||
+ | // Thermistor 1: Umgebungstemperatur | ||
+ | thermistor1.update(); | ||
+ | |||
+ | thermistor1Value = thermistor1.getValue(); | ||
+ | |||
+ | // Berechnung der Temperatur | ||
+ | thermistor1Value = 1023 / thermistor1Value - 1; | ||
+ | thermistor1Value = 10000 / thermistor1Value; | ||
+ | tempAmbient = thermistor1Value / 10000; | ||
+ | tempAmbient = log(tempAmbient); | ||
+ | tempAmbient /= 3950; // 1/B * ln(R/Ro) | ||
+ | tempAmbient += 1.0 / (25 + 273.15); | ||
+ | tempAmbient = 1.0 / tempAmbient; | ||
+ | tempAmbient -= 273.15; | ||
+ | |||
+ | |||
+ | // Thermistor 2: Kühlkörper | ||
+ | thermistor2.update(); | ||
+ | |||
+ | thermistor2Value = thermistor2.getValue(); | ||
+ | |||
+ | // Berechnung der Temperatur | ||
+ | thermistor2Value = 1023 / thermistor2Value - 1; | ||
+ | thermistor2Value = 10500 / thermistor2Value; | ||
+ | tempDissipator = thermistor2Value / 10000; | ||
+ | tempDissipator = log(tempDissipator); | ||
+ | tempDissipator /= 3950; // 1/B * ln(R/Ro) | ||
+ | tempDissipator += 1.0 / (25 + 273.15); | ||
+ | tempDissipator = 1.0 / tempDissipator; | ||
+ | tempDissipator -= 273.15; | ||
+ | |||
+ | /* | ||
+ | // Thermistor 3: Kühlbecherboden | ||
+ | thermistor3.update(); | ||
+ | |||
+ | thermistor3Value = thermistor3.getValue(); | ||
+ | |||
+ | // Berechnung der Temperatur | ||
+ | thermistor3Value = 1023 / thermistor3Value - 1; | ||
+ | thermistor3Value = 10400 / thermistor3Value; | ||
+ | tempCupbase = thermistor3Value / 10000; | ||
+ | tempCupbase = log(tempCupbase); | ||
+ | tempCupbase /= 3950; // 1/B * ln(R/Ro) | ||
+ | tempCupbase += 1.0 / (25 + 273.15); | ||
+ | tempCupbase = 1.0 / tempCupbase; | ||
+ | tempCupbase -= 273.15; | ||
+ | */ | ||
+ | |||
+ | // Thermistor 4: Kühlbecherrand | ||
+ | thermistor4.update(); | ||
+ | |||
+ | thermistor4Value = thermistor4.getValue(); | ||
+ | |||
+ | // Berechnung der Temperatur | ||
+ | thermistor4Value = 1023 / thermistor4Value - 1; | ||
+ | thermistor4Value = 10300 / thermistor4Value; | ||
+ | tempCupedge = thermistor4Value / 10000; | ||
+ | tempCupedge = log(tempCupedge); | ||
+ | tempCupedge /= 3950; // 1/B * ln(R/Ro) | ||
+ | tempCupedge += 1.0 / (25 + 273.15); | ||
+ | tempCupedge = 1.0 / tempCupedge; | ||
+ | tempCupedge -= 273.15; | ||
+ | |||
+ | |||
+ | // PID-Regler Lüfter | ||
+ | inputFan = tempDissipator; | ||
+ | setpointFan = tempAmbient + 10.00; | ||
+ | fanPID.Compute(); | ||
+ | pwmWrite(fanPin, | ||
+ | |||
+ | // Wenn der Output des PID-Reglers Null ist, wird der Lüfter ausgeschaltet. | ||
+ | if (outputFan > 0) { | ||
+ | digitalWrite(powerPin, | ||
+ | // | ||
+ | } | ||
+ | else { | ||
+ | digitalWrite(powerPin, | ||
+ | // | ||
+ | } | ||
+ | |||
+ | |||
+ | // PID-Regler Peltier-Element | ||
+ | inputPeltier = tempCupedge; | ||
+ | setpointPeltier = TargetTemp; | ||
+ | peltierPID.Compute(); | ||
+ | pwmWrite(peltierPin, | ||
+ | |||
+ | // Überpüft, ob outputPeltier sich geändert hat. | ||
+ | if (byte(previousOutputPeltier) != byte(outputPeltier)) { // Änderungen hinter dem Komma sollen ignoriert werden | ||
+ | refreshPeltier = true; | ||
+ | // | ||
+ | previousOutputPeltier = outputPeltier; | ||
+ | } | ||
+ | |||
+ | previousMillisControllers = currentMillis; | ||
+ | } | ||
+ | |||
+ | |||
+ | // | ||
+ | // Messung der Pulsweite des Tachosignals | ||
+ | tachoSignal = digitalRead(tachoPin); | ||
+ | if (tachoSignal == HIGH && high != true) { // Zeit in micros bei ansteigender Flanke | ||
+ | pulseOn = micros(); | ||
+ | high = true; | ||
+ | } | ||
+ | else if (tachoSignal == LOW && high == true) { | ||
+ | pulseOff = micros(); | ||
+ | high = false; | ||
+ | duration = pulseOff - pulseOn; | ||
+ | if (duration > 7500 && duration < 30000) { // Liegt die Variable über bzw. unter den angegebenen Werten, liegt ein Messfehler vor | ||
+ | medianDuration.add(duration); | ||
+ | } | ||
+ | } | ||
+ | |||
+ | // Berechnung der Drehzahl des Lüfters | ||
+ | if ((unsigned long)(currentMillis - previousMillisCalculateFanSpeed) >= intervalCalculateFanSpeed) { | ||
+ | |||
+ | // | ||
+ | if (outputFan == 0) { // Wenn der Lüfter ausgeschaltet wird, soll die Drehzahl " | ||
+ | rpm = 0; | ||
+ | } | ||
+ | else { | ||
+ | rpm = float(100000 * 2 * 60 / medianDuration.getMedian()); | ||
+ | } | ||
+ | |||
+ | // Bestimmung der höchsten Drehzahl aus den letzten 10 Werten | ||
+ | a++; // Es werden 10 Werte in einem Array gespeichert | ||
+ | rpmArray[a] = rpm; | ||
+ | if (a > 9) { | ||
+ | a = 0; | ||
+ | } | ||
+ | unsigned char kmax=0; | ||
+ | int max=0; | ||
+ | for (unsigned char k=0; k<10; k++) { | ||
+ | if (rpmArray[k] > max) { | ||
+ | max = rpmArray[k]; | ||
+ | kmax = k; | ||
+ | } | ||
+ | } | ||
+ | rpmMax = rpmArray[kmax]; | ||
+ | |||
+ | // Überpüft, ob rpmMax sich geändert hat. | ||
+ | if (previousRPMMax != rpmMax) { // Änderungen hinter dem Komma sollen ignoriert werden | ||
+ | refreshFan = true; | ||
+ | // | ||
+ | previousRPMMax = rpmMax; | ||
+ | } | ||
+ | |||
+ | |||
+ | / | ||
+ | Serial.print("; | ||
+ | Serial.print("; | ||
+ | Serial.print("; | ||
+ | Serial.print("; | ||
+ | Serial.print("; | ||
+ | // | ||
+ | // | ||
+ | // | ||
+ | // | ||
+ | // | ||
+ | Serial.print("; | ||
+ | */ | ||
+ | |||
+ | // Meldet eine Fehlfunktion des Lüfters, wenn das Tachosignal " | ||
+ | if (millis() >= 10000) { // Diese Funktion wird erst nach 10 Sekunden aktiv. | ||
+ | if (outputFan > 0) { // Wenn der Lüfter sich drehen sollte ... | ||
+ | if (rpmMax == 0) { // ... aber die gemittelte Drehzahl gleich " | ||
+ | if (millis() - previousMillis > fanAlertDelay) { // ... und eine definierte Zeit verstrichen ist. | ||
+ | previousMillis = millis(); | ||
+ | fanAlert = true; | ||
+ | fanAlertState = true; | ||
+ | Serial.println(" | ||
+ | refreshFan = true; | ||
+ | } | ||
+ | } | ||
+ | else if (rpmMax > 0) { // ... und die gemittelte Drehzahl größer " | ||
+ | fanAlert = false; | ||
+ | } | ||
+ | } | ||
+ | else if (outputFan == 0) { // Wenn der Lüfter sich nicht drehen soll kann nicht festgestellt werden, ob er blockiert ist. | ||
+ | fanAlert = false; | ||
+ | previousMillis = millis(); | ||
+ | if (fanAlert != fanAlertState) { | ||
+ | refreshFan = true; | ||
+ | fanAlertState = false; | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | previousMillisCalculateFanSpeed = currentMillis; | ||
+ | } | ||
+ | |||
+ | |||
+ | // | ||
+ | // Die Betriebsstunden werden alle 15 Minuten im EEPROM gespeichert. Die Einheit der Variable operatinTime ist also 0,25 Stunden. | ||
+ | if (millis() - lastTime >= 900000) { | ||
+ | // Betriebsstundenzähler | ||
+ | operatingTime = operatingTime + 0.25; | ||
+ | lastTime = millis(); | ||
+ | EEPROM.updateFloat(addrOperatingTime, | ||
+ | Serial.print(" | ||
+ | // Serviceintervallzähler | ||
+ | serviceInterval = serviceInterval + 0.25; | ||
+ | EEPROM.updateFloat(addrServiceInterval, | ||
+ | } | ||
+ | |||
+ | |||
+ | // | ||
+ | // Anzeige " | ||
+ | if (serviceIntervalReset == true) { | ||
+ | tft.fillScreen(BLACK); | ||
+ | tft.setCursor(15, | ||
+ | tft.setTextColor(WHITE, | ||
+ | tft.setTextSize(0); | ||
+ | tft.print(" | ||
+ | tft.setCursor(26, | ||
+ | tft.print(" | ||
+ | delay(2000); | ||
+ | tft.fillScreen(BLACK); | ||
+ | refreshPeltier = true; | ||
+ | refreshTargettemp = true; | ||
+ | previousTempCupedge = 150; | ||
+ | refreshFan= true; | ||
+ | serviceIntervalReset = false; | ||
+ | } | ||
+ | |||
+ | // Anzeigemodus " | ||
+ | if (displayMode == 0) { | ||
+ | // Anzeige Infobereich Zieltemperatur | ||
+ | if (refreshTargettemp == true) { // Wenn die Solltemperatur verändert wurde, muss die Anzeige aktualisiert werden. | ||
+ | tft.setCursor(15, | ||
+ | unsigned char i = TargetTemp * 2; // Berechnet den Index für den Array " | ||
+ | if (i < 0) i = 0; // i soll nicht kleiner als " | ||
+ | if (i > 60) i = 60; // i soll nicht größer als " | ||
+ | tft.setTextColor(colors[i], | ||
+ | tft.setTextSize(0); | ||
+ | tft.print(" | ||
+ | tft.setCursor(15, | ||
+ | tft.setTextSize(2); | ||
+ | if (TargetTemp < 10.00) { | ||
+ | tft.print(" | ||
+ | } | ||
+ | tft.print(TargetTemp); | ||
+ | tft.setTextColor(BLACK, | ||
+ | tft.print((char)218); | ||
+ | refreshTargettemp = false; | ||
+ | } | ||
+ | |||
+ | // Anzeige Infobereich Isttemperatur | ||
+ | if (tempCupedge != previousTempCupedge) { // Wenn sich die Isttemperatur verändert hat, muss das Display aktualisiert werden. | ||
+ | tft.setCursor(15, | ||
+ | unsigned char i = tempCupedge * 2; // Berechnet den Index für den Array " | ||
+ | if (i < 0) i = 0; // i soll nicht kleiner als " | ||
+ | if (i > 60) i = 60; // i soll nicht größer als " | ||
+ | tft.setTextColor(colors[i], | ||
+ | tft.setTextSize(0); | ||
+ | tft.print(" | ||
+ | tft.setCursor(15, | ||
+ | tft.setTextSize(2); | ||
+ | if (tempCupedge < 10.00) { | ||
+ | tft.print(" | ||
+ | } | ||
+ | tft.print(tempCupedge); | ||
+ | tft.setTextColor(BLACK, | ||
+ | tft.print((char)218); | ||
+ | previousTempCupedge = tempCupedge; | ||
+ | } | ||
+ | } | ||
+ | |||
+ | // Anzeigemodus " | ||
+ | if (displayMode == 1) { | ||
+ | // Anzeige Infobereich Peltier-Element | ||
+ | if ((unsigned long)(currentMillis - previousMillisDisplayCoolingPower) >= intervalDisplayCoolingPower) { | ||
+ | if (outputPeltier > 0) { | ||
+ | if (refreshPeltier == true) { | ||
+ | tft.setCursor(7, | ||
+ | tft.setTextSize(0); | ||
+ | tft.setTextColor(WHITE, | ||
+ | tft.print(" | ||
+ | tft.setTextColor(BLACK, | ||
+ | for (unsigned char i=0; i<3; i++) { // Füllt die Zeile mit schwarzen Kästchen; i muss ggf. angepasst werden | ||
+ | tft.print((char)218); | ||
+ | } | ||
+ | refreshPeltier = false; | ||
+ | } | ||
+ | } | ||
+ | else if (outputPeltier == 0) { | ||
+ | if (refreshPeltier == true) { | ||
+ | tft.setCursor(7, | ||
+ | tft.setTextColor(WHITE, | ||
+ | tft.setTextSize(0); | ||
+ | tft.print(" | ||
+ | tft.setTextColor(BLACK, | ||
+ | for (unsigned char i=0; i<3; i++) { // Füllt die Zeile mit schwarzen Kästchen; i muss ggf. angepasst werden | ||
+ | tft.print((char)218); | ||
+ | } | ||
+ | refreshPeltier = false; | ||
+ | } | ||
+ | } | ||
+ | previousMillisDisplayCoolingPower = currentMillis; | ||
+ | } | ||
+ | |||
+ | |||
+ | // Anzeige Infobereich Solltemperatur | ||
+ | if (refreshTargettemp == true) { | ||
+ | tft.setCursor(15, | ||
+ | unsigned char i = TargetTemp * 2; // Berechnet den Index für den Array " | ||
+ | if (i < 0) i = 0; // i soll nicht kleiner als " | ||
+ | if (i > 60) i = 60; // i soll nicht größer als " | ||
+ | tft.setTextColor(colors[i], | ||
+ | tft.setTextSize(0); | ||
+ | tft.print(" | ||
+ | tft.setCursor(15, | ||
+ | tft.setTextSize(2); | ||
+ | if (TargetTemp < 10.00) { | ||
+ | tft.print(" | ||
+ | } | ||
+ | tft.print(TargetTemp); | ||
+ | tft.setTextColor(BLACK, | ||
+ | tft.print((char)218); | ||
+ | refreshTargettemp = false; | ||
+ | } | ||
+ | |||
+ | // Anzeige Infobereich Isttemperatur | ||
+ | if (tempCupedge != previousTempCupedge) { | ||
+ | tft.setCursor(15, | ||
+ | unsigned char i = tempCupedge * 2; // Berechnet den Index für den Array " | ||
+ | if (i < 0) i = 0; // i soll nicht kleiner als " | ||
+ | if (i > 60) i = 60; // i soll nicht größer als " | ||
+ | tft.setTextColor(colors[i], | ||
+ | tft.setTextSize(0); | ||
+ | tft.print(" | ||
+ | tft.setCursor(15, | ||
+ | tft.setTextSize(2); | ||
+ | if (tempCupedge < 10.00) { | ||
+ | tft.print(" | ||
+ | } | ||
+ | tft.print(tempCupedge); | ||
+ | tft.setTextColor(BLACK, | ||
+ | tft.print((char)218); | ||
+ | previousTempCupedge = tempCupedge; | ||
+ | } | ||
+ | |||
+ | |||
+ | // Anzeige Infobereich Lüfter | ||
+ | if (fanAlert == true) { | ||
+ | if (refreshFan == true) { | ||
+ | tft.setCursor(0, | ||
+ | tft.setTextSize(0); | ||
+ | tft.setTextColor(RED, | ||
+ | tft.print(" | ||
+ | refreshFan = false; | ||
+ | } | ||
+ | } | ||
+ | else if (fanAlert == false) { | ||
+ | if ((unsigned long)(currentMillis - previousMillisDisplayFanSpeed) >= intervalDisplayFanSpeed) { | ||
+ | if (rpmMax > 0) { // Wenn die Drehzahl größer " | ||
+ | if (refreshFan == true) { | ||
+ | tft.setCursor(12, | ||
+ | tft.setTextSize(0); | ||
+ | tft.setTextColor(WHITE, | ||
+ | tft.print(" | ||
+ | tft.setTextColor(BLACK, | ||
+ | for (unsigned char i=0; i<5; i++) { // Füllt die Zeile mit schwarzen Kästchen; i muss ggf. angepasst werden | ||
+ | tft.print((char)218); | ||
+ | } | ||
+ | refreshFan = false; | ||
+ | } | ||
+ | } | ||
+ | else if (rpmMax == 0) { | ||
+ | if (refreshFan == true) { | ||
+ | tft.setCursor(12, | ||
+ | tft.setTextSize(0); | ||
+ | tft.setTextColor(WHITE, | ||
+ | tft.print(" | ||
+ | tft.setTextColor(BLACK, | ||
+ | for (unsigned char i=0; i<12; i++) { // Füllt die Zeile mit schwarzen Kästchen; i muss ggf. angepasst werden | ||
+ | tft.print((char)218); | ||
+ | } | ||
+ | refreshFan = false; | ||
+ | } | ||
+ | } | ||
+ | previousMillisDisplayFanSpeed = currentMillis; | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | // Anzeigemodus " | ||
+ | if (displayMode == 2) { | ||
+ | // Anzeige Solltemperatur | ||
+ | if (refreshTargettemp == true) { | ||
+ | tft.setCursor(0, | ||
+ | tft.setTextColor(WHITE, | ||
+ | tft.setTextSize(0); | ||
+ | tft.print(" | ||
+ | if (TargetTemp < 10.00) { | ||
+ | tft.print(" | ||
+ | } | ||
+ | tft.print(TargetTemp); | ||
+ | refreshTargettemp = false; | ||
+ | } | ||
+ | |||
+ | |||
+ | // Anzeige Isttemperatur | ||
+ | if (tempCupedge != previousTempCupedge) { | ||
+ | tft.setCursor(70, | ||
+ | tft.setTextSize(0); | ||
+ | unsigned char i = tempCupedge * 2; // Berechnet den Index für den Array " | ||
+ | if (i < 0) i = 0; // i soll nicht kleiner als " | ||
+ | if (i > 60) i = 60; // i soll nicht größer als " | ||
+ | tft.setTextColor(colors[i], | ||
+ | tft.print(" | ||
+ | if (tempCupedge < 10.00) { | ||
+ | tft.print(" | ||
+ | } | ||
+ | tft.print(tempCupedge); | ||
+ | tft.setTextColor(BLACK, | ||
+ | tft.print((char)218); | ||
+ | previousTempCupedge = tempCupedge; | ||
+ | } | ||
+ | |||
+ | |||
+ | // Anzeige Graphen | ||
+ | if ((unsigned long)(currentMillis - previousMillisDisplayGraph) >= intervalDisplayGraph) { | ||
+ | |||
+ | x++; | ||
+ | if (x >= 128) { | ||
+ | x = 0; | ||
+ | // | ||
+ | tft.fillRect(0, | ||
+ | // | ||
+ | // | ||
+ | // | ||
+ | // | ||
+ | // | ||
+ | // | ||
+ | } | ||
+ | |||
+ | // Graph Solltemperatur | ||
+ | unsigned char tt = 114 - TargetTemp * 2; | ||
+ | tft.drawPixel(x, | ||
+ | |||
+ | // Graph Isttemperatur | ||
+ | unsigned char tc = 114 - tempCupedge *2; | ||
+ | unsigned char i = tempCupedge * 2; // Berechnet den Index für den Array " | ||
+ | if (i < 0) i = 0; // i soll nicht kleiner als " | ||
+ | if (i > 60) i = 60; // i soll nicht größer als " | ||
+ | tft.drawPixel(x, | ||
+ | |||
+ | // Graph Kühlleistung | ||
+ | unsigned char p = 114 - outputPeltier * 0.25; | ||
+ | tft.drawPixel(x, | ||
+ | |||
+ | // Graph Lüfterdrehzahl | ||
+ | unsigned char d = 114 - rpmMax / 21; | ||
+ | tft.drawPixel(x, | ||
+ | |||
+ | previousMillisDisplayGraph = currentMillis; | ||
+ | } | ||
+ | |||
+ | // Anzeige Peltier-Element | ||
+ | if ((unsigned long)(currentMillis - previousMillisDisplayCoolingPower) >= intervalDisplayCoolingPower) { | ||
+ | if (refreshPeltier == true) { | ||
+ | tft.setCursor(0, | ||
+ | tft.setTextColor(BLUEBERRY, | ||
+ | tft.setTextSize(0); | ||
+ | tft.print(int(outputPeltier * 0.39 + 0.55)); tft.print(" | ||
+ | tft.setTextColor(BLACK, | ||
+ | for (unsigned char i=0; i<2; i++) { // Füllt die Zeile mit schwarzen Kästchen; i muss ggf. angepasst werden | ||
+ | tft.print((char)218); | ||
+ | } | ||
+ | refreshPeltier = false; | ||
+ | } | ||
+ | previousMillisDisplayCoolingPower = currentMillis; | ||
+ | } | ||
+ | |||
+ | // Anzeige Lüfter | ||
+ | if ((unsigned long)(currentMillis - previousMillisDisplayFanSpeed) >= intervalDisplayFanSpeed) { | ||
+ | tft.setCursor(64, | ||
+ | tft.setTextSize(0); | ||
+ | tft.setTextColor(BLUE, | ||
+ | tft.print(rpmMax); | ||
+ | tft.setTextColor(BLACK, | ||
+ | for (unsigned char i=0; i<3; i++) { // Füllt die Zeile mit schwarzen Kästchen; i muss ggf. angepasst werden | ||
+ | tft.print((char)218); | ||
+ | } | ||
+ | refreshFan = true; | ||
+ | previousMillisDisplayFanSpeed = currentMillis; | ||
+ | } | ||
+ | |||
+ | } | ||
+ | // | ||
+ | if ((unsigned long)(currentMillis - previousMillisSerialPrint) >= intervalSerialPrint) { | ||
+ | |||
+ | // Ausgabe für den seriellen Monitor | ||
+ | /* | ||
+ | // | ||
+ | // | ||
+ | // | ||
+ | |||
+ | Serial.print("; | ||
+ | // | ||
+ | Serial.print("; | ||
+ | // | ||
+ | // | ||
+ | // | ||
+ | |||
+ | // | ||
+ | // | ||
+ | // | ||
+ | Serial.print("; | ||
+ | Serial.print("; | ||
+ | // | ||
+ | // | ||
+ | // | ||
+ | // | ||
+ | // | ||
+ | Serial.print("; | ||
+ | */ | ||
+ | |||
+ | |||
+ | // Ausgabe für SerialComInstruments 4.1 | ||
+ | Serial.print("# | ||
+ | Serial.print("# | ||
+ | Serial.print("# | ||
+ | |||
+ | Serial.print("# | ||
+ | // | ||
+ | Serial.print("# | ||
+ | |||
+ | Serial.print("# | ||
+ | Serial.print("# | ||
+ | |||
+ | Serial.print("# | ||
+ | |||
+ | loopCounter = 0; | ||
+ | |||
+ | //Speichere die aktuelle Zeit in die zughörige Variable | ||
+ | previousMillisSerialPrint = currentMillis; | ||
+ | } | ||
+ | } | ||
+ | </ | ||
+ | Der Sketch verwendet 24428 Bytes (79%) des Programmspeicherplatzes. Das Maximum sind 30720 Bytes. | ||
+ | Globale Variablen verwenden 1700 Bytes (83%) des dynamischen Speichers, 348 Bytes für lokale Variablen verbleiben. Das Maximum sind 2048 Bytes. | ||
+ | |||
+ | Tags: # |
arduino/flaschenkuehler/programmversion_0.4.1500993165.txt.gz · Zuletzt geändert: 18.05.2023 12:16 (Externe Bearbeitung)