Lichterorgel

Ich bin gerade dabei, mit dem UNO eine kleine Lichterorgel zu bauen. Nun stehe ich aber vor einem Problem. Ich würde gerne, nun jedes Programm (Case-Anweisung) solange wiederholen bis S1 einmalig gedrückt wird. Das Problem ist aber, dass mein Programm sich mehrmals in delays befindet und somit nicht zwingend der Tasterdruck registriert wird.

int led_delay;
//Ausgänge der LED
int ledPins[] = {3, 5, 6, 9, 10};
//Länge des Array
int pinCount = 5;
//Merker Switch Case Anweisung
int led_step;
int case_step_max;
int led_random;

void setup(){
  //Asugänge als Ausgänge initialisieren
  for(int ledPin = 0; ledPin < pinCount; ledPin++){
    pinMode(ledPins[ledPin], OUTPUT);
  }
  
  led_step = 0;
 
}

void loop(){

  switch(led_step){
    case 0://LED einzeln AN || Rechtslauf
      led_delay = 50;//Zeit in ms
      case_step_max = 2;
      
      for(int case_step = 0; case_step < case_step_max; case_step++){
        for(int ledPin = 0; ledPin < pinCount; ledPin++){
          digitalWrite(ledPins[ledPin], HIGH);
          delay(led_delay);
          digitalWrite(ledPins[ledPin], LOW);
        }
      }
           
       led_step++;
       break;
     
    case 1://LED einzeln AN || Rechtslauf
      led_delay = 50;//Zeit in ms
      case_step_max = 2;
      
      for(int case_step = 0; case_step < case_step_max; case_step++){
        for(int ledPin = pinCount - 1; ledPin >= 0 ; ledPin--){
          digitalWrite(ledPins[ledPin], HIGH);
          delay(led_delay);
          digitalWrite(ledPins[ledPin], LOW);
        }
      }
      
       led_step++;
       break;
    
    case 2://LED alle AN blinkend
      led_delay = 50;//Zeit in ms
      case_step_max = 5;
      
      for(int case_step = 0; case_step < case_step_max; case_step++){
        for(int ledPin = 0; ledPin < pinCount; ledPin++) {
          digitalWrite(ledPins[ledPin], HIGH);}
        delay(led_delay);
        for(int ledPin = pinCount - 1; ledPin >= 0; ledPin--){
          digitalWrite(ledPins[ledPin], LOW);}
        delay(led_delay);
      }
  
        
      led_step++;
      break;
    
    case 3://LED fading
      led_delay = 10;//Zeit in ms
      case_step_max = 1;
      
      for(int case_step = 0; case_step < case_step_max; case_step++){
        for(int ledPin = 0; ledPin < pinCount; ledPin++){
          for(int fadeValue = 0 ; fadeValue <= 255; fadeValue +=5) { 
          analogWrite(ledPins[ledPin], fadeValue);         
          delay(led_delay);} 
        }
      
        for(int ledPin = 0; ledPin < pinCount; ledPin++){
          for(int fadeValue = 255 ; fadeValue >= 0; fadeValue -=5) { 
          analogWrite(ledPins[ledPin], fadeValue);         
          delay(led_delay);} 
        }
      }
       
       led_step++;
       break;
       
    case 4://LED random
      led_delay = 25;//Zeit in ms
      case_step_max = 50;
      
      for(int case_step = 0; case_step < case_step_max; case_step++){
          led_random = random(1, 5);
          digitalWrite(ledPins[led_random], HIGH);
          delay(led_delay);
          led_random = random(1, 5);         
          digitalWrite(ledPins[led_random], LOW);
          delay(led_delay);
        }
      
        for(int ledPin = pinCount - 1; ledPin >= 0; ledPin--){
          digitalWrite(ledPins[ledPin], LOW);}
      
       led_step++;
       break;

    default:
      led_step = 0;
      break;
  }
}

Benutze millis() wie im http://playground.arduino.cc/Learning/BlinkWithoutDelay_de Grüße Uwe

... zum einem dieses ... zum anderen vielleicht noch einen Interrupt bemühen, damit der Taster konsequent immer erkannt wird. Schau mal hier: http://arduino.cc/de/Reference/AttachInterrupt

Ich werde dann mal versuchen, das Programm erst einmal auf millis() umzuschreiben. Die ersten Versuche gingen gerade schonmal in die Hose. Hab erstmal die unnötigen Sachen rausgeschmießen.

TERWI: ... zum einem dieses ... zum anderen vielleicht noch einen Interrupt bemühen, damit der Taster konsequent immer erkannt wird. Schau mal hier: http://arduino.cc/de/Reference/AttachInterrupt

Interrups direkt von Taster machen immer Probleme wegen des Prellens und in der Interruptroutine kann man softwaremäßig nicht Entprellen Grüße Uwe

void loop()
{
    interval = 1000;
 
    for(int led_pin = 0; led_pin < led_count; ) {
      if(millis() - prev_ms > interval){
        prev_ms = millis();
        digitalWrite(led_pins[led_pin], LOW);
        digitalWrite(led_pins[led_pin++], HIGH);
      }
    }
  }

Kann mir einer weiterhelfen, wo hier der Fehler sitzt? Er schaltet jede Sek. eine weitere LED ein, bis die 5. LED an ist. Jedoch schaltet er mir nicht den Ausgang beim Weitergehen aus.

Schematik:

1s: X O O O O
2s: X X O O O
3s: X X X O O
4s: X X X X O
5s: X X X X X

Sollte aber eigentlich so ablaufen:

1s: X O O O O
2s: O X O O O
3s: O O X O O
4s: O O O X O
5s: O O O O X
void loop()
{
    static int led_pin = 0;
    interval = 1000;
 
      if(millis() - prev_ms > interval){
       prev_ms = millis();
       digitalWrite(led_pins[led_pin], HIGH);
       if(led_pin != 0)
       {
       digitalWrite(led_pins[led_pin-1], LOW);
       } else {
      digitalWrite(led_pins[led_count-1], LOW);
       }
       if(led_pin < led_count-1)
       {
       led_pin++;
       } else {
       led_pin = 0;
       }
     }
  }

Problem ist, dass die For-Schleife am Stück verarbeitet sind. Und du bei dem Intervall bei nem beliebigen PIN zufällig abgreifst… lösen könntest du das so, wie oben beschrieben.
Das ganze ist jetzt so ausgelegt, dasses immer wieder von vorne losgeht

uwefed: Interrups direkt von Taster machen immer Probleme wegen des Prellens und in der Interruptroutine kann man softwaremäßig nicht Entprellen Grüße Uwe

Werter Uwe, da möchte ich dir prinzipiell aber doch widersprechen ! Logo kann man das, wenn man z.B. den Modus auf CHANGE setzt und die millis dazwischen prüft ... bischen aufwendig, aber geht prima & sicher ! Bekannterweise kann man auch mit einem RC-Glied auf einfache, mechanische Weise abhelfen.

Ich möchte mal ausdrücklich Uwe zustimmen: Für Taster braucht man keine Interrupt-Routinen, bzw. man gewinnt nichts. Man hat nur Arbeit mit dem Entprellen, weil z.B. delay(2) verboten ist.

loop() sollte in maximal wenigen milli-Sekunden fertig sein, dann kann man Taster sehr schön direkt einlesen und bei Bedarf sogar zwischen kurzen (50 - 1000ms) und langen ( > 2500ms ) unterscheiden.

Klar kann man mit RC-Gliedern auch entprellen, wenn kein µC das Signal benutzt. Aber wenn doch, ist eine Zeile Code

delay(2); // entprellen

doch besser als zwei diskrete Bauteile...

Ich widerspreche auch dir ausdrücklich. :roll_eyes:

loop() sollte in maximal wenigen milli-Sekunden fertig sein, dann kann man Taster sehr schön direkt einlesen und bei Bedarf sogar zwischen kurzen (50 - 1000ms) und langen ( > 2500ms ) unterscheiden.

... das geht mit einer ISR wesentlich eleganter.

loop() sollte in maximal wenigen milli-Sekunden fertig sein, dann kann man Taster  sehr schön direkt einlesen und
bei Bedarf sogar zwischen kurzen (50 - 1000ms) und langen  ( > 2500ms ) unterscheiden.

eben genau das kann man mit einer ISR wesedntlich besser / einfacher / schneller erreichen. Eine volatile-var gesetzt und der Rest in der LOOP kan darauf reagieren ...

TERWI: eben genau das kann man mit einer ISR wesedntlich besser / einfacher / schneller erreichen. Eine volatile-var gesetzt und der Rest in der LOOP kan darauf reagieren ...

Ach? Den Zustand des Tasters in realtime in eine Variable abbilden und der loop() kommt dann wegen der delay()'s nach einer Ewigkeit vorbei um zu kontrollieren ob die Variable gesetzt ist oder nicht? Da ist ein schneller loop mit zeitverzögerungen durch millis() realisiert und sofortige Kontrolle des Tasters besser.

Grüße Uwe

Klar ist eine ISR schneller.
Ist eben nur zu schnell für einen prellenden Taster.

Sicher, in einer ISR kann man, mit etwas Aufwand, Tasterprellen erkennen.
Auch genau analysieren, wenn man möchte.

Bei einer ISR geht es darum, trotz einer relativ langsamen loop, keinen Signalwechsel zu verpassen.
Beim Entprellen geht es darum, unwichtige Signalwechsel zu ignorieren.

Das ist so ziemlich die entgegengesetzte Aufgabenstellung :wink:

Und was ist eleganter als eine einzige Zeile ?

delay(2); // entprellen

Die kann man nun nicht in eine ISR schreiben, da sind wir uns doch einig, oder ? :wink:


Nachtrag:
Sorry sschultewolter, dass wir deinen Thread so missbrauchen.
Für normale Verzögerungen, z.B. wie lange eine LED leuchten soll, ist delay() natürlich Mist, und durch sowas mit

if (millis() - lasttime > WARTEZEIT) { /* jetzt gehts weiter: LED wieder aus  */  }

zu ersetzen. … Damit man während dieser Zeit auch echte Tasterwechsel mitbekommt.
Und for-Schleifen sollte man möglichst auch wegkriegen, loop kommt doch sowieso gleich wieder vorbei …

Wenn dir Interrupt-Routinen erstmal zu kompliziert sein sollten, deine Lichtorgel kriegst du ohne hin.

Wenn du dich damit beschäftigen willst, auch nicht verkehrt. Wie TERWI richtig schreibt

bischen aufwendig, aber geht prima

:wink:
Ist ja nicht schlimm, etwas komplizierter zu machen als nötig, dem Arduino ist es egal.

(Eher brauchst du mal einen Timer Interrupt, wenn LED Multiplexen wirklich schnell werden soll, also mehrere LED schneller schalten müssen als du gucken kannst)

Danke schon mal für die Hilfe. Euer “Offtopic” sei euch verziehen. Habe soweit die gewünschten Sachen hinbekommen, nun haperts aber gerade etwas an einem Fading mit 3 Leds. Habe dabei noch keine gescheite Lösung gefunden und habe mich selbst ans Werk gemacht. Habe jedoch alles nur mit einfachen Arrays versucht, die Matrix bzw. mehrzeiligen Arrays komm ich noch nicht mit klar.

Es sollen 3 LED Gruppen angesteuert werden. Die mehr oder weniger abwechselnd leuchten (siehe ledPins_brightness).

//LED rot1-5, gelb 1, weiß 1-5 || fade
int ledPins[] = {3, 5, 6};
int ledPins_brightness[] = {255, 170, 85};
int fadeAmount[] = {5,5,5};
unsigned long prev_ms;
int interval
void setup() {
  for(int ledPin = 0; ledPin < pinCount; ledPin++) {
    pinMode(ledPins[ledPin], OUTPUT);
  }
}
    //Parameter Fading
    interval = 1000;

    
      if(millis() - prev_ms > interval) {
        prev_ms = millis();
        analogWrite(ledPins[0], ledPins_brightness[0]);
        analogWrite(ledPins[1], ledPins_brightness[1]);
        analogWrite(ledPins[2], ledPins_brightness[2]);
        
        ledPins_brightness[0] = ledPins_brightness[0] + fadeAmount[0];
        ledPins_brightness[1] = ledPins_brightness[1] + fadeAmount[1];
        ledPins_brightness[2] = ledPins_brightness[2] + fadeAmount[2];
        
        if ((ledPins_brightness[0] == 0 || ledPins_brightness[0] == 255) ||
        (ledPins_brightness[1] == 0 || ledPins_brightness[1] == 255) ||
        (ledPins_brightness[2] == 0 || ledPins_brightness[2] == 255)
        ) {
          if(ledPins_brightness[0] == 0 || ledPins_brightness[0] == 255) {
            prev_ms = millis();
            fadeAmount[0] = -fadeAmount[0];}
           if(ledPins_brightness[1] == 0 || ledPins_brightness[1] == 255) {
            prev_ms = millis();
            fadeAmount[1] = -fadeAmount[1];}           
          if(ledPins_brightness[2] == 0 || ledPins_brightness[2] == 255) {
            prev_ms = millis();
            fadeAmount[2] = -fadeAmount[2];}   
        }         
      }
void loop()
{
    interval = 1000;
 
    for(int led_pin = 0; led_pin < led_count; ) {
      if(millis() - prev_ms > interval){
        prev_ms = millis();
        digitalWrite(led_pins[led_pin], LOW);
        digitalWrite(led_pins[led_pin++], HIGH);
      }
    }
  }

Dein Fehler ist bei dem ++. Beim setzen auf LOW nimmst du den aktuellen Pin. Anschließend willst du ja den nächsten Pin auf HIGH setzen. Das nachgestellte ++ sagt dem Programm aber, dass es erst den Wert auslesen soll, und danach dann den Wert um 1 erhöhen soll. Du setzt also den einen Pin erst auf LOW, und dann direkt wieder auf HIGH und zählst dann erst hoch. Du musst da also einfach das ++ vorran stellen. So:

void loop()
{
    interval = 1000;
 
    for(int led_pin = 0; led_pin < led_count; ) {
      if(millis() - prev_ms > interval){
        prev_ms = millis();
        digitalWrite(led_pins[led_pin], LOW);
        digitalWrite(led_pins[++led_pin], HIGH);
      }
    }
  }

Dann wird erst hochgezählt und anschließend der Wert ausgelesen.

Hab den Code von Wordleader gerade getestet, der macht bei mir aber ebenfalls zicken. Das vorletzte Lämpchen bleibt gerne stehen. Werds morgen nocht mal mit Mega versuchen. Hab dort bereits die LED Ketten auf passende Lenge gebracht.

hab ja auch nur den einen fehler korrigiert, nicht gesagt das der code funktioniert ;-)

wenn du dir den code anschaust, dann zählst du ja immer weiter hoch. bedeutet mit dem code kommst du irgendwann an eine stelle, bei der du versuchst am Ende der schleife einen pin auf HIGH zu setzen, an dem gar keine led dran ist, der evtl. auch gar nicht als output deklariert ist.

du solltest da sowas probieren:

int interval = 1000;

void loop() {

   if(millis() - prev_ms > interval){ //nur einmal pro Intervall ausführen
      digitalWrite(led_pins[led_pin], LOW); //aktuelle LED ausschalten

      led_pin = (led_pin + 1) % led_count; //aktuelle LED auf die nächste setzen, evtl. zur ersten gehen wenn keine weiteren vorhanden sind

      digitalWrite(led_pins[led_pin], HIGH); //Neue aktuelle LED einschalten
   }
}

Damit hättest du auch eine Schleife drin die wieder von vorne beginnt.

Hallo Worldleader,

ich verstehe den Quellecode an einer stelle nicht so ganz.

led_pin = (led_pin + 1) % led_count;

Was genau besagt das % mit led_count? Ich kenne das % nur in Zusammenhang mit der Collatzfunktion zur Festellung, Zahl gerade oder ungerade.

Das ist eine Modulo-Division. Das gibt der den Rest einer ganzzahligen Divison zurück

0 mod 3 = 0
1 mod 3 = 1
2 mod 3 = 2
3 mod 3 = 0

Wie du siehst kann man damit das Ergebnis der Addition wieder auf 0 zurücksetzen wenn sie den Wert des Divisors erreicht.

Ahh, das hört sich doch dann schon sinnig an.

Also würde es bedeuten bei, static int led_pin = 0; led_count = 5;

1: led_pin = (led_pin + 1) % led_count; //led_pin=1 2: led_pin = (led_pin + 1) % led_count; //led_pin=2 3: led_pin = (led_pin + 1) % led_count; //led_pin=3 4: led_pin = (led_pin + 1) % led_count; //led_pin=4

5: led_pin = (led_pin + 1) % led_count; //led_pin=0 oder 5: led_pin = (led_pin + 1) % led_count; //led_pin=5 6: led_pin = (led_pin + 1) % led_count; //led_pin=0

?

Das erste, da 5 mod 5 = 0. Oder anders ausgedrückt 5 / 5 = 1, Rest 0

Siehst du auch an den Zahlen. Es sind 5 LEDs und die Indices gehen von 0 bis 4.