Bluetooth Low Energy MIDI Profile

[中文]

The BLE MIDI profile enables the transmission of MIDI (Musical Instrument Digital Interface) messages over Bluetooth Low Energy. It is designed for low-latency, low-power communication between MIDI devices such as musical instruments, controllers, and mobile applications.

BLE MIDI allows devices to exchange standard MIDI messages, including note events, control changes, and system messages, while benefiting from the reduced power consumption and flexible connection model provided by Bluetooth Low Energy.

The profile follows the MIDI over BLE specification defined by the MIDI Manufacturers Association (MMA) and the Bluetooth SIG.

Examples

bluetooth/ble_profiles/ble_midi.

API Reference

Header File

Functions

esp_err_t esp_ble_midi_profile_init(void)

Initialize the BLE MIDI profile.

This function must be called once before using any other BLE MIDI functions. It initializes internal synchronization primitives and state.

Returns

  • ESP_OK on success

  • ESP_ERR_NO_MEM if mutex creation fails

  • ESP_OK if already initialized (idempotent)

esp_err_t esp_ble_midi_profile_deinit(void)

Deinitialize the BLE MIDI profile.

This function cleans up internal resources including mutex and clears all callbacks and state. After calling this function, esp_ble_midi_profile_init() must be called again before using any other BLE MIDI functions.

Returns

  • ESP_OK on success

  • ESP_OK if already deinitialized (idempotent)

esp_err_t esp_ble_midi_register_rx_cb(esp_ble_midi_rx_cb_t cb, void *user_ctx)

Register application RX callback to receive BLE-MIDI event packets.

Called from the BLE host task; keep the handler short or hand off to another task.

Note

esp_ble_midi_profile_init() must be called before registering callbacks.

Parameters
  • cb[in] RX callback; pass NULL to unregister

  • user_ctx[in] User context pointer to be passed to the callback

Returns

  • ESP_OK on success

  • ESP_ERR_INVALID_STATE if esp_ble_midi_profile_init() has not been called

  • ESP_FAIL on other error

esp_err_t esp_ble_midi_register_event_cb(esp_ble_midi_event_cb_t cb)

Register parsed MIDI event callback.

Called once per decoded MIDI message (including SysEx and real-time). Runs in the BLE host task; keep the handler short or post to an app task.

Note

esp_ble_midi_profile_init() must be called before registering callbacks.

Parameters

cb[in] Event callback; pass NULL to unregister

Returns

  • ESP_OK on success

  • ESP_ERR_INVALID_STATE if esp_ble_midi_profile_init() has not been called

  • ESP_FAIL on other error

esp_err_t esp_ble_midi_send(const uint8_t *data, uint16_t len)

Send BLE-MIDI event packet via notification.

data must be a complete BEP payload (packet header timestamp high bits and a per-message timestamp byte before each message). Most applications should prefer esp_ble_midi_send_raw_midi() or the helper APIs.

Note

esp_ble_midi_profile_init() must be called before sending.

Parameters
  • data[in] Pointer to packet buffer

  • len[in] Packet length

Returns

  • ESP_OK on success

  • ESP_ERR_INVALID_ARG on invalid args

  • ESP_ERR_INVALID_STATE if esp_ble_midi_profile_init() has not been called or notification is not enabled

  • ESP_FAIL on other error

esp_err_t esp_ble_midi_send_raw_midi(const uint8_t *midi, uint8_t midi_len, uint16_t timestamp)

Build and send a BLE-MIDI Event Packet from raw MIDI bytes.

Packs the BLE-MIDI timestamps (packet header high bits + per-message low byte).

Parameters
  • midi[in] Pointer to MIDI message bytes (status + data)

  • midi_len[in] Length of MIDI message (1-3 bytes typical)

  • timestamp[in] 13-bit timestamp in milliseconds (rolls at 8192)

Returns

  • ESP_OK on success

  • ESP_ERR_INVALID_ARG on wrong parameter

  • ESP_FAIL on other error

esp_err_t esp_ble_midi_send_note_on(uint8_t channel, uint8_t note, uint8_t velocity, uint16_t timestamp)

Send MIDI Note On.

Parameters
  • channel[in] 0-15

  • note[in] 0-127

  • velocity[in] 0-127

  • timestamp[in] 13-bit timestamp in ms

Returns

  • ESP_OK on success

  • ESP_ERR_INVALID_ARG on wrong parameter

  • ESP_FAIL on other error

esp_err_t esp_ble_midi_send_note_off(uint8_t channel, uint8_t note, uint8_t velocity, uint16_t timestamp)

Send MIDI Note Off.

Parameters
  • channel[in] 0-15

  • note[in] 0-127

  • velocity[in] 0-127

  • timestamp[in] 13-bit timestamp in ms

Returns

  • ESP_OK on success

  • ESP_ERR_INVALID_ARG on wrong parameter

  • ESP_FAIL on other error

esp_err_t esp_ble_midi_send_cc(uint8_t channel, uint8_t controller, uint8_t value, uint16_t timestamp)

Send MIDI Control Change.

Parameters
  • channel[in] 0-15

  • controller[in] 0-127

  • value[in] 0-127

  • timestamp[in] 13-bit timestamp in ms

Returns

  • ESP_OK on success

  • ESP_ERR_INVALID_ARG on wrong parameter

  • ESP_FAIL on other error

esp_err_t esp_ble_midi_send_pitch_bend(uint8_t channel, uint16_t value, uint16_t timestamp)

Send MIDI Pitch Bend (14-bit value)

Parameters
  • channel[in] 0-15

  • value[in] 0-16383 (8192 is center)

  • timestamp[in] 13-bit timestamp in ms

Returns

  • ESP_OK on success

  • ESP_ERR_INVALID_ARG on wrong parameter

  • ESP_FAIL on other error

esp_err_t esp_ble_midi_send_multi(const uint8_t *const msgs[], const uint8_t msg_lens[], const uint16_t timestamps[], uint16_t count, size_t *first_unsent_index)

Send multiple MIDI events aggregated into a single BLE-MIDI packet.

Aggregates multiple MIDI messages in one notification to improve throughput. Each message will be prefixed with its own BLE-MIDI timestamp byte.

Notes:

  • The function packs as many messages as fit in an internal buffer (<= ATT MTU).

  • If not all messages fit, this function sends multiple notifications.

  • For SysEx, pass a complete 0xF0…0xF7 sequence. For automatic fragmentation, use esp_ble_midi_send_sysex(), or split by MTU and call this repeatedly.

  • On failure: If first_unsent_index is provided, it will be set to the index of the first message that was not sent. On success, it will be set to count. This allows callers to determine which messages were successfully transmitted before the error occurred.

Parameters
  • msgs[in] Array of pointers to MIDI messages (status + data bytes)

  • msg_lens[in] Array of message lengths (1..n)

  • timestamps[in] Array of 13-bit timestamps for each message (ms resolution)

  • count[in] Number of messages in arrays

  • first_unsent_index[out] Optional output parameter: on failure, set to the index of the first message that was not sent; on success, set to count. Pass NULL if not needed.

Returns

  • ESP_OK on success (all messages sent)

  • ESP_ERR_INVALID_ARG on wrong parameter

  • ESP_FAIL on other error (partial send may have occurred; check first_unsent_index)

esp_err_t esp_ble_midi_send_sysex(const uint8_t *sysex, uint16_t len, uint16_t timestamp)

Send a System Exclusive (SysEx) message with automatic multi-packet fragmentation.

The buffer must begin with 0xF0 and end with 0xF7. The function splits the SysEx into multiple BLE-MIDI Event Packets based on current ATT MTU.

Notes:

  • Uses the current MTU to choose fragment sizes (accounting for ATT and BEP overhead).

  • Real-time messages (0xF8..0xFF) are delivered as separate events on the receiver.

Parameters
  • sysex[in] Pointer to complete SysEx buffer (0xF0 … 0xF7)

  • len[in] Length of the SysEx buffer

  • timestamp[in] 13-bit timestamp in ms to use for all fragments

Returns

  • ESP_OK on success

  • ESP_ERR_INVALID_ARG on wrong parameter

  • ESP_FAIL on other error

void esp_ble_midi_on_bep_received(const uint8_t *data, uint16_t len)

Profile entry for raw BLE-MIDI Event Packet received from GATT.

The service layer should call this to hand off incoming packets.

Parameters
  • data[in] Pointer to BEP payload buffer

  • len[in] Length of BEP payload

uint16_t esp_ble_midi_get_timestamp_ms(void)

Get 13-bit rolling timestamp in milliseconds for BLE-MIDI.

Returns a 1 ms resolution counter modulo 8192 suitable for BLE-MIDI timestamps. Intended for use as the timestamp parameter in send helpers.

Returns

13-bit timestamp in milliseconds (0..8191)

esp_err_t esp_ble_midi_set_notify_enabled(bool enabled)

Set notification enabled state (typically called when CCCD is updated).

This should be called when the client writes to the CCCD (Client Characteristic Configuration Descriptor) to enable/disable notifications. MIDI send functions will only work when notification is enabled.

Note

esp_ble_midi_profile_init() must be called before setting notify state.

Parameters

enabled[in] true if notification is enabled, false otherwise

Returns

  • ESP_OK on success

  • ESP_ERR_INVALID_STATE if esp_ble_midi_profile_init() has not been called

bool esp_ble_midi_is_notify_enabled(void)

Check if notification is currently enabled.

Note

esp_ble_midi_profile_init() must be called before checking notify state.

Returns

true if notification is enabled, false otherwise (also returns false if not initialized)

Macros

BLE_MIDI_SERVICE_UUID128

BLE MIDI Service UUID (128-bit): 03B80E5A-EDE8-4B33-A751-6CE34EC4C700

BLE_MIDI_CHAR_UUID128

BLE MIDI IO Characteristic UUID (128-bit): 7772E5DB-3868-4112-A1A9-F2669D106BF3

BLE_MIDI_MAX_PKT_LEN

Maximum BLE-MIDI packet length (BLE 5.0+)

Used for buffer allocation and size validation in BLE-MIDI packets.

ESP_BLE_MIDI_TIMESTAMP_MAX

13-bit rolling timestamp (1 ms resolution)

Type Definitions

typedef void (*esp_ble_midi_rx_cb_t)(const uint8_t *data, uint16_t len, void *user_ctx)

RX callback for raw BLE-MIDI Event Packet (BEP) payloads.

The buffer contains the BEP payload as defined by the BLE-MIDI specification. For parsed per-message events, use esp_ble_midi_register_event_cb().

Param data

[in] Pointer to BEP payload buffer

Param len

[in] Length of BEP payload

Param user_ctx

[in] User context pointer passed during registration

typedef void (*esp_ble_midi_event_cb_t)(uint16_t timestamp_ms, esp_ble_midi_event_type_t event_type, const uint8_t *msg, uint16_t msg_len)

Parsed event callback for a single decoded MIDI message.

  • msg contains one complete MIDI message (status + data).

  • SysEx is delivered as a complete 0xF0 … 0xF7 sequence (reassembled).

  • Real-time messages (0xF8..0xFF) are delivered as 1-byte events and may interleave.

  • On SysEx overflow (ESP_BLE_MIDI_EVENT_SYSEX_OVERFLOW), msg is NULL and msg_len is 0.

Param timestamp_ms

[in] 13-bit rolling timestamp in milliseconds

Param event_type

[in] Event type (normal message or overflow)

Param msg

[in] Pointer to a single MIDI message (status + data), NULL on overflow

Param msg_len

[in] Message length in bytes, 0 on overflow

Enumerations

enum esp_ble_midi_event_type_t

MIDI event types.

Values:

enumerator ESP_BLE_MIDI_EVENT_NORMAL

Normal MIDI message

enumerator ESP_BLE_MIDI_EVENT_SYSEX_OVERFLOW

SysEx buffer overflow detected