Screek Human Sensor 2A - LD2450 24Ghz mmWave Human Tracker Sensor

I thought it would be best to share our progress with the LD2450 here, as of right now, we’ve made about 500+ DIY sensors for the LD2450.
It took us two months from the release of this module, to testing and designing the enclosure.
We are happy that in the recent test firmware, after getting feedback and suggestions from users, we have initially added three area detection and one exclusion area detection.

I don’t think this is a new thing, but still, we would like to have it here as a post to facilitate the exchange and discussion of this LD2450 radar module.

We have organized the documentation and information we have gathered at: Human Sensor 2A - Screek Workshop

Human Sensor 2A Zone Function Demo - YouTube

After getting a lot of feedback from users, we embarked on a lot of improvements to the original firmware, and now, judging by the feedback from the recent test firmware V230810_2_Beta, it’s working pretty well in general with the zone feature.

  • But there is still some room for improvement:For example, support for multiplayer mode in the official ld2450 firmware, and the demo mode, which we’ve heard from the hilink developers will be available this month. At least the multiplayer mode is already in their internal testing.
  • The display in HomeAssistant still doesn’t take full advantage of the intuitive function of coordinates, and we are still looking forward to working with you all to refine a lovelace display that shows a room area with a few dots moving around, which would be a lot cooler than the primitive ID display that shows a simple occupied state.
  • Likewise, the current setup area requires manual number entry, which isn’t intuitive enough, and would definitely be nice to have in some sort of drawing-like interface. We’re currently told that the folks at hilink are also envisioning a zoning feature from their app, presumably like the LD2410, which seems like a nice addition.
  • To give you a better experience of this groundbreaking cost-effective body tracking radar module, we’ve prepared more Sensor 2A to share with you, and in the meantime, we’re offering a 10% discount on all SCREEK sensors for the next 24 hours.
7 Likes

I’m looking forward to trying this out!

2 Likes

I have purchased 2, I have installed one in the kitchen to turn on the kitchen main light (zone 1), the sink light (zone 2), and the fires light (zone 3). Planning the second for the far side of the living room.

Seems to work reasonably well, need to optimize the timings and maybe put the sensor elsewhere (European kitchen, no central island big enough to dissect a dolphin) and need to light the main light better according to existing light level.

What is interesting is that the kitchen is open, and I could use a fourth zone to control the light in the living room just outside the kitchen area.

Maybe a template sensor to use the targets distance instead would be the solution, to check.

Thanks to this sensor I am finally starting to explore the real meaning of smart, autonomous home. Having HA control lights via tablet and buttons is fine but not really the idea of smart home and energy saving (i.e more comfort and sell wasted energy when the kids forget the lights on…).

2 Likes

After having countless issues with Aqara motion sensors in Smartthings I made the switch to Home assistant with a used thin client. I went on to purchase (5) esp32-c3s and (5) LD2450s thinking they were already supported by ESPHome. I have tried loading the 2A firmware onto my esp32-c3s since I saw images of the c3 being used in what I guess were earlier models of the 2A via ESPHome. However, I am not able to get the config to install on the c3s. Is there a barebones version of the config available for just the mmwave sensor without the light sensor? Or perhaps can someone point out what lines to remove to accomplish this? Is it possible to simply add the config to a esp32-c3 that is already connected to Home Assistant and local wifi without needing to install the screek firmware and connect to the AP created by the esp32? If this is not the correct place to ask could someone point me in the right direction? Any assistance is greatly appreciated.

Which pins on the ESP are you using for TX/RX?

I am at work away from the device but if I recall correctly it is RX GPIO05(I2C_SCL) and TX GPIO04(I2C_SDA).

You’re using these?

https://www.aliexpress.com/item/3256805648573146.html?spm=a2g0o.order_list.order_list_main.11.6b501802TqPBt4

Not actually able to pull up that link(work firewall maybe)… but I purchased the classic version of these ESP32 C3 Development Board ESP32 C3 CORE Board Onboard 2.4G Antenna 32Pin IDF WiFi + Bluetooth CH343P for Arduino Microprython - AliExpress

Looks like they forgot to update the board in the YAML posted in the Github. It’s set to the the c3 mini.

I’m guessing you’d need to update the board:

esp32-c3-devkitm-1

I previously noticed that and updated the yaml with esp32-c3-devkitm-1 but the install failed. However, in hindsight I tried to install wirelessly instead of directly connected to thin client running HA and also may have tinkered with other settings as well. So, when I get home I will grab the yaml from the github again and only change the board to be sure.

This config is working for me:

substitutions:
  # Set the device name
  name: 2a-test
  # Set the friendly name
  friendly_name: 2A Sensor Test
  # Provide the wifi SSID
  wifi: !secret wifi_ssid
  #Provide the wifi password
  wifi_pass: !secret wifi_password
  #Provide an OTA password:
  ota_pass: "trustbutverifywalkingwithabigstick"

esp32:
  board: esp32-c3-devkitm-1
  framework:
    type: arduino

# This is a custom firmware for Human Presence Sensor 2A
# https://docs.screek.io/2a

esphome:
  name: $name
  friendly_name: $friendly_name
  name_add_mac_suffix: false
  on_boot:
    - priority: 100
      then:
        lambda: |-
          id(cpu_speed) = ESP.getCpuFreqMHz();
    - priority: -200
      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);
  platformio_options:
    board_build.flash_mode: dio

preferences:
    flash_write_interval: 5s

external_components:
  - source:
      type: git
      url: https://github.com/screekworkshop/custom_components_support_for_screek_2a
      ref: main
    components: [esp32, uart]

globals:
  - id: cpu_speed
    type: int
    restore_value: no
    initial_value: '0'
  - 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"
  - id: last_illuminance
    type: float
    restore_value: no
    initial_value: "-1"

improv_serial:
  
logger:

debug:
  update_interval: 30s

api:

ota:
  password: $ota_pass

wifi:
  ssid: $wifi
  password: $wifi_pass
  power_save_mode: LIGHT
  # Enable fallback hotspot (captive portal) in case wifi connection fails
  ap:

captive_portal:

web_server:
  port: 80


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
      entity_category: "diagnostic"
      disabled_by_default: True
      icon: mdi:ip-network
    mac_address:
      name: ESP MAC
      entity_category: "diagnostic"
      icon: mdi:ip-network
      disabled_by_default: True
  - platform: template
    name: "Zone1 Info"
    id: tips_zone1_conf
    icon: mdi:information-outline
    entity_category: config
    lambda: |-
      return {"Configure below" };
    update_interval: 1000s
  - platform: template
    name: "Zone2 Info"
    id: tips_zone2_conf
    icon: mdi:information-outline
    entity_category: config
    lambda: |-
      return {"Configure below" };
    update_interval: 1000s
  - platform: template
    name: "Zone3 Info"
    id: tips_zone3_conf
    icon: mdi:information-outline
    entity_category: config
    lambda: |-
      return {"Configure below" };
    update_interval: 1000s
  - platform: template
    name: "Zout1 Info"
    id: tips_zone_ex1_conf
    icon: mdi:information-outline
    entity_category: config
    lambda: |-
      return {"Zone Exclusion 1" };
    update_interval: 1000s

number:
  - platform: template
    name: "Any Presence Timeout"
    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"
    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: "Zone2 Timeout"
    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: "Zone3 Timeout"
    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
  # Zone 1
  - platform: template
    name: Zone1 X-Begin
    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
    on_value: 
      then:
        - script.execute: check_zone1_vaild
  - platform: template
    name: Zone1 X-End
    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
    on_value: 
      then:
        - script.execute: check_zone1_vaild
  - platform: template
    name: Zone1 Y-Begin
    id: zone1_y_begin
    mode: box
    min_value: 0
    max_value: 6000
    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
    on_value: 
      then:
        - script.execute: check_zone1_vaild
  - platform: template
    name: Zone1 Y-End
    id: zone1_y_end
    icon: mdi:arrow-down-bold
    mode: box
    min_value: 0
    max_value: 6000
    initial_value: 0
    entity_category: config
    device_class: distance
    unit_of_measurement: mm
    step: 10
    optimistic: True
    restore_value: True
    on_value: 
      then:
        - script.execute: check_zone1_vaild
  
  # Zone 2
  - platform: template
    name: Zone2 X-Begin
    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
    on_value: 
      then:
        - script.execute: check_zone2_vaild
  - platform: template
    name: Zone2 X-End
    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
    on_value: 
      then:
        - script.execute: check_zone2_vaild
  - platform: template
    name: Zone2 Y-Begin
    id: zone2_y_begin
    mode: box
    min_value: 0
    max_value: 6000
    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
    on_value: 
      then:
        - script.execute: check_zone2_vaild
  - platform: template
    name: Zone2 Y-End
    id: zone2_y_end
    icon: mdi:arrow-down-bold
    mode: box
    min_value: 0
    max_value: 6000
    initial_value: 0
    entity_category: config
    device_class: distance
    unit_of_measurement: mm
    step: 10
    optimistic: True
    restore_value: True
    on_value: 
      then:
        - script.execute: check_zone2_vaild

  # Zone 3
  - platform: template
    name: Zone3 X-Begin
    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
    on_value: 
      then:
        - script.execute: check_zone3_vaild
  - platform: template
    name: Zone3 X-End
    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
    on_value: 
      then:
        - script.execute: check_zone3_vaild
  - platform: template
    name: Zone3 Y-Begin
    id: zone3_y_begin
    mode: box
    min_value: 0
    max_value: 6000
    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
    on_value: 
      then:
        - script.execute: check_zone3_vaild
  - platform: template
    name: Zone3 Y-End
    id: zone3_y_end
    icon: mdi:arrow-down-bold
    mode: box
    min_value: 0
    max_value: 6000
    initial_value: 0
    entity_category: config
    device_class: distance
    unit_of_measurement: mm
    step: 10
    optimistic: True
    restore_value: True
    on_value: 
      then:
        - script.execute: check_zone3_vaild
  
  # Zout1
  - platform: template
    name: Zout1 X-Begin
    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
    on_value: 
      then:
        - script.execute: check_zout1_vaild
  - platform: template
    name: Zout1 X-End
    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
    on_value: 
      then:
        - script.execute: check_zout1_vaild
  - platform: template
    name: Zout1 Y-Begin
    id: zone_ex1_y_begin
    mode: box
    min_value: 0
    max_value: 6000
    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
    on_value: 
      then:
        - script.execute: check_zout1_vaild
  - platform: template
    name: Zout1 Y-End
    id: zone_ex1_y_end
    icon: mdi:arrow-down-bold
    mode: box
    min_value: 0
    max_value: 6000
    initial_value: 0
    entity_category: config
    device_class: distance
    unit_of_measurement: mm
    step: 10
    optimistic: True
    restore_value: True
    on_value: 
      then:
        - script.execute: check_zout1_vaild

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

i2c:
  sda: GPIO7
  scl: GPIO11
  scan: true
  id: bus_a

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");

sensor:
  - platform: template
    name: "ESP CPU Speed"
    accuracy_decimals: 0
    icon: mdi:cpu-32-bit
    unit_of_measurement: Mhz
    disabled_by_default: True
    lambda: |-
      return (id(cpu_speed));
    entity_category: "diagnostic"
    update_interval: 600s
  - platform: template
    id: sys_esp_temperature
    name: ESP Temperature
    lambda: return temperatureRead();
    unit_of_measurement: °C
    device_class: TEMPERATURE
    update_interval: 45s
    entity_category: "diagnostic"
  - platform: uptime
    name: ESP Uptime
    id: sys_uptime
    update_interval: 60s
  - platform: wifi_signal 
    name: RSSI
    id: wifi_signal_db
    update_interval: 60s
    entity_category: "diagnostic"
  - platform: template
    id: esp_memory
    icon: mdi:memory
    name: ESP Free Memory
    lambda: return heap_caps_get_free_size(MALLOC_CAP_INTERNAL) / 1024;
    unit_of_measurement: 'kB'
    state_class: measurement
    entity_category: "diagnostic"
    update_interval: 60s
  - platform: bh1750
    name: "Illuminance"
    accuracy_decimals: 1
    id: bh1750_light
    update_interval: 1s
    filters:
      - lambda: !lambda |-
          if (id(last_illuminance) == x){
            return {};
          }
          if (id(bh1750_fast_update).state){
            id(last_illuminance) = x;
            // ESP_LOGD("custom", "Fast Update BH1850");
            return x;
          }
          if (abs(id(last_illuminance) - x) > 1){

              id(last_illuminance) = x;
              return x;
          }
          return {};

  #-------------------------------------#
  # 高级雷达数据
  - platform: template
    name: "All Target Counts"
    id: all_target_count
    accuracy_decimals: 0
    icon: "mdi:counter"
    unit_of_measurement: "targets"
  - platform: template
    name: "Zone1 Target Counts"
    id: zone1_target_count
    accuracy_decimals: 0
    icon: "mdi:counter"
    unit_of_measurement: "targets"
  - platform: template
    name: "Zone2 Target Counts"
    id: zone2_target_count
    accuracy_decimals: 0
    icon: "mdi:counter"
    unit_of_measurement: "targets"
  - platform: template
    name: "Zone3 Target Counts"
    id: zone3_target_count
    accuracy_decimals: 0
    icon: "mdi:counter"
    unit_of_measurement: "targets"
  - platform: template
    name: "Zout1 Target Counts"
    id: zone_ex1_target_count
    accuracy_decimals: 0
    icon: mdi:account-multiple-minus-outline
    unit_of_measurement: "targets"

  # Target 1
  - platform: template
    name: "Target1 X"
    id: target1_x
    accuracy_decimals: 0
    unit_of_measurement: 'mm'
    state_class: measurement
    device_class: distance
  - platform: template
    name: "Target1 Y"
    id: target1_y
    accuracy_decimals: 0
    unit_of_measurement: 'mm'
    state_class: measurement
    device_class: distance
  - platform: template
    name: "Target1 Speed"
    id: target1_speed
    accuracy_decimals: 2
    unit_of_measurement: 'm/s'
    state_class: measurement
    device_class: speed
  - platform: template
    name: "Target1 Resolution"
    id: target1_resolution
    accuracy_decimals: 0
    unit_of_measurement: 'mm'
    state_class: measurement
    device_class: distance
  
  # Target 2
  - platform: template
    name: "Target2 X"
    id: target2_x
    accuracy_decimals: 0
    unit_of_measurement: 'mm'
    state_class: measurement
    device_class: distance
  - platform: template
    name: "Target2 Y"
    id: target2_y
    accuracy_decimals: 0
    unit_of_measurement: 'mm'
    state_class: measurement
    device_class: distance
  - platform: template
    name: "Target2 Speed"
    id: target2_speed
    accuracy_decimals: 0
    unit_of_measurement: 'm/s'
    state_class: measurement
    device_class: speed
  - platform: template
    name: "Target2 Resolution"
    id: target2_resolution
    accuracy_decimals: 0
    unit_of_measurement: 'mm'
    state_class: measurement
    device_class: distance

  # Target 3
  - platform: template
    name: "Target3 X"
    id: target3_x
    accuracy_decimals: 0
    unit_of_measurement: 'mm'
    state_class: measurement
    device_class: distance
  - platform: template
    name: "Target3 Y"
    id: target3_y
    accuracy_decimals: 0
    unit_of_measurement: 'mm'
    state_class: measurement
    device_class: distance
  - platform: template
    name: "Target3 Speed"
    id: target3_speed
    accuracy_decimals: 0
    unit_of_measurement: 'm/s'
    state_class: measurement
    device_class: speed
  - platform: template
    name: "Target3 Resolution"
    id: target3_resolution
    accuracy_decimals: 0
    unit_of_measurement: 'mm'
    state_class: measurement
    device_class: distance


light:
  - platform: status_led
    name: sys_status
    pin: GPIO13
    internal: True
    restore_mode: ALWAYS_OFF
  - platform: binary
    name: "Red Info Light"
    output: board_info_ed
    entity_category: "config"
    restore_mode: ALWAYS_OFF

time:
  - platform: sntp
    id: time_now

output:
  - platform: gpio
    id: board_info_ed
    pin: GPIO12


switch:
  - platform: factory_reset
    name: Factory Reset
    disabled_by_default: True
    icon: mdi:heart-broken
  - platform: template
    name: Zout1 Enable
    id: zone_ex1_enable
    optimistic: True
    icon: mdi:account-cancel
    entity_category: config
    restore_mode: RESTORE_DEFAULT_OFF
  - platform: template
    name: Illuminance Fast-Update
    id: bh1750_fast_update
    optimistic: True
    entity_category: diagnostic
    restore_mode: RESTORE_DEFAULT_OFF
    icon: mdi:run-fast

button:
  - platform: restart
    icon: mdi:power-cycle
    name: "ESP Reboot"
    entity_category: diagnostic

uart:
  id: uart_bus
  tx_pin: 
    number: GPIO5
    mode:
      input: true
      pullup: true
  rx_pin: 
    number: GPIO4
    mode:
      input: true
      pullup: true
  baud_rate: 256000
  parity: NONE
  stop_bits: 1
  data_bits: 8
  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);
          }

          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);
          }

          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);
          }

          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);
          }
          
          // 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);
            id(zone_ex1_target_exsits).publish_state(has_target_in_zone_ex1);
          }

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

The config you provided installs but in logs I am seeing NO OTA attempt made, restarting. Eventually it show, “There have been 10 suspected unsuccessful boot attempts.” “Boot loop detected. Proceeding to safe mode.” The esp32-c3 connects to my wifi and shows connected in ESPHome addon. However, there is no notification for new device detected and in Integration it doesn’t show up under Discovered. Should I be installing factory-human-sensor-2a-V230908_1_stable.bin or maybe the OTA firmware?

I’m not sure then. I just tested again using the YAML I posted above.

  1. Created device in ESPhome.
  2. Edited config and did a direct paste of the YAML above.
  3. Selected install, manual, modern format.
  4. Use ESPHome Web to flash.

The board reset, connected to wifi, and showed up in device discovery. Added it and can see all the sensor data.

Hello,
is this the right mmwave to use?
https://it.aliexpress.com/item/1005006121923099.html?spm=a2g0o.productlist.main.11.23be6594uNe8QF&algo_pvid=1a51dce4-429d-4695-846c-8cba75946d7e&aem_p4p_detail=2023111303475112826149144584340003340071&algo_exp_id=1a51dce4-429d-4695-846c-8cba75946d7e-5&pdp_npi=4%40dis!EUR!1.18!1.12!!!1.23!!%40211b81a316998760717705740e93dd!12000035851121209!sea!IT!2692602029!&curPageLogUid=fy2lazD9Oofp&search_p4p_id=2023111303475112826149144584340003340071_6

Any got a neat diy 3d print case file for this?

i don’t understand why i’m on zone 3. but zone 3 not show detect

Zones? From one sensor? Where is this documented?

here:

and here:

  • Zone Support: Based on the current firmware, three recognized zones (Zone1, Zone2, Zone3) are supported, as well as one excluded zone (Zout1), which can be switched on and off individually. Enabling the exclusion zone will disregard excluded targets in all recognition judgments.
  • Target Coordinate detection: Compared to LD2410, now you can support X coordinate reading! And Y coordinates are more accurate! They are accurate up to 36cm.

One reason why the LD2450 with this Firmware is such an Awesome sensor!
Especially now with Multiple Target support.

1 Like

Hi, we haven’t received any such bug feedback yet, so our initial guess is that there may be some setup hurdles.
If possible, please provide a recording or screenshot that shows all the relevant settings and we can make an initial attempt to understand what’s happening.

A general and unexpected understanding might be that x-begin and x-end are reversed, i.e. in the case of a negative number, it might be that the former has a bigger number.
For example, think - 200 > -100, which would cause the judgment logic to fail.

1 Like

This is a feature of the ld2450, a new sensor that was discussed more extensively in our other thread and is now supported by more open source libraries.
It is a ld2410 similar to the 24g radar sensor but with support for area characterization.