/* | |
LED VU meter for Circuit Playground | |
This is a port of the Adafruit Amplitie project to Circuit Playground. | |
Based on code for the adjustable sensitivity version of amplitie from: | |
https://learn.adafruit.com/led-ampli-tie/the-code | |
Hardware requirements: | |
- Circuit Playground | |
Software requirements: | |
- Adafruit Circuit Playground library | |
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_CircuitPlayground.h> | |
#include <Wire.h> | |
#include <SPI.h> | |
#include <math.h> | |
#include <Adafruit_NeoPixel.h> | |
#define PIN 6 | |
#define pixelcount 29 | |
#define delayval 20 | |
#define brightval 40 | |
#define kickoff 2000 | |
Adafruit_NeoPixel pixels = Adafruit_NeoPixel(pixelcount, PIN, NEO_GRB + NEO_KHZ800); | |
#define MIC_PIN A4 // Microphone is attached to this analog pin (A4 for circuit playground) | |
#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 180 // 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 | |
byte Cpeak = 16; | |
unsigned int sample; | |
byte dotCount = 0; //Frame counter for peak dot | |
byte dotHangCount = 0; //Frame counter for holding peak dot | |
byte CdotCount = 0; //Frame counter for peak dot | |
byte CdotHangCount = 0; //Frame counter for holding peak dot | |
float fscale(float originalMin, float originalMax, float newBegin, float newEnd, float inputValue, float curve); | |
// ============================================= | |
void setup() | |
{ | |
CircuitPlayground.begin(); | |
Serial.begin(9600); | |
pixels.begin(); // This initializes the NeoPixel library. | |
pixels.setBrightness(brightval); | |
// +++++++++++++++++++++++++ | |
// just stuff to show that the strips are working on startup... | |
int pos = 0, dir = 1; // Position, direction of "eye" | |
for(int r=0;r<pixelcount*4;r++){ | |
int j; | |
// Draw 5 pixels centered on pos. setPixelColor() will clip any | |
// pixels off the ends of the strip, we don't need to watch for that. | |
pixels.setPixelColor(pos - 2, 0x100000); // Dark red | |
pixels.setPixelColor(pos - 1, 0x800000); // Medium red | |
pixels.setPixelColor(pos , 0xFF3000); // Center pixel is brightest | |
pixels.setPixelColor(pos + 1, 0x800000); // Medium red | |
pixels.setPixelColor(pos + 2, 0x100000); // Dark red | |
pixels.show(); | |
delay(30); | |
// Rather than being sneaky and erasing just the tail pixel, | |
// it's easier to erase it all and draw a new one next time. | |
for(j=-2; j<= 2; j++) pixels.setPixelColor(pos+j, 0); | |
// Bounce off ends of strip | |
pos += dir; | |
if(pos < 0) { | |
pos = 1; | |
dir = -dir; | |
} else if(pos >= pixels.numPixels()) { | |
pos = pixels.numPixels() - 2; | |
dir = -dir; | |
} | |
} | |
// +++++++++++++++++++++++++ | |
CircuitPlayground.playTone(392, 300); | |
delay(40); | |
CircuitPlayground.playTone(659, 300); | |
delay(40); | |
CircuitPlayground.playTone(523, 600); | |
delay(70); | |
colorWipe(pixels.Color(0, 0, 0), 10); // purple | |
theaterChase(CircuitPlayground.strip.Color(255, 0, 100), 50); // | |
theaterChase(CircuitPlayground.strip.Color(0, 0, 0), 0); // | |
colorWipe(pixels.Color(255, 0, 100), 50); // purple | |
theaterChase(CircuitPlayground.strip.Color(255, 0, 100), 50); // | |
theaterChase(CircuitPlayground.strip.Color(0, 0, 0), 0); // | |
delay(kickoff); | |
// ============================================= | |
} | |
void loop() | |
{ | |
//int numPixels = CircuitPlayground.strip.numPixels(); | |
int xnumPixels = 60; | |
int xxnumPixels = 10; | |
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, Cc, Cy; | |
// 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 g=0;g<=xxnumPixels-1;g++){ | |
CircuitPlayground.strip.setPixelColor(g,Wheel(map(g,0,xnumPixels-1,30,150))); | |
} | |
for (int i=0;i<=xnumPixels-1;i++){ | |
pixels.setPixelColor(i,Wheel(map(i,0,xnumPixels-1,30,150))); | |
} | |
//Scale the input logarithmically instead of linearly | |
c = fscale(INPUT_FLOOR, INPUT_CEILING, xnumPixels, 0, peakToPeak, 2); | |
Cc = fscale(INPUT_FLOOR, INPUT_CEILING, xxnumPixels, 0, peakToPeak, 2); | |
// Turn off pixels that are below volume threshold. | |
if(c < peak) { | |
peak = c; // Keep dot on top | |
dotHangCount = 0; // make the dot hang before falling | |
} | |
if (c <= xnumPixels) { // Fill partial column with off pixels | |
drawLine(xnumPixels, xnumPixels-c, CircuitPlayground.strip.Color(0, 0, 0)); | |
} | |
if(Cc < Cpeak) { | |
Cpeak = Cc; // Keep dot on top | |
CdotHangCount = 0; // make the dot hang before falling | |
} | |
if (Cc <= xxnumPixels) { // Fill partial column with off pixels | |
CdrawLine(xxnumPixels, xxnumPixels-Cc, CircuitPlayground.strip.Color(0, 0, 0)); | |
} | |
// Set the peak dot to match the rainbow gradient | |
y = xnumPixels - peak; | |
Cy = xxnumPixels - Cpeak; | |
Serial.println(y); | |
CircuitPlayground.strip.setPixelColor(Cy-1,Wheel(map(Cy,0,xxnumPixels-1,30,150))); | |
CircuitPlayground.strip.show(); | |
pixels.setPixelColor(y-1,Wheel(map(y,0,xnumPixels-1,30,150))); | |
pixels.show(); | |
delay(10); | |
// Frame based peak dot animation | |
if(dotHangCount > PEAK_HANG) { //Peak pause length | |
if(++dotCount >= PEAK_FALL) { //Fall rate | |
peak++; | |
dotCount = 0; | |
} | |
} | |
else { | |
dotHangCount++; | |
} | |
if(CdotHangCount > PEAK_HANG) { //Peak pause length | |
if(++CdotCount >= PEAK_FALL) { //Fall rate | |
Cpeak++; | |
CdotCount = 0; | |
} | |
} | |
else { | |
CdotHangCount++; | |
} | |
} | |
//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++){ | |
// CircuitPlayground.strip.setPixelColor(i, c); | |
pixels.setPixelColor(i, c); | |
} | |
} | |
void CdrawLine(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++){ | |
CircuitPlayground.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 CircuitPlayground.strip.Color(WheelPos * 3, 255 - WheelPos * 3, 0); | |
} | |
else if(WheelPos < 170) { | |
WheelPos -= 85; | |
return CircuitPlayground.strip.Color(255 - WheelPos * 3, 0, WheelPos * 3); | |
} | |
else { | |
WheelPos -= 170; | |
return CircuitPlayground.strip.Color(0, WheelPos * 3, 255 - WheelPos * 3); | |
} | |
} | |
// Fill the dots one after the other with a color | |
void colorWipe(uint32_t c, uint8_t wait) { | |
for(uint16_t i=0; i<pixels.numPixels(); i++) { | |
pixels.setPixelColor(i, c); | |
pixels.show(); | |
delay(wait); | |
} | |
} | |
//Theatre-style crawling lights. | |
void theaterChase(uint32_t c, uint8_t wait) { | |
for (int j=0; j<10; j++) { //do 10 cycles of chasing | |
for (int q=0; q < 3; q++) { | |
for (uint16_t i=0; i < CircuitPlayground.strip.numPixels(); i=i+3) { | |
CircuitPlayground.strip.setPixelColor(i+q, c); //turn every third pixel on | |
} | |
CircuitPlayground.strip.show(); | |
delay(wait); | |
for (uint16_t i=0; i < CircuitPlayground.strip.numPixels(); i=i+3) { | |
CircuitPlayground.strip.setPixelColor(i+q, 0); //turn every third pixel off | |
} | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment