Issues with sparkfun MIDI shield MIDI-Analyzer sketch

I am having issues with the MIDI-sniffer sketch found here.

I am a beginner circuit builder and coder and thought this sketch would be a good place to start to learn about the different aspects of MIDI data that I could work with using the Arduino MIDI library.

I am not using the sparkfun MIDI shield but have built a MIDI I/O circuit according to MIDI specs.

The circuit works with no issues when receiving input from a sequencer and the output goes to a software synthesizer sound source.

I have tested this circuit using the Arduino MIDI library (fortyseven effects) basic I/O and callback examples. No issues whatsoever.

The MIDI analyzer circuit uses the same MIDI library but also uses the softwareSerial library and msTimer2 library.

The issue I am having with the MIDI-sniffer.ino sketch is that while the serial monitor seems to be reading all the MIDI data, the output seems to be getting "stuck". Notes will get stuck "on" and after I stop the sequencer, I hear a distinct echo/ feedback noise. I have to reset MIDI output (send note off / panic message) in my DAW to stop the noise from continuing. Is this some kind of loopback?

Why do the basic I/O and callback examples work ok and this one has issues? How can I work to resolve this issue? Any guidance or pointers is greatly appreciated. The MIDI-sniffer code referenced above is here:

#include <softwareserial.h>
#include <mstimer2.h>
#include <midi.h>

#define PIN_RAW_INPUT 2

#define PIN_POT_A0 0
#define PIN_POT_A1 1

static const uint16_t DEBOUNCE_COUNT = 50;

// Need to use soft serial, so we can report what's happening
// via messages on hard serial.
SoftwareSerial SoftSerial(8, 9);

/* Args:
   - type of port to use (hard/soft)
   - port object name
   - name for this midi instance
*/
MIDI_CREATE_INSTANCE(SoftwareSerial, SoftSerial, MIDI);
// This doesn't make much sense to use with hardware serial, as it needs 
// hard serial to report what it's seeing...

void setup()
{
  // put your setup code here, to run
  once:

  // LED outputs
  Serial.begin(19200);
  Serial.println("Setting up");

  // do I need to init the soft serial port?
  // No - MIDI Lib will do it.

  // We want to receive messages on all channels
  MIDI.begin(MIDI_CHANNEL_OMNI);
  
  // We also want to echo the input to the output, 
  // so the sniffer can be dropped inline when things misbehave.
  MIDI.turnThruOn();

  pinMode(PIN_RAW_INPUT, INPUT_PULLUP);

}

void loop()
{
  static uint8_t  ticks = 0;
  static uint8_t  old_ticks = 0;


  // put your main code here, to run repeatedly:

  if(digitalRead(PIN_RAW_INPUT) == LOW)
  {
    // If you hold button D2 on the shield, we'll print
    // the raw hex values from the MIDI input.
    //
    // This can be useful if you need to troubleshoot issues with
    // running status

    byte input;
    if(SoftSerial.available() != 0)
    {
      input = SoftSerial.read();
    
      if(input & 0x80)
      {
        Serial.println();
      }
      Serial.print(input, HEX);
      Serial.print(' ');
    }
  }
  else
  {
    // turn the crank...
    if (  MIDI.read())
    {
      switch (MIDI.getType())
      {
        case midi::NoteOff :
          {
            Serial.print("NoteOff, chan: ");
            Serial.print(MIDI.getChannel());
            Serial.print(" Note#: ");
            Serial.print(MIDI.getData1());
            Serial.print(" Vel#: ");
            Serial.println(MIDI.getData2());
          }
          break;
        case midi::NoteOn :
          {
            uint8_t vel;

            Serial.print("NoteOn, chan: ");
            Serial.print(MIDI.getChannel());
            Serial.print(" Note#: ");
            Serial.print(MIDI.getData1());
            Serial.print(" Vel#: ");
            vel = MIDI.getData2();
            Serial.print(vel);
            if (vel == 0)
            {
              Serial.print(" *Implied off*");
            }
            Serial.println();
          }
          break;
        case midi::AfterTouchPoly :
          {
            Serial.print("PolyAT, chan: ");
            Serial.print(MIDI.getChannel());
            Serial.print(" Note#: ");
            Serial.print(MIDI.getData1());
            Serial.print(" AT: ");
            Serial.println(MIDI.getData2());
          }
          break;
        case midi::ControlChange :
          {
            Serial.print("Controller, chan: ");
            Serial.print(MIDI.getChannel());
            Serial.print(" Controller#: ");
            Serial.print(MIDI.getData1());
            Serial.print(" Value: ");
            Serial.println(MIDI.getData2());
          }
          break;
        case midi::ProgramChange :
          {
            Serial.print("PropChange, chan: ");
            Serial.print(MIDI.getChannel());
            Serial.print(" program: ");
            Serial.println(MIDI.getData1());
          }
          break;
        case midi::AfterTouchChannel :
          {
            Serial.print("ChanAT, chan: ");
            Serial.print(MIDI.getChannel());
            Serial.print(" program: ");
            Serial.println(MIDI.getData1());

          }
          break;
        case midi::PitchBend :
          {
            uint16_t val;

            Serial.print("Bend, chan: ");
            Serial.print(MIDI.getChannel());

            // concatenate MSB,LSB
            // LSB is Data1
            val = MIDI.getData2() << 7 | MIDI.getData1();

            Serial.print(" value: 0x");
            Serial.println(val, HEX);


          }
          break;
        case midi::SystemExclusive :
          {
            // Sysex is special.
            // could contain very long data...
            // the data bytes form the length of the message,
            // with data contained in array member
            uint16_t length;
            const uint8_t  * data_p;

            Serial.print("SysEx, chan: ");
            Serial.print(MIDI.getChannel());
            length = MIDI.getSysExArrayLength();

            Serial.print(" Data: 0x");
            data_p = MIDI.getSysExArray();
            for (uint16_t idx = 0; idx < length; idx++)
            {
              Serial.print(data_p[idx], HEX);
              Serial.print(" 0x");
            }
            Serial.println();
          }
          break;
        case midi::TimeCodeQuarterFrame :
          {
            // MTC is also special...
            // 1 byte of data carries 3 bits of field info 
            //      and 4 bits of data (sent as MS and LS nybbles)
            // It takes 2 messages to send each TC field,
          
            Serial.print("TC 1/4Frame, type: ");
            Serial.print(MIDI.getData1() >> 4);
            Serial.print("Data nybble: ");
            Serial.println(MIDI.getData1() & 0x0f);
          }
          break;
        case midi::SongPosition :
          {
            // Data is the number of elapsed sixteenth notes into the song, set as 
            // 7 seven-bit values, LSB, then MSB.
          
            Serial.print("SongPosition ");
            Serial.println(MIDI.getData2() << 7 | MIDI.getData1());
          }
          break;
        case midi::SongSelect :
          {
            Serial.print("SongSelect ");
            Serial.println(MIDI.getData1());
          }
          break;
        case midi::TuneRequest :
          {
            Serial.println("Tune Request");
          }
          break;
        case midi::Clock :
          {
            ticks++;

            Serial.print("Clock ");
            Serial.println(ticks);
          }
          break;
        case midi::Start :
          {
            ticks = 0;
            Serial.println("Starting");
          }
          break;
        case midi::Continue :
          {
            ticks = old_ticks;
            Serial.println("continuing");
          }
          break;
        case midi::Stop :
          {
            old_ticks = ticks;
            Serial.println("Stopping");
          }
          break;
        case midi::ActiveSensing :
          {
            Serial.println("ActiveSense");
          }
          break;
        case midi::SystemReset :
          {
            Serial.println("Stopping");
          }
          break;
        case midi::InvalidType :
          {
            Serial.println("Invalid Type");
          }
          break;
        default:
          {
            Serial.println();
          }
          break;
      }
    }
  }
}

I am not using the sparkfun MIDI shield but have built a MIDI I/O circuit according to MIDI specs.

Please show your circuit schematic as built - details matter. This is the full MIDI input, output and through?

MarkT:
Please show your circuit schematic as built - details matter. This is the full MIDI input, output and through?

Hi and thank you for the reply.

I am using the following for my input circuit:

2x 220 Ohm R, 1x 1N914, 1x 6N138, 1x 4.7K R

The circuit has an additional 4.7K Resistor (two 10K in parallel as I didn't have a 4.7K) between pin 7 and ground rail as shown here:

I am using the following for my output circuit:

2x 220 Ohm R

There is no thru circuit in my setup. My understanding is that the Arduino MIDI library has MIDI thru functionality as described here.

Anyone have any ideas here? Is there too much MIDI data running thru the software serial ports? I have even tried reducing the number of switch cases in the sketch but still the same behavior.

I haven't been able to pin anything down yet but I am just a beginner.

Update:

I reached out to the developer of the MIDI-analyzer sketch on Github and here is what they had to say about the issue I am having:

"So the dirty little secret of NewSoftSerial - it's half-duplex. Sending and receiving at the same time can interfere with one another. It'll get worse as the port utilization rises - tapping one key on a MIDI controller might be OK, while heavy multi-channel sequencer (with clocks, controllers, etc) data might not.
There are a couple things to try.
To have a reliable MIDI-thru for the sniffer, you could feed the TTL signal recovered from the input straight to the output driver - that would be akin to the out/thru solder jumper (SJ3) on the shield.
Alternatively, use a chip with multiple hardware UARTs, like an Arduino Mega or Teensy."

Ok, so now that I have identified what is causing the issue, I am unclear what they are saying here:

feed the TTL signal recovered from the input straight to the output driver

Because I am not using the sparkfun shield, how would I go about achieving what the suggested with the schematics I posted previously? I am not understanding exactly what they are saying to do.

Alternatively, could I also avoid the SoftwareSerial library altogether? Can I address the software serial pins on a lower level as described here?

Why not try the native TX and RX pins?

cameron206:
Why not try the native TX and RX pins?

Have a look at the MIDI-sniffer code posted above - it has good code documentation that explains what is going on.

Basically the sketch needs to use the software serial pins in order to report what's happening via messages on hardware serial.

But can you use Software Serial for the reporting?

Cuz maybe it is less important if there is some timing problems on info-data? Rather actual music-events?

cameron206:
But can you use Software Serial for the reporting?

Cuz maybe it is less important if there is some timing problems on info-data? Rather actual music-events?

No.

Yes.

Have a look at the MIDI-sniffer code posted above - it has good code documentation that explains what is going on.

It doesn’t explain what is going on because clearly it does not explain what is going wrong.
Basically you have some crap code that, as you have found, is not fit for purpose. So you have to fix it, given the problem is with the new software serial implementation you either fix that or get round it.

You might want to consider another type of Arduino. The Mega springs to mind as that has four hardware serial ports. The Leonardo has two serial ports, or at least one real serial port and another virtual port that your computer can communicate with.

Grumpy_Mike:
Yes.
It doesn’t explain what is going on because clearly it does not explain what is going wrong.
Basically you have some crap code that, as you have found, is not fit for purpose. So you have to fix it, given the problem is with the new software serial implementation you either fix that or get round it.

You might want to consider another type of Arduino. The Mega springs to mind as that has four hardware serial ports. The Leonardo has two serial ports, or at least one real serial port and another virtual port that your computer can communicate with.

Thanks for your reply. I guess I should have said No and Yes :wink: :

Yes: the code will work if you use an arduino with more than one hardware serial port
No: if you use an UNO and the sketch in its current state.

I am not sure if you saw the part of my thread here where the developer of the sketch confirmed the issue? Here's what it said:

"So the dirty little secret of NewSoftSerial - it's half-duplex. Sending and receiving at the same time can interfere with one another. It'll get worse as the port utilization rises - tapping one key on a MIDI controller might be OK, while heavy multi-channel sequencer (with clocks, controllers, etc) data might not.

There are a couple things to try.

To have a reliable MIDI-thru for the sniffer, you could feed the TTL signal recovered from the input straight to the output driver - that would be akin to the out/thru solder jumper (SJ3) on the shield.

Alternatively, use a chip with multiple hardware UARTs, like an Arduino Mega or Teensy."

I tried asking earlier in this thread how to

get round it

by asking what they meant by:
feed the TTL signal recovered from the input straight to the output driver - that would be akin to the out/thru solder jumper (SJ3) on the shield.

What does this mean?

I also asked if I could do direct port manipulation instead of using the softwareSerial library.

Again: is this a way to work around this issue? I tried the altSoftSerial library and got slightly better performance but not a fix for the above mentioned issues.

I do not know how to fix the issue which is why I initially asked my questions here hoping for some guidance with sorting through this as I only have an UNO to work with.

Is there an alternative MIDI-sniffer / analyzer sketch for the UNO that does not suffer from softwareSerial deficiencies?

Any guidance or suggestions would be immensely helpful.

feed the TTL signal recovered from the input straight to the output driver - that would be akin to the out/thru solder jumper (SJ3) on the shield.

What does this mean?

It means you take a wire and connect it from the MIDI input pin of the Arduino direct to where the MIDI output Pin of the Arduino is going, and you remove the wire from the output of the Arduino to your MIDI interface circuit. That means you will never loose messages and they will still be fed into the Arduino. However that only gets you so far and still doesn’t solve the problem of two messages arriving at the same time that you want to snif.

I also asked if I could do direct port manipulation instead of using the softwareSerial library.

You can access the pins doing the software serial and do direct manipulation like that link showed, or simply use the digitalWrite and digitalRead calls.
However what you are going to do with this access is another matter. I can’t see what would change.

I do not know how to fix the issue which is why I initially asked my questions here hoping for some guidance with sorting through this as I only have an UNO to work with.

You seem to have rejected the only viable solution, that is to use the hardware serial lines on the Arduino, so I see no other way of doing what you want with an Arduino Uno.

Hi and thanks again for your reply. This is super helpful!

You seem to have rejected the only viable solution, that is to use the hardware serial lines on the Arduino, so I see no other way of doing what you want with an Arduino Uno.

I seems I was under the apparently incorrect impression that I couldn't achieve what I wanted to accomplish with the Uno hardware serial lines. I am not rejecting anything: I am just here to learn more about Arduino & MIDI here. If I can use just the hardware serial on the Uno, then great!

Are you saying it is possible?

To start, I want to be able to use the Arduino MIDI library (if viable) to pass MIDI input through to display some goodies like clock (convert to BPM) and note #s (convert to note names). That would be where I would like to start.

You can access the pins doing the software serial and do direct manipulation like that link showed, or simply use the digitalWrite and digitalRead calls.
However what you are going to do with this access is another matter. I can't see what would change.

I suggested this only because I thought this was a solution to get the performance I needed to keep up with the MIDI input. If I don't need to use software pins, then that would be great.

Are you saying it is possible?

Yes.

To start, I want to be able to use the Arduino MIDI library (if viable) to pass MIDI input through to display some goodies like clock (convert to BPM) and note #s (convert to note names). That would be where I would like to start.

OK, the trick is that you have to switch over the serial lines in order to program it and then switch them back to use the lines as midi in and out.
Something like this project.

http://www.thebox.myzen.co.uk/Hardware/MIDI_Shield.html

OK, the trick is that you have to switch over the serial lines in order to program it and then switch them back to use the lines as midi in and out.
Something like this project.

http://www.thebox.myzen.co.uk/Hardware/MIDI_Shield.html

Ok, thanks so much for sharing this example. Yes, this is definitely what I was thinking about with the addition of an OLED that would display the BPM and Note names in real time.

This certainly seems possible with the Arduino MIDI library and the MIDI I/O I have currently built.

Ok if you go by that circuit then I think the pins on the midi sockets are swapped over from what they should be.