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