#define LIN_OUT 1 //FHT linear output magnitude
#define FHT_N 128 //set SAMPLES for FHT, Must be a power of 2
#include <FHT.h>
#define xres 31 //Total number of columns in the display, must be <= SAMPLES/2
#define yres 14 //Total number of rows in the display
#define PIN 6 //out pint to control Leds
#define NUM_LEDS (xres * yres) //total leds in Matrix
#include <Adafruit_NeoPixel.h>
#define colorPIN 5 //pin to change ledcolor
#define brightnessPIN 10 //pin to change brightness
byte displaycolor = 0; //default color value
byte brightness = 1; //default brightness level
#include <EEPROM.h>
#define CONFIG_START 32 //Memory start location
#define CONFIG_VERSION "VER01" //Config version configuration
typedef struct {
char version[6];
byte displaycolor;
byte brightness;
} configuration_type;
configuration_type CONFIGURATION = {
CONFIG_VERSION,
displaycolor,
brightness
};
byte yvalue;
int peaks[xres];
byte state = HIGH; // the current reading from the input pin
byte previousState = LOW; // the previous reading from the input pin
unsigned long lastDebounceTime = 0; // the last time the output pin was toggled
unsigned long debounceDelay = 100; // the debounce time; increase if the output flickers
byte data_avgs[xres]; //Array for samplig
// Parameter 1 = number of leds in matrix
// Parameter 2 = pin number
// Parameter 3 = pixel type flags, add together as needed:
// NEO_KHZ800 800 KHz bitstream (most NeoPixel products w/WS2812 LEDs)
// NEO_KHZ400 400 KHz (classic 'v1' (not v2) FLORA pixels, WS2811 drivers)
// NEO_GRB Pixels are wired for GRB bitstream (most NeoPixel products)
// NEO_RGB Pixels are wired for RGB bitstream (v1 FLORA pixels, not v2)
Adafruit_NeoPixel pixel = Adafruit_NeoPixel(NUM_LEDS, PIN, NEO_GRB + NEO_KHZ800);
// EQ filter
byte eq[32] = {
60, 65, 70, 75, 80, 85, 90, 95,
100, 100, 100, 100, 100, 100, 100, 100,
100, 100, 100, 100, 100, 100, 100, 100,
115, 125, 140, 160, 185, 200, 200, 200
};
bool EQ_ON = true; // set to false to disable eq
//Define 5 set of colors for leds, 0 for single custom color
byte colors[][8] = {
{170, 160, 150, 140, 130, 120, 1, 1},
{1, 5, 10, 15, 20, 25, 90, 90},
{90, 85, 80, 75, 70, 65, 1, 1},
{90, 90, 90, 30, 30, 30, 1, 1},
{170, 160, 150, 140, 130, 120, 110, 0}
};
void setup() {
pixel.begin(); //initialize Led Matrix
//Begin FFT operations
ADCSRA = 0b11100101; // set ADC to free running mode and set pre-scaler to 32 (0xe5)
ADMUX = 0b00000000; // use pin A0 and external voltage reference
// Read config data from EEPROM
// if (loadConfig()) {
// displaycolor = CONFIGURATION.displaycolor;
// brightness = CONFIGURATION.brightness;
// }
//Set brightness loaded from EEPROM
pixel.setBrightness(brightness * 24 + 8);
//Show current config on start
//change true to false if you don't want this
//showSettings(3, false);
}
void loop() {
while (1) { // reduces jitter
Sampling(); // FHT Library use only one data array
RearrangeFHT(); // re-arrange FHT result to match with no. of display columns
SendToDisplay(); // send to display according measured value
delay(10); // delay to reduce flickering (FHT is too fast :D)
}
}
void Sampling() {
for (int i = 0; i < FHT_N; i++) {
while (!(ADCSRA & 0x10)); // wait for ADC to complete current conversion ie ADIF bit set
ADCSRA = 0b11110101 ; // clear ADIF bit so that ADC can do next operation (0xf5)
//ADLAR bit is 0, so the 10 bits of ADC Data registers are right aligned
byte m = ADCL; // fetch adc data
byte j = ADCH;
int value = (j << 8) | m; // form into an int
value -= 0x0200; // form into a signed int
value <<= 6; // form into a 16b signed int
fht_input[i] = value / 8; // copy to fht input array after compressing
}
// ++ begin FHT data process -+-+--+-+--+-+--+-+--+-+--+-+--+-+-
fht_window(); // window the data for better frequency response
fht_reorder(); // reorder the data before doing the fht
fht_run(); // process the data in the fht
fht_mag_lin(); // take the output of the fht
}
void RearrangeFHT() {
// FHT return real value unsing only one array
// after fht_mag_lin() calling the samples value are in
// the first FHT_N/2 position of the array fht_lin_out[]
int step = (FHT_N / 2) / xres;
int c = 0;
for (int i = 0; i < (FHT_N / 2); i += step) {
data_avgs[c] = 0;
for (int k = 0 ; k < step ; k++) {
data_avgs[c] = data_avgs[c] + fht_lin_out[i + k]; // linear output magnitude
}
data_avgs[c] = data_avgs[c] / step ; // save avgs value
c++;
}
}
void SendToDisplay() {
for (int i = 0; i < xres; i++) {
if (EQ_ON)
data_avgs[i] = data_avgs[i] * (float)(eq[i]) / 100; // apply eq filter
data_avgs[i] = constrain(data_avgs[i], 0, 80); // set max & min values for buckets to 0-80
data_avgs[i] = map(data_avgs[i], 0, 80, 0, yres); // remap averaged values to yres 0-8
yvalue = data_avgs[i];
peaks[i] = peaks[i] - 1; // decay by one light
if (yvalue > peaks[i]) peaks[i] = yvalue; // save peak if > previuos peak
yvalue = peaks[i]; // pick peak to display
setColumn(i, yvalue); // draw columns
}
pixel.show(); // show column
}
// Light up leds of x column according to y value
void setColumn(int x, int y) {
int led, i;
for (i = 0; i < yres; i++) {
led = GetLedFromMatrix(x, i); //retrieve current led by x,y coordinates
if (peaks[x] > i) {
switch (displaycolor) {
case 4:
if (colors[displaycolor][i] == 0) {
// show custom color with zero value in array
pixel.setPixelColor(led, 255, 255, 255); //withe
}
else {
// standard color defined in colors array
pixel.setPixelColor(led, Wheel(colors[displaycolor][i]));
}
break;
case 5:
//change color by column
pixel.setPixelColor(led, Wheel(x * 16));
break;
case 6:
//change color by row
pixel.setPixelColor(led, Wheel(i * y * 3));
break;
case 7:
//change color by... country :D
//Italy flagh
//if (x < 11) pixel.setPixelColor(led, 0, 255, 0);
//if (x > 10 && x < 21) pixel.setPixelColor(led, 255, 255, 255);
//if (x > 20) pixel.setPixelColor(led, 255, 0, 0);
//stars and stripes
if (i < yres - 2) {
if (x & 0x01) {
pixel.setPixelColor(led, 0, 0, 255);
}
else {
pixel.setPixelColor(led, 255, 0, 0);
}
}
else {
pixel.setPixelColor(led, 255, 255, 255);
}
break;
default:
//display colors defined in color array
pixel.setPixelColor(led, Wheel(colors[displaycolor][i]));
} //END SWITCH
}
else {
//Light off leds
pixel.setPixelColor(led, pixel.Color(0, 0, 0));
}
}
}
//================================================================
// Calculate a led number by x,y coordinates
// valid for WS2812B with serpentine layout placed in horizzontal
// and zero led at bottom right (DIN connector on the right side)
// input value: x= 0 to xres-1 , y= 0 to yres-1
// return a led number from 0 to NUM_LED
//================================================================
int GetLedFromMatrix(int x, int y) {
int led;
if (y & 0x01) {
//odd columns increase backwards
led = xres*(y+1)-x-1;
}
else {
//even columns increase normally
led = x+xres*(y);
}
return constrain(led, 0, NUM_LEDS);
}
//================================================================
// Utility from Adafruit Neopixel demo sketch
// Input a value 0 to 255 to get a color value.
// The colours are a transition R - G - B - back to R.
unsigned long Wheel(byte WheelPos) {
WheelPos = 255 - WheelPos;
if (WheelPos < 85) {
return pixel.Color(255 - WheelPos * 3, 0, WheelPos * 3);
}
if (WheelPos < 170) {
WheelPos -= 85;
return pixel.Color(0, WheelPos * 3, 255 - WheelPos * 3);
}
WheelPos -= 170;
return pixel.Color(WheelPos * 3, 255 - WheelPos * 3, 0);
}
//by Janux®, Last version on 28/06/2020.