In 2014 I started with building my first DIY MIDI controller LambdaControl. I decided to release all material for this MIDI controller under open source licenses, such that other musicians can make us of my work for their own projects. Hence, this page should work as a documentation of the project. In which I try to explain my design decisions and the different parts of the controller.
First, I will explain my live performance concept and the design of LambdaControl. After that I start with building the front panel. The next chapter will be about the electronics of the controller and why I based it on the MIDIbox project. Following, I explain how I build the RGB button matrix and integrated it into Ableton Live by writing a custom MIDI remote script. Finally, I show you how I created the case of LambdaControl with my DIY 3D printer, cover the final assembly, and discuss the final result.
You can find all the material that I created to build LambdaControl on github. I distributed it into four repositories:
- Repository for the hardware related files like the faceplate or the 3D printable case
- MIOS32 firmware for the MIDIbox core, which is the main brain of the controller
- Matrix MCU Firmware for the separate microcontroller that drives the RGB matrix
- MIDI Remote Script for Ableton Live that integrates LambdaControl into the DAW
Ableton Push is my tool of choice for improvising during my upcoming live performance, since its interface is very well thought out for exactly this task. However, improvising during the live performance is only possible when controlling the musical foundation (my pre-produced songs) happens without hesitation. Hence, the main goal of this project is to build a controller, which allows me to control the different parts of the songs played during the live performance.
Before I started producing music, I learned to DJ first with Vinyl then CD and in the end Traktor. Maybe this is the reason why I prefer to build my live performance setup also based on the idea of two decks, such that I am able to control two different songs at the same time. Therefore, I need to build two LambdaControl MIDI controllers to have the ultimate separation between the two songs.
I also really like the idea of treating the MIDI controllers more like an instrument. Hence, assigning different functions to the same button is not an option, because it makes it difficult to play the controllers without to much thinking. So each controller needs to have a lot of inputs, such that there is no need to assign several functions to the some button.
Finally, this means that my live performance setup will consists of Ableton Push for improvising and two LambdaControl MIDI controllers for controlling two songs at the same time.
The basic shape of the controller is directly defined by the channel based concept of Ableton Live. Every audio track has its own channel with a volume control, a clip launcher to switch between the different parts of the audio track and knobs for different audio effects.
Every song played in the live performance will be divided into nine tracks/channels like kick, bassline, percussions, main lead, pads and effects. The finally grouping I will figure out when the first version of the live performance setup is finished.
Based on this properties I designed the following draft.
The controller consists of ten channels where the last one is the master channel to control the complete song. Every channel has a line fader to control the volume, six buttons to launch and stop clips, four rotary potentiometers to control the audio effects, and an encoder with button functionality which allows for example to control a looping mechanism.
After choosing the actual components for the faders, knobs and buttons, it was time to design the front panel. Therefore, I first draw a sketch in Inkscape. After the components have arrived I could measure them and use their actual dimensions for the drawing. In the following sketch you can see the different components with their space requirement behind the front panel (grey parts). Taking this additional space behind the front panel into account is very important, since only then everything will fit in the end. I also created a small paper prototype to check that each component can be easily accessed.
As material for the front panel I decided to use grey acrylic glass with 3mm thickness. Luckily, I was able to use the laser cutter at the Fab Lab Aachen, which allowed me to produce the front panel with a high precision. Moreover, I could directly use my Inkscape drawing as tool path for the laser cutter. The following video shows the laser cutting process at the Fab Lab.
Having access to an industrial grade laser cutter was really great, since the produced result is far better than anything I could have created by hand. Here you can see the final result.
Front Panel Assembly
After creating the front panel, I assembled it by inserting the components at the desired positions. Actually the metal shaft of the ordered potentiometers were a little bit shorter than excepted, such that I needed to use a file to have enough space to secure them onto the faceplate by a nut.
Finally, I planed the cable management for the components. I decided to soldered the connections directly to the components, since I thought that creating a dedicated PCB would have been an overkill. Now, after having done all the wiring I maybe would decide differentially. However, I saved a lot of connections by connecting the source and ground lanes by a large bus wire instead of using separate connections for the different components.
Now let us talk about the actual electronics of LambdaControl. First we will take a look at the MIDIbox project. Secondly, I show how I connected the basic inputs like linear potentiometers, rotary potentiometers, and encoders by using components from the MIDIbox project. The last parts focuses on my custom developed RGB button matrix and its different parts.
The MIDIbox project is an open source project started by Thorsten Klose that allows you to easily create MIDI interfaces. The project provides the necessary hardware, operating system and software for this task. The hardware part consists of a MIDIbox core and several extensions modules that can be connected to this board.
The current MIDIbox core is build around a development board that uses a 32 bit ARM processor (STM32F407VG) clocked at 168 MHz. The MIDIbox PCB extends this development board with useful I/O. The operating system MIOS32 is based on FreeRTOS and you can either chose to use on of their provided firmwares like MIDIbox NG to program your controller by specifications inside a configuration file or you can freely program it in C or C++ by creating custom firmwares for the MIOS32 OS. Take a look at the documentation on their website for a complete overview and nice programming examples.
I decided to use the MIDIbox project as base for my controller, since it provides a complete solution, but still can be extended by custom code and hardware. Hence, I save a lot of time utilizing from it while loosing no flexibility. Moreover, there is exists a really nice community on their forum that can help you when you are facing a challenging problem.
The analog inputs of LambdaControl are the linear and rotary potentiometers. LambdaControl consists of 60 rotary pots and ten linear ones, such that just using the internal A/D converter of the STM32F4-DISCOVERY board would require to implement a multiplexing to connect so much inputs. Luckily, the MIDIbox project provides a complete solution for this the AINSER64 extension module. The AINSER board contains a high quality A/D converter (it has a lower noise floor than the internal one) and eight multiplexers, such that up to 64 instead of one analog input can be converted. Moreover, the MIOS32 operating system directly implements the multiplexing for the analog inputs, such that you can just receive the position changes inside the firmware without additional coding.
Encoders are endless knobs that send out incremental updates for each step instead of having an absolute analog resistance value. These incremental outputs of an encoder are digital, such that we can make use of the DIN modules to scan a lot of encoders with a small amount of input pins. The DINx4 module basically consists of four 8-bit shift registers, such that it extends the amount of available input pins by 32. You can even cascade them to scan even more digital inputs. Moreover, MIOS32 directly implements handlers that listen for changes to the different digital inputs like encoders, such that we can easily send out MIDI messages for them.
Additionally, I decided to use encoders with push button functionality. Hence, you can also use them as normal buttons by simply pressing them down. Therefore, the encoder simply contains two more pins for the button that you can also scan by the help of the DIN modules.
RGB Button Matrix
A big part of the project was the design, build, and implementation of the RGB button matrix. It is a collection of 60 buttons arranged in a matrix with ten columns and six rows. I decided to go with silicone rubber buttons that establish a button connection by an attached conducting material and a PCB with matching contact pads. Each of the silicon based button is hollow, such that it can be equipped with a RGB led.
Luckily, Sparkfun produces nice silicon pads designed exactly for this purpose. Hence, I based my design around these pads.
In the following I will describes the importance of multiplexing and show the schematic and PCB that I have created. Also, I will describe the actual hard- and software which drives the leds and scans the buttons.
A RGB led consists of a red, green, and blue led which either share the same anode or cathode. Hence, an RGB led has four pins. Connecting all 60 RGB LEDs of our matrix directly to the MCU is not a good idea, since we would require 180 output pins and around 1.2 Ampere (in my case 20mA per RGB LED) when all LEDs are on. Therefore, connecting the LEDs in a LED matrix configuration is a smarter solution. By that we only need 28 output pins and use at max 0.12 Ampere when we drive just one column of the 6x10 matrix at the same time.
However, driving the LEDs with a matrix configuration is a little bit more complicated. Because, when we light up just on column at the time, we need to iterate over the different matrix columns quickly to create the illusion that all LEDs are on and not just one column.
However, 28 pins are not free on our microcontroller s.t. we still need to use DIN and DOUT modules to connect the different parts of the matrix to the core. Hence, we need to first feed the data into the shift registers and then trigger the latch before the data is set at the matrix column and rows. This deserialization is generally not a problem, but consumes additional time that we need to take into account.
Not only the RGB leds are connected in a matrix configuration, also the 60 input buttons are connected by utilizing a matrix. Additional, information about LED and button matrices can be easily found on the Internet.
Sparkfun also offers a small 4x4 PCB for the silicon pads, but I quickly decided to create my own, since I used an unusual matrix dimension (6x10 instead of the typical 8x8). Moreover, I thought that I could create a nicer layout for my specific purpose and components. Also using one big PCB instead of several small ones results in a overall more stable construction.
Generally, the PCB is created by using a standard matrix layout and consists of contact pads for the buttons, RGB leds, diodes, a column selection, and a row selections. The diodes are used to avoid masking errors with the buttons of the matrix, such that no button press is hidden by another one.
I designed the PCB in Eagle and could really benefit from the open approach of Sparkfun, since they license all their parts under an open source license. Hence, I could reuse the contact pads and measurements of their PCB. Actually, my PCB consists of two separate matrices one for the button input and one for the RGB buttons. However, I placed the column selection pins for the two matrices on one header, such that by soldering the pins together it is possible to drive the PCB with just one shared column selection. In this case the diode orientation and the chosen RGB led type (common anode or cathode) need to match. Routing the two layered PCB was really a challenge, since each of the 60 RGB buttons needs four connections for the RGB led and two for the button pad. The following shows the final layout of the button matrix PCB.
I sent this design to a professional PCB manufacturer and received after some days the following PCB.
After the arrival, I quickly soldered the diodes and RGB leds to the PCB and connected my multimeter in diode test mode to check that everything is working correctly.
The matrix microcontroller unit (MCU) operates the button matrix PCB by scanning the button matrix and driving the RGB led matrix. First, I explain why I added a second MCU to drive the matrix. Second, I show how the MCU creates a lot of different colors by using Bit Angle Modulation (BAM) without using a lot of processing power. The final section covers the communication between the MIDIbox core and the matrix MCU via I2C.
Reasons for a separate MCU
Driving the button matrix PCB was the greatest challenge of this project. The MIDIbox project provides with the MIDIbox NG software a nice overall solution for DIY midi controllers that also allows to scan a button matrix and drive a RGB matrix. However, I tried this variant and decided to drive the PCB by custom code, since the amount of available colors is currently limited inside MIDIbox NG. For my use case the amount of available colors is really important, since LambdaControl’s clip matrix should behave like the clip launcher of Ableton Push, which is not possible with only 16 colors. Especially, the unusual matrix dimension of LambdaControl conflicted with the approach of MIDIbox NG, since the amount of available colors is bound to the size of the matrix and a 6x10 matrix needs to be driven like a 16x16 matrix, which again limits the amount of colors.
With a custom implementation for the button scanning and driving of the RGB leds, I could solve these problems and achieving a high amount of different colors for the clip matrix.
First, I tried to drive the matrix by just using the MIDIbox core with a custom firmware, but this approach was for me not successful. The main reason for this was that the leds on the matrix quickly started to flicker when the MIDIbox core was busy with other tasks. Even after several iterations I could not solve this problem and achieve stable timings.
LambdaTon from the future here: Later after I have finished the controller and shared it with the community, Torsten Klose, the creator of the MIDIbox project, pointed out that in general there should be no problem to implement a RGB matrix like mine with just using the MIDIbox core. Hence, if you want to invest some time this should also be an option.
However, I decided to take a short cut and implement the matrix related tasks on a separate microcontroller unit (MCU) to decouple the several tasks and achieve a more consistent timing without digging deep into FreeRTOS. The connection between the MIDIbox core and the added matrix MCU is achieved by I2C.
Another option would have been to use one of the many existing RGB led matrix driver ICs out there, but I found the challenge of implementing all in software really interesting. Maybe, I will use one of them in a following project.
With RGB leds we can create seven colors by simply combining the different led colors red, green, and blue. The color mixing with these light emitting diodes is additive, such that we produce white when the red, green, and blue leds are turned on. The following picture shows these seven base colors.
Pulse Width Modulation (PWM)
However, seven colors are not enough for our purpose. Hence, we need to generate more colors by driving the three leds with different intensities (for example 50% of full power). This intensity control can be implemented by using a pulse width modulation (PWM). The PWM signal is a square wave signal (either completely on or off) and has a specific frequency at which this on and off period changes happen. A PWM signal with a 50% duty cycle is half the time on and half the time off. In our led case this means that the led with a 50% duty-cycle PWM is driven with half the power. Our eye does not recognize brightness linearly, such that this does not mean that the led driven with a 50% PWM signal looks half as bright as the full powered led. However, for just creating different colors for our pads, we do not need to learn more about this, since we can simply find nice colors by experimenting with the different settings.
The PWM allows us to control the intensity of the different leds, such that we can then combine the different intensity combinations to create a lot of different colors. The following picture shows the 16 intensity values (goes up from right to left) that I use with LambdaControl.
Problems with Software PWM
If we want to drive the RGB LED matrix with 120 Hz, this means that we need to set the complete matrix with its ten columns every 8.33 ms. So, we have 0.833 ms to set the color in one column. If we want to provide 16 intensity levels for each LED within one of these refresh periods, we need to split the 833 µs into sub-chunks for the PWM of 52 µs length. In a simple software PWM implementation this means that we need to fire in one seconds 19200 interrupts and turn the LED either on or off in each one depending on the desired color.
We see that in our scenario a software based PWM implementation is quite computing intensive for our small microcontroller, especially since we still need to change the multiplexing, feed the data into the shift registers and communicate with the MIDIbox core. Hence, using the more efficient Bit Angle Modulation (BAM) is a good decision in our scenario to reduce the number of necessary interrupts.
Bit Angle Modulation (BAM)
Luckily, our eye is sluggish in regards to very quick brightness changes. Hence, for us humans it does not make a difference if we see a perfectly timed PWM signal with a 50% duty-cycle or any other distribution as long as the overall on/off-ratio stays the same. BAM reduces the amount of necessary interrupts by using a small amount of interrupts with different durations instead of a large amount of interrupts with a fix duration. For example, lets say that our smallest interrupt should consume 52 µs. Then our second interrupt takes 111 µs, our third 222 µs, and our last one 444 µs. All four interrupts together take 833 µs, which results in a driving frequency of around 120 Hz for our ten column matrix. By turning the led either on or off in these four interrupts, we can achieve the same 2 to the power of 4 = 16 intensity levels, but we are only using 4,800 interrupts with BAM instead of the 19,200 with a software PWM in one second. Hence, the microcontroller is less occupied and free to do other stuff like communication with the MIDIbox core.
Drawbacks of the current Implementation
As already mentioned, we need to drive each matrix column with BAM to generate a nice variation of color. Therefore, we need to do the BAM routine really quickly to be able to achieve a good refresh rate. Because, the BAM routine needs to be done for each column before we switch to the next one. By experimenting I were able to produce good results when the BAM interrupts together consume 3.84 ms. This also means that a full refresh of the matrix takes 38.4 ms, which results in a refresh rate of around 26 Hz. This refresh rate is actually quite low, but with the 16 MHz processor and the necessary shift registers artifacts appeared when driving the matrix faster.
The following picture shows the nice colors that LambdaControl produces by using BAM with multiplexing.
I think that the low refresh rate of the matrix would only be a problem with quickly changing content. But LambdaControl’s grid content does not change that frequently. For future projects I will definitely use a specialized IC or FPGA for driving matrices, since with them you can drive the LED matrix with a really high refresh rate without consuming any computation time from the MIDI controller MCU. Moreover, they drive the matrix with stable timings such that flickering should not be a problem.
Here, you can find the complete implementation of the matrix MCU firmware written in C++.
Every mechanical button bounces (quickly changes between on and off) before a stable state is achieved. Therefore, debouncing the input buttons is an important task. LambdaControl implements this by sending only button change events when the inputs are stable for two complete matrix scanning runs. Each of this runs consume as shown in the previous section 38.4 ms, such that the input delay of LambdaControl is above 76.6 ms and below 115.2 ms depending on the timing of the button press. Additionally, the delay is increased by further processing steps like the I2C communication with the MIDIbox core and the MIDI message transmission over USB. This delay is to large to play notes with the grid, but for its current use-case of triggering clips this is fast enough. Techno normally is played at around 128 beats per second, such that we have in the last bar before a clip change need to happen 1.875 s to trigger the button. A faster button scan rate can be easily achieved by using a faster MCU or using a specialized IC to scan the matrix.
Moreover, the debouncing works on column-level and not on button-level, such that only one counter for each column is used instead of six counters for each row. This means that LambdaControl is able to detect multiple button presses, but each new button press retriggers the debounce procedure and increases the input delay. This is not a huge drawback, since in the given scenario only one button press in a column makes sense. This is a good optimization, because we only need to store and maintain ten counters for the columns instead of 60 counters for the individual buttons.
The introduction of the RGB button matrix MCU directly created the need for a reliable communication mechanism between the matrix MCU and the MIDIbox core, such that the MIDIbox core can send the color information to the matrix MCU and the matrix MCU can inform the MIDIbox core about button presses. This interface is implemented by I2C. I2C is a master-slave communication protocol that allows to communicate with up to 127 slave devices. Luckily, the used MIDIbox core directly supports I2C such as the MIOS32 OS.
When the MIDIbox core gets a color change event from Ableton, it directly transmits this event via I2C to the matrix MCU. Moreover, the MIDIbox core frequently polls the matrix MCU for new button presses. When no button press has happen, an empty button event is sent as response. Moreover, the matrix MCU stores the button events inside a ring buffer, such that the button events can be temporarily stored before the transmission. This mechanism works quite well in our given scenario. However, a nice future change could be to implement an external “new button event”-trigger, such that the MIDIbox core just asks for button events when necessary.
Ableton Live Integration
Currently I am using Ableton Live to produce my music, such that Live will also be used for my Live performance. Therefore, LambdaControl needs a complete integration into Ableton Live like Ableton Push. The simplest way to integrate a MIDI controller into Live is by using the MIDI mapping feature. However, the MIDI mapping feature does not provide a way to fully make use of the clip launcher, such that a special script called MIDI remote script is necessary.
Integrating simple controls like the volume fader of a MIDI controller into Ableton Live is really easy achievable by using the MIDI mapping feature. You simply click on the MIDI mapping tool in Live, select the desired function in the GUI and then use the corresponding fader on the MIDI controller to program the mapping. Therefore, all simple controls like the volume faders or send/return-pots can be programmed by using the MIDI mapping feature. This also has the benefit that changes to the mapping can be easily achieved from Live’s GUI. However, not all features from Live can be mapped by the MIDI mapping, such that LambdaControl also needs a special python script, which Ableton calls MIDI Remote Script.
MIDI Remote Script
As already mentioned, complex tasks like controlling the clip launcher (and getting feedback from it) is officially not supported by Ableton Live for custom controllers like mine. Hence, we need to make us of the so called MIDI Remote Script, which Live internally uses to integrate controllers like Ableton Push or the Launchpad. Actually, I got all my information about the MIDI remote scripts from Julien Bayle’s website.
The integration of LambdaControl’s grid into Ableton Live 9 is achieved by a specifically for this purpose written MIDI remote script, which you can find on github. The script tells Live that LambdaControl has a RGB matrix of size 6x10. Moreover, it programs the endless encoder of the master channel as selection method for the grid, such that it can be used to scroll up and down in the clip launcher. For more information check out the website of Julien Bayle or the MIDI remote script repository on github.
My first idea was to create the case for LambdaControl out of wood, but at the end of 2016 I discovered 3D printing as a new hobby, such that I quickly changed my plans and used my 3D printer to create the case. Overall, this creates a better final results, since my wood working skills and tools are limited.
First, I designed the 3D model for the case in Fusion 360 and selected a proper mounting mechanism to connect the different parts to the case. After that I printed LambdaControl’s case with my self-build FDM 3D printer out of PLA.
Design with Fusion 360
I designed the case in Fusion 360 such that it contains all necessary PCBs and a stable mounting mechanism for the RGB button matrix and the front panel. However, the actual case is with 416.4 mm x 276.4 mm to large to be printable by most common 3D printers (mine is sadly not an exception to this). Therefore, I splitted the case into six chunks that are connected to each other by using tongue and groove joints. Hence, the case can be printed by the most common 3D printers out there. All necessary files to print the case can be found in the hardware repository.
A stable mounting mechanism is necessary to mount the faceplate, PCBs, and RGB button matrix securly to the case. A good option for this are the common injection molding inserts. This inserts are made out of brass and are threaded, such that they can be used with standard screws. The modeled case contains for each connection a hole that can be filled with such an injection molding insert. These inserts can easily be pressed/melted down into the holes by using a soldering iron with a round tip.
Printing the case parts is relatively easy, since they only require supports in the tongue and groove joint areas. However, printing the parts required on my DIY 3D printer around 13 hours per piece with a 0.2mm layer height. So the complete case required around 78 hours to be printed. The following pictures were taken during the print process.
The following pictures show the final result. First you can see the six pieces and how they are arranged to create the complete case. The tongue and groove joints work perfectly and create a secure connection between the different pieces. Special thanks to my friend Henning for the tip with the tongue and groove joints. Overall, I were really happy with the results that my DIY 3D printer produced. But, I think with a little bit more tuning even better results can be achieved.
Assembling the different parts together was really straight forward after all the work. I simply put the electronics into the case, connected everything, and closed the case with the front panel. Sadly, I have not taken a lot of pictures during this process, because I could not wait to test out the final controller. However, the following pictures should give an impression about the inner workings.
Finally, my DIY MIDI controller LambdaControl is alive and performs as desired. During the build process I really learned a lot and my skills and tools have grown really quickly during it.
If you just want to make music, I strongly advice you to just by a finished MIDI controller and adjust it as far as possible to your needs and accept the compromises you need to make. Since, with a project like this you definitely spend more money and time (of course this highly depends on your currently skills and available tools). But, I still would jump into this rabbit hole of creating my own DIY controller without hesitating, because I enjoyed the journey.
So for me, the next chapter of the “Creating a live performance”-tale starts with implementing and testing my concept. And I am really sure that this will not be the last iteration of LambdaControl. Actually, I still need to assemble the second controller…
So, I hope this information will help you to create the MIDI controllers of your dreams.