/ 3D_Printed_LED_Microphone_Flag / 3D_Printed_LED_Microphone_Flag.ino
3D_Printed_LED_Microphone_Flag.ino
  1  // SPDX-FileCopyrightText: 2013 Phil Burgess for Adafruit Industries
  2  //
  3  // SPDX-License-Identifier: BSD-3-Clause
  4  /*
  5  LED VU meter for Arduino and Adafruit NeoPixel LEDs.
  6   
  7   Hardware requirements:
  8   - Most Arduino or Arduino-compatible boards (ATmega 328P or better).
  9   - Adafruit Electret Microphone Amplifier (ID: 1063)
 10   - Adafruit Flora RGB Smart Pixels (ID: 1260)
 11   OR
 12   - Adafruit NeoPixel Digital LED strip (ID: 1138)
 13   - Optional: battery for portable use (else power through USB or adapter)
 14   Software requirements:
 15   - Adafruit NeoPixel library
 16   
 17   Connections:
 18   - 3.3V to mic amp +
 19   - GND to mic amp -
 20   - Analog pin to microphone output (configurable below)
 21   - Digital pin to LED data input (configurable below)
 22   See notes in setup() regarding 5V vs. 3.3V boards - there may be an
 23   extra connection to make and one line of code to enable or disable.
 24   
 25   Written by Adafruit Industries.  Distributed under the BSD license.
 26   This paragraph must be included in any redistribution.
 27   
 28   fscale function:
 29   Floating Point Autoscale Function V0.1
 30   Written by Paul Badger 2007
 31   Modified from code by Greg Shakar
 32   
 33   */
 34  
 35  #include <Adafruit_NeoPixel.h>
 36  #include <math.h>
 37  
 38  #define N_PIXELS  16  // Number of pixels in strand
 39  #define MIC_PIN   A1  // Microphone is attached to this analog pin
 40  #define LED_PIN    1  // NeoPixel LED strand is connected to this pin
 41  #define SAMPLE_WINDOW   10  // Sample window for average level
 42  #define PEAK_HANG 24 //Time of pause before peak dot falls
 43  #define PEAK_FALL 4 //Rate of falling peak dot
 44  #define INPUT_FLOOR 10 //Lower range of analogRead input
 45  #define INPUT_CEILING 300 //Max range of analogRead input, the lower the value the more sensitive (1023 = max)
 46  
 47  
 48  
 49  byte peak = 16;      // Peak level of column; used for falling dots
 50  unsigned int sample;
 51  
 52  byte dotCount = 0;  //Frame counter for peak dot
 53  byte dotHangCount = 0; //Frame counter for holding peak dot
 54  
 55  Adafruit_NeoPixel strip = Adafruit_NeoPixel(N_PIXELS, LED_PIN, NEO_GRB + NEO_KHZ800);
 56  
 57  void setup() 
 58  {
 59    // This is only needed on 5V Arduinos (Uno, Leonardo, etc.).
 60    // Connect 3.3V to mic AND TO AREF ON ARDUINO and enable this
 61    // line.  Audio samples are 'cleaner' at 3.3V.
 62    // COMMENT OUT THIS LINE FOR 3.3V ARDUINOS (FLORA, ETC.):
 63    //  analogReference(EXTERNAL);
 64  
 65    // Serial.begin(9600);
 66    strip.begin();
 67    strip.show(); // Initialize all pixels to 'off'
 68  
 69  }
 70  
 71  void loop() 
 72  {
 73    unsigned long startMillis= millis();  // Start of sample window
 74    float peakToPeak = 0;   // peak-to-peak level
 75  
 76    unsigned int signalMax = 0;
 77    unsigned int signalMin = 1023;
 78    unsigned int c, y;
 79  
 80  
 81    // collect data for length of sample window (in mS)
 82    while (millis() - startMillis < SAMPLE_WINDOW)
 83    {
 84      sample = analogRead(MIC_PIN);
 85      if (sample < 1024)  // toss out spurious readings
 86      {
 87        if (sample > signalMax)
 88        {
 89          signalMax = sample;  // save just the max levels
 90        }
 91        else if (sample < signalMin)
 92        {
 93          signalMin = sample;  // save just the min levels
 94        }
 95      }
 96    }
 97    peakToPeak = signalMax - signalMin;  // max - min = peak-peak amplitude
 98   
 99    // Serial.println(peakToPeak);
100  
101  
102    //Fill the strip with rainbow gradient
103    for (int i=0;i<=strip.numPixels()-1;i++){
104      strip.setPixelColor(i,Wheel(map(i,0,strip.numPixels()-1,30,150)));
105    }
106  
107  
108    //Scale the input logarithmically instead of linearly
109    c = fscale(INPUT_FLOOR, INPUT_CEILING, strip.numPixels(), 0, peakToPeak, 2);
110  
111    
112  
113  
114    if(c < peak) {
115      peak = c;        // Keep dot on top
116      dotHangCount = 0;    // make the dot hang before falling
117    }
118    if (c <= strip.numPixels()) { // Fill partial column with off pixels
119      drawLine(strip.numPixels(), strip.numPixels()-c, strip.Color(0, 0, 0));
120    }
121  
122    // Set the peak dot to match the rainbow gradient
123    y = strip.numPixels() - peak;
124    
125    strip.setPixelColor(y-1,Wheel(map(y,0,strip.numPixels()-1,30,150)));
126  
127    strip.show();
128  
129    // Frame based peak dot animation
130    if(dotHangCount > PEAK_HANG) { //Peak pause length
131      if(++dotCount >= PEAK_FALL) { //Fall rate 
132        peak++;
133        dotCount = 0;
134      }
135    } 
136    else {
137      dotHangCount++; 
138    }
139  }
140  
141  //Used to draw a line between two points of a given color
142  void drawLine(uint8_t from, uint8_t to, uint32_t c) {
143    uint8_t fromTemp;
144    if (from > to) {
145      fromTemp = from;
146      from = to;
147      to = fromTemp;
148    }
149    for(int i=from; i<=to; i++){
150      strip.setPixelColor(i, c);
151    }
152  }
153  
154  
155  float fscale( float originalMin, float originalMax, float newBegin, float
156  newEnd, float inputValue, float curve){
157  
158    float OriginalRange = 0;
159    float NewRange = 0;
160    float zeroRefCurVal = 0;
161    float normalizedCurVal = 0;
162    float rangedValue = 0;
163    boolean invFlag = 0;
164  
165  
166    // condition curve parameter
167    // limit range
168  
169    if (curve > 10) curve = 10;
170    if (curve < -10) curve = -10;
171  
172    curve = (curve * -.1) ; // - invert and scale - this seems more intuitive - postive numbers give more weight to high end on output 
173    curve = pow(10, curve); // convert linear scale into lograthimic exponent for other pow function
174  
175    /*
176     Serial.println(curve * 100, DEC);   // multply by 100 to preserve resolution  
177     Serial.println(); 
178     */
179  
180    // Check for out of range inputValues
181    if (inputValue < originalMin) {
182      inputValue = originalMin;
183    }
184    if (inputValue > originalMax) {
185      inputValue = originalMax;
186    }
187  
188    // Zero Refference the values
189    OriginalRange = originalMax - originalMin;
190  
191    if (newEnd > newBegin){ 
192      NewRange = newEnd - newBegin;
193    }
194    else
195    {
196      NewRange = newBegin - newEnd; 
197      invFlag = 1;
198    }
199  
200    zeroRefCurVal = inputValue - originalMin;
201    normalizedCurVal  =  zeroRefCurVal / OriginalRange;   // normalize to 0 - 1 float
202  
203    // Check for originalMin > originalMax  - the math for all other cases i.e. negative numbers seems to work out fine 
204    if (originalMin > originalMax ) {
205      return 0;
206    }
207  
208    if (invFlag == 0){
209      rangedValue =  (pow(normalizedCurVal, curve) * NewRange) + newBegin;
210  
211    }
212    else     // invert the ranges
213    {   
214      rangedValue =  newBegin - (pow(normalizedCurVal, curve) * NewRange); 
215    }
216  
217    return rangedValue;
218  }
219  
220  
221  // Input a value 0 to 255 to get a color value.
222  // The colours are a transition r - g - b - back to r.
223  uint32_t Wheel(byte WheelPos) {
224    if(WheelPos < 85) {
225      return strip.Color(WheelPos * 3, 255 - WheelPos * 3, 0);
226    } 
227    else if(WheelPos < 170) {
228      WheelPos -= 85;
229      return strip.Color(255 - WheelPos * 3, 0, WheelPos * 3);
230    } 
231    else {
232      WheelPos -= 170;
233      return strip.Color(0, WheelPos * 3, 255 - WheelPos * 3);
234    }
235  }