Skip to content

rbas/LD240

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

11 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

LD2420 Radar Presence Sensor Project

Desk presence detection using an HLK-LD2420 24GHz radar sensor and LaskaKit ESP32-LPKit.

Setup

Prerequisites

Python 3 and PlatformIO:

python3 -m venv venv
source venv/bin/activate       # fish: source venv/bin/activate.fish
pip install platformio

On macOS you also need the CH9102 USB driver for the LaskaKit programmer. Install from https://www.wch.cn/downloads/CH34XSER_MAC_ZIP.html if /dev/cu.wchusbserial* doesn't appear when the board is plugged in.

Wiring

The LD2420 module has 5 pins. Some modules don't expose a TX pin — OT1 acts as serial data output instead.

  LD2420 sensor          ESP32-LPKit
  +-----------+          +-----------+
  | VCC ------+------>---+ 3.3V      |
  | GND ------+------>---+ GND       |
  | OT1 ------+------>---+ GPIO17    |  (sensor data out -> ESP32 receives)
  | RX  ------+----<-----+ GPIO16    |  (ESP32 sends -> sensor data in)
  | OT2       |          |           |  (not connected)
  +-----------+          +-----------+

Build and flash

pio run -t upload && pio device monitor

Library

The LD2420 protocol driver lives in lib/LD2420/ and can be copied into other PlatformIO projects. Usage:

#include <LD2420.h>

LD2420 radar(Serial2, 17, 16);   // UART, RX pin, TX pin

void setup() {
    radar.setMaxDistance(2.1);    // 2.1m = gate 3
    radar.setTimeout(10);
    radar.setDebug(&Serial);     // optional: verbose config output
    radar.begin();
}

void loop() {
    radar.update();
    if (radar.isPresent()) {
        // someone is at the desk
    }
}

For full gate threshold control, build an LD2420Config struct and pass it via radar.setConfig(cfg) before begin().

Hardware

  • MCU: LaskaKit ESP32-LPKit (ESP32-WROOM) with CH9102 USB-C programmer
  • Sensor: HLK-LD2420 24GHz radar presence sensor (3.3V, powered directly from ESP32)
  • Firmware version: v1.6.1
  • Serial port: /dev/cu.wchusbserial*

LaskaKit ESP32-LPKit notes

  • UART2 pins: RX=GPIO17, TX=GPIO16 (swapped from what pinout suggests — verified working)
  • I2C power gate: GPIO4 must be HIGH to enable I2C (SDA=21, SCL=22) — not used in this project
  • Programmer shows as /dev/cu.wchusbserial* (CH9102 driver)

LD2420 Protocol (verified against ESPHome source + our testing)

Key differences from LD2410

Parameter LD2410 LD2420
UART baud rate 256000 115200
Gates 9 × 0.75m 16 × 0.7m
Max range 6m 11.2m (practical ~8m)
Sensitivity format 0-100 (higher=more) Energy thresh (higher=less)
Config protocol ver 0x0001 0x0002

Frame structure

All frames are little-endian.

Command frames:

FD FC FB FA | length(2) | command(2) | payload(N) | 04 03 02 01
  • Length field = 2 (command bytes) + payload size

Energy data frames (output at ~10 Hz when in Energy mode):

F4 F3 F2 F1 | length(2) | presence(1) | distance(2) | gate_energy(2) × 16 | F8 F7 F6 F5

Simple mode output (factory default): ASCII text: ON\r\n or OFF\r\n followed by Range XX\r\n (distance in cm)

Commands

Command Code Payload format
Enter config 0x00FF 02 00 (protocol version)
Exit config (NVM) 0x00FE (none)
Read firmware 0x0000 (none)
Write gate params 0x0007 [reg_addr(2) + value(4)] × N
Read gate params 0x0008 [reg_addr(2)] × N (see below)
Write system param 0x0012 param_id(2) + mode(2) + padding(2)
Restart 0x0068 (none, no reply)

Read gate params response: [value(4)] × N starting at byte 10.

Critical protocol detail: NO num_regs field

The sensor infers the register count from the frame length. Do NOT prepend a register count — the sensor interprets those bytes as a register address, silently writing to the wrong location.

Correct single register write (cmd 0x0007):

FD FC FB FA  08 00  07 00  [reg_addr 2B] [value 4B]  04 03 02 01

Wrong (what we had initially — caused all writes to be garbled):

FD FC FB FA  0A 00  07 00  01 00  [reg_addr 2B] [value 4B]  04 03 02 01
                           ^^^^^ sensor reads this as reg 0x0001

Register addresses

Register Address Description
Min gate 0x0000 Minimum detection gate
Max gate 0x0001 Maximum detection gate
Timeout 0x0004 No-presence timeout (seconds)
Move threshold gate N 0x0010 + N N = 0-15
Still threshold gate N 0x0020 + N N = 0-15

System mode (cmd 0x0012)

Payload is always 6 bytes: param_id(2) + mode(2) + padding(2)

Mode Value Output format
Simple 0x0064 ASCII ON/OFF + Range (factory default)
Energy 0x0004 Binary frames with per-gate energy
Debug 0x0000 Doppler + range raw data

ESPHome-matching config sequence

  1. Enter config mode (0x00FF with protocol version 0x0002)
  2. Write min_gate + max_gate + timeout in ONE 0x0007 frame (3 register pairs, 18 bytes payload)
  3. Loop gates 0-15: write move+still thresholds per gate (2 register pairs per 0x0007 frame, 12 bytes payload, 125μs delay between gates)
  4. Set system mode (0x0012)
  5. Exit config mode (0x00FE — saves to NVM)

Threshold tuning

Factory defaults:

  • Gate 0: move=60000, still=40000
  • Gate 1: move=30000, still=20000
  • Gate 2: move=400, still=200
  • Gates 3-6: move=250, still=200
  • Gate 7+: move=250, still=100-150

Thresholds are energy values — higher = less sensitive. Set to 65535 to effectively disable a gate.

Tuning approach:

  1. Run in Energy mode with nobody present, note idle energy per gate
  2. Set thresholds ~50% above idle energy
  3. Walk through detection zone, verify presence triggers
  4. For through-wall blocking: set gates beyond the room to 65535 AND reduce MAX_GATE as a hard cutoff

ACK format

Response command byte = sent command | 0x0100. Status at bytes 4-5: 0 = success.

Current configuration (Office — desk presence)

  • MIN_GATE=0, MAX_GATE=3 (0-2.1m, covers person sitting at desk)
  • TIMEOUT=10s
  • Gates 0-3: factory-default thresholds
  • Gates 4-15: disabled (65535)
  • Energy mode active — per-gate energy output for monitoring
  • Through-wall detection to sleeping room (behind 15cm brick wall) confirmed blocked

Build

pio run -t upload && pio device monitor

Build modes

  • #define RAW_DUMP — raw hex byte dump for debugging wiring/baud issues
  • Comment it out — full config + monitoring (Energy or Simple mode auto-detected)

Lessons learned

  1. Always do a raw byte dump first — confirms wiring before touching protocol
  2. The LD2420 has no TX pin on some modules — OT1 serves as serial output
  3. Protocol has no num_regs field — this was the main bug; sensor infers count from length
  4. MAX_GATE is a hard cutoff — most effective way to prevent through-wall detection
  5. Config is saved to NVM on exit config — persists across power cycles
  6. ESPHome source is the authoritative protocol reference — manufacturer docs are incomplete
  7. Loose wires = silent failure — sensor just stops responding, solder connections for production

About

Desk presence detection using an HLK-LD2420 24GHz radar sensor and LaskaKit ESP32-LPKit.

Topics

Resources

Stars

Watchers

Forks

Contributors

Languages