Buenas gente, les traigo un proyecto casi terminado (me faltan ensamblar cosas) pero de código estoy contento con lo que tengo. Ya llevo casi dos años en esto, entre mi trabajo rotativo y la facultad y mi poco conocimiento de arduino fue lento el tema.
Aun así quiero compartirlo con ustedes ya que @IgnoranteAbsoluto me ofreció que lo comparta con ustedes y entre todos podemos tal vez mejorar el código y ampliar a mas funciones que el programa ofrece.
Una cosa, este programa que utilice no sirve para el comando de superficies de control de vuelo (osea, control de alas, y cola del avión, lo que el piloto hace con el volante y los pedales)
EMPECEMOS
lo dividire en 3 partes:
- Software
- Hardware
- Codigo
POR EL LADO DEL SOFTWARE:
El simulador:
Este programa es solo apto para el Flight Simulator X
El "programa":
Se llama Link2fs (la pagina del creador desaparecio, quedan algunos hilos de este tema en la pagina mycockpit.org) este programa a mi entender, interpreta los códigos que le enviamos por puerto serie con el arduino y manda la función al FSX.
Algunas caracteristicas del link2fs:
- El mismo permite conectar hasta 5 tarjetas arduino (nunca lo eh probado solo utilizo una)
- No permite modificar el baudrate del puerto serie esta preseteado a 115200 (primera imagen)
- Desde arduino se envian codigos por puerto serie para cada función (segunda imagen)
- Se púeden obtener datos del simulador de vuelo que el mismo link2fs imprime por el puerto serie, estos se pueden elegir cuales queremos recibir para después darle uso con el arduino.
Imagenes del Link2fs:
AHORA EL HARDWARE:
Ah nivel arduino uso un mega por la cantidad de conexiones para lo demás, es a gusto creo yo, como se ve en las imágenes anteriores y muchos datos que se pueden extraer o ingresar al link2fs, pero igual les comparto lo que arme.
se los pongo con fotos y descripciones de ellas.
Esta estructura roja al igual que las palancas la armo mi amigo, futuro piloto y dueño/usuario del simulador (yo soy el de las ideas, programación electrica y electronica del mismo) aca utilize 6 encoders accionados con poleas de teflon conectados con O-rings a cada palanca.
.
Aca esta el comando terminado (siento no tener mas fotos de la construcción de las demás botoneras)
Aun asi les describo las funciones de izquierda a derecha:
Arriba:
- Palancas negras = acelerador motor 1 y 2;
- Palancas azules = paso de helice motor 1 y 2;
- Palancas rojas = Mezcla aire/combustible motor 1 y 2;
Abajo:
- Llave 2 puntos + led rojo = Freno de estacionamiento + el indicador;
- Boton azul + llave de dos puntos + 3 leds verds + 1 led rojo = boton azul "test" tren de aterrizaje + llave 2 puntos baja o sube el tren de aterrizaje + leds verdes si encendido tren abajo + led rojo tren en movimiento;
- Tecla doble sentido + led rojo = Tecla doble sentido baja o sube los flaps + led rojo indica flaps activos
.
Los encoders los compre solos pero estan armados y conexionados como los que se compran para arduino. (los utilizados estan directamente cableados igual que estos y sin la silicona, pero este lo protegi de esa manera para evitar que se corte las soldaduras y tenerlo para pruebas)
.
Bueno y estas placas son parte de una matriz de botones que ira puesta al rededor de uno de los monitores del simulador. Como veran en la primera hice una indiada usando para placa una maderita por no saber hacer placas o no comprar las verdes que termine comprando despues para las otras dos partes jeje
.
Aca tengo todo conectado y probando:
Este es el mueble en el que ira montado todo
AHORA LO LINDO, EL CODIGO:
El codigo final que tengo en funcionamiento hasta llegar a funcionar tuve varias consultas aca en el foro de arduino, de los cuales les dejo los links por si les interesa:
Tenia problemas con los encoders
Necesitaba una matriz de botones de 6x7
Cuando adicione la matriz de botones dejaron de funcionar otra vez los encoders XD
Algo que hice para armar el código cada vez que quería agregar algo fue, en primera el codigo que tenia funcionando, hacerle una copia y ponerla en la carpeta que se llama "para toquetear". Con esto si rompía el código y no sabia como volver atras tenia una copia jeje.
El código a insertar "botonera funciona" (previamente comprobado que funciona solo) en el principal "PG_PH_M_F_FE" , lo abría a la par del original eh iba copiando las partes a los sectores que correspondian.
Una vez verificado en el programa que no habia conflictos si no tenia algo del hardware armado para probarlo completo lo dejaba en otra carpeta hasta poder comprobarlo "PG_PH_M_F_FE_Bot falta probar"
como veran el nombre del archivo es raro pero me servia para identificar las partes ya ensambladas, en este caso significan:
PG = Palanca de Gases PH = Paso de Helice M = Mezcla de motor F = Flaps
FE = Freno de Estacionamiento Bot = Botonera (la matriz de botones)
y el codigo:
// Matriz de botones
#include <Arduino.h>
#define BUTTON_COUNT 42
#define KEYPAD_OUTPUT_BEGIN 18
#define KEYPAD_OUTPUT_END 23
#define KEYPAD_INPUT_BEGIN 24
#define KEYPAD_INPUT_END 30
// Variables Globales Encodres
unsigned long time;
char buffer_temporal[10];
// 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 = 1;//_"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 = 1;// _"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 = 1;//_"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 = 1;// _"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 = 9;//_"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 = 9;// _"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.
int TAVEI = digitalRead(TAI);// _ "V" Variable, "E" Estado, "I" Interruptor.
int TAVEAI = 0;// _ _ _ _ _ _ _ _ "V" Variable, "E" Estado, "A" Anterior , "I" Interruptor.
//Variables Flaps
int FAL = 33; //_ _ _ _ _ "L" Led.
const int FPD = 34; //_ _ "P" Pulsador, "D" Down.
const int FPU = 35; //_ _ "P" Pulsador, "U" Up.
int FPUV = 0; //_ _ _ _ _ "V" Variable.
int FPUVA = 0; // _ _ _ _ "V" Variable, "A" Anterior.
int FPDV = 0; //_ _ _ _ _ "V" Variable.
int FPDVA = 0; // _ _ _ _ "V" Variable, "A" Anterior.
int FP = 0; // _ _ _ _ _ _"P" Posicion.
//Variables Freno de Estacionamiento
int FELP = 32; // _ _ _ _ _ _ _ _ "L" Led, "P" Peligro.
int FEI = 31; //_ _ _ _ _ _ _ _ _ "I" Interruptor.
int FEVEI = digitalRead(FEI);// _ "V" Variable, "E" Estado, "I" Interruptor.
int FEVEAI = 0;// _ _ _ _ _ _ _ _ "V" Variable, "E" Estado, "A" Anterior , "I" Interruptor.
//------------ Matriz de botones hasta SETUP
uint8_t keypad_button_pressed[BUTTON_COUNT];
uint32_t lastTrigger;
uint32_t math_calc_diff(uint32_t value1, uint32_t value2) {
if (value1 == value2) {
return 0;
}
if (value1 > value2) {
return (value1 - value2);
}
else {
// check for overflow
return (0xffffffff - value2 + value1);
}
}
void keypad_reset_output() {
// configure pull ups
digitalWrite(18, HIGH);
digitalWrite(19, HIGH);
digitalWrite(20, HIGH);
digitalWrite(21, HIGH);
digitalWrite(22, HIGH);
digitalWrite(23, HIGH);
}
void clear_buttons() {
for(int i=0; i < BUTTON_COUNT; i++) {
keypad_button_pressed[i] = 0;
}
}
void keypad_setup() {
// initialize the digital pin as an output:
pinMode(18, OUTPUT);
pinMode(19, OUTPUT);
pinMode(20, OUTPUT);
pinMode(21, OUTPUT);
pinMode(22, OUTPUT);
pinMode(23, OUTPUT);
keypad_reset_output();
pinMode(24, INPUT);
pinMode(25, INPUT);
pinMode(26, INPUT);
pinMode(27, INPUT);
pinMode(28, INPUT);
pinMode(29, INPUT);
pinMode(30, INPUT);
// configure pull ups
digitalWrite(24, HIGH);
digitalWrite(25, HIGH);
digitalWrite(26, HIGH);
digitalWrite(27, HIGH);
digitalWrite(28, HIGH);
digitalWrite(29, HIGH);
digitalWrite(30, HIGH);
}
// the loop() method runs over and over again,
// as long as the Arduino has power
void keypad_read_buttons() {
clear_buttons();
uint8_t y=0;
for(int i=KEYPAD_OUTPUT_BEGIN; i <= KEYPAD_OUTPUT_END; i++) {
keypad_reset_output();
digitalWrite(i, LOW);
uint8_t x=0;
for(int j=KEYPAD_INPUT_BEGIN; j <= KEYPAD_INPUT_END; j++) {
if (digitalRead(j) == LOW) {
uint8_t index = x+7*y; //OJO el numero de la ecuacion es la cantidad de columnas
keypad_button_pressed[index] = 1;
}
x++;
}
y++;
}
}
uint8_t keypad_button_is_pressed() {
for (int i=0; i < BUTTON_COUNT; i++) {
if (keypad_button_pressed[i]) {
return 1;
}
}
return 0; // no button pressed
}
String datos[42] = {
"H25","H09","H22","H20","H23","H21","G14",
"C26","A56","G18","G09","G13","G12","H01",
"C25","A55","H17","H18","H19","H20","H21",
"H24","H23","H22","H21","H20","H19","H18",
"B12","B11","A57","H14","H15","H16","H17",
"A58","H37","H38","H39","H40","H41","H42"
};
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);
pinMode(TAPT, INPUT_PULLUP);
//Setup Flaps
pinMode(FPU, INPUT_PULLUP);
pinMode(FPD, INPUT_PULLUP);
pinMode(FAL, OUTPUT);
//Setup Freno de Estacionamiento
pinMode(FEI, INPUT_PULLUP);
pinMode(FELP, OUTPUT);
//Setup matriz
// init_systicks();
keypad_setup();
}
void loop() {
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));
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;
}
//------Palanca de Gases Motor 2
if (digitalRead(PGM2_PCLK) == LOW){
PGM2_RE_T = time;
if (PGM2_RE_A == true){
PGM2_RE_P ++;
PGM2_RE_A = false;
PGM2_RE_B = false;
PGM2_RE_P = min(10, max(0, PGM2_RE_P));
sprintf(buffer_temporal, "C57%03u\r\n", PGM2_RE_P*99/10);
Serial.print(buffer_temporal);
}
}
if (digitalRead(PGM2_PDT)== LOW){
PGM2_RE_T = time;
if(PGM2_RE_B == true){
PGM2_RE_P --;
PGM2_RE_B = false;
PGM2_RE_A = false;
PGM2_RE_P = min(10, max(0, PGM2_RE_P));
sprintf(buffer_temporal, "C57%03u\r\n", PGM2_RE_P*99/10);
Serial.print(buffer_temporal);
}
}
if (time - PGM2_RE_T>10){
PGM2_RE_A = true;
PGM2_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));
sprintf(buffer_temporal, "C60%03u\r\n", PHM1_RE_P*99/10);
Serial.print(buffer_temporal);
}
}
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));
sprintf(buffer_temporal, "C60%03u\r\n", PHM1_RE_P*99/10);
Serial.print(buffer_temporal);
}
}
if (time - PHM1_RE_T>10){
PHM1_RE_A = true;
PHM1_RE_B = true;
}
//------Paso Helice Motor 2
if (digitalRead(PHM2_PCLK) == LOW){
PHM2_RE_T = time;
if (PHM2_RE_A == true){
PHM2_RE_P ++;
PHM2_RE_A = false;
PHM2_RE_B = false;
PHM2_RE_P = min(10, max(0, PHM2_RE_P));
sprintf(buffer_temporal, "C61%03u\r\n", PHM2_RE_P*99/10);
Serial.print(buffer_temporal);
}
}
if (digitalRead(PHM2_PDT)== LOW){
PHM2_RE_T = time;
if(PHM2_RE_B == true){
PHM2_RE_P --;
PHM2_RE_B = false;
PHM2_RE_A = false;
PHM2_RE_P = min(10, max(0, PHM2_RE_P));
sprintf(buffer_temporal, "C61%03u\r\n", PHM2_RE_P*99/10);
Serial.print(buffer_temporal);
}
}
if (time - PHM2_RE_T>10){
PHM2_RE_A = true;
PHM2_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));
sprintf(buffer_temporal, "C58%03u\r\n", PMM1_RE_P*99/10);
Serial.print(buffer_temporal);
}
}
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));
sprintf(buffer_temporal, "C58%03u\r\n", PMM1_RE_P*99/10);
Serial.print(buffer_temporal);
}
}
if (time - PMM1_RE_T>10){
PMM1_RE_A = true;
PMM1_RE_B = true;
}
//------Palanca de Mezcla Motor 2
if (digitalRead(PMM2_PCLK) == LOW){
PMM2_RE_T = time;
if (PMM2_RE_A == true){
PMM2_RE_P ++;
PMM2_RE_A = false;
PMM2_RE_B = false;
PMM2_RE_P = min(10, max(0, PMM2_RE_P));
sprintf(buffer_temporal, "C59%03u\r\n", PMM2_RE_P*99/10);
Serial.print(buffer_temporal);
}
}
if (digitalRead(PMM2_PDT)== LOW){
PMM2_RE_T = time;
if(PMM2_RE_B == true){
PMM2_RE_P --;
PMM2_RE_B = false;
PMM2_RE_A = false;
PMM2_RE_P = min(10, max(0, PMM2_RE_P));
sprintf(buffer_temporal, "C59%03u\r\n", PMM2_RE_P*99/10);
Serial.print(buffer_temporal);
}
}
if (time - PMM2_RE_T>10){
PMM2_RE_A = true;
PMM2_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
TAVEI = digitalRead(TAI);
if(TAVEI == 0 && digitalRead(TALN)== LOW && TAVEI != TAVEAI){Serial.println("C02");}
if(TAVEI != 0 && digitalRead(TALN) == HIGH && TAVEI != TAVEAI){Serial.println("C01");}
TAVEAI = TAVEI;
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);}
// Control F - Flaps
FPUV = digitalRead(FPU);
if (FPUV == HIGH && FPUVA != HIGH) {
Serial.println("C15");
FP --;
FP = min(4, max(0, FP));
}
delay(20);
FPUVA = FPUV;
FPDV = digitalRead(FPD);
if (FPDV == HIGH && FPDVA != HIGH) {
Serial.println("C14");
FP ++;
FP = min(4, max(0, FP));
}
FPDVA = FPDV;
if(FP == 0){digitalWrite(FAL, LOW);} else {digitalWrite(FAL, HIGH);}
// Control FE - Freno de Estacionamiento
FEVEI = digitalRead(FEI);
if(FEVEI == 0 && digitalRead(TALN)== HIGH && FEVEI != FEVEAI){Serial.println("C040"); digitalWrite(FELP, LOW);}
if(FEVEI != 0 && digitalRead(TALN) == HIGH && FEVEI != FEVEAI){Serial.println("C041");digitalWrite(FELP, HIGH);}
FEVEAI = FEVEI;
keypad_read_buttons();
// allow button processing only every 300ms (30 systicks)
if (keypad_button_is_pressed() && (math_calc_diff(time, lastTrigger) > 200)) {
lastTrigger = time;
for(int i=0; i < BUTTON_COUNT; i++) {
if (keypad_button_pressed[i]) {
Serial.println(datos[i]);
}
}
}
}
//FUNCION getChar() Pertenece al Tren de Aterrizaje
char getChar(){
while(Serial.available()==0);
return((char)Serial.read());
}
Agradezco toda la ayuda recibida en los demás hilos y mecionar a los que me han encontrado las soluciones: @Surbyte - @IgnoranteAbsoluto - @victorjam - @tauro0221 gracias a todos
y espero podamos mejorar mas esto y a lo mejor alguno mas se anima y lo encara a armar estoy a dispocicion para que me consulten.