piezo velocity

Status
Not open for further replies.
Very nearly but you have 23 elements in each rather than 21.

The space between the array name and brackets is optional (turns out) but I prefer no space.

Here is my utterly untested extension to multiple piezos:
Code:
/* Use a Piezo sensor (percussion / drum) to send USB MIDI note on
   messages, where the "velocity" represents how hard the Piezo was
   tapped.

   Connect a Pieze sensor to analog pin A0.  This example was tested
   with Murata 7BB-27-4L0.  Almost any piezo sensor (not a buzzer with
   built-in oscillator electronics) may be used.  However, Piezo
   sensors are easily damaged by excessive heat if soldering.  It
   is highly recommended to buy a Piezo with wires already attached!

   Use a 100K resistor between A0 to GND, to give the sensor a "load".
   The value of this resistor determines how "sensitive" the circuit is.

   A pair of 1N4148 diodes are recommended to protect the analog pin.
   The first diode connects to A0 with its stripe (cathode) and the other
   side to GND.  The other diode connects its non-stripe (anode) side to
   A0, and its stripe (cathode) side to 3.3V.

   Sensitivity may also be tuned with the map() function.  Uncomment
   the Serial.print lines to see the actual analog measurements in the
   Arduino Serial Monitor.

   You must select MIDI from the "Tools > USB Type" menu

   This example code is in the public domain.
   *multi-pad extension by oddson (untested)*
*/

const int channel = 10;  // General MIDI: channel 10 = percussion sounds
const int PINS = 22;     // number of signals incoming
const int note[PINS] = {36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57};     // array of MIDI note values for read signals

const int analogPin[PINS] = {A0,A1,A2,A3,A4,A5,A6,A7,A8,A9,A10,A11,A12,A13,A14,A15,A16,A17,A18,A19,A20,A21}; //array of analog PINs 
const int thresholdMin = 60;  // minimum reading, avoid noise and false starts
const int peakTrackMillis = 12;
const int aftershockMillis = 25; // aftershocks & vibration reject
  
int state[PINS];  // 0=idle, 1=looking for peak, 2=ignore aftershocks
int peak[PINS];   // remember the highest reading
int piezo[PINS];
elapsedMillis msec[PINS]; // timer to end states 1 and 2

void setup() {
  Serial.begin(115200);
  while (!Serial && millis() < 2500) /* wait for serial monitor */ ;
  Serial.println("Piezo Peak Capture");
}


void loop() {


  for (int i=0;i<PINS;i++){
    piezo[i] = analogRead(analogPin[i]);
  
  int voltage=piezo[i];
  
    switch (state[i]) {
      // IDLE state: wait for any reading is above threshold.  Do not set
      // the threshold too low.  You don't want to be too sensitive to slight
      // vibration.
      case 0:
        if (voltage > thresholdMin) {
          //Serial.print("begin peak track ");
          //Serial.println(voltage);
          peak[i] = voltage;
          msec[i] = 0;
          state[i] = 1;
        }
        return;
  
      // Peak Tracking state: capture largest reading
      case 1:
        if (voltage > peak[i]) {
          peak[i] = voltage;     
        }
        if (msec[i] >= peakTrackMillis) {
          //Serial.print("peak = ");
          //Serial.println(peak);
          int velocity = map(peak[i], thresholdMin, 1023, 22, 127);
          usbMIDI.sendNoteOn(note[i], velocity, channel);
          msec[i] = 0;
          state[i] = 2;
        }
        return;
  
      // Ignore Aftershock state: wait for things to be quiet again.
      default:
        if (voltage > thresholdMin) {
          msec[i] = 0; // keep resetting timer if above threshold
        } else if (msec[i] > aftershockMillis) {
          usbMIDI.sendNoteOff(note[i], 0, channel);
          state[i] = 0; // go back to idle when
        }
    }
  }
  // Add other tasks to loop, but avoid using delay() or waiting.
  // You need loop() to keep running rapidly to detect Piezo peaks!

  // MIDI Controllers should discard incoming MIDI messages.
  // http://forum.pjrc.com/threads/24179-Teensy-3-Ableton-Analog-CC-causes-midi-crash
  while (usbMIDI.read()) {
    // ignore incoming messages
  }
}


void peakDetect(int voltage) {
}
I tried to keep to the old code a much as possible other than merging the peak detection into the main loop; even where I had issues with the existing code.
 
Wow Thank you! It would have taken me weeks to figure all that out, I have to wait till tomorrow to test it, I'm on a different computer. I will let you know as soon as I do. Thanks again!
 
Well, it just plays A0 the same as before, I tried several pins along with A0 but no sound. tested the pins with another sketch, they all work. I reduced the analog pins to 21 (A0-A20) I tried to change the no. of incoming pins to 21 and I get this error - too many initializers for 'const int [21]'. If I don't want to limit this to channel 10 can I just eliminate that line so it will use all channels?
 
Well, it just plays A0 the same as before, I tried several pins along with A0 but no sound. tested the pins with another sketch, they all work. I reduced the analog pins to 21 (A0-A20) I tried to change the no. of incoming pins to 21 and I get this error - too many initializers for 'const int [21]'. If I don't want to limit this to channel 10 can I just eliminate that line so it will use all channels?

Sorry... there's nothing magical about 21. I typed it instead of 22. You had a 23 entries in the array you had initialized to have 22 entries. I said 21 in error.

It not working is to be expected frankly... I very rarely get this stuff right on the first go and so there is very likely some errors in my code and what is working and not working will hopefully let us find them together.

For testing you may want to reduce N from 22 to 3 until you get it working.

Note that in an array declaration the size declared inside the array cannot be smaller than the number of items you are putting into it. That's what that error meant, you can't put 22 items into 21 spots.

If it only works on pin A0 that implies the problem is with getting the value into voltage.

It looks like I accidentally declare the variable after it tries to assign peak to it... I can't see that working at all (even on just A0) so lets get that fixed and try again.

I've reduced PINS = 3 with pins A0,A1,A2 active.

Code:
/* Use a Piezo sensor (percussion / drum) to send USB MIDI note on
   messages, where the "velocity" represents how hard the Piezo was
   tapped.

   Connect a Pieze sensor to analog pin A0.  This example was tested
   with Murata 7BB-27-4L0.  Almost any piezo sensor (not a buzzer with
   built-in oscillator electronics) may be used.  However, Piezo
   sensors are easily damaged by excessive heat if soldering.  It
   is highly recommended to buy a Piezo with wires already attached!

   Use a 100K resistor between A0 to GND, to give the sensor a "load".
   The value of this resistor determines how "sensitive" the circuit is.

   A pair of 1N4148 diodes are recommended to protect the analog pin.
   The first diode connects to A0 with its stripe (cathode) and the other
   side to GND.  The other diode connects its non-stripe (anode) side to
   A0, and its stripe (cathode) side to 3.3V.

   Sensitivity may also be tuned with the map() function.  Uncomment
   the Serial.print lines to see the actual analog measurements in the
   Arduino Serial Monitor.

   You must select MIDI from the "Tools > USB Type" menu

   This example code is in the public domain.
   *multi-pad extension by oddson (untested)*
*/

const int channel = 10;  // General MIDI: channel 10 = percussion sounds
const int PINS = 3;     // number of signals incoming
const int note[PINS] = {36,37,38};     // array of MIDI note values for read signals

const int analogPin[PINS] = {A0,A1,A2}; //array of analog PINs 
const int thresholdMin = 60;  // minimum reading, avoid noise and false starts
const int peakTrackMillis = 12;
const int aftershockMillis = 25; // aftershocks & vibration reject
  
int state[PINS];  // 0=idle, 1=looking for peak, 2=ignore aftershocks
int peak[PINS];   // remember the highest reading
int piezo[PINS];
elapsedMillis msec[PINS]; // timer to end states 1 and 2

void setup() {
  Serial.begin(115200);
  while (!Serial && millis() < 2500) /* wait for serial monitor */ ;
  Serial.println("Piezo Peak Capture");
}


void loop() {


  for (int i=0;i<PINS;i++){
  
    int voltage=piezo[i];
    piezo[i] = analogRead(analogPin[i]);
  
    switch (state[i]) {
      // IDLE state: wait for any reading is above threshold.  Do not set
      // the threshold too low.  You don't want to be too sensitive to slight
      // vibration.
      case 0:
        if (voltage > thresholdMin) {
          Serial.print("begin peak track ");
          Serial.println(voltage);
          peak[i] = voltage;
          msec[i] = 0;
          state[i] = 1;
        }
        return;
  
      // Peak Tracking state: capture largest reading
      case 1:
        if (voltage > peak[i]) {
          peak[i] = voltage;     
        }
        if (msec[i] >= peakTrackMillis) {
          Serial.print("peak = ");
          Serial.println(peak[i]);
          int velocity = map(peak[i], thresholdMin, 1023, 22, 127);
          usbMIDI.sendNoteOn(note[i], velocity, channel);
          msec[i] = 0;
          state[i] = 2;
        }
        return;
  
      // Ignore Aftershock state: wait for things to be quiet again.
      default:
        if (voltage > thresholdMin) {
          msec[i] = 0; // keep resetting timer if above threshold
        } else if (msec[i] > aftershockMillis) {
          usbMIDI.sendNoteOff(note[i], 0, channel);
          state[i] = 0; // go back to idle when
        }
    }
  }
  // Add other tasks to loop, but avoid using delay() or waiting.
  // You need loop() to keep running rapidly to detect Piezo peaks!

  // MIDI Controllers should discard incoming MIDI messages.
  // http://forum.pjrc.com/threads/24179-Teensy-3-Ableton-Analog-CC-causes-midi-crash
  while (usbMIDI.read()) {
    // ignore incoming messages
  }
}


void peakDetect(int voltage) {
}
 
Thanks Oddson, I rarely get things right the first time also but I'm persistent. So still just A0, I only have 2 piezos connected right now A0/A1, I'm afraid I can't be of much help here, if this has to do with the loop going back to A0 this is beyond anything I know
 
ok... I'll have a look and will likely ask you to run another version, try an alternate setting or (hopefully) fix the obvious error that's eluded me so far.
 
...I completely misread how this code works...

I may be a bit.
 
Last edited:
Could you try this code?

I decided the thing to do was make it more like the original and restore the function call once i realized global variables would work if I passed the index value instead of the reading itself and then just replaced voltage with 'piezo'.

I also realized I could do some minimal testing with a pullup wire and some resistors to keep the analog pins from floating. So a jolt of 3.3 volts mimics the piezo transient (once it increased all the timeframes by 10 fold).

So this is a bit tested and it at least isn't pumping random garbage MIDI any more.

Code:
/* Use a Piezo sensor (percussion / drum) to send USB MIDI note on
   messages, where the "velocity" represents how hard the Piezo was
   tapped.

   Connect a Pieze sensor to analog pin A0.  This example was tested
   with Murata 7BB-27-4L0.  Almost any piezo sensor (not a buzzer with
   built-in oscillator electronics) may be used.  However, Piezo
   sensors are easily damaged by excessive heat if soldering.  It
   is highly recommended to buy a Piezo with wires already attached!

   Use a 100K resistor between A0 to GND, to give the sensor a "load".
   The value of this resistor determines how "sensitive" the circuit is.

   A pair of 1N4148 diodes are recommended to protect the analog pin.
   The first diode connects to A0 with its stripe (cathode) and the other
   side to GND.  The other diode connects its non-stripe (anode) side to
   A0, and its stripe (cathode) side to 3.3V.

   Sensitivity may also be tuned with the map() function.  Uncomment
   the Serial.print lines to see the actual analog measurements in the
   Arduino Serial Monitor.

   You must select MIDI from the "Tools > USB Type" menu

   This example code is in the public domain.
   *multi-pad extension by oddson (under-tested)*
*/

const int channel = 10;  // General MIDI: channel 10 = percussion sounds
const int PINS = 3;     // number of signals incoming

const int note[PINS] = {36,37,38};     // array of MIDI note values for read signals

const int analogPin[PINS] = {A0,A1,A3}; //array of analog PINs 
const int thresholdMin = 60;  // minimum reading, avoid noise and false starts
const int peakTrackMillis = 12;
const int aftershockMillis = 25; // aftershocks & vibration reject

int state[PINS];  // 0=idle, 1=looking for peak, 2=ignore aftershocks
int peak[PINS];   // remember the highest reading
int piezo[PINS];
elapsedMillis msec[PINS]; // timers to end states 1 and 2

void setup() {
  Serial.begin(115200);
  while (!Serial && millis() < 2500) /* wait for serial monitor */ ;
  Serial.println("Piezo Peak Capture");
}


void loop() {
  for (int i=0;i<PINS;i++){
    //delay(20);
    piezo[i] = analogRead(analogPin[i]);
 
  peakDetect(i);
  // Add other tasks to loop, but avoid using delay() or waiting.
  // You need loop() to keep running rapidly to detect Piezo peaks!

}
  // MIDI Controllers should discard incoming MIDI messages.
  // http://forum.pjrc.com/threads/24179-Teensy-3-Ableton-Analog-CC-causes-midi-crash
  while (usbMIDI.read()) {
    // ignore incoming messages
  }
}


void peakDetect(int i) {

        //Serial.println(state[i]);

  switch (state[i]) {
    // IDLE state: wait for any reading is above threshold.  Do not set
    // the threshold too low.  You don't want to be too sensitive to slight
    // vibration.
    case 0:
      if (piezo[i] > thresholdMin) {
        //Serial.print("begin peak track ");
        //Serial.println(piezo[i]);
        peak[i] = piezo[i];
        msec[i] = 0;
        state[i] = 1;
      }
      return;

    // Peak Tracking state: capture largest reading
    case 1:
      if (piezo[i] > peak[i]) {
        peak[i] = piezo[i];     
      }
      if (msec[i] >= peakTrackMillis) {
        //Serial.print("peak = ");
        //Serial.println(peak);
        int velocity = map(peak[i], thresholdMin, 1023, 1, 127);
        usbMIDI.sendNoteOn(note[i], velocity, channel);
        msec[i] = 0;
        state[i] = 2;
      }
      return;

    // Ignore Aftershock state: wait for things to be quiet again.
    default:
      if (piezo[i] > thresholdMin) {
        msec[i] = 0; // keep resetting timer if above threshold
      } else if (msec[i] > aftershockMillis) {
        usbMIDI.sendNoteOff(note[i], 0, channel);
        state[i] = 0; // go back to idle when
      }
  }
}
 
I new you could do it! I only have 2 piezos hooked up but they play 2 different notes with velocity. I can tell you there are many people still looking for a sketch like this. This can be used for several different instruments instead of just one. Ableton has some nice percussion instruments like brushed bells, marimba etc. I have 1 more thing i'd like to get with you about should be fairly simple but not tonight. Put your name on it, and thank you!!
 
Glad it worked for you...

You should know the accuracy of this system is potentially diminished as you scale N.

It relies on getting enough readings from ADC that one of them will roughly correspond to the top of the signal's envelope and therefore represent it's loudness.

But if you have a lot of pins to check, and in particular if you tried to extend further with multiplexers, then the number of usable samples goes down and potentially you're failing to find a representative peak value.
 
Will it be noticeable with 22 inputs or is there a better way, more than 1 board? My unit will have 21 piezos and 1 FSR which will be the hi hat pedal. I wanted to ask how I could control the notes played on a designated pad with the FSR pedal. Something like this?

if value of A 22 > 300 A3 plays note #40
if value of A 22 < 300 but > 50 A3 plays note #41
if value of A 22 < 50 A3 plays note #42
 
Beats me... T3.6 is wicked fast so it might be fine as is.

If not there are software and, if necessary, hardware fixes to try.
 
Any idea how to do this? I don't know how to word it or where to put it, if it can be done.

if value of A 22 > 300 A3 plays note #40
if value of A 22 < 300 but > 50 A3 plays note #41
if value of A 22 < 50 A3 plays note #42
 
... I don't know how to word it...
Knowing how to word it is 50% the way to knowing how to program it.

If we're still talking peak detection what would this even mean since the events are highly unlikely to be simultaneous enough to interact directly.

If you are thinking of a voltage divider (pot) that acts as a three way mode selector then this is relatively easy (although I'd recommend a more 'digital' selection method).

Whatever it is you may want to master this code before looking to alter it dramatically. (Advice I should have been following above! ;) )
 
Yes it would be the same as a pot, in fact some pedals use a pot. I had to order new resistors for the other piezos so I can't do anything else till they get here. (they work with 10k but not with 100k). I'm working with copy's so I may as well try to get this last thing going. The pedal itself has to trigger a note but I had that working with the other sketch.
I really appreciate all your help!!
 
I just went through all the pins, they all work, I don't understand why this works, - const int PINS = 3; but when I have 23 pins /23 notes I have to change that no. to 24. It's not important I just don't understand.
 
Array size and item count should match, but you had one too many for the 22 spots available in your earlier code.

If you initialize the array larger than the item count it will still work. You just don't call on the zero values it fills in anyway...

Still, count your values manually to double check

But if you give it more values than you have sized it for you get the error you quoted
 
screen shot.PNG doesn't enplane this though
 
Status
Not open for further replies.
Back
Top