/ NeoPixel_Cyber_Falls_Wig / NeoPixel_Cyber_Falls_Wig.ino
NeoPixel_Cyber_Falls_Wig.ino
1 // SPDX-FileCopyrightText: 2018 Mikey Sklar for Adafruit Industries 2 // 3 // SPDX-License-Identifier: MIT 4 5 // 'Cyber falls' sketch, adapted from code for Firewalker sneakers. 6 // Creates a fiery rain-like effect on multiple NeoPixel strips. 7 // Requires Adafruit Trinket and NeoPixel strips. Strip length is 8 // inherently limited by Trinket RAM and processing power; this is 9 // written for five 15-pixel strands, which are paired up per pin 10 // for ten 15-pixel strips total. 11 12 #include <Adafruit_NeoPixel.h> 13 #include <avr/power.h> 14 15 const uint8_t gamma[] PROGMEM = { // Gamma correction table for LED brightness 16 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 17 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 18 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 19 2, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 5, 5, 5, 20 5, 6, 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 9, 9, 9, 10, 21 10, 10, 11, 11, 11, 12, 12, 13, 13, 13, 14, 14, 15, 15, 16, 16, 22 17, 17, 18, 18, 19, 19, 20, 20, 21, 21, 22, 22, 23, 24, 24, 25, 23 25, 26, 27, 27, 28, 29, 29, 30, 31, 32, 32, 33, 34, 35, 35, 36, 24 37, 38, 39, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 50, 25 51, 52, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 66, 67, 68, 26 69, 70, 72, 73, 74, 75, 77, 78, 79, 81, 82, 83, 85, 86, 87, 89, 27 90, 92, 93, 95, 96, 98, 99,101,102,104,105,107,109,110,112,114, 28 115,117,119,120,122,124,126,127,129,131,133,135,137,138,140,142, 29 144,146,148,150,152,154,156,158,160,162,164,167,169,171,173,175, 30 177,180,182,184,186,189,191,193,196,198,200,203,205,208,210,213, 31 215,218,220,223,225,228,231,233,236,239,241,244,247,249,252,255 }; 32 33 #define STRIPLEN 15 // Length of LED strips 34 #define MAXDROPS 5 // Max concurrent 'raindrops' 35 #define N_STRIPS 5 // Connect strips to pins 0 to (N_STRIPS-1) 36 37 Adafruit_NeoPixel strip = Adafruit_NeoPixel(STRIPLEN, 0); 38 39 // Everything's declared volatile because state changes inside 40 // an interrupt routine. 41 volatile struct { 42 uint8_t strip; 43 int16_t pos; 44 uint8_t speed; 45 uint16_t brightness; 46 } drop[MAXDROPS]; 47 volatile uint8_t 48 nDrops = 0, // Number of 'active' raindrops 49 prevStrip = 255; // Last selected strip 50 volatile uint16_t 51 countdown = 0; // Time to next raindrop 52 53 void setup() { 54 55 // Set up Timer/Counter1 for ~30 Hz interrupt 56 #if F_CPU == 16000000L 57 clock_prescale_set(clock_div_1); 58 TCCR1 = _BV(PWM1A) |_BV(CS13) | _BV(CS12); // 1:2048 prescale 59 #else 60 TCCR1 = _BV(PWM1A) |_BV(CS13) | _BV(CS11) | _BV(CS10); // 1:1024 prescale 61 #endif 62 63 // Turn strips off ASAP (must follow clock_prescale_set) 64 strip.begin(); 65 for(uint8_t s=0; s<N_STRIPS; s++) { 66 strip.setPin(s); 67 strip.show(); 68 } 69 70 // Finish timer/counter setup 71 OCR1C = 255; // ~30 Hz 72 GTCCR = 0; // No PWM out 73 TIMSK |= _BV(TOIE1); // Enable overflow interrupt 74 } 75 76 void loop() { } // Not used -- everything's in interrupt below 77 78 // A timer interrupt is used so that everything runs at regular 79 // intervals, regardless of current amount of motion. 80 ISR(TIMER1_OVF_vect) { 81 82 uint16_t mag[STRIPLEN]; 83 uint8_t s, d, p, r, g, b; 84 int mx1, m, level; 85 86 if(countdown == 0) { // Time to launch new drop? 87 if(nDrops < MAXDROPS-1) { // Is there space for one in the list? 88 do { 89 s = random(N_STRIPS); 90 } while(s == prevStrip); // Don't repeat prior strip 91 drop[nDrops].strip = prevStrip = s; 92 drop[nDrops].pos = -32; // Start off top of strip 93 drop[nDrops].speed = 1 + random(3); 94 drop[nDrops].brightness = 250 + random(520); 95 nDrops++; 96 countdown = 9 + random(28); // Time to launch next one 97 } 98 } else countdown--; 99 100 101 for(s=0; s<N_STRIPS; s++) { // For each strip... 102 memset(mag, 0, sizeof(mag)); // Clear magnitude table 103 104 // Render active drops for this strip into magnitude table 105 for(d=0; d<nDrops; d++) { 106 if(drop[d].strip == s) { 107 for(p=0; p<STRIPLEN; p++) { // For each pixel... 108 mx1 = (p << 2) - drop[d].pos; // Position of LED along wave 109 if((mx1 <= 0) || (mx1 >= 32)) continue; // Out of range 110 if(mx1 > 24) { // Rising edge of wave; ramp up fast (2 px) 111 m = ((long)drop[d].brightness * (long)(32 - mx1)) >> 4; 112 } else { // Falling edge of wave; fade slow (6 px) 113 m = ((long)drop[d].brightness * (long)mx1) / 24; 114 } 115 mag[p] += m; // Accumulate result in magnitude buffer 116 } 117 } 118 } 119 120 // Remap magnitude table to RGB for strand 121 for(p=0; p<STRIPLEN; p++) { // For each pixel in strip 122 level = mag[p]; // Pixel magnitude (brightness) 123 if(level < 255) { // 0-254 = black to green-1 124 r = b = 0; 125 g = pgm_read_byte(&gamma[level]); 126 } else if(level < 510) { // 255-509 = green to yellow-1 127 r = pgm_read_byte(&gamma[level - 255]); 128 g = 255; 129 b = 0; 130 } else if(level < 765) { // 510-764 = yellow to white-1 131 r = g = 255; 132 b = pgm_read_byte(&gamma[level - 510]); 133 } else { // 765+ = white 134 r = g = b = 255; 135 } 136 strip.setPixelColor(p, r, g, b); 137 } 138 139 strip.setPin(s); // Select output pin 140 strip.show(); // Strips don't need to refresh in sync 141 } 142 143 // Move 'active' drops 144 for(d=0; d<nDrops; d++) { 145 drop[d].pos += drop[d].speed; 146 if(drop[d].pos >= (STRIPLEN * 4)) { // Off end? 147 // Remove drop from list (move last one to this place) 148 memcpy((void *)&drop[d], (void *)&drop[nDrops-1], sizeof(drop[0])); 149 nDrops--; 150 } 151 } 152 }