From fcd5bc52d3e8fc27bbe092b39b6b5951c3577b7e Mon Sep 17 00:00:00 2001 From: rafal Date: Sun, 18 Jan 2026 23:49:09 +0000 Subject: [PATCH] Upload files to "/" --- binary_sensors.yaml | 100 ++++++++++++++++++++++++ buttons.yaml | 36 +++++++++ ld2450_uart.h | 185 ++++++++++++++++++++++++++++++++++++++++++++ led_time_uart.yaml | 25 ++++++ numbers.yaml | 47 +++++++++++ 5 files changed, 393 insertions(+) create mode 100644 binary_sensors.yaml create mode 100644 buttons.yaml create mode 100644 ld2450_uart.h create mode 100644 led_time_uart.yaml create mode 100644 numbers.yaml diff --git a/binary_sensors.yaml b/binary_sensors.yaml new file mode 100644 index 0000000..12923db --- /dev/null +++ b/binary_sensors.yaml @@ -0,0 +1,100 @@ +# ======================= +# BINARY SENSORS +# ======================= +binary_sensor: + + - platform: status + name: Online + id: ink_ha_connected + + - platform: ld2410 + has_target: + name: Presence + id: ld2410_has_target + has_moving_target: + name: Moving Target + id: has_moving_target + has_still_target: + name: Still Target + id: has_still_target + + - platform: gpio + name: "OUT Pin" + id: ld2410_out_pin + pin: + number: GPIO16 + mode: + input: true + inverted: false + device_class: motion + + # ===== STREFA 1 (GŁÓWNA – WC / ŁAZIENKA) ===== + - platform: template + id: presence_zone_1 + name: "Presence Zone 1" + device_class: occupancy + filters: + - delayed_off: 1s # BYŁO: 5s → TERAZ SZYBKO + lambda: |- + // brak targetu = brak obecności + if (!id(ld2410_has_target).state) return false; + + // ====================== + // FAST PATH – RUCH + // ====================== + if (id(has_moving_target).state && + id(moving_distance).has_state() && + id(moving_distance).state > 0) { + + float dm = id(moving_distance).state; + + if (dm >= id(zone1_min_distance).state && + dm <= id(zone1_max_distance).state) { + return true; // NATYCHMIASTOWA REAKCJA + } + } + + // ====================== + // STABLE PATH – STILL + // ====================== + if (id(has_still_target).state && + id(still_distance).has_state() && + id(still_distance).state > 0) { + + float ds = id(still_distance).state; + + // ignorowanie odbić (lustra / drzwi / kot) + if (id(ignore_static_reflections).state && + id(still_energy).has_state() && + id(still_energy).state < 30) { + return false; + } + + return ds >= id(zone1_min_distance).state && + ds <= id(zone1_max_distance).state; + } + + // fallback: target jest → obecność + return true; + + # ===== STREFA 2 (DALEJ / ZA DRZWIAMI) ===== + - platform: template + id: presence_zone_2 + name: "Presence Zone 2" + device_class: occupancy + filters: + - delayed_off: 1s # BYŁO: 5s → TERAZ SZYBKO + lambda: |- + if (!id(ld2410_has_target).state) return false; + + if (id(has_moving_target).state && + id(moving_distance).has_state() && + id(moving_distance).state > 0) { + + float dm = id(moving_distance).state; + + return dm >= id(zone2_min_distance).state && + dm <= id(zone2_max_distance).state; + } + + return false; diff --git a/buttons.yaml b/buttons.yaml new file mode 100644 index 0000000..2ec3d2d --- /dev/null +++ b/buttons.yaml @@ -0,0 +1,36 @@ +# ======================= +# BUTTONS +# ======================= +button: + - platform: template + name: "Enable LD2410 BLE" + icon: mdi:bluetooth + entity_category: config + on_press: + lambda: |- + id(ld2410_radar)->ble_control(true); + + - platform: template + name: "Disable LD2410 BLE" + icon: mdi:bluetooth-off + entity_category: config + on_press: + lambda: |- + id(ld2410_radar)->ble_control(false); + + - platform: template + name: "LD2410 Reboot" + icon: mdi:radar + entity_category: config + on_press: + lambda: |- + id(ld2410_radar)->reboot(); + + - platform: restart + name: "ESP Reboot" + icon: mdi:power-cycle + + - platform: factory_reset + name: Factory Reset + id: factory_reset_all + disabled_by_default: true diff --git a/ld2450_uart.h b/ld2450_uart.h new file mode 100644 index 0000000..7349434 --- /dev/null +++ b/ld2450_uart.h @@ -0,0 +1,185 @@ +#include "esphome.h" + +#define CHECK_BIT(var, pos) (((var) >> (pos)) & 1) +#define STATE_SIZE 8 +#define TARGETS 3 + +static const char *TAG = "ld2450"; +class LD2450 : public PollingComponent, public UARTDevice +{ +public: + LD2450(UARTComponent *parent) : UARTDevice(parent) {} + + // BinarySensor *lastCommandSuccess = new BinarySensor(); + Sensor *lastCommandSuccess = new Sensor(); + Sensor *target1Resolution = new Sensor(); + Sensor *target1Speed = new Sensor(); + Sensor *target1X = new Sensor(); + Sensor *target1Y = new Sensor(); + Sensor *target2Resolution = new Sensor(); + Sensor *target2Speed = new Sensor(); + Sensor *target2X = new Sensor(); + Sensor *target2Y = new Sensor(); + Sensor *target3Resolution = new Sensor(); + Sensor *target3Speed = new Sensor(); + Sensor *target3X = new Sensor(); + Sensor *target3Y = new Sensor(); + Sensor *targets = new Sensor(); + + uint32_t lastPeriodicMillis = millis(); + + uint16_t twoByteToUint(char firstByte, char secondByte) { + return (uint16_t)(secondByte << 8) + firstByte; + } + + void reportTargetInfo(int target, char *raw) { + int16_t newX, newY, newSpeed, sum; + uint16_t newResolution; + + ESP_LOGV(TAG, "Will reporting taget %d", target); + + switch (target) { + case 0: + newX = twoByteToUint(raw[0], raw[1] & 0x7F); + if (raw[1] >> 7 != 0x1) + newX = 0 - newX / 10; + else + newX = newX / 10; + if (target1X->get_state() != newX) + target1X->publish_state(newX); + newY = twoByteToUint(raw[2], raw[3] & 0x7F); + if (raw[3] >> 7 != 0x1) + newY = 0 - newY / 10; + else + newY = newY / 10; + if (target1Y->get_state() != newY) + target1Y->publish_state(newY); + newSpeed = twoByteToUint(raw[4], raw[5] & 0x7F); + if (raw[5] >> 7 != 0x1) + newSpeed = 0 - newSpeed; + if (target1Speed->get_state() != newSpeed) + target1Speed->publish_state(newSpeed); + newResolution = twoByteToUint(raw[6], raw[7]); + if (target1Resolution->get_state() != newResolution) + target1Resolution->publish_state(newResolution); + break; + case 1: + newX = twoByteToUint(raw[0], raw[1] & 0x7F); + if (raw[1] >> 7 != 0x1) + newX = 0 - newX / 10; + else + newX = newX / 10; + if (target2X->get_state() != newX) + target2X->publish_state(newX); + newY = twoByteToUint(raw[2], raw[3] & 0x7F); + if (raw[3] >> 7 != 0x1) + newY = 0 - newY / 10; + else + newY = newY / 10; + if (target2Y->get_state() != newY) + target2Y->publish_state(newY); + newSpeed = twoByteToUint(raw[4], raw[5] & 0x7F); + if (raw[5] >> 7 != 0x1) + newSpeed = 0 - newSpeed; + if (target2Speed->get_state() != newSpeed) + target2Speed->publish_state(newSpeed); + newResolution = twoByteToUint(raw[6], raw[7]); + if (target2Resolution->get_state() != newResolution) + target2Resolution->publish_state(newResolution); + break; + case 2: + newX = twoByteToUint(raw[0], raw[1] & 0x7F); + if (raw[1] >> 7 != 0x1) + newX = 0 - newX / 10; + else + newX = newX / 10; + if (target3X->get_state() != newX) + target3X->publish_state(newX); + newY = twoByteToUint(raw[2], raw[3] & 0x7F); + if (raw[3] >> 7 != 0x1) + newY = 0 - newY / 10; + else + newY = newY / 10; + if (target3Y->get_state() != newY) + target3Y->publish_state(newY); + newSpeed = twoByteToUint(raw[4], raw[5] & 0x7F); + if (raw[5] >> 7 != 0x1) + newSpeed = 0 - newSpeed; + if (target3Speed->get_state() != newSpeed) + target3Speed->publish_state(newSpeed); + newResolution = twoByteToUint(raw[6], raw[7]); + if (target3Resolution->get_state() != newResolution) + target3Resolution->publish_state(newResolution); + break; + } + sum = 0; + if (target1Resolution->get_state() > 0){ + sum+=1; + } + if (target2Resolution->get_state() > 0){ + sum+=1; + } + if (target3Resolution->get_state() > 0){ + sum+=1; + } + if (targets->get_state() != sum){ + targets->publish_state(sum); + } + } + + void handlePeriodicData(char *buffer, int len) { + if (len < 29) + return; // 4 frame start bytes + 2 length bytes + 1 data end byte + 1 crc byte + 4 frame end bytes + if (buffer[0] != 0xAA || buffer[1] != 0xFF || buffer[2] != 0x03 || buffer[3] != 0x00) + return; // check 4 frame start bytes + if (buffer[len - 2] != 0x55 || buffer[len - 1] != 0xCC) + return; // data end=0x55, 0xcc + char stateBytes[STATE_SIZE]; + + /* + Reduce data update rate to prevent home assistant database size glow fast + */ + uint32_t currentMillis = millis(); + if (currentMillis - lastPeriodicMillis < 1000) + return; + lastPeriodicMillis = currentMillis; + for (int i = 0; i < TARGETS; i++) { + memcpy(stateBytes, &buffer[4 + i * STATE_SIZE], STATE_SIZE); + reportTargetInfo(i, stateBytes); + } + } + + void readline(int readch, char *buffer, int len) { + static int pos = 0; + if (readch >= 0) { + if (pos < len - 1) { + buffer[pos++] = readch; + buffer[pos] = 0; + } else { + pos = 0; + } + if (pos >= 4) { + if (buffer[pos - 2] == 0x55 && buffer[pos - 1] == 0xCC) { + handlePeriodicData(buffer, pos); + pos = 0; // Reset position index ready for next time + } + } + } + return; + } + + void setup() override { + set_update_interval(15000); + } + + void loop() override { + const int max_line_length = 160; + static char buffer[max_line_length]; + while (available()) { + readline(read(), buffer, max_line_length); + } + } + + void update(){ + } +}; diff --git a/led_time_uart.yaml b/led_time_uart.yaml new file mode 100644 index 0000000..f9162d1 --- /dev/null +++ b/led_time_uart.yaml @@ -0,0 +1,25 @@ +# ======================= +# LED / TIME / UART +# ======================= +light: + - platform: status_led + name: sys_status + pin: GPIO15 + internal: true + restore_mode: ALWAYS_OFF + +time: + - platform: sntp + id: time_sntp + +uart: + id: uart_ld2410 + tx_pin: GPIO18 + rx_pin: GPIO33 + baud_rate: 256000 + parity: NONE + stop_bits: 1 + +ld2410: + timeout: 150s + id: ld2410_radar diff --git a/numbers.yaml b/numbers.yaml new file mode 100644 index 0000000..51e364e --- /dev/null +++ b/numbers.yaml @@ -0,0 +1,47 @@ +# ======================= +# NUMBERS (SUWAKI) +# ======================= +number: + - platform: template + name: "Zone 1 MIN distance (cm)" + id: zone1_min_distance + min_value: 0 + max_value: 200 + step: 10 + initial_value: 0 + restore_value: true + optimistic: true + entity_category: config + + - platform: template + name: "Zone 1 MAX distance (cm)" + id: zone1_max_distance + min_value: 50 + max_value: 400 + step: 10 + initial_value: 220 + restore_value: true + optimistic: true + entity_category: config + + - platform: template + name: "Zone 2 MIN distance (cm)" + id: zone2_min_distance + min_value: 200 + max_value: 600 + step: 10 + initial_value: 230 + restore_value: true + optimistic: true + entity_category: config + + - platform: template + name: "Zone 2 MAX distance (cm)" + id: zone2_max_distance + min_value: 300 + max_value: 800 + step: 10 + initial_value: 450 + restore_value: true + optimistic: true + entity_category: config