Files
esp32s3-ld2453/README.md
T
2026-09-11 20:23:12 +07:00

274 lines
13 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# ESP32-S3 + HLK-LD2453 — Hệ thống phát hiện người theo vùng
> **Phiên bản tiếng Anh** xem tại: [README-en.md](README-en.md)
Dự án này được chuyển giao (port) từ [thanhtantran/ESP32-LD2450](https://github.com/thanhtantran/ESP32-LD2450), nhắm đến phần cứng:
- **Vi điều khiển:** ESP32-S3 (Arduino framework, dùng Arduino IDE thuần — không dùng PlatformIO)
- **Radar:** Hi-Link **HLK-LD2453** (24 GHz, 1TX+2RX, theo dõi tối đa 3 mục tiêu cùng lúc)
---
## ⚠️ Trạng thái hiện tại: Parser radar đang là bản tạm (stub)
Việc giải mã frame ở mức byte trong `LD2453Parser.cpp` **chưa được triển khai**.
Hiện tại chưa tìm được nguồn tin cậy cho phần "Giao tiếp giao thức" trong tài liệu chính thức của HLK-LD2453 (header/tail frame, bố cục byte cho mỗi target, quy ước dấu, mã hóa tốc độ) — xem mục "Xác nhận giao thức" bên dưới. Mọi thành phần khác (Wi-Fi, REST API, WebSocket, logic vùng, đọc UART không chặn, ghi lại raw hex) đều đã triển khai đầy đủ và sẵn sàng hoạt động.
---
## Những gì được kế thừa từ dự án ESP32-LD2450 gốc
- Mô hình vùng (zone): tối đa 3 hình chữ nhật theo đơn vị milimét, các endpoint REST `GET /zones` / `POST /updateZones`, dùng `ArduinoJson` cho (giải)tuần tự — xem `ZoneManager.cpp`.
- Mẫu broadcast WebSocket (`AsyncWebSocket` trên `/ws`, gọi `ws.textAll()` mỗi frame) — xem `WebServerManager.cpp`.
- Cấu trúc tổng thể `setup()` / `loop()` và cơ chế tự kết nối lại Wi-Fi.
---
## Những gì đã thay đổi so với bản gốc
- Bỏ thư viện phụ thuộc `rbegamer/HLK-LD2450` của LD2450; việc parse giờ được thực hiện bởi `LD2453Parser` tự viết, vì định dạng byte của cảm biến này khác biệt.
- Từ `Serial2` với chân cố định → `HardwareSerial` trên chân **có thể cấu hình** (`RADAR_RX_PIN` / `RADAR_TX_PIN` trong `config.h`).
- Loại bỏ hack "mở lại cảm biến khi lỗi" dựa trên `delay()` chặn; việc đọc radar giờ hoạt động theo kiểu máy trạng thái buffer vòng (ring-buffer) không chặn, chấp nhận các frame bị lỗi hoặc không đầy đủ mà không bao giờ gọi `delay()` trên đường đọc dữ liệu.
- Mỗi frame giờ mang theo `node_id` và `sequence_number`, giúp nút này sau này có thể kết nối vào hệ thống đa radar (`RADAR_A/B/C` → UDP → Orange Pi) mà không cần chỉnh sửa parser nữa.
- Từ dự án PlatformIO `.cpp`/`lib_deps` → thư mục sketch chuẩn của Arduino IDE.
---
## Sơ đồ nối dây
### Radar LD2453
```
LD2453 ESP32-S3
V -> 3V3
G -> GND
T/TX -> RADAR_RX_PIN (mặc định GPIO18)
R/RX -> RADAR_TX_PIN (mặc định GPIO17)
```
### Màn hình OLED I2C 128x64
```
OLED ESP32-S3
VCC -> 3V3
GND -> GND
SCL -> GPIO7 (OLED_SCL_PIN)
SDA -> GPIO6 (OLED_SDA_PIN)
```
Hãy thay đổi `RADAR_RX_PIN` / `RADAR_TX_PIN` trong `config.h` cho phù hợp với cách nối dây của bạn — bất kỳ hai GPIO trống nào hỗ trợ UART trên ESP32-S3 đều dùng được, vì Arduino core của ESP32 có thể remap chân UART bằng phần mềm.
**Không** dùng SoftwareSerial — bắt buộc dùng `HardwareSerial` ở tốc độ 256000 baud.
---
## Các thư viện Arduino yêu cầu
Cài đặt qua Library Manager (tìm kiếm đúng tên):
| Thư viện | Dùng cho | Ghi chú |
|---|---|---|
| `ArduinoJson` (tác giả Benoit Blanchon) | REST API + WebSocket JSON | Phiên bản v7.x — code dùng API `JsonDocument` hiện tại (không dùng `StaticJsonDocument` đã lỗi thời) |
| `ESP Async WebServer` (ESP32Async fork) | Máy chủ HTTP + WebSocket | Chỉ cần cho giai đoạn 2 (`DIAGNOSTIC_MODE 0`) |
| `AsyncTCP` | Thư viện phụ thuộc của ESP Async WebServer | Phải dùng đúng fork/phiên bản khớp với thư viện WebServer ở trên |
| `Adafruit GFX Library` | Thư viện đồ họa cho OLED | Dùng chung với Adafruit SSD1306 |
| `Adafruit SSD1306` | Driver cho màn hình OLED 128x64 I2C | Yêu cầu nếu bật `OLED_ENABLED 1` |
Board package: **esp32 by Espressif Systems**, chọn board = model ESP32-S3 dev board cụ thể của bạn.
Đã kiểm tra biên dịch sạch (không có warning) với: esp32 core 3.3.11, ArduinoJson (JsonDocument API), ESP_Async_WebServer + Async_TCP.
---
## Cấu trúc thư mục dự án
```
esp32s3-ld2453/
├── esp32s3-ld2453/
│ ├── esp32s3-ld2453.ino — setup()/loop(), in chẩn đoán
│ ├── LD2453Parser.h/.cpp — Đọc UART + parse frame (ĐANG STUB, xem trên)
│ ├── ZoneManager.h/.cpp — Logic xác định vùng có người (hình chữ nhật)
│ ├── WebServerManager.h/.cpp — Wi-Fi, REST API, broadcast WebSocket
│ ├── OLEDDisplay.h/.cpp — Quản lý màn hình OLED I2C 128x64
│ ├── config.example.h — Copy thành config.h rồi chỉnh sửa
│ └── config.h — File cấu hình (không commit lên git)
├── orangepi-server/
│ ├── README.md — Hướng dẫn chi tiết cho Orange Pi
│ ├── visualizer.py — Script Python visualize dữ liệu radar
│ └── requirements.txt
└── README.md
```
---
## Tính năng màn hình OLED
Màn hình OLED 128x64 hiển thị các thông tin sau:
### Khi DIAGNOSTIC_MODE = 1 (Chế độ Serial Debug)
- **Màn hình khởi động**: Hiển thị logo và Node ID
- **Thông báo chế độ Diagnostic**: Thông báo đang ở chế độ chỉ dùng Serial
- **Thống kê radar mỗi 5 giây**:
- Số bytes nhận được từ LD2453
- Số frame đã parse
- Số lỗi parse
- Packets per second (PPS)
- Số target phát hiện được
### Khi DIAGNOSTIC_MODE = 0 (Chế độ WiFi đầy đủ)
- **Đang kết nối WiFi**: Hiển thị SSID đang kết nối
- **Kết nối WiFi thành công**: Hiển thị địa chỉ IP
- **Phát hiện realtime**:
- Số target phát hiện (T:0-3)
- Trạng thái 3 vùng (YES/NO)
### Cấu hình OLED trong config.h
```cpp
#define OLED_ENABLED 1 // 1 = bật OLED, 0 = tắt
#define OLED_SDA_PIN 6 // Chân SDA
#define OLED_SCL_PIN 7 // Chân SCL
#define OLED_I2C_ADDR 0x3C // Địa chỉ I2C (thường là 0x3C hoặc 0x3D)
```
**Lưu ý**: Nếu màn hình không hoạt động, hãy thử đổi `OLED_I2C_ADDR` từ `0x3C` sang `0x3D`.
---
## Giai đoạn 1: Chế độ chẩn đoán (làm bước này trước)
1. Copy file `config.example.h` thành `config.h`.
2. Giữ `DIAGNOSTIC_MODE 1` (mặc định). Lựa chọn này tắt hoàn toàn Wi-Fi và web server — chỉ dùng Serial.
3. Nạp firmware, mở Serial Monitor ở tốc độ 115200.
4. Bạn sẽ thấy:
```
LD2453 connected
UART: 256000
...
```
Các dòng thông tin target hiện tại sẽ luôn hiển thị `INVALID` — điều này là bình thường, vì parser đang là bản stub. Giai đoạn này tồn tại để bạn kiểm tra việc nối dây, tốc độ baud, và board hoạt động ổn thỏa độc lập với parser.
---
## Xác nhận giao thức
Cho đến khi format frame thực sự của `LD2453Parser` được điền vào, hãy dùng công cụ này để thu thập raw data:
1. Trong `config.h`, đặt `DEBUG_RADAR_RAW 1`.
2. Nạp firmware, mở Serial Monitor ở 115200, và làm cho LD2453 phát hiện chuyển động. Bạn sẽ thấy hex dump của mọi byte raw mà module gửi ra, ví dụ:
```
[RAW] AA FF 03 00 4B 01 36 08 0C 00 ... (len=30)
```
3. Chia sẻ dữ liệu thu được (và nếu bạn có được phần "5. Communication Protocol" trong tài liệu người dùng chính thức của HLK-LD2453 — vì hiện tại link tải của Hi-Link đang bị gán nhầm và trỏ về tài liệu LD2450) để có thể xác nhận header, tail, bố cục byte của mỗi target, và mã hóa dấu/tốc độ, từ đó điền vào hàm `tryParseFrame()` trong `LD2453Parser.cpp`. Vị trí cần chèn mã được đánh dấu bằng comment block.
---
## Giai đoạn 2: Firmware đầy đủ (sau khi parser đã được xác nhận)
1. Đặt `DIAGNOSTIC_MODE 0` trong `config.h`.
2. Điền `WIFI_SSID` / `WIFI_PASSWORD`.
3. Nạp firmware. Board sẽ kết nối Wi-Fi, khởi động HTTP server, và broadcast một tin nhắn JSON mỗi frame radar qua đường dẫn `/ws`.
### Định dạng dữ liệu WebSocket (`ws://<ESP32_IP>/ws`)
```json
{
"sensor": "LD2453",
"node_id": "RADAR_A",
"sequence": 12345,
"timestamp": 12345678,
"targets": [
{ "id": 1, "valid": true, "x": -820, "y": 2840, "speed": 12 },
{ "id": 2, "valid": true, "x": 1350, "y": 4120, "speed": -4 }
]
}
```
Giải thích các trường:
- `sensor`: Tên loại cảm biến
- `node_id`: ID của nút radar (từ `NODE_ID` trong `config.h`)
- `sequence`: Số thứ tự frame, tăng dần mỗi frame
- `timestamp`: Thời gian tính bằng millis() tại lúc hoàn thành frame
- `targets[]`: Mảng các mục tiêu phát hiện (tối đa 3):
- `id`: Vị trí *slot* của radar (1–3), **không phải** ID cố định của người — một target biến mất rồi xuất hiện lại có thể rơi vào slot khác
- `valid`: true nếu target này có dữ liệu hợp lệ
- `x`, `y`: Tọa độ theo milimét (hệ quy chiếu của radar)
- `speed`: Tốc độ theo cm/s (dấu +/− chỉ hướng)
### REST API
| Endpoint | Phương thức | Mô tả |
|---|---|---|
| `/zones` | GET | Trả về các hình chữ nhật vùng hiện tại + trạng thái có người realtime |
| `/updateZones` | POST | Cập nhật một hoặc nhiều vùng (cùng định dạng với `GET /zones`, hỗ trợ cập nhật một phần) |
| `/status` | GET | Kiểm tra sức khỏe cơ bản (thời gian hoạt động / uptime) |
Ví dụ gọi cập nhật zones bằng `curl`:
```bash
curl -X POST http://192.168.1.100/updateZones \
-H "Content-Type: application/json" \
-d '[
{"id":1,"x1":-3000,"y1":500,"x2":-1,"y2":3000},
{"id":2,"x1":1,"y1":500,"x2":3000,"y2":3000},
{"id":3,"x1":-3000,"y1":3001,"x2":3000,"y2":5000}
]'
```
---
## Orange Pi Visualizer
Thư mục `orangepi-server/` chứa script Python để nhận dữ liệu từ ESP32 qua WebSocket và hiển thị realtime trên Orange Pi (hoặc máy tính bất kỳ có Python 3.8+).
### Tính năng chính
- Kết nối WebSocket tới ESP32, nhận frame radar realtime
- Vẽ vùng (3 hình chữ nhật) và vị trí các target (tối đa 3)
- Hiển thị tọa độ, tốc độ, khoảng cách, góc của từng target
- Hỗ trợ 2 chế độ:
- **GUI (Tkinter)**: Có đồ họa, cần màn hình
- **Headless (Console)**: Không cần màn hình, chạy qua SSH
### Cài đặt nhanh
```bash
sudo apt update
sudo apt install -y python3-tk python3-pip python3-venv
git clone https://git.ttcorp.net/orangepivietnam/
cd esp32s3-ld2453/orangepi-server
python3 -m venv venv
source venv/bin/activate
pip install -r requirements.txt
```
### Chạy chương trình
Chế độ Web (xem qua web):
```bash
source venv/bin/activate
python3 orange_pi_visualizer.py <ESP32_IP>
```
Chế độ Headless (SSH, không cần màn hình):
```bash
python3 orange_pi_visualizer.py <ESP32_IP> --headless
```
Xem hướng dẫn chi tiết tại: [README.md](orangepi-server/README.md)
---
## Hướng phát triển đa radar (chưa triển khai)
Mỗi `RadarFrame` đã mang theo `node_id` và `sequence_number`, nên sau này dữ liệu từ parser có thể được tiếp tiếp qua UDP đến Orange Pi để thực hiện hợp nhất (fusion) dữ liệu từ nhiều radar (`RADAR_A/B/C`), mà không cần chạm vào `LD2453Parser` nữa. Hiện tại chưa thêm mã cho UDP/MQTT/cloud — firmware này chỉ thực hiện Wi-Fi cục bộ + WebSocket, theo yêu cầu ban đầu.
---
## Ghi chú về chất lượng code
- Không có `delay()` nào trên đường đọc radar (`LD2453Parser::update()`, `feedByte()`) — chỉ có vòng lặp thử kết nối Wi-Fi trong `WebServerManager::begin()`, chạy một lần duy nhất trong `setup()` trước khi bắt đầu đọc dữ liệu.
- Không cấp phát động trong parser (buffer vòng kích thước cố định).
- Việc đọc radar, logic vùng, và mạng được tách riêng thành các class/file độc lập, không gọi trực tiếp vào nhau — file `.ino` đóng vai trò nối dây giữa các thành phần.
- Dự án đang trong giai đoạn phát triển, chưa hoạt động hoàn toàn chính xác, người dùng tự chịu trách nhiệm khi sử dụng