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