Skip to content

Instantly share code, notes, and snippets.

@caitlinsdad
Created April 5, 2018 19:20
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save caitlinsdad/e71f00bb4677f6906da3591810b08797 to your computer and use it in GitHub Desktop.
Save caitlinsdad/e71f00bb4677f6906da3591810b08797 to your computer and use it in GitHub Desktop.
/*
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