/ 3D_Printed_LED_Microphone_Flag / code.py
code.py
1 # SPDX-FileCopyrightText: 2013 Phil Burgess for Adafruit Industries 2 # SPDX-FileCopyrightText: 2017 Mikey Sklar for Adafruit Industries 3 # 4 # SPDX-License-Identifier: BSD-3-Clause 5 6 # LED VU meter for Arduino and Adafruit NeoPixel LEDs. 7 8 # Hardware requirements: 9 # - M0 boards 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 fromhere code by Greg Shakar 33 # Ported to Circuit Python by Mikey Sklar 34 35 import time 36 37 import board 38 import neopixel 39 from rainbowio import colorwheel 40 from analogio import AnalogIn 41 42 n_pixels = 16 # Number of pixels you are using 43 mic_pin = AnalogIn(board.A1) # Microphone is attached to this analog pin 44 led_pin = board.D1 # NeoPixel LED strand is connected to this pin 45 sample_window = .1 # Sample window for average level 46 peak_hang = 24 # Time of pause before peak dot falls 47 peak_fall = 4 # Rate of falling peak dot 48 input_floor = 10 # Lower range of analogRead input 49 # Max range of analogRead input, the lower the value the more sensitive 50 # (1023 = max) 51 input_ceiling = 300 52 53 peak = 16 # Peak level of column; used for falling dots 54 sample = 0 55 56 dotcount = 0 # Frame counter for peak dot 57 dothangcount = 0 # Frame counter for holding peak dot 58 59 strip = neopixel.NeoPixel(led_pin, n_pixels, brightness=1, auto_write=False) 60 61 62 def remapRange(value, leftMin, leftMax, rightMin, rightMax): 63 # this remaps a value fromhere original (left) range to new (right) range 64 # Figure out how 'wide' each range is 65 leftSpan = leftMax - leftMin 66 rightSpan = rightMax - rightMin 67 68 # Convert the left range into a 0-1 range (int) 69 valueScaled = int(value - leftMin) / int(leftSpan) 70 71 # Convert the 0-1 range into a value in the right range. 72 return int(rightMin + (valueScaled * rightSpan)) 73 74 75 def fscale(originalmin, originalmax, newbegin, newend, inputvalue, curve): 76 invflag = 0 77 78 # condition curve parameter 79 # limit range 80 if curve > 10: 81 curve = 10 82 if curve < -10: 83 curve = -10 84 85 # - invert and scale - 86 # this seems more intuitive 87 # postive numbers give more weight to high end on output 88 curve = (curve * -.1) 89 # convert linear scale into lograthimic exponent for other pow function 90 curve = pow(10, curve) 91 92 # Check for out of range inputValues 93 if inputvalue < originalmin: 94 inputvalue = originalmin 95 96 if inputvalue > originalmax: 97 inputvalue = originalmax 98 99 # Zero Refference the values 100 originalrange = originalmax - originalmin 101 102 if newend > newbegin: 103 newrange = newend - newbegin 104 else: 105 newrange = newbegin - newend 106 invflag = 1 107 108 zerorefcurval = inputvalue - originalmin 109 # normalize to 0 - 1 float 110 normalizedcurval = zerorefcurval / originalrange 111 112 # Check for originalMin > originalMax 113 # -the math for all other cases 114 # i.e. negative numbers seems to work out fine 115 if originalmin > originalmax: 116 return 0 117 118 if invflag == 0: 119 rangedvalue = (pow(normalizedcurval, curve) * newrange) + newbegin 120 else: # invert the ranges 121 rangedvalue = newbegin - (pow(normalizedcurval, curve) * newrange) 122 123 return rangedvalue 124 125 126 def drawLine(fromhere, to): 127 if fromhere > to: 128 to, fromhere = fromhere, to 129 130 for index in range(fromhere, to): 131 strip[index] = (0, 0, 0) 132 133 134 while True: 135 136 time_start = time.monotonic() # current time used for sample window 137 peaktopeak = 0 # peak-to-peak level 138 signalmax = 0 139 signalmin = 1023 140 c = 0 141 y = 0 142 143 # collect data for length of sample window (in seconds) 144 while (time.monotonic() - time_start) < sample_window: 145 146 # convert to arduino 10-bit [1024] fromhere 16-bit [65536] 147 sample = mic_pin.value / 64 148 149 if sample < 1024: # toss out spurious readings 150 151 if sample > signalmax: 152 signalmax = sample # save just the max levels 153 elif sample < signalmin: 154 signalmin = sample # save just the min levels 155 156 peaktopeak = signalmax - signalmin # max - min = peak-peak amplitude 157 158 # Fill the strip with rainbow gradient 159 for i in range(0, len(strip)): 160 strip[i] = colorwheel(remapRange(i, 0, (n_pixels - 1), 30, 150)) 161 162 # Scale the input logarithmically instead of linearly 163 c = fscale(input_floor, input_ceiling, (n_pixels - 1), 0, peaktopeak, 2) 164 165 if c < peak: 166 peak = c # keep dot on top 167 dothangcount = 0 # make the dot hang before falling 168 169 if c <= n_pixels: # fill partial column with off pixels 170 drawLine(n_pixels, n_pixels - int(c)) 171 172 # Set the peak dot to match the rainbow gradient 173 y = n_pixels - peak 174 strip.fill = (y - 1, colorwheel(remapRange(y, 0, (n_pixels - 1), 30, 150))) 175 strip.write() 176 177 # Frame based peak dot animation 178 if dothangcount > peak_hang: # Peak pause length 179 dotcount += 1 180 if dotcount >= peak_fall: # Fall rate 181 peak += 1 182 dotcount = 0 183 else: 184 dothangcount += 1