FSR pedal

Status
Not open for further replies.
In a hotel pjrc has banned the ip for... can't deal with forum on phone...may not get back to this until Saturday.
 
Some hi hats use as many as 9 different samples to go from closed to open, I don't think the pedal is accurate enough to take advantage of all those. The one I have now only has 3. If it's not to hard 5 would be great because it will be used for other sounds as well.
We are essentially quantizing a continuous signal so the number of zones is arbitrary provided you have sufficient signal resolution (over background noise etc.). I find getting to seven bits of midi is not difficult so 128 levels are feasible with regular potentiometer voltage dividers but even if the FSR is much noisier it should be no problem to get four-bits which gives 16 levels. I'll go for five for now but the code should be easy to extend to more later should you so choose.

The software has a choke group where you can place notes that kill each other. so when the open cymbal is hit it will ring until you hit the closed cymbal which kills it.
I was wondering about that. It's possible to do this within Teensy so the open-hat gets a note off when you send the closed note-on etc. but it's nice we don't need to worry about that.

I'm not sure I follow you about the velocity from the fsr signal. It may be that just having the splash note in a zone just before the chick note is good enough. If I had a way to test the pedal with 2 notes I could tell. The pedal worked well in sketch #16 though.
I'm not as familiar with how this works in drumming software but in general sample players the software normally allows you to indicate which sample to use for which velocity range rather than changing the note values. But we can do the two-note version inside teensy no problem once we figure out the best way to extract the trigger from the FSR. (Also the 'splash' is a bit backwards in that it's letting the pedal up after a close that makes it simple mapping likely will not capture it.)

I would like to see and understand where some things are in the code so for example if I wanted to map the pedal to be used as a pitch bend if that's possible.
If you have questions on how some portion of the code works I'd be happy to give you a line-by-line explanation.

However you need to understand C/Arduino programming structure a bit. You should look for an introductory reference that covers variable declarations, how the setup function and the loop function ('main' in standard C) are called and how to user functions are declared and called from within the setup or main loop. Understanding it conceptually is enough for now but to really learn you have to code rudimentary sketches for yourself but that can wait. I strongly recommend Paul's tutorials once you a ready to start learning hot to write your own code rather than just being able to read and alter other peoples (the RGB LED can be substituted with three regular LEDs or you can skip that part.)


I would like to understand the physical pedal a bit better. Is it a mass-market product or did you fashion it from parts?

I've not yet experimented with the FSR I picked up at my local store ages ago but I'm wondering if this is the best technology if you are building your own pedal. The 'mushy' feel makes me think it may not deliver much resolution for transient events like the splash transition.

Reading the level to modify note values is not a problem but it may not be the best signal for extracting trigger events (to become note-on/off).

Typically MIDI notes are triggered by two physically offset contacts on a switch and the time between the contact being made on one and the other is inversely proportional to the velocity of the key-contact's movement but this is tricky for a beginner and physically tricky to build too.

In this case you could look to another piezo that's contacted when the pedal bottoms-out. Since you already are running the software to convert that into note values then the two signals from your pedal could be used to extract the two signals you need. FYI I'm not suggesting you pursue that right away... we'll see if we can get it working they way you've envisioned first.

So I'm here in wintery Regina in a hotel whose IP is banned by PJRC... I have access to a computer today but it's limited. It will be the weekend before I will be able to provide any updated code.
 
Hope you're near a warm fire, I chose the FSR because it seamed easy, I read about others using them. I'm not apposed to the idea of something different down the road but it does work and I'd like to concentrate on getting it up and running. I will try to learn and understand how this sketch works but honestly code is never going to be easy for me. I new when I got my first error (expected unqualified id) that Arduino and I just don't speak the same language. I have other projects and if I get this up and running it's going to take a lot of time to learn how to use it. There are so many things you can do with it and I'd rather concentrate on that. I'm hoping I can add a mux to the last analog pin for my pots and use digital pins for the buttons these are all midi cc messages.

DSC3.jpgDs1.jpgN0371.jpgN0375.jpg
 
Last edited:
The FSR may work fine without any additional switches or sensors on the pedal. But it would be easy to put a piezo under that blanket with the FSR if it's signal performance makes it difficult to get a responsive splash or chick trigger.

(Is 'chick' the correct term for a foot-closed note?)
 
...I will try to learn and understand how this sketch works but honestly code is never going to be easy for me.
That's OK... but if you start to understand what it's doing it will help fine tune things once we get it close. It will also mean you understand what all those constant value assignments at the top of the code do and how to adjust their values to get the MIDI you want from your hardware.

...I new when I got my first error... that Arduino and I just don't speak the same language.
I was exposed coding via a high-level programming language a long time ago an it came very easily to me in part because the language (HyperTalk) was built for use by average users and used more natural and more forgiving syntax. Arduino chose C as it's parent language and C is decidedly not focused on ease of use by beginners but for maximum flexibility and efficiency. Arduino goes a long way to making it practical for beginners to get programming within a reasonable amount of time (if they stick to simple projects first!) but it does inherit some of steep learning curve of C along with it's power and efficiency. I've been at this on-and-off over several years and I'm nowhere near the level of guys here like Blackaddr or Theremingenieur. But for MIDI you can do a lot without getting into esoteric finer points and you normally don't need to worry about having the most efficient code because Teensy has so much power and MIDI is very light on resources (if you are not scanning many piezos for example!).

But like natural langauages it's easier to read/listen than write/speak. More so because you can get away with syntax errors while speaking another language and the listener will still likely extract your intended meaning but compilers it's either OK or not (with a bit of grey where it warns what issues your code may but will still compile).

If you are able to read sketches for the broad strokes it will definitely help with the next step which is altering existing sketches to do slightly different things.

...I have other projects and if I get this up and running it's going to take a lot of time to learn how to use it.
Yeah you've started with a very complex project but it appears your competent on the hardware side so I think I can help you get this one done. Maybe stick closer to a standard buttons and knobs sketch for the next one with maybe one additional feature. ;)

My shared code usually relies on arrays and 'for-loops' which are an extra bit of extra complexity for your early days but if you want 20+ signals processed you usually wouldn't want to code 20 different sets of lines to handle what is essentially the same thing each time.

...I'm hoping I can add a mux to the last analog pin for my pots and use digital pins for the buttons these are all midi cc messages.
Either buttons or pots can be read thru a MUX but by that point you will start to have to worry that you won't always get representative samples of the peaks if the sketch is busy reading dozen's of pins and waiting for MUX readings to stabalize. But let's not get bogged down with that now.
 
When I started this project I saw examples of kids building drum pads on you tube so I figured, how hard can it be? Every thing is just sending midi messages. I found numinous examples of velocity sensitive code and thought surely I can modify one of these and make it work. What I didn't realize was that most of those sketches were written for one of the Arduinos and wouldn't work with the Teensy. I had already designed my unit around the T3.6 because of the number of analog inputs and the ability to send midi through usb so I didn't want to give up on it. After many nights of trying to figure out error messages and other issues I gave up for a while. I felt like a stranger in a foreign country having to learn a whole new language when all I wanted to do was ask for directions. This is probably the only project I'll ever do that requires code. There is so much to learn about using the instrument that will keep me occupied for a long long time. I would be very happy if we could just get the pedal working, I just brought up the other things so you know where it's eventually going.
 
Hey... you almost lost me a while back but I'm all in now that I actually see what you're doing.

We'll get you running but I can't promise it will be all smooth.
 
Working on a version where 'chick' and 'splash' notes are fired based on a slow-polling of the HH pedal (on the assumption its 'signal' is slow moving).

Because I don't have an FSR handy I'm just using a pot and slowing the code down into the single Hz range so I can model what I think the signal would look like but in slow motion.

I have it working so that when you drop the pedal from the upper zone and the change between the current signal and the last pass is greater than some threshold it will 'splash'.

And similarly when the pedal reaches the upper zone from any other it will fire a 'chick' if the difference is greater than some other threshold.

What I'm not too sure about is note-off values for both of these.

I'm still thinking it thru but I believe the note-off for a splash should occur when you return to the top zone, regardless of speed (so even if the 'chick' doesn't fire) and the 'chick' note-off is when it opens again (even if it doesn't open with enough speed to 'splash').

I know the note-off for the 'chick' is likely not that important as the sample is typically short and dies on its own but it still should handle the case.

Does this note-off behaviour sound correct to you?
 
Wow a little more complex than I imagined, is there an easy way to send a segment that just has pin A0 so I could try it? I tried to send some samples but can't send mp3's I tried the pedal again with the sketch in #16 it really works well the splash triggers with a quick foot tap and the chick triggers when I hold it down. The software should choke the splash with the chick. I think that's all we need.

http://kondratko.com/TomHicks/tom_hicks_samples.html
 
Last edited:
Yeah we're getting there... I think this note-off is correct so I'm going with it for now.

I have code working with serial.print messages that I'm just adjusting to output MIDI but I also want to clean in up a bit before posting so it's a bit more comprehensible as you will have to understand the myriad of parameters that you must set and adjust (assuming the scheme I've come up with is viable for extracting usable performance data from the HH pedal).
 
Here's the code I'm working on. The piezo stuff is commented out in the main loop to focus on the HH pedal.
Code:
/* [COLOR="#40E0D0"]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 and hi-hat alteration by oddson -- un(der)-tested****[/COLOR]
*/

//********************************************************************************* SET PIN OPTIONS**************
const int PINS = 3;                                   // number of piezo signals incoming (does not include HH signal pin)
const int analogPin[PINS] = {A1,A2,A3};               //array of analog PINs used for piezo readings
const int zonePin = A0;                               // high-hat pedal pin                <- I NEED THIS AT 'A9' FOR TESTING AND MAY FORGET TO RETURN TO 'A0'


//********************************************************************************* SET HIGH HAT ZONE CONFIGURATION OPTIONS**************
const int ZONECOUNT = 5 ;                             // number of zones for FSR as modifier for HH
const int zoneMap[ZONECOUNT] = {[COLOR="#FF0000"]300,500,600,700,850[/COLOR]}; //raw reading value boundaries between zones


//********************************************************************************* SET MIDI CHANNEL AND NOTE VALUES *************************
const int channel = 10;                               // General MIDI: channel 10 = percussion sounds
const int note[PINS] = {-1,[COLOR="#FF0000"]36,38[/COLOR]};                    // note values for each pin - negative value flags to read the HH array instead
const int hhNote[ZONECOUNT] = {[COLOR="#FF0000"]46,2,3,4,42[/COLOR]} ;         // note values for flavours of HH from open to closed (no general MIDI for intermediaries so used low values as placeholders)
const int splashNote = [COLOR="#FF0000"]55[/COLOR];                            // note value for 'splash' note sent by HH pedal
const int chickNote = [COLOR="#FF0000"]42[/COLOR];                             // note value for 'chick' note sent by HH pedal (no general MIDI so set to 'closed hat' for now)


//********************************************************************************* SET TIMING AND THRESHOLD ADJUSTMENT OPTIONS**************
//****PIEZOS
const int peakTrackMillis = 12;      // how long to keep reading looking for the peak to become velocity
const int aftershockMillis = 25;     // aftershocks & vibration reject
const int thresholdMin = 60;         // minimum reading before a note will be generated (too low = phantom notes)
//****HIGH HAT PEDAL
const int zoneRefesh = [COLOR="#FF0000"]20[/COLOR];           // mS between HH pedal updates -  hat pedal is low freq. so we can check every few mS.
const int minSplash = [COLOR="#FF0000"]70[/COLOR];            // raw value difference required to fire 'splash'
const int minChick = [COLOR="#FF0000"]70[/COLOR];             // raw value difference required to fire 'chick'
const int deadBandLimit = [COLOR="#FF0000"]50[/COLOR];        // raw value deadband to limit HH updates above noise threshold - can very large with small number of zones



//*************VARIABLES
int zone;                   // calculated zone
int rawZone;                // raw pin reading
int rawZoneLag;             // lagged raw reading used to judge change magnitude
int rawDelta;               // difference between rawzone readings - used to trigger HH splash / chick
int state[PINS];            // 0=idle, 1=looking for peak, 2=ignore aftershocks
int peak[PINS];             // remember the highest reading
int piezo[PINS];            // current raw readings for piezos 
elapsedMillis msec[PINS];   // timers to end states 1 and 2 - separate values for each piezo pin
elapsedMillis zonePoll ;    // timer for polling the HH pedal


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


//*************MAIN LOOP
void loop() {
  HHpedal();   // checkZone polls the hi-hat modifer signal
[COLOR="#00FFFF"]//  for (int i=0;i<PINS;i++){ // loop through piezo readings
//    piezo[i] = analogRead(analogPin[i]); // piezo[] holds raw readings
//    peakDetect(i); // check for peaks events and send note events
//  }[/COLOR]
  while (usbMIDI.read()) { 
    // MIDI Controllers should discard incoming MIDI messages.
  }
}


//*************PEAK DETECT FOR PIEZO SIGNALS
void peakDetect(int i) {
  int playNote = note[i];
  if (!playNote){ // if it reads the negative flag it looks up the HH note instead
    playNote = hhNote[zone]  ;
  }
  switch (state[i]) {
    case 0:
      if (piezo[i] > thresholdMin) {
        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) {
        int velocity = map(peak[i], thresholdMin, 1023, 1, 127);
        usbMIDI.sendNoteOn(playNote, 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(playNote, 0, channel); // can send note-on with vel=0 instead
        state[i] = 0; // go back to idle when
      }
  }
}


//*************HIGH HAT PEDAL SIGNAL HANDLING - AS MODIFIER AND AS NOTE GENERATOR
void HHpedal() {
  if (zonePoll>zoneRefesh) { // modifiying pedal is low Freq. so limit reads
    rawZone = analogRead(zonePin);
          //Serial.println(rawZone);
    rawDelta =  rawZone - rawZoneLag;
    if (abs(rawDelta)>deadBandLimit){ //also limit by change size
      int j = 0;
      while ( rawZone>zoneMap[j] ) {
        j++;
      }
      zone = j;
      int velocity = abs(rawDelta)>>3;
      //Serial.println(velocity); 
      if (rawZoneLag > zoneMap[ZONECOUNT-1] && rawZone < zoneMap[ZONECOUNT-1]) {
        // stop 'chick' and play 'splash' if over threshold
        usbMIDI.sendNoteOff(chickNote, 0, channel); // can send note-on with vel=0 instead
        if (rawDelta + minSplash < 0) { // delta signal is negative when splash is possible
          usbMIDI.sendNoteOn(splashNote, velocity, channel);
        }
      }
      if (rawZone > zoneMap[ZONECOUNT-1]  && rawZoneLag < zoneMap[ZONECOUNT-1]) {
        // stop 'splash' and play 'chick' if over threshold
        usbMIDI.sendNoteOff(splashNote, 0, channel); // can send note-on with vel=0 instead
        if (rawDelta > minChick) { // delta signal is negative when splash is possible
          usbMIDI.sendNoteOn(chickNote, velocity, channel);
        }
      }
      rawZoneLag = rawZone;
    }
    zonePoll = 0;
    //Serial.println(zone);
  }
}
 
I works kind of, need to adjust when I tap the pedal down it doesn't always trigger. I assume these adjustable s are highlighted in red? The weather's nice here for today only, then its back to rain. so I'm in and out all day, later tonight I'll have a chance to get into it.
 
I think I need to adjust the threshold of the pedal notes, I don't see where to do that. I'm just working with 1 note (the chick note) trying to get that working good. The pedal requires a hard tap and doesn't always trigger so need to make it more sensitive somehow.
 
The highest value in the zoneMap, currently at 850, is the key value. That is the close point.

At least it's meant to be but this was hastily tested with a pot on a breadboard.
 
Btw it's possible this code will never generate usable midi... so don't get too bogged down with it.

This is the other key parameter
const int zoneRefesh = 20;

It's the milliseconds between scans. Try single digits and then back off. At 20 it might be missing too much of the signal
 
"it's possible this code will never generate usable midi"??? It seams like there's more latency now also. Isn't there some way to copy the parameters of just the pedal from the sketch in #16? that has by far the best response.
 
The splash is a very different event from a stick hit and FSR give a very different signal from piezo discs.

The FSR is a low frequency signal proportional to the level of pressure where the piezo signal is proportional to changes in pressure. To detect a change in pressure with the FSR you have to poll over time and look for a change in levels whIle with the piezo you just scan for maxima that reflect the changes in an chaotic oscilation.

And the splash is triggered by releasing pressure... that will not trigger a note with the piezo algorithm.



I posted that 'in progress' code but I'll need to guide you through configuration.

The first step is setting the zones in zoneMap.

Uncomment the Serial.println(zone) command and check that the zones feel about right and that you can bring up each at will. I straight-up guessed so I'd have been really surprised if it worked first time out.

Note the zones are zero indexed so the output should be 0-4.

The latency you're seeing is mostly in the zoneRefresh limit. Reduce it to single digits and check if you can still fire eventsat least some of the time.

Some of the parameters will interact and need to be tuned together... for example the minChick and minSplash parameters may need to be smaller if the FSR is polled more frequently when you reduce zoneRefresh.

Testing the chick should be fairly simple once you have the close point set in the zoneMap.

When I get back to Vancouver I'll see if I can locate an FSR in my parts to check on what the signal is like if it's really not working out.

I may need to wire up some kind of test rig to help get starting parameter values that work.

There are lots of other things we can do to address any shortcomings in the code but extracting the note triggers will involve polling for relatively slow changes in level rather than testing for local maxima.
 
If the signal from FSR is too erratic there is a library called ResponsiveAnalogRead that handles noise on a low frequency channel very well. If the readings are smoothed before we sample then they will get more representative samples.
 
Well the monitor just shows 0's / 4's , I guess the chick note is ok but the splash can't be on a release because every time I let go of the pedal after a chick note the splash will play. If it's triggered on the way down then the bottom or chick note will choke it out before it has a chance to ring, but on the way up there would be nothing to stop it. I believe that's the key, to find a zone just before the chick note. It's a very delicate zone even on a mechanical hi hat.
 
Don't worry too much about misfire notes for now. It seems you may not have much spread in the raw values or youve set the upper threshold too low. Changee 'zone' to 'rawZone' in the print to check your raw readings and make sure you're getting a good spread of values from the FSR. Make sure the upper threshold is as high as possible that still allows the user to get to closed without requiring excessive pressure. Make sure the lower threshold is just high enough that it's safely above the resting output. Split the difference on the middling values; the exact positions only matter for modifying A1 note selection.

To limit splash increase its trigger threshold parameter minSplash until you can 'release' the pedal without a splash note firing.

Getting this to feel right may take many iterations and possibly fairly dramatic changes to the code. Right now we're just figuring out the signal so don't worry if you can't get splash to fire appropriately without also misfiring.

I'll write an explanation of the code so you'll understand what the parameters do. I may also write a print serial function to help you report back with what the code is getting back from your hardware.

EDIT - there may be an error in the zone logic... there are six zones when there are five internal boundaries so either the lower or upper boundary may not be working properly. I think it's the lower and it will read zero if below either of the first TWO entries... will post more later.
 
Last edited:
...
EDIT - there may be an error in the zone logic... there are six zones when there are five internal boundaries so either the lower or upper boundary may not be working properly. I think it's the lower and it will read zero if below either of the first TWO entries... will post more later.

It was on the upper end... the top threshold should be 1024 as the upper boundary on the upper zone... this makes the critical 'close' point the second to last entry (900 here):

const int zoneMap[ZONECOUNT] = {300,500,600,900,1024}; //raw reading value boundaries between zones
 
If you're busy don't bother with the current version for now... I think there's some work I need to do.
 
Status
Not open for further replies.
Back
Top