Servosteuerung mit einem Taster

Hallo zusammen

Ich habe eine Servosteuerung mit einem Taster geschrieben. Der Taster zählt, ob 1x oder 2x gedrückt wurde und stellt dann bei erneutem gedrückt behaltenem Taster den entsprechenden Servo 1 oder Servo 2. Während 4 Sekunden kann man erneut den Taster gedrückt behalten, um den Servo zu verstellen. Nach 4 Sekunden muss man dann erneut per Tastendruck den Servo wählen.

Jetzt habe ich aber das Problem, dass wenn ich Servo 1 steuere, der "servoState" entsprechend geändert wird, damit der Servo während den 4 Sekunden entweder auf oder ab bewegt wird.
Möchte ich dann den Servo 2 bewegen, kommt es vor, dass durch den falschen Wert bei servoState keine Steuerung mehr möglich ist, bis ich den Servo 1 wieder zurück bewegt habe.
Ich sehe momentan nicht, wie ich die zwei servoState unabhängig einbringen kann, damit beide Servos unabhängig voneinander bewegt werden können, also auch wenn Servo 1 bereits bewegt wurde, den Servo 2 trotzdem bewegt werden kann ohne den Servo 1 wieder zurück bewegt werden muss.

#include <Servo.h>

const byte UABut = 4;
const byte servoPin = 2;
const byte servo2Pin = 3;

byte lastUAButState = 0;
byte servoState = 0;
byte servoPos = 0;
byte servo2Pos = 0;
byte UAButCounter = 0;
byte lastUAButCounter = 0;
unsigned long UAButTimer = 0;
bool UAButReset = true;

Servo servo;
Servo servo2;

void setup() {
  pinMode(UABut, INPUT_PULLUP);
  servo.attach(servoPin);
  servo2.attach(servo2Pin);
  servo.write(0);
  servo2.write(0);

}

void loop() {

  unsigned long currentMillis = millis();
  
  if(digitalRead(UABut) == LOW && UAButReset == true){
    UAButCounter = 1 + lastUAButCounter;
    lastUAButCounter = UAButCounter;
    UAButTimer = millis();
    delay(50);
  }

  if(currentMillis - UAButTimer >= 500 && currentMillis - UAButTimer < 4000){
    if(digitalRead(UABut) == HIGH && lastUAButState == LOW){
      servoState = 1 - servoState;
      lastUAButState = HIGH;
      UAButTimer = millis();
      delay(50);
    }
    while(digitalRead(UABut) == LOW && servoState == 1){
      if(servoPos < 90 && UAButCounter == 1){
        servoPos ++;
        servo.write(servoPos);
        lastUAButState = LOW;
        delay(10);
      }
      else if(servo2Pos < 90 && UAButCounter == 2){
        servo2Pos ++;
        servo2.write(servo2Pos);
        lastUAButState = LOW;
        delay(10);
      }
    }
    while(digitalRead(UABut) == LOW && servoState == 0){
      if(servoPos > 0 && UAButCounter == 1){
        servoPos --;
        servo.write(servoPos);
        lastUAButState = LOW;
        delay(10);
      }
      else if(servo2Pos > 0 && UAButCounter == 2){
        servo2Pos --;
        servo2.write(servo2Pos);
        lastUAButState = LOW;
        delay(10);
      }
    }
    UAButReset = false;
  }
  else if(currentMillis - UAButTimer >= 4000 && UAButReset == false){
    UAButCounter = 0;
    lastUAButCounter = 0;
    UAButReset = true;
  }

}

Ich kann es nicht besser beschreiben, man muss die Steuerung sonst mal kurz testen, dann sieht man sehr schnell, dass es manchmal klemmt und man den andern Servo erst wieder bewegen kann, sobald der vorherige Servo nochmals bewegt wird.

Kann mir hier jemand helfen wie ich die Zustände ändern muss?

Grüsse

Stef

Nicht wirklich, da ich nicht weiß, was es tun soll.

Aber du scheinst mir ein Freund von verschachtelten Schleifen, IF-Konstrukten und komplizierten Bedingungen zu sein.
Nicht, dass das unbedingt falsch ist, aber eben sehr unübersichtlich, ja, quasi verworren.
So komplex, dass du dich darin scheinbar selber verirrt hast.

Ich rate dir davon ab.
Ich rate zu flachen Programmen.

Du brauchst für jeden servo den Merker

bzw. last...

Ich hab nur noch nicht verstanden wie das auswahlprozedere läuft.
Kannst Du das mal mit Worten schreiben, was wie passieren soll?

Verschachtelte IF Funktionen nicht, ich habe nur aufgrund der Abfolge und Zeitsteuerung mit ein paar IF Abfragen geschrieben. Wenn es einfacher geht, dann bin ich sehr gerne bereit dies zu versuchen.

Die Funktion nochmals versucht zu erklären:

Ich habe zwei Servos und einen Taster. Der Taster soll als Auswahl des Servos 1 oder Servos 2 dienen. Sobald dieser dann gewählt wurde, kann man mit demselben Taster den Servo auf und ab bewegen, indem man den Taster gedrückt hält. Wird innerhalb der 4 Sekunden kein Taster mehr betätigt, stellt sich der Zähler der Servowahl wieder auf 0 zurück für eine erneute Auswahl.

Das funktioniert auch soweit, bis auf wenn man einen der beiden Servos bewegt hat, und den andern Servo bewegen möchte, dann kommt es vor, dass aufgrund der Variablen servoState und der Bedingungen wenn servoState = 1 oder = 0 und die servoPos > 0 oder < 90, dass beide Bedingungen falsch sind und der vorher gewählte Servo den man bewegt hatte nochmals bewegen muss um den servoState wieder zu ändern, damit es mit dem andern Servo wieder geht.

Versteht man dies nun besser?

Wenn man einen Servo bewegt hat, muss man also 4 sec warten. Danach wird der nächste Tastendruck als Servo-Auswahl interpretiert. Richtig?

Mit 2 Tastern , die entweder als Auswahl 1/2 oder als +/- interpretiert werden, könnte ich mir es halbwegs vorstellen, aber mit nur 1 Taster ???

Habe es nicht getestet, weil ich unterwegs bin, aber vielleicht so?

#include <Servo.h>

const byte UABut = 4;
const byte servoPin = 2;
const byte servo2Pin = 3;

byte lastUAButState = 0;
byte servoState = 0;
byte servo2State = 0;
byte servoPos = 0;
byte servo2Pos = 0;
byte UAButCounter = 0;
byte lastUAButCounter = 0;
unsigned long UAButTimer = 0;
bool UAButReset = true;

Servo servo;
Servo servo2;

void setup() {
  pinMode(UABut, INPUT_PULLUP);
  servo.attach(servoPin);
  servo2.attach(servo2Pin);
  servo.write(0);
  servo2.write(0);

}

void loop() {

  unsigned long currentMillis = millis();
  
  if(digitalRead(UABut) == LOW && UAButReset == true){
    UAButCounter = 1 + lastUAButCounter;
    lastUAButCounter = UAButCounter;
    UAButTimer = millis();
    delay(50);
  }

  if(currentMillis - UAButTimer >= 500 && currentMillis - UAButTimer < 4000){
    if(digitalRead(UABut) == HIGH && lastUAButState == LOW){
      if(UAButCounter == 1){
        servoState = 1 - servoState;
      }
      if(UAButCounter == 2){
        servo2State = 1 - servo2State;
      }
      lastUAButState = HIGH;
      UAButTimer = millis();
      delay(50);
    }
    while(digitalRead(UABut) == LOW && UAButCounter == 1 && servoState == 1){
      if(servoPos < 90){
        servoPos ++;
        servo.write(servoPos);
        lastUAButState = LOW;
        delay(10);
      }
    }
    while(digitalRead(UABut) == LOW && UAButCounter == 2 && servo2State == 1){
       if(servo2Pos < 90){
        servo2Pos ++;
        servo2.write(servo2Pos);
        lastUAButState = LOW;
        delay(10);
      }
    }
    while(digitalRead(UABut) == LOW && UAButCounter == 1 && servoState == 0){
      if(servoPos > 0){
        servoPos --;
        servo.write(servoPos);
        lastUAButState = LOW;
        delay(10);
      }
    }
    while(digitalRead(UABut) == LOW && UAButCounter == 2 && servo2State == 0){
      if(servo2Pos > 0){
        servo2Pos --;
        servo2.write(servo2Pos);
        lastUAButState = LOW;
        delay(10);
      }
    }
    UAButReset = false;
  }
  else if(currentMillis - UAButTimer >= 4000 && UAButReset == false){
    UAButCounter = 0;
    lastUAButCounter = 0;
    UAButReset = true;
  }

}

Genau, nach 4 Sekunden geht der Counter der die Servoauswahl zählt auf null zurück. Innerhalb der 4 Sekunden und nach 500 Millisekunden wo die Auswahl erfolgen muss, kann dann der gewählte Servo mit gedrücktem Taster gestellt werden.

Klar benannte endliche Automaten bauen.

Und nein, ich habe noch nicht verstanden, was es tun soll.

Hallo stef308

Wofür ist der/die/das Sketch im echten Leben gedacht?

Hallo,
noch mal langsam zum mitdenken.
Vermutlich ist bei Deiner Logik was nicht eindeutig.

  1. Der Taster wird 4s nicht gedrückt , damit ist der Betriebsmodus "auswählen" aktiv. Dann soll mit dem Taster der Servo ausgewählt werden. Dazu gibt es einen Status 0,1,2 . Dazu darf eine Wartezeit zwischen 2 mal drücken nicht länger als z.B 500 ms sein. Falls doch wechselt der Betrieb in den Verstellmodus, und ich denke da liegt Dein Problem

  2. Im Verstellmodus soll mit dem Taster der aktive Servo verfahren werden solange wie der Taster gedrückt ist. Lässt man den Taster los fährt der Servo zurück in 0 Pos. Das kann man so oft mit dem aktuellen Servo machen wie man will, auch über die 4 s hinaus.

  3. Wird der Taster länger als 4s nicht gedrückt, dann wechselt der Status zu 0 und es beginnt wieder mit 1.

Das könnte auf eine Schrittkette hinaus laufen.

Wenn Du jetzt die Teilaufgabe einzeln angehst solltest Du zu einer Lösung kommen.

Nachtrag
while ganz doof , es geht auch ohne, und das viel besser
beide Servos fahren nur im Status 0 zurück.

Hallo
just for fun :slight_smile:

wie funktioniert das mit einem Taster?
schwenkt der Servo selbständig von 0 - 180 - 0 hin und her und du musst den den Taster loslassen um in der Stellung zu verharren?

Wenn nicht beschreibe den Vorgang.
Besser - Zeichne ein Diagramm.

Ich habe einen Taster und zwei Servos die am Arduino angeschlossen sind. Taster mit GND und D4, Servos an D2 und D3, GND an GND und 5V direkt an der Stromversorgung per Trafo (5V, 10A) wo auch der Arduino angeschlossen ist.

Der Taster kann innerhalb 500ms per Tastendruck den Servo1 oder Servo2 wählen.
Wird nun mit einmaligem Drücken der Servo1 gewählt, dann kann innerhalb der nächsten 4s mit gedrücktem Taster der Servo1 in eine Richtung gestellt werden. Wird der Taster losgelassen, stoppt der Servo1 in der entsprechend gestellten Stellung. Sind die 4s noch nicht um und der Taster wird nochmals gedrückt gehalten, stellt sich Servo1 wieder zurück in Richting Ausgangsposition. Sobald hier der Taster losgelassen wird bevor der Servo in der Ausgangsposition ist, bleibt er wieder stehen in der aktuellen Stellung.
Das selbe kann mit dem Servo2 gemacht werden, wenn der Taster zu Beginn innerhalb der 500ms zweimal gedrückt wird.
Der Reset der Anzahl gedrückten Tasten für die Auswahl erfolgt dann nach den 4s.

Als endlicher Automaten müsste es per Switch/Case erfolgen, oder? Ich sehe hier aber keinen Lösungsansatz mit viel weniger IF Abfragen. Die IF innerhalb der While Schleifen bleiben trotzdem, und die erste IF Abfrage für die Tastendrücke bleibt doch auch. Dann macht es keinen grossen Unterschied mehr oder?

taste gedrückt
-> Zeit merken

taste losgelassen
-> Servo 1 ist ausgewählt

solange aktuelle Zeit - gemerkte Zeit < 500ms
taste gedrückt
taste losgelassen
Servo++
Servo > Anzahl Servos -> Servo 1 auswählen 

Wenn
aktuelle Zeit - gemerkete Zeit > 500ms UND 
Taste losgelassen
gehts hier weiter

Solange
aktuelle Zeit - gemerkete Zeit < 4500ms


Taste gedrückt
-> Servo Bewegung aufwärts
Taste nochmal gedrückt
-> Servo Bewegung abwärts

ggfls. auch erneut:
Taste gedrückt
-> Servo Bewegung aufwärts
Taste nochmal gedrückt
-> Servo Bewegung abwärts

aktuelle Zeit - gemerkete Zeit > 4500ms UND
Taste losgelassen
-> Fange an von vorn

Da Du nicht wiesst, wo sich die Servos befinden, musst Du die Position so wie von mir beschrieben in einer zum Servo passenden Varaiblen speichern.
Im Moment hast Du nur eine Varaiable und versuchst die für beide zu benutzen.
Das geht so nciht.

Ich habe in Post #6 versucht die beiden Variablen für den Zustand der Servostellung für Servo1 resp Servo2 zu trennen anhand des Counters mit dem ich die Tastendrücke zähle:

if(currentMillis - UAButTimer >= 500 && currentMillis - UAButTimer < 4000){
    if(digitalRead(UABut) == HIGH && lastUAButState == LOW){
      if(UAButCounter == 1){
        servoState = 1 - servoState;
      }
      if(UAButCounter == 2){
        servo2State = 1 - servo2State;
      }
      lastUAButState = HIGH;
      UAButTimer = millis();
      delay(50);
    }
......
}

Irgendwie ändert er so gar keinen servoState resp. servo2State mehr. Wieso ändert sich mit diesen zwei IF Bedingungen der servoState nicht mehr? Der UAButCounter hat er da ha bereits mit 1 oder 2 anhand der Tastendrücke definiert.

Mein Ansatz - rein aus dem Stehgreif. ungetestet und ohne Hardware.
kompiliert Fehler- und Warnungsfrei.
Egentlich erklärend kommentiert.
Vielleicht findet sich ja später jemand, der Dir hilft, wenn der nicht so läuft.
Ich hab heut blos keine Zeit....

// Forensketch
// https://forum.arduino.cc/

#include <Streaming.h>     // https://github.com/janelia-arduino/Streaming
#include "Servo.h"

//#define DEBUG              // Wenn aktiviert, werden Zwischenwerte ausgegeben

#ifdef DEBUG
  #define DBG_PRINTLN(...) Serial << __VA_ARGS__ << endl
#else
  #define DBG_PRINTLN(...)
#endif

Servo servoOne;
Servo servoTwo;
constexpr byte servos {2};
Servo myServo[servos] {servoOne, servoTwo};

constexpr byte tastePin {4};


uint8_t servoAuswahl;
uint8_t servoPos[servos] = {0, 0};
bool servoRichtung = false;
bool lastRichtung = false;   // Merker

uint32_t ausloesezeit;
constexpr uint32_t debounce {40};
constexpr uint32_t tasterReaktion {500};
constexpr uint32_t servoReaktion {4000};

enum class STATE {warten, auswahl, position};
STATE state = STATE::warten;

void setup()
{
  Serial.begin(115200);
#if (defined(ARDUINO_AVR_MEGA) || defined(ARDUINO_AVR_MEGA2650)) // https://github.com/arduino/Arduino/issues/10764
  delay(300);
#endif
  Serial << (F("\r\nStart...\r\n")) << endl;
  DBG_PRINTLN(__FILE__);
  DBG_PRINTLN( __TIMESTAMP__);
  pinMode(tastePin, INPUT_PULLUP);
  myServo[0].attach(2);
  myServo[1].attach(3);
}

void  loop()
{
  switch (state)
  {
    case STATE::warten:
      if (tasteCheck())                               // Taste ausgelöst
      {
        ausloesezeit = millis();                      // zeit merken
        servoAuswahl = 0;                             // ersten Servo einstellen
        state = STATE::auswahl;                       // nächster Status
      }
      break;
    case STATE::auswahl:
      if (millis() - ausloesezeit < tasterReaktion)   // Innerhalb der Reaktionszeit
      {
        if (tasteCheck())                             // Taste gedrückt
        { servoAuswahl++; }                           // nächsten Servo setzen
        if (servoAuswahl >= servos)                   // Begrenzung
        { servoAuswahl = 0; }                         // von vorn anfangen
      }
      else                                            // Zeit abgelaufen
      {
        if (!tasteCheck())                            // Taste losgelassen -> kann evtl. entfallen
        {
          servoRichtung = HIGH;                       // Vorbelegen
          lastRichtung = LOW;
          ausloesezeit = millis();
          state = STATE::position;                    // nächsten Status
        }
      }
      break;
    case STATE::position:
      if (!digitalRead(tastePin))  // Taste gedrückt
      {
        lastRichtung = servoRichtung;
        if (servoRichtung && servoPos[servoAuswahl] < 255)  // Aufwärts und unter oberer Grenze
        { servoPos[servoAuswahl]++; }
        else if (!servoRichtung && servoPos[servoAuswahl] > 0)
        { servoPos[servoAuswahl]--; }
      }
      else if (lastRichtung == servoRichtung)  // Taste losgelassen UND vorher gedrückt
      {
        servoRichtung = !servoRichtung;
      }
      if (millis() - ausloesezeit > servoReaktion)         // Zeit abgelaufen
      { state = STATE::warten; }                             // fange an von vorn
      break;
  }
}

bool tasteCheck()
{
  bool ret = false;
  static uint32_t lastPressed = 0;
  static bool lastState = LOW;
  if (!digitalRead(tastePin))                         // Taste gedrückt
  {
    if (!lastState)                                   // und vorher nicht
    {
      lastPressed = millis();                         // Zeit merken
      lastState = HIGH;                               // status setzen
      ret = true;                                     //
    }
  }
  else                                                // nciht gedrückt
  {
    if (lastState)                                    // vorher aber schon
    {
      if (millis() - lastPressed > debounce)          // Zeit abgelaufen
      {
        lastState = LOW;                              // merker löschen
      }
    }
  }
  return ret;
}

willst noch irgendwo servo writes einbauen?!?

(wird 2024-03 gelöscht)

Egal wo - ich hätte irgendwo auch nur den inhalt der pwm-Variablen auf dem SerMon ausgegeben :wink:
Ich habs oben nur mit reingenommen, damit er sieht, wie das mit dem array funktioniert....

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.