cập nhật lại README
This commit is contained in:
1 parent
649edec6ca
commit
87be72b52b
2 files changed
+451
-127
No files matched your search
+271
@@ -0,0 +1,271 @@
|
||||
# ESP32-S3 + HLK-LD2453 — Zone Presence Detection System
|
||||
|
||||
> **Vietnamese version** available at: [README.md](README.md)
|
||||
|
||||
This project is ported from [thanhtantran/ESP32-LD2450](https://github.com/thanhtantran/ESP32-LD2450), targeting the following hardware:
|
||||
|
||||
- **MCU:** ESP32-S3 (Arduino framework, plain Arduino IDE — no PlatformIO)
|
||||
- **Radar:** Hi-Link **HLK-LD2453** (24 GHz, 1TX+2RX, up to 3 targets simultaneously)
|
||||
|
||||
---
|
||||
|
||||
## ⚠️ Current status: Radar parser is a stub
|
||||
|
||||
Byte-level frame decoding in `LD2453Parser.cpp` is **not implemented yet**.
|
||||
A verifiable source for the official HLK-LD2453 "Communication Protocol" section (frame header/tail, per-target byte layout, sign convention, speed encoding) could not be found at the time this was written — see "Confirming the protocol" below. Everything else (Wi-Fi, REST API, WebSocket, zone logic, non-blocking UART acquisition, raw hex capture) is fully implemented and ready to go.
|
||||
|
||||
---
|
||||
|
||||
## What's reused from the original ESP32-LD2450 project
|
||||
|
||||
- The zone model: up to 3 rectangles in millimeters, `GET /zones` / `POST /updateZones` REST endpoints, `ArduinoJson` for (de)serialization — see `ZoneManager.cpp`.
|
||||
- The WebSocket broadcast pattern (`AsyncWebSocket` on `/ws`, `ws.textAll()` per frame) — see `WebServerManager.cpp`.
|
||||
- The overall `setup()` / `loop()` shape and Wi-Fi reconnect handling.
|
||||
|
||||
---
|
||||
|
||||
## What changed from the original
|
||||
|
||||
- Removed LD2450's `rbegamer/HLK-LD2450` library dependency; parsing is now handled by our own `LD2453Parser`, because the byte format is different for this sensor.
|
||||
- From `Serial2` with fixed pins → `HardwareSerial` on **configurable** pins (`RADAR_RX_PIN` / `RADAR_TX_PIN` in `config.h`).
|
||||
- Removed the blocking `delay()`-based sensor "reopen on failure" hack; radar acquisition now runs as a non-blocking ring-buffer state machine that tolerates partial/corrupted frames without ever calling `delay()` in the acquisition path.
|
||||
- Every frame now carries `node_id` and `sequence_number`, so this node can later feed a multi-radar fusion setup (`RADAR_A/B/C` → UDP → Orange Pi) without touching the parser again.
|
||||
- From PlatformIO `.cpp`/`lib_deps` project → standard Arduino IDE sketch folder.
|
||||
|
||||
---
|
||||
|
||||
## Wiring
|
||||
|
||||
### LD2453 Radar
|
||||
|
||||
```
|
||||
LD2453 ESP32-S3
|
||||
V -> 3V3
|
||||
G -> GND
|
||||
T/TX -> RADAR_RX_PIN (default GPIO18)
|
||||
R/RX -> RADAR_TX_PIN (default GPIO17)
|
||||
```
|
||||
|
||||
### OLED Display I2C 128x64
|
||||
|
||||
```
|
||||
OLED ESP32-S3
|
||||
VCC -> 3V3
|
||||
GND -> GND
|
||||
SCL -> GPIO7 (OLED_SCL_PIN)
|
||||
SDA -> GPIO6 (OLED_SDA_PIN)
|
||||
```
|
||||
|
||||
Change `RADAR_RX_PIN` / `RADAR_TX_PIN` in `config.h` to match your wiring — any two free GPIOs that support UART on your ESP32-S3 board will work, since the ESP32 Arduino core remaps UART pins in software.
|
||||
|
||||
Do **not** use SoftwareSerial — `HardwareSerial` is required at 256000 baud.
|
||||
|
||||
---
|
||||
|
||||
## Required Arduino libraries
|
||||
|
||||
Install via Library Manager (search for these exact names):
|
||||
|
||||
| Library | Used for | Notes |
|
||||
|---|---|---|
|
||||
| `ArduinoJson` (by Benoit Blanchon) | REST API + WebSocket JSON | v7.x — code uses the current `JsonDocument` API (not the deprecated `StaticJsonDocument`) |
|
||||
| `ESP Async WebServer` (ESP32Async fork) | HTTP + WebSocket server | Only needed for stage 2 (`DIAGNOSTIC_MODE 0`) |
|
||||
| `AsyncTCP` | dependency of ESP Async WebServer | matching fork/version to the WebServer library above |
|
||||
| `Adafruit GFX Library` | Graphics library for OLED | Used with Adafruit SSD1306 |
|
||||
| `Adafruit SSD1306` | Driver for OLED 128x64 I2C display | Required if `OLED_ENABLED 1` |
|
||||
|
||||
Board package: **esp32 by Espressif Systems**, board = your specific ESP32-S3 dev board.
|
||||
|
||||
Confirmed building clean (no warnings) with: esp32 core 3.3.11, ArduinoJson (JsonDocument API), ESP_Async_WebServer + Async_TCP.
|
||||
|
||||
---
|
||||
|
||||
## Project layout
|
||||
|
||||
```
|
||||
esp32s3-ld2453/
|
||||
├── esp32s3-ld2453/
|
||||
│ ├── esp32s3-ld2453.ino — setup()/loop(), diagnostic printing
|
||||
│ ├── LD2453Parser.h/.cpp — UART acquisition + frame parsing (STUBBED, see above)
|
||||
│ ├── ZoneManager.h/.cpp — Rectangular zone occupancy logic
|
||||
│ ├── WebServerManager.h/.cpp — Wi-Fi, REST API, WebSocket broadcast
|
||||
│ ├── OLEDDisplay.h/.cpp — OLED I2C 128x64 display manager
|
||||
│ ├── config.example.h — Copy to config.h and edit
|
||||
│ └── config.h — Configuration file (not committed to git)
|
||||
├── orangepi-server/
|
||||
│ ├── ORANGE_PI_GUIDE.md — Detailed guide for Orange Pi
|
||||
│ ├── orange_pi_visualizer.py — Python script to visualize radar data
|
||||
│ └── orange_pi_requirements.txt
|
||||
└── README-en.md
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## OLED Display Features
|
||||
|
||||
The OLED 128x64 display shows the following information:
|
||||
|
||||
### When DIAGNOSTIC_MODE = 1 (Serial Debug Mode)
|
||||
- **Boot screen**: Displays logo and Node ID
|
||||
- **Diagnostic mode notification**: Indicates Serial-only mode is active
|
||||
- **Radar statistics every 5 seconds**:
|
||||
- Number of bytes received from LD2453
|
||||
- Number of parsed frames
|
||||
- Number of parse errors
|
||||
- Packets per second (PPS)
|
||||
- Number of detected targets
|
||||
|
||||
### When DIAGNOSTIC_MODE = 0 (Full WiFi Mode)
|
||||
- **Connecting WiFi**: Shows SSID being connected
|
||||
- **WiFi connected**: Shows IP address
|
||||
- **Realtime detection**:
|
||||
- Number of detected targets (T:0-3)
|
||||
- Status of 3 zones (YES/NO)
|
||||
|
||||
### OLED configuration in config.h
|
||||
|
||||
```cpp
|
||||
#define OLED_ENABLED 1 // 1 = enable OLED, 0 = disable
|
||||
#define OLED_SDA_PIN 6 // SDA pin
|
||||
#define OLED_SCL_PIN 7 // SCL pin
|
||||
#define OLED_I2C_ADDR 0x3C // I2C address (typically 0x3C or 0x3D)
|
||||
```
|
||||
|
||||
**Note**: If the display does not work, try changing `OLED_I2C_ADDR` from `0x3C` to `0x3D`.
|
||||
|
||||
---
|
||||
|
||||
## Stage 1: Diagnostic mode (do this first)
|
||||
|
||||
1. Copy `config.example.h` to `config.h`.
|
||||
2. Leave `DIAGNOSTIC_MODE 1` (the default). This disables Wi-Fi/web entirely — Serial-only.
|
||||
3. Flash, open Serial Monitor at 115200.
|
||||
4. You should see:
|
||||
```
|
||||
LD2453 connected
|
||||
UART: 256000
|
||||
...
|
||||
```
|
||||
Target lines will currently always print `INVALID` — expected, since the parser is stubbed. This stage exists so you can validate wiring/baud/board bring-up independent of the parser.
|
||||
|
||||
---
|
||||
|
||||
## Confirming the protocol
|
||||
|
||||
Until `LD2453Parser`'s real frame format is filled in, use it as a raw-capture tool:
|
||||
|
||||
1. In `config.h`, set `DEBUG_RADAR_RAW 1`.
|
||||
2. Flash, open Serial Monitor at 115200, and let the LD2453 see some motion. You'll get a hex dump of every raw byte the module sends, e.g.:
|
||||
```
|
||||
[RAW] AA FF 03 00 4B 01 36 08 0C 00 ... (len=30)
|
||||
```
|
||||
3. Share that capture (and, if you can get it, the actual "5. Communication Protocol" section of the official HLK-LD2453 user manual — Hi-Link's own download link for it is currently mislabeled and points at the LD2450 manual instead) so the header, tail, per-target byte layout, and sign/speed encoding can be confirmed and dropped into `tryParseFrame()` in `LD2453Parser.cpp`. The exact insertion point is marked with a comment block.
|
||||
|
||||
---
|
||||
|
||||
## Stage 2: Full firmware (once the parser is confirmed)
|
||||
|
||||
1. Set `DIAGNOSTIC_MODE 0` in `config.h`.
|
||||
2. Fill in `WIFI_SSID` / `WIFI_PASSWORD`.
|
||||
3. Flash. The board connects to Wi-Fi, starts the HTTP server, and broadcasts one JSON message per radar frame over `/ws`.
|
||||
|
||||
### WebSocket data format (`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 }
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
Field explanations:
|
||||
- `sensor`: Sensor type name
|
||||
- `node_id`: Radar node ID (from `NODE_ID` in `config.h`)
|
||||
- `sequence`: Frame sequence number, incrementing per frame
|
||||
- `timestamp`: Time in millis() at frame completion
|
||||
- `targets[]`: Array of detected targets (max 3):
|
||||
- `id`: The radar's target *slot* (1–3), **NOT** a persistent person identity — a target disappearing and reappearing may land in a different slot
|
||||
- `valid`: true if this target has valid data
|
||||
- `x`, `y`: Coordinates in millimeters (radar coordinate system)
|
||||
- `speed`: Speed in cm/s (+/− sign indicates direction)
|
||||
|
||||
### REST API
|
||||
|
||||
| Endpoint | Method | Description |
|
||||
|---|---|---|
|
||||
| `/zones` | GET | Returns current zone rectangles + live occupancy status |
|
||||
| `/updateZones` | POST | Update one or more zones (same format as `GET /zones`, partial updates supported |
|
||||
| `/status` | GET | Basic health check (uptime) |
|
||||
|
||||
Example zone update with `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
|
||||
|
||||
The `orangepi-server/` folder contains a Python script to receive data from the ESP32 via WebSocket and display it in realtime on an Orange Pi (or any computer with Python 3.8+).
|
||||
|
||||
### Key features
|
||||
- Connect to ESP32 via WebSocket, receive realtime radar frames
|
||||
- Draw zones (3 rectangles) and target positions (max 3)
|
||||
- Display coordinates, speed, distance, and angle for each target
|
||||
- Supports 2 modes:
|
||||
- **GUI (Tkinter)**: Graphics display, requires a monitor
|
||||
- **Headless (Console)**: No monitor required, runs over SSH
|
||||
|
||||
### Quick setup
|
||||
|
||||
```bash
|
||||
sudo apt update
|
||||
sudo apt install -y python3-tk python3-pip python3-venv
|
||||
|
||||
cd ~/orangepi-server
|
||||
python3 -m venv venv
|
||||
source venv/bin/activate
|
||||
pip install -r orange_pi_requirements.txt
|
||||
```
|
||||
|
||||
### Running the program
|
||||
|
||||
GUI mode (with monitor):
|
||||
```bash
|
||||
source venv/bin/activate
|
||||
python3 orange_pi_visualizer.py <ESP32_IP>
|
||||
```
|
||||
|
||||
Headless mode (SSH, no monitor needed):
|
||||
```bash
|
||||
python3 orange_pi_visualizer.py <ESP32_IP> --headless
|
||||
```
|
||||
|
||||
See detailed guide at: [ORANGE_PI_GUIDE.md](orangepi-server/ORANGE_PI_GUIDE.md)
|
||||
|
||||
---
|
||||
|
||||
## Multi-radar future work (not implemented yet)
|
||||
|
||||
Every `RadarFrame` already carries `node_id` and `sequence_number` so that, later, this same parser output can be forwarded over UDP to an Orange Pi doing sensor fusion across `RADAR_A/B/C`, without touching `LD2453Parser` again. No UDP/MQTT/cloud code has been added — this firmware only does local Wi-Fi + WebSocket, as requested originally.
|
||||
|
||||
---
|
||||
|
||||
## Code quality notes
|
||||
|
||||
- No `delay()` in the radar acquisition path (`LD2453Parser::update()`, `feedByte()`) — only the one-time Wi-Fi connect retry in `WebServerManager::begin()`, which runs once during `setup()` before acquisition starts.
|
||||
- No dynamic allocation in the parser (fixed-size ring buffer).
|
||||
- Radar acquisition, zone logic, and networking are in separate classes/files and don't call into each other directly — the `.ino` wires them together.
|
||||
@@ -1,59 +1,53 @@
|
||||
# ESP32-S3 + HLK-LD2453 — Zone Presence Detection (port of ESP32-LD2450)
|
||||
# ESP32-S3 + HLK-LD2453 — Hệ thống phát hiện người theo vùng
|
||||
|
||||
Ported from [thanhtantran/ESP32-LD2450](https://github.com/thanhtantran/ESP32-LD2450),
|
||||
targeting:
|
||||
> **Phiên bản tiếng Anh** xem tại: [README-en.md](README-en.md)
|
||||
|
||||
- **MCU:** ESP32-S3 (Arduino framework, plain Arduino IDE — no PlatformIO)
|
||||
- **Radar:** Hi-Link **HLK-LD2453** (24 GHz, 1TX+2RX, up to 3 targets)
|
||||
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:
|
||||
|
||||
## ⚠️ Current status: radar parser is a stub
|
||||
- **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)
|
||||
|
||||
`LD2453Parser.cpp`'s byte-level frame decoding is **not implemented yet**.
|
||||
The official HLK-LD2453 "Communication Protocol" manual section (frame
|
||||
header/tail, per-target byte layout, sign convention, speed encoding)
|
||||
could not be sourced from a verifiable source at the time this was
|
||||
written — see "Confirming the protocol" below. Everything else (Wi-Fi,
|
||||
REST API, WebSocket, zone logic, non-blocking UART acquisition, raw hex
|
||||
capture) is fully implemented and ready to go.
|
||||
---
|
||||
|
||||
## What's reused from the original ESP32-LD2450 project
|
||||
## ⚠️ Trạng thái hiện tại: Parser radar đang là bản tạm (stub)
|
||||
|
||||
- The zone model: up to 3 rectangles in millimeters, `GET /zones` /
|
||||
`POST /updateZones` REST endpoints, `ArduinoJson` for
|
||||
(de)serialization — see `ZoneManager.cpp`.
|
||||
- The WebSocket broadcast pattern (`AsyncWebSocket` on `/ws`,
|
||||
`ws.textAll()` per frame) — see `WebServerManager.cpp`.
|
||||
- The overall setup()/loop() shape and Wi-Fi reconnect handling.
|
||||
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.
|
||||
|
||||
## What changed
|
||||
---
|
||||
|
||||
- LD2450's `rbegamer/HLK-LD2450` library dependency is gone; parsing is
|
||||
now our own `LD2453Parser`, because the byte format is different for
|
||||
this sensor.
|
||||
- `Serial2` with fixed pins → `HardwareSerial` on **configurable**
|
||||
pins (`RADAR_RX_PIN` / `RADAR_TX_PIN` in `config.h`).
|
||||
- Blocking `delay()`-based sensor "reopen on failure" hack removed;
|
||||
radar acquisition is now a non-blocking ring-buffer state machine
|
||||
that tolerates partial/corrupted frames without ever calling
|
||||
`delay()` in the acquisition path.
|
||||
- Every frame now carries `node_id` and a `sequence_number`, so this
|
||||
node can later feed a multi-radar fusion setup (`RADAR_A/B/C` → UDP
|
||||
→ Orange Pi) without touching the parser again.
|
||||
- Plain PlatformIO `.cpp`/lib_deps project → standard Arduino IDE
|
||||
sketch folder.
|
||||
## Những gì được kế thừa từ dự án ESP32-LD2450 gốc
|
||||
|
||||
## Wiring
|
||||
- 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 Radar
|
||||
```
|
||||
LD2453 ESP32-S3
|
||||
V -> 3V3
|
||||
G -> GND
|
||||
T/TX -> RADAR_RX_PIN (default GPIO18)
|
||||
R/RX -> RADAR_TX_PIN (default GPIO17)
|
||||
T/TX -> RADAR_RX_PIN (mặc định GPIO18)
|
||||
R/RX -> RADAR_TX_PIN (mặc định GPIO17)
|
||||
```
|
||||
|
||||
### OLED Display I2C 128x64
|
||||
### Màn hình OLED I2C 128x64
|
||||
|
||||
```
|
||||
OLED ESP32-S3
|
||||
VCC -> 3V3
|
||||
@@ -62,64 +56,74 @@ SCL -> GPIO7 (OLED_SCL_PIN)
|
||||
SDA -> GPIO6 (OLED_SDA_PIN)
|
||||
```
|
||||
|
||||
Change `RADAR_RX_PIN` / `RADAR_TX_PIN` in `config.h` to match your
|
||||
wiring — any two free GPIOs that support UART on your ESP32-S3 board
|
||||
will work, since the ESP32 Arduino core remaps UART pins in software.
|
||||
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.
|
||||
|
||||
Do **not** use SoftwareSerial — `HardwareSerial` is required at
|
||||
256000 baud.
|
||||
**Không** dùng SoftwareSerial — bắt buộc dùng `HardwareSerial` ở tốc độ 256000 baud.
|
||||
|
||||
## Required Arduino libraries
|
||||
---
|
||||
|
||||
Install via Library Manager (exact names to search for):
|
||||
## Các thư viện Arduino yêu cầu
|
||||
|
||||
| Library | Used for | Notes |
|
||||
Cài đặt qua Library Manager (tìm kiếm đúng tên):
|
||||
|
||||
| Thư viện | Dùng cho | Ghi chú |
|
||||
|---|---|---|
|
||||
| `ArduinoJson` (by Benoit Blanchon) | REST API + WebSocket JSON | v7.x — code uses the current `JsonDocument` API (not the deprecated `StaticJsonDocument`) |
|
||||
| `ESP Async WebServer` (ESP32Async fork) | HTTP + WebSocket server | Only needed for stage 2 (`DIAGNOSTIC_MODE 0`) |
|
||||
| `AsyncTCP` | dependency of ESP Async WebServer | matching fork/version to the WebServer library above |
|
||||
| `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**, board = your specific
|
||||
ESP32-S3 dev board.
|
||||
Board package: **esp32 by Espressif Systems**, chọn board = model ESP32-S3 dev board cụ thể của bạn.
|
||||
|
||||
Confirmed building clean (no warnings) with: esp32 core 3.3.11,
|
||||
ArduinoJson (JsonDocument API), ESP_Async_WebServer + Async_TCP.
|
||||
Đã 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.
|
||||
|
||||
## Project layout
|
||||
---
|
||||
|
||||
## Cấu trúc thư mục dự án
|
||||
|
||||
```
|
||||
ESP32_LD2453/
|
||||
ESP32_LD2453.ino - setup()/loop(), diagnostic printing
|
||||
LD2453Parser.h/.cpp - UART acquisition + frame parsing (STUBBED, see above)
|
||||
ZoneManager.h/.cpp - rectangular zone occupancy logic
|
||||
WebServerManager.h/.cpp - Wi-Fi, REST API, WebSocket broadcast
|
||||
OLEDDisplay.h/.cpp - OLED I2C 128x64 display manager
|
||||
config.example.h - copy to config.h and edit
|
||||
README.md
|
||||
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/
|
||||
│ ├── ORANGE_PI_GUIDE.md — Hướng dẫn chi tiết cho Orange Pi
|
||||
│ ├── orange_pi_visualizer.py — Script Python visualize dữ liệu radar
|
||||
│ └── orange_pi_requirements.txt
|
||||
└── README.md
|
||||
```
|
||||
|
||||
## OLED Display Features
|
||||
---
|
||||
|
||||
Màn hình OLED 128x64 hiển thị các thông tin:
|
||||
## Tính năng màn hình OLED
|
||||
|
||||
### DIAGNOSTIC_MODE = 1 (Serial Debug Mode)
|
||||
- **Màn hình boot**: Hiển thị logo và Node ID
|
||||
- **Chế độ Diagnostic**: Thông báo Serial-only mode
|
||||
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ố frames đã parse
|
||||
- Số frame đã parse
|
||||
- Số lỗi parse
|
||||
- Packets per second (PPS)
|
||||
- Số target phát hiện được
|
||||
|
||||
### DIAGNOSTIC_MODE = 0 (Full WiFi Mode)
|
||||
- **Kết nối WiFi**: Hiển thị SSID đang kết nối
|
||||
- **WiFi thành công**: Hiển thị địa chỉ IP
|
||||
- **Detection realtime**:
|
||||
### 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 zones (YES/NO)
|
||||
|
||||
- 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
|
||||
@@ -129,50 +133,43 @@ Màn hình OLED 128x64 hiển thị các thông tin:
|
||||
|
||||
**Lưu ý**: Nếu màn hình không hoạt động, hãy thử đổi `OLED_I2C_ADDR` từ `0x3C` sang `0x3D`.
|
||||
|
||||
## Stage 1: diagnostic mode (do this first)
|
||||
---
|
||||
|
||||
1. Copy `config.example.h` to `config.h`.
|
||||
2. Leave `DIAGNOSTIC_MODE 1` (the default). This disables Wi-Fi/web
|
||||
entirely — Serial-only.
|
||||
3. Flash, open Serial Monitor at 115200.
|
||||
4. You should see:
|
||||
## 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
|
||||
...
|
||||
```
|
||||
Target lines will currently always print `INVALID` — expected,
|
||||
since the parser is stubbed. This stage exists so you can validate
|
||||
wiring/baud/board bring-up independent of the parser.
|
||||
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.
|
||||
|
||||
## Confirming the protocol
|
||||
---
|
||||
|
||||
Until `LD2453Parser`'s real frame format is filled in, use it as a
|
||||
raw-capture tool:
|
||||
## Xác nhận giao thức
|
||||
|
||||
1. In `config.h`, set `DEBUG_RADAR_RAW 1`.
|
||||
2. Flash, open Serial Monitor at 115200, and let the LD2453 see some
|
||||
motion. You'll get a hex dump of every raw byte the module sends,
|
||||
e.g.:
|
||||
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. Share that capture (and, if you can get it, the actual "5.
|
||||
Communication Protocol" section of the official HLK-LD2453 user
|
||||
manual — Hi-Link's own download link for it is currently
|
||||
mislabeled and points at the LD2450 manual instead) so the header,
|
||||
tail, per-target byte layout, and sign/speed encoding can be
|
||||
confirmed and dropped into `tryParseFrame()` in `LD2453Parser.cpp`.
|
||||
The exact insertion point is marked with a comment block.
|
||||
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.
|
||||
|
||||
## Stage 2: full firmware (once the parser is confirmed)
|
||||
---
|
||||
|
||||
1. Set `DIAGNOSTIC_MODE 0` in `config.h`.
|
||||
2. Fill in `WIFI_SSID` / `WIFI_PASSWORD`.
|
||||
3. Flash. The board connects to Wi-Fi, starts the HTTP server, and
|
||||
broadcasts one JSON message per radar frame over `/ws`.
|
||||
## Giai đoạn 2: Firmware đầy đủ (sau khi parser đã được xác nhận)
|
||||
|
||||
### WebSocket data format (`ws://<ESP32_IP>/ws`)
|
||||
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
|
||||
{
|
||||
@@ -187,32 +184,88 @@ raw-capture tool:
|
||||
}
|
||||
```
|
||||
|
||||
`id` is the radar's target *slot* (1–3), not a persistent person
|
||||
identity — a target disappearing and reappearing may land in a
|
||||
different slot.
|
||||
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
|
||||
|
||||
- `GET /zones` — current zone rectangles + live occupancy
|
||||
- `POST /updateZones` — update one or more zones (same shape as
|
||||
`GET /zones`, partial updates supported)
|
||||
- `GET /status` — basic health check (uptime)
|
||||
| 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) |
|
||||
|
||||
## Multi-radar future work (not implemented yet)
|
||||
Ví dụ gọi cập nhật zones bằng `curl`:
|
||||
|
||||
Every `RadarFrame` already carries `node_id` and `sequence_number` so
|
||||
that, later, this same parser output can be forwarded over UDP to an
|
||||
Orange Pi doing sensor fusion across `RADAR_A/B/C`, without touching
|
||||
`LD2453Parser` again. No UDP/MQTT/cloud code has been added — this
|
||||
firmware only does local Wi-Fi + WebSocket, as requested.
|
||||
```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}
|
||||
]'
|
||||
```
|
||||
|
||||
## Code quality notes
|
||||
---
|
||||
|
||||
- No `delay()` in the radar acquisition path (`LD2453Parser::update()`,
|
||||
`feedByte()`) — only the one-time Wi-Fi connect retry in
|
||||
`WebServerManager::begin()`, which runs once during `setup()` before
|
||||
acquisition starts.
|
||||
- No dynamic allocation in the parser (fixed-size ring buffer).
|
||||
- Radar acquisition, zone logic, and networking are in separate
|
||||
classes/files and don't call into each other directly — the `.ino`
|
||||
wires them together.
|
||||
## 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
|
||||
|
||||
cd ~/orangepi-server
|
||||
python3 -m venv venv
|
||||
source venv/bin/activate
|
||||
pip install -r orange_pi_requirements.txt
|
||||
```
|
||||
|
||||
### Chạy chương trình
|
||||
|
||||
Chế độ GUI (có màn hình):
|
||||
```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: [ORANGE_PI_GUIDE.md](orangepi-server/ORANGE_PI_GUIDE.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.
|
||||
Reference in new issue
Block a user