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 }