Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[P159] Add LD2410 Radar presence detection #4852

Merged
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
0756853
[P159] Add LD2410 Radar presence detection
tonhuisman Oct 16, 2023
38bd3ef
[P159] Remove copy/paste evidence :-D
tonhuisman Oct 17, 2023
a3be20f
Merge branch 'mega' of https://github.com/letscontrolit/ESPEasy into …
tonhuisman Oct 18, 2023
3c81f77
[P159] Many improvements, add/update settings for sensitivity
tonhuisman Oct 21, 2023
ca48cb1
[P159] Fix compilation on LIMIT_BUILD_SIZE builds
tonhuisman Oct 22, 2023
402c5c8
[P159] Small improvements
tonhuisman Oct 22, 2023
d94d8bd
[P159] Small improvements
tonhuisman Oct 22, 2023
17db570
[P159] Add command for sensor factoryreset
tonhuisman Oct 22, 2023
cd2c6ec
Merge branch 'mega' of https://github.com/letscontrolit/ESPEasy into …
tonhuisman Oct 22, 2023
b3e1715
[Lib] LD2410: Fix bug (-1 for uint), add undocumented OutputPinState …
tonhuisman Oct 23, 2023
f3306c0
[P159] Add OutputPinState option after AmbientLight option (breaking …
tonhuisman Oct 23, 2023
0caef38
[P159] Minor change to determine how that may influence the build
tonhuisman Oct 24, 2023
f689cae
[Build] Freeze Python to 3.11 to fix pygit2 build issue
tonhuisman Oct 28, 2023
1938bf3
[P159] Add Gate0 energy values that where missing
tonhuisman Oct 28, 2023
b9030fa
Merge branch 'mega' of https://github.com/letscontrolit/ESPEasy into …
tonhuisman Oct 29, 2023
b753f5c
[Lib] LD2410: Read all data that can be read in 5 msec, add errorcoun…
tonhuisman Oct 29, 2023
5a9b6e6
[P159] Rework processing, now near-instant response
tonhuisman Oct 29, 2023
53ffcbd
[Lib] LD2410: Reformat source (nearly all is changed!) Replace yield(…
tonhuisman Oct 29, 2023
68ca970
[P159] Fix wrong use of F(""), should be EMPTY_STRING
tonhuisman Oct 29, 2023
01ed5da
Merge branch 'mega' of https://github.com/letscontrolit/ESPEasy into …
tonhuisman Nov 3, 2023
5395c92
Merge branch 'mega' of https://github.com/letscontrolit/ESPEasy into …
tonhuisman Nov 5, 2023
4b16752
Merge branch 'mega' of https://github.com/letscontrolit/ESPEasy into …
tonhuisman Nov 8, 2023
a3eea72
Merge branch 'mega' of https://github.com/letscontrolit/ESPEasy into …
tonhuisman Nov 13, 2023
c7344df
[P159] Add documentation
tonhuisman Nov 14, 2023
357ecb3
[P159] Update EasyColorMirror
tonhuisman Nov 15, 2023
30f0cb3
[P159] Update comments to explicitly name LD2410
tonhuisman Nov 15, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 52 additions & 16 deletions src/_P159_LD2410.ino
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@
// #######################################################################################################

/** Changelog:
* 2023-10-21 tonhuisman: Read data at 50/sec instead of 10/sec to catch up with the high speed of output
* Add/update settings for Sensitivity and nr. of active gates, idle seconds
* Shorten default value names, (breaking) change setting for Engineering mode
* Add setting for generating events only when a value has changed
* 2023-10-14 tonhuisman: Using ld2410 library from https://github.com/ncmreynolds/ld2410, but with PR #3 applied
* including a fix for index/index + 1, mentioned in the comments of that PR and timeout
* extended to 2500 msec
Expand Down Expand Up @@ -35,16 +39,17 @@ boolean Plugin_159(uint8_t function, struct EventStruct *event, String& string)
{
// This case defines the device characteristics

Device[++deviceCount].Number = PLUGIN_ID_159;
Device[deviceCount].Type = DEVICE_TYPE_SERIAL;
Device[deviceCount].VType = Sensor_VType::SENSOR_TYPE_SINGLE;
Device[deviceCount].ValueCount = 4;
Device[deviceCount].OutputDataType = Output_Data_type_t::Simple;
Device[deviceCount].FormulaOption = true;
Device[deviceCount].SendDataOption = true;
Device[deviceCount].GlobalSyncOption = true;
Device[deviceCount].TimerOption = true;
Device[deviceCount].PluginStats = true;
Device[++deviceCount].Number = PLUGIN_ID_159;
Device[deviceCount].Type = DEVICE_TYPE_SERIAL;
Device[deviceCount].VType = Sensor_VType::SENSOR_TYPE_SINGLE;
Device[deviceCount].ValueCount = 4;
Device[deviceCount].OutputDataType = Output_Data_type_t::Simple;
Device[deviceCount].FormulaOption = true;
Device[deviceCount].SendDataOption = true;
Device[deviceCount].GlobalSyncOption = true;
Device[deviceCount].TimerOption = true;
Device[deviceCount].PluginStats = true;
Device[deviceCount].ExitTaskBeforeSave = false; // Enable calling PLUGIN_WEBFORM_SAVE on the instantiated object

break;
}
Expand Down Expand Up @@ -136,7 +141,7 @@ boolean Plugin_159(uint8_t function, struct EventStruct *event, String& string)

case PLUGIN_WEBFORM_LOAD_OUTPUT_SELECTOR:
{
const uint8_t optionCount = P159_ENGINEERING_MODE == 1 ? P159_NR_ENGINEERING_OUTPUT_OPTIONS : P159_NR_OUTPUT_OPTIONS;
const uint8_t optionCount = P159_GET_ENGINEERING_MODE == 1 ? P159_NR_ENGINEERING_OUTPUT_OPTIONS : P159_NR_OUTPUT_OPTIONS;
String options[optionCount];

for (uint8_t option = 0; option < optionCount; ++option) {
Expand All @@ -158,23 +163,41 @@ boolean Plugin_159(uint8_t function, struct EventStruct *event, String& string)
const uint8_t pconfigIndex = i + P159_QUERY1_CONFIG_POS;
sensorTypeHelper_loadOutputSelector(event, pconfigIndex, i, optionCount, options);
}

success = true;

break;
}

case PLUGIN_WEBFORM_LOAD:
{
addFormSelector_YesNo(F("Engineering mode"), F("eng"), P159_ENGINEERING_MODE, true);
addFormSelector_YesNo(F("Engineering mode"), F("eng"), P159_GET_ENGINEERING_MODE, true);
addFormNote(F("When changing this setting the page will be reloaded"));

addFormCheckBox(F("Generate Events only when changed"), F("diff"), P159_GET_UPDATE_DIFF_ONLY);

success = true;

P159_data_struct *P159_data = static_cast<P159_data_struct *>(getPluginTaskData(event->TaskIndex));

if (nullptr != P159_data) {
addFormSubHeader(F("Sensitivity settings"));
success = P159_data->plugin_webform_load(event);
}

break;
}

case PLUGIN_WEBFORM_SAVE:
{
P159_ENGINEERING_MODE = getFormItemInt(F("eng"));
P159_SET_ENGINEERING_MODE(getFormItemInt(F("eng")));
P159_SET_UPDATE_DIFF_ONLY(isFormItemChecked(F("diff")) ? 1 : 0);

P159_data_struct *P159_data = static_cast<P159_data_struct *>(getPluginTaskData(event->TaskIndex));

if (nullptr != P159_data) {
P159_data->plugin_webform_save(event);
}

success = true;

Expand All @@ -187,7 +210,10 @@ boolean Plugin_159(uint8_t function, struct EventStruct *event, String& string)
ESPEasySerialPort portType = serialHelper_getSerialType(event);

// Create the P159_data_struct object that will do all the sensor interaction
initPluginTaskData(event->TaskIndex, new (std::nothrow) P159_data_struct(portType, rxPin, txPin, P159_ENGINEERING_MODE == 1));
initPluginTaskData(event->TaskIndex, new (std::nothrow) P159_data_struct(portType,
rxPin,
txPin,
P159_GET_ENGINEERING_MODE == 1));
P159_data_struct *P159_data = static_cast<P159_data_struct *>(getPluginTaskData(event->TaskIndex));

success = nullptr != P159_data;
Expand Down Expand Up @@ -231,8 +257,18 @@ boolean Plugin_159(uint8_t function, struct EventStruct *event, String& string)
{
P159_data_struct *P159_data = static_cast<P159_data_struct *>(getPluginTaskData(event->TaskIndex));

if (nullptr != P159_data) {
success = P159_data->processSensor(); // This has 50 msec delays included, so should NOT go in PLUGIN_FIFTY_PER_SECOND !
if ((nullptr != P159_data) && !P159_data->isRunning()) {
success = P159_data->processSensor(); // Not Running can have 50 msec delays included, so NOT in PLUGIN_FIFTY_PER_SECOND !
}

break;
}
case PLUGIN_FIFTY_PER_SECOND:
{
P159_data_struct *P159_data = static_cast<P159_data_struct *>(getPluginTaskData(event->TaskIndex));

if ((nullptr != P159_data) && P159_data->isRunning()) {
success = P159_data->processSensor(); // When running no delays are inserted, so can go in PLUGIN_FIFTY_PER_SECOND
}

break;
Expand Down
173 changes: 137 additions & 36 deletions src/src/PluginStructs/P159_data_struct.cpp
Original file line number Diff line number Diff line change
@@ -1,8 +1,3 @@
// ----------------------------------------------------------------------------
// P159 "Dust - PM1006(K) (Vindriktning)"
// Implementation of sensor abstraction
// 2022 By flashmark
// ----------------------------------------------------------------------------
#include "../PluginStructs/P159_data_struct.h"

#ifdef USES_P159
Expand All @@ -11,14 +6,14 @@ const __FlashStringHelper* Plugin_159_valuename(uint8_t value_nr,
bool displayString) {
const __FlashStringHelper *strings[] { /*** ATTENTION: Don't change order of values as these are stored as user-selected!!! ***/
F("Presence"), F("Presence"),
F("Stationary Presence"), F("StationaryPresence"),
F("Moving Presence"), F("MovingPresence"),
F("Stationary Presence"), F("StatPresence"),
F("Moving Presence"), F("MovPresence"),
F("Object distance"), F("Distance"),
F("Stationary Object distance"), F("StationaryDistance"),
F("Moving Object distance"), F("MovingDistance"),
F("Stationary Object energy"), F("StationaryEnergy"),
F("Moving Object energy"), F("MovingEnergy"),
F("Ambient light sensor"), F("AmbientLight"),
F("Stationary Object distance"), F("StatDistance"),
F("Moving Object distance"), F("MovDistance"),
tonhuisman marked this conversation as resolved.
Show resolved Hide resolved
F("Stationary Object energy"), F("StatEnergy"),
F("Moving Object energy"), F("MovEnergy"),
F("Ambient light sensor"), F("AmbLight"),
F("Stationary Object energy gate "), F("StatEnergyGate"),
F("Moving Object energy gate "), F("MovingEnergyGate"),

Expand Down Expand Up @@ -79,55 +74,73 @@ bool P159_data_struct::processSensor() {
bool new_data = false;

if (isValid()) {
uint32_t iStart = millis(); // FIXME Remove log
P159_state_e sState = state;

switch (state) {
case P159_state_e::Initializing:
addLog(LOG_LEVEL_INFO, F("LD2410: Initializing"));
break;
case P159_state_e::Restarting:

if ((milestone > 0) && (timePassedSince(milestone) > P159_DELAY_RESTART)) {
state = P159_state_e::Configuring;
state = P159_state_e::GetVersion;
milestone = 0;
radar->requestFirmwareVersion();
}
break;
case P159_state_e::GetVersion:

state = P159_state_e::GetConfiguration;

if (loglevelActiveFor(LOG_LEVEL_INFO)) {
addLog(LOG_LEVEL_INFO, concat(F("LD2410: Firmware version: "), radar->cmdFirmwareVersion()));
}
_configurationRead = radar->requestCurrentConfiguration();
break;
case P159_state_e::GetConfiguration:

state = P159_state_e::Engineering;

if (loglevelActiveFor(LOG_LEVEL_INFO)) {
addLog(LOG_LEVEL_INFO, concat(F("LD2410: Fetch configuration: "), _configurationRead ? 1 : 0));

if (_configurationRead) {
const uint8_t mGate = radar->cfgMaxGate();
const uint8_t mMvGate = radar->cfgMaxMovingGate();
const uint8_t mStGate = radar->cfgMaxStationaryGate();
const uint8_t mMax = std::max(mGate, std::max(mMvGate, mStGate));
addLog(LOG_LEVEL_INFO, strformat(F("LD2410: Sensor idle time: %d sec."), radar->cfgSensorIdleTimeInSeconds()));
addLog(LOG_LEVEL_INFO, strformat(F("LD2410: Max. gate: %d, max. moving gate: %d, max. stationary gate: %d"),
mGate, mMvGate, mStGate));

if (loglevelActiveFor(LOG_LEVEL_INFO)) {
radar->requestFirmwareVersion();
addLog(LOG_LEVEL_INFO, concat(F("LD2410: Firmware version: "), radar->cmdFirmwareVersion()));

const bool cfg = radar->requestCurrentConfiguration();
addLog(LOG_LEVEL_INFO, concat(F("LD2410: Fetch configuration: "), cfg ? 1 : 0));

if (cfg) {
const uint8_t mGate = radar->cfgMaxGate();
const uint8_t mMvGate = radar->cfgMaxMovingGate();
const uint8_t mStGate = radar->cfgMaxStationaryGate();
const uint8_t mMax = std::max(mGate, std::max(mMvGate, mStGate));
addLog(LOG_LEVEL_INFO, strformat(F("LD2410: Sensor idle time: %d sec."), radar->cfgSensorIdleTimeInSeconds()));
addLog(LOG_LEVEL_INFO, strformat(F("LD2410: Max. gate: %d, max. moving gate: %d, max. stationary gate: %d"),
mGate, mMvGate, mStGate));

for (uint8_t i = 0; i < mMax; ++i) {
addLog(LOG_LEVEL_INFO, strformat(F("LD2410: Sensitivity, gate %d (%1.2f - %1.2f mtr): moving:%3d, stationary:%3d"),
i + 1, i * 0.75f, (i + 1) * 0.75f,
i < mMvGate ? radar->cfgMovingGateSensitivity(i) : 0,
i < mStGate ? radar->cfgStationaryGateSensitivity(i) : 0));
}
for (uint8_t i = 0; i <= mMax; ++i) {
addLog(LOG_LEVEL_INFO, strformat(F("LD2410: Sensitivity, gate %d (%.2f - %.2f mtr): moving:%3d, stationary:%3d"),
i, i * 0.75f, (i + 1) * 0.75f,
i <= mMvGate ? radar->cfgMovingGateSensitivity(i) : 0,
i <= mStGate ? radar->cfgStationaryGateSensitivity(i) : 0));
}
}
}
break;
case P159_state_e::Configuring:
case P159_state_e::Engineering:
state = P159_state_e::Running;

if (loglevelActiveFor(LOG_LEVEL_INFO)) {
if (_engineeringMode) {
addLog(LOG_LEVEL_INFO, concat(F("LD2410: Engineering mode: "), radar->requestStartEngineeringMode() ? 1 : 0));
}
addLog(LOG_LEVEL_INFO, F("LD2410: Running mode."));
}
break;
case P159_state_e::Running:
new_data = radar->ld2410_loop();
break;
}

if (P159_state_e::Running != sState) { // FIXME Remove log
addLog(LOG_LEVEL_INFO, strformat(F("LD2410: Starting state: %d duration: %u msec."), static_cast<uint8_t>(sState), millis() - iStart));
tonhuisman marked this conversation as resolved.
Show resolved Hide resolved
}
} // isValid()

return new_data;
Expand All @@ -138,6 +151,7 @@ bool P159_data_struct::plugin_read(struct EventStruct *event) {

if (isValid() && radar->isConnected()) {
const int8_t valueCount = P159_NR_OUTPUT_VALUES;
result = !P159_GET_UPDATE_DIFF_ONLY;

for (int8_t i = 0; i < valueCount; ++i) {
const uint8_t pconfigIndex = i + P159_QUERY1_CONFIG_POS;
Expand Down Expand Up @@ -187,4 +201,91 @@ int P159_data_struct::getRadarValue(struct EventStruct *event,
return result;
} // getRadarValue()

void P159_data_struct::addJavascript() {
addHtml(F("<script type='text/javascript'>"
"function chDis(s,m) {"
"for(i=1;i<=m;i++){" // "console.log('pc_arg'+i);" // For debugging the js code
"document.getElementById('pc_arg'+i).disabled=!s"
"}"
"};</script>"
));
}

bool P159_data_struct::plugin_webform_load(struct EventStruct *event) {
bool result = false;

if (isValid()) {
if (!_configurationRead) {
_configurationRead = radar->requestCurrentConfiguration();
}

if (_configurationRead) {
const uint8_t mGate = radar->cfgMaxGate();
const uint8_t mMvGate = radar->cfgMaxMovingGate();
const uint8_t mStGate = radar->cfgMaxStationaryGate();
const uint8_t mMax = std::max(mGate, std::max(mMvGate, mStGate));
const uint16_t idleTime = radar->cfgSensorIdleTimeInSeconds();

addJavascript();
addFormCheckBox(F("Modify sensor settings"), F("saveSens"), false);

int idx = 0;
addFormNumericBox(F("Idle seconds"), getPluginCustomArgName(idx++), idleTime, 0, 65535, EMPTY_STRING, true);
addUnit(F("0..65535 sec."));
addFormNumericBox(F("Max. Moving gates"), getPluginCustomArgName(idx++), mMvGate, 2, 8, EMPTY_STRING, true);
addUnit(F("2..8"));
addFormNumericBox(F("Max. Stationary gates"), getPluginCustomArgName(idx++), mStGate, 2, 8, EMPTY_STRING, true);
addUnit(F("2..8"));

addRowLabel(F("Sensitivity"));
html_table(EMPTY_STRING, false); // Sub-table
html_table_header(F("Gate"), 200);
html_table_header(F("Moving"), 100);
html_table_header(F("Stationary"), 100);

for (uint8_t i = 0; i <= mMax; ++i) {
html_TR_TD();
addHtml(strformat(F("Gate %d (%.2f - %.2f mtr)"), i, i * 0.75f, (i + 1) * 0.75f));
html_TD();
addNumericBox(getPluginCustomArgName(idx++), radar->cfgMovingGateSensitivity(i), 0, 100, true);
html_TD();
addNumericBox(getPluginCustomArgName(idx++), radar->cfgStationaryGateSensitivity(i), 0, 100, true);
}
html_end_table();
addHtml(strformat(F("\n<script type='text/javascript'>document.getElementById('saveSens')."
"onclick=function(){chDis(this.checked,%d)};</script>"),
idx));
result = true;
}
}
return result;
}

bool P159_data_struct::plugin_webform_save(struct EventStruct *event) {
bool result = false;
const bool doSave = isFormItemChecked(F("saveSens"));

if (isValid() && doSave) {
int idx = 0;
const uint16_t idle = getFormItemInt(getPluginCustomArgName(idx++));
const uint8_t gMove = getFormItemInt(getPluginCustomArgName(idx++));
const uint8_t gStat = getFormItemInt(getPluginCustomArgName(idx++));
addLog(LOG_LEVEL_INFO, F("LD2410: Save sensitivity settings to sensor, start..."));
radar->requestConfigurationModeBegin();
radar->setMaxValues(gMove, gStat, idle);
const uint16_t maxGate = radar->cfgMaxGate();

for (uint16_t gate = 0; gate <= maxGate; ++gate) {
const uint16_t sMove = getFormItemInt(getPluginCustomArgName(idx++));
const uint16_t sStat = getFormItemInt(getPluginCustomArgName(idx++));

// Set sensitivity (level) to 100 to effectively disable sensitivity
radar->setGateSensitivityThreshold(gate, gate <= gMove ? sMove : 100, gate <= gStat ? sStat : 100);
}
radar->requestConfigurationModeEnd();
addLog(LOG_LEVEL_INFO, F("LD2410: Save sensitivity settings to sensor, done."));
}
return result;
}

#endif // USES_P159
Loading