Wednesday, April 3, 2013

Arduino + Android: Como controlar un Aire Acondicionado

Para mi primer proyecto de Arduino y Android decidí controlar mi aire acondicionado con receptor infrarrojo con una aplicación de android en mi celular desde cualquier lugar que me encuentre que tenga acceso a internet.

Video del resultado final




Este proceso lo realicé en un ambiente Windows pero es lo mismo para Linux.

Aquí les explicaré como lo logré.

Prerequisitos:
- Aire Acondicionado
- Control remoto del aire acondicionado (Tiene que funcionar para poder decodificar el IR)

Hardware: 
- Arduino MEGA 2560  - Lo compré aquí
- Arduino compatible Ethernet Shield  Lo compré aquí
- Small breadboard Lo compré aquí
Jumper Cables Lo compré aquí
(1) IR Receiver Diode Lo compré aquí
- (1) IR LED Lo compré aquí

Software:
- Arduino SDK - http://arduino.cc/en/main/software
- IR Remote Arduino Library - Descarga (Site oficial del autor de esta librería)


Ya estamos listos!

Primero añadimos (unzip) las librerias que descargamos la carpeta de libraries donde se instaló el SDK de Arduino. 
X:\...\arduino-1.0.4\libraries
Luego nos aseguramos que el Arduino este conectado a la computadora y que Windows haya instalado bien el driver.

Ahora tenemos que decirle al SDK que board y que puerto de comunicación utilizaremos.

Tools > Board > Arduino Mega 2560 or Mega ADK

Tools > Serial Port > (El puerto donde se haya detectado tu arduino)

Ahora tenemos que conectar el IR Receiver al Arduino de la siguiente manera:

Pin 1 va al pin 19 del Arduino.
Pin 2 va al pin de Ground.
Pin 3 va al pin de 5V.

Ilustración:

Luego copiamos este sketch en el Arduino SDK. 

/*
 * IRremote: IRrecvDump - dump details of IR codes with IRrecv
 * An IR detector/demodulator must be connected to the input RECV_PIN.
 * Version 0.1 July, 2009
 * Copyright 2009 Ken Shirriff
 * http://arcfn.com
 * JVC and Panasonic protocol added by Kristian Lauszus (Thanks to zenwheel and other people at the original blog post)
 */

#include <IRremote.h>

int RECV_PIN = 19;

IRrecv irrecv(RECV_PIN);

decode_results results;

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

// Dumps out the decode_results structure.
// Call this after IRrecv::decode()
// void * to work around compiler issue
//void dump(void *v) {
//  decode_results *results = (decode_results *)v
void dump(decode_results *results) {
  int count = results->rawlen;
  if (results->decode_type == UNKNOWN) {
    Serial.print("Unknown encoding: ");
  } 
  else if (results->decode_type == NEC) {
    Serial.print("Decoded NEC: ");
  } 
  else if (results->decode_type == SONY) {
    Serial.print("Decoded SONY: ");
  } 
  else if (results->decode_type == RC5) {
    Serial.print("Decoded RC5: ");
  } 
  else if (results->decode_type == RC6) {
    Serial.print("Decoded RC6: ");
  }
  else if (results->decode_type == PANASONIC) { 
    Serial.print("Decoded PANASONIC - Address: ");
    Serial.print(results->panasonicAddress,HEX);
    Serial.print(" Value: ");
  }
  else if (results->decode_type == JVC) {
     Serial.print("Decoded JVC: ");
  }
  Serial.print(results->value, HEX);
  Serial.print(" (");
  Serial.print(results->bits, DEC);
  Serial.println(" bits)");
  Serial.print("Raw (");
  Serial.print(count, DEC);
  Serial.print("): ");

  for (int i = 0; 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.println("");
}


void loop() {
  if (irrecv.decode(&results)) {
    Serial.println(results.value, HEX);
    dump(&results);
    irrecv.resume(); // Receive the next value
  }
}
Quedará algo así:


Oprimimos  (verify) para que se valide/compile el código y luego 
 (upload) para que suba a nuestro Arduino.

En este momento debes ir a al menú de Tools y abrir el Serial Monitor. Luego de que lo abras, con el control remoto del aire acondicionado apunta al IR Receiver y aprietas los botones uno a uno. Obtendrás algo así:


En este caso el control de mi AC resultó ser uno marca NEC con los siguientes codigos:

Decoded NEC: 80FF48B7 (32 bits) - AC ON
Decoded NEC: 80FFC837 (32 bits) - TEMP UP
Decoded NEC: 80FF58A7 (32 bits) - TEMP DOWN
Decoded NEC: 80FFD827 (32 bits) - TIMER

Ya tenemos una parte completada. Ahora hacemos otro sketch, esta ves para implementar el webserver que nos va a ayudar a ejecutar los comandos en el arduino. Para hacer esto utilicé y modifiqué el ejemplo de Web_Demo de la librería de Webduino.

Aqui debes asegurarte de definir las siguientes cosas:

-Tengas el EthernetShield ya instalado en tu MEGA 2560.
-Debes definir el MAC Address de tu Ethernet Shield y su IP.
-Debes definir el puerto donde el webserver estará corriendo.
-Debes cambiar los comandos infrarrojos por los que obtuviste.
-El IR LED debe estar en el arduino con el ánodo (negativo) en GRD y el cátodo (patita mas larga, positivo) en el pin #9.



* Web_Demo.pde -- sample code for Webduino server library */

/*
 * To use this demo,  enter one of the following USLs into your browser.
 * Replace "host" with the IP address assigned to the Arduino.
 *
 * http://host/
 * http://host/acOn
 *
 * This URL turns on the Air Conditioner
 * 
 */

#include "SPI.h"
#include "Ethernet.h"
#include "WebServer.h"
#include <IRremote.h>


// no-cost stream operator as described at 
// http://sundial.org/arduino/?page_id=119
template<class T>
inline Print &operator <<(Print &obj, T arg)
{ obj.print(arg); return obj; }

//IR Variable declaration
IRsend irsend;

// CHANGE THIS TO YOUR OWN UNIQUE VALUE
static uint8_t mac[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };

// CHANGE THIS TO MATCH YOUR HOST NETWORK
static uint8_t ip[] = { 192, 168, 1, X };

#define PREFIX ""

WebServer webserver(PREFIX, 80 ); //ASSIGN PORT HERE

// commands are functions that get called by the webserver framework
// they send the IR Code to the AC

void acOn(WebServer &server, WebServer::ConnectionType type, char *url_tail, bool tail_complete)
{
  if (type == WebServer::POST)
  {
    server.httpFail();
    return;
  }

  //server.httpSuccess(false, "application/acOn");
  server.httpSuccess();

  //Replace for your IR Code here. 
  irsend.sendNEC(0x80FF48B7, 32); //irsend.send(IRTYPE)(0x(IRCODE), (BITS)); 
  delay(40);
}

void tempUp(WebServer &server, WebServer::ConnectionType type, char *url_tail, bool tail_complete)
{
  if (type == WebServer::POST)
  {
    server.httpFail();
    return;
  }

  server.httpSuccess();
  
 //Replace for your IR Code here. 
  irsend.sendNEC(0x80FFC8A7, 32); //irsend.send(COMPANYIR)(0x(IRCODE), (BITS)); 
  delay(40);
 
}

void tempDown(WebServer &server, WebServer::ConnectionType type, char *url_tail, bool tail_complete)
{
  if (type == WebServer::POST)
  {
    server.httpFail();
    return;
  }

  server.httpSuccess();

  //Replace for your IR Code here.
  irsend.sendNEC(0x80FF5837, 32); // 
  delay(40);
}


void timer(WebServer &server, WebServer::ConnectionType type, char *url_tail, bool tail_complete)
{
  if (type == WebServer::POST)
  {
    server.httpFail();
    return;
  }

  server.httpSuccess();

  //Replace for your IR Code here.
  irsend.sendNEC(0x80FFD827, 32); // 
  delay(40);
}


void setup()
{

  Ethernet.begin(mac, ip);
  webserver.begin();

//Rename the commands to your liking
  webserver.addCommand("acon", &acOn);
  webserver.addCommand("tempup", &tempUp);
  webserver.addCommand("tempdown", &tempDown);
  webserver.addCommand("timer", &timer);
}

void loop()
{
  // process incoming connections one at a time forever
  webserver.processConnection();

  // if you wanted to do other work based on a connecton, it would go here
}



Oprimimos  (verify) para que se valide/compile el código y luego 
 (upload) para que suba a nuestro Arduino.


En este momento ya puedes manejar tu aire acondicionado desde el web browser de alguna computadora conectada a tu misma red. Con poner (IP simulado) http://192.168.1.20/acon  el aire acondicionado debe prender.

Ahora esto es opcional y bajo su reponsabilidad por las posibles consecuencias de seguridad que pueda traer. Para poder manejar el arduino de cualquier lugar en el internet necesitaremos hacer una regla de "Port Forward" en nuestro router. La misma debe contener el IP del arduino y el puerto escogido para el webserver. Les recomiendo que utilicen un puerto que no sea el 80.

Luego de que hagan esa regla van a poder acceder el webserver en el arduino desde el internet y no limitado a su network.

La aplicación de android no es realmente necesaria pues ya puedes controlar el aire desde tu web browser desde el internet. Pero es mas interesante (y se ve mas cool) tener un app para esto.

Soy un beginner programando en android asi que muchas de las cosas que hice lo mas probable se pueden hacer de una manera mas efectiva.

Aquí les dejo el projecto de la aplicación de Android: Descarga

Modifiquenla de acuerdo a su setup y ya!


Cualquier pregunta dejenme saber!


Friday, January 25, 2013

Instalación Oracle Instant Client 11g en Red Hat

Hoy tuve la necesidad de automatizar reportes que se generan utilizando información de varias bases de datos Oracle. El Oracle Instant Client nos permite accesar y manejar bases de datos utilizando sqlplus sin la necesidad de hacer una instalacion completa. 

Aquí verás como hacer esta instalación.


1. Primero necesitamos bajar el Instant Client y sqlplus de aqui.
- oracle-instantclient11.2-sqlplus-11.2.0.3.0-1.x86_64.rpm 
- oracle-instantclient11.2-basic-11.2.0.3.0-1.x86_64.rpm

2. Luego de que los bajes los instalas con este comando (tienes que ser root):
$ rpm -ilv oracle-instantclient11.2-sqlplus-11.2.0.3.0-1.x86_64.rpm oracle-instantclient11.2-basic-11.2.0.3.0-1.x86_64.rpm
3. Creamos un grupo específico para Oracle:
$ groupadd -g dba
4. Ahora creamos el usuario y le asignamos un password: 
**Nota: Si tu sistema es 32 bits elimina el numero 64 de todos los comandos que sometamos de este paso en adelante.
$ useradd -d /usr/lib/oracle/11.2/client64 -g "dba" oracle
$ passwd 
5. Modificamos el profile del usuario oracle (añade las lineas en negro):
vim ~oracle/.bash_profile

# Get the aliases and functions
if [ -f ~/.bashrc ]; then
        . ~/.bashrc
fi

# User specific environment and startup programs

PATH=$PATH:$HOME/bin
export LD_LIBRARY_PATH=/usr/lib/oracle/11.2/client64/lib
export TNS_ADMIN=/usr/lib/oracle/11.2/client64
export ORACLE_HOME=/usr/lib/oracle/11.2/client64
PATH=$ORACLE_HOME/bin:$PATH
export PATH
6. Creamos el archivo tnsnames.ora que es donde se definen las conexiones de las bases de datos:
$ vim /usr/lib/oracle/11.2/client/tnsnames.ora
Recuerda: Las entradas en este archivo deben tener este formato:
<AddressName> =
  (DESCRIPTION =
    (ADDRESS_LIST =
      (ADDRESS = (PROTOCOL = TCP)(HOST = <IP>)(PORT = <Puerto>))
    )
    (CONNECT_DATA =
      (SID = <SID>)
    )
  )
Entras al servidor con el usuario oracle e intentamos conexion a una base de datos con el siguiente comando:
$ sqlplus user/pass@DB

SQL*Plus: Release 11.2.0.3.0 Production on Fri Jan 25 14:30:01 2013

Copyright (c) 1982, 2011, Oracle.  All rights reserved.


Connected to:
Oracle Database 11g Enterprise Edition Release 11.1.0.7.0 - 64bit Production
With the Partitioning, OLAP, Data Mining and Real Application Testing options

SQL>

**Nota: Para tener la misma funcionalidad con el usuario root solo tienes que repetir el paso #5 en su .bash_profile.

Ya tenemos el Instant Client 11g de Oracle instalado y conectado a una base de datos!

Thursday, January 17, 2013

Como corregir error 'sqlplus: not found' definiendo la variable $ORACLE_HOME y añadiendola al $PATH

Heredé un servidor SunOS que tiene ya una instalación de Oracle a la cual quiero conectarme a través de command line usando 'sqlplus'. Cuando utilizé el comando me daba este error:


$ sqlplus 
sqlplus: not found
¿La causa? La variable de $ORACLE_HOME no estaba configurada. Aquí te voy a decir como solucionar ese problema.

1) Primero verificamos que tiene la variable de $ORACLE_HOME y confirmamos que esta vacía. De no estarlo entonces salta al paso 3.

$ $ORACLE_HOME
$
 2) Ahora necesitamos saber donde esta la instalación de Oracle. En mi caso estaba en /home/app/oracle/product/10.2.0.
**Nota: El ORACLE_HOME es un nivel antes de donde se encuentra el archivo llamado bin.
La parte en negro es el ORACLE_HOME:
/home/app/oracle/product/10.2.0/bin

Le asignamos el valor a la variable $ORACLE_HOME y le damos export.

$ ORACLE_HOME=/home/app/oracle/product/10.2.0
$ export ORACLE_HOME
3) Ahora lo añadimos a nuestro $PATH:


$ PATH=$ORACLE_HOME/bin:$PATH

Ya podemos intentar conectarnos con el comando 'sqlplus'


$ sqlplus

SQL*Plus: Release 10.2.0.4.0 - Production on Thu Jan 17 16:15:39 2013

Copyright (c) 1982, 2007, Oracle.  All Rights Reserved.

Enter user-name:


Thursday, December 20, 2012

Script: Como borrar archivos de diferentes directorios mas viejos de cierta fecha.

Este script es sencillo pero muy útil. Para ahorrar espacio en el servidor necesito borrar todos los archivos de hace más de 3 dias en diferentes directorios, diariamente.

Este script me permite hacer esto automáticamente.


#!/bin/bash
PATHS="/path/to/dir1 /path/to/dir2 /path/to/dir3" 
find $PATHS -mtime +3 -ls -exec rm {} \; 

En la variable $PATH ponemos todos los directorios que deseamos limpiar separados por un espacio. Luego el comando find se encargara de ir directorio por directorio listando (-ls) todos los archivos mayores de tres dias (-mtime +3). El resultado de ese ls (los nombres de nuestros archivos) serán eliminados por -exec rm{}.

Puedes modificar el tiempo de acuerdo a tu necesidad, por ejemplo -mtime +0 eliminaria todos los archivos que no sean del dia corriente.

Wednesday, December 12, 2012

Script: Automatizar envio de archivos por FTP

"Si tienes que hacer una tarea más de una vez, haz un script".  

Aqui verás un ejemplo de Shell Script que envía por FTP todos los archivos del día de hoy  encontrados en una carpeta en específico.

#!/bin/sh

#IP donde te quieres conectar
HOST='192.168.0.10'

#Usuario de conexion
USER='ftpuser'

#Contrasena del usuario
PASSWD='ftppass'

#Path donde quieres buscar los archivos
PATH='/home/ftpuser/reports'

#Path donde los depositaras
DESTINATION='/destinationserver/destinationfolder'


#Busca todos los archivos en $PATH donde la fecha de modificacion (-ctime) sea# 0 = hoy. Por cada archivo que encuentres (while read LINE).
#Conectate al host y deposita el archivo (put $LINE)

find $PATH -type f -ctime 0 | while read LINE  
do
        ftp -n $HOST <<END_SCRIPT
        quote USER $USER
        quote PASS $PASSWD
        cd $DESTINATION
        put $LINE
        quit
END_SCRIPT
done
Recuerda ajustar los niveles de seguridad del script de acuerdo a tu necesidad y ponerlo en tu crontab.



Friday, November 30, 2012

SunOS: Como crear un usuario con acceso a FTP solamente y limitado a su home folder.

Quizás en algún momento necesites que algún extraño entre a tu servidor a recoger unos archivos. Por razones de seguridad no quieres que ese extraño ejecute comandos ni que acceda archivos que no le competen. Por eso hoy te voy a mostrar como crear un usuario que tenga acceso a FTP *solamente* en SunOS . 

1. Creamos el usuario.
useradd -m ftpuser
2. Le asignamos un password
passwd ftppassword
3. Necesitamos asignarle un shell al usuario que no le permita ejecutar comandos pero si que le permita hacer login. Por lo que le vamos a asignar /bin/false
usermod -s /bin/false ftpuser
4. Hay que asegurarnos que el shell que le asignamos se encuentre en el grupo de los shells autorizados a hacer login. Así que corremos el próximo comando, editamos el archivo /etc/shells  y añadimos /bin/false (si es que no se encuentra).

vi /etc/shells
/bin/bash
/bin/csh
/bin/hash
/bin/jsh
/bin/ksh
/bin/pfcsh
/bin/pfksh
/bin/pfsh
/bin/remsh
/bin/rksh
/bin/rsh
/bin/sh
/bin/ssh
/bin/tcsh
/bin/zsh
/bin/ftpaccess
/sbin/sh
# add this line
/bin/false
*Nota: Si el archivo de /etc/shells no existe puedes crearlo pero tienes que asegurarte que incluyas todos los shells en tu sistema pues los shells que no estén aquí no podrán hacer login al sistema.

5. Vamos a restringir el usuario a su home folder mediante este comando:

ftpconfig -d /home/ftpuser
6. Tenemos que editar el archivo de ftpaccess  para decirle que usuario vamos a estar limitando.

vi /etc/ftpd/ftpaccess
Obtendrás esto:

# ident "@(#)ftpaccess  1.1     01/06/26 SMI"
#
# FTP server configuration file, see ftpaccess(4).
#

class           realusers       real            *
class           guestusers      guest           *
class           anonusers       anonymous       *

loginfails      3
passwd-check    trivial         warn
private         no
keepalive       yes
shutdown        /etc/ftpd/shutdown.msg

# email         user@hostname
guestuser       ftpuser
# timeout       idle            300

banner          /etc/ftpd/banner.msg
greeting        brief
message         /etc/ftpd/welcome.msg   login
message         .message                cwd=*
readme          README*                 login
readme          README*                 cwd=*

chmod           no              anonymous
delete          no              anonymous
overwrite       no              anonymous
rename          no              anonymous
umask           no              anonymous

compress        yes             realusers guestusers anonusers
tar             yes             realusers guestusers anonusers
...
Hay que asegurarnos que la línea  de "class guestusers guest *" se encuentre en el archivo, que la línea de "guestuser username" no este comentada y que en vez de username diga el nombre del usuario que queremos limitar "guestuser ftpuser". Salvamos el archivo.



7. Ahora corremos este comando para permitir el uso del file ftpaccess.

in.ftpd -a
Ya terminamos. Ahora solo resta hacer dos pruebas.

Prueba 1: Conexión mediante FTP para asegurarnos que no pueda acceder nada mas que su Home Folder.

ftp 1.1.1.1.1
Connected to 1.1.1.1.1
Name (1.1.1.1.1:root): ftpuser
331 Password required for ftpuser.
Password:
230 User ftpuser logged in.  Access restrictions apply.
Remote system type is UNIX.
Using binary mode to transfer files.
ftp> pwd
257 "/" is current directory.
ftp> cd ..
250 CWD command successful.
ftp> pwd
257 "/" is current directory.
ftp>
Podemos ver que mi directorio actual dice "/" en vez de "/home/ftpuser"

Prueba 2: Conexión mediante Putty.

Intente conectarse al servidor mediante Putty con las credenciales del usuario de ftp. Al poner las credenciales Putty se cerrará evitando que el usuario pueda ejecutar comandos directo en el command line. Esto es gracias al shell /bin/false.

Terminamos! 


Friday, October 19, 2012

Bash Scripts no corren en crontab pero si manualmente desde el Command Line.

Pues llevo desde las 8 de la mañana tratando de decifrar porque unos scripts corren bien desde el command line pero dan error desde el crontab.

Primero verifica que el script tenga permisos de ejecución. Si los tiene puede que sea lo siguiente.

Los scripts que se ejecutan desde el crontab corren en un subshell que NO hereda los valores de tus environmental variables. Lo que significa que la variable $PATH de ese subshell solamente mira a "/usr/bin:/bin:" por lo que si estas corriendo una aplicación que depende del path de java (como en mi caso) nunca va a ejecutarse.

Esto se corrige de la siguiente manera.

1. Desde el command line buscamos el valor de la variable $PATH
# echo $PATH
PATH=/usr/lib64/qt-3.3/bin:/usr/NX/bin:/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin:/root/bin:/usr/bin/java
2. Ahora editamos el crontab, copiamos el valor de la variable PATH y lo pegamos en la primera linea.

[root@server test]# crontab -e
PATH=/usr/lib64/qt-3.3/bin:/usr/NX/bin:/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin:/root/bin:/usr/bin/java

#Automatic Jobs
34 10 * * * /opt/Test.sh > /opt/Job.log
01 4 * * * /opt/Test2.sh > /opt/Test.log
De esta manera nos estamos asegurando que todos los cronjobs que se corran tengan el $PATH definido.

Ya terminamos, guarda el archivo y ya está! Haz una prueba!