Idea: A friend and I are building a metronome with an (Elegoo) Arduino-Mega which additionaly can send out a MIDI-Clock signal over the serialport. Blinking LED in sync with set BPM value, BPM-Control over rotary encoders and everything else works just fine. Only sending the MIDI signal over serial gives us headaches.
The Problem: The MIDI Clock signal (0xF8) needs to be sent 24 times on each beat. Therefore we simply calculated the time between the clock ticks and after the time interval is passed, we send a 0xF8 over Serial. Easy. But when we hooked it up to a Ditto X4 Guitar Looper the LED blinking of our metronome and that of the looper went out of sync. So we wrote a little script in C# .NET to verify what is being sent over Serial and it turns out, depending of the set BPM some Messages aren't being sent at all or are delayed which leads to the looper calculating a different BPM than we try to send out (Screenshot of script output).
But we are completely lost here. Why are some Messages delayed/not sent? Even on "normal" Baud rates like 9600 the Problem is the same. And it doesn't seem to scale with Arduino CPU usage or set BPM:
Set BPM: Lost Message every x Messages:
300 24-26
150 10-12
50 4-5
We also tested an Arduino Uno R3 (also from Elegoo) but the Problem is the same.
This sample script can be used to replicate the Problem:
#include <Arduino.h> //Einbinden der Arduino Bibliothek
//Timer Variables
unsigned long startTimeMIDI = 0;
unsigned long currentTime = 0;
unsigned long intervalLED;
unsigned long intervalMIDI;
short counter_BPM = 300 * 2; // Internally we use BMP*2
void setup()
{
Serial.begin(31250); //Forced by the MIDI standard
while ( !Serial ) /*wait for serial init*/ ;
}
void loop()
{
currentTime = micros();
intervalLED = (120000000/counter_BPM); //60000000*(BPM/2)
intervalMIDI = intervalLED/24; //Midi Clock has to be sent 24 times for each beat
if (currentTime - startTimeMIDI > intervalMIDI){
Serial.write(0xF8); //send MIDI Clock signal
startTimeMIDI = currentTime; //reset timer value
}
}
This is the C# script used to monitor what is sent:
static void Main(string[] args)
{
serial = new SerialPort("COM4", 31250);
serial.Open();
int cycleSize = 50; //Averaging over 50 Values
long[] latencyList = new long[cycleSize+1];
Stopwatch watch = new Stopwatch();
watch.Start();
int n = 0;
while(true)
{
n++;
watch.Start();
int response = serial.ReadByte();
watch.Stop();
long latency = watch.ElapsedTicks/(Stopwatch.Frequency/(1000L*1000L));
watch.Reset();
if (n <= cycleSize)
{
latencyList[n] = latency;
}
else
{
latencyList[n % cycleSize] = latency;
}
double average = latencyList.Average();
Console.WriteLine("" + n + " " + latency.ToString("000000") + "µs - response:" + response + " - Average: " + average.ToString("##0.00") + " - BPM: " + (int)(60000000/(average * 24)));
}
}
EDIT: (May 9, 2020) I need to clarify the Problem with the guitar Looper: Since the looper is used to sync it's effects to the rest of the Band this is the most important Problem. The blinking of the Arduinos BPM LED (we tapped it and came close enough at a variety of set BPMs to consider it accurate enough) and the blinking of the Loopers LED drift away from each other too fast to be acceptable. We put the LEDs right beside each other and they go from being in sync to blinking alternately in the matter of ~30 seconds so in a live concert everything would fall apart. Since the loopers LED blinking is triggered by the MIDI input it receives, we looked at the consistency of the sent clock signals and discovered the odd delay between signals.
if (currentTime - startTimeMIDI > intervalMIDI){;
? – Tom