Very nice work, this should be the first HA map card based on the LD2450!
We have it now!
Could never upgrade the radar on the ios app for some reason, had to use the android app for that
The target position map can also be flipped around so that it appears with the LD2450 on top (similar to the image from the HiLink PC app) by switching the range numbers around for the axis like below and the target size can be increased by changing the marker size value. This makes the visualisation of the position easier.
layout:
xaxis:
type: number
fixedrange: true
range:
- 4000
- -4000
yaxis:
fixedrange: true
range:
- 6000
- 0
Yes, it could be also be added the angle +/- 60degrees of the ld2450 range with another 2 segments by defining another entity similar with the zone ones. It may help in positioning the sensor.
Hey folks. I’ve designed another case for the LD2410C and an ESP32 C3 Mini. Source and model files are on Printables:
https://www.printables.com/model/582630-case-for-esp32-c3-super-mini-and-ld2410c
I’ve previously posted one for use with the ESP32 S2 Mini, but can’t recall if I posted it here or not:
https://www.printables.com/model/541368-case-for-esp32-s2-mini-and-ld2410c
Thanks for the idea. I’ve given that a go and the following coverage angle seems to fit my target detection area when I walk around the room.
- entity: ''
name: Coverage
mode: lines
line:
shape: line
width: 1
dash: dot
x:
- 4000
- 0
- -4000
'y':
- 2000
- 0
- 2000
I think the ymax is x/tan(60). It should be arround ymax=2312. May be we can calculate parametric based on range values and tan(60)=x/y.
I’ve added the calculation for the coverage angle and also for the 6m range of the sensor:
Here is the full YAML for the Plotly Graph card that I have put together:
type: custom:plotly-graph
title: Target Positions
refresh_interval: 1
hours_to_show: current_day
layout:
showlegend: true
margin:
l: 50
r: 20
t: 20
b: 40
autosize: false
height: 300
xaxis:
type: number
fixedrange: true
range:
- 4000
- -4000
yaxis:
fixedrange: true
range:
- 6000
- 0
entities:
- entity: ''
name: Target1
marker:
size: 15
line:
shape: spline
width: 5
x:
- $ex hass.states["sensor.screek_human_sensor_2a_734e6c_target1_x"].state
'y':
- $ex hass.states["sensor.screek_human_sensor_2a_734e6c_target1_y"].state
- entity: ''
name: Target2
marker:
size: 15
line:
shape: spline
width: 5
x:
- $ex hass.states["sensor.screek_human_sensor_2a_734e6c_target2_x"].state
'y':
- $ex hass.states["sensor.screek_human_sensor_2a_734e6c_target2_y"].state
- entity: ''
name: Target3
marker:
size: 15
line:
shape: spline
width: 5
x:
- $ex hass.states["sensor.screek_human_sensor_2a_734e6c_target3_x"].state
'y':
- $ex hass.states["sensor.screek_human_sensor_2a_734e6c_target3_y"].state
- entity: ''
name: Zone1
mode: lines
line:
shape: line
width: 2
x:
- $ex hass.states["number.screek_human_sensor_2a_734e6c_zone1_x_begin"].state
- $ex hass.states["number.screek_human_sensor_2a_734e6c_zone1_x_begin"].state
- $ex hass.states["number.screek_human_sensor_2a_734e6c_zone1_x_end"].state
- $ex hass.states["number.screek_human_sensor_2a_734e6c_zone1_x_end"].state
- $ex hass.states["number.screek_human_sensor_2a_734e6c_zone1_x_begin"].state
'y':
- $ex hass.states["number.screek_human_sensor_2a_734e6c_zone1_y_begin"].state
- $ex hass.states["number.screek_human_sensor_2a_734e6c_zone1_y_end"].state
- $ex hass.states["number.screek_human_sensor_2a_734e6c_zone1_y_end"].state
- $ex hass.states["number.screek_human_sensor_2a_734e6c_zone1_y_begin"].state
- $ex hass.states["number.screek_human_sensor_2a_734e6c_zone1_y_begin"].state
- entity: ''
name: Zone2
mode: lines
line:
shape: line
width: 2
x:
- $ex hass.states["number.screek_human_sensor_2a_734e6c_zone2_x_begin"].state
- $ex hass.states["number.screek_human_sensor_2a_734e6c_zone2_x_begin"].state
- $ex hass.states["number.screek_human_sensor_2a_734e6c_zone2_x_end"].state
- $ex hass.states["number.screek_human_sensor_2a_734e6c_zone2_x_end"].state
- $ex hass.states["number.screek_human_sensor_2a_734e6c_zone2_x_begin"].state
'y':
- $ex hass.states["number.screek_human_sensor_2a_734e6c_zone2_y_begin"].state
- $ex hass.states["number.screek_human_sensor_2a_734e6c_zone2_y_end"].state
- $ex hass.states["number.screek_human_sensor_2a_734e6c_zone2_y_end"].state
- $ex hass.states["number.screek_human_sensor_2a_734e6c_zone2_y_begin"].state
- $ex hass.states["number.screek_human_sensor_2a_734e6c_zone2_y_begin"].state
- entity: ''
name: Zone3
mode: lines
line:
shape: line
width: 2
x:
- $ex hass.states["number.screek_human_sensor_2a_734e6c_zone3_x_begin"].state
- $ex hass.states["number.screek_human_sensor_2a_734e6c_zone3_x_begin"].state
- $ex hass.states["number.screek_human_sensor_2a_734e6c_zone3_x_end"].state
- $ex hass.states["number.screek_human_sensor_2a_734e6c_zone3_x_end"].state
- $ex hass.states["number.screek_human_sensor_2a_734e6c_zone3_x_begin"].state
'y':
- $ex hass.states["number.screek_human_sensor_2a_734e6c_zone3_y_begin"].state
- $ex hass.states["number.screek_human_sensor_2a_734e6c_zone3_y_end"].state
- $ex hass.states["number.screek_human_sensor_2a_734e6c_zone3_y_end"].state
- $ex hass.states["number.screek_human_sensor_2a_734e6c_zone3_y_begin"].state
- $ex hass.states["number.screek_human_sensor_2a_734e6c_zone3_y_begin"].state
- entity: ''
name: ExZone
mode: lines
line:
shape: line
width: 2
x:
- $ex hass.states["number.screek_human_sensor_2a_734e6c_zout1_x_begin"].state
- $ex hass.states["number.screek_human_sensor_2a_734e6c_zout1_x_begin"].state
- $ex hass.states["number.screek_human_sensor_2a_734e6c_zout1_x_end"].state
- $ex hass.states["number.screek_human_sensor_2a_734e6c_zout1_x_end"].state
- $ex hass.states["number.screek_human_sensor_2a_734e6c_zout1_x_begin"].state
'y':
- $ex hass.states["number.screek_human_sensor_2a_734e6c_zout1_y_begin"].state
- $ex hass.states["number.screek_human_sensor_2a_734e6c_zout1_y_end"].state
- $ex hass.states["number.screek_human_sensor_2a_734e6c_zout1_y_end"].state
- $ex hass.states["number.screek_human_sensor_2a_734e6c_zout1_y_begin"].state
- $ex hass.states["number.screek_human_sensor_2a_734e6c_zout1_y_begin"].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 6000 * Math.sin((2 * Math.PI)/360 * 60)
- 4000
- 3000
- 2000
- 1000
- 0
- -1000
- -2000
- -3000
- -4000
- $ex -6000 * Math.sin((2 * Math.PI)/360 * 60)
- 0
'y':
- 0
- $ex 6000 * Math.cos((2 * Math.PI)/360 * 60)
- $ex Math.sqrt( 6000**2 - 4000**2 )
- $ex Math.sqrt( 6000**2 - 3000**2 )
- $ex Math.sqrt( 6000**2 - 2000**2 )
- $ex Math.sqrt( 6000**2 - 1000**2 )
- 6000
- $ex Math.sqrt( 6000**2 - 1000**2 )
- $ex Math.sqrt( 6000**2 - 2000**2 )
- $ex Math.sqrt( 6000**2 - 3000**2 )
- $ex Math.sqrt( 6000**2 - 4000**2 )
- $ex 6000 * Math.cos((2 * Math.PI)/360 * 60)
- 0
raw_plotly_config: true
Things are getting interesting, great job, athua.
The only problem it seems to have now is that it looks like they’re a little too sketchy in some places, maybe the lines of that zone could be made dashed as well to make them very light, or differentiate their backgrounds with a subtle color.
The solid lines, which look a bit puzzling, take up the entire field of view.
Can you include a screenshot so I can see what it looks like for you?
As you can see from the screenshot in my post above, it all appears ok for me.
I’ve changed the Zones to be shaded and made the lines thinner so it should be clearer and less cluttered now. Also with the Plotly graph, you can click on the Legend items to hide them if you only want to look at a specific items. It is also very customisable and is easy to change the colours and line thickness to meet individual needs.
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:
- 6000
- 0
entities:
- entity: ''
name: Target1
marker:
size: 12
line:
shape: spline
width: 5
x:
- $ex hass.states["sensor.screek_human_sensor_2a_734e6c_target1_x"].state
'y':
- $ex hass.states["sensor.screek_human_sensor_2a_734e6c_target1_y"].state
- entity: ''
name: Target2
marker:
size: 12
line:
shape: spline
width: 5
x:
- $ex hass.states["sensor.screek_human_sensor_2a_734e6c_target2_x"].state
'y':
- $ex hass.states["sensor.screek_human_sensor_2a_734e6c_target2_y"].state
- entity: ''
name: Target3
marker:
size: 12
line:
shape: spline
width: 5
x:
- $ex hass.states["sensor.screek_human_sensor_2a_734e6c_target3_x"].state
'y':
- $ex hass.states["sensor.screek_human_sensor_2a_734e6c_target3_y"].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.screek_human_sensor_2a_734e6c_zone1_x_begin"].state
- >-
$ex
hass.states["number.screek_human_sensor_2a_734e6c_zone1_x_begin"].state
- >-
$ex
hass.states["number.screek_human_sensor_2a_734e6c_zone1_x_end"].state
- >-
$ex
hass.states["number.screek_human_sensor_2a_734e6c_zone1_x_end"].state
- >-
$ex
hass.states["number.screek_human_sensor_2a_734e6c_zone1_x_begin"].state
'y':
- >-
$ex
hass.states["number.screek_human_sensor_2a_734e6c_zone1_y_begin"].state
- >-
$ex
hass.states["number.screek_human_sensor_2a_734e6c_zone1_y_end"].state
- >-
$ex
hass.states["number.screek_human_sensor_2a_734e6c_zone1_y_end"].state
- >-
$ex
hass.states["number.screek_human_sensor_2a_734e6c_zone1_y_begin"].state
- >-
$ex
hass.states["number.screek_human_sensor_2a_734e6c_zone1_y_begin"].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.screek_human_sensor_2a_734e6c_zone2_x_begin"].state
- >-
$ex
hass.states["number.screek_human_sensor_2a_734e6c_zone2_x_begin"].state
- >-
$ex
hass.states["number.screek_human_sensor_2a_734e6c_zone2_x_end"].state
- >-
$ex
hass.states["number.screek_human_sensor_2a_734e6c_zone2_x_end"].state
- >-
$ex
hass.states["number.screek_human_sensor_2a_734e6c_zone2_x_begin"].state
'y':
- >-
$ex
hass.states["number.screek_human_sensor_2a_734e6c_zone2_y_begin"].state
- >-
$ex
hass.states["number.screek_human_sensor_2a_734e6c_zone2_y_end"].state
- >-
$ex
hass.states["number.screek_human_sensor_2a_734e6c_zone2_y_end"].state
- >-
$ex
hass.states["number.screek_human_sensor_2a_734e6c_zone2_y_begin"].state
- >-
$ex
hass.states["number.screek_human_sensor_2a_734e6c_zone2_y_begin"].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.screek_human_sensor_2a_734e6c_zone3_x_begin"].state
- >-
$ex
hass.states["number.screek_human_sensor_2a_734e6c_zone3_x_begin"].state
- >-
$ex
hass.states["number.screek_human_sensor_2a_734e6c_zone3_x_end"].state
- >-
$ex
hass.states["number.screek_human_sensor_2a_734e6c_zone3_x_end"].state
- >-
$ex
hass.states["number.screek_human_sensor_2a_734e6c_zone3_x_begin"].state
'y':
- >-
$ex
hass.states["number.screek_human_sensor_2a_734e6c_zone3_y_begin"].state
- >-
$ex
hass.states["number.screek_human_sensor_2a_734e6c_zone3_y_end"].state
- >-
$ex
hass.states["number.screek_human_sensor_2a_734e6c_zone3_y_end"].state
- >-
$ex
hass.states["number.screek_human_sensor_2a_734e6c_zone3_y_begin"].state
- >-
$ex
hass.states["number.screek_human_sensor_2a_734e6c_zone3_y_begin"].state
- entity: ''
name: ExZone
mode: lines
fill: toself
fillcolor: RGBA(255,0,0,0.15)
line:
shape: line
width: 1
color: RGBA(255,0,0,0.2)
x:
- >-
$ex
hass.states["number.screek_human_sensor_2a_734e6c_zout1_x_begin"].state
- >-
$ex
hass.states["number.screek_human_sensor_2a_734e6c_zout1_x_begin"].state
- >-
$ex
hass.states["number.screek_human_sensor_2a_734e6c_zout1_x_end"].state
- >-
$ex
hass.states["number.screek_human_sensor_2a_734e6c_zout1_x_end"].state
- >-
$ex
hass.states["number.screek_human_sensor_2a_734e6c_zout1_x_begin"].state
'y':
- >-
$ex
hass.states["number.screek_human_sensor_2a_734e6c_zout1_y_begin"].state
- >-
$ex
hass.states["number.screek_human_sensor_2a_734e6c_zout1_y_end"].state
- >-
$ex
hass.states["number.screek_human_sensor_2a_734e6c_zout1_y_end"].state
- >-
$ex
hass.states["number.screek_human_sensor_2a_734e6c_zout1_y_begin"].state
- >-
$ex
hass.states["number.screek_human_sensor_2a_734e6c_zout1_y_begin"].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 6000 * Math.sin((2 * Math.PI)/360 * 60)
- 4500
- 4000
- 3000
- 2000
- 1000
- 0
- -1000
- -2000
- -3000
- -4000
- -4500
- $ex -6000 * Math.sin((2 * Math.PI)/360 * 60)
- 0
'y':
- 0
- $ex 6000 * Math.cos((2 * Math.PI)/360 * 60)
- $ex Math.sqrt( 6000**2 - 4500**2 )
- $ex Math.sqrt( 6000**2 - 4000**2 )
- $ex Math.sqrt( 6000**2 - 3000**2 )
- $ex Math.sqrt( 6000**2 - 2000**2 )
- $ex Math.sqrt( 6000**2 - 1000**2 )
- 6000
- $ex Math.sqrt( 6000**2 - 1000**2 )
- $ex Math.sqrt( 6000**2 - 2000**2 )
- $ex Math.sqrt( 6000**2 - 3000**2 )
- $ex Math.sqrt( 6000**2 - 4000**2 )
- $ex Math.sqrt( 6000**2 - 4500**2 )
- $ex 6000 * Math.cos((2 * Math.PI)/360 * 60)
- 0
raw_plotly_config: true
If you use the above config without the layout height set on a Dashboard Panel (1 card), you get to see the whole coverage area map:
This looks so cool, athua!
Nice work!
This is an awesome map system!
What are your UART settings for this combination (ESP32-C3 “Super Mini” and LD2410C). I just can’t get it to work.
Here’s the full config:
substitutions:
# Set the device name
name: devicename
# Set the friendly name
friendly_name: friendly
# Provide the wifi SSID
wifi: !secret wifi_ssid
#Provide the wifi password
wifi_pass: !secret wifi_password
### No modifications needed below here ###
####################################################################################
esphome:
name: $name
friendly_name: $friendly_name
platformio_options:
board_build.flash_mode: dio
board_build.f_cpu: 80000000L
name_add_mac_suffix: false
wifi:
ssid: $wifi
password: $wifi_pass
power_save_mode: LIGHT
# Enable fallback hotspot (captive portal) in case wifi connection fails
ap:
output_power: 15db
captive_portal:
web_server:
port: 80
esp32:
board: lolin_c3_mini
framework:
type: esp-idf
api:
ota:
logger:
level: DEBUG
uart:
tx_pin: GPIO4
rx_pin: GPIO3
baud_rate: 256000
parity: NONE
stop_bits: 1
ld2410:
binary_sensor:
- platform: ld2410
has_target:
name: Presence
has_moving_target:
name: Moving Target
has_still_target:
name: Still Target
out_pin_presence_status:
name: Out Pin Presence Status
sensor:
- platform: ld2410
light:
name: Light
moving_distance:
name : Moving Distance
still_distance:
name: Still Distance
moving_energy:
name: Move Energy
still_energy:
name: Still Energy
detection_distance:
name: Detection Distance
g0:
move_energy:
name: g0 move energy
still_energy:
name: g0 still energy
g1:
move_energy:
name: g1 move energy
still_energy:
name: g1 still energy
g2:
move_energy:
name: g2 move energy
still_energy:
name: g2 still energy
g3:
move_energy:
name: g3 move energy
still_energy:
name: g3 still energy
g4:
move_energy:
name: g4 move energy
still_energy:
name: g4 still energy
g5:
move_energy:
name: g5 move energy
still_energy:
name: g5 still energy
g6:
move_energy:
name: g6 move energy
still_energy:
name: g6 still energy
g7:
move_energy:
name: g7 move energy
still_energy:
name: g7 still energy
g8:
move_energy:
name: g8 move energy
still_energy:
name: g8 still energy
- platform: internal_temperature
name: ESP Temperature
unit_of_measurement: °C
device_class: TEMPERATURE
update_interval: 30s
entity_category: "diagnostic"
- platform: uptime
name: Uptime
id: sys_uptime
update_interval: 10s
- platform: wifi_signal
name: WiFi RSSI
id: wifi_signal_db
update_interval: 30s
entity_category: "diagnostic"
switch:
- platform: ld2410
engineering_mode:
name: "LD2410 Engineering Mode"
bluetooth:
name: "LD2410 bluetooth"
number:
- platform: ld2410
timeout:
name: Timeout
light_threshold:
name: Light Threshold
max_move_distance_gate:
name: Max Move Distance Gate
max_still_distance_gate:
name: Max Still Distance Gate
g0:
move_threshold:
name: g0 move threshold
still_threshold:
name: g0 still threshold
g1:
move_threshold:
name: g1 move threshold
still_threshold:
name: g1 still threshold
g2:
move_threshold:
name: g2 move threshold
still_threshold:
name: g2 still threshold
g3:
move_threshold:
name: g3 move threshold
still_threshold:
name: g3 still threshold
g4:
move_threshold:
name: g4 move threshold
still_threshold:
name: g4 still threshold
g5:
move_threshold:
name: g5 move threshold
still_threshold:
name: g5 still threshold
g6:
move_threshold:
name: g6 move threshold
still_threshold:
name: g6 still threshold
g7:
move_threshold:
name: g7 move threshold
still_threshold:
name: g7 still threshold
g8:
move_threshold:
name: g8 move threshold
still_threshold:
name: g8 still threshold
button:
- platform: ld2410
factory_reset:
name: "LD2410 Factory Reset"
restart:
name: "LD2410 Reboot"
query_params:
name: "LD2410 Query Params"
- platform: restart
icon: mdi:power-cycle
name: "ESP Reboot"
text_sensor:
- platform: ld2410
version:
name: "LD2410 Firmware Version"
mac_address:
name: "LD2410 MAC Address"
select:
- platform: ld2410
distance_resolution:
name: "Distance Resolution"
baud_rate:
name: "Baud Rate"
light_function:
name: Light Function
out_pin_level:
name: Out Pin Level
light:
- platform: status_led
name: sys_status
pin:
number: GPIO8
inverted: True
internal: True
restore_mode: ALWAYS_OFF
Alternatively, you can just use this bit, which imports the rest of the config from Github.
Just replace the items under substitutions.
This “packages” is pulling in the rest of the configs from here:
I totally missed that I posted this in the wrong thread. Whoopsies.
Are you using a Dashboard with a View Type of Panel (1 card) and tried in landscape mode? The screenshot above that shows the full area is from a PC and not a phone.
If you want to view on the phone, you will need to adjust the height value.
Experiment with the value and see what you get.
Alternatively, leave the height value there but change the X-axis range from 4000 to -4000 to 6000 to -6000 which should widen the chart.
There is no one size fits all due to the many different layouts and devices that can be used.
Hi, just updated to latest esphome addon and I’m getting a bunch of errors with the default yaml (component not found)
Any way to fix this?
@athua The Plotly graph is amazing! Thank you so much.
Just one thing - if your HA instance is set up for Imperial units like we are in the USA the positions all get converted to inches, so to make the graphs work right you have to multiply the target position x/y data by 25.4.
Confusingly, of course, all of the settings remain in mm.
Not sure if there is any good way to automate the adjustments or if this is something us backwards folks in the USA will just have to fix manually and live with.
In any case - THANK YOU for taking the time to put this together and publish it.
Thanks. It took lots of experimenting with different graphing tools before settling on Plotly which supported what I wanted by allowing a custom X-axis that was not time based.
As the data from the sensors are returning metric, maybe you could try creating template sensors to convert each of the metric sensors and then use those for the graph.
Maybe something like this:
template:
- sensor:
- name: "Target1_x_inches"
state: |
{{(states('sensor.target1_x_presence_radar')|float / 25.4) | round(2)}}
unit_of_measurement: "in"
state_class: "measurement"
Or do I have it around the wrong way around and the sensors are returning the values in imperial units but the graph is in metric? If its the graph, you could try chaging the axis limits from 6000 to 236.22 (6000/25.4) which should hopefully graph the points in inches rather than mm.