/ Flora_Demos / Example_5_Amplitie / Example_5_Amplitie.ino
Example_5_Amplitie.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  
 30  #include <Adafruit_NeoPixel.h>
 31  
 32  #define N_PIXELS  12  // Number of pixels in strand
 33  #define MIC_PIN   A9  // Microphone is attached to this analog pin
 34  #define LED_PIN    6  // NeoPixel LED strand is connected to this pin
 35  #define DC_OFFSET  0  // DC offset in mic signal - if unusure, leave 0
 36  #define NOISE     10  // Noise/hum/interference in mic signal
 37  #define SAMPLES   60  // Length of buffer for dynamic level adjustment
 38  #define TOP       (N_PIXELS + 2) // Allow dot to go slightly off scale
 39  #define PEAK_FALL 40  // Rate of peak falling dot
 40  
 41  byte
 42    peak      = 0,      // Used for falling dot
 43    dotCount  = 0,      // Frame counter for delaying dot-falling speed
 44    volCount  = 0;      // Frame counter for storing past volume data
 45  int
 46    vol[SAMPLES],       // Collection of prior volume samples
 47    lvl       = 10,      // Current "dampened" audio level
 48    minLvlAvg = 0,      // For dynamic adjustment of graph low & high
 49    maxLvlAvg = 512;
 50  Adafruit_NeoPixel
 51    strip = Adafruit_NeoPixel(N_PIXELS, LED_PIN, NEO_GRB + NEO_KHZ800);
 52  
 53  void setup() {
 54  
 55    // This is only needed on 5V Arduinos (Uno, Leonardo, etc.).
 56    // Connect 3.3V to mic AND TO AREF ON ARDUINO and enable this
 57    // line.  Audio samples are 'cleaner' at 3.3V.
 58    // COMMENT OUT THIS LINE FOR 3.3V ARDUINOS (FLORA, ETC.):
 59  //  analogReference(EXTERNAL);
 60  
 61    memset(vol, 0, sizeof(vol));
 62    strip.begin();
 63  }
 64  
 65  void loop() {
 66    uint8_t  i;
 67    uint16_t minLvl, maxLvl;
 68    int      n, height;
 69  
 70   
 71  
 72    n   = analogRead(MIC_PIN);                        // Raw reading from mic 
 73    n   = abs(n - 512 - DC_OFFSET); // Center on zero
 74    n   = (n <= NOISE) ? 0 : (n - NOISE);             // Remove noise/hum
 75    lvl = ((lvl * 7) + n) >> 3;    // "Dampened" reading (else looks twitchy)
 76  
 77    // Calculate bar height based on dynamic min/max levels (fixed point):
 78    height = TOP * (lvl - minLvlAvg) / (long)(maxLvlAvg - minLvlAvg);
 79  
 80    if(height < 0L)       height = 0;      // Clip output
 81    else if(height > TOP) height = TOP;
 82    if(height > peak)     peak   = height; // Keep 'peak' dot at top
 83  
 84  
 85    // Color pixels based on rainbow gradient
 86    for(i=0; i<N_PIXELS; i++) {
 87      if(i >= height)               strip.setPixelColor(i,   0,   0, 0);
 88      else strip.setPixelColor(i,Wheel(map(i,0,strip.numPixels()-1,30,150)));
 89      
 90    }
 91  
 92  
 93  
 94    // Draw peak dot  
 95    if(peak > 0 && peak <= N_PIXELS-1) strip.setPixelColor(peak,Wheel(map(peak,0,strip.numPixels()-1,30,150)));
 96    
 97     strip.show(); // Update strip
 98  
 99  // Every few frames, make the peak pixel drop by 1:
100  
101      if(++dotCount >= PEAK_FALL) { //fall rate 
102        
103        if(peak > 0) peak--;
104        dotCount = 0;
105      }
106  
107  
108  
109    vol[volCount] = n;                      // Save sample for dynamic leveling
110    if(++volCount >= SAMPLES) volCount = 0; // Advance/rollover sample counter
111  
112    // Get volume range of prior frames
113    minLvl = maxLvl = vol[0];
114    for(i=1; i<SAMPLES; i++) {
115      if(vol[i] < minLvl)      minLvl = vol[i];
116      else if(vol[i] > maxLvl) maxLvl = vol[i];
117    }
118    // minLvl and maxLvl indicate the volume range over prior frames, used
119    // for vertically scaling the output graph (so it looks interesting
120    // regardless of volume level).  If they're too close together though
121    // (e.g. at very low volume levels) the graph becomes super coarse
122    // and 'jumpy'...so keep some minimum distance between them (this
123    // also lets the graph go to zero when no sound is playing):
124    if((maxLvl - minLvl) < TOP) maxLvl = minLvl + TOP;
125    minLvlAvg = (minLvlAvg * 63 + minLvl) >> 6; // Dampen min/max levels
126    maxLvlAvg = (maxLvlAvg * 63 + maxLvl) >> 6; // (fake rolling average)
127  
128  }
129  
130  // Input a value 0 to 255 to get a color value.
131  // The colors are a transition r - g - b - back to r.
132  uint32_t Wheel(byte WheelPos) {
133    if(WheelPos < 85) {
134     return strip.Color(WheelPos * 3, 255 - WheelPos * 3, 0);
135    } else if(WheelPos < 170) {
136     WheelPos -= 85;
137     return strip.Color(255 - WheelPos * 3, 0, WheelPos * 3);
138    } else {
139     WheelPos -= 170;
140     return strip.Color(0, WheelPos * 3, 255 - WheelPos * 3);
141    }
142  }
143