ESP32 Servo Motor Controller Board – Wireless Control With Bluetooth App and ESP32 to ESP32

In this project, we’ll examine the setup and operation of a printed circuit board based on the ESP32 that lets you manage up to 16 servo motors. You can wirelessly communicate with other devices thanks to the ESP32 board’s many communication options and techniques. To learn how it operates, watch the video.

 How does It work?

I use the joystick and my smartphone to wirelessly operate several servo motors, as demonstrated in the video. Bluetooth and MAC addresses were both used for communication. You can also use WiFi, another ESP32 communication mechanism if you’d like. For MAC address communication, the ESP-NOW approach was employed. Thus, one-way and two-way communication between two ESP32 boards is possible. The ESP32 board’s built-in Bluetooth was utilized for application communication.

Required Components

Required Components
  • 100uF 25V~6V Capacitor
  • 470uF 25V~6V Capacitor
  • SB560 Diode
  • 7805CV Voltage Regulator
  • 3mm LED
  • 330Ohm Resistor
  • 2 Pin 5mm Screw Terminal
  • ESP32 Devkit V1
  • Male and Female Headers

Printed Circuit Board

Printed Circuit Board
Printed Circuit Board
Printed Circuit Board

Naturally, we require a printed circuit board to combine these parts. If we look more closely, the design contains 16 servo motor connection ports and a hexagonal shape. Other than servo motor control applications, input and output pins can be used for other purposes. For printed circuit board services, I favor PCBWay. Ordering is really simple; all you have to do is upload the shared Gerber file to pcbway.com to get started. Depending on your address, cheap and high-quality PCBs will be delivered in a few days.

Soldering

Soldering
Soldering
Soldering
Soldering

If you examine at the bill of materials (BOM), it shows that easily solderable components were preferred. By following the circuit diagram designator, you can assemble your printed circuit board with ease.

Purchase the following tools for soldering

  • Soldering Iron
  • Solder Wire
  • Soldering Clamp Holder

ESP32 Setup for Arduino IDE

ESP32 Setup for Arduino IDE
ESP32 Setup for Arduino IDE
ESP32 Setup for Arduino IDE

I utilized the ESP32 development board, which offers WiFi and Bluetooth as two distinct connection options, to wirelessly control the robot. A microcontroller called the ESP32 supports dual-mode Bluetooth and built-in WiFi. It can be programmed using a variety of platforms, such as the Arduino IDE. The first step in using the Arduino IDE to program the ESP32 board is to add support for the board in Arduino IDE. To do this, follow the instructions below:

  • Open the Arduino IDE, then select “File” from the menu bar and “Preferences.”
  • Copy the URL into the “Additional Board Manager URLs” box highlighted in the figure below as the “Preferences” dialogue box opens, and then click “OK.” URL: /dl/package esp32 index.json/dl.espressif.com
  • Go to “Tools>Board>Board Manager” at this time.
  • In Board Manager, type “esp32” to find the esp32 package by Espressif Systems. Click it, then choose “Install.”

Source Code 

Source Code (Self-moving)
Source Code (Self-moving)

Source Code (Self-moving)

In this section, we’ll look at the source code written for a simple servo motor control. Let’s put our board to the test by cycling eleven servo motors from 180 degrees to 0 degrees and back again.

#include <Servo.h>

static const int servo1Pin = 13;
static const int servo2Pin = 14;
static const int servo3Pin = 26;
static const int servo4Pin = 33;
static const int servo5Pin = 18;
static const int servo6Pin = 17;
static const int servo7Pin = 4;
static const int servo8Pin = 15;
static const int servo9Pin = 16;
static const int servo10Pin = 32;
static const int servo11Pin = 27;

Servo servo1;
Servo servo2;
Servo servo3;
Servo servo4;
Servo servo5;
Servo servo6;
Servo servo7;
Servo servo8;
Servo servo9;
Servo servo10;
Servo servo11;


void setup() {
    Serial.begin(115200);
    servo1.attach(servo1Pin);
    servo2.attach(servo2Pin);
    servo3.attach(servo3Pin);
    servo4.attach(servo4Pin);
    servo5.attach(servo5Pin);
    servo6.attach(servo6Pin);
    servo7.attach(servo7Pin);
    servo8.attach(servo8Pin);
    servo9.attach(servo9Pin);
    servo10.attach(servo10Pin);
    servo11.attach(servo11Pin);
    delay(3000);
}


void loop() {
    for(int posDegrees = 0; posDegrees <= 180; posDegrees++) {
        servo1.write(posDegrees);
        servo2.write(posDegrees);
        servo3.write(posDegrees);
        servo4.write(posDegrees);
        servo5.write(posDegrees);
        servo6.write(posDegrees);
        servo7.write(posDegrees);
        servo8.write(posDegrees);
        servo9.write(posDegrees);
        servo10.write(posDegrees);
        servo11.write(posDegrees);
        Serial.println(posDegrees);
        delay(10);
    }


    for(int posDegrees = 180; posDegrees >= 0; posDegrees–) {
        servo1.write(posDegrees);
        servo2.write(posDegrees);
        servo3.write(posDegrees);
        servo4.write(posDegrees);
        servo5.write(posDegrees);
        servo6.write(posDegrees);
        servo7.write(posDegrees);
        servo8.write(posDegrees);
        servo9.write(posDegrees);
        servo10.write(posDegrees);
        servo11.write(posDegrees);
        Serial.println(posDegrees);
        delay(10);
    }
}

When running the board, take into account the amount of power needed for the particular servo motor type. The ESP32 board’s power supply is directly connected externally to servo motors. The ESP32 uses a voltage regulator (L7805), and a minimum 5V–6V power supply rating is recommended.

Eleven hobby servo motors could be controlled by the board concurrently. In the following stage, let’s try moving a robotic arm.

#include <Servo.h>

static const int servo1Pin = 13;
static const int servo2Pin = 16;
static const int servo3Pin = 27;

#define LEDpin 23

Servo servo1;
Servo servo2;
Servo servo3;

void setup() {
    Serial.begin(115200);
    servo1.attach(servo1Pin);
    servo2.attach(servo2Pin);
    servo3.attach(servo3Pin);
    pinMode(LEDpin, OUTPUT);
    delay(3000);
}

void loop() {
    for(int posDegrees = 90; posDegrees <= 120; posDegrees++) {
        servo1.write(posDegrees);
        digitalWrite(LEDpin, HIGH);
        Serial.println(posDegrees);
        delay(20);
    }
    delay(100);
    for(int posDegrees = 120; posDegrees >= 90; posDegrees–) {
        servo1.write(posDegrees);
        Serial.println(posDegrees);
        digitalWrite(LEDpin, LOW);
        delay(20);
    }
    delay(100);
    for(int posDegrees = 90; posDegrees >= 60; posDegrees–) {
        servo1.write(posDegrees);
        Serial.println(posDegrees);
        digitalWrite(LEDpin, HIGH);
        delay(20);
    }
    delay(100);
    for(int posDegrees = 60; posDegrees <= 90; posDegrees++) {
        servo1.write(posDegrees);
        Serial.println(posDegrees);
        digitalWrite(LEDpin, LOW);
        delay(20);
    }
    delay(100);
    for(int posDegrees = 100; posDegrees <= 150; posDegrees++) {
        servo2.write(posDegrees);
        digitalWrite(LEDpin, HIGH);
        Serial.println(posDegrees);
        delay(20);
    }
    delay(100);
    for(int posDegrees = 150; posDegrees >= 100; posDegrees–) {
        servo2.write(posDegrees);
        digitalWrite(LEDpin, LOW);
        Serial.println(posDegrees);
        delay(20);
    }
    delay(100);
    for(int posDegrees = 80; posDegrees <= 130; posDegrees++) {
        servo3.write(posDegrees);
        digitalWrite(LEDpin, HIGH);
        Serial.println(posDegrees);
        delay(20);
    }
    delay(100);
    for(int posDegrees = 130; posDegrees >= 80; posDegrees–) {
        servo3.write(posDegrees);
        digitalWrite(LEDpin, LOW);
        Serial.println(posDegrees);
        delay(20);
    }
    delay(2000);
}

We shall once more support self-move in this code. Using the “for loop,” let’s move the servo motors at the desired angles. This time, I’ll give the board the 6.6 volts the robotic arm motors require. The robotic arm’s servo motors with high torque have been moved by the board successfully. We’ll move it wirelessly using a BlueTooth app in the segment after this.

Source Code (Smartphone App Via Bluetooth)

Source Code (Smartphone App Via Bluetooth)
Source Code (Smartphone App Via Bluetooth)
Source Code (Smartphone App Via Bluetooth)

We’ll use a smartphone app in this segment. I needed a smartphone app that could use Bluetooth to wirelessly control motors, and I found one. On Android and iOS devices, you can use this app, which was created by STEMpedia.

In order to have a hand controller with numerous buttons to operate the robotic arm in various directions, I decided to use the digital mode for this project. When the gamepad is turned on, the digital mode is the default setting. When the buttons in gamepad mode are pressed, let’s move the servo motors at the predetermined angle.

Using the Gamepad module with ESP32

  • First download library Dabble-ESP32: https://thestempedia.com/download/24469/
  • Go to “Sketch>>Include Library >>Add.ZIP Library” in the menu bar to add the library’s downloaded zip file.
  • Open the shared source code after the library has been installed properly.
#define CUSTOM_SETTINGS
#define INCLUDE_GAMEPAD_MODULE
#include <DabbleESP32.h>
#include <Arduino.h>
#include <Servo.h>

//create servo object to control a servo
Servo leftRight;
Servo upDown;
Servo backForth;

#define leftRightPin 13
#define upDownPin 12
#define backForthPin 15
#define LEDpin 23

//initial angle  for servo
int angle = 90;
int angleStep = 1;

void setup() {
  leftRight.attach(leftRightPin);
  upDown.attach(upDownPin);
  backForth.attach(backForthPin);
  pinMode(LEDpin, OUTPUT);
  Serial.begin(115200);
  Dabble.begin(“ESP32ServoController”);
}

void loop() {
  Dabble.processInput();
  if (GamePad.isLeftPressed()) {
    angle = angle + angleStep;
    leftRight.write(angle);
    digitalWrite(LEDpin, HIGH);
    delay(20);
  }
  else if (GamePad.isRightPressed()) {
    angle = angle – angleStep;
    leftRight.write(angle);
    digitalWrite(LEDpin, HIGH);
    delay(20);
  }
  else if (GamePad.isUpPressed()) {
    angle = angle – angleStep;
    upDown.write(angle);
    digitalWrite(LEDpin, HIGH);
    delay(20);
  }
  else if (GamePad.isDownPressed()) {
    angle = angle + angleStep;
    upDown.write(angle);
    digitalWrite(LEDpin, HIGH);
    delay(20);
  }
  else if (GamePad.isTrianglePressed()) {
    angle = angle + angleStep;
    backForth.write(angle);
    digitalWrite(LEDpin, HIGH);
    delay(20);
  }
  else if (GamePad.isCrossPressed()) {
    angle = angle – angleStep;
    backForth.write(angle);
    digitalWrite(LEDpin, HIGH);
    delay(20);
  }
  else if (GamePad.isStartPressed()) {
    leftRight.write(angle);
    delay(500);
    upDown.write(angle);
    delay(500);
    backForth.write(angle);
    delay(500);
    digitalWrite(LEDpin, HIGH);
    delay(100);
    digitalWrite(LEDpin, LOW);
    delay(100);
    digitalWrite(LEDpin, HIGH);
    delay(100);
    digitalWrite(LEDpin, LOW);
    delay(100);
    digitalWrite(LEDpin, HIGH);
    delay(100);
    digitalWrite(LEDpin, LOW);
  }
  else {
    digitalWrite(LEDpin, LOW);
  }
}
, LOW);  }}

The pin numbers are the only thing that needs alteration here; the servo motor names to which the robotic arm is connected are already defined. If you have used pins other than those listed in the schematic, kindly edit this section.

//create servo object to control a servo
Servo leftRight;
Servo upDown;
Servo backForth;

#define leftRightPin 13
#define upDownPin 12
#define backForthPin 15
#define LEDpin 23

The 10 digital buttons in digital mode transmit data to the device when they are depressed or released. The Up, Down, Right, Left, Triangle, Circle, Cross, Square, Select, and Start buttons are specified as follows in the code.

void loop() {
  Dabble.processInput();
  if (GamePad.isLeftPressed()) {
    angle = angle + angleStep;
    leftRight.write(angle);
    digitalWrite(LEDpin, HIGH);
    delay(20);
  }
  else if (GamePad.isRightPressed()) {
    angle = angle – angleStep;
    leftRight.write(angle);
    digitalWrite(LEDpin, HIGH);
    delay(20);
  }
  else if (GamePad.isUpPressed()) {
    angle = angle – angleStep;
    upDown.write(angle);
    digitalWrite(LEDpin, HIGH);
    delay(20);
  }
  else if (GamePad.isDownPressed()) {
    angle = angle + angleStep;
    upDown.write(angle);
    digitalWrite(LEDpin, HIGH);
    delay(20);
  }
  else if (GamePad.isTrianglePressed()) {
    angle = angle + angleStep;
    backForth.write(angle);
    digitalWrite(LEDpin, HIGH);
    delay(20);
  }
  else if (GamePad.isCrossPressed()) {
    angle = angle – angleStep;
    backForth.write(angle);
    digitalWrite(LEDpin, HIGH);
    delay(20);

Any gamepad button you press will do the desired function. The device established Bluetooth and smartphone communication successfully… Let’s communicate by enabling one-way communication between two ESP32 boards in the following and final stage.

Source Code (ESP32 to ESP32 Communication)

Source Code (ESP32 to ESP32 Communication)
Source Code (ESP32 to ESP32 Communication)
Source Code (ESP32 to ESP32 Communication)
Source Code (ESP32 to ESP32 Communication)

In this section, I’ll be using the ESP32 hand controller. We’ll utilize the ESP-NOW method to connect with two ESP32 devices using their MAC addresses. For the receiver and transmitter, we require two ESP32. To find the receiver’s MAC address, first, upload the code, then use the serial monitor to observe and record the address.


#include “WiFi.h”
 
void setup(){
  Serial.begin(115200);
  WiFi.mode(WIFI_MODE_STA);
  Serial.println(WiFi.macAddress());
}
 
void loop(){


}

Then open the receiver code created for the servo motor controller board, update it as you want and upload.

#include <esp_now.h>
#include <WiFi.h>
#include <Servo.h>

//create servo object to control a servo
Servo leftRight;
Servo upDown;
Servo backForth;

#define leftRightPin 13
#define upDownPin 12
#define backForthPin 15
#define LEDpin 23

//initial angle  for servo
int angle = 90;
int angleStep = 1;

// Structure example to receive data
// Must match the sender structure
typedef struct struct_message {
  int rightJoyXvalue;
  int rightJoyYvalue;
  int rightJoySWvalue;
  int rightButtonAvalue;
  int rightButtonBvalue;
  int leftJoyXvalue;
  int leftJoyYvalue;
  int leftJoySWvalue;
  int leftButtonAvalue;
  int leftButtonBvalue;
} struct_message;


// Create a struct_message called myData
struct_message readingData;


// callback function that will be executed when data is received
void OnDataRecv(const uint8_t * mac, const uint8_t *incomingData, int len) {
  memcpy(&readingData, incomingData, sizeof(readingData));
  //Serial.print(“Bytes received: “);
  //Serial.println(len);


  if (readingData.rightJoyXvalue == HIGH) {
    Serial.print(“Right Joy X : “);
    Serial.println(readingData.rightJoyXvalue);
    Serial.println();
    angle = angle + angleStep;
    leftRight.write(angle);
    digitalWrite(LEDpin, HIGH);
    delay(20);
  }
  else if (readingData.leftJoyXvalue == HIGH) {
    Serial.print(“Left Joy X : “);
    Serial.println(readingData.leftJoyXvalue);
    Serial.println();
    angle = angle – angleStep;
    leftRight.write(angle);
    digitalWrite(LEDpin, HIGH);
    delay(20);
  }
  else if (readingData.rightJoyYvalue == LOW) {
    Serial.print(“Right Joy Y : “);
    Serial.println(readingData.rightJoyYvalue);
    Serial.println();
    angle = angle + angleStep;
    upDown.write(angle);
    digitalWrite(LEDpin, HIGH);
    delay(20);
  }
  else if (readingData.leftJoyYvalue == HIGH) {
    Serial.print(“Left Joy Y : “);
    Serial.println(readingData.leftJoyYvalue);
    Serial.println();
    angle = angle – angleStep;
    upDown.write(angle);
    digitalWrite(LEDpin, HIGH);
    delay(20);
  }
  else if (readingData.rightJoySWvalue == LOW) {
    Serial.print(“Right Joy SW : “);
    Serial.println(readingData.rightJoySWvalue);
    Serial.println();
    angle = angle + angleStep;
    backForth.write(angle);
    digitalWrite(LEDpin, HIGH);
    delay(20);
  }
  else if (readingData.leftJoySWvalue == LOW) {
    Serial.print(“Left Joy SW : “);
    Serial.println(readingData.leftJoySWvalue);
    Serial.println();
    angle = angle – angleStep;
    backForth.write(angle);
    digitalWrite(LEDpin, HIGH);
    delay(20);
  }
  else {
    digitalWrite(LEDpin, LOW);
  }
}


void setup() {
  // Initialize Serial Monitor
  Serial.begin(115200);


  leftRight.attach(leftRightPin);
  upDown.attach(upDownPin);
  backForth.attach(backForthPin);
  pinMode(LEDpin, OUTPUT);


  // Set device as a Wi-Fi Station
  WiFi.mode(WIFI_STA);


  // Init ESP-NOW
  if (esp_now_init() != ESP_OK) {
    Serial.println(“Error initializing ESP-NOW”);
    return;
  }


  // Once ESPNow is successfully Init, we will register for recv CB to
  // get recv packer info
  esp_now_register_recv_cb(OnDataRecv);
}


void loop() {


}

Lastly, open the shared code for the transmitter, make the necessary adjustments to the receiver’s MAC address, and then upload the transmitter code.

#include <esp_now.h>
#include <WiFi.h>


#define rightJoyX     32
#define rightJoyY     35
#define rightJoySW    26
#define rightButtonA  2
#define rightButtonB  15


#define leftJoyX      34
#define leftJoyY      33
#define leftJoySW     25
#define leftButtonA   13
#define leftButtonB   12


#define LEDpin        23


int rightJoyXstate;
int rightJoyYstate;
int rightJoySWstate;
int rightButtonAstate;
int rightButtonBstate;
int leftJoyXstate;
int leftJoyYstate;
int leftJoySWstate;
int leftButtonAstate;
int leftButtonBstate;


// REPLACE WITH YOUR RECEIVER MAC Address
uint8_t broadcastAddress[] = {0x0C, 0xB8, 0x15, 0xC4, 0x82, 0x2C};


// Structure example to send data
// Must match the receiver structure
typedef struct struct_message {
  int rightJoyXvalue;
  int rightJoyYvalue;
  int rightJoySWvalue;
  int rightButtonAvalue;
  int rightButtonBvalue;
  int leftJoyXvalue;
  int leftJoyYvalue;
  int leftJoySWvalue;
  int leftButtonAvalue;
  int leftButtonBvalue;
} struct_message;


// Create a struct_message called myData
struct_message handControllerData;


esp_now_peer_info_t peerInfo;


// callback when data is sent
void OnDataSent(const uint8_t *mac_addr, esp_now_send_status_t status) {
  Serial.print(“\r\nLast Packet Send Status:\t”);
  Serial.println(status == ESP_NOW_SEND_SUCCESS ? “Delivery Success” : “Delivery Fail”);
}


void setup() {
  // Init Serial Monitor
  Serial.begin(115200);
  pinMode(rightJoyX, INPUT);
  pinMode(rightJoyY, INPUT);
  pinMode(rightJoySW, INPUT);
  pinMode(rightButtonA, INPUT);
  pinMode(rightButtonB, INPUT);
  pinMode(leftJoyX, INPUT);
  pinMode(leftJoyY, INPUT);
  pinMode(leftJoySW, INPUT);
  pinMode(leftButtonA, INPUT);
  pinMode(leftButtonB, INPUT);
  pinMode(LEDpin, OUTPUT);


  digitalWrite(LEDpin, HIGH);


  // Set device as a Wi-Fi Station
  WiFi.mode(WIFI_STA);


  // Init ESP-NOW
  if (esp_now_init() != ESP_OK) {
    Serial.println(“Error initializing ESP-NOW”);
    return;
  }


  // Once ESPNow is successfully Init, we will register for Send CB to
  // get the status of Trasnmitted packet
  esp_now_register_send_cb(OnDataSent);


  // Register peer
  memcpy(peerInfo.peer_addr, broadcastAddress, 6);
  peerInfo.channel = 0;
  peerInfo.encrypt = false;


  // Add peer
  if (esp_now_add_peer(&peerInfo) != ESP_OK) {
    Serial.println(“Failed to add peer”);
    return;
  }
}


void loop() {
  // Set values to send
  rightJoyXstate = digitalRead(rightJoyX);
  handControllerData.rightJoyXvalue = rightJoyXstate;
  rightJoyYstate = digitalRead(rightJoyY);
  handControllerData.rightJoyYvalue = rightJoyYstate;
  rightJoySWstate = digitalRead(rightJoySW);
  handControllerData.rightJoySWvalue = rightJoySWstate;
  Serial.print(“rightJoySWstate :”);
  Serial.println(rightJoySWstate);
  rightButtonAstate = digitalRead(rightButtonA);
  handControllerData.rightButtonAvalue = rightButtonAstate;
  rightButtonBstate = digitalRead(rightButtonB);
  handControllerData.rightButtonBvalue = rightButtonBstate;


  leftJoyXstate = digitalRead(leftJoyX);
  handControllerData.leftJoyXvalue = leftJoyXstate;
  leftJoyYstate = digitalRead(leftJoyY);
  handControllerData.leftJoyYvalue = leftJoyYstate;
  leftJoySWstate = digitalRead(leftJoySW);
  handControllerData.leftJoySWvalue = leftJoySWstate;
  leftButtonAstate = digitalRead(leftButtonA);
  handControllerData.leftButtonAvalue = leftButtonAstate;
  leftButtonBstate = digitalRead(leftButtonB);
  handControllerData.leftButtonBvalue = leftButtonBstate;


  // Send message via ESP-NOW
  esp_err_t result = esp_now_send(broadcastAddress, (uint8_t *) &handControllerData, sizeof(handControllerData));


  if (result == ESP_OK) {
    Serial.println(“Sent with success”);
  }
  else {
    Serial.println(“Error sending the data”);
  }
  delay(50);
}

The robotic arm’s servo motors were successfully moved by the board, which also successfully communicated with the two ESP32s. I appreciate you reading or seeing my project. Please share any suggestions you may have in the comments area. Follow to learn about upcoming projects.

Hope you enjoyed this project. We superkitz will be back soon with more interesting projects.

Leave a Reply