Skip to content

Latest commit

 

History

History
132 lines (111 loc) · 6.73 KB

File metadata and controls

132 lines (111 loc) · 6.73 KB

Project Specification: ESP32 Vue-Controlled Robot Car

1. Project Description & Framework Rationale

This project aims to build an ESP32-based robot car controlled remotely via a smartphone's web browser. To avoid the 2.4GHz interference of running multiple ESP32 Access Points in a single room during a workshop, the cars will connect as clients (Station Mode) to a central, offline router.

Instead of serving raw, hardcoded HTML from the ESP32, we are using a modern decoupled web development approach:

  • Frontend Framework (Vue 3 + Vite): Allows for rapid UI development, hot-reloading, and component-based architecture. Vite will compile the app into highly optimized static files.
  • Joystick Library (Nipple.js): A lightweight, zero-dependency vanilla JavaScript library specifically designed to handle multi-touch virtual joysticks and output x/y and angle/force data.
  • Backend Server (ESPAsyncWebServer): Runs on the ESP32 to serve the web files and manage traffic asynchronously without blocking the motor control loop.
  • Communication (WebSockets): Replaces traditional HTTP GET requests to provide a persistent, low-latency, two-way data stream (running at ~20Hz) for highly responsive motor control.
  • Storage (LittleFS + GZIP): The compiled Vue application will be compressed into .gz files and stored in the ESP32's 16MB Flash memory, ensuring fast transfer speeds over the local Wi-Fi.

2. Project Phases

Phase 1: Local Development (Hot Reloading)

  • Setup: The ESP32 is flashed with firmware and connects to the developer's home Wi-Fi. It acts only as a WebSocket server and motor controller.
  • Workflow: Engineers run the Vue app locally on their PC (npm run dev). The Vue app's WebSocket connection is hardcoded to target the ESP32's local IP address (e.g., ws://192.168.1.100/ws).
  • Benefit: Engineers can edit the Vue UI and see instant updates in their browser (Hot Module Replacement) while still driving the physical car in real-time, eliminating the need to reflash the ESP32 to test UI changes.

Phase 2: Production / Demo Day (Air-Gapped)

  • Setup: The Vue app is built (npm run build), gzipped, and uploaded directly to the ESP32's LittleFS. The ESP32 firmware is updated with the credentials of the workshop's central router.
  • Workflow: At the workshop, the ESP32 connects to the central router. Users connect their phones to the same router and navigate to the ESP32's IP address (or mDNS hostname like http://car1.local).
  • Execution: The ESP32 serves the gzipped Vue application to the phone. The Vue app uses a dynamic WebSocket URL (ws://${window.location.hostname}/ws) to seamlessly connect back to whichever ESP32 served it.

3. Software Specifications and Architecture

  • Microcontroller: ESP32-S3 (MCN16R8) - 16MB Flash, 8MB PSRAM.
    • Compiler settings: Flash: 16MB, PSRAM: OPI PSRAM.
  • Motor Driver: (e.g., L298N, TB6612FNG, or MX1508) controlled via ESP32 hardware PWM (LEDC).
  • ESP32 Dependencies (C++):
    • WiFi.h (Standard Wi-Fi library)
    • ESPAsyncWebServer (For serving files and WebSocket handling)
    • AsyncTCP (Required by ESPAsyncWebServer)
    • LittleFS (File system for serving .gz web files)
    • ArduinoJson (For parsing incoming joystick payloads)
  • Frontend Dependencies (Node/npm):
    • vue
    • vite
    • nipplejs

4. Software File Structure

The repository should be split into two distinct environments to keep the frontend build process separate from the C++ firmware.

/project-root
│
├── /esp32_firmware                 # PlatformIO or Arduino IDE project
│   ├── /data                       # Files here get uploaded to LittleFS
│   │   ├── index.html.gz           # Copied from web_app/dist after build
│   │   ├── assets/
│   │   │   ├── index-[hash].js.gz
│   │   │   └── index-[hash].css.gz
│   ├── /src
│   │   ├── main.cpp                # Main loop, Wi-Fi setup, Web server setup
│   │   ├── motors.cpp              # PWM initialization and movement logic
│   │   └── motors.h
│   └── platformio.ini              # Build configurations and library dependencies
│
└── /web_app                        # Vue + Vite frontend project
    ├── /public                     # Static assets
    ├── /src
    │   ├── /components
    │   │   └── Joystick.vue        # Wrapper for Nipple.js
    │   ├── App.vue                 # Main application view and WebSocket logic
    │   └── main.js                 # Vue initialization
    ├── package.json                # npm dependencies
    └── vite.config.js              # Vite build config (add gzip plugin here)

5. Interfaces Between Components

The core interface between the frontend and backend is a WebSocket connection transmitting JSON payloads.

WebSocket Connection Logic (Frontend)

The Vue app must automatically determine its environment to route WebSockets correctly:

// Example implementation for App.vue
const dev_esp32_ip = "192.168.1.100"; // Used only during Phase 1
const gateway = window.location.hostname === 'localhost' 
  ? `ws://${dev_esp32_ip}/ws` 
  : `ws://${window.location.hostname}/ws`; // Used during Phase 2

const websocket = new WebSocket(gateway);

JSON Payload Specification

Data is transmitted from the phone to the ESP32 at roughly 10-20Hz while the user is actively touching the joystick.

Directional Payload:

{
  "type": "drive",
  "x": 45,
  "y": 100
}
  • x: Integer representing left/right axis (e.g., -100 to 100).
  • y: Integer representing forward/reverse axis (e.g., -100 to 100).

Stop Payload: Sent exactly once when the user releases the screen to immediately halt the motors.

{
  "type": "stop"
}

6. Data Flow Graph

sequenceDiagram
    participant User as User (Smartphone)
    participant Vue as Vue App (Browser)
    participant WS as WebSocket (Network)
    participant ESP as ESP32 (AsyncServer)
    participant Motor as Motor Controller

    User->>Vue: Drags Virtual Joystick
    Vue->>Vue: Nipple.js calculates X/Y (e.g., X:0, Y:100)
    Vue->>WS: Send JSON: {"type":"drive", "x":0, "y":100}
    WS->>ESP: Trigger WebSocket Event (Data Rx)
    ESP->>ESP: ArduinoJson parses payload
    ESP->>ESP: Map X/Y to Left/Right PWM speeds
    ESP->>Motor: Apply PWM signals to GPIO pins
    Motor->>User: Physical car moves forward
    
    User->>Vue: Releases Joystick
    Vue->>WS: Send JSON: {"type":"stop"}
    WS->>ESP: Trigger WebSocket Event (Data Rx)
    ESP->>Motor: Set all PWM to 0 (Brake)
Loading