/ Jack_O_LED_trix / Jack_O_LED_trix.ino
Jack_O_LED_trix.ino
  1  // SPDX-FileCopyrightText: 2018 Anne Barela for Adafruit Industries
  2  //
  3  // SPDX-License-Identifier: MIT
  4  
  5  /*  This is the FastLED library NoisePlusPalette example code incorporating a momentary 
  6      push button to cycle between modes for the Jack-o-LED-trix project. 
  7      
  8      You should wire a momentary push button to connect from ground to a digital IO pin. 
  9      In this example, wire the button to ground and pin 11.
 10      
 11      FastLED has a number of built in "palettes" to choose from: 
 12        RainbowColors_p       is all the colors of the rainbow
 13        PartyColors_p         is all the colors of the rainbow minus greens
 14        RainbowStripeColors_p is all the colors of the rainbow divided into stripes
 15        HeatColors_p          is reds and yellows, white, and black
 16        LavaColors_p          is more reds and orangey colors
 17        ForestColors_p        is greens and yellows
 18        OceanColors_p         is lots of blues and aqua colors
 19        CloudColors_p         is blues and white
 20      FastLED also provides a number of easy ways to create you own personalized palettes,
 21      see the "HalloweenPalette_p" to personalize your own colors.
 22        
 23      There are also ways to create other palettes using functions, 
 24      see "SetupCandyCornPalette()" & "SetupBlackAndWhiteStripedPalette()" to personalize 
 25      your own colors.  
 26  */
 27  
 28  #include <FastLED.h>
 29  #include "colorutils.h"
 30  #include "colorpalettes.h"
 31  
 32  #define BRIGHTNESS  128 
 33  
 34  //Define the type of pixels you are using on the next line here. 
 35  //If you are using a strand or two of WS2801 pixels from Adafruit, update the chipset type to WS2801.
 36  #define LED_TYPE       WS2811
 37  
 38  //You may need to adjust your color order to "GRB" instead of "RGB" below. 
 39  #define COLOR_ORDER   RGB
 40  
 41  //If you are using 4-pin LEDs, you will need to uncomment and define the CLOCK_PIN 
 42  #define DATA_PIN      6
 43  //#define CLOCK_PIN   #
 44  
 45  #define BUTTON_PIN   11   // button is connected to pin 11 and GND
 46  #define UPDATES_PER_SECOND 100
 47  
 48  //This sketch is set up for a matrix of 8 pixels wide and 6 pixels high. Update to match your matrix.
 49  const uint8_t kMatrixWidth  = 8;
 50  const uint8_t kMatrixHeight = 6;
 51  const bool    kMatrixSerpentineLayout = true;
 52  
 53  #define NUM_LEDS (kMatrixWidth * kMatrixHeight)   //no need to define the number of LEDS - here's the math!
 54  #define MAX_DIMENSION ((kMatrixWidth>kMatrixHeight) ? kMatrixWidth : kMatrixHeight)
 55  
 56  // The leds
 57  CRGB leds[kMatrixWidth * kMatrixHeight];
 58  
 59  uint8_t gHue = 0; // rotating "base color" used by many of the patterns
 60  int ledMode = 0;
 61  
 62  // The 16 bit version of our coordinates
 63  static uint16_t x;
 64  static uint16_t y;
 65  static uint16_t z;
 66  
 67  // We're using the x/y dimensions to map to the x/y pixels on the matrix.  We'll
 68  // use the z-axis for "time".  speed determines how fast time moves forward.  Try
 69  // 1 for a very slow moving effect, or 60 for something that ends up looking like
 70  // water.
 71  uint16_t speed = 10; // speed is set dynamically once we've started up
 72  
 73  // Scale determines how far apart the pixels in our noise matrix are.  Try
 74  // changing these values around to see how it affects the motion of the display.  The
 75  // higher the value of scale, the more "zoomed out" the noise will be.  A value
 76  // of 1 will be so zoomed in, you'll mostly see solid colors.
 77  uint16_t scale = 50; // scale is set dynamically once we've started up
 78  
 79  // This is the array that we keep our computed noise values in
 80  uint8_t noise[MAX_DIMENSION][MAX_DIMENSION];
 81  
 82  CRGBPalette16 currentPalette( RainbowColors_p );
 83  uint8_t       colorLoop = 0;
 84  
 85  TBlendType    currentBlending;
 86  
 87  const TProgmemPalette16 HalloweenPalette_p PROGMEM =
 88  {
 89    CRGB:: OrangeRed,
 90    CRGB:: Gold,
 91    CRGB:: Purple,
 92    CRGB:: Gold,
 93    
 94    CRGB:: OrangeRed,
 95    CRGB:: Gold,
 96    CRGB:: Purple,
 97    CRGB:: OrangeRed,
 98    
 99    CRGB:: Gold,    
100    CRGB:: Purple,
101    CRGB:: Gold,
102    CRGB:: OrangeRed,
103    
104    CRGB:: Gold,
105    CRGB:: Purple,
106    CRGB:: Gold,
107    CRGB:: OrangeRed,
108  };
109  
110  
111  unsigned long keyPrevMillis = 0;
112  const unsigned long keySampleIntervalMs = 25;
113  byte longKeyPressCountMax = 80;    // 80 * 25 = 2000 ms
114  byte longKeyPressCount = 0;
115  
116  byte prevKeyState = HIGH;         // button is active low
117  
118  void setup() {
119    delay(3000);
120  
121  //If you are using 3-pin LEDs, uncomment the next line:
122    FastLED.addLeds<LED_TYPE, DATA_PIN, COLOR_ORDER>(leds, NUM_LEDS).setCorrection(TypicalLEDStrip);
123    
124  //If you are using 4-pin LEDs, uncomment the next line:
125    //FastLED.addLeds<LED_TYPE, DATA_PIN, CLOCK_PIN, COLOR_ORDER>(leds, NUM_LEDS).setCorrection(TypicalLEDStrip);
126  
127    FastLED.setBrightness(BRIGHTNESS);
128    currentBlending;
129    pinMode(BUTTON_PIN, INPUT_PULLUP);
130  
131    // Initialize our coordinates to some random values
132    x = random16();
133    y = random16();
134    z = random16();
135  }
136  
137  // Fill the x/y array of 8-bit noise values using the inoise8 function.
138  void fillnoise8() {
139    // If we're runing at a low "speed", some 8-bit artifacts become visible
140    // from frame-to-frame.  In order to reduce this, we can do some fast data-smoothing.
141    // The amount of data smoothing we're doing depends on "speed".
142    uint8_t dataSmoothing = 0;
143    if( speed < 50) {
144      dataSmoothing = 200 - (speed * 4);
145    }
146    
147    for(int i = 0; i < MAX_DIMENSION; i++) {
148      int ioffset = scale * i;
149      for(int j = 0; j < MAX_DIMENSION; j++) {
150        int joffset = scale * j;
151        
152        uint8_t data = inoise8(x + ioffset,y + joffset,z);
153  
154        // The range of the inoise8 function is roughly 16-238.
155        // These two operations expand those values out to roughly 0..255
156        // You can comment them out if you want the raw noise data.
157        data = qsub8(data,16);
158        data = qadd8(data,scale8(data,39));
159  
160        if( dataSmoothing ) {
161          uint8_t olddata = noise[i][j];
162          uint8_t newdata = scale8( olddata, dataSmoothing) + scale8( data, 256 - dataSmoothing);
163          data = newdata;
164        }
165        
166        noise[i][j] = data;
167      }
168    }
169    
170    z += speed;
171    
172    // apply slow drift to X and Y, just for visual variation.
173    x += speed / 8;
174    y -= speed / 16;
175  }
176  
177  void mapNoiseToLEDsUsingPalette()
178  {
179    static uint8_t ihue=0;
180    
181    for(int i = 0; i < kMatrixWidth; i++) {
182      for(int j = 0; j < kMatrixHeight; j++) {
183        // We use the value at the (i,j) coordinate in the noise
184        // array for our brightness, and the flipped value from (j,i)
185        // for our pixel's index into the color palette.
186  
187        uint8_t index = noise[j][i];
188        uint8_t bri =   noise[i][j];
189  
190        // if this palette is a 'loop', add a slowly-changing base value
191        if( colorLoop) { 
192          index += ihue;
193        }
194  
195        // brighten up, as the color palette itself often contains the 
196        // light/dark dynamic range desired
197        if( bri > 127 ) {
198          bri = 255;
199        } else {
200          bri = dim8_raw( bri * 2);
201        }
202  
203        CRGB color = ColorFromPalette( currentPalette, index, bri);
204        leds[XY(i,j)] = color;
205      }
206    }
207    
208    ihue+=1;
209  }
210  
211  void loop() {  
212    
213    byte currKeyState = digitalRead(BUTTON_PIN);
214  
215    if ((prevKeyState == LOW) && (currKeyState == HIGH)) {
216      shortKeyPress();
217    }
218    prevKeyState = currKeyState;
219  
220    static uint8_t startIndex = 0;
221    startIndex = startIndex + 1; /* motion speed */
222  
223    // generate noise data
224    fillnoise8();
225    
226    //The group of colors in a palette are sent through a strip of LEDS in speed and step increments youve chosen
227    //You can change the SPEED and STEPS to make things look exactly how you want
228    //SPEED refers to how fast the colors move.  
229    //Try 1 for a very slow moving effect, or 60 for something that ends up looking like water.
230    //SCALE refers to how zoomed in we are.  
231    //Try changing these values around to see how it affects the motion of the display.  
232    //The higher the value of scale, the more "zoomed out" the noise will be.  
233    //A value of 1 will be so zoomed in, you'll mostly see solid colors.
234    
235    // convert the noise data to colors in the LED array using the current palette
236    mapNoiseToLEDsUsingPalette();
237  
238    switch (ledMode)  {
239    case 0:
240    {currentPalette = RainbowColors_p;        speed = 25; scale = 25; colorLoop = 0; }
241    break;
242    
243    case 1: 
244    {currentPalette = HeatColors_p;           speed = 20; scale = 50; colorLoop = 2; }
245    break;
246    
247    case 2:
248    {SetupBlackAndWhiteStripedPalette();      speed = 20; scale = 100; colorLoop = 1; }
249    break;
250    
251    case 3: 
252    {currentPalette = LavaColors_p;           speed = 10; scale = 25; colorLoop = 0; }  
253    break;
254    
255    case 4:
256    {currentPalette = HalloweenPalette_p;     speed = 20; scale = 20; colorLoop = 1; }
257    break;
258    
259    case 5: 
260    {SetupCandyCornPalette();                 speed = 15; scale = 30; colorLoop = 1; }
261    break;
262    } 
263  
264    //FillLEDsFromPaletteColors( startIndex);
265    LEDS.show();
266    delay(1000/speed);
267  }
268  
269  void shortKeyPress() {
270    ledMode++;
271    if (ledMode > 5) {
272      ledMode=0; 
273    }  
274  }
275  
276  // This function sets up a palette of black and white stripes,
277  // using code.  Since the palette is effectively an array of
278  // sixteen CRGB colors, the various fill_* functions can be used
279  // to set them up.
280  void SetupBlackAndWhiteStripedPalette()
281  {
282    // 'black out' all 16 palette entries...
283    fill_solid( currentPalette, 16, CRGB::Black);
284    // and set every fourth one to White or Gray.
285    currentPalette[0] = CRGB::White;
286    currentPalette[4] = CRGB::Gray;
287    currentPalette[8] = CRGB::White;
288    currentPalette[12] = CRGB::Gray;
289  
290  }
291  
292  // This function sets up a palette of candy corn colors
293  void SetupCandyCornPalette()
294  {
295      fill_solid( currentPalette, 16, CRGB::Black);
296      // set half of the LEDs to the colors selected here. This palette incorporates a lot of black
297      currentPalette[0] = CRGB::Orange;
298      currentPalette[1] = CRGB::Yellow;
299      currentPalette[2] = CRGB::Red;
300      currentPalette[3] = CRGB::OrangeRed;
301  
302      currentPalette[8] = CRGB::Yellow;
303      currentPalette[9] = CRGB::OrangeRed;
304      currentPalette[10] = CRGB::Orange;
305      currentPalette[11] = CRGB::Red;
306  
307  }
308  
309  // Mark's xy coordinate mapping code.  See the XYMatrix for more information on it.
310  //
311  uint16_t XY( uint8_t x, uint8_t y)
312  {
313    uint16_t i;
314    if( kMatrixSerpentineLayout == false) {
315      i = (y * kMatrixWidth) + x;
316    }
317    if( kMatrixSerpentineLayout == true) {
318      if( y & 0x01) {
319        // Odd rows run backwards
320        uint8_t reverseX = (kMatrixWidth - 1) - x;
321        i = (y * kMatrixWidth) + reverseX;
322      } else {
323        // Even rows run forwards
324        i = (y * kMatrixWidth) + x;
325      }
326    }
327    return i;
328  }