Merge pull request #15 from adafruit/3D_Printed_LED_Microphone_Flag
ported to Circuit Python
This commit is contained in:
commit
eee2c35914
3 changed files with 428 additions and 0 deletions
|
|
@ -0,0 +1,232 @@
|
|||
/*
|
||||
LED VU meter for Arduino and Adafruit NeoPixel LEDs.
|
||||
|
||||
Hardware requirements:
|
||||
- Most Arduino or Arduino-compatible boards (ATmega 328P or better).
|
||||
- Adafruit Electret Microphone Amplifier (ID: 1063)
|
||||
- Adafruit Flora RGB Smart Pixels (ID: 1260)
|
||||
OR
|
||||
- Adafruit NeoPixel Digital LED strip (ID: 1138)
|
||||
- Optional: battery for portable use (else power through USB or adapter)
|
||||
Software requirements:
|
||||
- Adafruit NeoPixel library
|
||||
|
||||
Connections:
|
||||
- 3.3V to mic amp +
|
||||
- GND to mic amp -
|
||||
- Analog pin to microphone output (configurable below)
|
||||
- Digital pin to LED data input (configurable below)
|
||||
See notes in setup() regarding 5V vs. 3.3V boards - there may be an
|
||||
extra connection to make and one line of code to enable or disable.
|
||||
|
||||
Written by Adafruit Industries. Distributed under the BSD license.
|
||||
This paragraph must be included in any redistribution.
|
||||
|
||||
fscale function:
|
||||
Floating Point Autoscale Function V0.1
|
||||
Written by Paul Badger 2007
|
||||
Modified from code by Greg Shakar
|
||||
|
||||
*/
|
||||
|
||||
#include <Adafruit_NeoPixel.h>
|
||||
#include <math.h>
|
||||
|
||||
#define N_PIXELS 16 // Number of pixels in strand
|
||||
#define MIC_PIN A1 // Microphone is attached to this analog pin
|
||||
#define LED_PIN 1 // NeoPixel LED strand is connected to this pin
|
||||
#define SAMPLE_WINDOW 10 // Sample window for average level
|
||||
#define PEAK_HANG 24 //Time of pause before peak dot falls
|
||||
#define PEAK_FALL 4 //Rate of falling peak dot
|
||||
#define INPUT_FLOOR 10 //Lower range of analogRead input
|
||||
#define INPUT_CEILING 300 //Max range of analogRead input, the lower the value the more sensitive (1023 = max)
|
||||
|
||||
|
||||
|
||||
byte peak = 16; // Peak level of column; used for falling dots
|
||||
unsigned int sample;
|
||||
|
||||
byte dotCount = 0; //Frame counter for peak dot
|
||||
byte dotHangCount = 0; //Frame counter for holding peak dot
|
||||
|
||||
Adafruit_NeoPixel strip = Adafruit_NeoPixel(N_PIXELS, LED_PIN, NEO_GRB + NEO_KHZ800);
|
||||
|
||||
void setup()
|
||||
{
|
||||
// This is only needed on 5V Arduinos (Uno, Leonardo, etc.).
|
||||
// Connect 3.3V to mic AND TO AREF ON ARDUINO and enable this
|
||||
// line. Audio samples are 'cleaner' at 3.3V.
|
||||
// COMMENT OUT THIS LINE FOR 3.3V ARDUINOS (FLORA, ETC.):
|
||||
// analogReference(EXTERNAL);
|
||||
|
||||
// Serial.begin(9600);
|
||||
strip.begin();
|
||||
strip.show(); // Initialize all pixels to 'off'
|
||||
|
||||
}
|
||||
|
||||
void loop()
|
||||
{
|
||||
unsigned long startMillis= millis(); // Start of sample window
|
||||
float peakToPeak = 0; // peak-to-peak level
|
||||
|
||||
unsigned int signalMax = 0;
|
||||
unsigned int signalMin = 1023;
|
||||
unsigned int c, y;
|
||||
|
||||
|
||||
// collect data for length of sample window (in mS)
|
||||
while (millis() - startMillis < SAMPLE_WINDOW)
|
||||
{
|
||||
sample = analogRead(MIC_PIN);
|
||||
if (sample < 1024) // toss out spurious readings
|
||||
{
|
||||
if (sample > signalMax)
|
||||
{
|
||||
signalMax = sample; // save just the max levels
|
||||
}
|
||||
else if (sample < signalMin)
|
||||
{
|
||||
signalMin = sample; // save just the min levels
|
||||
}
|
||||
}
|
||||
}
|
||||
peakToPeak = signalMax - signalMin; // max - min = peak-peak amplitude
|
||||
|
||||
// Serial.println(peakToPeak);
|
||||
|
||||
|
||||
//Fill the strip with rainbow gradient
|
||||
for (int i=0;i<=strip.numPixels()-1;i++){
|
||||
strip.setPixelColor(i,Wheel(map(i,0,strip.numPixels()-1,30,150)));
|
||||
}
|
||||
|
||||
|
||||
//Scale the input logarithmically instead of linearly
|
||||
c = fscale(INPUT_FLOOR, INPUT_CEILING, strip.numPixels(), 0, peakToPeak, 2);
|
||||
|
||||
|
||||
|
||||
|
||||
if(c < peak) {
|
||||
peak = c; // Keep dot on top
|
||||
dotHangCount = 0; // make the dot hang before falling
|
||||
}
|
||||
if (c <= strip.numPixels()) { // Fill partial column with off pixels
|
||||
drawLine(strip.numPixels(), strip.numPixels()-c, strip.Color(0, 0, 0));
|
||||
}
|
||||
|
||||
// Set the peak dot to match the rainbow gradient
|
||||
y = strip.numPixels() - peak;
|
||||
|
||||
strip.setPixelColor(y-1,Wheel(map(y,0,strip.numPixels()-1,30,150)));
|
||||
|
||||
strip.show();
|
||||
|
||||
// Frame based peak dot animation
|
||||
if(dotHangCount > PEAK_HANG) { //Peak pause length
|
||||
if(++dotCount >= PEAK_FALL) { //Fall rate
|
||||
peak++;
|
||||
dotCount = 0;
|
||||
}
|
||||
}
|
||||
else {
|
||||
dotHangCount++;
|
||||
}
|
||||
}
|
||||
|
||||
//Used to draw a line between two points of a given color
|
||||
void drawLine(uint8_t from, uint8_t to, uint32_t c) {
|
||||
uint8_t fromTemp;
|
||||
if (from > to) {
|
||||
fromTemp = from;
|
||||
from = to;
|
||||
to = fromTemp;
|
||||
}
|
||||
for(int i=from; i<=to; i++){
|
||||
strip.setPixelColor(i, c);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
float fscale( float originalMin, float originalMax, float newBegin, float
|
||||
newEnd, float inputValue, float curve){
|
||||
|
||||
float OriginalRange = 0;
|
||||
float NewRange = 0;
|
||||
float zeroRefCurVal = 0;
|
||||
float normalizedCurVal = 0;
|
||||
float rangedValue = 0;
|
||||
boolean invFlag = 0;
|
||||
|
||||
|
||||
// condition curve parameter
|
||||
// limit range
|
||||
|
||||
if (curve > 10) curve = 10;
|
||||
if (curve < -10) curve = -10;
|
||||
|
||||
curve = (curve * -.1) ; // - invert and scale - this seems more intuitive - postive numbers give more weight to high end on output
|
||||
curve = pow(10, curve); // convert linear scale into lograthimic exponent for other pow function
|
||||
|
||||
/*
|
||||
Serial.println(curve * 100, DEC); // multply by 100 to preserve resolution
|
||||
Serial.println();
|
||||
*/
|
||||
|
||||
// Check for out of range inputValues
|
||||
if (inputValue < originalMin) {
|
||||
inputValue = originalMin;
|
||||
}
|
||||
if (inputValue > originalMax) {
|
||||
inputValue = originalMax;
|
||||
}
|
||||
|
||||
// Zero Refference the values
|
||||
OriginalRange = originalMax - originalMin;
|
||||
|
||||
if (newEnd > newBegin){
|
||||
NewRange = newEnd - newBegin;
|
||||
}
|
||||
else
|
||||
{
|
||||
NewRange = newBegin - newEnd;
|
||||
invFlag = 1;
|
||||
}
|
||||
|
||||
zeroRefCurVal = inputValue - originalMin;
|
||||
normalizedCurVal = zeroRefCurVal / OriginalRange; // normalize to 0 - 1 float
|
||||
|
||||
// Check for originalMin > originalMax - the math for all other cases i.e. negative numbers seems to work out fine
|
||||
if (originalMin > originalMax ) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (invFlag == 0){
|
||||
rangedValue = (pow(normalizedCurVal, curve) * NewRange) + newBegin;
|
||||
|
||||
}
|
||||
else // invert the ranges
|
||||
{
|
||||
rangedValue = newBegin - (pow(normalizedCurVal, curve) * NewRange);
|
||||
}
|
||||
|
||||
return rangedValue;
|
||||
}
|
||||
|
||||
|
||||
// Input a value 0 to 255 to get a color value.
|
||||
// The colours are a transition r - g - b - back to r.
|
||||
uint32_t Wheel(byte WheelPos) {
|
||||
if(WheelPos < 85) {
|
||||
return strip.Color(WheelPos * 3, 255 - WheelPos * 3, 0);
|
||||
}
|
||||
else if(WheelPos < 170) {
|
||||
WheelPos -= 85;
|
||||
return strip.Color(255 - WheelPos * 3, 0, WheelPos * 3);
|
||||
}
|
||||
else {
|
||||
WheelPos -= 170;
|
||||
return strip.Color(0, WheelPos * 3, 255 - WheelPos * 3);
|
||||
}
|
||||
}
|
||||
192
3D_Printed_LED_Microphone_Flag/3D_Printed_LED_Microphone_Flag.py
Normal file
192
3D_Printed_LED_Microphone_Flag/3D_Printed_LED_Microphone_Flag.py
Normal file
|
|
@ -0,0 +1,192 @@
|
|||
# LED VU meter for Arduino and Adafruit NeoPixel LEDs.
|
||||
|
||||
# Hardware requirements:
|
||||
# - M0 boards
|
||||
# - Adafruit Electret Microphone Amplifier (ID: 1063)
|
||||
# - Adafruit Flora RGB Smart Pixels (ID: 1260)
|
||||
# OR
|
||||
# - Adafruit NeoPixel Digital LED strip (ID: 1138)
|
||||
# - Optional: battery for portable use (else power through USB or adapter)
|
||||
# Software requirements:
|
||||
# - Adafruit NeoPixel library
|
||||
|
||||
# Connections:
|
||||
# - 3.3V to mic amp +
|
||||
# - GND to mic amp -
|
||||
# - Analog pin to microphone output (configurable below)
|
||||
# - Digital pin to LED data input (configurable below)
|
||||
# See notes in setup() regarding 5V vs. 3.3V boards - there may be an
|
||||
# extra connection to make and one line of code to enable or disable.
|
||||
|
||||
# Written by Adafruit Industries. Distributed under the BSD license.
|
||||
# This paragraph must be included in any redistribution.
|
||||
|
||||
# fscale function:
|
||||
# Floating Point Autoscale Function V0.1
|
||||
# Written by Paul Badger 2007
|
||||
# Modified fromhere code by Greg Shakar
|
||||
# Ported to Circuit Python by Mikey Sklar
|
||||
|
||||
import board
|
||||
import neopixel
|
||||
import time
|
||||
from analogio import AnalogIn
|
||||
import array
|
||||
|
||||
n_pixels = 16 # Number of pixels you are using
|
||||
mic_pin = AnalogIn(board.A1) # Microphone is attached to this analog pin
|
||||
led_pin = board.D1 # NeoPixel LED strand is connected to this pin
|
||||
sample_window = .1 # Sample window for average level
|
||||
peak_hang = 24 # Time of pause before peak dot falls
|
||||
peak_fall = 4 # Rate of falling peak dot
|
||||
input_floor = 10 # Lower range of analogRead input
|
||||
input_ceiling = 300 # Max range of analogRead input, the lower the value the more sensitive (1023 = max)
|
||||
|
||||
peak = 16 # Peak level of column; used for falling dots
|
||||
sample = 0
|
||||
|
||||
dotcount = 0 # Frame counter for peak dot
|
||||
dothangcount = 0 # Frame counter for holding peak dot
|
||||
|
||||
strip = neopixel.NeoPixel(led_pin, n_pixels, brightness=1, auto_write=False)
|
||||
|
||||
def wheel(pos):
|
||||
# Input a value 0 to 255 to get a color value.
|
||||
# The colours are a transition r - g - b - back to r.
|
||||
if (pos < 0) or (pos > 255):
|
||||
return (0, 0, 0)
|
||||
if (pos < 85):
|
||||
return (int(pos * 3), int(255 - (pos*3)), 0)
|
||||
elif (pos < 170):
|
||||
pos -= 85
|
||||
return (int(255 - pos*3), 0, int(pos*3))
|
||||
else:
|
||||
pos -= 170
|
||||
return (0, int(pos*3), int(255 - pos*3))
|
||||
|
||||
def remapRange(value, leftMin, leftMax, rightMin, rightMax):
|
||||
# this remaps a value fromhere original (left) range to new (right) range
|
||||
# Figure out how 'wide' each range is
|
||||
leftSpan = leftMax - leftMin
|
||||
rightSpan = rightMax - rightMin
|
||||
|
||||
# Convert the left range into a 0-1 range (int)
|
||||
valueScaled = int(value - leftMin) / int(leftSpan)
|
||||
|
||||
# Convert the 0-1 range into a value in the right range.
|
||||
return int(rightMin + (valueScaled * rightSpan))
|
||||
|
||||
def fscale(originalmin, originalmax, newbegin, newend, inputvalue, curve):
|
||||
originalrange = 0
|
||||
newrange = 0
|
||||
zerorefcurval = 0
|
||||
normalizedcurval = 0
|
||||
rangedvalue = 0
|
||||
invflag = 0
|
||||
|
||||
# condition curve parameter
|
||||
# limit range
|
||||
if (curve > 10):
|
||||
curve = 10
|
||||
if (curve < -10):
|
||||
curve = -10
|
||||
|
||||
# - invert and scale -
|
||||
# this seems more intuitive
|
||||
# postive numbers give more weight to high end on output
|
||||
curve = (curve * -.1)
|
||||
curve = pow(10, curve) # convert linear scale into lograthimic exponent for other pow function
|
||||
|
||||
# Check for out of range inputValues
|
||||
if (inputvalue < originalmin):
|
||||
inputvalue = originalmin
|
||||
|
||||
if (inputvalue > originalmax):
|
||||
inputvalue = originalmax
|
||||
|
||||
# Zero Refference the values
|
||||
originalrange = originalmax - originalmin
|
||||
|
||||
if (newend > newbegin):
|
||||
newrange = newend - newbegin
|
||||
else:
|
||||
newrange = newbegin - newend
|
||||
invflag = 1
|
||||
|
||||
zerorefcurval = inputvalue - originalmin
|
||||
normalizedcurval = zerorefcurval / originalrange # normalize to 0 - 1 float
|
||||
|
||||
# Check for originalMin > originalMax
|
||||
# -the math for all other cases
|
||||
# i.e. negative numbers seems to work out fine
|
||||
if (originalmin > originalmax ):
|
||||
return(0)
|
||||
|
||||
if (invflag == 0):
|
||||
rangedvalue = (pow(normalizedcurval, curve) * newrange) + newbegin
|
||||
else: # invert the ranges
|
||||
rangedvalue = newbegin - (pow(normalizedcurval, curve) * newrange);
|
||||
|
||||
return(rangedvalue)
|
||||
|
||||
def drawLine(fromhere, to):
|
||||
|
||||
fromheretemp = 0
|
||||
|
||||
if (fromhere > to):
|
||||
fromheretemp = fromhere
|
||||
fromhere = to
|
||||
to = fromheretemp
|
||||
|
||||
for i in range(fromhere, to):
|
||||
strip[i] = (0,0,0)
|
||||
|
||||
while True:
|
||||
|
||||
time_start = time.monotonic() # current time used for sample window
|
||||
peaktopeak = 0 # peak-to-peak level
|
||||
signalmax = 0
|
||||
signalmin = 1023
|
||||
c = 0
|
||||
y = 0
|
||||
|
||||
# collect data for length of sample window (in seconds)
|
||||
while ( ( time.monotonic() - time_start ) < sample_window):
|
||||
|
||||
sample = mic_pin.value / 64 # convert to arduino 10-bit [1024] fromhere 16-bit [65536]
|
||||
|
||||
if (sample < 1024): # toss out spurious readings
|
||||
|
||||
if (sample > signalmax):
|
||||
signalmax = sample # save just the max levels
|
||||
elif (sample < signalmin):
|
||||
signalmin = sample # save just the min levels
|
||||
|
||||
peaktopeak = signalmax - signalmin # max - min = peak-peak amplitude
|
||||
|
||||
# Fill the strip with rainbow gradient
|
||||
for i in range(0, len(strip)):
|
||||
strip[i] = wheel(remapRange(i, 0, (n_pixels - 1), 30, 150))
|
||||
|
||||
# Scale the input logarithmically instead of linearly
|
||||
c = fscale(input_floor, input_ceiling, (n_pixels - 1), 0, peaktopeak, 2)
|
||||
|
||||
if (c < peak):
|
||||
peak = c # keep dot on top
|
||||
dothangcount = 0 # make the dot hang before falling
|
||||
|
||||
if (c <= n_pixels): # fill partial column with off pixels
|
||||
drawLine(n_pixels, n_pixels - int(c))
|
||||
|
||||
# Set the peak dot to match the rainbow gradient
|
||||
y = n_pixels - peak
|
||||
strip.fill = (y - 1, wheel(remapRange(y, 0, (n_pixels - 1), 30, 150)))
|
||||
strip.write()
|
||||
|
||||
# Frame based peak dot animation
|
||||
if(dothangcount > peak_hang): # Peak pause length
|
||||
if(++dotcount >= peak_fall): # Fall rate
|
||||
peak += 1
|
||||
dotcount = 0
|
||||
else:
|
||||
dothangcount += 1
|
||||
4
3D_Printed_LED_Microphone_Flag/README.md
Normal file
4
3D_Printed_LED_Microphone_Flag/README.md
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
# 3D_Printed_LED_Microphone_Flag
|
||||
|
||||
Code to accompany this tutorial:
|
||||
https://learn.adafruit.com/3d-printed-led-microphone-flag
|
||||
Loading…
Reference in a new issue