-
1Wiring
General
- Power Pins: All components must connect to power pins (3VO, 5V) & ground pins (GND).
- ADC Pins: Reads analog outputs from joystick potentiometers (x-axis and y-axis).
- I2C Pins: Sends and receives data with two lines, a serial clock pin (SCL) and a serial data pin (SDA).
- PWM Pins: Generates a pulsed signal to control the servo position by varying the duty cycle.
- Push-Button Pin: Pressing the joystick down closes and opens the switch, changing the output between high and low.
Raspberry Pi Pico
- Power Pins: 3VO, GND
- ADC Pins: GPIO26 (to VRx), GPIO27 (to VRy)
- I2C Pins: GPIO4 (to SCL), GPIO5 (to SDA)
- PWM Pins: GPIO16 (to Servo X), GPIO17 (to Servo Y), GPIO18 (to Servo Z)
- Push-Button Pin: GPIO13 (to SW)
Credits: Raspberry Pi Microcontrollers
IMU BNO085
- Power Pins: 3VO, GND
- I2C Pins: SDA (to GPIO4), SCL (to GPIO5)
Joystick Module
- Power Pins: 5V, GND
- ADC Pins: VRx (to GPIO26), VRy (to GPIO27)
- Push-Button Pin: SW (to GPIO13)
Servo Motors
- Servos have 3 wires: Brown (Ground), Red (Power), Yellow (PWM)
- PWM: Servo X (to GPIO16), Servo Y (to GPIO17), Servo Z (to GPIO18)
- We recommend using wire connectors to link the power and ground wires of the servos to the Pi Pico and an external power supply, like a wall outlet.
-
2CircuitPython Code
import time import board import busio import pwmio import analogio import digitalio from adafruit_motor import servo from adafruit_bno08x import BNO_REPORT_GYROSCOPE from adafruit_bno08x import BNO_REPORT_ACCELEROMETER from adafruit_bno08x.i2c import BNO08X_I2C # Initialize I2C i2c = busio.I2C(board.GP5, board.GP4, frequency=400000) bno = BNO08X_I2C(i2c) # Enable Gyroscope bno.enable_feature(BNO_REPORT_GYROSCOPE) # Enable Accelerometer bno.enable_feature(BNO_REPORT_ACCELEROMETER) # Read Accelerometer Data accel_x, accel_y, accel_z = bno.accelerometer # Initialize Joystick x_axis = analogio.AnalogIn(board.GP26) y_axis = analogio.AnalogIn(board.GP27) # Initialize Joystick Push-Button button = digitalio.DigitalInOut(board.GP13) button.switch_to_input(pull=digitalio.Pull.UP) # Initialize PWM pwm_x = pwmio.PWMOut(board.GP16, duty_cycle=0, frequency=50) pwm_y = pwmio.PWMOut(board.GP17, duty_cycle=0, frequency=50) pwm_z = pwmio.PWMOut(board.GP18, duty_cycle=0, frequency=50) servo_x = servo.Servo(pwm_x) servo_y = servo.Servo(pwm_y) servo_z = servo.Servo(pwm_z) # Initial Servo Angles servo_angle_x = 90 servo_angle_y = 90 servo_angle_z = 90 # Scaling Factors gyro_scale_factor = 10 joystick_dead_zone = 5 joystick_active = False # Lock Servo Angles locked_angle_x = servo_angle_x locked_angle_y = servo_angle_y locked_angle_z = servo_angle_z # Track the last state of the button press for toggling last_button_state = False button_pressed = False # Function to map joystick input (0-65535) to servo angle (0-180) def map_range(value, in_min=0, in_max=65535, out_min=0, out_max=180): return (value - in_min) * (out_max - out_min) // (in_max - in_min) + out_min while True: time.sleep(0.1) # Read Joystick Values joy_x = map_range(x_axis.value, 0, 65535, -100, 100) joy_y = map_range(y_axis.value, 0, 65535, -100, 100) button_state = not button.value # Button is Pressed if button_state != last_button_state: if button_state: joystick_active = not joystick_active if joystick_active: print("Joystick is now active!") else: print("Joystick is now inactive, position locked.") last_button_state = button_state # When Joystick is Active if joystick_active: # Joystick controls servos servo_angle_x = map_range(x_axis.value) servo_angle_y = map_range(y_axis.value) locked_angle_x = servo_angle_x locked_angle_y = servo_angle_y else: # When Joystick is Inactive gyro_x, gyro_y, gyro_z = bno.gyro locked_angle_x += gyro_x * gyro_scale_factor locked_angle_y += gyro_y * gyro_scale_factor locked_angle_z += gyro_z * gyro_scale_factor locked_angle_x = max(0, min(180, locked_angle_x)) locked_angle_y = max(0, min(180, locked_angle_y)) locked_angle_z = max(0, min(180, locked_angle_z)) # Set Servo Angle (Based on Locked Angles) servo_x.angle = locked_angle_x servo_y.angle = locked_angle_y servo_z.angle = locked_angle_z mode = "Joystick" if joystick_active else "IMU" print(f"Mode: {mode}") print(f"X: {locked_angle_x}, Y: {locked_angle_y}, Z: {locked_angle_z}") print("-" * 50)
-
3CAD Design
STL Files for 3D Printed Designs
- Piece 1. Houses breadboard circuit and serves as base for Servo Y.
- Piece 2. Serves as base for Servo Z
- Piece 3. Serves as base for Servo X and supports platform for camera system.
Direction of Axes
- Servo X corresponds to X-axis (tilting forward/backward).
- Servo Y corresponds to Y-axis (tilting left/right)
- Servo Z corresponds to Z-axis (rotating left or right)
- Pushing joystick up/down corresponds to tilting forward/backward on X-axis.
- Pushing joystick left/right corresponds to tilting left/right on Y-axis.
During assembly, ensure each servo is connected to the correct axis by verifying that the GPIO pins correspond to the appropriate PWM pins. As shown in the image above, each servo corresponds to a specific plane and is responsible for controlling a particular axis (X, Y, or Z).
- ZY Plane (X-axis data): Held in the side of the gimbal, like "pitch" in an airplane.
- XZ Plane (Y-axis data): Held in the base of the gimbal, like "yaw" in an airplane.
- XY Plane (Z-axis data): Held in the back of the gimbal, like "roll" in an airplane.
Credits: Wikipedia
Servos can be attached using bolts, nuts, and washers. While servos come with mounting parts for easy attachment, additional holes must be drilled for a secure fit in this model. A key challenge we faced was the shallow servo mounts, causing the servos to wedge out over time. Our initial attempt to secure them with hot glue provided only a temporary fix, as the glue lacked strength and made reattachment more difficult. The final solution was to use screws to secure the servos to the axes at the center of the mount, ensuring a stable and lasting fit.
Discussions
Become a Hackaday.io Member
Create an account to leave a comment. Already have an account? Log In.