viernes, 23 de agosto de 2013

Control remoto con arduino, wrt54g y android

Debido a que la compañía con la que tengo contratada la televisión de paga, quería cobrar una tarifa extra cada mes, por ponerme otro decodificador. Decidí buscar la manera de evitarme este gasto, la respuesta me la dio una pequeña placa electrónica llamada arduino. En conjunto con un router Linksys wrt54g y mi teléfono celular con android.
La idea es poder cambiar de canal  en el decodificador sin tener que ir al cuarto donde este se encuentra.
Por medio de dos interfaces diferentes, una pagina web alojada en el wrt54g y también por medio de una aplicación para android utilizando el bluetooth para enviar las señales. Las 2 funcionan independientemente de la otra, osea que podrán seguir este tutorial ya sea para solo la interfaz web o solo para la app de android. o para ambas. Por la misma razón este tutorial estará dividido en 3 partes.


WRT54G

para esta parte necesitaremos lo siguiente:

* un router linksys wrt54g ,obviamente , ¿quien esta escribiendo este tutorial?  Previamente flasheado con DD-WRT
* una tarjeta SD no mayor a 2G ( mas grande puede que no nos la reconozca )
* un cable IDE que ya no nos sirva ( separaremos 7 cables )
* cautin y soldadura
* una tira de pin-headers (opcional pues podemos soldar cables directamente) como estos:


*mucha paciencia

Empezaremos por  particionar nuestra tarjeta SD. Para esto podemos utilizar Gparted. Necesitamos al menos 2 particiones ya que el router nos montara la primera partición en /mmc durante el arranque y no podremos utilizarla para nuestro propósito. En mi caso utilice 3 particiones, la primera de 100Mb formateada con ext2, la segunda de 64Mb formateada como SWAP y la tercera el resto de la tarjeta formateada con ext2.
Una vez que tengamos nuestra SD particionada, procedemos a soldar los 7 cables extraídos del cable IDE.
Adjunto una imagen para poder consultar los pines posteriormente:




Ya teniendo nuestros cables soldados a la tarjeta SD,abrimos el router ( pueden buscar en Internet como abrirlo )
Una vez abierto procederemos a ubicar los puntos donde soldaremos los cables,estos puntos pueden variar dependiendo la version de nuestro router, en mi caso es V2.0 por lo que los pines se ubican en los siguientes puntos:


el primero CS ( chip select ) se encuentra en el led DMZ


Los siguientes tres puntos se encuentran en un pequeño componte ( RP3 ) con 6 pequeñas patas muy cerca de uno de los chips mas grandes, el primero DI ( data input ) es la primera patita del lado izquierdo, el segundo CLK ( clock ) es la segunda pata del lado derecho y el tercero  DO ( data output ) es la tercer pata del lado derecho:




Los últimos 3 puntos se encuentran en el puerto serial que también utilizaremos para comunicarnos con el arduino.
el primero GND ( tierra ) puede ser cualquiera de los dos ultimos pines del puerto serial ( pines 9 y 10 ) en los que conectaremos los cables 3 y 6. Por ultimo VDD que es el pin 1 del puerto serial ( el único cuadrado )


Antes de cerrar nuestro router, procedemos a soldar los pin-headers al puerto serial en los pines 3 y 5 que son Tx y Tr respectivamente y el pin 9 que es tierra ( solo necesitamos esos tres para poder crear una conexión serial con el arduino ).
Para verificar que la tarjeta SD fue reconocida correctamente por el router podemos prenderlo y prestar atención al led DMZ, si durante el arranque parpadea quiere decir que la detecto, y si tenemos suerte no tendremos que modificar nada en la interfaz web del router. para verificar que realmente esta activada la SD, accedemos a la interfaz web de DD-WRT y nos vamos al apartado de Administración.
Activamos el uso de MMC/SD y ponemos los pines GPIO en manual para configurarlos de la siguiente manera:



Una ves configurado esto, reiniciamos el router para verificar que todo este bien, para esto nos debe aparecer la cantidad de Mb libres y en uso. Y aprovechamos para activar el servidor SSH para poder instalar optware en nuestra tarjeta SD.
En la misma pestaña de Administracion le damos click a la pestaña Diagnostico, y en el cuadro de texto ponemos el siguiente comando:

mount /dev/mmc/disk0/part3

con esto nos montara automáticamente nuestra partición en /opt, en mi caso es la 3.
reiniciamos el router y verificamos que la partición se monto, entrando al router por SSH y ejecutando el comando "df"





Es hora de instalar OPTware. Nos ubicamos en el directorio /opt con el comando

cd /opt
y descargamos el instalador con el comando

wget http://wd.mirmana.com/prep_optware

lo ejecutamos con

sh  prep_optware 


En este punto podemos descansar un rato, pues la instalación tardara algún tiempo.
NOTA: El router debe tener acceso a Internet para poder descargar lo necesario.

Una vez finalizada la instalación de OPTware reiniciamos el router y esperamos uno o dos minutos para darle tiempo a OPTware de cargar y procedemos a instalar nuestro servidor web para montar la pagina que controlara al arduino.
Ejecutamos el comando

 ipkg list | grep http


Como pueden ver, e subrayado el paquete que nos interesa, es un servidor web con soporte para PHP que es el lenguaje que utilizaremos para mandar las ordenes al arduino.
Lo instalamos con el comando:

ipkg install php-thttpd


Una vez instalado necesitamos cambiar de puerto a la interfaz web de DD-WRT al puerto 81 para poder montar nuestro servidor en el puerto 80, esto se logra fácilmente con los comandos


nvram set http_lanport=81
nvram commit


Reiniciamos nuevamente el router y verificamos que la interfaz web cambio de puerto, abriendo en nuestro navegador web la dirección del router

htttp://192.168.1.1:81


Antes de iniciar nuestro servidor vamos a configurarlo, editando el archivo "/opt/etc/thttpd.conf"  ya sea con vi o podemos instalar nano ( ipkg install nano ).
Modificamos la opción

dir=/opt/share/www

para ubicar la raiz de nuestro servidor en el lugar donde queramos, en mi caso la deje en donde viene por default.
guardamos los cambios y ahora si es tiempo de ejecutar nuestro servidor con el comando


/opt//etc/init.d/S80thttpd start

 Ahora es tiempo de crear nuestro index, podemos hacerlo de varias maneras, con vi o nano por SSH, o como lo hice yo, lo edite en la PC para después mandarlo al router por SSH.

Descargar archivo index.php+imagenes

Contenido del archivo index.php







Descargamos las imágenes y el index. Procedemos a subir los archivos al router con el comando en la PC

scp /ruta/del/archivo/web.zip root@192.168.1.1:/opt/share/www 

Una vez copiado al router por SSH descomprimimos el archivo web.zip con el comando
( por SSH )


cd /opt/share/www
unzip web.zip
 
Entramos en el navegador a nuestra nueva web y veremos algo como esto:




Y con esto terminamos la parte del router.


Andoid

Para esta parte solo necesitamos un navegador web.
Bueno para crear nuestra app necesitamos entrar a appinventor con una cuenta google

http://appinventor.mit.edu/


creamos un nuevo proyecto y añadimos lo que necesitemos.
Solo ay que arrastrar lo que queremos de la columna "Palette"  a la columna "Viewer".



Es importante señalar que añadí el modulo para cliente bluetooth. Una vez tengamos todo lo que necesitamos le damos click en "open block editor" nos descargara un archivo el cual ejecutamos con  con iced tea.
Les dejo la captura de mis blocks




Lo importante es el botón de conectar. Como se puede observar en la imagen, cree una variable global llamada MAC con el mac adress de del dispositivo bluetooth al que nos vamos a conectar ( el que ira conectado al arduino ).
También podemos ver como configure el botón para conectarnos, los demás botones son solo envió de texto que procesaremos con el arduino.
Una vez terminada nuestra app podemos descargar la app a la pc, obtener el código QR para descargarla con el teléfono o bien conectar el teléfono a la pc e instalar la app directamente.
( podría dejarles el link de mi app pero solo funciona para mi bluetooth )


Arduino


Para esta parte necesitaremos lo siguiente:


* Una placa arduino ( yo tengo la UNO-R3 ) y su cable usb
* Un modulo bluetooth para arduino
* El resto de nusetros pin-headers ( opcional )
* Un receptor infrarojo
* Un led emisor infrarojo
* Una placa tipo protoboard como cualquiera de estas ( opcional )
* Soldadura y cautin ( opcional )
* Cable delgado
* El control remoto del aparato que vamos a controlar
* El programa ( IDE ) para crear sketch de arduino
* Las librerias IRremote para arduino

Vamos a armar nuestro circuito de la siguiente manera:




Explico mas o menos las conexiones por si no se aprecia bien en la imagen.


del lado izquierdo tenemos el receptor infrarojo, que se conecta a negativo y positivo respectivamente y el pin de salida se conecta al pin 11 del arduino.

Al centro en la parte superior tenemos el modulo bluetooth que se conecta a negativo y positivo respectivamente, Tx y Rx se conectan a los pines 5 y 6 del arduino respectivamente.

Por ultimo a la derecha tenemos el emisor infrarojo, el pin negativo del diodo se conecta a tierra y el positivo al pin 3 del arduino.

Ya tenemos nuestro circuito armado, ahora vamos a averiguar los códigos de las teclas del control remoto, para eso utilizaremos este sketch que encontré por la red.


------------------------------------------------------------------------------------------------------------


// If one keypress results in multiple codes being output, then
// change in IRremoteInt.h:
// #define _GAP 50000

#include

int RECV_PIN = 11;
IRrecv irrecv(RECV_PIN);
decode_results results;

void setup()
{
  Serial.begin(9600);
  irrecv.enableIRIn(); // Start the receiver
}

int c = 1;

void dump(decode_results *results) {
  int count = results->rawlen;
  Serial.println(c);
  c++;
  Serial.println("For IR Scope: ");
  for (int i = 1; i < count; i++) {
 
    if ((i % 2) == 1) {
      Serial.print("+");
      Serial.print(results->rawbuf[i]*USECPERTICK, DEC);
    }
    else {
      Serial.print(-(int)results->rawbuf[i]*USECPERTICK, DEC);
    }
    Serial.print(" ");
  }
  Serial.println("");
  Serial.println("For Arduino sketch: ");
  Serial.print("unsigned int raw[");
  Serial.print(count, DEC);
  Serial.print("] = {");
  for (int i = 1; i < count; i++) {
 
    if ((i % 2) == 1) {
      Serial.print(results->rawbuf[i]*USECPERTICK, DEC);
    }
    else {
      Serial.print((int)results->rawbuf[i]*USECPERTICK, DEC);
    }
    Serial.print(",");
  }
  Serial.print("};");
  Serial.println("");
  Serial.print("irsend.sendRaw(raw,");
  Serial.print(count, DEC);
  Serial.print(",38);");
  Serial.println("");
  Serial.println("");
}

void loop() {
  if (irrecv.decode(&results)) {
    dump(&results);
    irrecv.resume(); // Receive the next value
  }
------------------------------------------------------------------------------------------------------------


lo cargamos al arduino y con el control remoto presionamos alguna tecla, lo que buscamos son 2 cosas, la variable que contiene el código que queremos enviar, que se ve mas o menos así:


unsigned int raw[36] = {450,250,200,200,200,550,250,350,250,350,250,200,200,250,200,250,200,250,200,550,200,550,200,400,200,550,250,200,200,250,200,250,200,400,200,};

en algún editor de texto como kwrite o gedit guardamos las variables que vamos a utilizar pero las modificamos para que queden mas o menos así:


unsigned int uno[36] = {450,250,200,200,200,550,250,350,250,350,250,200,200,250,200,250,200,250,200,550,200,550,200,400,200,550,250,200,200,250,200,250,200,400,200,}; 


en este caso presione el botón 1 y a la variable le asigne el nombre "uno". Esto se logra  sustituyendo "raw" por el nombre que le queremos poner.

ahora buscamos el código para enviar las señales almacenadas en nuestras variables. Se ve mas o menos así:


irsend.sendRaw(raw,36,38);

Y al igual que con nuestras variables solo modificamos "raw" y ponemos el nombre de la variable que queremos enviar.
Ya que tenemos todos los datos necesarios. Vamos a cargar un nuevo sketch.

--------------------------------------------------------------------------------------------------------

#include
#include
SoftwareSerial blueToothSerial(6, 5);
//Inicia la definicion de las varables con los codigos del control remoto
IRsend irsend;
unsigned int vol_up[36] = {
450,250,150,250,200,550,250,350,200,400,250,200,200,250,200,250,200,250,200,550,200,550,200,400,200,550,200,250,200,400,200,250,200,250,200,};
unsigned int vol_do[36] = {
450,250,200,250,200,550,200,400,200,400,200,250,200,200,200,250,200,250,200,550,200,550,200,400,200,550,250,200,200,400,250,200,200,400,200,};
unsigned int ch_up[36] = {
450,250,200,250,200,550,200,400,200,400,200,250,200,250,200,250,150,250,200,550,200,550,250,400,200,550,200,250,200,550,200,250,200,250,150,};
unsigned int ch_do[36] = {
450,250,200,250,200,550,200,400,200,400,200,250,200,250,150,300,150,250,200,250,200,550,200,400,200,550,200,250,200,550,200,250,200,400,200,};
unsigned int un[36] = {450,250,200,200,200,550,250,350,250,350,250,200,200,250,200,250,200,250,200,550,200,550,200,400,200,550,250,200,200,250,200,250,200,400,200,};
unsigned int dot[36] = {450,250,200,250,200,550,200,400,200,400,200,250,200,200,200,300,150,250,200,250,200,550,200,400,200,550,200,250,200,250,200,250,200,550,200,};
unsigned int tre[36] = {450,250,200,250,200,550,200,400,200,400,200,250,200,250,200,250,150,250,200,550,250,500,250,350,250,550,200,250,200,250,200,200,200,750,200,};
unsigned int cua[36] = {500,200,200,250,200,550,200,400,200,400,200,250,250,200,200,250,200,200,200,250,200,550,200,400,200,550,250,200,200,250,200,400,200,250,200,};
unsigned int cin[36] = {450,250,200,250,200,550,200,400,200,400,200,250,200,250,200,200,200,250,200,550,200,550,250,350,250,550,200,250,200,200,200,400,200,400,200,};
unsigned int sei[36] = {450,250,200,250,200,550,200,400,200,400,200,250,200,250,200,250,150,250,200,250,200,550,200,400,200,550,250,200,200,250,200,400,200,550,200,};
unsigned int sie[36] = {450,250,200,200,200,550,250,350,250,350,250,200,200,250,250,250,150,250,200,550,200,550,200,400,200,550,250,200,200,250,200,400,200,700,250,};
unsigned int och[36] = {450,250,200,250,200,550,200,400,200,400,200,250,200,250,200,200,200,250,200,250,200,550,200,400,200,550,200,250,200,250,200,550,200,250,200,};
unsigned int nue[36] = {450,250,200,250,200,550,200,400,200,400,200,250,200,200,200,250,200,250,200,550,200,550,200,400,250,550,200,250,200,200,200,550,250,350,250,};
unsigned int z[36] = {450,250,200,250,200,550,200,400,200,400,200,250,200,250,150,250,200,250,200,250,200,550,200,400,200,550,200,250,200,250,200,250,200,200,200,};
unsigned int po[36] = {500,200,200,250,200,550,200,400,200,400,200,250,200,250,200,250,200,200,250,500,250,550,200,400,200,550,200,250,200,250,200,700,200,250,250,};
unsigned int las[36] = {450,250,200,250,200,550,200,400,200,400,200,250,200,200,200,250,200,250,200,250,200,550,200,400,200,550,200,250,200,250,200,550,200,550,200,};
//Terminamos de definir las variables


int RECV_PIN = 11; //el pin 11 recive la señal del receptor infrarojo
IRrecv irrecv(RECV_PIN);
decode_results results;
void setup()
{
Serial.begin(9600);
irrecv.enableIRIn(); // Start the receiver
blueToothSerial.begin(9600);
}
void loop()
{
char test;
while(1){
if(blueToothSerial.available()){
test = blueToothSerial.read();
ddwrt( test );
}
else if (Serial.available()){
test=Serial.read();
ddwrt(test);
}
}
}



void ddwrt(char test)
{
if( test == '1')
{
Serial.write("numero 1\n");
uno();
}
else if( test == '2')
{
Serial.write("numero 2\n");
dos();
}
else if ( test == '3' )
{
Serial.write("numero 3\n");
tres();
}
else if( test == '4' ){
Serial.write("numero 4\n");
cuatro();
}
else if( test == '5' ){
Serial.write("numero 5\n");
cinco();
}
else if( test == '6' ){
Serial.write("numero 6\n");
seis();
}
else if( test == '7' ){
Serial.write("numero 7\n");
siete();
}
else if( test == '8' ){
Serial.write("numero 8\n");
ocho();
}
else if( test == '9' ){
Serial.write("numero 9\n");
nueve();
}
else if( test == 'z' ){
Serial.write("z\n");
zero();
}
else if( test == 'o' ){
Serial.write("o\n");
power();
}
else if( test == 'l' ){
Serial.write("l\n");
last();
}
else if( test == 'u' ){
Serial.write("u\n");
chanmas();
}
else if( test == 'd' ){
Serial.write("d\n");
chanmenos();
}
else if( test == 'v' ){
Serial.write("v\n");
volmas();
}
else if( test == 'm' ){
Serial.write("m\n");
volmenos();
}
}
//insertamos el codigo para enviar las variables
void uno(){
irsend.sendRaw(un,36,38);
delay(40);
irsend.sendRaw(un,36,38);
delay(40);
irsend.sendRaw(un,36,38);
delay(40);
}
void dos(){
irsend.sendRaw(dot,36,38);
delay(40);
irsend.sendRaw(dot,36,38);
delay(40);
irsend.sendRaw(dot,36,38);
delay(40);
}
void tres(){
irsend.sendRaw(tre,36,38);
delay(40);
irsend.sendRaw(tre,36,38);
delay(40);
irsend.sendRaw(tre,36,38);
delay(40);
}

void cuatro(){
irsend.sendRaw(cua,36,38);
delay(40);
irsend.sendRaw(cua,36,38);
delay(40);
irsend.sendRaw(cua,36,38);
delay(40);
}
void cinco(){
irsend.sendRaw(cin,36,38);
delay(40);
irsend.sendRaw(cin,36,38);
delay(40);
irsend.sendRaw(cin,36,38);
delay(40);
}
void seis(){
irsend.sendRaw(sei,36,38);
delay(40);
irsend.sendRaw(sei,36,38);
delay(40);
irsend.sendRaw(sei,36,38);
delay(40);
}
void siete(){
irsend.sendRaw(sie,36,38);
delay(40);
irsend.sendRaw(sie,36,38);
delay(40);
irsend.sendRaw(sie,36,38);
delay(40);
}
void ocho(){
irsend.sendRaw(och,36,38);
delay(40);
irsend.sendRaw(och,36,38);
delay(40);
irsend.sendRaw(och,36,38);
delay(40);
}
void nueve(){
irsend.sendRaw(nue,36,38);
delay(40);
irsend.sendRaw(nue,36,38);
delay(40);
irsend.sendRaw(nue,36,38);
delay(40);
}
void zero(){
irsend.sendRaw(z,36,38);
delay(40);
irsend.sendRaw(z,36,38);
delay(40);
irsend.sendRaw(z,36,38);
delay(40);
}
void power(){
irsend.sendRaw(po,36,38);
delay(40);
irsend.sendRaw(po,36,38);
delay(40);
irsend.sendRaw(po,36,38);
delay(40);
}
void last(){
irsend.sendRaw(las,36,38);
delay(40);
irsend.sendRaw(las,36,38);
delay(40);
irsend.sendRaw(las,36,38);
delay(40);
}
void volmas(){
irsend.sendRaw(vol_up,36,38);
delay(40);
irsend.sendRaw(vol_up,36,38);
delay(40);
irsend.sendRaw(vol_up,36,38);
delay(40);
}
void volmenos(){
irsend.sendRaw(vol_do,36,38);
delay(40);
irsend.sendRaw(vol_do,36,38);
delay(40);
irsend.sendRaw(vol_do,36,38);
delay(40);
}
void chanmas(){
irsend.sendRaw(ch_up,36,38);
delay(40);
irsend.sendRaw(ch_up,36,38);
delay(40);
irsend.sendRaw(ch_up,36,38);
delay(40);
}
void chanmenos(){
irsend.sendRaw(ch_do,36,38);
delay(40);
irsend.sendRaw(ch_do,36,38);
delay(40);
irsend.sendRaw(ch_do,36,38);
delay(40);
}
--------------------------------------------------------------------------------------------------------------------


Lo cargamos al arduino, y ya tenemos todo listo, ponemos el emisor infrarrojo
frente al aparato que controlaremos.






y con un cable largo lo conectamos al arduino, yo utilice la linea telefónica que no utilizo.










ya teniendo todo armado, solo es cuestión de probar, en mi caso el router lo tengo como repetidor wifi de mi modem 2wire que es e que me provee de Internet. así que solo tengo que conectarme a la red wifi del router y presionar algún botón para probar.
En el caso del teléfono tenemos que vincular el bluetooth directamente desde los ajustes de android, y después ejecutar nuestra app para cambiar de canal.