Built-in project · Arduino Uno R3

Claw machine controller on an Arduino Uno

Steer the claw over a prize in the cabinet with the joystick, then press Drop: the winch lowers it, the servo claw grabs, lifts the prize and lets it go. The whole build, an Arduino Uno and 5 more parts, runs here in your browser on the firmware below; open it in the editor to change the wiring or the code and run it again.

Teaching? Assign it to your class in one click.

Advanced About 60 minutes to build and run. Runs in your browser. Free, and no account needed.

Build it as the Claw Machine Kit: the parts in numbered bags and a booklet that checks every step. Retails for about $71, free on Mokxi.

The circuit runs here once the page has loaded.

What you'll learn

  • Driving DC motors both ways with an H-bridge
  • Speed control with PWM on an enable pin
  • A dead zone so a resting joystick does nothing
  • A state machine that runs a timed sequence
  • Making a servo pulse by hand
  • Difficulty: Advanced
  • Time: About 60 minutes
  • Board: Arduino Uno R3
  • Parts: 7

How it works

Two L293D motor drivers run three small gear motors: X and Y move the claw gantry, and Z is the winch. A thumb stick on A0 and A1 steers X and Y. The claw itself opens and closes from a servo signal on pin 12.

Push the stick and the gantry moves. Push it further and it goes faster. Press the arcade button on pin 2, or press the stick, and the claw runs one grab cycle on its own.

The cycle is: lower the winch for 1.4 seconds, close the claw, wait 0.7 seconds, lift for 1.4 seconds, then open the claw. Serial prints each step as 1/4 to 4/4.

Parts list

7 parts, plus the jumper wires. They retail for about $38; on Mokxi they're free.

Wiring table

Every connection, pin by pin, read from the circuit itself. Each row joins the pin on the left to the pin on the right, by a jumper wire or a breadboard strip.

Part and pinConnects to
Claw machine cabinet, gear motor gantry pin CLAWArduino Uno R3 pin 12
Motor driver, L293D (2) pin IN2Arduino Uno R3 pin 11
Motor driver, L293D (2) pin IN1Arduino Uno R3 pin 10
Motor driver, L293D (2) pin EN12Arduino Uno R3 pin 9
Motor driver, L293D (1) pin IN4Arduino Uno R3 pin 8
Motor driver, L293D (1) pin IN3Arduino Uno R3 pin 7
Motor driver, L293D (1) pin EN34Arduino Uno R3 pin 6
Motor driver, L293D (1) pin IN2Arduino Uno R3 pin 5
Motor driver, L293D (1) pin IN1Arduino Uno R3 pin 4
Motor driver, L293D (1) pin EN12Arduino Uno R3 pin 3
Arcade button pin 2Arduino Uno R3 pin 2
Thumb stick pin SWArduino Uno R3 pin 2
Motor driver, L293D (1) pin VSArduino Uno R3 pin 5V
Motor driver, L293D (1) pin VSSArduino Uno R3 pin 5V
Motor driver, L293D (2) pin VSArduino Uno R3 pin 5V
Motor driver, L293D (2) pin VSSArduino Uno R3 pin 5V
Claw machine cabinet, gear motor gantry pin VCCArduino Uno R3 pin 5V
Thumb stick pin VCCArduino Uno R3 pin 5V
Motor driver, L293D (1) pin GND1Arduino Uno R3 pin GND
Motor driver, L293D (2) pin GND1Arduino Uno R3 pin GND
Arcade button pin 1Arduino Uno R3 pin GND
Claw machine cabinet, gear motor gantry pin GNDArduino Uno R3 pin GND
Thumb stick pin GNDArduino Uno R3 pin GND
Thumb stick pin VRXArduino Uno R3 pin A0
Thumb stick pin VRYArduino Uno R3 pin A1
Claw machine cabinet, gear motor gantry pin X+Motor driver, L293D (1) pin OUT1
Claw machine cabinet, gear motor gantry pin X-Motor driver, L293D (1) pin OUT2
Claw machine cabinet, gear motor gantry pin Y+Motor driver, L293D (1) pin OUT3
Claw machine cabinet, gear motor gantry pin Y-Motor driver, L293D (1) pin OUT4
Claw machine cabinet, gear motor gantry pin W+Motor driver, L293D (2) pin OUT1
Claw machine cabinet, gear motor gantry pin W-Motor driver, L293D (2) pin OUT2

The code

The firmware exactly as the editor opens it. Change a line there and press Run: it compiles in the browser.

sketch.ino · Arduino Uno R3
// Claw machine controller: the working control electronics inside the cabinet.
//
// Drag the joystick to move the X and Y gantry motors. Press the blue arcade
// button, or press the joystick itself, to run one complete grab cycle:
// lower the winch, close the claw, lift, then release. The cabinet shows the
// claw doing it while Serial names each state. This controller lets go where
// it lifted; the arcade claw machine carries the prize home to the chute.

const int DROP_BUTTON = 2;

const int X_ENABLE = 3;
const int X_A = 4;
const int X_B = 5;
const int Y_ENABLE = 6;
const int Y_A = 7;
const int Y_B = 8;
const int Z_ENABLE = 9;
const int Z_A = 10;
const int Z_B = 11;
const int CLAW_SERVO = 12;

const int JOYSTICK_X = A0;
const int JOYSTICK_Y = A1;
const int DEAD_ZONE = 140;

enum GrabState { READY, LOWERING, CLOSING, LIFTING, RELEASING };
GrabState state = READY;
unsigned long stateStarted = 0;
unsigned long lastServoFrame = 0;
int clawAngle = 35;

void motor(int enable, int a, int b, int speed) {
  if (speed > 0) {
    digitalWrite(a, HIGH);
    digitalWrite(b, LOW);
    analogWrite(enable, speed);
  } else if (speed < 0) {
    digitalWrite(a, LOW);
    digitalWrite(b, HIGH);
    analogWrite(enable, -speed);
  } else {
    analogWrite(enable, 0);
    digitalWrite(a, LOW);
    digitalWrite(b, LOW);
  }
}

int axisSpeed(int reading) {
  int offset = reading - 512;
  if (offset > -DEAD_ZONE && offset < DEAD_ZONE) return 0;
  int magnitude = offset < 0 ? -offset : offset;
  int speed = map(magnitude, DEAD_ZONE, 512, 90, 255);
  if (speed > 255) speed = 255;
  return offset < 0 ? -speed : speed;
}

void servoFrame() {
  unsigned long now = micros();
  if (now - lastServoFrame < 20000) return;
  lastServoFrame = now;
  int pulse = map(clawAngle, 0, 180, 544, 2400);
  digitalWrite(CLAW_SERVO, HIGH);
  delayMicroseconds(pulse);
  digitalWrite(CLAW_SERVO, LOW);
}

void enter(GrabState next, const char *message) {
  state = next;
  stateStarted = millis();
  Serial.println(message);
}

void setup() {
  pinMode(DROP_BUTTON, INPUT_PULLUP);
  for (int pin = X_ENABLE; pin <= CLAW_SERVO; pin++) pinMode(pin, OUTPUT);
  motor(X_ENABLE, X_A, X_B, 0);
  motor(Y_ENABLE, Y_A, Y_B, 0);
  motor(Z_ENABLE, Z_A, Z_B, 0);
  Serial.begin(115200);
  Serial.println("Claw ready: move the stick, then press Drop");
}

void loop() {
  servoFrame();
  unsigned long elapsed = millis() - stateStarted;

  if (state == READY) {
    motor(X_ENABLE, X_A, X_B, axisSpeed(analogRead(JOYSTICK_X)));
    motor(Y_ENABLE, Y_A, Y_B, axisSpeed(analogRead(JOYSTICK_Y)));
    motor(Z_ENABLE, Z_A, Z_B, 0);
    clawAngle = 35;
    if (digitalRead(DROP_BUTTON) == LOW) {
      motor(X_ENABLE, X_A, X_B, 0);
      motor(Y_ENABLE, Y_A, Y_B, 0);
      enter(LOWERING, "1/4 lowering claw");
    }
  } else if (state == LOWERING) {
    motor(Z_ENABLE, Z_A, Z_B, 210);
    if (elapsed >= 1400) {
      motor(Z_ENABLE, Z_A, Z_B, 0);
      clawAngle = 135;
      enter(CLOSING, "2/4 claw closed");
    }
  } else if (state == CLOSING) {
    if (elapsed >= 700) enter(LIFTING, "3/4 lifting prize");
  } else if (state == LIFTING) {
    motor(Z_ENABLE, Z_A, Z_B, -210);
    if (elapsed >= 1400) {
      motor(Z_ENABLE, Z_A, Z_B, 0);
      clawAngle = 35;
      enter(RELEASING, "4/4 prize released");
    }
  } else if (elapsed >= 800 && digitalRead(DROP_BUTTON) == HIGH) {
    enter(READY, "Claw ready");
  }
}

Code walkthrough

The key functions of the sketch, and what each one does.

motor sketch.ino

Runs one motor. A positive speed sets input A HIGH and B LOW; a negative speed does the reverse, which spins it the other way. The speed itself goes to the enable pin with analogWrite. Zero turns everything off.

void motor(int enable, int a, int b, int speed) {
  if (speed > 0) {
    digitalWrite(a, HIGH);
    digitalWrite(b, LOW);
    analogWrite(enable, speed);
  } else if (speed < 0) {
    digitalWrite(a, LOW);
    digitalWrite(b, HIGH);
    analogWrite(enable, -speed);
  } else {
    analogWrite(enable, 0);
    digitalWrite(a, LOW);
    digitalWrite(b, LOW);
  }
}

axisSpeed sketch.ino

Turns a joystick reading into a signed speed. Readings within DEAD_ZONE (140) of the center at 512 give 0. Beyond that, the distance is mapped to a speed from 90 to 255. The sign says which way to turn.

int axisSpeed(int reading) {
  int offset = reading - 512;
  if (offset > -DEAD_ZONE && offset < DEAD_ZONE) return 0;
  int magnitude = offset < 0 ? -offset : offset;
  int speed = map(magnitude, DEAD_ZONE, 512, 90, 255);
  if (speed > 255) speed = 255;
  return offset < 0 ? -speed : speed;
}

enter sketch.ino

Moves the state machine to its next state. It stores the new state, remembers the time with millis() so each state can measure how long it has run, and prints a message.

void enter(GrabState next, const char *message) {
  state = next;
  stateStarted = millis();
  Serial.println(message);
}

loop sketch.ino

While READY, the stick drives X and Y. When the button reads LOW, both stop and the cycle starts. The other states each run for a set time, then call enter. The last state waits for the button to be released.

void loop() {
  servoFrame();
  unsigned long elapsed = millis() - stateStarted;

  if (state == READY) {
    motor(X_ENABLE, X_A, X_B, axisSpeed(analogRead(JOYSTICK_X)));
    motor(Y_ENABLE, Y_A, Y_B, axisSpeed(analogRead(JOYSTICK_Y)));
    motor(Z_ENABLE, Z_A, Z_B, 0);
    clawAngle = 35;
    if (digitalRead(DROP_BUTTON) == LOW) {
      motor(X_ENABLE, X_A, X_B, 0);
      motor(Y_ENABLE, Y_A, Y_B, 0);
      enter(LOWERING, "1/4 lowering claw");
    }
  } else if (state == LOWERING) {
    motor(Z_ENABLE, Z_A, Z_B, 210);
    if (elapsed >= 1400) {
      motor(Z_ENABLE, Z_A, Z_B, 0);
      clawAngle = 135;
      enter(CLOSING, "2/4 claw closed");
    }
  } else if (state == CLOSING) {
    if (elapsed >= 700) enter(LIFTING, "3/4 lifting prize");
  } else if (state == LIFTING) {
    motor(Z_ENABLE, Z_A, Z_B, -210);
    if (elapsed >= 1400) {
      motor(Z_ENABLE, Z_A, Z_B, 0);
      clawAngle = 35;
  // ... 6 more lines in the editor

Troubleshooting

The usual mistakes with this circuit, and the fix for each.

  • The gantry creeps when you let go of the stick. This project
    Raise DEAD_ZONE. A stick that rests a little off 512 needs a wider dead zone.
  • An axis moves the wrong way. This project
    Swap that motor's two wires on the driver output, or swap its input pins in the sketch.
  • The winch lowers but the claw never closes. This project
    The claw signal is on pin 12. Check it reaches the CLAW pin on the cabinet.
  • The motor is weak or does not turn, and the chip gets hot. Motor driver
    The L293D gives about 600 mA per channel and drops about 1.4 V per side. Use a motor that fits, and for bigger motors pick another driver.
  • Nothing moves even though the inputs are right. Motor driver
    Power both supply pins: VCC1 (pin 16) at 5 V for logic and VCC2 (pin 8) for the motor. Tie the enable pin high, and join all grounds.
  • The button reads pressed all the time, or never changes, when wired to a pin. Arcade button
    Use the COM and NO terminals of the microswitch. Wire COM to ground, NO to the pin, and use INPUT_PULLUP. NC is the opposite and reads pressed when released.
  • One press counts as several presses in your code. Arcade button
    Microswitch contacts bounce for a few milliseconds. Ignore changes for 10 to 20 ms after the first one, or react only to the edge and wait before checking again.
  • The gantry motors or the board misbehave when they run. Claw machine cabinet
    Drive each motor through an H bridge with its own supply, and join the grounds. Motors draw much more than a pin can give. Add a flyback or driver protection and a bulk capacitor.

Change it and keep it

Open it in the editor, change the circuit or the code, and keep your version in a free account.