From b60f47cfb60f1126c75ef5b2686d17a6378364f9 Mon Sep 17 00:00:00 2001 From: adrigongv23 Date: Wed, 25 Feb 2026 12:09:08 +0100 Subject: [PATCH] =?UTF-8?q?Prueba=20inicial=20rpm=20y=20bateria=20a=C3=B1a?= =?UTF-8?q?dida?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Escritorio_Boxes/diseño.py | 37 +++++--- Escritorio_Boxes/monitor.py | 167 +++++++++++++++++++++++++----------- 2 files changed, 142 insertions(+), 62 deletions(-) diff --git a/Escritorio_Boxes/diseño.py b/Escritorio_Boxes/diseño.py index 9d97477..c6b3956 100644 --- a/Escritorio_Boxes/diseño.py +++ b/Escritorio_Boxes/diseño.py @@ -3,27 +3,40 @@ import time import math import random -# --- CONFIGURACIÓN --- -UDP_IP = "127.0.0.1" +# --- CONFIGURACIÓN DE RED --- +UDP_IP = "127.0.0.1" # Enviamos a nuestro propio PC (Localhost) UDP_PORT = 4210 sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) -print(f"--- SIMULADOR DE COCHE G26 ---") -print(f"Enviando datos falsos a {UDP_IP}:{UDP_PORT}") +print("--- SIMULADOR G26 INICIADO ---") +print("Formato de envío: ECT | RPM | BATT") +print("Simulando batería bajando de 20V a 0V...") t = 0 +batt = 20.0 # La batería empieza al máximo + while True: - # Generar una temperatura que sube y baja mucho para probar todos los colores - # Oscilará entre 40ºC y 120ºC - ect = 80 + (40 * math.sin(t / 10.0)) + random.uniform(-0.5, 0.5) + # 1. Simular Temperatura (ECT) - Oscila entre 80 y 90 + ect = 85 + (5 * math.sin(t / 5.0)) + random.uniform(-0.5, 0.5) - # Enviar solo el número - mensaje = f"{ect:.2f}" + # 2. Simular RPM - Acelerones entre 0 y 15000 RPM + # Usamos valor absoluto del seno para simular que pisa el acelerador y suelta + rpm = abs(15000 * math.sin(t / 2.0)) - sock.sendto(mensaje.encode(), (UDP_IP, UDP_PORT)) + # 3. Simular Batería - Baja progresivamente + batt -= 0.05 # Restamos un poco de voltaje en cada ciclo + if batt < 0: + batt = 20.0 # Si llega a 0, reiniciamos a 20V para que el test continúe + + # 4. Empaquetar el mensaje (3 datos separados por '|') + mensaje = f"{ect:.1f}|{int(rpm)}|{batt:.1f}" - print(f"Simulando: {mensaje} °C") + # 5. Enviar por UDP + sock.sendto(mensaje.encode('utf-8'), (UDP_IP, UDP_PORT)) + + # Imprimir en consola para confirmar visualmente + print(f"Enviando -> {mensaje}") t += 0.1 - time.sleep(0.05) \ No newline at end of file + time.sleep(0.05) # Espera 50 milisegundos (20 veces por segundo) \ No newline at end of file diff --git a/Escritorio_Boxes/monitor.py b/Escritorio_Boxes/monitor.py index f2ca385..54f1aa4 100644 --- a/Escritorio_Boxes/monitor.py +++ b/Escritorio_Boxes/monitor.py @@ -1,31 +1,36 @@ import socket import matplotlib.pyplot as plt import matplotlib.animation as animation +from matplotlib.gridspec import GridSpec from collections import deque import time +import numpy as np from datetime import datetime # --- CONFIGURACIÓN --- UDP_IP = "0.0.0.0" # Escuchamos en Todas las interfaces posibles (WiFi, Ethernet...) UDP_PORT = 4210 # Mismo puerto que usamos para la ESP32 -MAX_PUNTOS = 200 # Vamos a mostrar en la gráfica los últimos 200 datos TIMEOUT_SEG = 1.5 # Para ver si existe desconexión +# --- CONFIGURACIÓN VISUAL --- +MAX_PUNTOS = 200 # Vamos a mostrar en la gráfica los últimos 200 datos +MAX_RPM = 15000 # Límite máximo del velocímetro +MIN_BATT = 0.0 # Mínimo voltaje para la gráfica +MAX_BATT = 20.0 # Máximo voltaje para la gráfica + # Cola de datos data_ect = deque([0]*MAX_PUNTOS, maxlen=MAX_PUNTOS) - -#Ocultamos estos valores ya que por el momento únicamente vamos a trabajar con la temperatura -#data_rpm = deque([0]*MAX_PUNTOS, maxlen=MAX_PUNTOS) -#data_bat = deque([0]*MAX_PUNTOS, maxlen=MAX_PUNTOS) +# Para controlar si existe una desconexión de envio de paquetes +ultimo_tiempo_dato = time.time() # Configuración del socket UDP sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) sock.bind((UDP_IP, UDP_PORT)) sock.setblocking(False) -# --- DISEÑO GRÁFICO --- +# --- DISEÑO DEL DASHBOARD --- plt.style.use('dark_background') -fig, ax1 = plt.subplots(figsize=(10, 6)) # Usaremos una única ventana grande +fig = plt.figure(figsize=(12, 8)) # Título principal + RELOJ fig.canvas.manager.set_window_title('G26 Telemetry - Monitor de Temperatura') @@ -34,72 +39,134 @@ fig.suptitle('GADES TELEMETRY SYSTEM', fontsize=16, fontweight='bold', color='wh # Reloj en la esquina superior derecha txt_reloj = fig.text(0.85, 0.95, '--:--:--', fontsize=12, color='white', fontweight='bold') -# Configuración de la línea (empieza en blanco pero cambiará) -line_ect, = ax1.plot([], [], color='white', lw=3) -ax1.set_ylabel('Temperatura (°C)', fontsize=14) -ax1.set_ylim(0, 130) # Rango de temperatura (0 a 130 grados) -ax1.grid(True, alpha=0.3, linestyle='--') +# Sistema de Grid (2 filas, 2 columnas) +gs = GridSpec(2, 2, height_ratios=[1, 1]) + +# --- 1. GRÁFICA DE TEMPERATURA --- +ax_ect = fig.add_subplot(gs[0, :]) +line_ect, = ax_ect.plot([], [], color='white', lw=3) +ax_ect.set_ylabel('Temperatura (°C)', fontsize=14) +ax_ect.set_ylim(0, 130) # Rango de temperatura (0 a 130 grados) +ax_ect.grid(True, alpha=0.3, linestyle='--') # Caja de texto con el valor actual props = dict(boxstyle='round', facecolor='black', alpha=0.8, edgecolor='white') -txt_ect = ax1.text(0.02, 0.90, 'ESPERANDO...', transform=ax1.transAxes, +txt_ect = ax_ect.text(0.02, 0.85, 'ESPERANDO...', transform=ax_ect.transAxes, fontsize=16, color='white', fontweight='bold', bbox=props) -#Para controlar si existe una desconexión de envio de paquetes -ultimo_tiempo_dato = time.time() +# --- 2. VELOCÍMETRO RPM (Fila inferior, Izquierda) --- +ax_rpm = fig.add_subplot(gs[1, 0], projection='polar') +ax_rpm.set_thetamin(0) +ax_rpm.set_thetamax(180) +ax_rpm.set_theta_zero_location("W") # El 0 empieza a la izquierda +ax_rpm.set_theta_direction(-1) # Gira en sentido horario +ax_rpm.set_xticklabels([]) # Quitar números del borde +ax_rpm.set_yticklabels([]) +ax_rpm.grid(False) + +# Arco gris de fondo +theta_bg = np.linspace(0, np.pi, 100) +ax_rpm.plot(theta_bg, np.ones_like(theta_bg), color='#444444', lw=8) + +# Arco rojo de límite (12000 a 15000 RPM) +theta_red = np.linspace((12000/MAX_RPM)*np.pi, np.pi, 50) +ax_rpm.plot(theta_red, np.ones_like(theta_red), color='red', lw=8) + +# Aguja y texto RPM +needle, = ax_rpm.plot([0, 0], [0, 0.9], color='white', lw=4) +txt_rpm = ax_rpm.text(0.5, 0.1, 'RPM: --', transform=ax_rpm.transAxes, ha='center', fontsize=20, fontweight='bold') + +# --- 3. BARRA DE BATERÍA (Fila inferior, Derecha) --- +ax_batt = fig.add_subplot(gs[1, 1]) +ax_batt.set_xlim(MIN_BATT, MAX_BATT) +ax_batt.set_ylim(-0.5, 0.5) +ax_batt.set_yticks([]) # Quitar eje Y +ax_batt.set_xlabel('Voltaje (V)', fontsize=12) + +# Contorno de la batería +borde = plt.Rectangle((MIN_BATT, -0.3), MAX_BATT-MIN_BATT, 0.6, fill=False, edgecolor='white', lw=2) +ax_batt.add_patch(borde) + +# Barra interior amarilla/verde/roja +bar_batt = ax_batt.barh(0, 0, left=MIN_BATT, height=0.5, color='green', align='center')[0] +txt_batt = ax_batt.text(0.5, 0.8, 'BATT: -- V', transform=ax_batt.transAxes, ha='center', fontsize=18, fontweight='bold') -def get_color(temp): - if temp > 105: - return '#ff3333' # ROJO (Peligro) - elif temp >= 95: - return '#ffff33' # AMARILLO (Precaución) - elif temp >= 65: - return '#33ff33' # VERDE (Ok) - else: - return '#33ffff' # AZUL (Frío) def update(frame): + global ultimo_tiempo_dato # <--- ¡VITAL! Para poder modificar la variable global + # 1. Actualizar el Reloj con la hora del PC ahora = datetime.now().strftime("%H:%M:%S") txt_reloj.set_text(f"HORA: {ahora}") + try: while True: data, addr = sock.recvfrom(1024) msg = data.decode('utf-8') - # partes = msg.split('|') + partes = msg.split('|') - try: - val_ect = float(msg) # Convertimos texto a número + if len(partes) == 3: + val_ect = float(partes[0]) + val_rpm = float(partes[1]) + val_batt = float(partes[2]) + + ultimo_tiempo_dato = time.time() # Reiniciar Watchdog - data_ect.append(val_ect) # Guardamos el dato - ultimo_tiempo_dato = time.time() # Reiniciamos el contador ya que ha llegado un nuevo dato + # 1. Actualizar ECT + data_ect.append(val_ect) + txt_ect.set_text(f"ECT: {val_ect:.1f} °C") + txt_ect.set_color('cyan') - color_actual = get_color(val_ect) # Para que el valor cambie de color segun la temepratura - txt_ect.set_text(f"TEMP: {val_ect: .1f} °C") - txt_ect.set_color(color_actual) - - except ValueError: - print(f"Error de formato: {msg}") + # 2. Actualizar RPM + val_rpm = max(0, min(MAX_RPM, val_rpm)) + angulo_rad = (val_rpm / MAX_RPM) * np.pi + needle.set_data([angulo_rad, angulo_rad], [0, 0.9]) + txt_rpm.set_text(f"RPM: {int(val_rpm)}") + + if val_rpm > 12000: # Zona roja a partir de 12000 + txt_rpm.set_color('red') + needle.set_color('red') + else: + txt_rpm.set_color('white') + needle.set_color('white') + + # 3. Actualizar Batería (Progresión visual 0V a 20V) + txt_batt.set_text(f"BATT: {val_batt:.1f} V") + ancho = max(0, min(MAX_BATT - MIN_BATT, val_batt - MIN_BATT)) + bar_batt.set_width(ancho) + + # Colores adaptados para coche (Avisos de voltaje bajo) + if val_batt < 11.5: + bar_batt.set_color('red') + elif val_batt < 12.5: + bar_batt.set_color('yellow') + else: + bar_batt.set_color('green') except BlockingIOError: pass except Exception as e: - print(f"Error: {e}") + pass - # Comprobamos si el tiempo actual menos el último registro es mayor a 1.5s + # --- WATCHDOG / TIMEOUT --- if (time.time() - ultimo_tiempo_dato) > TIMEOUT_SEG: - data_ect.append(0) # Forzamos la gráfica a caer a 0 - txt_ect.set_text("COCHE APAGADO") - txt_ect.set_color('#888888') # Color Gris - txt_ect.get_bbox_patch().set_edgecolor('#888888') - line_ect.set_color('#888888') - - # Actualizamos la línea gráfica - x_range = range(len(data_ect)) - line_ect.set_data(x_range, data_ect) - ax1.set_xlim(0, max(len(data_ect), 1)) - - return line_ect, txt_ect, txt_reloj + data_ect.append(0) + txt_ect.set_text("ECT: DESCONECTADO") + txt_ect.set_color('#555555') + txt_rpm.set_text("RPM: 0") + txt_rpm.set_color('#555555') + needle.set_data([0, 0], [0, 0.9]) # Bajar aguja a 0 + needle.set_color('#555555') + txt_batt.set_text("BATT: APAGADO") + bar_batt.set_width(0) -ani = animation.FuncAnimation(fig, update, interval=20, blit=False, cache_frame_data=False) + # Redibujar gráfica ECT + line_ect.set_data(range(len(data_ect)), data_ect) + ax_ect.set_xlim(0, max(len(data_ect), 1)) + + return line_ect, txt_ect, needle, txt_rpm, bar_batt, txt_batt + +# Iniciar animación +ani = animation.FuncAnimation(fig, update, interval=50, blit=False, cache_frame_data=False) +plt.tight_layout() plt.show() \ No newline at end of file