# ESP32-S3 LD2453 Radar — Web Visualizer cho Orange Pi > **English version** below / Xem bản tiếng Anh ở cuối file. Hướng dẫn cài đặt và sử dụng **Web Visualizer** trên Orange Pi (hoặc máy tính bất kỳ có Python 3.8+) để hiển thị dữ liệu radar realtime dưới dạng giao diện web đẹp trên trình duyệt. > ✅ **Không cần giao diện desktop (Tkinter/Pygame) nữa.** Visualizer chạy nền dạng Web Server (FastAPI), bạn chỉ cần mở trình duyệt (trên điện thoại, laptop, PC…) là xem được đồ họa. --- ## 1. Chuẩn bị ### 1.1 Yêu cầu hệ thống - Orange Pi / Raspberry Pi / PC bất kỳ có **Python 3.8+** - Cùng mạng LAN với ESP32 (WiFi hoặc cổng LAN) - Biết IP của ESP32 (xem Serial Monitor khi boot, hoặc vào router xem) ### 1.2 Cấu trúc thư mục ``` orangepi-server/ ├── visualizer.py # Web Server (FastAPI): kết nối ESP32 + broadcast tới trình duyệt ├── requirements.txt # Python dependencies ├── templates/ │ └── index.html # Giao diện Web (HTML) └── static/ ├── style.css # Giao diện (CSS): sáng/tối, responsive, song ngữ └── app.js # Canvas render + WebSocket client trong trình duyệt ``` ### 1.3 Copy file lên Orange Pi Copy cả thư mục `orangepi-server/` lên Orange Pi (clone repo, SCP, USB…). Ví dụ SCP (chạy từ máy bạn): ```bash scp -r orangepi-server/ orangepi@:/home/orangepi/ ``` --- ## 2. Cài đặt Python dependencies ```bash cd ~/orangepi-server # (Tùy chọn nhưng khuyến nghị) Dùng virtual env python3 -m venv venv source venv/bin/activate # Cài dependencies pip install -r requirements.txt ``` **Cài global (nhanh hơn, không khuyến nghị):** ```bash pip3 install -r requirements.txt ``` > Không cần `python3-tk` nữa — toàn bộ chạy trên web. --- ## 3. Sử dụng ### 3.1 Bật ESP32 ở chế độ Full Stage Trong `config.h` trên Arduino IDE: ```cpp #define DIAGNOSTIC_MODE 0 // Full mode: WiFi + WebSocket + REST API ``` Nạp code, mở Serial Monitor lấy IP ESP32. Ví dụ: `192.168.1.100` ### 3.2 Khởi động Web Visualizer trên Orange Pi ```bash # Nếu dùng venv: source venv/bin/activate # Chạy (thay bằng IP ESP32 của bạn): python3 visualizer.py 192.168.1.100 ``` Các tùy chọn lệnh: ```bash # Tùy chỉnh port (mặc định 8000): python3 visualizer.py 192.168.1.100 --port 8080 # Giới hạn truy cập chỉ local (không cho từ ngoài): python3 visualizer.py 192.168.1.100 --host 127.0.0.1 # Log chi tiết: python3 visualizer.py 192.168.1.100 --verbose ``` Màn hình console sẽ hiển thị: ``` ============================================================== ESP32-S3 LD2453 Radar — Web Visualizer ============================================================== ESP32 : ws://192.168.1.100/ws Web UI : http://0.0.0.0:8000/ WS UI : ws://:8000/ws ============================================================== ``` ### 3.3 Mở trình duyệt xem kết quả Trên **bất kỳ thiết bị nào cùng LAN** (điện thoại, laptop, PC…): ``` http://:8000/ ``` Ví dụ: Orange Pi có IP `192.168.1.50` → mở `http://192.168.1.50:8000/` --- ## 4. Tính năng giao diện Web ### 4.1 Tổng quan | Tính năng | Mô tả | |---|---| | 🎨 **Giao diện đẹp** | Chủ đề sáng/tối (Light/Dark), màu sắc theo chuẩn Catppuccin | | 🌐 **Song ngữ** | Bật/tắt Tiếng Việt / English ngay trên header | | 📱 **Responsive** | Xem tốt trên cả mobile, tablet, desktop | | 🧭 **Radar Canvas** | Hiển thị radar, grid 1m, vòng tròn khoảng cách, hiệu ứng quét (sweep) | | 🎯 **Targets** | Lên đến 3 target với màu khác nhau, đường dẫn (trail), mũi tên tốc độ, nhãn tọa độ | | 🟩 **Zones** | 3 vùng hình chữ nhật với màu & trạng thái (Có người / Trống) | | 📊 **Thống kê** | Frames nhận, target hợp lệ, vùng có người, cập nhật lần cuối | | 📝 **Chi tiết** | Thông tin chi tiết từng target & zone ở panel bên phải | | ✏️ **Sửa zones** | Chỉnh tọa độ zones ngay trên web, tự động gửi cập nhật tới ESP32 | | 🔗 **Multi client** | Nhiều trình duyệt xem cùng lúc, dữ liệu đồng bộ realtime | ### 4.2 Header - 🟢 / 🟡 / 🔴 Trạng thái kết nối **ESP32** và **WebSocket** - ☀️ / 🌙 Nút chuyển Sáng / Tối - `VI / EN` Nút chuyển ngôn ngữ ### 4.3 Radar View (Canvas) - **Grid**: Mỗi ô = 1 mét, có đánh số (−5m ← 0 → +5m ngang, 0 → 7m thẳng) - **Vòng tròn khoảng cách**: Các vòng tròn nét đứt cách nhau 1m tính từ radar - **Trục Y chính**: Nét đứt màu xanh, chỉ hướng thẳng từ radar - **Radar**: Vị trí radar ở giữa cạnh dưới (điểm đỏ, ghi chữ RADAR) - **Hiệu ứng quét**: Cảm giác radar thực tế, có thể tắt trong công cụ - **Zones (3 hình chữ nhật)**: - Zone 1 (Xanh lá), Zone 2 (Cam), Zone 3 (Đỏ) - Có người → Màu đậm, nét liền, badge `CÓ NGƯỜI` - Trống → Màu nhạt, nét đứt, badge `TRỐNG` - **Targets (3 điểm)**: - Màu: Vàng (Slot 1), Tím (Slot 2), Xanh (Slot 3) - Điểm sáng có viền trắng, bao quanh là hiệu ứng glow - Mũi tên: Tốc độ (độ dài tỉ lệ với cm/s, hướng theo chuyển động) - Nhãn: `T# | X,Y mm | tốc độ cm/s` - Đường dẫn (trail): Mờ dần theo thời gian (tối đa 3.5s) - Target vừa mất dấu: Hiển thị viền đứt mờ trong 3.5s nữa ### 4.4 Công cụ trên Canvas (bên phải tiêu đề) - ✅ **Hiệu ứng quét** — Bật/tắt hiệu ứng quét radar - ✅ **Đường dẫn target** — Bật/tắt trail fade - ✅ **Hiển thị label** — Bật/tắt nhãn trên zones & targets ### 4.5 Panel bên phải **Thống kê:** - Frames nhận (tổng cộng) - Cập nhật cách đây bao nhiêu giây - Target hợp lệ / 3 - Vùng có người / 3 **Chi tiết Target:** - Mỗi target có một thẻ màu, hiển thị: - Trạng thái: `Đã phát hiện` / `Vừa mất dấu` / `Trống` - `X, Y (mm)` - `Tốc độ (cm/s)` - `Khoảng cách (m)` - `Góc (°)` — 0° = thẳng, âm = trái, dương = phải **Zones:** - Danh sách 3 zones với tọa độ - Nút **Sửa zones** mở form chỉnh sửa từng toạ độ X1/Y1/X2/Y2 (mm) - **Mặc định**: Khôi phục layout gốc - **Hủy**: Bỏ sửa - **Lưu & Áp dụng**: Gửi cập nhật POST `/api/zones` → forward tới ESP32 `/updateZones`, hiển thị toast thành công --- ## 5. Kiến trúc & luồng dữ liệu ``` ┌───────────┐ WebSocket /ws ┌──────────────────┐ WebSocket /ws ┌──────────────┐ │ ESP32-S3 │ ◄──────────────────► │ Orange Pi (WS │ ◄──────────────────► │ Trình duyệt │ │ LD2453 │ JSON frame radar │ Server FastAPI) │ JSON state đồng bộ │ (Canvas 2D) │ └───────────┘ └──────────────────┘ └──────────────┘ ▲ ▲ ▲ │ REST GET /zones │ GET / → trả về index.html │ │ REST POST /updateZones │ GET /static/* → CSS, JS │ └───────────────────────────────────┘ REST API /api/* │ Visualizer tự động gọi ESP32 khi bạn chỉnh zones trên web │ ``` **Các endpoint chính:** | Method | Endpoint | Mục đích | |---|---|---| | GET | `/` | Trả về trang web chính | | GET | `/api/state` | Lấy trạng thái radar hiện tại (JSON) | | GET | `/api/zones` | Lấy danh sách zones | | POST | `/api/zones` | Cập nhật zones (forward tới ESP32 `/updateZones`) | | WS | `/ws` | Endpoint WebSocket cho trình duyệt (broadcast realtime) | --- ## 6. Tùy chỉnh & chạy nền ### 6.1 Chạy nền bằng tmux ```bash tmux new -s radar cd ~/orangepi-server && source venv/bin/activate python3 visualizer.py 192.168.1.100 # Ctrl+B D → detach (thoát ra mà vẫn chạy) # tmux attach -t radar → quay lại session ``` ### 6.2 systemd service (tự chạy khi boot Orange Pi) Tạo file `/etc/systemd/system/radar-web.service`: ```ini [Unit] Description=LD2453 Radar Web Visualizer After=network-online.target Wants=network-online.target [Service] Type=simple User=orangepi WorkingDirectory=/home/orangepi/orangepi-server ExecStart=/home/orangepi/orangepi-server/venv/bin/python3 /home/orangepi/orangepi-server/visualizer.py 192.168.1.100 --port 8000 Restart=always RestartSec=5 Environment=PYTHONUNBUFFERED=1 [Install] WantedBy=multi-user.target ``` Sau đó: ```bash sudo systemctl daemon-reload sudo systemctl enable radar-web sudo systemctl start radar-web journalctl -u radar-web -f # Xem log realtime ``` ### 6.3 Tùy chỉnh giao diện - **Màu sắc & chủ đề**: chỉnh các biến CSS ở đầu file `static/style.css` (định nghĩa `--z1`, `--t1`, `--bg`…) - **Range hiển thị**: chỉnh hằng `MAP.X_MIN/X_MAX/Y_MIN/Y_MAX` trong `static/app.js` - **Mặc định zones**: sửa `DEFAULT_ZONES` trong `static/app.js` và `STATE.zones[0..2]` trong `visualizer.py` --- ## 7. Troubleshooting — Kiểm tra lỗi nhanh ### 7.1 Quick check trong 60 giây Nếu backend log "Đã kết nối WebSocket tới ESP32" nhưng web không thấy dữ liệu, làm theo thứ tự: 1. **Mở DevTools (F12) → tab Console**: - Gõ: `await fetch('/api/state').then(r=>r.json()).then(d=>console.log(d))` - Kết quả đúng phải là object có `zones` và `targets` (3 phần tử mỗi mảng) - Nếu nhận được object: WebSocket frontend đang có vấn đề (hãy xem log có chữ `[Radar] WebSocket closed?`) - Nếu không nhận được: backend có vấn đề (xem log terminal backend) 2. **Xem 2 chip trạng thái trên header (góc trên phải)**: - `ESP32: Đã kết nối` (xanh) — backend ↔ ESP32 OK - `WS: Đã kết nối` (xanh) — trình duyệt ↔ backend OK - Nếu WS màu đỏ: mở DevTools → Network → WS → chọn `/ws` → xem "Messages" có dòng nào không. 3. **Hiển thị gì phải có ngay cả khi parser ESP32 là stub?** - Grid, vòng tròn khoảng cách, radar (điểm đỏ dưới cùng), **3 zones theo mặc định phải hiện rõ ràng** - Chip `ESP32` phải xanh, `Frames` trong Thống kê = 0 là bình thường (parser stub) - **Target không thấy là Bình thường** nếu parser chưa implement — nhưng zones & grid PHẢI hiện. --- ### 7.2 Không mở được trang web 1. **Ping Orange Pi**: `ping ` 2. **Kiểm tra port đang mở**: `ss -tulpn | grep 9000` (thay `9000` bằng port bạn dùng) 3. **Tường lửa?** `sudo ufw allow 9000/tcp` hoặc tạm thời `sudo ufw disable` 4. **Visualizer đang chạy?** `ps aux | grep visualizer` --- ### 7.3 Không thấy dữ liệu radar / không có target 1. **Kiểm tra 2 chip trạng thái** (section 7.1) 2. **Ping ESP32**: `ping 192.168.88.32` (thay IP của bạn) 3. **Test REST ESP32**: `curl http://192.168.88.32/status` → phải trả JSON `{"status":"ok",...}` 4. **Test state từ backend (ngoài trình duyệt)**: `curl http://:9000/api/state` → object JSON 2 mảng zones + targets 5. **ESP32 đang `DIAGNOSTIC_MODE 1`?** → phải là `0` mới có WiFi/WebSocket 6. **Parser ESP32 vẫn là stub?** → **Target luôn = 0 / INVALID là BÌNH THƯỜNG** cho đến khi bạn implement `tryParseFrame()` thật trong `LD2453Parser.cpp`. Grid + zones vẫn phải hiện. --- ### 7.4 Tốc độ cập nhật chậm - Mặc định radar LD2453 gửi ~2 frame/giây. Đó là giới hạn phần cứng. - **Bảo đảm Orange Pi gửi state liên tục**: heartbeat 1 giây luôn gửi ngay cả khi ESP32 không gửi frame mới. - Đảm bảo Orange Pi và ESP32 có tín hiệu WiFi tốt. --- ### 7.5 WebSocket không kết nối (trình duyệt báo lỗi) - Mở DevTools (F12) → **Console** sẽ log rõ ràng: `[Radar] WebSocket closed (code=...)` - `code=1006`: mạng bị ngắt giữa chừng → tự động reconnect 2s sau - `code=403/404`: mở sai port hoặc sai path - Kiểm tra trình duyệt truy cập đúng `http://:9000` (**KHÔNG** `https://`) - Nếu mở trên **mobile cùng WiFi** mà không được: tắt "VPN / Private DNS / Use random MAC" trên điện thoại thử lại - Nếu dùng reverse proxy (Caddy/Nginx): cấu hình WebSocket upgrade (header `Upgrade: websocket`) --- ## 8. So sánh với bản cũ (Tkinter/Pygame) | Đặc điểm | Tkinter cũ | **Web Visualizer mới** | |---|---|---| | Cần màn hình gắn Orange Pi? | ✅ Có | ❌ **Không** (xem trên trình duyệt bất kỳ đâu) | | Xem trên điện thoại? | ❌ Không | ✅ **Có** | | Nhiều người xem cùng lúc? | ❌ 1 người | ✅ **Không giới hạn** (multi-client) | | Đồ họa đẹp? | ⭐⭐ cơ bản | ⭐⭐⭐⭐⭐ **Canvas2D + hiệu ứng + sáng/tối** | | Chỉnh sửa zones? | Edit code Python | ✅ **Form trên web** | | Song ngữ VI/EN? | ❌ Không | ✅ **Có** | | Responsive mobile? | ❌ Không | ✅ **Có** | --- --- # 🇬🇧 English Version # ESP32-S3 LD2453 Radar — Orange Pi Web Visualizer Installation and usage guide for the **Web Visualizer** on Orange Pi (or any machine with Python 3.8+) to display realtime radar data via a beautiful web interface — **no Tkinter/Pygame desktop required anymore**. --- ## 1. Requirements - Orange Pi / Raspberry Pi / Any PC with **Python 3.8+** - Same LAN as the ESP32 (Wi-Fi or Ethernet) - ESP32 IP address known (check Serial Monitor at boot, or router admin) ### 1.1 Folder structure ``` orangepi-server/ ├── visualizer.py # FastAPI server: connects to ESP32 + broadcasts to browsers ├── requirements.txt # Python dependencies ├── templates/ │ └── index.html # Web UI HTML └── static/ ├── style.css # Styling: light/dark, responsive, bilingual └── app.js # Canvas renderer + browser WebSocket client ``` ### 1.2 Copy files to Orange Pi Copy the whole `orangepi-server/` folder to the Orange Pi. SCP example: ```bash scp -r orangepi-server/ orangepi@:/home/orangepi/ ``` --- ## 2. Install Python dependencies ```bash cd ~/orangepi-server # (Recommended) Use a virtual env python3 -m venv venv source venv/bin/activate # Install dependencies pip install -r requirements.txt ``` **Global install (faster, not recommended):** ```bash pip3 install -r requirements.txt ``` > No `python3-tk` needed — everything runs in the browser. --- ## 3. Usage ### 3.1 Flash ESP32 in Full Stage mode In `config.h` (Arduino IDE): ```cpp #define DIAGNOSTIC_MODE 0 // Full mode: WiFi + WebSocket + REST API ``` Flash the code, grab the ESP32 IP from Serial Monitor. Example: `192.168.1.100`. ### 3.2 Start the Web Visualizer on Orange Pi ```bash source venv/bin/activate # if using venv python3 visualizer.py 192.168.1.100 ``` CLI options: ```bash # Custom port (default 8000): python3 visualizer.py 192.168.1.100 --port 8080 # Restrict to localhost only: python3 visualizer.py 192.168.1.100 --host 127.0.0.1 # Verbose logging: python3 visualizer.py 192.168.1.100 --verbose ``` The console will print: ``` ============================================================== ESP32-S3 LD2453 Radar — Web Visualizer ============================================================== ESP32 : ws://192.168.1.100/ws Web UI : http://0.0.0.0:8000/ WS UI : ws://:8000/ws ============================================================== ``` ### 3.3 Open the web UI on any device On **any device on the same LAN** (phone, laptop, desktop…) open: ``` http://:8000/ ``` Example: if Orange Pi IP is `192.168.1.50` → `http://192.168.1.50:8000/` --- ## 4. Web UI features ### 4.1 Highlights | Feature | Description | |---|---| | 🎨 **Beautiful UI** | Light/Dark themes with Catppuccin color palette | | 🌐 **Bilingual** | Vietnamese / English toggle in header | | 📱 **Responsive** | Looks great on mobile, tablet, desktop | | 🧭 **Radar Canvas** | Grid, concentric range rings, sweep animation | | 🎯 **Targets** | Up to 3 colored targets, trails, velocity arrows, labels | | 🟩 **Zones** | 3 rectangular zones with occupancy state badges | | 📊 **Stats** | Frames received, valid targets, occupied zones, latency | | 📝 **Details** | Per-target and per-zone info in the right panel | | ✏️ **Zone editor** | Edit zone coordinates directly in the UI → pushes to ESP32 | | 🔗 **Multi-client** | Any number of browser tabs/screens stay in sync | ### 4.2 Header - 🟢/🟡/🔴 ESP32 + WebSocket connection status chips - ☀️/🌙 Theme toggle (Light/Dark) - `VI / EN` Language toggle ### 4.3 Radar View (Canvas) - **Grid**: 1m cells with labels (−5m ← 0 → +5m horizontal, 0 → 7m forward) - **Range rings**: Dashed concentric arcs every 1m from the radar - **Radar dot**: Red circle at bottom-center with "RADAR" label - **Sweep effect**: Rotating semi-transparent cone (toggleable) - **Zones (3 rectangles)**: - Z1 (Green), Z2 (Orange), Z3 (Red) - Occupied → solid fill, bold border, `OCCUPIED` badge - Empty → faint fill, dashed border, `EMPTY` badge - **Targets (3 slots)**: - Colors: Yellow (S1), Purple (S2), Cyan (S3) - Glowing dots with white outline - Velocity arrow (length ∝ cm/s, points along direction) - Label: `T# | X,Y mm | speed cm/s` - Fading trail for each target, persists ~3.5s - Recently-lost targets show as dashed ghost circles ### 4.4 Tool toggles (top-right of canvas) - Sweep effect on/off - Target trails on/off - Zone/target labels on/off ### 4.5 Side panels (right column) **Statistics:** - Total frames received - Seconds since last frame - Valid targets / 3 - Occupied zones / 3 **Target details:** - Colored cards per target with status badge (`Detected`/`Recently seen`/`Free`) - X/Y (mm), Speed (cm/s), Distance (m), Angle (°) **Zones:** - Per-zone list with coordinates and status - **Edit zones** button opens a form (X1/Y1/X2/Y2 in mm): - *Reset defaults* → reload original layout - *Cancel* → close without saving - *Save & Apply* → POST to `/api/zones` → forwarded to ESP32 `/updateZones` with success toast --- ## 5. Architecture & data flow ``` ┌───────────┐ WebSocket /ws ┌──────────────────┐ WebSocket /ws ┌──────────────┐ │ ESP32-S3 │ ◄──────────────────► │ Orange Pi (WS │ ◄──────────────────► │ Browser │ │ LD2453 │ JSON radar frames │ FastAPI Server) │ synced state JSON │ (Canvas 2D) │ └───────────┘ └──────────────────┘ └──────────────┘ ▲ ▲ ▲ │ REST /zones /updateZones │ GET / → index.html │ └───────────────────────────────────┘ GET /static/* → CSS, JS │ Zone edits in the UI are forwarded to the ESP32 │ ``` **Endpoints:** | Method | Endpoint | Purpose | |---|---|---| | GET | `/` | Main web page | | GET | `/api/state` | Current radar state (fallback poll) | | GET | `/api/zones` | Zones list | | POST | `/api/zones` | Update zones (forwarded to ESP32 `/updateZones`) | | WS | `/ws` | Realtime broadcast for browser clients | --- ## 6. Background running & customization ### 6.1 Run with tmux ```bash tmux new -s radar cd ~/orangepi-server && source venv/bin/activate python3 visualizer.py 192.168.1.100 # Ctrl+B D → detach; tmux attach -t radar to return ``` ### 6.2 systemd service (auto-start at boot) Create `/etc/systemd/system/radar-web.service`: ```ini [Unit] Description=LD2453 Radar Web Visualizer After=network-online.target Wants=network-online.target [Service] Type=simple User=orangepi WorkingDirectory=/home/orangepi/orangepi-server ExecStart=/home/orangepi/orangepi-server/venv/bin/python3 /home/orangepi/orangepi-server/visualizer.py 192.168.1.100 --port 8000 Restart=always RestartSec=5 Environment=PYTHONUNBUFFERED=1 [Install] WantedBy=multi-user.target ``` Then: ```bash sudo systemctl daemon-reload sudo systemctl enable radar-web sudo systemctl start radar-web journalctl -u radar-web -f # Live logs ``` ### 6.3 Customization tips - **Colors/themes**: edit CSS variables at top of `static/style.css` (`--z1`, `--t1`, `--bg`…) - **Display range**: tweak `MAP.X_MIN/X_MAX/Y_MIN/Y_MAX` constants in `static/app.js` - **Default zones**: modify `DEFAULT_ZONES` in `static/app.js` and `STATE.zones[0..2]` in `visualizer.py` --- ## 7. Troubleshooting ### Can't open the web page 1. Ping Orange Pi: `ping ` 2. Check open port: `ss -tulpn | grep 8000` 3. Firewall? `sudo ufw allow 8000/tcp` 4. Is visualizer running? `ps aux | grep visualizer` ### No radar targets visible 1. Check ESP32 connection status chip (top-right corner of UI) 2. Ping ESP32: `ping 192.168.1.100` 3. Test ESP32 REST: `curl http://192.168.1.100/status` → JSON expected 4. ESP32 still in `DIAGNOSTIC_MODE 1`? → set to `0` for WiFi/WebSocket 5. ESP32 parser is still stubbed? → Targets always `INVALID` until real byte parser is implemented (see main repo README) ### Slow UI updates - LD2453 radar sends ~2 frames/second by design (hardware limit) - Ensure good Wi-Fi signal on both ESP32 and Orange Pi ### Browser WebSocket fails to connect - Open DevTools → Console for the exact error - Make sure you visit `http://:8000` (not `https://`) - If behind Caddy/Nginx: enable WebSocket upgrade headers (`Upgrade: websocket`) --- ## 8. Comparison with old Tkinter/Pygame version | Aspect | Old Tkinter | **New Web Visualizer** | |---|---|---| | Requires a monitor on Orange Pi? | Yes | ❌ **No** — view anywhere | | View on phone? | No | ✅ **Yes** | | Multiple viewers? | No | ✅ **Unlimited clients** | | Graphics quality | Basic ⭐⭐ | **Canvas + effects + themes ⭐⭐⭐⭐⭐** | | Zone editor | Edit Python | ✅ **In-UI form** | | i18n (VI/EN) | No | ✅ **Built-in** | | Mobile responsive | No | ✅ **Yes** |