Using piezo, Arduino UNO, and Sonic pi to make Midi Drums

Hello, everyone, I am currently making a project that when I hit the piezo( a kind of sensor), the Arduino code will decide if the current hit is a real hit or just an interference, if it is a real hit, the Arduino code will transfer the midi signal to sonic pi through an USB-to-Midi cable( the Midi input side of the cable is connected to a midi 5 pin female din that has already been installed on a breadboard, and the USB side of the cable is connected to my Win10 laptop computer.) and then produce the drums sound by sonic pi code.

The pictures below show the detail about my project:


The picture above shows the combination of Arduino UNO, breadboard, and piezos.

However, my sonic pi can not receive any midi signal, the problem has already confused me for many months.

Here is the code to send Midi messages in Arduino.

   #include <MIDI.h>

   #define NUM_PIEZOS 2
   #define SNARE_THRESHOLD 20   //anything < TRIGGER_THRESHOLD is treated as 0
   #define LTOM_THRESHOLD 70
   #define START_SLOT 0     //first analog slot of piezos

//MIDI note defines for each trigger
#define SNARE_NOTE 65
#define LTOM_NOTE 64

//MIDI defines
#define NOTE_ON_CMD 0x90
#define NOTE_OFF_CMD 0x80
#define MAX_MIDI_VELOCITY 127

//MIDI baud rate
#define SERIAL_RATE 9600

//Program defines
//ALL TIME MEASURED IN MILLISECONDS
#define SIGNAL_BUFFER_SIZE 100
#define PEAK_BUFFER_SIZE 30
#define MAX_TIME_BETWEEN_PEAKS 20
#define MIN_TIME_BETWEEN_NOTES 50

//map that holds the mux slots of the piezos
unsigned short slotMap[NUM_PIEZOS];

//map that holds the respective note to each piezo
unsigned short noteMap[NUM_PIEZOS];

//map that holds the respective threshold to each piezo
unsigned short thresholdMap[NUM_PIEZOS];

//Ring buffers to store analog signal and peaks
short currentSignalIndex[NUM_PIEZOS];
short currentPeakIndex[NUM_PIEZOS];
unsigned short signalBuffer[NUM_PIEZOS][SIGNAL_BUFFER_SIZE];
unsigned short peakBuffer[NUM_PIEZOS][PEAK_BUFFER_SIZE];

boolean noteReady[NUM_PIEZOS];
unsigned short noteReadyVelocity[NUM_PIEZOS];
boolean isLastPeakZeroed[NUM_PIEZOS];

unsigned long lastPeakTime[NUM_PIEZOS];
unsigned long lastNoteTime[NUM_PIEZOS];


void setup() {
  Serial.begin(SERIAL_RATE);
  for(short i=0; i<NUM_PIEZOS; ++i)
  {
    currentSignalIndex[i] = 0;
    currentPeakIndex[i] = 0;
    memset(signalBuffer[i],0,sizeof(signalBuffer[i]));
    memset(peakBuffer[i],0,sizeof(peakBuffer[i]));
    noteReady[i] = false;
    noteReadyVelocity[i] = 0;
    isLastPeakZeroed[i] = true;
    lastPeakTime[i] = 0;
    lastNoteTime[i] = 0;    
    slotMap[i] = START_SLOT + i;
  }
  thresholdMap[0] = SNARE_THRESHOLD;
  thresholdMap[1] = LTOM_THRESHOLD;
  
  noteMap[0] = SNARE_NOTE;
  noteMap[1] = LTOM_NOTE;
}

void loop() {
  unsigned long currentTime = millis();
  
  for(short i=0; i<NUM_PIEZOS; ++i)
  {
    //get a new signal from analog read
    unsigned short newSignal = analogRead(slotMap[i]);
    signalBuffer[i][currentSignalIndex[i]] = newSignal;
    
    //if new signal is 0
    if(newSignal < thresholdMap[i])
    {
      if(!isLastPeakZeroed[i] && (currentTime - lastPeakTime[i]) > MAX_TIME_BETWEEN_PEAKS)
      {
        recordNewPeak(i,0);
      }
      else
      {
        //get previous signal
        short prevSignalIndex = currentSignalIndex[i]-1;
        if(prevSignalIndex < 0) prevSignalIndex = SIGNAL_BUFFER_SIZE-1;        
        unsigned short prevSignal = signalBuffer[i][prevSignalIndex];
        
        unsigned short newPeak = 0;
        
        //find the wave peak if previous signal was not 0 by going
        //through previous signal values until another 0 is reached
        while(prevSignal >= thresholdMap[i])
        {
          if(signalBuffer[i][prevSignalIndex] > newPeak)
          {
            newPeak = signalBuffer[i][prevSignalIndex];        
          }
          
          //decrement previous signal index, and get previous signal
          prevSignalIndex--; 
          if(prevSignalIndex < 0) prevSignalIndex = SIGNAL_BUFFER_SIZE-1;
          prevSignal = signalBuffer[i][prevSignalIndex];
        }
        
        if(newPeak > 0)
        {
          recordNewPeak(i, newPeak);
        }
      }
  
    }
        
    currentSignalIndex[i]++;
    if(currentSignalIndex[i] == SIGNAL_BUFFER_SIZE) currentSignalIndex[i] = 0;
  }
}

void recordNewPeak(short slot, short newPeak)
{
  isLastPeakZeroed[slot] = (newPeak == 0);
  
  unsigned long currentTime = millis();
  lastPeakTime[slot] = currentTime;
  
  //new peak recorded (newPeak)
  peakBuffer[slot][currentPeakIndex[slot]] = newPeak;
  
  //1 of 3 cases can happen:
  // 1) note ready - if new peak >= previous peak
  // 2) note fire - if new peak < previous peak and previous peak was a note ready
  // 3) no note - if new peak < previous peak and previous peak was NOT note ready
  
  //get previous peak
  short prevPeakIndex = currentPeakIndex[slot]-1;
  if(prevPeakIndex < 0) prevPeakIndex = PEAK_BUFFER_SIZE-1;        
  unsigned short prevPeak = peakBuffer[slot][prevPeakIndex];
   
  if(newPeak > prevPeak && (currentTime - lastNoteTime[slot])>MIN_TIME_BETWEEN_NOTES)
  {
    noteReady[slot] = true;
    if(newPeak > noteReadyVelocity[slot])
      noteReadyVelocity[slot] = newPeak;
  }
  else if(newPeak < prevPeak && noteReady[slot])
  {
    //Serial.println("Knock!");
    noteFire(noteMap[slot], noteReadyVelocity[slot]);
    noteReady[slot] = false;
    noteReadyVelocity[slot] = 0;
    lastNoteTime[slot] = currentTime;
  }
  
  currentPeakIndex[slot]++;
  if(currentPeakIndex[slot] == PEAK_BUFFER_SIZE) currentPeakIndex[slot] = 0;  
}

void noteFire(unsigned short note, unsigned short velocity)
{
  if(velocity > MAX_MIDI_VELOCITY)
    velocity = MAX_MIDI_VELOCITY;
  
  midiNoteOn(note, velocity);
  midiNoteOff(note, velocity);
}

void midiNoteOn(byte note, byte midiVelocity)
{
  Serial.write(NOTE_ON_CMD);
  Serial.write(note);
  Serial.write(midiVelocity);
}

void midiNoteOff(byte note, byte midiVelocity)
{
  Serial.write(NOTE_OFF_CMD);
  Serial.write(note);
  Serial.write(midiVelocity);
}

The code above is quoted from the website.

Here is the code in Sonic pi.

midi_port = "my_midi"
tempo = 240
left = 5
right = 6


live_loop :midi_piano do
  use_real_time
  note, velocity = sync "/midi:my_midi:0:4/note_on"
  if note == 60
    sample :drum_tom_hi_soft
  elsif note == 62
    sample :drum_tom_mid_soft
  elsif note == 64
    sample :drum_tom_lo_soft
  elsif note == 65
    sample :drum_snare_hard
  elsif note == 67
    sample :drum_bass_hard
  elsif note == 69
    sample :drum_cymbal_hard
  elsif note == 71
    sample :drum_cymbal_closed
  elsif note == 72
    sample :drum_cymbal_open
  elsif note == 74
    sample :drum_cymbal_pedal
  elsif note==58
    sample :drum_splash_hard
  end
  midi_note_on note, port: "my_midi", channel: 0
end


live_loop :midi_piano_off do
  use_real_time
  note, velocity = sync  "/midi:my_midi:0:4/note_off"
  midi_note_off note, port: "my_midi", channel: 0
end

The problem I encounter is my sonic pi can not receive any midi signal when I hit the piezo even though all hardwares are prepared.
Please help me solve the problem~ It has annoyed me for a long time.

P.S. The forum allows new user to only put an embedded item, so I couldn’t post more pictures to describe my project, if somebody wants to know more details, I will try to post more pictures in my response, and sorry for my poor English and my first time to post an article. Thanks so much.

Hello @ChunckLin!
A small tip for making code examples look a bit nicer: Rather than inserting code as a ‘quote’ (with >), to make code stand out from the rest of your message and make it nice and easy to read, you can place a line containing three backticks ``` above and below your code. It is then given nice coloured highlighting. (I have gone ahead and edited your message to show this :slightly_smiling_face:)

Also, if you can, I would suggest uninstalling Sonic Pi v3.2.2 and installing the latest version, v3.3.1.