Control Surface Library - Multiple Arduino for 16 Channels of MCU

Hello Ya'll!

This is my first post to the forum, so thank you to all who see this and have any insight :slight_smile:

I am creating a VCA automation system for my 16 Channel analog mixing console using the Control Surface Library, and it is coming along great! I have fader control and bidirectional communication from Ableton to my channel strip buttons and Faders.

I want to be able to have access to MCU for all 16 channels at one time without using banks, so I was hoping I can use 2 Arduino's, 8 channels of MCU on each.

Is this possible? I have both attached to the CPU, but it seems because the reference the same code i.e. {buttonMux.pin(0), {MCU::REC_RDY_1}}, for example, both would naturally trigger the same channel since REC_RDY_1 is for the first channel in the MCU protocol and DAW.

Is there a way I can assign, say all the MCU messages from the second Ardunio to midi Channel 2 and have those messages show up on channels 9-16?

Or is there a way I can use MCU for all 16 Channels on 1 Arduino?

Thanks for any advice and help you may be!!

Anthony

If you have two Arduinos, you can select one as a "Mackie Control Universal" and the other as a "Mackie Control Universal XT (XTender)".

If you use a Teensy, you can have multiple virtual MIDI cables over a single USB connection.

Thanks Pieter!

How exactly do the cables work? I’m trying to wrap my brain around this…serial connection between the arduinos? That would be sweet to have 4 Arduinos connected to 1 USB.

And I’m wondering about multiplexers…can I have 1 MUX connected to a single Arduino and have those faders/buttons communicate with the others that are connected? Does a midi cable or pipe do that?

Does that go for the ESP32-S3 as well as teensy?

Thank you for your support! Your library is kick ass!

MIDI USB cables are virtual cables, they do not correspond to separate physical copper wires (the data for all virtual MIDI cables travel over the same USB cable).
Cables allow you to have a single USB device that has the features of multiple MIDI devices, e.g. to have a MIDI sequencer, a MIDI keyboard, a MIDI controller, and an external MIDI jack in the same device. In such an case, you might use different cables for each of those four functions. They then show up as different MIDI devices on your computer, and can be used independently by different programs.

All cables exist inside of a single Teensy, there are no other Arduinos involved. The Teensy simply shows up on your computer as having multiple MIDI ports, e.g. "Teensy MIDI 1" and "Teensy MIDI 2". Your DAW treats them as different MIDI devices.

On the Arduino side, you can specify which cable you want to send a specific MIDI message to. Control Surface is built around MIDI addresses. Each address has a cable number, which is set to Cable_1 by default. Control Surface: MIDI Tutorial
For incoming MIDI, you can check the cable attribute of the message to see which cable it came from.

That would be sweet to have 4 Arduinos connected to 1 USB.

You usually don't want to involve multiple microcontrollers. The added communication and coordination significantly complicates matters.

And I’m wondering about multiplexers…can I have 1 MUX connected to a single Arduino and have those faders/buttons communicate with the others that are connected?

I'm not sure what you mean? Which parts communicate with which other parts?

Does a midi cable or pipe do that?

MIDI pipes are something different. Think of them as connections in a virtual MIDI patch bay inside of the Arduino. In your software and hardware, there are many MIDI sources and sinks: sources generate MIDI messages (e.g. the buttons and faders of your MIDI controller), and sinks consume MIDI messages (e.g. to update an LCD display connected to your Arduino based on information sent over MIDI).
MIDI interfaces are also sinks and sources, for example, when you send a MIDI message from your DAW to the Arduino over USB, the USBMIDI_Interface in your code acts as a source (your Arduino code gets messages from the interface). When you send a MIDI message from your Arduino to the DAW, the USBMIDI_Interface acts as a sink (your Arduino code sends messages to the interface, which will in turn forward them to the computer).
A MIDI pipe is simply a connection between a source and a sink: for example, you move a fader on your controller, this generates MIDI messages (source), the source sends the messages it produces over a pipe, and on the other side of the pipe, there could be a USBMIDI_Interface, which then acts as a sink and accepts the messages from the pipe.

By changing the pipes around in your code, you can determine how messages are routed inside of the Arduino, you can add MIDI "through" ports, merge multiple MIDI streams, forward certain messages to external MIDI devices, or to other Arduinos, you can log messages to the serial monitor, etc.

In short, pipes are connections between MIDI sinks and sources. Each source can decide to assign a number from 1 to 16 to each message it sends. This is the virtual cable number. When your computer receives such a MIDI message with virtual cable number 2, it makes it seem like it came from a different device than messages with cable number 1.

Does that go for the ESP32-S3 as well as teensy?

The USB MIDI backend in the arduino-esp32 core currently only supports a single virtual cable: arduino-esp32/libraries/USB/src/USBMIDI.h at master · espressif/arduino-esp32 · GitHub

The Adafruit-TinyUSB library does support up to 16 cables, but I haven't tried it myself.

Thank you for all the great information here!

I see why the Teensy would be the best route so I can have multiple "hui" devices show up in the DAW through a single sketch. My only issue is that the Teensy runs off of 3.3V and I need to send 5v from the analog console to the Arduino because that is what it functions on and outputs. It also needs to see 0-5v back from the Arduino. Is this an option...just knocking down that 5V from the console to 3.3v using say a 1.8k and 3.3K resistor in series? 1.8k from the wiper, tie 1.8k and 3.3k together, send those to to the Analog Input pin of the Teensy, send the other end of the 3.3k to ground. and put a .1cf cap between the Analog Pin and Ground for good measure? If this will work, I think the Teensy is my solution!

In regards to the multiplexer, I miss typed that. I want to have a single mux connected to multiple arduino's at the same time. For example, send ports C0-07 to Arduino 1, and send ports C8-C15 to another. This would help sine only 1 arudino can have 8 channels at one time, and in my case, I need 16 channels at one time, so being able save ports would be really effcient (and since I already have a bunch of 16 channel mux)

I guess I can also get 8 channel mix's and use them per Arduino to simplify things. Just trying to use that I have already. :slight_smile:

Thanks for the help!

Anthony

Hey there!

I updated everything to the teensy 4.1 and I have been having a lot of success, but I am running into road block with the LEDS using a MAX 7219.

The problem i am having is i can not get more than 2 LEDs to stay lit at any time, when I do more than 2, no LEDS light up. I know i have anough power, because I use the MAX with over 32 leds for my HUI project for Pro Tools no problem.

I just want to be able to use the MAX to get the LED feedback from Ableton Live so I can light up 16 LEDS for the Rec Buttons, and also the Transport, (REWIND, FF, STOP, PLAY REC)

Once i get That I may want to add other features, but once I understand how to properly code the MAX with the Control Surface Library, I will be able to move forward with more advance options.

Thank you for your help!! I really need it :slight_smile:

I should probably mention that my LEDS go LOW to be active.

Here is my code.

#include <Control_Surface.h>



// Instantiate a MIDI over USB interface.
USBMIDI_Interface midi;




MAX7219<1> max7219 {SPI, SS};

CD74HC4067 faderMux {
  A0,       // Analog input pin
  {33, 34, 35, 36} // Address pins S0, S1, S2, S3
};

CD74HC4067 buttonMux {
  A1,         // Analog input pin
  {2, 3, 4, 5} // Address pins S0, S1, S2, S3
};

CD74HC4067 transMux {
  A2,         // Analog input pin
  {6, 7, 8, 9} // Address pins S0, S1, S2, S3
};


PBPotentiometer volumePotentiometers [] {
  {faderMux.pin(0), {MCU::VOLUME_1, Cable_1}},
  {faderMux.pin(1), {MCU::VOLUME_2, Cable_1}},
  {faderMux.pin(2), {MCU::VOLUME_3, Cable_1}},
  {faderMux.pin(3), {MCU::VOLUME_4, Cable_1}},
  {faderMux.pin(4), {MCU::VOLUME_5, Cable_1}},
  {faderMux.pin(5), {MCU::VOLUME_6, Cable_1}},
  {faderMux.pin(6), {MCU::VOLUME_7, Cable_1}},
  {faderMux.pin(7), {MCU::VOLUME_8, Cable_1}},
  {faderMux.pin(8), {MCU::VOLUME_1, Cable_2}},
  {faderMux.pin(9), {MCU::VOLUME_2, Cable_2}},
  {faderMux.pin(10), {MCU::VOLUME_3, Cable_2}},
  {faderMux.pin(11), {MCU::VOLUME_4, Cable_2}},
  {faderMux.pin(12), {MCU::VOLUME_5, Cable_2}},
  {faderMux.pin(13), {MCU::VOLUME_6, Cable_2}},
  {faderMux.pin(14), {MCU::VOLUME_7, Cable_2}},
  {faderMux.pin(15), {MCU::VOLUME_8, Cable_2}}
};


// Define the Record Ready buttons
NoteButton recbuttons[] = {
    {buttonMux.pin(0), {MCU::REC_RDY_1, Channel_1, Cable_1}},
    {buttonMux.pin(1), {MCU::REC_RDY_2, Channel_1, Cable_1}},
    {buttonMux.pin(2), {MCU::REC_RDY_3, Channel_1, Cable_1}},
    {buttonMux.pin(3), {MCU::REC_RDY_4, Channel_1, Cable_1}},
    {buttonMux.pin(4), {MCU::REC_RDY_5, Channel_1, Cable_1}},
    {buttonMux.pin(5), {MCU::REC_RDY_6, Channel_1, Cable_1}},
    {buttonMux.pin(6), {MCU::REC_RDY_7, Channel_1, Cable_1}},
    {buttonMux.pin(7), {MCU::REC_RDY_8, Channel_1, Cable_1}},
    {buttonMux.pin(8), {MCU::REC_RDY_1, Channel_1, Cable_2}},
    {buttonMux.pin(9), {MCU::REC_RDY_2, Channel_1, Cable_2}},
    {buttonMux.pin(10), {MCU::REC_RDY_3, Channel_1, Cable_2}},
    {buttonMux.pin(11), {MCU::REC_RDY_4, Channel_1, Cable_2}},
    {buttonMux.pin(12), {MCU::REC_RDY_5, Channel_1, Cable_2}},
    {buttonMux.pin(13), {MCU::REC_RDY_6, Channel_1, Cable_2}},
    {buttonMux.pin(14), {MCU::REC_RDY_7, Channel_1, Cable_2}},
    {buttonMux.pin(15), {MCU::REC_RDY_8, Channel_1, Cable_2}}
};



// Define the transport control buttons
NoteButton transportbuttons[] = {
    {transMux.pin(0), {MCU::REWIND, Channel_1, Cable_1}},
    {transMux.pin(1), {MCU::FAST_FWD, Channel_1, Cable_1}},
    {transMux.pin(2), {MCU::STOP, Channel_1, Cable_1}},
    {transMux.pin(3), {MCU::PLAY, Channel_1, Cable_1}},
    {transMux.pin(4), {MCU::RECORD, Channel_1, Cable_1}}
};

NoteLED leds[] = {
    {max7219.pin(0), {MCU::REC_RDY_1}},
    {max7219.pin(1), {MCU::REC_RDY_2}},
    {max7219.pin(2), {MCU::REC_RDY_3}},
    {max7219.pin(3), {MCU::REC_RDY_4}},
    {max7219.pin(4), {MCU::REC_RDY_5}},
    {max7219.pin(5), {MCU::REC_RDY_6}},
    {max7219.pin(6), {MCU::REC_RDY_7}},
    {max7219.pin(7), {MCU::REC_RDY_8}}
};

// NoteButton button {
//   A4,                             // Push button on pin 5
//   {{MCU::REC_RDY_4}} // Note C4 on MIDI channel 1
// };



void setup() {
  Control_Surface.begin(); // Initialize Control Surface
  Button::setDebounceTime(50);  // Set debounce time to 50 milliseconds
}

void loop() {
  
  Control_Surface.loop(); // Update the Control Surface
  //start of loop Robojax code for LCD with I2C
 
}

I figured out my issue! Love learning my way through this library :slight_smile:

I have a question about button debouncing. Could you walk me through the best way to go about that?

Im having a hard time figuring that part out. I am having some issues on cable 1. I have another sketch working well without the Control Surface Library, so I know that the physical connections should be fine. I just need to be able to adjust the debounce time and threshold with the Control Surface library. I see that things in the documentation, but Im having trouble writing the code to implement them.

Any help would be greatly appreciated!

Are you sure the issue is contact bounce? 50 ms is already quite long.

Button::setDebounceTime(50); is the correct syntax.

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.