Switch Case - in Funktion gefangen

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

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 :wink:
i=5; //i 5 zuweisen
wäre Nix - Das kann man so sogar selber lesen :wink:
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.

OT

Aber so oder so solltest du mal von den 9600 Baud wegkommen

Wir haben üblicherweise 300 Baud verwendet. Das war langsam. Als Mensch, bei nicht so genauem Hinschauen, kann man sogar bis 9600 Baud mitlesen. Jetzt im Alter wird das "nicht so genaue Mitlesen" gröber.

Wenn für Ausgaben, die laufend mitgelesen werden sollen, 9600 nicht ausreicht, sollte man sich Gedanken machen, was man anzeigen will...

michael_x:
Wenn für Ausgaben, die laufend mitgelesen werden sollen, 9600 nicht ausreicht, sollte man sich Gedanken machen, was man anzeigen will...

Wenn man etwas am Stück ausgibt mag 1ms pro Zeichen reichen. Aber wenn man alle 1ms messen will und zwischen den Messungen die Daten direkt ausgibt ist 9600 Baud definitiv zu langsam.

Mir geht es auch darum dass er sich mal über die Problematik bewusst wird und überlegt ob und wie das sein Programm beeinflusst. Das ist überhaupt erst ein vernünftiger Grund weshalb man vielleicht alle Daten erst mal abspeichern will.

Hallo,

man kann sich einen seriellen Monitor bauen der aller x ms/s eine Zeile mit den gewünschten Daten raushaut.
Oder man baut sich einen Monitor der nur bei Änderung eines Wertes eine Zeile raushaut.
In beiden Fällen kann man mit höheren Baudraten fahren und mitlesen.

Hi

Die Baudrate sollte doch für das Mitlesen ziemlich egal sein - oder wollt Ihr hier nur ein delay verstecken, daß die Ausgaben 'nicht zu schnell' kommen?

DANN dürft Ihr Euch aber doch nicht wundern, daß der Knabe lahmaschig wie ne Schnecke ist!

MfG

Hallo,

von delay hat niemand etwas geschrieben.
Nur wenn er 250 Werte aller 1 Sekunde messen und ausgeben möchte, dann wird es knapp mit 9600 Baud ...