Control Surface main
MIDI Control Surface library for Arduino
Loading...
Searching...
No Matches
MIDI-Monitor-OLED.ino

MIDI-Monitor-OLED

This example shows all incoming and outgoing MIDI messages on an SSD1306 OLED display. This can be handy for debugging purposes when you don't have access to the Serial Monitor.

Boards: 🛈
AVR, Nano Every, Due, Nano 33 IoT, Nano 33 BLE, UNO R4, Teensy 3.x, ESP32, ESP8266

Connections

Behavior

Explanation

The code below defines the OLEDDebugMIDI_Output class, which implements the MIDI_Sink and MIDI_Sender interfaces. This means that it can accept MIDI messages coming from MIDI_Pipes, and it provides all of the usual functions for sending MIDI messages.

The MIDI over USB interface (midi) will be used as the primary MIDI interface (as in the other Control Surface examples).
Two instances of our OLEDDebugMIDI_Output class are created: one for printing messages that are received by the primary MIDI interface, and one for printing messages that are sent by Control Surface.
The MIDI interfaces and Control Surface itself are connected appropriately using some pipes.

See also
MIDI_Pipes-Routing.ino
Debug-MIDI-Interface.ino
MIDI Tutorial: Routing MIDI messages
MIDI-Monitor.ino

Written by PieterP, 2024-01-05
https://github.com/tttapa/Control-Surface

#include <Adafruit_SSD1306.h>
// Specify the options and pins for the display
constexpr uint8_t SCREEN_WIDTH = 128;
constexpr uint8_t SCREEN_HEIGHT = 64;
constexpr int8_t OLED_reset = 8; // Reset pin of the display
constexpr int8_t OLED_DC = 9; // Data/Command pin of the display
constexpr int8_t OLED_CS = 10; // Chip Select pin of the display
};
void init_display() {
if (!disp.begin())
FATAL_ERROR(F("SSD1306 allocation failed."), 0x1306);
disp.setTextColor(SSD1306_WHITE);
disp.setTextSize(1);
disp.cp437();
disp.clearDisplay();
disp.display();
disp << uppercase;
}
// Shifts the entire display up by the given number of lines and sets the
// cursor at the beginning of the first of the newly created empty lines.
auto buf = disp.getBuffer();
auto end = buf + SCREEN_WIDTH * SCREEN_HEIGHT / 8;
std::move(buf + lines * SCREEN_WIDTH, end, buf);
std::fill(end - lines * SCREEN_WIDTH, end, 0);
disp.setCursor(0, SCREEN_HEIGHT - 8 * lines);
}
constexpr const char *abbrev_midi_status_names[] {
"Note Off", "Note On", "Key Pres", "Ctrl Ch",
"Prog Ch", "Ch Pres", "Pitch Bend"};
class OLEDDebugMIDI_Output : public MIDI_Sender<OLEDDebugMIDI_Output>,
public TrueMIDI_Sink {
public:
OLEDDebugMIDI_Output(const char *prefix = nullptr) : prefix(prefix) {}
// Implement the required MIDI_Sink interface
void sinkMIDIfromPipe(ChannelMessage m) override { send(m); }
void sinkMIDIfromPipe(SysExMessage m) override { send(m); }
void sinkMIDIfromPipe(SysCommonMessage m) override { send(m); }
void sinkMIDIfromPipe(RealTimeMessage m) override { send(m); }
private:
// Implement the required MIDI_Sender interface
void sendChannelMessageImpl(ChannelMessage msg) {
uint8_t messageType = (msg.header >> 4) - 8;
if (messageType >= 7)
return;
if (prefix != nullptr)
disp << prefix << ' ';
disp << msg.getCable().getOneBased() << ':';
disp << msg.getChannel().getOneBased() << ' ';
if (msg.getMessageType() == msg.PitchBend)
disp << ' ' << hex << msg.getData14bit() << 'h' << dec;
else if (msg.hasTwoDataBytes())
disp << ' ' << hex << msg.getData1() << 'h' << ' ' << msg.getData2()
<< 'h' << dec;
else
disp << abbrev_midi_status_names[messageType] << ' ' << hex
<< msg.getData1() << 'h' << dec;
disp.display();
}
void sendSysCommonImpl(SysCommonMessage msg) {
if (prefix != nullptr)
disp << prefix << ' ';
disp << msg.getCable().getOneBased() << ' ';
disp << msg.getMessageType() << hex;
if (msg.getNumberOfDataBytes() >= 1)
disp << ' ' << msg.getData1() << 'h';
if (msg.getNumberOfDataBytes() >= 2)
disp << ' ' << msg.getData2() << 'h';
disp << dec;
disp.display();
}
void sendSysExImpl(SysExMessage msg) {
if (prefix != nullptr)
disp << prefix << ' ';
disp << msg.getCable().getOneBased() << ' ';
disp << F("Sys Ex [") << msg.length << (msg.isLastChunk() ? "]" : "+]");
uint16_t i = 0;
while (i < msg.length) {
uint16_t rem = msg.length - i;
uint16_t len = std::min(bytes_per_line, rem);
disp.println();
disp << ' ' << AH::HexDump(msg.data + i, len);
}
disp.display();
}
void sendRealTimeImpl(RealTimeMessage msg) {
if (prefix != nullptr)
disp << prefix << ' ';
disp << msg.getCable().getOneBased() << ' ';
disp << msg.getMessageType();
disp.display();
}
void sendNowImpl() {}
// Prefix is printed in front of every message
const char *prefix;
// Print the USB cable number
bool print_cable = false;
};
// Main MIDI interface used by Control Surface
// Prints messages being received to the display
OLEDDebugMIDI_Output midi_disp_in {"\x1a"}; // prefix="→"
// Prints messages being sent to the display
OLEDDebugMIDI_Output midi_disp_out {"\x1b"}; // prefix="←"
// Two pipes for MIDI routing
// MIDI element to send messages for testing
NoteButton button {5, MIDI_Notes::C[4]};
void setup() {
midi.setAsDefault();
// Route MIDI messages to the display
midi >> pipes >> midi_disp_in;
}
void loop() {
}
The main header file that includes all Control-Surface header files.
Control_Surface_ & Control_Surface
A predefined instance of the Control Surface to use in the Arduino sketches.
void begin()
Initialize the Control_Surface.
void loop()
Update all MIDI elements, send MIDI events and read MIDI input.
void setAsDefault()
Set this MIDI interface as the default interface.
Statically polymorphic template for classes that send MIDI messages.
void send(ChannelMessage message)
Send a MIDI Channel Voice message.
Receives MIDI messages from a MIDI pipe.
virtual void sinkMIDIfromPipe(ChannelMessage)=0
Accept an incoming MIDI Channel message.
A class of MIDIOutputElements that read the input of a momentary push button or switch,...
A class for MIDI interfaces sending MIDI messages over a USB MIDI connection.
Array< T, N > copyAs(const Array< U, N > &src)
Copy an Array to an Array of a different type.
#define FATAL_ERROR(msg, errc)
Print the error message and error code, and stop the execution.
Definition Error.hpp:57
Print & hex(Print &printer)
Print & uppercase(Print &printer)
Print & dec(Print &printer)
static constexpr Frequency SPI_MAX_SPEED
constexpr Note F
F (Fa)
Definition Notes.hpp:61
constexpr Note C
C (Do)
Definition Notes.hpp:56
Class that produces multiple MIDI_Pipes.