Controlador MIDI con MUX (Integrado 74HC4067)

Hola a toda la comunidad. Estoy realizando, junto con un colega, un controlador MIDI DIY
El proyecto está casi completo, consta de 12 potenciómetros análogos de 100k conectados a un Multiplexor Analógico 16 canales basado en 74HC4067. Realizamos las debidas conexiones de los potenciómetros al Multiplexor de 16 canales analogicos y de este último a la placa de Arduino UNO, estas últimas conexiones de manera desmontable para la verificación del proyecto y poder saber si la comunicación entre el controlador y la PC es correcta. Utilizamos los programas LoopMIDI y Hairless MIDIserial creando un puerto virtual y hacer la comunicación serial.

Probamos el controlador en Ableton Live 10 y los potes son reconocidos con los respectivos valores del protocolo MIDI (0 a 127), nuestro único inconveniente es que cuando los potenciómetros están enviando información los valores de los mismos empiezan a oscilar por sí solos de -1 a 1 (Ejemplo: Channel 1 = 020, oscila de manera continua a 019, 020, 021, 020, 018, 020, etc.). Después de investigar de varias fuentes, concluimos dos problemas de Hardware posibles:

A) Los potenciómetros son de baja calidad y son sensibles a posibles ruidos eléctricos.

B) La gran cantidad de cables conectados dificulta o genera interferencias en la energía del Arduino.

Comprobamos las conexiones y sus aislamientos entre sí y no parece haber problema en ello.

Una posible solución a esto es modificar el código con un filtro pasa bajos utilizando la técnica de media móvil, este método no fue probado aún. Tanto el código utilizado como el modificado fue realizado por el Chat GPT y cargado desde el programa arduino.

Otra solución, que me recomendo un profesor, es que pruebe primero incluyendo en el codigo un alibreria del MUX 74HC4067 y verificar que el problema no es del Multiplexor y luego incluir libreria <MIDI.h> en el codigo

Quería consultarles que le parece este método, si existe alguno que realmente funciones o recomendar librerias del integrado para incluir al codigo y si nos puedsn brindar algún consejo para que el controlador sea funcional y sin fallas ya que es nuestro primer proyecto con leguaje de programacion Arduino o protocolo MIDI.

A continuación les dejo los links de descarga de los programas que utilizamos, el código original que venimos usando, el codigo con filtro pasa bajos incluido:

Hairless MIDI: https://forum.arduino.cc/t/hairless-midi-download/1136528

LoopMIDI: https://www.tobias-erichsen.de/software/loopmidi.html

Codigo Original:

const int s0Pin = 8;   // Pin S0 del multiplexor conectado al pin digital 8 del Arduino
const int s1Pin = 9;   // Pin S1 del multiplexor conectado al pin digital 9 del Arduino
const int s2Pin = 10;  // Pin S2 del multiplexor conectado al pin digital 10 del Arduino
const int s3Pin = 11;  // Pin S3 del multiplexor conectado al pin digital 11 del Arduino

const int outPin = A0;  // Pin de salida del multiplexor conectado al pin analógico A0 del Arduino
const int numChannels = 12;  // Número de canales/potenciómetros

int potValues[numChannels];  // Array para almacenar los valores de los potenciómetros

void setup() {
  pinMode(s0Pin, OUTPUT);
  pinMode(s1Pin, OUTPUT);
  pinMode(s2Pin, OUTPUT);
  pinMode(s3Pin, OUTPUT);

  // Inicializar los valores de los potenciómetros
  for (int i = 0; i < numChannels; ++i) {
    potValues[i] = 0;
  }

  // Configurar la comunicación serial para MIDI
  Serial.begin(115200);
}

void loop() {
  // Leer el valor de cada potenciómetro
  for (int channel = 0; channel < numChannels; ++channel) {
    selectChannel(channel);
    int value = 127 - (analogRead(outPin) / 8);  // Escalar el valor analógico de 0-1023 a 127-0 (inverso)

    // Verificar si el valor del potenciómetro ha cambiado
    if (value != potValues[channel]) {
      potValues[channel] = value;

      // Enviar mensaje MIDI con el valor del potenciómetro
      sendMidiControlChange(channel, value);
    }

    delay(10);  // Pequeña pausa para evitar envíos demasiado rápidos
  }
}

void selectChannel(int channel) {
  digitalWrite(s0Pin, bitRead(channel, 0));
  digitalWrite(s1Pin, bitRead(channel, 1));
  digitalWrite(s2Pin, bitRead(channel, 2));
  digitalWrite(s3Pin, bitRead(channel, 3));
}

void sendMidiControlChange(int channel, int value) {
  // Enviar mensaje MIDI
  Serial.write(0xB0 | channel);  // Status byte para Control Change
  Serial.write(channel);         // Canal MIDI
  Serial.write(value);           // Valor del Control Change
}

Codigo con Filtro Pasa bajos:

#include <MIDI.h>

const int s0Pin = 8;   // Pin S0 del multiplexor conectado al pin digital 8 del Arduino
const int s1Pin = 9;   // Pin S1 del multiplexor conectado al pin digital 9 del Arduino
const int s2Pin = 10;  // Pin S2 del multiplexor conectado al pin digital 10 del Arduino
const int s3Pin = 11;  // Pin S3 del multiplexor conectado al pin digital 11 del Arduino

const int outPin = A0;  // Pin de salida del multiplexor conectado al pin analógico A0 del Arduino
const int numChannels = 12;  // Número de canales/potenciómetros

const int filterSamples = 5;  // Número de muestras para el filtro de media móvil
int filterBuffer[numChannels][filterSamples];  // Buffer para almacenar las muestras filtradas
int filterIndex = 0;  // Índice actual del buffer de filtrado

MIDI_CREATE_DEFAULT_INSTANCE();

int potValues[numChannels];  // Array para almacenar los valores de los potenciómetros

void selectChannel(int channel) {
  digitalWrite(s0Pin, bitRead(channel, 0));
  digitalWrite(s1Pin, bitRead(channel, 1));
  digitalWrite(s2Pin, bitRead(channel, 2));
  digitalWrite(s3Pin, bitRead(channel, 3));
}

void setup() {
  Serial.begin(31250);
  pinMode(s0Pin, OUTPUT);
  pinMode(s1Pin, OUTPUT);
  pinMode(s2Pin, OUTPUT);
  pinMode(s3Pin, OUTPUT);

  MIDI.begin(MIDI_CHANNEL_OMNI);  // Iniciar la comunicación MIDI

  // Inicializar los valores de los potenciómetros
  for (int i = 0; i < numChannels; ++i) {
    potValues[i] = 0;
    for (int j = 0; j < filterSamples; ++j) {
      filterBuffer[i][j] = 0;
    }
  }
}

void loop() {
  // Leer el valor de cada potenciómetro
  for (int channel = 0; channel < numChannels; ++channel) {
    selectChannel(channel);
    int rawValue = analogRead(outPin);

    // Aplicar el filtro de media móvil
    filterBuffer[channel][filterIndex] = rawValue;
    int filteredValue = 0;
    for (int i = 0; i < filterSamples; ++i) {
      filteredValue += filterBuffer[channel][i];
    }
    filteredValue /= filterSamples;

    // Escalar el valor analógico de 0-1023 a 0-127
    int scaledValue = map(filteredValue, 0, 1023, 0, 127);

    // Verificar si el valor del potenciómetro ha cambiado
    if (scaledValue != potValues[channel]) {
      potValues[channel] = scaledValue;

      // Enviar mensaje MIDI con el valor del potenciómetro
      MIDI.sendControlChange(channel, scaledValue, MIDI_CHANNEL_OMNI);

      // Imprimir el valor del potenciómetro por el puerto serie
      Serial.print("Pot ");
      Serial.print(channel);
      Serial.print(": ");
      Serial.println(scaledValue);
    }
  }

Desde ya, muchas gracias :grin:

Hola!

Perdón por la demora en responder.

El ruido que mencionan es muy comun en los potenciometros, sobre todo cuando no se encuentran montados de una manera firme, y los cables pueden moverse.

Además, se suele recomendar usar potenciómetros de 10k o menos si los vas a conectar de forma directa (el multiplexor no cuenta como algo intermedio) al ADC de la arduino.
Con 100k vas a tener más ruido y las lecturas van a ser menos precisas que con valores menores.
Podés usar 100k si entre el pote y el multiplexor ponés un seguidor de voltaje o un operacional en modo buffer.

image

Esto permite que el ADC vea una resistencia más baja que la del potenciómetro, las lecturas sean más rápidas y más precisas.

Otra opción es hacer una doble lectura con analogRead()

Por ejemplo:

a = analogRead(A1);
a = analogRead(A1);

Esta forma es más lenta, ya que se realizan dos lecturas en vez de una sola, pero el resultado es también una reducción del ruido.

Así y todo, a veces no es posible de librarse por completo del ruido.

En nuestros controladores usamos un filtro de umbral, que detecta si los cambios son repetitivos entre + UMBRAL o - UMBRAL del valor presente (es decir, si está saltando entre dos valores), o si se trata de una variación que se podría considerar genuina.
Para usarlo es necesario ir registrando si los potenciómetros están incrementando o decrementando su valor y los valores previos de cada señal
Si el valor actual no escapa cierto umbral definido (que depende de la resolución analógica utilizada), se considera ruido.

Tengan cuidado con los códigos arrojados por ChatGPT, ya que en algunos casos, y es el caso del segundo codigo que pasaste (con filtro), no son del todo correctos.
En ningun momento incrementa el indice del array del filtro (filterIndex) por lo que siempre escribe la nueva muestra en la posición 0, y el filtro no va a funcionar.
Acá podés ver la implementación de un filtro de media movil.

El ruido en potenciómetros es un problema que siempre está.
Hay que ir probado diversos métodos.

Suerte y ojalá que mi respuesta te ayude a avanzar con el problema.

Cualquier duda acá estamos!