graphing.py 7.6 KB

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