Prueba inicial rpm y bateria añadida

This commit is contained in:
adrigongv23 2026-02-25 12:09:08 +01:00
parent aacfd2f6c3
commit b60f47cfb6
2 changed files with 142 additions and 62 deletions

View file

@ -3,27 +3,40 @@ import time
import math import math
import random import random
# --- CONFIGURACIÓN --- # --- CONFIGURACIÓN DE RED ---
UDP_IP = "127.0.0.1" UDP_IP = "127.0.0.1" # Enviamos a nuestro propio PC (Localhost)
UDP_PORT = 4210 UDP_PORT = 4210
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
print(f"--- SIMULADOR DE COCHE G26 ---") print("--- SIMULADOR G26 INICIADO ---")
print(f"Enviando datos falsos a {UDP_IP}:{UDP_PORT}") print("Formato de envío: ECT | RPM | BATT")
print("Simulando batería bajando de 20V a 0V...")
t = 0 t = 0
batt = 20.0 # La batería empieza al máximo
while True: while True:
# Generar una temperatura que sube y baja mucho para probar todos los colores # 1. Simular Temperatura (ECT) - Oscila entre 80 y 90
# Oscilará entre 40ºC y 120ºC ect = 85 + (5 * math.sin(t / 5.0)) + random.uniform(-0.5, 0.5)
ect = 80 + (40 * math.sin(t / 10.0)) + random.uniform(-0.5, 0.5)
# Enviar solo el número # 2. Simular RPM - Acelerones entre 0 y 15000 RPM
mensaje = f"{ect:.2f}" # 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
print(f"Simulando: {mensaje} °C") # 4. Empaquetar el mensaje (3 datos separados por '|')
mensaje = f"{ect:.1f}|{int(rpm)}|{batt:.1f}"
# 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 t += 0.1
time.sleep(0.05) time.sleep(0.05) # Espera 50 milisegundos (20 veces por segundo)

View file

@ -1,31 +1,36 @@
import socket import socket
import matplotlib.pyplot as plt import matplotlib.pyplot as plt
import matplotlib.animation as animation import matplotlib.animation as animation
from matplotlib.gridspec import GridSpec
from collections import deque from collections import deque
import time import time
import numpy as np
from datetime import datetime from datetime import datetime
# --- CONFIGURACIÓN --- # --- CONFIGURACIÓN ---
UDP_IP = "0.0.0.0" # Escuchamos en Todas las interfaces posibles (WiFi, Ethernet...) UDP_IP = "0.0.0.0" # Escuchamos en Todas las interfaces posibles (WiFi, Ethernet...)
UDP_PORT = 4210 # Mismo puerto que usamos para la ESP32 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 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 # Cola de datos
data_ect = deque([0]*MAX_PUNTOS, maxlen=MAX_PUNTOS) data_ect = deque([0]*MAX_PUNTOS, maxlen=MAX_PUNTOS)
# Para controlar si existe una desconexión de envio de paquetes
#Ocultamos estos valores ya que por el momento únicamente vamos a trabajar con la temperatura ultimo_tiempo_dato = time.time()
#data_rpm = deque([0]*MAX_PUNTOS, maxlen=MAX_PUNTOS)
#data_bat = deque([0]*MAX_PUNTOS, maxlen=MAX_PUNTOS)
# Configuración del socket UDP # Configuración del socket UDP
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.bind((UDP_IP, UDP_PORT)) sock.bind((UDP_IP, UDP_PORT))
sock.setblocking(False) sock.setblocking(False)
# --- DISEÑO GRÁFICO --- # --- DISEÑO DEL DASHBOARD ---
plt.style.use('dark_background') 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 # Título principal + RELOJ
fig.canvas.manager.set_window_title('G26 Telemetry - Monitor de Temperatura') 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 # Reloj en la esquina superior derecha
txt_reloj = fig.text(0.85, 0.95, '--:--:--', fontsize=12, color='white', fontweight='bold') 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á) # Sistema de Grid (2 filas, 2 columnas)
line_ect, = ax1.plot([], [], color='white', lw=3) gs = GridSpec(2, 2, height_ratios=[1, 1])
ax1.set_ylabel('Temperatura (°C)', fontsize=14)
ax1.set_ylim(0, 130) # Rango de temperatura (0 a 130 grados) # --- 1. GRÁFICA DE TEMPERATURA ---
ax1.grid(True, alpha=0.3, linestyle='--') 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 # Caja de texto con el valor actual
props = dict(boxstyle='round', facecolor='black', alpha=0.8, edgecolor='white') 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) fontsize=16, color='white', fontweight='bold', bbox=props)
#Para controlar si existe una desconexión de envio de paquetes # --- 2. VELOCÍMETRO RPM (Fila inferior, Izquierda) ---
ultimo_tiempo_dato = time.time() 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): def update(frame):
global ultimo_tiempo_dato # <--- ¡VITAL! Para poder modificar la variable global
# 1. Actualizar el Reloj con la hora del PC # 1. Actualizar el Reloj con la hora del PC
ahora = datetime.now().strftime("%H:%M:%S") ahora = datetime.now().strftime("%H:%M:%S")
txt_reloj.set_text(f"HORA: {ahora}") txt_reloj.set_text(f"HORA: {ahora}")
try: try:
while True: while True:
data, addr = sock.recvfrom(1024) data, addr = sock.recvfrom(1024)
msg = data.decode('utf-8') msg = data.decode('utf-8')
# partes = msg.split('|') partes = msg.split('|')
try: if len(partes) == 3:
val_ect = float(msg) # Convertimos texto a número val_ect = float(partes[0])
val_rpm = float(partes[1])
val_batt = float(partes[2])
data_ect.append(val_ect) # Guardamos el dato ultimo_tiempo_dato = time.time() # Reiniciar Watchdog
ultimo_tiempo_dato = time.time() # Reiniciamos el contador ya que ha llegado un nuevo dato
color_actual = get_color(val_ect) # Para que el valor cambie de color segun la temepratura # 1. Actualizar ECT
txt_ect.set_text(f"TEMP: {val_ect: .1f} °C") data_ect.append(val_ect)
txt_ect.set_color(color_actual) txt_ect.set_text(f"ECT: {val_ect:.1f} °C")
txt_ect.set_color('cyan')
except ValueError: # 2. Actualizar RPM
print(f"Error de formato: {msg}") 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: except BlockingIOError:
pass pass
except Exception as e: 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: if (time.time() - ultimo_tiempo_dato) > TIMEOUT_SEG:
data_ect.append(0) # Forzamos la gráfica a caer a 0 data_ect.append(0)
txt_ect.set_text("COCHE APAGADO") txt_ect.set_text("ECT: DESCONECTADO")
txt_ect.set_color('#888888') # Color Gris txt_ect.set_color('#555555')
txt_ect.get_bbox_patch().set_edgecolor('#888888') txt_rpm.set_text("RPM: 0")
line_ect.set_color('#888888') 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)
# Actualizamos la línea gráfica # Redibujar gráfica ECT
x_range = range(len(data_ect)) line_ect.set_data(range(len(data_ect)), data_ect)
line_ect.set_data(x_range, data_ect) ax_ect.set_xlim(0, max(len(data_ect), 1))
ax1.set_xlim(0, max(len(data_ect), 1))
return line_ect, txt_ect, txt_reloj return line_ect, txt_ect, needle, txt_rpm, bar_batt, txt_batt
ani = animation.FuncAnimation(fig, update, interval=20, blit=False, cache_frame_data=False) # Iniciar animación
ani = animation.FuncAnimation(fig, update, interval=50, blit=False, cache_frame_data=False)
plt.tight_layout()
plt.show() plt.show()