Code Aufräumen/Verbessern, Speicherplatz freigeben

Danke erstmal für eure Hinweise :slight_smile:
Ich habe mir ein paar Gedanken dazu gemacht.

Der Bereich, also ganz oben im Code, wo Einstellungen vorgenommen werden, wo ich jetzt auch demotime belasse, dient dazu Werte einzustellen. Wenn ich (oder jemand anders) nach ein paar Monaten dort etwas ändere, wo die Erinnerung an die damalige (jetzige) Zeit verblasst ist, kann ich den Wert von brightness, aus welchen Gründen auch immer auf über 50 setzen. Dies würde dann im Setup, was sich erst weiter unten im Code befindet, wieder korrigiert. Natürlich ohne irgendwelche Fehlermeldung. Daher denke ich, das es sinnvoll ist diese Zeile im Setup zu belassen.

würde ja auch nur etwas bringen, wenn brightness unveränderbar bliebe. Sobald ich aber in den globalen Einstellungen brightness höher setzte, läuft diese Abfrage ebenso in das sinnlose.
Der Ansatz von combie ist dann schon eher etwas in die Richtung, denke aber das dort auch wieder mehr Speicher verbraucht wird? Muss mich damit mal tiefer auseinander setzen.

Der Wert, der die LED-Programme ausbremsen soll, habe ich jetzt zur Struktur Control hinzugefügt:

const Control controlArray[] = {                // Eigenschaften der Programme (faden, demoaktiv)
//{FadeInOut, Demoaktiv, Bremse} 
  {false,     false,     55    },  //Cube
  {false,     false,     5     },  //Dice
  {false,     false,     5     },  //FadeOnSite
  {true,      true,      55    },  //Disco
  {true,      true,      35    },  //Rainbow
  {false,     true,      55    },  //Flash
  {false,     true,      300   },  //Snake
  {false,     true,      5     },  //Rain
  {false,     true,      5     }   //HeartBeat
};

Und mache den millis() vergleich nur einmalig in der control Funktion:

void control(){
  if (bitRead(intdata,2)){
    freefallprog();                                                                     // Wenn Freier Fall erkannt, LED Muster für Freien Fall starten
    intdata = read8(ADXL345_INT_SOURCE);                                                // ADXL Interupt Speicher auslesen und damit leeren, verhindert doppeltes ausführen von freefallprog()
    nextProgram();                                                                      // Erhöhe den Programzähler oder setzt auf Null Wenn Programmanzahl überschritten
  }
  if (inaktiv && !controlArray[program].demoaktiv){                                     // Wenn inaktiv und aktuelles Programm nicht für DemoModus
    nextProgram();                                                                      // Erhöhe den Programzähler oder setzt auf Null Wenn Programmanzahl überschritten
  }
  else if(millis()-millis_merker_prog>controlArray[program].bremse){
    millis_merker_prog = millis();
    switch (program) {                                                                  // Programm nach Programmzähler wählen
      case Cube:
        cube(readADXLtop());  // Sechs immer oben
        break;
      case Dice:
        dice();               // WürfelModus
        break;
      case FadeOnSite:
        fadeonsite();         // Farbwechsel abhängig der oben liegenden Seite
        break;
      case Disco:
        disco();              // Disco, zufällige Farbwiedergabe
        break;
      case Rainbow:
        rainbow();            // Farbwechsel nach Regenbogen
        break;
      case Flash:
        flash();              // Zufälliges Aufblitzen
        break;
      case Snake:
        snake();              // Schlange
        break;
      case Rain:
        rain(readADXLtop());  // Simuliert Regen, Farbe abhängig der oben liegenden Seite
        break;
      case HeartBeat:
        heartbeat();          // Simuliert Herzklopfen
        break;
      default:
        break;    
    }
    fadeInOut();                                                                    // Funktion zum gemütlichen Übergang, wenn im Programm hinterlegt
  } 
}

Das reduziert den Code auf immerhin:

Der Sketch verwendet 6860 Bytes (83%) des Programmspeicherplatzes. Das Maximum sind 8192 Bytes.
Globale Variablen verwenden 334 Bytes (65%) des dynamischen Speichers, 178 Bytes für lokale Variablen verbleiben. Das Maximum sind 512 Bytes.

Treibt den Arbeitsspeicher etwas hoch, aber noch ok. Hatte schon mehr verbrauch in früherern Versionen und das Programm lief stabil.

Etwas stört mich aber daran. Ich habe LED-Programme, die die "bremse" beeinflussen wöllten. Da die Struktur Control aber eine Konstante ist, geht dies nicht mehr. Also müsste ich sie variabel machen.

constexpr weist dem Kompiler an, alle Variblen vor dem Kompilieren durch diesen Wert schon zu ersetzen, ähnlich dem define, nur das nach eine Typ abfrage stattfindet, ob der Wert überhaupt passt. Soweit richtig verstanden?
Ist constexpr generell const vorzuziehen? Also einfach alle const durch constexpr ersetzen? Oder gibt es bestimmte Fälle wo const dem constexpr vorzuziehen ist?

Hier noch der aktuelle Stand des Codes:

//            +----------+                                   +---------+                      
//    Pixel   | 51 52 53 |                           Fläche  |    ^    |
//            | 48 49 50 |                                   |    5    |
//            | 45 46 47 |                                   |         |
// +----------+----------+----------+----------+   +---------+---------+---------+---------+
// | 33 34 35 | 36 39 42 | 06 07 08 | 09 12 15 |   |    ^    |         |    ^    |         |
// | 30 31 32 | 37 40 43 | 03 04 05 | 10 13 16 |   |    3    |    4  > |    0    |    1  > |
// | 27 28 29 | 38 41 44 | 00 01 02 | 11 14 17 |   |         |         |         |         |
// +----------+----------+----------+----------+   +---------+---------+---------+---------+
//                                  | 18 21 24 |                                 |         |
//                                  | 19 22 25 |                                 |    2  > |
//                                  | 20 23 26 |                                 |         |
//                                  +----------+                                 +---------+


#if (F_CPU>7370000) //neopixel library required 7.37MHz minimum clock speed; this line is used to skip this sketch in internal testing. It is not needed in your sketches.

#include <tinyNeoPixel_Static.h>                // ATTiny WS2812b Bibliothek
#include <TinyWireM.h>                          // ATTiny I2C Bibliothek
#include <avr/power.h>

// ATTiny WS2812b Initialisierung
const uint8_t datenPin = 4;                     // WS2812b DatenPin
const uint8_t ledAnzahl = 54;                   // WS2812b LED Anzahl
uint8_t pixels[ledAnzahl * 3];                  // Array für jede LED und dessen Farbe
tinyNeoPixel strip = tinyNeoPixel(ledAnzahl, datenPin, NEO_GRB, pixels);


uint8_t  brightness = 50;                       // Variable für die Helligkeit; 0 bis 50, Aus bis Volle Heligkeit
const uint32_t demotime = 100000;               // Zeitschwelle zum wechseln zum nächsten Programm im Demo-Modus (Millisekunden)
uint8_t  intdata;                               // Variable für Interupts vom ADXL345b
uint16_t mem = 0;                               // Zwischenspeicher
uint8_t  program = 0;                           // Welches Programm ist aktiv
bool     inaktiv = false;                       // Inaktivität erkannt
uint8_t  red = 255, green = 255, blue = 255;    // Farbvariablen für den Strip
uint32_t millis_merker_demo;                    // Zwischenspeicher für Millis zum Programmwechsel im Demomodus
uint32_t millis_merker_prog;                    // Zwischenspeicher für Millis um die Programme etwas zu bremsen

const uint64_t Sequenz[] PROGMEM {              // Bitmuster der Zahlen die gezeigt werden entsprechend wie der Würfel sich dreht, verwendung im Programm cube() //bitRead von rechts nach links^^
  0b101010101101000101000010000100000001100010001111000111, // µC-PCP zeigt 6 //top=0
  0b101000101000010000100000001100010001111000111101010101, // µC-PCP zeigt 5 //top=1
  0b000010000100000001100010001111000111101010101101000101, // µC-PCP zeigt 4 //top=2
  0b100000001100010001111000111101010101101000101000010000, // µC-PCP zeigt 1 //top=3
  0b100010001111000111101010101101000101000010000100000001, // µC-PCP zeigt 2 //top=4
  0b111000111101010101101000101000010000100000001100010001, // µC-PCP zeigt 3 //top=5
};

enum : uint8_t{                                 // Oben, Rechts, Links, Unten durchnummeriert
  Oben,           // = 0b00 = 0
  Rechts,         // = 0b01 = 1
  Links,          // = 0b10 = 2
  Unten           // = 0b11 = 3
};

const uint8_t partner[6][4] PROGMEM {           // Erstellt ein FlächenArray (0-5) wo von jede anliegende Fläche (oben, rechts, links, unten) (0-3) die Fläche und welche Kante (oben, rechts, links, unten) anliegt Bitweise (0bKKFFF) hinterlegt ist
   // Oben liegt      // Rechts liegt    // Links liegt     // Unten liegt
  {5 + (Rechts << 3), 1 + (Unten << 3),  4 + (Oben << 3),   2 + (Unten << 3)},   // Fläche 0   // Bsp: An Fläche 0 liegt oben Fläche 5 an mit seiner rechten Kante
  {3 + (Links << 3),  2 + (Links << 3),  5 + (Oben << 3),   0 + (Rechts << 3)},  // Fläche 1   // Bsp: An Fläche 1 liegt rechts Fläche 2 an mit seiner linken Kante
  {3 + (Unten << 3),  4 + (Rechts << 3), 1 + (Rechts << 3), 0 + (Unten << 3)},   // Fläche 2
  {5 + (Links << 3),  4 + (Unten << 3),  1 + (Oben << 3),   2 + (Oben << 3)},    // Fläche 3
  {0 + (Links << 3),  2 + (Rechts << 3), 5 + (Unten << 3),  3 + (Rechts << 3)},  // Fläche 4
  {1 + (Links << 3),  0 + (Oben << 3),   3 + (Oben << 3),   4 + (Links << 3)}    // Fläche 5
};

enum : uint8_t{                                 // Programme in der Reihenfolge Nummeriert, gleichzeitig die Reihenfolge der Programme
  Cube,           // Programm 0
  Dice,           // Programm 1
  FadeOnSite,     // Programm 2
  Disco,          // Programm 3
  Rainbow,        // Programm 4
  Flash,          // Programm 5
  Snake,          // Programm 6
  Rain,           // Programm 7
  HeartBeat       // Programm 8
};

struct Control{                                 // Struktur zum kontrolliern der Programme
  bool fadeinout;                               // Soll im Demomodus ein Faden beim Programmwechsel stattfinden, weicher Programwechsel
  bool demoaktiv;                               // Soll das Programm im Demo-Modus wiedergegeben werden
  uint16_t bremse;                              // Bremst das LED-Program aus, gibt an aller wieviel Millisekunden es aufgerufen werden soll
};

const Control controlArray[] = {                // Eigenschaften der Programme (faden, demoaktiv)
//{FadeInOut, Demoaktiv, Bremse} 
  {false,     false,     100   },  //Cube
  {false,     false,     15    },  //Dice
  {false,     false,     15    },  //FadeOnSite
  {true,      true,      70    },  //Disco
  {true,      true,      40    },  //Rainbow
  {false,     true,      10    },  //Flash
  {false,     true,      300   },  //Snake
  {false,     true,      15    },  //Rain
  {false,     true,      10    }   //HeartBeat
};


// I²C Register am ADXL
/* registers */
#define ADXL345_ADDRESS          0x53
#define ADXL345_DEVID            0x00
#define ADXL345_THRESH_TAP       0x1D 
#define ADXL345_OFSX             0x1E
#define ADXL345_OFSY             0x1F
#define ADXL345_OFSZ             0x20
#define ADXL345_DUR              0x21
#define ADXL345_LATENT           0x22 
#define ADXL345_WINDOW           0x23 
#define ADXL345_THRESH_ACT       0x24
#define ADXL345_THRESH_INACT     0x25 
#define ADXL345_TIME_INACT       0x26 
#define ADXL345_ACT_INACT_CTL    0x27
#define ADXL345_THRESH_FF        0x28
#define ADXL345_TIME_FF          0x29 
#define ADXL345_TAP_AXES         0x2A
#define ADXL345_ACT_TAP_STATUS   0x2B
#define ADXL345_BW_RATE          0x2C
#define ADXL345_POWER_CTL        0x2D
#define ADXL345_INT_ENABLE       0x2E
#define ADXL345_INT_MAP          0x2F
#define ADXL345_INT_SOURCE       0x30
#define ADXL345_DATA_FORMAT      0x31
#define ADXL345_DATAX0           0x32
#define ADXL345_DATAX1           0x33
#define ADXL345_DATAY0           0x34
#define ADXL345_DATAY1           0x35
#define ADXL345_DATAZ0           0x36
#define ADXL345_DATAZ1           0x37
#define ADXL345_FIFO_CTL         0x38
#define ADXL345_FIFO_STATUS      0x39
/* Register bits */
#define ADXL345_FULL_RES         0x03
#define ADXL345_SUPPRESS         0x03
#define ADXL345_LOW_POWER        0x04


void setup(){  
  pinMode(datenPin,OUTPUT);
  pinMode(A3,INPUT);
  randomSeed(analogRead(A3));
  
  TinyWireM.begin();                                      // Beginnt eine I²C Verbindung als Master
  if (brightness > 50) brightness = 50;                   // Helligkeit auf 50 begrenzen, Hardwarebedingt, sonnst zu großer Strom
  strip.setBrightness(brightness);                        // Helligkeit setzen
  strip.clear();                                          // Alle LEDs auschalten
  strip.show();                                           // geänderte Werte der LEDs anzeigen

  /* Register setzen. Mehr information im Datenblatt*/
  writeTo(ADXL345_DATA_FORMAT,   B00001000);              // B3 == Volle Auflösung = 1, 10-Bit Auflösung = 0 | B1B0 == Sensibilität, siehe Datenblatt
  writeTo(ADXL345_BW_RATE,       B00001101);              // Setzt 800Hz Ausgangs Datenrate (nicht I²C), B4B3B2B1, siehe Datenblatt Tabelle 7 und 8, Seite 14
  writeTo(ADXL345_POWER_CTL,     0);                      // Always be sure to zero out register, don't asume it's zero
  writeTo(ADXL345_POWER_CTL,     B00001000);              // 0 | 0 | Verknüpfen von In-/Aktivität | Automatisch schlafen | Messen Aktiv | Sleep | Wake Up | Wake Up
  writeTo(ADXL345_INT_ENABLE,    B01111100);              // Interupts aktivieren DATA_READY | SINGLE_TAP | DOUBLE_TAP | Activity | Inactivity | FREE_FALL | Watermark | Overrun 
  writeTo(ADXL345_THRESH_TAP,    50);                     // Klopf Schwelle (guter Wert ist 200)
  writeTo(ADXL345_DUR,           0x30);                   // Klopf Dauer einstellen, 0 = Klopfen deaktiviert
  writeTo(ADXL345_LATENT,        0x30);                   // Latenz - Zeit bis zum erwarten des zweiten Klopfens, davor wird ignoriert, 0 = Doppelklopfen deaktiviert
  writeTo(ADXL345_WINDOW,        0xFF);                   // Zeit Spanne wo ein zweites Klopfen passieren muss, damit es ein Doppelklopfen wird, 0 = Doppelklopfen deaktiviert
  writeTo(ADXL345_TAP_AXES,      B00001111);              // Achsen zum (Klopfen) aktivieren: 0 | 0 | 0 | 0 | Unterdrückung bei Bewegung An/Aus | X-Achse Tap An/Aus | Y-Achse Tap An/Aus | Z-Achse Tap An/Aus 
  /* Freier Fall wird erkannt wenn Z, X und Y gleichzeitig den Wert nahe 0 haben, dazu müssen X, Y und Z im ADXL ein Offset haben damit der Free Fall Detektor das korrekt an den Interupt gibt */
  writeTo(ADXL345_THRESH_FF,     4);                      // Freier Fall Schwelle (0-255), 5-9 wird empfohlen laut Datenblatt
  writeTo(ADXL345_TIME_FF,       50);                     // Freier Fall Mindestzeit (0-255), 20 für 100mSec - 70 für 350mSec wird empfohlen laut Datenblatt
  writeTo(ADXL345_OFSX,          255);                    // internen Offset setzen, 0 Aus, 1-123 = +, 255-124 = - // im 2g Modus ~4Einheiten pro Bit
  writeTo(ADXL345_OFSY,          42);                     // internen Offset setzen, 0 Aus, 1-123 = +, 255-124 = - // im 2g Modus ~4Einheiten pro Bit
  writeTo(ADXL345_OFSZ,          187);                    // internen Offset setzen, 0 Aus, 1-123 = +, 255-124 = - // im 2g Modus ~4Einheiten pro Bit
  writeTo(ADXL345_THRESH_ACT,    30);                     // Schwellwert zum erkennen einer Aktivität
  writeTo(ADXL345_THRESH_INACT,  30);                     // Schwellwert zum erkennen einer Inaktivität
  writeTo(ADXL345_TIME_INACT,    255);                    // Zeit die der Schwellwert der Inaktivität überschritten werden muss 0-255 Sekunden
  writeTo(ADXL345_ACT_INACT_CTL, B11111111);              // Einstellungen ACT 0=DC 1=AC | ACT (erkennen) X 0=Aus 1=An | ACT Y | ACT Z | INACT 0=DC 1=AC | INACT (erkennen) X 0=Aus 1=An | INACT Y | INACT Z
}

void loop(){
  readADXLint();                                                                       // Funktion ADXL Interupts auswerten
  control();                                                                           // Funktion zum Programmablauf steuern
  strip.show();
}
  

void control(){
  if (bitRead(intdata,2)){
    freefallprog();                                                                     // Wenn Freier Fall erkannt, LED Muster für Freien Fall starten
    intdata = read8(ADXL345_INT_SOURCE);                                                // ADXL Interupt Speicher auslesen und damit leeren, verhindert doppeltes ausführen von freefallprog()
    nextProgram();                                                                      // Erhöhe den Programzähler oder setzt auf Null Wenn Programmanzahl überschritten
  }
  if (inaktiv && !controlArray[program].demoaktiv){                                     // Wenn inaktiv und aktuelles Programm nicht für DemoModus
    nextProgram();                                                                      // Erhöhe den Programzähler oder setzt auf Null Wenn Programmanzahl überschritten
  }
  else if(millis()-millis_merker_prog>controlArray[program].bremse){
    millis_merker_prog = millis();
    switch (program){                                                                   // Programm nach Programmzähler wählen
      case Cube:
        cube(readADXLtop());  // Sechs immer oben
        break;
      case Dice:
        dice();               // WürfelModus
        break;
      case FadeOnSite:
        fadeonsite();         // Farbwechsel abhängig der oben liegenden Seite
        break;
      case Disco:
        disco();              // Disco, zufällige Farbwiedergabe
        break;
      case Rainbow:
        rainbow();            // Farbwechsel nach Regenbogen
        break;
      case Flash:
        flash();              // Zufälliges Aufblitzen
        break;
      case Snake:
        snake();              // Schlange
        break;
      case Rain:
        rain(readADXLtop());  // Simuliert Regen, Farbe abhängig der oben liegenden Seite
        break;
      case HeartBeat:
        heartbeat();          // Simuliert Herzklopfen
        break;
      default:
        break;    
    }
    fadeInOut();                                                                    // Funktion zum gemütlichen Übergang, wenn im Programm hinterlegt
  } 
}

void fadeInOut(){
  static uint8_t fadebrightness = brightness;
  if(controlArray[program].fadeinout){
    if(millis() - millis_merker_demo > demotime && inaktiv){
      if (--fadebrightness == 0){
        millis_merker_demo = millis();
        nextProgram();
      }
    }
    else if(fadebrightness < brightness) fadebrightness++;
  }
  else{
    fadebrightness = brightness;
    if(millis() - millis_merker_demo > demotime && inaktiv){
      millis_merker_demo = millis();
      nextProgram();
      fadebrightness = 0;
    }
  }
  if(fadebrightness>brightness)fadebrightness=brightness;
}

void nextProgram(){
  if (++program >= (sizeof(controlArray)/sizeof(controlArray[0]))) program = 0;
}

uint8_t readNachbarPixel(uint8_t pixel, uint8_t orientation){
  uint8_t side  = pixel / 9;                    // vom übergebenen Pixel die Flächennummer 0-5
  uint8_t index = pixel % 9;                    // vom übergebenen Pixel die Indexnummer auf seiner Fläche 0-8
  uint8_t result = 0;                           // Rückgabewertvariable
  switch (orientation){                         // an welcher Kante liegt die abfragende Seite
    case Oben: // 0
      switch (index){                           // welches obere Pixel auf der Fläche sucht den oberen Nachbar
        case 6: // Ecke oben links
          result = callPixel(side, Oben, 0);    // callPixel(Fläche des Fragenden Pixels, Kannte des Fragenden Pixels, Position im Bezug zur Kante wenn man von der Mitte schaut (0=Rechts, 1=Mitte, 2=Links));
          break;
        case 7: // Seite oben mitte
          result = callPixel(side, Oben, 1);
          break;
        case 8: // Ecke oben rechts
          result = callPixel(side, Oben, 2);
          break;
        default: 
          result = pixel + 3;
          break;
      }
      break;
    case Rechts: // 1
      switch (index){
        case 8: // Ecke oben rechts
          result = callPixel(side, Rechts, 0);
          break;
        case 5: // Seite rechts mitte
          result = callPixel(side, Rechts, 1);
          break;
        case 2: // Ecke unten rechts
          result = callPixel(side, Rechts, 2);
          break;
        default: 
          result = pixel + 1;
          break;
      }
      break;
    case Links: // 2
      switch (index){
        case 0: // Ecke unten links
          result = callPixel(side, Links, 0); //ori-2+ori/3+ori/3
          break;
        case 3: // Seite links mitte
          result = callPixel(side, Links, 1); //1
          break;
        case 6: // Ecke oben links
          result = callPixel(side, Links, 2); //
          break;
        default: 
          result = pixel - 1;
          break;
      }
      break;
    case Unten: // 3
      switch (index){
        case 2: // Ecke unten rechts
          result = callPixel(side, Unten, 0);
          break;
        case 1: // Seite unten mitte
          result = callPixel(side, Unten, 1);
          break;
        case 0: // Ecke unten links
          result = callPixel(side, Unten, 2);
          break;
        default: 
          result = pixel - 3;
          break;
      }
      break;
  }
  return result;
}

uint8_t callPixel(uint8_t aufruferFlaeche, uint8_t aufruferKante, uint8_t index){
  uint8_t pgm = pgm_read_byte(&partner[aufruferFlaeche][aufruferKante]);  // Holt Daten über anliegende Fläche mit ihrer anliegenden Kante aus dem PROGMEM                                           
  uint8_t flaeche = pgm & 0b111;                                          // Beispiel [0][1] (Fläche rechts von 0) // 0b00001 + 0b11000 = 0b11001 // 0b11001 & 0b111 = 0b1 = 1 // Fläche 1
  uint8_t kante = pgm >> 3;                                               // schiebt Bits des Arrays partner [Fläche] [Kante] um 3 Bits nach rechts, Es bleibt die anliegende Kante übrig // 0b11001 >> 3 = 0b11 = 3 = Unten
  uint8_t result = 0;                                                     // Rückgabewertvariable
  switch (kante){                                                         // abhängig davon, welche Kante an der Aufruferfläche liegt
    case Oben:   //0
      result = 8 - index;                                                 // errechnet anhand des index, das Pixel auf der Fläche. Möglich 6, 7 oder 8
      break;
    case Unten:  //3
      result = 0 + index;                                                 // errechnet anhand des index, das Pixel auf der Fläche. Möglich 0, 1 oder 2
      break;
    case Rechts: //1
      result = 2 + index * 3;                                             // errechnet anhand des index, das Pixel auf der Fläche. Möglich 2, 5 oder 8
      break;
    case Links:  //2
      result = 6 - index * 3;                                             // errechnet anhand des index, das Pixel auf der Fläche. Möglich 0, 3 oder 6
      break;
  }
  result = result + flaeche * 9;                                          // errechnet nahand der Fläche die Pixelnummer
  return result;
}

/*ADXL345 Komunikation*/
void readADXLint(){
  static uint8_t  last_program = 0;                       // Programmspeicher welches vor Demo aktiv war
  intdata = read8(ADXL345_INT_SOURCE);                    // Interups Auslesen und im ADXL leeren
  if(bitRead(intdata,3)&&!inaktiv){                       // Wenn Inaktivität erkannt (keine Änderung über ADXL345_TIME_INACT eigestellte Sekunden)
    inaktiv = true;
    millis_merker_demo = millis();
    last_program = program;                               // Aktives Program im ProgramMerker hinterlegen
  }
  if(bitRead(intdata,4)&&inaktiv){                        // Wenn Aktivität erkannt
    inaktiv = false;
    program = last_program;                               // Letztes aktives Program aus dem ProgramMerker holen
  }
}

uint8_t readADXLtop(){                                    // Welche Seite liegt oben
  int16_t x = read16(ADXL345_DATAX0);                     // X Achsenwert auslesen
  int16_t y = read16(ADXL345_DATAY0);                     // Y Achsenwert auslesen
  int16_t z = read16(ADXL345_DATAZ0);                     // Z Achsenwert auslesen
  uint8_t top = 0;
  if(x>200) top=1;                                        //Fläche 1 oben                    
  if(y>200) top=2;                                        //Fläche 2 oben                    
  if(z>200) top=3;                                        //Fläche 3 oben                    
  if(x<-200) top=4;                                       //Fläche 4 oben                    
  if(y<-200) top=5;                                       //Fläche 5 oben                    
  if(z<-200) top=0;                                       //Fläche 0 oben  
  return top;                  
}

uint16_t read16(uint8_t address){
   uint8_t MSByte = 0, LSByte = 0;
   int16_t regValue = 0;
   TinyWireM.beginTransmission(ADXL345_ADDRESS);
   TinyWireM.write(address);                     
   TinyWireM.endTransmission();
   TinyWireM.requestFrom(ADXL345_ADDRESS, 2);             
   if(TinyWireM.available()){
     LSByte = TinyWireM.read();
     MSByte = TinyWireM.read();
   }
   regValue = (MSByte<<8) + LSByte;
   return regValue;
}

uint8_t read8(uint8_t address){
   uint8_t regValue = 0;
   TinyWireM.beginTransmission(ADXL345_ADDRESS);
   TinyWireM.write(address);                     
   TinyWireM.endTransmission();
   TinyWireM.requestFrom(ADXL345_ADDRESS, 1);             
   if(TinyWireM.available()){
     regValue = TinyWireM.read();
   }
   return regValue;
}

void writeTo(uint8_t address, uint8_t val){
  TinyWireM.beginTransmission(ADXL345_ADDRESS);  
  TinyWireM.write(address);             
  TinyWireM.write(val);                 
  TinyWireM.endTransmission();          
}

/*LED Programme*/
void heartbeat(){
  if(red>55) red=0;                                           // Wenn der RotWert über 55 dann RotWert auf 0 setzen
  if(red==55) mem++;                                          // Wenn RotWert 55 erreicht hat, Merker um eins erhöhen (damit später Rot reduziert wird bei mem gleich 1 oder 3)
  if(red==0) mem++;                                           // Wenn RotWert 0  erreicht hat, Merker um eins erhöhen (damit später Rot erhöht wird bei mem gleich 0 oder 2) und damit der Merker für die Pause erhöht wird
  if(mem>=4) delay(100);                                      // Wenn Merker über 4, eine Pause einlegen (Also 50-4= 46mal 0,1s Pause)
  if(mem>=50) mem=0;                                          // Wenn Merker über 50, dann Merker wieder auf 0, was die Pause beendet, und Rot wieder erhöht
  if(mem == 0 || mem == 2) red++;                             // Wenn Merker 0 oder 2 Rot erhöhen
  else if(mem == 1 || mem == 3) red--;                        // Wenn Merker 1 oder 3 Rot verringern
  else red=0;                                                 // Sicherstellen das Rot 0 ist wenn Merker einen anderen Wert hat (Kann durch andere Funktionen (LED-Programme) geändert werden
  strip.clear();
  for(uint8_t site=0; site<6; site++){                        // Auf jeder Würfelseite ausführen                                           
    if(red>=40) for(uint8_t i=0; i<9; i+=2) strip.setPixelColor(i+9*site, red-40, 0, 0);      // Wenn rot größer 40 (verspäteter start) schreibe jedes gerade pixel auf der Würfelseite (Ecken und Mitte (Mitte wird später überschrieben)                         
    if(red>=20) for(uint8_t i=1; i<8; i+=2) strip.setPixelColor(i+9*site, red-20, 0, 0);      // Wenn rot größer 20 (verspäteter start) schreibe jedes ungerade pixel auf der Würfelseite (oben, unten, links, rechts)                          
    strip.setPixelColor(4+9*site, red, 0, 0);                                                 // schreibe auf die mitte (Pixel 4) der Seite
  }                                    
}

void freefallprog(){                                     // lässt jedes zweite Pixel 6mal rot blinken // diese Funktion soll bewusst blockieren
  for(uint8_t j=0; j<6; j++){
    strip.clear();
    for(uint8_t i=0; i<ledAnzahl; i+=2){
      strip.setPixelColor(i,255,0,0);
    }
    strip.show();
    delay(100);
    strip.clear();
    strip.show();
    delay(100);
  }
}

void rain(uint8_t side){
  static uint8_t raincolor[12][4];                      // Zwischenpeicher der Farbwerte für Regenmuster
  uint8_t rainPixel[12];                                // Speichert die 12 obenliegenden Regenpixel
  int8_t changer[4];                                    // Speichert die Verechenoberratoren für die nach unten folgenden Pixel
  for(uint8_t i=0; i<4; i++){                           // Für alle 4 anliegenden Seiten (oben = 0, rechts = 1, links = 2, unten = 3)
    for(uint8_t j=0; j<3; j++){                         // Für alle 3 anliegenden Pixel der Seite (Index 0, 1, 2)
      rainPixel[i*3+j] = callPixel(side, i, j);         // Übergebe die 12 Pixel in das rainPixelArray // i*3+j ergibt die Nummerierung für das Array(0-11)
    }
    uint8_t upsite = pgm_read_byte(&partner[side][i]);  // Liest die Information von der anliegenden Fläche (BitArray)
    upsite = upsite >> 3;                               // gibt von der anliegenden Fläche die obere Kante wieder (schiebt die FlächenInfo Bits raus)
    changer[i] = upsite *2 -3;                          // Seite 5, index 852 -1 // Seite 1, 012 +3 // Seite 4, 678 -3 // Seite 2, 012 +3  // gibt gibt anhand der oben liegenden Kante, den verrechen operator für die darunter folgenden Pixel aus (0*2-3=-3, 1*2-3=-1, 2*2-3=1, 3*2-3=3)
  }                                                     // bei "oben" liegenden Pixel darunter liegende Pixel 3 kleiner, bei "rechts" liegenden Pixel, darunter liegende 1 kleiner, etc
  switch (side){                                        // Farbwahl nach Seite
    case 0: red=255; green=0; blue=0; break;
    case 1: red=0; green=255; blue=255; break;
    case 2: red=255; green=0; blue=255; break;
    case 3: red=255; green=255; blue=0; break;
    case 4: red=0; green=0; blue=255; break;
    case 5: red=0; green=255; blue=0; break;
  }
  mem=random(0,150);                                                                                                                                // erstellt merker zum zufälligen Starten
  strip.clear();
  for(uint8_t row=0; row<12; row++){                                                                                                                // row steht für eine der 12 möglichen senkrechten Reihen
    if (raincolor[row][3]>49){                                                                                                                      // wenn Zähler über 49
      strip.setPixelColor(rainPixel[row]+changer[row/3]+changer[row/3],raincolor[row][0],raincolor[row][1],raincolor[row][2]);                      // setze Farbe aufs untere pixel
      strip.setPixelColor(rainPixel[row]+changer[row/3],raincolor[row][0]/1.1,raincolor[row][1]/1.1,raincolor[row][2]/1.1);                         // setze Farbe aufs zweite Pixel von oben mit 90% Leuchtkraft
      strip.setPixelColor(rainPixel[row],raincolor[row][0]/1.1/1.1,raincolor[row][1]/1.1/1.1,raincolor[row][2]/1.1/1.1);                            // setze Farbe aufs obere Pixel mit 90% von 90% Leuichtkraft
      for(uint8_t i=0; i<3; i++){                                                                                                                      
        raincolor[row][i] = raincolor[row][i]/1.1;                                                                                                  // reduziere die Leuchtkraft um 90%
      }
      if(raincolor[row][0]<1&&raincolor[row][1]<1&&raincolor[row][2]<1){                                                                            // wenn alle Farbwerte unter 1
        for(uint8_t i=0; i<4; i++){
          raincolor[row][i] = 0;                                                                                                                    // Zähler zurücksetzen und sicherstellen das Farbwerte gleich 0
        }
      }
    }
    if (raincolor[row][3]<50&&raincolor[row][3]>24){                                                                                                // wenn Zähler 25 bis 49
      strip.setPixelColor(rainPixel[row]+changer[row/3],raincolor[row][0],raincolor[row][1],raincolor[row][2]);                                     // setze Farbe aufs zweite pixel von oben
      strip.setPixelColor(rainPixel[row],raincolor[row][0]/1.1,raincolor[row][1]/1.1,raincolor[row][2]/1.1);                                        // setzt Farbe aufs obere pixel mit 90% Leuichtkraft
      ++raincolor[row][3];
    }
    if (raincolor[row][3]<25&&raincolor[row][3]>0){                                                                                                 // wenn Zähler 1 bis 24
      strip.setPixelColor(rainPixel[row],raincolor[row][0],raincolor[row][1],raincolor[row][2]);                                                    // setze Farbe aufs obere Pixel der Reihe
      ++raincolor[row][3];                                                                                                                          // Zähler um 1 erhöhen
    }
    if (mem==row&&raincolor[row][3]==0){                                                                                                            // wenn Zufalsmerker gleich j, und Zähler der Reihe gleich 0
      raincolor[row][0]=red; raincolor[row][1]=green; raincolor[row][2]=blue;                                                                       // setze übergebe Farbewerte, gesetzt nach Seite die oben liegt
      ++raincolor[row][3];                                                                                                                          // Zähler für Reihe auf 1 setzen (Zählen ist füpr Zeitverzögerung zuständig)
    }
  }
}

void snake(){
  static uint8_t viper[] = {4,54,54,54,54,54};            // Schlangen Position Array
  uint8_t nachbarPixel[4];
  mem = 0;
  for(uint8_t i=0; i<4; i++){                             // für alle 4 Nachbarpixel überprüfen
    bool crash = false;
    for(uint8_t k=1; k<sizeof(viper); k++){               // für alle Pixel die die Schlange belegt überprüfen
      if(readNachbarPixel(viper[0],i) == viper[k]){       // wenn das Nachbarpixel gleich eines Pixels der Schlange
        crash = true;                           
      }
    }
    if (!crash) {                                         // wenn das Nachbarpixel keine besetzte Position ist
      nachbarPixel[mem] = readNachbarPixel(viper[0],i);   // dann schiebe den PixelIndex über eventuelle mögliche Pixel
      mem++;                                              // Zähler für mögliche Pixel erhöhen
    }
  }
  strip.clear();
  for(uint8_t j = sizeof(viper)-1; j>0; j--){             // Über die Länge der Schlange
    viper[j] = viper[j-1];                                // Übergib das gespeicherte Pixel im Viper Array eine Position Richtung Ende, das letzte fällt weg
    strip.setPixelColor(viper[j],255-(j*40),0+(j*40),0);  // setze Farbewerte ändernd Richtung Schanzende (Von Rot Kopf, über gelb Richtung grün
  }
  viper[0] = nachbarPixel[random(0,mem)];                 // übergibt dem Kopf, eine zufällige auswahl aus den möglichen Nachbarpixeln
  strip.setPixelColor(viper[0],255,0,0);                  // Farbwert Kopf
}

void flash(){
  if(!random(100)){                                       // nur in einem von 100 Fällen ausführen
    strip.setPixelColor(random(ledAnzahl-1),random(255),random(255),random(255));
    strip.show();
    delay(7);                                             // länge des aufblitzen in Millisekunden
    strip.clear();
  }
}

void rainbow(){
  if(red > 250 || green > 250 || blue > 250){
    red=250; blue=0; green=0;
  }
  uint8_t newred = red;
  uint8_t newgreen = green;
  uint8_t newblue = blue;
  for(uint8_t i=0; i<ledAnzahl; i++){
    if(newred == 250 && newblue == 0 && newgreen < 250) newgreen += 10;
    else if(newred > 0 && newblue == 0 && newgreen == 250) newred -= 10;
    else if(newred == 0 && newblue < 250 && newgreen == 250) newblue += 10;
    else if(newred == 0 && newblue == 250 && newgreen > 0) newgreen -= 10;
    else if(newred < 250 && newblue == 250 && newgreen == 0) newred += 10;
    else if(newred == 250 && newblue > 0 && newgreen == 0) newblue -= 10;
    strip.setPixelColor(i,newred,newgreen,newblue);
  }
  if(red == 250 && blue == 0 && green < 250) green += 10;
  else if(red > 0 && blue == 0 && green == 250) red -= 10;
  else if(red == 0 && blue < 250 && green == 250) blue += 10;
  else if(red == 0 && blue == 250 && green > 0) green -= 10;
  else if(red < 250 && blue == 250 && green == 0) red += 10;
  else if(red == 250 && blue > 0 && green == 0) blue -= 10;
  else{
    red=250; blue=0; green=0;
  }
}

void disco(){
  for(uint8_t i=0; i<=ledAnzahl; i++){
    strip.setPixelColor(i,random(255),random(255),random(255));
  }
}

void fadeonsite(){
  uint8_t newred = map(read16(ADXL345_DATAX0), -280, 280, 0, 255);
  uint8_t newgreen = map(read16(ADXL345_DATAY0), -280, 280, 0, 255);
  uint8_t newblue = map(read16(ADXL345_DATAZ0), -280, 280, 0, 255);
  if(newred+10 < red) red -= 2;
  if(newred-10 > red) red += 2;
  if(newgreen+10 < green) green -= 2;
  if(newgreen-10 > green) green += 2;
  if(newblue+10 < blue) blue -= 2;
  if(newblue-10 > blue) blue += 2;
  for(uint8_t i=0; i<=ledAnzahl; i++){
    strip.setPixelColor(i,red,green,blue);
  }
}

void dice(){
  if (bitRead(intdata,6)) mem=2;                              // Wenn Erschütterung erkannt
  if (mem < 520){                                             // Solange Zeitspanne unter 520 mS
    cube(random(0,6));                                        // Wähle eine zufällige Seite oben
    delay(mem);                                               // Warte die zwischengespeicherte Zeit
    mem = (mem * random(10,21)) / 10;                         // Erhöhe die wartezeit etwas im zufälligen Bereich (simuliert ein langsames auslaufen der Zeit
  }
}

void cube(byte side){
  uint64_t pgm = 0;                                           // Zwischenspeicher für das aus dem PROGMEM geholten Bitmuster
  memcpy_P(&pgm, &Sequenz[side], sizeof(pgm));                // memcpy_P holt aus dem PROGMEM Daten (Ziel, Quelle, Größe in Byte von Quelle und Ziel)
  for (uint8_t index = 0; index<54; index++){
    if (bitRead(pgm,index)) strip.setPixelColor(index, 255, 255, 255);
    else strip.setPixelColor(index, 0, 0, 0);
  }
}

#else //neopixel library required 7.37MHz minimum clock speed; these and following lines are used to skip this sketch in internal testing. It is not needed in your sketches.
#warning "Neopixel control requires F_CPU > 7.37MHz"
void setup(){}
void loop(){}
#endif