/ LED_Candles / LED_Candles.ino
LED_Candles.ino
1 // SPDX-FileCopyrightText: 2017 Mikey Sklar for Adafruit Industries 2 // 3 // SPDX-License-Identifier: MIT 4 5 #include <Adafruit_NeoPixel.h> 6 7 // The onboard red LED's pin 8 #define REDLED_PIN 1 9 // The data-in pin of the NeoPixel 10 #define WICK_PIN 0 11 // Any unconnected pin, to try to generate a random seed 12 #define UNCONNECTED_PIN 2 13 14 // The LED can be in only one of these states at any given time 15 #define BRIGHT 0 16 #define UP 1 17 #define DOWN 2 18 #define DIM 3 19 20 #define BRIGHT_HOLD 4 21 #define DIM_HOLD 5 22 23 // Percent chance the LED will suddenly fall to minimum brightness 24 #define INDEX_BOTTOM_PERCENT 10 25 // Absolute minimum red value (green value is a function of red's value) 26 #define INDEX_BOTTOM 128 27 // Minimum red value during "normal" flickering (not a dramatic change) 28 #define INDEX_MIN 192 29 // Maximum red value 30 #define INDEX_MAX 255 31 32 // Decreasing brightness will take place over a number of milliseconds in this range 33 #define DOWN_MIN_MSECS 20 34 #define DOWN_MAX_MSECS 250 35 // Increasing brightness will take place over a number of milliseconds in this range 36 #define UP_MIN_MSECS 20 37 #define UP_MAX_MSECS 250 38 // Percent chance the color will hold unchanged after brightening 39 #define BRIGHT_HOLD_PERCENT 20 40 // When holding after brightening, hold for a number of milliseconds in this range 41 #define BRIGHT_HOLD_MIN_MSECS 0 42 #define BRIGHT_HOLD_MAX_MSECS 100 43 // Percent chance the color will hold unchanged after dimming 44 #define DIM_HOLD_PERCENT 5 45 // When holding after dimming, hold for a number of milliseconds in this range 46 #define DIM_HOLD_MIN_MSECS 0 47 #define DIM_HOLD_MAX_MSECS 50 48 49 #define MINVAL(A,B) (((A) < (B)) ? (A) : (B)) 50 #define MAXVAL(A,B) (((A) > (B)) ? (A) : (B)) 51 52 Adafruit_NeoPixel *wick; 53 byte state; 54 unsigned long flicker_msecs; 55 unsigned long flicker_start; 56 byte index_start; 57 byte index_end; 58 59 void set_color(byte index) 60 { 61 index = MAXVAL(MINVAL(index, INDEX_MAX), INDEX_BOTTOM); 62 if (index >= INDEX_MIN) 63 wick->setPixelColor(0, index, (index * 3) / 8, 0); 64 else if (index < INDEX_MIN) 65 wick->setPixelColor(0, index, (index * 3.25) / 8, 0); 66 67 wick->show(); 68 return; 69 } 70 71 void setup() 72 { 73 // There is no good source of entropy to seed the random number generator, 74 // so we'll just read the analog value of an unconnected pin. This won't be 75 // very random either, but there's really nothing else we can do. 76 // 77 // True randomness isn't strictly necessary, we just don't want a whole 78 // string of these things to do exactly the same thing at the same time if 79 // they're all powered on simultaneously. 80 randomSeed(analogRead(UNCONNECTED_PIN)); 81 82 // Turn off the onboard red LED 83 pinMode(REDLED_PIN, OUTPUT); 84 digitalWrite(REDLED_PIN, LOW); 85 86 wick = new Adafruit_NeoPixel(1, WICK_PIN, NEO_RGB + NEO_KHZ800); 87 // wick = new Adafruit_NeoPixel(1, WICK_PIN); // for RGBW, if you see green uncomment this line 88 89 wick->begin(); 90 wick->show(); 91 92 set_color(255); 93 index_start = 255; 94 index_end = 255; 95 state = BRIGHT; 96 97 return; 98 } 99 100 void loop() 101 { 102 unsigned long current_time; 103 104 current_time = millis(); 105 106 switch (state) 107 { 108 case BRIGHT: 109 flicker_msecs = random(DOWN_MAX_MSECS - DOWN_MIN_MSECS) + DOWN_MIN_MSECS; 110 flicker_start = current_time; 111 index_start = index_end; 112 if ((index_start > INDEX_BOTTOM) && 113 (random(100) < INDEX_BOTTOM_PERCENT)) 114 index_end = random(index_start - INDEX_BOTTOM) + INDEX_BOTTOM; 115 else 116 index_end = random(index_start - INDEX_MIN) + INDEX_MIN; 117 118 state = DOWN; 119 break; 120 case DIM: 121 flicker_msecs = random(UP_MAX_MSECS - UP_MIN_MSECS) + UP_MIN_MSECS; 122 flicker_start = current_time; 123 index_start = index_end; 124 index_end = random(INDEX_MAX - index_start) + INDEX_MIN; 125 state = UP; 126 break; 127 case BRIGHT_HOLD: 128 case DIM_HOLD: 129 if (current_time >= (flicker_start + flicker_msecs)) 130 state = (state == BRIGHT_HOLD) ? BRIGHT : DIM; 131 132 break; 133 case UP: 134 case DOWN: 135 if (current_time < (flicker_start + flicker_msecs)) 136 set_color(index_start + ((index_end - index_start) * (((current_time - flicker_start) * 1.0) / flicker_msecs))); 137 else 138 { 139 set_color(index_end); 140 141 if (state == DOWN) 142 { 143 if (random(100) < DIM_HOLD_PERCENT) 144 { 145 flicker_start = current_time; 146 flicker_msecs = random(DIM_HOLD_MAX_MSECS - DIM_HOLD_MIN_MSECS) + DIM_HOLD_MIN_MSECS; 147 state = DIM_HOLD; 148 } 149 else 150 state = DIM; 151 } 152 else 153 { 154 if (random(100) < BRIGHT_HOLD_PERCENT) 155 { 156 flicker_start = current_time; 157 flicker_msecs = random(BRIGHT_HOLD_MAX_MSECS - BRIGHT_HOLD_MIN_MSECS) + BRIGHT_HOLD_MIN_MSECS; 158 state = BRIGHT_HOLD; 159 } 160 else 161 state = BRIGHT; 162 } 163 } 164 165 break; 166 } 167 168 return; 169 }