AnalogRead peak detection (local maxima)

I was fascinated when i saw some drumkit implementations and got frustrated afterwards by getting multiple note on messages
when hitting the piezo's...
As i am new in the arduino community and also a newbee in electronics in general, i replicated the schematic from SpikenzieLabs and
used todbot's code,made it work but got many multiple NoteOn messages with single hits.Many users had pretty good results though.

I always like to check on how things are done by others first and then do it another way (pride,egotism etc...)
with a little manipulation it can send MIDI notes with velocity information.

For minimising delayed response i used an alternate firmware for the U8 chip so it registered as a general midi device.
NO custom drivers,NO extra software (serial to MIDI port relay..)

The link for the MIDI MOCO for LUFA firmware:
http://web.mac.com/kuwatay/morecat_lab./MocoLUFA.html

this could be helpfull for other sensors types too and for many projects not related to sound.
please advice me where to post this code for this is my first post here and my first forum post EVER...(sounds lame,i know hehee;)

so here's my way of detecting Local Maxima on consecutive analogreads, it includes the
BlinkLed function in case someone congests his Tx line and cannot re-load code,i happened to me...

enjoy,comment!!

int Threshold = 100;
int Hit;
void setup(){

  pinMode (13,OUTPUT);
  BlinkLed (3,500);
  Serial.begin(38400);

}

void loop(){
  for (int i = 0;i<=5;i++){                //Cycle through ADC channels.
    if (analogRead(i)>Threshold){          //If current channel reading exceeds threshold
      Hit = AnalogMaxima (i,Threshold,2);  //find local maxima and return value.
      if (Hit == 1025 || Hit == 0){        //if still ascending or below threshold,
        break;                             //go to next ADC channel.
      }
      else{
        Serial.println(Hit);               //Show maxima. or play midi note ;)
      }
    }
  }
}


//--------------------------------------------------------------------------------
/* AnalogMaxima compares succesive readings from ADC on channel AnalogCh
 and returns :
   1025 if voltage is decreasing,so that only the maxima is returned.
   0    if below threshold.
   or returns the highest value when voltage starts to drop (local maxima).
 
 AnalogCh  :  Channel on the ADC to check for maxima.
 Threshold :  Lowest voltage reading to begin maxima evaluation.
 Delay     :  Delay between succesive readings.
 */

int AnalogMaxima (int AnalogCh, int Threshold, int Delay){    
  int check1;                                  //variable to store first reading.
  int check2;                                  //variable to store second reading.

  check1 = analogRead(AnalogCh);               //Assing first reading 
  delay(Delay);                                //wait
  check2 = analogRead (AnalogCh);              //Assing second reading.
  if (check1>check2){                          //If voltage is DECREASING (no maxima)...
    return 1025;                               //end loop and return 1025.
  }
  else{
    while (analogRead(AnalogCh)>Threshold){     //While above threshold and RISING
      check1 = analogRead(AnalogCh);            //Assing first reading
      delay(Delay/2);                           //wait
      check2 = analogRead (AnalogCh);           //Assing second reading.
      delay(Delay/2);                           //wait,and loop unless...
      if (check1>check2){                       //voltage drop is observed
        return check1;                          //if so return highest value :)

      }
    }
  }
}                            // end of AnalogMaxima //
//--------------------------------------------------------------------------------


//--------------------------------------------------------------------------------
/* BlinkLed ensures there is enough time in the start of the sketch to download a
 new sketch if desired,in case that the serial tx is constantly sending data.
 Times  :  times to blink.
 Delay  :  delay between blinks.
 */

void BlinkLed (int Times, int Delay){
  for (int i = 1; i<=Times; i++){
    digitalWrite (13,HIGH);
    delay(Delay);
    digitalWrite (13,LOW);
    delay(Delay);
  }
}                             // end of BlinkLed //
//--------------------------------------------------------------------------------

reading the code I think it wil find the first maximum. Assuming these (simplified) values:

1 2 3 4 5 6 7 8 9 10 9 11 12 ...
^^

10 will be seen as the maximum, but will the "glitch" == 9 be noticed?? Not tried to run your code, but it is allways a good exercise to try to fool your own code.

Note that analogRead() is "no exact" science as there might be small variations due to external factors. These fluctuations can be handled to some extent by averaging (2 or more) measurements. or use a smoothing filter

value = alpha * analogRead(n) + (1-alpha) * value; // e.g. alpha = 0.90

my 2 cents,
Rob

Hmm... well, 10 will be returned from the LocalMaxima function and 9 will be ignored as only check1 is returned as a value..
thanks to your reply i noticed a potential bug though...

in the series you provided 10 might NOT be actually returned...guess why!!!

1 = check1
2 = check2
3 = check1
4 = check2
5 = check1
6 = check2
7 = check1
8 = check2
9 = check1
10 = check2
9 = check1
11 = check2
12 = check1
..........and because the slope change is recognized by :

if (check1>check2){ //voltage drop is observed (but 10 is check2 and 9 is check1!!!!! stupid me.... :astonished:)
return check1; //if so return highest value :slight_smile:

}

so i guess ill have to pass the last value of check2 to check1 and then do analogread to check2....
.....thinking....
............typing......
...............hmmmm....
..................done(i guess)....I'll check it tomorrow....

//--------------------------------------------------------------------------------
/* AnalogMaxima compares succesive readings from ADC on channel AnalogCh
 and returns :
 1025 if voltage is decreasing,so that only the maxima is returned.
 0    if below threshold.
 or returns the highest value when voltage starts to drop (local maxima).
 
 AnalogCh  :  Channel on the ADC to check for maxima.
 Threshold :  Lowest voltage reading to begin maxima evaluation.
 Delay     :  Delay between succesive readings.
 */

int AnalogMaxima (int AnalogCh, int Threshold, int Delay){    
  int check1;                                  //variable to store first reading.
  int check2;                                  //variable to store second reading.

  check1 = analogRead(AnalogCh);               //Assing first reading 
  delay(Delay);                                //wait
  check2 = analogRead (AnalogCh);              //Assing second reading.
  if (check1>check2){                          //If voltage is DECREASING (no maxima)...
    return 1025;                               //end loop and return 1025.
  }
  else{
    while (analogRead(AnalogCh)>Threshold){     //While above threshold and RISING

      check1 = check2;                          //Write previous last reading as current first for comparison
      check2 = analogRead(AnalogCh);            //Assing second reading.
      delay(Delay/2);                           //wait,and loop unless...                
       
      if (check1>check2){                       //voltage drop is observed
        return check1;                          //if so return highest value :)

      }
    }
  }
}                            // end of AnalogMaxima //
//--------------------------------------------------------------------------------

Heheee!!!! less code = better code!!
works a bit faster now too :smiley:
tomorrow i'll check it to be sure,thanks for the collaboration robtillaart.

Typo alert :

delay(Delay)/2;

Also the logic as I read the program will return an unknown value if you get a rising value(or both same) below the threshold - no return value

Oh, wait, something familar here - I've seen a discussion on some similar software filter earlier on in this forum-site (in the "old" section). I'll see if I can find it.

(Edit: correct a typo :blush:)

...typo corrected.... thx Msquare :blush:

to answer the unknown value issue heres the full sketch revised and corrected,not tested (i'm not home right now..)

int Threshold = 100;
int Hit;
void setup(){

  pinMode (13,OUTPUT);
  BlinkLed (3,500);
  Serial.begin(38400);

}

void loop(){
  for (int i = 0;i<=5;i++){                //Cycle through ADC channels.
    if (analogRead(i)>Threshold){          //If current channel reading exceeds threshold
      Hit = AnalogMaxima (i,Threshold,2);  //find local maxima and return value.
      if (Hit == 1025 || Hit == 0){        //if still ascending or below threshold,
        break;                             //go to next ADC channel.
      }
      else{
        Serial.println(Hit);               //Show maxima. or play midi note ;)
      }
    }
  }
}


//--------------------------------------------------------------------------------
/* AnalogMaxima compares succesive readings from ADC on channel AnalogCh
 and returns :
 1025 if voltage is decreasing,so that only the maxima is returned.
 0    if below threshold.
 or returns the highest value when voltage starts to drop (local maxima).
 
 AnalogCh  :  Channel on the ADC to check for maxima.
 Threshold :  Lowest voltage reading to begin maxima evaluation.
 Delay     :  Delay between succesive readings.
 */

int AnalogMaxima (int AnalogCh, int Threshold, int Delay){    
  int check1;                                  //variable to store first reading.
  int check2;                                  //variable to store second reading.

  check1 = analogRead(AnalogCh);               //Assing first reading 
  delay(Delay);                                //wait
  check2 = analogRead (AnalogCh);              //Assing second reading.
  if (check1>check2){                          //If voltage is DECREASING (no maxima)...
    return 1025;                               //end loop and return 1025.
  }
  else{
    while (analogRead(AnalogCh)>Threshold){     //While above threshold and RISING

      check1 = check2;                          //Write previous last reading as current first for comparison
      check2 = analogRead(AnalogCh);            //Assing second reading.
      delay(Delay/2);                           //wait,and loop unless...                
       
      if (check1>check2){                       //voltage drop is observed
        return check1;                          //if so return highest value :)

      }
    }
  }
}                            // end of AnalogMaxima //
//--------------------------------------------------------------------------------


//--------------------------------------------------------------------------------
/* BlinkLed ensures there is enough time in the start of the sketch to download a
 new sketch if desired,in case that the serial tx is constantly sending data.
 Times  :  times to blink.
 Delay  :  delay between blinks.
 */

void BlinkLed (int Times, int Delay){
  for (int i = 1; i<=Times; i++){
    digitalWrite (13,HIGH);
    delay(Delay);
    digitalWrite (13,LOW);
    delay(Delay);
  }
}                             // end of BlinkLed //
//--------------------------------------------------------------------------------

in the rare case that check1=check2, AnalogMaxima continues to loop (i guess),only if check1>check2 will check1 be returned...
if below threshold it returns 0 by itself ,don't actually know why..
i remember getting zeros in the terminal when it was not still a function but some scrap-code in void loop()

Well,my protoshield is finished although it looks very sorry 'cause of my horrific soldering but it works!
included 6 leds to flash on hit.

now i'm converting the sketch to send Midi note messages like this :

#include <Compatibility_v2.5.h>
#include <MIDI.h>

int Threshold = 200;
int velocity;
int Hit;

int PadNote1 = 35;
int PadNote2 = 38;
int PadNote3 = 41;
int PadNote4 = 43;
int PadNote5 = 46;
int PadNote6 = 49;

void setup(){
  for (int i = 2;i<=7;i++){
    pinMode (i,OUTPUT);
  }
  pinMode (13,OUTPUT);
  BlinkLed (3,500);
  Serial.begin(38400);

}

void loop(){
  for (int i = 0;i<=5;i++){                //Cycle through ADC channels.
    if (analogRead(i)>Threshold){          //If current channel reading exceeds threshold
      Hit = AnalogMaxima (i,Threshold,2);  //find local maxima and return value.
      if (Hit == 1025 || Hit == 0){        //if still ascending or below threshold,
        break;                             //go to next ADC channel.
      }
      else{
        velocity = map(Hit,Threshold,1000,0,127);
        
        if (i==0){
        MIDI.sendNoteOn(PadNote1,velocity,10);
        MIDI.sendNoteOff(PadNote1,0,10);
        }
        if (i==1){
        MIDI.sendNoteOn(PadNote2,velocity,10);
        MIDI.sendNoteOff(PadNote2,0,10);
        }
        if (i==2){
        MIDI.sendNoteOn(PadNote3,velocity,10);
        MIDI.sendNoteOff(PadNote3,0,10);
        }
        if (i==3){
        MIDI.sendNoteOn(PadNote4,velocity,10);
        MIDI.sendNoteOff(PadNote4,0,10);
        }
        if (i==4){
        MIDI.sendNoteOn(PadNote5,velocity,10);
        MIDI.sendNoteOff(PadNote5,0,10);
        }
        if (i==5){
        MIDI.sendNoteOn(PadNote6,velocity,10);
        MIDI.sendNoteOff(PadNote6,0,10);
        }
       
  
        digitalWrite(i+2,HIGH);
        delay(7);
        digitalWrite(i+2,LOW);
       
      }
    }
  }
}

etc....

I really dislike using those 6 if's though...
could someone show me how to do it like this:

MIDI.sendNoteOn(PadNote'i',velocity,10);

i only need the syntax to use the i integer in conjunction with a prefix ('PadNote' in this instance for PadNote1...6)

i did not post the complete sketch,just enough to ask...

That's what arrays are for. There is a tutorial http://arduino.cc/en/Tutorial/Array

Gee... thanks man!!