In this project, we created a simple workflow to convert a standard .mid MIDI file into binary note data suitable for playback on an Arduino Mega 2560 using PlatformIO.
The Arduino reads the notes from memory and plays them using the tone() function on a PWM output pin.
We used Python with the mido library to parse MIDI files and extract note_on events.
These were saved into a header file as an array of [note, duration] pairs.
import mido
MIDI_FILE = "input.mid"
TICK_DIV = 10 # ms per duration unit
OUTPUT_HEADER = "midi_song.h"
mid = mido.MidiFile(MIDI_FILE)
events = []
for msg in mid:
if not msg.is_meta and msg.type == 'note_on' and msg.velocity > 0:
note = msg.note
duration = int(msg.time * 1000 / TICK_DIV)
duration = max(1, min(duration, 255))
events.append((note, duration))
with open(OUTPUT_HEADER, "w") as f:
f.write("#pragma once\n\n")
f.write("#include <stdint.h> // ✅ This works on all Arduino platforms")
f.write(f"const uint16_t SONG_LENGTH = {{len(events)}};\n")
f.write("const uint8_t songData[][2] = {\n")
for note, duration in events:
f.write(f" {{{note}, {duration}}},\n")
f.write("};\n")
print(f"Generated {{OUTPUT_HEADER}} with {{len(events)}} notes.")
This Arduino code reads the generated header file and plays each note using tone().
We used PlatformIO with the Arduino Mega 2560.
#include "midi_song.h"
const uint8_t speakerPin = 8;
uint16_t midiNoteToFreq(uint8_t note) {
return 440 * pow(2, (note - 69) / 12.0);
}
void setup() {
pinMode(speakerPin, OUTPUT);
Serial.begin(115200);
Serial.println("Starting MIDI Playback");
}
void loop() {
for (uint16_t i = 0; i < SONG_LENGTH; i++) {
uint8_t note = songData[i][0];
uint8_t duration = songData[i][1];
Serial.print("Note: "); Serial.print(note);
Serial.print(" Duration: "); Serial.println(duration);
if (note > 0) tone(speakerPin, midiNoteToFreq(note));
delay(duration * 10);
noTone(speakerPin);
}
delay(1000); // Pause before repeating the song
}
This project gave us a simple way to turn a standard MIDI file into an embedded music box running on an Arduino Mega.
Using PlatformIO allowed for better workflow and serial debugging, and the Python script made conversion fast and repeatable.
Happy hacking!