graphing.py 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201
  1. import threading
  2. import pygame
  3. import sys
  4. import dobluetooth
  5. import math
  6. # Initialize Pygame
  7. pygame.init()
  8. pygame.font.init()
  9. from collections import deque
  10. # Rolling history buffers
  11. graph_length = 200
  12. # Gyroscope history
  13. gyro_mag_history = deque(maxlen=graph_length)
  14. gyro_x_history = deque(maxlen=graph_length)
  15. gyro_y_history = deque(maxlen=graph_length)
  16. gyro_z_history = deque(maxlen=graph_length)
  17. # Accelerometer history
  18. acc_x_history = deque(maxlen=graph_length)
  19. acc_y_history = deque(maxlen=graph_length)
  20. acc_z_history = deque(maxlen=graph_length)
  21. # Set up the display
  22. width, height = 1000, 550
  23. screen = pygame.display.set_mode((width, height))
  24. pygame.display.set_caption("MPU BLE Visualizer")
  25. # Fonts
  26. font = pygame.font.SysFont("Arial", 20)
  27. warning_font = pygame.font.SysFont("Arial", 32, bold=True)
  28. # Colors
  29. WHITE = (255, 255, 255)
  30. GRAY = (180, 180, 180)
  31. BLACK = (0, 0, 0)
  32. GYRO_COLORS = [(0, 255, 255), (255, 0, 255), (255, 255, 0)] # rotX, rotY, rotZ
  33. ACCEL_COLORS = [(255, 0, 0), (0, 255, 0), (0, 0, 255)] # accX, accY, accZ
  34. # Circle setup
  35. padding = 50
  36. circle_spacing = (width - 2 * padding) // 6
  37. circle_radius = 40
  38. circle_positions = [(padding + i * circle_spacing, 150) for i in range(6)]
  39. # Axis labels
  40. axis_labels = ['rotX', 'rotY', 'rotZ', 'accX', 'accY', 'accZ']
  41. # Force threshold
  42. HIGH_FORCE_THRESHOLD = 3.00
  43. # Start BLE thread
  44. def ble_thread_func():
  45. import asyncio
  46. loop = asyncio.new_event_loop()
  47. asyncio.set_event_loop(loop)
  48. loop.run_until_complete(dobluetooth.getData())
  49. ble_thread = threading.Thread(target=ble_thread_func, daemon=True)
  50. ble_thread.start()
  51. def draw_graph(surface, history, color, y_base, height, height_scale=1.0):
  52. if len(history) < 2:
  53. return
  54. points = []
  55. for i, val in enumerate(history):
  56. x = i + 50
  57. y = y_base + height // 2 - val * height_scale
  58. y = max(y_base, min(y_base + height, y)) # Clamp inside graph area
  59. points.append((x, y))
  60. pygame.draw.lines(surface, color, False, points, 2)
  61. # Main loop
  62. running = True
  63. clock = pygame.time.Clock()
  64. while running:
  65. screen.fill(WHITE)
  66. for event in pygame.event.get():
  67. if event.type == pygame.QUIT:
  68. running = False
  69. try:
  70. data = dobluetooth.getDataArr()
  71. high_force : bool = False
  72. if len(data) >= 6:
  73. # Accelerometer values
  74. accX, accY, accZ = data[3:6]
  75. acc_x_history.append(accX)
  76. acc_y_history.append(accY)
  77. acc_z_history.append(accZ)
  78. acc_magnitude = math.sqrt(accX**2 + accY**2 + accZ**2)
  79. if acc_magnitude > HIGH_FORCE_THRESHOLD:
  80. high_force = True
  81. rotX, rotY, rotZ = data[0:3]
  82. gyro_magnitude = math.sqrt(rotX**2 + rotY**2 + rotZ**2)
  83. if gyro_magnitude < 10:
  84. gyro_condition = "🟢 Stable"
  85. gyro_color = (0, 200, 0)
  86. elif gyro_magnitude < 75:
  87. gyro_condition = "🟡 Moving"
  88. gyro_color = (255, 200, 0)
  89. else:
  90. gyro_condition = "🔴 High Rotation"
  91. gyro_color = (255, 50, 50)
  92. # Create the font surface
  93. gyro_status_surface = warning_font.render(f"Gyro Status: {gyro_condition}", True, gyro_color)
  94. # Position it — move it higher up on the screen
  95. gyro_status_rect = gyro_status_surface.get_rect(center=(width // 2, 250))
  96. # Blit it to the screen
  97. screen.blit(gyro_status_surface, gyro_status_rect)
  98. # Add to history
  99. gyro_mag_history.append(gyro_magnitude)
  100. gyro_x_history.append(rotX)
  101. gyro_y_history.append(rotY)
  102. gyro_z_history.append(rotZ)
  103. for i in range(6):
  104. val = abs(data[i])
  105. if i < 3: # Gyroscope
  106. scale = min(val / 5.0, 1.0)
  107. base_color = GYRO_COLORS[i]
  108. else: # Accelerometer
  109. scale = min(val / 5.0, 1.0) # ✅ Fixed scale
  110. base_color = ACCEL_COLORS[i - 3]
  111. color = [int(c * scale) for c in base_color]
  112. pygame.draw.circle(screen, color, circle_positions[i], circle_radius)
  113. # Axis label
  114. label_surface = font.render(axis_labels[i], True, BLACK)
  115. label_rect = label_surface.get_rect(center=(circle_positions[i][0], circle_positions[i][1] + circle_radius + 10))
  116. screen.blit(label_surface, label_rect)
  117. # Value label
  118. val_surface = font.render(f"{val:.2f}", True, BLACK)
  119. val_rect = val_surface.get_rect(center=(circle_positions[i][0], circle_positions[i][1] - circle_radius - 10))
  120. screen.blit(val_surface, val_rect)
  121. else:
  122. for pos in circle_positions:
  123. pygame.draw.circle(screen, GRAY, pos, circle_radius)
  124. except Exception as e:
  125. print("Error parsing BLE data:", e)
  126. # Display "High Force" warning
  127. if high_force:
  128. warning_text = warning_font.render("⚠ High Force Detected!", True, (255, 0, 0))
  129. warning_rect = warning_text.get_rect(center=(width // 2, 50))
  130. screen.blit(warning_text, warning_rect)
  131. # === GRAPH VISUALIZATION ===
  132. # Where to place the graphs
  133. graph_area_top = 300# Leave top area for circles/status
  134. graph_spacing = 120 # Vertical space per graph
  135. # Graph heights
  136. graph_height = 80
  137. # Top Y positions for gyro and accel graphs
  138. gyro_top = graph_area_top
  139. accel_top = gyro_top + graph_spacing
  140. # Background
  141. pygame.draw.rect(screen, (245, 245, 245), (0, graph_area_top, width, 220))
  142. # Labels
  143. screen.blit(font.render("Gyro X/Y/Z (°/s)", True, (0, 0, 0)), (10, gyro_top - 15))
  144. screen.blit(font.render("Accel X/Y/Z (g)", True, (0, 0, 0)), (10, accel_top - 15))
  145. # Gyroscope graph
  146. draw_graph(screen, gyro_x_history, (255, 0, 0), gyro_top, graph_height, height_scale=10)
  147. draw_graph(screen, gyro_y_history, (0, 255, 0), gyro_top, graph_height, height_scale=10)
  148. draw_graph(screen, gyro_z_history, (0, 0, 255), gyro_top, graph_height, height_scale=10)
  149. # Accelerometer graph
  150. draw_graph(screen, acc_x_history, (255, 128, 0), accel_top, graph_height, height_scale=25)
  151. draw_graph(screen, acc_y_history, (0, 200, 200), accel_top, graph_height, height_scale=25)
  152. draw_graph(screen, acc_z_history, (128, 0, 255), accel_top, graph_height, height_scale=25)
  153. pygame.display.flip()
  154. clock.tick(60)
  155. pygame.quit()
  156. sys.exit()