HLK-LD2450 Initial experiments to connect to HomeAssistant

Yep, just update all the 6000 values to 7500 or 8000 to have the range arc adjusted to the new longer range from the latest firmware.

2 Likes

Thanks @minhquanghp86 @athua
Yeah, this room is probably the exception.
Still using the default config for the rest

Cho em xin file yaml esp của bác với bác ơi, file của bác github em dán vô toàn báo lỗi. Thanks bác

1 Like

Yes, you cannot use the Screek’s YAML for your LD2450 because it’s for reference only,even if you purchase the exact components used by the development team. If everyone could simply copy/paste code then the sensor work, there would be no incentive to purchase commercial devices. That is what I learned after buying a 2A officially and trying to make another one by myselves. The result is I had to throw away 2 esp32-c3 after using the firmware because I couldn’t restore them to the way they were before. :joy:

1 Like

It doesn’t have as many shady trade secrets, and since the code is open, the decision has been made that people can build their own DIY sensors as they see fit.

If you’re using the 2A’s own hardware, and if it’s firmware is corrupt, then it can always be recovered by following the recovery guide in the documentation: Recover 2A Firmware

For those who want to build this sensor themselves, I think many people in this thread have already done so successfully. There’s not much of a secret here, it’s just that it so happens that we used an extra convenience pin for the c3, resulting in the need to unlock the spi. for those who don’t need the light sensor, just remove the code from the i2c section, and the code for the light sensor.

This is a hobbyist project, and when we initially tested and released them, we took on a very large amount of pressure, as products sent out internationally were essentially unrecoverable. The entire code was composed and improved based on suggestions and with code contributions from everyone.

But we really don’t have the time to make a full tutorial telling you how to diy a sensor of your own, all the knowledge is already there, and all it takes is a little esphome foundation to do it.
On other issues of use etc, we have taken the time to organize as much as possible to make some of the questions in the documentation so that you can get support from it. Human Sensor 2A - Screek Workshop

Using the lite version of the esp32c3 is an option in itself, you can use any of your favorite esp32 development boards to do the same thing.
In fact based on the handy libraries available for the ld2450, using a third party library is the easiest implementation if you only need coordinate output.

They require some additional knowledge about the esp32-c3 for those who want a complete clone of the entire 2a like hardware and mechanisms.
This means you have to put the same light sensor module in the same location and unlock a GPIO pin before flashing the firmware.

I would suggest that diy’s can start with one of the simplest hardware connections to the ld2450 and port the serial code over a little bit so you can get it working.

6 Likes

The Screek YAML is all open source and I have used it as a base to create my own sensor using a Wemos D1 mini ESP32 compatible board and the LD2450 and it works with no issues. The main thing to check in the YAML is the GPIO matches what you have connected on your ESP32 board and the correct esp32 board type is used. As I don’t have the Light sensor, I have removed that part from the Screek code as well as the LED bits.

Here is the YAML code for my custom built sensor:

substitutions:
  name: esp32-presence-radar
  friendly_name: "Presence Radar"
  espID: "esp32_presence_radar"

esphome:
  name: ${name}
  on_boot:
    - priority: 2000
      then:
        lambda: |-
          id(zone1_target_exsits).publish_state(false);
          id(zone2_target_exsits).publish_state(false);
          id(zone3_target_exsits).publish_state(false);
          id(zone_ex1_target_exsits).publish_state(false);

debug:
  update_interval: 30s

api:
ota:
#improv_serial:

globals:
  - id: last_update_ld2450
    type: unsigned long
    restore_value: no
    initial_value: '0'
  - id: init_zone_publish
    type: bool
    restore_value: no
    initial_value: "false"

logger:
  level: INFO
  baud_rate: 0

esp32:
  board: wemos_d1_mini32
  framework:
    type: arduino

wifi:
  networks:
  - ssid: !secret wifi_ssid
    password: !secret wifi_password
    priority: 2
  - ssid: !secret wifi2_ssid
    password: !secret wifi2_password
    priority: 1
  output_power: 10dB

script:
  - id: check_zone1_vaild
    then:
      - lambda: |-
          if (id(zone1_x_begin).state > id(zone1_x_end).state){
            id(tips_zone1_conf).publish_state("Err: X-Begin > X-End");
            return;
          }
          if (id(zone1_y_begin).state > id(zone1_y_end).state){
            id(tips_zone1_conf).publish_state("Err: Y-Begin > Y-End");
            return;
          }
          if (id(zone1_x_begin).state == 0, id(zone1_x_end).state == 0, id(zone1_y_begin).state == 0, id(zone1_y_end).state == 0){
            id(tips_zone1_conf).publish_state("Configure below");
            return;
          }

          int x_size = id(zone1_x_end).state - id(zone1_x_begin).state;
          int y_size = id(zone1_y_end).state - id(zone1_y_begin).state;

          char combined[80]; 
          sprintf(combined, "Curr Size: %d x %d", x_size, y_size);
          id(tips_zone1_conf).publish_state(combined);
  - id: check_zone2_vaild
    then:
      - lambda: |-
          if (id(zone2_x_begin).state > id(zone2_x_end).state){
            id(tips_zone2_conf).publish_state("Err: X-Begin > X-End");
            return;
          }
          if (id(zone2_y_begin).state > id(zone2_y_end).state){
            id(tips_zone2_conf).publish_state("Err: Y-Begin > Y-End");
            return;
          }
          if (id(zone2_x_begin).state == 0, id(zone2_x_end).state == 0, id(zone2_y_begin).state == 0, id(zone2_y_end).state == 0){
            id(tips_zone2_conf).publish_state("Configure below");
            return;
          }

          int x_size = id(zone2_x_end).state - id(zone2_x_begin).state;
          int y_size = id(zone2_y_end).state - id(zone2_y_begin).state;

          char combined[80]; 
          sprintf(combined, "Curr Size: %d x %d", x_size, y_size);
          id(tips_zone2_conf).publish_state(combined);
  - id: check_zone3_vaild
    then:
      - lambda: |-
          if (id(zone3_x_begin).state > id(zone3_x_end).state){
            id(tips_zone3_conf).publish_state("Err: X-Begin > X-End");
            return;
          }
          if (id(zone3_y_begin).state > id(zone3_y_end).state){
            id(tips_zone3_conf).publish_state("Err: Y-Begin > Y-End");
            return;
          }
          if (id(zone3_x_begin).state == 0, id(zone3_x_end).state == 0, id(zone3_y_begin).state == 0, id(zone3_y_end).state == 0){
            id(tips_zone3_conf).publish_state("Configure below");
            return;
          }

          int x_size = id(zone3_x_end).state - id(zone3_x_begin).state;
          int y_size = id(zone3_y_end).state - id(zone3_y_begin).state;

          char combined[80]; 
          sprintf(combined, "Curr Size: %d x %d", x_size, y_size);
          id(tips_zone3_conf).publish_state(combined);
  - id: check_zout1_vaild
    then:
      - lambda: |-
          if (id(zone_ex1_x_begin).state > id(zone_ex1_x_end).state){
            id(tips_zone_ex1_conf).publish_state("Err: X-Begin > X-End");
            return;
          }
          if (id(zone_ex1_y_begin).state > id(zone_ex1_y_end).state){
            id(tips_zone_ex1_conf).publish_state("Err: Y-Begin > Y-End");
            return;
          }
          id(tips_zone_ex1_conf).publish_state("Zone Exclusion 1");

button:
  - platform: restart
    name: "ESP32 Restart ${friendly_name}"
  - platform: safe_mode
    name: "ESP32 Safe Mode Boot ${friendly_name}"
    entity_category: diagnostic

switch:
  - platform: template
    name: "Zout1 Enable ${friendly_name}"
    id: zone_ex1_enable
    optimistic: True
    icon: mdi:account-cancel
    entity_category: config
    restore_mode: RESTORE_DEFAULT_OFF

text_sensor:
  - platform: debug
    reset_reason:
      name: "ESP Reset Reason"
      icon: mdi:anchor
      disabled_by_default: True
  - platform: wifi_info
    ip_address:
      name: "ESP IP Address ${friendly_name}"
    ssid:
      name: "ESP Connected SSID ${friendly_name}"
    bssid:
      name: "ESP Connected BSSID ${friendly_name}"
    mac_address:
      name: "ESP Mac Wifi Address ${friendly_name}"
    scan_results:
      name: "ESP Latest Scan Results ${friendly_name}"
  - platform: template
    name: "Zone1 Info ${friendly_name}"
    id: tips_zone1_conf
    icon: mdi:information-outline
    entity_category: config
    lambda: |-
      return {"Configure below" };
    update_interval: 1000s
  - platform: template
    name: "Zone2 Info ${friendly_name}"
    id: tips_zone2_conf
    icon: mdi:information-outline
    entity_category: config
    lambda: |-
      return {"Configure below" };
    update_interval: 1000s
  - platform: template
    name: "Zone3 Info ${friendly_name}"
    id: tips_zone3_conf
    icon: mdi:information-outline
    entity_category: config
    lambda: |-
      return {"Configure below" };
    update_interval: 1000s
  - platform: template
    name: "Zout1 Info ${friendly_name}"
    id: tips_zone_ex1_conf
    icon: mdi:information-outline
    entity_category: config
    lambda: |-
      return {"Zone Exclusion 1" };
    update_interval: 1000s

binary_sensor:
  - platform: status
    name: "ESP Status ${friendly_name}"
  - platform: template
    name: "Any Presence ${friendly_name}"
    id: any_target_exsits
    device_class: occupancy
    filters:
      - delayed_off: !lambda |-
          if (!id(init_zone_publish)) {
            return 0;
          };
          return id(any_presence_timeout).state * 1000.0;
  - platform: template
    name: "Zone1 Presence ${friendly_name}"
    id: zone1_target_exsits
    device_class: occupancy
    filters:
      - delayed_off: !lambda |-
          if (!id(init_zone_publish)) {
            return 0;
          };
          return id(zone1_x_timeout).state * 1000.0;
  - platform: template
    name: "Zone2 Presence ${friendly_name}"
    id: zone2_target_exsits
    device_class: occupancy
    filters:
      - delayed_off: !lambda |-
          if (!id(init_zone_publish)) {
            return 0;
          };
          return id(zone2_x_timeout).state * 1000.0;
  - platform: template
    name: "Zone3 Presence ${friendly_name}"
    id: zone3_target_exsits
    device_class: occupancy
    filters:
      - delayed_off: !lambda |-
          if (!id(init_zone_publish)) {
            return 0;
          };
          return id(zone3_x_timeout).state * 1000.0;
  - platform: template
    name: "Zout1 Presence ${friendly_name}"
    id: zone_ex1_target_exsits
    icon: mdi:account-multiple-remove
    device_class: occupancy

sensor:
  - platform: internal_temperature
    name: "ESP Temperature ${friendly_name}"
  - platform: wifi_signal # Reports the WiFi signal strength/RSSI in dB
    name: "WiFi Signal dB ${friendly_name}"
    id: wifi_signal_db
    update_interval: 60s
    entity_category: "diagnostic"
  - platform: copy # Reports the WiFi signal strength in %
    source_id: wifi_signal_db
    name: "WiFi Signal Percent ${friendly_name}"
    filters:
      - lambda: return min(max(2 * (x + 100.0), 0.0), 100.0);
    unit_of_measurement: "%"
    entity_category: "diagnostic"
  - platform: uptime
    name: "ESP Uptime ${friendly_name}"
  - platform: template
    name: "All Target Counts ${friendly_name}"
    id: all_target_count
    accuracy_decimals: 0
    icon: "mdi:counter"
    unit_of_measurement: "targets"
  - platform: template
    name: "Zone1 Target Counts ${friendly_name}"
    id: zone1_target_count
    accuracy_decimals: 0
    icon: "mdi:counter"
    unit_of_measurement: "targets"
  - platform: template
    name: "Zone2 Target Counts ${friendly_name}"
    id: zone2_target_count
    accuracy_decimals: 0
    icon: "mdi:counter"
    unit_of_measurement: "targets"
  - platform: template
    name: "Zone3 Target Counts ${friendly_name}"
    id: zone3_target_count
    accuracy_decimals: 0
    icon: "mdi:counter"
    unit_of_measurement: "targets"
  - platform: template
    name: "Zout1 Target Counts ${friendly_name}"
    id: zone_ex1_target_count
    accuracy_decimals: 0
    icon: mdi:account-multiple-minus-outline
    unit_of_measurement: "targets"
  - platform: template
    name: "Target1 X ${friendly_name}"
    id: target1_x
    accuracy_decimals: 0
    unit_of_measurement: 'mm'
    state_class: measurement
    device_class: distance
  - platform: template
    name: "Target1 Y ${friendly_name}"
    id: target1_y
    accuracy_decimals: 0
    unit_of_measurement: 'mm'
    state_class: measurement
    device_class: distance
  - platform: template
    name: "Target1 Speed ${friendly_name}"
    id: target1_speed
    accuracy_decimals: 2
    unit_of_measurement: 'm/s'
    state_class: measurement
    device_class: speed
  - platform: template
    name: "Target1 Resolution ${friendly_name}"
    id: target1_resolution
    accuracy_decimals: 0
    unit_of_measurement: 'mm'
    state_class: measurement
    device_class: distance
  - platform: template
    name: "Target2 X ${friendly_name}"
    id: target2_x
    accuracy_decimals: 0
    unit_of_measurement: 'mm'
    state_class: measurement
    device_class: distance
    # update_interval: 1s
  - platform: template
    name: "Target2 Y ${friendly_name}"
    id: target2_y
    accuracy_decimals: 0
    unit_of_measurement: 'mm'
    state_class: measurement
    device_class: distance
  - platform: template
    name: "Target2 Speed ${friendly_name}"
    id: target2_speed
    accuracy_decimals: 0
    unit_of_measurement: 'm/s'
    state_class: measurement
    device_class: speed
  - platform: template
    name: "Target2 Resolution ${friendly_name}"
    id: target2_resolution
    accuracy_decimals: 0
    unit_of_measurement: 'mm'
    state_class: measurement
    device_class: distance
  - platform: template
    name: "Target3 X ${friendly_name}"
    id: target3_x
    accuracy_decimals: 0
    unit_of_measurement: 'mm'
    state_class: measurement
    device_class: distance
  - platform: template
    name: "Target3 Y ${friendly_name}"
    id: target3_y
    accuracy_decimals: 0
    unit_of_measurement: 'mm'
    state_class: measurement
    device_class: distance
  - platform: template
    name: "Target3 Speed ${friendly_name}"
    id: target3_speed
    accuracy_decimals: 0
    unit_of_measurement: 'm/s'
    state_class: measurement
    device_class: speed
    # update_interval: 1s
  - platform: template
    name: "Target3 Resolution ${friendly_name}"
    id: target3_resolution
    accuracy_decimals: 0
    unit_of_measurement: 'mm'
    state_class: measurement
    device_class: distance

number:
  - platform: template
    name: "Any Presence Timeout ${friendly_name}"
    id: any_presence_timeout
    min_value: 0
    max_value: 600
    mode: box
    device_class: duration
    entity_category: config
    unit_of_measurement: s
    icon: mdi:timer-off
    step: 1
    optimistic: True
    initial_value: 0
    restore_value: True
  - platform: template
    name: "Zone1 Timeout ${friendly_name}"
    id: zone1_x_timeout
    min_value: 0
    max_value: 600
    mode: box
    device_class: duration
    entity_category: config
    unit_of_measurement: s
    icon: mdi:timer-off
    step: 1
    optimistic: True
    initial_value: 0
    restore_value: True
  - platform: template
    name: "Zone1 X-Begin ${friendly_name}"
    id: zone1_x_begin
    min_value: -4000
    max_value: 4000
    mode: box
    device_class: distance
    entity_category: config
    unit_of_measurement: mm
    icon: mdi:arrow-left-bold
    step: 10
    optimistic: True
    initial_value: 0
    restore_value: True
  - platform: template
    name: "Zone1 X-End ${friendly_name}"
    id: zone1_x_end
    mode: box
    min_value: -4000
    max_value: 4000
    device_class: distance
    unit_of_measurement: mm
    entity_category: config
    icon: mdi:arrow-right-bold
    step: 10
    initial_value: 0
    optimistic: True
    restore_value: True
  - platform: template
    name: "Zone1 Y-Begin ${friendly_name}"
    id: zone1_y_begin
    mode: box
    min_value: 0
    max_value: 7500
    device_class: distance
    entity_category: config
    icon: mdi:arrow-up-bold
    unit_of_measurement: mm
    step: 10
    initial_value: 0
    optimistic: True
    restore_value: True
  - platform: template
    name: "Zone1 Y-End ${friendly_name}"
    id: zone1_y_end
    icon: mdi:arrow-down-bold
    mode: box
    min_value: 0
    max_value: 7500
    initial_value: 0
    entity_category: config
    device_class: distance
    unit_of_measurement: mm
    step: 10
    optimistic: True
    restore_value: True
  - platform: template
    name: "Zone2 Timeout ${friendly_name}"
    id: zone2_x_timeout
    min_value: 0
    max_value: 600
    mode: box
    device_class: duration
    entity_category: config
    unit_of_measurement: s
    icon: mdi:timer-off
    step: 1
    optimistic: True
    initial_value: 0
    restore_value: True
  - platform: template
    name: "Zone2 X-Begin ${friendly_name}"
    id: zone2_x_begin
    min_value: -4000
    max_value: 4000
    mode: box
    device_class: distance
    entity_category: config
    unit_of_measurement: mm
    icon: mdi:arrow-left-bold
    step: 10
    optimistic: True
    initial_value: 0
    restore_value: True
  - platform: template
    name: "Zone2 X-End ${friendly_name}"
    id: zone2_x_end
    mode: box
    min_value: -4000
    max_value: 4000
    device_class: distance
    unit_of_measurement: mm
    entity_category: config
    icon: mdi:arrow-right-bold
    step: 10
    initial_value: 0
    optimistic: True
    restore_value: True
  - platform: template
    name: "Zone2 Y-Begin ${friendly_name}"
    id: zone2_y_begin
    mode: box
    min_value: 0
    max_value: 7500
    device_class: distance
    entity_category: config
    icon: mdi:arrow-up-bold
    unit_of_measurement: mm
    step: 10
    initial_value: 0
    optimistic: True
    restore_value: True
  - platform: template
    name: "Zone2 Y-End ${friendly_name}"
    id: zone2_y_end
    icon: mdi:arrow-down-bold
    mode: box
    min_value: 0
    max_value: 7500
    initial_value: 0
    entity_category: config
    device_class: distance
    unit_of_measurement: mm
    step: 10
    optimistic: True
    restore_value: True
  - platform: template
    name: "Zone3 Timeout ${friendly_name}"
    id: zone3_x_timeout
    min_value: 0
    max_value: 600
    mode: box
    device_class: duration
    entity_category: config
    unit_of_measurement: s
    icon: mdi:timer-off
    step: 1
    optimistic: True
    initial_value: 0
    restore_value: True
  - platform: template
    name: "Zone3 X-Begin ${friendly_name}"
    id: zone3_x_begin
    min_value: -4000
    max_value: 4000
    mode: box
    device_class: distance
    entity_category: config
    unit_of_measurement: mm
    icon: mdi:arrow-left-bold
    step: 10
    optimistic: True
    initial_value: 0
    restore_value: True
  - platform: template
    name: "Zone3 X-End ${friendly_name}"
    id: zone3_x_end
    mode: box
    min_value: -4000
    max_value: 4000
    device_class: distance
    unit_of_measurement: mm
    entity_category: config
    icon: mdi:arrow-right-bold
    step: 10
    initial_value: 0
    optimistic: True
    restore_value: True
  - platform: template
    name: "Zone3 Y-Begin ${friendly_name}"
    id: zone3_y_begin
    mode: box
    min_value: 0
    max_value: 7500
    device_class: distance
    entity_category: config
    icon: mdi:arrow-up-bold
    unit_of_measurement: mm
    step: 10
    initial_value: 0
    optimistic: True
    restore_value: True
  - platform: template
    name: "Zone3 Y-End ${friendly_name}"
    id: zone3_y_end
    icon: mdi:arrow-down-bold
    mode: box
    min_value: 0
    max_value: 7500
    initial_value: 0
    entity_category: config
    device_class: distance
    unit_of_measurement: mm
    step: 10
    optimistic: True
    restore_value: True
  - platform: template
    name: "Zout1 X-Begin ${friendly_name}"
    id: zone_ex1_x_begin
    min_value: -4000
    max_value: 4000
    mode: box
    device_class: distance
    entity_category: config
    unit_of_measurement: mm
    icon: mdi:arrow-left-bold
    step: 10
    optimistic: True
    initial_value: 0
    restore_value: True
  - platform: template
    name: "Zout1 X-End ${friendly_name}"
    id: zone_ex1_x_end
    mode: box
    min_value: -4000
    max_value: 4000
    device_class: distance
    unit_of_measurement: mm
    entity_category: config
    icon: mdi:arrow-right-bold
    step: 10
    initial_value: 0
    optimistic: True
    restore_value: True
  - platform: template
    name: "Zout1 Y-Begin ${friendly_name}"
    id: zone_ex1_y_begin
    mode: box
    min_value: 0
    max_value: 7500
    device_class: distance
    entity_category: config
    icon: mdi:arrow-up-bold
    unit_of_measurement: mm
    step: 10
    initial_value: 0
    optimistic: True
    restore_value: True
  - platform: template
    name: "Zout1 Y-End ${friendly_name}"
    id: zone_ex1_y_end
    icon: mdi:arrow-down-bold
    mode: box
    min_value: 0
    max_value: 7500
    initial_value: 0
    entity_category: config
    device_class: distance
    unit_of_measurement: mm
    step: 10
    optimistic: True
    restore_value: True

uart:
  id: uart_ld2450
  tx_pin: 
    number: GPIO17
    mode:
      pullup: true
  rx_pin: 
    number: GPIO16
    mode:
      pullup: true
  baud_rate: 256000
  parity: NONE
  stop_bits: 1
  debug:
    direction: BOTH
    dummy_receiver: True
    after:
     delimiter: [0X55, 0XCC]
    sequence:
      - lambda: |-
          if ((millis() - id(last_update_ld2450)) <= 500) { 
            return;
          };
          id(last_update_ld2450) = millis();

          // p1
          int16_t p1_x = (uint16_t((bytes[5] << 8) | bytes[4] ));
          if ((bytes[5] & 0x80) >> 7){
            p1_x -= pow(2, 15); 
          }else{
            p1_x = 0 - p1_x;
          }

          int16_t p1_y = (uint16_t((bytes[7] << 8) | bytes[6] ));
          if ((bytes[7] & 0x80) >> 7){
            p1_y -= pow(2, 15);
          }else{
            p1_y = 0 - p1_y;
          }

          int p1_speed = (bytes[9] << 8 | bytes[8] );
          if ((bytes[9] & 0x80) >> 7){
            p1_speed -= pow(2, 15);
          }else{
            p1_speed = 0 - p1_speed;
          }
          int16_t p1_distance_resolution = (uint16_t((bytes[11] << 8) | bytes[10] )); 

          // p2
          int16_t p2_x = (uint16_t((bytes[13] << 8) | bytes[12] ));
          if ((bytes[13] & 0x80) >> 7){
            p2_x -=  pow(2, 15); 
          }else{
            p2_x = 0 - p2_x;
          }

          int16_t p2_y = (uint16_t((bytes[15] << 8) | bytes[14] ));
          if ((bytes[15] & 0x80) >> 7){
            p2_y -= pow(2, 15);
          }else{
            p2_y = 0 - p2_y;
          }

          int p2_speed = (bytes[17] << 8 | bytes[16] );
          if ((bytes[17] & 0x80) >> 7){
            p2_speed -= pow(2, 15);
          }else{
            p2_speed = 0 - p2_speed;
          }
          int16_t p2_distance_resolution = (uint16_t((bytes[19] << 8) | bytes[18] )); 

          // p3
          int16_t p3_x = (uint16_t((bytes[21] << 8) | bytes[20] ));
          if ((bytes[21] & 0x80) >> 7){
            p3_x -=  pow(2, 15); 
          }else{
            p3_x = 0 - p3_x;
          }

          int16_t p3_y = (uint16_t((bytes[23] << 8) | bytes[22] ));
          if ((bytes[23] & 0x80) >> 7){
            p3_y -= pow(2, 15);
          }else{
            p3_y = 0 - p3_y;
          }

          int p3_speed = (bytes[25] << 8 | bytes[24] );
          if ((bytes[25] & 0x80) >> 7){
            p3_speed -= pow(2, 15);
          }else{
            p3_speed = 0 - p3_speed;
          }
          
          int16_t p3_distance_resolution = (uint16_t((bytes[27] << 8) | bytes[26] )); 

          bool p1_vaild = (p1_x != 0 || p1_y > 0);
          bool p2_vaild = (p2_x != 0 || p2_y > 0);
          bool p3_vaild = (p3_x != 0 || p3_y > 0);

          // zone exlude 1

          int16_t target_count_in_zone_ex1 = 0;

          int16_t zone_ex1_x_min = id(zone_ex1_x_begin).state;
          int16_t zone_ex1_x_max = id(zone_ex1_x_end).state;
          int16_t zone_ex1_y_min = id(zone_ex1_y_begin).state;
          int16_t zone_ex1_y_max = id(zone_ex1_y_end).state;

          bool p1_zone_ex_enter = false;
          bool p2_zone_ex_enter = false;
          bool p3_zone_ex_enter = false;

          if (id(zone_ex1_enable).state){
            if (p1_vaild){
              if (p1_x >= zone_ex1_x_min && p1_x <= zone_ex1_x_max && p1_y >= zone_ex1_y_min && p1_y <= zone_ex1_y_max){
                  p1_zone_ex_enter = true;
                  target_count_in_zone_ex1 ++;
              }
            }
            if (p2_vaild){
              if (p2_x >= zone_ex1_x_min && p2_x <= zone_ex1_x_max && p2_y >= zone_ex1_y_min && p2_y <= zone_ex1_y_max){
                  p2_zone_ex_enter = true;
                  target_count_in_zone_ex1 ++;
              }
            }
            if (p3_vaild){
              if (p3_x >= zone_ex1_x_min && p3_x <= zone_ex1_x_max && p3_y >= zone_ex1_y_min && p3_y <= zone_ex1_y_max){
                  p3_zone_ex_enter = true;
                  target_count_in_zone_ex1 ++;
              }
            }
          }

          bool has_target_in_zone_ex1 = (target_count_in_zone_ex1 > 0);
          
          int16_t all_target_counts = 0;
          if (p1_vaild && !p1_zone_ex_enter){
            all_target_counts ++;
          }
          if (p2_vaild && !p2_zone_ex_enter){
            all_target_counts ++;
          }
          if (p3_vaild && !p3_zone_ex_enter){
            all_target_counts ++;
          }

          bool has_target_in_zone_all = (all_target_counts > 0);

          // zone 1 check

          int16_t target_count_in_zone1 = 0;
          int16_t zone1_x_min = id(zone1_x_begin).state;
          int16_t zone1_x_max = id(zone1_x_end).state;
          int16_t zone1_y_min = id(zone1_y_begin).state;
          int16_t zone1_y_max = id(zone1_y_end).state;

          if (p1_vaild && !p1_zone_ex_enter){
            if (p1_x >= zone1_x_min && p1_x <= zone1_x_max && p1_y >= zone1_y_min && p1_y <= zone1_y_max){
                target_count_in_zone1 ++;
            }
          }
          if (p2_vaild && !p2_zone_ex_enter){
            if (p2_x >= zone1_x_min && p2_x <= zone1_x_max && p2_y >= zone1_y_min && p2_y <= zone1_y_max){
                target_count_in_zone1 ++;
            }
          }
          if (p3_vaild && !p3_zone_ex_enter){
            if (p3_x >= zone1_x_min && p3_x <= zone1_x_max && p3_y >= zone1_y_min && p3_y <= zone1_y_max){
                target_count_in_zone1 ++;
            }
          }
          bool has_target_in_zone1 = (target_count_in_zone1 > 0);

          // zone 2 check

          int16_t target_count_in_zone2 = 0;
          int16_t zone2_x_min = id(zone2_x_begin).state;
          int16_t zone2_x_max = id(zone2_x_end).state;
          int16_t zone2_y_min = id(zone2_y_begin).state;
          int16_t zone2_y_max = id(zone2_y_end).state;

          if (p1_vaild && !p1_zone_ex_enter){
            if (p1_x >= zone2_x_min && p1_x <= zone2_x_max && p1_y >= zone2_y_min && p1_y <= zone2_y_max){
                target_count_in_zone2 ++;
            }
          }
          if (p2_vaild && !p2_zone_ex_enter){
            if (p2_x >= zone2_x_min && p2_x <= zone2_x_max && p2_y >= zone2_y_min && p2_y <= zone2_y_max){
                target_count_in_zone2 ++;
            }
          }
          if (p3_vaild && !p3_zone_ex_enter){
            if (p3_x >= zone2_x_min && p3_x <= zone2_x_max && p3_y >= zone2_y_min && p3_y <= zone2_y_max){
                target_count_in_zone2 ++;
            }
          }

          bool has_target_in_zone2 = (target_count_in_zone2 > 0);

          // zone 3 check

          int16_t target_count_in_zone3 = 0;
          int16_t zone3_x_min = id(zone3_x_begin).state;
          int16_t zone3_x_max = id(zone3_x_end).state;
          int16_t zone3_y_min = id(zone3_y_begin).state;
          int16_t zone3_y_max = id(zone3_y_end).state;

          if (p1_vaild && !p1_zone_ex_enter){
            if (p1_x >= zone3_x_min && p1_x <= zone3_x_max && p1_y >= zone3_y_min && p1_y <= zone3_y_max){
                target_count_in_zone3 ++;
            }
          }
          if (p2_vaild && !p2_zone_ex_enter){
            if (p2_x >= zone3_x_min && p2_x <= zone3_x_max && p2_y >= zone3_y_min && p2_y <= zone3_y_max){
                target_count_in_zone3 ++;
            }
          }
          if (p3_vaild && !p3_zone_ex_enter){
            if (p3_x >= zone3_x_min && p3_x <= zone3_x_max && p3_y >= zone3_y_min && p3_y <= zone3_y_max){
                target_count_in_zone3 ++;
            }
          }
          bool has_target_in_zone3 = (target_count_in_zone3 > 0);

          // public all info
          if (id(target1_x).state != p1_x){
            id(target1_x).publish_state(p1_x);
          }
          if (id(target1_y).state != p1_y){
            id(target1_y).publish_state(p1_y);
          }

          float p1_m_speed = float(p1_speed) / 100.0;
          if (id(target1_speed).state != p1_m_speed){
            id(target1_speed).publish_state(p1_m_speed);
          }
          if (id(target1_resolution).state != p1_distance_resolution){
            id(target1_resolution).publish_state(p1_distance_resolution);
          }

          if (id(target2_x).state != p2_x){
            id(target2_x).publish_state(p2_x);
          }
          if (id(target2_y).state != p2_y){
            id(target2_y).publish_state(p2_y);
          }
          if (id(target2_speed).state != p2_speed){
            id(target2_speed).publish_state(p2_speed);
          }
          if (id(target2_resolution).state != p2_distance_resolution){
            id(target2_resolution).publish_state(p2_distance_resolution);
          }

          if (id(target3_x).state != p3_x){
            id(target3_x).publish_state(p3_x);
          }
          if (id(target3_y).state != p3_y){
            id(target3_y).publish_state(p3_y);
          }
          if (id(target3_speed).state != p3_speed){
            id(target3_speed).publish_state(p3_speed);
          }
          if (id(target3_resolution).state != p3_distance_resolution){
            id(target3_resolution).publish_state(p3_distance_resolution);
          }

          // publish target info
          
          if (id(all_target_count).state != all_target_counts){
            id(all_target_count).publish_state(all_target_counts);
            id(any_target_exsits).publish_state(has_target_in_zone_all);
          }else if (id(any_target_exsits).state != has_target_in_zone_all){
            id(any_target_exsits).publish_state(has_target_in_zone_all);
          }

          if (id(zone1_target_count).state != target_count_in_zone1){
            id(zone1_target_count).publish_state(target_count_in_zone1);
            id(zone1_target_exsits).publish_state(has_target_in_zone1);
          }else if (id(zone1_target_exsits).state != has_target_in_zone1){
            id(zone1_target_exsits).publish_state(has_target_in_zone1);
          }

          if (id(zone2_target_count).state != target_count_in_zone2){
            id(zone2_target_count).publish_state(target_count_in_zone2);
            id(zone2_target_exsits).publish_state(has_target_in_zone2);
          }else if (id(zone2_target_exsits).state != has_target_in_zone2){
            id(zone2_target_exsits).publish_state(has_target_in_zone2);
          }

          if (id(zone3_target_count).state != target_count_in_zone3){
            id(zone3_target_count).publish_state(target_count_in_zone3);
            id(zone3_target_exsits).publish_state(has_target_in_zone3);
          }else if (id(zone3_target_exsits).state != has_target_in_zone3){
            id(zone3_target_exsits).publish_state(has_target_in_zone3);
          }
          
          // zout
          if (id(zone_ex1_target_count).state != target_count_in_zone_ex1){
            id(zone_ex1_target_count).publish_state(target_count_in_zone_ex1);
          }

          if (id(zone_ex1_target_exsits).state != has_target_in_zone_ex1){
            id(zone_ex1_target_exsits).publish_state(has_target_in_zone_ex1);
          }

          if (!id(init_zone_publish)){
            id(init_zone_publish) = true;
          }

7 Likes

Please use Athua’s code shared below. That’s all I did. It works well with many variants of esp32.

1 Like

A great open source project. Good health

2 Likes

There’s a lot of great work being done by athua and others to popularize the LD2410, LD2450, and some other radar sensors to the wider open source community of enthusiasts.

We are busy at work and life in the middle of the gap, and recently often always in and out of the hospital, family members are sick and hospitalized to take care of, this is not to complain, when we have some time, we try to reply to every letter encountered problems users. When we have time in between, we build and test sensors in as much detail as we can to make sure they are ready to go wherever the world takes them.

These things take almost too much time for the amount of time spent (we tested the ld2450 for almost two months, scrutinizing it to make sure it would be suitable for long term work for ha. and investigating every serious bug with hlk techs on the firmware side of things).

We are very thankful and good for open source software, so we made preparations for open source code from the beginning.

We apologize if we didn’t explain all the details in a timely and complete manner. Our intention was to provide some pre-built out-of-the-box stabilized sensors to give an alternative to commercial products - it’s not a lucrative business compared to the crazy risks involved, it’s just fun to express one’s own ideas and try to do these things together with everyone else.
But for hobbyists who are more in-depth and have their own needs that our code can inspire and inform, that’s even better, because the difficulty of making a sensor of your own is not in the difficulty of getting access to everything, it’s in the need for some relevant basics, and we realize that, so we try to put together all the relevant things and share them.

I hope that people will find it interesting for themselves, open source enthusiasts can use it to customize their own sensors, and also for those who want to just simply use it out of the box to get a sensor that is as simple to get started with as a commercial product. Part of the problem with open source is that they are too detailed and there is a bit of a barrier to getting started.

That’s what we’ve done with the 2A, we’ve used simple logic to handle the serial output and simply determine the region, and we’ve added exclusion zones, and we’ve added logic to update the light sensor, and a few other features.
But to go and explain everything that’s happening, we’re really struggling to find the time for that, and English is not our first language, so I hope people in the community will understand.

7 Likes

One of the LD2450 radars failed after 10-12 hours of operation. I connected to ESP32 on VCC/GND/RX/TX pins. It worked fine from 22:00 to 04:00, then I went to bed and woke up at 12:30 to find that the radar was not responding to movement. The first thing I did was reboot the ESP32 via the virtual button

button:
  - platform: restart
    name: "Restart"
    icon: mdi:restart

The LD2450 radar did not respond, then I reset the ESP32 to power, removed the USB cable from the USB and plugged it back in. The ESP32 is powered by a USB cable, and the cable itself is connected to a +5V USB adapter. The LD2450 radar still did not respond, was not detected via bluetooth, and the second one, which was working, was detected quickly, but the application on Android 12 with MIUI13 always crashes as soon as I open the window with the radar.

Since there is no documentation for the HLK LD2450 on radio components and it is not clear what the breakdown was. I decided to take voltage measurements with a multimeter and compare them between the working and non-working radar. I discovered that on the faulty radar one of the resistors has a voltage of +0.5V.

I continued to take measurements further and to my misfortune, when I measured something with a multimeter (I don’t know what it is, a resistor or capacitor or inductance), the six-legged component J5EDH swelled and began to smoke. My BSIDE S20 multimeter automatically determines what we are measuring, but it could not determine what we are measuring, it showed either a voltage of 0.3V, then a resistance, then a diode, and as a result, while the multimeter was trying to determine, the six-legged J5EDH swelled and started smoking



Can you tell me about all the components, what I measured that caused the hexapod to swell, and what kind of hexapod J5EDH is? I haven’t found any information on the Internet about J5EDH. I assume that this is some kind of DC converter and I don’t know the characteristics and what analogue I can choose

1 Like

And also, why did the HLK LD2450 manufacturer mix up the wire colors? I bought the kit here and the wire in the photo goes like this

In the photo the wires go in this order

+5v (black)
RX (red)
TX (white)
GND (yellow)

but it should be
+5v (red)
RX (yellow)
TX (white)
GND (black)

I just want to ask, is there a possibility to add like path of the target for past ~10 minutes?
The new look is great! I love plotly!

I’m sorry, I cannot answer any of your questions, but I just wanted to say that you have a great way of explaining what you did and what happened as a result. Very valuable when asking for help! I did check what my component says and it does indeed look like J5EDH, but I cannot find anything about it either. I’m also not sure what happened when measuring that made it cook other then accidentaly shorting something. Good luck!

screenshot-2023-10-29-20-36-24

The color of the cable is just an info to separate them. It hasn’t to be i.e. black for the GND. You just have to connect the GND from your LD2450 to GND of your power supply. TX should be connected to RX of your module and RX to TX of your module (receive to transmit).
By checking the voltage you probably sorted some pins, since the components are close to each other

I would like to try to replace the J5EDH with a working analogue, but I do not know the characteristics of the J5EDH to choose the right analogue to replace the faulty J5EDH

I do not know what you mean. English is not my main language

Considering the ripple requirements needed for the radar module (100mv), and the cost control, it’s very much controlled to be an LDO chip like the me6211, which should be some cheaper LDO chip made in China.

But the reason for this damage seems strange, it’s possible that it’s damaged, you could try connecting to a PC client and give it a try.
In the earliest ld2450 there is a false death, that is, without a proper way to wake up the serial port, then the radar may be in hibernation and not working.
This was one of the early serious bugs.

Based on the current situation, I think it’s hard to find further information about this ldo, but one can work backwards based on the pin wiring and try to find the same pin-compatible ldo chip (most likely the me6211, the me6210’s high-frequency performance and ripple performance is so poor that it tends to cause instability).

It would be best to take some new LD2450 modules for testing again.

We are currently using about 1000 LD2450 chips, they work fine, hilink’s product testing is still quite responsible, the quality is excellent.
This includes the earliest 6-8 pieces of samples we took, which had false deaths in the original firmware and may not work after a long time of operation. These were also alive and well after updating with new firmware.

The hardware solution should come from an upstream manufacturer, and there is currently a corresponding module for such a positioning radar in aithink appearing rd-03d, and you can see from the appearance and all aspects that they have a very similar design:


1 Like

Showing tracks, which sounds cool, but imagining it actually seems crazy.

@athua

Perhaps this is beyond the logical part of a generic form.

For those familiar with and html and js framework, it seems like it would be super cool to implement one of these logics from scratch. But that would mean a lot of programming knowledge on the js side, which would be another story, but html seems really omnipotent these days.

ahua combined all of this into a ha lovelace, which achieves the leap from 0 to 1 and delivers a really fantastic visualization.
At this point, athua’s idea is pretty genius.

The color does defy human common sense, and we forwarded this interesting suggestion to the hilink developers.

1 Like