LD2410C Human Presence Sensor

The LD2410C is a millimeter-wave radar sensor for human presence detection, supporting both stationary and moving target detection. It uses UART communication and is ideal for smart home automation systems.

LD2410C Human Presence Sensor image
LD2410C · UART
UART
Interface
5pins
Connections
5V-12V DC
Supply
0.75-6 meter s
Range
-40 to +85 °C
Operating temp
$8
Typical price
On this page

LD2410C pinout

5 pins · UART

The LD2410C is a 5-pin 24GHz mmWave radar sensor with breadboard-friendly spacing:

View:
LD2410C Human Presence Sensor pinout
PinTypeDescriptionNotes
VCCPowerPower input5V to 12V DC (5V recommended)
GNDPowerGround connection
TXCommunicationUART transmitSends data from sensor (connect to ESP32 RX)
RXCommunicationUART receiveReceives commands (connect to ESP32 TX)
OUTCommunicationDigital presence output3.3V HIGH when presence detected (optional)
  • Interface: UART (256000 baud)

  • Technology: 24GHz FMCW radar

  • Detection: Both stationary and moving human targets

  • Range: 0.75 to 6 meters

  • Field of View: ±60° detection angle

  • Gates: Configurable distance gates (0.75m resolution)

  • Power: 5V-12V DC input range (5V recommended for ESP32)

  • Pin Spacing: 2.54mm (standard breadboard-compatible)

  • OUT Logic: 3.3V compatible, safe for ESP32 GPIO

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

Wiring the LD2410C to ESP32

5 connections · 1 optional

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

LD2410C Human Presence Sensor wiring with ESP32
LD2410C pinESP32 pinPurpose
VCC5VPower supply (5V recommended)
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: Accepts 5V-12V, but 5V recommended for ESP32 compatibility

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

  • OUT Pin: 3.3V logic level, safe for direct ESP32 GPIO connection

  • Configuration: Use HLK Radar Tool or ESPHome for gate sensitivity tuning

  • Distance Gates: 0.75m resolution, configurable for moving/stationary detection

  • Breadboard: 2.54mm pitch allows easy prototyping without adapters

LD2410C code examples

5 platforms
Platform:

LD2410C 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 LD2410C 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 proper voltage levels and UART configuration. See LD2410 Arduino Example for implementation details.

LD2410C ESP-IDF example

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

// LD2410C 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 LD2410C, 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.

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

LD2410C 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 LD2410C 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 LD2410C functionality. Refer to the LD2410 PlatformIO Example for a complete implementation using the PlatformIO environment.

LD2410C 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()

LD2410C 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. For the base script and driver link, see the MicroPython section of the LD2410 documentation.

LD2410C specifications

From the datasheet
Interface
UART (256000 baud)
Detection Range
0.75-6 meters
Detection Field
±60°
Power Supply
5V-12V DC
Output
UART, GPIO
Resolution
0.75m gate intervals
Operating Temperature
-40°C to +85°C
Dimensions
16mm × 22mm
Pin Spacing
2.54mm

About the LD2410C

The LD2410C carries the same 24GHz FMCW radar as the base LD2410 and the LD2410B, with identical presence-detection performance, but on a different board: a compact 16 x 22 mm PCB with a standard 2.54 mm pin pitch instead of the 1.27 mm spacing on the other two, so it drops straight into a breadboard or a standard female header with no adapter. Like the B, it includes Bluetooth Low Energy and works with Hi-Link’s HLKRadarTool app for wireless gate and sensitivity tuning within about 4 meters of range - worth knowing since not every listing for this board mentions the BLE radio.

Power is more forgiving too: Hi-Link rates the LD2410C for 5V to 12V input rather than a strict 5V rail, though 5V is still the simplest match for an ESP32 project and matches what its OUT and UART pins expect logically (3.3V levels, safe to wire directly into ESP32 GPIOs). Everything else carries over unchanged - the same 256000-baud UART framing, the same MyLD2410 Arduino library, and the same ESPHome ld2410 component used for the base sensor and the B.

Between the LD2410, LD2410B and LD2410C, the C is the easiest to prototype with: breadboard-friendly pins, Bluetooth setup without a wired connection, and a wider input voltage range, all in the same radar package. The only reason to reach past it within this family is battery power: the LD2410S’s 0.1 mA-class draw is in a different league from any 5V-rail LD2410 variant.

LD2410C troubleshooting

4 common issues

No Data Received

Issue: The ESP32 receives no data from the sensor.

Ensure correct wiring: swap TX and RX if needed, and confirm baud rate is set to 256000. Verify power is supplied at 5V.

Presence Not Detected

Issue: The sensor does not detect presence even when someone is in range.

Check detection gates and sensitivity settings using the LD2410C configuration tool. Ensure ESPHome configuration matches sensor wiring.

Intermittent Detection

Issue: Sensor intermittently detects presence or fails to maintain detection.

Adjust gate sensitivity or reposition sensor to minimize obstructions. Use the configuration tool for fine-tuning detection zones.

UART Initialization Fails

Issue: UART component in ESPHome reports failure to initialize.

Verify UART pins are correct, baud rate is 256000, and no other component is using the same UART bus.

Where to buy the LD2410C

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

Resources