Programm bleibt unerwartet stehen (Stepper Betrieb)

Edit: !!!Gelöst!!! War ein RAM Problem . Mit dem Workaround durch F-Macro und Malloc.c Datei wurde das Problem behoben!

Hallo zusammen,

ich bin dabei eine Schrittmotorensteuerung zu entwerfen bei der außerdem Ausgaben über ein LCD Display erfolgen (das ganze läuft über ein Leonardo) und habe folgendes kurioses Problem:

Die einzelnen Prozeduren funktionieren einwandfrei, auch die Messung() funktioniert als externe Prozedur (also in einer eigenen Datei) ohne Probleme, füge ich aber den Code aus dieser Datei in die Hauptdatei ein, so bleibt das Programm immer an einem Punkt stehen (unten durch rot markiert). Ich habe keine Ahnung woran das liegt und noch seltsamer macht es der Fakt, dass das Programm alle x Versuche mal funktioniert und dann wieder ewig nicht. Mal geh ich in die Pause, komme zurück, stecke die Stecker ein und aus, dann funktioniert es, mal funktioniert es nach einem neuem Upload. Allerdings ist die Erfolgsquote auch nur bei ca. 1%.

Es würde mich freuen, wenn ihr mir weiterhelfen könntet.

Messung() als eigenes Programm:

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

LiquidCrystal_I2C lcd(0x20,16,2);  // set the LCD address to 0x20 for a 16 chars and 2 line display

const int stepsPerRevolution = 400;

// initialize the stepper library on pins 8 through 11:
Stepper myStepperX(stepsPerRevolution, 8,9,10,11);
Stepper myStepperY(stepsPerRevolution, 4,5,6,7);

//Messfeldgrößen
int MessfeldX=4;
int MessfeldY=5;
String Messfeldausgabe="";

//LCD Variablen
String LCDText="";
String LCDNotaus="";
String LCDZwZeile="";

//Messung
String Messausgabe="";
int Messwert=0;
int Messreihe=1;
int Schrittweite=800;
int Messpkt=0;

void setup()
{
  myStepperX.setSpeed(60);
  myStepperY.setSpeed(60);
  Serial.begin(9600);
  lcd.init();                      // initialize the lcd 
  lcd.backlight();
}

void loop()
{
  Messung();
}

void Messung()
{
  LCDText="Messung startet";
  LCDWrite(LCDText);
  for (int p=1; p <= MessfeldX; p++)
  {
    Serial.println("Messreihe");
      myStepperX.step(800);
      //Serial.println(m);
    delay(1000);
    Serial.println("nach m");
    for (int i=1; i<=MessfeldY; i++)
    {
      Serial.println("i");
        myStepperY.step(800);
        //Serial.println(n);
      Serial.println("nach n");
      delay(1000);
      Serial.println("nach delay");
      Messausgabe="Messpunkt ";
      Serial.println(Messausgabe);
      Messpkt=(p-1)*MessfeldY +i;
      LCDText=Messausgabe + Messpkt;
      Serial.println(LCDText);
      LCDZwZeile="Messung laeuft";
      Serial.println(LCDZwZeile);
      Serial.println("nach Stringerstellung");
      LCDWriteZw(LCDText, LCDZwZeile);
      delay(5000);
      Messwert=random(10);
      delay(2);
      Serial.println("nach Random");
      LCDText="Mpkt erfasst";
      Messausgabe="Messwert: ";
      LCDZwZeile=Messausgabe + Messwert;
      LCDWriteZw(LCDText, LCDZwZeile);
      delay(1000);
    }
    delay(100);
  }
  LCDText="Messung beendet";
  LCDWrite(LCDText);
  delay(10000);  
}

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 komplette Programm

// Verschmelzung von Notaus_Procedure und Anfangsposition_mit_Wait_Monitor_Abfrage

//Stepper-Motoren-Lib+Variablen
#include <Stepper.h>
#include <Wire.h> 
#include <LiquidCrystal_I2C.h>

LiquidCrystal_I2C lcd(0x20,16,2);  // set the LCD address to 0x20 for a 16 chars and 2 line display

const int stepsPerRevolution = 400;

// initialize the stepper library on pins 8 through 11:
Stepper myStepperX(stepsPerRevolution, 8,9,10,11);
Stepper myStepperY(stepsPerRevolution, 4,5,6,7);

int stepCount = 0;         // number of steps the motor has taken

//Pinbelegung
const int StopTester = A1;
const int WaitSchalter = 13;
const int Sicherheitstest = A3;

//Spannungsfühler
int AnschlagSchalter = 0;
int NotAusSchalter = 0;
int SicherheitAbwarten = 0;

//Schleifen-Unterbrecher
int Startposi = 0;
int Anfangsposi = 0;
int Eingabe = 0;

//Eingabe-Auslese Variablen
String Inhalt = "";
char EingabeZeichen;
int EingabeZahl=0;
int RueckgabeZahl=0;
boolean EingabeTrue=false;

//Messfeldgrößen
int MessfeldX=0;
int MessfeldY=0;
String Messfeldausgabe="";

//LCD Variablen
String LCDText="";
String LCDNotaus="";
String LCDZwZeile="";

//Messung
String Messausgabe="";
int Messwert=0;
int Messreihe=1;
int Schrittweite=800;
int Messpkt=0;

void setup()
{
  Serial.begin(9600);
  myStepperX.setSpeed(60);
  myStepperY.setSpeed(60);
  pinMode(Sicherheitstest, INPUT);
  pinMode(StopTester, INPUT);
  lcd.init();                      // initialize the lcd 
  lcd.backlight();
}

void loop()
{
  NotausAbfrage();
  Ausgangsposition();
  StartpunktAnfahren();
  MessrasterAngabe();
  Messung();
}

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

Weiterführung

void Ausgangsposition()
{
   LCDText="Fahre Anfang an";
   LCDWrite(LCDText);
   stepCount=0;
   while (AnschlagSchalter == LOW) 
  {
      NotausAbfrage();   
      myStepperX.step(-1);
      myStepperY.step(1);
      //myStepperY.setSpeed(20);
      //Serial.print("steps rueckwaerts:" );
      //Serial.println(stepCount);
      stepCount++;
      //delay(100);
      AnschlagSchalter = digitalRead(StopTester);
      if (stepCount == 400)
        {
          Serial.println("Runde zuende");
        }
    }
  if ( AnschlagSchalter == HIGH )  
    {
      NotausAbfrage();
      LCDText="Anfang erreicht";
      LCDWrite(LCDText);
      Anfangsposi=1;
      while (Anfangsposi ==1)
      {
        if (Serial.available() <=0 )
        {
          NotausAbfrage();
          Serial.println("Motor steht, mit der Eingabe von '1' weiter fahren lassen.");
          delay(1000);
        }
        if (Serial.available() > 0) // auf Daten am serielle-Port warten
        {
          NotausAbfrage();
          delay(10); // kann man, muss nicht
          Eingabe = Serial.read(); // Daten Byte in die Variable comand schreiben
          AnschlagSchalter = digitalRead(StopTester);
          if (Eingabe == 49) // comand auswerten, 49 = 1 
            {
              Serial.println("Motor startet");
              LCDText="Motor startet";
              LCDWrite(LCDText);
              Anfangsposi=0;
              delay(50);       
            }
        }
      }
    }
}

void StartpunktAnfahren()
{
  LCDText="Fahre Start an";
  LCDWrite(LCDText);
  stepCount=0;
  while (AnschlagSchalter == HIGH) 
  {
      NotausAbfrage();
      myStepperX.step(1);
      myStepperY.step(-1);
//      Serial.print("steps vorwaerts:" );
//      Serial.println(stepCount);
      stepCount++;
      AnschlagSchalter = digitalRead(StopTester);
      if (stepCount == 400)
        {
          Serial.println("Runde zuende");
        }
    }
  if ( AnschlagSchalter == LOW )  
    {
      NotausAbfrage();
      LCDText="Messung kann ge";
      LCDZwZeile="startet werden";
      LCDWriteZw(LCDText, LCDZwZeile);
      Startposi=1;
      while (Startposi ==1)
      {
        NotausAbfrage();
        if (Serial.available() <=0 )
        {
          Serial.println("Messung kann mit der Eingabe von '1' gestartet werden");
          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
          AnschlagSchalter = digitalRead(StopTester);
          if (Eingabe == 49) // comand auswerten, 49 = 1 
            {
              Serial.println("Motor und Messung starten");
              LCDText="Motor und Mes";
              LCDZwZeile="sung starten";
              LCDWriteZw(LCDText, LCDZwZeile);
              Startposi=0;
              delay(50);       
            }
        }
      }
    }
}
  
  
void establishContact() {
  while (Serial.available() <= 0) {
    Serial.println("Eingabe '1' für Start");   // send an initial string
    delay(300);
  }
}

int StringAuslesen()
{
  EingabeTrue=false;
  NotausAbfrage();
  while(Serial.available() <= 0)
  {
    NotausAbfrage();
    Serial.println("Eingabe erforderlich");
    delay(5000);
  }
  while(Serial.available()>0) 
  {
    NotausAbfrage();
        EingabeZeichen = Serial.read();
        if ((EingabeZeichen >=48) && (EingabeZeichen <=57))
        {
          EingabeZahl = (EingabeZahl*10)+(EingabeZeichen-48);
        }
        else
        {
        Inhalt.concat(EingabeZeichen);
        }
        if (Inhalt == "!")
        {
         RueckgabeZahl=EingabeZahl;
         EingabeTrue=true;
         EingabeZahl=0;
         Inhalt="";
        } 
  }
  if (EingabeTrue==1)
  {
  return RueckgabeZahl;
  }
}

void MessrasterAngabe()
{
  LCDText="Messfeldeingabe";
  LCDWrite(LCDText);
  Serial.println("Laenge X angeben");
  MessfeldX=StringAuslesen();
  Serial.println("Laenge X lautet: ");
  Serial.println(MessfeldX);
  Serial.println("Breite Y angeben");
  MessfeldY=StringAuslesen();
  Serial.println("Breite Y lautet: ");
  Serial.println(MessfeldY);
  String Fueller=" x ";
  Messfeldausgabe=MessfeldX + Fueller + MessfeldY;
  Serial.println("Die Messfeldgroesse betraegt:");
  Serial.println(Messfeldausgabe);
  LCDWrite(Messfeldausgabe);
  delay(5000);
}

void Messung()
{
  LCDText="Messung startet";
  LCDWrite(LCDText);
  for (int p=1; p <= MessfeldX; p++)
  {
    Serial.println("Messreihe");
      myStepperX.step(800);
      //Serial.println(m);
    delay(1000);
    Serial.println("nach m");
    for (int i=1; i<=MessfeldY; i++)
    {
      Serial.println("i");
        myStepperY.step(800);
        //Serial.println(n);
      Serial.println("nach n");
      delay(1000);
      Serial.println("nach delay");
      Messausgabe="Messpunkt ";
      Serial.println(Messausgabe);
      Messpkt=(p-1)*MessfeldY +i;
      LCDText=Messausgabe + Messpkt;
      Serial.println(LCDText);
      LCDZwZeile="Messung laeuft";
      Serial.println(LCDZwZeile);
      Serial.println("nach Stringerstellung");

LCDWriteZw(LCDText, LCDZwZeile);

      delay(5000);
      Messwert=random(10);
      delay(2);
      Serial.println("nach Random");
      LCDText="Mpkt erfasst";
      Messausgabe="Messwert: ";
      LCDZwZeile=Messausgabe + Messwert;
      LCDWriteZw(LCDText, LCDZwZeile);
      delay(2000);
    }
  }
  LCDText="Messung beendet";
  LCDWrite(LCDText);
  delay(10000);  
}

void GezielterMesspunkt()
{
}

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

Die Aussage " .. bleibt unerwartet stehen" und String Objekte in einem Sketch verursachen in der Regel Reflexe...

Keine Ahnung, ob das in deinem Leonardo - Fall auch zutrifft.

Ich befürchte ein RAM-Problem ( Viel RAM nötig aber weniger vorhanden).
Grüße Uwe

Arghmano:
String LCDText="";

Verwendest Du die Arduino-Softwareversion 1.0.5 oder eine ältere?

"String" Objekte sind bis einschließlich Softwareversion 1.0.4 schwer fehlerhaft implementiert.

Und auch sonst haben String-Objekte gegenüber der Verwendung von C-Strings/Char-Arrays praktisch nur Nachteile.

Ceterum censeo objectum 'String' esse delendam.

Benutze die IDE1.0.2

Ich hab mit euren Ansätzen mal gesucht und bin hier im Forum auf die Anmerkung bezüglich String, dem F-Macro und der malloc.c Datei gestoßen. Hab das Work-around durchgeführt und jetzt klappt alles einwandfrei! Danke! Da sucht man ewig in seinem Code nach dem Fehler und dann liegts an der Software.

C-Strings sagen mir jetzt auf anhieb nichts und für Arrays war ich bisher schlicht zu faul (Prinzip kenn ich, nur lies sich das mit Strings schneller durchführen).

C-Strings sind Arrays aus chars, d.h. die Variable ist ein char* auf das erste Element. Das ist ganz einfach für das was du machst:

char str[] = "test"
lcd.print(str);

Ah ich muss garkeine Länge festlegen dafür? Das ist praktisch. Dann werd ich das mal noch damit probieren

Na ja.

char str[] = "test";

legt die Länge fest ( auf 5 Byte );
Wenn du danach
  str[10] = 0; schreibst, ist das kein Compiler-Fehler,
macht dir aber nicht unbedingt Freude.

c / c++ ist für Leute die wissen (sollten), was sie tun.

... oder so ähnlich, hab ich kürzlich gelesen.

c / c++ ist für Leute die wissen (sollten), was sie tun.

... oder so ähnlich, hab ich kürzlich gelesen.

Ja, ich entschuldige mich dafür, dass meine Muttersprache Deutsch und nicht c / c++ ist. Versäumnis meinerseits und das meiner Eltern. In meinem nächsten Leben werde ich alles von Anfang an beherrschen und "Learning by doing" wird dann nicht mehr nötig sein. Bis dahin bleibe ich dabei, dass ich Dinge nachlese,nachfrage und dann ausprobiere.

Wenn du den string gleich initialisierst, kann der Compiler die Länge selbst berechnen. Das ist das gleiche wie wenn man {} für andere Datentypen nimmt. z.B.:
int arr[] = { 1, 2, 3 };

Wenn du erst mal einen leeren string erstellen willst, musst du aber die Länge festlegen und dann strcpy() verwenden:

char str[5]

if(....)
{
       strcpy(str, "test");
}

str = "test" funktioniert hier nicht, da Literale const sind!

Was du bei dir vielleicht machen willst, ist das Array ein wenig größer machen, wenn du strings zusammenfügen willst. z.B:
char str1[10] = "Temp: ";

Dann kann man einfach mit strcat() einen zweiten string anhängen und muss nur darauf achten dass der Ziel-string groß genug für beide strings + Null-Terminator ist:
strcat(str1, str2);

Wenn du die C-String-Funktionen mit dem F-Makro verwenden willst, musst du glaube ich _P an die Funktion anhängen, damit auf das Flash zugegriffen werden kann. Also strcpy_P statt strcpy. Weil für Flash-Zugriff andere Pointer verwendet werden müssen.

Hätte noch ne andere Frage. Bei diesem Codestück

int StringAuslesen()
{
  EingabeTrue=false;
  NotausAbfrage();
  while(Serial.available() <= 0)
  {
    NotausAbfrage();
    Serial.println(F("Eingabe erforderlich"));
    delay(5000);
  }
  while(Serial.available()>0) 
  {
    NotausAbfrage();
        EingabeZeichen = Serial.read();
        if ((EingabeZeichen >=48) && (EingabeZeichen <=57))
        {
          EingabeZahl = (EingabeZahl*10)+(EingabeZeichen-48);
        }
        else
        {
        Inhalt.concat(EingabeZeichen);
        }
        if (Inhalt == "!")
        {
         RueckgabeZahl=EingabeZahl;
         EingabeTrue=true;
         EingabeZahl=0;
         Inhalt="";
        } 
  }
  if (EingabeTrue==true)
  {
  return RueckgabeZahl;
  }
}

hab ich das Problem, dass er die Ausgabe und Rückgabe auch dann macht, wenn ich nur eine Zahl eingebe, ohne das "!". Er sagt dann, dass die Eingabe "0" war. Verstehe leider nicht ganz warum. Ich hätte gerne, dass er den Wert erst übernimmt, wenn ich "3!" schreibe und nicht bei "3" dann einene "0" rein schreibt

  • Der Rückgabewert ist undefiniert, wenn EingabeTrue != true
  • Du gehst davon aus, dass entweder gar keine, oder eine komplette Eingabe vorhanden ist ?
    Da hilft dein delay(5000), aber sicher ist das nicht.
  • Inhalt == "!" trifft nur zu, wenn Inhalt ein String ist und '!' das erste und einzige Zeichen ist
  • Dein Codeschnipsel enthält keine Variablendefinitionen, das erschwert das Raten ...

Ich vermute mal, '!' soll ein Kennzeichen "Zahl zu Ende, Eingabe fertig" sein.
Die Funktion sollte -- möglichst ohne delay -- lesen was da ist und Eingabezahl weiterberechnen, und sobald ein '!' gelesen wurde, EingabeTrue setzen.

Den String Inhalt und Inhalt.Concat(EingabeZeichen) brauchst du nicht, da jedes Zeichen sofort verarbeitet wird.

Warum du eine NotausAbfrage zwischen 2 Zeichen machst, aber 5 sec Delay ( ohne NotausAbfrage ), verstehe ich nicht.
Ohne Delay musst du dir nur merken, wann du das letzte Mal "Eingabe erforderlich" ausgegeben hast, damit das nicht zu oft kommt.

NotausAbfrage(); kann dann nach loop, wo es hingehört , und mehrmals pro millisekunde drankommt.
StringAuslesen() könnte sich auch auf das Lesen eines einzelnen Zeichens beschränken ( oder gar keins ), dann wäre NotausAbfrage() noch häufiger dran.


Bis dahin bleibe ich dabei, dass ich Dinge nachlese,nachfrage und dann ausprobiere.

Das ist gut. Falls du meine Bemerkung über c++ - Programmierer als Kritik an dir aufgefasst hast, war das ein Missverständnis.
Es war eine allgemeine Warnung, vor c und dem c - Compiler, der nicht immer so hilfreich ist, wie man sich das gerne wünschte.
Bei mir ist gelegentilch ein leicht ranziger Ton hörbar. Das sollte keinen stören :wink:

michael_x:

  • Dein Codeschnipsel enthält keine Variablendefinitionen, das erschwert das Raten ...

Im allerersten Post und da im zweiten Code Feld sind die Variablen definiert

  • Der Rückgabewert ist undefiniert, wenn EingabeTrue != true

Den leg ich dort auf 0 fest, was mir dann auch ausgegeben wird. (Ich denke, das ist die selbe "0")

  • Du gehst davon aus, dass entweder gar keine, oder eine komplette Eingabe vorhanden ist ?
    Da hilft dein delay(5000), aber sicher ist das nicht.

Also meine Idee war, dass wenn EingabeTrue(ja der Variablenname ist eher unvorteilhaft) == false ist, er garkeine Rückgabe macht, sondern solange in dieser Prozedur festhängt, bis die richtige Eingabe (nämlich mit einem "!" am Ende zur Bestätigung) getätigt wurde. Gibt das Ding denn als default 0 zurück? Ich verstehe halt nicht, warum er die Rückgabe durchführt, wenn ich doch EingabeTrue == false habe :frowning: (Also im Prinzip habe ich die Funktionsweise einer Prozedur mit einem return noch nicht durchschaut).

  • Inhalt == "!" trifft nur zu, wenn Inhalt ein String ist und '!' das erste und einzige Zeichen ist
    Ich vermute mal, '!' soll ein Kennzeichen "Zahl zu Ende, Eingabe fertig" sein.
    Die Funktion sollte -- möglichst ohne delay -- lesen was da ist und Eingabezahl weiterberechnen, und sobald ein '!' gelesen wurde, EingabeTrue setzen.

Genau ich möchte, dass er die Zahl nur dann verwendet, wenn ma Ende ein "!" steht. Ist zwar irgendwie noch doof, weil er das dann auch annimmt, wenn da nur ein "!" steht, aber naja...

Den String Inhalt und Inhalt.Concat(EingabeZeichen) brauchst du nicht, da jedes Zeichen sofort verarbeitet wird.

Da dann lieber noch eine "if (EingabeZeichen ==33) { bla }" einfügen? Sollte den selben Zweck erfüllen oder?

Warum du eine NotausAbfrage zwischen 2 Zeichen machst, aber 5 sec Delay ( ohne NotausAbfrage ), verstehe ich nicht.
Ohne Delay musst du dir nur merken, wann du das letzte Mal "Eingabe erforderlich" ausgegeben hast, damit das nicht zu oft kommt.

Das Notaus zwischen den 2 Zeichen kann eigentlich weg, das stimmt. Das war nur ein "Ich muss überall Notaus haben"-Panik copy&paste. Ich muss mir das mit dem "Merken" mal anschauen. Das geht bestimmt mit millis() und sowas oder? Werd ich Montag mal probieren (Code und Arduino sind im Geschäft). So wies jetzt ist war ich auch sehr unzufrieden, weil mir alle Delays die Notausprozedur zeitlich verzögert haben (was ja nicht deren Sinn ist). Die meistens sind nur drin, damit mich Arduino im Serial-Monitor nicht zuspammt (also ziemlich primitiv). Ich wollte das ansonsten mit einem Interrupt machen, aber da hängt sich das Mega (inzwischen bin ich auf ein Mega umgestiegen, wegen Pinmangel am Leonardo), wenn ich etwas aufs LCD Display ausgeben möchte.

NotausAbfrage(); kann dann nach loop, wo es hingehört , und mehrmals pro millisekunde drankommt.

Kannst du mir erklären, wie ich eine Prozedur im loop aufrufe bzw. abfrage, während ich in einer anderen Prozedur bin? Z.B. Während der Prozedur messen, die unter anderem sehr sehr lange dauern kann, verbleibt er ja dann und kommt (in meiner Vorstellung) erst an deren Ende wieder zur Notausprozedur im loop. Ein Link wo das erklärt wird, reicht mir vorerst auch.

StringAuslesen() könnte sich auch auf das Lesen eines einzelnen Zeichens beschränken ( oder gar keins ), dann wäre NotausAbfrage() noch häufiger dran.

Darauf komm ich vielleicht später nochmal zurück.


Bis dahin bleibe ich dabei, dass ich Dinge nachlese,nachfrage und dann ausprobiere.

Das ist gut. Falls du meine Bemerkung über c++ - Programmierer als Kritik an dir aufgefasst hast, war das ein Missverständnis.

Es war eine allgemeine Warnung, vor c und dem c - Compiler, der nicht immer so hilfreich ist, wie man sich das gerne wünschte.
Bei mir ist gelegentilch ein leicht ranziger Ton hörbar. Das sollte keinen stören :wink:

Okay, dann war es ein Missverständnis :wink:

Wenn du in einer Methode was machen willst bis ein bestimmtes Ereignis eintritt, musst du das mit eine while-Schleife packen. Bei dir ist einfach die Methode zu Ende wenn EingabeTrue == false.

Pseudocode:

while(variable == false)
{
    //mach was

    variable = ....
}

Oh wie doof... bei allen anderen Prozeduren habe ich das ja verwendet gehabt, nur dachte ich, dass das bei return vielleicht nicht nötig ist, da die Prozedur auf den Return wert "wartet", was sie aber allem Anschein nach nicht tut.

Danke! Dann werde ich das Montag auch hier mit while lösen :slight_smile:

Kannst du mir erklären, wie ich eine Prozedur im loop aufrufe bzw. abfrage, während ich in einer anderen Prozedur bin? Z.B. Während der Prozedur messen, die unter anderem sehr sehr lange dauern kann, verbleibt er ja dann und kommt (in meiner Vorstellung) erst an deren Ende wieder zur Notausprozedur im loop. Ein Link wo das erklärt wird, reicht mir vorerst auch.

Das ist der "BlinkWithoutDelay" - Ansatz, findest du im Tutorial nach dem ersten Blink-Beispiel
Kannst du auch auf eigene Funktionsaufrufe statt digitalWrite() anwenden.
Innerhalb deiner Funktionen sind dann keine delays oder while - Schleifen.
Statt dessen stellt eine Funktion fest, dass noch nichts zu tun ist und beendet sich.
In deinem Beispiel also

if (Serial.available() )
lies ein Zeichen und
berechne EingabeZahl weiter
oder
Eingabezahl ist fertig

Das dauert ein paar Microsekunden und du bist wieder in loop, wo die nächste unabhängige Aufgabe ( NotAusTaster prüfen ) genausoschnell erledigt ist.

So kannst du gemütlich beliebig langsame serielle Eingaben bearbeiten und quasi gleichzeitig sofort auf andere Ereignisse reagieren.

Der Schlüssel sind static Variable, die von einem Funktionsaufruf zum nächsten ihren Wert behalten ( EingabeZahl , beim Blinken der Zeitpunkt des letzten an/aus Wechsels, usw. ). Falls sie auch ausserhalb der Funktion gebraucht werden, kannst du auch globale Variable nehmen...

Return gibt einfach einen Wert zurück und beendet die Methode. Das ist wieder so eine Stelle wo dich der C-Compiler einfach reinfallen lässt. Andere Compiler, bzw. Sprachen meckern da zum Teil wenn ein Pfad keinen Wert zurück gibt.

Wenn du es gleich auf die beste Weise machen willst, mach es aber wie oben gesagt. Dann kannst du die Methode aufrufen und wenn nichts zu tun ist kehrt sie sofort nach loop zurück. Wenn du in dem Fall z.B. 0 oder -1 zurück gibst weißt du in loop das nichts gemacht wurde. Wenn du dann mit dem Einlesen fertig bist, gibt du etwas größeres zurück und kannst es in loop verarbeiten.

z.B.:

loop()
{
   int var = method();

   if(var > 0)
   {
       //Eingabe verarbeiten
   }
}

michael_x:
Das ist der "BlinkWithoutDelay" - Ansatz, findest du im Tutorial nach dem ersten Blink-Beispiel
Kannst du auch auf eigene Funktionsaufrufe statt digitalWrite() anwenden.
Innerhalb deiner Funktionen sind dann keine delays oder while - Schleifen.
Statt dessen stellt eine Funktion fest, dass noch nichts zu tun ist und beendet sich.
In deinem Beispiel also

if (Serial.available() )
lies ein Zeichen und
berechne EingabeZahl weiter
oder
Eingabezahl ist fertig

Das dauert ein paar Microsekunden und du bist wieder in loop, wo die nächste unabhängige Aufgabe ( NotAusTaster prüfen ) genausoschnell erledigt ist.

So kannst du gemütlich beliebig langsame serielle Eingaben bearbeiten und quasi gleichzeitig sofort auf andere Ereignisse reagieren.

Der Schlüssel sind static Variable, die von einem Funktionsaufruf zum nächsten ihren Wert behalten ( EingabeZahl , beim Blinken der Zeitpunkt des letzten an/aus Wechsels, usw. ). Falls sie auch ausserhalb der Funktion gebraucht werden, kannst du auch globale Variable nehmen...

void loop()
{
  Joystick();
  Ausgangsposition();
  StartpunktAnfahren();
  MessrasterAngabe();
  Messung();
  unsigned long momentanerZeitpunkt = millis();
  if ( momentanerZeitpunkt - letzterZeitpunkt > interval )
  {
    letzterZeitpunkt = momentanerZeitpunkt;
    NotausAbfrage();
  }
}

Der führt die Prüfung in diesem Falle doch erst durch, wenn der Rest abgeschlossen ist oder? Aber ich will eigentlich, dass er das permanent prüft. Geht das überhaupt irgendwie? Oder muss ich das echt durch jede Prozedur durchschleifen? "Messung()" kann an sich mehrere Stunden dauern...

Wenn du das ständig machen willst, musst du einen Timer einrichten und das in einer Interrupt Service Routine durchführen. Die kann alles andere unterbrechen.

Wenn dir die manuelle Einrichtung eines Timers zu kompliziert ist, kannst du einfach die MsTimer2 Klasse verwenden:
http://playground.arduino.cc/Main/MsTimer2

Hier wie man es per Hand macht:
http://maxembedded.com/2011/07/14/avr-timers-ctc-mode/
http://www.avrfreaks.net/index.php?name=PNphpBB2&file=viewtopic&t=50106
http://elk.informatik.fh-augsburg.de/da/da-21/Tutorial/tutorial.html

Sieht vielleicht etwas erschlagend aus, ist aber gar nicht so kompliziert. Man braucht nur den CTC Modus und muss den Prescaler und das Compare Register auf den richtigen Wert einstellen um die Zeit zu erhalten. Das sind eine handvoll Zeilen Code. Normalerweise nimmt man da Timer2, da der den flexibelsten Prescaler hat.

Und denk daran, dass du Variablen die in der ISR verwendet werden als "volatile" deklarieren musst! Auch wenn du MsTimer2 verwendest.