Switch Case - in Funktion gefangen

Hallo Zusammen,

ich habe eine Problem bei einem Switch Case Programm.
Per Serial Read lese ich einen Wert ein, der mit den Case vorgibt. Beispielsweise 48,49,50,51.

Die Cases einrehen und rausdrehen habe ich der übersichtshalber weggelassen.

Mein Problem ist, dass ich nicht mehr in die Loop zurückkomme nachdem mein Case erst einmal ausgewählt ist. Ich verharre in der entsprechenden Funktion. Wenn ich im Seriellen Monitior erneut einen Case auswähle, wechselt dieser trotzdem nicht.

Ist das Problem dies, dass meine Funktionen nicht in der Loop sind?

Beste Grüße
Tim

void setup() {

  // Kommunikationsrate
  Serial.begin(9600);

}

void loop()
{
  char input;
  // Case Bedingung
  if (Serial.available() > 0) {
    input = Serial.read();
    Serial.println(input);
  }

  // Case Auswahl
  switch (input) {
    case 48: ruhemodus(); break;
    case 49: rausdrehen(); break;
    case 50: reindrehen(); break;
    case 51: messen();     break;

  }

}

void ruhemodus() {
  Serial.println("warten");
}

void messen() {
  Serial.println("messen aktiviert");

  int A0 = analogRead(A0);
  String messwerte[250];
  if ((A0 > 10) && (A0 < 1020))
    for (unsigned int i = 0; i < 250; i++) {
      messwerte[i] = analogRead(A0);
      delay(1000);
    }
}
for (unsigned int i = 0; i < 250; i++)
      delay(1000);

Du wartest auch 250 mal 1 Sekunde. Da brauchst du dich nicht zu wundern dass nichts mehr reagiert.

Und ein Array aus 250 String Objekten anzulegen um Messwerte zu speichern ist höchstgradiger Unsinn. Auf einem kleinen Prozessor belegst du leicht den kompletten Arbeitsspeicher. And auch auf den größeren 32 Bittern ist das einfach nur unnötig. Wenn überhaupt Speichern dann als Zahl. Oder besser direkt ausgeben wenn du zwischen den Messungen sowieso so lange wartest. Erst wenn man schneller als die Datenübertragung misst muss man wirklich zwischenspeichern

alllein dein "messen()" kann bis zu 4 Minuten dauern. Daher --> poste einen vollständigen Sketch und schreibe bei welchem Input du das Verhalten feststellst, und dazu die vollständige Ausgabe deines Serial Monitors.

(schon wieder zu spät ...)

Hi

Wie immer in so Fällen: stopfe den Sketch mit Serial.print("12"); voll (die Zahl sollte dabei überall anders sein ...). So SIEHST Du, wo der Käfer rum rennt.

In Deinem Fall ist's zwar klar - 250 delay(1000) am Stück sind 250 Sekunden Zwangspause, in Denen Du NICHTS ANDERES machen kannst - sonderlich clever ist Das nicht - soll heißen: Da gibt's elegantere Möglichkeiten - Suchwort State-Maschine, Blink_without_delay, Nachtwächtererklärung

MfG

Ratpack:
Ist das Problem dies, dass meine Funktionen nicht in der Loop sind?

Eine Funktion ist ein Teil deines Programms, ebenso wie die loop, die auch eine Funktion ist.
Also ist es kein Problem wenn diese Funktion nicht in der loop steht und darf es auch nicht.
Jede Funktion ist ein eigenständiger Bereich deines Programms.

Und jetzt freuen wir uns auf dein Ergebnis bzw. deine Rückmeldung.

Moin,

vielen Dank für eure Rückmeldungen. Ich hab leider nicht an die lange Wartezeit beim testen gedacht. Das war auch der Grund warum ich nicht wieder raus kam.

Das mit den 250 Einträgen als war eine erste herangehensweise um analoge Messwerte seriell weiterzugeben. Ich habe mich an dieser Stelle noch für keine Sinnvolle Funktion entschieden, bin mir aber bewusst dass es so schwachsinn ist. Ich möchte jede Millisekunde einen Wert Speichern, dass ganze eben für 250 ms. Die 1 sec delay in meinem Programm war dahingehend auch falsch.

Ich habe jetzt, um meine case Funkion auszutesten die Programme dahingehend vereinfacht, dass ich nur eine Notiz im Plotter bekomme sobald diese aktiviert ist. Was ich jetzt noch machen muss, ist eine Abbruchbedingung mit einbauen, also dass meine Funktion exakt 1x ausgeführt wird und dann case 0 (warten) aktiviert wird. Aber das versuche ich erst einmal selber herauszufinden :)

Vielen Dank für eure Hilfe Tim

void setup() {

  // Kommunikationsrate
  Serial.begin(9600);

}

void loop()
{
  char input;
  // Case Bedingung
  if (Serial.available() > 0) {
    input = Serial.read();
    Serial.println(input);
  }

  // Case Auswahl
  switch (input) {
    case 48: ruhemodus(); break;
    case 49: rausdrehen(); break;
    case 50: reindrehen(); break;
    case 51: messen();     break;

  }

}

void ruhemodus() {
  Serial.println("warten");
}

void rausdrehen() {
  Serial.println("rausdrehen");
}

void reindrehen() {
  Serial.println("reindrehen");
}

void messen() {
  Serial.println("messen aktiviert");

    }
}

Hallo Nochmal,

ich habe jetzt versucht meine Loop mit einer Flag zu versehen um das Programm nur ein mal auszuführen. Das klappt zwar, in dem es nach ausführen des gewählten case aufhört, aber die Loop wird nicht weiter durchlaufen.

void loop()
{
  char input;
  // Case Auswahl
  if (Serial.available() > 0) {
    input = Serial.read();
    Serial.println(input);
  }

 static boolean Flag = true;
if (Flag) {
  switch (input) {
    case 48: ruhemodus(); Flag = false; break;
    case 49: rausdrehen(); Flag = false; break;
    case 50: reindrehen(); Flag = false; break;
    case 51: messen();      Flag = false; break;
  }
  }

  }

Auch ein rausziehen von Flag = false für nur dazu, dass erst gar keine Eingabe mehr erkannt wird.
Was mache ich falsch? In den Einzelnen Case Funktionen ist nur ein Println drin.

Du willst einen Zustandsautomaten / endlichen Automaten. Dann merke dir auch in welchem Zustand du bist und nicht einfach nur mit einem bool der nur zwei Werte annehmen kann. Dazu gibt es in diesem Forum unendlich oft Beispiele. Und auch im Netz gibt es sehr viel dazu. switch/case ist schon ok, aber man verwenden im einfachsten Fall ein enum um den Zustand zu speichern.

Verzögerungen macht man mit millis() nach dem BlinkWithoutDelay Prinzip. Dann kannst du zwischen den Messungen Daten versenden wenn die Zeit reicht.

Auch solltest du die Baudrate weit, weit höher setzen. Bei 9600 Baud braucht ein einzelnes Zeichen schon ca. 1ms (1 / Baudrate * 10 Sekunden). Da die Übertragungzeit und die Zeit für die A/D-Wandlung bekannt sind, kann man so auch leicht sehen ob die Zeit für die Übertragung der Messwerte reicht und ob man überhaupt etwas zwischenspeichern muss

Hallo,

erstmal muss dein serielles einlesen funktionieren. Du möchtest Werte wie 48 oder 50 eingeben. Okay. Aber das sind beim einlesen 2 getrennte Ziffern. 48 wird als 4 und 8 eingelesen und 50 als 5 und 0. Schau dir atoi() an.

Doc_Arduino: erstmal muss dein serielles einlesen funktionieren. Du möchtest Werte wie 48 oder 50 eingeben. Okay. Aber das sind beim einlesen 2 getrennte Ziffern. 48 wird als 4 und 8 eingelesen und 50 als 5 und 0. Schau dir atoi() an.

Es werden schon einzelne Zeichen eingelesen. Und das ist sicherlich auch so gewollt. Es ist halt blöd geschrieben. So sieht sofort man was eigentlich gemeint ist und muss nicht rätseln oder die ASCII Tabelle auswendig können:

  case '0':
  case '1':
  case '2':
  case '3':

Wenn man ganze Strings einlesen will, muss man weiter ausholen. Also in Array einlesen, Endzeichen, Null-Terminator, etc. atoi() kommt erst am Ende. Das ist hier aber erst mal unnötig. Es gibt genug einzelne Zeichen um Kommandos darzustellen

Hallo,

ich dachte weil der TO Eingangs zweistellige Werte verwendet. Wenn er mit Einstelligen auch zufrieden ist, okay.
Habe mal ein Grundgerüst erstellt.

enum class Job : byte {ruhe, raus, rein, messen};
Job job = Job::ruhe;

void setup()  {

  Serial.begin(9600);

}


void loop(void) {

  readSerial();

  makeYourJob(1000);
}

/* --------------------------------------------------------------- */

void makeYourJob (unsigned int interval)
{
  static unsigned long last_ms = 0;
  unsigned long ms = millis();

  if (ms - last_ms >= interval) {
    last_ms = ms;
    switch (job) {
      case Job::ruhe:   Serial.println(F("ich bin in ruhe")); break;
      case Job::raus:   Serial.println(F("ich bin raus")); break;
      case Job::rein:   Serial.println(F("ich bin drin")); break;
      case Job::messen: Serial.println(F("ich messe")); break;
    }
  }
}


void readSerial()
{
  if (Serial.available() > 0)  {
    char c = Serial.read();

    switch (c) {
      case '1': job = Job::ruhe;   break;
      case '2': job = Job::raus;   break;
      case '3': job = Job::rein;   break;
      case '4': job = Job::messen; break;
      default: break;
    }
  }
}

Wenn man im Messmodus ist und der ist fertig, dann setzt man job einfach mit job = Job::ruhe in den Ruhemodus. Dort dreht er dann Däumchen bis du wieder mit einer Zifferneingabe einen anderen Modus auswählst.

ich habe jetzt versucht meine Loop mit einer Flag zu versehen um das Programm nur ein mal auszuführen. Das klappt zwar, in dem es nach ausführen des gewählten case aufhört, aber die Loop wird nicht weiter durchlaufen.

Du verwirrst dich selber mit dieser Interpretation zu deiner Version in #6. Natürlich wird loop weiter durchlaufen.
Allerdings wird dein static boolean Flag nie wieder auf true gesetzt.

Das Problem (schon in deinem ersten Versuch) ist eher, dass input immer den Wert des letzten empfangenen Zeichens behält.
Entweder
– dein switch-Block kommt in den Zweig
if (Serial.available()) {}
– oder du löschst input nach dem Abarbeiten einfach wieder
– oder du setzt Flag wieder auf true, wenn ein neuer Befehl über Serial reinkommt.

Doc_Arduino: ich dachte weil der TO Eingangs zweistellige Werte verwendet.

Es sind aber keine zweistelligen Werte. 50 ist was anderes als "50". 50 ist einfach die Integer Darstellung eines ASCII Zeichens

Hallo

mir ist ja noch nicht ganz klar was Du vorhast, spielt aber erst mal keine Rolle.

Du willst analoge Messwerte in einem Array ablegen und in festen Zeitabständen messen. Gesteuert werden soll das mit dem Monitor mit der Eingabe 0-3. Für die Zeitabstände kann man am einfachsten millis() verwenden. Jetzt bleibt da noch die Frage soll während einer Messperiode eine andere Funktion ausgeführt werden können , oder soll die Eingabe gesperrt sein. Ansonsten könnte es sein das die Zeit zwischen zwei Messungen nicht konstant ist. Wenn Du aber wie geschrieben jede ms einen Wert messen willst dann sollte man das besser sperren. Dazu dürfte dann eine neue Eingabe nur möglich sein wenn der Status nicht 3 ist.

Ich hab das jetzt auf die Schnelle mal nur mit zwei Blöcken hinbekommen.
1 Abfrage der Eingabe als char dabei geht so nur 1 Zeichen und damit einen byte Status setzen 0-3
2.Status auswerten

ob man das jetzt mit if , select oder enum macht ist Geschmackssache.

Hier mal ein Vorschlag damit Du da weiterkommst.

Heinz

const byte messpin = A0;
unsigned long altzeit;
char inchar;
int messwert[250];
const unsigned long messzyklus = 1;
byte i = 0; // Arrayindex
byte status = 0;

void setup() {
  // put your setup code here, to run once:
  Serial.begin(9600);
}

void loop() {
  // put your main code here, to run repeatedly:

  if (Serial.available() > 0) {
    char inchar = Serial.read();
    //Serial.println(inchar);
    switch (inchar) {
      case '0': status = 0; break;
      case '1': status = 1; break;
      case '2': status = 2; break;
      case '3': status = 3; break;
    }
  }
// status auswerten
  if (status == 0) ruhemodus();
  if (status == 1) rausdrehen();
  if (status == 2) reindrehen();
  if (status == 3) messen();
}


void ruhemodus() {
  //Serial.println("ruhe");
  status = 0;
}

void rausdrehen() {
  Serial.println("rausdrehen");
  status = 0;
}

void reindrehen() {
  Serial.println("reindrehen");
  status = 0;
}

void messen() {
  if (i < 250) { // Array füllen
    if (millis() - altzeit >= messzyklus) {
      altzeit = millis();
      if (i == 0) Serial.println("messen aktiv");
      messwert[i] = analogRead(messpin);
      i++;
    }
  }
  else {  // Ausgabe der Werte
    Serial.println("messen fertig");
    for (i = 0; i < 250; i++) {
      Serial.print(i); Serial.print("\t");
      Serial.println(messwert[i]);
    }
    status = 0; // status rücksetzen
  }
}

Serenifly: Es sind aber keine zweistelligen Werte. 50 ist was anderes als "50". 50 ist einfach die Integer Darstellung eines ASCII Zeichens

Wenn ich im seriellen Monitor 50 eingebe und Enter drücke, dann liest er eine 5 und eine 0 getrennt ein. Diese landen im Bufferarray auf Index 0 und 1. Um wieder die gewünschte 50 zu erhalten muss man die doch erst mittels atoi zusammensetzen? ASCII Code 48 bis 57 stellt doch nur eine einzelne Ziffer 0...9 dar. Oder wir reden aneinander vorbei? Vielleicht wäre es eine wichtige Frage an den TO wo die Werte herstammen? Per Eingabe oder wird von irgendwoher ein Byte übertragen?

Hallo Zusammen,

vielen Dank für die zahlreichen Antworten die mir sehr geholfen haben.

Bei meinem ersten Projekt habe ich immer zuerst die Funktion ausgestaltet und dann das Hauptgerüst erstellt, mittlerweile denke ich da eher andersherum desshalb habe ich das große ganze auch noch nicht erklären wollen, da grobe Fehler enthalten sind.

Soll-Funktion meines Programms.

  1. Seriell soll eines von vier Programmen angesteuert werden: 1. Schrittmotor rechtsherum 2. Schrittmotor linksherum 3. messen 4. kalibireren. Nie müssen zwei Funktionen gleichzeitig ausgeführt werden.

  2. Es wird analog die Spannung eines Positionssensors gemessen. Eine Messung generiert 250 Werte (von 0 bis 1023). Die Abtastfrequenz soll 1ms betragen. Die Werte sollen entweder in einem array oder einem string gespeichert werden da ich noch nicht weiß ob ich darauf verzichten kann. Im Anschluss soll dieser seriell zurückgegeben werden. Ein zweiter Arduino soll die Messwerte aufnehmen und weiterverarbeiten (andere Baustelle).

Die Funktionen für den Schrittmotor habe ich fertig (rein / raus sind zwei Funktionen analog aufgebaut).
Das funktioniert soweit gut.

Die Auswahl meiner Funktion habe ich jetzt wie vorgeschlagen gelöst:

void loop()
{
  char input;
  static boolean Flag;
  // Case Auswahl
  if (Serial.available() > 0) {
    input = Serial.read();
    Serial.println(input);
    Flag = true;
  }

  if (Flag) {
    switch (input) {
      case '1': rausdrehen(); Flag = false; break; 
      case '2': reindrehen(); Flag = false; break;
      case '3': messen();     Flag = false; break;
      case '4': kalibrieren(); Flag = false; break;
    }
  }

}

Dabei ist aber noch ein Problem aufgetaucht. Wenn ich die Messwerte mit Serial.print ausgebe, wird eine Funktion aktiviert die gar nicht aktiviert werden soll, da die Zahlen zur Case Auswahl einer Untermenge meines Wertebereichs der Messung ist. Hier muss ich also entweder meine inputs auf >1023 legen oder anders programmieren.

@Serenify:
Die Werte kann ich mir generell aussuchen. An Ende des Lieds wird per UDP ein Wert an den Arduino gesendet um eine Funktion auszuwählen.

Für die Aufnahme und Speicherung der Messwerte muss ich mir was eleganteres wie bisher überlegen. Vielen Dank Heinz, ich werde es mit deinem Vorschlag mal versuchen und dann berichten.
Meine Funktion wird nie mehr als 1x hintereinander aufgerufen.

Noch eine Frage: Wenn ich möchte, dass ein zweiter Arduino die Messwerte Seriell einliest, reicht es aus, diese mit Serial.print auszugeben oder muss ich serial.write verwenden? Soweit bin ich noch nicht, aber die Nacht ist ja lang :slight_smile:

Beste Grüße und vielen Dank,
Tim

Hi

Da ich mich früher viel mit händischer Zeicheneingabe in Assembler beschäftigt habe (8086 ... schon etwas her), sah ich in der 48 (=0x30 = '0') direkt das Zeichen Null, wie bei der 50 eine Zwei.

Also eine Wahl der Aktion per Zahlzeichen - ein Kommentar hätte Das allerdings auch dem Rest schnell vor's geistige Auge bringen können.

@TO: Kommentare erleichtern auch später Dir die Arbeit mit dem Sketch - aber bitte sinnvoll kommentieren ;) i=5; //i 5 zuweisen wäre Nix - Das kann man so sogar selber lesen ;) i=5; //i - die Anzahl der benötigten/erwarteten Zeichen ... wenn jetzt statt 'i' noch ein sinnvoller Name gewählt wurde, wird's noch klarer.

MfG

Edit Serial.print(wert) gibt den Wert als String in Zahlzeichen aus - eine 65 wird als '6' '5' ausgegeben. Serial.write(65) ergibt in jedem Fall ein A (ASCII-Code 65 -> großes A, .write schreibt genau diese Zahl, keine Interpretationen dabei. ... kA, was .write bei >255 macht, denke, dann splittet der Kram in die Einzel-Bytes auf, wobei bei Little-Endian (wie im Arduino) die Bytes vom Niederen zum Höchsten Byte ausgegeben werden. 255 -> FF 256 -> 00 01 257 -> 01 01 Edit 267 -> 257 !! ...

Ratpack:
Die Werte kann ich mir generell aussuchen. An Ende des Lieds wird per UDP ein Wert an den Arduino gesendet um eine Funktion auszuwählen.

Mich interessiert wo die Werte aktuell, also jetzt, herkommen?

Ratpack: Noch eine Frage: Wenn ich möchte, dass ein zweiter Arduino die Messwerte Seriell einliest, reicht es aus, diese mit Serial.print auszugeben oder muss ich serial.write verwenden?

print() sendet ASCII. write() sendet Binär

Wenn man Dinge also ASCII sendet hat man mehr Daten, aber die Synchronisation auf Anfang und Ende ist viel einfacher. Ich habe die gesagt wie man berechnet wie lange die Übertragung dauert. Wenn du zwischenspeicherst und einmal am Ende sendest ist es auch nicht so kritisch. Aber so oder so solltest du mal von den 9600 Baud wegkommen

Doc_Arduino: Wenn ich im seriellen Monitor 50 eingebe und Enter drücke, dann liest er eine 5 und eine 0 getrennt ein.

Das macht er aber nicht. Er gibt '0' ein und wird dann mal Serial.print(c, DEC) gemacht haben. Dann kommt 48 raus. Und das verwirrt Anfänger sehr gerne

Serenifly:
Das macht er aber nicht. Er gibt ‘0’ ein und wird dann mal Serial.print(c, DEC) gemacht haben. Dann kommt 48 raus. Und das verwirrt Anfänger sehr gerne

Jetzt habe ich das geschnallt. Danke. Echt verwirrend.