Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

possibility of motorized faders #5

Closed
oscarosky opened this issue Apr 27, 2018 · 9 comments
Closed

possibility of motorized faders #5

oscarosky opened this issue Apr 27, 2018 · 9 comments

Comments

@oscarosky
Copy link

Could it include the reading of messages (MIDI_Input "PitchBend") to manipulate motorized faders?

@tttapa
Copy link
Owner

tttapa commented Apr 27, 2018

Yes, but it requires specialized timing, and complicated control systems. That's why I didn't include it with the library (yet).

@oscarosky
Copy link
Author

It would be great! I think it's very good what you've written so far ..

@oscarosky oscarosky reopened this Jun 6, 2018
@oscarosky
Copy link
Author

oscarosky commented Jun 6, 2018

I am writing a code to incorporate the motorized faders and a bridge of meters powered by the Arduino PWM outputs. I have modified a code written in 2012 by Cody Hazelwood and updated in 2016 by Guerau Pasola and Adrian Salvador. But the original code only controlled one fader.
Everything seems to work correctly although not fluid enough as it should. :(

Your library seems very good to me and I would like to use it for the rest of the controls, but I have not been here for a long time and I do not know how to do it.
I thought that when you have time maybe you can help me solve this.

Thank you.

#include <CapacitiveSensor.h>                  // Biblioteca para la sensibilidad táctil del fader
#include <MIDI.h>                                     // Biblioteca para recibir mensajes MIDI
MIDI_CREATE_DEFAULT_INSTANCE();
#define PitchBend 0xE0                             // Byte de estado para control de faders
#define AfterTouchChannel 0xD0              // Byte de estado para control de medidores
#define N 4                                               // Especifica aquí el numero de faders (max.8)
 
// Asignación de Pins en Arduino (MEGA)

const int motorUp[] = {23, 25, 27, 29, 31, 33, 35, 37};        // Controla H-Bridge para hacer que el motor ascienda
const int motorDown[] = {22, 24, 26, 28, 30, 32, 34, 36};      // Controla H-Bridge para hacer que el motor descienda
const int meterPin [] = {2, 3, 4, 5, 6, 7, 8, 9};              // pines PWM que se van a utilizar para los medidores

// Entradas 

const int fader[] = {0, 1, 2, 3, 4, 5, 6, 7}; // Pin de entrada analogica del fader relativo a GND
const int touchSend = 10;                     // Pin de envio para circuito de detección de capacitancia
const int touchReceive = 11;                  // Pin de recepción para circuito de detección de capacitancia

// Variables

double faderMax[N];                            // Valor leído por la posición máxima del fader (0-1023)
double faderMin[N];                            // Valor leído por la posición mínima del fader (0-1023)
double posicion[N];
int faderChannel[N];                           // Canal MIDI del fader (desde 1-8)
int returnValue[N];

int meterChannel  = 0 ;                        // Canal MIDI del medidor
int valueAT = 0;                               // Valor de lectura desde DAW
int valueMeter = 0;                            // Valor de salida para PWM

bool touched = false;
bool positionUpdated[N];

CapacitiveSensor touchLine = CapacitiveSensor(touchSend, touchReceive);

void setup() {  
  MIDI.begin(MIDI_CHANNEL_OMNI);               // Recibe mensajes en todos los canales MIDI
//  Serial.begin(115200);                        // habilitar esta linea para testear a traves de Hairless MIDI
 
  for (int i = 0; i < N; i++) {
    pinMode (motorUp[i], OUTPUT);
    pinMode (motorDown[i], OUTPUT); 
    faderMax[i] = 0 ;
    faderMin[i] = 0 ;
    posicion[i] = 0 ;
    faderChannel[i] = (i+1);
    positionUpdated[i] = false ;
    analogWrite (meterPin[i], valueMeter);     // Puesta a cero de medidores
  }
   calibrateFader();  
}

void loop() { 

  for (int i = 0; i < N; i++) {

    /* Si hay un mensaje MIDI en espera, y corresponde al actualmente seleccionado
      fader, y es un mensaje PitchBend (usado para controlar el fader), luego convierte
      el valor PitchBend y actualizar la posición actual del fader. */
    if (MIDI.read() && MIDI.getChannel() == faderChannel[i] && MIDI.getType() == PitchBend ) {

      /* Mapeo bit a bit para tomar dos valores de 7 bits para PitchBend y convertir a 
        un solo valor de 14 bits. Luego lo convierte en valor entre 0 y 1023
        para controlar el fader */
      int value = (((MIDI.getData2() << 7) + MIDI.getData1()) * 0.0625);
      updateFader(value, i);
      }
   
  checkTouch(i);                        //Comprueba si el fader está siendo tocado  
 
    if (!positionUpdated[i]) {          //Si se ha tocado el fader, necesita actualizar la posición en el DAW
      updateFaderMidi(i);      
      positionUpdated[i] = true;
    }

    if (MIDI.read() && MIDI.getType() == AfterTouchChannel && ((MIDI.getData1() & 0x70) >> 4) == i) {
    // meterChannel = (MIDI.getData1() & 0x70) >> 4;
    valueAT = MIDI.getData1() & 0x0F;                     // Extrae el valor necesario del mensaje MIDI
    valueMeter = map(valueAT, 0, 13, 0, 190);             // Transformar los valores recibidos por el DAW para la salida PWM
    analogWrite (meterPin[i], valueMeter);                // Emite el valor por PWM hacia el filtro RC.
    }    
  }
}

void checkTouch(int i) {                // Comprueba el sensor capacitivo,
                                        // el valor 700 es arbitrario y puede ser cambiado
                                        //          |||
  if (!touched && touchLine.capacitiveSensor(30) >= 700) {  
    touched = true;                                         // Si se ha tocado .. 
                                                            // Envia mensaje Touch on  al DAW 
    Serial.write(0x90);
    Serial.write(0x67 + faderChannel[i]);
    Serial.write(0x7f);
  }
  else if (touched && touchLine.capacitiveSensor(30) < 700) {
    touched = false;                                        // Si se ha dejado de tocar ..
                                                            // Envia mensaje Touch Off al DAW
    Serial.write(0x90);
    Serial.write(0x67 + faderChannel[i]);
    Serial.write((byte) 0x00);
  }
  if (touched) {                                            // 
    positionUpdated[i] = false;
  }
}

void updateFaderMidi(int i) {                   // Envia posicion del fader al DAW
 
    int volumen = faderPosition(i);
    byte channelData = 0xE0 + (faderChannel[i] - 1);

     // MIDI Message:
     Serial.write(channelData);                 // E(PitchBend) Channel (0-9)
     Serial.write(volumen & 0x7F);              // Bits de datos menos significativos
     Serial.write((volumen >> 7) & 0x7F);       // Bits de datos más significativos
}

//  Muever el fader a una posición específica entre 0-1023 si aún no está allí

void updateFader(int valuepos ,int i ) {                                   // Envia posicion del DAW al fader
  posicion[i] = valuepos;
  if (posicion[i] < analogRead(fader[i]) - 10 && posicion[i] > faderMin[i] && !touched) {
      digitalWrite(motorDown[i], HIGH);
      while (posicion[i] < analogRead(fader[i]) - 10 && !touched) {};      // Bucles hasta que el motor se mueve
      digitalWrite(motorDown[i], LOW);
  }
  else if (posicion[i] > analogRead(fader[i]) + 10 && posicion[i] < faderMax[i] && !touched) {
      digitalWrite(motorUp[i], HIGH);
      while (posicion[i] > analogRead(fader[i]) + 10 && !touched) {};      // Bucles hasta que el motor se mueve
      digitalWrite(motorUp[i], LOW);
  }
}
int faderPosition(int i) {   
      posicion[i] = analogRead(fader[i]);
      
  if (posicion[i] <= faderMin[i]) {
      returnValue[i] = 0;
  }
  else if (posicion[i] >= faderMax[i]) {
      returnValue[i] = 16383;
  }
  else {
      returnValue[i] = ((float)(posicion[i] - faderMin[i]) / (faderMax[i] - faderMin[i])) * 16383;
  }
  return returnValue[i];      
}

void calibrateFader() {
  for (int i = 0; i < N; i++) {
                                                 // Enviar faders a la parte superior y leer las posiciónes máximas
    digitalWrite(motorUp[i], HIGH);
    delay(300);
    digitalWrite(motorUp[i], LOW);
    faderMax[i] = analogRead(fader[i]) - 5;
  }
  for (int i = 0; i < N; i++) { 
                                                 // Enviar faders a la parte inferior y leer las posiciónes mínimas
    digitalWrite(motorDown[i], HIGH);
    delay(300);
    digitalWrite(motorDown[i], LOW);
    faderMin[i] = analogRead(fader[i]) + 5;
  }
}

20180606_135932
20180602_155520
20180603_020346

@tttapa
Copy link
Owner

tttapa commented Jun 6, 2018

Looking good!

Like I said in the other issue, I currently have exams, so there's not much I can do until early July.

I have tried the same (or similar) motorized fader sketch, but I wasn't happy with the result, for multiple reasons:

  • The control system is very primitive, it's just "turn the motor all the way on until you reach your target value minus 10". It's not accurate.
  • The while loops of the fader movement block the entire system, so it doesn't react to MIDI in or button presses, etc.
  • "Smooth" fader curves aren't smooth, because of the terrible control system.

I've done some research into PID controllers, and that seems promising. However, I can't get them 100% smooth. The PWM power isn't linear at all, making it many times harder (especially at high frequencies, you don't want them in the audible spectrum, especially near audio equipent). You also need to call the control functions at a regular interval, which requires the use of timer interrupts. All tuning depends on the hardware it is used on, so it's really hard to come up with a "one-size-fits-all" solution to distribute with this library.

If I manage to do it, I'll probably only support Teensy boards, because it is a pain to support the right PWM and Timer settings on AVR, while Teensy comes with easily portable function for all of this. I don't expect anyone to run such a "heavy" project on an AVR anyways.

I also have some updates (almost) ready for the MIDI Controller library, improving filtering of the analog inputs. If you're having problems with that, keep an eye out for version 3.1.0 somewhere in the first week of July.

@oscarosky
Copy link
Author

I agree with you.
The behavior of the system gets worse as new elements are added. Maybe the control of the faders needs a dedicated microcontroller.
We will also talk in a few weeks about the level meters on PWM.
A greeting.

@masydoblig
Copy link

i was thinking that also if we put the faders on a dedicated controller

here is the design i'm working on for my studio. It is for Cubase.
i the code works really well as a generic controller

would love to get 8 motor faders working from one board so i can program them in banks of 8

long fader done

@masydoblig
Copy link

Any progress with the faders ???

@tttapa
Copy link
Owner

tttapa commented Oct 20, 2019

I posted my code here: https://github.com/tttapa/MotorFader

I'm not satisfied with the results, and it's a lot of work to integrate it with the Control Surface library, and I don't have the time. I also have to add a Kalman filter and a better way to tune the PID controller dynamically.

@tttapa
Copy link
Owner

tttapa commented Jul 19, 2021

For future reference:
Motorized faders are now supported, you can find the documentation at https://tttapa.github.io/Pages/Arduino/Control-Theory/Motor-Fader and the code in tttapa/Control-Surface-Motor-Fader.

@tttapa tttapa closed this as completed Jul 19, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants