Files
Studuino/libraries/Adafruit_CircuitPlayground/examples/vu_meter/vu_meter.ino
T
2026-09-25 10:48:51 +10:00

207 lines
5.7 KiB
Arduino

/*
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>
#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 500 // 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
float fscale(float originalMin, float originalMax, float newBegin, float newEnd, float inputValue, float curve);
void setup()
{
CircuitPlayground.begin();
}
void loop()
{
int numPixels = CircuitPlayground.strip.numPixels();
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<=numPixels-1;i++){
CircuitPlayground.strip.setPixelColor(i,Wheel(map(i,0,numPixels-1,30,150)));
}
//Scale the input logarithmically instead of linearly
c = fscale(INPUT_FLOOR, INPUT_CEILING, numPixels, 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 <= numPixels) { // Fill partial column with off pixels
drawLine(numPixels, numPixels-c, CircuitPlayground.strip.Color(0, 0, 0));
}
// Set the peak dot to match the rainbow gradient
y = numPixels - peak;
CircuitPlayground.strip.setPixelColor(y-1,Wheel(map(y,0,numPixels-1,30,150)));
CircuitPlayground.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++){
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);
}
}