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); } } }