ESP32-C3 Robot Mode Switching Issue

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();
}
}

Do you see this output? (possible outputs: automatic, manual)

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.