Heating system interface for communication with Arduino and data graphing
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

168 lines
6.2 KiB

  1. # -*- coding: utf-8 -*-
  2. """
  3. Created on Wed Feb 15 10:44:49 2023
  4. @author: david
  5. """
  6. from threading import Thread #Funciones para hilos
  7. import collections #Colección de datos para graficarlos
  8. import matplotlib.pyplot as plt #Gráficación de datos
  9. import matplotlib.animation as animation #Animar la gráfica
  10. import time #Delays
  11. import serial #comunicación con el monitor serial de Arduino
  12. from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg #Crear una figura para insertar en tkinter
  13. import tkinter as tk #Para la interfaz
  14. #Variables a utilizar
  15. global isRun #Valor booleano de programa corriendo
  16. isReceiving = False #Valor booleano de recibiendo datos
  17. isRun = True
  18. dato1 = 0.0 #Variable del dato a leer desde Arduino
  19. serialPort = 'COM3' #Puerto al que está conectado el Arduino
  20. baudRate = 9600 #Baudios configurados en Arduino
  21. #Función de lectura de datos en Arduino
  22. def leer_datos():
  23. time.sleep(1.0)
  24. arduino.reset_input_buffer() #limpia el buffer de datos
  25. print("leyendo")
  26. while(isRun):
  27. global isReceiving #valor booleano de recibiendo datos
  28. global dato1 #dato a recibir desde Arduino
  29. print("RECIBIENDO...")
  30. dato1 = float(arduino.readline().decode("utf-8").strip()) #Lectura del dato y conversión a float
  31. print(dato1)
  32. var.set("TEMPERATURA: " + str(dato1) + " °C") #impresión del dato numérico en la interfaz
  33. isReceiving = True #Confirmación de que se ha recibido el dato
  34. #Función para iniciar la gráfica
  35. def iniciarGrafica(self, muestras,lines):
  36. global dato1
  37. data.append(dato1) #Agrega el dato a la colección
  38. lines.set_data(range(muestras), data) #Se grafica en la línea la colección de datos
  39. #Establecer conexión con Arduino
  40. def conectar_serial():
  41. global arduino #Variable que va aguardar la conexión
  42. try:
  43. arduino = serial.Serial(serialPort, baudRate) #Asignación del objeto a la variable
  44. arduino.timeout = 0.5 #Tiempo a esperar para que haya datos disponibles en el puerto serie
  45. time.sleep(0.5) #Tiempo muerto para permitir la conexión
  46. print("CONECTADO")
  47. btnStart.config(state = "normal")
  48. btnConectar.config(state = "disabled")
  49. except:
  50. print("Error de conexión")
  51. #Hilo para la lectura de datos constante
  52. def iniciar_hilo():
  53. global thread #Variable donde guardar el hilo
  54. thread = Thread(target=leer_datos) #Creación del hilo con la función para leer los datos
  55. thread.start() #Inicio de la lectura
  56. btnStart.config(state = "disabled")
  57. btnPause.config(state = "normal")
  58. #Pestaña para guardar la gráfica
  59. def guardarGrafica():
  60. plt.savefig('miFigura.png')
  61. #Ventana de control Manual (ON/OFF controlado por el usuario)
  62. def control1():
  63. window2 = tk.Toplevel()
  64. window2.resizable(width = 0, height = 0)
  65. window2.title('Control Manual')
  66. window2.geometry('500x300')
  67. #Ventana para control según una temperatura dada (Automático)
  68. def control2():
  69. window3 = tk.Toplevel()
  70. window3.resizable(width = 0, height = 0)
  71. window3.title('Control ON/OFF')
  72. window3.geometry('500x300')
  73. #Función para pausar la gráfica
  74. def pausar():
  75. anim.event_source.stop()
  76. btnResume.config(state = "normal")
  77. btnPause.config(state = "disabled")
  78. #Función para continuar graficando
  79. def reanudar():
  80. anim.event_source.start()
  81. btnResume.config(state = "disabled")
  82. btnPause.config(state = "normal")
  83. #Función para finaliar la conexión con Arduino
  84. def desconectar_serial():
  85. global isRun
  86. anim.event_source.stop()
  87. isRun = False
  88. arduino.close()
  89. btnPause.config(state = "disabled")
  90. btnResume.config(state = "disabled")
  91. #Variables a graficar
  92. muestras = 100 #Número de datos a visualizar
  93. data = collections.deque([0] * muestras, maxlen = muestras) #Rango de datos
  94. tiempoMuestreo = 100
  95. fig = plt.figure(facecolor = '0.94') #creación de la gráfica (figura)
  96. ax = plt.axes(xlim=(0,100), ylim=(-40, 150)) #Rango de ejes
  97. plt.title("Sensor 1 - Arduino")
  98. ax.set_xlabel("Muestras")
  99. ax.set_ylabel("Voltaje")
  100. lines = ax.plot([], [])[0] #Creación de la línea a graficar
  101. root= tk.Tk()
  102. root.title("Sistema de calentamiento")
  103. var = tk.StringVar()
  104. frame = tk.Frame(root, bd=2)
  105. frame.grid(column=0, row=3, columnspan=2, sticky="nsew")
  106. frame1 = tk.Frame(root)
  107. frame1.grid(column=0, row=1, columnspan=2, sticky="EW")
  108. frame2 = tk.Frame(root)
  109. frame2.grid(column=0, row=2, columnspan=2, sticky="EW")
  110. frame0 = tk.Frame(root)
  111. frame0.grid(column=0, row=0, columnspan=2, sticky="EW")
  112. root.columnconfigure(0, weight=1)
  113. root.columnconfigure(1, weight=1)
  114. #self.master.rowconfigure(0, weigh=1)
  115. #self.master.rowconfigure(1, weigh=1)
  116. #self.master.rowconfigure(2, weigh=1)
  117. root.rowconfigure(3, weigh=5)
  118. canvas = FigureCanvasTkAgg(fig, master=frame)
  119. canvas.get_tk_widget().pack(padx=0, pady=0, expand=True, fill='both')
  120. btnManual = tk.Button(frame2, text = "Manual", command = control1)
  121. btnManual.grid(row=0, column=0, pady=2, padx=10)
  122. btnOnOff = tk.Button(frame2, text = "ON/OFF", command = control2)
  123. btnOnOff.grid(row=0, column=1, pady=2, padx=10)
  124. btnConectar = tk.Button(frame1, text = "Connect", command = conectar_serial, bg="#00F1FC")
  125. btnConectar.grid(row=0, column=0, pady=2, padx=10)
  126. btnStart = tk.Button(frame1, text = "Start", command = iniciar_hilo, bg="#008C17", state="disabled")
  127. btnStart.grid(row=0, column=1, pady=2, padx=10)
  128. btnPause = tk.Button(frame1, text = "Pause", command = pausar, bg="#E2E200", state="disabled")
  129. btnPause.grid(row=0, column=2, pady=2, padx=10)
  130. btnResume = tk.Button(frame1, text = "Resume", command = reanudar, bg="#00F428", state="disabled")
  131. btnResume.grid(row=0, column=3, pady=2, padx=10)
  132. btnDesconectar = tk.Button(frame2, text='Disconnect', command = desconectar_serial, bg="#FE5E5E")
  133. btnDesconectar.grid(row=0, column=3, pady=2, padx=10)
  134. labelData = tk.Label(frame2, textvariable=var)
  135. labelData.grid(row=0, column=4, pady=2, padx=500)
  136. barraMenu = tk.Menu(frame0)
  137. barra1 = tk.Menu(barraMenu)
  138. barra1.add_command(label="Guardar gráfica", command=guardarGrafica)
  139. barraMenu.add_cascade(label="Archivo", menu=barra1)
  140. root.config(menu=barraMenu)
  141. #Animación de la gráfica (figura, función que grafica la línea, argumentos para graficar, rango de la figura)
  142. anim = animation.FuncAnimation(fig, iniciarGrafica, fargs=(muestras, lines), interval = tiempoMuestreo)
  143. root.geometry('1000x600')
  144. root.mainloop()