Programm bleibt unerwartet stehen (Stepper Betrieb)

Messung() kann an sich mehrere Stunden dauern

Messung() muss natürlich spätestens nach einigen wenigen Millisekunden zurückkommen.
Normalerweise stellen solche Funktionen nur fest, dass noch nichts neues zu tun ist, und fertig.
Wenn doch, dauert das Einleiten des nächsten Schritts auch keine Zeit, und wieder fertig...

Ein "Programm" und einmal durch loop() hat nichts miteinander zu tun.

Timer sind natürlich auch schön, speziell wenn du keine Zeit hast, dass eine der Funktionen doch mal 1-5 ms dauert.
Alles was nicht ganz so zeitkritisch ist, geht auch in einer vernünftigen loop().

Das auch. Wieso willst du überhaupt mehrere Stunden in messung() hängen? Du kannst doch wenn darin einmal was gemacht wurde kurz nach loop zurückkehren, dort was anderes machen und in der nächsten Iteration von loop wieder messung() machen.

Wie gesagt kannst du den Zustand von Variablen von einem Methoden-Aufruf zum nächsten behalten wenn du sie als "static" deklarierst.

Und wo es bei dir hakt, ist die ganze Ablauf-Steuerung des Programms. Das ist alles auf einem sehr primitiven Niveau, a la BASIC mit einem "GOTO START" am Ende. Jetzt willst du was komplizierteres machen und weißt nicht wie. :slight_smile:

Ein Zustands-Automat wäre hier vielleicht eine Option:

typedef enum { STATE_START, STATE_MESSUNG } state;
state currentState = STATE_START;

Das muss global außerhalb von Setup deklariert werden.

Dann kann man in der Loop sowas machen:

void loop()
{
   if(currentState == STATE_START)
   {
       Joystick();
       Ausgangsposition();
       StartpunktAnfahren();
       MessrasterAngabe();
       currentState = ....
   }
   else if(currentState == STATE_MESSUNG)
   {
         Messung();
   }
}

Du kannst dann überall currentState nach Bedarf verändern. Wenn du dann wiedermal deine Start-Methoden ausführen musst machst du einfach "currentState = STATE_START" und in der nächsten Loop werden sie ausgeführt. Danach schaltest du wieder in einen anderen Zustand. Die Enumeration kannst du einfach je nach Bedarf mit weiteren Zuständen erweitern.

@Serenifly: Dein letztes Beispiel ist evtl. eher verwirrend, denn es heisst dass während Messung läuft, ist Joystick() nicht in Funktion.
Und Startpunktanfahren() ist nach meinem Verständnis auch nicht in einem loop() Durchlauf fertig.
( und wo ist NotAus, das die ganze Diskussion erst losgetreten hat ?! )

"State Maschine" ist ein hochtrabendes Wort, aber klar muss sich eine Funktion wie Messung(), die millionenmal aufgerufen wird, merken was gerade Sache ist, bis sie wieder in einem Zustand ist, wo sie nur noch nach einem neuen Startbefehl fragt.

Evtl kann die oberste Ebene Zustandsführung in loop realisiert werden :

struct { ... } JoystickState;
enum { STATE_IDLE, STATE_START, STATE_MESSUNG } currentState;
void setup() 
{
   currentstate = STATE_IDLE;
   Serial.begin(115200);
}

void loop()
{
   if (NotAusAbfrage() ) { NotAusFunktion(); return; }
   Joystick();  // Abfragen und Zustand global bereitstellen

   if(currentState == STATE_IDLE)
   {
        if ( Startbefehl() ) 
           currentState = STATE_START;
        else Ausgangsposition();
   }
   if (currentState == STATE_START)
   {
      if ( StartpunktAnfahren() )
        currentState = STATE_MESSUNG;
   }
   if (currentState == STATE_MESSUNG)
   {
      int result = Messung();
      switch (result) 
      {
       case 0: break; // läuft noch
       case 1:
           Serial.println("Messung erfolgreich beendet");
           currentState == STATE_IDLE;
           break;  
   }
}

Messung hat genauso wie loop eine eigene Zustandsführung, die aber nach aussen uninteressant ist ( Mal angenommen ).
Kann auch in einer eigenen Datei realisiwert werden

int Messung()
{
     // liefert 0 solange aktiv, oder eine Ergebniskennung 

     const unsigned long Minimalzeit = 3600000;  // frühestens nach 1 Stunde fertig

     static enum {M_SETUP, M_INIT, M_WAIT,M_PHASE1,M_PHASE2} state = M_SETUP;

     static unsigned long starttime;
     static boolean done;
  
     switch (state) 
     {  
      case M_SETUP: 
          starttime = millis();
          // more quick setup functions
          state = M_INIT;
          break;
       ...
     case M_PHASE2: 
         if ( done && millis()-starttime > MinimalZeit)
         state = M_SETUP;    // für die nächste Messung vorbereiten
         return 1;                 // Messung fertig
     } 
     return 0;
}

Ok, hast recht :frowning:
Das war aber auch nicht wirklich als lauffähiges Beispiel gedacht, sondern nur um das Prinzip aufzuzeigen :slight_smile:

Ich weiß Leute wollen hier immer lauffähigen Code haben, auch wenn es komplex ist, aber persönlich fällt mir es da schwer das fehlerfrei zu schreiben, da ich es selten testen kann und/oder die Hardware nicht habe. Deshalb bevorzuge ich es Hinweise zu geben wie man das implementieren kann und man kann es dann selbst anpassen.

Ok, ich glaube wir sind an einem Punkt angekommen, wo mein Code nicht mehr für sich selbst spricht, sondern ich euch vielleicht kurz erklären sollte, was mein Programm machen soll:

  • Arduino einschalten
  • Abfrage ob manueller Betrieb (Joystick) oder automatische Messung [noch nicht implementiert, der Joystickbetrieb ist zum Test am Anfang aktiv und kann durch Druck auf einen Taster beendet werden]
    (den manuellen Betrieb lass ich erstmal weg, das ist eher nebensächlich)
  • Anfahren einer fixen Ausgangsposition
  • Anfahren des vorher bestimmten Startpunktes (im Moment durch drücken eines Schalters simuliert)
  • Starten der Mess-Prozedur durch Knopfdruck (im Moment durch Eingabe von "1" simuliert)
  • Eingabe des Messfeldes (inzwischen über Joystick, statt "3!" etc.)
  • automatische Messung starten, diese kann bis zu über 1000 Messfelder enthalten, deren Messung jeweils bis zu 30 Sekunden dauern kann
  • Ausgabe, dass Messung beendet wurde.
  • Rückkehr zum "Menüpunkt" mit der Auswahl zwischen manuellem und automatischem Betrieb.

Das mit dem Zustandsautomaten find ich garnicht so schlecht und eigentlich einleuchtend. Im Moment habe ich in Messung() mehrere for Schleifen, deren Index ich ja behalten müsste. Das würde durch static funktionieren? Müsste ich das dann mit einer If-Abfrage regeln? Also die Indizes hochzählen und wenn sie den maximalen Wert erreicht haben, einfach die Messung verlassen?

Im Moment sieht Messung halt so aus:

void Messung()
{
  //NotausAbfrage();
  LCDText="Messung startet";
  LCDWrite(LCDText);
  for (int p=1; p <= MessfeldX; p++)
  {
    //NotausAbfrage();
    myStepperX.step(Schrittweite);
    delay(1000);
    for (int i=1; i<=MessfeldY; i++)
    {
      //NotausAbfrage();
      myStepperY.step(Schrittweite);
      delay(1000);
      Messausgabe="Messpunkt ";
      Messpkt=(p-1)*MessfeldY +i;
      LCDText=Messausgabe + Messpkt;
      LCDZwZeile="Messung laeuft";
      LCDWriteZw(LCDText, LCDZwZeile);
      delay(5000);
      Messwert=random(10);
      delay(2);
      LCDText="Mpkt erfasst";
      Messausgabe="Messwert: ";
      LCDZwZeile=Messausgabe + Messwert;
      LCDWriteZw(LCDText, LCDZwZeile);
      delay(2000);
    }
    myStepperY.step(-Schrittweite*MessfeldY);
  }
  LCDText="Messung beendet";
  LCDWrite(LCDText);
  delay(10000);  
}

Das mit dem Interrupt hab ich übrigends probiert, da hat sich das Board allerdings immer aufgehängt.

Das sah so aus:

void NotausAbfrage()
{
    LCDNotaus="Notaus AKTIV";
    LCDWrite(LCDNotaus);
    SicherheitAbwarten = 1;
    while (SicherheitAbwarten == 1) //While Schleife wird erst verlassen, wenn der Notaus deaktiviert und eine 1 eingegeben wurde
    {
      if (Serial.available() <=0 )
      {
      }
      if (Serial.available() > 0 ) // auf Daten am serielle-Port warten
      {
        delay(10); // kann man, muss nicht
        Eingabe = Serial.read(); // Daten Byte in die Variable comand schreiben
        if (Eingabe == 49)
        {
          SicherheitAbwarten = 0;
          lcd.clear();
          lcd.print("Notaus");
          lcd.setCursor(0,1), //LCD Cursor in die 2. Zeile packen
          lcd.print("deaktiviert");
          delay(2000);
          LCDWriteZw(LCDText, LCDZwZeile); //Schreibt das vorherige Vorgehen wieder in das Display (Bsp: "Messung startet" -> "Notaus aktiv" ->"Notaus deaktiviert" ->"Messung startet")
        }
      }
    }
  }

Liegt das an der while-Schleife ?

Dass mein Herangehen eher primitiver Natur ist und das Projekt doch größeres Ausmaß hat, liegt schlicht daran, dass ich nur son bischen hobbymäßig programmiert habe und das Querfeldein durch mehrere Programmiersprachen und jetzt für mein Praktikum eben etwas größeres entwickeln muss, in einer ungewohnten Umgebung :wink:

Edit: Mit Beispielen bin ich schon zufrieden. Rest probier ich selbst und poste dann hier wenn was nicht klappt. Dann kann immer noch gesagt werden, worans liegt

Arghmano:
Im Moment habe ich in Messung() mehrere for Schleifen, deren Index ich ja behalten müsste. Das würde durch static funktionieren?

Ja. Die andere Option ist die Variable einfach global zu machen. Wenn man sich strikt an Programmier-Paradigmen hält, heißt es halt man soll Variablen wenn möglich lokal machen. Dafür gibt es dann static (die bekanntere Funktion von static ist, dass mehrere Objekte einer Klasse auf die gleiche Variable zugreifen).

Müsste ich das dann mit einer If-Abfrage regeln? Also die Indizes hochzählen und wenn sie den maximalen Wert erreicht haben, einfach die Messung verlassen?

Es ging ja um Notaus. Deshalb die Idee, eben NICHT in Messung hängen zu bleiben. Sondern z.B. nach einer Messung einmal nach Loop zurückzukehren, dort Notaus abzufragen und dann die nächste Messung zu machen.

messung() hätte dann z.B. einen int als Rückgabe-Wert. Wenn du noch nicht fertig bist gibst du 0 oder -1 zurück. Dann weiß loop(), dass messung() nochmal aufgerufen werden muss. Wenn etwas >0 kommt, ist das das Ergebnis. Oder wenn dein Ergebnis komplexer ist und z.B. in arrays liegt kannst du da auch einen bool mit true/false als Status nehmen.

So kannst du halt auch während die Messung läuft auch noch auf andere Ereignisse reagieren. z.B. über Serial die Messung abbrechen oder ähnliches. Allerdings nicht sofort!

Du sagst aber eine Messung dauert bis zu 30s. Das ist dann schon wieder sehr lange um den Notaus rechtzeitig abzufragen. Da bietet sich dann wirklich ein Timer-Interrupt an. Der kann die Messung z.B. alle Sekunde kurz unterbrechen. Oder noch besser, man legt den Notaus gleich auf einen der externen Interrupts. Dann muss man gar nicht per Hand nachfragen. Siehe hier:

Du hast mit Pins 2 oder 3 zwei externe Interrupt-Quellen (auf dem MEGA auch mehr). Wenn du auf Pin 2 den Taster anschließt und das hier machst:

setup()
{
     attachInterrupt(0, notaus, RISING);   //evtl. auf FALLING stellen, je nachdem wie herum der Taster geht. Für Pin 3, 1 statt 0 schreiben
}

void notaus()
{
  ...
}

Sobald der Taster gedrückt wird springt das Programm dann nach notaus(), egal was gerade gemacht wird. Das geht wie gesagt mit einem richtigen Taster. Im Moment scheinst du das ja durch Serial zu simulieren.

Das mit dem Interrupt hab ich übrigends probiert, da hat sich das Board allerdings immer aufgehängt.

Das sah so aus:

Das ist kein Interrupt, sondern manuelles Polling. Und die while-Schleife da blockiert natürlich bis was passiert ist. Das sollte man nur machen wenn man weiß, dass das Ereignis auf jeden Fall ein Kürze eintritt.

In deinem Fall sinnlos, da du schon die Abfrage auf serial.available() hast.

Aber:
if (Serial.available() > 0 ) // auf Daten am serielle-Port warten

Das wartet nicht! In Verbindung mit der while-Schleife natürlich, ja, aber dann steckst du da eben fest bis Daten da sind. Lass das while weg und beende die Methode wenn nichts geschickt wurde.

Jo, den Interrupt hatte ich schon genau so initialisiert, allerdings mit LOW.

Eigentlich möchte ich aber da drin bleiben. Also wenn der Notaus aktiviert wird, soll das ganze erst weiterfahren, wenn die Notausdeaktivierung durch eine Eingabe bestätigt wurde. Wenn ich direkt aus der Prozedur rausgehe, ist das doch nicht der Fall oder? Die Idee ist einfach, dass wenn sich jemand die Hand einklemmt, das Ding nicht wieder anfährt, bevor man bestätigt hat, dass der Mensch die Hand da wieder raus hat. Nur den Notausschalter als Bestätigung halte ich nicht für ausreichend.

Ok, jetzt verstehe ich es :slight_smile:
Wenn du da die Ausführung blockieren willst bis der Benutzer was macht, ist das natürlich korrekt :slight_smile:

Was meinst du dann mit "aufgehängt"? Reagiert er nicht auf dem Empfang von Daten?

delay() funktioniert allerdings in Interrupts nicht korrekt, da millis() nicht korrekt inkrementiert (weil der millis-Zähler selbst in einem Timer-Interrupt läuft). Vielleicht liegt es daran. Mach das mal raus.

Äh ja, delay hatte ich auch raus. Sorry, hatte die Prozedur gerade aus dem jetzigen Programm kopiert und alles entfernt was nicht reingehört (Serial.println, If Abfragen). Hab das Delay vergessen.

Irgendwie ist er da reingesprungen und hat dann aber weder etwas aufs LCD geschrieben, noch hat er auf irgendwas reagiert. Also son typischer PC-hänger, wo eigentlich nur der Hard-Reset hilft.

Ich sitz jetzt natürlich leider wieder zuHause und hab die Hardware nicht bei mir. Ich kanns morgen nochmal ausprobieren, geht ja fix und den aktuellen Code posten und nochmal genau drauf achten, was er tut. Das typische Serial.println-debuggend, um zu schauen, bis wohin er kommt,geht hier ja leider nicht.Hast du ne Idee für ne Alternative?

Also mein Interrupt Code sieht jetzt so aus:

const int StopPin=1;
volatile int counter=0;
volatile int SicherheitAbwarten=0;

#include <Wire.h> 
#include <LiquidCrystal_I2C.h>

int Eingabe=0;
String LCDNotaus="";
//LCD Display festlegen
LiquidCrystal_I2C lcd(0x20,16,2);  // LCD Adresse 0x20, 16 Zeichen, 2 Zeilen

void setup()
{
  Serial.begin(9600);
  attachInterrupt(3,Stopfunktion,RISING);
  lcd.init();                      // LCD Initialisieren
  lcd.backlight();
}

void loop ()
{
 Serial.println(counter);
 counter++;
 lcd.clear();
 lcd.print("OK");
 delay(1000);
}

void Stopfunktion()
{
    LCDNotaus="Notaus AKTIV";
    LCDWrite(LCDNotaus);
    SicherheitAbwarten = 1;
    while (SicherheitAbwarten == 1) //While Schleife wird erst verlassen, wenn der Notaus deaktiviert und eine 1 eingegeben wurde
    {
      if (Serial.available() <=0 )
      {
        
      }
      if (Serial.available() > 0 ) // auf Daten am serielle-Port warten
      {
        Eingabe = Serial.read(); // Daten Byte in die Variable comand schreiben
        if (Eingabe == 49)
        {
          SicherheitAbwarten = 0;
          lcd.clear();
          lcd.print("Notaus");
          lcd.setCursor(0,1), //LCD Cursor in die 2. Zeile packen
          lcd.print("deaktiviert");
        }
      }
    }
}

void LCDWrite(String Eingabestring)
{
  lcd.clear();
  lcd.print(Eingabestring);
}

void LCDWriteZw(String Eingabestring, String EingabeZweiteZeile)
{
  lcd.clear();
  lcd.print(Eingabestring);
  lcd.setCursor(0,1);
  lcd.print(EingabeZweiteZeile); 
}

Das bleibt aber sofort hängen, also es passiert rein garnichts. Weder auf dem LCD noch im Serial.monitor

Serial und gar while Schleifen mit Serial haben in Interruptfunktionen nichts zu suchen !

also auch Serial.available nicht? Okay. Kann ich denn eine while Schleife einbauen, die erst durch Drücken eines Tasters beendet wird?

Solange du in der ISR bleibst, wird sich Serial.available() nicht ändern. :wink:
Wenn du auf zwei Sachen gleichzeitig wartest und nicht weisst, was zuerst kommt, brauchst du 2 ISR und musst in beiden auf beide Bedingungen abfragen, und falls nicht, sofort beenden.

"Wenn..." wohlgemerkt. Ein anderer Ansatz sagt, in so einem Fall liegt wahrscheinlich ein Design-Fehler vor.

  • Eine ISR kann nicht auf etwas warten, und braucht also keine while Schleife.
  • Mit Steppern und Tastern ( oder Joysticks ) braucht man keine ISR ( :wink: )

While Schleife wird erst verlassen, wenn der Notaus deaktiviert und eine 1 eingegeben wurde

Das mag ganz eventuell mal Absicht sein, so wie mein

void loop() {
if ( Notaus() ) return; // bei Notaus geht sonst nix !

// alles andere nur im Normalbetrieb
// ...
}

... aber doch nicht in einer Interrupt-Routine.


Nachtrag:
Es gibt eine while Scheife, und die hat Arduino in main():

void main() {
 setup(); 
 while (1) loop();  // leicht vereinfacht
}

nutze sie sinnvoll. Andere Warteschleifen machen dir alles nur komplizierter :wink:

Irgendwie hast du jetzt aufgezählt, was alles nicht geht und das was geht macht für mein Ziel keinen Sinn, oder sehe ich das falsch? Nochmal:
Ich möchte Notaus immer auslösen können, also zu jedem Zeitpunkt und das Programm soll nicht automatisch weiterfahren, sobald der handelsübliche Notaus-Schalter wieder in seine Ursprungsstellung gestellt wurde, sondern erst dann, wenn mit Hilfe einer Eingabe oder durch das Drücken eines Kopfes oder ähnliches, dem Programm gesagt wurde, dass die eingeklemmte Person befreit wurde.

Das war mein Sample code für loop:

   if (NotausAbfrage() ) { NotAusFunktion(); return; }

So könnte NotausAbfrage aussehen:

const byte NotAusPin = 2;
const boolean NotAusActive = LOW;  // sicherer PullDown Widerstand, Betrieb nur wenn HIGH an diesem Pin

boolean NotAus = false;

boolean NotausAbfrage ()
{
  if (digitalRead(NotAusPin) == NotausActive) NotAus = true;
  return NotAus;
}

NotAusFunktion() kann dann alles in einen sicheren Zustand fahren (eigentlich sollte ausschalten dazu ausreichen)
und "warten", bis die Wiederanfahrfreigabe kommt.
Dann kann NotAus = false gesetzt werden.
(Das schreibst du bitte selber.)

Und wehe dir, du verstehst "warten" als delay oder while - Schleife :wink: :slight_smile:
Das mag zwar hier und jetzt gehen, aber eventuell möchtest du irgendwann andere Funktionen auch im NotAus - Zustand laufen haben.
Die sollten dann nicht hier, sondern in loop() eingebaut werden, wenn sie eigentlich nichts mit dem NotAus an sich zu tun haben.

P.S. Dass ich NotausAbfrage und NotausFunktion getrennt habe, ist übrigens nicht zwingend. NotausFunktion könnte auch Teil der NotausAbfrage() sein.

Also auch auf die Gefahr hin, dass ich mich hier ganz zum Affen machen, aber entweder versteh ich überhaupt nicht, wie deine Lösung funktionieren soll, oder du verstehst nicht, was ich von dem Programm möchte.

Angenommen, ich packe deine IF-Abfrage in die loop() und erzeuge die Prozedur unten, dann ruf ich diese IF Abfrage doch nur 1x pro loop auf. Wenn mein loop jetzt aber ne halbe Stunde dauert, hilft mir das doch überhaupt nicht weiter?

Ich hätte gerne sofort(!) eine Rückmeldung nicht erst dann, wenn Messung() oder sonst irgendwas durchgelaufen ist.

Wenn ich folgendes mache:

If (NotAus == false)
{
//irgendwas
//an DIESER STELLE wird der Interrupt NOTAUS() durch einen Knopfdruck ausgelöst
//irgendwas anderes
}

und

void NOTAUS()
{
NotAus = true;
}

springt er dann trotzdem in die IF-Abfrage zurück, obwohl diese nicht mehr zutrifft?

Ich muss doch irgendwie einen Interrupt auslösen können, der verhindert, dass mein Programm einfach direkt weiter läuft und auf eine Bestätigung durch einen Taster wartet.

Das Problem ist halt dass in Interrupt-Routinen anscheinend sehr vieles nicht mehr läuft. Es stimmt zwar dass man generell ISRs so kurz wie möglich halten sollte, aber wenn die Arduino Firmware vernünftig programmiert wäre, sollte das theoretisch auch möglich sein darin mehr zu machen. Interrupts können sich nämlich auch gegenseitig unterbrechen, also hätte man es vielleicht auch so machen können, dass da Serial u.ä. noch funktioniert. Ist aber wohl nicht so, also geht es nicht.

Kannst du die Abfrage ob die Variable gesetzt wurde auch in deiner Mess-Methode machen? z.B. am Ende jeder Iteration der innersten for-Schleife.

void Messung()
{
 
  for (int p=1; p <= MessfeldX; p++)
  {
    for (int i=1; i<=MessfeldY; i++)
    {
          if(NotAus  == true)
            return;
    }
   }
}

Dann springt er direkt nach loop (genau genommen dahin wo du die Methode aufgerufen hast) und dort kannst du nochmal drauf abfragen.

void loop()
{
     ....

     messung();
     
     if(notAus == true)    // <--- return springt in diese Zeile
     {
        ....
     }
}

Da war wir vorher mal State Machines erwähnt hatten, kannst du da auch einen Zustand NOTAUS einführen.

if(currentState == NOTAUS)
{
}

Wenn du dann in der ISR den Zustand auf Notaus setzt und von messung() nach loop() springst (und im aktuellen Zustand nach messung() nichts mehr kommt) führt er immer diesen Block aus

Genau so hab ich es momentan (und auch schon seit Anfang an) geregelt, also, dass ich bei jeder Prozedur und auch in jeder Iteration abfrage, ob Notaus aktiv ist oder nicht. Habe das ganz simpel durch

NotausAbfrage();

und

void NotausAbfrage()
{
  NotAusSchalter = digitalRead(Sicherheitstest);
  if (NotAusSchalter == HIGH)
  {
    LCDNotaus="Notaus AKTIV";
    LCDWrite(LCDNotaus);
    SicherheitAbwarten = 1;
    while (SicherheitAbwarten == 1)
    {
      if (Serial.available() <=0 )
      {
        Serial.println("Notaus AKTIV");
        Serial.println("Bitte mit '1' bestaetigen, sobald Notaus deaktiviert wurde");
        delay(1000);
      }
      if (Serial.available() > 0 ) // auf Daten am serielle-Port warten
      {
        delay(10); // kann man, muss nicht
        Eingabe = Serial.read(); // Daten Byte in die Variable comand schreiben
        if (Eingabe == 49)
        {
          Serial.println("Notaus Deaktivierung bestaetigt");
          SicherheitAbwarten = 0;
          lcd.clear();
          lcd.print("Notaus");
          lcd.setCursor(0,1),
          lcd.print("deaktiviert");
          delay(2000);
          LCDWrite(LCDText);
        }
      }
    }
    delay(1000);
  }
  else
  {
    //Serial.println("Notaus nicht aktiv");
    SicherheitAbwarten = 0;
    //delay(1000);
  }
  
}

gelöst. (Dieser Code ist aus dem 1. Post, inzwischen ist er "verschönert", aber vom Prinzip her immer noch das gleiche.)

Momentan habe ich noch verschiedene Prozeduren die in Loop nach Messung() aufgerufen werden, das müsste ich dann umschreiben um return; nutzen zu können, aber nen wirklichen Vorteil gegenüber meiner Methode bietet das nicht oder? An sich lässt sich auch damit leben, es ohne Interrupt zu lösen , da die Delays inzwischen mit Hilfe von Millis() und ein paar weiteren Änderungen größten Teils verschwunden sind (und die Motoren auch Hardwareseitig abgeschaltet werden). Einzig Messung() ist halt das Problem, da die keine bestimmte Dauer hat (ist abhängig von externen Einflüssen). Ich fands halt nur nicht schön, das immer in jeder Iteration, If Abfrage, Whileschleife etc. abfragen zu müssen.

Ja, wir reden aneinander vorbei.

Was du unter einem 'Programm', das eine halbe Stunde dauert, verstehst, hat NICHTS mit der Anzahl loop-Durchläufe zu tun.

Liebe Mathematiker, bitte ganz ruhig bleiben: loop braucht 0 sekunden und läuft unendlich oft in 30 Minuten.

Na gut, realistisch dauert loop eine halbe millisekunde, eventuell auch mal 10 millisekunden, aber keine halbe Stunde.
Und hat also kein delay. Das Prinzip von BlinkWithoutDelay gilt nicht nur für das Blinken, sondern immer.

Und zur Übung sogar wenn die Kiste im Notaus hängt und eigentlich gar nicht mehr rauszukommen bräuchte,
da zur Zeit während Notaus sonst nichts anderes zu tun ist.

D.h. void NotausAbfrage() enthält auch kein delay, sondern merkt sich nur, wann das letzte Mal

[b]  Notaus AKTIV ...[/b]

ausgegeben wurde, um das nicht jedesmal, sondern nur ganz ganz selten, wenn nach "unendlich" vielen Umläufen 1000 millis vergangen sind, oder mehr, die Ausgabe zu wiederholen.
Du glaubst gar nicht, wie schnell Notausabfrage dann auf deine '1' - Freigabe reagiert.

Und wie flexibel du in deinem Programm bist,
und dass du auf die meisten ISR verzichten kannst...