Hey Everyone, i find that theres not an abundance of MIDI stuff for seed and i wrote an isolated test script that maybe you can edit and make applicable for your own stuff.
Note pin changes might be necessary. There was some inspo pulled from KnightHill’s Simple MIDI Clock Example for pod but made it orientated towards seed DFM
Enjoy
#include "daisy_seed.h"
#include "daisysp.h"
using namespace daisy;
using namespace daisysp;
// Globals
DaisySeed hw;
MidiHandler<MidiUartTransport> midi;
// Constants
constexpr uint8_t CLOCK_MSG = 0xF8;
constexpr uint8_t START_MSG = 0xFA;
constexpr uint8_t STOP_MSG = 0xFC;
constexpr int CLOCKS_PER_BEAT = 24;
constexpr dsy_gpio_pin BEAT_PULSE_PIN = {DSY_GPIOB, 9};
dsy_gpio beat_gpio;
float current_bpm = 120.0f; // Default tempo
// MIDI Transport State
bool is_running = false;
bool led_state = false;
uint32_t clock_count = 0;
uint32_t prev_tick_us = 0;
uint32_t last_midi_us = 0;
// Beat pulse state
bool pulse_active = false;
uint32_t pulse_start_time = 0;
bool midi_timed_out = false;
// DMA buffer for UART
__attribute__((aligned(4))) uint8_t midi_rx_buffer[512] __attribute__((section(".dma_buffer")));
static uint32_t fus_to_bpm(uint32_t us)
{
float fus = static_cast<float>(us);
float val = roundf(60000000.0f / fus);
return static_cast<uint32_t>(val);
}
void ProcessMidi()
{
while (midi.HasEvents())
{
MidiEvent e = midi.PopEvent();
uint8_t status = e.data[0];
hw.PrintLine("[RAW MIDI] 0x%02X", status);
hw.SetLed(true); //
System::Delay(10); //
hw.SetLed(false); // Test to see if LED works -> it does
last_midi_us = System::GetUs(); // mark clock arrival
// Only filter channel messages, allow all others (like START, CLOCK)
if ((status & 0xF0) >= 0x80 && (status & 0xF0) <= 0xE0)
{
uint8_t channel = status & 0x0F;
if (channel != 0)
{
hw.PrintLine("[Filter] Ignored channel: %d", channel + 1);
continue;
}
}
switch (status)
{
case START_MSG:
is_running = true;
clock_count = 0;
hw.PrintLine("MIDI START received");
break;
case STOP_MSG:
is_running = false;
hw.SetLed(false);
hw.PrintLine("MIDI STOP received");
break;
case CLOCK_MSG:
if (is_running)
{
clock_count++;
if (clock_count % CLOCKS_PER_BEAT == 0)
{
uint32_t now = System::GetUs();
uint32_t diff = now - prev_tick_us;
prev_tick_us = now;
uint32_t bpm = fus_to_bpm(diff);
current_bpm = bpm;
hw.PrintLine(" BPM: %lu", bpm);
// Non-blocking GPIO pulse
dsy_gpio_write(&beat_gpio, true);
pulse_active = true;
pulse_start_time = now;
// Toggle LED per beat
// led_state = !led_state;
if (!midi_timed_out)
hw.SetLed(true); // flash LED on any MIDI input
}
}
break;
default:
hw.PrintLine("[MIDI] Unhandled Status: 0x%02X", status);
break;
}
}
}
void AudioCallback(AudioHandle::InputBuffer in, AudioHandle::OutputBuffer out, size_t size)
{
for (size_t i = 0; i < size; i++)
{
out[0][i] = in[0][i];
out[1][i] = in[1][i];
}
}
void CheckMidi()
{
const uint32_t now = System::GetUs();
const uint32_t timeout_us = 1000000;
midi.Listen();
if (now - last_midi_us > timeout_us)
{
if (!midi_timed_out)
{
hw.PrintLine("MIDI timeout");
hw.SetLed(true); // Solid LED = no MIDI
midi_timed_out = true; // Enter timeout mode
}
is_running = false;
}
else
{
if (midi_timed_out)
{
hw.PrintLine("MIDI signal resumed");
hw.SetLed(false); // Clear LED to allow beat pulsing
midi_timed_out = false; // Exit timeout mode
}
}
}
int main(void)
{
hw.Configure();
hw.Init();
// System::Delay(1000);
// hw.StartLog(true);
// System::Delay(10);
// dsy_gpio beat_gpio;
beat_gpio.pin = BEAT_PULSE_PIN;
beat_gpio.mode = DSY_GPIO_MODE_OUTPUT_PP;
dsy_gpio_init(&beat_gpio);
hw.SetAudioBlockSize(4);
hw.SetAudioSampleRate(SaiHandle::Config::SampleRate::SAI_48KHZ);
MidiUartHandler::Config midi_cfg;
midi_cfg.transport_config.periph = UartHandler::Config::Peripheral::USART_1;
midi_cfg.transport_config.rx = hw.GetPin(14); // PB7
midi_cfg.transport_config.tx = Pin();
midi_cfg.transport_config.rx_buffer = midi_rx_buffer;
midi_cfg.transport_config.rx_buffer_size = sizeof(midi_rx_buffer);
midi.Init(midi_cfg);
// midi.StartReceive();
hw.PrintLine("MIDI UART Initialized on PB7");
hw.StartAudio(AudioCallback);
last_midi_us = System::GetUs();
prev_tick_us = last_midi_us;
if (!pulse_active && !is_running)
{
hw.SetLed(false); // Safety off
}
while (1)
{
ProcessMidi();
CheckMidi();
// static uint32_t last_beat = 0;
// if ((System::GetUs() - last_beat) > 500000) // every 500ms
// {
// dsy_gpio_write(&beat_gpio, true);
// pulse_start_time = System::GetUs();
// pulse_active = true;
// hw.PrintLine("SIMULATED BEAT");
// last_beat = System::GetUs();
// }
if (pulse_active && (System::GetUs() - pulse_start_time > 20000))
{
dsy_gpio_write(&beat_gpio, false);
hw.SetLed(false); // turn off LED when pulse ends
pulse_active = false;
}
}
}