Find fundamental frequency using FFT

Hello and excuse me for my english level in advance,

I'm a French student and I want to find the fundamental frequency of a note played by a guitar in order to tune it. So after many hours of research, I've thought the fix_fft library was the more easy way to do that. So I've used this program :

char im[128];
char data[128];
void setup() {
// put your setup code here, to run once:
Serial.begin(9600);
}

void loop() {
// put your main code here, to run repeatedly:
int static i = 0;
static long tt;
int val;

if (millis() > tt){
if (i < 128){
val = analogRead(A0);
data = val / 4 - 128;
_ im = 0;_
* i++; *

* }*
* else{*
* //this could be done with the fix_fftr function without the im array.
fix_fft(data,im,7,0);
_
// I am only interessted in the absolute value of the transformation*_
* for (i=0; i< 64;i++){*
data = sqrt(data * data + im * im*);*

* }*
* Serial.println(data[0]);*

* }*

* tt = millis();*
* }*
}[/quote]
I've used Serial.println(data[0])" because I've understood it's here I'll find the frequency value, this program should work according to this topic (FFT Library - Audio - Arduino Forum) . The problem is that the monitor's screen scrolls and nothing is written. I use an adafruit microphone for arduino and I've amplified it, but amplified or not, the result is the same. The wiring is good, 9600 baud for the serial works for other programs, there's no error message so I really don't know what's wrong.
I sincerely hope someone will be able to help me
Thank you

I've used Serial.println(data[0])" because I've understood it's here I'll find the frequency value

No that is the DC component of the signal. You have to search the data bins to find the lowest one with a peak in it.
Finding the fundamental frequency of a guitar string is not easy, print out all the bins and see what you get.

Ok thank you for answering, I've tried to print every bin but it's always the same : there's nothing on the monitor but it's scrolling like the program returns nothing again and again

I've tried to print every bin but it's always the same

Did you change the code to something like this:-

  for (i=0; i< 64;i++){
       data[i] = sqrt(data[i] * data[i] + im[i] * im[i]);
       Serial.println(data[i]);
    }

Because I noticed you are not actually removing the data from the array in your original code.

Also as the data type is only a byte would you not be better doing:-

  for (i=0; i< 64;i++){
       abs_data[i] = sqrt(data[i] * data[i] + im[i] * im[i]);
       Serial.println(abs_data[i]);
    }

where you have declared an array abs_data as a float array.

I've never used the FFT library but you can run the [u]Analog Read Serial[/u] to see if you are getting a signal.

I use an adafruit microphone for arduino and I've amplified it, but amplified or not, the result is the same.

I assume that's a "microphone board" with a preamp built-in. Usually, these things have a preamp with a biased output (because the Arduino can't read the negative-half of a normal audio signal.)

If that's all working, Analog Read Serial should read about 512 with no signal. When a signal is present the readings should jump-around "randomly" with quiet signals giving you readings near 512 and very loud signals jumping around between zero and 1023.

It that works as expected we know the hardware is working and you've got a software problem.

This has been done before (or at least attempted before :wink: ) so try searching the forum for "guitar tuner".

@ Grumpy_Mike I did what you tell me, nothing has changed but I noticed when there's no signal that the program returns > for data[0] and ) for data[1]. I don't know if this could help...

@Doug there's no problem with my microphone and my wiring, I tested it

Looks like it's printing the values as characters. Cast it to an int. See if that helps:

       Serial.println((int)abs_data[i]);

Pete

@el_supremo I think you got it. I join you a screenshot of the monitor (each line is a new loop). It looks like an expected result.

If it is, thanks a million for your help and your rapidity everyone.

Just an other question : how could I know which frequency are sampled and can I adjust this ( for example if I only want 0-400 Hz wich parameter should I change)?

There are a couple of things wrong with your code.
You declare 'i' to be global and initialize it to zero but you never reset it to zero after that.
Worse yet, you also use 'i' in the for loop. This will leave it set to 64 when the loop is done so that you will store subsequent samples only in the last 64 elements of the array.
I can't tell if it is a formatting error due to using quote instead of code tags but you don't appear to be storing anything in the array.
Change all of this:

     data = val / 4 - 128;
      im = 0;
      i++;
    }
    else {
      //this could be done with the fix_fftr function without the im array.
      fix_fft(data, im, 7, 0);
      // I am only interessted in the absolute value of the transformation
      for (i = 0; i < 64; i++) {
        data = sqrt(data * data + im * im);
      }
      Serial.println(data[0]);
    }

to this:

     data[i] = val / 4 - 128;
      im[i] = 0;
      i++;
    }
    else {
      // reset the array index
      i = 0;
      //this could be done with the fix_fftr function without the im array.
      fix_fft(data, im, 7, 0);
      // I am only interested in the absolute value of the transformation
      for (int j = 0; j < 64; j++) {
        int mag = sqrt(data[j] * data[j] + im[j] * im[j]);
        Serial.print(mag);
        Serial.print(" ");
      }
      Serial.println("");
    }

Please use code tags (use the </> icon) instead of quotes.

Pete

Ok I applied changes but it's exactly the same. Do you think results are bad? Because values remains constant and near to 0 until I play a chord and I can see several values peak, so for me it looked like an good result

And yes, quote has erased ,[i, from my program

Can you tell us what sort of Arduino you are using? That will help to determine the sample rate and thus what frequencies the bins correspond to.

but it's exactly the same. Do you think results are bad?

Not so much the results as the code. I can't see how the code you originally posted manages to store the samples in successive bytes of the data buffer. When you do put them in successive bytes and you say there is no difference leads me to think there is something more fundamentally wrong that you think.

@ Mike I'm using an Arduino Uno.
Maybe I'm stupid but in the for loop don't we initialize i to 0 again? Maybe that's why there's no change when I add the "i=0 " line

for loop don't we initialize i to 0 again?

Yes but when it exits the loop it will be at 64, so that is where it will start from when it does a second load of acquisition of samples.

Maybe that's why there's no change when I add the "i=0 " line

No, you have something very wrong.

I'm using an Arduino Uno

So basically your sample rate is 10KHz.

in the for loop don't we initialize i to 0 again

No. That's why I changed the index of the for loop from 'i' to 'j' so that there's no confusion.

Please post the current version of your code (in code tags) and also post its output (please copy and paste the output as text, again in code tags).

Pete

Here you go:

#include "fix_fft.h"
char im[128];
char data[128];
void setup() {
  // put your setup code here, to run once:
  Serial.begin(9600);
}

void loop() {
  // put your main code here, to run repeatedly:
int static i = 0;
  static long tt;
  int val;
  
   if (millis() > tt){
  if (i < 128){
    val = analogRead(A0);
    data[i] = val / 4 - 128;
    im[i] = 0;
    i++;  
    
  }
  else{
    i=0;
    //this could be done with the fix_fftr function without the im array.
    fix_fft(data,im,7,0);
    // I am only interessted in the absolute value of the transformation
    for (int j=0; j< 64;j++){
       data[j] = sqrt(data[j] * data[j] + im[j] * im[j]);
       
       Serial.print((int)data[j]);
       Serial.print("  ");
       
    }
    Serial.print("\n\n");
    //do something with the data values 1..64 and ignore im
    
  }
    
    tt = millis();
   }
}

And it returns (top lines when I play a string,bottom when I do nothing):

4  1  2  2  1  2  1  2  0  1  1  2  2  5  5  1  1  5  14  13  1  4  2  3  1  1  1  0  0  0  1  1  0  1  1  1  1  2  4  5  1  2  6  2  1  1  1  1  1  1  0  0  0  1  1  11  3  4  4  3  1  1  0  2  

1  1  0  1  1  0  1  1  0  1  0  0  0  0  0  0  1  2  13  10  2  2  2  1  1  0  0  1  1  2  1  1  1  2  2  2  4  5  1  1  0  1  1  1  2  1  1  0  1  2  1  1  1  1  1  12  1  0  0  1  0  1  1  0  

0  1  1  1  1  1  1  1  1  2  0  1  0  0  0  0  1  1  12  9  2  2  1  1  1  1  1  1  1  2  1  1  1  2  2  1  4  5  2  1  1  1  0  1  0  1  0  1  0  1  1  0  1  1  1  11  1  1  1  1  1  1  0  0  

1  1  0  1  1  1  1  1  1  2  2  1  0  1  1  1  1  2  7  2  2  0  1  0  1  1  1  1  1  0  1  1  1  1  1  0  2  7  2  1  2  2  2  1  1  1  2  1  1  1  1  0  1  0  0  12  1  1  1  1  1  1  1  1  

1  1  1  2  1  2  2  2  1  2  0  1  1  0  1  2  2  2  15  5  2  1  0  1  0  1  0  0  1  1  0  1  1  1  2  2  5  11  2  3  1  0  1  0  1  1  2  1  0  1  1  1  2  1  2  15  1  1  1  2  1  1  1  0  

1  0  0  1  0  0  1  2  0  0  0  0  0  0  1  2  2  4  12  8  3  1  1  1  1  1  1  0  1  1  0  0  1  1  1  0  2  9  2  2  0  1  0  0  2  0  1  0  1  1  1  1  1  1  0  14  1  0  0  1  1  0  1  0  

1  1  1  1  1  1  1  1  1  1  1  2  0  1  0  0  0  2  12  5  3  1  1  2  1  1  1  2  0  1  2  1  1  1  1  2  7  10  3  2  1  1  2  1  1  2  1  1  0  1  1  1  1  1  2  20  1  2  1  1  1  1  1  1  

1  1  1  1  1  1  2  1  1  1  2  1  1  0  1  1  1  2  13  6  2  1  2  1  1  1  1  1  1  1  2  1  1  2  1  1  1  5  1  1  1  1  1  1  0  1  2  1  0  1  1  1  1  1  1  13  1  0  1  1  1  1  1  1  

1  2  2  2  1  2  2  1  2  3  3  2  3  7  3  2  3  3  3  2  2  1  0  1  1  0  1  1  1  1  1  1  1  1  1  0  0  1  2  1  1  1  1  1  2  0  2  1  1  0  2  1  2  2  2  4  3  4  3  1  1  0  1  1  

1  0  1  1  1  0  1  1  1  1  1  1  1  1  1  0  0  2  1  1  1  0  0  1  1  1  1  1  1  1  2  1  1  1  1  1  1  0  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  0  0  1  1  1  1  0  1  1  0  0  

1  1  1  1  1  0  1  1  0  1  1  1  1  0  0  1  1  1  1  1  1  0  1  1  0  0  1  1  1  1  1  1  1  1  1  1  1  0  1  1  0  1  1  1  1  0  0  1  1  1  1  1  1  0  1  1  0  0  1  1  1  1  1  0  

0  0  0  1  0  1  0  1  0  0  0  1  0  1  0  1  0  0  0  1  0  1  0  0  0  0  0  1  0  1  0  0  0  1  1  1  0  1  0  1  0  0  0  1  0  1  0  1  1  1  0  1  0  1  0  1  0  0  0  1  0  1  0  0  

1  1  0  0  0  1  1  1  1  0  1  0  1  1  0  1  1  1  0  0  0  1  0  0  1  0  1  0  1  1  1  1  1  1  1  0  0  1  1  1  1  0  1  0  1  1  0  2  1  1  0  0  0  1  0  0  1  0  0  0  1  1  1  0  

1  0  0  1  0  1  1  2  1  0  0  1  1  1  1  1  0  0  0  2  1  1  1  1  0  1  1  1  1  1  1  0  1  1  1  0  0  1  1  0  1  0  0  2  1  1  1  1  0  1  0  0  1  1  1  1  0  0  1  1  1  1  1  0  

0  1  1  1  0  1  1  1  0  1  1  1  1  1  1  2  0  1  1  1  0  1  0  1  1  1  1  1  1  0  1  1  0  2  1  0  0  1  1  1  0  1  1  1  1  1  1  1  0  1  1  1  0  1  0  1  1  1  0  1  1  1  1  1  

1  1  1  1  1  1  1  1  1  1  1  0  1  0  1  1  1  1  1  0  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  0  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  0  0  

0  1  0  1  1  1  1  1  0  0  0  1  1  1  1  1  0  1  0  1  1  1  1  1  1  0  0  1  1  1  0  1  0  1  0  1  1  1  1  1  0  0  0  1  1  1  1  1  0  0  0  1  1  1  1  1  1  0  0  1  1  1  1  1  

0  0  0  2  0  1  1  1  0  0  0  1  0  0  1  1  0  0  0  1  0  0  1  1  0  0  0  1  0  0  1  1  1  1  0  1  0  0  1  1  0  0  0  1  0  0  1  1  0  0  0  1  0  1  1  1  0  0  0  1  0  1  1  1  

1  1  1  1  1  2  0  1  0  1  2  2  0  2  1  0  1  2  2  1  2  0  0  1  0  1  1  1  1  0  2  0  1  1  1  0  1  0  1  1  0  0  1  1  1  2  0  1  1  1  1  0  1  1  1  1  0  1  1  1  1  0  0  1

Can you tell me what you think this bit is doing?

 if (millis() > tt){

Because you only seem to be doing:-

 tt = millis();

And so for all but 1mS that if will pass.

The original program can be found here : Google Code Archive - Long-term storage for Google Code Project Hosting. (sketch.txt file). Since I'm not very at ease with programming, I didn't want to delete a instruction without knowing exactly what it does. So maybe it's useless if you say it but it was in the original code so I didn't want to make a mistake and delete it

I didn't want to delete a instruction without knowing exactly what it does.

But you were quite happy to change

"data[i]" into "data"

because you know what the difference is.

EDIT:- I see what happened, you did not use code tags like it says in How to use this forum and as a result of not using code tags the forum software took the i in square brackets as an instruction to put the text into italics. So you never saw these were missing or you just couldn't be bothered telling us even when we pointed out the missing index. Why did you not say "but I already have those"? You have wasted the time of the people trying to help you. This makes me cross. >:(
End of EDIT

Basically the original code puts in all the things we told you about. To save anyone else the trouble of looking it up here it is:-

char im[128];
char data[128];

void loop(){
  int static i = 0;
  static long tt;
  int val;
  
   if (millis() > tt){
 if (i < 128){
  val = analogRead(pin_adc);
  data[i] = val / 4 - 128;
  im[i] = 0;
  i++;  
  
 }
 else{
  //this could be done with the fix_fftr function without the im array.
  fix_fft(data,im,7,0);
  // I am only interessted in the absolute value of the transformation
  for (i=0; i< 64;i++){
     data[i] = sqrt(data[i] * data[i] + im[i] * im[i]);
  }
  
  //do something with the data values 1..64 and ignore im
  show_big_bars(data,0);
 }
    
    tt = millis();
   }
}

Now how have you installed this libiary? I can see no evidence that you have included it in your sketch. In fact the original sketch will not compile, so how have you got it to compile? Their is no #include statement in either your code or the original code, so how does it know where to find the fix_fft function?

Their is a whole lot of stuff you are not telling us.

How is your microphone module wired up?
Can you provide a link to it?
What have you done to test that it is actually giving you a waveform and not an envelope?

To answer this question:-

Just an other question : how could I know which frequency are sampled and can I adjust this ( for example if I only want 0-400 Hz wich parameter should I change)?

The sample rate and the length of the buffer determine what frequency each bin represents. You have a sample rate of 10KHz and it takes two samples to recognise a frequency so the top frequency is 5KHz. Each bin therefore represents :-
top frequency / number of bins
Which for a buffer of 128 is 39Hz - so you can only ever measure to the nearest 39Hz to be more accurate then use a bigger buffer.

Ok there's a lot of confusion and I'm at fault, so excuse me, I will start again:
There's some missing line int the demo code but I noticed it so I've changed to this :

#include "fix_fft.h"
char im[128];
char data[128];
void setup() {
  // put your setup code here, to run once:
  Serial.begin(9600);
}

void loop() {
  // put your main code here, to run repeatedly:
int static i = 0;
  static long tt;
  int val;
  
   if (millis() > tt){
  if (i < 128){
    val = analogRead(A0);
    data[i] = val / 4 - 128;
    im[i] = 0;
    i++;  
    
  }
  else{
    i=0;
    //this could be done with the fix_fftr function without the im array.
    fix_fft(data,im,7,0);
    // I am only interessted in the absolute value of the transformation
    for (i=0; i< 64;i++){
       data[i] = sqrt(data[i] * data[i] + im[i] * im[i]);
       
       Serial.print((data[i]);
       Serial.print("  ");
       
    }
    Serial.print("\n\n");
    //do something with the data values 1..64 and ignore im
    
  }
    
    tt = millis();
   }
}

Because I had no results, with your help I changed it to the program published in my last post and I have results published in this one too. When I said there was no change in my results, It was when Pete told me to change the i in the for loop to a j and to initialize i to 0 in the else part.

My microphone is an adafruit one for arduino, I know my wiring is ok because first I have values when I do the analog read serial test and also because I use an other program that can find fundamental frequency and it works ( my project consists in comparing ways to find guitar frequency). Scopes give me the expected signal so I'm pretty sure there's no problem with my microphone.

Ok I hope there's no misunderstanding now, I'm sorry to have wasted your time again even if you have already help me a lot.

Grumpy_Mike:
to be more accurate then use a bigger buffer.

Ok so in my case, if I want to be more accurate I have to change the lenght of im and data arrays ? And is there a way to change the top frequency because the one I'd like to have is around 500 Hz

Excuse me again

f I want to be more accurate I have to change the lenght of im and data arrays ?

Yes.

And is there a way to change the top frequency because the one I'd like to have is around 500 Hz

Reduce your sample rate to 1000 times a second. However, you need an anti-aliasing filter and a very good one at that. I would leave the sample rate alone.

I use an other program that can find fundamental frequency and it works

Can you supply a link to that.