Atelier.

atelier · lecții

Lecția 12 35 min intermediar 3 întrebări quiz

Telecomandă WiFi din browser — joystick pe telefonul tău

Cum face ESP32 un mic server web cu joystick virtual. Conduci mașina din pagina de pe forge.udev.ro pe telefon.

Ce înveți

  • Cum face ESP32 un server WiFi (Soft AP sau client pe rețea locală)
  • Ce-i WebSocket și de ce-i mai bun decât HTTP pentru un joystick (latență mică)
  • Cum pui pe pagina web un joystick care funcționează pe touch
  • Cum mapezi (x, y) joystick → comenzi pentru 2 motoare (tank drive)

Două moduri de a conecta telefonul la mașină

1. Mașina e CLIENT pe WiFi-ul casei (cel mai simplu)

WiFi.begin("WiFi-acasa", "parola-mea");

ESP32 se conectează la routerul tău. Primește IP (ex: 192.168.1.50). Tu deschizi pe telefon http://192.168.1.50/ și acolo-i joystick-ul.

Avantaj: integrat în rețea, simplu. Dezavantaj: trebuie să fii acasă cu acel WiFi.

2. Mașina e Soft AP (hotspot propriu)

WiFi.softAP("CARTK-M1", "parola123");

ESP32 devine propriul router. Telefonul se conectează la rețeaua CARTK-M1. Apoi deschizi http://192.168.4.1/ (default AP IP).

Avantaj: funcționează ORIUNDE, fără router. Dezavantaj: telefonul „pierde internetul” cât e conectat la mașină.

Pentru CART-K începem cu Soft AP — mai puțină dependență.

WebSocket pe ESP32

#include <WiFi.h>
#include <WebSocketsServer.h>

WebSocketsServer ws(81);   // port 81

void onMessage(uint8_t num, WStype_t type, uint8_t* payload, size_t length) {
  if (type == WStype_TEXT) {
    // payload = "x=42,y=-18"
    int x, y;
    sscanf((char*)payload, "x=%d,y=%d", &x, &y);
    drive(x, y);
  }
}

void setup() {
  WiFi.softAP("CARTK-M1", "parola123");
  ws.begin();
  ws.onEvent(onMessage);
}

void loop() {
  ws.loop();
}

Pe telefon — joystick touch

<canvas id="joy" width="300" height="300"></canvas>
<script>
  const ws = new WebSocket('ws://192.168.4.1:81/');
  const canvas = document.getElementById('joy');
  const ctx = canvas.getContext('2d');
  let touching = false;
  let x = 0, y = 0;

  canvas.addEventListener('touchmove', (e) => {
    const t = e.touches[0];
    const rect = canvas.getBoundingClientRect();
    x = Math.round((t.clientX - rect.left - 150) / 1.5);   // -100..+100
    y = -Math.round((t.clientY - rect.top - 150) / 1.5);
    ws.send(`x=${x},y=${y}`);
    redraw();
  });

  canvas.addEventListener('touchend', () => {
    x = 0; y = 0;
    ws.send('x=0,y=0');   // stop
    redraw();
  });

  function redraw() {
    ctx.clearRect(0, 0, 300, 300);
    ctx.beginPath();
    ctx.arc(150 + x * 1.5, 150 - y * 1.5, 30, 0, 7);
    ctx.fillStyle = '#1d4ed8';
    ctx.fill();
  }
</script>

Tank drive din (x, y)

void drive(int x, int y) {
  // y = -100..+100 (înainte/înapoi)
  // x = -100..+100 (stânga/dreapta)
  int stanga  = y + x;   // -200..+200
  int dreapta = y - x;
  stanga  = constrain(stanga, -200, 200);
  dreapta = constrain(dreapta, -200, 200);
  int pwm_s = abs(stanga) * 255 / 200;
  int pwm_d = abs(dreapta) * 255 / 200;
  if (stanga > 0)  forwardLeft(pwm_s);  else if (stanga < 0)  backwardLeft(pwm_s);  else stopLeft();
  if (dreapta > 0) forwardRight(pwm_d); else if (dreapta < 0) backwardRight(pwm_d); else stopRight();
}

Pentru CART-K — pagina de control pe site

În loc să servim HTML din ESP32 (greoi), facem pagina /sergiu/cart-k/control/ pe forge.udev.ro care se conectează prin WebSocket la IP-ul mașinii (ws://192.168.4.1:81/). Avantaj: poți schimba interfața fără să reflash-ezi mașina.

Pas următor

Lecția 13: cum cele 2 mașini comunică între ele cu ESP-NOW pentru PvP (scut, magnet, lovituri).

După lecția asta, poți

  • Faci ESP32 să fie WiFi access point fără router (mașina e propriul ei hotspot)
  • Conduci o mașină RC din orice telefon — fără cablu, fără telecomandă specială
  • Înțelegi diferența între HTTP poll (la 100ms) și WebSocket push (instant)