16x32 LED Panel as Spectrum Analyzer (need help!)

Our weekly LIVE video chat. Every Wednesday at 8pm ET!

Moderators: adafruit_support_bill, adafruit

Please be positive and constructive with your questions and comments.
User avatar
kctheelephant
 
Posts: 4
Joined: Thu Sep 04, 2014 11:44 am

16x32 LED Panel as Spectrum Analyzer (need help!)

Post by kctheelephant »

So, I have a 16x32 LED Panel and I have configured it with an Arduino UNO and using Processing i have been able to make it a spectrum Analyzer.

What i really want to do is hook up an electret microphone so it does not need to be hooked up to Processing/my computer the entire time.
Having trouble finding help on this online.
I want to do something very similar to the Piccolo project on here, but with a 16x32 LED panel instead of the the 8x8.

Help??
Thanks!

User avatar
adafruit_support_rick
 
Posts: 35092
Joined: Tue Mar 15, 2011 11:42 am

Re: 16x32 LED Panel as Spectrum Analyzer (need help!)

Post by adafruit_support_rick »

Are you looking to do something like this?
https://learn.adafruit.com/piccolo/overview

User avatar
pburgess
 
Posts: 4161
Joined: Sun Oct 26, 2008 2:29 am

Re: 16x32 LED Panel as Spectrum Analyzer (need help!)

Post by pburgess »

This might work. Requires the ffft library (same as used by Piccolo). In ffft.h, FFT_N *must* be set to 64 for this to work. Also, you might need to tweak the CLK/LAT/OE pin assignments to match your matrix wiring.

Code: Select all

#include <avr/pgmspace.h>
#include <ffft.h>
#include <math.h>
#include <Adafruit_GFX.h>   // Core graphics library
#include <RGBmatrixPanel.h> // Hardware-specific library

#define CLK  8  // MUST be on PORTB!
#define LAT  9
#define OE   9
#define A   A0
#define B   A1
#define C   A2
RGBmatrixPanel matrix(A, B, C, CLK, LAT, OE, false);

#define       ADC_CHANNEL 3      // Analog pin
int16_t       capture[FFT_N];    // Audio capture buffer
complex_t     bfly_buff[FFT_N];  // FFT "butterfly" buffer
uint16_t      spectrum[FFT_N/2]; // Spectrum output buffer
volatile byte samplePos = 0;     // Buffer position counter
// In library file ffft.h, FFT_N *must* be set to 64.  Due to the RGB
// matrix overhead, there's only enough RAM for a 64 sample FFT buffer
// (32 spectrum outputs).  Any higher WILL fail!

byte
  peak[32],        // Peak level of each column; used for falling dots
  count = 0;       // Frame counter for delaying dot-falling speed
int
  minLvlAvg = 0,   // For dynamic adjustment of low & high ends of the graph,
  maxLvlAvg = 512; // pseudo rolling averages for the prior few frames.
// These two tables help the graph adjust dynamically to varying input:
PROGMEM uint8_t
  // This is low-level noise that's subtracted from each FFT output column:
  lower[32] = { 44,28,5,6,4,3,3,5,3,4,5,4,4,4,4,5,
                 3,4,4,3,4,4,4,4,4,3,4,3,4,3,4,5 },
  // These are scaling quotients for each column.  Partly for filtering out
  // low-frequency (~150 Hz) noise from the FFT, and also because most music
  // is simply heavy toward the low end (rhythm, bass).
  upper[32] = { 200,160,90,48,23,11,5,3,1,1,0,0,0,0,0,0,
                  0,  0, 0, 0, 0, 0,0,0,0,0,0,0,0,0,0,0 };

void setup() {
  memset(peak, 0, sizeof(peak));
  matrix.begin();

  // Init ADC free-run mode; f = ( 16MHz/prescaler ) / 13 cycles/conversion 
  ADMUX  = ADC_CHANNEL; // Channel sel, right-adj, use AREF pin
  ADCSRA = _BV(ADEN)  | // ADC enable
           _BV(ADSC)  | // ADC start
           _BV(ADATE) | // Auto trigger
           _BV(ADIE)  | // Interrupt enable
           _BV(ADPS2) | _BV(ADPS1) | _BV(ADPS0); // 128:1 / 13 = 9615 Hz
  ADCSRB = 0;                // Free run mode, no high MUX bit
  DIDR0  = 1 << ADC_CHANNEL; // Turn off digital input for ADC pin
  TIMSK0 = 0;                // Timer0 off

  sei(); // Enable interrupts
}

void loop() {
  char     c;
  byte     x, y, y2, r, g, b;
  int      hue, minLvl, maxLvl;
  uint16_t n;
  long     level;

  while(ADCSRA & _BV(ADIE)); // Wait for audio sampling to finish

  fft_input(capture, bfly_buff);   // Samples -> complex #s
  samplePos = 0;                   // Reset sample counter
  ADCSRA |= _BV(ADIE);             // Resume sampling interrupt
  fft_execute(bfly_buff);          // Process complex data
  fft_output(bfly_buff, spectrum); // Complex -> spectrum

  // Get min & max levels in FFT output:
  for(x=0; x<32; x++) {
    level = (long)spectrum[x] - (long)pgm_read_byte(&lower[x]); // rm noise
    if(level < 0L) level = 0L; // Clip...else fixed-point scale:
    else           level = (level * (256L - pgm_read_byte(&upper[x]))) >> 8;

    if(x == 0) {
      minLvl = maxLvl = level;
    } else {
      if(level < minLvl)      minLvl = level;
      else if(level > maxLvl) maxLvl = level;
    }
  }
  // minLvl and maxLvl indicate the extents of the FFT output, used
  // for vertically scaling the output graph (so it looks interesting
  // regardless of volume level).  If they're too close together though
  // (e.g. at very low volume levels) the graph becomes super coarse
  // and 'jumpy'...so keep some minimum distance between them (this
  // also lets the graph go to zero when no sound is playing):
  if((maxLvl - minLvl) < 8) maxLvl = minLvl + 8;

  minLvlAvg = (minLvlAvg * 7 + minLvl) >> 3; // Dampen min/max levels
  maxLvlAvg = (maxLvlAvg * 7 + maxLvl) >> 3; // (fake rolling average)

  // Render display:
  for(x=0, hue=0; x<32; x++, hue += 40) { // For each column...
    level = (long)spectrum[x] - (long)pgm_read_byte(&lower[x]); // rm noise
    if(level < 0L) level = 0L; // Clip...else fixed-point scale:
    else           level = (level * (256L - pgm_read_byte(&upper[x]))) >> 8;
    // Second fixed-point scale based on dynamic min/max levels:
    level = ((level - minLvlAvg) * 16L) / (long)(maxLvlAvg - minLvlAvg);

    // Clip output and convert to byte (column height...0 to 16):
    if(level < 0L)       c = 0;
    else if(level > 16L) c = 16;
    else                 c = (char)level;

    if(c > peak[x]) peak[x] = c; // Keep white dot on top

    if(peak[x] <= 0) { // Empty column?
      matrix.drawLine(x, 0, x, 15, 0);
      continue;
    }

    y = 16 - peak[x]; // y = 0 to 15
    if(y > 0) matrix.drawLine(x, 0, x, y-1, 0); // Fill black above peak
    // WILL CRASH HERE if out of RAM!
    matrix.drawPixel(x, y, 0xffff); // Draw white dot at peak

    // Draw fading streak from below peak to just above current level
    for(y++, y2 = 16 - c, n=150; y < y2; y++) {
      matrix.drawPixel(x, y, matrix.ColorHSV(hue, 255, n, true));
      if(n >= 50) n -= 50;
    }

    // Draw bar for current level, with slight glow at top
    y = y2;
    n = 100;
    // If current level is peak, don't overwrite white dot
    if(c == peak[x]) {
      y++;
      n = 150;
    }
    while(y < 16) {
      matrix.drawPixel(x, y, matrix.ColorHSV(hue, n, 255, true));
      n += 50;
      if(n > 255) n = 255;
      y++;
    }
  }

  // Every alternate frame, make the peak pixels drop by 1:
  if(++count == 1) {
    count = 0;
    for(x=0; x<32; x++) {
      if(peak[x] > 0) peak[x]--;
    }
  }
}

ISR(ADC_vect) { // Audio-sampling interrupt
  static const int16_t noiseThreshold = 4;
  int16_t              sample         = ADC; // 0-1023

  capture[samplePos] =
    ((sample > (512-noiseThreshold)) &&
     (sample < (512+noiseThreshold))) ? 0 :
    sample - 512; // Sign-convert for FFT; -512 to +511

  if(++samplePos >= FFT_N) ADCSRA &= ~_BV(ADIE); // Buffer full, interrupt off
}

User avatar
kctheelephant
 
Posts: 4
Joined: Thu Sep 04, 2014 11:44 am

Re: 16x32 LED Panel as Spectrum Analyzer (need help!)

Post by kctheelephant »

The code you gave me seems like it will work when i understand it better! i'm a little lost on all the #defines though. for my 16x32 panel i have plugged it into my Arduino just like the Adafruit wiring guide says.
Could you explain your code a little more? Or guide me to more tutorials on this type of code?

Thank you!

User avatar
pburgess
 
Posts: 4161
Joined: Sun Oct 26, 2008 2:29 am

Re: 16x32 LED Panel as Spectrum Analyzer (need help!)

Post by pburgess »

Hmmm...actually I'd suggest changing one wire vs. the standard tutorials: move the LAT wire from pin A3 to pin 9 (you'll probably need to use a breadboard or solder up a little Y-splitter jumper, since this pin is now going both to LAT and OE). 3.3V goes to mic amp + and the Arduino's AREF pin. Mic output goes to A3. Mic - to GND. Then use the code as-is.

As for explaining it...erf...this is actually some pretty hairy stuff and would fill a book. If there's anything specific that's not covered in the comments, I could try to offer some guidance, but otherwise it's extremely non-trivial. I just had it around from a project we never released, posted it since it pretty much matches your description.

User avatar
kctheelephant
 
Posts: 4
Joined: Thu Sep 04, 2014 11:44 am

Re: 16x32 LED Panel as Spectrum Analyzer (need help!)

Post by kctheelephant »

I tried what you suggested in the previous comment and used your code as is, i got no results. Then i tried moving the LAT to a3, the mic to A4, and the OE at pin 9, and editted the code accordingly, still with no results.
Any ideas?

User avatar
pburgess
 
Posts: 4161
Joined: Sun Oct 26, 2008 2:29 am

Re: 16x32 LED Panel as Spectrum Analyzer (need help!)

Post by pburgess »

Use the wiring layout suggested; this was chosen for a reason, though it's been so long I can't remember exactly what that reason was. But I digress...

Did you change the ffft.h library as described?

If it still refuses to work, I would suggest first trying to get some of the other RGBmatrix library examples working with the alternate wiring as described (LAT on pin 9, should require just a one-line change to most of the demos). If it doesn't work, then either A) the wiring is wrong, or B) the code doesn't match the wiring. I can't help troubleshoot this further without A) clear photos of the wiring between Arduino and matrix, and B) the pin #defines and RGBmatrix constructor call from the beginning of the sketch.

Once you get that working, then try the full code I previously posted here.

User avatar
kctheelephant
 
Posts: 4
Joined: Thu Sep 04, 2014 11:44 am

Re: 16x32 LED Panel as Spectrum Analyzer (need help!)

Post by kctheelephant »

Where/what exactly in the fft needs to be changed? I'm struggling to find it.

User avatar
pburgess
 
Posts: 4161
Joined: Sun Oct 26, 2008 2:29 am

Re: 16x32 LED Panel as Spectrum Analyzer (need help!)

Post by pburgess »

Second line in ffft.h:

Code: Select all

#define FFT_N 128 /* Number of samples (64,128,256,512). */
Should be changed to:

Code: Select all

#define FFT_N 64 /* Number of samples (64,128,256,512). */

User avatar
rb3
 
Posts: 1
Joined: Sun Feb 24, 2013 6:46 pm

Re: 16x32 LED Panel as Spectrum Analyzer (need help!)

Post by rb3 »

Is the the correct modification do get around the update to Ardunio 1.6.6?

Code: Select all

PROGMEM const uint8_t
  // This is low-level noise that's subtracted from each FFT output column:
  lower[32] = { 44,28,5,6,4,3,3,5,3,4,5,4,4,4,4,5,
                 3,4,4,3,4,4,4,4,4,3,4,3,4,3,4,5 },
  // These are scaling quotients for each column.  Partly for filtering out
  // low-frequency (~150 Hz) noise from the FFT, and also because most music
  // is simply heavy toward the low end (rhythm, bass).
  upper[32] = { 200,160,90,48,23,11,5,3,1,1,0,0,0,0,0,0,
                  0,  0, 0, 0, 0, 0,0,0,0,0,0,0,0,0,0,0 };

User avatar
adafruit_support_rick
 
Posts: 35092
Joined: Tue Mar 15, 2011 11:42 am

Re: 16x32 LED Panel as Spectrum Analyzer (need help!)

Post by adafruit_support_rick »

Yes, that will work

User avatar
gbalici
 
Posts: 5
Joined: Wed Aug 31, 2016 3:09 pm

Re: 16x32 LED Panel as Spectrum Analyzer (need help!)

Post by gbalici »

Hello, what to use if instead of a mic I want to use a line in?
Could that be connected to A4?
Why LAT and OE are connected to pin 9? Shouldn't LAT be connected to A3?

Thanks!

User avatar
adafruit_support_rick
 
Posts: 35092
Joined: Tue Mar 15, 2011 11:42 am

Re: 16x32 LED Panel as Spectrum Analyzer (need help!)

Post by adafruit_support_rick »

What is the voltage of the line-in signal? it should be biased so that the range is 0 to 5V (0 to 3.3V if you're using a 3.3V processor.

User avatar
adafruit_support_rick
 
Posts: 35092
Joined: Tue Mar 15, 2011 11:42 am

Re: 16x32 LED Panel as Spectrum Analyzer (need help!)

Post by adafruit_support_rick »

gbalici wrote:Why LAT and OE are connected to pin 9? Shouldn't LAT be connected to A3?
As Phil said in his earlier post:
Use the wiring layout suggested; this was chosen for a reason, though it's been so long I can't remember exactly what that reason was. But I digress...

User avatar
gbalici
 
Posts: 5
Joined: Wed Aug 31, 2016 3:09 pm

Re: 16x32 LED Panel as Spectrum Analyzer (need help!)

Post by gbalici »

I was hoping you'd remember it :)

Locked
Please be positive and constructive with your questions and comments.

Return to “Ask an Engineer! VIDEO CHAT (closed)”