# -*- coding: utf-8 -*- """ Created on Wed Feb 15 10:44:49 2023 @author: david """ from threading import Thread #Funciones para hilos import collections #Colección de datos para graficarlos import matplotlib.pyplot as plt #Gráficación de datos import matplotlib.animation as animation #Animar la gráfica import time #Delays import serial #comunicación con el monitor serial de Arduino from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg #Crear una figura para insertar en tkinter import tkinter as tk #Para la interfaz from matplotlib.lines import Line2D #Para hacer las líneas de las gráficas #Variables a utilizar global isRun #Valor booleano de programa corriendo global on1 #Valor booleano del estatus del transistor global on2 global conected conected = False on1 = False #Inicializa transistor 1 apagado on2 = False #Inicializa transistor 2 apagado isReceiving = False #Valor booleano de recibiendo datos isRun = False dato = 0.0 #Variable del dato convertido a float serialPort = 'COM3' #Puerto al que está conectado el Arduino baudRate = 9600 #Baudios configurados en Arduino datos = 0.0 #Variable de dato a leer desde Arduino estado = "off" #Variable de estado que se enviará al serial numData = 2 #Número de datos a recibir de Arduino #Función de lectura de datos en Arduino def leer_datos(): time.sleep(1.0) arduino.reset_input_buffer() #limpia el buffer de datos print("leyendo") while(conected): if(isRun == True): global isReceiving #valor booleano de recibiendo datos global dato #dato a recibir desde Arduino global data #Colección de datos a graficar for i in range(numData): datos = arduino.readline().decode("utf-8").strip() #Lectura del dato print(datos) if(datos != ''): #Asegurar que se haya leído un dato print("RECIBIENDO..." + str(i)) dato = float(datos) #Conversión del dato en float data[i].append(dato) #Agrega el dato a la colección if(i == 0): var.set("TEMPERATURA 1: " + str(dato) + " °C") #impresión del dato numérico en la interfaz else: var2.set("TEMPERATURE 2: " + str(dato) + " °C") #impresión del dato numérico en la interfaz else: break; isReceiving = True #Confirmación de que se ha recibido el dato #Función para iniciar la gráfica def iniciarGrafica(self, muestras,lines): global dato #if(datos != ''): lines[0].set_data(range(muestras), data[0]) #Se grafica en la línea la colección de datos lines[1].set_data(range(muestras), data[1]) #Establecer conexión con Arduino def conectar_serial(): global arduino #Variable que va aguardar la conexión global conected try: arduino = serial.Serial(serialPort, baudRate) #Asignación del objeto a la variable arduino.timeout = 0.2 #Tiempo a esperar para que haya datos disponibles en el puerto serie time.sleep(0.5) #Tiempo muerto para permitir la conexión print("CONECTADO") btnStart.config(state = "normal") btnConectar.config(state = "disabled") btnManual.config(state = "normal") btnManual2.config(state = "normal") conected = True except: print("Error de conexión") #Hilo para la lectura de datos constante def iniciar_hilo(): global thread #Variable donde guardar el hilo global isRun btnStart.config(state = "disabled") isRun = True thread = Thread(target=leer_datos) #Creación del hilo con la función para leer los datos thread.start() #Inicio de la lectura btnStart.config(state = "disabled") btnPause.config(state = "normal") #Pestaña para guardar la gráfica def guardarGrafica(): plt.savefig('miFigura.png') #Función de control Manual (ON/OFF controlado por el usuario) def control1(): global on global estado global arduino if on1 == False: #Cuando el transistor está apagado on1 = True #Cambia la variable de condición btnManual.config(bg = "#55DE1E", text = "ON") #Cambia color y texto del botón estado = "on1" #cambia la variable de estado arduino.write(estado.encode()) #Se envía encender al serial de Arduino else: #Cuando el transistor está encendido on1 = False #Cambia la variable de condición btnManual.config(bg = "red", text = "OFF") #Cambia color y texto del botón estado = "off1" #Cambia la variable de estado arduino.write(estado.encode()) #Se envía apagar al serial de Arduino def control2(): global on2 global estado global arduino if on2 == False: #Cuando el transistor está apagado on2 = True #Cambia la variable de condición btnManual2.config(bg = "#55DE1E", text = "ON") #Cambia color y texto del botón estado = "on2" #cambia la variable de estado arduino.write(estado.encode()) #Se envía encender al serial de Arduino else: #Cuando el transistor está encendido on2 = False #Cambia la variable de condición btnManual2.config(bg = "red", text = "OFF") #Cambia color y texto del botón estado = "off2" #Cambia la variable de estado arduino.write(estado.encode()) #Se envía apagar al serial de Arduino #Ventana para control según una temperatura dada (Automático) #def control2(): # window3 = tk.Toplevel() # window3.resizable(width = 0, height = 0) # window3.title('Control ON/OFF') # window3.geometry('500x300') #Función para pausar la gráfica def pausar(): global isRun global arduino isRun = False anim.event_source.stop() arduino.close() btnResume.config(state = "normal") btnPause.config(state = "disabled") #Función para continuar graficando def reanudar(): global arduino global isRun conectar_serial() arduino.reset_input_buffer() #limpia el buffer de datos isRun = True anim.event_source.start() btnResume.config(state = "disabled") btnPause.config(state = "normal") #Función para finaliar la conexión con Arduino def desconectar_serial(): global isRun global conected anim.event_source.stop() isRun = False conected = False arduino.close() btnPause.config(state = "disabled") btnResume.config(state = "disabled") btnManual.config(state = "disabled") btnManual2.config(state = "disabled") #Variables a graficar muestras = 100 #Número de datos a visualizar tiempoMuestreo = 100 data = [] lines = [] for i in range(numData): data.append(collections.deque([0] * muestras, maxlen = muestras)) #Rango de datos lines.append(Line2D([], [], color = "blue")) fig = plt.figure(facecolor = '0.94') #creación de la gráfica (figura) ax1 = fig.add_subplot(2, 1, 1, xlim=(0,100), ylim=(0, 150)) #Rango de ejes ax1.title.set_text("Sensor 1 - Arduino") ax1.set_ylabel("Voltaje") ax1.add_line(lines[0]) ax2 = fig.add_subplot(2, 1, 2, xlim=(0,100), ylim=(0, 150)) #Rango de ejes ax2.title.set_text("Sensor 2 - Arduino") ax2.set_xlabel("Muestras") ax2.set_ylabel("Voltaje") ax2.add_line(lines[1]) root= tk.Tk() root.title("Sistema de calentamiento") var = tk.StringVar() var2 = tk.StringVar() frame = tk.Frame(root, bd=2) frame.grid(column=0, row=3, columnspan=2, sticky="nsew") frame1 = tk.Frame(root) frame1.grid(column=0, row=1, columnspan=2, sticky="EW") frame2 = tk.Frame(root) frame2.grid(column=0, row=2, columnspan=2, sticky="EW") frame0 = tk.Frame(root) frame0.grid(column=0, row=0, columnspan=2, sticky="EW") root.columnconfigure(0, weight=1) root.columnconfigure(1, weight=1) #self.master.rowconfigure(0, weigh=1) #self.master.rowconfigure(1, weigh=1) #self.master.rowconfigure(2, weigh=1) root.rowconfigure(3, weigh=5) canvas = FigureCanvasTkAgg(fig, master=frame) canvas.get_tk_widget().pack(padx=0, pady=0, expand=True, fill='both') labelBlank = tk.Label(frame1, text="") labelBlank.grid(row=0, column=4, pady=2, padx=25) btnManual = tk.Button(frame1, text = "OFF", command = control1, bg = "red", state = "disabled") btnManual.grid(row=0, column=6, pady=2, padx=10) labelState = tk.Label(frame1, text="Transistor 1 State:") labelState.grid(row=0, column=5, pady=2, padx=5) labelBlank2 = tk.Label(frame1, text="") labelBlank2.grid(row=0, column=7, pady=2, padx=125) btnManual2 = tk.Button(frame1, text = "OFF", command = control2, bg = "red", state = "disabled") btnManual2.grid(row=0, column=9, pady=2, padx=10) labelState2 = tk.Label(frame1, text="Transistor 2 State:") labelState2.grid(row=0, column=8, pady=2, padx=5) #btnOnOff = tk.Button(frame2, text = "ON/OFF", command = control2) #btnOnOff.grid(row=0, column=1, pady=2, padx=10) btnConectar = tk.Button(frame1, text = "Connect", command = conectar_serial, bg="#00F1FC") btnConectar.grid(row=0, column=0, pady=2, padx=10) btnStart = tk.Button(frame1, text = "Start", command = iniciar_hilo, bg="#008C17", state="disabled") btnStart.grid(row=0, column=1, pady=2, padx=10) btnPause = tk.Button(frame1, text = "Pause", command = pausar, bg="#E2E200", state="disabled") btnPause.grid(row=0, column=2, pady=2, padx=10) btnResume = tk.Button(frame1, text = "Resume", command = reanudar, bg="#00F428", state="disabled") btnResume.grid(row=0, column=3, pady=2, padx=10) btnDesconectar = tk.Button(frame2, text='Disconnect', command = desconectar_serial, bg="#FE5E5E") btnDesconectar.grid(row=0, column=0, pady=2, padx=10) labelData = tk.Label(frame2, textvariable=var, font="Helvetica 10 bold") labelData.grid(row=1, column=1, pady=2, padx=230) labelData2 = tk.Label(frame2, textvariable=var2, font="Helvetica 10 bold") labelData2.grid(row=1, column=2, pady=2, padx=1) barraMenu = tk.Menu(frame0) barra1 = tk.Menu(barraMenu) barra1.add_command(label="Guardar gráfica", command=guardarGrafica) barraMenu.add_cascade(label="Archivo", menu=barra1) root.config(menu=barraMenu) #Animación de la gráfica (figura, función que grafica la línea, argumentos para graficar, rango de la figura) anim = animation.FuncAnimation(fig, iniciarGrafica, fargs=(muestras, lines), interval = tiempoMuestreo, cache_frame_data=False) root.geometry('1000x600') root.mainloop()