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