arduino master MIDI clock unaccuracy

Hello everyone,

I am building a master MIDI metronome on an Arduino MEGA 2560 to control my drum machine and other electronic devices using a standard 5 pin midi connection.

My code is almost 100% functional, but the clock timing is not 100% accurate, thus causing a fluctuating BPM value.

A sample of the time between clock messages in micros at 60BPM taken directly from the serial monitor:

micros() elapsed time
6688928 3380
6692308 3380
6695688 3380
6699068 3380
6702448 3384
6705828 3376
6709208 3380
6712588 3380
6715968 3384
6719348 3376
6722728 3380
6726108 3380
6729488 3380
6732868 3380
6736248 3380
6739628 3380
6743008 3380
6746388 3380
6749768 3380
6753148 3380
6756528 3380
6759908 3384
6763288 3376

The resulting BPM value is not accurate either.

Attached there is an image of my breadboard. The in MIDI in connection and the display are out of use right now.

Any ideas?

I am also learning to code so corrections will be appreciated.

//From the book Arduino for Musicians
//Listing 4.9: Metronome 3 (Complete Metronome)
//Added midi output functionality for master clock use

//LEDS;
const int ledPin[] = {10, 9, 8, 7};
int ledState[] = {LOW, LOW, LOW, LOW};

//midi_clock values
byte midi_clock = 0xf8; //Declare the hex value of a midi clock message
byte midi_start = 0xfa; //Declare the hex value of a midi clock start message
byte midi_stop = 0xfc; //Declare the hex value of a midi clock stop message

//Potentiometer and smooth filter
const int potPin = 0;
const int numReadings = 20;    // a higher number make smoother readings
int readings[numReadings];      // the readings from the analog input
int readIndex = 0;              // the index of the current reading
unsigned long total = 0;        // the running total
unsigned long average = 0;      // the average
int preValue;                   // Variable to track the value of the pot

//Switch pin:
const int pushButtonPin = 2 ;

//Variable to track pushbutton status
int pushButtonStatus = LOW;

//A boolean variable to track if metronome is on or off:
boolean on = false;

//Variables to track tempo and time delay
unsigned int beats_per_minute = 60;
unsigned int milliseconds_per_minute = 1000 * 60;
unsigned int MS_per_beat = milliseconds_per_minute/beats_per_minute;

//Variables to track time
long last_time = 0;
int counter = 0;//Counts the number of ticks
int ledOn = 0;

byte offSequence = 1;

int previousLed = 0;


void setup()
{
    //interrupts();
    //Initialize serial port
    Serial.begin(31250); //Change to 38400 for debugging. MIDI 31250
    
    // set the digital pins 10 to 13 as output for leds:
    
    pinMode (ledPin[0], OUTPUT);
    pinMode (ledPin[1], OUTPUT);
    pinMode (ledPin[2], OUTPUT);
    pinMode (ledPin[3], OUTPUT);

    //Set the digital pin for input and pullup resistor:
    pinMode(pushButtonPin, INPUT_PULLUP);

    // initialize all the readings to 0:
    for (int thisReading = 0; thisReading < numReadings; thisReading++)
    {
      readings[thisReading] = 0;
    }
}

void loop()
{
    smoothFilter();
    checkPushButton();
    checkPot();
    
    long current_time = millis();    //Time reference. Elapsed time since board is on
    int ticks = MS_per_beat/24;      //24 ticks per beat
    int elapsed_time = current_time - last_time;
    
    //Midi clock sending routine
    if(elapsed_time >= ticks && on == true)
    {
      //Serial.write(midi_clock);
      last_time = current_time;
      counter++;
      ledBlink (ledOn);

      if (counter >= 24)
      {
        ledOn ++;
        counter = 0;
      }
      if (ledOn >= 4)
      {
        ledOn = 0;
      }    
    }
    //Switch off every led
    if (on == false)
    {
    
      if (offSequence)
      {
        for (int i = 0; i <=3; i++)
        {
        digitalWrite(ledPin[i], LOW);
        } 
       offSequence = offSequence -1;
      }
      counter = 0;
      ledOn = 0;
      previousLed = 0;
    }
}

void ledBlink (int ledOn)//Led blink routine
{
  previousLed = ledOn - 1;
  if (previousLed == -1)
  {
    previousLed = 3;
  }     
    ledState[ledOn] = HIGH;
    digitalWrite(ledPin[ledOn], ledState[ledOn]);
    digitalWrite(ledPin[previousLed], LOW);
}

void checkPot()
{
    int value = average;

    //Re-calculate the tempo if the value has changed.
    if(value != preValue)
    {
       //Map the value to a reasonable metronome range of 30 BPM to 350 BPM
       beats_per_minute = map(value, 0, 1023, 60, 240);

       //Recalculate the delay time
     MS_per_beat = milliseconds_per_minute/beats_per_minute; 
       //Update preValue
       preValue = value;
    }

void smoothFilter()
{
  //From http://www.arduino.cc/en/Tutorial/Smoothing
  // subtract the last reading:
  total = total - readings[readIndex];
  // read from the sensor:
  readings[readIndex] = analogRead(potPin);

  // add the reading to the total:
  total = total + readings[readIndex];
  
  // advance to the next position in the array:
  readIndex ++;

  // if we're at the end of the array...
  if (readIndex >= numReadings)
  {
    // ...wrap around to the beginning:
    readIndex = 0;
  }
  // calculate the average:
  average = total / numReadings;
}

void checkPushButton()
{
  //Check digital pin 2 for a button press
  int button_state = digitalRead(pushButtonPin);
    
  //Check for button press
  if(button_state == LOW && button_state != pushButtonStatus)
  {
    //Send start message
    Serial.write(midi_start);

    //Switch on the first led
    ledBlink (0);

    //Swap on/off states
    on =! on;
       
    //update buttonStatus;
    pushButtonStatus = button_state;
    //Use delay for a kludgy form of debouncing
    delay(20);
   }
    
  //Check for button release
  if(button_state == HIGH && button_state != pushButtonStatus)
  {
    //Send stop message
    Serial.write(midi_stop);
    //Reset counter
    offSequence = 1;
        
    //Updtate the pushbutton status to off
    pushButtonStatus = button_state;
    delay(20);
   }
}

Hi,
Your list of sample times range from 3376 to 3384, that's only +-4 millionths of a second.

Also notice that the deviations come in pairs:

6699068 3380
6702448 3384 +4
6705828 3376 - 4
6709208 3380

The resolution of the micros() probably only counts in 4's so you occasionally see plus or minus 1 count.

Don't worry about it.

Yours,
TonyWilk