Problema controlar LEDs con "Control_Surface"

Hola, antes que nada decir que soy novato con el tema arduino :slight_smile:
Estoy fabricando un sencillo controlador midi que tiene dos botones y un led en cada uno de ellos que se enciende y se apaga en función de si el botón se ha pulsado o no. Cuando se pulsa un botón el led contrario se apaga. Esto me funciona si los botones no son CC y uso el parametro digitalRead(buttonPin[i]) que me devuelve el estado del botón y en función de eso puedo encender o apagar los leds. El problema es que con los botones CC no sé cómo leer el estado de los mismos para luego gestionar los leds. Qué parametro debo usar? Cómo sé el estado de un botón CC?

Este es el código actual:

#include <Control_Surface.h>  // Include the Control Surface library

// Instantiate a MIDI over USB interface.
USBMIDI_Interface midi;



CCPotentiometer pot(A0, CHANNEL_3);  // Potenciómetro conectado al pin analógico A0
//CCPotentiometer pot1(A1, CHANNEL_4);   // Potenciómetro conectado al pin analógico A1

const int N_BUTTONS=2; //numero de botons

CCButton Buttons[] = {
  { 0, { 20, CHANNEL_1 } },  /// Buto 1 (pin, valor, canal)
  { 1, { 20, CHANNEL_1 } },  /// Buto 2 (pin, valor, canal)
  // { 2, { 20, CHANNEL_1 } },  /// Buto 3 (pin, valor, canal)
  // { 3, { 20, CHANNEL_1 } },  /// Buto 4 (pin, valor, canal)
  // { 4, { 20, CHANNEL_1 } },  /// Buto 5 (pin, valor, canal)
  // { 5, { 20, CHANNEL_1 } },  /// Buto 6 (pin, valor, canal)
  // { 6, { 20, CHANNEL_1 } },  /// Buto 7 (pin, valor, canal)
  // { 7, { 20, CHANNEL_1 } },  /// Buto 8 (pin, valor, canal)
};



/// LEDS

///***variables configurables***

const int N_LED = 2;              //* Numero de leds
byte ledPin[N_LED] = { 10, 11 };  //* Pins dels leds { 12, 13 }



void setup() {
  Control_Surface.begin();  // Initialize Control Surface
  for (int i = 0; i < N_LED; i++) {
    pinMode(ledPin[i], OUTPUT);  // declara els leds
  }

}

void loop() {
  
  Control_Surface.loop();  // Update the Control Surface
  delay(20);
}

Gracias

Con todos los ejemplos disponibles en la librería no encontraste algo que te sirva.

no se que es un boton CC. Puedes explicarlo?

Yo no soy muy experto en el tema, pero lo que yo entiendo es que un botón CC es un control change y debe devolver un valor 0 o 1 supongo, algo binario. De hecho cuando se declaran se declaran como CCButton. Yo lo quiero para encender o apagar algo.

Lo bueno es que la libreria "control Surface" hace que este tipo de botones solo se puedan pulsar una vez y no mandan más señales hasta que se dejan de pulsar, mientras que los botones normales están mandando señal mientras no lo sueltas...no se si me explico...

Esa es la manera correcta de trabajar con pulsadores, detectando los flancos de la señal (como seguramente hace esta librería).

Correcto, pero no soy capaz de leer el estado del botón si es un botón "CC" mientras que un botón "normal" que manda una nota si que lo lee con el siguiente codigo por ejjemplo:

  pushbutton.update();                              // read the button state
  if (pushbutton.getState() == Button::Falling)     // if the button is pressed
    midi.sendNoteOn(noteAddress, velocity);         // send a note on event
  else if (pushbutton.getState() == Button::Rising) // if the button is released
    midi.sendNoteOff(noteAddress, velocity);        // send a note off event

Pero claro, esto manda la nota repetidamente mientras el botón está pulsado y eso es lo que no quiero. Cuando es botón es declarado como CCButton funciona de perlas en cuanto a que solo manda valor cuando pulsas y cuando sueltas...pero no soy capaz de controlar el estado del mismo para poder activar un led en función de si pulsas o no...

No solo no querés, no debés.
Según el estándar MIDI por cada nota, después de un NoteON solo debe llegar NoteOFF.
O sea que que no se deberían enviar varios NoteON seguidos para la misma nota.

Por otro lado, ese código teóricamente trabaja con los flancos (falling y rising, bajada y subida del pulsador), si repite envíos es porque hay algo mal en la librería o te falta algo.
La verdad es que la librería es muy grande, tiene una gran cantidad de archivos y está programada de una manera que cuesta seguir y entender (al menos a mi me cuesta).
Sinceramente no entiendo como determina el estado del botón en la función update().

Lo que podés hacer es agregar una variable que te "recuerde" si la nota ya está sonando.
Supongamos una variable global

bool noteOn = false;

entonces

pushbutton.update();                              // read the button state
  if ((pushbutton.getState() == Button::Falling)  && !noteOn) {    // if the button is pressed
    midi.sendNoteOn(noteAddress, velocity);         // send a note on event
    noteOn = true;
  }
  else if ((pushbutton.getState() == Button::Rising) && noteOn) { // if the button is released
    midi.sendNoteOff(noteAddress, velocity);        // send a note off event
    noteOn = false;
  }

Hoy no puedo probarlo, mañana lo hago sin falta, muchas gracias!

Para poner en contexto, lo que hace el CCButton precisamente es evitar el tema "debounce" y es por eso que me gusta y lo quiero usar:

https://tttapa.github.io/Control-Surface-doc/Doxygen/d2/daa/classCCButton.html
"The button is debounced in software. "

Si no tengo mal entendido, el debounce son las minilecturas que hace el sistema de un botón cuando lo pulsas, como si fuese "ruido" que produce el sistema" y con esta librería las evita, por eso me gusta tanto el CCButton, porque evita este "problema". Ese es el motivo por el que si no uso el CCButton la señal sale repetidas veces, por el devounce este :slight_smile:

Es que pushbutton también tiene debouce, por eso creo que hay algo raro en la librería o tal vez hay algo que faltar definir.

Mañana la seguimos si hace falta.

Estaba revisando esto y esto que has puesto sin la variable de control ya funciona, de hecho era el ejemplo que ponía yo para mostrar lo que si que funcionaba...pero eso funciona si el botón se declara normalmente, no como CCButón. A lo mejor no me estoy explicando muy bien XD

Voy a poner un codigo que funciona perfecto pero usa otra librería y tiene unas cuantas lineas de codigo y variables para correguir el tema "debounce" tanto en botón como en potenciometro. Esto funciona perfecto, el "problema" es que con la librería controlsurface el codigo es más limpio ya que todo el debounce lo controla directamente con la librería, y me gustaría usarla por eso. Pero no doy con la tecla de cómo leer el estado al CCButtón.

Total, el código que funciona (los comentarios míos están en catalán, si alguién quiere que le traduzca alguna parte que me lo pida y lo haré encantado)

// NOTA CTRL+T ORDENA EL TEXT
// nomes lineas amb * es poden modificar

#include "MIDIUSB.h"  // afegim la llibreria

//-----------BUTTONS-----------

// Es fa tot lo del debounce y no delay perque el delay para tot el codi i amb aixo del debounce no. El DEBOUNCE es l efecte que fa que apretis un boto i sembla que l apretis mes
// d'un copo

///***variables configurables***

const int N_BUTTONS = 2;               //* Numero de botons
byte buttonPin[N_BUTTONS] = { 5, 6 };  //* Aqui s'ha de posar els pins on conectem els butos
int buttonTimeout = 200;               //* El temps que esperarà a tornar a llegir el boto per evitar el debounce en milisegons. Sugestió xxx
byte NN[N_BUTTONS] = { 36, 38 };       //* Asignem quines notes envien els botons en fucnio dels botons que hi ha 36= C 38= D 40= E

///***variables NO  configurables***

byte buttonState[N_BUTTONS] = { 0 };                //Aqui anira el estat ACTuAL del boto
byte buttonPrevState[N_BUTTONS] = { 0 };            //Aqui anira el estat PREVI del boto
unsigned long lastDebouncetime[N_BUTTONS] = { 0 };  //Variable per controlar el efecte "debounce" (que apretis un cop i reboti el boto dinant moltes senyals)
unsigned long buttonTimmer[N_BUTTONS] = { 0 };      //Variable per controlar el temps que ha passat




//-----POTENTIOMETERS-----

// Els pots poden tenir FLOATING, que es quan sembla que els toquis una mica quan ningu toca

///***variables configurables***

const int N_POTS = 1;          //* Numero de pots
byte potPin[N_POTS] = { A0 };  //* Aqui s'ha de posar els pins on conectem els pots, format { A0, A1, A2 }
int CC[N_POTS] = { 11 };       //* La variable on envia els valors midi, tants com Pots hi hagi format { 11, 12, 13 }
int potThreashold = 20;        //* Variable que controla si es un moviment soroll o real. son milisegons. sugerit 50
int potTimeOut = 20;          //* Temps que mira del potencimetre en milisegons. son milisegons, sugerit 100


///***variables NO  configurables***

int potState[N_POTS] = { 0 };               //Estat actual del pot
int potPrevState[N_POTS] = { 0 };           //Estat previ del pot
int midiState[N_POTS] = { 0 };              //El valor midi del estat del pot
int midiPrevState[N_POTS] = { 0 };          //El valor midi del estat previ del pot
unsigned long lastPotTime[N_POTS] = { 0 };  //Per mirar quan temps ha estar el potenciometre en moviment l ultim cop(no entenc)
unsigned long potTimer[N_POTS] = { 0 };     //Per mirar quan temps ha estar el potenciometre en moviment actual (no entenc)


/// LEDS

///***variables configurables***

const int N_LED = 2;              //* Numero de leds
byte ledPin[N_LED] = { 10, 11 };  //* Pins dels leds { 12, 13 }



/// VARIABLES GLOBALS
//-------------------
int MIDI_CH = { 0 };  //* Declarem a quin canal envia la señal midi (0 to 15)


//------

void setup() {
  // put your setup code here, to run once:

  for (int i = 0; i < N_BUTTONS; i++) {
    pinMode(buttonPin[i], INPUT_PULLUP);  // declara els pins dels botons
  }

  for (int i = 0; i < N_LED; i++) {
    pinMode(ledPin[i], OUTPUT);  // declara els leds
  }
}

void loop() {
  // put your main code here, to run repeatedly:
  buttons();
  potentiometers();
}

void buttons() {

  for (int i = 0; i < N_BUTTONS; i++) {
    buttonState[i] = digitalRead(buttonPin[i]);  // LLegeix l estat dels botons

    buttonTimmer[i] = millis() - lastDebouncetime[i];  // mirem el temps que ha passat desde l ultim cop que s ha pulsat el boto

    if (buttonTimmer[i] > buttonTimeout) {  // mirem el tems que ha passat desde l ultim cop que ha rebut señal pel tema debounce
      ///El que fa el seguent if es mira si el boto ha canviat d estat, si ha canviat fa coses i guarda l estat pel seguent cop poder saber si ha canviat
      //si no ha canviat no fa res

      if (buttonState[i] != buttonPrevState[i]) {  // compara si el estat del botó ha canviat, si no ha canviat no fa res (la placa no envia cap señal)

        lastDebouncetime[i] = millis();  // aqui mirem el rellotge com esta quan s apreta el botó

        if (buttonState[i] == LOW) {  // Mira si el buto está pulsat, LOW es igual a apretat
          //hauriem d'apagar tots els leds
          for (int a = 0; a < N_LED; a++) {
            digitalWrite(ledPin[a], LOW);  //apagem tots els led
          }
          noteOn(MIDI_CH, NN[i], 127);     // El que envía si el botó está pulsat
          MidiUSB.flush();                 // aixo sempre despres de que enviem midi
          digitalWrite(ledPin[i], HIGH);   // Encenem el led
          lastDebouncetime[i] = millis();  // guarda el temps per controlar el debounce

        } else {
          noteOn(MIDI_CH, NN[i], 0);    // El que envía si el botó está "released"
          MidiUSB.flush();              // aixo sempre despres de que enviem midi

        }


        buttonPrevState[i] = buttonState[i];  // Guarda el estat a previous
      }
    }
  }
  // Serial.println();
}


void potentiometers() {


  for (int i = 0; i < N_POTS; i++) {
    potState[i] = analogRead(potPin[i]);               // Llegeix el estat del potenciometre
    midiState[i] = map(potState[i], 0, 1023, 0, 127);  // convertim el valor a midi, format= (variable, valor maxim de la variable, valor minim, valor maxim midi, valor minim)

    int potVar = abs(potState[i] - potPrevState[i]);  // mirem la diferencia de valors per mirar si es "soroll" o es canvi real. abs converteix els valors negatius en positiu


    if (potVar > potThreashold) {  // compara si el moviment del pot es superior a la variable per mirar si es soroll, so ho es entra
      lastPotTime[i] = millis();   // Si s ha mogut el potenciometre, guardem el temps que ha transcorregut (RESET THE CLOCK)
    }
    potTimer[i] = millis() - lastPotTime[i];  // Controla quan temps ha passat desde que s ha mogut el pot
    if (potTimer[i] < potTimeOut) {
      if (midiState[i] != midiPrevState[i]) {  // Mirem si l estat d algun potencimetre ha canviat, si ha canviat mostrem

        controlChange(MIDI_CH, CC[i], midiState[i]);  // el que enviem a la funcio del midi
        MidiUSB.flush();                              // aixo sempre despres de que enviem midi
        midiPrevState[i] = midiState[i];  // guardem el estat del pot midi com a stat previo
      }
      potPrevState[i] = potState[i];  // guardem el estat del pot com a stat previo (no entenc perque posem la variabke anterior amb el midi)
    }
  }
}

void noteOn(byte channel, byte pitch, byte velocity) {
  midiEventPacket_t noteOn = { 0x09, 0x90 | channel, pitch, velocity };  // es lo midi que s envia amb els botons al ordinador
  MidiUSB.sendMIDI(noteOn);
}

void controlChange(byte channel, byte control, byte value) {
  midiEventPacket_t noteOn = { 0x09, 0x90 | channel, control, value };  // es lo midi que s envia amb el potenciometre al ordinador
  MidiUSB.sendMIDI(noteOn);
}

Y este es un codigo con "control surface" que también funciona, pero tiene debounce o yo hago algo mal porque cuando aprieto el botón manda decenas de mensajes midi, no hace un On/Off cómo me gustaría:

#include <Control_Surface.h>  // Include the Control Surface library

// Instantiate a MIDI over USB interface.
USBMIDI_Interface midi;



CCPotentiometer pot(A0, CHANNEL_3);  // Potenciómetro conectado al pin analógico A0
//CCPotentiometer pot1(A1, CHANNEL_4);   // Potenciómetro conectado al pin analógico A1

const int N_BUTTONS=2; //numero de botons

CCButton Buttons[] = {
  { 0, { 20, CHANNEL_1 } },  /// Buto 1 (pin, valor, canal)
  { 1, { 20, CHANNEL_1 } },  /// Buto 2 (pin, valor, canal)
  // { 2, { 20, CHANNEL_1 } },  /// Buto 3 (pin, valor, canal)
  // { 3, { 20, CHANNEL_1 } },  /// Buto 4 (pin, valor, canal)
  // { 4, { 20, CHANNEL_1 } },  /// Buto 5 (pin, valor, canal)
  // { 5, { 20, CHANNEL_1 } },  /// Buto 6 (pin, valor, canal)
  // { 6, { 20, CHANNEL_1 } },  /// Buto 7 (pin, valor, canal)
  // { 7, { 20, CHANNEL_1 } },  /// Buto 8 (pin, valor, canal)
};



/// LEDS

///***variables configurables***

const int N_LED = 2;              //* Numero de leds
byte ledPin[N_LED] = { 10, 11 };  //* Pins dels leds { 12, 13 }



void setup() {
  Control_Surface.begin();  // Initialize Control Surface
  for (int i = 0; i < N_LED; i++) {
    pinMode(ledPin[i], OUTPUT);  // declara els leds
  }

}

void loop() {
  
  Control_Surface.loop();  // Update the Control Surface
buttons();


  delay(20);
}

void buttons(){

byte buttonState[N_BUTTONS] = { 0 };    
  for (int i = 0; i < N_BUTTONS; i++) {
    buttonState[i] = digitalRead(Buttons[i]);  // LLegeix l estat dels botons

    if (buttonState[i] == LOW) {  // Mira si el buto está pulsat, LOW es igual a apretat
      //hauriem d'apagar tots els leds
      for (int a = 0; a < N_LED; a++) {
        digitalWrite(ledPin[a], LOW);  //apagem tots els led
      }
      midi.sendNoteOn(NN[i], 127);
      digitalWrite(ledPin[i], HIGH);  // Encenem el led
      midi.sendNoteOn(NN[i], 0);
    }
  }

En fin, por si alguien me guia un poco...

Si ya sé que funciona sin la variable que te agregué, la variable de control es para que no repita el envío.

Si, porque el autor no usó una librería de 3ros para manejar los botones, yo hago lo mismo.

Si, no ponés una variable de control como te expliqué antes.
Y si son varios botones usa un array con un elemento para cada botón.

1 Like

Vale, no me había fijado que le habías puesto el condicional "&& !noteOn" o "&& noteOn)" en el if de mi código. Lo había leído mal, perdona. A ver si esta tarde noche lo puedo probar. Gracias!

He probado y sigue sin funcionar correctamente en el sentido que mientras mantengo pulsado está mandando valor.

Buscando por internet he encontrado alguien que creo que hace lo que necesito:

#include <Control_Surface.h> // Include the library 

 
/* Instantiate a latched push button that sends MIDI CC messages for a BOOST function

This still uses a momentary physical switch. Assign to Volume in H9 control

Default values overriden. Adjust to boost strength preference

 */

CCButtonLatched boost = {12, {0x07, CHANNEL_1},{ 127, 80 }};  // CC#7 – VOLUME BOOST

const pin_t BoostLed= {13};  // The LED to display the state of the boost button.

 
void setup() {

Control_Surface.begin(); // Initialize main Control Surface code

pinMode(13, OUTPUT);  // assign Led pin as output pin

}

 

void loop() {

Control_Surface.loop(); // Update the main Control Surface

digitalWrite(BoostLed, boost.getState() ? HIGH : LOW); // Update the LED state to reflect the toggled switch state. Match name of led to name of button

he dejado solo lo que hace referencia a mi entender a un CCButton que actua similar a lo que necesito yo activando un led cuando se le pulsa. Entiendo que lee el estado del boton con el "getState?"...tendré que investigar un poco porque me suena a chino.

Creo que no tenés muy en claro para qué se usa cada cosa.

read the input of a momentary push button or switch, and send out MIDI **Control Change** events

CCButton está diseñado para enviar mensajes Control Change (por eso el prefijo CC), no para enviar notas.

En #7 pusiste el link a la documentación de la clase y ahí se explica claramente lo que hace.

Por otro lado, hace ya como 2 años que un músico amigo ejecuta un instrumento con 42 sensores, en el código controlo el envío o no de las notas tal y como te lo expliqué, y no repite ni un solo envío de una nota que no haya sido previamente apagada (tal y como lo exige la norma MIDI).
No me explico cómo dices que no funciona siendo que además es algo básico.

1 Like

Amigo MaximoEsfuerzo, es posible que no tenga claro para que se usa cada cosa, he empezado diciendo que soy relativamente novato con el tema....pero lo que yo quiero es el control change, no quiero mandar notas. Desde el principio he dicho que lo que necesito es encender o apagar algo y para eso el CC me sirve

El botón funciona perfecto, hace lo que necesito y encima tiene todo el tema del debounce ya tratado por la libreria. Pero quiero que a la vez encienda un led, de ahi que necesite ver cómo leo el estado de un botón CCButton de la librería "control surface" para encender o apagar en función de si está apretado o no. Y esto es lo que no entiendo cómo hacerlo.

Primero que nada me disculpo si te molestó mi comentario, no fue la intención.

La confusión se me armó por el código que pusiste de ejemplo en #5.

Probá cambiando en el código de #15

CCButtonLatched boost = {12, {0x07, CHANNEL_1},{ 127, 80 }}; // CC#7 – VOLUME BOOST 

por

CCButton boost = {12, {0x07, CHANNEL_1},{ 127, 80 }}; // CC#7 – VOLUME BOOST 

a ver si funciona.

1 Like

He encontrado la solución, era mucho más sencillo de lo que pensaba. Se lee el estado con "getButtonState()"

:slight_smile:

Gracias por la ayuda :slight_smile: