ESP32 UNO - Joystick - Servo Motor

Want to aim a camera mount by hand? Pair an ESP32 Uno with a joystick and two servo motors, and the pan-tilt head turns wherever your thumb points. The build runs on the ESP32 Uno form board with plain Arduino code, and the same joystick pan-tilt setup also works for two loose servos on a desk.

By the end, you can:

  1. Connect a thumb stick and two servos to the ESP32 Uno headers
  2. Read the X and Y axes as 12-bit numbers with analogRead()
  3. Turn each axis into a servo angle with map()
  4. Step a pan-tilt kit with direction commands and re-center it with the stick button
ESP32 Uno joystick servo motor

Two Ways to Steer

Inside the stick sit two potentiometers at 90 degrees to each other. One tracks left-right (X), the other tracks up-down (Y), and their voltages leave the module on VRX and VRY. VRX drives servo #1 (pan) and VRY drives servo #2 (tilt).

Mode Stick Action Servo Result
Follow mode Move the stick anywhere Each servo angle mirrors the stick position on its axis
Follow mode Release the stick The stick springs to the middle and both servos return to about 90 degrees
Command mode Hold left / right Servo #1 steps clockwise / counterclockwise
Command mode Hold up / down Servo #2 steps clockwise / counterclockwise
Command mode Release the stick Both servos stay at their last angle
Command mode Press the stick down (SW) Both servos jump back to 90 degrees

Components You Need

1×ESP32 Uno-form Board
1×USB Cable Type-A to Type-C (for USB-A PC)
1×USB Cable Type-C to Type-C (for USB-C PC)
1×Joystick
2×Servo Motor
2×Optionally, Pan-tilt kit with servo motor
1×Jumper Wires

Or you can buy the following kits:

1×DIYables Sensor Kit (18 sensors/displays)
Disclosure: Some of the links provided in this section are Amazon affiliate links. We may receive a commission for any purchases made through these links at no additional cost to you.
Additionally, some of these links are for products from our own brand, DIYables .

Buy Note: For controlling multiple servo motors, use the PCA9685 16 Channel PWM Servo Driver Module to save MCU pins and simplify wiring.

Joystick and Servo at a Glance

After this part, you know which signal each part sends or needs.

Part Pins Used Signal on the ESP32 Uno
Joystick X-axis VRX Analog voltage, read as 0 to 4095
Joystick Y-axis VRY Analog voltage, read as 0 to 4095
Joystick button SW Goes LOW while the stick is pressed down
Servo #1 and #2 Signal wire PWM pulse that sets an angle from 0 to 180 degrees

For the full pinout and the basics of each part, open the ESP32 Uno - Joystick page and the ESP32 Uno - Servo Motor page.

ESP32 Uno Pinout

On the ESP32 Uno form board, every Uno header label hides a GPIO number, and that number goes in your sketch. Find A0, A1, D2, D9 and D10 on the diagram before you start.

ESP32 Uno pinout diagram

How to Connect

When this part is done, the stick and both servos sit on the right headers.

The wiring diagram between ESP32 Uno Joystick Servo Motor

This image is created using Fritzing. Click to enlarge image

※ NOTE THAT:

The wiring diagram may not show the correct pin. On the ESP32 Uno, the joystick SW wire goes to D2 (GPIO26), not A5 as the picture may show. We will update the diagram soon. Until then, follow the wiring table and the code on this page, not the picture.

Component Pin ESP32 Uno Pin
Joystick +5V (VCC) 3.3V
Joystick GND GND
Joystick VRX A0 (GPIO2)
Joystick VRY A1 (GPIO4)
Joystick SW D2 (GPIO26)
Servo #1 red (VCC) 5V
Servo #1 brown or black (GND) GND
Servo #1 orange or yellow (signal) D9 (GPIO13)
Servo #2 red (VCC) 5V
Servo #2 brown or black (GND) GND
Servo #2 orange or yellow (signal) D10 (GPIO5)

Good to know: feed the joystick from 3.3V. The ESP32 Uno pins are not 5V tolerant, and a 5V stick could push VRX and VRY above the safe level. The servos may stay on 5V, because their signal line only receives 3.3V pulses from the board.

WARNING

A0 (GPIO2) and D10 (GPIO5) are boot strapping pins. The chip checks them at power-up to pick its boot mode. A joystick and a servo signal wire are normally harmless here, but if the board refuses to start or upload, unplug the VRX and servo #2 signal wires, upload, then plug them back.

WARNING

A0 (GPIO2) and A1 (GPIO4) belong to ADC2. These sketches do not use WiFi, so analogRead() works fine. If you later add WiFi to this project, the joystick readings on A0 and A1 will stop working.

ESP32 Uno Code

Two sketches follow, one per mode. Both read the stick on A0 and A1 and drive the servos on D9 and D10 through the ESP32Servo library, since the classic Servo library does not run on this board.

ESP32 Uno Code - Servos Follow the Joystick Position

This sketch reads both axes, scales each reading to an angle, and prints the raw numbers next to the angles.

/* * This ESP32 Uno code was developed by newbiely.com * * This ESP32 Uno code is made available for public use without any restriction * * For comprehensive instructions and wiring diagrams, please visit: * https://newbiely.com/tutorials/esp32-uno/esp32-uno-joystick-servo-motor */ #include <ESP32Servo.h> #define VRX_PIN 2 // A0 (GPIO2) reads the joystick VRX output #define VRY_PIN 4 // A1 (GPIO4) reads the joystick VRY output #define SERVO_X_PIN 13 // D9 (GPIO13) sends the signal to servo 1 #define SERVO_Y_PIN 5 // D10 (GPIO5) sends the signal to servo 2 Servo xServo; // servo object for the X-axis motor Servo yServo; // servo object for the Y-axis motor void setup() { Serial.begin(9600) ; // set the ADC attenuation to 11 dB (up to ~3.3V input) analogSetAttenuation(ADC_11db); xServo.attach(SERVO_X_PIN); yServo.attach(SERVO_Y_PIN); } void loop() { // get the raw position of both joystick axes int xValue = analogRead(VRX_PIN); int yValue = analogRead(VRY_PIN); int xAngle = map(xValue, 0, 4095, 0, 180); // turn the 12-bit reading into 0 to 180 degrees int yAngle = map(yValue, 0, 4095, 0, 180); // turn the 12-bit reading into 0 to 180 degrees xServo.write(xAngle); // move servo 1 to its new angle yServo.write(yAngle); // move servo 2 to its new angle // show the readings and angles in the Serial Monitor Serial.print("Joystick: "); Serial.print(xValue); Serial.print(", "); Serial.print(yValue); Serial.print(" => Servo Motor: "); Serial.print(xAngle); Serial.print("°, "); Serial.print(yAngle); Serial.println("°"); }

Good to know: setup() calls analogSetAttenuation(ADC_11db). This 11 dB setting lets each ESP32 Uno analog pin measure the whole span from 0V to about 3.3V. Without the wide span, the readings reach 4095 early and the servos slam to 180 degrees long before the stick hits its edge.

Upload and Test

  1. First time with this board? Set it up with ESP32 Uno - Getting Started before you continue.
  2. Build the circuit by following the wiring table above.
  3. Plug in the USB Type-C cable between the ESP32 Uno and your computer.
  4. Select the board in Arduino IDE: pick ESP32 Dev Module and the COM port of the board (CH340).
  5. Add the servo library: in the Library Manager, search ESP32Servo and install the package by Kevin Harrington.
  6. Paste the sketch into a new Arduino IDE window.
  7. Press Upload (Upload button) and wait for "Done uploading".
  8. Open the Serial Monitor at 9600 baud.
  9. Sweep the stick slowly in a circle and watch both servos track it.
∞
Newbiely | Arduino IDE 2.3.8
──
☐
✕
File
Edit
Sketch
Tools
Help
ESP32 Dev Module
Newbiely.ino
···
8 Serial.println("Hello World!");
Output
Serial Monitor
Message (Enter to send message to 'ESP32 Dev Module' on 'COM15')
New Line
9600 baud
Joystick: 1938, 1902 => Servo Motor: 85°, 83° Joystick: 312, 1915 => Servo Motor: 13°, 84° Joystick: 3417, 1897 => Servo Motor: 150°, 83° Joystick: 1946, 655 => Servo Motor: 85°, 28° Joystick: 1940, 4095 => Servo Motor: 85°, 180°
Ln 11, Col 1
ESP32 Dev Module on COM15
2
  1. Pro tip: note the X and Y numbers with the stick at rest. If they sit far from 2048, the pan-tilt head will rest a few degrees off center. Narrow the map() input range around your real rest value to fix it.

ESP32 Uno Code - Joystick Directions as Servo Commands

Every 100 ms, this sketch turns a pushed axis into a LEFT, RIGHT, UP or DOWN command and steps that servo by one degree. A press on the stick button re-centers both servos.

/* * This ESP32 Uno code was developed by newbiely.com * * This ESP32 Uno code is made available for public use without any restriction * * For comprehensive instructions and wiring diagrams, please visit: * https://newbiely.com/tutorials/esp32-uno/esp32-uno-joystick-servo-motor */ #include <ESP32Servo.h> #include <ezButton.h> #define VRX_PIN 2 // A0 (GPIO2) reads the joystick VRX output #define VRY_PIN 4 // A1 (GPIO4) reads the joystick VRY output #define SW_PIN 26 // D2 (GPIO26) reads the joystick SW button (internal pull-up) #define SERVO_X_PIN 13 // D9 (GPIO13) sends the signal to servo 1 #define SERVO_Y_PIN 5 // D10 (GPIO5) sends the signal to servo 2 #define COMMAND_NO 0x00 #define COMMAND_LEFT 0x01 #define COMMAND_RIGHT 0x02 #define COMMAND_UP 0x04 #define COMMAND_DOWN 0x08 #define LEFT_THRESHOLD 1600 #define RIGHT_THRESHOLD 3200 #define UP_THRESHOLD 1600 #define DOWN_THRESHOLD 3200 #define UPDATE_INTERVAL 100 // check the joystick every 100 ms ezButton button(SW_PIN); // ezButton turns on the internal pull-up of GPIO26 Servo xServo; // servo object for the X-axis motor Servo yServo; // servo object for the Y-axis motor int xValue = 0 ; // latest X-axis reading int yValue = 0 ; // latest Y-axis reading int xAngle = 90; // servo 1 starts in the middle int yAngle = 90; // servo 2 starts in the middle int command = COMMAND_NO; unsigned long lastUpdateTime = 0; void setup() { Serial.begin(9600) ; // set the ADC attenuation to 11 dB (up to ~3.3V input) analogSetAttenuation(ADC_11db); xServo.attach(SERVO_X_PIN); yServo.attach(SERVO_Y_PIN); button.setDebounceTime(50); // ignore button bounce shorter than 50 ms } void loop() { button.loop(); // keep this call at the top of loop() if (millis() - lastUpdateTime > UPDATE_INTERVAL) { lastUpdateTime = millis() ; // get the raw position of both joystick axes xValue = analogRead(VRX_PIN); yValue = analogRead(VRY_PIN); // turn the readings into direction commands // start with no command each time command = COMMAND_NO; // horizontal direction if (xValue < LEFT_THRESHOLD) command = command | COMMAND_LEFT; else if (xValue > RIGHT_THRESHOLD) command = command | COMMAND_RIGHT; // vertical direction if (yValue < UP_THRESHOLD) command = command | COMMAND_UP; else if (yValue > DOWN_THRESHOLD) command = command | COMMAND_DOWN; // NOTE: ZERO, ONE OR TWO COMMANDS CAN BE ACTIVE IN THE SAME ROUND // log each command and step the matching servo by one degree if (command & COMMAND_LEFT) { Serial.println("COMMAND LEFT"); xAngle--; } if (command & COMMAND_RIGHT) { Serial.println("COMMAND RIGHT"); xAngle++; } if (command & COMMAND_UP) { Serial.println("COMMAND UP"); yAngle--; } if (command & COMMAND_DOWN) { Serial.println("COMMAND DOWN"); yAngle++; } } if (button.isPressed()) { Serial.println("The button is pressed"); xAngle = 90; // send servo 1 back to the middle yAngle = 90; // send servo 2 back to the middle } xServo.write(xAngle); // move servo 1 to its current angle yServo.write(yAngle); // move servo 2 to its current angle // show the servo angles in the Serial Monitor Serial.print("Servo Motor's Angle: "); Serial.print(xAngle); Serial.print("°, "); Serial.print(xAngle); Serial.println("°"); }

Upload and Test

  1. Open the Library Manager from the Libraries button in the IDE sidebar.
  2. Type ezButton into the search field; the right entry comes from ArduinoGetStarted.com.
  3. Press Install.
  • Search for ezButton created by ArduinoGetStarted.com and click the Install button.
∞
Newbiely | Arduino IDE 2.3.8
──
☐
✕
File
Edit
Sketch
Tools
Help
ESP32 Dev Module
Library Manager
Type:
All
Topic:
All
ezButton by ArduinoGetStarted.com
Button library supports debounce, pressed/released events and the press counting. It is easy to use with multiple buttons. The library can be used for push-button, momentary switches, toggle switch, magnetic contact switch (door sensor)... It is designed for not only beginners but also experienced users. More info
1.0.6
INSTALL
Newbiely.ino
···
1 void setup() {
Output
Serial Monitor
Ln 1, Col 1
ESP32 Dev Module on COM15
1
  1. Paste the sketch into the Arduino IDE and press Upload.
  2. Open the Serial Monitor at 9600 baud.
  3. Hold the stick in one direction and watch that servo creep one degree at a time.
  4. Press the stick down to snap both servos back to the middle.
∞
Newbiely | Arduino IDE 2.3.8
──
☐
✕
File
Edit
Sketch
Tools
Help
ESP32 Dev Module
Newbiely.ino
···
8 Serial.println("Hello World!");
Output
Serial Monitor
Message (Enter to send message to 'ESP32 Dev Module' on 'COM15')
New Line
9600 baud
Servo Motor's Angle: 90°, 90° COMMAND RIGHT Servo Motor's Angle: 91°, 91° COMMAND RIGHT Servo Motor's Angle: 92°, 92° COMMAND RIGHT Servo Motor's Angle: 93°, 93° The button is pressed Servo Motor's Angle: 90°, 90°
Ln 11, Col 1
ESP32 Dev Module on COM15
2
  1. Pro tip: for a slower, finer pan, raise UPDATE_INTERVAL from 100 to 200. Each held second then moves the head 5 degrees instead of 10.

Video Tutorial

See the ESP32 Uno build in action in the video below.

Code Explanation

After this part, you can change the speed, range and thresholds with confidence.

Code Part What It Does
analogSetAttenuation(ADC_11db) Opens the ADC range to about 0 to 3.3V so the stick covers 0 to 4095
map(xValue, 0, 4095, 0, 180) Scales a 12-bit reading into a servo angle (follow mode)
LEFT_THRESHOLD 1600 / RIGHT_THRESHOLD 3200 Dead zone around the 2048 center; only a clear push creates a command
UPDATE_INTERVAL 100 Sets how often a held direction adds or removes one degree
button.isPressed() Fires once per press of SW and puts both angles back to 90

The result: follow mode moves the servos as fast as your hand, while command mode moves them in small, steady steps.

Good to know: the last print line of the command sketch shows xAngle twice, so the second number in the Serial Monitor is the pan angle, not the tilt angle. The servos still move correctly. To see the tilt angle, change the second Serial.print(xAngle) to Serial.print(yAngle).

ESP32 Uno vs UNO R4 for This Project

Moving this joystick pan-tilt build from the UNO R4 to the ESP32 Uno needs only a few changes.

Item Arduino UNO R4 ESP32 Uno
Logic voltage 5V 3.3V
Joystick power 5V 3.3V
Pins used A0, A1, D2, D9, D10 GPIO2, GPIO4, GPIO26, GPIO13, GPIO5
ADC range 0 to 1023 0 to 4095
Command thresholds 400 and 800 1600 and 3200
Servo library Servo.h ESP32Servo.h

Most wiring positions stay the same, but the SW wire differs from the UNO R4 picture: it goes to D2 (GPIO26) because the A5 pin of the ESP32 Uno is input only and has no internal pull-up. Also move the joystick VCC wire from 5V to 3.3V.

Go Wireless: Steer the Pan-Tilt from Your Phone

Here you get an idea for driving the servos without any wire to your hand.

The ESP32 Uno has WiFi and Bluetooth built in. Swap the physical stick for an on-screen joystick on your phone, and you can aim a pan-tilt camera from across the room. A slider or rotator page also works well for setting one servo to an exact angle.

Good to know: with WiFi on, A0 and A1 cannot be read. If you keep the physical stick in a WiFi version, move VRX and VRY to GPIO32 and GPIO33.

Function References

Each link explains one function used above. The Servo functions behave the same way in the ESP32Servo library.

Troubleshooting

Problem Possible Cause Solution
Board not listed under Port CH340 driver missing or charge-only cable Install the CH340 driver and use a USB cable that carries data
Upload stops at "Waiting for download" Board did not enter flash mode Hold the BOOT button while the upload starts, then release it
Board does not boot or upload with the circuit connected Boot strapping pins A0 (GPIO2) or D10 (GPIO5) pulled the wrong way Unplug the VRX and servo #2 signal wires while uploading, or use another pin
Servos twitch and the board restarts Two servos pull too much current from the USB 5V Power the servos from a separate 5V supply and join its GND to the board GND
One servo is dead while the other works Signal wire on the wrong header or loose GND Check D9 for servo #1, D10 for servo #2 and a shared GND
"ESP32Servo.h - No such file" when compiling Servo library for this board not installed Install ESP32Servo by Kevin Harrington in the Library Manager
Servo jumps to 180 degrees with a small push ADC range too small or joystick on 5V Keep analogSetAttenuation(ADC_11db) in setup() and power the stick from 3.3V

FAQ

How does a thumb joystick report its position?

It holds two potentiometers, one per axis. Each acts as a voltage divider, so VRX and VRY give a voltage between 0V and the supply. On the ESP32 Uno this becomes a number from 0 to 4095, with the middle near 2048.

Is it safe to run the joystick module on 5V with this board?

No. At 5V the VRX and VRY outputs can reach 5V, and the ESP32 Uno pins only accept 3.3V. Connect the module VCC to 3.3V instead. The stick still covers the full 0 to 4095 range.

Which library moves servos on the ESP32 Uno?

Install the Kevin Harrington ESP32Servo package, then put #include <ESP32Servo.h> at the top. It keeps the same attach() and write() calls as the classic Servo library, so only the include line changes.

What is analogSetAttenuation(ADC_11db) doing in the joystick sketch?

It sets the ESP32 Uno ADC input range to about 0 to 3.3V, so the whole joystick swing maps to 0 to 4095. With a smaller range, the reading saturates early and the servo hits its end too soon. An UNO R4 sketch never has this call; it belongs to the ESP32 Uno core only. The stick voltage must stay at or below 3.3V.

Can I add WiFi control and still read the joystick on A0 and A1?

Not on those pins. A0 (GPIO2) and A1 (GPIO4) are ADC2 pins, and ADC2 cannot be read while WiFi is on. Move VRX and VRY to A2 to A5 (GPIO35, 34, 36, 39) or to GPIO32 and GPIO33, then change the pin numbers in code.

How do I make the command mode react to a lighter push?

Bring the thresholds closer to the center. For example, use 1800 and 2800 instead of 1600 and 3200. Do not go too close to 2048, or the servos will drift while the stick rests.

Can two servos run from the board's 5V pin?

Two small SG90 servos often work on USB power when they move lightly. Under load, or when both start together, they can drop the voltage and reset the board. A separate 5V supply with a shared GND is the safe choice.

What if this pan-tilt project needs more pins than the Uno headers give?

The ESP32 Uno has a few extra holes near the power header. GPIO32 and GPIO33 offer I/O, PWM and analog on ADC1, so they keep working while WiFi is on. GPIO15 offers I/O, PWM and analog on ADC2. Add pin headers with a soldering iron, then use the plain GPIO number in the sketch. For this project, GPIO32 and GPIO33 suit a second joystick or a WiFi version of the stick. Two warnings: GPIO15 and GPIO0 (the IO0 pin at the unused Uno NC position) are boot strapping pins, and a wrong connection can stop the board from booting or uploading, so use GPIO32 and GPIO33 first. Also never use the six holes under the digital header (SD0 to SD3, CMD, CLK = GPIO6 to GPIO11), because they connect to the flash chip and using them crashes the board.

Will a joystick shield made for the Uno fit the ESP32 Uno?

The header layout matches, so it plugs in. Check that the shield powers the stick from 3.3V, or that it has a voltage switch. Then map each shield pin to its GPIO with the pinout diagram.

※ OUR MESSAGES

  • As freelancers, We are AVAILABLE for HIRE. See how to outsource your project to us
  • Please feel free to share the link of this tutorial. However, Please do not use our content on any other websites. We invested a lot of effort and time to create the content, please respect our work!