LD2410C Presence Sensor – First Steps & Beyond

LD2410C Presence Sensor

The LD2410C is a mmWave presence/movement sensor that is really cheap, widely available and with a little help and unserstanding, makes a GREAT alternative to infra-red and ultrasonic detectors – and at the price is suitable for everything from human presence/movement detection in farly large rooms right down to light controls for storage rooms and cabinets.

I’ve spent AGES getting to grips with this device, not helped by projects that don’t actually work, other projects with code behind paywalls and more. Finally this week I stumbled on a video by an American chap who has done a great job of explaining the sensor and how to get started using it.

Commercial pre-built units

So, I started this quest using Apollo Automation devices like the Apollo Pro-1 mm Sensor and others from the same company and they are GREAT, but not cheap. Depending where you live and your budget, they could end up being just too expensive for widespread use throughout the home. I love the Apollo stuff – see my various articles – just search “Apollo”.

Then I started looking at the LD2450 and finally decided to try out the LD2410C after seeing various YouTube videos on the subject… but I hit so many dead ends – until now.

Here we are… I have a few LD2410C boards from AliExpress… simply put, they generally have a 5-way connector which includes ground, serial RX, serial TX, an output for a LED and VCC. For now we can ignore the output and focus on the 4 pins – ground is obvious, Vcc to 5v, RX and TX.

Stand-alone use of the LD2410C – no ESP

Next I grabbed an FTDI – a small board which plugs into your computer’s serial port (or a hub) and gives you 3v3 volt level serial RX and TX as well as 3v3 and 5v outputs – again available for next to nothing from AliExpress. Going to their CHOICE items it shoud be possible to get a couple of LD2410C devices and an FTDI for around 10 Euros including postage. Today I generally choose serial devices like FTDIs with USB-C as I’m trying to standardise on USB-C but I tend to keep a few USB-A to C adaptors around as not all of my USB Hubs have USB-C connectors.

Anyway here is an older USB-A FTDI connected to an LD2410C sensor… the FTDI is providing the 5V power (around 79ma needed) and also serial RX and TX (connected to the LD2410c REVERSED – i.e. RX to TX, TX to RX.

FTDI and LD2410C connected
LD2410C

And some detail… looking at the LD2410C, on the right you see the device – sensor side visible. Of interest, my device is about 20mm by 15mm. As you see I’ve connected TX, TX, ground and VCC. That’s it – for a mere couple of euros, this device, talking to an APP or a PC for example, can reliably detect human presence and movement. Doing something useful takes a little more work but one thing at a time.

On the active face you are viewing on the right here, the device senses from straight out for maybe a 60 degree angle and can work from a few cm to several metres distance- though my experience so far suggests that maybe 20cm outwards works best. Oh and yes the LD2410c gets warm in use. Below is a better view of my FTDI and the connections.

USB-A FTDI

Hopefully the images above make the connections quite clear. I’ve done a video (below – embedded) to go with this article and hopefully between the two you’ll get a start on using these devices. As an aside, the LD2410C has a light sensor. Could be handy when used as a room presence sensor for lighting control to avoid turning on lights when it’s already bright out there. I’m not going into that here.

Don’t be cheap – An ESP32 S3 Mini costs only 3 £euros or equiv. from China

So a video fron Dronebot Workshop is what finally got me going – thanks to the owner for that – here’s his video for reference. There are two basic ways to talk to the LD2410C, Bluetooth or the serial pins. I’ll start with the latter. In a blog entry accompanying the above video, the writer mentions a free EXE tool available from the LD2410C manufacturer which uses Bluetooth. Here’s a 3-year-old Google Drive version of that tool. See the last item in the link above.

LD2410 Tool

On my PC I tried this tool and it works – see image on the right.

But that assumes you are using a PC… and it’s only SO useful – so I prefer the web-based online tool also described in the video. But first – the tool to the right and the web tool both need to talk to your FTDI via the serial port it creates when connected to your PC or whatever. Getting FTDIs to work is beyond the scope of this article – for me, they always seem to work “out of the box”.

I just plug them into a USB hub connected to my PC and a new serial port magically appears. That’s the port you’d connect to on the single .EXE program shown above right – and given the wiring arrangement I showed above, on hitting CONNECT at the default baud rate of 25600, you would immediately start seeing something happening.

However, I’m going to go straight to the web tool instead as I didn’t find the above THAT intuitive – similarly the web tool needs to connect to the serial port created by the FTDI. Here’s the link, this time not for a download but directly to the web-based LD2410 Configurator. And here it is in action – my video shows this all graphically and hopefully between this and the video you’ll see how easy it is to start with the LD2410C.

LD2410 Configurator

On loading up the page – and with your FTDI plugged into your computing device and to the LD2450C – this is what you should see.

I didn’t use the “Display Advanced connection options” box, I just picked a port as I showed above – and connected.

At this point I was up and running. Now we have the full main page of the LD2410 Configurator – showing live presence detection in the top left blue box and movement in the red box next to it – all as below. I go into detail in the video.

LD2410 Configurator

Simply watching the panel above and maybe playing with the controls on the sbove right, hopefully you should start to see what the LD2410C device is capable of (and it’s limits – the values do tend to jump about a bit so in practice a little filtering will be useful).

The next step which I’ll go into later (as not everyone uses ESPHOME and Home Assistant) is to get that data out into a form that is useful. On DronebotWorkshop’s article he goes into using Arduino with the LD2410C device and also refers to the HLKRadarTool App for Android. If you look at the control screen above you can see that you can turn Bluetooth on and off in the device from that web page. Turning it on I found the LD2410C device appear in the APP which I’d grabbed from the Android Playstore, but personally I decided to stick with the above web page and go off looking at the official ESPHOME web page where they go into code for the LD2410C.

See my video below – and try to avoid noticing that I’m not looking at the camera… I was too busy trying to get to grips with using OBSBOT Studio’s multi-window facilities.

Basics over – now for something real

NOW, if you’ve waded through that lot !!!! CONGRATULATIONS !!!!, you backed the right horse because after wasting over a day on an ESP32-C3 board which worked with slightly modded standard ESP-HOME LD2410 code – exciting in itself, today I tried an ESP32-S3 and after realising I’d randomly picked pins GPIO9 and 10 for RX and TX and not only got them the wrong way around, but also physically connected pins 8 and 9, I finally not only got the code working in ESP-HOME but it ALSO magically appeared in Home Assistant and that’s where the REAL magic starts…

Choice of ESP, Opportunities and Limits

Couple of things first, I’m sure there’s a reason the C3 is being awkward, we’ll get to that – my pal Antonio and I will work on that, secondly on the ESP32 boards it is important you realise you can’t use the standard UART1. In the end I just picked gpio9 and gpio10 as they are among a list of pins that can be used as UARTS. I did NOTHING more than specify gpio9 and gpio10 and lo and behold, an ESP32-S3 mini talking to the LD2410C and as it happens, the S3 Mini has a WS2812B LED on pin 48. Thats my next job… visual indication of distance as I use on my Apollo units.

So, in my case I also had problems (and I’ve had it for ages) trying to create projects using my main PC while Home Assistant and hence ESP-HOME are sitting on another machine… all of that went away when I continued as before but for the first flash, told the controls to compile to the machine running ESP-HOME – and plugging the board into the same machine – voila. Oh and attaching a fixed IP (any spare address) to the chip guarantees a better flashing experience.

I’m going to put the lot up here, no pay walls, no missing bits – bear with me…

Meanwhile as a teaser – the code… not done anything with the blue light.. and that encryption key your installation will have to generate it’s own (automatically)..

esphome:
  name: s3-radar
  friendly_name: s3-radar

esp32:
  board: esp32-s3-devkitc-1
  framework:
    type: arduino

# Enable logging
logger:

# Enable Home Assistant API
api:
  encryption:
    key: "the_esp-home-provided_api_key"

ota:
  - platform: esphome
    password: "the_esp-home-provided_ota_key"

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

  manual_ip:
    # Static IP settings
    static_ip: 192.168.1.226  # Your desired static IP address
    gateway: 192.168.1.1    # Your router's IP address
    subnet: 255.255.255.0  # Your network's subnet mask

  # Enable fallback hotspot (captive portal) in case wifi connection fails
  ap:
    ssid: "S3-Radar Fallback Hotspot"
    password: "the_esp-home-provided_fallback_key"

captive_portal:

web_server:
  port: 80
  include_internal: true

 
# ---------------------------------------------------------------------------------
 
light:
  - platform: binary
    name: "Blue Status Light"
    output: light_output
    id: led_light
    restore_mode: ALWAYS_OFF
 
output:
  - platform: gpio
    pin: GPIO2
    id: light_output
 
uart:
  id: ld2410_uart
  tx_pin: GPIO10 # TX
  rx_pin: GPIO9 # RX
  baud_rate: 256000
  parity: NONE
  stop_bits: 1
 
ld2410:
  uart_id: ld2410_uart
  id: ld2410_comp
 
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
 
button:
  - platform: ld2410
    factory_reset:
      name: "factory reset"
    restart:
      name: "restart"
    query_params:
      name: query params
 
number:
  - platform: ld2410
    timeout:
      name: timeout
    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
    light_threshold:
      name: light threshold
 
text_sensor:
  - platform: ld2410
    version:
      name: "presence sensor version"
    mac_address:
      name: "presence sensor mac address"
 
switch:
  - platform: ld2410
    engineering_mode:
      name: "engineering mode"
    bluetooth:
      name: control Bluetooth
 
sensor:
  - platform: internal_temperature
    name: "Internal Temperature"
 
  - platform: ld2410
    moving_distance:
      name: "Moving distance (cm)"
    still_distance:
      name: "Still Distance (cm)"
    moving_energy:
      name: "Move Energy (%)"
    still_energy:
      name: "Still Energy (%)"
    detection_distance:
      name: "Distance Detection (cm)"
    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
    light:
      name: light
 
binary_sensor:
 
  - platform: ld2410
    has_target:
      name: Presence
    has_moving_target:
      name: Moving Target
    has_still_target:
      name: Still Target

       

The code above was my first attempt ands had no WS2812B code – just the LD2450C.

And now with the aid of ChatGPT I’ve just LEAPFROGGED my previous attempts – the code below – for the ESP32-S3 with RGB LED on GPIO48 – and the LD2410C….. now the onboard RGB LED changes colour depending on presence distance…. ALL WITHIN the ESP32-S3 board with ESP-HOME – not relying on Home Assistant at all. MAGIC. Here it is… I disconnected the blue LED, may as well scrap that first LED platform eventually…

  • platform: binary
    name: “Blue Status Light”
    output: light_output
    id: led_light
    restore_mode: ALWAYS_OFF
esphome:
  name: s3-radar
  friendly_name: s3-radar

esp32:
  board: esp32-s3-devkitc-1
  framework:
    type: arduino

# Enable logging
logger:

# Enable Home Assistant API
api:
  encryption:
    key: "the_esp-home-provided_api_key"

ota:
  - platform: esphome
    password: "the_esp-home-provided_ota_key"

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

  manual_ip:
    static_ip: 192.168.1.226
    gateway: 192.168.1.1
    subnet: 255.255.255.0

  ap:
    ssid: "S3-Radar Fallback Hotspot"
    password: "the_esp-home-provided_fallback_key"

captive_portal:

web_server:
  port: 80
  include_internal: true

# ---------------------------------------------------------------------------------
light:
  - platform: binary
    name: "Blue Status Light"
    output: light_output
    id: led_light
    restore_mode: ALWAYS_OFF

  - platform: neopixelbus
    type: GRB
    variant: WS2812
    pin: GPIO48
    num_leds: 1
    name: "NeoPixel Light"
    id: neo_pixel_light

output:
  - platform: gpio
    pin: GPIO2
    id: light_output

uart:
  id: ld2410_uart
  tx_pin: GPIO10
  rx_pin: GPIO9
  baud_rate: 256000
  parity: NONE
  stop_bits: 1

ld2410:
  uart_id: ld2410_uart
  id: ld2410_comp

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

button:
  - platform: ld2410
    factory_reset:
      name: "factory reset"
    restart:
      name: "restart"
    query_params:
      name: query params

number:
  - platform: ld2410
    timeout:
      name: timeout
    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
    light_threshold:
      name: light threshold

text_sensor:
  - platform: ld2410
    version:
      name: "presence sensor version"
    mac_address:
      name: "presence sensor mac address"

switch:
  - platform: ld2410
    engineering_mode:
      name: "engineering mode"
    bluetooth:
      name: control Bluetooth

sensor:
  - platform: internal_temperature
    name: "Internal Temperature"

  - platform: ld2410
    moving_distance:
      name: "Moving distance (cm)"
    still_distance:
      name: "Still Distance (cm)"
      id: still_distance_sensor
    moving_energy:
      name: "Move Energy (%)"
    still_energy:
      name: "Still Energy (%)"
    detection_distance:
      name: "Distance Detection (cm)"
    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
    light:
      name: light

binary_sensor:
  - platform: ld2410
    has_target:
      name: Presence
    has_moving_target:
      name: Moving Target
    has_still_target:
      name: Still Target

# -------------------------------------------------------------------------------
# Local LED colour control based on still distance
interval:
  - interval: 2s
    then:
      - lambda: |-
          float dist = id(still_distance_sensor).state;
          ESP_LOGD("neo", "Still distance = %.1f cm", dist);

          if (dist <= 0) {
            // Turn off via ESPHome action
            auto call = id(neo_pixel_light).make_call();
            call.set_state(false);
            call.perform();
            return;
          }

          // Decide colour
          float r = 0.0, g = 0.0, b = 0.0;
          if (dist < 100) { r = 0.0; g = 1.0; b = 0.0; }         // Green
          else if (dist < 200) { r = 1.0; g = 1.0; b = 0.0; }    // Yellow
          else if (dist < 300) { r = 1.0; g = 0.5; b = 0.0; }    // Orange
          else if (dist < 400) { r = 1.0; g = 0.0; b = 0.0; }    // Red
          else { r = 0.0; g = 0.0; b = 1.0; }                   // Blue

          // Turn on NeoPixel via ESPHome light action
          auto call = id(neo_pixel_light).make_call();
          call.set_state(true);
          call.set_rgb(r,g,b);
          call.set_brightness(1.0);
          call.perform();

And here’s the quick and dirty video demo.. I have to work on those distances – but it’s getting there…

Did you notice I’d switched to the framework type Arduino (near the start) in the code above..

Well, that turned out to be a problem with my ESP-HOME installation having some carry-over out-of-date code – I hit the same problem with the ESP-C6 (another nice board with an RGB LED but this time on GPIO8). Eventually I we found a solution – uninstall the ESP-HOME add-in and reinstall it (trivial) though I did end up sticking with the ARDUINO framework for no particular reason. The simple uninstall and re-install clears cache and is harmless (you don’t lose your previous work) and then the ESP-IDF framework type once again works. I discovered this when I tried the C6 and was told that the Arduino framework type doesn’t support the C6. So I took the plunge – and after re-adding in ESP-HOME, I could use the C6 with esp-idf, changing the LED to GPIO8 – but then the original neopixel bus would not work with the C6 so I changed the WS2812 settings to use the esp32_rmt_led_strip platform with slightly different parameters – I suspect that will work with the earlier boards.

Here’s the code for the ESP32-C6… with a few mods to distances and that daft blue light commented out…

esphome:
  name: c6-radar
  friendly_name: c6-radar

esp32:
  board: esp32-c6-devkitc-1
  framework:
    type: esp-idf

# Enable logging
logger:

# Enable Home Assistant API
api:
  encryption:
    key: "the_esp-home-provided_api_key"

ota:
  - platform: esphome
    password: "the_esp-home-provided_ota_key"

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

  manual_ip:
    static_ip: 192.168.1.227
    gateway: 192.168.1.1
    subnet: 255.255.255.0

  # Enable fallback hotspot (captive portal) in case wifi connection fails
  ap:
    ssid: "C6-Radar Fallback Hotspot"
    password: "the_esp-home-provided_fallback_key"

captive_portal:

web_server:
  port: 80
  include_internal: true

# ---------------------------------------------------------------------------------
light:
#  - platform: binary
#    name: "Blue Status Light"
#    output: light_output
#    id: led_light
#    restore_mode: ALWAYS_OFF

  - platform: esp32_rmt_led_strip
    rgb_order: GRB
    pin: GPIO8
    num_leds: 1
    chipset: WS2812
    name: "NeoPixel Light"
    id: neo_pixel_light

#output:
#  - platform: gpio
#    pin: GPIO2
#    id: light_output

uart:
  id: ld2410_uart
  tx_pin: GPIO6
  rx_pin: GPIO7
  baud_rate: 256000
  parity: NONE
  stop_bits: 1

ld2410:
  uart_id: ld2410_uart
  id: ld2410_comp

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

button:
  - platform: ld2410
    factory_reset:
      name: "factory reset"
    restart:
      name: "restart"
    query_params:
      name: query params

number:
  - platform: ld2410
    timeout:
      name: timeout
    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
    light_threshold:
      name: light threshold

text_sensor:
  - platform: ld2410
    version:
      name: "presence sensor version"
    mac_address:
      name: "presence sensor mac address"

switch:
  - platform: ld2410
    engineering_mode:
      name: "engineering mode"
    bluetooth:
      name: control Bluetooth

sensor:
  - platform: internal_temperature
    name: "Internal Temperature"

  - platform: ld2410
    moving_distance:
      name: "Moving distance (cm)"
    still_distance:
      name: "Still Distance (cm)"
      id: still_distance_sensor
    moving_energy:
      name: "Move Energy (%)"
    still_energy:
      name: "Still Energy (%)"
    detection_distance:
      name: "Distance Detection (cm)"
    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
    light:
      name: light

binary_sensor:
  - platform: ld2410
    has_target:
      name: Presence
    has_moving_target:
      name: Moving Target
    has_still_target:
      name: Still Target

# -------------------------------------------------------------------------------
# Local LED colour control based on still distance
interval:
  - interval: 2s
    then:
      - lambda: |-
          float dist = id(still_distance_sensor).state;
          ESP_LOGD("neo", "Still distance = %.1f cm", dist);

          if (dist <= 0) {
            // Turn off via ESPHome action
            auto call = id(neo_pixel_light).make_call();
            call.set_state(false);
            call.perform();
            return;
          }

          // Decide colour
          float r = 0.0, g = 0.0, b = 0.0;
          if (dist < 80) { r = 0.0; g = 1.0; b = 0.0; }         // Green
          else if (dist < 160) { r = 1.0; g = 1.0; b = 0.0; }    // Yellow
          else if (dist < 240) { r = 1.0; g = 0.5; b = 0.0; }    // Orange
          else if (dist < 320) { r = 1.0; g = 0.0; b = 0.0; }    // Red
          else if (dist < 400) { r = 1.0; g = 0.0; b = 1.0; }    // purple
          else if (dist < 480) { r = 0.0; g = 0.0; b = 1.0; }    // blue
          else { r = 0.0; g = 0.0; b = 0.0; }                   // black

          // Turn on NeoPixel via ESPHome light action
          auto call = id(neo_pixel_light).make_call();
          call.set_state(true);
          call.set_rgb(r,g,b);
          call.set_brightness(1.0);
          call.perform();

The ultimate solution and some limits

As it happens I did some more reading and it seems that of these small boards, the ESP32-S3 has slightly more memory so I stuck with that and armed with success, began to get ambitious – I thought it would be nice to have a BME280 temperature/pressure/humidity sensor on the same board – GREAT idea – two gadgets in one… but for one thing – the latter uses an I2C library and also pulls in code for (in this case not used) SPI.

When it came to compile my latest miracle, the ESP-HOME linker would not have it. I thought that maybe the combined code of the LD2410C and the BME280 device might be too much – and I was right. However, I’d included in the ESP-HOME code (as I usually do) the basic web server so I could see results in a browser before looking to see if the device had been added to Home Assistant. Merely commenting out the web server made everything work – ChatGPT says it uses up to 300K just for the web server and I believe it.

Here is the code as it stands today, web server commented out but the rest all works a treat. Doing a little research it seems there may be ways to minimise code use so I can have my web server back – for example no API encryption if all of this is running on a trusted internal network. The web server has the biggest impact, followed by turning off logging or minimising it, turn off OTA (erm no thanks), the API encryption and other optimisations – none of that below – just no web server so no preview in a web page.. adjust the IP address to suit and don’t just use dhcp (dynamic addressing)..

Oh, below, I’ve changed the LD2410 serial pins to acommodate the standard I2C pins for the BME280..

HAH – last minute change – instead of disabling the web server, I disabled logging by setting the logging baud rate to 0. I could then re-enable the web server. My choice.

esphome:
  name: s3-bme
  friendly_name: s3-bme

esp32:
  board: esp32-s3-devkitc-1
  framework:
    type: arduino

# Disable logging
logger:
  level: DEBUG
  baud_rate: 0

# Enable Home Assistant API
api:
  encryption:
    key: "y82jDv7/Q3a5VP5ITf9WBwc86f+HtcBqmlK+qkPy+is="

ota:
  - platform: esphome
    password: "7a1e4dda0e189533af06d75347bce651"

wifi:
  ssid: !secret wifi_ssid
  password: !secret wifi_password
  
  manual_ip:
    # Static IP settings
    static_ip: 192.168.1.228  # Desired static IP address
    gateway: 192.168.1.1    # Your router's IP address
    subnet: 255.255.255.0  # Your network's subnet mask  

  # Enable fallback hotspot (captive portal) in case wifi connection fails
  ap:
    ssid: "S3-Bme Fallback Hotspot"
    password: "gEUATGjLMKVK"

# If logging is needed here, comment out the web server or play with a different ESP board...
web_server:
  port: 80
  include_internal: true

captive_portal:
    
i2c:
  sda: GPIO8
  scl: GPIO9
  scan: true
  id: bus_a
  
light:
  - platform: neopixelbus
    type: GRB
    variant: WS2812
    pin: GPIO48
    num_leds: 1
    name: "NeoPixel Light"
    id: neo_pixel_light

output:
  - platform: gpio
    pin: GPIO2
    id: light_output

sensor:
  - platform: bme280_i2c
    temperature:
     name: "BME280 Temperature"
     accuracy_decimals: 0
    pressure:
      name: "BME280 Pressure"
      accuracy_decimals: 0
    humidity:
      name: "BME280 Humidity"
      accuracy_decimals: 0


  - platform: internal_temperature
    name: "Internal Temperature"
  - platform: ld2410
    moving_distance:
      name: "Moving distance (cm)"
      id: moving_distance_sensor
    still_distance:
      name: "Still Distance (cm)"
      id: still_distance_sensor
    moving_energy:
      name: "Move Energy (%)"
    still_energy:
      name: "Still Energy (%)"
    detection_distance:
      name: "Distance Detection (cm)"
    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
    light:
      name: light



uart:
  id: ld2410_uart
  tx_pin: GPIO11
  rx_pin: GPIO10
  baud_rate: 256000
  parity: NONE
  stop_bits: 1

ld2410:
  uart_id: ld2410_uart
  id: ld2410_comp

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

button:
  - platform: ld2410
    factory_reset:
      name: "factory reset"
    restart:
      name: "restart"
    query_params:
      name: query params

number:
  - platform: ld2410
    timeout:
      name: timeout
    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
    light_threshold:
      name: light threshold

text_sensor:
  - platform: ld2410
    version:
      name: "presence sensor version"
    mac_address:
      name: "presence sensor mac address"

switch:
  - platform: ld2410
    engineering_mode:
      name: "engineering mode"
    bluetooth:
      name: control Bluetooth

binary_sensor:
  - platform: ld2410
    has_target:
      name: Presence
    has_moving_target:
      name: Moving Target
    has_still_target:
      name: Still Target

# -------------------------------------------------------------------------------
# Local LED colour control based on still distance
interval:
  - interval: 2s
    then:
      - lambda: |-
          float dist = id(moving_distance_sensor).state;

          if (dist <= 0) {
            // Turn off via ESPHome action
            auto call = id(neo_pixel_light).make_call();
            call.set_state(false);
            call.perform();
            return;
          }

          // Decide colour
          float r = 0.0, g = 0.0, b = 0.0;
          if (dist < 100) { r = 0.0; g = 1.0; b = 0.0; }         // Green
          else if (dist < 200) { r = 1.0; g = 1.0; b = 0.0; }    // Yellow
          else if (dist < 300) { r = 1.0; g = 0.5; b = 0.0; }    // Orange
          else if (dist < 400) { r = 1.0; g = 0.0; b = 0.0; }    // Red
          else { r = 0.0; g = 0.0; b = 1.0; }                   // Blue

          // Turn on NeoPixel via ESPHome light action
          auto call = id(neo_pixel_light).make_call();
          call.set_state(true);
          call.set_rgb(r,g,b);
          call.set_brightness(1.0);
          call.perform();

And by magic – in Home Assistant…

LD2410C and BME280 on ESP32-S3 in ESP-HOME
LD2410C and BME280 on ESP32-S3 in ESP-HOME
LD2410C and BME280 on ESP32-S3 in ESP-HOME
LD2410C and BME280 on ESP32-S3 in ESP-HOME
LD2410C and BME280 on ESP32-S3 in ESP-HOME

I missed out the MAC address line…

LD2410C and BME280 on ESP32-S3 in ESP-HOME
LD2410C and BME280 on ESP32-S3 in ESP-HOME

That’s it for now – to take this too much further I’m going to need an ESP with more memory – or a lot more knowledge 🙂

As you can see higher up, the sensors ARE working… and that onboard single WS2812 RGB LED is being automatically controlled – the icon colour changing magically with distance. Originally I did the control in Home Assistant – and you could do that.. or not… It’s more a nicety than an essential. I like to have visual feedback as to the working of the unit.

You can use any of those sensors as needed in Home Assistant automations, I often just use “moving target” and “still target” for my needs.

Thoughts welcome.

Leave a Reply

Your email address will not be published. Required fields are marked *

Leave the field below empty!


The maximum upload file size: 512 MB. You can upload: image, audio, video, document, spreadsheet, interactive, text, archive, code, other. Links to YouTube, Facebook, Twitter and other services inserted in the comment text will be automatically embedded. Drop file here