Help needed, RC car by bluepad using esp32 dev and drv8833,1mortor and servo

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);
}

Please post annotated schematics.

please read this.

All consoles share the same GND, and only the servo uses a 5V boost converter. The battery is 3.7V, 800mAh.

Power to the DFPlayer is missing and it phantompowers itself via Tx/Rx.
What's the capacity for current from the battery to 5 volt converter?
What's the voltage of the battery?

Hi!thanks for reply.
The voltage from the battery to the 5V converter is approximately 5.2 volts.

The battery's nominal voltage is 3.7V 800mAh, so its maximum voltage is approximately 4.2V.

There was a mistake. The VCC of the DFPlayer Mini is connected to 3.3V.

this code only works servo.

#include <Bluepad32.h>
#include <ESP32Servo.h>

#define SERVO_PIN 5

ControllerPtr myControllers[BP32_MAX_CONTROLLERS];
Servo myServo;

void setup() {
  Serial.begin(9600);
  while (!Serial) {
    ;
  }

  String fv = BP32.firmwareVersion();
  Serial.print("Firmware version installed: ");
  Serial.println(fv);

  const uint8_t* addr = BP32.localBdAddress();
  Serial.print("BD Address: ");
  for (int i = 0; i < 6; i++) {
    Serial.print(addr[i], HEX);
    if (i < 5)
      Serial.print(":");
    else
      Serial.println();
  }

  BP32.setup(&onConnectedController, &onDisconnectedController);
  BP32.forgetBluetoothKeys();

  myServo.attach(SERVO_PIN); // ć‚µćƒ¼ćƒœćƒ¢ćƒ¼ć‚æćƒ¼ć®åˆęœŸåŒ–
}

void onConnectedController(ControllerPtr ctl) {
  bool foundEmptySlot = false;
  for (int i = 0; i < BP32_MAX_GAMEPADS; i++) {
    if (myControllers[i] == nullptr) {
      Serial.print("CALLBACK: Controller is connected, index=");
      Serial.println(i);
      myControllers[i] = ctl;
      foundEmptySlot = true;

      ControllerProperties properties = ctl->getProperties();
      char buf[80];
      sprintf(buf,
              "BTAddr: %02x:%02x:%02x:%02x:%02x:%02x, VID/PID: %04x:%04x, "
              "flags: 0x%02x",
              properties.btaddr[0], properties.btaddr[1], properties.btaddr[2],
              properties.btaddr[3], properties.btaddr[4], properties.btaddr[5],
              properties.vendor_id, properties.product_id, properties.flags);
      Serial.println(buf);
      break;
    }
  }
  if (!foundEmptySlot) {
    Serial.println(
        "CALLBACK: Controller connected, but could not found empty slot");
  }
}

void onDisconnectedController(ControllerPtr ctl) {
  bool foundGamepad = false;

  for (int i = 0; i < BP32_MAX_GAMEPADS; i++) {
    if (myControllers[i] == ctl) {
      Serial.print("CALLBACK: Controller is disconnected from index=");
      Serial.println(i);
      myControllers[i] = nullptr;
      foundGamepad = true;
      break;
    }
  }

  if (!foundGamepad) {
    Serial.println(
        "CALLBACK: Controller disconnected, but not found in myControllers");
  }
}

void processGamepad(ControllerPtr gamepad) {
  int stickX = gamepad->axisRX(); // axisRX()を使用
  int servoAngle = map(stickX, -511, 512, 0, 180); // 範囲を-511恋悉512に修正
  myServo.write(servoAngle);

  char buf[256];
  snprintf(buf, sizeof(buf) - 1,
           "idx=%d, dpad: 0x%02x, buttons: 0x%04x, "
           "axis L: %4li, %4li, axis R: %4li, %4li, "
           "brake: %4ld, throttle: %4li, misc: 0x%02x, "
           "gyro x:%6d y:%6d z:%6d, accel x:%6d y:%6d z:%6d, "
           "battery: %d, stickX: %d, servoAngle: %d",
           gamepad->index(), gamepad->dpad(), gamepad->buttons(),
           gamepad->axisX(), gamepad->axisY(), gamepad->axisRX(),
           gamepad->axisRY(), gamepad->brake(), gamepad->throttle(),
           gamepad->miscButtons(), gamepad->gyroX(), gamepad->gyroY(),
           gamepad->gyroZ(), gamepad->accelX(), gamepad->accelY(),
           gamepad->accelZ(), gamepad->battery(), stickX, servoAngle);
  Serial.println(buf);
}

void loop() {
  BP32.update();

  for (int i = 0; i < BP32_MAX_CONTROLLERS; i++) {
    ControllerPtr myController = myControllers[i];

    if (myController && myController->isConnected() && myController->isGamepad()) {
      processGamepad(myController);
    }
  }
  delay(15);
}

You dropped the question about the max current from theconverter.

Do you mean the maximum current output of the boost converter?

Output: The voltage is continuously adjustable from DC 5V to 35V, with a maximum output current of 4A.

That should be enough for one servo and the stepper.

I am using battery power directly from the microcontroller via 3.7v, will it work with 3.3v?

Sorry it's not clear. Show how it's done. Using pen, paper and a phone picture is fine.

please read this.thank you.

Hi,
@pxf6sgse schematic.

Tom.... :smiley: :+1: :coffee: :australia:

Did you write your code in stages?
Do you have code that JUST controls the servos and WORKS correctly?
Do you have code the JUST controls the motor and WORKS correctly?

Until you can tell us that, it could be anything, lets establish that the hardware and basic code for each WORKS.

Sorry but its called TROUBLESHOOTING. Tom.... :smiley: :+1: :coffee: :australia:

thank you fore message.

Yes,I made a code only servo works.

But other is not working.

#include <Bluepad32.h>
#include <ESP32Servo.h>

#define SERVO_PIN 5

ControllerPtr myControllers[BP32_MAX_CONTROLLERS];
Servo myServo;

void setup() {
  Serial.begin(9600);
  while (!Serial) {
    ;
  }

  String fv = BP32.firmwareVersion();
  Serial.print("Firmware version installed: ");
  Serial.println(fv);

  const uint8_t* addr = BP32.localBdAddress();
  Serial.print("BD Address: ");
  for (int i = 0; i < 6; i++) {
    Serial.print(addr[i], HEX);
    if (i < 5)
      Serial.print(":");
    else
      Serial.println();
  }

  BP32.setup(&onConnectedController, &onDisconnectedController);
  BP32.forgetBluetoothKeys();

  myServo.attach(SERVO_PIN); // ć‚µćƒ¼ćƒœćƒ¢ćƒ¼ć‚æćƒ¼ć®åˆęœŸåŒ–
}

void onConnectedController(ControllerPtr ctl) {
  bool foundEmptySlot = false;
  for (int i = 0; i < BP32_MAX_GAMEPADS; i++) {
    if (myControllers[i] == nullptr) {
      Serial.print("CALLBACK: Controller is connected, index=");
      Serial.println(i);
      myControllers[i] = ctl;
      foundEmptySlot = true;

      ControllerProperties properties = ctl->getProperties();
      char buf[80];
      sprintf(buf,
              "BTAddr: %02x:%02x:%02x:%02x:%02x:%02x, VID/PID: %04x:%04x, "
              "flags: 0x%02x",
              properties.btaddr[0], properties.btaddr[1], properties.btaddr[2],
              properties.btaddr[3], properties.btaddr[4], properties.btaddr[5],
              properties.vendor_id, properties.product_id, properties.flags);
      Serial.println(buf);
      break;
    }
  }
  if (!foundEmptySlot) {
    Serial.println(
        "CALLBACK: Controller connected, but could not found empty slot");
  }
}

void onDisconnectedController(ControllerPtr ctl) {
  bool foundGamepad = false;

  for (int i = 0; i < BP32_MAX_GAMEPADS; i++) {
    if (myControllers[i] == ctl) {
      Serial.print("CALLBACK: Controller is disconnected from index=");
      Serial.println(i);
      myControllers[i] = nullptr;
      foundGamepad = true;
      break;
    }
  }

  if (!foundGamepad) {
    Serial.println(
        "CALLBACK: Controller disconnected, but not found in myControllers");
  }
}

void processGamepad(ControllerPtr gamepad) {
  int stickX = gamepad->axisRX(); // axisRX()を使用
  int servoAngle = map(stickX, -511, 512, 0, 180); // 範囲を-511恋悉512に修正
  myServo.write(servoAngle);

  char buf[256];
  snprintf(buf, sizeof(buf) - 1,
           "idx=%d, dpad: 0x%02x, buttons: 0x%04x, "
           "axis L: %4li, %4li, axis R: %4li, %4li, "
           "brake: %4ld, throttle: %4li, misc: 0x%02x, "
           "gyro x:%6d y:%6d z:%6d, accel x:%6d y:%6d z:%6d, "
           "battery: %d, stickX: %d, servoAngle: %d",
           gamepad->index(), gamepad->dpad(), gamepad->buttons(),
           gamepad->axisX(), gamepad->axisY(), gamepad->axisRX(),
           gamepad->axisRY(), gamepad->brake(), gamepad->throttle(),
           gamepad->miscButtons(), gamepad->gyroX(), gamepad->gyroY(),
           gamepad->gyroZ(), gamepad->accelX(), gamepad->accelY(),
           gamepad->accelZ(), gamepad->battery(), stickX, servoAngle);
  Serial.println(buf);
}

void loop() {
  BP32.update();

  for (int i = 0; i < BP32_MAX_CONTROLLERS; i++) {
    ControllerPtr myController = myControllers[i];

    if (myController && myController->isConnected() && myController->isGamepad()) {
      processGamepad(myController);
    }
  }
  delay(15);
}

0314.pdf (12.1 KB)

What about the motor ONLY code?

post #18 schematic, thanks..

Tom.... :smiley: :+1: :coffee: :australia:

Here's mortor code.
but doesn't work.

#include <bluepad32.h>

// MOTOR 1 with STBY pin
#define IN1 26
#define IN2 27
#define STBY 4

ControllerPtr myControllers[BP32_MAX_CONTROLLERS];

void setup() {
  Serial.begin(115200);
  pinMode(IN1, OUTPUT);
  pinMode(IN2, OUTPUT);
  pinMode(STBY, OUTPUT);
  digitalWrite(STBY, HIGH);

  BP32.setup(&onConnectedController, &onDisconnectedController);
}

void loop() {
  BP32.update();

  for (int i = 0; i < BP32_MAX_CONTROLLERS; i++) {
    ControllerPtr myController = myControllers[i];

    if (myController && myController->isConnected()) {
      if (myController->isGamepad()) {
        processGamepad(myController);
      }
    }
  }
  delay(150);
}

void onConnectedController(ControllerPtr ctl) {
  for (int i = 0; i < BP32_MAX_CONTROLLERS; i++) {
    if (myControllers[i] == nullptr) {
      myControllers[i] = ctl;
      break;
    }
  }
}

void onDisconnectedController(ControllerPtr ctl) {
  for (int i = 0; i < BP32_MAX_CONTROLLERS; i++) {
    if (myControllers[i] == ctl) {
      myControllers[i] = nullptr;
      break;
    }
  }
}

void processGamepad(ControllerPtr gamepad) {
  int motor_speed = gamepad->axisY(); // Yč»øć®å€¤ć‚’å–å¾—(-511恋悉512)
  motor_speed = map(motor_speed, -512, 511, -255, 255); // -255恋悉255ć«å¤‰ę›

  if (abs(motor_speed) > 10) { // ćƒ‡ćƒƒćƒ‰ć‚¾ćƒ¼ćƒ³
    if (motor_speed > 0) {
      analogWrite(IN1, motor_speed);
      digitalWrite(IN2, LOW);
    } else {
      analogWrite(IN2, abs(motor_speed));
      digitalWrite(IN1, LOW);
    }
  } else {
    digitalWrite(IN1, LOW);
    digitalWrite(IN2, LOW);
  }
}