Ausgänge mithilfe Einschaltbefehl verzögert schalten

Hallo,
Ich bin immer noch dabei, meine Lichtsteuerung der Modellbahn umzusetzen.
Die Steuerung erfolgt über ein in C# erstelltes Programm zum Master-Arduino.
Mithilfe eines RS485-Bussystems werden weitere Slave-Arduinos angesteuert.
Siehe hier.
Das Ein- /Auschalten funktioniert auch schon soweit.

Da es recht unrealistisch aussieht, wenn überall die Beleuchtung gleichzeitig angeht, dachte ich daran, die jeweiligen Slaves etwas mehr machen zu lassen, als nur zu schalten.
So soll nach dem Schaltbefehl (z.B. Tag -> Dämmerung) nach und nach die entsprechende Beleuchtung angehen z.B. erste Straßenzüge, manche Hausbeleuchtungen (werden über Attinys gesteuert), etc.
Mein Problem an der Sache ist, wie ich das am besten umsetzen soll.
LEDs nach Einschaltbefehl blinken lassen ist kein Problem. Wie aber kann ich LEDs nach Einschaltbefehl verzögert einschalten.
Z.B. Einschaltbefehl -> Haus 1 -> 20s warten -> Haus 2 -> 10s warten -> Straßenlaternen an, ...

Mir ist klar, dass sich das ganze (auch um die RS485 Kommunikation nicht zu blockieren) nur mit millis lösen lässt. Aber momentan stehe ich trotz viel Recherche und herumprobieren etwas auf dem Schlauch.

Vielleicht habt ihr einen Tipp für mich?

Vielen Dank schonmal im Voraus!

Im Anhang mein Test-Sketch.
Edit: Direkt eingefügt.

/* Beispiel-Sketch zum Testen der Lichtsteuerung
 *  Zum steuern wird der serielle Monitor verwendet. 
 *  Gibt man dort z.B. "1" ein, wird die Funktion "Dämmerung Ein" aktiviert, usw.
 */

int Daten[3] = {0, 0, 0};
const byte LED_PIN7 = 7;
const byte LED_PIN8 = 8;
const byte LED_PIN9 = 9;
const byte LED_PIN10 = 10;
const byte LED_PIN11 = 11;
const byte LED_PIN12 = 12;

// Die Blink-Class ist nicht von mir, funktioniert aber (als einzige) ohne Probleme.
class Blink {
  private:
    unsigned long prevtime;
    unsigned int zyklus;
    unsigned int an;
  public:
    Blink(unsigned int _an, unsigned int _zyklus) {
      zyklus = _zyklus;
      an = _an;
    }
    boolean currentState () {
      // sollte häufig aufgerufen werden, damit der jeweils aktuelle Zustand zurückgeliefert wird
      if (millis() - prevtime >= zyklus) {
        prevtime = millis();
      }
      if (millis() - prevtime < an) return true;
      else return false;
    }
};

Blink mittel(1000, 2000); // blinkt mit 1 Hz

void setup() {
  Serial.begin(9600);
  pinMode(LED_PIN7, OUTPUT);
  pinMode(LED_PIN8, OUTPUT);
  pinMode(LED_PIN9, OUTPUT);
  pinMode(LED_PIN10, OUTPUT);
  pinMode(LED_PIN11, OUTPUT);
  pinMode(LED_PIN12, OUTPUT);
}

void loop() {
  if (Serial.available() > 0) {
    for (int i = 0; i < 1; i++) {
      Daten[i] = Daten[i + 1];
    }
    Daten[1] = Serial.read();
  }

  if (Daten[0] == '1')
  {
    daemmerung_ein();
    //Serial.println("Tag Ein");
  }

  if ((Daten[0] == '0'))
  {
    daemmerung_aus();
    //Serial.println("Tag Aus");
  }
}

// Ab hier erfolgt die Ablaufsteuerung der einzelnen "Tageszeiten"
void daemmerung_ein()
{
  digitalWrite (LED_PIN12, HIGH);
  // hier würde ich LEDs nach und nach dazuschalten
  digitalWrite (LED_PIN9, HIGH);
  digitalWrite(LED_PIN10, mittel.currentState() );
}

void daemmerung_aus()
{
  digitalWrite (LED_PIN12, LOW);
    // hier würde ich LEDs nach und nach wieder abschalten
  digitalWrite (LED_PIN9, LOW);
  digitalWrite(LED_PIN10, LOW);
}

Moba-Lichttest.ino (2.04 KB)

Setze Deinen Code bitte direkt ins Forum. Benutze dazu Codetags (</>-Button oben links im Forumseditor oder [code] davor und [/code] dahinter ohne *).
Dann ist er auch auf mobilen Geräten besser lesbar.
Das kannst Du auch noch nachträglich ändern.

Gruß Tommy

Hi

Zeile 27f:
if (millis() - prevtime < an) return true;
      else return false;
-->
return (millis() - prevtime < an);

sollte auch 'tun' - und man hat return nur 1x in der Funktion.

Mir ist noch nicht aufgefallen, WO der Befehl her kommt - soll Das das
if (Daten[0] == '1')
sein?
Dann starte doch bei AN (oder auch bei AUS) einen Countdown - den Startwert kannst Du per random() setzen lassen - dann wird's sogar zufällig.
In loop() zählst Du diesen Countdown runter, wenn Er nicht auf Null steht.
Entweder merkst Du Dir, ob AN oder AUS gewählt wurde, oder man zählt einfach von einer positiven Zahl nach Null für AN und von einer Negativen nach Null für AUS.

static int32_t minzeit=3000;  //mindestens 1
bool Startbedingung_AN,Startbedingung_AUS;
int32_t countdown;

setup(){
...
}

loop(){
   if (Startbedingung_AN || Startbedingung_AUS){
      countdown=(random(32767-minzeit)+minzeit)*(Startbedingung_AN?1:-1);
   }
   if (countdown){
      if (countdown==-1){
         LichtAUS();
      }else if (countdown==1){
         LichtAN();
      }
      if (countdown>0){
         countdown--;
      }else{
         countdown++;
      }
   }
}

... ungetestet ...

Am einfachsten dürfte das mit dem MoToTimer aus meinen MobaTools sein. Für jede verzögert einzuschaltende Aktion legst Du dir ein Timer Objekt an. Kommt der Einschaltbefehl startest Du sie alle mit den jeweiligen Verzögerungszeiten. Mit der expired-Methode der jeweiligen Timer führst Du die Aktion dann aus.

Tommy56:
Setze Deinen Code bitte direkt ins Forum. Benutze dazu Codetags (</>-Button oben links im Forumseditor oder [code] davor und [/code] dahinter ohne *).
Dann ist er auch auf mobilen Geräten besser lesbar.
Das kannst Du auch noch nachträglich ändern.

Gruß Tommy

Gemacht :slight_smile:

postmaster-ino:
Hi

Zeile 27f:

if (millis() - prevtime < an) return true;
     else return false;
-->
return (millis() - prevtime < an);



sollte auch 'tun' - und man hat return nur 1x in der Funktion.

Mir ist noch nicht aufgefallen, WO der Befehl her kommt - soll Das das
if (Daten[0] == '1')
sein?

Ja genau. Ich übermittle aktuell nur simple Int-Werte zum Schalten.
Die endgültige Message aus meinem RS485-Programm ist länger, funktioniert aber genauso.
Dieser Sketch ist lediglich zum testen gedacht. Ich habe das "[Daten]" aus einem anderen Sketch verwendet, um meine Schaltbefehle realistisch simulieren zu können.

postmaster-ino:
In loop() zählst Du diesen Countdown runter, wenn Er nicht auf Null steht.
Entweder merkst Du Dir, ob AN oder AUS gewählt wurde, oder man zählt einfach von einer positiven Zahl nach Null für AN und von einer Negativen nach Null für AUS.

Ah, vielen Dank. So etwas in der Art habe ich auch schonmal probiert. Werde ich testen!

MicroBahner:
Am einfachsten dürfte das mit dem MoToTimer aus meinen MobaTools sein. Für jede verzögert einzuschaltende Aktion legst Du dir ein Timer Objekt an. Kommt der Einschaltbefehl startest Du sie alle mit den jeweiligen Verzögerungszeiten. Mit der expired-Methode der jeweiligen Timer führst Du die Aktion dann aus.

Etwa so?

int Timer1 = 2000;

MoToTimer myTimer;
void daemmerung_ein()
{
  digitalWrite (LED_PIN12, HIGH);
  myTimer.setTime( Timer1 );
  if ( myTimer.expired() ) {

    digitalWrite (LED_PIN9, HIGH);

  }
}

Es funktioniert bei mir leider nicht. Es leuchtet nur die LED an Pin 12.

Nein, das musst Du etwas anders machen.
Das 'setTime' kommt dahin, wo Du den Befehl empfängst. D.h. Wenn Du den Befehl empfängst, setzt Du noch gar nicht dein entsprechendes Flag, sondern startest nur den TImer.
Das Flag setzt Du dann mit timer.expired(). Der Rest kann bleiben wie er ist.

Ungefähr so:

  if (Serial.available() > 0) {
    int temp = Serial.read();
    if ( temp == '1' ) myTimer1.setTime( Time1 );
    else if ( temp == '0' ) myTimer2.setTime( Time2 );
  }

  if ( myTimer1.expired() ) daten[0] = '1';
  if ( myTimer2.expired() ) daten[0] = '0';
  
  ....

Wobei ich das mit deinem daten-Array nicht verstanden habe. Der Sinn ist einfach, das jeweilige Kommando erst verzögert in dein Daten-Array einzutragen. Je nachdem, wie dein Daten-Array funktionieren soll, muss Du das dann etwas anders machen und evtl. den Aufruf von daemmerung_ein bzw daemmerung_aus direkt vom expired abhängig machen. Das hängt davon ab, wie das genau ablaufen soll. Das habe ich, wie gesagt, noch nicht verstanden.

Aber das Grundprinzip: Jedes Kommando, das Du individuell verzögern willst, bekommt einen Timer. Der wird gestartet, wenn das Kommando empfangen wird. Und die Ausführung des Kommandos wird dann über das if( myTimer.expired ) gesteuert.

MicroBahner:
Nein, das musst Du etwas anders machen.
Das 'setTime' kommt dahin, wo Du den Befehl empfängst. D.h. Wenn Du den Befehl empfängst, setzt Du noch gar nicht dein entsprechendes Flag, sondern startest nur den TImer.
Das Flag setzt Du dann mit timer.expired(). Der Rest kann bleiben wie er ist.

Also müsste es ja eigentlich so funktionieren?
Ich setze 'set.Time' des myTimer1, wenn die entsprechende if-Abfrage true ist (es kommt eine '1' am seriellen Eingang). Der Timer1 hat eine Verzögerung von 2000ms. Anschließend wird meine Funktion tag_ein aktiviert, die nach Ablauf des Timers 'myTimer1.expired' die LED auf 1 schalten sollte.
Sorry, irgendwie verstehe ich es gerade nicht.

#include <MobaTools.h>

int Daten[3] = {0, 0, 0};
const byte LED_PIN7 = 7;
const byte LED_PIN8 = 8;
const byte LED_PIN9 = 9;
const byte LED_PIN10 = 10;
const byte LED_PIN11 = 11;
const byte LED_PIN12 = 12;

int Timer1 = 2000;

MoToTimer myTimer1;

void setup() {
  // put your setup code here, to run once:
  Serial.begin(9600);
  pinMode(LED_PIN7, OUTPUT);
  pinMode(LED_PIN8, OUTPUT);
  pinMode(LED_PIN9, OUTPUT);
  pinMode(LED_PIN10, OUTPUT);
  pinMode(LED_PIN11, OUTPUT);
  pinMode(LED_PIN12, OUTPUT);
}

void loop() {

  // put your main code here, to run repeatedly:
  if (Serial.available() > 0) {
    for (int i = 0; i < 1; i++) {
      Daten[i] = Daten[i + 1];
    }
    Daten[1] = Serial.read();
  }

  if (Daten[0] == '1')
  {
    myTimer1.setTime( Timer1 );
    daemmerung_ein();

  }

  if (Daten[0] == '0')
  {
    daemmerung_aus();
    //Serial.println("Tag Aus");
  }
  
  // ab hier kommen die Abfragen für Tag und Nacht
}

void daemmerung_ein()
{
  digitalWrite (LED_PIN12, HIGH);
  if ( myTimer1.expired() ) {
    digitalWrite (LED_PIN9, HIGH);
  }
}
void daemmerung_aus()
{
  digitalWrite (LED_PIN12, LOW);
  digitalWrite (LED_PIN9, LOW);
}

MicroBahner:
Wobei ich das mit deinem daten-Array nicht verstanden habe. Der Sinn ist einfach, das jeweilige Kommando erst verzögert in dein Daten-Array einzutragen. Je nachdem, wie dein Daten-Array funktionieren soll, muss Du das dann etwas anders machen und evtl. den Aufruf von daemmerung_ein bzw daemmerung_aus direkt vom expired abhängig machen. Das hängt davon ab, wie das genau ablaufen soll. Das habe ich, wie gesagt, noch nicht verstanden.

Aber das Grundprinzip: Jedes Kommando, das Du individuell verzögern willst, bekommt einen Timer. Der wird gestartet, wenn das Kommando empfangen wird. Und die Ausführung des Kommandos wird dann über das if( myTimer.expired ) gesteuert.

Das Array ist ein Überbleibsel aus einem anderen Sketch, aber es "funktioniert" genauso wie der spätere richtige Sketch:
Ich erhalte vom Master ein Datenarray bestehend aus (vorerst) drei Bytes. Das erste Byte enthält die Adresse des jeweiligen Arduino (hier nicht wichtig), die anderen zwei Bytes, welches "Programm" (Dämmerung, Nacht, etc.) abgespielt werden soll:

if ((buf [1] == 1) && (buf [2] == 1)) {
      tag_ein();
    }
    if ((buf [1] == 1) && (buf [2] == 0)) {
      tag_aus();
    }

    if ((buf [1] == 2) && (buf [2] == 1)) {
      daemmerung_ein();
    }
    if ((buf [1] == 2) && (buf [2] == 0)) {
      daemmerung_aus();
    }

In meine Funktion (tag_ein, _aus, etc.) sollte dann die Ablaufsteuerung direkt integriert werden,
so wie ich es anhand meines Beispielsketches auch machen will.

Du musst das setTime wirklich dort setzen, wo die '1' ankommt, also in der Abfrage der seriellen Schnittstelle. Sonst wird die Zeit ja dauernd wieder neu gesetzt.
Und mit dem expired verzögerst Du den Aufruf deiner 'Dämmerungsfunktion'.
Also etwa so ( da gilt es jetzt für beides: Ein- und Ausschalten ):

#include <MobaTools.h>

int Daten[3] = {0, 0, 0};
const byte LED_PIN7 = 7;
const byte LED_PIN8 = 8;
const byte LED_PIN9 = 9;
const byte LED_PIN10 = 10;
const byte LED_PIN11 = 11;
const byte LED_PIN12 = 12;

int Timer1 = 2000;

MoToTimer myTimer1;

void setup() {
  // put your setup code here, to run once:
  Serial.begin(9600);
  while (!Serial);
  pinMode(LED_PIN7, OUTPUT);
  pinMode(LED_PIN8, OUTPUT);
  pinMode(LED_PIN9, OUTPUT);
  pinMode(LED_PIN10, OUTPUT);
  pinMode(LED_PIN11, OUTPUT);
  pinMode(LED_PIN12, OUTPUT);
}

void loop() {

  // put your main code here, to run repeatedly:
  if (Serial.available() > 0) {
    for (int i = 0; i < 1; i++) {
      Daten[i] = Daten[i + 1];
    }
    Daten[1] = Serial.read();
    myTimer1.setTime( Timer1 );

  }

  if ( myTimer1.expired() ) {
    if (Daten[0] == '1')
    {
      daemmerung_ein();

    }

    if (Daten[0] == '0')
    {
      daemmerung_aus();
      //Serial.println("Tag Aus");
    }
  }
  // ab hier kommen die Abfragen für Tag und Nacht
}

void daemmerung_ein()
{
  digitalWrite (LED_PIN12, HIGH);
  digitalWrite (LED_PIN9, HIGH);
}
void daemmerung_aus()
{
  digitalWrite (LED_PIN12, LOW);
  digitalWrite (LED_PIN9, LOW);
}

Im Gegensatz zu deinem ursprünglichen Testsketch wird deine daemmerungsfunktion aber jetzt nur noch 1x beim Ablauf des Timers aufgerufen ( was - so wie sie jetzt sind - auch sinnvoll ist. ).
Sollen die im loop ständig aufgerufen werden, weil da noch weitere zeitgesteuerte Dinge passieren sollen, musst Du es so machen wie in meinem Post #6

Edit: die Abfrage des Timerablaufs kannst Du prinzipiell auch so machen wie in deinem Sketch. Wichtig ist aber, dass das Setzen der Zeit nur einmal beim Empfang des Befehls gemacht wird.

Wunderbar. Jetzt funktioniert es so, wie ich es mir vorstelle! Vielen Dank für deine Hilfe.
Ich stand echt auf dem Schlauch.
Klar muss die Zeitsetzung zur Abfrage (duckundweg).

Super, dann viel Erfolg. Ansonsten weist Du ja, wo Du fragen kannst :wink:

Mittlerweile habe ich versucht, den neuen Code in meinen Sketch zu übernehmen.
Leider bislang ohne Erfolg, denn scheinbar verhindert die verwendete RS485-Library RS485_protocol von Nick Gammon den Start der erstellten Timer.
Ich vermute ein delay irgendwo in der Library.
LEDs ohne Timer werden direkt eingeschaltet, nur die mit Timer (im Sketch z.B. Pin 7, 9 und 11) bleiben dunkel. Beim Ausschalten verhält es sich genauso.
Der prinzipielle Aufbau funktioniert also, nur die Timer und die Blink-Klasse funktionieren nicht.

Auf der vorgenannten Seite gibt es auch eine nonblocking-Version der Library, die jedoch ebenfalls kein (sichtbares) Ergebnis liefert.
Hat jemand vielleicht eine Idee, ob zeitkritische Events mit dieser Library trotzdem möglich sind?
Ansonsten würde ich mir ein eigenes Sende- und Empfangsprotokoll erstellen.

Master (Arduino Mega)

// Master
#include <RS485_protocol.h>
#include <SoftwareSerial.h>

const byte ENABLE_PIN = 4;
const byte LED_PIN = 13;

SoftwareSerial rs485 (2, 3);  // receive pin, transmit pin

int inPin = 46;   // choose the input pin (for a pushbutton)
int ledPin = 48; // choose the pin for the LED

int i, pinNummer, pinPegel;

// callback routines

void fWrite (const byte what)
{
  rs485.write (what);
}

int fAvailable ()
{
  return rs485.available ();
}

int fRead ()
{
  return rs485.read ();
}

void setup()
{
  rs485.begin (28800);
  pinMode (ENABLE_PIN, OUTPUT);  // driver output enable
  pinMode (LED_PIN, OUTPUT);  // built-in LED
  pinMode(inPin, INPUT);    // declare pushbutton as input
  pinMode(ledPin, OUTPUT);
  //Code für C#-Programm:
  Serial.begin(28800);
}  // end of setup


void loop()
{

  //serielle Daten von C#
  if ( Serial.available() > 1 ) {

    pinNummer = Serial.read(); // 1 ... 3
    pinPegel = Serial.read();  // 0 oder 1

    if (pinNummer == 1) {  // Test-LED
      digitalWrite(48, pinPegel);
    }


    // assemble message

    byte msg [] = {
      1,    // Adresse Arduino 1
      pinNummer,    // Modus: Tag(1)/ Daemmerung(2)/ Nacht(3)
      pinPegel // Ein/Aus 1/0
    };

    // send to slave
    digitalWrite (ENABLE_PIN, HIGH);  // enable sending
    sendMsg (fWrite, msg, sizeof msg);
    Serial.flush ();
    digitalWrite (ENABLE_PIN, LOW);  // disable sending

    // receive response (momentan nicht genutzt)
    /*byte buf [10];
      byte received = recvMsg (fAvailable, fRead, buf, sizeof buf);

      digitalWrite (LED_PIN, received == 0);  // turn on LED if error
    */

  }
}  // end of loop

Slave (Arduino UNO)

// Slave
#include <SoftwareSerial.h>
#include <RS485_protocol.h>

#include <MobaTools.h>
int Timer1 = 2000;
int Timer2 = 4500;
int Timer3 = 8000;
MoToTimer myTimer1;
MoToTimer myTimer2;
MoToTimer myTimer3;

/*class Blink {
  private:
    unsigned long prevtime;
    unsigned int zyklus;
    unsigned int an;
  public:
    Blink(unsigned int _an, unsigned int _zyklus) {
      zyklus = _zyklus;
      an = _an;
    }
    boolean currentState () {
      // sollte häufig aufgerufen werden, damit der jeweils aktuelle Zustand zurückgeliefert wird
      if (millis() - prevtime >= zyklus) {
        prevtime = millis();
      }
      return (millis() - prevtime < an);
    }
  };

  Blink mittel(1000, 2000); // blinkt mit 1 Hz
*/

SoftwareSerial rs485 (2, 3);  // receive pin, transmit pin
const byte ENABLE_PIN = 4;

const byte LED_PIN5 = 5;
const byte LED_PIN6 = 6;
const byte LED_PIN7 = 7;
const byte LED_PIN8 = 8;
const byte LED_PIN9 = 9;
const byte LED_PIN10 = 10;
const byte LED_PIN11 = 11;
const byte LED_PIN12 = 12;


void fWrite (const byte what)
{
  rs485.write (what);
}

int fAvailable ()
{
  return rs485.available ();
}

int fRead ()
{
  return rs485.read ();
}

void setup()
{
  rs485.begin (28800);
  Serial.begin(9600);
  pinMode (ENABLE_PIN, OUTPUT);  // driver output enable

  pinMode(LED_PIN5, OUTPUT);
  pinMode(LED_PIN6, OUTPUT);
  pinMode(LED_PIN7, OUTPUT);
  pinMode(LED_PIN8, OUTPUT);
  pinMode(LED_PIN9, OUTPUT);
  pinMode(LED_PIN10, OUTPUT);
  pinMode(LED_PIN11, OUTPUT);
  pinMode(LED_PIN12, OUTPUT);

}

void loop()
{


  if (rs485.available() > 0) {

    myTimer1.setTime( Timer1 );
    myTimer2.setTime( Timer2 );
    myTimer3.setTime( Timer3 );

  }


  byte buf [10];

  byte received = recvMsg (fAvailable, fRead, buf, sizeof (buf));

  if (received)
  {

    if (buf [0] != 1)
      return;  // not my device

    if ((buf [1] == 1) && (buf [2] == 1)) {
      tag_ein();
    }
    if ((buf [1] == 1) && (buf [2] == 0)) {
      tag_aus();
    }

    if ((buf [1] == 2) && (buf [2] == 0)) {
      daemmerung_aus();
    }
    if ((buf [1] == 2) && (buf [2] == 1)) {
      daemmerung_ein();
    }

    if ((buf [1] == 3) && (buf [2] == 0)) {
      nacht_aus();
    }
    if ((buf [1] == 3) && (buf [2] == 1)) {
      nacht_ein();
    }

  }  // end if something received

}  // end of loop

void tag_ein()
{
  digitalWrite (LED_PIN11, HIGH);
  digitalWrite (LED_PIN7, HIGH);

}

void tag_aus()
{
  digitalWrite (LED_PIN11, LOW);
  digitalWrite (LED_PIN7, LOW);
}

void daemmerung_ein()
{
  digitalWrite (LED_PIN12, HIGH);
  if ( myTimer1.expired() ) {
    digitalWrite (LED_PIN9, HIGH);
  }
  if ( myTimer2.expired() ) {
    digitalWrite (LED_PIN11, HIGH);
  }
  if ( myTimer3.expired() ) {
    digitalWrite (LED_PIN7, HIGH);
  }
  digitalWrite (LED_PIN8, HIGH);
  //digitalWrite(LED_PIN8, mittel.currentState() );
}

void daemmerung_aus()
{
  if ( myTimer3.expired() ) {
    digitalWrite (LED_PIN12, LOW);
  }
  digitalWrite (LED_PIN11, LOW);
  if ( myTimer2.expired() ) {
    digitalWrite (LED_PIN7, LOW);
  }
  digitalWrite (LED_PIN8, LOW);

  if ( myTimer1.expired() ) {
    digitalWrite (LED_PIN9, LOW);
  }
}

void nacht_ein()
{
  digitalWrite (LED_PIN8, HIGH);
  digitalWrite (LED_PIN10, HIGH);
}

void nacht_aus()
{
  digitalWrite (LED_PIN8, LOW);
  digitalWrite (LED_PIN10, LOW);
}

Leider wird es wohl etwas schwierig, die Sketche ohne das dazugehörige C#-Programm zu testen, aber vielleicht kann man ja auch so erkennen, ob es eine Chance für die Library gibt :wink:

Mobione:
Leider bislang ohne Erfolg, denn scheinbar verhindert die verwendete RS485-Library RS485_protocol von Nick Gammon den Start der erstellten Timer.
Ich vermute ein delay irgendwo in der Library.

Du kannst ganz sicher sein (das kannst Du selbst im Quelltext prüfen) das Nick Gammon kein delay in seiner Lib hat.

Gruß Tommy

Hi

Aber auch keine Timer o.Ä. - Die den MoBaTools in die Quere kommen könnten (habe mir den EggTimer noch nicht näher angesehen, vermute aber ebenfalls eine millis()-Konstruktion dahinter).

@TO
Stopfe Deinen Sketch mit Serial.print voll - damit Du siehst, wann Er Wo abbiegt, OB überhaupt geprüft wird, ob die Zeit vorbei ist oder ob Diese (wie auch immer) zuvor resettet wird ... kA.

MfG

Du startest die Zeiten jedesmal wenn irgendwas auf der RS485 Schnittstelle ankommt:

  if (rs485.available() > 0) {

    myTimer1.setTime( Timer1 );
    myTimer2.setTime( Timer2 );
    myTimer3.setTime( Timer3 );

  }

Das ist doch bestimmt nicht das, was Du erreichen willst. Du musst schon prüfen, was ankommt, und die Zeit nur spezifisch für den jeweiligen Befehl starten.

Hallo,

ich hab da vor einiger Zeit mal was rumgebastelt, und mir eine Lib erstellt die verschiedenes zur Verfügung stellt.
delayOff, delayOn,blink,pulsOn,cycle

eventuell kommst Du damit besser zurecht

Hier mal mein Test-Sketch und die Lib im Anhang

beide Lib Dateien im projekt Ordner oder im \Arduino\libraries\Timer Ordner

Heinz

/*
   Beispiel zu Timer Lib
*/
#include "timer.h"

Timer timer1;// Instanzen von Timer ertellen
Timer timer2;
const byte led = 13;
const byte btn = 3;// Taster
bool bn;// Hilfsflag für Taster
uint32_t aktual;

void setup() {
  // put your setup code here, to run once:
  pinMode (led, OUTPUT);
  pinMode(btn, INPUT_PULLUP);
  Serial.begin(9600);
}

void loop() {
  bn = !digitalRead(btn);// Taster lesen

  //  digitalWrite(led, timer1.pulsOn(bn, 3000));
  digitalWrite(led, timer1.delayOn(bn, 3000));
  // digitalWrite(led,timer1.delayOff(bn,3000));
  // digitalWrite(led, timer1.blink(true, 1000));
 

  // Zyklischer Aufruf
  if (timer2.cycle(true, 1000)) {
    Serial.print("actual Wert:"); Serial.println(millis());

  }

 
}

Timer.zip (2 KB)

Tommy56:
Du kannst ganz sicher sein (das kannst Du selbst im Quelltext prüfen) das Nick Gammon kein delay in seiner Lib hat.

Gruß Tommy

Ok, dann kann es daran nicht liegen.
Ich hatte in der RS485_protocol.h ein unsigned long timeout = 500); gefunden, daher meine Vermutung. Merkwürdigerweise funktioniert die (momentan auskommentierte) Blink-Class auch nicht.

MicroBahner:
Du startest die Zeiten jedesmal wenn irgendwas auf der RS485 Schnittstelle ankommt:

  if (rs485.available() > 0) {

myTimer1.setTime( Timer1 );
    myTimer2.setTime( Timer2 );
    myTimer3.setTime( Timer3 );

}


Das ist doch bestimmt nicht das, was Du erreichen willst. Du musst schon prüfen, was ankommt, und die Zeit nur spezifisch für den jeweiligen Befehl starten.

Für den Anfang würden mir diese drei Timer für viele Szenarien reichen. (In Zukunft vielleicht ein paar mehr...).
Ich möchte eigentlich, so wie es im Beispielsketch schon funktioniert, bestimmte Ausgänge zeitlich verzögert ein- und ausschalten bzw. mit Effekten (Blinken, etc.) versehen.
Die drei Timer stehen deshalb ganz am Anfang, weil ich (erstmal) erreichen wollte, dass die Timer funktionieren, wenn überhaupt irgendetwas am Port ankommt (was momentan nicht passiert).
Erst wenn das soweit funktioniert, würde ich abfragen, was ankommen muss, damit entsprechende Timer aktiv werden.
Also z.B.

if (buf [1] == 1) {
     myTimer1.setTime( Timer1 );
}

Rentner:
Hallo,

ich hab da vor einiger Zeit mal was rumgebastelt, und mir eine Lib erstellt die verschiedenes zur Verfügung stellt.
delayOff, delayOn,blink,pulsOn,cycle

eventuell kommst Du damit besser zurecht

Hier mal mein Test-Sketch und die Lib im Anhang

beide Lib Dateien im projekt Ordner oder im \Arduino\libraries\Timer Ordner

Heinz

Vielen Dank, werde ich ausprobieren!

Mobione:
Vielen Dank, werde ich ausprobieren!

Ich habe die Library jetzt getestet, jedoch fast das gleiche Ergebnis wie bei den MobaTools festgestellt:
Nach einschalten von z.B. "Dämmerung" ist die LED aus. Nach den festgelegten drei Sekunden Wartezeit passiert leider nichts.
Aber: Sobald ich "Dämmerung Aus" schalte und dann wieder "Dämmerung Ein" leuchtet die LED.
=> Der Timer zählt also im Hintergrund, schaltet aber die LED nicht auf High. Erst bei nochmaligem Senden des Einschaltbefehls -Timer ist mittlerweile abgelaufen- leuchtet die LED.

Mithilfe Serial.Print habe ich mir mal angesehen, ob mein ankommendes Signal dauerhaft kommt oder nicht.
Ein Signal kommt tatsächlich nur, sobald ich über das C#-Programm ein entsprechendes Signal sende.
Kann es daran liegen, dass z.B. die Bedingung (buf [1] == 2) nicht dauerhaft erfüllt ist, um auf den Timer zugreifen zu können?

if ((buf [1] == 2) && (buf [2] == 1)) {
      daemmerung_ein();
      digitalWrite(LED_PIN12, timer1.delayOn((buf [1] == 2), 3000));
       Serial.println(buf [1]);

Eigentlich musst Du das:

  if ( myTimer1.expired() ) {
    digitalWrite (LED_PIN9, HIGH);
  }

Vollkommen ohne eine if-Bedingung im loop ausführen. Wenn der Timer nicht gestartet wurde, wird das auch nie wahr, und deine Led wird nicht eingeschaltet. Da, wo Du deinen Befehl abfragst, da musst Du den Timer abhängig vom Befehl starten. Die Abfrage des Timerablaufes darfst Du dann nicht mehr vom Befehl abhängig machen. Der ist da ja schon längst vorbei.

Hallo,

du musst das Timer Objekt schon immer bearbeiten , wenn Du für die "ein" Bedingung merere Bedingungen logisch verknüpfen willst , muss das vorher gemacht werden

etwa so

bool ein;// z.b Globale Hilfsvariable 

if ((buf [1] == 2) && (buf [2] == 1)) {
      daemmerung_ein();
      ein=true;
       Serial.println(buf [1]);
}

digitalWrite(LED_PIN12, timer1.delayOn(ein, 3000));

Natürlich musst Du die Variable "ein" auch irgendwann wieder auf false setzten wenn die LED aus gehen soll. Im einfachsten Fall mit einem else hinter dem if, aber das weiss ich nicht.

leztlich ist das bei der Lösung von MicroBahner sicher ähnlich.

Heinz