Translate

lunes, 2 de abril de 2018

Arduino para activación de alarma automáticamente al llegar a casa

Muchos de nosotros tenemos alarma en casa, o sistemas de grabación o alguna tarea que queremos automatizar al llegar al domicilio o al irnos. Gracias a esos dispositivos conocidos como arduino esta tareas va a ser algo muy sencillo.

El articulo que hoy os dejo es un tutorial de construcción de un arduino para automatizar estas tareas.

Vamos a trabajar con un arduino mega, la verdad que por el tamaño del programa serviría el arduino one (el normal) pero va muy justo de memoria y me dió muchos problemas de estabilidad. Sin embargo lo probé con el arduino mega y no llega al 20% de ocupación del equipo y lo hace muy estable. Así que vamos a descartar la opción del arduino normal y vamos a escoger el mega directamente.



Así mismo precisaremos una tarjeta de red ethernet para este dispositivo, conocida como Arduino Ethernet Shield.

Además precisaremos un relé que será el que active físicamente la alarma o  lo que sea que queramos controlar.

Estos 3 dispositivos los podemos comprar en aliexpress en menos de 12€

Además de este material podría ser necesario una tarjeta micro SD si queréis enviar algún comando por red a algún servidor etc... y un led verde y uno rojo si queréis que se encienda una luz según el estado del dispositivo para que visualmente sepáis en que estado está... luego os lo explicaré mas en detalle.

El conexionado es sencillo, la tarjeta de red se monta encima del arduino y el relé tiene 3 pines. Dos son de alimentación que se cogerá directamente del arduino del pin de 5 voltios. Y el ultimo pin del relé es el de control que se conectará a una de las salidas digitales.





Como podéis observar le he puesto 2 leds para saber el estado del equipo. Y desde el relé he sacado 2 cables al botón de control inalámbrico de mi alarma. En esté caso la alarma es de XIOAMI pero sería aplicable a cualquier otra que tenga un botón o mando de activación donde poder puentear el botón de ON/OFF.

Podéis ver un vídeo de su funcionamiento:



La configuración que tiene es un clic para activar la larma y doble click para desactivarla. Pero podéis cambiar en el código del arduino esto dejar un click simple siempre o lo que preciseis.

Además comentar que he implementado también la opción de que envíe un archivo con un comando de msdos a un ftp. Esto es opcional y si no lo vais a usar es mejor comentar este código en el archivo del arduino. La idea es que además de la activación de la alarma físicamente podamos enviar algún comando a un servidor de red que lo ejecute. Yo dejo un archivo con el comando que quiero ejecutar en un FTP de un servidor de mi red interna de casa y en ese servidor tengo un programa que monitoriza continuamente la carpeta, en cuanto aparece en ella un archivo lo abre ejecuta su contenido y después lo borra de dicha carpeta. Así este pequeño programa va a interpretar todas las ordenes que provengan del arduino como si se tratase de comandos de msdos. Por ejemplo me sirve para dar orden a las cámaras de grabación de que comiencen a grabar o se muevan etc...

El código que he utilizado es este:
#include <ICMPPing.h>
#include <util.h>
#include <SD.h>
#include <SPI.h>
#include <Ethernet.h>
#include <avr/wdt.h>
#include <Arduino.h>

#define FTPWRITE

/* Parametros conexion a FTP */
IPAddress server(172, 16, 0, 2);
static char* User_of_FTP = "USER arduino_cams" ;
static char* Password = "PASS admin";
const int port = 21;


/* Parametros control */
#define OUPUT_PIN_LED_RED 8 // LED rojo, alarma conectada
#define OUPUT_PIN_LED_GREEN 9 // LED verde, alarma desconectada
#define OUPUT_PIN_ALARM 7 // Control del relé
int button_delay = 150; // Tiempo de espera de simulacion de doble click para el bonton inalambrico

/* Parametros internos del programa */
bool _FIRST_TIME_AWAY = true;
bool _FIRST_TIME_IN = true;
int resetPin = 2;
int counter = 0;
long long _time = 0;
char buffer [128];
char outBuf[128];
char outCount;
char* fileName = "file.txt";
String res ;
SOCKET pingSocket = 0;
File fh;
ICMPPing ping(pingSocket, (uint16_t)random(0, 255));
EthernetClient client;
EthernetClient dclient;

/* ----------------------------------------------------------------------------------------- */
/* ----------------------------------------------------------------------------------------- */
/* ----------------------------------------------------------------------------------------- */
void setup()
{
  pinMode(OUPUT_PIN_LED_RED, OUTPUT);
  pinMode(OUPUT_PIN_LED_GREEN, OUTPUT);
  pinMode(OUPUT_PIN_ALARM, OUTPUT);

  /* Siempre iniciamos con LED en verde y suponiendo alarma desactivada */
  digitalWrite(OUPUT_PIN_LED_GREEN, HIGH);
  digitalWrite(OUPUT_PIN_ALARM, HIGH);

  /* Pasamos el boton reset de arduino al reset de la ethernet */
  digitalWrite(2, HIGH);
  delay(200);
  pinMode(2, OUTPUT);
 
  /* Conexion a la tarjeta Ethernet*/
  Serial.begin(9600);
  digitalWrite(10,HIGH);

  /* Condiguracion de la SD */
  if(SD.begin(4) == 0)
  {
    Serial.println(F("Fallo en el inicio de la SD"));       
    digitalWrite(resetPin, LOW);
  }
  else
  {
    Serial.println(F("SD iniciada correctamente"));       
  }

  /* Levantamos la tarjeta Ethernet por DHCP*/
  byte mac[] = { 0x90, 0x89, 0x88, 0x88, 0x89, 0x90 }; // Se puede cambiar la mac del arduino cambiado estos parametros
  if (Ethernet.begin(mac) == 0)
  {
    Serial.println("Ha fallado el DHCP a la hora de asignar IPs");
    digitalWrite(resetPin, LOW);
  }
  digitalWrite(10,HIGH);
  delay(200);

  /* Impresion por pantalla de la IP recibida por DHCP */
  Serial.print("IP del arduino ");
  Serial.println(Ethernet.localIP());
}

/* ----------------------------------------------------------------------------------------- */
/* ----------------------------------------------------------------------------------------- */
/* ----------------------------------------------------------------------------------------- */
void loop()
{
  IPAddress ip_1( 172, 16, 0, 29 );              // IP 29 Android
  IPAddress ip_2( 172, 16, 0, 21 );              // IP 21 iPhone
  /* Se pueden añadir tantas ips como moviles a vigilar tengamos, habria que añadirlas tambien abajo en el if */
  IPAddress tester( 8, 8, 8, 8 );                // IP de test, solo activará la alarma si hay internet por si la wifi está caida o con mal funcionamiento evitamos activaciones inecesarias

  if((_ping(ip_1)) || (_ping(ip_2)))             // Ping a las ips definidas unas pocas lineas mas arriba ((_ping(ip_1)) || (_ping(ip_2)) || (_ping(ip_3)) || (_ping(ip_4)))
  {
     _time = millis();                           // Guardamos en la variable el tiempo actual en el que estamos
   
     Serial.println("Hay gente en casa...");     // Imprimimos en pantalla el mensaje
   
     if(_FIRST_TIME_IN)                          // Comprobamos que es la primera vez que hay alguien en casa
     {
        _FIRST_TIME_AWAY = true;                 // Reinicializamos la variable de primera vez que no hay nadie en casa a TRUE
        _FIRST_TIME_IN = false;                  // Reinicializamos la variable de primera vez que hay alguien en casa a FALSE
        digitalWrite(OUPUT_PIN_LED_GREEN, HIGH); // Encedemos el led Verde
        digitalWrite(OUPUT_PIN_LED_RED, LOW);    // Apagamos el led Rojo
        disarm_alarm();                          // Llamamos a la funciona de desactivar la alarma por rele
        res = "\"C:\\Script_Synology Surveillance Station Home Mode - Windows 10 32bits\\home_mode_on.bat\""; // Comando de activacion de alarma para grabacion de camaras
        Send_To_FTP(res);                        // Envio al FTP del comando anterior para su ejecución (Si no usamos FTP este punto se puede comentar)
     }
  } else if((millis()-_time > 420000) && (_ping(tester))) // Si el ping falla a todos los equipos para decir que no hay nadie en casa
  {                                                       // vamos a comprobar que no hay nadie durante x milisegundos (ej. 7 min = 420000)
                                                          // asi evitamos que por una breve desconexion del movil no se active la alarma
                                                          // y que además con la ip de test verificaremos que la red funciona bien y que internet no está caido                                                       
    Serial.println("No ha nadie en casa...");    // Imprimimos en pantalla el mensaje
 
    if(_FIRST_TIME_AWAY)                         // Comprobamos que es la primera vez que no hay nadie en casa
    {
      _FIRST_TIME_AWAY = false;                  // Reinicializamos la variable de primera vez que no hay nadie en casa a FALSE
      _FIRST_TIME_IN = true;                     // Reinicializamos la variable de primera vez que hay alguien en casa a TRUE
      digitalWrite(OUPUT_PIN_LED_RED, HIGH);     // Encedemos el led Rojo
      digitalWrite(OUPUT_PIN_LED_GREEN, LOW);    // Apagamos el led Verde
      arm_alarm();                               // Llamamos a la funciona de activar la alarma por rele
      res = "\"C:\\Script_Synology Surveillance Station Home Mode - Windows 10 32bits\\home_mode_off.bat\""; // Comando de desactivacion de alarma para grabacion
      Send_To_FTP(res);                          // Envio al FTP del comando anterior para su ejecución (Si no usamos FTP este punto se puede comentar)
    }
  }
  delay(2000);                                   // Esperamos 2 segundos para volver a iniciar el bucle
}

/* ----------------------------------------------------------------------------------------- */
/* ----------------------------------------------------------------------------------------- */
/* ----------------------------------------------------------------------------------------- */
bool Send_To_FTP(String payload)
{
  File dataFile = SD.open("file.txt", FILE_WRITE);
  if (dataFile)
  {
    dataFile.seek(0);
    for(int j=0; j<190; j++)
    {
      dataFile.print(" ");
    }
    dataFile.seek(0);
    dataFile.print(payload);
    dataFile.close();
    Serial.println("Archivo abierto y escrito correctamente en la SD");
    if(doFTP()){ Serial.println(F("FTP OK")); }
    else
    {
      Serial.println(F("Fallo de FTP"));
      digitalWrite(resetPin, LOW);
    }
  }
  else
  {
    Serial.println("Error abriendo file.txt");
    digitalWrite(resetPin, LOW);
  }
}

/* ----------------------------------------------------------------------------------------- */
/* ----------------------------------------------------------------------------------------- */
/* ----------------------------------------------------------------------------------------- */
bool _ping(IPAddress _target)
{
  bool ans = false;
  ICMPEchoReply echoReply = ping(_target, 1);
  if (echoReply.status == SUCCESS)
  {
    ans = true;
    Serial.print("El ping a la IP es OK - ");
    sprintf(buffer,
            "Respuesta num [%d] desde: %d.%d.%d.%d: bytes=%d time=%ldms TTL=%d",
            echoReply.data.seq,
            echoReply.addr[0],
            echoReply.addr[1],
            echoReply.addr[2],
            echoReply.addr[3],
            REQ_DATASIZE,
            millis() - echoReply.data.time,
            echoReply.ttl);
  }
  else
  {
    ans = false;
    sprintf(buffer, "El ping a la IP es KO - %d", echoReply.status);
  }
  Serial.println(buffer);
  return ans;
}


/* ----------------------------------------------------------------------------------------- */
/* ----------------------------------------------------------------------------------------- */
/* ----------------------------------------------------------------------------------------- */
void arm_alarm()
{
  digitalWrite(OUPUT_PIN_ALARM, LOW);
  delay(button_delay);
  digitalWrite(OUPUT_PIN_ALARM, HIGH);
}

/* ----------------------------------------------------------------------------------------- */
/* ----------------------------------------------------------------------------------------- */
/* ----------------------------------------------------------------------------------------- */
void disarm_alarm()
{
  digitalWrite(OUPUT_PIN_ALARM, LOW);
  delay(button_delay);
  digitalWrite(OUPUT_PIN_ALARM, HIGH);
  delay(button_delay);
  digitalWrite(OUPUT_PIN_ALARM, LOW);
  delay(button_delay);
  digitalWrite(OUPUT_PIN_ALARM, HIGH);
}

/* ----------------------------------------------------------------------------------------- */
/* ----------------------------------------------------------------------------------------- */
/* ----------------------------------------------------------------------------------------- */
byte doFTP()
{
  Serial.println("Iniciando subida al FTP...");
  #ifdef FTPWRITE
  fh = SD.open(fileName,FILE_READ);
  #else
  SD.remove(fileName);
  fh = SD.open(fileName,FILE_WRITE);
  #endif

  if(!fh)
  {
    Serial.println(F("Fallo al Abrir la SD"));
    return 0; 
  }

  #ifndef FTPWRITE
  if(!fh.seek(0))
  {
    Serial.println(F("Fallo Rewind"));
    fh.close();
    return 0; 
  }
  #endif

  Serial.println(F("SD abierta..."));

  if (client.connect(server,21)) {
    Serial.println(F("Comando de conexion"));
  }
  else {
    fh.close();
    Serial.println(F("Fallo en el comando de conexion"));
    return 0;
  }
  if(!eRcv()) return 0;
  client.println(F("USER arduino"));
  client.println(User_of_FTP);
  if(!eRcv()) return 0;
  client.println(Password);
  if(!eRcv()) return 0;
  client.println(F("SYST"));
  if(!eRcv()) return 0;
  client.println(F("Type I"));
  if(!eRcv()) return 0;
  client.println(F("PASV"));
  if(!eRcv()) return 0;
  char *tStr = strtok(outBuf,"(,");
  int array_pasv[6];
  for ( int i = 0; i < 6; i++)
  {
    tStr = strtok(NULL,"(,");
    array_pasv[i] = atoi(tStr);
  }
  unsigned int hiPort,loPort;
  hiPort = array_pasv[4] << 8;
  loPort = array_pasv[5] & 255;
  Serial.print(F("Data port: "));
  hiPort = hiPort | loPort;
  if (dclient.connect(server,hiPort))
  {
    Serial.println(F("Data connected"));
  }
  else
  {
    Serial.println(F("Data connection failed"));
    client.stop();
    fh.close();
    return 0;
  }
  #ifdef FTPWRITE
  client.print(F("STOR "));
  client.println(fileName);
  #else
  client.print(F("RETR "));
  client.println(fileName);
  #endif
  if(!eRcv())
  {
    dclient.stop();
    return 0;
  }

#ifdef FTPWRITE
  Serial.println(F("Writing"));

  byte clientBuf[128];
  int clientCount = 0;

  while(fh.available())
  {
    clientBuf[clientCount] = fh.read();
    clientCount++;

    if(clientCount > 127)
    {
      dclient.write(clientBuf,128);
      clientCount = 0;
    }
  }

  if(clientCount > 0) dclient.write(clientBuf,clientCount);

#else
  while(dclient.connected())
  {
    while(dclient.available())
    {
      char c = dclient.read();
      fh.write(c);   
      Serial.write(c);
    }
  }
#endif

  dclient.stop();
  Serial.println(F("Data disconnected"));

  if(!eRcv()) return 0;

  client.println(F("QUIT"));

  if(!eRcv()) return 0;

  client.stop();
  Serial.println(F("Command disconnected"));

  fh.close();
  Serial.println(F("SD closed"));
  return 1;
}

/* ----------------------------------------------------------------------------------------- */
/* ----------------------------------------------------------------------------------------- */
/* ----------------------------------------------------------------------------------------- */
byte eRcv()
{
  byte respCode;
  byte thisByte;
  while(!client.available()) delay(1);
  respCode = client.peek();
  outCount = 0;
  while(client.available())
  {
    thisByte = client.read(); 
    Serial.write(thisByte);
    if(outCount < 127)
    {
      outBuf[outCount] = thisByte;
      outCount++;   
      outBuf[outCount] = 0;
    }
  }
  if(respCode >= '4')
  {
    efail();
    return 0;
  }
  return 1;
}

/* ----------------------------------------------------------------------------------------- */
/* ----------------------------------------------------------------------------------------- */
/* ----------------------------------------------------------------------------------------- */
void efail()
{
  byte thisByte = 0;
  client.println(F("QUIT"));
  while(!client.available()) delay(1);
  while(client.available())
  {
    thisByte = client.read(); 
    Serial.write(thisByte);
  }
  client.stop();
  Serial.println(F("Comando de desconexion"));
  fh.close();
  Serial.println(F("Cierre de SD"));
  delay(20);
  digitalWrite(resetPin, LOW);
}

/* ----------------------------------------------------------------------------------------- */
/* ----------------------------------------------------------------------------------------- */
/* ----------------------------------------------------------------------------------------- */
void readSD()
{
  fh = SD.open(fileName,FILE_READ);
  if(!fh)
  {
    Serial.println(F("Fallo al abrir la SD"));
    return; 
  }
  while(fh.available())
  {
    Serial.write(fh.read());
  }
  fh.close();
}




No hay comentarios:

Publicar un comentario