# Receiving MIDI Messages > Original: [Receiving MIDI messages](https://tttapa.github.io/Control-Surface/Doxygen/d3/df7/midi-tutorial.html) ## With MIDI_Callbacks Register a callback object on the interface to handle incoming messages: ```cpp pico #include "pico/stdlib.h" #include "pico/cyw43_arch.h" #include using namespace cs; BluetoothMIDI_Interface midi; struct MyCallbacks : MIDI_Callbacks { void onChannelMessage(MIDI_Interface &, ChannelMessage msg) override { auto type = msg.getMessageType(); Channel ch = msg.getChannel(); uint8_t d1 = msg.getData1(); uint8_t d2 = msg.getData2(); (void)type; (void)ch; (void)d1; (void)d2; } } cb; int main() { stdio_init_all(); if (cyw43_arch_init()) return 1; midi.begin(); midi.setCallbacks(cb); while (true) { midi.update(); sleep_ms(1); } } ``` `midi.update()` reads incoming data and dispatches to callbacks. ## With FineGrainedMIDI_Callbacks For per-message-type handlers, use the CRTP pattern: ```cpp pico struct Handlers : FineGrainedMIDI_Callbacks { void onNoteOn(Channel ch, uint8_t note, uint8_t velocity, Cable cable) { (void)ch; (void)note; (void)velocity; (void)cable; } void onControlChange(Channel ch, uint8_t cc, uint8_t value, Cable cable) { (void)ch; (void)cc; (void)value; (void)cable; } }; Handlers handlers; // midi.setCallbacks(handlers); ``` Available overrides: `onNoteOn`, `onNoteOff`, `onKeyPressure`, `onControlChange`, `onProgramChange`, `onChannelPressure`, `onPitchBend`, `onSystemExclusive`, `onTimeCodeQuarterFrame`, `onSongPosition`, `onSongSelect`, `onTuneRequest`, `onClock`, `onStart`, `onContinue`, `onStop`, `onActiveSensing`, `onSystemReset`. ## ChannelMessage accessors | Method | Returns | |--------|---------| | `getMessageType()` | `MIDIMessageType` enum | | `getChannel()` | `Channel` | | `getData1()` | First data byte (note/CC number) | | `getData2()` | Second data byte (velocity/value) | | `getData14bit()` | 14-bit value (Pitch Bend) | | `getCable()` | `Cable` | ## With the element system MIDI input elements (`CCValue`, `NoteLED`, etc.) handle message matching and state management automatically — no callbacks needed. They update on each `Control_Surface.loop()` call.