WiFi Remote Control RC Car Code For ESP32

 #include <WiFi.h>

#include <WebServer.h>

#include <esp_wifi.h>


#define RELAY_FORWARD 26

#define RELAY_LEFT 27

#define ACTIVE_MODE LOW


WebServer server(80);


const char* ap_ssid = "CarRemote";

const char* ap_password = "123456789";


int lastRssi = -100;

unsigned long lastRssiUpdate = 0;


void setup() {

  Serial.begin(115200);

  pinMode(RELAY_FORWARD, OUTPUT);

  pinMode(RELAY_LEFT, OUTPUT);

  digitalWrite(RELAY_FORWARD, !ACTIVE_MODE);

  digitalWrite(RELAY_LEFT, !ACTIVE_MODE);


  WiFi.softAP(ap_ssid, ap_password);

 

  server.on("/", handleRoot);

  server.on("/forward", []{ controlRelay(RELAY_FORWARD, ACTIVE_MODE); });

  server.on("/left", []{ controlRelay(RELAY_LEFT, ACTIVE_MODE); });

  server.on("/stop", handleStop);

  server.on("/status", handleStatus);

 

  server.begin();

  Serial.println("Car Remote Controller Ready!");

  Serial.print("AP IP: "); Serial.println(WiFi.softAPIP());

}


void loop() {

  server.handleClient();

 

  if(millis() - lastRssiUpdate > 2000) {

    lastRssi = getRssi();

    lastRssiUpdate = millis();

  }

}


void controlRelay(int pin, int state) {

  digitalWrite(pin, state);

  server.send(200, "text/plain", "OK");

}


void handleStop() {

  digitalWrite(RELAY_FORWARD, !ACTIVE_MODE);

  digitalWrite(RELAY_LEFT, !ACTIVE_MODE);

  server.send(200, "text/plain", "STOPPED");

}


void handleStatus() {

  String json = "{";

  json += "\"forward\":";

  json += (digitalRead(RELAY_FORWARD) == ACTIVE_MODE) ? "1" : "0";

  json += ",\"left\":";

  json += (digitalRead(RELAY_LEFT) == ACTIVE_MODE) ? "1" : "0";

  json += ",\"rssi\":";

  json += String(lastRssi);

  json += "}";

  server.send(200, "application/json", json);

}


int getRssi() {

  wifi_sta_list_t station_list;

  esp_wifi_ap_get_sta_list(&station_list);

 

  if(station_list.num > 0) {

    return station_list.sta[0].rssi;

  }

  return -100;

}


void handleRoot() {

  String html = R"rawliteral(

<!DOCTYPE html>

<html lang="en">

<head>

  <meta charset="UTF-8">

  <meta name="viewport" content="width=device-width, initial-scale=1.0">

  <title>Animated Car Controller</title>

  <style>

    * {

      margin: 0;

      padding: 0;

      box-sizing: border-box;

      font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;

      -webkit-tap-highlight-color: transparent;

    }

   

    body {

      background: linear-gradient(135deg, #1a1a2e, #16213e);

      min-height: 100vh;

      display: flex;

      justify-content: center;

      align-items: center;

      padding: 20px;

      overflow: hidden;

    }

   

    .container {

      width: 100%;

      max-width: 420px;

      background: rgba(0, 0, 0, 0.75);

      border-radius: 25px;

      padding: 30px;

      box-shadow: 0 20px 40px rgba(0, 0, 0, 0.6);

      backdrop-filter: blur(12px);

      border: 1px solid rgba(255, 255, 255, 0.15);

      position: relative;

      overflow: hidden;

    }

   

    /* Animated background elements */

    .bg-elements {

      position: absolute;

      top: 0;

      left: 0;

      width: 100%;

      height: 100%;

      z-index: -1;

      overflow: hidden;

    }

   

    .circle {

      position: absolute;

      border-radius: 50%;

      background: radial-gradient(circle, rgba(144,224,239,0.1) 0%, transparent 70%);

      animation: float 15s infinite linear;

    }

   

    .circle:nth-child(1) {

      width: 120px;

      height: 120px;

      top: -30px;

      left: -30px;

      animation-delay: 0s;

    }

   

    .circle:nth-child(2) {

      width: 80px;

      height: 80px;

      bottom: -20px;

      right: -20px;

      animation-delay: -5s;

      animation-direction: reverse;

    }

   

    .circle:nth-child(3) {

      width: 60px;

      height: 60px;

      top: 40%;

      right: -30px;

      animation-delay: -10s;

    }

   

    @keyframes float {

      0% { transform: translate(0, 0) rotate(0deg); }

      25% { transform: translate(10px, 10px) rotate(90deg); }

      50% { transform: translate(0, 20px) rotate(180deg); }

      75% { transform: translate(-10px, 10px) rotate(270deg); }

      100% { transform: translate(0, 0) rotate(360deg); }

    }

   

    header {

      text-align: center;

      margin-bottom: 30px;

      position: relative;

      z-index: 1;

      transform: translateY(0);

      transition: transform 0.3s ease;

    }

   

    header:hover {

      transform: translateY(-5px);

    }

   

    h1 {

      color: #fff;

      font-size: 32px;

      text-transform: uppercase;

      letter-spacing: 2px;

      margin-bottom: 8px;

      text-shadow: 0 3px 6px rgba(0,0,0,0.6);

      background: linear-gradient(to right, #00b4d8, #90e0ef);

      -webkit-background-clip: text;

      background-clip: text;

      color: transparent;

      position: relative;

      display: inline-block;

    }

   

    h1::after {

      content: '';

      position: absolute;

      bottom: -8px;

      left: 0;

      width: 100%;

      height: 3px;

      background: linear-gradient(to right, #00b4d8, #90e0ef);

      border-radius: 3px;

      transform: scaleX(0);

      transform-origin: right;

      transition: transform 0.5s ease;

    }

   

    h1:hover::after {

      transform: scaleX(1);

      transform-origin: left;

    }

   

    h2 {

      color: #90e0ef;

      font-size: 18px;

      font-weight: 400;

      letter-spacing: 1px;

      opacity: 0.8;

    }

   

    .wifi-indicator {

      display: flex;

      align-items: center;

      justify-content: center;

      margin: 25px 0 30px;

      padding: 15px;

      background: rgba(30, 30, 50, 0.7);

      border-radius: 15px;

      box-shadow: 0 5px 15px rgba(0,0,0,0.3);

      position: relative;

      overflow: hidden;

      z-index: 1;

    }

   

    .wifi-indicator::before {

      content: '';

      position: absolute;

      top: 0;

      left: -100%;

      width: 100%;

      height: 100%;

      background: linear-gradient(90deg, transparent, rgba(144, 224, 239, 0.1), transparent);

      transition: left 0.6s ease;

    }

   

    .wifi-indicator:hover::before {

      left: 100%;

    }

   

    .wifi-icon {

      font-size: 32px;

      margin-right: 15px;

      color: #90e0ef;

      display: flex;

      align-items: center;

      animation: pulse 2s infinite;

    }

   

    @keyframes pulse {

      0% { transform: scale(1); }

      50% { transform: scale(1.1); }

      100% { transform: scale(1); }

    }

   

    .wifi-bar {

      width: 160px;

      height: 22px;

      background: #1e1e32;

      border-radius: 12px;

      overflow: hidden;

      position: relative;

      box-shadow: inset 0 0 8px rgba(0,0,0,0.4);

    }

   

    .wifi-level {

      height: 100%;

      background: linear-gradient(90deg, #00b4d8, #90e0ef);

      border-radius: 12px;

      width: 75%;

      transition: width 0.8s ease;

    }

   

    .wifi-percentage {

      color: white;

      margin-left: 12px;

      font-weight: bold;

      font-size: 20px;

      min-width: 50px;

      text-shadow: 0 1px 3px rgba(0,0,0,0.5);

    }

   

    .controls {

      display: grid;

      grid-template-columns: 1fr;

      gap: 25px;

      margin-top: 20px;

      position: relative;

      z-index: 1;

    }

   

    .control-row {

      display: flex;

      justify-content: center;

    }

   

    .control-btn {

      width: 200px;

      height: 200px;

      border-radius: 50%;

      background: linear-gradient(145deg, #1a1a1a, #000000);

      color: white;

      border: none;

      font-size: 24px;

      font-weight: bold;

      text-transform: uppercase;

      letter-spacing: 1.5px;

      display: flex;

      flex-direction: column;

      justify-content: center;

      align-items: center;

      cursor: pointer;

      margin: 10px;

      box-shadow:

        0 10px 20px rgba(0,0,0,0.4),

        inset 0 0 15px rgba(0, 180, 216, 0.2);

      transition: all 0.2s ease;

      position: relative;

      overflow: hidden;

      border: 4px solid #2a2a40;

      transform: translateY(0) scale(1);

      z-index: 1;

    }

   

    .control-btn:hover {

      transform: translateY(-5px) scale(1.03);

      box-shadow:

        0 15px 25px rgba(0,0,0,0.5),

        inset 0 0 20px rgba(0, 180, 216, 0.3);

    }

   

    .control-btn:active {

      transform: translateY(2px) scale(0.98);

      box-shadow:

        0 5px 12px rgba(0,0,0,0.3),

        inset 0 0 10px rgba(0, 180, 216, 0.4);

    }

   

    .control-btn::before {

      content: '';

      position: absolute;

      top: 0;

      left: 0;

      width: 100%;

      height: 100%;

      background: radial-gradient(circle at center, transparent 10%, rgba(0, 180, 216, 0.1) 100%);

      opacity: 0;

      transition: opacity 0.3s ease;

      pointer-events: none;

    }

   

    .control-btn:hover::before {

      opacity: 1;

    }

   

    .control-btn::after {

      content: '';

      position: absolute;

      top: 50%;

      left: 50%;

      width: 300px;

      height: 300px;

      background: radial-gradient(circle, rgba(255,255,255,0.15) 0%, transparent 70%);

      transform: translate(-50%, -50%) scale(0);

      opacity: 0;

      transition:

        transform 0.5s ease,

        opacity 0.5s ease;

      pointer-events: none;

    }

   

    .control-btn:active::after {

      transform: translate(-50%, -50%) scale(1);

      opacity: 1;

      transition:

        transform 0.3s ease,

        opacity 0.3s ease;

    }

   

    .btn-icon {

      width: 80px;

      height: 80px;

      margin-bottom: 18px;

      display: flex;

      justify-content: center;

      align-items: center;

      filter: drop-shadow(0 2px 4px rgba(0,0,0,0.5));

      transition: transform 0.3s ease;

    }

   

    .control-btn:hover .btn-icon {

      transform: scale(1.1);

    }

   

    .forward-btn {

      background: linear-gradient(145deg, #1a1a1a, #000000);

    }

   

    .forward-btn.active {

      background: linear-gradient(145deg, #00a300, #007500);

      box-shadow:

        0 0 30px rgba(0, 255, 100, 0.6),

        inset 0 0 20px rgba(0, 255, 150, 0.4);

      animation: glow 1.5s infinite alternate;

    }

   

    .left-btn {

      background: linear-gradient(145deg, #1a1a1a, #000000);

    }

   

    .left-btn.active {

      background: linear-gradient(145deg, #ff8c00, #cc7000);

      box-shadow:

        0 0 30px rgba(255, 165, 0, 0.6),

        inset 0 0 20px rgba(255, 200, 0, 0.4);

      animation: glow 1.5s infinite alternate;

    }

   

    @keyframes glow {

      from { box-shadow:

        0 0 20px rgba(0, 255, 100, 0.6),

        inset 0 0 15px rgba(0, 255, 150, 0.4); }

      to { box-shadow:

        0 0 40px rgba(0, 255, 100, 0.8),

        inset 0 0 25px rgba(0, 255, 150, 0.6); }

    }

   

    .stop-btn {

      width: 160px;

      height: 160px;

      background: linear-gradient(145deg, #1a1a1a, #000000);

      font-size: 20px;

    }

   

    .stop-btn.active {

      background: linear-gradient(145deg, #ff0000, #cc0000);

      box-shadow:

        0 0 30px rgba(255, 0, 0, 0.6),

        inset 0 0 20px rgba(255, 100, 100, 0.4);

      animation: pulse 0.8s infinite alternate;

    }

   

    .status-indicators {

      display: flex;

      justify-content: space-around;

      margin-top: 25px;

      padding: 15px;

      background: rgba(30, 30, 50, 0.5);

      border-radius: 15px;

      position: relative;

      overflow: hidden;

      z-index: 1;

    }

   

    .status-indicators::before {

      content: '';

      position: absolute;

      top: 0;

      left: 0;

      width: 100%;

      height: 3px;

      background: linear-gradient(to right, #00b4d8, #90e0ef);

      transform: scaleX(0);

      transform-origin: left;

      transition: transform 0.5s ease;

    }

   

    .status-indicators:hover::before {

      transform: scaleX(1);

    }

   

    .status-item {

      display: flex;

      flex-direction: column;

      align-items: center;

    }

   

    .status-label {

      color: #90e0ef;

      font-size: 16px;

      margin-bottom: 8px;

      opacity: 0.8;

    }

   

    .status-value {

      color: white;

      font-size: 20px;

      font-weight: bold;

      text-shadow: 0 1px 3px rgba(0,0,0,0.5);

      padding: 5px 15px;

      border-radius: 20px;

      transition: all 0.3s ease;

    }

   

    .status-on {

      background: linear-gradient(to right, #00c853, #64dd17);

      box-shadow: 0 0 15px rgba(0, 200, 83, 0.4);

      transform: scale(1.1);

    }

   

    .status-off {

      background: linear-gradient(to right, #ff5252, #ff867f);

      box-shadow: 0 0 15px rgba(255, 82, 82, 0.4);

    }

   

    .connection-info {

      text-align: center;

      margin-top: 25px;

      padding: 15px;

      background: rgba(30, 30, 50, 0.5);

      border-radius: 15px;

      position: relative;

      z-index: 1;

    }

   

    .connection-info::after {

      content: '';

      position: absolute;

      bottom: 0;

      left: 0;

      width: 100%;

      height: 3px;

      background: linear-gradient(to right, #00b4d8, #90e0ef);

      border-radius: 3px;

      transform: scaleX(0);

      transform-origin: right;

      transition: transform 0.5s ease;

    }

   

    .connection-info:hover::after {

      transform: scaleX(1);

      transform-origin: left;

    }

   

    .connection-label {

      color: #90e0ef;

      font-size: 16px;

      margin-bottom: 8px;

    }

   

    .connection-value {

      color: white;

      font-size: 18px;

      font-weight: bold;

      text-shadow: 0 1px 3px rgba(0,0,0,0.5);

      word-break: break-all;

    }

   

    /* Button press animation */

    @keyframes press {

      0% { transform: translateY(0) scale(1); }

      50% { transform: translateY(4px) scale(0.98); }

      100% { transform: translateY(0) scale(1); }

    }

   

    .press-animation {

      animation: press 0.3s ease;

    }

  </style>

</head>

<body>

  <div class="container">

    <div class="bg-elements">

      <div class="circle"></div>

      <div class="circle"></div>

      <div class="circle"></div>

    </div>

   

    <header>

      <h1>CAR REMOTE CONTROLLER</h1>

      <h2>Animated Touch Interface</h2>

    </header>

   

    <div class="wifi-indicator">

      <div class="wifi-icon">

        <svg width="32" height="32" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">

          <path d="M12 21C13.1046 21 14 20.1046 14 19C14 17.8954 13.1046 17 12 17C10.8954 17 10 17.8954 10 19C10 20.1046 10.8954 21 12 21Z" fill="#90e0ef"/>

          <path d="M8.00004 16C8.00004 16 9.00004 15 12 15C15 15 16 16 16 16" stroke="#90e0ef" stroke-width="2" stroke-linecap="round"/>

          <path d="M4.00004 12C4.00004 12 6.00004 10 12 10C18 10 20 12 20 12" stroke="#90e0ef" stroke-width="2" stroke-linecap="round"/>

          <path d="M1.00004 8C1.00004 8 4.00004 5 12 5C20 5 23 8 23 8" stroke="#90e0ef" stroke-width="2" stroke-linecap="round"/>

        </svg>

      </div>

      <div class="wifi-bar">

        <div class="wifi-level" id="wifiLevel"></div>

      </div>

      <div class="wifi-percentage" id="wifiPercent">75%</div>

    </div>

   

    <div class="controls">

      <div class="control-row">

        <button class="control-btn forward-btn" id="forwardBtn"

                ontouchstart="startForward(event)"

                ontouchend="stopRelays()"

                onmousedown="startForward(event)"

                onmouseup="stopRelays()"

                onmouseleave="stopRelays()">

          <div class="btn-icon">

            <svg width="80" height="80" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">

              <path d="M5 13L6.5 10.5L8 13H5Z" fill="white"/>

              <path d="M19 13H16L17.5 10.5L19 13Z" fill="white"/>

              <path d="M10 13H14V9H10V13Z" fill="white"/>

              <path d="M5 16H19V18C19 18.5523 18.5523 19 18 19H6C5.44772 19 5 18.5523 5 18V16Z" fill="white"/>

              <path d="M18 6H6C5.44772 6 5 6.44772 5 7V13H19V7C19 6.44772 18.5523 6 18 6Z" fill="white"/>

              <rect x="8" y="9" width="2" height="2" fill="#1a1a1a"/>

              <rect x="14" y="9" width="2" height="2" fill="#1a1a1a"/>

            </svg>

          </div>

          FORWARD

        </button>

      </div>

     

      <div class="control-row">

        <button class="control-btn left-btn" id="leftBtn"

                ontouchstart="startLeft(event)"

                ontouchend="stopRelays()"

                onmousedown="startLeft(event)"

                onmouseup="stopRelays()"

                onmouseleave="stopRelays()">

          <div class="btn-icon">

            <svg width="80" height="80" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">

              <path d="M10.5 16.5L7 13L10.5 9.5" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>

              <path d="M7 13H16C17.6569 13 19 11.6569 19 10V10C19 8.34315 17.6569 7 16 7H7" stroke="white" stroke-width="2" stroke-linecap="round"/>

            </svg>

          </div>

          LEFT TURN

        </button>

      </div>

     

      <div class="control-row">

        <button class="control-btn stop-btn" id="stopBtn"

                ontouchstart="stopRelays()"

                onmousedown="stopRelays()">

          <div class="btn-icon">

            <svg width="60" height="60" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">

              <rect x="5" y="5" width="14" height="14" rx="2" fill="white"/>

            </svg>

          </div>

          EMERGENCY STOP

        </button>

      </div>

    </div>

   

    <div class="status-indicators">

      <div class="status-item">

        <div class="status-label">FORWARD STATUS</div>

        <div class="status-value status-off" id="forwardStatus">OFF</div>

      </div>

      <div class="status-item">

        <div class="status-label">LEFT STATUS</div>

        <div class="status-value status-off" id="leftStatus">OFF</div>

      </div>

    </div>

   

    <div class="connection-info">

      <div class="connection-label">CONNECTED TO</div>

      <div class="connection-value" id="connectionValue">CarRemote (192.168.4.1)</div>

    </div>

  </div>

 

  <script>

    let activeBtn = null;

    let holdTimer = null;

    let isActive = false;

   

    // Button press animation

    function animateButton(btn) {

      btn.classList.add('press-animation');

      setTimeout(() => {

        btn.classList.remove('press-animation');

      }, 300);

    }

   

    function startForward(e) {

      e.preventDefault();

      if(activeBtn) activeBtn.classList.remove('active');

      activeBtn = document.getElementById('forwardBtn');

      activeBtn.classList.add('active');

      document.getElementById('forwardStatus').textContent = 'ON';

      document.getElementById('forwardStatus').className = 'status-value status-on';

      animateButton(activeBtn);

     

      // Send command to ESP32

      fetch('/forward');

     

      // Continuous hold

      holdTimer = setInterval(() => {

        // Keep relay active while holding

        fetch('/forward');

      }, 200);

    }

   

    function startLeft(e) {

      e.preventDefault();

      if(activeBtn) activeBtn.classList.remove('active');

      activeBtn = document.getElementById('leftBtn');

      activeBtn.classList.add('active');

      document.getElementById('leftStatus').textContent = 'ON';

      document.getElementById('leftStatus').className = 'status-value status-on';

      animateButton(activeBtn);

     

      // Send command to ESP32

      fetch('/left');

     

      // Continuous hold

      holdTimer = setInterval(() => {

        // Keep relay active while holding

        fetch('/left');

      }, 200);

    }

   

    function stopRelays() {

      if(activeBtn) {

        activeBtn.classList.remove('active');

        activeBtn = null;

      }

      if(holdTimer) {

        clearInterval(holdTimer);

        holdTimer = null;

      }

      document.getElementById('forwardStatus').textContent = 'OFF';

      document.getElementById('forwardStatus').className = 'status-value status-off';

      document.getElementById('leftStatus').textContent = 'OFF';

      document.getElementById('leftStatus').className = 'status-value status-off';

     

      // Send stop command to ESP32

      fetch('/stop');

      animateButton(document.getElementById('stopBtn'));

    }

   

    function updateStatus() {

      fetch('/status')

        .then(response => response.json())

        .then(data => {

          // Update relay status

          document.getElementById('forwardStatus').textContent =

            data.forward ? 'ON' : 'OFF';

          document.getElementById('forwardStatus').className =

            'status-value ' + (data.forward ? 'status-on' : 'status-off');

           

          document.getElementById('leftStatus').textContent =

            data.left ? 'ON' : 'OFF';

          document.getElementById('leftStatus').className =

            'status-value ' + (data.left ? 'status-on' : 'status-off');

           

          // Update button active state

          document.getElementById('forwardBtn').classList.toggle('active', data.forward);

          document.getElementById('leftBtn').classList.toggle('active', data.left);

         

          // Update WiFi signal

          let percent = 0;

          if(data.rssi > -50) percent = 100;

          else if(data.rssi > -60) percent = 80;

          else if(data.rssi > -70) percent = 60;

          else if(data.rssi > -80) percent = 40;

          else if(data.rssi > -90) percent = 20;

          else percent = 5;

         

          document.getElementById('wifiLevel').style.width = percent + '%';

          document.getElementById('wifiPercent').innerText = percent + '%';

        });

    }

   

    // Update status every second

    setInterval(updateStatus, 1000);

    window.onload = updateStatus;

  </script>

</body>

</html>

  )rawliteral";


  server.send(200, "text/html", html);

}


Comments

Popular posts from this blog

ESP32 Wi-Fi Extender Code