Arduiono Projekt für Modellbau

Hallo Zusammen,

ich bin noch komplett neu in der Thematik Arduino - und versuche gerade, die ersten Schritte an einem Online-Simulator, bis meine eigene Test-Hardware da ist.

Ich möchte gerne ein Modellbau-Projekt mit hilfe des Arduino beleuchten - dabei sollen verschiedene Leuchteffekte möglichst "unabhängig" voneinander durchgeführt werden... hierzu habe ich mir bereits Anleitungen und Hilfen zum Thema "blink without delay" angeschaut.

Eine der Anleitungen, welche hier im Forum gepostet wurde konnte ich auch einiger Maßen für meine zwecke weiter verwenden und anpassen... doch leider bin ich noch nicht so ganz dahinter gestiegen - und es funktioniert noch nicht in allen Bereichen, wie ich es gerne hätte :frowning:

Hier ist mal mein bisheriger Code, basierend auf einem Beispiel aus dem Forum hier (blinken & Faden einer LED)

unsigned long blink_StartMillis;
unsigned long fadeWarpEngine_StartMillis;
unsigned long fadeMainImpulseEngine_StartMillis;
unsigned long currentMillisNavigationLight;
unsigned long currentMillisWarpEngine;
unsigned long currentMillisMainImpulseEngine;

//blink period for Navigation Lights
const unsigned long blinkPeriod_NavigationLights = 800; 
//fade period for WarpEngine Lights 
const unsigned long fadePeriod_WarpEngine = 100;  
//fade period for Main Impulse Engine Lights
const unsigned long fadePeriod_MainImpulseEngine = 100;

// Define the PINs for each LED / Section
const byte NavigationLights = 13;
const byte WarpEngineLights = 10;
const byte MainImpulseEngineLights = 12;

//initial brightness of LED
byte brightness_WarpEngine = 0;  
byte brightness_MainImpulseEngine = 0;

//amount to change PWM value at each change
byte increment_WarpEngine = 1;  
byte increment_MainImpulseEngine = 1;

void setup()
{
  Serial.begin(115200);  //start Serial in case we need to print debugging info
  
  // define Pin Modes
  pinMode(NavigationLights, OUTPUT);
  pinMode(WarpEngineLights, OUTPUT);
  pinMode(MainImpulseEngineLights, OUTPUT);

  //start time of blinking LED
  blink_StartMillis = millis();  
  //start time of fading LED (Warp Engine)
  fadeWarpEngine_StartMillis = millis();  
  //start time of fading LED (Main Impulse Engine)
  fadeMainImpulseEngine_StartMillis = millis();
}

void loop()
{
  //get the current time - save individual for each
  currentMillisWarpEngine = millis();  
  currentMillisMainImpulseEngine = millis();
  currentMillisNavigationLight = millis();

  // call the individual functions
  NavigationLightBlinking();
  fadeWarpEngineIn();
  fadeMainImpulseEngineIn();
}

//function to blink an LED if the blink period has ended
void NavigationLightBlinking()  
{
  if (currentMillisNavigationLight - blink_StartMillis >= blinkPeriod_NavigationLights)  //test whether the period has elapsed
  {
    digitalWrite(NavigationLights, !digitalRead(NavigationLights));  //if so, change the state of the LED
    blink_StartMillis = currentMillisNavigationLight;  //IMPORTANT to save the start time of the current LED state.
  }
}

//function to fade the Warp-Engine LED
void fadeWarpEngineIn()    
{
  if (brightness_WarpEngine < 255) {
    if (currentMillisWarpEngine - fadeWarpEngine_StartMillis >= fadePeriod_WarpEngine)  //test whether the period has elapsed
    {
      analogWrite(WarpEngineLights, brightness_WarpEngine);    //set the brightness of the LED
      brightness_WarpEngine += /*brightness_WarpEngine +*/ increment_WarpEngine;    //will wrap round because brightness is an unsigned data type
      fadeWarpEngine_StartMillis = currentMillisWarpEngine;  //IMPORTANT to save the start time of the current LED state.
    }
  }
}

//function to fade the Main Impulse-Engine LED
void fadeMainImpulseEngineIn()    
{
  if (brightness_MainImpulseEngine < 255) {
    if (currentMillisMainImpulseEngine - fadeMainImpulseEngine_StartMillis >= fadePeriod_MainImpulseEngine)  //test whether the period has elapsed
    {
      analogWrite(MainImpulseEngineLights, brightness_MainImpulseEngine);    //set the brightness of the LED
      brightness_MainImpulseEngine += /*brightness_MainImpulseEngine +*/ increment_MainImpulseEngine;    //will wrap round because brightness is an unsigned data type
      fadeMainImpulseEngine_StartMillis = currentMillisMainImpulseEngine;  //IMPORTANT to save the start time of the current LED state.
    }
  }
}

Nun zu meinen aktuellen Problemen:

Obwohl der Code für "fadeMainImpulseEngineIn()" sowie "fadeWarpEngineIn()" identisch ist - und auch die jeweiligen Parameter identisch sind, werden beide Funktionen nacheinander aufgerufen.

Beide sollten aber quasi zeitgleich starten - und sich dann höchstens durch unterschiedlich lange Fade-In Zeiten unterscheiden.
Soweit ich es sehen kann, definieren

//fade period for WarpEngine Lights 
const unsigned long fadePeriod_WarpEngine = 100;  
//fade period for Main Impulse Engine Lights
const unsigned long fadePeriod_MainImpulseEngine = 100;

aber sowohl die Zeit, wann das Fade-In beginnt.

Mit

//amount to change PWM value at each change
byte increment_WarpEngine = 1;  
byte increment_MainImpulseEngine = 1;

wird definiert, um wieviele "schritte" das FadeIn pro zyklus hochlaufen soll, wenn ich mich nicht irre... (bis max. 255 = volle Helligkeit)

Nehmen wir also an, dass const byte WarpEngineLights = 10; 5 sekunden brauchen soll um von 0 auf 100% hochgedimmt zu werden, und const byte MainImpulseEngineLights = 12; soll Zeitgleich starten, aber in 2,5 Sekunden bei 100% sein... wie müsste ich meinen Code anpassen?

Aktuell scheint es so zu sein, dass const byte MainImpulseEngineLights = 12; erst nach ca. 13 Sekunden eingeschaltet wird.
Wieso dies so ist, konnte ich aktuell leider auch noch nicht wirklich erkennen.

Vermutlich ist es wenig Sinnvoll, für jede Aktion ein eigenes "currentMillis" zu nutzen - ich war mir aber nicht Sicher, ob es nicht kontraproduktiv ist, currentMillis nur einmal zu nutzen und in allen Funktionien einzusetzen - allerdings bleibt das Verhalten identisch, wenn ich nur currentMillis = millis(); verwende.

Ah und noch etwas...
Für eine Idee, wie ich zusätzlich ein Blitzlicht hinbekommen kann, das wäre die nächste Ergänzung :slight_smile:
Also: LED soll für ein paar ms an sein (ggf. von 0 auf 255 einfaden) - dann für 2 sekunden aus - und das ganze in einem loop.

Danke schon mal und Grüße,
Christoph

vertausche mal diese beiden Zeilen
ebenso in der anderen Routine

Hi, Danke für die Rückmeldung.
Leider hat das tauschen der Zeilen nicht wirklich etwas gebracht, aber ich habe inzwischen einen anderen Code-Schnipsel hier im Forum gefunden, welches zumindest für andere Bereiche hilfreich erscheint :slight_smile:

Vielleicht bringt mich das weiter :slight_smile:

Ja auch.
Vor allem aber hast Du ein Problem, das Deine Zeiten auch auseinander laufen, wenn beide Pausenzeiten gleich wären.

Versuch mal, ob das besser geht:

unsigned long blink_StartMillis;
unsigned long fadeWarpEngine_StartMillis;
unsigned long fadeMainImpulseEngine_StartMillis;
unsigned long myStartTime;

//blink period for Navigation Lights
const unsigned long blinkPeriod_NavigationLights = 800;
//fade period for WarpEngine Lights
const unsigned long fadePeriod_WarpEngine = 100;
//fade period for Main Impulse Engine Lights
const unsigned long fadePeriod_MainImpulseEngine = 100;

// Define the PINs for each LED / Section
const byte NavigationLights = 13;
const byte WarpEngineLights = 10;
const byte MainImpulseEngineLights = 12;

//initial brightness of LED
byte brightness_WarpEngine = 0;
byte brightness_MainImpulseEngine = 0;

//amount to change PWM value at each change
byte increment_WarpEngine = 1;
byte increment_MainImpulseEngine = 1;

void setup()
{
  Serial.begin(115200);  //start Serial in case we need to print debugging info
  // define Pin Modes
  pinMode(NavigationLights, OUTPUT);
  pinMode(WarpEngineLights, OUTPUT);
  pinMode(MainImpulseEngineLights, OUTPUT);
  //start time of blinking LED
  blink_StartMillis = millis();
  //start time of fading LED (Warp Engine)
  fadeWarpEngine_StartMillis = blink_StartMillis;
  //start time of fading LED (Main Impulse Engine)
  fadeMainImpulseEngine_StartMillis = fadeWarpEngine_StartMillis;
}

void loop()
{
  //get the current time - save individual for each
  // call the individual functions
  myStartTime = millis();
  NavigationLightBlinking();
  fadeWarpEngineIn();
  fadeMainImpulseEngineIn();
}

//function to blink an LED if the blink period has ended
void NavigationLightBlinking()
{
  if (myStartTime - blink_StartMillis >= blinkPeriod_NavigationLights) //test whether the period has elapsed
  {
    digitalWrite(NavigationLights, !digitalRead(NavigationLights));  //if so, change the state of the LED
    blink_StartMillis += blinkPeriod_NavigationLights; //IMPORTANT to save the start time of the current LED state.
  }
}

//function to fade the Warp-Engine LED
void fadeWarpEngineIn()
{
  if (myStartTime - fadeWarpEngine_StartMillis >= fadePeriod_WarpEngine)  //test whether the period has elapsed
  {
    fadeWarpEngine_StartMillis += fadePeriod_WarpEngine; //IMPORTANT to save the start time of the current LED state.
    if (brightness_WarpEngine < 255)
    {
      analogWrite(WarpEngineLights, brightness_WarpEngine);    //set the brightness of the LED
      brightness_WarpEngine += /*brightness_WarpEngine +*/ increment_WarpEngine;    //will wrap round because brightness is an unsigned data type
    }
  }
}

//function to fade the Main Impulse-Engine LED
void fadeMainImpulseEngineIn()
{
  if (myStartTime - fadeMainImpulseEngine_StartMillis >= fadePeriod_MainImpulseEngine)  //test whether the period has elapsed
  {
    fadeMainImpulseEngine_StartMillis += fadePeriod_MainImpulseEngine;  //IMPORTANT to save the start time of the current LED state.
    if (brightness_MainImpulseEngine < 255)
    {
      analogWrite(MainImpulseEngineLights, brightness_MainImpulseEngine);    //set the brightness of the LED
      brightness_MainImpulseEngine += /*brightness_MainImpulseEngine +*/ increment_MainImpulseEngine;    //will wrap round because brightness is an unsigned data type
    }
  }
}

Hi, leider nicht - ehrlich gesagt, sehe ich in der Simulation aktuell gar keinen Unterschied :frowning:

fadeMainImpulseEngineIn() wird nach wie vor erst nach ca. 13 Sekunden ausgeführt.

Und wenn ich das richtig sehe, dann hast du nur diesen Bereich hier angepasst, richtig?

  //start time of blinking LED
  blink_StartMillis = millis();
  //start time of fading LED (Warp Engine)
  fadeWarpEngine_StartMillis = blink_StartMillis;
  //start time of fading LED (Main Impulse Engine)
  fadeMainImpulseEngine_StartMillis = fadeWarpEngine_StartMillis;

was ich jetzt hier allerdings nicht ganz verstehe - und daher war mein Ansatz für jede Funktion einen eigenen millis() zu nutzen... xyz_StartMillis() wird in der jeweiligen Funktion neu gesetzt.

Das Bedeutet doch aber auch, dass die jeweilige Funktion auf den neu gesetzten Wert der anderen Funktion aufbaut ?

Also z.B. fadeWarpEngine_StartMillis nutzt den Wert, welcher durch blink_StartMillis gesetzt wurde... usw.

naja, ich habe jetzt einen Ansatz hier gefunden, welcher mir einige andere, noch geplanten Schritte ermöglicht - vielleicht versuche ich einfach, das mit dem Fading komplett neu zu machen und auf den neuen Code aufzubauen :slight_smile:

Zwei Dinge:
Doch es gibt noch mehr Änderungen.
Ich abeite nur mit einer "aktualMillis()" für alle drei Funktionen.
Dann habe ich die Ermittlung der Zeiten für die Nächste Prüfung geändert, damit die nicht auseinanderlaufen.

Und auf dem Seriellen Monitor funktioniert das ganz sauber:

13:19:48.316 -> Start...
13:19:48.416 -> fadeWarp
13:19:48.416 -> fadeMain
13:19:48.515 -> fadeWarp
13:19:48.515 -> fadeMain
13:19:48.615 -> fadeWarp
13:19:48.615 -> fadeMain
13:19:48.714 -> fadeWarp
13:19:48.714 -> fadeMain
13:19:48.818 -> fadeWarp
13:19:48.818 -> fadeMain
13:19:48.917 -> fadeWarp
13:19:48.917 -> fadeMain
13:19:49.016 -> fadeWarp
13:19:49.016 -> fadeMain
13:19:49.116 -> NaviLicht
13:19:49.116 -> fadeWarp
13:19:49.116 -> fadeMain
13:19:49.215 -> fadeWarp
13:19:49.215 -> fadeMain
13:19:49.314 -> fadeWarp
13:19:49.314 -> fadeMain
13:19:49.467 -> fadeWarp
13:19:49.467 -> fadeMain
13:19:49.513 -> fadeWarp
13:19:49.513 -> fadeMain
13:19:49.613 -> fadeWarp
13:19:49.613 -> fadeMain
13:19:49.712 -> fadeWarp
13:19:49.712 -> fadeMain
13:19:49.811 -> fadeWarp
13:19:49.811 -> fadeMain
13:19:49.911 -> NaviLicht
13:19:49.911 -> fadeWarp

Selber Code nur mit Ausgabe, wann der Einsprung in die Funktionen passiert.

unsigned long blink_StartMillis;
unsigned long fadeWarpEngine_StartMillis;
unsigned long fadeMainImpulseEngine_StartMillis;
unsigned long myStartTime;

//blink period for Navigation Lights
const unsigned long blinkPeriod_NavigationLights = 800;
//fade period for WarpEngine Lights
const unsigned long fadePeriod_WarpEngine = 100;
//fade period for Main Impulse Engine Lights
const unsigned long fadePeriod_MainImpulseEngine = 100;

// Define the PINs for each LED / Section
const byte NavigationLights = 13;
const byte WarpEngineLights = 10;
const byte MainImpulseEngineLights = 12;

//initial brightness of LED
byte brightness_WarpEngine = 0;
byte brightness_MainImpulseEngine = 0;

//amount to change PWM value at each change
byte increment_WarpEngine = 1;
byte increment_MainImpulseEngine = 1;

void setup()
{
  Serial.begin(115200);  //start Serial in case we need to print debugging info
  Serial.println(F("Start..."));
  // define Pin Modes
  pinMode(NavigationLights, OUTPUT);
  pinMode(WarpEngineLights, OUTPUT);
  pinMode(MainImpulseEngineLights, OUTPUT);
  //start time of blinking LED
  blink_StartMillis = millis();
  //start time of fading LED (Warp Engine)
  fadeWarpEngine_StartMillis = blink_StartMillis;
  //start time of fading LED (Main Impulse Engine)
  fadeMainImpulseEngine_StartMillis = fadeWarpEngine_StartMillis;
}

void loop()
{
  //get the current time - save individual for each
  // call the individual functions
  myStartTime = millis();
  NavigationLightBlinking();
  fadeWarpEngineIn();
  fadeMainImpulseEngineIn();
}

//function to blink an LED if the blink period has ended
void NavigationLightBlinking()
{
  if (myStartTime - blink_StartMillis >= blinkPeriod_NavigationLights) //test whether the period has elapsed
  {
    Serial.println(F("NaviLicht"));
    digitalWrite(NavigationLights, !digitalRead(NavigationLights));  //if so, change the state of the LED
    blink_StartMillis += blinkPeriod_NavigationLights; //IMPORTANT to save the start time of the current LED state.
  }
}

//function to fade the Warp-Engine LED
void fadeWarpEngineIn()
{
  if (myStartTime - fadeWarpEngine_StartMillis >= fadePeriod_WarpEngine)  //test whether the period has elapsed
  {
    Serial.println(F("fadeWarp"));
    fadeWarpEngine_StartMillis += fadePeriod_WarpEngine; //IMPORTANT to save the start time of the current LED state.
    if (brightness_WarpEngine < 255)
    {
      analogWrite(WarpEngineLights, brightness_WarpEngine);    //set the brightness of the LED
      brightness_WarpEngine += /*brightness_WarpEngine +*/ increment_WarpEngine;    //will wrap round because brightness is an unsigned data type
    }
  }
}

//function to fade the Main Impulse-Engine LED
void fadeMainImpulseEngineIn()
{
  if (myStartTime - fadeMainImpulseEngine_StartMillis >= fadePeriod_MainImpulseEngine)  //test whether the period has elapsed
  {
    Serial.println(F("fadeMain"));
    fadeMainImpulseEngine_StartMillis += fadePeriod_MainImpulseEngine;  //IMPORTANT to save the start time of the current LED state.
    if (brightness_MainImpulseEngine < 255)
    {
      analogWrite(MainImpulseEngineLights, brightness_MainImpulseEngine);    //set the brightness of the LED
      brightness_MainImpulseEngine += /*brightness_MainImpulseEngine +*/ increment_MainImpulseEngine;    //will wrap round because brightness is an unsigned data type
    }
  }
}

Ich denke eher, das Deine Zeit daher kommt, das Du erst nach xxx Durchläufen eine Änderung siehst.

Ändere den Code ab Zeile 85 in das hier:

    if (brightness_MainImpulseEngine < 255)
    {
      brightness_MainImpulseEngine += /*brightness_MainImpulseEngine +*/ increment_MainImpulseEngine;    //will wrap round because brightness is an unsigned data type
      if (brightness_MainImpulseEngine < 15)brightness_MainImpulseEngine = 15;
      analogWrite(MainImpulseEngineLights, brightness_MainImpulseEngine);    //set the brightness of the LED
    }

hm... okey danke.
Möglich, dass es auch an dem Online Simulator liegt... :slight_smile:
Werde ich testen, sobald ich meine Hardwareumgebung erhalten habe :wink:

Danke auf jeden Fall schon mal.

btw:
für die Blink-funktion nutze ich jetzt diesen Code hier:

class Blinker
{
  protected:
  const byte ledPin;
  const unsigned long hellPhase;
  const unsigned long dunkelPhase;
  unsigned long zeitMerker = 0;
  void *sprungZiel = 0; // zustandsmerker

  public:
  Blinker(const byte ledPin,const unsigned long hellPhase,const unsigned long dunkelPhase): ledPin(ledPin),hellPhase(hellPhase),dunkelPhase(dunkelPhase){}

  void run(/*const bool blinkAnforderung*/)
  {
    if(!sprungZiel) sprungZiel = &&Start;
    goto *sprungZiel;

    Start:
    //if(!blinkAnforderung) return;
    digitalWrite(ledPin,HIGH);
    sprungZiel = &&HellPhase;
    zeitMerker = millis();
    
    HellPhase:
    if(millis()-zeitMerker<hellPhase) return;
    digitalWrite(ledPin,LOW);
    sprungZiel = &&DunkelPhase;
    zeitMerker = millis();
  
    DunkelPhase:
    if(millis()-zeitMerker<dunkelPhase) return;
    sprungZiel = 0;
  }
  
  void init()
  {
    pinMode(ledPin,OUTPUT);
  }
};

Blinker blinkGruppe[] = { // {pin,hellzeit,dunkelzeit}
                          {13,50, 1000}, // Beacon Light
                          {12,500,1000}, // Navigation Lights
                        };

void setup() 
{
  //pinMode(taster,INPUT_PULLUP);
  for(Blinker &blinker:blinkGruppe) blinker.init();
}

void loop() 
{
  //bool blinkAnforderung = !digitalRead(taster);
  for(Blinker &blinker:blinkGruppe) blinker.run(/*blinkAnforderung*/);
}

das passt soweit eigentlich ganz gut - und erlaubt mir zudem, die ON & OFF-Zeiten individuell zu setzen.
Das ist sogar ziemlich genau das, was ich erreichen wollte.

Jetzt bleibt nur eine winzige Kleinigkeit, nach der ich eventuell noch schauen muss:

Wenn ich eine der LEDs (Pin 13) wie folgt blinken lassen möchte:

AN: 50ms -> AUS: 100ms -> AN: 50ms -> AUS: 1000ms
Dann müsste ich doch eigentlich nur eine weitere "dunkelzeit" definieren, welche ich dann an dieses Blinklicht übergeben muss.
Eventuell muss ich dann aber bei der Übergabe berücksichtigen, dass das zweite Blinklicht diesen Wert nicht hat - und nicht benötigt... oder?

hm... okey danke.
Möglich, dass es auch an dem Online Simulator liegt... :slight_smile:
Werde ich testen, sobald ich meine Hardwareumgebung erhalten habe :wink:

Danke auf jeden Fall schon mal.

btw:
für die Blink-funktion nutze ich jetzt diesen Code hier:

class Blinker
{
  protected:
  const byte ledPin;
  const unsigned long hellPhase;
  const unsigned long dunkelPhase;
  unsigned long zeitMerker = 0;
  void *sprungZiel = 0; // zustandsmerker

  public:
  Blinker(const byte ledPin,const unsigned long hellPhase,const unsigned long dunkelPhase): ledPin(ledPin),hellPhase(hellPhase),dunkelPhase(dunkelPhase){}

  void run(/*const bool blinkAnforderung*/)
  {
    if(!sprungZiel) sprungZiel = &&Start;
    goto *sprungZiel;

    Start:
    //if(!blinkAnforderung) return;
    digitalWrite(ledPin,HIGH);
    sprungZiel = &&HellPhase;
    zeitMerker = millis();
    
    HellPhase:
    if(millis()-zeitMerker<hellPhase) return;
    digitalWrite(ledPin,LOW);
    sprungZiel = &&DunkelPhase;
    zeitMerker = millis();
  
    DunkelPhase:
    if(millis()-zeitMerker<dunkelPhase) return;
    sprungZiel = 0;
  }
  
  void init()
  {
    pinMode(ledPin,OUTPUT);
  }
};

Blinker blinkGruppe[] = { // {pin,hellzeit,dunkelzeit}
                          {13,50, 1000}, // Beacon Light
                          {12,500,1000}, // Navigation Lights
                        };

void setup() 
{
  //pinMode(taster,INPUT_PULLUP);
  for(Blinker &blinker:blinkGruppe) blinker.init();
}

void loop() 
{
  //bool blinkAnforderung = !digitalRead(taster);
  for(Blinker &blinker:blinkGruppe) blinker.run(/*blinkAnforderung*/);
}

das passt soweit eigentlich ganz gut - und erlaubt mir zudem, die ON & OFF-Zeiten individuell zu setzen.
Das ist sogar ziemlich genau das, was ich erreichen wollte.

Jetzt müsste ich nur noch ein paar Erweiterungen hinbekommen, an denen ich aber gerade noch scheitere :frowning:

Das Blinken der LEDs wird bei Programmstart getriggert.
Ursprünglich war dies auf einen "Button Press" gelegt, was ich entfernt habe.

Tatsächlich aber, möchte ich eine weitere LED haben, welche NUR nach dem Drücken eines Buttons 3x Blinkt - und danach nicht mehr.

Ich müsste jetzt nur noch heraus finden, wie ich diese eine LED, die an einem weiteren Pin X angeschlossen ist an die Klasse übergeben kann.

Es müsste vermutlich irgendwie so aussehen:

void torpedoLaunch(bool blinkAnforderung)
{
 // Hier sollte der LED Pin, die HellZeit und DunkelZeit übergeben werden...
  for(Blinker &blinker:blinkGruppe) blinker.run(blinkAnforderung);
}
const byte buttonPin = 3;
void setup()
{
  Serial.begin(115200);
  Serial.println(F("Start..."));
  pinMode(buttonPin, INPUT_PULLUP);
  digitalWrite(LED_BUILTIN, LOW);
  pinMode(LED_BUILTIN, OUTPUT);
}

void loop()
{
  dreimalBlinken();
}

void dreimalBlinken()
{
  static bool istStart = false;
  static byte zaehler = 0;
  static unsigned long lastmillis = 0;
  if (!digitalRead(buttonPin))
  {
    digitalWrite(LED_BUILTIN, HIGH);
    istStart = true;
    lastmillis = millis();
    zaehler = 0;
  }
  if (istStart)
  {
    if (millis() - lastmillis > 500)
    {
      digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN));
      zaehler++;
      lastmillis = millis();
    }
    if (zaehler > 6)
    {
      digitalWrite(LED_BUILTIN, LOW);
      istStart = false;
    }
  }
}
1 Like

Danke, das geht schon in die Richtung :slight_smile:
Was aber schön wäre, wenn ich das Blinken über die "blinker" Gruppe / Classe machen könnte.

Denn darüber kann ich dann Steuern, wie lange die LED an bzw. aus sein soll.
In deinem Beispiel sind die Ein - und Auszeiten der LED wieder identisch, was bei einem entsprechend kurzen Zyklus leider einem "flackern" ähnelt und bei längerer Pause wäre die LED wiederum zu lange an...

Eine Option wäre natürlich, die ganze Klasse zu kopieren und dann nur für den zweck zu nutzen, den ich mit der separaten LED und dem Button vor habe... überleg

So, also soweit scheint das mit dem duplizieren der Classe funktioniert zu haben :slight_smile:

class Blinker
{
  protected:
  const byte ledPin;
  const unsigned long hellPhase;
  const unsigned long dunkelPhase;
  unsigned long zeitMerker = 0;
  void *sprungZiel = 0; // zustandsmerker

  public:
  Blinker(const byte ledPin,const unsigned long hellPhase,const unsigned long dunkelPhase): ledPin(ledPin),hellPhase(hellPhase),dunkelPhase(dunkelPhase){}
  

  void run(const bool blinkAnforderung)
  {
    if(!sprungZiel) sprungZiel = &&Start;
    goto *sprungZiel;

    Start:
    if(!blinkAnforderung) return;
    digitalWrite(ledPin,HIGH);
    sprungZiel = &&HellPhase;
    zeitMerker = millis();
    
    HellPhase:
    if(millis()-zeitMerker<hellPhase) return;
    digitalWrite(ledPin,LOW);
    sprungZiel = &&DunkelPhase;
    zeitMerker = millis();
  
    DunkelPhase:
    if(millis()-zeitMerker<dunkelPhase) return;
    sprungZiel = 0;
  }
  
  void init()
  {
    pinMode(ledPin,OUTPUT);
  }
};

class Blinker2
{
  protected:
  const byte ledPin;
  const unsigned long hellPhase;
  const unsigned long dunkelPhase;
  unsigned long zeitMerker = 0;
  void *sprungZiel = 0; // zustandsmerker

  public:
  Blinker2(const byte ledPin,const unsigned long hellPhase,const unsigned long dunkelPhase): ledPin(ledPin),hellPhase(hellPhase),dunkelPhase(dunkelPhase){}
  
  void run(const bool blinkAnforderung)
  {
    if(!sprungZiel) sprungZiel = &&Start;
    goto *sprungZiel;

    Start:
    if(!blinkAnforderung) return;
    digitalWrite(ledPin,HIGH);
    sprungZiel = &&HellPhase;
    zeitMerker = millis();
    
    HellPhase:
    if(millis()-zeitMerker<hellPhase) return;
    digitalWrite(ledPin,LOW);
    sprungZiel = &&DunkelPhase;
    zeitMerker = millis();
  
    DunkelPhase:
    if(millis()-zeitMerker<dunkelPhase) return;
    sprungZiel = 0;
  }
  
  void init()
  {
    pinMode(ledPin,OUTPUT);
  }
};

Blinker blinkGruppe[] = { // {pin,hellzeit,dunkelzeit}
                          {13, 20, 1000}, // Beacon Light
                          {12, 700, 1000}, // Navigation Lights
                        };

Blinker2 blinkGruppe2[] = { // {pin,hellzeit,dunkelzeit}
                          {11, 10, 150}, // LED_1
                        };

//int Button_State = 0;
const byte ButtonPin = 2;
const byte LED_1 = 11;

void setup() 
{
  pinMode(ButtonPin, INPUT_PULLUP);
  for(Blinker &blinker:blinkGruppe) blinker.init();
  for(Blinker2 &blinker2:blinkGruppe2) blinker2.init();

  Serial.begin(115200);
  //Serial.println(F("Start..."));
  
  digitalWrite(LED_1, LOW);
  pinMode(LED_1, OUTPUT);
}

void loop() 
{
  for(Blinker &blinker:blinkGruppe) blinker.run(HIGH);

  BinkenPerButton();
}

void BinkenPerButton()
{
  bool blinkAnforderung = !digitalRead(ButtonPin);
  //for (int i = 0; i > 3; i++) {
    //
    for(Blinker2 &blinker2:blinkGruppe2) blinker2.run(blinkAnforderung);
 // }
}

fehlt nur noch, dass das blinken drei mal ausgelöst wird - aktuell blinkt es, solange der Button gedrückt ist. :slight_smile:

Warum stattest Du Blinker nicht einfach mit einem weiteren Konstruktor aus?

Oder nimmst die zweite Dunkelphase als optionalen Parameter in Blinker dazu (default initialisiert mit einem unmöglichen Wert wie -1)?

Habe jetzt auf dem Mobilcomputer die Klassen nicht im Detail inspiziert: Wäre Blinket2 als Ableitung von Blinker möglich - um nur die geänderten Methoden überschreiben zu müssen?

vermutlich ja, aber dafür reichen meine Programmierkenntnisse leider noch nicht aus - wie gesagt, das basiert aktuell auf einem Code, welchen ich gefunden habe.

Ich will mich in nächster Zeit intensiver mit der Thematik befassen - auf der anderen Seite hätte ich dieses Projekt zumindest gerne soweit umgesetzt, dass ich mit meiner Hardware soweit anfangen kann (code-optimierung / re-write) kommt dann ggf. später.

Irgendwie war ich vorhin auf dem falschen Weg - dachte, Du brauchst noch eine Zeit mehr.

Die Klassen Blinker und Blinker2 sind exakt gleich.
Also würde es reichen, die blinkGruppe2 auch als Array von Objekten des Typs Blinker zu erzeugen:

Blinker blinkGruppe2[] = { // {pin,hellzeit,dunkelzeit}
                          {11, 10, 150}, // LED_1
                        };

Sollte genauso gut oder schlecht funktionieren wie der Sketch aus Post #12.

1 Like

hm... ok, danke :slight_smile:
Das versuche ich mal :slight_smile:

EDIT:
So, ich glaube, ich habe es jetzt endlich hinbekommen :slight_smile:

Dann kann ich mit anderen Optionen weiter machen :slight_smile:

Hallo Zusammen,

wie bereits gestern angesprochen, habe ich es hinbekommen, die Klasse so anzupassen, dass ich jetzt eine der LEDs via Button triggern kann, während die anderen konstant ab Programmstart blinken.

An dieser Stelle schon mal danke für den Input von Euch :slight_smile:

class Blinker
{
  protected:
  const byte ledPin;
  const unsigned long hellPhase;
  const unsigned long dunkelPhase;
  unsigned long zeitMerker = 0;
  void *sprungZiel = 0; // zustandsmerker

  public:
  Blinker(const byte ledPin,const unsigned long hellPhase,const unsigned long dunkelPhase): ledPin(ledPin),hellPhase(hellPhase),dunkelPhase(dunkelPhase){}

  void run(const bool blinkAnforderung)
  {
    if(!sprungZiel) sprungZiel = &&Start;
    goto *sprungZiel;

    Start:
    if(!blinkAnforderung) return;
    digitalWrite(ledPin,HIGH);
    sprungZiel = &&HellPhase;
    zeitMerker = millis();
    
    HellPhase:
    if(millis()-zeitMerker<hellPhase) return;
    digitalWrite(ledPin,LOW);
    sprungZiel = &&DunkelPhase;
    zeitMerker = millis();
  
    DunkelPhase:
    if(millis()-zeitMerker<dunkelPhase) return;
    sprungZiel = 0;
  }
  
  void init()
  {
    pinMode(ledPin,OUTPUT);
  }
};

// Pins and timings (On / Off) for Blinking Sequence
Blinker blinkGruppe[] = { // {pin,hellzeit,dunkelzeit}
                          {13, 20, 1000}, // Beacon Light
                          {12, 700, 1000}, // Navigation Lights
                        };

Blinker blinkGruppe2[] = { // {pin,hellzeit,dunkelzeit}
                          {11, 10, 150}, // Torpedo Light
                        };

// to which Pin is the Button for the Torpedo attached?
const byte ButtonPin = 2;

void setup() 
{
  // Debug Console Output
  Serial.begin(115200);
  //Serial.println(F("Start..."));

  pinMode(ButtonPin, INPUT_PULLUP);
  // Init main Blinking lights
  for(Blinker &blinker:blinkGruppe) blinker.init();
  // Init Torpedo Blinking light
  for(Blinker &blinker:blinkGruppe2) blinker.init();
}

void loop() 
{
  // Run Main Blinking lights immediately after startup
  for(Blinker &blinker:blinkGruppe) blinker.run(true);

  // Torpedo Launch Sequence
  // TBD: Should only be triggered three times and then quit.
  
  bool blinkAnforderung = !digitalRead(ButtonPin);
  
  torpedoLaunch(blinkAnforderung);  
}

void torpedoLaunch(bool blinkAnforderung)
{
  for(Blinker &blinker:blinkGruppe2) blinker.run(blinkAnforderung);
}

soweit - so gut... ein kleines Problem besteht allerdings noch, für welches ich momentan etwas auf dem Schlauch stehe :frowning:

Während die LEDs alle konstant über die gesamte Laufzeit hin blinken sollen (was auch gegeben ist), wird die LED in blinkerGruppe2 nur über den Push-Button getriggert.
Dazu wird die blinkAnforderung während des Drückens auf true gesetzt.

Sobald der Button los gelassen wird, setzt sich blinkAnfoderung auf false zurück und das blinken stoppt.

Ich möchte aber gerne erreichen, dass die LED nach dem drücken des Buttons drei mal aufblitzt.

Die Sequenz wäre also: ON=10 -> OFF=150 -> ON=10 -> OFF=150 -> ON=10 -> EXIT

Ich habe jetzt bereits ein paar Sachen ausprobiert, bekomme es aber nicht so richtig hin... Mein Ansatz (vermutlich nicht der beste) den Aufruf von torpedoLaunch(blinkAnforderung); Zeitlich zu steuern... aber das hat nur mäßig bis gar nicht funktioniert.

Ich vermute mal, es wäre einfacher, die Klasse so zu ändern, dass ein weiterer Parameter übergeben werden kann, welcher die Anzahl der Durchläufe steuert... ?!

Allerdings habe ich aktuell keine Ahnung, wie ich hier überhaupt Ansetzen müsste :frowning:

Eventuell mit:

  void run(const bool blinkAnforderung)
  {
    if (AnzDurchläufe != 0)
    {
      for (int i = 0; i >= AnzDurchläufe; i++)
      {
        if(!sprungZiel) sprungZiel = &&Start;
        goto *sprungZiel;

        Start:
        if(!blinkAnforderung) return;
        digitalWrite(ledPin,HIGH);
        sprungZiel = &&HellPhase;
        zeitMerker = millis();
    
        HellPhase:
        if(millis()-zeitMerker<hellPhase) return;
        digitalWrite(ledPin,LOW);
        sprungZiel = &&DunkelPhase;
        zeitMerker = millis();
  
        DunkelPhase:
        if(millis()-zeitMerker<dunkelPhase) return;
        sprungZiel = 0;
      }
    }
  }

Dann müsste ich lediglich die Anzahl der Durchläufe über den Konstruktor mit übergeben.... ?!
0 = dann wird die IF nicht ausgeführt und der Code läuft ohne schleife
Alles > 0 ergibt die anzahl der Durchläufe...

Versuche mal dies:

class Blinker
{
  protected:
  const byte ledPin;
  const unsigned long hellPhase;
  const unsigned long dunkelPhase;
  unsigned long zeitMerker = 0;
  void *sprungZiel = 0; // zustandsmerker

  public:
  Blinker(const byte ledPin,const unsigned long hellPhase,const unsigned long dunkelPhase): ledPin(ledPin),hellPhase(hellPhase),dunkelPhase(dunkelPhase){}

  void run(byte & blinkZahl)
  {
    if(!sprungZiel) sprungZiel = &&Start;
    goto *sprungZiel;

    Start:
    if(!blinkZahl) return;
    digitalWrite(ledPin,HIGH);
    sprungZiel = &&HellPhase;
    zeitMerker = millis();
    
    HellPhase:
    if(millis()-zeitMerker<hellPhase) return;
    digitalWrite(ledPin,LOW);
    sprungZiel = &&DunkelPhase;
    zeitMerker = millis();
  
    DunkelPhase:
    if(millis()-zeitMerker<dunkelPhase) return;
    if(blinkZahl!=255) blinkZahl--;
    sprungZiel = 0;
  }
  
  void init()
  {
    pinMode(ledPin,OUTPUT);
  }
};

// Pins and timings (On / Off) for Blinking Sequence
Blinker blinkGruppe[] = { // {pin,hellzeit,dunkelzeit}
                          {13, 20, 1000}, // Beacon Light
                          {12, 700, 1000}, // Navigation Lights
                        };

Blinker blinkGruppe2[] = { // {pin,hellzeit,dunkelzeit}
                          {11, 10, 150}, // Torpedo Light
                        };

// to which Pin is the Button for the Torpedo attached?
const byte ButtonPin = 2;

void setup() 
{
  // Debug Console Output
  //Serial.begin(115200);
  //Serial.println(F("Start..."));

  pinMode(ButtonPin, INPUT_PULLUP);
  // Init main Blinking lights
  for(Blinker &blinker:blinkGruppe) blinker.init();
  // Init Torpedo Blinking light
  for(Blinker &blinker:blinkGruppe2) blinker.init();
}

void loop() 
{
  // Run Main Blinking lights immediately after startup
  byte tmp = 255;
  for(Blinker &blinker:blinkGruppe) blinker.run(tmp);

  // Torpedo Launch Sequence
  // TBD: Should only be triggered three times and then quit.
  
  static byte blinkZahl = 0;
  if(!digitalRead(ButtonPin))
  {
    blinkZahl = 3;
  }
  
  for(Blinker &blinker:blinkGruppe2) blinker.run(blinkZahl);
}

Dafür hätte ich Ideen, beispielsweise blinkGruppe und blinkGruppe2 zusammenlegen.

Hi,

danke :slight_smile: Funktioniert wunderbar :slight_smile:
Die Code-Änderung von blinkAnforderung zur Übergabe einer Durchlauf-Zeit ist ja doch simpler als ich gedacht habe :smiley:

Über Ideen zur Optimierung bzw. zum Zusammenlegen der beiden Gruppen, würde ich mich sehr freuen :slight_smile:

Derweil versuche ich mal, das ganze auf Aktueller Hardware umzusetzen und zu testen :wink:

Anstelle goto jetzt switch/case und ein Feld:

class Blinker
{
  protected:
    const byte ledPin;
    const unsigned long hellPhase;
    const unsigned long dunkelPhase;
    unsigned long zeitMerker = 0;
    byte schritt = 0; // zustandsmerker

  public:
    Blinker(const byte ledPin, const unsigned long hellPhase, const unsigned long dunkelPhase): ledPin(ledPin), hellPhase(hellPhase), dunkelPhase(dunkelPhase) {}

    void run(byte & blinkZahl)
    {
      enum {Start, HellPhase, DunkelPhase};
      switch (schritt)
      {
        case Start:
          if (blinkZahl)
          {
            digitalWrite(ledPin, HIGH);
            schritt = HellPhase;
            zeitMerker = millis();
          }
          break;
        case HellPhase:
          if (millis() - zeitMerker >= hellPhase)
          {
            digitalWrite(ledPin, LOW);
            schritt = DunkelPhase;
            zeitMerker = millis();
          }
          break;
        case DunkelPhase:
          if (millis() - zeitMerker >= dunkelPhase)
          {
            if (blinkZahl != 255) blinkZahl--;
            schritt = Start;
          }
      }
    }

    void init()
    {
      pinMode(ledPin, OUTPUT);
    }
};

// Pins and timings (On / Off) for Blinking Sequence
Blinker blinkGruppe[] = { // {pin,hellzeit,dunkelzeit}
  {13, 20, 1000}, // Beacon Light
  {12, 700, 1000}, // Navigation Lights
  {11, 10, 150}, // Torpedo Light
};

// to which Pin is the Button for the Torpedo attached?
const byte ButtonPin = 2;

void setup()
{
  // Debug Console Output
  //Serial.begin(115200);
  //Serial.println(F("Start..."));

  pinMode(ButtonPin, INPUT_PULLUP);
  // Init Blinking lights
  for (Blinker &blinker : blinkGruppe) blinker.init();
}

void loop()
{
  // Run Main Blinking lights immediately after startup
  byte tmp = 255;
  for (byte j=0;j<2;j++)
  {
    blinkGruppe[j].run(tmp);
  }

  // Torpedo Launch Sequence
  // TBD: Should only be triggered three times and then quit.
  static byte blinkZahl = 0;
  if (!digitalRead(ButtonPin))
  {
    blinkZahl = 3;
  }
  blinkGruppe[2].run(blinkZahl);
}

Welche Art der Programmierung für Dein Projekt vorteilhaft ist, vermag ich nicht einzuschätzen, da ich nicht weiß, was Du noch planst. Muß beispielsweise blinkZahl variabel sein oder nicht.

1 Like