control de dos led's via web en tiempo real con raspberry y firebase

Post on 22-Jan-2017

610 Views

Category:

Education

3 Downloads

Preview:

Click to see full reader

TRANSCRIPT

CONTROL DE DOS LED´S VÍA WEB CON Y SIN TIEMPO REAL.

El presente trabajo está basado en los documentos de la dirección electrónica:

http://riverajefer.blogspot.com/

En la cual se desarrolla el software y se implementa el hardware para el manejo de un led, encendido y apagado, mediante una aplicación móvil Android y una aplicación Web que controlan los puertos GPIO de una Raspberry Pi.

Vamos a dejar de lado la aplicación Android y trabajaremos únicamente con la implementación Webcon la que controlaremos el encendido y apagado de dos led´s, conectados a los puertos GPIO de la Raspberry Pi y lo haremos sin utilizar el concepto de tiempo real y luego utilizando Firebase que nos permite realizar aplicaciones en tiempo real.

Queda sobreentendido que se posee los conocimientos necesarios y se está en capacidad de montar un servidor Web en la Raspberry Pi, dar permisos de usuario a las carpetas en la Raspberry Pi, que se pueda conectar remotamente al escritorio de la Raspberry Pi y que se posee una cuenta en Firebase, de no ser así se recomienda seguir los buenos tutoriales del mismo blog: http://riverajefer.blogspot.com/

CONTROL DE DOS LED´S SIN APLICAR CONCEPTO DE ACTUALIZACION EN TIEMPO REAL.

Una vez que se pueda establecer la conexión con el escritorio de la Raspberry Pi se debería tener una imagen como la siguiente:

Instalación de librerías necesarias para control de los pines GPIO:

sudo apt-get update

sudo apt-get install python-dev

sudo apt-get install git

sudo apt-get install python-gpiozero

sudo apt-get install python-pip

sudo pip install requests==1.1.0

Instalación de servidor apache

sudo apt-get install apache2 php5 libapache2-mod-php5

sudo service apache2 restart

Procedemos a instalar la versión ligera de Firefox para Raspberry Pi llamada “iceweasel” con el siguiente comando en la terminal:

sudo apt-get install iceweasel

Una vez instalado se puede verificar el funcionamiento del servidor Apache conectándose al localhost:

Posteriormente es necesario cambiar el usuario y el grupo al directorio con el comando:

sudo chown www-data:www-data /var/www/html

La ubicación de los archivos en los cuales vamos a realizar la prueba es: /var/www/html por lo que se deben dar permisos para que se permitan copiar, crear y editar archivos o carpetas en esta dirección con el comando:

sudo chmod 775 /var/www/html

Como el usuario por defecto de la Raspberry Pi es pi añadimos este al grupo www-data que se crea al instalar el servidor Apache, con el comando:

sudo usermod -a -G www-data pi

Ejecutamos el comando:

sudo visudo

Se añade al final del archivo:

www-data ALL=(root) NOPASSWD:ALL

CTRL O para grabar

CTRL X para salir.

Reiniciamos el Servidor Apache

sudo /etc/init.d/apache2 restart

Este proceso está descrito en la dirección: http://riverajefer.blogspot.com/

Transferencia de archivos

Se realiza la transferencia del archivo comprimido led_sc.tar que se lo descargó de internet hacia el directorio /var/www/html/ y se descomprime. La manera más fácil es utilizar una memoria flash.

Hardware

Se requiere para esta aplicación que se arme el siguiente circuito:

Con una computadora con sistema Ubuntu se encuentra la dirección de la red, con el comando:

ifconfig

Se encuentra la dirección de la Raspberry Pi, con el comando:

sudo nmap –sP 192.168.100.0/24

Desde cualquier equipo conectado a la red, se escribe la dirección:

192.168.100.4/led_sc/

Nota: El valor de la IP varía para cada caso

Dos luces apagadas:

Los dos led´s están apagados

Luz de la sala prendida

Led derecha prendido led de izquierda apagado

Luz del comedor prendida

Led de la derecha apagado y led de la izquierda prendido

Luces de la sala y comedor prendidas

Los dos led´s de encuentran prendidos

Dos usuarios al mismo tiempo

Utilizando Firefox (izquierda) y Chrome (derecha) al momento de activar la luz de la sala en el panel de la izquierda, en la derecha todavía sigue apagado.

Para algunas aplicaciones este sería un caso muy crítico ya que si bien la luz de la sala está encendida, el usuario de la derecha piensa que está apagada.

Este problema se soluciona más tarde con la aplicación de Firebase y la ejecución de la aplicación en tiempo real.

Software utilizado

index.html

<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <title>Domótica sin tiempo real</title> <script src="https://code.jquery.com/jquery-1.11.3.min.js"></script> <!-- Tratamiento de botones --> <script src="js/script.js"></script> <link rel="stylesheet" href="css/estilos.css"> <link href='https://fonts.googleapis.com/css?family=Fjalla+One' rel='stylesheet' type='text/css'></head><body>

<h1>DOMÓTICA SIN PROCESO EN TIEMPO REAL</h1> <div align="center">

<h2>Luz Sala</h2> <label class="switch"> <input class="switch-input" type="checkbox" id="switch_sala" /> <span class="switch-label" data-on="On" data-off="Off"></span> <span class="switch-handle"></span> </label> </div>

<div align="center">

<h2>Luz Comedor</h2> <label class="switch"> <input class="switch-input" type="checkbox" id="switch_comedor" /> <span class="switch-label" data-on="On" data-off="Off"></span> <span class="switch-handle"></span> </label> </div></body></html>

script.js

/*@autor: Jefferson Rivera@email: riverajefer@gmail.comadaptado por Fabián Velasco*/

$(document).ready(function() {

/***************************************************************** Procesa pulsador sala ******************************************************************/

$('#switch_sala').on('change',function(){ if(this.checked)

{console.log("On sala");var estado=1;$.ajax({ data:{valor_estado: estado},

url:'procesa.php', type:'POST', success: function(response){

//alert("Salida: "+response ); }});

} else {

console.log("Off sala");var estado=2;$.ajax({ data:{valor_estado: estado},

url:'procesa.php', type:'POST', success: function(response){

//alert("Salida: "+response ); }});

}

});

/***************************************************************** Procesa pulsador comedor ******************************************************************/

$('#switch_comedor').on('change',function(){ if(this.checked) {

console.log("On comedor");var estado=3;$.ajax({ data:{valor_estado: estado},

url:'procesa.php', type:'POST', success: function(response){

//alert("Salida: "+response ); }});

} else {

console.log("Off comedor");var estado=4;

$.ajax({ data:{valor_estado: estado},

url:'procesa.php', type:'POST', success: function(response){

//alert("Salida: "+response ); }});

}

});

});

estilos.ccs

body{ background-color: #2E2E2E;}

h1{ font-family: 'Fjalla One', sans-serif; text-align: center; color: #FFF; font-size: 50px; text-shadow: 1px 1px 7px rgba(150, 150, 150, 1);}h2{ text-align: center; color: #FFF; font-size: 45px; text-shadow: 1px 1px 7px rgba(150, 150, 150, 1);}

.bild { position:absolute;

display:none; width:1000px; height:735px;}.bg { position: fixed; top: 0; left: 0; z-index: -99; /* Preserve aspet ratio */ min-width: 100%; min-height: 100%; z-index: -999; }.bgwidth { width: 100%; }.bgheight { height: 100%; } .switch { position: relative; display: block; vertical-align: top; width: 100px; height: 30px; padding: 3px; margin: 0 10px 10px 0; background: linear-gradient(to bottom, #eeeeee, #FFFFFF 25px); background-image: -webkit-linear-gradient(top, #eeeeee, #FFFFFF 25px); border-radius: 18px; box-shadow: inset 0 -1px white, inset 0 1px 1px rgba(0, 0, 0, 0.05); cursor: pointer;}.switch-input { position: absolute; top: 0; left: 0; opacity: 0;}.switch-label { position: relative; display: block; height: inherit; font-size: 10px; text-transform: uppercase; background: #eceeef; border-radius: inherit; box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.12), inset 0 0 2px rgba(0, 0, 0, 0.15);}.switch-label:before, .switch-label:after { position: absolute; top: 50%; margin-top: -.5em; line-height: 1; -webkit-transition: inherit;

-moz-transition: inherit; -o-transition: inherit; transition: inherit;}.switch-label:before { content: attr(data-off); right: 11px; color: #aaaaaa; text-shadow: 0 1px rgba(255, 255, 255, 0.5);}.switch-label:after { content: attr(data-on); left: 11px; color: #FFFFFF; text-shadow: 0 1px rgba(0, 0, 0, 0.2); opacity: 0;}.switch-input:checked ~ .switch-label { background: #E1B42B; box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.15), inset 0 0 3px rgba(0, 0, 0, 0.2);}.switch-input:checked ~ .switch-label:before { opacity: 0;}.switch-input:checked ~ .switch-label:after { opacity: 1;}.switch-handle { position: absolute; top: 4px; left: 4px; width: 28px; height: 28px; background: linear-gradient(to bottom, #FFFFFF 40%, #f0f0f0); background-image: -webkit-linear-gradient(top, #FFFFFF 40%, #f0f0f0); border-radius: 100%; box-shadow: 1px 1px 5px rgba(0, 0, 0, 0.2);}.switch-handle:before { content: ""; position: absolute; top: 50%; left: 50%; margin: -6px 0 0 -6px; width: 12px; height: 12px; background: linear-gradient(to bottom, #eeeeee, #FFFFFF); background-image: -webkit-linear-gradient(top, #eeeeee, #FFFFFF); border-radius: 6px; box-shadow: inset 0 1px rgba(0, 0, 0, 0.02);}.switch-input:checked ~ .switch-handle {

left: 74px; box-shadow: -1px 1px 5px rgba(0, 0, 0, 0.2);} /* Transition========================== */.switch-label, .switch-handle { transition: All 0.3s ease; -webkit-transition: All 0.3s ease; -moz-transition: All 0.3s ease; -o-transition: All 0.3s ease;}

procesa.php

<?php$valor_estado=$_POST['valor_estado'];

if($valor_estado==1){ exec('sudo python /var/www/html/led_sc/apaga_sala.py');}

if($valor_estado==2){ exec('sudo python /var/www/html/led_sc/prende_sala.py');}

if($valor_estado==3){ exec('sudo python /var/www/html/led_sc/apaga_comedor.py');}

if($valor_estado==4){ exec('sudo python /var/www/html/led_sc/prende_comedor.py');}

?>

Manejo de GPIO de Raspberry:

prende y apaga para sala y comedor

prende_sala.py

import RPi.GPIO as GPIOGPIO.setmode(GPIO.BOARD)GPIO.setup(11, GPIO.OUT)GPIO.output(11, False)

apaga_sala.py

import RPi.GPIO as GPIOGPIO.setmode(GPIO.BOARD)GPIO.setup(11, GPIO.OUT)GPIO.output(11, True)

prende_comedor.py

import RPi.GPIO as GPIOGPIO.setmode(GPIO.BOARD)GPIO.setup(12, GPIO.OUT)GPIO.output(12, False)

apaga_comedor.py

import RPi.GPIO as GPIOGPIO.setmode(GPIO.BOARD)GPIO.setup(12, GPIO.OUT)GPIO.output(12, True)

APLICACIÓN EN TIEMPO REAL

Como hemos visto, el mayor problema radica en que los usuarios que se encuentren conectados no reciben la información actualizada, una alternativa que se tiene para ejecutar en tiempo real es utilizar websockets que son tratados también en el blog http://riverajefer.blogspot.com/ adicionalmente, se tiene como alternativa la utilización de Firebase.

En cuanto a la Raspberry es necesario instalar las siguientes librerías:

sudo pip install python-firebase

Instalar demjson:

Ejecutándose como root el comando:

apt-get install python-demjson

http://www.tutorialspoint.com/json/json_python_example.htm

http://deron.meranda.us/python/demjson/download

Esta librería permite la transformación en tipo de datos json.

Código para sala y comedor:

descargar los archivos: domótica_python.tar y tiempo_real_sc.tar .

El primero se lo puede colocar en cualquier carpeta creada en la Raspberry, por ejemplo yo he creado la carpeta /paso ahí lo copiamos y extraemos de manera que el archivo llamado main.py tiene la siguiente dirección: ~/paso/domótica_python/main.py, en cuanto al segundo debe colocarse en el directorio: /var/www/html/ el mismo en el cual guardamos el archivo led_sc y en el que se crearon los permisos necesarios, copiamos y extraemos, la manera más fácil de copiar es utilizando una memoria flash.

Cuenta en Firebase

La obtención de una cuenta en Firebase es muy simple y gratuita en el modo de desarrollo y lo que nos va a interesar en los siguientes pasos es la URL que le sea asignada a la aplicación:

En mi caso particular se me ha asignado la dirección:

'https://fabian-velasco-pru01.firebaseio.com' que va a ser incluida en el script de control posteriormente.

Como se puede observar en el diagrama de la raíz sale un ramal llamado luces del que se derivan uno llamado comedor y otro llamado sala, esto para que sigua el modelo propuesto en http://riverajefer.blogspot.com/

A diferencia del primer caso donde no se aplica el modelo de tiempo real, es necesario primero ejecutar el archivo main.py desde una terminal para que se puedan controlar las salidas GPIO de la Raspberry Pi:

hay que ubicarse en el directorio en el que se guardó el archivo, en el caso de este documento: ~/paso/domótica_python/ en la terminal:

ejecutar el comando:

sudo python main.py

Usuarios conectados:

Inmediatamente se hace un cambio en cualquiera de los dos usuarios, este se refleja en los led's de la Raspberry Pi así como en el otro usuario.

Código para sala y comedor:

http://docs.python-guide.org/en/latest/scenarios/json/

main.py

#!/usr/bin/python#autor: Jefferson Rivera#modificaciones: Fabián Velascoimport sysimport signalimport jsonimport demjsonfrom gpiozero import LEDfrom clases.Conexion import Conexion

led_sala = LED(17)

led_comedor = LED(18)

def procesa(respuesta):

print respuesta respu = demjson.encode(respuesta) print respu respuesta_json = json.loads(respu) sala_01 = respuesta_json["sala"] print sala_01 comedor_01 = respuesta_json["comedor"]

if sala_01 == True: led_sala.on() print "Foco sala encendido" else: led_sala.off() print "Foco sala apagado"

if comedor_01 == True: led_comedor.on() print "Foco comedor encendido" else: led_comedor.off() print "Foco comedor apagado" sys.stdout.flush()

try:print "Inicio"t = Conexion(procesa)t.daemon=Truet.start()signal.pause()

except (KeyboardInterrupt, SystemExit):raiseprint "Salida"

Conexion.py

from firebase import firebaseimport threadingimport jsonimport time

class Conexion(threading.Thread):

def __init__(self, cb): threading.Thread.__init__(self) self.callback = cb self.fire = firebase.FirebaseApplication('https://fabian-velasco-pru01.firebaseio.com', None) self.ultimo_estado = self.fire.get('/luces', None) self.callback(self.ultimo_estado)

def run(self):E = []E.append(self.ultimo_estado)i = 0

while True:estado_actual = self.fire.get('/luces', None)

E.append(estado_actual)

if E[i] != E[-1]:self.callback(estado_actual)

del E[0]i = i+itime.sleep(0.3)

Desarrollo aplicación Web

index.html

<!DOCTYPE html><html lang="en"><head>

<meta charset="UTF-8"><title>Domótica</title>

<script src="https://code.jquery.com/jquery-1.11.3.min.js"></script> <!-- importamos la libreria de firebase cliente para javascript --> <script src='https://cdn.firebase.com/js/client/2.2.1/firebase.js'></script>

<!-- Logica de comunicación con firebase --> <script src="js/script.js"></script> <link rel="stylesheet" href="css/estilos.css"> <link href='https://fonts.googleapis.com/css?family=Fjalla+One' rel='stylesheet' type='text/css'></head><body>

<h1>DOMÓTICA</h1> <div align="center">

<h2>Luz Sala</h2> <label class="switch"> <input class="switch-input" type="checkbox" id="switch_sala" /> <span class="switch-label" data-on="On" data-off="Off"></span> <span class="switch-handle"></span> </label> </div>

<div align="center">

<h2>Luz Comedor</h2> <label class="switch"> <input class="switch-input" type="checkbox" id="switch_comedor" /> <span class="switch-label" data-on="On" data-off="Off"></span> <span class="switch-handle"></span> </label> </div></body></html>

estilos.css

body{ background-color: #2E2E2E;}

h1{ font-family: 'Fjalla One', sans-serif; text-align: center; color: #FFF; font-size: 50px; text-shadow: 1px 1px 7px rgba(150, 150, 150, 1);}h2{ text-align: center; color: #FFF; font-size: 45px; text-shadow: 1px 1px 7px rgba(150, 150, 150, 1);}

.bild { position:absolute; display:none; width:1000px; height:735px;}.bg { position: fixed; top: 0; left: 0; z-index: -99; /* Preserve aspet ratio */ min-width: 100%; min-height: 100%; z-index: -999; }.bgwidth { width: 100%; }.bgheight { height: 100%; } .switch { position: relative; display: block; vertical-align: top; width: 100px; height: 30px; padding: 3px; margin: 0 10px 10px 0; background: linear-gradient(to bottom, #eeeeee, #FFFFFF 25px); background-image: -webkit-linear-gradient(top, #eeeeee, #FFFFFF 25px); border-radius: 18px; box-shadow: inset 0 -1px white, inset 0 1px 1px rgba(0, 0, 0, 0.05); cursor: pointer;}.switch-input {

position: absolute; top: 0; left: 0; opacity: 0;}.switch-label { position: relative; display: block; height: inherit; font-size: 10px; text-transform: uppercase; background: #eceeef; border-radius: inherit; box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.12), inset 0 0 2px rgba(0, 0, 0, 0.15);}.switch-label:before, .switch-label:after { position: absolute; top: 50%; margin-top: -.5em; line-height: 1; -webkit-transition: inherit; -moz-transition: inherit; -o-transition: inherit; transition: inherit;}.switch-label:before { content: attr(data-off); right: 11px; color: #aaaaaa; text-shadow: 0 1px rgba(255, 255, 255, 0.5);}.switch-label:after { content: attr(data-on); left: 11px; color: #FFFFFF; text-shadow: 0 1px rgba(0, 0, 0, 0.2); opacity: 0;}.switch-input:checked ~ .switch-label { background: #E1B42B; box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.15), inset 0 0 3px rgba(0, 0, 0, 0.2);}.switch-input:checked ~ .switch-label:before { opacity: 0;}.switch-input:checked ~ .switch-label:after { opacity: 1;}.switch-handle { position: absolute; top: 4px; left: 4px;

width: 28px; height: 28px; background: linear-gradient(to bottom, #FFFFFF 40%, #f0f0f0); background-image: -webkit-linear-gradient(top, #FFFFFF 40%, #f0f0f0); border-radius: 100%; box-shadow: 1px 1px 5px rgba(0, 0, 0, 0.2);}.switch-handle:before { content: ""; position: absolute; top: 50%; left: 50%; margin: -6px 0 0 -6px; width: 12px; height: 12px; background: linear-gradient(to bottom, #eeeeee, #FFFFFF); background-image: -webkit-linear-gradient(top, #eeeeee, #FFFFFF); border-radius: 6px; box-shadow: inset 0 1px rgba(0, 0, 0, 0.02);}.switch-input:checked ~ .switch-handle { left: 74px; box-shadow: -1px 1px 5px rgba(0, 0, 0, 0.2);} /* Transition========================== */.switch-label, .switch-handle { transition: All 0.3s ease; -webkit-transition: All 0.3s ease; -moz-transition: All 0.3s ease; -o-transition: All 0.3s ease;}

script.js

Nota: en este script es donde se utiliza la dirección a signada a su proyecto por Firebase.

/*@autor: Jefferson Rivera@email: riverajefer@gmail.comadaptado por Fabián Velasco*/

$(document).ready(function() {

//creamos un objeto de Firebase y le pasamos la URL como parámetro var ref = new Firebase("https://fabian-velasco-pru01.firebaseio.com/luces/");

//esta es la dirección que se debe cambiar por aquella que le sea asignada a su Proyecto en// Firebase

/***************************************************************** Obtenemos el valor del último estado ******************************************************************/ ref.once("value", function(res) {

var luzSala = res.child("sala").val(); $('#switch_sala').attr('checked', luzSala); // console.log("Estado actual_sala: " +luzSala)

var luzComedor = res.child("comedor").val(); $('#switch_comedor').attr('checked', luzComedor); // console.log("Estado actual_comedor: " +luzComedor)

});

/***************************************************************** Obtenemos el valor del estado de la luz en tiempo real, cada vez que haya cambio ****************************************************************** CAMBIOS EFECTUADOS EN AL CODIGO ORIGINAL ******************************************************************/ ref.child("sala").on("value", function(snapshot){ var luz_sala = snapshot.val(); $('#switch_sala').prop('checked', luz_sala); console.log("Cambio de estado_sala: " + luz_sala); });

ref.child("comedor").on("value", function(snapshot){ var luz_comedor = snapshot.val(); $('#switch_comedor').prop('checked', luz_comedor); console.log("Cambio de estado_comedor: " + luz_comedor); });

/***************************************************************** Actualizamos el valor, cambiado el estado del Switch ******************************************************************/ $('#switch_sala').on('change', function(){ if(this.checked) { console.log("On") ref.update({ sala: true }); } else{ console.log("Off") ref.update({ sala: false }); } });

$('#switch_comedor').on('change', function(){ if(this.checked) {

console.log("On") ref.update({ comedor: true }); } else{ console.log("Off") ref.update({ comedor: false }); } });

});

Firebase Dashboard

Conforme se vaya utilizando la aplicación se va registrando su uso en el Dashboard de Firebase.

Esta es toda la información requerida, espero que sea de utilidad.

Muchas gracias.

Archivo led_schttps://www.dropbox.com/s/9e212vygjd5hsl3/led_sc.tar?dl=0

Archivo tiempo_real_schttps://www.dropbox.com/s/fdbp2be0x4j9sbh/tiempo_real_sc.tar?dl=0

Archivo domotica_python.tarhttps://www.dropbox.com/s/jn1tx7tc17irh5m/domotica_python.tar?dl=0

top related