
Projekt robota, którego sterowanie odbywa się poprzez kod strzałkowy wprowadzany na stronie internetowej hostowanej przez mikrokontroler sterujący robotem.
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
"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.
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.




#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;
}
https://www.youtube.com/watch?v=ZAWeGD2Advw