Extra parts may be requierd.
Feel free to leave a coment for assembaly instructions or any other info.
Code to upload to ESP32:
#include <WiFi.h>
#include <NetworkClient.h>
#include <WebServer.h>
#include <ESPmDNS.h>
#include <WiFiAP.h>
// Motor 1
int motor1Pin1 = 26;
int motor1Pin2 = 25;
int enable1Pin = 14;
// Setting PWM properties
const int resolution = 8;
int dutyCycle = 0;
const char *ssid = "Train Controller"; // Network Name
const char *password = ""; // Set password Here
String valueString = String(0);
WebServer server(80);
void handleRoot() {
const char html[] PROGMEM = R"rawliteral(
<!DOCTYPE HTML><html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" href="data:,">
<style>
html { font-family: Helvetica; text-align: center; }
.button {
user-select: none; background-color: #4CAF50; border: none;
color: white; padding: 12px 28px; font-size: 26px;
margin: 1px; cursor: pointer;
}
.button2 { background-color: #ff0000; }
input[type=range]:disabled {
opacity: 0.5;
}
</style>
<script>
function moveForward() {
fetch('/forward').then(() => {
document.getElementById('motorSpeed').innerText = 0;
document.getElementById('motorSlider').value = 0;
document.getElementById('motorSlider').disabled = false;
document.getElementById('motorDirection').innerText = "Forward";
});
}
function stopMotor() {
fetch('/stop').then(() => {
document.getElementById('motorSpeed').innerText = 0;
document.getElementById('motorSlider').value = 0;
document.getElementById('motorSlider').disabled = true;
document.getElementById('motorDirection').innerText = "Stopped";
});
}
function moveReverse() {
fetch('/reverse').then(() => {
document.getElementById('motorSpeed').innerText = 0;
document.getElementById('motorSlider').value = 0;
document.getElementById('motorSlider').disabled = false;
document.getElementById('motorDirection').innerText = "Reverse";
});
}
function updateMotorSpeed(pos) {
document.getElementById('motorSpeed').innerText = pos;
fetch(/speed?value=${pos});
}
document.getElementById('motorSpeed').innerText = 0;
document.getElementById('motorSlider').value = 0;
document.getElementById('motorSlider').disabled = true;
</script>
</head>
<body>
<h1>5 inch locomotive controll</h1>
<p><button class="button" onclick="moveForward()">FORWARD</button></p>
<p><button class="button button2" onclick="stopMotor()">Neutral</button></p>
<p><button class="button" onclick="moveReverse()">REVERSE</button></p>
<p>Power: <span id="motorSpeed">0</span></p>
<p>Direction: <span id="motorDirection">Stopped</span></p>
<p>Throttle:</p>
<input type="range" min="0" max="100" step="5" id="motorSlider" oninput="updateMotorSpeed(this.value)" value="0" disabled=true/>
</body>
</html>)rawliteral";
server.send(200, "text/html", html);
}
void handleSpeed() {
if (server.hasArg("value")) {
valueString = server.arg("value");
int value = valueString.toInt();
if (value == 0) {
analogWrite(enable1Pin, 0);
} else {
dutyCycle = map(value, 5, 100, 100, 255);
analogWrite(enable1Pin, dutyCycle);
}
}
server.send(200);
}
void handleStop() {
digitalWrite(motor1Pin1, LOW);
digitalWrite(motor1Pin2, LOW);
analogWrite(enable1Pin, 0);
dutyCycle = 0;
server.send(200);
}
void handleForward() {
digitalWrite(motor1Pin1, HIGH);
digitalWrite(motor1Pin2, LOW);
analogWrite(enable1Pin, 0); // Reset speed to 0
server.send(200);
}
void handleReverse() {
digitalWrite(motor1Pin1, LOW);
digitalWrite(motor1Pin2, HIGH);
analogWrite(enable1Pin, 0); // Reset speed to 0
server.send(200);
}
void handleNotFound() {
server.send(404, "text/plain", "File Not Found");
}
void setup(void) {
pinMode(motor1Pin1, OUTPUT);
pinMode(motor1Pin2, OUTPUT);
analogWrite(enable1Pin, 0);
analogWriteResolution(enable1Pin, resolution);
WiFi.softAP(ssid, password);
server.begin();
MDNS.begin("esp32");
server.on("/", handleRoot);
server.on("/speed", handleSpeed);
server.on("/forward", handleForward);
server.on("/stop", handleStop);
server.on("/reverse", handleReverse);
server.onNotFound(handleNotFound);
}
void loop(void) {
server.handleClient();
}The author remixed this model.
Added parts to turn it into a locomotive including motor mount and clips to hold the body on.