u

🧩 Syntax:
#!/usr/bin/env python3
import rclpy
from rclpy.node import Node
from sensor_msgs.msg import Joy
import RPi.GPIO as GPIO
from time import sleep
 
class StepperJoyNode(Node):
    def __init__(self):
        super().__init__('stepper_joy_node')
        # ── CONFIG ──
        self.DIR1_PIN    = 20
        self.STEP1_PIN   = 21
        self.DIR2_PIN    = 10
        self.STEP2_PIN   = 11
        self.LB_BUTTON    = 4
        self.RB_BUTTON    = 5
        self.DPAD_UP     = 13
        self.DPAD_DOWN   = 14
        self.STEP_DELAY = 0.001
        self.STOP_DURATION = 2.0  # seconds
        # ────────────
        GPIO.setmode(GPIO.BCM)
        GPIO.setup(self.DIR1_PIN,  GPIO.OUT)
        GPIO.setup(self.STEP1_PIN, GPIO.OUT)
        GPIO.setup(self.DIR2_PIN,  GPIO.OUT)
        GPIO.setup(self.STEP2_PIN, GPIO.OUT)
        self._mode = None
        self._pressed_button = None
        self._press_start_time = None
        self._motor_disabled = False
 
        self.create_subscription(Joy, 'joy', self.joy_callback, 10)
        self.create_timer(self.STEP_DELAY * 2, self.step_timer)
        print("Initialized")
 
    def joy_callback(self, msg: Joy):
        buttons = msg.buttons
        new_mode = None
        pressed_button = None
 
        if buttons[self.LB_BUTTON]:
            new_mode = 'LB'
            pressed_button = self.LB_BUTTON
        elif buttons[self.RB_BUTTON]:
            new_mode = 'RB'
            pressed_button = self.RB_BUTTON
        elif buttons[self.DPAD_UP]:
            new_mode = 'UP'
            pressed_button = self.DPAD_UP
        elif buttons[self.DPAD_DOWN]:
            new_mode = 'DOWN'
            pressed_button = self.DPAD_DOWN
 
        if pressed_button is not None:
            if self._motor_disabled:
                return
            if pressed_button != self._pressed_button:
                # New press started
                self._pressed_button = pressed_button
                self._press_start_time = self.get_clock().now()
                self._mode = new_mode
                if pressed_button == self.LB_BUTTON:
                    print("Pressed LB")
                elif pressed_button == self.RB_BUTTON:
                    print("Pressed RB")
                elif pressed_button == self.DPAD_UP:
                    print("Pressed DPad Up")
                elif pressed_button == self.DPAD_DOWN:
                    print("Pressed DPad Down")
            else:
                # Button is being held
                elapsed = (self.get_clock().now() - self._press_start_time).nanoseconds * 1e-9
                if elapsed >= self.STOP_DURATION:
                    print("Button held too long — motor disabled.")
                    self._motor_disabled = True
                    self._mode = None
        else:
            # No button is being pressed — reset everything
            self._pressed_button = None
            self._press_start_time = None
            self._mode = None
            self._motor_disabled = False
 
    def step_timer(self):
        if self._mode is None:
            return
 
        if self._mode == 'LB':
            d1 = GPIO.LOW  # CCW
            d2 = GPIO.HIGH  # CW
        elif self._mode == 'RB':
            d1 = GPIO.HIGH
            d2 = GPIO.LOW
        elif self._mode == 'UP':
            d1 = GPIO.LOW
            d2 = GPIO.LOW
        else:
            d1 = GPIO.HIGH
            d2 = GPIO.HIGH
 
        GPIO.output(self.DIR1_PIN, d1)
        GPIO.output(self.DIR2_PIN, d2)
 
        GPIO.output(self.STEP1_PIN, GPIO.HIGH)
        GPIO.output(self.STEP2_PIN, GPIO.HIGH)
        sleep(self.STEP_DELAY)
        GPIO.output(self.STEP1_PIN, GPIO.LOW)
        GPIO.output(self.STEP2_PIN, GPIO.LOW)
 
    def destroy_node(self):
        super().destroy_node()
        GPIO.cleanup()
 
def main(args=None):
    rclpy.init(args=args)
    node = StepperJoyNode()
    try:
        rclpy.spin(node)
    except KeyboardInterrupt:
        pass
    finally:
        node.destroy_node()
        rclpy.shutdown()
 
if __name__ == '__main__':
    main()