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

174 lines
6.7 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 — Zone Presence Detection (port of ESP32-LD2450)
Ported from [thanhtantran/ESP32-LD2450](https://github.com/thanhtantran/ESP32-LD2450),
targeting:
- **MCU:** ESP32-S3 (Arduino framework, plain Arduino IDE — no PlatformIO)
- **Radar:** Hi-Link **HLK-LD2453** (24 GHz, 1TX+2RX, up to 3 targets)
## ⚠️ Current status: radar parser is a stub
`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
- 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
- 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.
## Wiring
```
LD2453 ESP32-S3
V -> 3V3
G -> GND
T/TX -> RADAR_RX_PIN (default GPIO18)
R/RX -> RADAR_TX_PIN (default GPIO17)
```
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 (exact names to search for):
| Library | Used for | Notes |
|---|---|---|
| `ArduinoJson` (by Benoit Blanchon) | REST API + WebSocket JSON | v7.x |
| `ESP Async WebServer` (by me-no-dev / ESP32Async fork) | HTTP + WebSocket server | Only needed for stage 2 (`DIAGNOSTIC_MODE 0`). Check current ESP32-S3/Arduino-core-3.x compatibility for whichever fork you install — this ecosystem has had breaking forks; if it doesn't build, the `ESP32Async/ESPAsyncWebServer` fork is the actively maintained one as of this writing. |
| `AsyncTCP` | dependency of ESP Async WebServer | matching fork/version to the WebServer library above |
Board package: **esp32 by Espressif Systems**, board = your specific
ESP32-S3 dev board.
## Project layout
```
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
config.example.h - copy to config.h and edit
README.md
```
## 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 }
]
}
```
`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.
### 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)
## 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.
## 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.