Switch Case Anweisung

Hallo,
ich hab folgendes Problem mit meinem Arduino Programm.
Also ich baue mir momentan ein Cocktail Mixer.
Über ein Tastenfeld sollen die Leute ihren Cocktail aussuchen, um das zu realisieren hab ich eine Switch-Case Anweisung verwendet.
Wenn ich das Programm frisch in den Arduino Mega 2560 lade, funktioniert alles wie es soll(Schrittmotor fährt zu den einzelnen Position und fährt anschließend solange zurück bis der Endschalter betätigt wurde).
Sobald ich aber einen zweiten Cocktail gewählt habe, spielt der Schrittmotor komplett verrückt (als wenn er gleichzeitig vorwärts und rückwärts fahren will).
Ich hatte den Verdacht das der ''Case'' nicht beendet wird und das Programm in der while Schleife hängen bleibt.
Um das Problem zu lösen, hab ich versucht den ''break'' Command zu verschieben, was aber gescheitert ist.

Habt ihr eine Lösung?
Das Programm ist nicht das vollständige sondern lediglich ein ''Case''.

#include <Servo.h>
#include <Keypad.h>
#include <AccelStepper.h>

const byte ROWS = 4; //four rows
const byte COLS = 4; //four columns
//define the cymbols on the buttons of the keypads
char hexaKeys[ROWS][COLS] = {
  {'1','2','3','A'},
  {'4','5','6','B'},
  {'7','8','9','C'},
  {'*','0','#','D'}
};

byte rowPins[ROWS] = {6, 7, 8, 9}; //connect to the row pinouts of the keypad
byte colPins[COLS] = {11, 12, 4, 5}; //connect to the column pinouts of the keypad

//Einstellung für den Servo
const int servoPin = 10;
const int angleOpen = 90;
const int angleClosed = 140;
const int openDelay = 3000;
const int openDelay2 = 5000;
const int relais = 10;

//Einstellungen Pos. der Schnäpse
/*int rum = 3380;
int vodka = 300;
int korn = 300;
int likoer = 300;
*/



//Treiber Schrittmotor
AccelStepper stepper (1, 2, 3);


//Servo-Object erzeugt
Servo myservo;

//initialize an instance of class NewKeypad
Keypad customKeypad = Keypad( makeKeymap(hexaKeys), rowPins, colPins, ROWS, COLS); 

//Relais

const int relais1 = A0;
const int relais2 = A1;
const int relais3 = A2;
const int relais4 = A3;
const int relais5 = A4;
const int relais6 = A5;
const int relais7 = A6;
const int relais8 = A7;


//Endstop
int endstop_1 = A14;
int endstop_2 = A15;

void setup(){
  Serial.begin(9600);
  myservo.attach(servoPin);
  myservo.write(angleOpen);
  
 //Relais
  pinMode(relais1, OUTPUT);
  pinMode(relais2, OUTPUT);
  pinMode(relais3, OUTPUT);
  pinMode(relais4, OUTPUT);
  pinMode(relais5, OUTPUT);
  pinMode(relais6, OUTPUT);
  pinMode(relais7, OUTPUT);

  //Endstop
  pinMode(endstop_1, INPUT);
  pinMode(endstop_2, INPUT);
  Serial.begin(115200);
 
  
  //Schritt Motor
  stepper.setMaxSpeed(1000);
  stepper.setAcceleration(1000);
  stepper.setSpeed(100);
  

  
  digitalWrite(relais1, HIGH);
  digitalWrite(relais2, HIGH);
  digitalWrite(relais3, HIGH);
  digitalWrite(relais4, HIGH);
  digitalWrite(relais5, HIGH);
  digitalWrite(relais6, HIGH);
  digitalWrite(relais7, HIGH);
  digitalWrite(relais8, HIGH);
 
  
}

  
void loop(){
char Taste = customKeypad.getKey();
Serial.begin(115200);

if (Taste){ //Wenn eine Taste gedrückt wurde
 Serial.print("Die Taste ");
 Serial.print(Taste);
 Serial.print(" wurde gedrueckt");
 Serial.println(); //Teile uns am Serial Monitor die gedrückte Taste mit
}

 

switch (Taste){
  
  //Coctail 1
  case 48:
 
  
//Startposition
  if(digitalRead(endstop_1) == LOW){

    
     digitalWrite(relais8, LOW);
     delay(300);
    stepper.setMaxSpeed(800);
    stepper.runToNewPosition(-3730);
    
      
    //myservo.write(angleClosed);
      delay(openDelay);
    myservo.write(angleOpen);
      delay(500);
    
    stepper.setMaxSpeed(400);
    stepper.runToNewPosition(-3250);
    
    //myservo.write(angleClosed);
      delay(openDelay);
    myservo.write(angleOpen);
    delay(500);

    stepper.setMaxSpeed(400);
    stepper.runToNewPosition(-2680);
    
      delay(500);

    //myservo.write(angleClosed);
      delay(openDelay);
    myservo.write(angleOpen);
    delay(500);
    
    stepper.setMaxSpeed(400);
    stepper.runToNewPosition(-1800);
      delay(500);
       digitalWrite(relais8, HIGH);

    //Mischgetränke
    //myservo.write(30);
    digitalWrite(relais1, LOW);
      delay(4000);
    digitalWrite(relais1, HIGH);
    delay(300);
    digitalWrite(relais2, LOW);
      delay(4000);
    digitalWrite(relais2, HIGH);
    delay(300);
    digitalWrite(relais3, LOW);
      delay(4000);
    digitalWrite(relais3, HIGH);
    delay(2000);

    while(digitalRead(endstop_2) != LOW){
    stepper.setMaxSpeed(400);
    stepper.move(10000);
    stepper.run();

     
    
    
    }
    
  }
  break;  


Mach doch mal ganz viele Serial Prints rein. Damit du sehen kannst wo du bist. Und benutze mal Strg-T um den Code zu formatieren.

1 Like

Auch wenn es bei dir noch nicht so ankommt, aber ein Hauptproblem sind die vielen delays in deinem Programm. Die blockieren den normalen Ablauf und somit ist ein sauberer, stabiler Ablauf nicht möglich.
Du solltest die delays durch eine Funktion "millis" ersetzen.
Sieh dir dazu das Beispiel "BlinkWithoutDelay" in der IDE an.

//Relais
const byte relais[] = {A0,A1,A2,A3,A4,A5,A6,A7};
...
void setup() {
  //Relais
  for (int i = 0; i < sizeof(relais); i++) {
    digitalWrite(relais[i], HIGH);
    pinMode(relais[i], OUTPUT);
  }

bitte den ganzen Code

merkst du?

wie hast du die verbunden? externe Pullups?

Nicht doch eher PollDowns bei INPUT?

Gruß Tommy

Das die vielen Delays nicht elegant sind, hab ich auch schon gelesen. Allerdings bin ich komplett neu in der Programmierung und da ich keine Sensoren oder Ähnliches, während des Delay auslesen möchte, schien mir das als eine gute Option.
Das ganze Programm hat auch schon einmal funktioniert, allerdings schein ich etwas verändert zu haben, weshalb es jetzt nicht mehr funktioniert.

Guter Typ danke. Allerdings möchte ich nachher die Relais nicht 1 und so weiter nennen , sondern diese mit den Mischgetränken deklarieren.

//Relais
const byte relais[] = {A0,A1,A2,A3,A4,A5,A6,A7};
#define MehrVodka digitalWrite(relais[0], LOW)
#define GenugVodka digitalWrite(relais[0], HIGH)

...
void setup() {
  //Relais
  for (byte i = 0; i < sizeof(relais); i++) {
    digitalWrite(relais[i], HIGH);
    pinMode(relais[i], OUTPUT);
  }
}
void loop(){
  MehrVodka;
}

Danke. Probier ich mal aus

Kleine Vereinfachung:
(nicht wirklich "besser", aber schlichter)

void setup() {
  //Relais
  for (auto r:relais) {
    digitalWrite(r, HIGH);
    pinMode(r, OUTPUT);
  }
}
1 Like

Dieser Kurzhinweis erhöht die Gefahr, dass ein Anfänger ein falsches Bild davon entwickelt wie nicht-blockierendes Timing funktioniert.
Die Gefahr besteht darin, dass man sich vorstellt ersetze delay() durch millis() und alles ist erledigt.

Wenn man sich mit diesem Bild im Kopf das Blink without delay() Beispiel anschaut, dann will der Code ums verrecken nicht zu diesem Bild passen!

Weil nicht blockierendes Timing eben fundamental anders funktioniert als delay().
Die Ursache dafür, dass das Thema millis() praktisch jede Woche neue postings "verstehe millis() nicht" produziert liegt genau darin begründet.
Das scheint aber weder die erfahrenen User zu jucken noch die Leute die die Aruino-IDE betreuen.

Erklärung des nicht-blockierenden Timings am Beispiel für Pizzabacken

als alltägliches Beispiel mit einfach zu verstehenden Zahlen
.
.
delay() ist blockierend. Solange die Verzögerung "verzögert", kann nichts anderes im Code ausgeführt werden.
delay() lässt den Mikrocontroller mit maximaler Geschwindigkeit "herumwirbeln" und belegt dabei 100% Rechenleistung

Die Funktion delay() sollte diesen Namen haben

freeze_microcontroller_completely_STOP_code_execution_until_freezingtime_is_over()
Jetzt gibt es eine Technik der nicht-blockierenden (nicht-einfrierenden) Zeitsteuerung.
Das Grundprinzip der nicht-blockierenden Zeitsteuerung unterscheidet sich grundlegend von der Verwendung von delay()

Sie müssen zuerst den Unterschied verstehen und sich dann den Code ansehen.

Andernfalls könnten Sie versuchen, im millis()-Code ein "delay-analog-thing" zu "sehen", was es in Wirklichkeit nicht ist
Der Versuch, ein "delay-analog-thing" in millis() zu sehen, macht es schwer, millis() zu verstehen
Wenn man das Grundprinzip der nicht-blockierenden Zeitsteuerung anhand von millis() verstanden hat, ist es leicht zu verstehen.

Stellen Sie sich vor, Sie backen eine Tiefkühlpizza
auf dem Deckel steht, dass man den Ofen auf 200°C vorheizen soll
dann die Pizza hineinlegen.
Backzeit 10 Minuten

Sie schätzen, dass das Aufheizen 3 Minuten dauert.
Sie schauen auf Ihre Uhr, es ist 13:02 (Momentaufnahme der Zeit)
Sie fangen an, die Zeitung zu lesen und schauen ab und zu auf Ihre Uhr
Die Uhr zeigt 13:02. 13:02 - 13:02 = 0 Minuten verstrichen, noch keine Zeit
Die Uhr zeigt 13:03. 13:03 - 13:02 = 1 Minute verstrichen noch keine Zeit
Uhr zeigt 13:04. 13:04 - 13:02 = 2 Minuten verstrichen, noch keine Zeit

Die Uhr zeigt 13:05 an, wann habe ich mit 13:02 angefangen? OK 13:05 - 13:02 = 3 Minuten Zeit, um die Pizza in den Ofen zu schieben

Neue Basiszeit 13:05 (die Momentaufnahme der Zeit)
Uhr 13:06 noch nicht Zeit (13:06 - 13:05 = 1 Minute ist weniger als 10 Minuten
Uhr 13:07 noch keine Zeit (13:07 - 13:05 = 2 Minuten sind weniger als 10 Minuten)
Uhr 13:08 noch keine Zeit (13:08 - 13:05 = 3 Minuten ist weniger als 10 Minuten
Uhr 13:09 noch nicht Zeit (13:09 - 13:05 = 4 Minuten ist weniger als 10 Minuten
Uhr 13:10 noch keine Zeit...
Uhr 13:11 noch keine Zeit
Uhr 13:12 noch keine Zeit
Uhr 13:13 noch nicht Zeit
Uhr 13:14 noch nicht Zeit (13:14 - 13:05 = 9 Minuten ist weniger als 10 Minuten
Uhr 13:15 wann habe ich angefangen 13:05 OK 13:15 - 13:05 = 10 Minuten Zeit um Pizza zu essen (yum yum)

Sie haben wiederholt verglichen, wie viel Zeit verstrichen ist
Das ist es, was die nicht-blockierende Zeitmessung macht

Im Code wird die Frage "Wie viel Zeit ist vergangen" wie folgt beantwortet

currentTime - startTime >= bakingTime (übrigens: diese if-Bedingung behandelt den Rollover von millis() 100% korrekt)

bakingTime ist 10 Minuten

13:06 - 13:05 = 1 Minute >= bakingTime ist falsch
13:07 - 13:05 = 2 Minuten >= bakingTime ist falsch
...
13:14 - 13:05 = 9 Minuten >= Backzeit ist falsch
13:15 - 13:05 = 10 Minuten >= bakingTime ist TRUE Zeit für zeitgesteuerte Aktion!!

um im Pizza-Beispiel als Pseudo-Code zu bleiben

das nicht-blockierende Timing sieht wie folgt aus

void loop() {
  readingNewsPaper();       // doing 
  answerWhatsAppMsg();      // OTHER things 
  take_A_Sip_of_Coffee();   // in PARALLEL to

  // checking if TimePeriodIsOver 
  if ( TimePeriodIsOver(myPizzaTimer,10Minutes) ) {
    // IF timeperiod IS over then execute timed action
    take_pizza_out_of_the_oven();
  }  
}

die benutzerdefinierte Funktion TimePeriodIsOver() ergibt true, wenn: selbsterklärender Name: die definierte Zeitspanne abgelaufen ist
die benutzerdefinierte Funktion TimePeriodIsOver() ergibt false, wenn: selbsterklärender Name: die definierte Zeitspanne ist noch NICHT vorbei

Sie könnten diesen Pseudocode erweitern. Sie wollen zum Beispiel eine spezielle Käsesorte auf die Pizza geben
Dieser Käse soll aber nur 3 Minuten lang leicht schmelzen.

Die gesamte Backzeit beträgt 10 Minuten. Wenn der hinzugefügte Käse nur 3 Minuten lang schmelzen soll
bedeutet dies, dass der Käse nach 7 Minuten Backzeit hinzugefügt werden muss.

Im Pseudocode würde man also schreiben

void loop() {
  readingNewsPaper(); // doing 
  answerWhatsAppMsg(); // ANDERE Dinge 
  take_a_sip_of_coffee(); // PARALLEL zu

  // Prüfung, ob TimePeriodIsOver 
  if ( TimePeriodIsOver(myPizzaTimer,10Minutes) ) {
    // WENN die Zeitspanne überschritten ist, wird eine zeitgesteuerte Aktion ausgeführt
    take_pizza_out_of_the_oven();
  }
  
  // Überprüfung, ob TimePeriodIsOver 
  if ( TimePeriodIsOver(myPizzaTimer,7Minutes) ) { // <=== siehe den Unterschied in der Zeitspanne
    // WENN die Zeitspanne überschritten ist, dann führe eine zeitgesteuerte Aktion aus
    add_cheese_to_pizza();
  }  
}

Damit ist das Grundprinzip erklärt. Es gibt noch ein paar weitere Dinge, die für eine echte Kodierung zu tun sind.
Aber es ist sehr wichtig, den Unterschied zu verstehen:

Beim non-blocking timing wird sehr oft und wiederholt überprüft, wie viel Zeit vergangen ist.
Und nur wenn die richtige Zeitspanne verstrichen ist, wird der Teil des Codes ausgeführt, der nur
von "Zeit zu Zeit" ausgeführt werden soll

alle delay()'s entfernen()
Um sehr oft überprüfen zu können, wie viel Zeit verstrichen ist

müssen alle Funktionsaufrufe delay(nnnn) entfernt werden.
Wenn Sie noch delay(nnnn) in Ihrem Code haben, ist die Verzögerung immer noch blockierend!

Solange noch delays() im Code vorhanden ist, kann die schnelle wiederholte Überprüfung nicht durchgeführt werden

1 Like

Unfug!
Es gibt ein Leben neben delay()

Und damit ist das folgende ebenfalls die Unwahrheit:

Merke:
Selbst wenn man Unsinn mit maximalem Selbstvertrauen und in Fettschrift vorbringt, bleibt es doch Unsinn.

Also hier geht es um Möglichkeiten "zurück zu schießen" weil du verschiedene Postings von mir als "aus dem Hintergrund anmeckern" empfunden hast.

Dann sei doch bitte so nett und schreibe ein Tutorial das die Ausnahmen die meine Aussage zum Unsinn machen so erklären, dass ein Anfänger sie versteht.

Am wahrscheinlichsten ist, dass du dich da auf den Standpunkt stellst "muss sich jeder selber durch entsprechend intensives Studium der Programmiersprache und der Bibliotheken selbst erarbeiten"

Macht nix. Für das Prinzip "didaktische Vereinfachung" bist du nun mal nicht zu begeistern.

So wie Du sie verstehst, muss sie nicht richtig sein. Bei Dir wird aus der Vereinfachung schnell eine Falschaussage und das darf nicht passieren.

Gruß Tommy

Dann muss ein Anwender eben tiefer einsteigen und genau lesen, was im entsprechendrn Beitrag steht.
Sollte er das nicht können oder wollen, hat er das falsche Hobby gewählt.

Da wird auch dein extrem langer Beitrag nicht helfen, eher die Anfänger abschrecken.

3 Likes

Was ist damit, dass es teilweise absolut keinen Sinn macht delay durch millis zu ersetzen?
Und zwar dann, wenn das Programm in diesem Zeitrahmen einfach nichts sinnvolles anderes machen kann.
In meinem Steuerprogramm für Wintergarten und Pool arbeite ich mit millis, weil das Programm in den Wartezeiten seine anderen Aufgaben noch erfüllen muss und kann und die ständigen Durchläufe damit auch Sinn machen.
Andererseits benutze ich delay dort, wo ein Programm eben warten muss und im Prinzip eben auch nichts blockiert werden kann.

cu

Grundsätzlich darf das jeder für sich entscheiden, wie und wodurch sein Programm blockiert wird, oder eben nicht.
Nur wenn hier "Hilferufe" aufschlagen und das Programm total unübersichtlich und übersät mit delays geschrieben wurde, dann gibt es eben von mir die Empfehlung, diese delays möglichst zu ersetzen.
Eine Entscheidung, ob es gemacht wird, muss der Fragesteller selber treffen.