🔧 SysEx (System Exclusive)¶
O MIDIHandler suporta envio e recepção de mensagens SysEx em todos os transportes — USB Host, USB Device e UART (DIN-5). A implementação usa uma fila separada da fila de eventos normais, sem breaking changes para código existente.
O que é SysEx¶
SysEx (System Exclusive) são mensagens MIDI de tamanho variável delimitadas por 0xF0 (início) e 0xF7 (fim). Diferente de NoteOn, CC ou PitchBend que têm tamanho fixo (2-3 bytes), uma mensagem SysEx pode ter de poucos bytes a vários kilobytes.
Enquanto mensagens de canal (notas, CCs) são padronizadas e genéricas, SysEx carrega dados proprietários ou universais que vão muito além do que o MIDI convencional oferece.
O que o SysEx abre de possibilidades¶
Identificação de dispositivos — envie um Universal Identity Request (F0 7E 7F 06 01 F7) e receba de volta o manufacturer ID, família, modelo e versão de firmware. Seu ESP32 pode auto-detectar o que está conectado e adaptar o comportamento. Plugou um Roland, um Korg, um Novation — o ESP32 sabe com quem está falando.
Gerenciamento de patches — a maioria dos sintetizadores usa SysEx para bulk dumps: patches, presets, wavetables, configurações inteiras. Com o buffer configurável você pode receber um dump completo, salvar em SPIFFS/SD e enviar de volta depois. Seu ESP32 vira um patch librarian.
Controle profundo de parâmetros — muitos dispositivos expõem parâmetros via SysEx que não existem como CC. Filter types, oscillator shapes, effects routing — coisas que vão muito além dos 128 valores de CC. Alguns dispositivos têm centenas de parâmetros acessíveis só via SysEx.
Atualização de firmware — alguns dispositivos MIDI aceitam update de firmware via SysEx. Com sendSysEx() dá pra construir um atualizador baseado em ESP32.
MIDI Sample Dump — existe um padrão (MMA Sample Dump Standard) para transferir samples de áudio via SysEx. É lento, mas funciona, e alguns samplers vintage só suportam esse método.
Como funciona internamente¶
Remontagem no nível do transporte¶
Cada transporte lida com a remontagem de forma diferente:
USB (Host e Device) — O protocolo USB MIDI 1.0 fragmenta SysEx em pacotes de 4 bytes com um Code Index Number (CIN) no header:
| CIN | Significado | Bytes de dados |
|---|---|---|
0x04 |
SysEx começa ou continua | 3 |
0x05 |
SysEx termina com 1 byte | 1 |
0x06 |
SysEx termina com 2 bytes | 2 |
0x07 |
SysEx termina com 3 bytes | 3 |
A biblioteca acumula os pacotes CIN 0x04 em um buffer interno e, ao receber 0x05/06/07, monta a mensagem completa e despacha via dispatchSysExData().
UART (DIN-5) — MIDI serial é mais simples: os bytes chegam sequencialmente. O parser acumula tudo entre 0xF0 e 0xF7. Se um status byte diferente chegar no meio (erro de protocolo), o SysEx é abortado e o novo status é processado normalmente.
Fila separada¶
SysEx usa sua própria std::deque<MIDISysExEvent>, completamente separada da fila de eventos (getQueue()). Isso foi uma decisão de design proposital — mensagens SysEx são de tamanho variável e misturá-las na fila existente quebraria a API para quem já usa getQueue().
Configuração¶
MIDIHandlerConfig config;
config.maxSysExSize = 512; // máximo de bytes por mensagem (inclui F0 e F7)
config.maxSysExEvents = 8; // quantas mensagens manter na fila
midiHandler.begin(config);
| Parâmetro | Default | Descrição |
|---|---|---|
maxSysExSize |
512 | Tamanho máximo de uma mensagem. Mensagens maiores são truncadas. 0 = desabilita SysEx. |
maxSysExEvents |
8 | Capacidade da fila. Mensagens mais antigas são descartadas. |
Sobre o tamanho do buffer
O ESP32-S3 tem 512KB de SRAM. Mesmo maxSysExSize = 2048 funciona tranquilo. O buffer é alocado no heap (std::vector<uint8_t>), então considere a RAM disponível se estiver rodando BLE, WiFi e display simultaneamente.
API¶
Recepção¶
// Acessar a fila de SysEx
const auto& queue = midiHandler.getSysExQueue();
for (const auto& msg : queue) {
// msg.index — contador global (crescente)
// msg.timestamp — millis() no momento da recepção
// msg.data — std::vector<uint8_t>, mensagem completa (F0 ... F7)
Serial.printf("SysEx #%d (%d bytes): ", msg.index, msg.data.size());
for (uint8_t b : msg.data) Serial.printf("%02X ", b);
Serial.println();
}
// Limpar a fila
midiHandler.clearSysExQueue();
Callback em tempo real¶
// Opcional: callback imediato em vez de polling
midiHandler.setSysExCallback([](const uint8_t* data, size_t len) {
// Chamado assim que a mensagem completa é montada
// data[0] = 0xF0, data[len-1] = 0xF7
});
Envio¶
// Enviar SysEx — transmite para todos os transportes
const uint8_t identityReq[] = { 0xF0, 0x7E, 0x7F, 0x06, 0x01, 0xF7 };
bool ok = midiHandler.sendSysEx(identityReq, sizeof(identityReq));
// Retorna false se a mensagem não começar com F0 ou não terminar com F7
Compatibilidade¶
A implementação segue a spec USB MIDI 1.0 (remontagem por CIN) e o framing padrão de MIDI serial. Funciona com qualquer dispositivo class-compliant — se ele manda SysEx válido entre F0 e F7, a biblioteca captura.
Error checking
SysEx não tem error checking nativo na spec MIDI. O transporte USB é confiável (CRC no nível USB), mas UART/DIN-5 é serial cru. Alguns dispositivos implementam checksums próprios dentro do payload SysEx — a biblioteca entrega os bytes crus pra que você valide no nível da aplicação.
Exemplo completo¶
Veja o exemplo T-Display-S3-SysEx — monitor SysEx com Identity Request no botão, display em tempo real:
