Arduino MIDI Logic Analyser

After having a few issues debugging the MIDI connection for my Multi-Mode MIDI Step Sequencer I started wondering about the possibility of building a simple Arduino-based tool to help debug MIDI serial links. I’m thinking something like a MIDI “logic analyser”.  This is the result.

Warning! I strongly recommend using old or second hand equipment for your experiments.  I am not responsible for any damage to expensive instruments!

These are the key Arduino tutorials for the main concepts used in this project:

If you are new to Arduino, see the Getting Started pages.

Parts list

  • Arduino Uno or Nano
  • MIDI Interface (optional)
  • Microcontroller MIDI project to analyse
  • Jumper wires

The Circuit

ArduinoMIDIAnalyser_bb

In this setup I have an Arduino Nano running a MIDI project, sending out a MIDI signal via its TX pin that I want to analyse.

My analyser is an Arduino Uno with the “analysis pin” being D2.  D2 is connected to the Nano’s “MIDI OUT” (TX).  Both boards are independently powered but are tied together via their GND connections.

In principle the analyser could also work via a proper MIDI interface, but I was really aiming to use it to debug serial-level (5V, TTL) MIDI signals in the first instance.

You might ask why I’m not using an oscilloscope or a logic analyser.  Good question!  I do have a Pulse-View compatible, cheap USB logic analyser that works quite well, but that needs hooking up to a computer.

So the simple answer is there would be too many connections required and there would be too much decoding “by hand” required to spot if there are recognised genuine MIDI messages or not.

But to be honest the real answer is “that’s no fun” and the Arduino should be able to do it for me in quite a nice self-contained, specialised unit (eventually).

The Code

The basic idea for the code is as follows:

  • The digital IO pin is not acting as a serial port, it is acting as a INPUT to detect the logic level on the pin at any point in time.
  • The pin is read (or “sampled”) at a known rate that is faster than the MIDI baud rate (31250 bits per second).
  • If a HIGH to LOW transition is detected then it starts to record the levels it sees on the pin recording them in an internal buffer until the buffer is full.
  • When the buffer is full, the received data is analysed to see if it looks like a MIDI message and if so, then the message is decoded and some information about the message is displayed to the Arduino’s serial port.

There are two main components to the code: a timer-driven interrupt to grab the samples when in “recording” mode; a slower loop to process the samples and print out the data.

I make use of the TimerOne library once again to give me a regular interrupt. MIDI runs at 31250 bits per second, which equates to 32uS per bit.  So to be able to accurately detect the signal I need to be examining the input pin at least twice as fast as that. I’ve opted for four times as fast which means I need the timer interrupt to run every 8uS and quickly store anything it reads.

To keep the interrupt running quickly, I’m using direct port IO and no buffer management.  When recording starts, it starts at the beginning of the buffer and continues until the buffer is full.  That is all.

Here is the timer interrupt routine.

void readSample () {
  if (recording) {
    buf[reccnt] = PIND & 0x04;
    reccnt++;
    if (reccnt >= BUFSIZE) {
      recording = false;
    }
  }
}

I am using D2 as my input pin which maps onto bit 2 (starting from 0) in the PIND register.  This is picked out from the other pins by ANDing with 0x04 (or 0b00000100).  This means that the buffer will end up containing a string of either “4”s or “0”s.  In the rest of the code, I’m working on the principle that 0 is LOW and non-zero is HIGH.

“recording” is a boolean flag that is set in the main loop when a HIGH to LOW transition is detected on the pin to start the recording.

There are two output modes.  The simplest is “graph mode” which just outputs a number or zero, one per line, to the serial port.  The idea is to use the Serial Plotter function to create a trace as follows.

ArduinoMIDIAnalyser-Graph

I think this is a MIDI note on followed by a MIDI note off, but whilst you can sort of work out the data, it isn’t easy, but if you put your mind to it, you can pick out the following bits at the start:

  • 0 – the “start bit” for the first byte in the message.
  • Four 0s – representing MIDI channel 1.
  • 1001 – representing MIDI command 9 (b1001 in binary).
  • 1 – a “stop bit” for the first byte.
  • 0-00111100-1 – 0x3C (with start/stop bits) which represents note 60 or C4.
  • 0-11111110-1 – 0x7F (with start/stop bits) which represents a note velocity of 127.]

Notice how the bit patterns are “back to front” in that we see them “least significant bit” first in the trace as that is how they appear on the serial line.  This is why the first byte is 0x90 but we see it as “09” and why the last byte looks like 0xFE but we decode it as 0x7F.

It is more obvious in the second trace, which is the corresponding “note off” message where the first byte is 0-00001000-1 or 0x80.

So this is pretty useful to see this much, but I have a microcontroller – why am I trying to count HIGHs and LOWs myself?

Hence the second mode of operation, selected by commenting out the GRAPHMODE definition at the top of the file – outputting to the Serial Monitor.

I still wanted a plot of the signal though, so use “ASCII art” to output something that looks like a trace.  Here is an example.

ArduinoMIDIAnalyser-Trace

It uses a combination of “-” and “_” for the HIGH or LOW indications, one line apart.  Then it decodes the buffer by taking a reading every SAMPLE values (SAMPLE is the sample rate, which in my case is 4) – that is where the 0 and 1 readings come from.

Next, as an aid to debugging it outputs the anticipated pattern of START and STOP bits that is used both for visual comparison and later on to see if it looks like there is a valid MIDI message.

Then the buffer is passed off to a decode routine to pull out the three MIDI bytes and work out what they mean.  I do this by storing the first 32 values in the buffer as bits in a 32-bit value (using the uint32_t type – “msg” in the code below).  This means it is easy to check for a valid message by “masking off” the START and STOP bits and making sure they are 0 and 1 in the right places.

// This is the pattern we're looking for wrt start and stop bits.
//                         ..1........01........01........0
uint32_t startstop     = 0b00100000000010000000001000000000;
uint32_t startstopmask = 0b00100000000110000000011000000001;
if ((msg & startstopmask) != startstop) {
  Serial.print ("No valid message found\n");
  return;
}

Then I can use bit shifting in the decoding routine to pull the data out from the start/stop bits as follows.

msg = msg >>1; // Skip start bit
uint8_t b1 = msg & 0xff;
msg = msg >> 10; // skip byte, stop bit, next start bit
uint8_t b2 = msg & 0xff;
msg = msg >> 10; // skip byte, stop bit, next start bit
uint8_t b3 = msg & 0xff;

There was one “gotcha” with all this though.  When I was building up the 32-bit value in “msg” initially, I was using code like the following:

uint32_t msg=0;
...
  if (buf[i]) {
    msg |= (1<<msgbit);
  } else {
    // Leave as zero
  }
  msgbit++;

Which all looks fine… except I was only getting values in the lower 16 bits of “msg” even though msg was defined as a “uint32_t”.  The reason for this turned out to be quite subtle.

As the Arduino Uno is an 8-bit microcontroller, it doesn’t have a native 32-bit type and “int”, which is always meant to be “the most optimum number for your processor to use” is a 16-bit value.  This means that by default the number 1 on its own in the (1<<msgbit) line is interpreted as (int)1. This means that the (1<<msgbit) calculation is treated as a 16-bit calculation so for all values of msgbit greater than 15, (1<<msgbit) will overflow the 16-bit value and the result will be 0.

The fix is to replace the line with the following.

 msg |= (1UL<<msgbit);

The “1UL” means “treat this 1 as if it was a 32-bit number” which means that it can now be shifted left up to 31 times and everything works fine without overflowing.

The final code only works on three-byte messages.  A two byte message will be seen on the trace but won’t be decoded properly.  It can’t understand multi-byte messages either, such as system exclusive messages or anything sending MIDI “Running Status” batched messages.

But as all I’m after is a bit of an indication that it looks like valid MIDI, I wasn’t too worried about accurate decoding. I’m not after a full “high speed protocol decoder” here. I just want something that when I use a MIDI project in test mode – sending out a single message say – I can get a quick indication of whether it looks valid when “on the wire” so to speak.

One thing I did want to add however was to give an estimation of the baud rate.  I do this by looking through the buffer for a sequence of “1”s that is more than a single byte’s worth of data.  If I find this, I assume this happens at the end of a message, starting from the final STOP bit, and that the message started at the beginning of the buffer.  Assuming a three-byte message I know there are 30 bits between these two points – three 8 bit messages, each with a start and stop bit – so I can work out the “time per bit” and consequently a baud rate within certain error limits.

One final point.  I tested this with one of my Ready-Made MIDI Modules hooked up to the PC via my Roland UM-ONE and there is a regular short “pulse” on the serial line for some reason I’ve yet to look into – I know MIDI has the concept of timing pulses and the like, but I’ve never really looked into those.  But this appears as a short – maybe two LOW bits – signal that triggers the analyser but then has a buffer mostly full of “1”s.  So in the interests of keeping things simple, I’ve added a short section of filtering which essentially says “discard the buffer if it looks like it is pretty much all 1s”.  This can be disabled using a if required.

Here is the actual ASCII trace of the above tests.

                  ----        --------            ----------------        ----    ----------------------------    --------------------------------------------------------------------------------------
__________________    ________        ____________                ________    ____                            ____                                                                                      

0   0   0   0   0   1   0   0   1   1   0   0   0   1   1   1   1   0   0   1   0   1   1   1   1   1   1   1   0   1   1   1   1   1   1   1   1   1   1   1   1   1   1   1   1   1   1   1   1   1   
0                                   1   0                                   1   0                                   1                                                                                   
>>>>>  0x903C7F		1	0x9	60	127	NoteOn	 C4
>>>>>  Estimated baud rate: 31328 (31250 + 0.2%)
                               --------            ----------------        ----                                    -------------------------------------------------------------------------------------
_______________________________        ____________                ________    ____________________________________                                                                                     

0   0   0   0   0   0   0   0   1   1   0   0   0   1   1   1   1   0   0   1   0   0   0   0   0   0   0   0   0   1   1   1   1   1   1   1   1   1   1   1   1   1   1   1   1   1   1   1   1   1   
0                                   1   0                                   1   0                                   1                                                                                   
>>>>>  0x803C00		1	0x8	60	0	NoteOff	 C4
>>>>>  Estimated baud rate: 31055 (31250 - 0.6%)
                    ----        --------        --------------------        ----    ----------------------------    ------------------------------------------------------------------------------------
____________________    ________        ________                    ________    ____                            ____                                                                                    

0   0   0   0   0   1   0   0   1   1   0   0   1   1   1   1   1   0   0   1   0   1   1   1   1   1   1   1   0   1   1   1   1   1   1   1   1   1   1   1   1   1   1   1   1   1   1   1   1   1   
0                                   1   0                                   1   0                                   1                                                                                   
>>>>>  0x903E7F		1	0x9	62	127	NoteOn	 D4
>>>>>  Estimated baud rate: 30788 (31250 - 1.4%)
                              --------        --------------------        ----                                    --------------------------------------------------------------------------------------
______________________________        ________                    ________    ____________________________________                                                                                      

0   0   0   0   0   0   0   0   1   1   0   0   1   1   1   1   1   0   0   1   0   0   0   0   0   0   0   0   0   1   1   1   1   1   1   1   1   1   1   1   1   1   1   1   1   1   1   1   1   1   
0                                   1   0                                   1   0                                   1                                                                                   
>>>>>  0x803E00		1	0x8	62	0	NoteOff	 D4
>>>>>  Estimated baud rate: 31328 (31250 + 0.2%)

Find the code on GitHub here.

Closing Thoughts

I’m really pleased with how this turned out. I’ve haven’t given it a robust set of tests, but I have tried it with a few different baud rates and it can cope.  With a larger buffer and larger “laststop” check it actually worked out a baud rate down to 19200, but in normal working, I’d expect it to really be looking at 30000 onwards.

There are a number of options for taking this further:

  • Build in more complete MIDI decoding, in particular allow it to expand beyond three-byte messages.
  • Implement a proper “circular buffer”, or at least the concept of “pre-recording”, to allow it to capture the whole trace.
  • Possibly implement some filtering – e.g. only look for specific MIDI channels, or MIDI commands.
  • Give the option of manual triggering or maybe manual resetting once triggered.
  • If memory and timing constraints allow it, it might be possible to use several digital IO pins and monitor several MIDI serial streams at the same time.

But most of all what I’d like to do is get a display built-in to the current system so it isn’t relying on the USB serial port.  Ideally something like the TFT display I used before.  Then I really would have a simple device that only needed power and the link to the MIDI signal to show me what is going on.

Kevin

One thought on “Arduino MIDI Logic Analyser

  1. Hi Kevin,
    First of all great work you did with the MIDI Logic Analyzer.

    I have some questions about deciphering my code read by the Arduino and wonder if it would be possible to chat via email?

    Best Regard

    Chris

    Like

Leave a comment