🚀 Primeiros Passos¶
Neste guia você terá o ESP32 recebendo e enviando MIDI em menos de 5 minutos.
Pré-requisito¶
- Biblioteca instalada (veja Instalação)
- ESP32-S3 com cabo USB-OTG ou qualquer ESP32 com Bluetooth
Passo 1 — O Sketch Mais Simples¶
Este sketch imprime todos os eventos MIDI recebidos via USB Host ou BLE no Serial Monitor:
#include <ESP32_Host_MIDI.h>
// Arduino IDE: Tools > USB Mode → "USB Host"
void setup() {
Serial.begin(115200);
midiHandler.begin(); // (1)
}
void loop() {
midiHandler.task(); // (2)
for (const auto& ev : midiHandler.getQueue()) { // (3)
Serial.printf("%-12s %-5s ch=%d vel=%d\n",
ev.status.c_str(), // "NoteOn" | "NoteOff" | "ControlChange"...
ev.noteOctave.c_str(), // "C4", "D#5"...
ev.channel,
ev.velocity);
}
}
Anotações:
begin()inicializa automaticamente USB Host (se o chip suportar) e BLE (se habilitado)task()deve ser chamado em todoloop()— ele drena os ring buffers de todos os transportesgetQueue()retorna a fila de eventos desde a última chamada detask()
Passo 2 — Acessar Campos do Evento¶
Cada evento tem os seguintes campos:
for (const auto& ev : midiHandler.getQueue()) {
// Identificação
ev.index; // Contador global de eventos (único, crescente)
ev.timestamp; // millis() no momento da chegada
ev.delay; // Δt em ms desde o evento anterior
// Tipo de mensagem
ev.status; // "NoteOn" | "NoteOff" | "ControlChange" |
// "ProgramChange" | "PitchBend" | "ChannelPressure"
ev.channel; // Canal MIDI: 1–16
// Nota (apenas NoteOn / NoteOff)
ev.note; // Número MIDI: 0–127 (60 = C4 = Dó central)
ev.noteName; // "C", "C#", "D"... (sem oitava)
ev.noteOctave; // "C4", "D#5", "G3"... (com oitava)
ev.velocity; // Velocidade: 0–127 (também: valor CC, program, pressure)
// Agrupamento de acordes
ev.chordIndex; // Notas simultâneas compartilham o mesmo chordIndex
// Pitch Bend (apenas PitchBend)
ev.pitchBend; // 0–16383 (centro = 8192)
}
Exemplo — Apenas NoteOn¶
for (const auto& ev : midiHandler.getQueue()) {
if (ev.status == "NoteOn" && ev.velocity > 0) {
Serial.printf("Nota: %s Velocidade: %d Canal: %d\n",
ev.noteOctave.c_str(),
ev.velocity,
ev.channel);
}
}
Exemplo — Control Change¶
for (const auto& ev : midiHandler.getQueue()) {
if (ev.status == "ControlChange") {
// ev.note = número do controlador (CC#)
// ev.velocity = valor do controlador (0–127)
Serial.printf("CC #%d = %d (canal %d)\n",
ev.note, ev.velocity, ev.channel);
}
}
Passo 3 — Enviar MIDI de Volta¶
Todos os métodos de envio transmitem simultaneamente para todos os transportes ativos:
// NoteOn: canal 1, nota C4 (60), velocidade 100
midiHandler.sendNoteOn(1, 60, 100);
// NoteOff: canal 1, nota C4 (60), velocidade 0
midiHandler.sendNoteOff(1, 60, 0);
// Control Change: canal 1, CC #7 (volume) = 127
midiHandler.sendControlChange(1, 7, 127);
// Program Change: canal 1, programa 0
midiHandler.sendProgramChange(1, 0);
// Pitch Bend: canal 1, valor -8192 a +8191 (0 = centro)
midiHandler.sendPitchBend(1, 4096); // +0.5 semitom
Exemplo — Echo MIDI (loopback)¶
Recebe qualquer NoteOn e reenvia para todos os transportes:
void loop() {
midiHandler.task();
for (const auto& ev : midiHandler.getQueue()) {
if (ev.status == "NoteOn") {
// Reenvia com velocidade dobrada (limitada a 127)
uint8_t vel = min(ev.velocity * 2, 127);
midiHandler.sendNoteOn(ev.channel, ev.note, vel);
}
if (ev.status == "NoteOff") {
midiHandler.sendNoteOff(ev.channel, ev.note, 0);
}
}
}
Passo 4 — Verificar Conexão BLE¶
void loop() {
midiHandler.task();
#if ESP32_HOST_MIDI_HAS_BLE
if (midiHandler.isBleConnected()) {
Serial.println("BLE MIDI conectado!");
}
#endif
}
Conectar via iOS
- Abra GarageBand no iPhone
- Toque em "+" → Música Mixada → Iniciar
- Menu de configurações → Conectar dispositivo MIDI via Bluetooth
- O ESP32 aparecerá como "ESP32 MIDI BLE" (ou o nome configurado)
Passo 5 — Sketch Completo com USB + BLE¶
#include <ESP32_Host_MIDI.h>
// Arduino IDE: Tools > USB Mode → "USB Host"
void setup() {
Serial.begin(115200);
delay(1000);
Serial.println("ESP32 Host MIDI — Iniciando...");
MIDIHandlerConfig cfg;
cfg.maxEvents = 20; // capacidade da fila
cfg.chordTimeWindow = 50; // ms para agrupar acordes
midiHandler.begin(cfg);
Serial.println("Pronto! Conecte um teclado USB ou use BLE.");
}
void loop() {
midiHandler.task();
for (const auto& ev : midiHandler.getQueue()) {
if (ev.status == "NoteOn" && ev.velocity > 0) {
Serial.printf("[NoteOn] %s vel=%3d ch=%d t=%lums\n",
ev.noteOctave.c_str(),
ev.velocity,
ev.channel,
ev.timestamp);
} else if (ev.status == "NoteOff" || ev.velocity == 0) {
Serial.printf("[NoteOff] %s ch=%d\n",
ev.noteOctave.c_str(),
ev.channel);
} else if (ev.status == "ControlChange") {
Serial.printf("[CC] #%3d = %3d ch=%d\n",
ev.note, ev.velocity, ev.channel);
} else if (ev.status == "PitchBend") {
Serial.printf("[Pitch] %d (centro=8192) ch=%d\n",
ev.pitchBend, ev.channel);
}
}
}
Fluxo de Inicialização¶
sequenceDiagram
participant SKETCH as Seu Sketch
participant HANDLER as MIDIHandler
participant USB as USBConnection
participant BLE as BLEConnection
SKETCH->>HANDLER: midiHandler.begin()
HANDLER->>HANDLER: Lê MIDIHandlerConfig
HANDLER->>USB: usbTransport.begin()
Note over USB: Inicia FreeRTOS task\nno Core 0
HANDLER->>BLE: bleTransport.begin(bleName)
Note over BLE: Inicia stack BLE\ne advertising
HANDLER-->>SKETCH: Pronto
loop Cada loop()
SKETCH->>HANDLER: midiHandler.task()
HANDLER->>USB: usb.task()
HANDLER->>BLE: ble.task()
USB-->>HANDLER: Eventos do ring buffer
BLE-->>HANDLER: Eventos do ring buffer
HANDLER-->>SKETCH: getQueue() com eventos
end
Próximos Passos¶
- Configuração → — ajustar
MIDIHandlerConfig, filtros e histórico - Transportes → — adicionar mais transportes (RTP-MIDI, UART, OSC...)
- Funcionalidades → — detecção de acordes, notas ativas