ESP32 W.R.C
Here is an image of a self-contained “Brain Box” for the ESP32 W.R.C
Here is an image to help you with your wiring.
And the code you will need to upload to your ESP32
#include <WiFi.h>
#include <WebServer.h>
// ===== Pins & PWM Setup =====
#define MOTOR_PIN 14
#define H1 33
#define H2 34
#define MOTOR_CHANNEL 0
#define SERVO_PIN 2
#define SERVO_CHANNEL 1
// Motor PWM
#define MOTOR_FREQ 5000
#define MOTOR_RES 12 // 0-4095
// Servo PWM
#define SERVO_FREQ 50
#define SERVO_RES 16 // 0-65535
// ===== WiFi Access Point =====
const char* ssid = "YTK_UNIT00";
const char* password = "12345678";
WebServer server(80);
// ===== Failsafe =====
unsigned long lastCommandTime = 0;
const unsigned long FAILSAFE_TIMEOUT = 5000; // 5 seconds
// ===== Helper: Map servo angle to duty cycle =====
uint32_t angleToDuty(int angle) {
int us = map(angle, 0, 180, 500, 2500); // 500–2500 µs
uint32_t maxDuty = (1 << SERVO_RES) - 1;
return (uint32_t)((float)us / 20000.0 * maxDuty);
}
// ===== HTML Page =====
void handleRoot() {
server.send(200, "text/html", R"rawliteral(
<!DOCTYPE html>
<html>
<head>
<title>ESP32 Motor & Servo Control</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<style>
body { font-family: sans-serif; margin: 40px; }
.container { display: flex; justify-content: space-between; }
.control { width: 48%; text-align: center; }
.vertical-slider {
-webkit-appearance: slider-vertical;
width: 30px;
height: 300px;
padding: 0 5px;
}
.horizontal-slider { width: 80%; }
h2 { margin-bottom: 30px; }
</style>
</head>
<body>
<h2>ESP32 Motor & Servo Control</h2>
<div class="container">
<div class="control">
<h3>Motor Speed</h3>
<input type="range" min="0" max="255" value="0" class="vertical-slider" id="speedSlider" oninput="updateSpeed(this.value)">
<p>Speed: <span id="speedVal">0</span>%</p>
</div>
<div class="control">
<h3>Servo Angle</h3>
<input type="range" min="0" max="180" value="90" class="horizontal-slider" id="angleSlider" oninput="updateAngle(this.value)">
<p>Angle: <span id="angleVal">90</span>°</p>
</div>
</div>
<script>
function updateSpeed(val) {
document.getElementById("speedVal").innerText = Math.round(val / 255 * 100);
fetch("/setSpeed", {
method: "POST",
headers: {"Content-Type": "application/x-www-form-urlencoded"},
body: "val=" + val
});
}
function updateAngle(val) {
document.getElementById("angleVal").innerText = val;
fetch("/setAngle", {
method: "POST",
headers: {"Content-Type": "application/x-www-form-urlencoded"},
body: "angle=" + val
});
}
</script>
</body>
</html>
)rawliteral");
}
// ===== Motor Handler =====
void handleSetSpeed() {
if (server.hasArg("val")) {
int sliderVal = constrain(server.arg("val").toInt(), 0, 255);
int speed = map(sliderVal, 0, 255, 0, 16000);
digitalWrite(H1, HIGH);
digitalWrite(H2, LOW);
ledcWrite(MOTOR_CHANNEL, speed);
lastCommandTime = millis();
float percent = (sliderVal / 255.0) * 100.0;
Serial.printf("[CMD] Motor speed set: %d (%.1f%%)\n", speed, percent);
server.send(200, "text/plain", String(percent, 1) + "%");
} else {
server.send(400, "text/plain", "Missing val");
}
}
// ===== Servo Handler =====
void handleSetAngle() {
if (server.hasArg("angle")) {
int angle = constrain(server.arg("angle").toInt(), 0, 180);
uint32_t duty = angleToDuty(angle);
ledcWrite(SERVO_CHANNEL, duty);
lastCommandTime = millis();
Serial.printf("[CMD] Servo angle set: %d° (duty=%u)\n", angle, duty);
server.send(200, "text/plain", String(angle));
} else {
server.send(400, "text/plain", "Missing angle");
}
}
// ===== Setup =====
void setup() {
Serial.begin(115200);
Serial.println("Starting Access Point...");
WiFi.softAP(ssid, password);
Serial.println("AP Started!");
Serial.print("IP Address: ");
Serial.println(WiFi.softAPIP());
// Motor setup
ledcSetup(MOTOR_CHANNEL, MOTOR_FREQ, MOTOR_RES);
ledcAttachPin(MOTOR_PIN, MOTOR_CHANNEL);
ledcWrite(MOTOR_CHANNEL, 0);
pinMode(H1, OUTPUT);
pinMode(H2, OUTPUT);
digitalWrite(H1, LOW);
digitalWrite(H2, LOW);
// Servo setup
ledcSetup(SERVO_CHANNEL, SERVO_FREQ, SERVO_RES);
ledcAttachPin(SERVO_PIN, SERVO_CHANNEL);
ledcWrite(SERVO_CHANNEL, angleToDuty(90));
// Server routes
server.on("/", HTTP_GET, handleRoot);
server.on("/setSpeed", HTTP_POST, handleSetSpeed);
server.on("/setAngle", HTTP_POST, handleSetAngle);
server.begin();
Serial.println("Server started!");
lastCommandTime = millis(); // failsafe init
}
// ===== Loop =====
void loop() {
server.handleClient();
// Failsafe
if (millis() - lastCommandTime > FAILSAFE_TIMEOUT) {
ledcWrite(MOTOR_CHANNEL, 0);
digitalWrite(H1, LOW);
digitalWrite(H2, LOW);
ledcWrite(SERVO_CHANNEL, angleToDuty(90));
Serial.println("[FAILSAFE] Timeout: Motor stopped, Servo centered");
lastCommandTime = millis();
}
} Finally here is the laser cutting file if you wish to cop the Brain Box design. Although I would recommend that you try and build one yourself.