Reporte - chrisolis/Proyecto-final-Arquitectura-computadoras Wiki

Código a ejecutar por parte del Arduino

Este reporte no entra en la descripción especifica de cada línea dado a los comentarios con los que ya vienen los códigos cargados. Este reporte sirve como un resumen general de las soluciones implementadas para cada uno de los incisos que se especificaron en el apartado de Canvas.

El código de Arduino bajo el nombre proyecto.ino se encarga del primer punto. Este establece que se deben medir 3 variables físicas diferentes. Las variables que decidimos medir fueron la distancia, luz y temperatura.

En general el código de Arduino recibe los valores de los sensores, agrega caracteres a dichos valores con el fin de diferenciarlos por parte de la Raspberry, manda dichos valores junto con los caracteres como mensajes enviados por la comunicación serial. Esto lo realiza cada segundo. Existe un contador que sube uno en valor al pasar cada segundo. Al llegar a un valor de 10 (indicando que pasaron 10 segundos) se envía un carácter que le indica a la Raspberry que pasaron los 10 segundo, con el fin de que esta mande los datos a Wia y realice otras acciones.

Para esto empezamos el código por lo que es la importación de la librería con la cual controlamos el sensor de temperatura, declaramos los pines de control del sensor de distancia, las variables para el envío de mensajes, las variables para los cálculos relacionados con la distancia y la luz; Los caracteres con los que la Raspberry diferencia los valores que se mandan por la comunicación serial y una con la cual se lleva un conteo del tiempo para medir cuando pasen los 10 segundos. Todo esto de la línea 1 a la 28.

//HC-SR04
#define echoPin 4 // Pin D4 del Arduino conectado al pin Echo del HC-SR04
#define trigPin 3 // Pin D3 del Arduino conectado al pin Trig del HC-SR04
//DHT11 o en especifico el modulo KY-015 que es equivalente en su manejo
#include <dht11.h> //Se importa la libreria con la que se controla el sensor DHT11 de temperatura y humedad.
dht11 DHT11; //Se declara un objeto del tipo dht11 de la libreria DHT11LIB con el nombre DHT11 para poder utilizarlo mas adelante y llamar a los metodos de la libreria misma.
//Comunicacion serial
const byte numChars = 32; //Variable para definir el largo del string basado en un numero de caracteres.
char receivedChars[numChars];   //Arreglo para guardar informacion recibida.
//LDR o fotoresistencia
const long A = 1000;     //Resistencia en oscuridad en KΩ
const int B = 15;        //Resistencia a la luz (10 Lux) en KΩ
const int Rc = 10;       //Resistencia calibracion en KΩ
const int LDRPin = A0;   //Pin del LDR o fotoresistencia.
int V; //Variable a emplear en el calculo del valor del brillo.
float ilum; //Variable a emplear en el calcuulo del valor del brillo.
float porcent; //Variable para guardar el resultado de brillo regresado por la fotoresistencia.
int maxi = 100; //Variable con el valor de brillo maximo reportable.
String tempa = "C"; //Caracter a acompaniar al valor de la temperatura.
String dista = "D"; //Caracter a acompaniar al valor de la distancia.
String luxa = "L"; //Caracter a acompaniar al valor del brillo.
String esHora = "T"; //Caracter a indicar que han pasado 10 segundos.

boolean newData = false;
int contador = 0; //Variable para contar los 10 segundos que accionan T.
long duration; //Variable para guardar cuando se tardo en regresar el sonido.
int distance; //Variable para guardar la distancia calculada

De la línea 29 a la 35, se configuran los pines del sensor de temperatura, se configura la velocidad de la comunicación serial y se vincula el pin del sensor de temperatura con su objeto.

void setup() {
  DHT11.attach(2); //Se realiza la conexion al pin analogo 2 al que se encuentra conectada la linea de datos del sensor.  
  Serial.begin(115200); //Comunicacion a 115200.
  pinMode(trigPin, OUTPUT); //Configura el pin del trigger como Output
  pinMode(echoPin, INPUT); //Configura el pin del echo como Input
}

De la línea 36 a la línea 76 se declara el loop del programa. De la 37 a la 42 se lee el valor del sensor de temperatura, se convierte a string, se le añade el carácter que lo diferencia y se envía por la comunicación serial. El retraso que se observa es para garantizar libertad de errores al realizar la siguiente lectura.

void loop() {
  //Manda cada segundo los valores de temperatura, humedad, brillo y distancia.
  int chk = DHT11.read(); //Se lee la temperatura y la humedad. El metodo de la libreria actualiza dichos atributos del objeto DHT11.
  String tempo = String(DHT11.temperature, DEC); //Se convierte en String el atributo de temperatura con el fin de concatenarlo despues.
  String completo = tempa + tempo; //Se concatena un String conteniendo una 'C' al string conteniendo la temperatura.
  Serial.println(completo); //Se imprime el string completo concatenado que contiene el atributo temperature del objeto DHT11 en decimal.
  delay(10); //Se realiza un delay con el motivo de evitar lecturas erroneas dado a una ejecucion inmediata de la siguiente linea al apenas haber terminado de mandarse el mensaje por medio de la comunicacion serial.

De la línea 43 a la 56 se lee el valor devuelto por la fotorresistencia, con este se calcula el nivel de brillo. Al haberlo calculado los pasamos a un porcentaje en base a la lectura máxima registrada en nuestras pruebas. Si aun así se obtuviera un valor por encima del 100 registrado entonces se devuelve un resultado solo de 100, en el caso contrario se devuelve el porcentaje equivalente. Ambos casos de los porcentajes al enviarse se convierten en Strings junto con un carácter que diferencia el dato para ser enviados por la comunicación serial. El retraso presente sirve el mismo propósito que el del valor de la temperatura.

V = analogRead(LDRPin); //Lee el valor analogo de la fotoresistencia.
  ilum = ((long)V*A*10)/((long)B*Rc*(1024-V));//Calculo del brillo
  porcent = ((ilum/3036)*100); //Valor del brillo registrado por la fotoresistencia convertido a un porcentaje de 0 a 100.
  if(porcent >= 100){ //Si la lectura sobrepasara el limite de 100, solo se regresa 100.
    tempo = String(maxi); //Se convierte en String el entero maxi con el valor de intensidad maxima de brillo.
    completo = luxa + tempo; //Se concatena un String conteniendo una 'L' al string conteniendo el entero que representa la intensidad del brillo.
    Serial.println(completo); //Se imprime el string completo concatenado que contiene el porcentaje de luz.
  }
  else{
    tempo = String(porcent); //Se convierte en String el float porcent con el valor de intensidad de la luz.
    completo = luxa + tempo; //Se concatena un String conteniendo una 'L' al string conteniendo el entero que representa la intensidad de la luz.
    Serial.println(completo); //Se imprime el string completo concatenado que contiene el porcentaje de luz.
  }
  delay(10); //Nuevamente se utiliza un retraso con el fin de garantizar errores al realizar la lectura del siguiente sensor.

De la línea 57 a la 69 se realiza la lectura del último sensor. Se mantiene apagado el trigger del sensor de distancia con el fin de garantizar que este se encuentre libre. Después se enciende por una cantidad de tiempo en lo que el echo registra el valor de la distancia. Este se convierte a centímetros, luego a un String y al igual que los otros datos se repite el proceso de añadirle un carácter, enviarlo por la comunicación serial y utilizar un retraso.

//Deja el trigger apagado previo a la lectura de la distancia por 2 microsegundos para asegurar que el mismo este libre.
  digitalWrite(trigPin, LOW);
  delayMicroseconds(2);
  //Enciende el trigger por 10 microsegundos con el fin de generar una rafaga ultrasonica de 8 ciclos.
  digitalWrite(trigPin, HIGH);
  delayMicroseconds(10);
  digitalWrite(trigPin, LOW);
  duration = pulseIn(echoPin, HIGH); //Lee el valor en microsegundos que tardo en volver el sonido.
  distance = duration * 0.034 / 2; //Calcula la distancia en base a la velocidad del sonido.
  tempo = String(distance); //Se convierte en String el valor entero de la distancia.
  completo = dista + tempo; //Se concatena un String conteniendo una 'D' al string conteniendo el entero que representa la intensidad del brillo.
  Serial.println(completo); //Envia el valor de la distancia junto con el caracter previamente descrito por la comunicacion serial.
  delay(10); //Se realiza un retraso despues de el envio del mensaje por comunicacion serial.

Para terminar de la línea 70 a la 76, aumenta el valor del contador y se completa el tiempo faltante para constituir un segundo entre tomas de datos. Al revisar el contador si este tiene un valor de 10 se envía un carácter diferente a los previos por la comunicación serial como prueba de que pasaron los 10 segundos. Una vez esto pasa, se reinicia el contador con el fin de llevar el conteo nuevamente.

contador = contador + 1; //Va aumentando el valor del contador en uno por cada segundo que pase.   
  delay(970); //Delay compensando los tiempos de lectura con el fin de completar el segundo.
  if(contador == 10){ //Si llega a diez el contador indica que ya pasaron 10 segundos
    contador = 0; //Se devuelve el contador a su valor inicial.
    Serial.println(esHora); //Se envia una 'T' para indicarle a la Raspberry que ya pasaron 10 segundos.
  }
}

Código a ejecutar por parte de la Raspberry Pi

Al encontrarse múltiples archivos en el folder de la Raspberry como los componentes de la interfaz, el código de la interfaz, etc. Solo se describe aquí el código principal para correr todo el sistema. Este código lleva de nombre hub.py.

Hub.py al ejecutarse inicia la interfaz gráfica gracias a las imágenes en el folder junto con el código de GUI.py (generado a partir de la herramienta de diseño de Qt Designer). Al iniciarse este actualiza las etiquetas de la interfaz con los valores de los sensores recibidos por medio de la comunicación serial. También se actualiza un reloj mostrando la hora y al iniciar el sistema se genera el texto de la etiqueta relacionada con la fecha. Al igual que con 2 botones en la interfaz se puede abrir (con la aplicación predeterminada del sistema operativo) un archivo de texto que registra los valores de los sensores cada segundo y con el otro ampliar la señal de video recibida por medio de una cámara externa. Al pasar 10 segundos se envían a Wia y a una pantalla OLED los valores registrados por parte de la Raspberry Pi.

De esta manera este código lidio con los puntos restantes requeridos que consisten en que se desplieguen los datos en una pantalla, que se registren los valores a cada segundo recibidos y que se puedan ver en la interfaz, que se observen estos junto con la hora y fecha en la que se recibieron (esto se registra junto con los datos de los sensores en el archivo de texto) y que se envíen los datos a Wia.

Empezando el código se puede ver que de la línea 1 a la 25 consisten en la importación de las librerías necesarias.

  
import serial #Libreria para la comunicacion serial.
import subprocess #Libreria para la apertura del archivo de texto generado en la aplicacion default del sistema.
#Librerias 1 a 4 para poder manejar la pantalla OLED, obtenidas de consultar la pagina de Github 
#de Adafruit SSD1306 para comunicacion con pantallas OLED por I2C.
import board #1
import digitalio #2
from PIL import Image, ImageDraw, ImageFont #3
import adafruit_ssd1306 #4
from wia import Wia #Libreria 1 para realizar llamadas a Wia.
import random #Libreria 2 para realizar llamadas a Wia.
import requests #Libreria 3 para realizar llamadas a Wia.
import sys #Libreria 1 para la ejecucion de las ventanas de la interfaz.
import math #Libreria 1 para el uso de la camara.
import cv2 #Libreria 2 para el uso de la camara.
import datetime #Libreria para el uso de la fecha al igual que liberia 4 para realizar llamadas a Wia.
import time #Libreria para el uso de timers y tiempo del sistema.
#Librerias 2 a 7 para el uso de la interfaz por medio de PyQt y llamadas a objetos generados 
#por medio de QTDesigner que pertenecen al codigo de Python correspondiente a la interfaz. 
from PyQt5.QtWidgets import * #2
from PyQt5.QtCore import * #3
from PyQt5.QtCore import Qt, QTimer, pyqtSlot #4
from PyQt5 import QtCore, QtGui, QtWidgets #5
from PyQt5.QtGui import * #6
from GUI import* #7

De la línea 26 a la 53 se realizó la configuración inicial de la cámara, el control de la pantalla OLED por medio de I2C y la configuración de la comunicación serial por parte de la Raspberry.

#Configuracion del video de la camara

Capture = cv2.VideoCapture(0) #Asigna el video de la camara que aparece en el indice como 0 a la variable Capture (para uso futuro en los metodos de Camera) por medio de la libreria cv2.

#Configuracion inicial de la pantalla OLED.

oled_reset = digitalio.DigitalInOut(board.D4) #Asigna un pin para que funcione como reset de la pantalla.
WIDTH = 128 #Pixeles de altura de la pantalla
HEIGHT = 32 #Pixeles de ancho de la pantalla
BORDER = 5  #Pixeles a usar como borde en la pantalla en llamadas futuras.
i2c = board.I2C() #Crea la interfaz I2C
#Crea el objeto OLED de la clase adafruit ssd1306 al pasar los parametros como altura, ancho, 
#interfaz, direccion I2C a como aparece en el listado de la Raspberry y un pin de reseteo. 
#Todos pertenecientes a la pantalla.
oled = adafruit_ssd1306.SSD1306_I2C(WIDTH, HEIGHT, i2c, addr=0x3C, reset=oled_reset)

#Configuracion inicial para la comunicacion serial.

ser = serial.Serial('/dev/ttyACM0',baudrate=115200, timeout = 0.05) #Declaracion de la comunicacion
#serial al pasar parametros direccion en los dispositivos listados bajo comunicacion USB 
#(la del Arduino), tasa de baudios (velocidad de comunicacion) y timeout (permite el paso de bytes 
#despues de que se recibe cierto numero o regresa los recibidos en un determinado intervalo 
#de tiempo)
ser.flushInput() #Prepara el buffer del Serial al descartar todo su contenido previo.

#Variables a emplear para la preparacion de los datos recibidos por la comunicacion serial a
#imprimir en la pantalla, mandar a Wia y almacenar en un archivo de texto.

De la línea 54 a la línea 61 se declaran los mismos caracteres con los que se mandan los datos desde la Arduino con el fin de poder revisar los mensajes recibidos por la comunicación serial y saber que dato va con que etiqueta. También se declara la instancia de Wia junto con la clave que proporciona la página con el fin de poder vincular los datos publicados a un evento (con dichos eventos se diferencian los datos por parte de la página.

tempi = "C" #Variable que contiene el caracter que relaciona el dato recibido con el valor de la temperatura.
dis = "D" #Variable que contiene el caracter que relaciona el dato recibido con el valor de la distancia.
lux = "L" #Variable que contiene el caracter que relaciona el dato recibido con el valor de la luz.
tim = "T" #Variable que contiene el caracter que indica el paso de 10 segundos.
wia = Wia() #Se declara una instancia de Wia
wia.access_token = "d_sk_q6BqHXZezCccCZOOblxNCo8j" #Token con el cual Wia reconoce a que 
#dispositivo se relaciona los eventos y/o informacion publicada a la pagina.

De la línea 62 a la línea 260 se describe los elementos de la interfaz en cuanto a estilo, funcionamiento, etc. De la línea 62 a la línea 91 el código consta en creación de instancias para las etiquetas, botones, reloj, etc. Al igual que el formateo del estilo con las que las mismas aparecen desplegadas.

class Ui_MainWindow(QtWidgets.QMainWindow,Ui_MainWindow): #Configuracion de la pantalla principal.
        def __init__(self):
                super().__init__()
                self.setupUi(self)
                self.setWindowTitle("Smart Hub") #Se altera el nombre principal de la ventana donde se monitorean los datos.
                self.camWindow = CamWindow() #Se asigna un formato para la ventana que contiene el video de la camara.
                self.Camera = Camera()
                self.Camera2 = Camera2()
                temp = 32 #Se declara un valor inicial para la etiqueta que refleja la temperatura (Pronto se remplaza por el valor leido por la comunicacion serial)
                oImage = QImage("b1.jpg") #Se asigna a una variable la imagen que se va a utilizar como fondo.								# Se asigna a una variable la imgaen que se va a utilizar como fondo 
                sImage = oImage.scaled(QSize(800,480)) #Se dimensiona la imagen al tamaño fijo de la ventana principal
                palette = QPalette() # QPalette -> clase que "pinta" los elementos en la ventana
                palette.setBrush(10, QBrush(sImage)) # Establece el pincel en el grupo de colores especificado, en este caso, los colores de la imagen utilizada
                self.setPalette(palette) #"Pinta" la imagen de la ventana principal

                timer = QTimer(self) #Se declara un timer por medio de Qtimer.
                timer.timeout.connect(self.showTime) #Se conecta la funcion de lectura de datos al timeout del timer. Se ejecutra cada vez que pase el tiempo declarado en timer.start.
                timer.start(100) # Actualiza cada 100 milisegundos

#-------------- Style ---------------- Establece el formato de los componentes de la interfaz como las etiquetas, botones, etc. Da color, tamano, etc.
                self.tmp_lb.setStyleSheet("""QLabel {border-radius: 25px; color: white; background-color: rgba(255, 255, 255, 50)}""")
                self.tmp_lb.setText(str(temp)+"°")
                self.tmp_lb.setStyleSheet("""QLabel {border: solid white;}""")
                self.dial_lb.setStyleSheet("""QLabel {border: 2px solid white; border-radius: 10px;color: white; background-color: rgba(0, 0, 0, 50)}""")
                self.dial_lb_2.setStyleSheet("""QLabel {border: 2px solid white; border-radius: 10px;color: white; background-color: rgba(0, 0, 0, 50)}""")
                self.label.setStyleSheet("""QLabel {border-radius: 10px;color: white; background-color: rgba(0, 0, 0, 50)}""")
                self.label_2.setStyleSheet("""QLabel {border-radius: 10px;color: white; background-color: rgba(0, 0, 0, 50)}""")
                self.date.setStyleSheet("""QLabel {color: #555b6e}""")
                self.Clock.setStyleSheet("""QLabel {color: white}""")
                self.alert.setStyleSheet("""QLabel {border-radius: 10px; color: white; background-color: rgba(255, 255, 255, 50)}""")

De la línea 92 a la línea 101 ocurren las vinculaciones de los botones con sus métodos en cuanto a que debe realizar al ser presionados, el inicio del método que actualiza la fecha, la hora, captura los datos recibidos, los registra en el archivo de texto (uno por uno para que siempre que se pueda consultar apenas llegue un dato nuevo), los despliega en la pantalla oled y los manda a Wia. AL igual que se inicia el método que muestra la señal de video de la cámara.

#--------------- Events ------------------- Establece las acciones a realizar de parte de los botones al ser presionados. Los vincula con los metodos existentes en la parte inferior del programa.		
                self.cam_btn.clicked.connect(self.on_click_cam) #Conecta al boton cam con el metodo self.on_click_cam
                self.home_btn.clicked.connect(self.on_click_home) #Conecta al boton home con el metodo self.on_click_home

#----------------------------------------
                self.showDate() #Se ejecuta la funcion showDate con la que se actualiza los elementos que despliegan la fecha en la interfaz.
                self.showTime() #Se ejecuta la funcion showTime con la que se actualiza la hora, se van leyendo los valores recibidos, se publica a Wia y se actualiza el archivo de texto.
                self.Camera.start() #Se ejecuta la apertura del video de la camara.
                self.Camera.ImageUpdate.connect(self.ImgUpdSlot) #Se vincula el metodo que actualiza la imagen de la camara con un atributo de la ejecucion de la misma.
                

De la línea 102 a la línea 108 se encuentran los métodos que preparan las imágenes recibidas por la cámara de video para funcionar con las etiquetas de PyQt. Existen 2 dado a las 2 ventanas que nosotros ocupamos. La primera es una vista previa de una dimensión chica y la segunda es una escala mayor que aparece en otra ventana.

#----------------------------------------
        def ImgUpdSlot(self, Image):
                self.cam.setPixmap(QPixmap.fromImage(Image)) #Convierte la imagen en un PixMap para que la muestre PyQt. Camara 1 o ventana 1 en otras palabras.

        def ImgUpdSlot2(self, Image):
                self.camWindow.cam_display.setPixmap(QPixmap.fromImage(Image)) #Convierte la imagen en un PixMap para que la muestre PyQt. Camara 2 o ventana 2 en otras palabras.

De la línea 109 a la línea 173 se encuentra el método que recibe las lecturas, las despliega en la pantalla OLED, las registra en el archivo de texto y las manda a Wia al igual que actualiza la hora del reloj. Para esto el método se ejecuta cada 100 milisegundos en base a un timer declarado previamente. Primero actualiza el reloj con la hora actual, dado a que llega solo hasta segundos realmente no se nota un cambio hasta que este último valor cambie. Si se detecta un nuevo mensaje recibido por la comunicación serial y este no viene vacío entonces se ejecuta el resto del programa. Se decodifica el valor del mensaje recibido y se convierte a String para su manipulación posterior. Se buscan los caracteres previamente descritos en líneas de código anteriores con el fin de ubicar a que sensor está relacionado el valor recibido. Conforme se van ubicando se van actualizando variables globales en las cuales se guardan los valores junto con un texto predeterminado. También se van actualizando las etiquetas correspondientes. Posteriormente se abre un archivo al cual se le agregan (en caso de no existir se crea) los datos junto con su texto, la fecha y la hora en la que llegaron. Luego se cierra el archivo para reflejar los cambios recientes. Al llegar el carácter ´T´ como mensaje se envían los datos a Wia junto con un nombre de eventos relacionados para que los widgets de Wia puedan distinguirlos (estas llamadas a Wia absorben los recursos del programa por lo cual pueden atrasar la llegada de los otros datos causando diferencias en hora de llegada sin embargo no se pierden datos). Finalmente se borra el contenido previo de la pantalla, se ajusta el formato del texto que imprime con los valores nuevos recibidos y se imprimen los datos en esta. Si se detecta una interrupción generada por el teclado esto causa que se termine el programa completo.

def showTime(self): #Metodo que actualiza la hora del reloj, realiza la lectura de los sensores, los despliega en la pantalla OLED, los manda a Wia y los guarda en un archivo de texto.
                currentTime = QTime.currentTime() #Se llamada al metodo currentTime del objeto QT time con el fin de asignar a una variable el tiempo actual de la computadora que nos devuelve el metodo.
                displayTxt = currentTime.toString('hh:mm') #Se le aplica un formato a la hora del sistema y se convierte a string.
                self.Clock.setText(displayTxt) #Actualiza el reloj con la hora previamente formateada.
                try: #Intenta lo siguiente a menos que detecte una interrupcion por parte del teclado.
                        s= ser.readline() #Lee el dato que llego por la comunicacion serial.
                        s= s.strip() #Elimina los espacios extra al inicio y al final del dato.
                        mensaje = s.decode() #Decodifica el dato y lo asigna como mensaje.
                        if (mensaje != ''):
                                #Conversion del mensaje a un string a desplegar en la terminal y a ser revisado por el codigo subsecuente.
                                text = str(mensaje)
                                print(text) #Imprime en la terminal.
                                #Se declaran como globales procetemp, procedist, procelux con el fin de poder ser accesados por todas las condiciones posteriores a en las que se les asgina un valor.
                                global procetemp
                                global procedist
                                global procelux
                                if(text.find(tempi) != -1): #Si se encuentra el caracter 'C'.
                                        procetemp = text.replace('C', '') #Se elimina el caracter del mensaje.
                                        self.tmp_lb.setText(str(procetemp)+"°") #Se modifica el texto de la etiqueta para que muestre la nueva temperatura.
                                elif(text.find(lux) != -1): #Si se encuentra el caracter 'L'.
                                        procelux = text.replace('L', '') #Se elimina el caracter del mensaje.
                                        self.dial_lb_2.setText(str(procelux)+" %") #Se modifica el texto de la etiqueta para que muestre el nuevo brillo.
                                elif(text.find(dis) != -1): #Si se encuentra el caracter 'D'.
                                        procedist = text.replace('D', '') #Se elimina el caracter del mensaje.
                                        self.dial_lb.setText(str(procedist)+" cm") #Se modifica el texto de la etiqueta para que muestre la nueva distancia.
                                        currentDT = datetime.datetime.now() #Se extrae la informacion de la fecha actual por medio de datetime.date.now y se asigna al objeto currentDT
                                        f = open('/home/pi/Documents/Proyecto/SmartHub/monitoreo.txt','a') #Se abre el archivo de texto monitoreo con una a (append) para agregarle informacion. Si no existiera el archivo entonces lo crea.
                                        fecha = str("\n" + "Dia: " + str(currentDT.day) + "/" + str(currentDT.month) + "/" + str(currentDT.year) + ", Hora: " + str(currentDT.hour) + ":" + str(currentDT.minute) + ":" + str(currentDT.second)) #Se arma un string con los valores del dia, mes, anio, hora, minuto y segundo.
                                        f.write(fecha) #Se escribe dicho string en el archivo de texto de monitoreo.
                                        sensores = str(". Valores registrados -> Temperatura: " + str(procetemp) + " Celsius , Distancia: " + str(procedist) + " cm y Brillo: " + str(procelux) + "\n") #Se arma un string con los valores de los sensores junto con texto especificando que son cada uno.
                                        f.write(sensores) #Se escribe dicho string posterior al string de la fecha.
                                        f.close() #Se cierra el archivo para poder consultar los cambios en medio de la ejecucion si asi se deseara.
                                elif(text.find(tim) != -1): #Si se encuentra el caracter 'T'. Se publican los valores de los sensores con su formato correspondiente a Wia, se limpia la pantalla y se despliegan los nuevos ultimos valores recibidos en esos 10 segundos.
                                        wia.Event.publish(name="temp", data=procetemp) #Se publica la informacion del sensor de temperatura bajo el evento temp.
                                        time.sleep(0.01)
                                        wia.Event.publish(name="dista", data=procedist) #Se publica la informacion del sensor de temperatura bajo el evento dista.
                                        time.sleep(0.01)
                                        wia.Event.publish(name="luxa", data=procelux) #Se publica la informacion del sensor de temperatura bajo el evento luxa.
                                        time.sleep(0.01)
                                        #Limpieza de pantalla, construccion de cuadros en el marco de la misma.
                                        oled.fill(0)
                                        oled.show()
                                        image = Image.new("1", (oled.width, oled.height))
                                        draw = ImageDraw.Draw(image)
                                        draw.rectangle((0, 0, oled.width, oled.height), outline=255, fill=255)
                                        draw.rectangle(
                                        (BORDER, BORDER, oled.width - BORDER - 1, oled.height - BORDER - 1),
                                        outline=0,
                                        fill=0,
                                        )
                                        font = ImageFont.load_default()
                                        #Cambio de text a desplegar en la pantalla OLED por uno conformado con los 3 valores de los sensores y despliegue del mensaje.
                                        text = str(str(procetemp) + ", " + str(procedist) + ", " + str(procelux))
                                        (font_width, font_height) = font.getsize(text)
                                        draw.text(
                                        (oled.width // 2 - font_width // 2, oled.height // 2 - font_height // 2),
                                        text,
                                        font=font,
                                        fill=255,
                                        )
                                        oled.image(image)
                                        oled.show()
                except KeyboardInterrupt: #En caso de detectarse una interrupcion causada por el teclado se termina el programa.
                        sys.exit() #Terminacion del programa al manejar la excepcion.

De la línea 174 a la línea 178 se encuentra el método que actualiza la etiqueta de la fecha al iniciar el programa.

def showDate(self): #Metodo el cual muestra la fecha en el cuadro de texto destinado para esta en la intefaz.
                today = datetime.date.today() #Se extrae la informacion de la fecha actual por medio de datetime.date.today y se asigna a today.
                read = today.strftime("%a, %b %d") #Se convierte en string la fecha asignada con un formato especifico por medio de strftime y se almacena en la variable read.
                self.date.setText(read) #Se muestra la informacion en el cuadro de texto asignado para la fecha al mandar el texto de read.
                        

De la línea 179 a la 186 se encuentra el método con el cual al presionarse el botón con una imagen de una casa se abre el archivo de texto en su último estado.

def on_click_home(self): #Metodo que se ejecuta al presionar el boton de casa. Abre el archivo de texto de monitoreo en la aplicacion default cada vez que se presiona.
                if (self.rb_home.isChecked()): #Si el circulo que aparece debajo estuviera encendido
                        self.rb_home.setChecked(False) #Se apaga el circulo
                        subprocess.call(["xdg-open", r'/home/pi/Documents/Proyecto/SmartHub/monitoreo.txt']) #Se abre el archivo de monitoreo en la aplicacion default.
                else:
                        self.rb_home.setChecked(True) #Si estuviera apagado entonces se enciende.
                        subprocess.call(["xdg-open", r'/home/pi/Documents/Proyecto/SmartHub/monitoreo.txt']) #Se abre el archivo de monitoreo en la aplicacion default.

De la línea 187 a la línea 203 se encuentra el método que alterna entre las ventanas de la cámara. Al iniciar el programa una vista de la cámara se ve en una ventana pequeña a lado de la etiqueta que registra el valor de la luz y al presionarse el botón de la cámara se oculta esta ventana y se abre otra en la cual se observa la misma imagen, pero con un tamaño mayor. Si se vuelve a presionar el botón se cierra la ventana con la imagen ampliada y vuelve a aparecer la que tiene la imagen chica.

def on_click_cam(self): #Metodo que se ejecuta al presionar el boton de la camara. Abre una ventana secundaria con una vista ampliada de la camara. En el caso de que esta estuviera abierta entonces se cierra.
                if (self.rb_cam.isChecked()): #En caso de estar cerrada la segunda ventana.
                        self.Camera.stop() #Se detiene la camara 1, la camara de la ventana chica.
                        self.rb_cam.setChecked(False) #Altera el circulo asociado a la ventana de la camara y lo pone en falso.
                        self.cam.setHidden(True) #Oculta el contenido del video de la camara 1.
                        self.camWindow.show() #Muestra la nueva ventana de la camara 2.
                        self.Camera2.start() #Inicia el video de la camara 2 a desplegar.
                        self.Camera2.ImageUpdate.connect(self.ImgUpdSlot2) #Vincula el metodo que va actualizando la imagen de la camara con lo que la prepara para trabajar con PyQt.
                        
                else: #En caso de estar abierta la segunda ventana.
                        self.Camera2.stop() #Se detiene la camara 2, la camara de la ventana grande.
                        self.rb_cam.setChecked(True) #Altera el circulo asociado a la ventana de la camara y lo pone encendido.
                        self.cam.setHidden(False) #Reaparece el contenido del video de la camara 1.
                        self.camWindow.hide() #Oculta la ventana grande de la camara 2.
                        self.Camera.start() #Inicia el video de la camara 1 a desplegar.
                        self.Camera.ImageUpdate.connect(self.ImgUpdSlot) #Vincula el metodo que va actualizando la imagen de la camara con lo que la prepara para trabajar con PyQt.		

De la línea 204 a la línea 237 se encuentran las clases de las cámaras con las cuales se formatean sus imágenes, se inician sus videos y se detienen.

#----------------------------------------
class Camera(QThread): #Clase que formatea el video de la camara 1, la acciona y la detiene.
        ImageUpdate = pyqtSignal(QImage)
        def run(self):
                self.ThreadActive = True
                while self.ThreadActive:
                        ret, frame = Capture.read()
                        if ret:
                                Image = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
                                FlippedImage = cv2.flip(Image, 1)
                                convert2QtFom = QImage(FlippedImage.data, FlippedImage.shape[1], FlippedImage.shape[0], QImage.Format_RGB888)
                                Pic = convert2QtFom.scaled(161,101, Qt.KeepAspectRatio)
                                self.ImageUpdate.emit(Pic)
        def stop(self): #En el caso de detener la imagen simplemente pone en falso el thread que mantienen la imagen corriendo.
                self.ThreadActive = False
                self.quit()

class Camera2(QThread): #Clase que formatea el video de la camara 2, la acciona y la detiene.
        ImageUpdate = pyqtSignal(QImage)
        def run(self):
                self.ThreadActive = True
                while self.ThreadActive:
                        ret, frame = Capture.read()
                        if ret:
                                Image = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
                                FlippedImage = cv2.flip(Image, 1)
                                convert2QtFom = QImage(FlippedImage.data, FlippedImage.shape[1], FlippedImage.shape[0], QImage.Format_RGB888)
                                Pic = convert2QtFom.scaled(500,300, Qt.KeepAspectRatio)
                                self.ImageUpdate.emit(Pic)
        def stop(self): #En el caso de detener la imagen simplemente pone en falso el thread que mantienen la imagen corriendo.
                self.ThreadActive = False
                self.quit()


De la línea 238 a la línea 260 se encuentra la clase que provee de formato a la ventana con la vista de la cámara más grande dado a que esta no se creó con QT Designer y fue hecha desde cero. Debido a esto requirió los elementos con los cuales vincular el video de la cámara, aparecer al ser invocada, etc.

class CamWindow(QWidget): #Clase con la que se forma la ventana de la version mas grande de la camara.
        def __init__(self, parent = None):
                super().__init__()
                self.setWindowTitle("Cam")

                self.setFixedSize(420, 380) #Se dimensiona el cuadro de la imagen para que la misma no aparezca pixeleada mas adelante.

                oImage = QImage("b4.jpg") #Se asigna a una variable la imgaen que se va a utilizar como fondo 
                sImage = oImage.scaled(QSize(600,450)) #Se dimensiona la imagen al tamaño fijo de la ventana principal
                palette = QPalette() #QPalette -> clase que "pinta" los elementos en la ventana
                palette.setBrush(10, QBrush(sImage)) #Establece el pincel en el grupo de colores especificado, en este caso, los colores de la imagen utilizada
                self.setPalette(palette)

                self.cam_display = QtWidgets.QLabel(self)
                self.cam_display.setGeometry(QtCore.QRect(10, 10, 400, 300))
                self.cam_display.setAutoFillBackground(True)
                self.cam_display.setFrameShape(QtWidgets.QFrame.Panel)
                self.cam_display.setFrameShadow(QtWidgets.QFrame.Sunken)
                self.cam_display.setText("")
                self.setWindowFlag(QtCore.Qt.WindowCloseButtonHint, False)
                self.setWindowFlag(QtCore.Qt.WindowMinimizeButtonHint, False)


De la línea 261 a la línea a la 265 se encuentran las líneas que crean la ventana principal de la interfaz, invocan los widgets relacionados y los ejecutan.

if __name__ == "__main__":
        app = QtWidgets.QApplication([]) #Declaracion de app la cual se usa para la aparicion de la ventana principal.
        window = Ui_MainWindow() #Asignacion de la invocacion de la ventana principal a la variable window.
        window.show() #Metodo show que causa la aparicion de la ventana principal.
        app.exec_() #Ejecucion de la ventana principal