diff --git a/docs/source/Plugin/P169.rst b/docs/source/Plugin/P169.rst
new file mode 100644
index 0000000000..4d0b9bfee3
--- /dev/null
+++ b/docs/source/Plugin/P169.rst
@@ -0,0 +1,362 @@
+.. include:: ../Plugin/_plugin_substitutions_p16x.repl
+.. _P169_page:
+
+|P169_typename|
+==================================================
+
+|P169_shortinfo|
+
+Plugin details
+--------------
+
+Type: |P169_type|
+
+Name: |P169_name|
+
+Status: |P169_status|
+
+GitHub: |P169_github|_
+
+Maintainer: |P169_maintainer|
+
+Used libraries: |P169_usedlibraries|
+
+Description
+-----------
+
+The AS3935 is a programmable fully integrated Lightning Sensor IC that detects the presence and approach of potentially
+hazardous lightning activity in the vicinity and provides an estimation on the distance to the head of the storm.
+The embedded lightning algorithm checks the incoming signal pattern to reject the potential man-made disturbers.
+
+Highlights:
+
+* Can detect lightning storm activity within a 40 km range.
+* Provides distance estimation to the head of the storm.
+* Detects both cloud-to-ground and intra-cloud (cloud-to-cloud) flashes.
+* Internal algorithm to reject false disturbances.
+
+
+
+This chip can be found on a number of boards, like this one from DFRobot.
+
+.. image:: P169_dfrobot-gravity-lightning-distance-sensor-as3935-600x600.jpg
+
+(Image (c) DFRobot)
+
+
+Calibration Procedure
+---------------------
+
+The AS3935 sensor is using 3 separate oscillators:
+
+* **LCO**: 500 kHz resonance frequency used to tune the antenna.
+* **SRCO**: Typ. ~1.1 MHz signal used internally in the sensor.
+* **TRCO**: Typ. 32768 Hz signal used internally in the sensor.
+
+The LCO oscillator needs to be calibrated within 3.5% of its intended 500 kHz.
+For this the sensor can connect upto 15 tuning capacitors of 8 pF parallel to another capacitor to tune the resonance frequency of the antenna to 500 kHz.
+
+The frequency of all of these three oscillators depends on environmental factors like temperature, but also the presence of other materials close to the antenna.
+
+The LCO calibration is performed by feeding the LCO clock via some divisor to a GPIO pin on the ESP board.
+This signal is then measured several times to find the best tuning capacitor.
+
+By default this LCO calibration does a quick test with antenna capacitor ``0`` and ``15`` and then computes the most likely capacitor.
+This candidate and its neighbors are then measured for a longer period (about 30 msec) to reduce the error in measurement.
+
+When the checkbox for "Slow LCO Calibration" is checked, each antenna capacitor is tested for about 30 msec.
+This may improve the accuracy and the success rate of the calibration.
+
+Below the calibration charts of a quick and slow LCO calibration of the same sensor:
+
+.. image:: P169_Quick_LCO_calibration_curve.png
+ :width: 500
+ :alt: Quick LCO Calibration Curve
+
+.. image:: P169_Slow_LCO_calibration_curve.png
+ :width: 500
+ :alt: Slow LCO Calibration Curve
+
+As can be seen, both were successful in calibrating the resonance frequency within 3.5% of 500 kHz.
+
+However, the best one on this specific board and setup is antenna capacitor 15, which is the last one.
+So in this specific setup, it is very well possible the calibration may fail when some external factor changes. (e.g. temperature)
+
+For setups like these, where the best option is close to the edge of the adjustable range, it is best to check the checkbox "Tolerate out-of-range calibration".
+This way the calibration will not be considered failed when the tolerance ends up slightly above 3.5%.
+
+On the other hand, if the best calibration is significantly further off from the optimal 500 kHz, there is something wrong with the setup.
+
+For example:
+
+* Metal parts mounted close to the antenna.
+* Noisy environment. (See also the reported noise level, as this should be around 2 or 3)
+* Unstable power supplied to the sensor.
+
+See the Wiring section below for more tips.
+
+
+.. note:: The **SRCO** and **TRCO** frequencies are calibrated after the **LCO** frequency. When the **LCO** frequency is off by too much, the calibration of the other two may also fail.
+
+
+Sensor Operating Modes
+----------------------
+
+The sensor can signal some event via the IRQ pin to ESPEasy.
+This pin state remains high until the sensor state is read.
+
+Power-Down Mode
+^^^^^^^^^^^^^^^
+
+This mode is set when ESPEasy enters deep sleep to reduce current consumption
+(typ 1μA).
+
+Listening Mode
+^^^^^^^^^^^^^^
+
+The sensor will be operating in this mode for most of the time.
+Typical current consumption in this mode is about 60μA. (70μA when the internal voltage regulator is enabled)
+
+There will always be some noise picked up by the antenna.
+When this noise exceeds the set noise floor, the sensor will pull the IRQ pin high and return to "Listening mode".
+
+ESPEasy will then try to increase the noise floor.
+
+After 15 seconds of not receiving any interrupt signal, ESPEasy will try to lower the noise floor.
+
+Every time the set watchdog threshold is passed, the sensor will enter "Signal Validation" mode.
+
+
+Signal Validation Mode
+^^^^^^^^^^^^^^^^^^^^^^
+
+Typical current consumption in this mode is about 350μA.
+
+In case the incoming signal does not have the shape characteristic to lightning, the signal validation fails and the
+event is classified as disturber.
+
+If the signal is classified as disturber the chip immediately aborts the signal processing and goes back into the "Listening Mode".
+Otherwise, the energy calculation is performed and the distance estimate provided.
+
+.. note:: The calculated energy does not reflect any physical unit of measure. It is just a number. However it seems the sensor does use it internally to estimate the distance.
+
+The received signal of a typical lightning strike consists of 3 .. 4 pulses about 40 msec apart.
+
+According to the datasheet, the sensor needs roughly 1 second to classify an event as a lightning strike.
+However tests have shown the sensor might be able to classify _some_ lightning strikes in as little as 250 msec.
+
+If the classification takes longer than 1.5 seconds, it will be classified as a disturber.
+
+This implies the practial shortest time span between two lightning strikes that the sensor can resolve is approximately one second.
+
+.. note:: Sometimes during intense thunderstorms there might be several lightning strikes in short succession. This sensor might classify those as disturbances as the total evaluation time might exceed 1.5 seconds.
+
+As soon as the event has been classified, the sensor will return to "Listening Mode".
+
+
+Signal Validation Parameters
+----------------------------
+
+During the signal validation phase the shape of the incoming signal is analyzed.
+The sensor can differentiate between signals that show the pattern characteristic of lightning strikes and man-made disturbers such as random impulses.
+
+Noise Floor
+^^^^^^^^^^^
+
+Range: ``0`` .. ``7`` (default: ``2``)
+
+The noise floor acts as a threshold to differentiate real signals from noise.
+
+When an interrupt signal for "noise floor threshold exceeded" is received, ESPEasy will increase the noise floor.
+
+
+Watchdog
+^^^^^^^^
+
+Range: ``0`` .. ``15`` (default: ``2``)
+
+This sets the duration for a signal to last before entering "Signal Validation" mode.
+
+Spike Rejection
+^^^^^^^^^^^^^^^
+
+Range: ``0`` .. ``15`` (default: ``2``)
+
+Can be used to increase the robustness against false alarms from such disturbers.
+Larger values correspond to more robust disturber rejection, yet with the
+drawback of a decrease in detection efficiency.
+
+When an "disturber detected" event is triggered, ESPEasy will try to increase either the Spike Rejection setting or Watchdog.
+
+
+AFE gain and Energy level
+^^^^^^^^^^^^^^^^^^^^^^^^^
+
+The AS3935 sensor does have an amplifier with an adjustable gain factor which amplifies the incoming signal.
+This amplified signal is then used to estimate the "Energy" value.
+
+Tests have shown the AS3935 sensor does use a lookup table to estimate the distance based on this energy level.
+
+
+
+Dynamic Adjustment
+^^^^^^^^^^^^^^^^^^
+
+ESPEasy does dynamically change these three settings.
+Upon interrupt signals for either noise floor exceeded or disturbance detected, these will be increased.
+
+After 15 seconds ESPEasy will try to lower these values again to get as close as possible to the optimal settings.
+
+
+Lightning Threshold
+^^^^^^^^^^^^^^^^^^^
+
+Values: ``1``, ``5``, ``9``, ``16`` (default: ``1``)
+
+This set minimum number of lightning events counted within 15 minutes must occur before the first "Lightning detected" interrupt is sent.
+Once this threshold is passed, the sensor will resume its normal interrupt handling with an interrupt per detected lightning.
+
+This internal count will also be cleared when the internal statistics are cleared or when the sensor is put to sleep. (when the ESPEasy task is ended, the sensor is put to sleep)
+
+
+Wiring
+------
+
+This sensor can be used via SPI or I2C.
+
+For ESPEasy it needs to be wired for I2C:
+
+* ``SI`` pin ("Select Interface") must be pulled high.
+* ``MOSI`` pin is I2C SDA.
+* ``SCL`` pin is I2C SCL.
+* ``A1`` and ``A0`` pulled high for the default address of ``0x03``.
+
+.. image:: P169_Guideline_I2C_PullUp_resistors.png
+ :width: 700
+ :alt: Guideline for Pull Up Resistors on I2CL and I2CD
+
+
+Apart from the I2C connection, there is an ``IRQ`` signal which must also be connected to the ESP board.
+This IRQ signal is used to calibrate the sensor as well as to signal the ESP about some changed condition like a detected lightning strike or noise disturbances.
+
+Board Specific
+^^^^^^^^^^^^^^
+
+Some boards already have resistors present to pull the unused pins high or low where needed and are default set for I2C.
+For example 8-pin brown or purple boards with ``GY-AS3935`` written on the back is default configured for I2C with address ``0x03``.
+
+Other boards like a slightly larger 11-pin purple board with ``WCMCU-3935`` written on the board,
+might need to have the unused pins (``CS`` & ``MISO``) explicitly pulled to GND.
+The ``EN-V`` pin should be pulled high.
+
+
+Power Supply and Noise
+^^^^^^^^^^^^^^^^^^^^^^
+
+This sensor is quite sensitive to noise in its direct surroundings.
+Especially signals around 500 kHz will cause this sensor to perform significantly worse.
+
+This sensor needs to have its resonance frequency calibrated within 3.5% of 500 kHz.
+
+Some tips:
+
+* Keep DC/DC converters and other power supplies away from this sensor.
+* Keep smart phone and smart watch displays away from the sensor.
+* Add some 100 uF ... 220 uF capacitor close to the power supply pins of the sensor.
+* Do not use any metal close to the antenna of the sensor as this will change the antenna resonance frequency.
+* Use short wires.
+* Lower I2C clock frequency to 100 kHz for all I2C devices on this ESPEasy board.
+* Lower I2C clock frequency on devices within a few meters from this sensor. (Explicitly stay away from 500 kHz)
+* Do not leave unused pins 'floating'. Either pull them to 3V3 or GND.
+* Orientation of the antenna is not really important to measure lightning strikes, since lightning is not discharging straight, but in a zigzag pattern and the distance is very far away. However the orientation of the antenna can have an effect on the noise picked up from nearby sources.
+
+The sensor chip does have an internal voltage regulator.
+
+On some boards, this regulator can be enabled by pulling the ``EN-V`` or ``EN_VREG`` pin high (if made availabe on the board).
+
+Onboard voltage regulator:
+
+* Enabled: ``EN_VREG`` pin high, ``VREG`` via 1uF to GND. Supply voltage range is 2.4V to 5.5V
+* Disabled: ``EN_VREG`` pin low, ``VREG`` connected to VDD pin. Supply voltage range is 2.4V to 3.6V
+
+With the onboard voltage regulator enabled, the current consumption will be slight higher.
+But the supplied voltage to the sensor will be more stable.
+
+See the `datasheet `_ pages 15 and 16 for more information.
+
+.. image:: P169_VoltageRegulator_OFF.png
+ :width: 500
+ :alt: AS3935 Application Diagram (Voltage Regulator OFF, I²C Active)
+
+.. image:: P169_VoltageRegulator_ON.png
+ :width: 500
+ :alt: AS3935 Application Diagram (Voltage Regulator ON, I²C Active)
+
+
+Configuration
+-------------
+
+.. image:: P169_DeviceConfiguration.png
+
+* **Name**: Required by ESPEasy, must be unique among the list of available devices/tasks.
+
+* **Enabled**: The device can be disabled or enabled. When not enabled the device should not use any resources.
+
+I2C options
+^^^^^^^^^^^
+
+* **I2C Address**: The device supports 83 addresses, and by default comes configured for address ``0x03``.
+
+Available addresses are in the range ``0x01`` to ``0x03``.
+
+The available I2C settings here depend on the build used. At least the **Force Slow I2C speed** option is available, but selections for the I2C Multiplexer can also be shown. For details see the :ref:`Hardware_page`
+
+Device Settings
+^^^^^^^^^^^^^^^
+
+* **IRQ**: Configure the GPIO pin on the ESP board connected to the IRQ pin of the sensor.
+ This pin is used for both the antenna calibration as well as to notify the ESP board of any lightning strike or detected disturbance.
+* **Lightning Threshold**: Minimum number of detected strikes in 15 minutes to let the sensor trigger the IRQ pin.
+* **Mode**: Set the Analog Front-End (AFE) gain for typical indoor/outdoor use case.
+* **Ignore Disturbance**: The sensor may trigger the IRQ pin to signal a lightning strike, high noise or detected disturbances. With "Ignore Disturbance" checked, this last one is ignored.
+
+
+
+
+Current Sensor Data
+^^^^^^^^^^^^^^^^^^^
+
+
+Data Acquisition
+^^^^^^^^^^^^^^^^
+
+This group of settings, **Single event with all values** and **Send to Controller** settings are standard available configuration items. Send to Controller is only visible when one or more Controllers are configured.
+
+* **Interval** By default, Interval will be set to 0 sec. The data will be collected and optionally sent to any configured controllers using this interval. When an output value is changed, the data will be sent to any configured controller, and an event will also be generated when the Rules are enabled (Tools/Advanced).
+
+Values
+^^^^^^
+
+
+In selected builds, per Value is a **Stats** checkbox available, that when checked, gathers the data and presents recent data in a graph, as described here: :ref:`Task Value Statistics: `
+
+Commands available
+^^^^^^^^^^^^^^^^^^
+
+.. include:: P169_commands.repl
+
+Get Config Values
+^^^^^^^^^^^^^^^^^
+
+
+.. include:: P169_config_values.repl
+
+Change log
+----------
+
+.. versionchanged:: 2.0
+ ...
+
+ |added|
+ 2024-05-24 Initial release version.
+
diff --git a/docs/source/Plugin/P169_DeviceConfiguration.png b/docs/source/Plugin/P169_DeviceConfiguration.png
new file mode 100644
index 0000000000..3f8594c8fc
Binary files /dev/null and b/docs/source/Plugin/P169_DeviceConfiguration.png differ
diff --git a/docs/source/Plugin/P169_Guideline_I2C_PullUp_resistors.png b/docs/source/Plugin/P169_Guideline_I2C_PullUp_resistors.png
new file mode 100644
index 0000000000..5a86da14a9
Binary files /dev/null and b/docs/source/Plugin/P169_Guideline_I2C_PullUp_resistors.png differ
diff --git a/docs/source/Plugin/P169_Quick_LCO_calibration_curve.png b/docs/source/Plugin/P169_Quick_LCO_calibration_curve.png
new file mode 100644
index 0000000000..ce7dd45ba9
Binary files /dev/null and b/docs/source/Plugin/P169_Quick_LCO_calibration_curve.png differ
diff --git a/docs/source/Plugin/P169_Slow_LCO_calibration_curve.png b/docs/source/Plugin/P169_Slow_LCO_calibration_curve.png
new file mode 100644
index 0000000000..dbb2003658
Binary files /dev/null and b/docs/source/Plugin/P169_Slow_LCO_calibration_curve.png differ
diff --git a/docs/source/Plugin/P169_Stats_no_lightning_strikes.png b/docs/source/Plugin/P169_Stats_no_lightning_strikes.png
new file mode 100644
index 0000000000..14efb88758
Binary files /dev/null and b/docs/source/Plugin/P169_Stats_no_lightning_strikes.png differ
diff --git a/docs/source/Plugin/P169_VoltageRegulator_OFF.png b/docs/source/Plugin/P169_VoltageRegulator_OFF.png
new file mode 100644
index 0000000000..528158381d
Binary files /dev/null and b/docs/source/Plugin/P169_VoltageRegulator_OFF.png differ
diff --git a/docs/source/Plugin/P169_VoltageRegulator_ON.png b/docs/source/Plugin/P169_VoltageRegulator_ON.png
new file mode 100644
index 0000000000..38c6c78f3a
Binary files /dev/null and b/docs/source/Plugin/P169_VoltageRegulator_ON.png differ
diff --git a/docs/source/Plugin/P169_commands.repl b/docs/source/Plugin/P169_commands.repl
new file mode 100644
index 0000000000..3c28b5e654
--- /dev/null
+++ b/docs/source/Plugin/P169_commands.repl
@@ -0,0 +1,37 @@
+.. csv-table::
+ :header: "Command Syntax", "Extra information"
+ :widths: 20, 30
+
+ "
+ ``as3935,clearstats``
+ ","
+ Clear statistics in the sensor, like lightning strike counts and intermediate values used to estimate the distance of the storm front.
+ "
+ "
+ ``as3935,calibrate``
+ ","
+ Perform calibration of all oscillators in the sensor.
+ "
+ "
+ ``as3935,setgain,``
+ ","
+ Set the AFE gain to given value.
+ Input can either be the internal sensor register value of ``10`` ... ``18`` or a floating point value signifying the gain factor.
+ This latter one will then be matched to the closest matching internal register value.
+ The floating point factor values range from ``0.30x`` ... ``3.34x`` .
+ "
+ "
+ ``as3935,setnf,``
+ ","
+ Set the Noise Floor threshold to given value.
+ "
+ "
+ ``as3935,setwd,``
+ ","
+ Set the Watchdog threshold to given value.
+ "
+ "
+ ``as3935,setsrej,``
+ ","
+ Set Spike Rejection threshold to given value.
+ "
diff --git a/docs/source/Plugin/P169_config_values.repl b/docs/source/Plugin/P169_config_values.repl
new file mode 100644
index 0000000000..2f7f01749b
--- /dev/null
+++ b/docs/source/Plugin/P169_config_values.repl
@@ -0,0 +1,25 @@
+.. csv-table::
+ :header: "Config value", "Information"
+ :widths: 20, 30
+
+ "
+ ``[#noisefloor]``
+ ","
+ Returns the current set Noise Floor threshold of the sensor.
+ "
+ "
+ ``[#watchdog]``
+ ","
+ Returns the current set Watchdog threshold of the sensor.
+ "
+ "
+ ``[#srej]``
+ ","
+ Returns the current set Spike Rejection threshold of the sensor.
+ "
+ "
+ ``[#gain]``
+ ","
+ Returns the current active AFE gain factor of the sensor. (``0.30x`` ... ``3.34x``)
+ "
+
diff --git a/docs/source/Plugin/P169_dfrobot-gravity-lightning-distance-sensor-as3935-600x600.jpg b/docs/source/Plugin/P169_dfrobot-gravity-lightning-distance-sensor-as3935-600x600.jpg
new file mode 100644
index 0000000000..477f83cbe1
Binary files /dev/null and b/docs/source/Plugin/P169_dfrobot-gravity-lightning-distance-sensor-as3935-600x600.jpg differ
diff --git a/docs/source/Plugin/_Plugin.rst b/docs/source/Plugin/_Plugin.rst
index 6778608dd0..73dead336b 100644
--- a/docs/source/Plugin/_Plugin.rst
+++ b/docs/source/Plugin/_Plugin.rst
@@ -392,6 +392,7 @@ There are different released versions of ESP Easy:
":ref:`P166_page`","|P166_status|","P166"
":ref:`P167_page`","|P167_status|","P167"
":ref:`P168_page`","|P168_status|","P168"
+ ":ref:`P169_page`","|P169_status|","P169"
":ref:`P170_page`","|P170_status|","P170"
diff --git a/docs/source/Plugin/_plugin_categories.repl b/docs/source/Plugin/_plugin_categories.repl
index c491645ee7..cf7d3e5f8f 100644
--- a/docs/source/Plugin/_plugin_categories.repl
+++ b/docs/source/Plugin/_plugin_categories.repl
@@ -8,7 +8,7 @@
.. |Plugin_Energy_AC| replace:: :ref:`P076_page`, :ref:`P077_page`, :ref:`P078_page`, :ref:`P102_page`, :ref:`P108_page`
.. |Plugin_Energy_DC| replace:: :ref:`P027_page`, :ref:`P085_page`, :ref:`P115_page`, :ref:`P132_page`
.. |Plugin_Energy_Heat| replace:: :ref:`P088_page`, :ref:`P093_page`
-.. |Plugin_Environment| replace:: :ref:`P004_page`, :ref:`P005_page`, :ref:`P006_page`, :ref:`P014_page`, :ref:`P024_page`, :ref:`P028_page`, :ref:`P030_page`, :ref:`P031_page`, :ref:`P032_page`, :ref:`P034_page`, :ref:`P039_page`, :ref:`P047_page`, :ref:`P051_page`, :ref:`P068_page`, :ref:`P069_page`, :ref:`P072_page`, :ref:`P103_page`, :ref:`P105_page`, :ref:`P106_page`, :ref:`P122_page`, :ref:`P150_page`, :ref:`P151_page`, :ref:`P153_page`, :ref:`P154_page`, :ref:`P167_page`
+.. |Plugin_Environment| replace:: :ref:`P004_page`, :ref:`P005_page`, :ref:`P006_page`, :ref:`P014_page`, :ref:`P024_page`, :ref:`P028_page`, :ref:`P030_page`, :ref:`P031_page`, :ref:`P032_page`, :ref:`P034_page`, :ref:`P039_page`, :ref:`P047_page`, :ref:`P051_page`, :ref:`P068_page`, :ref:`P069_page`, :ref:`P072_page`, :ref:`P103_page`, :ref:`P105_page`, :ref:`P106_page`, :ref:`P122_page`, :ref:`P150_page`, :ref:`P151_page`, :ref:`P153_page`, :ref:`P154_page`, :ref:`P167_page`, :ref:`P169_page`
.. |Plugin_Extra_IO| replace:: :ref:`P011_page`, :ref:`P022_page`
.. |Plugin_Gases| replace:: :ref:`P049_page`, :ref:`P052_page`, :ref:`P083_page`, :ref:`P090_page`, :ref:`P117_page`, :ref:`P127_page`, :ref:`P135_page`, :ref:`P145_page`, :ref:`P147_page`, :ref:`P164_page`
.. |Plugin_Generic| replace:: :ref:`P003_page`, :ref:`P026_page`, :ref:`P033_page`, :ref:`P037_page`, :ref:`P081_page`, :ref:`P100_page`, :ref:`P146_page`
diff --git a/docs/source/Plugin/_plugin_sets_overview.repl b/docs/source/Plugin/_plugin_sets_overview.repl
index 9bc869c87f..2b2f929133 100644
--- a/docs/source/Plugin/_plugin_sets_overview.repl
+++ b/docs/source/Plugin/_plugin_sets_overview.repl
@@ -2391,6 +2391,7 @@ Build set: :yellow:`CLIMATE`
":ref:`P154_page`","P154"
":ref:`P164_page`","P164"
":ref:`P167_page`","P167"
+ ":ref:`P169_page`","P169"
":ref:`C001_page`","C001"
":ref:`C002_page`","C002"
":ref:`C003_page`","C003"
@@ -2963,6 +2964,7 @@ Build set: :yellow:`MAX`
":ref:`P164_page`","P164"
":ref:`P166_page`","P166"
":ref:`P167_page`","P167"
+ ":ref:`P169_page`","P169"
":ref:`C001_page`","C001"
":ref:`C002_page`","C002"
":ref:`C003_page`","C003"
diff --git a/docs/source/Plugin/_plugin_substitutions_p16x.repl b/docs/source/Plugin/_plugin_substitutions_p16x.repl
index 7cf8b8c8c7..4caabb115f 100644
--- a/docs/source/Plugin/_plugin_substitutions_p16x.repl
+++ b/docs/source/Plugin/_plugin_substitutions_p16x.repl
@@ -61,4 +61,17 @@
.. |P168_shortinfo| replace:: `VEML6030/VEML7700 Light/Lux sensor`
.. |P168_maintainer| replace:: `tonhuisman`
.. |P168_compileinfo| replace:: `.`
-.. |P168_usedlibraries| replace:: `modified version of Adafruit_VEML7700`
\ No newline at end of file
+.. |P168_usedlibraries| replace:: `modified version of Adafruit_VEML7700`
+
+.. |P169_name| replace:: :cyan:`AS3935 Lightning Detector`
+.. |P169_type| replace:: :cyan:`Environment`
+.. |P169_typename| replace:: :cyan:`Environment - AS3935 Lightning Detector`
+.. |P169_porttype| replace:: `.`
+.. |P169_status| replace:: :yellow:`CLIMATE`
+.. |P169_github| replace:: P169_AS3935_LightningDetector.ino
+.. _P169_github: https://github.com/letscontrolit/ESPEasy/blob/mega/src/_P169_AS3935_LightningDetector.ino
+.. |P169_usedby| replace:: `.`
+.. |P169_shortinfo| replace:: `AS3935 Franklin Lightning Sensor IC / Lightning Detector`
+.. |P169_maintainer| replace:: `TD-er`
+.. |P169_compileinfo| replace:: `.`
+.. |P169_usedlibraries| replace:: `https://bitbucket.org/christandlg/as3935mi/src/master/`
diff --git a/docs/source/Reference/Command.rst b/docs/source/Reference/Command.rst
index aa6439bded..d667bf64f5 100644
--- a/docs/source/Reference/Command.rst
+++ b/docs/source/Reference/Command.rst
@@ -815,6 +815,11 @@ P167 :ref:`P167_page`
.. include:: ../Plugin/P167_commands.repl
+P169 :ref:`P169_page`
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+.. include:: ../Plugin/P169_commands.repl
+
.. .. *** Insert regular plugin commands above this remark! ***
.. _AdafruitGFX Helper commands:
diff --git a/docs/source/Reference/URLs.rst b/docs/source/Reference/URLs.rst
index d7e00e7ec9..8c686fe5ea 100644
--- a/docs/source/Reference/URLs.rst
+++ b/docs/source/Reference/URLs.rst
@@ -38,6 +38,13 @@ At the root of the JSON output is a value names ``TTL`` which reflects the lowes
N.B. task nr starts at 1.
"
+ "
+ ``http:///json?view=sensorupdate&tasknr=1&showpluginstats=1``
+ ","
+ All task values of a specific task nr and needed information to format the values, including all info needed to create ChartJS charts when ``stats`` has been enabled for that task.
+
+ N.B. task nr starts at 1.
+ "
@@ -87,3 +94,4 @@ N.B. task number and variable number do count starting at 0.
Control
-------
+
diff --git a/lib/AS3935MI/README.md b/lib/AS3935MI/README.md
new file mode 100644
index 0000000000..05428435cd
--- /dev/null
+++ b/lib/AS3935MI/README.md
@@ -0,0 +1,84 @@
+# Yet Another Arduino ams AS3935 'Franklin' lightning sensor library
+home: https://bitbucket.org/christandlg/as3935mi
+sensor: https://ams.com/as3935
+
+## Features:
+ - Supports I2C and SPI via the Wire and SPI libraries, respectively
+ - Supports I2C and SPI interfaces via other libraries (e.g. Software I2C) by inheritance
+ - Automatic antenna tuning
+
+## Changelog:
+- 1.3.5
+ - fixed #50
+ - implemented a more robust, interrupt based calibration procedure that is also faster. thanks to @td-er for reporting and impementing this.
+ - updated increase / decrease function signatures (should be backwards compatible)
+ - updated examples
+
+- 1.3.4
+ - partially fixed https://bitbucket.org/christandlg/as3935mi/issues/50/resonance-frequency-calibration-inaccurate : fixed a bug where occasionally I2C comms will silently fail during oscillator calibration - thanks to @td-er for reporting and fixing this issue
+
+- 1.3.3
+ - fixed https://bitbucket.org/christandlg/as3935mi/issues/49/class-spiclass-has-no-member-named
+
+- 1.3.2
+ - fixed https://bitbucket.org/christandlg/as3935mi/issues/47/need-help-using-as3935spiclass
+
+- 1.3.1
+ - fixed https://bitbucket.org/christandlg/as3935mi/issues/48/clear-statistics-function-to-be-added
+
+- 1.3.0
+ - fixed https://bitbucket.org/christandlg/as3935mi/issues/12/autocalibrate-no-longer-working
+
+- 1.2.1
+ - Merged PR by Hernán Freschi https://bitbucket.org/christandlg/as3935mi/pull-requests/2
+
+- 1.2.0
+ - extended examples to include increasing sensitivity if no disturbances are detected.
+
+- 1.1.1
+ - fixed an issue where ESP8266 would crash with message "ISR not in IRAM"
+
+- 1.1.0
+ - extended function calibrateResonanceFrequency() to return the resonance frequency of the antenna
+
+- 1.0.0
+ - added more values for watchdog threshold and spike rejection ratio settings
+ - fixed an incorrect function name
+ - added missing function names to keywords.txt
+
+- 0.5.0
+ - added new classes AS3935TwoWire and AS3935SPIClass for TwoWire and SPIClass interfaces
+ - moved AS3935I2C and AS3935SPI classes into their own respecitve source files, further separating data processing from communications
+ - when updating from an earlier version and using the AS3935I2C or AS3935SPI classes, change ```#include ``` to ```#include ``` or ```#include ```, respectively
+ - added examples for AS3935TwoWire and AS3935SPIClass
+
+- 0.4.1
+ - fixed an issue where checkIRQ() causes a deadlock on Arduino Nano
+
+- 0.4.0
+ - added functions to increase / decrease noise floor threshold
+ - added functions to increase / decrease watchdog threshold
+ - added functions to increase / decrease spike rejection ratio
+ - added function to check communication to sensor
+ - added function to check IRQ pin assignment
+
+- 0.3.0
+ - derived classes must now implement function beginInterface() instead of begin()
+
+- 0.2.0
+ - split code into 3 classes - AS3935MI, AS3935I2C, AS3935SPI
+ - users can now implement classes derived from AS3935MI easily
+ - writeRegister is nur used to send direct commands
+ - updated examples
+ - added arduino due I2C issue workaround
+ - minor fixes
+ - derived
+
+- 0.1.2
+ - renamed library
+
+- 0.1.1
+ - added license information
+
+- 0.1.0
+ - initial release
\ No newline at end of file
diff --git a/lib/AS3935MI/examples/AS3935MI_LightningDetector_I2C/AS3935MI_LightningDetector_I2C.ino b/lib/AS3935MI/examples/AS3935MI_LightningDetector_I2C/AS3935MI_LightningDetector_I2C.ino
new file mode 100644
index 0000000000..9a300fdbf2
--- /dev/null
+++ b/lib/AS3935MI/examples/AS3935MI_LightningDetector_I2C/AS3935MI_LightningDetector_I2C.ino
@@ -0,0 +1,264 @@
+// AS3935MI_LightningDetector_I2C.ino
+//
+// shows how to use the AS3935MI library with the lightning sensor connected using I2C.
+//
+// Copyright (c) 2018-2019 Gregor Christandl
+//
+// connect the AS3935 to the Arduino like this:
+//
+// Arduino - AS3935
+// 5V ------ VCC
+// GND ----- GND
+// D2 ------ IRQ must be a pin supporting external interrupts, e.g. D2 or D3 on an Arduino Uno.
+// SDA ----- MOSI
+// SCL ----- SCL
+// 5V ------ SI (activates I2C for the AS3935)
+// 5V ------ A0 (sets the AS3935' I2C address to 0x01)
+// GND ----- A1 (sets the AS3935' I2C address to 0x01)
+// 5V ------ EN_VREG !IMPORTANT when using 5V Arduinos (Uno, Mega2560, ...)
+// other pins can be left unconnected.
+
+#include
+#include
+
+#include
+
+#ifdef D1
+#define PIN_IRQ D1
+#else
+#define PIN_IRQ 2
+#endif
+
+//create an AS3935 object using the I2C interface, I2C address 0x01 and IRQ pin number 2
+AS3935I2C as3935(AS3935I2C::AS3935I2C_A01, PIN_IRQ);
+
+//this value will be set to true by the AS3935 interrupt service routine.
+volatile bool interrupt_ = false;
+
+constexpr uint32_t SENSE_INCREASE_INTERVAL = 15000; //15 s sensitivity increase interval
+uint32_t sense_adj_last_ = 0L; //time of last sensitivity adjustment
+
+void setup() {
+ // put your setup code here, to run once:
+ Serial.begin(9600);
+
+ //wait for serial connection to open (only necessary on some boards)
+ while (!Serial);
+
+ //set the IRQ pin as an input pin. do not use INPUT_PULLUP - the AS3935 will pull the pin
+ //high if an event is registered.
+ pinMode(PIN_IRQ, INPUT);
+
+
+#if defined(ESP8266)
+ Wire.begin(D2, D3);
+#else
+ Wire.begin(); //for Arduino boards
+#endif
+
+ //begin() checks the Interface and I2C Address passed to the constructor and resets the AS3935 to
+ //default values.
+ if (!as3935.begin())
+ {
+ Serial.println("begin() failed. Check the I2C address passed to the AS3935I2C constructor. ");
+ while (1);
+ }
+
+ //check I2C connection.
+ if (!as3935.checkConnection())
+ {
+ Serial.println("checkConnection() failed. check your I2C connection and I2C Address. ");
+ while (1);
+ }
+ else
+ Serial.println("I2C connection check passed. ");
+
+ //check the IRQ pin connection.
+ if (!as3935.checkIRQ())
+ {
+ Serial.println("checkIRQ() failed. check if the correct IRQ pin was passed to the AS3935I2C constructor. ");
+ while (1);
+ }
+ else
+ Serial.println("IRQ pin connection check passed. ");
+
+ //calibrate the resonance frequency. failing the resonance frequency could indicate an issue
+ //of the sensor. resonance frequency calibration will take about 1.7 seconds to complete.
+ uint8_t division_ratio = AS3935MI::AS3935_DR_16;
+ if (F_CPU < 48000000) //fixes https://bitbucket.org/christandlg/as3935mi/issues/12/autocalibrate-no-longer-working
+ division_ratio = AS3935MI::AS3935_DR_64;
+
+ int32_t frequency = 0;
+ if (!as3935.calibrateResonanceFrequency(frequency, division_ratio))
+ {
+ Serial.print("Resonance Frequency Calibration failed: is ");
+ Serial.print(frequency);
+ Serial.println(" Hz, should be 482500 Hz - 517500 Hz");
+ // while (1);
+ }
+ else
+ {
+ Serial.println("Resonance Frequency Calibration passed. Resonance Frequency is ");
+ Serial.print(frequency);
+ Serial.println(" Hz");
+ }
+
+ //calibrate the RCO.
+ if (!as3935.calibrateRCO())
+ {
+ Serial.println("RCO Calibration failed. ");
+ while (1);
+ }
+ else
+ Serial.println("RCP Calibration passed. ");
+
+ //set the analog front end to 'indoors'
+ as3935.writeAFE(AS3935MI::AS3935_INDOORS);
+
+ //set default value for noise floor threshold
+ as3935.writeNoiseFloorThreshold(AS3935MI::AS3935_NFL_2);
+
+ //set the default Watchdog Threshold
+ as3935.writeWatchdogThreshold(AS3935MI::AS3935_WDTH_2);
+
+ //set the default Spike Rejection
+ as3935.writeSpikeRejection(AS3935MI::AS3935_SREJ_2);
+
+ //write default value for minimum lightnings (1)
+ as3935.writeMinLightnings(AS3935MI::AS3935_MNL_1);
+
+ //do not mask disturbers
+ as3935.writeMaskDisturbers(false);
+
+ //the AS3935 will pull the interrupt pin HIGH when an event is registered and will keep it
+ //pulled high until the event register is read.
+ attachInterrupt(digitalPinToInterrupt(PIN_IRQ), AS3935ISR, RISING);
+
+ Serial.println("Initialization complete, waiting for events...");
+}
+
+void loop() {
+ // put your main code here, to run repeatedly:
+
+ if (interrupt_)
+ {
+ //the Arduino should wait at least 2ms after the IRQ pin has been pulled high
+ delay(2);
+
+ //reset the interrupt variable
+ interrupt_ = false;
+
+ //query the interrupt source from the AS3935
+ uint8_t event = as3935.readInterruptSource();
+
+ //send a report if the noise floor is too high.
+ if (event == AS3935MI::AS3935_INT_NH)
+ {
+ Serial.println("Noise floor too high. attempting to increase noise floor threshold. ");
+
+ //if the noise floor threshold setting is not yet maxed out, increase the setting.
+ //note that noise floor threshold events can also be triggered by an incorrect
+ //analog front end setting.
+ if (as3935.increaseNoiseFloorThreshold() == AS3935MI::AS3935_NFL_0)
+ Serial.println("noise floor threshold already at maximum");
+ else
+ Serial.println("increased noise floor threshold");
+ }
+
+ //send a report if a disturber was detected. if disturbers are masked with as3935.writeMaskDisturbers(true);
+ //this event will never be reported.
+ else if (event == AS3935MI::AS3935_INT_D)
+ {
+ Serial.println("Disturber detected, attempting to increase noise floor threshold. ");
+
+ //increasing the Watchdog Threshold and / or Spike Rejection setting improves the AS3935s resistance
+ //against disturbers but also decrease the lightning detection efficiency (see AS3935 datasheet)
+ uint8_t wdth = as3935.readWatchdogThreshold();
+ uint8_t srej = as3935.readSpikeRejection();
+
+ if ((wdth < AS3935MI::AS3935_WDTH_10) || (srej < AS3935MI::AS3935_SREJ_10))
+ {
+ sense_adj_last_ = millis();
+
+ //alternatively increase spike rejection and watchdog threshold
+ if (srej < wdth)
+ {
+ if (as3935.increaseSpikeRejection() == AS3935MI::AS3935_SREJ_0)
+ Serial.println("spike rejection ratio already at maximum");
+ else
+ Serial.println("increased spike rejection ratio");
+ }
+ else
+ {
+ if (as3935.increaseWatchdogThreshold() == AS3935MI::AS3935_WDTH_0)
+ Serial.println("watchdog threshold already at maximum");
+ else
+ Serial.println("increased watchdog threshold");
+ }
+ }
+ else
+ {
+ Serial.println("error: Watchdog Threshold and Spike Rejection settings are already maxed out.");
+ }
+ }
+
+ else if (event == AS3935MI::AS3935_INT_L)
+ {
+ Serial.print("Lightning detected! Storm Front is ");
+ Serial.print(as3935.readStormDistance());
+ Serial.println("km away.");
+ }
+ }
+
+ //increase sensor sensitivity every once in a while. SENSE_INCREASE_INTERVAL controls how quickly the code
+ //attempts to increase sensitivity.
+ if (millis() - sense_adj_last_ > SENSE_INCREASE_INTERVAL)
+ {
+ sense_adj_last_ = millis();
+
+ Serial.println("No disturber detected, attempting to decrease noise floor threshold. ");
+
+ uint8_t wdth = as3935.readWatchdogThreshold();
+ uint8_t srej = as3935.readSpikeRejection();
+
+ if ((wdth > AS3935MI::AS3935_WDTH_0) || (srej > AS3935MI::AS3935_SREJ_0))
+ {
+
+ //alternatively derease spike rejection and watchdog threshold
+ if (srej > wdth)
+ {
+ if (as3935.decreaseSpikeRejection())
+ Serial.println("decreased spike rejection ratio");
+ else
+ Serial.println("spike rejection ratio already at minimum");
+ }
+ else
+ {
+ if (as3935.decreaseWatchdogThreshold())
+ Serial.println("decreased watchdog threshold");
+ else
+ Serial.println("watchdog threshold already at minimum");
+ }
+ }
+ }
+}
+
+
+//interrupt service routine. this function is called each time the AS3935 reports an event by pulling
+//the IRQ pin high.
+#if defined(ESP32)
+ICACHE_RAM_ATTR void AS3935ISR()
+{
+ interrupt_ = true;
+}
+#elif defined(ESP8266)
+ICACHE_RAM_ATTR void AS3935ISR()
+{
+ interrupt_ = true;
+}
+#else
+void AS3935ISR()
+{
+ interrupt_ = true;
+}
+#endif
\ No newline at end of file
diff --git a/lib/AS3935MI/examples/AS3935MI_LightningDetector_SPI/AS3935MI_LightningDetector_SPI.ino b/lib/AS3935MI/examples/AS3935MI_LightningDetector_SPI/AS3935MI_LightningDetector_SPI.ino
new file mode 100644
index 0000000000..b030c61925
--- /dev/null
+++ b/lib/AS3935MI/examples/AS3935MI_LightningDetector_SPI/AS3935MI_LightningDetector_SPI.ino
@@ -0,0 +1,251 @@
+// AS3935_LightningDetector_SPI.ino
+//
+// shows how to use the AS3935 library with the lightning sensor connected using SPI.
+//
+// Copyright (c) 2018-2019 Gregor Christandl
+//
+// connect the AS3935 to the Arduino like this:
+//
+// Arduino - AS3935
+// 5V ------ VCC
+// GND ----- GND
+// D2 ------ IRQ must be a pin supporting external interrupts, e.g. D2 or D3 on an Arduino Uno.
+// MOSI ---- MOSI
+// MISO ---- MISO
+// SCK ----- SCK
+// GND ----- SI (activates SPI for the AS3935)
+// D3 ------ CS chip select pin for AS3935
+// 5V ------ EN_VREG !IMPORTANT when using 5V Arduinos (Uno, Mega2560, ...)
+// other pins can be left unconnected.
+
+#include
+#include
+
+#include
+
+#define PIN_IRQ 3
+#define PIN_CS 4
+
+//create an AS3935 object using the SPI interface, chip select pin 4 and IRQ pin number 3
+AS3935SPI as3935(PIN_CS, PIN_IRQ);
+
+//this value will be set to true by the AS3935 interrupt service routine.
+volatile bool interrupt_ = false;
+
+constexpr uint32_t SENSE_INCREASE_INTERVAL = 15000; //15 s sensitivity increase interval
+uint32_t sense_adj_last_ = 0L; //time of last sensitivity adjustment
+
+void setup() {
+ // put your setup code here, to run once:
+ Serial.begin(9600);
+
+ //wait for serial connection to open (only necessary on some boards)
+ while (!Serial);
+
+ //set the IRQ pin as an input pin. do not use INPUT_PULLUP - the AS3935 will pull the pin
+ //high if an event is registered.
+ pinMode(PIN_IRQ, INPUT);
+
+ SPI.begin();
+
+ //begin() checks the Interface passed to the constructor and resets the AS3935 to
+ //default values.
+ if (!as3935.begin())
+ {
+ Serial.println("begin() failed. check your AS3935 Interface setting.");
+ while (1);
+ }
+
+ //check SPI connection.
+ if (!as3935.checkConnection())
+ {
+ Serial.println("checkConnection() failed. check your SPI connection and SPI chip select pin. ");
+ while (1);
+ }
+ else
+ Serial.println("SPI connection check passed. ");
+
+ //check the IRQ pin connection.
+ if (!as3935.checkIRQ())
+ {
+ Serial.println("checkIRQ() failed. check if the correct IRQ pin was passed to the AS3935SPI constructor. ");
+ while (1);
+ }
+ else
+ Serial.println("IRQ pin connection check passed. ");
+
+ //calibrate the resonance frequency. failing the resonance frequency could indicate an issue
+ //of the sensor. resonance frequency calibration will take about 1.7 seconds to complete.
+ int32_t frequency = 0;
+ if (!as3935.calibrateResonanceFrequency(frequency))
+ {
+ Serial.print("Resonance Frequency Calibration failed: is ");
+ Serial.print(frequency);
+ Serial.println(" Hz, should be 482500 Hz - 517500 Hz");
+ //while (1);
+ }
+ else
+ Serial.println("Resonance Frequency Calibration passed. ");
+
+ Serial.print("Resonance Frequency is "); Serial.print(frequency); Serial.println(" Hz");
+
+
+ //calibrate the RCO.
+ if (!as3935.calibrateRCO())
+ {
+ Serial.println("RCP Calibration failed. ");
+ while (1);
+ }
+ else
+ Serial.println("RCO Calibration passed. ");
+
+ //set the analog front end to 'indoors'
+ as3935.writeAFE(AS3935MI::AS3935_INDOORS);
+
+ //set default value for noise floor threshold
+ as3935.writeNoiseFloorThreshold(AS3935MI::AS3935_NFL_2);
+
+ //set the default Watchdog Threshold
+ as3935.writeWatchdogThreshold(AS3935MI::AS3935_WDTH_2);
+
+ //set the default Spike Rejection
+ as3935.writeSpikeRejection(AS3935MI::AS3935_SREJ_2);
+
+ //write default value for minimum lightnings (1)
+ as3935.writeMinLightnings(AS3935MI::AS3935_MNL_1);
+
+ //do not mask disturbers
+ as3935.writeMaskDisturbers(false);
+
+ //the AS3935 will pull the interrupt pin HIGH when an event is registered and will keep it
+ //pulled high until the event register is read.
+ attachInterrupt(digitalPinToInterrupt(PIN_IRQ), AS3935ISR, RISING);
+
+ Serial.println("Initialization complete, waiting for events...");
+}
+
+void loop() {
+ // put your main code here, to run repeatedly:
+
+ if (interrupt_)
+ {
+ //the Arduino should wait at least 2ms after the IRQ pin has been pulled high
+ delay(2);
+
+ //reset the interrupt variable
+ interrupt_ = false;
+
+ //query the interrupt source from the AS3935
+ uint8_t event = as3935.readInterruptSource();
+
+ //send a report if the noise floor is too high.
+ if (event == AS3935MI::AS3935_INT_NH)
+ {
+ Serial.println("Noise floor too high. attempting to increase noise floor threshold. ");
+
+ //if the noise floor threshold setting is not yet maxed out, increase the setting.
+ //note that noise floor threshold events can also be triggered by an incorrect
+ //analog front end setting.
+ if (as3935.increaseNoiseFloorThreshold() == AS3935MI::AS3935_NFL_0)
+ Serial.println("noise floor threshold already at maximum");
+ else
+ Serial.println("increased noise floor threshold");
+ }
+
+ //send a report if a disturber was detected. if disturbers are masked with as3935.writeMaskDisturbers(true);
+ //this event will never be reported.
+ else if (event == AS3935MI::AS3935_INT_D)
+ {
+ Serial.println("Disturber detected, attempting to increase noise floor threshold. ");
+
+ //increasing the Watchdog Threshold and / or Spike Rejection setting improves the AS3935s resistance
+ //against disturbers but also decrease the lightning detection efficiency (see AS3935 datasheet)
+ uint8_t wdth = as3935.readWatchdogThreshold();
+ uint8_t srej = as3935.readSpikeRejection();
+
+ if ((wdth < AS3935MI::AS3935_WDTH_10) || (srej < AS3935MI::AS3935_SREJ_10))
+ {
+ sense_adj_last_ = millis();
+
+ //alternatively increase spike rejection and watchdog threshold
+ if (srej < wdth)
+ {
+ if (as3935.increaseSpikeRejection() == AS3935MI::AS3935_SREJ_0)
+ Serial.println("spike rejection ratio already at maximum");
+ else
+ Serial.println("increased spike rejection ratio");
+ }
+ else
+ {
+ if (as3935.increaseWatchdogThreshold() == AS3935MI::AS3935_WDTH_0)
+ Serial.println("watchdog threshold already at maximum");
+ else
+ Serial.println("increased watchdog threshold");
+ }
+ }
+ else
+ {
+ Serial.println("error: Watchdog Threshold and Spike Rejection settings are already maxed out.");
+ }
+ }
+
+ else if (event == AS3935MI::AS3935_INT_L)
+ {
+ Serial.print("Lightning detected! Storm Front is ");
+ Serial.print(as3935.readStormDistance());
+ Serial.println("km away.");
+ }
+ }
+
+ //increase sensor sensitivity every once in a while. SENSE_INCREASE_INTERVAL controls how quickly the code
+ //attempts to increase sensitivity.
+ if (millis() - sense_adj_last_ > SENSE_INCREASE_INTERVAL)
+ {
+ sense_adj_last_ = millis();
+
+ Serial.println("No disturber detected, attempting to decrease noise floor threshold. ");
+
+ uint8_t wdth = as3935.readWatchdogThreshold();
+ uint8_t srej = as3935.readSpikeRejection();
+
+ if ((wdth > AS3935MI::AS3935_WDTH_0) || (srej > AS3935MI::AS3935_SREJ_0))
+ {
+
+ //alternatively derease spike rejection and watchdog threshold
+ if (srej > wdth)
+ {
+ if (as3935.decreaseSpikeRejection())
+ Serial.println("decreased spike rejection ratio");
+ else
+ Serial.println("spike rejection ratio already at minimum");
+ }
+ else
+ {
+ if (as3935.decreaseWatchdogThreshold())
+ Serial.println("decreased watchdog threshold");
+ else
+ Serial.println("watchdog threshold already at minimum");
+ }
+ }
+ }
+}
+
+
+//interrupt service routine. this function is called each time the AS3935 reports an event by pulling
+//the IRQ pin high.
+#if defined(ESP32)
+ICACHE_RAM_ATTR void AS3935ISR()
+{
+ interrupt_ = true;
+}
+#elif defined(ESP8266)
+ICACHE_RAM_ATTR void AS3935ISR()
+{
+ interrupt_ = true;
+}
+#else
+void AS3935ISR()
+{
+ interrupt_ = true;
+}
+#endif
\ No newline at end of file
diff --git a/lib/AS3935MI/examples/AS3935MI_LightningDetector_SPIClass/AS3935MI_LightningDetector_SPIClass.ino b/lib/AS3935MI/examples/AS3935MI_LightningDetector_SPIClass/AS3935MI_LightningDetector_SPIClass.ino
new file mode 100644
index 0000000000..5198d02f0c
--- /dev/null
+++ b/lib/AS3935MI/examples/AS3935MI_LightningDetector_SPIClass/AS3935MI_LightningDetector_SPIClass.ino
@@ -0,0 +1,252 @@
+// AS3935_LightningDetector_SPIClass.ino
+//
+// shows how to use the AS3935 library with the lightning sensor connected using an interface that inherits from SPIClass.
+// in this example, the SPI object is used, but other objects that inherit from SPIClass may be used instead.
+//
+// Copyright (c) 2018-2019 Gregor Christandl
+//
+// connect the AS3935 to the Arduino like this:
+//
+// Arduino - AS3935
+// 5V ------ VCC
+// GND ----- GND
+// D2 ------ IRQ must be a pin supporting external interrupts, e.g. D2 or D3 on an Arduino Uno.
+// MOSI ---- MOSI
+// MISO ---- MISO
+// SCK ----- SCK
+// GND ----- SI (activates SPI for the AS3935)
+// D3 ------ CS chip select pin for AS3935
+// 5V ------ EN_VREG !IMPORTANT when using 5V Arduinos (Uno, Mega2560, ...)
+// other pins can be left unconnected.
+
+#include
+#include
+
+#include
+
+#define PIN_IRQ 3
+#define PIN_CS 4
+
+//create an AS3935 object using the SPI interface, chip select pin 4 and IRQ pin number 3
+//the SPIClass object is passed by reference
+AS3935SPIClass as3935(&SPI, PIN_CS, PIN_IRQ);
+
+//this value will be set to true by the AS3935 interrupt service routine.
+volatile bool interrupt_ = false;
+
+constexpr uint32_t SENSE_INCREASE_INTERVAL = 15000; //15 s sensitivity increase interval
+uint32_t sense_adj_last_ = 0L; //time of last sensitivity adjustment
+
+void setup() {
+ // put your setup code here, to run once:
+ Serial.begin(9600);
+
+ //wait for serial connection to open (only necessary on some boards)
+ while (!Serial);
+
+ //set the IRQ pin as an input pin. do not use INPUT_PULLUP - the AS3935 will pull the pin
+ //high if an event is registered.
+ pinMode(PIN_IRQ, INPUT);
+
+ SPI.begin();
+
+ //begin() checks the Interface passed to the constructor and resets the AS3935 to
+ //default values.
+ if (!as3935.begin())
+ {
+ Serial.println("begin() failed. check your AS3935 Interface setting.");
+ while (1);
+ }
+
+ //check SPI connection.
+ if (!as3935.checkConnection())
+ {
+ Serial.println("checkConnection() failed. check your SPI connection and SPI chip select pin. ");
+ while (1);
+ }
+ else
+ Serial.println("SPI connection check passed. ");
+
+ //check the IRQ pin connection.
+ if (!as3935.checkIRQ())
+ {
+ Serial.println("checkIRQ() failed. check if the correct IRQ pin was passed to the AS3935SPI constructor. ");
+ while (1);
+ }
+ else
+ Serial.println("IRQ pin connection check passed. ");
+
+ //calibrate the resonance frequency. failing the resonance frequency could indicate an issue
+ //of the sensor. resonance frequency calibration will take about 1.7 seconds to complete.
+ int32_t frequency = 0;
+ if (!as3935.calibrateResonanceFrequency(frequency))
+ {
+ Serial.print("Resonance Frequency Calibration failed: is ");
+ Serial.print(frequency);
+ Serial.println(" Hz, should be 482500 Hz - 517500 Hz");
+ //while (1);
+ }
+ else
+ Serial.println("Resonance Frequency Calibration passed. ");
+
+ Serial.print("Resonance Frequency is "); Serial.print(frequency); Serial.println(" Hz");
+
+
+ //calibrate the RCO.
+ if (!as3935.calibrateRCO())
+ {
+ Serial.println("RCP Calibration failed. ");
+ while (1);
+ }
+ else
+ Serial.println("RCO Calibration passed. ");
+
+ //set the analog front end to 'indoors'
+ as3935.writeAFE(AS3935MI::AS3935_INDOORS);
+
+ //set default value for noise floor threshold
+ as3935.writeNoiseFloorThreshold(AS3935MI::AS3935_NFL_2);
+
+ //set the default Watchdog Threshold
+ as3935.writeWatchdogThreshold(AS3935MI::AS3935_WDTH_2);
+
+ //set the default Spike Rejection
+ as3935.writeSpikeRejection(AS3935MI::AS3935_SREJ_2);
+
+ //write default value for minimum lightnings (1)
+ as3935.writeMinLightnings(AS3935MI::AS3935_MNL_1);
+
+ //do not mask disturbers
+ as3935.writeMaskDisturbers(false);
+
+ //the AS3935 will pull the interrupt pin HIGH when an event is registered and will keep it
+ //pulled high until the event register is read.
+ attachInterrupt(digitalPinToInterrupt(PIN_IRQ), AS3935ISR, RISING);
+
+ Serial.println("Initialization complete, waiting for events...");
+}
+
+void loop() {
+ // put your main code here, to run repeatedly:
+
+ if (interrupt_)
+ {
+ //the Arduino should wait at least 2ms after the IRQ pin has been pulled high
+ delay(2);
+
+ //reset the interrupt variable
+ interrupt_ = false;
+
+ //query the interrupt source from the AS3935
+ uint8_t event = as3935.readInterruptSource();
+
+ //send a report if the noise floor is too high.
+ if (event == AS3935MI::AS3935_INT_NH)
+ {
+ Serial.println("Noise floor too high. attempting to increase noise floor threshold. ");
+
+ //if the noise floor threshold setting is not yet maxed out, increase the setting.
+ //note that noise floor threshold events can also be triggered by an incorrect
+ //analog front end setting.
+ if (as3935.increaseNoiseFloorThreshold() == AS3935MI::AS3935_NFL_0)
+ Serial.println("noise floor threshold already at maximum");
+ else
+ Serial.println("increased noise floor threshold");
+ }
+
+ //send a report if a disturber was detected. if disturbers are masked with as3935.writeMaskDisturbers(true);
+ //this event will never be reported.
+ else if (event == AS3935MI::AS3935_INT_D)
+ {
+ Serial.println("Disturber detected, attempting to increase noise floor threshold. ");
+
+ //increasing the Watchdog Threshold and / or Spike Rejection setting improves the AS3935s resistance
+ //against disturbers but also decrease the lightning detection efficiency (see AS3935 datasheet)
+ uint8_t wdth = as3935.readWatchdogThreshold();
+ uint8_t srej = as3935.readSpikeRejection();
+
+ if ((wdth < AS3935MI::AS3935_WDTH_10) || (srej < AS3935MI::AS3935_SREJ_10))
+ {
+ sense_adj_last_ = millis();
+
+ //alternatively increase spike rejection and watchdog threshold
+ if (srej < wdth)
+ {
+ if (as3935.increaseSpikeRejection() == AS3935MI::AS3935_SREJ_0)
+ Serial.println("spike rejection ratio already at maximum");
+ else
+ Serial.println("increased spike rejection ratio");
+ }
+ else
+ {
+ if (as3935.increaseWatchdogThreshold() == AS3935MI::AS3935_WDTH_0)
+ Serial.println("watchdog threshold already at maximum");
+ else
+ Serial.println("increased watchdog threshold");
+ }
+ }
+ else
+ {
+ Serial.println("error: Watchdog Threshold and Spike Rejection settings are already maxed out.");
+ }
+ }
+
+ else if (event == AS3935MI::AS3935_INT_L)
+ {
+ Serial.print("Lightning detected! Storm Front is ");
+ Serial.print(as3935.readStormDistance());
+ Serial.println("km away.");
+ }
+ }
+
+ //increase sensor sensitivity every once in a while. SENSE_INCREASE_INTERVAL controls how quickly the code
+ //attempts to increase sensitivity.
+ if (millis() - sense_adj_last_ > SENSE_INCREASE_INTERVAL)
+ {
+ sense_adj_last_ = millis();
+
+ Serial.println("No disturber detected, attempting to decrease noise floor threshold. ");
+
+ uint8_t wdth = as3935.readWatchdogThreshold();
+ uint8_t srej = as3935.readSpikeRejection();
+
+ if ((wdth > AS3935MI::AS3935_WDTH_0) || (srej > AS3935MI::AS3935_SREJ_0))
+ {
+
+ //alternatively derease spike rejection and watchdog threshold
+ if (srej > wdth)
+ {
+ if (as3935.decreaseSpikeRejection())
+ Serial.println("decreased spike rejection ratio");
+ else
+ Serial.println("spike rejection ratio already at minimum");
+ }
+ else
+ {
+ if (as3935.decreaseWatchdogThreshold())
+ Serial.println("decreased watchdog threshold");
+ else
+ Serial.println("watchdog threshold already at minimum");
+ }
+ }
+ }
+}
+
+//interrupt service routine. this function is called each time the AS3935 reports an event by pulling
+//the IRQ pin high.
+#if defined(ESP32)
+ICACHE_RAM_ATTR void AS3935ISR()
+{
+ interrupt_ = true;
+}
+#elif defined(ESP8266)
+ICACHE_RAM_ATTR void AS3935ISR()
+{
+ interrupt_ = true;
+}
+#else
+void AS3935ISR()
+{
+ interrupt_ = true;
+}
+#endif
\ No newline at end of file
diff --git a/lib/AS3935MI/examples/AS3935MI_LightningDetector_TwoWire/AS3935MI_LightningDetector_TwoWire.ino b/lib/AS3935MI/examples/AS3935MI_LightningDetector_TwoWire/AS3935MI_LightningDetector_TwoWire.ino
new file mode 100644
index 0000000000..108a654c66
--- /dev/null
+++ b/lib/AS3935MI/examples/AS3935MI_LightningDetector_TwoWire/AS3935MI_LightningDetector_TwoWire.ino
@@ -0,0 +1,249 @@
+// AS3935MI_LightningDetector_TwoWire.ino
+//
+// shows how to use the AS3935 library with the lightning sensor connected using an interface that inherits from TwoWire.
+// in this example, the Wire object is used, but other objects that inherit from TwoWire may be used instead.
+//
+// Copyright (c) 2018-2019 Gregor Christandl
+//
+// connect the AS3935 to the Arduino like this:
+//
+// Arduino - AS3935
+// 5V ------ VCC
+// GND ----- GND
+// D2 ------ IRQ must be a pin supporting external interrupts, e.g. D2 or D3 on an Arduino Uno.
+// SDA ----- MOSI
+// SCL ----- SCL
+// 5V ------ SI (activates I2C for the AS3935)
+// 5V ------ A0 (sets the AS3935' I2C address to 0x01)
+// GND ----- A1 (sets the AS3935' I2C address to 0x01)
+// 5V ------ EN_VREG !IMPORTANT when using 5V Arduinos (Uno, Mega2560, ...)
+// other pins can be left unconnected.
+
+#include
+#include
+
+#include
+
+#define PIN_IRQ 2
+
+//create an AS3935 object using the I2C interface, I2C address 0x01 and IRQ pin number 2
+AS3935TwoWire as3935(&Wire, AS3935TwoWire::AS3935I2C_A01, PIN_IRQ);
+
+//this value will be set to true by the AS3935 interrupt service routine.
+volatile bool interrupt_ = false;
+
+constexpr uint32_t SENSE_INCREASE_INTERVAL = 15000; //15 s sensitivity increase interval
+uint32_t sense_adj_last_ = 0L; //time of last sensitivity adjustment
+
+void setup() {
+ // put your setup code here, to run once:
+ Serial.begin(9600);
+
+ //wait for serial connection to open (only necessary on some boards)
+ while (!Serial);
+
+ //set the IRQ pin as an input pin. do not use INPUT_PULLUP - the AS3935 will pull the pin
+ //high if an event is registered.
+ pinMode(PIN_IRQ, INPUT);
+
+ Wire.begin();
+
+ //begin() checks the Interface and I2C Address passed to the constructor and resets the AS3935 to
+ //default values.
+ if (!as3935.begin())
+ {
+ Serial.println("begin() failed. Check the I2C address passed to the AS3935I2C constructor. ");
+ while (1);
+ }
+
+ //check I2C connection.
+ if (!as3935.checkConnection())
+ {
+ Serial.println("checkConnection() failed. check your I2C connection and I2C Address. ");
+ while (1);
+ }
+ else
+ Serial.println("I2C connection check passed. ");
+
+ //check the IRQ pin connection.
+ if (!as3935.checkIRQ())
+ {
+ Serial.println("checkIRQ() failed. check if the correct IRQ pin was passed to the AS3935I2C constructor. ");
+ while (1);
+ }
+ else
+ Serial.println("IRQ pin connection check passed. ");
+
+ //calibrate the resonance frequency. failing the resonance frequency could indicate an issue
+ //of the sensor. resonance frequency calibration will take about 1.7 seconds to complete.
+ int32_t frequency = 0;
+ if (!as3935.calibrateResonanceFrequency(frequency))
+ {
+ Serial.print("Resonance Frequency Calibration failed: is ");
+ Serial.print(frequency);
+ Serial.println(" Hz, should be 482500 Hz - 517500 Hz");
+ //while (1);
+ }
+ else
+ Serial.println("Resonance Frequency Calibration passed. ");
+
+ Serial.print("Resonance Frequency is "); Serial.print(frequency); Serial.println(" Hz");
+
+ //calibrate the RCO.
+ if (!as3935.calibrateRCO())
+ {
+ Serial.println("RCO Calibration failed. ");
+ while (1);
+ }
+ else
+ Serial.println("RCP Calibration passed. ");
+
+ //set the analog front end to 'indoors'
+ as3935.writeAFE(AS3935MI::AS3935_INDOORS);
+
+ //set default value for noise floor threshold
+ as3935.writeNoiseFloorThreshold(AS3935MI::AS3935_NFL_2);
+
+ //set the default Watchdog Threshold
+ as3935.writeWatchdogThreshold(AS3935MI::AS3935_WDTH_2);
+
+ //set the default Spike Rejection
+ as3935.writeSpikeRejection(AS3935MI::AS3935_SREJ_2);
+
+ //write default value for minimum lightnings (1)
+ as3935.writeMinLightnings(AS3935MI::AS3935_MNL_1);
+
+ //do not mask disturbers
+ as3935.writeMaskDisturbers(false);
+
+ //the AS3935 will pull the interrupt pin HIGH when an event is registered and will keep it
+ //pulled high until the event register is read.
+ attachInterrupt(digitalPinToInterrupt(PIN_IRQ), AS3935ISR, RISING);
+
+ Serial.println("Initialization complete, waiting for events...");
+}
+
+void loop() {
+ // put your main code here, to run repeatedly:
+
+ if (interrupt_)
+ {
+ //the Arduino should wait at least 2ms after the IRQ pin has been pulled high
+ delay(2);
+
+ //reset the interrupt variable
+ interrupt_ = false;
+
+ //query the interrupt source from the AS3935
+ uint8_t event = as3935.readInterruptSource();
+
+ //send a report if the noise floor is too high.
+ if (event == AS3935MI::AS3935_INT_NH)
+ {
+ Serial.println("Noise floor too high. attempting to increase noise floor threshold. ");
+
+ //if the noise floor threshold setting is not yet maxed out, increase the setting.
+ //note that noise floor threshold events can also be triggered by an incorrect
+ //analog front end setting.
+ if (as3935.increaseNoiseFloorThreshold() == AS3935MI::AS3935_NFL_0)
+ Serial.println("noise floor threshold already at maximum");
+ else
+ Serial.println("increased noise floor threshold");
+ }
+
+ //send a report if a disturber was detected. if disturbers are masked with as3935.writeMaskDisturbers(true);
+ //this event will never be reported.
+ else if (event == AS3935MI::AS3935_INT_D)
+ {
+ Serial.println("Disturber detected, attempting to increase noise floor threshold. ");
+
+ //increasing the Watchdog Threshold and / or Spike Rejection setting improves the AS3935s resistance
+ //against disturbers but also decrease the lightning detection efficiency (see AS3935 datasheet)
+ uint8_t wdth = as3935.readWatchdogThreshold();
+ uint8_t srej = as3935.readSpikeRejection();
+
+ if ((wdth < AS3935MI::AS3935_WDTH_10) || (srej < AS3935MI::AS3935_SREJ_10))
+ {
+ sense_adj_last_ = millis();
+
+ //alternatively increase spike rejection and watchdog threshold
+ if (srej < wdth)
+ {
+ if (as3935.increaseSpikeRejection() == AS3935MI::AS3935_SREJ_0)
+ Serial.println("spike rejection ratio already at maximum");
+ else
+ Serial.println("increased spike rejection ratio");
+ }
+ else
+ {
+ if (as3935.increaseWatchdogThreshold() == AS3935MI::AS3935_WDTH_0)
+ Serial.println("watchdog threshold already at maximum");
+ else
+ Serial.println("increased watchdog threshold");
+ }
+ }
+ else
+ {
+ Serial.println("error: Watchdog Threshold and Spike Rejection settings are already maxed out.");
+ }
+ }
+
+ else if (event == AS3935MI::AS3935_INT_L)
+ {
+ Serial.print("Lightning detected! Storm Front is ");
+ Serial.print(as3935.readStormDistance());
+ Serial.println("km away.");
+ }
+ }
+
+ //increase sensor sensitivity every once in a while. SENSE_INCREASE_INTERVAL controls how quickly the code
+ //attempts to increase sensitivity.
+ if (millis() - sense_adj_last_ > SENSE_INCREASE_INTERVAL)
+ {
+ sense_adj_last_ = millis();
+
+ Serial.println("No disturber detected, attempting to decrease noise floor threshold. ");
+
+ uint8_t wdth = as3935.readWatchdogThreshold();
+ uint8_t srej = as3935.readSpikeRejection();
+
+ if ((wdth > AS3935MI::AS3935_WDTH_0) || (srej > AS3935MI::AS3935_SREJ_0))
+ {
+
+ //alternatively derease spike rejection and watchdog threshold
+ if (srej > wdth)
+ {
+ if (as3935.decreaseSpikeRejection())
+ Serial.println("decreased spike rejection ratio");
+ else
+ Serial.println("spike rejection ratio already at minimum");
+ }
+ else
+ {
+ if (as3935.decreaseWatchdogThreshold())
+ Serial.println("decreased watchdog threshold");
+ else
+ Serial.println("watchdog threshold already at minimum");
+ }
+ }
+ }
+}
+
+//interrupt service routine. this function is called each time the AS3935 reports an event by pulling
+//the IRQ pin high.
+#if defined(ESP32)
+ICACHE_RAM_ATTR void AS3935ISR()
+{
+ interrupt_ = true;
+}
+#elif defined(ESP8266)
+ICACHE_RAM_ATTR void AS3935ISR()
+{
+ interrupt_ = true;
+}
+#else
+void AS3935ISR()
+{
+ interrupt_ = true;
+}
+#endif
\ No newline at end of file
diff --git a/lib/AS3935MI/examples/AS3935MI_LightningDetector_otherInterfaces/AS3935MI_LightningDetector_otherInterfaces.ino b/lib/AS3935MI/examples/AS3935MI_LightningDetector_otherInterfaces/AS3935MI_LightningDetector_otherInterfaces.ino
new file mode 100644
index 0000000000..bc1c1a6334
--- /dev/null
+++ b/lib/AS3935MI/examples/AS3935MI_LightningDetector_otherInterfaces/AS3935MI_LightningDetector_otherInterfaces.ino
@@ -0,0 +1,327 @@
+// AS3935_LightningDetector_otherInterfaces.ino
+//
+// shows how to use the AS3935 library with interfaces that are not derived from TwoWire or SPIClass.
+// here, the second I2C port of an Arduino Due is used (Wire1)
+//
+// Copyright (c) 2018-2019 Gregor Christandl
+//
+// connect the AS3935 to the Arduino Due like this:
+//
+// Arduino - AS3935
+// 3.3V ---- VCC
+// GND ----- GND
+// D2 ------ IRQ must be a pin supporting external interrupts, e.g. D2 or D3 on an Arduino Uno.
+// SDA1 ---- MOSI
+// SCL1 ---- SCL
+// 5V ------ SI (activates I2C for the AS3935)
+// 5V ------ A0 (sets the AS3935' I2C address to 0x01)
+// GND ----- A1 (sets the AS3935' I2C address to 0x01)
+// 5V ------ EN_VREG !IMPORTANT when using 5V Arduinos (Uno, Mega2560, ...)
+// other pins can be left unconnected.
+
+#include
+
+#include
+
+#include
+
+#define PIN_IRQ 2
+
+//class derived from AS3935MI that implements communication via an interface other than native I2C or SPI.
+class AS3935Wire1 : public AS3935MI
+{
+ public:
+ enum I2C_address_t : uint8_t
+ {
+ AS3935I2C_A01 = 0b01,
+ AS3935I2C_A10 = 0b10,
+ AS3935I2C_A11 = 0b11
+ };
+
+ //constructor of the derived class. in this case, only 2 parameters are needed
+ //@param address i2c address of the sensor.
+ //@param irq input pin the sensors irq pin is connected to. this parameter is passed to the constructor of the parent class (AS3935MI)
+ AS3935Wire1(uint8_t address, uint8_t irq) :
+ AS3935MI(irq), //AS3935MI does not have a default constructor therefore the constructor must be called explicitly. it takes the irq pin number as an argument.
+ address_(address) //initialize the AS3935Wire1 classes private member address_ to the i2c address provided
+ {
+ //nothing else to do here...
+ }
+
+ //this function must be implemented by derived classes. it is used to initialize the interface.
+ //@return true if the interface was initializes successfully, false otherwise.
+ bool beginInterface()
+ {
+ //check if a valid i2c address for AS3935 lightning sensors has been provided.
+ switch (address_)
+ {
+ case 0x01:
+ case 0x02:
+ case 0x03:
+ break; //exit the switch statement
+ default:
+ //return false if an invalid I2C address was given.
+ return false;
+ }
+
+ return true;
+ }
+
+ private:
+ //this function must be implemented by derived classes. this function is responsible for reading data from the sensor.
+ //@param reg register to read.
+ //@return read data (1 byte).
+ uint8_t readRegister(uint8_t reg)
+ {
+ #if defined(ARDUINO_SAM_DUE)
+ //workaround for Arduino Due. The Due seems not to send a repeated start with the code below, so this
+ //undocumented feature of Wire::requestFrom() is used. can be used on other Arduinos too (tested on Mega2560)
+ //see this thread for more info: https://forum.arduino.cc/index.php?topic=385377.0
+ Wire1.requestFrom(address_, 1, reg, 1, true);
+ #else
+ Wire1.beginTransmission(address_);
+ Wire1.write(reg);
+ Wire1.endTransmission(false);
+ Wire1.requestFrom(address_, static_cast(1));
+ #endif
+
+ return Wire1.read();
+ }
+
+ //this function must be implemented by derived classes. this function is responsible for sending data to the sensor.
+ //@param reg register to write to.
+ //@param data data to write to register.
+ void writeRegister(uint8_t reg, uint8_t data)
+ {
+ Wire1.beginTransmission(address_);
+ Wire1.write(reg);
+ Wire1.write(data);
+ Wire1.endTransmission();
+ }
+
+ uint8_t address_; //i2c address of sensor
+};
+
+//create an AS3935 object using the Wire1 interface, I2C address 0x01 and IRQ pin number 2
+AS3935Wire1 as3935(AS3935Wire1::AS3935I2C_A01, PIN_IRQ);
+
+//this value will be set to true by the AS3935 interrupt service routine.
+volatile bool interrupt_ = false;
+
+constexpr uint32_t SENSE_INCREASE_INTERVAL = 15000; //15 s sensitivity increase interval
+uint32_t sense_adj_last_ = 0L; //time of last sensitivity adjustment
+
+void setup() {
+ // put your setup code here, to run once:
+ Serial.begin(9600);
+
+ //wait for serial connection to open (only necessary on some boards)
+ while (!Serial);
+
+ //set the IRQ pin as an input pin. do not use INPUT_PULLUP - the AS3935 will pull the pin
+ //high if an event is registered.
+ pinMode(PIN_IRQ, INPUT);
+
+ Wire1.begin();
+
+ //begin() checks the Interface passed to the constructor and resets the AS3935 to
+ //default values.
+ if (!as3935.begin())
+ {
+ Serial.println("begin() failed. Check the I2C address passed to the AS3935I2C constructor. ");
+ while (1);
+ }
+
+ //check I2C connection.
+ if (!as3935.checkConnection())
+ {
+ Serial.println("checkConnection() failed. check your I2C connection and I2C Address. ");
+ while (1);
+ }
+ else
+ Serial.println("I2C connection check passed. ");
+
+ //check the IRQ pin connection.
+ if (!as3935.checkIRQ())
+ {
+ Serial.println("checkIRQ() failed. check if the correct IRQ pin was passed to the AS3935Wire1 constructor. ");
+ while (1);
+ }
+ else
+ Serial.println("IRQ pin connection check passed. ");
+
+ //calibrate the resonance frequency. failing the resonance frequency could indicate an issue
+ //of the sensor. resonance frequency calibration will take about 1.7 seconds to complete.
+ int32_t frequency = 0;
+ if (!as3935.calibrateResonanceFrequency(frequency))
+ {
+ Serial.print("Resonance Frequency Calibration failed: is ");
+ Serial.print(frequency);
+ Serial.println(" Hz, should be 482500 Hz - 517500 Hz");
+ //while (1);
+ }
+ else
+ Serial.println("Resonance Frequency Calibration passed. ");
+
+ Serial.print("Resonance Frequency is "); Serial.print(frequency); Serial.println(" Hz");
+
+
+ //calibrate the RCO.
+ if (!as3935.calibrateRCO())
+ {
+ Serial.println("RCP Calibration failed. ");
+ while (1);
+ }
+ else
+ Serial.println("RCO Calibration passed. ");
+
+ //set the analog front end to 'indoors'
+ as3935.writeAFE(AS3935MI::AS3935_INDOORS);
+
+ //set default value for noise floor threshold
+ as3935.writeNoiseFloorThreshold(AS3935MI::AS3935_NFL_2);
+
+ //set the default Watchdog Threshold
+ as3935.writeWatchdogThreshold(AS3935MI::AS3935_WDTH_2);
+
+ //set the default Spike Rejection
+ as3935.writeSpikeRejection(AS3935MI::AS3935_SREJ_2);
+
+ //write default value for minimum lightnings (1)
+ as3935.writeMinLightnings(AS3935MI::AS3935_MNL_1);
+
+ //do not mask disturbers
+ as3935.writeMaskDisturbers(false);
+
+ //the AS3935 will pull the interrupt pin HIGH when an event is registered and will keep it
+ //pulled high until the event register is read.
+ attachInterrupt(digitalPinToInterrupt(PIN_IRQ), AS3935ISR, RISING);
+
+ Serial.println("Initialization complete, waiting for events...");
+}
+
+void loop() {
+ // put your main code here, to run repeatedly:
+
+ if (interrupt_)
+ {
+ //the Arduino should wait at least 2ms after the IRQ pin has been pulled high
+ delay(2);
+
+ //reset the interrupt variable
+ interrupt_ = false;
+
+ //query the interrupt source from the AS3935
+ uint8_t event = as3935.readInterruptSource();
+
+ //send a report if the noise floor is too high.
+ if (event == AS3935MI::AS3935_INT_NH)
+ {
+ Serial.println("Noise floor too high. attempting to increase noise floor threshold. ");
+
+ //if the noise floor threshold setting is not yet maxed out, increase the setting.
+ //note that noise floor threshold events can also be triggered by an incorrect
+ //analog front end setting.
+ if (as3935.increaseNoiseFloorThreshold() == AS3935MI::AS3935_NFL_0)
+ Serial.println("noise floor threshold already at maximum");
+ else
+ Serial.println("increased noise floor threshold");
+ }
+
+ //send a report if a disturber was detected. if disturbers are masked with as3935.writeMaskDisturbers(true);
+ //this event will never be reported.
+ else if (event == AS3935MI::AS3935_INT_D)
+ {
+ Serial.println("Disturber detected, attempting to increase noise floor threshold. ");
+
+ //increasing the Watchdog Threshold and / or Spike Rejection setting improves the AS3935s resistance
+ //against disturbers but also decrease the lightning detection efficiency (see AS3935 datasheet)
+ uint8_t wdth = as3935.readWatchdogThreshold();
+ uint8_t srej = as3935.readSpikeRejection();
+
+ if ((wdth < AS3935MI::AS3935_WDTH_10) || (srej < AS3935MI::AS3935_SREJ_10))
+ {
+ sense_adj_last_ = millis();
+
+ //alternatively increase spike rejection and watchdog threshold
+ if (srej < wdth)
+ {
+ if (as3935.increaseSpikeRejection() == AS3935MI::AS3935_SREJ_0)
+ Serial.println("spike rejection ratio already at maximum");
+ else
+ Serial.println("increased spike rejection ratio");
+ }
+ else
+ {
+ if (as3935.increaseWatchdogThreshold() == AS3935MI::AS3935_WDTH_0)
+ Serial.println("watchdog threshold already at maximum");
+ else
+ Serial.println("increased watchdog threshold");
+ }
+ }
+ else
+ {
+ Serial.println("error: Watchdog Threshold and Spike Rejection settings are already maxed out.");
+ }
+ }
+
+ else if (event == AS3935MI::AS3935_INT_L)
+ {
+ Serial.print("Lightning detected! Storm Front is ");
+ Serial.print(as3935.readStormDistance());
+ Serial.println("km away.");
+ }
+ }
+
+ //increase sensor sensitivity every once in a while. SENSE_INCREASE_INTERVAL controls how quickly the code
+ //attempts to increase sensitivity.
+ if (millis() - sense_adj_last_ > SENSE_INCREASE_INTERVAL)
+ {
+ sense_adj_last_ = millis();
+
+ Serial.println("No disturber detected, attempting to decrease noise floor threshold. ");
+
+ uint8_t wdth = as3935.readWatchdogThreshold();
+ uint8_t srej = as3935.readSpikeRejection();
+
+ if ((wdth > AS3935MI::AS3935_WDTH_0) || (srej > AS3935MI::AS3935_SREJ_0))
+ {
+
+ //alternatively derease spike rejection and watchdog threshold
+ if (srej > wdth)
+ {
+ if (as3935.decreaseSpikeRejection())
+ Serial.println("decreased spike rejection ratio");
+ else
+ Serial.println("spike rejection ratio already at minimum");
+ }
+ else
+ {
+ if (as3935.decreaseWatchdogThreshold())
+ Serial.println("decreased watchdog threshold");
+ else
+ Serial.println("watchdog threshold already at minimum");
+ }
+ }
+ }
+}
+
+
+//interrupt service routine. this function is called each time the AS3935 reports an event by pulling
+//the IRQ pin high.
+#if defined(ESP32)
+ICACHE_RAM_ATTR void AS3935ISR()
+{
+ interrupt_ = true;
+}
+#elif defined(ESP8266)
+ICACHE_RAM_ATTR void AS3935ISR()
+{
+ interrupt_ = true;
+}
+#else
+void AS3935ISR()
+{
+ interrupt_ = true;
+}
+#endif
\ No newline at end of file
diff --git a/lib/AS3935MI/keywords.txt b/lib/AS3935MI/keywords.txt
new file mode 100644
index 0000000000..6391acd288
--- /dev/null
+++ b/lib/AS3935MI/keywords.txt
@@ -0,0 +1,128 @@
+#######################################
+# Syntax Coloring Map For ExampleLibrary
+#######################################
+
+#######################################
+# Datatypes (KEYWORD1)
+#######################################
+
+AS3935MI KEYWORD1
+AS3935I2C KEYWORD1
+AS3935SPI KEYWORD1
+AS3935TwoWire KEYWORD1
+AS3935SPIClass KEYWORD1
+
+#######################################
+# Methods and Functions (KEYWORD2)
+#######################################
+
+begin KEYWORD2
+checkConnection KEYWORD2
+checkIRQ KEYWORD2
+clearstatistics KEYWORD2
+readStormDistance KEYWORD2
+readInterruptSource KEYWORD2
+readPowerDown KEYWORD2
+writePowerDown KEYWORD2
+readMaskDisturbers KEYWORD2
+writeMaskDisturbers KEYWORD2
+readAFE KEYWORD2
+writeAFE KEYWORD2
+readNoiseFloorThreshold KEYWORD2
+writeNoiseFloorThreshold KEYWORD2
+increaseNoiseFloorThreshold KEYWORD2
+decreaseNoiseFloorThreshold KEYWORD2
+readWatchdogThreshold KEYWORD2
+writeWatchdogThreshold KEYWORD2
+increaseWatchdogThreshold KEYWORD2
+decreaseWatchdogThreshold KEYWORD2
+readSpikeRejection KEYWORD2
+writeSpikeRejection KEYWORD2
+increaseSpikeRejection KEYWORD2
+decreaseSpikeRejection KEYWORD2
+readEnergy KEYWORD2
+readAntennaTuning KEYWORD2
+writeAntennaTuning KEYWORD2
+readDivisionRatio KEYWORD2
+writeDivisionRatio KEYWORD2
+readMinLightnings KEYWORD2
+writeMinLightnings KEYWORD2
+resetToDefaults KEYWORD2
+calibrateRCO KEYWORD2
+calibrateResonanceFrequency KEYWORD2
+readRegister KEYWORD2
+writeRegister KEYWORD2
+
+#######################################
+# Instances (KEYWORD2)
+#######################################
+
+#######################################
+# Constants (LITERAL1)
+
+AS3935_INDOORS LITERAL1
+AS3935_OUTDOORS LITERAL1
+
+AS3935_INT_NH LITERAL1
+AS3935_INT_D LITERAL1
+AS3935_INT_L LITERAL1
+
+AS3935_WDTH_0 LITERAL1
+AS3935_WDTH_1 LITERAL1
+AS3935_WDTH_2 LITERAL1
+AS3935_WDTH_3 LITERAL1
+AS3935_WDTH_4 LITERAL1
+AS3935_WDTH_5 LITERAL1
+AS3935_WDTH_6 LITERAL1
+AS3935_WDTH_7 LITERAL1
+AS3935_WDTH_8 LITERAL1
+AS3935_WDTH_9 LITERAL1
+AS3935_WDTH_10 LITERAL1
+AS3935_WDTH_11 LITERAL1
+AS3935_WDTH_12 LITERAL1
+AS3935_WDTH_13 LITERAL1
+AS3935_WDTH_14 LITERAL1
+AS3935_WDTH_15 LITERAL1
+
+AS3935_SREJ_0 LITERAL1
+AS3935_SREJ_1 LITERAL1
+AS3935_SREJ_2 LITERAL1
+AS3935_SREJ_3 LITERAL1
+AS3935_SREJ_4 LITERAL1
+AS3935_SREJ_5 LITERAL1
+AS3935_SREJ_6 LITERAL1
+AS3935_SREJ_7 LITERAL1
+AS3935_SREJ_8 LITERAL1
+AS3935_SREJ_9 LITERAL1
+AS3935_SREJ_10 LITERAL1
+AS3935_SREJ_11 LITERAL1
+AS3935_SREJ_12 LITERAL1
+AS3935_SREJ_13 LITERAL1
+AS3935_SREJ_14 LITERAL1
+AS3935_SREJ_15 LITERAL1
+
+AS3935_NFL_0 LITERAL1
+AS3935_NFL_1 LITERAL1
+AS3935_NFL_2 LITERAL1
+AS3935_NFL_3 LITERAL1
+AS3935_NFL_4 LITERAL1
+AS3935_NFL_5 LITERAL1
+AS3935_NFL_6 LITERAL1
+AS3935_NFL_7 LITERAL1
+
+AS3935_MNL_1 LITERAL1
+AS3935_MNL_5 LITERAL1
+AS3935_MNL_9 LITERAL1
+AS3935_MNL_16 LITERAL1
+
+AS3935_DR_16 LITERAL1
+AS3935_DR_32 LITERAL1
+AS3935_DR_64 LITERAL1
+AS3935_DR_128 LITERAL1
+
+AS3935I2C_A01 LITERAL1
+AS3935I2C_A10 LITERAL1
+AS3935I2C_A11 LITERAL1
+
+AS3935_DST_OOR LITERAL1
+#######################################
\ No newline at end of file
diff --git a/lib/AS3935MI/library.json b/lib/AS3935MI/library.json
new file mode 100644
index 0000000000..f15df8c853
--- /dev/null
+++ b/lib/AS3935MI/library.json
@@ -0,0 +1,19 @@
+{
+ "name": "AS3935MI",
+ "version": "1.3.5",
+ "keywords": "AS3935",
+ "description": "A library for the ams AS3935 lightning sensor. The library supports both the SPI (via the SPI Library) and I2C (via the Wire Library) interfaces. Use of other I2C / SPI libraries (e.g. software I2C) is supported by inheritance. ",
+ "authors":
+ {
+ "name": "Gregor Christandl",
+ "email": "christandlg@yahoo.com",
+ "url": "https://bitbucket.org/christandlg/as3935mi"
+ },
+ "repository":
+ {
+ "type": "git",
+ "url": "https://bitbucket.org/christandlg/as3935mi.git"
+ },
+ "frameworks": "arduino",
+ "platforms": "*"
+}
\ No newline at end of file
diff --git a/lib/AS3935MI/library.properties b/lib/AS3935MI/library.properties
new file mode 100644
index 0000000000..9a04cf66aa
--- /dev/null
+++ b/lib/AS3935MI/library.properties
@@ -0,0 +1,10 @@
+name=AS3935MI
+version=1.3.5
+author=Gregor Christandl
+maintainer=Gregor Christandl
+sentence=A library for the Austria Microsystems AS3935 Franklin Lightning Detector, supporting I2C and SPI interfaces.
+paragraph=The library supports both the SPI (via the SPI Library) and I2C (via the Wire Library) interfaces. Use of other I2C / SPI libraries (e.g. software I2C) is supported by inheritance.
+category=Sensors
+url=https://bitbucket.org/christandlg/as3935mi/
+architectures=*
+includes=
\ No newline at end of file
diff --git a/lib/AS3935MI/src/AS3935I2C.cpp b/lib/AS3935MI/src/AS3935I2C.cpp
new file mode 100644
index 0000000000..e071e00c6a
--- /dev/null
+++ b/lib/AS3935MI/src/AS3935I2C.cpp
@@ -0,0 +1,28 @@
+//Yet Another Arduino ams AS3935 'Franklin' lightning sensor library
+// Copyright (c) 2018-2019 Gregor Christandl
+// home: https://bitbucket.org/christandlg/as3935mi
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+#include "AS3935I2C.h"
+
+AS3935I2C::AS3935I2C(uint8_t address, uint8_t irq) :
+ AS3935TwoWire(&Wire, address, irq)
+{
+}
+
+AS3935I2C::~AS3935I2C()
+{
+}
diff --git a/lib/AS3935MI/src/AS3935I2C.h b/lib/AS3935MI/src/AS3935I2C.h
new file mode 100644
index 0000000000..8c42899f9d
--- /dev/null
+++ b/lib/AS3935MI/src/AS3935I2C.h
@@ -0,0 +1,32 @@
+//Yet Another Arduino ams AS3935 'Franklin' lightning sensor library
+// Copyright (c) 2018-2019 Gregor Christandl
+// home: https://bitbucket.org/christandlg/as3935mi
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+#ifndef AS3935I2C_H_
+#define AS3935I2C_H_
+
+#include "AS3935TwoWire.h"
+
+class AS3935I2C :
+ public AS3935TwoWire
+{
+public:
+ AS3935I2C(uint8_t address, uint8_t irq);
+ virtual ~AS3935I2C();
+};
+
+#endif /* AS3935I2C_H_ */
diff --git a/lib/AS3935MI/src/AS3935MI.cpp b/lib/AS3935MI/src/AS3935MI.cpp
new file mode 100644
index 0000000000..4358460c71
--- /dev/null
+++ b/lib/AS3935MI/src/AS3935MI.cpp
@@ -0,0 +1,877 @@
+//Yet Another Arduino ams AS3935 'Franklin' lightning sensor library
+// Copyright (c) 2018-2019 Gregor Christandl
+// home: https://bitbucket.org/christandlg/as3935
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+#include "AS3935MI.h"
+
+
+#ifdef ESP8266
+#define getMicros64 micros64
+#elif defined(ESP32)
+#define getMicros64 esp_timer_get_time
+#else
+#define getMicros64 micros
+#endif
+
+
+// When we can't use attachInterruptArg to directly access volatile members,
+// we must use static variables in the .cpp file
+#ifndef AS3935MI_HAS_ATTACHINTERRUPTARG_FUNCTION
+ AS3935MI_VOLATILE_TYPE interrupt_timestamp_ = 0;
+ AS3935MI_VOLATILE_TYPE interrupt_count_ = 0;
+
+ // Store the time micros as 32-bit int so it can be stored and comprared as an atomic operation.
+ // Expected duration will be much less than 2^32 usec, thus overflow isn't an issue here
+ AS3935MI_VOLATILE_TYPE calibration_start_micros_ = 0;
+ AS3935MI_VOLATILE_TYPE calibration_end_micros_ = 0;
+
+ uint32_t nr_calibration_samples_ = AS3935MI_NR_CALIBRATION_SAMPLES;
+#endif
+
+AS3935MI::AS3935MI(uint8_t irq) :
+ irq_(irq),
+ tuning_cap_cache_(0),
+ mode_(AS3935MI::AS3935_INTERRUPT_UNINITIALIZED),
+ calibration_mode_edgetrigger_trigger_(AS3935MI_CALIBRATION_MODE_EDGE_TRIGGER),
+ calibration_mode_division_ratio_(AS3935MI_LCO_DIVISION_RATIO),
+ calibrated_ant_cap_(-1),
+ calibrate_all_ant_cap_(true)
+{
+ // Setup these in the constructor body as these might not be a member
+ // if AS3935MI_HAS_ATTACHINTERRUPTARG_FUNCTION is not defined.
+ interrupt_timestamp_ = 0;
+ interrupt_count_ = 0;
+
+ calibration_start_micros_ = 0;
+ calibration_end_micros_ = 0;
+
+ nr_calibration_samples_ = AS3935MI_NR_CALIBRATION_SAMPLES;
+
+ pinMode(irq_, INPUT);
+}
+
+AS3935MI::~AS3935MI()
+{
+ if (mode_ == AS3935MI::AS3935_INTERRUPT_NORMAL ||
+ mode_ == AS3935MI::AS3935_INTERRUPT_CALIBRATION) {
+ detachInterrupt(irq_);
+ }
+}
+
+bool AS3935MI::begin()
+{
+ if (!beginInterface())
+ return false;
+
+ writePowerDown(false);
+
+ setInterruptMode(AS3935MI::AS3935_INTERRUPT_DETACHED);
+ resetToDefaults();
+
+ return true;
+}
+
+uint8_t AS3935MI::readStormDistance()
+{
+ return readRegisterValue(AS3935_REGISTER_DISTANCE, AS3935_MASK_DISTANCE);
+}
+
+uint8_t AS3935MI::readInterruptSource()
+{
+ interrupt_timestamp_ = 0;
+ interrupt_count_ = 0;
+ return readRegisterValue(AS3935_REGISTER_INT, AS3935_MASK_INT);
+}
+
+bool AS3935MI::readPowerDown()
+{
+ return (readRegisterValue(AS3935_REGISTER_PWD, AS3935_MASK_PWD) == 1 ? true : false);
+}
+
+void AS3935MI::writePowerDown(bool enabled)
+{
+ writeRegisterValue(AS3935_REGISTER_PWD, AS3935_MASK_PWD, enabled ? 1 : 0);
+ if (!enabled) {
+ delayMicroseconds(AS3935_TIMEOUT);
+ }
+}
+
+bool AS3935MI::readMaskDisturbers()
+{
+ return (readRegisterValue(AS3935_REGISTER_MASK_DIST, AS3935_MASK_MASK_DIST) == 1 ? true : false);
+}
+
+void AS3935MI::writeMaskDisturbers(bool enabled)
+{
+ writeRegisterValue(AS3935_REGISTER_MASK_DIST, AS3935_MASK_MASK_DIST, enabled ? 1 : 0);
+}
+
+uint8_t AS3935MI::readAFE()
+{
+ return readRegisterValue(AS3935_REGISTER_AFE_GB, AS3935_MASK_AFE_GB);
+}
+
+void AS3935MI::writeAFE(uint8_t afe_setting)
+{
+ writeRegisterValue(AS3935_REGISTER_AFE_GB, AS3935_MASK_AFE_GB, afe_setting);
+}
+
+uint8_t AS3935MI::readNoiseFloorThreshold()
+{
+ return readRegisterValue(AS3935_REGISTER_NF_LEV, AS3935_MASK_NF_LEV);
+}
+
+void AS3935MI::writeNoiseFloorThreshold(uint8_t threshold)
+{
+ if (threshold > AS3935_NFL_7)
+ return;
+
+ writeRegisterValue(AS3935_REGISTER_NF_LEV, AS3935_MASK_NF_LEV, threshold);
+
+ delayMicroseconds(AS3935_TIMEOUT);
+}
+
+uint8_t AS3935MI::readWatchdogThreshold()
+{
+ return readRegisterValue(AS3935_REGISTER_WDTH, AS3935_MASK_WDTH);
+}
+
+void AS3935MI::writeWatchdogThreshold(uint8_t threshold)
+{
+ if (threshold > AS3935_WDTH_15)
+ return;
+
+ writeRegisterValue(AS3935_REGISTER_WDTH, AS3935_MASK_WDTH, threshold);
+
+ delayMicroseconds(AS3935_TIMEOUT);
+}
+
+uint8_t AS3935MI::readSpikeRejection()
+{
+ return readRegisterValue(AS3935_REGISTER_SREJ, AS3935_MASK_SREJ);
+}
+
+void AS3935MI::writeSpikeRejection(uint8_t threshold)
+{
+ if (threshold > AS3935_SREJ_15)
+ return;
+
+ writeRegisterValue(AS3935_REGISTER_SREJ, AS3935_MASK_SREJ, threshold);
+
+ delayMicroseconds(AS3935_TIMEOUT);
+}
+
+uint32_t AS3935MI::readEnergy()
+{
+ uint32_t energy = 0;
+ //from https://www.eevblog.com/forum/microcontrollers/define-mmsbyte-for-as3935-lightning-detector/
+ //Reg 0x04: Energy word, bits 0 : 7
+ //Reg 0x05 : Energy word, bits 8 : 15
+ //Reg 0x06 : Energy word, bits 16 : 20
+ //energy |= LSB
+ //energy |= (MSB << 8)
+ //energy |= (MMSB << 16)
+ energy |= static_cast(readRegisterValue(AS3935_REGISTER_S_LIG_L, AS3935_MASK_S_LIG_L));
+ energy |= (static_cast(readRegisterValue(AS3935_REGISTER_S_LIG_M, AS3935_MASK_S_LIG_M)) << 8);
+ energy |= (static_cast(readRegisterValue(AS3935_REGISTER_S_LIG_MM, AS3935_MASK_S_LIG_MM)) << 16);
+
+ return energy;
+}
+
+uint8_t AS3935MI::readAntennaTuning()
+{
+ // Do not call readRegisterValue(AS3935_REGISTER_TUN_CAP, AS3935_MASK_TUN_CAP)
+ // here as we need to be able to detect read errors.
+ const uint8_t return_value = readRegister(AS3935_REGISTER_TUN_CAP);
+ if (return_value != static_cast(-1)) {
+ // No read error, so update the tuning_cap_cache_
+ tuning_cap_cache_ = return_value & AS3935_MASK_TUN_CAP;
+ } else {
+ return tuning_cap_cache_ & AS3935_MASK_TUN_CAP;
+ }
+
+ return return_value & AS3935_MASK_TUN_CAP;
+}
+
+bool AS3935MI::writeAntennaTuning(uint8_t tuning)
+{
+ if ((tuning & ~AS3935_MASK_TUN_CAP) != 0) {
+ return false;
+ }
+ tuning_cap_cache_ = tuning;
+ writeRegisterValue(AS3935_REGISTER_TUN_CAP, AS3935_MASK_TUN_CAP, tuning);
+ return true;
+}
+
+uint8_t AS3935MI::readDivisionRatio()
+{
+ return readRegisterValue(AS3935_REGISTER_LCO_FDIV, AS3935_MASK_LCO_FDIV);
+}
+
+void AS3935MI::writeDivisionRatio(uint8_t ratio)
+{
+ writeRegisterValue(AS3935_REGISTER_LCO_FDIV, AS3935_MASK_LCO_FDIV, ratio);
+}
+
+uint8_t AS3935MI::readMinLightnings()
+{
+ return readRegisterValue(AS3935_REGISTER_MIN_NUM_LIGH, AS3935_MASK_MIN_NUM_LIGH);
+}
+
+void AS3935MI::writeMinLightnings(uint8_t number)
+{
+ writeRegisterValue(AS3935_REGISTER_MIN_NUM_LIGH, AS3935_MASK_MIN_NUM_LIGH, number);
+}
+
+void AS3935MI::resetToDefaults()
+{
+ writeRegister(AS3935_REGISTER_PRESET_DEFAULT, AS3935_DIRECT_CMD);
+
+ delayMicroseconds(AS3935_TIMEOUT);
+}
+
+bool AS3935MI::calibrateRCO()
+{
+ //cannot calibrate if in power down mode.
+ if (readPowerDown())
+ return false;
+
+ //issue calibration command
+ writeRegister(AS3935_REGISTER_CALIB_RCO, AS3935_DIRECT_CMD);
+
+ //expose 1.1 MHz SRCO clock on IRQ pin
+ displaySrcoOnIrq(true);
+
+ //wait for calibration to finish...
+ delayMicroseconds(AS3935_TIMEOUT);
+
+ //stop exposing clock on IRQ pin
+ displaySrcoOnIrq(false);
+
+ //check calibration results. bits will be set if calibration failed.
+ bool success_TRCO = (readRegisterValue(AS3935_REGISTER_TRCO_CALIB_NOK, AS3935_MASK_TRCO_CALIB_ALL) == 0b10);
+ bool success_SRCO = (readRegisterValue(AS3935_REGISTER_SRCO_CALIB_NOK, AS3935_MASK_SRCO_CALIB_ALL) == 0b10);
+
+ return (success_TRCO && success_SRCO);
+}
+
+void AS3935MI::setFrequencyMeasureNrSamples(uint32_t nrSamples)
+{
+ nr_calibration_samples_ = nrSamples;
+}
+
+void AS3935MI::setFrequencyMeasureEdgeChange(bool triggerRisingAndFalling)
+{
+ calibration_mode_edgetrigger_trigger_ = triggerRisingAndFalling ? CHANGE : RISING;
+}
+
+void AS3935MI::setCalibrationDivisionRatio(uint8_t division_ratio)
+{
+ if (division_ratio <= AS3935MI::division_ratio_t::AS3935_DR_128) {
+ calibration_mode_division_ratio_ = static_cast(division_ratio);
+ } else {
+ calibration_mode_division_ratio_ = AS3935MI_LCO_DIVISION_RATIO;
+ }
+}
+
+bool AS3935MI::calibrateResonanceFrequency(int32_t& frequency, uint8_t division_ratio)
+{
+ if (readPowerDown())
+ return false;
+
+ setCalibrationDivisionRatio(division_ratio);
+
+ // Check for allowed deviation
+ constexpr uint32_t allowedDeviation = 500000 * AS3935MI_ALLOWED_DEVIATION;
+ const uint32_t cur_nr_samples = nr_calibration_samples_;
+
+ calibrated_ant_cap_ = -1;
+
+ uint32_t best_diff = 500000;
+ int8_t best_i = -1;
+
+ frequency = 0;
+
+ // Clear previous calibration results
+ for (uint8_t i = 0; i < 16; i++)
+ {
+ calibration_frequencies_[i] = 0.0f;
+ }
+
+ // When set to calibrate all ant_cap, the
+ uint8_t attempt = calibrate_all_ant_cap_ ? 0 : 2;
+ uint8_t lowest_cap = 0;
+ uint8_t highest_cap = 15;
+
+ // Find upper and lower bound of ant_caps to test using more samples
+ while (attempt > 0) {
+ --attempt;
+ const int32_t freq_0 = measureResonanceFrequency(
+ display_frequency_source_t::LCO, 0);
+ const int32_t freq_15 = measureResonanceFrequency(
+ display_frequency_source_t::LCO, 15);
+
+ if ((freq_0 == 0 || freq_15 == 0) || (freq_0 == freq_15)) {
+ setFrequencyMeasureNrSamples(nr_calibration_samples_ * 2);
+ } else {
+ const int estimated_cap = map(500000, freq_0, freq_15, 0, 15);
+ if (estimated_cap <= 0) {
+ highest_cap = 1;
+ } else if (estimated_cap >= 15) {
+ lowest_cap = 14;
+ } else {
+ lowest_cap = estimated_cap - 1;
+ highest_cap = estimated_cap + 1;
+ }
+ attempt = 0;
+ }
+ }
+
+ // Now test with higher number of samples to get better accuracy
+ if (nr_calibration_samples_ < AS3935MI_NR_CALIBRATION_SAMPLES) {
+ setFrequencyMeasureNrSamples(AS3935MI_NR_CALIBRATION_SAMPLES);
+ }
+ for (uint8_t i = lowest_cap; i <= highest_cap; i++)
+ {
+ const int32_t freq = measureResonanceFrequency(
+ display_frequency_source_t::LCO, i);
+
+ if (freq == 0) {
+ // restore nr of samples set by user
+ setFrequencyMeasureNrSamples(cur_nr_samples);
+ return false;
+ }
+ const uint32_t freq_diff = abs(500000 - freq);
+
+ if (freq_diff < best_diff) {
+ best_diff = freq_diff;
+ best_i = i;
+ frequency = freq;
+ }
+ }
+
+ // restore nr of samples set by user
+ setFrequencyMeasureNrSamples(cur_nr_samples);
+
+ if (best_i < 0) {
+ frequency = 0;
+ return false;
+ }
+
+ calibrated_ant_cap_ = best_i;
+
+ writeAntennaTuning(calibrated_ant_cap_);
+
+ return best_diff < allowedDeviation;
+}
+
+bool AS3935MI::calibrateResonanceFrequency(int32_t& frequency)
+{
+ return calibrateResonanceFrequency(frequency, calibration_mode_division_ratio_);
+}
+
+bool AS3935MI::calibrateResonanceFrequency()
+{
+ int32_t frequency = 0;
+ return calibrateResonanceFrequency(frequency, calibration_mode_division_ratio_);
+}
+
+bool AS3935MI::checkConnection()
+{
+ uint8_t afe = readAFE();
+
+ return ((afe == AS3935_INDOORS) || (afe == AS3935_OUTDOORS));
+}
+
+bool AS3935MI::checkIRQ()
+{
+ // Only need a quick check, so set nr of samples low as we're not yet interested in an accurate measurement
+ const uint32_t cur_nr_samples = nr_calibration_samples_;
+ setFrequencyMeasureNrSamples(128);
+ const uint32_t freq = measureResonanceFrequency(display_frequency_source_t::LCO);
+ setFrequencyMeasureNrSamples(cur_nr_samples);
+
+ // Expected LCO frequency is several kHz, so we should see at the very least see 1 kHz
+ return freq > 1000;
+}
+
+void AS3935MI::clearStatistics()
+{
+ writeRegisterValue(AS3935_REGISTER_CL_STAT, AS3935_MASK_CL_STAT, 1);
+ writeRegisterValue(AS3935_REGISTER_CL_STAT, AS3935_MASK_CL_STAT, 0);
+ writeRegisterValue(AS3935_REGISTER_CL_STAT, AS3935_MASK_CL_STAT, 1);
+}
+
+bool AS3935MI::decreaseNoiseFloorThreshold() {
+ uint8_t nf_lev{};
+ return decreaseNoiseFloorThreshold(nf_lev);
+}
+
+bool AS3935MI::decreaseNoiseFloorThreshold(uint8_t& nf_lev)
+{
+ nf_lev = readNoiseFloorThreshold();
+
+ if (nf_lev == AS3935_NFL_0)
+ return false;
+
+ writeNoiseFloorThreshold(--nf_lev);
+
+ return true;
+}
+
+bool AS3935MI::increaseNoiseFloorThreshold() {
+ uint8_t nf_lev{};
+ return increaseNoiseFloorThreshold(nf_lev);
+}
+
+bool AS3935MI::increaseNoiseFloorThreshold(uint8_t& nf_lev)
+{
+ nf_lev = readNoiseFloorThreshold();
+
+ if (nf_lev >= AS3935_NFL_7)
+ return false;
+
+ writeNoiseFloorThreshold(++nf_lev);
+
+ return true;
+}
+
+bool AS3935MI::decreaseWatchdogThreshold() {
+ uint8_t wdth{};
+ return decreaseWatchdogThreshold(wdth);
+}
+
+bool AS3935MI::decreaseWatchdogThreshold(uint8_t& wdth)
+{
+ wdth = readWatchdogThreshold();
+
+ if (wdth == AS3935_WDTH_0)
+ return false;
+
+ writeWatchdogThreshold(--wdth);
+
+ return true;
+}
+
+bool AS3935MI::increaseWatchdogThreshold() {
+ uint8_t wdth{};
+ return increaseWatchdogThreshold(wdth);
+}
+
+bool AS3935MI::increaseWatchdogThreshold(uint8_t& wdth)
+{
+ wdth = readWatchdogThreshold();
+
+ if (wdth >= AS3935_WDTH_15)
+ return false;
+
+ writeWatchdogThreshold(++wdth);
+
+ return true;
+}
+
+
+bool AS3935MI::decreaseSpikeRejection()
+{
+ uint8_t srej{};
+ return decreaseSpikeRejection(srej);
+}
+
+bool AS3935MI::decreaseSpikeRejection(uint8_t& srej)
+{
+ srej = readSpikeRejection();
+
+ if (srej == AS3935_SREJ_0)
+ return false;
+
+ writeSpikeRejection(--srej);
+
+ return true;
+}
+
+bool AS3935MI::increaseSpikeRejection()
+{
+ uint8_t srej{};
+ return increaseSpikeRejection(srej);
+}
+
+bool AS3935MI::increaseSpikeRejection(uint8_t& srej)
+{
+ srej = readSpikeRejection();
+
+ if (srej >= AS3935_SREJ_15)
+ return false;
+
+ writeSpikeRejection(++srej);
+
+ return true;
+}
+
+void AS3935MI::displayLcoOnIrq(bool enable)
+{
+ // With display of any frequency, the device may sometimes report NAK when reading registers
+ // So for this reason we're now writing directly and not try to read first, patch bits, write
+ uint8_t value = tuning_cap_cache_;
+ if (enable) {
+ value |= AS3935_MASK_DISP_LCO;
+ }
+ writeRegister(AS3935_REGISTER_DISP_LCO, value);
+}
+
+void AS3935MI::displaySrcoOnIrq(bool enable)
+{
+ uint8_t value = tuning_cap_cache_;
+ if (enable) {
+ value |= AS3935_MASK_DISP_SRCO;
+ }
+ writeRegister(AS3935_REGISTER_DISP_SRCO, value);
+}
+
+
+void AS3935MI::displayTrcoOnIrq(bool enable)
+{
+ uint8_t value = tuning_cap_cache_;
+ if (enable) {
+ value |= AS3935_MASK_DISP_TRCO;
+ }
+ writeRegister(AS3935_REGISTER_DISP_TRCO, value);
+}
+
+
+bool AS3935MI::validateCurrentResonanceFrequency(int32_t& frequency)
+{
+ frequency = measureResonanceFrequency(
+ display_frequency_source_t::LCO,
+ readAntennaTuning());
+
+ // Check for allowed deviation
+ constexpr int allowedDeviation = 500000 * AS3935MI_ALLOWED_DEVIATION;
+
+ return abs(500000 - frequency) < allowedDeviation;
+}
+
+int32_t AS3935MI::measureResonanceFrequency(display_frequency_source_t source)
+{
+ return measureResonanceFrequency(
+ source,
+ readAntennaTuning());
+}
+
+
+uint8_t AS3935MI::getMaskShift(uint8_t mask)
+{
+ uint8_t return_value = 0;
+
+ //count how many times the mask must be shifted right until the lowest bit is set
+ if (mask != 0)
+ {
+ while (!(mask & 1))
+ {
+ return_value++;
+ mask >>= 1;
+ }
+ }
+
+ return return_value;
+}
+
+uint8_t AS3935MI::getMaskedBits(uint8_t reg, uint8_t mask)
+{
+ //extract masked bits
+ return ((reg & mask) >> getMaskShift(mask));
+}
+
+uint8_t AS3935MI::setMaskedBits(uint8_t reg, uint8_t mask, uint8_t value)
+{
+ //clear mask bits in register
+ reg &= (~mask);
+
+ //set masked bits in register according to value
+ return ((value << getMaskShift(mask)) & mask) | reg;
+}
+
+uint8_t AS3935MI::readRegisterValue(uint8_t reg, uint8_t mask)
+{
+ return getMaskedBits(readRegister(reg), mask);
+}
+
+void AS3935MI::writeRegisterValue(uint8_t reg, uint8_t mask, uint8_t value)
+{
+ uint8_t reg_val = readRegister(reg);
+ writeRegister(reg, setMaskedBits(reg_val, mask, value));
+}
+
+
+uint32_t AS3935MI::computeCalibratedFrequency(int32_t divider)
+{
+ switch (divider)
+ {
+ case AS3935_DIVIDER_1:
+ case AS3935_DIVIDER_16:
+ case AS3935_DIVIDER_32:
+ case AS3935_DIVIDER_64:
+ case AS3935_DIVIDER_128:
+ break;
+ default:
+ return 0ul;
+ }
+
+ // Need to copy the timestamps first as they are volatile
+ const uint32_t start = calibration_start_micros_;
+ const uint32_t end = calibration_end_micros_;
+
+ if ((start == 0ul) || (end == 0ul)) {
+ return 0ul;
+ }
+
+ const int32_t duration_usec = (int32_t) (end - start);
+
+ if (duration_usec <= 0l) {
+ return 0ul;
+ }
+
+ // Compute measured frequency
+ // we have duration of nr_calibration_samples_ pulses in usec, thus measured frequency is:
+ // (nr_calibration_samples_ * 1000'000) / duration in usec.
+ // Actual frequency should take the division ratio into account.
+ uint64_t freq = (static_cast(divider) * 1000000ull * (nr_calibration_samples_ + 1));
+ if (calibration_mode_edgetrigger_trigger_ == CHANGE) {
+ // Counting on both rising and falling edge, so actual frequency is half
+ freq /= 2ull;
+ }
+
+ freq /= duration_usec;
+
+ return static_cast(freq);
+}
+
+
+uint32_t AS3935MI::measureResonanceFrequency(display_frequency_source_t source, uint8_t tuningCapacitance)
+{
+ setInterruptMode(interrupt_mode_t::AS3935_INTERRUPT_DETACHED);
+
+// delayMicroseconds(AS3935_TIMEOUT);
+
+ unsigned sourceFreq_kHz = 500;
+ int32_t divider = 1;
+
+ // display LCO on IRQ
+ switch (source) {
+ case display_frequency_source_t::LCO:
+ // set tuning capacitors
+ if (!writeAntennaTuning(tuningCapacitance)) {
+ return 0u;
+ }
+ displayLcoOnIrq(true);
+ writeDivisionRatio(calibration_mode_division_ratio_);
+ divider = 16 << static_cast(calibration_mode_division_ratio_);
+ sourceFreq_kHz = 500;
+ break;
+
+ // TD-er: Do not try to measure the 1.1 MHz signal as the ESP32 will not be able to keep up with all the interrupts.
+ case display_frequency_source_t::SRCO:
+ displaySrcoOnIrq(true);
+ sourceFreq_kHz = 1100;
+ break;
+ case display_frequency_source_t::TRCO:
+ displayTrcoOnIrq(true);
+ sourceFreq_kHz = 33;
+ break;
+ }
+
+ setInterruptMode(interrupt_mode_t::AS3935_INTERRUPT_CALIBRATION);
+
+ // Need to give enough time for the sensor to set the LCO signal on the IRQ pin
+ delayMicroseconds(AS3935_TIMEOUT);
+ calibration_end_micros_ = 0ul;
+ interrupt_count_ = 0ul;
+ calibration_start_micros_ = static_cast(getMicros64());
+
+ // Wait for the amount of samples to be counted (or timeout)
+ // Typically this takes 32 msec for the 500 kHz LCO when taking 1000 samples
+ unsigned expectedDuration = (divider * nr_calibration_samples_) / sourceFreq_kHz;
+ if (expectedDuration < 10) {
+ // For low nr of samples, we should still keep some minimum timeout of 10 msec.
+ expectedDuration = 10;
+ }
+
+ const uint32_t timeout = millis() + (2 * expectedDuration);
+ uint32_t freq = 0;
+
+ while (freq == 0 && (((int32_t)(millis() - timeout)) < 0)) {
+ delay(1);
+ freq = computeCalibratedFrequency(divider);
+ }
+
+ // Need to disable interrupts first or else sending I2C commands may fail
+ setInterruptMode(interrupt_mode_t::AS3935_INTERRUPT_DETACHED);
+
+ // stop displaying LCO on IRQ
+ displayLcoOnIrq(false);
+
+ if (source == display_frequency_source_t::LCO) {
+ calibration_frequencies_[tuningCapacitance] = freq;
+ }
+
+ return freq;
+}
+
+uint32_t AS3935MI::getInterruptTimestamp() const {
+ return interrupt_timestamp_;
+}
+
+uint32_t AS3935MI::getInterruptCount() const {
+ return interrupt_count_;
+}
+
+void AS3935MI::setInterruptMode(interrupt_mode_t mode) {
+ if (mode_ == mode) {
+ return;
+ }
+
+ if (mode_ == AS3935MI::AS3935_INTERRUPT_NORMAL ||
+ mode_ == AS3935MI::AS3935_INTERRUPT_CALIBRATION) {
+ detachInterrupt(irq_);
+ }
+
+ // set the IRQ pin as an input pin. do not use INPUT_PULLUP - the AS3935 will pull the pin
+ // high if an event is registered.
+ pinMode(irq_, INPUT);
+
+ interrupt_timestamp_ = 0;
+ interrupt_count_ = 0;
+ mode_ = mode;
+
+ switch (mode) {
+ case interrupt_mode_t::AS3935_INTERRUPT_UNINITIALIZED:
+ case interrupt_mode_t::AS3935_INTERRUPT_DETACHED:
+ break;
+ case interrupt_mode_t::AS3935_INTERRUPT_NORMAL:
+#ifdef AS3935MI_HAS_ATTACHINTERRUPTARG_FUNCTION
+ attachInterruptArg(digitalPinToInterrupt(irq_),
+ reinterpret_cast(interruptISR),
+ this,
+ RISING);
+#else
+ attachInterrupt(digitalPinToInterrupt(irq_),
+ interruptISR,
+ RISING);
+#endif
+ break;
+ case interrupt_mode_t::AS3935_INTERRUPT_CALIBRATION:
+ calibration_start_micros_ = 0;
+ calibration_end_micros_ = 0;
+#ifdef AS3935MI_HAS_ATTACHINTERRUPTARG_FUNCTION
+ attachInterruptArg(digitalPinToInterrupt(irq_),
+ reinterpret_cast(calibrateISR),
+ this,
+ calibration_mode_edgetrigger_trigger_);
+#else
+ attachInterrupt(digitalPinToInterrupt(irq_),
+ calibrateISR,
+ calibration_mode_edgetrigger_trigger_);
+#endif
+ break;
+ }
+}
+
+bool AS3935MI::checkProperlySetToListenMode() {
+ if (mode_ != AS3935MI::AS3935_INTERRUPT_NORMAL) {
+ // Nothing to check here, so just return all OK
+ return true;
+ }
+
+ if (interrupt_timestamp_ == 0 || interrupt_count_ == 0) {
+ // No interrupts since last clearing of these interrupt variables.
+ return true;
+ }
+
+ int retries = 2;
+ while (retries > 0 && interrupt_count_ < 2) {
+ // The possible displayed frequencies are several kHz,
+ // so time since last interrupt should be less than a msec since
+ // last trigger if these frequencies are still present.
+ //
+ // In 'normal' mode, the interrupt count should never be more than 1
+ // as the IRQ pin will be kept high until the interrupt source is read.
+ //
+ // N.B. Use the volatile variable here as those may be updated inbetween.
+ const int32_t msec_passed_since = (int32_t)(millis() - interrupt_timestamp_);
+ if (msec_passed_since > 2) {
+ return true;
+ }
+ --retries;
+ delay(1);
+ }
+
+ // Apparently there is still some frequency being displayed.
+ setInterruptMode(AS3935MI::AS3935_INTERRUPT_DETACHED);
+
+ delayMicroseconds(AS3935_TIMEOUT);
+
+ // stop displaying LCO on IRQ
+ displayLcoOnIrq(false);
+ delayMicroseconds(AS3935_TIMEOUT);
+
+ // restore normal operation
+ setInterruptMode(AS3935MI::AS3935_INTERRUPT_NORMAL);
+
+ // It wasn't how it should be so return false
+ return false;
+}
+
+
+#ifdef AS3935MI_HAS_ATTACHINTERRUPTARG_FUNCTION
+void AS3935MI_IRAM_ATTR AS3935MI::interruptISR(AS3935MI *self) {
+ self->interrupt_timestamp_ = millis();
+ ++(self->interrupt_count_);
+}
+
+void AS3935MI_IRAM_ATTR AS3935MI::calibrateISR(AS3935MI *self) {
+ // interrupt_count_ is volatile, so we can miss when testing for exactly nr_calibration_samples_
+ if (self->interrupt_count_ < self->nr_calibration_samples_) {
+ ++self->interrupt_count_;
+ }
+ else if (self->calibration_end_micros_ == 0ul) {
+ self->calibration_end_micros_ = static_cast(getMicros64());
+ }
+}
+#else
+void AS3935MI_IRAM_ATTR AS3935MI::interruptISR() {
+ interrupt_timestamp_ = millis();
+ ++interrupt_count_;
+}
+
+void AS3935MI_IRAM_ATTR AS3935MI::calibrateISR() {
+ // interrupt_count_ is volatile, so we can miss when testing for exactly nr_calibration_samples_
+ if (interrupt_count_ < nr_calibration_samples_) {
+ ++interrupt_count_;
+ }
+ else if (calibration_end_micros_ == 0ul) {
+ calibration_end_micros_ = static_cast(getMicros64());
+ }
+}
+#endif
+
+int32_t AS3935MI::getAntCapFrequency(uint8_t tuningCapacitance) const
+{
+ constexpr unsigned int nrElements = sizeof(calibration_frequencies_) / sizeof(calibration_frequencies_[0]);
+ if (tuningCapacitance < nrElements) {
+ return calibration_frequencies_[tuningCapacitance];
+ }
+ return -1;
+}
\ No newline at end of file
diff --git a/lib/AS3935MI/src/AS3935MI.h b/lib/AS3935MI/src/AS3935MI.h
new file mode 100644
index 0000000000..090ff99c33
--- /dev/null
+++ b/lib/AS3935MI/src/AS3935MI.h
@@ -0,0 +1,574 @@
+//Yet Another Arduino ams AS3935 'Franklin' lightning sensor library
+// Copyright (c) 2018-2019 Gregor Christandl
+// home: https://bitbucket.org/christandlg/as3935mi
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+#ifndef AS3935MI_H_
+#define AS3935MI_H_
+
+#include
+
+#if defined(ESP8266) || defined(ESP32)
+// When we can't use attachInterruptArg to directly access volatile members,
+// we must use static variables in the .cpp file
+// This means only a single instance of this class can be used.
+//
+// When we can use attachInterruptArg, we can use volatile members
+// and thus have multiple instances of this class without jumping through hoops
+// to avoid issues sharing volatile variables
+
+#define AS3935MI_HAS_ATTACHINTERRUPTARG_FUNCTION
+
+#define AS3935MI_IRAM_ATTR IRAM_ATTR
+#endif
+
+#if ESP_IDF_VERSION_MAJOR >= 5
+# include
+#endif
+
+#ifndef AS3935MI_IRAM_ATTR
+// Define this attribute as empty for platforms that don't need a special
+// IRAM_ATTR for ISR callback functions
+#define AS3935MI_IRAM_ATTR
+#endif
+
+// Allow for 3.5% deviation
+# define AS3935MI_ALLOWED_DEVIATION 0.035f
+
+// Division ratio and nr of samples chosen so we expect a
+// 500 kHz LCO measurement to take about 18 msec on ESP32
+// On others it will take about 32 msec.
+// ESP8266 can't handle > 20 kHz interrupt calls very well,
+// therefore set to DR_32 and edge trigger to "RISING"
+# ifdef ESP32
+
+// Expected LCO frequency for DR_16 = 31250 Hz
+# define AS3935MI_LCO_DIVISION_RATIO AS3935MI::AS3935_DR_16
+# define AS3935MI_NR_CALIBRATION_SAMPLES 1000ul
+# define AS3935MI_CALIBRATION_MODE_EDGE_TRIGGER CHANGE
+# else // ifdef ESP32
+
+// Expected LCO frequency for DR_32 = 15625 Hz
+# define AS3935MI_LCO_DIVISION_RATIO AS3935MI::AS3935_DR_32
+# define AS3935MI_NR_CALIBRATION_SAMPLES 500ul
+# define AS3935MI_CALIBRATION_MODE_EDGE_TRIGGER RISING
+# endif // ifdef ESP32
+
+class AS3935MI
+{
+public:
+ enum afe_setting_t : uint8_t
+ {
+ AS3935_INDOORS = 0b10010,
+ AS3935_OUTDOORS = 0b01110
+ };
+
+ enum interrupt_name_t : uint8_t
+ {
+ AS3935_INT_DUPDATE = 0b0000,//distance estimation has changed due to purging of old events in the statistics, based on the lightning distance estimation algorithm.
+ AS3935_INT_NH = 0b0001, //noise level too high
+ AS3935_INT_D = 0b0100, //disturber detected
+ AS3935_INT_L = 0b1000 //lightning interrupt
+ };
+
+ enum wdth_setting_t : uint8_t
+ {
+ AS3935_WDTH_0 = 0b0000,
+ AS3935_WDTH_1 = 0b0001,
+ AS3935_WDTH_2 = 0b0010,
+ AS3935_WDTH_3 = 0b0011,
+ AS3935_WDTH_4 = 0b0100,
+ AS3935_WDTH_5 = 0b0101,
+ AS3935_WDTH_6 = 0b0110,
+ AS3935_WDTH_7 = 0b0111,
+ AS3935_WDTH_8 = 0b1000,
+ AS3935_WDTH_9 = 0b1001,
+ AS3935_WDTH_10 = 0b1010,
+ AS3935_WDTH_11 = 0b1011,
+ AS3935_WDTH_12 = 0b1100,
+ AS3935_WDTH_13 = 0b1101,
+ AS3935_WDTH_14 = 0b1110,
+ AS3935_WDTH_15 = 0b1111
+ };
+
+ enum srej_setting_t : uint8_t
+ {
+ AS3935_SREJ_0 = 0b0000,
+ AS3935_SREJ_1 = 0b0001,
+ AS3935_SREJ_2 = 0b0010,
+ AS3935_SREJ_3 = 0b0011,
+ AS3935_SREJ_4 = 0b0100,
+ AS3935_SREJ_5 = 0b0101,
+ AS3935_SREJ_6 = 0b0110,
+ AS3935_SREJ_7 = 0b0111,
+ AS3935_SREJ_8 = 0b1000,
+ AS3935_SREJ_9 = 0b1001,
+ AS3935_SREJ_10 = 0b1010,
+ AS3935_SREJ_11 = 0b1011,
+ AS3935_SREJ_12 = 0b1100,
+ AS3935_SREJ_13 = 0b1101,
+ AS3935_SREJ_14 = 0b1110,
+ AS3935_SREJ_15 = 0b1111
+ };
+
+ enum noise_floor_threshold_t : uint8_t
+ {
+ AS3935_NFL_0 = 0b000,
+ AS3935_NFL_1 = 0b001,
+ AS3935_NFL_2 = 0b010, //default
+ AS3935_NFL_3 = 0b011,
+ AS3935_NFL_4 = 0b100,
+ AS3935_NFL_5 = 0b101,
+ AS3935_NFL_6 = 0b110,
+ AS3935_NFL_7 = 0b111,
+ };
+
+ enum min_num_lightnings_t : uint8_t
+ {
+ AS3935_MNL_1 = 0b00, //minimum number of lightnings: 1
+ AS3935_MNL_5 = 0b01, //minimum number of lightnings: 5
+ AS3935_MNL_9 = 0b10, //minimum number of lightnings: 9
+ AS3935_MNL_16 = 0b11, //minimum number of lightnings: 16
+ };
+
+ enum division_ratio_t : uint8_t
+ {
+ AS3935_DR_16 = 0b00,
+ AS3935_DR_32 = 0b01,
+ AS3935_DR_64 = 0b10,
+ AS3935_DR_128 = 0b11
+ };
+
+
+ enum class display_frequency_source_t {
+ LCO, // 500 kHz resonance freq
+ SRCO, // 1.1 MHz signal
+ TRCO // 32768 Hz signal
+ };
+
+ static const uint8_t AS3935_DST_OOR = 0b111111; //detected lightning was out of range
+
+ AS3935MI(uint8_t irq);
+ virtual ~AS3935MI();
+
+ bool begin();
+
+ /*
+ @return storm distance in km. */
+ uint8_t readStormDistance();
+
+ /*
+ @return interrupt source as AS9395::interrupt_name_t. */
+ uint8_t readInterruptSource();
+
+ /*
+ @return true: powered down, false: powered up. */
+ bool readPowerDown();
+
+ /*
+ @param enabled: true to power down, false to power up. */
+ void writePowerDown(bool enabled);
+
+ /*
+ @return true if disturbers are masked, false otherwise. */
+ bool readMaskDisturbers();
+
+ /*
+ @param enabled true to mask disturbers, false otherwise. */
+ void writeMaskDisturbers(bool enabled);
+
+ /*
+ @return AFE setting as afe_setting_t. */
+ uint8_t readAFE();
+
+ /*
+ @param afe_setting AFE setting as one if afe_setting_t. */
+ void writeAFE(uint8_t afe_setting);
+
+ /*
+ @return current noise floor. */
+ uint8_t readNoiseFloorThreshold();
+
+ /*
+ writes a noise floor threshold setting to the sensor.
+ @param threshold as noise_floor_threshold_t*/
+ void writeNoiseFloorThreshold(uint8_t threshold);
+
+ /*
+ @return current noise floor threshold. */
+ uint8_t readWatchdogThreshold();
+
+ /*
+ @param noise floor threshold setting. */
+ void writeWatchdogThreshold(uint8_t noise_floor);
+
+ /*
+ @return current spike rejection setting as srej_setting_t. */
+ uint8_t readSpikeRejection();
+
+ /*
+ @param spike rejection setting as srej_setting_t. */
+ void writeSpikeRejection(uint8_t threshold);
+
+ /*
+ @return lightning energy. no physical meaning. */
+ uint32_t readEnergy();
+
+ /*
+ @return antenna tuning*/
+ uint8_t readAntennaTuning();
+
+ /*
+ writes an antenna tuning setting to the sensor. */
+ bool writeAntennaTuning(uint8_t tuning);
+
+ /*
+ read the currently set antenna tuning division ratio from the sensor. */
+ uint8_t readDivisionRatio();
+
+ /*
+ writes an antenna tuning division ratio setting to the sensor. */
+ void writeDivisionRatio(uint8_t ratio);
+
+ /*
+ get the currently set minimum number of lightnings in the last 15 minues before lightning interrupts are issued, as min_num_lightnings_t. */
+ uint8_t readMinLightnings();
+
+ /*
+ @param minimum number of lightnings in the last 15 minues before lightning interrupts are issued, as min_num_lightnings_t. */
+ void writeMinLightnings(uint8_t number);
+
+ /*
+ resets all registers to default values. */
+ void resetToDefaults();
+
+ /*
+ calibrates the AS3935 TCRO accordingto procedure in AS3935 datasheet p36. must be done *after* calibrating the resonance frequency.
+ @return true on success, false otherwise. */
+ bool calibrateRCO();
+
+ // Set the number of samples counted during frequency measurements.
+ void setFrequencyMeasureNrSamples(uint32_t nrSamples);
+
+ // Set the edge mode trigger for any frequency measurement to either RISING or CHANGE
+ void setFrequencyMeasureEdgeChange(bool triggerRisingAndFalling);
+
+ // Set the division ratio, only used when measuring LCO (thus only during calibration)
+ void setCalibrationDivisionRatio(uint8_t division_ratio);
+
+ /*
+ calibrates the AS3935 antenna's resonance frequency.
+ @param (by reference, write only) frequency: after return, will hold the frequency the AS3935
+ has been calibrated to.
+ @return true on success, false on failure or if the resonance frequency could not be tuned
+ to within +-3.5% of 500kHz. */
+ bool calibrateResonanceFrequency(
+ int32_t& frequency,
+ uint8_t division_ratio);
+ bool calibrateResonanceFrequency(
+ int32_t& frequency);
+ bool calibrateResonanceFrequency();
+
+ /*
+ checks if the sensor is connected by attempting to read the AFE gain boost setting.
+ @return true if the AFE gain boost setting is 0b10010 or 0b01110, false otherwise. */
+ bool checkConnection();
+
+ /*
+ checks the IRQ pin by instructing the AS3935 to display the antenna's resonance frequency on the IRQ pin
+ and monitoring the pin for changing levels. IRQ pin interrupt must not be enabled during this test.
+ The test takes approximately 14ms. the test is considered successful if more than 100 transitions have
+ been detected (to prevent false positives).
+ @return true if more than 100 changes in IRQ pin logic level were detected, false otherwise. */
+ bool checkIRQ();
+
+ /*
+ * clears lightning distance estimation statistics
+ */
+ void clearStatistics();
+
+ /*
+ increases the noise floor threshold setting, if possible.
+ @return true on success, false otherwise. */
+ bool decreaseNoiseFloorThreshold();
+ bool decreaseNoiseFloorThreshold(uint8_t& nf_lev);
+
+ /*
+ increases the noise floor threshold setting, if possible.
+ @return new value on success, 0 otherwise. */
+ bool increaseNoiseFloorThreshold();
+ bool increaseNoiseFloorThreshold(uint8_t& nf_lev);
+
+ /*
+ increases the watchdog threshold setting, if possible.
+ @return true on success, false otherwise. */
+ bool decreaseWatchdogThreshold();
+ bool decreaseWatchdogThreshold(uint8_t& wdth);
+
+ /*
+ increases the watchdog threshold setting, if possible.
+ @return true on success, false otherwise. */
+ bool increaseWatchdogThreshold();
+ bool increaseWatchdogThreshold(uint8_t& wdth);
+
+ /*
+ increases the spike rejection setting, if possible.
+ @return true on success, false otherwise. */
+ bool decreaseSpikeRejection();
+ bool decreaseSpikeRejection(uint8_t& srej);
+
+ /*
+ increases the spike rejection setting, if possible.
+ @return true on success, false otherwise. */
+ bool increaseSpikeRejection();
+ bool increaseSpikeRejection(uint8_t& srej);
+
+ // Ideally 500 kHz signal divided by the set division ratio
+ void displayLcoOnIrq(bool enable);
+
+ // Ideally 1.1 MHz signal
+ void displaySrcoOnIrq(bool enable);
+
+ // Ideally 32.768 kHz signal
+ void displayTrcoOnIrq(bool enable);
+
+
+ bool validateCurrentResonanceFrequency(int32_t& frequency);
+
+ int32_t measureResonanceFrequency(display_frequency_source_t source);
+
+
+private:
+ enum AS3935_registers_t : uint8_t
+ {
+ AS3935_REGISTER_AFE_GB = 0x00, //Analog Frontend Gain Boost
+ AS3935_REGISTER_PWD = 0x00, //Power Down
+ AS3935_REGISTER_NF_LEV = 0x01, //Noise Floor Level
+ AS3935_REGISTER_WDTH = 0x01, //Watchdog threshold
+ AS3935_REGISTER_CL_STAT = 0x02, //Clear statistics
+ AS3935_REGISTER_MIN_NUM_LIGH = 0x02, //Minimum number of lightnings
+ AS3935_REGISTER_SREJ = 0x02, //Spike rejection
+ AS3935_REGISTER_LCO_FDIV = 0x03, //Frequency division ratio for antenna tuning
+ AS3935_REGISTER_MASK_DIST = 0x03, //Mask Disturber
+ AS3935_REGISTER_INT = 0x03, //Interrupt
+ AS3935_REGISTER_S_LIG_L = 0x04, //Energy of the Single Lightning LSBYTE
+ AS3935_REGISTER_S_LIG_M = 0x05, //Energy of the Single Lightning MSBYTE
+ AS3935_REGISTER_S_LIG_MM = 0x06, //Energy of the Single Lightning MMSBYTE
+ AS3935_REGISTER_DISTANCE = 0x07, //Distance estimation
+ AS3935_REGISTER_DISP_LCO = 0x08, //Display LCO on IRQ pin
+ AS3935_REGISTER_DISP_SRCO = 0x08, //Display SRCO on IRQ pin
+ AS3935_REGISTER_DISP_TRCO = 0x08, //Display TRCO on IRQ pin
+ AS3935_REGISTER_TUN_CAP = 0x08, //Internal Tuning Capacitors (from 0 to 120pF in steps of 8pF)
+ AS3935_REGISTER_TRCO_CALIB_DONE = 0x3A, //Calibration of TRCO done (1=successful)
+ AS3935_REGISTER_TRCO_CALIB_NOK = 0x3A, //Calibration of TRCO unsuccessful (1 = not successful)
+ AS3935_REGISTER_SRCO_CALIB_DONE = 0x3B, //Calibration of SRCO done (1=successful)
+ AS3935_REGISTER_SRCO_CALIB_NOK = 0x3B, //Calibration of SRCO unsuccessful (1 = not successful)
+ AS3935_REGISTER_PRESET_DEFAULT = 0x3C, //Sets all registers in default mode
+ AS3935_REGISTER_CALIB_RCO = 0x3D //Sets all registers in default mode
+ };
+
+ enum AS3935_register_mask_t : uint8_t
+ {
+ AS3935_MASK_AFE_GB = 0b00111110, //Analog Frontend Gain Boost
+ AS3935_MASK_PWD = 0b00000001, //Power Down
+ AS3935_MASK_NF_LEV = 0b01110000, //Noise Floor Level
+ AS3935_MASK_WDTH = 0b00001111, //Watchdog threshold
+ AS3935_MASK_CL_STAT = 0b01000000, //Clear statistics
+ AS3935_MASK_MIN_NUM_LIGH = 0b00110000, //Minimum number of lightnings
+ AS3935_MASK_SREJ = 0b00001111, //Spike rejection
+ AS3935_MASK_LCO_FDIV = 0b11000000, //Frequency division ratio for antenna tuning
+ AS3935_MASK_MASK_DIST = 0b00100000, //Mask Disturber
+ AS3935_MASK_INT = 0b00001111, //Interrupt
+ AS3935_MASK_S_LIG_L = 0b11111111, //Energy of the Single Lightning LSBYTE
+ AS3935_MASK_S_LIG_M = 0b11111111, //Energy of the Single Lightning MSBYTE
+ AS3935_MASK_S_LIG_MM = 0b00001111, //Energy of the Single Lightning MMSBYTE
+ AS3935_MASK_DISTANCE = 0b00111111, //Distance estimation
+ AS3935_MASK_DISP_LCO = 0b10000000, //Display LCO on IRQ pin
+ AS3935_MASK_DISP_SRCO = 0b01000000, //Display SRCO on IRQ pin
+ AS3935_MASK_DISP_TRCO = 0b00100000, //Display TRCO on IRQ pin
+ AS3935_MASK_TUN_CAP = 0b00001111, //Internal Tuning Capacitors (from 0 to 120pF in steps of 8pF)
+ AS3935_MASK_TRCO_CALIB_DONE = 0b10000000, //Calibration of TRCO done (1=successful)
+ AS3935_MASK_TRCO_CALIB_NOK = 0b01000000, //Calibration of TRCO unsuccessful (1 = not successful)
+ AS3935_MASK_TRCO_CALIB_ALL = 0b11000000, //Calibration of TRCO done (0b10 = successful)
+ AS3935_MASK_SRCO_CALIB_DONE = 0b10000000, //Calibration of SRCO done (1=successful)
+ AS3935_MASK_SRCO_CALIB_NOK = 0b01000000, //Calibration of SRCO unsuccessful (1 = not successful)
+ AS3935_MASK_SRCO_CALIB_ALL = 0b11000000, //Calibration of SRCO done (0b10 = successful)
+ AS3935_MASK_PRESET_DEFAULT = 0b11111111, //Sets all registers in default mode
+ AS3935_MASK_CALIB_RCO = 0b11111111 //Sets all registers in default mode
+ };
+
+ enum co_divider_t
+ {
+ AS3935_DIVIDER_1 = 1,
+ AS3935_DIVIDER_16 = 16,
+ AS3935_DIVIDER_32 = 32,
+ AS3935_DIVIDER_64 = 64,
+ AS3935_DIVIDER_128 = 128,
+ };
+
+
+ virtual bool beginInterface() = 0;
+
+ /*
+ @param mask
+ @return number of bits to shift value so it fits into mask. */
+ uint8_t getMaskShift(uint8_t mask);
+
+ /*
+ @param register value of register.
+ @param mask mask of value in register
+ @return value of masked bits. */
+ uint8_t getMaskedBits(uint8_t reg, uint8_t mask);
+
+ /*
+ @param register value of register
+ @param mask mask of value in register
+ @param value value to write into masked area
+ @param register value with masked bits set to value. */
+ uint8_t setMaskedBits(uint8_t reg, uint8_t mask, uint8_t value);
+
+ /*
+ reads the masked value from the register.
+ @param reg register to read.
+ @param mask mask of value.
+ @return masked value in register. */
+ uint8_t readRegisterValue(uint8_t reg, uint8_t mask);
+
+ /*
+ sets values in a register.
+ @param reg register to set values in
+ @param mask bits of register to set value in
+ @param value value to set */
+ void writeRegisterValue(uint8_t reg, uint8_t mask, uint8_t value);
+
+ /*
+ reads a register from the sensor. must be overwritten by derived classes.
+ @param reg register to read.
+ @return register content*/
+ virtual uint8_t readRegister(uint8_t reg) = 0;
+
+ /*
+ writes a register to the sensor. must be overwritten by derived classes.
+ this function is also used to send direct commands.
+ @param reg register to write to.
+ @param value value writeRegister write to register. */
+ virtual void writeRegister(uint8_t reg, uint8_t value) = 0;
+
+
+ uint32_t computeCalibratedFrequency(int32_t divider);
+
+public:
+
+ // Internal Tuning Capacitors (from 0 to 120pF in steps of 8pF)
+ uint32_t measureResonanceFrequency(display_frequency_source_t source, uint8_t tuningCapacitance);
+
+
+ enum interrupt_mode_t {
+ AS3935_INTERRUPT_UNINITIALIZED,
+ AS3935_INTERRUPT_DETACHED,
+ AS3935_INTERRUPT_NORMAL,
+ AS3935_INTERRUPT_CALIBRATION
+ };
+
+ interrupt_mode_t getInterruptMode() const { return mode_; }
+
+ uint32_t getInterruptTimestamp() const;
+
+ uint32_t getInterruptCount() const;
+
+ void setInterruptMode(interrupt_mode_t mode);
+
+ // For unknown reasons, the sensor may not have turned off any of the
+ // displayed frequencies and thus those may still cause lots of unwanted interrupts to be triggered.
+ bool checkProperlySetToListenMode();
+
+private:
+ static const uint8_t AS3935_DIRECT_CMD = 0x96;
+
+ static const uint32_t AS3935_TIMEOUT = 2000;
+
+ uint8_t irq_; //interrupt pin
+
+ // Tuning cap value is located in the same register as the display LCO/SRCO/TRCO flags
+ // When those are active the device may not give an ACK when trying to read
+ // (via I2C) the register to update those display flags
+ // To overcome this issue, we keep a cache of the tuning cap parameter
+ // and write directly to the register instead of read/set bits/write.
+ uint8_t tuning_cap_cache_ = 0;
+
+ AS3935MI::interrupt_mode_t mode_ = AS3935MI::AS3935_INTERRUPT_UNINITIALIZED;
+
+ int calibration_mode_edgetrigger_trigger_ = AS3935MI_CALIBRATION_MODE_EDGE_TRIGGER;
+ AS3935MI::division_ratio_t calibration_mode_division_ratio_ = AS3935MI_LCO_DIVISION_RATIO;
+
+
+#if ESP_IDF_VERSION_MAJOR >= 5
+#define AS3935MI_VOLATILE_TYPE std::atomic
+#else
+#define AS3935MI_VOLATILE_TYPE volatile uint32_t
+#endif
+
+
+#ifdef AS3935MI_HAS_ATTACHINTERRUPTARG_FUNCTION
+ static void AS3935MI_IRAM_ATTR interruptISR(AS3935MI *self);
+ static void AS3935MI_IRAM_ATTR calibrateISR(AS3935MI *self);
+
+ AS3935MI_VOLATILE_TYPE interrupt_timestamp_ = 0;
+ AS3935MI_VOLATILE_TYPE interrupt_count_ = 0;
+
+ // Store the time micros as 32-bit int so it can be stored and comprared as an atomic operation.
+ // Expected duration will be much less than 2^32 usec, thus overflow isn't an issue here
+ AS3935MI_VOLATILE_TYPE calibration_start_micros_ = 0;
+ AS3935MI_VOLATILE_TYPE calibration_end_micros_ = 0;
+
+ uint32_t nr_calibration_samples_ = AS3935MI_NR_CALIBRATION_SAMPLES;
+
+#else
+ static void AS3935MI_IRAM_ATTR interruptISR();
+ static void AS3935MI_IRAM_ATTR calibrateISR();
+#endif
+
+
+public:
+ // Return the result of the last frequency measurement of the given tuning cap index
+ // @retval -1 when tuningCapacitance is out of range
+ int32_t getAntCapFrequency(uint8_t tuningCapacitance) const;
+
+ // Return the best ant_cap found during last LCO calibration
+ // @retval -1 when no LCO calibration was performed
+ int8_t getCalibratedAntCap() const {
+ return calibrated_ant_cap_;
+ }
+
+ // When set to calibrate all ant_cap indices, the LCO calibration is
+ // effectively set to perform a 'slow' calibration.
+ // All caps will be tried and also using more samples.
+ void setCalibrateAllAntCap(bool calibrate_all) {
+ calibrate_all_ant_cap_ = calibrate_all;
+ }
+
+ bool getCalibrateAllAntCap() const {
+ return calibrate_all_ant_cap_;
+ }
+
+private:
+ int32_t calibration_frequencies_[16]{};
+ int8_t calibrated_ant_cap_ = -1;
+ bool calibrate_all_ant_cap_ = true;
+
+};
+
+#endif /* AS3935_H_ */
\ No newline at end of file
diff --git a/lib/AS3935MI/src/AS3935SPI.cpp b/lib/AS3935MI/src/AS3935SPI.cpp
new file mode 100644
index 0000000000..5f1d81fbcc
--- /dev/null
+++ b/lib/AS3935MI/src/AS3935SPI.cpp
@@ -0,0 +1,28 @@
+//Yet Another Arduino ams AS3935 'Franklin' lightning sensor library
+// Copyright (c) 2018-2019 Gregor Christandl
+// home: https://bitbucket.org/christandlg/as3935mi
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+#include "AS3935SPI.h"
+
+AS3935SPI::AS3935SPI(uint8_t cs, uint8_t irq) :
+ AS3935SPIClass(&SPI, cs, irq)
+{
+}
+
+AS3935SPI::~AS3935SPI()
+{
+}
diff --git a/lib/AS3935MI/src/AS3935SPI.h b/lib/AS3935MI/src/AS3935SPI.h
new file mode 100644
index 0000000000..20daa973e9
--- /dev/null
+++ b/lib/AS3935MI/src/AS3935SPI.h
@@ -0,0 +1,32 @@
+//Yet Another Arduino ams AS3935 'Franklin' lightning sensor library
+// Copyright (c) 2018-2019 Gregor Christandl
+// home: https://bitbucket.org/christandlg/as3935mi
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+#ifndef AS3935SPI_H_
+#define AS3935SPI_H_
+
+#include "AS3935SPIClass.h"
+
+class AS3935SPI :
+ public AS3935SPIClass
+{
+public:
+ AS3935SPI(uint8_t cs, uint8_t irq);
+ virtual ~AS3935SPI();
+};
+
+#endif /* AS3935SPI_H_ */
diff --git a/lib/AS3935MI/src/AS3935SPIClass.cpp b/lib/AS3935MI/src/AS3935SPIClass.cpp
new file mode 100644
index 0000000000..dc31019e77
--- /dev/null
+++ b/lib/AS3935MI/src/AS3935SPIClass.cpp
@@ -0,0 +1,103 @@
+//Yet Another Arduino ams AS3935 'Franklin' lightning sensor library
+// Copyright (c) 2018-2019 Gregor Christandl
+// home: https://bitbucket.org/christandlg/as3935mi
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+#include "AS3935SPIClass.h"
+
+#ifndef ESP32
+SPISettings AS3935SPIClass::spi_settings_ = SPISettings(1000000, MSBFIRST, SPI_MODE1);
+#endif
+
+AS3935SPIClass::AS3935SPIClass(SPIClass *spi, uint8_t cs, uint8_t irq) :
+ AS3935MI(irq),
+ spi_(spi),
+ cs_(cs)
+{
+}
+
+AS3935SPIClass::~AS3935SPIClass()
+{
+ spi_ = nullptr;
+}
+
+bool AS3935SPIClass::beginInterface()
+{
+ if (!spi_)
+ return false;
+
+ pinMode(cs_, OUTPUT);
+ digitalWrite(cs_, HIGH); //deselect
+
+ return true;
+}
+
+uint8_t AS3935SPIClass::readRegister(uint8_t reg)
+{
+ if (!spi_)
+ return 0;
+
+ uint8_t return_value = 0;
+
+#ifdef ESP32
+ spi_->setBitOrder(MSBFIRST);
+ spi_->setDataMode(SPI_MODE1);
+ spi_->setFrequency(1000000);
+ //spi_->setClockDivider(SPI_CLOCK_DIV16);
+#else
+ spi_->beginTransaction(spi_settings_);
+#endif
+
+ digitalWrite(cs_, LOW); //select sensor
+
+ spi_->transfer((reg & 0x3F) | 0x40); //select register and set pin 7 (indicates read)
+
+ return_value = spi_->transfer(0);
+
+ digitalWrite(cs_, HIGH); //deselect sensor
+
+#ifndef ESP32
+ spi_->endTransaction();
+#endif
+
+ return return_value;
+}
+
+void AS3935SPIClass::writeRegister(uint8_t reg, uint8_t value)
+{
+ if (!spi_)
+ return;
+
+#ifdef ESP32
+ spi_->setBitOrder(MSBFIRST);
+ spi_->setDataMode(SPI_MODE1);
+ spi_->setFrequency(1000000);
+ //spi_->setClockDivider(SPI_CLOCK_DIV16);
+#else
+ spi_->beginTransaction(spi_settings_);
+#endif
+
+ digitalWrite(cs_, LOW); //select sensor
+
+ spi_->transfer((reg & 0x3F)); //select regsiter
+ spi_->transfer(value);
+
+ digitalWrite(cs_, HIGH); //deselect sensor
+
+#ifndef ESP32
+ spi_->endTransaction();
+#endif
+}
diff --git a/lib/AS3935MI/src/AS3935SPIClass.h b/lib/AS3935MI/src/AS3935SPIClass.h
new file mode 100644
index 0000000000..55041541db
--- /dev/null
+++ b/lib/AS3935MI/src/AS3935SPIClass.h
@@ -0,0 +1,50 @@
+//Yet Another Arduino ams AS3935 'Franklin' lightning sensor library
+// Copyright (c) 2018-2019 Gregor Christandl
+// home: https://bitbucket.org/christandlg/as3935mi
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+#ifndef AS3935SPICLASS_H_
+#define AS3935SPICLASS_H_
+
+#include "AS3935MI.h"
+
+#include
+
+#include
+
+class AS3935SPIClass :
+ public AS3935MI
+{
+public:
+ AS3935SPIClass(SPIClass *spi, uint8_t cs, uint8_t irq);
+ virtual ~AS3935SPIClass();
+
+protected:
+ SPIClass *spi_;
+
+ uint8_t cs_;
+
+ static SPISettings spi_settings_; //spi settings object. is the same for all AS3935 sensors
+
+private:
+ virtual bool beginInterface();
+
+ virtual uint8_t readRegister(uint8_t reg);
+
+ virtual void writeRegister(uint8_t reg, uint8_t value);
+};
+
+#endif /* AS3935SPICLASS_H_ */
diff --git a/lib/AS3935MI/src/AS3935TwoWire.cpp b/lib/AS3935MI/src/AS3935TwoWire.cpp
new file mode 100644
index 0000000000..c1267ea239
--- /dev/null
+++ b/lib/AS3935MI/src/AS3935TwoWire.cpp
@@ -0,0 +1,81 @@
+//Yet Another Arduino ams AS3935 'Franklin' lightning sensor library
+// Copyright (c) 2018-2019 Gregor Christandl
+// home: https://bitbucket.org/christandlg/as3935mi
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+#include "AS3935TwoWire.h"
+
+AS3935TwoWire::AS3935TwoWire(TwoWire *wire, uint8_t address, uint8_t irq) :
+ AS3935MI(irq),
+ wire_(wire),
+ address_(address)
+{
+}
+
+AS3935TwoWire::~AS3935TwoWire()
+{
+ wire_ = nullptr;
+}
+
+bool AS3935TwoWire::beginInterface()
+{
+ if (!wire_)
+ return false;
+
+ switch (address_)
+ {
+ case AS3935I2C_A01:
+ case AS3935I2C_A10:
+ case AS3935I2C_A11:
+ break;
+ default:
+ //return false if an invalid I2C address was given.
+ return false;
+ }
+
+ return true;
+}
+
+uint8_t AS3935TwoWire::readRegister(uint8_t reg)
+{
+ if (!wire_)
+ return 0;
+
+#if defined(ARDUINO_SAM_DUE)
+ //workaround for Arduino Due. The Due seems not to send a repeated start with the code below, so this
+ //undocumented feature of Wire::requestFrom() is used. can be used on other Arduinos too (tested on Mega2560)
+ //see this thread for more info: https://forum.arduino.cc/index.php?topic=385377.0
+ wire_->requestFrom(address_, 1, reg, 1, true);
+#else
+ wire_->beginTransmission(address_);
+ wire_->write(reg);
+ wire_->endTransmission(false);
+ wire_->requestFrom(address_, static_cast(1));
+#endif
+
+ return wire_->read();
+}
+
+void AS3935TwoWire::writeRegister(uint8_t reg, uint8_t value)
+{
+ if (!wire_)
+ return;
+
+ wire_->beginTransmission(address_);
+ wire_->write(reg);
+ wire_->write(value);
+ wire_->endTransmission();
+}
\ No newline at end of file
diff --git a/lib/AS3935MI/src/AS3935TwoWire.h b/lib/AS3935MI/src/AS3935TwoWire.h
new file mode 100644
index 0000000000..1395288bef
--- /dev/null
+++ b/lib/AS3935MI/src/AS3935TwoWire.h
@@ -0,0 +1,55 @@
+//Yet Another Arduino ams AS3935 'Franklin' lightning sensor library
+// Copyright (c) 2018-2019 Gregor Christandl
+// home: https://bitbucket.org/christandlg/as3935mi
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+#ifndef AS3935TWOWIRE_H_
+#define AS3935TWOWIRE_H_
+
+#include "AS3935MI.h"
+
+#include
+
+#include
+
+class AS3935TwoWire :
+ public AS3935MI
+{
+public:
+ enum I2C_address_t : uint8_t
+ {
+ AS3935I2C_A01 = 0b01,
+ AS3935I2C_A10 = 0b10,
+ AS3935I2C_A11 = 0b11
+ };
+
+ AS3935TwoWire(TwoWire *wire, uint8_t address, uint8_t irq);
+ virtual ~AS3935TwoWire();
+
+protected:
+ TwoWire *wire_;
+
+ uint8_t address_;
+
+private:
+ virtual bool beginInterface();
+
+ virtual uint8_t readRegister(uint8_t reg);
+
+ virtual void writeRegister(uint8_t reg, uint8_t value);
+};
+
+#endif /* AS3935TWOWIRE_H_ */
diff --git a/lib/CircularBuffer/CircularBuffer.h b/lib/CircularBuffer/CircularBuffer.h
index 8a955753f5..088c675c3a 100644
--- a/lib/CircularBuffer/CircularBuffer.h
+++ b/lib/CircularBuffer/CircularBuffer.h
@@ -74,6 +74,8 @@ template.
- */
-
-template
-constexpr CircularBuffer::CircularBuffer() :
- head(buffer), tail(buffer), count(0) {
-}
-
-template
-bool CircularBuffer::unshift(T value) {
- if (head == buffer) {
- head = buffer + capacity;
- }
- *--head = value;
- if (count == capacity) {
- if (tail-- == buffer) {
- tail = buffer + capacity - 1;
- }
- return false;
- } else {
- if (count++ == 0) {
- tail = head;
- }
- return true;
- }
-}
-
-template
-bool CircularBuffer::push(T value) {
- if (++tail == buffer + capacity) {
- tail = buffer;
- }
- *tail = value;
- if (count == capacity) {
- if (++head == buffer + capacity) {
- head = buffer;
- }
- return false;
- } else {
- if (count++ == 0) {
- head = tail;
- }
- return true;
- }
-}
-
-template
-T CircularBuffer::shift() {
- if (count == 0) return *head;
- T result = *head++;
- if (head >= buffer + capacity) {
- head = buffer;
- }
- count--;
- return result;
-}
-
-template
-T CircularBuffer::pop() {
- if (count == 0) return *tail;
- T result = *tail--;
- if (tail < buffer) {
- tail = buffer + capacity - 1;
- }
- count--;
- return result;
-}
-
-template
-T inline CircularBuffer::first() const {
- return *head;
-}
-
-template
-T inline CircularBuffer::last() const {
- return *tail;
-}
-
-template
-T CircularBuffer::operator [](IT index) const {
- if (index >= count) return *tail;
- return *(buffer + ((head - buffer + index) % capacity));
-}
-
-template
-IT inline CircularBuffer::size() const {
- return count;
-}
-
-template
-IT inline CircularBuffer::available() const {
- return capacity - count;
-}
-
-template
-bool inline CircularBuffer::isEmpty() const {
- return count == 0;
-}
-
-template
-bool inline CircularBuffer::isFull() const {
- return count == capacity;
-}
-
-template
-void inline CircularBuffer::clear() {
- head = tail = buffer;
- count = 0;
-}
-
-#ifdef CIRCULAR_BUFFER_DEBUG
-#include
-template
-void inline CircularBuffer::debug(Print* out) {
- for (IT i = 0; i < capacity; i++) {
- int hex = (int)buffer + i;
- out->print("[");
- out->print(hex, HEX);
- out->print("] ");
- out->print(*(buffer + i));
- if (head == buffer + i) {
- out->print("<-head");
- }
- if (tail == buffer + i) {
- out->print("<-tail");
- }
- out->println();
- }
-}
-
-template
-void inline CircularBuffer::debugFn(Print* out, void (*printFunction)(Print*, T)) {
- for (IT i = 0; i < capacity; i++) {
- int hex = (int)buffer + i;
- out->print("[");
- out->print(hex, HEX);
- out->print("] ");
- printFunction(out, *(buffer + i));
- if (head == buffer + i) {
- out->print("<-head");
- }
- if (tail == buffer + i) {
- out->print("<-tail");
- }
- out->println();
- }
-}
-#endif
+/*
+ CircularBuffer.tpp - Circular buffer library for Arduino.
+ Copyright (c) 2017 Roberto Lo Giacco.
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation, either version 3 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see .
+ */
+
+template
+constexpr CircularBuffer::CircularBuffer() :
+ head(buffer), tail(buffer), count(0) {
+}
+
+template
+bool CircularBuffer::unshift(T value) {
+ if (head == buffer) {
+ head = buffer + capacity;
+ }
+ *--head = value;
+ if (count == capacity) {
+ if (tail-- == buffer) {
+ tail = buffer + capacity - 1;
+ }
+ return false;
+ } else {
+ if (count++ == 0) {
+ tail = head;
+ }
+ return true;
+ }
+}
+
+template
+bool CircularBuffer::push(T value) {
+ if (++tail == buffer + capacity) {
+ tail = buffer;
+ }
+ *tail = value;
+ if (count == capacity) {
+ if (++head == buffer + capacity) {
+ head = buffer;
+ }
+ return false;
+ } else {
+ if (count++ == 0) {
+ head = tail;
+ }
+ return true;
+ }
+}
+
+template
+bool CircularBuffer::set(IT index, T value) {
+ if (index >= count) return false;
+
+ *(buffer + ((head - buffer + index) % capacity)) = value;
+ return true;
+}
+
+
+template
+T CircularBuffer::shift() {
+ if (count == 0) return *head;
+ T result = *head++;
+ if (head >= buffer + capacity) {
+ head = buffer;
+ }
+ count--;
+ return result;
+}
+
+template
+T CircularBuffer::pop() {
+ if (count == 0) return *tail;
+ T result = *tail--;
+ if (tail < buffer) {
+ tail = buffer + capacity - 1;
+ }
+ count--;
+ return result;
+}
+
+template
+T inline CircularBuffer::first() const {
+ return *head;
+}
+
+template
+T inline CircularBuffer::last() const {
+ return *tail;
+}
+
+template
+T CircularBuffer::operator [](IT index) const {
+ if (index >= count) return *tail;
+ return *(buffer + ((head - buffer + index) % capacity));
+}
+
+template
+IT inline CircularBuffer::size() const {
+ return count;
+}
+
+template
+IT inline CircularBuffer::available() const {
+ return capacity - count;
+}
+
+template
+bool inline CircularBuffer::isEmpty() const {
+ return count == 0;
+}
+
+template
+bool inline CircularBuffer::isFull() const {
+ return count == capacity;
+}
+
+template
+void inline CircularBuffer::clear() {
+ head = tail = buffer;
+ count = 0;
+}
+
+#ifdef CIRCULAR_BUFFER_DEBUG
+#include
+template
+void inline CircularBuffer::debug(Print* out) {
+ for (IT i = 0; i < capacity; i++) {
+ int hex = (int)buffer + i;
+ out->print("[");
+ out->print(hex, HEX);
+ out->print("] ");
+ out->print(*(buffer + i));
+ if (head == buffer + i) {
+ out->print("<-head");
+ }
+ if (tail == buffer + i) {
+ out->print("<-tail");
+ }
+ out->println();
+ }
+}
+
+template
+void inline CircularBuffer::debugFn(Print* out, void (*printFunction)(Print*, T)) {
+ for (IT i = 0; i < capacity; i++) {
+ int hex = (int)buffer + i;
+ out->print("[");
+ out->print(hex, HEX);
+ out->print("] ");
+ printFunction(out, *(buffer + i));
+ if (head == buffer + i) {
+ out->print("<-head");
+ }
+ if (tail == buffer + i) {
+ out->print("<-tail");
+ }
+ out->println();
+ }
+}
+#endif
diff --git a/lib/VL53L0X/library.properties b/lib/VL53L0X/library.properties
index cac8675faf..5c3209c25a 100644
--- a/lib/VL53L0X/library.properties
+++ b/lib/VL53L0X/library.properties
@@ -1,9 +1,9 @@
-name=VL53L0X
-version=1.3.0
-author=Pololu
-maintainer=Pololu
-sentence=VL53L0X distance sensor library
-paragraph=This is a library for the Arduino IDE that helps interface with ST's VL53L0X distance sensor.
-category=Sensors
-url=https://github.com/pololu/vl53l0x-arduino
-architectures=*
+name=VL53L0X
+version=1.3.0
+author=Pololu
+maintainer=Pololu
+sentence=VL53L0X distance sensor library (heavily modified for ESPEasy)
+paragraph=This is a library for the Arduino IDE that helps interface with ST's VL53L0X distance sensor.
+category=Sensors
+url=https://github.com/pololu/vl53l0x-arduino
+architectures=*
diff --git a/lib/VL53L0X/src/VL53L0X.cpp b/lib/VL53L0X/src/VL53L0X.cpp
index f72b383079..72ef0173e6 100644
--- a/lib/VL53L0X/src/VL53L0X.cpp
+++ b/lib/VL53L0X/src/VL53L0X.cpp
@@ -1,1049 +1,1133 @@
-// Most of the functionality of this library is based on the VL53L0X API
-// provided by ST (STSW-IMG005), and some of the explanatory comments are quoted
-// or paraphrased from the API source code, API user manual (UM2039), and the
-// VL53L0X datasheet.
-
-#include "VL53L0X.h"
-#include
-
-// Defines /////////////////////////////////////////////////////////////////////
-
-// The Arduino two-wire interface uses a 7-bit number for the address,
-// and sets the last bit correctly based on reads and writes
-#define ADDRESS_DEFAULT 0b0101001
-
-// Record the current time to check an upcoming timeout against
-#define startTimeout() (timeout_start_ms = millis())
-
-// Check if timeout is enabled (set to nonzero value) and has expired
-#define checkTimeoutExpired() (io_timeout > 0 && ((uint16_t)(millis() - timeout_start_ms) > io_timeout))
-
-// Decode VCSEL (vertical cavity surface emitting laser) pulse period in PCLKs
-// from register value
-// based on VL53L0X_decode_vcsel_period()
-#define decodeVcselPeriod(reg_val) (((reg_val) + 1) << 1)
-
-// Encode VCSEL pulse period register value from period in PCLKs
-// based on VL53L0X_encode_vcsel_period()
-#define encodeVcselPeriod(period_pclks) (((period_pclks) >> 1) - 1)
-
-// Calculate macro period in *nanoseconds* from VCSEL period in PCLKs
-// based on VL53L0X_calc_macro_period_ps()
-// PLL_period_ps = 1655; macro_period_vclks = 2304
-#define calcMacroPeriod(vcsel_period_pclks) ((((uint32_t)2304 * (vcsel_period_pclks) * 1655) + 500) / 1000)
-
-// Constructors ////////////////////////////////////////////////////////////////
-
-VL53L0X::VL53L0X()
- : bus(&Wire)
- , address(ADDRESS_DEFAULT)
- , io_timeout(0) // no timeout
- , did_timeout(false)
-{
-}
-
-// Public Methods //////////////////////////////////////////////////////////////
-
-void VL53L0X::setAddress(uint8_t new_addr)
-{
- writeReg(I2C_SLAVE_DEVICE_ADDRESS, new_addr & 0x7F);
- address = new_addr;
-}
-
-// Initialize sensor using sequence based on VL53L0X_DataInit(),
-// VL53L0X_StaticInit(), and VL53L0X_PerformRefCalibration().
-// This function does not perform reference SPAD calibration
-// (VL53L0X_PerformRefSpadManagement()), since the API user manual says that it
-// is performed by ST on the bare modules; it seems like that should work well
-// enough unless a cover glass is added.
-// If io_2v8 (optional) is true or not given, the sensor is configured for 2V8
-// mode.
-bool VL53L0X::init(bool io_2v8)
-{
- initResult = F(""); // Clear any previous result
- // check model ID register (value specified in datasheet)
- uint8_t modelId = readReg(IDENTIFICATION_MODEL_ID);
- if (modelId != 0xEE) { // Recognize VL53L0X (0xEE)
- initResult = F("VL53L0X: Init: unrecognized Model-ID: 0x");
- initResult += String(modelId, HEX);
- return false;
- }
-
- // VL53L0X_DataInit() begin
-
- // sensor uses 1V8 mode for I/O by default; switch to 2V8 mode if necessary
- if (io_2v8)
- {
- writeReg(VHV_CONFIG_PAD_SCL_SDA__EXTSUP_HV,
- readReg(VHV_CONFIG_PAD_SCL_SDA__EXTSUP_HV) | 0x01); // set bit 0
- }
-
- // "Set I2C standard mode"
- writeReg(0x88, 0x00);
-
- writeReg(0x80, 0x01);
- writeReg(0xFF, 0x01);
- writeReg(0x00, 0x00);
- stop_variable = readReg(0x91);
- writeReg(0x00, 0x01);
- writeReg(0xFF, 0x00);
- writeReg(0x80, 0x00);
-
- // disable SIGNAL_RATE_MSRC (bit 1) and SIGNAL_RATE_PRE_RANGE (bit 4) limit checks
- writeReg(MSRC_CONFIG_CONTROL, readReg(MSRC_CONFIG_CONTROL) | 0x12);
-
- // set final range signal rate limit to 0.25 MCPS (million counts per second)
- setSignalRateLimit(0.25);
-
- writeReg(SYSTEM_SEQUENCE_CONFIG, 0xFF);
-
- // VL53L0X_DataInit() end
-
- // VL53L0X_StaticInit() begin
-
- uint8_t spad_count;
- bool spad_type_is_aperture;
- if (!getSpadInfo(&spad_count, &spad_type_is_aperture)) { return false; }
-
- // The SPAD map (RefGoodSpadMap) is read by VL53L0X_get_info_from_device() in
- // the API, but the same data seems to be more easily readable from
- // GLOBAL_CONFIG_SPAD_ENABLES_REF_0 through _6, so read it from there
- uint8_t ref_spad_map[6];
- readMulti(GLOBAL_CONFIG_SPAD_ENABLES_REF_0, ref_spad_map, 6);
-
- // -- VL53L0X_set_reference_spads() begin (assume NVM values are valid)
-
- writeReg(0xFF, 0x01);
- writeReg(DYNAMIC_SPAD_REF_EN_START_OFFSET, 0x00);
- writeReg(DYNAMIC_SPAD_NUM_REQUESTED_REF_SPAD, 0x2C);
- writeReg(0xFF, 0x00);
- writeReg(GLOBAL_CONFIG_REF_EN_START_SELECT, 0xB4);
-
- uint8_t first_spad_to_enable = spad_type_is_aperture ? 12 : 0; // 12 is the first aperture spad
- uint8_t spads_enabled = 0;
-
- for (uint8_t i = 0; i < 48; i++)
- {
- if (i < first_spad_to_enable || spads_enabled == spad_count)
- {
- // This bit is lower than the first one that should be enabled, or
- // (reference_spad_count) bits have already been enabled, so zero this bit
- ref_spad_map[i / 8] &= ~(1 << (i % 8));
- }
- else if ((ref_spad_map[i / 8] >> (i % 8)) & 0x1)
- {
- spads_enabled++;
- }
- }
-
- writeMulti(GLOBAL_CONFIG_SPAD_ENABLES_REF_0, ref_spad_map, 6);
-
- // -- VL53L0X_set_reference_spads() end
-
- // -- VL53L0X_load_tuning_settings() begin
- // DefaultTuningSettings from vl53l0x_tuning.h
-
- writeReg(0xFF, 0x01);
- writeReg(0x00, 0x00);
-
- writeReg(0xFF, 0x00);
- writeReg(0x09, 0x00);
- writeReg(0x10, 0x00);
- writeReg(0x11, 0x00);
-
- writeReg(0x24, 0x01);
- writeReg(0x25, 0xFF);
- writeReg(0x75, 0x00);
-
- writeReg(0xFF, 0x01);
- writeReg(0x4E, 0x2C);
- writeReg(0x48, 0x00);
- writeReg(0x30, 0x20);
-
- writeReg(0xFF, 0x00);
- writeReg(0x30, 0x09);
- writeReg(0x54, 0x00);
- writeReg(0x31, 0x04);
- writeReg(0x32, 0x03);
- writeReg(0x40, 0x83);
- writeReg(0x46, 0x25);
- writeReg(0x60, 0x00);
- writeReg(0x27, 0x00);
- writeReg(0x50, 0x06);
- writeReg(0x51, 0x00);
- writeReg(0x52, 0x96);
- writeReg(0x56, 0x08);
- writeReg(0x57, 0x30);
- writeReg(0x61, 0x00);
- writeReg(0x62, 0x00);
- writeReg(0x64, 0x00);
- writeReg(0x65, 0x00);
- writeReg(0x66, 0xA0);
-
- writeReg(0xFF, 0x01);
- writeReg(0x22, 0x32);
- writeReg(0x47, 0x14);
- writeReg(0x49, 0xFF);
- writeReg(0x4A, 0x00);
-
- writeReg(0xFF, 0x00);
- writeReg(0x7A, 0x0A);
- writeReg(0x7B, 0x00);
- writeReg(0x78, 0x21);
-
- writeReg(0xFF, 0x01);
- writeReg(0x23, 0x34);
- writeReg(0x42, 0x00);
- writeReg(0x44, 0xFF);
- writeReg(0x45, 0x26);
- writeReg(0x46, 0x05);
- writeReg(0x40, 0x40);
- writeReg(0x0E, 0x06);
- writeReg(0x20, 0x1A);
- writeReg(0x43, 0x40);
-
- writeReg(0xFF, 0x00);
- writeReg(0x34, 0x03);
- writeReg(0x35, 0x44);
-
- writeReg(0xFF, 0x01);
- writeReg(0x31, 0x04);
- writeReg(0x4B, 0x09);
- writeReg(0x4C, 0x05);
- writeReg(0x4D, 0x04);
-
- writeReg(0xFF, 0x00);
- writeReg(0x44, 0x00);
- writeReg(0x45, 0x20);
- writeReg(0x47, 0x08);
- writeReg(0x48, 0x28);
- writeReg(0x67, 0x00);
- writeReg(0x70, 0x04);
- writeReg(0x71, 0x01);
- writeReg(0x72, 0xFE);
- writeReg(0x76, 0x00);
- writeReg(0x77, 0x00);
-
- writeReg(0xFF, 0x01);
- writeReg(0x0D, 0x01);
-
- writeReg(0xFF, 0x00);
- writeReg(0x80, 0x01);
- writeReg(0x01, 0xF8);
-
- writeReg(0xFF, 0x01);
- writeReg(0x8E, 0x01);
- writeReg(0x00, 0x01);
- writeReg(0xFF, 0x00);
- writeReg(0x80, 0x00);
-
- // -- VL53L0X_load_tuning_settings() end
-
- // "Set interrupt config to new sample ready"
- // -- VL53L0X_SetGpioConfig() begin
-
- writeReg(SYSTEM_INTERRUPT_CONFIG_GPIO, 0x04);
- writeReg(GPIO_HV_MUX_ACTIVE_HIGH, readReg(GPIO_HV_MUX_ACTIVE_HIGH) & ~0x10); // active low
- writeReg(SYSTEM_INTERRUPT_CLEAR, 0x01);
-
- // -- VL53L0X_SetGpioConfig() end
-
- measurement_timing_budget_us = getMeasurementTimingBudget();
-
- // "Disable MSRC and TCC by default"
- // MSRC = Minimum Signal Rate Check
- // TCC = Target CentreCheck
- // -- VL53L0X_SetSequenceStepEnable() begin
-
- writeReg(SYSTEM_SEQUENCE_CONFIG, 0xE8);
-
- // -- VL53L0X_SetSequenceStepEnable() end
-
- // "Recalculate timing budget"
- setMeasurementTimingBudget(measurement_timing_budget_us);
-
- // VL53L0X_StaticInit() end
-
- // VL53L0X_PerformRefCalibration() begin (VL53L0X_perform_ref_calibration())
-
- // -- VL53L0X_perform_vhv_calibration() begin
-
- writeReg(SYSTEM_SEQUENCE_CONFIG, 0x01);
- if (!performSingleRefCalibration(0x40)) { return false; }
-
- // -- VL53L0X_perform_vhv_calibration() end
-
- // -- VL53L0X_perform_phase_calibration() begin
-
- writeReg(SYSTEM_SEQUENCE_CONFIG, 0x02);
- if (!performSingleRefCalibration(0x00)) { return false; }
-
- // -- VL53L0X_perform_phase_calibration() end
-
- // "restore the previous Sequence Config"
- writeReg(SYSTEM_SEQUENCE_CONFIG, 0xE8);
-
- // VL53L0X_PerformRefCalibration() end
-
- return true;
-}
-
-// Write an 8-bit register
-void VL53L0X::writeReg(uint8_t reg, uint8_t value)
-{
- bus->beginTransmission(address);
- bus->write(reg);
- bus->write(value);
- last_status = bus->endTransmission();
-}
-
-// Write a 16-bit register
-void VL53L0X::writeReg16Bit(uint8_t reg, uint16_t value)
-{
- bus->beginTransmission(address);
- bus->write(reg);
- bus->write((value >> 8) & 0xFF); // value high byte
- bus->write( value & 0xFF); // value low byte
- last_status = bus->endTransmission();
-}
-
-// Write a 32-bit register
-void VL53L0X::writeReg32Bit(uint8_t reg, uint32_t value)
-{
- bus->beginTransmission(address);
- bus->write(reg);
- bus->write((value >> 24) & 0xFF); // value highest byte
- bus->write((value >> 16) & 0xFF);
- bus->write((value >> 8) & 0xFF);
- bus->write( value & 0xFF); // value lowest byte
- last_status = bus->endTransmission();
-}
-
-// Read an 8-bit register
-uint8_t VL53L0X::readReg(uint8_t reg)
-{
- uint8_t value;
-
- bus->beginTransmission(address);
- bus->write(reg);
- last_status = bus->endTransmission();
-
- bus->requestFrom(address, (uint8_t)1);
- value = bus->read();
-
- return value;
-}
-
-// Read a 16-bit register
-uint16_t VL53L0X::readReg16Bit(uint8_t reg)
-{
- uint16_t value;
-
- bus->beginTransmission(address);
- bus->write(reg);
- last_status = bus->endTransmission();
-
- bus->requestFrom(address, (uint8_t)2);
- value = (uint16_t)bus->read() << 8; // value high byte
- value |= bus->read(); // value low byte
-
- return value;
-}
-
-// Read a 32-bit register
-uint32_t VL53L0X::readReg32Bit(uint8_t reg)
-{
- uint32_t value;
-
- bus->beginTransmission(address);
- bus->write(reg);
- last_status = bus->endTransmission();
-
- bus->requestFrom(address, (uint8_t)4);
- value = (uint32_t)bus->read() << 24; // value highest byte
- value |= (uint32_t)bus->read() << 16;
- value |= (uint16_t)bus->read() << 8;
- value |= bus->read(); // value lowest byte
-
- return value;
-}
-
-// Write an arbitrary number of bytes from the given array to the sensor,
-// starting at the given register
-void VL53L0X::writeMulti(uint8_t reg, uint8_t const * src, uint8_t count)
-{
- bus->beginTransmission(address);
- bus->write(reg);
-
- while (count-- > 0)
- {
- bus->write(*(src++));
- }
-
- last_status = bus->endTransmission();
-}
-
-// Read an arbitrary number of bytes from the sensor, starting at the given
-// register, into the given array
-void VL53L0X::readMulti(uint8_t reg, uint8_t * dst, uint8_t count)
-{
- bus->beginTransmission(address);
- bus->write(reg);
- last_status = bus->endTransmission();
-
- bus->requestFrom(address, count);
-
- while (count-- > 0)
- {
- *(dst++) = bus->read();
- }
-}
-
-// Set the return signal rate limit check value in units of MCPS (mega counts
-// per second). "This represents the amplitude of the signal reflected from the
-// target and detected by the device"; setting this limit presumably determines
-// the minimum measurement necessary for the sensor to report a valid reading.
-// Setting a lower limit increases the potential range of the sensor but also
-// seems to increase the likelihood of getting an inaccurate reading because of
-// unwanted reflections from objects other than the intended target.
-// Defaults to 0.25 MCPS as initialized by the ST API and this library.
-bool VL53L0X::setSignalRateLimit(float limit_Mcps)
-{
- if (limit_Mcps < 0 || limit_Mcps > 511.99f) { return false; }
-
- // Q9.7 fixed point format (9 integer bits, 7 fractional bits)
- writeReg16Bit(FINAL_RANGE_CONFIG_MIN_COUNT_RATE_RTN_LIMIT, limit_Mcps * (1 << 7));
- return true;
-}
-
-// Get the return signal rate limit check value in MCPS
-float VL53L0X::getSignalRateLimit()
-{
- return (float)readReg16Bit(FINAL_RANGE_CONFIG_MIN_COUNT_RATE_RTN_LIMIT) / (1 << 7);
-}
-
-// Set the measurement timing budget in microseconds, which is the time allowed
-// for one measurement; the ST API and this library take care of splitting the
-// timing budget among the sub-steps in the ranging sequence. A longer timing
-// budget allows for more accurate measurements. Increasing the budget by a
-// factor of N decreases the range measurement standard deviation by a factor of
-// sqrt(N). Defaults to about 33 milliseconds; the minimum is 20 ms.
-// based on VL53L0X_set_measurement_timing_budget_micro_seconds()
-bool VL53L0X::setMeasurementTimingBudget(uint32_t budget_us)
-{
- SequenceStepEnables enables;
- SequenceStepTimeouts timeouts;
-
- uint16_t const StartOverhead = 1910;
- uint16_t const EndOverhead = 960;
- uint16_t const MsrcOverhead = 660;
- uint16_t const TccOverhead = 590;
- uint16_t const DssOverhead = 690;
- uint16_t const PreRangeOverhead = 660;
- uint16_t const FinalRangeOverhead = 550;
-
- uint32_t const MinTimingBudget = 20000;
-
- if (budget_us < MinTimingBudget) { return false; }
-
- uint32_t used_budget_us = StartOverhead + EndOverhead;
-
- getSequenceStepEnables(&enables);
- getSequenceStepTimeouts(&enables, &timeouts);
-
- if (enables.tcc)
- {
- used_budget_us += (timeouts.msrc_dss_tcc_us + TccOverhead);
- }
-
- if (enables.dss)
- {
- used_budget_us += 2 * (timeouts.msrc_dss_tcc_us + DssOverhead);
- }
- else if (enables.msrc)
- {
- used_budget_us += (timeouts.msrc_dss_tcc_us + MsrcOverhead);
- }
-
- if (enables.pre_range)
- {
- used_budget_us += (timeouts.pre_range_us + PreRangeOverhead);
- }
-
- if (enables.final_range)
- {
- used_budget_us += FinalRangeOverhead;
-
- // "Note that the final range timeout is determined by the timing
- // budget and the sum of all other timeouts within the sequence.
- // If there is no room for the final range timeout, then an error
- // will be set. Otherwise the remaining time will be applied to
- // the final range."
-
- if (used_budget_us > budget_us)
- {
- // "Requested timeout too big."
- return false;
- }
-
- uint32_t final_range_timeout_us = budget_us - used_budget_us;
-
- // set_sequence_step_timeout() begin
- // (SequenceStepId == VL53L0X_SEQUENCESTEP_FINAL_RANGE)
-
- // "For the final range timeout, the pre-range timeout
- // must be added. To do this both final and pre-range
- // timeouts must be expressed in macro periods MClks
- // because they have different vcsel periods."
-
- uint32_t final_range_timeout_mclks =
- timeoutMicrosecondsToMclks(final_range_timeout_us,
- timeouts.final_range_vcsel_period_pclks);
-
- if (enables.pre_range)
- {
- final_range_timeout_mclks += timeouts.pre_range_mclks;
- }
-
- writeReg16Bit(FINAL_RANGE_CONFIG_TIMEOUT_MACROP_HI,
- encodeTimeout(final_range_timeout_mclks));
-
- // set_sequence_step_timeout() end
-
- measurement_timing_budget_us = budget_us; // store for internal reuse
- }
- return true;
-}
-
-// Get the measurement timing budget in microseconds
-// based on VL53L0X_get_measurement_timing_budget_micro_seconds()
-// in us
-uint32_t VL53L0X::getMeasurementTimingBudget()
-{
- SequenceStepEnables enables;
- SequenceStepTimeouts timeouts;
-
- uint16_t const StartOverhead = 1910;
- uint16_t const EndOverhead = 960;
- uint16_t const MsrcOverhead = 660;
- uint16_t const TccOverhead = 590;
- uint16_t const DssOverhead = 690;
- uint16_t const PreRangeOverhead = 660;
- uint16_t const FinalRangeOverhead = 550;
-
- // "Start and end overhead times always present"
- uint32_t budget_us = StartOverhead + EndOverhead;
-
- getSequenceStepEnables(&enables);
- getSequenceStepTimeouts(&enables, &timeouts);
-
- if (enables.tcc)
- {
- budget_us += (timeouts.msrc_dss_tcc_us + TccOverhead);
- }
-
- if (enables.dss)
- {
- budget_us += 2 * (timeouts.msrc_dss_tcc_us + DssOverhead);
- }
- else if (enables.msrc)
- {
- budget_us += (timeouts.msrc_dss_tcc_us + MsrcOverhead);
- }
-
- if (enables.pre_range)
- {
- budget_us += (timeouts.pre_range_us + PreRangeOverhead);
- }
-
- if (enables.final_range)
- {
- budget_us += (timeouts.final_range_us + FinalRangeOverhead);
- }
-
- measurement_timing_budget_us = budget_us; // store for internal reuse
- return budget_us;
-}
-
-// Set the VCSEL (vertical cavity surface emitting laser) pulse period for the
-// given period type (pre-range or final range) to the given value in PCLKs.
-// Longer periods seem to increase the potential range of the sensor.
-// Valid values are (even numbers only):
-// pre: 12 to 18 (initialized default: 14)
-// final: 8 to 14 (initialized default: 10)
-// based on VL53L0X_set_vcsel_pulse_period()
-bool VL53L0X::setVcselPulsePeriod(vcselPeriodType type, uint8_t period_pclks)
-{
- uint8_t vcsel_period_reg = encodeVcselPeriod(period_pclks);
-
- SequenceStepEnables enables;
- SequenceStepTimeouts timeouts;
-
- getSequenceStepEnables(&enables);
- getSequenceStepTimeouts(&enables, &timeouts);
-
- // "Apply specific settings for the requested clock period"
- // "Re-calculate and apply timeouts, in macro periods"
-
- // "When the VCSEL period for the pre or final range is changed,
- // the corresponding timeout must be read from the device using
- // the current VCSEL period, then the new VCSEL period can be
- // applied. The timeout then must be written back to the device
- // using the new VCSEL period.
- //
- // For the MSRC timeout, the same applies - this timeout being
- // dependant on the pre-range vcsel period."
-
-
- if (type == VcselPeriodPreRange)
- {
- // "Set phase check limits"
- switch (period_pclks)
- {
- case 12:
- writeReg(PRE_RANGE_CONFIG_VALID_PHASE_HIGH, 0x18);
- break;
-
- case 14:
- writeReg(PRE_RANGE_CONFIG_VALID_PHASE_HIGH, 0x30);
- break;
-
- case 16:
- writeReg(PRE_RANGE_CONFIG_VALID_PHASE_HIGH, 0x40);
- break;
-
- case 18:
- writeReg(PRE_RANGE_CONFIG_VALID_PHASE_HIGH, 0x50);
- break;
-
- default:
- // invalid period
- return false;
- }
- writeReg(PRE_RANGE_CONFIG_VALID_PHASE_LOW, 0x08);
-
- // apply new VCSEL period
- writeReg(PRE_RANGE_CONFIG_VCSEL_PERIOD, vcsel_period_reg);
-
- // update timeouts
-
- // set_sequence_step_timeout() begin
- // (SequenceStepId == VL53L0X_SEQUENCESTEP_PRE_RANGE)
-
- uint16_t new_pre_range_timeout_mclks =
- timeoutMicrosecondsToMclks(timeouts.pre_range_us, period_pclks);
-
- writeReg16Bit(PRE_RANGE_CONFIG_TIMEOUT_MACROP_HI,
- encodeTimeout(new_pre_range_timeout_mclks));
-
- // set_sequence_step_timeout() end
-
- // set_sequence_step_timeout() begin
- // (SequenceStepId == VL53L0X_SEQUENCESTEP_MSRC)
-
- uint16_t new_msrc_timeout_mclks =
- timeoutMicrosecondsToMclks(timeouts.msrc_dss_tcc_us, period_pclks);
-
- writeReg(MSRC_CONFIG_TIMEOUT_MACROP,
- (new_msrc_timeout_mclks > 256) ? 255 : (new_msrc_timeout_mclks - 1));
-
- // set_sequence_step_timeout() end
- }
- else if (type == VcselPeriodFinalRange)
- {
- switch (period_pclks)
- {
- case 8:
- writeReg(FINAL_RANGE_CONFIG_VALID_PHASE_HIGH, 0x10);
- writeReg(FINAL_RANGE_CONFIG_VALID_PHASE_LOW, 0x08);
- writeReg(GLOBAL_CONFIG_VCSEL_WIDTH, 0x02);
- writeReg(ALGO_PHASECAL_CONFIG_TIMEOUT, 0x0C);
- writeReg(0xFF, 0x01);
- writeReg(ALGO_PHASECAL_LIM, 0x30);
- writeReg(0xFF, 0x00);
- break;
-
- case 10:
- writeReg(FINAL_RANGE_CONFIG_VALID_PHASE_HIGH, 0x28);
- writeReg(FINAL_RANGE_CONFIG_VALID_PHASE_LOW, 0x08);
- writeReg(GLOBAL_CONFIG_VCSEL_WIDTH, 0x03);
- writeReg(ALGO_PHASECAL_CONFIG_TIMEOUT, 0x09);
- writeReg(0xFF, 0x01);
- writeReg(ALGO_PHASECAL_LIM, 0x20);
- writeReg(0xFF, 0x00);
- break;
-
- case 12:
- writeReg(FINAL_RANGE_CONFIG_VALID_PHASE_HIGH, 0x38);
- writeReg(FINAL_RANGE_CONFIG_VALID_PHASE_LOW, 0x08);
- writeReg(GLOBAL_CONFIG_VCSEL_WIDTH, 0x03);
- writeReg(ALGO_PHASECAL_CONFIG_TIMEOUT, 0x08);
- writeReg(0xFF, 0x01);
- writeReg(ALGO_PHASECAL_LIM, 0x20);
- writeReg(0xFF, 0x00);
- break;
-
- case 14:
- writeReg(FINAL_RANGE_CONFIG_VALID_PHASE_HIGH, 0x48);
- writeReg(FINAL_RANGE_CONFIG_VALID_PHASE_LOW, 0x08);
- writeReg(GLOBAL_CONFIG_VCSEL_WIDTH, 0x03);
- writeReg(ALGO_PHASECAL_CONFIG_TIMEOUT, 0x07);
- writeReg(0xFF, 0x01);
- writeReg(ALGO_PHASECAL_LIM, 0x20);
- writeReg(0xFF, 0x00);
- break;
-
- default:
- // invalid period
- return false;
- }
-
- // apply new VCSEL period
- writeReg(FINAL_RANGE_CONFIG_VCSEL_PERIOD, vcsel_period_reg);
-
- // update timeouts
-
- // set_sequence_step_timeout() begin
- // (SequenceStepId == VL53L0X_SEQUENCESTEP_FINAL_RANGE)
-
- // "For the final range timeout, the pre-range timeout
- // must be added. To do this both final and pre-range
- // timeouts must be expressed in macro periods MClks
- // because they have different vcsel periods."
-
- uint16_t new_final_range_timeout_mclks =
- timeoutMicrosecondsToMclks(timeouts.final_range_us, period_pclks);
-
- if (enables.pre_range)
- {
- new_final_range_timeout_mclks += timeouts.pre_range_mclks;
- }
-
- writeReg16Bit(FINAL_RANGE_CONFIG_TIMEOUT_MACROP_HI,
- encodeTimeout(new_final_range_timeout_mclks));
-
- // set_sequence_step_timeout end
- }
- else
- {
- // invalid type
- return false;
- }
-
- // "Finally, the timing budget must be re-applied"
-
- setMeasurementTimingBudget(measurement_timing_budget_us);
-
- // "Perform the phase calibration. This is needed after changing on vcsel period."
- // VL53L0X_perform_phase_calibration() begin
-
- uint8_t sequence_config = readReg(SYSTEM_SEQUENCE_CONFIG);
- writeReg(SYSTEM_SEQUENCE_CONFIG, 0x02);
- performSingleRefCalibration(0x0);
- writeReg(SYSTEM_SEQUENCE_CONFIG, sequence_config);
-
- // VL53L0X_perform_phase_calibration() end
-
- return true;
-}
-
-// Get the VCSEL pulse period in PCLKs for the given period type.
-// based on VL53L0X_get_vcsel_pulse_period()
-uint8_t VL53L0X::getVcselPulsePeriod(vcselPeriodType type)
-{
- if (type == VcselPeriodPreRange)
- {
- return decodeVcselPeriod(readReg(PRE_RANGE_CONFIG_VCSEL_PERIOD));
- }
- else if (type == VcselPeriodFinalRange)
- {
- return decodeVcselPeriod(readReg(FINAL_RANGE_CONFIG_VCSEL_PERIOD));
- }
- else { return 255; }
-}
-
-// Start continuous ranging measurements. If period_ms (optional) is 0 or not
-// given, continuous back-to-back mode is used (the sensor takes measurements as
-// often as possible); otherwise, continuous timed mode is used, with the given
-// inter-measurement period in milliseconds determining how often the sensor
-// takes a measurement.
-// based on VL53L0X_StartMeasurement()
-void VL53L0X::startContinuous(uint32_t period_ms)
-{
- writeReg(0x80, 0x01);
- writeReg(0xFF, 0x01);
- writeReg(0x00, 0x00);
- writeReg(0x91, stop_variable);
- writeReg(0x00, 0x01);
- writeReg(0xFF, 0x00);
- writeReg(0x80, 0x00);
-
- if (period_ms != 0)
- {
- // continuous timed mode
-
- // VL53L0X_SetInterMeasurementPeriodMilliSeconds() begin
-
- uint16_t osc_calibrate_val = readReg16Bit(OSC_CALIBRATE_VAL);
-
- if (osc_calibrate_val != 0)
- {
- period_ms *= osc_calibrate_val;
- }
-
- writeReg32Bit(SYSTEM_INTERMEASUREMENT_PERIOD, period_ms);
-
- // VL53L0X_SetInterMeasurementPeriodMilliSeconds() end
-
- writeReg(SYSRANGE_START, 0x04); // VL53L0X_REG_SYSRANGE_MODE_TIMED
- }
- else
- {
- // continuous back-to-back mode
- writeReg(SYSRANGE_START, 0x02); // VL53L0X_REG_SYSRANGE_MODE_BACKTOBACK
- }
-}
-
-// Stop continuous measurements
-// based on VL53L0X_StopMeasurement()
-void VL53L0X::stopContinuous()
-{
- writeReg(SYSRANGE_START, 0x01); // VL53L0X_REG_SYSRANGE_MODE_SINGLESHOT
-
- writeReg(0xFF, 0x01);
- writeReg(0x00, 0x00);
- writeReg(0x91, 0x00);
- writeReg(0x00, 0x01);
- writeReg(0xFF, 0x00);
-}
-
-// Returns a range reading in millimeters when continuous mode is active
-// (readRangeSingleMillimeters() also calls this function after starting a
-// single-shot range measurement)
-uint16_t VL53L0X::readRangeContinuousMillimeters()
-{
- startTimeout();
- while ((readReg(RESULT_INTERRUPT_STATUS) & 0x07) == 0)
- {
- if (checkTimeoutExpired())
- {
- did_timeout = true;
- return 65535;
- }
- }
-
- // assumptions: Linearity Corrective Gain is 1000 (default);
- // fractional ranging is not enabled
- uint16_t range = readReg16Bit(RESULT_RANGE_STATUS + 10);
-
- writeReg(SYSTEM_INTERRUPT_CLEAR, 0x01);
-
- return range;
-}
-
-// Performs a single-shot range measurement and returns the reading in
-// millimeters
-// based on VL53L0X_PerformSingleRangingMeasurement()
-uint16_t VL53L0X::readRangeSingleMillimeters()
-{
- writeReg(0x80, 0x01);
- writeReg(0xFF, 0x01);
- writeReg(0x00, 0x00);
- writeReg(0x91, stop_variable);
- writeReg(0x00, 0x01);
- writeReg(0xFF, 0x00);
- writeReg(0x80, 0x00);
-
- writeReg(SYSRANGE_START, 0x01);
-
- // "Wait until start bit has been cleared"
- startTimeout();
- while (readReg(SYSRANGE_START) & 0x01)
- {
- if (checkTimeoutExpired())
- {
- did_timeout = true;
- return 65535;
- }
- }
-
- return readRangeContinuousMillimeters();
-}
-
-// Did a timeout occur in one of the read functions since the last call to
-// timeoutOccurred()?
-bool VL53L0X::timeoutOccurred()
-{
- bool tmp = did_timeout;
- did_timeout = false;
- return tmp;
-}
-
-// Private Methods /////////////////////////////////////////////////////////////
-
-// Get reference SPAD (single photon avalanche diode) count and type
-// based on VL53L0X_get_info_from_device(),
-// but only gets reference SPAD count and type
-bool VL53L0X::getSpadInfo(uint8_t * count, bool * type_is_aperture)
-{
- uint8_t tmp;
-
- writeReg(0x80, 0x01);
- writeReg(0xFF, 0x01);
- writeReg(0x00, 0x00);
-
- writeReg(0xFF, 0x06);
- writeReg(0x83, readReg(0x83) | 0x04);
- writeReg(0xFF, 0x07);
- writeReg(0x81, 0x01);
-
- writeReg(0x80, 0x01);
-
- writeReg(0x94, 0x6b);
- writeReg(0x83, 0x00);
- startTimeout();
- while (readReg(0x83) == 0x00)
- {
- if (checkTimeoutExpired()) { return false; }
- }
- writeReg(0x83, 0x01);
- tmp = readReg(0x92);
-
- *count = tmp & 0x7f;
- *type_is_aperture = (tmp >> 7) & 0x01;
-
- writeReg(0x81, 0x00);
- writeReg(0xFF, 0x06);
- writeReg(0x83, readReg(0x83) & ~0x04);
- writeReg(0xFF, 0x01);
- writeReg(0x00, 0x01);
-
- writeReg(0xFF, 0x00);
- writeReg(0x80, 0x00);
-
- return true;
-}
-
-// Get sequence step enables
-// based on VL53L0X_GetSequenceStepEnables()
-void VL53L0X::getSequenceStepEnables(SequenceStepEnables * enables)
-{
- uint8_t sequence_config = readReg(SYSTEM_SEQUENCE_CONFIG);
-
- enables->tcc = (sequence_config >> 4) & 0x1;
- enables->dss = (sequence_config >> 3) & 0x1;
- enables->msrc = (sequence_config >> 2) & 0x1;
- enables->pre_range = (sequence_config >> 6) & 0x1;
- enables->final_range = (sequence_config >> 7) & 0x1;
-}
-
-// Get sequence step timeouts
-// based on get_sequence_step_timeout(),
-// but gets all timeouts instead of just the requested one, and also stores
-// intermediate values
-void VL53L0X::getSequenceStepTimeouts(SequenceStepEnables const * enables, SequenceStepTimeouts * timeouts)
-{
- timeouts->pre_range_vcsel_period_pclks = getVcselPulsePeriod(VcselPeriodPreRange);
-
- timeouts->msrc_dss_tcc_mclks = readReg(MSRC_CONFIG_TIMEOUT_MACROP) + 1;
- timeouts->msrc_dss_tcc_us =
- timeoutMclksToMicroseconds(timeouts->msrc_dss_tcc_mclks,
- timeouts->pre_range_vcsel_period_pclks);
-
- timeouts->pre_range_mclks =
- decodeTimeout(readReg16Bit(PRE_RANGE_CONFIG_TIMEOUT_MACROP_HI));
- timeouts->pre_range_us =
- timeoutMclksToMicroseconds(timeouts->pre_range_mclks,
- timeouts->pre_range_vcsel_period_pclks);
-
- timeouts->final_range_vcsel_period_pclks = getVcselPulsePeriod(VcselPeriodFinalRange);
-
- timeouts->final_range_mclks =
- decodeTimeout(readReg16Bit(FINAL_RANGE_CONFIG_TIMEOUT_MACROP_HI));
-
- if (enables->pre_range)
- {
- timeouts->final_range_mclks -= timeouts->pre_range_mclks;
- }
-
- timeouts->final_range_us =
- timeoutMclksToMicroseconds(timeouts->final_range_mclks,
- timeouts->final_range_vcsel_period_pclks);
-}
-
-// Decode sequence step timeout in MCLKs from register value
-// based on VL53L0X_decode_timeout()
-// Note: the original function returned a uint32_t, but the return value is
-// always stored in a uint16_t.
-uint16_t VL53L0X::decodeTimeout(uint16_t reg_val)
-{
- // format: "(LSByte * 2^MSByte) + 1"
- return (uint16_t)((reg_val & 0x00FF) <<
- (uint16_t)((reg_val & 0xFF00) >> 8)) + 1;
-}
-
-// Encode sequence step timeout register value from timeout in MCLKs
-// based on VL53L0X_encode_timeout()
-uint16_t VL53L0X::encodeTimeout(uint32_t timeout_mclks)
-{
- // format: "(LSByte * 2^MSByte) + 1"
-
- uint32_t ls_byte = 0;
- uint16_t ms_byte = 0;
-
- if (timeout_mclks > 0)
- {
- ls_byte = timeout_mclks - 1;
-
- while ((ls_byte & 0xFFFFFF00) > 0)
- {
- ls_byte >>= 1;
- ms_byte++;
- }
-
- return (ms_byte << 8) | (ls_byte & 0xFF);
- }
- else { return 0; }
-}
-
-// Convert sequence step timeout from MCLKs to microseconds with given VCSEL period in PCLKs
-// based on VL53L0X_calc_timeout_us()
-uint32_t VL53L0X::timeoutMclksToMicroseconds(uint16_t timeout_period_mclks, uint8_t vcsel_period_pclks)
-{
- uint32_t macro_period_ns = calcMacroPeriod(vcsel_period_pclks);
-
- return ((timeout_period_mclks * macro_period_ns) + 500) / 1000;
-}
-
-// Convert sequence step timeout from microseconds to MCLKs with given VCSEL period in PCLKs
-// based on VL53L0X_calc_timeout_mclks()
-uint32_t VL53L0X::timeoutMicrosecondsToMclks(uint32_t timeout_period_us, uint8_t vcsel_period_pclks)
-{
- uint32_t macro_period_ns = calcMacroPeriod(vcsel_period_pclks);
-
- return (((timeout_period_us * 1000) + (macro_period_ns / 2)) / macro_period_ns);
-}
-
-
-// based on VL53L0X_perform_single_ref_calibration()
-bool VL53L0X::performSingleRefCalibration(uint8_t vhv_init_byte)
-{
- writeReg(SYSRANGE_START, 0x01 | vhv_init_byte); // VL53L0X_REG_SYSRANGE_MODE_START_STOP
-
- startTimeout();
- while ((readReg(RESULT_INTERRUPT_STATUS) & 0x07) == 0)
- {
- if (checkTimeoutExpired()) { return false; }
- }
-
- writeReg(SYSTEM_INTERRUPT_CLEAR, 0x01);
-
- writeReg(SYSRANGE_START, 0x00);
-
- return true;
-}
-
-// Simply return the string, it is cleared and could be set during init
-String VL53L0X::getInitResult() {
- return initResult;
+// Most of the functionality of this library is based on the VL53L0X API
+// provided by ST (STSW-IMG005), and some of the explanatory comments are quoted
+// or paraphrased from the API source code, API user manual (UM2039), and the
+// VL53L0X datasheet.
+
+#include "VL53L0X.h"
+#include
+
+// Defines /////////////////////////////////////////////////////////////////////
+
+// The Arduino two-wire interface uses a 7-bit number for the address,
+// and sets the last bit correctly based on reads and writes
+#define ADDRESS_DEFAULT 0b0101001
+
+// Record the current time to check an upcoming timeout against
+#define startTimeout() (timeout_start_ms = millis())
+
+// Check if timeout is enabled (set to nonzero value) and has expired
+#define checkTimeoutExpired() (io_timeout > 0 && ((uint16_t)(millis() - timeout_start_ms) > io_timeout))
+
+// Decode VCSEL (vertical cavity surface emitting laser) pulse period in PCLKs
+// from register value
+// based on VL53L0X_decode_vcsel_period()
+#define decodeVcselPeriod(reg_val) (((reg_val) + 1) << 1)
+
+// Encode VCSEL pulse period register value from period in PCLKs
+// based on VL53L0X_encode_vcsel_period()
+#define encodeVcselPeriod(period_pclks) (((period_pclks) >> 1) - 1)
+
+// Calculate macro period in *nanoseconds* from VCSEL period in PCLKs
+// based on VL53L0X_calc_macro_period_ps()
+// PLL_period_ps = 1655; macro_period_vclks = 2304
+#define calcMacroPeriod(vcsel_period_pclks) ((((uint32_t)2304 * (vcsel_period_pclks) * 1655) + 500) / 1000)
+
+// Constructors ////////////////////////////////////////////////////////////////
+
+VL53L0X::VL53L0X()
+ : bus(&Wire)
+ , address(ADDRESS_DEFAULT)
+ , io_timeout(0) // no timeout
+ , did_timeout(false)
+{
+}
+
+// Public Methods //////////////////////////////////////////////////////////////
+
+void VL53L0X::setAddress(uint8_t new_addr)
+{
+ writeReg(I2C_SLAVE_DEVICE_ADDRESS, new_addr & 0x7F);
+ address = new_addr;
+}
+
+// Initialize sensor using sequence based on VL53L0X_DataInit(),
+// VL53L0X_StaticInit(), and VL53L0X_PerformRefCalibration().
+// This function does not perform reference SPAD calibration
+// (VL53L0X_PerformRefSpadManagement()), since the API user manual says that it
+// is performed by ST on the bare modules; it seems like that should work well
+// enough unless a cover glass is added.
+// If io_2v8 (optional) is true or not given, the sensor is configured for 2V8
+// mode.
+bool VL53L0X::init(bool io_2v8)
+{
+ initResult = F(""); // Clear any previous result
+ // check model ID register (value specified in datasheet)
+ uint8_t modelId = readReg(IDENTIFICATION_MODEL_ID);
+ if (modelId != 0xEE) { // Recognize VL53L0X (0xEE)
+ initResult = F("VL53L0X: Init: unrecognized Model-ID: 0x");
+ initResult += String(modelId, HEX);
+ return false;
+ }
+
+ // VL53L0X_DataInit() begin
+
+ // sensor uses 1V8 mode for I/O by default; switch to 2V8 mode if necessary
+ if (io_2v8)
+ {
+ writeReg(VHV_CONFIG_PAD_SCL_SDA__EXTSUP_HV,
+ readReg(VHV_CONFIG_PAD_SCL_SDA__EXTSUP_HV) | 0x01); // set bit 0
+ }
+
+ // "Set I2C standard mode"
+ writeReg(0x88, 0x00);
+
+ writeReg(0x80, 0x01);
+ writeReg(0xFF, 0x01);
+ writeReg(0x00, 0x00);
+ stop_variable = readReg(0x91);
+ writeReg(0x00, 0x01);
+ writeReg(0xFF, 0x00);
+ writeReg(0x80, 0x00);
+
+ // disable SIGNAL_RATE_MSRC (bit 1) and SIGNAL_RATE_PRE_RANGE (bit 4) limit checks
+ writeReg(MSRC_CONFIG_CONTROL, readReg(MSRC_CONFIG_CONTROL) | 0x12);
+
+ // set final range signal rate limit to 0.25 MCPS (million counts per second)
+ setSignalRateLimit(0.25);
+
+ writeReg(SYSTEM_SEQUENCE_CONFIG, 0xFF);
+
+ // VL53L0X_DataInit() end
+
+ // VL53L0X_StaticInit() begin
+
+ uint8_t spad_count;
+ bool spad_type_is_aperture;
+ if (!getSpadInfo(&spad_count, &spad_type_is_aperture)) { return false; }
+
+ // The SPAD map (RefGoodSpadMap) is read by VL53L0X_get_info_from_device() in
+ // the API, but the same data seems to be more easily readable from
+ // GLOBAL_CONFIG_SPAD_ENABLES_REF_0 through _6, so read it from there
+ uint8_t ref_spad_map[6];
+ readMulti(GLOBAL_CONFIG_SPAD_ENABLES_REF_0, ref_spad_map, 6);
+
+ // -- VL53L0X_set_reference_spads() begin (assume NVM values are valid)
+
+ writeReg(0xFF, 0x01);
+ writeReg(DYNAMIC_SPAD_REF_EN_START_OFFSET, 0x00);
+ writeReg(DYNAMIC_SPAD_NUM_REQUESTED_REF_SPAD, 0x2C);
+ writeReg(0xFF, 0x00);
+ writeReg(GLOBAL_CONFIG_REF_EN_START_SELECT, 0xB4);
+
+ uint8_t first_spad_to_enable = spad_type_is_aperture ? 12 : 0; // 12 is the first aperture spad
+ uint8_t spads_enabled = 0;
+
+ for (uint8_t i = 0; i < 48; i++)
+ {
+ if (i < first_spad_to_enable || spads_enabled == spad_count)
+ {
+ // This bit is lower than the first one that should be enabled, or
+ // (reference_spad_count) bits have already been enabled, so zero this bit
+ ref_spad_map[i / 8] &= ~(1 << (i % 8));
+ }
+ else if ((ref_spad_map[i / 8] >> (i % 8)) & 0x1)
+ {
+ spads_enabled++;
+ }
+ }
+
+ writeMulti(GLOBAL_CONFIG_SPAD_ENABLES_REF_0, ref_spad_map, 6);
+
+ // -- VL53L0X_set_reference_spads() end
+
+ // -- VL53L0X_load_tuning_settings() begin
+ // DefaultTuningSettings from vl53l0x_tuning.h
+
+ writeReg(0xFF, 0x01);
+ writeReg(0x00, 0x00);
+
+ writeReg(0xFF, 0x00);
+ writeReg(0x09, 0x00);
+ writeReg(0x10, 0x00);
+ writeReg(0x11, 0x00);
+
+ writeReg(0x24, 0x01);
+ writeReg(0x25, 0xFF);
+ writeReg(0x75, 0x00);
+
+ writeReg(0xFF, 0x01);
+ writeReg(0x4E, 0x2C);
+ writeReg(0x48, 0x00);
+ writeReg(0x30, 0x20);
+
+ writeReg(0xFF, 0x00);
+ writeReg(0x30, 0x09);
+ writeReg(0x54, 0x00);
+ writeReg(0x31, 0x04);
+ writeReg(0x32, 0x03);
+ writeReg(0x40, 0x83);
+ writeReg(0x46, 0x25);
+ writeReg(0x60, 0x00);
+ writeReg(0x27, 0x00);
+ writeReg(0x50, 0x06);
+ writeReg(0x51, 0x00);
+ writeReg(0x52, 0x96);
+ writeReg(0x56, 0x08);
+ writeReg(0x57, 0x30);
+ writeReg(0x61, 0x00);
+ writeReg(0x62, 0x00);
+ writeReg(0x64, 0x00);
+ writeReg(0x65, 0x00);
+ writeReg(0x66, 0xA0);
+
+ writeReg(0xFF, 0x01);
+ writeReg(0x22, 0x32);
+ writeReg(0x47, 0x14);
+ writeReg(0x49, 0xFF);
+ writeReg(0x4A, 0x00);
+
+ writeReg(0xFF, 0x00);
+ writeReg(0x7A, 0x0A);
+ writeReg(0x7B, 0x00);
+ writeReg(0x78, 0x21);
+
+ writeReg(0xFF, 0x01);
+ writeReg(0x23, 0x34);
+ writeReg(0x42, 0x00);
+ writeReg(0x44, 0xFF);
+ writeReg(0x45, 0x26);
+ writeReg(0x46, 0x05);
+ writeReg(0x40, 0x40);
+ writeReg(0x0E, 0x06);
+ writeReg(0x20, 0x1A);
+ writeReg(0x43, 0x40);
+
+ writeReg(0xFF, 0x00);
+ writeReg(0x34, 0x03);
+ writeReg(0x35, 0x44);
+
+ writeReg(0xFF, 0x01);
+ writeReg(0x31, 0x04);
+ writeReg(0x4B, 0x09);
+ writeReg(0x4C, 0x05);
+ writeReg(0x4D, 0x04);
+
+ writeReg(0xFF, 0x00);
+ writeReg(0x44, 0x00);
+ writeReg(0x45, 0x20);
+ writeReg(0x47, 0x08);
+ writeReg(0x48, 0x28);
+ writeReg(0x67, 0x00);
+ writeReg(0x70, 0x04);
+ writeReg(0x71, 0x01);
+ writeReg(0x72, 0xFE);
+ writeReg(0x76, 0x00);
+ writeReg(0x77, 0x00);
+
+ writeReg(0xFF, 0x01);
+ writeReg(0x0D, 0x01);
+
+ writeReg(0xFF, 0x00);
+ writeReg(0x80, 0x01);
+ writeReg(0x01, 0xF8);
+
+ writeReg(0xFF, 0x01);
+ writeReg(0x8E, 0x01);
+ writeReg(0x00, 0x01);
+ writeReg(0xFF, 0x00);
+ writeReg(0x80, 0x00);
+
+ // -- VL53L0X_load_tuning_settings() end
+
+ // "Set interrupt config to new sample ready"
+ // -- VL53L0X_SetGpioConfig() begin
+
+ writeReg(SYSTEM_INTERRUPT_CONFIG_GPIO, 0x04);
+ writeReg(GPIO_HV_MUX_ACTIVE_HIGH, readReg(GPIO_HV_MUX_ACTIVE_HIGH) & ~0x10); // active low
+ writeReg(SYSTEM_INTERRUPT_CLEAR, 0x01);
+
+ // -- VL53L0X_SetGpioConfig() end
+
+ measurement_timing_budget_us = getMeasurementTimingBudget();
+
+ // "Disable MSRC and TCC by default"
+ // MSRC = Minimum Signal Rate Check
+ // TCC = Target CentreCheck
+ // -- VL53L0X_SetSequenceStepEnable() begin
+
+ writeReg(SYSTEM_SEQUENCE_CONFIG, 0xE8);
+
+ // -- VL53L0X_SetSequenceStepEnable() end
+
+ // "Recalculate timing budget"
+ setMeasurementTimingBudget(measurement_timing_budget_us);
+
+ // VL53L0X_StaticInit() end
+
+ // VL53L0X_PerformRefCalibration() begin (VL53L0X_perform_ref_calibration())
+
+ // -- VL53L0X_perform_vhv_calibration() begin
+
+ writeReg(SYSTEM_SEQUENCE_CONFIG, 0x01);
+ if (!performSingleRefCalibration(0x40)) { return false; }
+
+ // -- VL53L0X_perform_vhv_calibration() end
+
+ // -- VL53L0X_perform_phase_calibration() begin
+
+ writeReg(SYSTEM_SEQUENCE_CONFIG, 0x02);
+ if (!performSingleRefCalibration(0x00)) { return false; }
+
+ // -- VL53L0X_perform_phase_calibration() end
+
+ // "restore the previous Sequence Config"
+ writeReg(SYSTEM_SEQUENCE_CONFIG, 0xE8);
+
+ // VL53L0X_PerformRefCalibration() end
+
+ return true;
+}
+
+// Write an 8-bit register
+void VL53L0X::writeReg(uint8_t reg, uint8_t value)
+{
+ bus->beginTransmission(address);
+ bus->write(reg);
+ bus->write(value);
+ last_status = bus->endTransmission();
+}
+
+// Write a 16-bit register
+void VL53L0X::writeReg16Bit(uint8_t reg, uint16_t value)
+{
+ bus->beginTransmission(address);
+ bus->write(reg);
+ bus->write((value >> 8) & 0xFF); // value high byte
+ bus->write( value & 0xFF); // value low byte
+ last_status = bus->endTransmission();
+}
+
+// Write a 32-bit register
+void VL53L0X::writeReg32Bit(uint8_t reg, uint32_t value)
+{
+ bus->beginTransmission(address);
+ bus->write(reg);
+ bus->write((value >> 24) & 0xFF); // value highest byte
+ bus->write((value >> 16) & 0xFF);
+ bus->write((value >> 8) & 0xFF);
+ bus->write( value & 0xFF); // value lowest byte
+ last_status = bus->endTransmission();
+}
+
+// Read an 8-bit register
+uint8_t VL53L0X::readReg(uint8_t reg)
+{
+ uint8_t value;
+
+ bus->beginTransmission(address);
+ bus->write(reg);
+ last_status = bus->endTransmission();
+
+ bus->requestFrom(address, (uint8_t)1);
+ value = bus->read();
+
+ return value;
+}
+
+// Read a 16-bit register
+uint16_t VL53L0X::readReg16Bit(uint8_t reg)
+{
+ uint16_t value;
+
+ bus->beginTransmission(address);
+ bus->write(reg);
+ last_status = bus->endTransmission();
+
+ bus->requestFrom(address, (uint8_t)2);
+ value = (uint16_t)bus->read() << 8; // value high byte
+ value |= bus->read(); // value low byte
+
+ return value;
+}
+
+// Read a 32-bit register
+uint32_t VL53L0X::readReg32Bit(uint8_t reg)
+{
+ uint32_t value;
+
+ bus->beginTransmission(address);
+ bus->write(reg);
+ last_status = bus->endTransmission();
+
+ bus->requestFrom(address, (uint8_t)4);
+ value = (uint32_t)bus->read() << 24; // value highest byte
+ value |= (uint32_t)bus->read() << 16;
+ value |= (uint16_t)bus->read() << 8;
+ value |= bus->read(); // value lowest byte
+
+ return value;
+}
+
+// Write an arbitrary number of bytes from the given array to the sensor,
+// starting at the given register
+void VL53L0X::writeMulti(uint8_t reg, uint8_t const * src, uint8_t count)
+{
+ bus->beginTransmission(address);
+ bus->write(reg);
+
+ while (count-- > 0)
+ {
+ bus->write(*(src++));
+ }
+
+ last_status = bus->endTransmission();
+}
+
+// Read an arbitrary number of bytes from the sensor, starting at the given
+// register, into the given array
+void VL53L0X::readMulti(uint8_t reg, uint8_t * dst, uint8_t count)
+{
+ bus->beginTransmission(address);
+ bus->write(reg);
+ last_status = bus->endTransmission();
+
+ bus->requestFrom(address, count);
+
+ while (count-- > 0)
+ {
+ *(dst++) = bus->read();
+ }
+}
+
+// Set the return signal rate limit check value in units of MCPS (mega counts
+// per second). "This represents the amplitude of the signal reflected from the
+// target and detected by the device"; setting this limit presumably determines
+// the minimum measurement necessary for the sensor to report a valid reading.
+// Setting a lower limit increases the potential range of the sensor but also
+// seems to increase the likelihood of getting an inaccurate reading because of
+// unwanted reflections from objects other than the intended target.
+// Defaults to 0.25 MCPS as initialized by the ST API and this library.
+bool VL53L0X::setSignalRateLimit(float limit_Mcps)
+{
+ if (limit_Mcps < 0 || limit_Mcps > 511.99f) { return false; }
+
+ // Q9.7 fixed point format (9 integer bits, 7 fractional bits)
+ writeReg16Bit(FINAL_RANGE_CONFIG_MIN_COUNT_RATE_RTN_LIMIT, limit_Mcps * (1 << 7));
+ return true;
+}
+
+// Get the return signal rate limit check value in MCPS
+float VL53L0X::getSignalRateLimit()
+{
+ return (float)readReg16Bit(FINAL_RANGE_CONFIG_MIN_COUNT_RATE_RTN_LIMIT) / (1 << 7);
+}
+
+// Set the measurement timing budget in microseconds, which is the time allowed
+// for one measurement; the ST API and this library take care of splitting the
+// timing budget among the sub-steps in the ranging sequence. A longer timing
+// budget allows for more accurate measurements. Increasing the budget by a
+// factor of N decreases the range measurement standard deviation by a factor of
+// sqrt(N). Defaults to about 33 milliseconds; the minimum is 20 ms.
+// based on VL53L0X_set_measurement_timing_budget_micro_seconds()
+bool VL53L0X::setMeasurementTimingBudget(uint32_t budget_us)
+{
+ uint32_t const MinTimingBudget = 20000;
+
+ if (budget_us < MinTimingBudget) { return false; }
+
+ // FIXME TD-er: Following is nearly the same as VL53L0X::getMeasurementTimingBudget()
+
+ SequenceStepEnables enables;
+ SequenceStepTimeouts timeouts;
+
+ uint16_t const StartOverhead = 1910;
+ uint16_t const EndOverhead = 960;
+ uint16_t const MsrcOverhead = 660;
+ uint16_t const TccOverhead = 590;
+ uint16_t const DssOverhead = 690;
+ uint16_t const PreRangeOverhead = 660;
+ uint16_t const FinalRangeOverhead = 550;
+
+ uint32_t used_budget_us = StartOverhead + EndOverhead;
+
+ getSequenceStepEnables(&enables);
+ getSequenceStepTimeouts(&enables, &timeouts);
+
+ if (enables.tcc)
+ {
+ used_budget_us += (timeouts.msrc_dss_tcc_us + TccOverhead);
+ }
+
+ if (enables.dss)
+ {
+ used_budget_us += 2 * (timeouts.msrc_dss_tcc_us + DssOverhead);
+ }
+ else if (enables.msrc)
+ {
+ used_budget_us += (timeouts.msrc_dss_tcc_us + MsrcOverhead);
+ }
+
+ if (enables.pre_range)
+ {
+ used_budget_us += (timeouts.pre_range_us + PreRangeOverhead);
+ }
+
+ if (enables.final_range)
+ {
+ used_budget_us += FinalRangeOverhead;
+
+ // "Note that the final range timeout is determined by the timing
+ // budget and the sum of all other timeouts within the sequence.
+ // If there is no room for the final range timeout, then an error
+ // will be set. Otherwise the remaining time will be applied to
+ // the final range."
+
+ if (used_budget_us > budget_us)
+ {
+ // "Requested timeout too big."
+ return false;
+ }
+
+ uint32_t final_range_timeout_us = budget_us - used_budget_us;
+
+ // set_sequence_step_timeout() begin
+ // (SequenceStepId == VL53L0X_SEQUENCESTEP_FINAL_RANGE)
+
+ // "For the final range timeout, the pre-range timeout
+ // must be added. To do this both final and pre-range
+ // timeouts must be expressed in macro periods MClks
+ // because they have different vcsel periods."
+
+ uint32_t final_range_timeout_mclks =
+ timeoutMicrosecondsToMclks(final_range_timeout_us,
+ timeouts.final_range_vcsel_period_pclks);
+
+ if (enables.pre_range)
+ {
+ final_range_timeout_mclks += timeouts.pre_range_mclks;
+ }
+
+ writeReg16Bit(FINAL_RANGE_CONFIG_TIMEOUT_MACROP_HI,
+ encodeTimeout(final_range_timeout_mclks));
+
+ // set_sequence_step_timeout() end
+
+ measurement_timing_budget_us = budget_us; // store for internal reuse
+ }
+ return true;
+}
+
+// Get the measurement timing budget in microseconds
+// based on VL53L0X_get_measurement_timing_budget_micro_seconds()
+// in us
+uint32_t VL53L0X::getMeasurementTimingBudget()
+{
+ SequenceStepEnables enables;
+ SequenceStepTimeouts timeouts;
+
+ uint16_t const StartOverhead = 1910;
+ uint16_t const EndOverhead = 960;
+ uint16_t const MsrcOverhead = 660;
+ uint16_t const TccOverhead = 590;
+ uint16_t const DssOverhead = 690;
+ uint16_t const PreRangeOverhead = 660;
+ uint16_t const FinalRangeOverhead = 550;
+
+ // "Start and end overhead times always present"
+ uint32_t budget_us = StartOverhead + EndOverhead;
+
+ getSequenceStepEnables(&enables);
+ getSequenceStepTimeouts(&enables, &timeouts);
+
+ if (enables.tcc)
+ {
+ budget_us += (timeouts.msrc_dss_tcc_us + TccOverhead);
+ }
+
+ if (enables.dss)
+ {
+ budget_us += 2 * (timeouts.msrc_dss_tcc_us + DssOverhead);
+ }
+ else if (enables.msrc)
+ {
+ budget_us += (timeouts.msrc_dss_tcc_us + MsrcOverhead);
+ }
+
+ if (enables.pre_range)
+ {
+ budget_us += (timeouts.pre_range_us + PreRangeOverhead);
+ }
+
+ if (enables.final_range)
+ {
+ budget_us += (timeouts.final_range_us + FinalRangeOverhead);
+ }
+
+ measurement_timing_budget_us = budget_us; // store for internal reuse
+ return budget_us;
+}
+
+// Set the VCSEL (vertical cavity surface emitting laser) pulse period for the
+// given period type (pre-range or final range) to the given value in PCLKs.
+// Longer periods seem to increase the potential range of the sensor.
+// Valid values are (even numbers only):
+// pre: 12 to 18 (initialized default: 14)
+// final: 8 to 14 (initialized default: 10)
+// based on VL53L0X_set_vcsel_pulse_period()
+bool VL53L0X::setVcselPulsePeriod(vcselPeriodType type, uint8_t period_pclks)
+{
+ uint8_t vcsel_period_reg = encodeVcselPeriod(period_pclks);
+
+ SequenceStepEnables enables;
+ SequenceStepTimeouts timeouts;
+
+ getSequenceStepEnables(&enables);
+ getSequenceStepTimeouts(&enables, &timeouts);
+
+ // "Apply specific settings for the requested clock period"
+ // "Re-calculate and apply timeouts, in macro periods"
+
+ // "When the VCSEL period for the pre or final range is changed,
+ // the corresponding timeout must be read from the device using
+ // the current VCSEL period, then the new VCSEL period can be
+ // applied. The timeout then must be written back to the device
+ // using the new VCSEL period.
+ //
+ // For the MSRC timeout, the same applies - this timeout being
+ // dependant on the pre-range vcsel period."
+
+
+ if (type == VcselPeriodPreRange)
+ {
+ // "Set phase check limits"
+ switch (period_pclks)
+ {
+ case 12:
+ writeReg(PRE_RANGE_CONFIG_VALID_PHASE_HIGH, 0x18);
+ break;
+
+ case 14:
+ writeReg(PRE_RANGE_CONFIG_VALID_PHASE_HIGH, 0x30);
+ break;
+
+ case 16:
+ writeReg(PRE_RANGE_CONFIG_VALID_PHASE_HIGH, 0x40);
+ break;
+
+ case 18:
+ writeReg(PRE_RANGE_CONFIG_VALID_PHASE_HIGH, 0x50);
+ break;
+
+ default:
+ // invalid period
+ return false;
+ }
+ writeReg(PRE_RANGE_CONFIG_VALID_PHASE_LOW, 0x08);
+
+ // apply new VCSEL period
+ writeReg(PRE_RANGE_CONFIG_VCSEL_PERIOD, vcsel_period_reg);
+
+ // update timeouts
+
+ // set_sequence_step_timeout() begin
+ // (SequenceStepId == VL53L0X_SEQUENCESTEP_PRE_RANGE)
+
+ uint16_t new_pre_range_timeout_mclks =
+ timeoutMicrosecondsToMclks(timeouts.pre_range_us, period_pclks);
+
+ writeReg16Bit(PRE_RANGE_CONFIG_TIMEOUT_MACROP_HI,
+ encodeTimeout(new_pre_range_timeout_mclks));
+
+ // set_sequence_step_timeout() end
+
+ // set_sequence_step_timeout() begin
+ // (SequenceStepId == VL53L0X_SEQUENCESTEP_MSRC)
+
+ uint16_t new_msrc_timeout_mclks =
+ timeoutMicrosecondsToMclks(timeouts.msrc_dss_tcc_us, period_pclks);
+
+ writeReg(MSRC_CONFIG_TIMEOUT_MACROP,
+ (new_msrc_timeout_mclks > 256) ? 255 : (new_msrc_timeout_mclks - 1));
+
+ // set_sequence_step_timeout() end
+ }
+ else if (type == VcselPeriodFinalRange)
+ {
+ switch (period_pclks)
+ {
+ case 8:
+ writeReg(FINAL_RANGE_CONFIG_VALID_PHASE_HIGH, 0x10);
+ writeReg(FINAL_RANGE_CONFIG_VALID_PHASE_LOW, 0x08);
+ writeReg(GLOBAL_CONFIG_VCSEL_WIDTH, 0x02);
+ writeReg(ALGO_PHASECAL_CONFIG_TIMEOUT, 0x0C);
+ writeReg(0xFF, 0x01);
+ writeReg(ALGO_PHASECAL_LIM, 0x30);
+ writeReg(0xFF, 0x00);
+ break;
+
+ case 10:
+ writeReg(FINAL_RANGE_CONFIG_VALID_PHASE_HIGH, 0x28);
+ writeReg(FINAL_RANGE_CONFIG_VALID_PHASE_LOW, 0x08);
+ writeReg(GLOBAL_CONFIG_VCSEL_WIDTH, 0x03);
+ writeReg(ALGO_PHASECAL_CONFIG_TIMEOUT, 0x09);
+ writeReg(0xFF, 0x01);
+ writeReg(ALGO_PHASECAL_LIM, 0x20);
+ writeReg(0xFF, 0x00);
+ break;
+
+ case 12:
+ writeReg(FINAL_RANGE_CONFIG_VALID_PHASE_HIGH, 0x38);
+ writeReg(FINAL_RANGE_CONFIG_VALID_PHASE_LOW, 0x08);
+ writeReg(GLOBAL_CONFIG_VCSEL_WIDTH, 0x03);
+ writeReg(ALGO_PHASECAL_CONFIG_TIMEOUT, 0x08);
+ writeReg(0xFF, 0x01);
+ writeReg(ALGO_PHASECAL_LIM, 0x20);
+ writeReg(0xFF, 0x00);
+ break;
+
+ case 14:
+ writeReg(FINAL_RANGE_CONFIG_VALID_PHASE_HIGH, 0x48);
+ writeReg(FINAL_RANGE_CONFIG_VALID_PHASE_LOW, 0x08);
+ writeReg(GLOBAL_CONFIG_VCSEL_WIDTH, 0x03);
+ writeReg(ALGO_PHASECAL_CONFIG_TIMEOUT, 0x07);
+ writeReg(0xFF, 0x01);
+ writeReg(ALGO_PHASECAL_LIM, 0x20);
+ writeReg(0xFF, 0x00);
+ break;
+
+ default:
+ // invalid period
+ return false;
+ }
+
+ // apply new VCSEL period
+ writeReg(FINAL_RANGE_CONFIG_VCSEL_PERIOD, vcsel_period_reg);
+
+ // update timeouts
+
+ // set_sequence_step_timeout() begin
+ // (SequenceStepId == VL53L0X_SEQUENCESTEP_FINAL_RANGE)
+
+ // "For the final range timeout, the pre-range timeout
+ // must be added. To do this both final and pre-range
+ // timeouts must be expressed in macro periods MClks
+ // because they have different vcsel periods."
+
+ uint16_t new_final_range_timeout_mclks =
+ timeoutMicrosecondsToMclks(timeouts.final_range_us, period_pclks);
+
+ if (enables.pre_range)
+ {
+ new_final_range_timeout_mclks += timeouts.pre_range_mclks;
+ }
+
+ writeReg16Bit(FINAL_RANGE_CONFIG_TIMEOUT_MACROP_HI,
+ encodeTimeout(new_final_range_timeout_mclks));
+
+ // set_sequence_step_timeout end
+ }
+ else
+ {
+ // invalid type
+ return false;
+ }
+
+ // "Finally, the timing budget must be re-applied"
+
+ setMeasurementTimingBudget(measurement_timing_budget_us);
+
+ // "Perform the phase calibration. This is needed after changing on vcsel period."
+ // VL53L0X_perform_phase_calibration() begin
+
+ uint8_t sequence_config = readReg(SYSTEM_SEQUENCE_CONFIG);
+ writeReg(SYSTEM_SEQUENCE_CONFIG, 0x02);
+ performSingleRefCalibration(0x0);
+ writeReg(SYSTEM_SEQUENCE_CONFIG, sequence_config);
+
+ // VL53L0X_perform_phase_calibration() end
+
+ return true;
+}
+
+// Get the VCSEL pulse period in PCLKs for the given period type.
+// based on VL53L0X_get_vcsel_pulse_period()
+uint8_t VL53L0X::getVcselPulsePeriod(vcselPeriodType type)
+{
+ if (type == VcselPeriodPreRange)
+ {
+ return decodeVcselPeriod(readReg(PRE_RANGE_CONFIG_VCSEL_PERIOD));
+ }
+ else if (type == VcselPeriodFinalRange)
+ {
+ return decodeVcselPeriod(readReg(FINAL_RANGE_CONFIG_VCSEL_PERIOD));
+ }
+ else { return 255; }
+}
+
+// Start continuous ranging measurements. If period_ms (optional) is 0 or not
+// given, continuous back-to-back mode is used (the sensor takes measurements as
+// often as possible); otherwise, continuous timed mode is used, with the given
+// inter-measurement period in milliseconds determining how often the sensor
+// takes a measurement.
+// based on VL53L0X_StartMeasurement()
+void VL53L0X::startContinuous(uint32_t period_ms)
+{
+ writeReg(0x80, 0x01);
+ writeReg(0xFF, 0x01);
+ writeReg(0x00, 0x00);
+ writeReg(0x91, stop_variable);
+ writeReg(0x00, 0x01);
+ writeReg(0xFF, 0x00);
+ writeReg(0x80, 0x00);
+
+ if (period_ms != 0)
+ {
+ // continuous timed mode
+
+ // VL53L0X_SetInterMeasurementPeriodMilliSeconds() begin
+
+ uint16_t osc_calibrate_val = readReg16Bit(OSC_CALIBRATE_VAL);
+
+ if (osc_calibrate_val != 0)
+ {
+ period_ms *= osc_calibrate_val;
+ }
+
+ writeReg32Bit(SYSTEM_INTERMEASUREMENT_PERIOD, period_ms);
+
+ // VL53L0X_SetInterMeasurementPeriodMilliSeconds() end
+
+ writeReg(SYSRANGE_START, 0x04); // VL53L0X_REG_SYSRANGE_MODE_TIMED
+ }
+ else
+ {
+ // continuous back-to-back mode
+ writeReg(SYSRANGE_START, 0x02); // VL53L0X_REG_SYSRANGE_MODE_BACKTOBACK
+ }
+ state = state_e::waitMeasurement;
+ start_timeout_ms = millis();
+}
+
+// Stop continuous measurements
+// based on VL53L0X_StopMeasurement()
+void VL53L0X::stopContinuous()
+{
+ writeReg(SYSRANGE_START, 0x01); // VL53L0X_REG_SYSRANGE_MODE_SINGLESHOT
+
+ writeReg(0xFF, 0x01);
+ writeReg(0x00, 0x00);
+ writeReg(0x91, 0x00);
+ writeReg(0x00, 0x01);
+ writeReg(0xFF, 0x00);
+ state = state_e::initialized;
+ start_timeout_ms = 0;
+}
+
+// Returns a range reading in millimeters when continuous mode is active
+// (readRangeSingleMillimeters() also calls this function after starting a
+// single-shot range measurement)
+uint16_t VL53L0X::readRangeContinuousMillimeters()
+{
+ int16_t distance;
+ start_timeout_ms = millis();
+ while (!loop(distance)) {
+ if (VL53L0X_NOT_WAITING == distance) {
+ return 65535u;
+ }
+ }
+ if (VL53L0X_TIMEOUT == distance)
+ {
+ return 65535u;
+ }
+ if (VL53L0X_WAITING == distance)
+ {
+ return 65534u;
+ }
+ return distance;
+}
+
+void VL53L0X::startSingleMeasurement()
+{
+ writeReg(0x80, 0x01);
+ writeReg(0xFF, 0x01);
+ writeReg(0x00, 0x00);
+ writeReg(0x91, stop_variable);
+ writeReg(0x00, 0x01);
+ writeReg(0xFF, 0x00);
+ writeReg(0x80, 0x00);
+
+ writeReg(SYSRANGE_START, 0x01);
+
+ state = state_e::waitStartBitCleared;
+ start_timeout_ms = millis();
+}
+
+bool VL53L0X::asyncReadRangeSingleMillimeters(int16_t& distance)
+{
+ const bool res = loop(distance);
+ if (res || (VL53L0X_WAITING != distance)) {
+ state = state_e::initialized;
+ start_timeout_ms = 0;
+ return true;
+ }
+ return false;
+}
+
+bool VL53L0X::asyncReadRangeContinuousMillimeters(int16_t& distance)
+{
+ return loop(distance);
+}
+
+// Performs a single-shot range measurement and returns the reading in
+// millimeters
+// based on VL53L0X_PerformSingleRangingMeasurement()
+uint16_t VL53L0X::readRangeSingleMillimeters()
+{
+ startSingleMeasurement();
+
+ // "Wait until start bit has been cleared"
+ int16_t distance;
+ while (!loop(distance)) {
+ delay(0);
+ }
+ state = state_e::initialized;
+ start_timeout_ms = 0;
+
+ uint16_t res = 65535u;
+ if (distance >= 0)
+ {
+ res = distance;
+ }
+ return res;
+}
+
+// Did a timeout occur in one of the read functions since the last call to
+// timeoutOccurred()?
+bool VL53L0X::timeoutOccurred()
+{
+ bool tmp = did_timeout;
+ did_timeout = false;
+ return tmp;
+}
+
+// Private Methods /////////////////////////////////////////////////////////////
+
+// Get reference SPAD (single photon avalanche diode) count and type
+// based on VL53L0X_get_info_from_device(),
+// but only gets reference SPAD count and type
+bool VL53L0X::getSpadInfo(uint8_t * count, bool * type_is_aperture)
+{
+ uint8_t tmp;
+
+ writeReg(0x80, 0x01);
+ writeReg(0xFF, 0x01);
+ writeReg(0x00, 0x00);
+
+ writeReg(0xFF, 0x06);
+ writeReg(0x83, readReg(0x83) | 0x04);
+ writeReg(0xFF, 0x07);
+ writeReg(0x81, 0x01);
+
+ writeReg(0x80, 0x01);
+
+ writeReg(0x94, 0x6b);
+ writeReg(0x83, 0x00);
+ startTimeout();
+ while (readReg(0x83) == 0x00)
+ {
+ if (checkTimeoutExpired()) { return false; }
+ }
+ writeReg(0x83, 0x01);
+ tmp = readReg(0x92);
+
+ *count = tmp & 0x7f;
+ *type_is_aperture = (tmp >> 7) & 0x01;
+
+ writeReg(0x81, 0x00);
+ writeReg(0xFF, 0x06);
+ writeReg(0x83, readReg(0x83) & ~0x04);
+ writeReg(0xFF, 0x01);
+ writeReg(0x00, 0x01);
+
+ writeReg(0xFF, 0x00);
+ writeReg(0x80, 0x00);
+
+ return true;
+}
+
+// Get sequence step enables
+// based on VL53L0X_GetSequenceStepEnables()
+void VL53L0X::getSequenceStepEnables(SequenceStepEnables * enables)
+{
+ uint8_t sequence_config = readReg(SYSTEM_SEQUENCE_CONFIG);
+
+ enables->tcc = (sequence_config >> 4) & 0x1;
+ enables->dss = (sequence_config >> 3) & 0x1;
+ enables->msrc = (sequence_config >> 2) & 0x1;
+ enables->pre_range = (sequence_config >> 6) & 0x1;
+ enables->final_range = (sequence_config >> 7) & 0x1;
+}
+
+// Get sequence step timeouts
+// based on get_sequence_step_timeout(),
+// but gets all timeouts instead of just the requested one, and also stores
+// intermediate values
+void VL53L0X::getSequenceStepTimeouts(SequenceStepEnables const * enables, SequenceStepTimeouts * timeouts)
+{
+ timeouts->pre_range_vcsel_period_pclks = getVcselPulsePeriod(VcselPeriodPreRange);
+
+ timeouts->msrc_dss_tcc_mclks = readReg(MSRC_CONFIG_TIMEOUT_MACROP) + 1;
+ timeouts->msrc_dss_tcc_us =
+ timeoutMclksToMicroseconds(timeouts->msrc_dss_tcc_mclks,
+ timeouts->pre_range_vcsel_period_pclks);
+
+ timeouts->pre_range_mclks =
+ decodeTimeout(readReg16Bit(PRE_RANGE_CONFIG_TIMEOUT_MACROP_HI));
+ timeouts->pre_range_us =
+ timeoutMclksToMicroseconds(timeouts->pre_range_mclks,
+ timeouts->pre_range_vcsel_period_pclks);
+
+ timeouts->final_range_vcsel_period_pclks = getVcselPulsePeriod(VcselPeriodFinalRange);
+
+ timeouts->final_range_mclks =
+ decodeTimeout(readReg16Bit(FINAL_RANGE_CONFIG_TIMEOUT_MACROP_HI));
+
+ if (enables->pre_range)
+ {
+ timeouts->final_range_mclks -= timeouts->pre_range_mclks;
+ }
+
+ timeouts->final_range_us =
+ timeoutMclksToMicroseconds(timeouts->final_range_mclks,
+ timeouts->final_range_vcsel_period_pclks);
+}
+
+// Decode sequence step timeout in MCLKs from register value
+// based on VL53L0X_decode_timeout()
+// Note: the original function returned a uint32_t, but the return value is
+// always stored in a uint16_t.
+uint16_t VL53L0X::decodeTimeout(uint16_t reg_val)
+{
+ // format: "(LSByte * 2^MSByte) + 1"
+ return (uint16_t)((reg_val & 0x00FF) <<
+ (uint16_t)((reg_val & 0xFF00) >> 8)) + 1;
+}
+
+// Encode sequence step timeout register value from timeout in MCLKs
+// based on VL53L0X_encode_timeout()
+uint16_t VL53L0X::encodeTimeout(uint32_t timeout_mclks)
+{
+ // format: "(LSByte * 2^MSByte) + 1"
+
+ uint32_t ls_byte = 0;
+ uint16_t ms_byte = 0;
+
+ if (timeout_mclks > 0)
+ {
+ ls_byte = timeout_mclks - 1;
+
+ while ((ls_byte & 0xFFFFFF00) > 0)
+ {
+ ls_byte >>= 1;
+ ms_byte++;
+ }
+
+ return (ms_byte << 8) | (ls_byte & 0xFF);
+ }
+ else { return 0; }
+}
+
+// Convert sequence step timeout from MCLKs to microseconds with given VCSEL period in PCLKs
+// based on VL53L0X_calc_timeout_us()
+uint32_t VL53L0X::timeoutMclksToMicroseconds(uint16_t timeout_period_mclks, uint8_t vcsel_period_pclks)
+{
+ uint32_t macro_period_ns = calcMacroPeriod(vcsel_period_pclks);
+
+ return ((timeout_period_mclks * macro_period_ns) + 500) / 1000;
+}
+
+// Convert sequence step timeout from microseconds to MCLKs with given VCSEL period in PCLKs
+// based on VL53L0X_calc_timeout_mclks()
+uint32_t VL53L0X::timeoutMicrosecondsToMclks(uint32_t timeout_period_us, uint8_t vcsel_period_pclks)
+{
+ uint32_t macro_period_ns = calcMacroPeriod(vcsel_period_pclks);
+
+ return (((timeout_period_us * 1000) + (macro_period_ns / 2)) / macro_period_ns);
+}
+
+
+// based on VL53L0X_perform_single_ref_calibration()
+bool VL53L0X::performSingleRefCalibration(uint8_t vhv_init_byte)
+{
+ writeReg(SYSRANGE_START, 0x01 | vhv_init_byte); // VL53L0X_REG_SYSRANGE_MODE_START_STOP
+
+ startTimeout();
+ while ((readReg(RESULT_INTERRUPT_STATUS) & 0x07) == 0)
+ {
+ if (checkTimeoutExpired()) { return false; }
+ }
+
+ writeReg(SYSTEM_INTERRUPT_CLEAR, 0x01);
+
+ writeReg(SYSRANGE_START, 0x00);
+
+ return true;
+}
+
+// Simply return the string, it is cleared and could be set during init
+String VL53L0X::getInitResult() {
+ return initResult;
+}
+
+
+bool VL53L0X::loop(int16_t& distance) {
+ distance = VL53L0X_NOT_WAITING;
+
+ const int32_t timePassedSince = (start_timeout_ms == 0) ? 0 : (int32_t)(millis() - start_timeout_ms);
+ switch (state) {
+ case state_e::uninitialized:
+ case state_e::initialized:
+ break;
+ case state_e::waitStartBitCleared:
+ {
+ distance = VL53L0X_WAITING;
+ if (timePassedSince > io_timeout) {
+ distance = VL53L0X_TIMEOUT;
+ did_timeout = true;
+ state = state_e::initialized;
+ start_timeout_ms = 0;
+ return true;
+ } else {
+ if (!(readReg(SYSRANGE_START) & 0x01)) {
+ // start bit has been cleared, wait for measurement to complete
+ state = state_e::waitMeasurement;
+ start_timeout_ms = millis();
+ }
+ }
+
+ return false;
+ }
+ case state_e::waitMeasurement:
+ {
+ if ((readReg(RESULT_INTERRUPT_STATUS) & 0x07) == 0) {
+ if (timePassedSince > io_timeout) {
+ distance = VL53L0X_TIMEOUT;
+ did_timeout = true;
+ start_timeout_ms = millis();
+ return true;
+ }
+ distance = VL53L0X_WAITING;
+ return false;
+ }
+ // assumptions: Linearity Corrective Gain is 1000 (default);
+ // fractional ranging is not enabled
+ distance = readReg16Bit(RESULT_RANGE_STATUS + 10);
+
+ writeReg(SYSTEM_INTERRUPT_CLEAR, 0x01);
+ did_timeout = false;
+ start_timeout_ms = millis();
+ return true;
+ }
+ }
+ return true;
}
\ No newline at end of file
diff --git a/lib/VL53L0X/src/VL53L0X.h b/lib/VL53L0X/src/VL53L0X.h
index 0b2884f343..4b2c4d3fa6 100644
--- a/lib/VL53L0X/src/VL53L0X.h
+++ b/lib/VL53L0X/src/VL53L0X.h
@@ -1,183 +1,220 @@
-#ifndef VL53L0X_h
-#define VL53L0X_h
-
-#include
-#include
-
-class VL53L0X
-{
- public:
- // register addresses from API vl53l0x_device.h (ordered as listed there)
- enum regAddr
- {
- SYSRANGE_START = 0x00,
-
- SYSTEM_THRESH_HIGH = 0x0C,
- SYSTEM_THRESH_LOW = 0x0E,
-
- SYSTEM_SEQUENCE_CONFIG = 0x01,
- SYSTEM_RANGE_CONFIG = 0x09,
- SYSTEM_INTERMEASUREMENT_PERIOD = 0x04,
-
- SYSTEM_INTERRUPT_CONFIG_GPIO = 0x0A,
-
- GPIO_HV_MUX_ACTIVE_HIGH = 0x84,
-
- SYSTEM_INTERRUPT_CLEAR = 0x0B,
-
- RESULT_INTERRUPT_STATUS = 0x13,
- RESULT_RANGE_STATUS = 0x14,
-
- RESULT_CORE_AMBIENT_WINDOW_EVENTS_RTN = 0xBC,
- RESULT_CORE_RANGING_TOTAL_EVENTS_RTN = 0xC0,
- RESULT_CORE_AMBIENT_WINDOW_EVENTS_REF = 0xD0,
- RESULT_CORE_RANGING_TOTAL_EVENTS_REF = 0xD4,
- RESULT_PEAK_SIGNAL_RATE_REF = 0xB6,
-
- ALGO_PART_TO_PART_RANGE_OFFSET_MM = 0x28,
-
- I2C_SLAVE_DEVICE_ADDRESS = 0x8A,
-
- MSRC_CONFIG_CONTROL = 0x60,
-
- PRE_RANGE_CONFIG_MIN_SNR = 0x27,
- PRE_RANGE_CONFIG_VALID_PHASE_LOW = 0x56,
- PRE_RANGE_CONFIG_VALID_PHASE_HIGH = 0x57,
- PRE_RANGE_MIN_COUNT_RATE_RTN_LIMIT = 0x64,
-
- FINAL_RANGE_CONFIG_MIN_SNR = 0x67,
- FINAL_RANGE_CONFIG_VALID_PHASE_LOW = 0x47,
- FINAL_RANGE_CONFIG_VALID_PHASE_HIGH = 0x48,
- FINAL_RANGE_CONFIG_MIN_COUNT_RATE_RTN_LIMIT = 0x44,
-
- PRE_RANGE_CONFIG_SIGMA_THRESH_HI = 0x61,
- PRE_RANGE_CONFIG_SIGMA_THRESH_LO = 0x62,
-
- PRE_RANGE_CONFIG_VCSEL_PERIOD = 0x50,
- PRE_RANGE_CONFIG_TIMEOUT_MACROP_HI = 0x51,
- PRE_RANGE_CONFIG_TIMEOUT_MACROP_LO = 0x52,
-
- SYSTEM_HISTOGRAM_BIN = 0x81,
- HISTOGRAM_CONFIG_INITIAL_PHASE_SELECT = 0x33,
- HISTOGRAM_CONFIG_READOUT_CTRL = 0x55,
-
- FINAL_RANGE_CONFIG_VCSEL_PERIOD = 0x70,
- FINAL_RANGE_CONFIG_TIMEOUT_MACROP_HI = 0x71,
- FINAL_RANGE_CONFIG_TIMEOUT_MACROP_LO = 0x72,
- CROSSTALK_COMPENSATION_PEAK_RATE_MCPS = 0x20,
-
- MSRC_CONFIG_TIMEOUT_MACROP = 0x46,
-
- SOFT_RESET_GO2_SOFT_RESET_N = 0xBF,
- IDENTIFICATION_MODEL_ID = 0xC0,
- IDENTIFICATION_REVISION_ID = 0xC2,
-
- OSC_CALIBRATE_VAL = 0xF8,
-
- GLOBAL_CONFIG_VCSEL_WIDTH = 0x32,
- GLOBAL_CONFIG_SPAD_ENABLES_REF_0 = 0xB0,
- GLOBAL_CONFIG_SPAD_ENABLES_REF_1 = 0xB1,
- GLOBAL_CONFIG_SPAD_ENABLES_REF_2 = 0xB2,
- GLOBAL_CONFIG_SPAD_ENABLES_REF_3 = 0xB3,
- GLOBAL_CONFIG_SPAD_ENABLES_REF_4 = 0xB4,
- GLOBAL_CONFIG_SPAD_ENABLES_REF_5 = 0xB5,
-
- GLOBAL_CONFIG_REF_EN_START_SELECT = 0xB6,
- DYNAMIC_SPAD_NUM_REQUESTED_REF_SPAD = 0x4E,
- DYNAMIC_SPAD_REF_EN_START_OFFSET = 0x4F,
- POWER_MANAGEMENT_GO1_POWER_FORCE = 0x80,
-
- VHV_CONFIG_PAD_SCL_SDA__EXTSUP_HV = 0x89,
-
- ALGO_PHASECAL_LIM = 0x30,
- ALGO_PHASECAL_CONFIG_TIMEOUT = 0x30,
- };
-
- enum vcselPeriodType { VcselPeriodPreRange, VcselPeriodFinalRange };
-
- uint8_t last_status; // status of last I2C transmission
-
- VL53L0X();
-
- void setBus(TwoWire * bus) { this->bus = bus; }
- TwoWire * getBus() { return bus; }
-
- void setAddress(uint8_t new_addr);
- inline uint8_t getAddress() { return address; }
-
- bool init(bool io_2v8 = true);
-
- void writeReg(uint8_t reg, uint8_t value);
- void writeReg16Bit(uint8_t reg, uint16_t value);
- void writeReg32Bit(uint8_t reg, uint32_t value);
- uint8_t readReg(uint8_t reg);
- uint16_t readReg16Bit(uint8_t reg);
- uint32_t readReg32Bit(uint8_t reg);
-
- void writeMulti(uint8_t reg, uint8_t const * src, uint8_t count);
- void readMulti(uint8_t reg, uint8_t * dst, uint8_t count);
-
- bool setSignalRateLimit(float limit_Mcps);
- float getSignalRateLimit();
-
- bool setMeasurementTimingBudget(uint32_t budget_us);
- uint32_t getMeasurementTimingBudget();
-
- bool setVcselPulsePeriod(vcselPeriodType type, uint8_t period_pclks);
- uint8_t getVcselPulsePeriod(vcselPeriodType type);
-
- void startContinuous(uint32_t period_ms = 0);
- void stopContinuous();
- uint16_t readRangeContinuousMillimeters();
- uint16_t readRangeSingleMillimeters();
-
- inline void setTimeout(uint16_t timeout) { io_timeout = timeout; }
- inline uint16_t getTimeout() { return io_timeout; }
- bool timeoutOccurred();
- String getInitResult();
-
- private:
- // TCC: Target CentreCheck
- // MSRC: Minimum Signal Rate Check
- // DSS: Dynamic Spad Selection
-
- struct SequenceStepEnables
- {
- boolean tcc, msrc, dss, pre_range, final_range;
- };
-
- struct SequenceStepTimeouts
- {
- uint16_t pre_range_vcsel_period_pclks, final_range_vcsel_period_pclks;
-
- uint16_t msrc_dss_tcc_mclks, pre_range_mclks, final_range_mclks;
- uint32_t msrc_dss_tcc_us, pre_range_us, final_range_us;
- };
-
- TwoWire * bus;
- uint8_t address;
- uint16_t io_timeout;
- bool did_timeout;
- uint16_t timeout_start_ms;
-
- uint8_t stop_variable; // read by init and used when starting measurement; is StopVariable field of VL53L0X_DevData_t structure in API
- uint32_t measurement_timing_budget_us;
-
- bool getSpadInfo(uint8_t * count, bool * type_is_aperture);
-
- void getSequenceStepEnables(SequenceStepEnables * enables);
- void getSequenceStepTimeouts(SequenceStepEnables const * enables, SequenceStepTimeouts * timeouts);
-
- bool performSingleRefCalibration(uint8_t vhv_init_byte);
-
- static uint16_t decodeTimeout(uint16_t value);
- static uint16_t encodeTimeout(uint32_t timeout_mclks);
- static uint32_t timeoutMclksToMicroseconds(uint16_t timeout_period_mclks, uint8_t vcsel_period_pclks);
- static uint32_t timeoutMicrosecondsToMclks(uint32_t timeout_period_us, uint8_t vcsel_period_pclks);
- String initResult;
-};
-
-#endif
-
-
-
+#ifndef VL53L0X_h
+#define VL53L0X_h
+
+#include
+#include
+
+class VL53L0X
+{
+ public:
+ // register addresses from API vl53l0x_device.h (ordered as listed there)
+ enum regAddr
+ {
+ SYSRANGE_START = 0x00,
+
+ SYSTEM_THRESH_HIGH = 0x0C,
+ SYSTEM_THRESH_LOW = 0x0E,
+
+ SYSTEM_SEQUENCE_CONFIG = 0x01,
+ SYSTEM_RANGE_CONFIG = 0x09,
+ SYSTEM_INTERMEASUREMENT_PERIOD = 0x04,
+
+ SYSTEM_INTERRUPT_CONFIG_GPIO = 0x0A,
+
+ GPIO_HV_MUX_ACTIVE_HIGH = 0x84,
+
+ SYSTEM_INTERRUPT_CLEAR = 0x0B,
+
+ RESULT_INTERRUPT_STATUS = 0x13,
+ RESULT_RANGE_STATUS = 0x14,
+
+ RESULT_CORE_AMBIENT_WINDOW_EVENTS_RTN = 0xBC,
+ RESULT_CORE_RANGING_TOTAL_EVENTS_RTN = 0xC0,
+ RESULT_CORE_AMBIENT_WINDOW_EVENTS_REF = 0xD0,
+ RESULT_CORE_RANGING_TOTAL_EVENTS_REF = 0xD4,
+ RESULT_PEAK_SIGNAL_RATE_REF = 0xB6,
+
+ ALGO_PART_TO_PART_RANGE_OFFSET_MM = 0x28,
+
+ I2C_SLAVE_DEVICE_ADDRESS = 0x8A,
+
+ MSRC_CONFIG_CONTROL = 0x60,
+
+ PRE_RANGE_CONFIG_MIN_SNR = 0x27,
+ PRE_RANGE_CONFIG_VALID_PHASE_LOW = 0x56,
+ PRE_RANGE_CONFIG_VALID_PHASE_HIGH = 0x57,
+ PRE_RANGE_MIN_COUNT_RATE_RTN_LIMIT = 0x64,
+
+ FINAL_RANGE_CONFIG_MIN_SNR = 0x67,
+ FINAL_RANGE_CONFIG_VALID_PHASE_LOW = 0x47,
+ FINAL_RANGE_CONFIG_VALID_PHASE_HIGH = 0x48,
+ FINAL_RANGE_CONFIG_MIN_COUNT_RATE_RTN_LIMIT = 0x44,
+
+ PRE_RANGE_CONFIG_SIGMA_THRESH_HI = 0x61,
+ PRE_RANGE_CONFIG_SIGMA_THRESH_LO = 0x62,
+
+ PRE_RANGE_CONFIG_VCSEL_PERIOD = 0x50,
+ PRE_RANGE_CONFIG_TIMEOUT_MACROP_HI = 0x51,
+ PRE_RANGE_CONFIG_TIMEOUT_MACROP_LO = 0x52,
+
+ SYSTEM_HISTOGRAM_BIN = 0x81,
+ HISTOGRAM_CONFIG_INITIAL_PHASE_SELECT = 0x33,
+ HISTOGRAM_CONFIG_READOUT_CTRL = 0x55,
+
+ FINAL_RANGE_CONFIG_VCSEL_PERIOD = 0x70,
+ FINAL_RANGE_CONFIG_TIMEOUT_MACROP_HI = 0x71,
+ FINAL_RANGE_CONFIG_TIMEOUT_MACROP_LO = 0x72,
+ CROSSTALK_COMPENSATION_PEAK_RATE_MCPS = 0x20,
+
+ MSRC_CONFIG_TIMEOUT_MACROP = 0x46,
+
+ SOFT_RESET_GO2_SOFT_RESET_N = 0xBF,
+ IDENTIFICATION_MODEL_ID = 0xC0,
+ IDENTIFICATION_REVISION_ID = 0xC2,
+
+ OSC_CALIBRATE_VAL = 0xF8,
+
+ GLOBAL_CONFIG_VCSEL_WIDTH = 0x32,
+ GLOBAL_CONFIG_SPAD_ENABLES_REF_0 = 0xB0,
+ GLOBAL_CONFIG_SPAD_ENABLES_REF_1 = 0xB1,
+ GLOBAL_CONFIG_SPAD_ENABLES_REF_2 = 0xB2,
+ GLOBAL_CONFIG_SPAD_ENABLES_REF_3 = 0xB3,
+ GLOBAL_CONFIG_SPAD_ENABLES_REF_4 = 0xB4,
+ GLOBAL_CONFIG_SPAD_ENABLES_REF_5 = 0xB5,
+
+ GLOBAL_CONFIG_REF_EN_START_SELECT = 0xB6,
+ DYNAMIC_SPAD_NUM_REQUESTED_REF_SPAD = 0x4E,
+ DYNAMIC_SPAD_REF_EN_START_OFFSET = 0x4F,
+ POWER_MANAGEMENT_GO1_POWER_FORCE = 0x80,
+
+ VHV_CONFIG_PAD_SCL_SDA__EXTSUP_HV = 0x89,
+
+ ALGO_PHASECAL_LIM = 0x30,
+ ALGO_PHASECAL_CONFIG_TIMEOUT = 0x30,
+ };
+
+ enum vcselPeriodType { VcselPeriodPreRange, VcselPeriodFinalRange };
+
+ uint8_t last_status; // status of last I2C transmission
+
+ VL53L0X();
+
+ void setBus(TwoWire * bus) { this->bus = bus; }
+ TwoWire * getBus() { return bus; }
+
+ void setAddress(uint8_t new_addr);
+ inline uint8_t getAddress() { return address; }
+
+ bool init(bool io_2v8 = true);
+
+ void writeReg(uint8_t reg, uint8_t value);
+ void writeReg16Bit(uint8_t reg, uint16_t value);
+ void writeReg32Bit(uint8_t reg, uint32_t value);
+ uint8_t readReg(uint8_t reg);
+ uint16_t readReg16Bit(uint8_t reg);
+ uint32_t readReg32Bit(uint8_t reg);
+
+ void writeMulti(uint8_t reg, uint8_t const * src, uint8_t count);
+ void readMulti(uint8_t reg, uint8_t * dst, uint8_t count);
+
+ bool setSignalRateLimit(float limit_Mcps);
+ float getSignalRateLimit();
+
+ bool setMeasurementTimingBudget(uint32_t budget_us);
+ uint32_t getMeasurementTimingBudget();
+
+ bool setVcselPulsePeriod(vcselPeriodType type, uint8_t period_pclks);
+ uint8_t getVcselPulsePeriod(vcselPeriodType type);
+
+ void startContinuous(uint32_t period_ms = 0);
+ void stopContinuous();
+ uint16_t readRangeContinuousMillimeters();
+ uint16_t readRangeSingleMillimeters();
+
+ inline void setTimeout(uint16_t timeout) { io_timeout = timeout; }
+ inline uint16_t getTimeout() { return io_timeout; }
+ bool timeoutOccurred();
+ String getInitResult();
+
+ private:
+ // TCC: Target CentreCheck
+ // MSRC: Minimum Signal Rate Check
+ // DSS: Dynamic Spad Selection
+
+ struct SequenceStepEnables
+ {
+ boolean tcc, msrc, dss, pre_range, final_range;
+ };
+
+ struct SequenceStepTimeouts
+ {
+ uint16_t pre_range_vcsel_period_pclks, final_range_vcsel_period_pclks;
+
+ uint16_t msrc_dss_tcc_mclks, pre_range_mclks, final_range_mclks;
+ uint32_t msrc_dss_tcc_us, pre_range_us, final_range_us;
+ };
+
+ TwoWire * bus;
+ uint8_t address;
+ uint16_t io_timeout;
+ bool did_timeout;
+ uint16_t timeout_start_ms;
+
+ uint8_t stop_variable; // read by init and used when starting measurement; is StopVariable field of VL53L0X_DevData_t structure in API
+ uint32_t measurement_timing_budget_us;
+
+ bool getSpadInfo(uint8_t * count, bool * type_is_aperture);
+
+ void getSequenceStepEnables(SequenceStepEnables * enables);
+ void getSequenceStepTimeouts(SequenceStepEnables const * enables, SequenceStepTimeouts * timeouts);
+
+ bool performSingleRefCalibration(uint8_t vhv_init_byte);
+
+ static uint16_t decodeTimeout(uint16_t value);
+ static uint16_t encodeTimeout(uint32_t timeout_mclks);
+ static uint32_t timeoutMclksToMicroseconds(uint16_t timeout_period_mclks, uint8_t vcsel_period_pclks);
+ static uint32_t timeoutMicrosecondsToMclks(uint32_t timeout_period_us, uint8_t vcsel_period_pclks);
+ String initResult;
+
+public:
+
+ #define VL53L0X_NOT_WAITING -1
+ #define VL53L0X_WAITING -2
+ #define VL53L0X_TIMEOUT -3
+
+private:
+
+ // Returns true when measurement done.
+ // distance < 0 when there was an error, otherwise successful measurement
+ bool loop(int16_t& distance);
+
+public:
+
+ void startSingleMeasurement();
+
+ // Check if taking a sample has finished
+ // Return true when measurement has finished or aborted.
+ // Valid distance when distance >= 0
+ bool asyncReadRangeSingleMillimeters(int16_t& distance);
+
+ // Check if new sample is ready.
+ // Return true when measurement has finished or aborted.
+ // Valid distance when distance >= 0
+ bool asyncReadRangeContinuousMillimeters(int16_t& distance);
+
+private:
+
+ enum class state_e {
+ uninitialized,
+ initialized,
+ waitStartBitCleared,
+ waitMeasurement
+ };
+ state_e state = state_e::uninitialized;
+ uint32_t start_timeout_ms = 0;
+};
+
+#endif
+
+
+
diff --git a/lib/pubsubclient/src/PubSubClient.cpp b/lib/pubsubclient/src/PubSubClient.cpp
index 5976062b6c..30948c57f9 100644
--- a/lib/pubsubclient/src/PubSubClient.cpp
+++ b/lib/pubsubclient/src/PubSubClient.cpp
@@ -1,772 +1,841 @@
-/*
- PubSubClient.cpp - A simple client for MQTT.
- Nick O'Leary
- http://knolleary.net
-*/
-
-#include "PubSubClient.h"
-#include
-
-#ifdef ESP32
-#include
-#endif
-
-#ifdef USE_SECOND_HEAP
- #include
-#endif
-
-
-PubSubClient::PubSubClient() {
- this->_state = MQTT_DISCONNECTED;
- this->_client = NULL;
- this->stream = NULL;
- setCallback(NULL);
-}
-
-PubSubClient::PubSubClient(Client& client) {
- this->_state = MQTT_DISCONNECTED;
- setClient(client);
- this->stream = NULL;
-}
-
-PubSubClient::PubSubClient(IPAddress addr, uint16_t port, Client& client) {
- this->_state = MQTT_DISCONNECTED;
- setServer(addr, port);
- setClient(client);
- this->stream = NULL;
-}
-PubSubClient::PubSubClient(IPAddress addr, uint16_t port, Client& client, Stream& stream) {
- this->_state = MQTT_DISCONNECTED;
- setServer(addr,port);
- setClient(client);
- setStream(stream);
-}
-PubSubClient::PubSubClient(IPAddress addr, uint16_t port, MQTT_CALLBACK_SIGNATURE, Client& client) {
- this->_state = MQTT_DISCONNECTED;
- setServer(addr, port);
- setCallback(callback);
- setClient(client);
- this->stream = NULL;
-}
-PubSubClient::PubSubClient(IPAddress addr, uint16_t port, MQTT_CALLBACK_SIGNATURE, Client& client, Stream& stream) {
- this->_state = MQTT_DISCONNECTED;
- setServer(addr,port);
- setCallback(callback);
- setClient(client);
- setStream(stream);
-}
-
-PubSubClient::PubSubClient(uint8_t *ip, uint16_t port, Client& client) {
- this->_state = MQTT_DISCONNECTED;
- setServer(ip, port);
- setClient(client);
- this->stream = NULL;
-}
-PubSubClient::PubSubClient(uint8_t *ip, uint16_t port, Client& client, Stream& stream) {
- this->_state = MQTT_DISCONNECTED;
- setServer(ip,port);
- setClient(client);
- setStream(stream);
-}
-PubSubClient::PubSubClient(uint8_t *ip, uint16_t port, MQTT_CALLBACK_SIGNATURE, Client& client) {
- this->_state = MQTT_DISCONNECTED;
- setServer(ip, port);
- setCallback(callback);
- setClient(client);
- this->stream = NULL;
-}
-PubSubClient::PubSubClient(uint8_t *ip, uint16_t port, MQTT_CALLBACK_SIGNATURE, Client& client, Stream& stream) {
- this->_state = MQTT_DISCONNECTED;
- setServer(ip,port);
- setCallback(callback);
- setClient(client);
- setStream(stream);
-}
-
-PubSubClient::PubSubClient(const char* domain, uint16_t port, Client& client) {
- this->_state = MQTT_DISCONNECTED;
- setServer(domain,port);
- setClient(client);
- this->stream = NULL;
-}
-PubSubClient::PubSubClient(const char* domain, uint16_t port, Client& client, Stream& stream) {
- this->_state = MQTT_DISCONNECTED;
- setServer(domain,port);
- setClient(client);
- setStream(stream);
-}
-PubSubClient::PubSubClient(const char* domain, uint16_t port, MQTT_CALLBACK_SIGNATURE, Client& client) {
- this->_state = MQTT_DISCONNECTED;
- setServer(domain,port);
- setCallback(callback);
- setClient(client);
- this->stream = NULL;
-}
-PubSubClient::PubSubClient(const char* domain, uint16_t port, MQTT_CALLBACK_SIGNATURE, Client& client, Stream& stream) {
- this->_state = MQTT_DISCONNECTED;
- setServer(domain,port);
- setCallback(callback);
- setClient(client);
- setStream(stream);
-}
-
-PubSubClient::~PubSubClient()
-{
- if (buffer != nullptr) {
- free(buffer);
- buffer = nullptr;
- }
-}
-
-boolean PubSubClient::connect(const char *id) {
- return connect(id,NULL,NULL,0,0,0,0,1);
-}
-
-boolean PubSubClient::connect(const char *id, const char *user, const char *pass) {
- return connect(id,user,pass,0,0,0,0,1);
-}
-
-boolean PubSubClient::connect(const char *id, const char* willTopic, uint8_t willQos, boolean willRetain, const char* willMessage) {
- return connect(id,NULL,NULL,willTopic,willQos,willRetain,willMessage,1);
-}
-
-boolean PubSubClient::connect(const char *id, const char *user, const char *pass, const char* willTopic, uint8_t willQos, boolean willRetain, const char* willMessage) {
- return connect(id,user,pass,willTopic,willQos,willRetain,willMessage,1);
-}
-
-boolean PubSubClient::connect(const char *id, const char *user, const char *pass, const char* willTopic, uint8_t willQos, boolean willRetain, const char* willMessage, boolean cleanSession) {
- if (!initBuffer()) {
- return false;
- }
-
- if (!connected()) {
- int result = 0;
-
- if (_client == nullptr) {
- return false;
- }
- if (_client->connected()) {
- result = 1;
- } else {
- if (domain.length() != 0) {
-#ifdef ESP32
- WiFiClient* wfc = (WiFiClient*)_client;
- result = wfc->connect(this->domain.c_str(), this->port, ESP32_CONNECTION_TIMEOUT);
-#else
- result = _client->connect(this->domain.c_str(), this->port);
-#endif
- } else {
-#ifdef ESP32
- WiFiClient* wfc = (WiFiClient*)_client;
- result = wfc->connect(this->ip, this->port, ESP32_CONNECTION_TIMEOUT);
-#else
- result = _client->connect(this->ip, this->port);
-#endif
- }
- }
- if (result == 1) {
- nextMsgId = 1;
- // Leave room in the buffer for header and variable length field
- uint16_t length = MQTT_MAX_HEADER_SIZE;
- unsigned int j;
-
-#if MQTT_VERSION == MQTT_VERSION_3_1
- uint8_t d[9] = {0x00,0x06,'M','Q','I','s','d','p', MQTT_VERSION};
-#define MQTT_HEADER_VERSION_LENGTH 9
-#elif MQTT_VERSION == MQTT_VERSION_3_1_1
- uint8_t d[7] = {0x00,0x04,'M','Q','T','T',MQTT_VERSION};
-#define MQTT_HEADER_VERSION_LENGTH 7
-#endif
- for (j = 0;j>1);
- }
- }
-
- buffer[length++] = v;
-
- buffer[length++] = ((MQTT_KEEPALIVE) >> 8);
- buffer[length++] = ((MQTT_KEEPALIVE) & 0xFF);
-
- CHECK_STRING_LENGTH(length,id)
- length = writeString(id,buffer,length);
- if (willTopic) {
- CHECK_STRING_LENGTH(length,willTopic)
- length = writeString(willTopic,buffer,length);
- CHECK_STRING_LENGTH(length,willMessage)
- length = writeString(willMessage,buffer,length);
- }
-
- if(user != NULL) {
- CHECK_STRING_LENGTH(length,user)
- length = writeString(user,buffer,length);
- if(pass != NULL) {
- CHECK_STRING_LENGTH(length,pass)
- length = writeString(pass,buffer,length);
- }
- }
-
- write(MQTTCONNECT,buffer,length-MQTT_MAX_HEADER_SIZE);
-
- lastInActivity = lastOutActivity = millis();
- pingOutstanding = false; // See: https://github.com/knolleary/pubsubclient/pull/802
-
- while (!_client->available()) {
- delay(0); // Prevent watchdog crashes
- unsigned long t = millis();
- if (t-lastInActivity >= ((int32_t) MQTT_SOCKET_TIMEOUT*1000UL)) {
- _state = MQTT_CONNECTION_TIMEOUT;
- _client->stop();
- return false;
- }
- }
- uint8_t llen;
- uint16_t len = readPacket(&llen);
-
- if (len == 4) {
- if (buffer[3] == 0) {
- lastInActivity = millis();
- pingOutstanding = false;
- _state = MQTT_CONNECTED;
- return true;
- } else {
- _state = buffer[3];
- }
- }
- _client->stop();
- } else {
- _state = MQTT_CONNECT_FAILED;
- }
- return false;
- }
- return true;
-}
-
-// reads a byte into result
-boolean PubSubClient::readByte(uint8_t * result) {
- if (_client == nullptr) {
- return false;
- }
- uint32_t previousMillis = millis();
- while(!_client->available()) {
- delay(1); // Prevent watchdog crashes
- uint32_t currentMillis = millis();
- if(currentMillis - previousMillis >= ((int32_t) MQTT_SOCKET_TIMEOUT * 1000)){
- return false;
- }
- }
- *result = _client->read();
- return true;
-}
-
-// reads a byte into result[*index] and increments index
-boolean PubSubClient::readByte(uint8_t * result, uint16_t * index){
- uint16_t current_index = *index;
- uint8_t * write_address = &(result[current_index]);
- if(readByte(write_address)){
- *index = current_index + 1;
- return true;
- }
- return false;
-}
-
-uint16_t PubSubClient::readPacket(uint8_t* lengthLength) {
- if (!initBuffer()) {
- return 0;
- }
-
- uint16_t len = 0;
- if(!readByte(buffer, &len)) return 0;
- bool isPublish = (buffer[0]&0xF0) == MQTTPUBLISH;
- uint32_t multiplier = 1;
- uint16_t length = 0;
- uint8_t digit = 0;
- uint16_t skip = 0;
- uint8_t start = 0;
-
- do {
- if (len == 5) {
- // Invalid remaining length encoding - kill the connection
- _state = MQTT_DISCONNECTED;
- _client->stop();
- return 0;
- }
- if(!readByte(&digit)) return 0;
- buffer[len++] = digit;
- length += (digit & 127) * multiplier;
- multiplier *= 128;
- } while ((digit & 128) != 0 && len < (MQTT_MAX_PACKET_SIZE -2));
- *lengthLength = len-1;
-
- if (isPublish) {
- // Read in topic length to calculate bytes to skip over for Stream writing
- if(!readByte(buffer, &len)) return 0;
- if(!readByte(buffer, &len)) return 0;
- skip = (buffer[*lengthLength+1]<<8)+buffer[*lengthLength+2];
- start = 2;
- if (buffer[0]&MQTTQOS1) {
- // skip message id
- skip += 2;
- }
- }
-
- for (uint16_t i = start;istream) {
- if (isPublish && len-*lengthLength-2>skip) {
- this->stream->write(digit);
- }
- }
- if (len < MQTT_MAX_PACKET_SIZE) {
- buffer[len] = digit;
- }
- len++;
- }
-
- if (!this->stream && len > MQTT_MAX_PACKET_SIZE) {
- len = 0; // This will cause the packet to be ignored.
- }
-
- return len;
-}
-
-bool PubSubClient::loop_read() {
- if (!initBuffer()) {
- return false;
- }
-
- if (_client == nullptr) {
- return false;
- }
- if (!_client->available()) {
- return false;
- }
- uint8_t llen;
- uint16_t len = readPacket(&llen);
- if (len == 0) {
- return false;
- }
- unsigned long t = millis();
- lastInActivity = t;
- uint8_t type = buffer[0]&0xF0;
-
- switch(type) {
- case MQTTPUBLISH:
- {
- if (callback) {
- const bool msgId_present = (buffer[0]&0x06) == MQTTQOS1;
- const uint16_t tl_offset = llen+1;
- const uint16_t tl = (buffer[tl_offset]<<8)+buffer[tl_offset+1]; /* topic length in bytes */
- const uint16_t topic_offset = tl_offset+2;
- const uint16_t msgId_offset = topic_offset+tl;
- const uint16_t payload_offset = msgId_present ? msgId_offset+2 : msgId_offset;
- if (payload_offset >= MQTT_MAX_PACKET_SIZE) return false;
- if (len < payload_offset) return false;
- // Need to move the topic 1 byte to insert a '\0' at the end of the topic.
- memmove(buffer+topic_offset-1,buffer+topic_offset,tl); /* move topic inside buffer 1 byte to front */
- buffer[topic_offset-1+tl] = 0; /* end the topic as a 'C' string with \x00 */
- char *topic = (char*) buffer+topic_offset-1;
- uint8_t *payload;
- // msgId only present for QOS>0
- if (msgId_present) {
- const uint16_t msgId = (buffer[msgId_offset]<<8)+buffer[msgId_offset+1];
- payload = buffer+payload_offset;
- callback(topic,payload,len-payload_offset);
- if (_client->connected()) {
- buffer[0] = MQTTPUBACK;
- buffer[1] = 2;
- buffer[2] = (msgId >> 8);
- buffer[3] = (msgId & 0xFF);
- if (_client->write(buffer,4) != 0) {
- lastOutActivity = t;
- }
- }
- } else {
- // No msgId
- payload = buffer+payload_offset;
- callback(topic,payload,len-payload_offset);
- }
- }
- break;
- }
- case MQTTPINGREQ:
- {
- if (_client->connected()) {
- buffer[0] = MQTTPINGRESP;
- buffer[1] = 0;
- _client->write(buffer,2);
- }
- break;
- }
- case MQTTPINGRESP:
- {
- pingOutstanding = false;
- break;
- }
- default:
- return false;
- }
- return true;
-}
-
-boolean PubSubClient::loop() {
- loop_read();
- if (connected()) {
- unsigned long t = millis();
- if ((t - lastInActivity > MQTT_KEEPALIVE*1000UL) || (t - lastOutActivity > MQTT_KEEPALIVE*1000UL)) {
- if (pingOutstanding) {
- this->_state = MQTT_CONNECTION_TIMEOUT;
- _client->stop();
- return false;
- } else {
- buffer[0] = MQTTPINGREQ;
- buffer[1] = 0;
- if (_client->write(buffer,2) != 0) {
- lastOutActivity = t;
- lastInActivity = t;
- }
- pingOutstanding = true;
- }
- }
- return true;
- }
- return false;
-}
-
-boolean PubSubClient::publish(const char* topic, const char* payload) {
- size_t plength = (payload != nullptr) ? strlen(payload) : 0;
- return publish(topic,(const uint8_t*)payload,plength,false);
-}
-
-boolean PubSubClient::publish(const char* topic, const char* payload, boolean retained) {
- size_t plength = (payload != nullptr) ? strlen(payload) : 0;
- return publish(topic,(const uint8_t*)payload,plength,retained);
-}
-
-boolean PubSubClient::publish(const char* topic, const uint8_t* payload, unsigned int plength) {
- return publish(topic, payload, plength, false);
-}
-
-boolean PubSubClient::publish(const char* topic, const uint8_t* payload, unsigned int plength, boolean retained) {
- if (!beginPublish(topic, plength, retained)) {
- return false;
- }
- for (unsigned int i=0;iwrite(buffer+(MQTT_MAX_HEADER_SIZE-hlen),length-(MQTT_MAX_HEADER_SIZE-hlen));
- if (rc > 0) {
- lastOutActivity = millis();
- }
- return (rc == (length-(MQTT_MAX_HEADER_SIZE-hlen)));
- }
- return false;
-}
-
-int PubSubClient::endPublish() {
- flushBuffer();
- return 1;
-}
-
-size_t PubSubClient::write(uint8_t data) {
- if (_client == nullptr) {
- lastOutActivity = millis();
- return 0;
- }
- size_t rc = appendBuffer(data);
- if (rc != 0) {
- lastOutActivity = millis();
- }
- return rc;
-}
-
-size_t PubSubClient::write(const uint8_t *data, size_t size) {
- if (_client == nullptr) {
- lastOutActivity = millis();
- return 0;
- }
- size_t rc = appendBuffer(data,size);
- if (rc != 0) {
- lastOutActivity = millis();
- }
- return rc;
-}
-
-size_t PubSubClient::write(const String& message) {
- return write(reinterpret_cast(message.c_str()), message.length());
-}
-
-
-size_t PubSubClient::buildHeader(uint8_t header, uint8_t* buf, uint32_t length) {
- uint8_t lenBuf[4];
- uint8_t llen = 0;
- uint8_t digit;
- uint8_t pos = 0;
- uint32_t len = length;
- do {
- digit = len % 128;
- len = len / 128;
- if (len > 0) {
- digit |= 0x80;
- }
- lenBuf[pos++] = digit;
- llen++;
- } while(len>0 && pos < 4);
-
- buf[4-llen] = header;
- for (int i=0;i 0) && result) {
- delay(0); // Prevent watchdog crashes
- bytesToWrite = (bytesRemaining > MQTT_MAX_TRANSFER_SIZE)?MQTT_MAX_TRANSFER_SIZE:bytesRemaining;
- rc = _client->write(writeBuf,bytesToWrite);
- result = (rc == bytesToWrite);
- bytesRemaining -= rc;
- writeBuf += rc;
- }
- return result;
-#else
- rc = _client->write(buf+(MQTT_MAX_HEADER_SIZE-hlen),length+hlen);
- if (rc != 0) {
- lastOutActivity = millis();
- }
- return (rc == hlen+length);
-#endif
-}
-
-boolean PubSubClient::subscribe(const char* topic) {
- return subscribe(topic, 0);
-}
-
-boolean PubSubClient::subscribe(const char* topic, uint8_t qos) {
- if (qos > 1) {
- return false;
- }
- if (MQTT_MAX_PACKET_SIZE < 9 + strlen(topic)) {
- // Too long
- return false;
- }
- if (connected()) {
- // Leave room in the buffer for header and variable length field
- uint16_t length = MQTT_MAX_HEADER_SIZE;
- nextMsgId++;
- if (nextMsgId == 0) {
- nextMsgId = 1;
- }
- buffer[length++] = (nextMsgId >> 8);
- buffer[length++] = (nextMsgId & 0xFF);
- length = writeString((char*)topic, buffer,length);
- buffer[length++] = qos;
- return write(MQTTSUBSCRIBE|MQTTQOS1,buffer,length-MQTT_MAX_HEADER_SIZE);
- }
- return false;
-}
-
-boolean PubSubClient::unsubscribe(const char* topic) {
- if (MQTT_MAX_PACKET_SIZE < 9 + strlen(topic)) {
- // Too long
- return false;
- }
- if (connected()) {
- uint16_t length = MQTT_MAX_HEADER_SIZE;
- nextMsgId++;
- if (nextMsgId == 0) {
- nextMsgId = 1;
- }
- buffer[length++] = (nextMsgId >> 8);
- buffer[length++] = (nextMsgId & 0xFF);
- length = writeString(topic, buffer,length);
- return write(MQTTUNSUBSCRIBE|MQTTQOS1,buffer,length-MQTT_MAX_HEADER_SIZE);
- }
- return false;
-}
-
-void PubSubClient::disconnect() {
- if (_state == MQTT_DISCONNECTED || !initBuffer()) {
- _state = MQTT_DISCONNECTED;
- lastInActivity = lastOutActivity = millis();
-
- return;
- }
-
- buffer[0] = MQTTDISCONNECT;
- buffer[1] = 0;
- if (_client != nullptr) {
- _client->write(buffer,2);
- _client->flush();
- _client->stop();
- }
- _state = MQTT_DISCONNECTED;
- lastInActivity = lastOutActivity = millis();
-}
-
-uint16_t PubSubClient::writeString(const char* string, uint8_t* buf, uint16_t pos) {
- const char* idp = string;
- uint16_t i = 0;
- pos += 2;
- while (*idp && pos < (MQTT_MAX_PACKET_SIZE - 2)) {
- buf[pos++] = *idp++;
- i++;
- }
- buf[pos-i-2] = (i >> 8);
- buf[pos-i-1] = (i & 0xFF);
- return pos;
-}
-
-size_t PubSubClient::appendBuffer(uint8_t data) {
- if (!initBuffer()) {
- return 0;
- }
-
- buffer[_bufferWritePos] = data;
- ++_bufferWritePos;
- if (_bufferWritePos >= MQTT_MAX_PACKET_SIZE) {
- if (flushBuffer() == 0) return 0;
- }
- return 1;
-}
-
-size_t PubSubClient::appendBuffer(const uint8_t *data, size_t size) {
- for (size_t i = 0; i < size; ++i) {
- if (appendBuffer(data[i]) == 0) return i;
- }
- return size;
-}
-
-size_t PubSubClient::flushBuffer() {
- size_t rc = 0;
- if (_bufferWritePos > 0) {
- if (connected()) {
- rc = _client->write(buffer, _bufferWritePos);
- if (rc != 0) {
- lastOutActivity = millis();
- }
- }
- _bufferWritePos = 0;
- }
- return rc;
-}
-
-bool PubSubClient::initBuffer()
-{
- if (buffer == nullptr) {
-#ifdef USE_SECOND_HEAP
- HeapSelectIram ephemeral;
-#endif
- buffer = (uint8_t*) malloc(sizeof(uint8_t) * MQTT_MAX_PACKET_SIZE);
- }
- return buffer != nullptr;
-}
-
-boolean PubSubClient::connected() {
- if (_client == NULL ) {
- this->_state = MQTT_DISCONNECTED;
- return false;
- }
- if (_client->connected() == 0) {
- bool lastStateConnected = this->_state == MQTT_CONNECTED;
- this->disconnect();
- if (lastStateConnected) {
- this->_state = MQTT_CONNECTION_LOST;
- }
- return false;
- }
- return this->_state == MQTT_CONNECTED;
-}
-
-PubSubClient& PubSubClient::setServer(uint8_t * ip, uint16_t port) {
- IPAddress addr(ip[0],ip[1],ip[2],ip[3]);
- return setServer(addr,port);
-}
-
-PubSubClient& PubSubClient::setServer(IPAddress ip, uint16_t port) {
- this->ip = ip;
- this->port = port;
- this->domain = "";
- return *this;
-}
-
-PubSubClient& PubSubClient::setServer(const char * domain, uint16_t port) {
- this->domain = domain;
- this->port = port;
- return *this;
-}
-
-PubSubClient& PubSubClient::setCallback(MQTT_CALLBACK_SIGNATURE) {
- this->callback = callback;
- return *this;
-}
-
-PubSubClient& PubSubClient::setClient(Client& client){
- this->_client = &client;
- return *this;
-}
-
-PubSubClient& PubSubClient::setStream(Stream& stream){
- this->stream = &stream;
- return *this;
-}
-
-int PubSubClient::state() {
- return this->_state;
-}
+/*
+ PubSubClient.cpp - A simple client for MQTT.
+ Nick O'Leary
+ http://knolleary.net
+*/
+
+#include "PubSubClient.h"
+#include
+
+#ifdef ESP32
+#include
+#endif
+
+#ifdef USE_SECOND_HEAP
+ #include
+#endif
+
+
+PubSubClient::PubSubClient() {
+ this->_state = MQTT_DISCONNECTED;
+ this->_client = NULL;
+ this->stream = NULL;
+ this->keepAlive_sec = MQTT_KEEPALIVE;
+ this->socketTimeout_msec = MQTT_SOCKET_TIMEOUT * 1000;
+
+ setCallback(NULL);
+}
+
+PubSubClient::PubSubClient(Client& client) {
+ this->_state = MQTT_DISCONNECTED;
+ setClient(client);
+ this->stream = NULL;
+ this->keepAlive_sec = MQTT_KEEPALIVE;
+ this->socketTimeout_msec = MQTT_SOCKET_TIMEOUT * 1000;
+}
+
+PubSubClient::PubSubClient(IPAddress addr, uint16_t port, Client& client) {
+ this->_state = MQTT_DISCONNECTED;
+ setServer(addr, port);
+ setClient(client);
+ this->stream = NULL;
+ this->keepAlive_sec = MQTT_KEEPALIVE;
+ this->socketTimeout_msec = MQTT_SOCKET_TIMEOUT * 1000;
+}
+PubSubClient::PubSubClient(IPAddress addr, uint16_t port, Client& client, Stream& stream) {
+ this->_state = MQTT_DISCONNECTED;
+ setServer(addr,port);
+ setClient(client);
+ setStream(stream);
+ this->keepAlive_sec = MQTT_KEEPALIVE;
+ this->socketTimeout_msec = MQTT_SOCKET_TIMEOUT * 1000;
+}
+PubSubClient::PubSubClient(IPAddress addr, uint16_t port, MQTT_CALLBACK_SIGNATURE, Client& client) {
+ this->_state = MQTT_DISCONNECTED;
+ setServer(addr, port);
+ setCallback(callback);
+ setClient(client);
+ this->stream = NULL;
+ this->keepAlive_sec = MQTT_KEEPALIVE;
+ this->socketTimeout_msec = MQTT_SOCKET_TIMEOUT * 1000;
+}
+PubSubClient::PubSubClient(IPAddress addr, uint16_t port, MQTT_CALLBACK_SIGNATURE, Client& client, Stream& stream) {
+ this->_state = MQTT_DISCONNECTED;
+ setServer(addr,port);
+ setCallback(callback);
+ setClient(client);
+ setStream(stream);
+ this->keepAlive_sec = MQTT_KEEPALIVE;
+ this->socketTimeout_msec = MQTT_SOCKET_TIMEOUT * 1000;
+}
+
+PubSubClient::PubSubClient(uint8_t *ip, uint16_t port, Client& client) {
+ this->_state = MQTT_DISCONNECTED;
+ setServer(ip, port);
+ setClient(client);
+ this->stream = NULL;
+ this->keepAlive_sec = MQTT_KEEPALIVE;
+ this->socketTimeout_msec = MQTT_SOCKET_TIMEOUT * 1000;
+}
+PubSubClient::PubSubClient(uint8_t *ip, uint16_t port, Client& client, Stream& stream) {
+ this->_state = MQTT_DISCONNECTED;
+ setServer(ip,port);
+ setClient(client);
+ setStream(stream);
+ this->keepAlive_sec = MQTT_KEEPALIVE;
+ this->socketTimeout_msec = MQTT_SOCKET_TIMEOUT * 1000;
+}
+PubSubClient::PubSubClient(uint8_t *ip, uint16_t port, MQTT_CALLBACK_SIGNATURE, Client& client) {
+ this->_state = MQTT_DISCONNECTED;
+ setServer(ip, port);
+ setCallback(callback);
+ setClient(client);
+ this->stream = NULL;
+ this->keepAlive_sec = MQTT_KEEPALIVE;
+ this->socketTimeout_msec = MQTT_SOCKET_TIMEOUT * 1000;
+}
+PubSubClient::PubSubClient(uint8_t *ip, uint16_t port, MQTT_CALLBACK_SIGNATURE, Client& client, Stream& stream) {
+ this->_state = MQTT_DISCONNECTED;
+ setServer(ip,port);
+ setCallback(callback);
+ setClient(client);
+ setStream(stream);
+ this->keepAlive_sec = MQTT_KEEPALIVE;
+ this->socketTimeout_msec = MQTT_SOCKET_TIMEOUT * 1000;
+}
+
+PubSubClient::PubSubClient(const char* domain, uint16_t port, Client& client) {
+ this->_state = MQTT_DISCONNECTED;
+ setServer(domain,port);
+ setClient(client);
+ this->stream = NULL;
+ this->keepAlive_sec = MQTT_KEEPALIVE;
+ this->socketTimeout_msec = MQTT_SOCKET_TIMEOUT * 1000;
+}
+PubSubClient::PubSubClient(const char* domain, uint16_t port, Client& client, Stream& stream) {
+ this->_state = MQTT_DISCONNECTED;
+ setServer(domain,port);
+ setClient(client);
+ setStream(stream);
+ this->keepAlive_sec = MQTT_KEEPALIVE;
+ this->socketTimeout_msec = MQTT_SOCKET_TIMEOUT * 1000;
+}
+PubSubClient::PubSubClient(const char* domain, uint16_t port, MQTT_CALLBACK_SIGNATURE, Client& client) {
+ this->_state = MQTT_DISCONNECTED;
+ setServer(domain,port);
+ setCallback(callback);
+ setClient(client);
+ this->stream = NULL;
+ this->keepAlive_sec = MQTT_KEEPALIVE;
+ this->socketTimeout_msec = MQTT_SOCKET_TIMEOUT * 1000;
+}
+PubSubClient::PubSubClient(const char* domain, uint16_t port, MQTT_CALLBACK_SIGNATURE, Client& client, Stream& stream) {
+ this->_state = MQTT_DISCONNECTED;
+ setServer(domain,port);
+ setCallback(callback);
+ setClient(client);
+ setStream(stream);
+ this->keepAlive_sec = MQTT_KEEPALIVE;
+ this->socketTimeout_msec = MQTT_SOCKET_TIMEOUT * 1000;
+}
+
+PubSubClient::~PubSubClient()
+{
+ if (buffer != nullptr) {
+ free(buffer);
+ buffer = nullptr;
+ }
+}
+
+boolean PubSubClient::connect(const char *id) {
+ return connect(id,NULL,NULL,0,0,0,0,1);
+}
+
+boolean PubSubClient::connect(const char *id, const char *user, const char *pass) {
+ return connect(id,user,pass,0,0,0,0,1);
+}
+
+boolean PubSubClient::connect(const char *id, const char* willTopic, uint8_t willQos, boolean willRetain, const char* willMessage) {
+ return connect(id,NULL,NULL,willTopic,willQos,willRetain,willMessage,1);
+}
+
+boolean PubSubClient::connect(const char *id, const char *user, const char *pass, const char* willTopic, uint8_t willQos, boolean willRetain, const char* willMessage) {
+ return connect(id,user,pass,willTopic,willQos,willRetain,willMessage,1);
+}
+
+boolean PubSubClient::connect(const char *id, const char *user, const char *pass, const char* willTopic, uint8_t willQos, boolean willRetain, const char* willMessage, boolean cleanSession) {
+ if (!initBuffer()) {
+ return false;
+ }
+
+ if (!connected()) {
+ int result = 0;
+
+ if (_client == nullptr) {
+ return false;
+ }
+ if (_client->connected()) {
+ result = 1;
+ } else {
+ if (domain.length() != 0) {
+#if defined(ESP32) && ESP_IDF_VERSION_MAJOR < 5
+ WiFiClient* wfc = (WiFiClient*)_client;
+ result = wfc->connect(this->domain.c_str(), this->port, _client->getTimeout());
+#else
+ result = _client->connect(this->domain.c_str(), this->port);
+#endif
+ } else {
+#if defined(ESP32) && ESP_IDF_VERSION_MAJOR < 5
+ WiFiClient* wfc = (WiFiClient*)_client;
+ result = wfc->connect(this->ip, this->port, _client->getTimeout());
+#else
+ result = _client->connect(this->ip, this->port);
+#endif
+ }
+ }
+ if (result == 1) {
+ nextMsgId = 1;
+ // Leave room in the buffer for header and variable length field
+ uint16_t length = MQTT_MAX_HEADER_SIZE;
+ unsigned int j;
+
+#if MQTT_VERSION == MQTT_VERSION_3_1
+ uint8_t d[9] = {0x00,0x06,'M','Q','I','s','d','p', MQTT_VERSION};
+#define MQTT_HEADER_VERSION_LENGTH 9
+#elif MQTT_VERSION == MQTT_VERSION_3_1_1
+ uint8_t d[7] = {0x00,0x04,'M','Q','T','T',MQTT_VERSION};
+#define MQTT_HEADER_VERSION_LENGTH 7
+#endif
+ for (j = 0;j>1);
+ }
+ }
+
+ buffer[length++] = v;
+
+ buffer[length++] = ((keepAlive_sec) >> 8);
+ buffer[length++] = ((keepAlive_sec) & 0xFF);
+
+ CHECK_STRING_LENGTH(length,id)
+ length = writeString(id,buffer,length);
+ if (willTopic) {
+ CHECK_STRING_LENGTH(length,willTopic)
+ length = writeString(willTopic,buffer,length);
+ CHECK_STRING_LENGTH(length,willMessage)
+ length = writeString(willMessage,buffer,length);
+ }
+
+ if(user != NULL) {
+ CHECK_STRING_LENGTH(length,user)
+ length = writeString(user,buffer,length);
+ if(pass != NULL) {
+ CHECK_STRING_LENGTH(length,pass)
+ length = writeString(pass,buffer,length);
+ }
+ }
+
+ write(MQTTCONNECT,buffer,length-MQTT_MAX_HEADER_SIZE);
+
+ lastInActivity = lastOutActivity = millis();
+ pingOutstanding = false; // See: https://github.com/knolleary/pubsubclient/pull/802
+
+ while (!_client->available()) {
+ delay(0); // Prevent watchdog crashes
+ unsigned long t = millis();
+ if ((int32_t)(t - lastInActivity) >= socketTimeout_msec) {
+ _state = MQTT_CONNECTION_TIMEOUT;
+ _client->stop();
+ return false;
+ }
+ }
+ uint8_t llen;
+ uint16_t len = readPacket(&llen);
+
+ if (len == 4) {
+ if (buffer[3] == 0) {
+ lastInActivity = millis();
+ pingOutstanding = false;
+ _state = MQTT_CONNECTED;
+ return true;
+ } else {
+ _state = buffer[3];
+ }
+ }
+ _client->stop();
+ } else {
+ _state = MQTT_CONNECT_FAILED;
+ }
+ return false;
+ }
+ return true;
+}
+
+// reads a byte into result
+boolean PubSubClient::readByte(uint8_t * result) {
+ if (_client == nullptr) {
+ return false;
+ }
+ uint32_t previousMillis = millis();
+
+ while(!_client->available() && _client->connected()) {
+ delay(1); // Prevent watchdog crashes
+
+ if((int32_t)(millis() - previousMillis) >= socketTimeout_msec){
+ return false;
+ }
+ }
+ *result = _client->read();
+ return true;
+}
+
+// reads a byte into result[*index] and increments index
+boolean PubSubClient::readByte(uint8_t * result, uint16_t * index){
+ uint16_t current_index = *index;
+ uint8_t * write_address = &(result[current_index]);
+ if(readByte(write_address)){
+ *index = current_index + 1;
+ return true;
+ }
+ return false;
+}
+
+uint16_t PubSubClient::readPacket(uint8_t* lengthLength) {
+ if (!initBuffer()) {
+ return 0;
+ }
+
+ uint16_t len = 0;
+ if(!readByte(buffer, &len)) return 0;
+ bool isPublish = (buffer[0]&0xF0) == MQTTPUBLISH;
+ uint32_t multiplier = 1;
+ uint16_t length = 0;
+ uint8_t digit = 0;
+ uint16_t skip = 0;
+ uint8_t start = 0;
+
+ do {
+ if (len == 5) {
+ // Invalid remaining length encoding - kill the connection
+ _state = MQTT_DISCONNECTED;
+ _client->stop();
+ return 0;
+ }
+ if(!readByte(&digit)) return 0;
+ buffer[len++] = digit;
+ length += (digit & 127) * multiplier;
+ multiplier *= 128;
+ } while ((digit & 128) != 0 && len < (MQTT_MAX_PACKET_SIZE -2));
+ *lengthLength = len-1;
+
+ if (isPublish) {
+ // Read in topic length to calculate bytes to skip over for Stream writing
+ if(!readByte(buffer, &len)) return 0;
+ if(!readByte(buffer, &len)) return 0;
+ skip = (buffer[*lengthLength+1]<<8)+buffer[*lengthLength+2];
+ start = 2;
+ if (buffer[0]&MQTTQOS1) {
+ // skip message id
+ skip += 2;
+ }
+ }
+
+ for (uint16_t i = start;istream) {
+ if (isPublish && len-*lengthLength-2>skip) {
+ this->stream->write(digit);
+ }
+ }
+ if (len < MQTT_MAX_PACKET_SIZE) {
+ buffer[len] = digit;
+ }
+ len++;
+ }
+
+ if (!this->stream && len > MQTT_MAX_PACKET_SIZE) {
+ len = 0; // This will cause the packet to be ignored.
+ }
+
+ return len;
+}
+
+bool PubSubClient::loop_read() {
+ if (!initBuffer()) {
+ return false;
+ }
+
+ if (_client == nullptr) {
+ return false;
+ }
+ if (!_client->available()) {
+ return false;
+ }
+ uint8_t llen;
+ uint16_t len = readPacket(&llen);
+ if (len == 0) {
+ return false;
+ }
+ unsigned long t = millis();
+ lastInActivity = t;
+ uint8_t type = buffer[0]&0xF0;
+
+ switch(type) {
+ case MQTTPUBLISH:
+ {
+ if (callback) {
+ const bool msgId_present = (buffer[0]&0x06) == MQTTQOS1;
+ const uint16_t tl_offset = llen+1;
+ const uint16_t tl = (buffer[tl_offset]<<8)+buffer[tl_offset+1]; /* topic length in bytes */
+ const uint16_t topic_offset = tl_offset+2;
+ const uint16_t msgId_offset = topic_offset+tl;
+ const uint16_t payload_offset = msgId_present ? msgId_offset+2 : msgId_offset;
+ if (payload_offset >= MQTT_MAX_PACKET_SIZE) return false;
+ if (len < payload_offset) return false;
+ // Need to move the topic 1 byte to insert a '\0' at the end of the topic.
+ memmove(buffer+topic_offset-1,buffer+topic_offset,tl); /* move topic inside buffer 1 byte to front */
+ buffer[topic_offset-1+tl] = 0; /* end the topic as a 'C' string with \x00 */
+ char *topic = (char*) buffer+topic_offset-1;
+ uint8_t *payload;
+ // msgId only present for QOS>0
+ if (msgId_present) {
+ const uint16_t msgId = (buffer[msgId_offset]<<8)+buffer[msgId_offset+1];
+ payload = buffer+payload_offset;
+ callback(topic,payload,len-payload_offset);
+ if (_client->connected()) {
+ buffer[0] = MQTTPUBACK;
+ buffer[1] = 2;
+ buffer[2] = (msgId >> 8);
+ buffer[3] = (msgId & 0xFF);
+ if (_client->write(buffer,4) != 0) {
+ lastOutActivity = t;
+ }
+ }
+ } else {
+ // No msgId
+ payload = buffer+payload_offset;
+ callback(topic,payload,len-payload_offset);
+ }
+ }
+ break;
+ }
+ case MQTTPINGREQ:
+ {
+ if (_client->connected()) {
+ buffer[0] = MQTTPINGRESP;
+ buffer[1] = 0;
+ if (_client->write(buffer,2) != 0) {
+ lastOutActivity = t;
+ }
+ }
+ break;
+ }
+ case MQTTPINGRESP:
+ {
+ pingOutstanding = false;
+ break;
+ }
+ default:
+ return false;
+ }
+ return true;
+}
+
+boolean PubSubClient::loop() {
+ loop_read();
+ if (connected()) {
+ unsigned long t = millis();
+ // Send message at 2/3 of keepalive interval
+ // Wait for server-sent keep-alive till 3/2 of keepalive interval
+ // Just to make sure the broker will not disconnect us
+ const int32_t keepalive_66pct = pingOutstanding
+ ? keepAlive_sec * 1500
+ : keepAlive_sec * 666;
+ if (((int32_t)(t - lastInActivity) > keepalive_66pct) ||
+ ((int32_t)(t - lastOutActivity) > keepalive_66pct)) {
+ if (pingOutstanding) {
+ this->_state = MQTT_CONNECTION_TIMEOUT;
+ _client->stop();
+ return false;
+ } else {
+ buffer[0] = MQTTPINGREQ;
+ buffer[1] = 0;
+ if (_client->write(buffer,2) != 0) {
+ lastOutActivity = t;
+ lastInActivity = t;
+ }
+ pingOutstanding = true;
+ }
+ }
+ return true;
+ }
+ return false;
+}
+
+boolean PubSubClient::publish(const char* topic, const char* payload) {
+ size_t plength = (payload != nullptr) ? strlen(payload) : 0;
+ return publish(topic,(const uint8_t*)payload,plength,false);
+}
+
+boolean PubSubClient::publish(const char* topic, const char* payload, boolean retained) {
+ size_t plength = (payload != nullptr) ? strlen(payload) : 0;
+ return publish(topic,(const uint8_t*)payload,plength,retained);
+}
+
+boolean PubSubClient::publish(const char* topic, const uint8_t* payload, unsigned int plength) {
+ return publish(topic, payload, plength, false);
+}
+
+boolean PubSubClient::publish(const char* topic, const uint8_t* payload, unsigned int plength, boolean retained) {
+ if (!beginPublish(topic, plength, retained)) {
+ return false;
+ }
+ for (unsigned int i=0;iwrite(buffer+(MQTT_MAX_HEADER_SIZE-hlen),length-(MQTT_MAX_HEADER_SIZE-hlen));
+ if (rc > 0) {
+ lastOutActivity = millis();
+ }
+ return (rc == (length-(MQTT_MAX_HEADER_SIZE-hlen)));
+ }
+ return false;
+}
+
+int PubSubClient::endPublish() {
+ flushBuffer();
+ return 1;
+}
+
+size_t PubSubClient::write(uint8_t data) {
+ if (_client == nullptr) {
+ lastOutActivity = millis();
+ return 0;
+ }
+ size_t rc = appendBuffer(data);
+ if (rc != 0) {
+ lastOutActivity = millis();
+ }
+ return rc;
+}
+
+size_t PubSubClient::write(const uint8_t *data, size_t size) {
+ if (_client == nullptr) {
+ lastOutActivity = millis();
+ return 0;
+ }
+ size_t rc = appendBuffer(data,size);
+ if (rc != 0) {
+ lastOutActivity = millis();
+ }
+ return rc;
+}
+
+size_t PubSubClient::write(const String& message) {
+ return write(reinterpret_cast(message.c_str()), message.length());
+}
+
+
+size_t PubSubClient::buildHeader(uint8_t header, uint8_t* buf, uint32_t length) {
+ uint8_t lenBuf[4];
+ uint8_t llen = 0;
+ uint8_t digit;
+ uint8_t pos = 0;
+ uint32_t len = length;
+ do {
+ digit = len % 128;
+ len = len / 128;
+ if (len > 0) {
+ digit |= 0x80;
+ }
+ lenBuf[pos++] = digit;
+ llen++;
+ } while(len>0 && pos < 4);
+
+ buf[4-llen] = header;
+ for (int i=0;i 0) && result) {
+ delay(0); // Prevent watchdog crashes
+ bytesToWrite = (bytesRemaining > MQTT_MAX_TRANSFER_SIZE)?MQTT_MAX_TRANSFER_SIZE:bytesRemaining;
+ rc = _client->write(writeBuf,bytesToWrite);
+ result = (rc == bytesToWrite);
+ bytesRemaining -= rc;
+ writeBuf += rc;
+ if (rc != 0) {
+ lastOutActivity = millis();
+ }
+ }
+ return result;
+#else
+ rc = _client->write(buf+(MQTT_MAX_HEADER_SIZE-hlen),length+hlen);
+ if (rc != 0) {
+ lastOutActivity = millis();
+ }
+ return (rc == hlen+length);
+#endif
+}
+
+boolean PubSubClient::subscribe(const char* topic) {
+ return subscribe(topic, 0);
+}
+
+boolean PubSubClient::subscribe(const char* topic, uint8_t qos) {
+ if (qos > 1) {
+ return false;
+ }
+ if (MQTT_MAX_PACKET_SIZE < 9 + strlen(topic)) {
+ // Too long
+ return false;
+ }
+ if (connected()) {
+ // Leave room in the buffer for header and variable length field
+ uint16_t length = MQTT_MAX_HEADER_SIZE;
+ nextMsgId++;
+ if (nextMsgId == 0) {
+ nextMsgId = 1;
+ }
+ buffer[length++] = (nextMsgId >> 8);
+ buffer[length++] = (nextMsgId & 0xFF);
+ length = writeString((char*)topic, buffer,length);
+ buffer[length++] = qos;
+ return write(MQTTSUBSCRIBE|MQTTQOS1,buffer,length-MQTT_MAX_HEADER_SIZE);
+ }
+ return false;
+}
+
+boolean PubSubClient::unsubscribe(const char* topic) {
+ if (MQTT_MAX_PACKET_SIZE < 9 + strlen(topic)) {
+ // Too long
+ return false;
+ }
+ if (connected()) {
+ uint16_t length = MQTT_MAX_HEADER_SIZE;
+ nextMsgId++;
+ if (nextMsgId == 0) {
+ nextMsgId = 1;
+ }
+ buffer[length++] = (nextMsgId >> 8);
+ buffer[length++] = (nextMsgId & 0xFF);
+ length = writeString(topic, buffer,length);
+ return write(MQTTUNSUBSCRIBE|MQTTQOS1,buffer,length-MQTT_MAX_HEADER_SIZE);
+ }
+ return false;
+}
+
+void PubSubClient::disconnect() {
+ if (_state == MQTT_DISCONNECTED || !initBuffer()) {
+ _state = MQTT_DISCONNECTED;
+ lastInActivity = lastOutActivity = millis();
+
+ return;
+ }
+
+ buffer[0] = MQTTDISCONNECT;
+ buffer[1] = 0;
+ if (_client != nullptr) {
+ _client->write(buffer,2);
+ _client->flush();
+ _client->stop();
+ }
+ _state = MQTT_DISCONNECTED;
+ lastInActivity = lastOutActivity = millis();
+}
+
+uint16_t PubSubClient::writeString(const char* string, uint8_t* buf, uint16_t pos) {
+ const char* idp = string;
+ uint16_t i = 0;
+ pos += 2;
+ while (*idp && pos < (MQTT_MAX_PACKET_SIZE - 2)) {
+ buf[pos++] = *idp++;
+ i++;
+ }
+ buf[pos-i-2] = (i >> 8);
+ buf[pos-i-1] = (i & 0xFF);
+ return pos;
+}
+
+size_t PubSubClient::appendBuffer(uint8_t data) {
+ if (!initBuffer()) {
+ return 0;
+ }
+
+ buffer[_bufferWritePos] = data;
+ ++_bufferWritePos;
+ if (_bufferWritePos >= MQTT_MAX_PACKET_SIZE) {
+ if (flushBuffer() == 0) return 0;
+ }
+ return 1;
+}
+
+size_t PubSubClient::appendBuffer(const uint8_t *data, size_t size) {
+ for (size_t i = 0; i < size; ++i) {
+ if (appendBuffer(data[i]) == 0) return i;
+ }
+ return size;
+}
+
+size_t PubSubClient::flushBuffer() {
+ size_t rc = 0;
+ if (_bufferWritePos > 0) {
+ if (connected()) {
+ rc = _client->write(buffer, _bufferWritePos);
+ if (rc != 0) {
+ lastOutActivity = millis();
+ }
+ }
+ _bufferWritePos = 0;
+ }
+ return rc;
+}
+
+bool PubSubClient::initBuffer()
+{
+ constexpr size_t size = sizeof(uint8_t) * MQTT_MAX_PACKET_SIZE;
+ if (buffer == nullptr) {
+#ifdef ESP32
+ buffer = (uint8_t*) heap_caps_malloc(size, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
+ if (buffer == nullptr) {
+ buffer = (uint8_t*) malloc(size);
+ }
+#else
+ {
+#ifdef USE_SECOND_HEAP
+ // Try allocating on ESP8266 2nd heap
+ HeapSelectIram ephemeral;
+#endif
+ buffer = (uint8_t*) malloc(size);
+ if (buffer != nullptr) return true;
+ }
+#ifdef USE_SECOND_HEAP
+ // Not successful, try allocating on (ESP8266) main heap
+ HeapSelectDram ephemeral;
+#endif
+ buffer = (uint8_t*) malloc(size);
+#endif
+ }
+ return buffer != nullptr;
+}
+
+boolean PubSubClient::connected() {
+ if (_client == NULL ) {
+ this->_state = MQTT_DISCONNECTED;
+ return false;
+ }
+ if (_client->connected() == 0) {
+ bool lastStateConnected = this->_state == MQTT_CONNECTED;
+ this->disconnect();
+ if (lastStateConnected) {
+ this->_state = MQTT_CONNECTION_LOST;
+ }
+ return false;
+ }
+ return this->_state == MQTT_CONNECTED;
+}
+
+PubSubClient& PubSubClient::setServer(uint8_t * ip, uint16_t port) {
+ IPAddress addr(ip[0],ip[1],ip[2],ip[3]);
+ return setServer(addr,port);
+}
+
+PubSubClient& PubSubClient::setServer(IPAddress ip, uint16_t port) {
+ this->ip = ip;
+ this->port = port;
+ this->domain = "";
+ return *this;
+}
+
+PubSubClient& PubSubClient::setServer(const char * domain, uint16_t port) {
+ this->domain = domain;
+ this->port = port;
+ return *this;
+}
+
+PubSubClient& PubSubClient::setCallback(MQTT_CALLBACK_SIGNATURE) {
+ this->callback = callback;
+ return *this;
+}
+
+PubSubClient& PubSubClient::setClient(Client& client){
+ this->_client = &client;
+ return *this;
+}
+
+PubSubClient& PubSubClient::setStream(Stream& stream){
+ this->stream = &stream;
+ return *this;
+}
+
+int PubSubClient::state() {
+ return this->_state;
+}
+
+PubSubClient& PubSubClient::setKeepAlive(uint16_t keepAlive_sec) {
+ this->keepAlive_sec = keepAlive_sec;
+ return *this;
+}
+
+PubSubClient& PubSubClient::setSocketTimeout(uint16_t timeout_ms) {
+ this->socketTimeout_msec = timeout_ms;
+ return *this;
+}
\ No newline at end of file
diff --git a/lib/pubsubclient/src/PubSubClient.h b/lib/pubsubclient/src/PubSubClient.h
index 199e243bfd..4a59af578c 100644
--- a/lib/pubsubclient/src/PubSubClient.h
+++ b/lib/pubsubclient/src/PubSubClient.h
@@ -100,10 +100,10 @@ class PubSubClient : public Print {
private:
Client* _client;
uint8_t *buffer = nullptr;
- uint16_t nextMsgId;
- unsigned long lastOutActivity;
- unsigned long lastInActivity;
- bool pingOutstanding;
+ uint16_t nextMsgId = 0;
+ unsigned long lastOutActivity = 0;
+ unsigned long lastInActivity = 0;
+ bool pingOutstanding = false;
MQTT_CALLBACK_SIGNATURE;
// Try to read from the client whatever is available.
bool loop_read();
@@ -127,10 +127,12 @@ class PubSubClient : public Print {
IPAddress ip;
String domain;
- uint16_t port;
+ uint16_t port = 0;
Stream* stream;
- int _state;
+ int _state = MQTT_DISCONNECTED;
int _bufferWritePos = 0;
+ int16_t keepAlive_sec = MQTT_KEEPALIVE;
+ int16_t socketTimeout_msec = MQTT_SOCKET_TIMEOUT*1000;
public:
PubSubClient();
PubSubClient(Client& client);
@@ -194,6 +196,9 @@ class PubSubClient : public Print {
boolean loop();
boolean connected();
int state();
+
+ PubSubClient& setKeepAlive(uint16_t keepAlive_sec);
+ PubSubClient& setSocketTimeout(uint16_t timeout_ms);
};
diff --git a/platformio.ini b/platformio.ini
index 07784dfedd..2765912768 100644
--- a/platformio.ini
+++ b/platformio.ini
@@ -1,108 +1,108 @@
-;
-; PlatformIO Project Configuration File
-;
-; Please make sure to read documentation with examples first
-; http://docs.platformio.org/en/stable/projectconf.html
-;
-
-; *********************************************************************;
-; You can uncomment or add here Your favorite environment you want to work on at the moment
-; (uncomment only one !)
-; *********************************************************************;
-
-[platformio]
-description = Firmware for ESP82xx/ESP32/ESP32-S2/ESP32-S3/ESP32-C3 for easy IoT deployment of sensors.
-boards_dir = boards
-lib_dir = lib
-extra_configs =
- platformio_core_defs.ini
- platformio_esp82xx_base.ini
- platformio_esp82xx_envs.ini
- platformio_esp32_envs.ini
- platformio_esp32_solo1.ini
- platformio_esp32c3_envs.ini
- platformio_esp32s2_envs.ini
- platformio_esp32s3_envs.ini
- platformio_special_envs.ini
- platformio_esp32c2_envs.ini
- platformio_esp32c6_envs.ini
-
-;default_envs = normal_ESP32_4M
-default_envs = max_ESP32_16M8M_LittleFS_ETH
-;default_envs = normal_ESP32c6_4M316k_LittleFS_CDC
-; default_envs = custom_ESP8266_4M1M
-
-;default_envs = normal_ESP8266_4M1M
-;default_envs = test_beta_ESP8266_4M1M
-; ..etc
-;build_cache_dir = $PROJECT_DIR\.buildcache
-
-
-; add these:
-; -Werror -Wall -Wextra -pedantic -Wcast-align -Wcast-qual -Wctor-dtor-privacy -Wdisabled-optimization -Wformat=2 -Winit-self -Wlogical-op
-; -Wmissing-include-dirs -Wnoexcept -Wold-style-cast -Woverloaded-virtual -Wredundant-decls -Wshadow -Wsign-promo -Wstrict-null-sentinel
-; -Wstrict-overflow=5 -Wundef -Wno-unused -Wno-variadic-macros -Wno-parentheses -fdiagnostics-show-option
-; thanks @chouffe103
-[compiler_warnings]
-build_flags = -Wall -Wno-parentheses -fdiagnostics-show-option
-
-
-[minimal_size]
-build_flags =
- -Os
- -ffunction-sections
- -fdata-sections
- -Wl,--gc-sections
- -s
-
-
-[espota]
-upload_protocol = espota
-; each flag in a new line
-; Do not use port 8266 for OTA, since that's used for ESPeasy p2p
-upload_flags_esp8266 =
- --port=18266
-upload_flags_esp32 =
- --port=3232
-build_flags = -DFEATURE_ARDUINO_OTA=1
-upload_port = 192.168.1.152
-
-
-[debug_flags]
-;build_flags = -Wstack-usage=300
-build_flags =
-
-[mqtt_flags]
-build_flags = -DMQTT_MAX_PACKET_SIZE=1024
-
-[extra_scripts_default]
-extra_scripts = pre:tools/pio/install-requirements.py
- pre:tools/pio/set-ci-defines.py
- pre:tools/pio/generate-compiletime-defines.py
- tools/pio/copy_files.py
-
-[extra_scripts_esp8266]
-extra_scripts = tools/pio/gzip-firmware.py
- pre:tools/pio/concat_cpp_files.py
- post:tools/pio/remove_concat_cpp_files.py
- ${extra_scripts_default.extra_scripts}
-
-
-[common]
-lib_archive = false
-lib_ldf_mode = chain
-lib_compat_mode = strict
-shared_libdeps_dir = lib
-framework = arduino
-upload_speed = 115200
-monitor_speed = 115200
-;targets = size, checkprogsize
-targets =
-src_filter = +<*> -<.git/> -<.svn/> - - - - -<*/Commands_tmp/> -<*/ControllerQueue_tmp/> -<*/DataStructs_tmp/> -<*/DataTypes_tmp/> -<*/ESPEasyCore_tmp/> -<*/Globals_tmp/> -<*/Helpers_tmp/> -<*/PluginStructs_tmp/> -<*/WebServer_tmp/>
-
-; Backwards compatibility: https://github.com/platformio/platformio-core/issues/4270
-;build_src_filter = +<*> -<.git/> -<.svn/> - - - - -<*/Commands/> -<*/ControllerQueue/> -<*/DataStructs/> -<*/DataTypes/> -<*/Globals/> -<*/Helpers/> -<*/PluginStructs/> -<*/WebServer/>
-
-
-[env]
-extends = common
+;
+; PlatformIO Project Configuration File
+;
+; Please make sure to read documentation with examples first
+; http://docs.platformio.org/en/stable/projectconf.html
+;
+
+; *********************************************************************;
+; You can uncomment or add here Your favorite environment you want to work on at the moment
+; (uncomment only one !)
+; *********************************************************************;
+
+[platformio]
+description = Firmware for ESP82xx/ESP32/ESP32-S2/ESP32-S3/ESP32-C3 for easy IoT deployment of sensors.
+boards_dir = boards
+lib_dir = lib
+extra_configs =
+ platformio_core_defs.ini
+ platformio_esp82xx_base.ini
+ platformio_esp82xx_envs.ini
+ platformio_esp32_envs.ini
+ platformio_esp32_solo1.ini
+ platformio_esp32c3_envs.ini
+ platformio_esp32s2_envs.ini
+ platformio_esp32s3_envs.ini
+ platformio_special_envs.ini
+ platformio_esp32c2_envs.ini
+ platformio_esp32c6_envs.ini
+
+;default_envs = normal_ESP32_4M
+default_envs = max_ESP32_16M8M_LittleFS_ETH
+;default_envs = normal_ESP32c6_4M316k_LittleFS_CDC
+; default_envs = custom_ESP8266_4M1M
+
+;default_envs = normal_ESP8266_4M1M
+;default_envs = test_beta_ESP8266_4M1M
+; ..etc
+;build_cache_dir = $PROJECT_DIR\.buildcache
+
+
+; add these:
+; -Werror -Wall -Wextra -pedantic -Wcast-align -Wcast-qual -Wctor-dtor-privacy -Wdisabled-optimization -Wformat=2 -Winit-self -Wlogical-op
+; -Wmissing-include-dirs -Wnoexcept -Wold-style-cast -Woverloaded-virtual -Wredundant-decls -Wshadow -Wsign-promo -Wstrict-null-sentinel
+; -Wstrict-overflow=5 -Wundef -Wno-unused -Wno-variadic-macros -Wno-parentheses -fdiagnostics-show-option
+; thanks @chouffe103
+[compiler_warnings]
+build_flags = -Wall -Wno-parentheses -fdiagnostics-show-option
+
+
+[minimal_size]
+build_flags =
+ -Os
+ -ffunction-sections
+ -fdata-sections
+ -Wl,--gc-sections
+ -s
+
+
+[espota]
+upload_protocol = espota
+; each flag in a new line
+; Do not use port 8266 for OTA, since that's used for ESPeasy p2p
+upload_flags_esp8266 =
+ --port=18266
+upload_flags_esp32 =
+ --port=3232
+build_flags = -DFEATURE_ARDUINO_OTA=1
+upload_port = 192.168.1.152
+
+
+[debug_flags]
+;build_flags = -Wstack-usage=300
+build_flags =
+
+[mqtt_flags]
+build_flags = -DMQTT_MAX_PACKET_SIZE=1024
+
+[extra_scripts_default]
+extra_scripts = pre:tools/pio/install-requirements.py
+ pre:tools/pio/set-ci-defines.py
+ pre:tools/pio/generate-compiletime-defines.py
+ tools/pio/copy_files.py
+
+[extra_scripts_esp8266]
+extra_scripts = tools/pio/gzip-firmware.py
+ pre:tools/pio/remove_concat_cpp_files.py
+ pre:tools/pio/concat_cpp_files.py
+ ${extra_scripts_default.extra_scripts}
+
+
+[common]
+lib_archive = false
+lib_ldf_mode = chain
+lib_compat_mode = strict
+shared_libdeps_dir = lib
+framework = arduino
+upload_speed = 115200
+monitor_speed = 115200
+;targets = size, checkprogsize
+targets =
+src_filter = +<*> -<.git/> -<.svn/> - - - - -<*/Commands_tmp/> -<*/ControllerQueue_tmp/> -<*/DataStructs_tmp/> -<*/DataTypes_tmp/> -<*/ESPEasyCore_tmp/> -<*/Globals_tmp/> -<*/Helpers_tmp/> -<*/PluginStructs_tmp/> -<*/WebServer_tmp/>
+
+; Backwards compatibility: https://github.com/platformio/platformio-core/issues/4270
+;build_src_filter = +<*> -<.git/> -<.svn/> - - - - -<*/Commands/> -<*/ControllerQueue/> -<*/DataStructs/> -<*/DataTypes/> -<*/Globals/> -<*/Helpers/> -<*/PluginStructs/> -<*/WebServer/>
+
+
+[env]
+extends = common
diff --git a/platformio_core_defs.ini b/platformio_core_defs.ini
index 75e04c8086..2defe40116 100644
--- a/platformio_core_defs.ini
+++ b/platformio_core_defs.ini
@@ -1,232 +1,235 @@
-; *********************************************************************
-
-; **** Definition cheat sheet:
-; board_build.flash_mode in terms of performance: QIO > QOUT > DIO > DOUT
-; for lib_ldf_mode, see http://docs.platformio.org/en/latest/librarymanager/ldf.html;ldf
-
-; **** Frequently used build flags:
-; Use custom.h file to override default settings for ESPeasy: -D USE_CUSTOM_H
-; Set VCC mode to measure Vcc of ESP chip : -D FEATURE_ADC_VCC=1
-
-; Build Flags:
-; -DUSE_CONFIG_OVERRIDE
-; lwIP 1.4 (Default)
-; -DPIO_FRAMEWORK_ARDUINO_LWIP_HIGHER_BANDWIDTH
-; lwIP 2 - Low Memory
-; -DPIO_FRAMEWORK_ARDUINO_LWIP2_LOW_MEMORY
-; lwIP 2 - Higher Bandwitdh
-; -DPIO_FRAMEWORK_ARDUINO_LWIP2_HIGHER_BANDWIDTH
-; VTABLES in Flash (default)
-; -DVTABLES_IN_FLASH
-; VTABLES in Heap
-; -DVTABLES_IN_DRAM
-; VTABLES in IRAM
-; -DVTABLES_IN_IRAM
-; NO_EXTRA_4K_HEAP - this forces the default NONOS-SDK user's heap location
-; Default currently overlaps cont stack (Arduino) with sys stack (System)
-; to save up-to 4 kB of heap. (starting core_2.4.2)
-; ESP8266_DISABLE_EXTRA4K - Calls disable_extra4k_at_link_time() from setup
-; to force the linker keep user's stack in user ram.
-; CONT_STACKSIZE to set the 'cont' (Arduino) stack size. Default = 4096
-; -mtarget-align see: https://github.com/arendst/Sonoff-Tasmota/issues/3678#issuecomment-419712437
-
-[esp82xx_defaults]
-build_flags = -D NDEBUG
- -lstdc++ -lsupc++
- -mtarget-align
- -DPIO_FRAMEWORK_ARDUINO_LWIP2_LOW_MEMORY
- -DVTABLES_IN_FLASH
- -DPUYA_SUPPORT=1
- -DDISABLE_SC16IS752_SPI
- -DCRON_USE_LOCAL_TIME
- -fno-strict-aliasing
- -I$PROJECT_DIR/src/include
- -include "ESPEasy_config.h"
-
-lib_ignore = ESP32_ping
- ESP32WebServer
- ESP32HTTPUpdateServer
- ServoESP32
- IRremoteESP8266
- HeatpumpIR
- TinyWireM
- ESP8266SdFat
- SD(esp8266)
- SD
- SDFS
- LittleFS(esp8266)
- LittleFS
- ArduinoOTA
- ESP8266mDNS
- I2C AXP192 Power management
-; EspSoftwareSerial
-
-
-
-; Keep optimization flag to -O2
-; See: https://github.com/platformio/platform-espressif8266/issues/288
-; For "-fno-strict-aliasing"
-; See: https://github.com/esp8266/Arduino/issues/8261
-[esp82xx_2_7_x]
-build_flags = -DNDEBUG
- -mtarget-align
- -DVTABLES_IN_FLASH
- -fno-exceptions
- -lstdc++
- -DPIO_FRAMEWORK_ARDUINO_LWIP2_HIGHER_BANDWIDTH_LOW_FLASH
- -DPUYA_SUPPORT=1
- -DCORE_POST_2_5_0
- -DDISABLE_SC16IS752_SPI
- -DCRON_USE_LOCAL_TIME
- -fno-strict-aliasing
- -DLIBRARIES_NO_LOG=1
- -DNO_GLOBAL_I2S
- -I$PROJECT_DIR/src/include
- -include "ESPEasy_config.h"
- -O2
- -s
- -DBEARSSL_SSL_BASIC
- -DCORE_POST_2_6_0
- ; remove the 4-bytes alignment for PSTR()
- -DPSTR_ALIGN=1
- -Werror=return-type
-build_unflags = ${esp82xx_common.build_unflags}
-lib_ignore = ${esp82xx_defaults.lib_ignore}
- EspSoftwareSerial
-
-
-[esp82xx_3_0_x]
-build_flags = ${esp82xx_2_7_x.build_flags}
- -DCORE_POST_3_0_0
- -Wno-deprecated-declarations
-; -flto=auto
-; -Wl,-flto
-build_unflags = -DDEBUG_ESP_PORT
- -fexceptions
- -Wall
-; -fno-lto
-lib_ignore = ${esp82xx_defaults.lib_ignore}
-extra_scripts = pre:tools/pio/pre_custom_esp8266_toolchain.py
-
-
-
-; See for SDK flags: https://github.com/esp8266/Arduino/blob/master/tools/platformio-build.py
-
-[core_2_7_4]
-extends = esp82xx_2_7_x
-platform = espressif8266@2.6.3
-platform_packages =
- framework-arduinoespressif8266 @ https://github.com/esp8266/Arduino.git#2.7.4
-build_flags = ${esp82xx_2_7_x.build_flags}
- -DPIO_FRAMEWORK_ARDUINO_ESPRESSIF_SDK22x_190703
- -DUSES_LATEST_SOFTWARE_SERIAL_LIBRARY=0
- -Wno-deprecated-declarations
- -DLIBRARIES_NO_LOG=1
-lib_ignore = ${esp82xx_2_7_x.lib_ignore}
-build_unflags = ${esp82xx_2_7_x.build_unflags}
-extra_scripts = ${esp82xx_common.extra_scripts}
-
-
-[core_stage]
-extends = esp82xx_3_0_x
-platform = espressif8266@4.2.1
-platform_packages =
-build_flags = ${esp82xx_3_0_x.build_flags}
- -DPIO_FRAMEWORK_ARDUINO_ESPRESSIF_SDK3
- -DUSES_LATEST_SOFTWARE_SERIAL_LIBRARY=1
- -DLIBRARIES_NO_LOG=1
- -DPHASE_LOCKED_WAVEFORM
-build_unflags = ${esp82xx_3_0_x.build_unflags}
-lib_ignore = ${esp82xx_defaults.lib_ignore}
-extra_scripts = ${esp82xx_common.extra_scripts}
-
-
-
-; See: https://arduino-esp8266.readthedocs.io/en/latest/mmu.html
-[core_stage_2ndheap]
-extends = esp82xx_3_0_x
-platform = espressif8266@4.2.1
-platform_packages =
-build_flags = ${esp82xx_3_0_x.build_flags}
- -DPIO_FRAMEWORK_ARDUINO_ESPRESSIF_SDK3
- -DUSES_LATEST_SOFTWARE_SERIAL_LIBRARY=1
- -DLIBRARIES_NO_LOG=1
- -DPHASE_LOCKED_WAVEFORM
- -DPIO_FRAMEWORK_ARDUINO_MMU_CACHE16_IRAM48_SECHEAP_SHARED
- -DUSE_SECOND_HEAP
-build_unflags = ${esp82xx_3_0_x.build_unflags}
-lib_ignore = ${core_stage.lib_ignore}
-extra_scripts = ${esp82xx_common.extra_scripts}
-
-
-
-; Updated ESP-IDF to the latest stable 4.0.1
-; See: https://github.com/platformio/platform-espressif32/releases
-; IDF 4.4 = platform-espressif32 3.4.x = espressif/arduino-esp32 tag 2.0.4
-; Just for those who lost track of the extremely confusing numbering schema.
-; For MUSTFIX_CLIENT_TIMEOUT_IN_SECONDS See: https://github.com/espressif/arduino-esp32/pull/6676
-[core_esp32_IDF4_4__2_0_14]
-;platform = https://github.com/tasmota/platform-espressif32/releases/download/v2.0.4.1/platform-espressif32-2.0.4.1.zip
-
-; debug boot log enabled
-; See: https://github.com/letscontrolit/ESPEasy/pull/4200#issuecomment-1216929859
-;platform = https://github.com/Jason2866/platform-espressif32.git
-;platform_packages = framework-arduinoespressif32 @ https://github.com/Jason2866/esp32-arduino-lib-builder/releases/download/936/framework-arduinoespressif32-443_esp421-9ce849ce72.tar.gz
-
-; debug boot log disabled
-;platform = https://github.com/Jason2866/platform-espressif32.git
-;platform_packages = framework-arduinoespressif32 @ https://github.com/Jason2866/esp32-arduino-lib-builder/releases/download/938/framework-arduinoespressif32-443_esp421-10ab11e815.tar.gz
-
-;platform = https://github.com/tasmota/platform-espressif32/releases/download/v2.0.5.2/platform-espressif32-2.0.5.2.zip
-;platform = https://github.com/tasmota/platform-espressif32/releases/download/2022.12.2/platform-espressif32.zip
-;platform = https://github.com/tasmota/platform-espressif32/releases/download/2023.01.01/platform-espressif32.zip
-;platform_packages = framework-arduinoespressif32 @ https://github.com/Jason2866/esp32-arduino-lib-builder/releases/download/1212/framework-arduinoespressif32-release_v4.4-fb9a7685e1.zip
-;platform = https://github.com/tasmota/platform-espressif32/releases/download/2023.01.02/platform-espressif32.zip
-;platform_packages =
-;platform = https://github.com/tasmota/platform-espressif32/releases/download/2023.02.00/platform-espressif32.zip
-;platform_packages = framework-arduinoespressif32 @ https://github.com/Jason2866/esp32-arduino-lib-builder/releases/download/1243/framework-arduinoespressif32-lwip_timeout-ed6742e7f0.zip
-
-;platform = https://github.com/tasmota/platform-espressif32/releases/download/2023.05.03/platform-espressif32.zip
-;platform = https://github.com/tasmota/platform-espressif32/releases/download/2023.06.04/platform-espressif32.zip
-platform = https://github.com/tasmota/platform-espressif32/releases/download/2023.10.03/platform-espressif32.zip
-platform_packages = framework-arduinoespressif32 @ https://github.com/Jason2866/esp32-arduino-lib-builder/releases/download/1645/framework-arduinoespressif32-release_v4.4_spiffs-e3fc63b439.zip
-build_flags = -DESP32_STAGE
- -DESP_IDF_VERSION_MAJOR=4
- -DMUSTFIX_CLIENT_TIMEOUT_IN_SECONDS
- -DLIBRARIES_NO_LOG=1
- -DDISABLE_SC16IS752_SPI
- -DCONFIG_PM_ENABLE
- -DCONFIG_FREERTOS_USE_TICKLESS_IDLE=1
- -DCONFIG_FREERTOS_IDLE_TIME_BEFORE_SLEEP=3
- -DNEOPIXEL_ESP32_RMT_DEFAULT
- -DCRON_USE_LOCAL_TIME
- -I$PROJECT_DIR/src/include
- -include "sdkconfig.h"
- -include "ESPEasy_config.h"
- -include "esp32x_fixes.h"
-lib_ignore =
-
-; ESP_IDF 5.1
-[core_esp32_IDF5_1__3_0_0]
-;platform = https://github.com/tasmota/platform-espressif32/releases/download/2024.04.11/platform-espressif32.zip
-;platform_packages = framework-arduinoespressif32 @ https://github.com/Jason2866/esp32-arduino-lib-builder/releases/download/2286/framework-arduinoespressif32-all-release_v5.1-11140aa.zip
-;platform = https://github.com/tasmota/platform-espressif32/releases/download/2024.04.14/platform-espressif32.zip
-;platform_packages = framework-arduinoespressif32 @ https://github.com/Jason2866/esp32-arduino-lib-builder/releases/download/2386/framework-arduinoespressif32-all-release_v5.1-324fdc1.zip
-platform = https://github.com/tasmota/platform-espressif32/releases/download/2024.05.13/platform-espressif32.zip
-platform_packages =
-build_flags = -DESP32_STAGE
- -DESP_IDF_VERSION_MAJOR=5
- -DLIBRARIES_NO_LOG=1
- -DDISABLE_SC16IS752_SPI
- -DCONFIG_PM_ENABLE
-; -DCONFIG_LWIP_L2_TO_L3_COPY
-; -DETH_SPI_SUPPORTS_NO_IRQ=1
- -DCONFIG_FREERTOS_USE_TICKLESS_IDLE=1
- -DCONFIG_FREERTOS_IDLE_TIME_BEFORE_SLEEP=3
- -DNEOPIXEL_ESP32_RMT_DEFAULT
- -DCRON_USE_LOCAL_TIME
- -I$PROJECT_DIR/src/include
- -include "sdkconfig.h"
- -include "ESPEasy_config.h"
- -include "esp32x_fixes.h"
-lib_ignore =
+; *********************************************************************
+
+; **** Definition cheat sheet:
+; board_build.flash_mode in terms of performance: QIO > QOUT > DIO > DOUT
+; for lib_ldf_mode, see http://docs.platformio.org/en/latest/librarymanager/ldf.html;ldf
+
+; **** Frequently used build flags:
+; Use custom.h file to override default settings for ESPeasy: -D USE_CUSTOM_H
+; Set VCC mode to measure Vcc of ESP chip : -D FEATURE_ADC_VCC=1
+
+; Build Flags:
+; -DUSE_CONFIG_OVERRIDE
+; lwIP 1.4 (Default)
+; -DPIO_FRAMEWORK_ARDUINO_LWIP_HIGHER_BANDWIDTH
+; lwIP 2 - Low Memory
+; -DPIO_FRAMEWORK_ARDUINO_LWIP2_LOW_MEMORY
+; lwIP 2 - Higher Bandwitdh
+; -DPIO_FRAMEWORK_ARDUINO_LWIP2_HIGHER_BANDWIDTH
+; VTABLES in Flash (default)
+; -DVTABLES_IN_FLASH
+; VTABLES in Heap
+; -DVTABLES_IN_DRAM
+; VTABLES in IRAM
+; -DVTABLES_IN_IRAM
+; NO_EXTRA_4K_HEAP - this forces the default NONOS-SDK user's heap location
+; Default currently overlaps cont stack (Arduino) with sys stack (System)
+; to save up-to 4 kB of heap. (starting core_2.4.2)
+; ESP8266_DISABLE_EXTRA4K - Calls disable_extra4k_at_link_time() from setup
+; to force the linker keep user's stack in user ram.
+; CONT_STACKSIZE to set the 'cont' (Arduino) stack size. Default = 4096
+; -mtarget-align see: https://github.com/arendst/Sonoff-Tasmota/issues/3678#issuecomment-419712437
+
+[esp82xx_defaults]
+build_flags = -D NDEBUG
+ -lstdc++ -lsupc++
+ -mtarget-align
+ -DPIO_FRAMEWORK_ARDUINO_LWIP2_LOW_MEMORY
+ -DVTABLES_IN_FLASH
+ -DPUYA_SUPPORT=1
+ -DDISABLE_SC16IS752_SPI
+ -DCRON_USE_LOCAL_TIME
+ -fno-strict-aliasing
+ -I$PROJECT_DIR/src/include
+ -include "ESPEasy_config.h"
+
+lib_ignore = ESP32_ping
+ ESP32WebServer
+ ESP32HTTPUpdateServer
+ ServoESP32
+ IRremoteESP8266
+ HeatpumpIR
+ TinyWireM
+ ESP8266SdFat
+ SD(esp8266)
+ SD
+ SDFS
+ LittleFS(esp8266)
+ LittleFS
+ ArduinoOTA
+ ESP8266mDNS
+ I2C AXP192 Power management
+; EspSoftwareSerial
+
+
+
+; Keep optimization flag to -O2
+; See: https://github.com/platformio/platform-espressif8266/issues/288
+; For "-fno-strict-aliasing"
+; See: https://github.com/esp8266/Arduino/issues/8261
+[esp82xx_2_7_x]
+build_flags = -DNDEBUG
+ -mtarget-align
+ -DVTABLES_IN_FLASH
+ -fno-exceptions
+ -lstdc++
+ -DPIO_FRAMEWORK_ARDUINO_LWIP2_HIGHER_BANDWIDTH_LOW_FLASH
+ -DPUYA_SUPPORT=1
+ -DCORE_POST_2_5_0
+ -DDISABLE_SC16IS752_SPI
+ -DCRON_USE_LOCAL_TIME
+ -fno-strict-aliasing
+ -DLIBRARIES_NO_LOG=1
+ -DNO_GLOBAL_I2S
+ -I$PROJECT_DIR/src/include
+ -include "ESPEasy_config.h"
+ -O2
+ -s
+ -DBEARSSL_SSL_BASIC
+ -DCORE_POST_2_6_0
+ ; remove the 4-bytes alignment for PSTR()
+ -DPSTR_ALIGN=1
+ -Werror=return-type
+build_unflags = ${esp82xx_common.build_unflags}
+lib_ignore = ${esp82xx_defaults.lib_ignore}
+ EspSoftwareSerial
+
+
+[esp82xx_3_0_x]
+build_flags = ${esp82xx_2_7_x.build_flags}
+ -DCORE_POST_3_0_0
+ -Wno-deprecated-declarations
+; -flto=auto
+; -Wl,-flto
+build_unflags = -DDEBUG_ESP_PORT
+ -fexceptions
+ -Wall
+; -fno-lto
+lib_ignore = ${esp82xx_defaults.lib_ignore}
+extra_scripts = pre:tools/pio/pre_custom_esp8266_toolchain.py
+
+
+
+; See for SDK flags: https://github.com/esp8266/Arduino/blob/master/tools/platformio-build.py
+
+[core_2_7_4]
+extends = esp82xx_2_7_x
+platform = espressif8266@2.6.3
+platform_packages =
+ framework-arduinoespressif8266 @ https://github.com/esp8266/Arduino.git#2.7.4
+build_flags = ${esp82xx_2_7_x.build_flags}
+ -DPIO_FRAMEWORK_ARDUINO_ESPRESSIF_SDK22x_190703
+ -DUSES_LATEST_SOFTWARE_SERIAL_LIBRARY=0
+ -Wno-deprecated-declarations
+ -DLIBRARIES_NO_LOG=1
+lib_ignore = ${esp82xx_2_7_x.lib_ignore}
+build_unflags = ${esp82xx_2_7_x.build_unflags}
+extra_scripts = ${esp82xx_common.extra_scripts}
+
+
+[core_stage]
+extends = esp82xx_3_0_x
+platform = espressif8266@4.2.1
+platform_packages =
+build_flags = ${esp82xx_3_0_x.build_flags}
+ -DPIO_FRAMEWORK_ARDUINO_ESPRESSIF_SDK3
+ -DUSES_LATEST_SOFTWARE_SERIAL_LIBRARY=1
+ -DLIBRARIES_NO_LOG=1
+ -DPHASE_LOCKED_WAVEFORM
+build_unflags = ${esp82xx_3_0_x.build_unflags}
+lib_ignore = ${esp82xx_defaults.lib_ignore}
+extra_scripts = ${esp82xx_common.extra_scripts}
+
+
+
+; See: https://arduino-esp8266.readthedocs.io/en/latest/mmu.html
+[core_stage_2ndheap]
+extends = esp82xx_3_0_x
+platform = espressif8266@4.2.1
+platform_packages =
+build_flags = ${esp82xx_3_0_x.build_flags}
+ -DPIO_FRAMEWORK_ARDUINO_ESPRESSIF_SDK3
+ -DUSES_LATEST_SOFTWARE_SERIAL_LIBRARY=1
+ -DLIBRARIES_NO_LOG=1
+ -DPHASE_LOCKED_WAVEFORM
+ -DPIO_FRAMEWORK_ARDUINO_MMU_CACHE16_IRAM48_SECHEAP_SHARED
+ -DUSE_SECOND_HEAP
+build_unflags = ${esp82xx_3_0_x.build_unflags}
+lib_ignore = ${core_stage.lib_ignore}
+extra_scripts = ${esp82xx_common.extra_scripts}
+
+
+
+; Updated ESP-IDF to the latest stable 4.0.1
+; See: https://github.com/platformio/platform-espressif32/releases
+; IDF 4.4 = platform-espressif32 3.4.x = espressif/arduino-esp32 tag 2.0.4
+; Just for those who lost track of the extremely confusing numbering schema.
+; For MUSTFIX_CLIENT_TIMEOUT_IN_SECONDS See: https://github.com/espressif/arduino-esp32/pull/6676
+[core_esp32_IDF4_4__2_0_14]
+;platform = https://github.com/tasmota/platform-espressif32/releases/download/v2.0.4.1/platform-espressif32-2.0.4.1.zip
+
+; debug boot log enabled
+; See: https://github.com/letscontrolit/ESPEasy/pull/4200#issuecomment-1216929859
+;platform = https://github.com/Jason2866/platform-espressif32.git
+;platform_packages = framework-arduinoespressif32 @ https://github.com/Jason2866/esp32-arduino-lib-builder/releases/download/936/framework-arduinoespressif32-443_esp421-9ce849ce72.tar.gz
+
+; debug boot log disabled
+;platform = https://github.com/Jason2866/platform-espressif32.git
+;platform_packages = framework-arduinoespressif32 @ https://github.com/Jason2866/esp32-arduino-lib-builder/releases/download/938/framework-arduinoespressif32-443_esp421-10ab11e815.tar.gz
+
+;platform = https://github.com/tasmota/platform-espressif32/releases/download/v2.0.5.2/platform-espressif32-2.0.5.2.zip
+;platform = https://github.com/tasmota/platform-espressif32/releases/download/2022.12.2/platform-espressif32.zip
+;platform = https://github.com/tasmota/platform-espressif32/releases/download/2023.01.01/platform-espressif32.zip
+;platform_packages = framework-arduinoespressif32 @ https://github.com/Jason2866/esp32-arduino-lib-builder/releases/download/1212/framework-arduinoespressif32-release_v4.4-fb9a7685e1.zip
+;platform = https://github.com/tasmota/platform-espressif32/releases/download/2023.01.02/platform-espressif32.zip
+;platform_packages =
+;platform = https://github.com/tasmota/platform-espressif32/releases/download/2023.02.00/platform-espressif32.zip
+;platform_packages = framework-arduinoespressif32 @ https://github.com/Jason2866/esp32-arduino-lib-builder/releases/download/1243/framework-arduinoespressif32-lwip_timeout-ed6742e7f0.zip
+
+;platform = https://github.com/tasmota/platform-espressif32/releases/download/2023.05.03/platform-espressif32.zip
+;platform = https://github.com/tasmota/platform-espressif32/releases/download/2023.06.04/platform-espressif32.zip
+platform = https://github.com/tasmota/platform-espressif32/releases/download/2023.10.03/platform-espressif32.zip
+platform_packages = framework-arduinoespressif32 @ https://github.com/Jason2866/esp32-arduino-lib-builder/releases/download/1645/framework-arduinoespressif32-release_v4.4_spiffs-e3fc63b439.zip
+build_flags = -DESP32_STAGE
+ -DESP_IDF_VERSION_MAJOR=4
+ -DMUSTFIX_CLIENT_TIMEOUT_IN_SECONDS
+ -DLIBRARIES_NO_LOG=1
+ -DDISABLE_SC16IS752_SPI
+ -DCONFIG_PM_ENABLE
+ -DCONFIG_FREERTOS_USE_TICKLESS_IDLE=1
+ -DCONFIG_FREERTOS_IDLE_TIME_BEFORE_SLEEP=3
+ -DNEOPIXEL_ESP32_RMT_DEFAULT
+ -DCRON_USE_LOCAL_TIME
+ -I$PROJECT_DIR/src/include
+ -include "sdkconfig.h"
+ -include "ESPEasy_config.h"
+ -include "esp32x_fixes.h"
+ -Wnull-dereference
+lib_ignore =
+
+; ESP_IDF 5.1
+[core_esp32_IDF5_1__3_0_0]
+;platform = https://github.com/tasmota/platform-espressif32/releases/download/2024.04.11/platform-espressif32.zip
+;platform_packages = framework-arduinoespressif32 @ https://github.com/Jason2866/esp32-arduino-lib-builder/releases/download/2286/framework-arduinoespressif32-all-release_v5.1-11140aa.zip
+;platform = https://github.com/tasmota/platform-espressif32/releases/download/2024.04.14/platform-espressif32.zip
+;platform_packages = framework-arduinoespressif32 @ https://github.com/Jason2866/esp32-arduino-lib-builder/releases/download/2386/framework-arduinoespressif32-all-release_v5.1-324fdc1.zip
+platform = https://github.com/tasmota/platform-espressif32/releases/download/2024.06.11/platform-espressif32.zip
+platform_packages = framework-arduinoespressif32 @ https://github.com/Jason2866/esp32-arduino-lib-builder/releases/download/2543/framework-arduinoespressif32-all-release_v5.1-bd03553.zip
+build_flags = -DESP32_STAGE
+ -DESP_IDF_VERSION_MAJOR=5
+ -DLIBRARIES_NO_LOG=1
+ -DDISABLE_SC16IS752_SPI
+ -DCONFIG_PM_ENABLE
+; -DCONFIG_LWIP_L2_TO_L3_COPY
+; -DETH_SPI_SUPPORTS_NO_IRQ=1
+ -DCONFIG_FREERTOS_USE_TICKLESS_IDLE=1
+ -DCONFIG_FREERTOS_IDLE_TIME_BEFORE_SLEEP=3
+ -DNEOPIXEL_ESP32_RMT_DEFAULT
+ -DCRON_USE_LOCAL_TIME
+ -I$PROJECT_DIR/src/include
+ -include "sdkconfig.h"
+ -include "ESPEasy_config.h"
+ -include "esp32x_fixes.h"
+ -Wnull-dereference
+lib_ignore =
+
diff --git a/platformio_esp32_envs.ini b/platformio_esp32_envs.ini
index 6930f5a3ff..105a56172f 100644
--- a/platformio_esp32_envs.ini
+++ b/platformio_esp32_envs.ini
@@ -1,572 +1,572 @@
-;;; ESP32 test build ********************************************************************;
-; Status of the ESP32 support is still considered "beta" ;
-; Most plugins work just fine on ESP32. ;
-; Especially some plugins using serial may not run very well (GPS does run fine). ;
-; ***************************************************************************************;
-
-
-[esp32_base]
-extends = common, core_esp32_IDF4_4__2_0_14
-upload_speed = 460800
-upload_before_reset = default_reset
-upload_after_reset = hard_reset
-extra_scripts = post:tools/pio/post_esp32.py
- ${extra_scripts_default.extra_scripts}
-; you can disable debug linker flag to reduce binary size (comment out line below), but the backtraces will become less readable
-; tools/pio/extra_linker_flags.py
-; fix the platform package to use gcc-ar and gcc-ranlib to enable lto linker plugin
-; more detail: https://embeddedartistry.com/blog/2020/04/13/prefer-gcc-ar-to-ar-in-your-buildsystems/
-; pre:tools/pio/apply_patches.py
-build_unflags = -Wall
-; -fno-lto
-build_flags = ${core_esp32_IDF4_4__2_0_14.build_flags}
-; ${mqtt_flags.build_flags}
- -DMQTT_MAX_PACKET_SIZE=2048
- -DCONFIG_FREERTOS_ASSERT_DISABLE
- -DCONFIG_LWIP_ESP_GRATUITOUS_ARP
- -fno-strict-aliasing
-; -flto
- -Wswitch
- -DCORE_DEBUG_LEVEL=ARDUHAL_LOG_LEVEL_NONE
-monitor_filters = esp32_exception_decoder
-lib_ignore =
- ${core_esp32_IDF4_4__2_0_14.lib_ignore}
-
-
-[esp32_base_idf5]
-extends = common, core_esp32_IDF5_1__3_0_0
-upload_speed = 460800
-upload_before_reset = default_reset
-upload_after_reset = hard_reset
-extra_scripts = post:tools/pio/post_esp32.py
- ${extra_scripts_default.extra_scripts}
-; you can disable debug linker flag to reduce binary size (comment out line below), but the backtraces will become less readable
-; tools/pio/extra_linker_flags.py
-; fix the platform package to use gcc-ar and gcc-ranlib to enable lto linker plugin
-; more detail: https://embeddedartistry.com/blog/2020/04/13/prefer-gcc-ar-to-ar-in-your-buildsystems/
-; pre:tools/pio/apply_patches.py
-
-; When using LTO, make sure NOT to use -mtext-section-literals
-; -mtext-section-literals may be required when building large builds
-; However LTO cannot optimize builds with text section literals and thus will result in quite a lot larger builds (80k - 140k larger)
-build_unflags = -Wall
- -fno-lto
-build_flags = ${core_esp32_IDF5_1__3_0_0.build_flags}
-; ${mqtt_flags.build_flags}
- -DMQTT_MAX_PACKET_SIZE=2048
- -DCONFIG_FREERTOS_ASSERT_DISABLE
- -DCONFIG_LWIP_ESP_GRATUITOUS_ARP
- -fno-strict-aliasing
- -flto=auto
- -Wswitch
- -DCORE_DEBUG_LEVEL=ARDUHAL_LOG_LEVEL_NONE
-; -DCORE_DEBUG_LEVEL=ARDUHAL_LOG_LEVEL_INFO
-; -DCORE_DEBUG_LEVEL=ARDUHAL_LOG_LEVEL_VERBOSE
- -DLWIP_IPV6=1
-monitor_filters = esp32_exception_decoder
-lib_ignore =
- ${core_esp32_IDF5_1__3_0_0.lib_ignore}
-
-
-; -flto cannot be used for ESP32 C3!
-; See: https://github.com/letscontrolit/ESPEasy/pull/3845#issuecomment-1014857366
-; TD-er: 2022-01-20: Disabled for now as it also resulted in obscure linker errors on ESP32-S2 and ESP32 running custom builds.
-;build_flags = ${esp32_base.build_flags}
-; -flto
-;build_unflags = ${esp32_base.build_unflags}
-; -fexceptions
-; -fno-lto
-
-
-[esp32_always]
-lib_ignore = ESP8266Ping
- ESP8266HTTPUpdateServer
- ESP8266WiFi
- ESP8266WebServer
- ESP8266mDNS
- ESPEasy_ESP8266Ping
- RABurton ESP8266 Mutex
- TinyWireM
- LittleFS_esp32
- Adafruit NeoPixel
- ${esp32_base.lib_ignore}
-
-
-[esp32_common]
-extends = esp32_base
-lib_ignore = ${esp32_always.lib_ignore}
- ESP32_ping
- ${no_ir.lib_ignore}
- ESP32 BLE Arduino
-build_flags = ${esp32_base.build_flags}
- -DESP32_CLASSIC
-extra_scripts = ${esp32_base.extra_scripts}
-build_unflags = ${esp32_base.build_unflags}
- -fexceptions
-
-[esp32_common_LittleFS]
-extends = esp32_base_idf5
-build_flags = ${esp32_base_idf5.build_flags}
-; -mtext-section-literals
- -DESP32_CLASSIC
- -DUSE_LITTLEFS
-build_unflags = ${esp32_base_idf5.build_unflags}
-extra_scripts = ${esp32_common.extra_scripts}
-board_build.filesystem = littlefs
-lib_ignore = ${esp32_always.lib_ignore}
- ESP32_ping
- ESP32 BLE Arduino
- ${core_esp32_IDF5_1__3_0_0.lib_ignore}
-
-
-[esp32_IRExt]
-extends = esp32_common
-lib_ignore = ${esp32_always.lib_ignore}
- ESP32_ping
-build_flags = ${esp32_common.build_flags}
- -DFEATURE_ARDUINO_OTA=1
- -DPLUGIN_BUILD_NORMAL_IRext
- -DCOLLECTION_USE_RTTTL
-extra_scripts = ${esp32_common.extra_scripts}
- pre:tools/pio/ir_build_check.py
-
-
-[esp32_custom_base]
-extends = esp32_common
-build_flags = ${esp32_common.build_flags}
- -DPLUGIN_BUILD_CUSTOM
-extra_scripts = ${esp32_common.extra_scripts}
- pre:tools/pio/pre_custom_esp32.py
-
-[esp32_custom_base_LittleFS]
-extends = esp32_common_LittleFS
-build_flags = ${esp32_common_LittleFS.build_flags}
- -DPLUGIN_BUILD_CUSTOM
-extra_scripts = ${esp32_common_LittleFS.extra_scripts}
- pre:tools/pio/pre_custom_esp32.py
-
-
-[env:custom_ESP32_4M316k]
-extends = esp32_custom_base
-board = esp32_4M
-
-; [env:custom_ESP32_4M316k_LittleFS]
-; extends = esp32_custom_base_LittleFS
-; board = esp32_4M
-
-[env:custom_ESP32_16M8M_LittleFS_ETH]
-extends = esp32_custom_base_LittleFS
-board = esp32_16M8M
-board_upload.flash_size = 16MB
-build_flags = ${esp32_custom_base_LittleFS.build_flags}
- -DFEATURE_ETHERNET=1
-
-[env:custom_IR_ESP32_4M316k]
-extends = esp32_common
-board = esp32_4M
-build_flags = ${esp32_common.build_flags}
- -DPLUGIN_BUILD_CUSTOM
- -DPLUGIN_BUILD_IR
-lib_ignore = ${esp32_always.lib_ignore}
- ESP32_ping
-extra_scripts = ${esp32_common.extra_scripts}
- pre:tools/pio/pre_custom_esp32_IR.py
- pre:tools/pio/ir_build_check.py
-
-[env:custom_ESP32_4M2M_NO_OTA_LittleFS_ETH]
-extends = esp32_custom_base_LittleFS
-board = esp32_4M2M
-build_flags = ${esp32_custom_base_LittleFS.build_flags}
- -DNO_HTTP_UPDATER
- -DFEATURE_ETHERNET=1
-
-
-[env:normal_ESP32_4M316k]
-extends = esp32_common
-board = esp32_4M
-lib_ignore = ${esp32_common.lib_ignore}
- ${no_ir.lib_ignore}
-
-; [env:normal_ESP32_4M316k_LittleFS]
-; extends = esp32_common_LittleFS
-; board = esp32_4M
-; lib_ignore = ${esp32_common_LittleFS.lib_ignore}
-; ${no_ir.lib_ignore}
-
-[env:collection_A_ESP32_4M316k]
-extends = esp32_common
-board = esp32_4M
-build_flags = ${esp32_common.build_flags}
- -DFEATURE_ARDUINO_OTA=1
- -DPLUGIN_SET_COLLECTION_ESP32
- -DCOLLECTION_USE_RTTTL
-
-[env:collection_B_ESP32_4M316k]
-extends = esp32_common
-board = esp32_4M
-build_flags = ${esp32_common.build_flags}
- -DFEATURE_ARDUINO_OTA=1
- -DPLUGIN_SET_COLLECTION_B_ESP32
- -DCOLLECTION_USE_RTTTL
-
-[env:collection_C_ESP32_4M316k]
-extends = esp32_common
-board = esp32_4M
-build_flags = ${esp32_common.build_flags}
- -DFEATURE_ARDUINO_OTA=1
- -DPLUGIN_SET_COLLECTION_C_ESP32
- -DCOLLECTION_USE_RTTTL
-
-[env:collection_D_ESP32_4M316k]
-extends = esp32_common
-board = esp32_4M
-build_flags = ${esp32_common.build_flags}
- -DFEATURE_ARDUINO_OTA=1
- -DPLUGIN_SET_COLLECTION_D_ESP32
- -DCOLLECTION_USE_RTTTL
-
-[env:collection_E_ESP32_4M316k]
-extends = esp32_common
-board = esp32_4M
-build_flags = ${esp32_common.build_flags}
- -DFEATURE_ARDUINO_OTA=1
- -DPLUGIN_SET_COLLECTION_E_ESP32
- -DCOLLECTION_USE_RTTTL
-
-[env:collection_F_ESP32_4M316k]
-extends = esp32_common
-board = esp32_4M
-build_flags = ${esp32_common.build_flags}
- -DFEATURE_ARDUINO_OTA=1
- -DPLUGIN_SET_COLLECTION_F_ESP32
- -DCOLLECTION_USE_RTTTL
-
-[env:collection_G_ESP32_4M316k]
-extends = esp32_common
-board = esp32_4M
-build_flags = ${esp32_common.build_flags}
- -DFEATURE_ARDUINO_OTA=1
- -DPLUGIN_SET_COLLECTION_G_ESP32
- -DCOLLECTION_USE_RTTTL
-
-
-[env:collection_A_ESP32_IRExt_4M316k]
-extends = esp32_IRExt
-board = esp32_4M
-build_flags = ${esp32_IRExt.build_flags}
- -DPLUGIN_SET_COLLECTION_ESP32
-
-[env:collection_B_ESP32_IRExt_4M316k]
-extends = esp32_IRExt
-board = esp32_4M
-build_flags = ${esp32_IRExt.build_flags}
- -DPLUGIN_SET_COLLECTION_B_ESP32
-
-[env:collection_C_ESP32_IRExt_4M316k]
-extends = esp32_IRExt
-board = esp32_4M
-build_flags = ${esp32_IRExt.build_flags}
- -DPLUGIN_SET_COLLECTION_C_ESP32
-
-[env:collection_D_ESP32_IRExt_4M316k]
-extends = esp32_IRExt
-board = esp32_4M
-build_flags = ${esp32_IRExt.build_flags}
- -DPLUGIN_SET_COLLECTION_D_ESP32
-
-[env:collection_E_ESP32_IRExt_4M316k]
-extends = esp32_IRExt
-board = esp32_4M
-build_flags = ${esp32_IRExt.build_flags}
- -DPLUGIN_SET_COLLECTION_E_ESP32
-
-[env:collection_F_ESP32_IRExt_4M316k]
-extends = esp32_IRExt
-board = esp32_4M
-build_flags = ${esp32_IRExt.build_flags}
- -DPLUGIN_SET_COLLECTION_F_ESP32
-
-[env:collection_G_ESP32_IRExt_4M316k]
-extends = esp32_IRExt
-board = esp32_4M
-build_flags = ${esp32_IRExt.build_flags}
- -DPLUGIN_SET_COLLECTION_G_ESP32
-
-[env:energy_ESP32_4M316k]
-extends = esp32_common
-board = esp32_4M
-build_flags = ${esp32_common.build_flags}
- -DFEATURE_ARDUINO_OTA=1
- -DPLUGIN_ENERGY_COLLECTION
-
-[env:display_ESP32_4M316k]
-extends = esp32_common
-board = esp32_4M
-build_flags = ${esp32_common.build_flags}
- -DFEATURE_ARDUINO_OTA=1
- -DPLUGIN_DISPLAY_COLLECTION
-
-[env:climate_ESP32_4M316k]
-extends = esp32_common
-board = esp32_4M
-build_flags = ${esp32_common.build_flags}
- -DFEATURE_ARDUINO_OTA=1
- -DPLUGIN_CLIMATE_COLLECTION
-
-[env:neopixel_ESP32_4M316k]
-extends = esp32_common
-board = esp32_4M
-build_flags = ${esp32_common.build_flags}
- -DFEATURE_ARDUINO_OTA=1
- -DFEATURE_SD=1
- -D PLUGIN_NEOPIXEL_COLLECTION
-
-
-[env:custom_ESP32_4M316k_ETH]
-extends = env:custom_ESP32_4M316k
-build_flags = ${env:custom_ESP32_4M316k.build_flags}
- -DFEATURE_ETHERNET=1
-
-[env:custom_ESP32_4M316k_LittleFS_ETH]
-extends = esp32_custom_base_LittleFS
-board = esp32_4M
-build_flags = ${esp32_custom_base_LittleFS.build_flags}
- -DFEATURE_ETHERNET=1
-
-
-[env:custom_IR_ESP32_4M316k_ETH]
-extends = env:custom_IR_ESP32_4M316k
-build_flags = ${env:custom_IR_ESP32_4M316k.build_flags}
- -DFEATURE_ETHERNET=1
-extra_scripts = ${env:custom_IR_ESP32_4M316k.extra_scripts}
-
-[env:custom_IR_ESP32_16M8M_LittleFS_ETH]
-extends = esp32_common_LittleFS
-board = esp32_16M8M
-board_upload.flash_size = 16MB
-build_flags = ${esp32_common_LittleFS.build_flags}
- -DPLUGIN_BUILD_CUSTOM
- -DPLUGIN_BUILD_IR
- -DFEATURE_ETHERNET=1
-lib_ignore = ${esp32_always.lib_ignore}
- ESP32_ping
- ${esp32_common_LittleFS.lib_ignore}
-extra_scripts = ${esp32_common.extra_scripts}
- pre:tools/pio/pre_custom_esp32_IR.py
- pre:tools/pio/ir_build_check.py
-
-[env:normal_ESP32_4M316k_ETH]
-extends = env:normal_ESP32_4M316k
-build_flags = ${env:normal_ESP32_4M316k.build_flags}
- -DFEATURE_ETHERNET=1
-
-
-[env:normal_ESP32_4M316k_LittleFS_ETH]
-extends = esp32_common_LittleFS
-board = esp32_4M
-lib_ignore = ${esp32_always.lib_ignore}
- ESP32_ping
- ${esp32_common_LittleFS.lib_ignore}
- ${no_ir.lib_ignore}
-build_flags = ${esp32_common_LittleFS.build_flags}
- -DFEATURE_ETHERNET=1
-
-
-[env:normal_ESP32_IRExt_4M316k_ETH]
-extends = esp32_IRExt
-board = esp32_4M
-build_flags = ${esp32_IRExt.build_flags}
- -DFEATURE_ETHERNET=1
-
-[env:collection_A_ESP32_4M316k_ETH]
-extends = env:collection_A_ESP32_4M316k
-build_flags = ${env:collection_A_ESP32_4M316k.build_flags}
- -DFEATURE_ETHERNET=1
- -DCOLLECTION_USE_RTTTL
-
-[env:collection_B_ESP32_4M316k_ETH]
-extends = env:collection_B_ESP32_4M316k
-build_flags = ${env:collection_B_ESP32_4M316k.build_flags}
- -DFEATURE_ETHERNET=1
- -DCOLLECTION_USE_RTTTL
-
-[env:collection_C_ESP32_4M316k_ETH]
-extends = env:collection_C_ESP32_4M316k
-build_flags = ${env:collection_C_ESP32_4M316k.build_flags}
- -DFEATURE_ETHERNET=1
- -DCOLLECTION_USE_RTTTL
-
-[env:collection_D_ESP32_4M316k_ETH]
-extends = env:collection_D_ESP32_4M316k
-build_flags = ${env:collection_D_ESP32_4M316k.build_flags}
- -DFEATURE_ETHERNET=1
- -DCOLLECTION_USE_RTTTL
-
-[env:collection_E_ESP32_4M316k_ETH]
-extends = env:collection_E_ESP32_4M316k
-build_flags = ${env:collection_E_ESP32_4M316k.build_flags}
- -DFEATURE_ETHERNET=1
- -DCOLLECTION_USE_RTTTL
-
-[env:collection_F_ESP32_4M316k_ETH]
-extends = env:collection_F_ESP32_4M316k
-build_flags = ${env:collection_F_ESP32_4M316k.build_flags}
- -DFEATURE_ETHERNET=1
- -DCOLLECTION_USE_RTTTL
-
-[env:collection_G_ESP32_4M316k_ETH]
-extends = env:collection_G_ESP32_4M316k
-build_flags = ${env:collection_G_ESP32_4M316k.build_flags}
- -DFEATURE_ETHERNET=1
- -DCOLLECTION_USE_RTTTL
-
-[env:energy_ESP32_4M316k_ETH]
-extends = env:energy_ESP32_4M316k
-build_flags = ${env:energy_ESP32_4M316k.build_flags}
- -DFEATURE_ETHERNET=1
-
-[env:display_ESP32_4M316k_ETH]
-extends = env:display_ESP32_4M316k
-build_flags = ${env:display_ESP32_4M316k.build_flags}
- -DFEATURE_ETHERNET=1
-
-[env:climate_ESP32_4M316k_ETH]
-extends = env:climate_ESP32_4M316k
-build_flags = ${env:climate_ESP32_4M316k.build_flags}
- -DFEATURE_ETHERNET=1
-
-[env:neopixel_ESP32_4M316k_ETH]
-extends = env:neopixel_ESP32_4M316k
-build_flags = ${env:neopixel_ESP32_4M316k.build_flags}
- -DFEATURE_ETHERNET=1
-
-; [env:collection_A_ESP32_IRExt_4M316k_ETH]
-; extends = esp32_IRExt
-; board = esp32_4M
-; build_flags = ${esp32_IRExt.build_flags}
-; -DPLUGIN_SET_COLLECTION_ESP32
-; -DFEATURE_ETHERNET=1
-
-; [env:collection_B_ESP32_IRExt_4M316k_ETH]
-; extends = esp32_IRExt
-; board = esp32_4M
-; build_flags = ${esp32_IRExt.build_flags}
-; -DPLUGIN_SET_COLLECTION_B_ESP32
-; -DFEATURE_ETHERNET=1
-
-; [env:collection_C_ESP32_IRExt_4M316k_ETH]
-; extends = esp32_IRExt
-; board = esp32_4M
-; build_flags = ${esp32_IRExt.build_flags}
-; -DPLUGIN_SET_COLLECTION_C_ESP32
-; -DFEATURE_ETHERNET=1
-
-; [env:collection_D_ESP32_IRExt_4M316k_ETH]
-; extends = esp32_IRExt
-; board = esp32_4M
-; build_flags = ${esp32_IRExt.build_flags}
-; -DPLUGIN_SET_COLLECTION_D_ESP32
-; -DFEATURE_ETHERNET=1
-
-; [env:collection_E_ESP32_IRExt_4M316k_ETH]
-; extends = esp32_IRExt
-; board = esp32_4M
-; build_flags = ${esp32_IRExt.build_flags}
-; -DPLUGIN_SET_COLLECTION_E_ESP32
-; -DFEATURE_ETHERNET=1
-
-; [env:collection_F_ESP32_IRExt_4M316k_ETH]
-; extends = esp32_IRExt
-; board = esp32_4M
-; build_flags = ${esp32_IRExt.build_flags}
-; -DPLUGIN_SET_COLLECTION_F_ESP32
-; -DFEATURE_ETHERNET=1
-
-; [env:collection_G_ESP32_IRExt_4M316k_ETH]
-; extends = esp32_IRExt
-; board = esp32_4M
-; build_flags = ${esp32_IRExt.build_flags}
-; -DPLUGIN_SET_COLLECTION_G_ESP32
-; -DFEATURE_ETHERNET=1
-
-; [env:energy_ESP32_IRExt_4M316k_ETH]
-; extends = esp32_IRExt
-; board = esp32_4M
-; build_flags = ${esp32_IRExt.build_flags}
-; -DPLUGIN_ENERGY_COLLECTION
-; -DFEATURE_ETHERNET=1
-
-; [env:display_ESP32_IRExt_4M316k_ETH]
-; extends = esp32_IRExt
-; board = esp32_4M
-; build_flags = ${esp32_IRExt.build_flags}
-; -DPLUGIN_DISPLAY_COLLECTION
-; -DFEATURE_ETHERNET=1
-
-; [env:climate_ESP32_IRExt_4M316k_ETH]
-; extends = esp32_IRExt
-; board = esp32_4M
-; build_flags = ${esp32_IRExt.build_flags}
-; -DPLUGIN_CLIMATE_COLLECTION
-; -DFEATURE_ETHERNET=1
-
-; [env:neopixel_ESP32_IRExt_4M316k_ETH]
-; extends = esp32_IRExt
-; board = esp32_4M
-; build_flags = ${esp32_IRExt.build_flags}
-; -D PLUGIN_NEOPIXEL_COLLECTION
-; -DFEATURE_ETHERNET=1
-
-
-; ESP32 MAX builds 16M flash ------------------------------
-
-; A Lolin D32 PRO with 16MB Flash, allowing 4MB sketch size, and file storage using the default (SPIFFS) filesystem
-[env:max_ESP32_16M1M]
-extends = esp32_common
-board = esp32_16M1M
-board_upload.flash_size = 16MB
-lib_ignore = ${esp32_always.lib_ignore}
- ESP32_ping
-build_flags = ${esp32_common.build_flags}
- -DFEATURE_ARDUINO_OTA=1
- -DPLUGIN_BUILD_MAX_ESP32
- -DPLUGIN_BUILD_IR_EXTENDED
-
-[env:max_ESP32_16M1M_ETH]
-extends = env:max_ESP32_16M1M
-build_flags = ${env:max_ESP32_16M1M.build_flags}
- -DFEATURE_ETHERNET=1
-
-
-; A Lolin D32 PRO with 16MB Flash, allowing 4MB sketch size, and file storage using LittleFS filesystem
-[env:max_ESP32_16M8M_LittleFS_ETH]
-extends = esp32_common_LittleFS
-board = esp32_16M8M
-board_upload.flash_size = 16MB
-lib_ignore = ${esp32_always.lib_ignore}
- ESP32_ping
- ${esp32_common_LittleFS.lib_ignore}
-build_flags = ${esp32_common_LittleFS.build_flags}
- -DFEATURE_ARDUINO_OTA=1
- -DPLUGIN_BUILD_MAX_ESP32
- -DPLUGIN_BUILD_IR_EXTENDED
- -DFEATURE_ETHERNET=1
-extra_scripts = ${esp32_common.extra_scripts}
-board_build.filesystem = littlefs
-
-; If you have a board with Ethernet integrated and 16MB Flash, then this configuration could be enabled, it's based on the max_ESP32_16M8M_LittleFS definition
-; [env:max_ESP32_16M8M_LittleFS_ETH]
-; extends = env:max_ESP32_16M8M_LittleFS
-; board = ${env:max_ESP32_16M8M_LittleFS.board}
-; build_flags = ${env:max_ESP32_16M8M_LittleFS.build_flags}
-; -DFEATURE_ETHERNET=1
-
-
-
-
-
-
-
-
-
-
+;;; ESP32 test build ********************************************************************;
+; Status of the ESP32 support is still considered "beta" ;
+; Most plugins work just fine on ESP32. ;
+; Especially some plugins using serial may not run very well (GPS does run fine). ;
+; ***************************************************************************************;
+
+
+[esp32_base]
+extends = common, core_esp32_IDF4_4__2_0_14
+upload_speed = 460800
+upload_before_reset = default_reset
+upload_after_reset = hard_reset
+extra_scripts = post:tools/pio/post_esp32.py
+ ${extra_scripts_default.extra_scripts}
+; you can disable debug linker flag to reduce binary size (comment out line below), but the backtraces will become less readable
+; tools/pio/extra_linker_flags.py
+; fix the platform package to use gcc-ar and gcc-ranlib to enable lto linker plugin
+; more detail: https://embeddedartistry.com/blog/2020/04/13/prefer-gcc-ar-to-ar-in-your-buildsystems/
+; pre:tools/pio/apply_patches.py
+build_unflags = -Wall
+; -fno-lto
+build_flags = ${core_esp32_IDF4_4__2_0_14.build_flags}
+; ${mqtt_flags.build_flags}
+ -DMQTT_MAX_PACKET_SIZE=2048
+ -DCONFIG_FREERTOS_ASSERT_DISABLE
+ -DCONFIG_LWIP_ESP_GRATUITOUS_ARP
+ -fno-strict-aliasing
+; -flto
+ -Wswitch
+ -DCORE_DEBUG_LEVEL=ARDUHAL_LOG_LEVEL_NONE
+monitor_filters = esp32_exception_decoder
+lib_ignore =
+ ${core_esp32_IDF4_4__2_0_14.lib_ignore}
+
+
+[esp32_base_idf5]
+extends = common, core_esp32_IDF5_1__3_0_0
+upload_speed = 460800
+upload_before_reset = default_reset
+upload_after_reset = hard_reset
+extra_scripts = post:tools/pio/post_esp32.py
+ ${extra_scripts_default.extra_scripts}
+; you can disable debug linker flag to reduce binary size (comment out line below), but the backtraces will become less readable
+; tools/pio/extra_linker_flags.py
+; fix the platform package to use gcc-ar and gcc-ranlib to enable lto linker plugin
+; more detail: https://embeddedartistry.com/blog/2020/04/13/prefer-gcc-ar-to-ar-in-your-buildsystems/
+; pre:tools/pio/apply_patches.py
+
+; When using LTO, make sure NOT to use -mtext-section-literals
+; -mtext-section-literals may be required when building large builds
+; However LTO cannot optimize builds with text section literals and thus will result in quite a lot larger builds (80k - 140k larger)
+build_unflags = -Wall
+ -fno-lto
+build_flags = ${core_esp32_IDF5_1__3_0_0.build_flags}
+; ${mqtt_flags.build_flags}
+ -DMQTT_MAX_PACKET_SIZE=2048
+ -DCONFIG_FREERTOS_ASSERT_DISABLE
+ -DCONFIG_LWIP_ESP_GRATUITOUS_ARP
+ -fno-strict-aliasing
+ -flto=auto
+ -Wswitch
+ -DCORE_DEBUG_LEVEL=ARDUHAL_LOG_LEVEL_NONE
+; -DCORE_DEBUG_LEVEL=ARDUHAL_LOG_LEVEL_INFO
+; -DCORE_DEBUG_LEVEL=ARDUHAL_LOG_LEVEL_VERBOSE
+ -DLWIP_IPV6=1
+monitor_filters = esp32_exception_decoder
+lib_ignore =
+ ${core_esp32_IDF5_1__3_0_0.lib_ignore}
+
+
+; -flto cannot be used for ESP32 C3!
+; See: https://github.com/letscontrolit/ESPEasy/pull/3845#issuecomment-1014857366
+; TD-er: 2022-01-20: Disabled for now as it also resulted in obscure linker errors on ESP32-S2 and ESP32 running custom builds.
+;build_flags = ${esp32_base.build_flags}
+; -flto
+;build_unflags = ${esp32_base.build_unflags}
+; -fexceptions
+; -fno-lto
+
+
+[esp32_always]
+lib_ignore = ESP8266Ping
+ ESP8266HTTPUpdateServer
+ ESP8266WiFi
+ ESP8266WebServer
+ ESP8266mDNS
+ ESPEasy_ESP8266Ping
+ RABurton ESP8266 Mutex
+ TinyWireM
+ LittleFS_esp32
+ Adafruit NeoPixel
+ ${esp32_base.lib_ignore}
+
+
+[esp32_common]
+extends = esp32_base
+lib_ignore = ${esp32_always.lib_ignore}
+ ESP32_ping
+ ${no_ir.lib_ignore}
+ ESP32 BLE Arduino
+build_flags = ${esp32_base.build_flags}
+ -DESP32_CLASSIC
+extra_scripts = ${esp32_base.extra_scripts}
+build_unflags = ${esp32_base.build_unflags}
+ -fexceptions
+
+[esp32_common_LittleFS]
+extends = esp32_base_idf5
+build_flags = ${esp32_base_idf5.build_flags}
+; -mtext-section-literals
+ -DESP32_CLASSIC
+ -DUSE_LITTLEFS
+build_unflags = ${esp32_base_idf5.build_unflags}
+extra_scripts = ${esp32_common.extra_scripts}
+board_build.filesystem = littlefs
+lib_ignore = ${esp32_always.lib_ignore}
+ ESP32_ping
+ ESP32 BLE Arduino
+ ${core_esp32_IDF5_1__3_0_0.lib_ignore}
+
+
+[esp32_IRExt]
+extends = esp32_common
+lib_ignore = ${esp32_always.lib_ignore}
+ ESP32_ping
+build_flags = ${esp32_common.build_flags}
+ -DFEATURE_ARDUINO_OTA=1
+ -DPLUGIN_BUILD_NORMAL_IRext
+ -DCOLLECTION_USE_RTTTL
+extra_scripts = ${esp32_common.extra_scripts}
+ pre:tools/pio/ir_build_check.py
+
+
+[esp32_custom_base]
+extends = esp32_common
+build_flags = ${esp32_common.build_flags}
+ -DPLUGIN_BUILD_CUSTOM
+extra_scripts = ${esp32_common.extra_scripts}
+ pre:tools/pio/pre_custom_esp32.py
+
+[esp32_custom_base_LittleFS]
+extends = esp32_common_LittleFS
+build_flags = ${esp32_common_LittleFS.build_flags}
+ -DPLUGIN_BUILD_CUSTOM
+extra_scripts = ${esp32_common_LittleFS.extra_scripts}
+ pre:tools/pio/pre_custom_esp32.py
+
+
+[env:custom_ESP32_4M316k]
+extends = esp32_custom_base
+board = esp32_4M
+
+; [env:custom_ESP32_4M316k_LittleFS]
+; extends = esp32_custom_base_LittleFS
+; board = esp32_4M
+
+[env:custom_ESP32_16M8M_LittleFS_ETH]
+extends = esp32_custom_base_LittleFS
+board = esp32_16M8M
+board_upload.flash_size = 16MB
+build_flags = ${esp32_custom_base_LittleFS.build_flags}
+ -DFEATURE_ETHERNET=1
+
+[env:custom_IR_ESP32_4M316k]
+extends = esp32_common
+board = esp32_4M
+build_flags = ${esp32_common.build_flags}
+ -DPLUGIN_BUILD_CUSTOM
+ -DPLUGIN_BUILD_IR
+lib_ignore = ${esp32_always.lib_ignore}
+ ESP32_ping
+extra_scripts = ${esp32_common.extra_scripts}
+ pre:tools/pio/pre_custom_esp32_IR.py
+ pre:tools/pio/ir_build_check.py
+
+[env:custom_ESP32_4M2M_NO_OTA_LittleFS_ETH]
+extends = esp32_custom_base_LittleFS
+board = esp32_4M2M
+build_flags = ${esp32_custom_base_LittleFS.build_flags}
+ -DNO_HTTP_UPDATER
+ -DFEATURE_ETHERNET=1
+
+
+[env:normal_ESP32_4M316k]
+extends = esp32_common
+board = esp32_4M
+lib_ignore = ${esp32_common.lib_ignore}
+ ${no_ir.lib_ignore}
+
+; [env:normal_ESP32_4M316k_LittleFS]
+; extends = esp32_common_LittleFS
+; board = esp32_4M
+; lib_ignore = ${esp32_common_LittleFS.lib_ignore}
+; ${no_ir.lib_ignore}
+
+[env:collection_A_ESP32_4M316k]
+extends = esp32_common
+board = esp32_4M
+build_flags = ${esp32_common.build_flags}
+ -DFEATURE_ARDUINO_OTA=1
+ -DPLUGIN_SET_COLLECTION_ESP32
+ -DCOLLECTION_USE_RTTTL
+
+[env:collection_B_ESP32_4M316k]
+extends = esp32_common
+board = esp32_4M
+build_flags = ${esp32_common.build_flags}
+ -DFEATURE_ARDUINO_OTA=1
+ -DPLUGIN_SET_COLLECTION_B_ESP32
+ -DCOLLECTION_USE_RTTTL
+
+[env:collection_C_ESP32_4M316k]
+extends = esp32_common
+board = esp32_4M
+build_flags = ${esp32_common.build_flags}
+ -DFEATURE_ARDUINO_OTA=1
+ -DPLUGIN_SET_COLLECTION_C_ESP32
+ -DCOLLECTION_USE_RTTTL
+
+[env:collection_D_ESP32_4M316k]
+extends = esp32_common
+board = esp32_4M
+build_flags = ${esp32_common.build_flags}
+ -DFEATURE_ARDUINO_OTA=1
+ -DPLUGIN_SET_COLLECTION_D_ESP32
+ -DCOLLECTION_USE_RTTTL
+
+[env:collection_E_ESP32_4M316k]
+extends = esp32_common
+board = esp32_4M
+build_flags = ${esp32_common.build_flags}
+ -DFEATURE_ARDUINO_OTA=1
+ -DPLUGIN_SET_COLLECTION_E_ESP32
+ -DCOLLECTION_USE_RTTTL
+
+[env:collection_F_ESP32_4M316k]
+extends = esp32_common
+board = esp32_4M
+build_flags = ${esp32_common.build_flags}
+ -DFEATURE_ARDUINO_OTA=1
+ -DPLUGIN_SET_COLLECTION_F_ESP32
+ -DCOLLECTION_USE_RTTTL
+
+[env:collection_G_ESP32_4M316k]
+extends = esp32_common
+board = esp32_4M
+build_flags = ${esp32_common.build_flags}
+ -DFEATURE_ARDUINO_OTA=1
+ -DPLUGIN_SET_COLLECTION_G_ESP32
+ -DCOLLECTION_USE_RTTTL
+
+
+[env:collection_A_ESP32_IRExt_4M316k]
+extends = esp32_IRExt
+board = esp32_4M
+build_flags = ${esp32_IRExt.build_flags}
+ -DPLUGIN_SET_COLLECTION_ESP32
+
+[env:collection_B_ESP32_IRExt_4M316k]
+extends = esp32_IRExt
+board = esp32_4M
+build_flags = ${esp32_IRExt.build_flags}
+ -DPLUGIN_SET_COLLECTION_B_ESP32
+
+[env:collection_C_ESP32_IRExt_4M316k]
+extends = esp32_IRExt
+board = esp32_4M
+build_flags = ${esp32_IRExt.build_flags}
+ -DPLUGIN_SET_COLLECTION_C_ESP32
+
+[env:collection_D_ESP32_IRExt_4M316k]
+extends = esp32_IRExt
+board = esp32_4M
+build_flags = ${esp32_IRExt.build_flags}
+ -DPLUGIN_SET_COLLECTION_D_ESP32
+
+[env:collection_E_ESP32_IRExt_4M316k]
+extends = esp32_IRExt
+board = esp32_4M
+build_flags = ${esp32_IRExt.build_flags}
+ -DPLUGIN_SET_COLLECTION_E_ESP32
+
+[env:collection_F_ESP32_IRExt_4M316k]
+extends = esp32_IRExt
+board = esp32_4M
+build_flags = ${esp32_IRExt.build_flags}
+ -DPLUGIN_SET_COLLECTION_F_ESP32
+
+[env:collection_G_ESP32_IRExt_4M316k]
+extends = esp32_IRExt
+board = esp32_4M
+build_flags = ${esp32_IRExt.build_flags}
+ -DPLUGIN_SET_COLLECTION_G_ESP32
+
+[env:energy_ESP32_4M316k]
+extends = esp32_common
+board = esp32_4M
+build_flags = ${esp32_common.build_flags}
+ -DFEATURE_ARDUINO_OTA=1
+ -DPLUGIN_ENERGY_COLLECTION
+
+[env:display_ESP32_4M316k]
+extends = esp32_common
+board = esp32_4M
+build_flags = ${esp32_common.build_flags}
+ -DFEATURE_ARDUINO_OTA=1
+ -DPLUGIN_DISPLAY_COLLECTION
+
+[env:climate_ESP32_4M316k]
+extends = esp32_common
+board = esp32_4M
+build_flags = ${esp32_common.build_flags}
+ -DFEATURE_ARDUINO_OTA=1
+ -DPLUGIN_CLIMATE_COLLECTION
+
+[env:neopixel_ESP32_4M316k]
+extends = esp32_common
+board = esp32_4M
+build_flags = ${esp32_common.build_flags}
+ -DFEATURE_ARDUINO_OTA=1
+ -DFEATURE_SD=1
+ -D PLUGIN_NEOPIXEL_COLLECTION
+
+
+[env:custom_ESP32_4M316k_ETH]
+extends = env:custom_ESP32_4M316k
+build_flags = ${env:custom_ESP32_4M316k.build_flags}
+ -DFEATURE_ETHERNET=1
+
+[env:custom_ESP32_4M316k_LittleFS_ETH]
+extends = esp32_custom_base_LittleFS
+board = esp32_4M
+build_flags = ${esp32_custom_base_LittleFS.build_flags}
+ -DFEATURE_ETHERNET=1
+
+
+[env:custom_IR_ESP32_4M316k_ETH]
+extends = env:custom_IR_ESP32_4M316k
+build_flags = ${env:custom_IR_ESP32_4M316k.build_flags}
+ -DFEATURE_ETHERNET=1
+extra_scripts = ${env:custom_IR_ESP32_4M316k.extra_scripts}
+
+[env:custom_IR_ESP32_16M8M_LittleFS_ETH]
+extends = esp32_common_LittleFS
+board = esp32_16M8M
+board_upload.flash_size = 16MB
+build_flags = ${esp32_common_LittleFS.build_flags}
+ -DPLUGIN_BUILD_CUSTOM
+ -DPLUGIN_BUILD_IR
+ -DFEATURE_ETHERNET=1
+lib_ignore = ${esp32_always.lib_ignore}
+ ESP32_ping
+ ${esp32_common_LittleFS.lib_ignore}
+extra_scripts = ${esp32_common.extra_scripts}
+ pre:tools/pio/pre_custom_esp32_IR.py
+ pre:tools/pio/ir_build_check.py
+
+[env:normal_ESP32_4M316k_ETH]
+extends = env:normal_ESP32_4M316k
+build_flags = ${env:normal_ESP32_4M316k.build_flags}
+ -DFEATURE_ETHERNET=1
+
+
+[env:normal_ESP32_4M316k_LittleFS_ETH]
+extends = esp32_common_LittleFS
+board = esp32_4M
+lib_ignore = ${esp32_always.lib_ignore}
+ ESP32_ping
+ ${esp32_common_LittleFS.lib_ignore}
+ ${no_ir.lib_ignore}
+build_flags = ${esp32_common_LittleFS.build_flags}
+ -DFEATURE_ETHERNET=1
+
+
+[env:normal_ESP32_IRExt_4M316k_ETH]
+extends = esp32_IRExt
+board = esp32_4M
+build_flags = ${esp32_IRExt.build_flags}
+ -DFEATURE_ETHERNET=1
+
+[env:collection_A_ESP32_4M316k_ETH]
+extends = env:collection_A_ESP32_4M316k
+build_flags = ${env:collection_A_ESP32_4M316k.build_flags}
+ -DFEATURE_ETHERNET=1
+ -DCOLLECTION_USE_RTTTL
+
+[env:collection_B_ESP32_4M316k_ETH]
+extends = env:collection_B_ESP32_4M316k
+build_flags = ${env:collection_B_ESP32_4M316k.build_flags}
+ -DFEATURE_ETHERNET=1
+ -DCOLLECTION_USE_RTTTL
+
+[env:collection_C_ESP32_4M316k_ETH]
+extends = env:collection_C_ESP32_4M316k
+build_flags = ${env:collection_C_ESP32_4M316k.build_flags}
+ -DFEATURE_ETHERNET=1
+ -DCOLLECTION_USE_RTTTL
+
+[env:collection_D_ESP32_4M316k_ETH]
+extends = env:collection_D_ESP32_4M316k
+build_flags = ${env:collection_D_ESP32_4M316k.build_flags}
+ -DFEATURE_ETHERNET=1
+ -DCOLLECTION_USE_RTTTL
+
+[env:collection_E_ESP32_4M316k_ETH]
+extends = env:collection_E_ESP32_4M316k
+build_flags = ${env:collection_E_ESP32_4M316k.build_flags}
+ -DFEATURE_ETHERNET=1
+ -DCOLLECTION_USE_RTTTL
+
+[env:collection_F_ESP32_4M316k_ETH]
+extends = env:collection_F_ESP32_4M316k
+build_flags = ${env:collection_F_ESP32_4M316k.build_flags}
+ -DFEATURE_ETHERNET=1
+ -DCOLLECTION_USE_RTTTL
+
+[env:collection_G_ESP32_4M316k_ETH]
+extends = env:collection_G_ESP32_4M316k
+build_flags = ${env:collection_G_ESP32_4M316k.build_flags}
+ -DFEATURE_ETHERNET=1
+ -DCOLLECTION_USE_RTTTL
+
+[env:energy_ESP32_4M316k_ETH]
+extends = env:energy_ESP32_4M316k
+build_flags = ${env:energy_ESP32_4M316k.build_flags}
+ -DFEATURE_ETHERNET=1
+
+[env:display_ESP32_4M316k_ETH]
+extends = env:display_ESP32_4M316k
+build_flags = ${env:display_ESP32_4M316k.build_flags}
+ -DFEATURE_ETHERNET=1
+
+[env:climate_ESP32_4M316k_ETH]
+extends = env:climate_ESP32_4M316k
+build_flags = ${env:climate_ESP32_4M316k.build_flags}
+ -DFEATURE_ETHERNET=1
+
+[env:neopixel_ESP32_4M316k_ETH]
+extends = env:neopixel_ESP32_4M316k
+build_flags = ${env:neopixel_ESP32_4M316k.build_flags}
+ -DFEATURE_ETHERNET=1
+
+; [env:collection_A_ESP32_IRExt_4M316k_ETH]
+; extends = esp32_IRExt
+; board = esp32_4M
+; build_flags = ${esp32_IRExt.build_flags}
+; -DPLUGIN_SET_COLLECTION_ESP32
+; -DFEATURE_ETHERNET=1
+
+; [env:collection_B_ESP32_IRExt_4M316k_ETH]
+; extends = esp32_IRExt
+; board = esp32_4M
+; build_flags = ${esp32_IRExt.build_flags}
+; -DPLUGIN_SET_COLLECTION_B_ESP32
+; -DFEATURE_ETHERNET=1
+
+; [env:collection_C_ESP32_IRExt_4M316k_ETH]
+; extends = esp32_IRExt
+; board = esp32_4M
+; build_flags = ${esp32_IRExt.build_flags}
+; -DPLUGIN_SET_COLLECTION_C_ESP32
+; -DFEATURE_ETHERNET=1
+
+; [env:collection_D_ESP32_IRExt_4M316k_ETH]
+; extends = esp32_IRExt
+; board = esp32_4M
+; build_flags = ${esp32_IRExt.build_flags}
+; -DPLUGIN_SET_COLLECTION_D_ESP32
+; -DFEATURE_ETHERNET=1
+
+; [env:collection_E_ESP32_IRExt_4M316k_ETH]
+; extends = esp32_IRExt
+; board = esp32_4M
+; build_flags = ${esp32_IRExt.build_flags}
+; -DPLUGIN_SET_COLLECTION_E_ESP32
+; -DFEATURE_ETHERNET=1
+
+; [env:collection_F_ESP32_IRExt_4M316k_ETH]
+; extends = esp32_IRExt
+; board = esp32_4M
+; build_flags = ${esp32_IRExt.build_flags}
+; -DPLUGIN_SET_COLLECTION_F_ESP32
+; -DFEATURE_ETHERNET=1
+
+; [env:collection_G_ESP32_IRExt_4M316k_ETH]
+; extends = esp32_IRExt
+; board = esp32_4M
+; build_flags = ${esp32_IRExt.build_flags}
+; -DPLUGIN_SET_COLLECTION_G_ESP32
+; -DFEATURE_ETHERNET=1
+
+; [env:energy_ESP32_IRExt_4M316k_ETH]
+; extends = esp32_IRExt
+; board = esp32_4M
+; build_flags = ${esp32_IRExt.build_flags}
+; -DPLUGIN_ENERGY_COLLECTION
+; -DFEATURE_ETHERNET=1
+
+; [env:display_ESP32_IRExt_4M316k_ETH]
+; extends = esp32_IRExt
+; board = esp32_4M
+; build_flags = ${esp32_IRExt.build_flags}
+; -DPLUGIN_DISPLAY_COLLECTION
+; -DFEATURE_ETHERNET=1
+
+; [env:climate_ESP32_IRExt_4M316k_ETH]
+; extends = esp32_IRExt
+; board = esp32_4M
+; build_flags = ${esp32_IRExt.build_flags}
+; -DPLUGIN_CLIMATE_COLLECTION
+; -DFEATURE_ETHERNET=1
+
+; [env:neopixel_ESP32_IRExt_4M316k_ETH]
+; extends = esp32_IRExt
+; board = esp32_4M
+; build_flags = ${esp32_IRExt.build_flags}
+; -D PLUGIN_NEOPIXEL_COLLECTION
+; -DFEATURE_ETHERNET=1
+
+
+; ESP32 MAX builds 16M flash ------------------------------
+
+; A Lolin D32 PRO with 16MB Flash, allowing 4MB sketch size, and file storage using the default (SPIFFS) filesystem
+[env:max_ESP32_16M1M]
+extends = esp32_common
+board = esp32_16M1M
+board_upload.flash_size = 16MB
+lib_ignore = ${esp32_always.lib_ignore}
+ ESP32_ping
+build_flags = ${esp32_common.build_flags}
+ -DFEATURE_ARDUINO_OTA=1
+ -DPLUGIN_BUILD_MAX_ESP32
+ -DPLUGIN_BUILD_IR_EXTENDED
+
+[env:max_ESP32_16M1M_ETH]
+extends = env:max_ESP32_16M1M
+build_flags = ${env:max_ESP32_16M1M.build_flags}
+ -DFEATURE_ETHERNET=1
+
+
+; A Lolin D32 PRO with 16MB Flash, allowing 4MB sketch size, and file storage using LittleFS filesystem
+[env:max_ESP32_16M8M_LittleFS_ETH]
+extends = esp32_common_LittleFS
+board = esp32_16M8M
+board_upload.flash_size = 16MB
+lib_ignore = ${esp32_always.lib_ignore}
+ ESP32_ping
+ ${esp32_common_LittleFS.lib_ignore}
+build_flags = ${esp32_common_LittleFS.build_flags}
+ -DFEATURE_ARDUINO_OTA=1
+ -DPLUGIN_BUILD_MAX_ESP32
+ -DPLUGIN_BUILD_IR_EXTENDED
+ -DFEATURE_ETHERNET=1
+extra_scripts = ${esp32_common.extra_scripts}
+board_build.filesystem = littlefs
+
+; If you have a board with Ethernet integrated and 16MB Flash, then this configuration could be enabled, it's based on the max_ESP32_16M8M_LittleFS definition
+; [env:max_ESP32_16M8M_LittleFS_ETH]
+; extends = env:max_ESP32_16M8M_LittleFS
+; board = ${env:max_ESP32_16M8M_LittleFS.board}
+; build_flags = ${env:max_ESP32_16M8M_LittleFS.build_flags}
+; -DFEATURE_ETHERNET=1
+
+
+
+
+
+
+
+
+
+
diff --git a/platformio_esp32_solo1.ini b/platformio_esp32_solo1.ini
index 09f9d53633..b27d256b84 100644
--- a/platformio_esp32_solo1.ini
+++ b/platformio_esp32_solo1.ini
@@ -1,63 +1,63 @@
-
-
-
-; IDF 4.4
-[esp32_solo1_common]
-extends = esp32_base
-platform_packages = framework-arduino-solo1 @ https://github.com/Jason2866/esp32-arduino-lib-builder/releases/download/1646/framework-arduinoespressif32-solo1-release_v4.4_spiffs-e3fc63b439.zip
-lib_ignore = ${esp32_always.lib_ignore}
- ESP32_ping
- ${no_ir.lib_ignore}
- ESP32 BLE Arduino
-build_flags = ${esp32_base.build_flags}
- -DFEATURE_ARDUINO_OTA=1
-extra_scripts = ${esp32_base.extra_scripts}
-build_unflags = ${esp32_base.build_unflags}
- -fexceptions
-
-; IDF 5.1.2
-[esp32_solo1_common_LittleFS]
-extends = esp32_base_idf5
-platform = https://github.com/tasmota/platform-espressif32/releases/download/2024.05.13/platform-espressif32.zip
-platform_packages =
-build_flags = ${esp32_base_idf5.build_flags}
- -DFEATURE_ARDUINO_OTA=1
- -DUSE_LITTLEFS
-extra_scripts = ${esp32_base_idf5.extra_scripts}
-build_unflags = ${esp32_base_idf5.build_unflags}
- -fexceptions
-board_build.filesystem = littlefs
-
-
-[env:custom_ESP32solo1_4M316k_LittleFS_ETH]
-extends = esp32_solo1_common_LittleFS
-board = esp32_solo1_4M
-build_flags = ${esp32_solo1_common_LittleFS.build_flags}
- -DPLUGIN_BUILD_CUSTOM
- -DFEATURE_ETHERNET=1
-extra_scripts = ${esp32_solo1_common_LittleFS.extra_scripts}
- pre:tools/pio/pre_custom_esp32.py
-
-
-[env:normal_ESP32solo1_4M316k_LittleFS_ETH]
-extends = esp32_solo1_common_LittleFS
-board = esp32_solo1_4M
-build_flags = ${esp32_solo1_common_LittleFS.build_flags}
- -DFEATURE_ETHERNET=1
-lib_ignore = ${esp32_solo1_common_LittleFS.lib_ignore}
- ${no_ir.lib_ignore}
-
-
-[env:energy_ESP32solo1_4M316k_LittleFS_ETH]
-extends = esp32_solo1_common_LittleFS
-board = esp32_solo1_4M
-build_flags = ${esp32_solo1_common_LittleFS.build_flags}
- -D PLUGIN_ENERGY_COLLECTION
- -DFEATURE_ETHERNET=1
-
-[env:climate_ESP32solo1_4M316k_LittleFS_ETH]
-extends = esp32_solo1_common_LittleFS
-board = esp32_solo1_4M
-build_flags = ${esp32_solo1_common_LittleFS.build_flags}
- -D PLUGIN_CLIMATE_COLLECTION
- -DFEATURE_ETHERNET=1
+
+
+
+; IDF 4.4
+[esp32_solo1_common]
+extends = esp32_base
+platform_packages = framework-arduino-solo1 @ https://github.com/Jason2866/esp32-arduino-lib-builder/releases/download/1646/framework-arduinoespressif32-solo1-release_v4.4_spiffs-e3fc63b439.zip
+lib_ignore = ${esp32_always.lib_ignore}
+ ESP32_ping
+ ${no_ir.lib_ignore}
+ ESP32 BLE Arduino
+build_flags = ${esp32_base.build_flags}
+ -DFEATURE_ARDUINO_OTA=1
+extra_scripts = ${esp32_base.extra_scripts}
+build_unflags = ${esp32_base.build_unflags}
+ -fexceptions
+
+; IDF 5.1.2
+[esp32_solo1_common_LittleFS]
+extends = esp32_base_idf5
+platform = https://github.com/tasmota/platform-espressif32/releases/download/2024.06.11/platform-espressif32.zip
+platform_packages = framework-arduinoespressif32 @ https://github.com/Jason2866/esp32-arduino-lib-builder/releases/download/2525/framework-arduinoespressif32-solo1-release_v5.1-e9a74b6.zip
+build_flags = ${esp32_base_idf5.build_flags}
+ -DFEATURE_ARDUINO_OTA=1
+ -DUSE_LITTLEFS
+extra_scripts = ${esp32_base_idf5.extra_scripts}
+build_unflags = ${esp32_base_idf5.build_unflags}
+ -fexceptions
+board_build.filesystem = littlefs
+
+
+[env:custom_ESP32solo1_4M316k_LittleFS_ETH]
+extends = esp32_solo1_common_LittleFS
+board = esp32_solo1_4M
+build_flags = ${esp32_solo1_common_LittleFS.build_flags}
+ -DPLUGIN_BUILD_CUSTOM
+ -DFEATURE_ETHERNET=1
+extra_scripts = ${esp32_solo1_common_LittleFS.extra_scripts}
+ pre:tools/pio/pre_custom_esp32.py
+
+
+[env:normal_ESP32solo1_4M316k_LittleFS_ETH]
+extends = esp32_solo1_common_LittleFS
+board = esp32_solo1_4M
+build_flags = ${esp32_solo1_common_LittleFS.build_flags}
+ -DFEATURE_ETHERNET=1
+lib_ignore = ${esp32_solo1_common_LittleFS.lib_ignore}
+ ${no_ir.lib_ignore}
+
+
+[env:energy_ESP32solo1_4M316k_LittleFS_ETH]
+extends = esp32_solo1_common_LittleFS
+board = esp32_solo1_4M
+build_flags = ${esp32_solo1_common_LittleFS.build_flags}
+ -D PLUGIN_ENERGY_COLLECTION
+ -DFEATURE_ETHERNET=1
+
+[env:climate_ESP32solo1_4M316k_LittleFS_ETH]
+extends = esp32_solo1_common_LittleFS
+board = esp32_solo1_4M
+build_flags = ${esp32_solo1_common_LittleFS.build_flags}
+ -D PLUGIN_CLIMATE_COLLECTION
+ -DFEATURE_ETHERNET=1
diff --git a/platformio_esp32c2_envs.ini b/platformio_esp32c2_envs.ini
index e5937ff2e0..7f8919bf5e 100644
--- a/platformio_esp32c2_envs.ini
+++ b/platformio_esp32c2_envs.ini
@@ -1,55 +1,51 @@
-
-
-[esp32c2_common_LittleFS]
-extends = esp32_base_idf5
-build_flags = ${esp32_base_idf5.build_flags}
- -DFEATURE_ARDUINO_OTA=1
- -DUSE_LITTLEFS
- -DESP32C2
-extra_scripts = ${esp32_base_idf5.extra_scripts}
-build_unflags = ${esp32_base_idf5.build_unflags}
- -fexceptions
-board_build.filesystem = littlefs
-lib_ignore = ${esp32_base_idf5.lib_ignore}
- NeoPixelBus
- NeoPixelBus_wrapper
- Adafruit NeoMatrix via NeoPixelBus
-
-
-[env:safeboot_ESP32c2_4M_LittleFS_ETH]
-extends = esp32c2_common_LittleFS
-board = esp32c2
-build_flags = ${esp32c2_common_LittleFS.build_flags}
- -DPLUGIN_BUILD_CUSTOM
- -DPLUGIN_BUILD_SAFEBOOT
- -DFEATURE_ETHERNET=1
-extra_scripts = ${esp32c2_common_LittleFS.extra_scripts}
- pre:tools/pio/pre_safeboot_esp32c2.py
-lib_ignore = ${esp32c2_common_LittleFS.lib_ignore}
-
-
-[env:custom_ESP32c2_2M320k_LittleFS_noOTA_ETH]
-extends = esp32c2_common_LittleFS
-board = esp32c2_2M
-build_flags = ${esp32c2_common_LittleFS.build_flags}
- -DPLUGIN_BUILD_CUSTOM
- -DFEATURE_ETHERNET=1
-extra_scripts = ${esp32c2_common_LittleFS.extra_scripts}
- pre:tools/pio/pre_custom_esp32c2.py
-
-[env:custom_ESP32c2_4M316k_LittleFS_ETH]
-extends = esp32c2_common_LittleFS
-board = esp32c2
-build_flags = ${esp32c2_common_LittleFS.build_flags}
- -DPLUGIN_BUILD_CUSTOM
- -DFEATURE_ETHERNET=1
-extra_scripts = ${esp32c2_common_LittleFS.extra_scripts}
- pre:tools/pio/pre_custom_esp32c2.py
-
-[env:normal_ESP32c2_4M316k_LittleFS_ETH]
-extends = esp32c2_common_LittleFS
-board = esp32c2
-build_flags = ${esp32c2_common_LittleFS.build_flags}
- -DFEATURE_ETHERNET=1
-lib_ignore = ${esp32c2_common_LittleFS.lib_ignore}
- ${no_ir.lib_ignore}
+; No Ethernet for ESP32-C2 as this one already hasn't much RAM.
+; Thus Jason removed Ethernet support for ESP32-C2 from the PIO platform_packages
+[esp32c2_common_LittleFS]
+extends = esp32_base_idf5
+build_flags = ${esp32_base_idf5.build_flags}
+ -DFEATURE_ARDUINO_OTA=1
+ -DUSE_LITTLEFS
+ -DESP32C2
+extra_scripts = ${esp32_base_idf5.extra_scripts}
+build_unflags = ${esp32_base_idf5.build_unflags}
+ -fexceptions
+board_build.filesystem = littlefs
+lib_ignore = ${esp32_base_idf5.lib_ignore}
+ NeoPixelBus
+ NeoPixelBus_wrapper
+ Adafruit NeoMatrix via NeoPixelBus
+
+
+[env:safeboot_ESP32c2_4M_LittleFS]
+extends = esp32c2_common_LittleFS
+board = esp32c2
+build_flags = ${esp32c2_common_LittleFS.build_flags}
+ -DPLUGIN_BUILD_CUSTOM
+ -DPLUGIN_BUILD_SAFEBOOT
+extra_scripts = ${esp32c2_common_LittleFS.extra_scripts}
+ pre:tools/pio/pre_safeboot_esp32c2.py
+lib_ignore = ${esp32c2_common_LittleFS.lib_ignore}
+
+
+[env:custom_ESP32c2_2M320k_LittleFS_noOTA]
+extends = esp32c2_common_LittleFS
+board = esp32c2_2M
+build_flags = ${esp32c2_common_LittleFS.build_flags}
+ -DPLUGIN_BUILD_CUSTOM
+extra_scripts = ${esp32c2_common_LittleFS.extra_scripts}
+ pre:tools/pio/pre_custom_esp32c2.py
+
+[env:custom_ESP32c2_4M316k_LittleFS]
+extends = esp32c2_common_LittleFS
+board = esp32c2
+build_flags = ${esp32c2_common_LittleFS.build_flags}
+ -DPLUGIN_BUILD_CUSTOM
+extra_scripts = ${esp32c2_common_LittleFS.extra_scripts}
+ pre:tools/pio/pre_custom_esp32c2.py
+
+[env:normal_ESP32c2_4M316k_LittleFS]
+extends = esp32c2_common_LittleFS
+board = esp32c2
+build_flags = ${esp32c2_common_LittleFS.build_flags}
+lib_ignore = ${esp32c2_common_LittleFS.lib_ignore}
+ ${no_ir.lib_ignore}
diff --git a/platformio_esp32c3_envs.ini b/platformio_esp32c3_envs.ini
index 2fb29ddb7b..f4a36b912b 100644
--- a/platformio_esp32c3_envs.ini
+++ b/platformio_esp32c3_envs.ini
@@ -1,174 +1,174 @@
-
-
-
-[esp32c3_common]
-extends = esp32_base
-lib_ignore = ${esp32_always.lib_ignore}
- ESP32_ping
- ${no_ir.lib_ignore}
- ESP32 BLE Arduino
-build_flags = ${esp32_base.build_flags}
- -DFEATURE_ARDUINO_OTA=1
- -DESP32C3
-extra_scripts = ${esp32_base.extra_scripts}
-build_unflags = ${esp32_base.build_unflags}
- -fexceptions
-
-[esp32c3_common_LittleFS]
-extends = esp32_base_idf5
-build_flags = ${esp32_base_idf5.build_flags}
- -DFEATURE_ARDUINO_OTA=1
- -DUSE_LITTLEFS
- -DESP32C3
-extra_scripts = ${esp32_base_idf5.extra_scripts}
-build_unflags = ${esp32_base_idf5.build_unflags}
- -fexceptions
-board_build.filesystem = littlefs
-
-
-[env:custom_ESP32c3_4M316k_CDC]
-extends = esp32c3_common
-board = esp32c3cdc
-build_flags = ${esp32c3_common.build_flags}
- -DPLUGIN_BUILD_CUSTOM
-extra_scripts = ${esp32c3_common.extra_scripts}
- pre:tools/pio/pre_custom_esp32.py
-
-
-[env:custom_IR_ESP32c3_4M316k_CDC]
-extends = esp32c3_common
-board = esp32c3cdc
-build_flags = ${esp32c3_common.build_flags}
- -DPLUGIN_BUILD_CUSTOM
- -DPLUGIN_BUILD_IR
-lib_ignore = ${esp32_always.lib_ignore}
- ESP32_ping
-extra_scripts = ${esp32c3_common.extra_scripts}
- pre:tools/pio/pre_custom_esp32_IR.py
- pre:tools/pio/ir_build_check.py
-
-; [env:custom_ESP32c3_4M316k_LittleFS_CDC]
-; extends = esp32c3_common_LittleFS
-; board = esp32c3cdc
-; build_flags = ${esp32c3_common_LittleFS.build_flags}
-; -DPLUGIN_BUILD_CUSTOM
-; extra_scripts = ${esp32c3_common_LittleFS.extra_scripts}
-; pre:tools/pio/pre_custom_esp32.py
-
-[env:custom_ESP32c3_4M316k_LittleFS_CDC_ETH]
-extends = esp32c3_common_LittleFS
-board = esp32c3cdc
-build_flags = ${esp32c3_common_LittleFS.build_flags}
- -DPLUGIN_BUILD_CUSTOM
- -DFEATURE_ETHERNET=1
-extra_scripts = ${esp32c3_common_LittleFS.extra_scripts}
- pre:tools/pio/pre_custom_esp32.py
-
-
-
-[env:normal_ESP32c3_4M316k_CDC]
-extends = esp32c3_common
-board = esp32c3cdc
-lib_ignore = ${esp32_common.lib_ignore}
- ${no_ir.lib_ignore}
-
-
-[env:normal_ESP32c3_4M316k_LittleFS_CDC_ETH]
-extends = esp32c3_common_LittleFS
-board = esp32c3cdc
-build_flags = ${esp32c3_common_LittleFS.build_flags}
- -DFEATURE_ETHERNET=1
-lib_ignore = ${esp32c3_common_LittleFS.lib_ignore}
- ${no_ir.lib_ignore}
-
-[env:collection_A_ESP32c3_4M316k_CDC]
-extends = esp32c3_common
-board = esp32c3cdc
-build_flags = ${esp32c3_common.build_flags}
- -DPLUGIN_SET_COLLECTION_ESP32
- -DCOLLECTION_USE_RTTTL
-
-[env:collection_B_ESP32c3_4M316k_CDC]
-extends = esp32c3_common
-board = esp32c3cdc
-build_flags = ${esp32c3_common.build_flags}
- -DPLUGIN_SET_COLLECTION_B_ESP32
- -DCOLLECTION_USE_RTTTL
-
-[env:collection_C_ESP32c3_4M316k_CDC]
-extends = esp32c3_common
-board = esp32c3cdc
-build_flags = ${esp32c3_common.build_flags}
- -DPLUGIN_SET_COLLECTION_C_ESP32
- -DCOLLECTION_USE_RTTTL
-
-[env:collection_D_ESP32c3_4M316k_CDC]
-extends = esp32c3_common
-board = esp32c3cdc
-build_flags = ${esp32c3_common.build_flags}
- -DPLUGIN_SET_COLLECTION_D_ESP32
- -DCOLLECTION_USE_RTTTL
-
-[env:collection_E_ESP32c3_4M316k_CDC]
-extends = esp32c3_common
-board = esp32c3cdc
-build_flags = ${esp32c3_common.build_flags}
- -DPLUGIN_SET_COLLECTION_E_ESP32
- -DCOLLECTION_USE_RTTTL
-
-[env:collection_F_ESP32c3_4M316k_CDC]
-extends = esp32c3_common
-board = esp32c3cdc
-build_flags = ${esp32c3_common.build_flags}
- -DPLUGIN_SET_COLLECTION_F_ESP32
- -DCOLLECTION_USE_RTTTL
-
-[env:collection_G_ESP32c3_4M316k_CDC]
-extends = esp32c3_common
-board = esp32c3cdc
-build_flags = ${esp32c3_common.build_flags}
- -DPLUGIN_SET_COLLECTION_G_ESP32
- -DCOLLECTION_USE_RTTTL
-
-
-[env:energy_ESP32c3_4M316k_CDC]
-extends = esp32c3_common
-board = esp32c3cdc
-build_flags = ${esp32c3_common.build_flags}
- -D PLUGIN_ENERGY_COLLECTION
-
-[env:energy_ESP32c3_4M316k_LittleFS_CDC_ETH]
-extends = esp32c3_common_LittleFS
-board = esp32c3cdc
-build_flags = ${esp32c3_common_LittleFS.build_flags}
- -D PLUGIN_ENERGY_COLLECTION
- -DFEATURE_ETHERNET=1
-
-[env:display_ESP32c3_4M316k_CDC]
-extends = esp32c3_common
-board = esp32c3cdc
-build_flags = ${esp32c3_common.build_flags}
- -D PLUGIN_DISPLAY_COLLECTION
-
-[env:climate_ESP32c3_4M316k_CDC]
-extends = esp32c3_common
-board = esp32c3cdc
-build_flags = ${esp32c3_common.build_flags}
- -D PLUGIN_CLIMATE_COLLECTION
-
-[env:neopixel_ESP32c3_4M316k_CDC]
-extends = esp32c3_common
-board = esp32c3cdc
-build_flags = ${esp32c3_common.build_flags}
- -DFEATURE_ARDUINO_OTA=1
- -DFEATURE_SD=1
- -DPLUGIN_NEOPIXEL_COLLECTION
-
-[env:neopixel_ESP32c3_4M316k_LittleFS_CDC_ETH]
-extends = esp32c3_common_LittleFS
-board = esp32c3cdc
-build_flags = ${esp32c3_common_LittleFS.build_flags}
- -DFEATURE_ARDUINO_OTA=1
- -DFEATURE_SD=1
- -DFEATURE_ETHERNET=1
- -DPLUGIN_NEOPIXEL_COLLECTION
+
+
+
+[esp32c3_common]
+extends = esp32_base
+lib_ignore = ${esp32_always.lib_ignore}
+ ESP32_ping
+ ${no_ir.lib_ignore}
+ ESP32 BLE Arduino
+build_flags = ${esp32_base.build_flags}
+ -DFEATURE_ARDUINO_OTA=1
+ -DESP32C3
+extra_scripts = ${esp32_base.extra_scripts}
+build_unflags = ${esp32_base.build_unflags}
+ -fexceptions
+
+[esp32c3_common_LittleFS]
+extends = esp32_base_idf5
+build_flags = ${esp32_base_idf5.build_flags}
+ -DFEATURE_ARDUINO_OTA=1
+ -DUSE_LITTLEFS
+ -DESP32C3
+extra_scripts = ${esp32_base_idf5.extra_scripts}
+build_unflags = ${esp32_base_idf5.build_unflags}
+ -fexceptions
+board_build.filesystem = littlefs
+
+
+[env:custom_ESP32c3_4M316k_CDC]
+extends = esp32c3_common
+board = esp32c3cdc
+build_flags = ${esp32c3_common.build_flags}
+ -DPLUGIN_BUILD_CUSTOM
+extra_scripts = ${esp32c3_common.extra_scripts}
+ pre:tools/pio/pre_custom_esp32.py
+
+
+[env:custom_IR_ESP32c3_4M316k_CDC]
+extends = esp32c3_common
+board = esp32c3cdc
+build_flags = ${esp32c3_common.build_flags}
+ -DPLUGIN_BUILD_CUSTOM
+ -DPLUGIN_BUILD_IR
+lib_ignore = ${esp32_always.lib_ignore}
+ ESP32_ping
+extra_scripts = ${esp32c3_common.extra_scripts}
+ pre:tools/pio/pre_custom_esp32_IR.py
+ pre:tools/pio/ir_build_check.py
+
+; [env:custom_ESP32c3_4M316k_LittleFS_CDC]
+; extends = esp32c3_common_LittleFS
+; board = esp32c3cdc
+; build_flags = ${esp32c3_common_LittleFS.build_flags}
+; -DPLUGIN_BUILD_CUSTOM
+; extra_scripts = ${esp32c3_common_LittleFS.extra_scripts}
+; pre:tools/pio/pre_custom_esp32.py
+
+[env:custom_ESP32c3_4M316k_LittleFS_CDC_ETH]
+extends = esp32c3_common_LittleFS
+board = esp32c3cdc
+build_flags = ${esp32c3_common_LittleFS.build_flags}
+ -DPLUGIN_BUILD_CUSTOM
+ -DFEATURE_ETHERNET=1
+extra_scripts = ${esp32c3_common_LittleFS.extra_scripts}
+ pre:tools/pio/pre_custom_esp32.py
+
+
+
+[env:normal_ESP32c3_4M316k_CDC]
+extends = esp32c3_common
+board = esp32c3cdc
+lib_ignore = ${esp32_common.lib_ignore}
+ ${no_ir.lib_ignore}
+
+
+[env:normal_ESP32c3_4M316k_LittleFS_CDC_ETH]
+extends = esp32c3_common_LittleFS
+board = esp32c3cdc
+build_flags = ${esp32c3_common_LittleFS.build_flags}
+ -DFEATURE_ETHERNET=1
+lib_ignore = ${esp32c3_common_LittleFS.lib_ignore}
+ ${no_ir.lib_ignore}
+
+[env:collection_A_ESP32c3_4M316k_CDC]
+extends = esp32c3_common
+board = esp32c3cdc
+build_flags = ${esp32c3_common.build_flags}
+ -DPLUGIN_SET_COLLECTION_ESP32
+ -DCOLLECTION_USE_RTTTL
+
+[env:collection_B_ESP32c3_4M316k_CDC]
+extends = esp32c3_common
+board = esp32c3cdc
+build_flags = ${esp32c3_common.build_flags}
+ -DPLUGIN_SET_COLLECTION_B_ESP32
+ -DCOLLECTION_USE_RTTTL
+
+[env:collection_C_ESP32c3_4M316k_CDC]
+extends = esp32c3_common
+board = esp32c3cdc
+build_flags = ${esp32c3_common.build_flags}
+ -DPLUGIN_SET_COLLECTION_C_ESP32
+ -DCOLLECTION_USE_RTTTL
+
+[env:collection_D_ESP32c3_4M316k_CDC]
+extends = esp32c3_common
+board = esp32c3cdc
+build_flags = ${esp32c3_common.build_flags}
+ -DPLUGIN_SET_COLLECTION_D_ESP32
+ -DCOLLECTION_USE_RTTTL
+
+[env:collection_E_ESP32c3_4M316k_CDC]
+extends = esp32c3_common
+board = esp32c3cdc
+build_flags = ${esp32c3_common.build_flags}
+ -DPLUGIN_SET_COLLECTION_E_ESP32
+ -DCOLLECTION_USE_RTTTL
+
+[env:collection_F_ESP32c3_4M316k_CDC]
+extends = esp32c3_common
+board = esp32c3cdc
+build_flags = ${esp32c3_common.build_flags}
+ -DPLUGIN_SET_COLLECTION_F_ESP32
+ -DCOLLECTION_USE_RTTTL
+
+[env:collection_G_ESP32c3_4M316k_CDC]
+extends = esp32c3_common
+board = esp32c3cdc
+build_flags = ${esp32c3_common.build_flags}
+ -DPLUGIN_SET_COLLECTION_G_ESP32
+ -DCOLLECTION_USE_RTTTL
+
+
+[env:energy_ESP32c3_4M316k_CDC]
+extends = esp32c3_common
+board = esp32c3cdc
+build_flags = ${esp32c3_common.build_flags}
+ -D PLUGIN_ENERGY_COLLECTION
+
+[env:energy_ESP32c3_4M316k_LittleFS_CDC_ETH]
+extends = esp32c3_common_LittleFS
+board = esp32c3cdc
+build_flags = ${esp32c3_common_LittleFS.build_flags}
+ -D PLUGIN_ENERGY_COLLECTION
+ -DFEATURE_ETHERNET=1
+
+[env:display_ESP32c3_4M316k_CDC]
+extends = esp32c3_common
+board = esp32c3cdc
+build_flags = ${esp32c3_common.build_flags}
+ -D PLUGIN_DISPLAY_COLLECTION
+
+[env:climate_ESP32c3_4M316k_CDC]
+extends = esp32c3_common
+board = esp32c3cdc
+build_flags = ${esp32c3_common.build_flags}
+ -D PLUGIN_CLIMATE_COLLECTION
+
+[env:neopixel_ESP32c3_4M316k_CDC]
+extends = esp32c3_common
+board = esp32c3cdc
+build_flags = ${esp32c3_common.build_flags}
+ -DFEATURE_ARDUINO_OTA=1
+ -DFEATURE_SD=1
+ -DPLUGIN_NEOPIXEL_COLLECTION
+
+[env:neopixel_ESP32c3_4M316k_LittleFS_CDC_ETH]
+extends = esp32c3_common_LittleFS
+board = esp32c3cdc
+build_flags = ${esp32c3_common_LittleFS.build_flags}
+ -DFEATURE_ARDUINO_OTA=1
+ -DFEATURE_SD=1
+ -DFEATURE_ETHERNET=1
+ -DPLUGIN_NEOPIXEL_COLLECTION
diff --git a/platformio_esp32c6_envs.ini b/platformio_esp32c6_envs.ini
index 461498bb1e..af5e25d2c4 100644
--- a/platformio_esp32c6_envs.ini
+++ b/platformio_esp32c6_envs.ini
@@ -1,55 +1,55 @@
-
-
-[esp32c6_common_LittleFS]
-extends = esp32_base_idf5
-build_flags = ${esp32_base_idf5.build_flags}
- -DFEATURE_ARDUINO_OTA=1
- -DUSE_LITTLEFS
- -DESP32C6
-extra_scripts = ${esp32_base_idf5.extra_scripts}
-build_unflags = ${esp32_base_idf5.build_unflags}
- -fexceptions
-board_build.filesystem = littlefs
-lib_ignore = ${esp32_base_idf5.lib_ignore}
-board = esp32c6cdc
-
-
-[env:custom_ESP32c6_4M316k_LittleFS_CDC_ETH]
-extends = esp32c6_common_LittleFS
-build_flags = ${esp32c6_common_LittleFS.build_flags}
- -DPLUGIN_BUILD_CUSTOM
- -DFEATURE_ETHERNET=1
-extra_scripts = ${esp32c6_common_LittleFS.extra_scripts}
- pre:tools/pio/pre_custom_esp32c6.py
-
-
-[env:normal_ESP32c6_4M316k_LittleFS_CDC_ETH]
-extends = esp32c6_common_LittleFS
-build_flags = ${esp32c6_common_LittleFS.build_flags}
- -DFEATURE_ETHERNET=1
-lib_ignore = ${esp32c6_common_LittleFS.lib_ignore}
- ${no_ir.lib_ignore}
-
-
-[env:max_ESP32c6_8M1M_LittleFS_CDC_ETH]
-extends = esp32c6_common_LittleFS
-board = esp32c6cdc-8M
-build_flags = ${esp32c6_common_LittleFS.build_flags}
- -DFEATURE_ETHERNET=1
- -DFEATURE_ARDUINO_OTA=1
- -DPLUGIN_BUILD_MAX_ESP32
- -DPLUGIN_BUILD_IR_EXTENDED
-extra_scripts = ${esp32c6_common_LittleFS.extra_scripts}
-
-
-[env:max_ESP32c6_16M8M_LittleFS_CDC_ETH]
-extends = esp32c6_common_LittleFS
-board = esp32c6cdc-16M
-build_flags = ${esp32c6_common_LittleFS.build_flags}
- -DFEATURE_ETHERNET=1
- -DFEATURE_ARDUINO_OTA=1
- -DPLUGIN_BUILD_MAX_ESP32
- -DPLUGIN_BUILD_IR_EXTENDED
-extra_scripts = ${esp32c6_common_LittleFS.extra_scripts}
-
-
+
+
+[esp32c6_common_LittleFS]
+extends = esp32_base_idf5
+build_flags = ${esp32_base_idf5.build_flags}
+ -DFEATURE_ARDUINO_OTA=1
+ -DUSE_LITTLEFS
+ -DESP32C6
+extra_scripts = ${esp32_base_idf5.extra_scripts}
+build_unflags = ${esp32_base_idf5.build_unflags}
+ -fexceptions
+board_build.filesystem = littlefs
+lib_ignore = ${esp32_base_idf5.lib_ignore}
+board = esp32c6cdc
+
+
+[env:custom_ESP32c6_4M316k_LittleFS_CDC_ETH]
+extends = esp32c6_common_LittleFS
+build_flags = ${esp32c6_common_LittleFS.build_flags}
+ -DPLUGIN_BUILD_CUSTOM
+ -DFEATURE_ETHERNET=1
+extra_scripts = ${esp32c6_common_LittleFS.extra_scripts}
+ pre:tools/pio/pre_custom_esp32c6.py
+
+
+[env:normal_ESP32c6_4M316k_LittleFS_CDC_ETH]
+extends = esp32c6_common_LittleFS
+build_flags = ${esp32c6_common_LittleFS.build_flags}
+ -DFEATURE_ETHERNET=1
+lib_ignore = ${esp32c6_common_LittleFS.lib_ignore}
+ ${no_ir.lib_ignore}
+
+
+[env:max_ESP32c6_8M1M_LittleFS_CDC_ETH]
+extends = esp32c6_common_LittleFS
+board = esp32c6cdc-8M
+build_flags = ${esp32c6_common_LittleFS.build_flags}
+ -DFEATURE_ETHERNET=1
+ -DFEATURE_ARDUINO_OTA=1
+ -DPLUGIN_BUILD_MAX_ESP32
+ -DPLUGIN_BUILD_IR_EXTENDED
+extra_scripts = ${esp32c6_common_LittleFS.extra_scripts}
+
+
+[env:max_ESP32c6_16M8M_LittleFS_CDC_ETH]
+extends = esp32c6_common_LittleFS
+board = esp32c6cdc-16M
+build_flags = ${esp32c6_common_LittleFS.build_flags}
+ -DFEATURE_ETHERNET=1
+ -DFEATURE_ARDUINO_OTA=1
+ -DPLUGIN_BUILD_MAX_ESP32
+ -DPLUGIN_BUILD_IR_EXTENDED
+extra_scripts = ${esp32c6_common_LittleFS.extra_scripts}
+
+
diff --git a/platformio_esp32s2_envs.ini b/platformio_esp32s2_envs.ini
index f4b530945f..4419571ba0 100644
--- a/platformio_esp32s2_envs.ini
+++ b/platformio_esp32s2_envs.ini
@@ -1,167 +1,167 @@
-
-
-
-
-[esp32s2_common]
-extends = esp32_base
-lib_ignore = ${esp32_always.lib_ignore}
- ESP32_ping
- ${no_ir.lib_ignore}
- ESP32 BLE Arduino
-build_flags = ${esp32_base.build_flags}
- -DFEATURE_ARDUINO_OTA=1
- -DESP32S2
-extra_scripts = ${esp32_base.extra_scripts}
-build_unflags = ${esp32_base.build_unflags}
- -fexceptions
-
-[esp32s2_common_LittleFS]
-extends = esp32_base_idf5
-build_flags = ${esp32_base_idf5.build_flags}
- -DFEATURE_ARDUINO_OTA=1
- -DUSE_LITTLEFS
- -DESP32S2
-extra_scripts = ${esp32_base_idf5.extra_scripts}
-build_unflags = ${esp32_base_idf5.build_unflags}
- -fexceptions
-board_build.filesystem = littlefs
-
-
-[env:custom_ESP32s2_4M316k_CDC]
-extends = esp32s2_common
-board = esp32s2cdc
-build_flags = ${esp32s2_common.build_flags}
- -DPLUGIN_BUILD_CUSTOM
- -DESP_CONSOLE_USB_CDC=y
-extra_scripts = ${esp32s2_common.extra_scripts}
- pre:tools/pio/pre_custom_esp32.py
-
-[env:neopixel_ESP32s2_4M316k_CDC]
-extends = esp32s2_common
-board = esp32s2cdc
-build_flags = ${esp32s2_common.build_flags}
- -DFEATURE_ARDUINO_OTA=1
- -DFEATURE_SD=1
- -DPLUGIN_NEOPIXEL_COLLECTION
-
-[env:neopixel_ESP32s2_4M316k_LittleFS_CDC_ETH]
-extends = esp32s2_common_LittleFS
-board = esp32s2cdc
-build_flags = ${esp32s2_common_LittleFS.build_flags}
- -DFEATURE_ARDUINO_OTA=1
- -DFEATURE_SD=1
- -DPLUGIN_NEOPIXEL_COLLECTION
- -DFEATURE_ETHERNET=1
-
-
-[env:custom_IR_ESP32s2_4M316k_CDC]
-extends = esp32s2_common
-board = esp32s2cdc
-build_flags = ${esp32s2_common.build_flags}
- -DPLUGIN_BUILD_CUSTOM
- -DPLUGIN_BUILD_IR
-lib_ignore = ${esp32_always.lib_ignore}
- ESP32_ping
-extra_scripts = ${esp32s2_common.extra_scripts}
- pre:tools/pio/pre_custom_esp32_IR.py
- pre:tools/pio/ir_build_check.py
-
-
-
-[env:normal_ESP32s2_4M316k_CDC]
-extends = esp32s2_common
-board = esp32s2cdc
-lib_ignore = ${esp32s2_common.lib_ignore}
- ${no_ir.lib_ignore}
-
-[env:custom_ESP32s2_4M316k_LittleFS_CDC_ETH]
-extends = esp32s2_common_LittleFS
-board = esp32s2cdc
-lib_ignore = ${esp32s2_common_LittleFS.lib_ignore}
- ${no_ir.lib_ignore}
-build_flags = ${esp32s2_common_LittleFS.build_flags}
- -DPLUGIN_BUILD_CUSTOM
- -DESP_CONSOLE_USB_CDC=y
- -DFEATURE_ETHERNET=1
-extra_scripts = ${esp32s2_common_LittleFS.extra_scripts}
- pre:tools/pio/pre_custom_esp32.py
-
-
-
-[env:normal_ESP32s2_4M316k_LittleFS_CDC_ETH]
-extends = esp32s2_common_LittleFS
-board = esp32s2cdc
-build_flags = ${esp32s2_common_LittleFS.build_flags}
- -DESP_CONSOLE_USB_CDC=y
- -DFEATURE_ETHERNET=1
-lib_ignore = ${esp32s2_common_LittleFS.lib_ignore}
- ${no_ir.lib_ignore}
-
-[env:collection_A_ESP32s2_4M316k_CDC]
-extends = esp32s2_common
-board = esp32s2cdc
-build_flags = ${esp32s2_common.build_flags}
- -DPLUGIN_SET_COLLECTION_ESP32
- -DCOLLECTION_USE_RTTTL
-
-[env:collection_B_ESP32s2_4M316k_CDC]
-extends = esp32s2_common
-board = esp32s2cdc
-build_flags = ${esp32s2_common.build_flags}
- -DPLUGIN_SET_COLLECTION_B_ESP32
- -DCOLLECTION_USE_RTTTL
-
-[env:collection_C_ESP32s2_4M316k_CDC]
-extends = esp32s2_common
-board = esp32s2cdc
-build_flags = ${esp32s2_common.build_flags}
- -DPLUGIN_SET_COLLECTION_C_ESP32
- -DCOLLECTION_USE_RTTTL
-
-[env:collection_D_ESP32s2_4M316k_CDC]
-extends = esp32s2_common
-board = esp32s2cdc
-build_flags = ${esp32s2_common.build_flags}
- -DPLUGIN_SET_COLLECTION_D_ESP32
- -DCOLLECTION_USE_RTTTL
-
-[env:collection_E_ESP32s2_4M316k_CDC]
-extends = esp32s2_common
-board = esp32s2cdc
-build_flags = ${esp32s2_common.build_flags}
- -DPLUGIN_SET_COLLECTION_E_ESP32
- -DCOLLECTION_USE_RTTTL
-
-[env:collection_F_ESP32s2_4M316k_CDC]
-extends = esp32s2_common
-board = esp32s2cdc
-build_flags = ${esp32s2_common.build_flags}
- -DPLUGIN_SET_COLLECTION_F_ESP32
- -DCOLLECTION_USE_RTTTL
-
-[env:collection_G_ESP32s2_4M316k_CDC]
-extends = esp32s2_common
-board = esp32s2cdc
-build_flags = ${esp32s2_common.build_flags}
- -DPLUGIN_SET_COLLECTION_G_ESP32
- -DCOLLECTION_USE_RTTTL
-
-
-[env:energy_ESP32s2_4M316k_CDC]
-extends = esp32s2_common
-board = esp32s2cdc
-build_flags = ${esp32s2_common.build_flags}
- -D PLUGIN_ENERGY_COLLECTION
-
-[env:display_ESP32s2_4M316k_CDC]
-extends = esp32s2_common
-board = esp32s2cdc
-build_flags = ${esp32s2_common.build_flags}
- -D PLUGIN_DISPLAY_COLLECTION
-
-[env:climate_ESP32s2_4M316k_CDC]
-extends = esp32s2_common
-board = esp32s2cdc
-build_flags = ${esp32s2_common.build_flags}
- -D PLUGIN_CLIMATE_COLLECTION
-
+
+
+
+
+[esp32s2_common]
+extends = esp32_base
+lib_ignore = ${esp32_always.lib_ignore}
+ ESP32_ping
+ ${no_ir.lib_ignore}
+ ESP32 BLE Arduino
+build_flags = ${esp32_base.build_flags}
+ -DFEATURE_ARDUINO_OTA=1
+ -DESP32S2
+extra_scripts = ${esp32_base.extra_scripts}
+build_unflags = ${esp32_base.build_unflags}
+ -fexceptions
+
+[esp32s2_common_LittleFS]
+extends = esp32_base_idf5
+build_flags = ${esp32_base_idf5.build_flags}
+ -DFEATURE_ARDUINO_OTA=1
+ -DUSE_LITTLEFS
+ -DESP32S2
+extra_scripts = ${esp32_base_idf5.extra_scripts}
+build_unflags = ${esp32_base_idf5.build_unflags}
+ -fexceptions
+board_build.filesystem = littlefs
+
+
+[env:custom_ESP32s2_4M316k_CDC]
+extends = esp32s2_common
+board = esp32s2cdc
+build_flags = ${esp32s2_common.build_flags}
+ -DPLUGIN_BUILD_CUSTOM
+ -DESP_CONSOLE_USB_CDC=y
+extra_scripts = ${esp32s2_common.extra_scripts}
+ pre:tools/pio/pre_custom_esp32.py
+
+[env:neopixel_ESP32s2_4M316k_CDC]
+extends = esp32s2_common
+board = esp32s2cdc
+build_flags = ${esp32s2_common.build_flags}
+ -DFEATURE_ARDUINO_OTA=1
+ -DFEATURE_SD=1
+ -DPLUGIN_NEOPIXEL_COLLECTION
+
+[env:neopixel_ESP32s2_4M316k_LittleFS_CDC_ETH]
+extends = esp32s2_common_LittleFS
+board = esp32s2cdc
+build_flags = ${esp32s2_common_LittleFS.build_flags}
+ -DFEATURE_ARDUINO_OTA=1
+ -DFEATURE_SD=1
+ -DPLUGIN_NEOPIXEL_COLLECTION
+ -DFEATURE_ETHERNET=1
+
+
+[env:custom_IR_ESP32s2_4M316k_CDC]
+extends = esp32s2_common
+board = esp32s2cdc
+build_flags = ${esp32s2_common.build_flags}
+ -DPLUGIN_BUILD_CUSTOM
+ -DPLUGIN_BUILD_IR
+lib_ignore = ${esp32_always.lib_ignore}
+ ESP32_ping
+extra_scripts = ${esp32s2_common.extra_scripts}
+ pre:tools/pio/pre_custom_esp32_IR.py
+ pre:tools/pio/ir_build_check.py
+
+
+
+[env:normal_ESP32s2_4M316k_CDC]
+extends = esp32s2_common
+board = esp32s2cdc
+lib_ignore = ${esp32s2_common.lib_ignore}
+ ${no_ir.lib_ignore}
+
+[env:custom_ESP32s2_4M316k_LittleFS_CDC_ETH]
+extends = esp32s2_common_LittleFS
+board = esp32s2cdc
+lib_ignore = ${esp32s2_common_LittleFS.lib_ignore}
+ ${no_ir.lib_ignore}
+build_flags = ${esp32s2_common_LittleFS.build_flags}
+ -DPLUGIN_BUILD_CUSTOM
+ -DESP_CONSOLE_USB_CDC=y
+ -DFEATURE_ETHERNET=1
+extra_scripts = ${esp32s2_common_LittleFS.extra_scripts}
+ pre:tools/pio/pre_custom_esp32.py
+
+
+
+[env:normal_ESP32s2_4M316k_LittleFS_CDC_ETH]
+extends = esp32s2_common_LittleFS
+board = esp32s2cdc
+build_flags = ${esp32s2_common_LittleFS.build_flags}
+ -DESP_CONSOLE_USB_CDC=y
+ -DFEATURE_ETHERNET=1
+lib_ignore = ${esp32s2_common_LittleFS.lib_ignore}
+ ${no_ir.lib_ignore}
+
+[env:collection_A_ESP32s2_4M316k_CDC]
+extends = esp32s2_common
+board = esp32s2cdc
+build_flags = ${esp32s2_common.build_flags}
+ -DPLUGIN_SET_COLLECTION_ESP32
+ -DCOLLECTION_USE_RTTTL
+
+[env:collection_B_ESP32s2_4M316k_CDC]
+extends = esp32s2_common
+board = esp32s2cdc
+build_flags = ${esp32s2_common.build_flags}
+ -DPLUGIN_SET_COLLECTION_B_ESP32
+ -DCOLLECTION_USE_RTTTL
+
+[env:collection_C_ESP32s2_4M316k_CDC]
+extends = esp32s2_common
+board = esp32s2cdc
+build_flags = ${esp32s2_common.build_flags}
+ -DPLUGIN_SET_COLLECTION_C_ESP32
+ -DCOLLECTION_USE_RTTTL
+
+[env:collection_D_ESP32s2_4M316k_CDC]
+extends = esp32s2_common
+board = esp32s2cdc
+build_flags = ${esp32s2_common.build_flags}
+ -DPLUGIN_SET_COLLECTION_D_ESP32
+ -DCOLLECTION_USE_RTTTL
+
+[env:collection_E_ESP32s2_4M316k_CDC]
+extends = esp32s2_common
+board = esp32s2cdc
+build_flags = ${esp32s2_common.build_flags}
+ -DPLUGIN_SET_COLLECTION_E_ESP32
+ -DCOLLECTION_USE_RTTTL
+
+[env:collection_F_ESP32s2_4M316k_CDC]
+extends = esp32s2_common
+board = esp32s2cdc
+build_flags = ${esp32s2_common.build_flags}
+ -DPLUGIN_SET_COLLECTION_F_ESP32
+ -DCOLLECTION_USE_RTTTL
+
+[env:collection_G_ESP32s2_4M316k_CDC]
+extends = esp32s2_common
+board = esp32s2cdc
+build_flags = ${esp32s2_common.build_flags}
+ -DPLUGIN_SET_COLLECTION_G_ESP32
+ -DCOLLECTION_USE_RTTTL
+
+
+[env:energy_ESP32s2_4M316k_CDC]
+extends = esp32s2_common
+board = esp32s2cdc
+build_flags = ${esp32s2_common.build_flags}
+ -D PLUGIN_ENERGY_COLLECTION
+
+[env:display_ESP32s2_4M316k_CDC]
+extends = esp32s2_common
+board = esp32s2cdc
+build_flags = ${esp32s2_common.build_flags}
+ -D PLUGIN_DISPLAY_COLLECTION
+
+[env:climate_ESP32s2_4M316k_CDC]
+extends = esp32s2_common
+board = esp32s2cdc
+build_flags = ${esp32s2_common.build_flags}
+ -D PLUGIN_CLIMATE_COLLECTION
+
diff --git a/platformio_esp32s3_envs.ini b/platformio_esp32s3_envs.ini
index a3d2a4e0b9..59ce3af294 100644
--- a/platformio_esp32s3_envs.ini
+++ b/platformio_esp32s3_envs.ini
@@ -1,220 +1,220 @@
-
-
-
-
-[esp32s3_common]
-extends = esp32_base
-lib_ignore = ${esp32_always.lib_ignore}
- ESP32_ping
- ${no_ir.lib_ignore}
- ESP32 BLE Arduino
-build_flags = ${esp32_base.build_flags}
- -DFEATURE_ARDUINO_OTA=1
- -DESP32S3
-extra_scripts = ${esp32_base.extra_scripts}
-build_unflags = ${esp32_base.build_unflags}
- -fexceptions
-
-[esp32s3_common_LittleFS]
-extends = esp32_base_idf5
-lib_ignore = ${esp32_common_LittleFS.lib_ignore}
- ESP32_ping
- ${esp32_base_idf5.lib_ignore}
-build_flags = ${esp32_base_idf5.build_flags}
-; -mtext-section-literals
- -DFEATURE_ARDUINO_OTA=1
- -DUSE_LITTLEFS
- -DESP32S3
-extra_scripts = ${esp32_base_idf5.extra_scripts}
-build_unflags = ${esp32_base_idf5.build_unflags}
- -fexceptions
-board_build.filesystem = littlefs
-
-
-[env:custom_ESP32s3_4M316k_CDC]
-extends = esp32s3_common
-board = esp32s3cdc-qio_qspi
-build_flags = ${esp32s3_common.build_flags}
- -DPLUGIN_BUILD_CUSTOM
-extra_scripts = ${esp32s3_common.extra_scripts}
- pre:tools/pio/pre_custom_esp32.py
-
-
-[env:custom_IR_ESP32s3_4M316k_CDC]
-extends = esp32s3_common
-board = esp32s3cdc-qio_qspi
-build_flags = ${esp32s3_common.build_flags}
- -DPLUGIN_BUILD_CUSTOM
- -DPLUGIN_BUILD_IR
-lib_ignore = ${esp32_always.lib_ignore}
- ESP32_ping
-extra_scripts = ${esp32s3_common.extra_scripts}
- pre:tools/pio/pre_custom_esp32_IR.py
- pre:tools/pio/ir_build_check.py
-
-
-
-[env:normal_ESP32s3_4M316k_CDC]
-extends = esp32s3_common
-board = esp32s3cdc-qio_qspi
-lib_ignore = ${esp32s3_common.lib_ignore}
- ${no_ir.lib_ignore}
-
-
-[env:collection_A_ESP32s3_4M316k_CDC]
-extends = esp32s3_common
-board = esp32s3cdc-qio_qspi
-build_flags = ${esp32s3_common.build_flags}
- -DPLUGIN_SET_COLLECTION_ESP32
- -DCOLLECTION_USE_RTTTL
-
-[env:collection_B_ESP32s3_4M316k_CDC]
-extends = esp32s3_common
-board = esp32s3cdc-qio_qspi
-build_flags = ${esp32s3_common.build_flags}
- -DPLUGIN_SET_COLLECTION_B_ESP32
- -DCOLLECTION_USE_RTTTL
-
-[env:collection_C_ESP32s3_4M316k_CDC]
-extends = esp32s3_common
-board = esp32s3cdc-qio_qspi
-build_flags = ${esp32s3_common.build_flags}
- -DPLUGIN_SET_COLLECTION_C_ESP32
- -DCOLLECTION_USE_RTTTL
-
-[env:collection_D_ESP32s3_4M316k_CDC]
-extends = esp32s3_common
-board = esp32s3cdc-qio_qspi
-build_flags = ${esp32s3_common.build_flags}
- -DPLUGIN_SET_COLLECTION_D_ESP32
- -DCOLLECTION_USE_RTTTL
-
-[env:collection_E_ESP32s3_4M316k_CDC]
-extends = esp32s3_common
-board = esp32s3cdc-qio_qspi
-build_flags = ${esp32s3_common.build_flags}
- -DPLUGIN_SET_COLLECTION_E_ESP32
- -DCOLLECTION_USE_RTTTL
-
-[env:collection_F_ESP32s3_4M316k_CDC]
-extends = esp32s3_common
-board = esp32s3cdc-qio_qspi
-build_flags = ${esp32s3_common.build_flags}
- -DPLUGIN_SET_COLLECTION_F_ESP32
- -DCOLLECTION_USE_RTTTL
-
-[env:collection_G_ESP32s3_4M316k_CDC]
-extends = esp32s3_common
-board = esp32s3cdc-qio_qspi
-build_flags = ${esp32s3_common.build_flags}
- -DPLUGIN_SET_COLLECTION_G_ESP32
- -DCOLLECTION_USE_RTTTL
-
-
-[env:energy_ESP32s3_4M316k_CDC]
-extends = esp32s3_common
-board = esp32s3cdc-qio_qspi
-build_flags = ${esp32s3_common.build_flags}
- -D PLUGIN_ENERGY_COLLECTION
-
-[env:display_ESP32s3_4M316k_CDC]
-extends = esp32s3_common
-board = esp32s3cdc-qio_qspi
-build_flags = ${esp32s3_common.build_flags}
- -D PLUGIN_DISPLAY_COLLECTION
-
-[env:climate_ESP32s3_4M316k_CDC]
-extends = esp32s3_common
-board = esp32s3cdc-qio_qspi
-build_flags = ${esp32s3_common.build_flags}
- -D PLUGIN_CLIMATE_COLLECTION
-
-[env:neopixel_ESP32s3_4M316k_CDC]
-extends = esp32s3_common
-board = esp32s3cdc-qio_qspi
-build_flags = ${esp32s3_common.build_flags}
- -DFEATURE_ARDUINO_OTA=1
- -DFEATURE_SD=1
- -DPLUGIN_NEOPIXEL_COLLECTION
-
-[env:neopixel_ESP32s3_4M316k_LittleFS_CDC_ETH]
-extends = esp32s3_common_LittleFS
-board = esp32s3cdc-qio_qspi
-build_flags = ${esp32s3_common_LittleFS.build_flags}
- -DFEATURE_ARDUINO_OTA=1
- -DFEATURE_SD=1
- -DPLUGIN_NEOPIXEL_COLLECTION
- -DFEATURE_ETHERNET=1
-
-
-[env:custom_ESP32s3_8M1M_LittleFS_CDC_ETH]
-extends = esp32s3_common_LittleFS
-board = esp32s3cdc-qio_qspi-8M
-build_flags = ${esp32s3_common_LittleFS.build_flags}
- -DFEATURE_ETHERNET=1
- -DFEATURE_ARDUINO_OTA=1
- -DPLUGIN_BUILD_CUSTOM
- -DFEATURE_SD=1
-extra_scripts = ${esp32s3_common_LittleFS.extra_scripts}
- pre:tools/pio/pre_custom_esp32.py
-
-[env:custom_ESP32s3_8M1M_LittleFS_OPI_PSRAM_CDC_ETH]
-extends = env:custom_ESP32s3_8M1M_LittleFS_CDC_ETH
-board = esp32s3cdc-qio_opi-8M
-
-
-[env:max_ESP32s3_8M1M_LittleFS_CDC_ETH]
-extends = esp32s3_common_LittleFS
-board = esp32s3cdc-qio_qspi-8M
-build_flags = ${esp32s3_common_LittleFS.build_flags}
- -DFEATURE_ETHERNET=1
- -DFEATURE_ARDUINO_OTA=1
- -DPLUGIN_BUILD_MAX_ESP32
- -DPLUGIN_BUILD_IR_EXTENDED
-extra_scripts = ${esp32s3_common_LittleFS.extra_scripts}
-
-
-[env:max_ESP32s3_8M1M_LittleFS_OPI_PSRAM_CDC_ETH]
-extends = env:max_ESP32s3_8M1M_LittleFS_CDC_ETH
-board = esp32s3cdc-qio_opi-8M
-
-
-[env:custom_ESP32s3_16M8M_LittleFS_CDC_ETH]
-extends = esp32s3_common_LittleFS
-board = esp32s3cdc-qio_qspi-16M
-build_flags = ${esp32s3_common_LittleFS.build_flags}
- -DFEATURE_ETHERNET=1
- -DFEATURE_ARDUINO_OTA=1
- -DPLUGIN_BUILD_CUSTOM
- -DPLUGIN_BUILD_IR_EXTENDED
- -DFEATURE_SD=1
-extra_scripts = ${esp32s3_common_LittleFS.extra_scripts}
- pre:tools/pio/pre_custom_esp32.py
-
-[env:custom_ESP32s3_16M8M_LittleFS_OPI_PSRAM_CDC_ETH]
-extends = env:custom_ESP32s3_16M8M_LittleFS_CDC_ETH
-board = esp32s3cdc-qio_opi-16M
-
-
-[env:max_ESP32s3_16M8M_LittleFS_CDC_ETH]
-extends = esp32s3_common_LittleFS
-board = esp32s3cdc-qio_qspi-16M
-build_flags = ${esp32s3_common_LittleFS.build_flags}
- -DFEATURE_ETHERNET=1
- -DFEATURE_ARDUINO_OTA=1
- -DPLUGIN_BUILD_MAX_ESP32
- -DPLUGIN_BUILD_IR_EXTENDED
-extra_scripts = ${esp32s3_common_LittleFS.extra_scripts}
-
-
-[env:max_ESP32s3_16M8M_LittleFS_OPI_PSRAM_CDC_ETH]
-extends = esp32s3_common_LittleFS
-board = esp32s3cdc-qio_opi-16M
-build_flags = ${esp32s3_common_LittleFS.build_flags}
- -DFEATURE_ETHERNET=1
- -DFEATURE_ARDUINO_OTA=1
- -DPLUGIN_BUILD_MAX_ESP32
- -DPLUGIN_BUILD_IR_EXTENDED
-extra_scripts = ${esp32s3_common_LittleFS.extra_scripts}
-
-
+
+
+
+
+[esp32s3_common]
+extends = esp32_base
+lib_ignore = ${esp32_always.lib_ignore}
+ ESP32_ping
+ ${no_ir.lib_ignore}
+ ESP32 BLE Arduino
+build_flags = ${esp32_base.build_flags}
+ -DFEATURE_ARDUINO_OTA=1
+ -DESP32S3
+extra_scripts = ${esp32_base.extra_scripts}
+build_unflags = ${esp32_base.build_unflags}
+ -fexceptions
+
+[esp32s3_common_LittleFS]
+extends = esp32_base_idf5
+lib_ignore = ${esp32_common_LittleFS.lib_ignore}
+ ESP32_ping
+ ${esp32_base_idf5.lib_ignore}
+build_flags = ${esp32_base_idf5.build_flags}
+; -mtext-section-literals
+ -DFEATURE_ARDUINO_OTA=1
+ -DUSE_LITTLEFS
+ -DESP32S3
+extra_scripts = ${esp32_base_idf5.extra_scripts}
+build_unflags = ${esp32_base_idf5.build_unflags}
+ -fexceptions
+board_build.filesystem = littlefs
+
+
+[env:custom_ESP32s3_4M316k_CDC]
+extends = esp32s3_common
+board = esp32s3cdc-qio_qspi
+build_flags = ${esp32s3_common.build_flags}
+ -DPLUGIN_BUILD_CUSTOM
+extra_scripts = ${esp32s3_common.extra_scripts}
+ pre:tools/pio/pre_custom_esp32.py
+
+
+[env:custom_IR_ESP32s3_4M316k_CDC]
+extends = esp32s3_common
+board = esp32s3cdc-qio_qspi
+build_flags = ${esp32s3_common.build_flags}
+ -DPLUGIN_BUILD_CUSTOM
+ -DPLUGIN_BUILD_IR
+lib_ignore = ${esp32_always.lib_ignore}
+ ESP32_ping
+extra_scripts = ${esp32s3_common.extra_scripts}
+ pre:tools/pio/pre_custom_esp32_IR.py
+ pre:tools/pio/ir_build_check.py
+
+
+
+[env:normal_ESP32s3_4M316k_CDC]
+extends = esp32s3_common
+board = esp32s3cdc-qio_qspi
+lib_ignore = ${esp32s3_common.lib_ignore}
+ ${no_ir.lib_ignore}
+
+
+[env:collection_A_ESP32s3_4M316k_CDC]
+extends = esp32s3_common
+board = esp32s3cdc-qio_qspi
+build_flags = ${esp32s3_common.build_flags}
+ -DPLUGIN_SET_COLLECTION_ESP32
+ -DCOLLECTION_USE_RTTTL
+
+[env:collection_B_ESP32s3_4M316k_CDC]
+extends = esp32s3_common
+board = esp32s3cdc-qio_qspi
+build_flags = ${esp32s3_common.build_flags}
+ -DPLUGIN_SET_COLLECTION_B_ESP32
+ -DCOLLECTION_USE_RTTTL
+
+[env:collection_C_ESP32s3_4M316k_CDC]
+extends = esp32s3_common
+board = esp32s3cdc-qio_qspi
+build_flags = ${esp32s3_common.build_flags}
+ -DPLUGIN_SET_COLLECTION_C_ESP32
+ -DCOLLECTION_USE_RTTTL
+
+[env:collection_D_ESP32s3_4M316k_CDC]
+extends = esp32s3_common
+board = esp32s3cdc-qio_qspi
+build_flags = ${esp32s3_common.build_flags}
+ -DPLUGIN_SET_COLLECTION_D_ESP32
+ -DCOLLECTION_USE_RTTTL
+
+[env:collection_E_ESP32s3_4M316k_CDC]
+extends = esp32s3_common
+board = esp32s3cdc-qio_qspi
+build_flags = ${esp32s3_common.build_flags}
+ -DPLUGIN_SET_COLLECTION_E_ESP32
+ -DCOLLECTION_USE_RTTTL
+
+[env:collection_F_ESP32s3_4M316k_CDC]
+extends = esp32s3_common
+board = esp32s3cdc-qio_qspi
+build_flags = ${esp32s3_common.build_flags}
+ -DPLUGIN_SET_COLLECTION_F_ESP32
+ -DCOLLECTION_USE_RTTTL
+
+[env:collection_G_ESP32s3_4M316k_CDC]
+extends = esp32s3_common
+board = esp32s3cdc-qio_qspi
+build_flags = ${esp32s3_common.build_flags}
+ -DPLUGIN_SET_COLLECTION_G_ESP32
+ -DCOLLECTION_USE_RTTTL
+
+
+[env:energy_ESP32s3_4M316k_CDC]
+extends = esp32s3_common
+board = esp32s3cdc-qio_qspi
+build_flags = ${esp32s3_common.build_flags}
+ -D PLUGIN_ENERGY_COLLECTION
+
+[env:display_ESP32s3_4M316k_CDC]
+extends = esp32s3_common
+board = esp32s3cdc-qio_qspi
+build_flags = ${esp32s3_common.build_flags}
+ -D PLUGIN_DISPLAY_COLLECTION
+
+[env:climate_ESP32s3_4M316k_CDC]
+extends = esp32s3_common
+board = esp32s3cdc-qio_qspi
+build_flags = ${esp32s3_common.build_flags}
+ -D PLUGIN_CLIMATE_COLLECTION
+
+[env:neopixel_ESP32s3_4M316k_CDC]
+extends = esp32s3_common
+board = esp32s3cdc-qio_qspi
+build_flags = ${esp32s3_common.build_flags}
+ -DFEATURE_ARDUINO_OTA=1
+ -DFEATURE_SD=1
+ -DPLUGIN_NEOPIXEL_COLLECTION
+
+[env:neopixel_ESP32s3_4M316k_LittleFS_CDC_ETH]
+extends = esp32s3_common_LittleFS
+board = esp32s3cdc-qio_qspi
+build_flags = ${esp32s3_common_LittleFS.build_flags}
+ -DFEATURE_ARDUINO_OTA=1
+ -DFEATURE_SD=1
+ -DPLUGIN_NEOPIXEL_COLLECTION
+ -DFEATURE_ETHERNET=1
+
+
+[env:custom_ESP32s3_8M1M_LittleFS_CDC_ETH]
+extends = esp32s3_common_LittleFS
+board = esp32s3cdc-qio_qspi-8M
+build_flags = ${esp32s3_common_LittleFS.build_flags}
+ -DFEATURE_ETHERNET=1
+ -DFEATURE_ARDUINO_OTA=1
+ -DPLUGIN_BUILD_CUSTOM
+ -DFEATURE_SD=1
+extra_scripts = ${esp32s3_common_LittleFS.extra_scripts}
+ pre:tools/pio/pre_custom_esp32.py
+
+[env:custom_ESP32s3_8M1M_LittleFS_OPI_PSRAM_CDC_ETH]
+extends = env:custom_ESP32s3_8M1M_LittleFS_CDC_ETH
+board = esp32s3cdc-qio_opi-8M
+
+
+[env:max_ESP32s3_8M1M_LittleFS_CDC_ETH]
+extends = esp32s3_common_LittleFS
+board = esp32s3cdc-qio_qspi-8M
+build_flags = ${esp32s3_common_LittleFS.build_flags}
+ -DFEATURE_ETHERNET=1
+ -DFEATURE_ARDUINO_OTA=1
+ -DPLUGIN_BUILD_MAX_ESP32
+ -DPLUGIN_BUILD_IR_EXTENDED
+extra_scripts = ${esp32s3_common_LittleFS.extra_scripts}
+
+
+[env:max_ESP32s3_8M1M_LittleFS_OPI_PSRAM_CDC_ETH]
+extends = env:max_ESP32s3_8M1M_LittleFS_CDC_ETH
+board = esp32s3cdc-qio_opi-8M
+
+
+[env:custom_ESP32s3_16M8M_LittleFS_CDC_ETH]
+extends = esp32s3_common_LittleFS
+board = esp32s3cdc-qio_qspi-16M
+build_flags = ${esp32s3_common_LittleFS.build_flags}
+ -DFEATURE_ETHERNET=1
+ -DFEATURE_ARDUINO_OTA=1
+ -DPLUGIN_BUILD_CUSTOM
+ -DPLUGIN_BUILD_IR_EXTENDED
+ -DFEATURE_SD=1
+extra_scripts = ${esp32s3_common_LittleFS.extra_scripts}
+ pre:tools/pio/pre_custom_esp32.py
+
+[env:custom_ESP32s3_16M8M_LittleFS_OPI_PSRAM_CDC_ETH]
+extends = env:custom_ESP32s3_16M8M_LittleFS_CDC_ETH
+board = esp32s3cdc-qio_opi-16M
+
+
+[env:max_ESP32s3_16M8M_LittleFS_CDC_ETH]
+extends = esp32s3_common_LittleFS
+board = esp32s3cdc-qio_qspi-16M
+build_flags = ${esp32s3_common_LittleFS.build_flags}
+ -DFEATURE_ETHERNET=1
+ -DFEATURE_ARDUINO_OTA=1
+ -DPLUGIN_BUILD_MAX_ESP32
+ -DPLUGIN_BUILD_IR_EXTENDED
+extra_scripts = ${esp32s3_common_LittleFS.extra_scripts}
+
+
+[env:max_ESP32s3_16M8M_LittleFS_OPI_PSRAM_CDC_ETH]
+extends = esp32s3_common_LittleFS
+board = esp32s3cdc-qio_opi-16M
+build_flags = ${esp32s3_common_LittleFS.build_flags}
+ -DFEATURE_ETHERNET=1
+ -DFEATURE_ARDUINO_OTA=1
+ -DPLUGIN_BUILD_MAX_ESP32
+ -DPLUGIN_BUILD_IR_EXTENDED
+extra_scripts = ${esp32s3_common_LittleFS.extra_scripts}
+
+
diff --git a/platformio_esp82xx_envs.ini b/platformio_esp82xx_envs.ini
index 9cbff2de14..d459c27b27 100644
--- a/platformio_esp82xx_envs.ini
+++ b/platformio_esp82xx_envs.ini
@@ -696,13 +696,16 @@ lib_ignore = ${regular_platform.lib_ignore}
; GPIO13 Blue Led (0 = On, 1 = Off)
[env:hard_SONOFF_POW_4M1M]
extends = esp8266_4M1M, hard_esp82xx
-platform = ${hard_esp82xx.platform}
-platform_packages = ${hard_esp82xx.platform_packages}
-build_flags = ${hard_esp82xx.build_flags}
+platform = ${core_2_7_4.platform}
+platform_packages = ${core_2_7_4.platform_packages}
+build_flags = ${core_2_7_4.build_flags}
${esp8266_4M1M.build_flags}
+ -DBUILD_NO_DEBUG
+ -DPLUGIN_BUILD_CUSTOM
-DPLUGIN_SET_SONOFF_POW
-DFEATURE_IMPROV=0
-lib_ignore = ${hard_esp82xx.lib_ignore}
+ -DPLUGIN_STATS_NR_ELEMENTS=64
+lib_ignore = ${esp8266_custom_common_274.lib_ignore}
diff --git a/src/Custom-sample.h b/src/Custom-sample.h
index 76ef872ab5..aae29b2472 100644
--- a/src/Custom-sample.h
+++ b/src/Custom-sample.h
@@ -542,6 +542,7 @@ static const char DATA_ESPEASY_DEFAULT_MIN_CSS[] PROGMEM = {
// #define USES_P164 // Gases - ENS16x TVOC/eCO2
// #define USES_P166 // Output - GP8403 Dual channel DAC (Digital Analog Converter)
// #define USES_P167 // Environment - Sensirion SEN5x / Ikea Vindstyrka
+// #define USES_P169 // Environment - AS3935 Lightning Detector
/*
#######################################################################################################
diff --git a/src/_C001.cpp b/src/_C001.cpp
index 13e180aa09..46ce884e6a 100644
--- a/src/_C001.cpp
+++ b/src/_C001.cpp
@@ -129,7 +129,7 @@ bool CPlugin_001(CPlugin::Function function, struct EventStruct *event, String&
// Uncrustify may change this into multi line, which will result in failed builds
// *INDENT-OFF*
-bool do_process_c001_delay_queue(int controller_number, const Queue_element_base& element_base, ControllerSettingsStruct& ControllerSettings) {
+bool do_process_c001_delay_queue(cpluginID_t cpluginID, const Queue_element_base& element_base, ControllerSettingsStruct& ControllerSettings) {
const C001_queue_element& element = static_cast(element_base);
// *INDENT-ON*
@@ -142,7 +142,7 @@ bool do_process_c001_delay_queue(int controller_number, const Queue_element_base
int httpCode = -1;
send_via_http(
- controller_number,
+ cpluginID,
ControllerSettings,
element._controller_idx,
element.txt,
diff --git a/src/_C003.cpp b/src/_C003.cpp
index ca58458271..0405f090bb 100644
--- a/src/_C003.cpp
+++ b/src/_C003.cpp
@@ -85,7 +85,7 @@ bool CPlugin_003(CPlugin::Function function, struct EventStruct *event, String&
// Uncrustify may change this into multi line, which will result in failed builds
// *INDENT-OFF*
-bool do_process_c003_delay_queue(int controller_number, const Queue_element_base& element_base, ControllerSettingsStruct& ControllerSettings) {
+bool do_process_c003_delay_queue(cpluginID_t cpluginID, const Queue_element_base& element_base, ControllerSettingsStruct& ControllerSettings) {
const C003_queue_element& element = static_cast(element_base);
// *INDENT-ON*
bool success = false;
@@ -93,7 +93,7 @@ bool do_process_c003_delay_queue(int controller_number, const Queue_element_base
// Use WiFiClient class to create TCP connections
WiFiClient client;
- if (!try_connect_host(controller_number, client, ControllerSettings, F("TELNT: ")))
+ if (!try_connect_host(cpluginID, client, ControllerSettings, F("TELNT: ")))
{
return success;
}
diff --git a/src/_C004.cpp b/src/_C004.cpp
index aa63b2dad1..5e32ca2eb7 100644
--- a/src/_C004.cpp
+++ b/src/_C004.cpp
@@ -94,7 +94,7 @@ bool CPlugin_004(CPlugin::Function function, struct EventStruct *event, String&
// Uncrustify may change this into multi line, which will result in failed builds
// *INDENT-OFF*
-bool do_process_c004_delay_queue(int controller_number, const Queue_element_base& element_base, ControllerSettingsStruct& ControllerSettings) {
+bool do_process_c004_delay_queue(cpluginID_t cpluginID, const Queue_element_base& element_base, ControllerSettingsStruct& ControllerSettings) {
const C004_queue_element& element = static_cast(element_base);
// *INDENT-ON*
String postDataStr = F("api_key=");
@@ -122,7 +122,7 @@ bool do_process_c004_delay_queue(int controller_number, const Queue_element_base
int httpCode = -1;
send_via_http(
- controller_number,
+ cpluginID,
ControllerSettings,
element._controller_idx,
F("/update"), // uri
diff --git a/src/_C005.cpp b/src/_C005.cpp
index f837e4374a..222994361d 100644
--- a/src/_C005.cpp
+++ b/src/_C005.cpp
@@ -1,290 +1,312 @@
-#include "src/Helpers/_CPlugin_Helper.h"
-#ifdef USES_C005
-
-# include "src/Commands/ExecuteCommand.h"
-# include "src/Globals/EventQueue.h"
-# include "src/Helpers/PeriodicalActions.h"
-# include "src/Helpers/StringParser.h"
-# include "_Plugin_Helper.h"
-
-// #######################################################################################################
-// ################### Controller Plugin 005: Home Assistant (openHAB) MQTT ##############################
-// #######################################################################################################
-
-# define CPLUGIN_005
-# define CPLUGIN_ID_005 5
-# define CPLUGIN_NAME_005 "Home Assistant (openHAB) MQTT"
-
-String CPlugin_005_pubname;
-bool CPlugin_005_mqtt_retainFlag = false;
-
-bool C005_parse_command(struct EventStruct *event);
-
-bool CPlugin_005(CPlugin::Function function, struct EventStruct *event, String& string)
-{
- bool success = false;
-
- switch (function)
- {
- case CPlugin::Function::CPLUGIN_PROTOCOL_ADD:
- {
- ProtocolStruct& proto = getProtocolStruct(event->idx); // = CPLUGIN_ID_005;
- proto.usesMQTT = true;
- proto.usesTemplate = true;
- proto.usesAccount = true;
- proto.usesPassword = true;
- proto.usesExtCreds = true;
- proto.defaultPort = 1883;
- proto.usesID = false;
- break;
- }
-
- case CPlugin::Function::CPLUGIN_GET_DEVICENAME:
- {
- string = F(CPLUGIN_NAME_005);
- break;
- }
-
- case CPlugin::Function::CPLUGIN_INIT:
- {
- success = init_mqtt_delay_queue(event->ControllerIndex, CPlugin_005_pubname, CPlugin_005_mqtt_retainFlag);
- break;
- }
-
- case CPlugin::Function::CPLUGIN_EXIT:
- {
- exit_mqtt_delay_queue();
- break;
- }
-
- case CPlugin::Function::CPLUGIN_PROTOCOL_TEMPLATE:
- {
- event->String1 = F("%sysname%/#");
- event->String2 = F("%sysname%/%tskname%/%valname%");
- break;
- }
-
- case CPlugin::Function::CPLUGIN_PROTOCOL_RECV:
- {
- controllerIndex_t ControllerID = findFirstEnabledControllerWithId(CPLUGIN_ID_005);
-
- if (validControllerIndex(ControllerID)) {
- C005_parse_command(event);
- }
- break;
- }
-
- case CPlugin::Function::CPLUGIN_PROTOCOL_SEND:
- {
- if (MQTT_queueFull(event->ControllerIndex)) {
- break;
- }
-
-
- String pubname = CPlugin_005_pubname;
- bool mqtt_retainFlag = CPlugin_005_mqtt_retainFlag;
-
- parseControllerVariables(pubname, event, false);
-
- uint8_t valueCount = getValueCountForTask(event->TaskIndex);
-
- for (uint8_t x = 0; x < valueCount; x++)
- {
- // MFD: skip publishing for values with empty labels (removes unnecessary publishing of unwanted values)
- if (getTaskValueName(event->TaskIndex, x).isEmpty()) {
- continue; // we skip values with empty labels
- }
-
- String tmppubname = pubname;
- parseSingleControllerVariable(tmppubname, event, x, false);
- String value;
- if (event->sensorType == Sensor_VType::SENSOR_TYPE_STRING) {
- value = event->String2.substring(0, 20); // For the log
- } else {
- value = formatUserVarNoCheck(event, x);
- }
-# ifndef BUILD_NO_DEBUG
-
- if (loglevelActiveFor(LOG_LEVEL_DEBUG)) {
- addLogMove(LOG_LEVEL_DEBUG,
- strformat(
- F("MQTT : %s %s"),
- tmppubname.c_str(),
- value.c_str()));
- }
-# endif // ifndef BUILD_NO_DEBUG
-
- // Small optimization so we don't try to copy potentially large strings
- if (event->sensorType == Sensor_VType::SENSOR_TYPE_STRING) {
- if (MQTTpublish(event->ControllerIndex, event->TaskIndex, tmppubname.c_str(), event->String2.c_str(), mqtt_retainFlag))
- success = true;
- } else {
- // Publish using move operator, thus tmppubname and value are empty after this call
- if (MQTTpublish(event->ControllerIndex, event->TaskIndex, std::move(tmppubname), std::move(value), mqtt_retainFlag))
- success = true;
- }
- }
- break;
- }
-
- case CPlugin::Function::CPLUGIN_FLUSH:
- {
- processMQTTdelayQueue();
- delay(0);
- break;
- }
-
- default:
- break;
- }
-
- return success;
-}
-
-bool C005_parse_command(struct EventStruct *event) {
- // FIXME TD-er: Command is not parsed for template arguments.
-
- // Topic : event->String1
- // Message: event->String2
- String cmd;
- bool validTopic = false;
- const int lastindex = event->String1.lastIndexOf('/');
- const String lastPartTopic = event->String1.substring(lastindex + 1);
- const bool has_cmd_arg_index = event->String1.lastIndexOf(F("cmd_arg")) != -1;
-
- if (equals(lastPartTopic, F("cmd"))) {
- // Example:
- // Topic: ESP_Easy/Bathroom_pir_env/cmd
- // Message: gpio,14,0
- // Full command: gpio,14,0
-
- move_special(cmd, String(event->String2));
-
- // SP_C005a: string= ;cmd=gpio,12,0 ;taskIndex=12 ;string1=ESPT12/cmd ;string2=gpio,12,0
- validTopic = true;
- } else if (has_cmd_arg_index) {
- // Example:
- // Topic: ESP_Easy/Bathroom_pir_env/cmd_arg1/GPIO/0
- // Message: 14
- // Full command: gpio,14,0
-
- uint8_t topic_index = 1;
- String topic_folder = parseStringKeepCase(event->String1, topic_index, '/');
-
- while(!topic_folder.startsWith(F("cmd_arg")) && !topic_folder.isEmpty()) {
- ++topic_index;
- topic_folder = parseStringKeepCase(event->String1, topic_index, '/');
- }
- if (!topic_folder.isEmpty()) {
- int32_t cmd_arg_nr = -1;
- if (validIntFromString(topic_folder.substring(7), cmd_arg_nr)) {
- int constructed_cmd_arg_nr = 0;
- ++topic_index;
- topic_folder = parseStringKeepCase(event->String1, topic_index, '/');
- bool msg_added = false;
- while(!topic_folder.isEmpty()) {
- if (constructed_cmd_arg_nr != 0) {
- cmd += ',';
- }
- if (constructed_cmd_arg_nr == cmd_arg_nr) {
- cmd += event->String2;
- msg_added = true;
- } else {
- cmd += topic_folder;
- ++topic_index;
- topic_folder = parseStringKeepCase(event->String1, topic_index, '/');
- }
- ++constructed_cmd_arg_nr;
- }
- if (!msg_added) {
- cmd += ',';
- cmd += event->String2;
- }
- // addLog(LOG_LEVEL_INFO, concat(F("MQTT cmd: "), cmd));
-
- validTopic = true;
- }
- }
- } else {
- // Example:
- // Topic: ESP_Easy/Bathroom_pir_env/GPIO/14
- // Message: 0 or 1
- // Full command: gpio,14,0
- if (lastindex > 0) {
- // Topic has at least one separator
- int32_t lastPartTopic_int;
- float value_f;
-
- if (validFloatFromString(event->String2, value_f) &&
- validIntFromString(lastPartTopic, lastPartTopic_int)) {
- const int prevLastindex = event->String1.lastIndexOf('/', lastindex - 1);
-
- cmd = strformat(
- F("%s,%d,%s"),
- event->String1.substring(prevLastindex + 1, lastindex).c_str(),
- lastPartTopic_int,
- event->String2.c_str() // Just use the original format
- );
- validTopic = true;
- }
- }
- }
-
- if (validTopic) {
- // in case of event, store to buffer and return...
- const String command = parseString(cmd, 1);
-
- if ((equals(command, F("event"))) || (equals(command, F("asyncevent")))) {
- if (Settings.UseRules) {
- // Need to sanitize the event a bit to allow for sending event values as MQTT messages.
- // For example:
- // Publish topic: espeasy_node/cmd_arg2/event/myevent/2
- // Message: 1
- // Actual event: myevent=1,2
-
- // Strip out the "event" or "asyncevent" part, leaving the actual event string
- cmd = parseStringToEndKeepCase(cmd, 2);
-
- {
- // Get the first part upto a parameter separator
- // Example: "myEvent,1,2,3", which needs to be converted to "myEvent=1,2,3"
- // N.B. This may contain the first eventvalue too
- // e.g. "myEvent=1,2,3" => "myEvent=1"
- String eventName = parseStringKeepCase(cmd, 1);
- String eventValues = parseStringToEndKeepCase(cmd, 2);
- const int equal_pos = eventName.indexOf('=');
- if (equal_pos != -1) {
- // We found an '=' character, so the actual event name is everything before that char.
- eventName = cmd.substring(0, equal_pos);
- eventValues = cmd.substring(equal_pos + 1); // Rest of the event, after the '=' char
- }
- if (eventValues.startsWith(F(","))) {
- // Need to reconstruct the event to get rid of calls like these:
- // myevent=,1,2
- eventValues = eventValues.substring(1);
- }
- // Now reconstruct the complete event
- // Without event values: "myEvent" (no '=' char)
- // With event values: "myEvent=1,2,3"
-
- // Re-using the 'cmd' String as that has pre-allocated memory which is
- // known to be large enough to hold the entire event.
- cmd = eventName;
- if (eventValues.length() > 0) {
- // Only append an = if there are eventvalues.
- cmd += '=';
- cmd += eventValues;
- }
- }
- // Check for duplicates, as sometimes a node may have multiple subscriptions to the same topic.
- // Then it may add several of the same events in a burst.
- eventQueue.addMove(std::move(cmd), true);
- }
- } else {
- ExecuteCommand_all({EventValueSource::Enum::VALUE_SOURCE_MQTT, std::move(cmd)}, true);
- }
- }
- return validTopic;
-}
-
-#endif // ifdef USES_C005
+#include "src/Helpers/_CPlugin_Helper.h"
+#ifdef USES_C005
+
+# include "src/Commands/ExecuteCommand.h"
+# include "src/Globals/EventQueue.h"
+# include "src/Helpers/PeriodicalActions.h"
+# include "src/Helpers/StringParser.h"
+# include "_Plugin_Helper.h"
+
+// #######################################################################################################
+// ################### Controller Plugin 005: Home Assistant (openHAB) MQTT ##############################
+// #######################################################################################################
+
+# define CPLUGIN_005
+# define CPLUGIN_ID_005 5
+# define CPLUGIN_NAME_005 "Home Assistant (openHAB) MQTT"
+
+String CPlugin_005_pubname;
+bool CPlugin_005_mqtt_retainFlag = false;
+
+bool C005_parse_command(struct EventStruct *event);
+
+bool CPlugin_005(CPlugin::Function function, struct EventStruct *event, String& string)
+{
+ bool success = false;
+
+ switch (function)
+ {
+ case CPlugin::Function::CPLUGIN_PROTOCOL_ADD:
+ {
+ ProtocolStruct& proto = getProtocolStruct(event->idx); // = CPLUGIN_ID_005;
+ proto.usesMQTT = true;
+ proto.usesTemplate = true;
+ proto.usesAccount = true;
+ proto.usesPassword = true;
+ proto.usesExtCreds = true;
+ proto.defaultPort = 1883;
+ proto.usesID = false;
+ break;
+ }
+
+ case CPlugin::Function::CPLUGIN_GET_DEVICENAME:
+ {
+ string = F(CPLUGIN_NAME_005);
+ break;
+ }
+
+ case CPlugin::Function::CPLUGIN_INIT:
+ {
+ success = init_mqtt_delay_queue(event->ControllerIndex, CPlugin_005_pubname, CPlugin_005_mqtt_retainFlag);
+ break;
+ }
+
+ case CPlugin::Function::CPLUGIN_EXIT:
+ {
+ exit_mqtt_delay_queue();
+ break;
+ }
+
+ case CPlugin::Function::CPLUGIN_PROTOCOL_TEMPLATE:
+ {
+ event->String1 = F("%sysname%/#");
+ event->String2 = F("%sysname%/%tskname%/%valname%");
+ break;
+ }
+
+ case CPlugin::Function::CPLUGIN_PROTOCOL_RECV:
+ {
+ controllerIndex_t ControllerID = findFirstEnabledControllerWithId(CPLUGIN_ID_005);
+
+ if (validControllerIndex(ControllerID)) {
+ C005_parse_command(event);
+ }
+ break;
+ }
+
+ case CPlugin::Function::CPLUGIN_PROTOCOL_SEND:
+ {
+ if (MQTT_queueFull(event->ControllerIndex)) {
+ break;
+ }
+
+
+ String pubname = CPlugin_005_pubname;
+ const bool contains_valname = pubname.indexOf(F("%valname%")) != -1;
+ bool mqtt_retainFlag = CPlugin_005_mqtt_retainFlag;
+
+ parseControllerVariables(pubname, event, false);
+
+ uint8_t valueCount = getValueCountForTask(event->TaskIndex);
+
+ for (uint8_t x = 0; x < valueCount; x++)
+ {
+ // MFD: skip publishing for values with empty labels (removes unnecessary publishing of unwanted values)
+ if (Cache.getTaskDeviceValueName(event->TaskIndex, x).isEmpty()) {
+ continue; // we skip values with empty labels
+ }
+
+ String tmppubname = pubname;
+
+ if (contains_valname) {
+ parseSingleControllerVariable(tmppubname, event, x, false);
+ }
+ String value;
+
+ if (event->sensorType == Sensor_VType::SENSOR_TYPE_STRING) {
+# ifndef BUILD_NO_DEBUG
+ if (loglevelActiveFor(LOG_LEVEL_DEBUG)) {
+ value = event->String2.substring(0, 20); // For the log
+ }
+# endif
+ } else {
+ value = formatUserVarNoCheck(event, x);
+ }
+# ifndef BUILD_NO_DEBUG
+
+ if (loglevelActiveFor(LOG_LEVEL_DEBUG)) {
+ addLogMove(LOG_LEVEL_DEBUG,
+ strformat(
+ F("MQTT : %s %s"),
+ tmppubname.c_str(),
+ value.c_str()));
+ }
+# endif // ifndef BUILD_NO_DEBUG
+
+ // Small optimization so we don't try to copy potentially large strings
+ if (event->sensorType == Sensor_VType::SENSOR_TYPE_STRING) {
+ if (MQTTpublish(event->ControllerIndex, event->TaskIndex, tmppubname.c_str(), event->String2.c_str(), mqtt_retainFlag)) {
+ success = true;
+ }
+ } else {
+ // Publish using move operator, thus tmppubname and value are empty after this call
+ if (MQTTpublish(event->ControllerIndex, event->TaskIndex, std::move(tmppubname), std::move(value), mqtt_retainFlag)) {
+ success = true;
+ }
+ }
+ }
+ break;
+ }
+
+ case CPlugin::Function::CPLUGIN_FLUSH:
+ {
+ processMQTTdelayQueue();
+ delay(0);
+ break;
+ }
+
+ default:
+ break;
+ }
+
+ return success;
+}
+
+bool C005_parse_command(struct EventStruct *event) {
+ // FIXME TD-er: Command is not parsed for template arguments.
+
+ // Topic : event->String1
+ // Message: event->String2
+ String cmd;
+ bool validTopic = false;
+ const int lastindex = event->String1.lastIndexOf('/');
+ const String lastPartTopic = event->String1.substring(lastindex + 1);
+ const bool has_cmd_arg_index = event->String1.lastIndexOf(F("cmd_arg")) != -1;
+
+ if (equals(lastPartTopic, F("cmd"))) {
+ // Example:
+ // Topic: ESP_Easy/Bathroom_pir_env/cmd
+ // Message: gpio,14,0
+ // Full command: gpio,14,0
+
+ move_special(cmd, String(event->String2));
+
+ // SP_C005a: string= ;cmd=gpio,12,0 ;taskIndex=12 ;string1=ESPT12/cmd ;string2=gpio,12,0
+ validTopic = true;
+ } else if (has_cmd_arg_index) {
+ // Example:
+ // Topic: ESP_Easy/Bathroom_pir_env/cmd_arg1/GPIO/0
+ // Message: 14
+ // Full command: gpio,14,0
+
+ uint8_t topic_index = 1;
+ String topic_folder = parseStringKeepCase(event->String1, topic_index, '/');
+
+ while (!topic_folder.startsWith(F("cmd_arg")) && !topic_folder.isEmpty()) {
+ ++topic_index;
+ topic_folder = parseStringKeepCase(event->String1, topic_index, '/');
+ }
+
+ if (!topic_folder.isEmpty()) {
+ int32_t cmd_arg_nr = -1;
+
+ if (validIntFromString(topic_folder.substring(7), cmd_arg_nr)) {
+ int constructed_cmd_arg_nr = 0;
+ ++topic_index;
+ topic_folder = parseStringKeepCase(event->String1, topic_index, '/');
+ bool msg_added = false;
+
+ while (!topic_folder.isEmpty()) {
+ if (constructed_cmd_arg_nr != 0) {
+ cmd += ',';
+ }
+
+ if (constructed_cmd_arg_nr == cmd_arg_nr) {
+ cmd += event->String2;
+ msg_added = true;
+ } else {
+ cmd += topic_folder;
+ ++topic_index;
+ topic_folder = parseStringKeepCase(event->String1, topic_index, '/');
+ }
+ ++constructed_cmd_arg_nr;
+ }
+
+ if (!msg_added) {
+ cmd += ',';
+ cmd += event->String2;
+ }
+
+ // addLog(LOG_LEVEL_INFO, concat(F("MQTT cmd: "), cmd));
+
+ validTopic = true;
+ }
+ }
+ } else {
+ // Example:
+ // Topic: ESP_Easy/Bathroom_pir_env/GPIO/14
+ // Message: 0 or 1
+ // Full command: gpio,14,0
+ if (lastindex > 0) {
+ // Topic has at least one separator
+ int32_t lastPartTopic_int;
+ float value_f;
+
+ if (validFloatFromString(event->String2, value_f) &&
+ validIntFromString(lastPartTopic, lastPartTopic_int)) {
+ const int prevLastindex = event->String1.lastIndexOf('/', lastindex - 1);
+
+ cmd = strformat(
+ F("%s,%d,%s"),
+ event->String1.substring(prevLastindex + 1, lastindex).c_str(),
+ lastPartTopic_int,
+ event->String2.c_str() // Just use the original format
+ );
+ validTopic = true;
+ }
+ }
+ }
+
+ if (validTopic) {
+ // in case of event, store to buffer and return...
+ const String command = parseString(cmd, 1);
+
+ if ((equals(command, F("event"))) || (equals(command, F("asyncevent")))) {
+ if (Settings.UseRules) {
+ // Need to sanitize the event a bit to allow for sending event values as MQTT messages.
+ // For example:
+ // Publish topic: espeasy_node/cmd_arg2/event/myevent/2
+ // Message: 1
+ // Actual event: myevent=1,2
+
+ // Strip out the "event" or "asyncevent" part, leaving the actual event string
+ cmd = parseStringToEndKeepCase(cmd, 2);
+
+ {
+ // Get the first part upto a parameter separator
+ // Example: "myEvent,1,2,3", which needs to be converted to "myEvent=1,2,3"
+ // N.B. This may contain the first eventvalue too
+ // e.g. "myEvent=1,2,3" => "myEvent=1"
+ String eventName = parseStringKeepCase(cmd, 1);
+ String eventValues = parseStringToEndKeepCase(cmd, 2);
+ const int equal_pos = eventName.indexOf('=');
+
+ if (equal_pos != -1) {
+ // We found an '=' character, so the actual event name is everything before that char.
+ eventName = cmd.substring(0, equal_pos);
+ eventValues = cmd.substring(equal_pos + 1); // Rest of the event, after the '=' char
+ }
+
+ if (eventValues.startsWith(F(","))) {
+ // Need to reconstruct the event to get rid of calls like these:
+ // myevent=,1,2
+ eventValues = eventValues.substring(1);
+ }
+
+ // Now reconstruct the complete event
+ // Without event values: "myEvent" (no '=' char)
+ // With event values: "myEvent=1,2,3"
+
+ // Re-using the 'cmd' String as that has pre-allocated memory which is
+ // known to be large enough to hold the entire event.
+ cmd = eventName;
+
+ if (eventValues.length() > 0) {
+ // Only append an = if there are eventvalues.
+ cmd += '=';
+ cmd += eventValues;
+ }
+ }
+
+ // Check for duplicates, as sometimes a node may have multiple subscriptions to the same topic.
+ // Then it may add several of the same events in a burst.
+ eventQueue.addMove(std::move(cmd), true);
+ }
+ } else {
+ ExecuteCommand_all({ EventValueSource::Enum::VALUE_SOURCE_MQTT, std::move(cmd) }, true);
+ }
+ }
+ return validTopic;
+}
+
+#endif // ifdef USES_C005
diff --git a/src/_C006.cpp b/src/_C006.cpp
index 601fcaf498..e514e5cb1e 100644
--- a/src/_C006.cpp
+++ b/src/_C006.cpp
@@ -1,154 +1,157 @@
-#include "src/Helpers/_CPlugin_Helper.h"
-#ifdef USES_C006
-
-// #######################################################################################################
-// ########################### Controller Plugin 006: PiDome MQTT ########################################
-// #######################################################################################################
-
-# include "src/Commands/ExecuteCommand.h"
-# include "src/ESPEasyCore/Controller.h"
-# include "src/Globals/Settings.h"
-# include "src/Helpers/Network.h"
-# include "src/Helpers/PeriodicalActions.h"
-# include "_Plugin_Helper.h"
-
-# define CPLUGIN_006
-# define CPLUGIN_ID_006 6
-# define CPLUGIN_NAME_006 "PiDome MQTT"
-
-String CPlugin_006_pubname;
-bool CPlugin_006_mqtt_retainFlag = false;
-
-
-bool CPlugin_006(CPlugin::Function function, struct EventStruct *event, String& string)
-{
- bool success = false;
-
- switch (function)
- {
- case CPlugin::Function::CPLUGIN_PROTOCOL_ADD:
- {
- ProtocolStruct& proto = getProtocolStruct(event->idx); // = CPLUGIN_ID_006;
- proto.usesMQTT = true;
- proto.usesTemplate = true;
- proto.usesAccount = false;
- proto.usesPassword = false;
- proto.usesExtCreds = true;
- proto.defaultPort = 1883;
- proto.usesID = false;
- break;
- }
-
- case CPlugin::Function::CPLUGIN_GET_DEVICENAME:
- {
- string = F(CPLUGIN_NAME_006);
- break;
- }
-
- case CPlugin::Function::CPLUGIN_INIT:
- {
- success = init_mqtt_delay_queue(event->ControllerIndex, CPlugin_006_pubname, CPlugin_006_mqtt_retainFlag);
- break;
- }
-
- case CPlugin::Function::CPLUGIN_EXIT:
- {
- exit_mqtt_delay_queue();
- break;
- }
-
- case CPlugin::Function::CPLUGIN_PROTOCOL_TEMPLATE:
- {
- event->String1 = F("/Home/#");
- event->String2 = F("/hooks/devices/%id%/SensorData/%valname%");
- break;
- }
-
- case CPlugin::Function::CPLUGIN_PROTOCOL_RECV:
- {
- // topic structure /Home/Floor/Location/device//gpio/16
- // Split topic into array
- String tmpTopic = event->String1.substring(1);
- String topicSplit[10];
- int SlashIndex = tmpTopic.indexOf('/');
- uint8_t count = 0;
-
- while (SlashIndex > 0 && count < 10 - 1)
- {
- topicSplit[count] = tmpTopic.substring(0, SlashIndex);
- tmpTopic = tmpTopic.substring(SlashIndex + 1);
- SlashIndex = tmpTopic.indexOf('/');
- count++;
- }
- topicSplit[count] = tmpTopic;
-
- String name = topicSplit[4];
-
- if (name.equals(Settings.getName()))
- {
- String cmd = topicSplit[5];
- cmd += ',';
- cmd += topicSplit[6].toInt(); // Par1
- cmd += ',';
-
- if ((event->String2.equalsIgnoreCase(F("false"))) ||
- (event->String2.equalsIgnoreCase(F("true"))))
- {
- cmd += (event->String2.equalsIgnoreCase(F("true"))) ? '1' : '0'; // Par2
- }
- else
- {
- cmd += event->String2; // Par2
- }
- ExecuteCommand_all({EventValueSource::Enum::VALUE_SOURCE_MQTT, std::move(cmd)}, true);
- }
- break;
- }
-
- case CPlugin::Function::CPLUGIN_PROTOCOL_SEND:
- {
- if (MQTT_queueFull(event->ControllerIndex)) {
- break;
- }
-
- String pubname = CPlugin_006_pubname;
- bool mqtt_retainFlag = CPlugin_006_mqtt_retainFlag;
-
- statusLED(true);
-
- //LoadTaskSettings(event->TaskIndex); // FIXME TD-er: This can probably be removed
- parseControllerVariables(pubname, event, false);
-
- uint8_t valueCount = getValueCountForTask(event->TaskIndex);
-
- for (uint8_t x = 0; x < valueCount; x++)
- {
- String tmppubname = pubname;
- parseSingleControllerVariable(tmppubname, event, x, false);
-
- // Small optimization so we don't try to copy potentially large strings
- if (event->sensorType == Sensor_VType::SENSOR_TYPE_STRING) {
- if (MQTTpublish(event->ControllerIndex, event->TaskIndex, tmppubname.c_str(), event->String2.c_str(), mqtt_retainFlag))
- success = true;
- } else {
- if (MQTTpublish(event->ControllerIndex, event->TaskIndex, std::move(tmppubname), formatUserVarNoCheck(event, x), mqtt_retainFlag))
- success = true;
- }
- }
- break;
- }
-
- case CPlugin::Function::CPLUGIN_FLUSH:
- {
- processMQTTdelayQueue();
- delay(0);
- break;
- }
-
- default:
- break;
- }
- return success;
-}
-
-#endif // ifdef USES_C006
+#include "src/Helpers/_CPlugin_Helper.h"
+#ifdef USES_C006
+
+// #######################################################################################################
+// ########################### Controller Plugin 006: PiDome MQTT ########################################
+// #######################################################################################################
+
+# include "src/Commands/ExecuteCommand.h"
+# include "src/ESPEasyCore/Controller.h"
+# include "src/Globals/Settings.h"
+# include "src/Helpers/Network.h"
+# include "src/Helpers/PeriodicalActions.h"
+# include "_Plugin_Helper.h"
+
+# define CPLUGIN_006
+# define CPLUGIN_ID_006 6
+# define CPLUGIN_NAME_006 "PiDome MQTT"
+
+String CPlugin_006_pubname;
+bool CPlugin_006_mqtt_retainFlag = false;
+
+
+bool CPlugin_006(CPlugin::Function function, struct EventStruct *event, String& string)
+{
+ bool success = false;
+
+ switch (function)
+ {
+ case CPlugin::Function::CPLUGIN_PROTOCOL_ADD:
+ {
+ ProtocolStruct& proto = getProtocolStruct(event->idx); // = CPLUGIN_ID_006;
+ proto.usesMQTT = true;
+ proto.usesTemplate = true;
+ proto.usesAccount = false;
+ proto.usesPassword = false;
+ proto.usesExtCreds = true;
+ proto.defaultPort = 1883;
+ proto.usesID = false;
+ break;
+ }
+
+ case CPlugin::Function::CPLUGIN_GET_DEVICENAME:
+ {
+ string = F(CPLUGIN_NAME_006);
+ break;
+ }
+
+ case CPlugin::Function::CPLUGIN_INIT:
+ {
+ success = init_mqtt_delay_queue(event->ControllerIndex, CPlugin_006_pubname, CPlugin_006_mqtt_retainFlag);
+ break;
+ }
+
+ case CPlugin::Function::CPLUGIN_EXIT:
+ {
+ exit_mqtt_delay_queue();
+ break;
+ }
+
+ case CPlugin::Function::CPLUGIN_PROTOCOL_TEMPLATE:
+ {
+ event->String1 = F("/Home/#");
+ event->String2 = F("/hooks/devices/%id%/SensorData/%valname%");
+ break;
+ }
+
+ case CPlugin::Function::CPLUGIN_PROTOCOL_RECV:
+ {
+ // topic structure /Home/Floor/Location/device//gpio/16
+ // Split topic into array
+ String tmpTopic = event->String1.substring(1);
+ String topicSplit[10];
+ int SlashIndex = tmpTopic.indexOf('/');
+ uint8_t count = 0;
+
+ while (SlashIndex > 0 && count < 10 - 1)
+ {
+ topicSplit[count] = tmpTopic.substring(0, SlashIndex);
+ tmpTopic = tmpTopic.substring(SlashIndex + 1);
+ SlashIndex = tmpTopic.indexOf('/');
+ count++;
+ }
+ topicSplit[count] = tmpTopic;
+
+ String name = topicSplit[4];
+
+ if (name.equals(Settings.getName()))
+ {
+ String cmd = topicSplit[5];
+ cmd += ',';
+ cmd += topicSplit[6].toInt(); // Par1
+ cmd += ',';
+
+ if ((event->String2.equalsIgnoreCase(F("false"))) ||
+ (event->String2.equalsIgnoreCase(F("true"))))
+ {
+ cmd += (event->String2.equalsIgnoreCase(F("true"))) ? '1' : '0'; // Par2
+ }
+ else
+ {
+ cmd += event->String2; // Par2
+ }
+ ExecuteCommand_all({EventValueSource::Enum::VALUE_SOURCE_MQTT, std::move(cmd)}, true);
+ }
+ break;
+ }
+
+ case CPlugin::Function::CPLUGIN_PROTOCOL_SEND:
+ {
+ if (MQTT_queueFull(event->ControllerIndex)) {
+ break;
+ }
+
+ String pubname = CPlugin_006_pubname;
+ const bool contains_valname = pubname.indexOf(F("%valname%")) != -1;
+ bool mqtt_retainFlag = CPlugin_006_mqtt_retainFlag;
+
+ statusLED(true);
+
+ //LoadTaskSettings(event->TaskIndex); // FIXME TD-er: This can probably be removed
+ parseControllerVariables(pubname, event, false);
+
+ const uint8_t valueCount = getValueCountForTask(event->TaskIndex);
+
+ for (uint8_t x = 0; x < valueCount; x++)
+ {
+ String tmppubname = pubname;
+ if (contains_valname) {
+ parseSingleControllerVariable(tmppubname, event, x, false);
+ }
+
+ // Small optimization so we don't try to copy potentially large strings
+ if (event->sensorType == Sensor_VType::SENSOR_TYPE_STRING) {
+ if (MQTTpublish(event->ControllerIndex, event->TaskIndex, tmppubname.c_str(), event->String2.c_str(), mqtt_retainFlag))
+ success = true;
+ } else {
+ if (MQTTpublish(event->ControllerIndex, event->TaskIndex, std::move(tmppubname), formatUserVarNoCheck(event, x), mqtt_retainFlag))
+ success = true;
+ }
+ }
+ break;
+ }
+
+ case CPlugin::Function::CPLUGIN_FLUSH:
+ {
+ processMQTTdelayQueue();
+ delay(0);
+ break;
+ }
+
+ default:
+ break;
+ }
+ return success;
+}
+
+#endif // ifdef USES_C006
diff --git a/src/_C007.cpp b/src/_C007.cpp
index dcab924655..8d2846cb96 100644
--- a/src/_C007.cpp
+++ b/src/_C007.cpp
@@ -90,7 +90,7 @@ bool CPlugin_007(CPlugin::Function function, struct EventStruct *event, String&
// Uncrustify may change this into multi line, which will result in failed builds
// *INDENT-OFF*
-bool do_process_c007_delay_queue(int controller_number, const Queue_element_base& element_base, ControllerSettingsStruct& ControllerSettings) {
+bool do_process_c007_delay_queue(cpluginID_t cpluginID, const Queue_element_base& element_base, ControllerSettingsStruct& ControllerSettings) {
const C007_queue_element& element = static_cast(element_base);
// *INDENT-ON*
String url = F("/emoncms/input/post.json?node=");
@@ -117,7 +117,7 @@ bool do_process_c007_delay_queue(int controller_number, const Queue_element_base
int httpCode = -1;
send_via_http(
- controller_number,
+ cpluginID,
ControllerSettings,
element._controller_idx,
url,
diff --git a/src/_C008.cpp b/src/_C008.cpp
index f9a3129882..3c21e97436 100644
--- a/src/_C008.cpp
+++ b/src/_C008.cpp
@@ -76,6 +76,7 @@ bool CPlugin_008(CPlugin::Function function, struct EventStruct *event, String&
LoadControllerSettings(event->ControllerIndex, *ControllerSettings);
pubname = ControllerSettings->Publish;
}
+ const bool contains_valname = pubname.indexOf(F("%valname%")) != -1;
uint8_t valueCount = getValueCountForTask(event->TaskIndex);
std::unique_ptr element(new (std::nothrow) C008_queue_element(event, valueCount));
@@ -101,7 +102,9 @@ bool CPlugin_008(CPlugin::Function function, struct EventStruct *event, String&
String txt;
txt += '/';
txt += pubname;
- parseSingleControllerVariable(txt, event, x, true);
+ if (contains_valname) {
+ parseSingleControllerVariable(txt, event, x, true);
+ }
# ifndef BUILD_NO_DEBUG
if (loglevelActiveFor(LOG_LEVEL_DEBUG)) {
@@ -145,7 +148,7 @@ bool CPlugin_008(CPlugin::Function function, struct EventStruct *event, String&
// Uncrustify may change this into multi line, which will result in failed builds
// *INDENT-OFF*
-bool do_process_c008_delay_queue(int controller_number, const Queue_element_base& element_base, ControllerSettingsStruct& ControllerSettings) {
+bool do_process_c008_delay_queue(cpluginID_t cpluginID, const Queue_element_base& element_base, ControllerSettingsStruct& ControllerSettings) {
const C008_queue_element& element = static_cast(element_base);
// *INDENT-ON*
while (element.txt[element.valuesSent].isEmpty()) {
@@ -158,7 +161,7 @@ bool do_process_c008_delay_queue(int controller_number, const Queue_element_base
int httpCode = -1;
send_via_http(
- controller_number,
+ cpluginID,
ControllerSettings,
element._controller_idx,
element.txt[element.valuesSent],
diff --git a/src/_C009.cpp b/src/_C009.cpp
index 47f7a4cadb..bcca8adf54 100644
--- a/src/_C009.cpp
+++ b/src/_C009.cpp
@@ -106,7 +106,7 @@ bool CPlugin_009(CPlugin::Function function, struct EventStruct *event, String&
// Uncrustify may change this into multi line, which will result in failed builds
// *INDENT-OFF*
-bool do_process_c009_delay_queue(int controller_number, const Queue_element_base& element_base, ControllerSettingsStruct& ControllerSettings) {
+bool do_process_c009_delay_queue(cpluginID_t cpluginID, const Queue_element_base& element_base, ControllerSettingsStruct& ControllerSettings) {
const C009_queue_element& element = static_cast(element_base);
// *INDENT-ON*
String jsonString;
@@ -178,7 +178,7 @@ bool do_process_c009_delay_queue(int controller_number, const Queue_element_base
{
jsonString += to_json_object_value(F("deviceName"), getTaskDeviceName(element._taskIndex));
jsonString += ',';
- jsonString += to_json_object_value(F("valueName"), getTaskValueName(element._taskIndex, x));
+ jsonString += to_json_object_value(F("valueName"), Cache.getTaskDeviceValueName(element._taskIndex, x));
jsonString += ',';
jsonString += to_json_object_value(F("type"), static_cast(element.sensorType));
jsonString += ',';
@@ -203,7 +203,7 @@ bool do_process_c009_delay_queue(int controller_number, const Queue_element_base
int httpCode = -1;
send_via_http(
- controller_number,
+ cpluginID,
ControllerSettings,
element._controller_idx,
F("/ESPEasy"),
diff --git a/src/_C010.cpp b/src/_C010.cpp
index c4c1904907..ff8921c1c5 100644
--- a/src/_C010.cpp
+++ b/src/_C010.cpp
@@ -83,8 +83,8 @@ bool CPlugin_010(CPlugin::Function function, struct EventStruct *event, String&
LoadControllerSettings(event->ControllerIndex, *ControllerSettings);
pubname = ControllerSettings->Publish;
}
-
parseControllerVariables(pubname, event, false);
+ const bool contains_valname = pubname.indexOf(F("%valname%")) != -1;
for (uint8_t x = 0; x < valueCount; x++)
{
@@ -94,7 +94,9 @@ bool CPlugin_010(CPlugin::Function function, struct EventStruct *event, String&
if (isvalid) {
String txt;
txt = pubname;
- parseSingleControllerVariable(txt, event, x, false);
+ if (contains_valname) {
+ parseSingleControllerVariable(txt, event, x, false);
+ }
txt.replace(F("%value%"), formattedValue);
move_special(element->txt[x], std::move(txt));
#ifndef BUILD_NO_DEBUG
@@ -129,7 +131,7 @@ bool CPlugin_010(CPlugin::Function function, struct EventStruct *event, String&
// Uncrustify may change this into multi line, which will result in failed builds
// *INDENT-OFF*
-bool do_process_c010_delay_queue(int controller_number, const Queue_element_base& element_base, ControllerSettingsStruct& ControllerSettings) {
+bool do_process_c010_delay_queue(cpluginID_t cpluginID, const Queue_element_base& element_base, ControllerSettingsStruct& ControllerSettings) {
const C010_queue_element& element = static_cast(element_base);
// *INDENT-ON*
while (element.txt[element.valuesSent].isEmpty()) {
@@ -143,7 +145,7 @@ bool do_process_c010_delay_queue(int controller_number, const Queue_element_base
if (!beginWiFiUDP_randomPort(C010_portUDP)) { return false; }
- if (!try_connect_host(controller_number, C010_portUDP, ControllerSettings)) {
+ if (!try_connect_host(cpluginID, C010_portUDP, ControllerSettings)) {
return false;
}
diff --git a/src/_C011.cpp b/src/_C011.cpp
index 8c091d5ea8..b4d1aab50b 100644
--- a/src/_C011.cpp
+++ b/src/_C011.cpp
@@ -194,7 +194,7 @@ bool CPlugin_011(CPlugin::Function function, struct EventStruct *event, String&
// Uncrustify may change this into multi line, which will result in failed builds
// *INDENT-OFF*
-bool do_process_c011_delay_queue(int controller_number, const Queue_element_base& element_base, ControllerSettingsStruct& ControllerSettings) {
+bool do_process_c011_delay_queue(cpluginID_t cpluginID, const Queue_element_base& element_base, ControllerSettingsStruct& ControllerSettings) {
const C011_queue_element& element = static_cast(element_base);
// *INDENT-ON*
@@ -203,7 +203,7 @@ bool do_process_c011_delay_queue(int controller_number, const Queue_element_base
int httpCode = -1;
send_via_http(
- controller_number,
+ cpluginID,
ControllerSettings,
element._controller_idx,
element.uri,
diff --git a/src/_C012.cpp b/src/_C012.cpp
index 6eccb2aee1..94d639cf60 100644
--- a/src/_C012.cpp
+++ b/src/_C012.cpp
@@ -105,7 +105,7 @@ bool CPlugin_012(CPlugin::Function function, struct EventStruct *event, String&
// Uncrustify may change this into multi line, which will result in failed builds
// *INDENT-OFF*
-bool do_process_c012_delay_queue(int controller_number, const Queue_element_base& element_base, ControllerSettingsStruct& ControllerSettings) {
+bool do_process_c012_delay_queue(cpluginID_t cpluginID, const Queue_element_base& element_base, ControllerSettingsStruct& ControllerSettings) {
const C012_queue_element& element = static_cast(element_base);
// *INDENT-ON*
while (element.txt[element.valuesSent].isEmpty()) {
diff --git a/src/_C013.cpp b/src/_C013.cpp
index c9855e882c..56243a0819 100644
--- a/src/_C013.cpp
+++ b/src/_C013.cpp
@@ -1,369 +1,424 @@
-#include "src/Helpers/_CPlugin_Helper.h"
-#ifdef USES_C013
-
-# if FEATURE_ESPEASY_P2P == 0
- # error "Controller C013 ESPEasy P2P requires the FEATURE_ESPEASY_P2P enabled"
-# endif // if FEATURE_ESPEASY_P2P == 0
-
-
-# include "src/Globals/Nodes.h"
-# include "src/DataStructs/C013_p2p_dataStructs.h"
-# include "src/ESPEasyCore/ESPEasyRules.h"
-# include "src/Helpers/Misc.h"
-# include "src/Helpers/Network.h"
-
-// #######################################################################################################
-// ########################### Controller Plugin 013: ESPEasy P2P network ################################
-// #######################################################################################################
-
-# define CPLUGIN_013
-# define CPLUGIN_ID_013 13
-# define CPLUGIN_NAME_013 "ESPEasy P2P Networking"
-
-
-// Forward declarations
-void C013_SendUDPTaskInfo(uint8_t destUnit,
- uint8_t sourceTaskIndex,
- uint8_t destTaskIndex);
-void C013_SendUDPTaskData(struct EventStruct *event,
- uint8_t destUnit,
- uint8_t destTaskIndex);
-void C013_sendUDP(uint8_t unit,
- const uint8_t *data,
- uint8_t size);
-void C013_Receive(struct EventStruct *event);
-
-
-bool CPlugin_013(CPlugin::Function function, struct EventStruct *event, String& string)
-{
- bool success = false;
-
- switch (function)
- {
- case CPlugin::Function::CPLUGIN_PROTOCOL_ADD:
- {
- ProtocolStruct& proto = getProtocolStruct(event->idx); // = CPLUGIN_ID_013;
- proto.usesMQTT = false;
- proto.usesTemplate = false;
- proto.usesAccount = false;
- proto.usesPassword = false;
- proto.usesHost = false;
- proto.defaultPort = 8266;
- proto.usesID = false;
- proto.Custom = true;
- break;
- }
-
- case CPlugin::Function::CPLUGIN_GET_DEVICENAME:
- {
- string = F(CPLUGIN_NAME_013);
- break;
- }
-
- case CPlugin::Function::CPLUGIN_TASK_CHANGE_NOTIFICATION:
- {
- C013_SendUDPTaskInfo(0, event->TaskIndex, event->TaskIndex);
- break;
- }
-
- case CPlugin::Function::CPLUGIN_PROTOCOL_SEND:
- {
- C013_SendUDPTaskData(event, 0, event->TaskIndex);
- success = true;
- break;
- }
-
- case CPlugin::Function::CPLUGIN_UDP_IN:
- {
- C013_Receive(event);
- break;
- }
-
- case CPlugin::Function::CPLUGIN_WEBFORM_SHOW_HOST_CONFIG:
- {
- string = F("-");
- break;
- }
-
- /*
- case CPlugin::Function::CPLUGIN_FLUSH:
- {
- process_c013_delay_queue(event->ControllerIndex);
- delay(0);
- break;
- }
- */
-
- default:
- break;
- }
- return success;
-}
-
-// ********************************************************************************
-// Generic UDP message
-// ********************************************************************************
-void C013_SendUDPTaskInfo(uint8_t destUnit, uint8_t sourceTaskIndex, uint8_t destTaskIndex)
-{
- if (!NetworkConnected(10)) {
- return;
- }
-
- if (!validTaskIndex(sourceTaskIndex) || !validTaskIndex(destTaskIndex)) {
- return;
- }
- pluginID_t pluginID = Settings.getPluginID_for_task(sourceTaskIndex);
-
- if (!validPluginID_fullcheck(pluginID)) {
- return;
- }
-
- struct C013_SensorInfoStruct infoReply;
-
- infoReply.sourceUnit = Settings.Unit;
- infoReply.sourceTaskIndex = sourceTaskIndex;
- infoReply.destTaskIndex = destTaskIndex;
- infoReply.deviceNumber = pluginID;
- safe_strncpy(infoReply.taskName, getTaskDeviceName(infoReply.sourceTaskIndex), sizeof(infoReply.taskName));
-
- for (uint8_t x = 0; x < VARS_PER_TASK; x++) {
- safe_strncpy(infoReply.ValueNames[x], getTaskValueName(infoReply.sourceTaskIndex, x), sizeof(infoReply.ValueNames[x]));
- }
-
- if (destUnit != 0)
- {
- infoReply.destUnit = destUnit;
- C013_sendUDP(destUnit, reinterpret_cast(&infoReply), sizeof(C013_SensorInfoStruct));
- } else {
- for (auto it = Nodes.begin(); it != Nodes.end(); ++it) {
- if (it->first != Settings.Unit) {
- infoReply.destUnit = it->first;
- C013_sendUDP(it->first, reinterpret_cast(&infoReply), sizeof(C013_SensorInfoStruct));
- }
- }
- }
-}
-
-void C013_SendUDPTaskData(struct EventStruct *event, uint8_t destUnit, uint8_t destTaskIndex)
-{
- if (!NetworkConnected(10)) {
- return;
- }
- struct C013_SensorDataStruct dataReply;
-
- dataReply.sourceUnit = Settings.Unit;
- dataReply.sourceTaskIndex = event->TaskIndex;
- dataReply.destTaskIndex = destTaskIndex;
- dataReply.deviceNumber = Settings.getPluginID_for_task(event->TaskIndex);
-
- // FIXME TD-er: We should check for sensorType and pluginID on both sides.
- // 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.getRawTaskValues_Data(event->TaskIndex);
-
- if (taskValues != nullptr) {
- for (taskVarIndex_t x = 0; x < VARS_PER_TASK; ++x)
- {
- dataReply.values.copyValue(*taskValues, x, dataReply.sensorType);
- }
- }
-
- if (destUnit != 0)
- {
- dataReply.destUnit = destUnit;
- C013_sendUDP(destUnit, reinterpret_cast(&dataReply), sizeof(C013_SensorDataStruct));
- } else {
- for (auto it = Nodes.begin(); it != Nodes.end(); ++it) {
- if (it->first != Settings.Unit) {
- dataReply.destUnit = it->first;
- C013_sendUDP(it->first, reinterpret_cast(&dataReply), sizeof(C013_SensorDataStruct));
- }
- }
- }
-}
-
-/*********************************************************************************************\
- Send UDP message (unit 255=broadcast)
-\*********************************************************************************************/
-void C013_sendUDP(uint8_t unit, const uint8_t *data, uint8_t size)
-{
- if (!NetworkConnected(10)) {
- return;
- }
-
- const IPAddress remoteNodeIP = getIPAddressForUnit(unit);
-
-
-# ifndef BUILD_NO_DEBUG
-
- if (loglevelActiveFor(LOG_LEVEL_DEBUG_MORE)) {
- addLogMove(LOG_LEVEL_DEBUG_MORE, strformat(
- F("C013 : Send UDP message to %d (%s)"),
- unit,
- remoteNodeIP.toString().c_str()));
- }
-# endif // ifndef BUILD_NO_DEBUG
-
- statusLED(true);
-
- WiFiUDP C013_portUDP;
-
- if (!beginWiFiUDP_randomPort(C013_portUDP)) { return; }
-
- FeedSW_watchdog();
-
- if (C013_portUDP.beginPacket(remoteNodeIP, Settings.UDPPort) == 0) { return; }
- C013_portUDP.write(data, size);
- C013_portUDP.endPacket();
- C013_portUDP.stop();
- FeedSW_watchdog();
- delay(0);
-}
-
-void C013_Receive(struct EventStruct *event) {
- if (event->Par2 < 6) { return; }
-# ifndef BUILD_NO_DEBUG
-
- if (loglevelActiveFor(LOG_LEVEL_DEBUG_MORE)) {
- if ((event->Data != nullptr) &&
- (event->Data[1] > 1) && (event->Data[1] < 6))
- {
- String log = (F("C013 : msg "));
-
- for (uint8_t x = 1; x < 6; x++)
- {
- log += ' ';
- log += static_cast(event->Data[x]);
- }
- addLogMove(LOG_LEVEL_DEBUG_MORE, log);
- }
- }
-# endif // ifndef BUILD_NO_DEBUG
-
- switch (event->Data[1]) {
- case 2: // sensor info pull request
- {
- // SendUDPTaskInfo(packetBuffer[2], packetBuffer[5], packetBuffer[4]);
- break;
- }
-
- case 3: // sensor info
- {
- struct C013_SensorInfoStruct infoReply;
- int structSize = sizeof(C013_SensorInfoStruct);
-
- if (event->Par2 < structSize) { structSize = event->Par2; }
-
- memcpy(reinterpret_cast(&infoReply), event->Data, structSize);
-
- if (infoReply.isValid()) {
- // to prevent flash wear out (bugs in communication?) we can only write to an empty task
- // so it will write only once and has to be cleared manually through webgui
- // Also check the receiving end does support the plugin ID.
- if (!validPluginID_fullcheck(Settings.getPluginID_for_task(infoReply.destTaskIndex)) &&
- supportedPluginID(infoReply.deviceNumber))
- {
- taskClear(infoReply.destTaskIndex, false);
- Settings.TaskDeviceNumber[infoReply.destTaskIndex] = infoReply.deviceNumber.value;
- Settings.TaskDeviceDataFeed[infoReply.destTaskIndex] = infoReply.sourceUnit; // remote feed store unit nr sending the data
-
- constexpr pluginID_t DUMMY_PLUGIN_ID{33};
- if ((infoReply.deviceNumber == DUMMY_PLUGIN_ID) && (infoReply.sensorType != Sensor_VType::SENSOR_TYPE_NONE)) {
- // Received a dummy device and the sensor type is actually set
- Settings.TaskDevicePluginConfig[infoReply.destTaskIndex][0] = static_cast(infoReply.sensorType);
- }
-
- for (controllerIndex_t x = 0; x < CONTROLLER_MAX; x++) {
- Settings.TaskDeviceSendData[x][infoReply.destTaskIndex] = false;
- }
- safe_strncpy(ExtraTaskSettings.TaskDeviceName, infoReply.taskName, sizeof(infoReply.taskName));
-
- for (uint8_t x = 0; x < VARS_PER_TASK; x++) {
- safe_strncpy(ExtraTaskSettings.TaskDeviceValueNames[x], infoReply.ValueNames[x], sizeof(infoReply.ValueNames[x]));
- }
- ExtraTaskSettings.TaskIndex = infoReply.destTaskIndex;
- SaveTaskSettings(infoReply.destTaskIndex);
- SaveSettings();
- }
- }
- break;
- }
-
- case 4: // sensor data pull request
- {
- // SendUDPTaskData(packetBuffer[2], packetBuffer[5], packetBuffer[4]);
- break;
- }
-
- case 5: // sensor data
- {
- struct C013_SensorDataStruct dataReply;
- int structSize = sizeof(C013_SensorDataStruct);
-
- if (event->Par2 < structSize) { structSize = event->Par2; }
- memcpy(reinterpret_cast(&dataReply), event->Data, structSize);
-
- // FIXME TD-er: We should check for sensorType and pluginID on both sides.
- // For example sending different sensor type data from one dummy to another is probably not going to work well
- if (dataReply.isValid()) {
- // only if this task has a remote feed, update values
- const uint8_t remoteFeed = Settings.TaskDeviceDataFeed[dataReply.destTaskIndex];
-
- if ((remoteFeed != 0) && (remoteFeed == dataReply.sourceUnit))
- {
- // deviceNumber and sensorType were not present before build 2023-05-05. (build NR 20460)
- // See: https://github.com/letscontrolit/ESPEasy/commit/cf791527eeaf31ca98b07c45c1b64e2561a7b041#diff-86b42dd78398b103e272503f05f55ee0870ae5fb907d713c2505d63279bb0321
- // Thus should not be checked
- //
- // If the node is not present in the nodes list (e.g. it had not announced itself in the last 10 minutes or announcement was missed)
- // Then we cannot be sure about its build.
- bool mustMatch = false;
- NodeStruct *sourceNode = Nodes.getNode(dataReply.sourceUnit);
- if (sourceNode != nullptr) {
- mustMatch = sourceNode->build >= 20460;
- }
-
- if (mustMatch && !dataReply.matchesPluginID(Settings.getPluginID_for_task(dataReply.destTaskIndex))) {
- // Mismatch in plugin ID from sending node
- if (loglevelActiveFor(LOG_LEVEL_ERROR)) {
- String log = concat(F("P2P data : PluginID mismatch for task "), dataReply.destTaskIndex + 1);
- log += concat(F(" from unit "), dataReply.sourceUnit);
- log += concat(F(" remote: "), dataReply.deviceNumber.value);
- log += concat(F(" local: "), Settings.getPluginID_for_task(dataReply.destTaskIndex).value);
- addLogMove(LOG_LEVEL_ERROR, log);
- }
- } else {
- struct EventStruct TempEvent(dataReply.destTaskIndex);
- TempEvent.Source = EventValueSource::Enum::VALUE_SOURCE_UDP;
-
- const Sensor_VType sensorType = TempEvent.getSensorType();
-
- if (!mustMatch || dataReply.matchesSensorType(sensorType)) {
- TaskValues_Data_t *taskValues = UserVar.getRawTaskValues_Data(dataReply.destTaskIndex);
-
- if (taskValues != nullptr) {
- for (taskVarIndex_t x = 0; x < VARS_PER_TASK; ++x)
- {
- taskValues->copyValue(dataReply.values, x, sensorType);
- }
- }
-
- SensorSendTask(&TempEvent);
- } else {
- // Mismatch in sensor types
- if (loglevelActiveFor(LOG_LEVEL_ERROR)) {
- String log = concat(F("P2P data : SensorType mismatch for task "), dataReply.destTaskIndex + 1);
- log += concat(F(" from unit "), dataReply.sourceUnit);
- addLogMove(LOG_LEVEL_ERROR, log);
- }
- }
- }
- }
- }
- break;
- }
- }
-}
-
-#endif // ifdef USES_C013
+#include "src/Helpers/_CPlugin_Helper.h"
+#ifdef USES_C013
+
+# if FEATURE_ESPEASY_P2P == 0
+ # error "Controller C013 ESPEasy P2P requires the FEATURE_ESPEASY_P2P enabled"
+# endif // if FEATURE_ESPEASY_P2P == 0
+
+
+# include "src/Globals/Nodes.h"
+# include "src/DataStructs/C013_p2p_SensorDataStruct.h"
+# include "src/DataStructs/C013_p2p_SensorInfoStruct.h"
+# include "src/ESPEasyCore/ESPEasyRules.h"
+# include "src/Helpers/Misc.h"
+# include "src/Helpers/Network.h"
+
+// #######################################################################################################
+// ########################### Controller Plugin 013: ESPEasy P2P network ################################
+// #######################################################################################################
+
+# define CPLUGIN_013
+# define CPLUGIN_ID_013 13
+# define CPLUGIN_NAME_013 "ESPEasy P2P Networking"
+
+
+// Forward declarations
+void C013_SendUDPTaskInfo(uint8_t destUnit,
+ uint8_t sourceTaskIndex,
+ uint8_t destTaskIndex);
+void C013_SendUDPTaskData(struct EventStruct *event,
+ uint8_t destUnit,
+ uint8_t destTaskIndex);
+void C013_sendUDP(uint8_t unit,
+ const uint8_t *data,
+ size_t size);
+void C013_Receive(struct EventStruct *event);
+
+
+bool CPlugin_013(CPlugin::Function function, struct EventStruct *event, String& string)
+{
+ bool success = false;
+
+ switch (function)
+ {
+ case CPlugin::Function::CPLUGIN_PROTOCOL_ADD:
+ {
+ ProtocolStruct& proto = getProtocolStruct(event->idx); // = CPLUGIN_ID_013;
+ proto.usesMQTT = false;
+ proto.usesTemplate = false;
+ proto.usesAccount = false;
+ proto.usesPassword = false;
+ proto.usesHost = false;
+ proto.defaultPort = 8266;
+ proto.usesID = false;
+ proto.Custom = true;
+ break;
+ }
+
+ case CPlugin::Function::CPLUGIN_GET_DEVICENAME:
+ {
+ string = F(CPLUGIN_NAME_013);
+ break;
+ }
+
+ case CPlugin::Function::CPLUGIN_TASK_CHANGE_NOTIFICATION:
+ {
+ C013_SendUDPTaskInfo(0, event->TaskIndex, event->TaskIndex);
+ break;
+ }
+
+ case CPlugin::Function::CPLUGIN_PROTOCOL_SEND:
+ {
+ C013_SendUDPTaskData(event, 0, event->TaskIndex);
+ success = true;
+ break;
+ }
+
+ case CPlugin::Function::CPLUGIN_UDP_IN:
+ {
+ C013_Receive(event);
+ break;
+ }
+
+ case CPlugin::Function::CPLUGIN_WEBFORM_SHOW_HOST_CONFIG:
+ {
+ string = F("-");
+ break;
+ }
+
+ /*
+ case CPlugin::Function::CPLUGIN_FLUSH:
+ {
+ process_c013_delay_queue(event->ControllerIndex);
+ delay(0);
+ break;
+ }
+ */
+
+ default:
+ break;
+ }
+ return success;
+}
+
+// ********************************************************************************
+// Generic UDP message
+// ********************************************************************************
+void C013_SendUDPTaskInfo(uint8_t destUnit, uint8_t sourceTaskIndex, uint8_t destTaskIndex)
+{
+ if (!NetworkConnected(10)) {
+ return;
+ }
+
+ if (!validTaskIndex(sourceTaskIndex) || !validTaskIndex(destTaskIndex)) {
+ return;
+ }
+ pluginID_t pluginID = Settings.getPluginID_for_task(sourceTaskIndex);
+
+ if (!validPluginID_fullcheck(pluginID)) {
+ return;
+ }
+
+ struct C013_SensorInfoStruct infoReply;
+
+ infoReply.sourceUnit = Settings.Unit;
+ infoReply.sourceTaskIndex = sourceTaskIndex;
+ infoReply.destTaskIndex = destTaskIndex;
+ infoReply.deviceNumber = pluginID;
+ infoReply.destUnit = destUnit;
+
+ if (destUnit == 0)
+ {
+ // Send to broadcast address
+ infoReply.destUnit = 255;
+ }
+ size_t sizeToSend{};
+
+ if (infoReply.prepareForSend(sizeToSend)) {
+ C013_sendUDP(infoReply.destUnit, reinterpret_cast(&infoReply), sizeToSend);
+ }
+}
+
+void C013_SendUDPTaskData(struct EventStruct *event, uint8_t destUnit, uint8_t destTaskIndex)
+{
+ if (!NetworkConnected(10)) {
+ return;
+ }
+ struct C013_SensorDataStruct dataReply;
+
+ dataReply.sourceUnit = Settings.Unit;
+ dataReply.sourceTaskIndex = event->TaskIndex;
+ dataReply.destTaskIndex = destTaskIndex;
+ dataReply.deviceNumber = Settings.getPluginID_for_task(event->TaskIndex);
+
+ // FIXME TD-er: We should check for sensorType and pluginID on both sides.
+ // 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.getRawTaskValues_Data(event->TaskIndex);
+
+ if (taskValues != nullptr) {
+ for (taskVarIndex_t x = 0; x < VARS_PER_TASK; ++x)
+ {
+ dataReply.values.copyValue(*taskValues, x, dataReply.sensorType);
+ }
+ }
+ dataReply.destUnit = destUnit;
+
+ if (destUnit == 0)
+ {
+ // Send to broadcast address
+ dataReply.destUnit = 255;
+ }
+ dataReply.prepareForSend();
+ C013_sendUDP(dataReply.destUnit, reinterpret_cast(&dataReply), sizeof(C013_SensorDataStruct));
+}
+
+/*********************************************************************************************\
+ Send UDP message (unit 255=broadcast)
+\*********************************************************************************************/
+void C013_sendUDP(uint8_t unit, const uint8_t *data, size_t size)
+{
+ START_TIMER
+
+ if (!NetworkConnected(10)) {
+ return;
+ }
+
+ const IPAddress remoteNodeIP = getIPAddressForUnit(unit);
+
+
+# ifndef BUILD_NO_DEBUG
+
+ if (loglevelActiveFor(LOG_LEVEL_DEBUG_MORE)) {
+ addLogMove(LOG_LEVEL_DEBUG_MORE, strformat(
+ F("C013 : Send UDP message to %d (%s)"),
+ unit,
+ remoteNodeIP.toString().c_str()));
+ }
+# endif // ifndef BUILD_NO_DEBUG
+
+ statusLED(true);
+
+ WiFiUDP C013_portUDP;
+
+ if (!beginWiFiUDP_randomPort(C013_portUDP)) {
+ STOP_TIMER(C013_SEND_UDP_FAIL);
+ return;
+ }
+
+ FeedSW_watchdog();
+
+ if (C013_portUDP.beginPacket(remoteNodeIP, Settings.UDPPort) == 0) {
+ STOP_TIMER(C013_SEND_UDP_FAIL);
+ return;
+ }
+ C013_portUDP.write(data, size);
+ C013_portUDP.endPacket();
+ C013_portUDP.stop();
+ FeedSW_watchdog();
+ delay(0);
+ STOP_TIMER(C013_SEND_UDP);
+}
+
+void C013_Receive(struct EventStruct *event) {
+ if (event->Par2 < 6) { return; }
+# ifndef BUILD_NO_DEBUG
+
+ if (loglevelActiveFor(LOG_LEVEL_DEBUG_MORE)) {
+ if ((event->Data != nullptr) &&
+ (event->Data[1] > 1) && (event->Data[1] < 6))
+ {
+ String log = (F("C013 : msg "));
+
+ for (uint8_t x = 1; x < 6; x++)
+ {
+ log += ' ';
+ log += static_cast(event->Data[x]);
+ }
+ addLogMove(LOG_LEVEL_DEBUG_MORE, log);
+ }
+ }
+# endif // ifndef BUILD_NO_DEBUG
+
+ START_TIMER
+
+ switch (event->Data[1]) {
+ case 2: // sensor info pull request
+ {
+ // SendUDPTaskInfo(packetBuffer[2], packetBuffer[5], packetBuffer[4]);
+ break;
+ }
+
+ case 3: // sensor info
+ {
+ bool mustSave = false;
+ taskIndex_t taskIndex = INVALID_TASK_INDEX;
+ {
+ // Allocate this is a separate scope since C013_SensorInfoStruct is a HUGE object
+ // Should not be left allocated on the stack when calling PLUGIN_INIT and save, etc.
+ struct C013_SensorInfoStruct infoReply;
+
+ if (infoReply.setData(event->Data, event->Par2)) {
+ // to prevent flash wear out (bugs in communication?) we can only write to an empty task
+ // so it will write only once and has to be cleared manually through webgui
+ // Also check the receiving end does support the plugin ID.
+ const pluginID_t currentPluginID = Settings.getPluginID_for_task(infoReply.destTaskIndex);
+ bool mustUpdateCurrentTask = false;
+
+ if (currentPluginID == infoReply.deviceNumber) {
+ // Check to see if task already is set to receive from this host
+ if ((Settings.TaskDeviceDataFeed[infoReply.destTaskIndex] == infoReply.sourceUnit) &&
+ Settings.TaskDeviceEnabled[infoReply.destTaskIndex]) {
+ mustUpdateCurrentTask = true;
+ }
+ }
+
+ if ((mustUpdateCurrentTask || !validPluginID_fullcheck(currentPluginID)) &&
+ supportedPluginID(infoReply.deviceNumber))
+ {
+ taskClear(infoReply.destTaskIndex, false);
+ Settings.TaskDeviceNumber[infoReply.destTaskIndex] = infoReply.deviceNumber.value;
+ Settings.TaskDeviceDataFeed[infoReply.destTaskIndex] = infoReply.sourceUnit; // remote feed store unit nr sending the data
+
+ if (mustUpdateCurrentTask) {
+ Settings.TaskDeviceEnabled[infoReply.destTaskIndex] = true;
+ }
+
+ constexpr pluginID_t DUMMY_PLUGIN_ID{ 33 };
+
+ if ((infoReply.deviceNumber == DUMMY_PLUGIN_ID) && (infoReply.sensorType != Sensor_VType::SENSOR_TYPE_NONE)) {
+ // Received a dummy device and the sensor type is actually set
+ Settings.TaskDevicePluginConfig[infoReply.destTaskIndex][0] = static_cast(infoReply.sensorType);
+ }
+
+ for (controllerIndex_t x = 0; x < CONTROLLER_MAX; x++) {
+ Settings.TaskDeviceSendData[x][infoReply.destTaskIndex] = false;
+ }
+ safe_strncpy(ExtraTaskSettings.TaskDeviceName, infoReply.taskName, sizeof(infoReply.taskName));
+
+ for (uint8_t x = 0; x < VARS_PER_TASK; x++) {
+ safe_strncpy(ExtraTaskSettings.TaskDeviceValueNames[x], infoReply.ValueNames[x], sizeof(infoReply.ValueNames[x]));
+ }
+
+ if (infoReply.sourceNodeBuild >= 20871) {
+ ExtraTaskSettings.version = infoReply.ExtraTaskSettings_version;
+
+ for (uint8_t x = 0; x < VARS_PER_TASK; x++) {
+// safe_strncpy(ExtraTaskSettings.TaskDeviceFormula[x], infoReply.TaskDeviceFormula[x], sizeof(infoReply.TaskDeviceFormula[x]));
+ ExtraTaskSettings.TaskDeviceValueDecimals[x] = infoReply.TaskDeviceValueDecimals[x];
+ ExtraTaskSettings.TaskDeviceMinValue[x] = infoReply.TaskDeviceMinValue[x];
+ ExtraTaskSettings.TaskDeviceMaxValue[x] = infoReply.TaskDeviceMaxValue[x];
+ ExtraTaskSettings.TaskDeviceErrorValue[x] = infoReply.TaskDeviceErrorValue[x];
+ ExtraTaskSettings.VariousBits[x] = infoReply.VariousBits[x];
+ }
+
+ for (uint8_t x = 0; x < PLUGIN_CONFIGVAR_MAX; ++x) {
+ Settings.TaskDevicePluginConfig[infoReply.destTaskIndex][x] = infoReply.TaskDevicePluginConfig[x];
+ }
+ }
+
+ ExtraTaskSettings.TaskIndex = infoReply.destTaskIndex;
+ taskIndex = infoReply.destTaskIndex;
+ mustSave = true;
+ }
+ }
+ }
+
+ if (mustSave) {
+ SaveTaskSettings(taskIndex);
+ SaveSettings();
+
+ if (Settings.TaskDeviceEnabled[taskIndex]) {
+ struct EventStruct TempEvent(taskIndex);
+ TempEvent.Source = EventValueSource::Enum::VALUE_SOURCE_UDP;
+
+ String dummy;
+ PluginCall(PLUGIN_INIT, &TempEvent, dummy);
+ }
+ }
+ break;
+ }
+
+ case 4: // sensor data pull request
+ {
+ // SendUDPTaskData(packetBuffer[2], packetBuffer[5], packetBuffer[4]);
+ break;
+ }
+
+ case 5: // sensor data
+ {
+ struct C013_SensorDataStruct dataReply;
+
+ // FIXME TD-er: We should check for sensorType and pluginID on both sides.
+ // For example sending different sensor type data from one dummy to another is probably not going to work well
+
+ if (dataReply.setData(event->Data, event->Par2)) {
+ // only if this task has a remote feed, update values
+ const uint8_t remoteFeed = Settings.TaskDeviceDataFeed[dataReply.destTaskIndex];
+
+ if ((remoteFeed != 0) && (remoteFeed == dataReply.sourceUnit))
+ {
+ // deviceNumber and sensorType were not present before build 2023-05-05. (build NR 20460)
+ // See:
+ // https://github.com/letscontrolit/ESPEasy/commit/cf791527eeaf31ca98b07c45c1b64e2561a7b041#diff-86b42dd78398b103e272503f05f55ee0870ae5fb907d713c2505d63279bb0321
+ // Thus should not be checked
+ //
+ // If the node is not present in the nodes list (e.g. it had not announced itself in the last 10 minutes or announcement was
+ // missed)
+ // Then we cannot be sure about its build.
+ const bool mustMatch = dataReply.sourceNodeBuild >= 20460;
+
+ if (mustMatch && !dataReply.matchesPluginID(Settings.getPluginID_for_task(dataReply.destTaskIndex))) {
+ // Mismatch in plugin ID from sending node
+ if (loglevelActiveFor(LOG_LEVEL_ERROR)) {
+ String log = concat(F("P2P data : PluginID mismatch for task "), dataReply.destTaskIndex + 1);
+ log += concat(F(" from unit "), dataReply.sourceUnit);
+ log += concat(F(" remote: "), dataReply.deviceNumber.value);
+ log += concat(F(" local: "), Settings.getPluginID_for_task(dataReply.destTaskIndex).value);
+ addLogMove(LOG_LEVEL_ERROR, log);
+ }
+ } else {
+ struct EventStruct TempEvent(dataReply.destTaskIndex);
+ TempEvent.Source = EventValueSource::Enum::VALUE_SOURCE_UDP;
+
+ const Sensor_VType sensorType = TempEvent.getSensorType();
+
+ if (!mustMatch || dataReply.matchesSensorType(sensorType)) {
+ TaskValues_Data_t *taskValues = UserVar.getRawTaskValues_Data(dataReply.destTaskIndex);
+
+ if (taskValues != nullptr) {
+ for (taskVarIndex_t x = 0; x < VARS_PER_TASK; ++x)
+ {
+ taskValues->copyValue(dataReply.values, x, sensorType);
+ }
+ }
+ STOP_TIMER(C013_RECEIVE_SENSOR_DATA);
+
+ if (node_time.systemTimePresent() && (dataReply.timestamp_sec != 0)) {
+ // Only use timestamp of remote unit when we got a system time ourselves
+ // If not, then the order of samples can get messed up.
+ // timestamp_fraq is 16 bit, so need to scale it to 32 bit
+ TempEvent.timestamp_frac = static_cast(dataReply.timestamp_frac) << 16;
+ SensorSendTask(&TempEvent, dataReply.timestamp_sec);
+ } else {
+ SensorSendTask(&TempEvent);
+ }
+ } else {
+ // Mismatch in sensor types
+ if (loglevelActiveFor(LOG_LEVEL_ERROR)) {
+ String log = concat(F("P2P data : SensorType mismatch for task "), dataReply.destTaskIndex + 1);
+ log += concat(F(" from unit "), dataReply.sourceUnit);
+ addLogMove(LOG_LEVEL_ERROR, log);
+ }
+ }
+ }
+ }
+ }
+
+ break;
+ }
+ }
+}
+
+#endif // ifdef USES_C013
diff --git a/src/_C014.cpp b/src/_C014.cpp
index 9a3486c6a6..08e2064f71 100644
--- a/src/_C014.cpp
+++ b/src/_C014.cpp
@@ -878,6 +878,7 @@ bool CPlugin_014(CPlugin::Function function, struct EventStruct *event, String&
}
String pubname = CPlugin_014_pubname;
+ const bool contains_valname = pubname.indexOf(F("%valname%")) != -1;
bool mqtt_retainFlag = CPlugin_014_mqtt_retainFlag;
statusLED(true);
@@ -891,7 +892,9 @@ bool CPlugin_014(CPlugin::Function function, struct EventStruct *event, String&
{
String tmppubname = pubname;
String value;
- parseSingleControllerVariable(tmppubname, event, x, false);
+ if (contains_valname) {
+ parseSingleControllerVariable(tmppubname, event, x, false);
+ }
// Small optimization so we don't try to copy potentially large strings
if (event->getSensorType() == Sensor_VType::SENSOR_TYPE_STRING) {
diff --git a/src/_C015.cpp b/src/_C015.cpp
index 8356993e32..e06d6eddac 100644
--- a/src/_C015.cpp
+++ b/src/_C015.cpp
@@ -193,6 +193,8 @@ bool CPlugin_015(CPlugin::Function function, struct EventStruct *event, String&
// and thus preventing the need to create a long string only to copy it to a queue element.
C015_queue_element& element = static_cast(*(C015_DelayHandler->sendQueue.back()));
+ const String taskDeviceName = getTaskDeviceName(event->TaskIndex);
+
for (uint8_t x = 0; x < valueCount; x++)
{
bool isvalid;
@@ -203,10 +205,10 @@ bool CPlugin_015(CPlugin::Function function, struct EventStruct *event, String&
formattedValue = String();
}
- const String valueName = getTaskValueName(event->TaskIndex, x);
+ const String valueName = Cache.getTaskDeviceValueName(event->TaskIndex, x);
const String valueFullName = strformat(
F("%s.%s"),
- getTaskDeviceName(event->TaskIndex).c_str(),
+ taskDeviceName.c_str(),
valueName.c_str());
const String vPinNumberStr = valueName.substring(1, 4);
int vPinNumber = vPinNumberStr.toInt();
@@ -253,7 +255,7 @@ bool CPlugin_015(CPlugin::Function function, struct EventStruct *event, String&
// Uncrustify may change this into multi line, which will result in failed builds
// *INDENT-OFF*
-bool do_process_c015_delay_queue(int controller_number, const Queue_element_base& element_base, ControllerSettingsStruct& ControllerSettings) {
+bool do_process_c015_delay_queue(cpluginID_t cpluginID, const Queue_element_base& element_base, ControllerSettingsStruct& ControllerSettings) {
const C015_queue_element& element = static_cast(element_base);
// *INDENT-ON*
if (!Settings.ControllerEnabled[element._controller_idx]) {
diff --git a/src/_C016.cpp b/src/_C016.cpp
index 59bfecbb1a..625c377b0a 100644
--- a/src/_C016.cpp
+++ b/src/_C016.cpp
@@ -1,183 +1,186 @@
-#include "src/Helpers/_CPlugin_Helper.h"
-#ifdef USES_C016
-
-// #######################################################################################################
-// ########################### Controller Plugin 016: Controller - Cache #################################
-// #######################################################################################################
-
-/*
- This is a cache layer to collect data while not connected to a network.
- The data will first be stored in RTC memory, which will survive a crash/reboot and even an OTA update.
- If this RTC buffer is full, it will be flushed to whatever is set here as storage.
-
- Typical sample sets contain:
- - UNIX timestamp
- - task index delivering the data
- - 4 float values
-
- These are the result of any plugin sending data to this controller.
-
- The controller can save the samples from RTC memory to several places on the flash:
- - Files on FS
- - Part reserved for OTA update (TODO)
- - Unused flash after the partitioned space (TODO)
-
- The controller can deliver the data to:
-
- */
-
-# include "src/Globals/C016_ControllerCache.h"
-# include "src/Globals/ESPEasy_time.h"
-
-# define CPLUGIN_016
-# define CPLUGIN_ID_016 16
-# define CPLUGIN_NAME_016 "Cache Controller [Experimental]"
-
-// #include
-
-bool C016_allowLocalSystemTime = false;
-
-bool CPlugin_016(CPlugin::Function function, struct EventStruct *event, String& string)
-{
- bool success = false;
-
- switch (function)
- {
- case CPlugin::Function::CPLUGIN_PROTOCOL_ADD:
- {
- ProtocolStruct& proto = getProtocolStruct(event->idx); // = CPLUGIN_ID_016;
- proto.usesMQTT = false;
- proto.usesTemplate = false;
- proto.usesAccount = false;
- proto.usesPassword = false;
- proto.usesExtCreds = false;
- proto.defaultPort = 80;
- proto.usesID = false;
- proto.usesHost = false;
- proto.usesPort = false;
- proto.usesQueue = false;
- proto.usesCheckReply = false;
- proto.usesTimeout = false;
- proto.usesSampleSets = false;
- proto.needsNetwork = false;
- proto.allowsExpire = false;
- proto.allowLocalSystemTime = true;
- break;
- }
-
- case CPlugin::Function::CPLUGIN_GET_DEVICENAME:
- {
- string = F(CPLUGIN_NAME_016);
- break;
- }
-
- case CPlugin::Function::CPLUGIN_INIT:
- {
- {
- MakeControllerSettings(ControllerSettings); // -V522
-
- if (AllocatedControllerSettings()) {
- LoadControllerSettings(event->ControllerIndex, *ControllerSettings);
- C016_allowLocalSystemTime = ControllerSettings->useLocalSystemTime();
- }
- }
- success = init_c016_delay_queue(event->ControllerIndex);
- ControllerCache.init();
- break;
- }
-
- case CPlugin::Function::CPLUGIN_EXIT:
- {
- exit_c016_delay_queue();
- break;
- }
-
- case CPlugin::Function::CPLUGIN_WEBFORM_LOAD:
- {
- break;
- }
-
- case CPlugin::Function::CPLUGIN_WEBFORM_SAVE:
- {
- break;
- }
-
- case CPlugin::Function::CPLUGIN_PROTOCOL_TEMPLATE:
- {
- event->String1 = String();
- event->String2 = String();
- break;
- }
-
- case CPlugin::Function::CPLUGIN_PROTOCOL_SEND:
- {
- // Collect the values at the same run, to make sure all are from the same sample
- uint8_t valueCount = getValueCountForTask(event->TaskIndex);
-
- if (event->timestamp == 0) {
- event->timestamp = C016_allowLocalSystemTime ? node_time.now() : node_time.getUnixTime();
- }
- const C016_queue_element element(
- event,
- valueCount);
-
- const C016_binary_element binary_element = element.getBinary();
- success = ControllerCache.write(reinterpret_cast(&binary_element), sizeof(C016_binary_element));
- break;
- }
-
- case CPlugin::Function::CPLUGIN_WRITE:
- {
- if (C016_CacheInitialized()) {
- const String command = parseString(string, 1);
-
- if (equals(command, F("cachecontroller"))) {
- const String subcommand = parseString(string, 2);
-
- if (equals(subcommand, F("flush"))) {
- C016_flush();
- success = true;
- }
- }
- }
- break;
- }
-
- case CPlugin::Function::CPLUGIN_FLUSH:
- {
- C016_flush();
- delay(0);
- break;
- }
-
- case CPlugin::Function::CPLUGIN_WEBFORM_SHOW_HOST_CONFIG:
- {
- string = F("-");
- break;
- }
-
- default:
- break;
- }
- return success;
-}
-
-// ********************************************************************************
-// Process the data from the cache
-// ********************************************************************************
-// Uncrustify may change this into multi line, which will result in failed builds
-// *INDENT-OFF*
-bool do_process_c016_delay_queue(int controller_number, const Queue_element_base& element_base, ControllerSettingsStruct& ControllerSettings) {
-// *INDENT-ON*
-return true;
-
-// FIXME TD-er: Hand over data to wherever it needs to be.
-// Ideas:
-// - Upload bin files to some server (HTTP post?)
-// - Provide a sample to any connected controller
-// - Do nothing and let some extern host pull the data from the node.
-// - JavaScript to process the data inside the browser.
-// - Feed it to some plugin (e.g. a display to show a chart)
-}
-
-#endif // ifdef USES_C016
+#include "src/Helpers/_CPlugin_Helper.h"
+#ifdef USES_C016
+
+// #######################################################################################################
+// ########################### Controller Plugin 016: Controller - Cache #################################
+// #######################################################################################################
+
+/*
+ This is a cache layer to collect data while not connected to a network.
+ The data will first be stored in RTC memory, which will survive a crash/reboot and even an OTA update.
+ If this RTC buffer is full, it will be flushed to whatever is set here as storage.
+
+ Typical sample sets contain:
+ - UNIX timestamp
+ - task index delivering the data
+ - 4 float values
+
+ These are the result of any plugin sending data to this controller.
+
+ The controller can save the samples from RTC memory to several places on the flash:
+ - Files on FS
+ - Part reserved for OTA update (TODO)
+ - Unused flash after the partitioned space (TODO)
+
+ The controller can deliver the data to:
+
+ */
+
+# include "src/Globals/C016_ControllerCache.h"
+# include "src/Globals/ESPEasy_time.h"
+
+# define CPLUGIN_016
+# define CPLUGIN_ID_016 16
+# define CPLUGIN_NAME_016 "Cache Controller [Experimental]"
+
+// #include
+
+bool C016_allowLocalSystemTime = false;
+
+bool CPlugin_016(CPlugin::Function function, struct EventStruct *event, String& string)
+{
+ bool success = false;
+
+ switch (function)
+ {
+ case CPlugin::Function::CPLUGIN_PROTOCOL_ADD:
+ {
+ ProtocolStruct& proto = getProtocolStruct(event->idx); // = CPLUGIN_ID_016;
+ proto.usesMQTT = false;
+ proto.usesTemplate = false;
+ proto.usesAccount = false;
+ proto.usesPassword = false;
+ proto.usesExtCreds = false;
+ proto.defaultPort = 80;
+ proto.usesID = false;
+ proto.usesHost = false;
+ proto.usesPort = false;
+ proto.usesQueue = false;
+ proto.usesCheckReply = false;
+ proto.usesTimeout = false;
+ proto.usesSampleSets = false;
+ proto.needsNetwork = false;
+ proto.allowsExpire = false;
+ proto.allowLocalSystemTime = true;
+ break;
+ }
+
+ case CPlugin::Function::CPLUGIN_GET_DEVICENAME:
+ {
+ string = F(CPLUGIN_NAME_016);
+ break;
+ }
+
+ case CPlugin::Function::CPLUGIN_INIT:
+ {
+ {
+ MakeControllerSettings(ControllerSettings); // -V522
+
+ if (AllocatedControllerSettings()) {
+ LoadControllerSettings(event->ControllerIndex, *ControllerSettings);
+ C016_allowLocalSystemTime = ControllerSettings->useLocalSystemTime();
+ }
+ }
+ success = init_c016_delay_queue(event->ControllerIndex);
+ ControllerCache.init();
+ break;
+ }
+
+ case CPlugin::Function::CPLUGIN_EXIT:
+ {
+ exit_c016_delay_queue();
+ break;
+ }
+
+ case CPlugin::Function::CPLUGIN_WEBFORM_LOAD:
+ {
+ break;
+ }
+
+ case CPlugin::Function::CPLUGIN_WEBFORM_SAVE:
+ {
+ break;
+ }
+
+ case CPlugin::Function::CPLUGIN_PROTOCOL_TEMPLATE:
+ {
+ event->String1 = String();
+ event->String2 = String();
+ break;
+ }
+
+ case CPlugin::Function::CPLUGIN_PROTOCOL_SEND:
+ {
+ // Collect the values at the same run, to make sure all are from the same sample
+ uint8_t valueCount = getValueCountForTask(event->TaskIndex);
+
+ if (event->timestamp_sec == 0) {
+ if (C016_allowLocalSystemTime)
+ event->setLocalTimeTimestamp();
+ else
+ event->setUnixTimeTimestamp();
+ }
+ const C016_queue_element element(
+ event,
+ valueCount);
+
+ const C016_binary_element binary_element = element.getBinary();
+ success = ControllerCache.write(reinterpret_cast(&binary_element), sizeof(C016_binary_element));
+ break;
+ }
+
+ case CPlugin::Function::CPLUGIN_WRITE:
+ {
+ if (C016_CacheInitialized()) {
+ const String command = parseString(string, 1);
+
+ if (equals(command, F("cachecontroller"))) {
+ const String subcommand = parseString(string, 2);
+
+ if (equals(subcommand, F("flush"))) {
+ C016_flush();
+ success = true;
+ }
+ }
+ }
+ break;
+ }
+
+ case CPlugin::Function::CPLUGIN_FLUSH:
+ {
+ C016_flush();
+ delay(0);
+ break;
+ }
+
+ case CPlugin::Function::CPLUGIN_WEBFORM_SHOW_HOST_CONFIG:
+ {
+ string = F("-");
+ break;
+ }
+
+ default:
+ break;
+ }
+ return success;
+}
+
+// ********************************************************************************
+// Process the data from the cache
+// ********************************************************************************
+// Uncrustify may change this into multi line, which will result in failed builds
+// *INDENT-OFF*
+bool do_process_c016_delay_queue(cpluginID_t cpluginID, const Queue_element_base& element_base, ControllerSettingsStruct& ControllerSettings) {
+// *INDENT-ON*
+return true;
+
+// FIXME TD-er: Hand over data to wherever it needs to be.
+// Ideas:
+// - Upload bin files to some server (HTTP post?)
+// - Provide a sample to any connected controller
+// - Do nothing and let some extern host pull the data from the node.
+// - JavaScript to process the data inside the browser.
+// - Feed it to some plugin (e.g. a display to show a chart)
+}
+
+#endif // ifdef USES_C016
diff --git a/src/_C017.cpp b/src/_C017.cpp
index d10cee5227..fc8f5131d0 100644
--- a/src/_C017.cpp
+++ b/src/_C017.cpp
@@ -88,7 +88,7 @@ bool CPlugin_017(CPlugin::Function function, struct EventStruct *event, String&
// Uncrustify may change this into multi line, which will result in failed builds
// *INDENT-OFF*
-bool do_process_c017_delay_queue(int controller_number, const Queue_element_base& element_base, ControllerSettingsStruct& ControllerSettings) {
+bool do_process_c017_delay_queue(cpluginID_t cpluginID, const Queue_element_base& element_base, ControllerSettingsStruct& ControllerSettings) {
const C017_queue_element& element = static_cast(element_base);
// *INDENT-ON*
if (element.valueCount == 0) {
@@ -102,7 +102,7 @@ bool do_process_c017_delay_queue(int controller_number, const Queue_element_base
WiFiClient client;
- if (!try_connect_host(controller_number, client, ControllerSettings, F("ZBX : ")))
+ if (!try_connect_host(cpluginID, client, ControllerSettings, F("ZBX : ")))
{
return false;
}
@@ -120,7 +120,7 @@ bool do_process_c017_delay_queue(int controller_number, const Queue_element_base
// Populate JSON with the data
for (uint8_t i = 0; i < element.valueCount; i++)
{
- const String taskValueName = getTaskValueName(element._taskIndex, i);
+ const String taskValueName = Cache.getTaskDeviceValueName(element._taskIndex, i);
if (taskValueName.isEmpty()) {
continue; // Zabbix will ignore an empty key anyway
}
diff --git a/src/_C018.cpp b/src/_C018.cpp
index f4032e4a67..6642609dc1 100644
--- a/src/_C018.cpp
+++ b/src/_C018.cpp
@@ -356,7 +356,7 @@ bool C018_init(struct EventStruct *event) {
// Uncrustify may change this into multi line, which will result in failed builds
// *INDENT-OFF*
-bool do_process_c018_delay_queue(int controller_number, const Queue_element_base& element_base, ControllerSettingsStruct& ControllerSettings) {
+bool do_process_c018_delay_queue(cpluginID_t cpluginID, const Queue_element_base& element_base, ControllerSettingsStruct& ControllerSettings) {
const C018_queue_element& element = static_cast(element_base);
// *INDENT-ON*
uint8_t pl = (element.packed.length() / 2);
diff --git a/src/_P039_Thermosensors.ino b/src/_P039_Thermosensors.ino
index 122768a157..096d141089 100644
--- a/src/_P039_Thermosensors.ino
+++ b/src/_P039_Thermosensors.ino
@@ -617,11 +617,13 @@ boolean Plugin_039(uint8_t function, struct EventStruct *event, String& string)
if (loglevelActiveFor(LOG_LEVEL_INFO)) {
String log = strformat(F("P039 : %s :"), getTaskDeviceName(event->TaskIndex).c_str());
- for (uint8_t i = 0; i < getValueCountForTask(event->TaskIndex); ++i)
+ const uint8_t valueCount = getValueCountForTask(event->TaskIndex);
+
+ for (uint8_t i = 0; i < valueCount; ++i)
{
log += strformat(
F(" %s: %s"),
- getTaskValueName(event->TaskIndex, i).c_str(),
+ Cache.getTaskDeviceValueName(event->TaskIndex, i).c_str(),
formatUserVarNoCheck(event, i).c_str());
}
addLogMove(LOG_LEVEL_INFO, log);
diff --git a/src/_P110_VL53L0X.ino b/src/_P110_VL53L0X.ino
index 7661329e3c..0f45c94ac4 100644
--- a/src/_P110_VL53L0X.ino
+++ b/src/_P110_VL53L0X.ino
@@ -142,56 +142,30 @@ boolean Plugin_110(uint8_t function, struct EventStruct *event, String& string)
initPluginTaskData(event->TaskIndex, new (std::nothrow) P110_data_struct(P110_I2C_ADDRESS, P110_TIMING, P110_RANGE == 1));
P110_data_struct *P110_data = static_cast(getPluginTaskData(event->TaskIndex));
- success = (nullptr != P110_data) && P110_data->begin(); // Start the sensor
- break;
- }
- case PLUGIN_READ:
- {
- P110_data_struct *P110_data = static_cast(getPluginTaskData(event->TaskIndex));
-
if (nullptr != P110_data) {
- const uint16_t dist = P110_data->getDistance();
- const uint16_t p_dist = UserVar.getFloat(event->TaskIndex, 0);
- const int16_t direct = dist == p_dist ? 0 : (dist < p_dist ? -1 : 1);
- const bool triggered = (dist > (p_dist + P110_DELTA)) || (dist < (p_dist - P110_DELTA));
-
- #ifdef P110_INFO_LOG
-
- if (loglevelActiveFor(LOG_LEVEL_INFO)) {
- addLog(LOG_LEVEL_INFO, strformat(F("VL53L0x: Perform read: trig: %d, prev: %d, dist: %d"), triggered, p_dist, dist));
- }
- #endif // ifdef P110_INFO_LOG
-
- if (triggered || (P110_SEND_ALWAYS == 1)) {
- UserVar.setFloat(event->TaskIndex, 0, dist); // Value is classified as invalid when > 8190, so no conversion or 'split' needed
- UserVar.setFloat(event->TaskIndex, 1, direct);
- success = true;
- }
+ const uint32_t interval_ms = Settings.TaskDeviceTimer[event->TaskIndex] * 1000;
+
+ // Clear the "previous" distance so there will be a new result when starting the task
+ UserVar.setFloat(event->TaskIndex, 3, -1);
+ success = P110_data->begin(interval_ms); // Start the sensor
}
break;
}
-
- case PLUGIN_TEN_PER_SECOND: // Handle sensor reading
+ case PLUGIN_READ:
{
P110_data_struct *P110_data = static_cast(getPluginTaskData(event->TaskIndex));
if (nullptr != P110_data) {
- P110_data->readDistance();
-
- if (P110_data->isReadSuccessful() && (Settings.TaskDeviceTimer[event->TaskIndex] == 0)) { // Trigger as soon as there's a valid
- // measurement and 0 interval is set
- Scheduler.schedule_task_device_timer(event->TaskIndex, millis() + 10);
- }
+ success = P110_data->plugin_read(event);
}
break;
}
-
- case PLUGIN_FIFTY_PER_SECOND: // Handle startup delay
+ case PLUGIN_TEN_PER_SECOND: // Handle startup delay and sensor reading
{
P110_data_struct *P110_data = static_cast(getPluginTaskData(event->TaskIndex));
if (nullptr != P110_data) {
- success = P110_data->plugin_fifty_per_second();
+ success = P110_data->check_reading_ready(event);
}
break;
}
diff --git a/src/_P132_INA3221.ino b/src/_P132_INA3221.ino
index 7a1460dee0..43aaf8c081 100644
--- a/src/_P132_INA3221.ino
+++ b/src/_P132_INA3221.ino
@@ -70,10 +70,10 @@ boolean Plugin_132(uint8_t function, struct EventStruct *event, String& string)
const uint8_t i2cAddressValues[] = { 0x40, 0x41, 0x42, 0x43 };
if (function == PLUGIN_WEBFORM_SHOW_I2C_PARAMS) {
- addFormSelectorI2C(F("i2c_addr"), 4, i2cAddressValues, P132_I2C_ADDR);
+ addFormSelectorI2C(F("i2c_addr"), NR_ELEMENTS(i2cAddressValues), i2cAddressValues, P132_I2C_ADDR);
addFormNote(F("A0 connected to: GND= 0x40, VCC= 0x41, SDA= 0x42, SCL= 0x43"));
} else {
- success = intArrayContains(4, i2cAddressValues, event->Par1);
+ success = intArrayContains(NR_ELEMENTS(i2cAddressValues), i2cAddressValues, event->Par1);
}
break;
}
diff --git a/src/_P169_AS3935_LightningDetector.ino b/src/_P169_AS3935_LightningDetector.ino
new file mode 100644
index 0000000000..8f1f8692bd
--- /dev/null
+++ b/src/_P169_AS3935_LightningDetector.ino
@@ -0,0 +1,284 @@
+#include "_Plugin_Helper.h"
+#ifdef USES_P169
+
+// #######################################################################################################
+// ######################## Plugin 169 AS3935 Lightning Detector I2C ##################################
+// #######################################################################################################
+
+# include "./src/PluginStructs/P169_data_struct.h"
+
+# define PLUGIN_169
+# define PLUGIN_ID_169 169
+# define PLUGIN_NAME_169 "Environment - AS3935 Lightning Detector"
+# define PLUGIN_VALUENAME1_169 "DistanceNear"
+# define PLUGIN_VALUENAME2_169 "DistanceFar"
+# define PLUGIN_VALUENAME3_169 "Lightning"
+# define PLUGIN_VALUENAME4_169 "Total"
+
+
+boolean Plugin_169(uint8_t function, struct EventStruct *event, String& string)
+{
+ boolean success = false;
+
+ switch (function)
+ {
+ case PLUGIN_DEVICE_ADD:
+ {
+ Device[++deviceCount].Number = PLUGIN_ID_169;
+ Device[deviceCount].Type = DEVICE_TYPE_I2C;
+ Device[deviceCount].VType = Sensor_VType::SENSOR_TYPE_TRIPLE;
+ Device[deviceCount].Ports = 0;
+ Device[deviceCount].PullUpOption = false;
+ Device[deviceCount].InverseLogicOption = false;
+ Device[deviceCount].FormulaOption = true;
+ Device[deviceCount].ValueCount = 4;
+ Device[deviceCount].SendDataOption = true;
+ Device[deviceCount].TimerOption = true;
+ Device[deviceCount].I2CNoDeviceCheck = true; // Sensor may sometimes not respond immediately
+ Device[deviceCount].GlobalSyncOption = true;
+ Device[deviceCount].PluginStats = true;
+ break;
+ }
+
+
+ case PLUGIN_GET_DEVICENAME:
+ {
+ string = F(PLUGIN_NAME_169);
+ break;
+ }
+
+
+ case PLUGIN_GET_DEVICEVALUENAMES:
+ {
+ strcpy_P(ExtraTaskSettings.TaskDeviceValueNames[0], PSTR(PLUGIN_VALUENAME1_169));
+ strcpy_P(ExtraTaskSettings.TaskDeviceValueNames[1], PSTR(PLUGIN_VALUENAME2_169));
+ strcpy_P(ExtraTaskSettings.TaskDeviceValueNames[2], PSTR(PLUGIN_VALUENAME3_169));
+ strcpy_P(ExtraTaskSettings.TaskDeviceValueNames[3], PSTR(PLUGIN_VALUENAME4_169));
+ break;
+ }
+
+
+ case PLUGIN_SET_DEFAULTS:
+ {
+ // Set a default config here, which will be called when a plugin is assigned to a task.
+ P169_I2C_ADDRESS = P169_I2C_ADDRESS_DFLT;
+ P169_LIGHTNING_THRESHOLD = AS3935MI::AS3935_MNL_1;
+ P169_AFE_GAIN_LOW = AS3935MI::AS3935_OUTDOORS;
+ P169_AFE_GAIN_HIGH = AS3935MI::AS3935_OUTDOORS;
+ P169_SET_MASK_DISTURBANCE(false);
+ P169_SET_SEND_ONLY_ON_LIGHTNING(true);
+ P169_SET_TOLERANT_CALIBRATION_RANGE(true);
+
+ ExtraTaskSettings.TaskDeviceValueDecimals[0] = 1; // Distance Near
+ ExtraTaskSettings.TaskDeviceValueDecimals[1] = 1; // Distance Far
+ ExtraTaskSettings.TaskDeviceValueDecimals[2] = 0; // Lightning count since last PLUGIN_READ
+ ExtraTaskSettings.TaskDeviceValueDecimals[3] = 0; // Total lightning count
+ success = true;
+ break;
+ }
+
+
+ # if FEATURE_I2C_GET_ADDRESS
+ case PLUGIN_I2C_GET_ADDRESS:
+ {
+ event->Par1 = P169_I2C_ADDRESS;
+ success = true;
+ break;
+ }
+ # endif // if FEATURE_I2C_GET_ADDRESS
+
+
+ case PLUGIN_I2C_HAS_ADDRESS:
+ case PLUGIN_WEBFORM_SHOW_I2C_PARAMS:
+ {
+ const uint8_t i2cAddressValues[] = { 0x01, 0x02, 0x03 };
+
+ if (function == PLUGIN_WEBFORM_SHOW_I2C_PARAMS)
+ {
+ // addFormSelectorI2C(P169_I2C_ADDRESS_LABEL, 3, i2cAddressValues, P169_I2C_ADDRESS);
+ addFormSelectorI2C(F("i2c_addr"), NR_ELEMENTS(i2cAddressValues), i2cAddressValues, P169_I2C_ADDRESS);
+ addFormNote(F("Addr: 0-0-0-0-0-A1-A0. Both A0 & A1 low is not valid."));
+ }
+ else
+ {
+ success = intArrayContains(NR_ELEMENTS(i2cAddressValues), i2cAddressValues, event->Par1);
+ }
+ break;
+ }
+
+ case PLUGIN_WEBFORM_SHOW_GPIO_DESCR:
+ {
+ string = concat(F("IRQ: "), formatGpioLabel(P169_IRQ_PIN, false));
+ success = true;
+ break;
+ }
+
+ case PLUGIN_WEBFORM_LOAD:
+ {
+ addFormPinSelect(
+ PinSelectPurpose::Generic_input,
+ formatGpioName_input(F("IRQ")),
+ F(P169_IRQ_PIN_LABEL),
+ P169_IRQ_PIN);
+
+ {
+ const __FlashStringHelper *options[] = { F("1"), F("5"), F("9"), F("16") };
+ const int optionValues[] = {
+ AS3935MI::AS3935_MNL_1,
+ AS3935MI::AS3935_MNL_5,
+ AS3935MI::AS3935_MNL_9,
+ AS3935MI::AS3935_MNL_16 };
+ addFormSelector(F("Lightning Threshold"),
+ P169_LIGHTNING_THRESHOLD_LABEL,
+ NR_ELEMENTS(optionValues),
+ options,
+ optionValues,
+ P169_LIGHTNING_THRESHOLD);
+ addFormNote(F("Minimum number of lightning strikes in the last 15 minutes"));
+ }
+ {
+ const __FlashStringHelper *options[] = {
+ F("0.30x"),
+ F("0.40x"),
+ F("0.55x"),
+ F("0.74x"),
+ F("1.00x (Outdoor)"),
+ F("1.35x"),
+ F("1.83x"),
+ F("2.47x"),
+ F("3.34x (Indoor)")
+ };
+ const int optionValues[] = {
+ 10,
+ 11,
+ 12,
+ 13,
+ 14,
+ 15,
+ 16,
+ 17,
+ 18
+ };
+ addFormSelector(F("AFE Gain Min"), P169_AFE_GAIN_LOW_LABEL, NR_ELEMENTS(optionValues), options, optionValues, P169_AFE_GAIN_LOW);
+ addFormSelector(F("AFE Gain Max"), P169_AFE_GAIN_HIGH_LABEL, NR_ELEMENTS(optionValues), options, optionValues, P169_AFE_GAIN_HIGH);
+ addFormNote(F("Lower and upper limit for the Analog Frond-End auto gain to use."));
+ }
+
+ addFormCheckBox(F("Ignore Disturbance"), F(P169_MASK_DISTURBANCE_LABEL), P169_GET_MASK_DISTURBANCE);
+ addFormCheckBox(F("Tolerate out-of-range calibration"), F(P169_TOLERANT_CALIBRATION_RANGE_LABEL), P169_GET_TOLERANT_CALIBRATION_RANGE);
+ addFormNote(F("When checked, allow for more than 3.5% deviation for the 500 kHz LCO resonance frequency"));
+ addFormCheckBox(F("Slow LCO Calibration"), F(P169_SLOW_LCO_CALIBRATION_LABEL), P169_GET_SLOW_LCO_CALIBRATION);
+ addFormNote(F("Slow Calibration may improve accuracy of measured resonance frequency"));
+ addFormCheckBox(F("Send Only On Lightning"), F(P169_SEND_ONLY_ON_LIGHTNING_LABEL), P169_GET_SEND_ONLY_ON_LIGHTNING);
+ addFormNote(F("Only send to controller when lightning detected since last taskrun"));
+
+ P169_data_struct *P169_data =
+ static_cast(getPluginTaskData(event->TaskIndex));
+
+ if (P169_data != nullptr) {
+ P169_data->html_show_sensor_info(event);
+ }
+
+ success = true;
+ break;
+ }
+
+ case PLUGIN_WEBFORM_SAVE:
+ {
+ P169_I2C_ADDRESS = getFormItemInt(F("i2c_addr"));
+
+ /*
+ P169_NOISE = getFormItemInt(P169_NOISE_LABEL);
+ P169_WATCHDOG = getFormItemInt(P169_WATCHDOG_LABEL);
+ P169_SPIKE_REJECTION = getFormItemInt(P169_SPIKE_REJECTION_LABEL);
+ */
+ P169_LIGHTNING_THRESHOLD = getFormItemInt(P169_LIGHTNING_THRESHOLD_LABEL);
+ const int gain_low = getFormItemInt(P169_AFE_GAIN_LOW_LABEL);
+ const int gain_high = getFormItemInt(P169_AFE_GAIN_HIGH_LABEL);
+ P169_AFE_GAIN_LOW = gain_low;
+ P169_AFE_GAIN_HIGH = gain_high;
+
+ if (gain_low > gain_high) {
+ P169_AFE_GAIN_LOW = gain_high;
+ P169_AFE_GAIN_HIGH = gain_low;
+ }
+ P169_SET_MASK_DISTURBANCE(isFormItemChecked(F(P169_MASK_DISTURBANCE_LABEL)));
+ P169_SET_SEND_ONLY_ON_LIGHTNING(isFormItemChecked(F(P169_SEND_ONLY_ON_LIGHTNING_LABEL)));
+ P169_SET_TOLERANT_CALIBRATION_RANGE(isFormItemChecked(F(P169_TOLERANT_CALIBRATION_RANGE_LABEL)));
+ P169_SET_SLOW_LCO_CALIBRATION(isFormItemChecked(F(P169_SLOW_LCO_CALIBRATION_LABEL)));
+ success = true;
+ break;
+ }
+
+ case PLUGIN_INIT:
+ {
+ initPluginTaskData(event->TaskIndex, new (std::nothrow) P169_data_struct(event));
+ P169_data_struct *P169_data = static_cast(getPluginTaskData(event->TaskIndex));
+
+ if (nullptr != P169_data) {
+ success = P169_data->plugin_init(event);
+ }
+
+ break;
+ }
+
+ case PLUGIN_READ:
+ {
+ P169_data_struct *P169_data = static_cast(getPluginTaskData(event->TaskIndex));
+
+ if (nullptr != P169_data) {
+ if (P169_data->getAndClearLightningCount() > 0) {
+ success = true;
+ } else {
+ UserVar.setFloat(event->TaskIndex, 0, -1.0f);
+ UserVar.setFloat(event->TaskIndex, 1, -1.0f);
+ UserVar.setFloat(event->TaskIndex, 2, 0.0f);
+ P169_data->clearStatistics();
+
+ if (!P169_GET_SEND_ONLY_ON_LIGHTNING) {
+ success = true;
+ }
+ }
+ }
+
+ break;
+ }
+
+ case PLUGIN_TEN_PER_SECOND:
+ {
+ P169_data_struct *P169_data = static_cast(getPluginTaskData(event->TaskIndex));
+
+ if (nullptr != P169_data) {
+ if (P169_data->loop(event)) {}
+ success = true;
+ }
+ break;
+ }
+
+ case PLUGIN_WRITE:
+ {
+ P169_data_struct *P169_data = static_cast(getPluginTaskData(event->TaskIndex));
+
+ if (nullptr != P169_data) {
+ success = P169_data->plugin_write(event, string);
+ }
+
+ break;
+ }
+
+ case PLUGIN_GET_CONFIG_VALUE:
+ {
+ P169_data_struct *P169_data = static_cast(getPluginTaskData(event->TaskIndex));
+
+ if (nullptr != P169_data) {
+ success = P169_data->plugin_get_config_value(event, string);
+ }
+
+ break;
+ }
+ }
+
+ return success;
+} // function
+
+#endif // USES_P169
diff --git a/src/_Plugin_Helper.cpp b/src/_Plugin_Helper.cpp
index fedde4567a..5e49b55123 100644
--- a/src/_Plugin_Helper.cpp
+++ b/src/_Plugin_Helper.cpp
@@ -1,208 +1,208 @@
-#include "_Plugin_Helper.h"
-
-#include "ESPEasy_common.h"
-
-#include "src/CustomBuild/ESPEasyLimits.h"
-#include "src/DataStructs/PluginTaskData_base.h"
-#include "src/DataStructs/SettingsStruct.h"
-#include "src/DataStructs/TimingStats.h"
-#include "src/Globals/Cache.h"
-#include "src/Globals/Plugins.h"
-#include "src/Globals/Settings.h"
-#include "src/Helpers/Misc.h"
-#include "src/Helpers/StringParser.h"
-
-
-PluginTaskData_base *Plugin_task_data[TASKS_MAX] = {};
-
-
-String PCONFIG_LABEL(int n) {
- if (n < PLUGIN_CONFIGVAR_MAX) {
- return concat(F("pconf_"), n);
- }
- return F("error");
-}
-
-void resetPluginTaskData() {
- for (taskIndex_t i = 0; i < TASKS_MAX; ++i) {
- Plugin_task_data[i] = nullptr;
- }
-}
-
-void clearPluginTaskData(taskIndex_t taskIndex) {
- if (validTaskIndex(taskIndex)) {
- if (Plugin_task_data[taskIndex] != nullptr) {
- delete Plugin_task_data[taskIndex];
- Plugin_task_data[taskIndex] = nullptr;
- }
- }
-}
-
-bool initPluginTaskData(taskIndex_t taskIndex, PluginTaskData_base *data) {
- if (!validTaskIndex(taskIndex)) {
- if (data != nullptr) {
- delete data;
- }
- return false;
- }
-
- // 2nd heap may have been active to allocate the PluginTaskData, but here we need to keep the default heap active
- # ifdef USE_SECOND_HEAP
- HeapSelectDram ephemeral;
- # endif // ifdef USE_SECOND_HEAP
-
-
- clearPluginTaskData(taskIndex);
-
- if (data != nullptr) {
- if (Settings.TaskDeviceEnabled[taskIndex]) {
- Plugin_task_data[taskIndex] = data;
- Plugin_task_data[taskIndex]->_taskdata_pluginID = Settings.getPluginID_for_task(taskIndex);
-
- #if FEATURE_PLUGIN_STATS
- const uint8_t valueCount = getValueCountForTask(taskIndex);
- for (size_t i = 0; i < valueCount; ++i) {
- if (Cache.enabledPluginStats(taskIndex, i)) {
- Plugin_task_data[taskIndex]->initPluginStats(i);
- }
- }
- #endif
- #if FEATURE_PLUGIN_FILTER
- // TODO TD-er: Implement init
-
- #endif
-
- } else {
- delete data;
- }
- }
- return getPluginTaskData(taskIndex) != nullptr;
-}
-
-PluginTaskData_base* getPluginTaskData(taskIndex_t taskIndex) {
- if (pluginTaskData_initialized(taskIndex)) {
-
- if (!Plugin_task_data[taskIndex]->baseClassOnly()) {
- return Plugin_task_data[taskIndex];
- }
- }
- return nullptr;
-}
-
-PluginTaskData_base* getPluginTaskDataBaseClassOnly(taskIndex_t taskIndex) {
- if (pluginTaskData_initialized(taskIndex)) {
- return Plugin_task_data[taskIndex];
- }
- return nullptr;
-}
-
-
-bool pluginTaskData_initialized(taskIndex_t taskIndex) {
- if (!validTaskIndex(taskIndex)) {
- return false;
- }
- return Plugin_task_data[taskIndex] != nullptr &&
- (Plugin_task_data[taskIndex]->_taskdata_pluginID == Settings.getPluginID_for_task(taskIndex));
-}
-
-String getPluginCustomArgName(int varNr) {
- return getPluginCustomArgName(F("pc_arg"), varNr);
-}
-
-String getPluginCustomArgName(const __FlashStringHelper * label, int varNr) {
- return concat(label, varNr + 1);
-}
-
-int getFormItemIntCustomArgName(int varNr) {
- return getFormItemInt(getPluginCustomArgName(varNr));
-}
-
-// Helper function to create formatted custom values for display in the devices overview page.
-// When called from PLUGIN_WEBFORM_SHOW_VALUES, the last item should add a traling div_br class
-// if the regular values should also be displayed.
-// The call to PLUGIN_WEBFORM_SHOW_VALUES should only return success = true when no regular values should be displayed
-// Note that the varNr of the custom values should not conflict with the existing variable numbers (e.g. start at VARS_PER_TASK)
-void pluginWebformShowValue(taskIndex_t taskIndex, uint8_t varNr, const __FlashStringHelper * label, const String& value, bool addTrailingBreak) {
- pluginWebformShowValue(taskIndex, varNr, String(label), value, addTrailingBreak);
-}
-
-void pluginWebformShowValue(taskIndex_t taskIndex,
- uint8_t varNr,
- const String& label,
- const String& value,
- bool addTrailingBreak) {
- if (varNr > 0) {
- addHtmlDiv(F("div_br"));
- }
- String postfix(taskIndex);
- postfix += '_';
- postfix += varNr;
-
- pluginWebformShowValue(
- label, concat(F("valuename_"), postfix),
- value, concat(F("value_"), postfix),
- addTrailingBreak);
-}
-
-void pluginWebformShowValue(const String& valName, const String& value, bool addBR) {
- pluginWebformShowValue(valName, EMPTY_STRING, value, EMPTY_STRING, addBR);
-}
-
-void pluginWebformShowValue(const String& valName, const String& valName_id, const String& value, const String& value_id, bool addBR) {
- String valName_tmp(valName);
-
- if (!valName_tmp.endsWith(F(":"))) {
- valName_tmp += ':';
- }
- addHtmlDiv(F("div_l"), valName_tmp, valName_id);
- addHtmlDiv(F("div_r"), value, value_id);
-
- if (addBR) {
- addHtmlDiv(F("div_br"));
- }
-}
-
-bool pluginOptionalTaskIndexArgumentMatch(taskIndex_t taskIndex, const String& string, uint8_t paramNr) {
- if (!validTaskIndex(taskIndex)) {
- return false;
- }
- const taskIndex_t found_taskIndex = parseCommandArgumentTaskIndex(string, paramNr);
-
- if (!validTaskIndex(found_taskIndex)) {
- // Optional parameter not present
- return true;
- }
- return found_taskIndex == taskIndex;
-}
-
-bool pluginWebformShowGPIOdescription(taskIndex_t taskIndex,
- const __FlashStringHelper * newline,
- String& description)
-{
- struct EventStruct TempEvent(taskIndex);
- TempEvent.String1 = newline;
- return PluginCall(PLUGIN_WEBFORM_SHOW_GPIO_DESCR, &TempEvent, description);
-}
-
-int getValueCountForTask(taskIndex_t taskIndex) {
- struct EventStruct TempEvent(taskIndex);
- String dummy;
-
- PluginCall(PLUGIN_GET_DEVICEVALUECOUNT, &TempEvent, dummy);
- return TempEvent.Par1;
-}
-
-int checkDeviceVTypeForTask(struct EventStruct *event) {
- // TD-er: Do not use event->getSensorType() here
- if (event->sensorType == Sensor_VType::SENSOR_TYPE_NOT_SET) {
- if (validTaskIndex(event->TaskIndex)) {
- String dummy;
-
- event->idx = -1;
- if (PluginCall(PLUGIN_GET_DEVICEVTYPE, event, dummy)) {
- return event->idx; // pconfig_index
- }
- }
- }
- return -1;
-}
+#include "_Plugin_Helper.h"
+
+#include "ESPEasy_common.h"
+
+#include "src/CustomBuild/ESPEasyLimits.h"
+#include "src/DataStructs/PluginTaskData_base.h"
+#include "src/DataStructs/SettingsStruct.h"
+#include "src/DataStructs/TimingStats.h"
+#include "src/Globals/Cache.h"
+#include "src/Globals/Plugins.h"
+#include "src/Globals/Settings.h"
+#include "src/Helpers/Misc.h"
+#include "src/Helpers/StringParser.h"
+
+
+PluginTaskData_base *Plugin_task_data[TASKS_MAX] = {};
+
+
+String PCONFIG_LABEL(int n) {
+ if (n < PLUGIN_CONFIGVAR_MAX) {
+ return concat(F("pconf_"), n);
+ }
+ return F("error");
+}
+
+void resetPluginTaskData() {
+ for (taskIndex_t i = 0; i < TASKS_MAX; ++i) {
+ Plugin_task_data[i] = nullptr;
+ }
+}
+
+void clearPluginTaskData(taskIndex_t taskIndex) {
+ if (validTaskIndex(taskIndex)) {
+ if (Plugin_task_data[taskIndex] != nullptr) {
+ delete Plugin_task_data[taskIndex];
+ Plugin_task_data[taskIndex] = nullptr;
+ }
+ }
+}
+
+bool initPluginTaskData(taskIndex_t taskIndex, PluginTaskData_base *data) {
+ if (!validTaskIndex(taskIndex)) {
+ if (data != nullptr) {
+ delete data;
+ }
+ return false;
+ }
+
+ // 2nd heap may have been active to allocate the PluginTaskData, but here we need to keep the default heap active
+ # ifdef USE_SECOND_HEAP
+ HeapSelectDram ephemeral;
+ # endif // ifdef USE_SECOND_HEAP
+
+
+ clearPluginTaskData(taskIndex);
+
+ if (data != nullptr) {
+ if (Settings.TaskDeviceEnabled[taskIndex]) {
+ Plugin_task_data[taskIndex] = data;
+ Plugin_task_data[taskIndex]->_taskdata_pluginID = Settings.getPluginID_for_task(taskIndex);
+
+ #if FEATURE_PLUGIN_STATS
+ const uint8_t valueCount = getValueCountForTask(taskIndex);
+ for (size_t i = 0; i < valueCount; ++i) {
+ if (Cache.enabledPluginStats(taskIndex, i)) {
+ Plugin_task_data[taskIndex]->initPluginStats(taskIndex, i);
+ }
+ }
+ #endif
+ #if FEATURE_PLUGIN_FILTER
+ // TODO TD-er: Implement init
+
+ #endif
+
+ } else {
+ delete data;
+ }
+ }
+ return getPluginTaskData(taskIndex) != nullptr;
+}
+
+PluginTaskData_base* getPluginTaskData(taskIndex_t taskIndex) {
+ if (pluginTaskData_initialized(taskIndex)) {
+
+ if (!Plugin_task_data[taskIndex]->baseClassOnly()) {
+ return Plugin_task_data[taskIndex];
+ }
+ }
+ return nullptr;
+}
+
+PluginTaskData_base* getPluginTaskDataBaseClassOnly(taskIndex_t taskIndex) {
+ if (pluginTaskData_initialized(taskIndex)) {
+ return Plugin_task_data[taskIndex];
+ }
+ return nullptr;
+}
+
+
+bool pluginTaskData_initialized(taskIndex_t taskIndex) {
+ if (!validTaskIndex(taskIndex)) {
+ return false;
+ }
+ return Plugin_task_data[taskIndex] != nullptr &&
+ (Plugin_task_data[taskIndex]->_taskdata_pluginID == Settings.getPluginID_for_task(taskIndex));
+}
+
+String getPluginCustomArgName(int varNr) {
+ return getPluginCustomArgName(F("pc_arg"), varNr);
+}
+
+String getPluginCustomArgName(const __FlashStringHelper * label, int varNr) {
+ return concat(label, varNr + 1);
+}
+
+int getFormItemIntCustomArgName(int varNr) {
+ return getFormItemInt(getPluginCustomArgName(varNr));
+}
+
+// Helper function to create formatted custom values for display in the devices overview page.
+// When called from PLUGIN_WEBFORM_SHOW_VALUES, the last item should add a traling div_br class
+// if the regular values should also be displayed.
+// The call to PLUGIN_WEBFORM_SHOW_VALUES should only return success = true when no regular values should be displayed
+// Note that the varNr of the custom values should not conflict with the existing variable numbers (e.g. start at VARS_PER_TASK)
+void pluginWebformShowValue(taskIndex_t taskIndex, uint8_t varNr, const __FlashStringHelper * label, const String& value, bool addTrailingBreak) {
+ pluginWebformShowValue(taskIndex, varNr, String(label), value, addTrailingBreak);
+}
+
+void pluginWebformShowValue(taskIndex_t taskIndex,
+ uint8_t varNr,
+ const String& label,
+ const String& value,
+ bool addTrailingBreak) {
+ if (varNr > 0) {
+ addHtmlDiv(F("div_br"));
+ }
+ String postfix(taskIndex);
+ postfix += '_';
+ postfix += varNr;
+
+ pluginWebformShowValue(
+ label, concat(F("valuename_"), postfix),
+ value, concat(F("value_"), postfix),
+ addTrailingBreak);
+}
+
+void pluginWebformShowValue(const String& valName, const String& value, bool addBR) {
+ pluginWebformShowValue(valName, EMPTY_STRING, value, EMPTY_STRING, addBR);
+}
+
+void pluginWebformShowValue(const String& valName, const String& valName_id, const String& value, const String& value_id, bool addBR) {
+ String valName_tmp(valName);
+
+ if (!valName_tmp.endsWith(F(":"))) {
+ valName_tmp += ':';
+ }
+ addHtmlDiv(F("div_l"), valName_tmp, valName_id);
+ addHtmlDiv(F("div_r"), value, value_id);
+
+ if (addBR) {
+ addHtmlDiv(F("div_br"));
+ }
+}
+
+bool pluginOptionalTaskIndexArgumentMatch(taskIndex_t taskIndex, const String& string, uint8_t paramNr) {
+ if (!validTaskIndex(taskIndex)) {
+ return false;
+ }
+ const taskIndex_t found_taskIndex = parseCommandArgumentTaskIndex(string, paramNr);
+
+ if (!validTaskIndex(found_taskIndex)) {
+ // Optional parameter not present
+ return true;
+ }
+ return found_taskIndex == taskIndex;
+}
+
+bool pluginWebformShowGPIOdescription(taskIndex_t taskIndex,
+ const __FlashStringHelper * newline,
+ String& description)
+{
+ struct EventStruct TempEvent(taskIndex);
+ TempEvent.String1 = newline;
+ return PluginCall(PLUGIN_WEBFORM_SHOW_GPIO_DESCR, &TempEvent, description);
+}
+
+int getValueCountForTask(taskIndex_t taskIndex) {
+ struct EventStruct TempEvent(taskIndex);
+ String dummy;
+
+ PluginCall(PLUGIN_GET_DEVICEVALUECOUNT, &TempEvent, dummy);
+ return TempEvent.Par1;
+}
+
+int checkDeviceVTypeForTask(struct EventStruct *event) {
+ // TD-er: Do not use event->getSensorType() here
+ if (event->sensorType == Sensor_VType::SENSOR_TYPE_NOT_SET) {
+ if (validTaskIndex(event->TaskIndex)) {
+ String dummy;
+
+ event->idx = -1;
+ if (PluginCall(PLUGIN_GET_DEVICEVTYPE, event, dummy)) {
+ return event->idx; // pconfig_index
+ }
+ }
+ }
+ return -1;
+}
diff --git a/src/src/ControllerQueue/C016_queue_element.cpp b/src/src/ControllerQueue/C016_queue_element.cpp
index 7f4ffb350d..d21222ee39 100644
--- a/src/src/ControllerQueue/C016_queue_element.cpp
+++ b/src/src/ControllerQueue/C016_queue_element.cpp
@@ -1,102 +1,102 @@
-#include "../ControllerQueue/C016_queue_element.h"
-
-#ifdef USES_C016
-
-# include "../DataStructs/ESPEasy_EventStruct.h"
-# include "../Globals/Plugins.h"
-# include "../Globals/RuntimeData.h"
-# include "../Helpers/_Plugin_SensorTypeHelper.h"
-# include "../Helpers/ESPEasy_math.h"
-
-C016_queue_element::C016_queue_element() : sensorType(
- Sensor_VType::SENSOR_TYPE_NONE) {
- _timestamp = 0;
- _controller_idx = 0;
- _taskIndex = INVALID_TASK_INDEX;
- values.clear();
-}
-
-C016_queue_element::C016_queue_element(C016_queue_element&& other)
- : sensorType(other.sensorType)
- , valueCount(other.valueCount)
-{
- _timestamp = other._timestamp;
- _controller_idx = other._controller_idx;
- _taskIndex = other._taskIndex;
- values = other.values;
-}
-
-C016_queue_element::C016_queue_element(const struct EventStruct *event, uint8_t value_count) :
- unixTime(event->timestamp),
- sensorType(event->sensorType),
- valueCount(value_count)
-{
- _controller_idx = event->ControllerIndex;
- _taskIndex = event->TaskIndex;
- values.clear();
- const TaskValues_Data_t* data = UserVar.getRawTaskValues_Data(event->TaskIndex);
-
- if (data != nullptr) {
- for (uint8_t i = 0; i < value_count; ++i) {
- values.copyValue(*data, i, sensorType);
- }
- }
-}
-
-C016_queue_element& C016_queue_element::operator=(C016_queue_element&& other) {
- _timestamp = other._timestamp;
- _taskIndex = other._taskIndex;
- _controller_idx = other._controller_idx;
- sensorType = other.sensorType;
- valueCount = other.valueCount;
- unixTime = other.unixTime;
- values = other.values;
-
- return *this;
-}
-
-size_t C016_queue_element::getSize() const {
- return sizeof(*this);
-}
-
-bool C016_queue_element::isDuplicate(const Queue_element_base& other) const {
- const C016_queue_element& oth = static_cast(other);
-
- if ((oth._controller_idx != _controller_idx) ||
- (oth._taskIndex != _taskIndex) ||
- (oth.sensorType != sensorType) ||
- (oth.valueCount != valueCount)) {
- return false;
- }
-
- for (uint8_t i = 0; i < valueCount; ++i) {
- if (isFloatOutputDataType(sensorType)) {
- if (!essentiallyEqual(oth.values.getFloat(i), values.getFloat(i))) {
- return false;
- }
- } else {
- if (oth.values.getUint32(i) != values.getUint32(i)) {
- return false;
- }
- }
- }
- return true;
-}
-
-C016_binary_element C016_queue_element::getBinary() const {
- C016_binary_element element;
-
- element.unixTime = unixTime;
- element.TaskIndex = _taskIndex;
- element.sensorType = sensorType;
- element.valueCount = valueCount;
- element.values = values;
-
- // It makes no sense to keep the controller index when storing it.
- // re-purpose it to store the pluginID
- element.pluginID = getPluginID_from_TaskIndex(_taskIndex);
-
- return element;
-}
-
-#endif // ifdef USES_C016
+#include "../ControllerQueue/C016_queue_element.h"
+
+#ifdef USES_C016
+
+# include "../DataStructs/ESPEasy_EventStruct.h"
+# include "../Globals/Plugins.h"
+# include "../Globals/RuntimeData.h"
+# include "../Helpers/_Plugin_SensorTypeHelper.h"
+# include "../Helpers/ESPEasy_math.h"
+
+C016_queue_element::C016_queue_element() : sensorType(
+ Sensor_VType::SENSOR_TYPE_NONE) {
+ _timestamp = 0;
+ _controller_idx = 0;
+ _taskIndex = INVALID_TASK_INDEX;
+ values.clear();
+}
+
+C016_queue_element::C016_queue_element(C016_queue_element&& other)
+ : sensorType(other.sensorType)
+ , valueCount(other.valueCount)
+{
+ _timestamp = other._timestamp;
+ _controller_idx = other._controller_idx;
+ _taskIndex = other._taskIndex;
+ values = other.values;
+}
+
+C016_queue_element::C016_queue_element(const struct EventStruct *event, uint8_t value_count) :
+ unixTime(event->timestamp_sec),
+ sensorType(event->sensorType),
+ valueCount(value_count)
+{
+ _controller_idx = event->ControllerIndex;
+ _taskIndex = event->TaskIndex;
+ values.clear();
+ const TaskValues_Data_t* data = UserVar.getRawTaskValues_Data(event->TaskIndex);
+
+ if (data != nullptr) {
+ for (uint8_t i = 0; i < value_count; ++i) {
+ values.copyValue(*data, i, sensorType);
+ }
+ }
+}
+
+C016_queue_element& C016_queue_element::operator=(C016_queue_element&& other) {
+ _timestamp = other._timestamp;
+ _taskIndex = other._taskIndex;
+ _controller_idx = other._controller_idx;
+ sensorType = other.sensorType;
+ valueCount = other.valueCount;
+ unixTime = other.unixTime;
+ values = other.values;
+
+ return *this;
+}
+
+size_t C016_queue_element::getSize() const {
+ return sizeof(*this);
+}
+
+bool C016_queue_element::isDuplicate(const Queue_element_base& other) const {
+ const C016_queue_element& oth = static_cast(other);
+
+ if ((oth._controller_idx != _controller_idx) ||
+ (oth._taskIndex != _taskIndex) ||
+ (oth.sensorType != sensorType) ||
+ (oth.valueCount != valueCount)) {
+ return false;
+ }
+
+ for (uint8_t i = 0; i < valueCount; ++i) {
+ if (isFloatOutputDataType(sensorType)) {
+ if (!essentiallyEqual(oth.values.getFloat(i), values.getFloat(i))) {
+ return false;
+ }
+ } else {
+ if (oth.values.getUint32(i) != values.getUint32(i)) {
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
+C016_binary_element C016_queue_element::getBinary() const {
+ C016_binary_element element;
+
+ element.unixTime = unixTime;
+ element.TaskIndex = _taskIndex;
+ element.sensorType = sensorType;
+ element.valueCount = valueCount;
+ element.values = values;
+
+ // It makes no sense to keep the controller index when storing it.
+ // re-purpose it to store the pluginID
+ element.pluginID = getPluginID_from_TaskIndex(_taskIndex);
+
+ return element;
+}
+
+#endif // ifdef USES_C016
diff --git a/src/src/ControllerQueue/ControllerDelayHandlerStruct.cpp b/src/src/ControllerQueue/ControllerDelayHandlerStruct.cpp
index 98b95cdbe1..9bdb7ac0f7 100644
--- a/src/src/ControllerQueue/ControllerDelayHandlerStruct.cpp
+++ b/src/src/ControllerQueue/ControllerDelayHandlerStruct.cpp
@@ -258,7 +258,7 @@ size_t ControllerDelayHandlerStruct::getQueueMemorySize() const {
}
void ControllerDelayHandlerStruct::process(
- int controller_number,
+ cpluginID_t cpluginID,
do_process_function func,
TimingStatsElements timerstats_id,
SchedulerIntervalTimer_e timerID)
@@ -274,7 +274,7 @@ void ControllerDelayHandlerStruct::process(
LoadControllerSettings(element->_controller_idx, *ControllerSettings);
cacheControllerSettings(*ControllerSettings);
START_TIMER;
- markProcessed(func(controller_number, *element, *ControllerSettings));
+ markProcessed(func(cpluginID, *element, *ControllerSettings));
#if FEATURE_TIMING_STATS
STOP_TIMER_VAR(timerstats_id);
#endif
diff --git a/src/src/ControllerQueue/ControllerDelayHandlerStruct.h b/src/src/ControllerQueue/ControllerDelayHandlerStruct.h
index d6bd792bd9..f5442cf502 100644
--- a/src/src/ControllerQueue/ControllerDelayHandlerStruct.h
+++ b/src/src/ControllerQueue/ControllerDelayHandlerStruct.h
@@ -28,7 +28,7 @@
# define CONTROLLER_QUEUE_MINIMAL_EXPIRE_TIME 10000
#endif // ifndef CONTROLLER_QUEUE_MINIMAL_EXPIRE_TIME
-typedef bool (*do_process_function)(int,
+typedef bool (*do_process_function)(cpluginID_t,
const Queue_element_base&,
ControllerSettingsStruct&);
@@ -71,7 +71,7 @@ struct ControllerDelayHandlerStruct {
size_t getQueueMemorySize() const;
void process(
- int controller_number,
+ cpluginID_t cpluginID,
do_process_function func,
TimingStatsElements timerstats_id,
SchedulerIntervalTimer_e timerID);
diff --git a/src/src/ControllerQueue/DelayQueueElements.h b/src/src/ControllerQueue/DelayQueueElements.h
index cf6e73c2a9..3c44e89edf 100644
--- a/src/src/ControllerQueue/DelayQueueElements.h
+++ b/src/src/ControllerQueue/DelayQueueElements.h
@@ -52,7 +52,7 @@
// in the element stored in the queue.
#define DEFINE_Cxxx_DELAY_QUEUE_MACRO(NNN, M) \
extern struct ControllerDelayHandlerStruct *C##NNN####M##_DelayHandler; \
- bool do_process_c##NNN####M##_delay_queue(int controller_number, const Queue_element_base & element, ControllerSettingsStruct & ControllerSettings); \
+ bool do_process_c##NNN####M##_delay_queue(cpluginID_t cpluginID, const Queue_element_base & element, ControllerSettingsStruct & ControllerSettings); \
void process_c##NNN####M##_delay_queue(); \
bool init_c##NNN####M##_delay_queue(controllerIndex_t ControllerIndex); \
void exit_c##NNN####M##_delay_queue(); \
diff --git a/src/src/CustomBuild/ESPEasyLimits.h b/src/src/CustomBuild/ESPEasyLimits.h
index f2a0048bb1..73b5ecdeb8 100644
--- a/src/src/CustomBuild/ESPEasyLimits.h
+++ b/src/src/CustomBuild/ESPEasyLimits.h
@@ -151,7 +151,7 @@
#define MAX_FLASHWRITES_PER_DAY 100 // per 24 hour window
#endif
#ifndef UDP_PACKETSIZE_MAX
- #define UDP_PACKETSIZE_MAX 256 // Currently only needed for C013_Receive
+ #define UDP_PACKETSIZE_MAX 512 // Currently only needed for C013_Receive
#endif
#ifndef TIMER_GRATUITOUS_ARP_MAX
#define TIMER_GRATUITOUS_ARP_MAX 5000
diff --git a/src/src/CustomBuild/define_plugin_sets.h b/src/src/CustomBuild/define_plugin_sets.h
index 0737c45372..6342bcc27d 100644
--- a/src/src/CustomBuild/define_plugin_sets.h
+++ b/src/src/CustomBuild/define_plugin_sets.h
@@ -1962,6 +1962,10 @@ To create/register a plugin, you have to :
#define USES_P168 // Light - VEML6030/VEML7700
#endif
+ #ifndef USES_P169
+ #define USES_P169 // Environment - AS3935 Lightning Detector
+ #endif
+
// Controllers
#ifndef USES_C011
#define USES_C011 // HTTP Advanced
@@ -2023,17 +2027,29 @@ To create/register a plugin, you have to :
#endif
#ifdef CONTROLLER_SET_COLLECTION
+ #ifndef USES_C011
#define USES_C011 // Generic HTTP Advanced
+ #endif
+ #ifndef USES_C012
#define USES_C012 // Blynk HTTP
+ #endif
+ #ifndef USES_C014
#define USES_C014 // homie 3 & 4dev MQTT
+ #endif
+ #ifndef USES_C015
//#define USES_C015 // Blynk
+ #endif
+ #ifndef USES_C017
#define USES_C017 // Zabbix
- #ifdef ESP32
- #ifndef USES_C018
- #define USES_C018 // TTN RN2483
- #endif
+ #endif
+ #ifdef ESP32
+ #ifndef USES_C018
+ #define USES_C018 // TTN RN2483
#endif
+ #endif
+ #ifndef USES_C019
// #define USES_C019 // ESPEasy-NOW
+ #endif
#endif
@@ -2374,7 +2390,7 @@ To create/register a plugin, you have to :
#define USES_P166 // Output - GP8403 DAC 0-10V
#endif
#ifndef USES_P167
- #define USES_P167 // Environment - SensirionSEN5x / Ikea Vindstyrka
+ #define USES_P167 // Environment - IKEA Vindstyrka SEN54 temperature , humidity and air quality
#endif
#ifndef USES_P168
#define USES_P168 // Light - VEML6030/VEML7700
@@ -2383,6 +2399,10 @@ To create/register a plugin, you have to :
#define USES_P170 // Input - I2C Liquid level sensor
#endif
+ #ifndef USES_P169
+ #define USES_P169 // Environment - AS3935 Lightning Detector
+ #endif
+
// Controllers
#ifndef USES_C015
#define USES_C015 // Blynk
@@ -2839,7 +2859,13 @@ To create/register a plugin, you have to :
#ifndef LIMIT_BUILD_SIZE
#ifndef FEATURE_MDNS
#ifdef ESP32
- #define FEATURE_MDNS 0
+ #if ESP_IDF_VERSION_MAJOR >= 5
+ // See if it is now more usable...
+ // See: https://github.com/letscontrolit/ESPEasy/issues/5061
+ #define FEATURE_MDNS 0
+ #else
+ #define FEATURE_MDNS 0
+ #endif
#else
// Do not use MDNS on ESP8266 due to memory leak
#define FEATURE_MDNS 0
diff --git a/src/src/DataStructs/C013_p2p_SensorDataStruct.cpp b/src/src/DataStructs/C013_p2p_SensorDataStruct.cpp
new file mode 100644
index 0000000000..881a378236
--- /dev/null
+++ b/src/src/DataStructs/C013_p2p_SensorDataStruct.cpp
@@ -0,0 +1,116 @@
+#include "../DataStructs/C013_p2p_SensorDataStruct.h"
+
+#ifdef USES_C013
+
+# include "../DataStructs/NodeStruct.h"
+# include "../Globals/Nodes.h"
+# include "../Globals/ESPEasy_time.h"
+# include "../Globals/Plugins.h"
+
+# include "../CustomBuild/CompiletimeDefines.h"
+
+bool C013_SensorDataStruct::prepareForSend()
+{
+ sourceNodeBuild = get_build_nr();
+ checksum.clear();
+
+ if (sourceNodeBuild >= 20871) {
+ if (node_time.systemTimePresent()) {
+ uint32_t unix_time_frac{};
+ timestamp_sec = node_time.getUnixTime(unix_time_frac);
+ timestamp_frac = unix_time_frac >> 16;
+ }
+
+
+ // Make sure to add checksum as last step
+ constexpr unsigned len_upto_checksum = offsetof(C013_SensorDataStruct, checksum);
+
+ const ShortChecksumType tmpChecksum(
+ reinterpret_cast(this),
+ sizeof(C013_SensorDataStruct),
+ len_upto_checksum);
+
+ checksum = tmpChecksum;
+ }
+
+ return validTaskIndex(sourceTaskIndex) &&
+ validTaskIndex(destTaskIndex);
+}
+
+bool C013_SensorDataStruct::setData(const uint8_t *data, size_t size)
+{
+ // First clear entire struct
+ memset(this, 0, sizeof(C013_SensorDataStruct));
+
+ if (size < 6) {
+ return false;
+ }
+
+ if ((data[0] != 255) || // header
+ (data[1] != 5)) { // ID
+ return false;
+ }
+
+ constexpr unsigned len_upto_checksum = offsetof(C013_SensorDataStruct, checksum);
+ const ShortChecksumType tmpChecksum(
+ data,
+ size,
+ len_upto_checksum);
+
+
+ // Need to keep track of different possible versions of data which still need to be supported.
+ // Really old versions of ESPEasy might send upto 80 bytes of uninitialized data
+ // meaning for sizes > 24 bytes we may need to check the version of ESPEasy running on the node.
+ if (size > sizeof(C013_SensorDataStruct)) {
+ size = sizeof(C013_SensorDataStruct);
+ }
+ NodeStruct *sourceNode = Nodes.getNode(data[2]); // sourceUnit
+
+ if (sourceNode != nullptr) {
+ if (sourceNode->build < 20871) {
+ if (size > 24) {
+ size = 24;
+ }
+ }
+ }
+
+ if (size <= 24) {
+ deviceNumber = INVALID_PLUGIN_ID;
+ sensorType = Sensor_VType::SENSOR_TYPE_NONE;
+
+ if (sourceNode != nullptr) {
+ sourceNodeBuild = sourceNode->build;
+ }
+ }
+
+ memcpy(this, data, size);
+
+ if (checksum.isSet()) {
+ if (!(tmpChecksum == checksum)) {
+ return false;
+ }
+ }
+
+ return validTaskIndex(sourceTaskIndex) &&
+ validTaskIndex(destTaskIndex);
+}
+
+bool C013_SensorDataStruct::matchesPluginID(pluginID_t pluginID) const
+{
+ if ((deviceNumber.value == 255) || !validPluginID(deviceNumber) || !validPluginID(pluginID)) {
+ // Was never set, so probably received data from older node.
+ return true;
+ }
+ return pluginID == deviceNumber;
+}
+
+bool C013_SensorDataStruct::matchesSensorType(Sensor_VType sensor_type) const
+{
+ if ((deviceNumber.value == 255) || (sensorType == Sensor_VType::SENSOR_TYPE_NONE)) {
+ // Was never set, so probably received data from older node.
+ return true;
+ }
+ return sensorType == sensor_type;
+}
+
+#endif // ifdef USES_C013
diff --git a/src/src/DataStructs/C013_p2p_SensorDataStruct.h b/src/src/DataStructs/C013_p2p_SensorDataStruct.h
new file mode 100644
index 0000000000..8306d2d0bc
--- /dev/null
+++ b/src/src/DataStructs/C013_p2p_SensorDataStruct.h
@@ -0,0 +1,59 @@
+#ifndef DATASTRUCTS_C013_P2P_SENSORDATASTRUCTS_H
+#define DATASTRUCTS_C013_P2P_SENSORDATASTRUCTS_H
+
+#include "../../ESPEasy_common.h"
+
+#ifdef USES_C013
+
+
+# include "../CustomBuild/ESPEasyLimits.h"
+# include "../DataStructs/DeviceStruct.h"
+# include "../DataStructs/ShortChecksumType.h"
+# include "../DataTypes/TaskIndex.h"
+# include "../DataTypes/TaskValues_Data.h"
+# include "../DataTypes/PluginID.h"
+
+
+// These structs are sent to other nodes, so make sure not to change order or offset in struct.
+struct __attribute__((__packed__)) C013_SensorDataStruct
+{
+ C013_SensorDataStruct() = default;
+
+ bool setData(const uint8_t *data,
+ size_t size);
+
+ bool prepareForSend();
+
+ bool matchesPluginID(pluginID_t pluginID) const;
+
+ bool matchesSensorType(Sensor_VType sensor_type) const;
+
+ uint8_t header = 255;
+ uint8_t ID = 5;
+ uint8_t sourceUnit = 0;
+ uint8_t destUnit = 0;
+ taskIndex_t sourceTaskIndex = INVALID_TASK_INDEX;
+ taskIndex_t destTaskIndex = INVALID_TASK_INDEX;
+
+ // deviceNumber and sensorType were not present before build 2023-05-05. (build NR 20460)
+ // See:
+ // https://github.com/letscontrolit/ESPEasy/commit/cf791527eeaf31ca98b07c45c1b64e2561a7b041#diff-86b42dd78398b103e272503f05f55ee0870ae5fb907d713c2505d63279bb0321
+ // Thus should not be checked
+ pluginID_t deviceNumber = INVALID_PLUGIN_ID;
+ Sensor_VType sensorType = Sensor_VType::SENSOR_TYPE_NONE;
+ TaskValues_Data_t values{};
+
+ // Extra info added on 20240619 (build ID 20871)
+ ShortChecksumType checksum;
+ uint16_t sourceNodeBuild = 0;
+ uint16_t timestamp_frac = 0;
+ uint32_t timestamp_sec = 0;
+
+ // Optional IDX value to allow receiving remote
+ // feed data on a different task index as is used on the sender node.
+ uint32_t IDX = 0;
+};
+
+#endif // ifdef USES_C013
+
+#endif // ifndef DATASTRUCTS_C013_P2P_SENSORDATASTRUCTS_H
diff --git a/src/src/DataStructs/C013_p2p_SensorInfoStruct.cpp b/src/src/DataStructs/C013_p2p_SensorInfoStruct.cpp
new file mode 100644
index 0000000000..d215bd07d5
--- /dev/null
+++ b/src/src/DataStructs/C013_p2p_SensorInfoStruct.cpp
@@ -0,0 +1,147 @@
+#include "../DataStructs/C013_p2p_SensorInfoStruct.h"
+
+#ifdef USES_C013
+
+# include "../DataStructs/NodeStruct.h"
+# include "../Globals/ExtraTaskSettings.h"
+# include "../Globals/Nodes.h"
+# include "../Globals/Plugins.h"
+# include "../Globals/Settings.h"
+
+# include "../CustomBuild/CompiletimeDefines.h"
+
+# include "../Helpers/ESPEasy_Storage.h"
+# include "../Helpers/StringConverter.h"
+
+bool C013_SensorInfoStruct::prepareForSend(size_t& sizeToSend)
+{
+ if (!(validTaskIndex(sourceTaskIndex) &&
+ validTaskIndex(destTaskIndex) &&
+ validPluginID(deviceNumber))) {
+ return false;
+ }
+
+ sizeToSend = sizeof(C013_SensorInfoStruct);
+
+ sourceNodeBuild = get_build_nr();
+ checksum.clear();
+
+ ZERO_FILL(taskName);
+ safe_strncpy(taskName, getTaskDeviceName(sourceTaskIndex), sizeof(taskName));
+
+ for (uint8_t x = 0; x < VARS_PER_TASK; x++) {
+ ZERO_FILL(ValueNames[x]);
+ safe_strncpy(ValueNames[x], getTaskValueName(sourceTaskIndex, x), sizeof(ValueNames[x]));
+ }
+
+
+ if (sourceNodeBuild >= 20871) {
+ LoadTaskSettings(sourceTaskIndex);
+
+ ExtraTaskSettings_version = ExtraTaskSettings.version;
+
+ for (uint8_t x = 0; x < VARS_PER_TASK; x++) {
+ TaskDeviceValueDecimals[x] = ExtraTaskSettings.TaskDeviceValueDecimals[x];
+ TaskDeviceMinValue[x] = ExtraTaskSettings.TaskDeviceMinValue[x];
+ TaskDeviceMaxValue[x] = ExtraTaskSettings.TaskDeviceMaxValue[x];
+ TaskDeviceErrorValue[x] = ExtraTaskSettings.TaskDeviceErrorValue[x];
+ VariousBits[x] = ExtraTaskSettings.VariousBits[x];
+
+/*
+ ZERO_FILL(TaskDeviceFormula[x]);
+
+ if (ExtraTaskSettings.TaskDeviceFormula[x][0] != 0) {
+ safe_strncpy(TaskDeviceFormula[x], ExtraTaskSettings.TaskDeviceFormula[x], sizeof(TaskDeviceFormula[x]));
+ }
+*/
+ }
+
+ for (uint8_t x = 0; x < PLUGIN_CONFIGVAR_MAX; ++x) {
+ TaskDevicePluginConfig[x] = Settings.TaskDevicePluginConfig[sourceTaskIndex][x];
+ }
+ }
+
+ // Check to see if last bytes are all zero, so we can simply not send them
+ bool doneShrinking = false;
+ constexpr unsigned len_upto_sourceNodeBuild = offsetof(C013_SensorInfoStruct, sourceNodeBuild);
+
+ const uint8_t *data = reinterpret_cast(this);
+
+ while (!doneShrinking) {
+ if (sizeToSend < len_upto_sourceNodeBuild) {
+ doneShrinking = true;
+ }
+ else {
+ if (data[sizeToSend - 1] == 0) {
+ --sizeToSend;
+ } else {
+ doneShrinking = true;
+ }
+ }
+ }
+
+ if (sourceNodeBuild >= 20871) {
+ // Make sure to add checksum as last step
+ constexpr unsigned len_upto_checksum = offsetof(C013_SensorInfoStruct, checksum);
+ const ShortChecksumType tmpChecksum(
+ reinterpret_cast(this),
+ sizeToSend,
+ len_upto_checksum);
+
+ checksum = tmpChecksum;
+ }
+
+ return true;
+}
+
+bool C013_SensorInfoStruct::setData(const uint8_t *data, size_t size)
+{
+ // First clear entire struct
+ memset(this, 0, sizeof(C013_SensorInfoStruct));
+
+ if (size < 6) {
+ return false;
+ }
+
+ if ((data[0] != 255) || // header
+ (data[1] != 3)) { // ID
+ return false;
+ }
+
+ // Before copying the data, compute the checksum of the entire packet
+ constexpr unsigned len_upto_checksum = offsetof(C013_SensorInfoStruct, checksum);
+ const ShortChecksumType tmpChecksum(
+ data,
+ size,
+ len_upto_checksum);
+
+ // Need to keep track of different possible versions of data which still need to be supported.
+ if (size > sizeof(C013_SensorInfoStruct)) {
+ size = sizeof(C013_SensorInfoStruct);
+ }
+
+ if (size <= 138) {
+ deviceNumber = INVALID_PLUGIN_ID;
+ sensorType = Sensor_VType::SENSOR_TYPE_NONE;
+
+ NodeStruct *sourceNode = Nodes.getNode(data[2]); // sourceUnit
+
+ if (sourceNode != nullptr) {
+ sourceNodeBuild = sourceNode->build;
+ }
+ }
+
+ memcpy(this, data, size);
+
+ if (checksum.isSet()) {
+ if (!(tmpChecksum == checksum)) {
+ return false;
+ }
+ }
+
+ return validTaskIndex(sourceTaskIndex) &&
+ validTaskIndex(destTaskIndex) &&
+ validPluginID(deviceNumber);
+}
+
+#endif // ifdef USES_C013
diff --git a/src/src/DataStructs/C013_p2p_SensorInfoStruct.h b/src/src/DataStructs/C013_p2p_SensorInfoStruct.h
new file mode 100644
index 0000000000..7f4650698e
--- /dev/null
+++ b/src/src/DataStructs/C013_p2p_SensorInfoStruct.h
@@ -0,0 +1,65 @@
+#ifndef DATASTRUCTS_C013_P2P_SENSORINFOSTRUCTS_H
+#define DATASTRUCTS_C013_P2P_SENSORINFOSTRUCTS_H
+
+#include "../../ESPEasy_common.h"
+
+#ifdef USES_C013
+
+
+# include "../CustomBuild/ESPEasyLimits.h"
+# include "../DataStructs/DeviceStruct.h"
+# include "../DataStructs/ShortChecksumType.h"
+# include "../DataTypes/TaskIndex.h"
+# include "../DataTypes/TaskValues_Data.h"
+# include "../DataTypes/PluginID.h"
+
+
+// These structs are sent to other nodes, so make sure not to change order or offset in struct.
+struct __attribute__((__packed__)) C013_SensorInfoStruct
+{
+ C013_SensorInfoStruct() = default;
+
+ bool setData(const uint8_t *data,
+ size_t size);
+
+ bool prepareForSend(size_t& sizeToSend);
+
+ uint8_t header = 255;
+ uint8_t ID = 3;
+ uint8_t sourceUnit = 0;
+ uint8_t destUnit = 0;
+ taskIndex_t sourceTaskIndex = INVALID_TASK_INDEX;
+ taskIndex_t destTaskIndex = INVALID_TASK_INDEX;
+ pluginID_t deviceNumber = INVALID_PLUGIN_ID;
+ char taskName[26]{};
+ char ValueNames[VARS_PER_TASK][26]{};
+ Sensor_VType sensorType = Sensor_VType::SENSOR_TYPE_NONE;
+
+ // Extra info added on 20240619 (build ID 20871)
+ ShortChecksumType checksum;
+ uint16_t sourceNodeBuild = 0;
+
+ // Optional IDX value to allow receiving remote
+ // feed data on a different task index as is used on the sender node.
+ uint32_t IDX = 0;
+
+ // Settings PCONFIG values
+ int16_t TaskDevicePluginConfig[PLUGIN_CONFIGVAR_MAX]{};
+
+ // Some info from ExtraTaskSettings Sorted so the most likely member to be 0 is at the end.
+ uint8_t ExtraTaskSettings_version = 0;
+ uint8_t TaskDeviceValueDecimals[VARS_PER_TASK]{};
+ uint32_t VariousBits[VARS_PER_TASK]{};
+ float TaskDeviceErrorValue[VARS_PER_TASK]{};
+ float TaskDeviceMinValue[VARS_PER_TASK]{};
+ float TaskDeviceMaxValue[VARS_PER_TASK]{};
+
+ // Put these as last as they are most likely to be empty
+ // FIXME TD-er: Sending formula over is not working well on the receiving end.
+// char TaskDeviceFormula[VARS_PER_TASK][NAME_FORMULA_LENGTH_MAX + 1]{};
+};
+
+
+#endif // ifdef USES_C013
+
+#endif // ifndef DATASTRUCTS_C013_P2P_SENSORINFOSTRUCTS_H
diff --git a/src/src/DataStructs/C013_p2p_dataStructs.cpp b/src/src/DataStructs/C013_p2p_dataStructs.cpp
deleted file mode 100644
index 6587ff001b..0000000000
--- a/src/src/DataStructs/C013_p2p_dataStructs.cpp
+++ /dev/null
@@ -1,44 +0,0 @@
-#include "../DataStructs/C013_p2p_dataStructs.h"
-
-#ifdef USES_C013
-
-# include "../Globals/Plugins.h"
-
-
-
-bool C013_SensorInfoStruct::isValid() const
-{
- if ((header != 255) || (ID != 3)) { return false; }
-
- return validTaskIndex(sourceTaskIndex) &&
- validTaskIndex(destTaskIndex) &&
- validPluginID(deviceNumber);
-}
-
-bool C013_SensorDataStruct::isValid() const
-{
- if ((header != 255) || (ID != 5)) { return false; }
-
- return validTaskIndex(sourceTaskIndex) &&
- validTaskIndex(destTaskIndex);
-}
-
-bool C013_SensorDataStruct::matchesPluginID(pluginID_t pluginID) const
-{
- if (deviceNumber.value == 255 || !validPluginID(deviceNumber) || !validPluginID(pluginID)) {
- // Was never set, so probably received data from older node.
- return true;
- }
- return pluginID == deviceNumber;
-}
-
-bool C013_SensorDataStruct::matchesSensorType(Sensor_VType sensor_type) const
-{
- if (deviceNumber.value == 255 || sensorType == Sensor_VType::SENSOR_TYPE_NONE) {
- // Was never set, so probably received data from older node.
- return true;
- }
- return sensorType == sensor_type;
-}
-
-#endif // ifdef USES_C013
diff --git a/src/src/DataStructs/C013_p2p_dataStructs.h b/src/src/DataStructs/C013_p2p_dataStructs.h
deleted file mode 100644
index fc316f93be..0000000000
--- a/src/src/DataStructs/C013_p2p_dataStructs.h
+++ /dev/null
@@ -1,64 +0,0 @@
-#ifndef DATASTRUCTS_C013_P2P_DATASTRUCTS_H
-#define DATASTRUCTS_C013_P2P_DATASTRUCTS_H
-
-#include "../../ESPEasy_common.h"
-
-#ifdef USES_C013
-
-
-# include "../CustomBuild/ESPEasyLimits.h"
-# include "../DataStructs/DeviceStruct.h"
-# include "../DataTypes/TaskIndex.h"
-# include "../DataTypes/TaskValues_Data.h"
-# include "../DataTypes/PluginID.h"
-
-// These structs are sent to other nodes, so make sure not to change order or offset in struct.
-
-struct __attribute__((__packed__)) C013_SensorInfoStruct
-{
- C013_SensorInfoStruct() = default;
-
- bool isValid() const;
-
- uint8_t header = 255;
- uint8_t ID = 3;
- uint8_t sourceUnit = 0;
- uint8_t destUnit = 0;
- taskIndex_t sourceTaskIndex = INVALID_TASK_INDEX;
- taskIndex_t destTaskIndex = INVALID_TASK_INDEX;
- pluginID_t deviceNumber = INVALID_PLUGIN_ID;
- char taskName[26]{};
- char ValueNames[VARS_PER_TASK][26]{};
- Sensor_VType sensorType = Sensor_VType::SENSOR_TYPE_NONE;
-};
-
-struct C013_SensorDataStruct
-{
- C013_SensorDataStruct() = default;
-
- bool isValid() const;
-
- bool matchesPluginID(pluginID_t pluginID) const;
-
- bool matchesSensorType(Sensor_VType sensor_type) const;
-
- uint8_t header = 255;
- uint8_t ID = 5;
- uint8_t sourceUnit = 0;
- uint8_t destUnit = 0;
- taskIndex_t sourceTaskIndex = INVALID_TASK_INDEX;
- taskIndex_t destTaskIndex = INVALID_TASK_INDEX;
-
- // deviceNumber and sensorType were not present before build 2023-05-05. (build NR 20460)
- // See: https://github.com/letscontrolit/ESPEasy/commit/cf791527eeaf31ca98b07c45c1b64e2561a7b041#diff-86b42dd78398b103e272503f05f55ee0870ae5fb907d713c2505d63279bb0321
- // Thus should not be checked
- pluginID_t deviceNumber = INVALID_PLUGIN_ID;
- Sensor_VType sensorType = Sensor_VType::SENSOR_TYPE_NONE;
- TaskValues_Data_t values{};
-};
-
-constexpr unsigned int size = sizeof(C013_SensorDataStruct);
-
-#endif // ifdef USES_C013
-
-#endif // DATASTRUCTS_C013_P2P_DATASTRUCTS_H
diff --git a/src/src/DataStructs/ChecksumType.cpp b/src/src/DataStructs/ChecksumType.cpp
index 7c998362f8..9c894fb9fb 100644
--- a/src/src/DataStructs/ChecksumType.cpp
+++ b/src/src/DataStructs/ChecksumType.cpp
@@ -1,107 +1,107 @@
-#include "../DataStructs/ChecksumType.h"
-
-#include "../Helpers/StringConverter.h"
-
-#include
-
-ChecksumType::ChecksumType(const ChecksumType& rhs)
-{
- memcpy(_checksum, rhs._checksum, 16);
-}
-
-ChecksumType::ChecksumType(uint8_t checksum[16])
-{
- memcpy(_checksum, checksum, 16);
-}
-
-ChecksumType::ChecksumType(const uint8_t *data,
- size_t data_length)
-{
- computeChecksum(_checksum, data, data_length, data_length, true);
-}
-
-ChecksumType::ChecksumType(const uint8_t *data,
- size_t data_length,
- size_t len_upto_md5)
-{
- computeChecksum(_checksum, data, data_length, len_upto_md5, true);
-}
-
-ChecksumType::ChecksumType(const String strings[], size_t nrStrings)
-{
- MD5Builder md5;
-
- md5.begin();
-
- for (size_t i = 0; i < nrStrings; ++i) {
- md5.add(strings[i].c_str());
- }
- md5.calculate();
- md5.getBytes(_checksum);
-}
-
-bool ChecksumType::computeChecksum(
- uint8_t checksum[16],
- const uint8_t *data,
- size_t data_length,
- size_t len_upto_md5,
- bool updateChecksum)
-{
- if (len_upto_md5 > data_length) { len_upto_md5 = data_length; }
- MD5Builder md5;
-
- md5.begin();
-
- if (len_upto_md5 > 0) {
- // MD5Builder::add has non-const argument
- md5.add(const_cast(data), len_upto_md5);
- }
-
- if ((len_upto_md5 + 16) < data_length) {
- data += len_upto_md5 + 16;
- const int len_after_md5 = data_length - 16 - len_upto_md5;
-
- if (len_after_md5 > 0) {
- // MD5Builder::add has non-const argument
- md5.add(const_cast(data), len_after_md5);
- }
- }
- md5.calculate();
- uint8_t tmp_md5[16] = { 0 };
-
- md5.getBytes(tmp_md5);
-
- if (memcmp(tmp_md5, checksum, 16) != 0) {
- // Data has changed, copy computed checksum
- if (updateChecksum) {
- memcpy(checksum, tmp_md5, 16);
- }
- return false;
- }
- return true;
-}
-
-void ChecksumType::getChecksum(uint8_t checksum[16]) const {
- memcpy(checksum, _checksum, 16);
-}
-
-void ChecksumType::setChecksum(const uint8_t checksum[16]) {
- memcpy(_checksum, checksum, 16);
-}
-
-bool ChecksumType::matchChecksum(const uint8_t checksum[16]) const {
- return memcmp(_checksum, checksum, 16) == 0;
-}
-
-bool ChecksumType::operator==(const ChecksumType& rhs) const {
- return memcmp(_checksum, rhs._checksum, 16) == 0;
-}
-
-ChecksumType& ChecksumType::operator=(const ChecksumType& rhs) {
- memcpy(_checksum, rhs._checksum, 16);
- return *this;
-}
-
-String ChecksumType::toString() const {
- return formatToHex_array(_checksum, 16);
+#include "../DataStructs/ChecksumType.h"
+
+#include "../Helpers/StringConverter.h"
+
+#include
+
+ChecksumType::ChecksumType(const ChecksumType& rhs)
+{
+ memcpy(_checksum, rhs._checksum, 16);
+}
+
+ChecksumType::ChecksumType(uint8_t checksum[16])
+{
+ memcpy(_checksum, checksum, 16);
+}
+
+ChecksumType::ChecksumType(const uint8_t *data,
+ size_t data_length)
+{
+ computeChecksum(_checksum, data, data_length, data_length, true);
+}
+
+ChecksumType::ChecksumType(const uint8_t *data,
+ size_t data_length,
+ size_t len_upto_md5)
+{
+ computeChecksum(_checksum, data, data_length, len_upto_md5, true);
+}
+
+ChecksumType::ChecksumType(const String strings[], size_t nrStrings)
+{
+ MD5Builder md5;
+
+ md5.begin();
+
+ for (size_t i = 0; i < nrStrings; ++i) {
+ md5.add(strings[i].c_str());
+ }
+ md5.calculate();
+ md5.getBytes(_checksum);
+}
+
+bool ChecksumType::computeChecksum(
+ uint8_t checksum[16],
+ const uint8_t *data,
+ size_t data_length,
+ size_t len_upto_md5,
+ bool updateChecksum)
+{
+ if (len_upto_md5 > data_length) { len_upto_md5 = data_length; }
+ MD5Builder md5;
+
+ md5.begin();
+
+ if (len_upto_md5 > 0) {
+ // MD5Builder::add has non-const argument
+ md5.add(const_cast(data), len_upto_md5);
+ }
+
+ if ((len_upto_md5 + 16) < data_length) {
+ data += len_upto_md5 + 16;
+ const int len_after_md5 = data_length - 16 - len_upto_md5;
+
+ if (len_after_md5 > 0) {
+ // MD5Builder::add has non-const argument
+ md5.add(const_cast(data), len_after_md5);
+ }
+ }
+ md5.calculate();
+ uint8_t tmp_md5[16] = { 0 };
+
+ md5.getBytes(tmp_md5);
+
+ if (memcmp(tmp_md5, checksum, 16) != 0) {
+ // Data has changed, copy computed checksum
+ if (updateChecksum) {
+ memcpy(checksum, tmp_md5, 16);
+ }
+ return false;
+ }
+ return true;
+}
+
+void ChecksumType::getChecksum(uint8_t checksum[16]) const {
+ memcpy(checksum, _checksum, 16);
+}
+
+void ChecksumType::setChecksum(const uint8_t checksum[16]) {
+ memcpy(_checksum, checksum, 16);
+}
+
+bool ChecksumType::matchChecksum(const uint8_t checksum[16]) const {
+ return memcmp(_checksum, checksum, 16) == 0;
+}
+
+bool ChecksumType::operator==(const ChecksumType& rhs) const {
+ return memcmp(_checksum, rhs._checksum, 16) == 0;
+}
+
+ChecksumType& ChecksumType::operator=(const ChecksumType& rhs) {
+ memcpy(_checksum, rhs._checksum, 16);
+ return *this;
+}
+
+String ChecksumType::toString() const {
+ return formatToHex_array(_checksum, 16);
}
\ No newline at end of file
diff --git a/src/src/DataStructs/ControllerSettingsStruct.h b/src/src/DataStructs/ControllerSettingsStruct.h
index d9a91c7455..3a42135399 100644
--- a/src/src/DataStructs/ControllerSettingsStruct.h
+++ b/src/src/DataStructs/ControllerSettingsStruct.h
@@ -157,6 +157,7 @@ struct ControllerSettingsStruct
bool UseDNS;
uint8_t IP[4];
+ uint8_t UNUSED_1[3];
unsigned int Port;
char HostName[65];
char Publish[129];
@@ -164,13 +165,16 @@ struct ControllerSettingsStruct
char MQTTLwtTopic[129];
char LWTMessageConnect[129];
char LWTMessageDisconnect[129];
+ uint8_t UNUSED_2[2];
unsigned int MinimalTimeBetweenMessages;
unsigned int MaxQueueDepth;
unsigned int MaxRetry;
bool DeleteOldest; // Action to perform when buffer full, delete oldest, or ignore newest.
+ uint8_t UNUSED_3[3];
unsigned int ClientTimeout;
bool MustCheckReply; // When set to false, a sent message is considered always successful.
taskIndex_t SampleSetInitiator; // The first task to start a sample set.
+ uint8_t UNUSED_4[2];
struct {
uint32_t unused_00 : 1; // Bit 00
diff --git a/src/src/DataStructs/ESPEasy_EventStruct.cpp b/src/src/DataStructs/ESPEasy_EventStruct.cpp
index 4e36d79f32..9d1cea0035 100644
--- a/src/src/DataStructs/ESPEasy_EventStruct.cpp
+++ b/src/src/DataStructs/ESPEasy_EventStruct.cpp
@@ -1,50 +1,75 @@
-#include "../DataStructs/ESPEasy_EventStruct.h"
-
-#include "../../ESPEasy_common.h"
-
-#include "../CustomBuild/ESPEasyLimits.h"
-#include "../DataTypes/EventValueSource.h"
-#include "../Globals/Plugins.h"
-#include "../Globals/CPlugins.h"
-#include "../Globals/NPlugins.h"
-
-#include "../../_Plugin_Helper.h"
-
-EventStruct::EventStruct(taskIndex_t taskIndex) :
- TaskIndex(taskIndex), BaseVarIndex(taskIndex * VARS_PER_TASK)
-{
- if (taskIndex >= INVALID_TASK_INDEX) {
- BaseVarIndex = 0;
- }
-}
-
-void EventStruct::deep_copy(const struct EventStruct& other) {
- this->operator=(other);
-}
-
-void EventStruct::deep_copy(const struct EventStruct *other) {
- if (other != nullptr) {
- deep_copy(*other);
- }
-}
-
-void EventStruct::setTaskIndex(taskIndex_t taskIndex) {
- TaskIndex = taskIndex;
-
- if (TaskIndex < INVALID_TASK_INDEX) {
- BaseVarIndex = taskIndex * VARS_PER_TASK;
- }
- sensorType = Sensor_VType::SENSOR_TYPE_NOT_SET;
-}
-
-void EventStruct::clear() {
- *this = EventStruct();
-}
-
-Sensor_VType EventStruct::getSensorType() {
- const int tmp_idx = idx;
-
- checkDeviceVTypeForTask(this);
- idx = tmp_idx;
- return sensorType;
-}
+#include "../DataStructs/ESPEasy_EventStruct.h"
+
+#include "../../ESPEasy_common.h"
+
+#include "../CustomBuild/ESPEasyLimits.h"
+#include "../DataTypes/EventValueSource.h"
+#include "../Globals/Plugins.h"
+#include "../Globals/CPlugins.h"
+#include "../Globals/NPlugins.h"
+
+#include "../../_Plugin_Helper.h"
+
+EventStruct::EventStruct(taskIndex_t taskIndex) :
+ TaskIndex(taskIndex), BaseVarIndex(taskIndex * VARS_PER_TASK)
+{
+ if (taskIndex >= INVALID_TASK_INDEX) {
+ BaseVarIndex = 0;
+ }
+}
+
+void EventStruct::deep_copy(const struct EventStruct& other) {
+ this->operator=(other);
+}
+
+void EventStruct::deep_copy(const struct EventStruct *other) {
+ if (other != nullptr) {
+ deep_copy(*other);
+ }
+}
+
+void EventStruct::setTaskIndex(taskIndex_t taskIndex) {
+ TaskIndex = taskIndex;
+
+ if (TaskIndex < INVALID_TASK_INDEX) {
+ BaseVarIndex = taskIndex * VARS_PER_TASK;
+ }
+ sensorType = Sensor_VType::SENSOR_TYPE_NOT_SET;
+}
+
+void EventStruct::clear() {
+ *this = EventStruct();
+}
+
+Sensor_VType EventStruct::getSensorType() {
+ const int tmp_idx = idx;
+
+ checkDeviceVTypeForTask(this);
+ idx = tmp_idx;
+ return sensorType;
+}
+
+int64_t EventStruct::getTimestamp_as_systemMicros() const
+{
+ if (timestamp_sec == 0)
+ return getMicros64();
+
+ // FIXME TD-er: What to do when system time has not been set?
+ int64_t res = node_time.Unixtime_to_systemMicros(timestamp_sec, timestamp_frac);
+ if (res < 0) {
+ // Unix time was from before we booted
+ // FIXME TD-er: What to do now?
+ return getMicros64();
+ }
+ return res;
+}
+
+void EventStruct::setUnixTimeTimestamp()
+{
+ timestamp_sec = node_time.getUnixTime(timestamp_frac);
+}
+
+void EventStruct::setLocalTimeTimestamp()
+{
+ timestamp_sec = node_time.getLocalUnixTime(timestamp_frac);
+}
\ No newline at end of file
diff --git a/src/src/DataStructs/ESPEasy_EventStruct.h b/src/src/DataStructs/ESPEasy_EventStruct.h
index 6197d007d6..97f36068ce 100644
--- a/src/src/DataStructs/ESPEasy_EventStruct.h
+++ b/src/src/DataStructs/ESPEasy_EventStruct.h
@@ -1,74 +1,81 @@
-#ifndef DATASTRUCTS_ESPEASY_EVENTSTRUCT_H
-#define DATASTRUCTS_ESPEASY_EVENTSTRUCT_H
-
-#include "../../ESPEasy_common.h"
-
-#include "../DataTypes/ControllerIndex.h"
-#include "../DataTypes/EventValueSource.h"
-#include "../DataTypes/TaskIndex.h"
-#include "../DataTypes/NotifierIndex.h"
-#include "../DataStructs/DeviceStruct.h"
-
-
-/*********************************************************************************************\
-* EventStruct
-* This should not be copied, only moved.
-* When copy is really needed, use deep_copy
-\*********************************************************************************************/
-struct EventStruct
-{
- EventStruct() = default;
- // Delete the copy constructor
- EventStruct(const struct EventStruct& event) = delete;
-private:
- // Hide the copy assignment operator by making it private
- EventStruct& operator=(const EventStruct&) = default;
-
-public:
- EventStruct(struct EventStruct&& event) = default;
- EventStruct& operator=(struct EventStruct&& other) = default;
-
- explicit EventStruct(taskIndex_t taskIndex);
-
- // Explicit deep_copy function to make sure this object is not accidentally copied using the copy-constructor
- // Copy constructor and assignment operator should not be used.
- void deep_copy(const struct EventStruct& other);
- void deep_copy(const struct EventStruct* other);
- // explicit EventStruct(const struct EventStruct& event);
- // EventStruct& operator=(const struct EventStruct& other);
-
-
- void setTaskIndex(taskIndex_t taskIndex);
-
- void clear();
-
- // Check (and update) sensorType if not set, plus return (corrected) sensorType
- Sensor_VType getSensorType();
-
- String String1;
- String String2;
- String String3;
- String String4;
- String String5;
- unsigned long timestamp = 0u;
- uint8_t *Data = nullptr;
- int idx = 0;
- int Par1 = 0;
- int Par2 = 0;
- int Par3 = 0;
- int Par4 = 0;
- int Par5 = 0;
-
- // The origin of the values in the event. See EventValueSource.h
- EventValueSource::Enum Source = EventValueSource::Enum::VALUE_SOURCE_NOT_SET;
- taskIndex_t TaskIndex = INVALID_TASK_INDEX; // index position in TaskSettings array, 0-11
- controllerIndex_t ControllerIndex = INVALID_CONTROLLER_INDEX; // index position in Settings.Controller, 0-3
-#if FEATURE_NOTIFIER
- notifierIndex_t NotificationIndex = INVALID_NOTIFIER_INDEX; // index position in Settings.Notification, 0-3
-#endif
- uint8_t BaseVarIndex = 0;
- Sensor_VType sensorType = Sensor_VType::SENSOR_TYPE_NOT_SET;
- uint8_t OriginTaskIndex = 0;
-};
-
-#endif // DATASTRUCTS_ESPEASY_EVENTSTRUCT_H
+#ifndef DATASTRUCTS_ESPEASY_EVENTSTRUCT_H
+#define DATASTRUCTS_ESPEASY_EVENTSTRUCT_H
+
+#include "../../ESPEasy_common.h"
+
+#include "../DataTypes/ControllerIndex.h"
+#include "../DataTypes/EventValueSource.h"
+#include "../DataTypes/TaskIndex.h"
+#include "../DataTypes/NotifierIndex.h"
+#include "../DataStructs/DeviceStruct.h"
+
+
+/*********************************************************************************************\
+* EventStruct
+* This should not be copied, only moved.
+* When copy is really needed, use deep_copy
+\*********************************************************************************************/
+struct EventStruct
+{
+ EventStruct() = default;
+ // Delete the copy constructor
+ EventStruct(const struct EventStruct& event) = delete;
+private:
+ // Hide the copy assignment operator by making it private
+ EventStruct& operator=(const EventStruct&) = default;
+
+public:
+ EventStruct(struct EventStruct&& event) = default;
+ EventStruct& operator=(struct EventStruct&& other) = default;
+
+ explicit EventStruct(taskIndex_t taskIndex);
+
+ // Explicit deep_copy function to make sure this object is not accidentally copied using the copy-constructor
+ // Copy constructor and assignment operator should not be used.
+ void deep_copy(const struct EventStruct& other);
+ void deep_copy(const struct EventStruct* other);
+ // explicit EventStruct(const struct EventStruct& event);
+ // EventStruct& operator=(const struct EventStruct& other);
+
+
+ void setTaskIndex(taskIndex_t taskIndex);
+
+ void clear();
+
+ // Check (and update) sensorType if not set, plus return (corrected) sensorType
+ Sensor_VType getSensorType();
+
+ int64_t getTimestamp_as_systemMicros() const;
+ void setUnixTimeTimestamp();
+ void setLocalTimeTimestamp();
+
+ String String1;
+ String String2;
+ String String3;
+ String String4;
+ String String5;
+
+
+ uint32_t timestamp_sec = 0u;
+ uint32_t timestamp_frac = 0u;
+ uint8_t *Data = nullptr;
+ int idx = 0;
+ int Par1 = 0;
+ int Par2 = 0;
+ int Par3 = 0;
+ int Par4 = 0;
+ int Par5 = 0;
+
+ // The origin of the values in the event. See EventValueSource.h
+ EventValueSource::Enum Source = EventValueSource::Enum::VALUE_SOURCE_NOT_SET;
+ taskIndex_t TaskIndex = INVALID_TASK_INDEX; // index position in TaskSettings array, 0-11
+ controllerIndex_t ControllerIndex = INVALID_CONTROLLER_INDEX; // index position in Settings.Controller, 0-3
+#if FEATURE_NOTIFIER
+ notifierIndex_t NotificationIndex = INVALID_NOTIFIER_INDEX; // index position in Settings.Notification, 0-3
+#endif
+ uint8_t BaseVarIndex = 0;
+ Sensor_VType sensorType = Sensor_VType::SENSOR_TYPE_NOT_SET;
+ uint8_t OriginTaskIndex = 0;
+};
+
+#endif // DATASTRUCTS_ESPEASY_EVENTSTRUCT_H
diff --git a/src/src/DataStructs/ExtraTaskSettingsStruct.cpp b/src/src/DataStructs/ExtraTaskSettingsStruct.cpp
index 37d82f4acc..3c42d964d1 100644
--- a/src/src/DataStructs/ExtraTaskSettingsStruct.cpp
+++ b/src/src/DataStructs/ExtraTaskSettingsStruct.cpp
@@ -20,6 +20,7 @@ void ExtraTaskSettingsStruct::clear() {
version = EXTRA_TASK_SETTINGS_VERSION;
for (int i = 0; i < VARS_PER_TASK; ++i) {
TaskDeviceValueDecimals[i] = 2;
+ TaskDeviceErrorValue[i] = NAN;
}
}
@@ -43,7 +44,7 @@ void ExtraTaskSettingsStruct::validate() {
// Need to initialize the newly added fields
for (uint8_t i = 0; i < VARS_PER_TASK; ++i) {
setIgnoreRangeCheck(i);
- TaskDeviceErrorValue[i] = 0.0f;
+ TaskDeviceErrorValue[i] = NAN;
VariousBits[i] = 0u;
}
}
@@ -74,7 +75,7 @@ void ExtraTaskSettingsStruct::clearUnusedValueNames(uint8_t usedVars) {
ZERO_FILL(TaskDeviceValueNames[i]);
TaskDeviceValueDecimals[i] = 2;
setIgnoreRangeCheck(i);
- TaskDeviceErrorValue[i] = 0.0f;
+ TaskDeviceErrorValue[i] = NAN;
VariousBits[i] = 0;
}
}
diff --git a/src/src/DataStructs/FactoryDefault_WiFi_NVS.cpp b/src/src/DataStructs/FactoryDefault_WiFi_NVS.cpp
index b97559841d..8cc486e544 100644
--- a/src/src/DataStructs/FactoryDefault_WiFi_NVS.cpp
+++ b/src/src/DataStructs/FactoryDefault_WiFi_NVS.cpp
@@ -1,109 +1,111 @@
-#include "../DataStructs/FactoryDefault_WiFi_NVS.h"
-
-#ifdef ESP32
-
-# include "../Globals/Settings.h"
-# include "../Globals/SecuritySettings.h"
-# include "../Helpers/StringConverter.h"
-
-// Max. 15 char keys for ESPEasy Factory Default marked keys
-# define FACTORY_DEFAULT_NVS_SSID1_KEY "WIFI_SSID1"
-# define FACTORY_DEFAULT_NVS_WPA_PASS1_KEY "WIFI_PASS1"
-# define FACTORY_DEFAULT_NVS_SSID2_KEY "WIFI_SSID2"
-# define FACTORY_DEFAULT_NVS_WPA_PASS2_KEY "WIFI_PASS2"
-# define FACTORY_DEFAULT_NVS_AP_PASS_KEY "WIFI_AP_PASS"
-# define FACTORY_DEFAULT_NVS_WIFI_FLAGS_KEY "WIFI_Flags"
-
-
-void FactoryDefault_WiFi_NVS::fromSettings() {
- bits.IncludeHiddenSSID = Settings.IncludeHiddenSSID();
- bits.ApDontForceSetup = Settings.ApDontForceSetup();
- bits.DoNotStartAP = Settings.DoNotStartAP();
- bits.ForceWiFi_bg_mode = Settings.ForceWiFi_bg_mode();
- bits.WiFiRestart_connection_lost = Settings.WiFiRestart_connection_lost();
- bits.WifiNoneSleep = Settings.WifiNoneSleep();
- bits.gratuitousARP = Settings.gratuitousARP();
- bits.UseMaxTXpowerForSending = Settings.UseMaxTXpowerForSending();
- bits.UseLastWiFiFromRTC = Settings.UseLastWiFiFromRTC();
- 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 {
- Settings.IncludeHiddenSSID(bits.IncludeHiddenSSID);
- Settings.ApDontForceSetup(bits.ApDontForceSetup);
- Settings.DoNotStartAP(bits.DoNotStartAP);
- Settings.ForceWiFi_bg_mode(bits.ForceWiFi_bg_mode);
- Settings.WiFiRestart_connection_lost(bits.WiFiRestart_connection_lost);
- Settings.WifiNoneSleep(bits.WifiNoneSleep);
- Settings.gratuitousARP(bits.gratuitousARP);
- Settings.UseMaxTXpowerForSending(bits.UseMaxTXpowerForSending);
- Settings.UseLastWiFiFromRTC(bits.UseLastWiFiFromRTC);
- 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 {
- FactoryDefault_WiFi_NVS_securityPrefs(const __FlashStringHelper *pref,
- char *dest,
- size_t size)
- : _pref(pref), _dest(dest), _size(size) {}
-
- const __FlashStringHelper *_pref;
- char *_dest;
- size_t _size;
-};
-
-const FactoryDefault_WiFi_NVS_securityPrefs _WiFi_NVS_securityPrefs_values[] = {
- { F(FACTORY_DEFAULT_NVS_SSID1_KEY), SecuritySettings.WifiSSID, sizeof(SecuritySettings.WifiSSID) },
- { F(FACTORY_DEFAULT_NVS_WPA_PASS1_KEY), SecuritySettings.WifiKey, sizeof(SecuritySettings.WifiKey) },
- { F(FACTORY_DEFAULT_NVS_SSID2_KEY), SecuritySettings.WifiSSID2, sizeof(SecuritySettings.WifiSSID2) },
- { F(FACTORY_DEFAULT_NVS_WPA_PASS2_KEY), SecuritySettings.WifiKey2, sizeof(SecuritySettings.WifiKey2) },
- { F(FACTORY_DEFAULT_NVS_AP_PASS_KEY), SecuritySettings.WifiAPKey, sizeof(SecuritySettings.WifiAPKey) }
-};
-
-
-bool FactoryDefault_WiFi_NVS::applyToSettings_from_NVS(ESPEasy_NVS_Helper& preferences) {
- String tmp;
- constexpr unsigned nr__WiFi_NVS_securityPrefs_values = NR_ELEMENTS(_WiFi_NVS_securityPrefs_values);
-
- for (unsigned i = 0; i < nr__WiFi_NVS_securityPrefs_values; ++i) {
- if (preferences.getPreference(_WiFi_NVS_securityPrefs_values[i]._pref, tmp)) {
- safe_strncpy(_WiFi_NVS_securityPrefs_values[i]._dest, tmp, _WiFi_NVS_securityPrefs_values[i]._size);
- }
- }
-
- if (!preferences.getPreference(F(FACTORY_DEFAULT_NVS_WIFI_FLAGS_KEY), data)) {
- return false;
- }
-
- applyToSettings();
- return true;
-}
-
-void FactoryDefault_WiFi_NVS::fromSettings_to_NVS(ESPEasy_NVS_Helper& preferences) {
- fromSettings();
- preferences.setPreference(F(FACTORY_DEFAULT_NVS_WIFI_FLAGS_KEY), data);
-
- // Store WiFi credentials
- constexpr unsigned nr__WiFi_NVS_securityPrefs_values = NR_ELEMENTS(_WiFi_NVS_securityPrefs_values);
-
- for (unsigned i = 0; i < nr__WiFi_NVS_securityPrefs_values; ++i) {
- preferences.setPreference(_WiFi_NVS_securityPrefs_values[i]._pref, String(_WiFi_NVS_securityPrefs_values[i]._dest));
- }
-}
-
-void FactoryDefault_WiFi_NVS::clear_from_NVS(ESPEasy_NVS_Helper& preferences) {
- constexpr unsigned nr__WiFi_NVS_securityPrefs_values = NR_ELEMENTS(_WiFi_NVS_securityPrefs_values);
-
- for (unsigned i = 0; i < nr__WiFi_NVS_securityPrefs_values; ++i) {
- preferences.remove(_WiFi_NVS_securityPrefs_values[i]._pref);
- }
- preferences.remove(F(FACTORY_DEFAULT_NVS_WIFI_FLAGS_KEY));
-}
-
-#endif // ifdef ESP32
+#include "../DataStructs/FactoryDefault_WiFi_NVS.h"
+
+#ifdef ESP32
+
+# include "../Globals/Settings.h"
+# include "../Globals/SecuritySettings.h"
+# include "../Helpers/StringConverter.h"
+
+// Max. 15 char keys for ESPEasy Factory Default marked keys
+# define FACTORY_DEFAULT_NVS_SSID1_KEY "WIFI_SSID1"
+# define FACTORY_DEFAULT_NVS_WPA_PASS1_KEY "WIFI_PASS1"
+# define FACTORY_DEFAULT_NVS_SSID2_KEY "WIFI_SSID2"
+# define FACTORY_DEFAULT_NVS_WPA_PASS2_KEY "WIFI_PASS2"
+# define FACTORY_DEFAULT_NVS_AP_PASS_KEY "WIFI_AP_PASS"
+# define FACTORY_DEFAULT_NVS_WIFI_FLAGS_KEY "WIFI_Flags"
+
+
+void FactoryDefault_WiFi_NVS::fromSettings() {
+ bits.IncludeHiddenSSID = Settings.IncludeHiddenSSID();
+ bits.ApDontForceSetup = Settings.ApDontForceSetup();
+ bits.DoNotStartAP = Settings.DoNotStartAP();
+ bits.ForceWiFi_bg_mode = Settings.ForceWiFi_bg_mode();
+ bits.WiFiRestart_connection_lost = Settings.WiFiRestart_connection_lost();
+ bits.WifiNoneSleep = Settings.WifiNoneSleep();
+ bits.gratuitousARP = Settings.gratuitousARP();
+ bits.UseMaxTXpowerForSending = Settings.UseMaxTXpowerForSending();
+ bits.UseLastWiFiFromRTC = Settings.UseLastWiFiFromRTC();
+ bits.WaitWiFiConnect = Settings.WaitWiFiConnect();
+ bits.SDK_WiFi_autoreconnect = Settings.SDK_WiFi_autoreconnect();
+ bits.HiddenSSID_SlowConnectPerBSSID = Settings.HiddenSSID_SlowConnectPerBSSID();
+ bits.EnableIPv6 = Settings.EnableIPv6();
+ bits.PassiveWiFiScan = Settings.PassiveWiFiScan();
+}
+
+void FactoryDefault_WiFi_NVS::applyToSettings() const {
+ Settings.IncludeHiddenSSID(bits.IncludeHiddenSSID);
+ Settings.ApDontForceSetup(bits.ApDontForceSetup);
+ Settings.DoNotStartAP(bits.DoNotStartAP);
+ Settings.ForceWiFi_bg_mode(bits.ForceWiFi_bg_mode);
+ Settings.WiFiRestart_connection_lost(bits.WiFiRestart_connection_lost);
+ Settings.WifiNoneSleep(bits.WifiNoneSleep);
+ Settings.gratuitousARP(bits.gratuitousARP);
+ Settings.UseMaxTXpowerForSending(bits.UseMaxTXpowerForSending);
+ Settings.UseLastWiFiFromRTC(bits.UseLastWiFiFromRTC);
+ Settings.WaitWiFiConnect(bits.WaitWiFiConnect);
+ Settings.SDK_WiFi_autoreconnect(bits.SDK_WiFi_autoreconnect);
+ Settings.HiddenSSID_SlowConnectPerBSSID(bits.HiddenSSID_SlowConnectPerBSSID);
+ Settings.EnableIPv6(bits.EnableIPv6);
+ Settings.PassiveWiFiScan(bits.PassiveWiFiScan);
+}
+
+struct FactoryDefault_WiFi_NVS_securityPrefs {
+ FactoryDefault_WiFi_NVS_securityPrefs(const __FlashStringHelper *pref,
+ char *dest,
+ size_t size)
+ : _pref(pref), _dest(dest), _size(size) {}
+
+ const __FlashStringHelper *_pref;
+ char *_dest;
+ size_t _size;
+};
+
+const FactoryDefault_WiFi_NVS_securityPrefs _WiFi_NVS_securityPrefs_values[] = {
+ { F(FACTORY_DEFAULT_NVS_SSID1_KEY), SecuritySettings.WifiSSID, sizeof(SecuritySettings.WifiSSID) },
+ { F(FACTORY_DEFAULT_NVS_WPA_PASS1_KEY), SecuritySettings.WifiKey, sizeof(SecuritySettings.WifiKey) },
+ { F(FACTORY_DEFAULT_NVS_SSID2_KEY), SecuritySettings.WifiSSID2, sizeof(SecuritySettings.WifiSSID2) },
+ { F(FACTORY_DEFAULT_NVS_WPA_PASS2_KEY), SecuritySettings.WifiKey2, sizeof(SecuritySettings.WifiKey2) },
+ { F(FACTORY_DEFAULT_NVS_AP_PASS_KEY), SecuritySettings.WifiAPKey, sizeof(SecuritySettings.WifiAPKey) }
+};
+
+
+bool FactoryDefault_WiFi_NVS::applyToSettings_from_NVS(ESPEasy_NVS_Helper& preferences) {
+ String tmp;
+ constexpr unsigned nr__WiFi_NVS_securityPrefs_values = NR_ELEMENTS(_WiFi_NVS_securityPrefs_values);
+
+ for (unsigned i = 0; i < nr__WiFi_NVS_securityPrefs_values; ++i) {
+ if (preferences.getPreference(_WiFi_NVS_securityPrefs_values[i]._pref, tmp)) {
+ safe_strncpy(_WiFi_NVS_securityPrefs_values[i]._dest, tmp, _WiFi_NVS_securityPrefs_values[i]._size);
+ }
+ }
+
+ if (!preferences.getPreference(F(FACTORY_DEFAULT_NVS_WIFI_FLAGS_KEY), data)) {
+ return false;
+ }
+
+ applyToSettings();
+ return true;
+}
+
+void FactoryDefault_WiFi_NVS::fromSettings_to_NVS(ESPEasy_NVS_Helper& preferences) {
+ fromSettings();
+ preferences.setPreference(F(FACTORY_DEFAULT_NVS_WIFI_FLAGS_KEY), data);
+
+ // Store WiFi credentials
+ constexpr unsigned nr__WiFi_NVS_securityPrefs_values = NR_ELEMENTS(_WiFi_NVS_securityPrefs_values);
+
+ for (unsigned i = 0; i < nr__WiFi_NVS_securityPrefs_values; ++i) {
+ preferences.setPreference(_WiFi_NVS_securityPrefs_values[i]._pref, String(_WiFi_NVS_securityPrefs_values[i]._dest));
+ }
+}
+
+void FactoryDefault_WiFi_NVS::clear_from_NVS(ESPEasy_NVS_Helper& preferences) {
+ constexpr unsigned nr__WiFi_NVS_securityPrefs_values = NR_ELEMENTS(_WiFi_NVS_securityPrefs_values);
+
+ for (unsigned i = 0; i < nr__WiFi_NVS_securityPrefs_values; ++i) {
+ preferences.remove(_WiFi_NVS_securityPrefs_values[i]._pref);
+ }
+ preferences.remove(F(FACTORY_DEFAULT_NVS_WIFI_FLAGS_KEY));
+}
+
+#endif // ifdef ESP32
diff --git a/src/src/DataStructs/FactoryDefault_WiFi_NVS.h b/src/src/DataStructs/FactoryDefault_WiFi_NVS.h
index 59a8d3b92c..e3638b084f 100644
--- a/src/src/DataStructs/FactoryDefault_WiFi_NVS.h
+++ b/src/src/DataStructs/FactoryDefault_WiFi_NVS.h
@@ -1,56 +1,57 @@
-#ifndef DATASTRUCTS_FACTORYDEFAULT_WIFI_NVS_H
-#define DATASTRUCTS_FACTORYDEFAULT_WIFI_NVS_H
-
-
-#include "../../ESPEasy_common.h"
-
-#ifdef ESP32
-
-# include "../Helpers/ESPEasy_NVS_Helper.h"
-
-
-class FactoryDefault_WiFi_NVS {
-private:
-
- void fromSettings();
-
- void applyToSettings() const;
-
-public:
-
- bool applyToSettings_from_NVS(ESPEasy_NVS_Helper& preferences);
-
- void fromSettings_to_NVS(ESPEasy_NVS_Helper& preferences);
-
- void clear_from_NVS(ESPEasy_NVS_Helper& preferences);
-
-private:
-
- union {
- struct {
- uint64_t IncludeHiddenSSID : 1;
- uint64_t ApDontForceSetup : 1;
- uint64_t DoNotStartAP : 1;
- uint64_t ForceWiFi_bg_mode : 1;
- uint64_t WiFiRestart_connection_lost : 1;
- uint64_t WifiNoneSleep : 1;
- uint64_t gratuitousARP : 1;
- uint64_t UseMaxTXpowerForSending : 1;
- uint64_t UseLastWiFiFromRTC : 1;
- uint64_t WaitWiFiConnect : 1;
- uint64_t SDK_WiFi_autoreconnect : 1;
- uint64_t HiddenSSID_SlowConnectPerBSSID : 1;
- uint64_t EnableIPv6 : 1;
-
- uint64_t unused : 51;
- } bits;
-
- uint64_t data{};
- };
-};
-
-
-#endif // ifdef ESP32
-
-
-#endif // ifndef DATASTRUCTS_FACTORYDEFAULT_WIFI_NVS_H
+#ifndef DATASTRUCTS_FACTORYDEFAULT_WIFI_NVS_H
+#define DATASTRUCTS_FACTORYDEFAULT_WIFI_NVS_H
+
+
+#include "../../ESPEasy_common.h"
+
+#ifdef ESP32
+
+# include "../Helpers/ESPEasy_NVS_Helper.h"
+
+
+class FactoryDefault_WiFi_NVS {
+private:
+
+ void fromSettings();
+
+ void applyToSettings() const;
+
+public:
+
+ bool applyToSettings_from_NVS(ESPEasy_NVS_Helper& preferences);
+
+ void fromSettings_to_NVS(ESPEasy_NVS_Helper& preferences);
+
+ void clear_from_NVS(ESPEasy_NVS_Helper& preferences);
+
+private:
+
+ union {
+ struct {
+ uint64_t IncludeHiddenSSID : 1;
+ uint64_t ApDontForceSetup : 1;
+ uint64_t DoNotStartAP : 1;
+ uint64_t ForceWiFi_bg_mode : 1;
+ uint64_t WiFiRestart_connection_lost : 1;
+ uint64_t WifiNoneSleep : 1;
+ uint64_t gratuitousARP : 1;
+ uint64_t UseMaxTXpowerForSending : 1;
+ uint64_t UseLastWiFiFromRTC : 1;
+ uint64_t WaitWiFiConnect : 1;
+ uint64_t SDK_WiFi_autoreconnect : 1;
+ uint64_t HiddenSSID_SlowConnectPerBSSID : 1;
+ uint64_t EnableIPv6 : 1;
+ uint64_t PassiveWiFiScan : 1;
+
+ uint64_t unused : 50;
+ } bits;
+
+ uint64_t data{};
+ };
+};
+
+
+#endif // ifdef ESP32
+
+
+#endif // ifndef DATASTRUCTS_FACTORYDEFAULT_WIFI_NVS_H
diff --git a/src/src/DataStructs/NTP_packet.cpp b/src/src/DataStructs/NTP_packet.cpp
new file mode 100644
index 0000000000..6e943b7419
--- /dev/null
+++ b/src/src/DataStructs/NTP_packet.cpp
@@ -0,0 +1,222 @@
+#include "../DataStructs/NTP_packet.h"
+
+#include "../Helpers/ESPEasy_time_calc.h"
+
+#include "../Helpers/StringConverter.h"
+
+NTP_packet::NTP_packet()
+{
+ // li, vn, and mode:
+ // - li. 2 bits. Leap indicator.
+ // 0 = no warning
+ // 1 = last minute of the day has 61 seconds
+ // 2 = last minute of the day has 59 seconds
+ // 3 = unknown (clock unsynchronized)
+ // - vn. 3 bits. Version number of the protocol. (0b100 = v4)
+ // - mode. 3 bits. Client will pick mode 3 for client.
+ // 0 = reserved
+ // 1 = symmetric active
+ // 2 = symmetric passive
+ // 3 = client
+ // 4 = server
+ // 5 = broadcast
+ // 6 = NTP control message
+ // 7 = reserved for private use
+ data[0] = 0b11100011; // Unsynchronized, V4, client mode
+
+ // Stratum level of the local clock.
+ // 0 = unspecified or invalid
+ // 1 = primary server (e.g., equipped with a GPS receiver)
+ // 2-15 = secondary server (via NTP)
+ // 16 = unsynchronized
+ // 17-255 = reserved
+ data[1] = 0u;
+
+ // Poll: 8-bit signed integer representing the maximum interval between
+ // successive messages, in log2 seconds. Suggested default limits for
+ // minimum and maximum poll intervals are 6 and 10, respectively.
+ data[2] = 6u;
+
+ // Precision: 8-bit signed integer representing the precision of the
+ // system clock, in log2 seconds. For instance, a value of -18
+ // corresponds to a precision of about one microsecond. The precision
+ // can be determined when the service first starts up as the minimum
+ // time of several iterations to read the system clock.
+ data[3] = 0xEC; // -20 -> 2^-20 sec -> microsec precision.
+
+ constexpr int8_t precision = 0xEC;
+
+ // Reference clock identifier. ASCII: "1N14"
+ data[12] = 0x31;
+ data[13] = 0x4E;
+ data[14] = 0x31;
+ data[15] = 0x34;
+}
+
+uint32_t NTP_packet::readWord(uint8_t startIndex) const
+{
+ uint32_t res{};
+
+ res = (uint32_t)data[startIndex] << 24;
+ res |= (uint32_t)data[startIndex + 1] << 16;
+ res |= (uint32_t)data[startIndex + 2] << 8;
+ res |= (uint32_t)data[startIndex + 3];
+ return res;
+}
+
+uint64_t NTP_packet::ntp_timestamp_to_Unix_time(uint8_t startIndex) const {
+ // Apply offset from 1900/01/01 to 1970/01/01
+ constexpr uint64_t offset_since_1900 = 2208988800ULL * 1000000ull;
+
+ const uint32_t Tm_s = readWord(startIndex);
+ const uint32_t Tm_f = readWord(startIndex + 4);
+ uint64_t usec_since_1900 = sec_time_frac_to_Micros(Tm_s, Tm_f);
+
+ if (usec_since_1900 < offset_since_1900) {
+ // Fix overflow which will occur in 2036
+ usec_since_1900 += (4294967296ull * 1000000ull);
+ }
+ return usec_since_1900 - offset_since_1900;
+}
+
+void NTP_packet::writeWord(uint32_t value, uint8_t startIndex)
+{
+ data[startIndex] = (value >> 24) & 0xFF;
+ data[startIndex + 1] = (value >> 16) & 0xFF;
+ data[startIndex + 2] = (value >> 8) & 0xFF;
+ data[startIndex + 3] = (value) & 0xFF;
+}
+
+bool NTP_packet::isUnsynchronized() const
+{
+ return (data[0] /*li_vn_mode*/ & 0b11000000) == 0b11000000;
+}
+
+uint64_t NTP_packet::getReferenceTimestamp_usec() const
+{
+ return ntp_timestamp_to_Unix_time(16);
+}
+
+uint64_t NTP_packet::getOriginTimestamp_usec() const
+{
+ return ntp_timestamp_to_Unix_time(24);
+}
+
+uint64_t NTP_packet::getReceiveTimestamp_usec() const
+{
+ return ntp_timestamp_to_Unix_time(32);
+}
+
+uint64_t NTP_packet::getTransmitTimestamp_usec() const
+{
+ return ntp_timestamp_to_Unix_time(40);
+}
+
+void NTP_packet::setTxTimestamp(uint64_t micros)
+{
+ constexpr uint64_t offset_since_1900 = 2208988800ULL * 1000000ull;
+
+ micros += offset_since_1900;
+ uint32_t tmp_origTm_f{};
+
+ writeWord(micros_to_sec_time_frac(micros, tmp_origTm_f), 40);
+ writeWord(tmp_origTm_f, 44);
+}
+
+bool NTP_packet::compute_usec(
+ uint64_t localTXTimestamp_usec,
+ uint64_t localRxTimestamp_usec,
+ uint64_t& offset_usec,
+ uint64_t& roundtripDelay_usec) const
+{
+ int64_t t1 = getOriginTimestamp_usec();
+
+ if (t1 == 0) {
+ t1 = localTXTimestamp_usec;
+ }
+
+ const int64_t t2 = getReceiveTimestamp_usec();
+ const int64_t t3 = getTransmitTimestamp_usec();
+
+ if ((t3 == 0) || (t3 < t2)) {
+ // No time stamp received
+ return false;
+ }
+ const int64_t t4 = localRxTimestamp_usec;
+
+ offset_usec = (t2 - t1) + (t3 - t4);
+ offset_usec /= 2;
+
+ roundtripDelay_usec = (t4 - t1) - (t3 - t2);
+ return true;
+}
+
+String NTP_packet::getRefID_str(bool& isError) const
+{
+ String refID;
+
+ const uint8_t stratum = data[1];
+
+ isError = false;
+
+ if ((stratum == 0) || (stratum == 1)) {
+ refID = strformat(F("%c%c%c%c"),
+ static_cast(data[12] & 0x7F),
+ static_cast(data[13] & 0x7F),
+ static_cast(data[14] & 0x7F),
+ static_cast(data[15] & 0x7F));
+
+ if (stratum == 0) {
+ if (refID.equals(F("DENY")) ||
+ refID.equals(F("RSTR"))) {
+ // For kiss codes DENY and RSTR, the client MUST
+ // demobilize any associations to that server and
+ // stop sending packets to that server;
+ // DENY = Access denied by remote server.
+ // RSTR = Access denied due to local policy.
+ isError = true;
+ } else if (refID.equals(F("RATE"))) {
+ // For kiss code RATE, the client MUST immediately reduce its
+ // polling interval to that server and continue to reduce it each
+ // time it receives a RATE kiss code.
+ }
+ }
+ } else {
+ const IPAddress addrv4(readWord(12));
+ refID = addrv4.toString();
+ }
+ return refID;
+}
+
+#ifndef BUILD_NO_DEBUG
+String NTP_packet::toDebugString() const
+{
+ const uint8_t li = (data[0] >> 6) & 0x3;
+ const uint8_t ver = (data[0] >> 3) & 0x7;
+ const uint8_t mode = data[0] & 0x7;
+
+ bool isError{};
+
+ return strformat(
+ F(" li: %u ver: %u mode: %u\n"
+ " strat: %u poll: %d prec: %d\n"
+ " del: %u disp: %u refID: '%s'\n"
+ " refTm_s : %u refTm_f : %u\n"
+ " origTm_s: %u origTm_f: %u\n"
+ " rxTm_s : %u rxTm_f : %u\n"
+ " txTm_s : %u txTm_f : %u\n"),
+ li, ver, mode, // li_vn_mode
+ data[1], // stratum,
+ (int8_t)(data[2]), // poll in log2 seconds
+ (int8_t)(data[3]), // precision in log2 seconds
+ readWord(4), readWord(8), // rootDelay, rootDispersion,
+ getRefID_str(isError).c_str(),
+ readWord(16), unix_time_frac_to_micros(readWord(20)), // refTm_s, unix_time_frac_to_micros(refTm_f),
+ readWord(24), unix_time_frac_to_micros(readWord(28)), // origTm_s, unix_time_frac_to_micros(origTm_f),
+ readWord(32), unix_time_frac_to_micros(readWord(36)), // rxTm_s, unix_time_frac_to_micros(rxTm_f),
+ readWord(40), unix_time_frac_to_micros(readWord(44)) // txTm_s, unix_time_frac_to_micros(txTm_f)
+
+ );
+}
+
+#endif // ifndef BUILD_NO_DEBUG
diff --git a/src/src/DataStructs/NTP_packet.h b/src/src/DataStructs/NTP_packet.h
new file mode 100644
index 0000000000..8852772d34
--- /dev/null
+++ b/src/src/DataStructs/NTP_packet.h
@@ -0,0 +1,57 @@
+#ifndef DATASTRUCTS_NTP_PACKET_H
+#define DATASTRUCTS_NTP_PACKET_H
+
+#include
+
+struct __attribute__((__packed__)) NTP_packet
+{
+ NTP_packet();
+ bool isUnsynchronized() const;
+
+ // Reference Timestamp: Time when the system clock was last set or corrected, in NTP timestamp format.
+ // Timestamp is Unixtime in microseconds
+ uint64_t getReferenceTimestamp_usec() const;
+
+ // Origin Timestamp (org): Time at the client when the request departed for the server, in NTP timestamp format.
+ // Timestamp is Unixtime in microseconds
+ uint64_t getOriginTimestamp_usec() const;
+
+ // Receive Timestamp (rec): Time at the server when the request arrived from the client, in NTP timestamp format.
+ // Timestamp is Unixtime in microseconds
+ uint64_t getReceiveTimestamp_usec() const;
+
+ // Transmit Timestamp (xmt): Time at the server when the response left for the client, in NTP timestamp format.
+ // The most important field the client cares about.
+ // Timestamp is Unixtime in microseconds
+ uint64_t getTransmitTimestamp_usec() const;
+
+
+ // Before sending, the TX-timestamp of the local machine must be set.
+ // This will be returned in the reply as "Origin" timestamp
+ void setTxTimestamp(uint64_t micros);
+
+ // The "Offset", the time difference of the two computer clocks
+ // The "Delay", the time that was needed to transfer the packet in the network
+ bool compute_usec(
+ uint64_t localTXTimestamp_usec,
+ uint64_t localRxTimestamp_usec,
+ uint64_t& offset_usec,
+ uint64_t& roundtripDelay_usec) const;
+
+ String getRefID_str(bool& isError) const;
+
+#ifndef BUILD_NO_DEBUG
+ String toDebugString() const;
+#endif // ifndef BUILD_NO_DEBUG
+
+ uint8_t data[48]{};
+
+private:
+
+ uint32_t readWord(uint8_t startIndex) const;
+ void writeWord(uint32_t value,
+ uint8_t startIndex);
+ uint64_t ntp_timestamp_to_Unix_time(uint8_t startIndex) const;
+};
+
+#endif // ifndef DATASTRUCTS_NTP_PACKET_H
diff --git a/src/src/DataStructs/NodesHandler.cpp b/src/src/DataStructs/NodesHandler.cpp
index db3c5e90cc..c75f83744e 100644
--- a/src/src/DataStructs/NodesHandler.cpp
+++ b/src/src/DataStructs/NodesHandler.cpp
@@ -504,7 +504,7 @@ void NodesHandler::updateThisNode() {
}
const NodeStruct * NodesHandler::getThisNode() {
- node_time.now();
+// node_time.now();
updateThisNode();
MAC_address this_mac = NetworkMacAddress();
return getNodeByMac(this_mac.mac);
diff --git a/src/src/DataStructs/PluginStats.cpp b/src/src/DataStructs/PluginStats.cpp
index d21780dd88..cd2ee0adeb 100644
--- a/src/src/DataStructs/PluginStats.cpp
+++ b/src/src/DataStructs/PluginStats.cpp
@@ -3,52 +3,164 @@
#if FEATURE_PLUGIN_STATS
# include "../../_Plugin_Helper.h"
+# include "../Globals/TimeZone.h"
+
# include "../Helpers/ESPEasy_math.h"
+# include "../Helpers/Memory.h"
# include "../WebServer/Chart_JS.h"
+
PluginStats::PluginStats(uint8_t nrDecimals, float errorValue) :
_errorValue(errorValue),
- _nrDecimals(nrDecimals)
+ _nrDecimals(nrDecimals),
+ _plugin_stats_timestamps(nullptr)
+
+{
+ // Try to allocate in PSRAM if possible
+ void *ptr = special_calloc(1, sizeof(PluginStatsBuffer_t));
+
+ if (ptr == nullptr) { _samples = nullptr; }
+ else {
+ _samples = new (ptr) PluginStatsBuffer_t();
+ }
+ _errorValueIsNaN = isnan(_errorValue);
+ _minValue = std::numeric_limits::max();
+ _maxValue = std::numeric_limits::lowest();
+ _minValueTimestamp = 0;
+ _maxValueTimestamp = 0;
+}
+PluginStats::~PluginStats()
{
- _errorValueIsNaN = isnan(_errorValue);
- _minValue = std::numeric_limits::max();
- _maxValue = std::numeric_limits::lowest();
+ if (_samples != nullptr) {
+ free(_samples);
+
+ // delete _samples;
+ }
+ _samples = nullptr;
+ _plugin_stats_timestamps = nullptr;
+}
+
+void PluginStats::processTimeSet(const double& time_offset)
+{
+ // Check to see if there was a unix time set before the system time was set
+ // For example when receiving data from a p2p node
+ const uint64_t cur_micros = getMicros64();
+ const uint64_t offset_micros = time_offset * 1000000ull;
+
+ if ((_maxValueTimestamp > cur_micros) && (_maxValueTimestamp > offset_micros)) {
+ _maxValueTimestamp -= offset_micros;
+ }
+
+ if ((_minValueTimestamp > cur_micros) && (_minValueTimestamp > offset_micros)) {
+ _minValueTimestamp -= offset_micros;
+ }
}
bool PluginStats::push(float value)
{
- return _samples.push(value);
+ if (_samples == nullptr) { return false; }
+ return _samples->push(value);
}
-void PluginStats::trackPeak(float value)
+bool PluginStats::matchesLastTwoEntries(float value) const
{
- if (value > _maxValue) { _maxValue = value; }
+ const size_t nrSamples = getNrSamples();
+
+ if (nrSamples < 2) { return false; }
+
+ const float last = (*_samples)[nrSamples - 1];
+ const float beforeLast = (*_samples)[nrSamples - 2];
+
+ const String value_str = toString(value, _nrDecimals);
+
+ return
+ toString(last, _nrDecimals).equals(value_str) &&
+ toString(beforeLast, _nrDecimals).equals(value_str);
+
- if (value < _minValue) { _minValue = value; }
+ /*
+ const bool value_valid = isValidFloat(value);
+ const bool last_valid = isValidFloat(last);
+
+ if (value_valid != last_valid) {
+ return false;
+ }
+ const bool beforeLast_valid = isValidFloat(beforeLast);
+
+ if (value_valid != beforeLast_valid) {
+ return false;
+ }
+
+ if (value_valid) {
+ return
+ approximatelyEqual(value, last) &&
+ approximatelyEqual(value, beforeLast);
+ }
+ return true;
+ */
+}
+
+void PluginStats::trackPeak(float value, int64_t timestamp)
+{
+ if ((value > _maxValue) || (value < _minValue)) {
+ if (timestamp == 0) {
+ // Make sure both extremes are flagged with the same timestamp.
+ timestamp = getMicros64();
+ }
+
+ if (value > _maxValue) {
+ _maxValueTimestamp = timestamp;
+ _maxValue = value;
+ }
+
+ if (value < _minValue) {
+ _minValueTimestamp = timestamp;
+ _minValue = value;
+ }
+ }
}
void PluginStats::resetPeaks()
{
- _minValue = std::numeric_limits::max();
- _maxValue = std::numeric_limits::lowest();
+ _minValue = std::numeric_limits::max();
+ _maxValue = std::numeric_limits::lowest();
+ _minValueTimestamp = 0;
+ _maxValueTimestamp = 0;
+}
+
+void PluginStats::clearSamples() {
+ if (_samples != nullptr) {
+ _samples->clear();
+ }
+}
+
+size_t PluginStats::getNrSamples() const {
+ if (_samples == nullptr) { return 0u; }
+ return _samples->size();
+}
+
+float PluginStats::getSampleAvg() const {
+ return getSampleAvg(getNrSamples());
}
float PluginStats::getSampleAvg(PluginStatsBuffer_t::index_t lastNrSamples) const
{
- if (_samples.size() == 0) { return _errorValue; }
+ const size_t nrSamples = getNrSamples();
+
+ if (nrSamples == 0) { return _errorValue; }
float sum = 0.0f;
PluginStatsBuffer_t::index_t i = 0;
- if (lastNrSamples < _samples.size()) {
- i = _samples.size() - lastNrSamples;
+ if (lastNrSamples < nrSamples) {
+ i = nrSamples - lastNrSamples;
}
PluginStatsBuffer_t::index_t samplesUsed = 0;
- for (; i < _samples.size(); ++i) {
- const float sample(_samples[i]);
+ for (; i < nrSamples; ++i) {
+ const float sample((*_samples)[i]);
if (usableValue(sample)) {
++samplesUsed;
@@ -60,22 +172,71 @@ float PluginStats::getSampleAvg(PluginStatsBuffer_t::index_t lastNrSamples) cons
return sum / samplesUsed;
}
+float PluginStats::getSampleAvg_time(PluginStatsBuffer_t::index_t lastNrSamples, uint64_t& totalDuration_usec) const
+{
+ const size_t nrSamples = getNrSamples();
+
+ totalDuration_usec = 0u;
+
+ if ((nrSamples == 0) || (_plugin_stats_timestamps == nullptr)) {
+ return _errorValue;
+ }
+
+ PluginStatsBuffer_t::index_t i = 0;
+
+ if (lastNrSamples < nrSamples) {
+ i = nrSamples - lastNrSamples;
+ }
+
+ int64_t lastTimestamp = 0;
+ float lastValue = 0.0f;
+ bool lastValueUsable = false;
+ float sum = 0.0f;
+
+ for (; i < nrSamples; ++i) {
+ const float sample((*_samples)[i]);
+ const int64_t curTimestamp = (*_plugin_stats_timestamps)[i];
+ const bool curValueUsable = usableValue(sample);
+
+ if ((lastTimestamp != 0) && lastValueUsable) {
+ const int64_t duration_usec = abs(timeDiff64(lastTimestamp, curTimestamp));
+
+ if (curValueUsable) {
+ // Old and new value usable, take average of this period.
+ sum += ((lastValue + sample) / 2.0f) * duration_usec;
+ } else {
+ // New value is not usable, so just add the last value for the duration.
+ sum += lastValue * duration_usec;
+ }
+ totalDuration_usec += duration_usec;
+ }
+
+ lastValueUsable = curValueUsable;
+ lastTimestamp = curTimestamp;
+ lastValue = sample;
+ }
+
+ if (totalDuration_usec == 0) { return _errorValue; }
+ return sum / totalDuration_usec;
+}
+
float PluginStats::getSampleStdDev(PluginStatsBuffer_t::index_t lastNrSamples) const
{
- float variance = 0.0f;
- const float average = getSampleAvg(lastNrSamples);
+ const size_t nrSamples = getNrSamples();
+ float variance = 0.0f;
+ const float average = getSampleAvg(lastNrSamples);
if (!usableValue(average)) { return 0.0f; }
PluginStatsBuffer_t::index_t i = 0;
- if (lastNrSamples < _samples.size()) {
- i = _samples.size() - lastNrSamples;
+ if (lastNrSamples < nrSamples) {
+ i = nrSamples - lastNrSamples;
}
PluginStatsBuffer_t::index_t samplesUsed = 0;
- for (; i < _samples.size(); ++i) {
- const float sample(_samples[i]);
+ for (; i < nrSamples; ++i) {
+ const float sample((*_samples)[i]);
if (usableValue(sample)) {
++samplesUsed;
@@ -92,20 +253,22 @@ float PluginStats::getSampleStdDev(PluginStatsBuffer_t::index_t lastNrSamples) c
float PluginStats::getSampleExtreme(PluginStatsBuffer_t::index_t lastNrSamples, bool getMax) const
{
- if (_samples.size() == 0) { return _errorValue; }
+ const size_t nrSamples = getNrSamples();
+
+ if (nrSamples == 0) { return _errorValue; }
PluginStatsBuffer_t::index_t i = 0;
- if (lastNrSamples < _samples.size()) {
- i = _samples.size() - lastNrSamples;
+ if (lastNrSamples < nrSamples) {
+ i = nrSamples - lastNrSamples;
}
bool changed = false;
float res = getMax ? INT_MIN : INT_MAX;
- for (; i < _samples.size(); ++i) {
- const float sample(_samples[i]);
+ for (; i < nrSamples; ++i) {
+ const float sample((*_samples)[i]);
if (usableValue(sample)) {
if ((getMax && (sample > res)) ||
@@ -123,25 +286,29 @@ float PluginStats::getSampleExtreme(PluginStatsBuffer_t::index_t lastNrSamples,
float PluginStats::getSample(int lastNrSamples) const
{
- if ((_samples.size() == 0) || (_samples.size() < abs(lastNrSamples))) { return _errorValue; }
+ const size_t nrSamples = getNrSamples();
+
+ if ((nrSamples == 0) || (nrSamples < abs(lastNrSamples))) { return _errorValue; }
PluginStatsBuffer_t::index_t i = 0;
if (lastNrSamples > 0) {
- i = _samples.size() - lastNrSamples;
+ i = nrSamples - lastNrSamples;
} else if (lastNrSamples < 0) {
i = abs(lastNrSamples) - 1;
}
- if (i < _samples.size()) {
- return _samples[i];
+ if (i < nrSamples) {
+ return (*_samples)[i];
}
return _errorValue;
}
float PluginStats::operator[](PluginStatsBuffer_t::index_t index) const
{
- if (index < _samples.size()) { return _samples[index]; }
+ const size_t nrSamples = getNrSamples();
+
+ if (index < nrSamples) { return (*_samples)[index]; }
return _errorValue;
}
@@ -240,14 +407,14 @@ bool PluginStats::plugin_get_config_value_base(struct EventStruct *event, String
}
} else if (matchedCommand(command, F("size"), nrSamples)) {
// [taskname#valuename.size] Number of samples in memory
- value = _samples.size();
+ value = getNrSamples();
success = true;
} else if (matchedCommand(command, F("sample"), nrSamples)) {
success = nrSamples != 0;
if (nrSamples == INT_MIN) {
// [taskname#valuename.sample] Number of samples in memory.
- value = _samples.size();
+ value = getNrSamples();
success = true;
} else {
if (nrSamples != 0) {
@@ -292,9 +459,20 @@ bool PluginStats::webformLoad_show_stats(struct EventStruct *event) const
bool PluginStats::webformLoad_show_avg(struct EventStruct *event) const
{
if (getNrSamples() > 0) {
- addRowLabel(concat(getLabel(), F(" Average")));
- addHtmlFloat(getSampleAvg(), _nrDecimals);
+ addRowLabel(concat(getLabel(), F(" Average / sample")));
+ addHtmlFloat(getSampleAvg(), (_nrDecimals == 0) ? 1 : _nrDecimals);
addHtml(strformat(F(" (%u samples)"), getNrSamples()));
+
+ if (_plugin_stats_timestamps != nullptr) {
+ uint64_t totalDuration_usec = 0u;
+ const float avg_per_sec = getSampleAvg_time(totalDuration_usec);
+
+ if (totalDuration_usec > 0) {
+ addRowLabel(concat(getLabel(), F(" Average / sec")));
+ addHtmlFloat(avg_per_sec, (_nrDecimals == 0) ? 1 : _nrDecimals);
+ addHtml(strformat(F(" (%s duration)"), secondsToDayHourMinuteSecond_ms(totalDuration_usec).c_str()));
+ }
+ }
return true;
}
return false;
@@ -306,7 +484,7 @@ bool PluginStats::webformLoad_show_stdev(struct EventStruct *event) const
if (usableValue(stdDev) && (getNrSamples() > 1)) {
addRowLabel(concat(getLabel(), F(" std. dev")));
- addHtmlFloat(stdDev, _nrDecimals);
+ addHtmlFloat(stdDev, (_nrDecimals == 0) ? 1 : _nrDecimals);
addHtml(strformat(F(" (%u samples)"), getNrSamples()));
return true;
}
@@ -316,10 +494,53 @@ bool PluginStats::webformLoad_show_stdev(struct EventStruct *event) const
bool PluginStats::webformLoad_show_peaks(struct EventStruct *event, bool include_peak_to_peak) const
{
if (hasPeaks() && (getNrSamples() > 1)) {
- addRowLabel(concat(getLabel(), F(" Peak Low/High")));
- addHtmlFloat(getPeakLow(), _nrDecimals);
- addHtml('/');
- addHtmlFloat(getPeakHigh(), _nrDecimals);
+ return webformLoad_show_peaks(
+ event,
+ getLabel(),
+ toString(getPeakLow(), _nrDecimals),
+ toString(getPeakHigh(), _nrDecimals),
+ include_peak_to_peak);
+ }
+ return false;
+}
+
+bool PluginStats::webformLoad_show_peaks(struct EventStruct *event,
+ const String & label,
+ const String & lowValue,
+ const String & highValue,
+ bool include_peak_to_peak) const
+{
+ if (hasPeaks() && (getNrSamples() > 1)) {
+ uint32_t peakLow_frac{};
+ uint32_t peakHigh_frac{};
+ const uint32_t peakLow = node_time.systemMicros_to_Unixtime(getPeakLowTimestamp(), peakLow_frac);
+ const uint32_t peakHigh = node_time.systemMicros_to_Unixtime(getPeakHighTimestamp(), peakHigh_frac);
+ const uint32_t current = node_time.getUnixTime();
+ const bool useTimeOnly = (current - peakLow) < 86400 && (current - peakHigh) < 86400;
+ struct tm ts;
+ breakTime(time_zone.toLocal(peakLow), ts);
+
+
+ addRowLabel(concat(label, F(" Peak Low")));
+ addHtml(strformat(
+ F("%s @ %s.%03u"),
+ lowValue.c_str(),
+ useTimeOnly
+ ? formatTimeString(ts).c_str()
+ : formatDateTimeString(ts).c_str(),
+ unix_time_frac_to_millis(peakLow_frac)));
+
+
+ breakTime(time_zone.toLocal(peakHigh), ts);
+
+ addRowLabel(concat(label, F(" Peak High")));
+ addHtml(strformat(
+ F("%s @ %s.%03u"),
+ highValue.c_str(),
+ useTimeOnly
+ ? formatTimeString(ts).c_str()
+ : formatDateTimeString(ts).c_str(),
+ unix_time_frac_to_millis(peakHigh_frac)));
if (include_peak_to_peak) {
addRowLabel(concat(getLabel(), F(" Peak-to-peak")));
@@ -350,14 +571,15 @@ void PluginStats::plot_ChartJS_dataset() const
add_ChartJS_dataset_header(_ChartJS_dataset_config);
PluginStatsBuffer_t::index_t i = 0;
+ const size_t nrSamples = getNrSamples();
- for (; i < _samples.size(); ++i) {
+ for (; i < nrSamples; ++i) {
if (i != 0) {
addHtml(',');
}
- if (!isnan(_samples[i])) {
- addHtmlFloat(_samples[i], _nrDecimals);
+ if (!isnan((*_samples)[i])) {
+ addHtmlFloat((*_samples)[i], _nrDecimals);
}
else {
addHtml(F("null"));
@@ -378,339 +600,4 @@ bool PluginStats::usableValue(float value) const
return false;
}
-PluginStats_array::~PluginStats_array()
-{
- for (size_t i = 0; i < VARS_PER_TASK; ++i) {
- if (_plugin_stats[i] != nullptr) {
- delete _plugin_stats[i];
- _plugin_stats[i] = nullptr;
- }
- }
-}
-
-void PluginStats_array::initPluginStats(taskVarIndex_t taskVarIndex)
-{
- if (taskVarIndex < VARS_PER_TASK) {
- delete _plugin_stats[taskVarIndex];
- _plugin_stats[taskVarIndex] = nullptr;
-
- if (ExtraTaskSettings.enabledPluginStats(taskVarIndex)) {
- # ifdef USE_SECOND_HEAP
- HeapSelectIram ephemeral;
- # endif // ifdef USE_SECOND_HEAP
-
- _plugin_stats[taskVarIndex] = new (std::nothrow) PluginStats(
- ExtraTaskSettings.TaskDeviceValueDecimals[taskVarIndex],
- ExtraTaskSettings.TaskDeviceErrorValue[taskVarIndex]);
-
- if (_plugin_stats[taskVarIndex] != nullptr) {
- _plugin_stats[taskVarIndex]->setLabel(ExtraTaskSettings.TaskDeviceValueNames[taskVarIndex]);
- # if FEATURE_CHART_JS
- const __FlashStringHelper *colors[] = { F("#A52422"), F("#BEA57D"), F("#0F4C5C"), F("#A4BAB7") };
- _plugin_stats[taskVarIndex]->_ChartJS_dataset_config.color = colors[taskVarIndex];
- _plugin_stats[taskVarIndex]->_ChartJS_dataset_config.displayConfig = ExtraTaskSettings.getPluginStatsConfig(taskVarIndex);
- # endif // if FEATURE_CHART_JS
- }
- }
- }
-}
-
-void PluginStats_array::clearPluginStats(taskVarIndex_t taskVarIndex)
-{
- if (taskVarIndex < VARS_PER_TASK) {
- if (_plugin_stats[taskVarIndex] != nullptr) {
- delete _plugin_stats[taskVarIndex];
- _plugin_stats[taskVarIndex] = nullptr;
- }
- }
-}
-
-bool PluginStats_array::hasStats() const
-{
- for (size_t i = 0; i < VARS_PER_TASK; ++i) {
- if (_plugin_stats[i] != nullptr) { return true; }
- }
- return false;
-}
-
-bool PluginStats_array::hasPeaks() const
-{
- for (size_t i = 0; i < VARS_PER_TASK; ++i) {
- if ((_plugin_stats[i] != nullptr) && _plugin_stats[i]->hasPeaks()) {
- return true;
- }
- }
- return false;
-}
-
-size_t PluginStats_array::nrSamplesPresent() const
-{
- for (size_t i = 0; i < VARS_PER_TASK; ++i) {
- if (_plugin_stats[i] != nullptr) {
- return _plugin_stats[i]->getNrSamples();
- }
- }
- return 0;
-}
-
-size_t PluginStats_array::nrPluginStats() const
-{
- size_t res{};
-
- for (size_t i = 0; i < VARS_PER_TASK; ++i) {
- if (_plugin_stats[i] != nullptr) {
- ++res;
- }
- }
- return res;
-}
-
-void PluginStats_array::pushPluginStatsValues(struct EventStruct *event, bool trackPeaks)
-{
- if (validTaskIndex(event->TaskIndex)) {
- const uint8_t valueCount = getValueCountForTask(event->TaskIndex);
- const Sensor_VType sensorType = event->getSensorType();
-
- for (size_t i = 0; i < valueCount; ++i) {
- if (_plugin_stats[i] != nullptr) {
- const float value = UserVar.getAsDouble(event->TaskIndex, i, sensorType);
- _plugin_stats[i]->push(value);
-
- if (trackPeaks) {
- _plugin_stats[i]->trackPeak(value);
- }
- }
- }
- }
-}
-
-bool PluginStats_array::plugin_get_config_value_base(struct EventStruct *event,
- String & string) const
-{
- // Full value name is something like "taskvaluename.avg"
- const String fullValueName = parseString(string, 1);
- const String valueName = parseString(fullValueName, 1, '.');
-
- for (taskVarIndex_t i = 0; i < VARS_PER_TASK; i++)
- {
- if (_plugin_stats[i] != nullptr) {
- // Check case insensitive, since the user entered value name can have any case.
- if (valueName.equalsIgnoreCase(getTaskValueName(event->TaskIndex, i)))
- {
- return _plugin_stats[i]->plugin_get_config_value_base(event, string);
- }
- }
- }
- return false;
-}
-
-bool PluginStats_array::plugin_write_base(struct EventStruct *event, const String& string)
-{
- bool success = false;
- const String cmd = parseString(string, 1); // command
-
- const bool resetPeaks = equals(cmd, F("resetpeaks")); // Command: "taskname.resetPeaks"
- const bool clearSamples = equals(cmd, F("clearsamples")); // Command: "taskname.clearSamples"
-
- if (resetPeaks || clearSamples) {
- for (size_t i = 0; i < VARS_PER_TASK; ++i) {
- if (_plugin_stats[i] != nullptr) {
- if (resetPeaks) {
- success = true;
- _plugin_stats[i]->resetPeaks();
- }
-
- if (clearSamples) {
- success = true;
- _plugin_stats[i]->clearSamples();
- }
- }
- }
- }
- return success;
-}
-
-bool PluginStats_array::webformLoad_show_stats(struct EventStruct *event) const
-{
- bool somethingAdded = false;
-
- for (size_t i = 0; i < VARS_PER_TASK; ++i) {
- if (_plugin_stats[i] != nullptr) {
- if (_plugin_stats[i]->webformLoad_show_stats(event)) {
- somethingAdded = true;
- }
- }
- }
- return somethingAdded;
-}
-
-# if FEATURE_CHART_JS
-void PluginStats_array::plot_ChartJS(bool onlyJSON) const
-{
- const size_t nrSamples = nrSamplesPresent();
-
- if (nrSamples == 0) { return; }
-
- // Chart Header
- {
- ChartJS_options_scales scales;
- scales.add({ F("x") });
-
- for (size_t i = 0; i < VARS_PER_TASK; ++i) {
- if (_plugin_stats[i] != nullptr) {
- ChartJS_options_scale scaleOption(
- _plugin_stats[i]->_ChartJS_dataset_config.displayConfig,
- _plugin_stats[i]->getLabel());
- scaleOption.axisTitle.color = _plugin_stats[i]->_ChartJS_dataset_config.color;
- scales.add(scaleOption);
-
- _plugin_stats[i]->_ChartJS_dataset_config.axisID = scaleOption.axisID;
- }
- }
-
- scales.update_Yaxis_TickCount();
-
- add_ChartJS_chart_header(
- F("line"),
- F("TaskStatsChart"),
- {},
- 500 + (70 * (scales.nr_Y_scales() - 1)),
- 500,
- scales.toString(),
- nrSamples,
- onlyJSON);
- }
-
-
- // Add labels
- addHtml(F("\"labels\":["));
-
- for (size_t i = 0; i < nrSamples; ++i) {
- if (i != 0) {
- addHtml(',');
- }
- addHtmlInt(i);
- }
- addHtml(F("],\n\"datasets\":["));
-
-
- // Data sets
- bool first = true;
- for (size_t i = 0; i < VARS_PER_TASK; ++i) {
- if (_plugin_stats[i] != nullptr) {
- if (!first) {
- addHtml(',');
- }
- first = false;
- _plugin_stats[i]->plot_ChartJS_dataset();
- }
- }
- add_ChartJS_chart_footer(onlyJSON);
-}
-
-void PluginStats_array::plot_ChartJS_scatter(
- taskVarIndex_t values_X_axis_index,
- taskVarIndex_t values_Y_axis_index,
- const __FlashStringHelper *id,
- const ChartJS_title & chartTitle,
- const ChartJS_dataset_config& datasetConfig,
- int width,
- int height,
- bool showAverage,
- const String & options,
- bool onlyJSON) const
-{
- const PluginStats *stats_X = getPluginStats(values_X_axis_index);
- const PluginStats *stats_Y = getPluginStats(values_Y_axis_index);
-
- if ((stats_X == nullptr) || (stats_Y == nullptr)) {
- return;
- }
-
- if ((stats_X->getNrSamples() < 2) || (stats_Y->getNrSamples() < 2)) {
- return;
- }
-
- String axisOptions;
-
- {
- ChartJS_options_scales scales;
- scales.add({ F("x"), stats_X->getLabel() });
- scales.add({ F("y"), stats_Y->getLabel() });
- axisOptions = scales.toString();
- }
-
-
- const size_t nrSamples = stats_X->getNrSamples();
-
- add_ChartJS_chart_header(
- F("scatter"),
- id,
- chartTitle,
- width,
- height,
- axisOptions,
- nrSamples,
- onlyJSON);
-
- // Add labels, which will be shown in a tooltip when hovering with the mouse over a point.
- addHtml(F("\"labels\":["));
-
- for (size_t i = 0; i < nrSamples; ++i) {
- if (i != 0) {
- addHtml(',');
- }
- addHtmlInt(i);
- }
- addHtml(F("],\n\"datasets\":["));
-
- // Long/Lat Coordinates
- add_ChartJS_dataset_header(datasetConfig);
-
- // Add scatter data
- for (size_t i = 0; i < nrSamples; ++i) {
- const float valX = (*stats_X)[i];
- const float valY = (*stats_Y)[i];
- add_ChartJS_scatter_data_point(valX, valY, 6);
- }
-
- add_ChartJS_dataset_footer(F("\"showLine\":true"));
-
- if (showAverage) {
- // Add single point showing the average
- addHtml(',');
- add_ChartJS_dataset_header(
- {
- F("Average"),
- F("#0F4C5C") });
-
- {
- const float valX = stats_X->getSampleAvg();
- const float valY = stats_Y->getSampleAvg();
- add_ChartJS_scatter_data_point(valX, valY, 6);
- }
- add_ChartJS_dataset_footer(F("\"pointRadius\":6,\"pointHoverRadius\":10"));
- }
- add_ChartJS_chart_footer(onlyJSON);
-}
-
-# endif // if FEATURE_CHART_JS
-
-
-PluginStats * PluginStats_array::getPluginStats(taskVarIndex_t taskVarIndex) const
-{
- if ((taskVarIndex < VARS_PER_TASK)) {
- return _plugin_stats[taskVarIndex];
- }
- return nullptr;
-}
-
-PluginStats * PluginStats_array::getPluginStats(taskVarIndex_t taskVarIndex)
-{
- if ((taskVarIndex < VARS_PER_TASK)) {
- return _plugin_stats[taskVarIndex];
- }
- return nullptr;
-}
-
#endif // if FEATURE_PLUGIN_STATS
diff --git a/src/src/DataStructs/PluginStats.h b/src/src/DataStructs/PluginStats.h
index ebf7563bd6..2a320eb809 100644
--- a/src/src/DataStructs/PluginStats.h
+++ b/src/src/DataStructs/PluginStats.h
@@ -6,6 +6,8 @@
#if FEATURE_PLUGIN_STATS
# include "../DataStructs/ChartJS_dataset_config.h"
+# include "../DataStructs/PluginStats_size.h"
+# include "../DataStructs/PluginStats_timestamp.h"
# include "../DataTypes/TaskIndex.h"
@@ -13,21 +15,6 @@
# include "../WebServer/Chart_JS_title.h"
# endif // if FEATURE_CHART_JS
-# include
-
-# ifndef PLUGIN_STATS_NR_ELEMENTS
-# ifdef ESP8266
-# ifdef USE_SECOND_HEAP
-# define PLUGIN_STATS_NR_ELEMENTS 50
-# else // ifdef USE_SECOND_HEAP
-# define PLUGIN_STATS_NR_ELEMENTS 16
-# endif // ifdef USE_SECOND_HEAP
-# endif // ifdef ESP8266
-# ifdef ESP32
-# define PLUGIN_STATS_NR_ELEMENTS 250
-# endif // ifdef ESP32
-# endif // ifndef PLUGIN_STATS_NR_ELEMENTS
-
class PluginStats {
public:
@@ -37,15 +24,26 @@ class PluginStats {
PluginStats(uint8_t nrDecimals,
float errorValue);
+ ~PluginStats();
+
+ void processTimeSet(const double& time_offset);
+
+ void setPluginStats_timestamp(PluginStats_timestamp *plugin_stats_timestamps)
+ {
+ _plugin_stats_timestamps = plugin_stats_timestamps;
+ }
// Add a sample to the _sample buffer
// This does not also track peaks as the peaks could be raw sensor data and the samples processed data.
bool push(float value);
+ // When only updating the timestamp of the last entry, we should look at the last
+ bool matchesLastTwoEntries(float value) const;
+
// Keep track of peaks.
// Use this for sensors that need to take several samples before actually output a task value.
// For example the ADC with oversampling
- void trackPeak(float value);
+ void trackPeak(float value, int64_t timestamp = 0u);
// Get lowest recorded value since reset
float getPeakLow() const {
@@ -57,34 +55,47 @@ class PluginStats {
return hasPeaks() ? _maxValue : _errorValue;
}
- bool hasPeaks() const {
+ int64_t getPeakLowTimestamp() const {
+ return hasPeaks() ? _minValueTimestamp : 0;
+ }
+
+ int64_t getPeakHighTimestamp() const {
+ return hasPeaks() ? _maxValueTimestamp : 0;
+ }
+
+ bool hasPeaks() const {
return _maxValue >= _minValue;
}
// Set the peaks to unset values
- void resetPeaks();
+ void resetPeaks();
- void clearSamples() {
- _samples.clear();
- }
+ void clearSamples();
- size_t getNrSamples() const {
- return _samples.size();
- }
+ size_t getNrSamples() const;
// Compute average over all stored values
- float getSampleAvg() const {
- return getSampleAvg(_samples.size());
- }
+ float getSampleAvg() const;
// Compute average over last N stored values
- float getSampleAvg(PluginStatsBuffer_t::index_t lastNrSamples) const;
+ float getSampleAvg(PluginStatsBuffer_t::index_t lastNrSamples) const;
// Compute the standard deviation over all stored values
- float getSampleStdDev() const {
- return getSampleStdDev(_samples.size());
+ float getSampleStdDev() const {
+ return getSampleStdDev(getNrSamples());
+ }
+
+ // Compute average over all stored values, taking timestamp into account.
+ // Returns average per second.
+ float getSampleAvg_time(uint64_t& totalDuration_usec) const {
+ return getSampleAvg_time(getNrSamples(), totalDuration_usec);
}
+ // Compute average over last N stored values, taking timestamp into account.
+ // Returns average per second.
+ float getSampleAvg_time(PluginStatsBuffer_t::index_t lastNrSamples,
+ uint64_t & totalDuration_usec) const;
+
// Compute the standard deviation over last N stored values
float getSampleStdDev(PluginStatsBuffer_t::index_t lastNrSamples) const;
@@ -116,6 +127,12 @@ class PluginStats {
bool webformLoad_show_stdev(struct EventStruct *event) const;
bool webformLoad_show_peaks(struct EventStruct *event,
bool include_peak_to_peak = true) const;
+ bool webformLoad_show_peaks(struct EventStruct *event,
+ const String& label,
+ const String& lowValue,
+ const String& highValue,
+ bool include_peak_to_peak = true) const;
+
void webformLoad_show_val(
struct EventStruct *event,
const String & label,
@@ -164,67 +181,18 @@ class PluginStats {
float _minValue;
float _maxValue;
+ int64_t _minValueTimestamp;
+ int64_t _maxValueTimestamp;
- PluginStatsBuffer_t _samples;
+ PluginStatsBuffer_t *_samples = nullptr;
float _errorValue;
bool _errorValueIsNaN;
uint8_t _nrDecimals = 3u;
-};
-
-class PluginStats_array {
-public:
- PluginStats_array() = default;
- ~PluginStats_array();
-
- void initPluginStats(taskVarIndex_t taskVarIndex);
- void clearPluginStats(taskVarIndex_t taskVarIndex);
-
- bool hasStats() const;
- bool hasPeaks() const;
-
- size_t nrSamplesPresent() const;
- size_t nrPluginStats() const;
-
- void pushPluginStatsValues(struct EventStruct *event,
- bool trackPeaks);
-
- bool plugin_get_config_value_base(struct EventStruct *event,
- String & string) const;
-
- bool plugin_write_base(struct EventStruct *event,
- const String & string);
-
- bool webformLoad_show_stats(struct EventStruct *event) const;
-
-# if FEATURE_CHART_JS
- void plot_ChartJS(bool onlyJSON = false) const;
-
- void plot_ChartJS_scatter(
- taskVarIndex_t values_X_axis_index,
- taskVarIndex_t values_Y_axis_index,
- const __FlashStringHelper *id,
- const ChartJS_title & chartTitle,
- const ChartJS_dataset_config& datasetConfig,
- int width,
- int height,
- bool showAverage = true,
- const String & options = EMPTY_STRING,
- bool onlyJSON = false) const;
-
-
-# endif // if FEATURE_CHART_JS
-
-
- PluginStats* getPluginStats(taskVarIndex_t taskVarIndex) const;
-
- PluginStats* getPluginStats(taskVarIndex_t taskVarIndex);
-
-private:
-
- PluginStats *_plugin_stats[VARS_PER_TASK] = {};
+ PluginStats_timestamp *_plugin_stats_timestamps = nullptr;
};
+
#endif // if FEATURE_PLUGIN_STATS
#endif // ifndef HELPERS_PLUGINSTATS_H
diff --git a/src/src/DataStructs/PluginStats_Config.h b/src/src/DataStructs/PluginStats_Config.h
index 00ce1a05dd..16dcff1d01 100644
--- a/src/src/DataStructs/PluginStats_Config.h
+++ b/src/src/DataStructs/PluginStats_Config.h
@@ -26,7 +26,9 @@ struct PluginStats_Config_t {
return static_cast(bits.chartAxisPosition);
}
- bool isLeft() const { return AxisPosition::Left == getAxisPosition(); }
+ bool isLeft() const {
+ return AxisPosition::Left == getAxisPosition();
+ }
void setAxisPosition(AxisPosition position) {
bits.chartAxisPosition = static_cast(position);
@@ -81,8 +83,7 @@ struct PluginStats_Config_t {
uint8_t chartAxisPosition : 1; // Bit 05
uint8_t unused_06 : 1; // Bit 06
uint8_t unused_07 : 1; // Bit 07
- } bits;
-
+ } bits;
};
#endif // if FEATURE_PLUGIN_STATS
diff --git a/src/src/DataStructs/PluginStats_array.cpp b/src/src/DataStructs/PluginStats_array.cpp
new file mode 100644
index 0000000000..f23524f53e
--- /dev/null
+++ b/src/src/DataStructs/PluginStats_array.cpp
@@ -0,0 +1,511 @@
+#include "../DataStructs/PluginStats_array.h"
+
+#if FEATURE_PLUGIN_STATS
+
+# include "../../_Plugin_Helper.h"
+
+# include "../Globals/TimeZone.h"
+
+# include "../Helpers/ESPEasy_math.h"
+# include "../Helpers/Memory.h"
+
+# include "../WebServer/Chart_JS.h"
+
+PluginStats_array::~PluginStats_array()
+{
+ for (size_t i = 0; i < VARS_PER_TASK; ++i) {
+ if (_plugin_stats[i] != nullptr) {
+ delete _plugin_stats[i];
+ _plugin_stats[i] = nullptr;
+ }
+ }
+
+ if (_plugin_stats_timestamps != nullptr) {
+ free(_plugin_stats_timestamps);
+ _plugin_stats_timestamps = nullptr;
+ }
+}
+
+void PluginStats_array::initPluginStats(taskIndex_t taskIndex, taskVarIndex_t taskVarIndex)
+{
+ if (taskVarIndex < VARS_PER_TASK) {
+ delete _plugin_stats[taskVarIndex];
+ _plugin_stats[taskVarIndex] = nullptr;
+
+ if (!hasStats()) {
+ if (_plugin_stats_timestamps != nullptr) {
+ free(_plugin_stats_timestamps);
+ _plugin_stats_timestamps = nullptr;
+ }
+ }
+
+ if (ExtraTaskSettings.enabledPluginStats(taskVarIndex)) {
+ # ifdef USE_SECOND_HEAP
+ HeapSelectIram ephemeral;
+ # endif // ifdef USE_SECOND_HEAP
+
+ // Try to allocate in PSRAM if possible
+ constexpr unsigned size = sizeof(PluginStats);
+ void *ptr = special_calloc(1, size);
+
+ if (ptr == nullptr) { _plugin_stats[taskVarIndex] = nullptr; }
+ else {
+ _plugin_stats[taskVarIndex] = new (ptr) PluginStats(
+ ExtraTaskSettings.TaskDeviceValueDecimals[taskVarIndex],
+ ExtraTaskSettings.TaskDeviceErrorValue[taskVarIndex]);
+ }
+
+
+ if (_plugin_stats[taskVarIndex] != nullptr) {
+ _plugin_stats[taskVarIndex]->setLabel(ExtraTaskSettings.TaskDeviceValueNames[taskVarIndex]);
+ # if FEATURE_CHART_JS
+ const __FlashStringHelper *colors[] = { F("#A52422"), F("#BEA57D"), F("#0F4C5C"), F("#A4BAB7") };
+ _plugin_stats[taskVarIndex]->_ChartJS_dataset_config.color = colors[taskVarIndex];
+ _plugin_stats[taskVarIndex]->_ChartJS_dataset_config.displayConfig = ExtraTaskSettings.getPluginStatsConfig(taskVarIndex);
+ # endif // if FEATURE_CHART_JS
+
+ if (_plugin_stats_timestamps != nullptr) {
+ _plugin_stats[taskVarIndex]->setPluginStats_timestamp(_plugin_stats_timestamps);
+ }
+ }
+ }
+ }
+
+ if (hasStats()) {
+ if (_plugin_stats_timestamps == nullptr) {
+ // Try to allocate in PSRAM if possible
+ constexpr unsigned size = sizeof(PluginStats_timestamp);
+ void *ptr = special_calloc(1, size);
+
+ if (ptr != nullptr) {
+ // TODO TD-er: Let the task decide whether we need 1/50 sec resolution or 1/10
+ // 1/10 sec resolution allows for ~13.6 years without overflow
+ _plugin_stats_timestamps = new (ptr) PluginStats_timestamp(true);
+ }
+
+ for (size_t i = 0; i < VARS_PER_TASK; ++i) {
+ _plugin_stats[taskVarIndex]->setPluginStats_timestamp(_plugin_stats_timestamps);
+ }
+ }
+ }
+}
+
+void PluginStats_array::clearPluginStats(taskVarIndex_t taskVarIndex)
+{
+ if (taskVarIndex < VARS_PER_TASK) {
+ if (_plugin_stats[taskVarIndex] != nullptr) {
+ delete _plugin_stats[taskVarIndex];
+ _plugin_stats[taskVarIndex] = nullptr;
+ }
+ }
+
+ if (!hasStats()) {
+ if (_plugin_stats_timestamps != nullptr) {
+ free(_plugin_stats_timestamps);
+ _plugin_stats_timestamps = nullptr;
+ }
+ }
+}
+
+void PluginStats_array::processTimeSet(const double& time_offset)
+{
+ if (_plugin_stats_timestamps != nullptr) {
+ _plugin_stats_timestamps->processTimeSet(time_offset);
+ }
+
+ // Also update timestamps of peaks
+ for (taskVarIndex_t taskVarIndex = 0; taskVarIndex < VARS_PER_TASK; ++taskVarIndex) {
+ PluginStats *stats = getPluginStats(taskVarIndex);
+
+ if (stats != nullptr) {
+ stats->processTimeSet(time_offset);
+ }
+ }
+}
+
+bool PluginStats_array::hasStats() const
+{
+ for (size_t i = 0; i < VARS_PER_TASK; ++i) {
+ if (_plugin_stats[i] != nullptr) { return true; }
+ }
+ return false;
+}
+
+bool PluginStats_array::hasPeaks() const
+{
+ for (size_t i = 0; i < VARS_PER_TASK; ++i) {
+ if ((_plugin_stats[i] != nullptr) && _plugin_stats[i]->hasPeaks()) {
+ return true;
+ }
+ }
+ return false;
+}
+
+size_t PluginStats_array::nrSamplesPresent() const
+{
+ for (size_t i = 0; i < VARS_PER_TASK; ++i) {
+ if (_plugin_stats[i] != nullptr) {
+ return _plugin_stats[i]->getNrSamples();
+ }
+ }
+ return 0;
+}
+
+size_t PluginStats_array::nrPluginStats() const
+{
+ size_t res{};
+
+ for (size_t i = 0; i < VARS_PER_TASK; ++i) {
+ if (_plugin_stats[i] != nullptr) {
+ ++res;
+ }
+ }
+ return res;
+}
+
+uint32_t PluginStats_array::getFullPeriodInSec(uint32_t& time_frac) const
+{
+ if (_plugin_stats_timestamps == nullptr) {
+ time_frac = 0u;
+ return 0u;
+ }
+ return _plugin_stats_timestamps->getFullPeriodInSec(time_frac);
+}
+
+void PluginStats_array::pushPluginStatsValues(
+ struct EventStruct *event,
+ bool trackPeaks,
+ bool onlyUpdateTimestampWhenSame)
+{
+ if (validTaskIndex(event->TaskIndex)) {
+ const uint8_t valueCount = getValueCountForTask(event->TaskIndex);
+
+ if (valueCount > 0) {
+ const Sensor_VType sensorType = event->getSensorType();
+
+ const int64_t timestamp_sysmicros = event->getTimestamp_as_systemMicros();
+
+ if (onlyUpdateTimestampWhenSame && (_plugin_stats_timestamps != nullptr)) {
+ // When only updating the timestamp of the last entry,
+ // we should look at the last 2 entries to see if they are the same.
+ bool isSame = true;
+ size_t i = 0;
+
+ while (isSame && i < valueCount) {
+ if (_plugin_stats[i] != nullptr) {
+ const float value = UserVar.getAsDouble(event->TaskIndex, i, sensorType);
+
+ if (!_plugin_stats[i]->matchesLastTwoEntries(value)) {
+ isSame = false;
+ }
+ }
+ ++i;
+ }
+
+ if (isSame) {
+ _plugin_stats_timestamps->updateLast(timestamp_sysmicros);
+ return;
+ }
+ }
+
+ if (_plugin_stats_timestamps != nullptr) {
+ _plugin_stats_timestamps->push(timestamp_sysmicros);
+ }
+
+ for (size_t i = 0; i < valueCount; ++i) {
+ if (_plugin_stats[i] != nullptr) {
+ const float value = UserVar.getAsDouble(event->TaskIndex, i, sensorType);
+ _plugin_stats[i]->push(value);
+
+ if (trackPeaks) {
+ _plugin_stats[i]->trackPeak(value, timestamp_sysmicros);
+ }
+ }
+ }
+ }
+ }
+}
+
+bool PluginStats_array::plugin_get_config_value_base(struct EventStruct *event,
+ String & string) const
+{
+ // Full value name is something like "taskvaluename.avg"
+ const String fullValueName = parseString(string, 1);
+ const String valueName = parseString(fullValueName, 1, '.');
+
+ const uint8_t valueCount = getValueCountForTask(event->TaskIndex);
+
+ for (taskVarIndex_t i = 0; i < valueCount; i++)
+ {
+ if (_plugin_stats[i] != nullptr) {
+ // Check case insensitive, since the user entered value name can have any case.
+ if (valueName.equalsIgnoreCase(Cache.getTaskDeviceValueName(event->TaskIndex, i)))
+ {
+ return _plugin_stats[i]->plugin_get_config_value_base(event, string);
+ }
+ }
+ }
+ return false;
+}
+
+bool PluginStats_array::plugin_write_base(struct EventStruct *event, const String& string)
+{
+ bool success = false;
+ const String cmd = parseString(string, 1); // command
+
+ const bool resetPeaks = equals(cmd, F("resetpeaks")); // Command: "taskname.resetPeaks"
+ const bool clearSamples = equals(cmd, F("clearsamples")); // Command: "taskname.clearSamples"
+
+ if (resetPeaks || clearSamples) {
+ for (size_t i = 0; i < VARS_PER_TASK; ++i) {
+ if (_plugin_stats[i] != nullptr) {
+ if (resetPeaks) {
+ success = true;
+ _plugin_stats[i]->resetPeaks();
+ }
+
+ if (clearSamples) {
+ success = true;
+ _plugin_stats[i]->clearSamples();
+ }
+ }
+ }
+ }
+ return success;
+}
+
+bool PluginStats_array::webformLoad_show_stats(struct EventStruct *event, bool showTaskValues) const
+{
+ bool somethingAdded = false;
+
+ uint32_t time_frac{};
+ const uint32_t duration = getFullPeriodInSec(time_frac);
+ const uint32_t nrSamples = nrSamplesPresent();
+
+ if ((duration > 0) && (nrSamples > 1)) {
+ const uint32_t duration_millis = unix_time_frac_to_millis(time_frac);
+ addRowLabel(F("Total Duration"));
+ addHtml(strformat(
+ F("%s.%03u (%u.%03u sec)"),
+ secondsToDayHourMinuteSecond(duration).c_str(),
+ duration_millis,
+ duration,
+ duration_millis));
+ const float duration_f = static_cast(duration) + (duration_millis / 1000.0f);
+ addRowLabel(F("Total Nr Samples"));
+ addHtmlInt(nrSamples);
+ addRowLabel(F("Avg Rate"));
+ addHtmlFloat(duration_f / static_cast(nrSamples - 1), 2);
+ addUnit(F("sec/sample"));
+ addFormSeparator(4);
+ somethingAdded = true;
+ }
+
+ if (showTaskValues) {
+ for (size_t i = 0; i < VARS_PER_TASK; ++i) {
+ if (_plugin_stats[i] != nullptr) {
+ if (_plugin_stats[i]->webformLoad_show_stats(event)) {
+ somethingAdded = true;
+ }
+ }
+ }
+ }
+ return somethingAdded;
+}
+
+# if FEATURE_CHART_JS
+void PluginStats_array::plot_ChartJS(bool onlyJSON) const
+{
+ const size_t nrSamples = nrSamplesPresent();
+
+ if (nrSamples == 0) { return; }
+
+ // Chart Header
+ {
+ ChartJS_options_scales scales;
+ {
+ ChartJS_options_scale scaleOption(F("x"));
+
+ if (_plugin_stats_timestamps != nullptr) {
+ scaleOption.scaleType = F("time");
+ }
+ scales.add(scaleOption);
+ }
+
+ for (size_t i = 0; i < VARS_PER_TASK; ++i) {
+ if (_plugin_stats[i] != nullptr) {
+ ChartJS_options_scale scaleOption(
+ _plugin_stats[i]->_ChartJS_dataset_config.displayConfig,
+ _plugin_stats[i]->getLabel());
+ scaleOption.axisTitle.color = _plugin_stats[i]->_ChartJS_dataset_config.color;
+ scales.add(scaleOption);
+
+ _plugin_stats[i]->_ChartJS_dataset_config.axisID = scaleOption.axisID;
+ }
+ }
+
+ scales.update_Yaxis_TickCount();
+
+ const bool enableZoom = true;
+
+ add_ChartJS_chart_header(
+ F("line"),
+ F("TaskStatsChart"),
+ {},
+ 500 + (70 * (scales.nr_Y_scales() - 1)),
+ 500,
+ scales.toString(),
+ enableZoom,
+ nrSamples,
+ onlyJSON);
+ }
+
+
+ // Add labels
+ addHtml(F("\"labels\":["));
+
+ for (size_t i = 0; i < nrSamples; ++i) {
+ if (i != 0) {
+ addHtml(',');
+ }
+
+ if (_plugin_stats_timestamps != nullptr) {
+ struct tm ts;
+ uint32_t unix_time_frac{};
+ const uint32_t uinxtime_sec = node_time.systemMicros_to_Unixtime((*_plugin_stats_timestamps)[i], unix_time_frac);
+ const uint32_t local_timestamp = time_zone.toLocal(uinxtime_sec);
+ breakTime(local_timestamp, ts);
+ addHtml('"');
+ addHtml(formatDateTimeString(ts));
+ addHtml(strformat(F(".%03u"), unix_time_frac_to_millis(unix_time_frac)));
+ addHtml('"');
+ } else {
+ addHtmlInt(i);
+ }
+ }
+ addHtml(F("],\n\"datasets\":["));
+
+
+ // Data sets
+ bool first = true;
+
+ for (size_t i = 0; i < VARS_PER_TASK; ++i) {
+ if (_plugin_stats[i] != nullptr) {
+ if (!first) {
+ addHtml(',');
+ }
+ first = false;
+ _plugin_stats[i]->plot_ChartJS_dataset();
+ }
+ }
+ add_ChartJS_chart_footer(onlyJSON);
+}
+
+void PluginStats_array::plot_ChartJS_scatter(
+ taskVarIndex_t values_X_axis_index,
+ taskVarIndex_t values_Y_axis_index,
+ const __FlashStringHelper *id,
+ const ChartJS_title & chartTitle,
+ const ChartJS_dataset_config& datasetConfig,
+ int width,
+ int height,
+ bool showAverage,
+ const String & options,
+ bool onlyJSON) const
+{
+ const PluginStats *stats_X = getPluginStats(values_X_axis_index);
+ const PluginStats *stats_Y = getPluginStats(values_Y_axis_index);
+
+ if ((stats_X == nullptr) || (stats_Y == nullptr)) {
+ return;
+ }
+
+ if ((stats_X->getNrSamples() < 2) || (stats_Y->getNrSamples() < 2)) {
+ return;
+ }
+
+ String axisOptions;
+
+ {
+ ChartJS_options_scales scales;
+ scales.add({ F("x"), stats_X->getLabel() });
+ scales.add({ F("y"), stats_Y->getLabel() });
+ axisOptions = scales.toString();
+ }
+
+
+ const size_t nrSamples = stats_X->getNrSamples();
+ const bool enableZoom = false;
+
+ add_ChartJS_chart_header(
+ F("scatter"),
+ id,
+ chartTitle,
+ width,
+ height,
+ axisOptions,
+ enableZoom,
+ nrSamples,
+ onlyJSON);
+
+ // Add labels, which will be shown in a tooltip when hovering with the mouse over a point.
+ addHtml(F("\"labels\":["));
+
+ for (size_t i = 0; i < nrSamples; ++i) {
+ if (i != 0) {
+ addHtml(',');
+ }
+ addHtmlInt(i);
+ }
+ addHtml(F("],\n\"datasets\":["));
+
+ // Long/Lat Coordinates
+ add_ChartJS_dataset_header(datasetConfig);
+
+ // Add scatter data
+ for (size_t i = 0; i < nrSamples; ++i) {
+ const float valX = (*stats_X)[i];
+ const float valY = (*stats_Y)[i];
+ add_ChartJS_scatter_data_point(valX, valY, 6);
+ }
+
+ add_ChartJS_dataset_footer(F("\"showLine\":true"));
+
+ if (showAverage) {
+ // Add single point showing the average
+ addHtml(',');
+ add_ChartJS_dataset_header(
+ {
+ F("Average"),
+ F("#0F4C5C") });
+
+ {
+ const float valX = stats_X->getSampleAvg();
+ const float valY = stats_Y->getSampleAvg();
+ add_ChartJS_scatter_data_point(valX, valY, 6);
+ }
+ add_ChartJS_dataset_footer(F("\"pointRadius\":6,\"pointHoverRadius\":10"));
+ }
+ add_ChartJS_chart_footer(onlyJSON);
+}
+
+# endif // if FEATURE_CHART_JS
+
+
+PluginStats * PluginStats_array::getPluginStats(taskVarIndex_t taskVarIndex) const
+{
+ if ((taskVarIndex < VARS_PER_TASK)) {
+ return _plugin_stats[taskVarIndex];
+ }
+ return nullptr;
+}
+
+PluginStats * PluginStats_array::getPluginStats(taskVarIndex_t taskVarIndex)
+{
+ if ((taskVarIndex < VARS_PER_TASK)) {
+ return _plugin_stats[taskVarIndex];
+ }
+ return nullptr;
+}
+
+#endif // if FEATURE_PLUGIN_STATS
diff --git a/src/src/DataStructs/PluginStats_array.h b/src/src/DataStructs/PluginStats_array.h
new file mode 100644
index 0000000000..ec3e1df712
--- /dev/null
+++ b/src/src/DataStructs/PluginStats_array.h
@@ -0,0 +1,86 @@
+#ifndef HELPERS_PLUGINSTATS_ARRAY_H
+#define HELPERS_PLUGINSTATS_ARRAY_H
+
+#include "../../ESPEasy_common.h"
+
+#if FEATURE_PLUGIN_STATS
+
+# include "../DataStructs/PluginStats.h"
+# include "../DataStructs/PluginStats_timestamp.h"
+
+# include "../DataStructs/ChartJS_dataset_config.h"
+# include "../DataTypes/TaskIndex.h"
+
+
+# if FEATURE_CHART_JS
+# include "../WebServer/Chart_JS_title.h"
+# endif // if FEATURE_CHART_JS
+
+
+class PluginStats_array {
+public:
+
+ PluginStats_array() = default;
+ ~PluginStats_array();
+
+ void initPluginStats(taskIndex_t taskIndex,
+ taskVarIndex_t taskVarIndex);
+ void clearPluginStats(taskVarIndex_t taskVarIndex);
+
+ // Update any logged timestamp with this newly set system time.
+ void processTimeSet(const double& time_offset);
+
+ bool hasStats() const;
+ bool hasPeaks() const;
+
+ size_t nrSamplesPresent() const;
+ size_t nrPluginStats() const;
+
+ // Compute the duration between first and last sample in seconds
+ // For 0 or 1 samples, the period will be 0 seconds.
+ uint32_t getFullPeriodInSec(uint32_t& time_frac) const;
+
+ void pushPluginStatsValues(struct EventStruct *event,
+ bool trackPeaks,
+ bool onlyUpdateTimestampWhenSame);
+
+ bool plugin_get_config_value_base(struct EventStruct *event,
+ String & string) const;
+
+ bool plugin_write_base(struct EventStruct *event,
+ const String & string);
+
+ bool webformLoad_show_stats(struct EventStruct *event,
+ bool showTaskValues = true) const;
+
+# if FEATURE_CHART_JS
+ void plot_ChartJS(bool onlyJSON = false) const;
+
+ void plot_ChartJS_scatter(
+ taskVarIndex_t values_X_axis_index,
+ taskVarIndex_t values_Y_axis_index,
+ const __FlashStringHelper *id,
+ const ChartJS_title & chartTitle,
+ const ChartJS_dataset_config& datasetConfig,
+ int width,
+ int height,
+ bool showAverage = true,
+ const String & options = EMPTY_STRING,
+ bool onlyJSON = false) const;
+
+
+# endif // if FEATURE_CHART_JS
+
+
+ PluginStats* getPluginStats(taskVarIndex_t taskVarIndex) const;
+
+ PluginStats* getPluginStats(taskVarIndex_t taskVarIndex);
+
+private:
+
+ PluginStats *_plugin_stats[VARS_PER_TASK] = {};
+ PluginStats_timestamp *_plugin_stats_timestamps = nullptr;
+};
+
+#endif // if FEATURE_PLUGIN_STATS
+#endif // ifndef HELPERS_PLUGINSTATS_ARRAY_H
diff --git a/src/src/DataStructs/PluginStats_size.h b/src/src/DataStructs/PluginStats_size.h
new file mode 100644
index 0000000000..b501d7fdef
--- /dev/null
+++ b/src/src/DataStructs/PluginStats_size.h
@@ -0,0 +1,25 @@
+#ifndef HELPERS_PLUGINSTATS_SIZE_H
+#define HELPERS_PLUGINSTATS_SIZE_H
+
+#include "../../ESPEasy_common.h"
+
+#if FEATURE_PLUGIN_STATS
+
+# include
+
+# ifndef PLUGIN_STATS_NR_ELEMENTS
+# ifdef ESP8266
+# ifdef USE_SECOND_HEAP
+# define PLUGIN_STATS_NR_ELEMENTS 50
+# else // ifdef USE_SECOND_HEAP
+# define PLUGIN_STATS_NR_ELEMENTS 16
+# endif // ifdef USE_SECOND_HEAP
+# endif // ifdef ESP8266
+# ifdef ESP32
+# define PLUGIN_STATS_NR_ELEMENTS 250
+# endif // ifdef ESP32
+# endif // ifndef PLUGIN_STATS_NR_ELEMENTS
+
+
+#endif // if FEATURE_PLUGIN_STATS
+#endif // ifndef HELPERS_PLUGINSTATS_SIZE_H
diff --git a/src/src/DataStructs/PluginStats_timestamp.cpp b/src/src/DataStructs/PluginStats_timestamp.cpp
new file mode 100644
index 0000000000..f71f05873b
--- /dev/null
+++ b/src/src/DataStructs/PluginStats_timestamp.cpp
@@ -0,0 +1,120 @@
+#include "../DataStructs/PluginStats_timestamp.h"
+
+#if FEATURE_PLUGIN_STATS
+
+# include "../Globals/ESPEasy_time.h"
+# include "../Helpers/ESPEasy_time_calc.h"
+
+PluginStats_timestamp::PluginStats_timestamp(bool useHighRes)
+ : _internal_to_micros_ratio(useHighRes
+? 20000ull // 1/50 sec resolution
+: 100000ull) // 1/10 sec resolution
+{}
+
+PluginStats_timestamp::~PluginStats_timestamp()
+{}
+
+bool PluginStats_timestamp::push(const int64_t& timestamp_sysmicros)
+{
+ return _timestamps.push(systemMicros_to_internalTimestamp(timestamp_sysmicros));
+}
+
+bool PluginStats_timestamp::updateLast(const int64_t& timestamp_sysmicros)
+{
+ const size_t nrElements = _timestamps.size();
+
+ if (nrElements == 0) { return false; }
+ return _timestamps.set(nrElements - 1, systemMicros_to_internalTimestamp(timestamp_sysmicros));
+}
+
+void PluginStats_timestamp::clear()
+{
+ _timestamps.clear();
+}
+
+void PluginStats_timestamp::processTimeSet(const double& time_offset)
+{
+ // Check to see if there was a unix time set before the system time was set
+ // For example when receiving data from a p2p node
+
+ /*
+ const uint64_t cur_micros = getMicros64();
+ const uint64_t offset_micros = time_offset * 1000000ull;
+ const size_t nrSamples = _timestamps.size();
+
+ // GMT Wed Jan 01 2020 00:00:00 GMT+0000
+ const int64_t unixTime_20200101 = 1577836800ll * _internal_to_micros_ratio;
+
+ for (PluginStatsTimestamps_t::index_t i = 0; i < nrSamples; ++i) {
+ if (_timestamps[i] < unixTime_20200101) {
+ _timestamps.set(i, _timestamps[i] + time_offset);
+ }
+ }
+ */
+}
+
+int64_t PluginStats_timestamp::getTimestamp(int lastNrSamples) const
+{
+ if ((_timestamps.size() == 0) || (_timestamps.size() < abs(lastNrSamples))) { return 0u; }
+
+ PluginStatsTimestamps_t::index_t i = 0;
+
+ if (lastNrSamples > 0) {
+ i = _timestamps.size() - lastNrSamples;
+ } else if (lastNrSamples < 0) {
+ i = abs(lastNrSamples) - 1;
+ }
+
+ if (i < _timestamps.size()) {
+ return internalTimestamp_to_systemMicros(_timestamps[i]);
+ }
+ return 0u;
+}
+
+uint32_t PluginStats_timestamp::getFullPeriodInSec(uint32_t& time_frac) const
+{
+ const size_t nrSamples = _timestamps.size();
+
+ time_frac = 0u;
+
+ if (nrSamples <= 1) {
+ return 0u;
+ }
+
+ const int64_t start = internalTimestamp_to_systemMicros(_timestamps[0]);
+ const int64_t end = internalTimestamp_to_systemMicros(_timestamps[nrSamples - 1]);
+
+ const int64_t period_usec = (end < start) ? (start - end) : (end - start);
+
+ return micros_to_sec_time_frac(period_usec, time_frac);
+}
+
+int64_t PluginStats_timestamp::operator[](PluginStatsTimestamps_t::index_t index) const
+{
+ if (index < _timestamps.size()) {
+ return internalTimestamp_to_systemMicros(_timestamps[index]);
+ }
+ return 0u;
+}
+
+uint32_t PluginStats_timestamp::systemMicros_to_internalTimestamp(const int64_t& timestamp_sysmicros) const
+{
+ return static_cast(timestamp_sysmicros / _internal_to_micros_ratio);
+}
+
+int64_t PluginStats_timestamp::internalTimestamp_to_systemMicros(const uint32_t& internalTimestamp) const
+{
+ const uint64_t cur_micros = getMicros64();
+ const uint64_t overflow_step = 4294967296ull * _internal_to_micros_ratio;
+
+ uint64_t sysMicros = static_cast(internalTimestamp) * _internal_to_micros_ratio;
+
+ // Try to get in the range of the current system micros
+ // This only does play a role in high res mode, when uptime is over 994 days.
+ while ((sysMicros + overflow_step) < cur_micros) {
+ sysMicros += overflow_step;
+ }
+ return sysMicros;
+}
+
+#endif // if FEATURE_PLUGIN_STATS
diff --git a/src/src/DataStructs/PluginStats_timestamp.h b/src/src/DataStructs/PluginStats_timestamp.h
new file mode 100644
index 0000000000..96e97457b9
--- /dev/null
+++ b/src/src/DataStructs/PluginStats_timestamp.h
@@ -0,0 +1,56 @@
+#ifndef HELPERS_PLUGINSTATS_TIMESTAMP_H
+#define HELPERS_PLUGINSTATS_TIMESTAMP_H
+
+#include "../../ESPEasy_common.h"
+
+#if FEATURE_PLUGIN_STATS
+
+# include "../DataStructs/PluginStats_size.h"
+
+// When using 'high res', the timestamps are stored internally
+// with 0.02 sec resolution. (1/50 sec) Default is 0.1 sec resolution
+// Stored timestamp will be based on the system micros
+// This also implies there might be overflow issues when the
+// full period exceeds (2^32 / 50) seconds (~ 1000 days)
+// or (2^32 / 10) seconds (~13.6 years)
+class PluginStats_timestamp {
+public:
+
+ typedef CircularBuffer PluginStatsTimestamps_t;
+
+ PluginStats_timestamp() = delete;
+
+ PluginStats_timestamp(bool useHighRes);
+ ~PluginStats_timestamp();
+
+ bool push(const int64_t& timestamp_sysmicros);
+
+ bool updateLast(const int64_t& timestamp_sysmicros);
+
+ void clear();
+
+ // Update any logged timestamp with this newly set system time.
+ void processTimeSet(const double& time_offset);
+
+ int64_t getTimestamp(int lastNrSamples) const;
+
+ // Compute the duration between first and last sample in seconds
+ // For 0 or 1 samples, the period will be 0 seconds.
+ uint32_t getFullPeriodInSec(uint32_t& time_frac) const;
+
+ int64_t operator[](PluginStatsTimestamps_t::index_t index) const;
+
+private:
+
+ // Conversion from system micros to internal timestamp
+ uint32_t systemMicros_to_internalTimestamp(const int64_t& timestamp_sysmicros) const;
+
+ // Conversion from internal timestamp to system micros
+ int64_t internalTimestamp_to_systemMicros(const uint32_t& internalTimestamp) const;
+
+ PluginStatsTimestamps_t _timestamps;
+ const uint32_t _internal_to_micros_ratio = 20000ul; // Default to 1/50 sec
+};
+
+#endif // if FEATURE_PLUGIN_STATS
+#endif // ifndef HELPERS_PLUGINSTATS_TIMESTAMP_H
diff --git a/src/src/DataStructs/PluginTaskData_base.cpp b/src/src/DataStructs/PluginTaskData_base.cpp
index 2a02a53bfc..28428100fd 100644
--- a/src/src/DataStructs/PluginTaskData_base.cpp
+++ b/src/src/DataStructs/PluginTaskData_base.cpp
@@ -56,7 +56,7 @@ size_t PluginTaskData_base::nrSamplesPresent() const {
}
#if FEATURE_PLUGIN_STATS
-void PluginTaskData_base::initPluginStats(taskVarIndex_t taskVarIndex)
+void PluginTaskData_base::initPluginStats(taskIndex_t taskIndex, taskVarIndex_t taskVarIndex)
{
if (taskVarIndex < VARS_PER_TASK) {
if (_plugin_stats_array == nullptr) {
@@ -64,7 +64,7 @@ void PluginTaskData_base::initPluginStats(taskVarIndex_t taskVarIndex)
}
if (_plugin_stats_array != nullptr) {
- _plugin_stats_array->initPluginStats(taskVarIndex);
+ _plugin_stats_array->initPluginStats(taskIndex, taskVarIndex);
}
}
}
@@ -81,15 +81,23 @@ void PluginTaskData_base::clearPluginStats(taskVarIndex_t taskVarIndex)
}
}
+void PluginTaskData_base::processTimeSet(const double& time_offset)
+{
+ if (_plugin_stats_array != nullptr) {
+ _plugin_stats_array->processTimeSet(time_offset);
+ }
+}
+
#endif // if FEATURE_PLUGIN_STATS
void PluginTaskData_base::pushPluginStatsValues(struct EventStruct *event,
- bool trackPeaks)
+ bool trackPeaks,
+ bool onlyUpdateTimestampWhenSame)
{
#if FEATURE_PLUGIN_STATS
if (_plugin_stats_array != nullptr) {
- _plugin_stats_array->pushPluginStatsValues(event, trackPeaks);
+ _plugin_stats_array->pushPluginStatsValues(event, trackPeaks, onlyUpdateTimestampWhenSame);
}
#endif // if FEATURE_PLUGIN_STATS
}
diff --git a/src/src/DataStructs/PluginTaskData_base.h b/src/src/DataStructs/PluginTaskData_base.h
index ba2da51bbd..f8fa2f2870 100644
--- a/src/src/DataStructs/PluginTaskData_base.h
+++ b/src/src/DataStructs/PluginTaskData_base.h
@@ -4,7 +4,7 @@
#include "../../ESPEasy_common.h"
-#include "../DataStructs/PluginStats.h"
+#include "../DataStructs/PluginStats_array.h"
#include "../DataTypes/PluginID.h"
#include "../DataTypes/TaskIndex.h"
@@ -25,20 +25,24 @@ struct PluginTaskData_base {
return _baseClassOnly;
}
- bool hasPluginStats() const;
+ bool hasPluginStats() const;
- bool hasPeaks() const;
+ bool hasPeaks() const;
size_t nrSamplesPresent() const;
#if FEATURE_PLUGIN_STATS
- void initPluginStats(taskVarIndex_t taskVarIndex);
- void clearPluginStats(taskVarIndex_t taskVarIndex);
+ void initPluginStats(taskIndex_t taskIndex, taskVarIndex_t taskVarIndex);
+ void clearPluginStats(taskVarIndex_t taskVarIndex);
+
+ // Update any logged timestamp with this newly set system time.
+ void processTimeSet(const double& time_offset);
#endif // if FEATURE_PLUGIN_STATS
// Called right after successful PLUGIN_READ to store task values
void pushPluginStatsValues(struct EventStruct *event,
- bool trackPeaks);
+ bool trackPeaks,
+ bool onlyUpdateTimestampWhenSame);
// Support task value notation to 'get' statistics
// Notations like [taskname#taskvalue.avg] can then be used to compute the average over a number of samples.
@@ -80,7 +84,7 @@ struct PluginTaskData_base {
PluginStats* getPluginStats(taskVarIndex_t taskVarIndex);
-private:
+protected:
// Array of pointers to PluginStats. One per task value.
PluginStats_array *_plugin_stats_array = nullptr;
diff --git a/src/src/DataStructs/SettingsStruct.h b/src/src/DataStructs/SettingsStruct.h
index 17d2550891..45fade5f86 100644
--- a/src/src/DataStructs/SettingsStruct.h
+++ b/src/src/DataStructs/SettingsStruct.h
@@ -1,577 +1,583 @@
-
-#ifndef DATASTRUCTS_SETTINGSSTRUCT_H
-#define DATASTRUCTS_SETTINGSSTRUCT_H
-
-#include "../../ESPEasy_common.h"
-
-#include "../CustomBuild/ESPEasyLimits.h"
-#include "../DataStructs/ChecksumType.h"
-#include "../DataStructs/DeviceStruct.h"
-#include "../DataTypes/EthernetParameters.h"
-#include "../DataTypes/NetworkMedium.h"
-#include "../DataTypes/NPluginID.h"
-#include "../DataTypes/PluginID.h"
-//#include "../DataTypes/TaskEnabledState.h"
-#include "../DataTypes/TimeSource.h"
-#include "../Globals/Plugins.h"
-
-#ifdef ESP32
-#include
-#endif
-
-//we disable SPI if not defined
-#ifndef DEFAULT_SPI
- #define DEFAULT_SPI 0
-#endif
-
-
-// FIXME TD-er: Move this PinBootState to DataTypes folder
-
-// State is stored, so don't change order
-enum class PinBootState {
- Default_state = 0,
- Output_low = 1,
- Output_high = 2,
- Input_pullup = 3,
- Input_pulldown = 4, // Only on ESP32 and GPIO16 on ESP82xx
- Input = 5,
-
- // Options for later:
- // ANALOG (only on ESP32)
- // WAKEUP_PULLUP (only on ESP8266)
- // WAKEUP_PULLDOWN (only on ESP8266)
- // SPECIAL
- // FUNCTION_0 (only on ESP8266)
- // FUNCTION_1
- // FUNCTION_2
- // FUNCTION_3
- // FUNCTION_4
- // FUNCTION_5 (only on ESP32)
- // FUNCTION_6 (only on ESP32)
-
-};
-
-
-
-
-/*********************************************************************************************\
- * SettingsStruct
-\*********************************************************************************************/
-template
-class SettingsStruct_tmpl
-{
- public:
-
-// SettingsStruct_tmpl() = default;
-
- // VariousBits1 defaults to 0, keep in mind when adding bit lookups.
- bool appendUnitToHostname() const { return !VariousBits_1.appendUnitToHostname; }
- void appendUnitToHostname(bool value) { VariousBits_1.appendUnitToHostname = !value;}
-
- bool uniqueMQTTclientIdReconnect_unused() const { return VariousBits_1.unused_02; }
- void uniqueMQTTclientIdReconnect_unused(bool value) { VariousBits_1.unused_02 = value; }
-
- bool OldRulesEngine() const {
-#ifdef WEBSERVER_NEW_RULES
- return !VariousBits_1.OldRulesEngine;
-#else
- return true;
-#endif
- }
- void OldRulesEngine(bool value) { VariousBits_1.OldRulesEngine = !value; }
-
- bool ForceWiFi_bg_mode() const { return VariousBits_1.ForceWiFi_bg_mode; }
- void ForceWiFi_bg_mode(bool value) { VariousBits_1.ForceWiFi_bg_mode = value; }
-
- bool WiFiRestart_connection_lost() const { return VariousBits_1.WiFiRestart_connection_lost; }
- void WiFiRestart_connection_lost(bool value) { VariousBits_1.WiFiRestart_connection_lost = value; }
-
- bool EcoPowerMode() const { return VariousBits_1.EcoPowerMode; }
- void EcoPowerMode(bool value) { VariousBits_1.EcoPowerMode = value; }
-
- bool WifiNoneSleep() const { return VariousBits_1.WifiNoneSleep; }
- void WifiNoneSleep(bool value) { VariousBits_1.WifiNoneSleep = value; }
-
- // Enable send gratuitous ARP by default, so invert the values (default = 0)
- bool gratuitousARP() const { return !VariousBits_1.gratuitousARP; }
- void gratuitousARP(bool value) { VariousBits_1.gratuitousARP = !value; }
-
- // Be a bit more tolerant when parsing the last argument of a command.
- // See: https://github.com/letscontrolit/ESPEasy/issues/2724
- bool TolerantLastArgParse() const { return VariousBits_1.TolerantLastArgParse; }
- void TolerantLastArgParse(bool value) { VariousBits_1.TolerantLastArgParse = value; }
-
- // SendToHttp command does not wait for ack, with this flag it does wait.
- bool SendToHttp_ack() const { return VariousBits_1.SendToHttp_ack; }
- void SendToHttp_ack(bool value) { VariousBits_1.SendToHttp_ack = value; }
-
- // Enable/disable ESPEasyNow protocol
- bool UseESPEasyNow() const {
-#ifdef USES_ESPEASY_NOW
- return VariousBits_1.UseESPEasyNow;
-#else
- return false;
-#endif
- }
- void UseESPEasyNow(bool value) {
-#ifdef USES_ESPEASY_NOW
- VariousBits_1.UseESPEasyNow = value;
-#endif
- }
-
- // Whether to try to connect to a hidden SSID network
- bool IncludeHiddenSSID() const { return VariousBits_1.IncludeHiddenSSID; }
- void IncludeHiddenSSID(bool value) { VariousBits_1.IncludeHiddenSSID = value; }
-
- // When sending, the TX power may be boosted to max TX power.
- bool UseMaxTXpowerForSending() const { return VariousBits_1.UseMaxTXpowerForSending; }
- void UseMaxTXpowerForSending(bool value) { VariousBits_1.UseMaxTXpowerForSending = value; }
-
- // When set you can use the Sensor in AP-Mode without beeing forced to /setup
- bool ApDontForceSetup() const { return VariousBits_1.ApDontForceSetup; }
- void ApDontForceSetup(bool value) { VariousBits_1.ApDontForceSetup = value; }
-
- // When outputting JSON bools use quoted values (on, backward compatible) or use official JSON true/false unquoted
- bool JSONBoolWithoutQuotes() const { return VariousBits_1.JSONBoolWithoutQuotes; }
- void JSONBoolWithoutQuotes(bool value) { VariousBits_1.JSONBoolWithoutQuotes = value; }
-
- // Enable timing statistics (may consume a few kB of RAM)
- bool EnableTimingStats() const { return VariousBits_1.EnableTimingStats; }
- void EnableTimingStats(bool value) { VariousBits_1.EnableTimingStats = value; }
-
- // Allow to actively reset I2C bus if it appears to be hanging.
- bool EnableClearHangingI2Cbus() const {
-#if FEATURE_CLEAR_I2C_STUCK
- return VariousBits_1.EnableClearHangingI2Cbus;
-#else
- return false;
-#endif
-}
- void EnableClearHangingI2Cbus(bool value) { VariousBits_1.EnableClearHangingI2Cbus = value; }
-
- // Enable RAM Tracking (may consume a few kB of RAM and cause some performance hit)
- bool EnableRAMTracking() const { return VariousBits_1.EnableRAMTracking; }
- void EnableRAMTracking(bool value) { VariousBits_1.EnableRAMTracking = value; }
-
- // Enable caching of rules, to speed up rules processing
- bool EnableRulesCaching() const { return !VariousBits_1.EnableRulesCaching; }
- void EnableRulesCaching(bool value) { VariousBits_1.EnableRulesCaching = !value; }
-
- // Allow the cached event entries to be sorted based on how frequent they occur.
- // This may speed up rules processing, especially on large rule sets with lots of rules blocks.
- bool EnableRulesEventReorder() const { return !VariousBits_1.EnableRulesEventReorder; }
- void EnableRulesEventReorder(bool value) { VariousBits_1.EnableRulesEventReorder = !value; }
-
- // Allow OTA to use 'unlimited' bin sized files, possibly overwriting the file-system, and trashing files
- // Can be used if the configuration is later retrieved/restored manually
- bool AllowOTAUnlimited() const { return VariousBits_1.AllowOTAUnlimited; }
- void AllowOTAUnlimited(bool value) { VariousBits_1.AllowOTAUnlimited = value; }
-
- // Default behavior is to not allow following redirects
- bool SendToHTTP_follow_redirects() const { return VariousBits_1.SendToHTTP_follow_redirects; }
- void SendToHTTP_follow_redirects(bool value) { VariousBits_1.SendToHTTP_follow_redirects = value; }
-
- #if FEATURE_I2C_DEVICE_CHECK
- // Check if an I2C device is found at configured address at plugin_INIT and plugin_READ
- bool CheckI2Cdevice() const { return !VariousBits_1.CheckI2Cdevice; }
- void CheckI2Cdevice(bool value) { VariousBits_1.CheckI2Cdevice = !value; }
- #endif // if FEATURE_I2C_DEVICE_CHECK
-
- // Wait for a second after calling WiFi.begin()
- // Especially useful for some FritzBox routers.
- bool WaitWiFiConnect() const { return VariousBits_2.WaitWiFiConnect; }
- void WaitWiFiConnect(bool value) { VariousBits_2.WaitWiFiConnect = value; }
-
- // Connect to Hidden SSID using channel and BSSID
- // This is much slower, but appears to be needed for some access points
- // like MikroTik.
- 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; }
-
- #if FEATURE_RULES_EASY_COLOR_CODE
- // Inhibit RulesCodeCompletion
- bool DisableRulesCodeCompletion() const { return VariousBits_2.DisableRulesCodeCompletion; }
- void DisableRulesCodeCompletion(bool value) { VariousBits_2.DisableRulesCodeCompletion = value; }
- #endif // if FEATURE_RULES_EASY_COLOR_CODE
-
- #if FEATURE_TARSTREAM_SUPPORT
- bool DisableSaveConfigAsTar() const { return VariousBits_2.DisableSaveConfigAsTar; }
- void DisableSaveConfigAsTar(bool value) { VariousBits_2.DisableSaveConfigAsTar = value; }
- #endif // if FEATURE_TARSTREAM_SUPPORT
-
- // Flag indicating whether all task values should be sent in a single event or one event per task value (default behavior)
- bool CombineTaskValues_SingleEvent(taskIndex_t taskIndex) const;
- void CombineTaskValues_SingleEvent(taskIndex_t taskIndex, bool value);
-
- bool DoNotStartAP() const { return VariousBits_1.DoNotStartAP; }
- void DoNotStartAP(bool value) { VariousBits_1.DoNotStartAP = value; }
-
- bool UseAlternativeDeepSleep() const { return VariousBits_1.UseAlternativeDeepSleep; }
- void UseAlternativeDeepSleep(bool value) { VariousBits_1.UseAlternativeDeepSleep = value; }
-
- bool UseLastWiFiFromRTC() const { return VariousBits_1.UseLastWiFiFromRTC; }
- void UseLastWiFiFromRTC(bool value) { VariousBits_1.UseLastWiFiFromRTC = value; }
-
- ExtTimeSource_e ExtTimeSource() const;
- void ExtTimeSource(ExtTimeSource_e value);
-
- bool UseNTP() const;
- void UseNTP(bool value);
-
- bool AllowTaskValueSetAllPlugins() const { return VariousBits_1.AllowTaskValueSetAllPlugins; }
- void AllowTaskValueSetAllPlugins(bool value) { VariousBits_1.AllowTaskValueSetAllPlugins = value; }
-
- #if FEATURE_AUTO_DARK_MODE
- uint8_t getCssMode() const { return VariousBits_1.CssMode; }
- void setCssMode(uint8_t value) { VariousBits_1.CssMode = value; }
- #endif // FEATURE_AUTO_DARK_MODE
-
- bool isTaskEnableReadonly(taskIndex_t taskIndex) const;
- void setTaskEnableReadonly(taskIndex_t taskIndex, bool value);
-
- #if FEATURE_PLUGIN_PRIORITY
- bool isPowerManagerTask(taskIndex_t taskIndex) const;
- void setPowerManagerTask(taskIndex_t taskIndex, bool value);
-
- bool isPriorityTask(taskIndex_t taskIndex) const;
- #endif // if FEATURE_PLUGIN_PRIORITY
-
- void validate();
-
- bool networkSettingsEmpty() const;
-
- void clearNetworkSettings();
-
- void clearTimeSettings();
-
- void clearNotifications();
-
- void clearControllers();
-
- void clearTasks();
-
- void clearLogSettings();
-
- void clearUnitNameSettings();
-
- void clearMisc();
-
- void clearTask(taskIndex_t task);
-
- // Return hostname + unit when selected to add unit.
- String getHostname() const;
-
- // Return hostname with explicit set append unit.
- String getHostname(bool appendUnit) const;
-
- // Return the name of the unit, without unitnr appended, with template parsing applied, replacement for Settings.Name in most places
- String getName() const;
-
-private:
-
- // Compute the index in either
- // - PinBootStates array (index_low) or
- // - PinBootStates_ESP32 (index_high)
- // Returns whether it is a valid index
- bool getPinBootStateIndex(
- int8_t gpio_pin,
- int8_t& index_low
- #ifdef ESP32
- , int8_t& index_high
- #endif
- ) const;
-
-public:
-
- PinBootState getPinBootState(int8_t gpio_pin) const;
- void setPinBootState(int8_t gpio_pin, PinBootState state);
-
- bool getSPI_pins(int8_t spi_gpios[3]) const;
-
- #ifdef ESP32
- spi_host_device_t getSPI_host() const;
- #endif
-
- // Return true when pin is one of the SPI pins and SPI is enabled
- bool isSPI_pin(int8_t pin) const;
-
- // Return true when SPI enabled and opt. user defined pins valid.
- bool isSPI_valid() const;
-
- // Return true when pin is one of the configured I2C pins.
- bool isI2C_pin(int8_t pin) const;
-
- // Return true if I2C settings are correct
- bool isI2CEnabled() const;
-
- // Return true when pin is one of the fixed Ethernet pins and Ethernet is enabled
- bool isEthernetPin(int8_t pin) const;
-
- // Return true when pin is one of the optional Ethernet pins and Ethernet is enabled
- bool isEthernetPinOptional(int8_t pin) const;
-
- // Access to TaskDevicePin1 ... TaskDevicePin3
- // @param pinnr 1 = TaskDevicePin1, ..., 3 = TaskDevicePin3
- int8_t getTaskDevicePin(taskIndex_t taskIndex, uint8_t pinnr) const;
-
- float getWiFi_TX_power() const;
- void setWiFi_TX_power(float dBm);
-
- pluginID_t getPluginID_for_task(taskIndex_t taskIndex) const;
-
- void forceSave() { memset(md5, 0, 16); }
-
- uint32_t getVariousBits1() const {
- uint32_t res;
- memcpy(&res, &VariousBits_1, sizeof(VariousBits_1));
- return res;
- }
-
- void setVariousBits1(uint32_t value) {
- memcpy(&VariousBits_1, &value, sizeof(VariousBits_1));
- }
-
- uint32_t getVariousBits2() const {
- uint32_t res;
- memcpy(&res, &VariousBits_2, sizeof(VariousBits_2));
- return res;
- }
-
- void setVariousBits2(uint32_t value) {
- memcpy(&VariousBits_2, &value, sizeof(VariousBits_2));
- }
-
-
- unsigned long PID = 0;
- int Version = 0;
- int16_t Build = 0;
- uint8_t IP[4] = {0};
- uint8_t Gateway[4] = {0};
- uint8_t Subnet[4] = {0};
- uint8_t DNS[4] = {0};
- uint8_t IP_Octet = 0;
- uint8_t Unit = 0;
- char Name[26] = {0};
- char NTPHost[64] = {0};
- // FIXME TD-er: Issue #2690
- unsigned long Delay = 0; // Sleep time in seconds
- int8_t Pin_i2c_sda = DEFAULT_PIN_I2C_SDA;
- int8_t Pin_i2c_scl = DEFAULT_PIN_I2C_SCL;
- int8_t Pin_status_led = DEFAULT_PIN_STATUS_LED;
- int8_t Pin_sd_cs = -1;
- int8_t PinBootStates[17] = {0}; // Only use getPinBootState and setPinBootState as multiple pins are packed for ESP32
- uint8_t Syslog_IP[4] = {0};
- unsigned int UDPPort = 8266;
- uint8_t SyslogLevel = 0;
- uint8_t SerialLogLevel = 0;
- uint8_t WebLogLevel = 0;
- uint8_t SDLogLevel = 0;
- unsigned long BaudRate = 115200;
- unsigned long MessageDelay_unused = 0; // MQTT settings now moved to the controller settings.
- uint8_t deepSleep_wakeTime = 0; // 0 = Sleep Disabled, else time awake from sleep in seconds
- boolean CustomCSS = false;
- boolean DST = false;
- uint8_t WDI2CAddress = 0;
- boolean UseRules = false;
- boolean UseSerial = false;
- boolean UseSSDP = false;
- uint8_t ExternalTimeSource = 0;
- unsigned long WireClockStretchLimit = 0;
- boolean GlobalSync = false;
- unsigned long ConnectionFailuresThreshold = 0;
- int16_t TimeZone = 0;
- boolean MQTTRetainFlag_unused = false;
- uint8_t InitSPI = 0; //0 = disabled, 1= enabled but for ESP32 there is option 2= SPI2 9 = User defined, see src/src/WebServer/HardwarePage.h enum SPI_Options_e
- // FIXME TD-er: Must change to cpluginID_t, but then also another check must be added since changing the pluginID_t will also render settings incompatible
- uint8_t Protocol[CONTROLLER_MAX] = {0};
- uint8_t Notification[NOTIFICATION_MAX] = {0}; //notifications, point to a NPLUGIN id
- // FIXME TD-er: Must change to pluginID_t, but then also another check must be added since changing the pluginID_t will also render settings incompatible
- uint8_t TaskDeviceNumber[N_TASKS] = {0}; // The "plugin number" set at as task (e.g. 4 for P004_dallas)
- unsigned int OLD_TaskDeviceID[N_TASKS] = {0}; //UNUSED: this can be reused
-
- // FIXME TD-er: When used on ESP8266, this conversion union may not work
- // It might work as it is 32-bit in size.
- union {
- struct {
- int8_t TaskDevicePin1[N_TASKS];
- int8_t TaskDevicePin2[N_TASKS];
- int8_t TaskDevicePin3[N_TASKS];
- uint8_t TaskDevicePort[N_TASKS];
- };
- int8_t TaskDevicePin[4][N_TASKS]{};
- };
- boolean TaskDevicePin1PullUp[N_TASKS] = {0};
- int16_t TaskDevicePluginConfig[N_TASKS][PLUGIN_CONFIGVAR_MAX]{};
- boolean TaskDevicePin1Inversed[N_TASKS] = {0};
- float TaskDevicePluginConfigFloat[N_TASKS][PLUGIN_CONFIGFLOATVAR_MAX]{};
-
- // FIXME TD-er: When used on ESP8266, this conversion union may not work
- // It might work as it is 32-bit in size.
- union {
- int32_t TaskDevicePluginConfigLong[N_TASKS][PLUGIN_CONFIGLONGVAR_MAX];
- uint32_t TaskDevicePluginConfigULong[N_TASKS][PLUGIN_CONFIGLONGVAR_MAX]{};
- };
- uint8_t TaskDeviceSendDataFlags[N_TASKS] = {0};
- uint8_t VariousTaskBits[N_TASKS] = {0};
- uint8_t TaskDeviceDataFeed[N_TASKS] = {0}; // When set to 0, only read local connected sensorsfeeds
- unsigned long TaskDeviceTimer[N_TASKS] = {0};
- boolean TaskDeviceEnabled[N_TASKS] = {0};
- boolean ControllerEnabled[CONTROLLER_MAX] = {0};
- boolean NotificationEnabled[NOTIFICATION_MAX] = {0};
- unsigned int TaskDeviceID[CONTROLLER_MAX][N_TASKS]{}; // IDX number (mainly used by Domoticz)
- boolean TaskDeviceSendData[CONTROLLER_MAX][N_TASKS]{};
- boolean Pin_status_led_Inversed = false;
- boolean deepSleepOnFail = false;
- boolean UseValueLogger = false;
- boolean ArduinoOTAEnable = false;
- uint16_t DST_Start = 0;
- uint16_t DST_End = 0;
- boolean UseRTOSMultitasking = false;
- int8_t Pin_Reset = -1;
- uint8_t SyslogFacility = 0;
- uint32_t StructSize = 0; // Forced to be 32 bit, to make sure alignment is clear.
- boolean MQTTUseUnitNameAsClientId_unused = false;
-
- //its safe to extend this struct, up to several bytes, default values in config are 0
- //look in misc.ino how config.dat is used because also other stuff is stored in it at different offsets.
- //TODO: document config.dat somewhere here
- float Latitude = 0.0f;
- float Longitude = 0.0f;
-
- // VariousBits_1 defaults to 0, keep in mind when adding bit lookups.
- struct {
- uint32_t unused_00 : 1; // Bit 00
- uint32_t appendUnitToHostname : 1; // Bit 01 Inverted
- uint32_t unused_02 : 1; // Bit 02 uniqueMQTTclientIdReconnect_unused
- uint32_t OldRulesEngine : 1; // Bit 03 Inverted
- uint32_t ForceWiFi_bg_mode : 1; // Bit 04
- uint32_t WiFiRestart_connection_lost : 1; // Bit 05
- uint32_t EcoPowerMode : 1; // Bit 06
- uint32_t WifiNoneSleep : 1; // Bit 07
- uint32_t gratuitousARP : 1; // Bit 08 Inverted
- uint32_t TolerantLastArgParse : 1; // Bit 09
- uint32_t SendToHttp_ack : 1; // Bit 10
- uint32_t UseESPEasyNow : 1; // Bit 11
- uint32_t IncludeHiddenSSID : 1; // Bit 12
- uint32_t UseMaxTXpowerForSending : 1; // Bit 13
- uint32_t ApDontForceSetup : 1; // Bit 14
- uint32_t unused_15 : 1; // Bit 15 was used by PeriodicalScanWiFi
- uint32_t JSONBoolWithoutQuotes : 1; // Bit 16
- uint32_t DoNotStartAP : 1; // Bit 17
- uint32_t UseAlternativeDeepSleep : 1; // Bit 18
- uint32_t UseLastWiFiFromRTC : 1; // Bit 19
- uint32_t EnableTimingStats : 1; // Bit 20
- uint32_t AllowTaskValueSetAllPlugins : 1; // Bit 21
- uint32_t EnableClearHangingI2Cbus : 1; // Bit 22
- uint32_t EnableRAMTracking : 1; // Bit 23
- uint32_t EnableRulesCaching : 1; // Bit 24 Inverted
- uint32_t EnableRulesEventReorder : 1; // Bit 25 Inverted
- uint32_t AllowOTAUnlimited : 1; // Bit 26
- uint32_t SendToHTTP_follow_redirects : 1; // Bit 27
- uint32_t CssMode : 2; // Bit 28
-// uint32_t unused_29 : 1; // Bit 29
- uint32_t CheckI2Cdevice : 1; // Bit 30 Inverted
- uint32_t DoNotUse_31 : 1; // Bit 31 Was used to detect whether various bits were even set
-
- } VariousBits_1;
-
- uint32_t ResetFactoryDefaultPreference = 0; // Do not clear this one in the clearAll()
- uint32_t I2C_clockSpeed = 400000;
- uint16_t WebserverPort = 80;
- uint16_t SyslogPort = DEFAULT_SYSLOG_PORT;
-
- int8_t ETH_Phy_Addr = -1;
- int8_t ETH_Pin_mdc_cs = -1;
- int8_t ETH_Pin_mdio_irq = -1;
- int8_t ETH_Pin_power_rst = -1;
- EthPhyType_t ETH_Phy_Type = EthPhyType_t::notSet;
- EthClockMode_t ETH_Clock_Mode = EthClockMode_t::Ext_crystal_osc;
- uint8_t ETH_IP[4] = {0};
- uint8_t ETH_Gateway[4] = {0};
- uint8_t ETH_Subnet[4] = {0};
- uint8_t ETH_DNS[4] = {0};
- NetworkMedium_t NetworkMedium = NetworkMedium_t::WIFI;
- int8_t I2C_Multiplexer_Type = I2C_MULTIPLEXER_NONE;
- int8_t I2C_Multiplexer_Addr = -1;
- int8_t I2C_Multiplexer_Channel[N_TASKS]{};
- uint8_t I2C_Flags[N_TASKS] = {0};
- uint32_t I2C_clockSpeed_Slow = 100000;
- int8_t I2C_Multiplexer_ResetPin = -1;
-
- #ifdef ESP32
- int8_t PinBootStates_ESP32[24] = {0}; // pins 17 ... 39
- #endif
- uint8_t WiFi_TX_power = 70; // 70 = 17.5dBm. unit: 0.25 dBm
- int8_t WiFi_sensitivity_margin = 3; // Margin in dBm on top of sensitivity.
- uint8_t NumberExtraWiFiScans = 0;
- int8_t SPI_SCLK_pin = -1;
- int8_t SPI_MISO_pin = -1;
- int8_t SPI_MOSI_pin = -1;
- int8_t ForceESPEasyNOWchannel = 0;
-
- // Do not rename or move this checksum.
- // Checksum calculation will work "around" this
- uint8_t md5[16]{}; // Store checksum of the settings.
-
- // VariousBits_2 defaults to 0, keep in mind when adding bit lookups.
- struct {
- uint32_t WaitWiFiConnect : 1; // Bit 00
- uint32_t SDK_WiFi_autoreconnect : 1; // Bit 01
- uint32_t DisableRulesCodeCompletion : 1; // Bit 02
- uint32_t HiddenSSID_SlowConnectPerBSSID : 1; // Bit 03 // inverted
- 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
- uint32_t unused_09 : 1; // Bit 09
- uint32_t unused_10 : 1; // Bit 10
- uint32_t unused_11 : 1; // Bit 11
- uint32_t unused_12 : 1; // Bit 12
- uint32_t unused_13 : 1; // Bit 13
- uint32_t unused_14 : 1; // Bit 14
- uint32_t unused_15 : 1; // Bit 15
- uint32_t unused_16 : 1; // Bit 16
- uint32_t unused_17 : 1; // Bit 17
- uint32_t unused_18 : 1; // Bit 18
- uint32_t unused_19 : 1; // Bit 19
- uint32_t unused_20 : 1; // Bit 20
- uint32_t unused_21 : 1; // Bit 21
- uint32_t unused_22 : 1; // Bit 22
- uint32_t unused_23 : 1; // Bit 23
- uint32_t unused_24 : 1; // Bit 24
- uint32_t unused_25 : 1; // Bit 25
- uint32_t unused_26 : 1; // Bit 26
- uint32_t unused_27 : 1; // Bit 27
- uint32_t unused_28 : 1; // Bit 28
- uint32_t unused_29 : 1; // Bit 29
- uint32_t unused_30 : 1; // Bit 30
- uint32_t unused_31 : 1; // Bit 31
-
- } VariousBits_2;
-
- uint8_t console_serial_port = DEFAULT_CONSOLE_PORT;
- int8_t console_serial_rxpin = DEFAULT_CONSOLE_PORT_RXPIN;
- int8_t console_serial_txpin = DEFAULT_CONSOLE_PORT_TXPIN;
- uint8_t console_serial0_fallback = DEFAULT_CONSOLE_SER0_FALLBACK;
-
- // Try to extend settings to make the checksum 4-uint8_t aligned.
-};
-
-/*
-SettingsStruct* SettingsStruct_ptr = new (std::nothrow) SettingsStruct;
-SettingsStruct& Settings = *SettingsStruct_ptr;
-*/
-
-
-
-typedef SettingsStruct_tmpl SettingsStruct;
-
-#endif // DATASTRUCTS_SETTINGSSTRUCT_H
+
+#ifndef DATASTRUCTS_SETTINGSSTRUCT_H
+#define DATASTRUCTS_SETTINGSSTRUCT_H
+
+#include "../../ESPEasy_common.h"
+
+#include "../CustomBuild/ESPEasyLimits.h"
+#include "../DataStructs/ChecksumType.h"
+#include "../DataStructs/DeviceStruct.h"
+#include "../DataTypes/EthernetParameters.h"
+#include "../DataTypes/NetworkMedium.h"
+#include "../DataTypes/NPluginID.h"
+#include "../DataTypes/PluginID.h"
+//#include "../DataTypes/TaskEnabledState.h"
+#include "../DataTypes/TimeSource.h"
+#include "../Globals/Plugins.h"
+
+#ifdef ESP32
+#include
+#endif
+
+//we disable SPI if not defined
+#ifndef DEFAULT_SPI
+ #define DEFAULT_SPI 0
+#endif
+
+
+// FIXME TD-er: Move this PinBootState to DataTypes folder
+
+// State is stored, so don't change order
+enum class PinBootState {
+ Default_state = 0,
+ Output_low = 1,
+ Output_high = 2,
+ Input_pullup = 3,
+ Input_pulldown = 4, // Only on ESP32 and GPIO16 on ESP82xx
+ Input = 5,
+
+ // Options for later:
+ // ANALOG (only on ESP32)
+ // WAKEUP_PULLUP (only on ESP8266)
+ // WAKEUP_PULLDOWN (only on ESP8266)
+ // SPECIAL
+ // FUNCTION_0 (only on ESP8266)
+ // FUNCTION_1
+ // FUNCTION_2
+ // FUNCTION_3
+ // FUNCTION_4
+ // FUNCTION_5 (only on ESP32)
+ // FUNCTION_6 (only on ESP32)
+
+};
+
+
+
+
+/*********************************************************************************************\
+ * SettingsStruct
+\*********************************************************************************************/
+template
+class SettingsStruct_tmpl
+{
+ public:
+
+// SettingsStruct_tmpl() = default;
+
+ // VariousBits1 defaults to 0, keep in mind when adding bit lookups.
+ bool appendUnitToHostname() const { return !VariousBits_1.appendUnitToHostname; }
+ void appendUnitToHostname(bool value) { VariousBits_1.appendUnitToHostname = !value;}
+
+ bool uniqueMQTTclientIdReconnect_unused() const { return VariousBits_1.unused_02; }
+ void uniqueMQTTclientIdReconnect_unused(bool value) { VariousBits_1.unused_02 = value; }
+
+ bool OldRulesEngine() const {
+#ifdef WEBSERVER_NEW_RULES
+ return !VariousBits_1.OldRulesEngine;
+#else
+ return true;
+#endif
+ }
+ void OldRulesEngine(bool value) { VariousBits_1.OldRulesEngine = !value; }
+
+ bool ForceWiFi_bg_mode() const { return VariousBits_1.ForceWiFi_bg_mode; }
+ void ForceWiFi_bg_mode(bool value) { VariousBits_1.ForceWiFi_bg_mode = value; }
+
+ bool WiFiRestart_connection_lost() const { return VariousBits_1.WiFiRestart_connection_lost; }
+ void WiFiRestart_connection_lost(bool value) { VariousBits_1.WiFiRestart_connection_lost = value; }
+
+ bool EcoPowerMode() const { return VariousBits_1.EcoPowerMode; }
+ void EcoPowerMode(bool value) { VariousBits_1.EcoPowerMode = value; }
+
+ bool WifiNoneSleep() const { return VariousBits_1.WifiNoneSleep; }
+ void WifiNoneSleep(bool value) { VariousBits_1.WifiNoneSleep = value; }
+
+ // Enable send gratuitous ARP by default, so invert the values (default = 0)
+ bool gratuitousARP() const { return !VariousBits_1.gratuitousARP; }
+ void gratuitousARP(bool value) { VariousBits_1.gratuitousARP = !value; }
+
+ // Be a bit more tolerant when parsing the last argument of a command.
+ // See: https://github.com/letscontrolit/ESPEasy/issues/2724
+ bool TolerantLastArgParse() const { return VariousBits_1.TolerantLastArgParse; }
+ void TolerantLastArgParse(bool value) { VariousBits_1.TolerantLastArgParse = value; }
+
+ // SendToHttp command does not wait for ack, with this flag it does wait.
+ bool SendToHttp_ack() const { return VariousBits_1.SendToHttp_ack; }
+ void SendToHttp_ack(bool value) { VariousBits_1.SendToHttp_ack = value; }
+
+ // Enable/disable ESPEasyNow protocol
+ bool UseESPEasyNow() const {
+#ifdef USES_ESPEASY_NOW
+ return VariousBits_1.UseESPEasyNow;
+#else
+ return false;
+#endif
+ }
+ void UseESPEasyNow(bool value) {
+#ifdef USES_ESPEASY_NOW
+ VariousBits_1.UseESPEasyNow = value;
+#endif
+ }
+
+ // Whether to try to connect to a hidden SSID network
+ bool IncludeHiddenSSID() const { return VariousBits_1.IncludeHiddenSSID; }
+ void IncludeHiddenSSID(bool value) { VariousBits_1.IncludeHiddenSSID = value; }
+
+ // When sending, the TX power may be boosted to max TX power.
+ bool UseMaxTXpowerForSending() const { return VariousBits_1.UseMaxTXpowerForSending; }
+ void UseMaxTXpowerForSending(bool value) { VariousBits_1.UseMaxTXpowerForSending = value; }
+
+ // When set you can use the Sensor in AP-Mode without beeing forced to /setup
+ bool ApDontForceSetup() const { return VariousBits_1.ApDontForceSetup; }
+ void ApDontForceSetup(bool value) { VariousBits_1.ApDontForceSetup = value; }
+
+ // When outputting JSON bools use quoted values (on, backward compatible) or use official JSON true/false unquoted
+ bool JSONBoolWithoutQuotes() const { return VariousBits_1.JSONBoolWithoutQuotes; }
+ void JSONBoolWithoutQuotes(bool value) { VariousBits_1.JSONBoolWithoutQuotes = value; }
+
+ // Enable timing statistics (may consume a few kB of RAM)
+ bool EnableTimingStats() const { return VariousBits_1.EnableTimingStats; }
+ void EnableTimingStats(bool value) { VariousBits_1.EnableTimingStats = value; }
+
+ // Allow to actively reset I2C bus if it appears to be hanging.
+ bool EnableClearHangingI2Cbus() const {
+#if FEATURE_CLEAR_I2C_STUCK
+ return VariousBits_1.EnableClearHangingI2Cbus;
+#else
+ return false;
+#endif
+}
+ void EnableClearHangingI2Cbus(bool value) { VariousBits_1.EnableClearHangingI2Cbus = value; }
+
+ // Enable RAM Tracking (may consume a few kB of RAM and cause some performance hit)
+ bool EnableRAMTracking() const { return VariousBits_1.EnableRAMTracking; }
+ void EnableRAMTracking(bool value) { VariousBits_1.EnableRAMTracking = value; }
+
+ // Enable caching of rules, to speed up rules processing
+ bool EnableRulesCaching() const { return !VariousBits_1.EnableRulesCaching; }
+ void EnableRulesCaching(bool value) { VariousBits_1.EnableRulesCaching = !value; }
+
+ // Allow the cached event entries to be sorted based on how frequent they occur.
+ // This may speed up rules processing, especially on large rule sets with lots of rules blocks.
+ bool EnableRulesEventReorder() const { return !VariousBits_1.EnableRulesEventReorder; }
+ void EnableRulesEventReorder(bool value) { VariousBits_1.EnableRulesEventReorder = !value; }
+
+ // Allow OTA to use 'unlimited' bin sized files, possibly overwriting the file-system, and trashing files
+ // Can be used if the configuration is later retrieved/restored manually
+ bool AllowOTAUnlimited() const { return VariousBits_1.AllowOTAUnlimited; }
+ void AllowOTAUnlimited(bool value) { VariousBits_1.AllowOTAUnlimited = value; }
+
+ // Default behavior is to not allow following redirects
+ bool SendToHTTP_follow_redirects() const { return VariousBits_1.SendToHTTP_follow_redirects; }
+ void SendToHTTP_follow_redirects(bool value) { VariousBits_1.SendToHTTP_follow_redirects = value; }
+
+ #if FEATURE_I2C_DEVICE_CHECK
+ // Check if an I2C device is found at configured address at plugin_INIT and plugin_READ
+ bool CheckI2Cdevice() const { return !VariousBits_1.CheckI2Cdevice; }
+ void CheckI2Cdevice(bool value) { VariousBits_1.CheckI2Cdevice = !value; }
+ #endif // if FEATURE_I2C_DEVICE_CHECK
+
+ // Wait for a second after calling WiFi.begin()
+ // Especially useful for some FritzBox routers.
+ bool WaitWiFiConnect() const { return VariousBits_2.WaitWiFiConnect; }
+ void WaitWiFiConnect(bool value) { VariousBits_2.WaitWiFiConnect = value; }
+
+#ifdef ESP32
+ // Toggle between passive/active WiFi scan.
+ bool PassiveWiFiScan() const { return !VariousBits_2.PassiveWiFiScan; }
+ void PassiveWiFiScan(bool value) { VariousBits_2.PassiveWiFiScan = !value; }
+#endif
+
+ // Connect to Hidden SSID using channel and BSSID
+ // This is much slower, but appears to be needed for some access points
+ // like MikroTik.
+ 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; }
+
+ #if FEATURE_RULES_EASY_COLOR_CODE
+ // Inhibit RulesCodeCompletion
+ bool DisableRulesCodeCompletion() const { return VariousBits_2.DisableRulesCodeCompletion; }
+ void DisableRulesCodeCompletion(bool value) { VariousBits_2.DisableRulesCodeCompletion = value; }
+ #endif // if FEATURE_RULES_EASY_COLOR_CODE
+
+ #if FEATURE_TARSTREAM_SUPPORT
+ bool DisableSaveConfigAsTar() const { return VariousBits_2.DisableSaveConfigAsTar; }
+ void DisableSaveConfigAsTar(bool value) { VariousBits_2.DisableSaveConfigAsTar = value; }
+ #endif // if FEATURE_TARSTREAM_SUPPORT
+
+ // Flag indicating whether all task values should be sent in a single event or one event per task value (default behavior)
+ bool CombineTaskValues_SingleEvent(taskIndex_t taskIndex) const;
+ void CombineTaskValues_SingleEvent(taskIndex_t taskIndex, bool value);
+
+ bool DoNotStartAP() const { return VariousBits_1.DoNotStartAP; }
+ void DoNotStartAP(bool value) { VariousBits_1.DoNotStartAP = value; }
+
+ bool UseAlternativeDeepSleep() const { return VariousBits_1.UseAlternativeDeepSleep; }
+ void UseAlternativeDeepSleep(bool value) { VariousBits_1.UseAlternativeDeepSleep = value; }
+
+ bool UseLastWiFiFromRTC() const { return VariousBits_1.UseLastWiFiFromRTC; }
+ void UseLastWiFiFromRTC(bool value) { VariousBits_1.UseLastWiFiFromRTC = value; }
+
+ ExtTimeSource_e ExtTimeSource() const;
+ void ExtTimeSource(ExtTimeSource_e value);
+
+ bool UseNTP() const;
+ void UseNTP(bool value);
+
+ bool AllowTaskValueSetAllPlugins() const { return VariousBits_1.AllowTaskValueSetAllPlugins; }
+ void AllowTaskValueSetAllPlugins(bool value) { VariousBits_1.AllowTaskValueSetAllPlugins = value; }
+
+ #if FEATURE_AUTO_DARK_MODE
+ uint8_t getCssMode() const { return VariousBits_1.CssMode; }
+ void setCssMode(uint8_t value) { VariousBits_1.CssMode = value; }
+ #endif // FEATURE_AUTO_DARK_MODE
+
+ bool isTaskEnableReadonly(taskIndex_t taskIndex) const;
+ void setTaskEnableReadonly(taskIndex_t taskIndex, bool value);
+
+ #if FEATURE_PLUGIN_PRIORITY
+ bool isPowerManagerTask(taskIndex_t taskIndex) const;
+ void setPowerManagerTask(taskIndex_t taskIndex, bool value);
+
+ bool isPriorityTask(taskIndex_t taskIndex) const;
+ #endif // if FEATURE_PLUGIN_PRIORITY
+
+ void validate();
+
+ bool networkSettingsEmpty() const;
+
+ void clearNetworkSettings();
+
+ void clearTimeSettings();
+
+ void clearNotifications();
+
+ void clearControllers();
+
+ void clearTasks();
+
+ void clearLogSettings();
+
+ void clearUnitNameSettings();
+
+ void clearMisc();
+
+ void clearTask(taskIndex_t task);
+
+ // Return hostname + unit when selected to add unit.
+ String getHostname() const;
+
+ // Return hostname with explicit set append unit.
+ String getHostname(bool appendUnit) const;
+
+ // Return the name of the unit, without unitnr appended, with template parsing applied, replacement for Settings.Name in most places
+ String getName() const;
+
+private:
+
+ // Compute the index in either
+ // - PinBootStates array (index_low) or
+ // - PinBootStates_ESP32 (index_high)
+ // Returns whether it is a valid index
+ bool getPinBootStateIndex(
+ int8_t gpio_pin,
+ int8_t& index_low
+ #ifdef ESP32
+ , int8_t& index_high
+ #endif
+ ) const;
+
+public:
+
+ PinBootState getPinBootState(int8_t gpio_pin) const;
+ void setPinBootState(int8_t gpio_pin, PinBootState state);
+
+ bool getSPI_pins(int8_t spi_gpios[3]) const;
+
+ #ifdef ESP32
+ spi_host_device_t getSPI_host() const;
+ #endif
+
+ // Return true when pin is one of the SPI pins and SPI is enabled
+ bool isSPI_pin(int8_t pin) const;
+
+ // Return true when SPI enabled and opt. user defined pins valid.
+ bool isSPI_valid() const;
+
+ // Return true when pin is one of the configured I2C pins.
+ bool isI2C_pin(int8_t pin) const;
+
+ // Return true if I2C settings are correct
+ bool isI2CEnabled() const;
+
+ // Return true when pin is one of the fixed Ethernet pins and Ethernet is enabled
+ bool isEthernetPin(int8_t pin) const;
+
+ // Return true when pin is one of the optional Ethernet pins and Ethernet is enabled
+ bool isEthernetPinOptional(int8_t pin) const;
+
+ // Access to TaskDevicePin1 ... TaskDevicePin3
+ // @param pinnr 1 = TaskDevicePin1, ..., 3 = TaskDevicePin3
+ int8_t getTaskDevicePin(taskIndex_t taskIndex, uint8_t pinnr) const;
+
+ float getWiFi_TX_power() const;
+ void setWiFi_TX_power(float dBm);
+
+ pluginID_t getPluginID_for_task(taskIndex_t taskIndex) const;
+
+ void forceSave() { memset(md5, 0, 16); }
+
+ uint32_t getVariousBits1() const {
+ uint32_t res;
+ memcpy(&res, &VariousBits_1, sizeof(VariousBits_1));
+ return res;
+ }
+
+ void setVariousBits1(uint32_t value) {
+ memcpy(&VariousBits_1, &value, sizeof(VariousBits_1));
+ }
+
+ uint32_t getVariousBits2() const {
+ uint32_t res;
+ memcpy(&res, &VariousBits_2, sizeof(VariousBits_2));
+ return res;
+ }
+
+ void setVariousBits2(uint32_t value) {
+ memcpy(&VariousBits_2, &value, sizeof(VariousBits_2));
+ }
+
+
+ unsigned long PID = 0;
+ int Version = 0;
+ int16_t Build = 0;
+ uint8_t IP[4] = {0};
+ uint8_t Gateway[4] = {0};
+ uint8_t Subnet[4] = {0};
+ uint8_t DNS[4] = {0};
+ uint8_t IP_Octet = 0;
+ uint8_t Unit = 0;
+ char Name[26] = {0};
+ char NTPHost[64] = {0};
+ // FIXME TD-er: Issue #2690
+ unsigned long Delay = 0; // Sleep time in seconds
+ int8_t Pin_i2c_sda = DEFAULT_PIN_I2C_SDA;
+ int8_t Pin_i2c_scl = DEFAULT_PIN_I2C_SCL;
+ int8_t Pin_status_led = DEFAULT_PIN_STATUS_LED;
+ int8_t Pin_sd_cs = -1;
+ int8_t PinBootStates[17] = {0}; // Only use getPinBootState and setPinBootState as multiple pins are packed for ESP32
+ uint8_t Syslog_IP[4] = {0};
+ unsigned int UDPPort = 8266;
+ uint8_t SyslogLevel = 0;
+ uint8_t SerialLogLevel = 0;
+ uint8_t WebLogLevel = 0;
+ uint8_t SDLogLevel = 0;
+ unsigned long BaudRate = 115200;
+ unsigned long MessageDelay_unused = 0; // MQTT settings now moved to the controller settings.
+ uint8_t deepSleep_wakeTime = 0; // 0 = Sleep Disabled, else time awake from sleep in seconds
+ boolean CustomCSS = false;
+ boolean DST = false;
+ uint8_t WDI2CAddress = 0;
+ boolean UseRules = false;
+ boolean UseSerial = false;
+ boolean UseSSDP = false;
+ uint8_t ExternalTimeSource = 0;
+ unsigned long WireClockStretchLimit = 0;
+ boolean GlobalSync = false;
+ unsigned long ConnectionFailuresThreshold = 0;
+ int16_t TimeZone = 0;
+ boolean MQTTRetainFlag_unused = false;
+ uint8_t InitSPI = 0; //0 = disabled, 1= enabled but for ESP32 there is option 2= SPI2 9 = User defined, see src/src/WebServer/HardwarePage.h enum SPI_Options_e
+ // FIXME TD-er: Must change to cpluginID_t, but then also another check must be added since changing the pluginID_t will also render settings incompatible
+ uint8_t Protocol[CONTROLLER_MAX] = {0};
+ uint8_t Notification[NOTIFICATION_MAX] = {0}; //notifications, point to a NPLUGIN id
+ // FIXME TD-er: Must change to pluginID_t, but then also another check must be added since changing the pluginID_t will also render settings incompatible
+ uint8_t TaskDeviceNumber[N_TASKS] = {0}; // The "plugin number" set at as task (e.g. 4 for P004_dallas)
+ unsigned int OLD_TaskDeviceID[N_TASKS] = {0}; //UNUSED: this can be reused
+
+ // FIXME TD-er: When used on ESP8266, this conversion union may not work
+ // It might work as it is 32-bit in size.
+ union {
+ struct {
+ int8_t TaskDevicePin1[N_TASKS];
+ int8_t TaskDevicePin2[N_TASKS];
+ int8_t TaskDevicePin3[N_TASKS];
+ uint8_t TaskDevicePort[N_TASKS];
+ };
+ int8_t TaskDevicePin[4][N_TASKS]{};
+ };
+ boolean TaskDevicePin1PullUp[N_TASKS] = {0};
+ int16_t TaskDevicePluginConfig[N_TASKS][PLUGIN_CONFIGVAR_MAX]{};
+ boolean TaskDevicePin1Inversed[N_TASKS] = {0};
+ float TaskDevicePluginConfigFloat[N_TASKS][PLUGIN_CONFIGFLOATVAR_MAX]{};
+
+ // FIXME TD-er: When used on ESP8266, this conversion union may not work
+ // It might work as it is 32-bit in size.
+ union {
+ int32_t TaskDevicePluginConfigLong[N_TASKS][PLUGIN_CONFIGLONGVAR_MAX];
+ uint32_t TaskDevicePluginConfigULong[N_TASKS][PLUGIN_CONFIGLONGVAR_MAX]{};
+ };
+ uint8_t TaskDeviceSendDataFlags[N_TASKS] = {0};
+ uint8_t VariousTaskBits[N_TASKS] = {0};
+ uint8_t TaskDeviceDataFeed[N_TASKS] = {0}; // When set to 0, only read local connected sensorsfeeds
+ unsigned long TaskDeviceTimer[N_TASKS] = {0};
+ boolean TaskDeviceEnabled[N_TASKS] = {0};
+ boolean ControllerEnabled[CONTROLLER_MAX] = {0};
+ boolean NotificationEnabled[NOTIFICATION_MAX] = {0};
+ unsigned int TaskDeviceID[CONTROLLER_MAX][N_TASKS]{}; // IDX number (mainly used by Domoticz)
+ boolean TaskDeviceSendData[CONTROLLER_MAX][N_TASKS]{};
+ boolean Pin_status_led_Inversed = false;
+ boolean deepSleepOnFail = false;
+ boolean UseValueLogger = false;
+ boolean ArduinoOTAEnable = false;
+ uint16_t DST_Start = 0;
+ uint16_t DST_End = 0;
+ boolean UseRTOSMultitasking = false;
+ int8_t Pin_Reset = -1;
+ uint8_t SyslogFacility = 0;
+ uint32_t StructSize = 0; // Forced to be 32 bit, to make sure alignment is clear.
+ boolean MQTTUseUnitNameAsClientId_unused = false;
+
+ //its safe to extend this struct, up to several bytes, default values in config are 0
+ //look in misc.ino how config.dat is used because also other stuff is stored in it at different offsets.
+ //TODO: document config.dat somewhere here
+ float Latitude = 0.0f;
+ float Longitude = 0.0f;
+
+ // VariousBits_1 defaults to 0, keep in mind when adding bit lookups.
+ struct {
+ uint32_t unused_00 : 1; // Bit 00
+ uint32_t appendUnitToHostname : 1; // Bit 01 Inverted
+ uint32_t unused_02 : 1; // Bit 02 uniqueMQTTclientIdReconnect_unused
+ uint32_t OldRulesEngine : 1; // Bit 03 Inverted
+ uint32_t ForceWiFi_bg_mode : 1; // Bit 04
+ uint32_t WiFiRestart_connection_lost : 1; // Bit 05
+ uint32_t EcoPowerMode : 1; // Bit 06
+ uint32_t WifiNoneSleep : 1; // Bit 07
+ uint32_t gratuitousARP : 1; // Bit 08 Inverted
+ uint32_t TolerantLastArgParse : 1; // Bit 09
+ uint32_t SendToHttp_ack : 1; // Bit 10
+ uint32_t UseESPEasyNow : 1; // Bit 11
+ uint32_t IncludeHiddenSSID : 1; // Bit 12
+ uint32_t UseMaxTXpowerForSending : 1; // Bit 13
+ uint32_t ApDontForceSetup : 1; // Bit 14
+ uint32_t unused_15 : 1; // Bit 15 was used by PeriodicalScanWiFi
+ uint32_t JSONBoolWithoutQuotes : 1; // Bit 16
+ uint32_t DoNotStartAP : 1; // Bit 17
+ uint32_t UseAlternativeDeepSleep : 1; // Bit 18
+ uint32_t UseLastWiFiFromRTC : 1; // Bit 19
+ uint32_t EnableTimingStats : 1; // Bit 20
+ uint32_t AllowTaskValueSetAllPlugins : 1; // Bit 21
+ uint32_t EnableClearHangingI2Cbus : 1; // Bit 22
+ uint32_t EnableRAMTracking : 1; // Bit 23
+ uint32_t EnableRulesCaching : 1; // Bit 24 Inverted
+ uint32_t EnableRulesEventReorder : 1; // Bit 25 Inverted
+ uint32_t AllowOTAUnlimited : 1; // Bit 26
+ uint32_t SendToHTTP_follow_redirects : 1; // Bit 27
+ uint32_t CssMode : 2; // Bit 28
+// uint32_t unused_29 : 1; // Bit 29
+ uint32_t CheckI2Cdevice : 1; // Bit 30 Inverted
+ uint32_t DoNotUse_31 : 1; // Bit 31 Was used to detect whether various bits were even set
+
+ } VariousBits_1;
+
+ uint32_t ResetFactoryDefaultPreference = 0; // Do not clear this one in the clearAll()
+ uint32_t I2C_clockSpeed = 400000;
+ uint16_t WebserverPort = 80;
+ uint16_t SyslogPort = DEFAULT_SYSLOG_PORT;
+
+ int8_t ETH_Phy_Addr = -1;
+ int8_t ETH_Pin_mdc_cs = -1;
+ int8_t ETH_Pin_mdio_irq = -1;
+ int8_t ETH_Pin_power_rst = -1;
+ EthPhyType_t ETH_Phy_Type = EthPhyType_t::notSet;
+ EthClockMode_t ETH_Clock_Mode = EthClockMode_t::Ext_crystal_osc;
+ uint8_t ETH_IP[4] = {0};
+ uint8_t ETH_Gateway[4] = {0};
+ uint8_t ETH_Subnet[4] = {0};
+ uint8_t ETH_DNS[4] = {0};
+ NetworkMedium_t NetworkMedium = NetworkMedium_t::WIFI;
+ int8_t I2C_Multiplexer_Type = I2C_MULTIPLEXER_NONE;
+ int8_t I2C_Multiplexer_Addr = -1;
+ int8_t I2C_Multiplexer_Channel[N_TASKS]{};
+ uint8_t I2C_Flags[N_TASKS] = {0};
+ uint32_t I2C_clockSpeed_Slow = 100000;
+ int8_t I2C_Multiplexer_ResetPin = -1;
+
+ #ifdef ESP32
+ int8_t PinBootStates_ESP32[24] = {0}; // pins 17 ... 39
+ #endif
+ uint8_t WiFi_TX_power = 70; // 70 = 17.5dBm. unit: 0.25 dBm
+ int8_t WiFi_sensitivity_margin = 3; // Margin in dBm on top of sensitivity.
+ uint8_t NumberExtraWiFiScans = 0;
+ int8_t SPI_SCLK_pin = -1;
+ int8_t SPI_MISO_pin = -1;
+ int8_t SPI_MOSI_pin = -1;
+ int8_t ForceESPEasyNOWchannel = 0;
+
+ // Do not rename or move this checksum.
+ // Checksum calculation will work "around" this
+ uint8_t md5[16]{}; // Store checksum of the settings.
+
+ // VariousBits_2 defaults to 0, keep in mind when adding bit lookups.
+ struct {
+ uint32_t WaitWiFiConnect : 1; // Bit 00
+ uint32_t SDK_WiFi_autoreconnect : 1; // Bit 01
+ uint32_t DisableRulesCodeCompletion : 1; // Bit 02
+ uint32_t HiddenSSID_SlowConnectPerBSSID : 1; // Bit 03 // inverted
+ uint32_t EnableIPv6 : 1; // Bit 04 // inverted
+ uint32_t DisableSaveConfigAsTar : 1; // Bit 05
+ uint32_t PassiveWiFiScan : 1; // Bit 06 // inverted
+ uint32_t unused_07 : 1; // Bit 07
+ uint32_t unused_08 : 1; // Bit 08
+ uint32_t unused_09 : 1; // Bit 09
+ uint32_t unused_10 : 1; // Bit 10
+ uint32_t unused_11 : 1; // Bit 11
+ uint32_t unused_12 : 1; // Bit 12
+ uint32_t unused_13 : 1; // Bit 13
+ uint32_t unused_14 : 1; // Bit 14
+ uint32_t unused_15 : 1; // Bit 15
+ uint32_t unused_16 : 1; // Bit 16
+ uint32_t unused_17 : 1; // Bit 17
+ uint32_t unused_18 : 1; // Bit 18
+ uint32_t unused_19 : 1; // Bit 19
+ uint32_t unused_20 : 1; // Bit 20
+ uint32_t unused_21 : 1; // Bit 21
+ uint32_t unused_22 : 1; // Bit 22
+ uint32_t unused_23 : 1; // Bit 23
+ uint32_t unused_24 : 1; // Bit 24
+ uint32_t unused_25 : 1; // Bit 25
+ uint32_t unused_26 : 1; // Bit 26
+ uint32_t unused_27 : 1; // Bit 27
+ uint32_t unused_28 : 1; // Bit 28
+ uint32_t unused_29 : 1; // Bit 29
+ uint32_t unused_30 : 1; // Bit 30
+ uint32_t unused_31 : 1; // Bit 31
+
+ } VariousBits_2;
+
+ uint8_t console_serial_port = DEFAULT_CONSOLE_PORT;
+ int8_t console_serial_rxpin = DEFAULT_CONSOLE_PORT_RXPIN;
+ int8_t console_serial_txpin = DEFAULT_CONSOLE_PORT_TXPIN;
+ uint8_t console_serial0_fallback = DEFAULT_CONSOLE_SER0_FALLBACK;
+
+ // Try to extend settings to make the checksum 4-uint8_t aligned.
+};
+
+/*
+SettingsStruct* SettingsStruct_ptr = new (std::nothrow) SettingsStruct;
+SettingsStruct& Settings = *SettingsStruct_ptr;
+*/
+
+
+
+typedef SettingsStruct_tmpl SettingsStruct;
+
+#endif // DATASTRUCTS_SETTINGSSTRUCT_H
diff --git a/src/src/DataStructs/ShortChecksumType.cpp b/src/src/DataStructs/ShortChecksumType.cpp
new file mode 100644
index 0000000000..b605779d7c
--- /dev/null
+++ b/src/src/DataStructs/ShortChecksumType.cpp
@@ -0,0 +1,137 @@
+#include "../DataStructs/ShortChecksumType.h"
+
+#include "../Helpers/StringConverter.h"
+
+#include
+
+void ShortChecksumType::md5sumToShortChecksum(const uint8_t md5[16], uint8_t shortChecksum[4])
+{
+ memset(shortChecksum, 0, 4);
+
+ for (uint8_t i = 0; i < 16; ++i) {
+ // shortChecksum is XOR per 32 bit
+ shortChecksum[i % 4] ^= md5[i];
+ }
+}
+
+ShortChecksumType::ShortChecksumType(const ShortChecksumType& rhs)
+{
+ memcpy(_checksum, rhs._checksum, 4);
+}
+
+ShortChecksumType::ShortChecksumType(uint8_t checksum[4])
+{
+ memcpy(_checksum, checksum, 4);
+}
+
+ShortChecksumType::ShortChecksumType(const uint8_t *data,
+ size_t data_length)
+{
+ computeChecksum(_checksum, data, data_length, data_length, true);
+}
+
+ShortChecksumType::ShortChecksumType(const uint8_t *data,
+ size_t data_length,
+ size_t len_upto_checksum)
+{
+ computeChecksum(_checksum, data, data_length, len_upto_checksum, true);
+}
+
+ShortChecksumType::ShortChecksumType(const String strings[], size_t nrStrings)
+{
+ MD5Builder md5;
+
+ md5.begin();
+
+ for (size_t i = 0; i < nrStrings; ++i) {
+ md5.add(strings[i].c_str());
+ }
+ md5.calculate();
+ uint8_t tmp_md5[16] = { 0 };
+
+ md5.getBytes(tmp_md5);
+ md5sumToShortChecksum(tmp_md5, _checksum);
+}
+
+bool ShortChecksumType::computeChecksum(
+ uint8_t checksum[4],
+ const uint8_t *data,
+ size_t data_length,
+ size_t len_upto_checksum,
+ bool updateChecksum)
+{
+ if (len_upto_checksum > data_length) { len_upto_checksum = data_length; }
+ MD5Builder md5;
+
+ md5.begin();
+
+ if (len_upto_checksum > 0) {
+ // MD5Builder::add has non-const argument
+ md5.add(const_cast(data), len_upto_checksum);
+ }
+
+ if ((len_upto_checksum + 4) < data_length) {
+ data += len_upto_checksum + 4;
+ const int len_after_checksum = data_length - 4 - len_upto_checksum;
+
+ if (len_after_checksum > 0) {
+ // MD5Builder::add has non-const argument
+ md5.add(const_cast(data), len_after_checksum);
+ }
+ }
+ md5.calculate();
+ uint8_t tmp_checksum[4] = { 0 };
+
+ {
+ uint8_t tmp_md5[16] = { 0 };
+ md5.getBytes(tmp_md5);
+ md5sumToShortChecksum(tmp_md5, tmp_checksum);
+ }
+
+ if (memcmp(tmp_checksum, checksum, 4) != 0) {
+ // Data has changed, copy computed checksum
+ if (updateChecksum) {
+ memcpy(checksum, tmp_checksum, 4);
+ }
+ return false;
+ }
+ return true;
+}
+
+void ShortChecksumType::getChecksum(uint8_t checksum[4]) const {
+ memcpy(checksum, _checksum, 4);
+}
+
+void ShortChecksumType::setChecksum(const uint8_t checksum[4]) {
+ memcpy(_checksum, checksum, 4);
+}
+
+bool ShortChecksumType::matchChecksum(const uint8_t checksum[4]) const {
+ return memcmp(_checksum, checksum, 4) == 0;
+}
+
+bool ShortChecksumType::operator==(const ShortChecksumType& rhs) const {
+ return memcmp(_checksum, rhs._checksum, 4) == 0;
+}
+
+ShortChecksumType& ShortChecksumType::operator=(const ShortChecksumType& rhs) {
+ memcpy(_checksum, rhs._checksum, 4);
+ return *this;
+}
+
+String ShortChecksumType::toString() const {
+ return formatToHex_array(_checksum, 4);
+}
+
+bool ShortChecksumType::isSet() const {
+ return
+ _checksum[0] != 0 ||
+ _checksum[1] != 0 ||
+ _checksum[2] != 0 ||
+ _checksum[3] != 0;
+}
+
+void ShortChecksumType::clear()
+{
+ memset(_checksum, 0, 4);
+}
diff --git a/src/src/DataStructs/ShortChecksumType.h b/src/src/DataStructs/ShortChecksumType.h
new file mode 100644
index 0000000000..cae8526047
--- /dev/null
+++ b/src/src/DataStructs/ShortChecksumType.h
@@ -0,0 +1,57 @@
+#ifndef DATASTRUCTS_SHORTCHECKSUMTYPE_H
+#define DATASTRUCTS_SHORTCHECKSUMTYPE_H
+
+#include "../../ESPEasy_common.h"
+
+// Short (4 byte) version of ChecksumType
+struct __attribute__((__packed__)) ShortChecksumType {
+ // Empty checksum
+ ShortChecksumType() = default;
+
+ ShortChecksumType(const ShortChecksumType& rhs);
+
+ ShortChecksumType(uint8_t checksum[4]);
+
+ // Construct with checksum over entire range of given data
+ ShortChecksumType(const uint8_t *data,
+ size_t data_length);
+
+ ShortChecksumType(const uint8_t *data,
+ size_t data_length,
+ size_t len_upto_checksum);
+
+ ShortChecksumType(const String strings[],
+ size_t nrStrings);
+
+ // Compute checksum of the data.
+ // Skip the part where the checksum may be located in the data
+ // @param checksum The expected checksum. Will contain checksum after call finished.
+ // @retval true when checksum matches
+ static bool computeChecksum(
+ uint8_t checksum[4],
+ const uint8_t *data,
+ size_t data_length,
+ size_t len_upto_checksum,
+ bool updateChecksum = true);
+
+ void getChecksum(uint8_t checksum[4]) const;
+ void setChecksum(const uint8_t checksum[4]);
+ bool matchChecksum(const uint8_t checksum[4]) const;
+ bool operator==(const ShortChecksumType& rhs) const;
+ ShortChecksumType& operator=(const ShortChecksumType& rhs);
+
+ String toString() const;
+
+ bool isSet() const;
+
+ void clear();
+
+private:
+
+ static void md5sumToShortChecksum(const uint8_t md5[16],
+ uint8_t shortChecksum[4]);
+
+ uint8_t _checksum[4] = { 0 };
+};
+
+#endif // ifndef DATASTRUCTS_SHORTCHECKSUMTYPE_H
diff --git a/src/src/DataStructs/TimingStats.cpp b/src/src/DataStructs/TimingStats.cpp
index eb45f2274f..fdf9431194 100644
--- a/src/src/DataStructs/TimingStats.cpp
+++ b/src/src/DataStructs/TimingStats.cpp
@@ -96,6 +96,7 @@ const __FlashStringHelper* getPluginFunctionName(int function) {
case PLUGIN_REQUEST: return F("REQUEST");
case PLUGIN_PROCESS_CONTROLLER_DATA: return F("PROCESS_CONTROLLER_DATA");
case PLUGIN_I2C_GET_ADDRESS: return F("I2C_CHECK_DEVICE");
+ case PLUGIN_READ_ERROR_OCCURED: return F("PLUGIN_READ_ERROR_OCCURED");
}
return F("Unknown");
}
@@ -134,6 +135,7 @@ bool mustLogFunction(int function) {
case PLUGIN_REQUEST: return true;
case PLUGIN_I2C_GET_ADDRESS: return true;
case PLUGIN_PROCESS_CONTROLLER_DATA: return true;
+ case PLUGIN_READ_ERROR_OCCURED: return true;
}
return false;
}
@@ -141,6 +143,8 @@ bool mustLogFunction(int function) {
const __FlashStringHelper* getCPluginCFunctionName(CPlugin::Function function) {
switch (function) {
case CPlugin::Function::CPLUGIN_PROTOCOL_ADD: return F("CPLUGIN_PROTOCOL_ADD");
+ case CPlugin::Function::CPLUGIN_CONNECT_SUCCESS: return F("CPLUGIN_CONNECT_SUCCESS");
+ case CPlugin::Function::CPLUGIN_CONNECT_FAIL: return F("CPLUGIN_CONNECT_FAIL");
case CPlugin::Function::CPLUGIN_PROTOCOL_TEMPLATE: return F("CPLUGIN_PROTOCOL_TEMPLATE");
case CPlugin::Function::CPLUGIN_PROTOCOL_SEND: return F("CPLUGIN_PROTOCOL_SEND");
case CPlugin::Function::CPLUGIN_PROTOCOL_RECV: return F("CPLUGIN_PROTOCOL_RECV");
@@ -173,6 +177,8 @@ bool mustLogCFunction(CPlugin::Function function) {
switch (function) {
case CPlugin::Function::CPLUGIN_PROTOCOL_ADD: return false;
+ case CPlugin::Function::CPLUGIN_CONNECT_SUCCESS: return true;
+ case CPlugin::Function::CPLUGIN_CONNECT_FAIL: return true;
case CPlugin::Function::CPLUGIN_PROTOCOL_TEMPLATE: return false;
case CPlugin::Function::CPLUGIN_PROTOCOL_SEND: return true;
case CPlugin::Function::CPLUGIN_PROTOCOL_RECV: return true;
@@ -247,6 +253,12 @@ const __FlashStringHelper* getMiscStatsName_F(TimingStatsElements stat) {
case TimingStatsElements::GRAT_ARP_STATS: return F("sendGratuitousARP()");
case TimingStatsElements::SAVE_TO_RTC: return F("saveToRTC()");
case TimingStatsElements::BACKGROUND_TASKS: return F("backgroundtasks()");
+ case TimingStatsElements::UPDATE_RTTTL: return F("update_rtttl()");
+ case TimingStatsElements::CHECK_UDP: return F("checkUDP()");
+ case TimingStatsElements::C013_SEND_UDP: return F("C013_sendUDP() SUCCESS");
+ case TimingStatsElements::C013_SEND_UDP_FAIL: return F("C013_sendUDP() FAIL");
+ case TimingStatsElements::C013_RECEIVE_SENSOR_DATA: return F("C013 Receive sensor data");
+ case TimingStatsElements::WEBSERVER_HANDLE_CLIENT: return F("web_server.handleClient()");
case TimingStatsElements::PROCESS_SYSTEM_EVENT_QUEUE: return F("process_system_event_queue()");
case TimingStatsElements::FORMAT_USER_VAR: return F("doFormatUserVar()");
case TimingStatsElements::IS_NUMERICAL: return F("isNumerical()");
diff --git a/src/src/DataStructs/TimingStats.h b/src/src/DataStructs/TimingStats.h
index e10de2fe77..41126efef2 100644
--- a/src/src/DataStructs/TimingStats.h
+++ b/src/src/DataStructs/TimingStats.h
@@ -106,6 +106,12 @@ enum class TimingStatsElements {
HANDLE_SCHEDULER_TASK,
HANDLE_SCHEDULER_IDLE,
BACKGROUND_TASKS,
+ CHECK_UDP,
+ C013_SEND_UDP,
+ C013_SEND_UDP_FAIL,
+ C013_RECEIVE_SENSOR_DATA,
+ WEBSERVER_HANDLE_CLIENT,
+ UPDATE_RTTTL,
// Web serving
HANDLE_SERVING_WEBPAGE,
diff --git a/src/src/DataStructs/UserVarStruct.cpp b/src/src/DataStructs/UserVarStruct.cpp
index 96d4bb333f..1c97650ada 100644
--- a/src/src/DataStructs/UserVarStruct.cpp
+++ b/src/src/DataStructs/UserVarStruct.cpp
@@ -263,7 +263,6 @@ ESPEASY_RULES_FLOAT_TYPE UserVarStruct::getAsDouble(taskIndex_t taskIndex,
String UserVarStruct::getAsString(taskIndex_t taskIndex, taskVarIndex_t varNr, Sensor_VType sensorType, uint8_t nrDecimals, bool raw) const
{
- START_TIMER
const TaskValues_Data_t *data = getRawOrComputed(taskIndex, varNr, sensorType, raw);
if (data != nullptr) {
diff --git a/src/src/DataStructs/WiFi_AP_Candidate.cpp b/src/src/DataStructs/WiFi_AP_Candidate.cpp
index e328d7e79d..ddb96812cc 100644
--- a/src/src/DataStructs/WiFi_AP_Candidate.cpp
+++ b/src/src/DataStructs/WiFi_AP_Candidate.cpp
@@ -1,259 +1,259 @@
-#include "../DataStructs/WiFi_AP_Candidate.h"
-
-#include "../Globals/ESPEasyWiFiEvent.h"
-#include "../Globals/SecuritySettings.h"
-#include "../Globals/Statistics.h"
-#include "../Helpers/ESPEasy_time_calc.h"
-#include "../Helpers/Misc.h"
-#include "../Helpers/StringConverter.h"
-#include "../Helpers/StringGenerator_WiFi.h"
-#include "../../ESPEasy_common.h"
-
-#if defined(ESP8266)
- # include
-#endif // if defined(ESP8266)
-#if defined(ESP32)
- # include
-#endif // if defined(ESP32)
-
-#define WIFI_AP_CANDIDATE_MAX_AGE 300000 // 5 minutes in msec
-
-
-WiFi_AP_Candidate::WiFi_AP_Candidate() :
-#ifdef ESP32
-# if ESP_IDF_VERSION_MAJOR >= 5
-country({
- .cc = "01",
- .schan = 1,
- .nchan = 11,
- .policy = WIFI_COUNTRY_POLICY_AUTO,
-}),
-#endif
-#endif
- last_seen(0), rssi(0), channel(0), index(0), enc_type(0)
-{
- memset(&bits, 0, sizeof(bits));
-}
-
-WiFi_AP_Candidate::WiFi_AP_Candidate(uint8_t index_c, const String& ssid_c) :
- last_seen(0), rssi(0), channel(0), index(index_c), enc_type(0)
-{
- memset(&bits, 0, sizeof(bits));
-
- const size_t ssid_length = ssid_c.length();
-
- if ((ssid_length == 0) || equals(ssid_c, F("ssid"))) {
- return;
- }
-
- if (ssid_length > 32) { return; }
-
- ssid = ssid_c;
-}
-
-WiFi_AP_Candidate::WiFi_AP_Candidate(uint8_t networkItem) : index(0) {
- // Need to make sure the phy isn't known as we can't get this information from the AP
- // See: https://github.com/letscontrolit/ESPEasy/issues/4996
- // Not sure why this makes any difference as the flags should already have been set to 0.
- memset(&bits, 0, sizeof(bits));
-
- ssid = WiFi.SSID(networkItem);
- rssi = WiFi.RSSI(networkItem);
- channel = WiFi.channel(networkItem);
- bssid = WiFi.BSSID(networkItem);
- enc_type = WiFi.encryptionType(networkItem);
- #ifdef ESP8266
- bits.isHidden = WiFi.isHidden(networkItem);
- # ifdef CORE_POST_3_0_0
- const bss_info *it = reinterpret_cast(WiFi.getScanInfoByIndex(networkItem));
-
- if (it) {
- bits.phy_11b = it->phy_11b;
- bits.phy_11g = it->phy_11g;
- bits.phy_11n = it->phy_11n;
- bits.wps = it->wps;
- }
- # endif // ifdef CORE_POST_3_0_0
- #endif // ifdef ESP8266
- #ifdef ESP32
- bits.isHidden = ssid.isEmpty();
- wifi_ap_record_t *it = reinterpret_cast(WiFi.getScanInfoByIndex(networkItem));
-
- if (it) {
- bits.phy_11b = it->phy_11b;
- bits.phy_11g = it->phy_11g;
- bits.phy_11n = it->phy_11n;
- bits.phy_lr = it->phy_lr;
-# if ESP_IDF_VERSION_MAJOR >= 5
- bits.phy_11ax = it->phy_11ax;
- bits.ftm_initiator = it->ftm_initiator;
- bits.ftm_responder = it->ftm_responder;
-# endif // if ESP_IDF_VERSION_MAJOR >= 5
- bits.wps = it->wps;
-
- // FIXME TD-er: Maybe also add other info like 2nd channel, ftm and phy_lr support?
-# if ESP_IDF_VERSION_MAJOR >= 5
- memcpy(&country, &(it->country), sizeof(wifi_country_t));
-#endif
- }
- #endif // ifdef ESP32
- last_seen = millis();
-}
-
-#ifdef ESP8266
-# if FEATURE_ESP8266_DIRECT_WIFI_SCAN
-WiFi_AP_Candidate::WiFi_AP_Candidate(const bss_info& ap) :
- rssi(ap.rssi), channel(ap.channel), bssid(ap.bssid),
- index(0), enc_type(0), isHidden(ap.is_hidden),
- phy_11b(ap.phy_11b), phy_11g(ap.phy_11g), phy_11n(ap.phy_11n),
- wps(ap.wps)
-{
- memset(&bits, 0, sizeof(bits));
-
- last_seen = millis();
-
- switch (ap.authmode) {
- case AUTH_OPEN: enc_type = ENC_TYPE_NONE; break;
- case AUTH_WEP: enc_type = ENC_TYPE_WEP; break;
- case AUTH_WPA_PSK: enc_type = ENC_TYPE_TKIP; break;
- case AUTH_WPA2_PSK: enc_type = ENC_TYPE_CCMP; break;
- case AUTH_WPA_WPA2_PSK: enc_type = ENC_TYPE_AUTO; break;
- case AUTH_MAX: break;
- }
-
- char tmp[33]; // ssid can be up to 32chars, => plus null term
- const size_t ssid_len = std::min(static_cast(ap.ssid_len), sizeof(ap.ssid));
-
- memcpy(tmp, ap.ssid, ssid_len);
- tmp[ssid_len] = 0; // nullterm marking end of string
-
- ssid = String(reinterpret_cast(tmp));
-}
-
-# endif // if FEATURE_ESP8266_DIRECT_WIFI_SCAN
-#endif // ifdef ESP8266
-
-
-bool WiFi_AP_Candidate::operator<(const WiFi_AP_Candidate& other) const {
- if (bits.isEmergencyFallback != other.bits.isEmergencyFallback) {
- return bits.isEmergencyFallback;
- }
-
- if (bits.lowPriority != other.bits.lowPriority) {
- return !bits.lowPriority;
- }
-
- // Prefer non hidden over hidden.
- if (bits.isHidden != other.bits.isHidden) {
- return !bits.isHidden;
- }
-
- // RSSI values >= 0 are invalid
- if (rssi >= 0) { return false; }
-
- if (other.rssi >= 0) { return true; }
-
- // RSSI values are negative, so the larger value is the better one.
- return rssi > other.rssi;
-}
-
-bool WiFi_AP_Candidate::usable() const {
- // Allow for empty pass
- // if (key.isEmpty()) return false;
- if (bits.isEmergencyFallback) {
- int allowedUptimeMinutes = 10;
- #ifdef CUSTOM_EMERGENCY_FALLBACK_ALLOW_MINUTES_UPTIME
- allowedUptimeMinutes = CUSTOM_EMERGENCY_FALLBACK_ALLOW_MINUTES_UPTIME;
- #endif // ifdef CUSTOM_EMERGENCY_FALLBACK_ALLOW_MINUTES_UPTIME
-
- if ((getUptimeMinutes() > allowedUptimeMinutes) ||
- !SecuritySettings.hasWiFiCredentials() ||
- WiFiEventData.performedClearWiFiCredentials ||
- (lastBootCause != BOOT_CAUSE_COLD_BOOT)) {
- return false;
- }
- }
-
- if (!bits.isHidden && (ssid.isEmpty())) { return false; }
- return !expired();
-}
-
-bool WiFi_AP_Candidate::expired() const {
- if (last_seen == 0) {
- // Not set, so cannot expire
- return false;
- }
- return timePassedSince(last_seen) > WIFI_AP_CANDIDATE_MAX_AGE;
-}
-
-String WiFi_AP_Candidate::toString(const String& separator) const {
- String result = ssid;
-
- htmlEscape(result);
-
- if (bits.isHidden) {
- result += F("#Hidden#");
- }
- result += strformat(
- F("%s%s%sCh:%u"),
- separator.c_str(),
- bssid.toString().c_str(),
- separator.c_str(),
- channel);
-
- if (rssi == -1) {
- result += F(" (RTC) ");
- } else {
- result += strformat(F(" (%ddBm)"), rssi);
- }
-
- result += encryption_type();
-
-#ifdef ESP32
-# if ESP_IDF_VERSION_MAJOR >= 5
- // Country code string
- if (country.cc[0] != '\0' && country.cc[1] != '\0') {
- result += strformat(F(" '%c%c'"), country.cc[0], country.cc[1]);
- switch (country.cc[2]) {
- case 'O': // Outdoor
- case 'I': // Indoor
- case 'X': // "non-country"
- result += strformat(F("(%c)"), country.cc[2]);
- break;
- }
- }
- if (country.nchan > 0) {
- result += strformat(F(" ch: %d..%d"), country.schan, country.schan + country.nchan - 1);
- }
-#endif
-#endif
-
- if (phy_known()) {
- String phy_str;
-
- if (bits.phy_11b) { phy_str += 'b'; }
-
- if (bits.phy_11g) { phy_str += 'g'; }
-
- if (bits.phy_11n) { phy_str += 'n'; }
-#ifdef ESP32
-
- if (bits.phy_11ax) { phy_str += F("/ax"); }
-
- if (bits.phy_lr) { phy_str += F("/lr"); }
-
- if (bits.ftm_initiator) { phy_str += F("/FTM_i"); }
-
- if (bits.ftm_responder) { phy_str += F("/FTM_r"); }
-#endif // ifdef ESP32
-
- if (phy_str.length()) {
- result += strformat(F(" (%s)"), phy_str.c_str());
- }
- }
- return result;
-}
-
-String WiFi_AP_Candidate::encryption_type() const {
- return WiFi_encryptionType(enc_type);
-}
+#include "../DataStructs/WiFi_AP_Candidate.h"
+
+#include "../Globals/ESPEasyWiFiEvent.h"
+#include "../Globals/SecuritySettings.h"
+#include "../Globals/Statistics.h"
+#include "../Helpers/ESPEasy_time_calc.h"
+#include "../Helpers/Misc.h"
+#include "../Helpers/StringConverter.h"
+#include "../Helpers/StringGenerator_WiFi.h"
+#include "../../ESPEasy_common.h"
+
+#if defined(ESP8266)
+ # include
+#endif // if defined(ESP8266)
+#if defined(ESP32)
+ # include
+#endif // if defined(ESP32)
+
+#define WIFI_AP_CANDIDATE_MAX_AGE 300000 // 5 minutes in msec
+
+
+WiFi_AP_Candidate::WiFi_AP_Candidate() :
+#ifdef ESP32
+# if ESP_IDF_VERSION_MAJOR >= 5
+country({
+ .cc = "01",
+ .schan = 1,
+ .nchan = 11,
+ .policy = WIFI_COUNTRY_POLICY_AUTO,
+}),
+#endif
+#endif
+ last_seen(0), rssi(0), channel(0), index(0), enc_type(0)
+{
+ memset(&bits, 0, sizeof(bits));
+}
+
+WiFi_AP_Candidate::WiFi_AP_Candidate(uint8_t index_c, const String& ssid_c) :
+ last_seen(0), rssi(0), channel(0), index(index_c), enc_type(0)
+{
+ memset(&bits, 0, sizeof(bits));
+
+ const size_t ssid_length = ssid_c.length();
+
+ if ((ssid_length == 0) || equals(ssid_c, F("ssid"))) {
+ return;
+ }
+
+ if (ssid_length > 32) { return; }
+
+ ssid = ssid_c;
+}
+
+WiFi_AP_Candidate::WiFi_AP_Candidate(uint8_t networkItem) : index(0) {
+ // Need to make sure the phy isn't known as we can't get this information from the AP
+ // See: https://github.com/letscontrolit/ESPEasy/issues/4996
+ // Not sure why this makes any difference as the flags should already have been set to 0.
+ memset(&bits, 0, sizeof(bits));
+
+ ssid = WiFi.SSID(networkItem);
+ rssi = WiFi.RSSI(networkItem);
+ channel = WiFi.channel(networkItem);
+ bssid = WiFi.BSSID(networkItem);
+ enc_type = WiFi.encryptionType(networkItem);
+ #ifdef ESP8266
+ bits.isHidden = WiFi.isHidden(networkItem);
+ # ifdef CORE_POST_3_0_0
+ const bss_info *it = reinterpret_cast(WiFi.getScanInfoByIndex(networkItem));
+
+ if (it) {
+ bits.phy_11b = it->phy_11b;
+ bits.phy_11g = it->phy_11g;
+ bits.phy_11n = it->phy_11n;
+ bits.wps = it->wps;
+ }
+ # endif // ifdef CORE_POST_3_0_0
+ #endif // ifdef ESP8266
+ #ifdef ESP32
+ bits.isHidden = ssid.isEmpty();
+ wifi_ap_record_t *it = reinterpret_cast(WiFi.getScanInfoByIndex(networkItem));
+
+ if (it) {
+ bits.phy_11b = it->phy_11b;
+ bits.phy_11g = it->phy_11g;
+ bits.phy_11n = it->phy_11n;
+ bits.phy_lr = it->phy_lr;
+# if ESP_IDF_VERSION_MAJOR >= 5
+ bits.phy_11ax = it->phy_11ax;
+ bits.ftm_initiator = it->ftm_initiator;
+ bits.ftm_responder = it->ftm_responder;
+# endif // if ESP_IDF_VERSION_MAJOR >= 5
+ bits.wps = it->wps;
+
+ // FIXME TD-er: Maybe also add other info like 2nd channel, ftm and phy_lr support?
+# if ESP_IDF_VERSION_MAJOR >= 5
+ memcpy(&country, &(it->country), sizeof(wifi_country_t));
+#endif
+ }
+ #endif // ifdef ESP32
+ last_seen = millis();
+}
+
+#ifdef ESP8266
+# if FEATURE_ESP8266_DIRECT_WIFI_SCAN
+WiFi_AP_Candidate::WiFi_AP_Candidate(const bss_info& ap) :
+ rssi(ap.rssi), channel(ap.channel), bssid(ap.bssid),
+ index(0), enc_type(0), isHidden(ap.is_hidden),
+ phy_11b(ap.phy_11b), phy_11g(ap.phy_11g), phy_11n(ap.phy_11n),
+ wps(ap.wps)
+{
+ memset(&bits, 0, sizeof(bits));
+
+ last_seen = millis();
+
+ switch (ap.authmode) {
+ case AUTH_OPEN: enc_type = ENC_TYPE_NONE; break;
+ case AUTH_WEP: enc_type = ENC_TYPE_WEP; break;
+ case AUTH_WPA_PSK: enc_type = ENC_TYPE_TKIP; break;
+ case AUTH_WPA2_PSK: enc_type = ENC_TYPE_CCMP; break;
+ case AUTH_WPA_WPA2_PSK: enc_type = ENC_TYPE_AUTO; break;
+ case AUTH_MAX: break;
+ }
+
+ char tmp[33]; // ssid can be up to 32chars, => plus null term
+ const size_t ssid_len = std::min(static_cast(ap.ssid_len), sizeof(ap.ssid));
+
+ memcpy(tmp, ap.ssid, ssid_len);
+ tmp[ssid_len] = 0; // nullterm marking end of string
+
+ ssid = String(reinterpret_cast(tmp));
+}
+
+# endif // if FEATURE_ESP8266_DIRECT_WIFI_SCAN
+#endif // ifdef ESP8266
+
+
+bool WiFi_AP_Candidate::operator<(const WiFi_AP_Candidate& other) const {
+ if (bits.isEmergencyFallback != other.bits.isEmergencyFallback) {
+ return bits.isEmergencyFallback;
+ }
+
+ if (bits.lowPriority != other.bits.lowPriority) {
+ return !bits.lowPriority;
+ }
+
+ // Prefer non hidden over hidden.
+ if (bits.isHidden != other.bits.isHidden) {
+ return !bits.isHidden;
+ }
+
+ // RSSI values >= 0 are invalid
+ if (rssi >= 0) { return false; }
+
+ if (other.rssi >= 0) { return true; }
+
+ // RSSI values are negative, so the larger value is the better one.
+ return rssi > other.rssi;
+}
+
+bool WiFi_AP_Candidate::usable() const {
+ // Allow for empty pass
+ // if (key.isEmpty()) return false;
+ if (bits.isEmergencyFallback) {
+ int allowedUptimeMinutes = 10;
+ #ifdef CUSTOM_EMERGENCY_FALLBACK_ALLOW_MINUTES_UPTIME
+ allowedUptimeMinutes = CUSTOM_EMERGENCY_FALLBACK_ALLOW_MINUTES_UPTIME;
+ #endif // ifdef CUSTOM_EMERGENCY_FALLBACK_ALLOW_MINUTES_UPTIME
+
+ if ((getUptimeMinutes() > allowedUptimeMinutes) ||
+ !SecuritySettings.hasWiFiCredentials() ||
+ WiFiEventData.performedClearWiFiCredentials ||
+ (lastBootCause != BOOT_CAUSE_COLD_BOOT)) {
+ return false;
+ }
+ }
+
+ if (!bits.isHidden && (ssid.isEmpty())) { return false; }
+ return !expired();
+}
+
+bool WiFi_AP_Candidate::expired() const {
+ if (last_seen == 0) {
+ // Not set, so cannot expire
+ return false;
+ }
+ return timePassedSince(last_seen) > WIFI_AP_CANDIDATE_MAX_AGE;
+}
+
+String WiFi_AP_Candidate::toString(const String& separator) const {
+ String result = ssid;
+
+ htmlEscape(result);
+
+ if (bits.isHidden) {
+ result += F("#Hidden#");
+ }
+ result += strformat(
+ F("%s%s%sCh:%u"),
+ separator.c_str(),
+ bssid.toString().c_str(),
+ separator.c_str(),
+ channel);
+
+ if (rssi == -1) {
+ result += F(" (RTC) ");
+ } else {
+ result += strformat(F(" (%ddBm) "), rssi);
+ }
+
+ result += encryption_type();
+
+#ifdef ESP32
+# if ESP_IDF_VERSION_MAJOR >= 5
+ // Country code string
+ if (country.cc[0] != '\0' && country.cc[1] != '\0') {
+ result += strformat(F(" '%c%c'"), country.cc[0], country.cc[1]);
+ switch (country.cc[2]) {
+ case 'O': // Outdoor
+ case 'I': // Indoor
+ case 'X': // "non-country"
+ result += strformat(F("(%c)"), country.cc[2]);
+ break;
+ }
+ }
+ if (country.nchan > 0) {
+ result += strformat(F(" ch: %d..%d"), country.schan, country.schan + country.nchan - 1);
+ }
+#endif
+#endif
+
+ if (phy_known()) {
+ String phy_str;
+
+ if (bits.phy_11b) { phy_str += 'b'; }
+
+ if (bits.phy_11g) { phy_str += 'g'; }
+
+ if (bits.phy_11n) { phy_str += 'n'; }
+#ifdef ESP32
+
+ if (bits.phy_11ax) { phy_str += F("/ax"); }
+
+ if (bits.phy_lr) { phy_str += F("/lr"); }
+
+ if (bits.ftm_initiator) { phy_str += F("/FTM_i"); }
+
+ if (bits.ftm_responder) { phy_str += F("/FTM_r"); }
+#endif // ifdef ESP32
+
+ if (phy_str.length()) {
+ result += strformat(F(" (%s)"), phy_str.c_str());
+ }
+ }
+ return result;
+}
+
+String WiFi_AP_Candidate::encryption_type() const {
+ return WiFi_encryptionType(enc_type);
+}
diff --git a/src/src/DataTypes/ESPEasy_plugin_functions.h b/src/src/DataTypes/ESPEasy_plugin_functions.h
index 37f05eb238..267ea867a0 100644
--- a/src/src/DataTypes/ESPEasy_plugin_functions.h
+++ b/src/src/DataTypes/ESPEasy_plugin_functions.h
@@ -84,6 +84,8 @@ class CPlugin {
enum class Function {
CPLUGIN_PROTOCOL_ADD = 127, // Called at boot for letting a controller adding itself to list of available controllers
+ CPLUGIN_CONNECT_SUCCESS, // Only used for timing stats
+ CPLUGIN_CONNECT_FAIL, // Only used for timing stats
CPLUGIN_PROTOCOL_TEMPLATE,
CPLUGIN_PROTOCOL_SEND,
CPLUGIN_PROTOCOL_RECV,
diff --git a/src/src/DataTypes/TaskValues_Data.cpp b/src/src/DataTypes/TaskValues_Data.cpp
index 62136f37b4..961b630b40 100644
--- a/src/src/DataTypes/TaskValues_Data.cpp
+++ b/src/src/DataTypes/TaskValues_Data.cpp
@@ -251,7 +251,6 @@ bool TaskValues_Data_t::isValid(uint8_t varNr, Sensor_VType sensorType) const
String TaskValues_Data_t::getAsString(uint8_t varNr, Sensor_VType sensorType, uint8_t nrDecimals) const
{
String result;
- START_TIMER;
if (isFloatOutputDataType(sensorType)) {
result = toString(getFloat(varNr), nrDecimals);
@@ -275,5 +274,5 @@ String TaskValues_Data_t::getAsString(uint8_t varNr, Sensor_VType sensorType, u
#endif
}
result.trim();
- return std::move(result);
+ return result;
}
diff --git a/src/src/ESPEasyCore/Controller.cpp b/src/src/ESPEasyCore/Controller.cpp
index 14f6ca1297..b56c14e4af 100644
--- a/src/src/ESPEasyCore/Controller.cpp
+++ b/src/src/ESPEasyCore/Controller.cpp
@@ -210,6 +210,8 @@ bool MQTTConnect(controllerIndex_t controller_idx)
#endif
MQTTclient.setClient(mqtt);
+ MQTTclient.setKeepAlive(10);
+ MQTTclient.setSocketTimeout(timeout);
if (ControllerSettings->UseDNS) {
MQTTclient.setServer(ControllerSettings->getHost().c_str(), ControllerSettings->Port);
@@ -232,7 +234,7 @@ bool MQTTConnect(controllerIndex_t controller_idx)
addLog(LOG_LEVEL_ERROR, F("MQTT : Intentional reconnect"));
}
- const unsigned long connect_start_time = millis();
+ const uint64_t statisticsTimerStart(getMicros64());
// https://github.com/knolleary/pubsubclient/issues/458#issuecomment-493875150
if (hasControllerCredentialsSet(controller_idx, *ControllerSettings)) {
@@ -257,7 +259,11 @@ bool MQTTConnect(controllerIndex_t controller_idx)
}
delay(0);
- count_connection_results(MQTTresult, F("MQTT : Broker "), Settings.Protocol[controller_idx], connect_start_time);
+ count_connection_results(
+ MQTTresult,
+ F("MQTT : Broker "),
+ Settings.Protocol[controller_idx],
+ statisticsTimerStart);
if (!MQTTresult) {
MQTTclient.disconnect();
@@ -632,7 +638,7 @@ void SensorSendTask(struct EventStruct *event, unsigned long timestampUnixTime,
struct EventStruct TempEvent(event->TaskIndex);
TempEvent.Source = event->Source;
- TempEvent.timestamp = timestampUnixTime;
+ TempEvent.timestamp_sec = timestampUnixTime;
checkDeviceVTypeForTask(&TempEvent);
String dummy;
diff --git a/src/src/ESPEasyCore/ESPEasyRules.cpp b/src/src/ESPEasyCore/ESPEasyRules.cpp
index 5abb96df35..f85db9df64 100644
--- a/src/src/ESPEasyCore/ESPEasyRules.cpp
+++ b/src/src/ESPEasyCore/ESPEasyRules.cpp
@@ -1,1344 +1,1344 @@
-#include "../ESPEasyCore/ESPEasyRules.h"
-
-#include "../../_Plugin_Helper.h"
-
-#include "../Commands/ExecuteCommand.h"
-#include "../DataStructs/TimingStats.h"
-#include "../DataTypes/EventValueSource.h"
-#include "../ESPEasyCore/ESPEasy_backgroundtasks.h"
-#include "../ESPEasyCore/Serial.h"
-#include "../Globals/Cache.h"
-#include "../Globals/Device.h"
-#include "../Globals/EventQueue.h"
-#include "../Globals/Plugins.h"
-#include "../Globals/Plugins_other.h"
-#include "../Globals/RulesCalculate.h"
-#include "../Globals/Settings.h"
-#include "../Helpers/ESPEasy_Storage.h"
-#include "../Helpers/ESPEasy_time_calc.h"
-#include "../Helpers/FS_Helper.h"
-#include "../Helpers/Misc.h"
-#include "../Helpers/Numerical.h"
-#include "../Helpers/RulesHelper.h"
-#include "../Helpers/RulesMatcher.h"
-#include "../Helpers/StringConverter.h"
-#include "../Helpers/StringParser.h"
-
-
-#include
-#include
-
-#ifdef WEBSERVER_NEW_RULES
-String EventToFileName(const String& eventName) {
- int size = eventName.length();
- int index = eventName.indexOf('=');
-
- if (index > -1) {
- size = index;
- }
-#if defined(ESP8266)
- String fileName = F("rules/");
-#endif // if defined(ESP8266)
-#if defined(ESP32)
- String fileName = F("/rules/");
-#endif // if defined(ESP32)
- fileName += eventName.substring(0, size);
- fileName.replace('#', RULE_FILE_SEPARAROR);
- fileName.toLowerCase();
- return fileName;
-}
-
-String FileNameToEvent(const String& fileName) {
-#if defined(ESP8266)
- String eventName = fileName.substring(6);
-#endif // if defined(ESP8266)
-#if defined(ESP32)
- String eventName = fileName.substring(7);
-#endif // if defined(ESP32)
- eventName.replace(RULE_FILE_SEPARAROR, '#');
- return eventName;
-}
-#endif
-
-void checkRuleSets() {
- Cache.rulesHelper.closeAllFiles();
-}
-
-/********************************************************************************************\
- Process next event from event queue
- \*********************************************************************************************/
-bool processNextEvent() {
- if (Settings.UseRules)
- {
- String nextEvent;
-
- if (eventQueue.getNext(nextEvent)) {
- rulesProcessing(nextEvent);
- return true;
- }
- }
-
- // Just make sure any (accidentally) added or remaining events are not kept.
- eventQueue.clear();
- return false;
-}
-
-/********************************************************************************************\
- Rules processing
- \*********************************************************************************************/
-void rulesProcessing(const String& event) {
- if (!Settings.UseRules) {
- return;
- }
- START_TIMER
- #ifndef BUILD_NO_RAM_TRACKER
- checkRAM(F("rulesProcessing"));
- #endif // ifndef BUILD_NO_RAM_TRACKER
-#ifndef BUILD_NO_DEBUG
- const unsigned long timer = millis();
-#endif // ifndef BUILD_NO_DEBUG
-
- if (loglevelActiveFor(LOG_LEVEL_INFO)) {
- addLogMove(LOG_LEVEL_INFO, concat(F("EVENT: "), event));
- }
-
- if (Settings.OldRulesEngine()) {
- bool eventHandled = false;
-
- if (Settings.EnableRulesCaching()) {
- String filename;
- size_t pos = 0;
- if (Cache.rulesHelper.findMatchingRule(event, filename, pos)) {
- const bool startOnMatched = true; // We already matched the event
- eventHandled = rulesProcessingFile(filename, event, pos, startOnMatched);
- }
- } else {
- for (uint8_t x = 0; x < RULESETS_MAX && !eventHandled; x++) {
- eventHandled = rulesProcessingFile(getRulesFileName(x), event);
- }
- }
- } else {
- #ifdef WEBSERVER_NEW_RULES
- String fileName = EventToFileName(event);
-
- // if exists processed the rule file
- if (fileExists(fileName)) {
- rulesProcessingFile(fileName, event);
- }
- # ifndef BUILD_NO_DEBUG
- else {
- addLog(LOG_LEVEL_DEBUG, strformat(F("EVENT: %s is ingnored. File %s not found."),
- event.c_str(), fileName.c_str()));
- }
- # endif // ifndef BUILD_NO_DEBUG
- #endif // WEBSERVER_NEW_RULES
- }
-
-#ifndef BUILD_NO_DEBUG
-
- if (loglevelActiveFor(LOG_LEVEL_DEBUG)) {
- addLogMove(LOG_LEVEL_DEBUG, strformat(F("EVENT: %s Processing: %d ms"), event.c_str(), timePassedSince(timer)));
- }
-#endif // ifndef BUILD_NO_DEBUG
- STOP_TIMER(RULES_PROCESSING);
- backgroundtasks();
-}
-
-/********************************************************************************************\
- Rules processing
- \*********************************************************************************************/
-bool rulesProcessingFile(const String& fileName,
- const String& event,
- size_t pos,
- bool startOnMatched) {
- if (!Settings.UseRules || !fileExists(fileName)) {
- return false;
- }
- #ifndef BUILD_NO_RAM_TRACKER
- checkRAM(F("rulesProcessingFile"));
- #endif // ifndef BUILD_NO_RAM_TRACKER
-#ifndef BUILD_NO_DEBUG
-
- if (Settings.SerialLogLevel == LOG_LEVEL_DEBUG_DEV) {
- serialPrint(F("RuleDebug Processing:"));
- serialPrintln(fileName);
- serialPrintln(F(" flags CMI parse output:"));
- }
-#endif // ifndef BUILD_NO_DEBUG
-
- static uint8_t nestingLevel = 0;
-
- nestingLevel++;
-
- if (nestingLevel > RULES_MAX_NESTING_LEVEL) {
- addLog(LOG_LEVEL_ERROR, F("EVENT: Error: Nesting level exceeded!"));
- nestingLevel--;
- return false;
- }
-
-
- bool match = false;
- bool codeBlock = false;
- bool isCommand = false;
- bool condition[RULES_IF_MAX_NESTING_LEVEL];
- bool ifBranche[RULES_IF_MAX_NESTING_LEVEL];
- uint8_t ifBlock = 0;
- uint8_t fakeIfBlock = 0;
-
-
- bool moreAvailable = true;
- bool eventHandled = false;
- while (moreAvailable && !eventHandled) {
- const bool searchNextOnBlock = !codeBlock && !match;
- String line = Cache.rulesHelper.readLn(fileName, pos, moreAvailable, searchNextOnBlock);
-
- // Parse the line and extract the action (if there is any)
- String action;
- {
- START_TIMER
- const bool matched_before_parse = match;
- bool isOneLiner = false;
- parseCompleteNonCommentLine(line, event, action, match, codeBlock,
- isCommand, isOneLiner, condition, ifBranche, ifBlock,
- fakeIfBlock, startOnMatched);
- if ((matched_before_parse && !match) || isOneLiner) {
- // We were processing a matching event and now crossed the "endon"
- // Or just dealing with a oneliner.
- // So we're done processing
- eventHandled = true;
- backgroundtasks();
- }
- STOP_TIMER(RULES_PARSE_LINE);
- }
-
- if (match) // rule matched for one action or a block of actions
- {
- START_TIMER
- processMatchedRule(action, event,
- isCommand, condition,
- ifBranche, ifBlock, fakeIfBlock);
- STOP_TIMER(RULES_PROCESS_MATCHED);
- }
- }
-
-/*
- if (f) {
- f.close();
- }
-*/
-
- nestingLevel--;
- #ifndef BUILD_NO_RAM_TRACKER
- checkRAM(F("rulesProcessingFile2"));
- #endif // ifndef BUILD_NO_RAM_TRACKER
- backgroundtasks();
- return eventHandled; // && nestingLevel == 0;
-}
-
-
-/********************************************************************************************\
- Parse string commands
- \*********************************************************************************************/
-bool get_next_inner_bracket(const String& line, int& startIndex, int& closingIndex, char closingBracket)
-{
- if (line.length() <= 1) {
- // Not possible to have opening and closing bracket on a line this short.
- return false;
- }
- char openingBracket = closingBracket;
-
- switch (closingBracket) {
- case ']': openingBracket = '['; break;
- case '}': openingBracket = '{'; break;
- case ')': openingBracket = '('; break;
- default:
- // unknown bracket type
- return false;
- }
- // Closing bracket should not be found on the first position.
- closingIndex = line.indexOf(closingBracket, startIndex + 1);
-
- if (closingIndex == -1) {
- // not found
- return false;
- }
-
- for (int i = (closingIndex - 1); i > startIndex; --i) {
- if (line[i] == openingBracket) {
- startIndex = i;
- return true;
- }
- }
- return false;
-}
-
-bool get_next_argument(const String& fullCommand, int& index, String& argument, char separator)
-{
- if (index == -1) {
- return false;
- }
- int newIndex = fullCommand.indexOf(separator, index);
-
- if (newIndex == -1) {
- argument = fullCommand.substring(index);
- } else {
- argument = fullCommand.substring(index, newIndex);
- }
-
- if (argument.startsWith(String(separator))) {
- argument = argument.substring(1);
- }
-
- // addLog(LOG_LEVEL_INFO, String("get_next_argument: ") + String(index) + " " + fullCommand + " " + argument);
- index = newIndex;
-
- if (index != -1) {
- ++index;
- }
- return argument.length() > 0;
-}
-
-const char bitwise_functions[] PROGMEM = "bitread|bitset|bitclear|bitwrite|xor|and|or";
-enum class bitwise_functions_e {
- bitread,
- bitset,
- bitclear,
- bitwrite,
- xor_e, // protected keywords, thus appended _e
- and_e,
- or_e
-};
-
-
-bool parse_bitwise_functions(const String& cmd_s_lower, const String& arg1, const String& arg2, const String& arg3, int64_t& result) {
- #ifndef BUILD_NO_DEBUG
- if (loglevelActiveFor(LOG_LEVEL_DEBUG)) {
- String log = F("Bitwise: {");
- log += wrapIfContains(cmd_s_lower, ':', '\"');
- log += ':';
- log += wrapIfContains(arg1, ':', '\"');
-
- if (arg2.length() > 0) {
- log += ':';
- log += wrapIfContains(arg2, ':', '\"');
-
- if (arg3.length() > 0) {
- log += ':';
- log += wrapIfContains(arg3, ':', '\"');
- }
- }
- log += '}';
- addLogMove(LOG_LEVEL_DEBUG, log);
- }
- #endif
-
- if (cmd_s_lower.length() < 2) {
- return false;
- }
-
- int command_i = GetCommandCode(cmd_s_lower.c_str(), bitwise_functions);
- if (command_i == -1) {
- // No matching function found
- return false;
- }
-
- if (cmd_s_lower.startsWith(F("bit"))) {
- uint32_t bitnr = 0;
- uint64_t iarg2 = 0;
-
- if (!validUIntFromString(arg1, bitnr) || !validUInt64FromString(arg2, iarg2)) {
- return false;
- }
-
- switch(static_cast(command_i)) {
- case bitwise_functions_e::bitread:
- // Syntax like {bitread:0:123} to get a single decimal '1'
- result = bitRead(iarg2, bitnr);
- break;
- case bitwise_functions_e::bitset:
- // Syntax like {bitset:0:122} to set least significant bit of the given nr '122' to '1' => '123'
- result = iarg2;
- bitSetULL(result, bitnr);
- break;
- case bitwise_functions_e::bitclear:
- // Syntax like {bitclear:0:123} to set least significant bit of the given nr '123' to '0' => '122'
- result = iarg2;
- bitClearULL(result, bitnr);
- break;
- case bitwise_functions_e::bitwrite:
- {
- uint32_t iarg3 = 0;
- // Syntax like {bitwrite:0:122:1} to set least significant bit of the given nr '122' to '1' => '123'
- if (validUIntFromString(arg3, iarg3)) {
- const int bitvalue = (iarg3 & 1); // Only use the last bit of the given parameter
- result = iarg2;
- bitWriteULL(result, bitnr, bitvalue);
- } else {
- // Need 3 parameters, but 3rd one is not a valid uint
- return false;
- }
- break;
- }
- default:
- return false;
- }
-
- // all functions starting with "bit" are checked
- return true;
- }
-
- uint64_t iarg1, iarg2 = 0;
-
- if (!validUInt64FromString(arg1, iarg1) || !validUInt64FromString(arg2, iarg2)) {
- return false;
- }
-
- switch(static_cast(command_i)) {
- case bitwise_functions_e::xor_e:
- // Syntax like {xor:127:15} to XOR the binary values 1111111 and 1111 => 1110000
- result = iarg1 ^ iarg2;
- break;
- case bitwise_functions_e::and_e:
- // Syntax like {and:254:15} to AND the binary values 11111110 and 1111 => 1110
- result = iarg1 & iarg2;
- break;
- case bitwise_functions_e::or_e:
- // Syntax like {or:254:15} to OR the binary values 11111110 and 1111 => 11111111
- result = iarg1 | iarg2;
- break;
- default:
- return false;
-
- }
- return true;
-}
-
-bool parse_math_functions(const String& cmd_s_lower, const String& arg1, const String& arg2, const String& arg3, ESPEASY_RULES_FLOAT_TYPE& result) {
- ESPEASY_RULES_FLOAT_TYPE farg1;
- float farg2, farg3 = 0.0f;
-
- if (!validDoubleFromString(arg1, farg1)) {
- return false;
- }
-
- if (equals(cmd_s_lower, F("constrain"))) {
- // Contrain a value X to be within range of A to B
- // Syntax like {constrain:x:a:b} to constrain x in range a...b
- if (validFloatFromString(arg2, farg2) && validFloatFromString(arg3, farg3)) {
- if (farg2 > farg3) {
- const float tmp = farg2;
- farg2 = farg3;
- farg3 = tmp;
- }
- result = constrain(farg1, farg2, farg3);
- } else {
- return false;
- }
- } else {
- // No matching function found
- return false;
- }
- return true;
-}
-
-const char string_commands[] PROGMEM = "substring|indexof|indexof_ci|equals|equals_ci|timetomin|timetosec|strtol|tobin|tohex|ord|urlencode";
-enum class string_commands_e {
- substring,
- indexof,
- indexof_ci,
- equals,
- equals_ci,
- timetomin,
- timetosec,
- strtol,
- tobin,
- tohex,
- ord,
- urlencode
-};
-
-
-void parse_string_commands(String& line) {
- int startIndex = 0;
- int closingIndex;
-
- bool mustReplaceMaskedChars = false;
-
- while (get_next_inner_bracket(line, startIndex, closingIndex, '}')) {
- // Command without opening and closing brackets.
- const String fullCommand = line.substring(startIndex + 1, closingIndex);
- const String cmd_s_lower = parseString(fullCommand, 1, ':');
- const String arg1 = parseStringKeepCaseNoTrim(fullCommand, 2, ':');
- const String arg2 = parseStringKeepCaseNoTrim(fullCommand, 3, ':');
- const String arg3 = parseStringKeepCaseNoTrim(fullCommand, 4, ':');
-
- if (cmd_s_lower.length() > 0) {
- String replacement; // maybe just replace with empty to avoid looping?
- uint64_t iarg1, iarg2 = 0;
- ESPEASY_RULES_FLOAT_TYPE fresult{};
- int64_t iresult = 0;
- int32_t startpos, endpos = -1;
- const bool arg1valid = validIntFromString(arg1, startpos);
- const bool arg2valid = validIntFromString(arg2, endpos);
-
- if (parse_math_functions(cmd_s_lower, arg1, arg2, arg3, fresult)) {
- const bool trimTrailingZeros = true;
- #if FEATURE_USE_DOUBLE_AS_ESPEASY_RULES_FLOAT_TYPE
- replacement = doubleToString(fresult, maxNrDecimals_fpType(fresult), trimTrailingZeros);
- #else
- replacement = floatToString(fresult, maxNrDecimals_fpType(fresult), trimTrailingZeros);
- #endif
- } else if (parse_bitwise_functions(cmd_s_lower, arg1, arg2, arg3, iresult)) {
- replacement = ull2String(iresult);
- } else {
-
- int command_i = GetCommandCode(cmd_s_lower.c_str(), string_commands);
- if (command_i != -1) {
- const string_commands_e command = static_cast