Objetivos
Material requerido.
Un control como Dios manda
El control mediante Bluetooth es práctico porque siempre tenemos a mano un móvil con él, pero un poco errático y al final siempre nos deja la sensación de ser un tanto incomodo o de fundir las baterías del móvil demasiado deprisa y en el mejor de los casos, incómodo para estar un rato con él.
Por eso, y como siempre solemos comentaros, al final, alguien tenía que venir con una solución “como Dios manda” y por eso el mercado nos proporciona una impecable para controlar nuestros robots, motores o proyectos varios con un mando único, que a muchos nos resultará familiar: Un mando como el de la PlayStation 2 inalámbrico.
Estos mandos se pueden conseguir por poco dinero en estos momentos (Gran cosa las bajadas de precios de todos los componentes electrónicos) y tiene la enorme virtud de ser ergonómicos y muy cómodos de manejar, además de estar dotados de dos joysticks analógicos y como 14 botones incluidos.
Estos mandos vienen con un receptor inalámbrico, que podemos conectar a nuestros Arduinos y usarlos para recibir las señales de control, sin complicarnos mucho la vida, y un mando inalámbrico aparte muy similar al que habréis usado más de uno para jugar con la PS2 de Sony.
En esta sesión vamos a ver como conectar este tipo de mando inalámbrico a nuestros Arduinos y veréis que gracias a las librerías que nuevamente se ha trabajado Mr. Bill Porter, son pero que muy fáciles de implementar y usar.
Conectando el receptor remoto
Como estamos montando un Rover 4×4 vamos a utilizar un Arduino Mega como el que venimos usando en las sesiones anteriores, para que el que quiera pueda tener un robot funcional.
Cuando empecé a mirar en Internet la información de este tipo de mandos me pareció bastante confusa y lo que es peor me pasé un buen número de horas tratando de hacerlo funcionar sin éxito
El problema que puede ocurrir al conectar el receptor inalámbrico a tu Duino es que lo enchufes al revés (Yo desde luego lo hice y me dio una tarea que no veas hasta que me di cuenta) porque la información que hay en Internet es un poco oscura y hay que leer entre líneas para comprender de que va el tema. En cuanto lo conectas bien no hay pérdida posible.
En su lugar usad este diagrama, mirando desde le frontal del receptor (Desde donde no salen cables)
Tampoco es necesario que os compliquéis la vida entendiendo la utilidad de cada cable, ya que la librería que vamos a usar, se va a encargar de la gestión a condición de que definamos correctamente los pines.
Lo más cómodo es que conectéis los pines a vuestro Arduino en secuencia. En mi caso que uso un MEGA y la tabla de conexión seria así:
Conexión receptor PS2 a Arduino MEGA | |||||||||
---|---|---|---|---|---|---|---|---|---|
Receptor | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
Mega | 22 | 24 | 26 | 28 | 30 | 32 | 34 | 36 | 38 |
Pin | data | command | vibration | GND | 5V | Attention | Clock | – | acknoledge |
Si disponéis de un pin header plano o a 90º es ideal para esto, porque dispondréis de un peine de pines que se conecta directamente a los conectores de la Mega y el enganche es más fiable, porque están agrupados.
De nuevo, tenemos un par de pines que son GND y 5V, pero como el consumo es despreciable podemos poner en HIGH el pin 30 y en LOW el pin 28.
El programa de control
Lo primero es descargar la librería PS2X_lib y que la instaléis siguiendo el procedimiento habitual.
Como ya es costumbre de la casa, vamos a empezar con un programa mínimo para comprobar unos pocos botones y después iremos viendo el resto de los mandos disponibles.
Vamos a empezar incluyendo la librería y creando un controlador tipo para el manejo del mando.
#include <PS2X_lib.h> //for v1.6 PS2X ps2x; // Creamos la clase del mando PS2
Después vamos a definir el setup para que nos alimente el receptor con GND y 5V.
Serial.begin(57600); pinMode(28, OUTPUT) ; pinMode(30, OUTPUT); digitalWrite(28, LOW); //Set Ground for remote digitalWrite(30, HIGH); // Set 5V for remote delay(500);
Ya habíamos usado antes este truco de alimentar algo con un pin del Arduino, pero recordar que este no debe superar los 40 mA Vamos a hora a definir los pines que vamos a usar en la librería con la línea:
error = ps2x.config_gamepad(34,24,32,22, true, true) //(clock, command, attention, data, Pressures?, Rumble?)
Fijaros que es aquí donde especificamos que pines vamos a usar y a que funciones del mando a distancia corresponden, por eso elegí que fueran consecutivos (Recordad que la vagancia es muy valiosa si te ahorra trabajo inútil), pero podéis definir los que queráis en función del modelo de Arduino que estéis usando.
Y por último, en el setup, vamos a asegurarnos de que hay un mando disponible e imprimimos el tipo en caso afirmativo:
error = ps2x.config_gamepad(34,24,32,22, true, true); if(error == 0) Serial.println("Controlador encontrado y configurado"); type = ps2x.readType(); if ( type == 1) Serial.println("Hallado controlador DualShock.");
Después de esto, si hemos detectado correctamente el mando ya podemos pasar a controlar los botones que se pulsan, Pero antes tenemos que salir si no hemos detectado el mando:
String S, Sentido = "" ; if(error == 1) return; //Salir si no encontramos mando
Vamos a hacer un pequeño programa que detecte los botones de la mano izquierda: UP, DOWN, LEFT, RIGTH.
while(true) { ps2x.read_gamepad(); //Leemos el mando if(ps2x.Button(PSB_PAD_UP)) //Es TRUE mientras se pulsa el boton Serial.println("UP"); else if(ps2x.Button(PSB_PAD_DOWN)) Serial.println("DOWN"); else if(ps2x.Button(PSB_PAD_RIGHT)) Serial.println("RIGHT"); else if(ps2x.Button(PSB_PAD_LEFT)) Serial.println("LEFT"); else Serial.println("STOP"); }
Si volcáis este programa Prog_96_1 a vuestro Arduino, veréis que detecta con un mensaje en la consola el botón que pulsáis. Naturalmente la librería provee variables para todos los botones disponibles en el mando mediante la función ps2x.Button().
Botones de la izquierda | ||||
---|---|---|---|---|
Boton | UP | DOWN | RGHT | LEFT |
FLAG | PSB_PAD_UP | PSB_PAD_DOWN | PSB_PAD_RIGHT | PSB_PAD_LEFT |
Botones de la derecha | ||||
---|---|---|---|---|
Boton | Circle Red | Cross Blue | Square Pink | Green Triangle |
FLAG | PSB_RED | PSB_BLUE | PSB_PINK | PSB_GREEN |
Más los botones centrales, de SELCT ( PSB_SELECT) y Start (PSB_START) . Para los botones traseros tenemos:
Botones Traseros | ||||
---|---|---|---|---|
Boton | L1 | L2 | R1 | R2 |
FLAG | PSB_L1 | PSB_L2 | PSB_R1 | PSB_R2 |
Los botones de la izquierda, tienen además, la curiosidad de que detectan de modo analógico la presión que ejerces en ellos con valores entre 0 y 255, mediante el comando
ps2x.Analog( Flag )
Donde Flag representa cualquiera de los valores indicados en la primera table. Por ejemplo para imprimir la intensidad con que s epulsa el botón UP, bastaría con hacer:
Serial.println(ps2x.Analog(PSAB_PAD_UP), DEC);
Leyendo los joysticks del mando
Por ultimo para detectar el movimiento de los dos joysticks incluidos en el mando, tenemos que entender que al ser analógicos tenemos que leer de cada uno, dos valores, X e Y, tal y como hacíamos en una sesión previa Joystick y Servo.
Como el mando nos devuelve un valor entre 0 y 255, cuando el joystick esta en reposo en la posición central, la lectura que nos da es de 127 tanto en X como en Y. Espero que el siguiente gráfico os resulte sencillo:
Para leer los joysticks tenemos que leer independientemente la posición en X e Y de cada uno de ellos
Joystick Izquierdo | Joystick Derecho | |||
---|---|---|---|---|
Eje | X | Y | X | Y |
Flag | PSS_LX | PSS_LY | PSS_RX | PSS_RY |
Para leer los valores de los joysticks podemos usar el siguiente trozo de código:
Serial.print("Stick Values:"); Serial.print(ps2x.Analog(PSS_LY), DEC); Serial.print(","); Serial.print(ps2x.Analog(PSS_LX), DEC); Serial.print(","); Serial.print(ps2x.Analog(PSS_RY), DEC); Serial.print(","); Serial.println(ps2x.Analog(PSS_RX), DEC);
Ahora te puede parecer muy complicado pero es una tontería (En serio), simplemente el mando tiene tantos botones que hay que usar un montón de flags para definir cada uno, pero siempre es lo mismo.
La librería viene con un ejemplo magnifico de uso, pero tiene el inconveniente de que muestra todas las posibilidades a la vez y puede ser un poco avasallador, pero si vais a usar este mando desde luego os recomiendo que la echéis una ojeada.
Para aquellos que hayáis hecho el montaje como el de este ejemplo, os pongo el ejemplo corregido para que os funcione a la primera, en la que simplemente he adaptado el ejemplo incluido a nuestros pines. Prog_96_2.
Moviendo nuestro robot con el mando de la PS2
Solo nos queda ya montar un programa completo que incorpore el control de los motores y la dirección del movimiento respondiendo a las pulsaciones del mando a distancia.
Vamos a empezar usando los 4 botones de la izquierda como controles simples de dirección. Para ello tenemos que incluir un par de librerías. Una para el control de la controladora de motores y otra para el manejo del mando de la PS2:
#include <AFMotor.h> #include <PS2X_lib.h> //for v1.6 PS2X ps2x; // Creamos la clase del mando PS2 AF_DCMotor Motor1(1); // Instanciamos los motores AF_DCMotor Motor2(2); AF_DCMotor Motor3(3); AF_DCMotor Motor4(4);
Y definimos algunas variables que usaremos luego
int error = 0; // Para reconocer el mando byte type = 0; // Tipo del mando. Volveremos sobre ello int Vel = 180 ; // Define la velocidad base del Rover
En el setup, definimos los pines que alimenten el mando de la PS2
void setup() { Serial.begin(57600); pinMode(28, OUTPUT) ; pinMode(30, OUTPUT); digitalWrite(28, LOW); // Ground para el receptor digitalWrite(30, HIGH); // 5V para el receptor delay(500); // GamePad(clock, command, attention, data, Pressures?, Rumble?) error = ps2x.config_gamepad(34,24,32,22, true, true); if(error == 0) Serial.println("Controlador encontrado y configurado"); type = ps2x.readType(); if ( type == 1) Serial.println("Hallado controlador DualShock."); }
Fijaros en la línea
error = ps2x.config_gamepad(34,24,32,22, true, true);
Aquí es donde inicializamos el controlador de la PS2 con los pines que usamos y nos aseguramos de que no haya error, para indicar que ha encontrado un mando compatible. Y por último leemos el tipo de mando que ha encontrado, que puede ser de varios tipos, Tipo DualShock = 1 o tipo Guitar Hero = 2.
Por último el loop es una variante del programa Prog_76_1, al que añadimos el control de los movimientos del robot que ya vimos en las sesiones previas.
void loop() { if(error == 1) return; //Salir si no encontramos mando While(true) { ps2x.read_gamepad(); //Leemos el mando if(ps2x.Button(PSB_PAD_UP)) //Es TRUE mientras se pulsa el boton { Serial.println("UP"); Avance(); } else if(ps2x.Button(PSB_PAD_DOWN)) { Serial.println("DOWN"); Retroceso(); } else if(ps2x.Button(PSB_PAD_RIGHT)) { Serial.println("RIGHT"); giroDerecha();} else if(ps2x.Button(PSB_PAD_LEFT)) { Serial.println("LEFT"); giroIzquierda() ; } else { Serial.println("STOP"); Paro() ; } delay(500); // Para que no saque mensajes continuamente } }
Aquí os dejo el programa completo Prog_96_3.
Y un video con el resultado del robot montado y completo
Resumen de la sesión
<