ESP32 UNO - DIYables Bluetooth App Joystick
Overview
Want to drive a robot car with your thumb? Put an ESP32 Uno Bluetooth joystick on your phone screen and the ESP32 Uno form board gets an X and a Y number for every finger move. The DIYables Bluetooth STEM app sends both values, from -100 to +100, as a BLE joystick for Android and iPhone or over Classic Bluetooth for Android.
By the end, you can:
- Add the DIYables Bluetooth library to Arduino IDE
- Flash a BLE or a Classic Bluetooth joystick sketch to the board
- Link the phone app and print X/Y positions in the Serial Monitor
- Mix the two axes into wheel speeds, servo angles or LED signals
- Smooth the control with a dead zone, speed ramping and angle math

※ NOTE THAT:
The ESP32 Uno has two Bluetooth radios in one chip: BLE and Classic Bluetooth. Choose BLE when an iPhone must connect, or when you want to skip the pairing step. Choose Classic when every phone in the project is Android.
What the Joystick App Gives You
Use this table to see what the library already does for you.
| Feature | Details |
|---|---|
| Two axes | X goes left to right, Y goes down to up, each from -100 to +100 |
| Live stream | New numbers arrive while your finger is still on the pad |
| Auto-center | Optional, the stick snaps back to (0, 0) when released |
| Sensitivity | A minimum move in percent, so tiny shakes send nothing |
| Two-way data | The board can push a position or a text line back to the app |
| Radio modes | BLE for Android and iOS, Classic Bluetooth for Android only |
| Power | BLE draws less current than Classic Bluetooth |
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 | × | Breadboard | |
| 1 | × | Jumper Wires |
Or you can buy the following kits:
| 1 | × | DIYables Sensor Kit (18 sensors/displays) |
Additionally, some of these links are for products from our own brand, DIYables .
ESP32 Uno Pinout
Your sketch never uses the printed header names like D9 or D10. It uses the GPIO number behind each one, so look it up on this map before you hook up a motor driver or a servo.

Good to know: the main joystick sketch needs no wires at all. The robot and servo examples further down use these header positions:
| Example Signal | ESP32 Uno Pin |
|---|---|
| Left motor speed (PWM) / pan servo | D9 (GPIO13) |
| Right motor speed (PWM) / tilt servo | D10 (GPIO5) |
| Left motor direction | D7 (GPIO14) |
| Right motor direction | D8 (GPIO12) |
| Extra outputs (LEDs, second direction pins, steppers) | D2 (GPIO26), D3 (GPIO25), D4 (GPIO17), D5 (GPIO16) |
WARNING
D8 (GPIO12) and D10 (GPIO5) are boot strapping pins. GPIO12 must be LOW while the board starts; if a motor driver pulls it HIGH, the flash gets the wrong voltage and the board will not boot. GPIO5 can also block a normal start. If uploads or resets fail, unplug the wires from D8 and D10, upload, then plug them back in. All ESP32 Uno pins run at 3.3V and are not 5V tolerant.
Upload and Test the ESP32 Uno Code
At the end of this part, the board waits for your phone and prints every joystick move. No wiring is needed yet, only the USB cable.
Step 1 — Prepare Arduino IDE
- First time with this board? Set it up with ESP32 Uno - Getting Started before you continue.
- Build the circuit by following the wiring table above. For this first test, leave all header pins empty.
- Plug in the USB Type-C cable between the ESP32 Uno and your computer.
- Select the board in Arduino IDE: pick ESP32 Dev Module and the COM port of the board (CH340).
- Give the sketch more flash: open Tools > Partition Scheme and select Huge APP (3MB No OTA/1MB SPIFFS). The normal layout has room for about 1.2 MB of program, and the Bluetooth stack is bigger.
- Open Library Manager: click the Libraries icon in the left bar.
- Type "DIYables Bluetooth" in the search box and find the library by DIYables.
- Press Install.
- Search for DIYables Bluetooth created by DIYables and click the Install button.
- Add the helpers: a pop-up lists the libraries it depends on. Press Install All.
Now flash ONE of the two sketches. They show the same joystick; only the radio differs.
Step 2 — Option A: ESP32 Uno Classic Bluetooth Sketch (Android only)
Good to know: iOS has no Classic Bluetooth support. iPhone users go straight to Option B.
- Load the sketch: in Arduino IDE open File Examples DIYables Bluetooth Esp32Bluetooth_Joystick, or paste the code below into a blank sketch.
- Upload: hit the Upload button.
- Open the Serial Monitor at 115200 baud. After a reset it prints:
Step 2 — Option B: ESP32 Uno BLE Sketch (Android and iOS)
- Load the sketch: open File Examples DIYables Bluetooth Esp32BLE_Joystick, or paste the code below.
- Upload it with the Upload button.
- Read the Serial Monitor at 115200 baud:
- Pro tip: give each board its own name if two ESP32 Uno robots run in the same room. Change DEVICE_NAME (BLE) or the text in bluetooth("ESP32_Joystick") (Classic), so you never steer the wrong car.
Step 3 — Connect the Phone App
When this step is done, your thumb moves the numbers on the board.
- Classic sketch only — pair first: on the Android phone go to Settings > Bluetooth, switch it on, tap "ESP32_Joystick", accept the request (no PIN), and wait until it says "Paired". The BLE sketch skips this step.
- Start the app and grant the access it asks for the first time:
- Nearby Devices (Android 12 or newer) or Bluetooth (iOS) — lets the app find and link boards
- Location (Android 11 or older) — old Android versions need it for a BLE scan

- Pick your board: "ESP32_Joystick" for Classic, or "ESP32BLE_Joystick" for BLE. Once linked, the app jumps back to the home screen.
- Open the pad: choose Joystick in the app menu.

- Drag the stick in any direction.

- Pro tip: the gear icon on the home screen hides the apps you do not need. Every screen is covered in the DIYables Bluetooth App User Manual.
The Serial Monitor now logs the link and each stick position:
How the Sketch Works
Here is what each part of the ESP32 Uno sketch does.
| Code Part | What It Does |
|---|---|
| DIYables_Esp32BLE / DIYables_Esp32Bluetooth | Picks the radio and the name the phone sees |
| DIYables_BluetoothJoystick(false, 5) | Makes the joystick app, auto-center off, 5% minimum move |
| onJoystickValue() | Runs on every move and stores X and Y in two global variables |
| onGetConfig() | Sends the stored X/Y back when the app opens the joystick screen |
| bluetoothServer.loop() | Handles all radio traffic, so keep loop() free of long delays |
The result: your control code only needs currentJoystickX and currentJoystickY, and the library handles the rest.
Library Functions for Your Own Build
These calls let you shape how the joystick feels and what the board sends back.
Auto-Center and Sensitivity
The first constructor value turns auto-center on or off. The second value is the smallest change, in percent, that triggers a new update. You can set both again at run time and read them back.
React to Stick Moves
Put all of your motor, servo or LED logic inside onJoystickValue(). It gets two integers on every move.
Answer the App's Position Request
Each time the joystick screen opens, the app asks the ESP32 Uno where the stick should be. Reply in onGetConfig() so the pad starts at the right spot.
Push Data to the Phone
The board can also talk first. Send a position, or a short status text.
Link Up and Link Lost Events
For anything that moves, the disconnect event is your safety stop. Use it to halt the motors when the phone goes out of range.
Reading the Joystick Pad
This tells you how to turn the numbers into real movement.
The app screen shows a round pad you touch and drag, plus the live X and Y numbers under it.
| Axis | Lowest | Middle | Highest |
|---|---|---|---|
| X | -100 (far left) | 0 | +100 (far right) |
| Y | -100 (far down) | 0 | +100 (far up) |
With no finger on the pad (or auto-center on and the finger lifted), the position reads (0, 0).
Project Code Examples
Each snippet below plugs into the main sketch. Pins follow the Uno header positions, with the ESP32 Uno GPIO number written in the code.
Minimal Handler
The smallest useful callback: save the position and print it.
Two-Wheel Robot Control
A two-wheel car steers by running the wheels at different speeds. X (turn) and Y (throttle) mix into a left and a right speed. Each motor uses one PWM pin and two direction pins, as on an L298N-type driver.
| Driver Input | ESP32 Uno Pin |
|---|---|
| Left PWM (ENA) | D9 (GPIO13) |
| Left IN1 | D7 (GPIO14) |
| Left IN2 | D2 (GPIO26) |
| Right PWM (ENB) | D10 (GPIO5) |
| Right IN3 | D8 (GPIO12) |
| Right IN4 | D3 (GPIO25) |
WARNING
D8 (GPIO12) and D10 (GPIO5) are boot strapping pins. Many motor driver boards have pull-up resistors on their inputs. If the ESP32 Uno will not start or upload with the driver attached, unplug the D8 and D10 wires during upload and reset. Power the motors from their own battery and join all GND wires.
Pan-Tilt Servo Mount
X turns the pan servo and Y tilts the second one. map() converts -100..+100 to 0..180 degrees. On the ESP32 Uno, include ESP32Servo.h (library "ESP32Servo" by Kevin Harrington) instead of Servo.h. Pan signal goes to D9 (GPIO13), tilt signal to D10 (GPIO5).
Good to know: feed the servos from a separate 5V supply with a shared GND. The 3.3V signal from the ESP32 Uno is enough for most hobby servos. The tilt signal sits on D10 (GPIO5), a boot strapping pin, so unplug it if the board will not start.
Four-LED Direction Indicator
Four LEDs show where the stick points. Each one lights only after the value passes 30, so small moves near the middle do nothing. Wire each LED through a 220 ohm resistor.
Smoother Control Tricks
Raw values are fine on the desk. On a real machine, these three tricks make driving safer and less jumpy.
Dead Zone Filter
A thumb never rests at exactly zero. The dead zone turns any value near the middle into zero, so the wheels do not creep.
Speed Ramping
A jump from stop to full power can flip a small robot. Here the callback only saves a target, and loop() walks the real value toward it a few steps at a time.
Magnitude and Angle
A polar view is handy for tank turrets and omni robots. The distance from the middle gives a speed; atan2() gives a heading in degrees.
Bigger Builds
Two larger starting points. Add your own driver code where the comments say so.
Mecanum Wheel Robot
Mecanum rollers let a car move sideways as well as forward. The code blends X and Y in a different way for each of the four wheels, and their PWM wires land on D2 (GPIO26), D3 (GPIO25), D4 (GPIO17) and D5 (GPIO16).
Stepper Motor Speed Control
With the AccelStepper library, each axis sets the speed of one stepper. Wire the X-axis driver to D2 (GPIO26) for STEP and D3 (GPIO25) for DIR, and the Y-axis driver to D4 (GPIO17) for STEP and D5 (GPIO16) for DIR.
ESP32 Uno vs UNO R4 for This Project
This shows what changes when you move the joystick project from the UNO R4 WiFi to the ESP32 Uno.
| Item | Arduino UNO R4 | ESP32 Uno |
|---|---|---|
| Logic voltage | 5V | 3.3V |
| Bluetooth | BLE only | BLE and Classic Bluetooth |
| Library platform | DIYables_ArduinoBLE.h | DIYables_Esp32BLE.h or DIYables_Esp32Bluetooth.h |
| Serial baud in the example | 9600 | 115200 |
| Servo library | Servo.h | ESP32Servo.h |
| Robot pins D9 / D10 / D7 / D8 | 9 / 10 / 7 / 8 | GPIO13 / GPIO5 / GPIO14 / GPIO12 |
| Arduino IDE board | Arduino UNO R4 WiFi | ESP32 Dev Module plus Huge APP partition |
The same header positions work as-is. Watch D8 and D10, which are boot strapping pins on the ESP32 Uno, and never send a 5V signal back into any pin.
BLE or Classic Bluetooth?
Pick the radio that fits your phone and your power budget.
| Point | BLE sketch (Esp32BLE_Joystick) | Classic sketch (Esp32Bluetooth_Joystick) |
|---|---|---|
| iOS support | Yes | No |
| Android support | Yes | Yes |
| Power use | Low | Higher |
| Range | About 30-100 m | About 10-100 m |
| Data rate | Lower | Higher |
| Pairing | Not needed, auto-connect | Needed once in phone settings |
| Best for | Battery builds, mixed phones | Android-only, more throughput |
Build Ideas
A joystick fits anything that moves in two directions.
| Area | Ideas |
|---|---|
| Robots | Differential drive car, mecanum or omni-wheel robot, robot arm joint, drone ground station |
| Camera rigs | Pan-tilt head, motorized slider, remote security camera |
| Games | Bluetooth game pad, maze game, LED matrix Snake or Pong |
| Tutorial | CNC jog pendant, motorized stage, small crane or winch |
Combine With Other Bluetooth Apps
One Bluetooth server can host several apps side by side.
Joystick Plus Slider
The stick picks the direction and a slider caps the top speed.
Joystick Plus Monitor
The stick drives the motors while the monitor shows live data on the phone.
Try These Next
Once your joystick works, these ESP32 Uno pages make good next steps:
- ESP32 Uno - Bluetooth Slider — set an exact value, such as a speed limit
- ESP32 Uno - Bluetooth Digital Pins — switch lights and relays on and off
- ESP32 Uno - Bluetooth Monitor — read joystick and status text on the phone
- ESP32 Uno - Bluetooth Multiple Apps — run the joystick next to sliders and other controls
- ESP32 Uno - Joystick — use a real thumb-stick module instead of the phone
For more help, check the library API reference, the DIYables tutorials and the Arduino community forums.
Debug Helper
Paste this function into your sketch and call it from the joystick callback. It prints the raw pair, the distance from the middle and the heading, so you see exactly what the app sends.
Troubleshooting
| Problem | Possible Cause | Solution |
|---|---|---|
| No COM port in Arduino IDE | CH340 driver not installed or a charge-only cable | Install the CH340 driver and use a USB cable that carries data |
| Upload stops at "Failed to connect" or "Waiting for download" | Board did not enter download mode | Hold the BOOT button while the upload starts, then let go |
| "Sketch too big" when compiling | Default partition is too small for Bluetooth | Set Tools > Partition Scheme to Huge APP (3MB No OTA/1MB SPIFFS) |
| Board does not boot or upload with the motor driver attached | D8 (GPIO12) or D10 (GPIO5) strapping pin pulled the wrong way | Unplug the module while uploading, or use another pin |
| ESP32_Joystick missing on an iPhone | iOS has no Classic Bluetooth | Flash the Esp32BLE_Joystick sketch |
| App scan finds nothing | Sketch not running, phone Bluetooth off or a missing permission | Check for "Waiting for Bluetooth connection..." in the Serial Monitor, allow Nearby Devices, and turn on Location on Android 11 or older |
| Serial Monitor shows strange symbols | Baud rate does not match | Set the Serial Monitor to 115200 |
| Robot creeps when the stick is released | Auto-center off or no dead zone | Call setAutoReturn(true) and add a dead zone filter |
| Motors keep running after the phone walks away | No stop in the disconnect event | Stop all motors inside setOnDisconnected() |
| Board resets when the motors start | Motors pull current from the board | Use a separate motor battery and connect all GND wires together |
FAQ
Which Bluetooth sketch should I flash on the ESP32 Uno, BLE or Classic?
Flash Esp32BLE_Joystick if anyone will use an iPhone, or if you do not want a pairing step. Flash Esp32Bluetooth_Joystick if all phones are Android and you prefer Classic Bluetooth. Both give the same X/Y values to your code.
What numbers does the phone joystick send?
Two integers, X and Y, each from -100 to +100. The middle is (0, 0). Right and up are positive; left and down are negative.
How do I stop the joystick from sending too many updates?
Raise the sensitivity with setSensitivity(10.0) or higher. The app then sends a new value only after the stick moves more than that percent. A dead zone in your callback also helps.
Can I make the stick spring back to the middle?
Yes. Call setAutoReturn(true), or create the object with DIYables_BluetoothJoystick bluetoothJoystick(true, 5);. When you lift your finger, the app sends (0, 0), which is a safe stop for a robot.
Can the ESP32 Uno drive a 5V motor driver like the L298N?
Usually yes. The L298N inputs read the 3.3V output of the ESP32 Uno as HIGH. Power the motors from their own battery and share GND. Any 5V signal going into the board, such as an encoder output, needs a level shifter or a voltage divider first.
Why does the servo example need ESP32Servo instead of Servo.h?
The standard Servo.h library does not support the ESP32 Uno chip. Install "ESP32Servo" by Kevin Harrington; it keeps the same attach() and write() calls, so the rest of the code stays the same.
Is the UNO R4 joystick sketch the same as the ESP32 Uno sketch?
The callback code is the same. The setup differs: the UNO R4 WiFi uses DIYables_ArduinoBLE.h and 9600 baud, while the ESP32 Uno uses DIYables_Esp32BLE.h or DIYables_Esp32Bluetooth.h and 115200 baud. The ESP32 Uno also needs the Huge APP partition scheme.
Why do D8 and D10 cause boot problems on the ESP32 Uno?
D8 is GPIO12 and D10 is GPIO5. The chip reads both at power-on to pick its boot mode and flash voltage. If a motor driver or servo pulls them the wrong way, the board will not start or upload. Unplug those wires while you upload.
Can I steer two ESP32 Uno robots from two phones at once?
Yes. Give each board its own name in the sketch, such as "Robot_A" and "Robot_B". Each phone then connects to its own robot. Keep the same UUIDs; only the name has to differ.
What if my project needs more pins than the Uno headers give on the ESP32 Uno?
The ESP32 Uno has a few extra holes near the power header: GPIO32 and GPIO33 (I/O, PWM, analog on ADC1, so they also work while WiFi is on) and GPIO15 (I/O, PWM, analog on ADC2). Solder pin headers and write the GPIO number in code. For a robot, GPIO32 and GPIO33 are good spots for extra direction pins or a battery voltage sensor. 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/33 first. Never use the six holes under the digital header (SD0–SD3, CMD, CLK = GPIO6–GPIO11); they go to the flash chip, and using them crashes the board.
Can I control the same robot from a web page instead of Bluetooth?
Yes. The ESP32 Uno also has WiFi. The ESP32 Uno - Web Joystick page shows a browser joystick that sends the same kind of X/Y values.