Skip to content

Instantly share code, notes, and snippets.

@m-bartlett
Created November 20, 2021 02:45
Show Gist options
  • Save m-bartlett/043a5bfca79c7e36fc0e7370920dbc9b to your computer and use it in GitHub Desktop.
Save m-bartlett/043a5bfca79c7e36fc0e7370920dbc9b to your computer and use it in GitHub Desktop.
Elk NeoPixel Spectrum Power Visualizer with Spectrum Shield
//For spectrum analyzer shield, these three pins are used.
//You can move pinds 4 and 5, but you must cut the trace on the shield and re-route from the 2 jumpers.
#define RESET 5
#define STROBE 4
#define BUTTON_1 8
#define BUTTON_2 9
#include <Adafruit_NeoPixel.h> //Library to simplify interacting with the LED strip
#ifdef __AVR__
#include <avr/power.h> //Includes the library for power reduction registers if your chip supports them.
#endif //More info: http://www.nongnu.org/avr-libc/user-manual/group__avr__power.htlm
//Constants (change these as necessary)
#define LED_PIN 2 //Pin for the pixel strip. Can be analog or digital.
#define LED_TOTAL 50 //Change this to the number of LEDs in your strip.
#define SPEC 7
//int spectrumAnalog = 1; //0 for left channel, 1 for right.
Adafruit_NeoPixel strip = Adafruit_NeoPixel(LED_TOTAL, LED_PIN, NEO_GRB + NEO_KHZ800);
// Spectrum analyzer read values will be kept here.
double Lmax[SPEC] = {300}, Rmax[SPEC] = {300};
float Lspectrum[SPEC], Rspectrum[SPEC];
float avgMag = 300;
int8_t mode = 0;
int count = 600;
long unsigned int secs = 0;
void setup() {
Serial.begin(9600);
//Setup pins to drive the spectrum analyzer.
pinMode(RESET, OUTPUT);
pinMode(STROBE, OUTPUT);
pinMode(BUTTON_1, INPUT); pinMode(BUTTON_2, INPUT);
digitalWrite(BUTTON_1, HIGH); digitalWrite(BUTTON_2, HIGH);
//Init spectrum analyzer
digitalWrite(STROBE, LOW); delay(1);
digitalWrite(RESET, HIGH); delay(1);
digitalWrite(STROBE, HIGH); delay(1);
digitalWrite(STROBE, LOW); delay(1);
digitalWrite(RESET, LOW); delay(5);
// Reading the analyzer now will read the lowest frequency.
strip.begin();
strip.clear();
strip.show();
}
void loop() {
if (!digitalRead(BUTTON_1)) {
delay(300);
for (int j = 0; digitalRead(BUTTON_1); j += 2) {
if (j > kolor(mode, -1)) j = 0;
for (int i = 1; i < strip.numPixels(); i++) {
float val = double(kolor(mode, -1)) * (float(i) / float(strip.numPixels() - 1));
strip.setPixelColor(i, kolor(mode, val + j));
}
strip.show();
if (!digitalRead(BUTTON_2)) {
delay(300);
mode++;
if (mode == kolor(-1, 0)) mode = 0;
}
}
delay(300);
}
if (!digitalRead(BUTTON_2)) {
delay(300);
mode++;
if (mode == kolor(-1, 0)) mode = 0;
}
showSpectrum();
strip.show();
fade(.925);
if (round(fmod(millis(), 6)) == 0) count++;
if (count > kolor(mode,-1)) count = 0;
//delay(20);
}
uint32_t kolor(int8_t mo, int i) {
if (mo == 2) return Sea(i);
if (mo == 1) return Sunset(i);
if (mo == 0) return Rain(i);
if (mo < 0) return 3;
else return 0;
}
// Read 7 band equalizer.
void readSpectrum()
{
// Band 0 = Lowest Frequencies.
for (byte band = 0; band < SPEC; band++) {
Lspectrum[band] = (analogRead(0) + analogRead(0) ) >> 1; //Read twice and take the average by dividing by 2
if (Lspectrum[band] > Lmax[band]) Lmax[band] = Lspectrum[band];
Rspectrum[band] = (analogRead(1) + analogRead(1) ) >> 1;
if (Rspectrum[band] > Rmax[band]) Rmax[band] = Rspectrum[band];
digitalWrite(STROBE, HIGH);
digitalWrite(STROBE, LOW);
}
}
void showSpectrum() {
readSpectrum();
Lspectrum[SPEC - 1] = (Lspectrum[SPEC - 1] + Rspectrum[SPEC - 1]) / 2.0;
Rspectrum[SPEC - 1] = Lspectrum[SPEC - 1];
Lmax[SPEC - 1] = (Lmax[SPEC - 1] + Rmax[SPEC - 1]) / 2.0;
Rmax[SPEC - 1] = Lmax[SPEC - 1];
Lspectrum[0] = (Lspectrum[0] + Rspectrum[0]) / 2.0;
Rspectrum[0] = Lspectrum[0];
Lmax[0] = (Lmax[0] + Rmax[0]) / 2.0;
Rmax[0] = Lmax[0];
int jump = round((float(strip.numPixels()) / 2.0) / float(SPEC));
float aspec = 0, amax = 0;
const float POW = 7.0;
for (int i = 0; i < SPEC; i++) {
aspec += pow((Lspectrum[i] + Rspectrum[i]) / 2.0, 2);
amax += pow((Lmax[i] + Rmax[i]) / 2.0, 2);
float val = double(kolor(mode,-1)) * (float(i) / float(SPEC));
if (Lmax[i] > 200) {
Lmax[i] -= .2;
//if (i == 3) Serial.println(Lmax[i]);
}
else continue;
if (Lspectrum[i] > 90) {
double Lratio = Lspectrum[i] / Lmax[i];
Lratio = pow(Lratio, POW);
uint8_t Lpos = (i == 0) ? 5 : (i == 1) ? 1 : pos(5 - (i * jump));
//Lpos = (Lpos+count)%(strip.numPixels()-1);
//uint32_t Lcol = Rainbow(val + 600), Lcol2 = strip.getPixelColor(Lpos);
uint32_t Lcol = kolor(mode,val + count), Lcol2 = strip.getPixelColor(Lpos);
uint8_t Lcols[3], Lcols2[3];
for (int j = 0; j < 3; j++) {
Lcols[j] = split(Lcol, j) * Lratio;
Lcols2[j] = split(Lcol2, j);
}
float Lmag = sqrt( pow(Lcols[0], 2) + pow(Lcols[1], 2) + pow(Lcols[2], 2));
float Lmag2 = sqrt( pow(Lcols2[0], 2) + pow(Lcols2[1], 2) + pow(Lcols2[2], 2));
if ( Lmag > Lmag2 && Lmag > 30) {
Lcol = strip.Color(Lcols[0], Lcols[1], Lcols[2]);
strip.setPixelColor(Lpos, Lcol);
bleed(Lpos, jump);
}
}
if (Rmax[i] > 200) Rmax[i] -= .2;
else continue;
if (Rspectrum[i] > 90) {
double Rratio = Rspectrum[i] / Rmax[i];
Rratio = pow(Rratio, POW);
uint8_t Rpos = (5 + (i * jump)) % strip.numPixels();
//Rpos = (Rpos+count)%(strip.numPixels()-1);
//uint32_t Rcol = Rainbow(val + 600), Rcol2 = strip.getPixelColor(Rpos);
uint32_t Rcol = kolor(mode,val + count), Rcol2 = strip.getPixelColor(Rpos);
uint8_t Rcols[3], Rcols2[3];
for (int j = 0; j < 3; j++) {
Rcols[j] = split(Rcol, j) * Rratio;
Rcols2[j] = split(Rcol2, j);
}
float Rmag = sqrt( pow(Rcols[0], 2) + pow(Rcols[1], 2) + pow(Rcols[2], 2));
float Rmag2 = sqrt( pow(Rcols2[0], 2) + pow(Rcols2[1], 2) + pow(Rcols2[2], 2));
if ( Rmag > Rmag2 && Rmag > 30) {
Rcol = strip.Color(Rcols[0], Rcols[1], Rcols[2]);
strip.setPixelColor(Rpos, Rcol);
bleed(Rpos, jump);
}
}
}
aspec = sqrt(aspec); amax = sqrt(amax);
float aratio = aspec / amax;
if (aratio - avgMag >= .6) {
jump /= 2.0;
for (int i = 0; i < (SPEC - 1) * 2; i++) {
uint8_t Lpos = pos(3 - (i * jump));
uint8_t Rpos = (7 + (i * jump)) % strip.numPixels();
uint8_t cols[3];
for (int j = 0; j < 3; j++) {
cols[j] = ((255 * aratio) + split(strip.getPixelColor(i), j)) / 2.0;
}
uint32_t col = strip.Color(cols[0], cols[1], cols[2]);
strip.setPixelColor(Lpos, col);
strip.setPixelColor(Rpos, col);
bleed(Rpos, jump * 2);
bleed(Lpos, jump * 2);
}
}
avgMag = (avgMag + aratio) / 2.0;
strip.setPixelColor(0, 0);
}
uint8_t split(uint32_t color, uint8_t i ) { //0 = Red, 1 = Green, 2 = Blue
if (i == 0) return color >> 16;
if (i == 1) return color >> 8;
if (i == 2) return color >> 0;
return -1;
}
void fade(float damper) {
for (int i = 0; i < strip.numPixels(); i++) {
uint32_t col = strip.getPixelColor(i);
if (col == 0) continue;
float colors[3]; //Array of the three RGB values
for (int j = 0; j < 3; j++) colors[j] = split(col, j) * damper;
strip.setPixelColor(i, strip.Color(colors[0] , colors[1], colors[2]));
}
}
void bleed(uint8_t Point, uint8_t radius) {
for (int i = 1; i < radius; i++) {
int sides[] = {pos(Point - i), pos(Point + i)};
for (int i = 0; i < 2; i++) {
int point = sides[i];
uint32_t colors[] = {strip.getPixelColor(pos(point - 1)), strip.getPixelColor(point), strip.getPixelColor(pos(point + 1)) };
strip.setPixelColor(point, strip.Color(
float( split(colors[0], 0) + split(colors[1], 0) + split(colors[2], 0) ) / 3.0,
float( split(colors[0], 1) + split(colors[1], 1) + split(colors[2], 1) ) / 3.0,
float( split(colors[0], 2) + split(colors[1], 2) + split(colors[2], 2) ) / 3.0)
);
}
}
}
uint8_t pos(int pos) {
if (pos < 1) return (strip.numPixels() - 1) + pos;
if (pos >= strip.numPixels()) return (pos % (strip.numPixels() - 1));
return pos;
//return (strip.numPixels()-1 + pos) % strip.numPixels();
}
/*
uint32_t Rainbow(int i) {
const unsigned int LOOP = 1785;
if (i < 0) return LOOP;
if (i > LOOP - 1) return Rainbow(i % LOOP);
if (i > 1529) return strip.Color(255, 0, 255 - (i % 255)); //violet -> red
if (i > 1274) return strip.Color((i % 255), 0, 255); //blue -> violet
if (i > 1019) return strip.Color(0, 255 - (i % 255), 255); //aqua -> blue
if (i > 764) return strip.Color(0, 255, (i % 255)); //green -> aqua
if (i > 509) return strip.Color(255 - (i % 255), 255, 0); //yellow -> green
if (i > 255) return strip.Color(255, 128 + (i % 255) / 2, 0); //orange -> yellow
return strip.Color(255, i / 2, 0); //red -> orange
}
*/
uint32_t Rain(int i) {
const unsigned int LOOP = 1020;
if (i < 0) return LOOP;
if (i > LOOP - 1) return Rain(i % LOOP);
if (i > 764) return strip.Color(i % 255, 0, 255 - (i % 255)); //blue -> red
if (i > 509) return strip.Color(0, 255 - (i % 255), i % 255); //green -> blue
if (i > 255) return strip.Color(255 - (i % 255), 128 + (i % 255) / 2, 0); //orange ->green
return strip.Color(255, i / 2, 0); //red -> orange
}
uint32_t Sea(int i) {
const unsigned int LOOP = 765;
if (i < 0) return LOOP;
if (i > LOOP - 1) return Sea(i % LOOP);
if (i > 509) return strip.Color(i % 255, 255 - (i % 255) / 2, 255 - (i % 255)); //cyan -> orange
if (i > 255) return strip.Color(255 - (i % 255), i % 255, 255); //purple -> cyan
return strip.Color(255, 128 - (i / 2), i); //orange -> purple
}
uint32_t Sunset(int i) {
const unsigned int LOOP = 765;
if (i < 0) return LOOP;
if (i > LOOP - 1) return Sunset(i % LOOP);
if (i > 509) return strip.Color(255 - (i % 255), 128 - (i % 255) / 2, (i % 255)); //orange -> blue
if (i > 255) return strip.Color(255, (i % 255) / 2, 0); //red -> orange
return strip.Color(i, 0, 255 - i); //blue -> red
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment