/ LED_Ampli_Tie / Ampli_Tie_Dynamic / Ampli_Tie_Dynamic.ino
Ampli_Tie_Dynamic.ino
  1  // SPDX-FileCopyrightText: 2017 Mikey Sklar for Adafruit Industries
  2  // SPDX-FileCopyrightText: 2019 Anne Barela for Adafruit Industries
  3  //
  4  // SPDX-License-Identifier: MIT
  5  
  6  /*
  7  LED VU meter for Arduino and Adafruit NeoPixel LEDs. More info: http://learn.adafruit.com/led-ampli-tie/
  8  
  9  Hardware requirements:
 10   - Most Arduino or Arduino-compatible boards (ATmega 328P or better).
 11   - Adafruit Electret Microphone Amplifier (ID: 1063)
 12   - Adafruit Flora RGB Smart Pixels (ID: 1260)
 13     OR
 14   - Adafruit NeoPixel Digital LED strip (ID: 1138)
 15   - Optional: battery for portable use (else power through USB or adapter)
 16  Software requirements:
 17   - Adafruit NeoPixel library
 18  
 19  Connections:
 20   - 3.3V to mic amp +
 21   - GND to mic amp -
 22   - Analog pin to microphone output (configurable below)
 23   - Digital pin to LED data input (configurable below)
 24   See notes in setup() regarding 5V vs. 3.3V boards - there may be an
 25   extra connection to make and one line of code to enable or disable.
 26  
 27  Written by Adafruit Industries.  Distributed under the BSD license.
 28  This paragraph must be included in any redistribution.
 29  */
 30  
 31  #include <Adafruit_NeoPixel.h>
 32  
 33  #define N_PIXELS  16  // Number of pixels in strand
 34  #define MIC_PIN   A9  // Microphone is attached to this analog pin
 35  #define LED_PIN    6  // NeoPixel LED strand is connected to this pin
 36  #define DC_OFFSET  0  // DC offset in mic signal - if unusure, leave 0
 37  #define NOISE     10  // Noise/hum/interference in mic signal
 38  #define SAMPLES   60  // Length of buffer for dynamic level adjustment
 39  #define TOP       (N_PIXELS + 2) // Allow dot to go slightly off scale
 40  #define PEAK_FALL 40  // Rate of peak falling dot
 41  
 42  byte
 43    peak      = 0,      // Used for falling dot
 44    dotCount  = 0,      // Frame counter for delaying dot-falling speed
 45    volCount  = 0;      // Frame counter for storing past volume data
 46  int
 47    vol[SAMPLES],       // Collection of prior volume samples
 48    lvl       = 10,      // Current "dampened" audio level
 49    minLvlAvg = 0,      // For dynamic adjustment of graph low & high
 50    maxLvlAvg = 512;
 51  Adafruit_NeoPixel
 52    strip = Adafruit_NeoPixel(N_PIXELS, LED_PIN, NEO_GRB + NEO_KHZ800);
 53  
 54  void setup() {
 55  
 56    // This is only needed on 5V Arduinos (Uno, Leonardo, etc.).
 57    // Connect 3.3V to mic AND TO AREF ON ARDUINO and enable this
 58    // line.  Audio samples are 'cleaner' at 3.3V.
 59    // COMMENT OUT THIS LINE FOR 3.3V ARDUINOS (FLORA, ETC.):
 60  //  analogReference(EXTERNAL);
 61  
 62    memset(vol, 0, sizeof(vol));
 63    strip.begin();
 64  }
 65  
 66  void loop() {
 67    uint8_t  i;
 68    uint16_t minLvl, maxLvl;
 69    int      n, height;
 70  
 71    n   = analogRead(MIC_PIN);                        // Raw reading from mic 
 72    n   = abs(n - 512 - DC_OFFSET); // Center on zero
 73    n   = (n <= NOISE) ? 0 : (n - NOISE);             // Remove noise/hum
 74    lvl = ((lvl * 7) + n) >> 3;    // "Dampened" reading (else looks twitchy)
 75  
 76    // Calculate bar height based on dynamic min/max levels (fixed point):
 77    height = TOP * (lvl - minLvlAvg) / (long)(maxLvlAvg - minLvlAvg);
 78  
 79    if(height < 0L)       height = 0;      // Clip output
 80    else if(height > TOP) height = TOP;
 81    if(height > peak)     peak   = height; // Keep 'peak' dot at top
 82  
 83  
 84    // Color pixels based on rainbow gradient
 85    for(i=0; i<N_PIXELS; i++) {
 86      if(i >= height)               strip.setPixelColor(i,   0,   0, 0);
 87      else strip.setPixelColor(i,Wheel(map(i,0,strip.numPixels()-1,30,150)));
 88      
 89    }
 90  
 91    // Draw peak dot  
 92    if(peak > 0 && peak <= N_PIXELS-1) strip.setPixelColor(peak,Wheel(map(peak,0,strip.numPixels()-1,30,150)));
 93    
 94     strip.show(); // Update strip
 95  
 96  // Every few frames, make the peak pixel drop by 1:
 97  
 98      if(++dotCount >= PEAK_FALL) { //fall rate 
 99        
100        if(peak > 0) peak--;
101        dotCount = 0;
102      }
103  
104    vol[volCount] = n;                      // Save sample for dynamic leveling
105    if(++volCount >= SAMPLES) volCount = 0; // Advance/rollover sample counter
106  
107    // Get volume range of prior frames
108    minLvl = maxLvl = vol[0];
109    for(i=1; i<SAMPLES; i++) {
110      if(vol[i] < minLvl)      minLvl = vol[i];
111      else if(vol[i] > maxLvl) maxLvl = vol[i];
112    }
113    // minLvl and maxLvl indicate the volume range over prior frames, used
114    // for vertically scaling the output graph (so it looks interesting
115    // regardless of volume level).  If they're too close together though
116    // (e.g. at very low volume levels) the graph becomes super coarse
117    // and 'jumpy'...so keep some minimum distance between them (this
118    // also lets the graph go to zero when no sound is playing):
119    if((maxLvl - minLvl) < TOP) maxLvl = minLvl + TOP;
120    minLvlAvg = (minLvlAvg * 63 + minLvl) >> 6; // Dampen min/max levels
121    maxLvlAvg = (maxLvlAvg * 63 + maxLvl) >> 6; // (fake rolling average)
122  
123  }
124  
125  // Input a value 0 to 255 to get a color value.
126  // The colors are a transition r - g - b - back to r.
127  uint32_t Wheel(byte WheelPos) {
128    if(WheelPos < 85) {
129     return strip.Color(WheelPos * 3, 255 - WheelPos * 3, 0);
130    } else if(WheelPos < 170) {
131     WheelPos -= 85;
132     return strip.Color(255 - WheelPos * 3, 0, WheelPos * 3);
133    } else {
134     WheelPos -= 170;
135     return strip.Color(0, WheelPos * 3, 255 - WheelPos * 3);
136    }
137  }