The goal was to create a custom MIDI controller for Touchdesigner with a specific layout for my mixing operator workflow.

Components:

  • Arduino Nano
  • 74hc4051 Multiplexer
  • 4x Rotary Switch
  • 3x Arcade Gaming Buttons
  • 4x On/Off Switch

Software Libraries:

  • Control Surface
  • Platform I/O

Notes

Source code

main.cpp:

#include <Arduino_Helpers.h>
#include <AH/Hardware/ExtendedInputOutput/AnalogMultiplex.hpp>
#include <Control_Surface.h>

// Interface MIDI USB
USBMIDI_Interface midi;

CD74HC4051 mux {
  6,       // Digital D6 Pin
  {5, 4, 3} // Address pins S0, S1, S2
};

CCButton btns[] {
  { mux.pin(0), { MIDI_CC::General_Purpose_Controller_1, Channel_2 } },
  { mux.pin(1), { MIDI_CC::General_Purpose_Controller_2, Channel_2 } },
  { mux.pin(2), { MIDI_CC::General_Purpose_Controller_3, Channel_2 } },
  { mux.pin(3), { MIDI_CC::General_Purpose_Controller_4, Channel_2 } },
  { mux.pin(4), { MIDI_CC::General_Purpose_Controller_5, Channel_2 } },
  { mux.pin(5), { MIDI_CC::General_Purpose_Controller_6, Channel_2 } },
  { mux.pin(6), { MIDI_CC::General_Purpose_Controller_7, Channel_2 } },
  { mux.pin(7), { MIDI_CC::General_Purpose_Controller_8, Channel_2 } },
};

// Déclaration des broches analogiques utilisées
const int switchPins[4] = {A0, A1, A2, A3};

// Paramètres de debounce (un par switch)
unsigned long lastChangeTime[4] = {0, 0, 0, 0};
const unsigned long debounceDelay = 150; // en ms

// Dernier index connu pour chaque switch
int lastIndex[4] = {-1, -1, -1, -1};

// Nombre de switchs
const int numSwitches = 4;

// Prototypes
int getSwitchIndex(int value);
int getSmoothedAnalog(int pin, int samples = 30);
int getSmoothedMuxAnalog(int pin, int samples = 20);

void setup() {
  mux.begin();
  Control_Surface.begin();
  for (int i = 0; i < numSwitches; i++) {
    pinMode(switchPins[i], INPUT);
  }
}

int lastResultCCBtn[7] = {0, 0, 0, 0, 0, 0, 0};

void loop() {
  unsigned long currentTime = millis();

  for (int i = 0; i < numSwitches; i++) {
    int analogValue = getSmoothedAnalog(switchPins[i]);
    int index = getSwitchIndex(analogValue);

    if (index != lastIndex[i] && index != -1 &&
        (currentTime - lastChangeTime[i] > debounceDelay)) {

      MIDIAddress address(i + 16, Channel_1);  // CC16 = GP Controller 1
      midi.sendControlChange(address, index);

      lastIndex[i] = index;
      lastChangeTime[i] = currentTime;
    }
  }

  Control_Surface.loop();
}

int getSmoothedAnalog(int pin, int samples) {
  long total = 0;
  for (int i = 0; i < samples; i++) {
    total += analogRead(pin);
    delay(2);
  }
  return total / samples;
}

// Mapping des plages vers index 0-11
int getSwitchIndex(int value) {
  if (value < 3120) return 0;
  else if (value < 3300) return 1;
  else if (value < 3400) return 2;
  else if (value < 3600) return 3;
  else if (value < 3660) return 4;
  else if (value < 3730) return 5;
  else if (value < 3780) return 6;
  else if (value < 3820) return 7;
  else if (value < 3860) return 8;
  else if (value < 3900) return 9;
  else if (value < 3933) return 10;
  else return 11;
}

Platform.ini

[env:nanoatmega328]
platform = nordicnrf52
board = nano33ble
framework = arduino
monitor_speed = 57600

lib_deps = https://github.com/tttapa/Control-Surface.git#main
lib_ignore = MIDIUSB

Build / Upload commands:

[env:nanoatmega328]
platform = nordicnrf52
board = nano33ble
framework = arduino
monitor_speed = 57600

lib_deps = https://github.com/tttapa/Control-Surface.git#main
lib_ignore = MIDIUSB

Scanning value of multiplexer

#include <Arduino_Helpers.h>
#include <AH/Hardware/ExtendedInputOutput/AnalogMultiplex.hpp>
#include <Control_Surface.h>

CD74HC4051 mux {
  A7, 
  {5, 4, 3}
};

void setup() {
  Serial.begin(115200);
  mux.begin(); 
}

void loop() {
  for (pin_int_t pin = 0; pin < mux.getLength(); ++pin) {
    Serial.print(mux.analogRead(pin));
    Serial.print('\t');
  }
  delay(1000);
  Serial.println();
}