DOMÓTICA CON RASPBERRY PI Y ANDROID EN TIEMPO REAL
De acuerdo a lo que se aprecia en la gráfica, se puede decir que Firebase es el punto central del sistema el cual recibe y envía peticiones en tiempo real a los diferentes clientes que estén conectados a él.
La aplicación Android se conecta a la URL de nuestro proyecto de Firebase, y lo que hace es actualizar y obtener los datos en tiempo real, específicamente cuando se cambia el estado del Switch, tanto en la aplicación móvil como en la página web, este cambio se ve reflejado de inmediato en Firebase, y como Raspberry Py con Python, está conectado a la misma URL de Firebase, puede obtener el valor de este estado y así poder encender o apagar un diodo led.
En las imágenes se ve como llega una notificación en tiempo real, cada vez que ha habido un cambio en Firebase
Aplicación Web se conecta a la misma URL de Firebase y en tiempo real envía y recibe los datos del estado.
Diagrama esquemático
CONTENIDO
- CREAR UN PROYECTO EN FIREBASE
- CÓDIGO APLICACIÓN MÓVIL ANDROID (JAVA)
- CÓDIGO APLICACIÓN WEB (HTML/JAVASCRIPT)
- INSTALANDO LIBRERÍAS NECESARIAS EN RASPBIAN
- CÓDIGO APLICACIÓN RASPBERRY PI (PYTHON)
CREAR UNA CUENTA EN FIREBASE
Firebase es un proveedor de servicios en la nube, tambien es un backend como servicio, algunos de sus servicios más destacados son:
- Base de datos en tiempo real
- Alojamiento (Hosting)
- API de autenticación usando diferentes redes sociales
Para crear una cuenta es muy sencillo, abrimos el siguiente enlace, nos pide loguearnos con Google, y listo, automáticamente Firebase crea un proyecto, y también da la opción de crear nuevos proyectos, solo hay que escribir un nombre único para el proyecto, luego le genera una URL, que es la que se va a usar para hacer peticiones y consultas en tiempo real.
MainActivity.java
package com.rivera.jeffer.domotica; import android.app.AlertDialog; import android.content.Intent; import android.os.Bundle; import android.support.v7.app.AppCompatActivity; import android.support.v7.widget.Toolbar; import android.view.Menu; import android.view.MenuItem; import android.widget.CompoundButton; import android.widget.Switch; import com.firebase.client.DataSnapshot; import com.firebase.client.Firebase; import com.firebase.client.FirebaseError; import com.firebase.client.ValueEventListener; public class MainActivity extends AppCompatActivity { Switch mySwitch; Firebase firebase; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); setSupportActionBar(toolbar); firebase.setAndroidContext(this); Intent intent = new Intent(this, FireService.class); startService(intent); mySwitch = (Switch)findViewById(R.id.mySwitch); firebase = new Firebase("https://android2016.firebaseio.com/luces/sala"); firebase.addValueEventListener(new ValueEventListener() { @Override public void onDataChange(DataSnapshot dataSnapshot) { Boolean estado = (Boolean) dataSnapshot.getValue(); mySwitch.setChecked(estado); if(estado){ mySwitch.setText("Luz Sala Encendida"); }else{ mySwitch.setText("Luz Sala Apagada"); } } @Override public void onCancelled(FirebaseError firebaseError) { } }); mySwitch.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { @Override public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { if (isChecked) { mySwitch.setText("Luz Sala Encendida"); firebase.setValue(true); } else { mySwitch.setText("Luz Sala Apagado"); firebase.setValue(false); } } }); } @Override public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.menu, menu); return true; } @Override public boolean onOptionsItemSelected(MenuItem item) { int id = item.getItemId(); if (id == R.id.action_settings) { AlertDialog.Builder builder = new AlertDialog.Builder(this); builder.setTitle("Desarrollado Por:"); builder.setMessage("Jefferson Rivera \nriverajefer@gmail.com\nBogotá Colombia"); builder.setPositiveButton("OK",null); builder.create(); builder.show(); return true; } return super.onOptionsItemSelected(item); } }
FireService.java
package com.rivera.jeffer.domotica; import android.app.NotificationManager; import android.app.PendingIntent; import android.app.Service; import android.content.Context; import android.content.Intent; import android.media.RingtoneManager; import android.net.Uri; import android.os.IBinder; import android.support.v4.app.NotificationCompat; import android.util.Log; import com.firebase.client.DataSnapshot; import com.firebase.client.Firebase; import com.firebase.client.FirebaseError; import com.firebase.client.ValueEventListener; public class FireService extends Service { private static final String TAG = "FireService"; protected Firebase firebase; private PendingIntent pendingIntent; private NotificationManager notificationManager; private NotificationCompat.Builder notificationBuilder; private int icono; @Override public void onCreate() { Log.i(TAG, "Service onCreate"); Firebase.setAndroidContext(this); firebase = new Firebase("https://android2016.firebaseio.com/luces/sala"); Intent myIntent = new Intent(this, MainActivity.class); pendingIntent = PendingIntent.getActivity(this, 0, myIntent, 0); //Initialize NotificationManager using Context.NOTIFICATION_SERVICE notificationManager = (NotificationManager)getSystemService(Context.NOTIFICATION_SERVICE); } @Override public int onStartCommand(Intent intent, int flags, int startId) { Log.i(TAG, "Service onStartCommand"); //Creating new thread for my service //Always write your long running tasks in a separate thread, to avoid ANR new Thread(new Runnable() { @Override public void run() { firebase.addValueEventListener(new ValueEventListener() { @Override public void onDataChange(DataSnapshot dataSnapshot) { Boolean estado = (Boolean) dataSnapshot.getValue(); String msgNotificacion; if(estado){ msgNotificacion = "Luz Sala Encendida"; icono = R.drawable.ic_on; }else{ msgNotificacion = "Luz Sala Apagada"; icono = R.drawable.ic_foco_off; } Log.v("CAMBIOOOOO DE:",msgNotificacion); notificacion(msgNotificacion, icono); } @Override public void onCancelled(FirebaseError firebaseError) { } }); //Stop service once it finishes its task //stopSelf(); } }).start(); return Service.START_STICKY; } public void notificacion(String msg, int icono){ //Prepare Notification Builder notificationBuilder = new NotificationCompat.Builder(this) .setContentTitle("Domótica").setSmallIcon(icono).setContentIntent(pendingIntent) .setContentText(msg); //add sound Uri uri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION); notificationBuilder.setSound(uri); long[] v = {80,260,80}; notificationBuilder.setVibrate(v); notificationManager.notify(1, notificationBuilder.build()); } //The system calls this method when another component wants to bind with the service @Override public void onDestroy() { Log.i(TAG, "SERVICIO DESTRUIDO"); } @Override public IBinder onBind(Intent arg0) { Log.i(TAG, "Service onBind"); return null; } }
contenido.xml
CÓDIGO APLICACIÓN WEB (HTML / JAVASCRIPT)
Estructura del proyecto:
index.html
Domótica DOMÓTICA
Luz Sala
js/script.js
/* @autor: Jefferson Rivera @email: riverajefer@gmail.com */ $(document).ready(function() { //creamos un objeto de firebase, y le pasamos la URL como parametro var ref = new Firebase("https://android2016.firebaseio.com/luces/"); /***************************************************************** Obtenemos el valor del último estado ******************************************************************/ ref.once("value", function(res) { var luzSala = res.child("sala").val(); $('#switch').attr('checked', luzSala); // console.log("Estado actual: " +luzSala) }); /***************************************************************** Obtenemos el valor del estado de la luz en tiempo real, cada vez que haya cambio ******************************************************************/ ref.on("child_changed", function(res) { var luz_sala = res.val(); $('#switch').prop('checked', luz_sala); console.log("Cambio de estado: " +luz_sala) }); /***************************************************************** Actualizamos el valor, cambiado el estado del Switch ******************************************************************/ $('#switch').on('change', function(){ if(this.checked) { console.log("On") ref.update({ sala: true }); } else{ console.log("Off") ref.update({ sala: false }); } }); });
css/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; }
Continua....
ver código completo en
https://github.com/rpi-jefer/domotica_raspberry_pi/blob/master/domotica_web/css/estilos.css
ver código completo en
https://github.com/rpi-jefer/domotica_raspberry_pi/blob/master/domotica_web/css/estilos.css
INSTALANDO LIBRERÍAS NECESARIAS EN RASPBIAN
sudo apt-get update sudo apt-get install python-dev sudo apt-get install python-gpiozero sudo wget https://bootstrap.pypa.io/get-pip.py sudo python get-pip.py sudo pip install requests==1.1.0 sudo pip install python-firebase
CÓDIGO APLICACIÓN RASPBERRY PI (PYTHON)
clases/Conexion.py
from firebase import firebase import threading import time class Conexion(threading.Thread): def __init__(self, cb): threading.Thread.__init__(self) self.callback = cb self.fire = firebase.FirebaseApplication('https://android2016.firebaseio.com/', None) self.ultimo_estado = self.fire.get('/luces/sala', 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/sala', None) E.append(estado_actual) if E[i] != E[-1]: self.callback(estado_actual) del E[0] i = i+i time.sleep(0.3)
main.py
import sys import signal from gpiozero import LED from clases.Conexion import Conexion led = LED(17) def procesa(respuesta): print respuesta if respuesta: led.on() print "Encendido" else: led.off() print "Apagado" sys.stdout.flush() try: print "Inicio" t = Conexion(procesa) t.daemon=True t.start() signal.pause() except (KeyboardInterrupt, SystemExit): raise print "Salida"
DESCARGAR EL CÓDIGO DEL PROYECTO
https://github.com/rpi-jefer/domotica_raspberry_pi
Jefferson Rivera Patiño
Bogotá Colombia
estimado jeferson....mi comentario es porque no puedo hacer el ultimo paso en el raspbian... sudo pip install python-firebase.... me da erores... intente por varios metodos y fue imposible... me interesa mucho tu proyecto para usarlo con alumnos de domotica en arquitectura de la UP. lo mismo que el de reconocimiento de la voz aunque por ahora no funcione con la nueva api de google... quisera saber si me podes dar una mano con este ultimo paso... desde ya muchos saludos!
ResponderEliminarHola.
EliminarSiga los siguientes pasos:
sudo apt-get remove python-pip
sudo wget https://bootstrap.pypa.io/get-pip.py
sudo python get-pip.py
sudo pip install requests==1.1.0
sudo pip install python-firebase
Me cuenta si le sirvió.
Saludos
Hola.
EliminarLa actualización de este tutorial.
La puede ver acá.
https://www.youtube.com/watch?v=pcryAtHpvCE
Saludos
Este comentario ha sido eliminado por el autor.
ResponderEliminarHola Jeferson , tengo una pegunta ¿la aplicación para el celular la descargaste de la App Store y con que nombre la encuentro ? , después de descargarla hay que modificare algo ?
ResponderEliminarHola, Julio.
EliminarLa aplicación la desarrolle desde cero, Usando el IDE de Android Studio.
Acá puede ver el proyecto.
https://github.com/rpi-jefer/domotica_raspberry_pi/tree/master/domotica_app/Domotica
El apk, se genera cuando compila el proyecto.
Igual, voy a buscar la última versión del apk, y se las comparto.
saludos
Hola Jeferson, excelente proyecto, tengo una pregunta, me baje tu código para probar, y me funciona, pero me cree una cuenta en firebase y en el programa main.py le puse la dirección que me dio firebase del nuevo proyecto que cree y no funciona, hay que hacer algo mas en firebase?
ResponderEliminarHola Julio, lo que pasa es que Firebase, hace poco se actualizo, por lo cual toca usar otra librería para python.
EliminarNo he tenido tiempo para actualizar, pero quinas te pueda servir esto:https://github.com/thisbejim/Pyrebase
Es la librería de Python para la nueva versión de Firebase.
Saludos
Hola para adicionar luces de quartos quais procedimento tks
ResponderEliminarHOLA COMPAÑERO SEAS TAN AMABLE DE DEJARME TU WHASAPP NECESITO COMUNICARME CONTIGO
ResponderEliminarHola.
EliminarEscribeme a mi correo.
riverajefer@gmail.com
TEngo una consulta cuando esta funcionando la rasberry pi luego de un tiempo se desconecta automaticamente del servicio y ya no funciona hay q volver a iniciar el main.py como lo puedo hacer para q quede funcionando siemnpre y cuando haya corte de energia o se corte el internet arranque el programa automaticamante gracias amigo
ResponderEliminarBuenas tardes mi estimado amigo necesito q me ayude con un problema que tengo cuando en rasbian ejecuto en el terminal el script python funciona de maravilla pero poco tiempo despues me sale el siguiente error y se desconecta tengo que salir de ese proceso y volver a ejecutar el script main.py para q siga funcionando pero deseo q funcione de forma constante que podria hacer gracias:
ResponderEliminarException in thread Thread-1:
Traceback (most recent call last):
File "/usr/lib/python2.7/threading.py", line 810, in __bootstrap_inner
self.run()
File "/var/www/html/python/clases/Conexion.py", line 22, in run
estado_actual = self.fire.get('/luces', None)
File "/usr/local/lib/python2.7/dist-packages/firebase/decorators.py", line 19, in wrapped
return f(*args, **kwargs)
File "/usr/local/lib/python2.7/dist-packages/firebase/firebase.py", line 274, in get
return make_get_request(endpoint, params, headers, connection=connection)
File "/usr/local/lib/python2.7/dist-packages/firebase/decorators.py", line 19, in wrapped
return f(*args, **kwargs)
File "/usr/local/lib/python2.7/dist-packages/firebase/firebase.py", line 38, in make_get_request
response = connection.get(url, params=params, headers=headers, timeout=timeout)
File "/usr/lib/python2.7/dist-packages/requests/sessions.py", line 469, in get
return self.request('GET', url, **kwargs)
File "/usr/lib/python2.7/dist-packages/requests/sessions.py", line 457, in request
resp = self.send(prep, **send_kwargs)
File "/usr/lib/python2.7/dist-packages/requests/sessions.py", line 569, in send
r = adapter.send(request, **kwargs)
File "/usr/lib/python2.7/dist-packages/requests/adapters.py", line 407, in send
raise ConnectionError(err, request=request)
ConnectionError: ('Connection aborted.', BadStatusLine("''",))
lograste solucionar el problema? me pasa lo mismo
EliminarHola Jeferson. Muchas gracias por todo el trabajo que haces.
ResponderEliminarEstoy haciendo un proyecto parecido al suyo pero he implementado un boton, el cual cuando lo presiono quiero que cambie el estado de mi variable en firebase de True a False, y no sé que código tengo que utilizar para realizar esto. Si puediera ayudarme se lo agradecería mucho. Un saludo!
Hola.
EliminarLo que pasa es que, toca usar ahora la nueva versión de Firebase, tanto en Android, como en Python.
Escríbame y le envío información:
riverajefer@gmail.com
Este comentario ha sido eliminado por el autor.
EliminarAmigo será que se podría trabajar en APache y MySql, y remplazar el FireBase?
ResponderEliminarHola Jeferson! De verdad quiero agradecerle por esta util informacion tan bien estructuradas y en español que nos ayuda a los latinos montones!!!! especialmente a mi que estoy empezando con los temas de android :) Solo te queria pedir una ayuda cuando compilo la aplicacion en android studio me saca el siguiente error:
ResponderEliminarConfiguration 'compile' is obsolete and has been replaced with 'implementation'.
It will be removed at the end of 2018
Configuration 'testCompile' is obsolete and has been replaced with 'testImplementation'.
It will be removed at the end of 2018
Configuration 'testApi' is obsolete and has been replaced with 'testImplementation'.
It will be removed at the end of 2018
Failed to resolve: com.google.firebase:firebase-messaging:12.0.1
Me puedes por favor guiar en como se resuelve?
Mil gracias y un fuerte abrazo!!
Saludos
Andres
Como puedo con el codigo del enlace
ResponderEliminarhttps://github.com/scottgarner/BeetBox
conectarlo con android para reproducir el audio por me dio del celular. por favor. gracias