Custom Midi Controller with Displays for Lighting Control
Introduction
I always wanted to have a lighting desk that has displays instead of the usual label tape. Since buying is not an option I made my own :-) The design is straight forward and consists of eight faders and eight flash buttons. You can find the whole code here.
The remainder of this post will go through some of the technical details in no particular order.
1000hz Midi Output
Midi values are calculated and send at 1000hz using a high priority timer. Fader readings and display updates are done using dma. Display buffers are calculated in a low priority loop. This way I can guarantee that the delay between moving a fader or pressing a button and the corresponding midi signal leaving the controller is always less than one millisecond.
Displays
The controller uses cheap i2c oled displays from aliexpress. The problem with those displays is, that they have a hard coded i2c address. To control eight displays simultaneously I use the TCA9548A I2C multiplexer which can be found on ebay for a few bucks.
Since I2C is very slow display updating takes a lot of time and needs precise timing. I modified the Adafruit SSD1306 driver to use dma. That way the display updates can run in the background while the processor does more useful stuff. One full display refresh takes approximately 20ms. Thus it is impossible to update all 8 displays with a decent frame rate. However that is rarely a problem because the user usually doesn’t move more than three or four faders at the same time. And even if he does its only the display output that lags, not the actual midi output.
Fader Smoothing
Due to my shitty soldering skills, unshielded wires and just overall bad circuit design the fader readings are super noisy. To compensate I use a rather high sampling time of 0,000019917s (239 cycles at 12Mhz) and an additional software filter. The filter is taken from here and very slightly modified.
Sampling all eight faders takes 0,000159336s and is continuously done in the background using dma.
Linear to Logarithmic Conversion
The controller uses linear faders. However I like to use logarithmic faders for some use cases (e.g. brightness changes). Thus I added a way to switch between logarithmic and linear fader behavior on the fly.
The conversation is done using a modified version of the arduino multiMap algorithm. The following conversion table is used. Note that, depending on the quality of the fader, you might never see 4095. In that case you have to reduce the last value.
Linear | Logarithmic |
---|---|
0 | 0 |
1 | 516 |
2 | 741 |
3 | 870 |
4 | 999 |
5 | 1096 |
6 | 1193 |
7 | 1257 |
8 | 1357 |
9 | 1419 |
10 | 1483 |
11 | 1548 |
15 | 1741 |
20 | 1935 |
25 | 2128 |
39 | 2547 |
60 | 3031 |
89 | 3547 |
127 | 4095 |
/** maps from linear to logarithmic fader scale
* modified from: https://playground.arduino.cc/Main/MultiMap */
int linToLog(uint16_t value)
{
//adc has only 12 bit resolution, everything above has to be garbage and shouldn't be there
if(value > 4095) value = 4095;
static float potiVals[] = {0, 516, 741, 870, 999, 1096, 1193, 1257, 1354, 1419, 1483, 1548, 1741, 1935, 2128, 2547, 3031, 3547, 4095};
static float midiVals[] = {0, 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10 , 11 , 15 , 20 , 25 , 39 , 60 , 89 , 127};
// search right interval
uint8_t pos = 0;
while(value > potiVals[pos]) pos++;
// this will handle all exact "points" in the potiVals array
if (value == potiVals[pos]) return midiVals[pos];
//if the code reaches here pos is always > 0
// interpolate in the right segment for the rest
return (value - potiVals[pos-1]) * (midiVals[pos] - midiVals[pos-1]) / (potiVals[pos] - potiVals[pos-1]) + midiVals[pos-1];
}