HELP! Teensyduino Piezo Sensor Multi-Input

Status
Not open for further replies.

ericldev

New member
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-Teensy-3-Ableton-Analog-CC-causes-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
}
}
}
 
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.
 
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...
 
Status
Not open for further replies.
Back
Top