HLK-LD2450 Initial experiments to connect to HomeAssistant

Good morning @SummerRain

Great box and nice work.
Is it possible for you to share your STP 3D printing plans?

thanks in advance

Sorry for the stupid question but how I could use it? Through HACS?
thanks

Just add to the yaml file:

esphome:
  name: ...
  friendly_name: ...

external_components:
  - source:
      type: git
      url: https://github.com/uncle-yura/esphome-ld2450
      ref: master
    components: [ ld2450 ]

like this

esphome:
  name: hlk-presence
  friendly_name: hlk-presence

external_components:
  - source:
      type: git
      url: https://github.com/uncle-yura/esphome-ld2450
      ref: master
    components: [ ld2450 ]

esp32:
  board: esp32dev
  framework:
    type: arduino

# Enable logging
logger:
  baud_rate: 0

api:
  encryption:
    key: "....."
ota:
  password: "........"

wifi:
  ssid: !secret wifi_ssid
  password: !secret wifi_password

captive_portal:   
web_server:
  port: 80
uart:
  id: uart_bus
  tx_pin: 
    number: GPIO17
    mode:
      input: true
      pullup: true
  rx_pin: 
    number: GPIO16
    mode:
      input: true
      pullup: true
  baud_rate: 256000
  parity: NONE
  stop_bits: 1
ld2450:
  uart_id: uart_bus
  update_interval: 1s
  invert_y: false
  invert_x: false

binary_sensor:
- platform: ld2450
  has_target:
    name: Presence
  has_moving_target:
    name: Moving Target
  has_still_target:
    name: Still Target
  presence_regions:
    - name: "Custom Presence Region 0"
      region_id: presence_region_0

text_sensor:
  - platform: ld2450
    version:
      name: "FW"
    mac_address:
      name: "MAC address"

button:
  - platform: restart
    name: "ESP Restart"
  - platform: ld2450
    factory_reset:
      name: "Factory reset"
    reboot:
      name: "Reboot"

switch:
  - platform: ld2450
    single_target:
      name: "Single target"
    bluetooth:
      name: "Bluetooth"

number:
- platform: ld2450
  rotate:
    restore_value: true
    initial_value: 0
    name: "Rotate angle"
  presence_timeout:
    name: "Presence timeout"
  presence_regions:
    - x0: 100
      y0: 100
      x1: 200
      y1: 200
      id: presence_region_0
  entry_points:
    - x: 0
      y: 0
  region_0:
    x0:
      name: R0X0
    y0:
      name: R0Y0
    x1:
      name: R0X1
    y1:
      name: R0Y1

  region_1:
    x0:
      name: R1X0
    y0:
      name: R1Y0
    x1:
      name: R1X1
    y1:
      name: R1Y1

  region_2:
    x0:
      name: R2X0
    y0:
      name: R2Y0
    x1:
      name: R2X1
    y1:
      name: R2Y1

select:
  - platform: ld2450
    baud_rate:
      name: "Baud rate"
    regions_type:
      name: "Regions type"

sensor:
- platform: ld2450
  target_count:
    name: Target count

  person_0:
    position_x:
      name: "P0X"  

    position_y:
      name: "P0Y"  

    speed:
      name: "S0"  

    resolution:
      name: "R0"  

  person_1:
    position_x:
      name: "P1X"  

    position_y:
      name: "P1Y"  

    speed:
      name: "S1"  

    resolution:
      name: "R1"  

  person_2:
    position_x:
      name: "P2X"  

    position_y:
      name: "P2Y"  

    speed:
      name: "S2"  

    resolution:
      name: "R2"

but where I can find some more instructions on how properly set and visualise the zones?

I was able to do it in Python code with the Plotly library but i lack the knowledge for the ā€œ$exā€ veriables and how to fill the data properly from the sensor.

import plotly.graph_objects as go
import random

# Function to generate random positions for a person moving in a room for 5 minutes
def generate_random_positions():
    x_positions = [random.uniform(-2000, 2000) for _ in range(5)]
    y_positions = [random.uniform(2000, 4000) for _ in range(5)]
    return x_positions, y_positions

# Coordinates for the first person
x_data_osoba1, y_data_osoba1 = generate_random_positions()

# Coordinates for the second person
x_data_osoba2, y_data_osoba2 = generate_random_positions()

# Current position of the first person (last point in data for the first person)
aktualni_x_osoba1 = x_data_osoba1[-1]
aktualni_y_osoba1 = y_data_osoba1[-1]

# Current position of the second person (last point in data for the second person)
aktualni_x_osoba2 = x_data_osoba2[-1]
aktualni_y_osoba2 = y_data_osoba2[-1]

# Coordinates for the polygon area
area_x = [0, 6000 * (3**0.5) / 2, 4500, 4000, 3000, 2000, 1000, 0, -1000, -2000, -3000, -4000, -4500, -6000 * (3**0.5) / 2, 0]
area_y = [0, 6000 / 2, (6000**2 - 4500**2)**0.5, (6000**2 - 4000**2)**0.5, (6000**2 - 3000**2)**0.5, (6000**2 - 2000**2)**0.5,
          (6000**2 - 1000**2)**0.5, 6000, (6000**2 - 1000**2)**0.5, (6000**2 - 2000**2)**0.5, (6000**2 - 3000**2)**0.5,
          (6000**2 - 4000**2)**0.5, (6000**2 - 4500**2)**0.5, 6000 / 2, 0]

# Create a trace for the first person's movement
trace_osoba1 = go.Scatter(x=x_data_osoba1, y=y_data_osoba1, mode='lines+markers', marker=dict(size=10, color='blue'), line=dict(width=2, color='gray'))

# Create a point for the current position of the first person
trace_aktualni_osoba1 = go.Scatter(x=[aktualni_x_osoba1], y=[aktualni_y_osoba1], mode='markers', marker=dict(size=15, color='red'))

# Create a trace for the second person's movement
trace_osoba2 = go.Scatter(x=x_data_osoba2, y=y_data_osoba2, mode='lines+markers', marker=dict(size=10, color='green'), line=dict(width=2, color='gray'))

# Create a point for the current position of the second person
trace_aktualni_osoba2 = go.Scatter(x=[aktualni_x_osoba2], y=[aktualni_y_osoba2], mode='markers', marker=dict(size=15, color='orange'))

# Create a trace for the filled area
trace_area = go.Scatter(x=area_x, y=area_y, fill='toself', fillcolor='rgba(168, 216, 234, 0.15)', line=dict(shape='linear', width=1, dash='dot'))

# Create a layout for the graph
layout = go.Layout(title='Pohyb osob v mĆ­stnosti za poslednĆ­ch 5 minut', xaxis=dict(title='Osa X'), yaxis=dict(title='Osa Y'))

# Create a figure object combining all traces and layout
fig = go.Figure(data=[trace_osoba1, trace_aktualni_osoba1, trace_osoba2, trace_aktualni_osoba2, trace_area], layout=layout)

# Show the graph
fig.show()

You can run this in ā€œ>>>ā€ python console

Looks like this:

@athua Do you think, it could be possible?

just store history for each target in array, then display this history

//some pseudocode

  const tracks = [[],[],[]]
  const numberOfFramesToRemember = 10

  mqtt_client.on('update', ([targetCoords1,targetCoords2,targetCoords3]) => {
    tracks[0].push(targetCoords1)
    tracks[1].push(targetCoords2)
    tracks[2].push(targetCoords3)

    tracks[0] = tracks[0].slice(-numberOfFramesToRemember)
    tracks[1] = tracks[1].slice(-numberOfFramesToRemember)
    tracks[2] = tracks[2].slice(-numberOfFramesToRemember)
  })

and to display something like that:


  for (const track of tracks) {
    const d = track.reduceRight((p,c) => `${p} L${c}`,`M${a.at(-1)}`)
    path.setAttribute('d',d)
  }

or if you want to make it fancy:

  //some pseudocode

  for (const track of tracks) {
    const [
        [_na,[x,y]],
        ...segments
      ] = track
        .reduceRight(
        (p,c) => [
          ...p,
          [ p.at(-1)?.at(-1), c ]
        ],
        []
      )

    svg.drawCircle(x,y, radius: 10)
    const segmentsLength = segments.length
    let iter = 0

    for (const [[x1,y1],[x2,y2]] in segments) {
      svg.line(
        x1, y1,
        x2, y2,
        strokeWidth: 2,
        strokeOpacity: iter/segments.length
      )
      iter++
    }
  }

Hi have used your code but I cannot see my position in the chart below.

this is the yaml

type: custom:plotly-graph
title: Target Positions
refresh_interval: 1
hours_to_show: current_day
layout:
  height: 230
  margin:
    l: 50
    r: 20
    t: 20
    b: 40
  showlegend: true
  xaxis:
    dtick: 1000
    gridcolor: RGBA(200,200,200,0.15)
    zerolinecolor: RGBA(200,200,200,0.15)
    type: number
    fixedrange: true
    range:
      - 4000
      - -4000
  yaxis:
    dtick: 1000
    gridcolor: RGBA(200,200,200,0.15)
    zerolinecolor: RGBA(200,200,200,0.15)
    scaleanchor: x
    scaleratio: 1
    fixedrange: true
    range:
      - 7500
      - 0
entities:
  - entity: ''
    name: Target1
    marker:
      size: 12
    line:
      shape: spline
      width: 5
    x:
      - $ex hass.states["sensor.hlk_presence_p0x"].state
    'y':
      - $ex hass.states["sensor.hlk_presence_p0y"].state
  - entity: ''
    name: Target2
    marker:
      size: 12
    line:
      shape: spline
      width: 5
    x:
      - $ex hass.states["sensor.hlk_presence_p1x"].state
    'y':
      - $ex hass.states["sensor.hlk_presence_p1y"].state
  - entity: ''
    name: Target3
    marker:
      size: 12
    line:
      shape: spline
      width: 5
    x:
      - $ex hass.states["sensor.hlk_presence_p2x"].state
    'y':
      - $ex hass.states["sensor.hlk_presence_p2y"].state
  - entity: ''
    name: Zone1
    mode: lines
    fill: toself
    fillcolor: RGBA(20,200,0,0.06)
    line:
      color: RGBA(20,200,0,0.2)
      shape: line
      width: 2
    x:
      - $ex hass.states["number.hlk_presence_r0x0"].state
      - $ex hass.states["number.hlk_presence_r0x0"].state
      - $ex hass.states["number.hlk_presence_r0x1"].state
      - $ex hass.states["number.hlk_presence_r0x1"].state
      - $ex hass.states["number.hlk_presence_r0x0"].state
    'y':
      - $ex hass.states["number.hlk_presence_r0y0"].state
      - $ex hass.states["number.hlk_presence_r0y0"].state
      - $ex hass.states["number.hlk_presence_r0y1"].state
      - $ex hass.states["number.hlk_presence_r0y1"].state
      - $ex hass.states["number.hlk_presence_r0y0"].state
  - entity: ''
    name: Zone2
    mode: lines
    fill: toself
    fillcolor: RGBA(200,0,255,0.06)
    line:
      color: RGBA(200,0,255,0.2)
      shape: line
      width: 2
    x:
      - $ex hass.states["number.hlk_presence_r1x0"].state
      - $ex hass.states["number.hlk_presence_r1x0"].state
      - $ex hass.states["number.hlk_presence_r1x1"].state
      - $ex hass.states["number.hlk_presence_r1x1"].state
      - $ex hass.states["number.hlk_presence_r1x0"].state
    'y':
      - $ex hass.states["number.hlk_presence_r1y0"].state
      - $ex hass.states["number.hlk_presence_r1y0"].state
      - $ex hass.states["number.hlk_presence_r1y1"].state
      - $ex hass.states["number.hlk_presence_r1y1"].state
      - $ex hass.states["number.hlk_presence_r1y0"].state
  - entity: ''
    name: Zone3
    mode: lines
    fill: toself
    fillcolor: RGBA(200,120,55,0.06)
    line:
      color: RGBA(200,120,55,0.2)
      shape: line
      width: 2
    x:
      - $ex hass.states["number.hlk_presence_r2x0"].state
      - $ex hass.states["number.hlk_presence_r2x0"].state
      - $ex hass.states["number.hlk_presence_r2x1"].state
      - $ex hass.states["number.hlk_presence_r2x1"].state
      - $ex hass.states["number.hlk_presence_r2x0"].state
    'y':
      - $ex hass.states["number.hlk_presence_r2y0"].state
      - $ex hass.states["number.hlk_presence_r2y0"].state
      - $ex hass.states["number.hlk_presence_r2y1"].state
      - $ex hass.states["number.hlk_presence_r2y1"].state
      - $ex hass.states["number.hlk_presence_r2y0"].state
  - entity: ''
    name: Coverage
    mode: lines
    fill: tonexty
    fillcolor: rgba(168, 216, 234, 0.15)
    line:
      shape: line
      width: 1
      dash: dot
    x:
      - 0
      - $ex 7500 * Math.sin((2 * Math.PI)/360 * 60)
      - 4500
      - 4000
      - 3000
      - 2000
      - 1000
      - 0
      - -1000
      - -2000
      - -3000
      - -4000
      - -4500
      - $ex -7500 * Math.sin((2 * Math.PI)/360 * 60)
      - 0
    'y':
      - 0
      - $ex 7500 * Math.cos((2 * Math.PI)/360 * 60)
      - $ex Math.sqrt( 7500**2 - 4500**2 )
      - $ex Math.sqrt( 7500**2 - 4000**2 )
      - $ex Math.sqrt( 7500**2 - 3000**2 )
      - $ex Math.sqrt( 7500**2 - 2000**2 )
      - $ex Math.sqrt( 7500**2 - 1000**2 )
      - 7500
      - $ex Math.sqrt( 7500**2 - 1000**2 )
      - $ex Math.sqrt( 7500**2 - 2000**2 )
      - $ex Math.sqrt( 7500**2 - 3000**2 )
      - $ex Math.sqrt( 7500**2 - 4000**2 )
      - $ex Math.sqrt( 7500**2 - 4500**2 )
      - $ex 7500 * Math.cos((2 * Math.PI)/360 * 60)
      - 0
raw_plotly_config: true

what was wrong?

Homeassistant already has the ā€œhistoryā€.
If you create a 3d graph with x:y:time, where time is from -10m to now,
then if you take a look from ā€œtopā€ perspective, it will look the same as the code i shared.

1 Like

Can you share files for 3D printing?

1 Like

I believe I have understood the reason but I donā€™t know how to fix it.
The LD2450 is providing data in cm while the Ploty Graph has been designed in mm and therefore all movements are too closed to the zero.

What is the easy way to change the Ploty YAML?

Solved with sensor_templates.yaml

Is there a way to show the current position instead the history one?

you may try this code(base on athusā€™s code), itā€™s support both inch and mm system.

Does it work also with uncle-yura component?

yes, but you would need to change some config. Andā€¦ 2A map dont support uncle-yuraā€™s zones (used it native app)
You would only see the dots/objects.
Screek is using itā€™s own virtual zones

I have tried to use your 2A YAML LD2450 config but I was not able to adjust it to my ESP32 DevkitV1.
Do you have a version for it?

TI is expensive, will not compare to HLKs, there are many mm-wave chip for you to try. Unless you like the challenge, buy Xiaomi FP2 instead. Even with the best chip, you must invest ton of time and knowledge to make it stable.

OK not sure what Iā€™m missing. I have a LD2450 connected to a Wemos D1 mini using uncle-yura yaml. But Iā€™m not getting any response. However, I know the LD2450 is working because I can see results in the iOS app. Iā€™ve checked the code several times, but not sure what config changes I need to make to even get it to track a single target.

Try swapping the TX and RX cables? This is my first esp project and not sure why I keep having to do so to get sensors recording. It works so I keep it that way.

@all: can I put the ld2450 in a box too, will that block the radar signal? Tya!

Tried that, no luck. In fact, I thought switching made more sense connecting TX (GPIO1) on D1 Mini to RX on LD2450 and RX (GPIO3) on D1 to TX on LD2450. If it werenā€™t for the iOS app that shows the LD2450 working, I was sure it was bad. Now Iā€™m hoping I just need to change something in the code. Iā€™m getting no data from it into HA.

Is module powered via USB? i experienced that one of uart pins can be disabled this way by built-in uart-usb chip (Rx or Tx, canā€™t remember anymore). Did you try different Tx/Rx pins?
I know iā€™ve had problems with fingerprint reader a while ago, it turned out this was the case. Back then i changed pins and it worked, so i couldnā€™t tell if pins 1 and 3 are released if you donā€™t power via USB or notā€¦