/ Glowing_Beehive_Hairdo_Wig / Glowing_Beehive_Hairdo_Wig.ino
Glowing_Beehive_Hairdo_Wig.ino
  1  // SPDX-FileCopyrightText: 2017 Erin St Blaine for Adafruit Industries
  2  //
  3  // SPDX-License-Identifier: MIT
  4  
  5  // Code by Erin St Blaine for Adafruit.com
  6  // Full tutorial at https://learn.adafruit.com/glowing-beehive-hairdo-wig/
  7  
  8  #include <Adafruit_CircuitPlayground.h>  // add this before the FastLED library to avoid issues
  9  #include <FastLED.h>
 10  
 11  #define LED_PIN     6    //led strand is soldered to pin 6
 12  #define CP_PIN      17   //circuit playground's neopixels live on pin 17
 13  #define NUM_LEDS    12   // number of LEDs in my strand
 14  #define NUM_CP      10   // number of neopixels on the circuit playground
 15  
 16  // I have purposefully mixed up the color order on the Neopixel strip to 
 17  // quickly and easily add color variety to this code -- This way the strip will always
 18  // show a different color from the light pipe. 
 19  
 20  #define COLOR_ORDER GRB   
 21  #define COLOR_ORDER_STRIP  RBG 
 22  
 23  uint8_t brightness = 255;  //led strand brightness control
 24  uint8_t cpbrightness = 255;  //circuit playground brightness control
 25  
 26  int STEPS = 25;  //makes the rainbow colors more or less spread out
 27  int NUM_MODES = 4;  // change this number if you add or subtract modes
 28  
 29  CRGB leds[NUM_LEDS];  //I've set up different arrays for the neopixel strand and the circuit playground
 30  CRGB cp[NUM_CP];      // so that we can control the brightness separately
 31  
 32  CRGBPalette16 currentPalette;
 33  TBlendType    currentBlending;
 34  
 35  int ledMode = 0;       //Initial mode 
 36  bool leftButtonPressed;
 37  bool rightButtonPressed;
 38  
 39  // SOUND REACTIVE SETUP --------------
 40  
 41  #define MIC_PIN         A4                                    // Analog port for microphone
 42  #define DC_OFFSET  0                                          // DC offset in mic signal - if unusure, leave 0
 43                                                                // I calculated this value by serialprintln lots of mic values
 44  #define NOISE     200                                         // Noise/hum/interference in mic signal and increased value until it went quiet
 45  #define SAMPLES   60                                          // Length of buffer for dynamic level adjustment
 46  #define TOP (NUM_LEDS + 2)                                    // Allow dot to go slightly off scale
 47  #define PEAK_FALL 10                                          // Rate of peak falling dot
 48   
 49  byte
 50    peak      = 0,                                              // Used for falling dot
 51    dotCount  = 0,                                              // Frame counter for delaying dot-falling speed
 52    volCount  = 0;                                              // Frame counter for storing past volume data
 53  int
 54    vol[SAMPLES],                                               // Collection of prior volume samples
 55    lvl       = 10,                                             // Current audio level, change this number to adjust sensitivity
 56    minLvlAvg = 0,                                              // For dynamic adjustment of graph low & high
 57    maxLvlAvg = 512;
 58  
 59  
 60  // SETUP -------------------
 61  
 62  void setup() {
 63    Serial.begin(57600);
 64    CircuitPlayground.begin();
 65    FastLED.addLeds<WS2812B, LED_PIN, COLOR_ORDER_STRIP>(leds, NUM_LEDS).setCorrection( TypicalLEDStrip );
 66    FastLED.addLeds<WS2812B, CP_PIN, COLOR_ORDER>(cp, 10).setCorrection( TypicalLEDStrip );
 67    currentBlending = LINEARBLEND;
 68    set_max_power_in_volts_and_milliamps(5, 500);               // FastLED 2.1 Power management set at 5V, 500mA
 69  
 70    
 71  }
 72  
 73  void loop()  {
 74  
 75    leftButtonPressed = CircuitPlayground.leftButton();
 76    rightButtonPressed = CircuitPlayground.rightButton();
 77  
 78    if (leftButtonPressed) {  //left button cycles through modes
 79      clearpixels(); 
 80      ledMode=ledMode+1;
 81      delay(300);
 82      if (ledMode > NUM_MODES){
 83      ledMode=0;
 84       }
 85    }
 86      if (rightButtonPressed) {   // right button turns all leds off
 87      ledMode=99;
 88    
 89      }
 90    
 91   switch (ledMode) {
 92         case 0: currentPalette = RainbowColors_p; rainbow(); break; 
 93         case 1: soundreactive(); break; 
 94         case 2: currentPalette = OceanColors_p; rainbow(); break;                    
 95         case 3: currentPalette = LavaColors_p; rainbow(); break; 
 96         case 4: currentPalette = RainbowStripeColors_p; rainbow(); break;    
 97         case 99: clearpixels(); break;
 98         
 99  }
100  }
101  
102  void clearpixels()
103  {
104    CircuitPlayground.clearPixels();
105    for (int i = 0; i < NUM_LEDS; i++) leds[i] = CRGB::Black; 
106    for (int i = 0; i < NUM_CP; i++) cp[i] = CRGB::Black;
107    FastLED.show();
108  }
109  
110  void rainbow()
111  {
112    
113    static uint8_t startIndex = 0;
114    startIndex = startIndex + 1; /* motion speed */
115  
116    FillLEDsFromPaletteColors( startIndex);
117  
118    FastLED.show();
119    FastLED.delay(20);}
120  
121  //this bit is in every palette mode, needs to be in there just once
122  void FillLEDsFromPaletteColors( uint8_t colorIndex)
123  { 
124    for (int i = 0; i < NUM_LEDS; i++) leds[i] = ColorFromPalette( currentPalette, colorIndex, brightness, currentBlending);
125    for (int i = 0; i < NUM_CP; i++) cp[i] = ColorFromPalette( currentPalette, colorIndex, cpbrightness, currentBlending);
126      colorIndex += 25;
127    
128  }
129  
130  
131  void soundreactive() {
132  
133    uint8_t  i;
134    uint16_t minLvl, maxLvl;
135    int      n, height;
136     
137    n = analogRead(MIC_PIN);                                    // Raw reading from mic
138    n = abs(n - 512 - DC_OFFSET);                               // Center on zero
139    
140    n = (n <= NOISE) ? 0 : (n - NOISE);                         // Remove noise/hum
141    lvl = ((lvl * 7) + n) >> 3;                                 // "Dampened" reading (else looks twitchy)
142   
143    // Calculate bar height based on dynamic min/max levels (fixed point):
144    height = TOP * (lvl - minLvlAvg) / (long)(maxLvlAvg - minLvlAvg);
145   
146    if (height < 0L)       height = 0;                          // Clip output
147    else if (height > TOP) height = TOP;
148    if (height > peak)     peak   = height;                     // Keep 'peak' dot at top
149   
150   
151    // Color pixels based on rainbow gradient -- led strand
152    for (i=0; i<NUM_LEDS; i++) {
153      if (i >= height)   leds[i].setRGB( 0, 0,0);
154      else leds[i] = CHSV(map(i,0,NUM_LEDS-1,0,255), 255, brightness);  //constrain colors here by changing HSV values
155    }
156   
157    // Draw peak dot  -- led strand
158    if (peak > 0 && peak <= NUM_LEDS-1) leds[peak] = CHSV(map(peak,0,NUM_LEDS-1,0,255), 255, brightness);
159  
160    // Color pixels based on rainbow gradient  -- circuit playground
161    for (i=0; i<NUM_CP; i++) {
162      if (i >= height)   cp[i].setRGB( 0, 0,0);
163      else cp[i] = CHSV(map(i,0,NUM_CP-1,0,255), 255, cpbrightness);  //constrain colors here by changing HSV values
164    }
165   
166    // Draw peak dot  -- circuit playground
167    if (peak > 0 && peak <= NUM_CP-1) cp[peak] = CHSV(map(peak,0,NUM_LEDS-1,0,255), 255, cpbrightness);
168  
169  // Every few frames, make the peak pixel drop by 1:
170   
171      if (++dotCount >= PEAK_FALL) {                            // fall rate 
172        if(peak > 0) peak--;
173        dotCount = 0;
174      }
175    
176    vol[volCount] = n;                                          // Save sample for dynamic leveling
177    if (++volCount >= SAMPLES) volCount = 0;                    // Advance/rollover sample counter
178   
179    // Get volume range of prior frames
180    minLvl = maxLvl = vol[0];
181    for (i=1; i<SAMPLES; i++) {
182      if (vol[i] < minLvl)      minLvl = vol[i];
183      else if (vol[i] > maxLvl) maxLvl = vol[i];
184    }
185    // minLvl and maxLvl indicate the volume range over prior frames, used
186    // for vertically scaling the output graph (so it looks interesting
187    // regardless of volume level).  If they're too close together though
188    // (e.g. at very low volume levels) the graph becomes super coarse
189    // and 'jumpy'...so keep some minimum distance between them (this
190    // also lets the graph go to zero when no sound is playing):
191    if((maxLvl - minLvl) < TOP) maxLvl = minLvl + TOP;
192    minLvlAvg = (minLvlAvg * 63 + minLvl) >> 6;                 // Dampen min/max levels
193    maxLvlAvg = (maxLvlAvg * 63 + maxLvl) >> 6;                 // (fake rolling average)
194  
195  
196  show_at_max_brightness_for_power();                         // Power managed FastLED display
197    Serial.println(LEDS.getFPS()); 
198  }