diff --git a/docs/source/Plugin/P000_events.repl b/docs/source/Plugin/P000_events.repl
index 19f0093654..c76f996ef9 100644
--- a/docs/source/Plugin/P000_events.repl
+++ b/docs/source/Plugin/P000_events.repl
@@ -208,6 +208,25 @@
SendToHTTP,url.com,80,/report.php?hash=123abc456&t=[temp2#out]
endon
+ "
+ "
+ ``WiFi#Disconnected``
+ Triggered when the ESP has disconnected from Wi-Fi.
+ ","
+
+ .. code-block:: none
+
+ On WiFi#Disconnected Do
+ LongPulse,2,1,1,1,-1 // 0.5 Hz flashing of WiFi led
+ Endon
+
+ .. code-block:: none
+
+ On WiFi#Connected Do
+ GPIO,2,1 // Turn off WiFi led
+ SendToHTTP,url.com,80,/report.php?hash=123abc456&t=[temp2#out]
+ Endon
+
"
"
``WiFi#ChangedAccesspoint``
diff --git a/docs/source/conf.py b/docs/source/conf.py
index d985ebed48..2fea1c7071 100644
--- a/docs/source/conf.py
+++ b/docs/source/conf.py
@@ -180,6 +180,9 @@
# Choose Bootstrap version.
# Values: "3" (default) or "2" (in quotes)
'bootstrap_version': "5",
+
+ # Disable showing the sidebar. Defaults to 'false'
+ 'nosidebar': True,
}
# Add any paths that contain custom static files (such as style sheets) here,
diff --git a/src/_P167_Vindstyrka.ino b/src/_P167_Vindstyrka.ino
new file mode 100644
index 0000000000..b97fd6a47f
--- /dev/null
+++ b/src/_P167_Vindstyrka.ino
@@ -0,0 +1,561 @@
+#include "_Plugin_Helper.h"
+#ifdef USES_P167
+
+// #######################################################################################################
+// ######################## Plugin 167 IKEA Vindstyrka I2C Sensor (SEN5x) ############################
+// #######################################################################################################
+// 19-06-2023 AndiBaciu creation based upon https://github.com/RobTillaart/SHT2x
+
+# include "./src/PluginStructs/P167_data_struct.h"
+
+#define PLUGIN_167
+#define PLUGIN_ID_167 167 // plugin id
+#define PLUGIN_NAME_167 "Environment - Sensirion SEN5x (IKEA Vindstyrka)" // What will be dislpayed in the selection list
+#define PLUGIN_VALUENAME1_167 "Temperature" // variable output of the plugin. The label is in quotation marks
+#define PLUGIN_VALUENAME2_167 "Humidity" // multiple outputs are supporte
+#define PLUGIN_VALUENAME3_167 "tVOC" // multiple outputs are supported
+#define PLUGIN_VALUENAME4_167 "NOx" // multiple outputs are supported
+#define PLUGIN_VALUENAME5_167 "PM 1.0" // multiple outputs are supported
+#define PLUGIN_VALUENAME6_167 "PM 2.5" // multiple outputs are supported
+#define PLUGIN_VALUENAME7_167 "PM 4.0" // multiple outputs are supported
+#define PLUGIN_VALUENAME8_167 "PM 10.0" // multiple outputs are supported
+#define PLUGIN_VALUENAME9_167 "DewPoint" // multiple outputs are supported
+#define PLUGIN_DEFAULT_NAME_1 "IKEA_Vindstyrka"
+#define PLUGIN_DEFAULT_NAME_2 "Sensirion_SEN5x"
+
+// PIN/port configuration is stored in the following:
+// CONFIG_PIN1 - The first GPIO pin selected within the task
+// CONFIG_PIN2 - The second GPIO pin selected within the task
+// CONFIG_PIN3 - The third GPIO pin selected within the task
+// CONFIG_PORT - The port in case the device has multiple in/out pins
+//
+// Custom configuration is stored in the following:
+// PCONFIG(x)
+// x can be between 1 - 8 and can store values between -32767 - 32768 (16 bit)
+//
+// N.B. these are aliases for a longer less readable amount of code. See _Plugin_Helper.h
+//
+// PCONFIG_LABEL(x) is a function to generate a unique label used as HTML id to be able to match
+// returned values when saving a configuration.
+
+// Make accessing specific parameters more readable in the code
+// #define Pxxx_OUTPUT_TYPE_INDEX 2
+# define P167_I2C_ADDRESS PCONFIG(0)
+# define P167_I2C_ADDRESS_LABEL PCONFIG_LABEL(0)
+# define P167_MODEL PCONFIG(1)
+# define P167_MODEL_LABEL PCONFIG_LABEL(1)
+# define P167_MON_SCL_PIN PCONFIG(2)
+# define P167_MON_SCL_PIN_LABEL PCONFIG_LABEL(2)
+# define P167_QUERY1 PCONFIG(3)
+# define P167_QUERY2 PCONFIG(4)
+# define P167_QUERY3 PCONFIG(5)
+# define P167_QUERY4 PCONFIG(6)
+# define P167_SEN_FIRST PCONFIG(7)
+# define P167_SEN_ATTEMPT PCONFIG_LONG(1)
+
+
+# define P167_I2C_ADDRESS_DFLT 0x69
+# define P167_MON_SCL_PIN_DFLT 13
+# define P167_MODEL_DFLT 0 // Vindstyrka or SEN54
+# define P167_QUERY1_DFLT 0 // Temperature (C)
+# define P167_QUERY2_DFLT 1 // Humidity (%)
+# define P167_QUERY3_DFLT 5 // PM2.5 (ug/m3)
+# define P167_QUERY4_DFLT 2 // tVOC (index)
+
+
+# define P167_NR_OUTPUT_VALUES 4
+# define P167_NR_OUTPUT_OPTIONS 10
+# define P167_QUERY1_CONFIG_POS 3
+# define P167_MAX_ATTEMPT 3 // Number of tentative before declaring NAN value
+
+//# define LIMIT_BUILD_SIZE 1
+
+// These pointers may be used among multiple instances of the same plugin,
+// as long as the same settings are used.
+P167_data_struct *Plugin_167_SEN = nullptr;
+boolean Plugin_167_init = false;
+
+void IRAM_ATTR Plugin_167_interrupt();
+
+// Forward declaration helper functions
+const __FlashStringHelper* p167_getQueryString(uint8_t query);
+const __FlashStringHelper* p167_getQueryValueString(uint8_t query);
+unsigned int p167_getRegister(uint8_t query, uint8_t model);
+float p167_readVal(uint8_t query, uint8_t node, unsigned int model);
+
+
+// A plugin has to implement the following function
+
+boolean Plugin_167(uint8_t function, struct EventStruct *event, String& string)
+{
+ // function: reason the plugin was called
+ // event: ??add description here??
+ // string: ??add description here??
+
+ boolean success = false;
+
+ switch (function)
+ {
+
+
+ case PLUGIN_DEVICE_ADD:
+ {
+ // This case defines the device characteristics
+ Device[++deviceCount].Number = PLUGIN_ID_167;
+ Device[deviceCount].Type = DEVICE_TYPE_I2C;
+ Device[deviceCount].VType = Sensor_VType::SENSOR_TYPE_QUAD;
+ Device[deviceCount].Ports = 0;
+ Device[deviceCount].PullUpOption = true;
+ Device[deviceCount].InverseLogicOption = false;
+ Device[deviceCount].FormulaOption = true;
+ Device[deviceCount].ValueCount = P167_NR_OUTPUT_VALUES;
+ Device[deviceCount].SendDataOption = true;
+ Device[deviceCount].TimerOption = true;
+ Device[deviceCount].I2CNoDeviceCheck = true;
+ Device[deviceCount].GlobalSyncOption = true;
+ Device[deviceCount].PluginStats = true;
+ Device[deviceCount].OutputDataType = Output_Data_type_t::Simple;
+ break;
+ }
+
+
+ case PLUGIN_GET_DEVICENAME:
+ {
+ // return the device name
+ string = F(PLUGIN_NAME_167);
+ break;
+ }
+
+
+ case PLUGIN_GET_DEVICEVALUENAMES:
+ {
+ // called when the user opens the module configuration page
+ // it allows to add a new row for each output variable of the plugin
+ // For plugins able to choose output types, see P026_Sysinfo.ino.
+ for (uint8_t i = 0; i < VARS_PER_TASK; ++i)
+ {
+ if ( i < P167_NR_OUTPUT_VALUES)
+ {
+ uint8_t choice = PCONFIG(i + P167_QUERY1_CONFIG_POS);
+ safe_strncpy(ExtraTaskSettings.TaskDeviceValueNames[i], p167_getQueryValueString(choice), sizeof(ExtraTaskSettings.TaskDeviceValueNames[i]));
+ }
+ else
+ {
+ ZERO_FILL(ExtraTaskSettings.TaskDeviceValueNames[i]);
+ }
+ }
+ break;
+ }
+
+
+ case PLUGIN_SET_DEFAULTS:
+ {
+ // Set a default config here, which will be called when a plugin is assigned to a task.
+ P167_I2C_ADDRESS = P167_I2C_ADDRESS_DFLT;
+ P167_MODEL = P167_MODEL_DFLT;
+ P167_QUERY1 = P167_QUERY1_DFLT;
+ P167_QUERY2 = P167_QUERY2_DFLT;
+ P167_QUERY3 = P167_QUERY3_DFLT;
+ P167_QUERY4 = P167_QUERY4_DFLT;
+ P167_MON_SCL_PIN = P167_MON_SCL_PIN_DFLT;
+ P167_SEN_FIRST = 99;
+ success = true;
+ break;
+ }
+
+
+ # if FEATURE_I2C_GET_ADDRESS
+ case PLUGIN_I2C_GET_ADDRESS:
+ {
+ event->Par1 = P167_I2C_ADDRESS_DFLT;
+ success = true;
+ break;
+ }
+ # endif // if FEATURE_I2C_GET_ADDRESS
+
+
+ case PLUGIN_I2C_HAS_ADDRESS:
+ case PLUGIN_WEBFORM_SHOW_I2C_PARAMS:
+ {
+ const uint8_t i2cAddressValues[] = { P167_I2C_ADDRESS_DFLT };
+ if (function == PLUGIN_WEBFORM_SHOW_I2C_PARAMS)
+ {
+ if (P167_SEN_FIRST == event->TaskIndex) // If first SEN, serial config available
+ {
+ //addFormSelectorI2C(P167_I2C_ADDRESS_LABEL, 3, i2cAddressValues, P167_I2C_ADDRESS);
+ addFormSelectorI2C(F("i2c_addr"), 1, i2cAddressValues, P167_I2C_ADDRESS);
+ addFormNote(F("Vindstyrka, SEN54, SEN55 default i2c address: 0x69"));
+ }
+ }
+ else
+ {
+ success = intArrayContains(1, i2cAddressValues, event->Par1);
+ }
+ break;
+ }
+
+
+ case PLUGIN_WEBFORM_SHOW_GPIO_DESCR:
+ {
+ if (P167_SEN_FIRST == event->TaskIndex) // If first SEN, serial config available
+ {
+ if(P167_MODEL==0)
+ {
+ string = F("MonPin SCL: ");
+ string += formatGpioLabel(P167_MON_SCL_PIN, false);
+ }
+ }
+ success = true;
+ break;
+ }
+
+
+ case PLUGIN_WEBFORM_LOAD_OUTPUT_SELECTOR:
+ {
+ const __FlashStringHelper *options[P167_NR_OUTPUT_OPTIONS];
+ for (int i = 0; i < P167_NR_OUTPUT_OPTIONS; ++i)
+ {
+ options[i] = p167_getQueryString(i);
+ }
+ for (uint8_t i = 0; i < P167_NR_OUTPUT_VALUES; ++i)
+ {
+ const uint8_t pconfigIndex = i + P167_QUERY1_CONFIG_POS;
+ sensorTypeHelper_loadOutputSelector(event, pconfigIndex, i, P167_NR_OUTPUT_OPTIONS, options);
+ }
+ addFormNote(F("NOx is available ONLY on Sensirion SEN55 model"));
+ break;
+ }
+
+
+ case PLUGIN_WEBFORM_LOAD:
+ {
+ // this case defines what should be displayed on the web form, when this plugin is selected
+ // The user's selection will be stored in
+ // PCONFIG(x) (custom configuration)
+
+ if (Plugin_167_SEN == nullptr)
+ {
+ P167_SEN_FIRST = event->TaskIndex; // To detect if first SEN or not
+ }
+
+ if (P167_SEN_FIRST == event->TaskIndex) // If first SEN, serial config available
+ {
+ addHtml(F("
This SEN5x is the first. Its configuration of Pins will affect next SEN5x."));
+ addHtml(F("
If several SEN5x's foreseen, don't use other pins."));
+
+ const __FlashStringHelper *options_model[3] = { F("IKEA Vindstyrka"), F("Sensirion SEN54"), F("Sensirion SEN55")};
+
+ addFormSelector(F("Model Type"), P167_MODEL_LABEL, 3, options_model, nullptr, P167_MODEL);
+
+ if(P167_MODEL==0)
+ {
+ addFormPinSelect(PinSelectPurpose::Generic_input, F("MonPin SCL"), F("taskdevicepin3"), P167_MON_SCL_PIN);
+ addFormNote(F("Pin for monitoring i2c communication between Vindstyrka controller and SEN5x. (Only when Model - IKEA Vindstyrka is selected.)"));
+ }
+
+ if (Plugin_167_SEN != nullptr)
+ {
+ addRowLabel(F("Device info"));
+ String prodname;
+ String sernum;
+ uint8_t firmware;
+ Plugin_167_SEN->getEID(prodname, sernum, firmware);
+ String txt = F("ProdName: ");
+ txt += prodname;
+ txt += F(" Serial Number: ");
+ txt += sernum;
+ txt += F(" Firmware: ");
+ txt += String (firmware);
+ addHtml(txt);
+
+ addRowLabel(F("Device status"));
+ txt = F("Speed warning: ");
+ txt += String((bool)Plugin_167_SEN->getStatusInfo(sensor_speed));
+ txt += F(" , Auto Cleaning: ");
+ txt += String((bool)Plugin_167_SEN->getStatusInfo(sensor_autoclean));
+ txt += F(" , GAS Error: ");
+ txt += String((bool)Plugin_167_SEN->getStatusInfo(sensor_gas));
+ txt += F(" , RHT Error: ");
+ txt += String((bool)Plugin_167_SEN->getStatusInfo(sensor_rht));
+ txt += F(" , LASER Error: ");
+ txt += String((bool)Plugin_167_SEN->getStatusInfo(sensor_laser));
+ txt += F(" , FAN Error: ");
+ txt += String((bool)Plugin_167_SEN->getStatusInfo(sensor_fan));
+ addHtml(txt);
+
+ addRowLabel(F("Check (pass/fail/errCode)"));
+ txt = Plugin_167_SEN->getSuccCount();
+ txt += '/';
+ txt += Plugin_167_SEN->getErrCount();
+ txt += '/';
+ txt += Plugin_167_SEN->getErrCode();
+ addHtml(txt);
+
+ }
+ }
+ else
+ {
+ addHtml(F("
This SEN5x is the NOT the first. Model and Pins config are DISABLED. Configuration is available in the first SEN5x plugin."));
+ addHtml(F("
Only output value can be configured."));
+
+ //looking for FIRST task Named "IKEA_Vindstyrka or Sensirion_SEN5x"
+ //Cache.taskIndexName
+ uint8_t allready_defined=88;
+ if(P167_MODEL==0)
+ {
+ allready_defined=findTaskIndexByName(PLUGIN_DEFAULT_NAME_1);
+ }
+ else
+ {
+ allready_defined=findTaskIndexByName(PLUGIN_DEFAULT_NAME_2);
+ }
+ P167_SEN_FIRST = allready_defined;
+ }
+
+ success = true;
+ break;
+ }
+
+
+ case PLUGIN_WEBFORM_SAVE:
+ {
+ // this case defines the code to be executed when the form is submitted
+ // the plugin settings should be saved to PCONFIG(x)
+ // ping configuration should be read from CONFIG_PIN1 and stored
+
+ // Save output selector parameters.
+ for (uint8_t i = 0; i < P167_NR_OUTPUT_VALUES; ++i)
+ {
+ const uint8_t pconfigIndex = i + P167_QUERY1_CONFIG_POS;
+ const uint8_t choice = PCONFIG(pconfigIndex);
+ sensorTypeHelper_saveOutputSelector(event, pconfigIndex, i, p167_getQueryValueString(choice));
+ }
+ P167_MODEL = getFormItemInt(P167_MODEL_LABEL);
+ P167_I2C_ADDRESS = P167_I2C_ADDRESS_DFLT;
+ if(P167_MODEL==0)
+ P167_MON_SCL_PIN = getFormItemInt(F("taskdevicepin3"));
+ P167_SEN_FIRST = P167_SEN_FIRST;
+
+ if (P167_SEN_FIRST == event->TaskIndex) // For first task set default name
+ {
+ if(P167_MODEL==0)
+ {
+ strcpy(ExtraTaskSettings.TaskDeviceName, PLUGIN_DEFAULT_NAME_1); // populate default name.
+ }
+ else
+ {
+ strcpy(ExtraTaskSettings.TaskDeviceName, PLUGIN_DEFAULT_NAME_2); // populate default name.
+ }
+ }
+
+ Plugin_167_init = false; // Force device setup next time
+ success = true;
+ break;
+ }
+
+
+ case PLUGIN_INIT:
+ {
+ // this case defines code to be executed when the plugin is initialised
+ // This will fail if the set to be first taskindex is no longer enabled
+
+ if (P167_SEN_FIRST == event->TaskIndex) // If first SEN5x, config available
+ {
+ if (Plugin_167_SEN != nullptr)
+ {
+ delete Plugin_167_SEN;
+ Plugin_167_SEN = nullptr;
+ }
+
+ Plugin_167_SEN = new (std::nothrow) P167_data_struct();
+
+ if (Plugin_167_SEN != nullptr)
+ {
+ Plugin_167_SEN->setupModel(P167_MODEL);
+ Plugin_167_SEN->setupDevice(P167_I2C_ADDRESS);
+ if(P167_MODEL==0)
+ {
+ Plugin_167_SEN->setupMonPin(P167_MON_SCL_PIN);
+ pinMode(P167_MON_SCL_PIN, INPUT_PULLUP);
+ attachInterrupt(P167_MON_SCL_PIN, Plugin_167_interrupt, RISING);
+ }
+ Plugin_167_SEN->reset();
+ }
+ }
+
+ //UserVar[event->BaseVarIndex] = NAN;
+ //UserVar[event->BaseVarIndex + 1] = NAN;
+ //UserVar[event->BaseVarIndex + 2] = NAN;
+ //UserVar[event->BaseVarIndex + 3] = NAN;
+ UserVar.setFloat(event->BaseVarIndex, 0, NAN);
+ UserVar.setFloat(event->BaseVarIndex, 1, NAN);
+ UserVar.setFloat(event->BaseVarIndex, 2, NAN);
+ UserVar.setFloat(event->BaseVarIndex, 3, NAN);
+
+ success = true;
+ Plugin_167_init = true;
+
+ break;
+ }
+
+
+ case PLUGIN_EXIT:
+ {
+ if (P167_SEN_FIRST == event->TaskIndex) // If first SEN5x, config available
+ {
+ if (Plugin_167_SEN != nullptr)
+ {
+ if(P167_MODEL==0)
+ {
+ Plugin_167_SEN->disableInterrupt_monpin();
+ }
+ delete Plugin_167_SEN;
+ Plugin_167_SEN = nullptr;
+ }
+ }
+
+ success = true;
+ break;
+ }
+
+
+ case PLUGIN_READ:
+ {
+ // code to be executed to read data
+ // It is executed according to the delay configured on the device configuration page, only once
+
+ if(event->TaskIndex!=P167_SEN_FIRST)
+ {
+ //All the DATA are in the first task of IKEA_Vindstyrka
+ //so all you have to do is to load this data in the current taskindex data
+ initPluginTaskData(event->TaskIndex, getPluginTaskData(P167_SEN_FIRST) );
+ }
+
+ if (nullptr != Plugin_167_SEN)
+ {
+ if (Plugin_167_SEN->inError())
+ {
+ //UserVar[event->BaseVarIndex] = NAN;
+ //UserVar[event->BaseVarIndex + 1] = NAN;
+ //UserVar[event->BaseVarIndex + 2] = NAN;
+ //UserVar[event->BaseVarIndex + 3] = NAN;
+ UserVar.setFloat(event->BaseVarIndex, 0, NAN);
+ UserVar.setFloat(event->BaseVarIndex, 1, NAN);
+ UserVar.setFloat(event->BaseVarIndex, 2, NAN);
+ UserVar.setFloat(event->BaseVarIndex, 3, NAN);
+ addLog(LOG_LEVEL_ERROR, F("Vindstyrka / SEN5X: in Error!"));
+ }
+ else
+ {
+ if(event->TaskIndex==P167_SEN_FIRST)
+ {
+ Plugin_167_SEN->startMeasurements(); // getting ready for another read cycle
+ }
+
+ //UserVar[event->BaseVarIndex] = Plugin_167_SEN->getRequestedValue(P167_QUERY1);
+ //UserVar[event->BaseVarIndex + 1] = Plugin_167_SEN->getRequestedValue(P167_QUERY2);
+ //UserVar[event->BaseVarIndex + 2] = Plugin_167_SEN->getRequestedValue(P167_QUERY3);
+ //UserVar[event->BaseVarIndex + 3] = Plugin_167_SEN->getRequestedValue(P167_QUERY4);
+ UserVar.setFloat(event->BaseVarIndex, 0, Plugin_167_SEN->getRequestedValue(P167_QUERY1));
+ UserVar.setFloat(event->BaseVarIndex, 1, Plugin_167_SEN->getRequestedValue(P167_QUERY2));
+ UserVar.setFloat(event->BaseVarIndex, 2, Plugin_167_SEN->getRequestedValue(P167_QUERY3));
+ UserVar.setFloat(event->BaseVarIndex, 3, Plugin_167_SEN->getRequestedValue(P167_QUERY4));
+ }
+ }
+
+ success = true;
+ break;
+ }
+
+
+ case PLUGIN_ONCE_A_SECOND:
+ {
+ // code to be executed once a second. Tasks which do not require fast response can be added here
+ success = true;
+ }
+
+
+ case PLUGIN_TEN_PER_SECOND:
+ {
+ // code to be executed 10 times per second. Tasks which require fast response can be added here
+ // be careful on what is added here. Heavy processing will result in slowing the module down!
+ success = true;
+ }
+
+
+ case PLUGIN_FIFTY_PER_SECOND:
+ {
+ // code to be executed 10 times per second. Tasks which require fast response can be added here
+ // be careful on what is added here. Heavy processing will result in slowing the module down!
+ if(event->TaskIndex==P167_SEN_FIRST)
+ {
+ if (nullptr != Plugin_167_SEN)
+ {
+ Plugin_167_SEN->monitorSCL(); // Vind / SEN5X FSM evaluation
+ Plugin_167_SEN->update();
+ }
+ }
+ success = true;
+ }
+ } // switch
+
+ return success;
+} // function
+
+
+
+/// @brief
+/// @param query
+/// @return
+const __FlashStringHelper* p167_getQueryString(uint8_t query)
+{
+ switch(query)
+ {
+ case 0: return F("Temperature (C)");
+ case 1: return F("Humidity (% RH)");
+ case 2: return F("tVOC (VOC index)");
+ case 3: return F("NOx (NOx index)");
+ case 4: return F("PM 1.0 (ug/m3)");
+ case 5: return F("PM 2.5 (ug/m3)");
+ case 6: return F("PM 4.0 (ug/m3)");
+ case 7: return F("PM 10.0 (ug/m3)");
+ case 8: return F("DewPoint (C)");
+ }
+ return F("");
+}
+
+/// @brief
+/// @param query
+/// @return
+const __FlashStringHelper* p167_getQueryValueString(uint8_t query)
+{
+ switch(query)
+ {
+ case 0: return F("Temperature");
+ case 1: return F("Humidity");
+ case 2: return F("tVOC");
+ case 3: return F("NOx");
+ case 4: return F("PM1p0");
+ case 5: return F("PM2p5");
+ case 6: return F("PM4p0");
+ case 7: return F("PM10p0");
+ case 8: return F("DewPoint");
+ }
+ return F("");
+}
+
+
+// When using interrupts we have to call the library entry point
+// whenever an interrupt is triggered
+void IRAM_ATTR Plugin_167_interrupt()
+{
+ //addLog(LOG_LEVEL_ERROR, F("********* SEN5X: interrupt apear!"));
+ if (Plugin_167_SEN)
+ {
+ Plugin_167_SEN->checkPin_interrupt();
+ }
+}
+
+
+
+#endif //USES_P167
\ No newline at end of file
diff --git a/src/src/DataStructs/EthernetEventData.cpp b/src/src/DataStructs/EthernetEventData.cpp
index 62acc93bf0..008320e9ea 100644
--- a/src/src/DataStructs/EthernetEventData.cpp
+++ b/src/src/DataStructs/EthernetEventData.cpp
@@ -3,6 +3,7 @@
#if FEATURE_ETHERNET
#include "../ESPEasyCore/ESPEasy_Log.h"
+#include "../Globals/Settings.h"
#include "../Helpers/Networking.h"
#include
@@ -189,20 +190,9 @@ void EthernetEventData_t::markConnected() {
#endif
#if FEATURE_USE_IPV6
- ETH.enableIPv6(true);
- /*
- // workaround for the race condition in LWIP, see https://github.com/espressif/arduino-esp32/pull/9016#discussion_r1451774885
- {
- uint32_t i = 5; // try 5 times only
- while (esp_netif_create_ip6_linklocal(ETH.netif()) != ESP_OK) {
- delay(1);
- if (i-- == 0) {
-// addLog(LOG_LEVEL_ERROR, ">>>> HELP");
- break;
- }
- }
+ if (Settings.EnableIPv6()) {
+ ETH.enableIPv6(true);
}
- */
#endif
}
diff --git a/src/src/DataStructs/FactoryDefault_WiFi_NVS.cpp b/src/src/DataStructs/FactoryDefault_WiFi_NVS.cpp
index 6b40da5175..b97559841d 100644
--- a/src/src/DataStructs/FactoryDefault_WiFi_NVS.cpp
+++ b/src/src/DataStructs/FactoryDefault_WiFi_NVS.cpp
@@ -28,6 +28,7 @@ void FactoryDefault_WiFi_NVS::fromSettings() {
bits.WaitWiFiConnect = Settings.WaitWiFiConnect();
bits.SDK_WiFi_autoreconnect = Settings.SDK_WiFi_autoreconnect();
bits.HiddenSSID_SlowConnectPerBSSID = Settings.HiddenSSID_SlowConnectPerBSSID();
+ bits.EnableIPv6 = Settings.EnableIPv6();
}
void FactoryDefault_WiFi_NVS::applyToSettings() const {
@@ -43,6 +44,7 @@ void FactoryDefault_WiFi_NVS::applyToSettings() const {
Settings.WaitWiFiConnect(bits.WaitWiFiConnect);
Settings.SDK_WiFi_autoreconnect(bits.SDK_WiFi_autoreconnect);
Settings.HiddenSSID_SlowConnectPerBSSID(bits.HiddenSSID_SlowConnectPerBSSID);
+ Settings.EnableIPv6(bits.EnableIPv6);
}
struct FactoryDefault_WiFi_NVS_securityPrefs {
diff --git a/src/src/DataStructs/FactoryDefault_WiFi_NVS.h b/src/src/DataStructs/FactoryDefault_WiFi_NVS.h
index f184abdd7b..59a8d3b92c 100644
--- a/src/src/DataStructs/FactoryDefault_WiFi_NVS.h
+++ b/src/src/DataStructs/FactoryDefault_WiFi_NVS.h
@@ -40,8 +40,9 @@ class FactoryDefault_WiFi_NVS {
uint64_t WaitWiFiConnect : 1;
uint64_t SDK_WiFi_autoreconnect : 1;
uint64_t HiddenSSID_SlowConnectPerBSSID : 1;
+ uint64_t EnableIPv6 : 1;
- uint64_t unused : 52;
+ uint64_t unused : 51;
} bits;
uint64_t data{};
diff --git a/src/src/DataStructs/NodeStruct.cpp b/src/src/DataStructs/NodeStruct.cpp
index 0bc543662c..9cd1278987 100644
--- a/src/src/DataStructs/NodeStruct.cpp
+++ b/src/src/DataStructs/NodeStruct.cpp
@@ -62,7 +62,9 @@ bool NodeStruct::validate(const IPAddress& remoteIP) {
#if FEATURE_USE_IPV6
// Check if we're in the same global subnet
- if (hasIPv6_mac_based_link_global && remoteIP.type() == IPv6) {
+ if (Settings.EnableIPv6() &&
+ hasIPv6_mac_based_link_global &&
+ remoteIP.type() == IPv6) {
const IPAddress this_global = NetworkGlobalIP6();
// Check first 64 bit to see if we're in the same global scope
for (int i = 0; i < 8 && hasIPv6_mac_based_link_global; ++i) {
@@ -151,7 +153,7 @@ IPAddress NodeStruct::IP() const {
#if FEATURE_USE_IPV6
IPAddress NodeStruct::IPv6_link_local(bool stripZone) const
{
- if (hasIPv6_mac_based_link_local) {
+ if (Settings.EnableIPv6() && hasIPv6_mac_based_link_local) {
// Base IPv6 on MAC address
IPAddress ipv6;
if (IPv6_link_local_from_MAC(sta_mac, ipv6)) {
@@ -166,7 +168,7 @@ IPAddress NodeStruct::IPv6_link_local(bool stripZone) const
IPAddress NodeStruct::IPv6_global() const
{
- if (hasIPv6_mac_based_link_global) {
+ if (Settings.EnableIPv6() && hasIPv6_mac_based_link_global) {
// Base IPv6 on MAC address
IPAddress ipv6;
if (IPv6_global_from_MAC(sta_mac, ipv6)) {
@@ -177,6 +179,7 @@ IPAddress NodeStruct::IPv6_global() const
}
bool NodeStruct::hasIPv6() const {
+ if (!Settings.EnableIPv6()) return false;
return hasIPv6_mac_based_link_local ||
hasIPv6_mac_based_link_global;
}
diff --git a/src/src/DataStructs/SettingsStruct.h b/src/src/DataStructs/SettingsStruct.h
index c66800376f..17d2550891 100644
--- a/src/src/DataStructs/SettingsStruct.h
+++ b/src/src/DataStructs/SettingsStruct.h
@@ -188,6 +188,9 @@ class SettingsStruct_tmpl
bool HiddenSSID_SlowConnectPerBSSID() const { return !VariousBits_2.HiddenSSID_SlowConnectPerBSSID; }
void HiddenSSID_SlowConnectPerBSSID(bool value) { VariousBits_2.HiddenSSID_SlowConnectPerBSSID = !value; }
+ bool EnableIPv6() const { return !VariousBits_2.EnableIPv6; }
+ void EnableIPv6(bool value) { VariousBits_2.EnableIPv6 = !value; }
+
// Use Espressif's auto reconnect.
bool SDK_WiFi_autoreconnect() const { return VariousBits_2.SDK_WiFi_autoreconnect; }
void SDK_WiFi_autoreconnect(bool value) { VariousBits_2.SDK_WiFi_autoreconnect = value; }
@@ -523,8 +526,8 @@ class SettingsStruct_tmpl
uint32_t SDK_WiFi_autoreconnect : 1; // Bit 01
uint32_t DisableRulesCodeCompletion : 1; // Bit 02
uint32_t HiddenSSID_SlowConnectPerBSSID : 1; // Bit 03 // inverted
- uint32_t DisableSaveConfigAsTar : 1; // Bit 04
- uint32_t unused_05 : 1; // Bit 05
+ uint32_t EnableIPv6 : 1; // Bit 04 // inverted
+ uint32_t DisableSaveConfigAsTar : 1; // Bit 05
uint32_t unused_06 : 1; // Bit 06
uint32_t unused_07 : 1; // Bit 07
uint32_t unused_08 : 1; // Bit 08
diff --git a/src/src/DataStructs/WiFiEventData.cpp b/src/src/DataStructs/WiFiEventData.cpp
index f84cdb8f4c..48906d5dc6 100644
--- a/src/src/DataStructs/WiFiEventData.cpp
+++ b/src/src/DataStructs/WiFiEventData.cpp
@@ -3,6 +3,7 @@
#include "../ESPEasyCore/ESPEasy_Log.h"
#include "../Globals/RTC.h"
+#include "../Globals/Settings.h"
#include "../Globals/WiFi_AP_Candidates.h"
#include "../Helpers/ESPEasy_Storage.h"
@@ -236,16 +237,8 @@ void WiFiEventData_t::markConnected(const String& ssid, const uint8_t bssid[6],
}
}
#if FEATURE_USE_IPV6
- WiFi.enableIPv6(true);
- // workaround for the race condition in LWIP, see https://github.com/espressif/arduino-esp32/pull/9016#discussion_r1451774885
- {
- uint32_t i = 5; // try 5 times only
- while (esp_netif_create_ip6_linklocal(get_esp_interface_netif(ESP_IF_WIFI_STA)) != ESP_OK) {
- delay(1);
- if (i-- == 0) {
- break;
- }
- }
+ if (Settings.EnableIPv6()) {
+ WiFi.enableIPv6(true);
}
#endif
}
diff --git a/src/src/ESPEasyCore/ESPEasyEth.cpp b/src/src/ESPEasyCore/ESPEasyEth.cpp
index ab0da4168c..5add9a198c 100644
--- a/src/src/ESPEasyCore/ESPEasyEth.cpp
+++ b/src/src/ESPEasyCore/ESPEasyEth.cpp
@@ -169,7 +169,9 @@ bool ETHConnectRelaxed() {
(eth_clock_mode_t)Settings.ETH_Clock_Mode);
#else
#if FEATURE_USE_IPV6
- ETH.enableIPv6(true);
+ if (Settings.EnableIPv6()) {
+ ETH.enableIPv6(true);
+ }
#endif
if (isSPI_EthernetType(Settings.ETH_Phy_Type)) {
diff --git a/src/src/ESPEasyCore/ESPEasyWifi.cpp b/src/src/ESPEasyCore/ESPEasyWifi.cpp
index 46f88667d0..a1a1340d2c 100644
--- a/src/src/ESPEasyCore/ESPEasyWifi.cpp
+++ b/src/src/ESPEasyCore/ESPEasyWifi.cpp
@@ -494,7 +494,9 @@ void AttemptWiFiConnect() {
const String key = WiFi_AP_CandidatesList::get_key(candidate.index);
#if FEATURE_USE_IPV6
- WiFi.enableIPv6(true);
+ if (Settings.EnableIPv6()) {
+ WiFi.enableIPv6(true);
+ }
#endif
if ((Settings.HiddenSSID_SlowConnectPerBSSID() || !candidate.bits.isHidden)
diff --git a/src/src/Helpers/StringProvider.cpp b/src/src/Helpers/StringProvider.cpp
index bad6887569..1d6eee9d81 100644
--- a/src/src/Helpers/StringProvider.cpp
+++ b/src/src/Helpers/StringProvider.cpp
@@ -197,6 +197,10 @@ const __FlashStringHelper * getLabel(LabelType::Enum label) {
case LabelType::CONNECT_HIDDEN_SSID: return F("Include Hidden SSID");
case LabelType::HIDDEN_SSID_SLOW_CONNECT: return F("Hidden SSID Slow Connect");
case LabelType::SDK_WIFI_AUTORECONNECT: return F("Enable SDK WiFi Auto Reconnect");
+#if FEATURE_USE_IPV6
+ case LabelType::ENABLE_IPV6: return F("Enable IPv6");
+#endif
+
case LabelType::BUILD_DESC: return F("Build");
case LabelType::GIT_BUILD: return F("Git Build");
@@ -515,6 +519,10 @@ String getValue(LabelType::Enum label) {
case LabelType::CONNECT_HIDDEN_SSID: return jsonBool(Settings.IncludeHiddenSSID());
case LabelType::HIDDEN_SSID_SLOW_CONNECT: return jsonBool(Settings.HiddenSSID_SlowConnectPerBSSID());
case LabelType::SDK_WIFI_AUTORECONNECT: return jsonBool(Settings.SDK_WiFi_autoreconnect());
+#if FEATURE_USE_IPV6
+ case LabelType::ENABLE_IPV6: return jsonBool(Settings.EnableIPv6());
+#endif
+
case LabelType::BUILD_DESC: return getSystemBuildString();
case LabelType::GIT_BUILD:
diff --git a/src/src/Helpers/StringProvider.h b/src/src/Helpers/StringProvider.h
index bb76393487..adae736886 100644
--- a/src/src/Helpers/StringProvider.h
+++ b/src/src/Helpers/StringProvider.h
@@ -145,6 +145,9 @@ struct LabelType {
HIDDEN_SSID_SLOW_CONNECT,
CONNECT_HIDDEN_SSID,
SDK_WIFI_AUTORECONNECT,
+#if FEATURE_USE_IPV6
+ ENABLE_IPV6,
+#endif
BUILD_DESC,
GIT_BUILD,
diff --git a/src/src/PluginStructs/P167_data_struct.cpp b/src/src/PluginStructs/P167_data_struct.cpp
new file mode 100644
index 0000000000..c9bc7d5a9e
--- /dev/null
+++ b/src/src/PluginStructs/P167_data_struct.cpp
@@ -0,0 +1,1402 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// P167 device class for IKEA Vindstyrka SEN54 temperature , humidity and air quality sensors
+// See datasheet https://sensirion.com/media/documents/6791EFA0/62A1F68F/Sensirion_Datasheet_Environmental_Node_SEN5x.pdf
+// and info about extra request https://sensirion.com/media/documents/2B6FC1F3/6409E74A/PS_AN_Read_RHT_VOC_and_NOx_RAW_signals_D1.pdf
+// Based upon code from Rob Tillaart, Viktor Balint, https://github.com/RobTillaart/SHT2x
+// Rewritten and adapted for ESPeasy by andibaciu
+// 2023-06-20 Initial version by andibaciu
+//////////////////////////////////////////////////////////////////////////////////////////////////
+
+#include "../PluginStructs/P167_data_struct.h"
+#include "../ESPEasyCore/ESPEasyGPIO.h"
+
+#include
+
+ #ifndef CORE_POST_3_0_0
+ #ifdef ESP8266
+ #define IRAM_ATTR ICACHE_RAM_ATTR
+ #endif
+ #endif
+
+#ifdef USES_P167
+
+
+#define P167_START_MEAS 0x0021 // Start measurement command
+#define P167_START_MEAS_RHT_GAS 0x0037 // Start measurement RHT/Gas command
+#define P167_STOP_MEAS 0x0104 // Stop measurement command
+#define P167_READ_DATA_RDY_FLAG 0x0202 // Read Data Ready Flag command
+#define P167_READ_MEAS 0x03C4 // Read measurement command
+#define P167_R_W_TEMP_COMP_PARAM 0x60B2 // Read/Write Temperature Compensation Parameters command
+#define P167_R_W_TWARM_START_PARAM 0x60C6 // Read/Write Warm Start Parameters command
+#define P167_R_W_VOC_ALG_PARAM 0x60D0 // Read/Write VOC Algorithm Tuning Parameters command
+#define P167_R_W_NOX_ALG_PARAM 0x60E1 // Read/Write NOx Algorithm Tuning Parameters command
+#define P167_R_W_RH_T_ACC_Mode 0x60F7 // Read/Write RH/T Acceleration Mode command
+#define P167_R_W_VOC_ALG_STATE 0x6181 // Read/Write VOC Algorithm State command
+#define P167_START_FAN_CLEAN 0x5607 // Start fan cleaning command
+#define P167_R_W_AUTOCLEN_PARAM 0x8004 // Read/Write Autocleaning Interval Parameters command
+#define P167_READ_PROD_NAME 0xD014 // Read Product Name command
+#define P167_READ_SERIAL_NO 0xD033 // Read Serial Number command
+#define P167_READ_FIRM_VER 0xD100 // Read Firmware Version command
+#define P167_READ_DEVICE_STATUS 0xD206 // Read Device Status command
+#define P167_CLEAR_DEVICE_STATUS 0xD210 // Clear Device Status command
+#define P167_RESET_DEVICE 0xD304 // Reset Device command
+#define P167_READ_RAW_MEAS 0x03D2 // Read relative humidity and temperature
+ // which are not compensated for temperature offset, and the
+ // VOC and NOx raw signals (proportional to the logarithm of the
+ // resistance of the MOX layer). It returns 4x2 bytes (+ 1 CRC
+ // byte each) command (see second datasheet fron header for more info)
+#define P167_READ_RAW_MYS_MEAS 0x03F5 // Read relative humidity and temperature and MYSTERY word (probably signed offset temperature)
+
+
+#define P167_START_MEAS_DELAY 50 // Timeout value for start measurement command [ms]
+#define P167_START_MEAS_RHT_GAS_DELAY 50 // Timeout value for start measurement RHT/Gas command [ms]
+#define P167_STOP_MEAS_DELAY 200 // Timeout value for start measurement command [ms]
+#define P167_READ_DATA_RDY_FLAG_DELAY 20 // Timeout value for read data ready flag command [ms]
+#define P167_READ_MEAS_DELAY 20 // Timeout value for read measurement command [ms]
+#define P167_R_W_TEMP_COMP_PARAM_DELAY 20 // Timeout value for read/write temperature compensation parameters command [ms]
+#define P167_R_W_WARM_START_PARAM_DELAY 20 // Timeout value for read/write warm start parameters command [ms]
+#define P167_R_W_VOC_ALG_PARAM_DELAY 20 // Timeout value for read/write VOC algorithm tuning parameters command [ms]
+#define P167_R_W_NOX_ALG_PARAM_DELAY 20 // Timeout value for read/write NOx algorithm tuning parameters command [ms]
+#define P167_R_W_RH_T_ACC_MODE_DELAY 20 // Timeout value for read/write RH/T acceleration mode command [ms]
+#define P167_R_W_VOC_ALG_STATE_DELAY 20 // Timeout value for read/write VOC algorithm State command [ms]
+#define P167_START_FAN_CLEAN_DELAY 20 // Timeout value for start fan cleaning command [ms]
+#define P167_R_W_AUTOCLEN_PARAM_DELAY 20 // Timeout value for read/write autoclean interval parameters command [ms]
+#define P167_READ_PROD_NAME_DELAY 20 // Timeout value for read product name command [ms]
+#define P167_READ_SERIAL_NO_DELAY 20 // Timeout value for read serial number command [ms]
+#define P167_READ_FIRM_VER_DELAY 20 // Timeout value for read firmware version command [ms]
+#define P167_READ_DEVICE_STATUS_DELAY 20 // Timeout value for read device status command [ms]
+#define P167_CLEAR_DEVICE_STATUS_DELAY 20 // Timeout value for clear device status command [ms]
+#define P167_RESET_DEVICE_DELAY 100 // Timeout value for reset device command [ms]
+#define P167_READ_RAW_MEAS_DELAY 20 // Timeout value for read raw temp and humidity command [ms]
+
+
+#define P167_MAX_RETRY 250 // Give up after amount of retries befoe going to error
+
+
+#define SCL_MONITOR_PIN 13 //pin13 as monitor scl i2c
+
+
+//////////////////////////////////////////////////////////////////////////////////////////////////
+//
+// PUBLIC
+//
+P167_data_struct::P167_data_struct()
+{
+ _errCount = 0;
+
+ _Temperature = 0.0;
+ _rawTemperature = 0.0;
+ _mysTemperature = 0.0;
+ _Humidity = 0.0;
+ _rawHumidity = 0.0;
+ _mysHumidity = 0.0;
+ _DewPoint = 0.0;
+
+ _tVOC = 0.0;
+ _rawtVOC = 0.0;
+ _NOx = 0.0;
+ _rawNOx = 0.0;
+ _mysOffset = 0.0;
+ _PM1p0 = 0.0;
+ _PM2p5 = 0.0;
+ _PM4p0 = 0.0;
+ _PM10p0 = 0.0;
+
+ _devicestatus.val = (uint32_t)0;
+
+ _readingerrcode = VIND_ERR_NO_ERROR;
+ _readingerrcount = 0;
+ _readingsuccesscount = 0;
+
+ _model = 0;
+ _i2caddr = 0;
+ _monpin = 0;
+
+ _state = P167_state::Uninitialized;
+ _eid_productname = F("");
+ _eid_serialnumber = F("");
+ _firmware = 0;
+ _last_action_started = 0;
+ _userreg = 0;
+}
+
+
+P167_data_struct::~P167_data_struct()
+{
+ //
+}
+
+// Initialize/setup device properties
+// Must be called at least once before oP167::Wairperating the device
+bool P167_data_struct::setupDevice(uint8_t i2caddr)
+{
+ _i2caddr = i2caddr;
+
+#ifdef PLUGIN_167_DEBUG
+ if (loglevelActiveFor(LOG_LEVEL_INFO))
+ {
+ String log = F("SEN5x: Setup with address= ");
+ log += formatToHex(_i2caddr);
+ addLog(LOG_LEVEL_INFO, log);
+ }
+#endif
+ return true;
+}
+
+
+bool P167_data_struct::setupMonPin(uint8_t monpin)
+{
+ if (validGpio(monpin))
+ {
+ _monpin = monpin;
+ pinMode(_monpin, INPUT_PULLUP); //declare monitoring pin as input with pullup's
+ //attachInterruptArg(digitalPinToInterrupt(_monpin), reinterpret_cast(checkPin), this, CHANGE);
+ //enableInterrupt_monpin();
+
+ #ifdef PLUGIN_167_DEBUG
+ if (loglevelActiveFor(LOG_LEVEL_INFO))
+ {
+ String log = F("SEN5x: Setup I2C SCL monpin= ");
+ log += _monpin;
+ addLog(LOG_LEVEL_INFO, log);
+ }
+ #endif
+ return true;
+ }
+ else
+ return false;
+}
+
+
+void P167_data_struct::enableInterrupt_monpin(void)
+{
+ //attachInterruptArg(digitalPinToInterrupt(_monpin), reinterpret_cast(checkPin), this, CHANGE);
+}
+
+
+void P167_data_struct::disableInterrupt_monpin(void)
+{
+ //detachInterrupt(digitalPinToInterrupt(_monpin));
+}
+
+// Initialize/setup device properties
+// Must be called at least once before oP167::Wairperating the device
+bool P167_data_struct::setupModel(uint8_t model)
+{
+ _model = model;
+
+#ifdef PLUGIN_167_DEBUG
+ if (loglevelActiveFor(LOG_LEVEL_INFO))
+ {
+ String log = F("SEN5x: Setup model= ");
+ log += String(_model);
+ addLog(LOG_LEVEL_INFO, log);
+ }
+#endif
+ return true;
+}
+
+//////////////////////////////////////////////////////////////////////////////////////////////////
+// Evaluate FSM for data acquisition
+// This is a state machine that is evaluated step by step by calling update() repetatively
+// NOTE: Function is expected to run as critical section w.r.t. other provided functions
+// This is typically met in ESPeasy plugin context when called from within the plugin
+bool P167_data_struct::update()
+{
+ bool stable = false; // signals when a stable state is reached
+#ifdef PLUGIN_167_DEBUG
+ P167_state oldState = _state;
+#endif
+
+
+ if(statusMonitoring == false)
+ return stable;
+
+
+ switch(_state)
+ {
+ case P167_state::Uninitialized:
+ //we have to stop trying after a while
+ if (_errCount>P167_MAX_RETRY)
+ {
+ _state = P167_state::Error;
+ stable = true;
+ }
+ else if (I2C_wakeup(_i2caddr) != 0) // Try to access the I2C device
+ {
+ if (loglevelActiveFor(LOG_LEVEL_ERROR))
+ {
+ String log = F("SEN5x : Not found at I2C address: ");
+ log += String(_i2caddr, HEX);
+ addLog(LOG_LEVEL_ERROR, log);
+ }
+ _errCount++;
+ }
+ else if (_model==0 ) //sensor is Vindstyrka and d'ont need to be reset
+ {
+ _errCount = 0; // Device is reachable and initialized, reset error counter
+ if (writeCmd(P167_READ_FIRM_VER)) // Issue a reset command
+ {
+ _state = P167_state::Read_firm_version; // Will take <20ms according to datasheet
+ _last_action_started = millis();
+ }
+ }
+ else if (_model==1)
+ {
+ _errCount = 0; // Device is reachable and initialized, reset error counter
+ if (writeCmd(P167_RESET_DEVICE)) // Issue a reset command
+ {
+ _state = P167_state::Wait_for_reset; // Will take <20ms according to datasheet
+ _last_action_started = millis();
+ }
+ }
+ break;
+
+ case P167_state::Wait_for_reset:
+ if (timeOutReached(_last_action_started + P167_RESET_DEVICE_DELAY)) //we need to wait for the chip to reset
+ {
+ if (I2C_wakeup(_i2caddr) != 0)
+ {
+ _errCount++;
+ _state = P167_state::Uninitialized; // Retry
+ }
+ else
+ {
+ _errCount = 0; // Device is reachable and initialized, reset error counter
+ if (writeCmd(P167_READ_FIRM_VER))
+ {
+ _state = P167_state::Read_firm_version; // Will take <20ms according to datasheet
+ _last_action_started = millis();
+ }
+ }
+ }
+ break;
+
+ case P167_state::Read_firm_version:
+ if (timeOutReached(_last_action_started + P167_READ_FIRM_VER_DELAY))
+ {
+ // Start read flag
+ if (!getFirmwareVersion())
+ {
+ _errCount++;
+ _state = P167_state::Uninitialized; // Retry
+ }
+ else if(!writeCmd(P167_READ_PROD_NAME))
+ {
+ _errCount++;
+ _state = P167_state::Uninitialized; // Retry
+ }
+ else
+ {
+ _last_action_started = millis();
+ _state = P167_state::Read_prod_name;
+ }
+ }
+ break;
+
+ case P167_state::Read_prod_name:
+ if (timeOutReached(_last_action_started + P167_READ_PROD_NAME_DELAY))
+ {
+ // Start read flag
+ if (!getProductName())
+ {
+ _errCount++;
+ _state = P167_state::Uninitialized; // Retry
+ }
+ else if(!writeCmd(P167_READ_SERIAL_NO))
+ {
+ _errCount++;
+ _state = P167_state::Uninitialized; // Retry
+ }
+ else
+ {
+ _last_action_started = millis();
+ _state = P167_state::Read_serial_no;
+ }
+ }
+ break;
+
+ case P167_state::Read_serial_no:
+ if (timeOutReached(_last_action_started + P167_READ_SERIAL_NO_DELAY))
+ {
+ // Start read flag
+ if (!getSerialNumber())
+ {
+ _errCount++;
+ _state = P167_state::Uninitialized; // Retry
+ }
+ else if(!writeCmd(P167_READ_SERIAL_NO))
+ {
+ _errCount++;
+ _state = P167_state::Uninitialized; // Retry
+ }
+ else
+ {
+ _last_action_started = millis();
+ _state = P167_state::Initialized;
+ }
+ }
+ break;
+
+ case P167_state::Write_user_reg:
+ _state = P167_state::Initialized;
+ break;
+
+ case P167_state::Initialized:
+ // For now trigger the first read cycle automatically
+ //_state = P167_state::Ready;
+ break;
+
+ case P167_state::Ready:
+ // Ready to execute a measurement cycle
+ if( _model==0 || _model==1 || _model==2)
+ {
+ // Start measuring data
+ if (!writeCmd(P167_START_MEAS))
+ {
+ _errCount++;
+ _state = P167_state::Uninitialized; // Retry
+ }
+ else
+ {
+ _last_action_started = millis();
+ _state = P167_state::Wait_for_start_meas;
+ }
+ }
+ break;
+
+ case P167_state::Wait_for_start_meas:
+ if (timeOutReached(_last_action_started + P167_START_MEAS_DELAY))
+ {
+ // Start read flag
+ if (!writeCmd(P167_READ_DATA_RDY_FLAG))
+ {
+ _errCount++;
+ _state = P167_state::Uninitialized; // Retry
+ }
+ else
+ {
+ _last_action_started = millis();
+ _state = P167_state::Wait_for_read_flag;
+ }
+ }
+ break;
+
+ case P167_state::Wait_for_read_flag:
+ if (timeOutReached(_last_action_started + P167_READ_DATA_RDY_FLAG_DELAY))
+ {
+ if(readDataRdyFlag())
+ {
+ // Ready to execute a measurement cycle
+ if (!writeCmd(P167_READ_MEAS))
+ {
+ _errCount++;
+ _state = P167_state::Uninitialized; // Retry
+ }
+ else
+ {
+ _last_action_started = millis();
+ _state = P167_state::Wait_for_read_meas;
+ }
+ }
+ else //Ready Flag NOT ok, so send again Start Measurement
+ {
+ // Start measuring data
+ if (!writeCmd(P167_START_MEAS))
+ {
+ _errCount++;
+ _state = P167_state::Uninitialized; // Retry
+ }
+ else
+ {
+ _last_action_started = millis();
+ _state = P167_state::Wait_for_start_meas;
+ }
+ }
+ }
+ break;
+
+ case P167_state::Wait_for_read_meas:
+ if (timeOutReached(_last_action_started + P167_READ_MEAS_DELAY))
+ {
+ if (!readMeasValue()) // Read the previously measured temperature
+ {
+ _errCount++;
+ //_state = P167_state::Uninitialized; // Lost connection
+ _state = P167_state::cmdSTARTmeas;
+ }
+ else
+ {
+ if (!writeCmd(P167_READ_RAW_MEAS))
+ {
+ _errCount++;
+ _state = P167_state::Uninitialized; // Retry
+ }
+ else
+ {
+ _last_action_started = millis();
+ _state = P167_state::Wait_for_read_raw_meas;
+ }
+ }
+ }
+ break;
+
+ case P167_state::Wait_for_read_raw_meas:
+ //make sure we wait for the measurement to complete
+ if (timeOutReached(_last_action_started + P167_READ_RAW_MEAS_DELAY))
+ {
+ if (!readMeasRawValue())
+ {
+ _errCount++;
+ //_state = P167_state::Uninitialized; // Lost connection
+ _state = P167_state::cmdSTARTmeas;
+ }
+ else
+ {
+ if (!writeCmd(P167_READ_RAW_MYS_MEAS))
+ {
+ _errCount++;
+ _state = P167_state::Uninitialized; // Retry
+ }
+ else
+ {
+ _last_action_started = millis();
+ _state = P167_state::Wait_for_read_raw_MYS_meas;
+ }
+ }
+ }
+ break;
+
+ case P167_state::Wait_for_read_raw_MYS_meas:
+ //make sure we wait for the measurement to complete
+ if (timeOutReached(_last_action_started + P167_READ_RAW_MEAS_DELAY))
+ {
+ if (!readMeasRawMYSValue())
+ {
+ _errCount++;
+ //_state = P167_state::Uninitialized; // Lost connection
+ _state = P167_state::cmdSTARTmeas;
+ }
+ else
+ {
+ if (!writeCmd(P167_READ_DEVICE_STATUS))
+ {
+ _errCount++;
+ _state = P167_state::Uninitialized; // Retry
+ }
+ else
+ {
+ _last_action_started = millis();
+ _state = P167_state::Wait_for_read_status;
+ }
+ calculateValue();
+ stable = true;
+ }
+ }
+ break;
+
+ case P167_state::Wait_for_read_status:
+ //make sure we wait for the measurement to complete
+ if (timeOutReached(_last_action_started + P167_READ_DEVICE_STATUS_DELAY))
+ {
+ if (!readDeviceStatus())
+ {
+ _errCount++;
+ //_state = P167_state::Uninitialized; // Lost connection
+ _state = P167_state::cmdSTARTmeas;
+ }
+ else
+ {
+ _last_action_started = millis();
+ _state = P167_state::cmdSTARTmeas;
+ stable = true;
+ }
+ }
+ break;
+
+ case P167_state::cmdSTARTmeas:
+ // Start measuring data
+ if(_model==0)
+ {
+ if (!writeCmd(P167_START_MEAS))
+ {
+ _errCount++;
+ _state = P167_state::Uninitialized; // Retry
+ }
+ else
+ {
+ _last_action_started = millis();
+ _state = P167_state::IDLE;
+ }
+ }
+ else
+ {
+ _state = P167_state::IDLE;
+ }
+ break;
+
+ case P167_state::IDLE:
+ stepMonitoring = 1;
+ startMonitoringFlag = false;
+ if(!_errmeas && !_errmeasraw && !_errmeasrawmys)
+ _state = P167_state::New_Values_Available;
+ stable = true;
+ break;
+
+ case P167_state::Error:
+ case P167_state::New_Values_Available:
+ //this state is used outside so all we need is to stay here
+ stable = true;
+ break;
+
+ //Missing states (enum values) to be checked by the compiler
+
+ } // switch
+
+#ifdef PLUGIN_167_DEBUG
+ if (_state != oldState)
+ {
+ if (loglevelActiveFor(LOG_LEVEL_INFO))
+ {
+ String log = F("SEN5x : *** state transition ");
+ log += String((int)oldState);
+ log += F("-->");
+ log += String((int)_state);
+ addLog(LOG_LEVEL_INFO, log);
+ }
+ }
+#endif
+ return stable;
+}
+
+
+bool P167_data_struct::monitorSCL()
+{
+ if(_model==0)
+ {
+ if(startMonitoringFlag)
+ {
+
+ if(stepMonitoring==1)
+ {
+ lastSCLLowTransitionMonitoringTime = monpinLastTransitionTime/1000;
+ if(millis() - lastSCLLowTransitionMonitoringTime < 100)
+ {
+ statusMonitoring = false;
+ return true;
+ }
+ else
+ {
+ lastSCLLowTransitionMonitoringTime = monpinLastTransitionTime/1000;
+ statusMonitoring = true;
+ stepMonitoring++;
+ }
+ }
+
+ if(stepMonitoring==2)
+ {
+ if(millis() - lastSCLLowTransitionMonitoringTime < 100)
+ {
+ lastSCLLowTransitionMonitoringTime = monpinLastTransitionTime/1000;
+ statusMonitoring = false;
+ stepMonitoring = 1;
+ return true;
+ }
+ else if(millis() - lastSCLLowTransitionMonitoringTime > 700)
+ {
+ statusMonitoring = false;
+ stepMonitoring = 1;
+ startMonitoringFlag = false;
+
+ //if _state not finish reading process then start from begining
+ if(_state >= P167_state::Wait_for_read_meas && _state < P167_state::New_Values_Available)
+ {
+ _state = P167_state::Ready;
+ }
+ return true;
+ }
+ else
+ {
+ //processing
+ }
+ }
+ }
+ monpinValuelast = monpinValue;
+ }
+
+ if(_model == 1 || _model == 2)
+ {
+ statusMonitoring = true;
+ startMonitoringFlag = false;
+ stepMonitoring = 0;
+ }
+
+ return true;
+}
+
+//////////////////////////////////////////////////////////////////////////////////////////////////
+// Returns the I2C connection state
+// Note: based upon the FSM state without actual accessing the device
+bool P167_data_struct::isConnected() const
+{
+ switch (_state)
+ {
+ case P167_state::Initialized:
+ case P167_state::Ready:
+ case P167_state::Wait_for_start_meas:
+ case P167_state::Wait_for_read_flag:
+ case P167_state::Wait_for_read_meas:
+ case P167_state::Wait_for_read_raw_meas:
+ case P167_state::Wait_for_read_raw_MYS_meas:
+ case P167_state::Wait_for_read_status:
+ case P167_state::cmdSTARTmeas:
+ case P167_state::New_Values_Available:
+ case P167_state::Read_firm_version:
+ case P167_state::Read_prod_name:
+ case P167_state::Read_serial_no:
+ case P167_state::Write_user_reg:
+ case P167_state::IDLE:
+ return true;
+ break;
+ case P167_state::Uninitialized:
+ case P167_state::Error:
+ case P167_state::Wait_for_reset:
+ return false;
+ break;
+
+ //Missing states (enum values) to be checked by the compiler
+ }
+ return false;
+}
+
+//////////////////////////////////////////////////////////////////////////////////////////////////
+// Returns if the device communication is in error
+// Note: based upon the FSM state without actual accessing the device
+bool P167_data_struct::inError() const
+{
+ return _state == P167_state::Error;
+}
+
+//////////////////////////////////////////////////////////////////////////////////////////////////
+// Returns if new acquired values are available
+bool P167_data_struct::newValues() const
+{
+ return _state == P167_state::New_Values_Available;
+}
+
+//////////////////////////////////////////////////////////////////////////////////////////////////
+// Restart the FSM used to access the device
+bool P167_data_struct::reset()
+{
+ startMonitoringFlag = true;
+ stepMonitoring = 1;
+ _state = P167_state::Uninitialized;
+ return true;
+}
+
+//////////////////////////////////////////////////////////////////////////////////////////////////
+// Start a new measurement cycle
+bool P167_data_struct::startMeasurements()
+{
+ if ((_state == P167_state::New_Values_Available) || (_state == P167_state::Initialized) || (_state == P167_state::IDLE))
+ {
+ _state = P167_state::Ready;
+ }
+ startMonitoringFlag = true;
+ stepMonitoring = 1;
+ return true;
+}
+
+//////////////////////////////////////////////////////////////////////////////////////////////////
+// Get the electronic idenfification data store in the device
+// Note: The data is read from the device during initialization
+bool P167_data_struct::getEID(String &eid_productname, String &eid_serialnumber, uint8_t &firmware) const
+{
+ eid_productname = _eid_productname;
+ eid_serialnumber = _eid_serialnumber;
+ firmware = _firmware;
+ return true;
+}
+
+//////////////////////////////////////////////////////////////////////////////////////////////////
+// Get the status informasion about different part of the sensor
+// Note: The data is read from the device after every measurement read request
+bool P167_data_struct::getStatusInfo(param_statusinfo param)
+{
+ switch(param)
+ {
+ case sensor_speed:
+ return (bool) _devicestatus.speed;
+ break;
+
+ case sensor_autoclean:
+ return (bool) _devicestatus.autoclean;
+ break;
+
+ case sensor_gas:
+ return (bool) _devicestatus.gas;
+ break;
+
+ case sensor_rht:
+ return (bool) _devicestatus.rht;
+ break;
+
+ case sensor_laser:
+ return (bool) _devicestatus.laser;
+ break;
+
+ case sensor_fan:
+ return (bool) _devicestatus.fan;
+ break;
+ }
+ return true;
+}
+
+//////////////////////////////////////////////////////////////////////////////////////////////////
+// Return the previously measured raw humidity data [bits]
+float P167_data_struct::getRequestedValue(uint8_t request) const
+{
+ //float requested_value=0;
+ switch(request)
+ {
+ case 0:
+ {
+ if(_model==0)
+ return (float) _TemperatureX;
+ else
+ return (float) _Temperature;
+ }
+ case 1:
+ {
+ if(_model==0)
+ return (float) _HumidityX;
+ else
+ return (float) _Humidity;
+ }
+ case 2: return (float) _tVOC;
+ case 3: return (float) _NOx;
+ case 4: return (float) _PM1p0;
+ case 5: return (float) _PM2p5;
+ case 6: return (float) _PM4p0;
+ case 7: return (float) _PM10p0;
+ case 8: return (float) _DewPoint;
+ }
+ return -1;
+}
+
+
+//////////////////////////////////////////////////////////////////////////////////////////////////
+//
+// PROTECTED
+//
+//////////////////////////////////////////////////////////////////////////////////////////////////
+
+//////////////////////////////////////////////////////////////////////////////////////////////////
+uint8_t P167_data_struct::crc8(const uint8_t *data, uint8_t len)
+{
+ // CRC-8 formula from page 14 of SHT spec pdf
+ // Sensirion_Humidity_Sensors_SHT2x_CRC_Calculation.pdf
+ const uint8_t POLY = 0x31;
+ uint8_t crc = 0xFF;
+
+ for (uint8_t j = 0; j> 8));
+ Wire.write((uint8_t)cmd);
+ return Wire.endTransmission() == 0;
+}
+
+//////////////////////////////////////////////////////////////////////////////////////////////////
+bool P167_data_struct::writeCmd(uint16_t cmd, uint8_t value)
+{
+ Wire.beginTransmission(_i2caddr);
+ Wire.write((uint8_t)(cmd >> 8));
+ Wire.write((uint8_t)cmd);
+ Wire.write((uint8_t)value);
+ return Wire.endTransmission() == 0;
+}
+
+
+
+bool P167_data_struct::writeCmd(uint16_t cmd, uint8_t length, uint8_t *buffer)
+{
+ Wire.beginTransmission(_i2caddr);
+ Wire.write((uint8_t)(cmd >> 8));
+ Wire.write((uint8_t)cmd);
+ for (int i = 0; i < length; i++) {
+ Wire.write(*(buffer + i));
+ }
+ return Wire.endTransmission() == 0;
+}
+
+//////////////////////////////////////////////////////////////////////////////////////////////////
+bool P167_data_struct::readBytes(uint8_t n, uint8_t *val, uint8_t maxDuration)
+{
+ // TODO check if part can be delegated to the I2C_access libraray from ESPeasy
+ Wire.requestFrom(_i2caddr, (uint8_t) n);
+ uint32_t start = millis();
+ while (Wire.available() < n)
+ {
+ if (timePassedSince(start) > maxDuration)
+ {
+ return false;
+ }
+ yield();
+ }
+
+ for (uint8_t i = 0; i < n; i++)
+ {
+ val[i] = Wire.read();
+ }
+ return true;
+}
+
+
+//////////////////////////////////////////////////////////////////////////////////////////////////
+// Read data ready flag from device
+bool P167_data_struct::readDataRdyFlag()
+{
+ uint8_t value=0;
+ uint8_t buffer[3];
+
+ if (!readBytes(3, (uint8_t*) &buffer[0], P167_READ_DATA_RDY_FLAG_DELAY))
+ {
+ return false;
+ }
+ if (crc8(&buffer[0], 2) == buffer[2])
+ {
+ value += buffer[1];
+ }
+ return value;
+}
+
+
+//////////////////////////////////////////////////////////////////////////////////////////////////
+// Read measurement values results from device
+bool P167_data_struct::readMeasValue()
+{
+ uint16_t value=0;
+ int16_t valuesign=0;
+ uint8_t buffer[24];
+ bool condition=true;
+
+ _errmeas = false;
+ if (!readBytes(24, (uint8_t*) &buffer[0], P167_READ_MEAS_DELAY))
+ {
+ _errmeas = true;
+ return false;
+ }
+
+ String log = F("SEN5x : *** meas value ");
+ for(int xx=0; xx<24;xx++)
+ {
+ log += String((int)buffer[xx]);
+ log += F(" ");
+ }
+
+ if(_model==0 || _model==1)
+ condition=(buffer[0] == 0xFF && buffer[1] == 0xFF) || (buffer[3] == 0xFF && buffer[4] == 0xFF) || (buffer[6] == 0xFF && buffer[7] == 0xFF) || (buffer[9] == 0xFF && buffer[10] == 0xFF) || (buffer[12] == 0xFF && buffer[13] == 0xFF) || (buffer[15] == 0xFF && buffer[16] == 0xFF) || (buffer[18] == 0xFF && buffer[19] == 0xFF);
+ if(_model==2)
+ condition=(buffer[0] == 0xFF && buffer[1] == 0xFF) || (buffer[3] == 0xFF && buffer[4] == 0xFF) || (buffer[6] == 0xFF && buffer[7] == 0xFF) || (buffer[9] == 0xFF && buffer[10] == 0xFF) || (buffer[12] == 0xFF && buffer[13] == 0xFF) || (buffer[15] == 0xFF && buffer[16] == 0xFF) || (buffer[18] == 0xFF && buffer[19] == 0xFF) || (buffer[21] == 0xFF && buffer[22] == 0xFF);
+
+ if(condition)
+ {
+ log += F("- error");
+ addLog(LOG_LEVEL_INFO, log);
+ _errmeas = true;
+ _readingerrcount++;
+ return false;
+ }
+ else
+ {
+ for(int xx=0; xx<8; xx++)
+ {
+ if ((crc8(&buffer[xx*3], 2) == buffer[xx*3+2]) && (buffer[xx*3] != 0xFF || buffer[xx*3+1] != 0xFF))
+ {
+ value = buffer[xx*3] << 8;
+ value += buffer[xx*3+1];
+ valuesign = buffer[xx*3] << 8;
+ valuesign += buffer[xx*3+1];
+ if(xx==0)
+ _PM1p0 = (float)value/10;
+ if(xx==1)
+ _PM2p5 = (float)value/10;
+ if(xx==2)
+ _PM4p0 = (float)value/10;
+ if(xx==3)
+ _PM10p0 = (float)value/10;
+ if(xx==4)
+ _Humidity = (float)valuesign/100.0;
+ if(xx==5)
+ _Temperature = (float)valuesign/200.0;
+ if(xx==6)
+ _tVOC = (float)valuesign/10.0;
+ if(xx==7)
+ {
+ if(_model==2)
+ _NOx = (float)valuesign/10.0;
+ else
+ _NOx = (float)0.0;
+ }
+ }
+ else
+ {
+ _errmeasrawmys = true;
+ }
+ }
+
+ if(_errmeas == true)
+ {
+ log += F("- crc error");
+ _readingerrcount++;
+ }
+ else
+ {
+ log += F("- pass");
+ _readingsuccesscount++;
+ }
+ addLog(LOG_LEVEL_INFO, log);
+ return !_errmeas;
+ }
+
+ return true;
+}
+
+
+//////////////////////////////////////////////////////////////////////////////////////////////////
+// Read measurement values results from device
+bool P167_data_struct::readMeasRawValue()
+{
+ uint16_t value=0;
+ int16_t valuesign=0;
+ uint8_t buffer[12];
+ bool condition=true;
+
+ _errmeasraw = false;
+ if (!readBytes(12, (uint8_t*) &buffer[0], P167_READ_RAW_MEAS_DELAY))
+ {
+ _errmeasraw = true;
+ return false;
+ }
+
+ String log = F("SEN5x : *** meas RAW value ");
+ for(int xx=0; xx<12;xx++)
+ {
+ log += String((int)buffer[xx]);
+ log += F(" ");
+ }
+
+ if(_model==0 || _model==1)
+ condition=(buffer[0] == 0xFF && buffer[1] == 0xFF) || (buffer[3] == 0xFF && buffer[4] == 0xFF) || (buffer[6] == 0xFF && buffer[7] == 0xFF);// || (buffer[9] == 0xFF && buffer[10] == 0xFF))
+ if(_model==2)
+ condition=(buffer[0] == 0xFF && buffer[1] == 0xFF) || (buffer[3] == 0xFF && buffer[4] == 0xFF) || (buffer[6] == 0xFF && buffer[7] == 0xFF) || (buffer[9] == 0xFF && buffer[10] == 0xFF);
+
+ if(condition)
+ {
+ log += F("- error");
+ addLog(LOG_LEVEL_INFO, log);
+ _errmeasraw = true;
+ _readingerrcount++;
+ return false;
+ }
+ else
+ {
+ for(int xx=0; xx<4; xx++)
+ {
+ if ((crc8(&buffer[xx*3], 2) == buffer[xx*3+2]) && (buffer[xx*3] != 0xFF || buffer[xx*3+1] != 0xFF))
+ {
+ value = buffer[xx*3] << 8;
+ value += buffer[xx*3+1];
+ valuesign = buffer[xx*3] << 8;
+ valuesign += buffer[xx*3+1];
+ if(xx==0)
+ _rawHumidity = (float)valuesign/100.0;
+ if(xx==1)
+ _rawTemperature = (float)valuesign/200.0;
+ if(xx==2)
+ _rawtVOC = (float)value/10.0;
+ if(xx==3)
+ {
+ if(_model==2)
+ _rawNOx = (float)value/10.0;
+ else
+ _rawNOx = (float)0.0;
+ }
+ }
+ else
+ {
+ _errmeasrawmys = true;
+ }
+ }
+
+ if(_errmeasraw == true)
+ {
+ log += F("- crc error");
+ _readingerrcount++;
+ }
+ else
+ {
+ log += F("- pass");
+ _readingsuccesscount++;
+ }
+ addLog(LOG_LEVEL_INFO, log);
+ return !_errmeasraw;
+ }
+
+
+ return true;
+}
+
+
+
+//////////////////////////////////////////////////////////////////////////////////////////////////
+// Read measurement values results from device
+bool P167_data_struct::readMeasRawMYSValue()
+{
+ uint16_t value=0;
+ int16_t valuesign=0;
+ uint8_t buffer[9];
+
+ _errmeasrawmys = false;
+ if (!readBytes(9, (uint8_t*) &buffer[0], P167_READ_RAW_MEAS_DELAY))
+ {
+ _errmeasrawmys = true;
+ return false;
+ }
+
+ String log = F("SEN5x : *** meas MYS value ");
+ for(int xx=0; xx<9;xx++)
+ {
+ log += String((int)buffer[xx]);
+ log += F(" ");
+ }
+
+ if((buffer[0] == 0xFF && buffer[1] == 0xFF) || (buffer[3] == 0xFF && buffer[4] == 0xFF) || (buffer[6] == 0xFF && buffer[7] == 0xFF))
+ {
+ log += F("- error");
+ addLog(LOG_LEVEL_INFO, log);
+ _errmeasrawmys = true;
+ _readingerrcount++;
+ return false;
+ }
+ else
+ {
+ for(int xx=0; xx<3; xx++)
+ {
+ if ((crc8(&buffer[xx*3], 2) == buffer[xx*3+2]) && (buffer[xx*3] != 0xFF || buffer[xx*3+1] != 0xFF))
+ {
+ value = buffer[xx*3] << 8;
+ value += buffer[xx*3+1];
+ valuesign = buffer[xx*3] << 8;
+ valuesign += buffer[xx*3+1];
+ if(xx==0)
+ _mysHumidity = (float)valuesign/100.0;
+ if(xx==1)
+ _mysTemperature = (float)valuesign/200.0;
+ if(xx==2)
+ _mysOffset = (float)valuesign/200.0;
+ }
+ else
+ {
+ _errmeasrawmys = true;
+ }
+ }
+
+ if(_errmeasrawmys == true)
+ {
+ log += F("- crc error");
+ _readingerrcount++;
+ }
+ else
+ {
+ log += F("- pass");
+ _readingsuccesscount++;
+ }
+ addLog(LOG_LEVEL_INFO, log);
+ return !_errmeasrawmys;
+ }
+
+
+ return true;
+}
+
+
+//////////////////////////////////////////////////////////////////////////////////////////////////
+// Calculate DewPoint, F Temp, F HUM
+bool P167_data_struct::calculateValue()
+{
+ float lnval;
+ float rapval;
+ float aval = 17.62;
+ float bval = 243.12;
+ float Dp;
+ float eeval = 0.0;
+
+ if(_model==0)
+ {
+ //_TemperatureX = _mysTemperature + _mysOffset - (_mysOffset<0.0?(_mysOffset*(-1.0)):_mysOffset)/2;
+ //_TemperatureX = _mysTemperature + _mysOffset*3.0/2.0;
+ _TemperatureX = _mysTemperature + _mysOffset - 2.4; //(2.4 - temperature offset because enclosure and esp8266 power disipation)
+
+ //version formula with DewPoint
+ //lnval = logf(_mysHumidity/100.0);
+ //rapval = (aval * _mysTemperature)/(bval+_mysTemperature);
+ //Dp = (bval*(lnval+rapval))/(aval-lnval-rapval);
+ //eeval = expf((aval*Dp)/(bval+Dp))/expf((aval*_TemperatureX)/(bval+_TemperatureX));
+ //_HumidityX = eeval*100.0;
+
+ //version formula with interpolation
+ _HumidityX = _Humidity+(_TemperatureX-_Temperature)*((_rawHumidity-_Humidity)/(_rawTemperature-_Temperature));
+ lnval = logf(_HumidityX/100.0);
+ rapval = (aval * _TemperatureX)/(bval+_TemperatureX);
+ Dp = (bval*(lnval+rapval))/(aval-lnval-rapval);
+
+ if(_HumidityX < 0.0)
+ _HumidityX = 0.0;
+ if(_HumidityX > 100.0)
+ _HumidityX = 100.0;
+ }
+ else
+ {
+ lnval = logf(_Humidity/100.0);
+ rapval = (aval * _Temperature)/(bval+_Temperature);
+ Dp = (bval*(lnval+rapval))/(aval-lnval-rapval);
+ }
+ _DewPoint = Dp;
+
+ return true;
+}
+
+//////////////////////////////////////////////////////////////////////////////////////////////////
+// Retrieve SEN5x identification code
+// Sensirion_SEN5x
+bool P167_data_struct::getProductName()
+{
+ String prodname=F("");
+ uint8_t buffer[48];
+ //writeCmd(P167_READ_PROD_NAME);
+ if (!readBytes(48, (uint8_t *) buffer, P167_READ_PROD_NAME_DELAY))
+ {
+ return false;
+ }
+ for (uint8_t i = 1; i <= 16; i++)
+ {
+ if(crc8(&buffer[i*3-3], 2) == buffer[i*3-1])
+ {
+ if (buffer[i*3-3] < 32)
+ break;
+ prodname+=char(buffer[i*3-3]);
+ if (buffer[i*3-2] < 32)
+ break;
+ prodname+=char(buffer[i*3-2]);
+ }
+ }
+ _eid_productname=prodname;
+
+ String log = F("SEN5x : *** Product name: ");
+ log += String(prodname);
+ addLog(LOG_LEVEL_INFO, log);
+
+ return true;
+}
+
+//////////////////////////////////////////////////////////////////////////////////////////////////
+// Retrieve SEN54 Serial Number
+bool P167_data_struct::getSerialNumber()
+{
+ String serno=F("");
+ uint8_t buffer[48];
+ //writeCmd(P167_READ_SERIAL_NO);
+ if (!readBytes(48, (uint8_t *) buffer, P167_READ_SERIAL_NO_DELAY))
+ {
+ return false;
+ }
+ for (uint8_t i = 1; i <= 16; i++)
+ {
+ if(crc8(&buffer[i*3-3], 2) == buffer[i*3-1])
+ {
+ if (buffer[i*3-3] < 32)
+ break;
+ serno+=char(buffer[i*3-3]);
+ if (buffer[i*3-2] < 32)
+ break;
+ serno+=char(buffer[i*3-2]);
+ }
+ }
+ _eid_serialnumber=serno;
+
+ String log = F("SEN5x : *** Serial number: ");
+ log += String(serno);
+ addLog(LOG_LEVEL_INFO, log);
+
+ return true;
+}
+
+//////////////////////////////////////////////////////////////////////////////////////////////////
+// Retrieve SEN54 Firmware version from device
+bool P167_data_struct::getFirmwareVersion()
+{
+ uint8_t version = 0;
+ uint8_t read_data[3];
+ //writeCmd(P167_READ_FIRM_VER);
+ if (!readBytes(3, (uint8_t *) &read_data, P167_READ_FIRM_VER_DELAY))
+ {
+ return false;
+ }
+ if( read_data[2] == crc8(&read_data[0],2) )
+ {
+ version=read_data[0];
+ }
+ else
+ {
+ version=0;
+ }
+ _firmware=version;
+
+ String log = F("SEN5x : *** Firmware version: ");
+ log += String((uint8_t)version);
+ addLog(LOG_LEVEL_INFO, log);
+
+ return true;
+}
+
+//////////////////////////////////////////////////////////////////////////////////////////////////
+// Retrieve SEN54 Device Status from device
+bool P167_data_struct::readDeviceStatus()
+{
+ uint32_t value=0;
+ uint8_t bufferstatus[6];
+ //writeCmd(P167_READ_FIRM_VER);
+ _errdevicestatus = false;
+ if (!readBytes(6, (uint8_t *) &bufferstatus, P167_READ_DEVICE_STATUS_DELAY))
+ {
+ _errdevicestatus = true;
+ return false;
+ }
+
+ String log = F("SEN5x : *** device status ");
+ for(int xx=0; xx<6;xx++)
+ {
+ log += String((int)bufferstatus[xx]);
+ log += F(" ");
+ }
+
+ if ((crc8(&bufferstatus[0], 2) == bufferstatus[2]) && (crc8(&bufferstatus[3], 2) == bufferstatus[5]))
+ {
+ value = bufferstatus[0] << 8;
+ value += bufferstatus[1] << 8;
+ value += bufferstatus[3] << 8;
+ value += bufferstatus[4] << 8;
+ _devicestatus.val = value;
+ }
+ else
+ {
+ _errdevicestatus = true;
+ }
+
+ if(_errdevicestatus == true)
+ {
+ log += F("- crc error");
+ _readingerrcount++;
+ }
+ else
+ {
+ log += String((bool)_devicestatus.speed);
+ log += String((bool)_devicestatus.autoclean);
+ log += String((bool)_devicestatus.gas);
+ log += String((bool)_devicestatus.rht);
+ log += String((bool)_devicestatus.laser);
+ log += String((bool)_devicestatus.fan);
+ log += F(" - pass");
+ _readingsuccesscount++;
+ }
+ addLog(LOG_LEVEL_INFO, log);
+ return !_errdevicestatus;
+}
+
+
+uint16_t P167_data_struct::getErrCode(bool _clear)
+{
+ uint16_t _tmp = _readingerrcode;
+ if (_clear == true)
+ clearErrCode();
+ return (_tmp);
+}
+
+uint16_t P167_data_struct::getErrCount(bool _clear)
+{
+ uint16_t _tmp = _readingerrcount;
+ if (_clear == true)
+ clearErrCount();
+ return (_tmp);
+}
+
+uint16_t P167_data_struct::getSuccCount(bool _clear)
+{
+ uint16_t _tmp = _readingsuccesscount;
+ if (_clear == true)
+ clearSuccCount();
+ return (_tmp);
+}
+
+void P167_data_struct::clearErrCode()
+{
+ _readingerrcode = VIND_ERR_NO_ERROR;
+}
+
+void P167_data_struct::clearErrCount()
+{
+ _readingerrcount = 0;
+}
+
+void P167_data_struct::clearSuccCount()
+{
+ _readingsuccesscount = 0;
+}
+
+
+void IRAM_ATTR P167_data_struct::checkPin_interrupt()
+{
+ //ISR_noInterrupts(); // s0170071: avoid nested interrups due to bouncing.
+
+ monpinValue++;
+ monpinLastTransitionTime = getMicros64();
+ // Mark pin value changed
+ monpinChanged = false;
+ if(monpinValue!=monpinValuelast)
+ monpinChanged = true;
+
+ //ISR_interrupts(); // enable interrupts again.
+}
+
+#endif // USES_P167
\ No newline at end of file
diff --git a/src/src/PluginStructs/P167_data_struct.h b/src/src/PluginStructs/P167_data_struct.h
new file mode 100644
index 0000000000..1686b351b5
--- /dev/null
+++ b/src/src/PluginStructs/P167_data_struct.h
@@ -0,0 +1,222 @@
+//////////////////////////////////////////////////////////////////////////////////////////////////
+// P167 device class for IKEA Vindstyrka SEN54 temperature , humidity and air quality sensors
+// See datasheet https://sensirion.com/media/documents/6791EFA0/62A1F68F/Sensirion_Datasheet_Environmental_Node_SEN5x.pdf
+// and info about extra request https://sensirion.com/media/documents/2B6FC1F3/6409E74A/PS_AN_Read_RHT_VOC_and_NOx_RAW_signals_D1.pdf
+// Based upon code from Rob Tillaart, Viktor Balint, https://github.com/RobTillaart/SHT2x
+// Rewritten and adapted for ESPeasy by andibaciu
+// 2023-06-20 Initial version by andibaciu
+//////////////////////////////////////////////////////////////////////////////////////////////////
+
+#include "../../_Plugin_Helper.h"
+#include "../ESPEasyCore/ESPEasyGPIO.h"
+#ifdef USES_P167
+
+#ifdef LIMIT_BUILD_SIZE
+#define PLUGIN_167_DEBUG false
+#else
+#define PLUGIN_167_DEBUG false // set to true for extra log info in the debug
+#endif
+
+// Vindstyrka device properties
+//#define P167_I2C_ADDRESS_DFLT 0x69
+
+
+//------------------------------------------------------------------------------
+#define VIND_ERR_NO_ERROR 0 // no error
+#define VIND_ERR_CRC_ERROR 1 // crc error
+#define VIND_ERR_WRONG_BYTES 2 // bytes b0,b1 or b2 wrong
+#define VIND_ERR_NOT_ENOUGHT_BYTES 3 // not enough bytes from sdm
+#define VIND_ERR_TIMEOUT 4 // timeout
+//------------------------------------------------------------------------------
+
+//////////////////////////////////////////////////////////////////////////////////////////////////
+// Access to the Vindstyrka device is mainly by sequencing a Finate State Machine
+enum class P167_state {
+ Uninitialized = 0, // Initial state, unknown status of sensor device
+ Wait_for_reset, // Reset being performed
+ Read_firm_version, // Reading firmware version
+ Read_prod_name, // Reading production
+ Read_serial_no, // Reading serial number
+ Write_user_reg, // Write the user register
+ Initialized, // Initialization completed
+ Ready, // Aquisition request is pending, ready to measure
+ Wait_for_start_meas, // Start measurement started
+ Wait_for_read_flag, // Read meas flag started
+ Wait_for_read_meas, // Read meas started
+ Wait_for_read_raw_meas, // RAW Read meas started
+ Wait_for_read_raw_MYS_meas, // RAW Read meas MYSTERY started
+ Wait_for_read_status, // Read status
+ cmdSTARTmeas, // send command START meas to leave SEN5x ready flag for Vindstyrka
+ IDLE, // Sensor device in IDLE mode
+ New_Values_Available, // Acqusition finished, new data available
+ Error // Sensor device cannot be accessed or in error
+};
+
+
+enum param_statusinfo
+{
+ sensor_speed = 0,
+ sensor_autoclean,
+ sensor_gas,
+ sensor_rht,
+ sensor_laser,
+ sensor_fan
+};
+
+
+
+//////////////////////////////////////////////////////////////////////////////////////////////////
+// ESPeasy standard PluginTaskData structure for this plugin
+//struct P167_data_struct : public PluginTaskData_base
+class P167_data_struct
+{
+
+public:
+ P167_data_struct();
+ //virtual ~P167_data_struct();
+ ~P167_data_struct();
+
+ void checkPin_interrupt(void);
+
+ /////////////////////////////////////////////////////////
+ // This method runs the FSM step by step on each call
+ // Returns true when a stable state is reached
+ bool update();
+ bool monitorSCL();
+
+ /////////////////////////////////////////////////////////
+ // (re)configure the device properties
+ // This will result in resetting and reloading the device
+ bool setupDevice(uint8_t i2caddr);
+ bool setupModel(uint8_t model);
+ bool setupMonPin(uint8_t monpin);
+ void enableInterrupt_monpin(void);
+ void disableInterrupt_monpin(void);
+
+ /////////////////////////////////////////////////////////
+ // check sensor is reachable over I2C
+ bool isConnected() const;
+
+ /////////////////////////////////////////////////////////
+ bool newValues() const;
+
+ /////////////////////////////////////////////////////////
+ bool inError() const;
+
+ /////////////////////////////////////////////////////////
+ // Reset the FSM to initial state
+ bool reset();
+
+ /////////////////////////////////////////////////////////
+ // Trigger a measurement cycle
+ // Only perform the measurements with big interval to prevent the sensor from warming up.
+ bool startMeasurements();
+
+ bool getStatusInfo(param_statusinfo param);
+ /////////////////////////////////////////////////////////
+ // Electronic Identification Code
+ // Sensirion_Humidity_SHT2x_Electronic_Identification_Code_V1.1.pdf
+ // Electronic ID bytes
+ bool getEID(String &eid_productname, String &eid_serialnumber, uint8_t &firmware) const;
+
+ /////////////////////////////////////////////////////////
+ // Temperature, humidity, DewPoint, PMxpy retrieval
+ // Note: values are fetched from memory and reflect latest succesful read cycle
+ float getRequestedValue(uint8_t request) const;
+
+
+ uint16_t getErrCode(bool _clear = false); //return last errorcode (optional clear this value, default false)
+ uint16_t getErrCount(bool _clear = false); //return total errors count (optional clear this value, default false)
+ uint16_t getSuccCount(bool _clear = false); //return total success count (optional clear this value, default false)
+ void clearErrCode(); //clear last errorcode
+ void clearErrCount(); //clear total errors count
+ void clearSuccCount(); //clear total success count
+
+//protected:
+private:
+
+ union devicestatus
+ {
+ uint32_t val;
+ struct
+ {
+ uint16_t dummy1:10;
+ bool speed;
+ bool dummy2;
+ bool autoclean;
+ uint16_t dummy3:11;
+ bool gas;
+ bool rht;
+ bool laser;
+ bool fan;
+ uint16_t dummy4:4;
+ };
+ };
+
+ devicestatus _devicestatus;
+ P167_state _state;
+
+ uint8_t crc8(const uint8_t *data, uint8_t len);
+ bool writeCmd(uint16_t cmd);
+ bool writeCmd(uint16_t cmd, uint8_t value);
+ bool writeCmd(uint16_t cmd, uint8_t length, uint8_t *buffer);
+ bool readBytes(uint8_t n, uint8_t *val, uint8_t maxDuration);
+
+ bool readMeasValue();
+ bool readMeasRawValue();
+ bool readMeasRawMYSValue();
+ bool readDataRdyFlag();
+ bool readDeviceStatus();
+ bool calculateValue();
+
+ bool getProductName();
+ bool getSerialNumber();
+ bool getFirmwareVersion();
+
+
+ float _Humidity; // Humidity as fetched from the device [bits]
+ float _HumidityX; // Humidity as calculated
+ float _Temperature; // Temperature as fetched from the device [bits]
+ float _TemperatureX; // Temperature as calculated
+ float _DewPoint; // DewPoint as calculated
+ float _rawHumidity; // Humidity as fetched from the device without compensation[bits]
+ float _rawTemperature; // Temperature as fetched from the device without compensation[bits]
+ float _mysHumidity; // Humidity as fetched from the device without compensation[bits]
+ float _mysTemperature; // Temperature as fetched from the device without compensation[bits]
+ float _tVOC; // tVOC as fetched from the device[bits]
+ float _NOx; // NOx as fetched from the device[bits]
+ float _rawtVOC; // tVOC as fetched from the device without compensation[bits]
+ float _rawNOx; // NOx as fetched from the device without compensation[bits]
+ float _mysOffset; // Temperature Offset fetched from the device[bits]
+ float _PM1p0; // PM1.0 as fetched from the device[bits]
+ float _PM2p5; // PM2.5 as fetched from the device[bits]
+ float _PM4p0; // PM4.0 as fetched from the device[bits]
+ float _PM10p0; // PM10.0 as fetched from the device[bits]
+ uint8_t _model; // Selected sensor model
+ uint8_t _i2caddr; // Programmed I2C address
+ uint8_t _monpin; // Pin to monitor I2C SCL to find when VindStyrka finish i2c communication
+ unsigned long _last_action_started; // Timestamp for last action that takes processing time
+ uint16_t _errCount; // Number of errors since last successful access
+ String _eid_productname; // Electronic Device ID - Product Name, read at initialization
+ String _eid_serialnumber; // Electronic Device ID - Serial Number, read at initialization
+ uint8_t _firmware; // Firmware version numer, read at initialization
+ uint8_t _userreg; // TODO debugging only
+ uint16_t _readingerrcode = VIND_ERR_NO_ERROR; // 4 = timeout; 3 = not enough bytes; 2 = number of bytes OK but bytes b0,b1 or b2 wrong, 1 = crc error
+ uint16_t _readingerrcount = 0; // total errors couter
+ uint32_t _readingsuccesscount = 0; // total success couter
+ bool _errmeas;
+ bool _errmeasraw;
+ bool _errmeasrawmys;
+ bool _errdevicestatus;
+ uint8_t stepMonitoring; // step for Monitorin SCL pin algorithm
+ bool startMonitoringFlag; // flag to START/STOP Monitoring algorithm
+ bool statusMonitoring; // flag for status return from Monitoring algorithm
+ unsigned long lastSCLLowTransitionMonitoringTime; // last time when SCL i2c pin rising
+
+ volatile uint32_t monpinValue = 0;
+ volatile uint32_t monpinValuelast = 0;
+ volatile uint8_t monpinChanged = 0;
+ volatile uint64_t monpinLastTransitionTime = 0;
+
+};
+#endif // USES_P167
\ No newline at end of file
diff --git a/src/src/WebServer/AdvancedConfigPage.cpp b/src/src/WebServer/AdvancedConfigPage.cpp
index b727e12bab..2ae1e5b585 100644
--- a/src/src/WebServer/AdvancedConfigPage.cpp
+++ b/src/src/WebServer/AdvancedConfigPage.cpp
@@ -134,6 +134,11 @@ void handle_advanced() {
Settings.WaitWiFiConnect(isFormItemChecked(LabelType::WAIT_WIFI_CONNECT));
Settings.HiddenSSID_SlowConnectPerBSSID(isFormItemChecked(LabelType::HIDDEN_SSID_SLOW_CONNECT));
Settings.SDK_WiFi_autoreconnect(isFormItemChecked(LabelType::SDK_WIFI_AUTORECONNECT));
+#if FEATURE_USE_IPV6
+ Settings.EnableIPv6(isFormItemChecked(LabelType::ENABLE_IPV6));
+ addFormNote(F("Toggling IPv6 requires reboot"));
+#endif
+
#ifndef BUILD_NO_RAM_TRACKER
@@ -393,6 +398,9 @@ void handle_advanced() {
addFormCheckBox(LabelType::WAIT_WIFI_CONNECT, Settings.WaitWiFiConnect());
addFormCheckBox(LabelType::SDK_WIFI_AUTORECONNECT, Settings.SDK_WiFi_autoreconnect());
addFormCheckBox(LabelType::HIDDEN_SSID_SLOW_CONNECT, Settings.HiddenSSID_SlowConnectPerBSSID());
+#if FEATURE_USE_IPV6
+ addFormCheckBox(LabelType::ENABLE_IPV6, Settings.EnableIPv6());
+#endif
diff --git a/src/src/WebServer/JSON.cpp b/src/src/WebServer/JSON.cpp
index 000400aaef..631e20d9f6 100644
--- a/src/src/WebServer/JSON.cpp
+++ b/src/src/WebServer/JSON.cpp
@@ -251,6 +251,7 @@ void handle_json()
#if FEATURE_USE_IPV6
LabelType::IP6_LOCAL,
LabelType::IP6_GLOBAL,
+ LabelType::ENABLE_IPV6,
#endif
LabelType::IP_SUBNET,
LabelType::GATEWAY,
@@ -288,6 +289,10 @@ void handle_json()
LabelType::WIFI_USE_LAST_CONN_FROM_RTC,
LabelType::WIFI_RSSI,
+ LabelType::WAIT_WIFI_CONNECT,
+ LabelType::HIDDEN_SSID_SLOW_CONNECT,
+ LabelType::CONNECT_HIDDEN_SSID,
+ LabelType::SDK_WIFI_AUTORECONNECT,
LabelType::MAX_LABEL
};
diff --git a/src/src/WebServer/RootPage.cpp b/src/src/WebServer/RootPage.cpp
index f7c8b2f15a..e7065ef6d9 100644
--- a/src/src/WebServer/RootPage.cpp
+++ b/src/src/WebServer/RootPage.cpp
@@ -197,8 +197,10 @@ void handle_root() {
{
addRowLabelValue(LabelType::IP_ADDRESS);
#if FEATURE_USE_IPV6
- addRowLabelValue(LabelType::IP6_LOCAL);
- // Do not show global IPv6 on the root page
+ if (Settings.EnableIPv6()) {
+ addRowLabelValue(LabelType::IP6_LOCAL);
+ // Do not show global IPv6 on the root page
+ }
#endif
addRowLabel(LabelType::WIFI_RSSI);
addHtml(strformat(
@@ -213,8 +215,10 @@ void handle_root() {
addRowLabelValue(LabelType::ETH_SPEED_STATE);
addRowLabelValue(LabelType::ETH_IP_ADDRESS);
#if FEATURE_USE_IPV6
- addRowLabelValue(LabelType::ETH_IP6_LOCAL);
- // Do not show global IPv6 on the root page
+ if (Settings.EnableIPv6()) {
+ addRowLabelValue(LabelType::ETH_IP6_LOCAL);
+ // Do not show global IPv6 on the root page
+ }
#endif
}
# endif // if FEATURE_ETHERNET
@@ -337,8 +341,10 @@ void handle_root() {
if (it->second.ip[0] != 0
#if FEATURE_USE_IPV6
- || it->second.hasIPv6_mac_based_link_local
- || it->second.hasIPv6_mac_based_link_global
+ || (Settings.EnableIPv6() &&
+ (it->second.hasIPv6_mac_based_link_local ||
+ it->second.hasIPv6_mac_based_link_global)
+ )
#endif
)
{
@@ -347,12 +353,14 @@ void handle_root() {
#if FEATURE_USE_IPV6
bool isIPv6 = false;
- if (it->second.hasIPv6_mac_based_link_local) {
- ip = it->second.IPv6_link_local(true);
- isIPv6 = true;
- } else if (it->second.hasIPv6_mac_based_link_global) {
- ip = it->second.IPv6_global();
- isIPv6 = true;
+ if (Settings.EnableIPv6()) {
+ if (it->second.hasIPv6_mac_based_link_local) {
+ ip = it->second.IPv6_link_local(true);
+ isIPv6 = true;
+ } else if (it->second.hasIPv6_mac_based_link_global) {
+ ip = it->second.IPv6_global();
+ isIPv6 = true;
+ }
}
if (it->second.hasIPv4 && it->second.hasIPv6()) {
// Add 2 buttons for IPv4 and IPv6 address
diff --git a/src/src/WebServer/SysInfoPage.cpp b/src/src/WebServer/SysInfoPage.cpp
index e8ba02427e..c660d44c39 100644
--- a/src/src/WebServer/SysInfoPage.cpp
+++ b/src/src/WebServer/SysInfoPage.cpp
@@ -121,8 +121,10 @@ void handle_sysinfo_json() {
json_prop(F("dhcp"), useStaticIP() ? getLabel(LabelType::IP_CONFIG_STATIC) : getLabel(LabelType::IP_CONFIG_DYNAMIC));
json_prop(F("ip"), getValue(LabelType::IP_ADDRESS));
#if FEATURE_USE_IPV6
- json_prop(F("ip6_local"), getValue(LabelType::IP6_LOCAL));
- json_prop(F("ip6_global"), getValue(LabelType::IP6_GLOBAL));
+ if (Settings.EnableIPv6()) {
+ json_prop(F("ip6_local"), getValue(LabelType::IP6_LOCAL));
+ json_prop(F("ip6_global"), getValue(LabelType::IP6_GLOBAL));
+ }
#endif
json_prop(F("subnet"), getValue(LabelType::IP_SUBNET));
@@ -153,7 +155,8 @@ void handle_sysinfo_json() {
json_prop(F("ethstate"), getValue(LabelType::ETH_STATE));
json_prop(F("ethspeedstate"), getValue(LabelType::ETH_SPEED_STATE));
#if FEATURE_USE_IPV6
- json_prop(F("ethipv6local"), getValue(LabelType::ETH_IP6_LOCAL));
+ if (Settings.EnableIPv6())
+ json_prop(F("ethipv6local"), getValue(LabelType::ETH_IP6_LOCAL));
#endif
json_close();
# endif // if FEATURE_ETHERNET
@@ -542,6 +545,9 @@ void handle_sysinfo_WiFiSettings() {
LabelType::HIDDEN_SSID_SLOW_CONNECT,
LabelType::CONNECT_HIDDEN_SSID,
LabelType::SDK_WIFI_AUTORECONNECT,
+#if FEATURE_USE_IPV6
+ LabelType::ENABLE_IPV6,
+#endif
LabelType::MAX_LABEL
};