2026-09-11 15:19:14 +07:00
2026-09-11 12:04:46 +07:00
2026-09-11 14:12:52 +07:00
2026-09-11 14:12:52 +07:00
2026-09-11 15:19:14 +07:00
2026-09-11 14:17:37 +07:00
2026-09-11 13:44:38 +07:00
2026-09-11 13:25:42 +07:00
2026-09-11 13:25:42 +07:00
2026-09-11 15:19:14 +07:00
2026-09-11 13:25:42 +07:00
2026-09-11 12:20:25 +07:00
2026-09-11 12:04:46 +07:00

ESP32-S3 + HLK-LD2453 — Zone Presence Detection (port of ESP32-LD2450)

Ported from 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 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 (exact names to search for):

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

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

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

OLED Display Features

Màn hình OLED 128x64 hiển thị các thông tin:

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
  • Thống kê radar mỗi 5 giây:
    • Số bytes nhận được từ LD2453
    • Số frames đã 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:
    • Số target phát hiện (T:0-3)
    • Trạng thái 3 zones (YES/NO)

Cấu hình OLED trong config.h

#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.

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)

{
  "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.
S
Description
esp32s3 cùng với cảm biến ld2453, chạy visulizer trên Orange Pi phát hiện đối tượng
https://ttaisolutions.com
Readme
356 KiB
0 Stars 1 Watchers 0 Forks
Languages
C++ 31.9%
JavaScript 24.4%
Python 18.4%
CSS 14.8%
HTML 7.1%
Other 3.4%