Skip to content

Instantly share code, notes, and snippets.

@jedgarpark
Last active February 7, 2018 16:46
Show Gist options
  • Save jedgarpark/4d1629f12c3ab9396cd6dc8dbd8898df to your computer and use it in GitHub Desktop.
Save jedgarpark/4d1629f12c3ab9396cd6dc8dbd8898df to your computer and use it in GitHub Desktop.
/* Use the FFT library with a Feather M0 Express and mic w amplifier.
* Requires the Adafruit Zero FFT library
*
* The LEDs will map around the circle when frequencies between FREQ_MIN
* and FREQ_MAX are detected
*/
#include <Adafruit_NeoPixel.h>
#include "Adafruit_ZeroFFT.h"
#define MIC_PIN A0 // Microphone is attached to this analog pin
#define LED_PIN 6 // NeoPixel LED strand is connected to this pin
#define NUM_PIXELS 47 // how many neopixesl on the strip 47
//set this to 0 to disable autoranging and scale to FFT_MAX
#define AUTOSCALE 0
//this must be a power of 2
#define DATA_SIZE 256 //was 1024 but too slow?
//the sample rate
#define FS 22000 //22000
//the lowest frequency that will register on the meter 200
#define FREQ_MIN 200
//the highest frequency that will register on the meter 3000
#define FREQ_MAX 2000
#define MIN_INDEX FFT_INDEX(FREQ_MIN, FS, DATA_SIZE)
#define MAX_INDEX FFT_INDEX(FREQ_MAX, FS, DATA_SIZE)
#define SCALE_FACTOR 32
int16_t pixelData[NUM_PIXELS + 1];
int16_t inputData[DATA_SIZE];
//int16_t val[DATA_SIZE];
const uint8_t
// R,G,B values for color wheel covering 47 NeoPixels:
// reds[] = { 0xAD, 0x9A, 0x84, 0x65, 0x00, 0x00, 0x00, 0x00, 0x65, 0x84 },
reds[] = { 0x65, 0x65, 0x65, 0x65, 0x65, 0x65, 0x65, 0x65, 0x65, 0x65,
0x65, 0x65, 0x65, 0x65, 0x65, 0x65, 0x65, 0x65, 0x65, 0x65,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
//greens[] = { 0x00, 0x66, 0x87, 0x9E, 0xB1, 0x87, 0x66, 0x00, 0x00, 0x00 },
greens[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x65, 0x65, 0x65, 0x65, 0x65, 0x65, 0x65, 0x65, 0x65, 0x65,
0x65, 0x65, 0x65, 0x65, 0x65, 0x65, 0x65, 0x65, 0x65, 0x65,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
//blues[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0xC3, 0xE4, 0xFF, 0xE4, 0xC3 },
blues[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x65, 0x65, 0x65, 0x65, 0x65, 0x65, 0x65 },
gamma8[] = { // Gamma correction improves the appearance of midrange colors
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x03, 0x03, 0x03, 0x03,
0x03, 0x03, 0x04, 0x04, 0x04, 0x04, 0x05, 0x05, 0x05, 0x05, 0x05, 0x06,
0x06, 0x06, 0x06, 0x07, 0x07, 0x07, 0x08, 0x08, 0x08, 0x09, 0x09, 0x09,
0x0A, 0x0A, 0x0A, 0x0B, 0x0B, 0x0B, 0x0C, 0x0C, 0x0D, 0x0D, 0x0D, 0x0E,
0x0E, 0x0F, 0x0F, 0x10, 0x10, 0x11, 0x11, 0x12, 0x12, 0x13, 0x13, 0x14,
0x14, 0x15, 0x15, 0x16, 0x16, 0x17, 0x18, 0x18, 0x19, 0x19, 0x1A, 0x1B,
0x1B, 0x1C, 0x1D, 0x1D, 0x1E, 0x1F, 0x1F, 0x20, 0x21, 0x22, 0x22, 0x23,
0x24, 0x25, 0x26, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2A, 0x2B, 0x2C, 0x2D,
0x2E, 0x2F, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39,
0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F, 0x40, 0x41, 0x42, 0x44, 0x45, 0x46,
0x47, 0x48, 0x49, 0x4B, 0x4C, 0x4D, 0x4E, 0x50, 0x51, 0x52, 0x54, 0x55,
0x56, 0x58, 0x59, 0x5A, 0x5C, 0x5D, 0x5E, 0x60, 0x61, 0x63, 0x64, 0x66,
0x67, 0x69, 0x6A, 0x6C, 0x6D, 0x6F, 0x70, 0x72, 0x73, 0x75, 0x77, 0x78,
0x7A, 0x7C, 0x7D, 0x7F, 0x81, 0x82, 0x84, 0x86, 0x88, 0x89, 0x8B, 0x8D,
0x8F, 0x91, 0x92, 0x94, 0x96, 0x98, 0x9A, 0x9C, 0x9E, 0xA0, 0xA2, 0xA4,
0xA6, 0xA8, 0xAA, 0xAC, 0xAE, 0xB0, 0xB2, 0xB4, 0xB6, 0xB8, 0xBA, 0xBC,
0xBF, 0xC1, 0xC3, 0xC5, 0xC7, 0xCA, 0xCC, 0xCE, 0xD1, 0xD3, 0xD5, 0xD7,
0xDA, 0xDC, 0xDF, 0xE1, 0xE3, 0xE6, 0xE8, 0xEB, 0xED, 0xF0, 0xF2, 0xF5,
0xF7, 0xFA, 0xFC, 0xFF };
Adafruit_NeoPixel strip = Adafruit_NeoPixel(NUM_PIXELS, LED_PIN, NEO_GRBW + NEO_KHZ800);
// the setup routine runs once when you press reset:
void setup() {
Serial.begin(9600);
strip.begin();
strip.show(); // Initialize all pixels to 'off'
}
// Track low and high levels for dynamic adjustment
int minLvl = 0, maxLvl = 1000;
void loop() {
int i;
/*
//CircuitPlayground.mic.capture(inputData, DATA_SIZE);
// Center data on average amplitude
int32_t avg = 0;
for(i=0; i<DATA_SIZE; i++) avg += inputData[i];
avg /= DATA_SIZE;
// Scale for FFT
for(i=0; i<DATA_SIZE; i++)
inputData[i] = (inputData[i] - avg) * SCALE_FACTOR;
*/
int32_t avg = 0;
for(int i=0; i<DATA_SIZE; i++){
inputData[i] = analogRead(MIC_PIN);
Serial.print("Input Data Value: ");
Serial.println(inputData[i]);
avg += inputData[i];
}
avg /= DATA_SIZE;
// Scale for FFT
for (i=0; i<DATA_SIZE; i++){
//inputData[i] = (inputData[i] - avg) * SCALE_FACTOR;
inputData[i] = (inputData[i] - avg);
inputData[i] = inputData[i] * SCALE_FACTOR;
}
/*
*
int32_t avg = 0;
for(int i=0; i<DATA_SIZE; i++){
int16_t val = analogRead(MIC_PIN);
Serial.print("Value: ");
Serial.println(val);
avg += val;
inputData[i] = val;
}
*/
//remove DC offset and gain up to 16 bits
avg = avg/DATA_SIZE;
for(int i=0; i<DATA_SIZE; i++) inputData[i] = (inputData[i] - avg) * 32;
//run the FFT
ZeroFFT(inputData, DATA_SIZE);
// Sum inputData[] (FFT output) into pixelData[] bins.
// Note that pixelData[] has one element more than the number of
// pixels actually displayed -- this is on purpose, as the last
// element tends to be zero and not visually interesting.
memset(pixelData, 0, sizeof pixelData); // Clear pixelData[] buffer
for(i=MIN_INDEX; i<=MAX_INDEX; i++){
int ix = map(i, MIN_INDEX, MAX_INDEX, 0, NUM_PIXELS);
pixelData[ix] += inputData[i];
}
// Figure the max and min levels for the current frame
int most = pixelData[0], least = pixelData[0];
for(i=1; i<NUM_PIXELS; i++) {
if(pixelData[i] > most) most = pixelData[i];
if(pixelData[i] < least) least = pixelData[i];
}
// Always have some minimum space between, else it's too "jumpy"
if((most - least) < 15) most = least + 15;
// Dynamic max/min is sort of a fake rolling average...
maxLvl = (most > maxLvl) ?
(maxLvl * 3 + most + 1) / 4 : // Climb fast
(maxLvl * 31 + most + 15) / 32; // Fall slow
minLvl = (least < minLvl) ?
(minLvl * 3 + least + 3) / 4 : // Fall fast
(minLvl * 31 + least + 31) / 32; // Climb slow
//display the data
int n;
for(i=0; i<NUM_PIXELS; i++) {
// Scale pixel data to 0-511ish range based on dynamic levels
n = map(pixelData[i], minLvl, maxLvl, 0, 511);
if(n < 0) n = 0;
else if(n > 511) n = 511;
int r, g, b;
if(n < 256) {
// Lower half of range: interp from black to RGB pixel color
r = map(n, 0, 255, 0, reds[i]);
g = map(n, 0, 255, 0, greens[i]);
b = map(n, 0, 255, 0, blues[i]);
} else {
// Upper half of range: interp from RGB color to white
r = map(n, 256, 511, reds[i] , 255);
g = map(n, 256, 511, greens[i], 255);
b = map(n, 256, 511, blues[i] , 255);
}
strip.setPixelColor(i, gamma8[r], gamma8[g], gamma8[b], 0);
}
strip.show();
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment