30 fader 12 button arduino midi controller

This is a place for sharing with the community the results you achieved with QLC+, as a sort of use case collection.
You can share photos, videos, personal hardware/software projects, interesting HOWTO that might help other users to achieve great results.
Post Reply
Posts: 53
Joined: Wed Jan 20, 2016 7:50 pm
Location: PARIS France
Real Name:

30 fader 12 button arduino midi controller

Post by willmodelisme » Thu Mar 02, 2017 2:23 pm

Here is a short presentation of my 42 control (30 faders and 12 buttons) arduino based midi interface to use with QLC+ or any soft that integrate midi control.

It is based on Notes and Volts blog tutorial for an “arduino midi controller: multiplexer” page that you will find here:
http://www.notesandvolts.com/2016/07/ar ... exers.html

Thanks to http://www.notesandvolts.com/ to let me publish his work here.

The video on the page is very clear and there is nothing more to add.
The wiring is very well explained also.

You will be able to make it work easily and the arduino code (call it “sketch”) works well if you follow the explanation on the youtube video.

My controller use 3 cd74hc4067 multiplexer module, called MUX board, wired in //.
PIN S0, S1, S2 and S3 of each MUX board are wired together, PIN SIG 1 and 2 from 2 MUX (fader) board goes to A0 and A1 of the arduino, and the SIG 3 from the third MUX board (button) goes to a digital IN of the arduino.

I have tried more MUX board when coding but the arduino run short of memory with 4 analog MUX board with all the 16 pins of each board coded to be active.
So we have to admit there is a limit in the input and 64 analog input is not possible.
With 30 analog an 12 digital input fro my project, global variables of the arduino code use 1080 bytes (52%) of dynamic memory.
There must be solution to have more input but for the moment that is enough for my use.

I grabbed a very old DMX controller just to have a hard case in which I could wire a lot of fader (the boxing have always been the difficult part of all my project…).
I ended up with a 30 fader/30 button case. That is enough for most application IMHO.
I decided to use only the metal case and I hooked inside 30 new fader and 12 new buttons, for a total of 42 control.

The fader uses 30 PIN of 2 DEMUX board, 15 on the first board, 15 on the second board (one PIN left unused on each board) and the flash button use 12 PIN on the third DEMUX board.
The button are just for flash/scene purpose and I don’t need more than 12.

For button you need to dedicate a MUX board only for digital because you can’t mix digital and analog SIG output.

The output is a midi signal. So you will use midi/usb converter. Mine is directly wired in the case but you can leave it outside of the box of course.
If you wire it inside, you can easily take the USB 5V to run the arduino board. That’s what I have done and it work like a charm. You have only one USB cable to make the controller work and it is recognised as a midi interface.

The midi message for each fader are set in the arduino sketch. You can change them as you need of course but it is more convenient to set them up for once.

For my use, I have distributed the midi note value from 50 to 79 (from fader 1 to fader 30 and each button have the same midi note than its corresponding fader).
That give me room for a specific QLC+widget that I use for theater.
It is a 48 dimmer widget that is set with midi message from 1 to 48.
Generally all theater that uses classical lighting set their dimmer from DMX channel 1 up to 48.
I have set midi message for this widget because I also use touchOSC and an ipad..
I use it for light setup and dimmer check up directly on stage and it is very helpful.

Here are some pictures.
Button are not yet wired because I had hard time to find suitable ones (ones that would fit into the existing mounting holes).
They are on the way home ;).

Find here some pictures.

Enttec DMX USB Pro
Windows 10

Posts: 58
Joined: Sun Jul 17, 2016 8:22 am
Location: Denmark
Real Name: Allan Madsen

Re: 30 fader 12 button arduino midi controller

Post by asm » Thu Mar 23, 2017 4:10 pm

Hi looks nice
You can use 4051 or 4067 8 and 16 ports multiplextor, that is what iam using to miy midi controler on my arduino uno. (With other firmware on the atmega16u2)

Posts: 58
Joined: Sun Jul 17, 2016 8:22 am
Location: Denmark
Real Name: Allan Madsen

Re: 30 fader 12 button arduino midi controller

Post by asm » Thu Mar 23, 2017 4:11 pm

Sorry i can see you do :-)
Take a look here https://github.com/ddiakopoulos/hiduino ... output.ino
Then you can make your own midi controller on a Arduino Uno (org)

Posts: 53
Joined: Wed Jan 20, 2016 7:50 pm
Location: PARIS France
Real Name:

Re: 30 fader 12 button arduino midi controller

Post by willmodelisme » Wed Mar 29, 2017 1:54 pm

Good to hear you also have fun as much as I had.
The ardruino project will be soon (I hope and I am waiting for) adapted for a teensy.
I will keep you informed.
Enttec DMX USB Pro
Windows 10

Posts: 58
Joined: Sun Jul 17, 2016 8:22 am
Location: Denmark
Real Name: Allan Madsen

Re: 30 fader 12 button arduino midi controller

Post by asm » Fri Apr 07, 2017 11:01 am

I am using a Arduino pro mikro with a atmega32u4 it works.
https://www.google.dk/search?q=arduino+ ... QFmDz6ZHM:

Here is the code for now. (Work in progress)

Code: Select all

 * MIDIUSB_v1.ino
 * Created: 3/29/2017
 * Author: Allan Madsen

#include "MIDIUSB.h"

// Total num of MIDI controls.
#define NUM_CONTROLS 1

//Num of channels to read directly through analog pins

// Num of channels read through 4051 muxchip

// Mux output to Arduino analog pin
#define MUX_COM A10

// Mux address select digital pins
#define MUX_ADDRESS_SEL_0 2
#define MUX_ADDRESS_SEL_1 3
#define MUX_ADDRESS_SEL_2 4

//MIDI channel to use
#define MIDI_CHANNEL 0

//CC slots 14-31 are recognizedas assignable controls by MIDI standard
#define MIDI_CC_START 14

//maps to map log taper slider pots to linear readings
int sliderFromMap[]= {0,62, 187, 550, 1023};
int sliderToMap[] = {0, 255};
byte sliderFromMapSize;
byte sliderToMapSize;

//maps to map log taper rotary pots to linear readings
int knobFromMap[] = {0, 55, 130, 516, 1023};
int knobToMap[] = {0, 127};
byte knobFromMapSize;
byte knobToMapSize;

//Holds currentMIDIcontrol values
int ccValue[NUM_CONTROLS];

//Stores pin numbers of analog channels
byte analogChannelPin[ANALOG_NUM_CHANNELS];

// First parameter is the event type (0x09 = note on, 0x08 = note off).
// Second parameter is note-on/note-off, combined with the channel.
// Channel can be anything between 0-15. Typically reported to the user as 1-16.
// Third parameter is the note number (48 = middle C).
// Fourth parameter is the velocity (64 = normal, 127 = fastest).

void noteOn(byte channel, byte pitch, byte velocity) {
  midiEventPacket_t noteOn = {0x09, 0x90 | channel, pitch, velocity};

void noteOff(byte channel, byte pitch, byte velocity) {
  midiEventPacket_t noteOff = {0x08, 0x80 | channel, pitch, velocity};

void setup() {

  sliderFromMapSize = sizeof(sliderFromMap) / sizeof(int);
  sliderToMapSize = sizeof(sliderToMap) / sizeof(int);

  knobFromMapSize = sizeof(knobFromMap) / sizeof(int);
  knobToMapSize = sizeof(knobToMap) / sizeof(int);

// First parameter is the event type (0x0B = control change).
// Second parameter is the event type, combined with the channel.
// Third parameter is the control number number (0-119).
// Fourth parameter is the control value (0-127).

void controlChange(byte channel, byte control, byte value) {
  midiEventPacket_t event = {0x0B, 0xB0 | channel, control, value};

void loop()
  //Loop through all mux channels
  for (byte muxChannel = 0; muxChannel < MUX_NUM_CHANNELS; ++muxChannel)
    int midiValue = multiMap(readMuxChannel(muxChannel), sliderFromMap, sliderFromMapSize, sliderToMap, sliderToMapSize);

    if (ccValue[muxChannel] != midiValue)
      ccValue[muxChannel] = midiValue;
      midiControlChange(MIDI_CHANNEL, MIDI_CC_START + muxChannel, midiValue);

  //Loop through all analog channels
  for (byte analogChannel = 0; analogChannel < ANALOG_NUM_CHANNELS; ++analogChannel)
    int analogValue = analogRead(analogChannelPin[analogChannel]);
    int midiValue = 0;

    if (analogChannel == 0)
      //read single remaining slider
      midiValue = multiMap(analogValue, sliderFromMap, sliderFromMapSize, sliderToMap, sliderToMapSize);
      //the rest are rotary pots
      midiValue = multiMap(analogValue, knobFromMap, knobFromMapSize, knobToMap, knobToMapSize);

    if (ccValue[MUX_NUM_CHANNELS + analogChannel] != midiValue)
      ccValue[MUX_NUM_CHANNELS + analogChannel] = midiValue;
      midiControlChange(MIDI_CHANNEL, MIDI_CC_START + MUX_NUM_CHANNELS + analogChannel, midiValue);

//Reads analog value of mux chip at selected channel
int readMuxChannel(byte channel)
  //Select mux channel
  digitalWrite(MUX_ADDRESS_SEL_0, channel & 1);
  digitalWrite(MUX_ADDRESS_SEL_1, (channel >> 1) & 1);
  digitalWrite(MUX_ADDRESS_SEL_2, (channel >> 2) & 1);

  //Read mux output
  return analogRead(MUX_COM);

//Linear interpolates a value in fromMap to toMap
int multiMap(int value, int fromMap[], int fromMapSize, int toMap[], int toMapSize)
  //Boundary cases
  if (value <= fromMap[0]) return toMap[0];
  if (value >= fromMap[fromMapSize - 1]) return toMap[toMapSize - 1];

  //Find the fromMap interval that value lies in
  byte fromInterval = 0;
  while (value > fromMap[fromInterval + 1])

  //Find the percentage of the interval that value lies in
  float fromIntervalPercentage = (float)(value - fromMap[fromInterval]) / (fromMap[fromInterval + 1] - fromMap[fromInterval]);

  //Map it to the toMap interval and percentage of that interval
  float toIntervalPercentage = ((fromInterval + fromIntervalPercentage) / (fromMapSize - 1)) * (toMapSize - 1);
  byte toInterval = (byte)toIntervalPercentage;
  toIntervalPercentage = toIntervalPercentage - toInterval;

  //Linear interpolate
  return toMap[toInterval] + toIntervalPercentage * (toMap[toInterval + 1] - toMap[toInterval]);

//Sends MIDI CC signal
void midiControlChange(byte channel, byte ccNum, byte value)
  //Send a MIDI control change event through USB
  controlChange(channel, ccNum, value);
/*void loop() {
  controlChange(1, 0, 0);   // Channel 0, control, value
 // delay(500);
  controlChange(0, 0, 100);   // Channel 0, control, value
 // delay(500);
  controlChange(0, 1, 200);   // Channel 0, control, value
 // delay(500);
  controlChange(1, 0, 255);   // Channel 0, control, value

  // controlChange(0, 10, 65); // Set the value of controller 10 on channel 0 to 65

Post Reply