Los «joysticks» o palancas de mando, son dispositivos de entrada que indican la posición o el ángulo al dispositivo que controla. Los mismos pueden ser digitales (solo indican estados de encendido y apagado) o análogos (indican posicion mediante potenciómetros). Hoy aprenderemos a usar Joysticks en Arduino.
Antes de empezar recomiendo leer los post acerca de lecturas análogas y la funcion map().
Como se mencionó anteriormente, Arduino ofrece la posibilidad de obtener lecturas de dispositivos análogos dando la posibilidad de conocer el estado o posición en la que se encuentra. Esta característica es aprovechable mediante la implementación de dispositivos de control como son los joysticks análogos.
Los joysticks análogos son dispositivos que, por lo general, constan de dos ejes los cuales controlan dos (2) potenciómetros que indicaran al dispositivo que maneja, la dirección que el usuario desea.
En esta experiencia trataremos de entender como utilizar joysticks en arduino y cómo aprovechar la información que brindan.
Para esta experiencia necesitaremos:
Antes de empezar a utilizar nuestro joystick debemos entender ciertos conceptos sobre su funcionamiento. Debemos entender como variaran las mediciones de acuerdo a la configuración que se utilice. Como ejemplo utilizaremos al potenciómetro vinculado al eje Y.
- Caso 1:
Donde:
- Y+: Pin superior del potenciómetro
- Y-: Pin inferior del potenciómetro
- Pin Análogo (A1): Punto de medición conectado a Pin Análogo 1
Los joysticks usan, por lo general, potenciómetros tipo B de 10kΩ , en los cuales el valor del voltaje es directamente proporcional al ángulo de giro con respecto al pin conectado a tierra (GND) , es decir entre mayor sea el ángulo con respecto a el pin conectado a tierra, mayor el voltaje en el punto de medición. Esto se debe a lo siguiente:
Donde:
- Vin: Voltaje de entrada (5v)
- Vout: Voltaje de salida en el punto de medición
- R1: Resistencia de positivo al punto de medición
- R2: Resistencia del punto de medición a GND
En este caso el valor de la resistencia R2 es equivalente al 5% de la resistencia total, por lo tanto la R1 equivale al 95% de la resistividad total, por lo que si tenemos una resistencia de 10 kΩ, R2 equivale a 500 Ω y R1 a 9,5 kΩ. Teniendo estos datos en cuenta y utilizando un divisor de voltaje podremos obtener el voltaje en el punto de medición:
Por lo que el voltaje en el punto de medición con las condiciones mencionadas equivale a 0.25 V que es igual al 5% de 5V (Vin).
A modo de prueba para relacionarnos con este dispositivo cargaremos el siguiente código a nuestra placa mediante ArduinoIDE:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
void setup(){ Serial.begin(9600); } void loop(){ int x= analogRead(A0);//Potenciometro de eje x int y= analogRead(A1);//Potenciometro de eje y int mapx=map(x,0,1023,1,-1);/*Se usa valor negativo en outMax ya que esperamos que el valor minimo de entrada sea en x-*/ int mapy=map(y,0,1023,-1,1);/*Se usa valor negativo en outMin ya que esperamos que el valor minimo de entrada sea en y-*/ Serial.print(mapx); Serial.print(" "); Serial.println(x); Serial.print(mapy); Serial.print(" "); Serial.println(y); Serial.println(" "); delay(5000); } |
El código leerá los pines A0 y A1 , mapeara los datos de -1 a 1, para reconocer la dirección e imprimirá los datos de x y luego los datos y, cada 5 segundos (5000 ms).
El siguiente esquema presenta la configuración a utilizar:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 |
0 519 0 527 0 517 0 527 0 518 1 1023 0 518 1 1023 0 518 -1 0 0 519 -1 0 -1 1023 0 528 -1 1023 0 527 1 0 0 528 1 0 0 528 1 0 0 1022 1 0 1 1023 1 0 1 1023 -1 1023 1 1023 -1 1023 1 1023 0 1022 -1 0 0 1022 -1 0 0 851 -1 0 -1 1023 -1 0 1 0 -1 0 1 0 -1 0 |
Prestar atención a las mediciones del joystick en estado de reposo ya que no todos los joysticks, en su estado de reposo, se mantienen en un punto medio por motivos de desgaste, fabricación o lo más común, ensamblaje; por lo que necesitaremos de estas medidas para ajustar los rangos de la función map para obtener los resultados deseados.
- Caso 2:
En este caso:
- Pin superior conectado a tierra (GND)
- Pin inferior conectado a Vin
Con esta configuración, los valores más bajos se esperan en Y+, ya que es en esta dirección que el ángulo de giro con respecto a tierra se hace menor. Aplicaremos los mismos conceptos utilizados en el caso 1, con el uso del siguiente diagrama:
Donde:
- Vin: Voltaje de entrada (5V para Arduino)
- Vout: Voltaje en el punto de medición
- R2: Valor resistivo desde Vout a GND
- R1: Valor resistivo de Vin a Vout
En este caso R1 equivale al 5% de la resistencia total por lo que R2 equivale al 95%, es decir, el valor R1 es de 500 Ω y el valor de R2 es de 9,5 kΩ. Recurriendo nuevamente a la fórmula de divisor de voltaje:
Para las condiciones dadas Vout equivale a 4,75 V, es decir el 95% de 5V
A modo de prueba cargaremos el siguiente código en nuestra placa mediante ArduinoIDE:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
void setup(){ Serial.begin(9600); } void loop(){ int x= analogRead(A0);//Potenciometro de eje x int y= analogRead(A1);//Potenciometro de eje y int mapx=map(x,0,1023,-1,1);/*Se usa valor positivo en outMax ya que esperamos que el valor maximo de entrada sea en x-*/ int mapy=map(y,0,1023,1,-1);/*Se usa valor positivo en outMin ya que esperamos que el valor maximo de entrada sea en y-*/ Serial.print(mapx); Serial.print(" "); Serial.println(x); Serial.print(mapy); Serial.print(" "); Serial.println(y); Serial.println(" "); delay(5000); } |
El código leerá los pines A0 y A1 , mapeara los datos de -1 a 1, para reconocer la dirección e imprimirá los datos de x y luego los datos y, cada 5 segundos (5000 ms).
El siguiente esquema presenta la configuración a utilizar:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 |
1 505 -1 496 1 505 -1 495 1 450 0 1022 1 451 0 1022 1 505 1 1023 1 505 1 1023 1 504 -1 0 -1 1023 1 1023 0 1022 -1 0 1 505 -1 0 1 0 -1 0 1 0 -1 0 1 186 0 1022 1 505 0 1022 1 506 1 1023 1 0 -1 204 1 505 -1 496 1 504 -1 496 |
Como se mencionó anteriormente, debemos tener en cuenta que valores se obtienen del joystick en reposo (3 primeras impresiones en mi caso), ya que no todos los joysticks en un estado de reposo están en un punto medio y como se habrán percatado la configuración también afecta a esto. Para corregir esto debemos modificar los valores en map, de la siguiente manera:
- Si el valor en reposo es menor a 512
Donde x es el valor de lectura en reposo
- Si el valor en reposo es mayor o igual a 512
Ya tocado estos puntos pasaremos a la utilización de los datos que obtenemos de los joysticks. En esta ocasión lo utilizaremos para controlar una matriz casera de LED de 3×3.
Una matriz de LED, es una configuración donde los ánodos y cátodos de todos los LEDs dentro del arreglo están conectados entre si , es decir los ánodos están conectados unos a otros en filas o columnas y los cátodos en filas o columnas. En nuestro caso armaremos una matriz de LED de 3×3 siguiendo el siguiente esquema:
y lo conectaremos a nuestra placa de la siguiente manera:
y cargaremos el siguiente código en nuestra placa:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
int filas[]={10,9,8}; int columnas[]={7,6,5}; byte statef[3]={4,2,1}; byte statec[3]={3,5,6}; void setup(){ Serial.begin(9600); for(int x6=0;x6 <3; x6++){ pinMode(filas[x6],OUTPUT); pinMode(columnas[x6],OUTPUT); } } void loop(){ int x= analogRead(A0); int y= analogRead(A1); Mcontrol(x,y); /*int x1= map(x,0,1023,2,0); //En caso de necesitar ajustar int y1= map(y,0,1023,2,0); //Utilizar esta parte delay(20); //y hacer los ajustes correspondientes Serial.println(x1); delay(20); Serial.println(y1); delay(20);*/ } void Mcontrol(int x2,int y2){ int mapx=map(x2,3,1020,2,0); int mapy=map(y2,3,1020,2,0); int h=2; for(int zz=0; zz<3;zz++){ byte mark=bitRead(statec[mapx],h); digitalWrite(columnas[zz],mark); byte mark2=bitRead(statef[mapy],h); digitalWrite(filas[zz],mark2); h=h-1; } } |
Si se ha configurado todo correctamente deberíamos obtener un resultado como el mostrado a continuación:
La pregunta aquí es, ¿Cómo esto funciona?
Para responder a esto debemos recordar que es un LED. Las siglas LED, significan Light-Emitting Diode (Diodo emisor de luz), y como todo diodo un LED solo permite el paso de corriente de ánodo a cátodo y solo bajo esta condición emitirán luz, ya que estarán en polarización directa permitiendo a los electrones recombinarse en los huecos del diodo y emitir energía en forma de fotones.
Podremos resumir el comportamiento de un LED con la siguiente tabla:
Ahora traduciendo esto a nuestra matriz, significaría que para encender un LED en la posición (x,y), debemos enviar una señal ALTA (HIGH o 1) en la fila «y» y una señal BAJA (LOW o 0) en la columna «x», ya que un pin configurado como salida (OUTPUT) con una señal LOW permite la entrada de corriente actuando como tierra . Esto permite que la corriente viaje del ánodo del led al cátodo del led.
Asumiendo que en nuestro arreglo, el LED de la esquina superior izquierda tiene la posición (0,0) y el LED de la esquina inferior derecha tiene la posición (2,2), podríamos resumir el comportamiento de la matriz en la siguiente tabla:
La siguiente animación muestra lo explicado, encendiendo los LED en diagonal, posiciones (0,0), (1,1), (2,2). Dale clic a la imagen para ver la animación.
En este caso se aplicó una técnica para ahorrar espacio, si bien no es necesaria en este proyecto quizás conocerla sea de ayuda en futuros proyectos. Esta técnica es el uso de un número en sistema decimal que representa una secuencia binaria(0 y 1), que pueden representar los estados en que deben estar una serie de pines. Para entender como hacer esta conversión recomiendo leer el artículo: BitMath del Aruduino playground donde se explica algunos pasos importantes de las operaciones con bits.
Si observamos a la función Mcontrol(x,y), la cual maneja el control de la matriz se podrá observar que utiliza dos arrays del tipo byte llamadas statec y state
1 2 |
byte statef[3]={4,2,1}; byte statec[3]={3,5,6}; |
Estas dos matrices contienen los estados correspondientes a cada posición (3 posiciones) tanto para fila (statef), como para columnas (statec), mediante un número decimal que representa la serie de 0 y 1 para cada posición. Para obtener ese número podremos usar la siguiente técnica. Usaremos como ejemplo la posición (0,0) cuyos valores son statef[0] y statec[0].
Una variable del tipo byte van de 0 a 255, es decir una secuencia ocho (8) ceros(0) o unos(1). Para la fila 1, según la tabla la secuencia a seguir es {1, 0,0}, si metemos esta secuencia para trabajarla como bits la ubicaremos en una tabla con 8 posiciones de derecha a izquierda, es decir el último valor de la secuencia a la primera casilla de derecha a izquierda y rellenando los espacios restante con ceros:
El valor de esta secuencia en decimal equivale a la suma del producto del número (0 o 1) por la potencia de dos (2) correspondiente a la posición en que se encuentre, es decir:
Ahora analicemos como esto es aplicado a la función mcontrol.
1 2 3 4 5 6 7 8 9 10 11 12 |
void Mcontrol(int x2,int y2){ int mapx=map(x2,3,1020,2,0); int mapy=map(y2,3,1020,2,0); int h=2; for(int zz=0; zz<3;zz++){ byte mark=bitRead(statec[mapx],h); digitalWrite(columnas[zz],mark); byte mark2=bitRead(statef[mapy],h); digitalWrite(filas[zz],mark2); h=h-1; } } |
La función utiliza las mediciones del joystick correspondiente al eje x y eje y, y los mapea de 2 a 0, para ser utilizados como índices en las matrices de estado. Además se puede observar una función llamada bitRead(), de la cual podemos leer más acerca de ella aquí. Esta función tiene la siguiente sintaxis:
Donde:
- X: el número de donde leer
- n: la posición a leer (0 para el valor más a la derecha)
Esta función devuelve el número (0 o 1) de la posición especificada. Como el valor más a la derecha es el último y nuestra matriz es de 3 x 3, solo utilizaremos 3 posiciones por lo que la función empieza a leer desde la casilla 2 hasta 0 y asignándolo a la fila y columna que corresponda, creando la secuencia necesaria para obtener un movimiento correspondiente al movimiento del joystick
Esto ha sido todo por hoy, esperemos que toda la información brindada sea de gran utilidad. Por favor cualquier duda, comentario, sugerencia o corrección no duden en expresarla.
Saludos.