Impuls an 3 LEDs von überall im Code

Hallo together :slight_smile:

Ich lerne am Code von Serenifly:

bool triggered;
unsigned long previousMillis;

void loop()
{
  if (triggered && (millis() - previousMillis > 100 * 5UL))
  {
     digitalWrite(13, LOW);
     triggered = false;
  }
}

void trigger()
{
  digitalWrite(13, HIGH);
  triggered = true;
  previousMillis = millis();
}

Ich möchte von überall im Code die Funktion trigger() aufrufen können,
also z.B. trigger(LED1), trigger(LED2) und trigger(LED3)
und es soll dann die jeweilige LED für 500ms "gepulsed" werden.

Und gern ohne Library.

Nun könnte ich natürlich für jede LED 1-3 die trigger-Funktion und das if-Konstrukt in den Code setzen, aber ich weiß, dass es schöner geht :wink:

Mir fehlt nur gerade der Denkansatz.

Die trigger-Funktion mit byte Parameter-Übergabe?

Das if-Konstrukt dann mit enum class und switch case?

Kann mir jemand mit ein paar Pseudo-Codezeilen auf die Sprünge helfen?

Danke euch :slight_smile:

1 Like

hast du dir schon blink without delay angesehen?

dieses Prinzip klappt auch für das pulsen.

Ein Prinzip das geht:

eine Klasse schreiben,
beim Objekt anlegen den Pin übergeben.
Wenn notwendig die Member Funktion pulse aufrufen
im Loop einfach eine Member Funktion run() aufrufen damit das Objekt gegebenenfalls den pin wieder abschaltet.

das soll unter 42 Zeilen für 3 LEDs zu machen sein.

edit bevor ich es neu schreibe, eh schon getan:

hier anfangen:
https://werner.rothschopf.net/202003_arduino_retriggerbares_nachlaufrelais.htm

dann dort weitermachen:
https://werner.rothschopf.net/202003_arduino_retriggerbares_nachlaufrelais_oop.htm

Nimm dir je 1h Zeit, dann bist du vor 23:00 Uhr fertig.

3 Likes

Versuch macht kluch :wink:

Nicht vergessen, dass das Rücksetzen der jeweiligen LED in loop() dann ebenfalls nicht mehr mit einer hart verdrahteten 13 funktionieren kann.
Tipp: Man kann - analog zum triggered Flag - auch Portpins aufheben.

Und es wäre auch möglich, sowas wie setTrigger() und resetTrigger() zu konstruieren - oder eine Klasse, die den LED-Pin und die Ablaufzeit hält und Methoden wie set() und run() kennt.

1 Like

3 LEDs, also auch drei Datensätze, mit Pin Zeit und Zustand.
Das bekommst du nicht in eine Funktion rein, ohne dass es sehr hässlich wird.

Also Ansatz umwerfen.

1 Like

Ha...
Das wird doch wieder so ein Ding in Richtung "aber ich hätte da gerne noch..." ?
Denn Du wirst auf retrigger ja/nein eingehen, genauso wie eine unterschiedliche Blinkzeit, wie eben auch eine unterschiedliche Triggertime, sodas Du jede LED unabhängig betreibst. Von den Blinker vorzeitig beenden sprech ich dabei noch nicht mal.

Hallo,
was soll denn passieren wenn Du trigger(LED1) innerhalb von 500 ms erneut aufruftst ? Bleibt die LED dann an oder geht sie dennoch nach 500ms aus.

Ich finde es auch nicht besonders gut triger(lLED1) an unterschiedlichen Stellen aufzurufen. Sowas macht doch nur Probleme bei der Fehlersuche. Stell Dir mal vor die LED geht an, was sie aber eigentlich nicht sollte , und du suchst an der falschen Stelle. Dann verstehst du die Welt nicht mehr. Ich würde sowas an einer Stelle machen und die Bedingungen logisch verknüpfen.

1 Like

Hallo progger

Probier mal den MultiBlinker Sketch für dein Eisenbahnprojekt aus:

#define ProjectName "MultiBlinker"
//variables
//-----------------------------------------------
// Hier können die Leds und die Blinkzeiten angepasst werden.
constexpr uint8_t LedPins[] {9, 10, 11};
constexpr uint32_t BlinkZeiten[] {333, 666, 999};
//-----------------------------------------------
//structures
struct MULTIBLINKER
{
  uint8_t ledPin;
  uint32_t vorherigeMillis;
  uint32_t blinkMillis;
  void make(uint8_t ledPin_, uint32_t blinkZeit,  uint8_t element)
  {
    Serial.print(__func__), Serial.print(" für Blinker "), Serial.println(element);
    ledPin = ledPin_;
    pinMode(ledPin, OUTPUT);
    blinkMillis = blinkZeit;
  }
  void run(uint32_t jetztMillis)
  {
    if (jetztMillis - vorherigeMillis >= blinkMillis)
    {
      vorherigeMillis = jetztMillis;
      digitalWrite(ledPin, digitalRead(ledPin) ? LOW : HIGH);
    }
  }
} multiBlinkers[sizeof(LedPins)];
void setup()
{
  Serial.begin(115200);
  Serial.println(ProjectName);
  uint8_t element = 0;
  for (auto &multiBlinker : multiBlinkers)
  {
    multiBlinker.make(LedPins[element] , BlinkZeiten[element], element);
    element++;
  }
}
void loop()
{
  uint32_t currentMillis = millis();
  for (auto &multiBlinker : multiBlinkers) multiBlinker.run(currentMillis);
}
1 Like

Das ging aber auch schon mal besser :slight_smile:

Gut, wenn schon Codes kommen, gebe ich mal einen zur Diskussion.
Der beinhaltet 2 Varianten.
Man könnte die ggfls. sogar mischen.

Du kannst den Auslöser jederzeit aufrufen via

void chkBlinkStart(const int led)

Ich mache das einfach mit dem SerMon. Da kannst 0 bis 2 eingeben und der jeweilige Blinker blinkt. Kannst auch 012 eingeben, dann starten die fast zugleich.... :wink:

Die Arrays kannste auch auflösen zu struct und dann ein arrayOfStruct bauen oder wie auch immer.

// Forensketch Multiblinker
// https://forum.arduino.cc/t/impuls-an-3-leds-von-uberall-im-code/1137677

#include <Streaming.h>     // https://github.com/janelia-arduino/Streaming


const byte ledNums = 3;
const byte ledPin[ledNums] = {13, 12, 11};
const uint32_t ledBlinkFreq[ledNums] = {500, 400, 300};
uint32_t ledblinkStart[ledNums] = {0, 0, 0};
byte ledAktivSwitch[ledNums] = {0, 0, 0};                     // Variante 1
uint32_t ledAktivTime[ledNums] = {0, 0, 0};                   // Variante 2

void setup()
{
  Serial.begin(115200);
#if (defined(ARDUINO_AVR_MEGA) || defined(ARDUINO_AVR_MEGA2650)) // https://github.com/arduino/Arduino/issues/10764
  delay(300);
#endif
  Serial << (F("\r\nStart...\r\n")) << endl;
  for (byte b = 0; b < ledNums; b++)
  {
    pinMode(ledPin[b], OUTPUT);
  }
}


void  loop()
{
  ledFromSerial();
  for (byte b = 0; b < ledNums; b++)
  {
    blink(b);
  }
}
//
void ledFromSerial()
{
  chkBlinkStart(Serial.read() - '0');
}
//
void chkBlinkStart(const int led)
{
  if (led > -1 && led < ledNums)
  {
    digitalWrite(ledPin[led], HIGH);
    ledblinkStart[led] = millis();
    ledAktivSwitch[led] = 20;           // Variante 1
    ledAktivTime[led] = 10000;          // Variante 2
  }
}

//Variante 1
void blink(const byte led)
{
  if (ledAktivSwitch[led] < 1)
  {
    digitalWrite(ledPin[led], LOW);
  }
  else
  {
    if (millis() - ledblinkStart[led] > ledBlinkFreq[led])
    {
      digitalWrite(ledPin[led], !digitalRead(ledPin[led]));
      ledblinkStart[led] = millis();
      ledAktivSwitch[led]--;
      Serial<<_WIDTH(ledblinkStart[led], 8)<<" LED: "<<led<<' '<<(digitalRead(ledPin[led])?"EIN":"AUS")<<endl;
    }
  }
}
// Variante 2
/*
void blink(const byte led)
{
  uint32_t myMillis = millis();
  if (ledAktivTime[led] < ledBlinkFreq[led])
  {
    digitalWrite(ledPin[led], LOW);
  }
  else
  {
    if (myMillis - ledblinkStart[led] > ledBlinkFreq[led])
    {
      digitalWrite(ledPin[led], !digitalRead(ledPin[led]));
      ledblinkStart[led] = myMillis;
      ledAktivTime[led] -= ledBlinkFreq[led];
      Serial<<_WIDTH(ledblinkStart[led], 8)<<" LED: "<<led<<' '<<(digitalRead(ledPin[led])?"EIN":"AUS")<<endl;
    }
  }
}
*/
1 Like

nur mal so als Idee, wenn du dir eh schon eine Struktur mit Member Functions machst, dann kannst gleich deinem Array der Struktur die Pins und Intervalle übergeben. Brauchst aus meiner Sicht nicht doppelt im Programm anführen (trotz constexpr, schaut das für mich eigenartig aus...).

edit:

#define ProjectName "MultiBlinker"
//variables
//-----------------------------------------------
// https://forum.arduino.cc/t/impuls-an-3-leds-von-uberall-im-code/1137677/6
//-----------------------------------------------

class MultiBlinker {
  protected:
    uint8_t ledPin;
    uint32_t vorherigeMillis;
    uint32_t blinkMillis;
  public:
    MultiBlinker (uint8_t ledPin, uint32_t blinkMillis ) : ledPin(ledPin), blinkMillis(blinkMillis) {}

    void make() {
      Serial.print(__func__), Serial.print(" für Blinker am pin "), Serial.println(ledPin);
      pinMode(ledPin, OUTPUT);
    }
    void run(uint32_t jetztMillis = millis()) {
      if (jetztMillis - vorherigeMillis >= blinkMillis) {
        vorherigeMillis = jetztMillis;
        digitalWrite(ledPin, digitalRead(ledPin) == HIGH ? LOW : HIGH);
      }
    }
} multiBlinkers[] {
  {9, 333},
  {10, 666},
  {11, 999},
};

void setup() {
  Serial.begin(115200);
  Serial.println(ProjectName);
  for (auto &multiBlinker : multiBlinkers) multiBlinker.make();
}

void loop() {
  uint32_t currentMillis = millis();
  for (auto &multiBlinker : multiBlinkers) multiBlinker.run(currentMillis);
}

blink - Wokwi ESP32, STM32, Arduino Simulator

1 Like

Ich danke euch für eure vielen Ideen und Vorschläge :slight_smile:

Ich habe „befürchtet“, dass es wohl „am besten“ eine Klasse wird, aber soweit bin ich noch nicht… lerne aber daraus!

Den Aufruf werde ich auf alle Fälle in eine Art „Steuerfunktion“ legen, das macht Sinn :slight_smile:

Mal sehen, was ich mir aus den ganzen Ideen bauen kann :smiley:

Danke nochmals :+1:

mein erster Link kommt noch ohne OOP aus :wink:

1 Like

Im Übrigen geht mein Code auch retriggerbar OHNE das die Blinkzyklen verändert werden.
Dazu einfach den hier

  if (led > -1 && led < ledNums)
  {
    digitalWrite(ledPin[led], HIGH);
    ledblinkStart[led] = millis();
    ledAktivSwitch[led] = 20;           // Variante 1
    ledAktivTime[led] = 10000;          // Variante 2
  }

ändern auf Prüfung, ob noch was aktiv ist:

  if (led > -1 && led < ledNums)
  {
    if (ledAktivSwitch[led] < 1 &&               // Variante 1
        ledAktivTime[led] < ledBlinkFreq[led])   // Variante 2
    {
      digitalWrite(ledPin[led], HIGH);
      ledblinkStart[led] = millis();
    }
    ledAktivSwitch[led] = 20;           // Variante 1
    ledAktivTime[led] = 10000;          // Variante 2
  }

Sonst könnte es passieren, das Du zweimal die volle Länge HIGH beim retirggern bekommst.

1 Like

Hallo,
ich hätte da eventuell noch eine generelle Anmerkung zu Thema. Der generelle Lösungsansatz hängt wie so oft von der Aufgabenstellung und dem Anwendungsfall ab.
Bei Maschinensteuerungen werden oft Meldeleuchten benutzt die mit Ihrem Leuchtstatus unterschiedliche Funktionen signalisieren. z.B

  1. langsames blinken = Funktion ist vorgewählt
  2. Leuchte ein_ Funktion in Betrieb
  3. schnelles Blinken = Funktion ist gestört.

In der Regel stellt das Programm dann dazu die beiden unterschiedlichen Blinkfrequenzen zur Verfügung. Diese werden an einer zentralen Stelle gebildet und dem Programm über globale "Merker" zu Verfügung gestellt.
Die einzelnen Meldeleuchten werden dann an den betreffenden unterschiedlichen Stellen im Programm mittels logischen "oder" angesteuert. In Etwa so:

Betrieb
oder Vorwahl & Vorwahlblinker & nicht Störung
oder Störung & Störblinker & nicht Vorwahl
LED ein

zur Realisierung bietet sich in dem Fall eine Funktion an die z.B als Rückgabewert eine bool Variable zur Verfügung stellt und als Parameter die bool Variablen Betrieb, Vorwahl, VW-Blinker,ST-Blinker hat. z.B

bool meldeleuchte (bool betrieb, bool vorwahl, bool vwBlinker, bool stBlinker)

Der Vorteil bei der Variante, alle gestörten oder vorgewählten leuchten blinken gleichzeitig und nicht wild durcheinander.

Für eine Aufgabestellung im Modellbau ist natürlich ehr gewünscht das verschiedene Leuchten zu unterschiedlichen Zeitpunkten ehr zufällig ein und ausgehen.

Heinz

1 Like

Hallo,
hier mal ein kleines Beispiel zum Gedanken in #14 bei dem die Signale Vorwahl , Betrieb , Störung über Eingänge eingelesen werden.

const byte vwPin = 2;
const byte betrPin = 3;
const byte stPin = 4;
const byte ledPin = 13;

bool vwblink, stblink;
bool vorwahl , betrieb, stoerung;
bool ledstatus;
void setup() {
  // put your setup code here, to run once:
  pinMode(vwPin, INPUT_PULLUP);
  pinMode(betrPin, INPUT_PULLUP);
  pinMode(stPin, INPUT_PULLUP);
  pinMode(ledPin, OUTPUT);

  Serial.begin(115200);

}

void loop() {
  // put your main code here, to run repeatedly:

  vorwahl = !digitalRead(vwPin);
  betrieb = !digitalRead(betrPin);
  stoerung = !digitalRead(stPin);

  blinker(vwblink, stblink); // Zentraler Blinker
  
  // ausgabe auf eine LED
  //ledstatus = meldeLED(vorwahl, betrieb, stoerung, vwblink, stblink);
  //digitalWrite(ledPin, ledstatus);
  // oder in einer Zeile
  digitalWrite(ledPin, meldeLED(vorwahl, betrieb, stoerung, vwblink, stblink));

}

bool meldeLED( bool vw, bool betr, bool st, bool vw_bl, bool st_bl) {
  bool led;
  if (vw && vw_bl && !st) led = true;
  else if (st && st_bl) led = true;
  else if (betr && !st)led = true;
  else led = false;
  return led;
}

void blinker( bool&vw, bool &st) {
  static uint32_t altzeit ;
  static byte count = 0;

  if ( millis() - altzeit > 250) {
    altzeit = millis();
    count++;
    //Serial.println(count);
    st = bitRead(count, 0);   // 2Hz
    vw = bitRead(count, 2);   // 0,5 Hz
  }
}
1 Like

Hallo und Danke für euren Einsatz :slight_smile:

Mir ging es vorrangig um das Zusammenspiel der Funktionen, da ich ja eine brauche, die immer durchlaufen wird, um den Impuls zu generieren.
Und dann verschiedene LEDs damit zu steuern.

Ich habe mir dazu folgendes überlegt.
Vielleicht könnt ihr mir dazu noch eure Meinungen sagen?

Danke mal wieder :slight_smile:

Global

enum class LED : byte {
  NULL,
  EINS,
  ZWEI,
  WARTEN
};
LED status {LED::WARTEN};
LED status_vorher {LED::WARTEN};


void loop() {
  impuls();
}

void LED(byte trigger) {
  if (trigger== 0)  status = LED::NULL;
  if (trigger== 1)  status = LED::EINS;
  if (trigger== 2)  status = LED::ZWEI;
}

void impuls() {
  if (status == status_vorher) return;
  constexpr unsigned long IMPULS {500UL};
  static bool ledLock {false};
  static unsigned long impulsMillis {0};
  switch (status) {
    case LED::NULL  :
      if (!ledLock) {
        impulsMillis = millis();
        digitalWrite(pinLED0, HIGH);
        ledLock = true;
      }
      if (millis() - impulsMillis < IMPULS) return;
      digitalWrite(pinLED0, LOW);
      break;

    case LED::EINS  :
      if (!ledLock) {
        impulsMillis = millis();
        digitalWrite(pinLED1, HIGH);
        ledLock = true;
      }
      if (millis() - impulsMillis < IMPULS) return;
      digitalWrite(pinLED1, LOW);
      break;
    case LED::ZWEI  :
      if (!ledLock) {
        impulsMillis = millis();
        digitalWrite(pinLED2, HIGH);
        ledLock = true;
      }
      if (millis() - impulsMillis < IMPULS) return;
      digitalWrite(pinLED2, LOW);
      break;
    case LED::WARTEN  :
      break;
  }
  ledLock = false;
  status_vorher = status;
}

So könnte ich in der Steuerfunktion mit
LED(1) ;
die LED 1 pulsen.

Oder ist das ganz daneben?

Das schaffst du!

Das halte ich für eine weniger gute Idee!
Meine Empfehlung: LED1.trigger() oder so ähnlich.

Beispiel:


#include <CooperativeTask.h>


class NachleuchtLED : public Task
{
  private:
  const byte pin;
  bool triggerSignal;

  public:
  NachleuchtLED(const byte pin):pin(pin),triggerSignal(false){}
  
  virtual void init() override
  {
    pinMode(pin,OUTPUT);
  }
  
  virtual void run() override
  {
    TaskBlock
    {
      taskWaitFor(triggerSignal);
      triggerSignal = false;
      digitalWrite(pin,HIGH);
      taskPause(500);
      digitalWrite(pin,LOW);
    }
  }
  
  void trigger()
  {
    triggerSignal = true;
  }
};

  
constexpr int leds {3}; // anzahl
NachleuchtLED led[leds] {13,12,11};

void serialEvent()
{
   int index = Serial.read()-'0';
   switch(index)
   {
     case 0 ... leds-1: led[index].trigger(); break; 
   }
}
  

 
void setup() 
{
  Serial.begin(9600);
  Serial.println("Start");
  
  scheduler.init();
}

void loop() 
{
  scheduler.run();
}

Das Programm reagiert auf Eingaben der seriellen Konsole.
Die Zahlen 0 bis 2 bringen die LEDs an den Pins 13 bis 11 jeweils für 500 ms zu leuchten

CooperativeTask.zip (10,6 KB)
(für die Insider: die Lib ist leicht verändert)

1 Like

Wenn du es einigermaßen richtig machst, wird jede Funktion "immer durchlaufen", wenn du das willst.
Du möchtest einen zentralen Blink-Geber, damit alle Lämpchen deiner Anzeigetafel, die gerade blinken sollen, synchron blinken. (Oder war das @Rentner?)

Falls ja, und du damit in das objektorientierte Programmieren einsteigen willst, mach lieber aus jeder Led ein Element der Klasse LED, das dann einen der Zustände AN, AUS, BLINKEN hat.
z.B. sowas

enum LEDSTATE {AUS, AN, BLINKEN};
class LED {
   const uint8_t pin;
   LEDSTATE state = AUS;
   static bool blinker;
   static unsigned long timer;
   static const uint16_t ON, OFF;
public: 
   static void ZentralBlinker() {
    if (millis() - timer >= (ON+OFF)) timer += ON+OFF;
    blinker = (millis() - timer <= ON);   
   }

  LED(uint8_t p): pin(p) {}
  void set(LEDSTATE s) {state = s;}
  void init() {
    pinMode(pin, OUTPUT);
    state = AUS;
  }
  void show() {
    switch (state) {
      case AUS: digitalWrite(pin, LOW); break;
      case AN:  digitalWrite(pin, HIGH); break;
      default: digitalWrite(pin, blinker); break;
    }
  }
};

LED l1 {LED_BUILTIN};
LED l2 {2};

// Der Zentrale Blinkgeber
bool LED::blinker;
unsigned long LED::timer;
// Zentrale Blink-Parameter
const uint16_t LED::ON {100};
const uint16_t LED::OFF {900};

void setup() {
   l1.init();
   l2.init();
   l2.set(BLINKEN);  // Beispielcode
}

void loop() {

// Beispielcode: Dies kann irgendwo beliebig gemacht werden
  uint16_t zyklus = millis() & 0x3FFF;  // 16384 ms Zyklus
  if (zyklus < 3000) l1.set(AUS);
  else if (zyklus < 8000) l1.set(BLINKEN);
  else if (zyklus > 12000) l1.set(AN); 
  // zwischen 8000 und 12000 wird set nicht aufgerufen, l1 bleibt blinkend

// Dies soll immer ausgeführt werden
  LED::ZentralBlinker();
  l1.show();  
  l2.show();
}

Da habe ich einen zentralen Blinker als static Elemente der Klasse LED hinzugefügt.
Falls das verwirrt, kann der auch global ausserhalb der Klasse angelegt werden.

1 Like

Hallo,
Ich bin mir da immer noch nicht sicher von was du redest, Geht es Dir um einen einzigen Impuls der ausgegeben wird .
Verlängerter Impuls ?

Die letzen Beispiele bezogen sich häufig auf Blinker.

Egal wenn Du mehrere davon benötigst die unabhängig voneinander laufen sollen dann benötigst du Speicher in dem die aktuellen Zustände gespeichert werden. Das kann man mit einer Struktur machen von der man z.B ein Array anlegt das ist dann C Style , oder man macht es mir einer Classe das ist dann C++

So schwer ist das nicht.

Heinz

1 Like

Hallo @michael_x , Na ja das war ein grundsätzlicher Gedankengang von mir zu Thema in #14
Gruß Heinz

1 Like