ACEBOTT ESP32-Powered Robotic Arm with Joystick, App & Web Control

In this project, we are gonna introduce one of the best 4DOF Robot Arm kits powered by the ESP32. This robot arm is really fun to play with, and we can control it in several cool ways, like joystick module control, web-based control, mobile app control, and even automatic mode. My favorite method is the joystick module control. It feels just like operating a mini excavator, which makes it super fun and satisfying to use. This robot arm is also easy to assemble and program. If you’re a beginner, you can use ACECode, a block-based platform. I personally used the Arduino IDE because I’m more familiar with it, but you can choose whichever platform suits you best.
- For more info about this Robot kit — Click on me
Alright, let’s assemble this robot arm step by step. You can also follow the ACEBOTT instruction guide for help. You can buy this Robot arm using the following links.
Step 1
Firstly, unbox the robot arm kit and take a moment to identify all the components inside the box.















Step 2
Secondly, peel off the protective stickers from all the acrylic parts.




Step 3
Thirdly, mount the ESP32 controller board onto the base part.




Step 4
Next, attach the side parts of the robot arm to the base.



Step 5
Now, connect the Dupont wires to the joystick modules and mount them onto the acrylic plate. Make sure the wires are connected to the correct pins, usually GND, VCC, X, Y, and SW. After that, plug the other ends of the wires into the ESP32 controller board.






Step 6
Afterward, install the joystick module setup onto the main base.


Step 7
Now, install the chassis servo onto the chassis servo mounting plate and connect the pins so you can easily drive the steering disk. Then, attach the servo horn to the steering disk





Step 8
Next, install the disk bracket onto the steering disk setup. Then, connect the chassis servo motor’s signal wire to the GPIO5 pin on the ESP32. Power up the controller board to set the servo to its 90-degree position. After the servo is centered, install it on the chassis servo.









Step 9
Now, install the shoulder brackets onto the side panels.






Step 10
Afterward, install the two servo motors onto the other shoulder bracket. Then, carefully route the servo wires through the chassis disk to keep everything neat. Finally, connect the servo wires to GPIO16 and GPIO17 on the ESP32, and set them to 90 degrees for proper calibration.








Step 11
Now, connect the servo-driven arm to the other arm part.




Step 12
Afterward, install the left paw structure of the robot arm.





Step 13
Next, install the left paw structure of the robot arm.




Step 14
Now, install the servo motor onto the claws servo mounting plate. Then, connect this servo to GPIO18 on the ESP32 and set it to 90 degrees to center it properly. Afterward, attach both the left and right paws to the servo horn and the opposite side of the bracket. Make sure the claws open and close smoothly.







Step 15
Next, connect the servo horn to the elbow bracket. Afterward, attach the elbow bracket to the shoulder servo.





Step 16
Now, connect the servos to the ESP32 as follows:
- Chassis servo → GPIO5
- Shoulder servo → GPIO16
- Elbow servo → GPIO17
- Claws servo → GPIO18


Step 17
Next, fix the base of this robot arm. Then, install the battery holder and connect the power jack to the controller board.







Step 18
Now, install the non-slip mat on the bottom side of the robot arm base. Then, connect the controller board to your computer using a USB cable.



OK, now let’s upload the programs one by one and test each part.
Robot Arm Stacking
OK, now let’s copy and paste the following program into the Arduino IDE. Before uploading, make sure to include the required libraries.
#include <ACB_ARM.h>//Add the Robot arm libraries
ACB_ARM ARM;
bool RunningState = true;
int startx = 7, starty = 13, startz = 2;//Initial coordinates
int midz = 15;//Height of middle point
int endx = -7, endy = 13, endz = 0;//End point coordinates
int openAngle = 130, closeAngle = 90;
int count = 2;//Number of blocks
int i = 0;
void setup() {
ARM.Chassis_angle_adjust(8);//Chassis error calibration
ARM.Slight_adjust(0,2);//Small calibration error
ARM.ARM_init(5,16,17,18);//The parameters are four Servo pins
Serial.begin(115200);
}
void loop() {
while(i < count) {
ARM.ClawsCmd(openAngle);//open claws
delay(1000);
ARM.PtpCmd(startx, starty, startz-i*2);//The target point is the upper object
delay(1000);
ARM.ClawsCmd(closeAngle);//close claws
delay(1000);
ARM.PtpCmd(startx, starty, midz);//Lift the object after picking it up
delay(1000);
ARM.PtpCmd(endx, endy, midz);//Rotate above the target point
delay(1000);
ARM.PtpCmd(endx, endy, endz+i*2);//Placing Objects
delay(1000);
ARM.ClawsCmd(openAngle);
delay(1000);
ARM.PtpCmd(endx, endy, midz);//Raise arms
delay(1000);
i = i + 1;
}
ARM.Zero();//Servo initialization
}
- Now, select the board and port. After, click the upload button.



- After uploading the code, remove the USB cable from the controller board. Then, insert the 18650 Li-ion batteries into the battery holder. Finally, power on the robot arm using the battery switch. Now you can test Your robot arm using the blocks.


JoyStick Controlled Robot Arm
Copy and paste the following program into the Arduino IDE. Before uploading, make sure to include the required libraries.
#include <ACB_ARM.h>//Add the Robot arm libraries
ACB_ARM ARM;
void setup() {
ARM.ARM_init(5,16,17,18);//The parameters are four Servo pins
ARM.JoyStick_init(32,33,34,35,36,39); // Joystick initialization
}
void loop() {
ARM.get_JoyStick();
if (ARM.JoyY1 < 50) {//chassis left
ARM.chassis_angle = ARM.chassis_angle + 1;
ARM.JoyChassisCmd(ARM.chassis_angle);
}
if (ARM.JoyY1 > 3500) {//chassis right
ARM.chassis_angle = ARM.chassis_angle - 1;
ARM.JoyChassisCmd(ARM.chassis_angle);
}
if (ARM.JoyX1 < 50) {//Shoulder down
ARM.shoulder_angle = ARM.shoulder_angle + 1;
ARM.JoyShoulderCmd(ARM.shoulder_angle);
}
if (ARM.JoyX1 > 4000) { // Shoulder up
ARM.shoulder_angle = ARM.shoulder_angle - 1;
ARM.JoyShoulderCmd(ARM.shoulder_angle);
}
if (ARM.JoyX2 < 50) { // Elbow up
ARM.elbow_angle = ARM.elbow_angle + 1;
ARM.JoyElbowCmd(ARM.elbow_angle);
}
if (ARM.JoyX2 > 4000) { // Elbow dwon
ARM.elbow_angle = ARM.elbow_angle - 1;
ARM.JoyElbowCmd(ARM.elbow_angle);
}
if (ARM.JoyY2 > 4000) { // Claws open
ARM.claws_angle = ARM.claws_angle + 1;
ARM.JoyClawsCmd(ARM.claws_angle);
}
if (ARM.JoyY2 < 50) { // Claws close
ARM.claws_angle = ARM.claws_angle - 1;
ARM.JoyClawsCmd(ARM.claws_angle);
}
}
- Now, select the board and port. After, click the upload button.



- Once the code is uploaded, you can control your robot arm using the joysticks.

Web Controlled Robot Arm
Copy and paste the following program into the Arduino IDE. Before uploading, make sure to include the required libraries.
#include <ACB_ARM.h>
#include <WiFi.h>
#define FIRMWARE_VERSION "ACB_ARM V2.0"
ACB_ARM ARM;
// WiFi user name and password
const char* ssid = "Robot_Arm";
const char* password = "12345678";
int Chassis_input, Shoulder_input, Elbow_input, Claws_input;
int Chassis_slide, Shoulder_slide, Elbow_slide, Claws_slide;
int PTPX,PTPY,PTPZ;
bool record = false;
void setup() { // initialize
Serial.begin(115200); // set the baud rate to 115200
ARM.Chassis_angle_adjust(8); // Default 0
ARM.Slight_adjust(0,2); // Default 0,0
ARM.ARM_init(5,16,17,18); // The parameters are four Servo pins
ARM.JoyStick_init(32,33,34,35,36,39); // Joystick initialization
WiFi.setTxPower(WIFI_POWER_19_5dBm); // Set the transmit power of WiFi
WiFi.mode(WIFI_AP); // Set WiFi mode to AP (Access Point) mode
WiFi.softAP(ssid, password, 9); // Set the WiFi hotspot name and password, and enable AP mode
IPAddress myIP = WiFi.softAPIP(); // Obtain the IP address of the AP mode
Serial.print("AP IP address: ");
Serial.println(myIP); // address: 192.168.4.1
ARM.startWebServer();
}
void loop() {
//---------------------JoyStick---------------------
handleJoystick();
//--------------------- W E B --------------------
// NULL
if (ARM.val == 3){
}
// button
else if (ARM.val == 1){
ARM.web_claw_x();
} else if (ARM.val == 2){
ARM.web_claw_y();
} else if (ARM.val == 4){
ARM.web_turn_x();
} else if (ARM.val == 5){
ARM.web_turn_y();
} else if (ARM.val == 6){
ARM.web_lower_arm_x();
} else if (ARM.val == 7){
ARM.web_lower_arm_y();
} else if (ARM.val == 8){
ARM.web_upper_arm_x();
} else if (ARM.val == 9){
ARM.web_upper_arm_y();
}
// Slide input
else if (ARM.val == 21) { // chassis
Chassis_input = ARM.Chassis_Silde_Angle;
ARM.ChassisCmd(Chassis_input);
ARM.chassis_angle = Chassis_input;
} else if (ARM.val == 22) { // shoulder
Shoulder_input = ARM.Shoulder_Silde_Angle;
ARM.ShoulderCmd(Shoulder_input);
ARM.shoulder_angle = Shoulder_input;
} else if (ARM.val == 23) { // elbow
Elbow_input = ARM.Elbow_Silde_Angle;
ARM.ElbowCmd(Elbow_input);
ARM.elbow_angle = Elbow_input;
} else if (ARM.val == 24) { // claws
ARM.Speed(50);
Claws_input = ARM.Claws_Silde_Angle;
ARM.ClawsCmd(Claws_input);
ARM.claws_angle = Claws_input;
}
// Silde control
else if (ARM.val == 25) { // chassis
Chassis_slide = ARM.Chassis_Silde_Angle;
ARM.JoyChassisCmd(Chassis_slide);
ARM.chassis_angle = Chassis_slide;
} else if (ARM.val == 26) { // shoulder
Shoulder_slide = ARM.Shoulder_Silde_Angle;
ARM.JoyShoulderCmd(Shoulder_slide);
ARM.shoulder_angle = Shoulder_slide;
} else if (ARM.val == 27) { // elbow
Elbow_slide = ARM.Elbow_Silde_Angle;
ARM.JoyElbowCmd(Elbow_slide);
ARM.elbow_angle = Elbow_slide;
} else if (ARM.val == 28) { // claws
Claws_slide = ARM.Claws_Silde_Angle;
ARM.JoyClawsCmd(Claws_slide);
ARM.claws_angle = Claws_slide;
}
// Mode control
else if (ARM.val == 31 && record) { // save
ARM.saveState();
delay(200);
ARM.val = 3;
} else if (ARM.val == 32) { // end Record
record = true;
} else if (ARM.val == 33) { // start Record
record = false;
} else if (ARM.val == 34 && !record) { // Run
ARM.executeStates();
delay(200);
} else if (ARM.val == 55 && !record) { // Run
ARM.executeStates();
delay(200);
} else if (ARM.val == 35 && !record) { // Reset
ARM.clearSavedStates();
delay(200);
ARM.val = 3;
}
// Mode select 1-6
else if (ARM.val == 40) { // 0
ARM.mode = 0;
} else if (ARM.val == 41) { // 1
ARM.mode = 1;
} else if (ARM.val == 42) { // 2
ARM.mode = 2;
} else if (ARM.val == 43) { // 3
ARM.mode = 3;
} else if (ARM.val == 44) { // 4
ARM.mode = 4;
} else if (ARM.val == 45) { // 5
ARM.mode = 5;
} else if (ARM.val == 46) { // 6
ARM.mode = 6;
}
// Spatial coordinate
else if (ARM.val == 51) { // x
PTPX = ARM.PTP_X;
} else if (ARM.val == 52) { // y
PTPY = ARM.PTP_Y;
} else if (ARM.val == 53) { // z
PTPZ = ARM.PTP_Z;
} else if (ARM.val == 54) { // ptp
ARM.PtpCmd(PTPX,PTPY,PTPZ);
delay(100);
ARM.val = 3;
}
}
void handleJoystick() {
ARM.get_JoyStick();
if (ARM.JoyY1 < 50) { // chassis left
ARM.chassis_angle = ARM.chassis_angle + 1;
ARM.JoyChassisCmd(ARM.chassis_angle);
}
if (ARM.JoyY1 > 3500) { // chassis right
ARM.chassis_angle = ARM.chassis_angle - 1;
ARM.JoyChassisCmd(ARM.chassis_angle);
}
if (ARM.JoyX1 < 50) { // Shoulder down
ARM.shoulder_angle = ARM.shoulder_angle + 1;
ARM.JoyShoulderCmd(ARM.shoulder_angle);
}
if (ARM.JoyX1 > 4000) { // Shoulder up
ARM.shoulder_angle = ARM.shoulder_angle - 1;
ARM.JoyShoulderCmd(ARM.shoulder_angle);
}
if (ARM.JoyX2 < 50) { // Elbow up
ARM.elbow_angle = ARM.elbow_angle + 1;
ARM.JoyElbowCmd(ARM.elbow_angle);
}
if (ARM.JoyX2 > 4000) { // Elbow dwon
ARM.elbow_angle = ARM.elbow_angle - 1;
ARM.JoyElbowCmd(ARM.elbow_angle);
}
if (ARM.JoyY2 > 4000) { // Claws open
ARM.claws_angle = ARM.claws_angle + 1;
ARM.JoyClawsCmd(ARM.claws_angle);
}
if (ARM.JoyY2 < 50) { // Claws close
ARM.claws_angle = ARM.claws_angle - 1;
ARM.JoyClawsCmd(ARM.claws_angle);
}
}
- Now, select the board and port in the Arduino IDE. Once the upload is complete, open the Serial Monitor by going to Tools > Serial Monitor. You should see the IP address of your robot arm displayed there.





- Then, connect to the robot arm’s hotspot using your phone. Next, open your favorite browser and enter the IP address you saw in the Serial Monitor. You should now see the web interface for controlling your robot arm! From there, you can control the arm’s movements directly from your phone’s browser.


APP Controlled Robot Arm
Copy and paste the following program into the Arduino IDE. Before uploading, make sure to include the required libraries.
#include <ACB_ARM.h>
#include <WiFi.h>
#define FIRMWARE_VERSION "ACB_ARM V2.1 20240925"
ACB_ARM ARM;
// WiFi user name and password
const char* ssid = "Robot_Arm";
const char* password = "12345678";
int Chassis_input, Shoulder_input, Elbow_input, Claws_input;
int Chassis_slide, Shoulder_slide, Elbow_slide, Claws_slide;
int PTPX,PTPY,PTPZ;
bool record = false;
void setup() { // initialize
Serial.begin(115200); // set the baud rate to 115200
ARM.Chassis_angle_adjust(8); // Default 0
ARM.Slight_adjust(0,2); // Default 0,0
ARM.ARM_init(5,16,17,18); // The parameters are four Servo pins
ARM.JoyStick_init(32,33,34,35,36,39); // Joystick initialization
WiFi.setTxPower(WIFI_POWER_19_5dBm); // Set the transmit power of WiFi
WiFi.mode(WIFI_AP); // Set WiFi mode to AP (Access Point) mode
WiFi.softAP(ssid, password, 9); // Set the WiFi hotspot name and password, and enable AP mode
IPAddress myIP = WiFi.softAPIP(); // Obtain the IP address of the AP mode
Serial.print("AP IP address: ");
Serial.println(myIP); // address: 192.168.4.1
ARM.startAppServer();
}
void loop() {
//---------------------JoyStick---------------------
handleJoystick();
//--------------------- A P P --------------------
// NULL
if (ARM.val == 3){
}
// speed
// else if (ARM.val == 20) { // speed
// ARM.Speed(50);
// }
// Slide input
else if (ARM.val == 21) { // chassis
Chassis_input = ARM.Chassis_Silde_Angle;
ARM.ChassisCmd(Chassis_input);
ARM.chassis_angle = Chassis_input;
ARM.getPositon();
ARM.val = 3;
} else if (ARM.val == 22) { // shoulder
Shoulder_input = ARM.Shoulder_Silde_Angle;
ARM.ShoulderCmd(Shoulder_input);
ARM.shoulder_angle = Shoulder_input;
ARM.getPositon();
ARM.val = 3;
} else if (ARM.val == 23) { // elbow
Elbow_input = ARM.Elbow_Silde_Angle;
ARM.ElbowCmd(Elbow_input);
ARM.elbow_angle = Elbow_input;
ARM.getPositon();
ARM.val = 3;
} else if (ARM.val == 24) { // claws
Claws_input = ARM.Claws_Silde_Angle;
ARM.ClawsCmd(Claws_input);
ARM.claws_angle = Claws_input;
ARM.getPositon();
ARM.val = 3;
}
// Silde control
else if (ARM.val == 25) { // chassis
Chassis_slide = ARM.Chassis_Silde_Angle;
ARM.JoyChassisCmd(Chassis_slide);
ARM.chassis_angle = Chassis_slide;
ARM.getPositon();
ARM.val = 3;
} else if (ARM.val == 26) { // shoulder
Shoulder_slide = ARM.Shoulder_Silde_Angle;
ARM.JoyShoulderCmd(Shoulder_slide);
ARM.shoulder_angle = Shoulder_slide;
ARM.getPositon();
ARM.val = 3;
} else if (ARM.val == 27) { // elbow
Elbow_slide = ARM.Elbow_Silde_Angle;
ARM.JoyElbowCmd(Elbow_slide);
ARM.elbow_angle = Elbow_slide;
ARM.getPositon();
ARM.val = 3;
} else if (ARM.val == 28) { // claws
Claws_slide = ARM.Claws_Silde_Angle;
ARM.JoyClawsCmd(Claws_slide);
ARM.claws_angle = Claws_slide;
ARM.getPositon();
ARM.val = 3;
}
// Mode control
else if (ARM.val == 31 && record) { // save
ARM.saveState();
delay(200);
ARM.val = 3;
} else if (ARM.val == 32) { // end Record
record = true;
} else if (ARM.val == 33) { // start Record
record = false;
} else if (ARM.val == 34 && !record) { // Run
ARM.executeStates();
delay(200);
ARM.val = 3;
} else if (ARM.val == 55 && !record) {
ARM.executeStates();
delay(100);
} else if (ARM.val == 35 && !record) { // Reset
ARM.clearSavedStates();
delay(200);
ARM.val = 3;
}
// Mode select 1-6
else if (ARM.val == 40) { // 0
// ARM.mode = 0;
} else if (ARM.val == 41) { // 1
ARM.mode = 1;
} else if (ARM.val == 42) { // 2
ARM.mode = 2;
} else if (ARM.val == 43) { // 3
ARM.mode = 3;
} else if (ARM.val == 44) { // 4
ARM.mode = 4;
} else if (ARM.val == 45) { // 5
ARM.mode = 5;
} else if (ARM.val == 46) { // 6
ARM.mode = 6;
}
else if (ARM.val == 54) { // ptp
ARM.PtpCmd(ARM.PTP_X,ARM.PTP_Y,ARM.PTP_Z);
}
//zero
else if (ARM.val == 60) { // x
ARM.Zero();
}
}
void handleJoystick() {
ARM.get_JoyStick();
if (ARM.JoyY1 < 50) { // chassis left
ARM.chassis_angle = ARM.chassis_angle + 1;
ARM.JoyChassisCmd(ARM.chassis_angle);
}
if (ARM.JoyY1 > 3500) { // chassis right
ARM.chassis_angle = ARM.chassis_angle - 1;
ARM.JoyChassisCmd(ARM.chassis_angle);
}
if (ARM.JoyX1 < 50) { // Shoulder down
if (ARM.limit_z > 0 ){
ARM.shoulder_angle = ARM.shoulder_angle + 1;
}
ARM.JoyShoulderCmd(ARM.shoulder_angle);
}
if (ARM.JoyX1 > 4000) { // Shoulder up
ARM.shoulder_angle = ARM.shoulder_angle - 1;
ARM.JoyShoulderCmd(ARM.shoulder_angle);
}
if (ARM.JoyX2 < 50) { // Elbow up
ARM.elbow_angle = ARM.elbow_angle + 1;
ARM.JoyElbowCmd(ARM.elbow_angle);
}
if (ARM.JoyX2 > 4000) { // Elbow dwon
if (ARM.limit_z > 0 ){
ARM.elbow_angle = ARM.elbow_angle - 1;
}
ARM.JoyElbowCmd(ARM.elbow_angle);
}
if (ARM.JoyY2 > 4000) { // Claws open
ARM.claws_angle = ARM.claws_angle + 1;
ARM.JoyClawsCmd(ARM.claws_angle);
}
if (ARM.JoyY2 < 50) { // Claws close
ARM.claws_angle = ARM.claws_angle - 1;
ARM.JoyClawsCmd(ARM.claws_angle);
}
}
- Now, select the board and port. After, click the upload button.



- Then, download and install the ACEBOTT app from the Play Store or App Store. Once installed, connect your phone to the robot arm’s hotspot just like before. Now, open the app and you’ll be able to control your robot arm straight from your phone! Ok, enjoy your robot arm. The full video guide is below. So, we hope to see you in the next project.



ACEBOTT ESP32-Powered Robotic Arm with Joystick, App & Web Control