Instructions on how to install the library and its dependencies can be found on the page Installation.
Include the library so that you have access to all the classes and functions it provides.
If you want to send out or receive MIDI messages, you have to define at least one MIDI interface. If you don't do that, you'll get an error when calling Control_Surface.begin()
.
There are many different MIDI interfaces to choose from:
USBMIDI_Interface
: On boards that support MIDI over USB natively, this will do exactly what you'd expect. You just have to plug it into your computer, and it shows up as a MIDI device. On Arduinos without native USB capabilities (e.g. UNO or MEGA), you have to use custom firmware for the ATmega16U2.HardwareSerialMIDI_Interface
: This interface will send and receive MIDI messages over a hardware UART. You can use it for MIDI over a 5-pin DIN connector, for example.HairlessMIDI_Interface
: If you have an Arduino without native USB support, an easy way to get the MIDI messages from your Arduino to your computer is the Hairless MIDI<->Serial Bridge. In that case, you can use this MIDI interface. The default baud rate is 115200 symbols per second.BluetoothMIDI_Interface
: If you have an ESP32, you can send and receive MIDI messages over Bluetooth Low Energy. This interface is still very much experimental, but it's pretty cool. If you know more about the MIDI BLE protocol, feel free to suggest some improvements.USBDebugMIDI_Interface
: Debugging MIDI Controllers can be very cumbersome. There are MIDI monitors available, but you have to reconnect every time you upload a new sketch, and sending MIDI to the Arduino is not always easy. 90 3C 7F
to turn on middle C).A complete overview of the available MIDI interfaces can be found in the MIDI Interfaces module.
For now, we'll use the USBMIDI_Interface
because it's probably the one you'll use in your final program. Do keep in mind that not all boards support MIDI over USB natively, for more details, see the MIDI over USB page.
You can give the interface any name you want. I'll be very original and choose midi
. It doesn't matter, and you don't need to use it afterwards, just defining the interface is enough, the Control Surface library will automatically detect and use it.
Note: some interfaces require additional parameters, for example, the
USBDebugMIDI_Interface
needs to know the baud rate.
In that case, you can instantiate it as follows:
USBDebugMIDI_Interface midi {115200};
For a more detailed overview of MIDI interfaces and using them to send and receive MIDI message, have a look at the MIDI Tutorial.
If your MIDI Controller requires many in- or outputs, you'll run out of IO pins really quickly. A solution is to use multiplexers or shift registers.
The Control Surface Library supports both of these options, and makes it easy to support other types of IO expanders in the future.
An overview of Extended Input/Output elements can be found in the Extended Input/Output module.
In this example, we'll use an 8-channel 74HC4051 analog multiplexer. This allows us to read eight analog inputs using just one analog pin on the Arduino, at the cost of only three digital output pins.
Each of the eight analog inputs of the multiplexer can be connected to the wiper of one potentiometer.
We'll connect the three address lines of the multiplexer (S0
, S1
and S2
) to digital pins 3
, 4
and 5
. The output of the multiplexer goes to analog pin A0
. Connect the enable pin (Ē
) to ground.
Now, we can specify the objects that read the input of the potentiometers and send out MIDI events accordingly.
Again, I'll refer to the overview of MIDI Output Elements.
Let's define a single potentiometer on pin A1
that sends out MIDI Control Change events.
In the documentation, you'll find that the first argument for the CCPotentiometer
constructor is the analog pin number, and the second is the MIDI address.
The MIDI address is a structure that consists of an address number, the MIDI channel, and the cable number.
In this case, the address number is the controller number, which is a number from 0 to 119. The MIDI channel is a channel from Channel_1
until Channel_16
. We'll ignore the cable number for now, if you don't specifically set it, it'll just use the default cable.
You can find more information about MIDI addresses in the MIDI Tutorial: MIDI addresses.
For the MIDI controller numbers, you can use the predefined constants, or you can just use a number.
In our case, we don't want a single potentiometer, we want eight. It's much easier to define them in an array.
Also note how we declare that the potentiometers are connected to the the pins of the multiplexer we defined in the previous step.
To go from the initialization of a single potentiometer to the initialization of an array of potentiometers, we just create a comma-separated list of initializers for single elements, and then we add another pair of curly braces around it.
Compare this to the initialization of an array of integers: to initialize a single integer, you use int i = 42;
and to initialize an array of integers, you use int ii[] = {42, 43, ...};
. Analogously, to initialize a single potentiometer object, you use CCPotentiometer pot = {pin, address};
and to initialize an array of potentiometers, you use CCPotentiometer pots[] = { {pin, address}, {pin, address}, ... };
. It is recommended to leave out the equal sign (=
), because this will result in more meaningful error messages if you get any of the arguments wrong: CCPotentiometer pots[] { {pin, address}, {pin, address}, ... };
.
Note: The first pin is of the multiplexer is
pin(0)
, notpin(1)
.
Note: Some other MIDI Control Elements might not need an address number, or they might not need a channel. In that case, you just leave out the optional parts that you don't need.
For example, aPBPotentiometer
doesn't need an address number, just a channel, so you can instantiate it as follows:PBPotentiometer potentiometer { A1, Channel_9 };A class of MIDIOutputElements that read the analog input from a potentiometer or fader,...Definition PBPotentiometer.hpp:22
There's a lot to be done in the setup
: The MIDI interface has to be initialized, all pins must be set to the correct mode, etc.
Luckily, the Control Surface Library handles almost all of it for you.
Note: If you forget to define a MIDI interface,
Control_Surface.begin()
will raise an error and the on-board LED will start blinking.
Other errors will also be indicated this way.
If you don't know why this happens, you should enable debug information in theControl_Surface/src/AH/Settings/Settings.h
file, and inspect the output in the Serial Monitor.
Someday, I will add a "Troubleshooting" page. For now, if you have any problems, just open an issue on GitHub to remind me.
Now that everything is set up, you can just update the Control Surface forever. It will refresh all inputs and send the appropriate MIDI messages if any of the inputs change.
Note: Using blocking code like delays will interfere with the Control Surface, so try to use Blink Without Delay techniques instead.
That's it!
Now you can just upload the sketch to your Arduino, open up your favorite audio software, map the potentiometers, and start playing!
Unlike the MIDI Controller library, the Control Surface library does support MIDI input.
This example shows how to use the NoteLED
class to turn on and off LEDs when MIDI Note On/Off messages are received.
The example shows the use of a shift register to drive the LEDs, but you can of course use any pins you want.
Include the library so that you have access to all the classes and functions.
See First Output: Instantiate a MIDI Interface.
See First Output: Add Extended Input/Output elements.
In this example, we'll use a 74HC595 8-bit serial in/parallel out shift register. This allows us to drive eight LEDs using just the SPI bus and a single digital pin. You can daisy chain as many shift registers as you want, without requiring any more pins.
Each of the eight outputs of the shift register can be connected to the anode of an LED. Connect the cathodes to ground through a current-limiting resistor.
Connect the clock input (SH_CP or SRCLK) of the shift register to the Arduino's SCK pin, the serial data input (DS or SER) of the shift register to the Arduino's MOSI pin, and the latch pin (ST_CP or RCLK) of the shift register to digital pin 10 of the Arduino. Connect the Output Enable pin (OE) of the shift register to ground, and the Master Reset (MR) pin of the shift register to Vcc to enable it.
The 8
between angle brackets (<>
) is the number of bits of the shift register. If you daisy chain two 8-bit shift registers together, you would use 16
instead of 8
, for example.
The bit order determines which pin of the shift register is the first pin in the program. MSBFIRST
means "most significant bit first". You can also use LSBFIRST
(least significant bit first).
Now, we can specify the objects that listen for MIDI input, and update the status of the LEDs accordingly.
I'll refer to the overview of MIDI Input Elements.
Let's define a single LED on pin 13
that listens for MIDI Note events for a middle C on channel 1.
In the documentation, you'll find that the first argument for the NoteLED
constructor is the number of the pin with the LED connected, and the second is the MIDI address.
The MIDI address is a structure that consists of an address number, the MIDI channel, and the cable number.
In this case, the address number is the note number, which is a number from 0 to 127. The MIDI channel is a channel from Channel_1
until Channel_16
. We'll ignore the cable number for now, if you don't specifically set it, it'll just use the default cable.
For the MIDI note numbers, you can use the note constants and the note
function in the MIDI_Notes namespace, or you can just use a number.
In our case, we don't want a single LED, we want eight. It's much easier to define them in an array.
Also note how we state that it should use the pins of the shift register we defined in the previous step. We omit the channel here, so it'll just use the default channel, Channel_1
.
Note: The first pin is
pin(0)
, notpin(1)
.
See First Output: Initialize the Control Surface.
See First Output: Continuously Update the Control Surface.
That's it!
Now you can just upload the sketch to your Arduino, open up your favorite audio software, redirect the MIDI output to the Arduino, and start playing!
If something goes wrong at run time, Control Surface will start blinking the on-board LED, with a pattern of two short flashes, followed by a longer pause, and a period of one second. You can enable debug output by defining the DEBUG_OUT
macro in ~Arduino/libraries/Control-Surface/src/AH/Settings/Settings.hpp
as Serial
, for example. This requires quite a bit of extra memory, so that might not be feasible on all boards, or you might have to simplify your sketch a bit.
If your sketch produces an error as soon as you call Control_Surface.begin()
, you might not be able to open the Serial Monitor quickly enough to catch the error message, in that case, you can wait for the Serial connection to be opened before initializing Control Surface. For example:
If you enable debug output, you'll get a helpful error message as soon as you open the Serial Monitor:
The error code at the end can be used to easily locate the source of the error in the library source code, and in this case the message also includes the name of the function and the line number where the error occurred.
Note: Since the built-in LED will be configured as an output when an error occurs, you should not use it as an input (e.g. to connect a switch), because this will cause a short circuit that might destroy the pin.