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):
scp -r orangepi-server/ orangepi@<ORANGE_PI_IP>:/home/orangepi/
2. Cài đặt Python dependencies
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ị):
pip3 install -r requirements.txt
Không cần
python3-tknữ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:
#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
# 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:
# 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://<OrangePi_IP>: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://<IP_OrangePi>: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 / ENNú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
- Trạng thá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
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:
[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 đó:
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_MAXtrongstatic/app.js - Mặc định zones: sửa
DEFAULT_ZONEStrongstatic/app.jsvàSTATE.zones[0..2]trongvisualizer.py
7. Troubleshooting
Không mở được trang web
- Ping Orange Pi:
ping <OrangePi_IP> - Kiểm tra port đang mở:
ss -tulpn | grep 8000 - Tường lửa?
sudo ufw allow 8000/tcphoặc tạm thờisudo ufw disable - Visualizer đang chạy?
ps aux | grep visualizer
Không thấy dữ liệu radar (không có target)
- Kiểm tra trạng thái ESP32 (chip màu đỏ/xanh trên cùng bên phải trang web)
- Ping ESP32:
ping 192.168.1.100 - Test REST ESP32:
curl http://192.168.1.100/status→ phải có JSON{"status":"ok",...} - ESP32 đang DIAGNOSTIC_MODE 1? → phải là
0mới có WiFi/WebSocket - Parser ESP32 vẫn là stub? → Target luôn
INVALIDnếu parser chưa triển khai (thông báo ở README gốc)
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.
- Đảm bảo Orange Pi và ESP32 có tín hiệu WiFi tốt.
WebSocket không kết nối (trình duyệt báo lỗi)
- Mở DevTools (F12) → Console xem lỗi chi tiết
- Kiểm tra trình duyệt truy cập đúng
http://<IP>:8000(không phảihttps://) - 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:
scp -r orangepi-server/ orangepi@<ORANGE_PI_IP>:/home/orangepi/
2. Install Python dependencies
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):
pip3 install -r requirements.txt
No
python3-tkneeded — everything runs in the browser.
3. Usage
3.1 Flash ESP32 in Full Stage mode
In config.h (Arduino IDE):
#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
source venv/bin/activate # if using venv
python3 visualizer.py 192.168.1.100
CLI options:
# 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://<OrangePi_IP>:8000/ws
==============================================================
3.3 Open the web UI on any device
On any device on the same LAN (phone, laptop, desktop…) open:
http://<OrangePi_IP>: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 / ENLanguage 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,
OCCUPIEDbadge - Empty → faint fill, dashed border,
EMPTYbadge
- 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/updateZoneswith 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
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:
[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:
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_MAXconstants instatic/app.js - Default zones: modify
DEFAULT_ZONESinstatic/app.jsandSTATE.zones[0..2]invisualizer.py
7. Troubleshooting
Can't open the web page
- Ping Orange Pi:
ping <OrangePi_IP> - Check open port:
ss -tulpn | grep 8000 - Firewall?
sudo ufw allow 8000/tcp - Is visualizer running?
ps aux | grep visualizer
No radar targets visible
- Check ESP32 connection status chip (top-right corner of UI)
- Ping ESP32:
ping 192.168.1.100 - Test ESP32 REST:
curl http://192.168.1.100/status→ JSON expected - ESP32 still in
DIAGNOSTIC_MODE 1? → set to0for WiFi/WebSocket - ESP32 parser is still stubbed? → Targets always
INVALIDuntil 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://<IP>:8000(nothttps://) - 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 |