Hello! Could you assist me with my project code? Iām currently working on a project to build a remote-controlled light armored vehicle using Bluepad, but I'm having trouble with the controls. Specifically, the motor and servo movements are conflicting with each other.
I have tried everything. The console is an ESP DEV KIT, and I am using a DRV8833 motor driver, SG90 servo, and a DFPlayer Mini clone. The DFPlayer Mini clone is particularly troublesome, possibly interfering with the code or communication. I have also created code without the DFPlayer Mini, but it still does not work
#include <Bluepad32.h>
#include <Arduino.h>
#include <HardwareSerial.h>
#include <DFRobotDFPlayerMini.h>
#include <ESP32Servo.h>
#include "esp_task_wdt.h"
// Bitmask for the D-pad
#define DPAD_UP 0x01
#define DPAD_DOWN 0x02
#define DPAD_LEFT 0x04
#define DPAD_RIGHT 0x08
// DFPlayer pin definitions (GPIO34 is input-only so changed to UART2 default pins)
#define MP3_RX_PIN 16 // Default RX pin for UART2 (GPIO16)
#define MP3_TX_PIN 17 // Default TX pin for UART2 (GPIO17)
#define MP3_SERIAL_SPEED 9600
// Hardware serial (using UART2)
HardwareSerial mp3Serial(2);
DFRobotDFPlayerMini mp3;
// Servo configuration
#define SERVO_PIN 14
#define INIT_ANGLE 90
Servo steeringServo;
// Motor control pins
int stbyPin = 4;
int motorPin1 = 5;
int motorPin2 = 22; // Using GPIO22
int enablePin = 25;
int PWM_CHANNEL = 2;
const int PWM_FREQ = 5000;
const int PWM_RESOLUTION = 8;
const float speed_sensitivity = 2.0f;
const int MAX_SPEED = 128;
const float joy2servo = 0.2344f;
// Audio settings
int currentVolume = 15;
// Bluepad32 controller array (using the latest API)
ControllerPtr myControllers[BP32_MAX_CONTROLLERS] = { nullptr };
// Task handles
TaskHandle_t bluetoothTask;
TaskHandle_t audioTask;
TaskHandle_t motorTask;
// Command type definitions
struct AudioCommand {
uint8_t cmd;
uint8_t value;
};
QueueHandle_t audioCommandQueue;
struct MotorCommand {
int speed;
int direction;
int servoAngle;
bool motorActive;
};
QueueHandle_t motorCommandQueue;
// Command definitions
enum {
CMD_PLAY_TRACK = 1,
CMD_STOP_PLAYBACK,
CMD_VOLUME_UP,
CMD_VOLUME_DOWN
};
// Previous state for edge detection for each button
bool prevA = false;
bool prevB = false;
bool prevX = false;
bool prevY = false;
bool prevDpadUp = false;
bool prevDpadDown = false;
bool prevDpadRight = false;
// Motor control function
void motor_run(int speed, int direction) {
digitalWrite(stbyPin, HIGH);
ledcWrite(PWM_CHANNEL, speed);
if (direction == 0) {
digitalWrite(motorPin1, HIGH); // Forward
digitalWrite(motorPin2, LOW);
Serial.println("Motor Forward: motorPin1 HIGH, motorPin2 LOW");
} else {
digitalWrite(motorPin1, LOW); // Reverse
digitalWrite(motorPin2, HIGH);
Serial.println("Motor Reverse: motorPin1 LOW, motorPin2 HIGH");
}
}
void motor_stop() {
digitalWrite(stbyPin, LOW);
ledcWrite(PWM_CHANNEL, 0);
digitalWrite(motorPin1, LOW);
digitalWrite(motorPin2, LOW);
Serial.println("Motor Stopped: All pins LOW");
}
// Callback when a controller is connected
void onConnectedController(ControllerPtr ctl) {
for (int i = 0; i < BP32_MAX_CONTROLLERS; i++) {
if (myControllers[i] == nullptr) {
Serial.print("Controller connected, index=");
Serial.println(i);
myControllers[i] = ctl;
break;
}
}
}
// Callback when a controller is disconnected (modified)
void onDisconnectedController(ControllerPtr ctl) {
for (int i = 0; i < BP32_MAX_CONTROLLERS; i++) {
if (myControllers[i] == ctl) {
Serial.print("Controller disconnected from index=");
Serial.println(i);
myControllers[i] = nullptr;
// When the connection is lost, stop the motor
MotorCommand cmd = {0, 0, INIT_ANGLE, false};
xQueueSend(motorCommandQueue, &cmd, 0);
break;
}
}
}
// Bluetooth task: Controller input processing
void bluetoothTaskFunction(void *parameter) {
TickType_t xLastWakeTime = xTaskGetTickCount();
const TickType_t xFrequency = 50 / portTICK_PERIOD_MS; // 50ms interval
esp_task_wdt_delete(NULL); // Watchdog related (can be removed if not needed)
while (true) {
BP32.update();
ControllerPtr controller = myControllers[0];
if (controller && controller->isConnected()) {
// Log output (only if connection is established)
Serial.println("Controller connected, index=0");
// Get the current state of each button
bool currA = controller->a();
bool currB = controller->b();
bool currX = controller->x();
bool currY = controller->y();
// Get D-pad state using bitmask
uint8_t dpad = controller->dpad();
bool currDpadUp = (dpad & DPAD_UP);
bool currDpadDown = (dpad & DPAD_DOWN);
bool currDpadRight = (dpad & DPAD_RIGHT);
// Button edge detection
if (currA && !prevA) {
AudioCommand cmd = {CMD_PLAY_TRACK, 1}; // A button: Play track 1
xQueueSend(audioCommandQueue, &cmd, 0);
}
if (currB && !prevB) {
AudioCommand cmd = {CMD_PLAY_TRACK, 2}; // B button: Play track 2
xQueueSend(audioCommandQueue, &cmd, 0);
}
if (currX && !prevX) {
AudioCommand cmd = {CMD_PLAY_TRACK, 3}; // X button: Play track 3
xQueueSend(audioCommandQueue, &cmd, 0);
}
if (currY && !prevY) {
AudioCommand cmd = {CMD_PLAY_TRACK, 4}; // Y button: Play track 4
xQueueSend(audioCommandQueue, &cmd, 0);
}
if (currDpadUp && !prevDpadUp) {
AudioCommand cmd = {CMD_VOLUME_UP, 0}; // D-pad up: Volume up
xQueueSend(audioCommandQueue, &cmd, 0);
}
if (currDpadDown && !prevDpadDown) {
AudioCommand cmd = {CMD_VOLUME_DOWN, 0}; // D-pad down: Volume down
xQueueSend(audioCommandQueue, &cmd, 0);
}
if (currDpadRight && !prevDpadRight) {
AudioCommand cmd = {CMD_STOP_PLAYBACK, 0}; // D-pad right: Stop playback
xQueueSend(audioCommandQueue, &cmd, 0);
}
// Update previous state
prevA = currA;
prevB = currB;
prevX = currX;
prevY = currY;
prevDpadUp = currDpadUp;
prevDpadDown = currDpadDown;
prevDpadRight = currDpadRight;
// Motor control using joystick input
int joy_left_y = controller->axisY();
int joy_right_x = controller->axisRX();
MotorCommand motorCmd;
if (abs(joy_left_y) < 50) {
motorCmd.motorActive = false;
motorCmd.speed = 0;
motorCmd.direction = 0;
Serial.println("Joystick neutral: Motor stopped");
} else {
motorCmd.motorActive = true;
motorCmd.speed = constrain((int)(abs(joy_left_y) * speed_sensitivity / 2), 0, MAX_SPEED);
// Here, if joy_left_y > 0 then Reverse, otherwise Forward (adjust as needed)
motorCmd.direction = (joy_left_y > 0) ? 1 : 0;
Serial.print("Motor Speed: ");
Serial.print(motorCmd.speed);
Serial.print(", Direction: ");
Serial.println(motorCmd.direction == 0 ? "Forward" : "Reverse");
}
motorCmd.servoAngle = (int)(joy2servo * joy_right_x + INIT_ANGLE);
xQueueSend(motorCommandQueue, &motorCmd, 0);
}
else {
// When the controller is not connected, simply wait for connection without reinitializing
Serial.println("Controller not connected");
vTaskDelay(1000 / portTICK_PERIOD_MS); // Wait 1 second
}
vTaskDelayUntil(&xLastWakeTime, xFrequency);
}
}
// Audio task: DFPlayer control
void audioTaskFunction(void *parameter) {
esp_task_wdt_delete(NULL);
AudioCommand cmd;
if (!mp3.begin(mp3Serial)) {
Serial.println("DFPlayer Mini failed to initialize");
} else {
mp3.volume(currentVolume);
}
while (true) {
if (xQueueReceive(audioCommandQueue, &cmd, 0) == pdPASS) {
switch (cmd.cmd) {
case CMD_PLAY_TRACK:
mp3.play(cmd.value);
break;
case CMD_STOP_PLAYBACK:
mp3.stop();
break;
case CMD_VOLUME_UP:
if (currentVolume < 30) {
currentVolume += 5;
mp3.volume(currentVolume);
}
break;
case CMD_VOLUME_DOWN:
if (currentVolume > 0) {
currentVolume -= 5;
mp3.volume(currentVolume);
}
break;
}
}
vTaskDelay(50 / portTICK_PERIOD_MS);
}
}
// Motor task
void motorTaskFunction(void *parameter) {
esp_task_wdt_delete(NULL);
MotorCommand cmd;
while (true) {
if (xQueueReceive(motorCommandQueue, &cmd, 0) == pdPASS) {
steeringServo.write(cmd.servoAngle);
if (cmd.motorActive) {
motor_run(cmd.speed, cmd.direction);
} else {
motor_stop();
}
}
vTaskDelay(20 / portTICK_PERIOD_MS);
}
}
void setup() {
Serial.begin(115200);
delay(100);
// Initialize motor-related pins (initial state LOW)
pinMode(stbyPin, OUTPUT);
digitalWrite(stbyPin, LOW);
pinMode(motorPin1, OUTPUT);
digitalWrite(motorPin1, LOW);
pinMode(motorPin2, OUTPUT);
digitalWrite(motorPin2, LOW);
pinMode(enablePin, OUTPUT);
digitalWrite(enablePin, LOW);
ledcSetup(PWM_CHANNEL, PWM_FREQ, PWM_RESOLUTION);
ledcAttachPin(enablePin, PWM_CHANNEL);
ledcWrite(PWM_CHANNEL, 0);
motor_stop(); // Ensure the motor is stopped
// Initialize servo
steeringServo.attach(SERVO_PIN);
steeringServo.write(INIT_ANGLE);
// Initialize DFPlayer
mp3Serial.begin(MP3_SERIAL_SPEED, SERIAL_8N1, MP3_RX_PIN, MP3_TX_PIN);
// Initialize Bluepad32 (only in setup)
BP32.forgetBluetoothKeys(); // Clear pairing info at startup if needed
BP32.setup(&onConnectedController, &onDisconnectedController);
// Create queues
audioCommandQueue = xQueueCreate(10, sizeof(AudioCommand));
motorCommandQueue = xQueueCreate(10, sizeof(MotorCommand));
// Create tasks
xTaskCreatePinnedToCore(bluetoothTaskFunction, "BluetoothTask", 10000, NULL, 5, &bluetoothTask, 0);
xTaskCreatePinnedToCore(audioTaskFunction, "AudioTask", 10000, NULL, 2, &audioTask, 1);
xTaskCreatePinnedToCore(motorTaskFunction, "MotorTask", 5000, NULL, 3, &motorTask, 1);
Serial.println("Setup complete");
}
void loop() {
// Since all processing is handled by tasks, the loop only includes a short delay
vTaskDelay(100 / portTICK_PERIOD_MS);
}

