Using the HC4067 multiplexer with potentiometers

Good morning everyone

I am trying to add 16 potentiometers with a HC4067 to a controller where I have two multiplexers with buttons and some encoders working without problem
The goal is to read each potentiometer independently, send a midi cc message, only when the potentiometer moves

i tried to make the code from scratch (first code)
and use a template (second code)
but i couldn't get it to work

I've been trying for a couple of days, I know how to program everything separately and put it in a "for" loop to the arduino, but with the multiplexer I'm making a mess

#include <MIDI.h>

const int muxSIG3 = A3;
const int muxS03 = 37;
const int muxS13 = 35;
const int muxS23 = 33;
const int muxS33 = 31;

const int entradasmplex3 = 16;
int mux3[entradasmplex3] = {0};
int vpmux3[entradasmplex3] = {0};


const int pinPots[3] = {A0 , A1 , A2};

int VAPot[3] = {0}; //current value pot
int VPPot[3] = {0};// previous value pot 
byte VAMidi[3] = {0}; //current value midi
byte VPMidi[3] = {0}; //previous value midi
byte VarPot = 0; 

int cc = 0;




MIDI_CREATE_DEFAULT_INSTANCE();


void SetMuxChannel3(byte channel)
{
  digitalWrite(muxS03, bitRead(channel, 0));
  digitalWrite(muxS13, bitRead(channel, 1));
  digitalWrite(muxS23, bitRead(channel, 2));
  digitalWrite(muxS33, bitRead(channel, 3));
}
void setup()
{
  
  pinMode(muxS03 , OUTPUT);
  pinMode(muxS13, OUTPUT);
  pinMode(muxS23 , OUTPUT);
  pinMode(muxS33 , OUTPUT);
  pinMode(muxSIG3 , INPUT);
  Serial.begin(57600);
  delay(1000);
}

void loop()
{
  for (int i = 0 ; i < 3 ; i++ )
  {
    VAPot[i]  = analogRead (pinPots[i]);
    VAMidi[i] = map(VAPot[i] , 0 , 1023 , 0 , 127);
    VarPot = abs(VPPot[i] - VAPot[i]);
    if (VarPot > 2)
    {
      if (VAMidi[i] != VPMidi[i])
      {
        MIDI.sendControlChange( cc + i , VAMidi[i] , 1 );
        VPPot[i] = VAPot[i];
        VPMidi[i] = VAMidi[i];
      }
    }

  }
}```

the template

#include <MIDI.h>
#include <Multiplexer4067.h>
MIDI_CREATE_DEFAULT_INSTANCE();
#define N_MUX 1
#define s0 37
#define s1 35
#define s2 33
#define s3 31
#define x1 A3
// Initializes the multiplexer
Multiplexer4067 mux[N_MUX] = {
  Multiplexer4067(s0, s1, s2, s3, x1),
};
const int N_POTS = 0 + 16 + 0; //* total numbers of pots (slide & rotary). Number of pots in the Arduino + number of pots on multiplexer 1 + number of pots on multiplexer 2...
const int N_POTS_ARDUINO = 0; //* number of pots connected straight to the Arduino
const int POT_ARDUINO_PIN[N_POTS_ARDUINO] = {}; //* pins of each pot connected straight to the Arduino
const int N_POTS_PER_MUX[N_MUX] = {16}; //* number of pots in each multiplexer (in order)
const int POT_MUX_PIN[N_MUX][16] = {{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14}}; //* pins of each pot of each mux in the order you want them to be};
int potCState[N_POTS] = {0}; // Current state of the pot
int potPState[N_POTS] = {0}; // Previous state of the pot
int potVar = 0; // Difference between the current and previous state of the pot

int potMidiCState[N_POTS] = {0}; // Current state of the midi value
int potMidiPState[N_POTS] = {0}; // Previous state of the midi value

const int TIMEOUT = 300; //* Amount of time the potentiometer will be read after it exceeds the varThreshold
const int varThreshold = 10; //* Threshold for the potentiometer signal variation
boolean potMoving = true; // If the potentiometer is moving
unsigned long PTime[N_POTS] = {0}; // Previously stored time
unsigned long timer[N_POTS] = {0}; // Stores the time that has elapsed since the timer was reset



void setup() {

  pinMode(s0 , OUTPUT);
  pinMode(s1 , OUTPUT);
  pinMode(s2 , OUTPUT);
  pinMode(s3 , OUTPUT);
  pinMode(x1 , INPUT);


  Serial.begin(57600);
}
void loop() {

  for (int i = 0; i < 16; i++) { // Loops through all the potentiometers

    potMidiCState[i] = map(potCState[i], 0, 1023, 0, 127); // Maps the reading of the potCState to a value usable in midi

    potVar = abs(potCState[i] - potPState[i]); // Calculates the absolute value between the difference between the current and previous state of the pot

    if (potVar > varThreshold) { // Opens the gate if the potentiometer variation is greater than the threshold
      PTime[i] = millis(); // Stores the previous time
    }

    timer[i] = millis() - PTime[i]; // Resets the timer 11000 - 11000 = 0ms

    if (timer[i] < TIMEOUT) { // If the timer is less than the maximum allowed time it means that the potentiometer is still moving
      potMoving = true;

    }
    else {
      potMoving = false;
    }

    if (potMoving == true) { // If the potentiometer is still moving, send the change control
      MIDI.sendControlChange (i , potMidiCState , 1 );
      if (potMidiPState[i] != potMidiCState[i]) {


      }
    }
  }



  int nPotsPerMuxSum = N_POTS_ARDUINO; //offsets the buttonCState at every mux reading

  for (int j = 0; j < N_MUX; j++) {
    for (int i = 0; i < N_POTS_PER_MUX[j]; i++) {
      potCState[i + nPotsPerMuxSum] = mux[j].readChannel(POT_MUX_PIN[j][i]);
    }
    nPotsPerMuxSum += N_POTS_PER_MUX[j];
  }


}

I am unfamiliar with MIDI programming, but saw your "template" has a conditional test with no action...

    if (potMoving == true) { // If the potentiometer is still moving, send the change control
      MIDI.sendControlChange (i , potMidiCState , 1 );
      if (potMidiPState[i] != potMidiCState[i]) {

// nothing happens if "P" and "C" are not equal
      }

for reference:

I like your first code better. The second code seems far more complex and confusing for such a simple task.

What I noticed about your first code was

  1. It only seems to be attempting to make 4 readings, not 16. Is that deliberate, for testing purposes?
  2. The function SetMuxChannel3() is defined but not used, so the multiplexer channel never gets updated. Why does this function have "3" in it's name?
  3. What is the pinPots[] array for?

Thanks for answering both of you.
I would like to keep my code, it seems simpler and more readable
we go by steps

1-that value is 4 because at that moment I only had 4 potentiometers connected

2-I just edited the code adding it, it still doesn't work
It has a 3 in the name because in the complete project I am already using 2 multiplexers

3-deleted , it is from another part of the complete code , it has no use here

#include <MIDI.h>

const int muxSIG3 = A3;
const int muxS03 = 37;
const int muxS13 = 35;
const int muxS23 = 33;
const int muxS33 = 31;

const int entradasmplex3 = 16;
int VAPot[3] = {0}; //current value pot
int VPPot[3] = {0};// previous value pot
byte VAMidi[3] = {0}; //current value midi
byte VPMidi[3] = {0}; //previous value midi
byte VarPot = 0;

MIDI_CREATE_DEFAULT_INSTANCE();


void SetMuxChannel3(byte channel)
{
  digitalWrite(muxS03, bitRead(channel, 0));
  digitalWrite(muxS13, bitRead(channel, 1));
  digitalWrite(muxS23, bitRead(channel, 2));
  digitalWrite(muxS33, bitRead(channel, 3));
}
void setup()
{

  pinMode(muxS03 , OUTPUT);
  pinMode(muxS13, OUTPUT);
  pinMode(muxS23 , OUTPUT);
  pinMode(muxS33 , OUTPUT);
  pinMode(muxSIG3 , INPUT);
  Serial.begin(57600);
  delay(1000);
}

void loop()
{
  for (int i = 0 ; i < entradasmplex3 ; i++ )
  {
    SetMuxChannel3(i);
    VAPot[i]  = analogRead (muxSIG3);
    VAMidi[i] = map(VAPot[i] , 0 , 1023 , 0 , 127);
    VarPot = abs(VPPot[i] - VAPot[i]);
    if (VarPot > 2)
    {
      if (VAMidi[i] != VPMidi[i])
      {
        MIDI.sendControlChange( 0 + i , VAMidi[i] , 1 );
        VPMidi[i] = VAMidi[i];
        VPPot[i] = VAPot[i];
      }
    }
  }
}

This is what I receive for hairless midi, without touching any potentiometer

You are not giving us an actual problem description. What happens?

The problem is that I need to read the potentiometers connected to the multiplexer independently, only when they move and that each send a midi cc message

What happens now is that they send midi data through all the channels consecutively, without touching any potentiometer and with values ​​that do not correspond to the position of the potentiometer

Do you use state change detection on the pots, to determine if their position has actually changed?

if abs( new reading - old reading) > threshold
{
old reading = new reading
report new reading
}

that's what i tried

int VAPot[3] = {0}; //current value pot
int VPPot[3] = {0};// previous value pot 
byte VAMidi[3] = {0}; //current value midi
byte VPMidi[3] = {0}; //previous value midi
byte VarPot = 0; 
VAPot[i]  = analogRead (muxSIG3);
    VAMidi[i] = map(VAPot[i] , 0 , 1023 , 0 , 127);
    VarPot = abs(VPPot[i] - VAPot[i]);
    if (VarPot > 2)
    {
      if (VAMidi[i] != VPMidi[i])

this is mi code for buttons and works perfectly in the multiplexer
vpmux2 and vmux2 correspond to previous value and current value

 for (int a = 0; a <= 15; a++) {

    SetMux2Channel(a);

    mux2[a] = digitalRead(mux2SIG);
    if ((millis() - lastdebouncetime[a]) > debouncedelay)
    {
      if (vpmux2[a] != mux2[a])
      {
        lastdebouncetime[a] = millis();

        if (mux2[a] == LOW)
        {
          MIDI.sendControlChange( a , 127, 1);
        }
        else {
          MIDI.sendControlChange ( a , 0, 1); 
        }
        vpmux2[a] = mux2[a];
      }
    }
  }

Try ditching all the midi code and just try to make the pots work first. Write a simple test sketch that only does that, and just reports changes and values to serial output.

Since you report that your button version works, it should be easy to integrate the pots after you make them work.

By the way, I can't resist a design suggestion. You detect the dead zone after scaling. That will work better if you do it before scaling (before using map() ).

1 Like

I just quickly programmed the part of the potentiometers, but connected to the arduino directly, without using the multiplexer and it works perfect, this is what I want, but with the mux

`#include <MIDI.h>
const int pinPots[3] = {A0 , A1 , A2};
int VAPot[3] = {0}; //current value pot
int VPPot[3] = {0}; //previous value pot
byte VAMidi[3] = {0}; //current value midi
byte VPMidi[3] = {0}; //previous value midi
byte VarPot = 0;

MIDI_CREATE_DEFAULT_INSTANCE();

void setup()
{
  Serial.begin(57600);
}

void loop()
{
  for (int i = 0 ; i < 3 ; i++ )
  {
    VAPot[i]  = analogRead (pinPots[i]);
    VAMidi[i] = map(VAPot[i] , 0 , 1023 , 0 , 127);
    VarPot = abs(VPPot[i] - VAPot[i]);
    if (VarPot > 2)
    {
      if (VAMidi[i] != VPMidi[i])
      {
        MIDI.sendControlChange( 0 + i , VAMidi[i] , 1 );
        VPPot[i] = VAPot[i];
        VPMidi[i] = VAMidi[i];
      }
    }

  }
}`

If you are having trouble with the mux code, then the simple example code you need is mux code. But that muxes the pots. So actually, to create working mux code, I will repeat again, make a test sketch that ONLY reads the pots. Using the mux, not just reading different analog pins.

I.E. if your difficulty is in the mux, no code that has no mux code in it, can be of any possible use in writing the code. If you are presenting code and hoping someone will pencil in the mux code for you, forget about it.

Also, not all mux circuits are the same. So please play by the forum guidelines, and post a schematic.

I just did this code, it reads the potentiometers without problem

#include <MIDI.h>

const int muxSIG3 = A3;
const int muxS03 = 37;
const int muxS13 = 35;
const int muxS23 = 33;
const int muxS33 = 31;
const int entradasmplex3 = 5;
int VAPot = 0; //current value pot
byte VAMidi = 0; //current value midi
MIDI_CREATE_DEFAULT_INSTANCE();

void SetMuxChannel3(byte channel)
{
  digitalWrite(muxS03, bitRead(channel, 0));
  digitalWrite(muxS13, bitRead(channel, 1));
  digitalWrite(muxS23, bitRead(channel, 2));
  digitalWrite(muxS33, bitRead(channel, 3));
}
void setup()
{

  pinMode(muxS03 , OUTPUT);
  pinMode(muxS13, OUTPUT);
  pinMode(muxS23 , OUTPUT);
  pinMode(muxS33 , OUTPUT);
  pinMode(muxSIG3 , INPUT);
  Serial.begin(9600);
  delay(1000);
}

void loop()
{
  for (int i = 0 ; i < 5 ; i++ )
  {
    SetMuxChannel3(i);
    VAPot  = analogRead (muxSIG3);
    VAMidi = map(VAPot , 0 , 1023 , 0 , 127);

    //MIDI.sendControlChange( 0 + i , VAMidi , 1 );
    Serial.print(i);
    Serial.print(".........");
    Serial.println(VAMidi);
  }
}

If you are presenting code and hoping someone will pencil in the mux code for you, forget about it.

Do you think that's what I'm doing?
I assure you, that coming to ask other people has been the last resort, and between answers I am here trying to make it work

It was hard to determine what you are doing, because you didn't say. I made that statement because you didn't follow my suggestion previously. But at least now, you have two working codes that you can integrate. Please make an attempt at that, and post the results if it still has problems.

Fixed, it was a silly error, the value of the array (VAPot, VPPot, VAMidi, VPMidi) did not match the value of "entradasmplex" ,
I had set the focus convinced that the error was from the "for loop" and even knowing that this error existed, I thought that I had already tried to correct it and it still did not work, so I ruled it out as the reason for the failure
For those who read this and are starting out like me, don't take things for granted, check everything

#include <MIDI.h>
 
const int muxSIG3 = A3;
const int muxS03 = 37;
const int muxS13 = 35;
const int muxS23 = 33;
const int muxS33 = 31;
 
const int entradasmplex3 = 15;
int VAPot[15] = {0}; //current value pot
int VPPot[15] = {0};// previous value pot
byte VAMidi[15] = {0}; //current value midi
byte VPMidi[15] = {0}; //previous value midi
byte VarPot = 0;
 
MIDI_CREATE_DEFAULT_INSTANCE();
 
 
void SetMuxChannel3(byte channel)
{
  digitalWrite(muxS03, bitRead(channel, 0));
  digitalWrite(muxS13, bitRead(channel, 1));
  digitalWrite(muxS23, bitRead(channel, 2));
  digitalWrite(muxS33, bitRead(channel, 3));
}
void setup()
{
 
  pinMode(muxS03 , OUTPUT);
  pinMode(muxS13, OUTPUT);
  pinMode(muxS23 , OUTPUT);
  pinMode(muxS33 , OUTPUT);
  pinMode(muxSIG3 , INPUT);
  Serial.begin(31250);
  delay(1000);
}
 
void loop()
{
  for (int i = 0 ; i < entradasmplex3 ; i++ )
  {
    SetMuxChannel3(i);
    VAPot[i]  = analogRead (muxSIG3);
    VAMidi[i] = map(VAPot[i] , 0 , 1023 , 0 , 127);
    VarPot = abs(VPPot[i] - VAPot[i]);
    if (VarPot > 2)
    {
      if (VAMidi[i] != VPMidi[i])
      {
        MIDI.sendControlChange( 0 + i , VAMidi[i] , 2 );
       // Serial.print("channel........." );
        // Serial.print(i);
         // Serial.print("value........." );
          // Serial.println(VAMidi[i] );
        VPMidi[i] = VAMidi[i];
        VPPot[i] = VAPot[i];
      }
    }
  }
}

A small suggestion to simplify your code:

#include <MIDI.h>
 
const int muxSIG3 = A3;
const int muxS3[4] = {37, 35, 33, 31};
 
const int entradasmplex3 = 15;
int VAPot[15] = {0}; //current value pot
int VPPot[15] = {0};// previous value pot
byte VAMidi[15] = {0}; //current value midi
byte VPMidi[15] = {0}; //previous value midi
byte VarPot = 0;
 
MIDI_CREATE_DEFAULT_INSTANCE();
 
 
void SetMuxChannel3(byte channel)
{
  for (byte b=0; b<4; b++)
    digitalWrite(muxS3[b], bitRead(channel, b));
}
void setup()
{
 
  for (byte b=0; b<4; b++)
    pinMode(muxS3[b] , OUTPUT);
  pinMode(muxSIG3 , INPUT);
  Serial.begin(31250);
  delay(1000);
}
 
void loop()
{
  for (int i = 0 ; i < entradasmplex3 ; i++ )
  {
    SetMuxChannel3(i);
    VAPot[i]  = analogRead (muxSIG3);
    VAMidi[i] = map(VAPot[i] , 0 , 1023 , 0 , 127);
    VarPot = abs(VPPot[i] - VAPot[i]);
    if (VarPot > 2)
    {
      if (VAMidi[i] != VPMidi[i])
      {
        MIDI.sendControlChange( 0 + i , VAMidi[i] , 2 );
       // Serial.print("channel........." );
        // Serial.print(i);
         // Serial.print("value........." );
          // Serial.println(VAMidi[i] );
        VPMidi[i] = VAMidi[i];
        VPPot[i] = VAPot[i];
      }
    }
  }
}

What is the purpose of the other 2 multiplexers? More pots? Do you need independent control of the channel selection of each multiplexer for any reason? If not, do you know that you can use the same 4 pins to control all 3 multiplexers?

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