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 complete 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 cover the build process for the front panel. The next chapter will be about the hardware and why I used the MIDIbox project. The RGB Button Matrix chapter is about my custom RGB button matrix and it’s software. Moreover, I explain how I integrated LambdaControl by a custom MIDI Remote Script into Ableton Live. Finally, I explain how I build the case of LambdaControl with a 3D printer, cover the final assembly, and present the final result.
You can find all the material that I created to build LambdaControl on github. I distributed the material into four repositories:
- Repository for the hardware related files like the faceplate or the 3D printable case
- MIOS32 firmware for the MIDIbox core that 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 controller without to much thinking. So the 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 the send/return based effect section.
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 of the controller.
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 send and return 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 it is really possible to decide if there is enough space between the components. I also created a small prototype to check if 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 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 shaft of the ordered potentiometers were a little bit shorter than excepted, such that I needed to use a file to get them fitting.
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 hardware 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 utilizing 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 to build the core and provides an FreeRTOS based operation system MIOS32. You can either chose to use on of their provided firmwares like MIDIbox NG to program your controller or you can freely program the core by creating custom firmwares for the MIOS32 OS by a standard programming tool chain.
I decided to use the MIDIbox project, since it provides a complete solution, but still can be extended by custom code and hardware. Hence, I save a lot of time while loosing no flexibility.
The analog inputs of LambdaControl are the linear and rotary potentiometers. LambdaControl consists of 60 rotary pots and 10 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 (lower noise floor than the internal one) and eight multiplexers, such that 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 resistance value. The outputs of an encoder are digital, such that we can make use of the DIN modules of the MIDIbox project to scan a lot of encoders with a small amount of input pins. The DINx4 module basically consists of four shift registers that allows to extend available input pins for each module by 32. 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. Therefore, the encoder simply contains two more pins for the button that you can also scan by the help of the DIN module.
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 schematic and PCB that I have created for the matrix and talk about the actual hard- and software which drives the leds and scans the buttons.
Sparkfun also offers a small 4x4 PCB for the silicon pads, but I quickly decided to create my own, since I designed an unusual matrix dimension (10x6 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 typically used inside a matrix configuration to avoid masking errors with the buttons.
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. 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 PCBs.
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 micro controller unit (MCU) operates the button matrix PCB by scanning the button matrix and driving the RGB led matrix. In the following, I explain first why a separate MCU was necessary and after that I cover the different software parts of the matrix MCU. Also I explain how the MCU creates a lot of different colors by using bit angular modulation (BAM) without using a lot of processing power. The final section covers the communication between the MIDIbox core and the matrix via I2C.
Reasons for a separate MCU
Driving the button matrix PCB was the greatest challenges 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 quickly decided to drive the PCB by custom code, since the amount of available colors is 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 10x6 matrix needs to be driven like a 16x16 matrix.
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 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. Hence, I decided to implement the matrix related tasks on a separate microcontroller unit (MCU), such that the MIDIbox core can focus on his main tasks. 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 them in one of my next projects.
With RGB leds we can achieve seven color plus the off state 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 led are turned on. The following picture shows these seven colors and the off state.
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 a bright as the full powered led. However, for just creating different colors for our pads, we do not take this into account.
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 that I use with LambdaControl.
Problems with Software PWM and Multiplexing
If we want to drive our matrix with 120 Hz, this means that we have 8.33 ms time to set the intensity of the led before the next time chunk needs to be executed. If we want to provide 16 intensity levels within this chunk, this means that we need to split the 8.33 ms chunk into sub-chunks for the PWM of 0.52 ms length. In our software PWM implementation this means that we need to fire in one seconds 1920 interrupts to achieve 16 intensity levels. Of course could you reduce the amount of necessary interrupts by using an adaptive size for the interrupt length depending on the current duty-cycle, but this would be really complex in our scenario, since we need drive three leds for each button and also multiplex the ten columns and six rows.
We see that in our scenario a software based PWM implementation is quite computing intensive for our micro controller, especially since we need to multiplex to drive the different rows and columns of our matrix. Hence, using the more efficient Bit Angular Modulation (BAM) is a good decision in our scenario.
Bit Angular 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 relationship stays the same. BAM reduces the amount of necessary interrupts by using a small amount of interrupts with different durations. For example, lets say that our smallest interrupt should consume 0.56 ms. Then our seconds interrupt takes 1.11 ms, our third 2.22 ms, and our last one 4.44ms. All of our interrupts together take 8.33 ms, which results in a driving frequency of around 120 Hz. By turning the led either on or off in these four interrupts, we can achieve the same 2^4 = 16 intensity levels, but we are only using 480 interrupts with BAM instead of the 1920 ones with a software PWM that can be multiplexed. Hence, the micro controller is less occupied and is free to do other stuff like multiplexing the BAM to drive all leds.
Drawbacks of the current Implementation
The complete implementation of the matrix MCU firmware is available on github. However, as already mentioned in a multiplexing environment things are a little bit more complex. The matrix MCU utilizes the shift registers of the DIN modules to connect the matrix. Therefore, we need to feed the data first into the shift registers and then trigger the latch before the data is set. This deserialization consumes some time. Moreover, the matrix is connected, such that the row is set/read in parallel and the column pins are used to select the desired row. By that we need to iterate over the ten columns frequently to set the red, green, and blue channels of the row’s six leds and read the six input buttons of the row.
Hence, we need to do the BAM routine really quickly to be able to achieve a good refresh rate. 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.
However, it seems like even this refresh rate is able to produce stable colors on LambdaControl. 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 often, such that the current solution is sufficient. For future projects I will either use a faster MCU or a specialized IC for driving matrices.
Every mechanical button bounces (quickly changes between on and off) before a stable state is achieved. Therefore, debouncing the input buttons of the grid 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 LambdaControl’ use-case of triggering clip changes 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 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
My favorite DAW is Ableton Live, such that Live will also be used for my Live performance. Therefore, LambdaControl needs a complete integration into Ableton Live like down with Ableton Push. The simplest way to integrate a MIDI controller into Live is by using the MIDI mapping feature. However, the MIDI mapping feature do not provide a way to fully make use of the clip launcher, such that also a special script called MIDI remote script is necessary.
Integration simple controls like the volume fader from a MIDI controller into Ableton Live is really easy achievable by using the MIDI mapping feature. You simply click on the MIDI mapping tool, select the desired function in the GUI and then use the corresponding fader to program the mapping. Therefore, all simple controls like the volume fader, send/return-pots, and rotary encoder for controlling the looping of LambdaControl is implemented 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 is called by Ableton 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. 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 10x6. 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 decided to use a 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 by a 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 like mine. Therefore, I cut 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. 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 filed with such an injection molding insert. These inserts can be easily 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 overall case required around 78 hours to be printed. The following pictures were taken during the print process.
The results were quite good on DIY 3D printer. I think with a little bit more tuning even better results can be achieved. The following pictures show the final results. 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.
Assembling the different parts of LambdaControl together was really straight forward after all the work. I simply put the electronics into the case, connected every things, and closed the case with the front panel. Sadly, I have not taken a lot of pictures during this process. However, the following pictures should give an impression.
Finally, my DIY MIDI controller LambdaControl is alive and performs as desired. During the build process I learned really a lot and my possibilities for future projects have also extended. However, with my current skills and tools I would have done some things differently. But, I am still satisfied with the result and will use LambdaControl to develop my live performance. Actually, I still need to assemble the seconds controller…
So, I hope this information will help others to create the MIDI controllers of their dreams.