Claw Machine DevLog #1

Preface

So I've recently wandered into an electronics hobbyist store with my girlfriend, and saw all the components in there and was overwhelmingly compelled to build something. I wanted to do something pretty ambitious, as I know how to code, so I figured that the software side of things wouldn't be too difficult. My girlfriend had mentioned that she wanted a mini claw machine she saw online, one where you put candy in and just play around to get the candy, and I thought "hmm... I could totally   build one myself". I bought a few parts and was on my way.

Originally, I wasn't intending to make a blog series or post anything other than the finished product online, but a co-worker of mine convinced me to create a dev log about it. This log will be a description of my learnings, thoughts, and understanding of the project at the time of writing, I'll detail my thought process, what materials I'm using, any progress I'm making, and any code that goes along with the build. Hopefully it'll be possible to follow along with this dev log and possibly use it as a guide, but do keep in mind, that if you do intend to follow this as a guide, these logs are subject to error. I may make mistakes and realize them later on. I might post a complete tutorial at a later point if I see that there is interest in me doing so, but for now, it's just going to be these logs.

Log

I'm writing this log about 2 weeks in to the development of the claw machine. So far, it's been a lot of learning and absorbing information, trial and error, and eventual success. Here's what I've done/learned so far:

At first, I bought 3 NEMA 17 Stepper Motors, 3 A4988 Stepper Motor Drivers, a Zippy Joystick, 3 arcade-style buttons, and a USB Game Controller Interface. I hooked up the motors to the drivers, and the drivers to a Raspberry Pi Model 3B, and then connected the joystick and buttons to the game controller, and connected the game controller to a USB port on the Pi.

The way these stepper motors work, is that they have 2 magnetic coils within them, which you alternate current between, causing the motor to spin. Here's a good video describing the process:


Now that I understand how this motor works, I can start connecting things to see if I can get it working. Initially, I connected the arcade buttons and joystick to the controller like so:

Note that the USB game controller interface that I've linked is a bit different than what is in that illustration, but the idea is the same. After I got the controls all wired up, I connected the controller to one of the USB ports on the pi. The next step is to connect the motors to the Pi as well. I'll only describe the process for connecting one, but the process can be applied to connect as many as you want.

Here is a good illustration of how we'll be connecting our motor:



As you can see, the reset pin and the sleep pin need to be connected to one another, and although the image doesn't show it, you'll need to connect the reset pin to a positive charge. The STEP and DIR pins on the chip will connect directly to GPIO pins on the Pi. I chose to connect DIR to pin 23 and STEP to pin 24 on the Pi, just because they were close to each other. If we look back at the image above, we'll notice that there are two power supplies here. This is because the Pi alone does not supply enough voltage to power the stepper motor, as the motor operates between 8V - 30V. A 9V battery is enough to run the stepper motor, but since I have a variable power supply, I used that. I connected a 100 microfarad capacitor between pins VMOT and GND, as shown on the picture. This is done because voltage spikes can damage the motor, and the capacitor helps minimize input voltage spikes going to the motor, keeping it running smoother. Keep in mind that the capacitor needs to be in parallel with the VMOT and GND pins in order to work properly, which can be done by connecting the anode (longer leg of the capacitor) to the VMOT pin and the cathode (shorter leg) to the GND, From here, I connect the anode of the capacitor to positive from my power supply, and the cathode to negative (ground). At this point, I'm done setting up the power supply for the motor, but I still have to connect the power supply for the A4988 chip. If we refer back to the image above, we see that the VDD and GND pins on the bottom right of the chip connect to a microcontroller (this is our Pi). I connected pin 2 on the Pi to the VDD pin on the chip, and pin 6 on the Pi to the GND pin on the chip. Pins 2 and 6 were 5V and Ground, respectively. Now I'm almost done connecting the motor; all that's left is connecting the electromagnet signal wires. There are 2 sets of wires on the motor, each set corresponding to one coil, and each set must be connected to the chip as a pair. The only problem is, there's no obvious way to tell which wires are connected to which coil. The way I determined it was by inserting an LED into 2 wires at a time, one leg of the LED connecting to one wire, and then turning the motor by hand. If the LED lights up (even slightly) then the 2 wires it is connected to are connected to the same coil, and therefore they're a pair. After finding both pairs, I connect the wires to the 1A, 1B, 2A, and 2B pins respectively. We're almost done hooking everything up; in fact, the wiring for the motor is done. If you now look at the chip again, you'll notice a screw on the bottom. This is a potentiometer built in to the chip, and controls the amount of current that flows to the motor. I twist this until I have about 0.4 amps running to the motor (it's rated to run at 0.45 amps I believe, so don't go past that point). Now I'm ready to write some code.

Since this is a claw machine, we're using joystick controls. The easiest/simplest way to read joystick and button controls on the Pi is by using the PyGame python library. I'll use this to hook the controls up to the motor. Here's the python code I wrote to connect it:


import pygame
from time import sleep
import RPi.GPIO as GPIO

# Define colours for pygame
BLACK    = (   0,   0,   0)
WHITE    = ( 255, 255, 255)

# --------------- Stepper motor setup -----------------------
DIR = 23 #GPIO direction pin
STEP = 24 #GPIO step pin
CW = 1 # Clockwise
CCW = 0 # Counterclockwise
SPR = 240

GPIO.setmode(GPIO.BCM)
GPIO.setup(DIR, GPIO.OUT)
GPIO.setup(STEP, GPIO.OUT)
GPIO.output(DIR, CW)

delay = 0.01 # How long we wait between GPIO outputs

xAxisMoving = False
yAxisMoving = False

# ----------------------------------------------------------

done = False

class TextPrint:
    def __init__(self):
        self.reset()
        self.font = pygame.font.Font(None, 20)

    def printt(self, screen, textString):
        textBitmap = self.font.render(textString, True, BLACK)
        screen.blit(textBitmap, [self.x, self.y])
        self.y += self.line_height
        
    def reset(self):
        self.x = 10
        self.y = 10
        self.line_height = 15
        
    def indent(self):
        self.x += 10
        
    def unindent(self):
        self.x -= 10
    

# -------- PyGame Initialization -------
pygame.init()
size = [1280, 720]
screen = pygame.display.set_mode(size)
pygame.display.set_caption("Claw Machine Debugger Module")
clock = pygame.time.Clock()
pygame.joystick.init()
textPrint = TextPrint()
# -------------------------------------

# -------- Main Program Loop -----------
while done==False:
    # EVENT PROCESSING STEP
    for event in pygame.event.get(): # User did something
        if event.type == pygame.QUIT: # If user clicked close
            done=True # Flag that we are done so we exit this loop
        
        # Possible joystick actions: JOYAXISMOTION JOYBALLMOTION JOYBUTTONDOWN JOYBUTTONUP JOYHATMOTION
        if event.type == pygame.JOYBUTTONDOWN:
            print("Joystick button pressed.")
        if event.type == pygame.JOYBUTTONUP:
            print("Joystick button released.")
        if event.type == pygame.JOYAXISMOTION and event.dict['axis'] == 1:
            if round(event.dict['value']) == -1:
                GPIO.output(DIR, CCW)
                xAxisMoving = True
            elif round(event.dict['value']) == 1:
                GPIO.output(DIR, CW)
                xAxisMoving = True
            elif round(event.dict['value']) == 0:
                xAxisMoving = False

    if xAxisMoving:
        GPIO.output(STEP, GPIO.HIGH)
        sleep(delay)
        GPIO.output(STEP, GPIO.LOW)
        sleep(delay)
 
    # DRAWING STEP
    # First, clear the screen to white. Don't put other drawing commands
    # above this, or they will be erased with this command.
    screen.fill(WHITE)
    textPrint.reset()

    # Get count of joysticks
    joystick_count = pygame.joystick.get_count()

    textPrint.printt(screen, "Number of joysticks: {}".format(joystick_count) )
    textPrint.indent()
    
    # For each joystick:
    for i in range(joystick_count):
        joystick = pygame.joystick.Joystick(i)
        joystick.init()
    
        textPrint.printt(screen, "Joystick {}".format(i) )
        textPrint.indent()
    
        # Get the name from the OS for the controller/joystick
        name = joystick.get_name()
        textPrint.printt(screen, "Joystick name: {}".format(name) )
        
        # Usually axis run in pairs, up/down for one, and left/right for
        # the other.
        axes = joystick.get_numaxes()
        textPrint.printt(screen, "Number of axes: {}".format(axes) )
        textPrint.indent()
        
        for i in range( axes ):
            axis = joystick.get_axis( i )
            textPrint.printt(screen, "Axis {} value: {:>6.3f}".format(i, axis) )
        textPrint.unindent()
            
        buttons = joystick.get_numbuttons()
        textPrint.printt(screen, "Number of buttons: {}".format(buttons) )
        textPrint.indent()

        for i in range( buttons ):
            button = joystick.get_button( i )
            textPrint.printt(screen, "Button {:>2} value: {}".format(i,button) )
        textPrint.unindent()
            
        # Hat switch. All or nothing for direction, not like joysticks.
        # Value comes back in an array.
        hats = joystick.get_numhats()
        textPrint.printt(screen, "Number of hats: {}".format(hats) )
        textPrint.indent()

        for i in range( hats ):
            hat = joystick.get_hat( i )
            textPrint.printt(screen, "Hat {} value: {}".format(i, str(hat)) )
        textPrint.unindent()
        
        textPrint.unindent()

    
    # ALL CODE TO DRAW SHOULD GO ABOVE THIS COMMENT
    
    # Go ahead and update the screen with what we've drawn.
    pygame.display.flip()

    # Limit to 20 frames per second
    clock.tick(20)
    
pygame.quit ()

At this point, if everything is properly connected, running the above code and moving the joystick will open a window with the joystick axes and buttons displayed, and moving the joystick left/right will turn the motor clockwise and counterclockwise respectively. The python code is fairly basic so I won't explain it in detail, but essentially what is does is initialize PyGame, set up the pins on our Pi to connect to the motor, and run a loop to check for movement of the joystick. If the joystick moves, we make the motor move. Here's a video of what this looks like (I tied something to the motor so you could see the movement)


The only issue now is that the motor runs slowly. This is because, since we are using PyGame and the clock library, we are limiting the amount of pulses we're able to send to the motor per second, and less pulses means less frequent movement. Fixing this is relatively simple, we just don't use PyGame to open a window, and only import what we really need instead (which is the joystick control portion). At this point, I begin restructuring the code and splitting it into classes to make it more readable. Here are the files:


claw.py

from time import sleep
import RPi.GPIO as GPIO

class Claw:
  def __init__(self, x_direction_pin, x_step_pin, x_movement_direction):
    self.x_direction_pin = x_direction_pin
    self.x_step_pin = x_step_pin
    self.xAxisMoving = False
    self.yAxisMoving = False
    self.delay = 0.001 # How long we wait between GPIO outputs

    GPIO.setmode(GPIO.BCM)
    GPIO.setup(x_direction_pin, GPIO.OUT)
    GPIO.setup(x_step_pin, GPIO.OUT)
    GPIO.output(x_direction_pin, x_movement_direction) # 1 IS CLOCKWISE, 0 IS COUNTERCLOCKWISE

  def toggleMoveLeft(self):
    GPIO.output(self.x_direction_pin, 0)
    self.xAxisMoving = True

  def toggleMoveRight(self):
    GPIO.output(self.x_direction_pin, 1)
    self.xAxisMoving = True

  def stopMovementOnAxis(self, axis):
    if axis == 'x':
      self.xAxisMoving = False
    elif axis == 'y':
      self.yAxisMoving = False

  def updatePosition(self):
    if self.xAxisMoving:
      GPIO.output(self.x_step_pin, GPIO.HIGH)
      sleep(self.delay)
      GPIO.output(self.x_step_pin, GPIO.LOW)
      sleep(self.delay)
    elif self.yAxisMoving:
      # TODO
      print "Not implemented"

claw_machine_game.py

import pygame
from claw import Claw

class ClawMachineGame:
    
    def __init__(self):
        pygame.init()
        self.joystick = pygame.joystick.Joystick(0)
        self.joystick.init()
        print 'Initialized Joystick : %s' % self.joystick.get_name()
        print 'Axes Detected: %s' % self.joystick.get_numaxes()
        print 'Buttons Detected: %s' % self.joystick.get_numbuttons()
        
        self.claw = Claw(23, 24, 1)
        
    def start(self):
        while True:
            self.claw.updatePosition()
            
            values = self.getJoyStickValues()
            print values
            
            if values[1] < 0:
                self.claw.toggleMoveLeft()
            elif values[1] > 0:
                self.claw.toggleMoveRight()
            elif values[1] == 0:
                self.claw.stopMovementOnAxis('x')
                
    def getJoyStickValues(self):
        out = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]
        it = 0 #iterator
        pygame.event.pump()
        
        #Read input from the two joysticks       
        for i in range(0, self.joystick.get_numaxes()):
            out[it] = self.joystick.get_axis(i)
            it+=1
        #Read input from buttons
        for i in range(0, self.joystick.get_numbuttons()):
            out[it] = self.joystick.get_button(i)
            it+=1
        return out
 

run.py

from claw_machine_game import ClawMachineGame

game = ClawMachineGame()

game.start()

Placing these files in the same directory and then running run.py will make your motor run much more quickly when moving the joystick.Now it moves much quicker:


Since the joystick has an up, down, left, and right movement, most of our controls are covered by repeating some of this code for additional axes. We just connect an additional motor for each axis we want to move across. Since claw machines can move along the x, y, and z axes, we need 3 stepper motors. Make sure to connect each motor in parallel, so that if 2 are ever running at the same time (I.E. moving forward and to the right), they don't slow down. Using the joystick, we will control 2 motors (2 axes). Left/right on the joystick will control one motor to move clockwise/counterclockwise, which will end up moving the claw left and right. We can mimic this same behavior for moving the claw forwards and backwards. Now, we've run out of axes to connect the joystick to. I opted to use the arcade buttons for moving the claw in the Y axis. One button moves the claw upwards, and the other moves it downwards. Since this is really just controlling the direction in which the motor turns, the code looks very similar to the joystick code. After writing out all that code and doing some refactoring, my files looked like this. At this point, it's time to start planning how the gantry of the claw machine will work. I whipped up a quick 3D rendering of how it'll look/be laid out:


This is a claw machine that will be 2'x2'x3'. My next steps are to buy aluminum extrusion, some rods, pulleys, and other materials. So I'll make another post once I've gathered those! In the meantime, if you have any questions regarding the build, feel free to email me at dovzhanyn.alex@gmail.com.