Gearshifter

Guten Abend miteinander,

ich bin derzeit dabei für ein Projekt im Rahmen des Studiums einen Arduino zu programmieren.
Ich habe derzeit noch nicht wirklich viel Ahnung davon, ich bitte mich daher für dumme Fragen zu entschuldigen.
Das System besteht aus 2 Magnetventilen und einem doppeltwirkenenden Pneumatikzylinder. Mit dem Pneumatikzylinder soll das Getriebe eines Motorradmotors geschaltet werden.
Das System an sich funktioniert soweit, allerdings möchte ich, dass beim Drücken eines Buttons (Hoch-/oder Runterschalten) das Ventil nur für eine bestimme Zeit angesteuert wird, auch wenn der Button gedrückt bleibt. Das soll verhindern, dass der Schaltvorgang durch den Fahrer in irgendeiner Weise verlangsamt wird.
Nun meine Frage: Wie mache ich das?
Der Code lautet:

int solenoidPinup = 4; //upshift
int switchPinup = 7;
int switchStateup = 0;
int solenoidPindo = 8; //downshift
int switchPindo = 9;
int switchStatedo = 0;


void setup() {
// put your setup code here, to run once:
pinMode(solenoidPinup, OUTPUT);
pinMode(solenoidPindo, OUTPUT);
pinMode(switchPinup, INPUT);
pinMode(switchPindo, INPUT);
}

void loop() {
// put your main code here, to run repeatedly:
switchStateup = digitalRead(switchPinup);
switchStatedo = digitalRead(switchPindo);

if (switchStateup == HIGH) {
 digitalWrite(solenoidPinup, HIGH);
 
}

else if (switchStatedo == HIGH) {
 digitalWrite(solenoidPindo, HIGH);
}

else {
 
 digitalWrite(solenoidPinup, LOW);
 digitalWrite(solenoidPindo, LOW);
}

}

Schreibe ich nun beispielweise

if (switchStateup == HIGH) {
 digitalWrite(solenoidPinup, HIGH);
 delay(50);
 digitalWrite(solneoidPinup, LOW);
}

mag es nicht so richtig funktionieren.
Vielleicht kann mir jemand weiterhelfen, danke!
Den Schaltplan habe ich auch nochmals in den Anhang gemacht.

Grüße,
Juless

Setze Deinen Sketch bitte in Codetags (</> Button oben links im Forumseditor).

Gruß Tommy

delay(50);

Ich empfehle dir die konsequente Anwendung des "BinkWithoutDelay" Prinzips.

Dazu den einfachen endlichen Automaten.

Juless92:
… Wie mache ich das? …

Wie Combie schon schrub, realisierst Du Dein Vorhaben am besten als „endlichen Automat“. Was mir hierzu eingefallen ist, habe ich hier ins Netz gekippt. Beachte ggf. auch die Folgeseite.

Gruß

Gregor

Die Flanke des Tasters detektieren (je nachdem wie er angeschlossen ist ein L-H Übergang oder ein H-L Übergang) und dann den Elektromagneten für die gewollte Zeit ansteuern.
Siehe Arduino - Switch

Grüße Uwe

Hallo,

es erfordert einen entprellten Taster, damit wird ein Flag gesetzt, welches dafür sorgt das ein Zylinder angesteuert wird und gleichzeitig die Zeit dafür läuft. Der Taster darf während die Zeit läuft nicht nochmal abgefragt werden bzw. das Flag darf nicht weiterhin ausgewertet werden. Am Ende der Zeit Flag zurücksetzen und Zylinder abschalten.
Wie schon erwähnt führt dich ein Zustandsautomat sicher zum Ziel. Stichwort switch-case , enum und millis.
Das entprellen kann man nur weglassen wenn der Taster kürzer prellt wie der Zylinder angesteuert wird.
Anders gesagt, wenn du bei den Schaltzeiten den heutigen Automatikgetrieben Konkurrenz machen möchtest mußte entprellen.
Wären so meine Gedanken zum Thema.

Vielen Dank für die vielen schnellen Antworten!
Ich werde mir die genannten Lösungen mal anschauen und mich bei Fragen melden.

Liebe Grüße

Juless92:
... im Rahmen des Studiums ...

Dann braucht es auch akademisches Niveau!

Konstanten sollten als solche deklariert werden und der Typ sollte stimmen:

const byte solenoidPinup = 4; //upshift
const byte switchPinup = 7;
bool switchStateup = 0;
const byte solenoidPindo = 8; //downshift
const byte switchPindo = 9;
bool switchStatedo = 0;

Mit R2-R3 machst Du bei HIGH einen Spannungsteiler. Je nach Transistor wird es funktionieren. Sauberer wären volle 5V am Gate, in dem Du R2 nach rechts verschiebst, ebenso R5.

Juless92:
Das System an sich funktioniert soweit, allerdings möchte ich, dass beim Drücken eines Buttons (Hoch-/oder Runterschalten) das Ventil nur für eine bestimme Zeit angesteuert wird, auch wenn der Button gedrückt bleibt.

Du möchtest eine monostabile Kippstufe.

Danke agmue für die weitere Antwort!

Ich habe mich jetzt am endlichen Automaten versucht, bin aber leider gescheitert :-[

Mein letzter Testsketch sah wie folgend aus:

const byte solenoidPinup = 4; //upshift
const byte switchPinup = 7;
bool switchStateup = 0;
const byte solenoidPindo = 8; //downshift
const byte switchPindo = 9;
bool switchStatedo = 0;
const int OPEN = 50;
unsigned long upMillis;
unsigned long downMillis;
enum Phase {UP, DOWN, INTERMEDIATE};
byte phase = INTERMEDIATE;


void setup() {
  // put your setup code here, to run once:
  pinMode(solenoidPinup, OUTPUT);
  pinMode(solenoidPindo, OUTPUT);
  pinMode(switchPinup, INPUT);
  pinMode(switchPindo, INPUT);
  phase = INTERMEDIATE;
}

void loop() {
  // put your main code here, to run repeatedly:
  switchStateup = digitalRead(switchPinup);
  switchStatedo = digitalRead(switchPindo);

  if (switchStateup == HIGH) {
    switch (phase) {{
      case INTERMEDIATE:
      phase = UP;
      upMillis = millis();
    }
    break;
    case UP: 
    if (millis()-upMillis <= OPEN){
      digitalWrite(solenoidPinup, HIGH);
    }
    break;
 
  }
  }

  else if (switchStatedo == HIGH) {
    switch (phase) {{
      case INTERMEDIATE:
      phase = DOWN;
      downMillis = millis();
    }
    break;
    case DOWN:
    if (millis()-downMillis <= OPEN){
      digitalWrite(solenoidPindo, HIGH);
     }
     break;
    
   }
   }
  
  
  else {
     digitalWrite(solenoidPinup, LOW);
     digitalWrite(solenoidPindo, LOW);
   }
  }

Ich kann nun entweder den Button für Up 1x drücken und das Ventil schaltet beim gedrückt halten auch nicht zurück oder den Button für Down 1x. Danach geht gar nichts mehr.
Vielleicht kann mir jemand weiter helfen und Sorry für meine Planlosigkeit.

Viele Grüße
Juless

Das geht in die richtige Richtung :slight_smile:

Wenn die Formatierung der IDE Dir

  }
  }

anzeigt, dann stimmt was nicht. In diesem Fall die Syntax von switch.

Im nächsten Schritt möchtest Du nur die Änderung eines Eingangs auswerten, also die Flanke. Aus

if (switchStatedo == HIGH)

wird dann

if (altSwitchStatedo == LOW && aktuellSwitchStatedo == HIGH)

oder kürzer

if (!altSwitchStatedo && aktuellSwitchStatedo)

Bei prellenden Tastern benötigst Du noch eine Entprellung, indem Du dies nur alle 30 ms ausführst:

  switchStateup = digitalRead(switchPinup);
  switchStatedo = digitalRead(switchPindo);

Okay und hier der nächste Versuch:

const byte solenoidPinup = 4; //upshift
const byte switchPinup = 7;
bool aktuellswitchStateup = 0;
bool altswitchStateup = 0;
const byte solenoidPindo = 8; //downshift
const byte switchPindo = 9;
bool aktuellswitchStatedo = 0;
bool altswitchStatedo = 0;
const int OPEN = 50;
unsigned long upMillis;
unsigned long downMillis;
unsigned long debounce = 30;
unsigned long pushMillis;
enum Phase {UP, DOWN, INTERMEDIATE};
byte phase = INTERMEDIATE;


void setup() {
  // put your setup code here, to run once:
  pinMode(solenoidPinup, OUTPUT);
  pinMode(solenoidPindo, OUTPUT);
  pinMode(switchPinup, INPUT);
  pinMode(switchPindo, INPUT);
  phase = INTERMEDIATE;
}

void loop() {
  // put your main code here, to run repeatedly:
  if (millis() - pushMillis > debounce) {
  aktuellswitchStateup = digitalRead(switchPinup);
  aktuellswitchStatedo = digitalRead(switchPindo);
  pushMillis = millis();
  }
  

  if (!altswitchStateup && aktuellswitchStateup) {
    switch (phase) {{
      case INTERMEDIATE:
      phase = UP;
      upMillis = millis();
    }
    break;
    case UP: 
    if (millis()-upMillis <= OPEN){
      digitalWrite(solenoidPinup, HIGH);
    }
    break;
 
  }
 }

  else if (!altswitchStatedo && aktuellswitchStatedo) {
    switch (phase) {{
      case INTERMEDIATE:
      phase = DOWN;
      downMillis = millis();
    }
    break;
    case DOWN:
    if (millis()-downMillis <= OPEN){
      digitalWrite(solenoidPindo, HIGH);
     }
     break;
    
   }
  
 }
  
  
  else {
     digitalWrite(solenoidPinup, LOW);
     digitalWrite(solenoidPindo, LOW);
   }
   altswitchStateup = aktuellswitchStateup;
   altswitchStatedo = aktuellswitchStatedo;
  }

Nun passiert gar nichts mehr, egal welchen Knopf ich drücke.
Was mache ich falsch?

Viele Grüße

Schau Dir mal die {} im switch/case an.

Gruß Tommy

Juless92:
... Was mache ich falsch?

Wenn ich Code debugge, fange ich immer damit an, den Code in eine gut lesbare Form zu bringen. Meistens springen einem dann die Fehler geradezu ins Auge. Damit meine ich jetzt auch (aber nicht nur) solche Klammergeschichten wie sie Tommy gerade angemerkt hat.

Gruß

Gregor

Tommy56:
Schau Dir mal die {} im switch/case an.

Gruß Tommy

Ist es so richtig?

const byte solenoidPinup = 4; //upshift
const byte switchPinup = 7;
bool aktuellswitchStateup = 0;
bool altswitchStateup = 0;
const byte solenoidPindo = 8; //downshift
const byte switchPindo = 9;
bool aktuellswitchStatedo = 0;
bool altswitchStatedo = 0;
const int OPEN = 50;
unsigned long upMillis;
unsigned long downMillis;
unsigned long debounce = 30;
unsigned long pushMillis;
enum Phase {UP, DOWN, INTERMEDIATE};
byte phase = INTERMEDIATE;


void setup() {
  // put your setup code here, to run once:
  pinMode(solenoidPinup, OUTPUT);
  pinMode(solenoidPindo, OUTPUT);
  pinMode(switchPinup, INPUT);
  pinMode(switchPindo, INPUT);
  phase = INTERMEDIATE;
}

void loop() {
  // put your main code here, to run repeatedly:
  if (millis() - pushMillis > debounce) {
  aktuellswitchStateup = digitalRead(switchPinup);
  aktuellswitchStatedo = digitalRead(switchPindo);
  pushMillis = millis();
  }
  

  if (!altswitchStateup && aktuellswitchStateup) {
    switch (phase) {
      case INTERMEDIATE:
        phase = UP;
        upMillis = millis();
        break;
      case UP: 
        if (millis()-upMillis <= OPEN){
        digitalWrite(solenoidPinup, HIGH);
        }
      break;
   }
  }

  else if (!altswitchStatedo && aktuellswitchStatedo) {
    switch (phase) {
      case INTERMEDIATE:
        phase = DOWN;
        downMillis = millis();
        break;
      case DOWN:
        if (millis()-downMillis <= OPEN){
        digitalWrite(solenoidPindo, HIGH);
        }
      break;   
   } 
  }
  
  else {
     digitalWrite(solenoidPinup, LOW);
     digitalWrite(solenoidPindo, LOW);
     }
   altswitchStateup = aktuellswitchStateup;
   altswitchStatedo = aktuellswitchStatedo;
}

Allerdings funktionierts es immer noch nicht. Bin wohl eher schlecht im Debugging :-\

Hallo,

die Wartezeit OPEN oder was das sein soll muss unsigned sein. Also entweder unsigned int/long oder hier auch byte möglich.

Ansonsten verschachtelst du if/if else mit 2x switch case kompliziert ineinander was zum scheitern führt.
Der Zustand wechselt zum Bsp. auch nur zwischen UP und DOWN.
Eine zweite phase Zuweisung in setup ist überflüssig.

Male dir einen Ablaufplan bis der schlüssig ist. Dann kannste das programmieren.
Doppelte switch case mit gleichen Ausdruck sind auch tödlich.

Edit:
Was ich mich gerade frage ist, benötigt man dafür nicht 2 Zylinder. Je einen für hoch- und runterschalten. Wie macht ihr das mit einem Zylinder? Sonst schaltet man ja unweigerlich wieder runter wenn man vorher hochgeschalten hat. Da komme ich gerade nicht mit. Pneumatikzylinder – FestoWiki - deutsch
Außer ihr habt einen doppelten Zylinder der nach beiden Seiten einfach wirkt, also mit 2 Federn. Der demnach ohne Druckluft in seine Mittelstellung zurückkehrt. Wären jetzt so meine Gedanken.

Danke, ich schaue mal ob ich das besser hinbekomme.
Der Zylinder ist ein doppeltwirkender Zylinder. Dieser wird durch eine Feder in der Mittelstellung (Intermediate) gehalten. Von dieser Stellung aus kann der Zylinder mit 2 Ventilen gesteuert einen positiven Hub oder aber einen negativen Hub von der Hälfte des Gesamthubes verrichten und wird danach durch die Feder wieder in Mittelstellung gedrückt, sobald die Luft entweicht ist.
Mein Zylinder beispielweise hat einen Hub von 40mm und kann dementsprechend einen Hub von +/- 20mm verrichten.

Edit:
Kann ich es nicht auf ganz einfach ohne Switch case machen?

const byte solenoidPinup = 4; //upshift
const byte switchPinup = 7;
bool aktuellswitchStateup = 0;
bool altswitchStateup = 0;
const byte solenoidPindo = 8; //downshift
const byte switchPindo = 9;
bool aktuellswitchStatedo = 0;
bool altswitchStatedo = 0;
unsigned long OPEN = 50;
unsigned long upMillis;
unsigned long downMillis;
unsigned long debounce = 30;
unsigned long pushMillis;


void setup() {
  // put your setup code here, to run once:
  pinMode(solenoidPinup, OUTPUT);
  pinMode(solenoidPindo, OUTPUT);
  pinMode(switchPinup, INPUT);
  pinMode(switchPindo, INPUT);
  
}

void loop() {
  // put your main code here, to run repeatedly:
  if (millis() - pushMillis > debounce) {
  aktuellswitchStateup = digitalRead(switchPinup);
  aktuellswitchStatedo = digitalRead(switchPindo);
  pushMillis = millis();
  }
  

  if (!altswitchStateup && aktuellswitchStateup) {
      digitalWrite(solenoidPinup, HIGH);
      upMillis = millis(); 
      
        if (millis()-upMillis >= OPEN){
          digitalWrite(solenoidPinup, LOW);
         }
  }

  else if (!altswitchStatedo && aktuellswitchStatedo) {
       digitalWrite(solenoidPindo, HIGH);
       downMillis = millis(); 
      
        if (millis()-downMillis >= OPEN){
          digitalWrite(solenoidPindo, LOW);
         }
   } 
  
  
  else {
     digitalWrite(solenoidPinup, LOW);
     digitalWrite(solenoidPindo, LOW);
     }
   altswitchStateup = aktuellswitchStateup;
   altswitchStatedo = aktuellswitchStatedo;
}

Juless92:
Kann ich es nicht auf ganz einfach ohne Switch case machen?

Ein endlicher Automat läßt sich auf verschiedene Arten realisieren. Die von mir favorisierte switch/case-Variante wird von manchen OOP-Anhängern als gruselig betrachtet. Ich habe anfangs mit verschachtelten if/else jongliert und empfand den Schritt zur bewußten Verwendung eines endlichen Automaten mit switch/case als Erlösung. Das ist meine ganz persönliche Sicht, die ich versuche, an nach Hilfe Suchenden weiterzugeben. Aber Programmieren ist eine kreative Tätigkeit und jeder muß den für ihn besten Weg herausfinden.

  if (!altswitchStateup && aktuellswitchStateup) {
      digitalWrite(solenoidPinup, HIGH);
      upMillis = millis();
     
        if (millis()-upMillis >= OPEN){
          digitalWrite(solenoidPinup, LOW);  // diese Zeile wird nie erreicht!
         }
  }

Mir gefällt #13 besser, daraus dieser Vorschlag:

  switch (phase) {
    case INTERMEDIATE:
      if (!altswitchStateup && aktuellswitchStateup) {
        digitalWrite(solenoidPinup, HIGH);
        phase = UP;
        upMillis = millis();
      }
      break;
    case UP:
      if (millis() - upMillis <= OPEN) {
        digitalWrite(solenoidPinup, LOW);
        phase = INTERMEDIATE;
      }
      break;
  }

Also mit der Flanke zum nächsten Zustand weiterschalten.

Hallo,

aha, Zylinder > Begriffsmissverständnis.

Erstelle dir einen Ablaufplan, sonst stocherst du nur wild im Code rum.

Allgmein, egal ob mit if oder switch case, macht es konsequent.
In switch case gibts folgende Zustände, IDLE, UP, DOWN, WAIT
Die Tasterabfrage, nicht die Entprellung, kann man zum Bsp. in IDLE (Ruhelage) einbauen, dann kann sich auch niemand verschalten. Wenn man das aufmalt wäre der Code schon fertig.

agmue:
Die von mir favorisierte switch/case-Variante wird von manchen OOP-Anhängern als gruselig betrachtet.

Mit OOP hat das erst mal nichts zu tun. Es gibt noch ein paar Varianten von Zustandsautomaten bevor man bei Objekten landet. So kann man Funktionszeiger verwendet um lange switch/case Konstrukte in kleinere Elemente aufzubrechen

Hallo,

ich hätte noch eine Gedankenergänzung für Juless. Wenn du dir den Ablaufplan erstellst, sollten weitere Zustände zum Vorschein kommen die überdacht werden sollten. Darf der Fahrer zum Bsp. Dauerfeuer betreiben? Finger bleibt auf dem Hochschalten-Taster zum Bsp. Was soll dann der Zylinder machen? Benötigt der Zylinder in Mittelstellung auch eine kurze Pause? Die Bewegung des Zylinders egal in welche Richtung benötigt Zeit. Darf sofort in die Gegenrichtung geschalten werden? Zu Ende Gedacht würde der Zylinder noch Positionssenoren benötigen. Ob er auch wirklich die Endlagen und Ruhestellung erreicht hat. Momentan verlässt du dich nur darauf das der Zylinder die Bewegung macht. Um dahin zu kommen sollten das Gedanken sein für das fertige Projekt. Aktuell aber erstmal das umsetzen wie es zur Zeit gedacht ist. Sollte leichter fallen.