Benötigen Hilfe bei Programmierung :)

Guten Abend zusammen, wir versuchen ein kleines Uni-Projekt zu realisieren und haben uns heute das erste mal mit der Arduinowelt befasst.

Wir möchten mit einem Taster einen ServoMotor ansteuern welcher sich im Bereich 0-180° bewegen soll.

Der Taster soll ihn EIN und ebenfalls AUS Schaltern. Leider haben wir es bisher nur geschafft das sich der Servo innerhalb der LOOP einschalten lässt und dann seines Weges zwischen 0-180° geht.... Und dann immer und immer weiter macht.... :frowning:

Nun haben wir ihn soweit dass er bei Tasterdruck seinen Vorgang 4x wiederholt und dann in die Lage 90° zurückfährt.

Was wir nun erreichen möchten :

Der Servo soll sich später parallel zu einerm Soundtrack bewegen und ungefähr gleichzeitig mit ihm auch Enden.
Dennoch soll der Servo jederzeit über den selben Taster ausschaltbar sein und anschließend auf die Ausgangsstellung zurückfahren.

#include <Servo.h>

int tasterPin1 = 2; //Taster1 an Pin 2 angeschlossen
int tasterPin2 = 3; //Taster2 an Pin 3 angeschlossen
int tasterStatus1, tasterStatus2;
Servo meinServo;

void setup()
{
pinMode(tasterPin1, INPUT); //tasterPin1 (Pin 2) wird als INPUT definiert
pinMode(tasterPin2, INPUT); //tasterPin2 (Pin 3) wird als INPUT definiert
meinServo.attach(9); //Servomotor wurde an Pin 12 angeschlossen

void loop()
{
tasterStatus1 = digitalRead(tasterPin1);
tasterStatus2 = digitalRead(tasterPin2);

if (tasterStatus1 == 1)
{
meinServo.write(10);
delay(1000);
meinServo.write(180);
delay(800);
meinServo.write(10);
delay(600);
meinServo.write(180);
delay(1000);
meinServo.write(10);
delay(500);
meinServo.write(180);
delay(1000);
meinServo.write(10);
delay(500);
meinServo.write(180);
delay(1000);
meinServo.write(90);
delay(1000);
}

if (tasterStatus2 == 1)
{
meinServo.write(10); //Dreht nach rechts
delay(50);
}

delay(200);

}

Unser Programmcode ist mit Sicherheit kein Augenschmau und wir sind leider auch pure Anfänger auf diesem Gebiet. Ich bitte dies zu berücksichtigen.

Freuen uns auf eure Antworten.

Liebe Grüße
Sören & Fred

Hallo,

habt ihr am Taster einen externen Pulldown Widerstand dran?
Mit euren delay's bemerkt ihr auch das prellen des Tasters noch nicht.

Ihr seit doch Studenten, dann beschäftigt Euch mal mit der Funktionsweise und dem Ansteuersignal eines Servomotors.

Wie lange seit ihr schon Studenten und was studiert ihr?

SoerenBrt:
Dennoch soll der Servo jederzeit über den selben Taster ausschaltbar sein und anschließend auf die Ausgangsstellung zurückfahren.

Wenn Euer Sketch "jederzeit" in der Lage sein soll, auf irgendwelche Eingaben zu reagieren, dann könnt ihr komplett vergessen, dass es einen Befehl namens "delay()" gibt, mit dem man den Controller für eine bestimmte Zeit komplett blockieren kann, damit er gar nichts macht.

Wenn ein Controller "jederzeit" auf Eingaben reagieren soll, dann darf er "niemals" mit delay() blockiert werden.

Ihr braucht eine Funktion, die in Abhängigkeit von der Zeit nach dem Drücken des Tasters eine Servoposition zurückgibt. So eine Funktion wie:

int getServopos(long timeSinceButtonwasPressed)
{
  if (timeSinceButtonwasPressed>180000L) return 0; // nach 180 Sekunden 0 
  return timeSinceButtonwasPressed/1000; // sonst Rückgabewerte jede Sekunde um 1 erhöhen
}

Und in der loop-Funktion die Zeit merken, wann der Button gedrückt wurde, und die Servoposition laufend mit der seitdem vergangenen Zeit über die Funktion abfragen (und nachstellen).

Kooperatives Multitasking!

Danke für die raschen Antworten!

@Doc_Arduino: Wir studieren Maschinenbau im 4. Semester! Wir haben einen 10KOhm Widerstand verwendet.

@jurs: Ja das haben wir uns fast schon gedacht. Wir haben dann versucht das ganze irgendwie über den Befehl "millies" zu bewerkstelligen.. sind aber bisher auf dem totalen Holzweg gefahren. Wie genau binden wir die Funktion denn nun mit ein ?

SoerenBrt:
@jurs: Ja das haben wir uns fast schon gedacht. Wir haben dann versucht das ganze irgendwie über den Befehl "millies" zu bewerkstelligen.. sind aber bisher auf dem totalen Holzweg gefahren. Wie genau binden wir die Funktion denn nun mit ein ?

Am einfachsten mit dem EVA-Prinzip in der loop-Funktion:

  • Eingabe
  • Verarbeitung
  • Ausgabe
    Immer rundum.

Auf Neudeutsch heißt das EVA-Prinzip "IPO principle" und die Buchstaben stehen dann für:

  • Input
  • Processing
  • Output
    Und es geht immer noch rundum. Oder fachlicher ausgedrückt: Round-robin-scheduling

Hier ein Beispielcode, mit Handling von zwei Buttons, davon einer zum Starten und einer zum Abbrechen des Vorgangs, einen Servo bei gestartetem Vorgang langsam drehen (180 Grad Drehwinkel in 180 Sekunden), und alle Aktionen werden auf dem seriellen Monitor dargestellt, um damit die Funktion debuggen zu können.

Beispielcode:

#include <Servo.h>
#define BUTTONMODE INPUT_PULLUP  // use INPUT or INPUT_PULLUP
#define NUMBUTTONS 2
const byte buttonPins[NUMBUTTONS]= {2,3};
byte buttonState[NUMBUTTONS];
byte buttonPressed[NUMBUTTONS];

Servo myServo;
boolean servoActive;
unsigned long servoActiveSince;
int servoPos=0;

void input()
{
  memset(buttonPressed,0,sizeof(buttonPressed)); // clear buttonPressed array
  for (int i=0;i<NUMBUTTONS;i++)
  {
    byte currentState=digitalRead(buttonPins[i]);
    if (BUTTONMODE==INPUT_PULLUP) currentState=!currentState; // inverted logic with INPUT_PULLUP
    if (currentState && !buttonState[i]) buttonPressed[i]=true;
    buttonState[i]=currentState;
  }
}

void processing()
{
  if (buttonPressed[0]) // first button is pressed ==> activate Servo
  {
    Serial.println("Button pressed - Servo active");
    servoActive=true;
    servoActiveSince=millis();
    servoPos=0;
  }
  if (buttonPressed[1]) // second button pressed ==> abort / deactivate Servo
  {
    Serial.println("Cancel Button pressed");
    servoActive=false;
    servoPos=0;
  }
  if (servoActive) // servo is currently active with rotating
  {
    long activeTime=millis()-servoActiveSince;
    if (activeTime>=180000) // we are finished after 180 seconds
    {
      Serial.println("Game Over!");
      servoActive=false;
      servoPos=0;
    }
    else servoPos= activeTime/1000; // rotate as time goes by
  }
  else servoPos=0; // servo not active
}

void output()
{ 
  static int oldServoPos=-1;
  if (servoPos==oldServoPos) return;
  oldServoPos= servoPos;
  myServo.write(servoPos); 
  Serial.println(servoPos);
}

void setup() {
  Serial.begin(9600);
  Serial.println("Good night and good luck!");
  for (int i=0;i<NUMBUTTONS;i++)
  {
    pinMode(buttonPins[i], BUTTONMODE);
  }
  myServo.attach(9); 
}


void loop() { // round-robin-scheduling of three tasks: input, processing, output
  input();  
  processing();
  output();
  delay(5); // kleines Delay zur Verhinderung von Tasterprellen
}

Da mechanische Taster prellen und das Programm sonst "zu schnell" arbeitet und anstelle eines Tastendrucks ggf. zwei, drei oder noch mehr Tastendrücke in kurzer Zeit feststellt, habe ich in die loop zum Ausbremsen noch 5 Millisekunden delay() pro Durchlauf eingefügt. Das könnte man ggf. aber auch anders lösen, wenn man diese 5 Millisekunden = 5000 Mikrosekunden = 80000 Instruktionen im Programm dringend für andere Zwecke benötigt und ihr den Controller nicht so "lange" blockieren wollt.

Das gepostete Programm ist für Buttons, die ohne externen Pull-Widerstand zwischen Arduino-Pin und GND angeschlossen sind. Im setup() wird dafür als pinMode INPUT_PULLUP eingestellt.

Wenn ihr Eure Buttons stattdessen mit externem PullDown-Widerstand und den Button zwischen Arduino-Pin und 5V angeschlossen habt, ändert den BUTTONMODE vor dem Hochladen ab auf:

#define BUTTONMODE INPUT

Danke für die gemachte Arbeit =) Leider tut sich aber nichts :frowning:

Auch wenn die den BUTTONMODE umgestellt werden tut sich leider nichts. Es ist zu hören dass der Servo Spannung bekommt, aber leider tut sich nichts.. :frowning:

SoerenBrt:
Danke für die gemachte Arbeit =) Leider tut sich aber nichts :frowning:

Auch wenn die den BUTTONMODE umgestellt werden tut sich leider nichts. Es ist zu hören dass der Servo Spannung bekommt, aber leider tut sich nichts.. :frowning:

Bitte Schaltbild posten, was Du genau wo angeschlossen hast. Oder wenigstens genau beschreiben.

Was passiert, wenn Du den seriellen Monitor auf 9600 Baud öffnest und die Taster drückst und loslässt?
Siehst Du nur die Begrüßungsmeldung ("Good night and good luck!"), oder erscheinen auch Meldungen, wenn die Taster gedrückt werden, wie "Button pressed - Servo active" beim einen Taster und "Cancel Button pressed" beim anderen, und nur der Servo dreht sich nicht?

Der Post gibt folgendes wieder:

Good night and good luck!
Button pressed - Servo active
Cancel Button pressed
0
Button pressed - Servo active
1
2
3
4
5
Cancel Button pressed
0
Button pressed - Servo active
1
2
3
4
5
6
7
8
9
Cancel Button pressed
0
Button pressed - Servo active
1
2
Cancel Button pressed
0

Der Schaltplan sieht wie folgt aus :

Servomotor : VILROS SG90
Brauner-Draht = GND
Roter Draht = 5V
Oranger-Draht = Digutaler Input Eingang 9 Arduino

Taster 1:

Eingang : 5V
Ausgang: 10 kOhm Widerstand -> GND UND Digitaler Input Eingang 2

Taster 2: Das selbe nur mit Input Eingang 3

Sorry vergessen: Der Servo dreht am Anfang ohne jeglichen Tastereinfluss an und bleibt dann egal was dann passiert einfach stehen und brummt vor sich hin.

SoerenBrt:
Button pressed - Servo active
1
2
3
4
5
Cancel Button pressed

Wenn Du sehen willst, wie der Servo dreht, müßte der Servo natürlich weiter als nur 5 Grad drehen. Ein Drehwinkel von 180 Grad in 180 Sekunden ist extrem langsam und Du müßtest den Servo länger drehen lassen, bis Du die Bewegung siehst. 5 Grad ist ja kaum oberhalb der Servoauflösung, die meist nur 3 Grad beträgt.

Drückst Du denn da jetzt auf den Cancel Button, wenn das im seriellen Monitor so angezeigt wird?

Oder ist das ein Phantom-Tastendruck auf den Cancel-Button, den Du gar nicht machst?

In dem Fall: Anschluss des PullDown-Widerstands prüfen! Offenbar ist der PullDown am Cancel-Button entweder gar nicht, falsch oder mit Wackelkontakt angeschlossen.

Zu den Servopositionen: Hast Du den Servo denn mit einem Test-Sketch vorab getestet, dass der überhaupt die Servopositionen 0° und 180° anfahren kann, ohne dabei an einen internen Endanschlag zu fahren? Wenn das nicht der Fall ist, teste lieber als Extrempositionen nicht mit 0° und 180°, sondern beispielsweise mit 45° und 135°!

Er zeichnet den Push des Buttons auf! Also keinerlei Phantomeffekt vorhanden.

Jetzt dreht sich der Servo langsam -> 1° pro 1s

Wie stellen wir denn nun ein, dass er eine Abfolge in einer bestimmten Zeit macht ( z.B. 180 ° in 5s ).

Leider blicken wir noch nicht ganz durch deine Programmierung durch :slight_smile: Wir geben aber grade unser bestes!

SoerenBrt:
Er zeichnet den Push des Buttons auf! Also keinerlei Phantomeffekt vorhanden.

Also Button wird gedrückt, und es erscheint eine Debug-Message im seriellen Monitor, dass der Button gedrückt wurde. So soll es sein. Dann ist die Verdrahtung OK.

SoerenBrt:
Jetzt dreht sich der Servo langsam -> 1° pro 1s

Genau das ist im Sketch ja auch so programmiert.

SoerenBrt:
Wie stellen wir denn nun ein, dass er eine Abfolge in einer bestimmten Zeit macht ( z.B. 180 ° in 5s ).

Leider blicken wir noch nicht ganz durch deine Programmierung durch :slight_smile: Wir geben aber grade unser bestes!

Die Einstellung der Drehgeschwindigkeit im Sketch erfolgt hier:

    if (activeTime>=180000) // we are finished after 180 seconds
    {
      Serial.println("Game Over!");
      servoActive=false;
      servoPos=0;
    }
    else servoPos= activeTime/1000; // rotate as time goes by

Wenn die 180 Grad Drehung schon nach fünf Sekunden fertig sein soll, wäre dies zu ändern auf:

    if (activeTime>=5000) // we are finished after 5 seconds
    {
      Serial.println("Game Over!");
      servoActive=false;
      servoPos=0;
    }
    else servoPos= 180*activeTime/5000; // rotate as time goes by

Also nach 5000 Millisekunden die Servodrehung abschalten und seinen Drehwinkel auf 0 zurücksetzen, und während der 5 Sekunden den Drehwinkel nach der Formel "180*activeTime/5000" berechnen. Wenn activeTime 0 ist, wäre dann der Drehwinkel auch 0. Und wenn activeTime 5000 ist, kürzt sich 5000 gegen 5000 und der Drehwinkel wäre zu 180 Grad berechnet. Aber ich glaube, er kommt nur bis 179°, weil activeTime ja nur bis 4999 hochläuft und bei 5000 bereits alles wieder auf 0 zurückgesetzt wird. Also an der Endposition muß ggf. noch nachgearbeitet werden, wenn der Servo tatsächlich bis 180° laufen soll und nicht nur bis 179°.

Die Programmlogik ist eigentlich ganz einfach und nach dem EVA-Prinzip gestrickt:
E - Eingabe ==> fragt ab, ob und welcher Button gedrückt wurde
V - Verarbeitung ==> die aktuelle Servoposition unter Berücksichtigung von Tastendrücken und Zeit ermitteln
A - Ausgabe ==> falls sich die aktuelle Servoposition seit der letzten Ausgabe geändert hat ==> neue Position setzen

Perfekt ! Er dreht nun innerhalb 3 Sekunden die 179° ( Reicht auch vollkommen!)

Nun macht er die Abfolge ja 1x und kehrt dann wieder auf die 0° zurück und wartet auf den nächsten Befehl.

Jetzt müssen wir es aber noch hinbekommen, dass das ArduinoBoard den Servo solange auf und ab fahren lässt bis der Taster2 diesen Vorgang abbricht und ihn Auf die Ausgangsstellung zurück fährt!

SoerenBrt:
Perfekt ! Er dreht nun innerhalb 3 Sekunden die 179° ( Reicht auch vollkommen!)

Nun macht er die Abfolge ja 1x und kehrt dann wieder auf die 0° zurück und wartet auf den nächsten Befehl.

Ja, im Code gibt es einen Timeout nach Ausführung der Bewegung und dann wird abgeschaltet.

SoerenBrt:
Jetzt müssen wir es aber noch hinbekommen, dass das ArduinoBoard den Servo solange auf und ab fahren lässt bis der Taster2 diesen Vorgang abbricht und ihn Auf die Ausgangsstellung zurück fährt!

Ohne so ein Timeout mit einer Endlosbewegung wird der Code noch einfacher. Ihr müßt nur die Servoposition anders berechnen. Z.B. in Zyklen von 3 Sekunden mit der auszuführenden Bewegung:

  • langsam in 3 Sekunden von 0 auf 180 Grad drehen
  • mit Höchstgeschwindigkeit zurück auf 0 Grad
  • und das immer wieder von vorne bis der Cancel-Button gedrückt wird.
  if (servoActive) // servo is currently active with rotating
  {
    long activeTime=millis()-servoActiveSince;
    #define DURATION 3000
    activeTime= activeTime%DURATION; // modulo arithmetik: activeTime alsways starts from 0 and counts up
    servoPos= 180*activeTime/DURATION; // rotate as time goes by
  }
  else servoPos=0; // servo not active

Super ! Es klappt wunderbar!

Jetzt kommt erstmal der mechanische Teil für unser Projekt bevor wir den nächsten Schritt mit dem Arduino wagen ! Sollten wir dann nochmal fragen haben, werden wir uns wieder melden =) Vielen Dank für deine Unterstützung !!