Code Aufräumen/Verbessern, Speicherplatz freigeben

Ich bin inzwischen weiter gekommen mit dem Speicherplatz sparen.

Der Sketch verwendet 6176 Bytes (75%) des Programmspeicherplatzes. Das Maximum sind 8192 Bytes.
Globale Variablen verwenden 294 Bytes (57%) des dynamischen Speichers, 218 Bytes für lokale Variablen verbleiben. Das Maximum sind 512 Bytes.

gegenüber

Viel Speicher hat es gespart, dass ich in der Regenfallanimation die Farbwerte der Pixel nicht mehr zwischen speichere, sondern wieder auslese. Auf die Funktion getPixelColor() bin ich gestoßen, da ich die dazugehörige Bibliothek mir angeschaut habe.
Die Funktion gibt den Farbwert als 32Bit zurück, und beinhaltet auf den linken 8 Bits den Wert von Weiß (werden nur gesetzt, wenn es sich um RGBW Strips handelt), gefolgt von den 8Bits für Rot, gefolgt für die 8Bits von Grün, und schlussendlich gefolgt von den 8Bits für Blau.
Da ich die roten, grünen und blauen Werte einzeln brauche, habe ich mir eine Funktion geschrieben (Zeile 276-286), die mir die Werte, entweder für Rot, Grün oder Blau (Weiß wäre auch möglich) zurück gibt.

uint8_t readPixelColor(uint8_t pixel, uint8_t color){                                         // Liefert den gesetzten Farbwert als 8bit entsprechend der übergeben Farbe als Enumeration
  uint32_t pixelColor = strip.getPixelColor(pixel);                                           // Liest den 32bit Farbwert wwwwwwwwrrrrrrrrggggggggbbbbbbbb
  uint32_t colorMask = 255;                                                                   // Erstellt eine Maske zum herrausfiltern einzelner Farbwerte
  colorMask = colorMask << (color*8);                                                         // Schiebt die Maske an die Übergebene Farbwertposition
  pixelColor = pixelColor & colorMask;                                                        // Filtert den gesuchten Farbwert raus
  pixelColor = pixelColor >> (color*8);                                                       // Schiebt den gesuchten Farbwert auf die letzten 8 Bits
  uint8_t returnValue = pixelColor;                                                           // Übergibt den Wert an eine 8Bit Variable
  return returnValue;                                                                         // Übergibt den Wert
}

Aufrufen tue ich die Funktion über die Pixelnummer und in einem Enum gespeichert Farben.
Zeile 81-85:

enum : uint8_t{                                 // Enum zum auslesen der Farbe des Pixel
  Blue,
  Green,
  Red
};

Zeile 501-505:

for(uint8_t pixel=0; pixel<ledAnzahl; pixel++)                                              // Für alle Pixel
  strip.setPixelColor(pixel,                                                                // Setze die Farbe des Pixels mit
    readPixelColor(pixel,Red),                                                              // dem ausgelesen roten Wert des Pixels wieder, reduziert die Leuchtkraft etwas
    readPixelColor(pixel,Green),                                                            // dem ausgelesen grünen Wert des Pixels wieder, reduziert die Leuchtkraft etwas
    readPixelColor(pixel,Blue));                                                            // dem ausgelesen blauen Wert des Pixels wieder, reduziert die Leuchtkraft etwas       

Bedingt dadurch, das die Bibliothek die Farbwert angepasst an Brightness speichert, und beim wieder zurückgeben wieder hochrechnet, sind die zurückgegebenen Werte etwas geringer. Grund ist das rechnen mit Integer und dem wegfallen der Nachkommastelle. Dies stört jetzt hier nicht, ist eher von Vorteil, da dadurch die Regentropfen langsam ausfaden.

Ziel war es auch, das auslesen der Farbwerte, in anderen, bzw. neuen Funktionen einzusetzen. Und jetzt kommt es zu einem für mich nicht erklärbaren Problem. Solange ich die Funktion readPixelColor(pixel,Farbe) nur in der Funktion rain() aufrufe läuft alles normal. Sobald ich die Funktion readPixelColor(pixel,Farbe) zusätzlich in anderen Funktionen aufrufe, verlangsamt sich der Code, und damit die Animation gefühlt um das Dreifache. Die Regenanimation sieht dann nicht mehr so aus wie früher. Wie ist das zu erklären? Schlägt es so stark auf die Rechenleistung, wenn eine Funktion an verschiedenen Stellen im Code aufgerufen wird?

Ein einkommentieren der Zeilen 466-470 bremst den Code aus.

// Danke an die Foren:
// https://www.arduinoforum.de/arduino-Thread-WS2812b-W%C3%BCrfel
// https://forum.arduino.cc/t/code-aufraumen-verbessern-speicherplatz-freigeben/904356
//
//            +----------+                                   +---------+                      
//    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    // https://github.com/SpenceKonde/megaTinyCore/tree/master/megaavr/libraries/tinyNeoPixel_Static
#include <TinyWireM.h>                          // ATTiny I2C Bibliothek v1.1.0 // https://github.com/adafruit/TinyWireM
#include <avr/power.h>

// ATTiny WS2812b Initialisierung
constexpr uint8_t datenPin = 4;                 // WS2812b DatenPin
constexpr 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);


constexpr uint8_t  brightness = 50;             // Variable für die Helligkeit; 0 bis 50, Aus bis Volle Heligkeit
constexpr uint32_t demotime = 100000;           // Zeitschwelle zum wechseln zum nächsten Programm im Demo-Modus (Millisekunden)
uint8_t  intdata;                               // Variable für Interupts vom ADXL345b    / Global, da von mehreren Funktionen ausgelsen wird
bool inaktiv = false;                           // Variable zum speichern der Inaktivität / Global, da von mehreren Funktionen ausgelesen wird
uint16_t mem = 0;                               // Zwischenspeicher                       / Global, da von mehreren Funktionen zu verschieden zwecken Durchlaufübergreifend genutzt
uint8_t  program = 0;                           // Welches Programm ist aktiv             / Global, da von mehreren Funktionen beeinflusst
uint8_t  red = 255, green = 255, blue = 255;    // Farbvariablen für den Strip            / Global, da von mehreren Funktionen beeinflusst
uint32_t millis_merker_demo;                    // Zwischenspeicher für Millis zum Programmwechsel im Demomodus / Global, da von mehreren Funktionen der Wert gesetzt wird

constexpr 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
};

constexpr 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
  Glow            // Programm 9
};

enum : uint8_t{                                 // Enum zum auslesen der Farbe des Pixel
  Blue,
  Green,
  Red
};

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
};

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


// 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
  static_assert(50>=brightness);                          // Prüfen ob brightness nicht über 50, 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(){
  intdata = read8(ADXL345_INT_SOURCE);                            // ADXL Interupt Speicher auslesen und damit leeren
  readADXLinaktiv();                                              // Prüft ob Aktiv/Inaktiv und setzt dadurch Normal-/Demo-Modus
  control();                                                      // Funktion zum Programmablauf steuern
  strip.show();                                                   // Sendet die eingestellten Farbwerte zu den Pixeln und zeigt sie
}
  

void control(){                                                   // Steuert die Programmwahl
  static uint32_t millis_merker_prog;                                                   // Zwischenspeicher für Millis um die Programme etwas zu bremsen
  if (bitRead(intdata,2)){                                                              // Im dritten Bit von rechts ist der Interrupt freier Fall hinterlegt
    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){                    // Führt die Programme nur aller der Zeit bei Programm Bremse hinterlegten x Millisekunden aus
    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;
      case Glow:
        glow(readADXLtop());
        break;
      default:
        break;    
    }
    if(inaktiv) fadeInOut();                                      // Funktion zum wechseln des Programms im Demo-Modus(inaktiv), wahlweise mit FadeInOut, wenn so im ControlStruct hinterlegt
    else strip.setBrightness(brightness);                         // Wenn nicht inaktiv, Standard Helligkeit setzen
  } 
}

void fadeInOut(){                                                 // Funktion zum gemütlichen Übergang im DemoModus
  static uint8_t fadebrightness = 0;                              // Variable zum verändern der Helligkeit beim Faden erstellen
  if(controlArray[program].fadeinout){                            // Wenn das Programm beim Wechsel Faden soll
    if(millis() - millis_merker_demo > demotime){                 // Wenn die Demo-Zeit überschritten ist
      if(--fadebrightness == 0) nextProgram();                    // Wenn Verringerung von Fadebrightness um 1 gleich 0 ist wähle nächstes Programm
    }
    else if(fadebrightness < brightness) fadebrightness++;        // Wenn die Demo-Zeit nicht überschritten und Fadebrightness unter Brightness, erhöhe Fadbrightness um 1
  }
  else{                                                           // Wenn das Programm beim Wechsel nicht Faden soll
    fadebrightness = brightness;                                  // Fadebrightness gelich Brightness setzen
    if(millis() - millis_merker_demo > demotime){                 // Wenn die Demo-Zeit überschritten ist
      nextProgram();                                              // wähle nächstes Programm
      fadebrightness = 0;                                         // Setze Fadebrightness auf 0, um evtuelles FadeIn vorzubereiten
    }
  }
  if(fadebrightness>brightness)fadebrightness=brightness;         // Sicherstellen, das Fadebrightness Brightness nicht übersteigt
  strip.setBrightness(fadebrightness);                            // Helligkeit setzen
}

void nextProgram(){                                               // Erhöhe den Programzähler oder setzt auf Null Wenn Programmanzahl überschritten
  millis_merker_demo = millis();                                  // DemoZeitMerker mit millis() gleich setzen
  if (++program >= (sizeof(controlArray)/sizeof(controlArray[0]))) program = 0;
  strip.clear();
}

void setColorOnSide(uint8_t side){
  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;
  }
}

/* Das Auslesen der Pixelfarbwerte ist immer etwas geringer als die gesetzten Werte, 
   da in der Bibliothek mit dem Brightnesswert erst runter, dann wieder hoch gerechnet wird */
uint8_t readPixelColor(uint8_t pixel, uint8_t color){                                         // Liefert den gesetzten Farbwert als 8bit entsprechend der übergeben Farbe als Enumeration
  uint32_t pixelColor = strip.getPixelColor(pixel);                                           // Liest den 32bit Farbwert wwwwwwwwrrrrrrrrggggggggbbbbbbbb
  uint32_t colorMask = 255;                                                                   // Erstellt eine Maske zum herrausfiltern einzelner Farbwerte
  colorMask = colorMask << (color*8);                                                         // Schiebt die Maske an die Übergebene Farbwertposition
  pixelColor = pixelColor & colorMask;                                                        // Filtert den gesuchten Farbwert raus
  pixelColor = pixelColor >> (color*8);                                                       // Schiebt den gesuchten Farbwert auf die letzten 8 Bits
  uint8_t returnValue = pixelColor;                                                           // Übergibt den Wert an eine 8Bit Variable
  return returnValue;                                                                         // Übergibt den Wert
}

/* Pixel Berechnung */
uint8_t readNachbarPixel(uint8_t pixel, uint8_t orientation){     // Liefert das Nachbarpixel vom übergebenen Pixel, anhand der Orientation (oben, unten, links oder rechts)
  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){ // errechnet die Nachbarpixel über die Kanten hinaus
  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 readADXLinaktiv(){                                           // Prüft ob Aktiv/Inaktiv
  static uint8_t last_program = 0;                                // Programmspeicher welches vor Demo aktiv war
  if(bitRead(intdata,3)&&!inaktiv){                               // Wenn Inaktivität erkannt und nicht vorher schon inaktiv
    inaktiv = true;
    millis_merker_demo = millis();                                // Demo Ablaufzeit auf 0 setzen
    last_program = program;                                       // Aktives Program im ProgramMerker hinterlegen
  }
  if(bitRead(intdata,4)&&inaktiv){                                // Wenn Aktivität erkannt, und vorher inaktiv
    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){                                 // Liest zwei Byte von der übergebene Adresse
   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){                                   // Liest ein Byte von der übergebene Adresse             
   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){                       // Schreibt ein Byte an die übergebene Adresse
  TinyWireM.beginTransmission(ADXL345_ADDRESS);  
  TinyWireM.write(address);             
  TinyWireM.write(val);                 
  TinyWireM.endTransmission();          
}

/*LED Programme*/
void heartbeat(){                                                 // "simuliert" Herzklopfen
  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>=300) mem=0;                                         // Wenn Merker über x*Laufzeit, dann Merker wieder auf 0, was die Pause beendet, und Rot wieder erhöht // Bsp. mem=300 Laufzeit=10ms > 3Sekunden Pause
  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 glow(uint8_t side){                                                                      // lässt alle Pixel zufällig glimmen
  setColorOnSide(side);                                                                       // Farbe setzen abhängig von der oben liegenden Seite
//  for(uint8_t pixel=0; pixel<ledAnzahl; pixel++)                                              // Für alle Pixel
//    strip.setPixelColor(pixel,                                                                // Setze die Farbe des Pixels mit
//      readPixelColor(pixel,Red),                                                              // dem ausgelesen roten Wert des Pixels wieder, reduziert die Leuchtkraft etwas
//      readPixelColor(pixel,Green),                                                            // dem ausgelesen grünen Wert des Pixels wieder, reduziert die Leuchtkraft etwas
//      readPixelColor(pixel,Blue));                                                            // dem ausgelesen blauen Wert des Pixels wieder, reduziert die Leuchtkraft etwas
}

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

void rain(uint8_t side){                                                                      // "simuliert" Regentropfen an den Seiten
  setColorOnSide(side);                                                                       // Farbe setzen abhängig von der oben liegenden Seite
  static uint8_t rainCounter[12];                                                             // 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 adjacentside=0; adjacentside<4; adjacentside++){                                // Für alle 4 anliegenden Seiten (oben = 0, rechts = 1, links = 2, unten = 3)
    for(uint8_t adjacentpixel=0; adjacentpixel<3; adjacentpixel++){                           // Für alle 3 anliegenden Pixel der Seite (Index 0, 1, 2)
      rainPixel[adjacentside*3+adjacentpixel] = callPixel(side, adjacentside, adjacentpixel); // Ü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][adjacentside]);                             // 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[adjacentside] = 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
  mem=random(0,150);                                                                          // erstellt zufälligen Merker
  for(uint8_t pixel=0; pixel<ledAnzahl; pixel++)                                              // Für alle Pixel
    strip.setPixelColor(pixel,                                                                // Setze die Farbe des Pixels mit
      readPixelColor(pixel,Red),                                                           // dem ausgelesen roten Wert des Pixels wieder, reduziert die Leuchtkraft etwas
      readPixelColor(pixel,Green),                                                         // dem ausgelesen grünen Wert des Pixels wieder, reduziert die Leuchtkraft etwas
      readPixelColor(pixel,Blue));                                                         // dem ausgelesen blauen Wert des Pixels wieder, reduziert die Leuchtkraft etwas       
  for(uint8_t row=0; row<12; row++){                                                          // row steht für eine der 12 möglichen senkrechten Reihen
    if (rainCounter[row]>0) ++rainCounter[row];                                               // Zähler erhöhen, wenn er nicht 0 ist
    if (rainCounter[row]==10)                                                                 // Wenn Zähler gleich 25
      strip.setPixelColor(rainPixel[row]+changer[row/3],red,green,blue);                      // zweites (mittleres) Pixel der Reihe auf Startwert setzen
    if (rainCounter[row]==20)                                                                 // Wenn Zähler gleich 50
      strip.setPixelColor(rainPixel[row]+changer[row/3]*2,red,green,blue);                    // drittes (unteres) Pixel der Reihe auf Startwert setzen
    if (mem==row&&rainCounter[row]==0){                                                       // Wenn Merker gleich der aktuellen Reihe (Zufall) und Zähler gleich 0                                                     
      strip.setPixelColor(rainPixel[row],red,green,blue);                                     // erstes (oberes) Pixel der Reihe auf Startwert setzen                                                      
      ++rainCounter[row];                                                                     // Zähler auf 1 setzen, damit das Hochzählen beginnen kann                                                        
    }
    if (strip.getPixelColor(rainPixel[row])+                                                  
        strip.getPixelColor(rainPixel[row]+changer[row/3])+
        strip.getPixelColor(rainPixel[row]+changer[row/3]*2)<1)                               // Wenn die Summe aller Farbwerte der Reihe kleiner 1 ist,
          rainCounter[row]=0;                                                                 // setze den Zähler auf 0 (Reihe beendet)
  }
}

void snake(){                                                     // Schlange
  static uint8_t viper[] = {4,54,54,54,54,54};                    // Schlangen Position Array
  uint8_t nachbarPixel[] = {0,0,0,0};                             // Array der vier Nachbarpixel
  mem = 0;
  for(uint8_t i=0; i<4; i++){                                     // für alle 4 Nachbarpixel überprüfen (Oben, Rechts, Links, Unten)
    bool crash = false;                                           // Varibale zum speichern einer möglichen Kolision
    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 des Schlangenkopfes gleich eines Pixels vom Rest 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 Schwanzende (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(){                                                     // Zufälliges Aufblitzen
  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(){                                                   // Farbwechsel nach Regenbogen
  uint8_t newred = red;                                           // Übergibt newred den aktuellen Wert von Rot
  uint8_t newgreen = green;                                       // Übergibt newgreen den aktuellen Wert von Grün
  uint8_t newblue = blue;                                         // Übergibt newblue den aktuellen Wert von Blau
  for(uint8_t i=0; i<ledAnzahl; i++){                             // Für jedes Pixel den Farbwert etwas ändern (Regenbogen)
    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;
    else {
      newred=250; newblue=0; newgreen=0;                          // wenn es nicht in die Range passt, Startwert Festlegen
    }
    strip.setPixelColor(i,newred,newgreen,newblue);               // setzt dem Aktuellen Pixel den Farbwert
    if (i==0) {                                                   // Nur nach beim ersten Durchlauf
      green = newgreen; red   = newred; blue  = newblue;          // Den ersten veränderten Farbwert den globalen Farbvariablen übergeben (Startwert etwas verschieben, um das wandern des Regenbogens zu simulieren)
    }
  }
}

void disco(){                                                     // Disco, zufällige Farbwiedergabe
  for(uint8_t i=0; i<=ledAnzahl; i++){                            // für alle Pixel
    strip.setPixelColor(i,random(255),random(255),random(255));   // setze einen zufälligen Farbwert
  }
}

void fadeonsite(){                                                // Farbwechsel abhängig der oben liegenden Seite
  uint8_t newred = map(read16(ADXL345_DATAX0), -280, 280, 0, 255);    // setzt den Soll Farbwert anhand der X-Achsenlage des ADXL
  uint8_t newgreen = map(read16(ADXL345_DATAY0), -280, 280, 0, 255);  // setzt den Soll Farbwert anhand der Y-Achsenlage des ADXL
  uint8_t newblue = map(read16(ADXL345_DATAZ0), -280, 280, 0, 255);   // setzt den Soll Farbwert anhand der Z-Achsenlage des ADXL
  if(newred+10 < red) red -= 2;                                       // Hysterese, um langsames Faden, ohne Flackern, zu ermöglichen
  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++){                                // für alle Pixel
    strip.setPixelColor(i,red,green,blue);                            // setze den neuen Farbwert
  }
}

void dice(){                                                      // WürfelModus
  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){                                             // Sechs immer oben
  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 (müssen gleich groß sein)
  for (uint8_t index = 0; index<=ledAnzahl; index++){             // für alle PixelIndexNummern
    if (bitRead(pgm,index)) strip.setPixelColor(index, 255, 255, 255);  // Wenn das Bit gesetzt, dann weise dem Pixel die Farbwerte zu
    else strip.setPixelColor(index, 0, 0, 0);                     // ansonnsten bleibt das Pixel aus
  }
}

#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