Hello everyone,
I am a student currently working on a project to build a robot car using ESP32-C3 as the microcontroller. My idea is to make this robot car capable of autonomous movement as well as controllable via a web interface. I intend to use the ESP32 as a WiFi Access Point so that I can control it without the need for an internet connection.
I have been facing an issue for the past two weeks and I haven't been able to solve it. Here's the problem:
When the robot car is powered on, it defaults to manual mode. I have implemented a feature where I can switch from manual mode to automatic mode via a web interface. When I switch the mode from manual to automatic, the car functions as expected. However, the problem arises when I try to switch the mode back from automatic to manual. I am unable to switch it back unless I restart the ESP32.
Below is the code I'm using. I would greatly appreciate it if someone could help me solve this problem. Thank you.
#include
#include
#include
#include
Servo myservo;
const char* ssid = "Projekt-Z";
const char* password = "12345678";
WebServer server(80);
WebSocketsServer webSocket = WebSocketsServer(81);
const int servoPin = 7;
const int triggerPin = 9;
const int echoPin = 8;
const int MaxAbstand = 50;
long zeit;
long abstand;
const int R1 = 21; // R1 = 1A2 (R1 dan L2)
const int R2 = 20; // R2 = 1B2 (R2 dan L1)
const int L1 = 18; // L1 = 1A1
const int L2 = 19; // L2 = 1B1
enum ControlMode { manual, automatic };
ControlMode mode = manual;
bool arrowHeld = false;
long Sensor() {
digitalWrite(triggerPin, LOW);
delayMicroseconds(2);
digitalWrite(triggerPin, HIGH);
delayMicroseconds(10);
digitalWrite(triggerPin, LOW);
zeit = pulseIn(echoPin, HIGH);
abstand = zeit / 58;
Serial.print("Abstand: ");
Serial.print(abstand);
Serial.println(" cm");
return abstand;
}
void SiehRecht() {
myservo.write(0);
}
void SiehLinks() {
myservo.write(180);
}
void SiehVorne() {
myservo.write(90);
}
void Gerade() {
Serial.println("Maju");
digitalWrite(R1, LOW);
digitalWrite(R2, HIGH);
digitalWrite(L1, HIGH);
digitalWrite(L2, LOW);
}
void Zurueck(){
Serial.println("Mundur");
digitalWrite(R1, HIGH);
digitalWrite(R2, LOW);
digitalWrite(L1, LOW);
digitalWrite(L2, HIGH);
}
void Rechtskurve(){
Serial.println("Ke Kanan");
digitalWrite(R1, HIGH);
digitalWrite(R2, LOW);
digitalWrite(L1, HIGH);
digitalWrite(L2, LOW);
}
void Linkskurve(){
Serial.println("Ke Kiri");
digitalWrite(R1, LOW);
digitalWrite(R2, HIGH);
digitalWrite(L1, LOW);
digitalWrite(L2, HIGH);
}
void Stop(){
Serial.println("Berhenti");
digitalWrite(R1, LOW);
digitalWrite(R2, LOW);
digitalWrite(L1, LOW);
digitalWrite(L2, LOW);
}
void handleRoot() {
server.send(200, "text/html", getHtml());
}
void handleCommand() {
String command = server.arg("plain");
Serial.println("Command received: " + command);
if (command == "F") {
Gerade();
} else if (command == "B") {
Zurueck();
} else if (command == "L") {
Linkskurve();
} else if (command == "R") {
Rechtskurve();
} else {
Stop();
}
server.send(200, "text/plain", "OK");
}
void handleChangeMode() {
String webControl = server.arg("plain");
Serial.println("Change mode to: " + webControl);
if (webControl == "automatic") {
mode = automatic;
webSocket.broadcastTXT("automatic");
} else if (webControl == "manual") {
mode = manual;
webSocket.broadcastTXT("manual");
}
server.send(200, "text/plain", "Mode changed to: " + webControl);
}
void onWebSocketEvent(uint8_t num, WStype_t type, uint8_t * payload, size_t length) {
switch (type) {
case WStype_TEXT:
Serial.printf("[%u] get Text: %s\n", num, payload);
if (strcmp((const char*)payload, "automatic") == 0) {
mode = automatic;
Serial.println("Mode changed to: automatic");
} else if (strcmp((const char*)payload, "manual") == 0) {
mode = manual;
Serial.println("Mode changed to: manual");
} else if (mode == manual) {
if (strcmp((const char*)payload, "F") == 0) {
Gerade();
} else if (strcmp((const char*)payload, "B") == 0) {
Zurueck();
} else if (strcmp((const char*)payload, "L") == 0) {
Linkskurve();
} else if (strcmp((const char*)payload, "R") == 0) {
Rechtskurve();
} else {
Stop();
}
}
break;
case WStype_DISCONNECTED:
Serial.printf("[%u] Disconnected!\n", num);
break;
}
}
String getHtml() {
String html = R"rawliteral(
Obstacle Car Control
/* Global Styles */
body {
font-family: 'Arial', sans-serif;
background: linear-gradient(270deg, #ff0077, #ff7700, #0077ff, #00ff77);
background-size: 800% 800%;
color: #ffffff;
margin: 0;
padding: 0;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
height: 100vh;
overflow: hidden;
animation: GradientBackground 15s ease infinite;
}
@keyframes GradientBackground {
0% { background-position: 0% 50%; }
50% { background-position: 100% 50%; }
100% { background-position: 0% 50%; }
}
h1 {
font-family: 'Alfa Slab One', cursive;
margin-top: 50px;
font-size: 3.5rem;
color: #ffffff;
text-align: center;
}
.z-text {
background: linear-gradient(45deg, #000000, #4b0082, #006400);
font-family: 'Alfa Slab One', cursive;
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
animation: alternate-reverse 15s ease infinite;
margin-top: 50px;
font-size: 3.5rem;
color: #ffffff;
text-align: center;
word-wrap: break-word;
}
.controls {
display: flex;
flex-direction: column;
align-items: center;
}
.arrow-keys {
display: grid;
grid-template-columns: repeat(3, 50px);
grid-template-rows: repeat(3, 50px);
gap: 10px;
}
.arrow-keys .arrow {
background-color: transparent;
border: none;
color: #ffffff;
font-size: 1.5rem;
cursor: pointer;
transition: color 0.3s ease;
width: 50px;
height: 50px;
display: flex;
justify-content: center;
align-items: center;
outline: none;
}
.arrow-keys .arrow:hover {
color: #00ff77;
}
.arrow-keys .arrow:active {
color: #ff7700;
}
.arrow-keys .empty {
width: 50px;
height: 50px;
}
.mode-panel {
margin-top: 20px;
display: flex;
flex-wrap: wrap;
justify-content: center;
}
.mode-panel button {
background-color: transparent;
color: #ffffff;
border: 1px solid #ffffff;
padding: 10px 20px;
font-size: 1rem;
cursor: pointer;
transition: background-color 0.3s ease, color 0.3s ease;
margin: 5px;
outline: none;
}
.mode-panel button:hover {
background-color: #ffffff;
color: #0077ff;
}
.mode-panel button.active {
background-color: #0077ff;
color: #ffffff;
}
/* Responsive Adjustments */
@media (max-width: 768px) {
h1 {
font-size: 2.5rem;
}
.z-text {
font-size: 2.5rem;
}
.arrow-keys {
grid-template-columns: repeat(3, 40px);
grid-template-rows: repeat(3, 40px);
}
.arrow-keys .arrow {
font-size: 1.2rem;
width: 40px;
height: 40px;
}
.mode-panel button {
padding: 8px 16px;
font-size:0.9rem;
}
}
@media (max-width: 480px) {
h1 {
font-size: 2rem;
}
.z-text { font-size: 2rem;
}
.arrow-keys {
grid-template-columns: repeat(3, 30px);
grid-template-rows: repeat(3, 30px);
}
.arrow-keys .arrow {
font-size: 1rem;
width: 30px;
height: 30px;
}
.mode-panel button {
padding: 6px 12px;
font-size: 0.8rem;
}
}
</style>
Control Your Z Car
▲
◄
►
▼
Automatic
Manual
const ws = new WebSocket('ws://' + window.location.hostname + ':81'); // Menghubungkan ke port 81 untuk WebSocket
let mode = 'manual'; // Default mode
ws.onopen = () => {
console.log('Connected to the server');
};
ws.onmessage = (message) => {
console.log('Message received: ' + message.data);
if (message.data === 'automatic') {
mode = 'automatic';
updateModeButtons();
} else if (message.data === 'manual') {
mode = 'manual';
updateModeButtons();
}
};
function sendCommand(command) {
console.log('Command sent: ' + command);
if (mode === 'manual') {
ws.send(command);
}
}
function setMode(newMode) {
console.log('Mode changed to: ' + newMode);
mode = newMode;
ws.send(newMode); // Mengirimkan perubahan mode ke server
updateModeButtons(); // Memperbarui tampilan tombol mode
}
function updateModeButtons() {
const autoButton = document.getElementById('mode-auto');
const manualButton = document.getElementById('mode-manual');
autoButton.classList.remove('active');
manualButton.classList.remove('active');
if (mode === 'automatic') {
autoButton.classList.add('active');
} else {
manualButton.classList.add('active');
}
}
</script>
)rawliteral";
return html;
}
void setup() {
Serial.begin(115200);
myservo.setPeriodHertz(50);
myservo.attach(servoPin);
myservo.write(90);
pinMode(R1, OUTPUT);
pinMode(R2, OUTPUT);
pinMode(L1, OUTPUT);
pinMode(L2, OUTPUT);
pinMode(triggerPin, OUTPUT);
pinMode(echoPin, INPUT);
Serial.println();
Serial.println("Configuring access point...");
if (!WiFi.softAP(ssid, password)) {
Serial.println("Soft AP creation failed");
while (1);
}
IPAddress myIP = WiFi.softAPIP();
Serial.print("AP IP address: ");
Serial.println(myIP);
server.on("/", HTTP_GET, handleRoot);
server.on("/command", HTTP_POST, handleCommand);
server.on("/mode", HTTP_POST, handleChangeMode);
server.begin();
webSocket.begin();
webSocket.onEvent(onWebSocketEvent);
Serial.println("Server started");
}
void AutomaticMode() {
abstand = Sensor();
if (abstand < MaxAbstand) {
Serial.println("Obstacle detected, stopping and checking directions.");
Stop();
delay(1000);
SiehRecht();
delay(1000);
abstand = Sensor();
if (abstand > MaxAbstand) {
SiehVorne();
delay(1000);
Serial.println("Clear on right, turning right.");
Rechtskurve();
delay(300);
Gerade();
} else {
Stop();
SiehLinks();
delay(1000);
abstand = Sensor();
if (abstand > MaxAbstand) {
SiehVorne();
delay(1000);
Serial.println("Clear on left, turning left.");
Linkskurve();
delay(300);
Gerade();
} else {
SiehVorne();
delay(1000);
Serial.println("No clear path, reversing and turning left.");
Zurueck();
delay(1000);
Linkskurve();
delay(300);
Gerade();
}
}
} else {
SiehVorne();
Gerade();
Serial.println("Path clear, moving forward.");
}
delay(500);
}
void ManualMode() {
webSocket.loop();
}
void loop() {
server.handleClient();
if (mode == automatic) {
AutomaticMode();
} else {
ManualMode();
}
}