/ 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    }