Need Some Help Multiplexing Potentiomters

Hello there! I am new to electronics and Arduino in general, and I wanted to build a MIDI Controller to use with FL Studio and other DAWs. I want to plug in 30 potentiometers (they are the issue), to begin with, and some rotary encoders + buttons. I was stupid enough to buy an Arduino DUE, because it has a lot of ports without me knowing the difference between Analog and Digital Pins, but it kinda worked out for the best after some research. Anyway, I'm struggling to code the Arduino using the 74hc4051 Multiplexers I got after. This is the library I found to be useful and starter friendly, and in an example it mentions that a certain command allows you to implement multiplexers, but I'm not able to understand how.

It would be greatly appreciated if someone could help me! Have a nice day!

The analog multiplexer chips have 8 inputs and one output. A set of three address pins allow you to select which of the 8 inputs is connected to the output. Use three Arduino output pins to control the address pins. Using one chip you can feed any of 8 analog inputs to one Arduino analog input pin. Set the address; read the analog input. Set the address; read the analog input.

The Arduino UNO has 6 analog inputs so you can put a chip on each to read up to 48 analog inputs. You can set all of the multiplexers to the same address so you only need three output pins for all of the multiplexers. Set the address; read the six analog inputs. Set the address; read the six analog inputs. You don't have to use all six. If you only need 30 inputs, use 4 chips (32 inputs) and 4 analog input pins.

johnwasser: The analog multiplexer chips have 8 inputs and one output. A set of three address pins allow you to select which of the 8 inputs is connected to the output. Use three Arduino output pins to control the address pins. Using one chip you can feed any of 8 analog inputs to one Arduino analog input pin. Set the address; read the analog input. Set the address; read the analog input.

The Arduino UNO has 6 analog inputs so you can put a chip on each to read up to 48 analog inputs. You can set all of the multiplexers to the same address so you only need three output pins for all of the multiplexers. Set the address; read the six analog inputs. Set the address; read the six analog inputs. You don't have to use all six. If you only need 30 inputs, use 4 chips (32 inputs) and 4 analog input pins.

I kind of know how a multiplexer works in theory, I need some help implementing it to the example code, if possible

Do a Google search for Multiplexer Shield and use their schematic and software.

I don’t see anything in the midi library that supports multiplexing directly. If I missed it, post a code fragment showing what you found. Use code tags (the <> icon) to post programs so the forum software doesn’t trash the formatting.

Meanwhile, backup a step and address just the multiplexing aspect of this. Build this in phases. Get the analog multiplexer working as single, separate program. Once you understand how the multiplexer works in software, then move on to the midi library.

You can find a multiplexer library here with a built-in examples directory that has a sketch called “mux-pot” that will demo the inner workings. If you don’t know how to download and install from Github, this should get you started.

HTH

/*
This is an example of the "ControlChange" class of the MIDI_controller library.
Connect a fader or a potentiometer to A0. This will be the channel volume of MIDI channel 1.
Map this in your DAW or DJ software.
The ControlChange class allows more control over the actual value that is sent, 
for example, you use inputs other than the analog inputs, like digital sensors,
inputs from multiplexers, etc.
You can also map these values if you need to.
If you are using a Teensy, make sure you have the USB type set to MIDI.
If you are using an Arduino Uno or Mega, use the HIDUINO firmware for the ATmega16U2.
Written by tttapa, 21/08/2015
https://github.com/tttapa/MIDI_controller
*/

#include <MIDI_controller.h> // include the library

const static byte Channel_Volume = 0x7; // controller number 7 is defined as Channel Volume in the MIDI implementation.
const static size_t analogAverage = 8; // Use the average of 8 samples to get smooth transitions and prevent noise
//________________________________________________________________________________________________________________________________

ControlChange fader(Channel_Volume, 1); // Create a new instance of the class 'ControlChange', called 'fader', that sends MIDI messages with controller 7 (channel volume) on channel 1.

//________________________________________________________________________________________________________________________________

void setup(){
  USBMidiController.blink(LED_BUILTIN);  // flash the built-in LED (pin 13 on most boards) on every message
  USBMidiController.setDelay(15);  // wait 15 ms after each message not to flood the connection
  delay(1000); // Wait a second...
  fader.average(analogAverage); // Use the average of 8 samples to get smooth transitions and prevent noise
}

//________________________________________________________________________________________________________________________________

void loop(){
  uint8_t value1 = analogRead(A0)>>3; // map the analog input from a number between 0 and 1023 to a number between 0 and 127
  fader.refresh(value1); // refresh the fader: update with the new value, and send a new MIDI message if it changed
  float value2 = analogRead(A0)/1023.0;
  fader.refresh(value2); // you can also use floats
}

This is the Example I was talking about, but I don’t know if this is a matter of 4 lines of code or messing with the whole code. I will although start with something smaller using the multiplexers and try to send a Midi message

Using a multiplexer would mean calling something other than "analogRead(A0)" to get the data. Perhaps a function you write like:

int analogReadMultiplexed(int address) {
    int multiplexerAddress = address % 8;
    int pinIndex = address/8;
    SetMultiplexerAddressPins(multiplexerAddress);
    return analogRead(AnalogInputPins[pinIndex]);
}

johnwasser: Using a multiplexer would mean calling something other than "analogRead(A0)" to get the data. Perhaps a function you write like:

I'm still a beginner so even this task seems hard to understand. I will try to code it step by step.

Basically a multiplexer is a multiway switch placed in front of the analogue input pin. Using it is simple, say you have an 8 input 1 output multiplexer:-

  1. You connect three output pins to the address select of the multiplexer
  2. You connect the output pin of the multiplexer to one of the analogue input pins.
    Then to use it:-
  3. You write to the output pins a combination of HIGH and LOW to select the multiplexer’s input you want to read. As you have three pins there are 8 different ways HIGH and LOW can be ( because 23 = 8 )
  4. You read the analogue input.
    Repeat 3 and 4 for each different combination of address pin states.

When you want another 8 inputs you do the same thing but connect it to a different analogue input. The three address lines of this second multiplexer can be connected to the same three address select lines as the first multiplexer.

You keep on repeating this, so that where you had n analogue inputs you now have n*8 inputs.

Good luck and keep us posted. :wink:

So, if I manage to store every value that I get from the Multiplexer and then replace A0 with the value, this should work, right?

So, I was trying to experiment with the code, hoping that the code magically works, but I get the error message: too many arguments to function ‘uint32_t analogRead(uint32_t)’.
This is the code I was experimenting with:

#include <MIDI_controller.h>

const static byte Channel_Volume = 0x7;
const static size_t analogAverage = 8;

int pin_Out_S0 = 8;
int pin_Out_S1 = 9;
int pin_Out_S2 = 10;
int pin_In_Mux1 = A0;
int Mux1_State[8] = {0};

Analog fader1(Mux1_State[1], Channel_Volume, 1); // Create a new instance of the class 'Analog, called 'fader1', on pin A0, that sends MIDI messages with controller 7 (channel volume) on channel 1.
Analog fader2(Mux1_State[2], Channel_Volume, 2);
Analog fader3(Mux1_State[3], Channel_Volume, 3);
Analog fader4(Mux1_State[4], Channel_Volume, 4);
Analog fader5(Mux1_State[5], Channel_Volume, 5);
Analog fader6(Mux1_State[6], Channel_Volume, 6);
Analog fader7(Mux1_State[7], Channel_Volume, 7);
Analog fader8(Mux1_State[8], Channel_Volume, 8);




void setup() {
  pinMode(pin_Out_S0, OUTPUT);
  pinMode(pin_Out_S1, OUTPUT);
  pinMode(pin_Out_S2, OUTPUT);
  USBMidiController.blink(LED_BUILTIN);  // flash the built-in LED (pin 13 on most boards) on every message
  USBMidiController.setDelay(15);  // wait 15 ms after each message not to flood the connection
  USBMidiController.begin();  // Initialise the USB MIDI connection
  delay(1000); // Wait a second...
  fader1.average(analogAverage); // Use the average of 8 samples to get smooth transitions and prevent noise
  fader2.average(analogAverage);
}

void loop() {
     for (int i = 0; i < 8; i++){
    analogRead(pin_Out_S0, HIGH && (i & B00000001));
    analogRead(pin_Out_S1, HIGH && (i & B00000010));
    analogRead(pin_Out_S2, HIGH && (i & B00000100));
    Mux1_State[i] = analogRead(pin_In_Mux1);
     }
  
    fader1.refresh(); // refresh the fader (check whether the input has changed since last time, if so, send it over MIDI)
    fader2.refresh();
    fader3.refresh();
    fader4.refresh();
    fader5.refresh();
    fader6.refresh();
    fader7.refresh();
    fader8.refresh();
  }
analogRead(pin_Out_S0, HIGH && (i & B00000001));
    analogRead(pin_Out_S1, HIGH && (i & B00000010));
    analogRead(pin_Out_S2, HIGH && (i & B00000100));

The analogRead here should be digitalWrite.

JonnyBGud: Analog fader1(Mux1_State[1], Channel_Volume, 1); // Create a new instance of the class 'Analog, called 'fader1', on pin A0, that sends MIDI messages with controller 7 (channel volume) on channel 1.

Parameters you pass into the Analog constructor stay the same throughout the entire program, changing the values of Mux1_State won't change that. The first parameter is the analog pin to read from.

If you have an 8-channel multiplexer connected to A0, all 8 faders should read from pin A0:

Analog fader1(A0, Channel_Volume, 1); // Create a new instance of the class 'Analog, called 'fader1', on pin A0, that sends MIDI messages with controller 7 (channel volume) on channel 1.

Then in the loop, you have to select the right Mux channel before refreshing:

void setMuxAddress(uint8_t address) {
  digitalWrite(pin_Out_S0, HIGH && (address & B00000001));
  digitalWrite(pin_Out_S1, HIGH && (address & B00000010));
  digitalWrite(pin_Out_S2, HIGH && (address & B00000100));
}

void loop() {
  setMuxAddress(0);
  fader1.refresh();
  setMuxAddress(1);
  fader2.refresh();
  setMuxAddress(2);
  fader3.refresh();
  // ...
  setMuxAddress(7);
  fader8.refresh();
}

Maybe take a look at the Analog_array_example. That's going to be much cleaner than having variables fader1 through fader30.

Pieter

Here’s an example for analog multiplexers with the MIDI controller library. I don’t have any multiplexers on hand, so I haven’t been able to test it.

Please let me know if it works, so I can add it to the library.

/*
  This is an example of the "Analog" class of the MIDI_controller library.
  Connect faders or potentiometers to the inputs of analog multiplexers, 
  and connect the outputs of the multiplexers to analog inputs on the Arduino
  (A0 and A1 in this case).

  Connect the address lines of the multiplexers together, and connect them 
  to digital outputs on the Arduino (pins 2, 3 and 4 in this case).

  Change the number of multiplexers, the number of address lines, the 
  analog inputs and the address select pins to fit your hardware.
  
  Upload the sketch, and map the faders in your DAW or DJ software.

  If you are using a Teensy, make sure you have the USB type set to MIDI.
  If you are using an Arduino Uno or Mega, use the HIDUINO firmware for the ATmega16U2.


  Written by tttapa, 04-7-2017
  https://github.com/tttapa/MIDI_controller
*/

#include <MIDI_controller.h> // include the library

const static size_t analogAverage = 8; // Use the average of 8 samples to get smooth transitions and prevent noise

const static uint8_t numberOfMultiplexers = 2; // Total number of multiplexers connected to the Arduino
const static uint8_t addressWidth = 3; // Number of address lines of the multiplexers

const static uint8_t addressLines[addressWidth] = {2, 3, 4}; // The pins connected to the address lines of the multiplexers
const static uint8_t analogInputs[numberOfMultiplexers] = {A0, A1}; // The pins connected to the outputs of the multiplexers


//________________________________________________________________________________________________________________________________

const static uint8_t numberOfInputsPerMultiplexer = 1 << addressWidth; // Number of inputs of each multiplexer

const static size_t totalNumberOfAnalogInputs = numberOfMultiplexers * numberOfInputsPerMultiplexer; // the total number of potentiometers

//________________________________________________________________________________________________________________________________

Analog* potentiometers[totalNumberOfAnalogInputs]; // create an empty array of pointers to Analog objects

//________________________________________________________________________________________________________________________________

void setup() {
  USBMidiController.blink(LED_BUILTIN);  // flash the built-in LED (pin 13 on most boards) on every message
  USBMidiController.setDelay(15);  // wait 15 ms after each message not to flood the connection
  USBMidiController.begin();  // Initialise the USB MIDI connection
  delay(1000); // Wait a second...
  for (unsigned int i = 0; i < totalNumberOfAnalogInputs; i++) {
    potentiometers[i] = new Analog(analogInputs[i % numberOfMultiplexers], i, 1); // create a new Analog object and store it in the array
    potentiometers[i]->average(analogAverage); // Use the average of 8 samples to get smooth transitions and prevent noise
  }
  for (unsigned int i = 0; i < addressWidth; i++) {
    pinMode(addressLines[i], OUTPUT); // set all address line pins as outputs
  }
}

//________________________________________________________________________________________________________________________________

void loop() {
  for (unsigned int i = 0; i < numberOfInputsPerMultiplexer; i++) { // refresh all Analog inputs
    setMuxAddress(i);
    for (unsigned int j = 0; j < numberOfMultiplexers; j++) {
      potentiometers[i * numberOfMultiplexers + j]->refresh();
    }
  }
}

void setMuxAddress(unsigned int address) {
  for (unsigned int i = 0; i < addressWidth; i++) {
    digitalWrite(addressLines[i], address & (1 << i));
  }
}

Or if you prefer to use ControlChange class you mentioned earlier, and the function Johnwasser proposed (it’s a bit easier to see what’s going on, without too much modulos and nested loops):

/*
  This is an example of the "ControlChange" class of the MIDI_controller library.
  Connect faders or potentiometers to the inputs of analog multiplexers,
  and connect the outputs of the multiplexers to analog inputs on the Arduino
  (A0 and A1 in this case).

  Connect the address lines of the different multiplexers together, and connect 
  them to digital outputs on the Arduino (pins 2, 3 and 4 in this case).

  Change the number of multiplexers, the number of address lines, the
  analog inputs and the address select pins to fit your hardware.

  Upload the sketch, and map the faders in your DAW or DJ software.

  If you are using a Teensy, make sure you have the USB type set to MIDI.
  If you are using an Arduino Uno or Mega, use the HIDUINO firmware for the ATmega16U2.


  Written by tttapa, 04-7-2017
  https://github.com/tttapa/MIDI_controller
*/

#include <MIDI_controller.h> // include the library

const static size_t analogAverage = 8; // Use the average of 8 samples to get smooth transitions and prevent noise

const static uint8_t numberOfMultiplexers = 2; // Total number of multiplexers connected to the Arduino
const static uint8_t addressWidth = 3; // Number of address lines of the multiplexers

const static uint8_t addressLines[addressWidth] = {2, 3, 4}; // The pins connected to the address lines of the multiplexers
const static uint8_t analogInputs[numberOfMultiplexers] = {A0, A1}; // The pins connected to the outputs of the multiplexers

//________________________________________________________________________________________________________________________________

const static uint8_t numberOfInputsPerMultiplexer = 1 << addressWidth; // Number of inputs of each multiplexer

const static size_t totalNumberOfAnalogInputs = numberOfMultiplexers * numberOfInputsPerMultiplexer; // the total number of potentiometers

//________________________________________________________________________________________________________________________________

ControlChange* potentiometers[totalNumberOfAnalogInputs]; // create an empty array of pointers to Analog objects

//________________________________________________________________________________________________________________________________

void setup() {
  USBMidiController.blink(LED_BUILTIN);  // flash the built-in LED (pin 13 on most boards) on every message
  USBMidiController.setDelay(15);  // wait 15 ms after each message not to flood the connection
  USBMidiController.begin();  // Initialise the USB MIDI connection
  delay(1000); // Wait a second...
  for (unsigned int i = 0; i < totalNumberOfAnalogInputs; i++) {
    potentiometers[i] = new ControlChange(i, 1); // create a new ControlChange object and store it in the array
    potentiometers[i]->average(analogAverage); // Use the average of 8 samples to get smooth transitions and prevent noise
  }
  for (unsigned int i = 0; i < addressWidth; i++) {
    pinMode(addressLines[i], OUTPUT); // set all address line pins as outputs
  }
}

//________________________________________________________________________________________________________________________________

void loop() {
  for (unsigned int i = 0; i < totalNumberOfAnalogInputs; i++) { // refresh all Analog inputs
    uint8_t value = analogReadMux(i) >> 3; // convert 10-bit analog value to 7-bit MIDI value (10-7 = 3)
    potentiometers[i]->refresh(value);
  }
}

void setMuxAddress(unsigned int address) {
  for (unsigned int i = 0; i < addressWidth; i++) {
    digitalWrite(addressLines[i], address & (1 << i));
  }
}

int analogReadMux(unsigned int muxPin) {
  int address = muxPin % numberOfInputsPerMultiplexer; // the input of the multiplexer
  int analogPinIndex = muxPin / numberOfInputsPerMultiplexer; // the index of the multiplexer
  setMuxAddress(address); // select the right input of the multiplexer
  return analogRead(analogInputs[analogPinIndex]); // read the output of the right multiplexer
}

Pieter

I tried the 2 codes above, but I have a problem mapping with the DAW. The channels go crazy

What do you mean "The channels go crazy"?

If you replace USBMidiController.begin(); with USBMidiController.begin(115200, true);, it'll print debug messages to the Serial Monitor (set it to 115200 baud instead of 9600).

What kind of messages do you see?

Did you connect all potentiometers, or are some of the inputs floating?

Pieter

I tried all of this, but all the numbers I get seems to be wrong. Am I supposed to change anything at Analog* potentiometers[totalNumberOfAnalogInputs]; // create an empty array of pointers to Analog objects or not?

JonnyBGud: I tried all of this, but all the numbers I get seems to be wrong.

Please post some of the debug messages. What makes you think that they are wrong?

Have you tried the "normal" Analog examples, without the multiplexers? Did those work? Does analogRead() on a Due return a 10-bit int or a 12-bit int? I haven't actually tried any of the code on a Due, so it's not impossible that you've hit a bug ;)

You could try to increase the average buffer length.

JonnyBGud: Am I supposed to change anything at Analog* potentiometers[totalNumberOfAnalogInputs]; // create an empty array of pointers to Analog objects or not?

No, you don't have to change that.

Pieter

Sorry for taking ages to reply. After doing some work with the wires, I spotted some mistakes that I made, and after making sure that every connection was right. Then I tested both of the codes that you provided and both of them worked fine, but I only used one Multiplexer, because it's tough for me to test the second one only using one breadboard. Anyway, when I finally manage to test the second one, I will send you a PM to inform you, so that you add it to your code on GitHub

I would like to thank everyone for their time, have a nice day! :)