DIYables Web Apps Web Joystick

Overview

This ESP32 S3 Uno web joystick tutorial shows you how to put a virtual joystick in any web browser and send its position to the ESP32 S3 Uno form board over WiFi. You move the stick with a mouse or a finger, and the board receives X/Y values from -100 to +100 in real time. This makes it a simple robot control interface for cars, pan/tilt heads or any project that needs 2D input.

In this tutorial, you will:

  1. Install the DIYables ESP32 WebApps library in the Arduino IDE
  2. Run the WebJoystick example and open the joystick page on your phone or PC
  3. Read X/Y joystick coordinates in your own code
  4. Use the virtual joystick to drive motors, servos and LEDs
  5. Add a dead zone and speed ramping for smooth control
ESP32 S3 Uno web joystick

Features

The virtual joystick runs fully inside the browser, so you do not need a physical joystick module. The ESP32 S3 Uno serves the page and gets every movement through a WebSocket connection.

Real-time coordinates

The stick sends X and Y values from -100 to +100. The page also shows the current position and a clear center mark for the neutral point.

Works on any device

You can use a mouse on a desktop, or touch on a tablet or phone. No app is needed, only a web browser.

Auto-return and sensitivity

You can choose if the stick jumps back to the center when you let go. A sensitivity setting stops the page from sending tiny changes, so the network is not flooded with updates.

Instant response

WebSocket keeps one open connection, so values arrive without a page refresh.

Hardware Preparation

1×ESP32 S3 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)

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 .

Setup Instructions

The basic example needs only the board and a USB cable. All the work happens in software: you install one library, add your WiFi details, and upload the sketch.

Detailed Instructions

Follow these steps in order:

  1. New to the ESP32 S3 Uno? Follow ESP32 S3 Uno - Getting Started first.
  2. Connect the board to your computer with a USB Type-C cable.
  3. Open Arduino IDE, choose the ESP32S3 Dev Module board and the correct COM port.
  4. Open the Library Manager by clicking the Libraries icon on the left bar of the Arduino IDE.
  5. Search the library: type "DIYables ESP32 WebApps" and find the DIYables ESP32 WebApps Library by DIYables.
  6. Install it by clicking the Install button.
  • Search for DIYables ESP32 WebApps created by DIYables and click the Install button.
∞
Newbiely | Arduino IDE 2.3.8
──
☐
✕
File
Edit
Sketch
Tools
Help
ESP32S3 Dev Module
Library Manager
Type:
All
Topic:
All
DIYables ESP32 WebApps by DIYables
A comprehensive library designed for ESP32 that provides multiple professional web applications including Web Monitor, Chat, Digital Pin Control, Sliders, Joystick, Analog Gauge, Rotator Control, and Temperature Display via WebSocket communication. Features modular architecture for memory efficiency, automatic config handling, and perfect for IoT projects, robotics, sensor monitoring, servo/stepper control, temperature monitoring, and remote ESP32 control. More info
1.0.1
INSTALL
Newbiely.ino
···
1 void setup() {
Output
Serial Monitor
Ln 1, Col 1
ESP32S3 Dev Module on COM15
1
  1. Install the dependencies: when the IDE asks about other libraries, click Install All.
  2. Open the example: go to File Examples DIYables ESP32 WebApps WebJoystick, or copy the code below into the Arduino IDE editor.
/* * This ESP32 S3 Uno code was developed by newbiely.com * * This ESP32 S3 Uno code is made available for public use without any restriction * * For comprehensive instructions and wiring diagrams, please visit: * https://newbiely.com/tutorials/esp32-s3-uno/diyables-web-apps-web-joystick */ /* * DIYables WebApp Library - Web Joystick Example * * This example demonstrates the Web Joystick feature: * - Interactive joystick control via web interface * - Real-time X/Y coordinate values (-100 to +100) * - Control pins based on joystick position * * Hardware: ESP32 S3 Uno * * Setup: * 1. Update WiFi credentials below * 2. Upload the sketch to your ESP32 S3 Uno * 3. Open Serial Monitor to see the IP address * 4. Navigate to http://[IP_ADDRESS]/webjoystick */ #include <DIYables_ESP32_Platform.h> #include <DIYablesWebApps.h> // WiFi credentials - UPDATE THESE WITH YOUR NETWORK const char WIFI_SSID[] = "YOUR_WIFI_SSID"; const char WIFI_PASSWORD[] = "YOUR_WIFI_PASSWORD"; // Create WebApp server and page instances // MEMORY SAFETY FIX: Use static factory to avoid stack object lifetime issues static ESP32ServerFactory serverFactory; // Static ensures lifetime matches program DIYablesWebAppServer webAppsServer(serverFactory, 80, 81); DIYablesHomePage homePage; // Configure joystick with autoReturn=false and sensitivity=5 (minimum 5% change to trigger updates) DIYablesWebJoystickPage webJoystickPage(false, 5); // Variables to store current joystick values int currentJoystickX = 0; int currentJoystickY = 0; void setup() { Serial.begin(9600); delay(1000); // TODO: initialize your hardware pins here Serial.println("DIYables ESP32 WebApp - Web Joystick Example"); // Add home and web joystick pages webAppsServer.addApp(&homePage); webAppsServer.addApp(&webJoystickPage); // Optional: Add 404 page for better user experience webAppsServer.setNotFoundPage(DIYablesNotFoundPage()); // Start the WebApp server if (!webAppsServer.begin(WIFI_SSID, WIFI_PASSWORD)) { while (1) { Serial.println("Failed to start WebApp server!"); delay(1000); } } // Set up joystick callback for position changes webJoystickPage.onJoystickValueFromWeb([](int x, int y) { // Store the received values currentJoystickX = x; currentJoystickY = y; // Print joystick position values (-100 to +100) Serial.println("Joystick - X: " + String(x) + ", Y: " + String(y)); // TODO: Add your control logic here based on joystick position // Examples: // - Control motors: if (x > 50) { /* move right */ } // - Control servos: servo.write(map(y, -100, 100, 0, 180)); // - Control LEDs: analogWrite(LED_PIN, map(abs(x), 0, 100, 0, 255)); // - Send commands to other devices via Serial, I2C, SPI, etc. }); // Optional: Handle requests for current joystick values (when web page loads) webJoystickPage.onJoystickValueToWeb([]() { // Send the stored joystick values back to the web client webJoystickPage.sendToWebJoystick(currentJoystickX, currentJoystickY); Serial.println("Web client requested values - Sent to Web: X=" + String(currentJoystickX) + ", Y=" + String(currentJoystickY)); }); // You can change configuration at runtime: // webJoystickPage.setAutoReturn(false); // Disable auto-return // webJoystickPage.setSensitivity(10.0); // Only send updates when joystick moves >10% (less sensitive) } void loop() { // Handle WebApp server communications webAppsServer.loop(); // TODO: Add your main application code here delay(10); }

The sketch starts a web server on port 80 and a WebSocket server on port 81, then adds a home page and the joystick page. Each time you move the stick, the callback stores the new X/Y values and prints them to the Serial Monitor. When a browser opens the page, the board sends the last stored position back, so the stick shows the right place.

  1. Add your WiFi details by changing these two lines:
const char WIFI_SSID[] = "YOUR_WIFI_SSID"; const char WIFI_PASSWORD[] = "YOUR_WIFI_PASSWORD";
  1. Upload the code: click the Upload button to send the sketch to the ESP32 S3 Uno.
  2. Open the Serial Monitor. You should see something like this:
∞
Newbiely | Arduino IDE 2.3.8
──
☐
✕
File
Edit
Sketch
Tools
Help
ESP32S3 Dev Module
Newbiely.ino
···
8 Serial.println("Hello World!");
Output
Serial Monitor
Message (Enter to send message to 'ESP32S3 Dev Module' on 'COM15')
New Line
9600 baud
DIYables ESP32 WebApp - Web Joystick Example INFO: Added app / INFO: Added app /web-joystick DIYables WebApp Library Platform: ESP32 Network connected! IP address: 192.168.0.2 HTTP server started on port 80 Configuring WebSocket server callbacks... WebSocket server started on port 81 WebSocket URL: ws://192.168.0.2:81 WebSocket server started on port 81 ========================================== DIYables WebApp Ready! ========================================== 📱 Web Interface: http://192.168.0.2 🔗 WebSocket: ws://192.168.0.2:81 📋 Available Applications: 🏠 Home Page: http://192.168.0.2/ 🕹️ Web Joystick: http://192.168.0.2/web-joystick ==========================================
Ln 11, Col 1
ESP32S3 Dev Module on COM15
2
  1. No output? Press the RESET button on the board and watch the Serial Monitor again.
  2. Open the web page: write down the IP address, then type it into the address bar of a browser on your phone or PC, for example http://192.168.0.2. The home page looks like this:
ESP32 S3 Uno DIYables WebApp Home page with Web Joystick app
  1. Open the joystick: click the Web Joystick link. You will see the joystick app:
ESP32 S3 Uno DIYables WebApp Web Joystick app
  1. Go there directly if you like: add /web-joystick after the IP address, for example http://192.168.0.2/web-joystick.
  2. Test it: click and drag (or touch and drag) the joystick, and watch the X/Y values from -100 to +100 appear in the Serial Monitor.
  3. Tip: your phone and the ESP32 S3 Uno must be on the same WiFi network. The ESP32 S3 Uno only supports 2.4 GHz WiFi, not 5 GHz.

Creative Customization - Adapt the Code to Your Project

Every project needs a different joystick feel. A robot car often wants the stick to snap back to center, while a camera head may want it to stay where you left it.

2. Configure Joystick Settings

You set the behavior when you create the joystick object. There are two settings: auto-return and sensitivity.

Basic Configuration

With no parameters, the stick returns to center on release and sends an update for every 1% of movement.

// Create joystick with default settings (autoReturn=true, sensitivity=1) DIYablesWebJoystickPage webJoystickPage;

Advanced Configuration

Here the stick stays at its last position, and the page only sends a new value when the stick moves more than 5%. This is the setting used in the example sketch.

// Create joystick with custom settings // autoReturn=false: Joystick stays at last position when released // sensitivity=5: Only send updates when movement > 5% DIYablesWebJoystickPage webJoystickPage(false, 5);

How to Use the Joystick

It helps to know how the page reads your input and what numbers it sends. Then you can turn those numbers into speed, angle or brightness with confidence.

Web Interface Controls

The page has a round joystick pad that takes touch or mouse input. A position marker shows where the stick is, a center point shows the neutral spot, and a small display shows the live X/Y values from -100 to +100.

Operating the Joystick

Desktop (Mouse Control)

  1. Click the stick and drag it to move.
  2. Let go of the mouse button. The stick goes back to center if autoReturn is true.
  3. Click anywhere on the pad to jump the stick to that spot.

Mobile/Tablet (Touch Control)

  1. Touch the stick and drag your finger.
  2. Use a single finger for the most precise control.
  3. Lift your finger. The stick returns to center if auto-return is on.

Coordinate System

The values follow a normal X/Y graph. The X-axis goes from -100 (full left) to +100 (full right). The Y-axis goes from -100 (full down) to +100 (full up). The center is X=0, Y=0. When you push the stick to a corner, you get both X and Y values at the same time.

ESP32 S3 Uno Pinout

The image below shows the pinout of the ESP32 S3 Uno form board. Use it to find the Uno header pins (D2, D9, D13…) used in the examples below and their GPIO numbers.

ESP32 S3 Uno pinout diagram

Programming Examples

These snippets show how to turn joystick values into real actions. They use the same Uno header positions as the classic Arduino examples, and the code uses the matching ESP32 S3 Uno GPIO numbers.

Uno Header ESP32 S3 Uno GPIO Used For
D2 GPIO18 Motor direction / LED up
D3 GPIO17 Motor direction / LED down
D4 GPIO19 Motor direction / LED left
D5 GPIO20 Motor direction / LED right
D9 GPIO46 Motor PWM / pan servo / red LED
D10 GPIO10 Motor PWM / tilt servo / green LED
D11 GPIO11 Blue LED
D13 GPIO12 Center LED

WARNING

D9 (GPIO46) is a boot strapping pin on the ESP32 S3 Uno. The board reads it at startup to pick the boot mode. If a motor driver or servo pulls this pin HIGH while the board powers up or resets, the board may not boot or may fail to upload code. If you have trouble, disconnect the wire from D9 during upload and reset, then connect it again.

Basic Joystick Handler

This is the smallest handler. It saves the latest X/Y values and prints them, so you can add your own control logic below.

void setup() { // Set up joystick callback for position changes webJoystickPage.onJoystickValueFromWeb([](int x, int y) { // Store the received values currentJoystickX = x; currentJoystickY = y; // Print joystick position values Serial.println("Joystick - X: " + String(x) + ", Y: " + String(y)); // Add your control logic here }); }

Motor Control Example

This example drives two DC motors through a motor driver. Y sets forward or backward speed, and X adds a turn by making one side faster than the other.

// Pin definitions for motor driver (ESP32 S3 Uno GPIO numbers) const int MOTOR_LEFT_PIN1 = 18; // D2 (GPIO18) const int MOTOR_LEFT_PIN2 = 17; // D3 (GPIO17) const int MOTOR_RIGHT_PIN1 = 19; // D4 (GPIO19) const int MOTOR_RIGHT_PIN2 = 20; // D5 (GPIO20) const int MOTOR_LEFT_PWM = 46; // D9 (GPIO46) const int MOTOR_RIGHT_PWM = 10; // D10 (GPIO10) void setup() { // Configure motor pins pinMode(MOTOR_LEFT_PIN1, OUTPUT); pinMode(MOTOR_LEFT_PIN2, OUTPUT); pinMode(MOTOR_RIGHT_PIN1, OUTPUT); pinMode(MOTOR_RIGHT_PIN2, OUTPUT); pinMode(MOTOR_LEFT_PWM, OUTPUT); pinMode(MOTOR_RIGHT_PWM, OUTPUT); webJoystickPage.onJoystickValueFromWeb([](int x, int y) { controlRobot(x, y); }); } void controlRobot(int x, int y) { // Convert joystick coordinates to motor speeds int leftSpeed = y + x; // Forward/backward + turn left/right int rightSpeed = y - x; // Forward/backward - turn left/right // Constrain speeds to valid range leftSpeed = constrain(leftSpeed, -100, 100); rightSpeed = constrain(rightSpeed, -100, 100); // Control left motor if (leftSpeed > 0) { digitalWrite(MOTOR_LEFT_PIN1, HIGH); digitalWrite(MOTOR_LEFT_PIN2, LOW); analogWrite(MOTOR_LEFT_PWM, map(leftSpeed, 0, 100, 0, 255)); } else if (leftSpeed < 0) { digitalWrite(MOTOR_LEFT_PIN1, LOW); digitalWrite(MOTOR_LEFT_PIN2, HIGH); analogWrite(MOTOR_LEFT_PWM, map(-leftSpeed, 0, 100, 0, 255)); } else { digitalWrite(MOTOR_LEFT_PIN1, LOW); digitalWrite(MOTOR_LEFT_PIN2, LOW); analogWrite(MOTOR_LEFT_PWM, 0); } // Control right motor if (rightSpeed > 0) { digitalWrite(MOTOR_RIGHT_PIN1, HIGH); digitalWrite(MOTOR_RIGHT_PIN2, LOW); analogWrite(MOTOR_RIGHT_PWM, map(rightSpeed, 0, 100, 0, 255)); } else if (rightSpeed < 0) { digitalWrite(MOTOR_RIGHT_PIN1, LOW); digitalWrite(MOTOR_RIGHT_PIN2, HIGH); analogWrite(MOTOR_RIGHT_PWM, map(-rightSpeed, 0, 100, 0, 255)); } else { digitalWrite(MOTOR_RIGHT_PIN1, LOW); digitalWrite(MOTOR_RIGHT_PIN2, LOW); analogWrite(MOTOR_RIGHT_PWM, 0); } }

Servo Control Example

Two servos make a pan/tilt head. X turns the pan servo and Y moves the tilt servo. On the ESP32 S3 Uno, use the ESP32Servo library (by Kevin Harrington) instead of Servo.h.

#include <ESP32Servo.h> Servo panServo; // X-axis control (left/right) Servo tiltServo; // Y-axis control (up/down) void setup() { // Attach servos to pins panServo.attach(46); // D9 (GPIO46) tiltServo.attach(10); // D10 (GPIO10) // Center servos initially panServo.write(90); tiltServo.write(90); webJoystickPage.onJoystickValueFromWeb([](int x, int y) { controlServos(x, y); }); } void controlServos(int x, int y) { // Map joystick coordinates to servo angles int panAngle = map(x, -100, 100, 0, 180); // X controls pan (0-180°) int tiltAngle = map(y, -100, 100, 180, 0); // Y controls tilt (inverted) // Move servos to calculated positions panServo.write(panAngle); tiltServo.write(tiltAngle); Serial.println("Pan: " + String(panAngle) + "°, Tilt: " + String(tiltAngle) + "°"); }

LED Position Indicator

Five LEDs show the stick direction. The center LED lights when the stick is near the middle, and the other four light when you push past 20 in that direction.

// LED pins for position indication (ESP32 S3 Uno GPIO numbers) const int LED_UP = 18; // D2 (GPIO18) const int LED_DOWN = 17; // D3 (GPIO17) const int LED_LEFT = 19; // D4 (GPIO19) const int LED_RIGHT = 20; // D5 (GPIO20) const int LED_CENTER = 12; // D13 (GPIO12) void setup() { // Configure LED pins pinMode(LED_UP, OUTPUT); pinMode(LED_DOWN, OUTPUT); pinMode(LED_LEFT, OUTPUT); pinMode(LED_RIGHT, OUTPUT); pinMode(LED_CENTER, OUTPUT); webJoystickPage.onJoystickValueFromWeb([](int x, int y) { updateLEDIndicators(x, y); }); } void updateLEDIndicators(int x, int y) { // Turn off all LEDs first digitalWrite(LED_UP, LOW); digitalWrite(LED_DOWN, LOW); digitalWrite(LED_LEFT, LOW); digitalWrite(LED_RIGHT, LOW); digitalWrite(LED_CENTER, LOW); // Check if joystick is near center if (abs(x) < 10 && abs(y) < 10) { digitalWrite(LED_CENTER, HIGH); return; } // Activate LEDs based on direction if (y > 20) digitalWrite(LED_UP, HIGH); if (y < -20) digitalWrite(LED_DOWN, HIGH); if (x > 20) digitalWrite(LED_RIGHT, HIGH); if (x < -20) digitalWrite(LED_LEFT, HIGH); }

Advanced Configuration

Once the basics work, you can change the joystick behavior while the program runs and make the control feel smoother.

Runtime Configuration Changes

You can turn auto-return off and change the sensitivity at any time. This example switches to a "precision mode" when you push the stick hard into a corner.

void setup() { // Initial setup with default values webJoystickPage.onJoystickValueFromWeb([](int x, int y) { handleJoystickInput(x, y); }); // Change settings at runtime webJoystickPage.setAutoReturn(false); // Disable auto-return webJoystickPage.setSensitivity(10.0); // Reduce sensitivity } void handleJoystickInput(int x, int y) { // Handle different modes based on current settings static bool precisionMode = false; // Toggle precision mode with extreme positions if (abs(x) > 95 && abs(y) > 95) { precisionMode = !precisionMode; if (precisionMode) { webJoystickPage.setSensitivity(1.0); // High sensitivity Serial.println("Precision mode ON"); } else { webJoystickPage.setSensitivity(10.0); // Low sensitivity Serial.println("Precision mode OFF"); } } }

Dead Zone Implementation

A finger is never perfectly still, so small values near the center can make a robot creep. A dead zone treats anything under 15% as zero, then scales the rest back to the full 0–100 range.

void processJoystickWithDeadZone(int x, int y) { const int DEAD_ZONE = 15; // 15% dead zone around center // Apply dead zone filtering int filteredX = (abs(x) < DEAD_ZONE) ? 0 : x; int filteredY = (abs(y) < DEAD_ZONE) ? 0 : y; // Scale values outside dead zone if (filteredX != 0) { filteredX = map(abs(filteredX), DEAD_ZONE, 100, 0, 100); filteredX = (x < 0) ? -filteredX : filteredX; } if (filteredY != 0) { filteredY = map(abs(filteredY), DEAD_ZONE, 100, 0, 100); filteredY = (y < 0) ? -filteredY : filteredY; } // Use filtered values for control controlDevice(filteredX, filteredY); }

Speed Ramping

Sudden jumps from 0 to full speed are hard on motors and gears. This class moves the output toward the target by 5 steps every 20 ms, so speed changes are smooth. Call update() in loop().

class SpeedController { private: int targetX = 0, targetY = 0; int currentX = 0, currentY = 0; unsigned long lastUpdate = 0; const int RAMP_RATE = 5; // Change per update cycle public: void setTarget(int x, int y) { targetX = x; targetY = y; } void update() { if (millis() - lastUpdate > 20) { // Update every 20ms // Ramp X value if (currentX < targetX) { currentX = min(currentX + RAMP_RATE, targetX); } else if (currentX > targetX) { currentX = max(currentX - RAMP_RATE, targetX); } // Ramp Y value if (currentY < targetY) { currentY = min(currentY + RAMP_RATE, targetY); } else if (currentY > targetY) { currentY = max(currentY - RAMP_RATE, targetY); } // Apply ramped values applyControlValues(currentX, currentY); lastUpdate = millis(); } } void applyControlValues(int x, int y) { // Your control logic here with smooth ramped values Serial.println("Ramped - X: " + String(x) + ", Y: " + String(y)); } }; SpeedController speedController; void setup() { webJoystickPage.onJoystickValueFromWeb([](int x, int y) { speedController.setTarget(x, y); }); } void loop() { webAppsServer.loop(); speedController.update(); // Apply speed ramping }

Hardware Integration Examples

These examples connect the joystick to real hardware. The pins match the table in the Programming Examples section, so remember the D9 (GPIO46) boot pin warning there.

Robot Car Control

This is a tank-drive robot car. Y sets the driving speed and half of X is used for steering, which makes turns gentler than in the motor example above.

void setupRobotCar() { // Motor driver pins (ESP32 S3 Uno GPIO numbers) pinMode(18, OUTPUT); // D2 (GPIO18) - Left motor direction 1 pinMode(17, OUTPUT); // D3 (GPIO17) - Left motor direction 2 pinMode(19, OUTPUT); // D4 (GPIO19) - Right motor direction 1 pinMode(20, OUTPUT); // D5 (GPIO20) - Right motor direction 2 pinMode(46, OUTPUT); // D9 (GPIO46) - Left motor PWM pinMode(10, OUTPUT); // D10 (GPIO10) - Right motor PWM webJoystickPage.onJoystickValueFromWeb([](int x, int y) { // Tank drive calculation int leftMotor = y + (x / 2); // Forward/back + steering int rightMotor = y - (x / 2); // Forward/back - steering // Constrain to valid range leftMotor = constrain(leftMotor, -100, 100); rightMotor = constrain(rightMotor, -100, 100); // Control motors setMotorSpeed(46, 18, 17, leftMotor); // Left motor setMotorSpeed(10, 19, 20, rightMotor); // Right motor }); } void setMotorSpeed(int pwmPin, int dir1Pin, int dir2Pin, int speed) { if (speed > 0) { digitalWrite(dir1Pin, HIGH); digitalWrite(dir2Pin, LOW); analogWrite(pwmPin, map(speed, 0, 100, 0, 255)); } else if (speed < 0) { digitalWrite(dir1Pin, LOW); digitalWrite(dir2Pin, HIGH); analogWrite(pwmPin, map(-speed, 0, 100, 0, 255)); } else { digitalWrite(dir1Pin, LOW); digitalWrite(dir2Pin, LOW); analogWrite(pwmPin, 0); } }

Camera Gimbal Control

A camera gimbal needs a limited range around the center. Here pan moves ±45° and tilt moves ±30° from 90°, and the result is kept inside 0–180°.

#include <ESP32Servo.h> Servo panServo, tiltServo; int panOffset = 90, tiltOffset = 90; // Center positions void setupCameraGimbal() { panServo.attach(46); // D9 (GPIO46) tiltServo.attach(10); // D10 (GPIO10) // Set initial center positions panServo.write(panOffset); tiltServo.write(tiltOffset); webJoystickPage.onJoystickValueFromWeb([](int x, int y) { // Calculate servo positions with offset int panPos = panOffset + map(x, -100, 100, -45, 45); // ±45° range int tiltPos = tiltOffset + map(y, -100, 100, -30, 30); // ±30° range // Constrain to servo limits panPos = constrain(panPos, 0, 180); tiltPos = constrain(tiltPos, 0, 180); // Move servos smoothly panServo.write(panPos); tiltServo.write(tiltPos); }); }

RGB LED Color Control

The stick position picks a color on an RGB LED. Each quadrant mixes a different pair of colors, and how far you push sets the brightness.

const int RED_PIN = 46; // D9 (GPIO46) const int GREEN_PIN = 10; // D10 (GPIO10) const int BLUE_PIN = 11; // D11 (GPIO11) void setupRGBControl() { pinMode(RED_PIN, OUTPUT); pinMode(GREEN_PIN, OUTPUT); pinMode(BLUE_PIN, OUTPUT); webJoystickPage.onJoystickValueFromWeb([](int x, int y) { // Convert joystick position to RGB values int red = map(abs(x), 0, 100, 0, 255); int green = map(abs(y), 0, 100, 0, 255); int blue = map(abs(x + y), 0, 141, 0, 255); // Diagonal distance // Apply quadrant-specific color mixing if (x > 0 && y > 0) { // Upper right: Red + Green = Yellow blue = 0; } else if (x < 0 && y > 0) { // Upper left: Green + Blue = Cyan red = 0; } else if (x < 0 && y < 0) { // Lower left: Blue + Red = Magenta green = 0; } else if (x > 0 && y < 0) { // Lower right: Red only green = blue = 0; } // Set RGB values analogWrite(RED_PIN, red); analogWrite(GREEN_PIN, green); analogWrite(BLUE_PIN, blue); }); }

Common Issues and Debug Tips

Most joystick problems come from the network or from the settings, not from the board. Check these first before you change your code.

Common Issues

Joystick does not respond

Open the browser console and check that the WebSocket is connected. Make sure the phone and the board are on the same network, refresh the page, and look at the Serial Monitor for error messages.

Movement is jerky or uneven

Raise the sensitivity value so the page sends fewer updates. You can also add the dead zone and speed ramping shown above. A slow or busy WiFi network can cause this too.

Auto-return does not work

Check that auto-return is on with webJoystickPage.setAutoReturn(true). Some touch devices act differently, so try both a mouse and a touch screen.

Values do not reach the full range

Drag the stick all the way to the edge of the pad. Then check the math in your callback, and try another browser or device.

Debug Tips

This function prints the X/Y values, their percentage, the distance from center and the angle. It is a quick way to see what the joystick really sends.

void debugJoystickState(int x, int y) { Serial.println("=== Joystick Debug ==="); Serial.println("X: " + String(x) + " (" + String(map(x, -100, 100, 0, 100)) + "%)"); Serial.println("Y: " + String(y) + " (" + String(map(y, -100, 100, 0, 100)) + "%)"); Serial.println("Distance from center: " + String(sqrt(x*x + y*y))); Serial.println("Angle: " + String(atan2(y, x) * 180 / PI) + "°"); Serial.println("====================="); }

Project Ideas

A web joystick fits any project where you want to move something in two directions from your phone. Here are some ideas to get you started.

Robotics

Build a remote-controlled robot car, steer a robotic arm, try basic drone movements, or drive a small pet robot around the room.

Home Automation

Set the position of smart curtains, aim a pan/tilt camera, change light brightness and color, or control fan speed and direction.

Education

Teach the X/Y coordinate system, show how motors and servos respond to input, or build a simple game controller.

Art and Creative

Control LED patterns, music visuals, a drawing robot or an interactive art piece.

Integration with Other Examples

The joystick works well together with other DIYables ESP32 WebApps pages. You can add several apps to the same server.

Combine with WebSlider

Use the joystick for direction and a WebSlider to set the top speed:

// Use joystick for direction, sliders for speed limits webJoystickPage.onJoystickValueFromWeb([](int x, int y) { int maxSpeed = getSliderValue(); // From WebSlider int scaledX = map(x, -100, 100, -maxSpeed, maxSpeed); int scaledY = map(y, -100, 100, -maxSpeed, maxSpeed); controlRobot(scaledX, scaledY); });

Combine with WebDigitalPins

Turn WebDigitalPins on or off based on which direction you push the stick:

webJoystickPage.onJoystickValueFromWeb([](int x, int y) { // Activate pins based on joystick quadrants (ESP32 S3 Uno GPIO numbers) webDigitalPinsPage.setPinState(18, x > 50); // D2 (GPIO18) - Right quadrant webDigitalPinsPage.setPinState(17, x < -50); // D3 (GPIO17) - Left quadrant webDigitalPinsPage.setPinState(19, y > 50); // D4 (GPIO19) - Upper quadrant webDigitalPinsPage.setPinState(20, y < -50); // D5 (GPIO20) - Lower quadrant });

Next Steps

When the joystick works well, try these examples next:

  1. WebSlider - add more analog control
  2. WebDigitalPins - simple on/off control
  3. WebMonitor - debug joystick values in the browser
  4. MultipleWebApps - put the joystick and other controls on one board

Support

If you need more help, read the library reference and the DIYables ESP32 WebApps overview. You can also ask questions in the Arduino community forums.

FAQ

Do I need a physical joystick module for this ESP32 S3 Uno project?

No. The joystick is drawn on a web page, and the ESP32 S3 Uno gets its position over WiFi. You only need the board, a USB cable, and a phone or PC on the same network.

Can I use the UNO R4 "DIYables WebApps" library on the ESP32 S3 Uno?

No. The ESP32 S3 Uno needs the "DIYables ESP32 WebApps" library and the DIYables_ESP32_Platform.h include. The page classes and callbacks, like DIYablesWebJoystickPage and onJoystickValueFromWeb(), work the same way, so most code moves over with only pin changes.

Which pins should I use for motors and servos on the ESP32 S3 Uno?

Almost all Uno header pins support PWM, so analogWrite() and servos work on them. Use the GPIO number in code, for example D2 is 18 and D10 is 10. D9 (GPIO46) and D6 (GPIO3) are boot strapping pins, so a motor driver or servo on them can block booting or uploading. Also use the ESP32Servo library instead of Servo.h.

Can I drive a 5V motor driver from the ESP32 S3 Uno?

Yes, most motor drivers like the L298N accept 3.3V logic signals on their inputs. The ESP32 S3 Uno pins are 3.3V and not 5V tolerant, so never send a 5V signal back into a board pin. Power the motors from a separate supply and connect all grounds together.

What if my robot needs more pins than the Uno headers give?

The ESP32 S3 Uno has two extra rows of holes with more GPIOs. GPIO15 and GPIO16 support I/O, PWM and analog. GPIO45 and GPIO35–GPIO42 support I/O and PWM. GPIO47 and GPIO48 are output only with PWM. Solder pin headers and write the GPIO number in code. For extra motor direction pins or LEDs, GPIO35–GPIO42 are a good choice. Be careful with two things. First, GPIO47 and GPIO48 may run at 1.8V instead of 3.3V on some modules (especially ones with "V" in the name, like R8V or R16V), so 3.3V devices can be damaged or act strangely. Second, GPIO0, GPIO3 (D6), GPIO45 and GPIO46 (D9) are boot strapping pins, and a wrong connection can stop the board from booting or uploading code. Use the other extra pins first.

Can I control the joystick from outside my home network?

Not directly. The web page is only reachable on your local network. To use it from the internet you would need port forwarding or a VPN, which also brings security risks.

Troubleshooting

Problem Possible Cause Solution
No COM port in Arduino IDE USB driver missing or board not in upload mode Install the CP210x or CH340 driver, try another USB cable, or hold BOOT while pressing RESET
Upload fails or board does not boot A motor driver or servo on D9 (GPIO46) holds the boot pin Unplug the D9 wire during upload and reset, then plug it back in
Serial Monitor shows "Failed to start WebApp server!" Wrong WiFi name or password, or a 5 GHz network Check WIFI_SSID and WIFI_PASSWORD and use a 2.4 GHz network
Page does not load at the IP address Phone or PC on a different network Connect both devices to the same WiFi and type the IP shown in the Serial Monitor
Joystick moves but nothing happens on the board WebSocket on port 81 is blocked Refresh the page, turn off VPN or data saver, and check the browser console
Motors or servos twitch or reset the board Motors powered from the board's 5V pin Use a separate power supply for motors and connect all GND together
Compile error about Servo.h The UNO servo library does not support the ESP32 S3 Uno Install ESP32Servo by Kevin Harrington and use #include
Garbled text in Serial Monitor Wrong baud rate Set the Serial Monitor to 9600 baud

※ 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!