Control-Surface Arduino MIDI Controller Library
Arduino library for creating MIDI controllers and other MIDI devices.
At a glance
- What is it?
- Control Surface is a C++ Arduino library for MIDI controllers, MIDI interfaces, and control surfaces across USB, BLE, and 5-pin DIN links. Its strongest fit is a device with several conventional controls, although wiring and interface support must be checked for the target board.
- Who is it for?
- Adopt Control Surface when an Arduino-based controller needs reusable MIDI mappings, interface routing, or support for multiplexed inputs. Do not choose it solely for a minimal fixed sketch without first checking board compatibility, transport support, and the GPL-3.0 implications.
- Can I use it commercially?
- Yes, with conditions. GPL-3.0 is a copyleft licence: if you distribute software that includes it, you must release that software's source code under the same licence. Running it internally without distributing it does not trigger that obligation.
- Is it still maintained?
- Yes. The repository last received commits 23 days ago.
- What is it written in?
- Mainly C++, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.
Editorial analysis
Control Surface targets physical MIDI devices
Control Surface addresses a recurring problem in embedded music hardware: turning switches, potentiometers, encoders, LEDs, and displays into a coherent MIDI device without making every sketch responsible for transport handling and control-state bookkeeping. It is intended for Arduino developers building controllers and larger control surfaces. The README also frames the MIDI layer as usable on its own for projects that only need to send or receive messages.
The library is not limited to USB. Its MIDI abstraction supports serial 5-pin DIN MIDI, MIDI over USB, and MIDI over BLE. That matters because the same controller program can be evaluated against different physical interfaces, although actual board compatibility still has to be checked in the documentation's support table. The supported-device topic also includes Arduino boards, ESP32, Teensy, and Raspberry Pi Pico, but the repository excerpt does not claim identical feature support across all of them.
The clearest audience is someone building a purpose-built controller with more than one input or output. A developer who needs direct access to serial or USB APIs may find the abstraction unnecessary. Control Surface earns its place when mappings, polling, incoming-message reactions, or interface routing would otherwise be repeated across the project.
MIDI interfaces, controls, and polling form one architecture
A Control Surface program declares hardware objects and then gives the library a regular update cycle. In the simplest controller sketch, a `USBMIDI_Interface` selects the transport, while `CCPotentiometer` associates an analog pin with a MIDI Control Change destination. `Control_Surface.begin()` initializes the declared objects, and `Control_Surface.loop()` updates them. This arrangement separates physical controls and logical MIDI behavior from the main sketch structure.
Incoming messages follow a related path. `NoteLED` binds an LED pin to `MIDI_Notes::C[4]`, so the object can react when the declared note arrives. The library therefore serves both output-oriented controls that generate messages and input-oriented controls that react to received messages. The README also lists motorized faders as an example of a more advanced device that combines physical movement, MIDI output, and feedback handling.
The transport layer can be composed directly when a controller also functions as an adapter. `BidirectionalMIDI_Pipe` connects two interfaces, allowing incoming USB traffic to reach BLE and incoming BLE traffic to reach USB. In that design, the sketch uses `MIDI_Interface::beginAll()` and `MIDI_Interface::updateAll()` rather than the higher-level `Control_Surface` calls. The important distinction is that a controller control is a mapped hardware object, while a pipe is an explicit routing connection between transports.
Multiplexed controls reduce GPIO pressure
Large controllers often run out of directly connected GPIO pins. Control Surface addresses this with abstractions for analog multiplexers, shift registers, and port expanders. Its documentation says these devices can be treated as ordinary GPIO pins, which lets the same control classes represent a physical pin that is accessed through added hardware.
The README's eight-potentiometer example uses `CD74HC4051`. Three address pins select one of eight analog inputs, and the objects obtain their input pins through `mux.pin(0)` through `mux.pin(7)`. Each `CCPotentiometer` then has its own MIDI destination, from channel volume on channel 1 through channel volume on channel 8. This is more useful than a simple example because it demonstrates how wiring density and application-level control declarations remain separate.
That convenience comes with a hardware constraint. The sketch still has to match the mux's address wiring, analog input, and physical channel connections. The README does not provide timing values, wiring diagrams, or board-specific electrical guidance in the excerpt, so those details must come from the Getting Started documentation and the chosen hardware. Software abstraction does not remove the need to validate the circuit.
Arduino installation starts with the documented guide
The README does not include an installation command, package-manager invocation, or Arduino IDE menu sequence. It directs users to the Installation Instructions page in the generated documentation, which is the authoritative first step for adding Control Surface to an Arduino environment. No installation command can be confirmed from the repository excerpt alone.
After installation, a first useful test is a single analog controller sending MIDI over USB. The following sketch is copied from the README. In the Arduino IDE, the sketch should include the library header, instantiate `USBMIDI_Interface`, bind `A0` to `MIDI_CC::General_Purpose_Controller_1`, and call the two Control Surface lifecycle functions.
A first USB MIDI controller sketch
The smallest complete example supplied by the README requires five code lines after the include. The pin and MIDI mapping are visible in the object declaration, while `setup()` and `loop()` manage the library lifecycle.
#include <Control_Surface.h>
USBMIDI_Interface midi;
CCPotentiometer pot { A0, MIDI_CC::General_Purpose_Controller_1 };
void setup() { Control_Surface.begin(); }
void loop() { Control_Surface.loop(); }On the hardware side, this example assumes that a potentiometer is wired to `A0` and that the selected Arduino board supports the USB MIDI interface used by `USBMIDI_Interface`. The repository excerpt does not state the supported USB stack or describe board-specific USB configuration, so those points belong in the documented board table. The expected application-level result is a Control Change message associated with `General_Purpose_Controller_1` when the potentiometer changes.
For an incoming-message check, the README gives a second short sketch using `LED_BUILTIN` and middle C. It is useful because it exercises the opposite direction, but it is not a substitute for wiring and board verification.
USB to BLE routing suits an adapter design
Control Surface can be used without physical control declarations when the real task is transport bridging. The README's adapter example creates `BluetoothMIDI_Interface` and `USBMIDI_Interface` objects, connects both to one `BidirectionalMIDI_Pipe`, and initializes all registered interfaces.
#include <Control_Surface.h>
BluetoothMIDI_Interface midi_ble;
USBMIDI_Interface midi_usb;
BidirectionalMIDI_Pipe pipes;
void setup() {
midi_usb | pipes | midi_ble;
MIDI_Interface::beginAll();
}
void loop() {
MIDI_Interface::updateAll();
}The `|` expressions establish routing in both directions. Initialization occurs once, while `updateAll()` continuously polls the registered interfaces. This example exposes an important distinction from the controller sketches: it does not instantiate buttons, LEDs, or analog controls. It is a MIDI bridge whose data flow is USB input to BLE output and BLE input to USB output.
The excerpt does not document BLE pairing, USB host or device roles, buffering limits, latency, or recovery after disconnect. It also does not promise that every listed board can operate the two transports simultaneously. Those are concrete checks for anyone evaluating this adapter pattern.
GPL-3.0 and recent releases affect adoption
The repository is licensed under GPL-3.0. That identifier matters when deciding whether the library fits a planned firmware product or distribution method. The repository provides a `LICENSE` file, but this review does not interpret legal obligations. Teams using Control Surface in distributed firmware or derivative works should review that license against their own build and distribution model.
Maintenance is recent. The repository is not archived, its last push was on 2026-09-30, and release 2.1.3 was published on the same date. Earlier listed releases include 2.1.2 on 2026-06-06 and 2.1.1 on 2026-05-03. These facts indicate recent repository activity, but they do not establish compatibility with a particular board or guarantee the response time for issue reports.
Upgrade cost is partly visible in the project structure and documentation. The repository contains examples for MIDI output, MIDI input, interfaces, banks, hardware, displays, Arduino helpers, boards, and library extension. It also includes test, mock, tools, scripts, and CMake-related entries. The README points to a change log and updating section. This suggests a substantial example and documentation surface, but the excerpt does not give migration instructions from 2.1.2 to 2.1.3. The documented change log is therefore the first place to check before changing a production sketch.
When direct Arduino MIDI code is the better choice
A direct Arduino implementation is the practical alternative for a small device with one fixed transport and a few mappings. Such a project can read the relevant pins and send MIDI through the board-specific serial or USB API without adopting Control Surface's objects, interface registry, or polling model. That approach avoids learning the library's abstraction, but it also leaves the developer responsible for message formatting, transport configuration, and maintenance of every device-specific behavior.
Control Surface becomes more defensible when the project includes several transport options, more controls than direct GPIO permits, incoming LED or display behavior, or explicit interface routing. Its abstractions make those cases easier to express, and the mux example shows that added hardware can remain part of ordinary control declarations. The trade-off is dependency breadth: the project covers considerably more than a single MIDI feature.
It is the wrong tool when the only requirement is a fixed, board-specific serial MIDI sender, or when required hardware falls outside the documented compatibility tables. Control Surface also does not fill gaps left by missing board setup, BLE pairing design, electrical wiring, or production update planning. Those remain separate engineering tasks.
Editorial conclusion
Adopt Control Surface when an Arduino-based controller needs reusable MIDI mappings, interface routing, or support for multiplexed inputs. Do not choose it solely for a minimal fixed sketch without first checking board compatibility, transport support, and the GPL-3.0 implications. The first verification should be the library's documented installation path for the selected Arduino board.
Frequently asked questions
How do I install Control Surface for an Arduino board?
The README directs users to the Installation Instructions page in the generated documentation and does not provide a package-manager or Arduino IDE command. It also points to the Getting Started page for the initial setup path.
Which MIDI transports does Control Surface support?
The documented MIDI abstraction supports serial 5-pin DIN MIDI, MIDI over USB, and MIDI over BLE. Compatibility depends on the selected Arduino board, so the linked support table is the relevant reference.
Can Control Surface send MIDI from a multiplexed analog input?
Yes. The README's eight-potentiometer example connects a `CD74HC4051` to `A0` and address pins `{3, 4, 5}`, then uses `mux.pin(0)` through `mux.pin(7)` as inputs to `CCPotentiometer` objects.
Can Control Surface route MIDI between USB and BLE?
Yes. A `BidirectionalMIDI_Pipe` can connect `USBMIDI_Interface` and `BluetoothMIDI_Interface`, allowing the README's adapter example to route incoming traffic in both directions.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/tttapa-control-surface)Was this useful?