|
| 1 | +// Fiery demon horns (rawr!) for Adafruit Trinket/Gemma. |
| 2 | +// Adafruit invests time and resources providing this open source code, |
| 3 | +// please support Adafruit and open-source hardware by purchasing |
| 4 | +// products from Adafruit! |
| 5 | +#include <Adafruit_NeoPixel.h> |
| 6 | +#include <avr/power.h> |
| 7 | + |
| 8 | +#define N_HORNS 1 |
| 9 | +#define N_LEDS 30 // Per horn |
| 10 | +#define PIN 0 |
| 11 | +Adafruit_NeoPixel pixels = Adafruit_NeoPixel(N_HORNS * N_LEDS, PIN); |
| 12 | + |
| 13 | +// /\ -> Fire-like effect is the sum of multiple triangle |
| 14 | +// ____/ \____ waves in motion, with a 'warm' color map applied. |
| 15 | +#define N_WAVES 6 // Number of simultaneous waves (per horn) |
| 16 | +// Coordinate space for waves is 16x the pixel spacing, |
| 17 | +// allowing fixed-point math to be used instead of floats. |
| 18 | +struct { |
| 19 | + int16_t lower; // Lower bound of wave |
| 20 | + int16_t upper; // Upper bound of wave |
| 21 | + int16_t mid; // Midpoint (peak) ((lower+upper)/2) |
| 22 | + uint8_t vlower; // Velocity of lower bound |
| 23 | + uint8_t vupper; // Velocity of upper bound |
| 24 | + uint16_t intensity; // Brightness at peak |
| 25 | +} wave[N_HORNS][N_WAVES]; |
| 26 | +long fade; // Decreases brightness as wave moves |
| 27 | + |
| 28 | +// Gamma correction improves appearance of midrange colors |
| 29 | +uint8_t gamma[] PROGMEM = { |
| 30 | + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
| 31 | + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, |
| 32 | + 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, |
| 33 | + 2, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 5, 5, 5, |
| 34 | + 5, 6, 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 9, 9, 9, 10, |
| 35 | + 10, 10, 11, 11, 11, 12, 12, 13, 13, 13, 14, 14, 15, 15, 16, 16, |
| 36 | + 17, 17, 18, 18, 19, 19, 20, 20, 21, 21, 22, 22, 23, 24, 24, 25, |
| 37 | + 25, 26, 27, 27, 28, 29, 29, 30, 31, 32, 32, 33, 34, 35, 35, 36, |
| 38 | + 37, 38, 39, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 50, |
| 39 | + 51, 52, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 66, 67, 68, |
| 40 | + 69, 70, 72, 73, 74, 75, 77, 78, 79, 81, 82, 83, 85, 86, 87, 89, |
| 41 | + 90, 92, 93, 95, 96, 98, 99,101,102,104,105,107,109,110,112,114, |
| 42 | + 115,117,119,120,122,124,126,127,129,131,133,135,137,138,140,142, |
| 43 | + 144,146,148,150,152,154,156,158,160,162,164,167,169,171,173,175, |
| 44 | + 177,180,182,184,186,189,191,193,196,198,200,203,205,208,210,213, |
| 45 | + 215,218,220,223,225,228,231,233,236,239,241,244,247,249,252,255 }; |
| 46 | + |
| 47 | +static void random_wave(uint8_t h,uint8_t w) { // Randomize one wave struct |
| 48 | + wave[h][w].upper = -1; // Always start just below head of strip |
| 49 | + wave[h][w].lower = -16 * (3 + random(4)); // Lower end starts ~3-7 pixels back |
| 50 | + wave[h][w].mid = (wave[h][w].lower + wave[h][w].upper) / 2; |
| 51 | + wave[h][w].vlower = 3 + random(4); // Lower end moves at ~1/8 to 1/4 pixel/frame |
| 52 | + wave[h][w].vupper = wave[h][w].vlower + random(4); // Upper end moves a bit faster, spreading wave |
| 53 | + wave[h][w].intensity = 300 + random(600); |
| 54 | +} |
| 55 | + |
| 56 | +void setup() { |
| 57 | + uint8_t h, w; |
| 58 | + |
| 59 | + randomSeed(analogRead(1)); |
| 60 | + pixels.begin(); |
| 61 | + for(h=0; h<N_HORNS; h++) { |
| 62 | + for(w=0; w<N_WAVES; w++) random_wave(h, w); |
| 63 | + } |
| 64 | + fade = 234 + N_LEDS / 2; |
| 65 | + if(fade > 255) fade = 255; |
| 66 | + |
| 67 | + // A ~100 Hz timer interrupt on Timer/Counter1 makes everything run |
| 68 | + // at regular intervals, regardless of current amount of motion. |
| 69 | +#if F_CPU == 16000000L |
| 70 | + clock_prescale_set(clock_div_1); |
| 71 | + TCCR1 = _BV(PWM1A) | _BV(CS13) | _BV(CS11) | _BV(CS10); // 1:1024 prescale |
| 72 | + OCR1C = F_CPU / 1024 / 100 - 1; |
| 73 | +#else |
| 74 | + TCCR1 = _BV(PWM1A) | _BV(CS13) | _BV(CS11); // 1:512 prescale |
| 75 | + OCR1C = F_CPU / 512 / 100 - 1; |
| 76 | +#endif |
| 77 | + GTCCR = 0; // No PWM out |
| 78 | + TIMSK |= _BV(TOIE1); // Enable overflow interrupt |
| 79 | +} |
| 80 | + |
| 81 | +void loop() { } // Not used -- everything's in interrupt below |
| 82 | + |
| 83 | +ISR(TIMER1_OVF_vect) { |
| 84 | + uint8_t h, w, i, r, g, b; |
| 85 | + int16_t x; |
| 86 | + uint16_t sum; |
| 87 | + |
| 88 | + for(h=0; h<N_HORNS; h++) { // For each horn... |
| 89 | + for(x=7, i=0; i<N_LEDS; i++, x+=16) { // For each LED along horn... |
| 90 | + for(sum=w=0; w<N_WAVES; w++) { // For each wave of horn... |
| 91 | + if((x < wave[h][w].lower) || (x > wave[h][w].upper)) continue; // Out of range |
| 92 | + if(x <= wave[h][w].mid) { // Lower half of wave (ramping up to peak brightness) |
| 93 | + sum += wave[h][w].intensity * (x - wave[h][w].lower) / (wave[h][w].mid - wave[h][w].lower); |
| 94 | + } else { // Upper half of wave (ramping down from peak) |
| 95 | + sum += wave[h][w].intensity * (wave[h][w].upper - x) / (wave[h][w].upper - wave[h][w].mid); |
| 96 | + } |
| 97 | + } |
| 98 | + // Now the magnitude (sum) is remapped to color for the LEDs. |
| 99 | + // A blackbody palette is used - fades white-yellow-red-black. |
| 100 | + if(sum < 255) { // 0-254 = black to red-1 |
| 101 | + r = pgm_read_byte(&gamma[sum]); |
| 102 | + g = b = 0; |
| 103 | + } else if(sum < 510) { // 255-509 = red to yellow-1 |
| 104 | + r = 255; |
| 105 | + g = pgm_read_byte(&gamma[sum - 255]); |
| 106 | + b = 0; |
| 107 | + } else if(sum < 765) { // 510-764 = yellow to white-1 |
| 108 | + r = g = 255; |
| 109 | + b = pgm_read_byte(&gamma[sum - 510]); |
| 110 | + } else { // 765+ = white |
| 111 | + r = g = b = 255; |
| 112 | + } |
| 113 | + pixels.setPixelColor(h * N_LEDS + i, r, g, b); |
| 114 | + } |
| 115 | + |
| 116 | + for(w=0; w<N_WAVES; w++) { // Update wave positions for each horn |
| 117 | + wave[h][w].lower += wave[h][w].vlower; // Advance lower position |
| 118 | + if(wave[h][w].lower >= (N_LEDS * 16)) { // Off end of strip? |
| 119 | + random_wave(h, w); // Yes, 'reboot' wave |
| 120 | + } else { // No, adjust other values... |
| 121 | + wave[h][w].upper += wave[h][w].vupper; |
| 122 | + wave[h][w].mid = (wave[h][w].lower + wave[h][w].upper) / 2; |
| 123 | + wave[h][w].intensity = (wave[h][w].intensity * fade) / 256; // Dimmer |
| 124 | + } |
| 125 | + } |
| 126 | + } |
| 127 | + pixels.show(); |
| 128 | +} |
0 commit comments