Go Down

Topic: Issue with FFT and analogWrite (Read 2593 times) previous topic - next topic

vmladenov

Jan 05, 2015, 03:47 am Last Edit: Jan 05, 2015, 04:45 am by vmladenov
I have made good progress since my last post. This is my schematic. First time using Fritzing, sorry for the bad wiring!

Code: [Select]
#include <stdint.h>
#include <ffft.h>
 
#define  IR_AUDIO  0 // ADC channel to capture

#define REDPIN 5
#define GREENPIN 6
#define BLUEPIN 3

volatile  byte  position = 0;
volatile  long  zero = 0;

int16_t capture[FFT_N]; /* Wave captureing buffer */
complex_t bfly_buff[FFT_N]; /* FFT buffer */
uint16_t spectrum[FFT_N/2]; /* Spectrum output buffer */
uint16_t spectrumPrior[FFT_N/2]; // prior of spectrum
float levelsPrior[3];

typedef struct _RGB {
byte r;
byte g;
byte b;
} RGB;

RGB led;

void setRGB(int r, int g, int b)
{
// sanitize inputs
if (r < 0) r = 0; if (r > 255) r = 255;
if (g < 0) g = 0; if (g > 255) g = 255;
if (b < 0) b = 0; if (b > 255) b = 255;

led.r = r;
led.g = g;
led.b = b;
updateLights();
}

void updateLights()
{
analogWrite(REDPIN, led.r);
analogWrite(GREENPIN, led.g);
analogWrite(BLUEPIN, led.b);
}

void turnOff()
{
setRGB(0, 0, 0);
updateLights();
}

void bootLEDSequence();
{
turnOff();
setRGB(255, 0, 0);
delay(500);
setRGB(0, 255, 0);
delay(500);
setRGB(0, 0, 255);
delay(500);
turnOff();
}

void graph()
{
for (byte i = 0; i < 64; i++) {
Serial.print(spectrum[i]);
Serial.print('\t');
}
Serial.println();
}

void setup()
{
bootLEDSequence();;
Serial.begin(57600);
adcInit();
adcCalb();
setRGB(0, 0, 255);
}

void loop()
{
if (position == FFT_N) {
fft_input(capture, bfly_buff);
fft_execute(bfly_buff);
fft_output(bfly_buff, spectrum);

graph();
byte peak = 0;
for (byte i = 0; i < 41; i++) {
if (spectrum[i] > 160 && spectrum[i] > spectrum[i-1] && spectrum[i] > spectrum[i+1]) peak = i;
}
if (peak < 21) {
// setRGB(255 - 25*peak, 25*peak,0);
} else {
peak -= 20;
// setRGB(0, 255 - 25*peak, 25*peak);
}

position = 0;
}
}

/**
 * Begin FFT functions
 */
ISR(ADC_vect)
{
if (position >= FFT_N)
return;

capture[position] = ADC + zero;
if (capture[position] == -1 || capture[position] == 1)
capture[position] = 0;

position++;
}

void adcInit()
{
/**   REFS0 : VCC use as a ref,
*    IR_AUDIO : channel selection,
*    ADEN : ADC Enable,
*    ADSC : ADC Start,
*    ADATE : ADC Auto Trigger Enable,
*    ADIE : ADC Interrupt Enable, 
*    ADPS : ADC Prescaler 
*/

// free running ADC mode, f = ( 16MHz / prescaler ) / 13 cycles per conversion
ADMUX = _BV(REFS0) | IR_AUDIO; // | _BV(ADLAR);

//  ADCSRA = _BV(ADSC) | _BV(ADEN) | _BV(ADATE) | _BV(ADIE) | _BV(ADPS2) | _BV(ADPS1)
//prescaler 64 : 19231 Hz - 300Hz per 64 divisions
ADCSRA = _BV(ADSC) | _BV(ADEN) | _BV(ADATE) | _BV(ADIE) | _BV(ADPS2) | _BV(ADPS1) | _BV(ADPS0);

// prescaler 128 : 9615 Hz - 150 Hz per 64 divisions, better for most music
sei();
}

void adcCalb()
{
long midl = 0;
// get 2 meashurment at 2 sec
// on ADC input must be NO SIGNAL!!!
for (byte i = 0; i < 2; i++) {
position = 0;
delay(100);
midl += capture[0];
delay(900);
}
zero = -midl/2;
}
/**
 * End FFT functions
 */


The LED in the top right is actually a 5m strip of analog RGB LEDs, but I couldn't find a drawing for them in Fritzing. The whole circuit is driven by a 12V power supply from Adafruit.

I'm using this as a baseline for my code. From the GitHub code, in the main loop, I get a 64 element array that is the magnitude of the spectrum from 0 to 4.8 kHz as given from the microphone. My graph() function writes this code out to Serial, as a tab delimited string of values from the array (one line per array instance). The main array is called spectrum.

Then I use LabVIEW to read this data, parse the string into an array, and display the array as a bar graph. It's somewhat of a workaround, but I wanted a visualization of the spectrum data. However, whenever I control the LED lights, the spectrum graph becomes messed up. These are the lines that are giving me trouble are here:
Code: [Select]
graph();
byte peak = 0;
for (byte i = 0; i < 41; i++) {
if (spectrum[i] > 160 && spectrum[i] > spectrum[i-1] && spectrum[i] > spectrum[i+1]) peak = i;
}
if (peak < 21) {
// setRGB(255 - 25*peak, 25*peak,0);
} else {
peak -= 20;
// setRGB(0, 255 - 25*peak, 25*peak);
}

With this code, I simply output the spectrum through USB, and then find the (last) peak index in the range 0 - 40 (my choice), and then do nothing with it. Index 40 corresponds to about 3kHz. This is what I see in LabVIEW:
Video (no LED control)
I'm using an iPhone to generate the tones. This is actually a very accurate output. I was surprised at how well it performed and how generally responsive it was given that I'm doing a software FFT.

However, if I uncomment the setRGB lines, then the array from the FFT starts going haywire:
Video (LEDs changing)
Can anyone help me reason as to why this is happening? Ideally, my code would give you a smooth transition from red to green to blue as the calculated peak goes from 0 to 40. Am I getting a noisy signal because of the analogWrites? When I went past 3kHz on the iPhone (index 40), the FFT stabilized because the Arduino stops changing the values of the LEDs (by design of the code -- there are no peaks > 160 through index 40 so the peak stays at 0 and the LEDs stay red).

Further note:
I initially had wired the microphone breakout, the three transistors, and the reset button all to the same ground, but I was getting a value of around 160 on spectrum[0] and about 80 on spectrum[1] with no input. With this setup, at least the spectrum stays clean. There is the slightest bit of noise on indexes 0 and 1, but nothing like the other setup. Here is a video of the Arduino analyzing a clip of a soundtrack, with the setRGB lines commented out. The spectrum is correct, even catching the accented high-pitched note.

I appreciate any and all feedback.

EDIT: The FFT calculation sometimes stabilizes when I switch off the LEDs, not sure why.

el_supremo

Code: [Select]
for (byte i = 0; i < 41; i++) {
if (spectrum[i] > 160 && spectrum[i] > spectrum[i-1] && spectrum[i] > spectrum[i+1]) peak = i;
}


When "i" is zero, the if statement references spectrum[-1] which can be any random value since it isn't part of the array. If you are looking for the highest peak (above 160) within those elements of the array, try this:

Code: [Select]
byte peak = 0;
for (byte i = 1; i < 41; i++) {
if ((spectrum[i] > 160) && (spectrum[i] > spectrum[peak])) peak = i;
}


Pete
Don't send me technical questions via Private Message.

vmladenov

Thanks! I had fixed that typo but I forgot to fix it again when I reset the bounds on the for loop.

However, the problem wasn't so much with the peak-finding algorithm as it was with the changing LEDs. I've moved the microphone breakout to 3.3V, and I get less random output now. The jumping still happens below 440 Hz (the A above middle C) though.

el_supremo

The fact the circuit works well until you try to drive the LEDs suggests there's a problem with the wiring.
You haven't got any resistors in your diagram at all. I don't know about the FETs, but the LEDs should each have a resistor in series to limit their current draw - perhaps the LED strip you are using has resistors in it.
You need to modify the diagram to show exactly how you have the 12V supply wired in to the LEDs and the UNO. At the moment it looks like you are powering the strip from the UNO's Vin which can't be right.

Pete
Don't send me technical questions via Private Message.

vmladenov

I am powering the strip from the Uno's Vin, as in the MOSFET example on this page: https://learn.adafruit.com/rgb-led-strips/usage

I know you need resistors for the TIP120s, but for the N-channel MOSFETs it's unnecessary.

el_supremo

If you are powering the 5m Led strip from Vin, I am very surprised that you haven't melted something on the UNO. The strip will be trying to pull several amps from Vin. The whole board uses less than half an amp.
Try powering the LED strip directly from the 12V supply (if it is a 12V strip). As soon as you try to turn the LEDs on, I think the supply to the UNO sags and causes all sorts of problems.

Pete
Don't send me technical questions via Private Message.

CrossRoads

Vin is only good for 1A of current, after that you risk blowing the reverse polarity protection that is between the barrel jack and the Vin pin.
Designing & building electrical circuits for over 25 years.  Screw Shield for Mega/Due/Uno,  Bobuino with ATMega1284P, & other '328P & '1284P creations & offerings at  my website.

tmd3

I vote with el supremo and Crossroads.  The first thing to do is to get the LED current outside the Arduino.  That's too much current for this board, regardless of what might be written in the Adafruit tutorial.

That might cure the problem.  If it doesn't, then I'd recommend two further tests:
  • Send the unprocessed analog data to LavView, as opposed to the processed data.  Does it look like the audio you expected?  If not, then the problem is likely in the analog acquisition
  • Provide calculated data to the FFT, rather than acquired data.  Right now, you can't tell what's not working.  If you give known data to the FFT program, you have a chance of figuring out whether the FFT is is delivering reasonable results.


I can't tell where you got the library, but a web search finds a library that's written largely in assembler.  There's no telling what it's doing.  Maybe you could provide a link to the library?

Finally, many of the unexpected images in the "LEDs on" video look to me to be what I'd expect from windowed input data, all full-scale, either positive or negative.  If you're using the library I think you're using, it claims to use a Hamming window.  So, my first guess is that some or all of the data is treated as 0, and is then set negative when variable zero is subtracted.  Note, though, that that is a raw conjecture.

vmladenov

#8
Jan 07, 2015, 11:01 pm Last Edit: Jan 08, 2015, 12:47 am by vmladenov
Thank you for the replies! I measured the current with the LED's set to full white -- setRGB(255,255,255) -- and it is about 1.3 Amps. I will attempt to power the LEDs separately and see if the FFT stabilizes.

I'm assuming that the current overdraw caused the signal from the microphone to go haywire.

EDIT: By heading off the current, the LEDs began to draw 1.75 Amps! Haven't had time to test the FFT.

Final edit:

I think I'm happy for now. I've switched to a different analog input pin for the microphone. With the microphone at 5V, I still get the switching when the LEDs are on, not when they're off. On 3.3V I don't get the interference. I will be sticking to 3.3V because the alternative would be powering the LEDs completely independently from the Arduino. At the moment, I've branched their power from the DC power connector for the Arduino. Thanks for all the help!

MarkT

I am powering the strip from the Uno's Vin, as in the MOSFET example on this page: https://learn.adafruit.com/rgb-led-strips/usage

I know you need resistors for the TIP120s, but for the N-channel MOSFETs it's unnecessary.
No, necessary for big MOSFETs too(*) - they are large capacitive loads.  Use 150 ohms.

(*) Well the datasheet doesn't specify the max capacitive load but does give 40mA
as absolute maximum without other qualification.

In answer to OP, star-ground, decoupling....
[ I will NOT respond to personal messages, I WILL delete them, use the forum please ]

Go Up