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.

254 lines
10 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. from matplotlib.lines import Line2D #Para hacer las líneas de las gráficas
  15. #Variables a utilizar
  16. global isRun #Valor booleano de programa corriendo
  17. global on1 #Valor booleano del estatus del transistor
  18. global on2
  19. global conected
  20. conected = False
  21. on1 = False #Inicializa transistor 1 apagado
  22. on2 = False #Inicializa transistor 2 apagado
  23. isReceiving = False #Valor booleano de recibiendo datos
  24. isRun = False
  25. dato = 0.0 #Variable del dato convertido a float
  26. serialPort = 'COM3' #Puerto al que está conectado el Arduino
  27. baudRate = 9600 #Baudios configurados en Arduino
  28. datos = 0.0 #Variable de dato a leer desde Arduino
  29. estado = "off" #Variable de estado que se enviará al serial
  30. numData = 2 #Número de datos a recibir de Arduino
  31. #Función de lectura de datos en Arduino
  32. def leer_datos():
  33. time.sleep(1.0)
  34. arduino.reset_input_buffer() #limpia el buffer de datos
  35. print("leyendo")
  36. while(conected):
  37. if(isRun == True):
  38. global isReceiving #valor booleano de recibiendo datos
  39. global dato #dato a recibir desde Arduino
  40. global data #Colección de datos a graficar
  41. for i in range(numData):
  42. datos = arduino.readline().decode("utf-8").strip() #Lectura del dato
  43. print(datos)
  44. if(datos != ''): #Asegurar que se haya leído un dato
  45. print("RECIBIENDO..." + str(i))
  46. dato = float(datos) #Conversión del dato en float
  47. data[i].append(dato) #Agrega el dato a la colección
  48. if(i == 0):
  49. var.set("TEMPERATURA 1: " + str(dato) + " °C") #impresión del dato numérico en la interfaz
  50. else:
  51. var2.set("TEMPERATURE 2: " + str(dato) + " °C") #impresión del dato numérico en la interfaz
  52. else:
  53. break;
  54. isReceiving = True #Confirmación de que se ha recibido el dato
  55. #Función para iniciar la gráfica
  56. def iniciarGrafica(self, muestras,lines):
  57. global dato
  58. #if(datos != ''):
  59. lines[0].set_data(range(muestras), data[0]) #Se grafica en la línea la colección de datos
  60. lines[1].set_data(range(muestras), data[1])
  61. #Establecer conexión con Arduino
  62. def conectar_serial():
  63. global arduino #Variable que va aguardar la conexión
  64. global conected
  65. try:
  66. arduino = serial.Serial(serialPort, baudRate) #Asignación del objeto a la variable
  67. arduino.timeout = 0.2 #Tiempo a esperar para que haya datos disponibles en el puerto serie
  68. time.sleep(0.5) #Tiempo muerto para permitir la conexión
  69. print("CONECTADO")
  70. btnStart.config(state = "normal")
  71. btnConectar.config(state = "disabled")
  72. btnManual.config(state = "normal")
  73. btnManual2.config(state = "normal")
  74. conected = True
  75. except:
  76. print("Error de conexión")
  77. #Hilo para la lectura de datos constante
  78. def iniciar_hilo():
  79. global thread #Variable donde guardar el hilo
  80. global isRun
  81. btnStart.config(state = "disabled")
  82. isRun = True
  83. thread = Thread(target=leer_datos) #Creación del hilo con la función para leer los datos
  84. thread.start() #Inicio de la lectura
  85. btnStart.config(state = "disabled")
  86. btnPause.config(state = "normal")
  87. #Pestaña para guardar la gráfica
  88. def guardarGrafica():
  89. plt.savefig('miFigura.png')
  90. #Función de control Manual (ON/OFF controlado por el usuario)
  91. def control1():
  92. global on
  93. global estado
  94. global arduino
  95. if on1 == False: #Cuando el transistor está apagado
  96. on1 = True #Cambia la variable de condición
  97. btnManual.config(bg = "#55DE1E", text = "ON") #Cambia color y texto del botón
  98. estado = "on1" #cambia la variable de estado
  99. arduino.write(estado.encode()) #Se envía encender al serial de Arduino
  100. else: #Cuando el transistor está encendido
  101. on1 = False #Cambia la variable de condición
  102. btnManual.config(bg = "red", text = "OFF") #Cambia color y texto del botón
  103. estado = "off1" #Cambia la variable de estado
  104. arduino.write(estado.encode()) #Se envía apagar al serial de Arduino
  105. def control2():
  106. global on2
  107. global estado
  108. global arduino
  109. if on2 == False: #Cuando el transistor está apagado
  110. on2 = True #Cambia la variable de condición
  111. btnManual2.config(bg = "#55DE1E", text = "ON") #Cambia color y texto del botón
  112. estado = "on2" #cambia la variable de estado
  113. arduino.write(estado.encode()) #Se envía encender al serial de Arduino
  114. else: #Cuando el transistor está encendido
  115. on2 = False #Cambia la variable de condición
  116. btnManual2.config(bg = "red", text = "OFF") #Cambia color y texto del botón
  117. estado = "off2" #Cambia la variable de estado
  118. arduino.write(estado.encode()) #Se envía apagar al serial de Arduino
  119. #Ventana para control según una temperatura dada (Automático)
  120. #def control2():
  121. # window3 = tk.Toplevel()
  122. # window3.resizable(width = 0, height = 0)
  123. # window3.title('Control ON/OFF')
  124. # window3.geometry('500x300')
  125. #Función para pausar la gráfica
  126. def pausar():
  127. global isRun
  128. global arduino
  129. isRun = False
  130. anim.event_source.stop()
  131. arduino.close()
  132. btnResume.config(state = "normal")
  133. btnPause.config(state = "disabled")
  134. #Función para continuar graficando
  135. def reanudar():
  136. global arduino
  137. global isRun
  138. conectar_serial()
  139. arduino.reset_input_buffer() #limpia el buffer de datos
  140. isRun = True
  141. anim.event_source.start()
  142. btnResume.config(state = "disabled")
  143. btnPause.config(state = "normal")
  144. #Función para finaliar la conexión con Arduino
  145. def desconectar_serial():
  146. global isRun
  147. global conected
  148. anim.event_source.stop()
  149. isRun = False
  150. conected = False
  151. arduino.close()
  152. btnPause.config(state = "disabled")
  153. btnResume.config(state = "disabled")
  154. btnManual.config(state = "disabled")
  155. btnManual2.config(state = "disabled")
  156. #Variables a graficar
  157. muestras = 100 #Número de datos a visualizar
  158. tiempoMuestreo = 100
  159. data = []
  160. lines = []
  161. for i in range(numData):
  162. data.append(collections.deque([0] * muestras, maxlen = muestras)) #Rango de datos
  163. lines.append(Line2D([], [], color = "blue"))
  164. fig = plt.figure(facecolor = '0.94') #creación de la gráfica (figura)
  165. ax1 = fig.add_subplot(2, 1, 1, xlim=(0,100), ylim=(0, 150)) #Rango de ejes
  166. ax1.title.set_text("Sensor 1 - Arduino")
  167. ax1.set_ylabel("Voltaje")
  168. ax1.add_line(lines[0])
  169. ax2 = fig.add_subplot(2, 1, 2, xlim=(0,100), ylim=(0, 150)) #Rango de ejes
  170. ax2.title.set_text("Sensor 2 - Arduino")
  171. ax2.set_xlabel("Muestras")
  172. ax2.set_ylabel("Voltaje")
  173. ax2.add_line(lines[1])
  174. root= tk.Tk()
  175. root.title("Sistema de calentamiento")
  176. var = tk.StringVar()
  177. var2 = tk.StringVar()
  178. frame = tk.Frame(root, bd=2)
  179. frame.grid(column=0, row=3, columnspan=2, sticky="nsew")
  180. frame1 = tk.Frame(root)
  181. frame1.grid(column=0, row=1, columnspan=2, sticky="EW")
  182. frame2 = tk.Frame(root)
  183. frame2.grid(column=0, row=2, columnspan=2, sticky="EW")
  184. frame0 = tk.Frame(root)
  185. frame0.grid(column=0, row=0, columnspan=2, sticky="EW")
  186. root.columnconfigure(0, weight=1)
  187. root.columnconfigure(1, weight=1)
  188. #self.master.rowconfigure(0, weigh=1)
  189. #self.master.rowconfigure(1, weigh=1)
  190. #self.master.rowconfigure(2, weigh=1)
  191. root.rowconfigure(3, weigh=5)
  192. canvas = FigureCanvasTkAgg(fig, master=frame)
  193. canvas.get_tk_widget().pack(padx=0, pady=0, expand=True, fill='both')
  194. labelBlank = tk.Label(frame1, text="")
  195. labelBlank.grid(row=0, column=4, pady=2, padx=25)
  196. btnManual = tk.Button(frame1, text = "OFF", command = control1, bg = "red", state = "disabled")
  197. btnManual.grid(row=0, column=6, pady=2, padx=10)
  198. labelState = tk.Label(frame1, text="Transistor 1 State:")
  199. labelState.grid(row=0, column=5, pady=2, padx=5)
  200. labelBlank2 = tk.Label(frame1, text="")
  201. labelBlank2.grid(row=0, column=7, pady=2, padx=125)
  202. btnManual2 = tk.Button(frame1, text = "OFF", command = control2, bg = "red", state = "disabled")
  203. btnManual2.grid(row=0, column=9, pady=2, padx=10)
  204. labelState2 = tk.Label(frame1, text="Transistor 2 State:")
  205. labelState2.grid(row=0, column=8, pady=2, padx=5)
  206. #btnOnOff = tk.Button(frame2, text = "ON/OFF", command = control2)
  207. #btnOnOff.grid(row=0, column=1, pady=2, padx=10)
  208. btnConectar = tk.Button(frame1, text = "Connect", command = conectar_serial, bg="#00F1FC")
  209. btnConectar.grid(row=0, column=0, pady=2, padx=10)
  210. btnStart = tk.Button(frame1, text = "Start", command = iniciar_hilo, bg="#008C17", state="disabled")
  211. btnStart.grid(row=0, column=1, pady=2, padx=10)
  212. btnPause = tk.Button(frame1, text = "Pause", command = pausar, bg="#E2E200", state="disabled")
  213. btnPause.grid(row=0, column=2, pady=2, padx=10)
  214. btnResume = tk.Button(frame1, text = "Resume", command = reanudar, bg="#00F428", state="disabled")
  215. btnResume.grid(row=0, column=3, pady=2, padx=10)
  216. btnDesconectar = tk.Button(frame2, text='Disconnect', command = desconectar_serial, bg="#FE5E5E")
  217. btnDesconectar.grid(row=0, column=0, pady=2, padx=10)
  218. labelData = tk.Label(frame2, textvariable=var, font="Helvetica 10 bold")
  219. labelData.grid(row=1, column=1, pady=2, padx=230)
  220. labelData2 = tk.Label(frame2, textvariable=var2, font="Helvetica 10 bold")
  221. labelData2.grid(row=1, column=2, pady=2, padx=1)
  222. barraMenu = tk.Menu(frame0)
  223. barra1 = tk.Menu(barraMenu)
  224. barra1.add_command(label="Guardar gráfica", command=guardarGrafica)
  225. barraMenu.add_cascade(label="Archivo", menu=barra1)
  226. root.config(menu=barraMenu)
  227. #Animación de la gráfica (figura, función que grafica la línea, argumentos para graficar, rango de la figura)
  228. anim = animation.FuncAnimation(fig, iniciarGrafica, fargs=(muestras, lines), interval = tiempoMuestreo, cache_frame_data=False)
  229. root.geometry('1000x600')
  230. root.mainloop()