ESP32 Bluetooth Car – Servo not turning and motor responds to steering commands

Hi everyone,

I'm working on a Bluetooth-controlled car using an ESP32. The setup includes:

  • One DC motor for traction (connected via BTS7960)
  • One servo motor for steering

I'm using a custom joystick app that sends Bluetooth commands to the ESP32 in the following format:

F50L30 → Forward at 50% speed, turn Left at 30 degrees
B30R20 → Backward at 30% speed, turn Right at 20 degrees

My issues:

  1. The servo doesn’t move – it stays in the center, even when I send L or R commands.
  2. The traction motor sometimes reacts to steering inputs – for example, when I move the joystick for steering, the motor starts turning even though I'm not sending forward/backward commands.

You can check out the full code and setup on my GitHub:
:backhand_index_pointing_right: carro-bluetooth/Controle_ESP32 at main · kv4044/carro-bluetooth · GitHub

I’ve been debugging for a while, but I’m not sure if it’s a logic issue in the code or how I’m handling the serial input. Any help or suggestions would be really appreciated!

Thanks in advance!

Post a wiring diagram.

How does the servo get power (wiring diagram), and at what level?

Depends on wiring, might be the solderless breadboard failing.

Post code and supply wiring diagram.

1 Like

Hi, @kaiky_404
Welcome to the forum.

https://forum.arduino.cc/t/how-to-get-the-best-out-of-this-forum

It is preferred that code and schematics be posted in your post, rather than on an off forum site.

So can you please post this information in your next post, please do not edit your first post.

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

1 Like

Your two or more topics on the same or similar subject have been merged.

Please do not duplicate your questions as doing so wastes the time and effort of the volunteers trying to help you as they are then answering the same thing in different places.

Please create one topic only for your question and choose the forum category carefully. If you have multiple questions about the same project then please ask your questions in the one topic as the answers to one question provide useful context for the others, and also you won’t have to keep explaining your project repeatedly.

Repeated duplicate posting could result in a temporary or permanent ban from the forum.

Could you take a few moments to Learn How To Use The Forum

It will help you get the best out of the forum in the future.

Thank you.

1 Like

Wiring diagram:

The full code:

#include <BluetoothSerial.h>
#include <ESP32Servo.h>
#include "driver/ledc.h"
#include <Arduino.h>

BluetoothSerial SerialBT;

#define IN1 5  // Motor
#define IN2 18 // Motor
#define SERVO_PIN 19 // Servo


#define HEADLIGHT_PIN 21 // Farol
#define LEFT_INDICATOR_PIN 22 // Seta esquerda
#define RIGHT_INDICATOR_PIN 23 // Seta direita 
#define BRAKE_LIGHT_PIN 26 // Luz de freio
#define HORN_PIN 25 // Buzina

Servo steeringServo;

const int pwmChannel1 = 0;
const int pwmChannel2 = 1;
const int pwmFreq = 5000;
const int pwmResolution = 8;

// Variáveis de controle
bool leftIndicatorOn = false;
bool rightIndicatorOn = false;
bool hazardOn = false;
unsigned long lastBlinkTime = 0;
bool blinkState = false;

// Controle do motor
unsigned long lastMotorCommandTime = 0;

void setup() {
  Serial.begin(115200);
  SerialBT.begin("ESP32_Carro");

  pinMode(IN1, OUTPUT);
  pinMode(IN2, OUTPUT);
  pinMode(HEADLIGHT_PIN, OUTPUT);
  pinMode(LEFT_INDICATOR_PIN, OUTPUT);
  pinMode(RIGHT_INDICATOR_PIN, OUTPUT);
  pinMode(HORN_PIN, OUTPUT);
  pinMode(BRAKE_LIGHT_PIN, OUTPUT);

  digitalWrite(HEADLIGHT_PIN, LOW);
  digitalWrite(LEFT_INDICATOR_PIN, LOW);
  digitalWrite(RIGHT_INDICATOR_PIN, LOW);
  digitalWrite(HORN_PIN, LOW);
  digitalWrite(BRAKE_LIGHT_PIN, HIGH); 

  steeringServo.attach(SERVO_PIN, 1000, 2000);
  steeringServo.write(90); // Centro

  ledcSetup(pwmChannel1, pwmFreq, pwmResolution);
  ledcSetup(pwmChannel2, pwmFreq, pwmResolution);
  ledcAttachPin(IN1, pwmChannel1);
  ledcAttachPin(IN2, pwmChannel2);

  ledcWrite(pwmChannel1, 0);
  ledcWrite(pwmChannel2, 0);
}

void loop() {
  if (SerialBT.available()) {
    String input = SerialBT.readStringUntil('\n');
    input.trim();
    Serial.println("Recebido: " + input);

    if (input.length() == 6) {
      char moveDir = input[0];
      int moveSpeed = input.substring(1, 3).toInt();
      char steerDir = input[3];
      int steerAngle = input.substring(4, 6).toInt();
      processMotion(moveDir, moveSpeed, steerDir, steerAngle);
    } else if (input.length() == 1) {
      processFunction(input[0]);
    }
  }

  // Verifica se parou de receber comandos do motor
  if (millis() - lastMotorCommandTime > 500) {
    stopMotor();
  }

  blinkIndicators();
}

void processMotion(char moveDir, int moveSpeed, char steerDir, int steerAngle) {


  if (steerDir == 'R' || steerDir == 'L') {
    int servoPosition = (steerDir == 'R') ? (90 - steerAngle) : (90 + steerAngle);
    steeringServo.write(servoPosition);
  }


  // Movimento do motor
  if (moveDir == 'F' || moveDir == 'B') {
    lastMotorCommandTime = millis(); 

    int pwmValue = map(moveSpeed, 0, 99, 0, 255);

    if (moveSpeed == 0) {
      stopMotor();
    } else {
      digitalWrite(BRAKE_LIGHT_PIN, LOW);

      if (moveDir == 'F') {
        ledcWrite(pwmChannel1, pwmValue);
        ledcWrite(pwmChannel2, 0);
      } else {
        ledcWrite(pwmChannel1, 0);
        ledcWrite(pwmChannel2, pwmValue);
      }
    }
  }
}

void stopMotor() {
  ledcWrite(pwmChannel1, 0);
  ledcWrite(pwmChannel2, 0);
  digitalWrite(BRAKE_LIGHT_PIN, HIGH);
}

void processFunction(char cmd) {
  switch (cmd) {
    case 'U': digitalWrite(HEADLIGHT_PIN, HIGH); break;

    case 'u': digitalWrite(HEADLIGHT_PIN, LOW); break;

    case 'W': leftIndicatorOn = true; hazardOn = false; break;

    case 'w': leftIndicatorOn = false; digitalWrite(LEFT_INDICATOR_PIN, LOW); break;

    case 'V': rightIndicatorOn = true; hazardOn = false; break;

    case 'v': rightIndicatorOn = false; digitalWrite(RIGHT_INDICATOR_PIN, LOW); break;

    case 'X': hazardOn = true; leftIndicatorOn = false; rightIndicatorOn = false; break;

    case 'x': hazardOn = false; digitalWrite(LEFT_INDICATOR_PIN, LOW); digitalWrite(RIGHT_INDICATOR_PIN, LOW); break;
    
    case 'Y': digitalWrite(HORN_PIN, HIGH); delay(200); digitalWrite(HORN_PIN, LOW); break;
  }
}

void blinkIndicators() {
  if (millis() - lastBlinkTime >= 500) {
    lastBlinkTime = millis();
    blinkState = !blinkState;

    if (hazardOn) {
      digitalWrite(LEFT_INDICATOR_PIN, blinkState);
      digitalWrite(RIGHT_INDICATOR_PIN, blinkState);
    } else {
      digitalWrite(LEFT_INDICATOR_PIN, leftIndicatorOn ? blinkState : LOW);
      digitalWrite(RIGHT_INDICATOR_PIN, rightIndicatorOn ? blinkState : LOW);
    }
  }
}

here there you can replace your servo motor not working well

Thanks for your suggestion! I tested the same servo motor with a different program, and it worked fine. So I believe the issue is not with the servo itself, but something in my current code or setup.

90 degrees is a servo's "home" position when power is applied... (unless the servo is 360 [probably not], and that is its "stop" position). So if "R" or "L" are not arriving... the servo will remain at 90/HOME.

Do you send/receive Upper Case R and Upper Case L?

What is the value of input.substring(4, 6).toInt()? (use Serial.print()) Compare that to the output of the transmit end of the bluetooth connection.