Go Down

Topic: Zeitsteurung mit millis (Read 1 time) previous topic - next topic

Pauli58

Jan 22, 2018, 06:59 pm Last Edit: Jan 22, 2018, 11:13 pm by uwefed Reason: add CODE TAGs </>
Hallo,

ich möchte ein Ventil nit 2 Zeitvorgaben (Pausenzeit und Betriebszeit) ansteurn, die ich mittels Drehpotensiometer verändern kann. Dazu habe ich folgenden Sketch verfasst.
Die Sensorwerte werden erfasst aber der Relaispin ist immer auf HIGH. Kann den Fehler leider nicht finden.
Die auskommentierte Variante mit delay funktioniert zwar aber eigenartiger Weise nicht den Sensorwerten entsprechend sondern wesentlich langsamer.
Vielleicht kann wer helfen.
Code: [Select]
int PausenGeberPin = A0;
int BetriebszeitGeberPin = A1;
unsigned long PausenDauer = 0;
unsigned long BetriebsDauer = 0;
int RelaisPin = 13;
unsigned long VergangenePausenZeit=0;
unsigned long VergangeneBetriebsZeit=0;

void setup()
{
 Serial.begin(9600);
 pinMode(PausenGeberPin, INPUT);
 pinMode(BetriebszeitGeberPin, INPUT);
 pinMode(RelaisPin, OUTPUT);
}

void loop()
{
 PausenDauer = analogRead(PausenGeberPin)*1.0;
 BetriebsDauer = analogRead(BetriebszeitGeberPin)*1.0;
 Serial.print("PausenDauer: ");
 Serial.println(PausenDauer);
 Serial.print("BetriebsDauer: ");
 Serial.println(BetriebsDauer);
 Serial.println(VergangenePausenZeit);
 Serial.println(VergangeneBetriebsZeit);
 Serial.println("");

 

 if (millis() - VergangenePausenZeit > PausenDauer)
 {
   digitalWrite(RelaisPin, LOW);
   if (millis() - VergangeneBetriebsZeit > BetriebsDauer)
   {
       VergangenePausenZeit = millis();
       VergangeneBetriebsZeit = millis();
       digitalWrite(RelaisPin, HIGH);
    }
 }

 // delay(PausenDauer);
 //digitalWrite(RelaisPin, LOW);
 //delay(BetriebsDauer);
 //digitalWrite(RelaisPin, HIGH);
 
}

MfG

Gerald

combie

#1
Jan 22, 2018, 07:18 pm Last Edit: Jan 22, 2018, 07:22 pm by combie
Quote
Code: [Select]
 

  if (millis() - VergangenePausenZeit > PausenDauer)
  {
    digitalWrite(RelaisPin, LOW);
    if (millis() - VergangeneBetriebsZeit > BetriebsDauer)
    {
        VergangenePausenZeit = millis();
        VergangeneBetriebsZeit = millis();
        digitalWrite(RelaisPin, HIGH);
     }
  }
Diese Verschachtlung dürfte ein Problem darstellen...

Ich könnte dir meine TimerLib anbieten...
Aber ...
Kluge Worte zu schreiben, ist schwer.
Schon ein einziger Buchstabendreher, kann alles urinieren.

Pauli58

#2
Jan 22, 2018, 09:44 pm Last Edit: Jan 22, 2018, 11:14 pm by uwefed Reason: add CODE TAGs </>
Habs hinbekommen,

If-Verschachtelung entfernt und in der zweiten Abfage die Zeiten addiert.
Soll mal ein automatischer Kettenöler fürs Motorrad werden.

Code: [Select]
int PausenGeberPin = A0;
int BetriebszeitGeberPin = A1;
unsigned long PausenDauer = 0;
unsigned long BetriebsDauer = 0;
int RelaisPin = 13;
unsigned long VergangeneZeit=0;

void setup()
{
 Serial.begin(9600);
 pinMode(PausenGeberPin, INPUT);
 pinMode(BetriebszeitGeberPin, INPUT);
 pinMode(RelaisPin, OUTPUT);
}

void loop()
{
 PausenDauer = analogRead(PausenGeberPin)*1.0;
 BetriebsDauer = analogRead(BetriebszeitGeberPin)*1.0;
 Serial.print("PausenDauer: ");
 Serial.println(PausenDauer);
 Serial.print("BetriebsDauer: ");
 Serial.println(BetriebsDauer);

 if (millis() - VergangeneZeit > PausenDauer)
 {
   digitalWrite(RelaisPin, LOW);
 }

 if (millis() - VergangeneZeit > PausenDauer+BetriebsDauer)
 {
     VergangeneZeit = millis();
     digitalWrite(RelaisPin, HIGH);
  }
}

TelosNox

Du kannst dir mal anschauen, wie ich das bei meiner PWM gelöst hab.
https://github.com/TelosNox/noxmatic/blob/master/Arduino/sketches/noxmatic/Pwm.h

Code: [Select]
  void runPwm() {
    unsigned long currentMillis = millis();
 
    if (nextMillis < currentMillis) {
      if (!pinActive && power > 0) {
        activatePin();
        nextMillis = currentMillis + calculateOnMillis();
      } else {
        deactivatePin();
        nextMillis = currentMillis + calculateOffMillis();
      }
    }
}


calculateOnMillis würde bei dir basierend auf dem BetriebszeitgeberPin die Einschaltzeit zurückgeben.
calculateOffMillis würde bei dir basierend auf dem PausenGeberPin die Pausenzeit zurückgeben.
power > 0 musst du noch rausschmeißen, das ist für dich uninteressant.

combie

#4
Feb 22, 2018, 10:34 am Last Edit: Feb 22, 2018, 10:36 am by combie
Quote
nextMillis = currentMillis + calculateOnMillis();
Tut mir leid, das sagen zu müssen: Das ist ein fataler Fehler!
Der wird dir recht pünktlich nach 49 Tagen Dauerlauf auf die Füße fallen.

Also:
Bitte du auch "BlinkWithoutDelay" lesen.
So oft, bis es verstanden ist, wieso da kein + auftauchen darf.
Kluge Worte zu schreiben, ist schwer.
Schon ein einziger Buchstabendreher, kann alles urinieren.

TelosNox

Wenn du 49 Tage am Stück Motorrad fährst, ohne auch nur einmal die Zündung aus zu machen, dann darfst du dir da auch gerne eine komplexere Lösung einfallen lassen.

Dass die Millis irgendwann überlaufen ist mir durchaus bewusst. Solange man keinen Dauerbetrieb plant (und das ist bei Pauli nicht der Fall), ist das absolut untragisch.

Dennoch ist es gut, dass du drauf hinweist. Nicht dass sich jemand meine Lösung für einen Dauerläufer abschaut  ;)

Tommy56

Besser: Nicht dass überhaupt einer auf die Idee kommt, diese "Lösung" einzusetzen.

Gruß Tommy
"Wer den schnellen Erfolg sucht, sollte nicht programmieren, sondern Holz hacken." (Quelle unbekannt)

Doc_Arduino

genau, lieber von Anfang an richtig machen, sonst droht später nur Kaos und man weiß nicht warum. Das einzigste Limit was es gibt betrifft die maximale Wartezeit von 49 Tagen, also das maximale einstellbare Intervall oder Pause oder wie auch immer man millis nutzen möchte. Der µC selbst kann ewig laufen trotz Werteüberlauf und macht alles richtig.
Tschau
Doc Arduino '\0'

Messschieber auslesen: http://forum.arduino.cc/index.php?topic=273445
EA-DOGM Display - Demos: http://forum.arduino.cc/index.php?topic=378279

TelosNox

#8
Feb 22, 2018, 03:31 pm Last Edit: Feb 22, 2018, 03:36 pm by TelosNox
Es ist aber ineffizient, wenn ich bei jedem Durchlauf des Loops zuerst rechne und dann vergleiche.
1x rechnen reicht aus.

Wenn mir der Überlauf tatsächlich wichtig ist, dann kann ich das ja abfangen:

Code: [Select]

static unsigned long nextMillis = 0;
unsigned long currentMillis = millis();

if (nextMillis < currentMillis) {
  //hier der doublecheck
  unsigned long lastMillis = nextMillis - delay;
  if (currentMillis - lastMillis >= delay) {
    //jetzt bin ich mir sicher, dass auch nach überlauf mein delay vergangen ist
    nextMillis = currentMillis + delay;
  }
}


Üblicherweise sind unsere Delays gegenüber der Loop Geschwindigkeit ziemlich lang. Das heißt die Loop läuft einige tausend male durch, bevor das delay überhaupt erreicht ist. Oder anders gesagt: die erste Condition wird viel öfter false liefern, als true.
Somit muss der Chip viel seltener rechnen. Ja der Code erzeugt im Bereich des Überlaufs zusätzliche Last, entlastet den Chip aber die ganze übrige Zeit. Wenn man davon ausgeht, dass so ein Delay sich im Bereich von Sekunden bewegt, dann ist die doppelte Prüfung nur für wenige Sekunden oder Minuten überhaupt Zusatzlast und ansonsten ist es von der Performance her ein Vorteil.

Natürlich kann man die Performanceaspekte auch komplett ignorieren. Das entspricht aber nicht meinem eigenen Anspruch an Code.

/edit
mir fällt auf, dass man das noch geschickter formulieren kann, wenn der Speicherbedarf nicht relevant ist:

Code: [Select]

static unsigned long nextMillis = 0;
static unsigned long prevMillis = 0;

unsigned long currentMillis = millis();

if (nextMillis < currentMillis && currentMillis - lastMillis >= delay) {
  lastMillis = currentMillis;
  nextMillis = currentMillis + delay;
  }
}

sschultewolter

Code: [Select]
void loop() {
static unsigned long lastMillis = 0;
const unsigned long interval = 1000;

if(millis() - lastMillis > interval) {
// ... mache etwas

lastMillis = millis();
}
}


Gibt eigentlich sinnvoll nur dieses. Wie Doc bereits geschriebene hat, wenn die Interval nicht > 49Tage entspricht, geht das ganze durchaus ohne viel zu schreiben.

Mit AVR entlasten hat das ganze wenig zu tun. Der rechnet den millis() Wert im Hintergrund sowie über die ISR mit. Du lässt dir mit millis() lediglich den aktuellen Wert zurückgeben.
Orginal Atmel AVRISP mkII zu verkaufen. Anfrage per PN ;)

TelosNox

Es geht doch nicht um millis() an sich.

Es geht darum, ob ich in jedem Durchlauf "millis() - lastMillis" rechne oder ob ich das nur dann tue, wenn ich tatsächlich in den Block reinkomme.

noiasca

OT: Um wie viel "Entlastet" du den AVR wenn du das - lastMillis weglässt ...
wie nutzt du die "gewonnen" Performance?

Da sehe ich mehr Ineffizienz in deinem Webserver. Da könntest du so richtig viel rausholen, aber auch dazu gilt - was macht man dann mit dem gesparten. Wenn es für dich so funktioniert ist ja alles gut. Aber für eine Zeitabfrage empehle ich dein Konstrukt nicht weiter.


@TO: wie Combie schon schrieb:

Bitte du auch "BlinkWithoutDelay" lesen.

DE: Wie man Fragen postet:
1. was hat man (Sketch und Hardware)
2. was SOLL es machen
3. was macht es: IST (Fehlverhalten, Fehlermeldungen, Serial.Output ...)
4. Eine Frage stellen bzw. erklären was man erwartet

sschultewolter

Bei Lust könnte das ja mal einer auswerten. Habe keinen Arduino oder Logik Analyser hier. Der Effekt wird so geschwinden gering sein, dass man damit keinerlei andere Aktion zusätzlich ausführen kann. 
Orginal Atmel AVRISP mkII zu verkaufen. Anfrage per PN ;)

combie

Ich finde, um Effizienz, kann es erst in zweiter Linie gehen.

§1 ist immer die zuverlässige Funktion

Kluge Worte zu schreiben, ist schwer.
Schon ein einziger Buchstabendreher, kann alles urinieren.

TelosNox

Die "Entlastung" ist garantiert marginal und wirkt sich wahrscheinlich nichtmal merklich aus. Dafür müsste man vermutlich tausend solcher Delays einbauen, damit das auffällt.
Allerdings habe ich mir angewöhnt prinzipiell unnötige Aufrufe bzw. Operationen zu vermeiden. Man weiß nicht immer, wie teuer ein Aufruf tatsächlich ist oder ob er nicht irgendwann mal teuer wird. Das gehört zum Job einfach dazu.

Was den Webserver angeht: Warum muss der effizient sein? Der Webserver ist für genau 30sec aktiv und wenn dann niemand conntected hat (was zu 99,9% der Fall ist), dann schaltet er sich komplett ab.

Mir ist es total wumpe, wie jemand sein Delay codet. Ich habe erläutert, warum ich es für richtig halte, die rechenoperation nicht als Teil der condition zu machen und da geht es mir ums Prinzip und nicht um das bischen, was diese Operation jetzt ausmachen würde.
Wenn man sich die unnötige Ausführung der Operationen angewöhnt, dann macht man das ggf. später auch mit wirklich teuren Operationen und dann wundert man sich, warum die Kiste nicht mehr läuft.
Ich hab das im Job oft genug erlebt, dass Code zwar funktioniert hat, wenn dann aber auf einmal die Datenmenge gestiegen ist, wurde es sehr schnell sehr langsam. Da waren die Augen dann schnell ganz groß.

Go Up