Skip to content

Instantly share code, notes, and snippets.

@jwhendy
Last active September 26, 2017 18:11
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jwhendy/c252ad78c4f6cefeeee82e85a24cb300 to your computer and use it in GitHub Desktop.
Save jwhendy/c252ad78c4f6cefeeee82e85a24cb300 to your computer and use it in GitHub Desktop.
Code used to run some fun animations/routines on a 16x16 WS2812 LED matrix
/*
* Based on SpectrumAnalyzerBasic by Paul Stoffregen included in the Teensy Audio Library
* Modified by Jason Coon for the SmartMatrix Library
* Requires Teensyduino 1.20 or higher and the Teensy Audio Library
* Also requires FastLED 3.1 or higher
* If you are having trouble compiling, see
* the troubleshooting instructions here:
* https://github.com/pixelmatix/SmartMatrix/#external-libraries
*
* actual code found:
* https://raw.githubusercontent.com/pixelmatix/SmartMatrix/master/examples/SpectrumAnalyzer/SpectrumAnalyzer.ino
*
* Requires the following libraries:
* Teensy Audio Library: https://github.com/PaulStoffregen/Audio
* SerialFlash Library (a dependency of the Teensy Audio Library): https://github.com/PaulStoffregen/SerialFlash
* FastLED v3.1 or higher: https://github.com/FastLED/FastLED/releases
*
* Uses line in on pin A2. For more information, and a recommended analog input circuit, see: http://www.pjrc.com/teensy/gui/?info=AudioInputAnalog
*
* You can change the pin used for ADC with the ADC_INPUT_PIN definition below.
* There are no dedicated ADC pins brought out on the SmartMatrix Shield,
* but even if you've used all the pins on the SmartMatrix expansion header,
* you can use solder pins directly to the Teensy to use A14/DAC, A11, or A10
*
* This SmartMatrix example uses just the background layer
*/
// all these libraries are required for the Teensy Audio Library
#define FASTLED_ALLOW_INTERRUPTS 0
#include <Audio.h>
#include <Wire.h>
#include <SPI.h>
#include <SD.h>
#include <FastLED.h>
#include <RotaryEncoder.h>
#define DATA_PIN 22
#define LED_TYPE WS2812B
#define COLOR_ORDER GRB
#define NUM_LEDS 256
CRGB leds[NUM_LEDS];
// adxl device for wire.h
#define DEVICE (0x53)
#define ADC_INPUT_PIN A2
// adxl setup
byte _buff[6];
char POWER_CTL = 0x2D; //Power Control Register
char DATA_FORMAT = 0x31;
char DATAX0 = 0x32; //X-Axis Data 0
char DATAX1 = 0x33; //X-Axis Data 1
char DATAY0 = 0x34; //Y-Axis Data 0
char DATAY1 = 0x35; //Y-Axis Data 1
char DATAZ0 = 0x36; //Z-Axis Data 0
char DATAZ1 = 0x37; //Z-Axis Data 1
uint8_t howManyBytesToRead = 6;
AudioInputAnalog input(ADC_INPUT_PIN);
AudioAnalyzeFFT256 fft;
AudioConnection audioConnection(input, 0, fft, 0);
int mode_aud = 0;
int mode_main = 0;
int16_t x_raw = 0;
int16_t y_raw = 0;
int16_t z_raw = 0;
int x_off = -129;
int y_off = 25;
int z_off = 1742;
int x_rng = 595;
int y_rng = 583;
int z_rng = 522;
float acc_x; float acc_y; float acc_z;
long now = millis();
float cx = 7;
float cy = 8;
float x = 0;
float y = 0;
float rad = 0;
int hue = 0;
int delta = 3;
int bright = 255;
int sat = 255;
int fill = 0;
long pulse = 0;
long running = 0;
//int x = 0;
//int y = 0;
RotaryEncoder enc_y(12, 11);
RotaryEncoder enc_x(15, 14);
int pin_enc_x = 10;
int pin_enc_y = 17;
// The scale sets how much sound is needed in each frequency range to
// show all 16 bars. Higher numbers are more sensitive.
float scale = 3000.0;
// An array to hold the 16 frequency bands
float level[16];
// This array holds the current max seen. We can't know ahead of time
// how big the signal will be, so this increases it per channel
// so that the max-seen-so-far corresponds to lighting all 16 pixels
int cur_max[16];
int cur_min[16];
void setup()
{
Serial.begin(9600);
pinMode(A9, INPUT);
pinMode(pin_enc_x, INPUT_PULLUP);
pinMode(pin_enc_y, INPUT_PULLUP);
// join i2c bus (address optional for master)
Wire.begin();
// Put the ADXL345 into +/- 4G range by writing the value 0x01 to the DATA_FORMAT register.
// think 0x00 = 2g, 00x1 = 4g, 00x2 = 8g, and 00x3 = 16g
writeTo(DATA_FORMAT, 0x0B);
//Put the ADXL345 into Measurement Mode by writing 0x08 to the POWER_CTL register.
writeTo(POWER_CTL, 0x08);
// Initialize Matrix
FastLED.addLeds<LED_TYPE, DATA_PIN, COLOR_ORDER>(leds, NUM_LEDS);
FastLED.setBrightness(200);
// Audio requires memory to work.
AudioMemory(12);
// initialize our maximums to some guess (10)
// from watching serial, the actual values looks to be ~30ish
for(int i = 0; i < 16; i++) { cur_max[i] = 0; }
for(int i = 0; i < 16; i++) { cur_min[i] = 0; }
FastLED.clear();
FastLED.show();
}
void loop()
{
now = millis();
int set = 0;
while((digitalRead(pin_enc_x) == LOW) & (digitalRead(pin_enc_y) == LOW))
{
if((millis() - now > 2000) & (set == 0))
{
FastLED.clear();
FastLED.show();
mode_main = mode_main + 1;
if(mode_main > 2) { mode_main = 0; }
set = 1;
}
}
if ((fft.available()) & (mode_main == 0))
{
// read the 128 FFT frequencies into 16 levels
// music is heard in octaves, but the FFT data
// is linear, so for the higher octaves, read
// many FFT bins together.
// I'm skipping the first two bins, as they seem to be unusable
// they start out at zero, but then climb and don't come back down
// even after sound input stops
level[0] = fft.read(1);
level[1] = fft.read(2);
level[2] = fft.read(3);
level[3] = fft.read(4);
level[4] = fft.read(5, 7);
level[5] = fft.read(7, 8);
level[6] = fft.read(9, 12);
level[7] = fft.read(13, 18);
level[8] = fft.read(19, 24);
level[9] = fft.read(25, 31);
level[10] = fft.read(32, 41);
level[11] = fft.read(42, 52);
level[12] = fft.read(53, 65);
level[13] = fft.read(66, 82);
level[14] = fft.read(83, 103);
level[15] = fft.read(104, 127);
for (int i = 0; i < 16; i++)
{
if(cur_max[i] < (level[i] * scale)) { cur_max[i] = level[i] * scale; }
cur_min[i] = (cur_max[i] / 25) + 1;
}
switch(mode_aud)
{
case 0:
color_per_band();
break;
case 1:
rainbow_bands();
break;
case 2:
rainbow_bands_cycle();
break;
case 3:
wild();
break;
case 4:
wild_split();
break;
case 5:
rainbow_bands_split();
break;
case 6:
rainbow_bands_cycle_split();
break;
case 7:
color_per_band_split();
break;
}
EVERY_N_MILLISECONDS(10) { fadeToBlackBy(leds, 256, 25); }
EVERY_N_MILLISECONDS(5000)
{
for (int i = 0; i < 16; i++)
{
cur_max[i] = int(cur_max[i] * 0.98);
}
} // every_n
EVERY_N_MILLISECONDS(10000)
{
mode_aud = mode_aud + 1;
if(mode_aud > 7) { mode_aud = 0; }
}
} // if()
if(mode_main == 1)
{
enc_x.tick();
enc_y.tick();
x = enc_x.getPosition();
y = enc_y.getPosition();
hue = beatsin8(12, 0, 150);
EVERY_N_MILLISECONDS(20) { fadeToBlackBy(leds, NUM_LEDS, 1); }
leds[(int(x)*16) + int(y)] = CHSV(hue, 255, 100);
FastLED.show();
}
if(mode_main == 2)
{
EVERY_N_MILLISECONDS(50)
{
//read the acceleration data from the ADXL345
readFrom(DATAX0, howManyBytesToRead, _buff);
x_raw = (((int)_buff[1]) << 8) | _buff[0];
y_raw = (((int)_buff[3]) << 8) | _buff[2];
z_raw = (((int)_buff[5]) << 8) | _buff[4];
acc_x = (x_raw - x_off) * (2.0/x_rng);
acc_y = (y_raw - y_off) * (2.0/y_rng);
acc_z = (z_raw - z_off) * (2.0/z_rng);
now = millis();
}
cx = constrain(cx - acc_y, 2, 13);
cy = constrain(cy - acc_x, 2, 13);
EVERY_N_MILLISECONDS(10) { fadeToBlackBy(leds, 256, 10); }
for(int i = 0; i < 16; i++)
{
for(int j = 0; j < 16; j++)
{
x = abs(i - cx);
y = abs(j - cy);
hue = beatsin8(10, 0, 255);
rad = sq(x) + sq(y);
if(rad <= 8)
{
//hue = constrain(map(rad, 0, 5, hue, hue + 120), hue, hue + 220);
hue = map(rad, 0, 8, hue, hue + 120);
leds[(i*16) + j] = CHSV(hue, 255, 255);
}
}
}
FastLED.show();
}
} // loop()
void color_per_band()
{
for (int i = 0; i < 16; i++)
{
fill = constrain(map(level[i] * scale, cur_min[i], int(cur_max[i]), 0, 16), 0, 16);
// this will give you a distinct color per bar cycling through the rainbow
fill_solid(leds + (i*16) + (16 - fill), fill, CHSV(i*220/16, 255, 255));
} // for()
FastLED.show();
}
void rainbow_bands()
{
delta = 12;
for (int i = 0; i < 16; i++)
{
fill = constrain(map(level[i] * scale, cur_min[i], int(cur_max[i]), 0, 16), 0, 16);
// this will provide a changing rainbow continuous across all bars
// rainbow always starts blue, goes to red
// fill_rainbow(leds + (i*16), fill, 160, -(160 / 16));
fill_rainbow(leds + ((i*16) + (16 - fill)), fill, 140 + (delta*fill), -delta);
} // for()
FastLED.show();
}
void rainbow_bands_cycle()
{
delta = 12;
for (int i = 0; i < 16; i++)
{
fill = constrain(map(level[i] * scale, cur_min[i], int(cur_max[i]), 0, 16), 0, 16);
// like the above, but start color constantly changes
fill_rainbow(leds + ((i*16) + (16 - fill)), fill, hue + (delta*fill), -delta);
EVERY_N_MILLISECONDS(10) { hue = hue + 1; }
} // for()
FastLED.show();
}
void wild()
{
for (int i = 0; i < 16; i++)
{
fill = constrain(map(level[i] * scale, cur_min[i], int(cur_max[i]), 0, 16), 0, 16);
// like the first one (solid color per band), but rotates teh colors
fill_solid(leds + ((i*16) + (16 - fill)), fill, CHSV(hue + (i*220/16), 255, 255));
EVERY_N_MILLISECONDS(3) { hue = hue + 5; }
} // for()
FastLED.show();
}
void wild_split()
{
for (int i = 0; i < 16; i++)
{
fill = constrain(map(level[i] * scale, cur_min[i], int(cur_max[i]), 0, 16), 0, 16);
// like first fill_solid, but bars rotate through colors
int filly = int(fill / 2);
fill_solid(leds + (i*16), filly, CHSV(hue + (i*220/16), 255, 255));
fill_solid(leds + (i*16) + (16-filly), filly, CHSV(hue - (i*220/16), 255, 255));
EVERY_N_MILLISECONDS(3) { hue = hue + 5; }
} // for()
FastLED.show();
}
void color_per_band_split()
{
for (int i = 0; i < 16; i++)
{
fill = constrain(map(level[i] * scale, cur_min[i], int(cur_max[i]), 0, 16), 0, 16);
// this will give you a distinct color per bar cycling through the rainbow
int filly = int(fill / 2);
fill_solid(leds + (i*16), filly, CHSV(i*220/16, 255, 255));
fill_solid(leds + (i*16) + (16 - filly), filly, CHSV(i*220/16, 255, 255));
} // for()
FastLED.show();
}
void rainbow_bands_split()
{
delta = 25;
for (int i = 0; i < 16; i++)
{
fill = constrain(map(level[i] * scale, cur_min[i], int(cur_max[i]), 0, 16), 0, 16);
// this will provide a changing rainbow continuous across all bars
// rainbow always starts blue, goes to red
// fill_rainbow(leds + (i*16), fill, 160, -(160 / 16));
int filly = int(fill / 2);
fill_rainbow(leds + (i*16) + (16 - filly), filly, 160 + (-delta*(filly-1)), delta);
fill_rainbow(leds + (i*16), filly, 160, -delta);
} // for()
FastLED.show();
}
void rainbow_bands_cycle_split()
{
delta = 12;
for (int i = 0; i < 16; i++)
{
fill = constrain(map(level[i] * scale, cur_min[i], int(cur_max[i]), 0, 16), 0, 16);
// like the above, but start color constantly changes
int filly = int(fill / 2);
fill_rainbow(leds + (i*16) + (16 - filly), filly, hue + (-delta*(filly-1)), delta);
fill_rainbow(leds + (i*16), filly, hue, -delta);
//fill_rainbow(leds + ((i*16) + (16 - fill)), fill, hue + (delta*fill), -delta);
EVERY_N_MILLISECONDS(10) { hue = hue + 1; }
} // for()
FastLED.show();
}
void writeTo(byte address, byte val) {
Wire.beginTransmission(DEVICE); // start transmission to device
Wire.write(address); // send register address
Wire.write(val); // send value to write
Wire.endTransmission(); // end transmission
}
// Reads num bytes starting from address register on device in to _buff array
void readFrom(byte address, int num, byte _buff[]) {
Wire.beginTransmission(DEVICE); // start transmission to device
Wire.write(address); // sends address to read from
Wire.endTransmission(); // end transmission
Wire.beginTransmission(DEVICE); // start transmission to device
Wire.requestFrom(DEVICE, num); // request 6 bytes from device
int i = 0;
while (Wire.available()) // device may send less than requested (abnormal)
{
_buff[i] = Wire.read(); // receive a byte
i++;
}
Wire.endTransmission(); // end transmission
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment