diff --git a/tasmota/include/tasmota_template.h b/tasmota/include/tasmota_template.h
index 946dffea0..f64bd3737 100644
--- a/tasmota/include/tasmota_template.h
+++ b/tasmota/include/tasmota_template.h
@@ -231,6 +231,7 @@ enum UserSelectablePins {
GPIO_TWAI_TX, GPIO_TWAI_RX, GPIO_TWAI_BO, GPIO_TWAI_CLK, // ESP32 TWAI serial interface
GPIO_C8_CO2_5K_TX, GPIO_C8_CO2_5K_RX, // C8-CO2-5K CO2 Sensor
GPIO_V9240_TX, GPIO_V9240_RX, // V9240 serial interface
+ GPIO_LD2402_TX, GPIO_LD2402_RX, // HLK-LD2402
GPIO_SENSOR_END };
// Error as warning to rethink GPIO usage with max 2045
@@ -509,6 +510,7 @@ const char kSensorNames[] PROGMEM =
D_SENSOR_TWAI_TX "|" D_SENSOR_TWAI_RX "|" D_SENSOR_TWAI_BO "|" D_SENSOR_TWAI_CLK "|"
D_SENSOR_C8_CO2_5K_TX "|" D_SENSOR_C8_CO2_5K_RX "|"
D_SENSOR_V9240_TX "|" D_SENSOR_V9240_RX
+ D_SENSOR_LD2402_TX "|" D_SENSOR_LD2402_RX
;
const char kSensorNamesFixed[] PROGMEM =
@@ -1126,6 +1128,10 @@ const uint16_t kGpioNiceList[] PROGMEM = {
AGPIO(GPIO_CM11_TXD), // CM110x Serial interface
AGPIO(GPIO_CM11_RXD), // CM110x Serial interface
#endif
+#ifdef USE_LD2402
+ AGPIO(GPIO_LD2402_TX), // HLK-LD2402 Serial interface
+ AGPIO(GPIO_LD2402_RX), // HLK-LD2402 Serial interface
+#endif
#ifdef USE_LD2410
AGPIO(GPIO_LD2410_TX), // HLK-LD2410 Serial interface
AGPIO(GPIO_LD2410_RX), // HLK-LD2410 Serial interface
diff --git a/tasmota/language/af_AF.h b/tasmota/language/af_AF.h
index aeb690224..b9ce56bb7 100644
--- a/tasmota/language/af_AF.h
+++ b/tasmota/language/af_AF.h
@@ -58,6 +58,7 @@
#define D_AS "as"
#define D_AT "at"
#define D_AUTO "AUTO"
+#define D_AUTO_UPDATE "Auto Update"
#define D_BATTERY "Battery"
#define D_BATT "Batt" // Short for Battery
#define D_BATTERY_CHARGE "Lading" // Battery charge in %
@@ -88,6 +89,7 @@
#define D_DEBUG "Ontfout"
#define D_DEWPOINT "Dou punt"
#define D_DISABLED "Gedeaktiveer"
+#define D_DISCONNECTED "Disconnected"
#define D_DISTANCE "Afstand"
#define D_DNS_SERVER "DNS"
#define D_DO "Opgeloste suurstof"
@@ -776,6 +778,8 @@
#define D_SENSOR_SDM630_RX "SDM630 Rx"
#define D_SENSOR_WE517_TX "WE517 Tx"
#define D_SENSOR_WE517_RX "WE517 Rx"
+#define D_SENSOR_LD2402_TX "LD2402 Tx"
+#define D_SENSOR_LD2402_RX "LD2402 Rx"
#define D_SENSOR_LD2410_TX "LD2410 Tx"
#define D_SENSOR_LD2410_RX "LD2410 Rx"
#define D_SENSOR_LD2410S_TX "LD2410S Tx"
diff --git a/tasmota/language/bg_BG.h b/tasmota/language/bg_BG.h
index 54e4fda15..8bcf0ed11 100644
--- a/tasmota/language/bg_BG.h
+++ b/tasmota/language/bg_BG.h
@@ -58,6 +58,7 @@
#define D_AS "като"
#define D_AT "at"
#define D_AUTO "АВТОМАТИЧНО"
+#define D_AUTO_UPDATE "Auto Update"
#define D_BATTERY "Battery"
#define D_BATT "Бат." // Short for Battery
#define D_BATTERY_CHARGE "Charge" // Battery charge in %
@@ -88,6 +89,7 @@
#define D_DEBUG "Премахване на дефекти"
#define D_DEWPOINT "Температура на оросяване"
#define D_DISABLED "Забранено"
+#define D_DISCONNECTED "Disconnected"
#define D_DISTANCE "Разстояние"
#define D_DNS_SERVER "Сървър на DNS"
#define D_DO "Разтворен кислород"
@@ -776,6 +778,8 @@
#define D_SENSOR_SDM630_RX "SDM630 Rx"
#define D_SENSOR_WE517_TX "WE517 Tx"
#define D_SENSOR_WE517_RX "WE517 Rx"
+#define D_SENSOR_LD2402_TX "LD2402 Tx"
+#define D_SENSOR_LD2402_RX "LD2402 Rx"
#define D_SENSOR_LD2410_TX "LD2410 Tx"
#define D_SENSOR_LD2410_RX "LD2410 Rx"
#define D_SENSOR_LD2410S_TX "LD2410S Tx"
diff --git a/tasmota/language/ca_AD.h b/tasmota/language/ca_AD.h
index 373ee312f..61501b131 100644
--- a/tasmota/language/ca_AD.h
+++ b/tasmota/language/ca_AD.h
@@ -58,6 +58,7 @@
#define D_AS "com"
#define D_AT "at"
#define D_AUTO "AUTO"
+#define D_AUTO_UPDATE "Auto Update"
#define D_BATTERY "Battery"
#define D_BATT "Bat" // Short for Battery
#define D_BATTERY_CHARGE "Charge" // Battery charge in %
@@ -88,6 +89,7 @@
#define D_DEBUG "Depuració"
#define D_DEWPOINT "Punt de rossada"
#define D_DISABLED "Deshabilitat"
+#define D_DISCONNECTED "Disconnected"
#define D_DISTANCE "Distancia"
#define D_DNS_SERVER "Servidor DNS"
#define D_DO "Oxígen dissolt"
@@ -776,6 +778,8 @@
#define D_SENSOR_SDM630_RX "SDM630 Rx"
#define D_SENSOR_WE517_TX "WE517 Tx"
#define D_SENSOR_WE517_RX "WE517 Rx"
+#define D_SENSOR_LD2402_TX "LD2402 Tx"
+#define D_SENSOR_LD2402_RX "LD2402 Rx"
#define D_SENSOR_LD2410_TX "LD2410 Tx"
#define D_SENSOR_LD2410_RX "LD2410 Rx"
#define D_SENSOR_LD2410S_TX "LD2410S Tx"
diff --git a/tasmota/language/cs_CZ.h b/tasmota/language/cs_CZ.h
index 7e4a1d6a2..ef7f3ef19 100644
--- a/tasmota/language/cs_CZ.h
+++ b/tasmota/language/cs_CZ.h
@@ -58,6 +58,7 @@
#define D_AS "jako"
#define D_AT "at"
#define D_AUTO "AUTO"
+#define D_AUTO_UPDATE "Auto Update"
#define D_BATTERY "Battery"
#define D_BATT "Batt" // Short for Battery
#define D_BATTERY_CHARGE "Charge" // Battery charge in %
@@ -88,6 +89,7 @@
#define D_DEBUG "Debug"
#define D_DEWPOINT "Dew point"
#define D_DISABLED "Zablokováno"
+#define D_DISCONNECTED "Disconnected"
#define D_DISTANCE "Distance"
#define D_DNS_SERVER "Server DNS"
#define D_DO "Disolved Oxygen"
@@ -776,6 +778,8 @@
#define D_SENSOR_SDM630_RX "SDM630 Rx"
#define D_SENSOR_WE517_TX "WE517 Tx"
#define D_SENSOR_WE517_RX "WE517 Rx"
+#define D_SENSOR_LD2402_TX "LD2402 Tx"
+#define D_SENSOR_LD2402_RX "LD2402 Rx"
#define D_SENSOR_LD2410_TX "LD2410 Tx"
#define D_SENSOR_LD2410_RX "LD2410 Rx"
#define D_SENSOR_LD2410S_TX "LD2410S Tx"
diff --git a/tasmota/language/de_DE.h b/tasmota/language/de_DE.h
index d48df566b..f00f11fcf 100644
--- a/tasmota/language/de_DE.h
+++ b/tasmota/language/de_DE.h
@@ -58,6 +58,7 @@
#define D_AS "als"
#define D_AT "an"
#define D_AUTO "AUTO"
+#define D_AUTO_UPDATE "Auto Update"
#define D_BATTERY "Batterie"
#define D_BATT "Batt" // Short for Battery
#define D_BATTERY_CHARGE "Ladung" // Battery charge in %
@@ -88,6 +89,7 @@
#define D_DEBUG "debug"
#define D_DEWPOINT "Taupunkt"
#define D_DISABLED "deaktiviert"
+#define D_DISCONNECTED "Disconnected"
#define D_DISTANCE "Abstand"
#define D_DNS_SERVER "DNS-Server"
#define D_DO "gelöster Sauerstoff"
@@ -776,6 +778,8 @@
#define D_SENSOR_SDM630_RX "SDM630 Rx"
#define D_SENSOR_WE517_TX "WE517 Tx"
#define D_SENSOR_WE517_RX "WE517 Rx"
+#define D_SENSOR_LD2402_TX "LD2402 Tx"
+#define D_SENSOR_LD2402_RX "LD2402 Rx"
#define D_SENSOR_LD2410_TX "LD2410 Tx"
#define D_SENSOR_LD2410_RX "LD2410 Rx"
#define D_SENSOR_LD2410S_TX "LD2410S Tx"
diff --git a/tasmota/language/el_GR.h b/tasmota/language/el_GR.h
index 72f00e61c..0521ed281 100644
--- a/tasmota/language/el_GR.h
+++ b/tasmota/language/el_GR.h
@@ -58,6 +58,7 @@
#define D_AS "ως"
#define D_AT "at"
#define D_AUTO "ΑΥΤΟΜΑΤΟ"
+#define D_AUTO_UPDATE "Auto Update"
#define D_BATTERY "Battery"
#define D_BATT "Batt" // Short for Battery
#define D_BATTERY_CHARGE "Charge" // Battery charge in %
@@ -88,6 +89,7 @@
#define D_DEBUG "Debug"
#define D_DEWPOINT "Dew point"
#define D_DISABLED "Ανενεργό"
+#define D_DISCONNECTED "Disconnected"
#define D_DISTANCE "Απόσταση"
#define D_DNS_SERVER "Διακομιστής DNS"
#define D_DO "Disolved Oxygen"
@@ -776,6 +778,8 @@
#define D_SENSOR_SDM630_RX "SDM630 Rx"
#define D_SENSOR_WE517_TX "WE517 Tx"
#define D_SENSOR_WE517_RX "WE517 Rx"
+#define D_SENSOR_LD2402_TX "LD2402 Tx"
+#define D_SENSOR_LD2402_RX "LD2402 Rx"
#define D_SENSOR_LD2410_TX "LD2410 Tx"
#define D_SENSOR_LD2410_RX "LD2410 Rx"
#define D_SENSOR_LD2410S_TX "LD2410S Tx"
diff --git a/tasmota/language/en_GB.h b/tasmota/language/en_GB.h
index f9de343bc..ede817f25 100644
--- a/tasmota/language/en_GB.h
+++ b/tasmota/language/en_GB.h
@@ -58,6 +58,7 @@
#define D_AS "as"
#define D_AT "at"
#define D_AUTO "AUTO"
+#define D_AUTO_UPDATE "Auto Update"
#define D_BATTERY "Battery"
#define D_BATT "Batt" // Short for Battery
#define D_BATTERY_CHARGE "Charge" // Battery charge in %
@@ -88,6 +89,7 @@
#define D_DEBUG "Debug"
#define D_DEWPOINT "Dew point"
#define D_DISABLED "Disabled"
+#define D_DISCONNECTED "Disconnected"
#define D_DISTANCE "Distance"
#define D_DNS_SERVER "DNS Server"
#define D_DO "Disolved Oxygen"
@@ -776,6 +778,8 @@
#define D_SENSOR_SDM630_RX "SDM630 Rx"
#define D_SENSOR_WE517_TX "WE517 Tx"
#define D_SENSOR_WE517_RX "WE517 Rx"
+#define D_SENSOR_LD2402_TX "LD2402 Tx"
+#define D_SENSOR_LD2402_RX "LD2402 Rx"
#define D_SENSOR_LD2410_TX "LD2410 Tx"
#define D_SENSOR_LD2410_RX "LD2410 Rx"
#define D_SENSOR_LD2410S_TX "LD2410S Tx"
diff --git a/tasmota/language/es_ES.h b/tasmota/language/es_ES.h
index a17b1f628..d59695f99 100644
--- a/tasmota/language/es_ES.h
+++ b/tasmota/language/es_ES.h
@@ -58,6 +58,7 @@
#define D_AS "como"
#define D_AT "at"
#define D_AUTO "AUTO"
+#define D_AUTO_UPDATE "Auto Update"
#define D_BATTERY "Battery"
#define D_BATT "Bat" // Short for Battery
#define D_BATTERY_CHARGE "Charge" // Battery charge in %
@@ -88,6 +89,7 @@
#define D_DEBUG "Debug"
#define D_DEWPOINT "Punto de Rocío"
#define D_DISABLED "Deshabilitado"
+#define D_DISCONNECTED "Disconnected"
#define D_DISTANCE "Distancia"
#define D_DNS_SERVER "Servidor DNS"
#define D_DO "Oxígeno Disuelto"
@@ -776,6 +778,8 @@
#define D_SENSOR_SDM630_RX "SDM630 Rx"
#define D_SENSOR_WE517_TX "WE517 Tx"
#define D_SENSOR_WE517_RX "WE517 Rx"
+#define D_SENSOR_LD2402_TX "LD2402 Tx"
+#define D_SENSOR_LD2402_RX "LD2402 Rx"
#define D_SENSOR_LD2410_TX "LD2410 Tx"
#define D_SENSOR_LD2410_RX "LD2410 Rx"
#define D_SENSOR_LD2410S_TX "LD2410S Tx"
diff --git a/tasmota/language/fr_FR.h b/tasmota/language/fr_FR.h
index 403304737..51529ee98 100644
--- a/tasmota/language/fr_FR.h
+++ b/tasmota/language/fr_FR.h
@@ -58,6 +58,7 @@
#define D_AS "comme"
#define D_AT "at"
#define D_AUTO "AUTO"
+#define D_AUTO_UPDATE "Auto Update"
#define D_BATTERY "Battery"
#define D_BATT "Batt" // Short for Battery
#define D_BATTERY_CHARGE "Charge" // Battery charge in %
@@ -88,6 +89,7 @@
#define D_DEBUG "Debug"
#define D_DEWPOINT "Point de rosée"
#define D_DISABLED "Désactivé"
+#define D_DISCONNECTED "Disconnected"
#define D_DISTANCE "Distance"
#define D_DNS_SERVER "Serveur DNS"
#define D_DO "Oxygène dissout"
@@ -776,6 +778,8 @@
#define D_SENSOR_SDM630_RX "SDM630 Rx"
#define D_SENSOR_WE517_TX "WE517 Tx"
#define D_SENSOR_WE517_RX "WE517 Rx"
+#define D_SENSOR_LD2402_TX "LD2402 Tx"
+#define D_SENSOR_LD2402_RX "LD2402 Rx"
#define D_SENSOR_LD2410_TX "LD2410 Tx"
#define D_SENSOR_LD2410_RX "LD2410 Rx"
#define D_SENSOR_LD2410S_TX "LD2410S Tx"
diff --git a/tasmota/language/fy_NL.h b/tasmota/language/fy_NL.h
index 0c362f0ba..e381ce01f 100644
--- a/tasmota/language/fy_NL.h
+++ b/tasmota/language/fy_NL.h
@@ -58,6 +58,7 @@
#define D_AS "als"
#define D_AT "at"
#define D_AUTO "AUTO"
+#define D_AUTO_UPDATE "Auto Update"
#define D_BATTERY "Battery"
#define D_BATT "Batt" // Short for Battery
#define D_BATTERY_CHARGE "Lading" // Battery charge in %
@@ -88,6 +89,7 @@
#define D_DEBUG "Debugearje"
#define D_DEWPOINT "Dauwpunt"
#define D_DISABLED "Útsetten"
+#define D_DISCONNECTED "Disconnected"
#define D_DISTANCE "Ôfstân"
#define D_DNS_SERVER "DNS Server"
#define D_DO "Oploste soerstof"
@@ -776,6 +778,8 @@
#define D_SENSOR_SDM630_RX "SDM630 Rx"
#define D_SENSOR_WE517_TX "WE517 Tx"
#define D_SENSOR_WE517_RX "WE517 Rx"
+#define D_SENSOR_LD2402_TX "LD2402 Tx"
+#define D_SENSOR_LD2402_RX "LD2402 Rx"
#define D_SENSOR_LD2410_TX "LD2410 Tx"
#define D_SENSOR_LD2410_RX "LD2410 Rx"
#define D_SENSOR_LD2410S_TX "LD2410S Tx"
diff --git a/tasmota/language/he_HE.h b/tasmota/language/he_HE.h
index ab67db617..7fee7c024 100644
--- a/tasmota/language/he_HE.h
+++ b/tasmota/language/he_HE.h
@@ -58,6 +58,7 @@
#define D_AS "-כ"
#define D_AT "at"
#define D_AUTO "אוטומטי"
+#define D_AUTO_UPDATE "Auto Update"
#define D_BATTERY "Battery"
#define D_BATT "Batt" // Short for Battery
#define D_BATTERY_CHARGE "Charge" // Battery charge in %
@@ -88,6 +89,7 @@
#define D_DEBUG "באגים"
#define D_DEWPOINT "Dew point"
#define D_DISABLED "מבוטל"
+#define D_DISCONNECTED "Disconnected"
#define D_DISTANCE "מרחק"
#define D_DNS_SERVER "DNS שרת"
#define D_DO "Disolved Oxygen"
@@ -776,6 +778,8 @@
#define D_SENSOR_SDM630_RX "SDM630 Rx"
#define D_SENSOR_WE517_TX "WE517 Tx"
#define D_SENSOR_WE517_RX "WE517 Rx"
+#define D_SENSOR_LD2402_TX "LD2402 Tx"
+#define D_SENSOR_LD2402_RX "LD2402 Rx"
#define D_SENSOR_LD2410_TX "LD2410 Tx"
#define D_SENSOR_LD2410_RX "LD2410 Rx"
#define D_SENSOR_LD2410S_TX "LD2410S Tx"
diff --git a/tasmota/language/hu_HU.h b/tasmota/language/hu_HU.h
index 87cb26693..6a27e71f6 100644
--- a/tasmota/language/hu_HU.h
+++ b/tasmota/language/hu_HU.h
@@ -58,6 +58,7 @@
#define D_AS "mint"
#define D_AT "at"
#define D_AUTO "AUTO"
+#define D_AUTO_UPDATE "Auto Update"
#define D_BATTERY "Battery"
#define D_BATT "Batt" // Short for Battery
#define D_BATTERY_CHARGE "Charge" // Battery charge in %
@@ -88,6 +89,7 @@
#define D_DEBUG "Debug"
#define D_DEWPOINT "Harmatpont"
#define D_DISABLED "Letiltva"
+#define D_DISCONNECTED "Disconnected"
#define D_DISTANCE "Távolság"
#define D_DNS_SERVER "DNS szerver"
#define D_DO "Oldott oxygén"
@@ -776,6 +778,8 @@
#define D_SENSOR_SDM630_RX "SDM630 Rx"
#define D_SENSOR_WE517_TX "WE517 Tx"
#define D_SENSOR_WE517_RX "WE517 Rx"
+#define D_SENSOR_LD2402_TX "LD2402 Tx"
+#define D_SENSOR_LD2402_RX "LD2402 Rx"
#define D_SENSOR_LD2410_TX "LD2410 Tx"
#define D_SENSOR_LD2410_RX "LD2410 Rx"
#define D_SENSOR_LD2410S_TX "LD2410S Tx"
diff --git a/tasmota/language/it_IT.h b/tasmota/language/it_IT.h
index 3a9db30cc..0d2fc54c9 100644
--- a/tasmota/language/it_IT.h
+++ b/tasmota/language/it_IT.h
@@ -58,6 +58,7 @@
#define D_AS "come"
#define D_AT "in"
#define D_AUTO "AUTO"
+#define D_AUTO_UPDATE "Auto Update"
#define D_BATTERY "Batteria"
#define D_BATT "Batt" // Short for Battery
#define D_BATTERY_CHARGE "Carica" // Battery charge in %
@@ -88,6 +89,7 @@
#define D_DEBUG "Debug"
#define D_DEWPOINT "Punto rugiada" //
#define D_DISABLED "Disabilitato/a"
+#define D_DISCONNECTED "Disconnected"
#define D_DISTANCE "Distanza"
#define D_DNS_SERVER "Server DNS"
#define D_DO "Ossigeno dissolto"
@@ -776,6 +778,8 @@
#define D_SENSOR_SDM630_RX "SDM630 - RX"
#define D_SENSOR_WE517_TX "WE517 - TX"
#define D_SENSOR_WE517_RX "WE517 - RX"
+#define D_SENSOR_LD2402_TX "LD2402 - Tx"
+#define D_SENSOR_LD2402_RX "LD2402 - Rx"
#define D_SENSOR_LD2410_TX "LD2410 - TX"
#define D_SENSOR_LD2410_RX "LD2410 - RX"
#define D_SENSOR_LD2410S_TX "LD2410S - TX"
diff --git a/tasmota/language/ko_KO.h b/tasmota/language/ko_KO.h
index fda914fd7..27204a1d5 100644
--- a/tasmota/language/ko_KO.h
+++ b/tasmota/language/ko_KO.h
@@ -58,6 +58,7 @@
#define D_AS "as"
#define D_AT "at"
#define D_AUTO "자동"
+#define D_AUTO_UPDATE "Auto Update"
#define D_BATTERY "Battery"
#define D_BATT "Batt" // Short for Battery
#define D_BATTERY_CHARGE "Charge" // Battery charge in %
@@ -88,6 +89,7 @@
#define D_DEBUG "디버그"
#define D_DEWPOINT "Dew point"
#define D_DISABLED "사용안함"
+#define D_DISCONNECTED "Disconnected"
#define D_DISTANCE "거리"
#define D_DNS_SERVER "DNS 서버"
#define D_DO "Disolved Oxygen"
@@ -776,6 +778,8 @@
#define D_SENSOR_SDM630_RX "SDM630 Rx"
#define D_SENSOR_WE517_TX "WE517 Tx"
#define D_SENSOR_WE517_RX "WE517 Rx"
+#define D_SENSOR_LD2402_TX "LD2402 Tx"
+#define D_SENSOR_LD2402_RX "LD2402 Rx"
#define D_SENSOR_LD2410_TX "LD2410 Tx"
#define D_SENSOR_LD2410_RX "LD2410 Rx"
#define D_SENSOR_LD2410S_TX "LD2410S Tx"
diff --git a/tasmota/language/lt_LT.h b/tasmota/language/lt_LT.h
index d03792699..ffb2488ff 100644
--- a/tasmota/language/lt_LT.h
+++ b/tasmota/language/lt_LT.h
@@ -58,6 +58,7 @@
#define D_AS "kaip"
#define D_AT "prie"
#define D_AUTO "AUTOMATINĖ"
+#define D_AUTO_UPDATE "Auto Update"
#define D_BATTERY "Baterija"
#define D_BATT "Baterija" // Short for Battery
#define D_BATTERY_CHARGE "Įkrova" // Battery charge in %
@@ -88,6 +89,7 @@
#define D_DEBUG "Derinimas"
#define D_DEWPOINT "Rasos taškas"
#define D_DISABLED "Išjungta"
+#define D_DISCONNECTED "Disconnected"
#define D_DISTANCE "Atstumas"
#define D_DNS_SERVER "DNS Serveris"
#define D_DO "Ištirpęs deguonis"
@@ -776,6 +778,8 @@
#define D_SENSOR_SDM630_RX "SDM630 Rx"
#define D_SENSOR_WE517_TX "WE517 Tx"
#define D_SENSOR_WE517_RX "WE517 Rx"
+#define D_SENSOR_LD2402_TX "LD2402 Tx"
+#define D_SENSOR_LD2402_RX "LD2402 Rx"
#define D_SENSOR_LD2410_TX "LD2410 Tx"
#define D_SENSOR_LD2410_RX "LD2410 Rx"
#define D_SENSOR_LD2410S_TX "LD2410S Tx"
diff --git a/tasmota/language/nl_NL.h b/tasmota/language/nl_NL.h
index cffeb5fc9..6e3fcb7f9 100644
--- a/tasmota/language/nl_NL.h
+++ b/tasmota/language/nl_NL.h
@@ -58,6 +58,7 @@
#define D_AS "als"
#define D_AT "at"
#define D_AUTO "AUTO"
+#define D_AUTO_UPDATE "Auto Update"
#define D_BATTERY "Battery"
#define D_BATT "Batt" // Short for Battery
#define D_BATTERY_CHARGE "Lading" // Battery charge in %
@@ -88,6 +89,7 @@
#define D_DEBUG "Debug"
#define D_DEWPOINT "Dauwpunt"
#define D_DISABLED "Uitgeschakeld"
+#define D_DISCONNECTED "Disconnected"
#define D_DISTANCE "Afstand"
#define D_DNS_SERVER "DNS Server"
#define D_DO "Opgelost zuurstof"
@@ -776,6 +778,8 @@
#define D_SENSOR_SDM630_RX "SDM630 Rx"
#define D_SENSOR_WE517_TX "WE517 Tx"
#define D_SENSOR_WE517_RX "WE517 Rx"
+#define D_SENSOR_LD2402_TX "LD2402 Tx"
+#define D_SENSOR_LD2402_RX "LD2402 Rx"
#define D_SENSOR_LD2410_TX "LD2410 Tx"
#define D_SENSOR_LD2410_RX "LD2410 Rx"
#define D_SENSOR_LD2410S_TX "LD2410S Tx"
diff --git a/tasmota/language/pl_PL.h b/tasmota/language/pl_PL.h
index 801924e5b..0bba643ab 100644
--- a/tasmota/language/pl_PL.h
+++ b/tasmota/language/pl_PL.h
@@ -58,6 +58,7 @@
#define D_AS "jak"
#define D_AT "at"
#define D_AUTO "AUTO"
+#define D_AUTO_UPDATE "Auto Update"
#define D_BATTERY "Bateria"
#define D_BATT "Bat" // Short for Battery
#define D_BATTERY_CHARGE "Ładowanie" // Battery charge in %
@@ -88,6 +89,7 @@
#define D_DEBUG "Debug"
#define D_DEWPOINT "Punkt rosy"
#define D_DISABLED "Wyłączony"
+#define D_DISCONNECTED "Disconnected"
#define D_DISTANCE "Odległość"
#define D_DNS_SERVER "Serwer DNS"
#define D_DO "Rozpuszczalność tlenu"
@@ -776,6 +778,8 @@
#define D_SENSOR_SDM630_RX "SDM630 Rx"
#define D_SENSOR_WE517_TX "WE517 Tx"
#define D_SENSOR_WE517_RX "WE517 Rx"
+#define D_SENSOR_LD2402_TX "LD2402 Tx"
+#define D_SENSOR_LD2402_RX "LD2402 Rx"
#define D_SENSOR_LD2410_TX "LD2410 Tx"
#define D_SENSOR_LD2410_RX "LD2410 Rx"
#define D_SENSOR_LD2410S_TX "LD2410S Tx"
diff --git a/tasmota/language/pt_BR.h b/tasmota/language/pt_BR.h
index cf26085df..68002b940 100644
--- a/tasmota/language/pt_BR.h
+++ b/tasmota/language/pt_BR.h
@@ -58,6 +58,7 @@
#define D_AS "como"
#define D_AT "at"
#define D_AUTO "Auto"
+#define D_AUTO_UPDATE "Auto Update"
#define D_BATTERY "Battery"
#define D_BATT "Bat" // Short for Battery
#define D_BATTERY_CHARGE "Charge" // Battery charge in %
@@ -88,6 +89,7 @@
#define D_DEBUG "Depurar"
#define D_DEWPOINT "Ponto de orvalho"
#define D_DISABLED "Desabilitado"
+#define D_DISCONNECTED "Disconnected"
#define D_DISTANCE "Distância"
#define D_DNS_SERVER "Servidor DNS"
#define D_DO "Oxigênio dissolvido"
@@ -776,6 +778,8 @@
#define D_SENSOR_SDM630_RX "SDM630 Rx"
#define D_SENSOR_WE517_TX "WE517 Tx"
#define D_SENSOR_WE517_RX "WE517 Rx"
+#define D_SENSOR_LD2402_TX "LD2402 Tx"
+#define D_SENSOR_LD2402_RX "LD2402 Rx"
#define D_SENSOR_LD2410_TX "LD2410 Tx"
#define D_SENSOR_LD2410_RX "LD2410 Rx"
#define D_SENSOR_LD2410S_TX "LD2410S Tx"
diff --git a/tasmota/language/pt_PT.h b/tasmota/language/pt_PT.h
index 7b7d90d5a..9b0800b13 100644
--- a/tasmota/language/pt_PT.h
+++ b/tasmota/language/pt_PT.h
@@ -58,6 +58,7 @@
#define D_AS "como"
#define D_AT "at"
#define D_AUTO "AUTO"
+#define D_AUTO_UPDATE "Auto Update"
#define D_BATTERY "Battery"
#define D_BATT "Batt" // Short for Battery
#define D_BATTERY_CHARGE "Charge" // Battery charge in %
@@ -88,6 +89,7 @@
#define D_DEBUG "Depurar"
#define D_DEWPOINT "Ponto de Condensação"
#define D_DISABLED "Disabilitado"
+#define D_DISCONNECTED "Disconnected"
#define D_DISTANCE "Distância"
#define D_DNS_SERVER "Servidor DNS"
#define D_DO "Oxigénio Dissolvido"
@@ -776,6 +778,8 @@
#define D_SENSOR_SDM630_RX "SDM630 Rx"
#define D_SENSOR_WE517_TX "WE517 Tx"
#define D_SENSOR_WE517_RX "WE517 Rx"
+#define D_SENSOR_LD2402_TX "LD2402 Tx"
+#define D_SENSOR_LD2402_RX "LD2402 Rx"
#define D_SENSOR_LD2410_TX "LD2410 Tx"
#define D_SENSOR_LD2410_RX "LD2410 Rx"
#define D_SENSOR_LD2410S_TX "LD2410S Tx"
diff --git a/tasmota/language/ro_RO.h b/tasmota/language/ro_RO.h
index 9898d3128..fcd19e50e 100644
--- a/tasmota/language/ro_RO.h
+++ b/tasmota/language/ro_RO.h
@@ -58,6 +58,7 @@
#define D_AS "as"
#define D_AT "at"
#define D_AUTO "AUTO"
+#define D_AUTO_UPDATE "Auto Update"
#define D_BATTERY "Battery"
#define D_BATT "Batt" // Short for Battery
#define D_BATTERY_CHARGE "Charge" // Battery charge in %
@@ -88,6 +89,7 @@
#define D_DEBUG "Depanare"
#define D_DEWPOINT "Punct de rouă"
#define D_DISABLED "Dezactivat"
+#define D_DISCONNECTED "Disconnected"
#define D_DISTANCE "Distanță"
#define D_DNS_SERVER "Server DNS"
#define D_DO "Disolved Oxygen"
@@ -776,6 +778,8 @@
#define D_SENSOR_SDM630_RX "SDM630 Rx"
#define D_SENSOR_WE517_TX "WE517 Tx"
#define D_SENSOR_WE517_RX "WE517 Rx"
+#define D_SENSOR_LD2402_TX "LD2402 Tx"
+#define D_SENSOR_LD2402_RX "LD2402 Rx"
#define D_SENSOR_LD2410_TX "LD2410 Tx"
#define D_SENSOR_LD2410_RX "LD2410 Rx"
#define D_SENSOR_LD2410S_TX "LD2410S Tx"
diff --git a/tasmota/language/ru_RU.h b/tasmota/language/ru_RU.h
index fc935ace2..df1fa52c8 100644
--- a/tasmota/language/ru_RU.h
+++ b/tasmota/language/ru_RU.h
@@ -59,6 +59,7 @@
#define D_AS "как"
#define D_AT "at"
#define D_AUTO "АВТО"
+#define D_AUTO_UPDATE "Auto Update"
#define D_BATTERY "Battery"
#define D_BATT "Batt" // Short for Battery
#define D_BATTERY_CHARGE "Charge" // Battery charge in %
@@ -89,6 +90,7 @@
#define D_DEBUG "Отладка"
#define D_DEWPOINT "Dew point"
#define D_DISABLED "Блокирован"
+#define D_DISCONNECTED "Disconnected"
#define D_DISTANCE "Расстояние"
#define D_DNS_SERVER "DNS Сервер"
#define D_DO "Disolved Oxygen"
@@ -777,6 +779,8 @@
#define D_SENSOR_SDM630_RX "SDM630 Rx"
#define D_SENSOR_WE517_TX "WE517 Tx"
#define D_SENSOR_WE517_RX "WE517 Rx"
+#define D_SENSOR_LD2402_TX "LD2402 Tx"
+#define D_SENSOR_LD2402_RX "LD2402 Rx"
#define D_SENSOR_LD2410_TX "LD2410 Tx"
#define D_SENSOR_LD2410_RX "LD2410 Rx"
#define D_SENSOR_LD2410S_TX "LD2410S Tx"
diff --git a/tasmota/language/sk_SK.h b/tasmota/language/sk_SK.h
index c6e37b675..3a62082de 100644
--- a/tasmota/language/sk_SK.h
+++ b/tasmota/language/sk_SK.h
@@ -58,6 +58,7 @@
#define D_AS "ako"
#define D_AT "at"
#define D_AUTO "AUTO"
+#define D_AUTO_UPDATE "Auto Update"
#define D_BATTERY "Battery"
#define D_BATT "Batt" // Short for Battery
#define D_BATTERY_CHARGE "Charge" // Battery charge in %
@@ -90,6 +91,7 @@
#define D_DEBUG "Debug"
#define D_DEWPOINT "Dew point"
#define D_DISABLED "Zablokované"
+#define D_DISCONNECTED "Disconnected"
#define D_DISTANCE "Vzdialenosť"
#define D_DNS_SERVER "Server DNS"
#define D_DO "Disolved Oxygen"
@@ -776,6 +778,8 @@
#define D_SENSOR_SDM630_RX "SDM630 Rx"
#define D_SENSOR_WE517_TX "WE517 Tx"
#define D_SENSOR_WE517_RX "WE517 Rx"
+#define D_SENSOR_LD2402_TX "LD2402 Tx"
+#define D_SENSOR_LD2402_RX "LD2402 Rx"
#define D_SENSOR_LD2410_TX "LD2410 Tx"
#define D_SENSOR_LD2410_RX "LD2410 Rx"
#define D_SENSOR_LD2410S_TX "LD2410S Tx"
diff --git a/tasmota/language/sv_SE.h b/tasmota/language/sv_SE.h
index 1314ca049..f21ed90d1 100644
--- a/tasmota/language/sv_SE.h
+++ b/tasmota/language/sv_SE.h
@@ -58,6 +58,7 @@
#define D_AS "som"
#define D_AT "at"
#define D_AUTO "AUTO"
+#define D_AUTO_UPDATE "Auto Update"
#define D_BATTERY "Battery"
#define D_BATT "Batt" // Short for Battery
#define D_BATTERY_CHARGE "Charge" // Battery charge in %
@@ -88,6 +89,7 @@
#define D_DEBUG "Debug"
#define D_DEWPOINT "Dew point"
#define D_DISABLED "Inaktiverad"
+#define D_DISCONNECTED "Disconnected"
#define D_DISTANCE "Distans"
#define D_DNS_SERVER "DNS-server"
#define D_DO "Disolved Oxygen"
@@ -776,6 +778,8 @@
#define D_SENSOR_SDM630_RX "SDM630 Rx"
#define D_SENSOR_WE517_TX "WE517 Tx"
#define D_SENSOR_WE517_RX "WE517 Rx"
+#define D_SENSOR_LD2402_TX "LD2402 Tx"
+#define D_SENSOR_LD2402_RX "LD2402 Rx"
#define D_SENSOR_LD2410_TX "LD2410 Tx"
#define D_SENSOR_LD2410_RX "LD2410 Rx"
#define D_SENSOR_LD2410S_TX "LD2410S Tx"
diff --git a/tasmota/language/tr_TR.h b/tasmota/language/tr_TR.h
index 228e6da08..6795e5e84 100644
--- a/tasmota/language/tr_TR.h
+++ b/tasmota/language/tr_TR.h
@@ -58,6 +58,7 @@
#define D_AS "as"
#define D_AT "at"
#define D_AUTO "OTOMATIK"
+#define D_AUTO_UPDATE "Auto Update"
#define D_BATTERY "Battery"
#define D_BATT "Batt" // Short for Battery
#define D_BATTERY_CHARGE "Charge" // Battery charge in %
@@ -88,6 +89,7 @@
#define D_DEBUG "Hata Ayıklama"
#define D_DEWPOINT "Dew point"
#define D_DISABLED "Etkin Değil"
+#define D_DISCONNECTED "Disconnected"
#define D_DISTANCE "Mesage"
#define D_DNS_SERVER "DNS Sunucu"
#define D_DO "Disolved Oxygen"
@@ -776,6 +778,8 @@
#define D_SENSOR_SDM630_RX "SDM630 Rx"
#define D_SENSOR_WE517_TX "WE517 Tx"
#define D_SENSOR_WE517_RX "WE517 Rx"
+#define D_SENSOR_LD2402_TX "LD2402 Tx"
+#define D_SENSOR_LD2402_RX "LD2402 Rx"
#define D_SENSOR_LD2410_TX "LD2410 Tx"
#define D_SENSOR_LD2410_RX "LD2410 Rx"
#define D_SENSOR_LD2410S_TX "LD2410S Tx"
diff --git a/tasmota/language/uk_UA.h b/tasmota/language/uk_UA.h
index c9e9a267f..f1c49089d 100644
--- a/tasmota/language/uk_UA.h
+++ b/tasmota/language/uk_UA.h
@@ -58,6 +58,7 @@
#define D_AS "як"
#define D_AT "at"
#define D_AUTO "АВТО"
+#define D_AUTO_UPDATE "Auto Update"
#define D_BATTERY "Battery"
#define D_BATT "Batt" // Short for Battery
#define D_BATTERY_CHARGE "Charge" // Battery charge in %
@@ -88,6 +89,7 @@
#define D_DEBUG "Налагодження"
#define D_DEWPOINT "Tочка роси"
#define D_DISABLED "Вимкнено"
+#define D_DISCONNECTED "Disconnected"
#define D_DISTANCE "Відстань"
#define D_DNS_SERVER "Сервер DNS"
#define D_DO "Disolved Oxygen"
@@ -776,6 +778,8 @@
#define D_SENSOR_SDM630_RX "SDM630 Rx"
#define D_SENSOR_WE517_TX "WE517 Tx"
#define D_SENSOR_WE517_RX "WE517 Rx"
+#define D_SENSOR_LD2402_TX "LD2402 Tx"
+#define D_SENSOR_LD2402_RX "LD2402 Rx"
#define D_SENSOR_LD2410_TX "LD2410 Tx"
#define D_SENSOR_LD2410_RX "LD2410 Rx"
#define D_SENSOR_LD2410S_TX "LD2410S Tx"
diff --git a/tasmota/language/vi_VN.h b/tasmota/language/vi_VN.h
index 9079f40e3..968cb05b4 100644
--- a/tasmota/language/vi_VN.h
+++ b/tasmota/language/vi_VN.h
@@ -58,6 +58,7 @@
#define D_AS "với tên gọi"
#define D_AT "at"
#define D_AUTO "AUTO"
+#define D_AUTO_UPDATE "Auto Update"
#define D_BATTERY "Battery"
#define D_BATT "Batt" // Short for Battery
#define D_BATTERY_CHARGE "Charge" // Battery charge in %
@@ -88,6 +89,7 @@
#define D_DEBUG "Tìm lỗi"
#define D_DEWPOINT "Điểm sương"
#define D_DISABLED "Vô hiệu hóa"
+#define D_DISCONNECTED "Disconnected"
#define D_DISTANCE "Khoảng cách"
#define D_DNS_SERVER "Máy chủ DNS"
#define D_DO "Disolved Oxygen"
@@ -776,6 +778,8 @@
#define D_SENSOR_SDM630_RX "SDM630 Rx"
#define D_SENSOR_WE517_TX "WE517 Tx"
#define D_SENSOR_WE517_RX "WE517 Rx"
+#define D_SENSOR_LD2402_TX "LD2402 Tx"
+#define D_SENSOR_LD2402_RX "LD2402 Rx"
#define D_SENSOR_LD2410_TX "LD2410 Tx"
#define D_SENSOR_LD2410_RX "LD2410 Rx"
#define D_SENSOR_LD2410S_TX "LD2410S Tx"
diff --git a/tasmota/language/zh_CN.h b/tasmota/language/zh_CN.h
index 520e90d0d..20523c5d5 100644
--- a/tasmota/language/zh_CN.h
+++ b/tasmota/language/zh_CN.h
@@ -58,6 +58,7 @@
#define D_AS "名称:"
#define D_AT "at"
#define D_AUTO "自动"
+#define D_AUTO_UPDATE "Auto Update"
#define D_BATTERY "Battery"
#define D_BATT "Batt" // Short for Battery
#define D_BATTERY_CHARGE "Charge" // Battery charge in %
@@ -88,6 +89,7 @@
#define D_DEBUG "调试"
#define D_DEWPOINT "Dew point"
#define D_DISABLED "禁用"
+#define D_DISCONNECTED "Disconnected"
#define D_DISTANCE "距离"
#define D_DNS_SERVER "DNS服务器"
#define D_DO "Disolved Oxygen"
@@ -776,6 +778,8 @@
#define D_SENSOR_SDM630_RX "SDM630 Rx"
#define D_SENSOR_WE517_TX "WE517 Tx"
#define D_SENSOR_WE517_RX "WE517 Rx"
+#define D_SENSOR_LD2402_TX "LD2402 Tx"
+#define D_SENSOR_LD2402_RX "LD2402 Rx"
#define D_SENSOR_LD2410_TX "LD2410 Tx"
#define D_SENSOR_LD2410_RX "LD2410 Rx"
#define D_SENSOR_LD2410S_TX "LD2410S Tx"
diff --git a/tasmota/language/zh_TW.h b/tasmota/language/zh_TW.h
index a3f615d2f..0d96ca41f 100644
--- a/tasmota/language/zh_TW.h
+++ b/tasmota/language/zh_TW.h
@@ -58,6 +58,7 @@
#define D_AS "名稱:"
#define D_AT "at"
#define D_AUTO "自動"
+#define D_AUTO_UPDATE "Auto Update"
#define D_BATTERY "Battery"
#define D_BATT "電池" // Short for Battery
#define D_BATTERY_CHARGE "Charge" // Battery charge in %
@@ -88,6 +89,7 @@
#define D_DEBUG "偵錯"
#define D_DEWPOINT "Dew point"
#define D_DISABLED "已停用"
+#define D_DISCONNECTED "Disconnected"
#define D_DISTANCE "距離"
#define D_DNS_SERVER "DNS伺服器"
#define D_DO "Disolved Oxygen"
@@ -776,6 +778,8 @@
#define D_SENSOR_SDM630_RX "SDM630 Rx"
#define D_SENSOR_WE517_TX "WE517 Tx"
#define D_SENSOR_WE517_RX "WE517 Rx"
+#define D_SENSOR_LD2402_TX "LD2402 Tx"
+#define D_SENSOR_LD2402_RX "LD2402 Rx"
#define D_SENSOR_LD2410_TX "LD2410 Tx"
#define D_SENSOR_LD2410_RX "LD2410 Rx"
#define D_SENSOR_LD2410S_TX "LD2410S Tx"
diff --git a/tasmota/tasmota_xsns_sensor/xsns_102_ld2402.ino b/tasmota/tasmota_xsns_sensor/xsns_102_ld2402.ino
new file mode 100644
index 000000000..0a91aa428
--- /dev/null
+++ b/tasmota/tasmota_xsns_sensor/xsns_102_ld2402.ino
@@ -0,0 +1,939 @@
+/*
+ xsns_102_ld2402.ino - HLK-LD2402 24GHz smart wave motion sensor support for Tasmota
+
+ Copyright (C) 2025 Anthony Sepa
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see .
+
+ Based in part on xsns_102_ld2410s.ino by:
+ SPDX-FileCopyrightText: 2022 Theo Arends, 2024 md5sum-as (https://github.com/md5sum-as)
+
+ SPDX-License-Identifier: GPL-3.0-only
+*/
+
+#ifdef USE_LD2402
+
+/*********************************************************************************************\
+ * HLK-LD2402 24GHz smart wave motion sensor
+ *
+ * Attention!
+ * This module works with HLK-LD2402 devices.
+ *
+ * Available commands:
+ * LD2402_Status 0|1|2 - showing parameters|sensor readings|firmware info
+ * LD2402_ReRead - reread common, motion and micro threshold parameters from device
+ * LD2402_SetCommon 7-100,0-65535 - set common: maximum distance, disappearance delay
+ * LD2402_SetMotion n,n1..n16 - set motion threshold values (16)
+ * LD2402_SetMicro n,n1..n16 - set micro threshold values (16)
+ * LD2402_Mode 0/1 - set device output mode 0-normal, 1-engineering
+ * LD2402_AutoUpdate 3.0,2.0,3.0 - start autoupdate trigger,keep,micro magnification factor
+ * LD2402_Follow 0/n - reports every n seconds
+ *
+\*********************************************************************************************/
+
+#define XSNS_102 102
+
+// Max line length is in engineering mode at 141 characters
+#define LD2402_BUFFER_SIZE 141
+
+#define LD2402_NUM_GATES 0x10
+
+// control commands
+#define LD2402_CMND_NOCMND 0x02
+
+// use command values << 4 (to allow delay space) as steps
+#define LD2402_CMND_READ_VERSION 0x00F
+#define LD2402_CMND_READ_SERIAL 0x11F
+#define LD2402_CMND_WRITE_PARAM 0x07F
+#define LD2402_CMND_READ_PARAM 0x08F
+#define LD2402_CMND_AUTO_THRESHOLD 0x09F
+#define LD2402_CMND_AUTO_PROGRESS 0x0AF
+#define LD2402_CMND_MODE 0x12F
+#define LD2402_CMND_AUTO_INTERFERENCE 0x14F
+#define LD2402_CMND_SAVE_PARAM 0xFDF
+#define LD2402_CMND_END_CONFIGURATION 0xFEF
+#define LD2402_CMND_START_CONFIGURATION 0xFFF
+
+#define CMD_LD2402_TIMEOUT_DELAY 8
+#define CMD_LD2402_BOOT_DELAY 3
+#define CMD_LD2402_Done 0x01
+#define CMD_LD2402_Wait_Timeout (CMD_LD2402_Done+CMD_LD2402_TIMEOUT_DELAY+1)
+
+#define CMD_LD2402_END_DELAY 2
+#define CMD_LD2402_PROGRESS_DELAY 7
+
+#define LD2402_CMND_PARAM_MAX_DISTANCE 0x01
+#define LD2402_CMND_PARAM_DISP_DELAY 0x04
+#define LD2402_CMND_PARAM_PWR_INTERFER 0x05
+#define LD2402_CMND_PARAM_MOTION_START 0x10
+#define LD2402_CMND_PARAM_MOTION_END LD2402_CMND_PARAM_MOTION_START + LD2402_NUM_GATES - 1
+#define LD2402_CMND_PARAM_MICRO_START 0x30
+#define LD2402_CMND_PARAM_MICRO_END LD2402_CMND_PARAM_MICRO_START + LD2402_NUM_GATES - 1
+
+#define LD2402_target_Error 0x4572726f
+#define LD2402_target_OFF 0x4F46460D
+#define LD2402_target_distance 0x64697374
+#define LD2402_config_header 0xFDFCFBFA
+#define LD2402_config_header_a {0xFD, 0xFC, 0xFB, 0xFA}
+#define LD2402_engineering_header 0xF4F3F2F1
+#define LD2402_config_footer 0x04030201
+const uint8_t LD2402_config_footer_a[4] {0x04, 0x03, 0x02, 0x01};
+#define LD2402_engineering_footer 0xF8F7F6F5
+
+#include
+TasmotaSerial *LD2402Serial = nullptr;
+
+union uint32_union {
+ uint8_t buffer[4];
+ uint32_t buffer_32;
+};
+
+struct {
+ uint8_t *buffer;
+ uint8_t cmnd_param[8] = {};
+ uint32_t initializing;
+ uint32_t busy;
+ uint32_t step;
+ uint32_t next_step;
+ uint32_t sent_ack = LD2402_CMND_NOCMND;
+// Static params
+ char *version = NULL;
+ char *serial_number = NULL;
+// Detection Parameters
+ uint32_t max_distance;
+ uint32_t disp_delay;
+ uint8_t motion_threshold[LD2402_NUM_GATES * 4];
+ uint8_t micro_threshold[LD2402_NUM_GATES * 4];
+// Results
+ uint32_t pwr_interf;
+ char gates[20] = "FFFF FFFF FFFF FFFF";
+// Report values
+ uint32_t person;
+ uint32_t detect_distance;
+ uint8_t motion_energy[LD2402_NUM_GATES * 4];
+ uint8_t micro_energy[LD2402_NUM_GATES * 4];
+// program flow
+ uint32_t follow;
+ uint32_t timer;
+ uint32_t report_type;
+// autoupdate
+ uint32_t auto_upd_progress;
+// endianess
+ uint32_t is_big;
+} LD2402;
+
+#define D_LD2402 "LD2402"
+#define D_LD2402_LOG_PREFIX "LD2: "
+#define D_BUSY_MSG "{\"" D_LD2402 "\":\"Busy, wait till finished current command.\"}"
+#define D_COMMAND_PREFIX_JSON "{\"" D_LD2402 "_%s\":"
+#define D_COMMAND_HELP_MSG D_COMMAND_PREFIX_JSON "\"%s\"}"
+/********************************************************************************************/
+
+void Ld2402HandleEngineeringData(void) {
+ /*
+ 01 - 0 type
+ 9100 - 1,2 distance
+ F6110000 6C0A0000 3D020000 A3020000 20030000 50060000 57030000 48010000 F3010000 3B010000 07010000 00010000 D2000000 23010000 F3000000 F4000000 - 3+ motion gates
+ B1270300 F30B0100 703E0000 8E120000 C5080000 3F100000 25030000 7A060000 7F080000 7E070000 FB050000 64040000 F3040000 2D040000 F9030000 43040000 - 67micro gates
+ */
+ LD2402.report_type = 1;
+ LD2402.detect_distance = LD2402.buffer[2] << 8 | LD2402.buffer[1];
+ LD2402.person = LD2402.buffer[0];
+ memcpy(LD2402.motion_energy, LD2402.buffer + 3, LD2402_NUM_GATES * 4);
+ memcpy(LD2402.micro_energy, LD2402.buffer + 67, LD2402_NUM_GATES * 4);
+}
+
+void Ld2402ToBinary(char *buffer, uint32_t byte_h, uint32_t byte_l) {
+ uint32_t mask_01 = 0x01, mask_10 = 0x10;
+ for (uint32_t i = 4; i; i--) {
+ buffer[i-1] = (byte_h & mask_10) ? '1' : '0';
+ buffer[i+4] = (byte_h & mask_01) ? '1' : '0';
+ buffer[i+9] = (byte_l & mask_10) ? '1' : '0';
+ buffer[i+14] = (byte_h & mask_01) ? '1' : '0';
+ mask_10<<=1;
+ mask_01<<=1;
+ }
+}
+
+void Ld2402HandleConfigData(void) {
+ uint32_t rcvd_ack = LD2402.buffer[0];
+ if (rcvd_ack != LD2402.sent_ack) {
+ return;
+ }
+ if (1 != LD2402.buffer[1]) {
+ // maybe do a retry?
+ return;
+ }
+
+ if (LD2402_CMND_READ_VERSION>>4 == rcvd_ack) {
+ // 00010000060076332E332E32
+ // 0 2 4 6
+ uint32_t len = LD2402.buffer[4];
+ free(LD2402.version);
+ LD2402.version = (char*)malloc(len+1);
+ memcpy(LD2402.version, LD2402.buffer + 6, len);
+ LD2402.version[len] = 0;
+ DEBUG_SENSOR_LOG(PSTR(D_LD2402_LOG_PREFIX "Rcvd %s"), LD2402.version);
+ } else if (LD2402_CMND_READ_SERIAL>>4 == rcvd_ack) {
+ // 1101000008004646464646464646
+ // 0 2 4 6
+ uint32_t len = LD2402.buffer[4];
+ free(LD2402.serial_number);
+ LD2402.serial_number = (char*)malloc(len+1);
+ memcpy(LD2402.serial_number, LD2402.buffer + 6, len);
+ LD2402.serial_number[len] = 0;
+ DEBUG_SENSOR_LOG(PSTR(D_LD2402_LOG_PREFIX "Rcvd %s"), LD2402.serial_number);
+ } else if (LD2402_CMND_READ_PARAM>>4 == rcvd_ack) {
+ //
+ switch (LD2402.cmnd_param[0]) {
+ // common params
+ case LD2402_CMND_PARAM_MAX_DISTANCE:
+ LD2402.max_distance = LD2402.buffer[4];
+ break;
+ case LD2402_CMND_PARAM_DISP_DELAY:
+ LD2402.disp_delay = LD2402.buffer[5] << 8 | LD2402.buffer[4];
+ break;
+ case LD2402_CMND_PARAM_PWR_INTERFER:
+ LD2402.pwr_interf = LD2402.buffer[4];
+ break;
+ default:
+ switch (LD2402.cmnd_param[0] & 0xF0) {
+ // gate specific params
+ case LD2402_CMND_PARAM_MOTION_START:
+ memcpy(LD2402.motion_threshold + (LD2402.cmnd_param[0] & 0x0F) * 4, LD2402.buffer + 4, 4);
+ break;
+ case LD2402_CMND_PARAM_MICRO_START:
+ memcpy(LD2402.micro_threshold + (LD2402.cmnd_param[0] & 0x0F) * 4, LD2402.buffer + 4, 4);
+ break;
+ default:
+ break;
+ }
+ break;
+ }
+ } else if ((LD2402_CMND_AUTO_PROGRESS>>4) == rcvd_ack) {
+ // 0A0100003C00
+ // 0 2 4
+ LD2402.auto_upd_progress = LD2402.buffer[4];
+ LD2402.report_type = 3;
+ } else if (LD2402_CMND_AUTO_INTERFERENCE>>4 == rcvd_ack) {
+ // 140100000000
+ // 0 2 4
+ Ld2402ToBinary(LD2402.gates, LD2402.buffer[5], LD2402.buffer[4]);
+ }
+ Ld2402OnDemand(rcvd_ack);
+}
+
+void Ld2402Input(void) {
+ uint32_t avail;
+ while (LD2402Serial->available()) {
+ static uint32_union header;
+ static uint32_union footer;
+ static uint32_t data_type = 0, byte_counter = 0;
+ if (!data_type) {
+ if (!byte_counter) {
+ avail = LD2402Serial->available();
+ if (avail < 4) {
+ DEBUG_SENSOR_LOG(PSTR(D_LD2402_LOG_PREFIX "Don't have enough for header."));
+ break;
+ }
+ for (uint32_t i = 3; i; i--) {
+ header.buffer_32<<=8;
+ header.buffer[LD2402.is_big*3] = LD2402Serial->read();
+ }
+ byte_counter = 4;
+ }
+
+ avail = LD2402Serial->available();
+ header.buffer_32<<=8;
+ header.buffer[LD2402.is_big*3] = LD2402Serial->read();
+
+ // I will get out of this mess with either:
+ // - Config/Engineering header match, set data_type (most likely/quickest further processing)
+ // - Known text match, process line, clear byte count for next line (less likely but no further processing)
+ // - No header matches, continue, shift in the next character if/when available
+ data_type = (LD2402_engineering_header == header.buffer_32) ? 2 : 0;
+ if (!data_type) {
+ data_type = (LD2402_config_header == header.buffer_32) ? 3 : 0;
+ if (!data_type) {
+ if (LD2402_target_Error != header.buffer_32) {
+ if (LD2402_target_OFF != header.buffer_32) {
+ if (LD2402_target_distance != header.buffer_32) {
+ continue;
+ } else {
+ // process distance line
+ byte_counter = LD2402Serial->readBytesUntil(0x0A, LD2402.buffer, LD2402_BUFFER_SIZE);
+ LD2402.buffer[byte_counter] = 0;
+ DEBUG_SENSOR_LOG(PSTR(D_LD2402_LOG_PREFIX "Dist%s"), LD2402.buffer);
+ // ance:105\r\n
+ LD2402.detect_distance = atoi((char*) LD2402.buffer + 5);
+ LD2402.person = 2;
+ }
+ } else {
+ // process OFF line
+ LD2402.detect_distance = 0;
+ LD2402.person = 0;
+ LD2402Serial->read();
+ }
+ LD2402.report_type = 2;
+ } else {
+ // preocess Error line
+ byte_counter = LD2402Serial->readBytesUntil(0x0A, LD2402.buffer, LD2402_BUFFER_SIZE);
+ LD2402.buffer[byte_counter] = 0;
+ AddLog(LOG_LEVEL_INFO, PSTR(D_LD2402_LOG_PREFIX "Erro%s"), LD2402.buffer);
+ LD2402.report_type = 0;
+ }
+ byte_counter = 0;
+ break;
+ }
+ }
+ }
+
+ static uint32_t length, got;
+ if (byte_counter < 6) {
+ avail = LD2402Serial->available();
+ if (avail < 2) {
+ DEBUG_SENSOR_LOG(PSTR(D_LD2402_LOG_PREFIX "Don't have enough for length."));
+ break;
+ }
+ length = LD2402Serial->read();
+ LD2402Serial->read();
+ got = 0;
+ if (length > LD2402_BUFFER_SIZE) {
+ data_type = 0;
+ byte_counter = 0;
+ break;
+ }
+ byte_counter = 6;
+ }
+
+ avail = LD2402Serial->available();
+ if (avail < (length - got)) {
+ got += LD2402Serial->readBytes(LD2402.buffer + got, avail);
+ break;
+ }
+ if (got < length) {
+ got += LD2402Serial->readBytes(LD2402.buffer + got, length - got);
+ }
+
+ avail = LD2402Serial->available();
+ if (avail < 4) {
+ DEBUG_SENSOR_LOG(PSTR(D_LD2402_LOG_PREFIX "Don't have enough for footer."));
+ break;
+ }
+ for (uint32_t i = 4; i; i--) {
+ footer.buffer_32<<=8;
+ footer.buffer[LD2402.is_big*3] = LD2402Serial->read();
+ }
+
+ if (2 == data_type) {
+ if (LD2402_engineering_footer == footer.buffer_32) {
+ Ld2402HandleEngineeringData();
+ } else {
+ DEBUG_SENSOR_LOG(PSTR(D_LD2402_LOG_PREFIX "Head %*_H"), 4, header.buffer);
+ DEBUG_SENSOR_LOG(PSTR(D_LD2402_LOG_PREFIX "Eng %*_H"), length, LD2402.buffer);
+ DEBUG_SENSOR_LOG(PSTR(D_LD2402_LOG_PREFIX "byte_counter: %d, avail: %d, got %d, length %d"), byte_counter, avail, got, length);
+ DEBUG_SENSOR_LOG(PSTR(D_LD2402_LOG_PREFIX "Foot %*_H"), 4, footer.buffer);
+ }
+ } else {
+ if (LD2402_config_footer == footer.buffer_32) {
+ Ld2402HandleConfigData();
+ LD2402Serial->setReadChunkMode(0);
+ } else {
+ DEBUG_SENSOR_LOG(PSTR(D_LD2402_LOG_PREFIX "Head %*_H"), 4, header.buffer);
+ DEBUG_SENSOR_LOG(PSTR(D_LD2402_LOG_PREFIX "Cmd %*_H"), length, LD2402.buffer);
+ DEBUG_SENSOR_LOG(PSTR(D_LD2402_LOG_PREFIX "byte_counter: %d, avail: %d, got %d, length %d"), byte_counter, avail, got, length);
+ DEBUG_SENSOR_LOG(PSTR(D_LD2402_LOG_PREFIX "Foot %*_H"), 4, footer.buffer);
+ }
+ }
+ data_type = 0;
+ byte_counter = 0;
+ break;
+ }
+ // If here then LD2402.byte_counter could still be partial correct for next loop
+}
+
+void Ld2402SendCommand(uint8_t command, uint32_t val_len = 0);
+void Ld2402SendCommand(uint8_t command, uint32_t val_len) {
+ uint8_t buffer[20] = LD2402_config_header_a;
+
+ buffer[4] = val_len + 2;
+ buffer[6] = command;
+ if (val_len) {
+ memcpy(buffer+8,LD2402.cmnd_param,val_len);
+ }
+ memcpy(buffer+8+val_len, LD2402_config_footer_a, sizeof(LD2402_config_footer_a));
+
+ LD2402.sent_ack = command;
+ LD2402Serial->setReadChunkMode(1); // Enable chunk mode introducing possible Hardware Watchdogs
+ LD2402Serial->flush();
+ LD2402Serial->write(buffer, val_len + 12);
+}
+
+void Ld2402LoadLastParam(uint32_t curr_param) {
+ memset(LD2402.cmnd_param, 0x00, 6);
+ LD2402.cmnd_param[0] = curr_param;
+ switch (curr_param) {
+ case LD2402_CMND_PARAM_MAX_DISTANCE:
+ LD2402.cmnd_param[2] = LD2402.max_distance;
+ break;
+
+ case LD2402_CMND_PARAM_DISP_DELAY:
+ LD2402.cmnd_param[2] = LD2402.disp_delay & 0x00FF;
+ LD2402.cmnd_param[3] = LD2402.disp_delay >> 8;
+ break;
+
+ default:
+ switch (curr_param & 0xF0) {
+ case LD2402_CMND_PARAM_MOTION_START:
+ memcpy(LD2402.cmnd_param + 2, LD2402.motion_threshold + (curr_param & 0x0F) * 4, 4);
+ break;
+
+ case LD2402_CMND_PARAM_MICRO_START:
+ memcpy(LD2402.cmnd_param + 2, LD2402.micro_threshold + (curr_param & 0x0F) * 4, 4);
+ break;
+
+ default:
+ break;
+ }
+ break;
+ }
+}
+
+void Ld2402WriteThresholds(uint8_t *thresholds, uint32_t cmnd_param) {
+ if (LD2402.busy) {
+ Response_P(PSTR(D_BUSY_MSG));
+ return;
+ }
+ if (ArgC() != 16) {
+ Response_P(PSTR("Use LS2410S_Set{Motion/Micro} 1,2,..16 (values are dB <= 95.00"));
+ return;
+ }
+ char strbuf[24];
+ float param;
+ uint32_t i = 0;
+ for (uint32_t j = 0; j < LD2402_NUM_GATES; j++) {
+ ArgV(strbuf, j+1);
+ param = CharToFloat(strbuf);
+ uint32_t val = exp10(param > 95.00f ? 95.00f : param);
+ thresholds[i++] = val & 0x000000FF;
+ thresholds[i++] = val >> 8 & 0x000000FF;
+ thresholds[i++] = val >> 16 & 0x000000FF;
+ thresholds[i++] = val >> 24;
+ }
+ Ld2402LoadLastParam(cmnd_param);
+ Ld2402ExecConfigCmnd(LD2402_CMND_WRITE_PARAM);
+ Response_P(PSTR("Accepted... Allow 2 seconds to write to device."));
+}
+
+void Ld2402ResponseAppendGates(uint8_t *energies) {
+ const float multiplier = 10.0f / 2.302585f;
+ uint32_t i = 0;
+ float val;
+ while (i < LD2402_NUM_GATES * 4) {
+ val = energies[i++] | energies[i++] << 8 | energies[i++] << 16 | energies[i++] << 24;
+ val = (val ? multiplier * logf(val) : 0);
+ if (4 != i) { ResponseAppend_P(PSTR(",")); }
+ ResponseAppend_P(PSTR("%2_f"), &val);
+ }
+}
+
+void Ld2402ResponseAppendReport() {
+ if (3 == LD2402.report_type) {
+ ResponseAppend_P(PSTR("\"AutoUpdate\":\"%1d%%\"}"), LD2402.auto_upd_progress);
+ } else if (0 == LD2402.report_type) {
+ ResponseAppend_P(PSTR("\"Error\":\"Disconnected\"}"));
+ } else {
+ ResponseAppend_P(PSTR("\"" D_JSON_DISTANCE "\":%d"), LD2402.detect_distance);
+ if (1 == LD2402.report_type) {
+ ResponseAppend_P(PSTR(",\"Motion\":%s"), (1 == LD2402.person ? "true" : "false"));
+ ResponseAppend_P(PSTR(",\"Occupancy\":%s"), (LD2402.person ? "true" : "false"));
+ ResponseAppend_P(PSTR(",\"PowerInterference\":%d"), LD2402.pwr_interf);
+ if (2 == LD2402.pwr_interf) { ResponseAppend_P(PSTR(",\"InterferenceGates\":\"%s\""), LD2402.gates); }
+ ResponseAppend_P(PSTR(",\"MotionEnergies\":["));
+ Ld2402ResponseAppendGates(LD2402.motion_energy);
+ ResponseAppend_P(PSTR("],\"MicroEnergies\":["));
+ Ld2402ResponseAppendGates(LD2402.micro_energy);
+ ResponseAppend_P(PSTR("]}"));
+ }
+ }
+}
+void Ld2402ExecQuickCmnd(uint32_t cmnd) {
+ LD2402.busy = true;
+ LD2402.step = cmnd;
+}
+
+void Ld2402ExecConfigCmnd(uint32_t cmnd) {
+ LD2402.next_step = cmnd;
+ Ld2402ExecQuickCmnd(LD2402_CMND_START_CONFIGURATION);
+}
+
+void Ld2402DeviceDisconnected(void) {
+ AddLog(LOG_LEVEL_INFO, PSTR(D_LD2402_LOG_PREFIX "Device disconnected"));
+ LD2402.step = CMD_LD2402_Done;
+ LD2402.report_type = 0;
+}
+
+/********************************************************************************************/
+
+void Ld2402OnDemand(uint32_t ack_rcvd) {
+ switch (ack_rcvd) {
+ case LD2402_CMND_START_CONFIGURATION>>4:
+ LD2402.step = LD2402.next_step;
+ break;
+
+ case LD2402_CMND_END_CONFIGURATION>>4:
+ LD2402.step = CMD_LD2402_Done;
+ return;
+
+ case LD2402_CMND_SAVE_PARAM>>4:
+ return;
+
+ case LD2402_CMND_AUTO_THRESHOLD>>4:
+ LD2402.step = LD2402_CMND_AUTO_PROGRESS;
+ return;
+
+ case LD2402_CMND_AUTO_PROGRESS>>4:
+ if (100 > LD2402.auto_upd_progress) {
+ LD2402.step = LD2402_CMND_AUTO_PROGRESS;
+ return;
+ }
+ LD2402.step = LD2402_CMND_READ_PARAM;
+ break;
+
+ case LD2402_CMND_AUTO_INTERFERENCE>>4:
+ LD2402.step = LD2402_CMND_READ_PARAM;
+ break;
+
+ case LD2402_CMND_MODE>>4:
+ LD2402.step = LD2402_CMND_END_CONFIGURATION;
+ return;
+
+ case LD2402_CMND_READ_VERSION>>4:
+ LD2402.step = LD2402_CMND_READ_SERIAL;
+ break;
+
+ case LD2402_CMND_READ_SERIAL>>4:
+ if (!LD2402.initializing) {
+ LD2402.step = LD2402_CMND_END_CONFIGURATION;
+ return;
+ }
+ LD2402.cmnd_param[0] = LD2402_CMND_PARAM_MAX_DISTANCE;
+ LD2402.next_step = LD2402_CMND_READ_PARAM;
+ LD2402.step = LD2402_CMND_START_CONFIGURATION;
+ break;
+
+ case LD2402_CMND_WRITE_PARAM>>4:
+ switch (LD2402.cmnd_param[0]) {
+ case LD2402_CMND_PARAM_MAX_DISTANCE:
+ Ld2402LoadLastParam(LD2402_CMND_PARAM_DISP_DELAY);
+ LD2402.step = LD2402_CMND_WRITE_PARAM;
+ break;
+
+ case LD2402_CMND_PARAM_DISP_DELAY:
+ case LD2402_CMND_PARAM_MOTION_END:
+ case LD2402_CMND_PARAM_MICRO_END:
+ LD2402.step = LD2402_CMND_END_CONFIGURATION;
+ return;
+
+ default:
+ switch (LD2402.cmnd_param[0] & 0xF0) {
+ // gate specific params
+ case LD2402_CMND_PARAM_MICRO_START:
+ case LD2402_CMND_PARAM_MOTION_START:
+ Ld2402LoadLastParam(++LD2402.cmnd_param[0]);
+ LD2402.step = LD2402_CMND_WRITE_PARAM;
+ break;
+ default:
+ LD2402.step = LD2402_CMND_END_CONFIGURATION;
+ return;
+ }
+ }
+ break;
+
+ case LD2402_CMND_READ_PARAM>>4:
+ switch (LD2402.cmnd_param[0]) {
+ case LD2402_CMND_PARAM_MAX_DISTANCE:
+ LD2402.cmnd_param[0] = LD2402_CMND_PARAM_DISP_DELAY;
+ break;
+
+ case LD2402_CMND_PARAM_DISP_DELAY:
+ LD2402.cmnd_param[0] = LD2402_CMND_PARAM_PWR_INTERFER;
+ break;
+
+ case LD2402_CMND_PARAM_PWR_INTERFER:
+ LD2402.cmnd_param[0] = LD2402_CMND_PARAM_MOTION_START;
+ if (2 == LD2402.pwr_interf) {
+ LD2402.step = LD2402_CMND_AUTO_INTERFERENCE;
+ }
+ break;
+
+ case LD2402_CMND_PARAM_MOTION_END:
+ LD2402.cmnd_param[0] = LD2402_CMND_PARAM_MICRO_START;
+ break;
+
+ case LD2402_CMND_PARAM_MICRO_END:
+ if (LD2402.initializing) {
+ // clear LD2402_CMND_READ_PARAM param
+ LD2402.cmnd_param[0] = 0x00;
+ LD2402.cmnd_param[2] = 0x04;
+ LD2402.step = LD2402_CMND_MODE;
+ Ld2402EveryXMSecond();
+ } else {
+ LD2402.step = LD2402_CMND_END_CONFIGURATION;
+ }
+ return;
+
+ default:
+ switch (LD2402.cmnd_param[0] & 0xF0) {
+ // gate specific params
+ case LD2402_CMND_PARAM_MOTION_START:
+ case LD2402_CMND_PARAM_MICRO_START:
+ LD2402.cmnd_param[0]++;
+ break;
+ default:
+ LD2402.step = LD2402_CMND_END_CONFIGURATION;
+ return;
+ }
+ break;
+ }
+ LD2402.step = LD2402_CMND_READ_PARAM;
+ break;
+
+ default:
+ return;
+ }
+ Ld2402EveryXMSecond();
+}
+
+void Ld2402EveryXMSecond(void) {
+ if (LD2402.step) {
+ uint32_t command = LD2402.step>>4;
+ switch (LD2402.step--) {
+ // special: get control of LD2402
+ case LD2402_CMND_READ_PARAM:
+ Ld2402SendCommand(command, 2);
+ break;
+
+ case LD2402_CMND_START_CONFIGURATION:
+ LD2402.cmnd_param[0] = 0x01;
+ Ld2402SendCommand(command, 2);
+ if (LD2402.initializing) {
+ LD2402.step = LD2402_CMND_START_CONFIGURATION+CMD_LD2402_BOOT_DELAY;
+ return;
+ }
+ break;
+
+ case LD2402_CMND_AUTO_PROGRESS-CMD_LD2402_PROGRESS_DELAY:
+ case LD2402_CMND_AUTO_INTERFERENCE:
+ case LD2402_CMND_READ_VERSION:
+ case LD2402_CMND_READ_SERIAL:
+ case LD2402_CMND_SAVE_PARAM:
+ case LD2402_CMND_END_CONFIGURATION-CMD_LD2402_END_DELAY:
+ Ld2402SendCommand(command);
+ break;
+
+ case LD2402_CMND_AUTO_THRESHOLD:
+ case LD2402_CMND_WRITE_PARAM:
+ case LD2402_CMND_MODE:
+ Ld2402SendCommand(command, 6);
+ break;
+
+ case CMD_LD2402_Wait_Timeout-CMD_LD2402_TIMEOUT_DELAY:
+ Ld2402DeviceDisconnected();
+ return;
+
+ case CMD_LD2402_Done:
+ LD2402.busy = 0;
+ LD2402.initializing = 0;
+ return;
+
+ default:
+ return;
+ }
+ LD2402.step = CMD_LD2402_Wait_Timeout;
+ }
+}
+
+void Ld2402EverySecond(void) {
+ static uint32_t person = 4;
+ if (!LD2402.report_type && !LD2402.initializing) {
+ AddLog(LOG_LEVEL_INFO, PSTR(D_LD2402_LOG_PREFIX "Trying to reconnect."));
+ Ld2402ExecQuickCmnd(LD2402_CMND_READ_VERSION);
+ return;
+ }
+
+ if (!LD2402.timer && (LD2402.person == person)) { return; }
+ if (LD2402.timer) { --LD2402.timer; }
+ if (LD2402.timer && (LD2402.person == person)) { return; }
+ if (LD2402.person != person) { person = LD2402.person; }
+ if (!LD2402.timer) { LD2402.timer = LD2402.follow; }
+
+ MqttPublishSensor();
+}
+
+void Ld2402Detect(void) {
+ if (PinUsed(GPIO_LD2402_RX) && PinUsed(GPIO_LD2402_TX)) {
+ LD2402.buffer = (uint8_t*)malloc(LD2402_BUFFER_SIZE);
+ AddLog(LOG_LEVEL_DEBUG, PSTR(D_LD2402_LOG_PREFIX "Buff size %d"), LD2402_BUFFER_SIZE);
+ if (!LD2402.buffer) { AddLog(LOG_LEVEL_DEBUG, PSTR(D_LD2402_LOG_PREFIX "No buff")); return; }
+ LD2402Serial = new TasmotaSerial(Pin(GPIO_LD2402_RX), Pin(GPIO_LD2402_TX), 2, 0, LD2402_BUFFER_SIZE);
+ if (LD2402Serial->begin(115200)) {
+ if (LD2402Serial->hardwareSerial()) { ClaimSerial(); }
+#ifdef ESP32
+ AddLog(LOG_LEVEL_DEBUG, PSTR(D_LD2402_LOG_PREFIX "Serial UART%d"), LD2402Serial->getUart());
+#endif
+ volatile uint32_t i=0x01234567;
+ LD2402.is_big = (*((uint8_t*)(&i))) != 0x67;
+ if (!LD2402.busy) {
+ AddLog(LOG_LEVEL_DEBUG, PSTR(D_LD2402_LOG_PREFIX "Starting initialization."));
+ LD2402.initializing = true;
+ LD2402.step = LD2402_CMND_START_CONFIGURATION+CMD_LD2402_BOOT_DELAY;
+ LD2402.next_step = LD2402_CMND_READ_VERSION;
+ }
+ }
+ }
+}
+
+/*********************************************************************************************\
+ * Commands
+\*********************************************************************************************/
+
+#define D_CMD_MODE "Mode"
+#define D_CMD_AUTOUPDATE "AutoUpdate"
+#define D_CMD_STATUS "Status"
+#define D_CMD_SETCOMMON "SetCommon"
+#define D_CMD_SETMOTION "SetMotion"
+#define D_CMD_SETMICRO "SetMicro"
+#define D_CMD_HELP "Help"
+#define D_CMD_REREAD "ReRead"
+#define D_CMD_FOLLOW "Follow"
+
+const char kLd2402Commands[] PROGMEM = D_LD2402 "_|" // Prefix
+ D_CMD_MODE "|" D_CMD_AUTOUPDATE "|" D_CMD_STATUS "|" D_CMD_SETCOMMON "|"
+ D_CMD_SETMOTION "|" D_CMD_SETMICRO "|" D_CMD_HELP "|" D_CMD_REREAD "|"
+ D_CMD_FOLLOW;
+
+void (* const Ld2402Command[])(void) PROGMEM = {
+ &CmndLd2402Mode, &CmndLd2402AutoUpdate, &CmndLd2402Status, &CmndLd2402Common,
+ &CmndLd2402Motion, &CmndLd2402Micro, &CmndLd2402Help, &CmndLd2402ReRead,
+ &CmndLd2402Follow };
+
+void CmndLd2402Help(void) {
+ Response_P(PSTR(D_COMMAND_PREFIX_JSON"\"%s"), D_CMD_HELP, "Available commands: ");
+ ResponseAppend_P(PSTR(D_LD2402 "_" D_CMD_MODE", "));
+ ResponseAppend_P(PSTR(D_LD2402 "_" D_CMD_AUTOUPDATE", "));
+ ResponseAppend_P(PSTR(D_LD2402 "_" D_CMD_STATUS", "));
+ ResponseAppend_P(PSTR(D_LD2402 "_" D_CMD_SETCOMMON", "));
+ ResponseAppend_P(PSTR(D_LD2402 "_" D_CMD_SETMOTION", "));
+ ResponseAppend_P(PSTR(D_LD2402 "_" D_CMD_SETMICRO", "));
+ ResponseAppend_P(PSTR(D_LD2402 "_" D_CMD_HELP", "));
+ ResponseAppend_P(PSTR(D_LD2402 "_" D_CMD_REREAD", "));
+ ResponseAppend_P(PSTR(D_LD2402 "_" D_CMD_FOLLOW"\"}"));
+}
+
+void CmndLd2402Follow(void) {
+ if (ArgC() != 1) {
+ Response_P(PSTR(D_COMMAND_HELP_MSG), D_CMD_FOLLOW, "Args: 0|n (MQTT update normal|MQTT update every n second(s))");
+ return;
+ }
+ char Argument[XdrvMailbox.data_len];
+ ArgV(Argument,1);
+ LD2402.timer = atoi(Argument);
+ LD2402.follow = LD2402.timer;
+ Response_P(PSTR(D_COMMAND_PREFIX_JSON"%d}"), D_CMD_FOLLOW, LD2402.follow);
+}
+
+void CmndLd2402Status(void) {
+ if (ArgC() != 1) {
+ Response_P(PSTR(D_COMMAND_HELP_MSG), D_CMD_STATUS, "Args: 0|1|2 (parameters|sensor|firmware)");
+ return;
+ }
+ uint32_t status_type = 0;
+ if (ArgC() == 1) {
+ char Argument[XdrvMailbox.data_len];
+ ArgV(Argument,1);
+ status_type = atoi(Argument);
+ }
+
+ Response_P(PSTR("{\"" D_LD2402));
+ if (1 == status_type) {
+ ResponseAppend_P(PSTR("SNS\":{"));
+ Ld2402ResponseAppendReport();
+ } else if (2 == status_type) {
+ ResponseAppend_P(PSTR("FWR\":{\"Version\":\"%s\","),LD2402.version);
+ ResponseAppend_P(PSTR("\"SerialNumber\":\"%s\"}}"),LD2402.serial_number);
+ } else {
+ ResponseAppend_P(PSTR("\":{\"MaximumDistance\":%d,"), LD2402.max_distance);
+ ResponseAppend_P(PSTR("\"DisappearenceDelay\":%d,"), LD2402.disp_delay);
+ ResponseAppend_P(PSTR("\"MotionThresholds\":["));
+ Ld2402ResponseAppendGates(LD2402.motion_threshold);
+ Ld2402ResponseAppendGates(LD2402.micro_threshold);
+ ResponseAppend_P(PSTR("]}}"));
+ }
+}
+
+void CmndLd2402ReRead(void) {
+ if (LD2402.busy) {
+ Response_P(PSTR(D_BUSY_MSG));
+ return;
+ }
+ if (ArgC()) {
+ Response_P(PSTR(D_COMMAND_HELP_MSG), D_CMD_REREAD, "No Args: Retrieves common and threshold data from device");
+ return;
+ }
+ LD2402.cmnd_param[0] = LD2402_CMND_PARAM_MAX_DISTANCE;
+ Ld2402ExecConfigCmnd(LD2402_CMND_READ_PARAM);
+ Response_P(PSTR(D_COMMAND_HELP_MSG), D_CMD_REREAD, "Retrieving ... wait six seconds for it to complete");
+}
+
+void CmndLd2402Common(void) {
+ if (LD2402.busy) {
+ Response_P(PSTR(D_BUSY_MSG));
+ return;
+ }
+ if (ArgC() != 2) {
+ Response_P(PSTR(D_COMMAND_HELP_MSG), D_CMD_SETCOMMON, "Args: MaximumDistance,DisappearanceDelay (decimeter,seconds)");
+ return;
+ }
+ uint32_t param[2] = { 0 };
+ ParseParameters(2, param);
+ if (param[0]>100) {param[0] = 100;}
+ if (param[0]<7) {param[0] = 7;}
+ if (param[1]<0) {param[1] = 0;}
+ if (param[1]>65535) {param[1] = 65535;}
+ LD2402.max_distance = param[0];
+ LD2402.disp_delay = param[1];
+ Ld2402LoadLastParam(LD2402_CMND_PARAM_MAX_DISTANCE);
+ Ld2402ExecConfigCmnd(LD2402_CMND_WRITE_PARAM);
+ CmndLd2402Status();
+}
+
+void CmndLd2402Mode(void) {
+ if (LD2402.busy) {
+ Response_P(PSTR(D_BUSY_MSG));
+ return;
+ }
+ if (ArgC() != 1) {
+ Response_P(PSTR(D_COMMAND_HELP_MSG), D_CMD_MODE, "Args: 0|1 (normal|engineering)");
+ return;
+ }
+ char Argument[XdrvMailbox.data_len];
+ ArgV(Argument,1);
+ memset(LD2402.cmnd_param, 0x00, 6);
+ LD2402.cmnd_param[2] = atoi(Argument) ? 0x04 : 0x64;
+ Response_P(PSTR(D_COMMAND_PREFIX_JSON"%d}"), D_CMD_FOLLOW, (0x04 == LD2402.cmnd_param[2]));
+ Ld2402ExecConfigCmnd(LD2402_CMND_MODE);
+}
+
+void CmndLd2402AutoUpdate(void) {
+ if (LD2402.busy) {
+ Response_P(PSTR(D_BUSY_MSG));
+ return;
+ }
+ if (ArgC() != 3) {
+ Response_P(PSTR(D_COMMAND_HELP_MSG), D_CMD_AUTOUPDATE, "Args: trigger,keep,micro (magnification factor, 1.0 to 20.0)");
+ return;
+ }
+
+ char strbuf[24];
+ for (uint32_t i = 3; i; i--) {
+ ArgV(strbuf, i);
+ float param = CharToFloat(strbuf);
+ LD2402.cmnd_param[(i-1)*2] = (param < 1.0f) ? 10.0f : (param > 20.0f ? 200.0f : param * 10.0f);
+ }
+ Ld2402ExecConfigCmnd(LD2402_CMND_AUTO_THRESHOLD);
+ Response_P(PSTR(D_COMMAND_HELP_MSG), D_CMD_REREAD, "Updating ...");
+}
+
+void CmndLd2402Motion(void) {
+ Ld2402WriteThresholds(LD2402.motion_threshold, LD2402_CMND_PARAM_MOTION_START);
+}
+
+void CmndLd2402Micro(void) {
+ Ld2402WriteThresholds(LD2402.micro_threshold, LD2402_CMND_PARAM_MICRO_START);
+}
+
+/*********************************************************************************************\
+ * Presentation
+\*********************************************************************************************/
+
+#ifdef USE_WEBSERVER
+const char HTTP_SNS_LD2402_DIS[] PROGMEM =
+ "{s}" D_LD2402 " " D_DISCONNECTED "{e}";
+const char HTTP_SNS_LD2402_CM[] PROGMEM =
+ "{s}" D_LD2402 " " D_DISTANCE "{m}%d " D_UNIT_CENTIMETER "{e}";
+const char HTTP_SNS_LD2402_MV[] PROGMEM =
+ "{s}" D_LD2402 " " D_DISTANCE " (!){m}%d " D_UNIT_CENTIMETER "{e}";
+const char HTTP_SNS_LD2402_UPD[] PROGMEM =
+ "{s}" D_LD2402 " " D_AUTO_UPDATE "{m}%d " D_UNIT_PERCENT "{e}";
+
+void Ld2402Web(void) {
+ if (3 == LD2402.report_type) {
+ WSContentSend_PD(HTTP_SNS_LD2402_UPD, LD2402.auto_upd_progress);
+ } else if (2 == LD2402.report_type) {
+ WSContentSend_PD(HTTP_SNS_LD2402_CM, LD2402.detect_distance);
+ } else if (1 == LD2402.report_type) {
+ WSContentSend_PD((1 == LD2402.person ? HTTP_SNS_LD2402_MV : HTTP_SNS_LD2402_CM), LD2402.detect_distance);
+ } else {
+ WSContentSend_PD(HTTP_SNS_LD2402_DIS);
+ }
+}
+#endif
+
+void Ld2402Show(void) {
+ ResponseAppend_P(PSTR(",\"" D_LD2402 "\":{"));
+ Ld2402ResponseAppendReport();
+}
+
+/*********************************************************************************************\
+ * Interface
+\*********************************************************************************************/
+
+bool Xsns102(uint32_t function) {
+ bool result = false;
+
+ if (FUNC_INIT == function) {
+ Ld2402Detect();
+ }
+ else if (LD2402Serial) {
+ switch (function) {
+ case FUNC_LOOP:
+ case FUNC_SLEEP_LOOP:
+ Ld2402Input();
+ break;
+ case FUNC_EVERY_50_MSECOND:
+ Ld2402EveryXMSecond();
+ break;
+ case FUNC_EVERY_100_MSECOND:
+ break;
+ case FUNC_EVERY_SECOND:
+ Ld2402EverySecond();
+ break;
+ case FUNC_JSON_APPEND:
+ Ld2402Show();
+ break;
+#ifdef USE_WEBSERVER
+ case FUNC_WEB_SENSOR:
+ Ld2402Web();
+ break;
+#endif // USE_WEBSERVER
+ case FUNC_COMMAND:
+ result = DecodeCommand(kLd2402Commands, Ld2402Command);
+ break;
+ }
+ }
+ return result;
+}
+
+#endif // USE_LD2402