Código con varios encoders

Hola no manejo mucho de foros, espero no meter la pata jej.

Estoy en un proyecto/desafió personal de armar una cabina simulador de vuelo con un amigo piloto, arme una parte de control, funciona pero tiene un problema.

En uso:
Arduino MEGA 2560, 6 encoders rotativos, 4 leds, un pulsador y un switch. ( por ahora, pero faltan muchas cosas por agregar jeje)
Imprime por puerto serie y es interpretado por el programa Link2FSX.

El problema es el siguiente: avanzando muy lentamente las palancas la lectura de los encoders es correcta, al apurarlos queda estancada la lectura, ejemplo: 1 2 3 4 3 4 3 4 3 4.

Creo que se entiende jej soy muy nuevo en programación voy buscando y leyendo pero entre mi trabajo y la facultad (nada que ver los dos con programación como para tener practica) no tengo mucho tiempo y hago lo que puedo con una o dos horas por día.

ACLARACIÓN: en el código verán que están detallados variables para Motores 1 y 2 pero falta código, borre pq me excedía de 9000 caracteres jeje.

Espero me puedan dar una ayudita, saludos y gracias.

// Variables Globales Encodres
unsigned long time;

// encoder "PGM1" - Palanca de Gases Motor 1
int PGM1_PCLK = 48;//"PCLK" Pin CLK.
int PGM1_PDT = 49;//_"PDT" Pin DT.
int PGM1_RE_P = 0;//_"RE" Rotary Encoder, "P" Posicion.
unsigned long PGM1_RE_T;
bool PGM1_RE_A = true;
bool PGM1_RE_B = true;

// encoder "PGM2" - Palanca de Gases Motor 2
int PGM2_PCLK = 52;// "PCLK" Pin CLK.
int PGM2_PDT = 53;// _"PDT" Pin DT.
int PGM2_RE_P = 0;// _"P" Posicion.
unsigned long PGM2_RE_T;
bool PGM2_RE_A = true;
bool PGM2_RE_B = true;

// encoder "PHM1" - Paso de Helice Motor 1
int PHM1_PCLK = 50;//"PCLK" Pin CLK.
int PHM1_PDT = 51;//_"PDT" Pin DT.
int PHM1_RE_P = 0;//_"RE" Rotary Encoder, "P" Posicion.
unsigned long PHM1_RE_T;
bool PHM1_RE_A = true;
bool PHM1_RE_B = true;

// encoder "PHM2" - Paso de Helice Motor 2
int PHM2_PCLK = 45;// "PCLK" Pin CLK.
int PHM2_PDT = 44;// _"PDT" Pin DT.
int PHM2_RE_P = 0;// _"P" Posicion.
unsigned long PHM2_RE_T;
bool PHM2_RE_A = true;
bool PHM2_RE_B = true;

// encoder "PMM1" - Palanca de Mezcla Motor 1
int PMM1_PCLK = 42;//"PCLK" Pin CLK.
int PMM1_PDT = 43;//_"PDT" Pin DT.
int PMM1_RE_P = 0;//_"RE" Rotary Encoder, "P" Posicion.
unsigned long PMM1_RE_T;
bool PMM1_RE_A = true;
bool PMM1_RE_B = true;

// encoder "PMM2" - Palanca de Mezcla Motor 2
int PMM2_PCLK = 47;// "PCLK" Pin CLK.
int PMM2_PDT = 46;// _"PDT" Pin DT.
int PMM2_RE_P = 0;// _"P" Posicion.
unsigned long PMM2_RE_T;
bool PMM2_RE_A = true;
bool PMM2_RE_B = true;

//Variables Tren de Aterrizaje
int TALN = 41; // _ _ _ "L" Led, "N" Nariz.
int TALI = 40; // _ _ _ "L" Led, "I" Izquierda.
int TALD = 38; // _ _ _ "L" Led, "D" Derecha.
int TALP = 39; // _ _ _ "L" Led, "P" Peligro.
int TAI = 36; //_ _ _ _ "I" Interruptor.
int TAPT = 37; // _ _ _ "P" Pulsador, "T" Test ok.
int TACR; //_ _ _ _ _ _ "C" Codigo, "R" Recibido: Almacena codigo de Link2FS para TA.

void setup() {
Serial.begin (115200);

// SETUP de Encoders
   pinMode (PGM1_PCLK,INPUT);
   pinMode (PGM1_PDT,INPUT);
   
   pinMode (PGM2_PCLK,INPUT);
   pinMode (PGM2_PDT,INPUT);

   pinMode (PHM1_PCLK,INPUT);
   pinMode (PHM1_PDT,INPUT);
   
   pinMode (PHM2_PCLK,INPUT);
   pinMode (PHM2_PDT,INPUT);

   pinMode (PMM1_PCLK,INPUT);
   pinMode (PMM1_PDT,INPUT);
   
   pinMode (PMM2_PCLK,INPUT);
   pinMode (PMM2_PDT,INPUT);

//Setup Tren de aterrizaje
  pinMode(TALN, OUTPUT);
  pinMode(TALI, OUTPUT);
  pinMode(TALD, OUTPUT);
  pinMode(TALP, OUTPUT);
  pinMode(TAI, INPUT_PULLUP); //Se usa input_pullup para flanco ascendente y asi evitar rebotes
  pinMode(TAPT, INPUT_PULLUP); //Se usa input_pullup para flanco ascendente y asi evitar rebotes
 }

void loop() {
  
//_________ LOOP Encoders______________
time = millis();
//------Palanca de Gases Motor 1
//-------------------------------------- 
  if (digitalRead(PGM1_PCLK) == LOW){
    PGM1_RE_T = time;
    if (PGM1_RE_A == true){
      PGM1_RE_P ++;
      PGM1_RE_A = false;
      PGM1_RE_B = false;
      PGM1_RE_P = min(10, max(0, PGM1_RE_P));
      if(PGM1_RE_P*99/10 > 9){Serial.print("C560");Serial.println(PGM1_RE_P*99/10);}
      if(PGM1_RE_P*99/10 <= 9){Serial.print("C5600");Serial.println(PGM1_RE_P*99/10);}
      }
  }
  if (digitalRead(PGM1_PDT)== LOW){
    PGM1_RE_T = time;
    if(PGM1_RE_B == true){
      PGM1_RE_P --;
      PGM1_RE_A = false;
      PGM1_RE_B = false;
    PGM1_RE_P = min(10, max(0, PGM1_RE_P));
    if(PGM1_RE_P*99/10 > 9){Serial.print("C560");Serial.println(PGM1_RE_P*99/10);}
    if(PGM1_RE_P*99/10 <= 9){Serial.print("C5600");Serial.println(PGM1_RE_P*99/10);}
     }
  }
  if (time - PGM1_RE_T>10){
    PGM1_RE_A = true;
    PGM1_RE_B = true;
  }
//-------------------------------------- 
//------Paso Helice Motor 1
//-------------------------------------- 

  if (digitalRead(PHM1_PCLK) == LOW){
    PHM1_RE_T = time;
    if (PHM1_RE_A == true){
      PHM1_RE_P ++;
      PHM1_RE_A = false;
      PHM1_RE_B = false;
      PHM1_RE_P = min(10, max(0, PHM1_RE_P));
      if(PHM1_RE_P*99/10 > 9){Serial.print("C600");Serial.println(PHM1_RE_P*99/10);}
      if(PHM1_RE_P*99/10 <= 9){Serial.print("C6000");Serial.println(PHM1_RE_P*99/10);}
      }
  }
  if (digitalRead(PHM1_PDT)== LOW){
    PHM1_RE_T = time;
    if(PHM1_RE_B == true){
      PHM1_RE_P --;
      PHM1_RE_A = false;
     PHM1_RE_B = false;
    PHM1_RE_P = min(10, max(0, PHM1_RE_P));
    if(PHM1_RE_P*99/10 > 9){Serial.print("C600");Serial.println(PHM1_RE_P*99/10);}
    if(PHM1_RE_P*99/10 <= 9){Serial.print("C6000");Serial.println(PHM1_RE_P*99/10);}
     }
  }
  if (time - PHM1_RE_T>10){
    PHM1_RE_A = true;
    PHM1_RE_B = true;
  }
//-------------------------------------- 
//------Palanca de Mezcla Motor 1
//-------------------------------------- 

  if (digitalRead(PMM1_PCLK) == LOW){
    PMM1_RE_T = time;
    if (PMM1_RE_A == true){
      PMM1_RE_P ++;
      PMM1_RE_A = false;
      PMM1_RE_B = false;
      PMM1_RE_P = min(10, max(0, PMM1_RE_P));
      if(PMM1_RE_P*99/10 > 9){Serial.print("C580");Serial.println(PMM1_RE_P*99/10);}
      if(PMM1_RE_P*99/10 <= 9){Serial.print("C5800");Serial.println(PMM1_RE_P*99/10);}
      }
  }
  if (digitalRead(PMM1_PDT)== LOW){
    PMM1_RE_T = time;
    if(PMM1_RE_B == true){
      PMM1_RE_P --;
      PMM1_RE_A = false;
      PMM1_RE_B = false;
      PMM1_RE_P = min(10, max(0, PMM1_RE_P));
    if(PMM1_RE_P*99/10 > 9){Serial.print("C580");Serial.println(PMM1_RE_P*99/10);}
    if(PMM1_RE_P*99/10 <= 9){Serial.print("C5800");Serial.println(PMM1_RE_P*99/10);}
     }
  }
  if (time - PMM1_RE_T>10){
    PMM1_RE_A = true;
    PMM1_RE_B = true;
  }
  }
//--------------------------------------
// Control TA - Tren de Aterrizaje
//--------------------------------------
  if(Serial.available()){
   TACR = getChar(); // _ _ Lectura codigo Link2FS para pocision del TA (ASCII) ? -> 63 Y -> 89
    if(TACR == 63){
     TACR = getChar();
      if(TACR == 89){
       int TAVN = getChar()-48; // _ _ "V" Variable, "N" Nariz. (Se resta 48 pq el 2 en ASCII se representa con el codigo 50)
       int TAVI = getChar()-48; // _ _ "V" Variable, "I" Izquierda. (Se resta 48 pq el 2 en ASCII se representa con el codigo 50)
       int TAVD = getChar()-48; // _ _ "V" Variable, "N" Derecha. (Se resta 48 pq el 2 en ASCII se representa con el codigo 50)          
       if(TAVN == 2){digitalWrite(TALN, HIGH);}else {digitalWrite(TALN, LOW);}
       if(TAVI == 2){digitalWrite(TALI, HIGH);} else {digitalWrite(TALI, LOW);}
       if(TAVD == 2){digitalWrite(TALD, HIGH);} else {digitalWrite(TALD, LOW);}
       if(TAVN+TAVI+TAVD<6 && TAVN+TAVI+TAVD>0){digitalWrite(TALP, HIGH);} else {digitalWrite(TALP, LOW);}
    }
   }
 }
// Envio de codigo a Link2FS acorde a la posicion del interruptor de TA 
 int TAVEI = digitalRead(TAI);// "V" Variable, "E" Estado, "I" Interruptor.
  if(TAVEI == 0 && digitalRead(TALN)== LOW){Serial.println("C02");}
  if(TAVEI != 0 && digitalRead(TALN) == HIGH){Serial.println("C01");}
 int TAVPT = digitalRead(TAPT);//"V" Variable, "P" Pulsador, "T" Test.
  if(TAVPT == 0){
   digitalWrite(TALN, HIGH); digitalWrite(TALI, HIGH); digitalWrite(TALD, HIGH);digitalWrite(TALP, HIGH);delay(500);
   digitalWrite(TALN, LOW); digitalWrite(TALI, LOW); digitalWrite(TALD, LOW);digitalWrite(TALP, LOW);}
}
//FUNCION getChar()
char getChar(){
  while(Serial.available()==0);
  return((char)Serial.read());
}

Bienvenido al foro y mi enhorabuena por haber etiquetado correctamente el código (pocos nuevos lo hacen).

No he leido todo el código pero me imagino donde puede estar el problema, a ver si soy capaz de explicarlo correctamente.

Primero, para la lectura de los encoder usas digitalRead, no interrupciones. Con lo que cuando haces una lectura del encoder la posición depende de lo que tarde el resto de codigo. Imagina que tu codigo tarda 10ms en ejecutarse, cuando mueves despacio el encoder se produce un cambio más lento en los pines que esos 10ms y te lo detecta. Cuando mueves el encoder muy deprisa, entonces los pines cambian en menos de esos 10ms asi que no detecta bien el cambio de posición y hace cuaquier cosa.

Si usaras interrupciones no notarias nada, pero en cambio por falta de pines de interrupcion no tendrias para muchos encoders.

No se si he explicado bien el problema.

Hola, gracias por responder y si, lo de las interrupciones lo investigue, pero justamente, lo descarte por la cantidad :confused:

Quizás haya una opción que me ha venido a la cabeza.

Usar la librería EnableInterrupt que te permite usar la interrupción "pin change" de cada pin. El atMega tiene un sistema de interrupciones que te permite detectar un cambio en un pin generando una interrupción y puede usarse en la "mayoria" de pins, con lo que te permitiría tener varios encoders basados en interrupción.

No he usado nunca la librería, pero sé que existe y quizá se cuestión de estudiarla.

Yo te recomendaría pasar a un arduino Due que tiene interrupciones en todos los pines.

Este link Encoder Library puede ser de utilidad y solo refuerza lo que te sugirió @PeterKanTropus, que el DUE es tu mejor opción.
Mira la table con los diferentes Arduinos y compatibles y las interrupciones disponibles.

victorjam:
Quizás haya una opción que me ha venido a la cabeza.

Usar la librería EnableInterrupt que te permite usar la interrupción "pin change" de cada pin. El atMega tiene un sistema de interrupciones que te permite detectar un cambio en un pin generando una interrupción y puede usarse en la "mayoria" de pins, con lo que te permitiría tener varios encoders basados en interrupción.

No he usado nunca la librería, pero sé que existe y quizá se cuestión de estudiarla.

Victorjam, investigue tu opción y parece tentadora, tendré que estudiar como usarlo, pero aparentemente, ademas de los pines base con interrupciones del mega (2, 3, 18, 19, 20, 21), se pueden activar como pin de interrupción los pines:

10 al 15, A8 a A15 y SS, SCK, MOSI, MISO.

surbyte:
Este link Encoder Library puede ser de utilidad y solo refuerza lo que te sugirió @PeterKanTropus, que el DUE es tu mejor opción.
Mira la table con los diferentes Arduinos y compatibles y las interrupciones disponibles.

Surbyte, posiblemente si no me funciona la idea de victorjam, caere en comprar un due, al final tengo que comprar otro cuando arme el proyecto.

por ahora gracias a todos, ya actualizare cuando tenga resultados, positivos o negativos...

Hola gente vengo a actualizar la info y compartir la solución.

Intente la librería que libera interrupciones que me aconsejo victorjam y un profesor de mi facultad, pero siendo sincero eso esta muy por arriba de mi nivel de compresión de programación. :confused:

Compre el DUE como me aconsejo Surbyte, pero me quedara para otra cosa.

SOLUCIÓN:

otro profesor de mi facultad me hizo reemplazar lo siguente:

      if(PHM2_RE_P*99/10 > 9){Serial.print("C610");Serial.println(PHM2_RE_P*99/10);}
      if(PHM2_RE_P*99/10 <= 9){Serial.print("C6100");Serial.println(PHM2_RE_P*99/10);}

Con esas lineas armaba el código que debía enviar por puerto serie a la PC, había dos de esas lineas por cada encoder (una para incremento la otra para disminución del valor )

todo lo anterior fue reemplazado con esto:

 sprintf(buffer_temporal, "C56%03u\r\n", PGM1_RE_P*99/10);
      Serial.print(buffer_temporal);

esa función sprintf concatena una variable a partir de los datos que quieras.
agregue una variable global "buffer_temporal" para que se almacene el dato y utilice solo un serial print para enviar el dato.

Aca les dejo el código de un encoder como ejemplo:

//------Palanca de Gases Motor 1
//-------------------------------------- 
  if (digitalRead(PGM1_PCLK) == LOW){
    PGM1_RE_T = time;
    if (PGM1_RE_A == true){
      PGM1_RE_P ++;
      PGM1_RE_A = false;
      PGM1_RE_B = false;
      PGM1_RE_P = min(10, max(0, PGM1_RE_P));
      sprintf(buffer_temporal, "C56%03u\r\n", PGM1_RE_P*99/10);
      Serial.print(buffer_temporal);
      }
  }
  if (digitalRead(PGM1_PDT)== LOW){
    PGM1_RE_T = time;
    if(PGM1_RE_B == true){
      PGM1_RE_P --;
      PGM1_RE_A = false;
      PGM1_RE_B = false;
    PGM1_RE_P = min(10, max(0, PGM1_RE_P));
      sprintf(buffer_temporal, "C56%03u\r\n", PGM1_RE_P*99/10);
      Serial.print(buffer_temporal);     }
  }
  if (time - PGM1_RE_T>10){
    PGM1_RE_A = true;
    PGM1_RE_B = true;
  }

Espero les sirva también a otros. y no se si los moderadores podría sugerir a la pagina que en la Arduino Reference - Arduino Reference agreguen esa función...

Saludos y muchas gracias

La función spritnf es una vieja conocida de los programadores en C (sin el ++). No está en la referencia por ese motivo, ya que no es propia de Arduino. Además tiene algún defecto.

En el caso de Arduino, no trabaja bien con números en coma flotante (float y double), esta mal implementado y hace cosas raras.

Si el buffer temporal no está bien dimensionado puede hacer que el Arduino se reinicie. Ten en cuenta que tu lo que haces es sustituir un valor númerico en una cadena de texto, por ejemplo, en "C56%03u" vas a sustituir %03u por un valor de 0 a 999, con lo que la cadena resultante será "C56XXX" que tiene longitud de 6 caracteres. Si reservas el tamaño de 6 funcionará mientras no escribas 1000, ya que entonces la cadena resultante será de longitud 7. fuera del tamaño del array y dará errores.

Para ejemplo, una cobaya, mira lo que me ocurrió una vez: Arduino Pt100 problema de reinicio.

En tu caso ha corregido el problema parcialmente. Lo que has hecho al cambiar los if por sprintf es mejorar la velocidad de ejecución, con lo que ahora responde mejor si vas más rápido.

Si así es válido no hay problema, si el encoder fuera más rápido o agrandaras el código volverías a tener el mismo problema.

Compre el DUE como me aconsejo Surbyte, pero me quedara para otra cosa.

El crédito por el DUE es para PeterKanTropus, yo solo lo refrendé.

@victorjam No comparto tu respuesta al 100% respecto de sprintf aunque es cierto que tiene sus limitaciones en Arduino viejos en general pero no en todos.
No se si en DUE. En los ESP8266 (ya se,, no son arduinos, pero programados como tales) sI funciona %f y todo lo demás.

Un buffer o cadena de caracteres del tamaño adecuado no generará ningún reinicio.

sprintf(buffer_temporal, "C56%03u\r\n", PGM1_RE_P*99/10);

hablamos de 8 caracteres.

con %u no habrá inconvenientes además.

Me retracto un poco:

Si el buffer temporal no está bien dimensionado puede hacer que el Arduino se reinicie.

He hecho un experimento y, efectivamente, ya no se reinicia. He usado la versión 1.8.7 y un UNO (cosas de la vida, esta muerto de risa enchufado al ordenador siempre). En aquel entonces usaba una versión muy anterior. Eso si, hay que tener cuidado con la dimensión del buffer ya que veo que aunque no se reinicia hace cosas raras.

Con float veo que sigue saliendo "?" en vez de un valor.

Con float no funciona porque el arduino UNO/NANO no tiene la libreria punto flotante cargada. Se puede hacer un cambio pero no lo he logrado.
Encontré un post de un sueco creo pero cuando le pregunté no me respondió.
Basicamente hacia una modificación a la configuración del IDE y podias optar por programarlo con punto flotante lo que agrega varios Kb pero el que lo usa sabe para qué lo necesita.

Como he dicho si vas a Arduinos (programables) como ESP8266 eso ya esta por defecto.
No se en el ST, aun no lo probe.
Tampoco lo he probado con el DUE.
MEGA y todo lo viejo si se me permite el término de Arduino no va.