Simulation läuft in Step 0 sauber durch, in Step 1 sind die Temperaturen nur sporadisch sichtbar.
Bei der Ist temp fehlt der Doppelpunkt
Alarmauslösung und Darstellung klappt
Simulation läuft in Step 0 sauber durch, in Step 1 sind die Temperaturen nur sporadisch sichtbar.
Bei der Ist temp fehlt der Doppelpunkt
Alarmauslösung und Darstellung klappt
Bild 1 find ich erstmal ok.
Wenn die Ist-Temp nicht kommt, stimmt was mit dem blinken nicht - schau ich drauf.
Bild 2 hab ich mal für die Berichtigung des Endscreens genutzt:
Nur die Funktion austauschen:
void endScreen()
{
lcd.clear();
uint32_t myTime = (zyklusEndTime - zyklusStartTime) / 100; // Laufzeit in 1/100 ms
uint32_t mySek = myTime / 10; // Sekunden errechnen
byte myZehnt = myTime - mySek * 10; // Zehntel...
byte myMin = mySek / 60; // Minuten
mySek = mySek % 60; // Sekunden ist der Rest aus Minuten
lcd.print(F("ZyklusTime: "));
lcd.setCursor(12, 0);
if (myMin < 10)
{ lcd.print(0); }
lcd.print(myMin);
lcd.print(':');
if (mySek < 10)
{ lcd.print(0); }
lcd.print(mySek);
lcd.print('.');
lcd.print(myZehnt);
lcd.setCursor(0, 1);
lcd.print(F("Ausloesung"));
if (alarmTime != 0)
{
myTime = (alarmTime - zyklusStartTime) / 100;
mySek = myTime / 10;
myZehnt = myTime - mySek * 10;
myMin = mySek / 60;
mySek = mySek % 60;
lcd.setCursor(0, 2);
lcd.print(F("Time: "));
if (myMin < 10)
{ lcd.print(0); }
lcd.print(myMin);
lcd.print(':');
if (mySek < 10)
{ lcd.print(0); }
lcd.print(mySek);
lcd.print('.');
lcd.print(myZehnt);
lcd.setCursor(0, 3);
lcd.print(F("Temp: "));
lcd.print(alarmTemp);
lcd.print(F("\337C"));
}
else
{
lcd.print(F("KEINE"));
}
}
Und dann will ich wissen:
Was passiert, wenn der Button gedrückt ist, wenn der Controller startet?
-> Normalerweise sollte eine Meldung kommen
Was passiert, wenn Du ein "Notaus" bei mehr als 80°C provozierst und dann gleich versuchst einen Neustart durch erneuten Tastendruckk auszulösen?
-> Es müsste die Meldung Notaus kommen
-> Beim Neustart solange noch heiss, müsste der Controller entweder auf einen weiteren Tastendruck oder auf abkühlen warten.
Du hast noch 1 Stunde.... ![]()
So, da hier nix mehr kam, ist wohl davon auszugehen, das es sich erledigt hat.
Dann mach das hier zu - heute ist eh Abgabe.
Ich hab noch das blinken der Temperature und die Prüfung der Startbedingungen überarbeitet.
Für mich ist dann der Sack zu. Ich hab wieder was gelernt. Das war Sinn und Zweck der Übung.
Viel Erfolg!
// Forensketch "Meisterarbeit" Sensoren/Actoren - Zusammenspiel und Livelogging
// via https://forum.arduino.cc/t/unlosbare-auflagen-meisterprojekt/1122451
// This Code is Public Domain
//
// Für die eingebundenen Libraries gelten die entsprechenden Lizenzbedingungen
//
/*
mehrstufige Heizungssteuerung mit Display an einem Arduino-kompatiblem UNO
PinBelegung - benutzte PIN
Digital:
0 - RXD / USB
1 - TXD / USB
2 - Taster
3 - (PWM) Mosfet / Relais (Heizung)
4 - Soft-SPI - CS
5 - Soft-SPI - DI
6 - Soft-SPI - DO
7 - Soft-SPI - CLK
8 - Alarm
13 - OnBoardLED - Heartbeat
Analog:
A4 - I2C - SDA
A5 - I2C - SCL
*/
const char version[] = {"Version 0.4b"};
#define SIMULATION // wenn aktiv, wird die Temperatur gezwungen hochgezählt
// verwendete Bibliotheken:
#include <Adafruit_MAX31865.h> // https://github.com/adafruit/Adafruit_MAX31865
Adafruit_MAX31865 thermo {4, 5, 6, 7}; // Eröffnet eine Instanz; Konfig als Software SPI(!)
// Wenn nicht aktiviert, wird später PT100 voreingestellt
#define PT1000
#ifdef PT1000 // Legt Referenzwerte fest
constexpr float RREF {4300.0}; // Einstellung PT1000
constexpr float RNOMINAL {1000.0};
#else
constexpr float RREF {430.0}; // Einstellung PT100
constexpr float RNOMINAL {100.0};
#endif
#include <LiquidCrystal_I2C.h> // https://github.com/lucasmaziero/LiquidCrystal_I2C
constexpr byte zeilen {4}; //
constexpr byte spalten {20}; //
constexpr byte address {0x27}; // I2C-Adresse
LiquidCrystal_I2C lcd(address, spalten, zeilen); // eröffnet eine Instanz
/*
Kein #define für die PIN verwenden!
Die Zuweisung könnte an jeder beliebigen Stelle im Code verändert werden und führt zwangsläufig zu Problemen
Pins können sich nicht ändern: Darum const.
Ein (unsigned) integer verwendet 2 byte! Auf den AVR Platz sparen! Darum uint8_t bzw byte.
Pins immer mit einem Namen versehen, damit im Code später klar ist, wozu der Pin dient!
Die Wartung des Code ist damit ebenfalls einfacher, weil, an einer Stelle geändert, gilt diese Änderung systemweit
*/
// verwendete Konstanten
// verwendete PIN
constexpr byte btnPin {2};
constexpr byte relPin {3};
constexpr byte ctlLed {LED_BUILTIN}; // Onboard-Led für Heartbeat
constexpr byte almPin {8};
// Relaisbausteine gibt es LOW-aktiv oder HIGH-aktiv darum Konstante für den Zustand
// Bei Verwendung eines Mosfet mit PWM wird das eine Statusvariable!
constexpr bool heaterOn {HIGH}; // wenn im Code eine Änderung der Logik notwendig ist, reicht es aus hier diese einzutragen!
// Entprellzeit für Taster
constexpr uint32_t debounceTime {200}; // Zeit in ms zur Unterdrückung des Prellen der Taste
// Heizdaten -> Wird ggfls noch weiterentwickelt!
enum class STATE {off, init, on, out, end}; // Zykluszustände
struct HEATPARAMETER // Wird später evtl. noch class
{
// Werte werden im INIT gefüllt
const int lowTemp; // untere Temperaturgrenze - Einschaltpunkt
const int highTemp; // obere Temperaturgrenze - Abschaltpunkt
const int tolerance; // Abweichung in Kelvin, die noch zulässig ist
const uint32_t holdTime; // Intervall in dem Temperaturgrenze nicht verlassen werden darf
// Werte werden zur Laufzeit ermittelt
uint32_t startTime; // Merker für Laufzeitauswertung (entspricht ggfls endTime vorheriger Stufe bzw. zyklusStartzeit)
uint32_t endTime; // dito. (entspricht ggfls. startTime der nächsten Stufe, bzw. zyklusEndzeit)
uint32_t highTime; // Merker für Zeit des erreichen von highTemp
};
// Für jeden Schritt eine Instanz
// Werte folgen der Liste im struct! (default-Initialisierungsliste)
HEATPARAMETER heatLow {29, 30, 1, 30000, 0, 0, 0}; // Anlage einer Instanz
HEATPARAMETER heatMiddle {60, 70, 1, 30000, 0, 0, 0};
HEATPARAMETER heatHigh {119, 120, 1, 30000, 0, 0, 0};
HEATPARAMETER heat[] = {heatLow, heatMiddle, heatHigh}; // Alle Instanzen in ein Array
const uint8_t heatSteps = sizeof(heat) / sizeof(heat[0]);// ermittelt die Anzahl der Instanzen
// Systemweite Variablen
int temperature; // Es werden keine Nachkommastellen benötigt
STATE status = STATE::off; // Ausgangszustand setzen
byte step; // Merker für Ablaufsteuerung / heat-Instanz
uint32_t zyklusStartTime; // Merker zum späteren ausrechnen der Zyklusgesamtzeit
uint32_t zyklusEndTime;
uint32_t alarmTime;
uint32_t alarmTemp;
//
void setup()
{
// Standard ist 9600, langsame Geschwindigkeit läßt den Puffer ggfls. zu schnell überlaufen und es gehen Infos verloren
Serial.begin(115200);
Serial.println(F("Start...")); // Um zu sehen, das der Controller läuft....
pinMode(ctlLed, OUTPUT); //
digitalWrite(ctlLed, HIGH);
pinMode(btnPin, INPUT_PULLUP); // Taster schliessen nach GND
pinMode(relPin, OUTPUT);
digitalWrite(relPin, !heaterOn);
pinMode(almPin, INPUT_PULLUP);
thermo.begin(MAX31865_2WIRE); // mögliche Varianten: 2WIRE, 3WIRE, 4WIRE (!)
lcd.begin();
lcd.backlight();
startStatesCheck(); // Grundeinstellungen abfragen / setzen
}
//
void loop()
{
#ifdef SIMULATION
static uint32_t lastmillis = 0;
static int myTemp = 0;
constexpr uint32_t intervall = 800;
if (digitalRead(relPin) == heaterOn)
{
if (millis() - lastmillis > intervall)
{
myTemp++;
lastmillis = millis();
}
}
temperature = myTemp;
if (status == STATE::off)
{ myTemp = 0; }
#else
// Holt die Temperatur und mit dem Rückgabewert kann die weitere Ausführung gesteuert werden
if (getTemperature()) // Temp-Messung fehlerhaft ?
{
// Die folgende Zeile würde bei einer fehlerhaften Messung
// status = STATE::off; // Status zurücksetzen
// Hier können noch weitere Auswertefunktionen rein
}
#endif
checkBtn(); // Buttonzustand muss unabhängig verarbeitet werden!
stateMachine(); // Testablauf
chkAlarm();
Serial.flush(); // Sicherstellen, das alle Ausgaben getätigt sind
}
void printThermoFailure(byte fault) // Basiert, mit leichten Änderungen, auf dem Example der Adafruit-Lib
{
if (fault & MAX31865_FAULT_HIGHTHRESH)
{ Serial.println(F("RTD High Threshold")); }
if (fault & MAX31865_FAULT_LOWTHRESH)
{ Serial.println(F("RTD Low Threshold")); }
if (fault & MAX31865_FAULT_REFINLOW)
{ Serial.println(F("REFIN- > 0.85 x Bias")); }
if (fault & MAX31865_FAULT_REFINHIGH)
{ Serial.println(F("REFIN- < 0.85 x Bias - FORCE- open")); }
if (fault & MAX31865_FAULT_RTDINLOW)
{ Serial.println(F("RTDIN- < 0.85 x Bias - FORCE- open")); }
if (fault & MAX31865_FAULT_OVUV)
{ Serial.println(F("Under/Over voltage")); }
}
//
bool getTemperature()
{
thermo.clearFault(); // löscht den Fehlerspeicher
temperature = static_cast<int>(thermo.temperature(RNOMINAL, RREF)); // Gibt Temp zurück anhand der Konfiguration
byte thermoCheck = thermo.readFault(); // Rückgabe des Fehlerspeichers
if (thermoCheck) // Fehlerspeicher war nicht iO?
{
Serial.println(F("Thermo Messung fehlerhaft"));
printThermoFailure(thermoCheck); // Kann entfallen, wenn detailierte Auskunft nicht benötigt
}
return thermoCheck; // gibt Fehlerwert zurück
}
//
void checkBtn()
{
/*
Um mit einer Taste sowohl den Programmstart, als auch ein "Not-"Aus zu realisieren:
ein wenig Logik und gegenseitiges Verriegeln der Zustände
*/
static bool btnPressed = false; // Merker das Taste ausgelöst wurde
static uint32_t lastBtnTime = 0; // Merker, wann Taste ausgelöst wurde
if (!digitalRead(btnPin)) // Wenn gedrueckt
{
lastBtnTime = millis(); // Auslösezeit merken für debounce
if (!btnPressed) // Wenn vorher nicht gedrueckt
{
btnPressed = true; // merken, das gedrueckt
if (status == STATE::off) // Wenn Status: warten
{
status = STATE::init; // Zyklus starten
}
else
{
if (status == STATE::on) // Wenn im Zyklus
{ Serial.println(F("Notaus!")); } // Ausgabe als was der Auslöser gewertet wird
else // sonst
{ Serial.println(F("Neustart!")); } // ->
status = STATE::off; // In jedem Fall: abschalten ;)
}
}
}
else if (btnPressed && // Wenn nicht gedrückt UND vorher gedrückt UND
millis() - lastBtnTime > debounceTime) // kein bounce mehr
{
btnPressed = false;
}
}
//
void heating(HEATPARAMETER &myHeat) // das struct spart Parameterliste ;)
{
if (temperature >= myHeat.highTemp) // Obere Grenze erreicht
{
digitalWrite(relPin, !heaterOn); // Heizung aus
/*
Die folgende Zeile vertraut darauf, das (beim Programmstart)
highTime mit 0 gesetzt wurde und millis() nicht überläuft
Nach etwas mehr als 49 Tage kommt es für millis() zum Überlauf
Wenn dann millis() == 0 wäre, würde es zu einer Ungenauigkeit
von 1ms für die Messung führen, da highTime bei millis() == 1
noch immer 0 ist und dann erneut gesetzt würde.
*/
if (myHeat.highTime == 0) // Wenn Platte bisher noch nicht heiss war
{
myHeat.highTime = millis(); // Zeitpunkt merken
}
}
else if (temperature < myHeat.lowTemp) // Temperatur unterschritten?
{
digitalWrite(relPin, heaterOn); // (nach)heizen
}
/*
// Da kein Abbruch bei Toleranzunter- / überschreitung gefordert ist: auskommentiert
if ((heat[step].highTime && // Oberer Grenzpunkt schon ausgelöst? UND
temperature < myHeat.lowTemp - myHeat.tolerance) ||// Temperatur unterschritten
temperature > myHeat.highTemp + myHeat.tolerance) // bzw. generell temperatur zu hoch?
{
// Hier kann neu gestartet werden oder whatever
}
*/
}
//
void stateMachine()
{
switch (status)
{
case STATE::off: // Zustand alles aus
digitalWrite(relPin, !heaterOn); // Platte gezielt aus
digitalWrite(ctlLed, LOW); // Anzeige aus
//status = STATE::init; // Um ohne Tastendruck die Simulation zu starten ;)
break;
case STATE::init:
Serial.println(F("Starte Zyklus"));
for (byte b = 0; b < heatSteps; b++) // durch alle Steps
{
heat[b].startTime = 0; // Alle Werte resetten
heat[b].endTime = 0; //
heat[b].highTime = 0; //
alarmTime = 0;
}
step = 0; // ersten Step setzen
zyklusStartTime = millis(); // Startzeit merken
zyklusEndTime = zyklusStartTime; // vorläufiges Ende ist der Start ;)
status = STATE::on;
break;
case STATE::on: // Heizen
if (heat[step].startTime == 0) // Prüfen ob nicht gestartet
{
heat[step].startTime = millis(); // Zeit Merken
}
heating(heat[step]); // heizen / prüfen / nachheizen
// Nächste Zeile prüft, ob .highTime gesetzt wurde UND
// ob die temperatur lange genug innerhalb der Toleranz gelegen hat
if (heat[step].highTime != 0 &&
millis() - heat[step].highTime > heat[step].holdTime)
{
heat[step].endTime = millis(); // Merken, wann die Stufe beendet war
if (step < heatSteps - 1) // wenn noch eine Stufe verfügbar
{
step++; // nächste Heizstufe setzen
Serial.print(F("starte nächste Stufe: "));
Serial.println(step + 1);
}
else // keine Stufe mehr => Ende des Zyklus
{
zyklusEndTime = millis(); // Ende merken
status = STATE::out; // Vorbereitung Ausgabe
}
}
stepScreen();
heartBeat(1000 / (step + 1)); // blinken
break;
// Ausgabe
case STATE::out:
//serialPrintOut();
endScreen();
status = STATE::end; // keine Wiederholung der Ausgabe erzwingen
break;
case STATE::end:
break;
}
}
//
void chkAlarm()
{
if (!digitalRead(almPin)) // Alarmpin aktiv gezogen
{
if (alarmTime == 0) // bisher keine Alarmzeit gemerkt?
{
alarmTime = millis(); // Merken
alarmTemp = temperature;
}
}
}
//
bool btnIsPressed() // gibt false zurück wenn nicht gedrückt und bouncetime abgelaufen
{
static uint32_t checkTime = 0;
if (!digitalRead(btnPin)) // Taste ausgelöst
{ checkTime = millis(); } // Zeit merken
bool ret = millis() - checkTime > debounceTime; // gibt true wenn debounce überschritten
return !ret; // umgekehrten Wert zurück geben
}
//
void startStatesCheck()
{
startScreen();
delay(3000); // Karl-Wilhelm-Gustav Gedächtniszeit
//bool merker = false;
// Prüfung auf gedrückten StartButton
if (!digitalRead(btnPin)) // Taste gedrückt?
{
lcd.setCursor(4, 3);
lcd.print(F("CHECK BUTTON!"));
}
while (btnIsPressed()); // Warten auf losgelassene Taste
Serial.println(F("Taste Freigegeben"));
lcdClearLine(3); // Meldung löschen
// Prüfung auf heisse Platte
while (checkHeatPlate4Start() && // Platte zu heiss? (z.B. nach notaus) UND
!btnIsPressed()) // Taste nicht ausgelöst
{
overTemperatureScreen();
}
Serial.println(F("chk Overtemperature - ok"));
lcdClearLine(3);
lcd.backlight(); // und an machen..
while (btnIsPressed()); // Warten auf losgelassene Taste
// Prüfung auf ausgelösten Alarmpin
while (!digitalRead(almPin) && // Vor Zyklusstart Alarmpin ausgelöst UND
!btnIsPressed()) // Taste nicht gedrückt
{
lcd.setCursor(4, 3);
lcd.print(F("CHECK ALARM!"));
}
Serial.println(F("chk AlarmPin - ok"));
while (btnIsPressed()); // Warten auf losgelassene Taste
//lcdClearLine(3);
startScreen(); // Zwangsweise Screen beschreiben
lcd.setCursor(0, 3);
lcd.print(F("Press Startbutton"));
}
//
void startScreen()
{
lcd.clear(); //
lcd.setCursor(3, 0); //
lcd.print(F("Startbildschirm"));
lcd.setCursor(6, 1);
lcd.print(version);
lcd.setCursor(4, 2);
lcd.print(F("Please wait"));
}
//
bool checkHeatPlate4Start() // prüft auf Temperatur der Heizung
{
int minTemp = 10000;
for (byte b = 0; b < heatSteps; b++) // durch alle Structs...
{
if (minTemp > heat[b].lowTemp) // finde die kleinste Temperatur
{
minTemp = heat[b].lowTemp; // Merken
}
}
return temperature >= minTemp;
}
//
void overTemperatureScreen() // Aufruf wenn beim Start Material zu heiss
{
constexpr uint32_t intervall {1000}; // hier ist intervall Ein- UND Auszeit(!)
static uint32_t lastBlink = 0;
if (millis() - lastBlink > intervall) //
{
lcd.backlight(); //
lastBlink = millis();
}
else if (millis() - lastBlink > intervall / 2) //
{
lcd.noBacklight();
lcd.clear();
lcd.setCursor(0, 0);
lcd.print(F(" OVERTEMPERATURE! "));
lcd.setCursor(0, 1);
lcd.print(F(" Please wait..."));
lcd.setCursor(1, 3);
lcd.print(F("ignore? Press Btn"));
}
}
//
void stepScreen()
{
static byte lastStep = 0xFF;
const byte bufSize = spalten + 1; // Anzahl Spalten + '\0'
char buf[bufSize] = {'\0'}; // Buffer für Ausgabezeile
if (step != lastStep)
// Screen mit statischen Werten füllen
{
lcd.clear();
// Zeile 1
lcd.setCursor(0, 0);
lcd.print("Step: "); // #5
lcd.print(step); // #6
lcd.print(" T: : ."); // #10 #14 #17
// Zeile 2
snprintf(buf, bufSize, "Soll:%3i\337C Ist: \337C", // #15
heat[step].highTemp);
lcd.setCursor(0, 1);
lcd.print(buf);
// Zeile 3
// wenn %2ld nicht geht, dann static_cast auf int!
snprintf(buf, bufSize, "Hs: %2ldsek Hi: sek", // #13
heat[step].holdTime / 1000);
lcd.setCursor(0, 2);
lcd.print(buf);
// Zeile 4 wird nur dynamisch angelegt!
lastStep = step;
}
// Zeile 1 zusammenstellen
static uint32_t lastTime = 0; // Merker für letzten Ausgabewert
uint32_t myTime = (millis() - heat[step].startTime) / 100; // Zeit seit Start des Step in zehntel Sekunde
if (lastTime != myTime) // Zeit hat sich geändert?
{
lastTime = myTime; // Merken
uint32_t mySek = myTime / 10; // Sekunden errechnen
byte myZehnt = myTime - mySek * 10; // Zehntel...
byte myMin = mySek / 60; // Minuten
mySek = mySek % 60; // Sekunden ist der Rest aus Minuten
lcd.setCursor(11, 0);
if (myMin < 10)
{ lcd.print(0); }
lcd.print(myMin);
lcd.setCursor(14, 0);
if (mySek < 10)
{ lcd.print(0); }
lcd.print(mySek);
lcd.setCursor(17, 0);
lcd.print(myZehnt);
}
// Zeile 2 zusammenstellen
static bool blinkStatus = false;
static uint32_t blinkTime = 0;
char tbuf[4] = {'\0'};
if (millis() - blinkTime > 250) // Letzte Ausgabezeit abgelaufen
{
if (heat[step].lowTemp - heat[step].tolerance > temperature) // Temperatur noch nicht erreicht
{
blinkStatus = !blinkStatus; // temperature blinkt ;)
}
else
{ blinkStatus = false; } // temperature blinkt nicht mehr ;)
if (blinkStatus) // wenn aktiv
{ snprintf(tbuf, bufSize, " "); }
else
{ snprintf(tbuf, bufSize, "%3i", temperature); }
lcd.setCursor(14, 1);
lcd.print(tbuf);
blinkTime = millis();
}
// Zeile 3 zusammenstellen
byte holdTime = 0;
static byte lastHoldTime = 0;
if (heat[step].highTime != 0)
{ holdTime = (millis() - heat[step].highTime) / 1000 ; }
if (holdTime != lastHoldTime)
{
lastHoldTime = holdTime;
lcd.setCursor(14, 2);
if (holdTime < 10)
{ lcd.print(0); }
lcd.print(holdTime);
}
// Zeile 4 zusammenstellen
static bool lastHeater = !heaterOn;
bool isHeat = digitalRead(relPin);
if (lastHeater != isHeat) // Wenn Heizung Zustand geändert
{
lastHeater = isHeat; // Zustand merken
lcd.setCursor(0, 3);
if (isHeat == heaterOn) // Ausgabe je nach Zustand
{ lcd.print(F("Heating")); }
else
{ lcdClearLine(3); }
}
if (alarmTime) // AlarmTime gesetzt?
{
lcd.setCursor(12, 3);
lcd.print(F("ALARM!")); // ausgeben
}
}
//
void endScreen()
{
lcd.clear();
uint32_t myTime = (zyklusEndTime - zyklusStartTime) / 100; // Laufzeit in 1/100 ms
uint32_t mySek = myTime / 10; // Sekunden errechnen
byte myZehnt = myTime - mySek * 10; // Zehntel...
byte myMin = mySek / 60; // Minuten
mySek = mySek % 60; // Sekunden ist der Rest aus Minuten
lcd.print(F("ZyklusTime: "));
lcd.setCursor(12, 0);
if (myMin < 10)
{ lcd.print(0); }
lcd.print(myMin);
lcd.print(':');
if (mySek < 10)
{ lcd.print(0); }
lcd.print(mySek);
lcd.print('.');
lcd.print(myZehnt);
lcd.setCursor(0, 1);
lcd.print(F("Ausloesung"));
if (alarmTime != 0)
{
myTime = (alarmTime - zyklusStartTime) / 100;
mySek = myTime / 10;
myZehnt = myTime - mySek * 10;
myMin = mySek / 60;
mySek = mySek % 60;
lcd.setCursor(0, 2);
lcd.print(F("Time: "));
if (myMin < 10)
{ lcd.print(0); }
lcd.print(myMin);
lcd.print(':');
if (mySek < 10)
{ lcd.print(0); }
lcd.print(mySek);
lcd.print('.');
lcd.print(myZehnt);
lcd.setCursor(0, 3);
lcd.print(F("Temp: "));
lcd.print(alarmTemp);
lcd.print(F("\337C"));
}
else
{
lcd.print(F("KEINE"));
}
}
//
void serialPrintOut()
{
Serial.println(F("Alle Zeiten in ms"));
Serial.print(F("Zyklus Start: "));
Serial.println(zyklusStartTime);
Serial.print(F("Zyklus Ende: "));
Serial.println(zyklusEndTime);
Serial.print(F("Gesamtlaufzeit Zyklus: "));
Serial.println(zyklusEndTime - zyklusStartTime);
Serial.println(F("Stufe Startzeit EndZeit Laufzeit Heiss:SOLL Heiss:IST"));
for (byte b = 0; b < heatSteps; b++)
{
Serial.print(b + 1);
Serial.print('\t');
Serial.print(heat[b].startTime);
Serial.print('\t');
Serial.print(heat[b].endTime);
Serial.print('\t');
Serial.print(heat[b].endTime - heat[b].startTime);
Serial.print('\t');
Serial.print(heat[b].holdTime);
Serial.print('\t');
Serial.println(heat[b].endTime - heat[b].highTime);
}
Serial.println();
}
//
void lcdClearLine(const byte line)
{
lcd.setCursor(0, line);
for (byte b = 0; b < spalten; b++)
{ lcd.print(' '); }
}
//
void heartBeat(const uint32_t blinkTime) // realisiert ein blinklicht
{
static uint32_t lastBlink = 0;
if (millis() - lastBlink > blinkTime)
{
lastBlink = toggle(ctlLed); // auf onboard-LED
}
}
//
uint32_t toggle(const byte pin)
{
digitalWrite(pin, !digitalRead(pin));
return millis();
}
Da kommt "CHECK BUTTON!"
Habe ich gerade getestet, wird normal wieder ein neuer Zyklus gestartet
Dann ist es richtig - Der Button muss losgelassen sein.
Zu jeder Bestätigung wird ausgewertet, ob der Taster auf LOW gezogen ist.
Wenn der vorher (unbeabsichtigt gedrückt) schon auf LOW wäre, würde der Zyklus starten, ohne das Du das aktiv willst.
Darum dort die Abfrage und Anzeige, das der losgelassen / frei sein muss.
Mit dem aktuellen Code auch?
Eigentlich sollte, wenn die Temperatur größer der kleinsten Temperatur aus den Haltezyklen ist, der Bildschirm anfangen zu blinken - und auf einen Tastendruck oder das abkühlen der des Sensors warten.
![]()
Ich bin schon spät dran...
versuch die Logik zu verstehen ...
Im Übrigen habe ich das mit der ausgelösten ThermalCam auch nich mit drin - wenn da also der status ausgelöst auf PIN 8 anliegt, musst Du ebtweder bestätigen oder warten, bis PIN8 wieder frei ist.
(So der Plan)
Ist bei Start die IST Temp größer der Soll Temp geht er sofort in die Haltezeit, bis er bei einer Stufe ist, wo der SOLL größer IST ist.
Ich würde es so programmieren, dass wenn der Alarm der Kamera zurückgesetzt wird, der Kontakt des ADAM-Moduls wieder zurückgesetzt wird.
Das ich dann vor Start eines neuen Zyklus einen Reset machen muss, ist für mich nicht so schlimm.
Wie kommst Du denn auf die krude Idee?
Wozu hast Du denn die Taste?
Ich versteh's nicht.... ![]()
Ich hab nochmal in den Code oben geschaut.
Das war genau die Idee dahinter, das ich vor einem Zyklus prüfe, ob der Eingangspin belegt wird.
Denn wenn Du vergisst den Auslöser zurückzusetzen, erfolgt mit dem Zyklusstart sofort die Erkennung, das ausgelöst wurde.
Also muss zum Start sichergestellt sein, das der Auslöser inaaktiv ist.
Da müsste ich nochmal drauf schaun.
Wäre schade, wenn das nicht klappt.
Allerdings ist die Frage, wie der aktuelle Stand zum Einreichen ist - sonst macht es keinen Sinn sich noch weiter damit zu beschäftigen.
Also ich habe den Code jetzt in den Entwurf gepackt, aber da er sowieso Zukauf ist und nicht bewertet wird, kann ich noch Änderungen in den Versionsständen vornehmen.
Hä...??
was ist den das für eine bescheuerte Bewertung?
Auf der einen Seite wird es verlangt, dann aber als zukauf eingestuft..
Wie soll man auch Zukäufe bewerten, es wurde ja nicht vom Prüfling gemacht.
Die Auflagen dienen ja einzig und allein dem Prüfungsausschuss, damit gesagt werden kann, "das ist für uns eines Meisters würdig"...
Was natürlich in jeder Handwerkskammer in Deutschland anders bewertet wird, jede HWK kann eigene Prüfungsschwerpunkte erstellen.
Also null komma null miteinander vergleichbar ![]()
Dass ein Zukauf nicht bewertet wird ist ja auch nicht das was ich kritisiere, sondern dass ein bestimmter Zukauf verlangt / erwartet wird...
Edit: Aber müssig das breit zu treten... ![]()
Wenn ich ein mega C++ Progammierer wäre, hätte ich das auch ohne Zukauf machen können und dann wäre es auch bewertet worden.
Bin ich nur leider nicht.
In der Prüfung wird noch mehr verlangt.
Stell Dir das als Sammlung verschiedener Module vor.
Über die Anzahl der Module hinweg gibt es eine bestimmte Menge, die der Prüfling selbst zu erledigen hat um die Prüfung zu bestehen.
Ebenfalls ist erforderlich, die Aufgabe zu lösen.
Der Meisteranwärter darf also einen Teil seiner Aufgaben auslagern, muss aber in der Lage sein das Ergebnis in seiner Vollständigkeit darzustellen und zu verteidigen, wie und mit welchen Mitteln er den Lösungsweg beschritten hat. (In manchen Fällen ist das auch wie beim Bachelor als Punktesystem gestaltet - hast Du die Mindestpunktezahl an Vorlesungen nicht erreicht, bist Du raus)
Sowas gab es schon früher in der Schule.
Hast Du eine Aufgabe richtig gelöst, der Lösungsweg war in Teillen vorgegeben und Du bist davon abgewichen, hast Du auch nur anteilig Punkte bekommen, die Aufgabe war aber bestanden. ![]()
Das ist ein Problem vieler Lehrer. Es gibt meistens nicht nur den einen Rechenweg. Egal wie man zur richtigen Lösung kommt. Wenn man es plausibel erklären bzw. nachvollziehen kann sollte alles i.O. sein. Ich hatte manchesmal meinen Ausbilder positiv verblüfft.
Der sah das sportlich.
ich bin da weniger happy mit diesen Zeilen/Kommentaren:
HEATPARAMETER heatLow {29, 30, 1, 30000, 0, 0, 0}; // Anlage einer Instanz
HEATPARAMETER heatMiddle {60, 70, 1, 30000, 0, 0, 0};
HEATPARAMETER heatHigh {119, 120, 1, 30000, 0, 0, 0};
HEATPARAMETER heat[] = {heatLow, heatMiddle, heatHigh}; // Alle Instanzen in ein Array
das legt nämlich Kopien an - finde ich unpraktisch/unnötig.
Beispiel 1:
// NON WORKING CODE
struct HEATPARAMETER // Wird später evtl. noch class
{
// Werte werden im INIT gefüllt
int lowTemp; // untere Temperaturgrenze - Einschaltpunkt
int highTemp; // obere Temperaturgrenze - Abschaltpunkt
int tolerance; // Abweichung in Kelvin, die noch zulässig ist
uint32_t holdTime; // Intervall in dem Temperaturgrenze nicht verlassen werden darf
// Werte werden zur Laufzeit ermittelt
uint32_t startTime; // Merker für Laufzeitauswertung (entspricht ggfls endTime vorheriger Stufe bzw. zyklusStartzeit)
uint32_t endTime; // dito. (entspricht ggfls. startTime der nächsten Stufe, bzw. zyklusEndzeit)
uint32_t highTime; // Merker für Zeit des erreichen von highTemp
};
// Für jeden Schritt eine Instanz
HEATPARAMETER heatLow {29, 30, 1, 30000, 0, 0, 0}; // Anlage einer Instanz
HEATPARAMETER heatMiddle {60, 70, 1, 30000, 0, 0, 0};
HEATPARAMETER heatHigh {119, 120, 1, 30000, 0, 0, 0};
HEATPARAMETER heat[] = {heatLow, heatMiddle, heatHigh}; // Alle Instanzen in ein Array
void setup() {
Serial.begin(115200);
heat[0].lowTemp = 42;
Serial.println(heat[0].lowTemp);
Serial.println(heat[1].lowTemp);
Serial.println(heat[2].lowTemp);
Serial.println(heatLow.lowTemp);
Serial.println(heatMiddle.lowTemp);
Serial.println(heatHigh.lowTemp);
}
void loop() {
// put your main code here, to run repeatedly:
}
das gibt aus:
42
60
119
29
60
119
heat[0] hat sich verändert, die heatLow aber nicht.
Ich kann mir nicht vorstellen, dass das Absicht ist.
In einem deiner anderen Sketch ist mir das auch schon mal aufgefallen, daher mal ein Versuch das zu ändern.
Beispiel 2:
hat man einzelne Objekte/Strukturen könnte man ein Array wie folgt machen:
// use single variables, have pointer array
struct HEATPARAMETER // Wird später evtl. noch class
{
// Werte werden im INIT gefüllt
int lowTemp; // untere Temperaturgrenze - Einschaltpunkt
int highTemp; // obere Temperaturgrenze - Abschaltpunkt
int tolerance; // Abweichung in Kelvin, die noch zulässig ist
uint32_t holdTime; // Intervall in dem Temperaturgrenze nicht verlassen werden darf
// Werte werden zur Laufzeit ermittelt
uint32_t startTime; // Merker für Laufzeitauswertung (entspricht ggfls endTime vorheriger Stufe bzw. zyklusStartzeit)
uint32_t endTime; // dito. (entspricht ggfls. startTime der nächsten Stufe, bzw. zyklusEndzeit)
uint32_t highTime; // Merker für Zeit des erreichen von highTemp
};
// Für jeden Schritt eine Instanz
HEATPARAMETER heatLow {29, 30, 1, 30000, 0, 0, 0}; // Anlage einer Instanz
HEATPARAMETER heatMiddle {60, 70, 1, 30000, 0, 0, 0};
HEATPARAMETER heatHigh {119, 120, 1, 30000, 0, 0, 0};
HEATPARAMETER * heat[] {&heatLow, &heatMiddle, &heatHigh}; // create an array of 3 pointers
void setup() {
Serial.begin(115200);
heat[0]->lowTemp = 42; // change a value
Serial.println(heat[0]->lowTemp);
Serial.println(heat[1]->lowTemp);
Serial.println(heat[2]->lowTemp);
Serial.println(heatLow.lowTemp);
Serial.println(heatMiddle.lowTemp);
Serial.println(heatHigh.lowTemp);
}
void loop() {
// put your main code here, to run repeatedly:
}
Beispiel 3
oder - man nimmt das array und macht sich pointer auf die einzelnen Elemente:
struct HEATPARAMETER // Wird später evtl. noch class
{
// Werte werden im INIT gefüllt
int lowTemp; // untere Temperaturgrenze - Einschaltpunkt
int highTemp; // obere Temperaturgrenze - Abschaltpunkt
int tolerance; // Abweichung in Kelvin, die noch zulässig ist
uint32_t holdTime; // Intervall in dem Temperaturgrenze nicht verlassen werden darf
// Werte werden zur Laufzeit ermittelt
uint32_t startTime; // Merker für Laufzeitauswertung (entspricht ggfls endTime vorheriger Stufe bzw. zyklusStartzeit)
uint32_t endTime; // dito. (entspricht ggfls. startTime der nächsten Stufe, bzw. zyklusEndzeit)
uint32_t highTime; // Merker für Zeit des erreichen von highTemp
};
// Für jeden Schritt eine Instanz
HEATPARAMETER heat[] { // create an array of structures
{29, 30, 1, 30000, 0, 0, 0},
{60, 70, 1, 30000, 0, 0, 0},
{119, 120, 1, 30000, 0, 0, 0}
};
HEATPARAMETER * heatLow = heat; // pointer to index 0
HEATPARAMETER * heatMiddle = heat + 1;
HEATPARAMETER * heatHigh = heat + 2;
void setup() {
Serial.begin(115200);
heat[0].lowTemp = 42; // change a value
Serial.println(heat[0].lowTemp);
Serial.println(heat[1].lowTemp);
Serial.println(heat[2].lowTemp);
Serial.println(heatLow->lowTemp);
Serial.println(heatMiddle->lowTemp);
Serial.println(heatHigh->lowTemp);
}
void loop() {
// put your main code here, to run repeatedly:
}
Beispiel 2 und 3 geben aus:
42
60
119
42
60
119
Schlusswort
da du imho die "benannten" Strukturen eh nicht wirklich verwendest, kannst im Beispiel 3 diese auch gleich ganz weg lassen.
Da statt Zeigern gern Referenzen verwendet werden, aber ein Array von Referenzen in C++ nicht geht, könnte man den umgekehrten Weg erwähnen
// Für jeden Schritt eine Instanz
HEATPARAMETER heat[] { // create an array of structures
{29, 30, 1, 30000, 0, 0, 0},
{60, 70, 1, 30000, 0, 0, 0},
{119, 120, 1, 30000, 0, 0, 0}
};
HEATPARAMETER& heatLow = heat[0];
HEATPARAMETER& heatMiddle = heat[1];
HEATPARAMETER& heatHigh = heat [2];
...
heat[1].lowTemp = 42;
Serial.println(heatMiddle.lowTemp); // liefert 42
Doch ist es. Denn wie Du selbst bemerkst, werden die benamsten Strukturen nur für das Array verwendet.
Ja, natürlich geht das auch mit Pointer und referenzen - Bis gestern war ja nicht mal klar, ob das überhaupt zum Einsatz kommt.
Und irgendwie gefunden hat sich ja hier weiter niemand.
Ich wäre schon froh, wenn die Funktionen erstmal täten.
Und wenn sie nicht tun, den Punkt zu erfahren, wo es hängt.
Ich nehm das mit - vielleicht bau ichs noch um.
[OT]
Diese Forensoftware ist ein wunderbarer Keylogger - ich sitze hier grad bei 1Mbit und kann jedem Tastenanschlag zusehen, wie er tröpfchenweise erst rausgeht, und dann auf dem Display erscheint und dann in der Vorschau...
das hätte die Lesbarkeit nicht verschlechtert:
HEATPARAMETER heat[] { // create an array of structures
{29, 30, 1, 30000, 0, 0, 0},
{60, 70, 1, 30000, 0, 0, 0},
{119, 120, 1, 30000, 0, 0, 0}
};