Help me add CD74HC4067 multiplexor code to my midi controller build

This is my code... I'm using Leonardo.

// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Libraries ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

#include <MIDIUSB.h>
#include <ResponsiveAnalogRead.h>

// ~~~~~~~~~~~~~~~~~~~~~~~~~ Pots Variables
const int N_POTS = 4;
int potPin[N_POTS] = { A0, A1, A2, A3 };
int potState[N_POTS] = { 0 };
int potPState[N_POTS] = { 0 };

// Midi Pots Variables
int potCC[N_POTS] = { 11, 12, 13, 14 };
int POT_CH = 0;
int potMidiState[N_POTS] = { 0 };
int potMidiPState[N_POTS] = { 0 };

// Pot Threshold Variables
const byte potThreshold = 3;
const int POT_TIMEOUT = 100;
unsigned long pPotTime[N_POTS] = { 0 };
unsigned long potTimer[N_POTS] = { 0 };

// ResponsiveAnalogRead library Variables
float snapMultiplier = 0.01;
ResponsiveAnalogRead responsivePot[N_POTS] = {};
int potReading[N_POTS] = { 0 };


// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ put your setup code here, to run once: ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void setup() {
  Serial.begin(9600);
  delay(1000);  // Add a delay of 1 second (1000 milliseconds)
  //
  //
  //
  // Pot threshold setup
  for (int i = 0; i < N_POTS; i++) {
    responsivePot[i] = ResponsiveAnalogRead(0, true, snapMultiplier);
    responsivePot[i].setAnalogResolution(1023);  // Sets the resolution
  }
}
//
//
//
//
//
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ put your main code here, to run repeatedly: ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void loop() {

  // Pot loop
  for (int i = 0; i < N_POTS; i++) {

    potReading[i] = analogRead(potPin[i]);
    responsivePot[i].update(potReading[i]);
    potState[i] = responsivePot[i].getValue();
    potMidiState[i] = map(potState[i], 0, 1023, 0, 128);

    int potVar = abs(potState[i] - potPState[i]);

    if (potVar > potThreshold) {
      pPotTime[i] = millis();
    }

    potTimer[i] = millis() - pPotTime[i];

    if (potTimer[i] < POT_TIMEOUT) {
      if (potMidiState[i] != potMidiPState[i]) {
        controlChange(POT_CH, potCC[i], potMidiState[i]);
        MidiUSB.flush();
        //Serial.print("PotState: ");
        //Serial.print(potState[i]);
        //Serial.print(" | potMidiState: ");
        //Serial.println(potMidiState[i]);
        potMidiPState[i] = potMidiState[i];
      }
      potPState[i] = potState[i];
    }
  }
}

// FUNCTIONS for MIDI
void noteOn(byte channel, byte pitch, byte velocity) {
  midiEventPacket_t noteOn = { 0x09, 0x90 | channel, pitch, velocity };
  MidiUSB.sendMIDI(noteOn);
}

/*void noteOff(byte channel, byte pitch, byte velocity) {
  midiEventPatcket_t noteOff = { 0x08, 0x80 | channel, pitch, velocity };
  MidiUSB.sendMIDI(noteOff);
}*/

void controlChange(byte channel, byte control, byte value) {
  midiEventPacket_t event = { 0x0B, 0xB0 | channel, control, value };
  MidiUSB.sendMIDI(event);
}

I want to increase the pots to 12 using this mux.
I was able to do a test of the mux and 12 pots that successfully show changes in my serial.print but I'm unsure how to integrate it into the existing midi code.

// CD74HC4067 Multiplexer control pins
const int S0 = 2;
const int S1 = 3;
const int S2 = 4;
const int S3 = 5;

// Number of channels in the multiplexer
const int NUM_CHANNELS = 12;

void setup() {
  Serial.begin(9600);
  
  // Set control pins as outputs
  pinMode(S0, OUTPUT);
  pinMode(S1, OUTPUT);
  pinMode(S2, OUTPUT);
  pinMode(S3, OUTPUT);
  
  // Initialize multiplexer (all selection pins low)
  digitalWrite(S0, LOW);
  digitalWrite(S1, LOW);
  digitalWrite(S2, LOW);
  digitalWrite(S3, LOW);
}

void loop() {
  for (int channel = 0; channel < NUM_CHANNELS; channel++) {
    int sensorValue = readChannel(channel);
    Serial.print("Channel ");
    Serial.print(channel);
    Serial.print(": ");
    Serial.println(sensorValue);
    delay(100); // Adjust delay as needed
  }
}

// Function to read analog value from specified channel
int readChannel(int channel) {
  // Set selection pins according to channel number
  digitalWrite(S0, channel & 0x01);
  digitalWrite(S1, (channel >> 1) & 0x01);
  digitalWrite(S2, (channel >> 2) & 0x01);
  digitalWrite(S3, (channel >> 3) & 0x01);
  
  // Wait for signals to settle
  delay(10);
  
  // Read analog value from the selected channel
  int sensorValue = analogRead(A0); // Assuming A0 is used to read multiplexer output
  return sensorValue;
}

Don't let being unsure stop you. Try it. If you can't make it work, post your best attempt here and describe what the problem is.

1 Like

This may be useful: Control Surface: Getting Started

make no more sense.

can you post a schematic? or block diagram of your setup?

#include <MIDIUSB.h>
//attention, HC4067 connected to Arduino D2-D5, and output to A0
const byte N_POTS = 12;
const byte potThreshold = 3;
const uint16_t POT_TIMEOUT = 100;
uint16_t potPState[N_POTS] = { 0 };
byte potMidiPState[N_POTS] = { 0 };
unsigned long pPotTime[N_POTS] = { 0 };

void setup() {
  DDRD |= 0xc3;
}

void loop() {
  for (int i = 0; i < N_POTS; i++) {
    byte D = PORTD & 0xc3;
    D |= (i << 2);
    PORTD = D;
    uint16_t potState = analogRead(A0);
    if (abs(potState - potPState[i]) > potThreshold)pPotTime[i] = millis();
    if (millis() - pPotTime[i] < POT_TIMEOUT) {
      byte potMidiState = map(potState, 0, 1023, 0, 127);
      if (potMidiState != potMidiPState[i]) {
        controlChange( i + 11, potMidiState);
        potMidiPState[i] = potMidiState;
      }
      potPState[i] = potState;
    }
  }
}

void controlChange( byte control, byte value) {
  midiEventPacket_t event = { 0x0B, 0xB0, control, value };
  MidiUSB.sendMIDI(event);
  MidiUSB.flush();
}

OK I did it! well chatGPT did it and I sort of understand how lol.

#include <MIDIUSB.h>
#include <ResponsiveAnalogRead.h>

//~~~~~~~~~~~~~~~~~ Multiplexor setup

const int S0 = 2;  // CD74HC4067 Multiplexer control pins
const int S1 = 3;
const int S2 = 4;
const int S3 = 5;

const int N_POTS = 13;  // Number of pots (channels)

// ResponsiveAnalogRead library Variables
float snapMultiplier = 0.01;
ResponsiveAnalogRead responsivePot[N_POTS] = {};

// Midi Pots Variables
int potCC[N_POTS] = { 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23 };  // MIDI control change numbers
int POT_CH = 0;
int potMidiState[N_POTS] = { 0 };
int potMidiPState[N_POTS] = { 0 };
int potState[N_POTS] = { 0 };
int potPState[N_POTS] = { 0 };

// Pot Threshold Variables
const byte potThreshold = 3;
const int POT_TIMEOUT = 100;
unsigned long pPotTime[N_POTS] = { 0 };
unsigned long potTimer[N_POTS] = { 0 };

// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ put your setup code here, to run once: ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void setup() {
  Serial.begin(9600);
  delay(1000);

  // Set control pins as outputs
  pinMode(S0, OUTPUT);
  pinMode(S1, OUTPUT);
  pinMode(S2, OUTPUT);
  pinMode(S3, OUTPUT);

  // Initialize multiplexer (all selection pins low)
  digitalWrite(S0, LOW);
  digitalWrite(S1, LOW);
  digitalWrite(S2, LOW);
  digitalWrite(S3, LOW);

  // Initialize ResponsiveAnalogRead for each pot
  for (int i = 0; i < N_POTS; i++) {
    responsivePot[i] = ResponsiveAnalogRead(0, true, snapMultiplier);
    responsivePot[i].setAnalogResolution(1023);  // Sets the resolution
  }
}

// Function to read analog value from specified channel on the multiplexer
int readMultiplexerChannel(int channel) {
  digitalWrite(S0, channel & 0x01);
  digitalWrite(S1, (channel >> 1) & 0x01);
  digitalWrite(S2, (channel >> 2) & 0x01);
  digitalWrite(S3, (channel >> 3) & 0x01);

  // Wait for signals to settle (adjust as needed)
  delayMicroseconds(10);

  // Read analog value from the selected channel
  int sensorValue = analogRead(A0);  // Assuming A0 is connected to multiplexer output
  return sensorValue;
}

// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ put your main code here, to run repeatedly: ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void loop() {
  // Pot loop
  for (int i = 0; i < N_POTS; i++) {
    int sensorValue = readMultiplexerChannel(i);  // Read from multiplexer channel i
    responsivePot[i].update(sensorValue);
    int potState = responsivePot[i].getValue();
    int potMidiState = map(potState, 0, 1023, 0, 127);  // Map to MIDI range

    int potVar = abs(potState - potPState[i]);

    if (potVar > potThreshold) {
      pPotTime[i] = millis();
    }

    potTimer[i] = millis() - pPotTime[i];

    if (potTimer[i] < POT_TIMEOUT) {
      if (potMidiState != potMidiPState[i]) {
        controlChange(POT_CH, potCC[i], potMidiState);
        MidiUSB.flush();
        
        Serial.print("PotState: ");
        Serial.print(potState);
        Serial.print(" | midiCC: ");
        Serial.print(potCC[i]);
        Serial.print(" | potMidiState: ");
        Serial.println(potMidiState);
        potMidiPState[i] = potMidiState;
        
      }
      potPState[i] = potState;  // Update potPState with current potState
    }
  }
}

// Function to send MIDI Control Change message
void controlChange(byte channel, byte control, byte value) {
  midiEventPacket_t event = { 0x0B, 0xB0 | channel, control, value };
  MidiUSB.sendMIDI(event);
}

From what I can gather, first created the variables for the multiplexor outputs and mapped to digital pins 2-5.

const int S0 = 2; // CD74HC4067 Multiplexer control pins
const int S1 = 3;
const int S2 = 4;
const int S3 = 5;

Then set all 4 of those pins to LOW and also set them as outputs:

// Set control pins as outputs
  pinMode(S0, OUTPUT);
  pinMode(S1, OUTPUT);
  pinMode(S2, OUTPUT);
  pinMode(S3, OUTPUT);

  // Initialize multiplexer (all selection pins low)
  digitalWrite(S0, LOW);
  digitalWrite(S1, LOW);
  digitalWrite(S2, LOW);
  digitalWrite(S3, LOW);

this next section is where I got help from chatGPT because I had no idea what this does and how to write this. I couldn't find any documentation in the datasheet to help me with this:

// Function to read analog value from specified channel on the multiplexer
int readMultiplexerChannel(int channel) {
  digitalWrite(S0, channel & 0x01);
  digitalWrite(S1, (channel >> 1) & 0x01);
  digitalWrite(S2, (channel >> 2) & 0x01);
  digitalWrite(S3, (channel >> 3) & 0x01);

  // Wait for signals to settle (adjust as needed)
  delayMicroseconds(10);

  // Read analog value from the selected channel
  int sensorValue = analogRead(A0);  // Assuming A0 is connected to multiplexer output
  return sensorValue;
}

Then the loop pretty much stayed the same but the top part added the readMultiplexerChannel and sensorValue, which I dont understand yet.

void loop() {
  // Pot loop
  for (int i = 0; i < N_POTS; i++) {
    int sensorValue = readMultiplexerChannel(i);  // Read from multiplexer channel i
    responsivePot[i].update(sensorValue);
    int potState = responsivePot[i].getValue();
    int potMidiState = map(potState, 0, 1023, 0, 127);  // Map to MIDI range

I'd love a few explanations if someone wants to help me understand, what is happening here.

This function is not new, it was in one of your original sketches.

Did you ask the chat bot what it does?