mehrere Funktionen "parallel" abarbeiten

Hallo Forum

ich bin neu hier und möchte euch um Hilfe bitten.

Ich bin als Hobby-Fotograf gerade dabei mir einen Time Lapse Slider zu bauen… Die Mechanik ist voll funktionsfähig und ich denke auch das ich die Elektronik ohne Probleme zum laufen bringe… nur die Programmierung des Arduino Nano macht mir zu schaffen.

Zum Problem:
Ich habe mir von Johannes Benkert ( www.Glohbe.de ) gegen eine kleine Spende den Sketch seines Sliders besorgt. Leider ist sein Programm nicht so wie ich es für meine Zwecke brauche. Daher habe ich mich dran gemacht den Sketch umzuschreiben. Vieles (wohl eher 90%) konnte ich weiterverwenden. Nur benötige ich als absoluter Arduino-Anfänger eine kleine Hilfestellung.

Im Grunde ist das Programm recht einfach. Es läuft im Loop ein Menü welches aus zwei Unterpunkten besteht (“Setup” für Timelapse und “Manuell” für naja Manuell bedienen halt). Innerhalb der Funktion “Setup” werden dann die einzelnen Aufgaben bzw. Eingaben abgearbeitet (Anzahl Bilder, Intervall, etc.) bis man an die Funktion “dotimelapse()” kommt welche dann als While()Schleife die eigentliche Arbeit vollbringt und danach wieder ins Hauptmenü springt.
Erst an dieser Stelle weicht meine Programmierung von der Vorlage ab (oder besser sollte davon abweichen)

Ich habe es mir so vorgestellt:

void doTimelapse(){
steps = (1828 * Strecke); //Anzahl Schritte für gewünschte Fahrstrecke
pictureCounter = 0;
setRes(16);
lcd.clear();
digitalWrite(motorEnable,LOW);
digitalWrite(motorDir,motorDirection);
speed = 800;
while(pictureCounter < maxPics){
shutter();
lcd.setCursor(0,0); lcd.print(“Timelapse…”);
myTxt = String("Bilder: “) + pictureCounter + (” / ") + maxPics;
lcd.setCursor(0,1); lcd.print(myTxt);
Move(steps);
}
}

Innerhalb der While() Schleife werden die Funktionen shutter() und Move() aufgerufen

void shutter(){
digitalWrite(shutterPin, HIGH);
delay(50); //geht sicher auch schneller
digitalWrite(shutterPin, LOW);
pictureCounter++;
}

void Move(int steps){
delay((Beli*1000) + 450); //Belichtungszeit + Puffer abwarten bevor losgefahren wird
for(int i = 0;i<steps;i++){
digitalWrite(motorStep,HIGH); // Output high
delayMicroseconds(speed);
digitalWrite(motorStep,LOW); // Output low
delayMicroseconds(speed);
}
}

Nun möchte ich aber das shutter() und move() “parallel” beginnen, da das ganze ja “Zeitkritisch” ist.

Wie kann man das lösen?

Google spuckt millis() und FSM und so was aus aber dazu stecke ich zu wenig in der Materie um es von selbst zu verstehen und für mein Problem zu nutzen.

Vielleicht könnt ihr mir ja helfen.

Gruß
Simon

Hi

Solange delay() in Deinem Sketch vorkommt, wird Das Nichts.
Während delay() macht der Arduino Pause.
Also in der move-Funktion macht Er einen Schritt, Pause, Schritt, Pause, ... bis der Kaffe-Bauch gefüllt ist.
Auch werden dort erst sämtliche Steps abge-wartet.

Shutter sieht ähnlich aus, wenn auch nur mit EINEM delay().

in doTimelapse() stört wiederum die WHILE-Schleife.
Die bleibt so lange aktiv, wie die Bedingung erfüllt ist - also noch nicht alle Bilder gemacht wurden.

Drei Baustellen, Die Du ausmerzen musst.
Wie viel noch in dem Rest des von Dir erstandenen Code faul ist - kA - da Du dafür aber Geld gegeben hast, ist eine Veröffentlichung ggf. 'nicht so toll' - bitte kläre Das ab.

Und: Suche hier nach der Nachtwächter-Erklärung - DAS ist der Weg, Den Du einschlagen willst.

MfG

Danke Postmaster-ino and noiasca,

ich denke das ich mich erst mal in die Materie millis() und was man damit anstellen kann reinarbeiten muss.

was das parallele abarbeiten angeht.

Die wichtigste Konstante bei Zeitraffern ist das Intervall, welches (ziemlich) genau eingehalten sein muss.

der Arduino weis nicht wie lang genau die Kamera geöffnet ist - ich gebe ihm das zwar als Eingabewert an damit ich damit rechnen kann aber Verzögerungen etc. sind ja auch vorhanden. Außerdem weis der Arduino nicht wie lange der Motor genau braucht um die gewollte Strecke zu fahren - ich kann das errechnen lassen aber ob das dann 100% passt...
Daher meine Idee beides quasi parallel zu starten und zu verschiedenen Zeitpunkten innerhalb des Intervalls die notwendigen Aktionen durchzuführen. So ist die einzige Zeit die ich betrachten muss das Intervall zu dessen Beginn die Kamera ausgelöst wird. Der Motor läuft erst los wenn die Belichtungszeit + ein Puffer innerhalb des Intervalls abgelaufen sind. (Jetzt wo ich das so schreibe wird mir klar warum ich millis() brauche...)

Wenn ich shoot-move-shoot sequentiell abarbeite habe ich das Gefühl das es ungenau wird, da ich die Zeiten für shoot, die Puffer und für move aufaddieren und irgendwie für jedes Intervall abgleichen müsste.

Natürlich sage ich nicht das ich es nicht völlig zerdacht habe und viel zu komplex denke aber so scheint es mir logisch.

Wer für mein Problem eine elegante Lösung kennt darf sich meiner ewigen Dankbarkeit erfreuen.

Auf auf in die weiten des I-Net...

Ich berichte wenn ich eine "Lösung" gebastelt habe...

Gruß
Simon

Hi

Wichtig ist doch 'nur' der Zeitpunkt, wo das Bild ausgelöst wird, oder?
Dann ist noch wichtig, daß die Kamera so lange nicht bewegt wird, wie die Belichtung dauert.

  • Status 0: Wenn Auslösezeit erreicht: Verschluss auf, Zeit merken, Status++
  • Status 1: Wenn Belichtungszeit vorbei, Verschluss zu, Anfahrt neue Position starten, Status ++
  • Status 2: Wenn Position erreicht, Status=0

Die neue Auslösezeit errechnet sich aus dem Auslösezeitpunkt (denke ich).
Du musst also 'nur' immer auf 'letzte Zeit' plus Wartezeit oder Belichtungszeit warten - also 'Nicht tun'.

MfG

Ich verstehe das mit der Zeitungenauigkeit nicht.

Intervall = Belichtung + Bewegung Motor.
2 Größen stellst Du ein, die dritte ergibt sich aus den anderen 2.
Die Geschwindigkeit der Bewegung ist auch nicht wichtig. Sie muß nur so groß sein daß in der zur Bewegung verfügbare Zeit auch ausgeführt wird.

Es ist egal ob man das dann mittels delay oder millis macht da keine Funktionalität paralell laufen muß.
Grüße Uwe

... trotzdem sollten wir delay() hier nur zweitrangig erwähnen - wenn man's direkt 'ordentlich' macht, kann man mit dem Sketch auch noch andere Dinge machen - wenn mit delay() wohl eher nicht.
Sehe ja ein, daß Das auch mit delay() klappt (bis jetzt) - aber die Ausbaufähigkeit wird direkt kastriert.

MfG

ja mit so verstümmelten Code ist das halt immer so eine Sache, man weis ja nicht was sonst noch drinnen ist.

Aber ein einfaches BlinkWithoutDelay reicht doch als Zeitgeber.

für den TO nur mal als Idee:

byte steps = 10;
byte Strecke = 1;
const byte shutterPin = 13;
const byte motorStep = 12;
byte speed = 42;
uint16_t pictureCounter;

bool go = true; // eine Variable die du im LCD Menu als Go=true setzt

void setup() {
  Serial.begin(115200);
  Serial.println(F("\nStart"));
}

void loop() {
  if (go) doTimelapse();
}

void doTimelapse() {
  static uint32_t previousMillis;
  uint32_t currentMillis = millis();
  uint32_t interval = 1000UL;     // pseudo Logik zur Intervallermittlung
  if (currentMillis - previousMillis >= interval)
  {
    previousMillis = currentMillis;
    Serial.println(F("bum"));
    shutter();  // das kann ruhig etwas blockierend sein
    move(4711); // auch das kann blockierend sein, solange beide unter interval liegen!
  }
}

void shutter() {
  digitalWrite(shutterPin, HIGH);
  delay(50);                               //geht sicher auch schneller
  digitalWrite(shutterPin, LOW);
  pictureCounter++;
}


void move(int steps) {
  //  delay((Beli * 1000) + 450);   //Belichtungszeit + Puffer abwarten bevor losgefahren wird
  for (int i = 0; i < steps; i++) {
    digitalWrite(motorStep, HIGH); // Output high
    delayMicroseconds(speed);
    digitalWrite(motorStep, LOW); // Output low
    delayMicroseconds(speed);
  }
}

Hi,

@uwefed:

Das Intervall besteht aus Belichtungszeit + Puffer + Bewegung Motor + der Zeit die bis zum nächsten Intervall noch übrig ist. Der Motor fährt die gewünschte Strecke in z.B. ner halben oder maximal ner dreiviertel Sekunde.

Be einem Intervall von 5sec. belichte ich zw. 0,5 und 2 sec. (je nachdem welche Optik das Video später haben soll). Der Puffer sollte ne viertel oder ne halbe Sec. sein.
Jetzt mal angenommen der Motor braucht zum Fahren der Strecke 0,68963… sec.

Woher weis dann der Arduino das er noch 5 - (2 + 0,25 + 0,68963…) = 2,06037… sec. warten muss bis er das nächste Bild macht ohne die Zeit zwischen zwei Aufnahmen"genau" zu messen?

Ich glaube Postmaster-ino und noiasca haben recht. Ich sollte es richtig (ohne delay() ) programmieren und ich muss die Zeit zwischen zwei Aufnahmen genau betrachten. Alles andere muss zwangsläufig innerhalb dieser Zeit passieren und natürlich abgeschlossen sein :wink:

Danke schon mal für eure Rückmeldungen

schönen Sonntag noch

Gruß
Simon

Hi

millis() ist Deine Armband-Uhr - Da sieht der Arduino die aktuelle Zeit.
Wenn 'ab Jetzt' in 1000ms etwas passieren soll, merkst Du Dir die Zeit.
Und prüfst zukünftig, ob 'aktuelle Uhrzeit - gemerkte Uhrzait >= Wartezeit' ist.
Wenn Nein: doing nothing
Wenn Ja: Dann tue was, z.B. Belichtung starten und neue Zeit merken.

MfG

PS: Bei 5 Stellen hinterm Komma wird's aber langsam eng - micros() läuft in 4er Schritten, Das wäre die 6.te Stelle.
Habe aber die Hoffnung, daß Dir schnöde Millisekunden bei 'alle 5 Sekunden ein Bild' ausreichen werden.

Woher weis dann der Arduino das er noch 5 - (2 + 0,25 + 0,68963...) = 2,06037... sec. warten muss bis er das nächste Bild macht ohne die Zeit zwischen zwei Aufnahmen"genau" zu messen?

Gleich, wie Du das ausrechnest kann der Arduino das auch berechnen.
Mit delay() mußt den Rest nach Belichern, Bewegen und Wartezeiten dazwischen ausrechnen und dann einfach mit delay() Zeit verplempert.
Mittels millis() kannst Du direkt das Intervall kontrollieren. Also wenn die Zeit für den Intervall vorbei ist, die ganze Sequenz (Belichtung, Wartezeit, Bewegung, Wartezeit) zu starten. Sicherzustehen ist, daß die Zeit für die Sequenz kleiner ist als die Intervallzeit. So Kannst Du zwischendurch auf Tastendrücke reagieren.

Grüße Uwe

mobanis:
Ich bin als Hobby-Fotograf gerade dabei mir einen Time Lapse Slider zu bauen…

Das Thema gibt es hier gefühlt alle drei Monate, leider werden kaum fertige Lösungen vorgestellt.

mobanis:
ich denke das ich mich erst mal in die Materie millis() und was man damit anstellen kann reinarbeiten muss.

was das parallele abarbeiten angeht.

Ja, blockadearme Programmierung für eine quasi parallele Abarbeitung. Außerdem benötigst Du eine Schrittkette (=endlicher Automat, =finite state machine).

Als Vorschlag zwei Übungen:

  1. Lasse zwei LEDs mit unterschiedlicher Frequenz unabhängig voneinander blinken.

  2. Wie 1., aber eine LED soll kurz an, kurz aus, kurz an, länger aus blinken. Man sieht das bei gelben Blinklichtern von Reinigungsfahrzeugen.

Danach läßt Du zur Kontrolle immer eine LED als Herzschlag blinken. Bei Herzrhythmusstörungen muß Dein Programm in den OP.

Möglicherweise sind die MobaTools für Bewegungen und Zeitsteuerungen für Dich eine gute Wahl.

Hallo Jungen und Mädchen und Freunde der gepflegten Unterhaltung… was jetzt kommt ist nicht schön aber funktioniert… und das sogar genau so wie ich es wollte.

Ich weis das geht sicher schöner und einfacher und sauberer und schneller und und und aber für den blutigen Anfänger finde ich das schon ganz gut. Kritik egal ob positiv oder negativ sowie Verbesserungsvorschläge etc. sind sehr erwünscht…

Danke für eure Hilfe - ich werde die in Zukunft wohl öfter brauchen.

Gruß
Simon

void doTimelapse() {
  setRes(16);
  steps = (1828 * Strecke);
  pictureCounter = 0;
  lcd.clear();
  digitalWrite(motorDir, motorDirection);
  lcd.setCursor(0, 0); lcd.print("Timelapse...");
  while (pictureCounter < maxPics) {
    currentTime = millis();
    if (currentTime - previousTime >= (intervall * 1000)) {
      digitalWrite(shutterPin, HIGH);
      delay(50);
      digitalWrite(shutterPin, LOW);
      pictureCounter++;
      myTxt = String("Bilder: ") + pictureCounter + (" / ") + maxPics;
      lcd.setCursor(0, 1); lcd.print(myTxt);
      previousTime = currentTime;
    }
    else if (currentTime - previousTime >= ((belichtung * 1000) + puffer) && currentTime - previousSteptime >= (intervall * 1000)) {
      previousSteptime = currentTime;
      digitalWrite(motorEnable, LOW);
      for (int i = 0; i < steps; i++) {
        digitalWrite(motorStep, HIGH); // Output high
        delayMicroseconds(motorspeed);
        digitalWrite(motorStep, LOW); // Output low
        delayMicroseconds(motorspeed);
      }
    }
    else {
    }
  }
  digitalWrite(motorEnable, HIGH);
}

mobanis:
Danke für eure Hilfe - ich werde die in Zukunft wohl öfter brauchen.

Sorge „nur“ dafür, dass Du zwei etwas vertrackte Programmier-Strickmuster verstanden hast: Rekursion und den Endlichen Automaten. Dann wirst Du es mit den meisten Deiner Vorhaben wesentlich leichter haben :slight_smile:

Gruß

Gregor