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 }