ESP32 Bluetooth Joystick Example Hướng Dẫn Giao Diện Điều Khiển 2D Tương Tác
Ví dụ Bluetooth Joystick cung cấp bộ điều khiển joystick 2D tương tác có thể truy cập thông qua ứng dụng DIYables Bluetooth STEM. Được thiết kế cho board ESP32 với hỗ trợ cả BLE (Bluetooth Low Energy) và Classic Bluetooth. Joystick gửi giá trị tọa độ X và Y theo thời gian thực từ -100 đến +100, lý tưởng cho điều khiển robot, điều khiển động cơ, định vị servo và bất kỳ ứng dụng nào cần đầu vào hướng.
Ví dụ này hỗ trợ hai chế độ Bluetooth:
Điều Khiển 2D: Trục X và Y với giá trị từ -100 đến +100
Cập Nhật Thời Gian Thực: Cập nhật vị trí tức thì qua giao tiếp Bluetooth
Tùy Chọn Tự Động Về Tâm: Có thể cấu hình tự động về vị trí trung tâm (0, 0)
Độ Nhạy Điều Chỉnh: Cấu hình ngưỡng chuyển động tối thiểu để lọc các thay đổi nhỏ
Tương Thích Robot: Giá trị dễ dàng ánh xạ đến đầu vào driver động cơ
BLE & Classic Bluetooth: Chọn chế độ Bluetooth phù hợp với dự án của bạn
Đa Nền Tảng: Chế độ BLE hoạt động trên cả Android và iOS; Classic Bluetooth hoạt động trên Android
Tùy Chọn Tiết Kiệm Điện: Chế độ BLE tiêu thụ ít điện hơn Classic Bluetooth
| 1 | × | mô-đun phát triển ESP-WROOM-32 | | |
| 1 | × | Alternatively, ESP32 Uno-form board | | |
| 1 | × | Alternatively, 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) | | |
| 1 | × | breadboard | | |
| 1 | × | dây jumper | | |
| 1 | × | (Khuyến nghị) Screw Terminal Expansion Board for ESP32 | | |
| 1 | × | (Khuyến nghị) Breakout Expansion Board for ESP32 | | |
| 1 | × | (Khuyến nghị) Power Splitter for ESP32 | | |
Or you can buy the following kits:
| 1 | × | DIYables ESP32 Starter Kit (ESP32 included) | | |
| 1 | × | DIYables Sensor Kit (30 sensors/displays) | | |
| 1 | × | DIYables Sensor Kit (18 sensors/displays) | | |
Thực hiện theo hướng dẫn từng bước:
Kết nối board ESP32 với máy tính của bạn bằng cáp USB.
Mở Arduino IDE trên máy tính.
Chọn board ESP32 và cổng COM phù hợp.
Điều hướng đến biểu tượng Libraries ở thanh bên trái của Arduino IDE.
Tìm kiếm "DIYables Bluetooth", sau đó tìm thư viện DIYables Bluetooth của DIYables
Nhấp nút Install để cài đặt thư viện.
Chọn một trong hai chế độ Bluetooth dưới đây tùy thuộc vào nhu cầu của bạn:
Lưu ý: Classic Bluetooth KHÔNG được hỗ trợ trên iOS. Nếu bạn cần hỗ trợ iOS, hãy sử dụng code BLE dưới đây.
#include <DIYables_BluetoothServer.h>
#include <DIYables_BluetoothJoystick.h>
#include <platforms/DIYables_Esp32Bluetooth.h>
DIYables_Esp32Bluetooth bluetooth("ESP32_Joystick");
DIYables_BluetoothServer bluetoothServer(bluetooth);
DIYables_BluetoothJoystick bluetoothJoystick(false, 5);
int currentJoystickX = 0;
int currentJoystickY = 0;
void setup() {
Serial.begin(115200);
delay(1000);
Serial.println("DIYables Bluetooth - ESP32 Joystick Example");
bluetoothServer.begin();
bluetoothServer.addApp(&bluetoothJoystick);
bluetoothServer.setOnConnected([]() {
Serial.println("Bluetooth connected!");
});
bluetoothServer.setOnDisconnected([]() {
Serial.println("Bluetooth disconnected!");
});
bluetoothJoystick.onJoystickValue([](int x, int y) {
currentJoystickX = x;
currentJoystickY = y;
Serial.print("Joystick - X: ");
Serial.print(x);
Serial.print(", Y: ");
Serial.println(y);
}
});
bluetoothJoystick.onGetConfig([]() {
bluetoothJoystick.send(currentJoystickX, currentJoystickY);
Serial.print("App requested values - Sent: X=");
Serial.print(currentJoystickX);
Serial.print(", Y=");
Serial.println(currentJoystickY);
});
Serial.println("Waiting for Bluetooth connection...");
}
void loop() {
bluetoothServer.loop();
delay(10);
}
DIYables Bluetooth - ESP32 Joystick Example
Waiting for Bluetooth connection...
#include <DIYables_BluetoothServer.h>
#include <DIYables_BluetoothJoystick.h>
#include <platforms/DIYables_Esp32BLE.h>
const char* DEVICE_NAME = "ESP32BLE_Joystick";
const char* SERVICE_UUID = "19B10000-E8F2-537E-4F6C-D104768A1214";
const char* TX_UUID = "19B10001-E8F2-537E-4F6C-D104768A1214";
const char* RX_UUID = "19B10002-E8F2-537E-4F6C-D104768A1214";
DIYables_Esp32BLE bluetooth(DEVICE_NAME, SERVICE_UUID, TX_UUID, RX_UUID);
DIYables_BluetoothServer bluetoothServer(bluetooth);
DIYables_BluetoothJoystick bluetoothJoystick(false, 5);
int currentJoystickX = 0;
int currentJoystickY = 0;
void setup() {
Serial.begin(115200);
delay(1000);
Serial.println("DIYables Bluetooth - ESP32 BLE Joystick Example");
bluetoothServer.begin();
bluetoothServer.addApp(&bluetoothJoystick);
bluetoothServer.setOnConnected([]() {
Serial.println("Bluetooth connected!");
});
bluetoothServer.setOnDisconnected([]() {
Serial.println("Bluetooth disconnected!");
});
bluetoothJoystick.onJoystickValue([](int x, int y) {
currentJoystickX = x;
currentJoystickY = y;
Serial.print("Joystick - X: ");
Serial.print(x);
Serial.print(", Y: ");
Serial.println(y);
});
bluetoothJoystick.onGetConfig([]() {
bluetoothJoystick.send(currentJoystickX, currentJoystickY);
Serial.print("App requested values - Sent: X=");
Serial.print(currentJoystickX);
Serial.print(", Y=");
Serial.println(currentJoystickY);
});
Serial.println("Waiting for Bluetooth connection...");
}
void loop() {
bluetoothServer.loop();
delay(10);
}
DIYables Bluetooth - ESP32 BLE Joystick Example
Waiting for Bluetooth connection...
Cài đặt ứng dụng DIYables Bluetooth trên smartphone của bạn:
Android |
iOS
Nếu bạn đang sử dụng code ESP32 Classic Bluetooth, bạn cần ghép nối ESP32 với điện thoại Android trước khi mở ứng dụng:
Đi đến Cài Đặt > Bluetooth trên điện thoại
Đảm bảo Bluetooth đã được bật
Điện thoại của bạn sẽ quét các thiết bị có sẵn
Tìm và nhấn "ESP32_Joystick" trong danh sách thiết bị có sẵn
Xác nhận yêu cầu ghép nối (không cần PIN)
Đợi cho đến khi nó hiển thị "Paired" dưới tên thiết bị
Nếu bạn đang sử dụng code ESP32 BLE, không cần ghép nối. Chỉ cần tiến hành bước tiếp theo.
Mở ứng dụng DIYables Bluetooth
Khi mở ứng dụng lần đầu tiên, nó sẽ yêu cầu quyền. Vui lòng cấp các quyền sau:
Quyền Nearby Devices (Android 12+) / Bluetooth (iOS) - cần thiết để quét và kết nối với thiết bị Bluetooth
Quyền Location (chỉ Android 11 trở xuống) - được yêu cầu bởi các phiên bản Android cũ để quét thiết bị BLE
Đảm bảo Bluetooth đã được bật trên điện thoại của bạn
Trên màn hình chính, nhấn nút Connect. Ứng dụng sẽ quét cả thiết bị BLE và Classic Bluetooth.

Tìm và nhấn thiết bị của bạn trong kết quả quét để kết nối:
Sau khi kết nối, ứng dụng tự động quay về màn hình chính. Chọn ứng dụng Joystick từ menu ứng dụng.
Lưu ý: Bạn có thể nhấn biểu tượng cài đặt trên màn hình chính để ẩn/hiện các ứng dụng trên màn hình chính. Để biết thêm chi tiết, xem Hướng Dẫn Sử Dụng DIYables Bluetooth App.
Bây giờ hãy nhìn lại Serial Monitor trên Arduino IDE. Bạn sẽ thấy:
Bluetooth connected!
Joystick - X: 50, Y: 0
Joystick - X: 75, Y: -30
Joystick - X: 0, Y: 100
Joystick - X: -60, Y: 45
Joystick - X: 0, Y: 0
Thiết lập hành vi joystick với các tham số constructor:
DIYables_BluetoothJoystick bluetoothJoystick(false, 5);
bluetoothJoystick.setAutoReturn(true);
bluetoothJoystick.setSensitivity(10.0);
bool autoReturn = bluetoothJoystick.getAutoReturn();
float sensitivity = bluetoothJoystick.getSensitivity();
Sử dụng callback onJoystickValue() để nhận tọa độ X/Y:
bluetoothJoystick.onJoystickValue([](int x, int y) {
Serial.print("X: ");
Serial.print(x);
Serial.print(", Y: ");
Serial.println(y);
});
Khi ứng dụng kết nối và mở màn hình Joystick, nó yêu cầu cấu hình joystick từ ESP32. Bạn có thể sử dụng callback onGetConfig() để gửi giá trị joystick hiện tại cho ứng dụng tại thời điểm đó:
bluetoothJoystick.onGetConfig([]() {
bluetoothJoystick.send(currentJoystickX, currentJoystickY);
Serial.println("Ứng dụng yêu cầu cấu hình - đã gửi giá trị hiện tại");
});
Bạn có thể gửi giá trị tọa độ joystick từ ESP32 đến ứng dụng:
bluetoothJoystick.send(currentJoystickX, currentJoystickY);
bluetoothJoystick.send("Joystick đã được hiệu chỉnh");
Bạn có thể phát hiện khi ứng dụng kết nối hoặc ngắt kết nối với ESP32:
bluetoothServer.setOnConnected([]() {
Serial.println("Bluetooth đã kết nối!");
bluetoothJoystick.send(currentJoystickX, currentJoystickY);
});
bluetoothServer.setOnDisconnected([]() {
Serial.println("Bluetooth đã ngắt kết nối!");
stopAllMotors();
});
if (bluetoothServer.isConnected()) {
}
Giao diện joystick trong ứng dụng DIYables Bluetooth cung cấp:
Bảng Joystick Ảo: Chạm và kéo để điều khiển vị trí
Hiển Thị Giá Trị X: Hiển thị vị trí ngang hiện tại (-100 đến +100)
Hiển Thị Giá Trị Y: Hiển thị vị trí dọc hiện tại (-100 đến +100)
Joystick cung cấp:
Trục X: -100 (hoàn toàn trái) đến 0 (giữa) đến +100 (hoàn toàn phải)
Trục Y: -100 (hoàn toàn xuống) đến 0 (giữa) đến +100 (hoàn toàn lên)
Vị Trí Trung Tâm: (0, 0) khi joystick ở trạng thái nghỉ
void setup() {
bluetoothJoystick.onJoystickValue([](int x, int y) {
currentJoystickX = x;
currentJoystickY = y;
Serial.print("Joystick - X: ");
Serial.print(x);
Serial.print(", Y: ");
Serial.println(y);
});
}
const int MOTOR_LEFT_PWM = 16;
const int MOTOR_LEFT_DIR1 = 18;
const int MOTOR_LEFT_DIR2 = 19;
const int MOTOR_RIGHT_PWM = 17;
const int MOTOR_RIGHT_DIR1 = 21;
const int MOTOR_RIGHT_DIR2 = 22;
void setup() {
pinMode(MOTOR_LEFT_PWM, OUTPUT);
pinMode(MOTOR_LEFT_DIR1, OUTPUT);
pinMode(MOTOR_LEFT_DIR2, OUTPUT);
pinMode(MOTOR_RIGHT_PWM, OUTPUT);
pinMode(MOTOR_RIGHT_DIR1, OUTPUT);
pinMode(MOTOR_RIGHT_DIR2, OUTPUT);
bluetoothJoystick.onJoystickValue([](int x, int y) {
int leftSpeed = constrain(y + x, -100, 100);
int rightSpeed = constrain(y - x, -100, 100);
if (leftSpeed >= 0) {
digitalWrite(MOTOR_LEFT_DIR1, HIGH);
digitalWrite(MOTOR_LEFT_DIR2, LOW);
} else {
digitalWrite(MOTOR_LEFT_DIR1, LOW);
digitalWrite(MOTOR_LEFT_DIR2, HIGH);
}
analogWrite(MOTOR_LEFT_PWM, map(abs(leftSpeed), 0, 100, 0, 255));
if (rightSpeed >= 0) {
digitalWrite(MOTOR_RIGHT_DIR1, HIGH);
digitalWrite(MOTOR_RIGHT_DIR2, LOW);
} else {
digitalWrite(MOTOR_RIGHT_DIR1, LOW);
digitalWrite(MOTOR_RIGHT_DIR2, HIGH);
}
analogWrite(MOTOR_RIGHT_PWM, map(abs(rightSpeed), 0, 100, 0, 255));
Serial.print("Trái: ");
Serial.print(leftSpeed);
Serial.print("%, Phải: ");
Serial.print(rightSpeed);
Serial.println("%");
});
}
#include <ESP32Servo.h>
Servo panServo;
Servo tiltServo;
const int PAN_PIN = 13;
const int TILT_PIN = 14;
void setup() {
panServo.attach(PAN_PIN);
tiltServo.attach(TILT_PIN);
panServo.write(90);
tiltServo.write(90);
bluetoothJoystick.onJoystickValue([](int x, int y) {
int panAngle = map(x, -100, 100, 0, 180);
int tiltAngle = map(y, -100, 100, 0, 180);
panServo.write(panAngle);
tiltServo.write(tiltAngle);
Serial.print("Pan: ");
Serial.print(panAngle);
Serial.print("°, Tilt: ");
Serial.print(tiltAngle);
Serial.println("°");
});
}
const int LED_UP = 16;
const int LED_DOWN = 17;
const int LED_LEFT = 18;
const int LED_RIGHT = 19;
const int THRESHOLD = 30;
void setup() {
pinMode(LED_UP, OUTPUT);
pinMode(LED_DOWN, OUTPUT);
pinMode(LED_LEFT, OUTPUT);
pinMode(LED_RIGHT, OUTPUT);
bluetoothJoystick.onJoystickValue([](int x, int y) {
digitalWrite(LED_UP, y > THRESHOLD ? HIGH : LOW);
digitalWrite(LED_DOWN, y < -THRESHOLD ? HIGH : LOW);
digitalWrite(LED_RIGHT, x > THRESHOLD ? HIGH : LOW);
digitalWrite(LED_LEFT, x < -THRESHOLD ? HIGH : LOW);
String direction = "";
if (y > THRESHOLD) direction += "LÊN ";
if (y < -THRESHOLD) direction += "XUỐNG ";
if (x > THRESHOLD) direction += "PHẢI ";
if (x < -THRESHOLD) direction += "TRÁI ";
if (direction == "") direction = "GIỮA";
Serial.println("Hướng: " + direction);
});
}
const int DEAD_ZONE = 15;
bluetoothJoystick.onJoystickValue([](int x, int y) {
int filteredX = (abs(x) > DEAD_ZONE) ? x : 0;
int filteredY = (abs(y) > DEAD_ZONE) ? y : 0;
controlMotors(filteredX, filteredY);
});
int targetX = 0, targetY = 0;
int currentX = 0, currentY = 0;
const int RAMP_RATE = 5;
void setup() {
bluetoothJoystick.onJoystickValue([](int x, int y) {
targetX = x;
targetY = y;
});
}
void loop() {
bluetoothServer.loop();
if (currentX < targetX) currentX = min(currentX + RAMP_RATE, targetX);
else if (currentX > targetX) currentX = max(currentX - RAMP_RATE, targetX);
if (currentY < targetY) currentY = min(currentY + RAMP_RATE, targetY);
else if (currentY > targetY) currentY = max(currentY - RAMP_RATE, targetY);
controlMotors(currentX, currentY);
delay(20);
}
bluetoothJoystick.onJoystickValue([](int x, int y) {
float magnitude = sqrt(x * x + y * y);
magnitude = constrain(magnitude, 0, 100);
float angle = atan2(y, x) * 180.0 / PI;
Serial.print("Độ lớn: ");
Serial.print(magnitude, 1);
Serial.print(", Góc: ");
Serial.print(angle, 1);
Serial.println("°");
int speed = map((int)magnitude, 0, 100, 0, 255);
});
const int MOTOR_FL = 16;
const int MOTOR_FR = 17;
const int MOTOR_BL = 18;
const int MOTOR_BR = 19;
void setupMecanumRobot() {
bluetoothJoystick.onJoystickValue([](int x, int y) {
int fl = constrain(y + x, -100, 100);
int fr = constrain(y - x, -100, 100);
int bl = constrain(y - x, -100, 100);
int br = constrain(y + x, -100, 100);
setMotor(MOTOR_FL, fl);
setMotor(MOTOR_FR, fr);
setMotor(MOTOR_BL, bl);
setMotor(MOTOR_BR, br);
});
}
void setMotor(int pin, int speed) {
analogWrite(pin, map(abs(speed), 0, 100, 0, 255));
}
#include <AccelStepper.h>
AccelStepper stepperX(AccelStepper::DRIVER, 16, 17);
AccelStepper stepperY(AccelStepper::DRIVER, 18, 19);
void setup() {
stepperX.setMaxSpeed(1000);
stepperX.setAcceleration(500);
stepperY.setMaxSpeed(1000);
stepperY.setAcceleration(500);
bluetoothJoystick.onJoystickValue([](int x, int y) {
int speedX = map(x, -100, 100, -1000, 1000);
int speedY = map(y, -100, 100, -1000, 1000);
stepperX.setSpeed(speedX);
stepperY.setSpeed(speedY);
});
}
void loop() {
bluetoothServer.loop();
stepperX.runSpeed();
stepperY.runSpeed();
}