Unlösbare Auflagen Meisterprojekt

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.... :sleeping:

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.

:thinking:
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.... :weary:

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 :clap:

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... :slight_smile:

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. :grin:

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. :wink: Der sah das sportlich.

@my_xy_projekt

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.

2 Likes

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
2 Likes

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