bessere Zufallszahlen erzeugen

Schon modulo 6 von micros() ist eine gute Zufallszahl für einen Würfel, weil der Moment des Tastendrucks unmöglich genau auf die µS sein kann.

Wenn alle Microsekunden gleich lang sind, ist es genausogut wie random().
Allerdings:

On 16 MHz Arduino boards (e.g. Duemilanove and Nano), this function has a resolution of four microseconds (i.e. the value returned is always a multiple of four).

D.h. du würfelst nur gerade Zahlen :wink:

Ich würde daher mindestens ( micros() >> 2 ) modulo 6 nehmen, oder gleich millis(), oder doch random() , nach einmaligem

randomSeed(x);

x kann analogRead(0) sein oder die millis() des ersten Tastendrucks nach Reset, ganz egal ...

random() liefert dir gleichverteilte Werte, besser als jeder echte Würfel.
Es sollten halt mehr als vier mögliche Sequenzen ( = mögliche Seed-Werte ) sein, sonnst hast du irgendwann einen Vorteil gegenüber deinen seltener mitspielenden Freunden :wink:

Doc_Arduino:
okay, hab das getestet. Leider hat das nicht viel mit Zufall zu tun. Meistens kommt immer nur 0, 2 oder 4 raus. Muß wohl doch random verwenden?

Die micros() Funktion zählt meines Erachtens immer in Vierer-Schritten weiter. Das wird nichts.

Du müßtest stattdessen die millis() Funktion nehmen.

Hallo,

Leute, probiert das mal aus. Laut meinem Gefühl arbeitet die random Funktion am zufälligsten. Braucht nur einen Taster und das Terminal.
Zuerst kommt die Random Ziffer, dann Modulo 12 / 2 und am Ende Modulo 60 / 10 immer auf Tastendruck auf Low Pegel.

Zusatzfrage. Kann es sein, dass sich der µC aufhängt wenn zu viel in der Interruptroutine steht?
Ich hatte erst noch paar Zeilen mehr drin zum vergleichen, nach gefühlten 20x tasten ging nichts mehr. Mußte µC reseten.

int TASTER = 2;
unsigned long ZEITPUNKT_MILLI;
unsigned long ZEITPUNKT_MICRO;
int Wuerfel_MICRO;
int Wuerfel_MILLI;
int Wuerfel;


void setup()
{
  Serial.begin(9600);
   
  pinMode(TASTER, INPUT); 
  digitalWrite(TASTER, HIGH);       // internen PullUp Widerstand aktiviert
  
  attachInterrupt(0, JUMP, FALLING);   // INT.0 = Pin.2 (TASTER)
}

void loop()
{
  
}

void JUMP()
{
  ZEITPUNKT_MILLI = millis();

  Wuerfel_MILLI = (ZEITPUNKT_MILLI % 12) / 2;
  Wuerfel = random(1,7);
  
  Serial.print("Random "); Serial.print(Wuerfel); Serial.print("   ");

  Serial.print("MILLI  "); Serial.print(Wuerfel_MILLI); Serial.print("  ");                            // zuerst mit Modulo 12 durch 2
  Wuerfel_MILLI = (ZEITPUNKT_MILLI % 60) / 10; Serial.print("  "); Serial.println(Wuerfel_MILLI);    // dann Modulo 60 durch 10
  
  Serial.println();
}

Doc_Arduino:
Zusatzfrage. Kann es sein, dass sich der µC aufhängt wenn zu viel in der Interruptroutine steht?

Die Ausgabe auf der seriellen Schnittstelle ist nicht sicher für die Verwendung innerhalb von Interrupts.

Solange Du Zeichen auf der seriellen Schnittstelle aus der ISR heraus nur so langsam ausgibst, dass sie noch gesendet werden können, bevor der nächste Interrupt kommt, klappt es trotzdem. Aber irgendwann bricht es zusammen.

Du darfst eben keine Library-Funktionen innerhalb von Interruptroutinen verwenden, die nicht sicher sind für die Verwendung in Interruptroutinen, sonst hängst Du Dein Programm auf.

Hallo,

dann habe ich zu schnell hintereinander den Taster gedrückt und dann zufällig genau in dem Moment wo noch Daten über die serielle gingen?

Laut meinem Gefühl arbeitet die random Funktion am zufälligsten

:wink:

Es soll Leute geben, die spucken erstmal auf den Würfel. ( und flüstern dabei "Hexenfett" )
Wenn du denen erzählst, es kommt gar nicht drauf an, wer den Taster, wie heftig, oder wenigstens wann, betätigt, sondern dass ( ohne randomSeed ) eine "sehr zufällig" aussehende, aber immer die gleiche Würfelfolge, die sich erst nach 65535 Würfen genau wiederholt, rauskommt, weil du den Taster super entprellt hast, spielen die garantiert nicht mehr mit.

Nimm das Prellen als randomSeed-Ersatz. D. H. jedes tastendrücken erzeugt eine undefinierte Anzahl von Interrupts ( = random() -Aufrufen ). In loop() zeigst du nur den letzten Wert an, wenn der Taster seit einiger Zeit ( 10..2000 ms ) wieder losgelassen wurde ( solange "rollt" der Würfel )

In ISRs nie Serial.write aufrufen. Solange noch Platz im 64 Zeichen großen Puffer ist, geht es zwar, aber das sollte man nicht herausfordern.

Hallo,

ich habe in der Zwischenzeit den Link entdeckt und gelesen und dann die letzte Antwort hier. :slight_smile: Deckt sich von der Idee her. Müßte funktionieren Zufallszahlen für Würfel - Mikrocontroller.net

Wenn ich in der ISR ständig random() aufrufe solange der Taster gedrückt ist, dann ist das doch die letzte Zahl womit man aus der ISR rauspringt echter Zufall. WieDu sagst. Dann bräuchte man kein randomSeed. Weil wenn ich mir die bestehende Zahlenabfolge von besagten 65535 vorstelle, dann ist es doch Zufall wo ich stehen bleibe. Egal ob die sich selbst wiederholt oder nicht.

Ich habe das mal nachgestellt mit LED Anzeige. Scheint zu funktionieren. Bleibt zufällig an irgendeiner LED stehen.
Nur ist die Gleichverteilung auch noch gegeben? Das Thema wird im Link angesprochen.
Wenn alles i.O., stellt sich die Frage, braucht man dafür überhaupt noch einen ISR? Könnte man auch in der Loop Schleife machen?

Falls man beim ISR bleiben muß oder soll, kann man dann den Tasterzustand noch in der Loop verwenden/auswerten oder bleibt der nur dem ISR vorbehalten? Ich brauche den noch für mein Lauflicht (Kometenschweif) und dessen Richtungswechsel. Wenn ich dafür noch ein extra Eingang verschwenden müßte wird es arg knapp. Kann schlecht einen Mega in ein kleines Würfelgehäuse bauen.

Einwände?
Denkfehler?

Ist nur der relevante Code.

void setup(){ 

  attachInterrupt(interruptNumber, interruptroutine, LOW); 
} 
 
void loop(){ 

} 
 
void interruptroutine(){ 

  digitalWrite(statusLED, LOW); 
  statusLED = random(3,9); 
  digitalWrite(statusLED, HIGH); 

}

Ist nur der relevante Code.

Relevant ist auch

[b] volatile byte statusLED=3; // initialisierte globale Variable[/b]

und

[b]   for (byte i = 3, i < 9, i++) pinMode(i, OUTPUT); // in setup() [/b]

Die übrigen CompilerFehler kriegt jeder sowieso mit und selbst gefixt, das ist irrelevant, stimmt.
Ich geh mal davon aus, dass dein Taster beim Drücken einen Pullup-Widerstand auf GND zieht.

Wie oft wird die ISR eigentlich gestartet bei
  attachInterrupt(0, interruptroutine, [b]LOW[/b]);
? So schnell wie möglich, d.h. ist loop in der Zeit komplett blockiert ?
? oder ist LOW das gleiche wie FALLING, d.h. ein paar mal bei prellendem Taster ?

Leuchten bei gedrücktem Taster permanent alle 6 LED's ? -- ein schönes Testprogramm übrigens !

Natürlich kannst du den Taster zusätzlich auch in loop() abfragen. ( Falls deine ISR nicht verhindert, dass loop überhaupt läuft )

Kannst auf die Interrupt-Routine verzichten, wenn loop() so schnell ist, dass du das meiste Prellen mitkriegst
--> auf keinen Fall delay(), Dauer < 1ms. Bis du deinen Kometenschweif berechnet hast, ist wohl zu lang, denke ich ...

Ich würde gar nicht erst mit Interrupts anfangen sondern einfach wie folgt vorgehen:

while (!digitalRead(TasterPin)) {
    // Zufallszahlengenerator "bewegen" solange Taste gedrückt
    random();
}
result = random ...

D.h. solange jemand drückt wird gewürfelt und dann ist gut.

Wenn jemand sein Würfeln so gut timen kann, daß das nicht zufällig ist, dann hat er Computerunterstützung --> dann kannst Du alle einfachen Methoden sowieso gleich vergessen.

Stimmt, ob die ISR oder daswhile den Rest von loop() blockiert solange der Taster gedrückt ist, macht kaum einen Unterschied.

Hallo,

michael_x:
Wie oft wird die ISR eigentlich gestartet bei
  attachInterrupt(0, interruptroutine, [b]LOW[/b]);
? So schnell wie möglich, d.h. ist loop in der Zeit komplett blockiert ?
? oder ist LOW das gleiche wie FALLING, d.h. ein paar mal bei prellendem Taster ?

Leuchten bei gedrücktem Taster permanent alle 6 LED's ? -- ein schönes Testprogramm übrigens !

Natürlich kannst du den Taster zusätzlich auch in loop() abfragen. ( Falls deine ISR nicht verhindert, dass loop überhaupt läuft )

Kannst auf die Interrupt-Routine verzichten, wenn loop() so schnell ist, dass du das meiste Prellen mitkriegst
--> auf keinen Fall delay(), Dauer < 1ms. Bis du deinen Kometenschweif berechnet hast, ist wohl zu lang, denke ich ...

Eure Fragen möchte ich natürlich auch beantworten. :wink:

Es wird immer zufällig eine LED angesteuert und vor der nächsten gelöscht. Das geht mit ISR Bedingung "LOW" so schnell, dass quasi alle irgendwie flimmern bis man den Taster los lässt.

Mit Interruptbedingung "LOW" wird der ISR solange aufgerufen wie ich den Taster drücke. Der Taster zieht das Signal auf Masse mit µC internen PullUp. Wenn ich statt "LOW" "FALLING" nutze, dann wird nur einmal beim drücken in den ISR gesprungen bzw. so oft wie der Taster prellt. Wenn man das prellen mal außen vor lässt, würde bei jeden Tastendruck genau eine neue Zahl erzeugt.

Eine schöne ISR Erklärung gibts übrigens hier. http://erik-bartmann.de//download/ArduinoInterruptsteuerungTeil1.pdf

Dann werde ich mich mal ranmachen und es in die Loop programmieren. Die festen delays in der Loop sollen eh noch raus und durch millis() ersetzt werden. Sodass die Loop mit maximalen Speed läuft ohne künstliche Blockierung.

Danke Euch allen für die Hinweise und Diskussion bis her her erstmal.

hi,

ich verstehe den interrupt nicht. bei tastendruck ein delay von 200, beim loslassen millis messen, delay 200 und den modulo-wert von millis verwenden.
was spricht dagegen?

gruß stefan

Hallo,

weil aus irgendwelchen Gründen, wie selbst überraschend festgestellt, damit keine Gleichverteilung der Zufallswerte eintritt. Lies Dir mal den Link durch. Dort wird das Thema angesprochen. Wenn ich dagegen an einer fast endlosen Zahlenreihen entlang laufe und dann irgendwo zufällig stehen bleibe, ist das laut meiner Meinung ein sehr guter Zufall. Zudem die Zahlenreihe selbst auch etwas durcheinander ist.

Würde ich dagegen nicht ständig random durchlaufen lassen, sondern immer eine feste Abfolge 1 bis 6, dann könnte ein Mitspieler vielleicht mitbekommen wie lange Loop benötigt und wahrscheinlich immer die gleich Zahl würfeln. Zumindestens mit hoher Wahrscheinlichkeit.

Edit:

Nachfrage, weil Udo mit der while () Schleife um die Ecke kommt. Ich habe z.Z. mehrere if Schleifen in Verwendung. while würde in dem Fall das gleiche Ergebnis liefern. Wann nimmt man nun lieber while und wann lieber if ?

Auszug:

void loop()
{
  
  TasterStatus = digitalRead(TASTER);      // Taster abfragen, wenn nicht gedrückt liegt HIGH Signal an

  if (TASTER_Pressed == true && TasterStatus == HIGH)  {
     Schweif_nach_links_drehen ();         // LEDs werden in der Reihenfolge umgedreht
     TASTER_Pressed = false;               // Hilfsvariable wird geändert  
    }

  if (TASTER_Pressed == false && TasterStatus == LOW)  {    
     Schweif_nach_rechts_drehen ();        // LEDs werden in der Reihenfolge umgedreht
     TASTER_Pressed = true;                // Hilfsvariable wird geändert
    }  

  if (TasterStatus == LOW)  {              // wenn Taster gedrückt wird liegt LOW Signal an
     Kometenschweif_Rechts ();             // Schweif dreht rechts herum und beschleunigt
    }  
      
  if (TasterStatus == HIGH)  {             // wenn Taster nicht gedrückt wird liegt HIGH Signal an 
     Kometenschweif_Links ();              // Schweif dreht links herum und bremst ab
    }

  if (TASTER_Pressed == true)  {
     Wuerfelzahl_loeschen ();              // Wuerfelaugen Anzeige löschen
     
}

Hallo,

komme mit while nicht zu recht. While wird doch solange durchlaufen wie die Bedingung wahr ist. Also Taster gedrückt, LOW Signal liegt an.

µC reseten, Sketch geht los. Taster drücken, Zufallszahlen werden ohne Ende erzeugt, ich lasse Taster los, aber es ändert sich nichts. Zahlen werden weiterhin erzeugt. Er springt nicht aus der while Schleife raus. Dabei wird doch am Loop Anfang permanent der Taster abgefragt.
Ersetze ich while durch if funktioniert es? Sollte doch keinen Unterschied machen?

der komplette Sketch. Das Pause Array hat nur zu Testzwecken solche seltsamen Werte. Also nicht wundern.

// Arduino Mega 2560

// Würfelaugen LED Zusammenfassung
int GruppeA = 22;  // Port.A.22  LED Nr. 4 (mittelste)
int GruppeB = 23;  // Port.A.23  LED Nr. 1 + 7
int GruppeC = 24;  // Port.A.24  LED Nr. 3 + 5
int GruppeD = 25;  // Port.A.25  LED Nr. 2 + 6
int WuerfelAugen = 0;        // Wert zwischen 1 und 6

int TASTER = 2;              // Start-Taster zum würfeln, im Setup mit internen PullUp Widerstand aktiv
int TasterStatus;            // Zustand vom Taster

// Geschwindigkeitssteigerung des Kometenschweifs bzw. abbremsen
int PAUSE [44] = {173,73,73,74,74,74,75,75,75,75,76,76,76,76,77,77,77,77,77,78,78,78,78,78,79,79,79,79,79,79,80,80,80,80,80,80,80,81,81,81,81,81,81,181};
int SPEED = 43;       

// Anfangsposition vom Kometenschweif
int LEDPOS_1 = 3;                      // auf sichere LED Position setzen für ersten Durchlauf
int LEDPOS_2 = 4;                      // auf sichere LED Position setzen für ersten Durchlauf
int LEDPOS_3 = 5;                      // auf sichere LED Position setzen für ersten Durchlauf
int LAST_LED = 6;                      // auf sichere LED Position setzen für ersten Durchlauf

int LED_1, LED_2, LED_3;               // zum zwischen speichern der LED Position zum umdrehen

unsigned long ZEIT_NEW;
unsigned long ZEIT_OLD = 0;

int LED_H1 = 255;        // 255, Helligkeiten der LEDs     
int LED_H2 = 96;         //  96
int LED_H3 = 16;	 //  16											

boolean TASTER_Pressed = false;        // Taster Anfangsstatus definieren ( Wurde Taster schon gedrückt oder nicht? )

void setup()
{     
   Serial.begin(9600);

pinMode(TASTER, INPUT);           // Start-Taster zum würfeln und
digitalWrite(TASTER, HIGH);       // internen PullUp Widerstand aktiviert

// Pins 3 bis 6 für den Kometenschweif setzen
  for (int thisPin = 3; thisPin <= 6; thisPin++)  { 
    pinMode(thisPin, OUTPUT); 
   }

// Pins für die Würfelaugen setzen und kurz einschalten 
  DDRA = 15;       // Port.A, Bit 0 bis 3 Ausgang
  PORTA = 15;      // Port.A, Bit 0 bis 3 eingeschalten
  delay(500);
  PORTA = 0;       // Port.A alle Ausgänge Aus
  
//  Serial.println("\n[memCheck]"); 
//  Serial.println(freeRAM(), DEC);  
   
}

void loop()  {
  
  TasterStatus = digitalRead(TASTER);      // Taster abfragen, wenn nicht gedrückt liegt HIGH Signal an

  if (TASTER_Pressed == true && TasterStatus == HIGH)  {
     Schweif_nach_links_drehen ();         // LEDs werden in der Reihenfolge umgedreht
     TASTER_Pressed = false;               // Hilfsvariable wird geändert  
    }

  if (TASTER_Pressed == false && TasterStatus == LOW)  {    
     Schweif_nach_rechts_drehen ();        // LEDs werden in der Reihenfolge umgedreht
     TASTER_Pressed = true;                // Hilfsvariable wird geändert
    }  

  if (TasterStatus == LOW)  {              // wenn Taster gedrückt wird liegt LOW Signal an
     Kometenschweif_Rechts ();             // Schweif dreht rechts herum und beschleunigt
    }  
      
  if (TasterStatus == HIGH)  {             // wenn Taster nicht gedrückt wird liegt HIGH Signal an 
     Kometenschweif_Links ();              // Schweif dreht links herum und bremst ab
    }

  if (TASTER_Pressed == true)  {
     Wuerfel_loeschen ();                  // Wuerfelaugen Anzeige löschen
    }  

  if (TASTER_Pressed == false)  {
     Wuerfelzahl_anzeigen (WuerfelAugen);          // Zahl der Würfelaugen mittels LEDs anzeigen
    }
   
  while (TasterStatus == LOW)  {           // Zufallszahl erzeugen solange Taster gedrückt ist
     WuerfelAugen = random(1,7);  
     Serial.println(WuerfelAugen);         // zu Testzwecken
    } 


    
}  // Loop Ende


/* ***  Funktionen  *** */

void Kometenschweif_Rechts ()
{
     if (LEDPOS_1 > 6) { LEDPOS_1 = 3; }             // wenn "letzte" LED erreicht, auf Anfangsposition setzen
     
     analogWrite(LEDPOS_1, LED_H1);                  // 5, Schweifspitze ganz hell                                    
     analogWrite(LEDPOS_2, LED_H2);                  // 4, LED dahinter etwas dunkler   
     analogWrite(LEDPOS_3, LED_H3);                  // 3, nächste LED noch dunkler                  
     analogWrite(LAST_LED, LOW);                     // letzte alte LED ausschalten
             
     LAST_LED = LEDPOS_3;                            // 3

     LEDPOS_3 = LEDPOS_2;                            // 4
     LEDPOS_2 = LEDPOS_1;                            // 5
     LEDPOS_1++;                                     // 6

//   delay(1000);  
     delay(PAUSE[SPEED]);                            // Kometenschweif beschleunigen
     SPEED--;
    if (SPEED < 1) { SPEED = 0; }                    // schnellsten Wert beibehalten
                            
}


void Kometenschweif_Links ()
{
     if (LEDPOS_1 < 3) { LEDPOS_1 = 6; }             // wenn "letzte" LED erreicht, auf Anfangsposition setzen

     analogWrite(LEDPOS_1, LED_H1);                  // Schweifspitze ganz hell                 
     analogWrite(LEDPOS_2, LED_H2);                  // LED dahinter etwas dunkler        
     analogWrite(LEDPOS_3, LED_H3);                  // nächste LED noch dunkler                      
     analogWrite(LAST_LED, LOW);                     // letzte alte LED ausschalten
             
     LAST_LED = LEDPOS_3;

     LEDPOS_3 = LEDPOS_2;
     LEDPOS_2 = LEDPOS_1;
     LEDPOS_1--;
          
//   delay(1000);  
    delay(PAUSE[SPEED]);                            // Kometenschweif abbremsen
    SPEED++;
    if (SPEED > 43) { SPEED = 43; }                 // langsamsten Wert beibehalten
}
 

void Schweif_nach_links_drehen ()
{
     LED_1 = LAST_LED;             //5,  LED Positionen zwischen speichern
     LED_2 = LEDPOS_3;             //6
     LED_3 = LEDPOS_2;             //7

     LEDPOS_1 = LED_1;             //5,  LED Positionen umdrehen
     LEDPOS_2 = LED_2;             //6
     LEDPOS_3 = LED_3;             //7

     if (LEDPOS_3 >= 6) { LAST_LED = 3;}
       else { LAST_LED = LEDPOS_3 + 1; }  
}


void Schweif_nach_rechts_drehen ()
{
     LED_1 = LAST_LED;              //  LED Positionen zwischen speichern
     LED_2 = LEDPOS_3;             
     LED_3 = LEDPOS_2;        

     LEDPOS_1 = LED_1;              //  LED Positionen umdrehen
     LEDPOS_2 = LED_2;            
     LEDPOS_3 = LED_3;           

     if (LEDPOS_3 <= 3) { LAST_LED = 6;}
       else { LAST_LED = LEDPOS_3 - 1; }              
}


// Zahl der Würfelaugen mittels LEDs anzeigen
void Wuerfelzahl_anzeigen (int Augen)
{  
  if (Augen%2 != 0) {digitalWrite(GruppeA, HIGH);}     // ist der Augenwert ungerade ?  (Modulo, ist Restwert ungleich 0)
  if (Augen > 1)    {digitalWrite(GruppeB, HIGH);}     // ist der Augenwert größer 1 ?
  if (Augen > 3)    {digitalWrite(GruppeC, HIGH);}     // ist der Augenwert größer 3 ?
  if (Augen == 6)   {digitalWrite(GruppeD, HIGH);}     // ist der Augenwert gleich 6 ?
}


// Würfelaugen löschen
void Wuerfel_loeschen ()
{
   digitalWrite(GruppeA, LOW);             // Wuerfelaugen löschen
   digitalWrite(GruppeB, LOW);
   digitalWrite(GruppeC, LOW);
   digitalWrite(GruppeD, LOW);
}

     
int freeRAM()           //  Funktion im WWW gefunden, zeigt freien Speicher an
  { 
  extern int __heap_start, *__brkval;  
  int v;  
  return (int) &v - (__brkval == 0 ? (int) &__heap_start : (int) __brkval); 
  }

Doc_Arduino:
komme mit while nicht zu recht. While wird doch solange durchlaufen wie die Bedingung wahr ist. Also Taster gedrückt, LOW Signal liegt an.

Das ist eine typische Endlosschleife:
while (TasterStatus == LOW) { // Zufallszahl erzeugen solange Taster gedrückt ist
WuerfelAugen = random(1,7);
Serial.println(WuerfelAugen); // zu Testzwecken
}

Wenn die Bedingung (TasterStatus == LOW) beim Eintritt in die Schleife erfüllt ist, dann ist diese Bedingung danach immer erfüllt, weil sie sich innerhalb der Schleife nicht ändert, also insbesondere TasterStatus innerhalb der Schleife nie einen anderen Wert zugewiesen bekommt.

Hallo,

in einer while Schleife muß also die Bedingung innerhalb geändert werden durch irgendwas, sonst wird zur Endlosschleife?

Danke. Wieder was gelernt. :slight_smile:

Ich lasse Euch teilhaben, habe jetzt die Loop Endlosschleife :wink: delay frei bekommen. Bin happy. Keine Blokaden mehr drin.

// Arduino Mega 2560

// Würfelaugen LED Zusammenfassung (Methode von Erik Bartmann, o´reilly Verlag, Arduino entdecken
int GruppeA = 22;  // Port.A.22  LED Nr. 4 (mittelste)
int GruppeB = 23;  // Port.A.23  LED Nr. 1 + 7
int GruppeC = 24;  // Port.A.24  LED Nr. 3 + 5
int GruppeD = 25;  // Port.A.25  LED Nr. 2 + 6
int WuerfelAugen = 0;        // Wert zwischen 1 und 6

int TASTER = 2;              // Start-Taster zum würfeln, im Setup mit internen PullUp Widerstand aktiv
int TasterStatus;            // Zustand vom Taster

// Geschwindigkeitssteigerung des Kometenschweifs bzw. abbremsen
int PAUSE [44] = {173,73,73,74,74,74,75,75,75,75,76,76,76,76,77,77,77,77,77,78,78,78,78,78,79,79,79,79,79,79,80,80,80,80,80,80,80,81,81,81,81,81,81,181};
int SPEED = 43;       

// Anfangsposition vom Kometenschweif
int LEDPOS_1 = 3;                      // auf sichere LED Position setzen für ersten Durchlauf
int LEDPOS_2 = 4;                      // auf sichere LED Position setzen für ersten Durchlauf
int LEDPOS_3 = 5;                      // auf sichere LED Position setzen für ersten Durchlauf
int LAST_LED = 6;                      // auf sichere LED Position setzen für ersten Durchlauf

int LED_1, LED_2, LED_3;               // zum zwischen speichern der LED Position zum umdrehen

unsigned long ZEIT_NEW = 0;            // Ersatzvariable für millis() um delay() zu ersetzen
unsigned long ZEIT_OLD = 0;            // Ersatzvariable für millis() um delay() zu ersetzen

int LED_H1 = 255;        // 255, Helligkeiten der LEDs     
int LED_H2 = 96;         //  96
int LED_H3 = 16;	 //  16											

boolean TASTER_Pressed = false;        // Taster Anfangsstatus definieren ( Wurde Taster schon gedrückt oder nicht? )

void setup()
{     
   Serial.begin(9600);

pinMode(TASTER, INPUT);           // Start-Taster zum würfeln und
digitalWrite(TASTER, HIGH);       // internen PullUp Widerstand aktiviert

// Pins 3 bis 6 für den Kometenschweif setzen
  for (int thisPin = 3; thisPin <= 6; thisPin++)  { 
    pinMode(thisPin, OUTPUT); 
   }

// Pins für die Würfelaugen setzen und kurz einschalten 
  DDRA = 15;       // Port.A, Bit 0 bis 3 Ausgang
  PORTA = 15;      // Port.A, Bit 0 bis 3 eingeschalten
  delay(500);
  PORTA = 0;       // Port.A alle Ausgänge Aus
  
//  Serial.println("\n[memCheck]"); 
//  Serial.println(freeRAM(), DEC);  
   
}

void loop()  {
  
  ZEIT_NEW = millis();                         // Millisekunden seit µC Start/Reset abfragen und merken
  
  TasterStatus = digitalRead(TASTER);      // Taster abfragen, wenn nicht gedrückt liegt HIGH Signal an

  if (TASTER_Pressed == true && TasterStatus == HIGH)  {
     Schweif_nach_links_drehen ();         // LEDs werden in der Reihenfolge umgedreht
     TASTER_Pressed = false;               // Hilfsvariable wird geändert  
    }

  if (TASTER_Pressed == false && TasterStatus == LOW)  {    
     Schweif_nach_rechts_drehen ();        // LEDs werden in der Reihenfolge umgedreht
     TASTER_Pressed = true;                // Hilfsvariable wird geändert
    }  

  if (TasterStatus == LOW)  {              // wenn Taster gedrückt wird liegt LOW Signal an
     Kometenschweif_Rechts ();             // Schweif dreht rechts herum und beschleunigt
     WuerfelAugen = random(1,7);  
     Serial.println(WuerfelAugen);         // zu Testzwecken
    }  
      
  if (TasterStatus == HIGH)  {             // wenn Taster nicht gedrückt wird liegt HIGH Signal an 
     Kometenschweif_Links ();              // Schweif dreht links herum und bremst ab
    }

  if (TASTER_Pressed == true)  {
     Wuerfel_loeschen ();                  // Wuerfelaugen Anzeige löschen
    }  

  if (TASTER_Pressed == false)  {
     Wuerfelzahl_anzeigen (WuerfelAugen);          // Zahl der Würfelaugen mittels LEDs anzeigen
    }


    
}  // Loop Ende


/* ***  Funktionen  *** */

void Kometenschweif_Rechts ()  {
 
  if (ZEIT_NEW - ZEIT_OLD > PAUSE[SPEED])  {         // vorgegebene Zeit aus Array schon abgelaufen?
  
     if (LEDPOS_1 > 6) { LEDPOS_1 = 3; }             // wenn "letzte" LED erreicht, auf Anfangsposition setzen
     
     analogWrite(LEDPOS_1, LED_H1);                  // 5, Schweifspitze ganz hell                                    
     analogWrite(LEDPOS_2, LED_H2);                  // 4, LED dahinter etwas dunkler   
     analogWrite(LEDPOS_3, LED_H3);                  // 3, nächste LED noch dunkler                  
     analogWrite(LAST_LED, LOW);                     // letzte alte LED ausschalten
             
     LAST_LED = LEDPOS_3;                            // 3

     LEDPOS_3 = LEDPOS_2;                            // 4
     LEDPOS_2 = LEDPOS_1;                            // 5
     LEDPOS_1++;                                     // 6

     SPEED--;                                        // Kometenschweif beschleunigen
    if (SPEED < 1) { SPEED = 0; }                    // schnellsten Wert beibehalten
     
     ZEIT_OLD = ZEIT_NEW;                            // alte Zeit aktualisieren, Ersatz für delay()
   } 
                            
}


void Kometenschweif_Links ()  {

  if (ZEIT_NEW - ZEIT_OLD > PAUSE[SPEED])  {         // vorgegebene Zeit aus Array schon abgelaufen?
     
     if (LEDPOS_1 < 3) { LEDPOS_1 = 6; }             // wenn "letzte" LED erreicht, auf Anfangsposition setzen

     analogWrite(LEDPOS_1, LED_H1);                  // Schweifspitze ganz hell                 
     analogWrite(LEDPOS_2, LED_H2);                  // LED dahinter etwas dunkler        
     analogWrite(LEDPOS_3, LED_H3);                  // nächste LED noch dunkler                      
     analogWrite(LAST_LED, LOW);                     // letzte alte LED ausschalten
             
     LAST_LED = LEDPOS_3;

     LEDPOS_3 = LEDPOS_2;
     LEDPOS_2 = LEDPOS_1;
     LEDPOS_1--;
          
     SPEED++;                                        // Kometenschweif abbremsen
     if (SPEED > 43) { SPEED = 43; }                 // langsamsten Wert beibehalten
     
     ZEIT_OLD = ZEIT_NEW;                            // alte Zeit aktualisieren, Ersatz für delay()
   }    
}
 

void Schweif_nach_links_drehen ()
{
     LED_1 = LAST_LED;             //5,  LED Positionen zwischen speichern
     LED_2 = LEDPOS_3;             //6
     LED_3 = LEDPOS_2;             //7

     LEDPOS_1 = LED_1;             //5,  LED Positionen umdrehen
     LEDPOS_2 = LED_2;             //6
     LEDPOS_3 = LED_3;             //7

     if (LEDPOS_3 >= 6) { LAST_LED = 3;}
       else { LAST_LED = LEDPOS_3 + 1; }  
}


void Schweif_nach_rechts_drehen ()
{
     LED_1 = LAST_LED;              //  LED Positionen zwischen speichern
     LED_2 = LEDPOS_3;             
     LED_3 = LEDPOS_2;        

     LEDPOS_1 = LED_1;              //  LED Positionen umdrehen
     LEDPOS_2 = LED_2;            
     LEDPOS_3 = LED_3;           

     if (LEDPOS_3 <= 3) { LAST_LED = 6;}
       else { LAST_LED = LEDPOS_3 - 1; }              
}


// Zahl der Würfelaugen mittels LEDs anzeigen
void Wuerfelzahl_anzeigen (int Augen)
{  
  if (Augen%2 != 0) {digitalWrite(GruppeA, HIGH);}     // ist der Augenwert ungerade ?  (Modulo, ist Restwert ungleich 0)
  if (Augen > 1)    {digitalWrite(GruppeB, HIGH);}     // ist der Augenwert größer 1 ?
  if (Augen > 3)    {digitalWrite(GruppeC, HIGH);}     // ist der Augenwert größer 3 ?
  if (Augen == 6)   {digitalWrite(GruppeD, HIGH);}     // ist der Augenwert gleich 6 ?
}


// Würfelaugen löschen
void Wuerfel_loeschen ()
{
   digitalWrite(GruppeA, LOW);             // Wuerfelaugen löschen
   digitalWrite(GruppeB, LOW);
   digitalWrite(GruppeC, LOW);
   digitalWrite(GruppeD, LOW);
}

     
int freeRAM()           //  Funktion im WWW gefunden, zeigt freien Speicher an
  { 
  extern int __heap_start, *__brkval;  
  int v;  
  return (int) &v - (__brkval == 0 ? (int) &__heap_start : (int) __brkval); 
  }