Quantcast
Viewing all articles
Browse latest Browse all 5398

Python • Struggling with GPIOZero for Motor Control Using TB6600 Microstep Driver

Hello everyone,

I'm currently working on a project where I’m trying to control a motor using the TB6600 microstep driver and a Raspberry Pi. I’ve been reading through the GPIOZero documentation and trying to implement motor control with it, but I'm hitting a wall when it comes to controlling the TB6600, which requires three pins:

1.Direction Pin (DIR) – Determines the rotation direction of the motor (CW/CCW).
2.Pulse Pin (PUL) – Sends a pulse to the driver to make the motor step.
3.Enable Pin (ENA) – Enables or disables the motor.

Here’s what I’m trying to achieve:

• I want to control the motor using GPIOZero, specifically setting the direction, sending pulses, and enabling the motor with three separate GPIO pins.
• I would like to make the motor rotate in both clockwise (CW) and counter-clockwise (CCW) directions using a simple button press, controlling the DIR pin to switch directions.
• The PUL pin should send a pulse every time I want the motor to take a step (this could be triggered by a button or other events in the program).
• The ENA pin should be used to enable and disable the motor as needed, turning the motor on and off with a separate button.
• I’m using the gpiozero library to interact with the GPIO pins, but I'm not sure how to set up the DIR, PUL, and ENA pins properly with gpiozero.
• I’m looking for a clear, working example or a guide on how to configure these pins to make the TB6600 work with the Raspberry Pi.

My Setup:
Raspberry Pi (version 5)
TB6600 Microstep Driver
Nema 17 and Nema 16
gpiozero library

What I've Tried:
I created a simple Tkinter GUI to control the motors, where I use gpiozero for the DIR, PUL, and ENA pins (using the Motor and OutputDevice classes) to control the motor connected to the TB6600 microstep driver. Below is the Python code I tried:

Code:

import tkinter as tkfrom gpiozero import OutputDevice, Motorfrom time import sleep# Initialize the Direction, Pulse, and Enable pinsDIR = OutputDevice(17)  # Direction pinPUL = OutputDevice(18)  # Pulse pinENA = OutputDevice(27)  # Enable pin# Initialize motorsmotor_left = Motor(5, 6)  # Left motor (example)motor_right = Motor(12, 16)  # Right motor (example)# Create the main window using Tkinterroot = tk.Tk()root.title("Motor Control")# Function to set direction and pulse for forward movementdef move_forward():    DIR.value = True  # Set direction to clockwise    ENA.on()  # Enable motor    for _ in range(100):  # Example: send 100 pulses        PUL.on()        sleep(0.001)  # Pulse duration (adjust as needed)        PUL.off()# Function to set direction and pulse for backward movementdef move_backward():    DIR.value = False  # Set direction to counter-clockwise    ENA.on()  # Enable motor    for _ in range(100):  # Example: send 100 pulses        PUL.on()        sleep(0.001)  # Pulse duration (adjust as needed)        PUL.off()# Function to turn left (example - no pulse, just turning motors)def move_left():    ENA.on()  # Enable motor    motor_left.forward()# Function to turn right (example - no pulse, just turning motors)def move_right():    ENA.on()  # Enable motor    motor_right.forward()# Function to stop the robotdef stop_movement():    ENA.off()  # Disable motor to stop movement    motor_left.stop()    motor_right.stop()# Create buttons on the Tkinter windowforward_button = tk.Button(root, text="Forward", width=20, height=2, command=move_forward)forward_button.grid(row=0, column=1)backward_button = tk.Button(root, text="Backward", width=20, height=2, command=move_backward)backward_button.grid(row=2, column=1)left_button = tk.Button(root, text="Left", width=20, height=2, command=move_left)left_button.grid(row=1, column=0)right_button = tk.Button(root, text="Right", width=20, height=2, command=move_right)right_button.grid(row=1, column=2)stop_button = tk.Button(root, text="Stop", width=20, height=2, command=stop_movement)stop_button.grid(row=1, column=1)# Run the Tkinter event looproot.mainloop()
However, I’m not sure if this approach is correct and if I should be using gpiozero's Motor object for any of the pins (since it’s more commonly used with motors and not microstep drivers).

Specific Issues I Need Help With:
• How to control the Pulse Pin (PUL): I’m not sure how to send pulses correctly to make the motor take steps.
• Motor Direction: How can I properly switch the direction between clockwise and counter-clockwise using the DIR pin?
• Enable Pin (ENA): How do I properly enable and disable the motor using the ENA pin, ensuring that the motor only moves when enabled?
• Timing and Pulse Frequency: What should be the correct timing (delay between pulses) to make the motor run smoothly without skipping steps?

If anyone has experience with gpiozero and TB6600, I’d greatly appreciate any help or advice on how to get this setup working!

I'm trying to migrate from RPi.GPIO to gpiozero because my Raspberry pi 4 got bricked so I purchase Raspi 5

Here's the old code:

Code:

#!/usr/bin/python3import tkinter as tkfrom time import sleepimport subprocessimport threadingimport RPi.GPIO as GPIOimport os# Setup GPIO pins for Motor 1 and Motor 2direction_pin_1 = 5pulse_pin_1 = 6en_pin_1 = 26  # Enable pin for Motor 1cw_direction_1 = 0ccw_direction_1 = 1direction_pin_2 = 12pulse_pin_2 = 16en_pin_2 = 19  # Enable pin for Motor 2ccw_direction_2 = 1cw_direction_2 = 0# Buttons setupbutton1 = 21  # Mid Leftbutton2 = 8  # Mid Rightbutton3 = 7   # Top Frontbutton4 = 1   # Top Back# Step sequences for each motorSteps_Motor_1 = [0,4000,3700,3700,3700,3800,3800,3800]Steps_Motor_2 = [3300,2200,2300,2250,2300,2200,2200,3400,2200,2300,2250,2300,2200,2200]#Step Motor LogsMotor_Step_1 = 0Motor_Step_2 = 0detected_labels = ""Slot_names= [["slot_1","slot_2","slot_3","slot_4","slot_5","slot_6","slot_7","slot_57","slot_58","slot_59","slot_60","slot_61","slot_62","slot_63"],            ["slot_8","slot_9","slot_10","slot_11","slot_12","slot_13","slot_14","slot_64","slot_65","slot_66","slot_67","slot_68","slot_69","slot_70"],            ["slot_15","slot_16","slot_17","slot_18","slot_19","slot_20","slot_21","slot_71","slot_72","slot_73","slot_74","slot_75","slot_76","slot_77"],            ["slot_22","slot_23","slot_24","slot_25","slot_26","slot_27","slot_28","slot_78","slot_79","slot_80","slot_81","slot_82","slot_83","slot_84"],            ["slot_29","slot_30","slot_31","slot_32","slot_33","slot_34","slot_35","slot_85","slot_86","slot_87","slot_88","slot_89","slot_90","slot_91"],            ["slot_36","slot_37","slot_38","slot_39","slot_40","slot_41","slot_42","slot_92","slot_93","slot_94","slot_95","slot_96","slot_97","slot_98"],            ["slot_43","slot_44","slot_45","slot_46","slot_47","slot_48","slot_49","slot_99","slot_100","slot_101","slot_102","slot_103","slot_104","slot_105"],            ["slot_50","slot_51","slot_52","slot_53","slot_54","slot_55","slot_56","slot_106","slot_107","slot_108","slot_109","slot_110","slot_111","slot_112"]]# Set up GPIO modeGPIO.setmode(GPIO.BCM)GPIO.setup(direction_pin_1, GPIO.OUT)GPIO.setup(pulse_pin_1, GPIO.OUT)GPIO.setup(en_pin_1, GPIO.OUT)  # Set up enable pin for Motor 1GPIO.setup(direction_pin_2, GPIO.OUT)GPIO.setup(pulse_pin_2, GPIO.OUT)GPIO.setup(en_pin_2, GPIO.OUT)  # Set up enable pin for Motor 2GPIO.setup(button1, GPIO.IN, pull_up_down=GPIO.PUD_UP)GPIO.setup(button2, GPIO.IN, pull_up_down=GPIO.PUD_UP)GPIO.setup(button3, GPIO.IN, pull_up_down=GPIO.PUD_UP)GPIO.setup(button4, GPIO.IN, pull_up_down=GPIO.PUD_UP)# Disable motors initiallyGPIO.output(en_pin_1, GPIO.LOW)GPIO.output(en_pin_2, GPIO.LOW)# Function to spin the motor for a specified number of steps and directiondef spin_motor(direction_pin, pulse_pin, enable_pin, direction, steps):    GPIO.output(enable_pin, GPIO.LOW)  # Enable motor    GPIO.output(direction_pin, cw_direction_1 if direction == "CW" else ccw_direction_1)    for _ in range(steps):        GPIO.output(pulse_pin, GPIO.HIGH)        sleep(0.0001)        GPIO.output(pulse_pin, GPIO.LOW)        sleep(0.0005)    GPIO.output(enable_pin, GPIO.LOW)  # Disable motor after spin# Function to handle motor 1 continuous spinning until a button is presseddef continuous_spin_motor_1(direction):    global Motor_Step_1    print(Motor_Step_1)    GPIO.output(en_pin_1, GPIO.HIGH)  # Enable Motor 1    while GPIO.input(button3) == GPIO.HIGH if direction == "CW" else GPIO.input(button4) == GPIO.HIGH:        spin_motor(direction_pin_1, pulse_pin_1, en_pin_1, direction, 100)  # 450 steps for each spin        sleep(0.00005)    GPIO.output(en_pin_1, GPIO.LOW)  # Disable Motor 1    if direction == "CW":        Motor_Step_1 = 8    else:        Motor_Step_1 = 0    print(Motor_Step_1)# Function to handle motor 2 continuous spinning until a button is presseddef continuous_spin_motor_2(direction):    global Motor_Step_2    print(Motor_Step_2)    if direction == "CCW":        Motor_Step_2 = 15    else:        Motor_Step_2 = 0    print(Motor_Step_2)    GPIO.output(en_pin_2, GPIO.HIGH)  # Enable Motor 2 #motor 2 will move at 500 steps    while GPIO.input(button2) == GPIO.HIGH if direction == "CCW" else GPIO.input(button1) == GPIO.HIGH:        spin_motor(direction_pin_2, pulse_pin_2, en_pin_2, direction, 100)  # 450 steps for each spin        sleep(0.00005)    GPIO.output(en_pin_2, GPIO.LOW)  # Disable Motor 2# Function to start Motor 1 sequence with steps in Steps_Motor_1 arraydef start_motor_1_sequence():    GPIO.output(en_pin_1, GPIO.HIGH)  # Enable Motor 1    for steps in Steps_Motor_1:        spin_motor(direction_pin_1, pulse_pin_1, en_pin_1, "CW", steps)        sleep(1)  # Delay between steps    #continuous_spin_motor_1("CCW")    GPIO.output(en_pin_1, GPIO.LOW)  # Disable Motor 1# Function to start Motor 2 sequence with steps in Steps_Motor_2 arraydef start_motor_2_sequence():    GPIO.output(en_pin_2, GPIO.HIGH)  # Enable Motor 2    for steps in Steps_Motor_2:        spin_motor(direction_pin_2, pulse_pin_2, en_pin_2, "CCW", steps)        sleep(1)  # Delay between steps    #continuous_spin_motor_2("CW")    GPIO.output(en_pin_2, GPIO.LOW)  # Disable Motor 2# Function to handle Motor 2 steps (forward direction, CW)def start_motor_step_2(index, steps, direction_pin, pulse_pin, en_pin):    spin_motor(direction_pin_2, pulse_pin_2, en_pin_2, "CCW", steps)    sleep(1)  # Delay between steps    # After Motor 2 finishes its current array, hand over control to Motor 1    start_motor_step_1(index)# Function to handle Motor 1 steps (forward direction, CW)def start_motor_step_1(index):    if index < len(Steps_Motor_2):  # If Motor 2 has more steps        # Motor 1 runs all its steps in CW direction        for steps in Steps_Motor_1:            spin_motor(direction_pin_1, pulse_pin_1, en_pin_1, "CW", steps)  # CW        # When Motor 1 completes, switch to reverse direction (CCW)        start_motor_step_1_reverse(index)# Function to handle Motor 1 steps (reverse direction, CCW)def start_motor_step_1_reverse(index):    # Motor 1 runs its reverse steps in CCW direction    continuous_spin_motor_1("CCW")    # After Motor 1 completes the reverse sequence, call Motor 2 to move to the next array    if index < len(Steps_Motor_2) - 1:        start_motor_step_2(index + 1, Steps_Motor_2[index + 1], direction_pin_2, pulse_pin_2, en_pin_2)    else:        # If it's the last array, finish the cycle and go home        go_home()# Function to return both motors home (final step)def go_home():    # Final movement to home positions    continuous_spin_motor_2("CW")  # Motor 2 full rotation home    continuous_spin_motor_1("CCW")  # Motor 1 full reverse home    GPIO.output(en_pin_1, GPIO.LOW)  # Disable Motor 1    GPIO.output(en_pin_2, GPIO.LOW)  # Disable Motor 2# Start the sequencedef start_motor_sequencing():    # Wait for Gear1 to be LOW (reflected), then break and check Gear2    while True:        if ser.in_waiting > 0:  # Check if data is available on the serial port            response = ser.readline().decode().strip()  # Read and decode the response            print(f"Serial received: {response}")            # Split response into Command and Output            if "Gear1" in response:                command, output = response.split(",")  # Split into command and output                print("Gear FOUND")                if command == "Gear":  # Check if command is "Gear1"                    while output != "Done":  # Keep checking until Output is "LOW"                        if ser.in_waiting > 0:  # Check if data is available on the serial port                            response = ser.readline().decode().strip()  # Read and decode the response                            # Split response into Command and Output                            if "," in response:                                command, output = response.split(",")                                print(f"Command: {command}, Output: {output}")                                break # Split into command and output                        if output == "LOW":                            print("Gear1 is LOW. Proceeding to check Gear2...")                            break  # Exit the loop to proceed to checking Gear2                        else:                            print(f"Command: {command}, Output: {output}")                else:                    print("Gear1 not found. Waiting for Gear1...")        else:            print("No data available from serial port.")        sleep(0.1)  # Avoid busy waiting    print("DONE!")    sleep(100)    GPIO.output(en_pin_1, GPIO.HIGH)  # Enable Motor 1    GPIO.output(en_pin_2, GPIO.HIGH)  # Enable Motor 2    spin_motor(direction_pin_1, pulse_pin_1, en_pin_1, "CW", 500) #motor 1 will move at 500 steps    spin_motor(direction_pin_2, pulse_pin_2, en_pin_2, "CCW", 500) #motor 2 will move at 500 steps    continuous_spin_motor_2("CW")  # Motor 2 full rotation home    continuous_spin_motor_1("CCW")  # Motor 1 full reverse home    # Start the sequence by passing the first array of Motor_Step_2    Step_Sequence(direction_pin_1, pulse_pin_1, en_pin_1, direction_pin_2, pulse_pin_2, en_pin_2)def Next_Steps(direction_pin, pulse_pin, enable_pin):    global Motor_Step_1    global Motor_Step_2    if direction_pin == direction_pin_1:        print(Motor_Step_1)        if 0 >= Motor_Step_1 <= 7:            spin_motor(direction_pin, pulse_pin, enable_pin, "CW", Steps_Motor_1[Motor_Step_1])            Motor_Step_1 += 1        print(Motor_Step_1)    elif direction_pin == direction_pin_2:        print(Motor_Step_2)        if 0 <= Motor_Step_2 <= 15:            spin_motor(direction_pin, pulse_pin, enable_pin, "CCW", Steps_Motor_2[Motor_Step_2])            Motor_Step_2 += 1            print(Motor_Step_2)def Previous_Steps(direction_pin, pulse_pin, enable_pin):    global Motor_Step_1    global Motor_Step_2    if direction_pin == direction_pin_1:        print(Motor_Step_1)        if 0 <= Motor_Step_1 <= 8:            Motor_Step_1 -= 1            spin_motor(direction_pin, pulse_pin, enable_pin, "CCW", Steps_Motor_1[Motor_Step_1])    elif direction_pin == direction_pin_2:        print(Motor_Step_2)        if 0 <= Motor_Step_2 <= 14:            Motor_Step_2 -= 1            spin_motor(direction_pin, pulse_pin, enable_pin, "CW", Steps_Motor_2[Motor_Step_2])            print(Motor_Step_2)#new Step Sequence def Step_Sequence(direction_pin_1, pulse_pin_1, enable_pin_1, direction_pin_2, pulse_pin_2, enable_pin_2):    global Motor_Step_1    global Motor_Step_2       global Slot_names    for _ in range(8):  # Repeat 8 times for the full cycle        # Next step on Motor_Step_2 (1 step)        Next_Steps(direction_pin_2, pulse_pin_2, enable_pin_2)        spin_motor(direction_pin_1, pulse_pin_1, enable_pin_1, "CW", 1000)        # Next step on Motor_Step_1 (8 steps)        for _ in range(8):            Next_Steps(direction_pin_1, pulse_pin_1, enable_pin_1)            sleep(1)        # Next step on Motor_Step_2 (1 step)        Next_Steps(direction_pin_2, pulse_pin_2, enable_pin_2)        # Previous step on Motor_Step_1 (8 steps)        for _ in range(8):            Previous_Steps(direction_pin_1, pulse_pin_1, enable_pin_1)            sleep(1)        # Next step on Motor_Step_2 (1 step)        Next_Steps(direction_pin_2, pulse_pin_2, enable_pin_2)        # Next step on Motor_Step_1 (8 steps)        for _ in range(8):            Next_Steps(direction_pin_1, pulse_pin_1, enable_pin_1)            sleep(1)        # Next step on Motor_Step_2 (1 step)        Next_Steps(direction_pin_2, pulse_pin_2, enable_pin_2)        # Previous step on Motor_Step_1 (8 steps)        for _ in range(8):            Previous_Steps(direction_pin_1, pulse_pin_1, enable_pin_1)            sleep(1)        # Repeat for the next cycle# GUI setuproot = tk.Tk()root.title("Motor Control UI")root.geometry("800x480")# Motor 1 Controlsmotor1_frame = tk.LabelFrame(root, text="Motor 1 Controls", padx=10, pady=10)motor1_frame.pack(fill="both", expand=True, padx=10, pady=10)motor1_home_button = tk.Button(motor1_frame, text="Home", command=lambda: threading.Thread(target=continuous_spin_motor_1, args=("CCW",)).start())motor1_home_button.pack(side=tk.LEFT, padx=5, pady=5)motor1_cw_button = tk.Button(motor1_frame, text="Previous", command=lambda: threading.Thread(target=Previous_Steps, args=(direction_pin_1, pulse_pin_1, en_pin_1)).start())motor1_cw_button.pack(side=tk.LEFT, padx=5, pady=5)motor1_ccw_button = tk.Button(motor1_frame, text="Next", command=lambda: threading.Thread(target=Next_Steps, args=(direction_pin_1, pulse_pin_1, en_pin_1)).start())motor1_ccw_button.pack(side=tk.LEFT, padx=5, pady=5)motor1_start_button = tk.Button(motor1_frame, text="Start Motor 1", command=lambda: threading.Thread(target=start_motor_1_sequence).start())motor1_start_button.pack(side=tk.LEFT, padx=5, pady=5)motor1_last_button = tk.Button(motor1_frame, text="Last", command=lambda: threading.Thread(target=continuous_spin_motor_1, args=("CW",)).start())motor1_last_button.pack(side=tk.LEFT, padx=5, pady=5)# Motor 2 Controlsmotor2_frame = tk.LabelFrame(root, text="Motor 2 Controls", padx=10, pady=10)motor2_frame.pack(fill="both", expand=True, padx=10, pady=10)motor2_home_button = tk.Button(motor2_frame, text="Home", command=lambda: threading.Thread(target=continuous_spin_motor_2, args=("CW",)).start())motor2_home_button.pack(side=tk.LEFT, padx=5, pady=5)motor2_cw_button = tk.Button(motor2_frame, text="Previous", command=lambda: threading.Thread(target=Previous_Steps, args=(direction_pin_2, pulse_pin_2, en_pin_2)).start())motor2_cw_button.pack(side=tk.LEFT, padx=5, pady=5)motor2_ccw_button = tk.Button(motor2_frame, text="Next", command=lambda: threading.Thread(target=spin_motor, args=(direction_pin_2, pulse_pin_2, en_pin_2, "CW", 100)).start())motor2_ccw_button.pack(side=tk.LEFT, padx=5, pady=5)motor2_start_button = tk.Button(motor2_frame, text="Start Motor 2", command=lambda: threading.Thread(target=start_motor_2_sequence).start())motor2_start_button.pack(side=tk.LEFT, padx=5, pady=5)motor2_last_button = tk.Button(motor2_frame, text="Last", command=lambda: threading.Thread(target=continuous_spin_motor_2, args=("CCW",)).start())motor2_last_button.pack(side=tk.LEFT, padx=5, pady=5)# Start allstart_frame = tk.LabelFrame(root, text="Start Controls", padx=10, pady=10)start_frame.pack(fill="both", expand=True, padx=10, pady=10)start_button = tk.Button(start_frame, text="Start Sequence", command=lambda: threading.Thread(target=start_motor_sequencing).start())start_button.pack(side=tk.LEFT, padx=5, pady=5)root.mainloop()# Clean up GPIO settings when the application is closedGPIO.cleanup()
Thanks in advance for your help!

Statistics: Posted by XGTFrostKing — Thu Dec 12, 2024 7:02 pm



Viewing all articles
Browse latest Browse all 5398

Trending Articles