Problem mit Servo und blinkender LED

Hallo Leute.

Obwohl es relativ einfach sein soll das ein Neueinsteiger mit dem Arduino zurecht kommen soll muss ich nun doch eine Frage an Euch stellen.

Zuerst eine Vorab-Information:
Mein Name ist Ernst, bin jetzt vor einigen Tagen ein halbes Jahrhundert alt geworden und ich baue gerade einen Astromech-Druiden im Maßstab 1:1 (StarWars).
Genauer gesagt baue ich einen R2-D2.

Innerhalb des Drioden wird vieles mittels mehrerer Arduinos gesteuert.
Jetzt brauche ich für das Periscope und den Life-Form-Scanner einen Mechanismus der die beiden Geräte bewegt, sprich nach oben aus dem Dome (Kopf) den Druiden hebt und bewegt.

Das Ganze soll sich folgendermaßen abspielen:

  1. Ein 12V-Motor hebt über eine Zahnstange oder einen Actuator eine kleine Plattform vorauf sich das Periscope bzw. der LFS befindet. Dieser Motor soll mittels einer H-Brücke oder eines Motor-Shields gesteuert werden.
  2. Ist der obere Totpunkt erreicht schaltet ein Endschalter den 12-Motor ab.
  3. Nun soll sich ein Servo das mit nach oben bewegt wurde eine bestimmte Zeit hin und her bewegen.
  4. Gleichzeitig sollen zwei LEDs abwechselnd blinken (astabiler Multivibrator)
  5. Ist die im Punkt 3 eingestellte Zeit abgelaufen wird das Servo in die Mittelstellung gestellt und die beiden LEDs sollen aufhören zu blinken.
  6. Der 12V-Motor dreht sich nun in die andere Richtung und fährt das ganze Gebilde wieder zurück in die Ausgangsposition wo ihn ein zweiter Endschalter abschaltet.

Jetzt mein Problem:
Ich schaffe es nicht den Servo hin und her zu bewegen und gleichzeitig die LEDs blinken zu lassen.

Als erstes nahm ich die beiden Sketch-Beispiele "Blink" und "Sweep" und beide für sich stellen kein Problem dar.
Aber wenn ich beide Sketche vereine werden die Sketche logischerweise hintereinander abgearbeitet.
Die Funktionen sollen aber gleichzeitig laufen, also die LEDs sollen blinken und das Servo soll sich hin und her bewegen.
Die LEDs sollen aber nicht synchron mit den Bewegungen des Servos blinken, das habe ich schon geschafft, sieht aber nicht gut aus.
Die LEDs sollen 4x pro Sekunde blinken und in 2 Sekunden soll sich das Servo ein Mal innerhalb eines 90° Winkels hin und her bewegt haben.

Ich hoffe einer von Euch Spezialisten hat einen Tipp für mich wie ich diese beiden Funktionen kombinieren kann. Der Rest in dann ja nicht schwer.

Danke
Ernst

Direkt bei den Sketchen im Arduino gibt es den Sketch Blink without delay. Der zeigt Dir wie es geht.

Während dem delay tut der Arduino nichts anderes, als zu warten. Im Link oben wird gezeigt, wie es ohne delay geht.

Grüße,

Sven

So, und hier nochmal auf deutsch:
http://playground.arduino.cc/Learning/BlinkWithoutDelay_de

Hallo sven.

Das kenne ich natürlich und habe es auch schon probiert.
Funktioniert aber nicht.
Die LED geht an,
die LED geht aus,
das Servo wird um einen Schritt weiter bewegt,
die LED geht an,
die LED geht aus,
das Servo wird um einen Schritt weiter bewegt,
die LED geht an, … usw.

Hier der Code:

#include <Servo.h>
Servo myservo;
int pos = 0;
int x;
int servospeed = 10;
const int led1 =  13;
int motor = 8;
int ledState = LOW;
long previousMillis = 0;

long interval = 125;

void setup() {
  myservo.attach(9);
  pinMode(led1, OUTPUT);      
  pinMode(motor, OUTPUT); 

// Stellmotor faehrt den Scanner aus dem Dome
  digitalWrite(motor, HIGH);        // 12V-Motor EIN
  delay(3000);                      // steht für die Einschaltdauer des 12V-Motors
  digitalWrite(motor, LOW);         // 12V-Motor AUS durch Endschalter
}

void loop()
{
  unsigned long currentMillis = millis();
  if(currentMillis - previousMillis > interval) {
    previousMillis = currentMillis;   
    if (ledState == LOW)
      ledState = HIGH;
    else
      ledState = LOW;
    digitalWrite(led1, ledState);
  }

// Servo wird von der Mittellage in die rechte Startposition gefahren
for(pos = 75; pos > 25; pos -= 1) {
  myservo.write(pos);
  delay(servospeed); }

delay(200);
// Servo soll 5x hin und her fahren
for(x = 1; x < 5; x += 1) {

// Servo faehrt los
for(pos = 25; pos < 125; pos += 1) {
    myservo.write(pos);
  digitalWrite(led1, HIGH);
  delay(125);
  digitalWrite(led1, LOW);
  delay(125);
    delay(servospeed); } 
  
  delay(200);
 
// Servo faehrt wieder zurueck
for(pos = 125; pos>=25; pos-=1) {
    myservo.write(pos);
  digitalWrite(led1, HIGH);
  delay(125);
  digitalWrite(led1, LOW);
  delay(125);
    delay(servospeed); } 

  delay(200);
}

// Servo wird von der Endposition zurueck in die Mittellage gefahren
for(pos = 25; pos < 75; pos += 1) {
  myservo.write(pos);
  delay(servospeed); } 

delay(1000);

//Stellmotor faehrt den Scanner wieder in den Dome zurueck
digitalWrite(motor, HIGH);        // 12V-Motor EIN
delay(3000);                      //steht für die Einschaltdauer des 12V-Motors
digitalWrite(motor, LOW);         // 12V-Motor AUS durch Endschalter
}

Ernst

W3873oR:
Funktioniert aber nicht.

Dein Sketch darf KEIN DELAY enthalten!

Delay bedeutet “blockiere die Programmausführung für die angegebene Zeit”.

Du willst aber Dein Programm garantiert nicht blockieren, sondern es soll ständig etwas passieren!

Ich empfehle anstelle des “blinkWithoutDelay” Beispiels eher, sich einen Takt aus dem millis() Timer abzuleiten, indem mit der Modulo-Arithmetik (Rest einer ganzzahligen Division) durch die Taktdauer ermittelt wird, wo im Takt man sich befindet. Und entsprechend dann handelt.

Anbei mal mein Codebeispiel für zwei unabhängige Aktionen, die mit unterschiedlicher Taktung laufen

  • blinken ohne delay
    und
  • Servo drehen ohne delay
#define LEDPIN 13
#define SERVOPIN 9

void setup()
{
  pinMode(LEDPIN, OUTPUT);
  pinMode(SERVOPIN,OUTPUT);
}

long millisTakt(long value)
// Eingabewert: Taktdauer
// Rückgabewert: Millisekunden seit Anbruch des letzten Taktes
{
  return millis()%value;
}

void servoRefresh(byte pin, byte angle)
// Eingabewerte: Servopin, Drehwinkel des Servos (0...180)
#define MINPULSE 700   // Minimale Impulslänge bei Linksausschlag 0
#define MAXPULSE 2300  // Maximale Impulslänge bei Rechtsausschlag 180
{
  static unsigned long lastRefresh=0;
  if (millis()-lastRefresh<20) return; // noch keine 20 ms vergangen
  lastRefresh=millis(); // Zeit des erfolgten Refresh merken
  if (angle>180) angle=180; // Stellwinkel bis 180 Grad erlaubt
  int pulselen = MINPULSE + (MAXPULSE-MINPULSE)*(long)angle/180;
  digitalWrite(pin,HIGH); // Beide Refresh-Impulse "gleichzeitig" starten
  delayMicroseconds(pulselen);
  digitalWrite(pin,LOW);
}


int servoPos;

void loop()
{
  // LEDs im Sekundentakt schalten
  if (millisTakt(250)<125)    // Taktdauer 250 ms
    digitalWrite(LEDPIN,HIGH); // in den ersten 125 ms LED ein
  else  
    digitalWrite(LEDPIN,LOW); // danach LED aus
    
  // Servoposition abhängig vom Zweisekundentakt berechnen
  servoPos= 45+abs(1000-millisTakt(2000))*90/1000;   // Taktdauer 2 Sekunden, servoPos 45..135 Grad
  servoRefresh(SERVOPIN, servoPos);
}

Diese Art Taktung läuft mit dem Timer-Überlauf alle 50 Tage einmal über, so dass (wenn der Sketch so lange am Stück läuft) sich der Sketch alle 50 Tage einmal kurz verblinkt bzw. der Servo einmal außer der Reihe in eine andere Position zuckt, bevor er dann wieder ca. 50 Tage einwandfrei durchläuft.

Hallo jurs.

Danke für den Denkanstoß.
Klar, bei einen Delay wird der Ablauf angehalten, daher auch de Name. :blush:

Habe gerade das Sketch am laufen - es funktioniert.
Der Servo läuft zwar etwas "unrund" aber das liegt an den Werten, das kienne ich.
Ich werde jetzt mal versuchen das zu beheben.

Auf jeden Fall ein großes DANKE für das Sketch.
Daraus werde ich mir was Schönes zusammen stellen.

Ernst

W3873oR:
Habe gerade das Sketch am laufen - es funktioniert.

Für ein besonders klein kompilierendes Programm habe ich mal die Servo-Library aus meinem Beispielprogramm rausgeworfen und steuere den einzelnen Servo direkt an, ohne Library. Du kannst es natürlich auch mit Servo-Library machen und spätestens ab dem dritten zeitgleich angesteuerten Servo bietet es sich auch an, eine Library zu verwenden.

W3873oR:
Der Servo läuft zwar etwas "unrund" aber das liegt an den Werten, das kienne ich.
Ich werde jetzt mal versuchen das zu beheben.

Kleiner Denkanstoß:
Die Unrundheit im Demosketch kommt hauptsächlich dadurch zustande, dass die Drehbewegung in den Endpunkten der Drehung abrupt wechselt und von "voll Rechtslauf" unmittelbar auf "voll Linkslauf" umgesteuert wird. Bei einer Hin- und Herbewegung wird ein geschmeidiger "runder Lauf" dadurch erzeugt, dass der Servo an den Umkehrpunkten langsam die Geschwindigkeit auf 0 herunterbremst, danach wieder beschleunigt, "in der Mitte" die Höchstgeschwindigkeit erreicht, und am anderen Umkehrpunkt wieder herunterbremst. Die Sinus- bzw. Cosinusfunktion sollte beim Erstellen einer geschmeidigen Drehung helfen.

Nachtrag: Hier noch die Zeile zur Berechnung der Servoposition mit "Abbremsen vor den Umkehrpunkten":

  servoPos= 90+45*sin(2*PI*millisTakt(2000)/2000);   // Taktdauer 2 Sekunden, servoPos 45..135 Grad