Table of Contents
Domotica
Freddy Navarro Umaña B04492
Introducción
Para este proyecto se utilizan ciertos dispositivos abiertos específicos que facilitan el desarrollo del mismo. Para cada interruptor inalámbrico, se utilizara al menos un arduino, un módulo inalámbrico de zigbee (Xbee) y un relay (Led) para lograr lo que se desea. Se usó un raspberry pi donde se diseñó un servidor con acceso mediante wi-fi y que se comunique a su vez por zigbee para controlar los dispositivos inalámbricos. Se creo una aplicacion Android para controlar el sistema.
Objetivos
Objetivo general
Elaborar un sistema capaz de controlar interruptores de manera inalámbrica por medio de una red zigbee, ya sea desde una computadora o un teléfono inteligente.
Objetivos específicos
- Adaptar el codigo del arduino para multiples dispositivos.
- Diseñar una aplicacion Android para controlar el sistema
- Implementar un protocolo de comunicacion para asegurarse el control de varios dispositivos
Herramientas principales
Adicional a las herramientas necesarias del proyecto 1, es necesario utilizar una plataforma para poder compilar aplicaciones de android. Para obtener un software con estas cualidades, se puede conseguir una guia detallada en la pagina oficial de desarrollo de android Aqui
Protocolo de comunicación
El protocolo de comunicación que se implemento es muy básico ya que múltiples dispositivos inalambricos no han sido implementados por lo que no hay que lidiar con las direcciones de 16 bits que utiliza el protocolo Zigbee. De todas formas, existe una libreria en python para los dispositivos Xbee la cual hace la implementación bastante sencilla pero por falta de dispositivos, para las pruebas y corrección de errores, no se implementó. Dicho esto, la aplicaciòn inicialmente solicita el numero del dispositivo, el cual equivale a cada pin del arduino, el cual se guarda y se le define el comando que va a ejecutar el boton de manera automatica. La estructura de datos que recibe el arduino es de la forma DD,CCn donde DD corresponde al numero del dispositivo, CC corresponde al comando y la letra n se utilizo para definir el final de la secuencia. Los comandos validos son 2 para cambiar el estado del pin como un interruptor, 20 para apagar sea cual sea el estado actual, y 21 para encender igual que el anterior.
Programación del arduino
El microcontrolador empleado tiene la función básica de encender o apagar la luz cuando recibe el comando especifico. El arduino tiene un modulo Xbee conectado en sus terminales Tx y Rx para poder enviar y recibir información inalambricamente. Este microcontrolador espera que llegue información por el puerto serial, y cuando esto se cumple, parsea los digitos antes de la coma y los guarda en una variable, hace lo mismo con los digitos despues de la coma y los guarda en otra variable y cuando encuentra la letra n procede a revisar si encuentra el dispositivo y a su vez le pasa el comando. El código, se compila y se carga por medio del programa Arduino al dispositivo conectado. El codigo elaborado para lograr esto corresponde al siguiente:
int ledPin3 = 13; int ledPin2 = 12; int ledPin1 = 11; int ledPin9 = 9; int ledPin10 = 10; // Define global variable for LED (or Relay) state int ledState1 = -1; int ledState2 = -1; int ledState3 = -1; int ledState9 = -1; int ledState10 = -1; void setup() { // Initialize the serial Serial.begin(9600); // Define our pins pinMode(ledPin1, OUTPUT); pinMode(ledPin2, OUTPUT); pinMode(ledPin3, OUTPUT); pinMode(ledPin9, OUTPUT); pinMode(ledPin10, OUTPUT); digitalWrite(ledPin1, LOW); digitalWrite(ledPin2, LOW); digitalWrite(ledPin3, LOW); digitalWrite(ledPin9, LOW); digitalWrite(ledPin10, LOW); // and set the LED (Relay) state ledState1 = LOW; ledState2 = LOW; ledState3 = LOW; ledState9 = LOW; ledState10 = LOW; } void loop() { // check to see if there is data waiting //int avail = Serial.available(); // initialize our command value (really can be anything) // I like -1 as an impossible value (even though it is possible) // but whatever it doesn't matter. int device=0; int command = -1; while (Serial.available() > 0) { int avail = Serial.available(); device = Serial.parseInt(); // parses numeric characters before the comma command= Serial.parseInt();// parses numeric characters after the comma // look for the newline. That's the end of your // sentence: //if (Serial.read() == '\n') if (Serial.find("n")){ if(device==9){ if(command==2){ ledState9=changeLedState(device,ledState9); digitalWrite(device, ledState9); Serial.println(ledState9); delay(10); break; } if(command==21){ digitalWrite(device,HIGH); ledState9=HIGH; Serial.println(ledState9); delay(10); break; } if(command==20){ digitalWrite(device, LOW); ledState9=LOW; Serial.println(ledState9); delay(10); break; } } if(device==10){ if(command==2){ ledState10=changeLedState(device,ledState10); digitalWrite(device, ledState10); Serial.println(ledState10); delay(10); break; } if(command==21){ digitalWrite(device,HIGH); ledState10=HIGH; Serial.println(ledState10); delay(10); break; } if(command==20){ digitalWrite(device, LOW); ledState10=LOW; Serial.println(ledState10); delay(10); break; } } if(device==11){ if(command==2){ ledState1=changeLedState(device,ledState1); digitalWrite(device, ledState1); Serial.println(ledState1); delay(10); break; } if(command==21){ digitalWrite(device,HIGH); ledState1=HIGH; Serial.println(ledState1); delay(10); break; } if(command==20){ digitalWrite(device, LOW); ledState1=LOW; Serial.println(ledState1); delay(10); break; } } if(device==12){ if(command==2){ ledState2=changeLedState(device,ledState2); digitalWrite(device, ledState2); Serial.println(ledState2); delay(10); break; } if(command==21){ digitalWrite(device,HIGH); ledState2=HIGH; Serial.println(ledState2); delay(10); break; } if(command==20){ digitalWrite(device, LOW); ledState2=LOW; Serial.println(ledState2); delay(10); break; } } if(device==13){ if(command==2){ ledState3=changeLedState(device,ledState3); digitalWrite(device, ledState3); Serial.println(ledState3); delay(10); break; } if(command==21){ digitalWrite(device,HIGH); ledState3=HIGH; Serial.println(ledState3); delay(10); break; } if(command==20){ digitalWrite(device, LOW); ledState3=LOW; Serial.println(ledState3); delay(10); break; } } } } } // This menthon changes the switch state from the opposite of what it is int changeLedState(int device, int ledState) { // XOR the LED (Relay) state with 1 int ledState5 = ledState ^ 1; // and turn off or on the LED (Relay) from the previous outcome return ledState5; }
Programación del servidor
El servidor se programó en python y presenta ciertas características a destacar. El servidor utiliza sockets para comunicarse con los clientes, utilizando el protocolo TCP/IP. . Para que el servidor pueda manejar múltiples clientes simultáneamente, se implementan “threads” para manejar cada cliente por separado. De esta manera, las peticiones de los clientes no se colocan en espera sino que se procesan inmediatamente. Una vez que se recibe un comando de un cliente, se crea el thread para manejarlo, el servidor los maneja mediante la clase Cliente y el mismo toma el comando y lo envía por medio del puerto serial hacia el modulo Xbee el cual transmite el mensaje inalambricamente por Zigbee. Luego de enviar el mensaje hace limpieza y cierra ambos puertos. El codigo que hace todo esto posible, se guarda en un archivo con extensión *.py y se ejecuta desde una terminal mediante:
python nombredelarchivo.py
Dicho codigo se presenta a continuacion:
#!/usr/bin/env python import select import socket import sys import threading import serial import time class Server: def __init__(self): self.host = '' self.portIn = 4444 self.backlog = 5 self.size = 1024 self.server = None self.threads = [] def open_socket(self): try: self.server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.server.bind((self.host,self.portIn)) self.server.listen(self.backlog) except socket.error, (value,message): if self.server: self.server.close() print "Could not open socket: " + message sys.exit(1) def run(self): self.open_socket() input = [self.server,sys.stdin] running = 1 while running: inputready,outputready,exceptready = select.select(input,[],[]) for s in inputready: if s == self.server: c = Client(self.server.accept()) c.start() self.threads.append(c) elif s == sys.stdin: junk = sys.stdin.readline() running = 0 self.server.close() for c in self.threads: c.join() class Client(threading.Thread): def __init__(self,(client,address)): threading.Thread.__init__(self) self.client = client self.address = address self.portOut = "/dev/ttyUSB0" self.size = 1024 def getTime(self): return int(round(time.time() * 1000)) def sendData(self,data,ser): try: ser.write(data) except: return "Could not send data" currTime = self.getTime() while self.getTime() - currTime < 2000: time.sleep(.1) if ser.inWaiting() > 0: response = ser.read(1) ser.flushInput() return response def run(self): running = 1 while running: data = self.client.recv(self.size) self.client.close() try: ser = serial.Serial(self.portOut, 9600) except serial.serialutil.SerialException, e: print "Could not connect to serial port" exit() if data: result = self.sendData(data, ser) ser.close() running = 0 if __name__ == "__main__": s = Server() s.run()
Diseño de la aplicación
La aplicación se creo para ejecutar 2 funciones principales, añadir dispositivos y crear combinaciones con los mismos. Para agregar dispositivos la aplicación cuenta con una base de datos donde guarda la información necesaria y se crean vistas dinámicamente en la actividad principal para desplegar la información con sus respectivos botones. Al presionar el boton de agregar dispositivos se despliega un dialogo para introducir los datos necesarios y al presionar agregar el mismo se incluye en la base de datos. Este paso anterior se ve asi:
Al añadir combinaciones, de la misma manera se crean vistas de forma dinámica según los objetos guardados en la base de datos con botones de selección única para añadir a la combinación. De esta manera, al sleccionar un boton la combinacion agregar a un string el dispositivo y el comando correspondientes a la seleccion, y agrega la letra “n” luego de cada seleccion para que el arduino procese los dispositivos por aparte secuencialmente. Un ejemplo de de un comando de combinacion seria algo como “12,20n13,2n11,21n”.La interfaz de agregar combinaciones se ve de la siguiente manera:
Un archivo comprimido con el codigo fuente completamente funcional de la aplicacion, se obtiene a continuacion:
Referencias
Python
https://docs.python.org/2/library/socket.html
https://docs.python.org/2/howto/sockets.html
http://www.binarytides.com/python-socket-programming-tutorial/
http://pyserial.sourceforge.net/index.html
https://learn.adafruit.com/arduino-lesson-17-email-sending-movement-detector/installing-python-and-pyserial
Xbee
http://www.faludi.com/bwsn/
http://xbee.wikispaces.com/Mesh+with+Xbee
Android
http://developer.android.com/guide/topics/ui/declaring-layout.html
http://stackoverflow.com/questions/8127704/how-to-set-a-buttons-parameters-programatically
http://stackoverflow.com/questions/15762905/how-to-display-list-view-in-alert-dialog-in-android
http://stackoverflow.com/questions/8323778/how-to-set-on-click-listener-on-the-radio-button-in-android
http://developer.android.com/guide/topics/ui/controls/radiobutton.html