Sound Detection Circuit - Following Handclaps

I have made 3 identical circuits for sound detection using some LM741's, and they will function as frontal sound sensors for detecting the position (left, left center, center, right center, right) of a handclap. The robot will then turn to that heading and roam towards the source of the sound. I am having a problem, though. Distinguishing between a handclap coming from dead left and dead right is easy, but determining of it is left center, center, and right center is not so easy. I have tried different methods, but none of them seemed to work. I need help with the software. The code is below (ignore any commented out sections. Also, the code below only has the 'detect left' and 'detect right' parts worked out.) Any suggestions?

//int txPin = 7;
int leftmicPin = 0;
int rightmicPin = 1;
int centermicPin = 2;
int val1 = 0;
int val2 = 0;
int val3 = 0;

void setup() {
      Serial.begin(9600);      
}

void loop()
{
  val1 = analogRead(leftmicPin);
  val2 = analogRead(rightmicPin);
  val3 = analogRead(centermicPin); 
//  delay(3);
//  Serial.println("VALS");
  //Serial.println(val1); 
  //Serial.println(val2);
  //Serial.println(val3);
  //delay(500);
  if(val1 > 500 || val2 > 500)
     if(val1 > val2)
        Serial.println("Left Detect!");
     if(val2 > val1)
        Serial.println("Right Detect!");
        
}

Here is some pseudocode that I hope helps solve your problem. I have renamed the values read from the analog ports for clarity

 if (leftval > centerval) {
       // here if left is louder than center
       if(leftval > rightval)
          // leftval is loudest
       else
           // righttval is loudest
}
else {
        // here if center is louder then left;
       if(centerval > rightval)        
           // centerval is loudest
        else
            // rightval is loudest
}

You may want to add a threshold in the comparison to make sure there is a meaningful difference in the levels.

Oooh...I know why I was screwing a lot up; I was not using the 'else' statement. That messed EVERYTHING up...

Wait - I appreciate the help, but I don't think you understand what I m getting at. Here's the code I've gotten thus far (read on after the code):

int leftmicPin = 0;
int rightmicPin = 1;
int centermicPin = 2;
int val1 = 0;
int val2 = 0;
int val3 = 0;

void setup() {
      Serial.begin(9600);      
}

void loop()
{
  val1 = analogRead(leftmicPin);
  val2 = analogRead(rightmicPin);
  val3 = analogRead(centermicPin); 
  
  if(val1 > 500 || val2 > 500 || val3 > 500)
     if(val1 > val2)
        Serial.println("Left Detect!");
     else if(val2 > val1)
        Serial.println("Right Detect!");
     //else if()
        //Serial.println("Center Detect!");
     //else if()
        //Serial.println("Left Center Detect!");
     //else if()
        //Serial.println("Right Center Detect!");
}

The commented out areas are what I haven't yet figured out. The triggering amplitude for a handclap, represented as a decimal after A/D conversion is ~500 or more. Therefore, I use that value [500] in the if...then statements. These microphones will be spaced ~6" apart. Maybe I should use a conical fixture on each to make it more accurate? :-/

I assumed you wanted to detect the loudest of three inputs that is over 500? If so, then you can add the following to the fragment I posted earlier:
put your check:
if(val1 > 500 || val2 > 500 || val3 > 500)
before the if statements. And replace the comments:
//xxx val is the loudest with the appropriate
Serial.println("xxx Detect!");

And you can stick with your variable naming or mine but it needs to be consistent.

anyway, I hope that helps. Or did you want to do something completly different?

Hmmm...I still don't quite see what you are trying to tell me :-[. Yes, I know how to get it to register which value is the highest over 500. And no, I don't need to put that statement before every if statement. The thing is, the circuits are relatively sensitive, so if I were to stand across the room (say, 8 feet away), it's not like only one of the three microphones is going to register the handclap; all three most likely will exceed the 500 threshold, the closest mic being the highest, the adjacent one being the second highest, and the third one probably registering just above 500 (usually if you clap more towards the extremes of right and left, the opposite one will not register the handclap. for example, if you clapped way off to the right, the mic on the far left would not exceed the 500 value). The problem I am having is distinguishing a clap from directly in front of the array, between the left and center microphones (left-center), and between the right and center microphones (right-center). I am HOPING that I can get that kind of resolution (just left-center and right-center; I know I can pick out a clap from the dead center...), but maybe it can't be done?

Aha, I think I am clearer on what you want to do.

Here is a simple way you could use the output from the louder two of the three mics to determine intermediate sound sources.

First determine which two mics are the loudest . This should be two adjacent mics, if all three are about the same then you can assume the source is coming from the center. If the left and right are louder than the center then you either have two sound sources or a faulty mic.

If it's the left and center mics that are loudest, then multiply the value you get from the left mic by minus one so the sign is inverted and add it to the right mic value. A resultant value close to 0 will be centered between the mics, values more positive will be closer to the center, more negative closer to the left.

You can do the same thing with the center and right mics, and it may be more intuitive if in this case you invert the center value so positive values always indicate sound sources to the right.

I hope that makes sense.

Yay! ;D I figured it out!

"Here is a simple way you could use the output from the louder two of the three mics to determine intermediate sound sources.

First determine which two mics are the loudest . This should be two adjacent mics, if all three are about the same then you can assume the source is coming from the center."

Those couple first words from you gave me an idea. I figured out that the Arduino had a MAX function, which worked well. Now I am going to move on to doing the more complex logic (invert the values, add them, see which is closer or farther from zero...) and see if I can pull that off... :-/

Oh yeah, and here's the code:

int leftmicPin = 0;
int rightmicPin = 1;
int centermicPin = 2;
int val1 = 0;
int val2 = 0;
int val3 = 0;
int maximumVal = 0;

void setup() {
      Serial.begin(9600);      
}

void loop()
{
  val1 = analogRead(leftmicPin);
  val2 = analogRead(rightmicPin);
  val3 = analogRead(centermicPin); 
  
  if(val1 > 500 || val2 > 500 )//|| val3 > 500)
     if(val1 > val2)
        Serial.println("Left Detect!");
     else if(val2 > val1)
        Serial.println("Right Detect!");
     //else if
        //Serial.println("Center Detect!");
     //else if()
        //Serial.println("Left Center Detect!");
     //else if()
        //Serial.println("Right Center Detect!");
  if(val3 > 500)
     maximumVal = max(val1, val2);
        if(maximumVal == val1)
           if(maximumVal > 500)
              Serial.println("Left Center Detect!");
        if(maximumVal == val2)
           if(maximumVal > 500)
              Serial.println("Right Center Detect!"); 
}

...And here's the more advanced one. But there's one problem with it: for some reason, the right value (when the center mic value is subtracted from the right value/inverted and added) sometimes returns negative values. Why? It shouldn't, right? I don't want that; that will mess up logic if you know what I mean. BTW, thanks a million for the help.

int leftmicPin = 0;
int rightmicPin = 1;
int centermicPin = 2;
int val1 = 0;
int val2 = 0;
int val3 = 0;
int maximumVal = 0;
int directionVal = 0;

void setup() {
      Serial.begin(9600);      
}

void loop()
{
  val1 = analogRead(leftmicPin);                 //Read all the sound sensor outputs
  val2 = analogRead(rightmicPin);
  val3 = analogRead(centermicPin); 
  
  if(val3 > 500)                                 //If the center sensor has been tiggered
     maximumVal = max(val1, val2);               //Find the highest value out of the left and right sensors
        if(maximumVal == val1)                   //If the result is the left sensor, continue statement 
           if(maximumVal > 500)                  //If the sensor has been triggered, continue statement
             {directionVal = val2 - val1;        //Determine how far; more negative = more to the left; 0 = at absolute center
               Serial.println("Left");
               Serial.println(directionVal);}  
        if(maximumVal == val2)                   //If the result is the right sensor, continue statement 
           if(maximumVal > 500)                  //If the sensor has been triggered, continue statement
             {directionVal = val2 - val3;        //Determine how far; more positive = more to the right; 0 = at absolute center
               Serial.println("Right");
               Serial.println(directionVal);}        
}

I take that back - the values for the right mic are wrong altogether. When the handclaps come nearer to the center (and therefore farther left from the far right mic), they go even further into the negatives (like -175 for example), and when you move towards the right away from the center mic, and closer towards the far right mic, the value eventually reaches zero. What is wrong? Remember the values in the code are:

'val1' - The left mic
'val3' - The center mic
'val2' - The right mic

I have not 'negated' the values and then added them, I simply subtracted. Be sure not to mix that up. Thanks!

because there are no brackets after the first if statement, the code does not correctly handle cases where val3 <= 500 ( the statement maximumVal = max(val1,val2) will not be executed)

using if statements is a little cumbersome for what you want to do so perhaps something like the following is a better approach.

int leftmicPin = 0;
int rightmicPin = 1;
int centermicPin = 2;

int values[3];   // this holds readings from the three mics
int loudest[3];  // this holds indexes to the above values, after sorting, the loudest will be first (i.e. loudest[0]);   
char *Positions[3] = {"Left", "Center", "Right"};  // string used to display the calculated position

void setup() {
   Serial.begin(9600);      
}

void readValues(){     // function to load the value array with sensor data and initialise the loudest array so it can later be sorted
  for(int i=0; i < 3; i++){
     values[i] = analogRead(i); 
     loudest[i] = i; // this stores the index to the above values, ready for sorting
  }  
}

void sortValues(){   // this function orders indexes in the loudest array with the loudest first
   if(values[0] < values[1])
      swap(0,1);
   if(values[1] < values[2])
      swap(1,2);
   if(values[0] < values[1])
      swap(0,1);     
}

void swap(int index1, int index2){  // utility function to swap the values in the values array at the given indexes
   int temp;
   temp = values[index1];
   values[index1] = values[index2];
   values[index2] = temp;   
}

void processLoudest(int index){
    Serial.print("the sound is coming from the ");
    Serial.println(Positions[index]);   
}

void processBetween(int index1, int index2){
    Serial.print("the sound is coming from the ");
    Serial.print(Positions[index1]);   
    Serial.println(Positions[index2]);   
    // TODO: you could add code to calculate values[index1] - values[index2] to get the relative position if required
}


void loop()
{
   readValues();                 //Read all the sound sensor outputs
   sortValues();                     // order the array of indexes with the loudest first
   if(values[loudest[0]] > 500){      // only process output if at least one value is greaer than 500
      if(values[loudest[1]] > 500)    // check if the first and second loudest are greater than 500 
         processBetween(loudest[0], loudest[1]);   // process the two loudest values
      else
         processLoudest(loudest[0]);  // only one value greater than 500
   }                
}

I have not tested the code so it may need some work but I hope it helps.

Wow. Thanks! I just tested it though, and something must be written wrong because when any single microphone detects a sound, it always says "...from the left"; The "LeftCenter" part works correctly, but the "Center", "Right" and "RightCenter" parts do not. I am sorry to say that I am just now building my knowledge of the Arduino code, so what you have written for me looks like German. Whether you have the patience or not to explain it to me, that's up to you. I would love to understand how it works, if you have the time. Here's what I DO know:

-The 'void setup () {} part is only run once.
-the 'for(int i=x; i _ y; i ++ or --) behavior
NOTE: I noticed the "int i" part. Does that indicate that you declared the variable 'i' right there? Or is 'i' and operator and does not have to be declared??

What I DON'T know or am confused about:
-all the rest
-also, I noticed these statements:

void processLoudest(int index){
Serial.print("the sound is coming from the ");
Serial.println(Positions[index]);
}

void processBetween(int index1, int index2){
Serial.print("the sound is coming from the ");
Serial.print(Positions[index1]);
Serial.println(Positions[index2]);

I can understand why you have the first one, but why do you need the second? What is the difference between the two? Also, I have never been able to get the Arduino to output a value obtained from a sensor on the same line as some works (e.g "LDR Reads: -value-). Is that, in a sense, what is being done in the above statements? And lastly, what do you mean by 'the if statements are cumbersome? How should they be any different? Maybe I should just build on that for now...If I wanted to improve the code I have already written, how do I fix the improper 'right' values? Put the brackets in? Where? Thank you for all your help.

I think I see what the problem is with the code I posted but lets debug it together so you can get a better idea of what is happening in the program.

Below is the sketch with some debug code added that will show how the program is manipulating the data as it runs. I think you will see that the loudest[] array never gets sorted. Do you see why it doesn't and what change you need to make to fix the program?

int leftmicPin = 0;
int rightmicPin = 1;
int centermicPin = 2;

int values[3];   // this holds readings from the three mics
int loudest[3];  // this holds indexes to the above values, after sorting, the loudest will be first (i.e. loudest[0]);   
char *Positions[3] = {"Left", "Center", "Right"};  // string used to display the calculated position

void setup() {
   Serial.begin(9600);      
}

void readValues(){     // function to load the value array with sensor data and initialise the loudest array so it can later be sorted
  for(int i=0; i < 3; i++){
     values[i] = analogRead(i); 
     loudest[i] = i; // this stores the index to the above values, ready for sorting
  }  
  debugPrintValues() ;
  debugPrintLoudest();  // this is the order before sorting

}

void sortValues(){   // this function is supposed to order indexes in the loudest array with the loudest first, it doesn't, do you see why?
   if(values[0] < values[1])
      swap(0,1);
   if(values[1] < values[2])
      swap(1,2);
   if(values[0] < values[1])
      swap(0,1);     
   debugPrintLoudest();    // this is the order of the loudest array ofter a single pass through sort 
}

void swap(int index1, int index2){  // utility function to swap the values in the values array at the given indexes
   int temp;
   temp = values[index1];
   values[index1] = values[index2];
   values[index2] = temp;   
}

void processLoudest(int index){
    Serial.print("the sound is coming from the ");
    Serial.println(Positions[index]);   
}

void processBetween(int index1, int index2){
    Serial.print("the sound is coming from the ");
    Serial.print(Positions[index1]);   
    Serial.println(Positions[index2]);   
    // TODO: you could add code to calculate values[index1] - values[index2] to get the relative position if required
}


void loop()
{
   readValues();                 //Read all the sound sensor outputs
   sortValues();                     // order the array of indexes with the loudest first
   if(values[loudest[0]] > 500){      // only process output if at least one value is greaer than 500
      if(values[loudest[1]] > 500)    // check if the first and second loudest are greater than 500 
         processBetween(loudest[0], loudest[1]);   // process the two loudest values
      else
         processLoudest(loudest[0]);  // only one value greater than 500
   }                
} 

void debugPrintValues(){  // show whats happening on the serial monitor
    // Show the three values we collected 
    // this should print: "Left=x, Center=y, Right=z" where x,y&z are values previously read
    for(int i=0; i < 3;i++) {
       Serial.print(Positions[i]);   // i is the index, Position contains an array of strings
       Serial.print("="); 
       Serial.print(values[i]);     // print the value 
       Serial.print(", ");       
     }
     Serial.println();
} 

void debugPrintLoudest(){  // show the array with sorted positions on the monitor
    // Show the three readings in order of loudest to quitest
    // This shows how the indexes are moved as they get sorted
    // this should print: "Left, Center, Right" the first time, the order should change as the array gets sorted
    for(int i=0; i < 3;i++) {
       Serial.print(loudest[i]);   // i is the index, loudest contains an index to the actual value haled in values[]
       Serial.print(", ");       
     }
     Serial.println();
}

To answer your other questions:

'for(int i=x; i _ y; i ++ or --) behavior , Does that indicate you declared the variable 'i' right there?

Yes, int i= is a declaration of the variable i that has the benefit of only being visible within the scope of the enclosing curly brackets {} of the for loop. This construct only works in source files compiled as C++ such as the main sketch file.

Serial.print("LDR Reads: ");
Serial.println(value);

Serial.print can display strings, and numbers in various bases but it can only do one of those things in each call. So to display a string of characters followed by a number we need to call it twice. The second call to println sends the end of line character as well so the next print will be on another line. There are more examples of this usage in the debug functions just added

what do you mean by 'the if statements are cumbersome? How should they be any different?

The first versions of the sketch (before introducing the sorting function) would have required if statements detecting the six possible cases of loudness. That's a lot 'if else if' stuff and I thought that the sorting solution would be easier to follow. But the sketch can certainly be implemented using just if statements and don't let me stop you if you are more comfortable doing it that way.

Anyway, run the sketch with the debugging code and let me know if you have difficulty seeing what change you need to make to get it to work. Feel free to add any of your own Serial.print statements to further clarify what the program is doing.

Ok. I'll run it later today. Going back to what I said before, I REALLY don't understand that code, but when I run it tonight, I'll see if I can figure it out. I was sticking to all the if...then statements before because that's easier for me; I have only been using the Arduino since Christmas. But one thing I want to know is: is the if...then method SLOWER than yours? If you could explain to me how that program works, that would be great. I would love to know how to write mine more efficiently.

The if then solution should not be slower and if that is easier for you then you can certainly impliment it that way.

Perhaps the easiest way of implementing that is bear in mind that you need to check for the six valid cases:
None > 500
Left only > 500
Left and Center > 500
Center only > 500
Center and Right > 500
Right only >500

But do have a look at the debug output of the posted version to see if that helps you understand what is happening.

To make your life a little easier, the bug in the code is that the swap function should be operating on the loudest array, not the values array. Changing values to loudest in the four places it is used in swap should make it work

I fixed that...and it works better (your code), but it still has problems. Let me just make things easier. From this point on, can we modify my code? So can we diagnose the ghost in the machine in my code (the most recent one I posted)? remember, it's not displaying the values for the right side (the center and right mics together. Maybe I have made a mistake in my code where it isn't even using the center mic in conjunction with the right mic at all...). Can you help me figure out what's wrong? You said before something about putting brackets around "the code after the first if statement", but remember: I'm new to the Arduino, and with my BS2 (Basic Stamp), I didn't have to utilize those brackets, so therefore I don't know WHERE specifically in the code to start and end them, in this case (for the if statements). You didn't specify to that degree. I'm just saying that when TWO people really know what they're doing (since I AM familiar with easy if...then structure), then we can better figure this out quicker. I have no idea how to manipulate the code you have made, neither do I know what parts do what task. I do really appreciate, though, the effort you put into it. Thanks for that.

Here's some more info: At a resting state with no noise present, the Arduino reads a value of ~300 to ~325. When I clap, it reads 500 or more; up to 650. I don't know if that will help you, or if what you said earlier (about the brackets) will simply solve everything.

out of curiosity, do you have a scheamtic for the 741 circuit, showing how it is connected to the Arduino?

D

This is how I would structure the sketch using if statements.

int leftmicPin = 0;
int rightmicPin = 1;
int centermicPin = 2;
int val1 = 0;
int val2 = 0;
int val3 = 0;
int maximumVal = 0;

void setup() {
      Serial.begin(9600);      
}

void loop()
{
  val1 = analogRead(leftmicPin);
  val2= analogRead(centermicPin);   // I swapped this so val2 is center and val3 is right
  val3 = analogRead(rightmicPin);     // feel free to change it back if you prefer
  
     if(val1 > 500 && val2 > 500 && val3 > 500)   
        Serial.println("sound from everywhere Detect!!!");  // i think you said this shouldn't happen but its here just in case
     else if (val1 > 500 && val3 > 500)    
        Serial.println("sound from left and right but not center!!!"); // this also shouldn't happen
     else if(val1 > 500 && val2 > 500)   
        Serial.println("Left and Center Detect!");
     else if(val1 > 500 ) 
        Serial.println("Left Detect!");
     else if (val2 > 500 && val3 <= 500)
        Serial.println("Center Detect!");
     if(val2 > 500 && val3 > 500)   
        Serial.println("Right and Center Detect!");
     else if (val3 > 500)
        Serial.println("Right Detect!");   
     else
         ; // do something here if you need to handle the case where none were over 500 
}

edit: after posting the code I noticed the your numbering was not consecutive from left to right (i.e. I expected that if val1 was left , val2 would be center and val3 right ) so I changed the assignment of values from analogRead rather then all the if statments. May I suggest that your naming would be clearer if you renamed your values: valLeft, valCenter, and valRight. The logic of the sketch doesn't care about pin numbers but it does care about the location.

Anyway, I hope that the posted sketch helps.