From be3d329a0f2009c1a99923f647d9aaae5036f0cd Mon Sep 17 00:00:00 2001 From: Ton Huisman Date: Tue, 25 Jul 2023 23:22:29 +0200 Subject: [PATCH 1/8] [P028] Add detection-mode setting --- src/_P028_BME280.ino | 35 ++++++++++++++++++---- src/src/PluginStructs/P028_data_struct.cpp | 8 ++--- src/src/PluginStructs/P028_data_struct.h | 15 +++++++--- 3 files changed, 44 insertions(+), 14 deletions(-) diff --git a/src/_P028_BME280.ino b/src/_P028_BME280.ino index 5613e3be17..355e1d723e 100644 --- a/src/_P028_BME280.ino +++ b/src/_P028_BME280.ino @@ -5,6 +5,11 @@ // #################### Plugin 028 BME280 I2C Temp/Hum/Barometric Pressure Sensor ####################### // ####################################################################################################### +/** Changelog: + * 2023-07-25 tonhuisman: Add setting to enable forcing the plugin into either BME280 or BMP280 mode, default is Auto-detect + * Add changelog + */ + # include "src/PluginStructs/P028_data_struct.h" // #include @@ -138,7 +143,7 @@ boolean Plugin_028(uint8_t function, struct EventStruct *event, String& string) if (nullptr != P028_data) { if (P028_data->sensorID != P028_data_struct::Unknown_DEVICE) { String detectedString = F("Detected: "); - detectedString += P028_data->getDeviceName(); + detectedString += P028_data->getDeviceName(P028_data->sensorID); addUnit(detectedString); } } @@ -157,6 +162,20 @@ boolean Plugin_028(uint8_t function, struct EventStruct *event, String& string) } addFormNote(offsetNote); + { + const __FlashStringHelper *detectOptionList[] = { + F("Auto"), + P028_data_struct::getDeviceName(P028_data_struct::BMx_ChipId::BME280_DEVICE), + P028_data_struct::getDeviceName(P028_data_struct::BMx_ChipId::BMP280_DEVICE), + }; + const int detectOptions[] = { + static_cast(P028_data_struct::BMx_DetectMode::Auto), + static_cast(P028_data_struct::BMx_DetectMode::BME280), + static_cast(P028_data_struct::BMx_DetectMode::BMP280), + }; + addFormSelector(F("Sensor model"), F("det"), 3, detectOptionList, detectOptions, P028_DETECTION_MODE); + } + success = true; break; } @@ -221,6 +240,7 @@ boolean Plugin_028(uint8_t function, struct EventStruct *event, String& string) P028_ALTITUDE = getFormItemInt(F("elev")); P028_TEMPERATURE_OFFSET = getFormItemInt(F("tempoffset")); P028_ERROR_STATE_OUTPUT = getFormItemInt(F("err")); + P028_DETECTION_MODE = getFormItemInt(F("det")); success = true; break; } @@ -261,7 +281,10 @@ boolean Plugin_028(uint8_t function, struct EventStruct *event, String& string) } else { P028_data->state = P028_data_struct::BMx_Values_read; - if (!P028_data->hasHumidity()) { + const P028_data_struct::BMx_DetectMode detectMode = static_cast(P028_DETECTION_MODE); + + if (((detectMode == P028_data_struct::BMx_DetectMode::Auto) && !P028_data->hasHumidity()) || + (detectMode == P028_data_struct::BMx_DetectMode::BMP280)) { // Patch the sensor type to output only the measured values. event->sensorType = Sensor_VType::SENSOR_TYPE_TEMP_EMPTY_BARO; } @@ -281,24 +304,24 @@ boolean Plugin_028(uint8_t function, struct EventStruct *event, String& string) String log; if (log.reserve(40)) { // Prevent re-allocation - log = P028_data->getDeviceName(); + log = P028_data->getDeviceName(P028_data->sensorID); log += F(": Address: "); log += formatToHex(P028_I2C_ADDRESS, 2); addLogMove(LOG_LEVEL_INFO, log); // addLogMove does also clear the string. - log = P028_data->getDeviceName(); + log = P028_data->getDeviceName(P028_data->sensorID); log += F(": Temperature: "); log += formatUserVarNoCheck(event->TaskIndex, 0); addLogMove(LOG_LEVEL_INFO, log); if (P028_data->hasHumidity()) { - log = P028_data->getDeviceName(); + log = P028_data->getDeviceName(P028_data->sensorID); log += F(": Humidity: "); log += formatUserVarNoCheck(event->TaskIndex, 1); addLogMove(LOG_LEVEL_INFO, log); } - log = P028_data->getDeviceName(); + log = P028_data->getDeviceName(P028_data->sensorID); log += F(": Barometric Pressure: "); log += formatUserVarNoCheck(event->TaskIndex, 2); addLogMove(LOG_LEVEL_INFO, log); diff --git a/src/src/PluginStructs/P028_data_struct.cpp b/src/src/PluginStructs/P028_data_struct.cpp index cd36b1db41..be6f0689c0 100644 --- a/src/src/PluginStructs/P028_data_struct.cpp +++ b/src/src/PluginStructs/P028_data_struct.cpp @@ -21,10 +21,10 @@ uint8_t P028_data_struct::get_control_settings() const { return sensorID == Unknown_DEVICE ? 0u : 0x93; // Oversampling: 8x P, 8x T, normal mode } -const __FlashStringHelper * P028_data_struct::getDeviceName() const { +const __FlashStringHelper * P028_data_struct::getDeviceName(BMx_ChipId sensorID) { switch (sensorID) { case BMP280_DEVICE_SAMPLE1: - case BMP280_DEVICE_SAMPLE2: return F("BMP280 sample"); + case BMP280_DEVICE_SAMPLE2: return F("sample BMP280"); case BMP280_DEVICE: return F("BMP280"); case BME280_DEVICE: return F("BME280"); default: return F("Unknown"); @@ -102,7 +102,7 @@ bool P028_data_struct::updateMeasurements(taskIndex_t task_index) { if (loglevelActiveFor(LOG_LEVEL_INFO)) { log.reserve(120); // Prevent re-allocation - log = getDeviceName(); + log = getDeviceName(sensorID); log += ':'; } bool logAdded = false; @@ -210,7 +210,7 @@ bool P028_data_struct::check() { if (loglevelActiveFor(LOG_LEVEL_INFO)) { String log = F("BMx280: Detected "); - log += getDeviceName(); + log += getDeviceName(sensorID); addLogMove(LOG_LEVEL_INFO, log); } } diff --git a/src/src/PluginStructs/P028_data_struct.h b/src/src/PluginStructs/P028_data_struct.h index 99643a1e27..c23762b0d4 100644 --- a/src/src/PluginStructs/P028_data_struct.h +++ b/src/src/PluginStructs/P028_data_struct.h @@ -61,6 +61,7 @@ # define P028_ALTITUDE PCONFIG(1) # define P028_TEMPERATURE_OFFSET PCONFIG(2) # define P028_ERROR_STATE_OUTPUT PCONFIG(3) +# define P028_DETECTION_MODE PCONFIG(4) struct P028_data_struct : public PluginTaskData_base { struct bme280_calib_data @@ -107,6 +108,12 @@ struct P028_data_struct : public PluginTaskData_base { BME280_DEVICE = 0x60 }; + enum BMx_DetectMode : uint8_t { + Auto = 0u, + BME280 = BMx_ChipId::BME280_DEVICE, + BMP280 = BMx_ChipId::BMP280_DEVICE, + }; + enum BMx_state { BMx_Uninitialized = 0, BMx_Initialized, @@ -119,7 +126,7 @@ struct P028_data_struct : public PluginTaskData_base { P028_data_struct(uint8_t addr, float tempOffset); - P028_data_struct() = delete; + P028_data_struct() = delete; virtual ~P028_data_struct() = default; private: @@ -130,11 +137,11 @@ struct P028_data_struct : public PluginTaskData_base { public: - const __FlashStringHelper* getDeviceName() const; + static const __FlashStringHelper* getDeviceName(BMx_ChipId sensorID); - bool hasHumidity() const; + bool hasHumidity() const; - bool initialized() const; + bool initialized() const; private: From a99a6397d3be1d96d052570d9d707dbbed8a2bc9 Mon Sep 17 00:00:00 2001 From: Ton Huisman Date: Wed, 26 Jul 2023 21:59:49 +0200 Subject: [PATCH 2/8] [P028] Ignore humidity if Sensor model BMP280 is selected --- src/_P028_BME280.ino | 23 ++++++++++++---------- src/src/PluginStructs/P028_data_struct.cpp | 14 ++++++------- src/src/PluginStructs/P028_data_struct.h | 6 ++++-- 3 files changed, 24 insertions(+), 19 deletions(-) diff --git a/src/_P028_BME280.ino b/src/_P028_BME280.ino index 355e1d723e..becf7d9fd3 100644 --- a/src/_P028_BME280.ino +++ b/src/_P028_BME280.ino @@ -6,6 +6,7 @@ // ####################################################################################################### /** Changelog: + * 2023-07-26 tonhuisman: Ignore all humidity data (and log messages) if BMP280 Sensor model is selected * 2023-07-25 tonhuisman: Add setting to enable forcing the plugin into either BME280 or BMP280 mode, default is Auto-detect * Add changelog */ @@ -103,7 +104,8 @@ boolean Plugin_028(uint8_t function, struct EventStruct *event, String& string) { const float tempOffset = P028_TEMPERATURE_OFFSET / 10.0f; initPluginTaskData(event->TaskIndex, - new (std::nothrow) P028_data_struct(P028_I2C_ADDRESS, tempOffset)); + new (std::nothrow) P028_data_struct(P028_I2C_ADDRESS, tempOffset, + static_cast(P028_DETECTION_MODE))); P028_data_struct *P028_data = static_cast(getPluginTaskData(event->TaskIndex)); @@ -143,7 +145,7 @@ boolean Plugin_028(uint8_t function, struct EventStruct *event, String& string) if (nullptr != P028_data) { if (P028_data->sensorID != P028_data_struct::Unknown_DEVICE) { String detectedString = F("Detected: "); - detectedString += P028_data->getDeviceName(P028_data->sensorID); + detectedString += P028_data_struct::getDeviceName(P028_data->sensorID); addUnit(detectedString); } } @@ -156,7 +158,8 @@ boolean Plugin_028(uint8_t function, struct EventStruct *event, String& string) String offsetNote = F("Offset in units of 0.1 degree Celsius"); if (nullptr != P028_data) { - if (P028_data->hasHumidity()) { + if ((P028_data_struct::BMx_DetectMode::BMP280 != static_cast(P028_DETECTION_MODE)) && + P028_data->hasHumidity()) { offsetNote += F(" (also correct humidity)"); } } @@ -283,8 +286,8 @@ boolean Plugin_028(uint8_t function, struct EventStruct *event, String& string) const P028_data_struct::BMx_DetectMode detectMode = static_cast(P028_DETECTION_MODE); - if (((detectMode == P028_data_struct::BMx_DetectMode::Auto) && !P028_data->hasHumidity()) || - (detectMode == P028_data_struct::BMx_DetectMode::BMP280)) { + if (((P028_data_struct::BMx_DetectMode::Auto == detectMode) && !P028_data->hasHumidity()) || + (P028_data_struct::BMx_DetectMode::BMP280 == detectMode)) { // Patch the sensor type to output only the measured values. event->sensorType = Sensor_VType::SENSOR_TYPE_TEMP_EMPTY_BARO; } @@ -304,24 +307,24 @@ boolean Plugin_028(uint8_t function, struct EventStruct *event, String& string) String log; if (log.reserve(40)) { // Prevent re-allocation - log = P028_data->getDeviceName(P028_data->sensorID); + log = P028_data_struct::getDeviceName(P028_data->sensorID); log += F(": Address: "); log += formatToHex(P028_I2C_ADDRESS, 2); addLogMove(LOG_LEVEL_INFO, log); // addLogMove does also clear the string. - log = P028_data->getDeviceName(P028_data->sensorID); + log = P028_data_struct::getDeviceName(P028_data->sensorID); log += F(": Temperature: "); log += formatUserVarNoCheck(event->TaskIndex, 0); addLogMove(LOG_LEVEL_INFO, log); - if (P028_data->hasHumidity()) { - log = P028_data->getDeviceName(P028_data->sensorID); + if ((P028_data_struct::BMx_DetectMode::BMP280 != detectMode) && P028_data->hasHumidity()) { + log = P028_data_struct::getDeviceName(P028_data->sensorID); log += F(": Humidity: "); log += formatUserVarNoCheck(event->TaskIndex, 1); addLogMove(LOG_LEVEL_INFO, log); } - log = P028_data->getDeviceName(P028_data->sensorID); + log = P028_data_struct::getDeviceName(P028_data->sensorID); log += F(": Barometric Pressure: "); log += formatUserVarNoCheck(event->TaskIndex, 2); addLogMove(LOG_LEVEL_INFO, log); diff --git a/src/src/PluginStructs/P028_data_struct.cpp b/src/src/PluginStructs/P028_data_struct.cpp index be6f0689c0..8a4e2be395 100644 --- a/src/src/PluginStructs/P028_data_struct.cpp +++ b/src/src/PluginStructs/P028_data_struct.cpp @@ -9,8 +9,8 @@ // 1 second = 63% of the time needed to perform a measurement. # define P028_MEASUREMENT_TIMEOUT 1.587f -P028_data_struct::P028_data_struct(uint8_t addr, float tempOffset) : - i2cAddress(addr), temp_offset(tempOffset) {} +P028_data_struct::P028_data_struct(uint8_t addr, float tempOffset, BMx_DetectMode detectMode) : + i2cAddress(addr), temp_offset(tempOffset), _detectMode(detectMode) {} uint8_t P028_data_struct::get_config_settings() const { @@ -108,7 +108,7 @@ bool P028_data_struct::updateMeasurements(taskIndex_t task_index) { bool logAdded = false; # endif // ifndef LIMIT_BUILD_SIZE - if (hasHumidity()) { + if ((BMx_DetectMode::BMP280 != _detectMode) && hasHumidity()) { // Apply half of the temp offset, to correct the dew point offset. // The sensor is warmer than the surrounding air, which has effect on the perceived humidity. last_dew_temp_val = compute_dew_point_temp(last_temp_val + (temp_offset / 2.0f), last_hum_val); @@ -128,7 +128,7 @@ bool P028_data_struct::updateMeasurements(taskIndex_t task_index) { } # endif // ifndef LIMIT_BUILD_SIZE - if (hasHumidity()) { + if ((BMx_DetectMode::BMP280 != _detectMode) && hasHumidity()) { # ifndef LIMIT_BUILD_SIZE if (loglevelActiveFor(LOG_LEVEL_INFO)) { @@ -172,7 +172,7 @@ bool P028_data_struct::updateMeasurements(taskIndex_t task_index) { # ifndef LIMIT_BUILD_SIZE - if (hasHumidity()) { + if ((BMx_DetectMode::BMP280 != _detectMode) && hasHumidity()) { if (loglevelActiveFor(LOG_LEVEL_INFO)) { log += F(" dew point: "); log += last_dew_temp_val; @@ -273,7 +273,7 @@ void P028_data_struct::readCoefficients() calib.dig_P8 = I2C_readS16_LE_reg(i2cAddress, BMx280_REGISTER_DIG_P8); calib.dig_P9 = I2C_readS16_LE_reg(i2cAddress, BMx280_REGISTER_DIG_P9); - if (hasHumidity()) { + if ((BMx_DetectMode::BMP280 != _detectMode) && hasHumidity()) { calib.dig_H1 = I2C_read8_reg(i2cAddress, BMx280_REGISTER_DIG_H1); calib.dig_H2 = I2C_readS16_LE_reg(i2cAddress, BMx280_REGISTER_DIG_H2); calib.dig_H3 = I2C_read8_reg(i2cAddress, BMx280_REGISTER_DIG_H3); @@ -364,7 +364,7 @@ float P028_data_struct::readPressure() const float P028_data_struct::readHumidity() const { - if (!hasHumidity()) { + if (!hasHumidity() || (BMx_DetectMode::BMP280 == _detectMode)) { // No support for humidity return 0.0f; } diff --git a/src/src/PluginStructs/P028_data_struct.h b/src/src/PluginStructs/P028_data_struct.h index c23762b0d4..058f46ba85 100644 --- a/src/src/PluginStructs/P028_data_struct.h +++ b/src/src/PluginStructs/P028_data_struct.h @@ -124,8 +124,9 @@ struct P028_data_struct : public PluginTaskData_base { }; - P028_data_struct(uint8_t addr, - float tempOffset); + P028_data_struct(uint8_t addr, + float tempOffset, + BMx_DetectMode detectMode); P028_data_struct() = delete; virtual ~P028_data_struct() = default; @@ -197,6 +198,7 @@ struct P028_data_struct : public PluginTaskData_base { bme280_uncomp_data uncompensated; bme280_calib_data calib; + BMx_DetectMode _detectMode; unsigned long last_measurement = 0; From 9daceb776226cecaae1e01b056f9f0a29b0f1f8b Mon Sep 17 00:00:00 2001 From: Ton Huisman Date: Thu, 27 Jul 2023 22:27:43 +0200 Subject: [PATCH 3/8] [Devices] Add event PLUGIN_WEBFORM_LOAD_ALWAYS that is also called for remote data-feed devices --- src/src/DataTypes/ESPEasy_plugin_functions.h | 1 + src/src/Globals/Plugins.cpp | 4 +++- src/src/WebServer/DevicesPage.cpp | 8 ++++++-- 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/src/src/DataTypes/ESPEasy_plugin_functions.h b/src/src/DataTypes/ESPEasy_plugin_functions.h index f73fe81903..0a4c8c8c3a 100644 --- a/src/src/DataTypes/ESPEasy_plugin_functions.h +++ b/src/src/DataTypes/ESPEasy_plugin_functions.h @@ -60,6 +60,7 @@ #define PLUGIN_PROCESS_CONTROLLER_DATA 48 // Can be called from the controller to signal the plugin to generate (or handle) sending the data. #define PLUGIN_PRIORITY_INIT_ALL 49 // Pre-initialize all plugins that are set to PowerManager priority (not implemented in plugins) #define PLUGIN_PRIORITY_INIT 50 // Pre-initialize a singe plugins that is set to PowerManager priority +#define PLUGIN_WEBFORM_LOAD_ALWAYS 51 // Loaded *after* PLUGIN_WEBFORM_LOAD, also shown for remote data-feed devices diff --git a/src/src/Globals/Plugins.cpp b/src/src/Globals/Plugins.cpp index bc872c14cb..04b852e08a 100644 --- a/src/src/Globals/Plugins.cpp +++ b/src/src/Globals/Plugins.cpp @@ -630,6 +630,7 @@ bool PluginCall(uint8_t Function, struct EventStruct *event, String& str) case PLUGIN_INIT: case PLUGIN_EXIT: case PLUGIN_WEBFORM_LOAD: + case PLUGIN_WEBFORM_LOAD_ALWAYS: case PLUGIN_WEBFORM_LOAD_OUTPUT_SELECTOR: case PLUGIN_READ: case PLUGIN_GET_PACKED_RAW_DATA: @@ -654,7 +655,7 @@ bool PluginCall(uint8_t Function, struct EventStruct *event, String& str) // Only exception is when ErrorStateValues is needed. // Therefore only need to call LoadTaskSettings for those tasks with ErrorStateValues LoadTaskSettings(event->TaskIndex); - } else if (Function == PLUGIN_INIT || Function == PLUGIN_WEBFORM_LOAD) { + } else if (Function == PLUGIN_INIT || Function == PLUGIN_WEBFORM_LOAD || Function == PLUGIN_WEBFORM_LOAD_ALWAYS) { // LoadTaskSettings may call PLUGIN_GET_DEVICEVALUENAMES. LoadTaskSettings(event->TaskIndex); } @@ -824,6 +825,7 @@ bool PluginCall(uint8_t Function, struct EventStruct *event, String& str) if (Function == PLUGIN_GET_DEVICEVALUENAMES || Function == PLUGIN_WEBFORM_SAVE || Function == PLUGIN_WEBFORM_LOAD || + Function == PLUGIN_WEBFORM_LOAD_ALWAYS || Function == PLUGIN_SET_DEFAULTS || Function == PLUGIN_INIT_VALUE_RANGES || Function == PLUGIN_WEBFORM_SHOW_SERIAL_PARAMS diff --git a/src/src/WebServer/DevicesPage.cpp b/src/src/WebServer/DevicesPage.cpp index 7aa21808d2..8c7f7d9f70 100644 --- a/src/src/WebServer/DevicesPage.cpp +++ b/src/src/WebServer/DevicesPage.cpp @@ -970,10 +970,10 @@ void handle_devices_TaskSettingsPage(taskIndex_t taskIndex, uint8_t page) addFormSubHeader(F("Device Settings")); } + String webformLoadString; + struct EventStruct TempEvent(taskIndex); // add plugins content if (Settings.TaskDeviceDataFeed[taskIndex] == 0) { // only show additional config for local connected sensors - String webformLoadString; - struct EventStruct TempEvent(taskIndex); PluginCall(PLUGIN_WEBFORM_LOAD, &TempEvent, webformLoadString); if (webformLoadString.length() > 0) { @@ -982,6 +982,8 @@ void handle_devices_TaskSettingsPage(taskIndex_t taskIndex, uint8_t page) errorMessage += F(": Bug in PLUGIN_WEBFORM_LOAD, should not append to string, use addHtml() instead"); addHtmlError(errorMessage); } + + PluginCall(PLUGIN_WEBFORM_LOAD_ALWAYS, &TempEvent, webformLoadString); // Load settings also useful for remote-datafeed devices } else { #if FEATURE_ESPEASY_P2P @@ -1001,6 +1003,8 @@ void handle_devices_TaskSettingsPage(taskIndex_t taskIndex, uint8_t page) } addFormNote(F("0 = disable remote feed, 255 = broadcast")); // FIXME TD-er: Must verify if broadcast can be set. #endif + + PluginCall(PLUGIN_WEBFORM_LOAD_ALWAYS, &TempEvent, webformLoadString); // Load settings also useful for remote-datafeed devices } devicePage_show_output_data_type(taskIndex, DeviceIndex); From 13e6cbe5fd8f8b6ef52987ab93fa41451471113c Mon Sep 17 00:00:00 2001 From: Ton Huisman Date: Thu, 27 Jul 2023 22:29:18 +0200 Subject: [PATCH 4/8] [P028] Fix VType matching for remote data-feed, implement new WEBFORM_LOAD_ALWAYS event --- src/_P028_BME280.ino | 52 +++++++++++++++------- src/src/PluginStructs/P028_data_struct.cpp | 14 +++--- src/src/PluginStructs/P028_data_struct.h | 6 +-- 3 files changed, 46 insertions(+), 26 deletions(-) diff --git a/src/_P028_BME280.ino b/src/_P028_BME280.ino index becf7d9fd3..027bc2d4f8 100644 --- a/src/_P028_BME280.ino +++ b/src/_P028_BME280.ino @@ -6,6 +6,8 @@ // ####################################################################################################### /** Changelog: + * 2023-07-27 tonhuisman: Revert most below changes and implement PLUGIN_GET_DEVICEVTYPE so the P2P controller validates against the correct + * setting. Setting is only available if a remote data-feed is active, and offers BME280 and BMP280 options only. * 2023-07-26 tonhuisman: Ignore all humidity data (and log messages) if BMP280 Sensor model is selected * 2023-07-25 tonhuisman: Add setting to enable forcing the plugin into either BME280 or BMP280 mode, default is Auto-detect * Add changelog @@ -100,12 +102,26 @@ boolean Plugin_028(uint8_t function, struct EventStruct *event, String& string) break; } + case PLUGIN_GET_DEVICEVTYPE: + { + const P028_data_struct::BMx_DetectMode detectMode = static_cast(P028_DETECTION_MODE); + + // We want to configure this only when a remote data-feed is used + if ((Settings.TaskDeviceDataFeed[event->TaskIndex] != 0) && (P028_data_struct::BMx_DetectMode::BMP280 == detectMode)) { + // Patch the sensor type to output only the measured values, and/or match with a P2P remote sensor + event->sensorType = Sensor_VType::SENSOR_TYPE_TEMP_EMPTY_BARO; + event->idx = getValueCountFromSensorType(Sensor_VType::SENSOR_TYPE_TEMP_EMPTY_BARO); + } + + success = true; + break; + } + case PLUGIN_INIT: { const float tempOffset = P028_TEMPERATURE_OFFSET / 10.0f; initPluginTaskData(event->TaskIndex, - new (std::nothrow) P028_data_struct(P028_I2C_ADDRESS, tempOffset, - static_cast(P028_DETECTION_MODE))); + new (std::nothrow) P028_data_struct(P028_I2C_ADDRESS, tempOffset)); P028_data_struct *P028_data = static_cast(getPluginTaskData(event->TaskIndex)); @@ -165,21 +181,26 @@ boolean Plugin_028(uint8_t function, struct EventStruct *event, String& string) } addFormNote(offsetNote); - { + success = true; + break; + } + + case PLUGIN_WEBFORM_LOAD_ALWAYS: + { + if (Settings.TaskDeviceDataFeed[event->TaskIndex] != 0) { // We want to configure this *only* when a remote data-feed is used const __FlashStringHelper *detectOptionList[] = { - F("Auto"), P028_data_struct::getDeviceName(P028_data_struct::BMx_ChipId::BME280_DEVICE), P028_data_struct::getDeviceName(P028_data_struct::BMx_ChipId::BMP280_DEVICE), }; const int detectOptions[] = { - static_cast(P028_data_struct::BMx_DetectMode::Auto), static_cast(P028_data_struct::BMx_DetectMode::BME280), static_cast(P028_data_struct::BMx_DetectMode::BMP280), }; - addFormSelector(F("Sensor model"), F("det"), 3, detectOptionList, detectOptions, P028_DETECTION_MODE); - } + addFormSelector(F("Output values mode"), F("det"), 2, detectOptionList, detectOptions, P028_DETECTION_MODE); + addFormNote(F("'Auto' is the suggested setting, see documentation (i)")); - success = true; + success = true; + } break; } @@ -243,8 +264,11 @@ boolean Plugin_028(uint8_t function, struct EventStruct *event, String& string) P028_ALTITUDE = getFormItemInt(F("elev")); P028_TEMPERATURE_OFFSET = getFormItemInt(F("tempoffset")); P028_ERROR_STATE_OUTPUT = getFormItemInt(F("err")); - P028_DETECTION_MODE = getFormItemInt(F("det")); - success = true; + + if (Settings.TaskDeviceDataFeed[event->TaskIndex] != 0) { // We want to configure this only when a remote data-feed is used + P028_DETECTION_MODE = getFormItemInt(F("det")); + } + success = true; break; } case PLUGIN_ONCE_A_SECOND: @@ -284,12 +308,10 @@ boolean Plugin_028(uint8_t function, struct EventStruct *event, String& string) } else { P028_data->state = P028_data_struct::BMx_Values_read; - const P028_data_struct::BMx_DetectMode detectMode = static_cast(P028_DETECTION_MODE); - - if (((P028_data_struct::BMx_DetectMode::Auto == detectMode) && !P028_data->hasHumidity()) || - (P028_data_struct::BMx_DetectMode::BMP280 == detectMode)) { + if (!P028_data->hasHumidity()) { // Patch the sensor type to output only the measured values. event->sensorType = Sensor_VType::SENSOR_TYPE_TEMP_EMPTY_BARO; + event->idx = getValueCountFromSensorType(Sensor_VType::SENSOR_TYPE_TEMP_EMPTY_BARO); } UserVar[event->BaseVarIndex] = ExtraTaskSettings.checkAllowedRange(0, P028_data->last_temp_val); UserVar[event->BaseVarIndex + 1] = P028_data->last_hum_val; @@ -318,7 +340,7 @@ boolean Plugin_028(uint8_t function, struct EventStruct *event, String& string) log += formatUserVarNoCheck(event->TaskIndex, 0); addLogMove(LOG_LEVEL_INFO, log); - if ((P028_data_struct::BMx_DetectMode::BMP280 != detectMode) && P028_data->hasHumidity()) { + if (P028_data->hasHumidity()) { log = P028_data_struct::getDeviceName(P028_data->sensorID); log += F(": Humidity: "); log += formatUserVarNoCheck(event->TaskIndex, 1); diff --git a/src/src/PluginStructs/P028_data_struct.cpp b/src/src/PluginStructs/P028_data_struct.cpp index 8a4e2be395..be6f0689c0 100644 --- a/src/src/PluginStructs/P028_data_struct.cpp +++ b/src/src/PluginStructs/P028_data_struct.cpp @@ -9,8 +9,8 @@ // 1 second = 63% of the time needed to perform a measurement. # define P028_MEASUREMENT_TIMEOUT 1.587f -P028_data_struct::P028_data_struct(uint8_t addr, float tempOffset, BMx_DetectMode detectMode) : - i2cAddress(addr), temp_offset(tempOffset), _detectMode(detectMode) {} +P028_data_struct::P028_data_struct(uint8_t addr, float tempOffset) : + i2cAddress(addr), temp_offset(tempOffset) {} uint8_t P028_data_struct::get_config_settings() const { @@ -108,7 +108,7 @@ bool P028_data_struct::updateMeasurements(taskIndex_t task_index) { bool logAdded = false; # endif // ifndef LIMIT_BUILD_SIZE - if ((BMx_DetectMode::BMP280 != _detectMode) && hasHumidity()) { + if (hasHumidity()) { // Apply half of the temp offset, to correct the dew point offset. // The sensor is warmer than the surrounding air, which has effect on the perceived humidity. last_dew_temp_val = compute_dew_point_temp(last_temp_val + (temp_offset / 2.0f), last_hum_val); @@ -128,7 +128,7 @@ bool P028_data_struct::updateMeasurements(taskIndex_t task_index) { } # endif // ifndef LIMIT_BUILD_SIZE - if ((BMx_DetectMode::BMP280 != _detectMode) && hasHumidity()) { + if (hasHumidity()) { # ifndef LIMIT_BUILD_SIZE if (loglevelActiveFor(LOG_LEVEL_INFO)) { @@ -172,7 +172,7 @@ bool P028_data_struct::updateMeasurements(taskIndex_t task_index) { # ifndef LIMIT_BUILD_SIZE - if ((BMx_DetectMode::BMP280 != _detectMode) && hasHumidity()) { + if (hasHumidity()) { if (loglevelActiveFor(LOG_LEVEL_INFO)) { log += F(" dew point: "); log += last_dew_temp_val; @@ -273,7 +273,7 @@ void P028_data_struct::readCoefficients() calib.dig_P8 = I2C_readS16_LE_reg(i2cAddress, BMx280_REGISTER_DIG_P8); calib.dig_P9 = I2C_readS16_LE_reg(i2cAddress, BMx280_REGISTER_DIG_P9); - if ((BMx_DetectMode::BMP280 != _detectMode) && hasHumidity()) { + if (hasHumidity()) { calib.dig_H1 = I2C_read8_reg(i2cAddress, BMx280_REGISTER_DIG_H1); calib.dig_H2 = I2C_readS16_LE_reg(i2cAddress, BMx280_REGISTER_DIG_H2); calib.dig_H3 = I2C_read8_reg(i2cAddress, BMx280_REGISTER_DIG_H3); @@ -364,7 +364,7 @@ float P028_data_struct::readPressure() const float P028_data_struct::readHumidity() const { - if (!hasHumidity() || (BMx_DetectMode::BMP280 == _detectMode)) { + if (!hasHumidity()) { // No support for humidity return 0.0f; } diff --git a/src/src/PluginStructs/P028_data_struct.h b/src/src/PluginStructs/P028_data_struct.h index 058f46ba85..c23762b0d4 100644 --- a/src/src/PluginStructs/P028_data_struct.h +++ b/src/src/PluginStructs/P028_data_struct.h @@ -124,9 +124,8 @@ struct P028_data_struct : public PluginTaskData_base { }; - P028_data_struct(uint8_t addr, - float tempOffset, - BMx_DetectMode detectMode); + P028_data_struct(uint8_t addr, + float tempOffset); P028_data_struct() = delete; virtual ~P028_data_struct() = default; @@ -198,7 +197,6 @@ struct P028_data_struct : public PluginTaskData_base { bme280_uncomp_data uncompensated; bme280_calib_data calib; - BMx_DetectMode _detectMode; unsigned long last_measurement = 0; From 3080b47f647a94e73144f7311d1e11d0f8b1bd3a Mon Sep 17 00:00:00 2001 From: Ton Huisman Date: Sat, 29 Jul 2023 12:24:48 +0200 Subject: [PATCH 5/8] [P028] Update documentation --- docs/source/Plugin/P028.rst | 27 +++++++++++++++++++ docs/source/Plugin/P028_DataSource.png | Bin 0 -> 6811 bytes docs/source/Plugin/P028_OutputValuesMode.png | Bin 0 -> 13997 bytes src/_P028_BME280.ino | 1 - src/src/WebServer/DevicesPage.cpp | 2 +- 5 files changed, 28 insertions(+), 2 deletions(-) create mode 100644 docs/source/Plugin/P028_DataSource.png create mode 100644 docs/source/Plugin/P028_OutputValuesMode.png diff --git a/docs/source/Plugin/P028.rst b/docs/source/Plugin/P028.rst index c835fcea99..80c4c979d0 100644 --- a/docs/source/Plugin/P028.rst +++ b/docs/source/Plugin/P028.rst @@ -99,11 +99,38 @@ Single event with all values, Send to Controller and Interval settings are stand * **Interval** By default, Interval will be set to 60 sec. The minimum value allowed is 1 sec. + +Data Source +^^^^^^^^^^^ + +.. note:: The **Data Source** section is only available when the task is configured to receive data from a remote node! + +When using :ref:`c013_page` and having multiple ESP nodes using the same ``ESPEasy p2p UDP port`` (see the Tools/Advanced page, default: 8266, IANA registered), you can receive values from a remote node with the same plugin active. How to configure this is documented in the P2P Controller page. + +In a regular configuration, having the sensor connected locally, the plugin auto-detects what type of sensor is used, either BME280 or BMP280, and auto-adjusts the values supported. When receiving data from the P2P network, this 'setting' is verified and must match on the receiving end of the P2P connection. This information is not included in the P2P protocol data, so a configuration option is available, only shown if the task is configured to receive remote data. + +.. image:: P028_DataSource.png + +* **Remote Unit**: Shows the unit number and name the data is received from. + +* **Output values mode**: Allows selection of the sensor that is installed on the sending unit (node). + +.. image:: P028_OutputValuesMode.png + +Available options: + +* *BME280*: Sending node has a BME280 sensor, will provide the Temperature, Humidity and Pressure values. + +* *BMP280*: Sending node has a BMP280 sensor, will provide the Temperature and Pressure values. + + Values ^^^^^^ The measured values are available in ``Temperature``, ``Humidity`` and ``Pressure``. A formula can be set to recalculate. The number of decimals is by default set to 2, and can be set to 0 for ``Humidity`` and ``Pressure``, as no decimals are provided from the measurement. +.. note:: When a BMP280 sensor, that provides only ``Temperature`` and ``Pressure`` data, is connected, the ``Humidity`` value will still be visible, but show 0. + .. Commands available .. ^^^^^^^^^^^^^^^^^^ diff --git a/docs/source/Plugin/P028_DataSource.png b/docs/source/Plugin/P028_DataSource.png new file mode 100644 index 0000000000000000000000000000000000000000..4c7ac0bfe0e0b6a6c98ebe902781ffe5e5fd3269 GIT binary patch literal 6811 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 lWO4`Xi*gX_eMUzQkG!bwRJAIa&i>{6FzcIf?)P literal 0 HcmV?d00001 diff --git a/src/_P028_BME280.ino b/src/_P028_BME280.ino index 027bc2d4f8..08189ea94c 100644 --- a/src/_P028_BME280.ino +++ b/src/_P028_BME280.ino @@ -197,7 +197,6 @@ boolean Plugin_028(uint8_t function, struct EventStruct *event, String& string) static_cast(P028_data_struct::BMx_DetectMode::BMP280), }; addFormSelector(F("Output values mode"), F("det"), 2, detectOptionList, detectOptions, P028_DETECTION_MODE); - addFormNote(F("'Auto' is the suggested setting, see documentation (i)")); success = true; } diff --git a/src/src/WebServer/DevicesPage.cpp b/src/src/WebServer/DevicesPage.cpp index 8c7f7d9f70..3396e8ef0e 100644 --- a/src/src/WebServer/DevicesPage.cpp +++ b/src/src/WebServer/DevicesPage.cpp @@ -989,7 +989,7 @@ void handle_devices_TaskSettingsPage(taskIndex_t taskIndex, uint8_t page) #if FEATURE_ESPEASY_P2P // Show remote feed information. addFormSubHeader(F("Data Source")); - uint8_t remoteUnit = Settings.TaskDeviceDataFeed[taskIndex]; + const uint8_t remoteUnit = Settings.TaskDeviceDataFeed[taskIndex]; addFormNumericBox(F("Remote Unit"), F("RemoteUnit"), remoteUnit, 0, 255); if (remoteUnit != 255) { From 291c967bcdeb1bbf5314da465bf796828171a047 Mon Sep 17 00:00:00 2001 From: Ton Huisman Date: Sat, 29 Jul 2023 16:48:14 +0200 Subject: [PATCH 6/8] [Controllers] Show correct data (or nothing) for controllers without Host or Port settings --- src/_C013.cpp | 13 +++++++++++-- src/_C016.cpp | 6 ++++++ src/src/WebServer/ControllerPage.cpp | 23 +++++++++++++++-------- 3 files changed, 32 insertions(+), 10 deletions(-) diff --git a/src/_C013.cpp b/src/_C013.cpp index c8face7fde..4421213317 100644 --- a/src/_C013.cpp +++ b/src/_C013.cpp @@ -48,6 +48,7 @@ bool CPlugin_013(CPlugin::Function function, struct EventStruct *event, String& Protocol[protocolCount].usesTemplate = false; Protocol[protocolCount].usesAccount = false; Protocol[protocolCount].usesPassword = false; + Protocol[protocolCount].usesHost = false; Protocol[protocolCount].defaultPort = 8266; Protocol[protocolCount].usesID = false; Protocol[protocolCount].Custom = true; @@ -79,6 +80,12 @@ bool CPlugin_013(CPlugin::Function function, struct EventStruct *event, String& break; } + case CPlugin::Function::CPLUGIN_WEBFORM_SHOW_HOST_CONFIG: + { + string = F("-"); + break; + } + /* case CPlugin::Function::CPLUGIN_FLUSH: { @@ -157,7 +164,8 @@ void C013_SendUDPTaskData(struct EventStruct *event, uint8_t destUnit, uint8_t d // For example sending different sensor type data from one dummy to another is probably not going to work well dataReply.sensorType = event->getSensorType(); - const TaskValues_Data_t* taskValues = UserVar.getTaskValues_Data(event->TaskIndex); + const TaskValues_Data_t *taskValues = UserVar.getTaskValues_Data(event->TaskIndex); + if (taskValues != nullptr) { for (taskVarIndex_t x = 0; x < VARS_PER_TASK; ++x) { @@ -319,7 +327,8 @@ void C013_Receive(struct EventStruct *event) { const Sensor_VType sensorType = TempEvent.getSensorType(); if (dataReply.matchesSensorType(sensorType)) { - TaskValues_Data_t * taskValues = UserVar.getTaskValues_Data(dataReply.destTaskIndex); + TaskValues_Data_t *taskValues = UserVar.getTaskValues_Data(dataReply.destTaskIndex); + if (taskValues != nullptr) { for (taskVarIndex_t x = 0; x < VARS_PER_TASK; ++x) { diff --git a/src/_C016.cpp b/src/_C016.cpp index 27d2102305..48f0deb985 100644 --- a/src/_C016.cpp +++ b/src/_C016.cpp @@ -150,6 +150,12 @@ bool CPlugin_016(CPlugin::Function function, struct EventStruct *event, String& break; } + case CPlugin::Function::CPLUGIN_WEBFORM_SHOW_HOST_CONFIG: + { + string = F("-"); + break; + } + default: break; } diff --git a/src/src/WebServer/ControllerPage.cpp b/src/src/WebServer/ControllerPage.cpp index deb0faf15e..356f7b9c91 100644 --- a/src/src/WebServer/ControllerPage.cpp +++ b/src/src/WebServer/ControllerPage.cpp @@ -58,6 +58,7 @@ void handle_controllers() { // Otherwise the checksum will fail and settings will be saved too often. memset(&ControllerSettings, 0, sizeof(ControllerSettingsStruct)); ControllerSettings.reset(); + if (Settings.Protocol[controllerindex] != protocol) { // Protocol has changed. @@ -146,7 +147,7 @@ void handle_controllers_clearLoadDefaults(uint8_t controllerindex, ControllerSet struct EventStruct TempEvent; // Hand over the controller settings in the Data pointer, so the controller can set some defaults. - TempEvent.Data = (uint8_t*)(&ControllerSettings); + TempEvent.Data = (uint8_t *)(&ControllerSettings); if (Protocol[ProtocolIndex].usesTemplate) { String dummy; @@ -245,8 +246,8 @@ void handle_controllers_ShowAllControllersTable() html_TD(); addHtml(getCPluginNameFromCPluginID(Settings.Protocol[x])); html_TD(); + const protocolIndex_t ProtocolIndex = getProtocolIndex_from_ControllerIndex(x); { - const protocolIndex_t ProtocolIndex = getProtocolIndex_from_ControllerIndex(x); String hostDescription; CPluginCall(ProtocolIndex, CPlugin::Function::CPLUGIN_WEBFORM_SHOW_HOST_CONFIG, 0, hostDescription); @@ -258,7 +259,9 @@ void handle_controllers_ShowAllControllersTable() } html_TD(); - addHtmlInt(ControllerSettings.Port); + if ((INVALID_PROTOCOL_INDEX == ProtocolIndex) || (Protocol[ProtocolIndex].usesPort)) { + addHtmlInt(13 != Settings.Protocol[x] ? ControllerSettings.Port : Settings.UDPPort); // P2P exception + } } else { html_TD(3); @@ -329,6 +332,7 @@ void handle_controllers_ControllerSettingsPage(controllerIndex_t controllerindex addControllerParameterForm(ControllerSettings, controllerindex, ControllerSettingsStruct::CONTROLLER_IP); } } + if (Protocol[ProtocolIndex].usesPort) { addControllerParameterForm(ControllerSettings, controllerindex, ControllerSettingsStruct::CONTROLLER_PORT); } @@ -364,6 +368,7 @@ void handle_controllers_ControllerSettingsPage(controllerIndex_t controllerindex if (Protocol[ProtocolIndex].usesSampleSets) { addControllerParameterForm(ControllerSettings, controllerindex, ControllerSettingsStruct::CONTROLLER_SAMPLE_SET_INITIATOR); } + if (Protocol[ProtocolIndex].allowLocalSystemTime) { addControllerParameterForm(ControllerSettings, controllerindex, ControllerSettingsStruct::CONTROLLER_USE_LOCAL_SYSTEM_TIME); } @@ -386,7 +391,8 @@ void handle_controllers_ControllerSettingsPage(controllerIndex_t controllerindex { addControllerParameterForm(ControllerSettings, controllerindex, ControllerSettingsStruct::CONTROLLER_PASS); } - #if FEATURE_MQTT + # if FEATURE_MQTT + if (Protocol[ProtocolIndex].usesMQTT) { addTableSeparator(F("MQTT"), 2, 3); @@ -397,7 +403,7 @@ void handle_controllers_ControllerSettingsPage(controllerIndex_t controllerindex addFormNote(F("Updated on load of this page")); addControllerParameterForm(ControllerSettings, controllerindex, ControllerSettingsStruct::CONTROLLER_RETAINFLAG); } - #endif // if FEATURE_MQTT + # endif // if FEATURE_MQTT if (Protocol[ProtocolIndex].usesTemplate || Protocol[ProtocolIndex].usesMQTT) @@ -405,7 +411,8 @@ void handle_controllers_ControllerSettingsPage(controllerIndex_t controllerindex addControllerParameterForm(ControllerSettings, controllerindex, ControllerSettingsStruct::CONTROLLER_SUBSCRIBE); addControllerParameterForm(ControllerSettings, controllerindex, ControllerSettingsStruct::CONTROLLER_PUBLISH); } - #if FEATURE_MQTT + # if FEATURE_MQTT + if (Protocol[ProtocolIndex].usesMQTT) { addControllerParameterForm(ControllerSettings, controllerindex, ControllerSettingsStruct::CONTROLLER_LWT_TOPIC); @@ -415,7 +422,7 @@ void handle_controllers_ControllerSettingsPage(controllerIndex_t controllerindex addControllerParameterForm(ControllerSettings, controllerindex, ControllerSettingsStruct::CONTROLLER_WILL_RETAIN); addControllerParameterForm(ControllerSettings, controllerindex, ControllerSettingsStruct::CONTROLLER_CLEAN_SESSION); } - #endif // if FEATURE_MQTT + # endif // if FEATURE_MQTT } } @@ -448,4 +455,4 @@ void handle_controllers_ControllerSettingsPage(controllerIndex_t controllerindex html_end_form(); } -#endif // ifdef WEBSERVER_CONTROLLERS \ No newline at end of file +#endif // ifdef WEBSERVER_CONTROLLERS From c74434f9c4e99335e86d76cc4427ce32429d9f31 Mon Sep 17 00:00:00 2001 From: Ton Huisman Date: Mon, 7 Aug 2023 13:14:45 +0200 Subject: [PATCH 7/8] [Controllers] Code improvement --- src/src/WebServer/ControllerPage.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/src/WebServer/ControllerPage.cpp b/src/src/WebServer/ControllerPage.cpp index 356f7b9c91..2ac9f908f0 100644 --- a/src/src/WebServer/ControllerPage.cpp +++ b/src/src/WebServer/ControllerPage.cpp @@ -260,7 +260,7 @@ void handle_controllers_ShowAllControllersTable() html_TD(); if ((INVALID_PROTOCOL_INDEX == ProtocolIndex) || (Protocol[ProtocolIndex].usesPort)) { - addHtmlInt(13 != Settings.Protocol[x] ? ControllerSettings.Port : Settings.UDPPort); // P2P exception + addHtmlInt(13 == Settings.Protocol[x] ? Settings.UDPPort : ControllerSettings.Port); // P2P/C013 exception } } else { From f8b671773697265baa81a6d4373ea85ed638bc23 Mon Sep 17 00:00:00 2001 From: Ton Huisman Date: Fri, 18 Aug 2023 19:17:06 +0200 Subject: [PATCH 8/8] [P028] Fix merge conflict --- src/src/WebServer/ControllerPage.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/src/WebServer/ControllerPage.cpp b/src/src/WebServer/ControllerPage.cpp index 29bf063f1f..bb511a95cd 100644 --- a/src/src/WebServer/ControllerPage.cpp +++ b/src/src/WebServer/ControllerPage.cpp @@ -259,7 +259,7 @@ void handle_controllers_ShowAllControllersTable() html_TD(); if ((INVALID_PROTOCOL_INDEX == ProtocolIndex) || (Protocol[ProtocolIndex].usesPort)) { - addHtmlInt(13 == Settings.Protocol[x] ? Settings.UDPPort : ControllerSettings.Port); // P2P/C013 exception + addHtmlInt(13 == Settings.Protocol[x] ? Settings.UDPPort : ControllerSettings->Port); // P2P/C013 exception } } else {