How to use multiple Multiplexers with MIDI control surface library?

Hi, I'm trying to use two multiplexers (CD74HC4067) but I don't know how to make the code work. I'm specifically using the MIDI Control surface library with a Teensy 4.1 and two CD74HC4067's. My current sketch is copied from the MIDI Control Surface Library Page, which works fine with one multiplexer, but I don't know how to get two to work. I would like to have multiple analog inputs through one multiplexer to analog pin 0, and then a second set of analog inputs multiplexed to analog pin 1. My understanding is that I can use the same address pins for both multiplexers but I need to use separate input pins. The Teensy 4.1 has enough digital pins spare that I'm happy to use separate address pins if that would make it easier. Any help would be appreciated. This is my first project.
Thanks!

// Include the library
#include <Control_Surface.h>
 
// Instantiate a MIDI Interface to use
USBMIDI_Interface midi;
 
// Instantiate an analog multiplexer
CD74HC4067 mux {
  A0,       // Analog input pin
  {0, 1, 2, 3} // Address pins S0, S1, S2
};
 
// Create an array of potentiometers that send out
// MIDI Control Change messages when you turn the
// potentiometers connected to the eight input pins of
// the multiplexer
CCPotentiometer volumePotentiometers[] {
  {mux.pin(0), {MIDI_CC::Channel_Volume, Channel_1}},
  {mux.pin(1), {MIDI_CC::Channel_Volume, Channel_2}},
  {mux.pin(2), {MIDI_CC::Channel_Volume, Channel_3}},
  {mux.pin(3), {MIDI_CC::Channel_Volume, Channel_4}},
  {mux.pin(4), {MIDI_CC::Channel_Volume, Channel_5}},
  {mux.pin(5), {MIDI_CC::Channel_Volume, Channel_6}},
  {mux.pin(6), {MIDI_CC::Channel_Volume, Channel_7}},
  {mux.pin(7), {MIDI_CC::Channel_Volume, Channel_8}},
  {mux.pin(8), {MIDI_CC::Channel_Volume, Channel_9}},
  {mux.pin(9), {MIDI_CC::Channel_Volume, Channel_10}},
  {mux.pin(10), {MIDI_CC::Channel_Volume, Channel_11}},
  {mux.pin(11), {MIDI_CC::Channel_Volume, Channel_12}},
  {mux.pin(12), {MIDI_CC::Channel_Volume, Channel_13}},
  {mux.pin(13), {MIDI_CC::Channel_Volume, Channel_14}},
  {mux.pin(14), {MIDI_CC::Channel_Volume, Channel_15}},
  {mux.pin(15), {MIDI_CC::Channel_Volume, Channel_16}},
};
 
// Initialize the Control Surface
void setup() {
  Control_Surface.begin();
}
 
// Update the Control Surface
void loop() {
  Control_Surface.loop();
}

I know very little about MIDI Control surface library, so this is a guess:

// Include the library
#include <Control_Surface.h>
 
// Instantiate a MIDI Interface to use
USBMIDI_Interface midi;
 
// Instantiate an analog multiplexer
CD74HC4067 mux {
  A0,       // Analog input pin
  {0, 1, 2, 3} // Address pins S0, S1, S2
};

// Instantiate another analog multiplexer
CD74HC4067 mux2 {
  A1,       // Analog input pin
  {0, 1, 2, 3} // Address pins S0, S1, S2
};
 
// Create an array of potentiometers that send out
// MIDI Control Change messages when you turn the
// potentiometers connected to the eight input pins of
// the multiplexer
CCPotentiometer volumePotentiometers[] {
  {mux.pin(0), {MIDI_CC::Channel_Volume, Channel_1}},
  {mux.pin(1), {MIDI_CC::Channel_Volume, Channel_2}},
  {mux.pin(2), {MIDI_CC::Channel_Volume, Channel_3}},
  {mux.pin(3), {MIDI_CC::Channel_Volume, Channel_4}},
  {mux.pin(4), {MIDI_CC::Channel_Volume, Channel_5}},
  {mux.pin(5), {MIDI_CC::Channel_Volume, Channel_6}},
  {mux.pin(6), {MIDI_CC::Channel_Volume, Channel_7}},
  {mux.pin(7), {MIDI_CC::Channel_Volume, Channel_8}},
  {mux.pin(8), {MIDI_CC::Channel_Volume, Channel_9}},
  {mux.pin(9), {MIDI_CC::Channel_Volume, Channel_10}},
  {mux.pin(10), {MIDI_CC::Channel_Volume, Channel_11}},
  {mux.pin(11), {MIDI_CC::Channel_Volume, Channel_12}},
  {mux.pin(12), {MIDI_CC::Channel_Volume, Channel_13}},
  {mux.pin(13), {MIDI_CC::Channel_Volume, Channel_14}},
  {mux.pin(14), {MIDI_CC::Channel_Volume, Channel_15}},
  {mux.pin(15), {MIDI_CC::Channel_Volume, Channel_16}},
  {mux2.pin(0), {MIDI_CC::Channel_Volume, Channel_17}},
  {mux2.pin(1), {MIDI_CC::Channel_Volume, Channel_18}},
  {mux2.pin(2), {MIDI_CC::Channel_Volume, Channel_19}},
  // And so on...
};
 
// Initialize the Control Surface
void setup() {
  Control_Surface.begin();
}
 
// Update the Control Surface
void loop() {
  Control_Surface.loop();
}

In case of doubt, ask the Author @PieterP

Thanks so much for the reply! Unfortunately this gives the error:
Compilation error: 'Channel_17' was not declared in this scope; did you mean 'DMAChannel_h_'?

When I changed Channel 17 to Channel 1 instead, the second multiplexer does in fact work, so a step in the right direction, but obviously the second multiplexer is then overlapping with the first as they're both controlling the same channels. Thanks for the reply though, it's nice to know that all of the hardware is working now at least. Hopefully someone can help to fix the issue with declaring channels higher than 16

such multplers do use the same (4) lower order address bits, but use the higher order address bits to select individual multiplexers.

the 74hc4067 is analog. A 74138 can use used to decoder higher order address bits resulting in one of its output being LOW and used as the active LOW Enable of a 4067. bear in mind, even the 74138 has an enable bit

Maybe add lines like this at the top of your code (after the #include) ?

constexpr Channel Channel_17 = Channel::createChannel(17);

Because the library code only defines 1 to 16:

That's not possible: MIDI only has 16 channels.

ah I see, thanks for the reply. Any suggestions for how I can get 32 potentiometers to work through two multiplexers? I can see that in the Arduino IDE there's an option to select USB Type: MIDIx4 and MIDIx16. Would this allow me to set up the Teensy 4.1 as two virtual midi devices with 16 channels each? I think I would need to configure it as Cable 1 and Cable 2. But I'd need help with writing the code for this. If that's not how it works though, I'd love to learn. I'm just trying to get 32 analog inputs working.
Any assistance would be greatly appreciated!

Also I realised I might barking up the wrong tree here. Maybe MIDI channels are not the right tool? My goal is to use physical fader pots to control the volume sliders in my DAW. Is there a way to do this that doesn't involve using MIDI channels? I feel like there must be a way to get more than 16 analog inputs to control parameters in the DAW.

Found a solution that seems to work!
I don't know if this is the correct way to do things, but using this code I'm able to get each of the 32 pots to control and individual fader in my DAW, which is exactly what I wanted. If there is a better way of doing this I'm definitely open to suggestions, but this seems to work so far.

Instead of using MIDI Channels, I'm using MCU::VOLUME_1 to 8 across 4 Cables, with the USB Type set to MIDIx4.
I don't really understand why or how this works, but it's a good enough start for me.
My end goal is to connect a total of 96 potentiometers and a handful of rotary encoders to control parameters within my DAW. The reason I want to connect so many potentiometers is that I'm recycling a broken 16ch mixing desk which has over 128 pots. My goal is to be able to use the entire mixing desk as a control surface for my DAW. The 32 pots I've connected so far covers most of the controls on the first 4 channels, which is a good start. I'd love to see how many pots I can get working through the Teensy but I'm just not sure how to make the code work with so many analog inputs, my experience is only in putting together small analog effects, so no knowledge of code at all.

If anyone can help me with how to write the code for 64 or more potentiometers through 4 or more 16ch multiplexers I'd be very grateful.

Anyway, here's the code for anyone who's curious:

// Include the library
#include <Control_Surface.h>
 
// Instantiate a MIDI Interface to use
USBMIDI_Interface midi;
 
// Instantiate an analog multiplexer
CD74HC4067 faderMux {
  A0,       // Analog input pin
  {0, 1, 2, 3} // Address pins S0, S1, S2
};

CD74HC4067 mux2 {
  A1,       // Analog input pin
  {0, 1, 2, 3} // Address pins S0, S1, S2
};
// Create an array of potentiometers that send out
// MIDI Control Change messages when you turn the
// potentiometers connected to the eight input pins of
// the multiplexer
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}},
  {mux2.pin(0), {MCU::VOLUME_1, Cable_3}},
  {mux2.pin(1), {MCU::VOLUME_2, Cable_3}},
  {mux2.pin(2), {MCU::VOLUME_3, Cable_3}},
  {mux2.pin(3), {MCU::VOLUME_4, Cable_3}},
  {mux2.pin(4), {MCU::VOLUME_5, Cable_3}},
  {mux2.pin(5), {MCU::VOLUME_6, Cable_3}},
  {mux2.pin(6), {MCU::VOLUME_7, Cable_3}},
  {mux2.pin(7), {MCU::VOLUME_8, Cable_3}},
  {mux2.pin(8), {MCU::VOLUME_1, Cable_4}},
  {mux2.pin(9), {MCU::VOLUME_2, Cable_4}},
  {mux2.pin(10), {MCU::VOLUME_3, Cable_4}},
  {mux2.pin(11), {MCU::VOLUME_4, Cable_4}},
  {mux2.pin(12), {MCU::VOLUME_5, Cable_4}},
  {mux2.pin(13), {MCU::VOLUME_6, Cable_4}},
  {mux2.pin(14), {MCU::VOLUME_7, Cable_4}},
  {mux2.pin(15), {MCU::VOLUME_8, Cable_4}},
};
 
// Initialize the Control Surface
void setup() {
  Control_Surface.begin();
}
 
// Update the Control Surface
void loop() {
  Control_Surface.loop();
}

If you look at the docs, you'll see that the MCU::VOLUME_X constants are just aliases for Channel_X: Control Surface: MCU Pitch Bend controls

MIDI only has a single Pitch Bend per channel (so 16 in total). With MIDI over USB, you can have up to 16 virtual cables (so 256 in total).
If you need even more, you can use Control Change instead of Pitch Bend: there's 120 different 7-bit controllers per channel, so 1920 per cable (or fewer if you use them as 14-bit controllers).

At a certain point, you'll notice that the time to scan all potentiometers becomes noticeable, even on a powerful Teensy 4. By default, Control Surface starts an ADC conversion on demand: Each PBPotentiometer starts one or more analogReads when it is updated by Control_Surface.loop(). When using a external multiplexer, the first reading is discarded. Additionally, you need some delay between asserting the address lines and the actual readings. Those delays add up.
As an alternative, you'd want to use the ADC continuously (e.g. interrupt driven or free running), scanning all multiplexers in the background.

Nothing like real knobs. Nice project!

a7

Thanks so much for the info! You mention I could use Control Change instead of Pitch Bend, how would I write this in the code? I assume I would still write faderMux.pin(0), but what would follow after that?

Have a look at Control Surface: Getting Started and Control Surface: CCPotentiometer Class Reference.