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.
|
||||
Reference in new issue
Block a user