Robot na kod strzałkowy | ESP-12E

Typ_projektu
Arduino
Zdjecie główne
Krótki opis projektu

Projekt robota, którego sterowanie odbywa się poprzez kod strzałkowy wprowadzany na stronie internetowej hostowanej przez mikrokontroler sterujący robotem.

Niezbędne elementy

1. Płytka ESP-12E

2. 2 silniki z przekładniami

3. 2 koła z oponami

4. mostek H

5. baza do zamontowania silników

6. powerbank

7. kable do podłączenia silników i płytki

Sprzęt

"Mózgiem" robota jest płytka ESP-12E hostująca stronę internetową z interfejsem użytkownika. Płytka połączona jest mostkiem H do dwóch silników elektrycznych z przekładniami.

Opis projektu

Sterowanie odbywa się przy pomocy płytki ESP-12E, która tworzy stronę internetową służącą za interfejs do wprowadzania kodu strzałkowego. Następnie użytkownik wprowadza ścieżkę jaką robot ma przejechać i uruchamia sekwencję. Po jej zakończeniu robot gotowy jest przyjąć nową sekwencję ruchów.

Kod strzałkowy jest prostą metodą sterowania robotem układając sekwencję podstawowych ruchów kryjących się pod piktogramami strzałek. Są to:

  • jazda prosto
  • jazda w tył
  • skręt w lewo
  • skręt w prawo
  • zawracanie

Interfejs pozwala także na usunięcie wpisanego kodu strzałkowego przed jego uruchomieniem (bo np chciało się inny ruch) a także możliwość zatrzymania wykonania kodu w dowolnym momencie po uruchomieniu.

Zdjęcia
interfejs użytkownika
robot (od góry)
robot (z boku)
robot z powerbankiem (od boku)
kod programu
#include <ESP8266WebServer.h>

const char* ssid = "Bombodupiarz";

#define RIGHT_MOTOR_PIN1  D1
#define RIGHT_MOTOR_PIN2  D3
#define LEFT_MOTOR_PIN1   D2
#define LEFT_MOTOR_PIN2   D4
#define LED_PIN           2

#define LEFT_MOTOR_SPEED 1023
#define RIGHT_MOTOR_SPEED 700

IPAddress local_ip(192, 168, 4, 1);
IPAddress gateway(192, 168, 100, 1);
IPAddress subnet(255, 255, 255, 0);
ESP8266WebServer server(80);

int car_mode = 0;

void setup() {
  Serial.begin(115200);
  Serial.println("Bombodupiarz3000");

  pinMode(RIGHT_MOTOR_PIN1, OUTPUT);
  pinMode(RIGHT_MOTOR_PIN2, OUTPUT);
  pinMode(LEFT_MOTOR_PIN1, OUTPUT);
  pinMode(LEFT_MOTOR_PIN2, OUTPUT);
  pinMode(LED_PIN, OUTPUT);
  car_control();

  WiFi.mode(WIFI_AP);
  WiFi.softAP(ssid);
  Serial.print("AP IP address: ");
  Serial.println(WiFi.softAPIP());

  server.on("/", HTTP_GET, handle_OnConnect);
  server.on("/forward", HTTP_GET, handle_forward);
  server.on("/backward", HTTP_GET, handle_backward);
  server.on("/left", HTTP_GET, handle_left);
  server.on("/right", HTTP_GET, handle_right);
  server.on("/turn", HTTP_GET, handle_turn);
  server.on("/stop", HTTP_GET, handle_stop);
  server.onNotFound(handle_NotFound);

  server.begin();
  Serial.println("ESP8266 car server started.");
  digitalWrite(LED_PIN, LOW);
}

void loop() {
  server.handleClient();
  car_control();
}

// Web Handlers
void handle_OnConnect() {
  car_mode = 0;
  Serial.println("Client connected");
  server.send(200, "text/html", SendHTML());
}

void handle_stop() {
  car_mode = 0;
  Serial.println("Stopped");
  server.send(200, "text/plain", "Stopped");
}

void handle_forward() {
  car_mode = 2;
  Serial.println("Go forward (actually backward)...");
  server.send(200, "text/plain", "Forward (reversed)");
}

void handle_backward() {
  car_mode = 1;
  Serial.println("Go backward (actually forward)...");
  server.send(200, "text/plain", "Backward (reversed)");
}

// ✅ Zmiana miejscami: poprawiony kierunek skrętów
void handle_left() {
  car_mode = 3;
  Serial.println("Turn left (naprawione)...");
  server.send(200, "text/plain", "Left");
}

void handle_right() {
  car_mode = 4;
  Serial.println("Turn right (naprawione)...");
  server.send(200, "text/plain", "Right");
}

void handle_turn() {
  car_mode = 5;
  Serial.println("Turn (U-turn)...");
  server.send(200, "text/plain", "Turn");
}

void handle_NotFound() {
  car_mode = 0;
  Serial.println("Page error");
  server.send(404, "text/plain", "Not found");
}

void car_control() {
  switch (car_mode) {
    case 0: // stop
      digitalWrite(LEFT_MOTOR_PIN1, LOW);
      digitalWrite(RIGHT_MOTOR_PIN2, LOW);
      digitalWrite(RIGHT_MOTOR_PIN1, LOW);
      digitalWrite(LEFT_MOTOR_PIN2, LOW);
      break;
    case 1: // forward
      analogWrite(LEFT_MOTOR_PIN1, LEFT_MOTOR_SPEED);
      digitalWrite(RIGHT_MOTOR_PIN2, LOW);
      analogWrite(RIGHT_MOTOR_PIN1, RIGHT_MOTOR_SPEED);
      digitalWrite(LEFT_MOTOR_PIN2, LOW);
      break;
    case 2: // backward
      analogWrite(LEFT_MOTOR_PIN1, LEFT_MOTOR_SPEED);
      digitalWrite(RIGHT_MOTOR_PIN2, HIGH);
      analogWrite(RIGHT_MOTOR_PIN1, RIGHT_MOTOR_SPEED);
      digitalWrite(LEFT_MOTOR_PIN2, HIGH);
      break;
    case 3: // skręt w prawo
      analogWrite(LEFT_MOTOR_PIN1, LEFT_MOTOR_SPEED);
      digitalWrite(RIGHT_MOTOR_PIN2, HIGH);
      analogWrite(RIGHT_MOTOR_PIN1, RIGHT_MOTOR_SPEED);
      digitalWrite(LEFT_MOTOR_PIN2, LOW);
      break;
    case 4: // skręt w lewo
      analogWrite(LEFT_MOTOR_PIN1, LEFT_MOTOR_SPEED);
      digitalWrite(RIGHT_MOTOR_PIN2, LOW);
      analogWrite(RIGHT_MOTOR_PIN1, RIGHT_MOTOR_SPEED);
      digitalWrite(LEFT_MOTOR_PIN2, HIGH);
      break;
    case 5: // U-turn
      analogWrite(LEFT_MOTOR_PIN1, LEFT_MOTOR_SPEED);
      digitalWrite(RIGHT_MOTOR_PIN2, LOW);
      analogWrite(RIGHT_MOTOR_PIN1, RIGHT_MOTOR_SPEED);
      digitalWrite(LEFT_MOTOR_PIN2, HIGH);
      break;
  }
}

String SendHTML() {
  String html = R"=====(<!DOCTYPE html>
<html>
<head>
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <style>
    body {background-color: PeachPuff; font-family: Arial;}
    input[type=button] {
      height: 100px; width: 100px; font-size: 24px; margin: 5px;
    }
    .clearBtn {
      height: 50px !important;
      background-color: Tomato;
    }
    .track {
      margin-top: 20px; padding: 10px;
      border: 2px solid #444; min-height: 50px; background: #fff;
    }
    .tile {
      display: inline-block; margin: 5px; padding: 10px 20px;
      background: LightBlue; border-radius: 10px; font-weight: bold;
    }
    .sliders {
      margin-top: 20px;
      padding: 10px;
      border: 2px dashed #666;
      background: #f9f9f9;
      width: 300px;
      margin-left: auto;
      margin-right: auto;
    }
    .slider-container {
      margin: 10px 0;
    }
    .slider-label {
      font-weight: bold;
      display: block;
      margin-bottom: 5px;
    }
    input[type=range] {
      width: 100%;
    }
  </style>
</head>
<body>
  <div align="center">
    <form onsubmit="return false;">
      <table>
        <tr><td colspan="3" align="center">
          <input type="button" value="--^--" onclick="addMove('forward', '--^--', 2000)">
        </td></tr>
        <tr>
          <td align="center"><input type="button" value="<--" onclick="addMove('left', '<--', 500)"></td>
          <td align="center"><input type="button" id="startStopBtn" value="START" onclick="toggleExecution()"></td>
          <td align="center"><input type="button" value="-->" onclick="addMove('right', '-->', 500)"></td>
        </tr>
        <tr>
          <td colspan="3" align="center">
            <input type="button" value="--v--" onclick="addMove('backward', '--v--', 2000)">
          </td>
        </tr>
        <tr>
          <td colspan="3" align="center">
            <input type="button" value="TURN" onclick="addMove('turn', 'TURN', 750)">
          </td>
        </tr>
        <tr>
          <td colspan="3" align="center">
            <input type="button" class="clearBtn" value="CLEAR" onclick="clearMoves()">
          </td>
        </tr>
      </table>
      <div class="track" id="moveTrack"></div>
      <div class="sliders">
        <div class="slider-container">
          <label class="slider-label">Lewy silnik</label>
          <input type="range" min="0" max="100" value="100">
        </div>
        <div class="slider-container">
          <label class="slider-label">Prawy silnik</label>
          <input type="range" min="0" max="100" value="100">
        </div>
      </div>
    </form>
  </div>
  <script>
    let queue = [];
    let executing = false;
    let currentIndex = 0;

    function addMove(command, label, delay) {
      if (executing) return;
      queue.push({command, label, delay});
      updateTrack();
    }

    function clearMoves() {
      if (executing) return;
      queue = [];
      updateTrack();
    }

    function updateTrack() {
      const track = document.getElementById("moveTrack");
      track.innerHTML = '';
      for (let m of queue) {
        const tile = document.createElement('span');
        tile.className = 'tile';
        tile.innerText = m.label;
        track.appendChild(tile);
      }
    }

    function toggleExecution() {
      if (!executing) {
        if (queue.length === 0) return;
        executing = true;
        document.getElementById("startStopBtn").value = "STOP";
        executeMoves();
      } else {
        executing = false;
        queue = [];
        currentIndex = 0;
        updateTrack();
        document.getElementById("startStopBtn").value = "START";
        fetch('/stop');
      }
    }

    function executeMoves() {
      if (!executing || currentIndex >= queue.length) {
        executing = false;
        currentIndex = 0;
        queue = [];
        updateTrack();
        document.getElementById("startStopBtn").value = "START";
        fetch('/stop');
        return;
      }

      const move = queue[currentIndex];
      fetch('/' + move.command).then(() => {
        currentIndex++;
        setTimeout(executeMoves, move.delay);
      });
    }
  </script>
</body>
</html>)=====";
  return html;
}
Pliki_projektu
Schemat
Youtube
Tagi
ESP-12E ESP kod-strzalkowy robot sterowanie-przez-internet
Odnośniki zewnętrzne
chat.openai.com

https://www.youtube.com/watch?v=ZAWeGD2Advw