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.
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.
- 1 × Arduino Uno R3In Starter Kit, Claw Machine Kit, Vending Machine Kit.
- 1 × Full-size breadboard
- 2 × Motor driver, L293DIn Claw Machine Kit.
- 1 × Arcade buttonIn Claw Machine Kit, Fight Stick Kit, Pinball Table Kit.
- 1 × Claw machine cabinet, gear motor gantryIn Claw Machine Kit.
- 1 × Thumb stickIn Claw Machine Kit, Spaceship Panel Kit.
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 pin | Connects to |
|---|---|
| Claw machine cabinet, gear motor gantry pin CLAW | Arduino Uno R3 pin 12 |
| Motor driver, L293D (2) pin IN2 | Arduino Uno R3 pin 11 |
| Motor driver, L293D (2) pin IN1 | Arduino Uno R3 pin 10 |
| Motor driver, L293D (2) pin EN12 | Arduino Uno R3 pin 9 |
| Motor driver, L293D (1) pin IN4 | Arduino Uno R3 pin 8 |
| Motor driver, L293D (1) pin IN3 | Arduino Uno R3 pin 7 |
| Motor driver, L293D (1) pin EN34 | Arduino Uno R3 pin 6 |
| Motor driver, L293D (1) pin IN2 | Arduino Uno R3 pin 5 |
| Motor driver, L293D (1) pin IN1 | Arduino Uno R3 pin 4 |
| Motor driver, L293D (1) pin EN12 | Arduino Uno R3 pin 3 |
| Arcade button pin 2 | Arduino Uno R3 pin 2 |
| Thumb stick pin SW | Arduino Uno R3 pin 2 |
| Motor driver, L293D (1) pin VS | Arduino Uno R3 pin 5V |
| Motor driver, L293D (1) pin VSS | Arduino Uno R3 pin 5V |
| Motor driver, L293D (2) pin VS | Arduino Uno R3 pin 5V |
| Motor driver, L293D (2) pin VSS | Arduino Uno R3 pin 5V |
| Claw machine cabinet, gear motor gantry pin VCC | Arduino Uno R3 pin 5V |
| Thumb stick pin VCC | Arduino Uno R3 pin 5V |
| Motor driver, L293D (1) pin GND1 | Arduino Uno R3 pin GND |
| Motor driver, L293D (2) pin GND1 | Arduino Uno R3 pin GND |
| Arcade button pin 1 | Arduino Uno R3 pin GND |
| Claw machine cabinet, gear motor gantry pin GND | Arduino Uno R3 pin GND |
| Thumb stick pin GND | Arduino Uno R3 pin GND |
| Thumb stick pin VRX | Arduino Uno R3 pin A0 |
| Thumb stick pin VRY | Arduino 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.
// 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 editorTroubleshooting
The usual mistakes with this circuit, and the fix for each.
- The gantry creeps when you let go of the stick. This project
RaiseDEAD_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.