For this week, I created a Flask-hosted web interface to select MIDI files and stream them over WiFi to ESP32-C3 clients. The interface allows users to:
Here is the final result in action:
The Flask app serves a simple web interface for selecting and streaming MIDI files. The server communicates over sockets to send MIDI messages to connected ESP clients:
import socket
import select
import time
import mido
import os
from flask import Flask, render_template_string, request, redirect, url_for
app = Flask(__name__)
MIDI_DIR = 'midi'
HOST = ''
PORT = 5001
selected_file = None
desired_bpm = 120
clients = []
@app.route("/", methods=["GET", "POST"])
def index():
global selected_file, desired_bpm
midi_files = [f for f in os.listdir(MIDI_DIR) if f.endswith('.mid')]
if request.method == "POST":
selected_file = os.path.join(MIDI_DIR, request.form.get("file"))
desired_bpm = int(request.form.get("bpm", 120))
start_socket_server(selected_file, desired_bpm)
return redirect(url_for("index"))
return render_template_string(HTML_TEMPLATE, midi_files=midi_files, selected_file=selected_file, bpm=desired_bpm)
if __name__ == "__main__":
app.run(host="0.0.0.0", port=8080, debug=True)
The ESP32-C3 connects to the server and listens for MIDI messages. It maps received note events to solenoids, toggling their state:
#include <WiFi.h>
const char* ssid = "MIT";
const char* password = "CENSORED";
const char* host = "CENSORED";
const int port = 5001;
#define NUM_SOLENOIDS 5
int handledNotes[NUM_SOLENOIDS] = {60, 61, 62, 63, 64};
int solenoidPins[NUM_SOLENOIDS] = {2, 3, 4, 5, 6};
WiFiClient client;
void setup() {
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) delay(500);
client.connect(host, port);
for (int i = 0; i < NUM_SOLENOIDS; i++) {
pinMode(solenoidPins[i], OUTPUT);
}
}
void loop() {
if (client.available() > 0) {
uint8_t lengthByte;
client.read(&lengthByte, 1);
uint8_t message[256];
client.read(message, lengthByte);
uint8_t status = message[0];
uint8_t command = status & 0xF0;
if ((command == 0x90 || command == 0x80) && lengthByte >= 3) {
uint8_t note = message[1];
bool isOn = (command == 0x90 && message[2] > 0);
handleNoteEvent(note, isOn);
}
}
}
void handleNoteEvent(uint8_t note, bool isOn) {
for (int i = 0; i < NUM_SOLENOIDS; i++) {
if (handledNotes[i] == note) {
digitalWrite(solenoidPins[i], isOn ? HIGH : LOW);
}
}
}