LD2410B Human Presence Sensor

The LD2410B is a millimeter-wave radar sensor for human presence detection with enhanced Bluetooth-based configuration support. It maintains UART communication while offering wireless setup and tuning via BLE.

LD2410B Human Presence Sensor image
LD2410B · UART / Bluetooth
2modes
UART · Bluetooth
5pins
Connections
5V DC
Supply
Up to 6 meter s
Range
-20 to +85 °C
Operating temp
$12
Typical price
On this page

LD2410B pinout

5 pins · UART · Bluetooth

The LD2410B is a 5-pin 24GHz mmWave radar sensor with Bluetooth configuration:

View:
LD2410B Human Presence Sensor pinout
PinTypeDescriptionNotes
VCCPowerPower input5V DC
GNDPowerGround connection
TXCommunicationUART transmitSends data from sensor (connect to ESP32 RX)
RXCommunicationUART receiveReceives commands (connect to ESP32 TX)
OUTCommunicationDigital presence outputHIGH when presence detected (optional)
  • Interface: UART (256000 baud) + Bluetooth LE

  • Technology: 24GHz FMCW radar with integrated BLE

  • Detection: Both stationary and moving human targets

  • Range: Up to 6 meters (improved from LD2410)

  • Field of View: ±60° detection angle

  • Bluetooth: BLE for wireless configuration (HiLink app)

  • Gates: Configurable distance gates (0.2m or 0.75m resolution)

  • Power: 5V DC, stable power required

  • Pin Spacing: 1.27mm (compact design)

  • Applications: Smart lighting, occupancy sensing, HVAC control, security systems

Wiring the LD2410B to ESP32

5 connections · 1 optional

To interface the LD2410B with an ESP32 for human presence detection via UART:

LD2410B Human Presence Sensor wiring with ESP32
LD2410B pinESP32 pinPurpose
VCC5VPower supply (5V)
GNDGNDGround
TXGPIO16 (RX2)Sensor transmit to ESP32 receive
RXGPIO17 (TX2)Sensor receive from ESP32 transmit
OUTGPIO18Digital presence output (optional) · optional
  • UART2: Use UART2 on ESP32 (GPIO16/17 are default UART2 pins)

  • Baud Rate: 256000 bps - must match in firmware configuration

  • Power: Requires stable 5V supply (power even when using BLE)

  • TX/RX: Connect sensor TX to ESP32 RX, sensor RX to ESP32 TX

  • OUT Pin: Optional digital output goes HIGH on presence detection

  • Bluetooth Setup: Use HiLink/HLK Radar BLE app for wireless configuration

  • BLE vs UART: BLE and UART cannot operate simultaneously on most firmware

  • Workflow: Configure via BLE, then use UART for continuous operation

LD2410B code examples

5 platforms
Platform:

LD2410B Arduino example

Copy
// Requires library: "MyLD2410"
#if defined(ARDUINO_SAMD_NANO_33_IOT) || defined(ARDUINO_AVR_LEONARDO)
//ARDUINO_SAMD_NANO_33_IOT RX_PIN is D1, TX_PIN is D0
//ARDUINO_AVR_LEONARDO RX_PIN(RXI) is D0, TX_PIN(TXO) is D1
#define sensorSerial Serial1
#elif defined(ARDUINO_XIAO_ESP32C3) || defined(ARDUINO_XIAO_ESP32C6)
//RX_PIN is D7, TX_PIN is D6
#define sensorSerial Serial0
#elif defined(ESP32)
//Other ESP32 device - choose available GPIO pins
#define sensorSerial Serial1
#if defined(ARDUINO_ESP32S3_DEV)
#define RX_PIN 18
#define TX_PIN 17
#else
#define RX_PIN 16
#define TX_PIN 17
#endif
#else
#error "This sketch only works on ESP32, Arduino Nano 33IoT, and Arduino Leonardo (Pro-Micro)"
#endif

// User defines
// #define DEBUG_MODE
#define ENHANCED_MODE
#define SERIAL_BAUD_RATE 115200

//Change the communication baud rate here, if necessary
//#define LD2410_BAUD_RATE 256000
#include "MyLD2410.h"

#ifdef DEBUG_MODE
MyLD2410 sensor(sensorSerial, true);
#else
MyLD2410 sensor(sensorSerial);
#endif

unsigned long nextPrint = 0, printEvery = 1000;  // print every second

void printValue(const byte &val) {
  Serial.print(' ');
  Serial.print(val);
}

void printData() {
  Serial.print(sensor.statusString());
  if (sensor.presenceDetected()) {
    Serial.print(", distance: ");
    Serial.print(sensor.detectedDistance());
    Serial.print("cm");
  }
  Serial.println();
  if (sensor.movingTargetDetected()) {
    Serial.print(" MOVING    = ");
    Serial.print(sensor.movingTargetSignal());
    Serial.print("@");
    Serial.print(sensor.movingTargetDistance());
    Serial.print("cm ");
    if (sensor.inEnhancedMode()) {
      Serial.print("\n signals->[");
      sensor.getMovingSignals().forEach(printValue);
      Serial.print(" ] thresholds:[");
      sensor.getMovingThresholds().forEach(printValue);
      Serial.print(" ]");
    }
    Serial.println();
  }
  if (sensor.stationaryTargetDetected()) {
    Serial.print(" STATIONARY= ");
    Serial.print(sensor.stationaryTargetSignal());
    Serial.print("@");
    Serial.print(sensor.stationaryTargetDistance());
    Serial.print("cm ");
    if (sensor.inEnhancedMode()) {
      Serial.print("\n signals->[");
      sensor.getStationarySignals().forEach(printValue);
      Serial.print(" ] thresholds:[");
      sensor.getStationaryThresholds().forEach(printValue);
      Serial.print(" ]");
    }
    Serial.println();
  }

  if (sensor.inEnhancedMode() && (sensor.getFirmwareMajor() > 1)) {
    Serial.print("Light level: ");
    Serial.println(sensor.getLightLevel());
    Serial.print("Output level: ");
    Serial.println((sensor.getOutLevel()) ? "HIGH" : "LOW");
  }

  Serial.println();
}

void setup() {
  Serial.begin(SERIAL_BAUD_RATE);
#if defined(ARDUINO_XIAO_ESP32C3) || defined(ARDUINO_XIAO_ESP32C6) || defined(ARDUINO_SAMD_NANO_33_IOT) || defined(ARDUINO_AVR_LEONARDO)
  sensorSerial.begin(LD2410_BAUD_RATE);
#else
  sensorSerial.begin(LD2410_BAUD_RATE, SERIAL_8N1, RX_PIN, TX_PIN);
#endif
  delay(2000);
  Serial.println(__FILE__);
  if (!sensor.begin()) {
    Serial.println("Failed to communicate with the sensor.");
    while (true) {}
  }

#ifdef ENHANCED_MODE
  sensor.enhancedMode();
#else
  sensor.enhancedMode(false);
#endif

  delay(nextPrint);
}

void loop() {
  if ((sensor.check() == MyLD2410::Response::DATA) && (millis() > nextPrint)) {
    nextPrint = millis() + printEvery;
    printData();
  }
}

The LD2410B is fully compatible with the same Arduino code used for the LD2410 sensor. You can refer to the MyLD2410 library and use identical UART wiring and commands. No code changes are required aside from ensuring BLE is disabled or inactive during UART communication. See LD2410 Arduino Example for implementation details.

LD2410B ESP-IDF example

Copy
#include <stdio.h>
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/uart.h"

// LD2410B on UART2: radar TX -> GPIO16 (RX2), RX -> GPIO17 (TX2), 256000 baud
#define RADAR_UART UART_NUM_2
#define RADAR_RX_PIN 16
#define RADAR_TX_PIN 17

static const uint8_t FRAME_HEAD[4] = {0xF4, 0xF3, 0xF2, 0xF1};

void app_main(void)
{
    uart_config_t config = {
        .baud_rate = 256000,
        .data_bits = UART_DATA_8_BITS,
        .parity = UART_PARITY_DISABLE,
        .stop_bits = UART_STOP_BITS_1,
        .flow_ctrl = UART_HW_FLOWCTRL_DISABLE,
    };
    ESP_ERROR_CHECK(uart_param_config(RADAR_UART, &config));
    ESP_ERROR_CHECK(uart_set_pin(RADAR_UART, RADAR_TX_PIN, RADAR_RX_PIN,
                                 UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE));
    ESP_ERROR_CHECK(uart_driver_install(RADAR_UART, 1024, 0, 0, NULL, 0));

    uint8_t buf[64];
    int matched = 0;

    while (1) {
        uint8_t byte;
        if (uart_read_bytes(RADAR_UART, &byte, 1, pdMS_TO_TICKS(100)) != 1)
            continue;

        // Sync on the F4 F3 F2 F1 data-frame header
        if (matched < 4) {
            matched = (byte == FRAME_HEAD[matched]) ? matched + 1 : (byte == FRAME_HEAD[0]);
            continue;
        }

        // Read length (2 bytes, little-endian), then the payload + 4-byte tail
        uint8_t len_bytes[2];
        len_bytes[0] = byte;
        uart_read_bytes(RADAR_UART, &len_bytes[1], 1, pdMS_TO_TICKS(100));
        int length = len_bytes[0] | (len_bytes[1] << 8);
        matched = 0;
        if (length < 11 || length > (int)sizeof(buf))
            continue; // implausible - resync

        if (uart_read_bytes(RADAR_UART, buf, length + 4, pdMS_TO_TICKS(100)) != length + 4)
            continue;

        // Basic-mode report: [0]=0x02, [1]=0xAA, [2]=state, then little-endian values
        if (buf[0] != 0x02 || buf[1] != 0xAA)
            continue; // engineering-mode or config frame - ignore here

        uint8_t state = buf[2]; // 0 none, 1 moving, 2 still, 3 both
        int moving_cm = buf[3] | (buf[4] << 8);
        int still_cm = buf[6] | (buf[7] << 8);
        int detect_cm = buf[9] | (buf[10] << 8);

        static const char *STATES[] = {"no target", "moving", "still", "moving + still"};
        printf("%s | moving %d cm (energy %d) | still %d cm (energy %d) | detect %d cm\n",
               STATES[state & 0x03], moving_cm, buf[5], still_cm, buf[8], detect_cm);
    }
}

ESP-IDF has no component for the LD2410B, but the radar's continuous data stream is easy to parse directly: every report starts with the header F4 F3 F2 F1 followed by a little-endian length, a basic-mode payload (type 0x02, head 0xAA) carrying the target state (none/moving/still/both), the moving and still target distances in centimeters with their energy values, and the overall detection distance. The example syncs on the header, validates the type bytes and prints each report. Configuration commands (sensitivity, gate settings) use a separate FD FC FB FA frame format described in the vendor's protocol manual.

LD2410B ESPHome example

Copy
uart:
  id: uart_bus
  tx_pin: GPIO17
  rx_pin: GPIO16
  baud_rate: 256000

ld2410:
  uart_id: uart_bus

binary_sensor:
  - platform: ld2410
    has_target:
      name: "Presence Detected"
    has_moving_target:
      name: "Moving Target Detected"
    has_still_target:
      name: "Still Target Detected"

sensor:
  - platform: ld2410
    moving_distance:
      name: "Moving Distance"
    still_distance:
      name: "Still Distance"
    moving_energy:
      name: "Moving Energy"
    still_energy:
      name: "Still Energy"
    detection_distance:
      name: "Detection Distance"

text_sensor:
  - platform: ld2410
    version:
      name: "LD2410 Firmware Version"
    mac_address:
      name: "LD2410 MAC Address"

This uses ESPHome's core ld2410 component over UART2 at the radar's fixed 256000 baud. Older examples carried their own esphome: block with the removed platform: ESP32 syntax - the snippet now contains only the sensor-specific parts. Presence, moving/still target states and their distances/energies all become entities, and the component also exposes configuration entities (timeout, gate sensitivities) if you add them.

LD2410B PlatformIO example

Copy
[env:esp32dev]
platform = espressif32
board = esp32dev
framework = arduino
monitor_speed = 115200
lib_deps =
    iavorvel/MyLD2410 @ ^1.2.8
src/main.cppCopy
#include <Arduino.h>
#if defined(ARDUINO_SAMD_NANO_33_IOT) || defined(ARDUINO_AVR_LEONARDO)
//ARDUINO_SAMD_NANO_33_IOT RX_PIN is D1, TX_PIN is D0
//ARDUINO_AVR_LEONARDO RX_PIN(RXI) is D0, TX_PIN(TXO) is D1
#define sensorSerial Serial1
#elif defined(ARDUINO_XIAO_ESP32C3) || defined(ARDUINO_XIAO_ESP32C6)
//RX_PIN is D7, TX_PIN is D6
#define sensorSerial Serial0
#elif defined(ESP32)
//Other ESP32 device - choose available GPIO pins
#define sensorSerial Serial1
#if defined(ARDUINO_ESP32S3_DEV)
#define RX_PIN 18
#define TX_PIN 17
#else
#define RX_PIN 16
#define TX_PIN 17
#endif
#else
#error "This sketch only works on ESP32, Arduino Nano 33IoT, and Arduino Leonardo (Pro-Micro)"
#endif

// User defines
// #define DEBUG_MODE
#define ENHANCED_MODE
#define SERIAL_BAUD_RATE 115200

//Change the communication baud rate here, if necessary
//#define LD2410_BAUD_RATE 256000
#include "MyLD2410.h"

#ifdef DEBUG_MODE
MyLD2410 sensor(sensorSerial, true);
#else
MyLD2410 sensor(sensorSerial);
#endif

unsigned long nextPrint = 0, printEvery = 1000;  // print every second

void printValue(const byte &val) {
  Serial.print(' ');
  Serial.print(val);
}

void printData() {
  Serial.print(sensor.statusString());
  if (sensor.presenceDetected()) {
    Serial.print(", distance: ");
    Serial.print(sensor.detectedDistance());
    Serial.print("cm");
  }
  Serial.println();
  if (sensor.movingTargetDetected()) {
    Serial.print(" MOVING    = ");
    Serial.print(sensor.movingTargetSignal());
    Serial.print("@");
    Serial.print(sensor.movingTargetDistance());
    Serial.print("cm ");
    if (sensor.inEnhancedMode()) {
      Serial.print("\n signals->[");
      sensor.getMovingSignals().forEach(printValue);
      Serial.print(" ] thresholds:[");
      sensor.getMovingThresholds().forEach(printValue);
      Serial.print(" ]");
    }
    Serial.println();
  }
  if (sensor.stationaryTargetDetected()) {
    Serial.print(" STATIONARY= ");
    Serial.print(sensor.stationaryTargetSignal());
    Serial.print("@");
    Serial.print(sensor.stationaryTargetDistance());
    Serial.print("cm ");
    if (sensor.inEnhancedMode()) {
      Serial.print("\n signals->[");
      sensor.getStationarySignals().forEach(printValue);
      Serial.print(" ] thresholds:[");
      sensor.getStationaryThresholds().forEach(printValue);
      Serial.print(" ]");
    }
    Serial.println();
  }

  if (sensor.inEnhancedMode() && (sensor.getFirmwareMajor() > 1)) {
    Serial.print("Light level: ");
    Serial.println(sensor.getLightLevel());
    Serial.print("Output level: ");
    Serial.println((sensor.getOutLevel()) ? "HIGH" : "LOW");
  }

  Serial.println();
}

void setup() {
  Serial.begin(SERIAL_BAUD_RATE);
#if defined(ARDUINO_XIAO_ESP32C3) || defined(ARDUINO_XIAO_ESP32C6) || defined(ARDUINO_SAMD_NANO_33_IOT) || defined(ARDUINO_AVR_LEONARDO)
  sensorSerial.begin(LD2410_BAUD_RATE);
#else
  sensorSerial.begin(LD2410_BAUD_RATE, SERIAL_8N1, RX_PIN, TX_PIN);
#endif
  delay(2000);
  Serial.println(__FILE__);
  if (!sensor.begin()) {
    Serial.println("Failed to communicate with the sensor.");
    while (true) {}
  }

#ifdef ENHANCED_MODE
  sensor.enhancedMode();
#else
  sensor.enhancedMode(false);
#endif

  delay(nextPrint);
}

void loop() {
  if ((sensor.check() == MyLD2410::Response::DATA) && (millis() > nextPrint)) {
    nextPrint = millis() + printEvery;
    printData();
  }
}

The LD2410B is fully compatible with the same MyLD2410 PlatformIO library and example code used for the LD2410. You can use identical UART wiring (TX=17, RX=16) and no code changes are necessary to support LD2410B functionality. The sensor will operate normally over UART as long as Bluetooth is not actively connected. Refer to the LD2410 PlatformIO Example for a complete implementation using the PlatformIO environment.

LD2410B MicroPython example

Copy
from machine import UART
import time

# Initialize UART2: TX=17, RX=16, baud rate 256000
uart = UART(2, baudrate=256000, tx=17, rx=16)

# Function to read and print data from LD2410
def read_ld2410():
    while True:
        if uart.any():
            data = uart.read()
            if data:
                print(data)
        time.sleep(0.1)

read_ld2410()

LD2410B supports the same UART communication protocol as the original LD2410, making it fully compatible with existing MicroPython code. You can use the same UART setup (TX=17, RX=16, baudrate=256000) and raw data reading scripts. Note that BLE should not be active while using UART. For the base script and driver link, see the MicroPython section of the LD2410 documentation.

LD2410B specifications

From the datasheet
Interface
UART (256000 baud), Bluetooth LE
Detection Range
Up to 6 meters
Detection Field
±60°
Power Supply
5V DC
Output
UART, GPIO, BLE
Resolution
0.2m or 0.75m gate intervals
Operating Temperature
-20°C to +85°C
Dimensions
35mm × 7mm
Wireless Setup
Bluetooth Configuration App

About the LD2410B

The LD2410B takes the base LD2410 radar - same 24GHz FMCW chip, same roughly 5-to-6-meter range in 0.2 m or 0.75 m gates, same ±60 degree field of view - and adds Bluetooth Low Energy so the sensor can be tuned from Hi-Link’s HLKRadarTool phone app instead of over a wired serial connection. That matters once a sensor is mounted inside a ceiling fixture or behind a wall plate: reconfiguring gate sensitivities over UART means pulling it back out, while BLE reaches it in place, provided you stay within the app’s roughly 4-meter Bluetooth range.

Detection capability is otherwise unchanged from the plain LD2410 - the radar hardware and 256000-baud UART protocol are identical, and the same MyLD2410 library and ESPHome ld2410 component (which exposes a Bluetooth on/off switch) work without modification. The trade-off is the same 1.27 mm pin pitch as the base model, which is fiddly on a standard breadboard; the LD2410C offers the same Bluetooth configuration on 2.54 mm header pins instead.

Buy the LD2410B over the plain LD2410 only if wireless configuration is worth the modest price premium - for a bench project with the sensor sitting on a breadboard within reach of a USB cable, the base model does everything the B does except let you tune it over BLE.

LD2410B troubleshooting

4 common issues

Bluetooth Not Detected

›

Issue: The sensor does not appear in the BLE scanner app during setup.

Ensure the module is powered and not actively communicating via UART. BLE is typically disabled during UART operation. Power cycle the sensor without a UART connection and scan again.

No Data Over UART

›

Issue: ESP32 receives no UART data from the sensor.

Confirm correct wiring of TX/RX and check that BLE mode is not interfering. BLE and UART should not operate simultaneously in some firmware versions.

Detection Delay

›

Issue: Presence detection is delayed or inconsistent.

Use the Bluetooth configuration app to adjust sensitivity gates and energy thresholds. Ensure no large obstacles block the radar field.

Cannot Access Configuration

›

Issue: Neither BLE nor UART configuration is working.

Reset the device using a hardware reset if available, or power cycle while holding a configuration button (if present). Consult the datasheet for boot behavior.

Where to buy the LD2410B

LD2410B Human Presence Sensor
LD2410B Human Presence Sensor
$12per unit, typical
Amazon and AliExpress links are affiliate links - buying through them supports the site at no extra cost to you.

Resources