From e50a66b57b71cd940f6fa3984453064ad8a7e808 Mon Sep 17 00:00:00 2001 From: Martin Donnelly Date: Tue, 15 Oct 2024 09:12:03 +0100 Subject: [PATCH] Init --- sketch_nov2a.ino | 600 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 600 insertions(+) create mode 100644 sketch_nov2a.ino diff --git a/sketch_nov2a.ino b/sketch_nov2a.ino new file mode 100644 index 0000000..4582379 --- /dev/null +++ b/sketch_nov2a.ino @@ -0,0 +1,600 @@ +// Simple strand test for Adafruit Dot Star RGB LED strip. +// This is a basic diagnostic tool, NOT a graphics demo...helps confirm +// correct wiring and tests each pixel's ability to display red, green +// and blue and to forward data down the line. By limiting the number +// and color of LEDs, it's reasonably safe to power a couple meters off +// the Arduino's 5V pin. DON'T try that with other code! + +#include + +// Because conditional #includes don't work w/Arduino sketches... +#include // COMMENT OUT THIS LINE FOR GEMMA OR TRINKET +//#include // ENABLE THIS LINE FOR GEMMA OR TRINKET + +#define NUMPIXELS 64 // Number of LEDs in strip + +#define NEONUMPIXELS 8 // Number of LEDs in strip + +// Here's how to control the LEDs from any two pins: +// External +// DATAPIN 4 - CLOCKPIN 5 + +// Internal +// DATAPIN 7 - CLOCKPIN 8 + +#define DATAPIN 29 +#define CLOCKPIN 30 + +#define PIN 0 + +Adafruit_DotStar strip = Adafruit_DotStar((NUMPIXELS * 2 ) + 1, DOTSTAR_BGR); + + +// Adafruit_DotStar strip = Adafruit_DotStar(NUMPIXELS, DATAPIN, CLOCKPIN, DOTSTAR_RGB); +// The last parameter is optional -- this is the color data order of the +// DotStar strip, which has changed over time in different production runs. +// Your code just uses R,G,B colors, the library then reassigns as needed. +// Default is DOTSTAR_BRG, so change this if you have an earlier strip. + +// Hardware SPI is a little faster, but must be wired to specific pins +// (Arduino Uno = pin 11 for data, 13 for clock, other boards are different). +//Adafruit_DotStar strip = Adafruit_DotStar(NUMPIXELS, DOTSTAR_BRG); + +#define FIRE_SIZE 15 +#define RED_SIZE 12 +#define LIGHTNING_SIZE 14 +const uint32_t fire[] = { 0xff2426, 0xfe3223, 0xfe4121, 0xfd4f1f, 0xfd5e1d, 0xfc6d1b, 0xfc7b18, 0xfb8a16, 0xfb9814, 0xfaa712, 0xfab610, 0xffc20d, 0xffd30b, 0xffe309, 0xfff407, 0xf7ff05 }; +const uint32_t lightning[] = { 0xffffff, 0x2436ff, 0x2e43fe, 0x3950fe, 0x4354fe, 0x4e6bfe, 0xffffff, 0x5879fe, 0x6386fe, 0x6d93fe, 0x78a1fe, 0x82aefe, 0x8dbcfe, 0x97c9fe, 0xa2d6fe, 0xace4fe }; // ,0xc2fffe, , , , , , , , , , }; +const uint32_t reds[] = { 0xf4c262, 0xff6961 , 0xff5c5c, 0xff1c00, 0xff0800, 0xff0000, 0xcd5c5c, 0xe34234, 0xd73b3e, 0xce1620, 0xcc0000, 0xb22222 }; //, , , , , , , , , , , 0xb31b1b}; + +const uint32_t clu_red_yellow[] = { 0xff0000, 0xff1100, 0xff2200, 0xff3300, 0xff4400, 0xff5500, 0xff6600, 0xff7700, 0xff8800, 0xff9900, 0xffaa00, 0xffbb00, 0xffcc00, 0xffdd00, 0xffee00, 0xffff00 }; +const uint32_t clu_blue_red[] = {0x0000ff, 0x2200ff, 0x4400ff, 0x6600ff, 0x8800ff, 0xaa00ff, 0xcc00ff, 0xee00ff, 0xff00ee, 0xff00cc, 0xff00aa, 0xff0088, 0xff0066, 0xff0044, 0xff0022, 0xff0000}; +const uint32_t clu_teal_purple[] = {0x00ffff, 0x00ddff, 0x00bbff, 0x0099ff, 0x0077ff, 0x0055ff, 0x0033ff, 0x0011ff, 0x1100ff, 0x3300ff, 0x5500ff, 0x7700ff, 0x9900ff, 0xbb00ff, 0xdd00ff, 0xff00ff}; +const uint32_t clu_red_orange[] = { 0xff0000, 0xff0c00, 0xff1700, 0xff2300, 0xff2f00, 0xff3a00, 0xff4600, 0xff5200, 0xff5d00, 0xff6900, 0xff7500, 0xff8000, 0xff8c00, 0xff9800, 0xffa300, 0xffaf00 }; +const uint32_t clu_teal_blue[] = { 0x00ffff, 0x00eeff, 0x00ddff, 0x00ccff, 0x00bbff, 0x00aaff, 0x0099ff, 0x0088ff, 0x0077ff, 0x0066ff, 0x0055ff, 0x0044ff, 0x0033ff, 0x0022ff, 0x0011ff, 0x0000ff }; +const uint32_t clu_red_black[] = { 0xff0000, 0xee0000, 0xdd0000, 0xcc0000, 0xbb0000, 0xaa0000, 0x990000, 0x880000, 0x770000, 0x660000, 0x550000, 0x440000, 0x330000, 0x220000, 0x080000, 0x000000 }; +const uint32_t clu_purple_yellow[] = { 0xff00ff, 0xff00dd, 0xff00bb, 0xff0099, 0xff0077, 0xff0055, 0xff0033, 0xff0011, 0xff1100, 0xff3300, 0xff5500, 0xff7700, 0xff9900, 0xffbb00, 0xffdd00, 0xffff00}; +const uint32_t clu_yellow_green[] = { 0xffff00, 0xeeff00, 0xddff00, 0xccff00, 0xbbff00, 0xaaff00, 0x99ff00, 0x88ff00, 0x77ff00, 0x66ff00, 0x55ff00, 0x44ff00, 0x33ff00, 0x22ff00, 0x11ff00, 0x00ff00 }; +const uint32_t clu_orange_green[] = { 0xff5300, 0xff6f00, 0xff8c00, 0xffa800, 0xffc500, 0xffe100, 0xfffe00, 0xe4ff00, 0xc7ff00, 0xabff00, 0x8eff00, 0x72ff00, 0x55ff00, 0x39ff00, 0x1cff00, 0x00ff00 }; +const uint32_t clu_guardians[] = {0xff00ff, 0xff22dd, 0xff44bb, 0xff6699, 0xff8877, 0xffaa55, 0xffcc33, 0xffee11, 0xeeff11, 0xccff33, 0xaaff55, 0x88ff77, 0x66ff99, 0x44ffbb, 0x22ffdd, 0x00ffff}; + +const uint32_t clu_c64[] = { 0x000000, 0xFFFFFF, 0xff0000, 0x00ffff, 0xff00ff, 0x00ff00, 0x0000ff, 0xffff00, 0xff8800, 0xff5588, 0xff8888, 0x555555, 0x888888, 0x88ff88, 0x8888ff, 0xb8b8b8 }; + +const uint32_t clu_testb[] = { 0xff0000, 0xff0000, 0xff0000, 0x00ff00, 0x00ff00, 0x00ff00, 0x0000ff, 0x0000ff, 0x0000ff, 0xff00ff, 0xff00ff, 0xff00ff, 0xffff00, 0xffff00, 0xffff00, 0x00ffff, 0x00ffff}; +const byte smoke[] = { 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11, 11, 11, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5 }; + + +uint32_t clu_main[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + +//const uint32_t clu_red_black[] ={ 0x010000, 0x020000, 0x030000, 0x040000, 0x050000, 0x060000, 0x070000, 0x080000, 0x090000, 0x0a0000, 0x0b0000, 0x0c0000, 0x0d0000, 0x0e0000, 0x0f0000, 0x100000 }; + +const uint32_t clu_test[] = { 0x00ffff, 0xff00ff, 0xff0000, 0x0ff00, 0x00ff00, 0x0000ff, 0x0000ff, 0xffff00 }; + +const byte quickSmoke[] = { 11, 10, 10, 10, 9, 9, 8, 8, 7, 6, 6, 6, 6, 6, 6, 6, 6, 8, 9, 10, 10, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 10, 9, 8, 8, 7, 7, 7, 7, 7, 7, 7, 6, 5, 4, 3, 2, 3, 3, 4, 5, 6, 6, 8, 10, 12, 12, 12, 12, 11, 10, 10, 9, 11, 10, 10, 10, 9, 9, 8, 8, 7, 6, 6, 6, 6, 6, 6, 6, 6, 8, 9, 10, 10}; +const byte p_cycle[] = { 0, 2, 6, 15, 15, 15, 15, 15, 15, 15, 15, 15, 0, 2, 6, 15, 15, 15, 15, 15, 15, 15, 15, 15}; +const byte spaced[] = { 0, 15, 0, 15, 0, 15, 0, 15, 0, 15, 0, 15 }; + +const byte ramp[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1 }; + +const byte triad[] = { 15, 0, 15, 15, 0, 15, 15, 15, 0, 15, 15, 15, 15, 0, 15, 0, 15, 15, 0, 15, 15, 15, 0, 15, 15, 15, 15, 0 }; + +byte weighed_list[100] = {}; + +const byte list[] = {4, 0, 1, 2, 3, 5, 6, 7, 8, 9, 10, 11}; +const float weight[] = {0.50, 0.25, 0.025, 0.025, 0.025, 0.025, 0.025, 0.025 , 0.025 , 0.025, 0.025, 0.025}; + + + +uint32_t buffer[(NUMPIXELS * 2) + 1]; +byte animbuffer[NUMPIXELS + 1]; + +uint32_t neobuffer[NEONUMPIXELS + 1]; +byte neoanimbuffer[NEONUMPIXELS + 1]; + +unsigned long previousMillis = 0; +unsigned long interval = 30000; + +unsigned int patternSelect = 4; + +void setup() { + +#if defined(__AVR_ATtiny85__) && (F_CPU == 16000000L) + clock_prescale_set(clock_div_1); // Enable 16 MHz on Trinket +#endif + + strip.begin(); // Initialize pins for output + strip.clear(); + strip.show(); // Turn all LEDs off ASAP + strip.setBrightness(1); + + + for ( int i = 0; i <= (NUMPIXELS * 2); i++) { + buffer[i] = 0; + } + + for ( int i = 0; i <= NUMPIXELS; i++) { + animbuffer[i] = 0; + } + + + for ( int i = 0; i <= NEONUMPIXELS; i++) { + neobuffer[i] = 0; + neoanimbuffer[i] = 0; + } + + copyPalette(random(10)); + generateWeightedList(); +} + +// Runs 10 LEDs at a time along strip, cycling through red, green and blue. +// This requires about 200 mA for all the 'on' pixels + 1 mA per 'off' pixel. + +uint32_t color = 0xFF00FF; // 'On' color (starts red) + +uint32_t red = 0xFF0000; +uint32_t green = 0x00FF00; +uint32_t blue = 0x0000FF; +int frame = 0; +int neoframe = 0; + +int mirror(int v) { + int m = (NUMPIXELS * 2) - 1 - v; + return m; +} + +int mv; + +void copyPalette(byte i) { + + switch (i) { + case 0: + memcpy( clu_main, clu_red_yellow, sizeof(clu_red_yellow) ); + break; + case 1: + memcpy( clu_main, clu_blue_red, sizeof(clu_red_yellow) ); + break; + + case 2: + memcpy( clu_main, clu_teal_purple, sizeof(clu_red_yellow) ); + break; + + case 3: + memcpy( clu_main, clu_red_orange, sizeof(clu_red_yellow) ); + break; + + case 4: + memcpy( clu_main, clu_teal_blue, sizeof(clu_red_yellow) ); + break; + + case 5: + memcpy( clu_main, clu_red_black, sizeof(clu_red_yellow) ); + break; + case 6: + memcpy( clu_main, clu_purple_yellow, sizeof(clu_red_yellow) ); + break; + + case 7: + memcpy( clu_main, clu_yellow_green, sizeof(clu_red_yellow) ); + break; + + case 8: + memcpy( clu_main, clu_orange_green, sizeof(clu_red_yellow) ); + break; + + case 9: + memcpy( clu_main, clu_guardians, sizeof(clu_red_yellow) ); + break; + + + default: + // if nothing else matches, do the default + // default is optional + memcpy( clu_main, clu_red_black, sizeof(clu_red_yellow) ); + break; + } + + previousMillis = millis(); + +} + +void generateWeightedList() { + + int counter = 0; + + + int l; + int multiples; + l = sizeof(list); + // Loop over weights + for (int i = 0; i < l; i++) { + multiples = weight[i] * 100; + + // Loop over the list of items + for (int j = 0; j < multiples; j++) { + weighed_list[counter] = list[i]; + counter++; + } + } + +} + + +void displayBuffer() { + Serial.println("---"); + strip.clear(); + for ( int i = 0; i <= (NUMPIXELS * 2); i++) { + strip.setPixelColor(i, buffer[i]); + // Serial.println(String(buffer[i], HEX)); + buffer[i] = 0; + } + strip.show(); +} + + + +void test() { + buffer[0] = red; + buffer[mirror(0)] = buffer[0]; +} + +void sparkle() { + for ( int i = 0; i < NUMPIXELS; i++) { + if (animbuffer[i] > 7) { + animbuffer[i] = animbuffer[i] - 8; + } else { + animbuffer[i] = 0; + } + } + if ((frame % 8) == 0) { + animbuffer[random(NUMPIXELS)] = 255; + } + Serial.println(">> Anim"); + for ( int i = 0; i < NUMPIXELS; i++) { + // Serial.println(animbuffer[i]); + if (animbuffer[i] != 0) { + animbuffer[i] = animbuffer[i] - 8; + buffer[i] = strip.Color(animbuffer[i], 0, 0); + buffer[mirror(i)] = buffer[i]; + } + } + + frame++; +} + + +void doSmoke() { + // copy a frame to the buffer. + Serial.println(">> Smoke"); + for ( int i = 0; i < NUMPIXELS; i++) { + Serial.println(smoke[frame + i]); + buffer[i] = strip.Color(smoke[frame + i] * 16, 0, 0); + buffer[mirror(i)] = buffer[i]; + } + + frame++; + if (frame > (512) ) { + frame = 0; + } +} + + +void doQuickSmoke() { + // copy a frame to the buffer. + Serial.println(">> Smoke"); + byte v; + for ( int i = 0; i < NUMPIXELS; i++) { + v = quickSmoke[frame + i]; + Serial.println(v); + buffer[i] = clu_main[v]; + buffer[mirror(i)] = buffer[i]; + } + + frame++; + if (frame > (64) ) { + frame = 0; + } +} + +void doPattern(bool masked) { + Serial.println(">> Pattern"); + byte v; + uint32_t c; + for ( int i = 0; i < NUMPIXELS; i++) { + v = p_cycle[frame + i]; + Serial.println(v); + + c = clu_main[v]; + if (masked == true) { + if ((i + 1) % 2 == 0) { + c = 0; + } + } + + buffer[i] = c; + buffer[mirror(i)] = buffer[i]; + + + } + + frame++; + if (frame > (11) ) { + frame = 0; + } +} + +void doRamp(bool masked) { + Serial.println(">> Pattern"); + byte v; + uint32_t c; + for ( int i = 0; i < NUMPIXELS; i++) { + v = ramp[i]; + Serial.println(v); + + c = clu_testb[v]; + if (masked == true) { + if ((i + 1) % 2 == 0) { + c = 0; + } + } + + buffer[i] = c; + buffer[mirror(i)] = buffer[i]; + + + } + +} + + +void doLoading(bool masked) { + Serial.println(">> Pattern"); + byte v; + uint32_t c; + for ( int i = 0; i < NUMPIXELS; i++) { + v = random(16); + Serial.println(v); + + c = clu_c64[v]; + if (masked == true) { + if ((i + 1) % 2 == 0) { + c = 0; + } + } + + buffer[i] = c; + buffer[mirror(i)] = buffer[i]; + + + } + +} + +void doTriad(bool masked) { + Serial.println(">> Triad"); + byte v; + uint32_t c; + for ( int i = 0; i < NUMPIXELS; i++) { + v = triad[frame + i]; + Serial.println(v); + + c = clu_main[v]; + if (masked == true) { + if ((i + 1) % 2 == 0) { + c = 0; + } + } + + buffer[i] = c; + buffer[mirror(i)] = buffer[i]; + + + } + + frame++; + if (frame > (13) ) { + frame = 0; + } + +} + +void doSpaced() { + + Serial.println(">> Spaced"); + byte v; + for ( int i = 0; i < NUMPIXELS; i++) { + v = spaced[i]; + // Serial.println(v); + buffer[i] = clu_red_black[v]; + buffer[mirror(i)] = buffer[i]; + } + +} + +void doSine(bool masked) { + Serial.println(">> Sine"); + int p; + byte v; + uint32_t c; + for ( int i = 0; i < NUMPIXELS; i++) { + p = frame + i; + if (p > 255) { + p = p - 255; + } + // Serial.println(p); + v = strip.sine8(p); + c = strip.Color(v, 0, 0); + + if (masked == true) { + if ((i + 1) % 2 == 0) { + c = 0; + } + } + buffer[i] = c; + buffer[mirror(i)] = buffer[i]; + } + + frame++; + if (frame > (255) ) { + frame = 0; + } +} + +void doSineBlock(bool masked) { + Serial.println(">> Sine Block"); + int p; + byte v; + uint32_t c; + for ( int i = 0; i < NUMPIXELS; i++) { + p = frame + i; + if (p > 255) { + p = p - 255; + } + + v = strip.sine8(p); + if (v == 0) { + v = 1; + } + + v = v / 16; + // Serial.println(v); + c = clu_main[v]; + + + if (masked == true) { + if ((i + 1) % 2 == 0) { + c = 0; + } + } + buffer[i] = c; + buffer[mirror(i)] = buffer[i]; + } + + frame++; + if (frame > (255) ) { + frame = 0; + } +} + + +void doNeoPattern() { + Serial.println(">> NeoPattern"); + byte v; + for ( int i = 0; i < NEONUMPIXELS; i++) { + v = p_cycle[neoframe + i]; + neobuffer[i] = clu_red_yellow[v]; + } + + neoframe++; + if (neoframe > (7) ) { + neoframe = 0; + } +} + + +void doNeoSolid() { + Serial.println(">> NeoSolid"); + int l; + byte v; + l = sizeof(clu_red_yellow) / sizeof(uint32_t); + for ( int i = 0; i < NEONUMPIXELS; i++) { + neobuffer[i] = clu_red_yellow[random(l)]; + // Serial.println(neobuffer[i]); + } + + Serial.println(l); +} + +void showPallete() { + Serial.println(">> Pallete"); + //Serial.println("sizeof"); + // Serial.println(sizeof(clu_red_black) ); + uint32_t v; + for ( int i = 0; i <= 15; i++) { + Serial.println(i); + v = clu_red_black[i]; + + Serial.println(v); + buffer[i] = v; + } + buffer[16] = 0x666666; +} + +void loop() { + strip.setBrightness(32); + + patternSelect = 8; + + switch (patternSelect) { + case 0: + sparkle(); + break; + case 1: + doQuickSmoke(); + break; + + case 2: + doPattern(false); + break; + + case 3: + doPattern(true); + break; + + case 4: + doSpaced(); + break; + + case 5: + doSine(true); + break; + case 6: + doSine(false); + break; + + case 7: + doSineBlock(true); + break; + case 8: + doSineBlock(false); + break; + case 9: + doTriad(false); + break; + case 10: + doLoading(false); + break; + case 11: + doLoading(true); + break; + default: + // if nothing else matches, do the default + // default is optional + doSine(true); + break; + } + doNeoSolid(); + displayBuffer(); + // displayNeoBuffer(); + + unsigned long currentMillis = millis(); // grab current time + + if (patternSelect == 0) { + interval = 30000; + } else { + interval = 75000; + } + // check if "interval" time has passed (1000 milliseconds) + if ((unsigned long)(currentMillis - previousMillis) >= interval) { + patternSelect = weighed_list[random(100)]; + + Serial.print("patternSelect"); + Serial.println(patternSelect); + copyPalette(random(10)); + } +Serial.print("patternSelect"); + Serial.println(patternSelect); + + delay(40); // Pause 20 milliseconds (~50 FPS) + + +} \ No newline at end of file