Frontblitzer ohne Delay

Hallo zusammen,

ich möchte für ein kleines Projekt eine Fahrzeugbeleuchtung simulieren. Das ganze soll wie in einem Feuerwehrfahrzeug funktionieren.
Bisher habe ich normales Abblendlicht, was dauerhaft leuchtet, sowie zwei LEDs, die in einem bestimmten Rhytmus blitzen sollen.

Bisher habe ich das nur über die Delay-Funktion geschafft. Jedoch soll das Projekt auch noch erweitert werden, daher denke ich, dass die Delay-Funktion dafür nicht geeignet ist.

Die blitzenden LEDs sollen 3mal mit einer kurzen Pause an und ausgehen, anschließend gibt es eine etwas längere Pause.

Bisher sieht mein Code so aus:

/*
 * Abblendlicht auf Pin G19 gesteckt
 * Rücklicht auf Pin G22 gesetzt
 * Blitzer auf Pin G23 gesetzt
 */
int Abblendlicht = 19;  
int Ruecklicht = 22;  
int Blitzer = 23;     

void setup() {
  /*
*Abblendlcht als Ausgang
*Rücklicht als Ausgang
*Blitzer als Ausgang
*/
pinMode(Abblendlicht, OUTPUT);
pinMode(Ruecklicht, OUTPUT);
pinMode(Blitzer, OUTPUT);
}

void loop() {
  
digitalWrite(Abblendlicht, HIGH);
digitalWrite(Ruecklicht, HIGH);

/*
 * Wenn Spannung anliegt, Blaue LED blinken lassen
*  50ms eingeschaltet, 300ms ausgeschaltet
 */

digitalWrite(Blitzer, HIGH);
delay(50);
digitalWrite(Blitzer, LOW);
delay(50);
digitalWrite(Blitzer, HIGH);
delay(50);
digitalWrite(Blitzer, LOW);
delay(50);
digitalWrite(Blitzer, HIGH);
delay(50);
digitalWrite(Blitzer, LOW);
delay(300);
}

Ich glaube, du möchtest dich mit endlichen Automaten beschäftigen.
Hier könnten dir die Multitasking Macros den Weg bereiten.

Ablaufsteuerung
Meine Standardantwort zu Ablaufsteuerungen:

Eine Stichworte Sammlung für Google Suchen:
Endlicher Automat,
ProtoThreads,
State Machine,
Multitasking,
Coroutinen,
Ablaufsteuerung,
Schrittkette,
BlinkWithoutDelay,

Automat (Informatik)
Blink Without Delay
Die Nachtwächter Erklärung

MicroBahner/MobaTools
Intervall Macro
Multitasking Macros
INTERVAL Ersatzstoff
CooperativeTask

(deleted)

Welchen Controller / Arduino hast Du?
Grüße Uwe

das kommt eh öfter. Da gibts verschiedene offizielle Blinkmuster.
Bei Hella gab es da mal ein Datenblatt dafür.

Ich würde es in etwa so machen:

/* Blink a LED in a specific rhythm

  Turns on and off light emitting diodes (LED) connected to a digital pin,
  without using the delay() function. This means that other code can run at the
  same time without being interrupted by the LED code.

  noiasca
  2019-05-05

*/

class BlinkLed {
    byte state = 1;       // 1 blink, 0 off
    unsigned long previousMillis;
    uint16_t on = 180;
    uint16_t off = 320;   // 180/320 is according ECE
    const byte ledPin;

  public:
    BlinkLed(byte attachTo):
      ledPin(attachTo)
    {}

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

    void set(uint16_t _on, uint16_t _off)  // modify on/off times during runtime
    {
      on = _on;
      off = _off;
    }

    void setState(byte _state)            // 1 Switch on blinking; 0 switch off blinking
    {
      state = _state;
    }

    void tick()
    {
      if (state)
      {
        uint32_t currentMillis = millis();
        if (digitalRead(ledPin) && currentMillis - previousMillis >= on) {
          // save the last time you blinked the LED
          previousMillis = currentMillis;
          digitalWrite(ledPin, LOW);
        }
        if (!digitalRead(ledPin) && currentMillis - previousMillis >= off) {
          // save the last time you blinked the LED
          previousMillis = currentMillis;
          digitalWrite(ledPin, HIGH);
        }
      }
    }
};

class RhythmLed {
    byte state = 1;
    unsigned long previousMillis;
    const byte ledPin;
    uint8_t lengthOfPattern;
    // the intervall pattern
    //                          ON    OFF  ON   OFF
    //                          1     2    3    4
    uint16_t intervall[5] = {0, 150,  60, 20, 270};                  // ECE 2
    //uint16_t intervall[3] = {0, 180, 320};                         // ECE 1
    //uint16_t intervall[7] = {0, 25, 25, 25, 25, 25, 375};          // HELLA 3
    //uint16_t intervall[9] = {0, 40, 40, 40, 40, 40, 40, 40, 220 }; // HELLA 4
    //uint16_t intervall[5] = {0, 150,  60, 20, 400};                // wie gewünscht vom TO

  public:
    RhythmLed(byte attachTo):
      ledPin(attachTo)
    {}

    void begin()
    {
      pinMode(ledPin, OUTPUT);
      lengthOfPattern = sizeof(intervall) / sizeof(intervall[0]);
    }

    void setState(byte _state)
    {
      state = _state;
    }

    void setIntervall(uint16_t _intervall1, uint16_t _intervall2, uint16_t _intervall3, uint16_t _intervall4)       // Modify the intervalls to your needs
    {
      intervall[1] = _intervall1;
      intervall[2] = _intervall2;
      intervall[3] = _intervall3;
      if (lengthOfPattern>3) intervall[4] = _intervall4;   // if someone declares a dual-intervall, this line would crash the sketch, therefore this if on the index length ;-)
    }

    void tick()
    {
      uint32_t currentMillis = millis();
      if (state > 0)                            // iterate through the states 1 to n, we don't use state 0 because state 0 is reserved to "switch off" the light
      {
        if (currentMillis - previousMillis > intervall[state])
        {
          if (state % 2 == 1)  // all uneven states are ON, at the end of an intervall we switch to the oposite pin state
          {
            digitalWrite(ledPin, LOW);
          }
          else
          {
            digitalWrite(ledPin, HIGH);
          }
          state++;
          if (state > lengthOfPattern - 1) state = 1;
          previousMillis = currentMillis;
        }
      }
    }
};

BlinkLed myBlinkLed(5);       // a simple blink led
RhythmLed topRKL(13);         // define an output PIN for your LED
RhythmLed outOffSyncRKL(6);  // eine Rundumkennleuchte die wegen eines "Massefehlers" am Originalfahrzeugs eine leicht verschobene Blinkfrequenz hat ;-)

void setup()
{
  myBlinkLed.begin();
  topRKL.begin();
  outOffSyncRKL.begin();
  outOffSyncRKL.setIntervall(250,  90, 30, 400);  // Modify the intervalls for this LED
}

void loop()
{
  myBlinkLed.tick();  // each object has to be called once in the loop
  topRKL.tick();
  outOffSyncRKL.tick();

  // put here other code which needs to run:

}

Auch Felder können ihren Reiz haben (getestet mit Mega2560):

/*
   Abblendlicht auf Pin G19 gesteckt
   Rücklicht auf Pin G22 gesetzt
   Blitzer auf Pin G23 gesetzt
*/
const int Abblendlicht = 19;
const int Ruecklicht = 22;
const int Blitzer = 23;
const uint16_t Blitzintervalle[] = {50, 50, 50, 50, 50, 300};
uint32_t jetzt;

void setup() {
  /*
    Abblendlcht als Ausgang
    Rücklicht als Ausgang
    Blitzer als Ausgang
  */
  pinMode(Abblendlicht, OUTPUT);
  pinMode(Ruecklicht, OUTPUT);
  pinMode(Blitzer, OUTPUT);
  digitalWrite(Abblendlicht, HIGH);
  digitalWrite(Ruecklicht, HIGH);
  digitalWrite(Blitzer, HIGH);
}

void loop() {
  jetzt = millis();
  blitzen();
}

void blitzen() {
  static byte intervallindex = 0;
  static uint32_t vorhin;
  if (jetzt - vorhin >= Blitzintervalle[intervallindex]) {
    vorhin = jetzt;
    digitalWrite(Blitzer, !digitalRead(Blitzer));
    intervallindex = ++intervallindex < (sizeof(Blitzintervalle) / sizeof(Blitzintervalle[0])) ? intervallindex : 0;
  }
}

konstruktiven

Was das wohl bedeuten mag....
Immerhin habe ich 2 Konstanten von dir übernommen

#include <Streaming.h>
#include <TaskMacro.h>

constexpr unsigned long blitzMilliKurz =  50;
constexpr unsigned long blitzMilliLang = 300;

void blitzen()
{
  static int i;
  taskBegin();
  pinMode(LED_BUILTIN,OUTPUT);
  while(1)
  {
    for(i=0;i<3;i++)
    {
      digitalWrite(LED_BUILTIN,1);
      taskPause(blitzMilliKurz);
      digitalWrite(LED_BUILTIN,0);
      taskPause(blitzMilliKurz);
    }
    taskPause(blitzMilliLang-blitzMilliKurz);
  }
  taskEnd();
}

void loopCount()
{
  static unsigned long count;
  count++;
  taskBegin();
  while(1)
  {
    taskPause(1000);
    Serial << "Loops per Sec: "<< count << endl;
    count = 0;
  }
  taskEnd();
}

void setup() 
{
  Serial.begin(9600);
  Serial << "Start: "<< __FILE__ << endl;
}

void loop() 
{
  loopCount();
  blitzen();
}

(deleted)

@combie: konstruktiv wäre, wenn du das Wesentliche an deinem Beispiel nicht weggelassen hättest:
Woher/Was ist TaskMacro.h ? (und Streaming.h?)

Und selbst mit Quellenangabe ist der Sinn deiner Kapselung, zu verbergen wie es geht. Ob die Library wohl mit oder ohne delay() funktioniert, soll ja gerade egal sein.

Also, wie du schon befürchtet hast: schön, aber nicht die Antwort auf die Frage wie es ohne delay(), aber mit Arduino Funktionen, geht.

Woher/Was ist TaskMacro.h ?

Habe ich vorher schon verlinkt.
Da drauf klicken, dann finden sich Erklärungen und auch der Code.
Wenigstens das runterladen, installieren, und kompilieren, sollte einen nicht überfordern ....

Aber hier gerne nochmal: Multitasking Macros

(und Streaming.h?)

Kann man finden.
Alternativ: Raus operieren.

Also, wie du schon befürchtet hast: schön, aber nicht die Antwort auf die Frage wie es ohne delay(), aber mit Arduino Funktionen, geht.

Habe ich nicht befürchtet.
Und natürlich ist es eine der möglichen Lösungen für das Problem.
Damit auch eine Antwort auf die Frage.
Und ebenso natürlich: Alles mit den mitgelieferten Werkzeugen/Mitteln.

Klarer:
Auch die TaskMacros basieren auf dem "blink without delay" Prinzip.
Eingebettet in einen "verschlankten" ProtoThread Nachbau.
Keine Geheimnisse.

Und selbst mit Quellenangabe ist der Sinn deiner Kapselung, zu verbergen wie es geht.

Wenn ich der Argumentation folge:
Dann darf ich kein Wire verwenden, weil es die I2C Statemachine verbirgt.
Gleiches/Vergleichbares gilt für alle gefühlten 50000 Libraries
Alle haben den Zweck, die Komplexität eines Problems zu verbergen, zu abstrahieren und eine möglichst einfache Schnittstelle zu liefern.
Ich denke, da befinde ich mich in guter Gesellschaft, eigentlich.

Ich verstehe deine Kritik nicht.
Betrachte dieses Unverständnis bitte als Merkmal meiner ****.
(bei **** ein beliebiges Wort, deiner Wahl einsetzen)

Peter-CAD-HST:
Die Idee mit dem Feld hat mir super gefallen und ich mußte die gleich umsetzen :slight_smile:

blitzMilliInterval[digitalRead(BlitzLED)]

Moin!

Freut mich, allerdings erfüllt Deine Variante nicht die gewünschten Kriterien des ursprünglichen Programms, denn das Blitzmuster ist unterschiedlich.

const unsigned long blitzMilliKurz = 50;
const unsigned long blitzMilliLang = 300;
unsigned long blitzMilliInterval[]={blitzMilliLang,blitzMilliKurz};

würde ich abkürzen auf

const unsigned long blitzMilliInterval[]={300, 50};  // lange und kurze Blitzzeit

denn die Konstanten blitzMilliKurz und blitzMilliLang werden sonst nirgendwo verwendet.

Nur so meine Gedanken :slight_smile:

(deleted)

Hallo zusammen,

danke erstmal für die Hilfe. Zur Info noch, ich benutze einen ESP32 Mikrocontroller. (Haben wir für das Projekt in der Technikerschule genommen)

Da ich noch am Anfang mit dem Thema Programmierung stecke, würde es mich freuen, wenn mir jemand noch die ganzen Begriffe in dem Sketch erklären könnte.

Ich habe jetzt den ersten Sketch von agmue verwendet.
Könntest du mir die ganze Geschichte in der "Blitzen" Stelle noch erklären?

ich benutze einen ESP32 Mikrocontroller.

Dieser hat FreeRtos und 2(+1) Kerne im Bauch.
Es ist also überhaupt kein Problem nebenläufige Dinge damit zu realisieren.
(nur eben ein "wenig" Lernaufwand)

timschillert:
Könntest du mir die ganze Geschichte in der "Blitzen" Stelle noch erklären?

Da bin ich nicht so gut drin, aber ich probier's:

jetzt ist die aktuelle Zeit in Millisekunden seit Reset.

vorhin ist die Zeit beim letzten Wechsel in Millisekunden seit Reset.

Statische Variablen behalten am Ende der Funktion ihren Wert.

Die Intervalle, die Du bei delay verwendest hast, wandern in ein Feld. Die Anzahl der Feldelemente steckt in sizeof(Blitzintervalle) / sizeof(Blitzintervalle[0]). Der intervallindex durchläuft 0 bis 5.

Das ist es, oder?

agmue:
Da bin ich nicht so gut drin, aber ich probier's:

jetzt ist die aktuelle Zeit in Millisekunden seit Reset.

vorhin ist die Zeit beim letzten Wechsel in Millisekunden seit Reset.

Statische Variablen behalten am Ende der Funktion ihren Wert.

Die Intervalle, die Du bei delay verwendest hast, wandern in ein Feld. Die Anzahl der Feldelemente steckt in sizeof(Blitzintervalle) / sizeof(Blitzintervalle[0]). Der intervallindex durchläuft 0 bis 5.

Das ist es, oder?

const uint16_t

uint32_t

if (jetzt - vorhin >= Blitzintervalle[intervallindex]) {
    vorhin = jetzt;
    digitalWrite(Blitzer, !digitalRead(Blitzer));
    intervallindex = ++intervallindex < (sizeof(Blitzintervalle) / sizeof(Blitzintervalle[0])) ? intervallindex : 0;




kannst du mir diese Sachen oben erklären? Steht ja einmal vor dem Feld "Blitzintervalle" und einmal vor "jetzt"

bzw. einmal die ganze if-Schleife. Der erste Teil jetzt-vorhin ist ja klar, nur halt der Teil danach wäre noch interessant

Ich habe da mal was vorbereitet, was diese Zeile hier angeht:

intervallindex = ++intervallindex < (sizeof(Blitzintervalle) / sizeof(Blitzintervalle[0])) ? intervallindex : 0;

Das ist die Abkürzung für dieses Stück Programm:

// nächster (kommender) Wert ist eins höher als der aktuelle
intervallindexNeu = intervallindex + 1;

// Größe des ganzen Arrays / Größe des ersten Elements = Anzahl Elemente
anzahlBlitzintervalle = (sizeof(Blitzintervalle) / sizeof(Blitzintervalle[0]);  

// Kurzform: <bedingung> ? <wenn wahr> : <wenn falsch>
// Begrenzung auf die Anzahl der definierten Intervalle
if (intervallindexNeu < anzahlBlitzintervalle)
{
  // nächster - da es den gibt
  intervallindex = intervallindexNeu;
}
else
{
  // erster, wenn wir am Ende angekommen waren
  intervallindex = 0;
}

wno158:
Das ist die Abkürzung für dieses Stück Programm:

Danke für's Aufdröseln :slight_smile:

@agmue: Entschuldige, dass ich mich da eingemischt habe. Hatte das die Tage schon mal geschrieben, war aber nicht sicher, ob es benötigt wird.

@tim:
Fehlt ja dann nur noch das hier:

digitalWrite(Blitzer, !digitalRead(Blitzer));

Was passiert da eigentlich?
Man muss das von innen nach außen auflösen:

  1. digitalRead(Blitzer) kann LOW oder HIGH liefern - je nachdem, ob das Ding gerade aus- oder eingeschaltet ist.
  2. ! --> invertiert das Ergebnis vom read: aus (LOW) --> ein (HIGH) bzw. ein (HIGH) --> aus (LOW)
  3. das kannst Du selbst.
    Am Ende wird also einfach der Pinstatus invertiert.

Pfiffig, oder?

wno158:
agmue: Entschuldige, ...

Nö, ich bin Dir dankbar :slight_smile:

Sonst habe ich

intervallindex = (1 + intervallindex) % anzahlBlitzintervalle;

genutzt, aber Modulo soll, wie ich las, rechenaufwendig sein, mit der Bedingung geht es schneller. In diesem Thema ist die anfängerfreundliche aufgedröselte Variante aber vermutlich die verständlichere, so hoffe ich.