Zufällig 5 LED´s an und aus

Ich benötige für ein Projekt eine zufällig leuchtende LED (immer nur eine von 5), die nach dem Intervall auch wieder ausgehen soll. Aber irgendwie leuchtet eine LED, teilweise erlischt diese, aber es leuchtet dann eine andere LED, während die andere weiter leuchtet.

Kurzum : Die LED die per Zufall leuchtet, soll danach auch wieder ausgehen.
Was habe ich vergessen ?
Delays kann ich nicht gebrauchen, da der Sketch unterbrochen wird.....

const unsigned int MIN_LED_PIN = 3;         // globale Konstante für ersten Pin mit LED
const unsigned int MAX_LED_PIN = 7;         // globale Konstante für letzten Pin mit LED

unsigned long neuMillis, altMillis, intervall=500;
bool ledStatus;

void setup() {
  Serial.begin(38400);                      // Serielle Kommunikation mit 38400 Baud starten

  pinMode(A7, INPUT);                       // Analogen Pin A1 als Eingang definieren
  randomSeed(analogRead(A7));               // Zufallszahlen-Sequenz initialisieren. Durch analogRead am nicht verbundenen Analogeingang wird gewährleistet, 
                                            // dass bei jedem Programmstart eine neue Zufallszahlenfolge verwendet wird.

  for(int pin=MIN_LED_PIN; pin<=MAX_LED_PIN; pin++){
  pinMode(pin, OUTPUT);                   // PINs von MIN_LED_PIN bis MAX_LED_PIN als OUTPUT festlegen.
  altMillis=0;
  }
}


void loop() {
  int zufallsWert = random(MIN_LED_PIN, MAX_LED_PIN+1);     // Da der obere Wert exklusiv angegeben wird, 
                                                            // addieren wir hier 1 dazu um auch die letzte LED zu integrieren
  
  int zufallsAnzahl = random(2);               // Zufällige Blink-Anzahl ermitteln (maximal 4)

  for(int i=0;i<zufallsAnzahl;i++){
    neuMillis = millis();
    if( (neuMillis - altMillis) >= intervall){
    altMillis = neuMillis;
    ledStatus = !ledStatus;
    digitalWrite(zufallsWert, ledStatus);

  }
 }
}

Vielleicht so ?

using uint = unsigned int;
using ulong = unsigned long;

constexpr byte LED_PINS[5] {3, 4, 5, 6, 7};
constexpr byte NUM_LED_PINS {sizeof(LED_PINS)};
constexpr byte MAX_LED_PINS_IDX {NUM_LED_PINS - 1};

// Time im milliseconds (ms)
constexpr ulong LIGHT_DELAY_MS {500};    // Time between two LDR measurements

// Class / struct Definition(s)

//////////////////////////////////////////////////////////////////////////////
/// @brief Helper class for non-blocking execution of
/// code sections at certain intervals.
///
//////////////////////////////////////////////////////////////////////////////
class Interval {
  public:
    //////////////////////////////////////////////////////////////////////////////
    /// @brief  Determine whether the specified interval has expired.
    ///
    /// @param duration    Interval duration
    /// @return true       when the interval has elapsed
    /// @return false      interval not elapsed yet
    //////////////////////////////////////////////////////////////////////////////
    bool operator()(const ulong duration) {
      if (false == isStarted) {
        return start(false);
      }
      return (millis() - timeStamp >= duration) ? start(true) : retFalse();
    }

  private:
    bool start(bool state = false) {
      isStarted = !state;   // Set the value to true on the first call
      timeStamp = millis();
      return state;
    }
    bool retFalse() {
      return false;
    }

  private:
    bool isStarted {false};   // Flag = true if the first Operator() call has been made.
    ulong timeStamp {0};
};

Interval timer;

void setup() {
  Serial.begin(115200);
  for (auto pin : LED_PINS) { pinMode(pin, OUTPUT); }
  randomSeed(A0);
}

void loop() {
  static byte lastLedIdx {0};

  if (true == timer(LIGHT_DELAY_MS)) {
    byte ledIdx = random(0, NUM_LED_PINS);
    if (ledIdx == lastLedIdx) { ledIdx = (ledIdx + 1) % MAX_LED_PINS_IDX; } 
    lastLedIdx = ledIdx;
    for (auto pin : LED_PINS ) { digitalWrite(pin, LOW); }
    digitalWrite(LED_PINS[ledIdx], HIGH);
  }
}

Zum ausprobieren:

vielleicht so:

const unsigned int MIN_LED_PIN = 3;         // globale Konstante für ersten Pin mit LED
const unsigned int MAX_LED_PIN = 7;         // globale Konstante für letzten Pin mit LED

unsigned long altMillis, neuMillis;
const unsigned long intervall = 500;

uint8_t blinkAnzahl;
uint8_t blinkPin;

void setup()
{
  Serial.begin(38400);                      // Serielle Kommunikation mit 38400 Baud starten
  pinMode(A7, INPUT);                       // Analogen Pin A1 als Eingang definieren
  randomSeed(analogRead(A7));               // Zufallszahlen-Sequenz initialisieren. Durch analogRead am nicht verbundenen Analogeingang wird gewährleistet,
  // dass bei jedem Programmstart eine neue Zufallszahlenfolge verwendet wird.
  for (int pin = MIN_LED_PIN; pin <= MAX_LED_PIN; pin++)
  {
    pinMode(pin, OUTPUT);                   // PINs von MIN_LED_PIN bis MAX_LED_PIN als OUTPUT festlegen.
    altMillis = 0;
  }
}

void loop()
{
  if (blinkAnzahl == 0)
  {
    //   Variante 1:  - Alle Pins gesichert ausmachen
    for (byte b = MIN_LED_PIN; b <= MAX_LED_PIN; b++)
    {
      digitalWrite(b, LOW);
    }
    //   Variante 2: - letzten aktiven Pin gesichert ausmachen
    //  digitalWrite(blinkPin, LOW);
    blinkPin = random(MIN_LED_PIN, MAX_LED_PIN + 1);   // Da der obere Wert exklusiv angegeben wird,
    // addieren wir hier 1 dazu um auch die letzte LED zu integrieren
    blinkAnzahl = random(1, 5);              // Zufällige Blink-Anzahl ermitteln (maximal 4)
  }
  else
  {
    neuMillis = millis();
    if ( (neuMillis - altMillis) >= intervall)
    {
      altMillis = neuMillis;
      digitalWrite(blinkPin, !digitalRead(blinkPin));
      if (!digitalRead(blinkPin))
      {
        blinkAnzahl--;
      }
    }
  }
}

Dankeschöööön. Beide Variantien funktionieren :smiley: Jetzt muß ich schauen daß ich diese in mein Projekt eingebaut bekomme...... Kai-R Du hast ja schon mal geholfen :smiley: Du kennst mein Projekt mit dem Schießstand. Jetzt soll noch ein zweiter Spielmodus hinzu. 1 Punkt pro Treffer (nur 1, weil man nie weiß, welches Ziel leuchtet) wenn während des leuchtens einer Diode getroffen wird.
Aber dieses Woki.com hab ich mir erst mal in die Lesezeichen gesetzt. Interressante Seite.

Ich habe noch eine kleine Änderung eingebaut. Falls Random zwei mal hintereinander die gleiche LED ergibt, wird der Array-Index (Led-Pins) um 1 erhöht, damit auf jeden Fall ein Wechsel stattfindet. Damit der Index durch die Addition nicht überschritten werden kann, findet auch eine modulo (%) Operation statt.

Ja, Wokwi eignet sich sehr gut um Programmcode auszuprobieren ohne groß etwas aufbauen zu müssen. Ist schon sehr praktisch für viele Dinge.

Hallo dk0975

oder sowas :slight_smile:

constexpr uint8_t LedPins[] {9, 10, 11}; // füge hier die Led´s hinzu

void setup()
{
  for (auto LedPin : LedPins)  pinMode(LedPin, OUTPUT);
}
void loop()
{
  static uint32_t previousTime = millis();
  constexpr uint32_t Interval = 1000;
  if (millis() - previousTime >= Interval)
  {
    previousTime = millis();
    uint8_t randomNumber;
    static uint8_t randomNumberOld = random(0, sizeof(LedPins));
    digitalWrite (LedPins[randomNumberOld], LOW);
    do
    {
      randomNumber = random(0, sizeof(LedPins));
    }
    while (randomNumberOld == randomNumber);
    randomNumberOld = randomNumber;
    digitalWrite (LedPins[randomNumber], HIGH);
  }
}
1 Like

Nach dem Vorschlag von @paulpaulson zum Vermeiden der doppelten Random-Zahl, noch etwas aufgehübscht:

using uint = unsigned int;
using ulong = unsigned long;

constexpr byte LED_PINS[5] {3, 4, 5, 6, 7};

// Time im milliseconds (ms)
constexpr ulong LIGHT_DELAY_MS {500};   // Time between two LDR measurements

// Class / struct Definition(s)

//////////////////////////////////////////////////////////////////////////////
/// @brief Helper class for non-blocking execution of
/// code sections at certain intervals.
///
//////////////////////////////////////////////////////////////////////////////
class Interval {
public:
  //////////////////////////////////////////////////////////////////////////////
  /// @brief  Determine whether the specified interval has expired.
  ///
  /// @param duration    Interval duration
  /// @return true       when the interval has elapsed
  /// @return false      interval not elapsed yet
  //////////////////////////////////////////////////////////////////////////////
  bool operator()(const ulong duration) {
    if (false == isStarted) { return start(false); }
    return (millis() - timeStamp >= duration) ? start(true) : retFalse();
  }

private:
  bool start(bool state = false) {
    isStarted = !state;   // Set the value to true on the first call
    timeStamp = millis();
    return state;
  }
  bool retFalse() { return false; }

private:
  bool isStarted {false};   // Flag = true if the first Operator() call has been made.
  ulong timeStamp {0};
};

// Class / struct Definition(s) end

//
// Global variables/objects
//
Interval timer;

//
// Functions
//
template <size_t N> void setLeds(const byte (&pins)[N], byte &lastIdx ){
  byte idx;
  do {   // Prevents the same (random)number from being used twice in a row.
    idx = random(0, N);   
  } while (idx == lastIdx);   
  digitalWrite(pins[lastIdx], LOW);
  lastIdx = idx;
  digitalWrite(pins[idx], HIGH);
}

//
// Main
//
void setup() {
  Serial.begin(115200);
  for (auto pin : LED_PINS) { pinMode(pin, OUTPUT); }
  randomSeed(A0);
}

void loop() {
  static byte currentLedIndex {0};
  if (true == timer(LIGHT_DELAY_MS)) { setLeds(LED_PINS, currentLedIndex); }  
}

  1. Analoge Eingänge Muß und soll man nicht als Eingänge definieren.
  2. A7 auf einem Arduino 328 SMD ist kein Digitales Pin sondern nur analog.
    Grüße Uwe

Okay.... ein Teil des Sketches stammt aus einem Youtube-Video, allerdings war es da der Eingang A1 (der ist ja bei mir belegt, daher der nächste freie Platz). In meinem Fall ist es der 2560 Mega (wobei das mit dem A7 wohl nichts ändert).

Kanst so belassen A7 am Mega kann man als Digital nutzen.

Im Arduino MEGA 2560 mit dem ATmega2560 sind alle analogen Eingänge auch digitale Pins.
Grüße Uwe

Weit weg kann ich nicht mehr wirklich sein..... ich da noch ein Problem mit der Trefferabfrage.....
Leider macht er´s nicht so wie´s sein soll. Die Punkte sollen nur gezählt werden wenn ein Treffer registriert wurde UND die dazugehörige Diode leuchtet. Von der Theorie her müssten die Dioden doch aufgrund der Arrays gleich zugeordnet sein, wie die Punkte zu den Zielen.....

// Bibliotheken einbinden
#include <Wire.h> // Wire Bibliothek einbinden
#include <LiquidCrystal_I2C.h> // Vorher hinzugefügte LiquidCrystal_I2C Bibliothek einbinden
LiquidCrystal_I2C lcd(0x27, 16, 2); //Hier wird festgelegt um was für einen Display es sich handelt. In diesem Fall eines mit 16 Zeichen in 2 Zeilen und der HEX-Adresse 0x27. Für ein vierzeiliges I2C-LCD verwendet man den Code "LiquidCrystal_I2C lcd(0x27, 20, 4)" 

// Pins für Targets festlegen -> 10mm Target (A1) bis 50mm Target (A5) - A0 dient zur Rückstellung der Punkte
constexpr byte TARGET_PINS[] {A1, A2, A3, A4, A5, A0};
constexpr byte NUM_TARGET_PINS {sizeof(TARGET_PINS) / sizeof(TARGET_PINS[0])};

//------------------------- ab hier Random LED´s
using uint = unsigned int;
using ulong = unsigned long;
constexpr byte LED_PINS[5] {3, 4, 5, 6, 7};
// Time im milliseconds (ms)
constexpr ulong LIGHT_DELAY_MS {3000};   // Time between two LDR measurements
class Interval {
public:
  bool operator()(const ulong duration) {
    if (false == isStarted) { return start(false); }
    return (millis() - timeStamp >= duration) ? start(true) : retFalse();
  }

private:
  bool start(bool state = false) {
    isStarted = !state;   // Set the value to true on the first call
    timeStamp = millis();
    return state;
  }
  bool retFalse() { return false; }

private:
  bool isStarted {false};   // Flag = true if the first Operator() call has been made.
  ulong timeStamp {0};
};

Interval timer;
template <size_t N> void setLeds(const byte (&pins)[N], byte &lastIdx ){
  byte idx;
  do {   // Prevents the same (random)number from being used twice in a row.
    idx = random(0, N);   
  } while (idx == lastIdx);   
  digitalWrite(pins[lastIdx], LOW);
  lastIdx = idx;
  digitalWrite(pins[idx], HIGH);
}
//------------------------- bis hier Random LED´s

// Punkte für 10mm Target bis 50mm Target - 0 Punkte zur Rückstellung der Punkte
constexpr byte T_POINTS_VALUE[NUM_TARGET_PINS] {50, 40, 30, 20, 10, 0};

// Startpunkte auf 0
unsigned int score = 0;

int Piezo = 2;

// Abfrage der Pins - N enthält die Anzahl der Pins
template <size_t N> bool checkPins(const byte (&pins)[N], byte &index) {
  for (; index < N; ++index) {
    if (HIGH == digitalRead(pins[index])) { return true; }
  }
  return false;
}

uint8_t numDigits(unsigned int number) {
  uint8_t digits = (number == 0) ? 1 : 0;
  while (number) {
    number /= 10;
    ++digits;
  }
  return digits;
}

void reset(){  // Reset wenn Treffer = 0 Punkte, Kontakt an A0 
    lcd.setCursor(10, 0);//Hier wird die Position des ersten Zeichens festgelegt. In diesem Fall bedeutet (0,0) das erste Zeichen in der ersten Zeile. 
    lcd.print("    "); 
    lcd.setCursor(8, 1);//Hier wird die Position des ersten Zeichens festgelegt. In diesem Fall bedeutet (0,0) das erste Zeichen in der ersten Zeile. 
    lcd.print("    ");
  }


void setup() {
  //Serial.begin(9600);
  Serial.begin(19200);
  for (auto pin : TARGET_PINS) {
  for (auto pin : LED_PINS) { pinMode(pin, OUTPUT); }
  randomSeed(A7);
    pinMode(pin, INPUT);
    pinMode(Piezo, OUTPUT); 
    lcd.init(); //Im Setup wird der LCD gestartet 
    lcd.backlight(); //Hintergrundbeleuchtung einschalten (lcd.noBacklight(); schaltet die Beleuchtung aus).
    lcd.setCursor(0, 0);//Hier wird die Position des ersten Zeichens festgelegt. In diesem Fall bedeutet (0,0) das erste Zeichen in der ersten Zeile. 
    lcd.print("Treffer :"); 
    lcd.setCursor(0, 1);// In diesem Fall bedeutet (0,1) das erste Zeichen in der zweiten Zeile. 
    lcd.print("Gesamt : "); 
    //lcd.print(score);
    }
}

void loop() {
  static unsigned int oldScore {0};
  static byte currentLedIndex {0};
  if (true == timer(LIGHT_DELAY_MS))  { setLeds(LED_PINS, currentLedIndex); }  

  byte index {0};
  // Es wurde ein Treffer registriert. Je nach Index von 10mm (0) bis 50mm (4)
  if ((true == checkPins(TARGET_PINS, index)) && (true == (LED_PINS, index))) { 
    digitalWrite(Piezo, HIGH);
    delay(50);
    digitalWrite(Piezo, LOW);
    delay(250);
    lcd.setCursor(10, 0);//Hier wird die Position des ersten Zeichens festgelegt. In diesem Fall bedeutet (0,0) das erste Zeichen in der ersten Zeile. 
    lcd.print(T_POINTS_VALUE[index]); 
    delay(500);
    score = (T_POINTS_VALUE[index] > 5) ? score + T_POINTS_VALUE[index] : 0;
    reset();    
    oldScore = score;
    lcd.setCursor(9, 1);//Hier wird die Position des ersten Zeichens festgelegt. In diesem Fall bedeutet (0,0) das erste Zeichen in der ersten Zeile. 
    lcd.print(score);
  }
}

Alternativ (Danke Kai-R)

Wokwi

Ja.
Das wird aber schwierig bei sechs Targetpins und nur fünf LEDs.
:blush:

Der sechste Pin soll "nur" dazu dienen, um die Punkte auf 0 zu setzen. Die wichtigsten sind A1 bis A5. Sonst müßte ich mal schauen ob A0 separat gelegt werden sollte..... (also aus dem Array raus).

if ((true == checkPins(TARGET_PINS, index)) && (true == (LED_PINS, index)))

Was hast Du Dir eigentlich bei der Konstruktion rechts vom && gedacht? Was soll das machen?

Ich habe jetzt gerade keine Zeit mir das genauer anzugucken aber probier mal

if ((true == checkPins(TARGET_PINS, index)) && (index == currentLedIndex))

Der pin ist vollkommen egal und war so im Code des TO schon vorhanden.

Das Ganze ist nur der Übersichtlichkeit geschuldet, wie man eine Zeile weiter sieht.
Von daher ist der Hinweis zwar berechtigt, ändert aber an der Funktionalität nichts.

Darf ich Dich Gott nennen ?? Ist ja wieder mal ein Volltreffer !!
Nuja, mit dem rechten Bereich habe ich mir gedacht :
Wenn die Trefferabfrage (checkPins der Targets) wahr ist UND die Led´s aus dem Index wahr, dann.... hatte schon mehreres versucht, index == currentIndex noch nicht (habe ich auch nicht wirklich mit verbinden können.

Ne Du ... das mit dem Gott lass mal. Ich bin einfach nur ein Hobbyprogrammierer.

Aber mach sowas: true == (LED_PINS, index) nicht wieder. Das ergibt keinen Sinn. Der Kommaoperator bringt hier nichts und der Ausdruck wird nur wahr, wenn index = 1 ist.
Der erste Teil des if-Ausdrucks prüft den Rückgabewert einer Funktion.

Die Referenz auf die beiden Variablen index und currentLedIndex werden an die jeweilig aufgerufenen Funktionen checkPins() und setLeds() übergeben und innerhalb dieser Funktion die Werte der Variablen auf den passenden Indexwert des jeweiligen Arrays gesetzt. Die erstgenannte Funktion hat aber zusätzlich noch einen Zustand als Rückgabewert.

Okay.... ich gelobe Besserung :rofl: :rofl:
Da A0 ja als Ziel mit den Dioden jetzt ja nicht abgefragt wird, habe ich jetzt eine Tasterabfrage mit IF eingesetzt : Wenn A0=HIGH, dann setze Punkte auf 0 (bzw. überschreibe diese.....).
Und es funktioniert.

Jetzt geht´s erst mal an´s konstruieren des Objekt´s und anschließend in den Drucker.
Ich weiß jetzt schon, wenn ich das Ding konstruire (was mir wesentlich leichter fällt), wie ich einen Timer einbau, das man nur eine bestimmte Zeit für diese Runde hat und nach Ablauf dieser das Spiel vorbei ist...... aber das hat Zeit.....

Mal nebenbei : Ich möchte nicht wissen, wieviele Jahre man benötigt, um solche Sketche einigermaßen zustande zu bringen. Ich habe gemerkt, wenn ich mal einige Wochen Pause mache, muß ich was aufarbeiten :rofl:

Muss noch mal kurz nerven..... Zur Zeit habe ich das ganze an einem Mega 2560.
Ich glaube nicht das sich das ganze auf einen Arduino UNO oder gar Nano reduzieren lässt, oder ?
Also zumindest nicht mit diesem Sketch. Beim UNO gibt es ja die zwei Pin´s für SCL und SDA, die wohl aber mit A4 und A5 verknüpft sind. Beim NANO geht SCL und SDA ja nur auf A4 und A5. Oder lieg ich da verkehrt ?