aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/pico/joystick_to_cursor.py177
1 files changed, 177 insertions, 0 deletions
diff --git a/src/pico/joystick_to_cursor.py b/src/pico/joystick_to_cursor.py
new file mode 100644
index 0000000..361af68
--- /dev/null
+++ b/src/pico/joystick_to_cursor.py
@@ -0,0 +1,177 @@
+import pygame
+import time
+from pynput.mouse import Controller, Button
+
+pygame.init()
+pygame.joystick.init()
+
+# Screen mapping size based on mouse position (my screen ins 2560 x 1600)
+SCREEN_WIDTH = 1680
+SCREEN_HEIGHT = 1050
+
+# Check for connected joystick
+if pygame.joystick.get_count() == 0:
+ print("No joystick detected. Please connect a game controller.")
+ exit()
+
+joystick = pygame.joystick.Joystick(0)
+joystick.init()
+mouse = Controller()
+
+# Get controller information
+button_count = joystick.get_numbuttons()
+print(f"Joystick detected: {joystick.get_name()} with {button_count} buttons")
+
+# Initial calibration phase
+print("Calibrating joystick. Please don't touch the controller...")
+time.sleep(1) # Wait for 1 second to make sure joystick is at rest
+
+# Get initial position as offset
+pygame.event.pump()
+x_offset = joystick.get_axis(0)
+y_offset = joystick.get_axis(1)
+print(f"Center calibration complete. Offset values: X={x_offset}, Y={y_offset}")
+
+# Full calibration phase to get min/max values
+print("\n--- FULL CALIBRATION ---")
+print("Please move the joystick to its maximum extents in all directions.")
+print("Move in circles covering the entire range for 5 seconds...")
+
+# Initialize min/max values
+x_min, x_max = 0, 0
+y_min, y_max = 0, 0
+
+# Calibration duration
+cal_start_time = time.time()
+cal_duration = 5 # seconds
+
+while time.time() - cal_start_time < cal_duration:
+ pygame.event.pump()
+
+ # Get calibrated values
+ x_val = joystick.get_axis(0) - x_offset
+ y_val = joystick.get_axis(1) - y_offset
+
+ # Update min/max
+ x_min = min(x_min, x_val)
+ x_max = max(x_max, x_val)
+ y_min = min(y_min, y_val)
+ y_max = max(y_max, y_val)
+
+ # Show progress
+ progress = int((time.time() - cal_start_time) / cal_duration * 20)
+ print(f"\rCalibrating: [{'#' * progress}{' ' * (20-progress)}] Current ranges: X({x_min:.2f} to {x_max:.2f}) Y({y_min:.2f} to {y_max:.2f})", end="")
+ time.sleep(0.1)
+
+print("\nCalibration complete!")
+print(f"X range: {x_min:.2f} to {x_max:.2f}")
+print(f"Y range: {y_min:.2f} to {y_max:.2f}")
+
+# Dead zone
+DEAD_ZONE = 0.05 # Ignore small movements (5% of range)
+SMOOTHING_FACTOR = 0.85 # Higher values (closer to 1) make movement smoother but less responsive
+
+# Function to map joystick value to screen coordinates
+def map_to_screen(value, in_min, in_max, out_min, out_max):
+ # Clamp input value to range
+ value = max(min(value, in_max), in_min)
+ # Map input range to output range
+ return (value - in_min) * (out_max - out_min) / (in_max - in_min) + out_min
+
+running = True
+# Store the last mouse position to calculate deltas
+last_screen_x, last_screen_y = SCREEN_WIDTH/2, SCREEN_HEIGHT/2
+
+# Variables for smoothing
+smooth_x = 0
+smooth_y = 0
+
+# Set initial mouse position to center of screen
+mouse.position = (int(last_screen_x), int(last_screen_y))
+
+# Dictionary to track button states to detect changes
+button_states = {i: False for i in range(button_count)}
+
+# Define controller buttons to mouse mapping
+RIGHT_CLICK_BUTTON = 4
+LEFT_CLICK_BUTTON = 5
+
+print(f"\nButton mapping:\n- Button {LEFT_CLICK_BUTTON}: Left mouse click\n- Button {RIGHT_CLICK_BUTTON}: Right mouse click")
+print(f"Smoothing factor: {SMOOTHING_FACTOR} (higher = smoother but less responsive)")
+
+while running:
+ pygame.event.pump()
+
+ # Process events to ensure button presses are detected
+ for event in pygame.event.get():
+ if event.type == pygame.QUIT:
+ running = False
+
+ # Read joystick axes and apply calibration
+ x_axis = joystick.get_axis(0) - x_offset
+ y_axis = joystick.get_axis(1) - y_offset
+
+ # Apply dead zone
+ if abs(x_axis) < DEAD_ZONE:
+ x_axis = 0
+ if abs(y_axis) < DEAD_ZONE:
+ y_axis = 0
+
+ # Apply smoothing with exponential moving average
+ smooth_x = SMOOTHING_FACTOR * smooth_x + (1 - SMOOTHING_FACTOR) * x_axis
+ smooth_y = SMOOTHING_FACTOR * smooth_y + (1 - SMOOTHING_FACTOR) * y_axis
+
+ # Map smoothed joystick to screen coordinates
+ screen_x = map_to_screen(smooth_x, x_min, x_max, 0, SCREEN_WIDTH)
+ screen_y = map_to_screen(smooth_y, y_min, y_max, 0, SCREEN_HEIGHT)
+
+ # Use absolute positioning instead of relative movement
+ if abs(x_axis) >= DEAD_ZONE or abs(y_axis) >= DEAD_ZONE:
+ # Set the mouse position directly to the calculated screen coordinates
+ mouse.position = (int(screen_x), int(screen_y))
+
+ # Check all button states
+ button_changes = False
+ button_status = []
+
+ for i in range(button_count):
+ button_value = joystick.get_button(i)
+
+ # Handle mouse button clicks
+ if i == LEFT_CLICK_BUTTON:
+ if button_value and not button_states[i]:
+ mouse.press(Button.left)
+ button_status.append(f"Button {i}: LEFT CLICK")
+ elif not button_value and button_states[i]:
+ mouse.release(Button.left)
+
+ elif i == RIGHT_CLICK_BUTTON:
+ if button_value and not button_states[i]:
+ mouse.press(Button.right)
+ button_status.append(f"Button {i}: RIGHT CLICK")
+ elif not button_value and button_states[i]:
+ mouse.release(Button.right)
+
+ # Track button state changes for all buttons
+ if button_value != button_states[i]:
+ button_changes = True
+ button_states[i] = button_value
+
+ # Add status for non-mouse buttons
+ if button_value and i != LEFT_CLICK_BUTTON and i != RIGHT_CLICK_BUTTON:
+ button_status.append(f"Button {i}: PRESSED")
+
+ # Only display button information if there are changes or buttons pressed
+ if button_changes or any(button_states.values()):
+ print("\nButton Status:")
+ if not button_status:
+ print("No buttons currently pressed")
+ else:
+ for status in button_status:
+ print(status)
+
+ # Display the scaled joystick position
+ print(f"Scaled position: ({int(screen_x)}, {int(screen_y)}) | Raw: ({x_axis:.2f}, {y_axis:.2f}) | Smoothed: ({smooth_x:.2f}, {smooth_y:.2f})")
+ time.sleep(0.05)
+
+pygame.quit()