Forum Rule: Always post complete source code & details to reproduce any issue!
Results 1 to 3 of 3

Thread: HELP! Teensyduino Piezo Sensor Multi-Input

  1. #1
    Junior Member
    Join Date
    Jan 2019
    Posts
    1

    HELP! Teensyduino Piezo Sensor Multi-Input

    Hi PJRC troops, I'm working on a project for Uni for which I'm building essentially a MIDI drum machine with four piezo sensors for input and some knobs to control effects. I've been scouring the internet for weeks trying to find a way of getting my Teensy LC to communicate outside of Arduino IDE via MIDI and eventually, using an example patch (Examples > Teensy > USB_midi > Piezo_Drum) was able to finally get it to trigger a Kick in my DAW, BUT my problem now is that when I try to write in the code for the next sensor, I can similarly make this trigger a note, but it registers as the same 'note' as the first sensor, meaning I basically have two sensors that send the exact same message to play MIDI note 60. Can anyone help me update the code so that my other sensors are recognised as different buttons? I'm very new to working with this technology and coding in general and don't understand what I'm missing here and would massively appreciate anyone that could point me in the right direction!

    I've included my attempt so far of this code for folk to check but in any case, happy New Year if you're reading this!

    -

    const int channel = 10; // General MIDI: channel 10 = percussion sounds
    const int note = 60; // General MIDI: note 38 = acoustic snare

    const int analogPin = A0;
    const int analogPin1 = A1;
    const int thresholdMin = 60; // minimum reading, avoid noise and false starts
    const int peakTrackMillis = 12;
    const int aftershockMillis = 25; // aftershocks & vibration reject


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


    void loop() {
    int piezo = analogRead(analogPin);
    peakDetect(piezo);
    int piezo1 = analogRead(analogPin1);
    peakDetect(piezo1);
    // 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-...ses-midi-crash
    while (usbMIDI.read()) {
    // ignore incoming messages
    }
    }


    void peakDetect(int voltage) {
    // "static" variables keep their numbers between each run of this function
    static int state; // 0=idle, 1=looking for peak, 2=ignore aftershocks
    static int peak; // remember the highest reading
    static elapsedMillis msec; // timer to end states 1 and 2

    switch (state) {
    // 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 = voltage;
    msec = 0;
    state = 1;
    }
    return;

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

    // Ignore Aftershock state: wait for things to be quiet again.
    default:
    if (voltage > thresholdMin) {
    msec = 0; // keep resetting timer if above threshold
    } else if (msec > aftershockMillis) {
    usbMIDI.sendNoteOff(note, 0, channel);
    state = 0; // go back to idle when
    }
    }
    }

  2. #2
    Junior Member
    Join Date
    Jan 2019
    Posts
    18
    Teensy n00b, but I know some programming :-). This may not solve the issue completely, but it should help:

    First, I'd use more descriptive names for your instrument-related variables:

    Code:
    // at the top of your file
    const int kickPin = A0;
    const int kickNote = 60;
    
    const int snarePin = A1;
    const int snareNote = ???; // your call, I don't know midi :-)
    
    // ...
    Then, I'd turn this

    Code:
    void peakDetect(int voltage) {
      // ...
    into

    Code:
    void peakDetect(int pin) {
      int voltage = analogRead(pin);
    
      // ...snip...
    
      int velocity = map(peak, thresholdMin, 1023, 1, 127);
      switch(pin) {
        case kickPin: 
          usbMIDI.sendNoteOn(kickNote, velocity, channel);
          break;
        case snarePin: 
          usbMIDI.sendNoteOn(snareNote, velocity, channel);
          break;
        default:
          Serial.println("Unknown channel");
      }
      // ...
    }
    Then, in the loop, you call
    Code:
      peakDetect(kickPin);
      peakDetect(snarePin);
    Also, you'll probably want to have several slots in the state, peak and msec static variables (use arrays with as many slots as there are channels, and index into them accordingly).

    Code:
    const totalInstrumentNumber = 2;
    
    int getInstrumentIndex(int pin) {
      switch (pin) {
        case kickPin: return 0;
        case snarePin: return 1;
        default: Serial.println("unknown instrument");
      }
    }
    
    // then in peakDetect
    void peakDetect(int pin) {
      int voltage = analogRead(pin);
      int instrumentIndex = getInstrumentIndex(pin);
    
      static int state[totalInstrumentNumber]; // 0=idle, 1=looking for peak, 2=ignore aftershocks
      static int peak[totalInstrumentNumber]; // remember the highest reading
      static elapsedMillis msec[totalInstrumentNumber];
    Then you can use state[instrumentsIndex] (etc...) where you were using state previously.

  3. #3
    Junior Member
    Join Date
    Jan 2019
    Posts
    18
    It would probably be better to start by defining an Instrument struct and and instrument array:

    Code:
    struct Instrument {
      int pin;
      int note;
      int state;
      int peak;
      elapsedMillis msec;
    };
    
    struct Instrument instruments[] = {
      { // kick
        .pin = A0,
        . note = 60
        // the fields omitted are set to 0
      },
      {
        .pin = A1,
        .note = ?? //...
      }
    };
    
    const int instrumentCount = sizeof instruments / sizeof instruments[0];
    then loop from 0 to instrumentCount - 1 in the loop function and pass the index to peakDetect().

    Then you could do

    Code:
    void peakDetect(int index) {
    
      Instrument instr = instruments[index];
    
      // use instr.pin, instr.state, etc...
    If/when you want to add an instrument all you have to do is populate the instruments array with the proper values, and the rest of the code won't need any change.

    However, elapsedMillis is a C++ class. I'm not very used to C++, and I'm out of my depth as to how a struct field can be initialized to a class value. I suppose it has to be heap allocated, meaning that we'd have to use pointers, but I'm not sure...

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •