From cb0df141f487e0e0c7d0dface90530ab5931da88 Mon Sep 17 00:00:00 2001
From: Jason2866 <24528715+Jason2866@users.noreply.github.com>
Date: Tue, 1 Sep 2020 17:24:36 +0200
Subject: [PATCH 001/148] Squashed commit of the following:
commit 0917b430c3b76e26fad584ddacb1fc389e1b4c36
Author: Jason2866 <24528715+Jason2866@users.noreply.github.com>
Date: Tue Sep 1 17:13:53 2020 +0200
Domoticz
commit e878ae5ac4e2b72443598d699f76f1efbe90b5c2
Merge: 77957d70 46789ef4
Author: Jason2866 <24528715+Jason2866@users.noreply.github.com>
Date: Tue Sep 1 13:24:37 2020 +0200
Merge pull request #118 from device111/vl53l1x
VL53L1X correction
commit 46789ef40b6b8a3fbf730ed69712e0790b876075
Author: device111 <48546979+device111@users.noreply.github.com>
Date: Sun Aug 30 11:41:32 2020 +0200
Update support_features.ino
commit 8f8720d37e5d81d6c483620a83936b2f5ffd9f43
Author: device111 <48546979+device111@users.noreply.github.com>
Date: Sun Aug 30 11:36:24 2020 +0200
VL53L1X correction
sorry, but i have killed all of my repos..:-(
commit 77957d70435e99bb42b64b39acb7b7a176ecac79
Author: Jason2866 <24528715+Jason2866@users.noreply.github.com>
Date: Fri Aug 28 13:54:25 2020 +0200
use registry
commit d85954db359fb789c685fbf1ceb6d80fadc5132c
Author: Jason2866 <24528715+Jason2866@users.noreply.github.com>
Date: Thu Aug 27 14:43:20 2020 +0200
No DOMOTICZ
commit e6d763f10b0d34420f908382442af21581358a84
Merge: 788e4681 6c5fdb4d
Author: Jason2866 <24528715+Jason2866@users.noreply.github.com>
Date: Thu Aug 27 14:42:29 2020 +0200
Merge remote-tracking branch 'Tasmota/development' into vl53l1x
commit 788e468145520f3832255d92bf50d0ee70c18e0c
Merge: 51491df1 33f3f9ef
Author: Jason2866 <24528715+Jason2866@users.noreply.github.com>
Date: Wed Aug 26 14:50:28 2020 +0200
Merge remote-tracking branch 'Tasmota/development' into vl53l1x
commit 51491df1419fa50d1327565b61a40ca5f81a99d6
Author: Jason2866 <24528715+Jason2866@users.noreply.github.com>
Date: Wed Aug 26 14:49:56 2020 +0200
Real 0x52 is 0x29 in Arduino 7 bit
commit 00dad36b086b03a93dd02ffa28e5d097ba479f10
Author: Jason2866 <24528715+Jason2866@users.noreply.github.com>
Date: Mon Aug 24 11:41:11 2020 +0200
1.01
commit 50e88038c3de477f6b657b3d3caa5e53992ca838
Author: Jason2866 <24528715+Jason2866@users.noreply.github.com>
Date: Sun Aug 23 20:04:47 2020 +0200
correct support_feature
commit e5baabc41c1c016066a0ba9d3a8d8006cadd32ec
Author: Jason2866 <24528715+Jason2866@users.noreply.github.com>
Date: Sun Aug 23 19:53:15 2020 +0200
VL53L1X
---
I2CDEVICES.md | 3 +-
lib/vl53l1x-arduino-1.01/LICENSE.txt | 42 +
lib/vl53l1x-arduino-1.01/README.md | 167 ++
lib/vl53l1x-arduino-1.01/VL53L1X.cpp | 788 ++++++++++
lib/vl53l1x-arduino-1.01/VL53L1X.h | 1384 +++++++++++++++++
.../examples/Continuous/Continuous.ino | 44 +
.../ContinuousWithDetails.ino | 55 +
lib/vl53l1x-arduino-1.01/keywords.txt | 41 +
lib/vl53l1x-arduino-1.01/library.properties | 9 +
tasmota/my_user_config.h | 1 +
tasmota/support_features.ino | 4 +-
tasmota/tasmota_configurations.h | 1 +
tasmota/xsns_77_vl53l1x.ino | 128 ++
13 files changed, 2665 insertions(+), 2 deletions(-)
create mode 100644 lib/vl53l1x-arduino-1.01/LICENSE.txt
create mode 100644 lib/vl53l1x-arduino-1.01/README.md
create mode 100644 lib/vl53l1x-arduino-1.01/VL53L1X.cpp
create mode 100644 lib/vl53l1x-arduino-1.01/VL53L1X.h
create mode 100644 lib/vl53l1x-arduino-1.01/examples/Continuous/Continuous.ino
create mode 100644 lib/vl53l1x-arduino-1.01/examples/ContinuousWithDetails/ContinuousWithDetails.ino
create mode 100644 lib/vl53l1x-arduino-1.01/keywords.txt
create mode 100644 lib/vl53l1x-arduino-1.01/library.properties
create mode 100644 tasmota/xsns_77_vl53l1x.ino
diff --git a/I2CDEVICES.md b/I2CDEVICES.md
index 457116347..60e43db44 100644
--- a/I2CDEVICES.md
+++ b/I2CDEVICES.md
@@ -74,4 +74,5 @@ Index | Define | Driver | Device | Address(es) | Description
49 | USE_VEML6075 | xsns_70 | VEML6075 | 0x10 | UVA/UVB/UVINDEX Sensor
50 | USE_VEML7700 | xsns_71 | VEML7700 | 0x10 | Ambient light intensity sensor
51 | USE_MCP9808 | xsns_72 | MCP9808 | 0x18 - 0x1F | Temperature sensor
- 52 | USE_HP303B | xsns_73 | HP303B | 0x76 - 0x77 | Pressure and temperature sensor
\ No newline at end of file
+ 52 | USE_HP303B | xsns_73 | HP303B | 0x76 - 0x77 | Pressure and temperature sensor
+ 53 | USE_VL53L1X | xsns_77 | VL53L1X | 0x29 | Time-of-flight (ToF) distance sensor
\ No newline at end of file
diff --git a/lib/vl53l1x-arduino-1.01/LICENSE.txt b/lib/vl53l1x-arduino-1.01/LICENSE.txt
new file mode 100644
index 000000000..8a2abe9ed
--- /dev/null
+++ b/lib/vl53l1x-arduino-1.01/LICENSE.txt
@@ -0,0 +1,42 @@
+Most of the functionality of this library is based on the VL53L1X API provided
+provided by ST (STSW-IMG007), and some of the explanatory comments are quoted
+or paraphrased from the API source code, API user manual (UM2356), and VL53L1X
+datasheet. Therefore, the license terms for the API source code (BSD 3-clause
+"New" or "Revised" License) also apply to this derivative work, as specified
+below.
+
+For more information, see
+
+https://www.pololu.com/
+https://forum.pololu.com/
+
+--------------------------------------------------------------------------------
+
+Copyright (c) 2017, STMicroelectronics
+Copyright (c) 2018, Pololu Corporation
+All Rights Reserved
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this
+list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright notice,
+this list of conditions and the following disclaimer in the documentation
+and/or other materials provided with the distribution.
+
+3. Neither the name of the copyright holder nor the names of its contributors
+may be used to endorse or promote products derived from this software
+without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/lib/vl53l1x-arduino-1.01/README.md b/lib/vl53l1x-arduino-1.01/README.md
new file mode 100644
index 000000000..41ec21d9f
--- /dev/null
+++ b/lib/vl53l1x-arduino-1.01/README.md
@@ -0,0 +1,167 @@
+# VL53L1X library for Arduino
+
+Version: 1.0.1
+Release date: 2018-09-19
+[](https://travis-ci.org/pololu/vl53l1x-arduino)
+[www.pololu.com](https://www.pololu.com/)
+
+## Summary
+
+This is a library for the Arduino IDE that helps interface with ST's [VL53L1X time-of-flight distance sensor](https://www.pololu.com/product/3415). The library makes it simple to configure the sensor and read range data from it via I²C.
+
+## Supported platforms
+
+This library is designed to work with the Arduino IDE versions 1.6.x or later; we have not tested it with earlier versions. This library should support any Arduino-compatible board, including the [Pololu A-Star controllers](https://www.pololu.com/category/149/a-star-programmable-controllers).
+
+## Getting started
+
+### Hardware
+
+A [VL53L1X carrier](https://www.pololu.com/product/3415) can be purchased from Pololu's website. Before continuing, careful reading of the [product page](https://www.pololu.com/product/3415) as well as the VL53L1X datasheet is recommended.
+
+Make the following connections between the Arduino and the VL53L1X board:
+
+#### 5V Arduino boards
+
+(including Arduino Uno, Leonardo, Mega; Pololu A-Star 32U4)
+
+ Arduino VL53L1X board
+ ------- -------------
+ 5V - VIN
+ GND - GND
+ SDA - SDA
+ SCL - SCL
+
+#### 3.3V Arduino boards
+
+(including Arduino Due)
+
+ Arduino VL53L1X board
+ ------- -------------
+ 3V3 - VIN
+ GND - GND
+ SDA - SDA
+ SCL - SCL
+
+### Software
+
+If you are using version 1.6.2 or later of the [Arduino software (IDE)](http://www.arduino.cc/en/Main/Software), you can use the Library Manager to install this library:
+
+1. In the Arduino IDE, open the "Sketch" menu, select "Include Library", then "Manage Libraries...".
+2. Search for "VL53L1X".
+3. Click the VL53L1X entry in the list.
+4. Click "Install".
+
+If this does not work, you can manually install the library:
+
+1. Download the [latest release archive from GitHub](https://github.com/pololu/vl53l1x-arduino/releases) and decompress it.
+2. Rename the folder "vl53l1x-arduino-master" to "VL53L1X".
+3. Move the "VL53L1X" folder into the "libraries" directory inside your Arduino sketchbook directory. You can view your sketchbook location by opening the "File" menu and selecting "Preferences" in the Arduino IDE. If there is not already a "libraries" folder in that location, you should make the folder yourself.
+4. After installing the library, restart the Arduino IDE.
+
+## Examples
+
+Several example sketches are available that show how to use the library. You can access them from the Arduino IDE by opening the "File" menu, selecting "Examples", and then selecting "VL53L1X". If you cannot find these examples, the library was probably installed incorrectly and you should retry the installation instructions above.
+
+## ST's VL53L1X API and this library
+
+Most of the functionality of this library is based on the [VL53L1X API](http://www.st.com/content/st_com/en/products/embedded-software/proximity-sensors-software/stsw-img007.html) provided by ST (STSW-IMG007), and some of the explanatory comments in the code are quoted or paraphrased from the API source code, API user manual (UM2356), and the VL53L1X datasheet. For more explanation about the library code and how it was derived from the API, see the comments in VL53L1X.cpp.
+
+This library is intended to provide a quicker and easier way to get started using the VL53L1X with an Arduino-compatible controller, in contrast to using ST's API on the Arduino. The library has a more streamlined interface, as well as smaller storage and memory footprints. However, it does not currently implement some of the more advanced functionality available in the API (for example, calibrating the sensor to work well under a cover glass or selecting a smaller region of interest (ROI)), and it has less robust error checking. For advanced applications, especially when storage and memory are less of an issue, consider using the VL53L1X API directly. We have an [implementation of ST's VL53L1X API for Arduino](https://github.com/pololu/vl53l1x-st-api-arduino) available.
+
+## Library reference
+
+* `RangingData ranging_data`
+ This struct contains information about the last ranging measurement. Its members are:
+ * `uint16_t range_mm`
+ Range reading from the last measurement, in millimeters. (This reading can also be obtained as the return value of `read()`.)
+ * `RangeStatus range_status`
+ Status of the last measurement; see the definition of the `RangeStatus` enumeration type in VL53L1X.h (or the API user manual and source code) for descriptions of the possible statuses. A status of `VL53L1X::RangeValid` means there were no problems with the measurement.
+ * `float peak_signal_count_rate_MCPS`
+ Peak signal count rate of the last measurement, in units of mega counts per second.
+ * `float ambient_count_rate_MCPS`
+ Ambient count rate of the last measurement, in units of mega counts per second.
+
+* `uint8_t last_status`
+ The status of the last I²C write transmission. See the [`Wire.endTransmission()` documentation](http://arduino.cc/en/Reference/WireEndTransmission) for return values.
+
+* `VL53L1X()`
+ Constructor.
+
+* `void setAddress(uint8_t new_addr)`
+ Changes the I²C slave device address of the VL53L1X to the given value (7-bit).
+
+* `uint8_t getAddress()`
+ Returns the current I²C address.
+
+* `bool init(bool io_2v8 = true)`
+ Iniitializes and configures the sensor. If the optional argument `io_2v8` is true (the default if not specified), the sensor is configured for 2V8 mode (2.8 V I/O); if false, the sensor is left in 1V8 mode. The return value is a boolean indicating whether the initialization completed successfully.
+
+* `void writeReg(uint16_t reg, uint8_t value)`
+ Writes an 8-bit sensor register with the given value.
+
+ Register address constants are defined by the `regAddr` enumeration type in VL53L1X.h.
+ Example use: `sensor.writeReg(VL53L1X::SOFT_RESET, 0x00);`
+
+* `void writeReg16Bit(uint16_t reg, uint16_t value)`
+ Writes a 16-bit sensor register with the given value.
+
+* `void writeReg32Bit(uint16_t reg, uint32_t value)`
+ Writes a 32-bit sensor register with the given value.
+
+* `uint8_t readReg(uint16_t reg)`
+ Reads an 8-bit sensor register and returns the value read.
+
+* `uint16_t readReg16Bit(uint16_t reg)`
+ Reads a 16-bit sensor register and returns the value read.
+
+* `uint32_t readReg32Bit(uint16_t reg)`
+ Reads a 32-bit sensor register and returns the value read.
+
+* `bool setDistanceMode(DistanceMode mode)`
+ Sets the distance mode of the sensor (`VL53L1X::Short`, `VL53L1X::Medium`, or `VL53L1X::Long`). Shorter distance modes are less affected by ambient light but have lower maximum ranges. See the datasheet for more information. The return value is a boolean indicating whether the requested mode was valid.
+
+* `DistanceMode getDistanceMode()`
+ Returns the previously set distance mode.
+
+* `bool setMeasurementTimingBudget(uint32_t budget_us)`
+ Sets the measurement timing budget to the given value in microseconds. This is the time allowed for one range measurement; a longer timing budget allows for more accurate measurements. The minimum budget is 20 ms (20000 us) in short distance mode and 33 ms for medium and long distance modes. See the VL53L1X datasheet for more information on range and timing limits. The return value is a boolean indicating whether the requested budget was valid.
+
+* `uint32_t getMeasurementTimingBudget()`
+ Returns the current measurement timing budget in microseconds.
+
+* `void startContinuous(uint32_t period_ms)`
+ Starts continuous ranging measurements. The specified inter-measurement period in milliseconds determines how often the sensor takes a measurement; if it is shorter than the timing budget, the sensor will start a new measurement as soon as the previous one finishes.
+
+* `void stopContinuous()`
+ Stops continuous mode.
+
+* `uint16_t read(bool blocking = true)`
+ After continuous ranging measurements have been started, calling this function returns a range reading in millimeters and updates the `ranging_data` struct with details about the last measurement. If the optional argument `blocking` is true (the default if not specified), this function will wait until data from a new measurement is available before returning.
+
+ If you do not want this function to block, you can use the `dataReady()` function to check if new data is available before calling `read(false)`. Calling `read(false)` before new data is available will return a reading of 0, and `ranging_data.range_status` will have the value `VL53L1X::None`, indicating that there has been no update.
+
+* `uint16_t readRangeContinuousMillimeters(bool blocking = true)`
+ Alias of `read()` for convenience.
+
+* `bool dataReady()`
+ Returns a boolean indicating whether data from a new measurement is available from the sensor.
+
+* `static const char * rangeStatusToString(RangeStatus status)`
+ Converts a `RangeStatus` into a readable string describing that status.
+
+ Note that on an AVR, the strings in this function are stored in RAM (dynamic memory), which makes working with them easier but uses up 200+ bytes of RAM (many AVR-based Arduinos only have about 2000 bytes of RAM). You can avoid this memory usage if you do not call this function in your sketch.
+
+* `void setTimeout(uint16_t timeout)`
+ Sets a timeout period in milliseconds after which read operations will abort if the sensor is not ready. A value of 0 disables the timeout.
+
+* `uint16_t getTimeout()`
+ Returns the current timeout period setting.
+
+* `bool timeoutOccurred()`
+ Indicates whether a read timeout has occurred since the last call to `timeoutOccurred()`.
+
+## Version history
+
+* 1.0.1 (2018-09-19): Fix Arduino 101 hanging in init().
+* 1.0.0 (2018-05-31): Original release.
diff --git a/lib/vl53l1x-arduino-1.01/VL53L1X.cpp b/lib/vl53l1x-arduino-1.01/VL53L1X.cpp
new file mode 100644
index 000000000..78d93e2d2
--- /dev/null
+++ b/lib/vl53l1x-arduino-1.01/VL53L1X.cpp
@@ -0,0 +1,788 @@
+// Most of the functionality of this library is based on the VL53L1X API
+// provided by ST (STSW-IMG007), and some of the explanatory comments are quoted
+// or paraphrased from the API source code, API user manual (UM2356), and
+// VL53L1X datasheet.
+
+#include
+#include
+
+// Constructors ////////////////////////////////////////////////////////////////
+
+VL53L1X::VL53L1X()
+ : address(AddressDefault)
+ , io_timeout(0) // no timeout
+ , did_timeout(false)
+ , calibrated(false)
+ , saved_vhv_init(0)
+ , saved_vhv_timeout(0)
+ , distance_mode(Unknown)
+{
+}
+
+// Public Methods //////////////////////////////////////////////////////////////
+
+void VL53L1X::setAddress(uint8_t new_addr)
+{
+ writeReg(I2C_SLAVE__DEVICE_ADDRESS, new_addr & 0x7F);
+ address = new_addr;
+}
+
+// Initialize sensor using settings taken mostly from VL53L1_DataInit() and
+// VL53L1_StaticInit().
+// If io_2v8 (optional) is true or not given, the sensor is configured for 2V8
+// mode.
+bool VL53L1X::init(bool io_2v8)
+{
+ // check model ID and module type registers (values specified in datasheet)
+ if (readReg16Bit(IDENTIFICATION__MODEL_ID) != 0xEACC) { return false; }
+
+ // VL53L1_software_reset() begin
+
+ writeReg(SOFT_RESET, 0x00);
+ delayMicroseconds(100);
+ writeReg(SOFT_RESET, 0x01);
+
+ // give it some time to boot; otherwise the sensor NACKs during the readReg()
+ // call below and the Arduino 101 doesn't seem to handle that well
+ delay(1);
+
+ // VL53L1_poll_for_boot_completion() begin
+
+ startTimeout();
+
+ // check last_status in case we still get a NACK to try to deal with it correctly
+ while ((readReg(FIRMWARE__SYSTEM_STATUS) & 0x01) == 0 || last_status != 0)
+ {
+ if (checkTimeoutExpired())
+ {
+ did_timeout = true;
+ return false;
+ }
+ }
+ // VL53L1_poll_for_boot_completion() end
+
+ // VL53L1_software_reset() end
+
+ // VL53L1_DataInit() begin
+
+ // sensor uses 1V8 mode for I/O by default; switch to 2V8 mode if necessary
+ if (io_2v8)
+ {
+ writeReg(PAD_I2C_HV__EXTSUP_CONFIG,
+ readReg(PAD_I2C_HV__EXTSUP_CONFIG) | 0x01);
+ }
+
+ // store oscillator info for later use
+ fast_osc_frequency = readReg16Bit(OSC_MEASURED__FAST_OSC__FREQUENCY);
+ osc_calibrate_val = readReg16Bit(RESULT__OSC_CALIBRATE_VAL);
+
+ // VL53L1_DataInit() end
+
+ // VL53L1_StaticInit() begin
+
+ // Note that the API does not actually apply the configuration settings below
+ // when VL53L1_StaticInit() is called: it keeps a copy of the sensor's
+ // register contents in memory and doesn't actually write them until a
+ // measurement is started. Writing the configuration here means we don't have
+ // to keep it all in memory and avoids a lot of redundant writes later.
+
+ // the API sets the preset mode to LOWPOWER_AUTONOMOUS here:
+ // VL53L1_set_preset_mode() begin
+
+ // VL53L1_preset_mode_standard_ranging() begin
+
+ // values labeled "tuning parm default" are from vl53l1_tuning_parm_defaults.h
+ // (API uses these in VL53L1_init_tuning_parm_storage_struct())
+
+ // static config
+ // API resets PAD_I2C_HV__EXTSUP_CONFIG here, but maybe we don't want to do
+ // that? (seems like it would disable 2V8 mode)
+ writeReg16Bit(DSS_CONFIG__TARGET_TOTAL_RATE_MCPS, TargetRate); // should already be this value after reset
+ writeReg(GPIO__TIO_HV_STATUS, 0x02);
+ writeReg(SIGMA_ESTIMATOR__EFFECTIVE_PULSE_WIDTH_NS, 8); // tuning parm default
+ writeReg(SIGMA_ESTIMATOR__EFFECTIVE_AMBIENT_WIDTH_NS, 16); // tuning parm default
+ writeReg(ALGO__CROSSTALK_COMPENSATION_VALID_HEIGHT_MM, 0x01);
+ writeReg(ALGO__RANGE_IGNORE_VALID_HEIGHT_MM, 0xFF);
+ writeReg(ALGO__RANGE_MIN_CLIP, 0); // tuning parm default
+ writeReg(ALGO__CONSISTENCY_CHECK__TOLERANCE, 2); // tuning parm default
+
+ // general config
+ writeReg16Bit(SYSTEM__THRESH_RATE_HIGH, 0x0000);
+ writeReg16Bit(SYSTEM__THRESH_RATE_LOW, 0x0000);
+ writeReg(DSS_CONFIG__APERTURE_ATTENUATION, 0x38);
+
+ // timing config
+ // most of these settings will be determined later by distance and timing
+ // budget configuration
+ writeReg16Bit(RANGE_CONFIG__SIGMA_THRESH, 360); // tuning parm default
+ writeReg16Bit(RANGE_CONFIG__MIN_COUNT_RATE_RTN_LIMIT_MCPS, 192); // tuning parm default
+
+ // dynamic config
+
+ writeReg(SYSTEM__GROUPED_PARAMETER_HOLD_0, 0x01);
+ writeReg(SYSTEM__GROUPED_PARAMETER_HOLD_1, 0x01);
+ writeReg(SD_CONFIG__QUANTIFIER, 2); // tuning parm default
+
+ // VL53L1_preset_mode_standard_ranging() end
+
+ // from VL53L1_preset_mode_timed_ranging_*
+ // GPH is 0 after reset, but writing GPH0 and GPH1 above seem to set GPH to 1,
+ // and things don't seem to work if we don't set GPH back to 0 (which the API
+ // does here).
+ writeReg(SYSTEM__GROUPED_PARAMETER_HOLD, 0x00);
+ writeReg(SYSTEM__SEED_CONFIG, 1); // tuning parm default
+
+ // from VL53L1_config_low_power_auto_mode
+ writeReg(SYSTEM__SEQUENCE_CONFIG, 0x8B); // VHV, PHASECAL, DSS1, RANGE
+ writeReg16Bit(DSS_CONFIG__MANUAL_EFFECTIVE_SPADS_SELECT, 200 << 8);
+ writeReg(DSS_CONFIG__ROI_MODE_CONTROL, 2); // REQUESTED_EFFFECTIVE_SPADS
+
+ // VL53L1_set_preset_mode() end
+
+ // default to long range, 50 ms timing budget
+ // note that this is different than what the API defaults to
+ setDistanceMode(Long);
+ setMeasurementTimingBudget(50000);
+
+ // VL53L1_StaticInit() end
+
+ // the API triggers this change in VL53L1_init_and_start_range() once a
+ // measurement is started; assumes MM1 and MM2 are disabled
+ writeReg16Bit(ALGO__PART_TO_PART_RANGE_OFFSET_MM,
+ readReg16Bit(MM_CONFIG__OUTER_OFFSET_MM) * 4);
+
+ return true;
+}
+
+// Write an 8-bit register
+void VL53L1X::writeReg(uint16_t reg, uint8_t value)
+{
+ Wire.beginTransmission(address);
+ Wire.write((reg >> 8) & 0xFF); // reg high byte
+ Wire.write( reg & 0xFF); // reg low byte
+ Wire.write(value);
+ last_status = Wire.endTransmission();
+}
+
+// Write a 16-bit register
+void VL53L1X::writeReg16Bit(uint16_t reg, uint16_t value)
+{
+ Wire.beginTransmission(address);
+ Wire.write((reg >> 8) & 0xFF); // reg high byte
+ Wire.write( reg & 0xFF); // reg low byte
+ Wire.write((value >> 8) & 0xFF); // value high byte
+ Wire.write( value & 0xFF); // value low byte
+ last_status = Wire.endTransmission();
+}
+
+// Write a 32-bit register
+void VL53L1X::writeReg32Bit(uint16_t reg, uint32_t value)
+{
+ Wire.beginTransmission(address);
+ Wire.write((reg >> 8) & 0xFF); // reg high byte
+ Wire.write( reg & 0xFF); // reg low byte
+ Wire.write((value >> 24) & 0xFF); // value highest byte
+ Wire.write((value >> 16) & 0xFF);
+ Wire.write((value >> 8) & 0xFF);
+ Wire.write( value & 0xFF); // value lowest byte
+ last_status = Wire.endTransmission();
+}
+
+// Read an 8-bit register
+uint8_t VL53L1X::readReg(regAddr reg)
+{
+ uint8_t value;
+
+ Wire.beginTransmission(address);
+ Wire.write((reg >> 8) & 0xFF); // reg high byte
+ Wire.write( reg & 0xFF); // reg low byte
+ last_status = Wire.endTransmission();
+
+ Wire.requestFrom(address, (uint8_t)1);
+ value = Wire.read();
+
+ return value;
+}
+
+// Read a 16-bit register
+uint16_t VL53L1X::readReg16Bit(uint16_t reg)
+{
+ uint16_t value;
+
+ Wire.beginTransmission(address);
+ Wire.write((reg >> 8) & 0xFF); // reg high byte
+ Wire.write( reg & 0xFF); // reg low byte
+ last_status = Wire.endTransmission();
+
+ Wire.requestFrom(address, (uint8_t)2);
+ value = (uint16_t)Wire.read() << 8; // value high byte
+ value |= Wire.read(); // value low byte
+
+ return value;
+}
+
+// Read a 32-bit register
+uint32_t VL53L1X::readReg32Bit(uint16_t reg)
+{
+ uint32_t value;
+
+ Wire.beginTransmission(address);
+ Wire.write((reg >> 8) & 0xFF); // reg high byte
+ Wire.write( reg & 0xFF); // reg low byte
+ last_status = Wire.endTransmission();
+
+ Wire.requestFrom(address, (uint8_t)4);
+ value = (uint32_t)Wire.read() << 24; // value highest byte
+ value |= (uint32_t)Wire.read() << 16;
+ value |= (uint16_t)Wire.read() << 8;
+ value |= Wire.read(); // value lowest byte
+
+ return value;
+}
+
+// set distance mode to Short, Medium, or Long
+// based on VL53L1_SetDistanceMode()
+bool VL53L1X::setDistanceMode(DistanceMode mode)
+{
+ // save existing timing budget
+ uint32_t budget_us = getMeasurementTimingBudget();
+
+ switch (mode)
+ {
+ case Short:
+ // from VL53L1_preset_mode_standard_ranging_short_range()
+
+ // timing config
+ writeReg(RANGE_CONFIG__VCSEL_PERIOD_A, 0x07);
+ writeReg(RANGE_CONFIG__VCSEL_PERIOD_B, 0x05);
+ writeReg(RANGE_CONFIG__VALID_PHASE_HIGH, 0x38);
+
+ // dynamic config
+ writeReg(SD_CONFIG__WOI_SD0, 0x07);
+ writeReg(SD_CONFIG__WOI_SD1, 0x05);
+ writeReg(SD_CONFIG__INITIAL_PHASE_SD0, 6); // tuning parm default
+ writeReg(SD_CONFIG__INITIAL_PHASE_SD1, 6); // tuning parm default
+
+ break;
+
+ case Medium:
+ // from VL53L1_preset_mode_standard_ranging()
+
+ // timing config
+ writeReg(RANGE_CONFIG__VCSEL_PERIOD_A, 0x0B);
+ writeReg(RANGE_CONFIG__VCSEL_PERIOD_B, 0x09);
+ writeReg(RANGE_CONFIG__VALID_PHASE_HIGH, 0x78);
+
+ // dynamic config
+ writeReg(SD_CONFIG__WOI_SD0, 0x0B);
+ writeReg(SD_CONFIG__WOI_SD1, 0x09);
+ writeReg(SD_CONFIG__INITIAL_PHASE_SD0, 10); // tuning parm default
+ writeReg(SD_CONFIG__INITIAL_PHASE_SD1, 10); // tuning parm default
+
+ break;
+
+ case Long: // long
+ // from VL53L1_preset_mode_standard_ranging_long_range()
+
+ // timing config
+ writeReg(RANGE_CONFIG__VCSEL_PERIOD_A, 0x0F);
+ writeReg(RANGE_CONFIG__VCSEL_PERIOD_B, 0x0D);
+ writeReg(RANGE_CONFIG__VALID_PHASE_HIGH, 0xB8);
+
+ // dynamic config
+ writeReg(SD_CONFIG__WOI_SD0, 0x0F);
+ writeReg(SD_CONFIG__WOI_SD1, 0x0D);
+ writeReg(SD_CONFIG__INITIAL_PHASE_SD0, 14); // tuning parm default
+ writeReg(SD_CONFIG__INITIAL_PHASE_SD1, 14); // tuning parm default
+
+ break;
+
+ default:
+ // unrecognized mode - do nothing
+ return false;
+ }
+
+ // reapply timing budget
+ setMeasurementTimingBudget(budget_us);
+
+ // save mode so it can be returned by getDistanceMode()
+ distance_mode = mode;
+
+ return true;
+}
+
+// Set the measurement timing budget in microseconds, which is the time allowed
+// for one measurement. A longer timing budget allows for more accurate
+// measurements.
+// based on VL53L1_SetMeasurementTimingBudgetMicroSeconds()
+bool VL53L1X::setMeasurementTimingBudget(uint32_t budget_us)
+{
+ // assumes PresetMode is LOWPOWER_AUTONOMOUS
+
+ if (budget_us <= TimingGuard) { return false; }
+
+ uint32_t range_config_timeout_us = budget_us -= TimingGuard;
+ if (range_config_timeout_us > 1100000) { return false; } // FDA_MAX_TIMING_BUDGET_US * 2
+
+ range_config_timeout_us /= 2;
+
+ // VL53L1_calc_timeout_register_values() begin
+
+ uint32_t macro_period_us;
+
+ // "Update Macro Period for Range A VCSEL Period"
+ macro_period_us = calcMacroPeriod(readReg(RANGE_CONFIG__VCSEL_PERIOD_A));
+
+ // "Update Phase timeout - uses Timing A"
+ // Timeout of 1000 is tuning parm default (TIMED_PHASECAL_CONFIG_TIMEOUT_US_DEFAULT)
+ // via VL53L1_get_preset_mode_timing_cfg().
+ uint32_t phasecal_timeout_mclks = timeoutMicrosecondsToMclks(1000, macro_period_us);
+ if (phasecal_timeout_mclks > 0xFF) { phasecal_timeout_mclks = 0xFF; }
+ writeReg(PHASECAL_CONFIG__TIMEOUT_MACROP, phasecal_timeout_mclks);
+
+ // "Update MM Timing A timeout"
+ // Timeout of 1 is tuning parm default (LOWPOWERAUTO_MM_CONFIG_TIMEOUT_US_DEFAULT)
+ // via VL53L1_get_preset_mode_timing_cfg(). With the API, the register
+ // actually ends up with a slightly different value because it gets assigned,
+ // retrieved, recalculated with a different macro period, and reassigned,
+ // but it probably doesn't matter because it seems like the MM ("mode
+ // mitigation"?) sequence steps are disabled in low power auto mode anyway.
+ writeReg16Bit(MM_CONFIG__TIMEOUT_MACROP_A, encodeTimeout(
+ timeoutMicrosecondsToMclks(1, macro_period_us)));
+
+ // "Update Range Timing A timeout"
+ writeReg16Bit(RANGE_CONFIG__TIMEOUT_MACROP_A, encodeTimeout(
+ timeoutMicrosecondsToMclks(range_config_timeout_us, macro_period_us)));
+
+ // "Update Macro Period for Range B VCSEL Period"
+ macro_period_us = calcMacroPeriod(readReg(RANGE_CONFIG__VCSEL_PERIOD_B));
+
+ // "Update MM Timing B timeout"
+ // (See earlier comment about MM Timing A timeout.)
+ writeReg16Bit(MM_CONFIG__TIMEOUT_MACROP_B, encodeTimeout(
+ timeoutMicrosecondsToMclks(1, macro_period_us)));
+
+ // "Update Range Timing B timeout"
+ writeReg16Bit(RANGE_CONFIG__TIMEOUT_MACROP_B, encodeTimeout(
+ timeoutMicrosecondsToMclks(range_config_timeout_us, macro_period_us)));
+
+ // VL53L1_calc_timeout_register_values() end
+
+ return true;
+}
+
+// Get the measurement timing budget in microseconds
+// based on VL53L1_SetMeasurementTimingBudgetMicroSeconds()
+uint32_t VL53L1X::getMeasurementTimingBudget()
+{
+ // assumes PresetMode is LOWPOWER_AUTONOMOUS and these sequence steps are
+ // enabled: VHV, PHASECAL, DSS1, RANGE
+
+ // VL53L1_get_timeouts_us() begin
+
+ // "Update Macro Period for Range A VCSEL Period"
+ uint32_t macro_period_us = calcMacroPeriod(readReg(RANGE_CONFIG__VCSEL_PERIOD_A));
+
+ // "Get Range Timing A timeout"
+
+ uint32_t range_config_timeout_us = timeoutMclksToMicroseconds(decodeTimeout(
+ readReg16Bit(RANGE_CONFIG__TIMEOUT_MACROP_A)), macro_period_us);
+
+ // VL53L1_get_timeouts_us() end
+
+ return 2 * range_config_timeout_us + TimingGuard;
+}
+
+// Start continuous ranging measurements, with the given inter-measurement
+// period in milliseconds determining how often the sensor takes a measurement.
+void VL53L1X::startContinuous(uint32_t period_ms)
+{
+ // from VL53L1_set_inter_measurement_period_ms()
+ writeReg32Bit(SYSTEM__INTERMEASUREMENT_PERIOD, period_ms * osc_calibrate_val);
+
+ writeReg(SYSTEM__INTERRUPT_CLEAR, 0x01); // sys_interrupt_clear_range
+ writeReg(SYSTEM__MODE_START, 0x40); // mode_range__timed
+}
+
+// Stop continuous measurements
+// based on VL53L1_stop_range()
+void VL53L1X::stopContinuous()
+{
+ writeReg(SYSTEM__MODE_START, 0x80); // mode_range__abort
+
+ // VL53L1_low_power_auto_data_stop_range() begin
+
+ calibrated = false;
+
+ // "restore vhv configs"
+ if (saved_vhv_init != 0)
+ {
+ writeReg(VHV_CONFIG__INIT, saved_vhv_init);
+ }
+ if (saved_vhv_timeout != 0)
+ {
+ writeReg(VHV_CONFIG__TIMEOUT_MACROP_LOOP_BOUND, saved_vhv_timeout);
+ }
+
+ // "remove phasecal override"
+ writeReg(PHASECAL_CONFIG__OVERRIDE, 0x00);
+
+ // VL53L1_low_power_auto_data_stop_range() end
+}
+
+// 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 VL53L1X::read(bool blocking)
+{
+ if (blocking)
+ {
+ startTimeout();
+ while (!dataReady())
+ {
+ if (checkTimeoutExpired())
+ {
+ did_timeout = true;
+ ranging_data.range_status = None;
+ ranging_data.range_mm = 0;
+ ranging_data.peak_signal_count_rate_MCPS = 0;
+ ranging_data.ambient_count_rate_MCPS = 0;
+ return ranging_data.range_mm;
+ }
+ }
+ }
+
+ readResults();
+
+ if (!calibrated)
+ {
+ setupManualCalibration();
+ calibrated = true;
+ }
+
+ updateDSS();
+
+ getRangingData();
+
+ writeReg(SYSTEM__INTERRUPT_CLEAR, 0x01); // sys_interrupt_clear_range
+
+ return ranging_data.range_mm;
+}
+
+// convert a RangeStatus to a readable string
+// Note that on an AVR, these strings are stored in RAM (dynamic memory), which
+// makes working with them easier but uses up 200+ bytes of RAM (many AVR-based
+// Arduinos only have about 2000 bytes of RAM). You can avoid this memory usage
+// if you do not call this function in your sketch.
+const char * VL53L1X::rangeStatusToString(RangeStatus status)
+{
+ switch (status)
+ {
+ case RangeValid:
+ return "range valid";
+
+ case SigmaFail:
+ return "sigma fail";
+
+ case SignalFail:
+ return "signal fail";
+
+ case RangeValidMinRangeClipped:
+ return "range valid, min range clipped";
+
+ case OutOfBoundsFail:
+ return "out of bounds fail";
+
+ case HardwareFail:
+ return "hardware fail";
+
+ case RangeValidNoWrapCheckFail:
+ return "range valid, no wrap check fail";
+
+ case WrapTargetFail:
+ return "wrap target fail";
+
+ case XtalkSignalFail:
+ return "xtalk signal fail";
+
+ case SynchronizationInt:
+ return "synchronization int";
+
+ case MinRangeFail:
+ return "min range fail";
+
+ case None:
+ return "no update";
+
+ default:
+ return "unknown status";
+ }
+}
+
+// Did a timeout occur in one of the read functions since the last call to
+// timeoutOccurred()?
+bool VL53L1X::timeoutOccurred()
+{
+ bool tmp = did_timeout;
+ did_timeout = false;
+ return tmp;
+}
+
+// Private Methods /////////////////////////////////////////////////////////////
+
+// "Setup ranges after the first one in low power auto mode by turning off
+// FW calibration steps and programming static values"
+// based on VL53L1_low_power_auto_setup_manual_calibration()
+void VL53L1X::setupManualCalibration()
+{
+ // "save original vhv configs"
+ saved_vhv_init = readReg(VHV_CONFIG__INIT);
+ saved_vhv_timeout = readReg(VHV_CONFIG__TIMEOUT_MACROP_LOOP_BOUND);
+
+ // "disable VHV init"
+ writeReg(VHV_CONFIG__INIT, saved_vhv_init & 0x7F);
+
+ // "set loop bound to tuning param"
+ writeReg(VHV_CONFIG__TIMEOUT_MACROP_LOOP_BOUND,
+ (saved_vhv_timeout & 0x03) + (3 << 2)); // tuning parm default (LOWPOWERAUTO_VHV_LOOP_BOUND_DEFAULT)
+
+ // "override phasecal"
+ writeReg(PHASECAL_CONFIG__OVERRIDE, 0x01);
+ writeReg(CAL_CONFIG__VCSEL_START, readReg(PHASECAL_RESULT__VCSEL_START));
+}
+
+// read measurement results into buffer
+void VL53L1X::readResults()
+{
+ Wire.beginTransmission(address);
+ Wire.write((RESULT__RANGE_STATUS >> 8) & 0xFF); // reg high byte
+ Wire.write( RESULT__RANGE_STATUS & 0xFF); // reg low byte
+ last_status = Wire.endTransmission();
+
+ Wire.requestFrom(address, (uint8_t)17);
+
+ results.range_status = Wire.read();
+
+ Wire.read(); // report_status: not used
+
+ results.stream_count = Wire.read();
+
+ results.dss_actual_effective_spads_sd0 = (uint16_t)Wire.read() << 8; // high byte
+ results.dss_actual_effective_spads_sd0 |= Wire.read(); // low byte
+
+ Wire.read(); // peak_signal_count_rate_mcps_sd0: not used
+ Wire.read();
+
+ results.ambient_count_rate_mcps_sd0 = (uint16_t)Wire.read() << 8; // high byte
+ results.ambient_count_rate_mcps_sd0 |= Wire.read(); // low byte
+
+ Wire.read(); // sigma_sd0: not used
+ Wire.read();
+
+ Wire.read(); // phase_sd0: not used
+ Wire.read();
+
+ results.final_crosstalk_corrected_range_mm_sd0 = (uint16_t)Wire.read() << 8; // high byte
+ results.final_crosstalk_corrected_range_mm_sd0 |= Wire.read(); // low byte
+
+ results.peak_signal_count_rate_crosstalk_corrected_mcps_sd0 = (uint16_t)Wire.read() << 8; // high byte
+ results.peak_signal_count_rate_crosstalk_corrected_mcps_sd0 |= Wire.read(); // low byte
+}
+
+// perform Dynamic SPAD Selection calculation/update
+// based on VL53L1_low_power_auto_update_DSS()
+void VL53L1X::updateDSS()
+{
+ uint16_t spadCount = results.dss_actual_effective_spads_sd0;
+
+ if (spadCount != 0)
+ {
+ // "Calc total rate per spad"
+
+ uint32_t totalRatePerSpad =
+ (uint32_t)results.peak_signal_count_rate_crosstalk_corrected_mcps_sd0 +
+ results.ambient_count_rate_mcps_sd0;
+
+ // "clip to 16 bits"
+ if (totalRatePerSpad > 0xFFFF) { totalRatePerSpad = 0xFFFF; }
+
+ // "shift up to take advantage of 32 bits"
+ totalRatePerSpad <<= 16;
+
+ totalRatePerSpad /= spadCount;
+
+ if (totalRatePerSpad != 0)
+ {
+ // "get the target rate and shift up by 16"
+ uint32_t requiredSpads = ((uint32_t)TargetRate << 16) / totalRatePerSpad;
+
+ // "clip to 16 bit"
+ if (requiredSpads > 0xFFFF) { requiredSpads = 0xFFFF; }
+
+ // "override DSS config"
+ writeReg16Bit(DSS_CONFIG__MANUAL_EFFECTIVE_SPADS_SELECT, requiredSpads);
+ // DSS_CONFIG__ROI_MODE_CONTROL should already be set to REQUESTED_EFFFECTIVE_SPADS
+
+ return;
+ }
+ }
+
+ // If we reached this point, it means something above would have resulted in a
+ // divide by zero.
+ // "We want to gracefully set a spad target, not just exit with an error"
+
+ // "set target to mid point"
+ writeReg16Bit(DSS_CONFIG__MANUAL_EFFECTIVE_SPADS_SELECT, 0x8000);
+}
+
+// get range, status, rates from results buffer
+// based on VL53L1_GetRangingMeasurementData()
+void VL53L1X::getRangingData()
+{
+ // VL53L1_copy_sys_and_core_results_to_range_results() begin
+
+ uint16_t range = results.final_crosstalk_corrected_range_mm_sd0;
+
+ // "apply correction gain"
+ // gain factor of 2011 is tuning parm default (VL53L1_TUNINGPARM_LITE_RANGING_GAIN_FACTOR_DEFAULT)
+ // Basically, this appears to scale the result by 2011/2048, or about 98%
+ // (with the 1024 added for proper rounding).
+ ranging_data.range_mm = ((uint32_t)range * 2011 + 0x0400) / 0x0800;
+
+ // VL53L1_copy_sys_and_core_results_to_range_results() end
+
+ // set range_status in ranging_data based on value of RESULT__RANGE_STATUS register
+ // mostly based on ConvertStatusLite()
+ switch(results.range_status)
+ {
+ case 17: // MULTCLIPFAIL
+ case 2: // VCSELWATCHDOGTESTFAILURE
+ case 1: // VCSELCONTINUITYTESTFAILURE
+ case 3: // NOVHVVALUEFOUND
+ // from SetSimpleData()
+ ranging_data.range_status = HardwareFail;
+ break;
+
+ case 13: // USERROICLIP
+ // from SetSimpleData()
+ ranging_data.range_status = MinRangeFail;
+ break;
+
+ case 18: // GPHSTREAMCOUNT0READY
+ ranging_data.range_status = SynchronizationInt;
+ break;
+
+ case 5: // RANGEPHASECHECK
+ ranging_data.range_status = OutOfBoundsFail;
+ break;
+
+ case 4: // MSRCNOTARGET
+ ranging_data.range_status = SignalFail;
+ break;
+
+ case 6: // SIGMATHRESHOLDCHECK
+ ranging_data.range_status = SigmaFail;
+ break;
+
+ case 7: // PHASECONSISTENCY
+ ranging_data.range_status = WrapTargetFail;
+ break;
+
+ case 12: // RANGEIGNORETHRESHOLD
+ ranging_data.range_status = XtalkSignalFail;
+ break;
+
+ case 8: // MINCLIP
+ ranging_data.range_status = RangeValidMinRangeClipped;
+ break;
+
+ case 9: // RANGECOMPLETE
+ // from VL53L1_copy_sys_and_core_results_to_range_results()
+ if (results.stream_count == 0)
+ {
+ ranging_data.range_status = RangeValidNoWrapCheckFail;
+ }
+ else
+ {
+ ranging_data.range_status = RangeValid;
+ }
+ break;
+
+ default:
+ ranging_data.range_status = None;
+ }
+
+ // from SetSimpleData()
+ ranging_data.peak_signal_count_rate_MCPS =
+ countRateFixedToFloat(results.peak_signal_count_rate_crosstalk_corrected_mcps_sd0);
+ ranging_data.ambient_count_rate_MCPS =
+ countRateFixedToFloat(results.ambient_count_rate_mcps_sd0);
+}
+
+// Decode sequence step timeout in MCLKs from register value
+// based on VL53L1_decode_timeout()
+uint32_t VL53L1X::decodeTimeout(uint16_t reg_val)
+{
+ return ((uint32_t)(reg_val & 0xFF) << (reg_val >> 8)) + 1;
+}
+
+// Encode sequence step timeout register value from timeout in MCLKs
+// based on VL53L1_encode_timeout()
+uint16_t VL53L1X::encodeTimeout(uint32_t timeout_mclks)
+{
+ // encoded 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 macro periods to microseconds with given
+// macro period in microseconds (12.12 format)
+// based on VL53L1_calc_timeout_us()
+uint32_t VL53L1X::timeoutMclksToMicroseconds(uint32_t timeout_mclks, uint32_t macro_period_us)
+{
+ return ((uint64_t)timeout_mclks * macro_period_us + 0x800) >> 12;
+}
+
+// Convert sequence step timeout from microseconds to macro periods with given
+// macro period in microseconds (12.12 format)
+// based on VL53L1_calc_timeout_mclks()
+uint32_t VL53L1X::timeoutMicrosecondsToMclks(uint32_t timeout_us, uint32_t macro_period_us)
+{
+ return (((uint32_t)timeout_us << 12) + (macro_period_us >> 1)) / macro_period_us;
+}
+
+// Calculate macro period in microseconds (12.12 format) with given VCSEL period
+// assumes fast_osc_frequency has been read and stored
+// based on VL53L1_calc_macro_period_us()
+uint32_t VL53L1X::calcMacroPeriod(uint8_t vcsel_period)
+{
+ // from VL53L1_calc_pll_period_us()
+ // fast osc frequency in 4.12 format; PLL period in 0.24 format
+ uint32_t pll_period_us = ((uint32_t)0x01 << 30) / fast_osc_frequency;
+
+ // from VL53L1_decode_vcsel_period()
+ uint8_t vcsel_period_pclks = (vcsel_period + 1) << 1;
+
+ // VL53L1_MACRO_PERIOD_VCSEL_PERIODS = 2304
+ uint32_t macro_period_us = (uint32_t)2304 * pll_period_us;
+ macro_period_us >>= 6;
+ macro_period_us *= vcsel_period_pclks;
+ macro_period_us >>= 6;
+
+ return macro_period_us;
+}
diff --git a/lib/vl53l1x-arduino-1.01/VL53L1X.h b/lib/vl53l1x-arduino-1.01/VL53L1X.h
new file mode 100644
index 000000000..689e9b6fd
--- /dev/null
+++ b/lib/vl53l1x-arduino-1.01/VL53L1X.h
@@ -0,0 +1,1384 @@
+#pragma once
+
+#include
+
+class VL53L1X
+{
+ public:
+
+ // register addresses from API vl53l1x_register_map.h
+ enum regAddr : uint16_t
+ {
+ SOFT_RESET = 0x0000,
+ I2C_SLAVE__DEVICE_ADDRESS = 0x0001,
+ ANA_CONFIG__VHV_REF_SEL_VDDPIX = 0x0002,
+ ANA_CONFIG__VHV_REF_SEL_VQUENCH = 0x0003,
+ ANA_CONFIG__REG_AVDD1V2_SEL = 0x0004,
+ ANA_CONFIG__FAST_OSC__TRIM = 0x0005,
+ OSC_MEASURED__FAST_OSC__FREQUENCY = 0x0006,
+ OSC_MEASURED__FAST_OSC__FREQUENCY_HI = 0x0006,
+ OSC_MEASURED__FAST_OSC__FREQUENCY_LO = 0x0007,
+ VHV_CONFIG__TIMEOUT_MACROP_LOOP_BOUND = 0x0008,
+ VHV_CONFIG__COUNT_THRESH = 0x0009,
+ VHV_CONFIG__OFFSET = 0x000A,
+ VHV_CONFIG__INIT = 0x000B,
+ GLOBAL_CONFIG__SPAD_ENABLES_REF_0 = 0x000D,
+ GLOBAL_CONFIG__SPAD_ENABLES_REF_1 = 0x000E,
+ GLOBAL_CONFIG__SPAD_ENABLES_REF_2 = 0x000F,
+ GLOBAL_CONFIG__SPAD_ENABLES_REF_3 = 0x0010,
+ GLOBAL_CONFIG__SPAD_ENABLES_REF_4 = 0x0011,
+ GLOBAL_CONFIG__SPAD_ENABLES_REF_5 = 0x0012,
+ GLOBAL_CONFIG__REF_EN_START_SELECT = 0x0013,
+ REF_SPAD_MAN__NUM_REQUESTED_REF_SPADS = 0x0014,
+ REF_SPAD_MAN__REF_LOCATION = 0x0015,
+ ALGO__CROSSTALK_COMPENSATION_PLANE_OFFSET_KCPS = 0x0016,
+ ALGO__CROSSTALK_COMPENSATION_PLANE_OFFSET_KCPS_HI = 0x0016,
+ ALGO__CROSSTALK_COMPENSATION_PLANE_OFFSET_KCPS_LO = 0x0017,
+ ALGO__CROSSTALK_COMPENSATION_X_PLANE_GRADIENT_KCPS = 0x0018,
+ ALGO__CROSSTALK_COMPENSATION_X_PLANE_GRADIENT_KCPS_HI = 0x0018,
+ ALGO__CROSSTALK_COMPENSATION_X_PLANE_GRADIENT_KCPS_LO = 0x0019,
+ ALGO__CROSSTALK_COMPENSATION_Y_PLANE_GRADIENT_KCPS = 0x001A,
+ ALGO__CROSSTALK_COMPENSATION_Y_PLANE_GRADIENT_KCPS_HI = 0x001A,
+ ALGO__CROSSTALK_COMPENSATION_Y_PLANE_GRADIENT_KCPS_LO = 0x001B,
+ REF_SPAD_CHAR__TOTAL_RATE_TARGET_MCPS = 0x001C,
+ REF_SPAD_CHAR__TOTAL_RATE_TARGET_MCPS_HI = 0x001C,
+ REF_SPAD_CHAR__TOTAL_RATE_TARGET_MCPS_LO = 0x001D,
+ ALGO__PART_TO_PART_RANGE_OFFSET_MM = 0x001E,
+ ALGO__PART_TO_PART_RANGE_OFFSET_MM_HI = 0x001E,
+ ALGO__PART_TO_PART_RANGE_OFFSET_MM_LO = 0x001F,
+ MM_CONFIG__INNER_OFFSET_MM = 0x0020,
+ MM_CONFIG__INNER_OFFSET_MM_HI = 0x0020,
+ MM_CONFIG__INNER_OFFSET_MM_LO = 0x0021,
+ MM_CONFIG__OUTER_OFFSET_MM = 0x0022,
+ MM_CONFIG__OUTER_OFFSET_MM_HI = 0x0022,
+ MM_CONFIG__OUTER_OFFSET_MM_LO = 0x0023,
+ DSS_CONFIG__TARGET_TOTAL_RATE_MCPS = 0x0024,
+ DSS_CONFIG__TARGET_TOTAL_RATE_MCPS_HI = 0x0024,
+ DSS_CONFIG__TARGET_TOTAL_RATE_MCPS_LO = 0x0025,
+ DEBUG__CTRL = 0x0026,
+ TEST_MODE__CTRL = 0x0027,
+ CLK_GATING__CTRL = 0x0028,
+ NVM_BIST__CTRL = 0x0029,
+ NVM_BIST__NUM_NVM_WORDS = 0x002A,
+ NVM_BIST__START_ADDRESS = 0x002B,
+ HOST_IF__STATUS = 0x002C,
+ PAD_I2C_HV__CONFIG = 0x002D,
+ PAD_I2C_HV__EXTSUP_CONFIG = 0x002E,
+ GPIO_HV_PAD__CTRL = 0x002F,
+ GPIO_HV_MUX__CTRL = 0x0030,
+ GPIO__TIO_HV_STATUS = 0x0031,
+ GPIO__FIO_HV_STATUS = 0x0032,
+ ANA_CONFIG__SPAD_SEL_PSWIDTH = 0x0033,
+ ANA_CONFIG__VCSEL_PULSE_WIDTH_OFFSET = 0x0034,
+ ANA_CONFIG__FAST_OSC__CONFIG_CTRL = 0x0035,
+ SIGMA_ESTIMATOR__EFFECTIVE_PULSE_WIDTH_NS = 0x0036,
+ SIGMA_ESTIMATOR__EFFECTIVE_AMBIENT_WIDTH_NS = 0x0037,
+ SIGMA_ESTIMATOR__SIGMA_REF_MM = 0x0038,
+ ALGO__CROSSTALK_COMPENSATION_VALID_HEIGHT_MM = 0x0039,
+ SPARE_HOST_CONFIG__STATIC_CONFIG_SPARE_0 = 0x003A,
+ SPARE_HOST_CONFIG__STATIC_CONFIG_SPARE_1 = 0x003B,
+ ALGO__RANGE_IGNORE_THRESHOLD_MCPS = 0x003C,
+ ALGO__RANGE_IGNORE_THRESHOLD_MCPS_HI = 0x003C,
+ ALGO__RANGE_IGNORE_THRESHOLD_MCPS_LO = 0x003D,
+ ALGO__RANGE_IGNORE_VALID_HEIGHT_MM = 0x003E,
+ ALGO__RANGE_MIN_CLIP = 0x003F,
+ ALGO__CONSISTENCY_CHECK__TOLERANCE = 0x0040,
+ SPARE_HOST_CONFIG__STATIC_CONFIG_SPARE_2 = 0x0041,
+ SD_CONFIG__RESET_STAGES_MSB = 0x0042,
+ SD_CONFIG__RESET_STAGES_LSB = 0x0043,
+ GPH_CONFIG__STREAM_COUNT_UPDATE_VALUE = 0x0044,
+ GLOBAL_CONFIG__STREAM_DIVIDER = 0x0045,
+ SYSTEM__INTERRUPT_CONFIG_GPIO = 0x0046,
+ CAL_CONFIG__VCSEL_START = 0x0047,
+ CAL_CONFIG__REPEAT_RATE = 0x0048,
+ CAL_CONFIG__REPEAT_RATE_HI = 0x0048,
+ CAL_CONFIG__REPEAT_RATE_LO = 0x0049,
+ GLOBAL_CONFIG__VCSEL_WIDTH = 0x004A,
+ PHASECAL_CONFIG__TIMEOUT_MACROP = 0x004B,
+ PHASECAL_CONFIG__TARGET = 0x004C,
+ PHASECAL_CONFIG__OVERRIDE = 0x004D,
+ DSS_CONFIG__ROI_MODE_CONTROL = 0x004F,
+ SYSTEM__THRESH_RATE_HIGH = 0x0050,
+ SYSTEM__THRESH_RATE_HIGH_HI = 0x0050,
+ SYSTEM__THRESH_RATE_HIGH_LO = 0x0051,
+ SYSTEM__THRESH_RATE_LOW = 0x0052,
+ SYSTEM__THRESH_RATE_LOW_HI = 0x0052,
+ SYSTEM__THRESH_RATE_LOW_LO = 0x0053,
+ DSS_CONFIG__MANUAL_EFFECTIVE_SPADS_SELECT = 0x0054,
+ DSS_CONFIG__MANUAL_EFFECTIVE_SPADS_SELECT_HI = 0x0054,
+ DSS_CONFIG__MANUAL_EFFECTIVE_SPADS_SELECT_LO = 0x0055,
+ DSS_CONFIG__MANUAL_BLOCK_SELECT = 0x0056,
+ DSS_CONFIG__APERTURE_ATTENUATION = 0x0057,
+ DSS_CONFIG__MAX_SPADS_LIMIT = 0x0058,
+ DSS_CONFIG__MIN_SPADS_LIMIT = 0x0059,
+ MM_CONFIG__TIMEOUT_MACROP_A = 0x005A, // added by Pololu for 16-bit accesses
+ MM_CONFIG__TIMEOUT_MACROP_A_HI = 0x005A,
+ MM_CONFIG__TIMEOUT_MACROP_A_LO = 0x005B,
+ MM_CONFIG__TIMEOUT_MACROP_B = 0x005C, // added by Pololu for 16-bit accesses
+ MM_CONFIG__TIMEOUT_MACROP_B_HI = 0x005C,
+ MM_CONFIG__TIMEOUT_MACROP_B_LO = 0x005D,
+ RANGE_CONFIG__TIMEOUT_MACROP_A = 0x005E, // added by Pololu for 16-bit accesses
+ RANGE_CONFIG__TIMEOUT_MACROP_A_HI = 0x005E,
+ RANGE_CONFIG__TIMEOUT_MACROP_A_LO = 0x005F,
+ RANGE_CONFIG__VCSEL_PERIOD_A = 0x0060,
+ RANGE_CONFIG__TIMEOUT_MACROP_B = 0x0061, // added by Pololu for 16-bit accesses
+ RANGE_CONFIG__TIMEOUT_MACROP_B_HI = 0x0061,
+ RANGE_CONFIG__TIMEOUT_MACROP_B_LO = 0x0062,
+ RANGE_CONFIG__VCSEL_PERIOD_B = 0x0063,
+ RANGE_CONFIG__SIGMA_THRESH = 0x0064,
+ RANGE_CONFIG__SIGMA_THRESH_HI = 0x0064,
+ RANGE_CONFIG__SIGMA_THRESH_LO = 0x0065,
+ RANGE_CONFIG__MIN_COUNT_RATE_RTN_LIMIT_MCPS = 0x0066,
+ RANGE_CONFIG__MIN_COUNT_RATE_RTN_LIMIT_MCPS_HI = 0x0066,
+ RANGE_CONFIG__MIN_COUNT_RATE_RTN_LIMIT_MCPS_LO = 0x0067,
+ RANGE_CONFIG__VALID_PHASE_LOW = 0x0068,
+ RANGE_CONFIG__VALID_PHASE_HIGH = 0x0069,
+ SYSTEM__INTERMEASUREMENT_PERIOD = 0x006C,
+ SYSTEM__INTERMEASUREMENT_PERIOD_3 = 0x006C,
+ SYSTEM__INTERMEASUREMENT_PERIOD_2 = 0x006D,
+ SYSTEM__INTERMEASUREMENT_PERIOD_1 = 0x006E,
+ SYSTEM__INTERMEASUREMENT_PERIOD_0 = 0x006F,
+ SYSTEM__FRACTIONAL_ENABLE = 0x0070,
+ SYSTEM__GROUPED_PARAMETER_HOLD_0 = 0x0071,
+ SYSTEM__THRESH_HIGH = 0x0072,
+ SYSTEM__THRESH_HIGH_HI = 0x0072,
+ SYSTEM__THRESH_HIGH_LO = 0x0073,
+ SYSTEM__THRESH_LOW = 0x0074,
+ SYSTEM__THRESH_LOW_HI = 0x0074,
+ SYSTEM__THRESH_LOW_LO = 0x0075,
+ SYSTEM__ENABLE_XTALK_PER_QUADRANT = 0x0076,
+ SYSTEM__SEED_CONFIG = 0x0077,
+ SD_CONFIG__WOI_SD0 = 0x0078,
+ SD_CONFIG__WOI_SD1 = 0x0079,
+ SD_CONFIG__INITIAL_PHASE_SD0 = 0x007A,
+ SD_CONFIG__INITIAL_PHASE_SD1 = 0x007B,
+ SYSTEM__GROUPED_PARAMETER_HOLD_1 = 0x007C,
+ SD_CONFIG__FIRST_ORDER_SELECT = 0x007D,
+ SD_CONFIG__QUANTIFIER = 0x007E,
+ ROI_CONFIG__USER_ROI_CENTRE_SPAD = 0x007F,
+ ROI_CONFIG__USER_ROI_REQUESTED_GLOBAL_XY_SIZE = 0x0080,
+ SYSTEM__SEQUENCE_CONFIG = 0x0081,
+ SYSTEM__GROUPED_PARAMETER_HOLD = 0x0082,
+ POWER_MANAGEMENT__GO1_POWER_FORCE = 0x0083,
+ SYSTEM__STREAM_COUNT_CTRL = 0x0084,
+ FIRMWARE__ENABLE = 0x0085,
+ SYSTEM__INTERRUPT_CLEAR = 0x0086,
+ SYSTEM__MODE_START = 0x0087,
+ RESULT__INTERRUPT_STATUS = 0x0088,
+ RESULT__RANGE_STATUS = 0x0089,
+ RESULT__REPORT_STATUS = 0x008A,
+ RESULT__STREAM_COUNT = 0x008B,
+ RESULT__DSS_ACTUAL_EFFECTIVE_SPADS_SD0 = 0x008C,
+ RESULT__DSS_ACTUAL_EFFECTIVE_SPADS_SD0_HI = 0x008C,
+ RESULT__DSS_ACTUAL_EFFECTIVE_SPADS_SD0_LO = 0x008D,
+ RESULT__PEAK_SIGNAL_COUNT_RATE_MCPS_SD0 = 0x008E,
+ RESULT__PEAK_SIGNAL_COUNT_RATE_MCPS_SD0_HI = 0x008E,
+ RESULT__PEAK_SIGNAL_COUNT_RATE_MCPS_SD0_LO = 0x008F,
+ RESULT__AMBIENT_COUNT_RATE_MCPS_SD0 = 0x0090,
+ RESULT__AMBIENT_COUNT_RATE_MCPS_SD0_HI = 0x0090,
+ RESULT__AMBIENT_COUNT_RATE_MCPS_SD0_LO = 0x0091,
+ RESULT__SIGMA_SD0 = 0x0092,
+ RESULT__SIGMA_SD0_HI = 0x0092,
+ RESULT__SIGMA_SD0_LO = 0x0093,
+ RESULT__PHASE_SD0 = 0x0094,
+ RESULT__PHASE_SD0_HI = 0x0094,
+ RESULT__PHASE_SD0_LO = 0x0095,
+ RESULT__FINAL_CROSSTALK_CORRECTED_RANGE_MM_SD0 = 0x0096,
+ RESULT__FINAL_CROSSTALK_CORRECTED_RANGE_MM_SD0_HI = 0x0096,
+ RESULT__FINAL_CROSSTALK_CORRECTED_RANGE_MM_SD0_LO = 0x0097,
+ RESULT__PEAK_SIGNAL_COUNT_RATE_CROSSTALK_CORRECTED_MCPS_SD0 = 0x0098,
+ RESULT__PEAK_SIGNAL_COUNT_RATE_CROSSTALK_CORRECTED_MCPS_SD0_HI = 0x0098,
+ RESULT__PEAK_SIGNAL_COUNT_RATE_CROSSTALK_CORRECTED_MCPS_SD0_LO = 0x0099,
+ RESULT__MM_INNER_ACTUAL_EFFECTIVE_SPADS_SD0 = 0x009A,
+ RESULT__MM_INNER_ACTUAL_EFFECTIVE_SPADS_SD0_HI = 0x009A,
+ RESULT__MM_INNER_ACTUAL_EFFECTIVE_SPADS_SD0_LO = 0x009B,
+ RESULT__MM_OUTER_ACTUAL_EFFECTIVE_SPADS_SD0 = 0x009C,
+ RESULT__MM_OUTER_ACTUAL_EFFECTIVE_SPADS_SD0_HI = 0x009C,
+ RESULT__MM_OUTER_ACTUAL_EFFECTIVE_SPADS_SD0_LO = 0x009D,
+ RESULT__AVG_SIGNAL_COUNT_RATE_MCPS_SD0 = 0x009E,
+ RESULT__AVG_SIGNAL_COUNT_RATE_MCPS_SD0_HI = 0x009E,
+ RESULT__AVG_SIGNAL_COUNT_RATE_MCPS_SD0_LO = 0x009F,
+ RESULT__DSS_ACTUAL_EFFECTIVE_SPADS_SD1 = 0x00A0,
+ RESULT__DSS_ACTUAL_EFFECTIVE_SPADS_SD1_HI = 0x00A0,
+ RESULT__DSS_ACTUAL_EFFECTIVE_SPADS_SD1_LO = 0x00A1,
+ RESULT__PEAK_SIGNAL_COUNT_RATE_MCPS_SD1 = 0x00A2,
+ RESULT__PEAK_SIGNAL_COUNT_RATE_MCPS_SD1_HI = 0x00A2,
+ RESULT__PEAK_SIGNAL_COUNT_RATE_MCPS_SD1_LO = 0x00A3,
+ RESULT__AMBIENT_COUNT_RATE_MCPS_SD1 = 0x00A4,
+ RESULT__AMBIENT_COUNT_RATE_MCPS_SD1_HI = 0x00A4,
+ RESULT__AMBIENT_COUNT_RATE_MCPS_SD1_LO = 0x00A5,
+ RESULT__SIGMA_SD1 = 0x00A6,
+ RESULT__SIGMA_SD1_HI = 0x00A6,
+ RESULT__SIGMA_SD1_LO = 0x00A7,
+ RESULT__PHASE_SD1 = 0x00A8,
+ RESULT__PHASE_SD1_HI = 0x00A8,
+ RESULT__PHASE_SD1_LO = 0x00A9,
+ RESULT__FINAL_CROSSTALK_CORRECTED_RANGE_MM_SD1 = 0x00AA,
+ RESULT__FINAL_CROSSTALK_CORRECTED_RANGE_MM_SD1_HI = 0x00AA,
+ RESULT__FINAL_CROSSTALK_CORRECTED_RANGE_MM_SD1_LO = 0x00AB,
+ RESULT__SPARE_0_SD1 = 0x00AC,
+ RESULT__SPARE_0_SD1_HI = 0x00AC,
+ RESULT__SPARE_0_SD1_LO = 0x00AD,
+ RESULT__SPARE_1_SD1 = 0x00AE,
+ RESULT__SPARE_1_SD1_HI = 0x00AE,
+ RESULT__SPARE_1_SD1_LO = 0x00AF,
+ RESULT__SPARE_2_SD1 = 0x00B0,
+ RESULT__SPARE_2_SD1_HI = 0x00B0,
+ RESULT__SPARE_2_SD1_LO = 0x00B1,
+ RESULT__SPARE_3_SD1 = 0x00B2,
+ RESULT__THRESH_INFO = 0x00B3,
+ RESULT_CORE__AMBIENT_WINDOW_EVENTS_SD0 = 0x00B4,
+ RESULT_CORE__AMBIENT_WINDOW_EVENTS_SD0_3 = 0x00B4,
+ RESULT_CORE__AMBIENT_WINDOW_EVENTS_SD0_2 = 0x00B5,
+ RESULT_CORE__AMBIENT_WINDOW_EVENTS_SD0_1 = 0x00B6,
+ RESULT_CORE__AMBIENT_WINDOW_EVENTS_SD0_0 = 0x00B7,
+ RESULT_CORE__RANGING_TOTAL_EVENTS_SD0 = 0x00B8,
+ RESULT_CORE__RANGING_TOTAL_EVENTS_SD0_3 = 0x00B8,
+ RESULT_CORE__RANGING_TOTAL_EVENTS_SD0_2 = 0x00B9,
+ RESULT_CORE__RANGING_TOTAL_EVENTS_SD0_1 = 0x00BA,
+ RESULT_CORE__RANGING_TOTAL_EVENTS_SD0_0 = 0x00BB,
+ RESULT_CORE__SIGNAL_TOTAL_EVENTS_SD0 = 0x00BC,
+ RESULT_CORE__SIGNAL_TOTAL_EVENTS_SD0_3 = 0x00BC,
+ RESULT_CORE__SIGNAL_TOTAL_EVENTS_SD0_2 = 0x00BD,
+ RESULT_CORE__SIGNAL_TOTAL_EVENTS_SD0_1 = 0x00BE,
+ RESULT_CORE__SIGNAL_TOTAL_EVENTS_SD0_0 = 0x00BF,
+ RESULT_CORE__TOTAL_PERIODS_ELAPSED_SD0 = 0x00C0,
+ RESULT_CORE__TOTAL_PERIODS_ELAPSED_SD0_3 = 0x00C0,
+ RESULT_CORE__TOTAL_PERIODS_ELAPSED_SD0_2 = 0x00C1,
+ RESULT_CORE__TOTAL_PERIODS_ELAPSED_SD0_1 = 0x00C2,
+ RESULT_CORE__TOTAL_PERIODS_ELAPSED_SD0_0 = 0x00C3,
+ RESULT_CORE__AMBIENT_WINDOW_EVENTS_SD1 = 0x00C4,
+ RESULT_CORE__AMBIENT_WINDOW_EVENTS_SD1_3 = 0x00C4,
+ RESULT_CORE__AMBIENT_WINDOW_EVENTS_SD1_2 = 0x00C5,
+ RESULT_CORE__AMBIENT_WINDOW_EVENTS_SD1_1 = 0x00C6,
+ RESULT_CORE__AMBIENT_WINDOW_EVENTS_SD1_0 = 0x00C7,
+ RESULT_CORE__RANGING_TOTAL_EVENTS_SD1 = 0x00C8,
+ RESULT_CORE__RANGING_TOTAL_EVENTS_SD1_3 = 0x00C8,
+ RESULT_CORE__RANGING_TOTAL_EVENTS_SD1_2 = 0x00C9,
+ RESULT_CORE__RANGING_TOTAL_EVENTS_SD1_1 = 0x00CA,
+ RESULT_CORE__RANGING_TOTAL_EVENTS_SD1_0 = 0x00CB,
+ RESULT_CORE__SIGNAL_TOTAL_EVENTS_SD1 = 0x00CC,
+ RESULT_CORE__SIGNAL_TOTAL_EVENTS_SD1_3 = 0x00CC,
+ RESULT_CORE__SIGNAL_TOTAL_EVENTS_SD1_2 = 0x00CD,
+ RESULT_CORE__SIGNAL_TOTAL_EVENTS_SD1_1 = 0x00CE,
+ RESULT_CORE__SIGNAL_TOTAL_EVENTS_SD1_0 = 0x00CF,
+ RESULT_CORE__TOTAL_PERIODS_ELAPSED_SD1 = 0x00D0,
+ RESULT_CORE__TOTAL_PERIODS_ELAPSED_SD1_3 = 0x00D0,
+ RESULT_CORE__TOTAL_PERIODS_ELAPSED_SD1_2 = 0x00D1,
+ RESULT_CORE__TOTAL_PERIODS_ELAPSED_SD1_1 = 0x00D2,
+ RESULT_CORE__TOTAL_PERIODS_ELAPSED_SD1_0 = 0x00D3,
+ RESULT_CORE__SPARE_0 = 0x00D4,
+ PHASECAL_RESULT__REFERENCE_PHASE = 0x00D6,
+ PHASECAL_RESULT__REFERENCE_PHASE_HI = 0x00D6,
+ PHASECAL_RESULT__REFERENCE_PHASE_LO = 0x00D7,
+ PHASECAL_RESULT__VCSEL_START = 0x00D8,
+ REF_SPAD_CHAR_RESULT__NUM_ACTUAL_REF_SPADS = 0x00D9,
+ REF_SPAD_CHAR_RESULT__REF_LOCATION = 0x00DA,
+ VHV_RESULT__COLDBOOT_STATUS = 0x00DB,
+ VHV_RESULT__SEARCH_RESULT = 0x00DC,
+ VHV_RESULT__LATEST_SETTING = 0x00DD,
+ RESULT__OSC_CALIBRATE_VAL = 0x00DE,
+ RESULT__OSC_CALIBRATE_VAL_HI = 0x00DE,
+ RESULT__OSC_CALIBRATE_VAL_LO = 0x00DF,
+ ANA_CONFIG__POWERDOWN_GO1 = 0x00E0,
+ ANA_CONFIG__REF_BG_CTRL = 0x00E1,
+ ANA_CONFIG__REGDVDD1V2_CTRL = 0x00E2,
+ ANA_CONFIG__OSC_SLOW_CTRL = 0x00E3,
+ TEST_MODE__STATUS = 0x00E4,
+ FIRMWARE__SYSTEM_STATUS = 0x00E5,
+ FIRMWARE__MODE_STATUS = 0x00E6,
+ FIRMWARE__SECONDARY_MODE_STATUS = 0x00E7,
+ FIRMWARE__CAL_REPEAT_RATE_COUNTER = 0x00E8,
+ FIRMWARE__CAL_REPEAT_RATE_COUNTER_HI = 0x00E8,
+ FIRMWARE__CAL_REPEAT_RATE_COUNTER_LO = 0x00E9,
+ FIRMWARE__HISTOGRAM_BIN = 0x00EA,
+ GPH__SYSTEM__THRESH_HIGH = 0x00EC,
+ GPH__SYSTEM__THRESH_HIGH_HI = 0x00EC,
+ GPH__SYSTEM__THRESH_HIGH_LO = 0x00ED,
+ GPH__SYSTEM__THRESH_LOW = 0x00EE,
+ GPH__SYSTEM__THRESH_LOW_HI = 0x00EE,
+ GPH__SYSTEM__THRESH_LOW_LO = 0x00EF,
+ GPH__SYSTEM__ENABLE_XTALK_PER_QUADRANT = 0x00F0,
+ GPH__SPARE_0 = 0x00F1,
+ GPH__SD_CONFIG__WOI_SD0 = 0x00F2,
+ GPH__SD_CONFIG__WOI_SD1 = 0x00F3,
+ GPH__SD_CONFIG__INITIAL_PHASE_SD0 = 0x00F4,
+ GPH__SD_CONFIG__INITIAL_PHASE_SD1 = 0x00F5,
+ GPH__SD_CONFIG__FIRST_ORDER_SELECT = 0x00F6,
+ GPH__SD_CONFIG__QUANTIFIER = 0x00F7,
+ GPH__ROI_CONFIG__USER_ROI_CENTRE_SPAD = 0x00F8,
+ GPH__ROI_CONFIG__USER_ROI_REQUESTED_GLOBAL_XY_SIZE = 0x00F9,
+ GPH__SYSTEM__SEQUENCE_CONFIG = 0x00FA,
+ GPH__GPH_ID = 0x00FB,
+ SYSTEM__INTERRUPT_SET = 0x00FC,
+ INTERRUPT_MANAGER__ENABLES = 0x00FD,
+ INTERRUPT_MANAGER__CLEAR = 0x00FE,
+ INTERRUPT_MANAGER__STATUS = 0x00FF,
+ MCU_TO_HOST_BANK__WR_ACCESS_EN = 0x0100,
+ POWER_MANAGEMENT__GO1_RESET_STATUS = 0x0101,
+ PAD_STARTUP_MODE__VALUE_RO = 0x0102,
+ PAD_STARTUP_MODE__VALUE_CTRL = 0x0103,
+ PLL_PERIOD_US = 0x0104,
+ PLL_PERIOD_US_3 = 0x0104,
+ PLL_PERIOD_US_2 = 0x0105,
+ PLL_PERIOD_US_1 = 0x0106,
+ PLL_PERIOD_US_0 = 0x0107,
+ INTERRUPT_SCHEDULER__DATA_OUT = 0x0108,
+ INTERRUPT_SCHEDULER__DATA_OUT_3 = 0x0108,
+ INTERRUPT_SCHEDULER__DATA_OUT_2 = 0x0109,
+ INTERRUPT_SCHEDULER__DATA_OUT_1 = 0x010A,
+ INTERRUPT_SCHEDULER__DATA_OUT_0 = 0x010B,
+ NVM_BIST__COMPLETE = 0x010C,
+ NVM_BIST__STATUS = 0x010D,
+ IDENTIFICATION__MODEL_ID = 0x010F,
+ IDENTIFICATION__MODULE_TYPE = 0x0110,
+ IDENTIFICATION__REVISION_ID = 0x0111,
+ IDENTIFICATION__MODULE_ID = 0x0112,
+ IDENTIFICATION__MODULE_ID_HI = 0x0112,
+ IDENTIFICATION__MODULE_ID_LO = 0x0113,
+ ANA_CONFIG__FAST_OSC__TRIM_MAX = 0x0114,
+ ANA_CONFIG__FAST_OSC__FREQ_SET = 0x0115,
+ ANA_CONFIG__VCSEL_TRIM = 0x0116,
+ ANA_CONFIG__VCSEL_SELION = 0x0117,
+ ANA_CONFIG__VCSEL_SELION_MAX = 0x0118,
+ PROTECTED_LASER_SAFETY__LOCK_BIT = 0x0119,
+ LASER_SAFETY__KEY = 0x011A,
+ LASER_SAFETY__KEY_RO = 0x011B,
+ LASER_SAFETY__CLIP = 0x011C,
+ LASER_SAFETY__MULT = 0x011D,
+ GLOBAL_CONFIG__SPAD_ENABLES_RTN_0 = 0x011E,
+ GLOBAL_CONFIG__SPAD_ENABLES_RTN_1 = 0x011F,
+ GLOBAL_CONFIG__SPAD_ENABLES_RTN_2 = 0x0120,
+ GLOBAL_CONFIG__SPAD_ENABLES_RTN_3 = 0x0121,
+ GLOBAL_CONFIG__SPAD_ENABLES_RTN_4 = 0x0122,
+ GLOBAL_CONFIG__SPAD_ENABLES_RTN_5 = 0x0123,
+ GLOBAL_CONFIG__SPAD_ENABLES_RTN_6 = 0x0124,
+ GLOBAL_CONFIG__SPAD_ENABLES_RTN_7 = 0x0125,
+ GLOBAL_CONFIG__SPAD_ENABLES_RTN_8 = 0x0126,
+ GLOBAL_CONFIG__SPAD_ENABLES_RTN_9 = 0x0127,
+ GLOBAL_CONFIG__SPAD_ENABLES_RTN_10 = 0x0128,
+ GLOBAL_CONFIG__SPAD_ENABLES_RTN_11 = 0x0129,
+ GLOBAL_CONFIG__SPAD_ENABLES_RTN_12 = 0x012A,
+ GLOBAL_CONFIG__SPAD_ENABLES_RTN_13 = 0x012B,
+ GLOBAL_CONFIG__SPAD_ENABLES_RTN_14 = 0x012C,
+ GLOBAL_CONFIG__SPAD_ENABLES_RTN_15 = 0x012D,
+ GLOBAL_CONFIG__SPAD_ENABLES_RTN_16 = 0x012E,
+ GLOBAL_CONFIG__SPAD_ENABLES_RTN_17 = 0x012F,
+ GLOBAL_CONFIG__SPAD_ENABLES_RTN_18 = 0x0130,
+ GLOBAL_CONFIG__SPAD_ENABLES_RTN_19 = 0x0131,
+ GLOBAL_CONFIG__SPAD_ENABLES_RTN_20 = 0x0132,
+ GLOBAL_CONFIG__SPAD_ENABLES_RTN_21 = 0x0133,
+ GLOBAL_CONFIG__SPAD_ENABLES_RTN_22 = 0x0134,
+ GLOBAL_CONFIG__SPAD_ENABLES_RTN_23 = 0x0135,
+ GLOBAL_CONFIG__SPAD_ENABLES_RTN_24 = 0x0136,
+ GLOBAL_CONFIG__SPAD_ENABLES_RTN_25 = 0x0137,
+ GLOBAL_CONFIG__SPAD_ENABLES_RTN_26 = 0x0138,
+ GLOBAL_CONFIG__SPAD_ENABLES_RTN_27 = 0x0139,
+ GLOBAL_CONFIG__SPAD_ENABLES_RTN_28 = 0x013A,
+ GLOBAL_CONFIG__SPAD_ENABLES_RTN_29 = 0x013B,
+ GLOBAL_CONFIG__SPAD_ENABLES_RTN_30 = 0x013C,
+ GLOBAL_CONFIG__SPAD_ENABLES_RTN_31 = 0x013D,
+ ROI_CONFIG__MODE_ROI_CENTRE_SPAD = 0x013E,
+ ROI_CONFIG__MODE_ROI_XY_SIZE = 0x013F,
+ GO2_HOST_BANK_ACCESS__OVERRIDE = 0x0300,
+ MCU_UTIL_MULTIPLIER__MULTIPLICAND = 0x0400,
+ MCU_UTIL_MULTIPLIER__MULTIPLICAND_3 = 0x0400,
+ MCU_UTIL_MULTIPLIER__MULTIPLICAND_2 = 0x0401,
+ MCU_UTIL_MULTIPLIER__MULTIPLICAND_1 = 0x0402,
+ MCU_UTIL_MULTIPLIER__MULTIPLICAND_0 = 0x0403,
+ MCU_UTIL_MULTIPLIER__MULTIPLIER = 0x0404,
+ MCU_UTIL_MULTIPLIER__MULTIPLIER_3 = 0x0404,
+ MCU_UTIL_MULTIPLIER__MULTIPLIER_2 = 0x0405,
+ MCU_UTIL_MULTIPLIER__MULTIPLIER_1 = 0x0406,
+ MCU_UTIL_MULTIPLIER__MULTIPLIER_0 = 0x0407,
+ MCU_UTIL_MULTIPLIER__PRODUCT_HI = 0x0408,
+ MCU_UTIL_MULTIPLIER__PRODUCT_HI_3 = 0x0408,
+ MCU_UTIL_MULTIPLIER__PRODUCT_HI_2 = 0x0409,
+ MCU_UTIL_MULTIPLIER__PRODUCT_HI_1 = 0x040A,
+ MCU_UTIL_MULTIPLIER__PRODUCT_HI_0 = 0x040B,
+ MCU_UTIL_MULTIPLIER__PRODUCT_LO = 0x040C,
+ MCU_UTIL_MULTIPLIER__PRODUCT_LO_3 = 0x040C,
+ MCU_UTIL_MULTIPLIER__PRODUCT_LO_2 = 0x040D,
+ MCU_UTIL_MULTIPLIER__PRODUCT_LO_1 = 0x040E,
+ MCU_UTIL_MULTIPLIER__PRODUCT_LO_0 = 0x040F,
+ MCU_UTIL_MULTIPLIER__START = 0x0410,
+ MCU_UTIL_MULTIPLIER__STATUS = 0x0411,
+ MCU_UTIL_DIVIDER__START = 0x0412,
+ MCU_UTIL_DIVIDER__STATUS = 0x0413,
+ MCU_UTIL_DIVIDER__DIVIDEND = 0x0414,
+ MCU_UTIL_DIVIDER__DIVIDEND_3 = 0x0414,
+ MCU_UTIL_DIVIDER__DIVIDEND_2 = 0x0415,
+ MCU_UTIL_DIVIDER__DIVIDEND_1 = 0x0416,
+ MCU_UTIL_DIVIDER__DIVIDEND_0 = 0x0417,
+ MCU_UTIL_DIVIDER__DIVISOR = 0x0418,
+ MCU_UTIL_DIVIDER__DIVISOR_3 = 0x0418,
+ MCU_UTIL_DIVIDER__DIVISOR_2 = 0x0419,
+ MCU_UTIL_DIVIDER__DIVISOR_1 = 0x041A,
+ MCU_UTIL_DIVIDER__DIVISOR_0 = 0x041B,
+ MCU_UTIL_DIVIDER__QUOTIENT = 0x041C,
+ MCU_UTIL_DIVIDER__QUOTIENT_3 = 0x041C,
+ MCU_UTIL_DIVIDER__QUOTIENT_2 = 0x041D,
+ MCU_UTIL_DIVIDER__QUOTIENT_1 = 0x041E,
+ MCU_UTIL_DIVIDER__QUOTIENT_0 = 0x041F,
+ TIMER0__VALUE_IN = 0x0420,
+ TIMER0__VALUE_IN_3 = 0x0420,
+ TIMER0__VALUE_IN_2 = 0x0421,
+ TIMER0__VALUE_IN_1 = 0x0422,
+ TIMER0__VALUE_IN_0 = 0x0423,
+ TIMER1__VALUE_IN = 0x0424,
+ TIMER1__VALUE_IN_3 = 0x0424,
+ TIMER1__VALUE_IN_2 = 0x0425,
+ TIMER1__VALUE_IN_1 = 0x0426,
+ TIMER1__VALUE_IN_0 = 0x0427,
+ TIMER0__CTRL = 0x0428,
+ TIMER1__CTRL = 0x0429,
+ MCU_GENERAL_PURPOSE__GP_0 = 0x042C,
+ MCU_GENERAL_PURPOSE__GP_1 = 0x042D,
+ MCU_GENERAL_PURPOSE__GP_2 = 0x042E,
+ MCU_GENERAL_PURPOSE__GP_3 = 0x042F,
+ MCU_RANGE_CALC__CONFIG = 0x0430,
+ MCU_RANGE_CALC__OFFSET_CORRECTED_RANGE = 0x0432,
+ MCU_RANGE_CALC__OFFSET_CORRECTED_RANGE_HI = 0x0432,
+ MCU_RANGE_CALC__OFFSET_CORRECTED_RANGE_LO = 0x0433,
+ MCU_RANGE_CALC__SPARE_4 = 0x0434,
+ MCU_RANGE_CALC__SPARE_4_3 = 0x0434,
+ MCU_RANGE_CALC__SPARE_4_2 = 0x0435,
+ MCU_RANGE_CALC__SPARE_4_1 = 0x0436,
+ MCU_RANGE_CALC__SPARE_4_0 = 0x0437,
+ MCU_RANGE_CALC__AMBIENT_DURATION_PRE_CALC = 0x0438,
+ MCU_RANGE_CALC__AMBIENT_DURATION_PRE_CALC_HI = 0x0438,
+ MCU_RANGE_CALC__AMBIENT_DURATION_PRE_CALC_LO = 0x0439,
+ MCU_RANGE_CALC__ALGO_VCSEL_PERIOD = 0x043C,
+ MCU_RANGE_CALC__SPARE_5 = 0x043D,
+ MCU_RANGE_CALC__ALGO_TOTAL_PERIODS = 0x043E,
+ MCU_RANGE_CALC__ALGO_TOTAL_PERIODS_HI = 0x043E,
+ MCU_RANGE_CALC__ALGO_TOTAL_PERIODS_LO = 0x043F,
+ MCU_RANGE_CALC__ALGO_ACCUM_PHASE = 0x0440,
+ MCU_RANGE_CALC__ALGO_ACCUM_PHASE_3 = 0x0440,
+ MCU_RANGE_CALC__ALGO_ACCUM_PHASE_2 = 0x0441,
+ MCU_RANGE_CALC__ALGO_ACCUM_PHASE_1 = 0x0442,
+ MCU_RANGE_CALC__ALGO_ACCUM_PHASE_0 = 0x0443,
+ MCU_RANGE_CALC__ALGO_SIGNAL_EVENTS = 0x0444,
+ MCU_RANGE_CALC__ALGO_SIGNAL_EVENTS_3 = 0x0444,
+ MCU_RANGE_CALC__ALGO_SIGNAL_EVENTS_2 = 0x0445,
+ MCU_RANGE_CALC__ALGO_SIGNAL_EVENTS_1 = 0x0446,
+ MCU_RANGE_CALC__ALGO_SIGNAL_EVENTS_0 = 0x0447,
+ MCU_RANGE_CALC__ALGO_AMBIENT_EVENTS = 0x0448,
+ MCU_RANGE_CALC__ALGO_AMBIENT_EVENTS_3 = 0x0448,
+ MCU_RANGE_CALC__ALGO_AMBIENT_EVENTS_2 = 0x0449,
+ MCU_RANGE_CALC__ALGO_AMBIENT_EVENTS_1 = 0x044A,
+ MCU_RANGE_CALC__ALGO_AMBIENT_EVENTS_0 = 0x044B,
+ MCU_RANGE_CALC__SPARE_6 = 0x044C,
+ MCU_RANGE_CALC__SPARE_6_HI = 0x044C,
+ MCU_RANGE_CALC__SPARE_6_LO = 0x044D,
+ MCU_RANGE_CALC__ALGO_ADJUST_VCSEL_PERIOD = 0x044E,
+ MCU_RANGE_CALC__ALGO_ADJUST_VCSEL_PERIOD_HI = 0x044E,
+ MCU_RANGE_CALC__ALGO_ADJUST_VCSEL_PERIOD_LO = 0x044F,
+ MCU_RANGE_CALC__NUM_SPADS = 0x0450,
+ MCU_RANGE_CALC__NUM_SPADS_HI = 0x0450,
+ MCU_RANGE_CALC__NUM_SPADS_LO = 0x0451,
+ MCU_RANGE_CALC__PHASE_OUTPUT = 0x0452,
+ MCU_RANGE_CALC__PHASE_OUTPUT_HI = 0x0452,
+ MCU_RANGE_CALC__PHASE_OUTPUT_LO = 0x0453,
+ MCU_RANGE_CALC__RATE_PER_SPAD_MCPS = 0x0454,
+ MCU_RANGE_CALC__RATE_PER_SPAD_MCPS_3 = 0x0454,
+ MCU_RANGE_CALC__RATE_PER_SPAD_MCPS_2 = 0x0455,
+ MCU_RANGE_CALC__RATE_PER_SPAD_MCPS_1 = 0x0456,
+ MCU_RANGE_CALC__RATE_PER_SPAD_MCPS_0 = 0x0457,
+ MCU_RANGE_CALC__SPARE_7 = 0x0458,
+ MCU_RANGE_CALC__SPARE_8 = 0x0459,
+ MCU_RANGE_CALC__PEAK_SIGNAL_RATE_MCPS = 0x045A,
+ MCU_RANGE_CALC__PEAK_SIGNAL_RATE_MCPS_HI = 0x045A,
+ MCU_RANGE_CALC__PEAK_SIGNAL_RATE_MCPS_LO = 0x045B,
+ MCU_RANGE_CALC__AVG_SIGNAL_RATE_MCPS = 0x045C,
+ MCU_RANGE_CALC__AVG_SIGNAL_RATE_MCPS_HI = 0x045C,
+ MCU_RANGE_CALC__AVG_SIGNAL_RATE_MCPS_LO = 0x045D,
+ MCU_RANGE_CALC__AMBIENT_RATE_MCPS = 0x045E,
+ MCU_RANGE_CALC__AMBIENT_RATE_MCPS_HI = 0x045E,
+ MCU_RANGE_CALC__AMBIENT_RATE_MCPS_LO = 0x045F,
+ MCU_RANGE_CALC__XTALK = 0x0460,
+ MCU_RANGE_CALC__XTALK_HI = 0x0460,
+ MCU_RANGE_CALC__XTALK_LO = 0x0461,
+ MCU_RANGE_CALC__CALC_STATUS = 0x0462,
+ MCU_RANGE_CALC__DEBUG = 0x0463,
+ MCU_RANGE_CALC__PEAK_SIGNAL_RATE_XTALK_CORR_MCPS = 0x0464,
+ MCU_RANGE_CALC__PEAK_SIGNAL_RATE_XTALK_CORR_MCPS_HI = 0x0464,
+ MCU_RANGE_CALC__PEAK_SIGNAL_RATE_XTALK_CORR_MCPS_LO = 0x0465,
+ MCU_RANGE_CALC__SPARE_0 = 0x0468,
+ MCU_RANGE_CALC__SPARE_1 = 0x0469,
+ MCU_RANGE_CALC__SPARE_2 = 0x046A,
+ MCU_RANGE_CALC__SPARE_3 = 0x046B,
+ PATCH__CTRL = 0x0470,
+ PATCH__JMP_ENABLES = 0x0472,
+ PATCH__JMP_ENABLES_HI = 0x0472,
+ PATCH__JMP_ENABLES_LO = 0x0473,
+ PATCH__DATA_ENABLES = 0x0474,
+ PATCH__DATA_ENABLES_HI = 0x0474,
+ PATCH__DATA_ENABLES_LO = 0x0475,
+ PATCH__OFFSET_0 = 0x0476,
+ PATCH__OFFSET_0_HI = 0x0476,
+ PATCH__OFFSET_0_LO = 0x0477,
+ PATCH__OFFSET_1 = 0x0478,
+ PATCH__OFFSET_1_HI = 0x0478,
+ PATCH__OFFSET_1_LO = 0x0479,
+ PATCH__OFFSET_2 = 0x047A,
+ PATCH__OFFSET_2_HI = 0x047A,
+ PATCH__OFFSET_2_LO = 0x047B,
+ PATCH__OFFSET_3 = 0x047C,
+ PATCH__OFFSET_3_HI = 0x047C,
+ PATCH__OFFSET_3_LO = 0x047D,
+ PATCH__OFFSET_4 = 0x047E,
+ PATCH__OFFSET_4_HI = 0x047E,
+ PATCH__OFFSET_4_LO = 0x047F,
+ PATCH__OFFSET_5 = 0x0480,
+ PATCH__OFFSET_5_HI = 0x0480,
+ PATCH__OFFSET_5_LO = 0x0481,
+ PATCH__OFFSET_6 = 0x0482,
+ PATCH__OFFSET_6_HI = 0x0482,
+ PATCH__OFFSET_6_LO = 0x0483,
+ PATCH__OFFSET_7 = 0x0484,
+ PATCH__OFFSET_7_HI = 0x0484,
+ PATCH__OFFSET_7_LO = 0x0485,
+ PATCH__OFFSET_8 = 0x0486,
+ PATCH__OFFSET_8_HI = 0x0486,
+ PATCH__OFFSET_8_LO = 0x0487,
+ PATCH__OFFSET_9 = 0x0488,
+ PATCH__OFFSET_9_HI = 0x0488,
+ PATCH__OFFSET_9_LO = 0x0489,
+ PATCH__OFFSET_10 = 0x048A,
+ PATCH__OFFSET_10_HI = 0x048A,
+ PATCH__OFFSET_10_LO = 0x048B,
+ PATCH__OFFSET_11 = 0x048C,
+ PATCH__OFFSET_11_HI = 0x048C,
+ PATCH__OFFSET_11_LO = 0x048D,
+ PATCH__OFFSET_12 = 0x048E,
+ PATCH__OFFSET_12_HI = 0x048E,
+ PATCH__OFFSET_12_LO = 0x048F,
+ PATCH__OFFSET_13 = 0x0490,
+ PATCH__OFFSET_13_HI = 0x0490,
+ PATCH__OFFSET_13_LO = 0x0491,
+ PATCH__OFFSET_14 = 0x0492,
+ PATCH__OFFSET_14_HI = 0x0492,
+ PATCH__OFFSET_14_LO = 0x0493,
+ PATCH__OFFSET_15 = 0x0494,
+ PATCH__OFFSET_15_HI = 0x0494,
+ PATCH__OFFSET_15_LO = 0x0495,
+ PATCH__ADDRESS_0 = 0x0496,
+ PATCH__ADDRESS_0_HI = 0x0496,
+ PATCH__ADDRESS_0_LO = 0x0497,
+ PATCH__ADDRESS_1 = 0x0498,
+ PATCH__ADDRESS_1_HI = 0x0498,
+ PATCH__ADDRESS_1_LO = 0x0499,
+ PATCH__ADDRESS_2 = 0x049A,
+ PATCH__ADDRESS_2_HI = 0x049A,
+ PATCH__ADDRESS_2_LO = 0x049B,
+ PATCH__ADDRESS_3 = 0x049C,
+ PATCH__ADDRESS_3_HI = 0x049C,
+ PATCH__ADDRESS_3_LO = 0x049D,
+ PATCH__ADDRESS_4 = 0x049E,
+ PATCH__ADDRESS_4_HI = 0x049E,
+ PATCH__ADDRESS_4_LO = 0x049F,
+ PATCH__ADDRESS_5 = 0x04A0,
+ PATCH__ADDRESS_5_HI = 0x04A0,
+ PATCH__ADDRESS_5_LO = 0x04A1,
+ PATCH__ADDRESS_6 = 0x04A2,
+ PATCH__ADDRESS_6_HI = 0x04A2,
+ PATCH__ADDRESS_6_LO = 0x04A3,
+ PATCH__ADDRESS_7 = 0x04A4,
+ PATCH__ADDRESS_7_HI = 0x04A4,
+ PATCH__ADDRESS_7_LO = 0x04A5,
+ PATCH__ADDRESS_8 = 0x04A6,
+ PATCH__ADDRESS_8_HI = 0x04A6,
+ PATCH__ADDRESS_8_LO = 0x04A7,
+ PATCH__ADDRESS_9 = 0x04A8,
+ PATCH__ADDRESS_9_HI = 0x04A8,
+ PATCH__ADDRESS_9_LO = 0x04A9,
+ PATCH__ADDRESS_10 = 0x04AA,
+ PATCH__ADDRESS_10_HI = 0x04AA,
+ PATCH__ADDRESS_10_LO = 0x04AB,
+ PATCH__ADDRESS_11 = 0x04AC,
+ PATCH__ADDRESS_11_HI = 0x04AC,
+ PATCH__ADDRESS_11_LO = 0x04AD,
+ PATCH__ADDRESS_12 = 0x04AE,
+ PATCH__ADDRESS_12_HI = 0x04AE,
+ PATCH__ADDRESS_12_LO = 0x04AF,
+ PATCH__ADDRESS_13 = 0x04B0,
+ PATCH__ADDRESS_13_HI = 0x04B0,
+ PATCH__ADDRESS_13_LO = 0x04B1,
+ PATCH__ADDRESS_14 = 0x04B2,
+ PATCH__ADDRESS_14_HI = 0x04B2,
+ PATCH__ADDRESS_14_LO = 0x04B3,
+ PATCH__ADDRESS_15 = 0x04B4,
+ PATCH__ADDRESS_15_HI = 0x04B4,
+ PATCH__ADDRESS_15_LO = 0x04B5,
+ SPI_ASYNC_MUX__CTRL = 0x04C0,
+ CLK__CONFIG = 0x04C4,
+ GPIO_LV_MUX__CTRL = 0x04CC,
+ GPIO_LV_PAD__CTRL = 0x04CD,
+ PAD_I2C_LV__CONFIG = 0x04D0,
+ PAD_STARTUP_MODE__VALUE_RO_GO1 = 0x04D4,
+ HOST_IF__STATUS_GO1 = 0x04D5,
+ MCU_CLK_GATING__CTRL = 0x04D8,
+ TEST__BIST_ROM_CTRL = 0x04E0,
+ TEST__BIST_ROM_RESULT = 0x04E1,
+ TEST__BIST_ROM_MCU_SIG = 0x04E2,
+ TEST__BIST_ROM_MCU_SIG_HI = 0x04E2,
+ TEST__BIST_ROM_MCU_SIG_LO = 0x04E3,
+ TEST__BIST_RAM_CTRL = 0x04E4,
+ TEST__BIST_RAM_RESULT = 0x04E5,
+ TEST__TMC = 0x04E8,
+ TEST__PLL_BIST_MIN_THRESHOLD = 0x04F0,
+ TEST__PLL_BIST_MIN_THRESHOLD_HI = 0x04F0,
+ TEST__PLL_BIST_MIN_THRESHOLD_LO = 0x04F1,
+ TEST__PLL_BIST_MAX_THRESHOLD = 0x04F2,
+ TEST__PLL_BIST_MAX_THRESHOLD_HI = 0x04F2,
+ TEST__PLL_BIST_MAX_THRESHOLD_LO = 0x04F3,
+ TEST__PLL_BIST_COUNT_OUT = 0x04F4,
+ TEST__PLL_BIST_COUNT_OUT_HI = 0x04F4,
+ TEST__PLL_BIST_COUNT_OUT_LO = 0x04F5,
+ TEST__PLL_BIST_GONOGO = 0x04F6,
+ TEST__PLL_BIST_CTRL = 0x04F7,
+ RANGING_CORE__DEVICE_ID = 0x0680,
+ RANGING_CORE__REVISION_ID = 0x0681,
+ RANGING_CORE__CLK_CTRL1 = 0x0683,
+ RANGING_CORE__CLK_CTRL2 = 0x0684,
+ RANGING_CORE__WOI_1 = 0x0685,
+ RANGING_CORE__WOI_REF_1 = 0x0686,
+ RANGING_CORE__START_RANGING = 0x0687,
+ RANGING_CORE__LOW_LIMIT_1 = 0x0690,
+ RANGING_CORE__HIGH_LIMIT_1 = 0x0691,
+ RANGING_CORE__LOW_LIMIT_REF_1 = 0x0692,
+ RANGING_CORE__HIGH_LIMIT_REF_1 = 0x0693,
+ RANGING_CORE__QUANTIFIER_1_MSB = 0x0694,
+ RANGING_CORE__QUANTIFIER_1_LSB = 0x0695,
+ RANGING_CORE__QUANTIFIER_REF_1_MSB = 0x0696,
+ RANGING_CORE__QUANTIFIER_REF_1_LSB = 0x0697,
+ RANGING_CORE__AMBIENT_OFFSET_1_MSB = 0x0698,
+ RANGING_CORE__AMBIENT_OFFSET_1_LSB = 0x0699,
+ RANGING_CORE__AMBIENT_OFFSET_REF_1_MSB = 0x069A,
+ RANGING_CORE__AMBIENT_OFFSET_REF_1_LSB = 0x069B,
+ RANGING_CORE__FILTER_STRENGTH_1 = 0x069C,
+ RANGING_CORE__FILTER_STRENGTH_REF_1 = 0x069D,
+ RANGING_CORE__SIGNAL_EVENT_LIMIT_1_MSB = 0x069E,
+ RANGING_CORE__SIGNAL_EVENT_LIMIT_1_LSB = 0x069F,
+ RANGING_CORE__SIGNAL_EVENT_LIMIT_REF_1_MSB = 0x06A0,
+ RANGING_CORE__SIGNAL_EVENT_LIMIT_REF_1_LSB = 0x06A1,
+ RANGING_CORE__TIMEOUT_OVERALL_PERIODS_MSB = 0x06A4,
+ RANGING_CORE__TIMEOUT_OVERALL_PERIODS_LSB = 0x06A5,
+ RANGING_CORE__INVERT_HW = 0x06A6,
+ RANGING_CORE__FORCE_HW = 0x06A7,
+ RANGING_CORE__STATIC_HW_VALUE = 0x06A8,
+ RANGING_CORE__FORCE_CONTINUOUS_AMBIENT = 0x06A9,
+ RANGING_CORE__TEST_PHASE_SELECT_TO_FILTER = 0x06AA,
+ RANGING_CORE__TEST_PHASE_SELECT_TO_TIMING_GEN = 0x06AB,
+ RANGING_CORE__INITIAL_PHASE_VALUE_1 = 0x06AC,
+ RANGING_CORE__INITIAL_PHASE_VALUE_REF_1 = 0x06AD,
+ RANGING_CORE__FORCE_UP_IN = 0x06AE,
+ RANGING_CORE__FORCE_DN_IN = 0x06AF,
+ RANGING_CORE__STATIC_UP_VALUE_1 = 0x06B0,
+ RANGING_CORE__STATIC_UP_VALUE_REF_1 = 0x06B1,
+ RANGING_CORE__STATIC_DN_VALUE_1 = 0x06B2,
+ RANGING_CORE__STATIC_DN_VALUE_REF_1 = 0x06B3,
+ RANGING_CORE__MONITOR_UP_DN = 0x06B4,
+ RANGING_CORE__INVERT_UP_DN = 0x06B5,
+ RANGING_CORE__CPUMP_1 = 0x06B6,
+ RANGING_CORE__CPUMP_2 = 0x06B7,
+ RANGING_CORE__CPUMP_3 = 0x06B8,
+ RANGING_CORE__OSC_1 = 0x06B9,
+ RANGING_CORE__PLL_1 = 0x06BB,
+ RANGING_CORE__PLL_2 = 0x06BC,
+ RANGING_CORE__REFERENCE_1 = 0x06BD,
+ RANGING_CORE__REFERENCE_3 = 0x06BF,
+ RANGING_CORE__REFERENCE_4 = 0x06C0,
+ RANGING_CORE__REFERENCE_5 = 0x06C1,
+ RANGING_CORE__REGAVDD1V2 = 0x06C3,
+ RANGING_CORE__CALIB_1 = 0x06C4,
+ RANGING_CORE__CALIB_2 = 0x06C5,
+ RANGING_CORE__CALIB_3 = 0x06C6,
+ RANGING_CORE__TST_MUX_SEL1 = 0x06C9,
+ RANGING_CORE__TST_MUX_SEL2 = 0x06CA,
+ RANGING_CORE__TST_MUX = 0x06CB,
+ RANGING_CORE__GPIO_OUT_TESTMUX = 0x06CC,
+ RANGING_CORE__CUSTOM_FE = 0x06CD,
+ RANGING_CORE__CUSTOM_FE_2 = 0x06CE,
+ RANGING_CORE__SPAD_READOUT = 0x06CF,
+ RANGING_CORE__SPAD_READOUT_1 = 0x06D0,
+ RANGING_CORE__SPAD_READOUT_2 = 0x06D1,
+ RANGING_CORE__SPAD_PS = 0x06D2,
+ RANGING_CORE__LASER_SAFETY_2 = 0x06D4,
+ RANGING_CORE__NVM_CTRL__MODE = 0x0780,
+ RANGING_CORE__NVM_CTRL__PDN = 0x0781,
+ RANGING_CORE__NVM_CTRL__PROGN = 0x0782,
+ RANGING_CORE__NVM_CTRL__READN = 0x0783,
+ RANGING_CORE__NVM_CTRL__PULSE_WIDTH_MSB = 0x0784,
+ RANGING_CORE__NVM_CTRL__PULSE_WIDTH_LSB = 0x0785,
+ RANGING_CORE__NVM_CTRL__HV_RISE_MSB = 0x0786,
+ RANGING_CORE__NVM_CTRL__HV_RISE_LSB = 0x0787,
+ RANGING_CORE__NVM_CTRL__HV_FALL_MSB = 0x0788,
+ RANGING_CORE__NVM_CTRL__HV_FALL_LSB = 0x0789,
+ RANGING_CORE__NVM_CTRL__TST = 0x078A,
+ RANGING_CORE__NVM_CTRL__TESTREAD = 0x078B,
+ RANGING_CORE__NVM_CTRL__DATAIN_MMM = 0x078C,
+ RANGING_CORE__NVM_CTRL__DATAIN_LMM = 0x078D,
+ RANGING_CORE__NVM_CTRL__DATAIN_LLM = 0x078E,
+ RANGING_CORE__NVM_CTRL__DATAIN_LLL = 0x078F,
+ RANGING_CORE__NVM_CTRL__DATAOUT_MMM = 0x0790,
+ RANGING_CORE__NVM_CTRL__DATAOUT_LMM = 0x0791,
+ RANGING_CORE__NVM_CTRL__DATAOUT_LLM = 0x0792,
+ RANGING_CORE__NVM_CTRL__DATAOUT_LLL = 0x0793,
+ RANGING_CORE__NVM_CTRL__ADDR = 0x0794,
+ RANGING_CORE__NVM_CTRL__DATAOUT_ECC = 0x0795,
+ RANGING_CORE__RET_SPAD_EN_0 = 0x0796,
+ RANGING_CORE__RET_SPAD_EN_1 = 0x0797,
+ RANGING_CORE__RET_SPAD_EN_2 = 0x0798,
+ RANGING_CORE__RET_SPAD_EN_3 = 0x0799,
+ RANGING_CORE__RET_SPAD_EN_4 = 0x079A,
+ RANGING_CORE__RET_SPAD_EN_5 = 0x079B,
+ RANGING_CORE__RET_SPAD_EN_6 = 0x079C,
+ RANGING_CORE__RET_SPAD_EN_7 = 0x079D,
+ RANGING_CORE__RET_SPAD_EN_8 = 0x079E,
+ RANGING_CORE__RET_SPAD_EN_9 = 0x079F,
+ RANGING_CORE__RET_SPAD_EN_10 = 0x07A0,
+ RANGING_CORE__RET_SPAD_EN_11 = 0x07A1,
+ RANGING_CORE__RET_SPAD_EN_12 = 0x07A2,
+ RANGING_CORE__RET_SPAD_EN_13 = 0x07A3,
+ RANGING_CORE__RET_SPAD_EN_14 = 0x07A4,
+ RANGING_CORE__RET_SPAD_EN_15 = 0x07A5,
+ RANGING_CORE__RET_SPAD_EN_16 = 0x07A6,
+ RANGING_CORE__RET_SPAD_EN_17 = 0x07A7,
+ RANGING_CORE__SPAD_SHIFT_EN = 0x07BA,
+ RANGING_CORE__SPAD_DISABLE_CTRL = 0x07BB,
+ RANGING_CORE__SPAD_EN_SHIFT_OUT_DEBUG = 0x07BC,
+ RANGING_CORE__SPI_MODE = 0x07BD,
+ RANGING_CORE__GPIO_DIR = 0x07BE,
+ RANGING_CORE__VCSEL_PERIOD = 0x0880,
+ RANGING_CORE__VCSEL_START = 0x0881,
+ RANGING_CORE__VCSEL_STOP = 0x0882,
+ RANGING_CORE__VCSEL_1 = 0x0885,
+ RANGING_CORE__VCSEL_STATUS = 0x088D,
+ RANGING_CORE__STATUS = 0x0980,
+ RANGING_CORE__LASER_CONTINUITY_STATE = 0x0981,
+ RANGING_CORE__RANGE_1_MMM = 0x0982,
+ RANGING_CORE__RANGE_1_LMM = 0x0983,
+ RANGING_CORE__RANGE_1_LLM = 0x0984,
+ RANGING_CORE__RANGE_1_LLL = 0x0985,
+ RANGING_CORE__RANGE_REF_1_MMM = 0x0986,
+ RANGING_CORE__RANGE_REF_1_LMM = 0x0987,
+ RANGING_CORE__RANGE_REF_1_LLM = 0x0988,
+ RANGING_CORE__RANGE_REF_1_LLL = 0x0989,
+ RANGING_CORE__AMBIENT_WINDOW_EVENTS_1_MMM = 0x098A,
+ RANGING_CORE__AMBIENT_WINDOW_EVENTS_1_LMM = 0x098B,
+ RANGING_CORE__AMBIENT_WINDOW_EVENTS_1_LLM = 0x098C,
+ RANGING_CORE__AMBIENT_WINDOW_EVENTS_1_LLL = 0x098D,
+ RANGING_CORE__RANGING_TOTAL_EVENTS_1_MMM = 0x098E,
+ RANGING_CORE__RANGING_TOTAL_EVENTS_1_LMM = 0x098F,
+ RANGING_CORE__RANGING_TOTAL_EVENTS_1_LLM = 0x0990,
+ RANGING_CORE__RANGING_TOTAL_EVENTS_1_LLL = 0x0991,
+ RANGING_CORE__SIGNAL_TOTAL_EVENTS_1_MMM = 0x0992,
+ RANGING_CORE__SIGNAL_TOTAL_EVENTS_1_LMM = 0x0993,
+ RANGING_CORE__SIGNAL_TOTAL_EVENTS_1_LLM = 0x0994,
+ RANGING_CORE__SIGNAL_TOTAL_EVENTS_1_LLL = 0x0995,
+ RANGING_CORE__TOTAL_PERIODS_ELAPSED_1_MM = 0x0996,
+ RANGING_CORE__TOTAL_PERIODS_ELAPSED_1_LM = 0x0997,
+ RANGING_CORE__TOTAL_PERIODS_ELAPSED_1_LL = 0x0998,
+ RANGING_CORE__AMBIENT_MISMATCH_MM = 0x0999,
+ RANGING_CORE__AMBIENT_MISMATCH_LM = 0x099A,
+ RANGING_CORE__AMBIENT_MISMATCH_LL = 0x099B,
+ RANGING_CORE__AMBIENT_WINDOW_EVENTS_REF_1_MMM = 0x099C,
+ RANGING_CORE__AMBIENT_WINDOW_EVENTS_REF_1_LMM = 0x099D,
+ RANGING_CORE__AMBIENT_WINDOW_EVENTS_REF_1_LLM = 0x099E,
+ RANGING_CORE__AMBIENT_WINDOW_EVENTS_REF_1_LLL = 0x099F,
+ RANGING_CORE__RANGING_TOTAL_EVENTS_REF_1_MMM = 0x09A0,
+ RANGING_CORE__RANGING_TOTAL_EVENTS_REF_1_LMM = 0x09A1,
+ RANGING_CORE__RANGING_TOTAL_EVENTS_REF_1_LLM = 0x09A2,
+ RANGING_CORE__RANGING_TOTAL_EVENTS_REF_1_LLL = 0x09A3,
+ RANGING_CORE__SIGNAL_TOTAL_EVENTS_REF_1_MMM = 0x09A4,
+ RANGING_CORE__SIGNAL_TOTAL_EVENTS_REF_1_LMM = 0x09A5,
+ RANGING_CORE__SIGNAL_TOTAL_EVENTS_REF_1_LLM = 0x09A6,
+ RANGING_CORE__SIGNAL_TOTAL_EVENTS_REF_1_LLL = 0x09A7,
+ RANGING_CORE__TOTAL_PERIODS_ELAPSED_REF_1_MM = 0x09A8,
+ RANGING_CORE__TOTAL_PERIODS_ELAPSED_REF_1_LM = 0x09A9,
+ RANGING_CORE__TOTAL_PERIODS_ELAPSED_REF_1_LL = 0x09AA,
+ RANGING_CORE__AMBIENT_MISMATCH_REF_MM = 0x09AB,
+ RANGING_CORE__AMBIENT_MISMATCH_REF_LM = 0x09AC,
+ RANGING_CORE__AMBIENT_MISMATCH_REF_LL = 0x09AD,
+ RANGING_CORE__GPIO_CONFIG__A0 = 0x0A00,
+ RANGING_CORE__RESET_CONTROL__A0 = 0x0A01,
+ RANGING_CORE__INTR_MANAGER__A0 = 0x0A02,
+ RANGING_CORE__POWER_FSM_TIME_OSC__A0 = 0x0A06,
+ RANGING_CORE__VCSEL_ATEST__A0 = 0x0A07,
+ RANGING_CORE__VCSEL_PERIOD_CLIPPED__A0 = 0x0A08,
+ RANGING_CORE__VCSEL_STOP_CLIPPED__A0 = 0x0A09,
+ RANGING_CORE__CALIB_2__A0 = 0x0A0A,
+ RANGING_CORE__STOP_CONDITION__A0 = 0x0A0B,
+ RANGING_CORE__STATUS_RESET__A0 = 0x0A0C,
+ RANGING_CORE__READOUT_CFG__A0 = 0x0A0D,
+ RANGING_CORE__WINDOW_SETTING__A0 = 0x0A0E,
+ RANGING_CORE__VCSEL_DELAY__A0 = 0x0A1A,
+ RANGING_CORE__REFERENCE_2__A0 = 0x0A1B,
+ RANGING_CORE__REGAVDD1V2__A0 = 0x0A1D,
+ RANGING_CORE__TST_MUX__A0 = 0x0A1F,
+ RANGING_CORE__CUSTOM_FE_2__A0 = 0x0A20,
+ RANGING_CORE__SPAD_READOUT__A0 = 0x0A21,
+ RANGING_CORE__CPUMP_1__A0 = 0x0A22,
+ RANGING_CORE__SPARE_REGISTER__A0 = 0x0A23,
+ RANGING_CORE__VCSEL_CONT_STAGE5_BYPASS__A0 = 0x0A24,
+ RANGING_CORE__RET_SPAD_EN_18 = 0x0A25,
+ RANGING_CORE__RET_SPAD_EN_19 = 0x0A26,
+ RANGING_CORE__RET_SPAD_EN_20 = 0x0A27,
+ RANGING_CORE__RET_SPAD_EN_21 = 0x0A28,
+ RANGING_CORE__RET_SPAD_EN_22 = 0x0A29,
+ RANGING_CORE__RET_SPAD_EN_23 = 0x0A2A,
+ RANGING_CORE__RET_SPAD_EN_24 = 0x0A2B,
+ RANGING_CORE__RET_SPAD_EN_25 = 0x0A2C,
+ RANGING_CORE__RET_SPAD_EN_26 = 0x0A2D,
+ RANGING_CORE__RET_SPAD_EN_27 = 0x0A2E,
+ RANGING_CORE__RET_SPAD_EN_28 = 0x0A2F,
+ RANGING_CORE__RET_SPAD_EN_29 = 0x0A30,
+ RANGING_CORE__RET_SPAD_EN_30 = 0x0A31,
+ RANGING_CORE__RET_SPAD_EN_31 = 0x0A32,
+ RANGING_CORE__REF_SPAD_EN_0__EWOK = 0x0A33,
+ RANGING_CORE__REF_SPAD_EN_1__EWOK = 0x0A34,
+ RANGING_CORE__REF_SPAD_EN_2__EWOK = 0x0A35,
+ RANGING_CORE__REF_SPAD_EN_3__EWOK = 0x0A36,
+ RANGING_CORE__REF_SPAD_EN_4__EWOK = 0x0A37,
+ RANGING_CORE__REF_SPAD_EN_5__EWOK = 0x0A38,
+ RANGING_CORE__REF_EN_START_SELECT = 0x0A39,
+ RANGING_CORE__REGDVDD1V2_ATEST__EWOK = 0x0A41,
+ SOFT_RESET_GO1 = 0x0B00,
+ PRIVATE__PATCH_BASE_ADDR_RSLV = 0x0E00,
+ PREV_SHADOW_RESULT__INTERRUPT_STATUS = 0x0ED0,
+ PREV_SHADOW_RESULT__RANGE_STATUS = 0x0ED1,
+ PREV_SHADOW_RESULT__REPORT_STATUS = 0x0ED2,
+ PREV_SHADOW_RESULT__STREAM_COUNT = 0x0ED3,
+ PREV_SHADOW_RESULT__DSS_ACTUAL_EFFECTIVE_SPADS_SD0 = 0x0ED4,
+ PREV_SHADOW_RESULT__DSS_ACTUAL_EFFECTIVE_SPADS_SD0_HI = 0x0ED4,
+ PREV_SHADOW_RESULT__DSS_ACTUAL_EFFECTIVE_SPADS_SD0_LO = 0x0ED5,
+ PREV_SHADOW_RESULT__PEAK_SIGNAL_COUNT_RATE_MCPS_SD0 = 0x0ED6,
+ PREV_SHADOW_RESULT__PEAK_SIGNAL_COUNT_RATE_MCPS_SD0_HI = 0x0ED6,
+ PREV_SHADOW_RESULT__PEAK_SIGNAL_COUNT_RATE_MCPS_SD0_LO = 0x0ED7,
+ PREV_SHADOW_RESULT__AMBIENT_COUNT_RATE_MCPS_SD0 = 0x0ED8,
+ PREV_SHADOW_RESULT__AMBIENT_COUNT_RATE_MCPS_SD0_HI = 0x0ED8,
+ PREV_SHADOW_RESULT__AMBIENT_COUNT_RATE_MCPS_SD0_LO = 0x0ED9,
+ PREV_SHADOW_RESULT__SIGMA_SD0 = 0x0EDA,
+ PREV_SHADOW_RESULT__SIGMA_SD0_HI = 0x0EDA,
+ PREV_SHADOW_RESULT__SIGMA_SD0_LO = 0x0EDB,
+ PREV_SHADOW_RESULT__PHASE_SD0 = 0x0EDC,
+ PREV_SHADOW_RESULT__PHASE_SD0_HI = 0x0EDC,
+ PREV_SHADOW_RESULT__PHASE_SD0_LO = 0x0EDD,
+ PREV_SHADOW_RESULT__FINAL_CROSSTALK_CORRECTED_RANGE_MM_SD0 = 0x0EDE,
+ PREV_SHADOW_RESULT__FINAL_CROSSTALK_CORRECTED_RANGE_MM_SD0_HI = 0x0EDE,
+ PREV_SHADOW_RESULT__FINAL_CROSSTALK_CORRECTED_RANGE_MM_SD0_LO = 0x0EDF,
+ PREV_SHADOW_RESULT__PEAK_SIGNAL_COUNT_RATE_CROSSTALK_CORRECTED_MCPS_SD0 = 0x0EE0,
+ PREV_SHADOW_RESULT__PEAK_SIGNAL_COUNT_RATE_CROSSTALK_CORRECTED_MCPS_SD0_HI = 0x0EE0,
+ PREV_SHADOW_RESULT__PEAK_SIGNAL_COUNT_RATE_CROSSTALK_CORRECTED_MCPS_SD0_LO = 0x0EE1,
+ PREV_SHADOW_RESULT__MM_INNER_ACTUAL_EFFECTIVE_SPADS_SD0 = 0x0EE2,
+ PREV_SHADOW_RESULT__MM_INNER_ACTUAL_EFFECTIVE_SPADS_SD0_HI = 0x0EE2,
+ PREV_SHADOW_RESULT__MM_INNER_ACTUAL_EFFECTIVE_SPADS_SD0_LO = 0x0EE3,
+ PREV_SHADOW_RESULT__MM_OUTER_ACTUAL_EFFECTIVE_SPADS_SD0 = 0x0EE4,
+ PREV_SHADOW_RESULT__MM_OUTER_ACTUAL_EFFECTIVE_SPADS_SD0_HI = 0x0EE4,
+ PREV_SHADOW_RESULT__MM_OUTER_ACTUAL_EFFECTIVE_SPADS_SD0_LO = 0x0EE5,
+ PREV_SHADOW_RESULT__AVG_SIGNAL_COUNT_RATE_MCPS_SD0 = 0x0EE6,
+ PREV_SHADOW_RESULT__AVG_SIGNAL_COUNT_RATE_MCPS_SD0_HI = 0x0EE6,
+ PREV_SHADOW_RESULT__AVG_SIGNAL_COUNT_RATE_MCPS_SD0_LO = 0x0EE7,
+ PREV_SHADOW_RESULT__DSS_ACTUAL_EFFECTIVE_SPADS_SD1 = 0x0EE8,
+ PREV_SHADOW_RESULT__DSS_ACTUAL_EFFECTIVE_SPADS_SD1_HI = 0x0EE8,
+ PREV_SHADOW_RESULT__DSS_ACTUAL_EFFECTIVE_SPADS_SD1_LO = 0x0EE9,
+ PREV_SHADOW_RESULT__PEAK_SIGNAL_COUNT_RATE_MCPS_SD1 = 0x0EEA,
+ PREV_SHADOW_RESULT__PEAK_SIGNAL_COUNT_RATE_MCPS_SD1_HI = 0x0EEA,
+ PREV_SHADOW_RESULT__PEAK_SIGNAL_COUNT_RATE_MCPS_SD1_LO = 0x0EEB,
+ PREV_SHADOW_RESULT__AMBIENT_COUNT_RATE_MCPS_SD1 = 0x0EEC,
+ PREV_SHADOW_RESULT__AMBIENT_COUNT_RATE_MCPS_SD1_HI = 0x0EEC,
+ PREV_SHADOW_RESULT__AMBIENT_COUNT_RATE_MCPS_SD1_LO = 0x0EED,
+ PREV_SHADOW_RESULT__SIGMA_SD1 = 0x0EEE,
+ PREV_SHADOW_RESULT__SIGMA_SD1_HI = 0x0EEE,
+ PREV_SHADOW_RESULT__SIGMA_SD1_LO = 0x0EEF,
+ PREV_SHADOW_RESULT__PHASE_SD1 = 0x0EF0,
+ PREV_SHADOW_RESULT__PHASE_SD1_HI = 0x0EF0,
+ PREV_SHADOW_RESULT__PHASE_SD1_LO = 0x0EF1,
+ PREV_SHADOW_RESULT__FINAL_CROSSTALK_CORRECTED_RANGE_MM_SD1 = 0x0EF2,
+ PREV_SHADOW_RESULT__FINAL_CROSSTALK_CORRECTED_RANGE_MM_SD1_HI = 0x0EF2,
+ PREV_SHADOW_RESULT__FINAL_CROSSTALK_CORRECTED_RANGE_MM_SD1_LO = 0x0EF3,
+ PREV_SHADOW_RESULT__SPARE_0_SD1 = 0x0EF4,
+ PREV_SHADOW_RESULT__SPARE_0_SD1_HI = 0x0EF4,
+ PREV_SHADOW_RESULT__SPARE_0_SD1_LO = 0x0EF5,
+ PREV_SHADOW_RESULT__SPARE_1_SD1 = 0x0EF6,
+ PREV_SHADOW_RESULT__SPARE_1_SD1_HI = 0x0EF6,
+ PREV_SHADOW_RESULT__SPARE_1_SD1_LO = 0x0EF7,
+ PREV_SHADOW_RESULT__SPARE_2_SD1 = 0x0EF8,
+ PREV_SHADOW_RESULT__SPARE_2_SD1_HI = 0x0EF8,
+ PREV_SHADOW_RESULT__SPARE_2_SD1_LO = 0x0EF9,
+ PREV_SHADOW_RESULT__SPARE_3_SD1 = 0x0EFA,
+ PREV_SHADOW_RESULT__SPARE_3_SD1_HI = 0x0EFA,
+ PREV_SHADOW_RESULT__SPARE_3_SD1_LO = 0x0EFB,
+ PREV_SHADOW_RESULT_CORE__AMBIENT_WINDOW_EVENTS_SD0 = 0x0EFC,
+ PREV_SHADOW_RESULT_CORE__AMBIENT_WINDOW_EVENTS_SD0_3 = 0x0EFC,
+ PREV_SHADOW_RESULT_CORE__AMBIENT_WINDOW_EVENTS_SD0_2 = 0x0EFD,
+ PREV_SHADOW_RESULT_CORE__AMBIENT_WINDOW_EVENTS_SD0_1 = 0x0EFE,
+ PREV_SHADOW_RESULT_CORE__AMBIENT_WINDOW_EVENTS_SD0_0 = 0x0EFF,
+ PREV_SHADOW_RESULT_CORE__RANGING_TOTAL_EVENTS_SD0 = 0x0F00,
+ PREV_SHADOW_RESULT_CORE__RANGING_TOTAL_EVENTS_SD0_3 = 0x0F00,
+ PREV_SHADOW_RESULT_CORE__RANGING_TOTAL_EVENTS_SD0_2 = 0x0F01,
+ PREV_SHADOW_RESULT_CORE__RANGING_TOTAL_EVENTS_SD0_1 = 0x0F02,
+ PREV_SHADOW_RESULT_CORE__RANGING_TOTAL_EVENTS_SD0_0 = 0x0F03,
+ PREV_SHADOW_RESULT_CORE__SIGNAL_TOTAL_EVENTS_SD0 = 0x0F04,
+ PREV_SHADOW_RESULT_CORE__SIGNAL_TOTAL_EVENTS_SD0_3 = 0x0F04,
+ PREV_SHADOW_RESULT_CORE__SIGNAL_TOTAL_EVENTS_SD0_2 = 0x0F05,
+ PREV_SHADOW_RESULT_CORE__SIGNAL_TOTAL_EVENTS_SD0_1 = 0x0F06,
+ PREV_SHADOW_RESULT_CORE__SIGNAL_TOTAL_EVENTS_SD0_0 = 0x0F07,
+ PREV_SHADOW_RESULT_CORE__TOTAL_PERIODS_ELAPSED_SD0 = 0x0F08,
+ PREV_SHADOW_RESULT_CORE__TOTAL_PERIODS_ELAPSED_SD0_3 = 0x0F08,
+ PREV_SHADOW_RESULT_CORE__TOTAL_PERIODS_ELAPSED_SD0_2 = 0x0F09,
+ PREV_SHADOW_RESULT_CORE__TOTAL_PERIODS_ELAPSED_SD0_1 = 0x0F0A,
+ PREV_SHADOW_RESULT_CORE__TOTAL_PERIODS_ELAPSED_SD0_0 = 0x0F0B,
+ PREV_SHADOW_RESULT_CORE__AMBIENT_WINDOW_EVENTS_SD1 = 0x0F0C,
+ PREV_SHADOW_RESULT_CORE__AMBIENT_WINDOW_EVENTS_SD1_3 = 0x0F0C,
+ PREV_SHADOW_RESULT_CORE__AMBIENT_WINDOW_EVENTS_SD1_2 = 0x0F0D,
+ PREV_SHADOW_RESULT_CORE__AMBIENT_WINDOW_EVENTS_SD1_1 = 0x0F0E,
+ PREV_SHADOW_RESULT_CORE__AMBIENT_WINDOW_EVENTS_SD1_0 = 0x0F0F,
+ PREV_SHADOW_RESULT_CORE__RANGING_TOTAL_EVENTS_SD1 = 0x0F10,
+ PREV_SHADOW_RESULT_CORE__RANGING_TOTAL_EVENTS_SD1_3 = 0x0F10,
+ PREV_SHADOW_RESULT_CORE__RANGING_TOTAL_EVENTS_SD1_2 = 0x0F11,
+ PREV_SHADOW_RESULT_CORE__RANGING_TOTAL_EVENTS_SD1_1 = 0x0F12,
+ PREV_SHADOW_RESULT_CORE__RANGING_TOTAL_EVENTS_SD1_0 = 0x0F13,
+ PREV_SHADOW_RESULT_CORE__SIGNAL_TOTAL_EVENTS_SD1 = 0x0F14,
+ PREV_SHADOW_RESULT_CORE__SIGNAL_TOTAL_EVENTS_SD1_3 = 0x0F14,
+ PREV_SHADOW_RESULT_CORE__SIGNAL_TOTAL_EVENTS_SD1_2 = 0x0F15,
+ PREV_SHADOW_RESULT_CORE__SIGNAL_TOTAL_EVENTS_SD1_1 = 0x0F16,
+ PREV_SHADOW_RESULT_CORE__SIGNAL_TOTAL_EVENTS_SD1_0 = 0x0F17,
+ PREV_SHADOW_RESULT_CORE__TOTAL_PERIODS_ELAPSED_SD1 = 0x0F18,
+ PREV_SHADOW_RESULT_CORE__TOTAL_PERIODS_ELAPSED_SD1_3 = 0x0F18,
+ PREV_SHADOW_RESULT_CORE__TOTAL_PERIODS_ELAPSED_SD1_2 = 0x0F19,
+ PREV_SHADOW_RESULT_CORE__TOTAL_PERIODS_ELAPSED_SD1_1 = 0x0F1A,
+ PREV_SHADOW_RESULT_CORE__TOTAL_PERIODS_ELAPSED_SD1_0 = 0x0F1B,
+ PREV_SHADOW_RESULT_CORE__SPARE_0 = 0x0F1C,
+ RESULT__DEBUG_STATUS = 0x0F20,
+ RESULT__DEBUG_STAGE = 0x0F21,
+ GPH__SYSTEM__THRESH_RATE_HIGH = 0x0F24,
+ GPH__SYSTEM__THRESH_RATE_HIGH_HI = 0x0F24,
+ GPH__SYSTEM__THRESH_RATE_HIGH_LO = 0x0F25,
+ GPH__SYSTEM__THRESH_RATE_LOW = 0x0F26,
+ GPH__SYSTEM__THRESH_RATE_LOW_HI = 0x0F26,
+ GPH__SYSTEM__THRESH_RATE_LOW_LO = 0x0F27,
+ GPH__SYSTEM__INTERRUPT_CONFIG_GPIO = 0x0F28,
+ GPH__DSS_CONFIG__ROI_MODE_CONTROL = 0x0F2F,
+ GPH__DSS_CONFIG__MANUAL_EFFECTIVE_SPADS_SELECT = 0x0F30,
+ GPH__DSS_CONFIG__MANUAL_EFFECTIVE_SPADS_SELECT_HI = 0x0F30,
+ GPH__DSS_CONFIG__MANUAL_EFFECTIVE_SPADS_SELECT_LO = 0x0F31,
+ GPH__DSS_CONFIG__MANUAL_BLOCK_SELECT = 0x0F32,
+ GPH__DSS_CONFIG__MAX_SPADS_LIMIT = 0x0F33,
+ GPH__DSS_CONFIG__MIN_SPADS_LIMIT = 0x0F34,
+ GPH__MM_CONFIG__TIMEOUT_MACROP_A_HI = 0x0F36,
+ GPH__MM_CONFIG__TIMEOUT_MACROP_A_LO = 0x0F37,
+ GPH__MM_CONFIG__TIMEOUT_MACROP_B_HI = 0x0F38,
+ GPH__MM_CONFIG__TIMEOUT_MACROP_B_LO = 0x0F39,
+ GPH__RANGE_CONFIG__TIMEOUT_MACROP_A_HI = 0x0F3A,
+ GPH__RANGE_CONFIG__TIMEOUT_MACROP_A_LO = 0x0F3B,
+ GPH__RANGE_CONFIG__VCSEL_PERIOD_A = 0x0F3C,
+ GPH__RANGE_CONFIG__VCSEL_PERIOD_B = 0x0F3D,
+ GPH__RANGE_CONFIG__TIMEOUT_MACROP_B_HI = 0x0F3E,
+ GPH__RANGE_CONFIG__TIMEOUT_MACROP_B_LO = 0x0F3F,
+ GPH__RANGE_CONFIG__SIGMA_THRESH = 0x0F40,
+ GPH__RANGE_CONFIG__SIGMA_THRESH_HI = 0x0F40,
+ GPH__RANGE_CONFIG__SIGMA_THRESH_LO = 0x0F41,
+ GPH__RANGE_CONFIG__MIN_COUNT_RATE_RTN_LIMIT_MCPS = 0x0F42,
+ GPH__RANGE_CONFIG__MIN_COUNT_RATE_RTN_LIMIT_MCPS_HI = 0x0F42,
+ GPH__RANGE_CONFIG__MIN_COUNT_RATE_RTN_LIMIT_MCPS_LO = 0x0F43,
+ GPH__RANGE_CONFIG__VALID_PHASE_LOW = 0x0F44,
+ GPH__RANGE_CONFIG__VALID_PHASE_HIGH = 0x0F45,
+ FIRMWARE__INTERNAL_STREAM_COUNT_DIV = 0x0F46,
+ FIRMWARE__INTERNAL_STREAM_COUNTER_VAL = 0x0F47,
+ DSS_CALC__ROI_CTRL = 0x0F54,
+ DSS_CALC__SPARE_1 = 0x0F55,
+ DSS_CALC__SPARE_2 = 0x0F56,
+ DSS_CALC__SPARE_3 = 0x0F57,
+ DSS_CALC__SPARE_4 = 0x0F58,
+ DSS_CALC__SPARE_5 = 0x0F59,
+ DSS_CALC__SPARE_6 = 0x0F5A,
+ DSS_CALC__SPARE_7 = 0x0F5B,
+ DSS_CALC__USER_ROI_SPAD_EN_0 = 0x0F5C,
+ DSS_CALC__USER_ROI_SPAD_EN_1 = 0x0F5D,
+ DSS_CALC__USER_ROI_SPAD_EN_2 = 0x0F5E,
+ DSS_CALC__USER_ROI_SPAD_EN_3 = 0x0F5F,
+ DSS_CALC__USER_ROI_SPAD_EN_4 = 0x0F60,
+ DSS_CALC__USER_ROI_SPAD_EN_5 = 0x0F61,
+ DSS_CALC__USER_ROI_SPAD_EN_6 = 0x0F62,
+ DSS_CALC__USER_ROI_SPAD_EN_7 = 0x0F63,
+ DSS_CALC__USER_ROI_SPAD_EN_8 = 0x0F64,
+ DSS_CALC__USER_ROI_SPAD_EN_9 = 0x0F65,
+ DSS_CALC__USER_ROI_SPAD_EN_10 = 0x0F66,
+ DSS_CALC__USER_ROI_SPAD_EN_11 = 0x0F67,
+ DSS_CALC__USER_ROI_SPAD_EN_12 = 0x0F68,
+ DSS_CALC__USER_ROI_SPAD_EN_13 = 0x0F69,
+ DSS_CALC__USER_ROI_SPAD_EN_14 = 0x0F6A,
+ DSS_CALC__USER_ROI_SPAD_EN_15 = 0x0F6B,
+ DSS_CALC__USER_ROI_SPAD_EN_16 = 0x0F6C,
+ DSS_CALC__USER_ROI_SPAD_EN_17 = 0x0F6D,
+ DSS_CALC__USER_ROI_SPAD_EN_18 = 0x0F6E,
+ DSS_CALC__USER_ROI_SPAD_EN_19 = 0x0F6F,
+ DSS_CALC__USER_ROI_SPAD_EN_20 = 0x0F70,
+ DSS_CALC__USER_ROI_SPAD_EN_21 = 0x0F71,
+ DSS_CALC__USER_ROI_SPAD_EN_22 = 0x0F72,
+ DSS_CALC__USER_ROI_SPAD_EN_23 = 0x0F73,
+ DSS_CALC__USER_ROI_SPAD_EN_24 = 0x0F74,
+ DSS_CALC__USER_ROI_SPAD_EN_25 = 0x0F75,
+ DSS_CALC__USER_ROI_SPAD_EN_26 = 0x0F76,
+ DSS_CALC__USER_ROI_SPAD_EN_27 = 0x0F77,
+ DSS_CALC__USER_ROI_SPAD_EN_28 = 0x0F78,
+ DSS_CALC__USER_ROI_SPAD_EN_29 = 0x0F79,
+ DSS_CALC__USER_ROI_SPAD_EN_30 = 0x0F7A,
+ DSS_CALC__USER_ROI_SPAD_EN_31 = 0x0F7B,
+ DSS_CALC__USER_ROI_0 = 0x0F7C,
+ DSS_CALC__USER_ROI_1 = 0x0F7D,
+ DSS_CALC__MODE_ROI_0 = 0x0F7E,
+ DSS_CALC__MODE_ROI_1 = 0x0F7F,
+ SIGMA_ESTIMATOR_CALC__SPARE_0 = 0x0F80,
+ VHV_RESULT__PEAK_SIGNAL_RATE_MCPS = 0x0F82,
+ VHV_RESULT__PEAK_SIGNAL_RATE_MCPS_HI = 0x0F82,
+ VHV_RESULT__PEAK_SIGNAL_RATE_MCPS_LO = 0x0F83,
+ VHV_RESULT__SIGNAL_TOTAL_EVENTS_REF = 0x0F84,
+ VHV_RESULT__SIGNAL_TOTAL_EVENTS_REF_3 = 0x0F84,
+ VHV_RESULT__SIGNAL_TOTAL_EVENTS_REF_2 = 0x0F85,
+ VHV_RESULT__SIGNAL_TOTAL_EVENTS_REF_1 = 0x0F86,
+ VHV_RESULT__SIGNAL_TOTAL_EVENTS_REF_0 = 0x0F87,
+ PHASECAL_RESULT__PHASE_OUTPUT_REF = 0x0F88,
+ PHASECAL_RESULT__PHASE_OUTPUT_REF_HI = 0x0F88,
+ PHASECAL_RESULT__PHASE_OUTPUT_REF_LO = 0x0F89,
+ DSS_RESULT__TOTAL_RATE_PER_SPAD = 0x0F8A,
+ DSS_RESULT__TOTAL_RATE_PER_SPAD_HI = 0x0F8A,
+ DSS_RESULT__TOTAL_RATE_PER_SPAD_LO = 0x0F8B,
+ DSS_RESULT__ENABLED_BLOCKS = 0x0F8C,
+ DSS_RESULT__NUM_REQUESTED_SPADS = 0x0F8E,
+ DSS_RESULT__NUM_REQUESTED_SPADS_HI = 0x0F8E,
+ DSS_RESULT__NUM_REQUESTED_SPADS_LO = 0x0F8F,
+ MM_RESULT__INNER_INTERSECTION_RATE = 0x0F92,
+ MM_RESULT__INNER_INTERSECTION_RATE_HI = 0x0F92,
+ MM_RESULT__INNER_INTERSECTION_RATE_LO = 0x0F93,
+ MM_RESULT__OUTER_COMPLEMENT_RATE = 0x0F94,
+ MM_RESULT__OUTER_COMPLEMENT_RATE_HI = 0x0F94,
+ MM_RESULT__OUTER_COMPLEMENT_RATE_LO = 0x0F95,
+ MM_RESULT__TOTAL_OFFSET = 0x0F96,
+ MM_RESULT__TOTAL_OFFSET_HI = 0x0F96,
+ MM_RESULT__TOTAL_OFFSET_LO = 0x0F97,
+ XTALK_CALC__XTALK_FOR_ENABLED_SPADS = 0x0F98,
+ XTALK_CALC__XTALK_FOR_ENABLED_SPADS_3 = 0x0F98,
+ XTALK_CALC__XTALK_FOR_ENABLED_SPADS_2 = 0x0F99,
+ XTALK_CALC__XTALK_FOR_ENABLED_SPADS_1 = 0x0F9A,
+ XTALK_CALC__XTALK_FOR_ENABLED_SPADS_0 = 0x0F9B,
+ XTALK_RESULT__AVG_XTALK_USER_ROI_KCPS = 0x0F9C,
+ XTALK_RESULT__AVG_XTALK_USER_ROI_KCPS_3 = 0x0F9C,
+ XTALK_RESULT__AVG_XTALK_USER_ROI_KCPS_2 = 0x0F9D,
+ XTALK_RESULT__AVG_XTALK_USER_ROI_KCPS_1 = 0x0F9E,
+ XTALK_RESULT__AVG_XTALK_USER_ROI_KCPS_0 = 0x0F9F,
+ XTALK_RESULT__AVG_XTALK_MM_INNER_ROI_KCPS = 0x0FA0,
+ XTALK_RESULT__AVG_XTALK_MM_INNER_ROI_KCPS_3 = 0x0FA0,
+ XTALK_RESULT__AVG_XTALK_MM_INNER_ROI_KCPS_2 = 0x0FA1,
+ XTALK_RESULT__AVG_XTALK_MM_INNER_ROI_KCPS_1 = 0x0FA2,
+ XTALK_RESULT__AVG_XTALK_MM_INNER_ROI_KCPS_0 = 0x0FA3,
+ XTALK_RESULT__AVG_XTALK_MM_OUTER_ROI_KCPS = 0x0FA4,
+ XTALK_RESULT__AVG_XTALK_MM_OUTER_ROI_KCPS_3 = 0x0FA4,
+ XTALK_RESULT__AVG_XTALK_MM_OUTER_ROI_KCPS_2 = 0x0FA5,
+ XTALK_RESULT__AVG_XTALK_MM_OUTER_ROI_KCPS_1 = 0x0FA6,
+ XTALK_RESULT__AVG_XTALK_MM_OUTER_ROI_KCPS_0 = 0x0FA7,
+ RANGE_RESULT__ACCUM_PHASE = 0x0FA8,
+ RANGE_RESULT__ACCUM_PHASE_3 = 0x0FA8,
+ RANGE_RESULT__ACCUM_PHASE_2 = 0x0FA9,
+ RANGE_RESULT__ACCUM_PHASE_1 = 0x0FAA,
+ RANGE_RESULT__ACCUM_PHASE_0 = 0x0FAB,
+ RANGE_RESULT__OFFSET_CORRECTED_RANGE = 0x0FAC,
+ RANGE_RESULT__OFFSET_CORRECTED_RANGE_HI = 0x0FAC,
+ RANGE_RESULT__OFFSET_CORRECTED_RANGE_LO = 0x0FAD,
+ SHADOW_PHASECAL_RESULT__VCSEL_START = 0x0FAE,
+ SHADOW_RESULT__INTERRUPT_STATUS = 0x0FB0,
+ SHADOW_RESULT__RANGE_STATUS = 0x0FB1,
+ SHADOW_RESULT__REPORT_STATUS = 0x0FB2,
+ SHADOW_RESULT__STREAM_COUNT = 0x0FB3,
+ SHADOW_RESULT__DSS_ACTUAL_EFFECTIVE_SPADS_SD0 = 0x0FB4,
+ SHADOW_RESULT__DSS_ACTUAL_EFFECTIVE_SPADS_SD0_HI = 0x0FB4,
+ SHADOW_RESULT__DSS_ACTUAL_EFFECTIVE_SPADS_SD0_LO = 0x0FB5,
+ SHADOW_RESULT__PEAK_SIGNAL_COUNT_RATE_MCPS_SD0 = 0x0FB6,
+ SHADOW_RESULT__PEAK_SIGNAL_COUNT_RATE_MCPS_SD0_HI = 0x0FB6,
+ SHADOW_RESULT__PEAK_SIGNAL_COUNT_RATE_MCPS_SD0_LO = 0x0FB7,
+ SHADOW_RESULT__AMBIENT_COUNT_RATE_MCPS_SD0 = 0x0FB8,
+ SHADOW_RESULT__AMBIENT_COUNT_RATE_MCPS_SD0_HI = 0x0FB8,
+ SHADOW_RESULT__AMBIENT_COUNT_RATE_MCPS_SD0_LO = 0x0FB9,
+ SHADOW_RESULT__SIGMA_SD0 = 0x0FBA,
+ SHADOW_RESULT__SIGMA_SD0_HI = 0x0FBA,
+ SHADOW_RESULT__SIGMA_SD0_LO = 0x0FBB,
+ SHADOW_RESULT__PHASE_SD0 = 0x0FBC,
+ SHADOW_RESULT__PHASE_SD0_HI = 0x0FBC,
+ SHADOW_RESULT__PHASE_SD0_LO = 0x0FBD,
+ SHADOW_RESULT__FINAL_CROSSTALK_CORRECTED_RANGE_MM_SD0 = 0x0FBE,
+ SHADOW_RESULT__FINAL_CROSSTALK_CORRECTED_RANGE_MM_SD0_HI = 0x0FBE,
+ SHADOW_RESULT__FINAL_CROSSTALK_CORRECTED_RANGE_MM_SD0_LO = 0x0FBF,
+ SHADOW_RESULT__PEAK_SIGNAL_COUNT_RATE_CROSSTALK_CORRECTED_MCPS_SD0 = 0x0FC0,
+ SHADOW_RESULT__PEAK_SIGNAL_COUNT_RATE_CROSSTALK_CORRECTED_MCPS_SD0_HI = 0x0FC0,
+ SHADOW_RESULT__PEAK_SIGNAL_COUNT_RATE_CROSSTALK_CORRECTED_MCPS_SD0_LO = 0x0FC1,
+ SHADOW_RESULT__MM_INNER_ACTUAL_EFFECTIVE_SPADS_SD0 = 0x0FC2,
+ SHADOW_RESULT__MM_INNER_ACTUAL_EFFECTIVE_SPADS_SD0_HI = 0x0FC2,
+ SHADOW_RESULT__MM_INNER_ACTUAL_EFFECTIVE_SPADS_SD0_LO = 0x0FC3,
+ SHADOW_RESULT__MM_OUTER_ACTUAL_EFFECTIVE_SPADS_SD0 = 0x0FC4,
+ SHADOW_RESULT__MM_OUTER_ACTUAL_EFFECTIVE_SPADS_SD0_HI = 0x0FC4,
+ SHADOW_RESULT__MM_OUTER_ACTUAL_EFFECTIVE_SPADS_SD0_LO = 0x0FC5,
+ SHADOW_RESULT__AVG_SIGNAL_COUNT_RATE_MCPS_SD0 = 0x0FC6,
+ SHADOW_RESULT__AVG_SIGNAL_COUNT_RATE_MCPS_SD0_HI = 0x0FC6,
+ SHADOW_RESULT__AVG_SIGNAL_COUNT_RATE_MCPS_SD0_LO = 0x0FC7,
+ SHADOW_RESULT__DSS_ACTUAL_EFFECTIVE_SPADS_SD1 = 0x0FC8,
+ SHADOW_RESULT__DSS_ACTUAL_EFFECTIVE_SPADS_SD1_HI = 0x0FC8,
+ SHADOW_RESULT__DSS_ACTUAL_EFFECTIVE_SPADS_SD1_LO = 0x0FC9,
+ SHADOW_RESULT__PEAK_SIGNAL_COUNT_RATE_MCPS_SD1 = 0x0FCA,
+ SHADOW_RESULT__PEAK_SIGNAL_COUNT_RATE_MCPS_SD1_HI = 0x0FCA,
+ SHADOW_RESULT__PEAK_SIGNAL_COUNT_RATE_MCPS_SD1_LO = 0x0FCB,
+ SHADOW_RESULT__AMBIENT_COUNT_RATE_MCPS_SD1 = 0x0FCC,
+ SHADOW_RESULT__AMBIENT_COUNT_RATE_MCPS_SD1_HI = 0x0FCC,
+ SHADOW_RESULT__AMBIENT_COUNT_RATE_MCPS_SD1_LO = 0x0FCD,
+ SHADOW_RESULT__SIGMA_SD1 = 0x0FCE,
+ SHADOW_RESULT__SIGMA_SD1_HI = 0x0FCE,
+ SHADOW_RESULT__SIGMA_SD1_LO = 0x0FCF,
+ SHADOW_RESULT__PHASE_SD1 = 0x0FD0,
+ SHADOW_RESULT__PHASE_SD1_HI = 0x0FD0,
+ SHADOW_RESULT__PHASE_SD1_LO = 0x0FD1,
+ SHADOW_RESULT__FINAL_CROSSTALK_CORRECTED_RANGE_MM_SD1 = 0x0FD2,
+ SHADOW_RESULT__FINAL_CROSSTALK_CORRECTED_RANGE_MM_SD1_HI = 0x0FD2,
+ SHADOW_RESULT__FINAL_CROSSTALK_CORRECTED_RANGE_MM_SD1_LO = 0x0FD3,
+ SHADOW_RESULT__SPARE_0_SD1 = 0x0FD4,
+ SHADOW_RESULT__SPARE_0_SD1_HI = 0x0FD4,
+ SHADOW_RESULT__SPARE_0_SD1_LO = 0x0FD5,
+ SHADOW_RESULT__SPARE_1_SD1 = 0x0FD6,
+ SHADOW_RESULT__SPARE_1_SD1_HI = 0x0FD6,
+ SHADOW_RESULT__SPARE_1_SD1_LO = 0x0FD7,
+ SHADOW_RESULT__SPARE_2_SD1 = 0x0FD8,
+ SHADOW_RESULT__SPARE_2_SD1_HI = 0x0FD8,
+ SHADOW_RESULT__SPARE_2_SD1_LO = 0x0FD9,
+ SHADOW_RESULT__SPARE_3_SD1 = 0x0FDA,
+ SHADOW_RESULT__THRESH_INFO = 0x0FDB,
+ SHADOW_RESULT_CORE__AMBIENT_WINDOW_EVENTS_SD0 = 0x0FDC,
+ SHADOW_RESULT_CORE__AMBIENT_WINDOW_EVENTS_SD0_3 = 0x0FDC,
+ SHADOW_RESULT_CORE__AMBIENT_WINDOW_EVENTS_SD0_2 = 0x0FDD,
+ SHADOW_RESULT_CORE__AMBIENT_WINDOW_EVENTS_SD0_1 = 0x0FDE,
+ SHADOW_RESULT_CORE__AMBIENT_WINDOW_EVENTS_SD0_0 = 0x0FDF,
+ SHADOW_RESULT_CORE__RANGING_TOTAL_EVENTS_SD0 = 0x0FE0,
+ SHADOW_RESULT_CORE__RANGING_TOTAL_EVENTS_SD0_3 = 0x0FE0,
+ SHADOW_RESULT_CORE__RANGING_TOTAL_EVENTS_SD0_2 = 0x0FE1,
+ SHADOW_RESULT_CORE__RANGING_TOTAL_EVENTS_SD0_1 = 0x0FE2,
+ SHADOW_RESULT_CORE__RANGING_TOTAL_EVENTS_SD0_0 = 0x0FE3,
+ SHADOW_RESULT_CORE__SIGNAL_TOTAL_EVENTS_SD0 = 0x0FE4,
+ SHADOW_RESULT_CORE__SIGNAL_TOTAL_EVENTS_SD0_3 = 0x0FE4,
+ SHADOW_RESULT_CORE__SIGNAL_TOTAL_EVENTS_SD0_2 = 0x0FE5,
+ SHADOW_RESULT_CORE__SIGNAL_TOTAL_EVENTS_SD0_1 = 0x0FE6,
+ SHADOW_RESULT_CORE__SIGNAL_TOTAL_EVENTS_SD0_0 = 0x0FE7,
+ SHADOW_RESULT_CORE__TOTAL_PERIODS_ELAPSED_SD0 = 0x0FE8,
+ SHADOW_RESULT_CORE__TOTAL_PERIODS_ELAPSED_SD0_3 = 0x0FE8,
+ SHADOW_RESULT_CORE__TOTAL_PERIODS_ELAPSED_SD0_2 = 0x0FE9,
+ SHADOW_RESULT_CORE__TOTAL_PERIODS_ELAPSED_SD0_1 = 0x0FEA,
+ SHADOW_RESULT_CORE__TOTAL_PERIODS_ELAPSED_SD0_0 = 0x0FEB,
+ SHADOW_RESULT_CORE__AMBIENT_WINDOW_EVENTS_SD1 = 0x0FEC,
+ SHADOW_RESULT_CORE__AMBIENT_WINDOW_EVENTS_SD1_3 = 0x0FEC,
+ SHADOW_RESULT_CORE__AMBIENT_WINDOW_EVENTS_SD1_2 = 0x0FED,
+ SHADOW_RESULT_CORE__AMBIENT_WINDOW_EVENTS_SD1_1 = 0x0FEE,
+ SHADOW_RESULT_CORE__AMBIENT_WINDOW_EVENTS_SD1_0 = 0x0FEF,
+ SHADOW_RESULT_CORE__RANGING_TOTAL_EVENTS_SD1 = 0x0FF0,
+ SHADOW_RESULT_CORE__RANGING_TOTAL_EVENTS_SD1_3 = 0x0FF0,
+ SHADOW_RESULT_CORE__RANGING_TOTAL_EVENTS_SD1_2 = 0x0FF1,
+ SHADOW_RESULT_CORE__RANGING_TOTAL_EVENTS_SD1_1 = 0x0FF2,
+ SHADOW_RESULT_CORE__RANGING_TOTAL_EVENTS_SD1_0 = 0x0FF3,
+ SHADOW_RESULT_CORE__SIGNAL_TOTAL_EVENTS_SD1 = 0x0FF4,
+ SHADOW_RESULT_CORE__SIGNAL_TOTAL_EVENTS_SD1_3 = 0x0FF4,
+ SHADOW_RESULT_CORE__SIGNAL_TOTAL_EVENTS_SD1_2 = 0x0FF5,
+ SHADOW_RESULT_CORE__SIGNAL_TOTAL_EVENTS_SD1_1 = 0x0FF6,
+ SHADOW_RESULT_CORE__SIGNAL_TOTAL_EVENTS_SD1_0 = 0x0FF7,
+ SHADOW_RESULT_CORE__TOTAL_PERIODS_ELAPSED_SD1 = 0x0FF8,
+ SHADOW_RESULT_CORE__TOTAL_PERIODS_ELAPSED_SD1_3 = 0x0FF8,
+ SHADOW_RESULT_CORE__TOTAL_PERIODS_ELAPSED_SD1_2 = 0x0FF9,
+ SHADOW_RESULT_CORE__TOTAL_PERIODS_ELAPSED_SD1_1 = 0x0FFA,
+ SHADOW_RESULT_CORE__TOTAL_PERIODS_ELAPSED_SD1_0 = 0x0FFB,
+ SHADOW_RESULT_CORE__SPARE_0 = 0x0FFC,
+ SHADOW_PHASECAL_RESULT__REFERENCE_PHASE_HI = 0x0FFE,
+ SHADOW_PHASECAL_RESULT__REFERENCE_PHASE_LO = 0x0FFF,
+ };
+
+ enum DistanceMode { Short, Medium, Long, Unknown };
+
+ enum RangeStatus : uint8_t
+ {
+ RangeValid = 0,
+
+ // "sigma estimator check is above the internal defined threshold"
+ // (sigma = standard deviation of measurement)
+ SigmaFail = 1,
+
+ // "signal value is below the internal defined threshold"
+ SignalFail = 2,
+
+ // "Target is below minimum detection threshold."
+ RangeValidMinRangeClipped = 3,
+
+ // "phase is out of bounds"
+ // (nothing detected in range; try a longer distance mode if applicable)
+ OutOfBoundsFail = 4,
+
+ // "HW or VCSEL failure"
+ HardwareFail = 5,
+
+ // "The Range is valid but the wraparound check has not been done."
+ RangeValidNoWrapCheckFail = 6,
+
+ // "Wrapped target, not matching phases"
+ // "no matching phase in other VCSEL period timing."
+ WrapTargetFail = 7,
+
+ // "Internal algo underflow or overflow in lite ranging."
+ // ProcessingFail = 8: not used in API
+
+ // "Specific to lite ranging."
+ // should never occur with this lib (which uses low power auto ranging,
+ // as the API does)
+ XtalkSignalFail = 9,
+
+ // "1st interrupt when starting ranging in back to back mode. Ignore
+ // data."
+ // should never occur with this lib
+ SynchronizationInt = 10, // (the API spells this "syncronisation")
+
+ // "All Range ok but object is result of multiple pulses merging together.
+ // Used by RQL for merged pulse detection"
+ // RangeValid MergedPulse = 11: not used in API
+
+ // "Used by RQL as different to phase fail."
+ // TargetPresentLackOfSignal = 12:
+
+ // "Target is below minimum detection threshold."
+ MinRangeFail = 13,
+
+ // "The reported range is invalid"
+ // RangeInvalid = 14: can't actually be returned by API (range can never become negative, even after correction)
+
+ // "No Update."
+ None = 255,
+ };
+
+ struct RangingData
+ {
+ uint16_t range_mm;
+ RangeStatus range_status;
+ float peak_signal_count_rate_MCPS;
+ float ambient_count_rate_MCPS;
+ };
+
+ RangingData ranging_data;
+
+ uint8_t last_status; // status of last I2C transmission
+
+ VL53L1X();
+
+ void setAddress(uint8_t new_addr);
+ uint8_t getAddress() { return address; }
+
+ bool init(bool io_2v8 = true);
+
+ void writeReg(uint16_t reg, uint8_t value);
+ void writeReg16Bit(uint16_t reg, uint16_t value);
+ void writeReg32Bit(uint16_t reg, uint32_t value);
+ uint8_t readReg(regAddr reg);
+ uint16_t readReg16Bit(uint16_t reg);
+ uint32_t readReg32Bit(uint16_t reg);
+
+ bool setDistanceMode(DistanceMode mode);
+ DistanceMode getDistanceMode() { return distance_mode; }
+
+ bool setMeasurementTimingBudget(uint32_t budget_us);
+ uint32_t getMeasurementTimingBudget();
+
+ void startContinuous(uint32_t period_ms);
+ void stopContinuous();
+ uint16_t read(bool blocking = true);
+ uint16_t readRangeContinuousMillimeters(bool blocking = true) { return read(blocking); } // alias of read()
+
+ // check if sensor has new reading available
+ // assumes interrupt is active low (GPIO_HV_MUX__CTRL bit 4 is 1)
+ bool dataReady() { return (readReg(GPIO__TIO_HV_STATUS) & 0x01) == 0; }
+
+ static const char * rangeStatusToString(RangeStatus status);
+
+ void setTimeout(uint16_t timeout) { io_timeout = timeout; }
+ uint16_t getTimeout() { return io_timeout; }
+ bool timeoutOccurred();
+
+ private:
+
+ // The Arduino two-wire interface uses a 7-bit number for the address,
+ // and sets the last bit correctly based on reads and writes
+ static const uint8_t AddressDefault = 0b0101001;
+
+ // value used in measurement timing budget calculations
+ // assumes PresetMode is LOWPOWER_AUTONOMOUS
+ //
+ // vhv = LOWPOWER_AUTO_VHV_LOOP_DURATION_US + LOWPOWERAUTO_VHV_LOOP_BOUND
+ // (tuning parm default) * LOWPOWER_AUTO_VHV_LOOP_DURATION_US
+ // = 245 + 3 * 245 = 980
+ // TimingGuard = LOWPOWER_AUTO_OVERHEAD_BEFORE_A_RANGING +
+ // LOWPOWER_AUTO_OVERHEAD_BETWEEN_A_B_RANGING + vhv
+ // = 1448 + 2100 + 980 = 4528
+ static const uint32_t TimingGuard = 4528;
+
+ // value in DSS_CONFIG__TARGET_TOTAL_RATE_MCPS register, used in DSS
+ // calculations
+ static const uint16_t TargetRate = 0x0A00;
+
+ // for storing values read from RESULT__RANGE_STATUS (0x0089)
+ // through RESULT__PEAK_SIGNAL_COUNT_RATE_CROSSTALK_CORRECTED_MCPS_SD0_LOW
+ // (0x0099)
+ struct ResultBuffer
+ {
+ uint8_t range_status;
+ // uint8_t report_status: not used
+ uint8_t stream_count;
+ uint16_t dss_actual_effective_spads_sd0;
+ // uint16_t peak_signal_count_rate_mcps_sd0: not used
+ uint16_t ambient_count_rate_mcps_sd0;
+ // uint16_t sigma_sd0: not used
+ // uint16_t phase_sd0: not used
+ uint16_t final_crosstalk_corrected_range_mm_sd0;
+ uint16_t peak_signal_count_rate_crosstalk_corrected_mcps_sd0;
+ };
+
+ // making this static would save RAM for multiple instances as long as there
+ // aren't multiple sensors being read at the same time (e.g. on separate
+ // I2C buses)
+ ResultBuffer results;
+
+ uint8_t address;
+
+ uint16_t io_timeout;
+ bool did_timeout;
+ uint16_t timeout_start_ms;
+
+ uint16_t fast_osc_frequency;
+ uint16_t osc_calibrate_val;
+
+ bool calibrated;
+ uint8_t saved_vhv_init;
+ uint8_t saved_vhv_timeout;
+
+ DistanceMode distance_mode;
+
+ // Record the current time to check an upcoming timeout against
+ void startTimeout() { timeout_start_ms = millis(); }
+
+ // Check if timeout is enabled (set to nonzero value) and has expired
+ bool checkTimeoutExpired() {return (io_timeout > 0) && ((uint16_t)(millis() - timeout_start_ms) > io_timeout); }
+
+ void setupManualCalibration();
+ void readResults();
+ void updateDSS();
+ void getRangingData();
+
+ static uint32_t decodeTimeout(uint16_t reg_val);
+ static uint16_t encodeTimeout(uint32_t timeout_mclks);
+ static uint32_t timeoutMclksToMicroseconds(uint32_t timeout_mclks, uint32_t macro_period_us);
+ static uint32_t timeoutMicrosecondsToMclks(uint32_t timeout_us, uint32_t macro_period_us);
+ uint32_t calcMacroPeriod(uint8_t vcsel_period);
+
+ // Convert count rate from fixed point 9.7 format to float
+ float countRateFixedToFloat(uint16_t count_rate_fixed) { return (float)count_rate_fixed / (1 << 7); }
+};
\ No newline at end of file
diff --git a/lib/vl53l1x-arduino-1.01/examples/Continuous/Continuous.ino b/lib/vl53l1x-arduino-1.01/examples/Continuous/Continuous.ino
new file mode 100644
index 000000000..e910466fa
--- /dev/null
+++ b/lib/vl53l1x-arduino-1.01/examples/Continuous/Continuous.ino
@@ -0,0 +1,44 @@
+/*
+This example shows how to take simple range measurements with the VL53L1X. The
+range readings are in units of mm.
+*/
+
+#include
+#include
+
+VL53L1X sensor;
+
+void setup()
+{
+ Serial.begin(115200);
+ Wire.begin();
+ Wire.setClock(400000); // use 400 kHz I2C
+
+ sensor.setTimeout(500);
+ if (!sensor.init())
+ {
+ Serial.println("Failed to detect and initialize sensor!");
+ while (1);
+ }
+
+ // Use long distance mode and allow up to 50000 us (50 ms) for a measurement.
+ // You can change these settings to adjust the performance of the sensor, but
+ // the minimum timing budget is 20 ms for short distance mode and 33 ms for
+ // medium and long distance modes. See the VL53L1X datasheet for more
+ // information on range and timing limits.
+ sensor.setDistanceMode(VL53L1X::Long);
+ sensor.setMeasurementTimingBudget(50000);
+
+ // Start continuous readings at a rate of one measurement every 50 ms (the
+ // inter-measurement period). This period should be at least as long as the
+ // timing budget.
+ sensor.startContinuous(50);
+}
+
+void loop()
+{
+ Serial.print(sensor.read());
+ if (sensor.timeoutOccurred()) { Serial.print(" TIMEOUT"); }
+
+ Serial.println();
+}
diff --git a/lib/vl53l1x-arduino-1.01/examples/ContinuousWithDetails/ContinuousWithDetails.ino b/lib/vl53l1x-arduino-1.01/examples/ContinuousWithDetails/ContinuousWithDetails.ino
new file mode 100644
index 000000000..938b79bfc
--- /dev/null
+++ b/lib/vl53l1x-arduino-1.01/examples/ContinuousWithDetails/ContinuousWithDetails.ino
@@ -0,0 +1,55 @@
+/*
+This example takes range measurements with the VL53L1X and displays additional
+details (status and signal/ambient rates) for each measurement, which can help
+you determine whether the sensor is operating normally and the reported range is
+valid. The range is in units of mm, and the rates are in units of MCPS (mega
+counts per second).
+*/
+
+#include
+#include
+
+VL53L1X sensor;
+
+void setup()
+{
+ Serial.begin(115200);
+ Wire.begin();
+ Wire.setClock(400000); // use 400 kHz I2C
+
+ sensor.setTimeout(500);
+ if (!sensor.init())
+ {
+ Serial.println("Failed to detect and initialize sensor!");
+ while (1);
+ }
+
+ // Use long distance mode and allow up to 50000 us (50 ms) for a measurement.
+ // You can change these settings to adjust the performance of the sensor, but
+ // the minimum timing budget is 20 ms for short distance mode and 33 ms for
+ // medium and long distance modes. See the VL53L1X datasheet for more
+ // information on range and timing limits.
+ sensor.setDistanceMode(VL53L1X::Long);
+ sensor.setMeasurementTimingBudget(50000);
+
+ // Start continuous readings at a rate of one measurement every 50 ms (the
+ // inter-measurement period). This period should be at least as long as the
+ // timing budget.
+ sensor.startContinuous(50);
+}
+
+void loop()
+{
+ sensor.read();
+
+ Serial.print("range: ");
+ Serial.print(sensor.ranging_data.range_mm);
+ Serial.print("\tstatus: ");
+ Serial.print(VL53L1X::rangeStatusToString(sensor.ranging_data.range_status));
+ Serial.print("\tpeak signal: ");
+ Serial.print(sensor.ranging_data.peak_signal_count_rate_MCPS);
+ Serial.print("\tambient: ");
+ Serial.print(sensor.ranging_data.ambient_count_rate_MCPS);
+
+ Serial.println();
+}
\ No newline at end of file
diff --git a/lib/vl53l1x-arduino-1.01/keywords.txt b/lib/vl53l1x-arduino-1.01/keywords.txt
new file mode 100644
index 000000000..32e3d3fa3
--- /dev/null
+++ b/lib/vl53l1x-arduino-1.01/keywords.txt
@@ -0,0 +1,41 @@
+VL53L1X KEYWORD1
+
+setAddress KEYWORD2
+getAddress KEYWORD2
+init KEYWORD2
+writeReg KEYWORD2
+writeReg16Bit KEYWORD2
+writeReg32Bit KEYWORD2
+readReg KEYWORD2
+readReg16Bit KEYWORD2
+readReg32Bit KEYWORD2
+setDistanceMode KEYWORD2
+getDistanceMode KEYWORD2
+setMeasurementTimingBudget KEYWORD2
+getMeasurementTimingBudget KEYWORD2
+startContinuous KEYWORD2
+stopContinuous KEYWORD2
+read KEYWORD2
+readRangeContinuousMillimeters KEYWORD2
+rangeStatusToString KEYWORD2
+setTimeout KEYWORD2
+getTimeout KEYWORD2
+timeoutOccurred KEYWORD2
+
+Short LITERAL1
+Medium LITERAL1
+Long LITERAL1
+Unknown LITERAL1
+
+RangeValid LITERAL1
+SigmaFail LITERAL1
+SignalFail LITERAL1
+RangeValidMinRangeClipped LITERAL1
+OutOfBoundsFail LITERAL1
+HardwareFail LITERAL1
+RangeValidNoWrapCheckFail LITERAL1
+WrapTargetFail LITERAL1
+XtalkSignalFail LITERAL1
+SyncronisationInt LITERAL1
+MinRangeFail LITERAL1
+None LITERAL1
\ No newline at end of file
diff --git a/lib/vl53l1x-arduino-1.01/library.properties b/lib/vl53l1x-arduino-1.01/library.properties
new file mode 100644
index 000000000..1bf06dae9
--- /dev/null
+++ b/lib/vl53l1x-arduino-1.01/library.properties
@@ -0,0 +1,9 @@
+name=VL53L1X
+version=1.0.1
+author=Pololu
+maintainer=Pololu
+sentence=VL53L1X distance sensor library
+paragraph=This is a library for the Arduino IDE that helps interface with ST's VL53L1X distance sensor.
+category=Sensors
+url=https://github.com/pololu/vl53l1x-arduino
+architectures=*
diff --git a/tasmota/my_user_config.h b/tasmota/my_user_config.h
index 5f94c5fc0..d6df82729 100644
--- a/tasmota/my_user_config.h
+++ b/tasmota/my_user_config.h
@@ -538,6 +538,7 @@
// #define USE_SPS30 // [I2cDriver30] Enable Sensiron SPS30 particle sensor (I2C address 0x69) (+1.7 code)
#define USE_ADE7953 // [I2cDriver7] Enable ADE7953 Energy monitor as used on Shelly 2.5 (I2C address 0x38) (+1k5)
// #define USE_VL53L0X // [I2cDriver31] Enable VL53L0x time of flight sensor (I2C address 0x29) (+4k code)
+// #define USE_VL53L1X // [I2cDriver53] Enable support for VL53L1X sensor (I2C address 0x29) using Pololu VL53L1X library (+2k9 code)
// #define USE_MLX90614 // [I2cDriver32] Enable MLX90614 ir temp sensor (I2C address 0x5a) (+0.6k code)
// #define USE_CHIRP // [I2cDriver33] Enable CHIRP soil moisture sensor (variable I2C address, default 0x20)
// #define USE_PAJ7620 // [I2cDriver34] Enable PAJ7620 gesture sensor (I2C address 0x73) (+2.5k code)
diff --git a/tasmota/support_features.ino b/tasmota/support_features.ino
index 8707650c0..e565eeac9 100644
--- a/tasmota/support_features.ino
+++ b/tasmota/support_features.ino
@@ -598,7 +598,9 @@ void GetFeatures(void)
#ifdef USE_DYP
feature6 |= 0x00400000; // xsns_76_dyp.ino
#endif
-// feature6 |= 0x00800000;
+#if defined(USE_I2C) && defined(USE_VL53L1X)
+ feature6 |= 0x00800000; // xsns_77_vl53l1x.ino
+#endif
// feature6 |= 0x01000000;
// feature6 |= 0x02000000;
diff --git a/tasmota/tasmota_configurations.h b/tasmota/tasmota_configurations.h
index 25da1c180..f94844bf9 100644
--- a/tasmota/tasmota_configurations.h
+++ b/tasmota/tasmota_configurations.h
@@ -113,6 +113,7 @@
//#define USE_SPS30 // Enable Sensiron SPS30 particle sensor (I2C address 0x69) (+1.7 code)
#define USE_ADE7953 // Enable ADE7953 Energy monitor as used on Shelly 2.5 (I2C address 0x38) (+1k5)
//#define USE_VL53L0X // Enable VL53L0x time of flight sensor (I2C address 0x29) (+4k code)
+//#define USE_VL53L1X // Enable support for VL53L1X sensor (I2C address 0x29) using Pololu VL53L1X library (+2k9 code)
//#define USE_MLX90614 // Enable MLX90614 ir temp sensor (I2C address 0x5a) (+0.6k code)
//#define USE_CHIRP // Enable CHIRP soil moisture sensor (variable I2C address, default 0x20)
//#define USE_PAJ7620 // Enable PAJ7620 gesture sensor (I2C address 0x73) (+2.5k code)
diff --git a/tasmota/xsns_77_vl53l1x.ino b/tasmota/xsns_77_vl53l1x.ino
new file mode 100644
index 000000000..2176ee9ed
--- /dev/null
+++ b/tasmota/xsns_77_vl53l1x.ino
@@ -0,0 +1,128 @@
+/*
+ xsns_77_vl53l1x.ino - VL53L1X
+
+ Copyright (C) 2018 Theo Arends, Rui Marinho and Johann Obermeier
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see .
+*/
+
+#ifdef USE_I2C
+#ifdef USE_VL53L1X
+/*********************************************************************************************\
+ * VL53L1X
+ *
+ * Source:
+ *
+ * I2C Address: 0x29
+\*********************************************************************************************/
+
+#define XSNS_77 77
+#define XI2C_53 53 // See I2CDEVICES.md
+
+#include "VL53L1X.h"
+VL53L1X vl53l1x = VL53L1X(); // create object copy
+
+#define VL53L1X_ADDRESS 0x29
+
+struct {
+ bool ready = false;
+ uint16_t distance = 0;
+} vl53l1x_sensors;
+
+/********************************************************************************************/
+
+void Vl53l1Detect(void) {
+ if (!I2cSetDevice(VL53L1X_ADDRESS)) { return; }
+ if (!vl53l1x.init()) { return; }
+
+ I2cSetActiveFound(vl53l1x.getAddress(), "VL53L1X");
+ vl53l1x.setTimeout(500);
+ vl53l1x.setDistanceMode(VL53L1X::Long); // could be Short, Medium, Long
+ vl53l1x.setMeasurementTimingBudget(140000);
+ vl53l1x.startContinuous(50);
+ vl53l1x_sensors.ready = true;
+}
+
+#ifdef USE_WEBSERVER
+const char HTTP_SNS_VL53L1X[] PROGMEM =
+ "{s}VL53L1X " D_DISTANCE "{m}%d" D_UNIT_MILLIMETER "{e}"; // {s} = , {m} = | , {e} = |
+#endif // USE_WEBSERVER
+
+void Vl53l1Every_250MSecond(void) {
+ // every 250 ms
+ uint16_t dist = vl53l1x.read();
+ if (!dist || dist > 4000) {
+ dist = 9999;
+ }
+ vl53l1x_sensors.distance = dist;
+}
+
+#ifdef USE_DOMOTICZ
+void Vl53l1Every_Second(void) {
+ DomoticzSensor(DZ_ILLUMINANCE, vl53l1x_sensors.distance);
+}
+#endif // USE_DOMOTICZ
+
+void Vl53l1Show(boolean json) {
+ if (json) {
+#ifdef USE_DOMOTICZ
+ if (0 == tele_period) {
+ DomoticzSensor(DZ_ILLUMINANCE, vl53l1x_sensors.distance);
+ }
+#endif // USE_DOMOTICZ
+ ResponseAppend_P(PSTR(",\"VL53L1X\":{\"" D_JSON_DISTANCE "\":%d}"), vl53l1x_sensors.distance);
+#ifdef USE_WEBSERVER
+ } else {
+ WSContentSend_PD(HTTP_SNS_VL53L1X, vl53l1x_sensors.distance);
+#endif
+ }
+}
+
+/*********************************************************************************************\
+ * Interface
+\*********************************************************************************************/
+
+bool Xsns77(byte function)
+{
+ if (!I2cEnabled(XI2C_53)) { return false; }
+ bool result = false;
+
+ if (FUNC_INIT == function) {
+ Vl53l1Detect();
+ }
+ else if (vl53l1x_sensors.ready) {
+ switch (function) {
+ case FUNC_EVERY_250_MSECOND:
+ Vl53l1Every_250MSecond();
+ break;
+#ifdef USE_DOMOTICZ
+ case FUNC_EVERY_SECOND:
+ Vl53l1Every_Second();
+ break;
+#endif // USE_DOMOTICZ
+ case FUNC_JSON_APPEND:
+ Vl53l1Show(1);
+ break;
+#ifdef USE_WEBSERVER
+ case FUNC_WEB_SENSOR:
+ Vl53l1Show(0);
+ break;
+#endif // USE_WEBSERVER
+ }
+ }
+ return result;
+}
+
+#endif // USE_VL53L1X
+#endif // USE_I2C
From 5c7e73f29b088e08874c458485136e5685369ee0 Mon Sep 17 00:00:00 2001
From: stefanbode
Date: Sat, 5 Sep 2020 20:39:24 +0200
Subject: [PATCH 002/148] New major Version
---
tasmota/xdrv_27_shutter.ino | 585 ++++++++++++++++++++++--------------
1 file changed, 355 insertions(+), 230 deletions(-)
diff --git a/tasmota/xdrv_27_shutter.ino b/tasmota/xdrv_27_shutter.ino
index e5a5353f6..3a9b43d7e 100644
--- a/tasmota/xdrv_27_shutter.ino
+++ b/tasmota/xdrv_27_shutter.ino
@@ -23,29 +23,37 @@
\*********************************************************************************************/
#define XDRV_27 27
+#ifndef SHUTTER_STEPPER
+ #define SHUTTER_STEPPER
+#endif
#define D_SHUTTER "SHUTTER"
const uint16_t MOTOR_STOP_TIME = 500; // in mS
const uint8_t steps_per_second = 20; // FUNC_EVERY_50_MSECOND
+const uint16_t pwm_max = 500;
+const uint16_t pwm_min = 90;
uint8_t calibrate_pos[6] = {0,30,50,70,90,100};
uint16_t messwerte[5] = {30,50,70,90,100};
uint16_t last_execute_step;
+int32_t stop_position_delta = 20;
-enum ShutterModes { SHT_OFF_OPEN__OFF_CLOSE, SHT_OFF_ON__OPEN_CLOSE, SHT_PULSE_OPEN__PULSE_CLOSE, SHT_OFF_ON__OPEN_CLOSE_STEPPER,};
+const uint8_t MAX_MODES = 7;
+enum ShutterPositionMode {SHT_UNDEF, SHT_TIME, SHT_TIME_UP_DOWN, SHT_TIME_GARAGE, SHT_COUNTER, SHT_PWM_VALUE, SHT_PWM_TIME,};
+enum ShutterSwitchMode {SHT_SWITCH, SHT_PULSE,};
enum ShutterButtonStates { SHT_NOT_PRESSED, SHT_PRESSED_MULTI, SHT_PRESSED_HOLD, SHT_PRESSED_IMMEDIATE, SHT_PRESSED_EXT_HOLD, SHT_PRESSED_MULTI_SIMULTANEOUS, SHT_PRESSED_HOLD_SIMULTANEOUS, SHT_PRESSED_EXT_HOLD_SIMULTANEOUS,};
const char kShutterCommands[] PROGMEM = D_PRFX_SHUTTER "|"
D_CMND_SHUTTER_OPEN "|" D_CMND_SHUTTER_CLOSE "|" D_CMND_SHUTTER_TOGGLE "|" D_CMND_SHUTTER_TOGGLEDIR "|" D_CMND_SHUTTER_STOP "|" D_CMND_SHUTTER_POSITION "|"
- D_CMND_SHUTTER_OPENTIME "|" D_CMND_SHUTTER_CLOSETIME "|" D_CMND_SHUTTER_RELAY "|"
+ D_CMND_SHUTTER_OPENTIME "|" D_CMND_SHUTTER_CLOSETIME "|" D_CMND_SHUTTER_RELAY "|" D_CMND_SHUTTER_MODE "|"
D_CMND_SHUTTER_SETHALFWAY "|" D_CMND_SHUTTER_SETCLOSE "|" D_CMND_SHUTTER_SETOPEN "|" D_CMND_SHUTTER_INVERT "|" D_CMND_SHUTTER_CLIBRATION "|"
D_CMND_SHUTTER_MOTORDELAY "|" D_CMND_SHUTTER_FREQUENCY "|" D_CMND_SHUTTER_BUTTON "|" D_CMND_SHUTTER_LOCK "|" D_CMND_SHUTTER_ENABLEENDSTOPTIME "|" D_CMND_SHUTTER_INVERTWEBBUTTONS "|"
D_CMND_SHUTTER_STOPOPEN "|" D_CMND_SHUTTER_STOPCLOSE "|" D_CMND_SHUTTER_STOPTOGGLE "|" D_CMND_SHUTTER_STOPTOGGLEDIR "|" D_CMND_SHUTTER_STOPPOSITION;
void (* const ShutterCommand[])(void) PROGMEM = {
&CmndShutterOpen, &CmndShutterClose, &CmndShutterToggle, &CmndShutterToggleDir, &CmndShutterStop, &CmndShutterPosition,
- &CmndShutterOpenTime, &CmndShutterCloseTime, &CmndShutterRelay,
+ &CmndShutterOpenTime, &CmndShutterCloseTime, &CmndShutterRelay, &CmndShutterMode,
&CmndShutterSetHalfway, &CmndShutterSetClose, &CmndShutterSetOpen, &CmndShutterInvert, &CmndShutterCalibration , &CmndShutterMotorDelay,
&CmndShutterFrequency, &CmndShutterButton, &CmndShutterLock, &CmndShutterEnableEndStopTime, &CmndShutterInvertWebButtons,
&CmndShutterStopOpen, &CmndShutterStopClose, &CmndShutterStopToggle, &CmndShutterStopToggleDir, &CmndShutterStopPosition};
@@ -71,40 +79,65 @@ struct SHUTTER {
uint16_t close_velocity[MAX_SHUTTERS]; // in relation to open velocity. higher value = faster
int8_t direction[MAX_SHUTTERS]; // 1 == UP , 0 == stop; -1 == down
int8_t lastdirection[MAX_SHUTTERS]; // last direction (1 == UP , -1 == down)
- uint8_t mode = 0; // operation mode definition. see enum type above SHT_OFF_OPEN__OFF_CLOSE, SHT_OFF_ON__OPEN_CLOSE, SHT_PULSE_OPEN__PULSE_CLOSE
+ uint8_t PositionMode = 0; // how to calculate actual position: SHT_TIME, SHT_COUNTER, SHT_PWM_VALUE, SHT_PWM_TIME
+ uint8_t SwitchMode = 0; // how to switch relays: SHT_SWITCH, SHT_PULSE
int16_t motordelay[MAX_SHUTTERS]; // initial motorstarttime in 0.05sec.
- int16_t pwm_frequency[MAX_SHUTTERS]; // frequency of PWN for stepper motors
- uint16_t max_pwm_frequency = 1000; // maximum of PWM frequency for openig the shutter. depend on the motor and drivers
- uint16_t max_close_pwm_frequency[MAX_SHUTTERS];// maximum of PWM frequency for closeing the shutter. depend on the motor and drivers
+ int16_t pwm_velocity[MAX_SHUTTERS]; // frequency of PWN for stepper motors or PWM duty cycle change for PWM servo
+ uint16_t pwm_value[MAX_SHUTTERS]; // dutyload of PWM 0..1023 on ESP8266
+ uint16_t pwm_min[MAX_SHUTTERS]; // dutyload of PWM 0..1023 on ESP8266
+ uint16_t pwm_max[MAX_SHUTTERS]; // dutyload of PWM 0..1023 on ESP8266
+ uint16_t max_pwm_velocity = 1000; // maximum of PWM frequency for openig the shutter. depend on the motor and drivers
+ uint16_t max_close_pwm_velocity[MAX_SHUTTERS];// maximum of PWM frequency for closeing the shutter. depend on the motor and drivers
uint8_t skip_relay_change; // avoid overrun at endstops
int32_t accelerator[MAX_SHUTTERS]; // speed of ramp-up, ramp down of shutter
uint8_t start_reported = 0;
} Shutter;
+#define SHT_DIV_ROUND(__A, __B) (((__A) + (__B)/2) / (__B))
+
void ShutterLogPos(uint32_t i)
{
char stemp2[10];
dtostrfd((float)Shutter.time[i] / steps_per_second, 2, stemp2);
- AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT: Shutter%d Real %d, Start %d, Stop %d, Dir %d, Delay %d, Rtc %s [s], Freq %d"),
- i+1, Shutter.real_position[i], Shutter.start_position[i], Shutter.target_position[i], Shutter.direction[i], Shutter.motordelay[i], stemp2, Shutter.pwm_frequency[i]);
+ AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT: Shutter%d Real %d, Start %d, Stop %d, Dir %d, Delay %d, Rtc %s [s], Freq %d, PWM %d"),
+ i+1, Shutter.real_position[i], Shutter.start_position[i], Shutter.target_position[i], Shutter.direction[i], Shutter.motordelay[i], stemp2, Shutter.pwm_velocity[i], Shutter.pwm_value[i]);
+}
+
+void ExecuteCommandPowerShutter(uint32_t device, uint32_t state, uint32_t source)
+{
+ if (device <= devices_present) ExecuteCommandPower(device,state,source);
+}
+
+void ShutterUpdateVelocity(uint8_t i)
+{
+ Shutter.pwm_velocity[i] += Shutter.accelerator[i];
+ Shutter.pwm_velocity[i] = tmax(1,tmin(Shutter.direction[i]==1 ? Shutter.max_pwm_velocity : Shutter.max_close_pwm_velocity[i],Shutter.pwm_velocity[i]));
}
void ShutterRtc50mS(void)
{
for (uint8_t i = 0; i < shutters_present; i++) {
Shutter.time[i]++;
- if (Shutter.accelerator[i]) {
- //AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: accelerator i=%d -> %d"),i, Shutter.accelerator[i]);
- Shutter.pwm_frequency[i] += Shutter.accelerator[i];
- Shutter.pwm_frequency[i] = tmax(0,tmin(Shutter.direction[i]==1 ? Shutter.max_pwm_frequency : Shutter.max_close_pwm_frequency[i],Shutter.pwm_frequency[i]));
- analogWriteFreq(Shutter.pwm_frequency[i]);
- analogWrite(Pin(GPIO_PWM1, i), 50);
+ switch (Shutter.PositionMode) {
+ case SHT_PWM_VALUE:
+ if (Shutter.accelerator[i]) ShutterUpdateVelocity(i);
+ Shutter.real_position[i] += Shutter.direction[i] > 0 ? Shutter.pwm_velocity[i] : -Shutter.pwm_velocity[i];
+ Shutter.pwm_value[i] = SHT_DIV_ROUND((Shutter.pwm_max[i]-Shutter.pwm_min[i]) * Shutter.real_position[i] , Shutter.open_max[i])+Shutter.pwm_min[i];
+ analogWrite(Pin(GPIO_PWM1, i), Shutter.pwm_value[i]);
+ break;
+
+ case SHT_COUNTER:
+ if (Shutter.accelerator[i]) {
+ //AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: accelerator i=%d -> %d"),i, Shutter.accelerator[i]);
+ ShutterUpdateVelocity(i);
+ analogWriteFreq(Shutter.pwm_velocity[i]);
+ analogWrite(Pin(GPIO_PWM1, i), 50);
+ }
+ break;
}
}
}
-#define SHT_DIV_ROUND(__A, __B) (((__A) + (__B)/2) / (__B))
-
int32_t ShutterPercentToRealPosition(uint32_t percent, uint32_t index)
{
if (Settings.shutter_set50percent[index] != 50) {
@@ -176,7 +209,7 @@ void ShutterInit(void)
// if shutter 4 is unused
if (Settings.shutter_startrelay[MAX_SHUTTERS -1] == 0) {
- Shutter.max_pwm_frequency = Settings.shuttercoeff[4][3] > 0 ? Settings.shuttercoeff[4][3] : Shutter.max_pwm_frequency;
+ Shutter.max_pwm_velocity = Settings.shuttercoeff[4][3] > 0 ? Settings.shuttercoeff[4][3] : Shutter.max_pwm_velocity;
}
for (uint32_t i = 0; i < MAX_SHUTTERS; i++) {
// set startrelay to 1 on first init, but only to shutter 1. 90% usecase
@@ -194,22 +227,35 @@ void ShutterInit(void)
relay_in_interlock = true;
}
}
- if (relay_in_interlock) {
- if (Settings.pulse_timer[i] > 0) {
- Shutter.mode = SHT_PULSE_OPEN__PULSE_CLOSE;
- } else {
- Shutter.mode = SHT_OFF_OPEN__OFF_CLOSE;
+ switch (Settings.pulse_timer[i]) {
+ case 0:
+ Shutter.SwitchMode = SHT_SWITCH;
+ break;
+ default:
+ Shutter.SwitchMode = SHT_PULSE;
+ break;
+ }
+
+ if (Settings.shutter_mode == SHT_UNDEF) {
+ AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: mode undef.. calculate..."));
+ switch (Settings.pulse_timer[i+1]) {
+ case 0:
+ Shutter.PositionMode = SHT_TIME_GARAGE;
+ break;
+ default:
+ if (relay_in_interlock) {
+ Shutter.PositionMode = SHT_TIME;
+ } else {
+ Shutter.PositionMode = SHT_TIME_UP_DOWN;
+ if (PinUsed(GPIO_PWM1, i) && PinUsed(GPIO_CNTR1, i)) {
+ Shutter.PositionMode = SHT_COUNTER;
+ }
+ }
+
+ break;
}
} else {
- Shutter.mode = SHT_OFF_ON__OPEN_CLOSE;
- if (PinUsed(GPIO_PWM1, i) && PinUsed(GPIO_CNTR1, i)) {
- Shutter.mode = SHT_OFF_ON__OPEN_CLOSE_STEPPER;
- Shutter.pwm_frequency[i] = 0;
- Shutter.accelerator[i] = 0;
- analogWriteFreq(Shutter.pwm_frequency[i]);
- analogWrite(Pin(GPIO_PWM1, i), 0);
-// ExecuteCommandPower(Settings.shutter_startrelay[i]+2, 0, SRC_SHUTTER);
- }
+ Shutter.PositionMode = Settings.shutter_mode;
}
TickerShutter.attach_ms(50, ShutterRtc50mS );
@@ -220,11 +266,13 @@ void ShutterInit(void)
Shutter.open_time[i] = (Settings.shutter_opentime[i] > 0) ? Settings.shutter_opentime[i] : 100;
Shutter.close_time[i] = (Settings.shutter_closetime[i] > 0) ? Settings.shutter_closetime[i] : 100;
+ Shutter.pwm_min[i] = pwm_min;
+ Shutter.pwm_max[i] = pwm_max;
+
// Update Calculation 20 because time interval is 0.05 sec
Shutter.open_max[i] = 200 * Shutter.open_time[i];
Shutter.close_velocity[i] = Shutter.open_max[i] / Shutter.close_time[i] / 2 ;
- Shutter.max_close_pwm_frequency[i] = Shutter.max_pwm_frequency*Shutter.open_time[i] / Shutter.close_time[i];
- AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Shutter %d Closefreq: %d"),i, Shutter.max_close_pwm_frequency[i]);
+
// calculate a ramp slope at the first 5 percent to compensate that shutters move with down part later than the upper part
if (Settings.shutter_set50percent[i] != 50) {
@@ -240,9 +288,17 @@ void ShutterInit(void)
Shutter.motordelay[i] = Settings.shutter_motordelay[i];
Shutter.lastdirection[i] = (50 < Settings.shutter_position[i]) ? 1 : -1;
- AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT%d: Init. Pos: %d,inverted %d, locked %d, end stop time enabled %d, webButtons inverted %d, shuttermode %d"),
- i+1, Shutter.real_position[i],
- (Settings.shutter_options[i]&1) ? 1 : 0, (Settings.shutter_options[i]&2) ? 1 : 0, (Settings.shutter_options[i]&4) ? 1 : 0, (Settings.shutter_options[i]&8) ? 1 : 0, Shutter.mode);
+ switch (Shutter.PositionMode) {
+ case SHT_COUNTER:
+ case SHT_PWM_VALUE:
+ Shutter.max_close_pwm_velocity[i] = Shutter.max_pwm_velocity*Shutter.open_time[i] / Shutter.close_time[i];
+ break;
+ }
+
+ //AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Shutter %d Closevel: %d"),i, Shutter.max_close_pwm_velocity[i]);
+ AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT%d: Init. Pos: %d,inverted %d, locked %d, end stop time enabled %d, webButtons inverted %d"),
+ i+1, Shutter.real_position[i],
+ (Settings.shutter_options[i]&1) ? 1 : 0, (Settings.shutter_options[i]&2) ? 1 : 0, (Settings.shutter_options[i]&4) ? 1 : 0, (Settings.shutter_options[i]&8) ? 1 : 0);
} else {
// terminate loop at first INVALID shutter.
@@ -250,6 +306,7 @@ void ShutterInit(void)
}
ShutterLimitRealAndTargetPositions(i);
Settings.shutter_accuracy = 1;
+ Settings.shutter_mode = Shutter.PositionMode;
}
}
@@ -290,101 +347,130 @@ void ShutterLimitRealAndTargetPositions(uint32_t i) {
if (Shutter.target_position[i]>Shutter.open_max[i]) Shutter.target_position[i] = Shutter.open_max[i];
}
+void ShutterCalculateAccelerator(uint8_t i)
+{
+ switch (Shutter.PositionMode) {
+ case SHT_COUNTER:
+ case SHT_PWM_VALUE:
+ int32_t max_frequency = Shutter.direction[i] == 1 ? Shutter.max_pwm_velocity : Shutter.max_close_pwm_velocity[i];
+ int32_t max_freq_change_per_sec = Shutter.max_pwm_velocity*steps_per_second / (Shutter.motordelay[i]>0 ? Shutter.motordelay[i] : 1);
+ int32_t min_runtime_ms = Shutter.pwm_velocity[i]*1000 / max_freq_change_per_sec;
+ int32_t velocity = Shutter.direction[i] == 1 ? 100 : Shutter.close_velocity[i];
+ int32_t minstopway = min_runtime_ms * velocity / 100 * Shutter.pwm_velocity[i] / max_frequency * Shutter.direction[i] ;
+
+ int32_t next_possible_stop = Shutter.real_position[i] + minstopway ;
+ stop_position_delta =200 * Shutter.pwm_velocity[i]/max_frequency + Shutter.direction[i] * (next_possible_stop - Shutter.target_position[i]);
+
+ //Shutter.accelerator[i] = tmin(tmax(max_freq_change_per_sec*(100-(Shutter.direction[i]*(Shutter.target_position[i]-next_possible_stop) ))/2000 , max_freq_change_per_sec*9/200), max_freq_change_per_sec*11/200);
+ //int32_t act_freq_change = max_freq_change_per_sec/20;
+ AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: time: %d, velocity %d, minstopway %d,cur_freq %d, max_frequency %d, act_freq_change %d, min_runtime_ms %d, act.pos %d, next_stop %d, target: %d"),Shutter.time[i],velocity,minstopway,
+ Shutter.pwm_velocity[i],max_frequency, Shutter.accelerator[i],min_runtime_ms,Shutter.real_position[i], next_possible_stop,Shutter.target_position[i]);
+
+ if (Shutter.accelerator[i] < 0 || next_possible_stop * Shutter.direction[i] > (Shutter.target_position[i]- (100 * Shutter.direction[i])) * Shutter.direction[i] ) {
+
+ Shutter.accelerator[i] = - tmin(tmax(max_freq_change_per_sec*(100-(Shutter.direction[i]*(Shutter.target_position[i]-next_possible_stop) ))/2000 , max_freq_change_per_sec*9/200), max_freq_change_per_sec*11/200);
+ //AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Ramp down: acc: %d"), Shutter.accelerator[i]);
+ } else if ( Shutter.accelerator[i] > 0 && Shutter.pwm_velocity[i] == max_frequency) {
+ Shutter.accelerator[i] = 0;
+ }
+ break;
+ }
+}
+
+void ShutterDecellerateForStop(uint8_t i)
+{
+ switch (Shutter.PositionMode) {
+ case SHT_PWM_VALUE:
+ case SHT_COUNTER:
+ int16_t missing_steps;
+ Shutter.accelerator[i] = -(Shutter.direction[i] == 1 ? Shutter.max_pwm_velocity : Shutter.max_close_pwm_velocity[i])/(Shutter.motordelay[i]+1);
+ while (Shutter.pwm_velocity[i] > -Shutter.accelerator[i]) {
+ //AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: velocity: %ld, delta: %d"), Shutter.pwm_velocity[i], Shutter.accelerator[i] );
+ //Shutter.pwm_velocity[i] = tmax(Shutter.pwm_velocity[i]-Shutter.accelerator[i] , 0);
+ // Control will be done in RTC Ticker.
+ delay(50);
+ }
+ if (Shutter.PositionMode == SHT_COUNTER){
+ missing_steps = ((Shutter.target_position[i]-Shutter.start_position[i])*Shutter.direction[i]*Shutter.max_pwm_velocity/2000) - RtcSettings.pulse_counter[i];
+ //prepare for stop PWM
+ AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Remain steps %d, counter %d, freq %d"), missing_steps, RtcSettings.pulse_counter[i] ,Shutter.pwm_velocity[i]);
+ Shutter.accelerator[i] = 0;
+ Shutter.pwm_velocity[i] = Shutter.pwm_velocity[i] > 250 ? 250 : Shutter.pwm_velocity[i];
+ analogWriteFreq(Shutter.pwm_velocity[i]);
+ analogWrite(Pin(GPIO_PWM1, i), 50);
+ Shutter.pwm_velocity[i] = 0;
+ analogWriteFreq(Shutter.pwm_velocity[i]);
+ while (RtcSettings.pulse_counter[i] < (uint32_t)(Shutter.target_position[i]-Shutter.start_position[i])*Shutter.direction[i]*Shutter.max_pwm_velocity/2000) {
+ delay(1);
+ }
+ analogWrite(Pin(GPIO_PWM1, i), 0); // removed with 8.3 because of reset caused by watchog
+ Shutter.real_position[i] = ShutterCalculatePosition(i);
+ AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Real %d, pulsecount %d, start %d"), Shutter.real_position[i],RtcSettings.pulse_counter[i], Shutter.start_position[i]);
+
+ }
+ Shutter.direction[i] = 0;
+ break;
+ }
+}
+
+void ShutterPowerOff(uint8_t i) {
+ AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT: Stop Shutter %d .."), i);
+ ShutterDecellerateForStop(i);
+ if (Shutter.direction[i] !=0) {
+ Shutter.direction[i] = 0;
+ delay(MOTOR_STOP_TIME);
+ }
+ switch (Shutter.SwitchMode) {
+ case SHT_SWITCH:
+ if ((1 << (Settings.shutter_startrelay[i]-1)) & power) {
+ ExecuteCommandPowerShutter(Settings.shutter_startrelay[i], 0, SRC_SHUTTER);
+ }
+ if ((1 << (Settings.shutter_startrelay[i])) & power) {
+ ExecuteCommandPowerShutter(Settings.shutter_startrelay[i]+1, 0, SRC_SHUTTER);
+ }
+ break;
+ case SHT_PULSE:
+ uint8_t cur_relay = Settings.shutter_startrelay[i] + (Shutter.direction[i] == 1 ? 0 : (uint8_t)(Shutter.PositionMode == SHT_TIME)) ;
+ // we have a momentary switch here. Needs additional pulse on same relay after the end
+ if ((SRC_PULSETIMER == last_source || SRC_SHUTTER == last_source || SRC_WEBGUI == last_source)) {
+ ExecuteCommandPowerShutter(cur_relay, 1, SRC_SHUTTER);
+ // switch off direction relay to make it power less
+ if ((1 << (Settings.shutter_startrelay[i])) & power) {
+ ExecuteCommandPowerShutter(Settings.shutter_startrelay[i]+1, 0, SRC_SHUTTER);
+ }
+ } else {
+ last_source = SRC_SHUTTER;
+ }
+ break;
+ }
+}
+
void ShutterUpdatePosition(void)
{
char scommand[CMDSZ];
char stopic[TOPSZ];
+ stop_position_delta = 20;
+
for (uint32_t i = 0; i < shutters_present; i++) {
if (Shutter.direction[i] != 0) {
- int32_t stop_position_delta = 20;
+
// Calculate position with counter. Much more accurate and no need for motordelay workaround
// adding some steps to stop early
- Shutter.real_position[i] = ShutterCounterBasedPosition(i);
+ Shutter.real_position[i] = ShutterCalculatePosition(i);
if (!Shutter.start_reported) {
ShutterReportPosition(true, i);
XdrvRulesProcess();
Shutter.start_reported = 1;
}
+ ShutterCalculateAccelerator(i);
- if (Shutter.mode == SHT_OFF_ON__OPEN_CLOSE_STEPPER) {
- int32_t max_frequency = Shutter.direction[i] == 1 ? Shutter.max_pwm_frequency : Shutter.max_close_pwm_frequency[i];
- int32_t max_freq_change_per_sec = Shutter.max_pwm_frequency*steps_per_second / (Shutter.motordelay[i]>0 ? Shutter.motordelay[i] : 1);
- int32_t min_runtime_ms = Shutter.pwm_frequency[i]*1000 / max_freq_change_per_sec;
- int32_t velocity = Shutter.direction[i] == 1 ? 100 : Shutter.close_velocity[i];
- int32_t minstopway = min_runtime_ms * velocity / 100 * Shutter.pwm_frequency[i] / max_frequency * Shutter.direction[i] ;
-
- int32_t next_possible_stop = Shutter.real_position[i] + minstopway ;
- stop_position_delta =200 * Shutter.pwm_frequency[i]/max_frequency + Shutter.direction[i] * (next_possible_stop - Shutter.target_position[i]);
-
- //Shutter.accelerator[i] = tmin(tmax(max_freq_change_per_sec*(100-(Shutter.direction[i]*(Shutter.target_position[i]-next_possible_stop) ))/2000 , max_freq_change_per_sec*9/200), max_freq_change_per_sec*11/200);
- //int32_t act_freq_change = max_freq_change_per_sec/20;
- AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: time: %d, velocity %d, minstopway %d,cur_freq %d, max_frequency %d, act_freq_change %d, min_runtime_ms %d, act.pos %d, next_stop %d, target: %d"),Shutter.time[i],velocity,minstopway,
- Shutter.pwm_frequency[i],max_frequency, Shutter.accelerator[i],min_runtime_ms,Shutter.real_position[i], next_possible_stop,Shutter.target_position[i]);
-
- if (Shutter.accelerator[i] < 0 || next_possible_stop * Shutter.direction[i] > (Shutter.target_position[i]- (100 * Shutter.direction[i])) * Shutter.direction[i] ) {
-
- Shutter.accelerator[i] = - tmin(tmax(max_freq_change_per_sec*(100-(Shutter.direction[i]*(Shutter.target_position[i]-next_possible_stop) ))/2000 , max_freq_change_per_sec*9/200), max_freq_change_per_sec*11/200);
- //AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Ramp down: acc: %d"), Shutter.accelerator[i]);
- } else if ( Shutter.accelerator[i] > 0 && Shutter.pwm_frequency[i] == max_frequency) {
- Shutter.accelerator[i] = 0;
- }
- } else {
- Shutter.real_position[i] = Shutter.start_position[i] + ( (Shutter.time[i] - Shutter.motordelay[i]) * (Shutter.direction[i] > 0 ? 100 : -Shutter.close_velocity[i]));
- }
- if ( Shutter.real_position[i] * Shutter.direction[i] + stop_position_delta >= Shutter.target_position[i] * Shutter.direction[i] ) {
- // calculate relay number responsible for current movement.
- //AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Stop Condition detected: real: %d, Target: %d, direction: %d"),Shutter.real_position[i], Shutter.target_position[i],Shutter.direction[i]);
- uint8_t cur_relay = Settings.shutter_startrelay[i] + (Shutter.direction[i] == 1 ? 0 : 1) ;
- int16_t missing_steps;
-
- switch (Shutter.mode) {
- case SHT_PULSE_OPEN__PULSE_CLOSE:
- // we have a momentary switch here. Needs additional pulse on same relay after the end
- if (SRC_PULSETIMER == last_source || SRC_SHUTTER == last_source || SRC_WEBGUI == last_source) {
- ExecuteCommandPower(cur_relay, 1, SRC_SHUTTER);
- } else {
- last_source = SRC_SHUTTER;
- }
- break;
- case SHT_OFF_ON__OPEN_CLOSE_STEPPER:
- missing_steps = ((Shutter.target_position[i]-Shutter.start_position[i])*Shutter.direction[i]*Shutter.max_pwm_frequency/2000) - RtcSettings.pulse_counter[i];
- //prepare for stop PWM
- AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Remain steps %d, counter %d, freq %d"), missing_steps, RtcSettings.pulse_counter[i] ,Shutter.pwm_frequency[i]);
- Shutter.accelerator[i] = 0;
- Shutter.pwm_frequency[i] = Shutter.pwm_frequency[i] > 250 ? 250 : Shutter.pwm_frequency[i];
- analogWriteFreq(Shutter.pwm_frequency[i]);
- analogWrite(Pin(GPIO_PWM1, i), 50);
- Shutter.pwm_frequency[i] = 0;
- analogWriteFreq(Shutter.pwm_frequency[i]);
- while (RtcSettings.pulse_counter[i] < (uint32_t)(Shutter.target_position[i]-Shutter.start_position[i])*Shutter.direction[i]*Shutter.max_pwm_frequency/2000) {
- delay(1);
- }
- analogWrite(Pin(GPIO_PWM1, i), 0); // removed with 8.3 because of reset caused by watchog
-// ExecuteCommandPower(Settings.shutter_startrelay[i]+2, 0, SRC_SHUTTER);
- Shutter.real_position[i] = ShutterCounterBasedPosition(i);
- AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Real %d, pulsecount %d, start %d"), Shutter.real_position[i],RtcSettings.pulse_counter[i], Shutter.start_position[i]);
-
- if ((1 << (Settings.shutter_startrelay[i]-1)) & power) {
- ExecuteCommandPower(Settings.shutter_startrelay[i], 0, SRC_SHUTTER);
- ExecuteCommandPower(Settings.shutter_startrelay[i]+1, 0, SRC_SHUTTER);
- }
- break;
- case SHT_OFF_ON__OPEN_CLOSE:
- if ((1 << (Settings.shutter_startrelay[i]-1)) & power) {
- ExecuteCommandPower(Settings.shutter_startrelay[i], 0, SRC_SHUTTER);
- ExecuteCommandPower(Settings.shutter_startrelay[i]+1, 0, SRC_SHUTTER);
- }
- break;
- case SHT_OFF_OPEN__OFF_CLOSE:
- // avoid switching OFF a relay already OFF
- if ((1 << (cur_relay-1)) & power) {
- // Relay is on and need to be switched off.
- ExecuteCommandPower(cur_relay, 0, SRC_SHUTTER);
- }
- break;
+ if ( Shutter.real_position[i] * Shutter.direction[i] + stop_position_delta >= Shutter.target_position[i] * Shutter.direction[i] ) {
+ if (Shutter.direction[i] != 0) {
+ Shutter.lastdirection[i] = Shutter.direction[i];
}
+ ShutterPowerOff(i);
ShutterLimitRealAndTargetPositions(i);
Settings.shutter_position[i] = ShutterRealToPercentPosition(Shutter.real_position[i], i);
@@ -397,11 +483,6 @@ void ShutterUpdatePosition(void)
GetTopic_P(stopic, STAT, mqtt_topic, scommand);
Response_P("%d", (Settings.shutter_options[i] & 1) ? 100 - Settings.shutter_position[i]: Settings.shutter_position[i]);
MqttPublish(stopic, Settings.flag.mqtt_power_retain); // CMND_POWERRETAIN
-
- if (Shutter.direction[i] != 0) {
- Shutter.lastdirection[i] = Shutter.direction[i];
- }
- Shutter.direction[i] = 0;
ShutterReportPosition(true, i);
rules_flag.shutter_moved = 1;
XdrvRulesProcess();
@@ -425,14 +506,20 @@ void ShutterStartInit(uint32_t i, int32_t direction, int32_t target_pos)
|| ( (-1 == direction) && (Shutter.real_position[i] / Shutter.close_velocity[i] <= 2)) ) {
Shutter.skip_relay_change = 1;
} else {
- if (Shutter.mode == SHT_OFF_ON__OPEN_CLOSE_STEPPER) {
- Shutter.pwm_frequency[i] = 0;
- analogWriteFreq(Shutter.pwm_frequency[i]);
- analogWrite(Pin(GPIO_PWM1, i), 0);
- RtcSettings.pulse_counter[i] = 0;
- Shutter.accelerator[i] = Shutter.max_pwm_frequency / (Shutter.motordelay[i]>0 ? Shutter.motordelay[i] : 1);
- AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Ramp up: %d"), Shutter.accelerator[i]);
+ Shutter.pwm_velocity[i] = 0;
+ switch (Shutter.PositionMode) {
+#ifdef SHUTTER_STEPPER
+ case SHT_COUNTER:
+ analogWriteFreq(Shutter.pwm_velocity[i]);
+ analogWrite(Pin(GPIO_PWM1, i), 0);
+ RtcSettings.pulse_counter[i] = 0;
+ break;
+#endif
+ case SHT_PWM_VALUE:
+ Shutter.max_pwm_velocity = 100;
+ break;
}
+ Shutter.accelerator[i] = Shutter.max_pwm_velocity / (Shutter.motordelay[i]>0 ? Shutter.motordelay[i] : 1);
Shutter.target_position[i] = target_pos;
Shutter.start_position[i] = Shutter.real_position[i];
Shutter.time[i] = 0;
@@ -441,40 +528,31 @@ void ShutterStartInit(uint32_t i, int32_t direction, int32_t target_pos)
rules_flag.shutter_moving = 1;
rules_flag.shutter_moved = 0;
Shutter.start_reported = 0;
- //AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: real %d, start %d, counter %d, max_freq %d, dir %d, freq %d"),Shutter.real_position[i], Shutter.start_position[i] ,RtcSettings.pulse_counter[i],Shutter.max_pwm_frequency , Shutter.direction[i] ,Shutter.max_pwm_frequency );
+ //AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: real %d, start %d, counter %d, max_freq %d, dir %d, freq %d"),Shutter.real_position[i], Shutter.start_position[i] ,RtcSettings.pulse_counter[i],Shutter.max_pwm_velocity , Shutter.direction[i] ,Shutter.max_pwm_velocity );
}
- //AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT: Start shutter: %d from %d to %d in directin %d"), i, Shutter.start_position[i], Shutter.target_position[i], Shutter.direction[i]);
+ //AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT: Start shutter: %d from %d to %d in direction %d"), i, Shutter.start_position[i], Shutter.target_position[i], Shutter.direction[i]);
}
-void ShutterWaitForMotorStop(uint32_t i)
+
+int32_t ShutterCalculatePosition(uint32_t i)
{
- AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT: Wait for Motorstop.."));
- if ((SHT_OFF_ON__OPEN_CLOSE == Shutter.mode) || (SHT_OFF_ON__OPEN_CLOSE_STEPPER == Shutter.mode)) {
- if (SHT_OFF_ON__OPEN_CLOSE_STEPPER == Shutter.mode) {
- //AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT: Frequency change %d"), Shutter.pwm_frequency);
- while (Shutter.pwm_frequency[i] > 0) {
- //AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Frequency: %ld, delta: %d"), Shutter.pwm_frequency[i], (int32_t)((Shutter.direction[i] == 1 ? Shutter.max_pwm_frequency : Shutter.max_close_pwm_frequency[i])/(Shutter.motordelay[i]+1)) );
- Shutter.pwm_frequency[i] = tmax(Shutter.pwm_frequency[i]-((Shutter.direction[i] == 1 ? Shutter.max_pwm_frequency : Shutter.max_close_pwm_frequency[i])/(Shutter.motordelay[i]+1)) , 0);
- //AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Frequency: %ld"), Shutter.pwm_frequency[i]);
- analogWriteFreq(Shutter.pwm_frequency[i]);
- analogWrite(Pin(GPIO_PWM1, i), 50);
- delay(50);
- }
- analogWrite(Pin(GPIO_PWM1, i), 0);
-// ExecuteCommandPower(Settings.shutter_startrelay[i]+2, 0, SRC_SHUTTER);
- Shutter.real_position[i] = ShutterCounterBasedPosition(i);
- } else {
- ExecuteCommandPower(Settings.shutter_startrelay[i], 0, SRC_SHUTTER);
- delay(MOTOR_STOP_TIME);
+ switch (Shutter.PositionMode) {
+ case SHT_COUNTER:
+ return ((int32_t)RtcSettings.pulse_counter[i]*Shutter.direction[i]*2000 / Shutter.max_pwm_velocity)+Shutter.start_position[i];
+ break;
+ case SHT_TIME:
+ case SHT_TIME_UP_DOWN:
+ case SHT_TIME_GARAGE:
+ return Shutter.start_position[i] + ( (Shutter.time[i] - Shutter.motordelay[i]) * (Shutter.direction[i] > 0 ? 100 : -Shutter.close_velocity[i]));
+ break;
+ case SHT_PWM_TIME:
+ break;
+ case SHT_PWM_VALUE:
+ return Shutter.real_position[i];
+ break;
+ default:
+ break;
}
- } else {
- delay(MOTOR_STOP_TIME);
- }
-}
-
-int32_t ShutterCounterBasedPosition(uint32_t i)
-{
- return ((int32_t)RtcSettings.pulse_counter[i]*Shutter.direction[i]*2000 / Shutter.max_pwm_frequency)+Shutter.start_position[i];
}
void ShutterRelayChanged(void)
@@ -487,45 +565,72 @@ void ShutterRelayChanged(void)
for (uint32_t i = 0; i < shutters_present; i++) {
power_t powerstate_local = (power >> (Settings.shutter_startrelay[i] -1)) & 3;
+ // SRC_IGNORE added because INTERLOCK function bite causes this as last source for changing the relay.
//uint8 manual_relays_changed = ((Shutter.switched_relay >> (Settings.shutter_startrelay[i] -1)) & 3) && SRC_IGNORE != last_source && SRC_SHUTTER != last_source && SRC_PULSETIMER != last_source ;
uint8 manual_relays_changed = ((Shutter.switched_relay >> (Settings.shutter_startrelay[i] -1)) & 3) && SRC_SHUTTER != last_source && SRC_PULSETIMER != last_source ;
- //AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Shutter %d: source: %s, powerstate_local %ld, Shutter.switched_relay %d, manual change %d"), i+1, GetTextIndexed(stemp1, sizeof(stemp1), last_source, kCommandSource), powerstate_local,Shutter.switched_relay,manual_relays_changed);
+ AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Shutter %d: source: %s, powerstate_local %ld, Shutter.switched_relay %d, manual change %d"), i+1, GetTextIndexed(stemp1, sizeof(stemp1), last_source, kCommandSource), powerstate_local,Shutter.switched_relay,manual_relays_changed);
if (manual_relays_changed) {
//Shutter.skip_relay_change = true;
ShutterLimitRealAndTargetPositions(i);
- if (Shutter.mode == SHT_OFF_ON__OPEN_CLOSE || Shutter.mode == SHT_OFF_ON__OPEN_CLOSE_STEPPER) {
- ShutterWaitForMotorStop(i);
- switch (powerstate_local) {
- case 1:
- ShutterStartInit(i, 1, Shutter.open_max[i]);
- break;
- case 3:
- ShutterStartInit(i, -1, 0);
- break;
- default:
- //AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Shutter %d: Switch OFF motor."),i);
- Shutter.target_position[i] = Shutter.real_position[i];
- }
- } else {
- if (Shutter.direction[i] != 0 && (!powerstate_local || (powerstate_local && Shutter.mode == SHT_PULSE_OPEN__PULSE_CLOSE))) {
- Shutter.target_position[i] = Shutter.real_position[i];
- AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Shutter %d: Switch OFF motor. Target: %ld, source: %s, powerstate_local %ld, Shutter.switched_relay %d, manual change %d"), i+1, Shutter.target_position[i], GetTextIndexed(stemp1, sizeof(stemp1), last_source, kCommandSource), powerstate_local,Shutter.switched_relay,manual_relays_changed);
- } else {
- last_source = SRC_SHUTTER; // avoid switch off in the next loop
- if (powerstate_local == 2) { // testing on CLOSE relay, if ON
- // close with relay two
- ShutterWaitForMotorStop(i);
- ShutterStartInit(i, -1, 0);
- } else {
- // opens with relay one
- ShutterWaitForMotorStop(i);
- ShutterStartInit(i, 1, Shutter.open_max[i]);
- }
- }
+ switch (Shutter.SwitchMode ) {
+ case SHT_PULSE:
+ if (Shutter.direction[i] != 0 && powerstate_local) {
+ Shutter.target_position[i] = Shutter.real_position[i];
+ powerstate_local = 0;
+ AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Shutter %d: Switch OFF motor. Target: %ld, source: %s, powerstate_local %ld, Shutter.switched_relay %d, manual change %d"), i+1, Shutter.target_position[i], GetTextIndexed(stemp1, sizeof(stemp1), last_source, kCommandSource), powerstate_local,Shutter.switched_relay,manual_relays_changed);
+ }
+ break;
+ default:
+ last_source = SRC_SHUTTER; // avoid switch off in the next loop
+ if (Shutter.direction[i] != 0 )ShutterPowerOff(i);
+ }
+ switch (Shutter.PositionMode) {
+ // enum ShutterPositionMode {SHT_TIME, SHT_TIME_UP_DOWN, SHT_TIME_GARAGE, SHT_COUNTER, SHT_PWM_VALUE, SHT_PWM_TIME,};
+ case SHT_TIME_UP_DOWN:
+ case SHT_COUNTER:
+ case SHT_PWM_VALUE:
+ case SHT_PWM_TIME:
+ ShutterPowerOff(i);
+ switch (powerstate_local) {
+ case 1:
+ ShutterStartInit(i, 1, Shutter.open_max[i]);
+ break;
+ case 3:
+ ShutterStartInit(i, -1, 0);
+ break;
+ default:
+ //AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Shutter %d: Switch OFF motor."),i);
+ Shutter.target_position[i] = Shutter.real_position[i];
+ }
+ break;
+ case SHT_TIME:
+ switch (powerstate_local) {
+ case 1:
+ ShutterStartInit(i, 1, Shutter.open_max[i]);
+ break;
+ case 2:
+ ShutterStartInit(i, -1, 0);
+ break;
+ default:
+ //AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Shutter %d: Switch OFF motor."),i);
+ Shutter.target_position[i] = Shutter.real_position[i];
+ }
+ break;
+ case SHT_TIME_GARAGE:
+ switch (powerstate_local) {
+ case 1:
+ ShutterStartInit(i, Shutter.lastdirection[i]*-1 , Shutter.lastdirection[i] == 1 ? 0 : Shutter.open_max[i]);
+ AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Shutter %d Garage. NewTarget %d"), i, Shutter.target_position[i]);
+ break;
+ default:
+ Shutter.target_position[i] = Shutter.real_position[i];
+ }
+
+
+ } // switch (Shutter.PositionMode)
AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Shutter %d: Target: %ld, powerstatelocal %d"), i+1, Shutter.target_position[i], powerstate_local);
- }
- }
- }
+ } // if (manual_relays_changed)
+ } // for (uint32_t i = 0; i < shutters_present; i++)
}
bool ShutterButtonIsSimultaneousHold(uint32_t button_index, uint32_t shutter_index) {
@@ -723,12 +828,12 @@ void ShutterSetPosition(uint32_t device, uint32_t position)
{
char svalue[32]; // Command and number parameter
snprintf_P(svalue, sizeof(svalue), PSTR(D_PRFX_SHUTTER D_CMND_SHUTTER_POSITION "%d %d"), device, position);
- ExecuteCommand(svalue, SRC_IGNORE);
+ ExecuteCommand(svalue, SRC_SHUTTER);
}
void ShutterToggle(bool dir)
{
- //AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT: Payload toggle: %d, i %d"), XdrvMailbox.payload, XdrvMailbox.index);
+ AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT: Payload toggle: %d, i %d, dir %d"), XdrvMailbox.payload, XdrvMailbox.index, dir);
if ((1 == XdrvMailbox.index) && (XdrvMailbox.payload != -99)) {
XdrvMailbox.index = XdrvMailbox.payload;
}
@@ -843,6 +948,8 @@ void CmndShutterStop(void)
AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT: Stop moving %d: dir: %d"), XdrvMailbox.index, Shutter.direction[i]);
// set stop position 10 steps ahead (0.5sec to allow normal stop)
+
+ //ToDo: Replace with function
int32_t temp_realpos = Shutter.start_position[i] + ( (Shutter.time[i]+10) * (Shutter.direction[i] > 0 ? 100 : -Shutter.close_velocity[i]));
XdrvMailbox.payload = ShutterRealToPercentPosition(temp_realpos, i);
//XdrvMailbox.payload = Settings.shuttercoeff[2][i] * 5 > temp_realpos ? temp_realpos / Settings.shuttercoeff[2][i] : (temp_realpos-Settings.shuttercoeff[0,i]) / Settings.shuttercoeff[1][i];
@@ -900,7 +1007,7 @@ void CmndShutterPosition(void)
if (XdrvMailbox.payload != -99) {
//target_pos_percent = (Settings.shutter_options[index] & 1) ? 100 - target_pos_percent : target_pos_percent;
Shutter.target_position[index] = ShutterPercentToRealPosition(target_pos_percent, index);
- //Shutter.accelerator[index] = Shutter.max_pwm_frequency / ((Shutter.motordelay[index] > 0) ? Shutter.motordelay[index] : 1);
+ //Shutter.accelerator[index] = Shutter.max_pwm_velocity / ((Shutter.motordelay[index] > 0) ? Shutter.motordelay[index] : 1);
//Shutter.target_position[index] = XdrvMailbox.payload < 5 ? Settings.shuttercoeff[2][index] * XdrvMailbox.payload : Settings.shuttercoeff[1][index] * XdrvMailbox.payload + Settings.shuttercoeff[0,index];
AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: lastsource %d:, real %d, target %d, payload %d"), last_source, Shutter.real_position[index] ,Shutter.target_position[index],target_pos_percent);
}
@@ -911,44 +1018,50 @@ void CmndShutterPosition(void)
}
int8_t new_shutterdirection = Shutter.real_position[index] < Shutter.target_position[index] ? 1 : -1;
if (Shutter.direction[index] == -new_shutterdirection) {
- // direction need to be changed. on momentary switches first stop the Shutter
- if (SHT_PULSE_OPEN__PULSE_CLOSE == Shutter.mode) {
- // code for momentary shutters only small switch on to stop Shutter
- ExecuteCommandPower(Settings.shutter_startrelay[index] + ((new_shutterdirection == 1) ? 0 : 1), 1, SRC_SHUTTER);
- delay(100);
- } else {
- if (SHT_OFF_OPEN__OFF_CLOSE == Shutter.mode) {
- ExecuteCommandPower(Settings.shutter_startrelay[index] + ((new_shutterdirection == 1) ? 1 : 0), 0, SRC_SHUTTER);
- ShutterWaitForMotorStop(index);
- }
- }
+ ShutterPowerOff(index);
}
if (Shutter.direction[index] != new_shutterdirection) {
- if ((SHT_OFF_ON__OPEN_CLOSE == Shutter.mode) || (SHT_OFF_ON__OPEN_CLOSE_STEPPER == Shutter.mode)) {
- //AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Delay5 5s, xdrv %d"), XdrvMailbox.payload);
- ShutterWaitForMotorStop(index);
- ExecuteCommandPower(Settings.shutter_startrelay[index], 0, SRC_SHUTTER);
- ShutterStartInit(index, new_shutterdirection, Shutter.target_position[index]);
- if (Shutter.skip_relay_change == 0) {
- // Code for shutters with circuit safe configuration, switch the direction Relay
- ExecuteCommandPower(Settings.shutter_startrelay[index] +1, new_shutterdirection == 1 ? 0 : 1, SRC_SHUTTER);
- // power on
- ExecuteCommandPower(Settings.shutter_startrelay[index], 1, SRC_SHUTTER);
- if (SHT_OFF_ON__OPEN_CLOSE_STEPPER == Shutter.mode) {
- ExecuteCommandPower(Settings.shutter_startrelay[index]+2, 1, SRC_SHUTTER);
+ ShutterStartInit(index, new_shutterdirection, Shutter.target_position[index]);
+ switch (Shutter.PositionMode) {
+ case SHT_COUNTER:
+ case SHT_PWM_TIME:
+ case SHT_PWM_VALUE:
+ case SHT_TIME_UP_DOWN:
+ if (!Shutter.skip_relay_change) {
+ // Code for shutters with circuit safe configuration, switch the direction Relay
+ ExecuteCommandPowerShutter(Settings.shutter_startrelay[index] +1, new_shutterdirection == 1 ? 0 : 1, SRC_SHUTTER);
+ // power on
+ ExecuteCommandPowerShutter(Settings.shutter_startrelay[index], 1, SRC_SHUTTER);
}
- }
- } else {
- // now start the motor for the right direction, work for momentary and normal shutters.
- AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT: Start in dir %d"), Shutter.direction[index]);
- ShutterStartInit(index, new_shutterdirection, Shutter.target_position[index]);
- if (Shutter.skip_relay_change == 0) {
- ExecuteCommandPower(Settings.shutter_startrelay[index] + (new_shutterdirection == 1 ? 0 : 1), 1, SRC_SHUTTER);
- }
- //AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Delay6 5s, xdrv %d"), XdrvMailbox.payload);
- }
+ if (Shutter.PositionMode != SHT_TIME_UP_DOWN) ExecuteCommandPowerShutter(Settings.shutter_startrelay[index]+2, 1, SRC_SHUTTER);
+ break;
+ case SHT_TIME:
+ if (!Shutter.skip_relay_change) {
+ if ( (power >> (Settings.shutter_startrelay[index] -1)) & 3 > 0 ) {
+ ExecuteCommandPowerShutter(Settings.shutter_startrelay[index] + (new_shutterdirection == 1 ? 1 : 0), Shutter.SwitchMode == SHT_SWITCH ? 0 : 1, SRC_SHUTTER);
+ }
+ ExecuteCommandPowerShutter(Settings.shutter_startrelay[index] + (new_shutterdirection == 1 ? 0 : 1), 1, SRC_SHUTTER);
+ }
+ break;
+ case SHT_TIME_GARAGE:
+ if (!Shutter.skip_relay_change) {
+ if (new_shutterdirection == Shutter.lastdirection[index]) {
+ AddLog_P2(LOG_LEVEL_INFO, PSTR("SHT: Garage not move in this direction: %d"), Shutter.SwitchMode == SHT_PULSE);
+ for (uint8_t k=0 ; k <= (uint8_t)(Shutter.SwitchMode == SHT_PULSE) ; k++) {
+ ExecuteCommandPowerShutter(Settings.shutter_startrelay[index], 1, SRC_SHUTTER);
+ delay(500);
+ ExecuteCommandPowerShutter(Settings.shutter_startrelay[index], 0, SRC_SHUTTER);
+ delay(500);
+ }
+ // reset shutter time to avoid 2 seconds above count as runtime
+ Shutter.time[index] = 0;
+ } // if (new_shutterdirection == Shutter.lastdirection[index])
+ ExecuteCommandPowerShutter(Settings.shutter_startrelay[index], 1, SRC_SHUTTER);
+ } // if (!Shutter.skip_relay_change)
+ break;
+ } // switch (Shutter.PositionMode)
Shutter.switched_relay = 0;
- }
+ } // if (Shutter.direction[index] != new_shutterdirection)
} else {
target_pos_percent = ShutterRealToPercentPosition(Shutter.real_position[index], index);
ShutterReportPosition(true, index);
@@ -1015,6 +1128,18 @@ void CmndShutterMotorDelay(void)
}
}
+void CmndShutterMode(void)
+{
+ if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= MAX_MODES)) {
+ Shutter.PositionMode = XdrvMailbox.payload;
+ Settings.shutter_mode = XdrvMailbox.payload;
+ ShutterInit();
+ ResponseCmndNumber(XdrvMailbox.payload); // ????
+ } else {
+ ResponseCmndNumber(Shutter.PositionMode);
+ }
+}
+
void CmndShutterRelay(void)
{
if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_SHUTTERS)) {
@@ -1184,14 +1309,14 @@ void CmndShutterSetHalfway(void)
void CmndShutterFrequency(void)
{
if ((XdrvMailbox.payload > 0) && (XdrvMailbox.payload <= 20000)) {
- Shutter.max_pwm_frequency = XdrvMailbox.payload;
+ Shutter.max_pwm_velocity = XdrvMailbox.payload;
if (shutters_present < 4) {
- Settings.shuttercoeff[4][3] = Shutter.max_pwm_frequency;
+ Settings.shuttercoeff[4][3] = Shutter.max_pwm_velocity;
}
ShutterInit();
ResponseCmndNumber(XdrvMailbox.payload); // ????
} else {
- ResponseCmndNumber(Shutter.max_pwm_frequency);
+ ResponseCmndNumber(Shutter.max_pwm_velocity);
}
}
@@ -1353,7 +1478,7 @@ bool Xdrv27(uint8_t function)
result = true;
Shutter.skip_relay_change = 0;
AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Skipping switch off relay %d"),i);
- ExecuteCommandPower(i+1, 0, SRC_SHUTTER);
+ ExecuteCommandPowerShutter(i+1, 0, SRC_SHUTTER);
}
break;
case FUNC_BUTTON_PRESSED:
From f4190a96092a7bb8751a40b168da499247c94a37 Mon Sep 17 00:00:00 2001
From: stefanbode
Date: Sat, 5 Sep 2020 20:41:08 +0200
Subject: [PATCH 003/148] Update i18n.h
New command
---
tasmota/i18n.h | 1 +
1 file changed, 1 insertion(+)
diff --git a/tasmota/i18n.h b/tasmota/i18n.h
index ba7e925f8..25b1cb8b7 100644
--- a/tasmota/i18n.h
+++ b/tasmota/i18n.h
@@ -585,6 +585,7 @@
#define D_CMND_SHUTTER_TOGGLEDIR "ToggleDir"
#define D_CMND_SHUTTER_UP "Up"
#define D_CMND_SHUTTER_DOWN "Down"
+#define D_CMND_SHUTTER_MODE "Mode"
#define D_CMND_SHUTTER_STOPOPEN "StopOpen"
#define D_CMND_SHUTTER_STOPCLOSE "StopClose"
#define D_CMND_SHUTTER_STOPTOGGLE "StopToggle"
From 68c260d520a3940fbdb6f5c438c4a1872c3cb98b Mon Sep 17 00:00:00 2001
From: stefanbode
Date: Sat, 5 Sep 2020 20:43:39 +0200
Subject: [PATCH 004/148] Adding 1 byte new shutter mode to set on demand
---
tasmota/settings.h | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/tasmota/settings.h b/tasmota/settings.h
index 60c837364..3ca80ecd6 100644
--- a/tasmota/settings.h
+++ b/tasmota/settings.h
@@ -613,8 +613,9 @@ struct {
uint8_t free_f43[1]; // F43
uint16_t energy_power_delta[3]; // F44
+ uint8_t shutter_mode; // F45
- uint8_t free_f4e[106]; // F4A - Decrement if adding new Setting variables just above and below
+ uint8_t free_f4e[105]; // F4A - Decrement if adding new Setting variables just above and below
// Only 32 bit boundary variables below
SysBitfield5 flag5; // FB4
From b7565811db1236c73d7c2cd6df0532c008563578 Mon Sep 17 00:00:00 2001
From: stefanbode
Date: Sat, 5 Sep 2020 20:45:42 +0200
Subject: [PATCH 005/148] Adding new shuttermode
---
tasmota/support_command.ino | 7 ++++---
1 file changed, 4 insertions(+), 3 deletions(-)
diff --git a/tasmota/support_command.ino b/tasmota/support_command.ino
index 0cdba2969..db83627e3 100644
--- a/tasmota/support_command.ino
+++ b/tasmota/support_command.ino
@@ -583,11 +583,12 @@ void CmndStatus(void)
if (i > 0) { ResponseAppend_P(PSTR(",")); }
ResponseAppend_P(PSTR("{\"" D_STATUS13_SHUTTER "%d\":{\"Relay1\":%d,\"Relay2\":%d,\"Open\":%d,\"Close\":%d,"
"\"50perc\":%d,\"Delay\":%d,\"Opt\":\"%s\","
- "\"Calib\":\"%d:%d:%d:%d:%d\"}"),
+ "\"Calib\":\"%d:%d:%d:%d:%d\","
+ "\"Mode\":\"%d\"}"),
i, Settings.shutter_startrelay[i], Settings.shutter_startrelay[i] +1, Settings.shutter_opentime[i], Settings.shutter_closetime[i],
Settings.shutter_set50percent[i], Settings.shutter_motordelay[i], GetBinary(&Settings.shutter_options[i], 4).c_str(),
- Settings.shuttercoeff[0][i], Settings.shuttercoeff[1][i], Settings.shuttercoeff[2][i], Settings.shuttercoeff[3][i], Settings.shuttercoeff[4][i]);
- }
+ Settings.shuttercoeff[0][i], Settings.shuttercoeff[1][i], Settings.shuttercoeff[2][i], Settings.shuttercoeff[3][i], Settings.shuttercoeff[4][i],
+ Settings.shutter_mode); }
ResponseJsonEnd();
MqttPublishPrefixTopic_P(option, PSTR(D_CMND_STATUS "13"));
}
From b2b0dfcbfdbf31ef2fe9e3dc28d6c501ce671f10 Mon Sep 17 00:00:00 2001
From: stefanbode
Date: Sat, 5 Sep 2020 21:07:59 +0200
Subject: [PATCH 006/148] Bugfix
---
tasmota/xdrv_27_shutter.ino | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/tasmota/xdrv_27_shutter.ino b/tasmota/xdrv_27_shutter.ino
index 3a9b43d7e..d0fd0ae29 100644
--- a/tasmota/xdrv_27_shutter.ino
+++ b/tasmota/xdrv_27_shutter.ino
@@ -121,7 +121,7 @@ void ShutterRtc50mS(void)
switch (Shutter.PositionMode) {
case SHT_PWM_VALUE:
if (Shutter.accelerator[i]) ShutterUpdateVelocity(i);
- Shutter.real_position[i] += Shutter.direction[i] > 0 ? Shutter.pwm_velocity[i] : -Shutter.pwm_velocity[i];
+ Shutter.real_position[i] += Shutter.direction[i] > 0 ? Shutter.pwm_velocity[i] : (Shutter.direction[i] < 0 ? -Shutter.pwm_velocity[i] : 0);
Shutter.pwm_value[i] = SHT_DIV_ROUND((Shutter.pwm_max[i]-Shutter.pwm_min[i]) * Shutter.real_position[i] , Shutter.open_max[i])+Shutter.pwm_min[i];
analogWrite(Pin(GPIO_PWM1, i), Shutter.pwm_value[i]);
break;
From c3f04288fa85fddbd52ebfd6e94565201658797b Mon Sep 17 00:00:00 2001
From: stefanbode
Date: Sat, 5 Sep 2020 23:35:05 +0200
Subject: [PATCH 007/148] Update xdrv_27_shutter.ino
---
tasmota/xdrv_27_shutter.ino | 30 ++++++++++++++++++++----------
1 file changed, 20 insertions(+), 10 deletions(-)
diff --git a/tasmota/xdrv_27_shutter.ino b/tasmota/xdrv_27_shutter.ino
index d0fd0ae29..c75a2fca2 100644
--- a/tasmota/xdrv_27_shutter.ino
+++ b/tasmota/xdrv_27_shutter.ino
@@ -121,7 +121,7 @@ void ShutterRtc50mS(void)
switch (Shutter.PositionMode) {
case SHT_PWM_VALUE:
if (Shutter.accelerator[i]) ShutterUpdateVelocity(i);
- Shutter.real_position[i] += Shutter.direction[i] > 0 ? Shutter.pwm_velocity[i] : (Shutter.direction[i] < 0 ? -Shutter.pwm_velocity[i] : 0);
+ Shutter.real_position[i] += Shutter.direction[i] > 0 ? Shutter.pwm_velocity[i] : (Shutter.direction[i] < 0 ? -Shutter.pwm_velocity[i] : 0);
Shutter.pwm_value[i] = SHT_DIV_ROUND((Shutter.pwm_max[i]-Shutter.pwm_min[i]) * Shutter.real_position[i] , Shutter.open_max[i])+Shutter.pwm_min[i];
analogWrite(Pin(GPIO_PWM1, i), Shutter.pwm_value[i]);
break;
@@ -292,6 +292,7 @@ void ShutterInit(void)
case SHT_COUNTER:
case SHT_PWM_VALUE:
Shutter.max_close_pwm_velocity[i] = Shutter.max_pwm_velocity*Shutter.open_time[i] / Shutter.close_time[i];
+ stop_position_delta = 0;
break;
}
@@ -357,22 +358,31 @@ void ShutterCalculateAccelerator(uint8_t i)
int32_t min_runtime_ms = Shutter.pwm_velocity[i]*1000 / max_freq_change_per_sec;
int32_t velocity = Shutter.direction[i] == 1 ? 100 : Shutter.close_velocity[i];
int32_t minstopway = min_runtime_ms * velocity / 100 * Shutter.pwm_velocity[i] / max_frequency * Shutter.direction[i] ;
-
+ int32_t toBeAcc = 0;
int32_t next_possible_stop = Shutter.real_position[i] + minstopway ;
- stop_position_delta =200 * Shutter.pwm_velocity[i]/max_frequency + Shutter.direction[i] * (next_possible_stop - Shutter.target_position[i]);
+ stop_position_delta = Shutter.direction[i] * (next_possible_stop - Shutter.target_position[i]);
+ if (Shutter.PositionMode == SHT_COUNTER) {
+ // ToDo need to check the influence of frequency and speed on the secure area to stop right in time.
+ // seems currently only work with motordelay but not ok without motordelay.
+ stop_position_delta =+ 200 * Shutter.pwm_velocity[i]/max_frequency;
+ } else {
+ stop_position_delta =+ Shutter.pwm_velocity[i];
+ }
//Shutter.accelerator[i] = tmin(tmax(max_freq_change_per_sec*(100-(Shutter.direction[i]*(Shutter.target_position[i]-next_possible_stop) ))/2000 , max_freq_change_per_sec*9/200), max_freq_change_per_sec*11/200);
//int32_t act_freq_change = max_freq_change_per_sec/20;
- AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: time: %d, velocity %d, minstopway %d,cur_freq %d, max_frequency %d, act_freq_change %d, min_runtime_ms %d, act.pos %d, next_stop %d, target: %d"),Shutter.time[i],velocity,minstopway,
- Shutter.pwm_velocity[i],max_frequency, Shutter.accelerator[i],min_runtime_ms,Shutter.real_position[i], next_possible_stop,Shutter.target_position[i]);
- if (Shutter.accelerator[i] < 0 || next_possible_stop * Shutter.direction[i] > (Shutter.target_position[i]- (100 * Shutter.direction[i])) * Shutter.direction[i] ) {
-
- Shutter.accelerator[i] = - tmin(tmax(max_freq_change_per_sec*(100-(Shutter.direction[i]*(Shutter.target_position[i]-next_possible_stop) ))/2000 , max_freq_change_per_sec*9/200), max_freq_change_per_sec*11/200);
+ if (Shutter.accelerator[i] < 0 || next_possible_stop * Shutter.direction[i] > (Shutter.target_position[i]- (2*stop_position_delta * Shutter.direction[i])) * Shutter.direction[i] ) {
+ toBeAcc = 100+(Shutter.direction[i]*velocity*(next_possible_stop-Shutter.target_position[i])/Shutter.pwm_velocity[i]);
+ Shutter.accelerator[i] = - tmin(tmax((toBeAcc > 100 ? max_freq_change_per_sec*toBeAcc/2000+1 : max_freq_change_per_sec*toBeAcc/2000) , (max_freq_change_per_sec*9/200)-1), (max_freq_change_per_sec*11/200)+1);
//AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Ramp down: acc: %d"), Shutter.accelerator[i]);
} else if ( Shutter.accelerator[i] > 0 && Shutter.pwm_velocity[i] == max_frequency) {
Shutter.accelerator[i] = 0;
}
+
+ AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: time: %d, toBeAcc %d,velocity %d, minstopway %d,cur_vel %d, max_vel %d, act_vel_change %d, min_runtime_ms %d, act.pos %d, next_stop %d, target: %d, stop_position_delta %d, max_vel_change_per_sec %d"),Shutter.time[i],toBeAcc,velocity,minstopway,
+ Shutter.pwm_velocity[i],max_frequency, Shutter.accelerator[i],min_runtime_ms,Shutter.real_position[i], next_possible_stop,Shutter.target_position[i],stop_position_delta,max_freq_change_per_sec);
+
break;
}
}
@@ -384,8 +394,8 @@ void ShutterDecellerateForStop(uint8_t i)
case SHT_COUNTER:
int16_t missing_steps;
Shutter.accelerator[i] = -(Shutter.direction[i] == 1 ? Shutter.max_pwm_velocity : Shutter.max_close_pwm_velocity[i])/(Shutter.motordelay[i]+1);
- while (Shutter.pwm_velocity[i] > -Shutter.accelerator[i]) {
- //AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: velocity: %ld, delta: %d"), Shutter.pwm_velocity[i], Shutter.accelerator[i] );
+ while (Shutter.pwm_velocity[i] > -Shutter.accelerator[i] && Shutter.accelerator[i] != 0) {
+ AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: velocity: %ld, delta: %d"), Shutter.pwm_velocity[i], Shutter.accelerator[i] );
//Shutter.pwm_velocity[i] = tmax(Shutter.pwm_velocity[i]-Shutter.accelerator[i] , 0);
// Control will be done in RTC Ticker.
delay(50);
From 1c657e86e7de6ab3aac6c14d49e77eb5908f22c4 Mon Sep 17 00:00:00 2001
From: stefanbode
Date: Sun, 6 Sep 2020 12:14:29 +0200
Subject: [PATCH 008/148] Optimizes ramp
---
tasmota/xdrv_27_shutter.ino | 35 +++++++++++++++++------------------
1 file changed, 17 insertions(+), 18 deletions(-)
diff --git a/tasmota/xdrv_27_shutter.ino b/tasmota/xdrv_27_shutter.ino
index c75a2fca2..9b1b33027 100644
--- a/tasmota/xdrv_27_shutter.ino
+++ b/tasmota/xdrv_27_shutter.ino
@@ -292,7 +292,6 @@ void ShutterInit(void)
case SHT_COUNTER:
case SHT_PWM_VALUE:
Shutter.max_close_pwm_velocity[i] = Shutter.max_pwm_velocity*Shutter.open_time[i] / Shutter.close_time[i];
- stop_position_delta = 0;
break;
}
@@ -353,35 +352,35 @@ void ShutterCalculateAccelerator(uint8_t i)
switch (Shutter.PositionMode) {
case SHT_COUNTER:
case SHT_PWM_VALUE:
- int32_t max_frequency = Shutter.direction[i] == 1 ? Shutter.max_pwm_velocity : Shutter.max_close_pwm_velocity[i];
- int32_t max_freq_change_per_sec = Shutter.max_pwm_velocity*steps_per_second / (Shutter.motordelay[i]>0 ? Shutter.motordelay[i] : 1);
- int32_t min_runtime_ms = Shutter.pwm_velocity[i]*1000 / max_freq_change_per_sec;
- int32_t velocity = Shutter.direction[i] == 1 ? 100 : Shutter.close_velocity[i];
- int32_t minstopway = min_runtime_ms * velocity / 100 * Shutter.pwm_velocity[i] / max_frequency * Shutter.direction[i] ;
+ int32_t max_velocity = Shutter.direction[i] == 1 ? Shutter.max_pwm_velocity : Shutter.max_close_pwm_velocity[i];
+ int32_t max_velocity_change_per_step = Shutter.max_pwm_velocity / (Shutter.motordelay[i]>0 ? Shutter.motordelay[i] : 1);
+ int32_t min_runtime_ms = Shutter.pwm_velocity[i] * 1000 / steps_per_second / max_velocity_change_per_step;
+ //int32_t velocity = Shutter.direction[i] == 1 ? 100 : Shutter.close_velocity[i];
+ int32_t minstopway = (min_runtime_ms * (Shutter.pwm_velocity[i]+max_velocity_change_per_step)/100 - Shutter.pwm_velocity[i]) * Shutter.direction[i] ;
int32_t toBeAcc = 0;
int32_t next_possible_stop = Shutter.real_position[i] + minstopway ;
stop_position_delta = Shutter.direction[i] * (next_possible_stop - Shutter.target_position[i]);
if (Shutter.PositionMode == SHT_COUNTER) {
// ToDo need to check the influence of frequency and speed on the secure area to stop right in time.
// seems currently only work with motordelay but not ok without motordelay.
- stop_position_delta =+ 200 * Shutter.pwm_velocity[i]/max_frequency;
+ stop_position_delta =+ 200 * Shutter.pwm_velocity[i]/max_velocity;
} else {
- stop_position_delta =+ Shutter.pwm_velocity[i];
+ stop_position_delta =+ Shutter.pwm_velocity[i]-max_velocity_change_per_step;
}
- //Shutter.accelerator[i] = tmin(tmax(max_freq_change_per_sec*(100-(Shutter.direction[i]*(Shutter.target_position[i]-next_possible_stop) ))/2000 , max_freq_change_per_sec*9/200), max_freq_change_per_sec*11/200);
- //int32_t act_freq_change = max_freq_change_per_sec/20;
+ //Shutter.accelerator[i] = tmin(tmax(max_velocity_change_per_step*(100-(Shutter.direction[i]*(Shutter.target_position[i]-next_possible_stop) ))/2000 , max_velocity_change_per_step*9/200), max_velocity_change_per_step*11/200);
+ //int32_t act_freq_change = max_velocity_change_per_step/20;
- if (Shutter.accelerator[i] < 0 || next_possible_stop * Shutter.direction[i] > (Shutter.target_position[i]- (2*stop_position_delta * Shutter.direction[i])) * Shutter.direction[i] ) {
- toBeAcc = 100+(Shutter.direction[i]*velocity*(next_possible_stop-Shutter.target_position[i])/Shutter.pwm_velocity[i]);
- Shutter.accelerator[i] = - tmin(tmax((toBeAcc > 100 ? max_freq_change_per_sec*toBeAcc/2000+1 : max_freq_change_per_sec*toBeAcc/2000) , (max_freq_change_per_sec*9/200)-1), (max_freq_change_per_sec*11/200)+1);
+ if (Shutter.accelerator[i] < 0 || next_possible_stop * Shutter.direction[i] > (Shutter.target_position[i]- (stop_position_delta * Shutter.direction[i])) * Shutter.direction[i] ) {
+ toBeAcc = 100+(Shutter.direction[i]*max_velocity*(next_possible_stop-Shutter.target_position[i])/Shutter.pwm_velocity[i]);
+ Shutter.accelerator[i] = - tmin(tmax((toBeAcc > 100 ? max_velocity_change_per_step*toBeAcc/100 : max_velocity_change_per_step*toBeAcc/100) , (max_velocity_change_per_step*9/10)-1), (max_velocity_change_per_step*11/10)+1);
//AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Ramp down: acc: %d"), Shutter.accelerator[i]);
- } else if ( Shutter.accelerator[i] > 0 && Shutter.pwm_velocity[i] == max_frequency) {
+ } else if ( Shutter.accelerator[i] > 0 && Shutter.pwm_velocity[i] == max_velocity) {
Shutter.accelerator[i] = 0;
}
- AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: time: %d, toBeAcc %d,velocity %d, minstopway %d,cur_vel %d, max_vel %d, act_vel_change %d, min_runtime_ms %d, act.pos %d, next_stop %d, target: %d, stop_position_delta %d, max_vel_change_per_sec %d"),Shutter.time[i],toBeAcc,velocity,minstopway,
- Shutter.pwm_velocity[i],max_frequency, Shutter.accelerator[i],min_runtime_ms,Shutter.real_position[i], next_possible_stop,Shutter.target_position[i],stop_position_delta,max_freq_change_per_sec);
+ AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: time: %d, toBeAcc %d, minstopway %d,cur_vel %d, max_vel %d, act_vel_change %d, min_runtime_ms %d, act.pos %d, next_stop %d, target: %d, stop_position_delta %d, max_vel_change_per_step %d"),Shutter.time[i],toBeAcc,minstopway,
+ Shutter.pwm_velocity[i],max_velocity, Shutter.accelerator[i],min_runtime_ms,Shutter.real_position[i], next_possible_stop,Shutter.target_position[i],stop_position_delta,max_velocity_change_per_step);
break;
}
@@ -393,7 +392,7 @@ void ShutterDecellerateForStop(uint8_t i)
case SHT_PWM_VALUE:
case SHT_COUNTER:
int16_t missing_steps;
- Shutter.accelerator[i] = -(Shutter.direction[i] == 1 ? Shutter.max_pwm_velocity : Shutter.max_close_pwm_velocity[i])/(Shutter.motordelay[i]+1);
+ Shutter.accelerator[i] = -(Shutter.max_pwm_velocity / (Shutter.motordelay[i]>0 ? Shutter.motordelay[i] : 1));
while (Shutter.pwm_velocity[i] > -Shutter.accelerator[i] && Shutter.accelerator[i] != 0) {
AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: velocity: %ld, delta: %d"), Shutter.pwm_velocity[i], Shutter.accelerator[i] );
//Shutter.pwm_velocity[i] = tmax(Shutter.pwm_velocity[i]-Shutter.accelerator[i] , 0);
@@ -526,7 +525,7 @@ void ShutterStartInit(uint32_t i, int32_t direction, int32_t target_pos)
break;
#endif
case SHT_PWM_VALUE:
- Shutter.max_pwm_velocity = 100;
+ Shutter.max_pwm_velocity = 100;
break;
}
Shutter.accelerator[i] = Shutter.max_pwm_velocity / (Shutter.motordelay[i]>0 ? Shutter.motordelay[i] : 1);
From 5176e3ab02ffab614e1fb1a52e55b06694ed1fe7 Mon Sep 17 00:00:00 2001
From: Theo Arends <11044339+arendst@users.noreply.github.com>
Date: Mon, 7 Sep 2020 14:54:31 +0200
Subject: [PATCH 009/148] Bump version to 8.5.0.1
---
RELEASENOTES.md | 27 +--------------------------
tasmota/CHANGELOG.md | 14 ++++++++++++++
tasmota/tasmota_version.h | 2 +-
3 files changed, 16 insertions(+), 27 deletions(-)
diff --git a/RELEASENOTES.md b/RELEASENOTES.md
index 3703517fc..8c95d47b5 100644
--- a/RELEASENOTES.md
+++ b/RELEASENOTES.md
@@ -53,29 +53,4 @@ The following binary downloads have been compiled with ESP8266/Arduino library c
## Changelog
-### Version 8.4.0.3
-
-- Remove support for direct upgrade from versions before 6.6.0.11 to versions after 8.4.0.1
-- Change references from http://thehackbox.org/tasmota/ to http://ota.tasmota.com/tasmota/
-- Change triple-mode TLS via configuration in a single firmware (TLS AWS IoT, Letsencrypt and No-TLS)
-- Change White blend mode to using command ``SetOption 105`` instead of ``RGBWWTable``
-- Fix ESP32 PWM range
-- Fix display power control (#9114)
-- Add command ``SetOption102 0/1`` to set Baud rate for Teleinfo communication (0 = 1200 or 1 = 9600)
-- Add command ``SetOption103 0/1`` to set TLS mode when TLS is selected
-- Add command ``SetOption104 1`` to disable all MQTT retained messages
-- Add command ``SetOption106 1`` to create a virtual White ColorTemp for RGBW lights
-- Add command ``SetOption107 0/1`` to select virtual White as (0) Warm or (1) Cold
-- Add command ``SetOption108 0/1`` to enable Teleinfo telemetry into Tasmota Energy MQTT (0) or Teleinfo only (1)
-- Add command ``SetOption109 1`` to force gen1 Alexa mode, for Echo Dot 2nd gen devices only
-- Add command ``Restart 2`` to halt system. Needs hardware reset or power cycle to restart (#9046)
-- Add command ``PowerDelta1`` to ``PowerDelta3`` to trigger on up to three phases (#9134)
-- Add Zigbee options to ``ZbSend`` ``Config`` and ``ReadCondig``
-- Add Zigbee better support for IKEA Motion Sensor
-- Add Zigbee web gui widget for Battery and Temp/Humidity/Pressure sensors
-- Add Zigbee web ui for power metering plugs
-- Add better configuration corruption recovery (#9046)
-- Add virtual CT for 4 channels lights, emulating a 5th channel
-- Add support for DYP ME007 ultrasonic distance sensor by Janusz Kostorz (#9113)
-- Add ESP32 Analog input support for GPIO32 to GPIO39
-- Add experimental support for ESP32 TTGO Watch and I2S Audio by Gerhard Mutz
+### Version 8.5.0.1
diff --git a/tasmota/CHANGELOG.md b/tasmota/CHANGELOG.md
index e5967a9a1..0f5b4ec47 100644
--- a/tasmota/CHANGELOG.md
+++ b/tasmota/CHANGELOG.md
@@ -1,5 +1,15 @@
+## Released
+
## Unreleased (development)
+### 8.5.0.1 20200907
+
+- New released
+
+### 8.5.0 20200907
+
+- Release Hannah
+
### 8.4.0.3 20200823
- Change references from http://thehackbox.org/tasmota/ to http://ota.tasmota.com/tasmota/
@@ -14,6 +24,10 @@
- Remove support for direct upgrade from versions before 6.6.0.11 to versions after 8.4.0.1
- Change White blend mode moved to using ``SetOption 105`` instead of ``RGBWWTable``
- Fix display power control (#9114)
+- Add command ``SetOption103 0/1`` to set TLS mode when TLS is selected
+- Add command ``SetOption104 1`` to disable all MQTT retained messages
+- Add command ``SetOption106 1`` to create a virtual White ColorTemp for RGBW lights
+- Add command ``SetOption107 0/1`` to select virtual White as (0) Warm or (1) Cold
- Add command ``SetOption108 0/1`` to enable Teleinfo telemetry into Tasmota Energy MQTT (0) or Teleinfo only (1) - Add better config corruption recovery (#9046)
- Add virtual CT for 4 channels lights, emulating a 5th channel
- Add support for DYP ME007 ultrasonic distance sensor by Janusz Kostorz (#9113)
diff --git a/tasmota/tasmota_version.h b/tasmota/tasmota_version.h
index ce7e073c2..3103534c2 100644
--- a/tasmota/tasmota_version.h
+++ b/tasmota/tasmota_version.h
@@ -20,7 +20,7 @@
#ifndef _TASMOTA_VERSION_H_
#define _TASMOTA_VERSION_H_
-const uint32_t VERSION = 0x08040003;
+const uint32_t VERSION = 0x08050001;
// Lowest compatible version
const uint32_t VERSION_COMPATIBLE = 0x07010006;
From 654f9de322e89ee6743244adff0a50905dfd0d9e Mon Sep 17 00:00:00 2001
From: Jason2866 <24528715+Jason2866@users.noreply.github.com>
Date: Mon, 7 Sep 2020 17:16:23 +0200
Subject: [PATCH 010/148] espressif32@2.0.0
---
platformio_tasmota32.ini | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/platformio_tasmota32.ini b/platformio_tasmota32.ini
index feca8c849..549f2e333 100644
--- a/platformio_tasmota32.ini
+++ b/platformio_tasmota32.ini
@@ -2,7 +2,7 @@
; *** expect the unexpected. Some features not working!!! ***
[common32]
-platform = espressif32@1.12.4
+platform = espressif32@2.0.0
platform_packages = tool-esptoolpy@1.20800.0
board = esp32dev
board_build.ldscript = esp32_out.ld
From 72afb156017447c9a566eed3ce8b24a94f595c6e Mon Sep 17 00:00:00 2001
From: stefanbode
Date: Tue, 8 Sep 2020 19:34:10 +0200
Subject: [PATCH 011/148] Fixes on Stepper + Servo
- fix restart bug on servo
- refactor smooth-ramp for stepper and servo. Now in RTC and much more stable
- increased internal resolution to better work with ramps
- testing, testing, testing....
---
tasmota/xdrv_27_shutter.ino | 208 ++++++++++++++++++++----------------
1 file changed, 113 insertions(+), 95 deletions(-)
diff --git a/tasmota/xdrv_27_shutter.ino b/tasmota/xdrv_27_shutter.ino
index 9b1b33027..439800ac2 100644
--- a/tasmota/xdrv_27_shutter.ino
+++ b/tasmota/xdrv_27_shutter.ino
@@ -30,14 +30,22 @@
#define D_SHUTTER "SHUTTER"
const uint16_t MOTOR_STOP_TIME = 500; // in mS
-const uint8_t steps_per_second = 20; // FUNC_EVERY_50_MSECOND
+const uint16_t RESOLUTION = 1000;
+const uint8_t STEPS_PER_SECOND = 20; // FUNC_EVERY_50_MSECOND
const uint16_t pwm_max = 500;
const uint16_t pwm_min = 90;
uint8_t calibrate_pos[6] = {0,30,50,70,90,100};
uint16_t messwerte[5] = {30,50,70,90,100};
uint16_t last_execute_step;
-int32_t stop_position_delta = 20;
+
+int32_t max_velocity = 0;
+int32_t max_velocity_change_per_step = 0;
+int32_t min_runtime_ms = 0;
+int32_t minstopway = 0;
+int32_t next_possible_stop = 0;
+int32_t toBeAcc = 0;
+
const uint8_t MAX_MODES = 7;
enum ShutterPositionMode {SHT_UNDEF, SHT_TIME, SHT_TIME_UP_DOWN, SHT_TIME_GARAGE, SHT_COUNTER, SHT_PWM_VALUE, SHT_PWM_TIME,};
@@ -98,8 +106,8 @@ struct SHUTTER {
void ShutterLogPos(uint32_t i)
{
char stemp2[10];
- dtostrfd((float)Shutter.time[i] / steps_per_second, 2, stemp2);
- AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT: Shutter%d Real %d, Start %d, Stop %d, Dir %d, Delay %d, Rtc %s [s], Freq %d, PWM %d"),
+ dtostrfd((float)Shutter.time[i] / STEPS_PER_SECOND, 2, stemp2);
+ AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT: Shutter %d Real %d, Start %d, Stop %d, Dir %d, Delay %d, Rtc %s [s], Freq %d, PWM %d"),
i+1, Shutter.real_position[i], Shutter.start_position[i], Shutter.target_position[i], Shutter.direction[i], Shutter.motordelay[i], stemp2, Shutter.pwm_velocity[i], Shutter.pwm_value[i]);
}
@@ -111,30 +119,35 @@ void ExecuteCommandPowerShutter(uint32_t device, uint32_t state, uint32_t source
void ShutterUpdateVelocity(uint8_t i)
{
Shutter.pwm_velocity[i] += Shutter.accelerator[i];
- Shutter.pwm_velocity[i] = tmax(1,tmin(Shutter.direction[i]==1 ? Shutter.max_pwm_velocity : Shutter.max_close_pwm_velocity[i],Shutter.pwm_velocity[i]));
+ Shutter.pwm_velocity[i] = tmax(0,tmin(Shutter.direction[i]==1 ? Shutter.max_pwm_velocity : Shutter.max_close_pwm_velocity[i],Shutter.pwm_velocity[i]));
}
void ShutterRtc50mS(void)
{
for (uint8_t i = 0; i < shutters_present; i++) {
- Shutter.time[i]++;
- switch (Shutter.PositionMode) {
- case SHT_PWM_VALUE:
- if (Shutter.accelerator[i]) ShutterUpdateVelocity(i);
- Shutter.real_position[i] += Shutter.direction[i] > 0 ? Shutter.pwm_velocity[i] : (Shutter.direction[i] < 0 ? -Shutter.pwm_velocity[i] : 0);
- Shutter.pwm_value[i] = SHT_DIV_ROUND((Shutter.pwm_max[i]-Shutter.pwm_min[i]) * Shutter.real_position[i] , Shutter.open_max[i])+Shutter.pwm_min[i];
- analogWrite(Pin(GPIO_PWM1, i), Shutter.pwm_value[i]);
- break;
-
- case SHT_COUNTER:
- if (Shutter.accelerator[i]) {
- //AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: accelerator i=%d -> %d"),i, Shutter.accelerator[i]);
+ if (Shutter.direction[i]) {
+ // update position data before increasing counter
+ Shutter.real_position[i] = ShutterCalculatePosition(i);
+ Shutter.time[i]++;
+ ShutterCalculateAccelerator(i);
+ switch (Shutter.PositionMode) {
+ case SHT_PWM_VALUE:
ShutterUpdateVelocity(i);
- analogWriteFreq(Shutter.pwm_velocity[i]);
- analogWrite(Pin(GPIO_PWM1, i), 50);
- }
- break;
- }
+ Shutter.real_position[i] += Shutter.direction[i] > 0 ? Shutter.pwm_velocity[i] : (Shutter.direction[i] < 0 ? -Shutter.pwm_velocity[i] : 0);
+ Shutter.pwm_value[i] = SHT_DIV_ROUND((Shutter.pwm_max[i]-Shutter.pwm_min[i]) * Shutter.real_position[i] , Shutter.open_max[i])+Shutter.pwm_min[i];
+ analogWrite(Pin(GPIO_PWM1, i), Shutter.pwm_value[i]);
+ break;
+
+ case SHT_COUNTER:
+ if (Shutter.accelerator[i]) {
+ //AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: accelerator i=%d -> %d"),i, Shutter.accelerator[i]);
+ ShutterUpdateVelocity(i);
+ analogWriteFreq(Shutter.pwm_velocity[i]);
+ analogWrite(Pin(GPIO_PWM1, i), 50);
+ }
+ break;
+ }
+ } // if (Shutter.direction[i])
}
}
@@ -266,14 +279,14 @@ void ShutterInit(void)
Shutter.open_time[i] = (Settings.shutter_opentime[i] > 0) ? Settings.shutter_opentime[i] : 100;
Shutter.close_time[i] = (Settings.shutter_closetime[i] > 0) ? Settings.shutter_closetime[i] : 100;
+ //temporary hard coded.
Shutter.pwm_min[i] = pwm_min;
Shutter.pwm_max[i] = pwm_max;
- // Update Calculation 20 because time interval is 0.05 sec
- Shutter.open_max[i] = 200 * Shutter.open_time[i];
+ // Update Calculation 20 because time interval is 0.05 sec ans time is in 0.1sec
+ Shutter.open_max[i] = STEPS_PER_SECOND * RESOLUTION * Shutter.open_time[i] / 10;
Shutter.close_velocity[i] = Shutter.open_max[i] / Shutter.close_time[i] / 2 ;
-
// calculate a ramp slope at the first 5 percent to compensate that shutters move with down part later than the upper part
if (Settings.shutter_set50percent[i] != 50) {
Settings.shuttercoeff[1][i] = Shutter.open_max[i] * (100 - Settings.shutter_set50percent[i] ) / 5000;
@@ -283,19 +296,19 @@ void ShutterInit(void)
Shutter.mask |= 3 << (Settings.shutter_startrelay[i] -1);
Shutter.real_position[i] = ShutterPercentToRealPosition(Settings.shutter_position[i], i);
- //Shutter.real_position[i] = Settings.shutter_position[i] <= 5 ? Settings.shuttercoeff[2][i] * Settings.shutter_position[i] : Settings.shuttercoeff[1][i] * Settings.shutter_position[i] + Settings.shuttercoeff[0,i];
+
Shutter.start_position[i] = Shutter.target_position[i] = Shutter.real_position[i];
Shutter.motordelay[i] = Settings.shutter_motordelay[i];
Shutter.lastdirection[i] = (50 < Settings.shutter_position[i]) ? 1 : -1;
switch (Shutter.PositionMode) {
- case SHT_COUNTER:
case SHT_PWM_VALUE:
- Shutter.max_close_pwm_velocity[i] = Shutter.max_pwm_velocity*Shutter.open_time[i] / Shutter.close_time[i];
+ Shutter.max_pwm_velocity = RESOLUTION;
break;
}
+ Shutter.max_close_pwm_velocity[i] = Shutter.max_pwm_velocity*Shutter.open_time[i] / Shutter.close_time[i];
- //AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Shutter %d Closevel: %d"),i, Shutter.max_close_pwm_velocity[i]);
+ //AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Shutter %d Openvel %d, Closevel: %d"),i, Shutter.max_pwm_velocity, Shutter.max_close_pwm_velocity[i]);
AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT%d: Init. Pos: %d,inverted %d, locked %d, end stop time enabled %d, webButtons inverted %d"),
i+1, Shutter.real_position[i],
(Settings.shutter_options[i]&1) ? 1 : 0, (Settings.shutter_options[i]&2) ? 1 : 0, (Settings.shutter_options[i]&4) ? 1 : 0, (Settings.shutter_options[i]&8) ? 1 : 0);
@@ -349,40 +362,35 @@ void ShutterLimitRealAndTargetPositions(uint32_t i) {
void ShutterCalculateAccelerator(uint8_t i)
{
- switch (Shutter.PositionMode) {
- case SHT_COUNTER:
- case SHT_PWM_VALUE:
- int32_t max_velocity = Shutter.direction[i] == 1 ? Shutter.max_pwm_velocity : Shutter.max_close_pwm_velocity[i];
- int32_t max_velocity_change_per_step = Shutter.max_pwm_velocity / (Shutter.motordelay[i]>0 ? Shutter.motordelay[i] : 1);
- int32_t min_runtime_ms = Shutter.pwm_velocity[i] * 1000 / steps_per_second / max_velocity_change_per_step;
- //int32_t velocity = Shutter.direction[i] == 1 ? 100 : Shutter.close_velocity[i];
- int32_t minstopway = (min_runtime_ms * (Shutter.pwm_velocity[i]+max_velocity_change_per_step)/100 - Shutter.pwm_velocity[i]) * Shutter.direction[i] ;
- int32_t toBeAcc = 0;
- int32_t next_possible_stop = Shutter.real_position[i] + minstopway ;
- stop_position_delta = Shutter.direction[i] * (next_possible_stop - Shutter.target_position[i]);
- if (Shutter.PositionMode == SHT_COUNTER) {
- // ToDo need to check the influence of frequency and speed on the secure area to stop right in time.
- // seems currently only work with motordelay but not ok without motordelay.
- stop_position_delta =+ 200 * Shutter.pwm_velocity[i]/max_velocity;
- } else {
- stop_position_delta =+ Shutter.pwm_velocity[i]-max_velocity_change_per_step;
- }
+ if (Shutter.direction[i] != 0) {
+ switch (Shutter.PositionMode) {
+ case SHT_COUNTER:
+ case SHT_PWM_VALUE:
+ // calculate max velocity allowed in this direction
+ max_velocity = Shutter.direction[i] == 1 ? Shutter.max_pwm_velocity : Shutter.max_close_pwm_velocity[i];
+ // calculate max change of velocyty based on the defined motordelay in steps
+ max_velocity_change_per_step = max_velocity / (Shutter.motordelay[i]>0 ? Shutter.motordelay[i] : 1);
+ // minimumtime required from current velocity to stop
+ min_runtime_ms = Shutter.pwm_velocity[i] * 1000 / STEPS_PER_SECOND / max_velocity_change_per_step;
+ // decellartion way from current velocity
+ minstopway = (min_runtime_ms * (Shutter.pwm_velocity[i]+max_velocity_change_per_step)/100 - Shutter.pwm_velocity[i])*RESOLUTION/Shutter.max_pwm_velocity * Shutter.direction[i] ;
+ next_possible_stop = Shutter.real_position[i] + minstopway ;
+ toBeAcc = 0;
+ // ensure that accelerator kicks in IN TIME and that STOP procedure kicks in at least ONE step before reach end position.
+ //Shutter.accelerator[i] = tmin(tmax(max_velocity_change_per_step*(100-(Shutter.direction[i]*(Shutter.target_position[i]-next_possible_stop) ))/2000 , max_velocity_change_per_step*9/200), max_velocity_change_per_step*11/200);
+ //int32_t act_freq_change = max_velocity_change_per_step/20;
- //Shutter.accelerator[i] = tmin(tmax(max_velocity_change_per_step*(100-(Shutter.direction[i]*(Shutter.target_position[i]-next_possible_stop) ))/2000 , max_velocity_change_per_step*9/200), max_velocity_change_per_step*11/200);
- //int32_t act_freq_change = max_velocity_change_per_step/20;
-
- if (Shutter.accelerator[i] < 0 || next_possible_stop * Shutter.direction[i] > (Shutter.target_position[i]- (stop_position_delta * Shutter.direction[i])) * Shutter.direction[i] ) {
- toBeAcc = 100+(Shutter.direction[i]*max_velocity*(next_possible_stop-Shutter.target_position[i])/Shutter.pwm_velocity[i]);
- Shutter.accelerator[i] = - tmin(tmax((toBeAcc > 100 ? max_velocity_change_per_step*toBeAcc/100 : max_velocity_change_per_step*toBeAcc/100) , (max_velocity_change_per_step*9/10)-1), (max_velocity_change_per_step*11/10)+1);
- //AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Ramp down: acc: %d"), Shutter.accelerator[i]);
- } else if ( Shutter.accelerator[i] > 0 && Shutter.pwm_velocity[i] == max_velocity) {
- Shutter.accelerator[i] = 0;
- }
-
- AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: time: %d, toBeAcc %d, minstopway %d,cur_vel %d, max_vel %d, act_vel_change %d, min_runtime_ms %d, act.pos %d, next_stop %d, target: %d, stop_position_delta %d, max_vel_change_per_step %d"),Shutter.time[i],toBeAcc,minstopway,
- Shutter.pwm_velocity[i],max_velocity, Shutter.accelerator[i],min_runtime_ms,Shutter.real_position[i], next_possible_stop,Shutter.target_position[i],stop_position_delta,max_velocity_change_per_step);
-
- break;
+ // ensure that the accelerotor kicks in at least one step BEFORE it is to late and a hard stop required.
+ if (Shutter.accelerator[i] < 0 || (next_possible_stop * Shutter.direction[i]) +RESOLUTION*Shutter.pwm_velocity[i]/Shutter.max_pwm_velocity>= Shutter.target_position[i] * Shutter.direction[i] ) {
+ // 10 times the deviation is the value of this simple p-regulator
+ toBeAcc = 100+(Shutter.direction[i]*(next_possible_stop-Shutter.target_position[i])*max_velocity/Shutter.pwm_velocity[i]*10/RESOLUTION);
+ Shutter.accelerator[i] = - tmin(tmax( max_velocity_change_per_step*toBeAcc/100 , (max_velocity_change_per_step*9/10)), (max_velocity_change_per_step*11/10));
+ //AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Ramp down: acc: %d"), Shutter.accelerator[i]);
+ } else if ( Shutter.accelerator[i] > 0 && Shutter.pwm_velocity[i] == max_velocity) {
+ Shutter.accelerator[i] = 0;
+ }
+ break;
+ }
}
}
@@ -392,15 +400,15 @@ void ShutterDecellerateForStop(uint8_t i)
case SHT_PWM_VALUE:
case SHT_COUNTER:
int16_t missing_steps;
- Shutter.accelerator[i] = -(Shutter.max_pwm_velocity / (Shutter.motordelay[i]>0 ? Shutter.motordelay[i] : 1));
- while (Shutter.pwm_velocity[i] > -Shutter.accelerator[i] && Shutter.accelerator[i] != 0) {
+ Shutter.accelerator[i] = -(Shutter.max_pwm_velocity / (Shutter.motordelay[i]>0 ? Shutter.motordelay[i] : 1) *11/10);
+ while (Shutter.pwm_velocity[i] > -2*Shutter.accelerator[i] ) {
AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: velocity: %ld, delta: %d"), Shutter.pwm_velocity[i], Shutter.accelerator[i] );
//Shutter.pwm_velocity[i] = tmax(Shutter.pwm_velocity[i]-Shutter.accelerator[i] , 0);
// Control will be done in RTC Ticker.
delay(50);
}
if (Shutter.PositionMode == SHT_COUNTER){
- missing_steps = ((Shutter.target_position[i]-Shutter.start_position[i])*Shutter.direction[i]*Shutter.max_pwm_velocity/2000) - RtcSettings.pulse_counter[i];
+ missing_steps = ((Shutter.target_position[i]-Shutter.start_position[i])*Shutter.direction[i]*Shutter.max_pwm_velocity/RESOLUTION/STEPS_PER_SECOND) - RtcSettings.pulse_counter[i];
//prepare for stop PWM
AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Remain steps %d, counter %d, freq %d"), missing_steps, RtcSettings.pulse_counter[i] ,Shutter.pwm_velocity[i]);
Shutter.accelerator[i] = 0;
@@ -409,7 +417,7 @@ void ShutterDecellerateForStop(uint8_t i)
analogWrite(Pin(GPIO_PWM1, i), 50);
Shutter.pwm_velocity[i] = 0;
analogWriteFreq(Shutter.pwm_velocity[i]);
- while (RtcSettings.pulse_counter[i] < (uint32_t)(Shutter.target_position[i]-Shutter.start_position[i])*Shutter.direction[i]*Shutter.max_pwm_velocity/2000) {
+ while (RtcSettings.pulse_counter[i] < (uint32_t)(Shutter.target_position[i]-Shutter.start_position[i])*Shutter.direction[i]*Shutter.max_pwm_velocity/RESOLUTION/STEPS_PER_SECOND) {
delay(1);
}
analogWrite(Pin(GPIO_PWM1, i), 0); // removed with 8.3 because of reset caused by watchog
@@ -418,6 +426,7 @@ void ShutterDecellerateForStop(uint8_t i)
}
Shutter.direction[i] = 0;
+ Shutter.pwm_velocity[i] = 0;
break;
}
}
@@ -452,6 +461,14 @@ void ShutterPowerOff(uint8_t i) {
}
break;
}
+ // Store current PWM value to ensure proper position after reboot.
+ switch (Shutter.PositionMode) {
+ case SHT_PWM_VALUE:
+ char scmnd[20];
+ snprintf_P(scmnd, sizeof(scmnd), PSTR(D_CMND_PWM " %d" ),Shutter.pwm_value[i]);
+ ExecuteCommand(scmnd, SRC_BUTTON);
+ break;
+ }
}
void ShutterUpdatePosition(void)
@@ -460,33 +477,33 @@ void ShutterUpdatePosition(void)
char scommand[CMDSZ];
char stopic[TOPSZ];
- stop_position_delta = 20;
-
for (uint32_t i = 0; i < shutters_present; i++) {
if (Shutter.direction[i] != 0) {
// Calculate position with counter. Much more accurate and no need for motordelay workaround
// adding some steps to stop early
- Shutter.real_position[i] = ShutterCalculatePosition(i);
+ //Shutter.real_position[i] = ShutterCalculatePosition(i);
if (!Shutter.start_reported) {
ShutterReportPosition(true, i);
XdrvRulesProcess();
Shutter.start_reported = 1;
}
- ShutterCalculateAccelerator(i);
+ //ShutterCalculateAccelerator(i);
+ AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: time: %d, toBeAcc %d, minstopway %d,cur_vel %d, max_vel %d, act_vel_change %d, min_runtime_ms %d, act.pos %d, next_stop %d, target: %d, max_vel_change_per_step %d"),Shutter.time[i],toBeAcc,minstopway,
+ Shutter.pwm_velocity[i],max_velocity, Shutter.accelerator[i],min_runtime_ms,Shutter.real_position[i], next_possible_stop,Shutter.target_position[i],max_velocity_change_per_step);
- if ( Shutter.real_position[i] * Shutter.direction[i] + stop_position_delta >= Shutter.target_position[i] * Shutter.direction[i] ) {
+
+ if ( Shutter.real_position[i] * Shutter.direction[i] >= Shutter.target_position[i] * Shutter.direction[i] || Shutter.pwm_velocity[i]0 ? Shutter.motordelay[i] : 1);
Shutter.target_position[i] = target_pos;
@@ -545,22 +559,26 @@ void ShutterStartInit(uint32_t i, int32_t direction, int32_t target_pos)
int32_t ShutterCalculatePosition(uint32_t i)
{
- switch (Shutter.PositionMode) {
- case SHT_COUNTER:
- return ((int32_t)RtcSettings.pulse_counter[i]*Shutter.direction[i]*2000 / Shutter.max_pwm_velocity)+Shutter.start_position[i];
+ if (Shutter.direction[i] != 0) {
+ switch (Shutter.PositionMode) {
+ case SHT_COUNTER:
+ return ((int32_t)RtcSettings.pulse_counter[i]*Shutter.direction[i]*STEPS_PER_SECOND*RESOLUTION / Shutter.max_pwm_velocity)+Shutter.start_position[i];
+ break;
+ case SHT_TIME:
+ case SHT_TIME_UP_DOWN:
+ case SHT_TIME_GARAGE:
+ return Shutter.start_position[i] + ( (Shutter.time[i] - Shutter.motordelay[i]) * (Shutter.direction[i] > 0 ? RESOLUTION : -Shutter.close_velocity[i]));
+ break;
+ case SHT_PWM_TIME:
+ break;
+ case SHT_PWM_VALUE:
+ return Shutter.real_position[i];
break;
- case SHT_TIME:
- case SHT_TIME_UP_DOWN:
- case SHT_TIME_GARAGE:
- return Shutter.start_position[i] + ( (Shutter.time[i] - Shutter.motordelay[i]) * (Shutter.direction[i] > 0 ? 100 : -Shutter.close_velocity[i]));
- break;
- case SHT_PWM_TIME:
- break;
- case SHT_PWM_VALUE:
+ default:
+ break;
+ }
+ } else {
return Shutter.real_position[i];
- break;
- default:
- break;
}
}
@@ -591,7 +609,7 @@ void ShutterRelayChanged(void)
break;
default:
last_source = SRC_SHUTTER; // avoid switch off in the next loop
- if (Shutter.direction[i] != 0 )ShutterPowerOff(i);
+ if (Shutter.direction[i] != 0 ) ShutterPowerOff(i);
}
switch (Shutter.PositionMode) {
// enum ShutterPositionMode {SHT_TIME, SHT_TIME_UP_DOWN, SHT_TIME_GARAGE, SHT_COUNTER, SHT_PWM_VALUE, SHT_PWM_TIME,};
@@ -1022,8 +1040,8 @@ void CmndShutterPosition(void)
}
if ( (target_pos_percent >= 0) && (target_pos_percent <= 100) && abs(Shutter.target_position[index] - Shutter.real_position[index] ) / Shutter.close_velocity[index] > 2) {
if (Settings.shutter_options[index] & 4) {
- if (0 == target_pos_percent) Shutter.target_position[index] -= 1 * 2000;
- if (100 == target_pos_percent) Shutter.target_position[index] += 1 * 2000;
+ if (0 == target_pos_percent) Shutter.target_position[index] -= 1 * RESOLUTION * STEPS_PER_SECOND;
+ if (100 == target_pos_percent) Shutter.target_position[index] += 1 * RESOLUTION * STEPS_PER_SECOND;
}
int8_t new_shutterdirection = Shutter.real_position[index] < Shutter.target_position[index] ? 1 : -1;
if (Shutter.direction[index] == -new_shutterdirection) {
@@ -1128,11 +1146,11 @@ void CmndShutterMotorDelay(void)
{
if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= shutters_present)) {
if (XdrvMailbox.data_len > 0) {
- Settings.shutter_motordelay[XdrvMailbox.index -1] = (uint16_t)(steps_per_second * CharToFloat(XdrvMailbox.data));
+ Settings.shutter_motordelay[XdrvMailbox.index -1] = (uint16_t)(STEPS_PER_SECOND * CharToFloat(XdrvMailbox.data));
ShutterInit();
}
char time_chr[10];
- dtostrfd((float)(Settings.shutter_motordelay[XdrvMailbox.index -1]) / steps_per_second, 2, time_chr);
+ dtostrfd((float)(Settings.shutter_motordelay[XdrvMailbox.index -1]) / STEPS_PER_SECOND, 2, time_chr);
ResponseCmndIdxChar(time_chr);
}
}
From 5eb24348084d7ad4291214a7b1e1a3da04a51ada Mon Sep 17 00:00:00 2001
From: Stephan Hadinger
Date: Tue, 8 Sep 2020 21:10:24 +0200
Subject: [PATCH 012/148] Fix crash in `ZbRestore`
---
tasmota/xdrv_23_zigbee_1_headers.ino | 2 +-
tasmota/xdrv_23_zigbee_7_statemachine.ino | 2 +-
tasmota/xdrv_23_zigbee_9_serial.ino | 1 +
3 files changed, 3 insertions(+), 2 deletions(-)
diff --git a/tasmota/xdrv_23_zigbee_1_headers.ino b/tasmota/xdrv_23_zigbee_1_headers.ino
index bb7555757..604c53835 100644
--- a/tasmota/xdrv_23_zigbee_1_headers.ino
+++ b/tasmota/xdrv_23_zigbee_1_headers.ino
@@ -57,7 +57,7 @@ JsonVariant &startsWithCaseInsensitive(const JsonObject &json, const char *needl
return *(JsonVariant*)nullptr;
}
- String needle_s(needle);
+ String needle_s((const __FlashStringHelper *)needle);
needle_s.toLowerCase();
for (auto kv : json) {
diff --git a/tasmota/xdrv_23_zigbee_7_statemachine.ino b/tasmota/xdrv_23_zigbee_7_statemachine.ino
index 5a6ab4e29..ee9bfd19a 100644
--- a/tasmota/xdrv_23_zigbee_7_statemachine.ino
+++ b/tasmota/xdrv_23_zigbee_7_statemachine.ino
@@ -681,7 +681,7 @@ ZBM(ZBS_SET_CONCENTRATOR, EZSP_setConcentrator, 0x00 /*high*/, 0x00 /*false*/, 0
ZBM(ZBR_SET_CONCENTRATOR, EZSP_setConcentrator, 0x00 /*high*/, 0x00 /*ok*/) // 100000
// setInitialSecurityState
-#define EZ_SECURITY_MODE EMBER_TRUST_CENTER_GLOBAL_LINK_KEY | EMBER_PRECONFIGURED_NETWORK_KEY_MODE | EMBER_HAVE_NETWORK_KEY | EMBER_HAVE_PRECONFIGURED_KEY | EMBER_NO_FRAME_COUNTER_RESET
+#define EZ_SECURITY_MODE EMBER_TRUST_CENTER_GLOBAL_LINK_KEY | EMBER_PRECONFIGURED_NETWORK_KEY_MODE | EMBER_HAVE_NETWORK_KEY | EMBER_HAVE_PRECONFIGURED_KEY
ZBR(ZBS_SET_SECURITY, EZSP_setInitialSecurityState, 0x00 /*high*/,
Z_B0(EZ_SECURITY_MODE), Z_B1(EZ_SECURITY_MODE),
// preConfiguredKey
diff --git a/tasmota/xdrv_23_zigbee_9_serial.ino b/tasmota/xdrv_23_zigbee_9_serial.ino
index f5056c58c..fd69ee16a 100644
--- a/tasmota/xdrv_23_zigbee_9_serial.ino
+++ b/tasmota/xdrv_23_zigbee_9_serial.ino
@@ -598,6 +598,7 @@ int32_t ZigbeeProcessInputEZSP(class SBuffer &buf) {
case EZSP_messageSentHandler: // 3F00
case EZSP_setConfigurationValue: // 5300
case EZSP_setPolicy: // 5500
+ case 0x0059: // 5900 - supposedly removed by still happening
case EZSP_setMulticastTableEntry: // 6400
case EZSP_setInitialSecurityState: // 6800
case EZSP_getCurrentSecurityState: // 6900
From bef03c69f6d89f3573581838a45ee758bf63133f Mon Sep 17 00:00:00 2001
From: Stephan Hadinger
Date: Tue, 8 Sep 2020 21:22:52 +0200
Subject: [PATCH 013/148] Add ``#define USE_MQTT_AWS_IOT_LIGHT`` for password
based AWS IoT authentication
---
tasmota/CHANGELOG.md | 2 +-
tasmota/StackThunk_light.cpp | 4 +++-
tasmota/WiFiClientSecureLightBearSSL.cpp | 13 +++++--------
tasmota/my_user_config.h | 5 +++--
tasmota/xdrv_02_mqtt.ino | 6 +++---
5 files changed, 15 insertions(+), 15 deletions(-)
diff --git a/tasmota/CHANGELOG.md b/tasmota/CHANGELOG.md
index 0f5b4ec47..d9c9b1bfc 100644
--- a/tasmota/CHANGELOG.md
+++ b/tasmota/CHANGELOG.md
@@ -4,7 +4,7 @@
### 8.5.0.1 20200907
-- New released
+- Add ``#define USE_MQTT_AWS_IOT_LIGHT`` for password based AWS IoT authentication
### 8.5.0 20200907
diff --git a/tasmota/StackThunk_light.cpp b/tasmota/StackThunk_light.cpp
index c9f9bc78e..3c0d91bcf 100644
--- a/tasmota/StackThunk_light.cpp
+++ b/tasmota/StackThunk_light.cpp
@@ -40,8 +40,10 @@ uint32_t *stack_thunk_light_save = NULL; /* Saved A1 while in BearSSL */
uint32_t stack_thunk_light_refcnt = 0;
//#define _stackSize (5600/4)
-#ifdef USE_MQTT_TLS_FORCE_EC_CIPHER
+#if defined(USE_MQTT_AWS_IOT)
#define _stackSize (5300/4) // using a light version of bearssl we can save 300 bytes
+#elif defined(USE_MQTT_TLS_FORCE_EC_CIPHER)
+ #define _stackSize (4800/4) // no private key, we can reduce a little, max observed 4300
#else
#define _stackSize (3600/4) // using a light version of bearssl we can save 2k
#endif
diff --git a/tasmota/WiFiClientSecureLightBearSSL.cpp b/tasmota/WiFiClientSecureLightBearSSL.cpp
index 209555c3f..d85373688 100755
--- a/tasmota/WiFiClientSecureLightBearSSL.cpp
+++ b/tasmota/WiFiClientSecureLightBearSSL.cpp
@@ -872,7 +872,11 @@ extern "C" {
#ifdef USE_MQTT_TLS_FORCE_EC_CIPHER
// we support only P256 EC curve for AWS IoT, no EC curve for Letsencrypt unless forced
- br_ssl_engine_set_ec(&cc->eng, &br_ec_p256_m15);
+ br_ssl_engine_set_ec(&cc->eng, &br_ec_p256_m15); // TODO
+#endif
+#ifdef USE_MQTT_AWS_IOT_LIGHT
+ static const char * alpn_mqtt = "mqtt";
+ br_ssl_engine_set_protocol_names(&cc->eng, &alpn_mqtt, 1);
#endif
}
}
@@ -880,13 +884,6 @@ extern "C" {
// Called by connect() to do the actual SSL setup and handshake.
// Returns if the SSL handshake succeeded.
bool WiFiClientSecure_light::_connectSSL(const char* hostName) {
-// #ifdef USE_MQTT_AWS_IOT
-// if ((!_chain_P) || (!_sk_ec_P)) {
-// setLastError(ERR_MISSING_EC_KEY);
-// return false;
-// }
-// #endif
-
// Validation context, either full CA validation or checking only fingerprints
#ifdef USE_MQTT_TLS_CA_CERT
br_x509_minimal_context *x509_minimal;
diff --git a/tasmota/my_user_config.h b/tasmota/my_user_config.h
index 5f94c5fc0..961a82e10 100644
--- a/tasmota/my_user_config.h
+++ b/tasmota/my_user_config.h
@@ -389,7 +389,8 @@
// #define USE_MQTT_TLS_CA_CERT // Force full CA validation instead of fingerprints, slower, but simpler to use. (+2.2k code, +1.9k mem during connection handshake)
// This includes the LetsEncrypt CA in tasmota_ca.ino for verifying server certificates
// #define USE_MQTT_TLS_FORCE_EC_CIPHER // Force Elliptic Curve cipher (higher security) required by some servers (automatically enabled with USE_MQTT_AWS_IOT) (+11.4k code, +0.4k mem)
-// #define USE_MQTT_AWS_IOT // Enable MQTT for AWS IoT - requires a private key (+11.9k code, +0.4k mem)
+// #define USE_MQTT_AWS_IOT_LIGHT // Enable MQTT for AWS IoT in light mode, with user/password instead of private certificate
+// #define USE_MQTT_AWS_IOT // [Deprecated] Enable MQTT for AWS IoT - requires a private key (+11.9k code, +0.4k mem)
// Note: you need to generate a private key + certificate per device and update 'tasmota/tasmota_aws_iot.cpp'
// Full documentation here: https://github.com/arendst/Tasmota/wiki/AWS-IoT
// #define USE_4K_RSA // Support 4096 bits certificates, instead of 2048
@@ -820,7 +821,7 @@
#include "user_config_override.h" // Configuration overrides for my_user_config.h
#endif
-#if defined(USE_DISCOVERY) && defined(USE_MQTT_AWS_IOT)
+#if defined(USE_DISCOVERY) && (defined(USE_MQTT_AWS_IOT) || defined(USE_MQTT_AWS_IOT_LIGHT))
#error "Select either USE_DISCOVERY or USE_MQTT_AWS_IOT, mDNS takes too much code space and is not needed for AWS IoT"
#endif
diff --git a/tasmota/xdrv_02_mqtt.ino b/tasmota/xdrv_02_mqtt.ino
index 767e65b8e..bcd71005f 100644
--- a/tasmota/xdrv_02_mqtt.ino
+++ b/tasmota/xdrv_02_mqtt.ino
@@ -154,7 +154,7 @@ void MqttInit(void)
String host = String(SettingsText(SET_MQTT_HOST));
if (host.indexOf(".iot.") && host.endsWith(".amazonaws.com")) { // look for ".iot." and ".amazonaws.com" in the domain name
Settings.flag4.mqtt_no_retain = true;
- Mqtt.tls_private_key = true;
+ // Mqtt.tls_private_key = true;
}
if (Settings.flag4.mqtt_tls) {
@@ -353,7 +353,7 @@ void MqttPublishPrefixTopic_P(uint32_t prefix, const char* subtopic, bool retain
GetTopic_P(stopic, prefix, mqtt_topic, romram);
MqttPublish(stopic, retained);
-#ifdef USE_MQTT_AWS_IOT
+#if defined(USE_MQTT_AWS_IOT) || defined(USE_MQTT_AWS_IOT_LIGHT)
if ((prefix > 0) && (Settings.flag4.awsiot_shadow) && (Mqtt.connected)) { // placeholder for SetOptionXX
// compute the target topic
char *topic = SettingsText(SET_MQTT_TOPIC);
@@ -1350,7 +1350,7 @@ void MqttSaveSettings(void)
#endif
WebGetArg("mc", tmp, sizeof(tmp));
SettingsUpdateText(SET_MQTT_CLIENT, (!strlen(tmp)) ? MQTT_CLIENT_ID : tmp);
-#if defined(USE_MQTT_TLS) && defined(USE_MQTT_AWS_IOT)
+#if defined(USE_MQTT_TLS) && (defined(USE_MQTT_AWS_IOT) || defined(USE_MQTT_AWS_IOT_LIGHT))
AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_MQTT D_CMND_MQTTHOST " %s, " D_CMND_MQTTPORT " %d, " D_CMND_MQTTCLIENT " %s, " D_CMND_TOPIC " %s, " D_CMND_FULLTOPIC " %s"),
SettingsText(SET_MQTT_HOST), Settings.mqtt_port, SettingsText(SET_MQTT_CLIENT), SettingsText(SET_MQTT_TOPIC), SettingsText(SET_MQTT_FULLTOPIC));
#else // USE_MQTT_AWS_IOT
From 60aeeb445f4213c61da51b7af2840e0720330878 Mon Sep 17 00:00:00 2001
From: stefanbode
Date: Wed, 9 Sep 2020 08:58:00 +0200
Subject: [PATCH 014/148] Editorial changes
- harmonized variable naming
- add more code comments to help others to understand
- SWITCH/PULSE now defined for each shutter.
---
tasmota/xdrv_27_shutter.ino | 250 ++++++++++++++++++------------------
1 file changed, 128 insertions(+), 122 deletions(-)
diff --git a/tasmota/xdrv_27_shutter.ino b/tasmota/xdrv_27_shutter.ino
index 439800ac2..2fd86464f 100644
--- a/tasmota/xdrv_27_shutter.ino
+++ b/tasmota/xdrv_27_shutter.ino
@@ -30,26 +30,25 @@
#define D_SHUTTER "SHUTTER"
const uint16_t MOTOR_STOP_TIME = 500; // in mS
-const uint16_t RESOLUTION = 1000;
+const uint16_t RESOLUTION = 1000; // incresed to 1000 in 8.5 to ramp servos
const uint8_t STEPS_PER_SECOND = 20; // FUNC_EVERY_50_MSECOND
const uint16_t pwm_max = 500;
const uint16_t pwm_min = 90;
uint8_t calibrate_pos[6] = {0,30,50,70,90,100};
uint16_t messwerte[5] = {30,50,70,90,100};
-uint16_t last_execute_step;
-int32_t max_velocity = 0;
-int32_t max_velocity_change_per_step = 0;
+int32_t velocity_max = 0;
+int32_t velocity_change_per_step_max = 0;
int32_t min_runtime_ms = 0;
-int32_t minstopway = 0;
-int32_t next_possible_stop = 0;
+int32_t current_stop_way = 0;
+int32_t next_possible_stop_position = 0;
int32_t toBeAcc = 0;
const uint8_t MAX_MODES = 7;
-enum ShutterPositionMode {SHT_UNDEF, SHT_TIME, SHT_TIME_UP_DOWN, SHT_TIME_GARAGE, SHT_COUNTER, SHT_PWM_VALUE, SHT_PWM_TIME,};
-enum ShutterSwitchMode {SHT_SWITCH, SHT_PULSE,};
+enum Shutterposition_mode {SHT_UNDEF, SHT_TIME, SHT_TIME_UP_DOWN, SHT_TIME_GARAGE, SHT_COUNTER, SHT_PWM_VALUE, SHT_PWM_TIME,};
+enum Shutterswitch_mode {SHT_SWITCH, SHT_PULSE,};
enum ShutterButtonStates { SHT_NOT_PRESSED, SHT_PRESSED_MULTI, SHT_PRESSED_HOLD, SHT_PRESSED_IMMEDIATE, SHT_PRESSED_EXT_HOLD, SHT_PRESSED_MULTI_SIMULTANEOUS, SHT_PRESSED_HOLD_SIMULTANEOUS, SHT_PRESSED_EXT_HOLD_SIMULTANEOUS,};
const char kShutterCommands[] PROGMEM = D_PRFX_SHUTTER "|"
@@ -74,31 +73,31 @@ void (* const ShutterCommand[])(void) PROGMEM = {
Ticker TickerShutter;
struct SHUTTER {
- power_t mask = 0; // bit mask with 11 at the position of relays that belong to at least ONE shutter
- power_t old_power = 0; // preserve old bitmask for power to extract the relay that changes.
- power_t switched_relay = 0; // bitmatrix that contain the relays that was lastly changed.
- uint32_t time[MAX_SHUTTERS]; // operating time of the shutter in 0.05sec
- int32_t open_max[MAX_SHUTTERS]; // max value on maximum open calculated
- int32_t target_position[MAX_SHUTTERS]; // position to go to
- int32_t start_position[MAX_SHUTTERS]; // position before a movement is started. init at start
- int32_t real_position[MAX_SHUTTERS]; // value between 0 and Shutter.open_max
- uint16_t open_time[MAX_SHUTTERS]; // duration to open the shutter. 112 = 11.2sec
- uint16_t close_time[MAX_SHUTTERS]; // duration to close the shutter. 112 = 11.2sec
- uint16_t close_velocity[MAX_SHUTTERS]; // in relation to open velocity. higher value = faster
- int8_t direction[MAX_SHUTTERS]; // 1 == UP , 0 == stop; -1 == down
- int8_t lastdirection[MAX_SHUTTERS]; // last direction (1 == UP , -1 == down)
- uint8_t PositionMode = 0; // how to calculate actual position: SHT_TIME, SHT_COUNTER, SHT_PWM_VALUE, SHT_PWM_TIME
- uint8_t SwitchMode = 0; // how to switch relays: SHT_SWITCH, SHT_PULSE
- int16_t motordelay[MAX_SHUTTERS]; // initial motorstarttime in 0.05sec.
- int16_t pwm_velocity[MAX_SHUTTERS]; // frequency of PWN for stepper motors or PWM duty cycle change for PWM servo
- uint16_t pwm_value[MAX_SHUTTERS]; // dutyload of PWM 0..1023 on ESP8266
- uint16_t pwm_min[MAX_SHUTTERS]; // dutyload of PWM 0..1023 on ESP8266
- uint16_t pwm_max[MAX_SHUTTERS]; // dutyload of PWM 0..1023 on ESP8266
- uint16_t max_pwm_velocity = 1000; // maximum of PWM frequency for openig the shutter. depend on the motor and drivers
- uint16_t max_close_pwm_velocity[MAX_SHUTTERS];// maximum of PWM frequency for closeing the shutter. depend on the motor and drivers
+ power_t RelayShutterMask = 0; // bit mask with 11 at the position of relays that belong to at least ONE shutter
+ power_t RelayOldMask = 0; // bitmatrix that contain the last known state of all relays. Required to detemine the manual changed relay.
+ power_t RelayCurrentMask = 0; // bitmatrix that contain the current state of all relays
+ uint32_t time[MAX_SHUTTERS]; // operating time of the shutter in 0.05sec
+ int32_t open_max[MAX_SHUTTERS]; // max value on maximum open calculated
+ int32_t target_position[MAX_SHUTTERS]; // position to go to
+ int32_t start_position[MAX_SHUTTERS]; // position before a movement is started. init at start
+ int32_t real_position[MAX_SHUTTERS]; // value between 0 and Shutter.open_max
+ uint16_t open_time[MAX_SHUTTERS]; // duration to open the shutter. 112 = 11.2sec
+ uint16_t close_time[MAX_SHUTTERS]; // duration to close the shutter. 112 = 11.2sec
+ uint16_t close_velocity[MAX_SHUTTERS]; // in relation to open velocity. higher value = faster
+ int8_t direction[MAX_SHUTTERS]; // 1 == UP , 0 == stop; -1 == down
+ int8_t lastdirection[MAX_SHUTTERS]; // last direction (1 == UP , -1 == down)
+ uint8_t position_mode=0; // how to calculate actual position: SHT_TIME, SHT_COUNTER, SHT_PWM_VALUE, SHT_PWM_TIME
+ uint8_t switch_mode[MAX_SHUTTERS]; // how to switch relays: SHT_SWITCH, SHT_PULSE
+ int16_t motordelay[MAX_SHUTTERS]; // initial motorstarttime in 0.05sec. Also uses for ramp at steppers and servos
+ int16_t pwm_velocity[MAX_SHUTTERS]; // frequency of PWN for stepper motors or PWM duty cycle change for PWM servo
+ uint16_t pwm_value[MAX_SHUTTERS]; // dutyload of PWM 0..1023 on ESP8266
+ uint16_t pwm_min[MAX_SHUTTERS]; // dutyload of PWM 0..1023 on ESP8266
+ uint16_t pwm_max[MAX_SHUTTERS]; // dutyload of PWM 0..1023 on ESP8266
+ uint16_t open_velocity_max = 1000; // maximum of PWM change during opening. Defines velocity on opening. Steppers and Servos only
+ uint16_t close_velocity_max[MAX_SHUTTERS]; // maximum of PWM change during closeing. Defines velocity on opening. Steppers and Servos only
uint8_t skip_relay_change; // avoid overrun at endstops
- int32_t accelerator[MAX_SHUTTERS]; // speed of ramp-up, ramp down of shutter
- uint8_t start_reported = 0;
+ int32_t accelerator[MAX_SHUTTERS]; // speed of ramp-up, ramp down of shutters with velocity control. Steppers and Servos only
+ uint8_t start_reported = 0; // indicates of the shutter start was reported through MQTT JSON
} Shutter;
#define SHT_DIV_ROUND(__A, __B) (((__A) + (__B)/2) / (__B))
@@ -113,24 +112,28 @@ void ShutterLogPos(uint32_t i)
void ExecuteCommandPowerShutter(uint32_t device, uint32_t state, uint32_t source)
{
+ // first implementation for virtual relays. Avoid switching relay numbers that do not exist.
if (device <= devices_present) ExecuteCommandPower(device,state,source);
}
void ShutterUpdateVelocity(uint8_t i)
{
+ // No Logging allowed. Part of RTC Timer
+ // will be calles through RTC every 50ms.
Shutter.pwm_velocity[i] += Shutter.accelerator[i];
- Shutter.pwm_velocity[i] = tmax(0,tmin(Shutter.direction[i]==1 ? Shutter.max_pwm_velocity : Shutter.max_close_pwm_velocity[i],Shutter.pwm_velocity[i]));
+ Shutter.pwm_velocity[i] = tmax(0,tmin(Shutter.direction[i]==1 ? Shutter.open_velocity_max : Shutter.close_velocity_max[i],Shutter.pwm_velocity[i]));
}
void ShutterRtc50mS(void)
{
+ // No Logging allowed. RTC Timer
for (uint8_t i = 0; i < shutters_present; i++) {
if (Shutter.direction[i]) {
// update position data before increasing counter
Shutter.real_position[i] = ShutterCalculatePosition(i);
Shutter.time[i]++;
ShutterCalculateAccelerator(i);
- switch (Shutter.PositionMode) {
+ switch (Shutter.position_mode) {
case SHT_PWM_VALUE:
ShutterUpdateVelocity(i);
Shutter.real_position[i] += Shutter.direction[i] > 0 ? Shutter.pwm_velocity[i] : (Shutter.direction[i] < 0 ? -Shutter.pwm_velocity[i] : 0);
@@ -215,14 +218,14 @@ uint8_t ShutterRealToPercentPosition(int32_t realpos, uint32_t index)
void ShutterInit(void)
{
shutters_present = 0;
- Shutter.mask = 0;
+ Shutter.RelayShutterMask = 0;
//Initialize to get relay that changed
- Shutter.old_power = power;
- bool relay_in_interlock = false;
+ Shutter.RelayOldMask = power;
+
// if shutter 4 is unused
if (Settings.shutter_startrelay[MAX_SHUTTERS -1] == 0) {
- Shutter.max_pwm_velocity = Settings.shuttercoeff[4][3] > 0 ? Settings.shuttercoeff[4][3] : Shutter.max_pwm_velocity;
+ Shutter.open_velocity_max = Settings.shuttercoeff[4][3] > 0 ? Settings.shuttercoeff[4][3] : Shutter.open_velocity_max;
}
for (uint32_t i = 0; i < MAX_SHUTTERS; i++) {
// set startrelay to 1 on first init, but only to shutter 1. 90% usecase
@@ -230,48 +233,54 @@ void ShutterInit(void)
if (Settings.shutter_startrelay[i] && (Settings.shutter_startrelay[i] < 9)) {
shutters_present++;
- // Determine shutter types
- Shutter.mask |= 3 << (Settings.shutter_startrelay[i] -1) ;
+ // Add the two relays to the mask to knaw they belong to shutters
+ Shutter.RelayShutterMask |= 3 << (Settings.shutter_startrelay[i] -1) ;
- for (uint32_t j = 0; j < MAX_INTERLOCKS * Settings.flag.interlock; j++) { // CMND_INTERLOCK - Enable/disable interlock
- //AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT: Interlock state i=%d %d, flag %d, , shuttermask %d, maskedIL %d"),i, Settings.interlock[i], Settings.flag.interlock,Shutter.mask, Settings.interlock[i]&Shutter.mask);
- if (Settings.interlock[j] && (Settings.interlock[j] & Shutter.mask)) {
- //AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT: Relay in Interlock group"));
- relay_in_interlock = true;
- }
- }
+ // All shutters must have same mode. Switch OR Pulse. N
switch (Settings.pulse_timer[i]) {
case 0:
- Shutter.SwitchMode = SHT_SWITCH;
+ Shutter.switch_mode[i] = SHT_SWITCH;
break;
default:
- Shutter.SwitchMode = SHT_PULSE;
+ Shutter.switch_mode[i] = SHT_PULSE;
break;
}
if (Settings.shutter_mode == SHT_UNDEF) {
+ bool relay_in_interlock = false;
AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: mode undef.. calculate..."));
+
+ for (uint32_t j = 0; j < MAX_INTERLOCKS * Settings.flag.interlock; j++) { // CMND_INTERLOCK - Enable/disable interlock
+ //AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT: Interlock state i=%d %d, flag %d, , shuttermask %d, maskedIL %d"),i, Settings.interlock[i], Settings.flag.interlock,Shutter.RelayShutterMask, Settings.interlock[i]&Shutter.RelayShutterMask);
+ if (Settings.interlock[j] && (Settings.interlock[j] & Shutter.RelayShutterMask)) {
+ //AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT: Relay in Interlock group"));
+ relay_in_interlock = true;
+ }
+ }
+
switch (Settings.pulse_timer[i+1]) {
case 0:
- Shutter.PositionMode = SHT_TIME_GARAGE;
+ Shutter.position_mode = SHT_TIME_GARAGE;
break;
default:
if (relay_in_interlock) {
- Shutter.PositionMode = SHT_TIME;
+ Shutter.position_mode = SHT_TIME;
} else {
- Shutter.PositionMode = SHT_TIME_UP_DOWN;
+ Shutter.position_mode = SHT_TIME_UP_DOWN;
if (PinUsed(GPIO_PWM1, i) && PinUsed(GPIO_CNTR1, i)) {
- Shutter.PositionMode = SHT_COUNTER;
+ Shutter.position_mode = SHT_COUNTER;
}
}
break;
}
} else {
- Shutter.PositionMode = Settings.shutter_mode;
+ Shutter.position_mode = Settings.shutter_mode;
}
+ // main function for stepper and servos to control velocity and acceleration.
TickerShutter.attach_ms(50, ShutterRtc50mS );
+
// default the 50 percent should not have any impact without changing it. set to 60
Settings.shutter_set50percent[i] = (Settings.shutter_set50percent[i] > 0) ? Settings.shutter_set50percent[i] : 50;
@@ -293,7 +302,7 @@ void ShutterInit(void)
Settings.shuttercoeff[0][i] = Shutter.open_max[i] - (Settings.shuttercoeff[1][i] * 100);
Settings.shuttercoeff[2][i] = (Settings.shuttercoeff[0][i] + 5 * Settings.shuttercoeff[1][i]) / 5;
}
- Shutter.mask |= 3 << (Settings.shutter_startrelay[i] -1);
+ Shutter.RelayShutterMask |= 3 << (Settings.shutter_startrelay[i] -1);
Shutter.real_position[i] = ShutterPercentToRealPosition(Settings.shutter_position[i], i);
@@ -301,14 +310,14 @@ void ShutterInit(void)
Shutter.motordelay[i] = Settings.shutter_motordelay[i];
Shutter.lastdirection[i] = (50 < Settings.shutter_position[i]) ? 1 : -1;
- switch (Shutter.PositionMode) {
+ switch (Shutter.position_mode) {
case SHT_PWM_VALUE:
- Shutter.max_pwm_velocity = RESOLUTION;
+ Shutter.open_velocity_max = RESOLUTION;
break;
}
- Shutter.max_close_pwm_velocity[i] = Shutter.max_pwm_velocity*Shutter.open_time[i] / Shutter.close_time[i];
+ Shutter.close_velocity_max[i] = Shutter.open_velocity_max*Shutter.open_time[i] / Shutter.close_time[i];
- //AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Shutter %d Openvel %d, Closevel: %d"),i, Shutter.max_pwm_velocity, Shutter.max_close_pwm_velocity[i]);
+ //AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Shutter %d Openvel %d, Closevel: %d"),i, Shutter.open_velocity_max, Shutter.close_velocity_max[i]);
AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT%d: Init. Pos: %d,inverted %d, locked %d, end stop time enabled %d, webButtons inverted %d"),
i+1, Shutter.real_position[i],
(Settings.shutter_options[i]&1) ? 1 : 0, (Settings.shutter_options[i]&2) ? 1 : 0, (Settings.shutter_options[i]&4) ? 1 : 0, (Settings.shutter_options[i]&8) ? 1 : 0);
@@ -319,7 +328,7 @@ void ShutterInit(void)
}
ShutterLimitRealAndTargetPositions(i);
Settings.shutter_accuracy = 1;
- Settings.shutter_mode = Shutter.PositionMode;
+ Settings.shutter_mode = Shutter.position_mode;
}
}
@@ -362,31 +371,27 @@ void ShutterLimitRealAndTargetPositions(uint32_t i) {
void ShutterCalculateAccelerator(uint8_t i)
{
+ // No Logging allowed. Part of RTC Timer
if (Shutter.direction[i] != 0) {
- switch (Shutter.PositionMode) {
+ switch (Shutter.position_mode) {
case SHT_COUNTER:
case SHT_PWM_VALUE:
// calculate max velocity allowed in this direction
- max_velocity = Shutter.direction[i] == 1 ? Shutter.max_pwm_velocity : Shutter.max_close_pwm_velocity[i];
+ velocity_max = Shutter.direction[i] == 1 ? Shutter.open_velocity_max : Shutter.close_velocity_max[i];
// calculate max change of velocyty based on the defined motordelay in steps
- max_velocity_change_per_step = max_velocity / (Shutter.motordelay[i]>0 ? Shutter.motordelay[i] : 1);
+ velocity_change_per_step_max = velocity_max / (Shutter.motordelay[i]>0 ? Shutter.motordelay[i] : 1);
// minimumtime required from current velocity to stop
- min_runtime_ms = Shutter.pwm_velocity[i] * 1000 / STEPS_PER_SECOND / max_velocity_change_per_step;
+ min_runtime_ms = Shutter.pwm_velocity[i] * 1000 / STEPS_PER_SECOND / velocity_change_per_step_max;
// decellartion way from current velocity
- minstopway = (min_runtime_ms * (Shutter.pwm_velocity[i]+max_velocity_change_per_step)/100 - Shutter.pwm_velocity[i])*RESOLUTION/Shutter.max_pwm_velocity * Shutter.direction[i] ;
- next_possible_stop = Shutter.real_position[i] + minstopway ;
+ current_stop_way = (min_runtime_ms * (Shutter.pwm_velocity[i]+velocity_change_per_step_max)/100 - Shutter.pwm_velocity[i])*RESOLUTION/Shutter.open_velocity_max * Shutter.direction[i] ;
+ next_possible_stop_position = Shutter.real_position[i] + current_stop_way ;
toBeAcc = 0;
- // ensure that accelerator kicks in IN TIME and that STOP procedure kicks in at least ONE step before reach end position.
- //Shutter.accelerator[i] = tmin(tmax(max_velocity_change_per_step*(100-(Shutter.direction[i]*(Shutter.target_position[i]-next_possible_stop) ))/2000 , max_velocity_change_per_step*9/200), max_velocity_change_per_step*11/200);
- //int32_t act_freq_change = max_velocity_change_per_step/20;
-
// ensure that the accelerotor kicks in at least one step BEFORE it is to late and a hard stop required.
- if (Shutter.accelerator[i] < 0 || (next_possible_stop * Shutter.direction[i]) +RESOLUTION*Shutter.pwm_velocity[i]/Shutter.max_pwm_velocity>= Shutter.target_position[i] * Shutter.direction[i] ) {
- // 10 times the deviation is the value of this simple p-regulator
- toBeAcc = 100+(Shutter.direction[i]*(next_possible_stop-Shutter.target_position[i])*max_velocity/Shutter.pwm_velocity[i]*10/RESOLUTION);
- Shutter.accelerator[i] = - tmin(tmax( max_velocity_change_per_step*toBeAcc/100 , (max_velocity_change_per_step*9/10)), (max_velocity_change_per_step*11/10));
- //AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Ramp down: acc: %d"), Shutter.accelerator[i]);
- } else if ( Shutter.accelerator[i] > 0 && Shutter.pwm_velocity[i] == max_velocity) {
+ if (Shutter.accelerator[i] < 0 || (next_possible_stop_position * Shutter.direction[i]) +RESOLUTION*Shutter.pwm_velocity[i]/Shutter.open_velocity_max>= Shutter.target_position[i] * Shutter.direction[i] ) {
+ // 10 times the deviation is the p-value of this simple p-regulator
+ toBeAcc = 100+(Shutter.direction[i]*(next_possible_stop_position-Shutter.target_position[i])*velocity_max/Shutter.pwm_velocity[i]*10/RESOLUTION);
+ Shutter.accelerator[i] = - tmin(tmax( velocity_change_per_step_max*toBeAcc/100 , (velocity_change_per_step_max*9/10)), (velocity_change_per_step_max*11/10));
+ } else if ( Shutter.accelerator[i] > 0 && Shutter.pwm_velocity[i] == velocity_max) {
Shutter.accelerator[i] = 0;
}
break;
@@ -396,19 +401,19 @@ void ShutterCalculateAccelerator(uint8_t i)
void ShutterDecellerateForStop(uint8_t i)
{
- switch (Shutter.PositionMode) {
+ switch (Shutter.position_mode) {
case SHT_PWM_VALUE:
case SHT_COUNTER:
int16_t missing_steps;
- Shutter.accelerator[i] = -(Shutter.max_pwm_velocity / (Shutter.motordelay[i]>0 ? Shutter.motordelay[i] : 1) *11/10);
+ Shutter.accelerator[i] = -(Shutter.open_velocity_max / (Shutter.motordelay[i]>0 ? Shutter.motordelay[i] : 1) *11/10);
while (Shutter.pwm_velocity[i] > -2*Shutter.accelerator[i] ) {
AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: velocity: %ld, delta: %d"), Shutter.pwm_velocity[i], Shutter.accelerator[i] );
//Shutter.pwm_velocity[i] = tmax(Shutter.pwm_velocity[i]-Shutter.accelerator[i] , 0);
// Control will be done in RTC Ticker.
delay(50);
}
- if (Shutter.PositionMode == SHT_COUNTER){
- missing_steps = ((Shutter.target_position[i]-Shutter.start_position[i])*Shutter.direction[i]*Shutter.max_pwm_velocity/RESOLUTION/STEPS_PER_SECOND) - RtcSettings.pulse_counter[i];
+ if (Shutter.position_mode == SHT_COUNTER){
+ missing_steps = ((Shutter.target_position[i]-Shutter.start_position[i])*Shutter.direction[i]*Shutter.open_velocity_max/RESOLUTION/STEPS_PER_SECOND) - RtcSettings.pulse_counter[i];
//prepare for stop PWM
AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Remain steps %d, counter %d, freq %d"), missing_steps, RtcSettings.pulse_counter[i] ,Shutter.pwm_velocity[i]);
Shutter.accelerator[i] = 0;
@@ -417,7 +422,7 @@ void ShutterDecellerateForStop(uint8_t i)
analogWrite(Pin(GPIO_PWM1, i), 50);
Shutter.pwm_velocity[i] = 0;
analogWriteFreq(Shutter.pwm_velocity[i]);
- while (RtcSettings.pulse_counter[i] < (uint32_t)(Shutter.target_position[i]-Shutter.start_position[i])*Shutter.direction[i]*Shutter.max_pwm_velocity/RESOLUTION/STEPS_PER_SECOND) {
+ while (RtcSettings.pulse_counter[i] < (uint32_t)(Shutter.target_position[i]-Shutter.start_position[i])*Shutter.direction[i]*Shutter.open_velocity_max/RESOLUTION/STEPS_PER_SECOND) {
delay(1);
}
analogWrite(Pin(GPIO_PWM1, i), 0); // removed with 8.3 because of reset caused by watchog
@@ -438,7 +443,7 @@ void ShutterPowerOff(uint8_t i) {
Shutter.direction[i] = 0;
delay(MOTOR_STOP_TIME);
}
- switch (Shutter.SwitchMode) {
+ switch (Shutter.switch_mode[i]) {
case SHT_SWITCH:
if ((1 << (Settings.shutter_startrelay[i]-1)) & power) {
ExecuteCommandPowerShutter(Settings.shutter_startrelay[i], 0, SRC_SHUTTER);
@@ -448,7 +453,7 @@ void ShutterPowerOff(uint8_t i) {
}
break;
case SHT_PULSE:
- uint8_t cur_relay = Settings.shutter_startrelay[i] + (Shutter.direction[i] == 1 ? 0 : (uint8_t)(Shutter.PositionMode == SHT_TIME)) ;
+ uint8_t cur_relay = Settings.shutter_startrelay[i] + (Shutter.direction[i] == 1 ? 0 : (uint8_t)(Shutter.position_mode == SHT_TIME)) ;
// we have a momentary switch here. Needs additional pulse on same relay after the end
if ((SRC_PULSETIMER == last_source || SRC_SHUTTER == last_source || SRC_WEBGUI == last_source)) {
ExecuteCommandPowerShutter(cur_relay, 1, SRC_SHUTTER);
@@ -462,7 +467,7 @@ void ShutterPowerOff(uint8_t i) {
break;
}
// Store current PWM value to ensure proper position after reboot.
- switch (Shutter.PositionMode) {
+ switch (Shutter.position_mode) {
case SHT_PWM_VALUE:
char scmnd[20];
snprintf_P(scmnd, sizeof(scmnd), PSTR(D_CMND_PWM " %d" ),Shutter.pwm_value[i]);
@@ -489,11 +494,11 @@ void ShutterUpdatePosition(void)
Shutter.start_reported = 1;
}
//ShutterCalculateAccelerator(i);
- AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: time: %d, toBeAcc %d, minstopway %d,cur_vel %d, max_vel %d, act_vel_change %d, min_runtime_ms %d, act.pos %d, next_stop %d, target: %d, max_vel_change_per_step %d"),Shutter.time[i],toBeAcc,minstopway,
- Shutter.pwm_velocity[i],max_velocity, Shutter.accelerator[i],min_runtime_ms,Shutter.real_position[i], next_possible_stop,Shutter.target_position[i],max_velocity_change_per_step);
+ AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: time: %d, toBeAcc %d, current_stop_way %d,vel_vur %d, vel_max %d, act_vel_change %d, min_runtime_ms %d, act.pos %d, next_stop %d, target: %d, velocity_change_per_step_max %d"),Shutter.time[i],toBeAcc,current_stop_way,
+ Shutter.pwm_velocity[i],velocity_max, Shutter.accelerator[i],min_runtime_ms,Shutter.real_position[i], next_possible_stop_position,Shutter.target_position[i],velocity_change_per_step_max);
- if ( Shutter.real_position[i] * Shutter.direction[i] >= Shutter.target_position[i] * Shutter.direction[i] || Shutter.pwm_velocity[i]= Shutter.target_position[i] * Shutter.direction[i] || Shutter.pwm_velocity[i]0 ? Shutter.motordelay[i] : 1);
+ Shutter.accelerator[i] = Shutter.open_velocity_max / (Shutter.motordelay[i]>0 ? Shutter.motordelay[i] : 1);
Shutter.target_position[i] = target_pos;
Shutter.start_position[i] = Shutter.real_position[i];
Shutter.time[i] = 0;
@@ -551,7 +556,7 @@ void ShutterStartInit(uint32_t i, int32_t direction, int32_t target_pos)
rules_flag.shutter_moving = 1;
rules_flag.shutter_moved = 0;
Shutter.start_reported = 0;
- //AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: real %d, start %d, counter %d, max_freq %d, dir %d, freq %d"),Shutter.real_position[i], Shutter.start_position[i] ,RtcSettings.pulse_counter[i],Shutter.max_pwm_velocity , Shutter.direction[i] ,Shutter.max_pwm_velocity );
+ //AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: real %d, start %d, counter %d,freq_max %d, dir %d, freq %d"),Shutter.real_position[i], Shutter.start_position[i] ,RtcSettings.pulse_counter[i],Shutter.open_velocity_max , Shutter.direction[i] ,Shutter.open_velocity_max );
}
//AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT: Start shutter: %d from %d to %d in direction %d"), i, Shutter.start_position[i], Shutter.target_position[i], Shutter.direction[i]);
}
@@ -559,10 +564,11 @@ void ShutterStartInit(uint32_t i, int32_t direction, int32_t target_pos)
int32_t ShutterCalculatePosition(uint32_t i)
{
+ // No Logging allowed. Part of RTC Timer
if (Shutter.direction[i] != 0) {
- switch (Shutter.PositionMode) {
+ switch (Shutter.position_mode) {
case SHT_COUNTER:
- return ((int32_t)RtcSettings.pulse_counter[i]*Shutter.direction[i]*STEPS_PER_SECOND*RESOLUTION / Shutter.max_pwm_velocity)+Shutter.start_position[i];
+ return ((int32_t)RtcSettings.pulse_counter[i]*Shutter.direction[i]*STEPS_PER_SECOND*RESOLUTION / Shutter.open_velocity_max)+Shutter.start_position[i];
break;
case SHT_TIME:
case SHT_TIME_UP_DOWN:
@@ -585,7 +591,7 @@ int32_t ShutterCalculatePosition(uint32_t i)
void ShutterRelayChanged(void)
{
- // Shutter.switched_relay = binary relay that was recently changed and cause an Action
+ // Shutter.RelayCurrentMask = binary relay that was recently changed and cause an Action
// powerstate_local = binary powermatrix and relays from shutter: 0..3
// relays_changed = bool if one of the relays that belong to the shutter changed not by shutter or pulsetimer
char stemp1[10];
@@ -593,26 +599,26 @@ void ShutterRelayChanged(void)
for (uint32_t i = 0; i < shutters_present; i++) {
power_t powerstate_local = (power >> (Settings.shutter_startrelay[i] -1)) & 3;
// SRC_IGNORE added because INTERLOCK function bite causes this as last source for changing the relay.
- //uint8 manual_relays_changed = ((Shutter.switched_relay >> (Settings.shutter_startrelay[i] -1)) & 3) && SRC_IGNORE != last_source && SRC_SHUTTER != last_source && SRC_PULSETIMER != last_source ;
- uint8 manual_relays_changed = ((Shutter.switched_relay >> (Settings.shutter_startrelay[i] -1)) & 3) && SRC_SHUTTER != last_source && SRC_PULSETIMER != last_source ;
- AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Shutter %d: source: %s, powerstate_local %ld, Shutter.switched_relay %d, manual change %d"), i+1, GetTextIndexed(stemp1, sizeof(stemp1), last_source, kCommandSource), powerstate_local,Shutter.switched_relay,manual_relays_changed);
+ //uint8 manual_relays_changed = ((Shutter.RelayCurrentMask >> (Settings.shutter_startrelay[i] -1)) & 3) && SRC_IGNORE != last_source && SRC_SHUTTER != last_source && SRC_PULSETIMER != last_source ;
+ uint8 manual_relays_changed = ((Shutter.RelayCurrentMask >> (Settings.shutter_startrelay[i] -1)) & 3) && SRC_SHUTTER != last_source && SRC_PULSETIMER != last_source ;
+ AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Shutter %d: source: %s, powerstate_local %ld, Shutter.RelayCurrentMask %d, manual change %d"), i+1, GetTextIndexed(stemp1, sizeof(stemp1), last_source, kCommandSource), powerstate_local,Shutter.RelayCurrentMask,manual_relays_changed);
if (manual_relays_changed) {
//Shutter.skip_relay_change = true;
ShutterLimitRealAndTargetPositions(i);
- switch (Shutter.SwitchMode ) {
+ switch (Shutter.switch_mode[i] ) {
case SHT_PULSE:
if (Shutter.direction[i] != 0 && powerstate_local) {
Shutter.target_position[i] = Shutter.real_position[i];
powerstate_local = 0;
- AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Shutter %d: Switch OFF motor. Target: %ld, source: %s, powerstate_local %ld, Shutter.switched_relay %d, manual change %d"), i+1, Shutter.target_position[i], GetTextIndexed(stemp1, sizeof(stemp1), last_source, kCommandSource), powerstate_local,Shutter.switched_relay,manual_relays_changed);
+ AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Shutter %d: Switch OFF motor. Target: %ld, source: %s, powerstate_local %ld, Shutter.RelayCurrentMask %d, manual change %d"), i+1, Shutter.target_position[i], GetTextIndexed(stemp1, sizeof(stemp1), last_source, kCommandSource), powerstate_local,Shutter.RelayCurrentMask,manual_relays_changed);
}
break;
default:
last_source = SRC_SHUTTER; // avoid switch off in the next loop
if (Shutter.direction[i] != 0 ) ShutterPowerOff(i);
}
- switch (Shutter.PositionMode) {
- // enum ShutterPositionMode {SHT_TIME, SHT_TIME_UP_DOWN, SHT_TIME_GARAGE, SHT_COUNTER, SHT_PWM_VALUE, SHT_PWM_TIME,};
+ switch (Shutter.position_mode) {
+ // enum Shutterposition_mode {SHT_TIME, SHT_TIME_UP_DOWN, SHT_TIME_GARAGE, SHT_COUNTER, SHT_PWM_VALUE, SHT_PWM_TIME,};
case SHT_TIME_UP_DOWN:
case SHT_COUNTER:
case SHT_PWM_VALUE:
@@ -654,7 +660,7 @@ void ShutterRelayChanged(void)
}
- } // switch (Shutter.PositionMode)
+ } // switch (Shutter.position_mode)
AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Shutter %d: Target: %ld, powerstatelocal %d"), i+1, Shutter.target_position[i], powerstate_local);
} // if (manual_relays_changed)
} // for (uint32_t i = 0; i < shutters_present; i++)
@@ -1034,7 +1040,7 @@ void CmndShutterPosition(void)
if (XdrvMailbox.payload != -99) {
//target_pos_percent = (Settings.shutter_options[index] & 1) ? 100 - target_pos_percent : target_pos_percent;
Shutter.target_position[index] = ShutterPercentToRealPosition(target_pos_percent, index);
- //Shutter.accelerator[index] = Shutter.max_pwm_velocity / ((Shutter.motordelay[index] > 0) ? Shutter.motordelay[index] : 1);
+ //Shutter.accelerator[index] = Shutter.open_velocity_max / ((Shutter.motordelay[index] > 0) ? Shutter.motordelay[index] : 1);
//Shutter.target_position[index] = XdrvMailbox.payload < 5 ? Settings.shuttercoeff[2][index] * XdrvMailbox.payload : Settings.shuttercoeff[1][index] * XdrvMailbox.payload + Settings.shuttercoeff[0,index];
AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: lastsource %d:, real %d, target %d, payload %d"), last_source, Shutter.real_position[index] ,Shutter.target_position[index],target_pos_percent);
}
@@ -1049,7 +1055,7 @@ void CmndShutterPosition(void)
}
if (Shutter.direction[index] != new_shutterdirection) {
ShutterStartInit(index, new_shutterdirection, Shutter.target_position[index]);
- switch (Shutter.PositionMode) {
+ switch (Shutter.position_mode) {
case SHT_COUNTER:
case SHT_PWM_TIME:
case SHT_PWM_VALUE:
@@ -1060,12 +1066,12 @@ void CmndShutterPosition(void)
// power on
ExecuteCommandPowerShutter(Settings.shutter_startrelay[index], 1, SRC_SHUTTER);
}
- if (Shutter.PositionMode != SHT_TIME_UP_DOWN) ExecuteCommandPowerShutter(Settings.shutter_startrelay[index]+2, 1, SRC_SHUTTER);
+ if (Shutter.position_mode != SHT_TIME_UP_DOWN) ExecuteCommandPowerShutter(Settings.shutter_startrelay[index]+2, 1, SRC_SHUTTER);
break;
case SHT_TIME:
if (!Shutter.skip_relay_change) {
if ( (power >> (Settings.shutter_startrelay[index] -1)) & 3 > 0 ) {
- ExecuteCommandPowerShutter(Settings.shutter_startrelay[index] + (new_shutterdirection == 1 ? 1 : 0), Shutter.SwitchMode == SHT_SWITCH ? 0 : 1, SRC_SHUTTER);
+ ExecuteCommandPowerShutter(Settings.shutter_startrelay[index] + (new_shutterdirection == 1 ? 1 : 0), Shutter.switch_mode[index] == SHT_SWITCH ? 0 : 1, SRC_SHUTTER);
}
ExecuteCommandPowerShutter(Settings.shutter_startrelay[index] + (new_shutterdirection == 1 ? 0 : 1), 1, SRC_SHUTTER);
}
@@ -1073,8 +1079,8 @@ void CmndShutterPosition(void)
case SHT_TIME_GARAGE:
if (!Shutter.skip_relay_change) {
if (new_shutterdirection == Shutter.lastdirection[index]) {
- AddLog_P2(LOG_LEVEL_INFO, PSTR("SHT: Garage not move in this direction: %d"), Shutter.SwitchMode == SHT_PULSE);
- for (uint8_t k=0 ; k <= (uint8_t)(Shutter.SwitchMode == SHT_PULSE) ; k++) {
+ AddLog_P2(LOG_LEVEL_INFO, PSTR("SHT: Garage not move in this direction: %d"), Shutter.switch_mode[index] == SHT_PULSE);
+ for (uint8_t k=0 ; k <= (uint8_t)(Shutter.switch_mode[index] == SHT_PULSE) ; k++) {
ExecuteCommandPowerShutter(Settings.shutter_startrelay[index], 1, SRC_SHUTTER);
delay(500);
ExecuteCommandPowerShutter(Settings.shutter_startrelay[index], 0, SRC_SHUTTER);
@@ -1086,8 +1092,8 @@ void CmndShutterPosition(void)
ExecuteCommandPowerShutter(Settings.shutter_startrelay[index], 1, SRC_SHUTTER);
} // if (!Shutter.skip_relay_change)
break;
- } // switch (Shutter.PositionMode)
- Shutter.switched_relay = 0;
+ } // switch (Shutter.position_mode)
+ Shutter.RelayCurrentMask = 0;
} // if (Shutter.direction[index] != new_shutterdirection)
} else {
target_pos_percent = ShutterRealToPercentPosition(Shutter.real_position[index], index);
@@ -1158,12 +1164,12 @@ void CmndShutterMotorDelay(void)
void CmndShutterMode(void)
{
if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= MAX_MODES)) {
- Shutter.PositionMode = XdrvMailbox.payload;
+ Shutter.position_mode = XdrvMailbox.payload;
Settings.shutter_mode = XdrvMailbox.payload;
ShutterInit();
ResponseCmndNumber(XdrvMailbox.payload); // ????
} else {
- ResponseCmndNumber(Shutter.PositionMode);
+ ResponseCmndNumber(Shutter.position_mode);
}
}
@@ -1173,9 +1179,9 @@ void CmndShutterRelay(void)
if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 64)) {
Settings.shutter_startrelay[XdrvMailbox.index -1] = XdrvMailbox.payload;
if (XdrvMailbox.payload > 0) {
- Shutter.mask |= 3 << (XdrvMailbox.payload - 1);
+ Shutter.RelayShutterMask |= 3 << (XdrvMailbox.payload - 1);
} else {
- Shutter.mask ^= 3 << (Settings.shutter_startrelay[XdrvMailbox.index -1] - 1);
+ Shutter.RelayShutterMask ^= 3 << (Settings.shutter_startrelay[XdrvMailbox.index -1] - 1);
}
Settings.shutter_startrelay[XdrvMailbox.index -1] = XdrvMailbox.payload;
ShutterInit();
@@ -1336,14 +1342,14 @@ void CmndShutterSetHalfway(void)
void CmndShutterFrequency(void)
{
if ((XdrvMailbox.payload > 0) && (XdrvMailbox.payload <= 20000)) {
- Shutter.max_pwm_velocity = XdrvMailbox.payload;
+ Shutter.open_velocity_max = XdrvMailbox.payload;
if (shutters_present < 4) {
- Settings.shuttercoeff[4][3] = Shutter.max_pwm_velocity;
+ Settings.shuttercoeff[4][3] = Shutter.open_velocity_max;
}
ShutterInit();
ResponseCmndNumber(XdrvMailbox.payload); // ????
} else {
- ResponseCmndNumber(Shutter.max_pwm_velocity);
+ ResponseCmndNumber(Shutter.open_velocity_max);
}
}
@@ -1487,19 +1493,19 @@ bool Xdrv27(uint8_t function)
case FUNC_SET_POWER:
char stemp1[10];
// extract the number of the relay that was switched and save for later in Update Position.
- Shutter.switched_relay = XdrvMailbox.index ^ Shutter.old_power;
- AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Switched relay: %d by %s"), Shutter.switched_relay,GetTextIndexed(stemp1, sizeof(stemp1), last_source, kCommandSource));
+ Shutter.RelayCurrentMask = XdrvMailbox.index ^ Shutter.RelayOldMask;
+ AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Switched relay: %d by %s"), Shutter.RelayCurrentMask,GetTextIndexed(stemp1, sizeof(stemp1), last_source, kCommandSource));
ShutterRelayChanged();
- Shutter.old_power = XdrvMailbox.index;
+ Shutter.RelayOldMask = XdrvMailbox.index;
break;
case FUNC_SET_DEVICE_POWER:
if (Shutter.skip_relay_change ) {
uint8_t i;
for (i = 0; i < devices_present; i++) {
- if (Shutter.switched_relay &1) {
+ if (Shutter.RelayCurrentMask &1) {
break;
}
- Shutter.switched_relay >>= 1;
+ Shutter.RelayCurrentMask >>= 1;
}
//AddLog_P2(LOG_LEVEL_ERROR, PSTR("SHT: skip relay change: %d"),i+1);
result = true;
From 021ec06553c35e54f4c1d955bbfa3958d8b996e3 Mon Sep 17 00:00:00 2001
From: Theo Arends <11044339+arendst@users.noreply.github.com>
Date: Wed, 9 Sep 2020 09:40:28 +0200
Subject: [PATCH 015/148] Update settings.h
---
tasmota/settings.h | 7 ++-----
1 file changed, 2 insertions(+), 5 deletions(-)
diff --git a/tasmota/settings.h b/tasmota/settings.h
index 3ca80ecd6..048b61754 100644
--- a/tasmota/settings.h
+++ b/tasmota/settings.h
@@ -609,13 +609,10 @@ struct {
uint8_t ledpwm_off; // F40
uint8_t tcp_baudrate; // F41
uint8_t fallback_module; // F42
-
- uint8_t free_f43[1]; // F43
-
+ uint8_t shutter_mode; // F43
uint16_t energy_power_delta[3]; // F44
- uint8_t shutter_mode; // F45
- uint8_t free_f4e[105]; // F4A - Decrement if adding new Setting variables just above and below
+ uint8_t free_f4a[106]; // F4A - Decrement if adding new Setting variables just above and below
// Only 32 bit boundary variables below
SysBitfield5 flag5; // FB4
From 9f26068b7d01d4a48f327097abeb5a2365deffcc Mon Sep 17 00:00:00 2001
From: Theo Arends <11044339+arendst@users.noreply.github.com>
Date: Wed, 9 Sep 2020 10:49:04 +0200
Subject: [PATCH 016/148] Fix HLW8012 related total energy counters
Fix HLW8012 related total energy counters (#9263, #9266)
---
tasmota/xnrg_01_hlw8012.ino | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/tasmota/xnrg_01_hlw8012.ino b/tasmota/xnrg_01_hlw8012.ino
index 40d3e4185..dfc536672 100644
--- a/tasmota/xnrg_01_hlw8012.ino
+++ b/tasmota/xnrg_01_hlw8012.ino
@@ -208,7 +208,7 @@ void HlwEverySecond(void)
hlw_len = 10000 * 1000 / Hlw.energy_period_counter; // Add *1000 to fix rounding on loads at 3.6kW (#9160)
Hlw.energy_period_counter = 0;
if (hlw_len) {
- Energy.kWhtoday_delta += ((Hlw.power_ratio * Settings.energy_power_calibration) * 1000 / hlw_len) / 36;
+ Energy.kWhtoday_delta += (((Hlw.power_ratio * Settings.energy_power_calibration) / 36) * 1000) / hlw_len;
EnergyUpdateToday();
}
}
From 0eb078a5cb072d53c46aacce0aa2793cdba89801 Mon Sep 17 00:00:00 2001
From: Theo Arends <11044339+arendst@users.noreply.github.com>
Date: Wed, 9 Sep 2020 10:56:18 +0200
Subject: [PATCH 017/148] Final fix
---
tasmota/xnrg_01_hlw8012.ino | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/tasmota/xnrg_01_hlw8012.ino b/tasmota/xnrg_01_hlw8012.ino
index dfc536672..4e73f0926 100644
--- a/tasmota/xnrg_01_hlw8012.ino
+++ b/tasmota/xnrg_01_hlw8012.ino
@@ -205,10 +205,10 @@ void HlwEverySecond(void)
unsigned long hlw_len;
if (Hlw.energy_period_counter) {
- hlw_len = 10000 * 1000 / Hlw.energy_period_counter; // Add *1000 to fix rounding on loads at 3.6kW (#9160)
+ hlw_len = 10000 * 100 / Hlw.energy_period_counter; // Add *100 to fix rounding on loads at 3.6kW (#9160)
Hlw.energy_period_counter = 0;
if (hlw_len) {
- Energy.kWhtoday_delta += (((Hlw.power_ratio * Settings.energy_power_calibration) / 36) * 1000) / hlw_len;
+ Energy.kWhtoday_delta += (((Hlw.power_ratio * Settings.energy_power_calibration) / 36) * 100) / hlw_len;
EnergyUpdateToday();
}
}
From 05c153c7b131d7b604dc0a0c2b5553970c112046 Mon Sep 17 00:00:00 2001
From: Theo Arends <11044339+arendst@users.noreply.github.com>
Date: Wed, 9 Sep 2020 11:22:34 +0200
Subject: [PATCH 018/148] Update change log
---
RELEASENOTES.md | 5 +++++
tasmota/CHANGELOG.md | 3 +++
2 files changed, 8 insertions(+)
diff --git a/RELEASENOTES.md b/RELEASENOTES.md
index 8c95d47b5..db8706750 100644
--- a/RELEASENOTES.md
+++ b/RELEASENOTES.md
@@ -54,3 +54,8 @@ The following binary downloads have been compiled with ESP8266/Arduino library c
## Changelog
### Version 8.5.0.1
+
+- Fix energy total counters (#9263, #9266)
+- Fix crash in ``ZbRestore``
+- Add new shutter modes (#9244)
+- Add ``#define USE_MQTT_AWS_IOT_LIGHT`` for password based AWS IoT authentication
diff --git a/tasmota/CHANGELOG.md b/tasmota/CHANGELOG.md
index d9c9b1bfc..faaa2ae17 100644
--- a/tasmota/CHANGELOG.md
+++ b/tasmota/CHANGELOG.md
@@ -4,6 +4,9 @@
### 8.5.0.1 20200907
+- Fix energy total counters (#9263, #9266)
+- Fix crash in ``ZbRestore``
+- Add new shutter modes (#9244)
- Add ``#define USE_MQTT_AWS_IOT_LIGHT`` for password based AWS IoT authentication
### 8.5.0 20200907
From 0e5c1c31ef69602302a8c90b80131a3dc9883c91 Mon Sep 17 00:00:00 2001
From: Theo Arends <11044339+arendst@users.noreply.github.com>
Date: Wed, 9 Sep 2020 12:44:30 +0200
Subject: [PATCH 019/148] Update RELEASENOTES.md
---
RELEASENOTES.md | 2 ++
1 file changed, 2 insertions(+)
diff --git a/RELEASENOTES.md b/RELEASENOTES.md
index db8706750..796435d03 100644
--- a/RELEASENOTES.md
+++ b/RELEASENOTES.md
@@ -47,6 +47,8 @@ The following binary downloads have been compiled with ESP8266/Arduino library c
- **tasmota-zbbridge.bin** = The dedicated Sonoff Zigbee Bridge version.
- **tasmota-minimal.bin** = The Minimal version allows intermediate OTA uploads to support larger versions and does NOT change any persistent parameter. This version **should NOT be used for initial installation**.
+Binaries for ESP8266 based devices can be downloaded from http://ota.tasmota.com/tasmota/release. Binaries for ESP32 based devices can be downloaded from http://ota.tasmota.com/tasmota32/release. The base links can be used for OTA upgrades like ``OtaUrl http://ota.tasmota.com/tasmota/release/tasmota.bin``
+
[List](MODULES.md) of embedded modules.
[Complete list](BUILDS.md) of available feature and sensors.
From 43c9705349a0682bb6b80846ab4f4dd676d1fb55 Mon Sep 17 00:00:00 2001
From: stefanbode
Date: Wed, 9 Sep 2020 14:04:57 +0200
Subject: [PATCH 020/148] continues optimization
---
tasmota/xdrv_27_shutter.ino | 568 +++++++++++++++++-------------------
1 file changed, 270 insertions(+), 298 deletions(-)
diff --git a/tasmota/xdrv_27_shutter.ino b/tasmota/xdrv_27_shutter.ino
index 2fd86464f..6b0ef98e7 100644
--- a/tasmota/xdrv_27_shutter.ino
+++ b/tasmota/xdrv_27_shutter.ino
@@ -1,5 +1,5 @@
/*
- xdrv_27_shutter.ino - Shutter/Blind support for Tasmota
+ xdrv_27_Shutter[i].ino - Shutter/Blind support for Tasmota
Copyright (C) 2020 Stefan Bode
@@ -31,7 +31,7 @@
const uint16_t MOTOR_STOP_TIME = 500; // in mS
const uint16_t RESOLUTION = 1000; // incresed to 1000 in 8.5 to ramp servos
-const uint8_t STEPS_PER_SECOND = 20; // FUNC_EVERY_50_MSECOND
+const uint8_t STEPS_PER_SECOND = 20; // FUNC_EVERY_50_MSECOND
const uint16_t pwm_max = 500;
const uint16_t pwm_min = 90;
@@ -73,41 +73,44 @@ void (* const ShutterCommand[])(void) PROGMEM = {
Ticker TickerShutter;
struct SHUTTER {
- power_t RelayShutterMask = 0; // bit mask with 11 at the position of relays that belong to at least ONE shutter
- power_t RelayOldMask = 0; // bitmatrix that contain the last known state of all relays. Required to detemine the manual changed relay.
- power_t RelayCurrentMask = 0; // bitmatrix that contain the current state of all relays
- uint32_t time[MAX_SHUTTERS]; // operating time of the shutter in 0.05sec
- int32_t open_max[MAX_SHUTTERS]; // max value on maximum open calculated
- int32_t target_position[MAX_SHUTTERS]; // position to go to
- int32_t start_position[MAX_SHUTTERS]; // position before a movement is started. init at start
- int32_t real_position[MAX_SHUTTERS]; // value between 0 and Shutter.open_max
- uint16_t open_time[MAX_SHUTTERS]; // duration to open the shutter. 112 = 11.2sec
- uint16_t close_time[MAX_SHUTTERS]; // duration to close the shutter. 112 = 11.2sec
- uint16_t close_velocity[MAX_SHUTTERS]; // in relation to open velocity. higher value = faster
- int8_t direction[MAX_SHUTTERS]; // 1 == UP , 0 == stop; -1 == down
- int8_t lastdirection[MAX_SHUTTERS]; // last direction (1 == UP , -1 == down)
- uint8_t position_mode=0; // how to calculate actual position: SHT_TIME, SHT_COUNTER, SHT_PWM_VALUE, SHT_PWM_TIME
- uint8_t switch_mode[MAX_SHUTTERS]; // how to switch relays: SHT_SWITCH, SHT_PULSE
- int16_t motordelay[MAX_SHUTTERS]; // initial motorstarttime in 0.05sec. Also uses for ramp at steppers and servos
- int16_t pwm_velocity[MAX_SHUTTERS]; // frequency of PWN for stepper motors or PWM duty cycle change for PWM servo
- uint16_t pwm_value[MAX_SHUTTERS]; // dutyload of PWM 0..1023 on ESP8266
- uint16_t pwm_min[MAX_SHUTTERS]; // dutyload of PWM 0..1023 on ESP8266
- uint16_t pwm_max[MAX_SHUTTERS]; // dutyload of PWM 0..1023 on ESP8266
+ uint32_t time; // operating time of the shutter in 0.05sec
+ int32_t open_max; // max value on maximum open calculated
+ int32_t target_position; // position to go to
+ int32_t start_position; // position before a movement is started. init at start
+ int32_t real_position; // value between 0 and Shutter[i].open_max
+ uint16_t open_time; // duration to open the Shutter[i]. 112 = 11.2sec
+ uint16_t close_time; // duration to close the Shutter[i]. 112 = 11.2sec
+ uint16_t close_velocity; // in relation to open velocity. higher value = faster
+ int8_t direction; // 1 == UP , 0 == stop; -1 == down
+ int8_t lastdirection; // last direction (1 == UP , -1 == down)
+ uint8_t switch_mode; // how to switch relays: SHT_SWITCH, SHT_PULSE
+ int16_t motordelay; // initial motorstarttime in 0.05sec. Also uses for ramp at steppers and servos
+ int16_t pwm_velocity; // frequency of PWN for stepper motors or PWM duty cycle change for PWM servo
+ uint16_t pwm_value; // dutyload of PWM 0..1023 on ESP8266
+ uint16_t pwm_min; // dutyload of PWM 0..1023 on ESP8266
+ uint16_t pwm_max; // dutyload of PWM 0..1023 on ESP8266
+ uint16_t close_velocity_max; // maximum of PWM change during closeing. Defines velocity on opening. Steppers and Servos only
+ int32_t accelerator; // speed of ramp-up, ramp down of shutters with velocity control. Steppers and Servos only
+} Shutter[MAX_SHUTTERS];
+
+struct SHUTTERGLOBAL {
+ power_t RelayShutterMask = 0; // bit mask with 11 at the position of relays that belong to at least ONE shutter
+ power_t RelayOldMask = 0; // bitmatrix that contain the last known state of all relays. Required to detemine the manual changed relay.
+ power_t RelayCurrentMask = 0; // bitmatrix that contain the current state of all relays
+ uint8_t position_mode = 0; // how to calculate actual position: SHT_TIME, SHT_COUNTER, SHT_PWM_VALUE, SHT_PWM_TIME
+ uint8_t skip_relay_change; // avoid overrun at endstops
+ uint8_t start_reported = 0; // indicates of the shutter start was reported through MQTT JSON
uint16_t open_velocity_max = 1000; // maximum of PWM change during opening. Defines velocity on opening. Steppers and Servos only
- uint16_t close_velocity_max[MAX_SHUTTERS]; // maximum of PWM change during closeing. Defines velocity on opening. Steppers and Servos only
- uint8_t skip_relay_change; // avoid overrun at endstops
- int32_t accelerator[MAX_SHUTTERS]; // speed of ramp-up, ramp down of shutters with velocity control. Steppers and Servos only
- uint8_t start_reported = 0; // indicates of the shutter start was reported through MQTT JSON
-} Shutter;
+} ShutterGlobal;
#define SHT_DIV_ROUND(__A, __B) (((__A) + (__B)/2) / (__B))
void ShutterLogPos(uint32_t i)
{
char stemp2[10];
- dtostrfd((float)Shutter.time[i] / STEPS_PER_SECOND, 2, stemp2);
+ dtostrfd((float)Shutter[i].time / STEPS_PER_SECOND, 2, stemp2);
AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT: Shutter %d Real %d, Start %d, Stop %d, Dir %d, Delay %d, Rtc %s [s], Freq %d, PWM %d"),
- i+1, Shutter.real_position[i], Shutter.start_position[i], Shutter.target_position[i], Shutter.direction[i], Shutter.motordelay[i], stemp2, Shutter.pwm_velocity[i], Shutter.pwm_value[i]);
+ i+1, Shutter[i].real_position, Shutter[i].start_position, Shutter[i].target_position, Shutter[i].direction, Shutter[i].motordelay, stemp2, Shutter[i].pwm_velocity, Shutter[i].pwm_value);
}
void ExecuteCommandPowerShutter(uint32_t device, uint32_t state, uint32_t source)
@@ -120,37 +123,37 @@ void ShutterUpdateVelocity(uint8_t i)
{
// No Logging allowed. Part of RTC Timer
// will be calles through RTC every 50ms.
- Shutter.pwm_velocity[i] += Shutter.accelerator[i];
- Shutter.pwm_velocity[i] = tmax(0,tmin(Shutter.direction[i]==1 ? Shutter.open_velocity_max : Shutter.close_velocity_max[i],Shutter.pwm_velocity[i]));
+ Shutter[i].pwm_velocity += Shutter[i].accelerator;
+ Shutter[i].pwm_velocity = tmax(0,tmin(Shutter[i].direction==1 ? ShutterGlobal.open_velocity_max : Shutter[i].close_velocity_max,Shutter[i].pwm_velocity));
}
void ShutterRtc50mS(void)
{
// No Logging allowed. RTC Timer
for (uint8_t i = 0; i < shutters_present; i++) {
- if (Shutter.direction[i]) {
+ if (Shutter[i].direction) {
// update position data before increasing counter
- Shutter.real_position[i] = ShutterCalculatePosition(i);
- Shutter.time[i]++;
+ Shutter[i].real_position = ShutterCalculatePosition(i);
+ Shutter[i].time++;
ShutterCalculateAccelerator(i);
- switch (Shutter.position_mode) {
+ switch (ShutterGlobal.position_mode) {
case SHT_PWM_VALUE:
ShutterUpdateVelocity(i);
- Shutter.real_position[i] += Shutter.direction[i] > 0 ? Shutter.pwm_velocity[i] : (Shutter.direction[i] < 0 ? -Shutter.pwm_velocity[i] : 0);
- Shutter.pwm_value[i] = SHT_DIV_ROUND((Shutter.pwm_max[i]-Shutter.pwm_min[i]) * Shutter.real_position[i] , Shutter.open_max[i])+Shutter.pwm_min[i];
- analogWrite(Pin(GPIO_PWM1, i), Shutter.pwm_value[i]);
+ Shutter[i].real_position += Shutter[i].direction > 0 ? Shutter[i].pwm_velocity : (Shutter[i].direction < 0 ? -Shutter[i].pwm_velocity : 0);
+ Shutter[i].pwm_value = SHT_DIV_ROUND((Shutter[i].pwm_max-Shutter[i].pwm_min) * Shutter[i].real_position , Shutter[i].open_max)+Shutter[i].pwm_min;
+ analogWrite(Pin(GPIO_PWM1, i), Shutter[i].pwm_value);
break;
case SHT_COUNTER:
- if (Shutter.accelerator[i]) {
- //AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: accelerator i=%d -> %d"),i, Shutter.accelerator[i]);
+ if (Shutter[i].accelerator) {
+ //AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: accelerator i=%d -> %d"),i, Shutter[i].accelerator);
ShutterUpdateVelocity(i);
- analogWriteFreq(Shutter.pwm_velocity[i]);
+ analogWriteFreq(Shutter[i].pwm_velocity);
analogWrite(Pin(GPIO_PWM1, i), 50);
}
break;
}
- } // if (Shutter.direction[i])
+ } // if (Shutter[i].direction)
}
}
@@ -169,17 +172,17 @@ int32_t ShutterPercentToRealPosition(uint32_t percent, uint32_t index)
}
}
}
- for (uint32_t i = 0; i < 5; i++) {
- if ((percent * 10) >= Settings.shuttercoeff[i][index]) {
- realpos = SHT_DIV_ROUND(Shutter.open_max[index] * calibrate_pos[i+1], 100);
+ for (uint32_t k = 0; k < 5; k++) {
+ if ((percent * 10) >= Settings.shuttercoeff[k][index]) {
+ realpos = SHT_DIV_ROUND(Shutter[index].open_max * calibrate_pos[k+1], 100);
//AddLog_P2(LOG_LEVEL_DEBUG, PSTR("Realposition TEMP1: %d, %% %d, coeff %d"), realpos, percent, Settings.shuttercoeff[i][index]);
} else {
- if (0 == i) {
- realpos = SHT_DIV_ROUND(SHT_DIV_ROUND(percent * Shutter.open_max[index] * calibrate_pos[i+1], Settings.shuttercoeff[i][index]), 10);
+ if (0 == k) {
+ realpos = SHT_DIV_ROUND(SHT_DIV_ROUND(percent * Shutter[index].open_max * calibrate_pos[k+1], Settings.shuttercoeff[k][index]), 10);
} else {
//uint16_t addon = ( percent*10 - Settings.shuttercoeff[i-1][index] ) * Shutter_Open_Max[index] * (calibrate_pos[i+1] - calibrate_pos[i]) / (Settings.shuttercoeff[i][index] -Settings.shuttercoeff[i-1][index]) / 100;
//AddLog_P2(LOG_LEVEL_DEBUG, PSTR("Realposition TEMP2: %d, %% %d, coeff %d"), addon, (calibrate_pos[i+1] - calibrate_pos[i]), (Settings.shuttercoeff[i][index] -Settings.shuttercoeff[i-1][index]));
- realpos += SHT_DIV_ROUND(SHT_DIV_ROUND((percent*10 - Settings.shuttercoeff[i-1][index] ) * Shutter.open_max[index] * (calibrate_pos[i+1] - calibrate_pos[i]), Settings.shuttercoeff[i][index] - Settings.shuttercoeff[i-1][index]), 100);
+ realpos += SHT_DIV_ROUND(SHT_DIV_ROUND((percent*10 - Settings.shuttercoeff[k-1][index] ) * Shutter[index].open_max * (calibrate_pos[k+1] - calibrate_pos[k]), Settings.shuttercoeff[k][index] - Settings.shuttercoeff[k-1][index]), 100);
}
break;
}
@@ -195,18 +198,18 @@ uint8_t ShutterRealToPercentPosition(int32_t realpos, uint32_t index)
} else {
uint16_t realpercent;
- for (uint32_t i = 0; i < 5; i++) {
- if (realpos >= Shutter.open_max[index] * calibrate_pos[i+1] / 100) {
- realpercent = SHT_DIV_ROUND(Settings.shuttercoeff[i][index], 10);
+ for (uint32_t j = 0; j < 5; j++) {
+ if (realpos >= Shutter[index].open_max * calibrate_pos[j+1] / 100) {
+ realpercent = SHT_DIV_ROUND(Settings.shuttercoeff[j][index], 10);
//AddLog_P2(LOG_LEVEL_DEBUG, PSTR("Realpercent TEMP1: %d, %% %d, coeff %d"), realpercent, realpos, Shutter_Open_Max[index] * calibrate_pos[i+1] / 100);
} else {
- if (0 == i) {
- realpercent = SHT_DIV_ROUND(SHT_DIV_ROUND((realpos - SHT_DIV_ROUND(Shutter.open_max[index] * calibrate_pos[i], 100)) * 10 * Settings.shuttercoeff[i][index], calibrate_pos[i+1]), Shutter.open_max[index]);
+ if (0 == j) {
+ realpercent = SHT_DIV_ROUND(SHT_DIV_ROUND((realpos - SHT_DIV_ROUND(Shutter[index].open_max * calibrate_pos[j], 100)) * 10 * Settings.shuttercoeff[j][index], calibrate_pos[j+1]), Shutter[index].open_max);
} else {
//uint16_t addon = ( realpos - (Shutter_Open_Max[index] * calibrate_pos[i] / 100) ) * 10 * (Settings.shuttercoeff[i][index] - Settings.shuttercoeff[i-1][index]) / (calibrate_pos[i+1] - calibrate_pos[i])/ Shutter_Open_Max[index];
//uint16_t addon = ( percent*10 - Settings.shuttercoeff[i-1][index] ) * Shutter_Open_Max[index] * (calibrate_pos[i+1] - calibrate_pos[i]) / (Settings.shuttercoeff[i][index] -Settings.shuttercoeff[i-1][index]) / 100;
//AddLog_P2(LOG_LEVEL_DEBUG, PSTR("Realpercent TEMP2: %d, delta %d, %% %d, coeff %d"), addon,( realpos - (Shutter_Open_Max[index] * calibrate_pos[i] / 100) ) , (calibrate_pos[i+1] - calibrate_pos[i])* Shutter_Open_Max[index]/100, (Settings.shuttercoeff[i][index] -Settings.shuttercoeff[i-1][index]));
- realpercent += SHT_DIV_ROUND(SHT_DIV_ROUND((realpos - SHT_DIV_ROUND(Shutter.open_max[index] * calibrate_pos[i], 100)) * 10 * (Settings.shuttercoeff[i][index] - Settings.shuttercoeff[i-1][index]), (calibrate_pos[i+1] - calibrate_pos[i])), Shutter.open_max[index]) ;
+ realpercent += SHT_DIV_ROUND(SHT_DIV_ROUND((realpos - SHT_DIV_ROUND(Shutter[index].open_max * calibrate_pos[j], 100)) * 10 * (Settings.shuttercoeff[j][index] - Settings.shuttercoeff[j-1][index]), (calibrate_pos[j+1] - calibrate_pos[j])), Shutter[index].open_max) ;
}
break;
}
@@ -218,14 +221,14 @@ uint8_t ShutterRealToPercentPosition(int32_t realpos, uint32_t index)
void ShutterInit(void)
{
shutters_present = 0;
- Shutter.RelayShutterMask = 0;
+ ShutterGlobal.RelayShutterMask = 0;
//Initialize to get relay that changed
- Shutter.RelayOldMask = power;
+ ShutterGlobal.RelayOldMask = power;
// if shutter 4 is unused
if (Settings.shutter_startrelay[MAX_SHUTTERS -1] == 0) {
- Shutter.open_velocity_max = Settings.shuttercoeff[4][3] > 0 ? Settings.shuttercoeff[4][3] : Shutter.open_velocity_max;
+ ShutterGlobal.open_velocity_max = Settings.shuttercoeff[4][3] > 0 ? Settings.shuttercoeff[4][3] : ShutterGlobal.open_velocity_max;
}
for (uint32_t i = 0; i < MAX_SHUTTERS; i++) {
// set startrelay to 1 on first init, but only to shutter 1. 90% usecase
@@ -234,15 +237,15 @@ void ShutterInit(void)
shutters_present++;
// Add the two relays to the mask to knaw they belong to shutters
- Shutter.RelayShutterMask |= 3 << (Settings.shutter_startrelay[i] -1) ;
+ ShutterGlobal.RelayShutterMask |= 3 << (Settings.shutter_startrelay[i] -1) ;
// All shutters must have same mode. Switch OR Pulse. N
switch (Settings.pulse_timer[i]) {
case 0:
- Shutter.switch_mode[i] = SHT_SWITCH;
+ Shutter[i].switch_mode = SHT_SWITCH;
break;
default:
- Shutter.switch_mode[i] = SHT_PULSE;
+ Shutter[i].switch_mode = SHT_PULSE;
break;
}
@@ -251,31 +254,24 @@ void ShutterInit(void)
AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: mode undef.. calculate..."));
for (uint32_t j = 0; j < MAX_INTERLOCKS * Settings.flag.interlock; j++) { // CMND_INTERLOCK - Enable/disable interlock
- //AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT: Interlock state i=%d %d, flag %d, , shuttermask %d, maskedIL %d"),i, Settings.interlock[i], Settings.flag.interlock,Shutter.RelayShutterMask, Settings.interlock[i]&Shutter.RelayShutterMask);
- if (Settings.interlock[j] && (Settings.interlock[j] & Shutter.RelayShutterMask)) {
+ //AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT: Interlock state i=%d %d, flag %d, , shuttermask %d, maskedIL %d"),i, Settings.interlock[i], Settings.flag.interlock,ShutterGlobal.RelayShutterMask, Settings.interlock[i]&ShutterGlobal.RelayShutterMask);
+ if (Settings.interlock[j] && (Settings.interlock[j] & ShutterGlobal.RelayShutterMask)) {
//AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT: Relay in Interlock group"));
relay_in_interlock = true;
}
}
- switch (Settings.pulse_timer[i+1]) {
- case 0:
- Shutter.position_mode = SHT_TIME_GARAGE;
- break;
- default:
- if (relay_in_interlock) {
- Shutter.position_mode = SHT_TIME;
- } else {
- Shutter.position_mode = SHT_TIME_UP_DOWN;
- if (PinUsed(GPIO_PWM1, i) && PinUsed(GPIO_CNTR1, i)) {
- Shutter.position_mode = SHT_COUNTER;
- }
- }
-
- break;
+ if (relay_in_interlock) {
+ ShutterGlobal.position_mode = SHT_TIME;
+ } else {
+ ShutterGlobal.position_mode = SHT_TIME_UP_DOWN;
+ if (PinUsed(GPIO_PWM1, i) && PinUsed(GPIO_CNTR1, i)) {
+ ShutterGlobal.position_mode = SHT_COUNTER;
+ }
}
+
} else {
- Shutter.position_mode = Settings.shutter_mode;
+ ShutterGlobal.position_mode = Settings.shutter_mode;
}
// main function for stepper and servos to control velocity and acceleration.
@@ -285,50 +281,50 @@ void ShutterInit(void)
Settings.shutter_set50percent[i] = (Settings.shutter_set50percent[i] > 0) ? Settings.shutter_set50percent[i] : 50;
// use 10 sec. as default to allow everybody to play without deep initialize
- Shutter.open_time[i] = (Settings.shutter_opentime[i] > 0) ? Settings.shutter_opentime[i] : 100;
- Shutter.close_time[i] = (Settings.shutter_closetime[i] > 0) ? Settings.shutter_closetime[i] : 100;
+ Shutter[i].open_time = Settings.shutter_opentime[i] = (Settings.shutter_opentime[i] > 0) ? Settings.shutter_opentime[i] : 100;
+ Shutter[i].close_time = Settings.shutter_closetime[i] = (Settings.shutter_closetime[i] > 0) ? Settings.shutter_closetime[i] : 100;
//temporary hard coded.
- Shutter.pwm_min[i] = pwm_min;
- Shutter.pwm_max[i] = pwm_max;
+ Shutter[i].pwm_min = pwm_min;
+ Shutter[i].pwm_max = pwm_max;
// Update Calculation 20 because time interval is 0.05 sec ans time is in 0.1sec
- Shutter.open_max[i] = STEPS_PER_SECOND * RESOLUTION * Shutter.open_time[i] / 10;
- Shutter.close_velocity[i] = Shutter.open_max[i] / Shutter.close_time[i] / 2 ;
+ Shutter[i].open_max = STEPS_PER_SECOND * RESOLUTION * Shutter[i].open_time / 10;
+ Shutter[i].close_velocity = Shutter[i].open_max / Shutter[i].close_time / 2 ;
// calculate a ramp slope at the first 5 percent to compensate that shutters move with down part later than the upper part
if (Settings.shutter_set50percent[i] != 50) {
- Settings.shuttercoeff[1][i] = Shutter.open_max[i] * (100 - Settings.shutter_set50percent[i] ) / 5000;
- Settings.shuttercoeff[0][i] = Shutter.open_max[i] - (Settings.shuttercoeff[1][i] * 100);
+ Settings.shuttercoeff[1][i] = Shutter[i].open_max * (100 - Settings.shutter_set50percent[i] ) / 5000;
+ Settings.shuttercoeff[0][i] = Shutter[i].open_max - (Settings.shuttercoeff[1][i] * 100);
Settings.shuttercoeff[2][i] = (Settings.shuttercoeff[0][i] + 5 * Settings.shuttercoeff[1][i]) / 5;
}
- Shutter.RelayShutterMask |= 3 << (Settings.shutter_startrelay[i] -1);
+ ShutterGlobal.RelayShutterMask |= 3 << (Settings.shutter_startrelay[i] -1);
- Shutter.real_position[i] = ShutterPercentToRealPosition(Settings.shutter_position[i], i);
+ Shutter[i].real_position = ShutterPercentToRealPosition(Settings.shutter_position[i], i);
- Shutter.start_position[i] = Shutter.target_position[i] = Shutter.real_position[i];
- Shutter.motordelay[i] = Settings.shutter_motordelay[i];
- Shutter.lastdirection[i] = (50 < Settings.shutter_position[i]) ? 1 : -1;
+ Shutter[i].start_position = Shutter[i].target_position = Shutter[i].real_position;
+ Shutter[i].motordelay = Settings.shutter_motordelay[i];
+ Shutter[i].lastdirection = (50 < Settings.shutter_position[i]) ? 1 : -1;
- switch (Shutter.position_mode) {
+ switch (ShutterGlobal.position_mode) {
case SHT_PWM_VALUE:
- Shutter.open_velocity_max = RESOLUTION;
+ ShutterGlobal.open_velocity_max = RESOLUTION;
break;
}
- Shutter.close_velocity_max[i] = Shutter.open_velocity_max*Shutter.open_time[i] / Shutter.close_time[i];
+ Shutter[i].close_velocity_max = ShutterGlobal.open_velocity_max*Shutter[i].open_time / Shutter[i].close_time;
- //AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Shutter %d Openvel %d, Closevel: %d"),i, Shutter.open_velocity_max, Shutter.close_velocity_max[i]);
+ //AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Shutter %d Openvel %d, Closevel: %d"),i, ShutterGlobal.open_velocity_max, Shutter[i].close_velocity_max);
AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT%d: Init. Pos: %d,inverted %d, locked %d, end stop time enabled %d, webButtons inverted %d"),
- i+1, Shutter.real_position[i],
+ i+1, Shutter[i].real_position,
(Settings.shutter_options[i]&1) ? 1 : 0, (Settings.shutter_options[i]&2) ? 1 : 0, (Settings.shutter_options[i]&4) ? 1 : 0, (Settings.shutter_options[i]&8) ? 1 : 0);
} else {
- // terminate loop at first INVALID shutter.
+ // terminate loop at first INVALID Shutter[i].
break;
}
ShutterLimitRealAndTargetPositions(i);
Settings.shutter_accuracy = 1;
- Settings.shutter_mode = Shutter.position_mode;
+
}
}
@@ -343,15 +339,15 @@ void ShutterReportPosition(bool always, uint32_t index)
n = index+1;
}
for (i; i < n; i++) {
- //AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT: Shutter %d: Real Pos: %d"), i+1,Shutter.real_position[i]);
- uint32_t position = ShutterRealToPercentPosition(Shutter.real_position[i], i);
- if (Shutter.direction[i] != 0) {
+ //AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT: Shutter %d: Real Pos: %d"), i+1,Shutter[i].real_position);
+ uint32_t position = ShutterRealToPercentPosition(Shutter[i].real_position, i);
+ if (Shutter[i].direction != 0) {
rules_flag.shutter_moving = 1;
ShutterLogPos(i);
}
if (i && index == MAX_SHUTTERS) { ResponseAppend_P(PSTR(",")); }
- uint32_t target = ShutterRealToPercentPosition(Shutter.target_position[i], i);
- ResponseAppend_P(JSON_SHUTTER_POS, i+1, (Settings.shutter_options[i] & 1) ? 100-position : position, Shutter.direction[i],(Settings.shutter_options[i] & 1) ? 100-target : target );
+ uint32_t target = ShutterRealToPercentPosition(Shutter[i].target_position, i);
+ ResponseAppend_P(JSON_SHUTTER_POS, i+1, (Settings.shutter_options[i] & 1) ? 100-position : position, Shutter[i].direction,(Settings.shutter_options[i] & 1) ? 100-target : target );
}
ResponseJsonEnd();
if (always || (rules_flag.shutter_moving)) {
@@ -363,36 +359,36 @@ void ShutterReportPosition(bool always, uint32_t index)
}
void ShutterLimitRealAndTargetPositions(uint32_t i) {
- if (Shutter.real_position[i]<0) Shutter.real_position[i] = 0;
- if (Shutter.real_position[i]>Shutter.open_max[i]) Shutter.real_position[i] = Shutter.open_max[i];
- if (Shutter.target_position[i]<0) Shutter.target_position[i] = 0;
- if (Shutter.target_position[i]>Shutter.open_max[i]) Shutter.target_position[i] = Shutter.open_max[i];
+ if (Shutter[i].real_position<0) Shutter[i].real_position = 0;
+ if (Shutter[i].real_position>Shutter[i].open_max) Shutter[i].real_position = Shutter[i].open_max;
+ if (Shutter[i].target_position<0) Shutter[i].target_position = 0;
+ if (Shutter[i].target_position>Shutter[i].open_max) Shutter[i].target_position = Shutter[i].open_max;
}
void ShutterCalculateAccelerator(uint8_t i)
{
// No Logging allowed. Part of RTC Timer
- if (Shutter.direction[i] != 0) {
- switch (Shutter.position_mode) {
+ if (Shutter[i].direction != 0) {
+ switch (ShutterGlobal.position_mode) {
case SHT_COUNTER:
case SHT_PWM_VALUE:
// calculate max velocity allowed in this direction
- velocity_max = Shutter.direction[i] == 1 ? Shutter.open_velocity_max : Shutter.close_velocity_max[i];
+ velocity_max = Shutter[i].direction == 1 ? ShutterGlobal.open_velocity_max : Shutter[i].close_velocity_max;
// calculate max change of velocyty based on the defined motordelay in steps
- velocity_change_per_step_max = velocity_max / (Shutter.motordelay[i]>0 ? Shutter.motordelay[i] : 1);
+ velocity_change_per_step_max = velocity_max / (Shutter[i].motordelay>0 ? Shutter[i].motordelay : 1);
// minimumtime required from current velocity to stop
- min_runtime_ms = Shutter.pwm_velocity[i] * 1000 / STEPS_PER_SECOND / velocity_change_per_step_max;
+ min_runtime_ms = Shutter[i].pwm_velocity * 1000 / STEPS_PER_SECOND / velocity_change_per_step_max;
// decellartion way from current velocity
- current_stop_way = (min_runtime_ms * (Shutter.pwm_velocity[i]+velocity_change_per_step_max)/100 - Shutter.pwm_velocity[i])*RESOLUTION/Shutter.open_velocity_max * Shutter.direction[i] ;
- next_possible_stop_position = Shutter.real_position[i] + current_stop_way ;
+ current_stop_way = (min_runtime_ms * (Shutter[i].pwm_velocity+velocity_change_per_step_max)/100 - Shutter[i].pwm_velocity)*RESOLUTION/ShutterGlobal.open_velocity_max * Shutter[i].direction ;
+ next_possible_stop_position = Shutter[i].real_position + current_stop_way ;
toBeAcc = 0;
// ensure that the accelerotor kicks in at least one step BEFORE it is to late and a hard stop required.
- if (Shutter.accelerator[i] < 0 || (next_possible_stop_position * Shutter.direction[i]) +RESOLUTION*Shutter.pwm_velocity[i]/Shutter.open_velocity_max>= Shutter.target_position[i] * Shutter.direction[i] ) {
+ if (Shutter[i].accelerator < 0 || (next_possible_stop_position * Shutter[i].direction) +RESOLUTION*Shutter[i].pwm_velocity/ShutterGlobal.open_velocity_max>= Shutter[i].target_position * Shutter[i].direction ) {
// 10 times the deviation is the p-value of this simple p-regulator
- toBeAcc = 100+(Shutter.direction[i]*(next_possible_stop_position-Shutter.target_position[i])*velocity_max/Shutter.pwm_velocity[i]*10/RESOLUTION);
- Shutter.accelerator[i] = - tmin(tmax( velocity_change_per_step_max*toBeAcc/100 , (velocity_change_per_step_max*9/10)), (velocity_change_per_step_max*11/10));
- } else if ( Shutter.accelerator[i] > 0 && Shutter.pwm_velocity[i] == velocity_max) {
- Shutter.accelerator[i] = 0;
+ toBeAcc = 100+(Shutter[i].direction*(next_possible_stop_position-Shutter[i].target_position)*velocity_max/Shutter[i].pwm_velocity*10/RESOLUTION);
+ Shutter[i].accelerator = - tmin(tmax( velocity_change_per_step_max*toBeAcc/100 , (velocity_change_per_step_max*9/10)), (velocity_change_per_step_max*11/10));
+ } else if ( Shutter[i].accelerator > 0 && Shutter[i].pwm_velocity == velocity_max) {
+ Shutter[i].accelerator = 0;
}
break;
}
@@ -401,37 +397,37 @@ void ShutterCalculateAccelerator(uint8_t i)
void ShutterDecellerateForStop(uint8_t i)
{
- switch (Shutter.position_mode) {
+ switch (ShutterGlobal.position_mode) {
case SHT_PWM_VALUE:
case SHT_COUNTER:
int16_t missing_steps;
- Shutter.accelerator[i] = -(Shutter.open_velocity_max / (Shutter.motordelay[i]>0 ? Shutter.motordelay[i] : 1) *11/10);
- while (Shutter.pwm_velocity[i] > -2*Shutter.accelerator[i] ) {
- AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: velocity: %ld, delta: %d"), Shutter.pwm_velocity[i], Shutter.accelerator[i] );
- //Shutter.pwm_velocity[i] = tmax(Shutter.pwm_velocity[i]-Shutter.accelerator[i] , 0);
+ Shutter[i].accelerator = -(ShutterGlobal.open_velocity_max / (Shutter[i].motordelay>0 ? Shutter[i].motordelay : 1) *11/10);
+ while (Shutter[i].pwm_velocity > -2*Shutter[i].accelerator ) {
+ AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: velocity: %ld, delta: %d"), Shutter[i].pwm_velocity, Shutter[i].accelerator );
+ //Shutter[i].pwm_velocity = tmax(Shutter[i].pwm_velocity-Shutter[i].accelerator , 0);
// Control will be done in RTC Ticker.
delay(50);
}
- if (Shutter.position_mode == SHT_COUNTER){
- missing_steps = ((Shutter.target_position[i]-Shutter.start_position[i])*Shutter.direction[i]*Shutter.open_velocity_max/RESOLUTION/STEPS_PER_SECOND) - RtcSettings.pulse_counter[i];
+ if (ShutterGlobal.position_mode == SHT_COUNTER){
+ missing_steps = ((Shutter[i].target_position-Shutter[i].start_position)*Shutter[i].direction*ShutterGlobal.open_velocity_max/RESOLUTION/STEPS_PER_SECOND) - RtcSettings.pulse_counter[i];
//prepare for stop PWM
- AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Remain steps %d, counter %d, freq %d"), missing_steps, RtcSettings.pulse_counter[i] ,Shutter.pwm_velocity[i]);
- Shutter.accelerator[i] = 0;
- Shutter.pwm_velocity[i] = Shutter.pwm_velocity[i] > 250 ? 250 : Shutter.pwm_velocity[i];
- analogWriteFreq(Shutter.pwm_velocity[i]);
+ AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Remain steps %d, counter %d, freq %d"), missing_steps, RtcSettings.pulse_counter[i] ,Shutter[i].pwm_velocity);
+ Shutter[i].accelerator = 0;
+ Shutter[i].pwm_velocity = Shutter[i].pwm_velocity > 250 ? 250 : Shutter[i].pwm_velocity;
+ analogWriteFreq(Shutter[i].pwm_velocity);
analogWrite(Pin(GPIO_PWM1, i), 50);
- Shutter.pwm_velocity[i] = 0;
- analogWriteFreq(Shutter.pwm_velocity[i]);
- while (RtcSettings.pulse_counter[i] < (uint32_t)(Shutter.target_position[i]-Shutter.start_position[i])*Shutter.direction[i]*Shutter.open_velocity_max/RESOLUTION/STEPS_PER_SECOND) {
+ Shutter[i].pwm_velocity = 0;
+ analogWriteFreq(Shutter[i].pwm_velocity);
+ while (RtcSettings.pulse_counter[i] < (uint32_t)(Shutter[i].target_position-Shutter[i].start_position)*Shutter[i].direction*ShutterGlobal.open_velocity_max/RESOLUTION/STEPS_PER_SECOND) {
delay(1);
}
analogWrite(Pin(GPIO_PWM1, i), 0); // removed with 8.3 because of reset caused by watchog
- Shutter.real_position[i] = ShutterCalculatePosition(i);
- AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Real %d, pulsecount %d, start %d"), Shutter.real_position[i],RtcSettings.pulse_counter[i], Shutter.start_position[i]);
+ Shutter[i].real_position = ShutterCalculatePosition(i);
+ AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Real %d, pulsecount %d, start %d"), Shutter[i].real_position,RtcSettings.pulse_counter[i], Shutter[i].start_position);
}
- Shutter.direction[i] = 0;
- Shutter.pwm_velocity[i] = 0;
+ Shutter[i].direction = 0;
+ Shutter[i].pwm_velocity = 0;
break;
}
}
@@ -439,11 +435,11 @@ void ShutterDecellerateForStop(uint8_t i)
void ShutterPowerOff(uint8_t i) {
AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT: Stop Shutter %d .."), i);
ShutterDecellerateForStop(i);
- if (Shutter.direction[i] !=0) {
- Shutter.direction[i] = 0;
+ if (Shutter[i].direction !=0) {
+ Shutter[i].direction = 0;
delay(MOTOR_STOP_TIME);
}
- switch (Shutter.switch_mode[i]) {
+ switch (Shutter[i].switch_mode) {
case SHT_SWITCH:
if ((1 << (Settings.shutter_startrelay[i]-1)) & power) {
ExecuteCommandPowerShutter(Settings.shutter_startrelay[i], 0, SRC_SHUTTER);
@@ -453,7 +449,7 @@ void ShutterPowerOff(uint8_t i) {
}
break;
case SHT_PULSE:
- uint8_t cur_relay = Settings.shutter_startrelay[i] + (Shutter.direction[i] == 1 ? 0 : (uint8_t)(Shutter.position_mode == SHT_TIME)) ;
+ uint8_t cur_relay = Settings.shutter_startrelay[i] + (Shutter[i].direction == 1 ? 0 : (uint8_t)(ShutterGlobal.position_mode == SHT_TIME)) ;
// we have a momentary switch here. Needs additional pulse on same relay after the end
if ((SRC_PULSETIMER == last_source || SRC_SHUTTER == last_source || SRC_WEBGUI == last_source)) {
ExecuteCommandPowerShutter(cur_relay, 1, SRC_SHUTTER);
@@ -467,10 +463,10 @@ void ShutterPowerOff(uint8_t i) {
break;
}
// Store current PWM value to ensure proper position after reboot.
- switch (Shutter.position_mode) {
+ switch (ShutterGlobal.position_mode) {
case SHT_PWM_VALUE:
char scmnd[20];
- snprintf_P(scmnd, sizeof(scmnd), PSTR(D_CMND_PWM " %d" ),Shutter.pwm_value[i]);
+ snprintf_P(scmnd, sizeof(scmnd), PSTR(D_CMND_PWM " %d" ),Shutter[i].pwm_value);
ExecuteCommand(scmnd, SRC_BUTTON);
break;
}
@@ -483,29 +479,29 @@ void ShutterUpdatePosition(void)
char stopic[TOPSZ];
for (uint32_t i = 0; i < shutters_present; i++) {
- if (Shutter.direction[i] != 0) {
+ if (Shutter[i].direction != 0) {
// Calculate position with counter. Much more accurate and no need for motordelay workaround
// adding some steps to stop early
- //Shutter.real_position[i] = ShutterCalculatePosition(i);
- if (!Shutter.start_reported) {
+ //Shutter[i].real_position = ShutterCalculatePosition(i);
+ if (!ShutterGlobal.start_reported) {
ShutterReportPosition(true, i);
XdrvRulesProcess();
- Shutter.start_reported = 1;
+ ShutterGlobal.start_reported = 1;
}
//ShutterCalculateAccelerator(i);
- AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: time: %d, toBeAcc %d, current_stop_way %d,vel_vur %d, vel_max %d, act_vel_change %d, min_runtime_ms %d, act.pos %d, next_stop %d, target: %d, velocity_change_per_step_max %d"),Shutter.time[i],toBeAcc,current_stop_way,
- Shutter.pwm_velocity[i],velocity_max, Shutter.accelerator[i],min_runtime_ms,Shutter.real_position[i], next_possible_stop_position,Shutter.target_position[i],velocity_change_per_step_max);
+ AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: time: %d, toBeAcc %d, current_stop_way %d,vel_vur %d, vel_max %d, act_vel_change %d, min_runtime_ms %d, act.pos %d, next_stop %d, target: %d, velocity_change_per_step_max %d"),Shutter[i].time,toBeAcc,current_stop_way,
+ Shutter[i].pwm_velocity,velocity_max, Shutter[i].accelerator,min_runtime_ms,Shutter[i].real_position, next_possible_stop_position,Shutter[i].target_position,velocity_change_per_step_max);
- if ( Shutter.real_position[i] * Shutter.direction[i] >= Shutter.target_position[i] * Shutter.direction[i] || Shutter.pwm_velocity[i]= Shutter[i].target_position * Shutter[i].direction || Shutter[i].pwm_velocity0 ? Shutter.motordelay[i] : 1);
- Shutter.target_position[i] = target_pos;
- Shutter.start_position[i] = Shutter.real_position[i];
- Shutter.time[i] = 0;
- Shutter.skip_relay_change = 0;
- Shutter.direction[i] = direction;
+ Shutter[i].accelerator = ShutterGlobal.open_velocity_max / (Shutter[i].motordelay>0 ? Shutter[i].motordelay : 1);
+ Shutter[i].target_position = target_pos;
+ Shutter[i].start_position = Shutter[i].real_position;
+ Shutter[i].time = 0;
+ ShutterGlobal.skip_relay_change = 0;
+ Shutter[i].direction = direction;
rules_flag.shutter_moving = 1;
rules_flag.shutter_moved = 0;
- Shutter.start_reported = 0;
- //AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: real %d, start %d, counter %d,freq_max %d, dir %d, freq %d"),Shutter.real_position[i], Shutter.start_position[i] ,RtcSettings.pulse_counter[i],Shutter.open_velocity_max , Shutter.direction[i] ,Shutter.open_velocity_max );
+ ShutterGlobal.start_reported = 0;
+ //AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: real %d, start %d, counter %d,freq_max %d, dir %d, freq %d"),Shutter[i].real_position, Shutter[i].start_position ,RtcSettings.pulse_counter[i],ShutterGlobal.open_velocity_max , Shutter[i].direction ,ShutterGlobal.open_velocity_max );
}
- //AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT: Start shutter: %d from %d to %d in direction %d"), i, Shutter.start_position[i], Shutter.target_position[i], Shutter.direction[i]);
+ //AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT: Start shutter: %d from %d to %d in direction %d"), i, Shutter[i].start_position, Shutter[i].target_position, Shutter[i].direction);
}
-
int32_t ShutterCalculatePosition(uint32_t i)
{
// No Logging allowed. Part of RTC Timer
- if (Shutter.direction[i] != 0) {
- switch (Shutter.position_mode) {
+ if (Shutter[i].direction != 0) {
+ switch (ShutterGlobal.position_mode) {
case SHT_COUNTER:
- return ((int32_t)RtcSettings.pulse_counter[i]*Shutter.direction[i]*STEPS_PER_SECOND*RESOLUTION / Shutter.open_velocity_max)+Shutter.start_position[i];
+ return ((int32_t)RtcSettings.pulse_counter[i]*Shutter[i].direction*STEPS_PER_SECOND*RESOLUTION / ShutterGlobal.open_velocity_max)+Shutter[i].start_position;
break;
case SHT_TIME:
case SHT_TIME_UP_DOWN:
case SHT_TIME_GARAGE:
- return Shutter.start_position[i] + ( (Shutter.time[i] - Shutter.motordelay[i]) * (Shutter.direction[i] > 0 ? RESOLUTION : -Shutter.close_velocity[i]));
+ return Shutter[i].start_position + ( (Shutter[i].time - Shutter[i].motordelay) * (Shutter[i].direction > 0 ? RESOLUTION : -Shutter[i].close_velocity));
break;
case SHT_PWM_TIME:
break;
case SHT_PWM_VALUE:
- return Shutter.real_position[i];
+ return Shutter[i].real_position;
break;
default:
break;
}
} else {
- return Shutter.real_position[i];
+ return Shutter[i].real_position;
}
}
void ShutterRelayChanged(void)
{
- // Shutter.RelayCurrentMask = binary relay that was recently changed and cause an Action
+ // ShutterGlobal.RelayCurrentMask = binary relay that was recently changed and cause an Action
// powerstate_local = binary powermatrix and relays from shutter: 0..3
// relays_changed = bool if one of the relays that belong to the shutter changed not by shutter or pulsetimer
char stemp1[10];
@@ -599,25 +594,25 @@ void ShutterRelayChanged(void)
for (uint32_t i = 0; i < shutters_present; i++) {
power_t powerstate_local = (power >> (Settings.shutter_startrelay[i] -1)) & 3;
// SRC_IGNORE added because INTERLOCK function bite causes this as last source for changing the relay.
- //uint8 manual_relays_changed = ((Shutter.RelayCurrentMask >> (Settings.shutter_startrelay[i] -1)) & 3) && SRC_IGNORE != last_source && SRC_SHUTTER != last_source && SRC_PULSETIMER != last_source ;
- uint8 manual_relays_changed = ((Shutter.RelayCurrentMask >> (Settings.shutter_startrelay[i] -1)) & 3) && SRC_SHUTTER != last_source && SRC_PULSETIMER != last_source ;
- AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Shutter %d: source: %s, powerstate_local %ld, Shutter.RelayCurrentMask %d, manual change %d"), i+1, GetTextIndexed(stemp1, sizeof(stemp1), last_source, kCommandSource), powerstate_local,Shutter.RelayCurrentMask,manual_relays_changed);
+ //uint8 manual_relays_changed = ((ShutterGlobal.RelayCurrentMask >> (Settings.shutter_startrelay[i] -1)) & 3) && SRC_IGNORE != last_source && SRC_SHUTTER != last_source && SRC_PULSETIMER != last_source ;
+ uint8 manual_relays_changed = ((ShutterGlobal.RelayCurrentMask >> (Settings.shutter_startrelay[i] -1)) & 3) && SRC_SHUTTER != last_source && SRC_PULSETIMER != last_source ;
+ AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Shutter %d: source: %s, powerstate_local %ld, ShutterGlobal.RelayCurrentMask %d, manual change %d"), i+1, GetTextIndexed(stemp1, sizeof(stemp1), last_source, kCommandSource), powerstate_local,ShutterGlobal.RelayCurrentMask,manual_relays_changed);
if (manual_relays_changed) {
- //Shutter.skip_relay_change = true;
+ //ShutterGlobal.skip_relay_change = true;
ShutterLimitRealAndTargetPositions(i);
- switch (Shutter.switch_mode[i] ) {
+ switch (Shutter[i].switch_mode ) {
case SHT_PULSE:
- if (Shutter.direction[i] != 0 && powerstate_local) {
- Shutter.target_position[i] = Shutter.real_position[i];
+ if (Shutter[i].direction != 0 && powerstate_local) {
+ Shutter[i].target_position = Shutter[i].real_position;
powerstate_local = 0;
- AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Shutter %d: Switch OFF motor. Target: %ld, source: %s, powerstate_local %ld, Shutter.RelayCurrentMask %d, manual change %d"), i+1, Shutter.target_position[i], GetTextIndexed(stemp1, sizeof(stemp1), last_source, kCommandSource), powerstate_local,Shutter.RelayCurrentMask,manual_relays_changed);
+ AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Shutter %d: Switch OFF motor. Target: %ld, source: %s, powerstate_local %ld, ShutterGlobal.RelayCurrentMask %d, manual change %d"), i+1, Shutter[i].target_position, GetTextIndexed(stemp1, sizeof(stemp1), last_source, kCommandSource), powerstate_local,ShutterGlobal.RelayCurrentMask,manual_relays_changed);
}
break;
default:
last_source = SRC_SHUTTER; // avoid switch off in the next loop
- if (Shutter.direction[i] != 0 ) ShutterPowerOff(i);
+ if (Shutter[i].direction != 0 ) ShutterPowerOff(i);
}
- switch (Shutter.position_mode) {
+ switch (ShutterGlobal.position_mode) {
// enum Shutterposition_mode {SHT_TIME, SHT_TIME_UP_DOWN, SHT_TIME_GARAGE, SHT_COUNTER, SHT_PWM_VALUE, SHT_PWM_TIME,};
case SHT_TIME_UP_DOWN:
case SHT_COUNTER:
@@ -626,42 +621,42 @@ void ShutterRelayChanged(void)
ShutterPowerOff(i);
switch (powerstate_local) {
case 1:
- ShutterStartInit(i, 1, Shutter.open_max[i]);
+ ShutterStartInit(i, 1, Shutter[i].open_max);
break;
case 3:
ShutterStartInit(i, -1, 0);
break;
default:
//AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Shutter %d: Switch OFF motor."),i);
- Shutter.target_position[i] = Shutter.real_position[i];
+ Shutter[i].target_position = Shutter[i].real_position;
}
break;
case SHT_TIME:
switch (powerstate_local) {
case 1:
- ShutterStartInit(i, 1, Shutter.open_max[i]);
+ ShutterStartInit(i, 1, Shutter[i].open_max);
break;
case 2:
ShutterStartInit(i, -1, 0);
break;
default:
//AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Shutter %d: Switch OFF motor."),i);
- Shutter.target_position[i] = Shutter.real_position[i];
+ Shutter[i].target_position = Shutter[i].real_position;
}
break;
case SHT_TIME_GARAGE:
switch (powerstate_local) {
case 1:
- ShutterStartInit(i, Shutter.lastdirection[i]*-1 , Shutter.lastdirection[i] == 1 ? 0 : Shutter.open_max[i]);
- AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Shutter %d Garage. NewTarget %d"), i, Shutter.target_position[i]);
+ ShutterStartInit(i, Shutter[i].lastdirection*-1 , Shutter[i].lastdirection == 1 ? 0 : Shutter[i].open_max);
+ AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Shutter %d Garage. NewTarget %d"), i, Shutter[i].target_position);
break;
default:
- Shutter.target_position[i] = Shutter.real_position[i];
+ Shutter[i].target_position = Shutter[i].real_position;
}
- } // switch (Shutter.position_mode)
- AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Shutter %d: Target: %ld, powerstatelocal %d"), i+1, Shutter.target_position[i], powerstate_local);
+ } // switch (ShutterGlobal.position_mode)
+ AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Shutter %d: Target: %ld, powerstatelocal %d"), i+1, Shutter[i].target_position, powerstate_local);
} // if (manual_relays_changed)
} // for (uint32_t i = 0; i < shutters_present; i++)
}
@@ -690,7 +685,7 @@ void ShutterButtonHandler(void)
buttonState = SHT_PRESSED_MULTI;
press_index = 1;
} else {
- if ((Shutter.direction[shutter_index]) && (Button.press_counter[button_index]==0)) {
+ if ((Shutter[shutter_index].direction) && (Button.press_counter[button_index]==0)) {
buttonState = SHT_PRESSED_IMMEDIATE;
press_index = 1;
Button.press_counter[button_index] = 99; // Remember to discard further action for press & hold within button timings
@@ -820,7 +815,7 @@ void ShutterButtonHandler(void)
} else {
uint8_t position = (Settings.shutter_button[button_index]>>(6*pos_press_index + 2)) & 0x03f;
if (position) {
- if (Shutter.direction[shutter_index]) {
+ if (Shutter[shutter_index].direction) {
XdrvMailbox.payload = XdrvMailbox.index;
CmndShutterStop();
} else {
@@ -873,10 +868,10 @@ void ShutterToggle(bool dir)
if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= shutters_present)) {
uint32_t index = XdrvMailbox.index-1;
if (dir) {
- XdrvMailbox.payload = (Shutter.lastdirection[index] > 0) ? 0 : 100;
+ XdrvMailbox.payload = (Shutter[index].lastdirection > 0) ? 0 : 100;
}
else {
- XdrvMailbox.payload = (50 < ShutterRealToPercentPosition(Shutter.real_position[index], index)) ? 0 : 100;
+ XdrvMailbox.payload = (50 < ShutterRealToPercentPosition(Shutter[index].real_position, index)) ? 0 : 100;
}
XdrvMailbox.data_len = 0;
last_source = SRC_WEBGUI;
@@ -903,7 +898,7 @@ void CmndShutterStopOpen(void)
{
if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= shutters_present)) {
uint32_t index = XdrvMailbox.index-1;
- if (Shutter.direction[index]) {
+ if (Shutter[index].direction) {
CmndShutterStop();
} else {
CmndShutterOpen();
@@ -927,7 +922,7 @@ void CmndShutterStopClose(void)
{
if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= shutters_present)) {
uint32_t index = XdrvMailbox.index-1;
- if (Shutter.direction[index]) {
+ if (Shutter[index].direction) {
CmndShutterStop();
} else {
CmndShutterClose();
@@ -949,7 +944,7 @@ void CmndShutterStopToggle(void)
{
if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= shutters_present)) {
uint32_t index = XdrvMailbox.index-1;
- if (Shutter.direction[index]) {
+ if (Shutter[index].direction) {
CmndShutterStop();
} else {
CmndShutterToggle();
@@ -961,7 +956,7 @@ void CmndShutterStopToggleDir(void)
{
if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= shutters_present)) {
uint32_t index = XdrvMailbox.index-1;
- if (Shutter.direction[index]) {
+ if (Shutter[index].direction) {
CmndShutterStop();
} else {
CmndShutterToggleDir();
@@ -977,13 +972,13 @@ void CmndShutterStop(void)
XdrvMailbox.index = XdrvMailbox.payload;
}
uint32_t i = XdrvMailbox.index -1;
- if (Shutter.direction[i] != 0) {
+ if (Shutter[i].direction != 0) {
- AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT: Stop moving %d: dir: %d"), XdrvMailbox.index, Shutter.direction[i]);
+ AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT: Stop moving %d: dir: %d"), XdrvMailbox.index, Shutter[i].direction);
// set stop position 10 steps ahead (0.5sec to allow normal stop)
//ToDo: Replace with function
- int32_t temp_realpos = Shutter.start_position[i] + ( (Shutter.time[i]+10) * (Shutter.direction[i] > 0 ? 100 : -Shutter.close_velocity[i]));
+ int32_t temp_realpos = Shutter[i].start_position + ( (Shutter[i].time+10) * (Shutter[i].direction > 0 ? 100 : -Shutter[i].close_velocity));
XdrvMailbox.payload = ShutterRealToPercentPosition(temp_realpos, i);
//XdrvMailbox.payload = Settings.shuttercoeff[2][i] * 5 > temp_realpos ? temp_realpos / Settings.shuttercoeff[2][i] : (temp_realpos-Settings.shuttercoeff[0,i]) / Settings.shuttercoeff[1][i];
last_source = SRC_WEBGUI;
@@ -1011,11 +1006,11 @@ void CmndShutterPosition(void)
// special handling fo UP,DOWN,TOGGLE,STOP command comming with payload -99
if ((XdrvMailbox.data_len > 1) && (XdrvMailbox.payload <= 0)) {
//UpperCase(XdrvMailbox.data, XdrvMailbox.data);
- if (!strcasecmp(XdrvMailbox.data,D_CMND_SHUTTER_UP) || !strcasecmp(XdrvMailbox.data,D_CMND_SHUTTER_OPEN) || ((Shutter.direction[index]==0) && !strcasecmp(XdrvMailbox.data,D_CMND_SHUTTER_STOPOPEN))) {
+ if (!strcasecmp(XdrvMailbox.data,D_CMND_SHUTTER_UP) || !strcasecmp(XdrvMailbox.data,D_CMND_SHUTTER_OPEN) || ((Shutter[index].direction==0) && !strcasecmp(XdrvMailbox.data,D_CMND_SHUTTER_STOPOPEN))) {
CmndShutterOpen();
return;
}
- if (!strcasecmp(XdrvMailbox.data,D_CMND_SHUTTER_DOWN) || !strcasecmp(XdrvMailbox.data,D_CMND_SHUTTER_CLOSE) || ((Shutter.direction[index]==0) && !strcasecmp(XdrvMailbox.data,D_CMND_SHUTTER_STOPCLOSE))) {
+ if (!strcasecmp(XdrvMailbox.data,D_CMND_SHUTTER_DOWN) || !strcasecmp(XdrvMailbox.data,D_CMND_SHUTTER_CLOSE) || ((Shutter[index].direction==0) && !strcasecmp(XdrvMailbox.data,D_CMND_SHUTTER_STOPCLOSE))) {
CmndShutterClose();
return;
}
@@ -1027,76 +1022,76 @@ void CmndShutterPosition(void)
CmndShutterToggleDir();
return;
}
- if (!strcasecmp(XdrvMailbox.data,D_CMND_SHUTTER_STOP) || ((Shutter.direction[index]) && (!strcasecmp(XdrvMailbox.data,D_CMND_SHUTTER_STOPOPEN) || !strcasecmp(XdrvMailbox.data,D_CMND_SHUTTER_STOPCLOSE)))) {
+ if (!strcasecmp(XdrvMailbox.data,D_CMND_SHUTTER_STOP) || ((Shutter[index].direction) && (!strcasecmp(XdrvMailbox.data,D_CMND_SHUTTER_STOPOPEN) || !strcasecmp(XdrvMailbox.data,D_CMND_SHUTTER_STOPCLOSE)))) {
XdrvMailbox.payload = -99;
CmndShutterStop();
return;
}
}
- int8_t target_pos_percent = (XdrvMailbox.payload < 0) ? (XdrvMailbox.payload == -99 ? ShutterRealToPercentPosition(Shutter.real_position[index], index) : 0) : ((XdrvMailbox.payload > 100) ? 100 : XdrvMailbox.payload);
+ int8_t target_pos_percent = (XdrvMailbox.payload < 0) ? (XdrvMailbox.payload == -99 ? ShutterRealToPercentPosition(Shutter[index].real_position, index) : 0) : ((XdrvMailbox.payload > 100) ? 100 : XdrvMailbox.payload);
// webgui still send also on inverted shutter the native position.
target_pos_percent = ((Settings.shutter_options[index] & 1) && (SRC_WEBGUI != last_source)) ? 100 - target_pos_percent : target_pos_percent;
if (XdrvMailbox.payload != -99) {
//target_pos_percent = (Settings.shutter_options[index] & 1) ? 100 - target_pos_percent : target_pos_percent;
- Shutter.target_position[index] = ShutterPercentToRealPosition(target_pos_percent, index);
- //Shutter.accelerator[index] = Shutter.open_velocity_max / ((Shutter.motordelay[index] > 0) ? Shutter.motordelay[index] : 1);
- //Shutter.target_position[index] = XdrvMailbox.payload < 5 ? Settings.shuttercoeff[2][index] * XdrvMailbox.payload : Settings.shuttercoeff[1][index] * XdrvMailbox.payload + Settings.shuttercoeff[0,index];
- AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: lastsource %d:, real %d, target %d, payload %d"), last_source, Shutter.real_position[index] ,Shutter.target_position[index],target_pos_percent);
+ Shutter[index].target_position = ShutterPercentToRealPosition(target_pos_percent, index);
+ //Shutter[i].accelerator[index] = ShutterGlobal.open_velocity_max / ((Shutter[i].motordelay[index] > 0) ? Shutter[i].motordelay[index] : 1);
+ //Shutter[i].target_position[index] = XdrvMailbox.payload < 5 ? Settings.shuttercoeff[2][index] * XdrvMailbox.payload : Settings.shuttercoeff[1][index] * XdrvMailbox.payload + Settings.shuttercoeff[0,index];
+ AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: lastsource %d:, real %d, target %d, payload %d"), last_source, Shutter[index].real_position ,Shutter[index].target_position,target_pos_percent);
}
- if ( (target_pos_percent >= 0) && (target_pos_percent <= 100) && abs(Shutter.target_position[index] - Shutter.real_position[index] ) / Shutter.close_velocity[index] > 2) {
+ if ( (target_pos_percent >= 0) && (target_pos_percent <= 100) && abs(Shutter[index].target_position - Shutter[index].real_position ) / Shutter[index].close_velocity > 2) {
if (Settings.shutter_options[index] & 4) {
- if (0 == target_pos_percent) Shutter.target_position[index] -= 1 * RESOLUTION * STEPS_PER_SECOND;
- if (100 == target_pos_percent) Shutter.target_position[index] += 1 * RESOLUTION * STEPS_PER_SECOND;
+ if (0 == target_pos_percent) Shutter[index].target_position -= 1 * RESOLUTION * STEPS_PER_SECOND;
+ if (100 == target_pos_percent) Shutter[index].target_position += 1 * RESOLUTION * STEPS_PER_SECOND;
}
- int8_t new_shutterdirection = Shutter.real_position[index] < Shutter.target_position[index] ? 1 : -1;
- if (Shutter.direction[index] == -new_shutterdirection) {
+ int8_t new_shutterdirection = Shutter[index].real_position < Shutter[index].target_position ? 1 : -1;
+ if (Shutter[index].direction == -new_shutterdirection) {
ShutterPowerOff(index);
}
- if (Shutter.direction[index] != new_shutterdirection) {
- ShutterStartInit(index, new_shutterdirection, Shutter.target_position[index]);
- switch (Shutter.position_mode) {
+ if (Shutter[index].direction != new_shutterdirection) {
+ ShutterStartInit(index, new_shutterdirection, Shutter[index].target_position);
+ switch (ShutterGlobal.position_mode) {
case SHT_COUNTER:
case SHT_PWM_TIME:
case SHT_PWM_VALUE:
case SHT_TIME_UP_DOWN:
- if (!Shutter.skip_relay_change) {
+ if (!ShutterGlobal.skip_relay_change) {
// Code for shutters with circuit safe configuration, switch the direction Relay
ExecuteCommandPowerShutter(Settings.shutter_startrelay[index] +1, new_shutterdirection == 1 ? 0 : 1, SRC_SHUTTER);
// power on
ExecuteCommandPowerShutter(Settings.shutter_startrelay[index], 1, SRC_SHUTTER);
}
- if (Shutter.position_mode != SHT_TIME_UP_DOWN) ExecuteCommandPowerShutter(Settings.shutter_startrelay[index]+2, 1, SRC_SHUTTER);
+ if (ShutterGlobal.position_mode != SHT_TIME_UP_DOWN) ExecuteCommandPowerShutter(Settings.shutter_startrelay[index]+2, 1, SRC_SHUTTER);
break;
case SHT_TIME:
- if (!Shutter.skip_relay_change) {
+ if (!ShutterGlobal.skip_relay_change) {
if ( (power >> (Settings.shutter_startrelay[index] -1)) & 3 > 0 ) {
- ExecuteCommandPowerShutter(Settings.shutter_startrelay[index] + (new_shutterdirection == 1 ? 1 : 0), Shutter.switch_mode[index] == SHT_SWITCH ? 0 : 1, SRC_SHUTTER);
+ ExecuteCommandPowerShutter(Settings.shutter_startrelay[index] + (new_shutterdirection == 1 ? 1 : 0), Shutter[index].switch_mode == SHT_SWITCH ? 0 : 1, SRC_SHUTTER);
}
ExecuteCommandPowerShutter(Settings.shutter_startrelay[index] + (new_shutterdirection == 1 ? 0 : 1), 1, SRC_SHUTTER);
}
break;
case SHT_TIME_GARAGE:
- if (!Shutter.skip_relay_change) {
- if (new_shutterdirection == Shutter.lastdirection[index]) {
- AddLog_P2(LOG_LEVEL_INFO, PSTR("SHT: Garage not move in this direction: %d"), Shutter.switch_mode[index] == SHT_PULSE);
- for (uint8_t k=0 ; k <= (uint8_t)(Shutter.switch_mode[index] == SHT_PULSE) ; k++) {
+ if (!ShutterGlobal.skip_relay_change) {
+ if (new_shutterdirection == Shutter[index].lastdirection) {
+ AddLog_P2(LOG_LEVEL_INFO, PSTR("SHT: Garage not move in this direction: %d"), Shutter[index].switch_mode == SHT_PULSE);
+ for (uint8_t k=0 ; k <= (uint8_t)(Shutter[index].switch_mode == SHT_PULSE) ; k++) {
ExecuteCommandPowerShutter(Settings.shutter_startrelay[index], 1, SRC_SHUTTER);
delay(500);
ExecuteCommandPowerShutter(Settings.shutter_startrelay[index], 0, SRC_SHUTTER);
delay(500);
}
// reset shutter time to avoid 2 seconds above count as runtime
- Shutter.time[index] = 0;
- } // if (new_shutterdirection == Shutter.lastdirection[index])
+ Shutter[index].time = 0;
+ } // if (new_shutterdirection == Shutter[i].lastdirection[index])
ExecuteCommandPowerShutter(Settings.shutter_startrelay[index], 1, SRC_SHUTTER);
- } // if (!Shutter.skip_relay_change)
+ } // if (!ShutterGlobal.skip_relay_change)
break;
- } // switch (Shutter.position_mode)
- Shutter.RelayCurrentMask = 0;
- } // if (Shutter.direction[index] != new_shutterdirection)
+ } // switch (ShutterGlobal.position_mode)
+ ShutterGlobal.RelayCurrentMask = 0;
+ } // if (Shutter[i].direction[index] != new_shutterdirection)
} else {
- target_pos_percent = ShutterRealToPercentPosition(Shutter.real_position[index], index);
+ target_pos_percent = ShutterRealToPercentPosition(Shutter[index].real_position, index);
ShutterReportPosition(true, index);
}
XdrvMailbox.index = index +1; // Fix random index for ShutterClose
@@ -1114,7 +1109,7 @@ void CmndShutterStopPosition(void)
{
if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= shutters_present)) {
uint32_t index = XdrvMailbox.index-1;
- if (Shutter.direction[index]) {
+ if (Shutter[index].direction) {
XdrvMailbox.payload = -99;
CmndShutterStop();
} else {
@@ -1164,13 +1159,11 @@ void CmndShutterMotorDelay(void)
void CmndShutterMode(void)
{
if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= MAX_MODES)) {
- Shutter.position_mode = XdrvMailbox.payload;
+ ShutterGlobal.position_mode = XdrvMailbox.payload;
Settings.shutter_mode = XdrvMailbox.payload;
ShutterInit();
- ResponseCmndNumber(XdrvMailbox.payload); // ????
- } else {
- ResponseCmndNumber(Shutter.position_mode);
}
+ ResponseCmndNumber(ShutterGlobal.position_mode);
}
void CmndShutterRelay(void)
@@ -1179,9 +1172,9 @@ void CmndShutterRelay(void)
if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 64)) {
Settings.shutter_startrelay[XdrvMailbox.index -1] = XdrvMailbox.payload;
if (XdrvMailbox.payload > 0) {
- Shutter.RelayShutterMask |= 3 << (XdrvMailbox.payload - 1);
+ ShutterGlobal.RelayShutterMask |= 3 << (XdrvMailbox.payload - 1);
} else {
- Shutter.RelayShutterMask ^= 3 << (Settings.shutter_startrelay[XdrvMailbox.index -1] - 1);
+ ShutterGlobal.RelayShutterMask ^= 3 << (Settings.shutter_startrelay[XdrvMailbox.index -1] - 1);
}
Settings.shutter_startrelay[XdrvMailbox.index -1] = XdrvMailbox.payload;
ShutterInit();
@@ -1342,21 +1335,19 @@ void CmndShutterSetHalfway(void)
void CmndShutterFrequency(void)
{
if ((XdrvMailbox.payload > 0) && (XdrvMailbox.payload <= 20000)) {
- Shutter.open_velocity_max = XdrvMailbox.payload;
+ ShutterGlobal.open_velocity_max = XdrvMailbox.payload;
if (shutters_present < 4) {
- Settings.shuttercoeff[4][3] = Shutter.open_velocity_max;
+ Settings.shuttercoeff[4][3] = ShutterGlobal.open_velocity_max;
}
ShutterInit();
- ResponseCmndNumber(XdrvMailbox.payload); // ????
- } else {
- ResponseCmndNumber(Shutter.open_velocity_max);
}
+ ResponseCmndNumber(ShutterGlobal.open_velocity_max);
}
void CmndShutterSetClose(void)
{
if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= shutters_present)) {
- Shutter.real_position[XdrvMailbox.index -1] = 0;
+ Shutter[XdrvMailbox.index -1].real_position = 0;
ShutterStartInit(XdrvMailbox.index -1, 0, 0);
Settings.shutter_position[XdrvMailbox.index -1] = 0;
ResponseCmndIdxChar(D_CONFIGURATION_RESET);
@@ -1366,25 +1357,13 @@ void CmndShutterSetClose(void)
void CmndShutterSetOpen(void)
{
if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= shutters_present)) {
- Shutter.real_position[XdrvMailbox.index -1] = Shutter.open_max[XdrvMailbox.index -1];
- ShutterStartInit(XdrvMailbox.index -1, 0, Shutter.open_max[XdrvMailbox.index -1]);
+ Shutter[XdrvMailbox.index -1].real_position = Shutter[XdrvMailbox.index -1].open_max;
+ ShutterStartInit(XdrvMailbox.index -1, 0, Shutter[XdrvMailbox.index -1].open_max);
Settings.shutter_position[XdrvMailbox.index -1] = 100;
ResponseCmndIdxChar(D_CONFIGURATION_RESET);
}
}
-void CmndShutterInvert(void)
-{
- if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= shutters_present)) {
- if (XdrvMailbox.payload == 0) {
- Settings.shutter_options[XdrvMailbox.index -1] &= ~(1);
- } else if (XdrvMailbox.payload == 1) {
- Settings.shutter_options[XdrvMailbox.index -1] |= (1);
- }
- ResponseCmndIdxNumber((Settings.shutter_options[XdrvMailbox.index -1] & 1) ? 1 : 0);
- }
-}
-
void CmndShutterCalibration(void)
{
if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= shutters_present)) {
@@ -1418,38 +1397,31 @@ void CmndShutterCalibration(void)
}
}
-void CmndShutterLock(void) {
+void ShutterOptionsSetHelper(uint16_t option){
if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= shutters_present)) {
if (XdrvMailbox.payload == 0) {
- Settings.shutter_options[XdrvMailbox.index -1] &= ~(2);
+ Settings.shutter_options[XdrvMailbox.index -1] &= ~(option);
} else if (XdrvMailbox.payload == 1) {
- Settings.shutter_options[XdrvMailbox.index -1] |= (2);
+ Settings.shutter_options[XdrvMailbox.index -1] |= (option);
}
- ResponseCmndIdxNumber((Settings.shutter_options[XdrvMailbox.index -1] & 2) ? 1 : 0);
+ ResponseCmndIdxNumber((Settings.shutter_options[XdrvMailbox.index -1] & option) ? 1 : 0);
}
}
+void CmndShutterInvert(void) {
+ ShutterOptionsSetHelper(1);
+}
+
+void CmndShutterLock(void) {
+ ShutterOptionsSetHelper(2);
+}
+
void CmndShutterEnableEndStopTime(void) {
- if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= shutters_present)) {
- if (XdrvMailbox.payload == 0) {
- Settings.shutter_options[XdrvMailbox.index -1] &= ~(4);
- } else if (XdrvMailbox.payload == 1) {
- Settings.shutter_options[XdrvMailbox.index -1] |= (4);
- }
- ResponseCmndIdxNumber((Settings.shutter_options[XdrvMailbox.index -1] & 4) ? 1 : 0);
- }
+ ShutterOptionsSetHelper(4);
}
-void CmndShutterInvertWebButtons(void)
-{
- if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= shutters_present)) {
- if (XdrvMailbox.payload == 0) {
- Settings.shutter_options[XdrvMailbox.index -1] &= ~(8);
- } else if (XdrvMailbox.payload == 1) {
- Settings.shutter_options[XdrvMailbox.index -1] |= (8);
- }
- ResponseCmndIdxNumber((Settings.shutter_options[XdrvMailbox.index -1] & 8) ? 1 : 0);
- }
+void CmndShutterInvertWebButtons(void) {
+ ShutterOptionsSetHelper(8);
}
/*********************************************************************************************\
@@ -1479,10 +1451,10 @@ bool Xdrv27(uint8_t function)
case FUNC_JSON_APPEND:
for (uint8_t i = 0; i < shutters_present; i++) {
uint8_t position = (Settings.shutter_options[i] & 1) ? 100 - Settings.shutter_position[i] : Settings.shutter_position[i];
- uint8_t target = (Settings.shutter_options[i] & 1) ? 100 - ShutterRealToPercentPosition(Shutter.target_position[i], i) : ShutterRealToPercentPosition(Shutter.target_position[i], i);
+ uint8_t target = (Settings.shutter_options[i] & 1) ? 100 - ShutterRealToPercentPosition(Shutter[i].target_position, i) : ShutterRealToPercentPosition(Shutter[i].target_position, i);
ResponseAppend_P(",");
- ResponseAppend_P(JSON_SHUTTER_POS, i+1, position, Shutter.direction[i],target);
+ ResponseAppend_P(JSON_SHUTTER_POS, i+1, position, Shutter[i].direction,target);
#ifdef USE_DOMOTICZ
if ((0 == tele_period) && (0 == i)) {
DomoticzSensor(DZ_SHUTTER, position);
@@ -1493,23 +1465,23 @@ bool Xdrv27(uint8_t function)
case FUNC_SET_POWER:
char stemp1[10];
// extract the number of the relay that was switched and save for later in Update Position.
- Shutter.RelayCurrentMask = XdrvMailbox.index ^ Shutter.RelayOldMask;
- AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Switched relay: %d by %s"), Shutter.RelayCurrentMask,GetTextIndexed(stemp1, sizeof(stemp1), last_source, kCommandSource));
+ ShutterGlobal.RelayCurrentMask = XdrvMailbox.index ^ ShutterGlobal.RelayOldMask;
+ AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Switched relay: %d by %s"), ShutterGlobal.RelayCurrentMask,GetTextIndexed(stemp1, sizeof(stemp1), last_source, kCommandSource));
ShutterRelayChanged();
- Shutter.RelayOldMask = XdrvMailbox.index;
+ ShutterGlobal.RelayOldMask = XdrvMailbox.index;
break;
case FUNC_SET_DEVICE_POWER:
- if (Shutter.skip_relay_change ) {
+ if (ShutterGlobal.skip_relay_change ) {
uint8_t i;
for (i = 0; i < devices_present; i++) {
- if (Shutter.RelayCurrentMask &1) {
+ if (ShutterGlobal.RelayCurrentMask &1) {
break;
}
- Shutter.RelayCurrentMask >>= 1;
+ ShutterGlobal.RelayCurrentMask >>= 1;
}
//AddLog_P2(LOG_LEVEL_ERROR, PSTR("SHT: skip relay change: %d"),i+1);
result = true;
- Shutter.skip_relay_change = 0;
+ ShutterGlobal.skip_relay_change = 0;
AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Skipping switch off relay %d"),i);
ExecuteCommandPowerShutter(i+1, 0, SRC_SHUTTER);
}
From 2db18c3e6d96df2c62346aa786422869b837880d Mon Sep 17 00:00:00 2001
From: stefanbode
Date: Wed, 9 Sep 2020 15:24:21 +0200
Subject: [PATCH 021/148] Introduced PWM range for servo shutters
---
tasmota/xdrv_27_shutter.ino | 44 +++++++++++++++++++++++++++++++------
1 file changed, 37 insertions(+), 7 deletions(-)
diff --git a/tasmota/xdrv_27_shutter.ino b/tasmota/xdrv_27_shutter.ino
index 6b0ef98e7..15e765ff4 100644
--- a/tasmota/xdrv_27_shutter.ino
+++ b/tasmota/xdrv_27_shutter.ino
@@ -53,14 +53,14 @@ enum ShutterButtonStates { SHT_NOT_PRESSED, SHT_PRESSED_MULTI, SHT_PRESSED_HOLD,
const char kShutterCommands[] PROGMEM = D_PRFX_SHUTTER "|"
D_CMND_SHUTTER_OPEN "|" D_CMND_SHUTTER_CLOSE "|" D_CMND_SHUTTER_TOGGLE "|" D_CMND_SHUTTER_TOGGLEDIR "|" D_CMND_SHUTTER_STOP "|" D_CMND_SHUTTER_POSITION "|"
- D_CMND_SHUTTER_OPENTIME "|" D_CMND_SHUTTER_CLOSETIME "|" D_CMND_SHUTTER_RELAY "|" D_CMND_SHUTTER_MODE "|"
+ D_CMND_SHUTTER_OPENTIME "|" D_CMND_SHUTTER_CLOSETIME "|" D_CMND_SHUTTER_RELAY "|" D_CMND_SHUTTER_MODE "|" D_CMND_SHUTTER_PWMRANGE "|"
D_CMND_SHUTTER_SETHALFWAY "|" D_CMND_SHUTTER_SETCLOSE "|" D_CMND_SHUTTER_SETOPEN "|" D_CMND_SHUTTER_INVERT "|" D_CMND_SHUTTER_CLIBRATION "|"
D_CMND_SHUTTER_MOTORDELAY "|" D_CMND_SHUTTER_FREQUENCY "|" D_CMND_SHUTTER_BUTTON "|" D_CMND_SHUTTER_LOCK "|" D_CMND_SHUTTER_ENABLEENDSTOPTIME "|" D_CMND_SHUTTER_INVERTWEBBUTTONS "|"
D_CMND_SHUTTER_STOPOPEN "|" D_CMND_SHUTTER_STOPCLOSE "|" D_CMND_SHUTTER_STOPTOGGLE "|" D_CMND_SHUTTER_STOPTOGGLEDIR "|" D_CMND_SHUTTER_STOPPOSITION;
void (* const ShutterCommand[])(void) PROGMEM = {
&CmndShutterOpen, &CmndShutterClose, &CmndShutterToggle, &CmndShutterToggleDir, &CmndShutterStop, &CmndShutterPosition,
- &CmndShutterOpenTime, &CmndShutterCloseTime, &CmndShutterRelay, &CmndShutterMode,
+ &CmndShutterOpenTime, &CmndShutterCloseTime, &CmndShutterRelay, &CmndShutterMode, &CmndShutterPwmRange,
&CmndShutterSetHalfway, &CmndShutterSetClose, &CmndShutterSetOpen, &CmndShutterInvert, &CmndShutterCalibration , &CmndShutterMotorDelay,
&CmndShutterFrequency, &CmndShutterButton, &CmndShutterLock, &CmndShutterEnableEndStopTime, &CmndShutterInvertWebButtons,
&CmndShutterStopOpen, &CmndShutterStopClose, &CmndShutterStopToggle, &CmndShutterStopToggleDir, &CmndShutterStopPosition};
@@ -87,8 +87,6 @@ struct SHUTTER {
int16_t motordelay; // initial motorstarttime in 0.05sec. Also uses for ramp at steppers and servos
int16_t pwm_velocity; // frequency of PWN for stepper motors or PWM duty cycle change for PWM servo
uint16_t pwm_value; // dutyload of PWM 0..1023 on ESP8266
- uint16_t pwm_min; // dutyload of PWM 0..1023 on ESP8266
- uint16_t pwm_max; // dutyload of PWM 0..1023 on ESP8266
uint16_t close_velocity_max; // maximum of PWM change during closeing. Defines velocity on opening. Steppers and Servos only
int32_t accelerator; // speed of ramp-up, ramp down of shutters with velocity control. Steppers and Servos only
} Shutter[MAX_SHUTTERS];
@@ -140,7 +138,7 @@ void ShutterRtc50mS(void)
case SHT_PWM_VALUE:
ShutterUpdateVelocity(i);
Shutter[i].real_position += Shutter[i].direction > 0 ? Shutter[i].pwm_velocity : (Shutter[i].direction < 0 ? -Shutter[i].pwm_velocity : 0);
- Shutter[i].pwm_value = SHT_DIV_ROUND((Shutter[i].pwm_max-Shutter[i].pwm_min) * Shutter[i].real_position , Shutter[i].open_max)+Shutter[i].pwm_min;
+ Shutter[i].pwm_value = SHT_DIV_ROUND((Settings.shutter_pwmrange[1][i]-Settings.shutter_pwmrange[0][i]) * Shutter[i].real_position , Shutter[i].open_max)+Settings.shutter_pwmrange[0][i];
analogWrite(Pin(GPIO_PWM1, i), Shutter[i].pwm_value);
break;
@@ -285,8 +283,8 @@ void ShutterInit(void)
Shutter[i].close_time = Settings.shutter_closetime[i] = (Settings.shutter_closetime[i] > 0) ? Settings.shutter_closetime[i] : 100;
//temporary hard coded.
- Shutter[i].pwm_min = pwm_min;
- Shutter[i].pwm_max = pwm_max;
+ Settings.shutter_pwmrange[0][i] = pwm_min;
+ Settings.shutter_pwmrange[1][i] = pwm_max;
// Update Calculation 20 because time interval is 0.05 sec ans time is in 0.1sec
Shutter[i].open_max = STEPS_PER_SECOND * RESOLUTION * Shutter[i].open_time / 10;
@@ -309,6 +307,9 @@ void ShutterInit(void)
switch (ShutterGlobal.position_mode) {
case SHT_PWM_VALUE:
ShutterGlobal.open_velocity_max = RESOLUTION;
+ // Initiate pwm range with defaults if not already set.
+ Settings.shutter_pwmrange[0][i] = Settings.shutter_pwmrange[0][i] > 0 ? Settings.shutter_pwmrange[0][i] : pwm_min;
+ Settings.shutter_pwmrange[1][i] = Settings.shutter_pwmrange[1][i] > 0 ? Settings.shutter_pwmrange[0][i] : pwm_max;
break;
}
Shutter[i].close_velocity_max = ShutterGlobal.open_velocity_max*Shutter[i].open_time / Shutter[i].close_time;
@@ -1364,6 +1365,35 @@ void CmndShutterSetOpen(void)
}
}
+void CmndShutterPwmRange(void)
+{
+ if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= shutters_present)) {
+ if (XdrvMailbox.data_len > 0) {
+ uint32_t i = 0;
+ char *str_ptr;
+
+ char data_copy[strlen(XdrvMailbox.data) +1];
+ strncpy(data_copy, XdrvMailbox.data, sizeof(data_copy)); // Duplicate data as strtok_r will modify it.
+ // Loop through the data string, splitting on ' ' seperators.
+ for (char *str = strtok_r(data_copy, " ", &str_ptr); str && i < 5; str = strtok_r(nullptr, " ", &str_ptr), i++) {
+ int field = atoi(str);
+ // The fields in a data string can only range from 1-30000.
+ // and following value must be higher than previous one
+ if ((field <= 0) || (field > 1023)) {
+ break;
+ }
+ Settings.shutter_pwmrange[i][XdrvMailbox.index -1] = field;
+ }
+ ShutterInit();
+ ResponseCmndIdxChar(XdrvMailbox.data);
+ } else {
+ char setting_chr[30] = "0";
+ snprintf_P(setting_chr, sizeof(setting_chr), PSTR("Shutter %d: min:%d max:%d"), XdrvMailbox.index, Settings.shutter_pwmrange[0][XdrvMailbox.index -1], Settings.shutter_pwmrange[1][XdrvMailbox.index -1]);
+ ResponseCmndIdxChar(setting_chr);
+ }
+ }
+}
+
void CmndShutterCalibration(void)
{
if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= shutters_present)) {
From 61b2d8e53ed23c1e9b2b2fb1133ca808272f6143 Mon Sep 17 00:00:00 2001
From: stefanbode
Date: Wed, 9 Sep 2020 15:24:55 +0200
Subject: [PATCH 022/148] Update i18n.h
---
tasmota/i18n.h | 1 +
1 file changed, 1 insertion(+)
diff --git a/tasmota/i18n.h b/tasmota/i18n.h
index 25b1cb8b7..24b26b032 100644
--- a/tasmota/i18n.h
+++ b/tasmota/i18n.h
@@ -607,6 +607,7 @@
#define D_CMND_SHUTTER_LOCK "Lock"
#define D_CMND_SHUTTER_ENABLEENDSTOPTIME "EnableEndStopTime"
#define D_CMND_SHUTTER_INVERTWEBBUTTONS "InvertWebButtons"
+#define D_CMND_SHUTTER_PWMRANGE "PWMRange"
// Commands xdrv_32_hotplug.ino
#define D_CMND_HOTPLUG "HotPlug"
From fb59126f9897cfdb27dac7d3b5caa9cb98474703 Mon Sep 17 00:00:00 2001
From: stefanbode
Date: Wed, 9 Sep 2020 15:27:43 +0200
Subject: [PATCH 023/148] Update settings.h
---
tasmota/settings.h | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/tasmota/settings.h b/tasmota/settings.h
index 048b61754..2ceadd523 100644
--- a/tasmota/settings.h
+++ b/tasmota/settings.h
@@ -611,8 +611,9 @@ struct {
uint8_t fallback_module; // F42
uint8_t shutter_mode; // F43
uint16_t energy_power_delta[3]; // F44
+ uint16_t shutter_pwmrange[2][MAX_SHUTTERS]; // F4A
- uint8_t free_f4a[106]; // F4A - Decrement if adding new Setting variables just above and below
+ uint8_t free_f5a[90]; // F5A - Decrement if adding new Setting variables just above and below
// Only 32 bit boundary variables below
SysBitfield5 flag5; // FB4
From 9b8138f2a055624ee6f3a42d8f8e4cd8da734083 Mon Sep 17 00:00:00 2001
From: stefanbode
Date: Wed, 9 Sep 2020 16:23:50 +0200
Subject: [PATCH 024/148] PWMrange introduced
---
tasmota/xdrv_27_shutter.ino | 13 +++++--------
1 file changed, 5 insertions(+), 8 deletions(-)
diff --git a/tasmota/xdrv_27_shutter.ino b/tasmota/xdrv_27_shutter.ino
index 15e765ff4..b7b609265 100644
--- a/tasmota/xdrv_27_shutter.ino
+++ b/tasmota/xdrv_27_shutter.ino
@@ -282,10 +282,6 @@ void ShutterInit(void)
Shutter[i].open_time = Settings.shutter_opentime[i] = (Settings.shutter_opentime[i] > 0) ? Settings.shutter_opentime[i] : 100;
Shutter[i].close_time = Settings.shutter_closetime[i] = (Settings.shutter_closetime[i] > 0) ? Settings.shutter_closetime[i] : 100;
- //temporary hard coded.
- Settings.shutter_pwmrange[0][i] = pwm_min;
- Settings.shutter_pwmrange[1][i] = pwm_max;
-
// Update Calculation 20 because time interval is 0.05 sec ans time is in 0.1sec
Shutter[i].open_max = STEPS_PER_SECOND * RESOLUTION * Shutter[i].open_time / 10;
Shutter[i].close_velocity = Shutter[i].open_max / Shutter[i].close_time / 2 ;
@@ -1369,14 +1365,14 @@ void CmndShutterPwmRange(void)
{
if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= shutters_present)) {
if (XdrvMailbox.data_len > 0) {
- uint32_t i = 0;
+ uint8_t i = 0;
char *str_ptr;
char data_copy[strlen(XdrvMailbox.data) +1];
strncpy(data_copy, XdrvMailbox.data, sizeof(data_copy)); // Duplicate data as strtok_r will modify it.
// Loop through the data string, splitting on ' ' seperators.
- for (char *str = strtok_r(data_copy, " ", &str_ptr); str && i < 5; str = strtok_r(nullptr, " ", &str_ptr), i++) {
- int field = atoi(str);
+ for (char *str = strtok_r(data_copy, " ", &str_ptr); str && i < 2; str = strtok_r(nullptr, " ", &str_ptr), i++) {
+ uint16_t field = atoi(str);
// The fields in a data string can only range from 1-30000.
// and following value must be higher than previous one
if ((field <= 0) || (field > 1023)) {
@@ -1384,6 +1380,7 @@ void CmndShutterPwmRange(void)
}
Settings.shutter_pwmrange[i][XdrvMailbox.index -1] = field;
}
+ AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT%d: Init1. pwmmin %d, pwmmax %d"), XdrvMailbox.index , Settings.shutter_pwmrange[0][XdrvMailbox.index -1], Settings.shutter_pwmrange[1][XdrvMailbox.index -1]);
ShutterInit();
ResponseCmndIdxChar(XdrvMailbox.data);
} else {
@@ -1398,7 +1395,7 @@ void CmndShutterCalibration(void)
{
if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= shutters_present)) {
if (XdrvMailbox.data_len > 0) {
- uint32_t i = 0;
+ uint8_t i = 0;
char *str_ptr;
char data_copy[strlen(XdrvMailbox.data) +1];
From 15ef20b6859fffb3baf326c144750dfa6c1eab39 Mon Sep 17 00:00:00 2001
From: stefanbode
Date: Wed, 9 Sep 2020 16:34:03 +0200
Subject: [PATCH 025/148] small bug
---
tasmota/xdrv_27_shutter.ino | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/tasmota/xdrv_27_shutter.ino b/tasmota/xdrv_27_shutter.ino
index b7b609265..19e89f113 100644
--- a/tasmota/xdrv_27_shutter.ino
+++ b/tasmota/xdrv_27_shutter.ino
@@ -305,7 +305,7 @@ void ShutterInit(void)
ShutterGlobal.open_velocity_max = RESOLUTION;
// Initiate pwm range with defaults if not already set.
Settings.shutter_pwmrange[0][i] = Settings.shutter_pwmrange[0][i] > 0 ? Settings.shutter_pwmrange[0][i] : pwm_min;
- Settings.shutter_pwmrange[1][i] = Settings.shutter_pwmrange[1][i] > 0 ? Settings.shutter_pwmrange[0][i] : pwm_max;
+ Settings.shutter_pwmrange[1][i] = Settings.shutter_pwmrange[1][i] > 0 ? Settings.shutter_pwmrange[1][i] : pwm_max;
break;
}
Shutter[i].close_velocity_max = ShutterGlobal.open_velocity_max*Shutter[i].open_time / Shutter[i].close_time;
From cc6a8a1c126dce95388be5af084ed804702f1eb7 Mon Sep 17 00:00:00 2001
From: Chris Keydel
Date: Wed, 9 Sep 2020 20:00:55 +0200
Subject: [PATCH 026/148] Adding buzzer frequency output again to latest
development branch. This should now pass all tests.
---
tasmota/settings.h | 2 +-
tasmota/xdrv_24_buzzer.ino | 42 +++++++++++++++++++++++++++++++-------
2 files changed, 36 insertions(+), 8 deletions(-)
diff --git a/tasmota/settings.h b/tasmota/settings.h
index 2ceadd523..97cefd392 100644
--- a/tasmota/settings.h
+++ b/tasmota/settings.h
@@ -129,7 +129,7 @@ typedef union { // Restricted by MISRA-C Rule 18.4 bu
uint32_t virtual_ct_cw : 1; // bit 25 (v8.4.0.1) - SetOption107 - Virtual CT Channel - signals whether the hardware white is cold CW (true) or warm WW (false)
uint32_t teleinfo_rawdata : 1; // bit 26 (v8.4.0.2) - SetOption108 - enable Teleinfo + Tasmota Energy device (0) or Teleinfo raw data only (1)
uint32_t alexa_gen_1 : 1; // bit 27 (v8.4.0.3) - SetOption109 - Alexa gen1 mode - if you only have Echo Dot 2nd gen devices
- uint32_t spare28 : 1; // bit 28
+ uint32_t buzzer_freq_mode : 1; // bit 28 (v8.4.0.3) - SetOption110 - SetOption110 - Use frequency output for buzzer pin instead of on/off signal
uint32_t spare29 : 1; // bit 29
uint32_t spare30 : 1; // bit 30
uint32_t spare31 : 1; // bit 31
diff --git a/tasmota/xdrv_24_buzzer.ino b/tasmota/xdrv_24_buzzer.ino
index 10232cc52..82ebfc439 100644
--- a/tasmota/xdrv_24_buzzer.ino
+++ b/tasmota/xdrv_24_buzzer.ino
@@ -32,6 +32,7 @@ struct BUZZER {
uint8_t inverted = 0; // Buzzer inverted flag (1 = (0 = On, 1 = Off))
uint8_t count = 0; // Number of buzzes
uint8_t mode = 0; // Buzzer mode (0 = regular, 1 = infinite, 2 = follow LED)
+ uint8_t freq_mode = 0; // Output mode (0 = regular, 1 = using frequency output)
uint8_t set[2];
uint8_t duration;
uint8_t state = 0;
@@ -39,9 +40,28 @@ struct BUZZER {
/*********************************************************************************************/
-void BuzzerOff(void)
+void BuzzerSet(uint8_t state)
{
- DigitalWrite(GPIO_BUZZER, 0, Buzzer.inverted); // Buzzer Off
+ if (Buzzer.inverted) {
+ state = !state;
+ }
+
+ if (Buzzer.freq_mode == 1) {
+ static uint8_t last_state = 0;
+ if (last_state != state) {
+ if (state) {
+ analogWrite(Pin(GPIO_BUZZER, 0), Settings.pwm_range / 2); // set 50% duty cycle for frequency output
+ }
+ else {
+ analogWrite(Pin(GPIO_BUZZER, 0), 0); // set 0% (or 100% for inverted PWM) duty cycle which turns off frequency output either way
+ }
+ last_state = state;
+ }
+ }
+ else {
+ DigitalWrite(GPIO_BUZZER, 0, state); // Buzzer On/Off
+ }
+
}
//void BuzzerBeep(uint32_t count = 1, uint32_t on = 1, uint32_t off = 1, uint32_t tune = 0, uint32_t mode = 0);
@@ -69,11 +89,19 @@ void BuzzerBeep(uint32_t count, uint32_t on, uint32_t off, uint32_t tune, uint32
}
Buzzer.count = count * 2; // Start buzzer
- AddLog_P2(LOG_LEVEL_DEBUG, PSTR("BUZ: %d(%d),%d,%d,0x%08X(0x%08X)"), count, Buzzer.count, on, off, tune, Buzzer.tune);
+ // We can use PWM mode for buzzer output if enabled.
+ if (Settings.flag4.buzzer_freq_mode) { // SetOption110 - Enable frequency output mode for buzzer
+ Buzzer.freq_mode = 1;
+ }
+ else {
+ Buzzer.freq_mode = 0;
+ }
+
+ AddLog_P2(LOG_LEVEL_DEBUG, PSTR("BUZ: %d(%d),%d,%d,0x%08X(0x%08X),%d"), count, Buzzer.count, on, off, tune, Buzzer.tune, Buzzer.freq_mode);
Buzzer.enable = (Buzzer.count > 0);
if (!Buzzer.enable) {
- BuzzerOff();
+ BuzzerSet(0);
}
}
@@ -81,7 +109,7 @@ void BuzzerSetStateToLed(uint32_t state)
{
if (Buzzer.enable && (2 == Buzzer.mode)) {
Buzzer.state = (state != 0);
- DigitalWrite(GPIO_BUZZER, 0, (Buzzer.inverted) ? !Buzzer.state : Buzzer.state);
+ BuzzerSet(Buzzer.state);
}
}
@@ -113,7 +141,7 @@ void BuzzerInit(void)
{
if (PinUsed(GPIO_BUZZER)) {
pinMode(Pin(GPIO_BUZZER), OUTPUT);
- BuzzerOff();
+ BuzzerSet(0);
} else {
Buzzer.active = false;
}
@@ -140,7 +168,7 @@ void BuzzerEvery100mSec(void)
Buzzer.duration = Buzzer.set[Buzzer.state];
}
}
- DigitalWrite(GPIO_BUZZER, 0, (Buzzer.inverted) ? !Buzzer.state : Buzzer.state);
+ BuzzerSet(Buzzer.state);
} else {
Buzzer.enable = false;
}
From fd3f77fb171c5d0acce493870634e416e98ee860 Mon Sep 17 00:00:00 2001
From: Jason2866 <24528715+Jason2866@users.noreply.github.com>
Date: Thu, 10 Sep 2020 15:06:06 +0200
Subject: [PATCH 027/148] Fix compile error on MAC
See https://github.com/platformio/platformio-core/issues/3659
Thx @ivankravets
---
platformio.ini | 1 -
1 file changed, 1 deletion(-)
diff --git a/platformio.ini b/platformio.ini
index ecf7d41ab..f70cfa812 100644
--- a/platformio.ini
+++ b/platformio.ini
@@ -11,7 +11,6 @@
description = Provide ESP8266 based devices with Web, MQTT and OTA firmware
src_dir = tasmota
build_dir = .pioenvs
-workspace_dir = .pioenvs
build_cache_dir = .cache
extra_configs = platformio_tasmota32.ini
platformio_tasmota_env.ini
From ed83e770a4a2dd9452dc739c5fb02959fea3263a Mon Sep 17 00:00:00 2001
From: Jason2866 <24528715+Jason2866@users.noreply.github.com>
Date: Thu, 10 Sep 2020 15:10:34 +0200
Subject: [PATCH 028/148] is not needed anymore too
since we do not support core 2.3.0 anymore
---
platformio.ini | 1 -
1 file changed, 1 deletion(-)
diff --git a/platformio.ini b/platformio.ini
index f70cfa812..b07184478 100644
--- a/platformio.ini
+++ b/platformio.ini
@@ -10,7 +10,6 @@
[platformio]
description = Provide ESP8266 based devices with Web, MQTT and OTA firmware
src_dir = tasmota
-build_dir = .pioenvs
build_cache_dir = .cache
extra_configs = platformio_tasmota32.ini
platformio_tasmota_env.ini
From da4caa6ec166b51586ad6d7362cb696c8d83f5c3 Mon Sep 17 00:00:00 2001
From: Jason2866 <24528715+Jason2866@users.noreply.github.com>
Date: Thu, 10 Sep 2020 16:41:31 +0200
Subject: [PATCH 029/148] path changed
---
pio/name-firmware.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/pio/name-firmware.py b/pio/name-firmware.py
index 1490ecc5c..1c79056de 100644
--- a/pio/name-firmware.py
+++ b/pio/name-firmware.py
@@ -5,7 +5,7 @@ import shutil
OUTPUT_DIR = "build_output{}".format(os.path.sep)
def bin_map_copy(source, target, env):
- variant = str(target[0]).split(os.path.sep)[1]
+ variant = str(target[0]).split(os.path.sep)[2]
# check if output directories exist and create if necessary
if not os.path.isdir(OUTPUT_DIR):
From 3d256b83c2214b52252e4c8dc01effde96dd92ac Mon Sep 17 00:00:00 2001
From: Jason2866 <24528715+Jason2866@users.noreply.github.com>
Date: Thu, 10 Sep 2020 16:42:37 +0200
Subject: [PATCH 030/148] path changed
---
pio/gzip-firmware.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/pio/gzip-firmware.py b/pio/gzip-firmware.py
index 43af1f933..248bb5a02 100644
--- a/pio/gzip-firmware.py
+++ b/pio/gzip-firmware.py
@@ -6,7 +6,7 @@ import gzip
OUTPUT_DIR = "build_output{}".format(os.path.sep)
def bin_gzip(source, target, env):
- variant = str(target[0]).split(os.path.sep)[1]
+ variant = str(target[0]).split(os.path.sep)[2]
# create string with location and file names based on variant
bin_file = "{}firmware{}{}.bin".format(OUTPUT_DIR, os.path.sep, variant)
From b239a1d77e10f64f9ffa5f0267bd12c19794ef04 Mon Sep 17 00:00:00 2001
From: Jason2866 <24528715+Jason2866@users.noreply.github.com>
Date: Fri, 11 Sep 2020 10:11:18 +0200
Subject: [PATCH 031/148] Update platformio.ini
---
platformio.ini | 22 +++++++++++++---------
1 file changed, 13 insertions(+), 9 deletions(-)
diff --git a/platformio.ini b/platformio.ini
index b07184478..a0c4d82b5 100644
--- a/platformio.ini
+++ b/platformio.ini
@@ -7,16 +7,9 @@
; Please visit documentation for the other options and examples
; http://docs.platformio.org/en/stable/projectconf.html
-[platformio]
-description = Provide ESP8266 based devices with Web, MQTT and OTA firmware
-src_dir = tasmota
-build_cache_dir = .cache
-extra_configs = platformio_tasmota32.ini
- platformio_tasmota_env.ini
- platformio_tasmota_env32.ini
- platformio_override.ini
-; *** Build/upload environment
+; *** Tasmota build variant selection
+[build_envs]
default_envs =
; *** Uncomment by deleting ";" in the line(s) below to select version(s)
; tasmota
@@ -51,10 +44,21 @@ default_envs =
; tasmota-TW
; tasmota-UK
;
+; *** Selection for Tasmota ESP32 is done in platformio_tasmota32.ini
+;
; *** alternatively can be done in: platformio_override.ini
; *** See example: platformio_override_sample.ini
; *********************************************************************
+[platformio]
+description = Provide ESP8266 / ESP32 based devices with Web, MQTT and OTA firmware
+src_dir = tasmota
+build_cache_dir = .cache
+extra_configs = platformio_tasmota32.ini
+ platformio_tasmota_env.ini
+ platformio_tasmota_env32.ini
+ platformio_override.ini
+default_envs = ${build_envs.default_envs}
[common]
framework = arduino
From ef3855e92f24cc3099bca4127f104f784a3b993d Mon Sep 17 00:00:00 2001
From: Jason2866 <24528715+Jason2866@users.noreply.github.com>
Date: Fri, 11 Sep 2020 10:13:12 +0200
Subject: [PATCH 032/148] Make selecting ESP32 versions possible
without using override file
---
platformio_tasmota32.ini | 38 ++++++++++++++++++++++++++++++++++++++
1 file changed, 38 insertions(+)
diff --git a/platformio_tasmota32.ini b/platformio_tasmota32.ini
index 549f2e333..739ec2338 100644
--- a/platformio_tasmota32.ini
+++ b/platformio_tasmota32.ini
@@ -1,6 +1,44 @@
; *** BETA ESP32 Tasmota version ***
; *** expect the unexpected. Some features not working!!! ***
+[platformio]
+
+; *** Tasmota build variant selection
+default_envs = ${build_envs.default_envs}
+; *** Uncomment by deleting ";" in the line(s) below to select version(s)
+; tasmota32
+; tasmota32-webcam
+; tasmota32-minimal
+; tasmota32-lite
+; tasmota32-knx
+; tasmota32-sensors
+; tasmota32-display
+; tasmota32-ir
+; tasmota32-ircustom
+; tasmota32-BG
+; tasmota32-BR
+; tasmota32-CN
+; tasmota32-CZ
+; tasmota32-DE
+; tasmota32-ES
+; tasmota32-FR
+; tasmota32-GR
+; tasmota32-HE
+; tasmota32-HU
+; tasmota32-IT
+; tasmota32-KO
+; tasmota32-NL
+; tasmota32-PL
+; tasmota32-PT
+; tasmota32-RO
+; tasmota32-RU
+; tasmota32-SE
+; tasmota32-SK
+; tasmota32-TR
+; tasmota32-TW
+; tasmota32-UK
+
+
[common32]
platform = espressif32@2.0.0
platform_packages = tool-esptoolpy@1.20800.0
From 9afa8a5d4f564d9b5295809114d12c35f9b5d6e5 Mon Sep 17 00:00:00 2001
From: gemu2015
Date: Fri, 11 Sep 2020 15:44:16 +0200
Subject: [PATCH 033/148] scripter update
reformatting,
bug fixes,
new options for google charts
formulas in text substitutions %(formula)%
---
tasmota/xdrv_10_scripter.ino | 4120 +++++++++++++++++-----------------
1 file changed, 2068 insertions(+), 2052 deletions(-)
diff --git a/tasmota/xdrv_10_scripter.ino b/tasmota/xdrv_10_scripter.ino
index cd5f986d2..ea8223233 100755
--- a/tasmota/xdrv_10_scripter.ino
+++ b/tasmota/xdrv_10_scripter.ino
@@ -136,19 +136,19 @@ Ticker Script_ticker4;
void Script_ticker1_end(void) {
Script_ticker1.detach();
- Run_Scripter(">ti1", 4,0);
+ Run_Scripter(">ti1", 4, 0);
}
void Script_ticker2_end(void) {
Script_ticker2.detach();
- Run_Scripter(">ti2", 4,0);
+ Run_Scripter(">ti2", 4, 0);
}
void Script_ticker3_end(void) {
Script_ticker3.detach();
- Run_Scripter(">ti3", 4,0);
+ Run_Scripter(">ti3", 4, 0);
}
void Script_ticker4_end(void) {
Script_ticker4.detach();
- Run_Scripter(">ti4", 4,0);
+ Run_Scripter(">ti4", 4, 0);
}
#endif
@@ -169,7 +169,7 @@ FS *fsp;
#ifdef LITTLEFS_SCRIPT_SIZE
-void SaveFile(const char *name,const uint8_t *buf,uint32_t len) {
+void SaveFile(const char *name, const uint8_t *buf, uint32_t len) {
File file = fsp->open(name, "w");
if (!file) return;
file.write(buf, len);
@@ -179,7 +179,7 @@ void SaveFile(const char *name,const uint8_t *buf,uint32_t len) {
uint8_t fs_mounted=0;
-void LoadFile(const char *name,uint8_t *buf,uint32_t len) {
+void LoadFile(const char *name, uint8_t *buf, uint32_t len) {
if (!fs_mounted) {
#ifdef ESP32
if (!SPIFFS.begin(FORMAT_SPIFFS_IF_FAILED)) {
@@ -214,7 +214,7 @@ FS *fsp;
#else
SDClass *fsp;
#endif
-#endif
+#endif //USE_SCRIPT_FATFS
#ifndef ESP32
// esp8266
@@ -404,7 +404,7 @@ struct SCRIPT_MEM {
FILE_FLAGS file_flags[SFS_MAX];
uint8_t script_sd_found;
char flink[2][14];
-#endif
+#endif //USE_SCRIPT_FATFS
#ifdef USE_SCRIPT_GLOBVARS
UDP_FLAGS udp_flags;
#endif
@@ -423,8 +423,8 @@ char * IPAddressToString(const IPAddress& ip_address) {
sprintf_P(ipbuffer, PSTR("%u.%u.%u.%u"), ip_address[0], ip_address[1], ip_address[2], ip_address[3]);
return ipbuffer;
}
-#endif
-#endif
+#endif //USE_DEVICE_GROUPS
+#endif //USE_SCRIPT_GLOBVARS
int16_t last_findex;
int16_t last_sindex;
@@ -436,10 +436,10 @@ uint32_t script_lastmillis;
#ifdef USE_BUTTON_EVENT
int8_t script_button[MAX_KEYS];
-#endif
+#endif //USE_BUTTON_EVENT
-char *GetNumericResult(char *lp,uint8_t lastop,float *fp,JsonObject *jo);
-char *GetStringResult(char *lp,uint8_t lastop,char *cp,JsonObject *jo);
+char *GetNumericArgument(char *lp,uint8_t lastop,float *fp,JsonObject *jo);
+char *GetStringArgument(char *lp,uint8_t lastop,char *cp,JsonObject *jo);
char *ForceStringVar(char *lp,char *dstr);
void send_download(void);
uint8_t reject(char *name);
@@ -447,75 +447,80 @@ uint8_t reject(char *name);
void ScriptEverySecond(void) {
if (bitRead(Settings.rule_enabled, 0)) {
- struct T_INDEX *vtp=glob_script_mem.type;
- float delta=(millis()-script_lastmillis)/1000.0;
- script_lastmillis=millis();
+ struct T_INDEX *vtp = glob_script_mem.type;
+ float delta = (millis() - script_lastmillis) / 1000.0;
+ script_lastmillis = millis();
for (uint8_t count=0; count0) {
// decrement
- *fp-=delta;
- if (*fp<0) *fp=0;
+ *fp -= delta;
+ if (*fp<0) *fp = 0;
}
}
if (vtp[count].bits.is_autoinc) {
// increments timers
- float *fp=&glob_script_mem.fvars[vtp[count].index];
+ float *fp = &glob_script_mem.fvars[vtp[count].index];
if (*fp>=0) {
- *fp+=delta;
+ *fp += delta;
}
}
}
- Run_Scripter(">S",2,0);
+ Run_Scripter(">S", 2, 0);
}
}
void RulesTeleperiod(void) {
- if (bitRead(Settings.rule_enabled, 0) && mqtt_data[0]) Run_Scripter(">T",2, mqtt_data);
+ if (bitRead(Settings.rule_enabled, 0) && mqtt_data[0]) Run_Scripter(">T", 2, mqtt_data);
}
// EEPROM MACROS
// i2c eeprom
-#define EEP_WRITE(A,B,C) eeprom_writeBytes(A,B,(uint8_t*)C);
-#define EEP_READ(A,B,C) eeprom_readBytes(A,B,(uint8_t*)C);
+#define EEP_WRITE(A,B,C) eeprom_writeBytes(A, B, (uint8_t*)C);
+#define EEP_READ(A,B,C) eeprom_readBytes(A, B, (uint8_t*)C);
#define SCRIPT_SKIP_SPACES while (*lp==' ' || *lp=='\t') lp++;
#define SCRIPT_SKIP_EOL while (*lp==SCRIPT_EOL) lp++;
-float *Get_MFAddr(uint8_t index,uint16_t *len,uint16_t *ipos);
+float *Get_MFAddr(uint8_t index, uint16_t *len, uint16_t *ipos);
// allocates all variables and presets them
int16_t Init_Scripter(void) {
char *script;
- script=glob_script_mem.script_ram;
+ script = glob_script_mem.script_ram;
// scan lines for >DEF
- uint16_t lines=0,nvars=0,svars=0,vars=0;
- char *lp=script;
+ uint16_t lines = 0;
+ uint16_t nvars = 0;
+ uint16_t svars = 0;
+ uint16_t vars = 0;
+ char *lp = script;
char vnames[MAXVARS*10];
- char *vnames_p=vnames;
+ char *vnames_p = vnames;
char *vnp[MAXVARS];
- char **vnp_p=vnp;
+ char **vnp_p = vnp;
char strings[MAXSVARS*SCRIPT_MAXSSIZE];
struct M_FILT mfilt[MAXFILT];
- char *strings_p=strings;
+ char *strings_p = strings;
char *snp[MAXSVARS];
- char **snp_p=snp;
- uint8_t numperm=0,numflt=0,count;
+ char **snp_p = snp;
+ uint8_t numperm = 0;
+ uint8_t numflt = 0;
+ uint8_t count;
- glob_script_mem.max_ssize=SCRIPT_SVARSIZE;
- glob_script_mem.scriptptr=0;
+ glob_script_mem.max_ssize = SCRIPT_SVARSIZE;
+ glob_script_mem.scriptptr = 0;
if (!*script) return -999;
float fvalues[MAXVARS];
struct T_INDEX vtypes[MAXVARS];
- char init=0;
+ char init = 0;
while (1) {
// check line
// skip leading spaces
@@ -527,84 +532,84 @@ char *script;
if (init) {
// init section
if (*lp=='>' || !*lp) {
- init=0;
+ init = 0;
break;
}
- char *op=strchr(lp,'=');
+ char *op = strchr(lp, '=');
if (op) {
- vtypes[vars].bits.data=0;
+ vtypes[vars].bits.data = 0;
// found variable definition
if (*lp=='p' && *(lp+1)==':') {
- lp+=2;
+ lp += 2;
if (numpermMAXFILT) {
return -6;
}
} else {
- vtypes[vars].bits.is_filter=0;
+ vtypes[vars].bits.is_filter = 0;
}
- *vnp_p++=vnames_p;
+ *vnp_p ++= vnames_p;
while (lpMAXNVARS) {
return -1;
@@ -616,27 +621,27 @@ char *script;
while (*op==' ') op++;
if (isdigit(*op)) {
// lenght define follows
- uint16_t flen=atoi(op);
+ uint16_t flen = atoi(op);
if (flen>MAX_ARRAY_SIZE) {
// limit array size
- flen=MAX_ARRAY_SIZE;
+ flen = MAX_ARRAY_SIZE;
}
- mfilt[numflt-1].numvals&=OR_FILT_MASK;
- mfilt[numflt-1].numvals|=flen&AND_FILT_MASK;
+ mfilt[numflt-1].numvals &= OR_FILT_MASK;
+ mfilt[numflt-1].numvals |= flen&AND_FILT_MASK;
}
}
} else {
// string vars
op++;
- *snp_p++=strings_p;
+ *snp_p ++= strings_p;
while (*op!='\"') {
if (*op==SCRIPT_EOL) break;
- *strings_p++=*op++;
+ *strings_p++ = *op++;
}
- *strings_p++=0;
- vtypes[vars].bits.is_string=1;
- vtypes[vars].index=svars;
+ *strings_p++ = 0;
+ vtypes[vars].bits.is_string = 1;
+ vtypes[vars].index = svars;
svars++;
if (svars>MAXSVARS) {
return -2;
@@ -648,15 +653,15 @@ char *script;
}
}
} else {
- if (!strncmp(lp,">D",2)) {
- lp+=2;
+ if (!strncmp(lp, ">D", 2)) {
+ lp += 2;
SCRIPT_SKIP_SPACES
if (isdigit(*lp)) {
- uint8_t ssize=atoi(lp)+1;
+ uint8_t ssize = atoi(lp)+1;
if (ssize<10 || ssize>SCRIPT_MAXSSIZE) ssize=SCRIPT_MAXSSIZE;
- glob_script_mem.max_ssize=ssize;
+ glob_script_mem.max_ssize = ssize;
}
- init=1;
+ init = 1;
}
}
// next line
@@ -666,78 +671,78 @@ char *script;
lp++;
}
- uint16_t fsize=0;
+ uint16_t fsize = 0;
for (count=0; countnumvals=mfilt[count].numvals;
- mp+=sizeof(struct M_FILT)+((mfilt[count].numvals&AND_FILT_MASK)-1)*sizeof(float);
+ uint8_t *mp = (uint8_t*)glob_script_mem.mfilt;
+ for (count = 0; countnumvals = mfilt[count].numvals;
+ mp += sizeof(struct M_FILT) + ((mfilt[count].numvals & AND_FILT_MASK) - 1) * sizeof(float);
}
- glob_script_mem.numvars=vars;
- glob_script_mem.script_dprec=SCRIPT_FLOAT_PRECISION;
- glob_script_mem.script_loglevel=LOG_LEVEL_INFO;
+ glob_script_mem.numvars = vars;
+ glob_script_mem.script_dprec = SCRIPT_FLOAT_PRECISION;
+ glob_script_mem.script_loglevel = LOG_LEVEL_INFO;
#if SCRIPT_DEBUG>2
- struct T_INDEX *dvtp=glob_script_mem.type;
- for (uint8_t count=0; count=0
// user sd card
- fsp=&SD;
+ fsp = &SD;
if (SD.begin(USE_SCRIPT_FATFS)) {
#else
// use flash file
@@ -836,35 +841,35 @@ char *script;
if (FFat.begin(true)) {
#else
if (fsp->begin()) {
-#endif
+#endif // ESP32
#endif // USE_SCRIPT_FATFS>=0
- glob_script_mem.script_sd_found=1;
+ glob_script_mem.script_sd_found = 1;
} else {
- glob_script_mem.script_sd_found=0;
+ glob_script_mem.script_sd_found = 0;
}
}
- for (uint8_t cnt=0;cnt0
ClaimSerial();
SetSerialBaudrate(9600);
-#endif
+#endif //SCRIPT_DEBUG
// store start of actual program here
- glob_script_mem.scriptptr=lp-1;
- glob_script_mem.scriptptr_bu=glob_script_mem.scriptptr;
+ glob_script_mem.scriptptr = lp - 1;
+ glob_script_mem.scriptptr_bu = glob_script_mem.scriptptr;
#ifdef USE_SCRIPT_GLOBVARS
if (glob_script_mem.udp_flags.udp_used) {
Script_Init_UDP();
- glob_script=Run_Scripter(">G",-2,0);
+ glob_script = Run_Scripter(">G", -2, 0);
}
-#endif
+#endif //USE_SCRIPT_GLOBVARS
return 0;
@@ -899,42 +904,42 @@ void Script_PollUdp(void) {
if (glob_script_mem.udp_flags.udp_connected ) {
while (Script_PortUdp.parsePacket()) {
char packet_buffer[SCRIPT_UDP_BUFFER_SIZE];
- int32_t len = Script_PortUdp.read(packet_buffer, SCRIPT_UDP_BUFFER_SIZE -1);
+ int32_t len = Script_PortUdp.read(packet_buffer, SCRIPT_UDP_BUFFER_SIZE - 1);
packet_buffer[len] = 0;
script_udp_remote_ip = Script_PortUdp.remoteIP();
AddLog_P2(LOG_LEVEL_DEBUG, PSTR("UDP: Packet %s - %d - %s"), packet_buffer, len, script_udp_remote_ip.toString().c_str());
char *lp=packet_buffer;
- if (!strncmp(lp,"=>",2)) {
- lp+=2;
- char *cp=strchr(lp,'=');
+ if (!strncmp(lp,"=>", 2)) {
+ lp += 2;
+ char *cp=strchr(lp, '=');
if (cp) {
char vnam[32];
- for (uint32_t count=0; countG",2,0);
+ Run_Scripter(">G", 2, 0);
}
}
}
@@ -950,167 +955,169 @@ void script_udp_sendvar(char *vname,float *fp,char *sp) {
if (!glob_script_mem.udp_flags.udp_used) return;
if (!glob_script_mem.udp_flags.udp_connected) return;
- char sbuf[SCRIPT_MAXSSIZE+4];
- strcpy(sbuf,"=>");
- strcat(sbuf,vname);
- strcat(sbuf,"=");
+ char sbuf[SCRIPT_MAXSSIZE + 4];
+ strcpy(sbuf, "=>");
+ strcat(sbuf, vname);
+ strcat(sbuf, "=");
if (fp) {
char flstr[16];
- dtostrfd(*fp,8,flstr);
- strcat(sbuf,flstr);
- AddLog_P2(LOG_LEVEL_DEBUG, PSTR("num var updated - %s"),sbuf);
+ dtostrfd(*fp, 8, flstr);
+ strcat(sbuf, flstr);
+ AddLog_P2(LOG_LEVEL_DEBUG, PSTR("num var updated - %s"), sbuf);
} else {
- strcat(sbuf,sp);
- AddLog_P2(LOG_LEVEL_DEBUG, PSTR("string var updated - %s"),sbuf);
+ strcat(sbuf, sp);
+ AddLog_P2(LOG_LEVEL_DEBUG, PSTR("string var updated - %s"), sbuf);
}
- Script_PortUdp.beginPacket(IPAddress(239,255,255,250), SCRIPT_UDP_PORT);
+ Script_PortUdp.beginPacket(IPAddress(239, 255, 255, 250), SCRIPT_UDP_PORT);
// Udp.print(String("RET UC: ") + String(recv_Packet));
- Script_PortUdp.write((const uint8_t*)sbuf,strlen(sbuf));
+ Script_PortUdp.write((const uint8_t*)sbuf, strlen(sbuf));
Script_PortUdp.endPacket();
}
-#endif
+#endif //USE_SCRIPT_GLOBVARS
#ifdef USE_LIGHT
#ifdef USE_WS2812
void ws2812_set_array(float *array ,uint32_t len, uint32_t offset) {
Ws2812ForceSuspend();
- for (uint32_t cnt=0;cntSettings.light_pixels) break;
- uint32_t col=array[cnt];
- Ws2812SetColor(index+1,col>>16,col>>8,col,0);
+ uint32_t col = array[cnt];
+ Ws2812SetColor(index + 1, col>>16, col>>8, col, 0);
}
Ws2812ForceUpdate();
}
-#endif
-#endif
+#endif //USE_WS2812
+#endif //USE_LIGHT
-float median_array(float *array,uint16_t len) {
+float median_array(float *array, uint16_t len) {
uint8_t ind[len];
- uint8_t mind=0,index=0,flg;
- float min=FLT_MAX;
+ uint8_t mind = 0;
+ uint8_t index = 0;
+ uint8_t flg;
+ float min = FLT_MAX;
- for (uint8_t hcnt=0; hcntnumvals&AND_FILT_MASK;
- if (ipos) *ipos=mflp->index;
+ *len = mflp->numvals & AND_FILT_MASK;
+ if (ipos) *ipos = mflp->index;
return mflp->rbuff;
}
- mp+=sizeof(struct M_FILT)+((mflp->numvals&AND_FILT_MASK)-1)*sizeof(float);
+ mp += sizeof(struct M_FILT) + ((mflp->numvals & AND_FILT_MASK) - 1) * sizeof(float);
}
return 0;
}
-float Get_MFVal(uint8_t index,int16_t bind) {
- uint8_t *mp=(uint8_t*)glob_script_mem.mfilt;
- for (uint8_t count=0; countnumvals&AND_FILT_MASK;
+ uint16_t maxind = mflp->numvals & AND_FILT_MASK;
if (!bind) {
return mflp->index;
}
if (bind<0) {
return maxind;
}
- if (bind<1 || bind>maxind) bind=maxind;
- return mflp->rbuff[bind-1];
+ if (bind<1 || bind>maxind) bind = maxind;
+ return mflp->rbuff[bind - 1];
}
- mp+=sizeof(struct M_FILT)+((mflp->numvals&AND_FILT_MASK)-1)*sizeof(float);
+ mp += sizeof(struct M_FILT) + ((mflp->numvals & AND_FILT_MASK) - 1) * sizeof(float);
}
return 0;
}
-void Set_MFVal(uint8_t index,uint16_t bind,float val) {
- uint8_t *mp=(uint8_t*)glob_script_mem.mfilt;
- for (uint8_t count=0; countnumvals&AND_FILT_MASK;
+ uint16_t maxind = mflp->numvals & AND_FILT_MASK;
if (!bind) {
- mflp->index=val;
+ mflp->index = val;
} else {
- if (bind<1 || bind>maxind) bind=maxind;
- mflp->rbuff[bind-1]=val;
+ if (bind<1 || bind>maxind) bind = maxind;
+ mflp->rbuff[bind-1] = val;
}
return;
}
- mp+=sizeof(struct M_FILT)+((mflp->numvals&AND_FILT_MASK)-1)*sizeof(float);
+ mp += sizeof(struct M_FILT) + ((mflp->numvals & AND_FILT_MASK) - 1) * sizeof(float);
}
}
float Get_MFilter(uint8_t index) {
- uint8_t *mp=(uint8_t*)glob_script_mem.mfilt;
- for (uint8_t count=0; countnumvals&OR_FILT_MASK) {
+ if (mflp->numvals & OR_FILT_MASK) {
// moving average
- return mflp->maccu/(mflp->numvals&AND_FILT_MASK);
+ return mflp->maccu / (mflp->numvals & AND_FILT_MASK);
} else {
// median, sort array indices
- return median_array(mflp->rbuff,mflp->numvals);
+ return median_array(mflp->rbuff, mflp->numvals);
}
}
- mp+=sizeof(struct M_FILT)+((mflp->numvals&AND_FILT_MASK)-1)*sizeof(float);
+ mp += sizeof(struct M_FILT) + ((mflp->numvals & AND_FILT_MASK) - 1) * sizeof(float);
}
return 0;
}
void Set_MFilter(uint8_t index, float invar) {
- uint8_t *mp=(uint8_t*)glob_script_mem.mfilt;
- for (uint8_t count=0; countnumvals&OR_FILT_MASK) {
+ if (mflp->numvals & OR_FILT_MASK) {
// moving average
- mflp->maccu-=mflp->rbuff[mflp->index];
- mflp->maccu+=invar;
- mflp->rbuff[mflp->index]=invar;
+ mflp->maccu -= mflp->rbuff[mflp->index];
+ mflp->maccu += invar;
+ mflp->rbuff[mflp->index] = invar;
mflp->index++;
- if (mflp->index>=(mflp->numvals&AND_FILT_MASK)) mflp->index=0;
+ if (mflp->index>=(mflp->numvals&AND_FILT_MASK)) mflp->index = 0;
} else {
// median
- mflp->rbuff[mflp->index]=invar;
+ mflp->rbuff[mflp->index] = invar;
mflp->index++;
- if (mflp->index>=mflp->numvals) mflp->index=0;
+ if (mflp->index>=mflp->numvals) mflp->index = 0;
}
break;
}
- mp+=sizeof(struct M_FILT)+((mflp->numvals&AND_FILT_MASK)-1)*sizeof(float);
+ mp += sizeof(struct M_FILT) + ((mflp->numvals & AND_FILT_MASK) - 1) * sizeof(float);
}
}
@@ -1124,17 +1131,16 @@ int8_t index;
float DoMedian5(uint8_t index, float in) {
- if (index>=MEDIAN_FILTER_NUM) index=0;
+ if (index>=MEDIAN_FILTER_NUM) index = 0;
- struct MEDIAN_FILTER* mf=&script_mf[index];
- mf->buffer[mf->index]=in;
+ struct MEDIAN_FILTER* mf = &script_mf[index];
+ mf->buffer[mf->index] = in;
mf->index++;
- if (mf->index>=MEDIAN_SIZE) mf->index=0;
- return median_array(mf->buffer,MEDIAN_SIZE);
+ if (mf->index>=MEDIAN_SIZE) mf->index = 0;
+ return median_array(mf->buffer, MEDIAN_SIZE);
}
#ifdef USE_LIGHT
-//#ifdef USE_WS2812
uint32_t HSVToRGB(uint16_t hue, uint8_t saturation, uint8_t value) {
float r = 0, g = 0, b = 0;
struct HSV {
@@ -1143,9 +1149,9 @@ struct HSV {
float V;
} hsv;
-hsv.H=hue;
-hsv.S=(float)saturation/100.0;
-hsv.V=(float)value/100.0;
+hsv.H = hue;
+hsv.S = (float)saturation / 100.0;
+hsv.V = (float)value / 100.0;
if (hsv.S == 0) {
r = hsv.V;
@@ -1208,16 +1214,16 @@ if (hsv.S == 0) {
}
- uint8_t ir,ig,ib;
- ir=r*255;
- ig=g*255;
- ib=b*255;
+ uint8_t ir, ig, ib;
+ ir = r * 255;
+ ig = g * 255;
+ ib = b * 255;
- uint32_t rgb=(ir<<16)|(ig<<8)|ib;
+ uint32_t rgb = (ir<<16) | (ig<<8) | ib;
return rgb;
}
-#endif
-//#endif
+#endif //USE_LIGHT
+
#ifdef USE_ANGLE_FUNC
uint32_t pulse_time_hl;
@@ -1249,8 +1255,8 @@ uint32_t MeasurePulseTime(int32_t in) {
if (in >= 0) {
// define pin;
pt_pin = in;
- pinMode(pt_pin&0x3f,INPUT_PULLUP);
- attachInterrupt(pt_pin&0x3f, MP_Timer, CHANGE);
+ pinMode(pt_pin & 0x3f, INPUT_PULLUP);
+ attachInterrupt(pt_pin & 0x3f, MP_Timer, CHANGE);
pulse_ltime_lh = millis();
pulse_ltime_hl = millis();
return 0;
@@ -1269,26 +1275,26 @@ uint32_t MeasurePulseTime(int32_t in) {
#ifdef USE_SCRIPT_GLOBVARS
uint32_t match_vars(char *dvnam, float **fp, char **sp, uint32_t *ind) {
- uint16_t olen=strlen(dvnam);
- struct T_INDEX *vtp=glob_script_mem.type;
- for (uint32_t count=0; count ff=nothing found, fe=constant number,fd = constant string else bit 7 => 80 = string, 0 = number
// no flash strings here for performance reasons!!!
-char *isvar(char *lp, uint8_t *vtype,struct T_INDEX *tind,float *fp,char *sp,JsonObject *jo) {
- uint16_t count,len=0;
- uint8_t nres=0;
+char *isvar(char *lp, uint8_t *vtype, struct T_INDEX *tind, float *fp, char *sp, JsonObject *jo) {
+ uint16_t count,len = 0;
+ uint8_t nres = 0;
char vname[32];
- float fvar=0;
- tind->index=0;
- tind->bits.data=0;
+ float fvar = 0;
+ tind->index = 0;
+ tind->bits.data = 0;
if (isdigit(*lp) || (*lp=='-' && isdigit(*(lp+1))) || *lp=='.') {
// isnumber
if (fp) {
if (*lp=='0' && *(lp+1)=='x') {
- lp+=2;
- *fp=strtol(lp,0,16);
+ lp += 2;
+ *fp = strtol(lp, 0, 16);
} else {
- *fp=CharToFloat(lp);
+ *fp = CharToFloat(lp);
}
}
if (*lp=='-') lp++;
@@ -1324,109 +1330,109 @@ char *isvar(char *lp, uint8_t *vtype,struct T_INDEX *tind,float *fp,char *sp,Jso
if (*lp==0 || *lp==SCRIPT_EOL) break;
lp++;
}
- tind->bits.constant=1;
- tind->bits.is_string=0;
- *vtype=NUM_RES;
+ tind->bits.constant = 1;
+ tind->bits.is_string = 0;
+ *vtype = NUM_RES;
return lp;
}
if (*lp=='"') {
lp++;
while (*lp!='"') {
if (*lp==0 || *lp==SCRIPT_EOL) break;
- uint8_t iob=*lp;
+ uint8_t iob = *lp;
if (iob=='\\') {
lp++;
if (*lp=='t') {
- iob='\t';
+ iob = '\t';
} else if (*lp=='n') {
- iob='\n';
+ iob = '\n';
} else if (*lp=='r') {
- iob='\r';
+ iob = '\r';
} else if (*lp=='\\') {
- iob='\\';
+ iob = '\\';
} else {
lp--;
}
- if (sp) *sp++=iob;
+ if (sp) *sp++ = iob;
} else {
- if (sp) *sp++=iob;
+ if (sp) *sp++ = iob;
}
lp++;
}
- if (sp) *sp=0;
- *vtype=STR_RES;
- tind->bits.constant=1;
- tind->bits.is_string=1;
- return lp+1;
+ if (sp) *sp = 0;
+ *vtype = STR_RES;
+ tind->bits.constant = 1;
+ tind->bits.is_string = 1;
+ return lp + 1;
}
if (*lp=='-') {
// inverted var
- nres=1;
+ nres = 1;
lp++;
}
- const char *term="\n\r ])=+-/*%>index=VAR_NV;
- glob_script_mem.var_not_found=1;
+ *vtype = VAR_NV;
+ tind->index = VAR_NV;
+ glob_script_mem.var_not_found = 1;
return lp;
}
- struct T_INDEX *vtp=glob_script_mem.type;
+ struct T_INDEX *vtp = glob_script_mem.type;
char dvnam[32];
- strcpy (dvnam,vname);
- uint8_t olen=len;
- last_findex=-1;
- last_sindex=-1;
- char *ja=strchr(dvnam,'[');
+ strcpy (dvnam, vname);
+ uint8_t olen = len;
+ last_findex = -1;
+ last_sindex = -1;
+ char *ja = strchr(dvnam, '[');
if (ja) {
- *ja=0;
+ *ja = 0;
ja++;
- olen=strlen(dvnam);
+ olen = strlen(dvnam);
}
- for (count=0; countindex=count; // overwrite with global var index
+ if (!strncmp(cp, dvnam, olen)) {
+ uint8_t index = vtp[count].index;
+ *tind = vtp[count];
+ tind->index = count; // overwrite with global var index
if (vtp[count].bits.is_string==0) {
- *vtype=NTYPE|index;
+ *vtype = NTYPE | index;
if (vtp[count].bits.is_filter) {
if (ja) {
- lp+=olen+1;
- lp=GetNumericResult(lp,OPER_EQU,&fvar,0);
- last_findex=fvar;
- fvar=Get_MFVal(index,fvar);
- len=1;
+ lp += olen + 1;
+ lp = GetNumericArgument(lp, OPER_EQU, &fvar, 0);
+ last_findex = fvar;
+ fvar = Get_MFVal(index, fvar);
+ len = 1;
} else {
- fvar=Get_MFilter(index);
+ fvar = Get_MFilter(index);
}
} else {
- fvar=glob_script_mem.fvars[index];
+ fvar = glob_script_mem.fvars[index];
}
- if (nres) fvar=-fvar;
- if (fp) *fp=fvar;
+ if (nres) fvar = -fvar;
+ if (fp) *fp = fvar;
} else {
- *vtype=STYPE|index;
- if (sp) strlcpy(sp,glob_script_mem.glob_snp+(index*glob_script_mem.max_ssize),SCRIPT_MAXSSIZE);
+ *vtype = STYPE|index;
+ if (sp) strlcpy(sp, glob_script_mem.glob_snp + (index * glob_script_mem.max_ssize), SCRIPT_MAXSSIZE);
}
- return lp+len;
+ return lp + len;
}
}
}
@@ -1434,50 +1440,50 @@ char *isvar(char *lp, uint8_t *vtype,struct T_INDEX *tind,float *fp,char *sp,Jso
if (jo) {
// look for json input
char jvname[32];
- strcpy(jvname,vname);
+ strcpy(jvname, vname);
const char* str_value;
uint8_t aindex;
String vn;
- char *ja=strchr(jvname,'[');
+ char *ja=strchr(jvname, '[');
if (ja) {
// json array
- *ja=0;
+ *ja = 0;
ja++;
// fetch array index
float fvar;
- GetNumericResult(ja,OPER_EQU,&fvar,0);
- aindex=fvar;
- if (aindex<1 || aindex>6) aindex=1;
+ GetNumericArgument(ja, OPER_EQU, &fvar, 0);
+ aindex = fvar;
+ if (aindex<1 || aindex>6) aindex = 1;
aindex--;
}
if (jo->success()) {
- char *subtype=strchr(jvname,'#');
+ char *subtype = strchr(jvname, '#');
char *subtype2;
if (subtype) {
- *subtype=0;
+ *subtype = 0;
subtype++;
- subtype2=strchr(subtype,'#');
+ subtype2 = strchr(subtype, '#');
if (subtype2) {
- *subtype2=0;
+ *subtype2 = 0;
*subtype2++;
}
}
- vn=jvname;
+ vn = jvname;
str_value = (*jo)[vn];
if ((*jo)[vn].success()) {
if (subtype) {
- JsonObject &jobj1=(*jo)[vn];
+ JsonObject &jobj1 = (*jo)[vn];
if (jobj1.success()) {
- vn=subtype;
- jo=&jobj1;
+ vn = subtype;
+ jo = &jobj1;
str_value = (*jo)[vn];
if ((*jo)[vn].success()) {
// 2. stage
if (subtype2) {
- JsonObject &jobj2=(*jo)[vn];
+ JsonObject &jobj2 = (*jo)[vn];
if ((*jo)[vn].success()) {
- vn=subtype2;
- jo=&jobj2;
+ vn = subtype2;
+ jo = &jobj2;
str_value = (*jo)[vn];
if ((*jo)[vn].success()) {
goto skip;
@@ -1502,33 +1508,33 @@ char *isvar(char *lp, uint8_t *vtype,struct T_INDEX *tind,float *fp,char *sp,Jso
}
if (str_value && *str_value) {
if ((*jo).is(vn)) {
- if (!strncmp(str_value,"ON",2)) {
- if (fp) *fp=1;
+ if (!strncmp(str_value, "ON", 2)) {
+ if (fp) *fp = 1;
goto nexit;
- } else if (!strncmp(str_value,"OFF",3)) {
- if (fp) *fp=0;
+ } else if (!strncmp(str_value, "OFF", 3)) {
+ if (fp) *fp = 0;
goto nexit;
} else {
- *vtype=STR_RES;
- tind->bits.constant=1;
- tind->bits.is_string=1;
- if (sp) strlcpy(sp,str_value,SCRIPT_MAXSSIZE);
- return lp+len;
+ *vtype = STR_RES;
+ tind->bits.constant = 1;
+ tind->bits.is_string = 1;
+ if (sp) strlcpy(sp, str_value, SCRIPT_MAXSSIZE);
+ return lp + len;
}
} else {
if (fp) {
- if (!strncmp(vn.c_str(),"Epoch",5)) {
- *fp=atoi(str_value)-(uint32_t)EPOCH_OFFSET;
+ if (!strncmp(vn.c_str(), "Epoch", 5)) {
+ *fp = atoi(str_value) - (uint32_t)EPOCH_OFFSET;
} else {
- *fp=CharToFloat((char*)str_value);
+ *fp = CharToFloat((char*)str_value);
}
}
nexit:
- *vtype=NUM_RES;
- tind->bits.constant=1;
- tind->bits.is_string=0;
- return lp+len;
+ *vtype = NUM_RES;
+ tind->bits.constant = 1;
+ tind->bits.is_string = 0;
+ return lp + len;
}
}
}
@@ -1539,29 +1545,28 @@ chknext:
switch (vname[0]) {
case 'a':
#ifdef USE_ANGLE_FUNC
- if (!strncmp(vname,"acos(",5)) {
- lp+=5;
- lp=GetNumericResult(lp,OPER_EQU,&fvar,0);
- fvar=acosf(fvar);
+ if (!strncmp(vname, "acos(", 5)) {
+ lp=GetNumericArgument(lp + 5, OPER_EQU, &fvar, 0);
+ fvar = acosf(fvar);
lp++;
- len=0;
+ len = 0;
goto exit;
}
#endif
- if (!strncmp(vname,"asc(",4)) {
+ if (!strncmp(vname, "asc(", 4)) {
char str[SCRIPT_MAXSSIZE];
- lp=GetStringResult(lp+4,OPER_EQU,str,0);
- fvar=str[0];
+ lp = GetStringArgument(lp + 4, OPER_EQU, str, 0);
+ fvar = str[0];
lp++;
- len=0;
+ len = 0;
goto exit;
}
- if (!strncmp(vname,"adc(",4)) {
- lp=GetNumericResult(lp+4,OPER_EQU,&fvar,0);
+ if (!strncmp(vname, "adc(", 4)) {
+ lp = GetNumericArgument(lp + 4, OPER_EQU, &fvar, 0);
while (*lp==' ') lp++;
- float fvar1=1;
+ float fvar1 = 1;
if (*lp!=')') {
- lp=GetNumericResult(lp,OPER_EQU,&fvar1,0);
+ lp = GetNumericArgument(lp, OPER_EQU, &fvar1, 0);
if (fvar1<32 || fvar1>39) fvar1 = 32;
}
lp++;
@@ -1569,146 +1574,144 @@ chknext:
#ifdef ESP32
// ESP32
#ifdef USE_ADC
- fvar=AdcRead(fvar1, fvar);
+ fvar = AdcRead(fvar1, fvar);
#else
- fvar=999.999;
+ fvar = 999.999;
#endif // USE_ADC
#else
// ESP8266
#ifndef USE_ADC_VCC
- fvar=AdcRead(fvar);
+ fvar = AdcRead(fvar);
#else
- fvar=(float)ESP.getVcc()/1000.0;
+ fvar = (float)ESP.getVcc() / 1000.0;
#endif // USE_ADC_VCC
#endif // ESP32
- len=0;
+ len = 0;
goto exit;
}
break;
case 'b':
- if (!strncmp(vname,"boot",4)) {
+ if (!strncmp(vname, "boot", 4)) {
if (rules_flag.system_boot) {
- rules_flag.system_boot=0;
- fvar=1;
+ rules_flag.system_boot = 0;
+ fvar = 1;
}
goto exit;
}
#ifdef USE_BUTTON_EVENT
- if (!strncmp(vname,"bt[",3)) {
+ if (!strncmp(vname, "bt[", 3)) {
// tasmota button state
- GetNumericResult(vname+3,OPER_EQU,&fvar,0);
- uint32_t index=fvar;
- if (index<1 || index>MAX_KEYS) index=1;
- fvar=script_button[index-1];
- script_button[index-1]|=0x80;
+ GetNumericArgument(vname+3, OPER_EQU, &fvar, 0);
+ uint32_t index = fvar;
+ if (index<1 || index>MAX_KEYS) index = 1;
+ fvar=script_button[index - 1];
+ script_button[index - 1] |= 0x80;
len++;
goto exit;
}
-#endif
+#endif //USE_BUTTON_EVENT
break;
case 'c':
- if (!strncmp(vname,"chg[",4)) {
+ if (!strncmp(vname, "chg[", 4)) {
// var changed
struct T_INDEX ind;
uint8_t vtype;
- isvar(vname+4,&vtype,&ind,0,0,0);
+ isvar(vname + 4, &vtype, &ind, 0, 0, 0);
if (!ind.bits.constant) {
- uint8_t index=glob_script_mem.type[ind.index].index;
- if (glob_script_mem.fvars[index]!=glob_script_mem.s_fvars[index]) {
+ uint8_t index = glob_script_mem.type[ind.index].index;
+ if (glob_script_mem.fvars[index] != glob_script_mem.s_fvars[index]) {
// var has changed
- glob_script_mem.s_fvars[index]=glob_script_mem.fvars[index];
- fvar=1;
+ glob_script_mem.s_fvars[index] = glob_script_mem.fvars[index];
+ fvar = 1;
len++;
goto exit;
} else {
- fvar=0;
+ fvar = 0;
len++;
goto exit;
}
}
}
#ifdef ESP32
- if (!strncmp(vname,"core",4)) {
- fvar=xPortGetCoreID();
+ if (!strncmp(vname, "core", 4)) {
+ fvar = xPortGetCoreID();
goto exit;
}
#ifdef USE_SCRIPT_TASK
- if (!strncmp(vname,"ct(",3)) {
- lp+=3;
- lp=GetNumericResult(lp,OPER_EQU,&fvar,0);
+ if (!strncmp(vname, "ct(", 3)) {
+ lp = GetNumericArgument(lp + 3, OPER_EQU, &fvar, 0);
while (*lp==' ') lp++;
float fvar1;
- lp=GetNumericResult(lp,OPER_EQU,&fvar1,0);
+ lp = GetNumericArgument(lp, OPER_EQU, &fvar1, 0);
while (*lp==' ') lp++;
float fvar2;
- lp=GetNumericResult(lp,OPER_EQU,&fvar2,0);
+ lp = GetNumericArgument(lp, OPER_EQU, &fvar2, 0);
lp++;
- fvar=scripter_create_task(fvar,fvar1,fvar2);
- len=0;
+ fvar = scripter_create_task(fvar, fvar1, fvar2);
+ len = 0;
goto exit;
}
-#endif
-#endif
+#endif //USE_SCRIPT_TASK
+#endif //ESP32
break;
case 'd':
- if (!strncmp(vname,"day",3)) {
- fvar=RtcTime.day_of_month;
+ if (!strncmp(vname, "day", 3)) {
+ fvar = RtcTime.day_of_month;
goto exit;
}
break;
case 'e':
- if (!strncmp(vname,"epoch",5)) {
- fvar=UtcTime()-(uint32_t)EPOCH_OFFSET;
+ if (!strncmp(vname, "epoch", 5)) {
+ fvar = UtcTime() - (uint32_t)EPOCH_OFFSET;
goto exit;
}
- if (!strncmp(vname,"eres",4)) {
- fvar=event_handeled;
- tind->index=SCRIPT_EVENT_HANDLED;
+ if (!strncmp(vname, "eres", 4)) {
+ fvar = event_handeled;
+ tind->index = SCRIPT_EVENT_HANDLED;
goto exit_settable;
}
#ifdef USE_ENERGY_SENSOR
- if (!strncmp(vname,"enrg[",5)) {
- lp+=5;
- lp=GetNumericResult(lp,OPER_EQU,&fvar,0);
+ if (!strncmp(vname, "enrg[", 5)) {
+ lp=GetNumericArgument(lp + 5, OPER_EQU, &fvar, 0);
while (*lp==' ') lp++;
switch ((uint32_t)fvar) {
case 0:
- fvar=Energy.total;
+ fvar = Energy.total;
break;
case 1:
- fvar=Energy.voltage[0];
+ fvar = Energy.voltage[0];
break;
case 2:
- fvar=Energy.voltage[1];
+ fvar = Energy.voltage[1];
break;
case 3:
- fvar=Energy.voltage[2];
+ fvar = Energy.voltage[2];
break;
case 4:
- fvar=Energy.current[0];
+ fvar = Energy.current[0];
break;
case 5:
- fvar=Energy.current[1];
+ fvar = Energy.current[1];
break;
case 6:
- fvar=Energy.current[2];
+ fvar = Energy.current[2];
break;
case 7:
- fvar=Energy.active_power[0];
+ fvar = Energy.active_power[0];
break;
case 8:
- fvar=Energy.active_power[1];
+ fvar = Energy.active_power[1];
break;
case 9:
- fvar=Energy.active_power[2];
+ fvar = Energy.active_power[2];
break;
default:
- fvar=99999;
+ fvar = 99999;
break;
}
- len=0;
+ len = 0;
lp++;
goto exit;
}
@@ -1717,166 +1720,161 @@ chknext:
case 'f':
//#define DEBUG_FS
#ifdef USE_SCRIPT_FATFS
- if (!strncmp(vname,"fo(",3)) {
- lp+=3;
+ if (!strncmp(vname, "fo(", 3)) {
char str[SCRIPT_MAXSSIZE];
- lp=GetStringResult(lp,OPER_EQU,str,0);
+ lp = GetStringArgument(lp + 3, OPER_EQU, str, 0);
while (*lp==' ') lp++;
- uint8_t mode=0;
+ uint8_t mode = 0;
if ((*lp=='r') || (*lp=='w') || (*lp=='a')) {
switch (*lp) {
case 'r':
- mode=0;
+ mode = 0;
break;
case 'w':
- mode=1;
+ mode = 1;
break;
case 'a':
- mode=2;
+ mode = 2;
break;
}
lp++;
} else {
- lp=GetNumericResult(lp,OPER_EQU,&fvar,0);
- mode=fvar;
+ lp = GetNumericArgument(lp, OPER_EQU, &fvar, 0);
+ mode = fvar;
}
- fvar=-1;
- for (uint8_t cnt=0;cntopen(str,FILE_READ);
+ glob_script_mem.files[cnt] = fsp->open(str, FILE_READ);
if (glob_script_mem.files[cnt].isDirectory()) {
glob_script_mem.files[cnt].rewindDirectory();
- glob_script_mem.file_flags[cnt].is_dir=1;
+ glob_script_mem.file_flags[cnt].is_dir = 1;
} else {
- glob_script_mem.file_flags[cnt].is_dir=0;
+ glob_script_mem.file_flags[cnt].is_dir = 0;
}
}
else {
if (mode==1) {
- glob_script_mem.files[cnt]=fsp->open(str,FILE_WRITE);
+ glob_script_mem.files[cnt] = fsp->open(str,FILE_WRITE);
#ifdef DEBUG_FS
- AddLog_P2(LOG_LEVEL_INFO,PSTR("open file for write %d"),cnt);
+ AddLog_P2(LOG_LEVEL_INFO, PSTR("open file for write %d"), cnt);
#endif
} else {
- glob_script_mem.files[cnt]=fsp->open(str,FILE_APPEND);
+ glob_script_mem.files[cnt] = fsp->open(str,FILE_APPEND);
#ifdef DEBUG_FS
- AddLog_P2(LOG_LEVEL_INFO,PSTR("open file for append %d"),cnt);
+ AddLog_P2(LOG_LEVEL_INFO, PSTR("open file for append %d"), cnt);
#endif
}
}
if (glob_script_mem.files[cnt]) {
- fvar=cnt;
- glob_script_mem.file_flags[cnt].is_open=1;
+ fvar = cnt;
+ glob_script_mem.file_flags[cnt].is_open = 1;
} else {
- AddLog_P(LOG_LEVEL_INFO,PSTR("file open failed"));
+ AddLog_P(LOG_LEVEL_INFO, PSTR("file open failed"));
}
break;
}
}
lp++;
- len=0;
+ len = 0;
goto exit;
}
- if (!strncmp(vname,"fc(",3)) {
- lp+=3;
- lp=GetNumericResult(lp,OPER_EQU,&fvar,0);
+ if (!strncmp(vname, "fc(", 3)) {
+ lp = GetNumericArgument(lp + 3, OPER_EQU, &fvar, 0);
if (fvar>=0) {
- uint8_t ind=fvar;
- if (ind>=SFS_MAX) ind=SFS_MAX-1;
+ uint8_t ind = fvar;
+ if (ind>=SFS_MAX) ind = SFS_MAX - 1;
#ifdef DEBUG_FS
- AddLog_P2(LOG_LEVEL_INFO,PSTR("closing file %d"),ind);
+ AddLog_P2(LOG_LEVEL_INFO, PSTR("closing file %d"), ind);
#endif
glob_script_mem.files[ind].close();
- glob_script_mem.file_flags[ind].is_open=0;
+ glob_script_mem.file_flags[ind].is_open = 0;
}
- fvar=0;
+ fvar = 0;
lp++;
- len=0;
+ len = 0;
goto exit;
}
- if (!strncmp(vname,"ff(",3)) {
- lp+=3;
- lp=GetNumericResult(lp,OPER_EQU,&fvar,0);
- uint8_t ind=fvar;
- if (ind>=SFS_MAX) ind=SFS_MAX-1;
+ if (!strncmp(vname, "ff(", 3)) {
+ lp = GetNumericArgument(lp + 3, OPER_EQU, &fvar, 0);
+ uint8_t ind = fvar;
+ if (ind>=SFS_MAX) ind = SFS_MAX - 1;
glob_script_mem.files[ind].flush();
- fvar=0;
+ fvar = 0;
lp++;
- len=0;
+ len = 0;
goto exit;
}
- if (!strncmp(vname,"fw(",3)) {
- lp+=3;
+ if (!strncmp(vname, "fw(", 3)) {
char str[SCRIPT_MAXSSIZE];
- lp=ForceStringVar(lp,str);
+ lp = ForceStringVar(lp + 3, str);
while (*lp==' ') lp++;
- lp=GetNumericResult(lp,OPER_EQU,&fvar,0);
- uint8_t ind=fvar;
- if (ind>=SFS_MAX) ind=SFS_MAX-1;
+ lp = GetNumericArgument(lp, OPER_EQU, &fvar, 0);
+ uint8_t ind = fvar;
+ if (ind>=SFS_MAX) ind = SFS_MAX - 1;
if (glob_script_mem.file_flags[ind].is_open) {
- fvar=glob_script_mem.files[ind].print(str);
+ fvar = glob_script_mem.files[ind].print(str);
} else {
- fvar=0;
+ fvar = 0;
}
lp++;
- len=0;
+ len = 0;
goto exit;
}
- if (!strncmp(vname,"fr(",3)) {
- lp+=3;
+ if (!strncmp(vname, "fr(", 3)) {
struct T_INDEX ind;
uint8_t vtype;
- uint8_t sindex=0;
- lp=isvar(lp,&vtype,&ind,0,0,0);
+ uint8_t sindex = 0;
+ lp = isvar(lp + 3, &vtype, &ind, 0, 0, 0);
if (vtype!=VAR_NV) {
// found variable as result
if ((vtype&STYPE)==0) {
// error
- fvar=0;
+ fvar = 0;
goto exit;
} else {
// string result
- sindex=glob_script_mem.type[ind.index].index;
+ sindex = glob_script_mem.type[ind.index].index;
}
} else {
// error
- fvar=0;
+ fvar = 0;
goto exit;
}
while (*lp==' ') lp++;
- lp=GetNumericResult(lp,OPER_EQU,&fvar,0);
- uint8_t find=fvar;
- if (find>=SFS_MAX) find=SFS_MAX-1;
- uint8_t index=0;
- char str[glob_script_mem.max_ssize+1];
- char *cp=str;
+ lp = GetNumericArgument(lp, OPER_EQU, &fvar, 0);
+ uint8_t find = fvar;
+ if (find>=SFS_MAX) find = SFS_MAX - 1;
+ uint8_t index = 0;
+ char str[glob_script_mem.max_ssize + 1];
+ char *cp = str;
if (glob_script_mem.file_flags[find].is_open) {
if (glob_script_mem.file_flags[find].is_dir) {
while (true) {
- File entry=glob_script_mem.files[find].openNextFile();
+ File entry = glob_script_mem.files[find].openNextFile();
if (entry) {
if (!reject((char*)entry.name())) {
- char *ep=(char*)entry.name();
+ char *ep = (char*)entry.name();
if (*ep=='/') ep++;
char *lcp = strrchr(ep,'/');
if (lcp) {
- ep=lcp+1;
+ ep = lcp + 1;
}
- strcpy(str,ep);
+ strcpy(str, ep);
entry.close();
break;
}
} else {
- *cp=0;
+ *cp = 0;
break;
}
entry.close();
}
- index=strlen(str);
+ index = strlen(str);
} else {
while (glob_script_mem.files[find].available()) {
uint8_t buf[1];
@@ -1884,244 +1882,234 @@ chknext:
if (buf[0]=='\t' || buf[0]==',' || buf[0]=='\n' || buf[0]=='\r') {
break;
} else {
- *cp++=buf[0];
+ *cp++ = buf[0];
index++;
- if (index>=glob_script_mem.max_ssize-1) break;
+ if (index>=glob_script_mem.max_ssize - 1) break;
}
}
- *cp=0;
+ *cp = 0;
}
} else {
- strcpy(str,"file error");
+ strcpy(str, "file error");
}
lp++;
- strlcpy(glob_script_mem.glob_snp+(sindex*glob_script_mem.max_ssize),str,glob_script_mem.max_ssize);
- fvar=index;
- len=0;
+ strlcpy(glob_script_mem.glob_snp + (sindex * glob_script_mem.max_ssize), str, glob_script_mem.max_ssize);
+ fvar = index;
+ len = 0;
goto exit;
}
- if (!strncmp(vname,"fd(",3)) {
- lp+=3;
- char str[glob_script_mem.max_ssize+1];
- lp=GetStringResult(lp,OPER_EQU,str,0);
+ if (!strncmp(vname, "fd(", 3)) {
+ char str[glob_script_mem.max_ssize + 1];
+ lp = GetStringArgument(lp + 3, OPER_EQU, str, 0);
fsp->remove(str);
lp++;
- len=0;
+ len = 0;
goto exit;
}
#if defined(ESP32) && defined(USE_WEBCAM)
- if (!strncmp(vname,"fwp(",4)) {
- lp+=4;
- lp=GetNumericResult(lp,OPER_EQU,&fvar,0);
+ if (!strncmp(vname, "fwp(", 4)) {
+ lp = GetNumericArgument(lp + 4, OPER_EQU, &fvar, 0);
while (*lp==' ') lp++;
float fvar1;
- lp=GetNumericResult(lp,OPER_EQU,&fvar1,0);
- uint8_t ind=fvar1;
- if (ind>=SFS_MAX) ind=SFS_MAX-1;
+ lp = GetNumericArgument(lp, OPER_EQU, &fvar1, 0);
+ uint8_t ind = fvar1;
+ if (ind>=SFS_MAX) ind = SFS_MAX - 1;
if (glob_script_mem.file_flags[ind].is_open) {
uint8_t *buff;
- float maxps=WcGetPicstore(-1,0);
- if (fvar<1 || fvar>maxps) fvar=1;
- uint32_t len=WcGetPicstore(fvar-1, &buff);
+ float maxps = WcGetPicstore(-1, 0);
+ if (fvar<1 || fvar>maxps) fvar = 1;
+ uint32_t len = WcGetPicstore(fvar - 1, &buff);
if (len) {
//glob_script_mem.files[ind].seek(0,SeekEnd);
- fvar=glob_script_mem.files[ind].write(buff,len);
+ fvar = glob_script_mem.files[ind].write(buff, len);
} else {
- fvar=0;
+ fvar = 0;
}
//AddLog_P2(LOG_LEVEL_INFO, PSTR("picture save: %d"), len);
} else {
- fvar=0;
+ fvar = 0;
}
lp++;
- len=0;
+ len = 0;
goto exit;
}
-#endif
+#endif //ESP32 && USE_WEBCAM
#ifdef USE_SCRIPT_FATFS_EXT
- if (!strncmp(vname,"fe(",3)) {
- lp+=3;
- char str[glob_script_mem.max_ssize+1];
- lp=GetStringResult(lp,OPER_EQU,str,0);
+ if (!strncmp(vname, "fe(", 3)) {
+ char str[glob_script_mem.max_ssize + 1];
+ lp = GetStringArgument(lp + 3, OPER_EQU, str, 0);
// execute script
- File ef=fsp->open(str,FILE_READ);
+ File ef = fsp->open(str, FILE_READ);
if (ef) {
- uint16_t fsiz=ef.size();
+ uint16_t fsiz = ef.size();
if (fsiz<2048) {
- char *script=(char*)calloc(fsiz+16,1);
+ char *script = (char*)calloc(fsiz + 16, 1);
if (script) {
ef.read((uint8_t*)script,fsiz);
execute_script(script);
free(script);
- fvar=1;
+ fvar = 1;
}
}
ef.close();
}
lp++;
- len=0;
+ len = 0;
goto exit;
}
- if (!strncmp(vname,"fmd(",4)) {
- lp+=4;
- char str[glob_script_mem.max_ssize+1];
- lp=GetStringResult(lp,OPER_EQU,str,0);
- fvar=fsp->mkdir(str);
+ if (!strncmp(vname, "fmd(", 4)) {
+ char str[glob_script_mem.max_ssize + 1];
+ lp = GetStringArgument(lp + 4, OPER_EQU, str, 0);
+ fvar = fsp->mkdir(str);
lp++;
- len=0;
+ len = 0;
goto exit;
}
- if (!strncmp(vname,"frd(",4)) {
- lp+=4;
- char str[glob_script_mem.max_ssize+1];
- lp=GetStringResult(lp,OPER_EQU,str,0);
- fvar=fsp->rmdir(str);
+ if (!strncmp(vname, "frd(", 4)) {
+ char str[glob_script_mem.max_ssize + 1];
+ lp = GetStringArgument(lp + 4, OPER_EQU, str, 0);
+ fvar = fsp->rmdir(str);
lp++;
- len=0;
+ len = 0;
goto exit;
}
- if (!strncmp(vname,"fx(",3)) {
- lp+=3;
- char str[glob_script_mem.max_ssize+1];
- lp=GetStringResult(lp,OPER_EQU,str,0);
- if (fsp->exists(str)) fvar=1;
- else fvar=0;
+ if (!strncmp(vname, "fx(", 3)) {
+ char str[glob_script_mem.max_ssize + 1];
+ lp = GetStringArgument(lp + 3, OPER_EQU, str, 0);
+ if (fsp->exists(str)) fvar = 1;
+ else fvar = 0;
lp++;
- len=0;
+ len = 0;
goto exit;
}
#endif // USE_SCRIPT_FATFS_EXT
- if (!strncmp(vname,"fl1(",4) || !strncmp(vname,"fl2(",4) ) {
- uint8_t lknum=*(lp+2)&3;
- lp+=4;
- char str[glob_script_mem.max_ssize+1];
- lp=GetStringResult(lp,OPER_EQU,str,0);
- if (lknum<1 || lknum>2) lknum=1;
- strlcpy(glob_script_mem.flink[lknum-1],str,14);
+ if (!strncmp(vname, "fl1(", 4) || !strncmp(vname, "fl2(", 4) ) {
+ uint8_t lknum = *(lp+2)&3;
+ char str[glob_script_mem.max_ssize + 1];
+ lp = GetStringArgument(lp + 4, OPER_EQU, str, 0);
+ if (lknum<1 || lknum>2) lknum = 1;
+ strlcpy(glob_script_mem.flink[lknum - 1], str, 14);
lp++;
- fvar=0;
- len=0;
+ fvar = 0;
+ len = 0;
goto exit;
}
- if (!strncmp(vname,"fsm",3)) {
+ if (!strncmp(vname, "fsm", 3)) {
fvar=glob_script_mem.script_sd_found;
//card_init();
goto exit;
}
#endif //USE_SCRIPT_FATFS
- if (!strncmp(vname,"freq",4)) {
+ if (!strncmp(vname, "freq", 4)) {
#ifdef ESP32
- fvar=getCpuFrequencyMhz();
+ fvar = getCpuFrequencyMhz();
#else
- fvar=ESP.getCpuFreqMHz();
+ fvar = ESP.getCpuFreqMHz();
#endif
goto exit;
}
break;
case 'g':
- if (!strncmp(vname,"gtmp",4)) {
- fvar=global_temperature_celsius;
+ if (!strncmp(vname, "gtmp", 4)) {
+ fvar = global_temperature_celsius;
goto exit;
}
- if (!strncmp(vname,"ghum",4)) {
- fvar=global_humidity;
+ if (!strncmp(vname, "ghum", 4)) {
+ fvar = global_humidity;
goto exit;
}
- if (!strncmp(vname,"gprs",4)) {
- fvar=global_pressure_hpa;
+ if (!strncmp(vname, "gprs", 4)) {
+ fvar = global_pressure_hpa;
goto exit;
}
- if (!strncmp(vname,"gtopic",6)) {
- if (sp) strlcpy(sp,SettingsText(SET_MQTT_GRP_TOPIC),glob_script_mem.max_ssize);
+ if (!strncmp(vname, "gtopic", 6)) {
+ if (sp) strlcpy(sp, SettingsText(SET_MQTT_GRP_TOPIC), glob_script_mem.max_ssize);
goto strexit;
}
#ifdef SCRIPT_GET_HTTPS_JP
- if (!strncmp(vname,"gjp(",4)) {
+ if (!strncmp(vname, "gjp(", 4)) {
char host[SCRIPT_MAXSSIZE];
- lp=GetStringResult(lp+4,OPER_EQU,host,0);
+ lp = GetStringArgument(lp + 4, OPER_EQU, host, 0);
SCRIPT_SKIP_SPACES
char path[SCRIPT_MAXSSIZE];
- lp=GetStringResult(lp,OPER_EQU,path,0);
- fvar=call2https(host,path);
+ lp = GetStringArgument(lp, OPER_EQU, path, 0);
+ fvar = call2https(host, path);
lp++;
- len=0;
+ len = 0;
goto exit;
}
-#endif
+#endif //SCRIPT_GET_HTTPS_JP
break;
case 'h':
- if (!strncmp(vname,"hours",5)) {
- fvar=RtcTime.hour;
+ if (!strncmp(vname, "hours", 5)) {
+ fvar = RtcTime.hour;
goto exit;
}
- if (!strncmp(vname,"heap",4)) {
- fvar=ESP_getFreeHeap();
+ if (!strncmp(vname, "heap", 4)) {
+ fvar = ESP_getFreeHeap();
goto exit;
}
- if (!strncmp(vname,"hn(",3)) {
- lp=GetNumericResult(lp+3,OPER_EQU,&fvar,0);
- if (fvar<0 || fvar>255) fvar=0;
+ if (!strncmp(vname, "hn(", 3)) {
+ lp = GetNumericArgument(lp + 3, OPER_EQU, &fvar, 0);
+ if (fvar<0 || fvar>255) fvar = 0;
lp++;
- len=0;
+ len = 0;
if (sp) {
- sprintf(sp,"%02x",(uint8_t)fvar);
+ sprintf(sp, "%02x", (uint8_t)fvar);
}
goto strexit;
}
- if (!strncmp(vname,"hx(",3)) {
- lp=GetNumericResult(lp+3,OPER_EQU,&fvar,0);
+ if (!strncmp(vname, "hx(", 3)) {
+ lp = GetNumericArgument(lp + 3, OPER_EQU, &fvar, 0);
lp++;
- len=0;
+ len = 0;
if (sp) {
- sprintf(sp,"%08x",(uint32_t)fvar);
+ sprintf(sp, "%08x", (uint32_t)fvar);
}
goto strexit;
}
- if (!strncmp(vname,"hd(",3)) {
+ if (!strncmp(vname, "hd(", 3)) {
char str[SCRIPT_MAXSSIZE];
- lp=GetStringResult(lp+3,OPER_EQU,str,0);
- fvar=strtol(str,NULL,16);
+ lp = GetStringArgument(lp + 3, OPER_EQU, str, 0);
+ fvar = strtol(str, NULL, 16);
lp++;
- len=0;
+ len = 0;
goto exit;
}
#ifdef USE_LIGHT
-//#ifdef USE_WS2812
- if (!strncmp(vname,"hsvrgb(",7)) {
- lp=GetNumericResult(lp+7,OPER_EQU,&fvar,0);
- if (fvar<0 || fvar>360) fvar=0;
+ if (!strncmp(vname, "hsvrgb(", 7)) {
+ lp = GetNumericArgument(lp + 7, OPER_EQU, &fvar, 0);
+ if (fvar<0 || fvar>360) fvar = 0;
SCRIPT_SKIP_SPACES
// arg2
float fvar2;
- lp=GetNumericResult(lp,OPER_EQU,&fvar2,0);
- if (fvar2<0 || fvar2>100) fvar2=0;
+ lp = GetNumericArgument(lp, OPER_EQU, &fvar2, 0);
+ if (fvar2<0 || fvar2>100) fvar2 = 0;
SCRIPT_SKIP_SPACES
// arg3
float fvar3;
- lp=GetNumericResult(lp,OPER_EQU,&fvar3,0);
- if (fvar3<0 || fvar3>100) fvar3=0;
-
- fvar=HSVToRGB(fvar,fvar2,fvar3);
+ lp = GetNumericArgument(lp, OPER_EQU, &fvar3, 0);
+ if (fvar3<0 || fvar3>100) fvar3 = 0;
+ fvar = HSVToRGB(fvar, fvar2, fvar3);
lp++;
- len=0;
+ len = 0;
goto exit;
}
-//#endif
-#endif
+#endif //USE_LIGHT
break;
#define MAX_SARRAY_NUM 32
case 'i':
- if (!strncmp(vname,"int(",4)) {
- lp=GetNumericResult(lp+4,OPER_EQU,&fvar,0);
- fvar=floor(fvar);
+ if (!strncmp(vname, "int(", 4)) {
+ lp = GetNumericArgument(lp + 4, OPER_EQU, &fvar, 0);
+ fvar = floor(fvar);
lp++;
- len=0;
+ len = 0;
goto exit;
}
- if (!strncmp(vname,"is(",3)) {
- lp=GetNumericResult(lp+3,OPER_EQU,&fvar,0);
+ if (!strncmp(vname, "is(", 3)) {
+ lp = GetNumericArgument(lp + 3, OPER_EQU, &fvar, 0);
SCRIPT_SKIP_SPACES
if (*lp!='"') {
break;
@@ -2131,15 +2119,15 @@ chknext:
if (glob_script_mem.si_num>0 && glob_script_mem.last_index_string) {
free(glob_script_mem.last_index_string);
}
- char *sstart=lp;
- uint8_t slen=0;
- for (uint32_t cnt=0; cnt<256; cnt++) {
+ char *sstart = lp;
+ uint8_t slen = 0;
+ for (uint32_t cnt = 0; cnt<256; cnt++) {
if (*lp=='\n' || *lp=='"' || *lp==0) {
lp++;
if (cnt>0 && !slen) {
slen++;
}
- glob_script_mem.siro_num=slen;
+ glob_script_mem.siro_num = slen;
break;
}
if (*lp=='|') {
@@ -2151,31 +2139,31 @@ chknext:
glob_script_mem.si_num = fvar;
if (glob_script_mem.si_num>0) {
if (glob_script_mem.si_num>MAX_SARRAY_NUM) {
- glob_script_mem.si_num=MAX_SARRAY_NUM;
+ glob_script_mem.si_num = MAX_SARRAY_NUM;
}
- glob_script_mem.last_index_string=(char*)calloc(glob_script_mem.max_ssize*glob_script_mem.si_num,1);
- for (uint32_t cnt=0; cntglob_script_mem.si_num) {
- index=glob_script_mem.si_num;
+ index = glob_script_mem.si_num;
}
- strlcpy(str,glob_script_mem.last_index_string+(index*glob_script_mem.max_ssize),glob_script_mem.max_ssize);
+ strlcpy(str,glob_script_mem.last_index_string + (index * glob_script_mem.max_ssize), glob_script_mem.max_ssize);
}
}
lp++;
- if (sp) strlcpy(sp,str,glob_script_mem.max_ssize);
- len=0;
+ if (sp) strlcpy(sp, str, glob_script_mem.max_ssize);
+ len = 0;
goto strexit;
}
break;
case 'l':
- if (!strncmp(vname,"lip",3)) {
- if (sp) strlcpy(sp,(const char*)WiFi.localIP().toString().c_str(),glob_script_mem.max_ssize);
+ if (!strncmp(vname, "lip", 3)) {
+ if (sp) strlcpy(sp, (const char*)WiFi.localIP().toString().c_str(), glob_script_mem.max_ssize);
goto strexit;
}
#ifdef USE_SCRIPT_GLOBVARS
- if (!strncmp(vname,"luip",4)) {
- if (sp) strlcpy(sp,IPAddressToString(last_udp_ip),glob_script_mem.max_ssize);
+ if (!strncmp(vname, "luip", 4)) {
+ if (sp) strlcpy(sp, IPAddressToString(last_udp_ip), glob_script_mem.max_ssize);
goto strexit;
}
-#endif
- if (!strncmp(vname,"loglvl",6)) {
- fvar=glob_script_mem.script_loglevel;
- tind->index=SCRIPT_LOGLEVEL;
+#endif //USE_SCRIPT_GLOBVARS
+ if (!strncmp(vname, "loglvl", 6)) {
+ fvar = glob_script_mem.script_loglevel;
+ tind->index = SCRIPT_LOGLEVEL;
exit_settable:
- if (fp) *fp=fvar;
- *vtype=NTYPE;
- tind->bits.settable=1;
- tind->bits.is_string=0;
- return lp+len;
+ if (fp) *fp = fvar;
+ *vtype = NTYPE;
+ tind->bits.settable = 1;
+ tind->bits.is_string = 0;
+ return lp + len;
}
break;
case 'm':
- if (!strncmp(vname,"med(",4)) {
+ if (!strncmp(vname, "med(", 4)) {
float fvar1;
- lp=GetNumericResult(lp+4,OPER_EQU,&fvar1,0);
+ lp = GetNumericArgument(lp + 4, OPER_EQU, &fvar1, 0);
SCRIPT_SKIP_SPACES
// arg2
float fvar2;
- lp=GetNumericResult(lp,OPER_EQU,&fvar2,0);
- fvar=DoMedian5(fvar1,fvar2);
+ lp = GetNumericArgument(lp, OPER_EQU, &fvar2, 0);
+ fvar = DoMedian5(fvar1, fvar2);
lp++;
- len=0;
+ len = 0;
goto exit;
}
#ifdef USE_ANGLE_FUNC
- if (!strncmp(vname,"mpt(",4)) {
- lp=GetNumericResult(lp+4,OPER_EQU,&fvar,0);
- fvar=MeasurePulseTime(fvar);
+ if (!strncmp(vname, "mpt(", 4)) {
+ lp = GetNumericArgument(lp + 4, OPER_EQU, &fvar, 0);
+ fvar = MeasurePulseTime(fvar);
lp++;
- len=0;
+ len = 0;
goto exit;
}
-#endif
- if (!strncmp(vname,"micros",6)) {
- fvar=micros();
+#endif //USE_ANGLE_FUNC
+ if (!strncmp(vname, "micros", 6)) {
+ fvar = micros();
goto exit;
}
- if (!strncmp(vname,"millis",6)) {
- fvar=millis();
+ if (!strncmp(vname, "millis", 6)) {
+ fvar = millis();
goto exit;
}
- if (!strncmp(vname,"mins",4)) {
- fvar=RtcTime.minute;
+ if (!strncmp(vname, "mins", 4)) {
+ fvar = RtcTime.minute;
goto exit;
}
- if (!strncmp(vname,"month",5)) {
- fvar=RtcTime.month;
+ if (!strncmp(vname, "month", 5)) {
+ fvar = RtcTime.month;
goto exit;
}
- if (!strncmp(vname,"mqttc",5)) {
+ if (!strncmp(vname, "mqttc", 5)) {
if (rules_flag.mqtt_connected) {
- rules_flag.mqtt_connected=0;
- fvar=1;
+ rules_flag.mqtt_connected = 0;
+ fvar = 1;
}
goto exit;
}
- if (!strncmp(vname,"mqttd",5)) {
+ if (!strncmp(vname, "mqttd", 5)) {
if (rules_flag.mqtt_disconnected) {
- rules_flag.mqtt_disconnected=0;
- fvar=1;
+ rules_flag.mqtt_disconnected = 0;
+ fvar = 1;
}
goto exit;
}
- if (!strncmp(vname,"mqtts",5)) {
- fvar=!global_state.mqtt_down;
+ if (!strncmp(vname, "mqtts", 5)) {
+ fvar = !global_state.mqtt_down;
goto exit;
}
- if (!strncmp(vname,"mp(",3)) {
- lp+=3;
+ if (!strncmp(vname, "mp(", 3)) {
float fvar1;
- lp=GetNumericResult(lp,OPER_EQU,&fvar1,0);
+ lp = GetNumericArgument(lp + 3, OPER_EQU, &fvar1, 0);
SCRIPT_SKIP_SPACES
while (*lp!=')') {
- char *opp=lp;
+ char *opp = lp;
lp++;
float fvar2;
- lp=GetNumericResult(lp,OPER_EQU,&fvar2,0);
+ lp = GetNumericArgument(lp, OPER_EQU, &fvar2, 0);
SCRIPT_SKIP_SPACES
- fvar=fvar1;
+ fvar = fvar1;
if ((*opp=='<' && fvar1' && fvar1>fvar2) ||
- (*opp=='=' && fvar1==fvar2))
- {
- if (*lp!='<' && *lp!='>' && *lp!='=' && *lp!=')' && *lp!=SCRIPT_EOL) {
- float fvar3;
- lp=GetNumericResult(lp,OPER_EQU,&fvar3,0);
- SCRIPT_SKIP_SPACES
- fvar=fvar3;
- } else {
- fvar=fvar2;
- }
- break;
+ (*opp=='=' && fvar1==fvar2)) {
+ if (*lp!='<' && *lp!='>' && *lp!='=' && *lp!=')' && *lp!=SCRIPT_EOL) {
+ float fvar3;
+ lp = GetNumericArgument(lp, OPER_EQU, &fvar3, 0);
+ SCRIPT_SKIP_SPACES
+ fvar=fvar3;
+ } else {
+ fvar = fvar2;
+ }
+ break;
}
while (*lp!='<' && *lp!='>' && *lp!='=' && *lp!=')' && *lp!=SCRIPT_EOL) lp++;
}
- len=0;
+ len = 0;
goto exit;
}
#ifdef USE_MORITZ
- if (!strncmp(vname,"mo(",3)) {
+ if (!strncmp(vname, "mo(", 3)) {
float fvar1;
- lp=GetNumericResult(lp+3,OPER_EQU,&fvar1,0);
+ lp = GetNumericArgument(lp + 3, OPER_EQU, &fvar1, 0);
SCRIPT_SKIP_SPACES
float fvar2;
- lp=GetNumericResult(lp,OPER_EQU,&fvar2,0);
+ lp = GetNumericArgument(lp, OPER_EQU, &fvar2, 0);
SCRIPT_SKIP_SPACES
char rbuff[64];
- fvar=mo_getvars(fvar1,fvar2,rbuff);
+ fvar = mo_getvars(fvar1, fvar2, rbuff);
lp++;
- if (sp) strlcpy(sp,rbuff,glob_script_mem.max_ssize);
- len=0;
+ if (sp) strlcpy(sp, rbuff, glob_script_mem.max_ssize);
+ len = 0;
goto strexit;
}
-#endif
+#endif //USE_MORITZ
break;
case 'p':
- if (!strncmp(vname,"pin[",4)) {
+ if (!strncmp(vname, "pin[", 4)) {
// raw pin level
- GetNumericResult(vname+4,OPER_EQU,&fvar,0);
- fvar=digitalRead((uint8_t)fvar);
+ GetNumericArgument(vname + 4, OPER_EQU, &fvar, 0);
+ fvar = digitalRead((uint8_t)fvar);
// skip ] bracket
len++;
goto exit;
}
- if (!strncmp(vname,"pn[",3)) {
- GetNumericResult(vname+3,OPER_EQU,&fvar,0);
- fvar=Pin(fvar);
+ if (!strncmp(vname, "pn[", 3)) {
+ GetNumericArgument(vname + 3, OPER_EQU, &fvar, 0);
+ fvar = Pin(fvar);
// skip ] bracket
len++;
goto exit;
}
#if defined(ESP32) && (defined(USE_I2S_AUDIO) || defined(USE_TTGO_WATCH))
- if (!strncmp(vname,"pl(",3)) {
+ if (!strncmp(vname, "pl(", 3)) {
char path[SCRIPT_MAXSSIZE];
- lp=GetStringResult(lp+3,OPER_EQU,path,0);
+ lp = GetStringArgument(lp + 3, OPER_EQU, path, 0);
Play_mp3(path);
len++;
- len=0;
+ len = 0;
goto exit;
}
#endif // USE_I2S_AUDIO
- if (!strncmp(vname,"pd[",3)) {
- GetNumericResult(vname+3,OPER_EQU,&fvar,0);
- uint8_t gpiopin=fvar;
+ if (!strncmp(vname, "pd[", 3)) {
+ GetNumericArgument(vname + 3, OPER_EQU, &fvar, 0);
+ uint8_t gpiopin = fvar;
/*
for (uint8_t i=0;iMAX_COUNTERS) index=1;
- fvar=RtcSettings.pulse_counter[index-1];
- len+=1;
+ if (!strncmp(vname, "pc[", 3)) {
+ GetNumericArgument(vname + 3, OPER_EQU, &fvar, 0);
+ uint8_t index = fvar;
+ if (index<1 || index>MAX_COUNTERS) index = 1;
+ fvar = RtcSettings.pulse_counter[index - 1];
+ len += 1;
goto exit;
}
break;
case 'r':
- if (!strncmp(vname,"ram",3)) {
- fvar=glob_script_mem.script_mem_size+(glob_script_mem.script_size)+(PMEM_SIZE);
+ if (!strncmp(vname, "ram", 3)) {
+ fvar = glob_script_mem.script_mem_size + (glob_script_mem.script_size) + (PMEM_SIZE);
goto exit;
}
- if (!strncmp(vname,"rnd(",4)) {
+ if (!strncmp(vname, "rnd(", 4)) {
// tasmota switch state
- GetNumericResult(vname+4,OPER_EQU,&fvar,0);
+ GetNumericArgument(vname + 4, OPER_EQU, &fvar, 0);
if (fvar<0) {
randomSeed(-fvar);
- fvar=0;
+ fvar = 0;
} else {
- fvar=random(fvar);
+ fvar = random(fvar);
}
// skip ] bracket
len++;
@@ -2444,81 +2429,78 @@ chknext:
}
break;
case 's':
- if (!strncmp(vname,"secs",4)) {
- fvar=RtcTime.second;
+ if (!strncmp(vname, "secs", 4)) {
+ fvar = RtcTime.second;
goto exit;
}
- if (!strncmp(vname,"sw[",3)) {
+ if (!strncmp(vname, "sw[", 3)) {
// tasmota switch state
- GetNumericResult(vname+3,OPER_EQU,&fvar,0);
- fvar=SwitchLastState((uint32_t)fvar);
+ GetNumericArgument(vname + 3, OPER_EQU, &fvar, 0);
+ fvar = SwitchLastState((uint32_t)fvar);
// skip ] bracket
len++;
goto exit;
}
- if (!strncmp(vname,"stack",5)) {
- fvar=GetStack();
+ if (!strncmp(vname, "stack", 5)) {
+ fvar = GetStack();
goto exit;
}
- if (!strncmp(vname,"slen",4)) {
- fvar=strlen(glob_script_mem.script_ram);
+ if (!strncmp(vname, "slen", 4)) {
+ fvar = strlen(glob_script_mem.script_ram);
goto exit;
}
- if (!strncmp(vname,"sl(",3)) {
- lp+=3;
+ if (!strncmp(vname, "sl(", 3)) {
char str[SCRIPT_MAXSSIZE];
- lp=GetStringResult(lp,OPER_EQU,str,0);
+ lp = GetStringArgument(lp + 3, OPER_EQU, str, 0);
lp++;
- len=0;
- fvar=strlen(str);
+ len = 0;
+ fvar = strlen(str);
goto exit;
}
- if (!strncmp(vname,"sb(",3)) {
- lp+=3;
+ if (!strncmp(vname, "sb(", 3)) {
char str[SCRIPT_MAXSSIZE];
- lp=GetStringResult(lp,OPER_EQU,str,0);
+ lp = GetStringArgument(lp + 3, OPER_EQU, str, 0);
SCRIPT_SKIP_SPACES
float fvar1;
- lp=GetNumericResult(lp,OPER_EQU,&fvar1,0);
+ lp = GetNumericArgument(lp, OPER_EQU, &fvar1, 0);
SCRIPT_SKIP_SPACES
float fvar2;
- lp=GetNumericResult(lp,OPER_EQU,&fvar2,0);
+ lp = GetNumericArgument(lp, OPER_EQU, &fvar2, 0);
lp++;
- len=0;
+ len = 0;
if (fvar1<0) {
- fvar1=strlen(str)+fvar1;
+ fvar1 = strlen(str) + fvar1;
}
- memcpy(sp,&str[(uint8_t)fvar1],(uint8_t)fvar2);
+ memcpy(sp, &str[(uint8_t)fvar1], (uint8_t)fvar2);
sp[(uint8_t)fvar2] = '\0';
goto strexit;
}
- if (!strncmp(vname,"st(",3)) {
- lp+=3;
+ if (!strncmp(vname, "st(", 3)) {
char str[SCRIPT_MAXSSIZE];
- lp=GetStringResult(lp,OPER_EQU,str,0);
+ lp = GetStringArgument(lp + 3, OPER_EQU, str, 0);
while (*lp==' ') lp++;
char token[2];
- token[0]=*lp++;
- token[1]=0;
+ token[0] = *lp++;
+ token[1] = 0;
while (*lp==' ') lp++;
- lp=GetNumericResult(lp,OPER_EQU,&fvar,0);
+ lp = GetNumericArgument(lp, OPER_EQU, &fvar, 0);
// skip ) bracket
lp++;
- len=0;
+ len = 0;
if (sp) {
// get stringtoken
- char *st=strtok(str,token);
+ char *st = strtok(str, token);
if (!st) {
- *sp=0;
+ *sp = 0;
} else {
- for (uint8_t cnt=1; cnt<=fvar; cnt++) {
+ for (uint8_t cnt = 1; cnt<=fvar; cnt++) {
if (cnt==fvar) {
- strcpy(sp,st);
+ strcpy(sp, st);
break;
}
- st=strtok(NULL,token);
+ st = strtok(NULL, token);
if (!st) {
- *sp=0;
+ *sp = 0;
break;
}
}
@@ -2526,249 +2508,237 @@ chknext:
}
goto strexit;
}
- if (!strncmp(vname,"s(",2)) {
- lp+=2;
- lp=GetNumericResult(lp,OPER_EQU,&fvar,0);
- char str[glob_script_mem.max_ssize+1];
- dtostrfd(fvar,glob_script_mem.script_dprec,str);
- if (sp) strlcpy(sp,str,glob_script_mem.max_ssize);
+ if (!strncmp(vname, "s(", 2)) {
+ lp = GetNumericArgument(lp + 2, OPER_EQU, &fvar, 0);
+ char str[glob_script_mem.max_ssize + 1];
+ dtostrfd(fvar, glob_script_mem.script_dprec, str);
+ if (sp) strlcpy(sp, str, glob_script_mem.max_ssize);
lp++;
- len=0;
+ len = 0;
goto strexit;
}
#if defined(ESP32) && (defined(USE_I2S_AUDIO) || defined(USE_TTGO_WATCH))
- if (!strncmp(vname,"say(",4)) {
+ if (!strncmp(vname, "say(", 4)) {
char text[SCRIPT_MAXSSIZE];
- lp=GetStringResult(lp+4,OPER_EQU,text,0);
+ lp = GetStringArgument(lp + 4, OPER_EQU, text, 0);
Say(text);
len++;
- len=0;
+ len = 0;
goto exit;
}
#endif // USE_I2S_AUDIO
#ifdef ESP32
- if (!strncmp(vname,"sf(",3)) {
- lp+=2;
- lp=GetNumericResult(lp,OPER_EQU,&fvar,0);
- if (fvar<80) fvar=80;
- if (fvar>240) fvar=240;
+ if (!strncmp(vname, "sf(", 3)) {
+ lp = GetNumericArgument(lp + 3, OPER_EQU, &fvar, 0);
+ if (fvar<80) fvar = 80;
+ if (fvar>240) fvar = 240;
setCpuFrequencyMhz(fvar);
- fvar=getCpuFrequencyMhz();
+ fvar = getCpuFrequencyMhz();
lp++;
- len=0;
+ len = 0;
goto exit;
}
-#endif
+#endif //ESP32
#ifdef USE_TTGO_WATCH
- if (!strncmp(vname,"slp(",4)) {
- lp=GetNumericResult(lp+4,OPER_EQU,&fvar,0);
+ if (!strncmp(vname, "slp(", 4)) {
+ lp = GetNumericArgument(lp + 4, OPER_EQU, &fvar, 0);
SCRIPT_SKIP_SPACES
TTGO_Sleep(fvar);
lp++;
- len=0;
+ len = 0;
goto exit;
}
-#endif
+#endif //USE_TTGO_WATCH
#if defined(USE_TIMERS) && defined(USE_SUNRISE)
- if (!strncmp(vname,"sunrise",7)) {
- fvar=SunMinutes(0);
+ if (!strncmp(vname, "sunrise", 7)) {
+ fvar = SunMinutes(0);
goto exit;
}
- if (!strncmp(vname,"sunset",6)) {
- fvar=SunMinutes(1);
+ if (!strncmp(vname, "sunset", 6)) {
+ fvar = SunMinutes(1);
goto exit;
}
-#endif
+#endif //USE_TIMERS
#ifdef USE_SHUTTER
- if (!strncmp(vname,"sht[",4)) {
- GetNumericResult(vname+4,OPER_EQU,&fvar,0);
- uint8_t index=fvar;
+ if (!strncmp(vname, "sht[", 4)) {
+ GetNumericArgument(vname + 4, OPER_EQU, &fvar, 0);
+ uint8_t index = fvar;
if (index<=shutters_present) {
- fvar=Settings.shutter_position[index-1];
+ fvar = Settings.shutter_position[index - 1];
} else {
- fvar=-1;
+ fvar = -1;
}
- len+=1;
+ len += 1;
goto exit;
}
-#endif
+#endif //USE_SHUTTER
#ifdef USE_ANGLE_FUNC
- if (!strncmp(vname,"sin(",4)) {
- lp+=4;
- lp=GetNumericResult(lp,OPER_EQU,&fvar,0);
- fvar=sinf(fvar);
+ if (!strncmp(vname, "sin(", 4)) {
+ lp = GetNumericArgument(lp + 4, OPER_EQU, &fvar, 0);
+ fvar = sinf(fvar);
lp++;
- len=0;
+ len = 0;
goto exit;
}
- if (!strncmp(vname,"sqrt(",5)) {
- lp+=5;
- lp=GetNumericResult(lp,OPER_EQU,&fvar,0);
- fvar=sqrtf(fvar);
+ if (!strncmp(vname, "sqrt(", 5)) {
+ lp = GetNumericArgument(lp + 5, OPER_EQU, &fvar, 0);
+ fvar = sqrtf(fvar);
lp++;
- len=0;
+ len = 0;
goto exit;
}
-#endif
+#endif //USE_ANGLE_FUNC
#if defined(USE_SML_M) && defined (USE_SML_SCRIPT_CMD)
- if (!strncmp(vname,"sml[",4)) {
- lp+=4;
- lp=GetNumericResult(lp,OPER_EQU,&fvar,0);
+ if (!strncmp(vname, "sml[", 4)) {
+ lp = GetNumericArgument(lp + 4, OPER_EQU, &fvar, 0);
SCRIPT_SKIP_SPACES
- fvar=SML_GetVal(fvar);
+ fvar = SML_GetVal(fvar);
lp++;
- len=0;
+ len = 0;
goto exit;
}
- if (!strncmp(vname,"sml(",4)) {
- lp+=4;
+ if (!strncmp(vname, "sml(", 4)) {
float fvar1;
- lp=GetNumericResult(lp,OPER_EQU,&fvar1,0);
+ lp = GetNumericArgument(lp + 4, OPER_EQU, &fvar1, 0);
SCRIPT_SKIP_SPACES
float fvar2;
- lp=GetNumericResult(lp,OPER_EQU,&fvar2,0);
+ lp = GetNumericArgument(lp, OPER_EQU, &fvar2, 0);
SCRIPT_SKIP_SPACES
if (fvar2==0) {
float fvar3;
- lp=GetNumericResult(lp,OPER_EQU,&fvar3,0);
- fvar=SML_SetBaud(fvar1,fvar3);
+ lp = GetNumericArgument(lp, OPER_EQU, &fvar3, 0);
+ fvar = SML_SetBaud(fvar1, fvar3);
} else if (fvar2==1) {
char str[SCRIPT_MAXSSIZE];
- lp=GetStringResult(lp,OPER_EQU,str,0);
- fvar=SML_Write(fvar1,str);
+ lp = GetStringArgument(lp, OPER_EQU, str, 0);
+ fvar = SML_Write(fvar1, str);
} else if (fvar2==2) {
char str[SCRIPT_MAXSSIZE];
- str[0]=0;
- fvar=SML_Read(fvar1,str,SCRIPT_MAXSSIZE);
- if (sp) strlcpy(sp,str,glob_script_mem.max_ssize);
+ str[0] = 0;
+ fvar = SML_Read(fvar1, str, SCRIPT_MAXSSIZE);
+ if (sp) strlcpy(sp, str, glob_script_mem.max_ssize);
lp++;
- len=0;
+ len = 0;
goto strexit;
} else {
#ifdef ED300L
- fvar=SML_Status(fvar1);
+ fvar = SML_Status(fvar1);
#else
- fvar=0;
-#endif
+ fvar = 0;
+#endif //ED300L
}
lp++;
- len=0;
+ len = 0;
goto exit;
}
-#endif
+#endif //USE_SML_M
break;
case 't':
- if (!strncmp(vname,"time",4)) {
- fvar=MinutesPastMidnight();
+ if (!strncmp(vname, "time", 4)) {
+ fvar = MinutesPastMidnight();
goto exit;
}
- if (!strncmp(vname,"tper",4)) {
- fvar=Settings.tele_period;
- tind->index=SCRIPT_TELEPERIOD;
+ if (!strncmp(vname, "tper", 4)) {
+ fvar = Settings.tele_period;
+ tind->index = SCRIPT_TELEPERIOD;
goto exit_settable;
}
- if (!strncmp(vname,"tinit",5)) {
- if (rules_flag.time_init) {
- rules_flag.time_init=0;
- fvar=1;
- }
+ if (!strncmp(vname, "tinit", 5)) {
+ fvar = rules_flag.time_init;
goto exit;
}
- if (!strncmp(vname,"tset",4)) {
- if (rules_flag.time_set) {
- rules_flag.time_set=0;
- fvar=1;
- }
+ if (!strncmp(vname, "tset", 4)) {
+ fvar = rules_flag.time_set;
goto exit;
}
- if (!strncmp(vname,"tstamp",6)) {
- if (sp) strlcpy(sp,GetDateAndTime(DT_LOCAL).c_str(),glob_script_mem.max_ssize);
+ if (!strncmp(vname, "tstamp", 6)) {
+ if (sp) strlcpy(sp, GetDateAndTime(DT_LOCAL).c_str(), glob_script_mem.max_ssize);
goto strexit;
}
- if (!strncmp(vname,"topic",5)) {
- if (sp) strlcpy(sp,SettingsText(SET_MQTT_TOPIC),glob_script_mem.max_ssize);
+ if (!strncmp(vname, "topic", 5)) {
+ if (sp) strlcpy(sp, SettingsText(SET_MQTT_TOPIC), glob_script_mem.max_ssize);
goto strexit;
}
#ifdef USE_SCRIPT_TIMER
- if (!strncmp(vname,"ts1(",4)) {
- lp=GetNumericResult(lp+4,OPER_EQU,&fvar,0);
- if (fvar<10) fvar=10;
+ if (!strncmp(vname, "ts1(", 4)) {
+ lp = GetNumericArgument(lp + 4, OPER_EQU, &fvar, 0);
+ if (fvar<10) fvar = 10;
Script_ticker1.attach_ms(fvar, Script_ticker1_end);
lp++;
- len=0;
+ len = 0;
goto exit;
}
- if (!strncmp(vname,"ts2(",4)) {
- lp=GetNumericResult(lp+4,OPER_EQU,&fvar,0);
- if (fvar<10) fvar=10;
+ if (!strncmp(vname, "ts2(", 4)) {
+ lp = GetNumericArgument(lp + 4, OPER_EQU, &fvar, 0);
+ if (fvar<10) fvar = 10;
Script_ticker2.attach_ms(fvar, Script_ticker2_end);
lp++;
- len=0;
+ len = 0;
goto exit;
}
- if (!strncmp(vname,"ts3(",4)) {
- lp=GetNumericResult(lp+4,OPER_EQU,&fvar,0);
- if (fvar<10) fvar=10;
+ if (!strncmp(vname, "ts3(", 4)) {
+ lp = GetNumericArgument(lp + 4, OPER_EQU, &fvar, 0);
+ if (fvar<10) fvar = 10;
Script_ticker3.attach_ms(fvar, Script_ticker3_end);
lp++;
- len=0;
+ len = 0;
goto exit;
}
- if (!strncmp(vname,"ts4(",4)) {
- lp=GetNumericResult(lp+4,OPER_EQU,&fvar,0);
- if (fvar<10) fvar=10;
+ if (!strncmp(vname, "ts4(", 4)) {
+ lp = GetNumericArgument(lp + 4, OPER_EQU, &fvar, 0);
+ if (fvar<10) fvar = 10;
Script_ticker4.attach_ms(fvar, Script_ticker4_end);
lp++;
- len=0;
+ len = 0;
goto exit;
}
#endif // USE_SCRIPT_TIMER
#ifdef USE_DISPLAY
#ifdef USE_TOUCH_BUTTONS
- if (!strncmp(vname,"tbut[",5)) {
- GetNumericResult(vname+5,OPER_EQU,&fvar,0);
- uint8_t index=fvar;
- if (index<1 || index>MAXBUTTONS) index=1;
+ if (!strncmp(vname, "tbut[", 5)) {
+ GetNumericArgument(vname + 5, OPER_EQU, &fvar, 0);
+ uint8_t index = fvar;
+ if (index<1 || index>MAXBUTTONS) index = 1;
index--;
if (buttons[index]) {
- fvar=buttons[index]->vpower.on_off;
+ fvar = buttons[index]->vpower.on_off;
} else {
- fvar=-1;
+ fvar = -1;
}
- len+=1;
+ len += 1;
goto exit;
}
-#endif
-#endif
+#endif //USE_TOUCH_BUTTONS
+#endif //USE_DISPLAY
break;
case 'u':
- if (!strncmp(vname,"uptime",6)) {
- fvar=MinutesUptime();
+ if (!strncmp(vname, "uptime", 6)) {
+ fvar = MinutesUptime();
goto exit;
}
- if (!strncmp(vname,"upsecs",6)) {
- fvar=uptime;
+ if (!strncmp(vname, "upsecs", 6)) {
+ fvar = uptime;
goto exit;
}
- if (!strncmp(vname,"upd[",4)) {
+ if (!strncmp(vname, "upd[", 4)) {
// var was updated
struct T_INDEX ind;
uint8_t vtype;
- isvar(vname+4,&vtype,&ind,0,0,0);
+ isvar(vname + 4, &vtype, &ind, 0, 0, 0);
if (!ind.bits.constant) {
if (!ind.bits.changed) {
- fvar=0;
+ fvar = 0;
len++;
goto exit;
} else {
- glob_script_mem.type[ind.index].bits.changed=0;
- fvar=1;
+ glob_script_mem.type[ind.index].bits.changed = 0;
+ fvar = 1;
len++;
goto exit;
}
@@ -2779,112 +2749,111 @@ chknext:
case 'w':
#if defined(ESP32) && defined(USE_WEBCAM)
- if (!strncmp(vname,"wc(",3)) {
- lp+=3;
+ if (!strncmp(vname, "wc(", 3)) {
float fvar1;
- lp=GetNumericResult(lp,OPER_EQU,&fvar1,0);
+ lp = GetNumericArgument(lp + 3, OPER_EQU, &fvar1, 0);
SCRIPT_SKIP_SPACES
switch ((uint32)fvar1) {
case 0:
{ float fvar2;
- lp=GetNumericResult(lp,OPER_EQU,&fvar2,0);
- fvar=WcSetup(fvar2);
+ lp = GetNumericArgument(lp, OPER_EQU, &fvar2, 0);
+ fvar = WcSetup(fvar2);
}
break;
case 1:
{ float fvar2;
- lp=GetNumericResult(lp,OPER_EQU,&fvar2,0);
- fvar=WcGetFrame(fvar2);
+ lp = GetNumericArgument(lp, OPER_EQU, &fvar2, 0);
+ fvar = WcGetFrame(fvar2);
}
break;
case 2:
{ float fvar2,fvar3;
- lp=GetNumericResult(lp,OPER_EQU,&fvar2,0);
+ lp = GetNumericArgument(lp, OPER_EQU, &fvar2, 0);
SCRIPT_SKIP_SPACES
- lp=GetNumericResult(lp,OPER_EQU,&fvar3,0);
- fvar=WcSetOptions(fvar2,fvar3);
+ lp = GetNumericArgument(lp, OPER_EQU, &fvar3, 0);
+ fvar = WcSetOptions(fvar2, fvar3);
}
break;
case 3:
- fvar=WcGetWidth();
+ fvar = WcGetWidth();
break;
case 4:
- fvar=WcGetHeight();
+ fvar = WcGetHeight();
break;
case 5:
{ float fvar2;
- lp=GetNumericResult(lp,OPER_EQU,&fvar2,0);
- fvar=WcSetStreamserver(fvar2);
+ lp = GetNumericArgument(lp, OPER_EQU, &fvar2, 0);
+ fvar = WcSetStreamserver(fvar2);
}
break;
case 6:
{ float fvar2;
- lp=GetNumericResult(lp,OPER_EQU,&fvar2,0);
- fvar=WcSetMotionDetect(fvar2);
+ lp = GetNumericArgument(lp, OPER_EQU, &fvar2, 0);
+ fvar = WcSetMotionDetect(fvar2);
}
break;
#ifdef USE_FACE_DETECT
case 7:
{ float fvar2;
- lp=GetNumericResult(lp,OPER_EQU,&fvar2,0);
- fvar=WcSetFaceDetect(fvar2);
+ lp = GetNumericArgument(lp, OPER_EQU, &fvar2, 0);
+ fvar = WcSetFaceDetect(fvar2);
}
break;
-#endif
+#endif //USE_FACE_DETECT
default:
- fvar=0;
+ fvar = 0;
}
lp++;
- len=0;
+ len = 0;
goto exit;
}
#endif //ESP32, USE_WEBCAM
#if defined(USE_TTGO_WATCH) && defined(USE_BMA423)
- if (!strncmp(vname,"wdclk",5)) {
- fvar=TTGO_doubleclick();
+ if (!strncmp(vname, "wdclk", 5)) {
+ fvar = TTGO_doubleclick();
goto exit;
}
- if (!strncmp(vname,"wbut",4)) {
- fvar=TTGO_button();
+ if (!strncmp(vname, "wbut", 4)) {
+ fvar = TTGO_button();
goto exit;
}
#endif // USE_TTGO_WATCH
#if defined(USE_TTGO_WATCH) && defined(USE_FT5206)
- if (!strncmp(vname,"wtch(",5)) {
- lp=GetNumericResult(lp+5,OPER_EQU,&fvar,0);
- fvar=Touch_Status(fvar);
+ if (!strncmp(vname, "wtch(", 5)) {
+ lp = GetNumericArgument(lp + 5, OPER_EQU, &fvar, 0);
+ fvar = Touch_Status(fvar);
lp++;
- len=0;
+ len = 0;
goto exit;
}
#endif // USE_FT5206
- if (!strncmp(vname,"wday",4)) {
- fvar=RtcTime.day_of_week;
+ if (!strncmp(vname, "wday", 4)) {
+ fvar = RtcTime.day_of_week;
goto exit;
}
- if (!strncmp(vname,"wific",5)) {
+ if (!strncmp(vname, "wific", 5)) {
if (rules_flag.wifi_connected) {
- rules_flag.wifi_connected=0;
- fvar=1;
+ rules_flag.wifi_connected = 0;
+ fvar = 1;
}
goto exit;
}
- if (!strncmp(vname,"wifid",5)) {
+ if (!strncmp(vname, "wifid", 5)) {
if (rules_flag.wifi_disconnected) {
- rules_flag.wifi_disconnected=0;
- fvar=1;
+ rules_flag.wifi_disconnected = 0;
+ fvar = 1;
}
goto exit;
}
- if (!strncmp(vname,"wifis",5)) {
- fvar=!global_state.wifi_down;
+ if (!strncmp(vname, "wifis", 5)) {
+ fvar = !global_state.wifi_down;
goto exit;
}
break;
case 'y':
- if (!strncmp(vname,"year",4)) {
- fvar=RtcTime.year;
+ if (!strncmp(vname, "year", 4)) {
+ fvar = RtcTime.year;
goto exit;
}
break;
@@ -2894,23 +2863,23 @@ chknext:
// nothing valid found
notfound:
if (fp) *fp=0;
- *vtype=VAR_NV;
- tind->index=VAR_NV;
- glob_script_mem.var_not_found=1;
+ *vtype = VAR_NV;
+ tind->index = VAR_NV;
+ glob_script_mem.var_not_found = 1;
return lp;
// return constant numbers
exit:
- if (fp) *fp=fvar;
- *vtype=NUM_RES;
- tind->bits.constant=1;
- tind->bits.is_string=0;
- return lp+len;
+ if (fp) *fp = fvar;
+ *vtype = NUM_RES;
+ tind->bits.constant = 1;
+ tind->bits.is_string = 0;
+ return lp + len;
// return constant strings
strexit:
- *vtype=STYPE;
- tind->bits.constant=1;
- tind->bits.is_string=1;
- return lp+len;
+ *vtype = STYPE;
+ tind->bits.constant = 1;
+ tind->bits.is_string = 1;
+ return lp + len;
}
@@ -2918,113 +2887,113 @@ strexit:
char *getop(char *lp, uint8_t *operand) {
switch (*lp) {
case '=':
- if (*(lp+1)=='=') {
- *operand=OPER_EQUEQU;
- return lp+2;
+ if (*(lp + 1)=='=') {
+ *operand = OPER_EQUEQU;
+ return lp + 2;
} else {
- *operand=OPER_EQU;
- return lp+1;
+ *operand = OPER_EQU;
+ return lp + 1;
}
break;
case '+':
- if (*(lp+1)=='=') {
- *operand=OPER_PLSEQU;
- return lp+2;
+ if (*(lp + 1)=='=') {
+ *operand = OPER_PLSEQU;
+ return lp + 2;
} else {
- *operand=OPER_PLS;
- return lp+1;
+ *operand = OPER_PLS;
+ return lp + 1;
}
break;
case '-':
- if (*(lp+1)=='=') {
- *operand=OPER_MINEQU;
- return lp+2;
+ if (*(lp + 1)=='=') {
+ *operand = OPER_MINEQU;
+ return lp + 2;
} else {
- *operand=OPER_MIN;
- return lp+1;
+ *operand = OPER_MIN;
+ return lp + 1;
}
break;
case '*':
- if (*(lp+1)=='=') {
- *operand=OPER_MULEQU;
- return lp+2;
+ if (*(lp + 1)=='=') {
+ *operand = OPER_MULEQU;
+ return lp + 2;
} else {
- *operand=OPER_MUL;
- return lp+1;
+ *operand = OPER_MUL;
+ return lp + 1;
}
break;
case '/':
- if (*(lp+1)=='=') {
- *operand=OPER_DIVEQU;
- return lp+2;
+ if (*(lp + 1)=='=') {
+ *operand = OPER_DIVEQU;
+ return lp + 2;
} else {
- *operand=OPER_DIV;
- return lp+1;
+ *operand = OPER_DIV;
+ return lp + 1;
}
break;
case '!':
- if (*(lp+1)=='=') {
- *operand=OPER_NOTEQU;
- return lp+2;
+ if (*(lp + 1)=='=') {
+ *operand = OPER_NOTEQU;
+ return lp + 2;
}
break;
case '>':
- if (*(lp+1)=='=') {
- *operand=OPER_GRTEQU;
- return lp+2;
+ if (*(lp + 1)=='=') {
+ *operand = OPER_GRTEQU;
+ return lp + 2;
} else {
- *operand=OPER_GRT;
- return lp+1;
+ *operand = OPER_GRT;
+ return lp + 1;
}
break;
case '<':
- if (*(lp+1)=='=') {
- *operand=OPER_LOWEQU;
- return lp+2;
+ if (*(lp + 1)=='=') {
+ *operand = OPER_LOWEQU;
+ return lp + 2;
} else {
- *operand=OPER_LOW;
- return lp+1;
+ *operand = OPER_LOW;
+ return lp + 1;
}
break;
case '%':
- if (*(lp+1)=='=') {
- *operand=OPER_PERCEQU;
- return lp+2;
+ if (*(lp + 1)=='=') {
+ *operand = OPER_PERCEQU;
+ return lp + 2;
} else {
- *operand=OPER_PERC;
- return lp+1;
+ *operand = OPER_PERC;
+ return lp + 1;
}
break;
case '^':
- if (*(lp+1)=='=') {
- *operand=OPER_XOREQU;
- return lp+2;
+ if (*(lp + 1)=='=') {
+ *operand = OPER_XOREQU;
+ return lp + 2;
} else {
- *operand=OPER_XOR;
- return lp+1;
+ *operand = OPER_XOR;
+ return lp + 1;
}
break;
case '&':
- if (*(lp+1)=='=') {
- *operand=OPER_ANDEQU;
- return lp+2;
+ if (*(lp + 1)=='=') {
+ *operand = OPER_ANDEQU;
+ return lp + 2;
} else {
- *operand=OPER_AND;
- return lp+1;
+ *operand = OPER_AND;
+ return lp + 1;
}
break;
case '|':
- if (*(lp+1)=='=') {
- *operand=OPER_OREQU;
- return lp+2;
+ if (*(lp + 1)=='=') {
+ *operand = OPER_OREQU;
+ return lp + 2;
} else {
- *operand=OPER_OR;
- return lp+1;
+ *operand = OPER_OR;
+ return lp + 1;
}
break;
}
- *operand=0;
+ *operand = 0;
return lp;
}
@@ -3043,31 +3012,31 @@ uint16_t GetStack(void) {
register uint8_t *sp asm("a1");
return (sp - pxTaskGetStackStart(NULL));
}
-#endif
+#endif //ESP8266
-char *GetStringResult(char *lp,uint8_t lastop,char *cp,JsonObject *jo) {
- uint8_t operand=0;
+char *GetStringArgument(char *lp, uint8_t lastop, char *cp, JsonObject *jo) {
+ uint8_t operand = 0;
uint8_t vtype;
char *slp;
struct T_INDEX ind;
char str[SCRIPT_MAXSSIZE],str1[SCRIPT_MAXSSIZE];
while (1) {
- lp=isvar(lp,&vtype,&ind,0,str1,jo);
- if (vtype!=STR_RES && !(vtype&STYPE)) {
+ lp=isvar(lp, &vtype, &ind, 0, str1, jo);
+ if (vtype!=STR_RES && !(vtype & STYPE)) {
// numeric type
- glob_script_mem.glob_error=1;
+ glob_script_mem.glob_error = 1;
return lp;
}
switch (lastop) {
case OPER_EQU:
- strlcpy(str,str1,sizeof(str));
+ strlcpy(str, str1, sizeof(str));
break;
case OPER_PLS:
- strncat(str,str1,sizeof(str)-strlen(str1));
+ strncat(str, str1, sizeof(str) - strlen(str1));
break;
}
- slp=lp;
- lp=getop(lp,&operand);
+ slp = lp;
+ lp = getop(lp, &operand);
switch (operand) {
case OPER_EQUEQU:
case OPER_NOTEQU:
@@ -3075,24 +3044,24 @@ char *GetStringResult(char *lp,uint8_t lastop,char *cp,JsonObject *jo) {
case OPER_LOWEQU:
case OPER_GRT:
case OPER_GRTEQU:
- lp=slp;
- strcpy(cp,str);
+ lp = slp;
+ strcpy(cp, str);
return lp;
break;
default:
break;
}
- lastop=operand;
+ lastop = operand;
if (!operand) {
- strcpy(cp,str);
+ strcpy(cp, str);
return lp;
}
}
return lp;
}
-char *GetNumericResult(char *lp,uint8_t lastop,float *fp,JsonObject *jo) {
-uint8_t operand=0;
+char *GetNumericArgument(char *lp, uint8_t lastop, float *fp, JsonObject *jo) {
+uint8_t operand = 0;
float fvar1,fvar;
char *slp;
uint8_t vtype;
@@ -3101,50 +3070,50 @@ struct T_INDEX ind;
// get 1. value
if (*lp=='(') {
lp++;
- lp=GetNumericResult(lp,OPER_EQU,&fvar1,jo);
+ lp = GetNumericArgument(lp, OPER_EQU, &fvar1, jo);
lp++;
//if (*lp==')') lp++;
} else {
- lp=isvar(lp,&vtype,&ind,&fvar1,0,jo);
- if (vtype!=NUM_RES && vtype&STYPE) {
+ lp = isvar(lp, &vtype, &ind, &fvar1, 0, jo);
+ if ((vtype!=NUM_RES) && (vtype&STYPE)) {
// string type
- glob_script_mem.glob_error=1;
+ glob_script_mem.glob_error = 1;
}
}
switch (lastop) {
case OPER_EQU:
- fvar=fvar1;
+ fvar = fvar1;
break;
case OPER_PLS:
- fvar+=fvar1;
+ fvar += fvar1;
break;
case OPER_MIN:
- fvar-=fvar1;
+ fvar -= fvar1;
break;
case OPER_MUL:
- fvar*=fvar1;
+ fvar *= fvar1;
break;
case OPER_DIV:
- fvar/=fvar1;
+ fvar /= fvar1;
break;
case OPER_PERC:
- fvar=fmodf(fvar,fvar1);
+ fvar = fmodf(fvar, fvar1);
break;
case OPER_XOR:
- fvar=(uint32_t)fvar^(uint32_t)fvar1;
+ fvar = (uint32_t)fvar ^ (uint32_t)fvar1;
break;
case OPER_AND:
- fvar=(uint32_t)fvar&(uint32_t)fvar1;
+ fvar = (uint32_t)fvar & (uint32_t)fvar1;
break;
case OPER_OR:
- fvar=(uint32_t)fvar|(uint32_t)fvar1;
+ fvar = (uint32_t)fvar | (uint32_t)fvar1;
break;
default:
break;
}
- slp=lp;
- lp=getop(lp,&operand);
+ slp = lp;
+ lp = getop(lp, &operand);
switch (operand) {
case OPER_EQUEQU:
case OPER_NOTEQU:
@@ -3152,102 +3121,115 @@ struct T_INDEX ind;
case OPER_LOWEQU:
case OPER_GRT:
case OPER_GRTEQU:
- lp=slp;
- *fp=fvar;
+ lp = slp;
+ *fp = fvar;
return lp;
break;
default:
break;
}
- lastop=operand;
+ lastop = operand;
if (!operand) {
- *fp=fvar;
+ *fp = fvar;
return lp;
}
}
}
-char *ForceStringVar(char *lp,char *dstr) {
+char *ForceStringVar(char *lp, char *dstr) {
float fvar;
- char *slp=lp;
- glob_script_mem.glob_error=0;
- lp=GetStringResult(lp,OPER_EQU,dstr,0);
+ char *slp = lp;
+ glob_script_mem.glob_error = 0;
+ lp = GetStringArgument(lp, OPER_EQU, dstr, 0);
if (glob_script_mem.glob_error) {
// mismatch
- lp=GetNumericResult(slp,OPER_EQU,&fvar,0);
- dtostrfd(fvar,6,dstr);
- glob_script_mem.glob_error=0;
+ lp = GetNumericArgument(slp, OPER_EQU, &fvar, 0);
+ dtostrfd(fvar, 6, dstr);
+ glob_script_mem.glob_error = 0;
}
return lp;
}
// replace vars in cmd %var%
-void Replace_Cmd_Vars(char *srcbuf,uint32_t srcsize, char *dstbuf,uint32_t dstsize) {
+void Replace_Cmd_Vars(char *srcbuf, uint32_t srcsize, char *dstbuf, uint32_t dstsize) {
char *cp;
uint16_t count;
uint8_t vtype;
- uint8_t dprec=glob_script_mem.script_dprec;
+ uint8_t dprec = glob_script_mem.script_dprec;
float fvar;
- cp=srcbuf;
+ cp = srcbuf;
struct T_INDEX ind;
char string[SCRIPT_MAXSSIZE];
- dstsize-=2;
- for (count=0;count=sizeof(str)) len=len>=sizeof(str);
- strlcpy(str,cp,len);
+ if (len>=sizeof(str)) len = sizeof(str);
+ strlcpy(str, cp, len);
toSLog(str);
}
void toLogEOL(const char *s1,const char *str) {
if (!str) return;
- uint8_t index=0;
- char *cp=log_data;
- strcpy(cp,s1);
- cp+=strlen(s1);
+ uint8_t index = 0;
+ char *cp = log_data;
+ strcpy(cp, s1);
+ cp += strlen(s1);
while (*str) {
if (*str==SCRIPT_EOL) break;
- *cp++=*str++;
+ *cp++ = *str++;
}
- *cp=0;
+ *cp = 0;
AddLog(LOG_LEVEL_INFO);
}
@@ -3297,70 +3279,70 @@ void toSLog(const char *str) {
#endif
}
-char *Evaluate_expression(char *lp,uint8_t and_or, uint8_t *result,JsonObject *jo) {
+char *Evaluate_expression(char *lp, uint8_t and_or, uint8_t *result,JsonObject *jo) {
float fvar,*dfvar,fvar1;
uint8_t numeric;
struct T_INDEX ind;
- uint8_t vtype=0,lastop;
- uint8_t res=0;
- char *llp=lp;
+ uint8_t vtype = 0,lastop;
+ uint8_t res = 0;
+ char *llp = lp;
char *slp;
SCRIPT_SKIP_SPACES
if (*lp=='(') {
- uint8_t res=0;
- uint8_t xand_or=0;
+ uint8_t res = 0;
+ uint8_t xand_or = 0;
lp++;
loop:
SCRIPT_SKIP_SPACES
- lp=Evaluate_expression(lp,xand_or,&res,jo);
+ lp = Evaluate_expression(lp, xand_or, &res, jo);
if (*lp==')') {
lp++;
goto exit0;
}
// check for next and or
SCRIPT_SKIP_SPACES
- if (!strncmp(lp,"or",2)) {
- lp+=2;
- xand_or=1;
+ if (!strncmp(lp, "or", 2)) {
+ lp += 2;
+ xand_or = 1;
goto loop;
- } else if (!strncmp(lp,"and",3)) {
- lp+=3;
- xand_or=2;
+ } else if (!strncmp(lp, "and", 3)) {
+ lp += 3;
+ xand_or = 2;
goto loop;
}
exit0:
if (!and_or) {
- *result=res;
+ *result = res;
} else if (and_or==1) {
*result|=res;
} else {
- *result&=res;
+ *result &= res;
}
goto exit10;
}
- llp=lp;
+ llp = lp;
// compare
- dfvar=&fvar;
- glob_script_mem.glob_error=0;
- slp=lp;
- numeric=1;
- lp=GetNumericResult(lp,OPER_EQU,dfvar,0);
+ dfvar = &fvar;
+ glob_script_mem.glob_error = 0;
+ slp = lp;
+ numeric = 1;
+ lp = GetNumericArgument(lp, OPER_EQU, dfvar, 0);
if (glob_script_mem.glob_error==1) {
// was string, not number
char cmpstr[SCRIPT_MAXSSIZE];
- lp=slp;
- numeric=0;
+ lp = slp;
+ numeric = 0;
// get the string
- lp=isvar(lp,&vtype,&ind,0,cmpstr,0);
- lp=getop(lp,&lastop);
+ lp = isvar(lp, &vtype, &ind, 0, cmpstr, 0);
+ lp = getop(lp, &lastop);
// compare string
char str[SCRIPT_MAXSSIZE];
- lp=GetStringResult(lp,OPER_EQU,str,jo);
+ lp = GetStringArgument(lp, OPER_EQU, str, jo);
if (lastop==OPER_EQUEQU || lastop==OPER_NOTEQU) {
- res=strcmp(cmpstr,str);
+ res = strcmp(cmpstr, str);
if (lastop==OPER_EQUEQU) res=!res;
goto exit;
}
@@ -3368,26 +3350,26 @@ exit0:
} else {
// numeric
// evaluate operand
- lp=getop(lp,&lastop);
- lp=GetNumericResult(lp,OPER_EQU,&fvar1,jo);
+ lp = getop(lp, &lastop);
+ lp = GetNumericArgument(lp, OPER_EQU, &fvar1, jo);
switch (lastop) {
case OPER_EQUEQU:
- res=(*dfvar==fvar1);
+ res = (*dfvar==fvar1);
break;
case OPER_NOTEQU:
- res=(*dfvar!=fvar1);
+ res = (*dfvar!=fvar1);
break;
case OPER_LOW:
- res=(*dfvarfvar1);
+ res = (*dfvar>fvar1);
break;
case OPER_GRTEQU:
- res=(*dfvar>=fvar1);
+ res = (*dfvar>=fvar1);
break;
default:
// error
@@ -3396,11 +3378,11 @@ exit0:
exit:
if (!and_or) {
- *result=res;
+ *result = res;
} else if (and_or==1) {
- *result|=res;
+ *result |= res;
} else {
- *result&=res;
+ *result &= res;
}
}
@@ -3408,8 +3390,8 @@ exit:
exit10:
#if IFTHEN_DEBUG>0
char tbuff[128];
- sprintf(tbuff,"p1=%d,p2=%d,cmpres=%d,and_or=%d line: ",(int32_t)*dfvar,(int32_t)fvar1,*result,and_or);
- toLogEOL(tbuff,llp);
+ sprintf(tbuff,"p1=%d,p2=%d,cmpres=%d,and_or=%d line: ", (int32_t)*dfvar, (int32_t)fvar1, *result, and_or);
+ toLogEOL(tbuff, llp);
#endif
return lp;
}
@@ -3420,28 +3402,28 @@ TimerHandle_t beep_th;
void StopBeep( TimerHandle_t xTimer );
void StopBeep( TimerHandle_t xTimer ) {
- ledcWriteTone(7,0);
+ ledcWriteTone(7, 0);
xTimerStop(xTimer, 0);
}
void esp32_beep(int32_t freq ,uint32_t len) {
if (freq<0) {
- ledcSetup(7,500,10);
- ledcAttachPin(-freq,7);
- ledcWriteTone(7,0);
+ ledcSetup(7, 500, 10);
+ ledcAttachPin(-freq, 7);
+ ledcWriteTone(7, 0);
if (!beep_th) {
- beep_th = xTimerCreate("beep",100,pdFALSE,( void * ) 0,StopBeep);
+ beep_th = xTimerCreate("beep", 100, pdFALSE, ( void * ) 0, StopBeep);
}
} else {
if (!beep_th) return;
if (!freq) {
- ledcWriteTone(7,0);
+ ledcWriteTone(7, 0);
xTimerStop(beep_th, 10);
return;
}
if (len < 10) return;
if (xTimerIsTimerActive(beep_th)) return;
- ledcWriteTone(7,freq);
+ ledcWriteTone(7, freq);
uint32_t ticks = pdMS_TO_TICKS(len);
xTimerChangePeriod( beep_th, ticks, 10);
}
@@ -3460,13 +3442,13 @@ int16_t Run_Scripter(const char *type, int8_t tlen, char *js) {
if (tasm_cmd_activ && tlen>0) return 0;
- JsonObject *jo=0;
+ JsonObject *jo = 0;
DynamicJsonBuffer jsonBuffer; // on heap
- JsonObject &jobj=jsonBuffer.parseObject(js);
+ JsonObject &jobj = jsonBuffer.parseObject(js);
if (js) {
- jo=&jobj;
+ jo = &jobj;
} else {
- jo=0;
+ jo = 0;
}
return Run_script_sub(type, tlen, jo);
@@ -3477,23 +3459,23 @@ int16_t Run_script_sub(const char *type, int8_t tlen, JsonObject *jo) {
char *lp_next;
int16_t globaindex,saindex;
struct T_INDEX ind;
- uint8_t operand,lastop,numeric=1,if_state[IF_NEST],if_exe[IF_NEST],if_result[IF_NEST],and_or,ifstck=0;
- if_state[ifstck]=0;
- if_result[ifstck]=0;
- if_exe[ifstck]=1;
+ uint8_t operand,lastop,numeric = 1,if_state[IF_NEST],if_exe[IF_NEST],if_result[IF_NEST],and_or,ifstck = 0;
+ if_state[ifstck] = 0;
+ if_result[ifstck] = 0;
+ if_exe[ifstck] = 1;
char cmpstr[SCRIPT_MAXSSIZE];
- uint8_t check=0;
+ uint8_t check = 0;
if (tlen<0) {
- tlen=abs(tlen);
- check=1;
+ tlen = abs(tlen);
+ check = 1;
}
float *dfvar,*cv_count,cv_max,cv_inc;
char *cv_ptr;
- float fvar=0,fvar1,sysvar,swvar;
- uint8_t section=0,sysv_type=0,swflg=0;
+ float fvar = 0,fvar1,sysvar,swvar;
+ uint8_t section = 0,sysv_type = 0,swflg = 0;
- char *lp=glob_script_mem.scriptptr;
+ char *lp = glob_script_mem.scriptptr;
while (1) {
// check line
@@ -3514,71 +3496,71 @@ int16_t Run_script_sub(const char *type, int8_t tlen, JsonObject *jo) {
if (*lp=='#') {
return 0;
}
- glob_script_mem.var_not_found=0;
+ glob_script_mem.var_not_found = 0;
//#if SCRIPT_DEBUG>0
#ifdef IFTHEN_DEBUG
char tbuff[128];
- sprintf(tbuff,"stack=%d,exe=%d,state=%d,cmpres=%d line: ",ifstck,if_exe[ifstck],if_state[ifstck],if_result[ifstck]);
- toLogEOL(tbuff,lp);
-#endif
+ sprintf(tbuff, "stack=%d,exe=%d,state=%d,cmpres=%d line: ", ifstck, if_exe[ifstck], if_state[ifstck], if_result[ifstck]);
+ toLogEOL(tbuff, lp);
+#endif //IFTHEN_DEBUG
//if (if_state[s_ifstck]==3 && if_result[s_ifstck]) goto next_line;
//if (if_state[s_ifstck]==2 && !if_result[s_ifstck]) goto next_line;
- if (!strncmp(lp,"if",2)) {
- lp+=2;
+ if (!strncmp(lp, "if", 2)) {
+ lp += 2;
if (ifstck=2) {
- lp+=5;
+ if_state[ifstck] = 1;
+ if_result[ifstck] = 0;
+ if (ifstck==1) if_exe[ifstck] = 1;
+ else if_exe[ifstck] = if_exe[ifstck - 1];
+ and_or = 0;
+ } else if (!strncmp(lp, "then", 4) && if_state[ifstck]==1) {
+ lp += 4;
+ if_state[ifstck] = 2;
+ if (if_exe[ifstck - 1]) if_exe[ifstck] = if_result[ifstck];
+ } else if (!strncmp(lp, "else", 4) && if_state[ifstck]==2) {
+ lp += 4;
+ if_state[ifstck] = 3;
+ if (if_exe[ifstck - 1]) if_exe[ifstck] = !if_result[ifstck];
+ } else if (!strncmp(lp, "endif", 5) && if_state[ifstck]>=2) {
+ lp += 5;
if (ifstck>0) {
- if_state[ifstck]=0;
+ if_state[ifstck] = 0;
ifstck--;
}
goto next_line;
- } else if (!strncmp(lp,"or",2) && if_state[ifstck]==1) {
- lp+=2;
- and_or=1;
- } else if (!strncmp(lp,"and",3) && if_state[ifstck]==1) {
- lp+=3;
- and_or=2;
+ } else if (!strncmp(lp, "or", 2) && if_state[ifstck]==1) {
+ lp += 2;
+ and_or = 1;
+ } else if (!strncmp(lp, "and", 3) && if_state[ifstck]==1) {
+ lp += 3;
+ and_or = 2;
}
if (*lp=='{' && if_state[ifstck]==1) {
- lp+=1; // then
- if_state[ifstck]=2;
- if (if_exe[ifstck-1]) if_exe[ifstck]=if_result[ifstck];
+ lp += 1; // then
+ if_state[ifstck] = 2;
+ if (if_exe[ifstck - 1]) if_exe[ifstck]=if_result[ifstck];
} else if (*lp=='{' && if_state[ifstck]==3) {
- lp+=1; // after else
+ lp += 1; // after else
//if_state[ifstck]=3;
} else if (*lp=='}' && if_state[ifstck]>=2) {
lp++; // must check for else
- char *slp=lp;
- uint8_t iselse=0;
- for (uint8_t count=0; count<8;count++) {
+ char *slp = lp;
+ uint8_t iselse = 0;
+ for (uint8_t count = 0; count<8;count++) {
if (*lp=='}') {
// must be endif
break;
}
- if (!strncmp(lp,"else",4)) {
+ if (!strncmp(lp, "else", 4)) {
// is before else, no endif
- if_state[ifstck]=3;
+ if_state[ifstck] = 3;
if (if_exe[ifstck-1]) if_exe[ifstck]=!if_result[ifstck];
- lp+=4;
- iselse=1;
+ lp += 4;
+ iselse = 1;
SCRIPT_SKIP_SPACES
if (*lp=='{') lp++;
break;
@@ -3586,111 +3568,111 @@ int16_t Run_script_sub(const char *type, int8_t tlen, JsonObject *jo) {
lp++;
}
if (!iselse) {
- lp=slp;
+ lp = slp;
// endif
if (ifstck>0) {
- if_state[ifstck]=0;
+ if_state[ifstck] = 0;
ifstck--;
}
goto next_line;
}
}
- if (!strncmp(lp,"for",3)) {
+ if (!strncmp(lp, "for", 3)) {
// start for next loop, fetch 3 params
// simple implementation, zero loop count not supported
- lp+=3;
+ lp += 3;
SCRIPT_SKIP_SPACES
- lp_next=0;
- lp=isvar(lp,&vtype,&ind,0,0,0);
+ lp_next = 0;
+ lp = isvar(lp, &vtype, &ind, 0, 0, 0);
if ((vtype!=VAR_NV) && (vtype&STYPE)==0) {
// numeric var
- uint8_t index=glob_script_mem.type[ind.index].index;
- cv_count=&glob_script_mem.fvars[index];
+ uint8_t index = glob_script_mem.type[ind.index].index;
+ cv_count = &glob_script_mem.fvars[index];
SCRIPT_SKIP_SPACES
- lp=GetNumericResult(lp,OPER_EQU,cv_count,0);
+ lp = GetNumericArgument(lp, OPER_EQU, cv_count, 0);
SCRIPT_SKIP_SPACES
- lp=GetNumericResult(lp,OPER_EQU,&cv_max,0);
+ lp = GetNumericArgument(lp, OPER_EQU, &cv_max, 0);
SCRIPT_SKIP_SPACES
- lp=GetNumericResult(lp,OPER_EQU,&cv_inc,0);
+ lp = GetNumericArgument(lp, OPER_EQU, &cv_inc, 0);
//SCRIPT_SKIP_EOL
- cv_ptr=lp;
+ cv_ptr = lp;
if (*cv_count<=cv_max && cv_inc>0) {
// inc loop
- floop=1;
+ floop = 1;
} else {
// dec loop
- floop=2;
+ floop = 2;
if (cv_inc>0) {
- floop=1;
+ floop = 1;
}
}
} else {
// error
- toLogEOL("for error",lp);
+ toLogEOL("for error", lp);
}
- } else if (!strncmp(lp,"next",4)) {
- lp_next=lp;
+ } else if (!strncmp(lp, "next", 4)) {
+ lp_next = lp;
if (floop>0) {
// for next loop
- *cv_count+=cv_inc;
+ *cv_count += cv_inc;
if (floop==1) {
if (*cv_count<=cv_max) {
- lp=cv_ptr;
+ lp = cv_ptr;
} else {
- lp+=4;
- floop=0;
+ lp += 4;
+ floop = 0;
}
} else {
if (*cv_count>=cv_max) {
- lp=cv_ptr;
+ lp = cv_ptr;
} else {
- lp+=4;
- floop=0;
+ lp += 4;
+ floop = 0;
}
}
}
}
- if (!strncmp(lp,"switch",6)) {
- lp+=6;
+ if (!strncmp(lp, "switch", 6)) {
+ lp += 6;
SCRIPT_SKIP_SPACES
- char *slp=lp;
- lp=GetNumericResult(lp,OPER_EQU,&swvar,0);
+ char *slp = lp;
+ lp = GetNumericArgument(lp, OPER_EQU, &swvar, 0);
if (glob_script_mem.glob_error==1) {
// was string, not number
- lp=slp;
+ lp = slp;
// get the string
- lp=isvar(lp,&vtype,&ind,0,cmpstr,0);
- swflg=0x81;
+ lp = isvar(lp, &vtype, &ind, 0, cmpstr, 0);
+ swflg = 0x81;
} else {
- swflg=1;
+ swflg = 1;
}
- } else if (!strncmp(lp,"case",4) && swflg>0) {
- lp+=4;
+ } else if (!strncmp(lp, "case", 4) && swflg>0) {
+ lp += 4;
SCRIPT_SKIP_SPACES
float cvar;
- if (!(swflg&0x80)) {
- lp=GetNumericResult(lp,OPER_EQU,&cvar,0);
+ if (!(swflg & 0x80)) {
+ lp = GetNumericArgument(lp, OPER_EQU, &cvar, 0);
if (swvar!=cvar) {
- swflg=2;
+ swflg = 2;
} else {
- swflg=1;
+ swflg = 1;
}
} else {
char str[SCRIPT_MAXSSIZE];
- lp=GetStringResult(lp,OPER_EQU,str,0);
- if (!strcmp(cmpstr,str)) {
- swflg=0x81;
+ lp = GetStringArgument(lp, OPER_EQU, str, 0);
+ if (!strcmp(cmpstr, str)) {
+ swflg = 0x81;
} else {
- swflg=0x82;
+ swflg = 0x82;
}
}
- } else if (!strncmp(lp,"ends",4) && swflg>0) {
- lp+=4;
- swflg=0;
+ } else if (!strncmp(lp, "ends", 4) && swflg>0) {
+ lp += 4;
+ swflg = 0;
}
- if ((swflg&3)==2) goto next_line;
+ if ((swflg & 3)==2) goto next_line;
SCRIPT_SKIP_SPACES
//SCRIPT_SKIP_EOL
@@ -3702,34 +3684,34 @@ int16_t Run_script_sub(const char *type, int8_t tlen, JsonObject *jo) {
if (!if_exe[ifstck] && if_state[ifstck]!=1) goto next_line;
#ifdef IFTHEN_DEBUG
- sprintf(tbuff,"stack=%d,exe=%d,state=%d,cmpres=%d execute line: ",ifstck,if_exe[ifstck],if_state[ifstck],if_result[ifstck]);
- toLogEOL(tbuff,lp);
-#endif
+ sprintf(tbuff, "stack=%d,exe=%d,state=%d,cmpres=%d execute line: ", ifstck, if_exe[ifstck], if_state[ifstck], if_result[ifstck]);
+ toLogEOL(tbuff, lp);
+#endif //IFTHEN_DEBUG
- if (!strncmp(lp,"break",5)) {
- lp+=5;
+ if (!strncmp(lp, "break", 5)) {
+ lp += 5;
if (floop) {
// should break loop
if (lp_next) {
- lp=lp_next;
+ lp = lp_next;
}
- floop=0;
+ floop = 0;
} else {
- section=0;
+ section = 0;
}
goto next_line;
- } else if (!strncmp(lp,"dp",2) && isdigit(*(lp+2))) {
- lp+=2;
+ } else if (!strncmp(lp, "dp", 2) && isdigit(*(lp + 2))) {
+ lp += 2;
// number precision
- glob_script_mem.script_dprec=atoi(lp);
+ glob_script_mem.script_dprec = atoi(lp);
goto next_line;
}
#ifdef USE_DISPLAY
- else if (!strncmp(lp,"dt",2)) {
+ else if (!strncmp(lp, "dt", 2)) {
char dstbuf[256];
- lp+=2;
+ lp += 2;
SCRIPT_SKIP_SPACES
- Replace_Cmd_Vars(lp,1,dstbuf,sizeof(dstbuf));
+ Replace_Cmd_Vars(lp, 1, dstbuf, sizeof(dstbuf));
char *savptr = XdrvMailbox.data;
XdrvMailbox.data = dstbuf;
XdrvMailbox.data_len = 0;
@@ -3737,165 +3719,160 @@ int16_t Run_script_sub(const char *type, int8_t tlen, JsonObject *jo) {
XdrvMailbox.data = savptr;
goto next_line;
}
-#endif
- else if (!strncmp(lp,"delay(",6)) {
- lp+=5;
+#endif //USE_DISPLAY
+ else if (!strncmp(lp, "delay(", 6)) {
// delay
- lp=GetNumericResult(lp,OPER_EQU,&fvar,0);
+ lp = GetNumericArgument(lp + 5, OPER_EQU, &fvar, 0);
delay(fvar);
goto next_line;
- } else if (!strncmp(lp,"spinm(",6)) {
- lp+=6;
+ } else if (!strncmp(lp, "spinm(", 6)) {
// set pin mode
- lp=GetNumericResult(lp,OPER_EQU,&fvar,0);
- int8_t pinnr=fvar;
+ lp = GetNumericArgument(lp + 6, OPER_EQU, &fvar, 0);
+ int8_t pinnr = fvar;
SCRIPT_SKIP_SPACES
- uint8_t mode=0;
+ uint8_t mode = 0;
if ((*lp=='I') || (*lp=='O') || (*lp=='P')) {
switch (*lp) {
case 'I':
- mode=0;
+ mode = 0;
break;
case 'O':
- mode=1;
+ mode = 1;
break;
case 'P':
- mode=2;
+ mode = 2;
break;
}
lp++;
} else {
- lp=GetNumericResult(lp,OPER_EQU,&fvar,0);
- mode=fvar;
+ lp = GetNumericArgument(lp, OPER_EQU, &fvar, 0);
+ mode = fvar;
}
uint8_t pm=0;
- if (mode==0) pm=INPUT;
- if (mode==1) pm=OUTPUT;
- if (mode==2) pm=INPUT_PULLUP;
- pinMode(pinnr,pm);
+ if (mode==0) pm = INPUT;
+ if (mode==1) pm = OUTPUT;
+ if (mode==2) pm = INPUT_PULLUP;
+ pinMode(pinnr, pm);
goto next_line;
- } else if (!strncmp(lp,"spin(",5)) {
- lp+=5;
+ } else if (!strncmp(lp, "spin(", 5)) {
// set pin
- lp=GetNumericResult(lp,OPER_EQU,&fvar,0);
- int8_t pinnr=fvar;
+ lp = GetNumericArgument(lp + 5, OPER_EQU, &fvar, 0);
+ int8_t pinnr = fvar;
SCRIPT_SKIP_SPACES
- lp=GetNumericResult(lp,OPER_EQU,&fvar,0);
- int8_t mode=fvar;
- digitalWrite(pinnr,mode&1);
+ lp = GetNumericArgument(lp, OPER_EQU, &fvar, 0);
+ int8_t mode = fvar;
+ digitalWrite(pinnr, mode & 1);
goto next_line;
- } else if (!strncmp(lp,"svars(",5)) {
- lp+=5;
+ } else if (!strncmp(lp, "svars(", 5)) {
+ lp += 5;
// save vars
Scripter_save_pvars();
goto next_line;
}
#ifdef USE_LIGHT
#ifdef USE_WS2812
- else if (!strncmp(lp,"ws2812(",7)) {
- lp+=7;
- lp=isvar(lp,&vtype,&ind,0,0,0);
+ else if (!strncmp(lp, "ws2812(", 7)) {
+ lp = isvar(lp + 7, &vtype, &ind, 0, 0, 0);
if (vtype!=VAR_NV) {
SCRIPT_SKIP_SPACES
if (*lp!=')') {
- lp=GetNumericResult(lp,OPER_EQU,&fvar,0);
+ lp = GetNumericArgument(lp, OPER_EQU, &fvar, 0);
} else {
- fvar=0;
+ fvar = 0;
}
// found variable as result
- uint8_t index=glob_script_mem.type[ind.index].index;
+ uint8_t index = glob_script_mem.type[ind.index].index;
if ((vtype&STYPE)==0) {
// numeric result
if (glob_script_mem.type[ind.index].bits.is_filter) {
- uint16_t len=0;
- float *fa=Get_MFAddr(index,&len,0);
+ uint16_t len = 0;
+ float *fa = Get_MFAddr(index, &len, 0);
//Serial.printf(">> 2 %d\n",(uint32_t)*fa);
- if (fa && len) ws2812_set_array(fa,len,fvar);
+ if (fa && len) ws2812_set_array(fa, len, fvar);
}
}
}
goto next_line;
}
-#endif
-#endif
+#endif //USE_WS2812
+#endif //USE_LIGHT
#ifdef ESP32
- else if (!strncmp(lp,"beep(",5)) {
- lp+=5;
- lp=GetNumericResult(lp,OPER_EQU,&fvar,0);
+ else if (!strncmp(lp, "beep(", 5)) {
+ lp = GetNumericArgument(lp + 5, OPER_EQU, &fvar, 0);
SCRIPT_SKIP_SPACES
float fvar1;
- lp=GetNumericResult(lp,OPER_EQU,&fvar1,0);
- esp32_beep(fvar,fvar1);
+ lp = GetNumericArgument(lp, OPER_EQU, &fvar1, 0);
+ esp32_beep(fvar, fvar1);
lp++;
goto next_line;
}
-#endif
+#endif //ESP32
else if (!strncmp(lp,"=>",2) || !strncmp(lp,"->",2) || !strncmp(lp,"+>",2) || !strncmp(lp,"print",5)) {
// execute cmd
- uint8_t sflag=0,pflg=0,svmqtt,swll;
+ uint8_t sflag = 0,pflg = 0,svmqtt,swll;
if (*lp=='p') {
- pflg=1;
- lp+=5;
+ pflg = 1;
+ lp += 5;
}
else {
- if (*lp=='-') sflag=1;
- if (*lp=='+') sflag=2;
- lp+=2;
+ if (*lp=='-') sflag = 1;
+ if (*lp=='+') sflag = 2;
+ lp += 2;
}
- char *slp=lp;
+ char *slp = lp;
SCRIPT_SKIP_SPACES
#define SCRIPT_CMDMEM 512
- char *cmdmem=(char*)malloc(SCRIPT_CMDMEM);
+ char *cmdmem = (char*)malloc(SCRIPT_CMDMEM);
if (cmdmem) {
- char *cmd=cmdmem;
+ char *cmd = cmdmem;
uint16_t count;
- for (count=0; count';
- lp=GetStringResult(lp,OPER_EQU,&str[1],0);
+ str[0] = '>';
+ lp = GetStringArgument(lp, OPER_EQU, &str[1], 0);
lp++;
//execute_script(str);
- char *svd_sp=glob_script_mem.scriptptr;
- strcat(str,"\n#");
- glob_script_mem.scriptptr=str;
- Run_script_sub(">",1,jo);
- glob_script_mem.scriptptr=svd_sp;
+ char *svd_sp = glob_script_mem.scriptptr;
+ strcat(str, "\n#");
+ glob_script_mem.scriptptr = str;
+ Run_script_sub(">", 1, jo);
+ glob_script_mem.scriptptr = svd_sp;
}
// check for variable result
if (if_state[ifstck]==1) {
// evaluate exxpression
- lp=Evaluate_expression(lp,and_or,&if_result[ifstck],jo);
+ lp = Evaluate_expression(lp, and_or, &if_result[ifstck], jo);
SCRIPT_SKIP_SPACES
if (*lp=='{' && if_state[ifstck]==1) {
- lp+=1; // then
- if_state[ifstck]=2;
- if (if_exe[ifstck-1]) if_exe[ifstck]=if_result[ifstck];
+ lp += 1; // then
+ if_state[ifstck] = 2;
+ if (if_exe[ifstck - 1]) if_exe[ifstck] = if_result[ifstck];
}
goto next_line;
} else {
- char *vnp=lp;
- lp=isvar(lp,&vtype,&ind,&sysvar,0,0);
+ char *vnp = lp;
+ lp = isvar(lp, &vtype, &ind, &sysvar, 0, 0);
if (vtype!=VAR_NV) {
#ifdef USE_SCRIPT_GLOBVARS
char varname[16];
- uint32_t vnl=(uint32_t)lp-(uint32)vnp;
- strncpy(varname,vnp,vnl);
- varname[vnl]=0;
-#endif
+ uint32_t vnl = (uint32_t)lp - (uint32)vnp;
+ strncpy(varname, vnp, vnl);
+ varname[vnl] = 0;
+#endif //USE_SCRIPT_GLOBVARS
// found variable as result
- globvindex=ind.index; // save destination var index here
- globaindex=last_findex;
- uint8_t index=glob_script_mem.type[ind.index].index;
+ globvindex = ind.index; // save destination var index here
+ globaindex = last_findex;
+ uint8_t index = glob_script_mem.type[ind.index].index;
if ((vtype&STYPE)==0) {
// numeric result
if (ind.bits.settable || ind.bits.is_filter) {
- dfvar=&sysvar;
+ dfvar = &sysvar;
if (ind.bits.settable) {
- sysv_type=ind.index;
+ sysv_type = ind.index;
} else {
- sysv_type=0;
+ sysv_type = 0;
}
} else {
- dfvar=&glob_script_mem.fvars[index];
- sysv_type=0;
+ dfvar = &glob_script_mem.fvars[index];
+ sysv_type = 0;
}
- numeric=1;
- lp=getop(lp,&lastop);
- char *slp=lp;
- glob_script_mem.glob_error=0;
- lp=GetNumericResult(lp,OPER_EQU,&fvar,jo);
+ numeric = 1;
+ lp = getop(lp, &lastop);
+ char *slp = lp;
+ glob_script_mem.glob_error = 0;
+ lp = GetNumericArgument(lp, OPER_EQU, &fvar, jo);
if (glob_script_mem.glob_error==1) {
// mismatch was string, not number
// get the string and convert to number
- lp=isvar(slp,&vtype,&ind,0,cmpstr,jo);
- fvar=CharToFloat(cmpstr);
+ lp = isvar(slp, &vtype, &ind, 0, cmpstr, jo);
+ fvar = CharToFloat(cmpstr);
}
switch (lastop) {
case OPER_EQU:
@@ -3984,105 +3961,105 @@ int16_t Run_script_sub(const char *type, int8_t tlen, JsonObject *jo) {
if (!jo) toLogEOL("var not found: ",lp);
goto next_line;
}
- *dfvar=fvar;
+ *dfvar = fvar;
break;
case OPER_PLSEQU:
- *dfvar+=fvar;
+ *dfvar += fvar;
break;
case OPER_MINEQU:
- *dfvar-=fvar;
+ *dfvar -= fvar;
break;
case OPER_MULEQU:
- *dfvar*=fvar;
+ *dfvar *= fvar;
break;
case OPER_DIVEQU:
- *dfvar/=fvar;
+ *dfvar /= fvar;
break;
case OPER_PERCEQU:
- *dfvar=fmodf(*dfvar,fvar);
+ *dfvar = fmodf(*dfvar, fvar);
break;
case OPER_ANDEQU:
- *dfvar=(uint32_t)*dfvar&(uint32_t)fvar;
+ *dfvar = (uint32_t)*dfvar & (uint32_t)fvar;
break;
case OPER_OREQU:
- *dfvar=(uint32_t)*dfvar|(uint32_t)fvar;
+ *dfvar = (uint32_t)*dfvar | (uint32_t)fvar;
break;
case OPER_XOREQU:
- *dfvar=(uint32_t)*dfvar^(uint32_t)fvar;
+ *dfvar = (uint32_t)*dfvar ^ (uint32_t)fvar;
break;
default:
// error
break;
}
// var was changed
- glob_script_mem.type[globvindex].bits.changed=1;
+ glob_script_mem.type[globvindex].bits.changed = 1;
#ifdef USE_SCRIPT_GLOBVARS
if (glob_script_mem.type[globvindex].bits.global) {
- script_udp_sendvar(varname,dfvar,0);
+ script_udp_sendvar(varname, dfvar, 0);
}
-#endif
+#endif //USE_SCRIPT_GLOBVARS
if (glob_script_mem.type[globvindex].bits.is_filter) {
if (globaindex>=0) {
- Set_MFVal(glob_script_mem.type[globvindex].index,globaindex,*dfvar);
+ Set_MFVal(glob_script_mem.type[globvindex].index, globaindex, *dfvar);
} else {
- Set_MFilter(glob_script_mem.type[globvindex].index,*dfvar);
+ Set_MFilter(glob_script_mem.type[globvindex].index, *dfvar);
}
}
if (sysv_type) {
switch (sysv_type) {
case SCRIPT_LOGLEVEL:
- glob_script_mem.script_loglevel=*dfvar;
+ glob_script_mem.script_loglevel = *dfvar;
break;
case SCRIPT_TELEPERIOD:
- if (*dfvar<10) *dfvar=10;
- if (*dfvar>300) *dfvar=300;
- Settings.tele_period=*dfvar;
+ if (*dfvar<10) *dfvar = 10;
+ if (*dfvar>300) *dfvar = 300;
+ Settings.tele_period = *dfvar;
break;
case SCRIPT_EVENT_HANDLED:
- event_handeled=*dfvar;
+ event_handeled = *dfvar;
break;
}
- sysv_type=0;
+ sysv_type = 0;
}
} else {
// string result
- numeric=0;
- sindex=index;
- saindex=last_sindex;
+ numeric = 0;
+ sindex = index;
+ saindex = last_sindex;
// string result
char str[SCRIPT_MAXSSIZE];
- lp=getop(lp,&lastop);
- char *slp=lp;
- glob_script_mem.glob_error=0;
- lp=GetStringResult(lp,OPER_EQU,str,jo);
+ lp = getop(lp, &lastop);
+ char *slp = lp;
+ glob_script_mem.glob_error = 0;
+ lp = GetStringArgument(lp, OPER_EQU, str, jo);
if (!jo && glob_script_mem.glob_error) {
// mismatch
- lp=GetNumericResult(slp,OPER_EQU,&fvar,0);
- dtostrfd(fvar,6,str);
- glob_script_mem.glob_error=0;
+ lp = GetNumericArgument(slp, OPER_EQU, &fvar, 0);
+ dtostrfd(fvar, 6, str);
+ glob_script_mem.glob_error = 0;
}
if (!glob_script_mem.var_not_found) {
// var was changed
- glob_script_mem.type[globvindex].bits.changed=1;
+ glob_script_mem.type[globvindex].bits.changed = 1;
#ifdef USE_SCRIPT_GLOBVARS
if (glob_script_mem.type[globvindex].bits.global) {
- script_udp_sendvar(varname,0,str);
+ script_udp_sendvar(varname, 0, str);
}
-#endif
+#endif //USE_SCRIPT_GLOBVARS
if (saindex>=0) {
if (lastop==OPER_EQU) {
- strlcpy(glob_script_mem.last_index_string+(saindex*glob_script_mem.max_ssize),str,glob_script_mem.max_ssize);
+ strlcpy(glob_script_mem.last_index_string + (saindex * glob_script_mem.max_ssize), str, glob_script_mem.max_ssize);
} else if (lastop==OPER_PLSEQU) {
- strncat(glob_script_mem.last_index_string+(saindex*glob_script_mem.max_ssize),str,glob_script_mem.max_ssize);
+ strncat(glob_script_mem.last_index_string + (saindex * glob_script_mem.max_ssize), str, glob_script_mem.max_ssize);
}
- last_sindex=-1;
+ last_sindex = -1;
} else {
if (lastop==OPER_EQU) {
- strlcpy(glob_script_mem.glob_snp+(sindex*glob_script_mem.max_ssize),str,glob_script_mem.max_ssize);
+ strlcpy(glob_script_mem.glob_snp + (sindex * glob_script_mem.max_ssize), str, glob_script_mem.max_ssize);
} else if (lastop==OPER_PLSEQU) {
- strncat(glob_script_mem.glob_snp+(sindex*glob_script_mem.max_ssize),str,glob_script_mem.max_ssize);
+ strncat(glob_script_mem.glob_snp + (sindex * glob_script_mem.max_ssize), str, glob_script_mem.max_ssize);
}
}
}
@@ -4091,7 +4068,7 @@ int16_t Run_script_sub(const char *type, int8_t tlen, JsonObject *jo) {
}
SCRIPT_SKIP_SPACES
if (*lp=='{' && if_state[ifstck]==3) {
- lp+=1; // else
+ lp += 1; // else
//if_state[ifstck]=3;
}
goto next_line;
@@ -4102,67 +4079,67 @@ int16_t Run_script_sub(const char *type, int8_t tlen, JsonObject *jo) {
if (*lp=='>' && tlen==1) {
// called from cmdline
lp++;
- section=1;
- fromscriptcmd=1;
+ section = 1;
+ fromscriptcmd = 1;
goto startline;
}
- if (!strncmp(lp,type,tlen)) {
+ if (!strncmp(lp, type, tlen)) {
// found section
- section=1;
- glob_script_mem.section_ptr=lp;
+ section = 1;
+ glob_script_mem.section_ptr = lp;
if (check) {
return 99;
}
// check for subroutine
- char *ctype=(char*)type;
+ char *ctype = (char*)type;
if (*ctype=='#') {
// check for parameter
- ctype+=tlen;
+ ctype += tlen;
if (*ctype=='(' && *(lp+tlen)=='(') {
float fparam;
- numeric=1;
- glob_script_mem.glob_error=0;
- GetNumericResult((char*)ctype,OPER_EQU,&fparam,0);
+ numeric = 1;
+ glob_script_mem.glob_error = 0;
+ GetNumericArgument((char*)ctype, OPER_EQU, &fparam, 0);
if (glob_script_mem.glob_error==1) {
// was string, not number
- numeric=0;
+ numeric = 0;
// get the string
- GetStringResult((char*)ctype+1,OPER_EQU,cmpstr,0);
+ GetStringArgument((char*)ctype + 1, OPER_EQU, cmpstr, 0);
}
- lp+=tlen;
+ lp += tlen;
if (*lp=='(') {
// fetch destination
lp++;
- lp=isvar(lp,&vtype,&ind,0,0,0);
+ lp = isvar(lp, &vtype, &ind, 0, 0, 0);
if (vtype!=VAR_NV) {
// found variable as result
- uint8_t index=glob_script_mem.type[ind.index].index;
+ uint8_t index = glob_script_mem.type[ind.index].index;
if ((vtype&STYPE)==0) {
// numeric result
- dfvar=&glob_script_mem.fvars[index];
+ dfvar = &glob_script_mem.fvars[index];
if (numeric) {
- *dfvar=fparam;
+ *dfvar = fparam;
} else {
// mismatch
- *dfvar=CharToFloat(cmpstr);
+ *dfvar = CharToFloat(cmpstr);
}
} else {
// string result
- sindex=index;
+ sindex = index;
if (!numeric) {
- strlcpy(glob_script_mem.glob_snp+(sindex*glob_script_mem.max_ssize),cmpstr,glob_script_mem.max_ssize);
+ strlcpy(glob_script_mem.glob_snp + (sindex * glob_script_mem.max_ssize), cmpstr, glob_script_mem.max_ssize);
} else {
// mismatch
- dtostrfd(fparam,6,glob_script_mem.glob_snp+(sindex*glob_script_mem.max_ssize));
+ dtostrfd(fparam, 6, glob_script_mem.glob_snp + (sindex * glob_script_mem.max_ssize));
}
}
}
}
} else {
- lp+=tlen;
+ lp += tlen;
if (*ctype=='(' || (*lp!=SCRIPT_EOL && *lp!='?')) {
// revert
- section=0;
+ section = 0;
}
}
}
@@ -4201,11 +4178,11 @@ void ScripterEvery100ms(void) {
if (strlen(mqtt_data)) {
mqtt_data[0] = '{';
snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s}"), mqtt_data);
- Run_Scripter(">T",2, mqtt_data);
+ Run_Scripter(">T", 2, mqtt_data);
}
}
if (Settings.rule_enabled) {
- if (fast_script==99) Run_Scripter(">F",2,0);
+ if (fast_script==99) Run_Scripter(">F", 2, 0);
}
}
@@ -4213,48 +4190,48 @@ void ScripterEvery100ms(void) {
// can hold 11 floats or floats + strings
// should report overflow later
void Scripter_save_pvars(void) {
- int16_t mlen=0;
- float *fp=(float*)glob_script_mem.script_pram;
+ int16_t mlen = 0;
+ float *fp = (float*)glob_script_mem.script_pram;
mlen+=sizeof(float);
- struct T_INDEX *vtp=glob_script_mem.type;
- for (uint8_t count=0; countglob_script_mem.script_pram_size) {
- vtp[count].bits.is_permanent=0;
+ vtp[count].bits.is_permanent = 0;
return;
}
while (len--) {
- *fp++=*fa++;
+ *fp++ = *fa++;
}
} else {
- mlen+=sizeof(float);
+ mlen += sizeof(float);
if (mlen>glob_script_mem.script_pram_size) {
- vtp[count].bits.is_permanent=0;
+ vtp[count].bits.is_permanent = 0;
return;
}
- *fp++=glob_script_mem.fvars[index];
+ *fp++ = glob_script_mem.fvars[index];
}
}
}
- char *cp=(char*)fp;
- for (uint8_t count=0; countglob_script_mem.script_pram_size) {
- vtp[count].bits.is_permanent=0;
+ vtp[count].bits.is_permanent = 0;
return;
}
- strcpy(cp,sp);
- cp+=slen+1;
+ strcpy(cp, sp);
+ cp += slen + 1;
}
}
}
@@ -4351,7 +4328,7 @@ const char HTTP_FORM_SCRIPT1b[] PROGMEM =
"});"
-#endif
+#endif //SCRIPT_STRIP_COMMENTS
"";
@@ -4415,38 +4392,38 @@ void script_upload_start(void) {
HTTPUpload& upload = Webserver->upload();
if (upload.status == UPLOAD_FILE_START) {
//AddLog_P(LOG_LEVEL_INFO, PSTR("HTP: upload start"));
- script_ex_ptr=(uint8_t*)glob_script_mem.script_ram;
+ script_ex_ptr = (uint8_t*)glob_script_mem.script_ram;
//AddLog_P2(LOG_LEVEL_INFO, PSTR("HTP: upload file %s, %d"),upload.filename.c_str(),upload.totalSize);
- if (strcmp(upload.filename.c_str(),"execute_script")) {
- Web.upload_error=1;
+ if (strcmp(upload.filename.c_str(), "execute_script")) {
+ Web.upload_error = 1;
WSSend(500, CT_PLAIN, F("500: wrong filename"));
return;
}
if (upload.totalSize>=glob_script_mem.script_size) {
- Web.upload_error=1;
+ Web.upload_error = 1;
WSSend(500, CT_PLAIN, F("500: file to large"));
return;
}
- uplsize=0;
+ uplsize = 0;
sc_state = bitRead(Settings.rule_enabled, 0);
- bitWrite(Settings.rule_enabled,0,0);
+ bitWrite(Settings.rule_enabled, 0, 0);
} else if(upload.status == UPLOAD_FILE_WRITE) {
//AddLog_P(LOG_LEVEL_INFO, PSTR("HTP: upload write"));
- uint32_t csiz=upload.currentSize;
- uint32_t tsiz=glob_script_mem.script_size-1;
+ uint32_t csiz = upload.currentSize;
+ uint32_t tsiz = glob_script_mem.script_size - 1;
if (uplsizeopen(path, FILE_READ);
+ File dir = fsp->open(path, FILE_READ);
if (dir) {
dir.rewindDirectory();
if (strlen(path)>1) {
- snprintf_P(npath,sizeof(npath),PSTR("http://%s/upl?download=%s"),WiFi.localIP().toString().c_str(),path);
- for (uint8_t cnt=strlen(npath)-1;cnt>0;cnt--) {
+ snprintf_P(npath, sizeof(npath), PSTR("http://%s/upl?download=%s"), WiFi.localIP().toString().c_str(),path);
+ for (uint8_t cnt = strlen(npath) - 1; cnt>0; cnt--) {
if (npath[cnt]=='/') {
- if (npath[cnt-1]=='=') npath[cnt+1]=0;
- else npath[cnt]=0;
+ if (npath[cnt - 1]=='=') npath[cnt + 1] = 0;
+ else npath[cnt] = 0;
break;
}
}
- WSContentSend_P(HTTP_FORM_SDC_DIRd,npath,path,"..");
+ WSContentSend_P(HTTP_FORM_SDC_DIRd, npath,path, "..");
}
char *ep;
while (true) {
- File entry=dir.openNextFile();
+ File entry = dir.openNextFile();
if (!entry) {
break;
}
// esp32 returns path here, shorten to filename
- ep=(char*)entry.name();
+ ep = (char*)entry.name();
if (*ep=='/') ep++;
char *lcp = strrchr(ep,'/');
if (lcp) {
- ep=lcp+1;
+ ep = lcp + 1;
}
//AddLog_P2(LOG_LEVEL_INFO, PSTR("entry: %s"),ep);
- time_t tm=entry.getLastWrite();
+ time_t tm = entry.getLastWrite();
char tstr[24];
strftime(tstr, 22, "%d-%m-%Y - %H:%M:%S ", localtime(&tm));
- char *pp=path;
- if (!*(pp+1)) pp++;
- char *cp=name;
+ char *pp = path;
+ if (!*(pp + 1)) pp++;
+ char *cp = name;
// osx formatted disks contain a lot of stuff we dont want
if (reject((char*)ep)) goto fclose;
- for (uint8_t cnt=0;cnt1) {
- strcat(path,"/");
+ strcat(path, "/");
}
- strcat(path,ep);
- ListDir(path,depth+4);
- path[plen]=0;
+ strcat(path, ep);
+ ListDir(path, depth + 4);
+ path[plen] = 0;
} else {
- snprintf_P(npath,sizeof(npath),HTTP_FORM_SDC_HREF,WiFi.localIP().toString().c_str(),pp,ep);
- WSContentSend_P(HTTP_FORM_SDC_DIRb,npath,ep,name,tstr,entry.size());
+ snprintf_P(npath, sizeof(npath), HTTP_FORM_SDC_HREF, WiFi.localIP().toString().c_str(), pp,ep);
+ WSContentSend_P(HTTP_FORM_SDC_DIRb, npath, ep, name, tstr, entry.size());
}
fclose:
entry.close();
@@ -4576,18 +4553,18 @@ void ListDir(char *path, uint8_t depth) {
char path[48];
void Script_FileUploadConfiguration(void) {
- uint8_t depth=0;
+ uint8_t depth = 0;
- strcpy(path,"/");
+ strcpy(path, "/");
if (!HttpCheckPriviledgedAccess()) { return; }
if (Webserver->hasArg("download")) {
String stmp = Webserver->arg("download");
- char *cp=(char*)stmp.c_str();
+ char *cp = (char*)stmp.c_str();
if (DownloadFile(cp)) {
// is directory
- strcpy(path,cp);
+ strcpy(path, cp);
}
}
@@ -4598,7 +4575,7 @@ void Script_FileUploadConfiguration(void) {
#ifdef SDCARD_DIR
WSContentSend_P(HTTP_FORM_SDC_DIRa);
if (glob_script_mem.script_sd_found) {
- ListDir(path,depth);
+ ListDir(path, depth);
}
WSContentSend_P(HTTP_FORM_SDC_DIRc);
#endif
@@ -4629,15 +4606,15 @@ void script_upload(void) {
char npath[48];
#if defined(ESP32) && defined(USE_SCRIPT_FATFS) && USE_SCRIPT_FATFS==-1
//sprintf(npath,"/%s",upload.filename.c_str());
- sprintf(npath,"%s/%s",path,upload.filename.c_str());
+ sprintf(npath, "%s/%s", path, upload.filename.c_str());
#else
- sprintf(npath,"%s/%s",path,upload.filename.c_str());
+ sprintf(npath, "%s/%s", path, upload.filename.c_str());
#endif
fsp->remove(npath);
- upload_file=fsp->open(npath,FILE_WRITE);
- if (!upload_file) Web.upload_error=1;
+ upload_file = fsp->open(npath, FILE_WRITE);
+ if (!upload_file) Web.upload_error = 1;
} else if(upload.status == UPLOAD_FILE_WRITE) {
- if (upload_file) upload_file.write(upload.buf,upload.currentSize);
+ if (upload_file) upload_file.write(upload.buf, upload.currentSize);
} else if(upload.status == UPLOAD_FILE_END) {
if (upload_file) upload_file.close();
if (Web.upload_error) {
@@ -4658,7 +4635,7 @@ uint8_t DownloadFile(char *file) {
return 0;
}
- download_file=fsp->open(file,FILE_READ);
+ download_file = fsp->open(file, FILE_READ);
if (!download_file) {
AddLog_P(LOG_LEVEL_INFO,PSTR("could not open file"));
return 0;
@@ -4669,20 +4646,20 @@ uint8_t DownloadFile(char *file) {
return 1;
}
- uint32_t flen=download_file.size();
+ uint32_t flen = download_file.size();
download_Client = Webserver->client();
Webserver->setContentLength(flen);
char attachment[100];
char *cp;
- for (uint8_t cnt=strlen(file); cnt>=0; cnt--) {
+ for (uint8_t cnt = strlen(file); cnt>=0; cnt--) {
if (file[cnt]=='/') {
- cp=&file[cnt+1];
+ cp = &file[cnt + 1];
break;
}
}
- snprintf_P(attachment, sizeof(attachment), PSTR("attachment; filename=%s"),cp);
+ snprintf_P(attachment, sizeof(attachment), PSTR("attachment; filename=%s"), cp);
Webserver->sendHeader(F("Content-Disposition"), attachment);
WSSend(200, CT_STREAM, "");
@@ -4690,15 +4667,15 @@ uint8_t DownloadFile(char *file) {
uint16_t bread;
// transfer is about 150kb/s
- uint8_t cnt=0;
+ uint8_t cnt = 0;
while (download_file.available()) {
- bread=download_file.read(buff,sizeof(buff));
- uint16_t bw=download_Client.write((const char*)buff,bread);
+ bread = download_file.read(buff, sizeof(buff));
+ uint16_t bw = download_Client.write((const char*)buff, bread);
if (!bw) break;
cnt++;
if (cnt>7) {
- cnt=0;
- if (glob_script_mem.script_loglevel&0x80) {
+ cnt = 0;
+ if (glob_script_mem.script_loglevel & 0x80) {
// this indeed multitasks, but is slower 50 kB/s
loop();
}
@@ -4747,8 +4724,8 @@ void HandleScriptConfiguration(void) {
#ifdef xSCRIPT_STRIP_COMMENTS
- uint16_t ssize=glob_script_mem.script_size;
- if (bitRead(Settings.rule_enabled, 1)) ssize*=2;
+ uint16_t ssize = glob_script_mem.script_size;
+ if (bitRead(Settings.rule_enabled, 1)) ssize *= 2;
WSContentSend_P(HTTP_FORM_SCRIPT1,1,1,bitRead(Settings.rule_enabled,0) ? " checked" : "",ssize);
#else
WSContentSend_P(HTTP_FORM_SCRIPT1,1,1,bitRead(Settings.rule_enabled,0) ? " checked" : "",glob_script_mem.script_size);
@@ -4763,10 +4740,10 @@ void HandleScriptConfiguration(void) {
#ifdef USE_SCRIPT_FATFS
if (glob_script_mem.script_sd_found) {
WSContentSend_P(HTTP_FORM_SCRIPT1d);
- if (glob_script_mem.flink[0][0]) WSContentSend_P(HTTP_FORM_SCRIPT1c,1,glob_script_mem.flink[0]);
- if (glob_script_mem.flink[1][0]) WSContentSend_P(HTTP_FORM_SCRIPT1c,2,glob_script_mem.flink[1]);
+ if (glob_script_mem.flink[0][0]) WSContentSend_P(HTTP_FORM_SCRIPT1c, 1, glob_script_mem.flink[0]);
+ if (glob_script_mem.flink[1][0]) WSContentSend_P(HTTP_FORM_SCRIPT1c, 2, glob_script_mem.flink[1]);
}
-#endif
+#endif //USE_SCRIPT_FATFS
WSContentSend_P(HTTP_SCRIPT_FORM_END);
WSContentSpaceButton(BUTTON_CONFIGURATION);
@@ -4777,22 +4754,22 @@ void SaveScript(void) {
#ifdef EEP_SCRIPT_SIZE
if (glob_script_mem.flags&1) {
- EEP_WRITE(0,EEP_SCRIPT_SIZE,glob_script_mem.script_ram);
+ EEP_WRITE(0, EEP_SCRIPT_SIZE, glob_script_mem.script_ram);
}
#endif // EEP_SCRIPT_SIZE
#ifdef USE_SCRIPT_FATFS
- if (glob_script_mem.flags&1) {
+ if (glob_script_mem.flags & 1) {
fsp->remove(FAT_SCRIPT_NAME);
- File file=fsp->open(FAT_SCRIPT_NAME,FILE_WRITE);
- file.write((const uint8_t*)glob_script_mem.script_ram,FAT_SCRIPT_SIZE);
+ File file = fsp->open(FAT_SCRIPT_NAME, FILE_WRITE);
+ file.write((const uint8_t*)glob_script_mem.script_ram, FAT_SCRIPT_SIZE);
file.close();
}
#endif // USE_SCRIPT_FATFS
#ifdef LITTLEFS_SCRIPT_SIZE
if (glob_script_mem.flags&1) {
- SaveFile("/script.txt",(uint8_t*)glob_script_mem.script_ram,LITTLEFS_SCRIPT_SIZE);
+ SaveFile("/script.txt", (uint8_t*)glob_script_mem.script_ram, LITTLEFS_SCRIPT_SIZE);
}
#endif // LITTLEFS_SCRIPT_SIZE
}
@@ -4800,9 +4777,9 @@ void SaveScript(void) {
void ScriptSaveSettings(void) {
if (Webserver->hasArg("c1")) {
- bitWrite(Settings.rule_enabled,0,1);
+ bitWrite(Settings.rule_enabled, 0, 1);
} else {
- bitWrite(Settings.rule_enabled,0,0);
+ bitWrite(Settings.rule_enabled, 0, 0);
}
@@ -4810,42 +4787,42 @@ void ScriptSaveSettings(void) {
if (*str.c_str()) {
- str.replace("\r\n","\n");
- str.replace("\r","\n");
+ str.replace("\r\n", "\n");
+ str.replace("\r", "\n");
#ifdef xSCRIPT_STRIP_COMMENTS
if (bitRead(Settings.rule_enabled, 1)) {
- char *sp=(char*)str.c_str();
- char *sp1=sp;
- char *dp=sp;
- uint8_t flg=0;
+ char *sp = (char*)str.c_str();
+ char *sp1 = sp;
+ char *dp = sp;
+ uint8_t flg = 0;
while (*sp) {
while (*sp==' ') sp++;
- sp1=sp;
- sp=strchr(sp,'\n');
+ sp1 = sp;
+ sp = strchr(sp,'\n');
if (!sp) {
- flg=1;
+ flg = 1;
} else {
- *sp=0;
+ *sp = 0;
}
if (*sp1!=';') {
- uint8_t slen=strlen(sp1);
+ uint8_t slen = strlen(sp1);
if (slen) {
- strcpy(dp,sp1);
- dp+=slen;
- *dp++='\n';
+ strcpy(dp, sp1);
+ dp += slen;
+ *dp++ = '\n';
}
}
if (flg) {
- *dp=0;
+ *dp = 0;
break;
}
sp++;
}
}
-#endif
+#endif //xSCRIPT_STRIP_COMMENTS
- strlcpy(glob_script_mem.script_ram,str.c_str(), glob_script_mem.script_size);
+ strlcpy(glob_script_mem.script_ram, str.c_str(), glob_script_mem.script_size);
if (glob_script_mem.script_ram[0]!='>' && glob_script_mem.script_ram[1]!='D') {
AddLog_P2(LOG_LEVEL_INFO, PSTR("script error: must start with >D"));
@@ -4863,8 +4840,8 @@ void SaveScriptEnd(void) {
if (glob_script_mem.script_mem) {
Scripter_save_pvars();
free(glob_script_mem.script_mem);
- glob_script_mem.script_mem=0;
- glob_script_mem.script_mem_size=0;
+ glob_script_mem.script_mem = 0;
+ glob_script_mem.script_mem_size = 0;
}
#ifdef USE_SCRIPT_COMPRESSION
@@ -4879,20 +4856,20 @@ void SaveScriptEnd(void) {
#endif // USE_SCRIPT_COMPRESSION
if (bitRead(Settings.rule_enabled, 0)) {
- int16_t res=Init_Scripter();
+ int16_t res = Init_Scripter();
if (res) {
AddLog_P2(LOG_LEVEL_INFO, PSTR("script init error: %d"), res);
return;
}
- Run_Scripter(">B\n",3,0);
- Run_Scripter(">BS",3,0);
+ Run_Scripter(">B\n", 3, 0);
+ Run_Scripter(">BS", 3, 0);
- fast_script=Run_Scripter(">F",-2,0);
+ fast_script = Run_Scripter(">F", -2, 0);
}
}
-#endif
+#endif // USE_WEBSERVER
#if defined(USE_SCRIPT_HUE) && defined(USE_WEBSERVER) && defined(USE_EMULATION) && defined(USE_EMULATION_HUE) && defined(USE_LIGHT)
@@ -5026,22 +5003,22 @@ break;
void Script_HueStatus(String *response, uint16_t hue_devs) {
if (hue_script[hue_devs].type=='p') {
- *response+=FPSTR(SCRIPT_HUE_LIGHTS_STATUS_JSON2);
- response->replace("{j1",hue_script[hue_devs].name);
+ *response += FPSTR(SCRIPT_HUE_LIGHTS_STATUS_JSON2);
+ response->replace("{j1", hue_script[hue_devs].name);
response->replace("{j2", GetHueDeviceId(hue_devs));
- uint8_t pwr=glob_script_mem.fvars[hue_script[hue_devs].index[0]-1];
+ uint8_t pwr = glob_script_mem.fvars[hue_script[hue_devs].index[0] - 1];
response->replace("{state}", (pwr ? "true" : "false"));
return;
}
- *response+=FPSTR(SCRIPT_HUE_LIGHTS_STATUS_JSON1);
- uint8_t pwr=glob_script_mem.fvars[hue_script[hue_devs].index[0]-1];
+ *response += FPSTR(SCRIPT_HUE_LIGHTS_STATUS_JSON1);
+ uint8_t pwr = glob_script_mem.fvars[hue_script[hue_devs].index[0] - 1];
response->replace("{state}", (pwr ? "true" : "false"));
String light_status = "";
if (hue_script[hue_devs].index[1]>0) {
// bri
light_status += "\"bri\":";
- uint32_t bri=glob_script_mem.fvars[hue_script[hue_devs].index[1]-1];
+ uint32_t bri = glob_script_mem.fvars[hue_script[hue_devs].index[1] - 1];
if (bri > 254) bri = 254;
if (bri < 1) bri = 1;
light_status += String(bri);
@@ -5049,7 +5026,7 @@ void Script_HueStatus(String *response, uint16_t hue_devs) {
}
if (hue_script[hue_devs].index[2]>0) {
// hue
- uint32_t hue=glob_script_mem.fvars[hue_script[hue_devs].index[2]-1];
+ uint32_t hue = glob_script_mem.fvars[hue_script[hue_devs].index[2] - 1];
//hue = changeUIntScale(hue, 0, 359, 0, 65535);
light_status += "\"hue\":";
light_status += String(hue);
@@ -5057,7 +5034,7 @@ void Script_HueStatus(String *response, uint16_t hue_devs) {
}
if (hue_script[hue_devs].index[3]>0) {
// sat
- uint32_t sat=glob_script_mem.fvars[hue_script[hue_devs].index[3]-1] ;
+ uint32_t sat = glob_script_mem.fvars[hue_script[hue_devs].index[3] - 1] ;
if (sat > 254) sat = 254;
if (sat < 1) sat = 1;
light_status += "\"sat\":";
@@ -5066,7 +5043,7 @@ void Script_HueStatus(String *response, uint16_t hue_devs) {
}
if (hue_script[hue_devs].index[4]>0) {
// ct
- uint32_t ct=glob_script_mem.fvars[hue_script[hue_devs].index[4]-1];
+ uint32_t ct = glob_script_mem.fvars[hue_script[hue_devs].index[4] - 1];
light_status += "\"ct\":";
light_status += String(ct);
light_status += ",";
@@ -5101,7 +5078,7 @@ void Script_HueStatus(String *response, uint16_t hue_devs) {
}
response->replace("{light_status}", light_status);
- response->replace("{j1",hue_script[hue_devs].name);
+ response->replace("{j1", hue_script[hue_devs].name);
response->replace("{j2", GetHueDeviceId(hue_devs));
}
@@ -5109,14 +5086,14 @@ void Script_HueStatus(String *response, uint16_t hue_devs) {
void Script_Check_Hue(String *response) {
if (!bitRead(Settings.rule_enabled, 0)) return;
- uint8_t hue_script_found=Run_Scripter(">H",-2,0);
+ uint8_t hue_script_found = Run_Scripter(">H", -2, 0);
if (hue_script_found!=99) return;
char tmp[256];
- uint8_t hue_devs=0;
- uint8_t vindex=0;
+ uint8_t hue_devs = 0;
+ uint8_t vindex = 0;
char *cp;
- char *lp=glob_script_mem.section_ptr+2;
+ char *lp = glob_script_mem.section_ptr + 2;
while (lp) {
SCRIPT_SKIP_SPACES
while (*lp==SCRIPT_EOL) {
@@ -5127,69 +5104,69 @@ void Script_Check_Hue(String *response) {
}
if (*lp!=';') {
// check this line
- Replace_Cmd_Vars(lp,1,tmp,sizeof(tmp));
+ Replace_Cmd_Vars(lp, 1, tmp, sizeof(tmp));
// check for hue defintions
// NAME, TYPE , vars
- cp=tmp;
- cp=strchr(cp,',');
+ cp = tmp;
+ cp = strchr(cp,',');
if (!cp) break;
- *cp=0;
+ *cp = 0;
// copy name
- strlcpy(hue_script[hue_devs].name,tmp,HUE_DEV_NSIZE);
+ strlcpy(hue_script[hue_devs].name, tmp, HUE_DEV_NSIZE);
cp++;
while (*cp==' ') cp++;
// get type
- hue_script[hue_devs].type=*cp;
+ hue_script[hue_devs].type = *cp;
- for (vindex=0;vindex0) *response+=",\"";
- else *response+="\"";
+ if (hue_devs>0) *response += ",\"";
+ else *response += "\"";
}
- *response+=String(EncodeLightId(hue_devs+devices_present+1))+"\":";
- Script_HueStatus(response,hue_devs);
+ *response += String(EncodeLightId(hue_devs + devices_present + 1))+"\":";
+ Script_HueStatus(response, hue_devs);
//AddLog_P2(LOG_LEVEL_INFO, PSTR("Hue: %s - %d "),response->c_str(), hue_devs);
}
@@ -5252,13 +5229,13 @@ void Script_Handle_Hue(String *path) {
bool resp = false;
uint8_t device = DecodeLightId(atoi(path->c_str()));
- uint8_t index = device-devices_present-1;
+ uint8_t index = device - devices_present - 1;
if (Webserver->args()) {
response = "[";
StaticJsonBuffer<400> jsonBuffer;
- JsonObject &hue_json = jsonBuffer.parseObject(Webserver->arg((Webserver->args())-1));
+ JsonObject &hue_json = jsonBuffer.parseObject(Webserver->arg((Webserver->args()) - 1));
if (hue_json.containsKey("on")) {
response += FPSTR(sHUE_LIGHT_RESPONSE_JSON);
@@ -5267,26 +5244,26 @@ void Script_Handle_Hue(String *path) {
bool on = hue_json["on"];
if (on==false) {
- glob_script_mem.fvars[hue_script[index].index[0]-1]=0;
+ glob_script_mem.fvars[hue_script[index].index[0] - 1] = 0;
response.replace("{re", "false");
} else {
- glob_script_mem.fvars[hue_script[index].index[0]-1]=1;
+ glob_script_mem.fvars[hue_script[index].index[0] - 1] = 1;
response.replace("{re", "true");
}
- glob_script_mem.type[hue_script[index].vindex[0]].bits.changed=1;
+ glob_script_mem.type[hue_script[index].vindex[0]].bits.changed = 1;
resp = true;
}
if (hue_json.containsKey("bri")) { // Brightness is a scale from 1 (the minimum the light is capable of) to 254 (the maximum). Note: a brightness of 1 is not off.
tmp = hue_json["bri"];
- bri=tmp;
+ bri = tmp;
if (254 <= bri) { bri = 255; }
if (resp) { response += ","; }
response += FPSTR(sHUE_LIGHT_RESPONSE_JSON);
response.replace("{id", String(EncodeLightId(device)));
response.replace("{cm", "bri");
response.replace("{re", String(tmp));
- glob_script_mem.fvars[hue_script[index].index[1]-1]=bri;
- glob_script_mem.type[hue_script[index].vindex[1]].bits.changed=1;
+ glob_script_mem.fvars[hue_script[index].index[1] - 1] = bri;
+ glob_script_mem.type[hue_script[index].vindex[1]].bits.changed = 1;
resp = true;
}
if (hue_json.containsKey("xy")) { // Saturation of the light. 254 is the most saturated (colored) and 0 is the least saturated (white).
@@ -5303,10 +5280,10 @@ void Script_Handle_Hue(String *path) {
response.replace("{id", String(device));
response.replace("{cm", "xy");
response.replace("{re", "[" + x_str + "," + y_str + "]");
- glob_script_mem.fvars[hue_script[index].index[2]-1]=hue;
- glob_script_mem.type[hue_script[index].vindex[2]].bits.changed=1;
- glob_script_mem.fvars[hue_script[index].index[3]-1]=sat;
- glob_script_mem.type[hue_script[index].vindex[3]].bits.changed=1;
+ glob_script_mem.fvars[hue_script[index].index[2]-1] = hue;
+ glob_script_mem.type[hue_script[index].vindex[2]].bits.changed = 1;
+ glob_script_mem.fvars[hue_script[index].index[3]-1] = sat;
+ glob_script_mem.type[hue_script[index].vindex[3]].bits.changed = 1;
resp = true;
}
@@ -5314,27 +5291,27 @@ void Script_Handle_Hue(String *path) {
tmp = hue_json["hue"];
//hue = changeUIntScale(tmp, 0, 65535, 0, 359);
//tmp = changeUIntScale(hue, 0, 359, 0, 65535);
- hue=tmp;
+ hue = tmp;
if (resp) { response += ","; }
response += FPSTR(sHUE_LIGHT_RESPONSE_JSON);
response.replace("{id", String(EncodeLightId(device)));
response.replace("{cm", "hue");
response.replace("{re", String(tmp));
- glob_script_mem.fvars[hue_script[index].index[2]-1]=hue;
- glob_script_mem.type[hue_script[index].vindex[2]].bits.changed=1;
+ glob_script_mem.fvars[hue_script[index].index[2] - 1] = hue;
+ glob_script_mem.type[hue_script[index].vindex[2]].bits.changed = 1;
resp = true;
}
if (hue_json.containsKey("sat")) { // Saturation of the light. 254 is the most saturated (colored) and 0 is the least saturated (white).
tmp = hue_json["sat"];
- sat=tmp;
+ sat = tmp;
if (254 <= sat) { sat = 255; }
if (resp) { response += ","; }
response += FPSTR(sHUE_LIGHT_RESPONSE_JSON);
response.replace("{id", String(EncodeLightId(device)));
response.replace("{cm", "sat");
response.replace("{re", String(tmp));
- glob_script_mem.fvars[hue_script[index].index[3]-1]=sat;
- glob_script_mem.type[hue_script[index].vindex[3]].bits.changed=1;
+ glob_script_mem.fvars[hue_script[index].index[3] - 1] = sat;
+ glob_script_mem.type[hue_script[index].vindex[3]].bits.changed = 1;
resp = true;
}
if (hue_json.containsKey("ct")) { // Color temperature 153 (Cold) to 500 (Warm)
@@ -5344,8 +5321,8 @@ void Script_Handle_Hue(String *path) {
response.replace("{id", String(EncodeLightId(device)));
response.replace("{cm", "ct");
response.replace("{re", String(ct));
- glob_script_mem.fvars[hue_script[index].index[4]-1]=ct;
- glob_script_mem.type[hue_script[index].vindex[4]].bits.changed=1;
+ glob_script_mem.fvars[hue_script[index].index[4] - 1] = ct;
+ glob_script_mem.type[hue_script[index].vindex[4]].bits.changed = 1;
resp = true;
}
response += "]";
@@ -5356,7 +5333,7 @@ void Script_Handle_Hue(String *path) {
AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR(D_LOG_HTTP D_HUE " Result (%s)"), response.c_str());
WSSend(code, CT_JSON, response);
if (resp) {
- Run_Scripter(">E",2,0);
+ Run_Scripter(">E", 2, 0);
}
}
#endif // hue interface
@@ -5369,30 +5346,30 @@ bool Script_SubCmd(void) {
if (tasm_cmd_activ) return false;
char command[CMDSZ];
- strlcpy(command,XdrvMailbox.topic,CMDSZ);
- uint32_t pl=XdrvMailbox.payload;
+ strlcpy(command, XdrvMailbox.topic, CMDSZ);
+ uint32_t pl = XdrvMailbox.payload;
char pld[64];
- strlcpy(pld,XdrvMailbox.data,sizeof(pld));
+ strlcpy(pld, XdrvMailbox.data, sizeof(pld));
char cmdbuff[128];
- char *cp=cmdbuff;
- *cp++='#';
- strcpy(cp,XdrvMailbox.topic);
- uint8_t tlen=strlen(XdrvMailbox.topic);
- cp+=tlen;
+ char *cp = cmdbuff;
+ *cp++ = '#';
+ strcpy(cp, XdrvMailbox.topic);
+ uint8_t tlen = strlen(XdrvMailbox.topic);
+ cp += tlen;
if (XdrvMailbox.index > 0) {
- *cp++=XdrvMailbox.index|0x30;
+ *cp++ = XdrvMailbox.index | 0x30;
tlen++;
}
if ((XdrvMailbox.payload>0) || (XdrvMailbox.data_len>0)) {
- *cp++='(';
- strncpy(cp,XdrvMailbox.data,XdrvMailbox.data_len);
- cp+=XdrvMailbox.data_len;
- *cp++=')';
- *cp=0;
+ *cp++ = '(';
+ strncpy(cp, XdrvMailbox.data,XdrvMailbox.data_len);
+ cp += XdrvMailbox.data_len;
+ *cp++ = ')';
+ *cp = 0;
}
//toLog(cmdbuff);
- uint32_t res=Run_Scripter(cmdbuff,tlen+1,0);
+ uint32_t res = Run_Scripter(cmdbuff, tlen + 1, 0);
//AddLog_P2(LOG_LEVEL_INFO,">>%d",res);
if (res) return false;
else {
@@ -5404,14 +5381,14 @@ bool Script_SubCmd(void) {
}
return true;
}
-#endif
+#endif //USE_SCRIPT_SUB_COMMAND
void execute_script(char *script) {
- char *svd_sp=glob_script_mem.scriptptr;
- strcat(script,"\n#");
- glob_script_mem.scriptptr=script;
- Run_Scripter(">",1,0);
- glob_script_mem.scriptptr=svd_sp;
+ char *svd_sp = glob_script_mem.scriptptr;
+ strcat(script, "\n#");
+ glob_script_mem.scriptptr = script;
+ Run_Scripter(">", 1, 0);
+ glob_script_mem.scriptptr = svd_sp;
}
#define D_CMND_SCRIPT "Script"
#define D_CMND_SUBSCRIBE "Subscribe"
@@ -5441,44 +5418,44 @@ bool ScriptCommand(void) {
break;
#ifdef xSCRIPT_STRIP_COMMENTS
case 2:
- bitWrite(Settings.rule_enabled, 1,0);
+ bitWrite(Settings.rule_enabled, 1, 0);
break;
case 3:
- bitWrite(Settings.rule_enabled, 1,1);
+ bitWrite(Settings.rule_enabled, 1, 1);
break;
-#endif
+#endif //xSCRIPT_STRIP_COMMENTS
}
} else {
if ('>' == XdrvMailbox.data[0]) {
// execute script
- snprintf_P (mqtt_data, sizeof(mqtt_data), PSTR("{\"%s\":\"%s\"}"),command,XdrvMailbox.data);
+ snprintf_P (mqtt_data, sizeof(mqtt_data), PSTR("{\"%s\":\"%s\"}"), command,XdrvMailbox.data);
if (bitRead(Settings.rule_enabled, 0)) {
- for (uint8_t count=0; count, [, ]
String result = ScriptSubscribe(XdrvMailbox.data, XdrvMailbox.data_len);
@@ -5486,7 +5463,7 @@ bool ScriptCommand(void) {
} else if (CMND_UNSUBSCRIBE == command_code) { //MQTT Un-subscribe command. UnSubscribe
String result = ScriptUnsubscribe(XdrvMailbox.data, XdrvMailbox.data_len);
Response_P(S_JSON_COMMAND_SVALUE, command, result.c_str());
-#endif //SUPPORT_MQTT_EVENT
+#endif //SUPPORT_MQTT_EVENT
}
return serviced;
}
@@ -5508,7 +5485,7 @@ void dateTime(uint16_t* date, uint16_t* time) {
*time = xFAT_TIME(RtcTime.hour,RtcTime.minute,RtcTime.second);
}
-#endif
+#endif //USE_SCRIPT_FATFS
@@ -5519,7 +5496,7 @@ void dateTime(uint16_t* date, uint16_t* time) {
#endif
#ifndef MQTT_EVENT_JSIZE
#define MQTT_EVENT_JSIZE 400
-#endif
+#endif //SUPPORT_MQTT_EVENT
/********************************************************************************************/
/*
@@ -5565,21 +5542,21 @@ bool ScriptMqttData(void)
if ((dot = key1.indexOf('.')) > 0) {
key2 = key1.substring(dot+1);
key1 = key1.substring(0, dot);
- lkey=key2;
+ lkey = key2;
if (!jsonData[key1][key2].success()) break; //Failed to get the key/value, ignore this message.
value = (const char *)jsonData[key1][key2];
} else {
if (!jsonData[key1].success()) break;
value = (const char *)jsonData[key1];
- lkey=key1;
+ lkey = key1;
}
}
value.trim();
char sbuffer[128];
- if (!strncmp(lkey.c_str(),"Epoch",5)) {
- uint32_t ep=atoi(value.c_str())-(uint32_t)EPOCH_OFFSET;
- snprintf_P(sbuffer, sizeof(sbuffer), PSTR(">%s=%d\n"), event_item.Event.c_str(),ep);
+ if (!strncmp(lkey.c_str(), "Epoch", 5)) {
+ uint32_t ep = atoi(value.c_str()) - (uint32_t)EPOCH_OFFSET;
+ snprintf_P(sbuffer, sizeof(sbuffer), PSTR(">%s=%d\n"), event_item.Event.c_str(), ep);
} else {
snprintf_P(sbuffer, sizeof(sbuffer), PSTR(">%s=\"%s\"\n"), event_item.Event.c_str(), value.c_str());
}
@@ -5611,7 +5588,7 @@ String ScriptSubscribe(const char *data, int data_len)
MQTT_Subscription subscription_item;
String events;
if (data_len > 0) {
- char parameters[data_len+1];
+ char parameters[data_len + 1];
memcpy(parameters, data, data_len);
parameters[data_len] = '\0';
String event_name, topic, key;
@@ -5632,7 +5609,7 @@ String ScriptSubscribe(const char *data, int data_len)
//event_name.toUpperCase();
if (event_name.length() > 0 && topic.length() > 0) {
//Search all subscriptions
- for (uint32_t index=0; index < subscriptions.size(); index++) {
+ for (uint32_t index = 0; index < subscriptions.size(); index++) {
if (subscriptions.get(index).Event.equals(event_name)) {
//If find exists one, remove it.
String stopic = subscriptions.get(index).Topic + "/#";
@@ -5665,7 +5642,7 @@ String ScriptSubscribe(const char *data, int data_len)
}
} else {
//If did not specify the event name, list all subscribed event
- for (uint32_t index=0; index < subscriptions.size(); index++) {
+ for (uint32_t index = 0; index < subscriptions.size(); index++) {
subscription_item = subscriptions.get(index);
events.concat(subscription_item.Event + "," + subscription_item.Topic
+ (subscription_item.Key.length()>0 ? "," : "")
@@ -5795,15 +5772,15 @@ void ScriptGetSDCard(void) {
if (!HttpCheckPriviledgedAccess()) { return; }
String stmp = Webserver->uri();
- char *cp=strstr_P(stmp.c_str(),PSTR("/sdc/"));
+ char *cp = strstr_P(stmp.c_str(), PSTR("/sdc/"));
// if (cp) Serial.printf(">>>%s\n",cp);
if (cp) {
#ifdef ESP32
- cp+=4;
+ cp += 4;
#else
- cp+=5;
+ cp += 5;
#endif
- if (strstr_P(cp,PSTR("scrdmp.bmp"))) {
+ if (strstr_P(cp, PSTR("scrdmp.bmp"))) {
SendFile(cp);
return;
} else {
@@ -5821,34 +5798,34 @@ extern uint8_t *buffer;
void SendFile(char *fname) {
char buff[512];
const char *mime;
- uint8_t sflg=0;
- char *jpg=strstr(fname,".jpg");
+ uint8_t sflg = 0;
+ char *jpg = strstr(fname,".jpg");
if (jpg) {
- mime="image/jpeg";
+ mime = "image/jpeg";
}
#ifdef USE_DISPLAY_DUMP
- char *sbmp=strstr_P(fname,PSTR("scrdmp.bmp"));
+ char *sbmp = strstr_P(fname, PSTR("scrdmp.bmp"));
if (sbmp) {
- mime="image/bmp";
- sflg=1;
+ mime = "image/bmp";
+ sflg = 1;
}
#endif // USE_DISPLAY_DUMP
- char *bmp=strstr(fname,".bmp");
+ char *bmp = strstr(fname, ".bmp");
if (bmp) {
- mime="image/bmp";
+ mime = "image/bmp";
}
- char *html=strstr(fname,".html");
+ char *html = strstr(fname, ".html");
if (html) {
- mime="text/html";
+ mime = "text/html";
}
- char *txt=strstr(fname,".txt");
+ char *txt = strstr(fname, ".txt");
if (txt) {
- mime="text/plain";
+ mime = "text/plain";
}
- WSContentSend_P(HTTP_SCRIPT_MIMES,fname,mime);
+ WSContentSend_P(HTTP_SCRIPT_MIMES, fname, mime);
if (sflg) {
#ifdef USE_DISPLAY_DUMP
@@ -5856,8 +5833,8 @@ char buff[512];
#define fileHeaderSize 14
#define infoHeaderSize 40
if (buffer) {
- uint8_t *bp=buffer;
- uint8_t *lbuf=(uint8_t*)calloc(Settings.display_width+2,3);
+ uint8_t *bp = buffer;
+ uint8_t *lbuf = (uint8_t*)calloc(Settings.display_width + 2, 3);
uint8_t *lbp;
uint8_t fileHeader[fileHeaderSize];
createBitmapFileHeader(Settings.display_height , Settings.display_width , fileHeader);
@@ -5865,37 +5842,37 @@ char buff[512];
uint8_t infoHeader[infoHeaderSize];
createBitmapInfoHeader(Settings.display_height, Settings.display_width, infoHeader );
Webserver->client().write((uint8_t *)infoHeader, infoHeaderSize);
- for (uint32_t lins=0; lins>1;
+ bits = bits>>1;
}
bp++;
}
- Webserver->client().write((const char*)lbuf, Settings.display_width*3);
+ Webserver->client().write((const char*)lbuf, Settings.display_width * 3);
}
if (lbuf) free(lbuf);
Webserver->client().stop();
}
#endif // USE_DISPLAY_DUMP
} else {
- File file=fsp->open(fname,FILE_READ);
+ File file = fsp->open(fname,FILE_READ);
uint32_t siz = file.size();
- uint32_t len=sizeof(buff);
+ uint32_t len = sizeof(buff);
while (siz > 0) {
- if (len>siz) len=siz;
- file.read((uint8_t *)buff,len );
+ if (len>siz) len = siz;
+ file.read((uint8_t *)buff, len);
Webserver->client().write((const char*)buff, len);
siz -= len;
}
@@ -5930,7 +5907,7 @@ void ScriptFullWebpage(void) {
}
WSContentBegin(200, CT_HTML);
- const char *title="Full Screen";
+ const char *title = "Full Screen";
WSContentSend_P(HTTP_SCRIPT_FULLPAGE1, SettingsText(SET_DEVICENAME), title, fullpage_refresh);
WSContentSend_P(HTTP_SCRIPT_FULLPAGE2, fullpage_refresh);
//WSContentSend_P(PSTR(""));
@@ -5954,34 +5931,34 @@ void Script_Check_HTML_Setvars(void) {
if (Webserver->hasArg("sv")) {
String stmp = Webserver->arg("sv");
- Serial.printf("fwp has arg dv %s\n",stmp.c_str());
+ Serial.printf("fwp has arg dv %s\n", stmp.c_str());
char cmdbuf[64];
- memset(cmdbuf,0,sizeof(cmdbuf));
- char *cp=cmdbuf;
- *cp++='>';
- strncpy(cp,stmp.c_str(),sizeof(cmdbuf)-1);
- char *cp1=strchr(cp,'_');
+ memset(cmdbuf, 0, sizeof(cmdbuf));
+ char *cp = cmdbuf;
+ *cp++ = '>';
+ strncpy(cp, stmp.c_str(), sizeof(cmdbuf) - 1);
+ char *cp1 = strchr(cp, '_');
if (!cp1) return;
- *cp1=0;
+ *cp1 = 0;
char vname[32];
- strncpy(vname,cp,sizeof(vname));
- *cp1='=';
+ strncpy(vname, cp, sizeof(vname));
+ *cp1 = '=';
cp1++;
struct T_INDEX ind;
uint8_t vtype;
- isvar(vname,&vtype,&ind,0,0,0);
+ isvar(vname, &vtype, &ind, 0, 0, 0);
if (vtype!=NUM_RES && vtype&STYPE) {
// string type must insert quotes
- uint8_t tlen=strlen(cp1);
- memmove(cp1+1,cp1,tlen);
- *cp1='\"';
- *(cp1+tlen+1)='\"';
+ uint8_t tlen = strlen(cp1);
+ memmove(cp1 + 1, cp1, tlen);
+ *cp1 = '\"';
+ *(cp1 + tlen +1 ) = '\"';
}
//toLog(cmdbuf);
execute_script(cmdbuf);
- Run_Scripter(">E",2,0);
+ Run_Scripter(">E", 2, 0);
}
}
@@ -6048,7 +6025,9 @@ const char SCRIPT_MSG_GTABLEd[] PROGMEM =
const char SCRIPT_MSG_GTABLEb[] PROGMEM =
"]);"
- "var options={%s" CHART_EXTRA_OPTIONS "};"
+ "var options={%s" CHART_EXTRA_OPTIONS "};";
+
+const char SCRIPT_MSG_GTABLEbx[] PROGMEM =
"var chart=new google.visualization.%s(document.getElementById('chart%1d'));"
"chart.draw(data,options);}"
"google.charts.setOnLoadCallback(drawChart);";
@@ -6087,60 +6066,60 @@ const char SCRIPT_MSG_GTE1[] PROGMEM = "'%s'";
char *gc_get_arrays(char *lp, float **arrays, uint8_t *ranum, uint16_t *rentries, uint16_t *ipos) {
struct T_INDEX ind;
uint8_t vtype;
-uint16 entries=0;
-uint16_t cipos=0;
+uint16 entries = 0;
+uint16_t cipos = 0;
- uint8_t anum=0;
+ uint8_t anum = 0;
while (anum> 2 %d\n",len);
if (fa && len>=entries) {
if (!entries) {
entries = len;
}
// add array to list
- arrays[anum]=fa;
+ arrays[anum] = fa;
anum++;
}
} else {
// single numeric
- arrays[anum]=&glob_script_mem.fvars[index];
+ arrays[anum] = &glob_script_mem.fvars[index];
anum++;
- entries=1;
+ entries = 1;
}
} else {
- lp=lp1;
+ lp = lp1;
break;
}
}
}
//Serial.printf(">> %d - %d - %d\n",anum,entries,(uint32_t)*arrays[0]);
- *ranum=anum;
- *rentries=entries;
- *ipos=cipos;
+ *ranum = anum;
+ *rentries = entries;
+ *ipos = cipos;
return lp;
}
char *gc_send_labels(char *lp,uint32_t anum) {
WSContentSend_PD("[");
- for (uint32_t cnt=0; cntw",-2,0);
+ web_script = Run_Scripter(">w", -2, 0);
} else {
- web_script=Run_Scripter(">W",-2,0);
+ web_script = Run_Scripter(">W", -2, 0);
}
if (web_script==99) {
char tmp[256];
- uint8_t optflg=0;
- uint8_t chartindex=1;
- uint8_t google_libs=0;
- char *lp=glob_script_mem.section_ptr+2;
+ uint8_t optflg = 0;
+ uint8_t chartindex = 1;
+ uint8_t google_libs = 0;
+ char *lp = glob_script_mem.section_ptr + 2;
if (mc=='w') {
while (*lp) {
if (*lp=='\n') break;
@@ -6194,175 +6173,175 @@ void ScriptWebShow(char mc) {
}
if (*lp!=';') {
// send this line to web
- Replace_Cmd_Vars(lp,1,tmp,sizeof(tmp));
- char *lin=tmp;
+ Replace_Cmd_Vars(lp, 1, tmp, sizeof(tmp));
+ char *lin = tmp;
if ((!mc && (*lin!='$')) || (mc=='w' && (*lin!='$'))) {
// normal web section
if (*lin=='@') {
lin++;
- optflg=1;
+ optflg = 1;
} else {
- optflg=0;
+ optflg = 0;
}
// check for input elements
- if (!strncmp(lin,"sl(",3)) {
+ if (!strncmp(lin, "sl(", 3)) {
// insert slider sl(min max var left mid right)
- char *lp=lin;
+ char *lp = lin;
float min;
- lp=GetNumericResult(lp+3,OPER_EQU,&min,0);
+ lp = GetNumericArgument(lp + 3, OPER_EQU, &min, 0);
SCRIPT_SKIP_SPACES
// arg2
float max;
- lp=GetNumericResult(lp,OPER_EQU,&max,0);
+ lp = GetNumericArgument(lp, OPER_EQU, &max, 0);
SCRIPT_SKIP_SPACES
float val;
- char *slp=lp;
- lp=GetNumericResult(lp,OPER_EQU,&val,0);
+ char *slp = lp;
+ lp = GetNumericArgument(lp, OPER_EQU, &val, 0);
SCRIPT_SKIP_SPACES
char vname[16];
- ScriptGetVarname(vname,slp,sizeof(vname));
+ ScriptGetVarname(vname, slp, sizeof(vname));
char left[SCRIPT_MAXSSIZE];
- lp=GetStringResult(lp,OPER_EQU,left,0);
+ lp = GetStringArgument(lp, OPER_EQU, left, 0);
SCRIPT_SKIP_SPACES
char mid[SCRIPT_MAXSSIZE];
- lp=GetStringResult(lp,OPER_EQU,mid,0);
+ lp = GetStringArgument(lp, OPER_EQU, mid, 0);
SCRIPT_SKIP_SPACES
char right[SCRIPT_MAXSSIZE];
- lp=GetStringResult(lp,OPER_EQU,right,0);
+ lp = GetStringArgument(lp, OPER_EQU, right, 0);
SCRIPT_SKIP_SPACES
- WSContentSend_PD(SCRIPT_MSG_SLIDER,left,mid,right,(uint32_t)min,(uint32_t)max,(uint32_t)val,vname);
+ WSContentSend_PD(SCRIPT_MSG_SLIDER, left,mid, right, (uint32_t)min, (uint32_t)max, (uint32_t)val, vname);
- } else if (!strncmp(lin,"ck(",3)) {
- char *lp=lin+3;
- char *slp=lp;
+ } else if (!strncmp(lin, "ck(", 3)) {
+ char *lp = lin + 3;
+ char *slp = lp;
float val;
- lp=GetNumericResult(lp,OPER_EQU,&val,0);
+ lp = GetNumericArgument(lp, OPER_EQU, &val, 0);
SCRIPT_SKIP_SPACES
char vname[16];
- ScriptGetVarname(vname,slp,sizeof(vname));
+ ScriptGetVarname(vname, slp, sizeof(vname));
char label[SCRIPT_MAXSSIZE];
- lp=GetStringResult(lp,OPER_EQU,label,0);
+ lp = GetStringArgument(lp, OPER_EQU, label, 0);
const char *cp;
uint8_t uval;
if (val>0) {
- cp="checked='checked'";
- uval=0;
+ cp = "checked='checked'";
+ uval = 0;
} else {
- cp="";
- uval=1;
+ cp = "";
+ uval = 1;
}
- WSContentSend_PD(SCRIPT_MSG_CHKBOX,label,(char*)cp,uval,vname);
+ WSContentSend_PD(SCRIPT_MSG_CHKBOX, label, (char*)cp, uval, vname);
- } else if (!strncmp(lin,"bu(",3)) {
- char *lp=lin+3;
- uint8_t bcnt=0;
- char *found=lin;
+ } else if (!strncmp(lin, "bu(", 3)) {
+ char *lp = lin + 3;
+ uint8_t bcnt = 0;
+ char *found = lin;
while (bcnt<4) {
- found=strstr(found,"bu(");
+ found = strstr(found, "bu(");
if (!found) break;
- found+=3;
+ found += 3;
bcnt++;
}
- uint8_t proz=100/bcnt;
- if (!optflg && bcnt>1) proz-=2;
+ uint8_t proz = 100 / bcnt;
+ if (!optflg && bcnt>1) proz -= 2;
if (optflg) WSContentSend_PD(SCRIPT_MSG_BUT_START_TBL);
else WSContentSend_PD(SCRIPT_MSG_BUT_START);
- for (uint32_t cnt=0;cnt0) {
- cp=ontxt;
- uval=0;
+ cp = ontxt;
+ uval = 0;
} else {
- cp=offtxt;
- uval=1;
+ cp = offtxt;
+ uval = 1;
}
if (bcnt>1 && cnt==bcnt-1) {
- if (!optflg) proz+=2;
+ if (!optflg) proz += 2;
}
if (!optflg) {
- WSContentSend_PD(SCRIPT_MSG_BUTTONa,proz,uval,vname,cp);
+ WSContentSend_PD(SCRIPT_MSG_BUTTONa, proz, uval, vname, cp);
} else {
- WSContentSend_PD(SCRIPT_MSG_BUTTONa_TBL,proz,uval,vname,cp);
+ WSContentSend_PD(SCRIPT_MSG_BUTTONa_TBL, proz, uval, vname, cp);
}
if (bcnt>1 && cnt%s"),lin);
+ WSContentSend_PD(PSTR("%s
"), lin);
} else {
- WSContentSend_PD(PSTR("{s}%s{e}"),lin);
+ WSContentSend_PD(PSTR("{s}%s{e}"), lin);
}
}
}
@@ -6375,58 +6354,67 @@ void ScriptWebShow(char mc) {
lin++;
exgc:
char *lp;
- if (!strncmp(lin,"gc(",3)) {
+ if (!strncmp(lin, "gc(", 3)) {
// get google table
- lp=lin+3;
+ lp = lin + 3;
SCRIPT_SKIP_SPACES
const char *type;
const char *func;
char options[312];
- uint8_t nanum=MAX_GARRAY;
- uint8_t y2f=0;
+ uint8_t nanum = MAX_GARRAY;
+ uint8_t y2f = 0;
+ uint8_t tonly = 0;
char ctype;
- ctype=*lp;
+ ctype = *lp;
lp++;
- if (!(google_libs&GLIBS_MAIN)) {
- google_libs|=GLIBS_MAIN;
+ if (!(google_libs & GLIBS_MAIN)) {
+ google_libs |= GLIBS_MAIN;
WSContentSend_PD(SCRIPT_MSG_GTABLE);
}
switch (ctype) {
case 'l':
- type=PSTR("LineChart");
+ type = PSTR("LineChart");
break;
case 'b':
- type=PSTR("BarChart");
+ type = PSTR("BarChart");
break;
case 'p':
- type=PSTR("PieChart");
+ type = PSTR("PieChart");
break;
case 'g':
- type=PSTR("Gauge");
- if (!(google_libs&GLIBS_GAUGE)) {
- google_libs|=GLIBS_GAUGE;
+ type = PSTR("Gauge");
+ if (!(google_libs & GLIBS_GAUGE)) {
+ google_libs |= GLIBS_GAUGE;
WSContentSend_PD(SCRIPT_MSG_GAUGE);
}
break;
case 't':
- type=PSTR("Table");
- if (!(google_libs&GLIBS_TABLE)) {
- google_libs|=GLIBS_TABLE;
+ type = PSTR("Table");
+ if (!(google_libs & GLIBS_TABLE)) {
+ google_libs |= GLIBS_TABLE;
WSContentSend_PD(SCRIPT_MSG_TABLE);
}
break;
case 'T':
- type=PSTR("Timeline");
- if (!(google_libs&GLIBS_TIMELINE)) {
- google_libs|=GLIBS_TIMELINE;
+ type = PSTR("Timeline");
+ if (!(google_libs & GLIBS_TIMELINE)) {
+ google_libs |= GLIBS_TIMELINE;
WSContentSend_PD(SCRIPT_MSG_TIMELINE);
}
break;
case 'h':
- type=PSTR("Histogram");
+ type = PSTR("Histogram");
break;
case 'c':
- type=PSTR("ColumnChart");
+ type = PSTR("ColumnChart");
+ break;
+ case 'C':
+ type = PSTR("ComboChart");
+ break;
+ case 'e':
+ WSContentSend_PD(SCRIPT_MSG_GTABLEbx, type, chartindex);
+ chartindex++;
+ goto nextwebline;
break;
default:
// error
@@ -6435,24 +6423,28 @@ exgc:
}
if (ctype=='l' && *lp=='f') {
lp++;
- func=PSTR(",curveType:'function'");
+ func = PSTR(",curveType:'function'");
} else {
- func="";
+ func = "";
}
if (*lp=='2') {
lp++;
- nanum=2;
- y2f=1;
+ nanum = 2;
+ y2f = 1;
+ }
+ if (*lp=='t') {
+ lp++;
+ tonly = 1;
}
SCRIPT_SKIP_SPACES
//Serial.printf("type %d\n",ctype);
float *arrays[MAX_GARRAY];
- uint8_t anum=0;
- uint16_t entries=0;
- uint16_t ipos=0;
- lp=gc_get_arrays(lp, &arrays[0], &anum, &entries, &ipos);
+ uint8_t anum = 0;
+ uint16_t entries = 0;
+ uint16_t ipos = 0;
+ lp = gc_get_arrays(lp, &arrays[0], &anum, &entries, &ipos);
if (anum>nanum) {
goto nextwebline;
@@ -6461,98 +6453,120 @@ exgc:
//Serial.printf("arrays %d\n",anum);
//Serial.printf("entries %d\n",entries);
if (ctype=='T') {
- if (anum && !(entries&1)) {
+ if (anum && !(entries & 1)) {
WSContentSend_PD(SCRIPT_MSG_GTABLEa);
WSContentSend_PD(SCRIPT_MSG_GTABLEd);
char label[SCRIPT_MAXSSIZE];
- lp=GetStringResult(lp,OPER_EQU,label,0);
+ lp = GetStringArgument(lp, OPER_EQU, label, 0);
SCRIPT_SKIP_SPACES
- for (uint32_t ind=0; ind=entries) todflg=entries-1;
+ int16_t divflg = 1;
+ int16_t todflg = -1;
+ if (!strncmp(label, "cnt", 3)) {
+ char *cp = &label[3];
+ //todflg=atoi(&label[3]);
+ todflg = strtol(cp, &cp, 10);
+ if (todflg>=entries) todflg = entries - 1;
+ if (*cp=='/') {
+ cp++;
+ divflg = strtol(cp, &cp, 10);
+ }
} else {
- uint16 segments=1;
- for (uint32_t cnt=0; cnt=entries) aind=entries-1;
- for (uint32_t cnt=0; cnt=entries) aind = entries - 1;
+ for (uint32_t cnt = 0; cnt < entries; cnt++) {
WSContentSend_PD("['");
char lbl[16];
if (todflg>=0) {
- sprintf(lbl,"%d",todflg);
+ sprintf(lbl, "%d", todflg / divflg);
todflg++;
- if (todflg>=entries) {
- todflg=0;
+ if (todflg >= entries) {
+ todflg = 0;
}
} else {
- GetTextIndexed(lbl, sizeof(lbl), aind/divflg, label);
+ if (todflg==-1) {
+ GetTextIndexed(lbl, sizeof(lbl), aind / divflg, label);
+ } else {
+ // day,hours,mins
+ GetTextIndexed(lbl, sizeof(lbl), aind / divflg, label + 4);
+ sprintf(lbl, "%s-%02d", lbl, aind % divflg);
+ }
}
WSContentSend_PD(lbl);
WSContentSend_PD("',");
- for (uint32_t ind=0; ind=entries) {
- aind=0;
+ aind = 0;
}
}
-
+ // table complete
+ if (tonly) {
+ WSContentSend_PD("]);");
+ goto nextwebline;
+ }
// get header
char header[SCRIPT_MAXSSIZE];
- lp=GetStringResult(lp,OPER_EQU,header,0);
+ lp = GetStringArgument(lp, OPER_EQU, header, 0);
SCRIPT_SKIP_SPACES
switch (ctype) {
case 't':
- snprintf_P(options,sizeof(options),SCRIPT_MSG_GOPT2);
+ snprintf_P(options, sizeof(options), SCRIPT_MSG_GOPT2);
break;
default:
- snprintf_P(options,sizeof(options),SCRIPT_MSG_GOPT1,header);
+ snprintf_P(options, sizeof(options), SCRIPT_MSG_GOPT1, header);
break;
}
// check for 2 axis option
@@ -6560,49 +6574,52 @@ exgc:
// 2 y axes variant
SCRIPT_SKIP_SPACES
float max1;
- lp=GetNumericResult(lp,OPER_EQU,&max1,0);
+ lp = GetNumericArgument(lp, OPER_EQU, &max1, 0);
SCRIPT_SKIP_SPACES
float max2;
- lp=GetNumericResult(lp,OPER_EQU,&max2,0);
+ lp = GetNumericArgument(lp, OPER_EQU, &max2, 0);
SCRIPT_SKIP_SPACES
- snprintf_P(options,sizeof(options),SCRIPT_MSG_GOPT3,header,(uint32_t)max1,(uint32_t)max2,func);
+ snprintf_P(options, sizeof(options), SCRIPT_MSG_GOPT3, header, (uint32_t)max1, (uint32_t)max2, func);
} else {
SCRIPT_SKIP_SPACES
- if (*lp!=')') {
- float max1;
- lp=GetNumericResult(lp,OPER_EQU,&max1,0);
- SCRIPT_SKIP_SPACES
- float max2;
- lp=GetNumericResult(lp,OPER_EQU,&max2,0);
- SCRIPT_SKIP_SPACES
- snprintf_P(options,sizeof(options),SCRIPT_MSG_GOPT6,header,(uint32_t)max1,(uint32_t)max2,func);
+ if (ctype!='g') {
+ if (*lp!=')') {
+ float max1;
+ lp = GetNumericArgument(lp, OPER_EQU, &max1, 0);
+ SCRIPT_SKIP_SPACES
+ float max2;
+ lp = GetNumericArgument(lp, OPER_EQU, &max2, 0);
+ SCRIPT_SKIP_SPACES
+ snprintf_P(options, sizeof(options), SCRIPT_MSG_GOPT6, header, (uint32_t)max1, (uint32_t)max2, func);
+ }
}
}
if (ctype=='g') {
float yellowFrom;
- lp=GetNumericResult(lp,OPER_EQU,&yellowFrom,0);
+ lp = GetNumericArgument(lp, OPER_EQU, &yellowFrom, 0);
SCRIPT_SKIP_SPACES
float redFrom;
- lp=GetNumericResult(lp,OPER_EQU,&redFrom,0);
+ lp = GetNumericArgument(lp, OPER_EQU, &redFrom, 0);
SCRIPT_SKIP_SPACES
float maxValue;
- lp=GetNumericResult(lp,OPER_EQU,&maxValue,0);
+ lp = GetNumericArgument(lp, OPER_EQU, &maxValue, 0);
SCRIPT_SKIP_SPACES
- float redTo=maxValue;
- float yellowTo=redFrom;
- snprintf_P(options,sizeof(options),SCRIPT_MSG_GAUGEOPT,(uint32_t)maxValue,(uint32_t)redFrom,(uint32_t)redTo,
- (uint32_t)yellowFrom,(uint32_t)yellowTo);
+ float redTo = maxValue;
+ float yellowTo = redFrom;
+ snprintf_P(options, sizeof(options), SCRIPT_MSG_GAUGEOPT, (uint32_t)maxValue, (uint32_t)redFrom, (uint32_t)redTo,
+ (uint32_t)yellowFrom, (uint32_t)yellowTo);
}
}
- WSContentSend_PD(SCRIPT_MSG_GTABLEb,options,type,chartindex);
+ WSContentSend_PD(SCRIPT_MSG_GTABLEb, options);
+ WSContentSend_PD(SCRIPT_MSG_GTABLEbx, type, chartindex);
chartindex++;
} else {
- WSContentSend_PD(PSTR("%s"),lin);
+ WSContentSend_PD(PSTR("%s"), lin);
}
#else
lin++;
- WSContentSend_PD(PSTR("%s"),lin);
+ WSContentSend_PD(PSTR("%s"), lin);
} else {
// WSContentSend_PD(PSTR("%s"),lin);
#endif //USE_GOOGLE_CHARTS
@@ -6626,10 +6643,10 @@ nextwebline:
#ifdef USE_SENDMAIL
void script_send_email_body(void(*func)(char *)) {
-uint8_t msect=Run_Scripter(">m",-2,0);
+uint8_t msect = Run_Scripter(">m", -2, 0);
if (msect==99) {
char tmp[256];
- char *lp=glob_script_mem.section_ptr+2;
+ char *lp = glob_script_mem.section_ptr + 2;
while (lp) {
while (*lp==SCRIPT_EOL) {
lp++;
@@ -6639,7 +6656,7 @@ uint8_t msect=Run_Scripter(">m",-2,0);
}
if (*lp!=';') {
// send this line to smtp
- Replace_Cmd_Vars(lp,1,tmp,sizeof(tmp));
+ Replace_Cmd_Vars(lp, 1, tmp, sizeof(tmp));
//client->println(tmp);
func(tmp);
}
@@ -6656,14 +6673,14 @@ uint8_t msect=Run_Scripter(">m",-2,0);
func((char*)"*");
}
}
-#endif
+#endif //USE_SENDMAIL
#ifdef USE_SCRIPT_JSON_EXPORT
void ScriptJsonAppend(void) {
- uint8_t web_script=Run_Scripter(">J",-2,0);
+ uint8_t web_script = Run_Scripter(">J", -2, 0);
if (web_script==99) {
char tmp[256];
- char *lp=glob_script_mem.section_ptr+2;
+ char *lp = glob_script_mem.section_ptr + 2;
while (lp) {
while (*lp==SCRIPT_EOL) {
lp++;
@@ -6673,8 +6690,8 @@ void ScriptJsonAppend(void) {
}
if (*lp!=';') {
// send this line to mqtt
- Replace_Cmd_Vars(lp,1,tmp,sizeof(tmp));
- ResponseAppend_P(PSTR("%s"),tmp);
+ Replace_Cmd_Vars(lp, 1, tmp, sizeof(tmp));
+ ResponseAppend_P(PSTR("%s"), tmp);
}
if (*lp==SCRIPT_EOL) {
lp++;
@@ -6690,7 +6707,7 @@ void ScriptJsonAppend(void) {
bool RulesProcessEvent(char *json_event) {
- if (bitRead(Settings.rule_enabled, 0)) Run_Scripter(">E",2,json_event);
+ if (bitRead(Settings.rule_enabled, 0)) Run_Scripter(">E", 2, json_event);
return true;
}
@@ -6703,7 +6720,7 @@ bool RulesProcessEvent(char *json_event) {
#ifndef STASK_PRIO
#define STASK_PRIO 1
-#endif
+#endif //ESP32
#if 1
@@ -6724,7 +6741,7 @@ void script_task1(void *arg) {
//if (time<=esp32_tasks[0].task_timer) {vTaskDelay( pdMS_TO_TICKS( time ) ); }
delay(esp32_tasks[0].task_timer);
if (bitRead(Settings.rule_enabled, 0)) {
- Run_Scripter(">t1",3,0);
+ Run_Scripter(">t1", 3, 0);
}
}
}
@@ -6740,7 +6757,7 @@ void script_task2(void *arg) {
//if (time<=esp32_tasks[1].task_timer) {vTaskDelay( pdMS_TO_TICKS( time ) ); }
delay(esp32_tasks[1].task_timer);
if (bitRead(Settings.rule_enabled, 0)) {
- Run_Scripter(">t2",3,0);
+ Run_Scripter(">t2", 3, 0);
}
}
}
@@ -6769,14 +6786,14 @@ TaskHandle_t task_t2;
void script_task1(void *arg) {
while (1) {
delay(task_timer1);
- Run_Scripter(">t1",3,0);
+ Run_Scripter(">t1", 3, 0);
}
}
void script_task2(void *arg) {
while (1) {
delay(task_timer2);
- Run_Scripter(">t2",3,0);
+ Run_Scripter(">t2", 3, 0);
}
}
@@ -6805,12 +6822,12 @@ uint32_t scripter_create_task(uint32_t num, uint32_t time, uint32_t core) {
#include "WiFiClientSecureLightBearSSL.h"
#else
#include
-#endif
+#endif //ESP8266
// get tesla powerwall info page json string
uint32_t call2https(const char *host, const char *path) {
if (global_state.wifi_down) return 1;
- uint32_t status=0;
+ uint32_t status = 0;
#ifdef ESP32
WiFiClientSecure *httpsClient;
httpsClient = new WiFiClientSecure;
@@ -6821,8 +6838,7 @@ uint32_t call2https(const char *host, const char *path) {
httpsClient->setTimeout(1500);
- int retry = 0;
- String result;
+ uint32_t retry = 0;
while ((!httpsClient->connect(host, 443)) && (retry < 5)) {
delay(100);
retry++;
@@ -6842,6 +6858,7 @@ uint32_t call2https(const char *host, const char *path) {
break;
}
}
+ String result;
while (httpsClient->available()) {
String line = httpsClient->readStringUntil('\n');
if (line!="") {
@@ -6850,20 +6867,19 @@ uint32_t call2https(const char *host, const char *path) {
}
httpsClient->stop();
delete httpsClient;
- Run_Scripter(">jp",3,(char*)result.c_str());
+ Run_Scripter(">jp", 3, (char*)result.c_str());
return 0;
}
-
#endif // SCRIPT_GET_HTTPS_JP
-void cpy2lf(char *dst,uint32_t dstlen, char *src) {
+void cpy2lf(char *dst, uint32_t dstlen, char *src) {
for (uint32_t cnt=0; cnt0) glob_script_mem.script_ram[len_decompressed]=0;
+ if (len_decompressed>0) glob_script_mem.script_ram[len_decompressed] = 0;
// indicates scripter use compression
bitWrite(Settings.rule_once, 6, 1);
//AddLog_P2(LOG_LEVEL_INFO, PSTR("decompressed script len %d"),len_decompressed);
@@ -6908,29 +6924,29 @@ bool Xdrv10(uint8_t function)
#endif // USE_SCRIPT_COMPRESSION
#ifdef USE_BUTTON_EVENT
- for (uint32_t cnt=0;cnt=0
AddLog_P(LOG_LEVEL_INFO,PSTR("FATFS mount OK!"));
//fsp->dateTimeCallback(dateTime);
- glob_script_mem.script_sd_found=1;
+ glob_script_mem.script_sd_found = 1;
char *script;
- script=(char*)calloc(FAT_SCRIPT_SIZE+4,1);
+ script = (char*)calloc(FAT_SCRIPT_SIZE + 4, 1);
if (!script) break;
- glob_script_mem.script_ram=script;
- glob_script_mem.script_size=FAT_SCRIPT_SIZE;
+ glob_script_mem.script_ram = script;
+ glob_script_mem.script_size = FAT_SCRIPT_SIZE;
if (fsp->exists(FAT_SCRIPT_NAME)) {
- File file=fsp->open(FAT_SCRIPT_NAME,FILE_READ);
- file.read((uint8_t*)script,FAT_SCRIPT_SIZE);
+ File file = fsp->open(FAT_SCRIPT_NAME, FILE_READ);
+ file.read((uint8_t*)script, FAT_SCRIPT_SIZE);
file.close();
}
- script[FAT_SCRIPT_SIZE-1]=0;
+ script[FAT_SCRIPT_SIZE - 1] = 0;
// use rules storage for permanent vars
- glob_script_mem.script_pram=(uint8_t*)Settings.rules[0];
- glob_script_mem.script_pram_size=MAX_SCRIPT_SIZE;
+ glob_script_mem.script_pram = (uint8_t*)Settings.rules[0];
+ glob_script_mem.script_pram_size = MAX_SCRIPT_SIZE;
- glob_script_mem.flags=1;
+ glob_script_mem.flags = 1;
} else {
AddLog_P(LOG_LEVEL_INFO,PSTR("FATFS mount failed!"));
- glob_script_mem.script_sd_found=0;
+ glob_script_mem.script_sd_found = 0;
}
#endif // USE_SCRIPT_FATFS
@@ -6997,46 +7013,46 @@ bool Xdrv10(uint8_t function)
#else
// lfs on esp8266
fsp = &LittleFS;
-#endif
+#endif //ESP32
char *script;
- script=(char*)calloc(LITTLEFS_SCRIPT_SIZE+4,1);
+ script = (char*)calloc(LITTLEFS_SCRIPT_SIZE + 4, 1);
if (!script) break;
- LoadFile("/script.txt",(uint8_t*)script,LITTLEFS_SCRIPT_SIZE);
+ LoadFile("/script.txt", (uint8_t*)script, LITTLEFS_SCRIPT_SIZE);
- glob_script_mem.script_ram=script;
- glob_script_mem.script_size=LITTLEFS_SCRIPT_SIZE;
- script[LITTLEFS_SCRIPT_SIZE-1]=0;
+ glob_script_mem.script_ram = script;
+ glob_script_mem.script_size = LITTLEFS_SCRIPT_SIZE;
+ script[LITTLEFS_SCRIPT_SIZE-1] = 0;
// use rules storage for permanent vars
- glob_script_mem.script_pram=(uint8_t*)Settings.rules[0];
- glob_script_mem.script_pram_size=MAX_SCRIPT_SIZE;
- glob_script_mem.flags=1;
+ glob_script_mem.script_pram = (uint8_t*)Settings.rules[0];
+ glob_script_mem.script_pram_size = MAX_SCRIPT_SIZE;
+ glob_script_mem.flags = 1;
#endif // LITTLEFS_SCRIPT_SIZE
// a valid script MUST start with >D
if (glob_script_mem.script_ram[0]!='>' && glob_script_mem.script_ram[1]!='D') {
// clr all
- memset(glob_script_mem.script_ram,0,glob_script_mem.script_size);
+ memset(glob_script_mem.script_ram, 0 ,glob_script_mem.script_size);
strcpy_P(glob_script_mem.script_ram, PSTR(">D\nscript error must start with >D"));
bitWrite(Settings.rule_enabled, 0, 0);
}
// assure permanent memory is 4 byte aligned
- { uint32_t ptr=(uint32_t)glob_script_mem.script_pram;
- ptr&=0xfffffffc;
- ptr+=4;
- glob_script_mem.script_pram=(uint8_t*)ptr;
- glob_script_mem.script_pram_size-=4;
+ { uint32_t ptr = (uint32_t)glob_script_mem.script_pram;
+ ptr &= 0xfffffffc;
+ ptr += 4;
+ glob_script_mem.script_pram = (uint8_t*)ptr;
+ glob_script_mem.script_pram_size -= 4;
}
if (bitRead(Settings.rule_enabled, 0)) Init_Scripter();
break;
case FUNC_INIT:
if (bitRead(Settings.rule_enabled, 0)) {
- Run_Scripter(">B\n",3,0);
- fast_script=Run_Scripter(">F",-2,0);
+ Run_Scripter(">B\n", 3, 0);
+ fast_script = Run_Scripter(">F", -2, 0);
#if defined(USE_SCRIPT_HUE) && defined(USE_WEBSERVER) && defined(USE_EMULATION) && defined(USE_EMULATION_HUE) && defined(USE_LIGHT)
Script_Check_Hue(0);
-#endif
+#endif //USE_SCRIPT_HUE
}
break;
case FUNC_EVERY_100_MSECOND:
@@ -7050,18 +7066,18 @@ bool Xdrv10(uint8_t function)
break;
case FUNC_SET_POWER:
#ifdef SCRIPT_POWER_SECTION
- if (bitRead(Settings.rule_enabled, 0)) Run_Scripter(">P",2,0);
+ if (bitRead(Settings.rule_enabled, 0)) Run_Scripter(">P", 2, 0);
#else
if (bitRead(Settings.rule_enabled, 0)) {
- Run_Scripter(">E",2,0);
- result=event_handeled;
+ Run_Scripter(">E", 2, 0);
+ result = event_handeled;
}
-#endif
+#endif //SCRIPT_POWER_SECTION
break;
case FUNC_RULES_PROCESS:
if (bitRead(Settings.rule_enabled, 0)) {
- Run_Scripter(">E",2,mqtt_data);
- result=event_handeled;
+ Run_Scripter(">E", 2, mqtt_data);
+ result = event_handeled;
}
break;
#ifdef USE_WEBSERVER
@@ -7073,11 +7089,11 @@ bool Xdrv10(uint8_t function)
if (bitRead(Settings.rule_enabled, 0)) {
ScriptWebShow('$');
#ifdef SCRIPT_FULL_WEBPAGE
- uint8_t web_script=Run_Scripter(">w",-2,0);
+ uint8_t web_script = Run_Scripter(">w", -2, 0);
if (web_script==99) {
char bname[48];
- cpy2lf(bname,sizeof(bname),glob_script_mem.section_ptr+3);
- WSContentSend_PD(HTTP_WEB_FULL_DISPLAY,bname);
+ cpy2lf(bname, sizeof(bname), glob_script_mem.section_ptr + 3);
+ WSContentSend_PD(HTTP_WEB_FULL_DISPLAY, bname);
Webserver->on("/sfd", ScriptFullWebpage);
#ifdef USE_SCRIPT_FATFS
Webserver->onNotFound(ScriptGetSDCard);
@@ -7090,24 +7106,24 @@ bool Xdrv10(uint8_t function)
case FUNC_WEB_ADD_HANDLER:
Webserver->on("/" WEB_HANDLE_SCRIPT, HandleScriptConfiguration);
Webserver->on("/ta",HTTP_POST, HandleScriptTextareaConfiguration);
- Webserver->on("/exs", HTTP_POST,[]() { Webserver->sendHeader("Location","/exs");Webserver->send(303);},script_upload_start);
- Webserver->on("/exs", HTTP_GET,ScriptExecuteUploadSuccess);
+ Webserver->on("/exs", HTTP_POST,[]() { Webserver->sendHeader("Location","/exs");Webserver->send(303);}, script_upload_start);
+ Webserver->on("/exs", HTTP_GET, ScriptExecuteUploadSuccess);
#ifdef USE_SCRIPT_FATFS
- Webserver->on("/u3", HTTP_POST,[]() { Webserver->sendHeader("Location","/u3");Webserver->send(303);},script_upload);
- Webserver->on("/u3", HTTP_GET,ScriptFileUploadSuccess);
- Webserver->on("/upl", HTTP_GET,Script_FileUploadConfiguration);
-#endif
+ Webserver->on("/u3", HTTP_POST,[]() { Webserver->sendHeader("Location","/u3");Webserver->send(303);}, script_upload);
+ Webserver->on("/u3", HTTP_GET, ScriptFileUploadSuccess);
+ Webserver->on("/upl", HTTP_GET, Script_FileUploadConfiguration);
+#endif //USE_SCRIPT_FATFS
break;
#endif // USE_WEBSERVER
case FUNC_SAVE_BEFORE_RESTART:
if (bitRead(Settings.rule_enabled, 0)) {
- Run_Scripter(">R",2,0);
+ Run_Scripter(">R", 2, 0);
Scripter_save_pvars();
}
#ifdef USE_SCRIPT_GLOBVARS
Script_Stop_UDP();
-#endif
+#endif //USE_SCRIPT_GLOBVARS
break;
#ifdef SUPPORT_MQTT_EVENT
case FUNC_MQTT_DATA:
@@ -7136,18 +7152,18 @@ bool Xdrv10(uint8_t function)
case FUNC_BUTTON_PRESSED:
if (bitRead(Settings.rule_enabled, 0)) {
if ((script_button[XdrvMailbox.index]&1)!=(XdrvMailbox.payload&1)) {
- script_button[XdrvMailbox.index]=XdrvMailbox.payload;
- Run_Scripter(">b",2,0);
+ script_button[XdrvMailbox.index] = XdrvMailbox.payload;
+ Run_Scripter(">b", 2, 0);
}
}
break;
-#endif
+#endif //USE_BUTTON_EVENT
#ifdef USE_SCRIPT_GLOBVARS
case FUNC_LOOP:
Script_PollUdp();
break;
-#endif
+#endif //USE_SCRIPT_GLOBVARS
}
return result;
From 01ab99f8a1108f06144349f25a98f8babc013ab3 Mon Sep 17 00:00:00 2001
From: gemu2015
Date: Fri, 11 Sep 2020 15:45:21 +0200
Subject: [PATCH 034/148] display fix touch init, some formatting
---
tasmota/xdrv_13_display.ino | 28 ++++++++++++++--------------
1 file changed, 14 insertions(+), 14 deletions(-)
diff --git a/tasmota/xdrv_13_display.ino b/tasmota/xdrv_13_display.ino
index 3a87826f3..8a0c5d395 100644
--- a/tasmota/xdrv_13_display.ino
+++ b/tasmota/xdrv_13_display.ino
@@ -506,7 +506,7 @@ void DisplayText(void)
}
}
break;
-#endif
+#endif // USE_SCRIPT_FATFS
case 'h':
// hor line to
var = atoiv(cp, &temp);
@@ -696,7 +696,7 @@ void DisplayText(void)
Restore_graph(temp,bbuff);
break;
}
-#endif
+#endif // USE_SCRIPT_FATFS
{ int16_t num,gxp,gyp,gxs,gys,dec,icol;
float ymin,ymax;
var=atoiv(cp,&num);
@@ -744,7 +744,7 @@ void DisplayText(void)
AddValue(num,temp);
}
break;
-#endif
+#endif // USE_GRAPH
#ifdef USE_AWATCH
case 'w':
@@ -752,7 +752,7 @@ void DisplayText(void)
cp += var;
DrawAClock(temp);
break;
-#endif
+#endif // USE_AWATCH
#ifdef USE_TOUCH_BUTTONS
case 'b':
@@ -834,12 +834,13 @@ void DisplayText(void)
buttons[num]->vpower.is_pushbutton=0;
}
if (dflg) buttons[num]->xdrawButton(buttons[num]->vpower.on_off);
+ buttons[num]->vpower.disable=!dflg;
}
}
}
}
break;
-#endif
+#endif // USE_TOUCH_BUTTONS
default:
// unknown escape
Response_P(PSTR("Unknown Escape"));
@@ -1530,8 +1531,8 @@ void CmndDisplayRows(void)
bool jpg2rgb888(const uint8_t *src, size_t src_len, uint8_t * out, jpg_scale_t scale);
char get_jpeg_size(unsigned char* data, unsigned int data_size, unsigned short *width, unsigned short *height);
void rgb888_to_565(uint8_t *in, uint16_t *out, uint32_t len);
-#endif
-#endif
+#endif // JPEG_PICTS
+#endif // ESP32
#if defined(USE_SCRIPT_FATFS) && defined(USE_SCRIPT)
extern FS *fsp;
@@ -1626,7 +1627,7 @@ void Draw_RGB_Bitmap(char *file,uint16_t xp, uint16_t yp) {
#endif // ESP32
}
}
-#endif
+#endif // USE_SCRIPT_FATFS
#ifdef USE_AWATCH
#define MINUTE_REDUCT 4
@@ -1663,7 +1664,7 @@ void DrawAClock(uint16_t rad) {
temp=((float)RtcTime.minute*(pi/30.0)-(pi/2.0));
renderer->writeLine(disp_xpos, disp_ypos,disp_xpos+(frad-MINUTE_REDUCT)*cosf(temp),disp_ypos+(frad-MINUTE_REDUCT)*sinf(temp), fg_color);
}
-#endif
+#endif // USE_AWATCH
#ifdef USE_GRAPH
@@ -1938,7 +1939,7 @@ void Restore_graph(uint8_t num, char *path) {
fp.close();
RedrawGraph(num,1);
}
-#endif
+#endif // USE_SCRIPT_FATFS
void RedrawGraph(uint8_t num, uint8_t flags) {
uint16_t index=num%NUM_GRAPHS;
@@ -2050,16 +2051,13 @@ void AddValue(uint8_t num,float fval) {
#ifdef USE_FT5206
+#include
// touch panel controller
#undef FT5206_address
#define FT5206_address 0x38
-#include
FT5206_Class *touchp;
TP_Point pLoc;
-
-
-extern VButton *buttons[];
bool FT5206_found;
bool Touch_Init(TwoWire &i2c) {
@@ -2088,6 +2086,7 @@ uint32_t Touch_Status(uint32_t sel) {
}
}
+
#ifdef USE_TOUCH_BUTTONS
void Touch_MQTT(uint8_t index, const char *cp) {
ResponseTime_P(PSTR(",\"FT5206\":{\"%s%d\":\"%d\"}}"), cp, index+1, buttons[index]->vpower.on_off);
@@ -2184,6 +2183,7 @@ uint8_t vbutt=0;
pLoc.y = 0;
}
}
+
#endif // USE_TOUCH_BUTTONS
#endif // USE_FT5206
From 8468e093fc74b4c6f304a3af43f95d3547f749bb Mon Sep 17 00:00:00 2001
From: gemu2015
Date: Fri, 11 Sep 2020 15:45:59 +0200
Subject: [PATCH 035/148] i2s audio update
save heap memory on esp32 with psram
---
tasmota/xdrv_42_i2s_audio.ino | 18 +++++++++++++++---
1 file changed, 15 insertions(+), 3 deletions(-)
diff --git a/tasmota/xdrv_42_i2s_audio.ino b/tasmota/xdrv_42_i2s_audio.ino
index 77492992b..5a31d832b 100644
--- a/tasmota/xdrv_42_i2s_audio.ino
+++ b/tasmota/xdrv_42_i2s_audio.ino
@@ -52,7 +52,7 @@ AudioFileSourceFS *file;
AudioOutputI2S *out;
AudioFileSourceID3 *id3;
AudioGeneratorMP3 *decoder = NULL;
-
+void *mp3ram = NULL;
#ifdef USE_WEBRADIO
AudioFileSourceICYStream *ifile = NULL;
@@ -210,6 +210,12 @@ void I2S_Init(void) {
is2_volume=10;
out->SetGain(((float)is2_volume/100.0)*4.0);
out->stop();
+ mp3ram = nullptr;
+
+#ifdef ESP32
+ if (psramFound()) {
+ mp3ram = heap_caps_malloc(preallocateCodecSize, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
+ }
#ifdef USE_WEBRADIO
if (psramFound()) {
@@ -223,6 +229,7 @@ void I2S_Init(void) {
//Serial.printf_P(PSTR("FATAL ERROR: Unable to preallocate %d bytes for app\n"), preallocateBufferSize+preallocateCodecSize);
}
#endif // USE_WEBRADIO
+#endif // ESP32
}
#ifdef ESP32
@@ -285,7 +292,7 @@ void Webradio(const char *url) {
retryms = millis() + 2000;
}
- xTaskCreatePinnedToCore(mp3_task2, "MP3", 8192, NULL, 3, &mp3_task_h, 1);
+ xTaskCreatePinnedToCore(mp3_task2, "MP3-2", 8192, NULL, 3, &mp3_task_h, 1);
}
void mp3_task2(void *arg){
@@ -366,7 +373,12 @@ void Play_mp3(const char *path) {
file = new AudioFileSourceFS(*fsp,path);
id3 = new AudioFileSourceID3(file);
- mp3 = new AudioGeneratorMP3();
+
+ if (mp3ram) {
+ mp3 = new AudioGeneratorMP3(mp3ram, preallocateCodecSize);
+ } else {
+ mp3 = new AudioGeneratorMP3();
+ }
mp3->begin(id3, out);
if (I2S_Task) {
From ecc27aa3835cfe8b0fb78027762a743771b92212 Mon Sep 17 00:00:00 2001
From: Stephan Hadinger
Date: Sat, 12 Sep 2020 10:57:54 +0200
Subject: [PATCH 036/148] Add Zigbee auto-config when pairing
---
tasmota/CHANGELOG.md | 1 +
tasmota/settings.h | 2 +-
tasmota/xdrv_23_zigbee_2_devices.ino | 57 +++-
tasmota/xdrv_23_zigbee_5_converters.ino | 66 +++--
tasmota/xdrv_23_zigbee_6_commands.ino | 10 +-
tasmota/xdrv_23_zigbee_8_parsers.ino | 337 +++++++++++++++++++++++-
6 files changed, 430 insertions(+), 43 deletions(-)
diff --git a/tasmota/CHANGELOG.md b/tasmota/CHANGELOG.md
index faaa2ae17..d07791a2e 100644
--- a/tasmota/CHANGELOG.md
+++ b/tasmota/CHANGELOG.md
@@ -8,6 +8,7 @@
- Fix crash in ``ZbRestore``
- Add new shutter modes (#9244)
- Add ``#define USE_MQTT_AWS_IOT_LIGHT`` for password based AWS IoT authentication
+- Add Zigbee auto-config when pairing
### 8.5.0 20200907
diff --git a/tasmota/settings.h b/tasmota/settings.h
index 2ceadd523..a9c8680c1 100644
--- a/tasmota/settings.h
+++ b/tasmota/settings.h
@@ -129,7 +129,7 @@ typedef union { // Restricted by MISRA-C Rule 18.4 bu
uint32_t virtual_ct_cw : 1; // bit 25 (v8.4.0.1) - SetOption107 - Virtual CT Channel - signals whether the hardware white is cold CW (true) or warm WW (false)
uint32_t teleinfo_rawdata : 1; // bit 26 (v8.4.0.2) - SetOption108 - enable Teleinfo + Tasmota Energy device (0) or Teleinfo raw data only (1)
uint32_t alexa_gen_1 : 1; // bit 27 (v8.4.0.3) - SetOption109 - Alexa gen1 mode - if you only have Echo Dot 2nd gen devices
- uint32_t spare28 : 1; // bit 28
+ uint32_t zb_disable_autobind : 1; // bit 28 (v8.5.0.1) - SetOption110 - disable Zigbee auto-config when pairing new devices
uint32_t spare29 : 1; // bit 29
uint32_t spare30 : 1; // bit 30
uint32_t spare31 : 1; // bit 31
diff --git a/tasmota/xdrv_23_zigbee_2_devices.ino b/tasmota/xdrv_23_zigbee_2_devices.ino
index 8ad36f861..1e2c29704 100644
--- a/tasmota/xdrv_23_zigbee_2_devices.ino
+++ b/tasmota/xdrv_23_zigbee_2_devices.ino
@@ -36,6 +36,10 @@ public:
char * manufacturerId;
char * modelId;
char * friendlyName;
+ // _defer_last_time : what was the last time an outgoing message is scheduled
+ // this is designed for flow control and avoid messages to be lost or unanswered
+ uint32_t defer_last_message_sent;
+
uint8_t endpoints[endpoints_max]; // static array to limit memory consumption, list of endpoints until 0x00 or end of array
// Used for attribute reporting
Z_attribute_list attr_list;
@@ -78,6 +82,7 @@ public:
manufacturerId(nullptr),
modelId(nullptr),
friendlyName(nullptr),
+ defer_last_message_sent(0),
endpoints{ 0, 0, 0, 0, 0, 0, 0, 0 },
attr_list(),
shortaddr(_shortaddr),
@@ -145,21 +150,28 @@ public:
* Structures for deferred callbacks
\*********************************************************************************************/
-typedef int32_t (*Z_DeviceTimer)(uint16_t shortaddr, uint16_t groupaddr, uint16_t cluster, uint8_t endpoint, uint32_t value);
+typedef void (*Z_DeviceTimer)(uint16_t shortaddr, uint16_t groupaddr, uint16_t cluster, uint8_t endpoint, uint32_t value);
// Category for Deferred actions, this allows to selectively remove active deferred or update them
typedef enum Z_Def_Category {
- Z_CAT_NONE = 0, // no category, it will happen anyways
+ Z_CAT_ALWAYS = 0, // no category, it will happen whatever new timers
+ // Below will clear any event in the same category for the same address (shortaddr / groupaddr)
+ Z_CLEAR_DEVICE = 0x01,
Z_CAT_READ_ATTR, // Attribute reporting, either READ_ATTRIBUTE or REPORT_ATTRIBUTE, we coalesce all attributes reported if we can
Z_CAT_VIRTUAL_OCCUPANCY, // Creation of a virtual attribute, typically after a time-out. Ex: Aqara presence sensor
Z_CAT_REACHABILITY, // timer set to measure reachability of device, i.e. if we don't get an answer after 1s, it is marked as unreachable (for Alexa)
- Z_CAT_READ_0006, // Read 0x0006 cluster
- Z_CAT_READ_0008, // Read 0x0008 cluster
- Z_CAT_READ_0102, // Read 0x0300 cluster
- Z_CAT_READ_0300, // Read 0x0300 cluster
+ // Below will clear based on device + cluster pair.
+ Z_CLEAR_DEVICE_CLUSTER,
+ Z_CAT_READ_CLUSTER,
+ // Below will clear based on device + cluster + endpoint
+ Z_CLEAR_DEVICE_CLUSTER_ENDPOINT,
+ Z_CAT_EP_DESC, // read endpoint descriptor to gather clusters
+ Z_CAT_BIND, // send auto-binding to coordinator
+ Z_CAT_CONFIG_ATTR, // send a config attribute reporting request
+ Z_CAT_READ_ATTRIBUTE, // read a single attribute
} Z_Def_Category;
-const uint32_t Z_CAT_REACHABILITY_TIMEOUT = 1000; // 1000 ms or 1s
+const uint32_t Z_CAT_REACHABILITY_TIMEOUT = 2000; // 1000 ms or 1s
typedef struct Z_Deferred {
// below are per device timers, used for example to query the new state of the device
@@ -258,8 +270,9 @@ public:
bool isHueBulbHidden(uint16_t shortaddr) const ;
// Timers
- void resetTimersForDevice(uint16_t shortaddr, uint16_t groupaddr, uint8_t category);
+ void resetTimersForDevice(uint16_t shortaddr, uint16_t groupaddr, uint8_t category, uint16_t cluster = 0xFFFF, uint8_t endpoint = 0xFF);
void setTimer(uint16_t shortaddr, uint16_t groupaddr, uint32_t wait_ms, uint16_t cluster, uint8_t endpoint, uint8_t category, uint32_t value, Z_DeviceTimer func);
+ void queueTimer(uint16_t shortaddr, uint16_t groupaddr, uint32_t wait_ms, uint16_t cluster, uint8_t endpoint, uint8_t category, uint32_t value, Z_DeviceTimer func);
void runTimer(void);
// Append or clear attributes Json structure
@@ -723,12 +736,17 @@ bool Z_Devices::isHueBulbHidden(uint16_t shortaddr) const {
// Deferred actions
// Parse for a specific category, of all deferred for a device if category == 0xFF
-void Z_Devices::resetTimersForDevice(uint16_t shortaddr, uint16_t groupaddr, uint8_t category) {
+// Only with specific cluster number or for all clusters if cluster == 0xFFFF
+void Z_Devices::resetTimersForDevice(uint16_t shortaddr, uint16_t groupaddr, uint8_t category, uint16_t cluster, uint8_t endpoint) {
// iterate the list of deferred, and remove any linked to the shortaddr
for (auto & defer : _deferred) {
if ((defer.shortaddr == shortaddr) && (defer.groupaddr == groupaddr)) {
if ((0xFF == category) || (defer.category == category)) {
- _deferred.remove(&defer);
+ if ((0xFFFF == cluster) || (defer.cluster == cluster)) {
+ if ((0xFF == endpoint) || (defer.endpoint == endpoint)) {
+ _deferred.remove(&defer);
+ }
+ }
}
}
}
@@ -737,8 +755,8 @@ void Z_Devices::resetTimersForDevice(uint16_t shortaddr, uint16_t groupaddr, uin
// Set timer for a specific device
void Z_Devices::setTimer(uint16_t shortaddr, uint16_t groupaddr, uint32_t wait_ms, uint16_t cluster, uint8_t endpoint, uint8_t category, uint32_t value, Z_DeviceTimer func) {
// First we remove any existing timer for same device in same category, except for category=0x00 (they need to happen anyway)
- if (category) { // if category == 0, we leave all previous
- resetTimersForDevice(shortaddr, groupaddr, category); // remove any cluster
+ if (category >= Z_CLEAR_DEVICE) { // if category == 0, we leave all previous timers
+ resetTimersForDevice(shortaddr, groupaddr, category, category >= Z_CLEAR_DEVICE_CLUSTER ? cluster : 0xFFFF, category >= Z_CLEAR_DEVICE_CLUSTER_ENDPOINT ? endpoint : 0xFF); // remove any cluster
}
// Now create the new timer
@@ -753,6 +771,21 @@ void Z_Devices::setTimer(uint16_t shortaddr, uint16_t groupaddr, uint32_t wait_m
func };
}
+// Set timer after the already queued events
+// I.e. the wait_ms is not counted from now, but from the last event queued, which is 'now' or in the future
+void Z_Devices::queueTimer(uint16_t shortaddr, uint16_t groupaddr, uint32_t wait_ms, uint16_t cluster, uint8_t endpoint, uint8_t category, uint32_t value, Z_DeviceTimer func) {
+ Z_Device & device = getShortAddr(shortaddr);
+ uint32_t now_millis = millis();
+ if (TimeReached(device.defer_last_message_sent)) {
+ device.defer_last_message_sent = now_millis;
+ }
+ // defer_last_message_sent equals now or a value in the future
+ device.defer_last_message_sent += wait_ms;
+
+ // for queueing we don't clear the backlog, so we force category to Z_CAT_ALWAYS
+ setTimer(shortaddr, groupaddr, (device.defer_last_message_sent - now_millis), cluster, endpoint, Z_CAT_ALWAYS, value, func);
+}
+
// Run timer at each tick
// WARNING: don't set a new timer within a running timer, this causes memory corruption
void Z_Devices::runTimer(void) {
diff --git a/tasmota/xdrv_23_zigbee_5_converters.ino b/tasmota/xdrv_23_zigbee_5_converters.ino
index 1155a6f2e..e70e6702d 100644
--- a/tasmota/xdrv_23_zigbee_5_converters.ino
+++ b/tasmota/xdrv_23_zigbee_5_converters.ino
@@ -124,6 +124,16 @@ uint16_t CxToCluster(uint8_t cx) {
}
return 0xFFFF;
}
+
+uint8_t ClusterToCx(uint16_t cluster) {
+ for (uint8_t i=0; icluster_short));
+ uint16_t conv_attr_id = pgm_read_word(&converter->attribute);
+
+ if ((conv_cluster == cluster) && (conv_attr_id == attr_id)) {
+ if (multiplier) { *multiplier = pgm_read_byte(&converter->multiplier); }
+ if (attr_type) { *attr_type = pgm_read_byte(&converter->type); }
+ return (const __FlashStringHelper*) (Z_strings + pgm_read_word(&converter->name_offset));
+ }
+ }
+ return nullptr;
+}
+
class ZCLFrame {
public:
@@ -1095,35 +1124,30 @@ void ZCLFrame::generateCallBacks(Z_attribute_list& attr_list) {
// Set timers to read back values.
// If it's a device address, also set a timer for reachability test
void sendHueUpdate(uint16_t shortaddr, uint16_t groupaddr, uint16_t cluster, uint8_t endpoint = 0) {
- int32_t z_cat = -1;
- uint32_t wait_ms = 0;
+ uint32_t wait_ms = 0xFFFF;
switch (cluster) {
case 0x0006:
- z_cat = Z_CAT_READ_0006;
wait_ms = 200; // wait 0.2 s
break;
case 0x0008:
- z_cat = Z_CAT_READ_0008;
wait_ms = 1050; // wait 1.0 s
break;
case 0x0102:
- z_cat = Z_CAT_READ_0102;
wait_ms = 10000; // wait 10.0 s
break;
case 0x0300:
- z_cat = Z_CAT_READ_0300;
wait_ms = 1050; // wait 1.0 s
break;
default:
break;
}
- if (z_cat >= 0) {
+ if (0xFFFF != wait_ms) {
if ((BAD_SHORTADDR != shortaddr) && (0 == endpoint)) {
endpoint = zigbee_devices.findFirstEndpoint(shortaddr);
}
if ((BAD_SHORTADDR == shortaddr) || (endpoint)) { // send if group address or endpoint is known
- zigbee_devices.setTimer(shortaddr, groupaddr, wait_ms, cluster, endpoint, z_cat, 0 /* value */, &Z_ReadAttrCallback);
+ zigbee_devices.queueTimer(shortaddr, groupaddr, wait_ms, cluster, endpoint, Z_CAT_READ_CLUSTER, 0 /* value */, &Z_ReadAttrCallback);
if (BAD_SHORTADDR != shortaddr) { // reachability test is not possible for group addresses, since we don't know the list of devices in the group
zigbee_devices.setTimer(shortaddr, groupaddr, wait_ms + Z_CAT_REACHABILITY_TIMEOUT, cluster, endpoint, Z_CAT_REACHABILITY, 0 /* value */, &Z_Unreachable);
}
@@ -1170,16 +1194,27 @@ void ZCLFrame::parseReadAttributes(Z_attribute_list& attr_list) {
// ZCL_CONFIGURE_REPORTING_RESPONSE
void ZCLFrame::parseConfigAttributes(Z_attribute_list& attr_list) {
- uint32_t i = 0;
uint32_t len = _payload.len();
- uint8_t status = _payload.get8(i);
- Z_attribute_list attr_config_response;
- attr_config_response.addAttribute(F("Status")).setUInt(status);
- attr_config_response.addAttribute(F("StatusMsg")).setStr(getZigbeeStatusMessage(status).c_str());
+ Z_attribute_list attr_config_list;
+ for (uint32_t i=0; len >= i+4; i+=4) {
+ uint8_t status = _payload.get8(i);
+ uint16_t attr_id = _payload.get8(i+2);
+
+ Z_attribute_list attr_config_response;
+ attr_config_response.addAttribute(F("Status")).setUInt(status);
+ attr_config_response.addAttribute(F("StatusMsg")).setStr(getZigbeeStatusMessage(status).c_str());
+
+ const __FlashStringHelper* attr_name = zigbeeFindAttributeById(_cluster_id, attr_id, nullptr, nullptr);
+ if (attr_name) {
+ attr_config_list.addAttribute(attr_name).setStrRaw(attr_config_response.toString(true).c_str());
+ } else {
+ attr_config_list.addAttribute(_cluster_id, attr_id).setStrRaw(attr_config_response.toString(true).c_str());
+ }
+ }
Z_attribute &attr_1 = attr_list.addAttribute(F("ConfigResponse"));
- attr_1.setStrRaw(attr_config_response.toString(true).c_str());
+ attr_1.setStrRaw(attr_config_list.toString(true).c_str());
}
// ZCL_READ_REPORTING_CONFIGURATION_RESPONSE
@@ -1534,11 +1569,10 @@ void ZCLFrame::syntheticAqaraVibration(class Z_attribute_list &attr_list, class
}
/// Publish a message for `"Occupancy":0` when the timer expired
-int32_t Z_OccupancyCallback(uint16_t shortaddr, uint16_t groupaddr, uint16_t cluster, uint8_t endpoint, uint32_t value) {
+void Z_OccupancyCallback(uint16_t shortaddr, uint16_t groupaddr, uint16_t cluster, uint8_t endpoint, uint32_t value) {
Z_attribute_list attr_list;
attr_list.addAttribute(F(OCCUPANCY)).setUInt(0);
zigbee_devices.jsonPublishNow(shortaddr, attr_list);
- return 0; // Fix GCC 10.1 warning
}
// ======================================================================
diff --git a/tasmota/xdrv_23_zigbee_6_commands.ino b/tasmota/xdrv_23_zigbee_6_commands.ino
index a32f637c4..947e2ffe0 100644
--- a/tasmota/xdrv_23_zigbee_6_commands.ino
+++ b/tasmota/xdrv_23_zigbee_6_commands.ino
@@ -148,7 +148,7 @@ const uint8_t CLUSTER_0009[] = { ZLE(0x0000) }; // AlarmCount
const uint8_t CLUSTER_0300[] = { ZLE(0x0000), ZLE(0x0001), ZLE(0x0003), ZLE(0x0004), ZLE(0x0007), ZLE(0x0008) }; // Hue, Sat, X, Y, CT, ColorMode
// This callback is registered after a cluster specific command and sends a read command for the same cluster
-int32_t Z_ReadAttrCallback(uint16_t shortaddr, uint16_t groupaddr, uint16_t cluster, uint8_t endpoint, uint32_t value) {
+void Z_ReadAttrCallback(uint16_t shortaddr, uint16_t groupaddr, uint16_t cluster, uint8_t endpoint, uint32_t value) {
size_t attrs_len = 0;
const uint8_t* attrs = nullptr;
@@ -188,16 +188,14 @@ int32_t Z_ReadAttrCallback(uint16_t shortaddr, uint16_t groupaddr, uint16_t clus
attrs, attrs_len
}));
}
- return 0; // Fix GCC 10.1 warning
}
// This callback is registered after a an attribute read command was made to a light, and fires if we don't get any response after 1000 ms
-int32_t Z_Unreachable(uint16_t shortaddr, uint16_t groupaddr, uint16_t cluster, uint8_t endpoint, uint32_t value) {
+void Z_Unreachable(uint16_t shortaddr, uint16_t groupaddr, uint16_t cluster, uint8_t endpoint, uint32_t value) {
if (BAD_SHORTADDR != shortaddr) {
zigbee_devices.setReachable(shortaddr, false); // mark device as reachable
}
- return 0; // Fix GCC 10.1 warning
}
// returns true if char is 'x', 'y' or 'z'
@@ -349,6 +347,10 @@ void convertClusterSpecific(class Z_attribute_list &attr_list, uint16_t cluster,
if ((0 != xyz.z) && (0xFF != xyz.z)) {
attr_list.addAttribute(command_name, PSTR("Zone")).setUInt(xyz.z);
}
+ // for now convert alamrs 1 and 2 to Occupancy
+ // TODO we may only do this conversion to ZoneType == 0x000D 'Motion Sensor'
+ // Occupancy is 0406/0000 of type Zmap8
+ attr_list.addAttribute(0x0406, 0x0000).setUInt((xyz.x) & 0x01 ? 1 : 0);
break;
case 0x00040000:
case 0x00040001:
diff --git a/tasmota/xdrv_23_zigbee_8_parsers.ino b/tasmota/xdrv_23_zigbee_8_parsers.ino
index d800eaf71..788c72e88 100644
--- a/tasmota/xdrv_23_zigbee_8_parsers.ino
+++ b/tasmota/xdrv_23_zigbee_8_parsers.ino
@@ -533,7 +533,11 @@ int32_t Z_ReceiveActiveEp(int32_t res, const class SBuffer &buf) {
#endif
for (uint32_t i = 0; i < activeEpCount; i++) {
- zigbee_devices.addEndpoint(nwkAddr, activeEpList[i]);
+ uint8_t ep = activeEpList[i];
+ zigbee_devices.addEndpoint(nwkAddr, ep);
+ if ((i < 4) && (ep < 0x10)) {
+ zigbee_devices.queueTimer(nwkAddr, 0 /* groupaddr */, 1500, ep /* fake cluster as ep */, ep, Z_CAT_EP_DESC, 0 /* value */, &Z_SendSimpleDescReq);
+ }
}
Response_P(PSTR("{\"" D_JSON_ZIGBEE_STATE "\":{"
@@ -546,7 +550,132 @@ int32_t Z_ReceiveActiveEp(int32_t res, const class SBuffer &buf) {
ResponseAppend_P(PSTR("]}}"));
MqttPublishPrefixTopicRulesProcess_P(RESULT_OR_TELE, PSTR(D_JSON_ZIGBEEZCL_RECEIVED));
- Z_SendAFInfoRequest(nwkAddr); // probe for ModelId and ManufId
+ Z_SendDeviceInfoRequest(nwkAddr); // probe for ModelId and ManufId
+
+ return -1;
+}
+
+// list of clusters that need bindings
+const uint8_t Z_bindings[] PROGMEM = {
+ Cx0001, Cx0006, Cx0008, Cx0300,
+ Cx0400, Cx0402, Cx0403, Cx0405, Cx0406,
+ Cx0500,
+};
+
+int32_t Z_ClusterToCxBinding(uint16_t cluster) {
+ uint8_t cx = ClusterToCx(cluster);
+ for (uint32_t i=0; i= 0) {
+ bitSet(cluster_map, found_cx);
+ bitSet(cluster_in_map, found_cx);
+ }
+ }
+ // scan out clusters
+ for (uint32_t i=0; i= 0) {
+ bitSet(cluster_map, found_cx);
+ }
+ }
+
+ // if IAS device, request the device type
+ if (bitRead(cluster_map, Z_ClusterToCxBinding(0x0500))) {
+ // send a read command to cluster 0x0500, attribute 0x0001 (ZoneType) - to read the type of sensor
+ zigbee_devices.queueTimer(shortaddr, 0 /* groupaddr */, 2000, 0x0500, endpoint, Z_CAT_READ_ATTRIBUTE, 0x0001, &Z_SendSingleAttributeRead);
+ }
+
+ // enqueue bind requests
+ for (uint32_t i=0; i 0) { ResponseAppend_P(PSTR(",")); }
+ ResponseAppend_P(PSTR("\"0x%04X\""), buf.get16(numInIndex + i*2));
+ }
+ ResponseAppend_P(PSTR("],\"OutClusters\":["));
+ for (uint32_t i = 0; i < numOutCluster; i++) {
+ if (i > 0) { ResponseAppend_P(PSTR(",")); }
+ ResponseAppend_P(PSTR("\"0x%04X\""), buf.get16(numOutIndex + i*2));
+ }
+ ResponseAppend_P(PSTR("]}}"));
+ MqttPublishPrefixTopic_P(RESULT_OR_TELE, PSTR(D_JSON_ZIGBEEZCL_RECEIVED));
+ XdrvRulesProcess();
+ }
return -1;
}
@@ -831,7 +960,7 @@ int32_t Z_MgmtBindRsp(int32_t res, const class SBuffer &buf) {
//uint64_t srcaddr = buf.get16(idx); // unused
uint8_t srcep = buf.get8(idx + 8);
- uint8_t cluster = buf.get16(idx + 9);
+ uint16_t cluster = buf.get16(idx + 9);
uint8_t addrmode = buf.get8(idx + 11);
uint16_t group = 0x0000;
uint64_t dstaddr = 0;
@@ -960,9 +1089,31 @@ void Z_SendActiveEpReq(uint16_t shortaddr) {
}
//
-// Send AF Info Request
+// Probe the clusters_out on the first endpoint
//
-void Z_SendAFInfoRequest(uint16_t shortaddr) {
+// Send ZDO_SIMPLE_DESC_REQ to get full list of supported Clusters for a specific endpoint
+void Z_SendSimpleDescReq(uint16_t shortaddr, uint16_t groupaddr, uint16_t cluster, uint8_t endpoint, uint32_t value) {
+#ifdef USE_ZIGBEE_ZNP
+ uint8_t SimpleDescReq[] = { Z_SREQ | Z_ZDO, ZDO_SIMPLE_DESC_REQ, // 2504
+ Z_B0(shortaddr), Z_B1(shortaddr), Z_B0(shortaddr), Z_B1(shortaddr),
+ endpoint };
+ ZigbeeZNPSend(SimpleDescReq, sizeof(SimpleDescReq));
+#endif
+#ifdef USE_ZIGBEE_EZSP
+ uint8_t SimpleDescReq[] = { Z_B0(shortaddr), Z_B1(shortaddr), endpoint };
+ EZ_SendZDO(shortaddr, ZDO_SIMPLE_DESC_REQ, SimpleDescReq, sizeof(SimpleDescReq));
+#endif
+}
+
+
+//
+// Send AF Info Request
+// Queue requests for the device
+// 1. Request for 'ModelId' and 'Manufacturer': 0000/0005, 0000/0006
+// 2. Auto-bind to coordinator:
+// Iterate among
+//
+void Z_SendDeviceInfoRequest(uint16_t shortaddr) {
uint8_t endpoint = zigbee_devices.findFirstEndpoint(shortaddr);
if (0x00 == endpoint) { endpoint = 0x01; } // if we don't know the endpoint, try 0x01
uint8_t transacid = zigbee_devices.getNextSeqNumber(shortaddr);
@@ -983,6 +1134,170 @@ void Z_SendAFInfoRequest(uint16_t shortaddr) {
}));
}
+//
+// Send sing attribute read request in Timer
+//
+void Z_SendSingleAttributeRead(uint16_t shortaddr, uint16_t groupaddr, uint16_t cluster, uint8_t endpoint, uint32_t value) {
+ uint8_t transacid = zigbee_devices.getNextSeqNumber(shortaddr);
+ uint8_t InfoReq[2] = { Z_B0(value), Z_B1(value) }; // list of single attribute
+
+ ZigbeeZCLSend_Raw(ZigbeeZCLSendMessage({
+ shortaddr,
+ 0x0000, /* group */
+ cluster /*cluster*/,
+ endpoint,
+ ZCL_READ_ATTRIBUTES,
+ 0x0000, /* manuf */
+ false /* not cluster specific */,
+ true /* response */,
+ transacid, /* zcl transaction id */
+ InfoReq, sizeof(InfoReq)
+ }));
+}
+
+//
+// Auto-bind some clusters to the coordinator's endpoint 0x01
+//
+void Z_AutoBind(uint16_t shortaddr, uint16_t groupaddr, uint16_t cluster, uint8_t endpoint, uint32_t value) {
+ uint64_t srcLongAddr = zigbee_devices.getDeviceLongAddr(shortaddr);
+
+ AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_ZIGBEE "auto-bind `ZbBind {\"Device\":\"0x%04X\",\"Endpoint\":%d,\"Cluster\":\"0x%04X\"}`"),
+ shortaddr, endpoint, cluster);
+#ifdef USE_ZIGBEE_ZNP
+ SBuffer buf(34);
+ buf.add8(Z_SREQ | Z_ZDO);
+ buf.add8(ZDO_BIND_REQ);
+ buf.add16(shortaddr);
+ buf.add64(srcLongAddr);
+ buf.add8(endpoint);
+ buf.add16(cluster);
+ buf.add8(Z_Addr_IEEEAddress); // DstAddrMode - 0x03 = ADDRESS_64_BIT
+ buf.add64(localIEEEAddr);
+ buf.add8(0x01); // toEndpoint
+
+ ZigbeeZNPSend(buf.getBuffer(), buf.len());
+#endif // USE_ZIGBEE_ZNP
+
+#ifdef USE_ZIGBEE_EZSP
+ SBuffer buf(24);
+
+ // ZDO message payload (see Zigbee spec 2.4.3.2.2)
+ buf.add64(srcLongAddr);
+ buf.add8(endpoint);
+ buf.add16(cluster);
+ buf.add8(Z_Addr_IEEEAddress); // DstAddrMode - 0x03 = ADDRESS_64_BIT
+ buf.add64(localIEEEAddr);
+ buf.add8(0x01); // toEndpoint
+
+ EZ_SendZDO(shortaddr, ZDO_BIND_REQ, buf.buf(), buf.len());
+#endif // USE_ZIGBEE_EZSP
+}
+
+//
+// Auto-bind some clusters to the coordinator's endpoint 0x01
+//
+
+// the structure below indicates which attributes need to be configured for attribute reporting
+typedef struct Z_autoAttributeReporting_t {
+ uint16_t cluster;
+ uint16_t attr_id;
+ uint16_t min_interval; // minimum interval in seconds (consecutive reports won't happen before this value)
+ uint16_t max_interval; // maximum interval in seconds (attribut will always be reported after this interval)
+ float report_change; // for non discrete attributes, the value change that triggers a report
+} Z_autoAttributeReporting_t;
+
+// Note the attribute must be registered in the converter list, used to retrieve the type of the attribute
+const Z_autoAttributeReporting_t Z_autoAttributeReporting[] PROGMEM = {
+ { 0x0001, 0x0020, 15*60, 15*60, 0.1 }, // BatteryVoltage
+ { 0x0001, 0x0021, 15*60, 15*60, 1 }, // BatteryPercentage
+ { 0x0006, 0x0000, 1, 60*60, 0 }, // Power
+ { 0x0008, 0x0000, 1, 60*60, 5 }, // Dimmer
+ { 0x0300, 0x0000, 1, 60*60, 5 }, // Hue
+ { 0x0300, 0x0001, 1, 60*60, 5 }, // Sat
+ { 0x0300, 0x0003, 1, 60*60, 100 }, // X
+ { 0x0300, 0x0004, 1, 60*60, 100 }, // Y
+ { 0x0300, 0x0007, 1, 60*60, 5 }, // CT
+ { 0x0300, 0x0008, 1, 60*60, 0 }, // ColorMode
+ { 0x0400, 0x0000, 10, 60*60, 5 }, // Illuminance (5 lux)
+ { 0x0402, 0x0000, 30, 60*60, 0.2 }, // Temperature (0.2 °C)
+ { 0x0403, 0x0000, 30, 60*60, 1 }, // Pressure (1 hPa)
+ { 0x0405, 0x0000, 30, 60*60, 1.0 }, // Humidity (1 %)
+ { 0x0406, 0x0000, 10, 60*60, 0 }, // Occupancy
+ { 0x0500, 0x0002, 1, 60*60, 0 }, // ZoneStatus
+};
+
+//
+// Called by Device Auto-config
+// Configures default values for the most common Attribute Rerporting configurations
+//
+// Note: must be of type `Z_DeviceTimer`
+void Z_AutoConfigReportingForCluster(uint16_t shortaddr, uint16_t groupaddr, uint16_t cluster, uint8_t endpoint, uint32_t value) {
+ // Buffer, max 12 bytes per attribute
+ SBuffer buf(12*6);
+
+
+ Response_P(PSTR("ZbSend {\"Device\":\"0x%04X\",\"Config\":{"), shortaddr);
+
+ boolean comma = false;
+ for (uint32_t i=0; i 0) {
+ AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_ZIGBEE "auto-bind `%s`"), mqtt_data);
+ ZigbeeZCLSend_Raw(ZigbeeZCLSendMessage({
+ shortaddr,
+ 0x0000, /* group */
+ cluster /*cluster*/,
+ endpoint,
+ ZCL_CONFIGURE_REPORTING,
+ 0x0000, /* manuf */
+ false /* not cluster specific */,
+ false /* no response */,
+ zigbee_devices.getNextSeqNumber(shortaddr), /* zcl transaction id */
+ buf.buf(), buf.len()
+ }));
+ }
+}
//
// Handle trustCenterJoinHandler
@@ -1189,6 +1504,8 @@ int32_t EZ_IncomingMessage(int32_t res, const class SBuffer &buf) {
return Z_ReceiveActiveEp(res, zdo_buf);
case ZDO_IEEE_addr_rsp:
return Z_ReceiveIEEEAddr(res, zdo_buf);
+ case ZDO_Simple_Desc_rsp:
+ return Z_ReceiveSimpleDesc(res, zdo_buf);
case ZDO_Bind_rsp:
return Z_BindRsp(res, zdo_buf);
case ZDO_Unbind_rsp:
@@ -1279,9 +1596,8 @@ int32_t EZ_Recv_Default(int32_t res, const class SBuffer &buf) {
\*********************************************************************************************/
// Publish the received values once they have been coalesced
-int32_t Z_PublishAttributes(uint16_t shortaddr, uint16_t groupaddr, uint16_t cluster, uint8_t endpoint, uint32_t value) {
+void Z_PublishAttributes(uint16_t shortaddr, uint16_t groupaddr, uint16_t cluster, uint8_t endpoint, uint32_t value) {
zigbee_devices.jsonPublishFlush(shortaddr);
- return 1;
}
/*********************************************************************************************\
@@ -1363,6 +1679,7 @@ const Z_Dispatcher Z_DispatchTable[] PROGMEM = {
{ { Z_AREQ | Z_ZDO, ZDO_PERMIT_JOIN_IND }, &ZNP_ReceivePermitJoinStatus }, // 45CB
{ { Z_AREQ | Z_ZDO, ZDO_NODE_DESC_RSP }, &ZNP_ReceiveNodeDesc }, // 4582
{ { Z_AREQ | Z_ZDO, ZDO_ACTIVE_EP_RSP }, &Z_ReceiveActiveEp }, // 4585
+ { { Z_AREQ | Z_ZDO, ZDO_SIMPLE_DESC_RSP}, &Z_ReceiveSimpleDesc}, // 4584
{ { Z_AREQ | Z_ZDO, ZDO_IEEE_ADDR_RSP }, &Z_ReceiveIEEEAddr }, // 4581
{ { Z_AREQ | Z_ZDO, ZDO_BIND_RSP }, &Z_BindRsp }, // 45A1
{ { Z_AREQ | Z_ZDO, ZDO_UNBIND_RSP }, &Z_UnbindRsp }, // 45A2
@@ -1413,11 +1730,11 @@ void Z_Query_Bulb(uint16_t shortaddr, uint32_t &wait_ms) {
uint8_t endpoint = zigbee_devices.findFirstEndpoint(shortaddr);
if (endpoint) { // send only if we know the endpoint
- zigbee_devices.setTimer(shortaddr, 0 /* groupaddr */, wait_ms, 0x0006, endpoint, Z_CAT_NONE, 0 /* value */, &Z_ReadAttrCallback);
+ zigbee_devices.setTimer(shortaddr, 0 /* groupaddr */, wait_ms, 0x0006, endpoint, Z_CAT_READ_CLUSTER, 0 /* value */, &Z_ReadAttrCallback);
wait_ms += inter_message_ms;
- zigbee_devices.setTimer(shortaddr, 0 /* groupaddr */, wait_ms, 0x0008, endpoint, Z_CAT_NONE, 0 /* value */, &Z_ReadAttrCallback);
+ zigbee_devices.setTimer(shortaddr, 0 /* groupaddr */, wait_ms, 0x0008, endpoint, Z_CAT_READ_CLUSTER, 0 /* value */, &Z_ReadAttrCallback);
wait_ms += inter_message_ms;
- zigbee_devices.setTimer(shortaddr, 0 /* groupaddr */, wait_ms, 0x0300, endpoint, Z_CAT_NONE, 0 /* value */, &Z_ReadAttrCallback);
+ zigbee_devices.setTimer(shortaddr, 0 /* groupaddr */, wait_ms, 0x0300, endpoint, Z_CAT_READ_CLUSTER, 0 /* value */, &Z_ReadAttrCallback);
wait_ms += inter_message_ms;
zigbee_devices.setTimer(shortaddr, 0, wait_ms + Z_CAT_REACHABILITY_TIMEOUT, 0, endpoint, Z_CAT_REACHABILITY, 0 /* value */, &Z_Unreachable);
wait_ms += 1000; // wait 1 second between devices
From 828490a1b20495dda963dde01c78a6a8a9225638 Mon Sep 17 00:00:00 2001
From: Jason2866 <24528715+Jason2866@users.noreply.github.com>
Date: Sat, 12 Sep 2020 11:15:46 +0200
Subject: [PATCH 037/148] override not needed
---
.github/workflows/CI_github_ESP32.yml | 90 +++++++++-----------------
.github/workflows/Tasmota_build.yml | 93 +++++++++------------------
2 files changed, 61 insertions(+), 122 deletions(-)
diff --git a/.github/workflows/CI_github_ESP32.yml b/.github/workflows/CI_github_ESP32.yml
index cc9af6f19..64fbd39b2 100644
--- a/.github/workflows/CI_github_ESP32.yml
+++ b/.github/workflows/CI_github_ESP32.yml
@@ -17,8 +17,7 @@ jobs:
platformio upgrade --dev
platformio update
- name: Run PlatformIO
- run: |
- mv -f platformio_override_sample.ini platformio_override.ini
+ run: |
platformio run -e tasmota32
tasmota32-webcam:
@@ -34,8 +33,7 @@ jobs:
platformio upgrade --dev
platformio update
- name: Run PlatformIO
- run: |
- mv -f platformio_override_sample.ini platformio_override.ini
+ run: |
platformio run -e tasmota32-webcam
tasmota32-minimal:
@@ -51,8 +49,7 @@ jobs:
platformio upgrade --dev
platformio update
- name: Run PlatformIO
- run: |
- mv -f platformio_override_sample.ini platformio_override.ini
+ run: |
platformio run -e tasmota32-minimal
tasmota32-lite:
runs-on: ubuntu-latest
@@ -67,8 +64,7 @@ jobs:
platformio upgrade --dev
platformio update
- name: Run PlatformIO
- run: |
- mv -f platformio_override_sample.ini platformio_override.ini
+ run: |
platformio run -e tasmota32-lite
tasmota32-knx:
runs-on: ubuntu-latest
@@ -83,8 +79,7 @@ jobs:
platformio upgrade --dev
platformio update
- name: Run PlatformIO
- run: |
- mv -f platformio_override_sample.ini platformio_override.ini
+ run: |
platformio run -e tasmota32-knx
tasmota32-sensors:
@@ -100,8 +95,7 @@ jobs:
platformio upgrade --dev
platformio update
- name: Run PlatformIO
- run: |
- mv -f platformio_override_sample.ini platformio_override.ini
+ run: |
platformio run -e tasmota32-sensors
tasmota32-display:
runs-on: ubuntu-latest
@@ -116,8 +110,7 @@ jobs:
platformio upgrade --dev
platformio update
- name: Run PlatformIO
- run: |
- mv -f platformio_override_sample.ini platformio_override.ini
+ run: |
platformio run -e tasmota32-display
tasmota32-ir:
runs-on: ubuntu-latest
@@ -132,8 +125,7 @@ jobs:
platformio upgrade --dev
platformio update
- name: Run PlatformIO
- run: |
- mv -f platformio_override_sample.ini platformio_override.ini
+ run: |
platformio run -e tasmota32-ir
tasmota32-BG:
runs-on: ubuntu-latest
@@ -148,8 +140,7 @@ jobs:
platformio upgrade --dev
platformio update
- name: Run PlatformIO
- run: |
- mv -f platformio_override_sample.ini platformio_override.ini
+ run: |
platformio run -e tasmota32-BG
tasmota32-BR:
@@ -165,8 +156,7 @@ jobs:
platformio upgrade --dev
platformio update
- name: Run PlatformIO
- run: |
- mv -f platformio_override_sample.ini platformio_override.ini
+ run: |
platformio run -e tasmota32-BR
tasmota32-CN:
runs-on: ubuntu-latest
@@ -181,8 +171,7 @@ jobs:
platformio upgrade --dev
platformio update
- name: Run PlatformIO
- run: |
- mv -f platformio_override_sample.ini platformio_override.ini
+ run: |
platformio run -e tasmota32-CN
tasmota32-CZ:
runs-on: ubuntu-latest
@@ -197,8 +186,7 @@ jobs:
platformio upgrade --dev
platformio update
- name: Run PlatformIO
- run: |
- mv -f platformio_override_sample.ini platformio_override.ini
+ run: |
platformio run -e tasmota32-CZ
tasmota32-DE:
runs-on: ubuntu-latest
@@ -213,8 +201,7 @@ jobs:
platformio upgrade --dev
platformio update
- name: Run PlatformIO
- run: |
- mv -f platformio_override_sample.ini platformio_override.ini
+ run: |
platformio run -e tasmota32-DE
tasmota32-ES:
@@ -230,8 +217,7 @@ jobs:
platformio upgrade --dev
platformio update
- name: Run PlatformIO
- run: |
- mv -f platformio_override_sample.ini platformio_override.ini
+ run: |
platformio run -e tasmota32-ES
tasmota32-FR:
runs-on: ubuntu-latest
@@ -246,8 +232,7 @@ jobs:
platformio upgrade --dev
platformio update
- name: Run PlatformIO
- run: |
- mv -f platformio_override_sample.ini platformio_override.ini
+ run: |
platformio run -e tasmota32-FR
tasmota32-GR:
runs-on: ubuntu-latest
@@ -262,8 +247,7 @@ jobs:
platformio upgrade --dev
platformio update
- name: Run PlatformIO
- run: |
- mv -f platformio_override_sample.ini platformio_override.ini
+ run: |
platformio run -e tasmota32-GR
tasmota32-HE:
runs-on: ubuntu-latest
@@ -278,8 +262,7 @@ jobs:
platformio upgrade --dev
platformio update
- name: Run PlatformIO
- run: |
- mv -f platformio_override_sample.ini platformio_override.ini
+ run: |
platformio run -e tasmota32-HE
tasmota32-HU:
@@ -295,8 +278,7 @@ jobs:
platformio upgrade --dev
platformio update
- name: Run PlatformIO
- run: |
- mv -f platformio_override_sample.ini platformio_override.ini
+ run: |
platformio run -e tasmota32-HU
tasmota32-IT:
runs-on: ubuntu-latest
@@ -311,8 +293,7 @@ jobs:
platformio upgrade --dev
platformio update
- name: Run PlatformIO
- run: |
- mv -f platformio_override_sample.ini platformio_override.ini
+ run: |
platformio run -e tasmota32-IT
tasmota32-KO:
runs-on: ubuntu-latest
@@ -327,8 +308,7 @@ jobs:
platformio upgrade --dev
platformio update
- name: Run PlatformIO
- run: |
- mv -f platformio_override_sample.ini platformio_override.ini
+ run: |
platformio run -e tasmota32-KO
tasmota32-NL:
runs-on: ubuntu-latest
@@ -343,8 +323,7 @@ jobs:
platformio upgrade --dev
platformio update
- name: Run PlatformIO
- run: |
- mv -f platformio_override_sample.ini platformio_override.ini
+ run: |
platformio run -e tasmota32-NL
tasmota32-PL:
@@ -360,8 +339,7 @@ jobs:
platformio upgrade --dev
platformio update
- name: Run PlatformIO
- run: |
- mv -f platformio_override_sample.ini platformio_override.ini
+ run: |
platformio run -e tasmota32-PL
tasmota32-PT:
runs-on: ubuntu-latest
@@ -376,8 +354,7 @@ jobs:
platformio upgrade --dev
platformio update
- name: Run PlatformIO
- run: |
- mv -f platformio_override_sample.ini platformio_override.ini
+ run: |
platformio run -e tasmota32-PT
tasmota32-RO:
runs-on: ubuntu-latest
@@ -392,8 +369,7 @@ jobs:
platformio upgrade --dev
platformio update
- name: Run PlatformIO
- run: |
- mv -f platformio_override_sample.ini platformio_override.ini
+ run: |
platformio run -e tasmota32-RO
tasmota32-RU:
runs-on: ubuntu-latest
@@ -408,8 +384,7 @@ jobs:
platformio upgrade --dev
platformio update
- name: Run PlatformIO
- run: |
- mv -f platformio_override_sample.ini platformio_override.ini
+ run: |
platformio run -e tasmota32-RU
tasmota32-SE:
@@ -425,8 +400,7 @@ jobs:
platformio upgrade --dev
platformio update
- name: Run PlatformIO
- run: |
- mv -f platformio_override_sample.ini platformio_override.ini
+ run: |
platformio run -e tasmota32-SE
tasmota32-SK:
runs-on: ubuntu-latest
@@ -441,8 +415,7 @@ jobs:
platformio upgrade --dev
platformio update
- name: Run PlatformIO
- run: |
- mv -f platformio_override_sample.ini platformio_override.ini
+ run: |
platformio run -e tasmota32-SK
tasmota32-TR:
runs-on: ubuntu-latest
@@ -457,8 +430,7 @@ jobs:
platformio upgrade --dev
platformio update
- name: Run PlatformIO
- run: |
- mv -f platformio_override_sample.ini platformio_override.ini
+ run: |
platformio run -e tasmota32-TR
tasmota32-TW:
runs-on: ubuntu-latest
@@ -473,8 +445,7 @@ jobs:
platformio upgrade --dev
platformio update
- name: Run PlatformIO
- run: |
- mv -f platformio_override_sample.ini platformio_override.ini
+ run: |
platformio run -e tasmota32-TW
tasmota32-UK:
@@ -490,6 +461,5 @@ jobs:
platformio upgrade --dev
platformio update
- name: Run PlatformIO
- run: |
- mv -f platformio_override_sample.ini platformio_override.ini
+ run: |
platformio run -e tasmota32-UK
diff --git a/.github/workflows/Tasmota_build.yml b/.github/workflows/Tasmota_build.yml
index 5d5665715..4a890b89d 100644
--- a/.github/workflows/Tasmota_build.yml
+++ b/.github/workflows/Tasmota_build.yml
@@ -750,8 +750,7 @@ jobs:
platformio upgrade --dev
platformio update
- name: Run PlatformIO
- run: |
- mv -f platformio_override_sample.ini platformio_override.ini
+ run: |
platformio run -e tasmota32
- uses: actions/upload-artifact@v2
with:
@@ -774,8 +773,7 @@ jobs:
platformio upgrade --dev
platformio update
- name: Run PlatformIO
- run: |
- mv -f platformio_override_sample.ini platformio_override.ini
+ run: |
platformio run -e tasmota32-minimal
- uses: actions/upload-artifact@v2
with:
@@ -798,8 +796,7 @@ jobs:
platformio upgrade --dev
platformio update
- name: Run PlatformIO
- run: |
- mv -f platformio_override_sample.ini platformio_override.ini
+ run: |
platformio run -e tasmota32-lite
- uses: actions/upload-artifact@v2
with:
@@ -822,8 +819,7 @@ jobs:
platformio upgrade --dev
platformio update
- name: Run PlatformIO
- run: |
- mv -f platformio_override_sample.ini platformio_override.ini
+ run: |
platformio run -e tasmota32-webcam
- uses: actions/upload-artifact@v2
with:
@@ -846,8 +842,7 @@ jobs:
platformio upgrade --dev
platformio update
- name: Run PlatformIO
- run: |
- mv -f platformio_override_sample.ini platformio_override.ini
+ run: |
platformio run -e tasmota32-knx
- uses: actions/upload-artifact@v2
with:
@@ -870,8 +865,7 @@ jobs:
platformio upgrade --dev
platformio update
- name: Run PlatformIO
- run: |
- mv -f platformio_override_sample.ini platformio_override.ini
+ run: |
platformio run -e tasmota32-sensors
- uses: actions/upload-artifact@v2
with:
@@ -894,8 +888,7 @@ jobs:
platformio upgrade --dev
platformio update
- name: Run PlatformIO
- run: |
- mv -f platformio_override_sample.ini platformio_override.ini
+ run: |
platformio run -e tasmota32-display
- uses: actions/upload-artifact@v2
with:
@@ -918,8 +911,7 @@ jobs:
platformio upgrade --dev
platformio update
- name: Run PlatformIO
- run: |
- mv -f platformio_override_sample.ini platformio_override.ini
+ run: |
platformio run -e tasmota32-ir
- uses: actions/upload-artifact@v2
with:
@@ -942,8 +934,7 @@ jobs:
platformio upgrade --dev
platformio update
- name: Run PlatformIO
- run: |
- mv -f platformio_override_sample.ini platformio_override.ini
+ run: |
platformio run -e tasmota32-ircustom
- uses: actions/upload-artifact@v2
with:
@@ -966,8 +957,7 @@ jobs:
platformio upgrade --dev
platformio update
- name: Run PlatformIO
- run: |
- mv -f platformio_override_sample.ini platformio_override.ini
+ run: |
platformio run -e tasmota32-BG
- uses: actions/upload-artifact@v2
with:
@@ -990,8 +980,7 @@ jobs:
platformio upgrade --dev
platformio update
- name: Run PlatformIO
- run: |
- mv -f platformio_override_sample.ini platformio_override.ini
+ run: |
platformio run -e tasmota32-BR
- uses: actions/upload-artifact@v2
with:
@@ -1014,8 +1003,7 @@ jobs:
platformio upgrade --dev
platformio update
- name: Run PlatformIO
- run: |
- mv -f platformio_override_sample.ini platformio_override.ini
+ run: |
platformio run -e tasmota32-CN
- uses: actions/upload-artifact@v2
with:
@@ -1038,8 +1026,7 @@ jobs:
platformio upgrade --dev
platformio update
- name: Run PlatformIO
- run: |
- mv -f platformio_override_sample.ini platformio_override.ini
+ run: |
platformio run -e tasmota32-CZ
- uses: actions/upload-artifact@v2
with:
@@ -1062,8 +1049,7 @@ jobs:
platformio upgrade --dev
platformio update
- name: Run PlatformIO
- run: |
- mv -f platformio_override_sample.ini platformio_override.ini
+ run: |
platformio run -e tasmota32-DE
- uses: actions/upload-artifact@v2
with:
@@ -1086,8 +1072,7 @@ jobs:
platformio upgrade --dev
platformio update
- name: Run PlatformIO
- run: |
- mv -f platformio_override_sample.ini platformio_override.ini
+ run: |
platformio run -e tasmota32-ES
- uses: actions/upload-artifact@v2
with:
@@ -1110,8 +1095,7 @@ jobs:
platformio upgrade --dev
platformio update
- name: Run PlatformIO
- run: |
- mv -f platformio_override_sample.ini platformio_override.ini
+ run: |
platformio run -e tasmota32-FR
- uses: actions/upload-artifact@v2
with:
@@ -1134,8 +1118,7 @@ jobs:
platformio upgrade --dev
platformio update
- name: Run PlatformIO
- run: |
- mv -f platformio_override_sample.ini platformio_override.ini
+ run: |
platformio run -e tasmota32-GR
- uses: actions/upload-artifact@v2
with:
@@ -1158,8 +1141,7 @@ jobs:
platformio upgrade --dev
platformio update
- name: Run PlatformIO
- run: |
- mv -f platformio_override_sample.ini platformio_override.ini
+ run: |
platformio run -e tasmota32-HE
- uses: actions/upload-artifact@v2
with:
@@ -1182,8 +1164,7 @@ jobs:
platformio upgrade --dev
platformio update
- name: Run PlatformIO
- run: |
- mv -f platformio_override_sample.ini platformio_override.ini
+ run: |
platformio run -e tasmota32-HU
- uses: actions/upload-artifact@v2
with:
@@ -1206,8 +1187,7 @@ jobs:
platformio upgrade --dev
platformio update
- name: Run PlatformIO
- run: |
- mv -f platformio_override_sample.ini platformio_override.ini
+ run: |
platformio run -e tasmota32-IT
- uses: actions/upload-artifact@v2
with:
@@ -1230,8 +1210,7 @@ jobs:
platformio upgrade --dev
platformio update
- name: Run PlatformIO
- run: |
- mv -f platformio_override_sample.ini platformio_override.ini
+ run: |
platformio run -e tasmota32-KO
- uses: actions/upload-artifact@v2
with:
@@ -1254,8 +1233,7 @@ jobs:
platformio upgrade --dev
platformio update
- name: Run PlatformIO
- run: |
- mv -f platformio_override_sample.ini platformio_override.ini
+ run: |
platformio run -e tasmota32-NL
- uses: actions/upload-artifact@v2
with:
@@ -1278,8 +1256,7 @@ jobs:
platformio upgrade --dev
platformio update
- name: Run PlatformIO
- run: |
- mv -f platformio_override_sample.ini platformio_override.ini
+ run: |
platformio run -e tasmota32-PL
- uses: actions/upload-artifact@v2
with:
@@ -1302,8 +1279,7 @@ jobs:
platformio upgrade --dev
platformio update
- name: Run PlatformIO
- run: |
- mv -f platformio_override_sample.ini platformio_override.ini
+ run: |
platformio run -e tasmota32-PT
- uses: actions/upload-artifact@v2
with:
@@ -1326,8 +1302,7 @@ jobs:
platformio upgrade --dev
platformio update
- name: Run PlatformIO
- run: |
- mv -f platformio_override_sample.ini platformio_override.ini
+ run: |
platformio run -e tasmota32-RO
- uses: actions/upload-artifact@v2
with:
@@ -1350,8 +1325,7 @@ jobs:
platformio upgrade --dev
platformio update
- name: Run PlatformIO
- run: |
- mv -f platformio_override_sample.ini platformio_override.ini
+ run: |
platformio run -e tasmota32-RU
- uses: actions/upload-artifact@v2
with:
@@ -1374,8 +1348,7 @@ jobs:
platformio upgrade --dev
platformio update
- name: Run PlatformIO
- run: |
- mv -f platformio_override_sample.ini platformio_override.ini
+ run: |
platformio run -e tasmota32-SE
- uses: actions/upload-artifact@v2
with:
@@ -1398,8 +1371,7 @@ jobs:
platformio upgrade --dev
platformio update
- name: Run PlatformIO
- run: |
- mv -f platformio_override_sample.ini platformio_override.ini
+ run: |
platformio run -e tasmota32-SK
- uses: actions/upload-artifact@v2
with:
@@ -1422,8 +1394,7 @@ jobs:
platformio upgrade --dev
platformio update
- name: Run PlatformIO
- run: |
- mv -f platformio_override_sample.ini platformio_override.ini
+ run: |
platformio run -e tasmota32-TR
- uses: actions/upload-artifact@v2
with:
@@ -1446,8 +1417,7 @@ jobs:
platformio upgrade --dev
platformio update
- name: Run PlatformIO
- run: |
- mv -f platformio_override_sample.ini platformio_override.ini
+ run: |
platformio run -e tasmota32-TW
- uses: actions/upload-artifact@v2
with:
@@ -1470,8 +1440,7 @@ jobs:
platformio upgrade --dev
platformio update
- name: Run PlatformIO
- run: |
- mv -f platformio_override_sample.ini platformio_override.ini
+ run: |
platformio run -e tasmota32-UK
- uses: actions/upload-artifact@v2
with:
From 414cf1f9ac38976f15944108a6fc6ba7a73ab234 Mon Sep 17 00:00:00 2001
From: nicandris
Date: Sat, 12 Sep 2020 12:59:51 +0200
Subject: [PATCH 038/148] added support for Aqara button WXKG12LM
---
tasmota/xdrv_23_zigbee_5_converters.ino | 27 ++++++++++++++++++++++++-
1 file changed, 26 insertions(+), 1 deletion(-)
diff --git a/tasmota/xdrv_23_zigbee_5_converters.ino b/tasmota/xdrv_23_zigbee_5_converters.ino
index e70e6702d..5e3140afd 100644
--- a/tasmota/xdrv_23_zigbee_5_converters.ino
+++ b/tasmota/xdrv_23_zigbee_5_converters.ino
@@ -1494,7 +1494,7 @@ void ZCLFrame::syntheticAqaraCubeOrButton(class Z_attribute_list &attr_list, cla
// presentValue = x + 128 = 180º flip to side x on top
// presentValue = x + 256 = push/slide cube while side x is on top
// presentValue = x + 512 = double tap while side x is on top
- } else if (modelId.startsWith(F("lumi.remote"))) { // only for Aqara button
+ } else if (modelId.startsWith(F("lumi.remote"))) { // only for Aqara button WXKG11LM
int32_t val = attr.getInt();
const __FlashStringHelper *aqara_click = F("click");
const __FlashStringHelper *aqara_action = F("action");
@@ -1516,6 +1516,31 @@ void ZCLFrame::syntheticAqaraCubeOrButton(class Z_attribute_list &attr_list, cla
attr_list.addAttribute(aqara_click).setUInt(val);
break;
}
+ } else if (modelId.startsWith(F("lumi.sensor_switch"))) { // only for Aqara button WXKG12LM
+ int32_t val = attr.getInt();
+ const __FlashStringHelper *aqara_click = F("click");
+ const __FlashStringHelper *aqara_action = F("action");
+
+ switch (val) {
+ case 1:
+ attr_list.addAttribute(aqara_click).setStr(PSTR("single"));
+ break;
+ case 2:
+ attr_list.addAttribute(aqara_click).setStr(PSTR("double"));
+ break;
+ case 16:
+ attr_list.addAttribute(aqara_action).setStr(PSTR("hold"));
+ break;
+ case 17:
+ attr_list.addAttribute(aqara_action).setStr(PSTR("release"));
+ break;
+ case 18:
+ attr_list.addAttribute(aqara_action).setStr(PSTR("shake"));
+ break;
+ default:
+ attr_list.addAttribute(aqara_click).setUInt(val);
+ break;
+ }
}
}
From a951a38d393ba2fb70602a4f6f269dfdb2384c4f Mon Sep 17 00:00:00 2001
From: nicandris
Date: Sat, 12 Sep 2020 13:10:35 +0200
Subject: [PATCH 039/148] merged together the 2 aqara buttons
---
tasmota/xdrv_23_zigbee_5_converters.ino | 24 ++++--------------------
1 file changed, 4 insertions(+), 20 deletions(-)
diff --git a/tasmota/xdrv_23_zigbee_5_converters.ino b/tasmota/xdrv_23_zigbee_5_converters.ino
index 5e3140afd..542aa3a41 100644
--- a/tasmota/xdrv_23_zigbee_5_converters.ino
+++ b/tasmota/xdrv_23_zigbee_5_converters.ino
@@ -1494,7 +1494,7 @@ void ZCLFrame::syntheticAqaraCubeOrButton(class Z_attribute_list &attr_list, cla
// presentValue = x + 128 = 180º flip to side x on top
// presentValue = x + 256 = push/slide cube while side x is on top
// presentValue = x + 512 = double tap while side x is on top
- } else if (modelId.startsWith(F("lumi.remote"))) { // only for Aqara button WXKG11LM
+ } else if (modelId.startsWith(F("lumi.remote")) || modelId.startsWith(F("lumi.sensor_switch"))) { // only for Aqara buttons WXKG11LM & WXKG12LM
int32_t val = attr.getInt();
const __FlashStringHelper *aqara_click = F("click");
const __FlashStringHelper *aqara_action = F("action");
@@ -1503,25 +1503,6 @@ void ZCLFrame::syntheticAqaraCubeOrButton(class Z_attribute_list &attr_list, cla
case 0:
attr_list.addAttribute(aqara_action).setStr(PSTR("hold"));
break;
- case 1:
- attr_list.addAttribute(aqara_click).setStr(PSTR("single"));
- break;
- case 2:
- attr_list.addAttribute(aqara_click).setStr(PSTR("double"));
- break;
- case 255:
- attr_list.addAttribute(aqara_click).setStr(PSTR("release"));
- break;
- default:
- attr_list.addAttribute(aqara_click).setUInt(val);
- break;
- }
- } else if (modelId.startsWith(F("lumi.sensor_switch"))) { // only for Aqara button WXKG12LM
- int32_t val = attr.getInt();
- const __FlashStringHelper *aqara_click = F("click");
- const __FlashStringHelper *aqara_action = F("action");
-
- switch (val) {
case 1:
attr_list.addAttribute(aqara_click).setStr(PSTR("single"));
break;
@@ -1537,6 +1518,9 @@ void ZCLFrame::syntheticAqaraCubeOrButton(class Z_attribute_list &attr_list, cla
case 18:
attr_list.addAttribute(aqara_action).setStr(PSTR("shake"));
break;
+ case 255:
+ attr_list.addAttribute(aqara_action).setStr(PSTR("release"));
+ break;
default:
attr_list.addAttribute(aqara_click).setUInt(val);
break;
From c46fa48a30f7c38bd0143f7b500cb203571d1685 Mon Sep 17 00:00:00 2001
From: Staars
Date: Sat, 12 Sep 2020 19:04:11 +0200
Subject: [PATCH 040/148] support MLX90640
---
tasmota/xdrv_84_MLX90640.ino | 625 +++++++++++++++++++++++++++++++++++
1 file changed, 625 insertions(+)
create mode 100644 tasmota/xdrv_84_MLX90640.ino
diff --git a/tasmota/xdrv_84_MLX90640.ino b/tasmota/xdrv_84_MLX90640.ino
new file mode 100644
index 000000000..808b8f9d2
--- /dev/null
+++ b/tasmota/xdrv_84_MLX90640.ino
@@ -0,0 +1,625 @@
+/*
+ xdrv_84_MLX90640.ino - MLX90640 support for Tasmota
+
+ Copyright (C) 2020 Christian Baars and Theo Arends
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see .
+
+
+ --------------------------------------------------------------------------------------------
+ Version yyyymmdd Action Description
+ --------------------------------------------------------------------------------------------
+ 0.9.0.0 20200827 started - based on https://github.com/melexis/mlx90640-library
+*/
+
+#ifdef USE_I2C
+#ifdef USE_MLX90640
+
+#define MLX90640_ADDRESS 0x33
+#define MLX90640_POI_NUM 6 //some parts of the JS are hardcoded for 6!!
+
+/*********************************************************************************************\
+* MLX90640
+\*********************************************************************************************/
+
+#define XDRV_84 84
+#include
+
+const char MLX90640type[] PROGMEM = "MLX90640";
+
+#ifdef USE_WEBSERVER
+#define WEB_HANDLE_MLX90640 "mlx"
+const char HTTP_BTN_MENU_MLX90640[] PROGMEM = "
";
+#endif // USE_WEBSERVER
+
+struct {
+ uint32_t type:1;
+ uint32_t ready:1;
+ uint32_t dumpedEE:1;
+ uint32_t extractedParams:1;
+ paramsMLX90640 *params;
+ float Ta;
+ uint16_t Frame[834];
+ float To[768];
+ uint8_t pois[2*MLX90640_POI_NUM] = {2,1, 30,1, 10,12, 22,12, 2,23, 30,23}; // {x1,y1,x2,y2,...,x6,y6}
+} MLX90640;
+
+/*********************************************************************************************\
+ * commands
+\*********************************************************************************************/
+
+#define D_CMND_MLX90640 "MLX"
+
+const char S_JSON_MLX90640_COMMAND_NVALUE[] PROGMEM = "{\"" D_CMND_MLX90640 "%s\":%d}";
+const char S_JSON_MLX90640_COMMAND[] PROGMEM = "{\"" D_CMND_MLX90640 "%s\"}";
+const char kMLX90640_Commands[] PROGMEM = "POI";
+
+enum MLX90640_Commands { // commands useable in console or rules
+ CMND_MLX90640_POI // MLXPOIn xxyy - set POI number n to x,y
+ };
+
+/************************************************************************\
+ * Web GUI
+\************************************************************************/
+#ifdef USE_WEBSERVER
+
+#ifdef USE_UNISHOX_COMPRESSION
+const size_t HTTP_MLX90640_1_SNS_SIZE = 389;
+const char HTTP_MLX90640_1_SNS_COMPRESSED[] PROGMEM = "\x3D\x3C\x1F\xF4\x65\x2A\x2B\x32\x18\xCF\x87\xDD\x33\x65\x1D\x86\xBB\x33\xB0\x41"
+ "\xA4\x7D\x9F\x81\xE7\x7A\x90\xDB\x18\x7C\x3B\xA6\x76\x10\xB6\x75\x1B\x0E\x43\xA8"
+ "\x8C\x8E\x43\xA8\x8D\x87\x28\xEA\x23\x23\x94\x77\x8F\x87\xE1\x02\x0D\x13\xAC\xD8"
+ "\x72\x1D\xE3\xD6\x77\x48\xC8\xE5\x1D\x64\x6C\x39\x47\x78\xEC\x3B\xA4\x64\x72\x1D"
+ "\x64\x6C\x39\x0E\xF1\xDB\x23\x61\xCA\x3C\x10\x20\xE3\x3A\x36\xC7\x9A\x3E\x2E\x63"
+ "\xE8\xB4\x6D\x8F\x33\xC1\x9D\xFD\x07\x7C\x67\x7E\x3A\x83\xA3\x61\xD4\x3D\xF1\x0F"
+ "\x06\x77\xF4\x3C\x43\x0D\x87\x50\xCC\xD3\xE1\xEF\x1E\xF9\xE0\xCE\xFE\xBE\x56\x7C"
+ "\x3D\xE3\xDF\x3C\x18\x17\xC1\xD6\xE7\x21\xE7\x44\x37\x05\xF9\x90\xCC\xF1\xDD\x04"
+ "\x2C\x65\x33\x3A\x3B\xC8\xF6\x82\x0E\x87\xF6\x1D\x23\xE0\x21\x66\x87\x41\xE7\x44"
+ "\x3B\x05\xF0\x9B\xC3\xC4\x18\x5A\xFA\x8B\xEC\x3A\x3B\xA7\x78\xF0\x67\x7F\x46\xC4"
+ "\x7C\x4C\xCE\x8E\x81\x85\xAF\xA8\x8D\x87\x5F\xD8\x74\x74\x09\x98\xA3\xC6\x98\x3B"
+ "\xA6\xC3\xF0\xE5\xD3\x3B\xC7\xB4\x8D\x87\xC3\x97\x11\xE0\xF7\x17\xDD\x0B\xFF\x23"
+ "\xDA\x6C\x3C\xD1\x0D\xBA\x14\x74\x30\x16\x67\xCE\xE8\xDB\x18\x77\x4D\x87\x51\xC6"
+ "\x75\x5D\x33\xA9\x9D\x57\x0E\x88\xEF\x1D\xE3\xA8\x8C\x81\x32\xF9\xDD\x04\x5D\x04"
+ "\x8C\x91\xD6\xBE\xC3\xA3\xA5\x60\xC3\xBC\x75\x1C\x67\x55\x63\x3A\x99\xD5\x56\x74"
+ "\x47\x78\xEF\x1E\xE3\xC1\xEE";
+#define HTTP_MLX90640_1_SNS Decompress(HTTP_MLX90640_1_SNS_COMPRESSED,HTTP_MLX90640_1_SNS_SIZE).c_str()
+#else
+const char HTTP_MLX90640_1_SNS[] PROGMEM =
+ ""
+;
+#endif //USE_UNISHOX_COMPRESSION
+#ifdef USE_UNISHOX_COMPRESSION
+const size_t HTTP_MLX90640_4b_SNS_SIZE = 418;
+const char HTTP_MLX90640_4b_SNS_COMPRESSED[] PROGMEM = "\x3D\x07\x60\x86\x4B\x38\x2C\xB1\x0F\x87\xDF\x9D\x0B\x18\x77\x4E\xF1\xE0\xFB\x3F"
+ "\x0F\x40\xEF\x8C\xEF\xCB\x44\x3E\x1F\x63\x42\x36\x1F\x68\x7F\x44\xA1\x47\xC3\xEC"
+ "\xE5\xE3\x3E\xCE\xE1\x0A\x7A\x3C\x2A\x2B\x8F\x87\xD9\xCA\xC6\x7D\x9F\x87\xA1\xD8"
+ "\x40\x83\x83\x9F\x87\xA0\x9A\x66\x7E\x1E\x87\x60\x9A\x66\x7E\x1E\x9E\x61\x30\xE9"
+ "\x68\x87\xC3\xEC\x66\x69\x04\x7D\xAC\xE0\xC5\x5F\x0F\x33\xE1\xF6\x37\x3C\x77\x4E"
+ "\xF1\xF6\x7E\x1E\x98\x32\xB7\x39\x19\xD8\x42\xD9\xF0\xFB\x38\xCF\xB3\xF0\x88\x61"
+ "\x61\x69\xD6\x72\x1E\x87\x61\x02\x0D\x40\x4B\xB8\x72\x10\x20\xDC\x39\x44\x0A\x77"
+ "\x0E\x51\x02\x0D\xC3\x96\x40\xA7\x70\xE5\x90\x20\xDC\x39\x84\x0A\x77\x0E\x61\x02"
+ "\x0D\xC3\x9A\x40\xA7\x70\xE6\x90\x20\xDC\x39\xC4\x08\xB7\x0E\xC0\x41\xE1\x2A\x01"
+ "\xFC\x3D\x04\xD3\x30\x41\xE2\x0C\xE4\x3E\xC8\x10\xF8\x5B\x13\x4C\xCF\xC2\x18\x58"
+ "\x5A\x75\x9C\x67\x99\xDC\x3D\x0B\xC3\x2F\x96\x88\x7C\x3E\xEC\xE4\x3E\xCF\xC3\xD0"
+ "\xEC\x2F\x0C\xBE\x3F\x26\x3B\x32\xF2\x0D\x1D\xDF\x3E\xF6\x7C\xEF\x02\x2E\x1E\x08"
+ "\x39\x11\xCA\x20\x44\xC8\x8E\xC1\xD8\x21\x91\xF8";
+#define HTTP_MLX90640_4b_SNS Decompress(HTTP_MLX90640_4b_SNS_COMPRESSED,HTTP_MLX90640_4b_SNS_SIZE).c_str()
+#else
+const char HTTP_MLX90640_4b_SNS[] PROGMEM =
+ ""
+ ""
+ ""
+ ""
+ ""
+ "POI-0: °C (sensor)
"
+ ""
+ ""
+ ;
+#endif //USE_UNISHOX_COMPRESSION
+void MLX90640UpdateGUI(void){
+ WSContentStart_P("mlx");
+ WSContentSendStyle();
+ WSContentSend_P(HTTP_MLX90640_1_SNS);
+ WSContentSend_P(HTTP_MLX90640_2a_SNS);
+ WSContentSend_P(HTTP_MLX90640_2b_SNS);
+ WSContentSend_P(HTTP_MLX90640_3a_SNS);
+ WSContentSend_P(HTTP_MLX90640_3b_SNS);
+ WSContentSend_P(HTTP_MLX90640_4a_SNS);
+ WSContentSend_P(HTTP_MLX90640_4b_SNS);
+ WSContentSpaceButton(BUTTON_MAIN);
+ WSContentStop();
+}
+
+void MLX90640HandleWebGuiResponse(void){
+ char tmp[(MLX90640_POI_NUM*2)+4];
+ WebGetArg("ul", tmp, sizeof(tmp)); // update line
+ if (strlen(tmp)) {
+ uint8_t _line = atoi(tmp);
+ // AddLog_P2(LOG_LEVEL_DEBUG, "MLX90640: send line %u", _line);
+ float _buf[65];
+ if(_line==0){_buf[0]=1000+MLX90640.Ta;} //ambient temperature modulation hack
+ else{_buf[0]=(float)_line;}
+ memcpy((char*)&_buf[1],(char*)&MLX90640.To[_line*64],64*4);
+ Webserver->send(200,PSTR("application/octet-stream"),(const char*)&_buf,65*4);
+ return;
+ }
+ WebGetArg("up", tmp, sizeof(tmp)); // update POI to browser
+ if (strlen(tmp)==1) {
+ Webserver->send(200,PSTR("application/octet-stream"),(const char*)&MLX90640.pois,MLX90640_POI_NUM*2);
+ return;
+ }
+ else if (strlen(tmp)>2) { // receive updated POI from browser
+ uint32_t _poi = atoi(tmp);
+ uint32_t _poiNum = (_poi-(_poi%10000))/10000;
+ MLX90640.pois[_poiNum*2] = (_poi%10000)/100;
+ MLX90640.pois[(_poiNum*2)+1] = _poi%100;
+ // AddLog_P2(LOG_LEVEL_DEBUG, PSTR("RAW: %u, POI-%u: x: %u, y: %u"),_poi,_poiNum,MLX90640.pois[_poiNum],MLX90640.pois[_poiNum+1]);
+ for(int i = 0;i(MLX90640_POI_NUM-1)&&XdrvMailbox.index<1) return false;
+ _idx = (XdrvMailbox.index-1)*2;
+ if (XdrvMailbox.data_len > 0) {
+ uint32_t _coord = TextToInt(XdrvMailbox.data);
+ MLX90640.pois[_idx] = (_coord%10000)/100;
+ if(MLX90640.pois[_idx]>31) MLX90640.pois[_idx]=31;
+ MLX90640.pois[_idx+1] = _coord%100;
+ if(MLX90640.pois[_idx+1]>23) MLX90640.pois[_idx+1]=23;
+ }
+ AddLog_P2(LOG_LEVEL_INFO, PSTR("POI-%u = x:%u,y:%u"),XdrvMailbox.index,MLX90640.pois[_idx],MLX90640.pois[_idx+1]);
+ Response_P(S_JSON_MLX90640_COMMAND_NVALUE, command, XdrvMailbox.payload);
+ break;
+ default:
+ // else for Unknown command
+ serviced = false;
+ break;
+ }
+ } else {
+ return false;
+ }
+ return serviced;
+}
+
+/************************************************************************\
+ * Init
+\************************************************************************/
+void MLX90640init()
+{
+ if (MLX90640.type || !I2cSetDevice(MLX90640_ADDRESS)) { return; }
+
+ Wire.setClock(400000);
+ int status = -1;
+ if(!MLX90640.dumpedEE){
+ status = MLX90640_DumpEE(MLX90640_ADDRESS, MLX90640.Frame);
+ if (status != 0){
+ AddLog_P2(LOG_LEVEL_INFO, PSTR("Failed to load system parameters"));
+ }
+ else {
+ AddLog_P2(LOG_LEVEL_INFO, PSTR("MLX90640: started"));
+ MLX90640.type = true;
+ }
+ MLX90640.params = new paramsMLX90640;
+ }
+}
+
+/************************************************************************\
+ * Run loop
+\************************************************************************/
+void MLX90640every100msec(){
+ static uint32_t _job = 0;
+ int status;
+ uint32_t _time;
+
+ if(!MLX90640.extractedParams){
+ static uint32_t _chunk = 0;
+ AddLog_P2(LOG_LEVEL_DEBUG, PSTR("MLX90640: will read chunk: %u"), _chunk);
+ _time = millis();
+ status = MLX90640_ExtractParameters(MLX90640.Frame, MLX90640.params, _chunk);
+ if (status == 0){
+ AddLog_P2(LOG_LEVEL_DEBUG, PSTR("MLX90640: parameter received after: %u msec, status: %u"), TimePassedSince(_time), status);
+ }
+ if (_chunk == 5) MLX90640.extractedParams = true;
+ _chunk++;
+ return;
+ }
+
+ switch(_job){
+ case 0:
+ if(MLX90640_SynchFrame(MLX90640_ADDRESS)!=0){
+ _job=-1;
+ AddLog_P2(LOG_LEVEL_DEBUG, PSTR("MLX90640: frame not ready"));
+ break;
+ }
+ // _time = millis();
+ status = MLX90640_GetFrameData(MLX90640_ADDRESS, MLX90640.Frame);
+ // AddLog_P2(LOG_LEVEL_DEBUG, PSTR("MLX90640: got frame 0 in %u msecs, status: %i"), TimePassedSince(_time), status);
+ break;
+ case 1:
+ MLX90640.Ta = MLX90640_GetTa(MLX90640.Frame, MLX90640.params);
+ break;
+ case 2:
+ // _time = millis();
+ MLX90640_CalculateTo(MLX90640.Frame, MLX90640.params, 0.95f, MLX90640.Ta - 8, MLX90640.To, 0);
+ // AddLog_P2(LOG_LEVEL_DEBUG, PSTR("MLX90640: calculated temperatures in %u msecs"), TimePassedSince(_time));
+ break;
+ case 5:
+ if(MLX90640_SynchFrame(MLX90640_ADDRESS)!=0){
+ _job=4;
+ break;
+ }
+ // _time = millis();
+ status = MLX90640_GetFrameData(MLX90640_ADDRESS, MLX90640.Frame);
+ // // AddLog_P2(LOG_LEVEL_DEBUG, PSTR("MLX90640: got frame 1 in %u msecs, status: %i"), TimePassedSince(_time), status);
+ break;
+ case 7:
+ // _time = millis();
+ MLX90640_CalculateTo(MLX90640.Frame, MLX90640.params, 0.95f, MLX90640.Ta - 8, MLX90640.To, 1);
+ // AddLog_P2(LOG_LEVEL_DEBUG, PSTR("MLX90640: calculated temperatures in %u msecs"), TimePassedSince(_time));
+ break;
+ default:
+ break;
+ }
+ _job++;
+ if(_job>10) _job=0;
+}
+
+
+/*********************************************************************************************\
+ * Interface
+\*********************************************************************************************/
+
+void MLX90640Show(uint8_t json)
+{
+ char amb_tstr[FLOATSZ];
+ dtostrfd(MLX90640.Ta, Settings.flag2.temperature_resolution, amb_tstr);
+ if (json) {
+ ResponseAppend_P(PSTR(",\"MLX90640\":{\"" D_JSON_TEMPERATURE "\":[%s"), amb_tstr);
+ for(int i = 0;ion("/mlx", MLX90640HandleWebGui);
+ break;
+#endif // USE_WEBSERVER
+ case FUNC_COMMAND:
+ result = MLX90640Cmd();
+ break;
+ }
+ }
+ return result;
+}
+
+#endif // USE_MLX90640_SENSOR
+#endif // USE_I2C
From 3300196436cc97be1c18c7d27088735165e63cf7 Mon Sep 17 00:00:00 2001
From: Staars
Date: Sat, 12 Sep 2020 19:05:19 +0200
Subject: [PATCH 041/148] add modified mlx90640-lib
---
lib/mlx90640-library/MLX90640_API.cpp | 1640 +++++++++++++++++++++++++
lib/mlx90640-library/MLX90640_API.h | 74 ++
2 files changed, 1714 insertions(+)
create mode 100644 lib/mlx90640-library/MLX90640_API.cpp
create mode 100644 lib/mlx90640-library/MLX90640_API.h
diff --git a/lib/mlx90640-library/MLX90640_API.cpp b/lib/mlx90640-library/MLX90640_API.cpp
new file mode 100644
index 000000000..87d871530
--- /dev/null
+++ b/lib/mlx90640-library/MLX90640_API.cpp
@@ -0,0 +1,1640 @@
+/**
+ * @copyright (C) 2017 Melexis N.V.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include
+#include
+#include
+
+void ExtractVDDParameters(uint16_t *eeData, paramsMLX90640 *mlx90640);
+void ExtractPTATParameters(uint16_t *eeData, paramsMLX90640 *mlx90640);
+void ExtractGainParameters(uint16_t *eeData, paramsMLX90640 *mlx90640);
+void ExtractTgcParameters(uint16_t *eeData, paramsMLX90640 *mlx90640);
+void ExtractResolutionParameters(uint16_t *eeData, paramsMLX90640 *mlx90640);
+void ExtractKsTaParameters(uint16_t *eeData, paramsMLX90640 *mlx90640);
+void ExtractKsToParameters(uint16_t *eeData, paramsMLX90640 *mlx90640);
+void ExtractAlphaParameters(uint16_t *eeData, paramsMLX90640 *mlx90640);
+void ExtractOffsetParameters(uint16_t *eeData, paramsMLX90640 *mlx90640);
+void ExtractKtaPixelParameters(uint16_t *eeData, paramsMLX90640 *mlx90640);
+void ExtractKvPixelParameters(uint16_t *eeData, paramsMLX90640 *mlx90640);
+void ExtractCPParameters(uint16_t *eeData, paramsMLX90640 *mlx90640);
+void ExtractCILCParameters(uint16_t *eeData, paramsMLX90640 *mlx90640);
+int ExtractDeviatingPixels(uint16_t *eeData, paramsMLX90640 *mlx90640);
+int CheckAdjacentPixels(uint16_t pix1, uint16_t pix2);
+float GetMedian(float *values, int n);
+int IsPixelBad(uint16_t pixel,paramsMLX90640 *params);
+int ValidateFrameData(uint16_t *frameData);
+int ValidateAuxData(uint16_t *auxData);
+int MLX90640_I2CRead(uint8_t addr, uint32_t reg, uint16_t len, uint16_t *reg_data);
+int MLX90640_I2CWrite(uint8_t _deviceAddress, unsigned int writeAddress, uint16_t data);
+
+// I2C
+#define I2C_BUFFER_LENGTH 128
+
+int MLX90640_I2CRead(uint8_t addr, uint32_t reg, uint16_t len, uint16_t *reg_data){
+ int bytesRemaining = len * 2;
+ int dataSpot = 0; //Start at beginning of array
+ while (bytesRemaining > 0)
+ {
+ Wire.beginTransmission(addr);
+ Wire.write(reg >> 8); //MSB
+ Wire.write(reg & 0xFF); //LSB
+ if (Wire.endTransmission(false) != 0) //Do not release bus
+ {
+ return (0); //Sensor did not ACK
+ }
+ int numberOfBytesToRead = bytesRemaining;
+ if (numberOfBytesToRead > I2C_BUFFER_LENGTH) numberOfBytesToRead = I2C_BUFFER_LENGTH;
+ Wire.requestFrom((int)addr, numberOfBytesToRead);
+ if (Wire.available())
+ {
+ for (uint32_t x = 0 ; x < numberOfBytesToRead / 2; x++)
+ {
+ reg_data[dataSpot] = Wire.read() << 8; //MSB
+ reg_data[dataSpot] |= Wire.read(); //LSB
+ dataSpot++;
+ }
+ }
+ bytesRemaining -= numberOfBytesToRead;
+ reg += numberOfBytesToRead / 2;
+ }
+ return (0); //Success
+}
+
+int MLX90640_I2CWrite(uint8_t _deviceAddress, unsigned int writeAddress, uint16_t data)
+{
+ Wire.beginTransmission((uint8_t)_deviceAddress);
+ Wire.write(writeAddress >> 8); //MSB
+ Wire.write(writeAddress & 0xFF); //LSB
+ Wire.write(data >> 8); //MSB
+ Wire.write(data & 0xFF); //LSB
+ if (Wire.endTransmission() != 0)
+ {
+ //Sensor did not ACK
+ return (-1);
+ }
+ uint16_t dataCheck;
+ MLX90640_I2CRead(_deviceAddress, writeAddress, 1, &dataCheck);
+ if (dataCheck != data)
+ {
+ return -2;
+ }
+ return (0); //Success
+}
+
+int MLX90640_DumpEE(uint8_t slaveAddr, uint16_t *eeData)
+{
+ return MLX90640_I2CRead(slaveAddr, 0x2400, 832, eeData);
+}
+
+int MLX90640_SynchFrame(uint8_t slaveAddr)
+{
+ uint16_t dataReady = 0;
+ uint16_t statusRegister;
+ int error = 1;
+
+ error = MLX90640_I2CWrite(slaveAddr, 0x8000, 0x0030);
+ if(error == -1)
+ {
+ return error;
+ }
+
+ while(dataReady == 0)
+ {
+ error = MLX90640_I2CRead(slaveAddr, 0x8000, 1, &statusRegister);
+ if(error != 0)
+ {
+ return error;
+ }
+ dataReady = statusRegister & 0x0008;
+ }
+
+ return 0;
+}
+
+// int MLX90640_TriggerMeasurement(uint8_t slaveAddr) // ATM not used in Tasmota
+// {
+// int error = 1;
+// uint16_t ctrlReg;
+
+// error = MLX90640_I2CRead(slaveAddr, 0x800D, 1, &ctrlReg);
+
+// if ( error != 0)
+// {
+// return error;
+// }
+
+// ctrlReg |= 0x8000;
+// error = MLX90640_I2CWrite(slaveAddr, 0x800D, ctrlReg);
+
+// if ( error != 0)
+// {
+// return error;
+// }
+
+// // error = MLX90640_I2CGeneralReset();
+
+// // if ( error != 0)
+// // {
+// // return error;
+// // }
+
+// error = MLX90640_I2CRead(slaveAddr, 0x800D, 1, &ctrlReg);
+
+// if ( error != 0)
+// {
+// return error;
+// }
+
+// if ((ctrlReg & 0x8000) != 0)
+// {
+// return -9;
+// }
+
+// return 0;
+// }
+
+int MLX90640_GetFrameData(uint8_t slaveAddr, uint16_t *frameData)
+{
+ uint16_t dataReady = 0;
+ uint16_t controlRegister1;
+ uint16_t statusRegister;
+ int error = 1;
+ uint16_t data[64];
+ uint8_t cnt = 0;
+
+ while(dataReady == 0)
+ {
+ error = MLX90640_I2CRead(slaveAddr, 0x8000, 1, &statusRegister);
+ if(error != 0)
+ {
+ return error;
+ }
+ dataReady = statusRegister & 0x0008;
+ }
+
+ error = MLX90640_I2CWrite(slaveAddr, 0x8000, 0x0030);
+ if(error == -1)
+ {
+ return error;
+ }
+
+ error = MLX90640_I2CRead(slaveAddr, 0x0400, 768, frameData);
+ if(error != 0)
+ {
+ return error;
+ }
+
+ error = MLX90640_I2CRead(slaveAddr, 0x0700, 64, data);
+ if(error != 0)
+ {
+ return error;
+ }
+
+ error = MLX90640_I2CRead(slaveAddr, 0x800D, 1, &controlRegister1);
+ frameData[832] = controlRegister1;
+ frameData[833] = statusRegister & 0x0001;
+
+ if(error != 0)
+ {
+ return error;
+ }
+
+ error = ValidateAuxData(data);
+ if(error == 0)
+ {
+ for(cnt=0; cnt<64; cnt++)
+ {
+ frameData[cnt+768] = data[cnt];
+ }
+ }
+
+ error = ValidateFrameData(frameData);
+ if (error != 0)
+ {
+ return error;
+ }
+
+ return frameData[833];
+}
+
+int ValidateFrameData(uint16_t *frameData)
+{
+ uint8_t line = 0;
+
+ for(int i=0; i<768; i+=32)
+ {
+ if((frameData[i] == 0x7FFF) && (line%2 == frameData[833])) return -8;
+ line = line + 1;
+ }
+
+ return 0;
+}
+
+int ValidateAuxData(uint16_t *auxData)
+{
+
+ if(auxData[0] == 0x7FFF) return -8;
+
+ for(int i=8; i<19; i++)
+ {
+ if(auxData[i] == 0x7FFF) return -8;
+ }
+
+ for(int i=20; i<23; i++)
+ {
+ if(auxData[i] == 0x7FFF) return -8;
+ }
+
+ for(int i=24; i<33; i++)
+ {
+ if(auxData[i] == 0x7FFF) return -8;
+ }
+
+ for(int i=40; i<51; i++)
+ {
+ if(auxData[i] == 0x7FFF) return -8;
+ }
+
+ for(int i=52; i<55; i++)
+ {
+ if(auxData[i] == 0x7FFF) return -8;
+ }
+
+ for(int i=56; i<64; i++)
+ {
+ if(auxData[i] == 0x7FFF) return -8;
+ }
+
+ return 0;
+
+}
+
+int MLX90640_ExtractParameters(uint16_t *eeData, paramsMLX90640 *mlx90640, int _chunk) // Tasmota
+{
+ int error = 0;
+ switch(_chunk){
+ case 0:
+ ExtractVDDParameters(eeData, mlx90640);
+ ExtractPTATParameters(eeData, mlx90640);
+ ExtractGainParameters(eeData, mlx90640);
+ ExtractTgcParameters(eeData, mlx90640);
+ ExtractResolutionParameters(eeData, mlx90640);
+ ExtractKsTaParameters(eeData, mlx90640);
+ ExtractKsToParameters(eeData, mlx90640);
+ break;
+ case 1:
+ ExtractCPParameters(eeData, mlx90640);
+ ExtractAlphaParameters(eeData, mlx90640);
+ break;
+ case 2:
+ ExtractOffsetParameters(eeData, mlx90640);
+ break;
+ case 3:
+ ExtractKtaPixelParameters(eeData, mlx90640);
+ break;
+ case 4:
+ ExtractKvPixelParameters(eeData, mlx90640);
+ break;
+ case 5:
+ ExtractCILCParameters(eeData, mlx90640);
+ error = ExtractDeviatingPixels(eeData, mlx90640);
+ break;
+ }
+ return error;
+}
+
+//------------------------------------------------------------------------------
+
+int MLX90640_SetResolution(uint8_t slaveAddr, uint8_t resolution)
+{
+ uint16_t controlRegister1;
+ int value;
+ int error;
+
+ value = (resolution & 0x03) << 10;
+
+ error = MLX90640_I2CRead(slaveAddr, 0x800D, 1, &controlRegister1);
+
+ if(error == 0)
+ {
+ value = (controlRegister1 & 0xF3FF) | value;
+ error = MLX90640_I2CWrite(slaveAddr, 0x800D, value);
+ }
+
+ return error;
+}
+
+//------------------------------------------------------------------------------
+
+int MLX90640_GetCurResolution(uint8_t slaveAddr)
+{
+ uint16_t controlRegister1;
+ int resolutionRAM;
+ int error;
+
+ error = MLX90640_I2CRead(slaveAddr, 0x800D, 1, &controlRegister1);
+ if(error != 0)
+ {
+ return error;
+ }
+ resolutionRAM = (controlRegister1 & 0x0C00) >> 10;
+
+ return resolutionRAM;
+}
+
+//------------------------------------------------------------------------------
+
+int MLX90640_SetRefreshRate(uint8_t slaveAddr, uint8_t refreshRate)
+{
+ uint16_t controlRegister1;
+ int value;
+ int error;
+
+ value = (refreshRate & 0x07)<<7;
+
+ error = MLX90640_I2CRead(slaveAddr, 0x800D, 1, &controlRegister1);
+ if(error == 0)
+ {
+ value = (controlRegister1 & 0xFC7F) | value;
+ error = MLX90640_I2CWrite(slaveAddr, 0x800D, value);
+ }
+
+ return error;
+}
+
+//------------------------------------------------------------------------------
+
+int MLX90640_GetRefreshRate(uint8_t slaveAddr)
+{
+ uint16_t controlRegister1;
+ int refreshRate;
+ int error;
+
+ error = MLX90640_I2CRead(slaveAddr, 0x800D, 1, &controlRegister1);
+ if(error != 0)
+ {
+ return error;
+ }
+ refreshRate = (controlRegister1 & 0x0380) >> 7;
+
+ return refreshRate;
+}
+
+//------------------------------------------------------------------------------
+
+int MLX90640_SetInterleavedMode(uint8_t slaveAddr)
+{
+ uint16_t controlRegister1;
+ int value;
+ int error;
+
+ error = MLX90640_I2CRead(slaveAddr, 0x800D, 1, &controlRegister1);
+
+ if(error == 0)
+ {
+ value = (controlRegister1 & 0xEFFF);
+ error = MLX90640_I2CWrite(slaveAddr, 0x800D, value);
+ }
+
+ return error;
+}
+
+//------------------------------------------------------------------------------
+
+int MLX90640_SetChessMode(uint8_t slaveAddr)
+{
+ uint16_t controlRegister1;
+ int value;
+ int error;
+
+ error = MLX90640_I2CRead(slaveAddr, 0x800D, 1, &controlRegister1);
+
+ if(error == 0)
+ {
+ value = (controlRegister1 | 0x1000);
+ error = MLX90640_I2CWrite(slaveAddr, 0x800D, value);
+ }
+
+ return error;
+}
+
+//------------------------------------------------------------------------------
+
+int MLX90640_GetCurMode(uint8_t slaveAddr)
+{
+ uint16_t controlRegister1;
+ int modeRAM;
+ int error;
+
+ error = MLX90640_I2CRead(slaveAddr, 0x800D, 1, &controlRegister1);
+ if(error != 0)
+ {
+ return error;
+ }
+ modeRAM = (controlRegister1 & 0x1000) >> 12;
+
+ return modeRAM;
+}
+
+//------------------------------------------------------------------------------
+void MLX90640_CalculateTo(uint16_t *frameData, const paramsMLX90640 *params, float emissivity, float tr, float *result, uint8_t _part)
+{
+ float vdd;
+ float ta;
+ float ta4;
+ float tr4;
+ float taTr;
+ float gain;
+ float irDataCP[2];
+ float irData;
+ float alphaCompensated;
+ uint8_t mode;
+ int8_t ilPattern;
+ int8_t chessPattern;
+ int8_t pattern;
+ int8_t conversionPattern;
+ float Sx;
+ float To;
+ float alphaCorrR[4];
+ int8_t range;
+ uint16_t subPage;
+ float ktaScale;
+ float kvScale;
+ float alphaScale;
+ float kta;
+ float kv;
+
+ subPage = frameData[833];
+ vdd = MLX90640_GetVdd(frameData, params);
+ ta = MLX90640_GetTa(frameData, params);
+
+ ta4 = (ta + 273.15);
+ ta4 = ta4 * ta4;
+ ta4 = ta4 * ta4;
+ tr4 = (tr + 273.15);
+ tr4 = tr4 * tr4;
+ tr4 = tr4 * tr4;
+ taTr = tr4 - (tr4-ta4)/emissivity;
+
+ ktaScale = pow(2,(double)params->ktaScale);
+ kvScale = pow(2,(double)params->kvScale);
+ alphaScale = pow(2,(double)params->alphaScale);
+
+ alphaCorrR[0] = 1 / (1 + params->ksTo[0] * 40);
+ alphaCorrR[1] = 1 ;
+ alphaCorrR[2] = (1 + params->ksTo[1] * params->ct[2]);
+ alphaCorrR[3] = alphaCorrR[2] * (1 + params->ksTo[2] * (params->ct[3] - params->ct[2]));
+
+//------------------------- Gain calculation -----------------------------------
+ gain = frameData[778];
+ if(gain > 32767)
+ {
+ gain = gain - 65536;
+ }
+
+ gain = params->gainEE / gain;
+
+//------------------------- To calculation -------------------------------------
+ mode = (frameData[832] & 0x1000) >> 5;
+
+ irDataCP[0] = frameData[776];
+ irDataCP[1] = frameData[808];
+ for( int i = 0; i < 2; i++)
+ {
+ if(irDataCP[i] > 32767)
+ {
+ irDataCP[i] = irDataCP[i] - 65536;
+ }
+ irDataCP[i] = irDataCP[i] * gain;
+ }
+ irDataCP[0] = irDataCP[0] - params->cpOffset[0] * (1 + params->cpKta * (ta - 25)) * (1 + params->cpKv * (vdd - 3.3));
+ if( mode == params->calibrationModeEE)
+ {
+ irDataCP[1] = irDataCP[1] - params->cpOffset[1] * (1 + params->cpKta * (ta - 25)) * (1 + params->cpKv * (vdd - 3.3));
+ }
+ else
+ {
+ irDataCP[1] = irDataCP[1] - (params->cpOffset[1] + params->ilChessC[0]) * (1 + params->cpKta * (ta - 25)) * (1 + params->cpKv * (vdd - 3.3));
+ }
+
+ uint32_t _offset = _part*(768/2);
+ for( int pixelNumber = _offset; pixelNumber < (_offset+(768/2)); pixelNumber++)
+ {
+ ilPattern = pixelNumber / 32 - (pixelNumber / 64) * 2;
+ chessPattern = ilPattern ^ (pixelNumber - (pixelNumber/2)*2);
+ conversionPattern = ((pixelNumber + 2) / 4 - (pixelNumber + 3) / 4 + (pixelNumber + 1) / 4 - pixelNumber / 4) * (1 - 2 * ilPattern);
+
+ if(mode == 0)
+ {
+ pattern = ilPattern;
+ }
+ else
+ {
+ pattern = chessPattern;
+ }
+
+ if(pattern == frameData[833])
+ {
+ irData = frameData[pixelNumber];
+ if(irData > 32767)
+ {
+ irData = irData - 65536;
+ }
+ irData = irData * gain;
+
+ kta = params->kta[pixelNumber]/ktaScale;
+ kv = params->kv[pixelNumber]/kvScale;
+ irData = irData - params->offset[pixelNumber]*(1 + kta*(ta - 25))*(1 + kv*(vdd - 3.3));
+
+ if(mode != params->calibrationModeEE)
+ {
+ irData = irData + params->ilChessC[2] * (2 * ilPattern - 1) - params->ilChessC[1] * conversionPattern;
+ }
+
+ irData = irData - params->tgc * irDataCP[subPage];
+ irData = irData / emissivity;
+
+ alphaCompensated = SCALEALPHA*alphaScale/params->alpha[pixelNumber];
+ alphaCompensated = alphaCompensated*(1 + params->KsTa * (ta - 25));
+
+ Sx = alphaCompensated * alphaCompensated * alphaCompensated * (irData + alphaCompensated * taTr);
+ Sx = sqrt(sqrt(Sx)) * params->ksTo[1];
+
+ To = sqrt(sqrt(irData/(alphaCompensated * (1 - params->ksTo[1] * 273.15) + Sx) + taTr)) - 273.15;
+
+ if(To < params->ct[1])
+ {
+ range = 0;
+ }
+ else if(To < params->ct[2])
+ {
+ range = 1;
+ }
+ else if(To < params->ct[3])
+ {
+ range = 2;
+ }
+ else
+ {
+ range = 3;
+ }
+
+ To = sqrt(sqrt(irData / (alphaCompensated * alphaCorrR[range] * (1 + params->ksTo[range] * (To - params->ct[range]))) + taTr)) - 273.15;
+
+ result[pixelNumber] = To;
+ }
+ }
+}
+
+//------------------------------------------------------------------------------
+
+// void MLX90640_GetImage(uint16_t *frameData, const paramsMLX90640 *params, float *result)
+// {
+// float vdd;
+// float ta;
+// float gain;
+// float irDataCP[2];
+// float irData;
+// float alphaCompensated;
+// uint8_t mode;
+// int8_t ilPattern;
+// int8_t chessPattern;
+// int8_t pattern;
+// int8_t conversionPattern;
+// float image;
+// uint16_t subPage;
+// float ktaScale;
+// float kvScale;
+// float kta;
+// float kv;
+
+// subPage = frameData[833];
+// vdd = MLX90640_GetVdd(frameData, params);
+// ta = MLX90640_GetTa(frameData, params);
+
+// ktaScale = pow(2,(double)params->ktaScale);
+// kvScale = pow(2,(double)params->kvScale);
+
+// //------------------------- Gain calculation -----------------------------------
+// gain = frameData[778];
+// if(gain > 32767)
+// {
+// gain = gain - 65536;
+// }
+
+// gain = params->gainEE / gain;
+
+// //------------------------- Image calculation -------------------------------------
+// mode = (frameData[832] & 0x1000) >> 5;
+
+// irDataCP[0] = frameData[776];
+// irDataCP[1] = frameData[808];
+// for( int i = 0; i < 2; i++)
+// {
+// if(irDataCP[i] > 32767)
+// {
+// irDataCP[i] = irDataCP[i] - 65536;
+// }
+// irDataCP[i] = irDataCP[i] * gain;
+// }
+// irDataCP[0] = irDataCP[0] - params->cpOffset[0] * (1 + params->cpKta * (ta - 25)) * (1 + params->cpKv * (vdd - 3.3));
+// if( mode == params->calibrationModeEE)
+// {
+// irDataCP[1] = irDataCP[1] - params->cpOffset[1] * (1 + params->cpKta * (ta - 25)) * (1 + params->cpKv * (vdd - 3.3));
+// }
+// else
+// {
+// irDataCP[1] = irDataCP[1] - (params->cpOffset[1] + params->ilChessC[0]) * (1 + params->cpKta * (ta - 25)) * (1 + params->cpKv * (vdd - 3.3));
+// }
+
+// for( int pixelNumber = 0; pixelNumber < 768; pixelNumber++)
+// {
+// ilPattern = pixelNumber / 32 - (pixelNumber / 64) * 2;
+// chessPattern = ilPattern ^ (pixelNumber - (pixelNumber/2)*2);
+// conversionPattern = ((pixelNumber + 2) / 4 - (pixelNumber + 3) / 4 + (pixelNumber + 1) / 4 - pixelNumber / 4) * (1 - 2 * ilPattern);
+
+// if(mode == 0)
+// {
+// pattern = ilPattern;
+// }
+// else
+// {
+// pattern = chessPattern;
+// }
+
+// if(pattern == frameData[833])
+// {
+// irData = frameData[pixelNumber];
+// if(irData > 32767)
+// {
+// irData = irData - 65536;
+// }
+// irData = irData * gain;
+
+// kta = params->kta[pixelNumber]/ktaScale;
+// kv = params->kv[pixelNumber]/kvScale;
+// irData = irData - params->offset[pixelNumber]*(1 + kta*(ta - 25))*(1 + kv*(vdd - 3.3));
+
+// if(mode != params->calibrationModeEE)
+// {
+// irData = irData + params->ilChessC[2] * (2 * ilPattern - 1) - params->ilChessC[1] * conversionPattern;
+// }
+
+// irData = irData - params->tgc * irDataCP[subPage];
+
+// alphaCompensated = params->alpha[pixelNumber];
+
+// image = irData*alphaCompensated;
+
+// result[pixelNumber] = image;
+// }
+// }
+// }
+
+//------------------------------------------------------------------------------
+
+float MLX90640_GetVdd(uint16_t *frameData, const paramsMLX90640 *params)
+{
+ float vdd;
+ float resolutionCorrection;
+
+ int resolutionRAM;
+
+ vdd = frameData[810];
+ if(vdd > 32767)
+ {
+ vdd = vdd - 65536;
+ }
+ resolutionRAM = (frameData[832] & 0x0C00) >> 10;
+ resolutionCorrection = pow(2, (double)params->resolutionEE) / pow(2, (double)resolutionRAM);
+ vdd = (resolutionCorrection * vdd - params->vdd25) / params->kVdd + 3.3;
+
+ return vdd;
+}
+
+//------------------------------------------------------------------------------
+
+float MLX90640_GetTa(uint16_t *frameData, const paramsMLX90640 *params)
+{
+ float ptat;
+ float ptatArt;
+ float vdd;
+ float ta;
+
+ vdd = MLX90640_GetVdd(frameData, params);
+
+ ptat = frameData[800];
+ if(ptat > 32767)
+ {
+ ptat = ptat - 65536;
+ }
+
+ ptatArt = frameData[768];
+ if(ptatArt > 32767)
+ {
+ ptatArt = ptatArt - 65536;
+ }
+ ptatArt = (ptat / (ptat * params->alphaPTAT + ptatArt)) * pow(2, (double)18);
+
+ ta = (ptatArt / (1 + params->KvPTAT * (vdd - 3.3)) - params->vPTAT25);
+ ta = ta / params->KtPTAT + 25;
+
+ return ta;
+}
+
+//------------------------------------------------------------------------------
+
+int MLX90640_GetSubPageNumber(uint16_t *frameData)
+{
+ return frameData[833];
+
+}
+
+//------------------------------------------------------------------------------
+void MLX90640_BadPixelsCorrection(uint16_t *pixels, float *to, int mode, paramsMLX90640 *params)
+{
+ float ap[4];
+ uint8_t pix;
+ uint8_t line;
+ uint8_t column;
+
+ pix = 0;
+ while(pixels[pix] != 0xFFFF)
+ {
+ line = pixels[pix]>>5;
+ column = pixels[pix] - (line<<5);
+
+ if(mode == 1)
+ {
+ if(line == 0)
+ {
+ if(column == 0)
+ {
+ to[pixels[pix]] = to[33];
+ }
+ else if(column == 31)
+ {
+ to[pixels[pix]] = to[62];
+ }
+ else
+ {
+ to[pixels[pix]] = (to[pixels[pix]+31] + to[pixels[pix]+33])/2.0;
+ }
+ }
+ else if(line == 23)
+ {
+ if(column == 0)
+ {
+ to[pixels[pix]] = to[705];
+ }
+ else if(column == 31)
+ {
+ to[pixels[pix]] = to[734];
+ }
+ else
+ {
+ to[pixels[pix]] = (to[pixels[pix]-33] + to[pixels[pix]-31])/2.0;
+ }
+ }
+ else if(column == 0)
+ {
+ to[pixels[pix]] = (to[pixels[pix]-31] + to[pixels[pix]+33])/2.0;
+ }
+ else if(column == 31)
+ {
+ to[pixels[pix]] = (to[pixels[pix]-33] + to[pixels[pix]+31])/2.0;
+ }
+ else
+ {
+ ap[0] = to[pixels[pix]-33];
+ ap[1] = to[pixels[pix]-31];
+ ap[2] = to[pixels[pix]+31];
+ ap[3] = to[pixels[pix]+33];
+ to[pixels[pix]] = GetMedian(ap,4);
+ }
+ }
+ else
+ {
+ if(column == 0)
+ {
+ to[pixels[pix]] = to[pixels[pix]+1];
+ }
+ else if(column == 1 || column == 30)
+ {
+ to[pixels[pix]] = (to[pixels[pix]-1]+to[pixels[pix]+1])/2.0;
+ }
+ else if(column == 31)
+ {
+ to[pixels[pix]] = to[pixels[pix]-1];
+ }
+ else
+ {
+ if(IsPixelBad(pixels[pix]-2,params) == 0 && IsPixelBad(pixels[pix]+2,params) == 0)
+ {
+ ap[0] = to[pixels[pix]+1] - to[pixels[pix]+2];
+ ap[1] = to[pixels[pix]-1] - to[pixels[pix]-2];
+ if(fabs(ap[0]) > fabs(ap[1]))
+ {
+ to[pixels[pix]] = to[pixels[pix]-1] + ap[1];
+ }
+ else
+ {
+ to[pixels[pix]] = to[pixels[pix]+1] + ap[0];
+ }
+ }
+ else
+ {
+ to[pixels[pix]] = (to[pixels[pix]-1]+to[pixels[pix]+1])/2.0;
+ }
+ }
+ }
+ pix = pix + 1;
+ }
+}
+
+//------------------------------------------------------------------------------
+
+void ExtractVDDParameters(uint16_t *eeData, paramsMLX90640 *mlx90640)
+{
+ int16_t kVdd;
+ int16_t vdd25;
+
+ kVdd = eeData[51];
+
+ kVdd = (eeData[51] & 0xFF00) >> 8;
+ if(kVdd > 127)
+ {
+ kVdd = kVdd - 256;
+ }
+ kVdd = 32 * kVdd;
+ vdd25 = eeData[51] & 0x00FF;
+ vdd25 = ((vdd25 - 256) << 5) - 8192;
+
+ mlx90640->kVdd = kVdd;
+ mlx90640->vdd25 = vdd25;
+}
+
+//------------------------------------------------------------------------------
+
+void ExtractPTATParameters(uint16_t *eeData, paramsMLX90640 *mlx90640)
+{
+ float KvPTAT;
+ float KtPTAT;
+ int16_t vPTAT25;
+ float alphaPTAT;
+
+ KvPTAT = (eeData[50] & 0xFC00) >> 10;
+ if(KvPTAT > 31)
+ {
+ KvPTAT = KvPTAT - 64;
+ }
+ KvPTAT = KvPTAT/4096;
+
+ KtPTAT = eeData[50] & 0x03FF;
+ if(KtPTAT > 511)
+ {
+ KtPTAT = KtPTAT - 1024;
+ }
+ KtPTAT = KtPTAT/8;
+
+ vPTAT25 = eeData[49];
+
+ alphaPTAT = (eeData[16] & 0xF000) / pow(2, (double)14) + 8.0f;
+
+ mlx90640->KvPTAT = KvPTAT;
+ mlx90640->KtPTAT = KtPTAT;
+ mlx90640->vPTAT25 = vPTAT25;
+ mlx90640->alphaPTAT = alphaPTAT;
+}
+
+//------------------------------------------------------------------------------
+
+void ExtractGainParameters(uint16_t *eeData, paramsMLX90640 *mlx90640)
+{
+ int16_t gainEE;
+
+ gainEE = eeData[48];
+ if(gainEE > 32767)
+ {
+ gainEE = gainEE -65536;
+ }
+
+ mlx90640->gainEE = gainEE;
+}
+
+//------------------------------------------------------------------------------
+
+void ExtractTgcParameters(uint16_t *eeData, paramsMLX90640 *mlx90640)
+{
+ float tgc;
+ tgc = eeData[60] & 0x00FF;
+ if(tgc > 127)
+ {
+ tgc = tgc - 256;
+ }
+ tgc = tgc / 32.0f;
+
+ mlx90640->tgc = tgc;
+}
+
+//------------------------------------------------------------------------------
+
+void ExtractResolutionParameters(uint16_t *eeData, paramsMLX90640 *mlx90640)
+{
+ uint8_t resolutionEE;
+ resolutionEE = (eeData[56] & 0x3000) >> 12;
+
+ mlx90640->resolutionEE = resolutionEE;
+}
+
+//------------------------------------------------------------------------------
+
+void ExtractKsTaParameters(uint16_t *eeData, paramsMLX90640 *mlx90640)
+{
+ float KsTa;
+ KsTa = (eeData[60] & 0xFF00) >> 8;
+ if(KsTa > 127)
+ {
+ KsTa = KsTa -256;
+ }
+ KsTa = KsTa / 8192.0f;
+
+ mlx90640->KsTa = KsTa;
+}
+
+//------------------------------------------------------------------------------
+
+void ExtractKsToParameters(uint16_t *eeData, paramsMLX90640 *mlx90640)
+{
+ int KsToScale;
+ int8_t step;
+
+ step = ((eeData[63] & 0x3000) >> 12) * 10;
+
+ mlx90640->ct[0] = -40;
+ mlx90640->ct[1] = 0;
+ mlx90640->ct[2] = (eeData[63] & 0x00F0) >> 4;
+ mlx90640->ct[3] = (eeData[63] & 0x0F00) >> 8;
+
+ mlx90640->ct[2] = mlx90640->ct[2]*step;
+ mlx90640->ct[3] = mlx90640->ct[2] + mlx90640->ct[3]*step;
+ mlx90640->ct[4] = 400;
+
+ KsToScale = (eeData[63] & 0x000F) + 8;
+ KsToScale = 1 << KsToScale;
+
+ mlx90640->ksTo[0] = eeData[61] & 0x00FF;
+ mlx90640->ksTo[1] = (eeData[61] & 0xFF00) >> 8;
+ mlx90640->ksTo[2] = eeData[62] & 0x00FF;
+ mlx90640->ksTo[3] = (eeData[62] & 0xFF00) >> 8;
+
+ for(int i = 0; i < 4; i++)
+ {
+ if(mlx90640->ksTo[i] > 127)
+ {
+ mlx90640->ksTo[i] = mlx90640->ksTo[i] - 256;
+ }
+ mlx90640->ksTo[i] = mlx90640->ksTo[i] / KsToScale;
+ }
+
+ mlx90640->ksTo[4] = -0.0002;
+}
+
+//------------------------------------------------------------------------------
+
+void ExtractAlphaParameters(uint16_t *eeData, paramsMLX90640 *mlx90640)
+{
+ int accRow[24];
+ int accColumn[32];
+ int p = 0;
+ int alphaRef;
+ uint8_t alphaScale;
+ uint8_t accRowScale;
+ uint8_t accColumnScale;
+ uint8_t accRemScale;
+ float alphaTemp[768];
+ float temp;
+
+
+ accRemScale = eeData[32] & 0x000F;
+ accColumnScale = (eeData[32] & 0x00F0) >> 4;
+ accRowScale = (eeData[32] & 0x0F00) >> 8;
+ alphaScale = ((eeData[32] & 0xF000) >> 12) + 30;
+ alphaRef = eeData[33];
+
+ for(int i = 0; i < 6; i++)
+ {
+ p = i * 4;
+ accRow[p + 0] = (eeData[34 + i] & 0x000F);
+ accRow[p + 1] = (eeData[34 + i] & 0x00F0) >> 4;
+ accRow[p + 2] = (eeData[34 + i] & 0x0F00) >> 8;
+ accRow[p + 3] = (eeData[34 + i] & 0xF000) >> 12;
+ }
+
+ for(int i = 0; i < 24; i++)
+ {
+ if (accRow[i] > 7)
+ {
+ accRow[i] = accRow[i] - 16;
+ }
+ }
+
+ for(int i = 0; i < 8; i++)
+ {
+ p = i * 4;
+ accColumn[p + 0] = (eeData[40 + i] & 0x000F);
+ accColumn[p + 1] = (eeData[40 + i] & 0x00F0) >> 4;
+ accColumn[p + 2] = (eeData[40 + i] & 0x0F00) >> 8;
+ accColumn[p + 3] = (eeData[40 + i] & 0xF000) >> 12;
+ }
+
+ for(int i = 0; i < 32; i ++)
+ {
+ if (accColumn[i] > 7)
+ {
+ accColumn[i] = accColumn[i] - 16;
+ }
+ }
+
+ for(int i = 0; i < 24; i++)
+ {
+ for(int j = 0; j < 32; j ++)
+ {
+ p = 32 * i +j;
+ alphaTemp[p] = (eeData[64 + p] & 0x03F0) >> 4;
+ if (alphaTemp[p] > 31)
+ {
+ alphaTemp[p] = alphaTemp[p] - 64;
+ }
+ alphaTemp[p] = alphaTemp[p]*(1 << accRemScale);
+ alphaTemp[p] = (alphaRef + (accRow[i] << accRowScale) + (accColumn[j] << accColumnScale) + alphaTemp[p]);
+ alphaTemp[p] = alphaTemp[p] / pow(2,(double)alphaScale);
+ alphaTemp[p] = alphaTemp[p] - mlx90640->tgc * (mlx90640->cpAlpha[0] + mlx90640->cpAlpha[1])/2;
+ alphaTemp[p] = SCALEALPHA/alphaTemp[p];
+ }
+ }
+
+ temp = alphaTemp[0];
+ for(int i = 1; i < 768; i++)
+ {
+ if (alphaTemp[i] > temp)
+ {
+ temp = alphaTemp[i];
+ }
+ }
+
+ alphaScale = 0;
+ while(temp < 32767.4)
+ {
+ temp = temp*2;
+ alphaScale = alphaScale + 1;
+ }
+
+ for(int i = 0; i < 768; i++)
+ {
+ temp = alphaTemp[i] * pow(2,(double)alphaScale);
+ mlx90640->alpha[i] = (temp + 0.5);
+
+ }
+
+ mlx90640->alphaScale = alphaScale;
+
+}
+
+//------------------------------------------------------------------------------
+
+void ExtractOffsetParameters(uint16_t *eeData, paramsMLX90640 *mlx90640)
+{
+ int occRow[24];
+ int occColumn[32];
+ int p = 0;
+ int16_t offsetRef;
+ uint8_t occRowScale;
+ uint8_t occColumnScale;
+ uint8_t occRemScale;
+
+
+ occRemScale = (eeData[16] & 0x000F);
+ occColumnScale = (eeData[16] & 0x00F0) >> 4;
+ occRowScale = (eeData[16] & 0x0F00) >> 8;
+ offsetRef = eeData[17];
+ if (offsetRef > 32767)
+ {
+ offsetRef = offsetRef - 65536;
+ }
+
+ for(int i = 0; i < 6; i++)
+ {
+ p = i * 4;
+ occRow[p + 0] = (eeData[18 + i] & 0x000F);
+ occRow[p + 1] = (eeData[18 + i] & 0x00F0) >> 4;
+ occRow[p + 2] = (eeData[18 + i] & 0x0F00) >> 8;
+ occRow[p + 3] = (eeData[18 + i] & 0xF000) >> 12;
+ }
+
+ for(int i = 0; i < 24; i++)
+ {
+ if (occRow[i] > 7)
+ {
+ occRow[i] = occRow[i] - 16;
+ }
+ }
+
+ for(int i = 0; i < 8; i++)
+ {
+ p = i * 4;
+ occColumn[p + 0] = (eeData[24 + i] & 0x000F);
+ occColumn[p + 1] = (eeData[24 + i] & 0x00F0) >> 4;
+ occColumn[p + 2] = (eeData[24 + i] & 0x0F00) >> 8;
+ occColumn[p + 3] = (eeData[24 + i] & 0xF000) >> 12;
+ }
+
+ for(int i = 0; i < 32; i ++)
+ {
+ if (occColumn[i] > 7)
+ {
+ occColumn[i] = occColumn[i] - 16;
+ }
+ }
+
+ for(int i = 0; i < 24; i++)
+ {
+ for(int j = 0; j < 32; j ++)
+ {
+ p = 32 * i +j;
+ mlx90640->offset[p] = (eeData[64 + p] & 0xFC00) >> 10;
+ if (mlx90640->offset[p] > 31)
+ {
+ mlx90640->offset[p] = mlx90640->offset[p] - 64;
+ }
+ mlx90640->offset[p] = mlx90640->offset[p]*(1 << occRemScale);
+ mlx90640->offset[p] = (offsetRef + (occRow[i] << occRowScale) + (occColumn[j] << occColumnScale) + mlx90640->offset[p]);
+ }
+ }
+}
+
+//------------------------------------------------------------------------------
+
+void ExtractKtaPixelParameters(uint16_t *eeData, paramsMLX90640 *mlx90640)
+{
+ int p = 0;
+ int8_t KtaRC[4];
+ int8_t KtaRoCo;
+ int8_t KtaRoCe;
+ int8_t KtaReCo;
+ int8_t KtaReCe;
+ uint8_t ktaScale1;
+ uint8_t ktaScale2;
+ uint8_t split;
+ float ktaTemp[768];
+ float temp;
+
+ KtaRoCo = (eeData[54] & 0xFF00) >> 8;
+ if (KtaRoCo > 127)
+ {
+ KtaRoCo = KtaRoCo - 256;
+ }
+ KtaRC[0] = KtaRoCo;
+
+ KtaReCo = (eeData[54] & 0x00FF);
+ if (KtaReCo > 127)
+ {
+ KtaReCo = KtaReCo - 256;
+ }
+ KtaRC[2] = KtaReCo;
+
+ KtaRoCe = (eeData[55] & 0xFF00) >> 8;
+ if (KtaRoCe > 127)
+ {
+ KtaRoCe = KtaRoCe - 256;
+ }
+ KtaRC[1] = KtaRoCe;
+
+ KtaReCe = (eeData[55] & 0x00FF);
+ if (KtaReCe > 127)
+ {
+ KtaReCe = KtaReCe - 256;
+ }
+ KtaRC[3] = KtaReCe;
+
+ ktaScale1 = ((eeData[56] & 0x00F0) >> 4) + 8;
+ ktaScale2 = (eeData[56] & 0x000F);
+
+ for(int i = 0; i < 24; i++)
+ {
+ for(int j = 0; j < 32; j ++)
+ {
+ p = 32 * i +j;
+ split = 2*(p/32 - (p/64)*2) + p%2;
+ ktaTemp[p] = (eeData[64 + p] & 0x000E) >> 1;
+ if (ktaTemp[p] > 3)
+ {
+ ktaTemp[p] = ktaTemp[p] - 8;
+ }
+ ktaTemp[p] = ktaTemp[p] * (1 << ktaScale2);
+ ktaTemp[p] = KtaRC[split] + ktaTemp[p];
+ ktaTemp[p] = ktaTemp[p] / pow(2,(double)ktaScale1);
+ //ktaTemp[p] = ktaTemp[p] * mlx90640->offset[p];
+ }
+ }
+
+ temp = fabs(ktaTemp[0]);
+ for(int i = 1; i < 768; i++)
+ {
+ if (fabs(ktaTemp[i]) > temp)
+ {
+ temp = fabs(ktaTemp[i]);
+ }
+ }
+
+ ktaScale1 = 0;
+ while(temp < 63.4)
+ {
+ temp = temp*2;
+ ktaScale1 = ktaScale1 + 1;
+ }
+
+ for(int i = 0; i < 768; i++)
+ {
+ temp = ktaTemp[i] * pow(2,(double)ktaScale1);
+ if (temp < 0)
+ {
+ mlx90640->kta[i] = (temp - 0.5);
+ }
+ else
+ {
+ mlx90640->kta[i] = (temp + 0.5);
+ }
+
+ }
+
+ mlx90640->ktaScale = ktaScale1;
+}
+
+
+//------------------------------------------------------------------------------
+
+void ExtractKvPixelParameters(uint16_t *eeData, paramsMLX90640 *mlx90640)
+{
+ int p = 0;
+ int8_t KvT[4];
+ int8_t KvRoCo;
+ int8_t KvRoCe;
+ int8_t KvReCo;
+ int8_t KvReCe;
+ uint8_t kvScale;
+ uint8_t split;
+ float kvTemp[768];
+ float temp;
+
+ KvRoCo = (eeData[52] & 0xF000) >> 12;
+ if (KvRoCo > 7)
+ {
+ KvRoCo = KvRoCo - 16;
+ }
+ KvT[0] = KvRoCo;
+
+ KvReCo = (eeData[52] & 0x0F00) >> 8;
+ if (KvReCo > 7)
+ {
+ KvReCo = KvReCo - 16;
+ }
+ KvT[2] = KvReCo;
+
+ KvRoCe = (eeData[52] & 0x00F0) >> 4;
+ if (KvRoCe > 7)
+ {
+ KvRoCe = KvRoCe - 16;
+ }
+ KvT[1] = KvRoCe;
+
+ KvReCe = (eeData[52] & 0x000F);
+ if (KvReCe > 7)
+ {
+ KvReCe = KvReCe - 16;
+ }
+ KvT[3] = KvReCe;
+
+ kvScale = (eeData[56] & 0x0F00) >> 8;
+
+
+ for(int i = 0; i < 24; i++)
+ {
+ for(int j = 0; j < 32; j ++)
+ {
+ p = 32 * i +j;
+ split = 2*(p/32 - (p/64)*2) + p%2;
+ kvTemp[p] = KvT[split];
+ kvTemp[p] = kvTemp[p] / pow(2,(double)kvScale);
+ //kvTemp[p] = kvTemp[p] * mlx90640->offset[p];
+ }
+ }
+
+ temp = fabs(kvTemp[0]);
+ for(int i = 1; i < 768; i++)
+ {
+ if (fabs(kvTemp[i]) > temp)
+ {
+ temp = fabs(kvTemp[i]);
+ }
+ }
+
+ kvScale = 0;
+ while(temp < 63.4)
+ {
+ temp = temp*2;
+ kvScale = kvScale + 1;
+ }
+
+ for(int i = 0; i < 768; i++)
+ {
+ temp = kvTemp[i] * pow(2,(double)kvScale);
+ if (temp < 0)
+ {
+ mlx90640->kv[i] = (temp - 0.5);
+ }
+ else
+ {
+ mlx90640->kv[i] = (temp + 0.5);
+ }
+
+ }
+
+ mlx90640->kvScale = kvScale;
+}
+
+//------------------------------------------------------------------------------
+
+void ExtractCPParameters(uint16_t *eeData, paramsMLX90640 *mlx90640)
+{
+ float alphaSP[2];
+ int16_t offsetSP[2];
+ float cpKv;
+ float cpKta;
+ uint8_t alphaScale;
+ uint8_t ktaScale1;
+ uint8_t kvScale;
+
+ alphaScale = ((eeData[32] & 0xF000) >> 12) + 27;
+
+ offsetSP[0] = (eeData[58] & 0x03FF);
+ if (offsetSP[0] > 511)
+ {
+ offsetSP[0] = offsetSP[0] - 1024;
+ }
+
+ offsetSP[1] = (eeData[58] & 0xFC00) >> 10;
+ if (offsetSP[1] > 31)
+ {
+ offsetSP[1] = offsetSP[1] - 64;
+ }
+ offsetSP[1] = offsetSP[1] + offsetSP[0];
+
+ alphaSP[0] = (eeData[57] & 0x03FF);
+ if (alphaSP[0] > 511)
+ {
+ alphaSP[0] = alphaSP[0] - 1024;
+ }
+ alphaSP[0] = alphaSP[0] / pow(2,(double)alphaScale);
+
+ alphaSP[1] = (eeData[57] & 0xFC00) >> 10;
+ if (alphaSP[1] > 31)
+ {
+ alphaSP[1] = alphaSP[1] - 64;
+ }
+ alphaSP[1] = (1 + alphaSP[1]/128) * alphaSP[0];
+
+ cpKta = (eeData[59] & 0x00FF);
+ if (cpKta > 127)
+ {
+ cpKta = cpKta - 256;
+ }
+ ktaScale1 = ((eeData[56] & 0x00F0) >> 4) + 8;
+ mlx90640->cpKta = cpKta / pow(2,(double)ktaScale1);
+
+ cpKv = (eeData[59] & 0xFF00) >> 8;
+ if (cpKv > 127)
+ {
+ cpKv = cpKv - 256;
+ }
+ kvScale = (eeData[56] & 0x0F00) >> 8;
+ mlx90640->cpKv = cpKv / pow(2,(double)kvScale);
+
+ mlx90640->cpAlpha[0] = alphaSP[0];
+ mlx90640->cpAlpha[1] = alphaSP[1];
+ mlx90640->cpOffset[0] = offsetSP[0];
+ mlx90640->cpOffset[1] = offsetSP[1];
+}
+
+//------------------------------------------------------------------------------
+
+void ExtractCILCParameters(uint16_t *eeData, paramsMLX90640 *mlx90640)
+{
+ float ilChessC[3];
+ uint8_t calibrationModeEE;
+
+ calibrationModeEE = (eeData[10] & 0x0800) >> 4;
+ calibrationModeEE = calibrationModeEE ^ 0x80;
+
+ ilChessC[0] = (eeData[53] & 0x003F);
+ if (ilChessC[0] > 31)
+ {
+ ilChessC[0] = ilChessC[0] - 64;
+ }
+ ilChessC[0] = ilChessC[0] / 16.0f;
+
+ ilChessC[1] = (eeData[53] & 0x07C0) >> 6;
+ if (ilChessC[1] > 15)
+ {
+ ilChessC[1] = ilChessC[1] - 32;
+ }
+ ilChessC[1] = ilChessC[1] / 2.0f;
+
+ ilChessC[2] = (eeData[53] & 0xF800) >> 11;
+ if (ilChessC[2] > 15)
+ {
+ ilChessC[2] = ilChessC[2] - 32;
+ }
+ ilChessC[2] = ilChessC[2] / 8.0f;
+
+ mlx90640->calibrationModeEE = calibrationModeEE;
+ mlx90640->ilChessC[0] = ilChessC[0];
+ mlx90640->ilChessC[1] = ilChessC[1];
+ mlx90640->ilChessC[2] = ilChessC[2];
+}
+
+//------------------------------------------------------------------------------
+
+int ExtractDeviatingPixels(uint16_t *eeData, paramsMLX90640 *mlx90640)
+{
+ uint16_t pixCnt = 0;
+ uint16_t brokenPixCnt = 0;
+ uint16_t outlierPixCnt = 0;
+ int warn = 0;
+ int i;
+
+ for(pixCnt = 0; pixCnt<5; pixCnt++)
+ {
+ mlx90640->brokenPixels[pixCnt] = 0xFFFF;
+ mlx90640->outlierPixels[pixCnt] = 0xFFFF;
+ }
+
+ pixCnt = 0;
+ while (pixCnt < 768 && brokenPixCnt < 5 && outlierPixCnt < 5)
+ {
+ if(eeData[pixCnt+64] == 0)
+ {
+ mlx90640->brokenPixels[brokenPixCnt] = pixCnt;
+ brokenPixCnt = brokenPixCnt + 1;
+ }
+ else if((eeData[pixCnt+64] & 0x0001) != 0)
+ {
+ mlx90640->outlierPixels[outlierPixCnt] = pixCnt;
+ outlierPixCnt = outlierPixCnt + 1;
+ }
+
+ pixCnt = pixCnt + 1;
+
+ }
+
+ if(brokenPixCnt > 4)
+ {
+ warn = -3;
+ }
+ else if(outlierPixCnt > 4)
+ {
+ warn = -4;
+ }
+ else if((brokenPixCnt + outlierPixCnt) > 4)
+ {
+ warn = -5;
+ }
+ else
+ {
+ for(pixCnt=0; pixCntbrokenPixels[pixCnt],mlx90640->brokenPixels[i]);
+ if(warn != 0)
+ {
+ return warn;
+ }
+ }
+ }
+
+ for(pixCnt=0; pixCntoutlierPixels[pixCnt],mlx90640->outlierPixels[i]);
+ if(warn != 0)
+ {
+ return warn;
+ }
+ }
+ }
+
+ for(pixCnt=0; pixCntbrokenPixels[pixCnt],mlx90640->outlierPixels[i]);
+ if(warn != 0)
+ {
+ return warn;
+ }
+ }
+ }
+
+ }
+
+
+ return warn;
+
+}
+
+//------------------------------------------------------------------------------
+
+ int CheckAdjacentPixels(uint16_t pix1, uint16_t pix2)
+ {
+ int pixPosDif;
+
+ pixPosDif = pix1 - pix2;
+ if(pixPosDif > -34 && pixPosDif < -30)
+ {
+ return -6;
+ }
+ if(pixPosDif > -2 && pixPosDif < 2)
+ {
+ return -6;
+ }
+ if(pixPosDif > 30 && pixPosDif < 34)
+ {
+ return -6;
+ }
+
+ return 0;
+ }
+
+//------------------------------------------------------------------------------
+
+float GetMedian(float *values, int n)
+ {
+ float temp;
+
+ for(int i=0; ioutlierPixels[i] || pixel == params->brokenPixels[i])
+ {
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+//------------------------------------------------------------------------------
diff --git a/lib/mlx90640-library/MLX90640_API.h b/lib/mlx90640-library/MLX90640_API.h
new file mode 100644
index 000000000..efbcdff5b
--- /dev/null
+++ b/lib/mlx90640-library/MLX90640_API.h
@@ -0,0 +1,74 @@
+/**
+ * @copyright (C) 2017 Melexis N.V.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+#ifndef _MLX90640_API_H_
+#define _MLX90640_API_H_
+
+#include
+
+#define SCALEALPHA 0.000001
+
+typedef struct
+ {
+ int16_t kVdd;
+ int16_t vdd25;
+ float KvPTAT;
+ float KtPTAT;
+ uint16_t vPTAT25;
+ float alphaPTAT;
+ int16_t gainEE;
+ float tgc;
+ float cpKv;
+ float cpKta;
+ uint8_t resolutionEE;
+ uint8_t calibrationModeEE;
+ float KsTa;
+ float ksTo[5];
+ int16_t ct[5];
+ uint16_t alpha[768];
+ uint8_t alphaScale;
+ int16_t offset[768];
+ int8_t kta[768];
+ uint8_t ktaScale;
+ int8_t kv[768];
+ uint8_t kvScale;
+ float cpAlpha[2];
+ int16_t cpOffset[2];
+ float ilChessC[3];
+ uint16_t brokenPixels[5];
+ uint16_t outlierPixels[5];
+ } paramsMLX90640;
+
+ int MLX90640_DumpEE(uint8_t slaveAddr, uint16_t *eeData);
+ int MLX90640_SynchFrame(uint8_t slaveAddr);
+ // int MLX90640_TriggerMeasurement(uint8_t slaveAddr);
+ int MLX90640_GetFrameData(uint8_t slaveAddr, uint16_t *frameData);
+ int MLX90640_ExtractParameters(uint16_t *eeData, paramsMLX90640 *mlx90640,int _chunk);
+ float MLX90640_GetVdd(uint16_t *frameData, const paramsMLX90640 *params);
+ float MLX90640_GetTa(uint16_t *frameData, const paramsMLX90640 *params);
+ // void MLX90640_GetImage(uint16_t *frameData, const paramsMLX90640 *params, float *result);
+ void MLX90640_CalculateTo(uint16_t *frameData, const paramsMLX90640 *params, float emissivity, float tr, float *result, uint8_t _part);
+ int MLX90640_SetResolution(uint8_t slaveAddr, uint8_t resolution);
+ int MLX90640_GetCurResolution(uint8_t slaveAddr);
+ int MLX90640_SetRefreshRate(uint8_t slaveAddr, uint8_t refreshRate);
+ int MLX90640_GetRefreshRate(uint8_t slaveAddr);
+ int MLX90640_GetSubPageNumber(uint16_t *frameData);
+ int MLX90640_GetCurMode(uint8_t slaveAddr);
+ int MLX90640_SetInterleavedMode(uint8_t slaveAddr);
+ int MLX90640_SetChessMode(uint8_t slaveAddr);
+ void MLX90640_BadPixelsCorrection(uint16_t *pixels, float *to, int mode, paramsMLX90640 *params);
+
+#endif
From a62ff5176e0abd3582e3f1633fc2397ab5f8c1cc Mon Sep 17 00:00:00 2001
From: Staars
Date: Sat, 12 Sep 2020 19:16:36 +0200
Subject: [PATCH 042/148] add mlx90640 to configs
---
I2CDEVICES.md | 3 ++-
tasmota/my_user_config.h | 1 +
tasmota/xdrv_84_MLX90640.ino | 1 +
tools/decode-status.py | 2 +-
4 files changed, 5 insertions(+), 2 deletions(-)
diff --git a/I2CDEVICES.md b/I2CDEVICES.md
index 457116347..5d60c5fb4 100644
--- a/I2CDEVICES.md
+++ b/I2CDEVICES.md
@@ -74,4 +74,5 @@ Index | Define | Driver | Device | Address(es) | Description
49 | USE_VEML6075 | xsns_70 | VEML6075 | 0x10 | UVA/UVB/UVINDEX Sensor
50 | USE_VEML7700 | xsns_71 | VEML7700 | 0x10 | Ambient light intensity sensor
51 | USE_MCP9808 | xsns_72 | MCP9808 | 0x18 - 0x1F | Temperature sensor
- 52 | USE_HP303B | xsns_73 | HP303B | 0x76 - 0x77 | Pressure and temperature sensor
\ No newline at end of file
+ 52 | USE_HP303B | xsns_73 | HP303B | 0x76 - 0x77 | Pressure and temperature sensor
+ 53 | USE_MLX90640 | xdrv_84 | MLX90640 | 0x33 | IR array temperature sensor
\ No newline at end of file
diff --git a/tasmota/my_user_config.h b/tasmota/my_user_config.h
index 5f94c5fc0..e7fd061f5 100644
--- a/tasmota/my_user_config.h
+++ b/tasmota/my_user_config.h
@@ -557,6 +557,7 @@
// #define USE_VEML7700 // [I2cDriver50] Enable VEML7700 Ambient Light sensor (I2C addresses 0x10) (+4k5 code)
// #define USE_MCP9808 // [I2cDriver51] Enable MCP9808 temperature sensor (I2C addresses 0x18 - 0x1F) (+0k9 code)
// #define USE_HP303B // [I2cDriver52] Enable HP303B temperature and pressure sensor (I2C address 0x76 or 0x77) (+6k2 code)
+// #define USE_MLX90640 // Enable MLX90640 IR array temperature sensor (I2C address 0x33) (+4k9 code)
// #define USE_DISPLAY // Add I2C Display Support (+2k code)
#define USE_DISPLAY_MODES1TO5 // Enable display mode 1 to 5 in addition to mode 0
diff --git a/tasmota/xdrv_84_MLX90640.ino b/tasmota/xdrv_84_MLX90640.ino
index 808b8f9d2..a1016c917 100644
--- a/tasmota/xdrv_84_MLX90640.ino
+++ b/tasmota/xdrv_84_MLX90640.ino
@@ -34,6 +34,7 @@
\*********************************************************************************************/
#define XDRV_84 84
+#define XI2C_53 53 // See I2CDEVICES.md
#include
const char MLX90640type[] PROGMEM = "MLX90640";
diff --git a/tools/decode-status.py b/tools/decode-status.py
index 876c9eb91..3758b528c 100755
--- a/tools/decode-status.py
+++ b/tools/decode-status.py
@@ -220,7 +220,7 @@ a_features = [[
"USE_WINDMETER","USE_OPENTHERM","USE_THERMOSTAT","USE_VEML6075",
"USE_VEML7700","USE_MCP9808","USE_BL0940","USE_TELEGRAM",
"USE_HP303B","USE_TCP_BRIDGE","USE_TELEINFO","USE_LMT01",
- "USE_PROMETHEUS","USE_IEM3000","USE_DYP","",
+ "USE_PROMETHEUS","USE_IEM3000","USE_DYP","USE_MLX90640",
"","","","",
"","","USE_ETHERNET","USE_WEBCAM"
],[
From 9b67090ee8291c273b01890963ff279429a86f8b Mon Sep 17 00:00:00 2001
From: Staars
Date: Sat, 12 Sep 2020 19:23:10 +0200
Subject: [PATCH 043/148] add I2cDriver53
---
tasmota/my_user_config.h | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/tasmota/my_user_config.h b/tasmota/my_user_config.h
index e7fd061f5..e77efe401 100644
--- a/tasmota/my_user_config.h
+++ b/tasmota/my_user_config.h
@@ -557,7 +557,7 @@
// #define USE_VEML7700 // [I2cDriver50] Enable VEML7700 Ambient Light sensor (I2C addresses 0x10) (+4k5 code)
// #define USE_MCP9808 // [I2cDriver51] Enable MCP9808 temperature sensor (I2C addresses 0x18 - 0x1F) (+0k9 code)
// #define USE_HP303B // [I2cDriver52] Enable HP303B temperature and pressure sensor (I2C address 0x76 or 0x77) (+6k2 code)
-// #define USE_MLX90640 // Enable MLX90640 IR array temperature sensor (I2C address 0x33) (+4k9 code)
+// #define USE_MLX90640 // [I2cDriver53] Enable MLX90640 IR array temperature sensor (I2C address 0x33) (+4k9 code)
// #define USE_DISPLAY // Add I2C Display Support (+2k code)
#define USE_DISPLAY_MODES1TO5 // Enable display mode 1 to 5 in addition to mode 0
From b0b35a953d8ae0f081e0ab7caa7b5ab251cc6bc0 Mon Sep 17 00:00:00 2001
From: Stephan Hadinger
Date: Sun, 13 Sep 2020 16:11:20 +0200
Subject: [PATCH 044/148] Zigbee minor fixes
---
tasmota/xdrv_23_zigbee_5_converters.ino | 5 +++++
tasmota/xdrv_23_zigbee_8_parsers.ino | 2 +-
tasmota/xdrv_23_zigbee_A_impl.ino | 13 +++++++++----
3 files changed, 15 insertions(+), 5 deletions(-)
diff --git a/tasmota/xdrv_23_zigbee_5_converters.ino b/tasmota/xdrv_23_zigbee_5_converters.ino
index 542aa3a41..edcdb3135 100644
--- a/tasmota/xdrv_23_zigbee_5_converters.ino
+++ b/tasmota/xdrv_23_zigbee_5_converters.ino
@@ -1080,6 +1080,11 @@ void ZCLFrame::generateSyntheticAttributes(Z_attribute_list& attr_list) {
uint32_t ccccaaaa = (attr.key.id.cluster << 16) | attr.key.id.attr_id;
switch (ccccaaaa) { // 0xccccaaaa . c=cluster, a=attribute
+ case 0x00010020: // BatteryVoltage
+ if (attr_list.countAttribute(0x0001,0x0021) == 0) { // if it does not already contain BatteryPercentage
+ uint32_t mv = attr.getUInt()*100;
+ attr_list.addAttribute(0x0001, 0x0021).setUInt(toPercentageCR2032(mv) * 2);
+ }
case 0x0000FF01:
syntheticAqaraSensor(attr_list, attr);
break;
diff --git a/tasmota/xdrv_23_zigbee_8_parsers.ino b/tasmota/xdrv_23_zigbee_8_parsers.ino
index 788c72e88..c7585134b 100644
--- a/tasmota/xdrv_23_zigbee_8_parsers.ino
+++ b/tasmota/xdrv_23_zigbee_8_parsers.ino
@@ -1774,7 +1774,7 @@ void Z_AutoResponder(uint16_t srcaddr, uint16_t cluster, uint8_t endpoint, const
for (uint32_t i=0; i()) {
+ // value is an array []
const JsonArray& attr_arr = val_attr.as();
attrs_len = attr_arr.size() * attr_item_len;
attrs = (uint8_t*) calloc(attrs_len, 1);
@@ -569,6 +570,7 @@ void ZbSendRead(const JsonVariant &val_attr, uint16_t device, uint16_t groupaddr
i += attr_item_len - 2 - attr_item_offset; // normally 0
}
} else if (val_attr.is()) {
+ // value is an object {}
const JsonObject& attr_obj = val_attr.as();
attrs_len = attr_obj.size() * attr_item_len;
attrs = (uint8_t*) calloc(attrs_len, 1);
@@ -619,10 +621,13 @@ void ZbSendRead(const JsonVariant &val_attr, uint16_t device, uint16_t groupaddr
attrs_len = actual_attr_len;
} else {
- attrs_len = attr_item_len;
- attrs = (uint8_t*) calloc(attrs_len, 1);
- attrs[0 + attr_item_offset] = val & 0xFF; // little endian
- attrs[1 + attr_item_offset] = val >> 8;
+ // value is a literal
+ if (0xFFFF != cluster) {
+ attrs_len = attr_item_len;
+ attrs = (uint8_t*) calloc(attrs_len, 1);
+ attrs[0 + attr_item_offset] = val & 0xFF; // little endian
+ attrs[1 + attr_item_offset] = val >> 8;
+ }
}
if (attrs_len > 0) {
From 9aa18c23f0d123b16704631a74a0da3160a2daee Mon Sep 17 00:00:00 2001
From: Theo Arends <11044339+arendst@users.noreply.github.com>
Date: Mon, 14 Sep 2020 11:49:33 +0200
Subject: [PATCH 045/148] Add support for MLX90640
Add support for MLX90640 IR array temperature sensor by Christian Baars
---
RELEASENOTES.md | 4 +++-
tasmota/CHANGELOG.md | 1 +
tasmota/my_user_config.h | 2 +-
tasmota/support_features.ino | 5 +++--
...rv_84_MLX90640.ino => xdrv_43_mlx90640.ino} | 18 +++++++++---------
5 files changed, 17 insertions(+), 13 deletions(-)
rename tasmota/{xdrv_84_MLX90640.ino => xdrv_43_mlx90640.ino} (98%)
diff --git a/RELEASENOTES.md b/RELEASENOTES.md
index 796435d03..3e4973403 100644
--- a/RELEASENOTES.md
+++ b/RELEASENOTES.md
@@ -47,7 +47,7 @@ The following binary downloads have been compiled with ESP8266/Arduino library c
- **tasmota-zbbridge.bin** = The dedicated Sonoff Zigbee Bridge version.
- **tasmota-minimal.bin** = The Minimal version allows intermediate OTA uploads to support larger versions and does NOT change any persistent parameter. This version **should NOT be used for initial installation**.
-Binaries for ESP8266 based devices can be downloaded from http://ota.tasmota.com/tasmota/release. Binaries for ESP32 based devices can be downloaded from http://ota.tasmota.com/tasmota32/release. The base links can be used for OTA upgrades like ``OtaUrl http://ota.tasmota.com/tasmota/release/tasmota.bin``
+The attached binaries can also be downloaded from http://ota.tasmota.com/tasmota/release for ESP8266 or http://ota.tasmota.com/tasmota32/release for ESP32. The links can be used for OTA upgrades too like ``OtaUrl http://ota.tasmota.com/tasmota/release/tasmota.bin``
[List](MODULES.md) of embedded modules.
@@ -61,3 +61,5 @@ Binaries for ESP8266 based devices can be downloaded from http://ota.tasmota.com
- Fix crash in ``ZbRestore``
- Add new shutter modes (#9244)
- Add ``#define USE_MQTT_AWS_IOT_LIGHT`` for password based AWS IoT authentication
+- Add Zigbee auto-config when pairing
+- Add support for MLX90640 IR array temperature sensor by Christian Baars
diff --git a/tasmota/CHANGELOG.md b/tasmota/CHANGELOG.md
index d07791a2e..66a086bd6 100644
--- a/tasmota/CHANGELOG.md
+++ b/tasmota/CHANGELOG.md
@@ -9,6 +9,7 @@
- Add new shutter modes (#9244)
- Add ``#define USE_MQTT_AWS_IOT_LIGHT`` for password based AWS IoT authentication
- Add Zigbee auto-config when pairing
+- Add support for MLX90640 IR array temperature sensor by Christian Baars
### 8.5.0 20200907
diff --git a/tasmota/my_user_config.h b/tasmota/my_user_config.h
index d74cecfe5..f2673bda6 100644
--- a/tasmota/my_user_config.h
+++ b/tasmota/my_user_config.h
@@ -558,7 +558,7 @@
// #define USE_VEML7700 // [I2cDriver50] Enable VEML7700 Ambient Light sensor (I2C addresses 0x10) (+4k5 code)
// #define USE_MCP9808 // [I2cDriver51] Enable MCP9808 temperature sensor (I2C addresses 0x18 - 0x1F) (+0k9 code)
// #define USE_HP303B // [I2cDriver52] Enable HP303B temperature and pressure sensor (I2C address 0x76 or 0x77) (+6k2 code)
-// #define USE_MLX90640 // [I2cDriver53] Enable MLX90640 IR array temperature sensor (I2C address 0x33) (+4k9 code)
+// #define USE_MLX90640 // [I2cDriver53] Enable MLX90640 IR array temperature sensor (I2C address 0x33) (+20k code)
// #define USE_DISPLAY // Add I2C Display Support (+2k code)
#define USE_DISPLAY_MODES1TO5 // Enable display mode 1 to 5 in addition to mode 0
diff --git a/tasmota/support_features.ino b/tasmota/support_features.ino
index d7f8328d3..50da89faa 100644
--- a/tasmota/support_features.ino
+++ b/tasmota/support_features.ino
@@ -601,8 +601,9 @@ void GetFeatures(void)
#ifdef USE_I2S_AUDIO
feature6 |= 0x00800000; // xdrv_42_i2s_audio.ino
#endif
-
-// feature6 |= 0x01000000;
+#ifdef USE_MLX90640
+ feature6 |= 0x01000000; // xdrv_43_mlx90640.ino
+#endif
// feature6 |= 0x02000000;
// feature6 |= 0x04000000;
// feature6 |= 0x08000000;
diff --git a/tasmota/xdrv_84_MLX90640.ino b/tasmota/xdrv_43_mlx90640.ino
similarity index 98%
rename from tasmota/xdrv_84_MLX90640.ino
rename to tasmota/xdrv_43_mlx90640.ino
index a1016c917..88832acc6 100644
--- a/tasmota/xdrv_84_MLX90640.ino
+++ b/tasmota/xdrv_43_mlx90640.ino
@@ -1,5 +1,5 @@
/*
- xdrv_84_MLX90640.ino - MLX90640 support for Tasmota
+ xdrv_43_mlx90640.ino - MLX90640 support for Tasmota
Copyright (C) 2020 Christian Baars and Theo Arends
@@ -33,7 +33,7 @@
* MLX90640
\*********************************************************************************************/
-#define XDRV_84 84
+#define XDRV_43 43
#define XI2C_53 53 // See I2CDEVICES.md
#include
@@ -144,7 +144,7 @@ const char HTTP_MLX90640_2a_SNS_COMPRESSED[] PROGMEM = "\x33\xBF\xA0\xB7\x9A\x3E
#else
const char HTTP_MLX90640_2a_SNS[] PROGMEM =
"var line = 0;"
- "setInterval(function() {"
+ "setInterval(function() {"
"rl('ul',line);" // 0 = do NOT force refresh
"},200);"
"function rl(s,v){" //source, value
@@ -209,7 +209,7 @@ const char HTTP_MLX90640_2b_SNS[] PROGMEM =
"for (var j=0;j<32;j++){"
"var y = 239 - Math.floor(map(rA[(i*32)+j],0,40,0,239));" // 40 is max. temp for heat map
// console.log(gPx.data[y],gPx.data[y+1],gPx.data[y+2]);
- "octx.fillStyle = 'rgb(' + gPx.data[(y*4)] + ',' + gPx.data[(y*4)+1] +',' + gPx.data[(y*4)+2] + ')';"
+ "octx.fillStyle = 'rgb(' + gPx.data[(y*4)] + ',' + gPx.data[(y*4)+1] +',' + gPx.data[(y*4)+2] + ')';"
"octx.fillRect(j*1,i*1,1,1);"
"}"
"}"
@@ -254,7 +254,7 @@ const char HTTP_MLX90640_3a_SNS[] PROGMEM =
"rO.deleteContents();"
"for(var p in poi){"
//"console.log('poi:'+ poi[p][0]);"
- "var c=150;"
+ "var c=150;"
"if(eb('poiL').value==p){c=255;}"
// "console.log(c);"
"ctx.fillStyle = 'rgba('+c+','+c+','+c+',0.6)';"
@@ -411,18 +411,18 @@ void MLX90640UpdateGUI(void){
void MLX90640HandleWebGuiResponse(void){
char tmp[(MLX90640_POI_NUM*2)+4];
WebGetArg("ul", tmp, sizeof(tmp)); // update line
- if (strlen(tmp)) {
+ if (strlen(tmp)) {
uint8_t _line = atoi(tmp);
// AddLog_P2(LOG_LEVEL_DEBUG, "MLX90640: send line %u", _line);
float _buf[65];
- if(_line==0){_buf[0]=1000+MLX90640.Ta;} //ambient temperature modulation hack
+ if(_line==0){_buf[0]=1000+MLX90640.Ta;} //ambient temperature modulation hack
else{_buf[0]=(float)_line;}
memcpy((char*)&_buf[1],(char*)&MLX90640.To[_line*64],64*4);
Webserver->send(200,PSTR("application/octet-stream"),(const char*)&_buf,65*4);
return;
}
WebGetArg("up", tmp, sizeof(tmp)); // update POI to browser
- if (strlen(tmp)==1) {
+ if (strlen(tmp)==1) {
Webserver->send(200,PSTR("application/octet-stream"),(const char*)&MLX90640.pois,MLX90640_POI_NUM*2);
return;
}
@@ -591,7 +591,7 @@ void MLX90640Show(uint8_t json)
* Interface
\*********************************************************************************************/
-bool Xdrv84(uint8_t function)
+bool Xdrv43(uint8_t function)
{
bool result = false;
From 26e041ab6efe14da3d918154e9d4d0d87f352198 Mon Sep 17 00:00:00 2001
From: Theo Arends <11044339+arendst@users.noreply.github.com>
Date: Mon, 14 Sep 2020 12:26:32 +0200
Subject: [PATCH 046/148] Fix reset BMP sensors
Fix reset BMP sensors when executing command ``SaveData`` and define USE_DEEPSLEEP enabled (#9300)
---
RELEASENOTES.md | 1 +
tasmota/CHANGELOG.md | 1 +
tasmota/xsns_09_bmp.ino | 20 +++++++++++---------
3 files changed, 13 insertions(+), 9 deletions(-)
diff --git a/RELEASENOTES.md b/RELEASENOTES.md
index 3e4973403..9b3740986 100644
--- a/RELEASENOTES.md
+++ b/RELEASENOTES.md
@@ -59,6 +59,7 @@ The attached binaries can also be downloaded from http://ota.tasmota.com/tasmota
- Fix energy total counters (#9263, #9266)
- Fix crash in ``ZbRestore``
+- Fix reset BMP sensors when executing command ``SaveData`` and define USE_DEEPSLEEP enabled (#9300)
- Add new shutter modes (#9244)
- Add ``#define USE_MQTT_AWS_IOT_LIGHT`` for password based AWS IoT authentication
- Add Zigbee auto-config when pairing
diff --git a/tasmota/CHANGELOG.md b/tasmota/CHANGELOG.md
index 66a086bd6..5c0d36fd2 100644
--- a/tasmota/CHANGELOG.md
+++ b/tasmota/CHANGELOG.md
@@ -6,6 +6,7 @@
- Fix energy total counters (#9263, #9266)
- Fix crash in ``ZbRestore``
+- Fix reset BMP sensors when executing command ``SaveData`` and define USE_DEEPSLEEP enabled (#9300)
- Add new shutter modes (#9244)
- Add ``#define USE_MQTT_AWS_IOT_LIGHT`` for password based AWS IoT authentication
- Add Zigbee auto-config when pairing
diff --git a/tasmota/xsns_09_bmp.ino b/tasmota/xsns_09_bmp.ino
index 9e3ee59b4..8e1bd074a 100644
--- a/tasmota/xsns_09_bmp.ino
+++ b/tasmota/xsns_09_bmp.ino
@@ -613,15 +613,17 @@ void BmpShow(bool json)
void BMP_EnterSleep(void)
{
- for (uint32_t bmp_idx = 0; bmp_idx < bmp_count; bmp_idx++) {
- switch (bmp_sensors[bmp_idx].bmp_type) {
- case BMP180_CHIPID:
- case BMP280_CHIPID:
- case BME280_CHIPID:
- I2cWrite8(bmp_sensors[bmp_idx].bmp_address, BMP_REGISTER_RESET, BMP_CMND_RESET);
- break;
- default:
- break;
+ if (DeepSleepEnabled()) {
+ for (uint32_t bmp_idx = 0; bmp_idx < bmp_count; bmp_idx++) {
+ switch (bmp_sensors[bmp_idx].bmp_type) {
+ case BMP180_CHIPID:
+ case BMP280_CHIPID:
+ case BME280_CHIPID:
+ I2cWrite8(bmp_sensors[bmp_idx].bmp_address, BMP_REGISTER_RESET, BMP_CMND_RESET);
+ break;
+ default:
+ break;
+ }
}
}
}
From bf115647c314e94242a6eaafd080e08911b4c3bb Mon Sep 17 00:00:00 2001
From: Theo Arends <11044339+arendst@users.noreply.github.com>
Date: Mon, 14 Sep 2020 16:14:58 +0200
Subject: [PATCH 047/148] Prep 8-bit GPIO to 16-bit GPIO change
Prep 8-bit GPIO to 16-bit GPIO change - phase 1
---
tasmota/tasmota_template.h | 2207 ++++++++++++++++++++----------------
1 file changed, 1215 insertions(+), 992 deletions(-)
diff --git a/tasmota/tasmota_template.h b/tasmota/tasmota_template.h
index 32a914f8f..944165eef 100644
--- a/tasmota/tasmota_template.h
+++ b/tasmota/tasmota_template.h
@@ -254,6 +254,229 @@ enum ProgramSelectablePins {
GPIO_USER, // User configurable needs to be 255
GPIO_MAX };
+/*
+// Indexed by UserSelectablePins to convert legacy (8-bit) GPIOs
+const uint16_t kGpioConvert[] PROGMEM = {
+ GPIO_NONE,
+ AGPIO(GPIO_DHT11), // DHT11
+ AGPIO(GPIO_DHT22), // DHT21, DHT22, AM2301, AM2302, AM2321
+ AGPIO(GPIO_SI7021), // iTead SI7021
+ AGPIO(GPIO_DSB), // Single wire DS18B20 or DS18S20
+ AGPIO(GPIO_I2C_SCL), // I2C SCL
+ AGPIO(GPIO_I2C_SDA), // I2C SDA
+ AGPIO(GPIO_WS2812), // WS2812 Led string
+ AGPIO(GPIO_IRSEND), // IR remote
+ AGPIO(GPIO_SWT1), // Switch
+ AGPIO(GPIO_SWT1) +1,
+ AGPIO(GPIO_SWT1) +2,
+ AGPIO(GPIO_SWT1) +3,
+ AGPIO(GPIO_SWT1) +4,
+ AGPIO(GPIO_SWT1) +5,
+ AGPIO(GPIO_SWT1) +6,
+ AGPIO(GPIO_SWT1) +7,
+ AGPIO(GPIO_KEY1), // Button
+ AGPIO(GPIO_KEY1) +1,
+ AGPIO(GPIO_KEY1) +2,
+ AGPIO(GPIO_KEY1) +3,
+ AGPIO(GPIO_REL1), // Relay
+ AGPIO(GPIO_REL1) +1,
+ AGPIO(GPIO_REL1) +2,
+ AGPIO(GPIO_REL1) +3,
+ AGPIO(GPIO_REL1) +4,
+ AGPIO(GPIO_REL1) +5,
+ AGPIO(GPIO_REL1) +6,
+ AGPIO(GPIO_REL1) +7,
+ AGPIO(GPIO_REL1_INV), // Relay inverted
+ AGPIO(GPIO_REL1_INV) +1,
+ AGPIO(GPIO_REL1_INV) +2,
+ AGPIO(GPIO_REL1_INV) +3,
+ AGPIO(GPIO_REL1_INV) +4,
+ AGPIO(GPIO_REL1_INV) +5,
+ AGPIO(GPIO_REL1_INV) +6,
+ AGPIO(GPIO_REL1_INV) +7,
+ AGPIO(GPIO_PWM1), // PWM
+ AGPIO(GPIO_PWM1) +1,
+ AGPIO(GPIO_PWM1) +2,
+ AGPIO(GPIO_PWM1) +3,
+ AGPIO(GPIO_PWM1) +4,
+ AGPIO(GPIO_CNTR1), // Counter
+ AGPIO(GPIO_CNTR1) +1,
+ AGPIO(GPIO_CNTR1) +2,
+ AGPIO(GPIO_CNTR1) +3,
+ AGPIO(GPIO_PWM1_INV), // PWM inverted
+ AGPIO(GPIO_PWM1_INV) +1,
+ AGPIO(GPIO_PWM1_INV) +2,
+ AGPIO(GPIO_PWM1_INV) +3,
+ AGPIO(GPIO_PWM1_INV) +4,
+ AGPIO(GPIO_IRRECV), // IR receive
+ AGPIO(GPIO_LED1), // Led
+ AGPIO(GPIO_LED1) +1,
+ AGPIO(GPIO_LED1) +2,
+ AGPIO(GPIO_LED1) +3,
+ AGPIO(GPIO_LED1_INV), // Led inverted
+ AGPIO(GPIO_LED1_INV) +1,
+ AGPIO(GPIO_LED1_INV) +2,
+ AGPIO(GPIO_LED1_INV) +3,
+ AGPIO(GPIO_MHZ_TXD), // MH-Z19 Serial interface
+ AGPIO(GPIO_MHZ_RXD),
+ AGPIO(GPIO_PZEM0XX_TX), // PZEM0XX Serial interface
+ AGPIO(GPIO_PZEM004_RX), // PZEM004T Serial interface
+ AGPIO(GPIO_SAIR_TX), // SenseAir Serial interface
+ AGPIO(GPIO_SAIR_RX), // SenseAir Serial interface
+ AGPIO(GPIO_SPI_CS), // SPI Chip Select
+ AGPIO(GPIO_SPI_DC), // SPI Data Direction
+ AGPIO(GPIO_BACKLIGHT), // Display backlight control
+ AGPIO(GPIO_PMS5003_RX), // Plantower PMS5003 Serial interface
+ AGPIO(GPIO_SDS0X1_RX), // Nova Fitness SDS011 Serial interface
+ AGPIO(GPIO_SBR_TX), // Serial Bridge Serial interface
+ AGPIO(GPIO_SBR_RX), // Serial Bridge Serial interface
+ AGPIO(GPIO_SR04_TRIG), // SR04 Tri/TXgger pin
+ AGPIO(GPIO_SR04_ECHO), // SR04 Ech/RXo pin
+ AGPIO(GPIO_SDM120_TX), // SDM120 Serial interface
+ AGPIO(GPIO_SDM120_RX), // SDM120 Serial interface
+ AGPIO(GPIO_SDM630_TX), // SDM630 Serial interface
+ AGPIO(GPIO_SDM630_RX), // SDM630 Serial interface
+ AGPIO(GPIO_TM16CLK), // TM1638 Clock
+ AGPIO(GPIO_TM16DIO), // TM1638 Data I/O
+ AGPIO(GPIO_TM16STB), // TM1638 Strobe
+ AGPIO(GPIO_SWT1_NP), // Switch no pullup
+ AGPIO(GPIO_SWT1_NP) +1,
+ AGPIO(GPIO_SWT1_NP) +2,
+ AGPIO(GPIO_SWT1_NP) +3,
+ AGPIO(GPIO_SWT1_NP) +4,
+ AGPIO(GPIO_SWT1_NP) +5,
+ AGPIO(GPIO_SWT1_NP) +6,
+ AGPIO(GPIO_SWT1_NP) +7,
+ AGPIO(GPIO_KEY1_NP), // Button no pullup
+ AGPIO(GPIO_KEY1_NP) +1,
+ AGPIO(GPIO_KEY1_NP) +2,
+ AGPIO(GPIO_KEY1_NP) +3,
+ AGPIO(GPIO_CNTR1_NP), // Counter no pullup
+ AGPIO(GPIO_CNTR1_NP) +1,
+ AGPIO(GPIO_CNTR1_NP) +2,
+ AGPIO(GPIO_CNTR1_NP) +3,
+ AGPIO(GPIO_PZEM016_RX), // PZEM-014,016 Serial Modbus interface
+ AGPIO(GPIO_PZEM017_RX), // PZEM-003,017 Serial Modbus interface
+ AGPIO(GPIO_MP3_DFR562), // RB-DFR-562, DFPlayer Mini MP3 Player Serial interface
+ AGPIO(GPIO_SDS0X1_TX), // Nova Fitness SDS011 Serial interface
+ AGPIO(GPIO_HX711_SCK), // HX711 Load Cell clock
+ AGPIO(GPIO_HX711_DAT), // HX711 Load Cell data
+ AGPIO(GPIO_TX2X_TXD_BLACK), // TX20/TX23 Transmission Pin
+ AGPIO(GPIO_RFSEND), // RF transmitter
+ AGPIO(GPIO_RFRECV), // RF receiver
+ AGPIO(GPIO_TUYA_TX), // Tuya Serial interface
+ AGPIO(GPIO_TUYA_RX), // Tuya Serial interface
+ AGPIO(GPIO_MGC3130_XFER),
+ AGPIO(GPIO_MGC3130_RESET),
+ AGPIO(GPIO_SSPI_MISO), // Software SPI Master Input Client Output
+ AGPIO(GPIO_SSPI_MOSI), // Software SPI Master Output Client Input
+ AGPIO(GPIO_SSPI_SCLK), // Software SPI Serial Clock
+ AGPIO(GPIO_SSPI_CS), // Software SPI Chip Select
+ AGPIO(GPIO_SSPI_DC), // Software SPI Data or Command
+ AGPIO(GPIO_RF_SENSOR), // Rf receiver with sensor decoding
+ AGPIO(GPIO_AZ_TXD), // AZ-Instrument 7798 CO2 datalogger Serial interface
+ AGPIO(GPIO_AZ_RXD), // AZ-Instrument 7798 CO2 datalogger Serial interface
+ AGPIO(GPIO_MAX31855CS), // MAX31855 Serial interface
+ AGPIO(GPIO_MAX31855CLK), // MAX31855 Serial interface
+ AGPIO(GPIO_MAX31855DO), // MAX31855 Serial interface
+ AGPIO(GPIO_KEY1_INV), // Button inverted
+ AGPIO(GPIO_KEY1_INV) +1,
+ AGPIO(GPIO_KEY1_INV) +2,
+ AGPIO(GPIO_KEY1_INV) +3,
+ AGPIO(GPIO_KEY1_INV_NP), // Button inverted no pullup
+ AGPIO(GPIO_KEY1_INV_NP) +1,
+ AGPIO(GPIO_KEY1_INV_NP) +2,
+ AGPIO(GPIO_KEY1_INV_NP) +3,
+ AGPIO(GPIO_NRG_SEL), // HLW8012/HLJ-01 Sel output (1 = Voltage)
+ AGPIO(GPIO_NRG_SEL_INV), // HLW8012/HLJ-01 Sel output (0 = Voltage)
+ AGPIO(GPIO_NRG_CF1), // HLW8012/HLJ-01 CF1 voltage / current
+ AGPIO(GPIO_HLW_CF), // HLW8012 CF power
+ AGPIO(GPIO_HJL_CF), // HJL-01/BL0937 CF power
+ AGPIO(GPIO_MCP39F5_TX), // MCP39F501 Serial interface (Shelly2)
+ AGPIO(GPIO_MCP39F5_RX), // MCP39F501 Serial interface (Shelly2)
+ AGPIO(GPIO_MCP39F5_RST), // MCP39F501 Reset (Shelly2)
+ AGPIO(GPIO_PN532_TXD), // PN532 HSU Tx
+ AGPIO(GPIO_PN532_RXD), // PN532 HSU Rx
+ AGPIO(GPIO_SM16716_CLK), // SM16716 CLOCK
+ AGPIO(GPIO_SM16716_DAT), // SM16716 DATA
+ AGPIO(GPIO_SM16716_SEL), // SM16716 SELECT
+ AGPIO(GPIO_DI), // my92x1 PWM input
+ AGPIO(GPIO_DCKI), // my92x1 CLK input
+ AGPIO(GPIO_CSE7766_TX), // CSE7766 Serial interface (S31 and Pow R2)
+ AGPIO(GPIO_CSE7766_RX), // CSE7766 Serial interface (S31 and Pow R2)
+ AGPIO(GPIO_ARIRFRCV), // AriLux RF Receive input
+ AGPIO(GPIO_TXD), // Serial interface
+ AGPIO(GPIO_RXD), // Serial interface
+ AGPIO(GPIO_ROT1A), // Rotary A Pin
+ AGPIO(GPIO_ROT1B), // Rotary B Pin
+ AGPIO(GPIO_ROT1A) +1, // Rotary A Pin
+ AGPIO(GPIO_ROT1B) +1, // Rotary B Pin
+ AGPIO(GPIO_HRE_CLOCK),
+ AGPIO(GPIO_HRE_DATA),
+ AGPIO(GPIO_ADE7953_IRQ), // ADE7953 IRQ
+ AGPIO(GPIO_LEDLNK), // Link led
+ AGPIO(GPIO_LEDLNK_INV), // Inverted link led
+ AGPIO(GPIO_ARIRFSEL), // Arilux RF Receive input selected
+ AGPIO(GPIO_BUZZER), // Buzzer
+ AGPIO(GPIO_BUZZER_INV), // Inverted buzzer
+ AGPIO(GPIO_OLED_RESET), // OLED Display Reset
+ AGPIO(GPIO_SOLAXX1_TX), // Solax Inverter tx pin
+ AGPIO(GPIO_SOLAXX1_RX), // Solax Inverter rx pin
+ AGPIO(GPIO_ZIGBEE_TX), // Zigbee Serial interface
+ AGPIO(GPIO_ZIGBEE_RX), // Zigbee Serial interface
+ AGPIO(GPIO_RDM6300_RX),
+ AGPIO(GPIO_IBEACON_TX),
+ AGPIO(GPIO_IBEACON_RX),
+ AGPIO(GPIO_A4988_DIR), // A4988 direction pin
+ AGPIO(GPIO_A4988_STP), // A4988 step pin
+ AGPIO(GPIO_A4988_ENA), // A4988 enabled pin
+ AGPIO(GPIO_A4988_MS1), // A4988 microstep pin1
+ AGPIO(GPIO_A4988_MS2), // A4988 microstep pin2
+ AGPIO(GPIO_A4988_MS3), // A4988 microstep pin3
+ AGPIO(GPIO_DDS2382_TX), // DDS2382 Serial interface
+ AGPIO(GPIO_DDS2382_RX), // DDS2382 Serial interface
+ AGPIO(GPIO_DDSU666_TX), // DDSU666 Serial interface
+ AGPIO(GPIO_DDSU666_RX), // DDSU666 Serial interface
+ AGPIO(GPIO_SM2135_CLK), // SM2135 CLOCK
+ AGPIO(GPIO_SM2135_DAT), // SM2135 DATA
+ AGPIO(GPIO_DEEPSLEEP),
+ AGPIO(GPIO_EXS_ENABLE), // EXS MCU Enable
+ AGPIO(GPIO_TASMOTACLIENT_TXD), // Tasmota Client TX
+ AGPIO(GPIO_TASMOTACLIENT_RXD), // Tasmota Client RX
+ AGPIO(GPIO_TASMOTACLIENT_RST), // Tasmota Client Reset
+ AGPIO(GPIO_TASMOTACLIENT_RST_INV), // Tasmota Client Reset Inverted
+ AGPIO(GPIO_HPMA_RX), // Honeywell HPMA115S0 Serial interface
+ AGPIO(GPIO_HPMA_TX), // Honeywell HPMA115S0 Serial interface
+ AGPIO(GPIO_GPS_RX), // GPS serial interface
+ AGPIO(GPIO_GPS_TX), // GPS serial interface
+ AGPIO(GPIO_DSB_OUT), // Pseudo Single wire DS18B20 or DS18S20
+ AGPIO(GPIO_DHT11_OUT), // Pseudo Single wire DHT11, DHT21, DHT22, AM2301, AM2302, AM2321
+ AGPIO(GPIO_HM10_RX), // GPS serial interface
+ AGPIO(GPIO_HM10_TX), // GPS serial interface
+ AGPIO(GPIO_LE01MR_RX), // F7F LE-01MR energy meter rx pin
+ AGPIO(GPIO_LE01MR_TX), // F7F LE-01MR energy meter tx pin
+ AGPIO(GPIO_CC1101_GDO0), // CC1101 pin for RX
+ AGPIO(GPIO_CC1101_GDO2), // CC1101 pin for RX
+ AGPIO(GPIO_HRXL_RX),
+ AGPIO(GPIO_ELECTRIQ_MOODL_TX),
+ AGPIO(GPIO_AS3935), // AS3935 IRQ Pin
+ AGPIO(GPIO_PMS5003_TX), // Plantower PMS5003 Serial interface
+ AGPIO(GPIO_BOILER_OT_RX),
+ AGPIO(GPIO_BOILER_OT_TX),
+ AGPIO(GPIO_WINDMETER_SPEED),
+ AGPIO(GPIO_BL0940_RX), // BL0940 Serial interface
+ AGPIO(GPIO_TCP_TX), // TCP Serial bridge
+ AGPIO(GPIO_TCP_RX), // TCP Serial bridge
+ AGPIO(GPIO_TELEINFO_RX),
+ AGPIO(GPIO_TELEINFO_ENABLE),
+ AGPIO(GPIO_LMT01), // LMT01, count pulses on GPIO
+ AGPIO(GPIO_IEM3000_TX), // IEM3000 Serial interface
+ AGPIO(GPIO_IEM3000_RX), // IEM3000 Serial interface
+ AGPIO(GPIO_ZIGBEE_RST), // Zigbee reset
+ AGPIO(GPIO_DYP_RX)
+};
+*/
+
// Text in webpage Module Parameters and commands GPIOS and GPIO
const char kSensorNames[] PROGMEM =
D_SENSOR_NONE "|"
@@ -1032,1065 +1255,1065 @@ const uint8_t kModuleTemplateList[MAXMODULE] PROGMEM = {
\*********************************************************************************************/
const mytmplt8266 kModules8266[TMP_MAXMODULE_8285] PROGMEM = {
- { // SONOFF_BASIC - Sonoff Basic (ESP8266)
- AGPIO(GPIO_KEY1), // GPIO00 Button
- AGPIO(GPIO_USER), // GPIO01 Serial RXD and Optional sensor
- AGPIO(GPIO_USER), // GPIO02 Only available on newer Sonoff Basic R2 V1
- AGPIO(GPIO_USER), // GPIO03 Serial TXD and Optional sensor
- AGPIO(GPIO_USER), // GPIO04 Optional sensor
- 0, // GPIO05
- // GPIO06 (SD_CLK Flash)
- // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT)
- // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT)
- // GPIO09 (SD_DATA2 Flash QIO or ESP8285)
- // GPIO10 (SD_DATA3 Flash QIO or ESP8285)
- // GPIO11 (SD_CMD Flash)
- AGPIO(GPIO_REL1), // GPIO12 Red Led and Relay (0 = Off, 1 = On)
- AGPIO(GPIO_LED1_INV), // GPIO13 Green Led (0 = On, 1 = Off) - Link and Power status
- AGPIO(GPIO_USER), // GPIO14 Optional sensor
- 0, // GPIO15
- 0, // GPIO16
- 0 // ADC0 Analog input
+ { // SONOFF_BASIC - Sonoff Basic (ESP8266)
+ GPIO_KEY1, // GPIO00 Button
+ GPIO_USER, // GPIO01 Serial RXD and Optional sensor
+ GPIO_USER, // GPIO02 Only available on newer Sonoff Basic R2 V1
+ GPIO_USER, // GPIO03 Serial TXD and Optional sensor
+ GPIO_USER, // GPIO04 Optional sensor
+ 0, // GPIO05
+ // GPIO06 (SD_CLK Flash)
+ // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT)
+ // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT)
+ // GPIO09 (SD_DATA2 Flash QIO or ESP8285)
+ // GPIO10 (SD_DATA3 Flash QIO or ESP8285)
+ // GPIO11 (SD_CMD Flash)
+ GPIO_REL1, // GPIO12 Red Led and Relay (0 = Off, 1 = On)
+ GPIO_LED1_INV, // GPIO13 Green Led (0 = On, 1 = Off) - Link and Power status
+ GPIO_USER, // GPIO14 Optional sensor
+ 0, // GPIO15
+ 0, // GPIO16
+ 0 // ADC0 Analog input
},
- { // SONOFF_SV - Sonoff SV (ESP8266)
- AGPIO(GPIO_KEY1), // GPIO00 Button
- AGPIO(GPIO_USER), // GPIO01 Serial RXD and Optional sensor
+ { // SONOFF_SV - Sonoff SV (ESP8266)
+ GPIO_KEY1, // GPIO00 Button
+ GPIO_USER, // GPIO01 Serial RXD and Optional sensor
0,
- AGPIO(GPIO_USER), // GPIO03 Serial TXD and Optional sensor
- AGPIO(GPIO_USER), // GPIO04 Optional sensor
- AGPIO(GPIO_USER), // GPIO05 Optional sensor
- // GPIO06 (SD_CLK Flash)
- // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT)
- // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT)
- // GPIO09 (SD_DATA2 Flash QIO or ESP8285)
- // GPIO10 (SD_DATA3 Flash QIO or ESP8285)
- // GPIO11 (SD_CMD Flash)
- AGPIO(GPIO_REL1), // GPIO12 Red Led and Relay (0 = Off, 1 = On)
- AGPIO(GPIO_LED1_INV), // GPIO13 Green Led (0 = On, 1 = Off) - Link and Power status
- AGPIO(GPIO_USER), // GPIO14 Optional sensor
+ GPIO_USER, // GPIO03 Serial TXD and Optional sensor
+ GPIO_USER, // GPIO04 Optional sensor
+ GPIO_USER, // GPIO05 Optional sensor
+ // GPIO06 (SD_CLK Flash)
+ // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT)
+ // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT)
+ // GPIO09 (SD_DATA2 Flash QIO or ESP8285)
+ // GPIO10 (SD_DATA3 Flash QIO or ESP8285)
+ // GPIO11 (SD_CMD Flash)
+ GPIO_REL1, // GPIO12 Red Led and Relay (0 = Off, 1 = On)
+ GPIO_LED1_INV, // GPIO13 Green Led (0 = On, 1 = Off) - Link and Power status
+ GPIO_USER, // GPIO14 Optional sensor
0, 0,
- AGPIO(ADC0_USER) // ADC0 Analog input
+ ADC0_USER // ADC0 Analog input
},
- { // SONOFF_DUAL - Sonoff Dual (ESP8266)
- AGPIO(GPIO_USER), // GPIO00 Pad
- AGPIO(GPIO_TXD), // GPIO01 Relay control
+ { // SONOFF_DUAL - Sonoff Dual (ESP8266)
+ GPIO_USER, // GPIO00 Pad
+ GPIO_TXD, // GPIO01 Relay control
0,
- AGPIO(GPIO_RXD), // GPIO03 Relay control
- AGPIO(GPIO_USER), // GPIO04 Optional sensor
+ GPIO_RXD, // GPIO03 Relay control
+ GPIO_USER, // GPIO04 Optional sensor
0,
- // GPIO06 (SD_CLK Flash)
- // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT)
- // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT)
- // GPIO09 (SD_DATA2 Flash QIO or ESP8285)
- // GPIO10 (SD_DATA3 Flash QIO or ESP8285)
- // GPIO11 (SD_CMD Flash)
+ // GPIO06 (SD_CLK Flash)
+ // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT)
+ // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT)
+ // GPIO09 (SD_DATA2 Flash QIO or ESP8285)
+ // GPIO10 (SD_DATA3 Flash QIO or ESP8285)
+ // GPIO11 (SD_CMD Flash)
0,
- AGPIO(GPIO_LED1_INV), // GPIO13 Blue Led (0 = On, 1 = Off) - Link and Power status
- AGPIO(GPIO_USER), // GPIO14 Optional sensor
+ GPIO_LED1_INV, // GPIO13 Blue Led (0 = On, 1 = Off) - Link and Power status
+ GPIO_USER, // GPIO14 Optional sensor
0, 0, 0
},
- { // SONOFF_POW - Sonoff Pow (ESP8266 - HLW8012)
- AGPIO(GPIO_KEY1), // GPIO00 Button
+ { // SONOFF_POW - Sonoff Pow (ESP8266 - HLW8012)
+ GPIO_KEY1, // GPIO00 Button
0, 0, 0, 0,
- AGPIO(GPIO_NRG_SEL), // GPIO05 HLW8012 Sel output (1 = Voltage)
- // GPIO06 (SD_CLK Flash)
- // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT)
- // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT)
- // GPIO09 (SD_DATA2 Flash QIO or ESP8285)
- // GPIO10 (SD_DATA3 Flash QIO or ESP8285)
- // GPIO11 (SD_CMD Flash)
- AGPIO(GPIO_REL1), // GPIO12 Red Led and Relay (0 = Off, 1 = On)
- AGPIO(GPIO_NRG_CF1), // GPIO13 HLW8012 CF1 voltage / current
- AGPIO(GPIO_HLW_CF), // GPIO14 HLW8012 CF power
- AGPIO(GPIO_LED1), // GPIO15 Blue Led (0 = On, 1 = Off) - Link and Power status
+ GPIO_NRG_SEL, // GPIO05 HLW8012 Sel output (1 = Voltage)
+ // GPIO06 (SD_CLK Flash)
+ // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT)
+ // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT)
+ // GPIO09 (SD_DATA2 Flash QIO or ESP8285)
+ // GPIO10 (SD_DATA3 Flash QIO or ESP8285)
+ // GPIO11 (SD_CMD Flash)
+ GPIO_REL1, // GPIO12 Red Led and Relay (0 = Off, 1 = On)
+ GPIO_NRG_CF1, // GPIO13 HLW8012 CF1 voltage / current
+ GPIO_HLW_CF, // GPIO14 HLW8012 CF power
+ GPIO_LED1, // GPIO15 Blue Led (0 = On, 1 = Off) - Link and Power status
0, 0
},
- { // SONOFF_LED - Sonoff LED (ESP8266)
- AGPIO(GPIO_KEY1), // GPIO00 Button
+ { // SONOFF_LED - Sonoff LED (ESP8266)
+ GPIO_KEY1, // GPIO00 Button
0, 0, 0,
- AGPIO(GPIO_USER), // GPIO04 Optional sensor (PWM3 Green)
- AGPIO(GPIO_USER), // GPIO05 Optional sensor (PWM2 Red)
- // GPIO06 (SD_CLK Flash)
- // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT)
- // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT)
- // GPIO09 (SD_DATA2 Flash QIO or ESP8285)
- // GPIO10 (SD_DATA3 Flash QIO or ESP8285)
- // GPIO11 (SD_CMD Flash)
- AGPIO(GPIO_PWM1), // GPIO12 Cold light (PWM0 Cold)
- AGPIO(GPIO_LED1_INV), // GPIO13 Blue Led (0 = On, 1 = Off) - Link and Power status
- AGPIO(GPIO_PWM1) +1, // GPIO14 Warm light (PWM1 Warm)
- AGPIO(GPIO_USER), // GPIO15 Optional sensor (PWM4 Blue)
+ GPIO_USER, // GPIO04 Optional sensor (PWM3 Green)
+ GPIO_USER, // GPIO05 Optional sensor (PWM2 Red)
+ // GPIO06 (SD_CLK Flash)
+ // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT)
+ // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT)
+ // GPIO09 (SD_DATA2 Flash QIO or ESP8285)
+ // GPIO10 (SD_DATA3 Flash QIO or ESP8285)
+ // GPIO11 (SD_CMD Flash)
+ GPIO_PWM1, // GPIO12 Cold light (PWM0 Cold)
+ GPIO_LED1_INV, // GPIO13 Blue Led (0 = On, 1 = Off) - Link and Power status
+ GPIO_PWM2, // GPIO14 Warm light (PWM1 Warm)
+ GPIO_USER, // GPIO15 Optional sensor (PWM4 Blue)
0, 0
},
- { // ELECTRODRAGON - ElectroDragon IoT Relay Board (ESP8266)
- AGPIO(GPIO_KEY1) +1, // GPIO00 Button 2
- AGPIO(GPIO_USER), // GPIO01 Serial RXD and Optional sensor
- AGPIO(GPIO_KEY1), // GPIO02 Button 1
- AGPIO(GPIO_USER), // GPIO03 Serial TXD and Optional sensor
- AGPIO(GPIO_USER), // GPIO04 Optional sensor
- AGPIO(GPIO_USER), // GPIO05 Optional sensor
- // GPIO06 (SD_CLK Flash)
- // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT)
- // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT)
- // GPIO09 (SD_DATA2 Flash QIO or ESP8285)
- // GPIO10 (SD_DATA3 Flash QIO or ESP8285)
- // GPIO11 (SD_CMD Flash)
- AGPIO(GPIO_REL1) +1, // GPIO12 Red Led and Relay 2 (0 = Off, 1 = On)
- AGPIO(GPIO_REL1), // GPIO13 Red Led and Relay 1 (0 = Off, 1 = On)
- AGPIO(GPIO_USER), // GPIO14 Optional sensor
- AGPIO(GPIO_USER), // GPIO15 Optional sensor
- AGPIO(GPIO_LED1), // GPIO16 Green/Blue Led (1 = On, 0 = Off) - Link and Power status
- AGPIO(ADC0_USER) // ADC0 A0 Analog input
+ { // ELECTRODRAGON - ElectroDragon IoT Relay Board (ESP8266)
+ GPIO_KEY2, // GPIO00 Button 2
+ GPIO_USER, // GPIO01 Serial RXD and Optional sensor
+ GPIO_KEY1, // GPIO02 Button 1
+ GPIO_USER, // GPIO03 Serial TXD and Optional sensor
+ GPIO_USER, // GPIO04 Optional sensor
+ GPIO_USER, // GPIO05 Optional sensor
+ // GPIO06 (SD_CLK Flash)
+ // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT)
+ // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT)
+ // GPIO09 (SD_DATA2 Flash QIO or ESP8285)
+ // GPIO10 (SD_DATA3 Flash QIO or ESP8285)
+ // GPIO11 (SD_CMD Flash)
+ GPIO_REL2, // GPIO12 Red Led and Relay 2 (0 = Off, 1 = On)
+ GPIO_REL1, // GPIO13 Red Led and Relay 1 (0 = Off, 1 = On)
+ GPIO_USER, // GPIO14 Optional sensor
+ GPIO_USER, // GPIO15 Optional sensor
+ GPIO_LED1, // GPIO16 Green/Blue Led (1 = On, 0 = Off) - Link and Power status
+ ADC0_USER // ADC0 A0 Analog input
},
- { // EXS_RELAY - ES-Store Latching relay(s) (ESP8266)
- // https://ex-store.de/ESP8266-WiFi-Relay-V31
- // V3.1 Module Pin 1 VCC 3V3, Module Pin 6 GND
- // https://ex-store.de/2-Kanal-WiFi-WLan-Relay-V5-Blackline-fuer-Unterputzmontage
- AGPIO(GPIO_USER), // GPIO00 V3.1 Module Pin 8 - V5.0 Module Pin 4
- AGPIO(GPIO_USER), // GPIO01 UART0_TXD V3.1 Module Pin 2 - V5.0 Module Pin 3
- AGPIO(GPIO_USER), // GPIO02 V3.1 Module Pin 7
- AGPIO(GPIO_USER), // GPIO03 UART0_RXD V3.1 Module Pin 3
- AGPIO(GPIO_USER), // GPIO04 V3.1 Module Pin 10 - V5.0 Module Pin 2
- AGPIO(GPIO_USER), // GPIO05 V3.1 Module Pin 9 - V5.0 Module Pin 1
- // GPIO06 (SD_CLK Flash)
- // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT)
- // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT)
- // GPIO09 (SD_DATA2 Flash QIO or ESP8285)
- // GPIO10 (SD_DATA3 Flash QIO or ESP8285)
- // GPIO11 (SD_CMD Flash)
- AGPIO(GPIO_REL1), // GPIO12 Relay1 ( 1 = Off)
- AGPIO(GPIO_REL1) +1, // GPIO13 Relay1 ( 1 = On)
- AGPIO(GPIO_USER), // GPIO14 V3.1 Module Pin 5 - V5.0 GPIO_REL3_INV Relay2 ( 1 = Off)
- AGPIO(GPIO_LED1), // GPIO15 V5.0 LED1 - Link and Power status
- AGPIO(GPIO_USER), // GPIO16 V3.1 Module Pin 4 - V5.0 GPIO_REL4_INV Relay2 ( 1 = On)
+ { // EXS_RELAY - ES-Store Latching relay(s) (ESP8266)
+ // https://ex-store.de/ESP8266-WiFi-Relay-V31
+ // V3.1 Module Pin 1 VCC 3V3, Module Pin 6 GND
+ // https://ex-store.de/2-Kanal-WiFi-WLan-Relay-V5-Blackline-fuer-Unterputzmontage
+ GPIO_USER, // GPIO00 V3.1 Module Pin 8 - V5.0 Module Pin 4
+ GPIO_USER, // GPIO01 UART0_TXD V3.1 Module Pin 2 - V5.0 Module Pin 3
+ GPIO_USER, // GPIO02 V3.1 Module Pin 7
+ GPIO_USER, // GPIO03 UART0_RXD V3.1 Module Pin 3
+ GPIO_USER, // GPIO04 V3.1 Module Pin 10 - V5.0 Module Pin 2
+ GPIO_USER, // GPIO05 V3.1 Module Pin 9 - V5.0 Module Pin 1
+ // GPIO06 (SD_CLK Flash)
+ // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT)
+ // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT)
+ // GPIO09 (SD_DATA2 Flash QIO or ESP8285)
+ // GPIO10 (SD_DATA3 Flash QIO or ESP8285)
+ // GPIO11 (SD_CMD Flash)
+ GPIO_REL1, // GPIO12 Relay1 ( 1 = Off)
+ GPIO_REL2, // GPIO13 Relay1 ( 1 = On)
+ GPIO_USER, // GPIO14 V3.1 Module Pin 5 - V5.0 GPIO_REL3_INV Relay2 ( 1 = Off)
+ GPIO_LED1, // GPIO15 V5.0 LED1 - Link and Power status
+ GPIO_USER, // GPIO16 V3.1 Module Pin 4 - V5.0 GPIO_REL4_INV Relay2 ( 1 = On)
0
},
- { // WION - Indoor Tap (ESP8266)
- // https://www.amazon.com/gp/product/B00ZYLUBJU/ref=s9_acsd_al_bw_c_x_3_w
- AGPIO(GPIO_USER), // GPIO00 Optional sensor (pm clock)
+ { // WION - Indoor Tap (ESP8266)
+ // https://www.amazon.com/gp/product/B00ZYLUBJU/ref=s9_acsd_al_bw_c_x_3_w
+ GPIO_USER, // GPIO00 Optional sensor (pm clock)
0,
- AGPIO(GPIO_LED1), // GPIO02 Green Led (1 = On, 0 = Off) - Link and Power status
+ GPIO_LED1, // GPIO02 Green Led (1 = On, 0 = Off) - Link and Power status
0, 0, 0,
- // GPIO06 (SD_CLK Flash)
- // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT)
- // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT)
- // GPIO09 (SD_DATA2 Flash QIO or ESP8285)
- // GPIO10 (SD_DATA3 Flash QIO or ESP8285)
- // GPIO11 (SD_CMD Flash)
- AGPIO(GPIO_USER), // GPIO12 Optional sensor (pm data)
- AGPIO(GPIO_KEY1), // GPIO13 Button
+ // GPIO06 (SD_CLK Flash)
+ // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT)
+ // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT)
+ // GPIO09 (SD_DATA2 Flash QIO or ESP8285)
+ // GPIO10 (SD_DATA3 Flash QIO or ESP8285)
+ // GPIO11 (SD_CMD Flash)
+ GPIO_USER, // GPIO12 Optional sensor (pm data)
+ GPIO_KEY1, // GPIO13 Button
0,
- AGPIO(GPIO_REL1), // GPIO15 Relay (0 = Off, 1 = On)
+ GPIO_REL1, // GPIO15 Relay (0 = Off, 1 = On)
0, 0
},
- { // SONOFF_DEV - Sonoff Dev (ESP8266)
- AGPIO(GPIO_KEY1), // GPIO00 E-FW Button
- AGPIO(GPIO_USER), // GPIO01 TX Serial RXD and Optional sensor
+ { // SONOFF_DEV - Sonoff Dev (ESP8266)
+ GPIO_KEY1, // GPIO00 E-FW Button
+ GPIO_USER, // GPIO01 TX Serial RXD and Optional sensor
0, // GPIO02
- AGPIO(GPIO_USER), // GPIO03 RX Serial TXD and Optional sensor
- AGPIO(GPIO_USER), // GPIO04 Optional sensor
- AGPIO(GPIO_USER), // GPIO05 Optional sensor
- // GPIO06 (SD_CLK Flash)
- // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT)
- // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT)
- // GPIO09 (SD_DATA2 Flash QIO or ESP8285)
- // GPIO10 (SD_DATA3 Flash QIO or ESP8285)
- // GPIO11 (SD_CMD Flash)
- AGPIO(GPIO_USER), // GPIO12
- AGPIO(GPIO_USER), // GPIO13 BLUE LED
- AGPIO(GPIO_USER), // GPIO14 Optional sensor
- 0, // GPIO15
- 0, // GPIO16
- AGPIO(ADC0_USER) // ADC0 A0 Analog input
+ GPIO_USER, // GPIO03 RX Serial TXD and Optional sensor
+ GPIO_USER, // GPIO04 Optional sensor
+ GPIO_USER, // GPIO05 Optional sensor
+ // GPIO06 (SD_CLK Flash)
+ // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT)
+ // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT)
+ // GPIO09 (SD_DATA2 Flash QIO or ESP8285)
+ // GPIO10 (SD_DATA3 Flash QIO or ESP8285)
+ // GPIO11 (SD_CMD Flash)
+ GPIO_USER, // GPIO12
+ GPIO_USER, // GPIO13 BLUE LED
+ GPIO_USER, // GPIO14 Optional sensor
+ 0, // GPIO15
+ 0, // GPIO16
+ ADC0_USER // ADC0 A0 Analog input
},
- { // H801 - Lixada H801 Wifi (ESP8266)
- AGPIO(GPIO_USER), // GPIO00 E-FW Button
- AGPIO(GPIO_LED1), // GPIO01 Green LED - Link and Power status
- AGPIO(GPIO_USER), // GPIO02 TX and Optional sensor - Pin next to TX on the PCB
- AGPIO(GPIO_USER), // GPIO03 RX and Optional sensor - Pin next to GND on the PCB
- AGPIO(GPIO_PWM1) +4, // GPIO04 W2 - PWM5
- AGPIO(GPIO_LED1_INV) +1, // GPIO05 Red LED
- // GPIO06 (SD_CLK Flash)
- // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT)
- // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT)
- // GPIO09 (SD_DATA2 Flash QIO or ESP8285)
- // GPIO10 (SD_DATA3 Flash QIO or ESP8285)
- // GPIO11 (SD_CMD Flash)
- AGPIO(GPIO_PWM1) +2, // GPIO12 Blue
- AGPIO(GPIO_PWM1) +1, // GPIO13 Green
- AGPIO(GPIO_PWM1) +3, // GPIO14 W1 - PWM4
- AGPIO(GPIO_PWM1), // GPIO15 Red
+ { // H801 - Lixada H801 Wifi (ESP8266)
+ GPIO_USER, // GPIO00 E-FW Button
+ GPIO_LED1, // GPIO01 Green LED - Link and Power status
+ GPIO_USER, // GPIO02 TX and Optional sensor - Pin next to TX on the PCB
+ GPIO_USER, // GPIO03 RX and Optional sensor - Pin next to GND on the PCB
+ GPIO_PWM5, // GPIO04 W2 - PWM5
+ GPIO_LED2_INV, // GPIO05 Red LED
+ // GPIO06 (SD_CLK Flash)
+ // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT)
+ // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT)
+ // GPIO09 (SD_DATA2 Flash QIO or ESP8285)
+ // GPIO10 (SD_DATA3 Flash QIO or ESP8285)
+ // GPIO11 (SD_CMD Flash)
+ GPIO_PWM3, // GPIO12 Blue
+ GPIO_PWM2, // GPIO13 Green
+ GPIO_PWM4, // GPIO14 W1 - PWM4
+ GPIO_PWM1, // GPIO15 Red
0, 0
},
- { // SONOFF_SC - onoff SC (ESP8266)
- AGPIO(GPIO_KEY1), // GPIO00 Button
- AGPIO(GPIO_TXD), // GPIO01 RXD to ATMEGA328P
- AGPIO(GPIO_USER), // GPIO02 Optional sensor
- AGPIO(GPIO_RXD), // GPIO03 TXD to ATMEGA328P
+ { // SONOFF_SC - onoff SC (ESP8266)
+ GPIO_KEY1, // GPIO00 Button
+ GPIO_TXD, // GPIO01 RXD to ATMEGA328P
+ GPIO_USER, // GPIO02 Optional sensor
+ GPIO_RXD, // GPIO03 TXD to ATMEGA328P
0, 0,
- // GPIO06 (SD_CLK Flash)
- // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT)
- // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT)
- // GPIO09 (SD_DATA2 Flash QIO or ESP8285)
- // GPIO10 (SD_DATA3 Flash QIO or ESP8285)
- // GPIO11 (SD_CMD Flash)
+ // GPIO06 (SD_CLK Flash)
+ // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT)
+ // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT)
+ // GPIO09 (SD_DATA2 Flash QIO or ESP8285)
+ // GPIO10 (SD_DATA3 Flash QIO or ESP8285)
+ // GPIO11 (SD_CMD Flash)
0,
- AGPIO(GPIO_LED1_INV), // GPIO13 Green Led (0 = On, 1 = Off) - Link and Power status
+ GPIO_LED1_INV, // GPIO13 Green Led (0 = On, 1 = Off) - Link and Power status
0, 0, 0, 0
},
- { // SONOFF_BN - Sonoff BN-SZ01 Ceiling led (ESP8285)
+ { // SONOFF_BN - Sonoff BN-SZ01 Ceiling led (ESP8285)
0, 0, 0, 0, 0, 0,
- // GPIO06 (SD_CLK Flash)
- // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT)
- // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT)
- // GPIO09 (SD_DATA2 Flash QIO or ESP8285)
- // GPIO10 (SD_DATA3 Flash QIO or ESP8285)
- // GPIO11 (SD_CMD Flash)
- AGPIO(GPIO_PWM1), // GPIO12 Light
- AGPIO(GPIO_LED1_INV), // GPIO13 Red Led (0 = On, 1 = Off) - Link and Power status
+ // GPIO06 (SD_CLK Flash)
+ // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT)
+ // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT)
+ // GPIO09 (SD_DATA2 Flash QIO or ESP8285)
+ // GPIO10 (SD_DATA3 Flash QIO or ESP8285)
+ // GPIO11 (SD_CMD Flash)
+ GPIO_PWM1, // GPIO12 Light
+ GPIO_LED1_INV, // GPIO13 Red Led (0 = On, 1 = Off) - Link and Power status
0, 0, 0, 0
},
- { // HUAFAN_SS - Hua Fan Smart Socket (ESP8266) - like Sonoff Pow
- AGPIO(GPIO_LEDLNK_INV), // GPIO00 Blue Led (0 = On, 1 = Off) - Link status
+ { // HUAFAN_SS - Hua Fan Smart Socket (ESP8266) - like Sonoff Pow
+ GPIO_LEDLNK_INV, // GPIO00 Blue Led (0 = On, 1 = Off) - Link status
0, 0,
- AGPIO(GPIO_LED1_INV), // GPIO03 Red Led (0 = On, 1 = Off) - Power status
- AGPIO(GPIO_KEY1), // GPIO04 Button
- AGPIO(GPIO_REL1_INV), // GPIO05 Relay (0 = On, 1 = Off)
- // GPIO06 (SD_CLK Flash)
- // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT)
- // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT)
- // GPIO09 (SD_DATA2 Flash QIO or ESP8285)
- // GPIO10 (SD_DATA3 Flash QIO or ESP8285)
- // GPIO11 (SD_CMD Flash)
- AGPIO(GPIO_NRG_CF1), // GPIO12 HLW8012 CF1 voltage / current
- AGPIO(GPIO_NRG_SEL), // GPIO13 HLW8012 Sel output (1 = Voltage)
- AGPIO(GPIO_HLW_CF), // GPIO14 HLW8012 CF power
+ GPIO_LED1_INV, // GPIO03 Red Led (0 = On, 1 = Off) - Power status
+ GPIO_KEY1, // GPIO04 Button
+ GPIO_REL1_INV, // GPIO05 Relay (0 = On, 1 = Off)
+ // GPIO06 (SD_CLK Flash)
+ // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT)
+ // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT)
+ // GPIO09 (SD_DATA2 Flash QIO or ESP8285)
+ // GPIO10 (SD_DATA3 Flash QIO or ESP8285)
+ // GPIO11 (SD_CMD Flash)
+ GPIO_NRG_CF1, // GPIO12 HLW8012 CF1 voltage / current
+ GPIO_NRG_SEL, // GPIO13 HLW8012 Sel output (1 = Voltage)
+ GPIO_HLW_CF, // GPIO14 HLW8012 CF power
0, 0, 0
},
- { // SONOFF_BRIDGE - Sonoff RF Bridge 433 (ESP8285)
- AGPIO(GPIO_KEY1), // GPIO00 Button
- AGPIO(GPIO_TXD), // GPIO01 RF bridge control
- AGPIO(GPIO_USER), // GPIO02 Optional sensor
- AGPIO(GPIO_RXD), // GPIO03 RF bridge control
- AGPIO(GPIO_USER), // GPIO04 Optional sensor
- AGPIO(GPIO_USER), // GPIO05 Optional sensor
- // GPIO06 (SD_CLK Flash)
- // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT)
- // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT)
- // GPIO09 (SD_DATA2 Flash QIO or ESP8285)
- // GPIO10 (SD_DATA3 Flash QIO or ESP8285)
- // GPIO11 (SD_CMD Flash)
- AGPIO(GPIO_USER), // GPIO12 Optional sensor
- AGPIO(GPIO_LED1_INV), // GPIO13 Blue Led (0 = On, 1 = Off) - Link and Power status
- AGPIO(GPIO_USER), // GPIO14 Optional sensor
+ { // SONOFF_BRIDGE - Sonoff RF Bridge 433 (ESP8285)
+ GPIO_KEY1, // GPIO00 Button
+ GPIO_TXD, // GPIO01 RF bridge control
+ GPIO_USER, // GPIO02 Optional sensor
+ GPIO_RXD, // GPIO03 RF bridge control
+ GPIO_USER, // GPIO04 Optional sensor
+ GPIO_USER, // GPIO05 Optional sensor
+ // GPIO06 (SD_CLK Flash)
+ // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT)
+ // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT)
+ // GPIO09 (SD_DATA2 Flash QIO or ESP8285)
+ // GPIO10 (SD_DATA3 Flash QIO or ESP8285)
+ // GPIO11 (SD_CMD Flash)
+ GPIO_USER, // GPIO12 Optional sensor
+ GPIO_LED1_INV, // GPIO13 Blue Led (0 = On, 1 = Off) - Link and Power status
+ GPIO_USER, // GPIO14 Optional sensor
0, 0, 0
},
- { // SONOFF_B1 - Sonoff B1 (ESP8285 - my9231)
- AGPIO(GPIO_KEY1), // GPIO00 Pad
- AGPIO(GPIO_USER), // GPIO01 Serial RXD and Optional sensor pad
- AGPIO(GPIO_USER), // GPIO02 Optional sensor SDA pad
- AGPIO(GPIO_USER), // GPIO03 Serial TXD and Optional sensor pad
+ { // SONOFF_B1 - Sonoff B1 (ESP8285 - my9231)
+ GPIO_KEY1, // GPIO00 Pad
+ GPIO_USER, // GPIO01 Serial RXD and Optional sensor pad
+ GPIO_USER, // GPIO02 Optional sensor SDA pad
+ GPIO_USER, // GPIO03 Serial TXD and Optional sensor pad
0, 0,
- // GPIO06 (SD_CLK Flash)
- // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT)
- // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT)
- // GPIO09 (SD_DATA2 Flash QIO or ESP8285)
- // GPIO10 (SD_DATA3 Flash QIO or ESP8285)
- // GPIO11 (SD_CMD Flash)
- AGPIO(GPIO_DI), // GPIO12 my9231 DI
+ // GPIO06 (SD_CLK Flash)
+ // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT)
+ // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT)
+ // GPIO09 (SD_DATA2 Flash QIO or ESP8285)
+ // GPIO10 (SD_DATA3 Flash QIO or ESP8285)
+ // GPIO11 (SD_CMD Flash)
+ GPIO_DI, // GPIO12 my9231 DI
0,
- AGPIO(GPIO_DCKI), // GPIO14 my9231 DCKI
+ GPIO_DCKI, // GPIO14 my9231 DCKI
0, 0, 0
},
- { // AILIGHT - Ai-Thinker RGBW led (ESP8266 - my9291)
- AGPIO(GPIO_KEY1), // GPIO00 Pad
- AGPIO(GPIO_USER), // GPIO01 Serial RXD and Optional sensor pad
- AGPIO(GPIO_USER), // GPIO02 Optional sensor SDA pad
- AGPIO(GPIO_USER), // GPIO03 Serial TXD and Optional sensor pad
+ { // AILIGHT - Ai-Thinker RGBW led (ESP8266 - my9291)
+ GPIO_KEY1, // GPIO00 Pad
+ GPIO_USER, // GPIO01 Serial RXD and Optional sensor pad
+ GPIO_USER, // GPIO02 Optional sensor SDA pad
+ GPIO_USER, // GPIO03 Serial TXD and Optional sensor pad
0, 0,
- // GPIO06 (SD_CLK Flash)
- // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT)
- // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT)
- // GPIO09 (SD_DATA2 Flash QIO or ESP8285)
- // GPIO10 (SD_DATA3 Flash QIO or ESP8285)
- // GPIO11 (SD_CMD Flash)
+ // GPIO06 (SD_CLK Flash)
+ // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT)
+ // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT)
+ // GPIO09 (SD_DATA2 Flash QIO or ESP8285)
+ // GPIO10 (SD_DATA3 Flash QIO or ESP8285)
+ // GPIO11 (SD_CMD Flash)
0,
- AGPIO(GPIO_DI), // GPIO13 my9291 DI
+ GPIO_DI, // GPIO13 my9291 DI
0,
- AGPIO(GPIO_DCKI), // GPIO15 my9291 DCKI
+ GPIO_DCKI, // GPIO15 my9291 DCKI
0, 0
},
- { // SONOFF_T11 - Sonoff T1 1CH (ESP8285)
- AGPIO(GPIO_KEY1), // GPIO00 Button 1
- AGPIO(GPIO_USER), // GPIO01 Serial RXD and Optional sensor
- AGPIO(GPIO_USER), // GPIO02 Optional Sensor (J3 Pin 5)
- AGPIO(GPIO_USER), // GPIO03 Serial TXD and Optional sensor
+ { // SONOFF_T11 - Sonoff T1 1CH (ESP8285)
+ GPIO_KEY1, // GPIO00 Button 1
+ GPIO_USER, // GPIO01 Serial RXD and Optional sensor
+ GPIO_USER, // GPIO02 Optional Sensor (J3 Pin 5)
+ GPIO_USER, // GPIO03 Serial TXD and Optional sensor
0, 0,
- // GPIO06 (SD_CLK Flash)
- // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT)
- // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT)
- // GPIO09 (SD_DATA2 Flash QIO or ESP8285)
- // GPIO10 (SD_DATA3 Flash QIO or ESP8285)
- // GPIO11 (SD_CMD Flash)
- AGPIO(GPIO_REL1), // GPIO12 Blue Led and Relay 1 (0 = Off, 1 = On)
- AGPIO(GPIO_LED1_INV), // GPIO13 Blue Led (0 = On, 1 = Off) - Link and Power status
+ // GPIO06 (SD_CLK Flash)
+ // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT)
+ // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT)
+ // GPIO09 (SD_DATA2 Flash QIO or ESP8285)
+ // GPIO10 (SD_DATA3 Flash QIO or ESP8285)
+ // GPIO11 (SD_CMD Flash)
+ GPIO_REL1, // GPIO12 Blue Led and Relay 1 (0 = Off, 1 = On)
+ GPIO_LED1_INV, // GPIO13 Blue Led (0 = On, 1 = Off) - Link and Power status
0, 0, 0, 0
},
- { // SUPLA1 - Supla Espablo (ESP8266)
- // http://www.wykop.pl/ramka/3325399/diy-supla-do-puszki-instalacyjnej-podtynkowej-supla-org/
- 0, // GPIO00 Flash jumper
- AGPIO(GPIO_USER), // GPIO01 Serial RXD and Optional sensor
+ { // SUPLA1 - Supla Espablo (ESP8266)
+ // http://www.wykop.pl/ramka/3325399/diy-supla-do-puszki-instalacyjnej-podtynkowej-supla-org/
+ 0, // GPIO00 Flash jumper
+ GPIO_USER, // GPIO01 Serial RXD and Optional sensor
#ifdef USE_DS18x20
- AGPIO(GPIO_DSB), // GPIO02 DS18B20 sensor
+ GPIO_DSB, // GPIO02 DS18B20 sensor
#else
- AGPIO(GPIO_USER), // GPIO02 Optional sensor
+ GPIO_USER, // GPIO02 Optional sensor
#endif
- AGPIO(GPIO_USER), // GPIO03 Serial TXD and Optional sensor
- AGPIO(GPIO_KEY1), // GPIO04 Button 1
- AGPIO(GPIO_REL1), // GPIO05 Relay 1 (0 = Off, 1 = On)
- // GPIO06 (SD_CLK Flash)
- // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT)
- // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT)
- // GPIO09 (SD_DATA2 Flash QIO or ESP8285)
- // GPIO10 (SD_DATA3 Flash QIO or ESP8285)
- // GPIO11 (SD_CMD Flash)
- AGPIO(GPIO_USER), // GPIO12 Optional sensor
- AGPIO(GPIO_REL1) +1, // GPIO13 Relay 2 (0 = Off, 1 = On)
- AGPIO(GPIO_USER), // GPIO14 Optional sensor
+ GPIO_USER, // GPIO03 Serial TXD and Optional sensor
+ GPIO_KEY1, // GPIO04 Button 1
+ GPIO_REL1, // GPIO05 Relay 1 (0 = Off, 1 = On)
+ // GPIO06 (SD_CLK Flash)
+ // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT)
+ // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT)
+ // GPIO09 (SD_DATA2 Flash QIO or ESP8285)
+ // GPIO10 (SD_DATA3 Flash QIO or ESP8285)
+ // GPIO11 (SD_CMD Flash)
+ GPIO_USER, // GPIO12 Optional sensor
+ GPIO_REL2, // GPIO13 Relay 2 (0 = Off, 1 = On)
+ GPIO_USER, // GPIO14 Optional sensor
0,
- AGPIO(GPIO_LED1), // GPIO16 Led (1 = On, 0 = Off) - Link and Power status
- AGPIO(ADC0_USER) // ADC0 A0 Analog input
+ GPIO_LED1, // GPIO16 Led (1 = On, 0 = Off) - Link and Power status
+ ADC0_USER // ADC0 A0 Analog input
},
- { // WITTY - Witty Cloud Dev Board (ESP8266)
- // https://www.aliexpress.com/item/ESP8266-serial-WIFI-Witty-cloud-Development-Board-ESP-12F-module-MINI-nodemcu/32643464555.html
- AGPIO(GPIO_USER), // GPIO00 D3 flash push button on interface board
- AGPIO(GPIO_USER), // GPIO01 Serial RXD and Optional sensor
- AGPIO(GPIO_LED1_INV), // GPIO02 D4 Blue Led (0 = On, 1 = Off) on ESP-12F - Link and Power status
- AGPIO(GPIO_USER), // GPIO03 Serial TXD and Optional sensor
- AGPIO(GPIO_KEY1), // GPIO04 D2 push button on ESP-12F board
- AGPIO(GPIO_USER), // GPIO05 D1 optional sensor
- // GPIO06 (SD_CLK Flash)
- // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT)
- // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT)
- // GPIO09 (SD_DATA2 Flash QIO or ESP8285)
- // GPIO10 (SD_DATA3 Flash QIO or ESP8285)
- // GPIO11 (SD_CMD Flash)
- AGPIO(GPIO_PWM1) +1, // GPIO12 D6 RGB LED Green
- AGPIO(GPIO_PWM1) +2, // GPIO13 D7 RGB LED Blue
- AGPIO(GPIO_USER), // GPIO14 D5 optional sensor
- AGPIO(GPIO_PWM1), // GPIO15 D8 RGB LED Red
- AGPIO(GPIO_USER), // GPIO16 D0 optional sensor
- AGPIO(ADC0_USER) // ADC0 A0 Light sensor / Requires USE_ADC_VCC in user_config.h to be disabled
+ { // WITTY - Witty Cloud Dev Board (ESP8266)
+ // https://www.aliexpress.com/item/ESP8266-serial-WIFI-Witty-cloud-Development-Board-ESP-12F-module-MINI-nodemcu/32643464555.html
+ GPIO_USER, // GPIO00 D3 flash push button on interface board
+ GPIO_USER, // GPIO01 Serial RXD and Optional sensor
+ GPIO_LED1_INV, // GPIO02 D4 Blue Led (0 = On, 1 = Off) on ESP-12F - Link and Power status
+ GPIO_USER, // GPIO03 Serial TXD and Optional sensor
+ GPIO_KEY1, // GPIO04 D2 push button on ESP-12F board
+ GPIO_USER, // GPIO05 D1 optional sensor
+ // GPIO06 (SD_CLK Flash)
+ // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT)
+ // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT)
+ // GPIO09 (SD_DATA2 Flash QIO or ESP8285)
+ // GPIO10 (SD_DATA3 Flash QIO or ESP8285)
+ // GPIO11 (SD_CMD Flash)
+ GPIO_PWM2, // GPIO12 D6 RGB LED Green
+ GPIO_PWM3, // GPIO13 D7 RGB LED Blue
+ GPIO_USER, // GPIO14 D5 optional sensor
+ GPIO_PWM1, // GPIO15 D8 RGB LED Red
+ GPIO_USER, // GPIO16 D0 optional sensor
+ ADC0_USER // ADC0 A0 Light sensor / Requires USE_ADC_VCC in user_config.h to be disabled
},
- { // YUNSHAN - Yunshan Wifi Relay (ESP8266)
- // https://www.ebay.com/p/Esp8266-220v-10a-Network-Relay-WiFi-Module/1369583381
- // Schematics and Info https://ucexperiment.wordpress.com/2016/12/18/yunshan-esp8266-250v-15a-acdc-network-wifi-relay-module/
- 0, // GPIO00 Flash jumper - Module Pin 8
- AGPIO(GPIO_USER), // GPIO01 Serial RXD and Optional sensor - Module Pin 2
- AGPIO(GPIO_LED1_INV), // GPIO02 Blue Led (0 = On, 1 = Off) on ESP-12F - Module Pin 7 - Link and Power status
- AGPIO(GPIO_USER), // GPIO03 Serial TXD and Optional sensor - Module Pin 3
- AGPIO(GPIO_REL1), // GPIO04 Red Led and Relay (0 = Off, 1 = On) - Module Pin 10
- AGPIO(GPIO_KEY1), // GPIO05 Blue Led and OptoCoupler input - Module Pin 9
- // GPIO06 (SD_CLK Flash)
- // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT)
- // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT)
- // GPIO09 (SD_DATA2 Flash QIO or ESP8285)
- // GPIO10 (SD_DATA3 Flash QIO or ESP8285)
- // GPIO11 (SD_CMD Flash)
+ { // YUNSHAN - Yunshan Wifi Relay (ESP8266)
+ // https://www.ebay.com/p/Esp8266-220v-10a-Network-Relay-WiFi-Module/1369583381
+ // Schematics and Info https://ucexperiment.wordpress.com/2016/12/18/yunshan-esp8266-250v-15a-acdc-network-wifi-relay-module/
+ 0, // GPIO00 Flash jumper - Module Pin 8
+ GPIO_USER, // GPIO01 Serial RXD and Optional sensor - Module Pin 2
+ GPIO_LED1_INV, // GPIO02 Blue Led (0 = On, 1 = Off) on ESP-12F - Module Pin 7 - Link and Power status
+ GPIO_USER, // GPIO03 Serial TXD and Optional sensor - Module Pin 3
+ GPIO_REL1, // GPIO04 Red Led and Relay (0 = Off, 1 = On) - Module Pin 10
+ GPIO_KEY1, // GPIO05 Blue Led and OptoCoupler input - Module Pin 9
+ // GPIO06 (SD_CLK Flash)
+ // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT)
+ // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT)
+ // GPIO09 (SD_DATA2 Flash QIO or ESP8285)
+ // GPIO10 (SD_DATA3 Flash QIO or ESP8285)
+ // GPIO11 (SD_CMD Flash)
0, 0, 0, 0, 0, 0
},
- { // MAGICHOME - Magic Home (aka Flux-light) (ESP8266) and Arilux LC10 (ESP8285)
- // https://www.aliexpress.com/item/Magic-Home-Mini-RGB-RGBW-Wifi-Controller-For-Led-Strip-Panel-light-Timing-Function-16million-colors/32686853650.html
+ { // MAGICHOME - Magic Home (aka Flux-light) (ESP8266) and Arilux LC10 (ESP8285)
+ // https://www.aliexpress.com/item/Magic-Home-Mini-RGB-RGBW-Wifi-Controller-For-Led-Strip-Panel-light-Timing-Function-16million-colors/32686853650.html
0,
- AGPIO(GPIO_USER), // GPIO01 Serial RXD and Optional sensor
- AGPIO(GPIO_LED1_INV), // GPIO02 Blue onboard LED - Link and Power status
- AGPIO(GPIO_USER), // GPIO03 Serial TXD and Optional sensor
- AGPIO(GPIO_ARIRFRCV), // GPIO04 IR or RF receiver (optional) (Arilux LC10)
- AGPIO(GPIO_PWM1) +1, // GPIO05 RGB LED Green
- // GPIO06 (SD_CLK Flash)
- // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT)
- // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT)
- // GPIO09 (SD_DATA2 Flash QIO or ESP8285)
- // GPIO10 (SD_DATA3 Flash QIO or ESP8285)
- // GPIO11 (SD_CMD Flash)
- AGPIO(GPIO_PWM1) +2, // GPIO12 RGB LED Blue
- AGPIO(GPIO_USER), // GPIO13 RGBW LED White (optional - set to PWM4 for Cold White or Warm White as used on Arilux LC10)
- AGPIO(GPIO_PWM1), // GPIO14 RGB LED Red
- AGPIO(GPIO_ARIRFSEL), // GPIO15 RF receiver control (Arilux LC10)
+ GPIO_USER, // GPIO01 Serial RXD and Optional sensor
+ GPIO_LED1_INV, // GPIO02 Blue onboard LED - Link and Power status
+ GPIO_USER, // GPIO03 Serial TXD and Optional sensor
+ GPIO_ARIRFRCV, // GPIO04 IR or RF receiver (optional) (Arilux LC10)
+ GPIO_PWM2, // GPIO05 RGB LED Green
+ // GPIO06 (SD_CLK Flash)
+ // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT)
+ // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT)
+ // GPIO09 (SD_DATA2 Flash QIO or ESP8285)
+ // GPIO10 (SD_DATA3 Flash QIO or ESP8285)
+ // GPIO11 (SD_CMD Flash)
+ GPIO_PWM3, // GPIO12 RGB LED Blue
+ GPIO_USER, // GPIO13 RGBW LED White (optional - set to PWM4 for Cold White or Warm White as used on Arilux LC10)
+ GPIO_PWM1, // GPIO14 RGB LED Red
+ GPIO_ARIRFSEL, // GPIO15 RF receiver control (Arilux LC10)
0, 0
},
- { // LUANIHVIO - ESP8266_HVIO
- // https://luani.de/projekte/esp8266-hvio/
- 0, // GPIO00 Flash jumper
- AGPIO(GPIO_USER), // GPIO01 Serial RXD and Optional sensor
- AGPIO(GPIO_USER), // GPIO02 Optional sensor / I2C SDA pad
- AGPIO(GPIO_USER), // GPIO03 Serial TXD and Optional sensor
- AGPIO(GPIO_REL1), // GPIO04 Relay 1 (0 = Off, 1 = On)
- AGPIO(GPIO_REL1) +1, // GPIO05 Relay 2 (0 = Off, 1 = On)
- // GPIO06 (SD_CLK Flash)
- // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT)
- // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT)
- // GPIO09 (SD_DATA2 Flash QIO or ESP8285)
- // GPIO10 (SD_DATA3 Flash QIO or ESP8285)
- // GPIO11 (SD_CMD Flash)
- AGPIO(GPIO_SWT1), // GPIO12 External input 1 (0 = On, 1 = Off)
- AGPIO(GPIO_SWT1) +1, // GPIO13 External input 2 (0 = On, 1 = Off)
- AGPIO(GPIO_USER), // GPIO14 Optional sensor / I2C SCL pad
- AGPIO(GPIO_LED1), // GPIO15 Led (1 = On, 0 = Off) - Link and Power status
+ { // LUANIHVIO - ESP8266_HVIO
+ // https://luani.de/projekte/esp8266-hvio/
+ 0, // GPIO00 Flash jumper
+ GPIO_USER, // GPIO01 Serial RXD and Optional sensor
+ GPIO_USER, // GPIO02 Optional sensor / I2C SDA pad
+ GPIO_USER, // GPIO03 Serial TXD and Optional sensor
+ GPIO_REL1, // GPIO04 Relay 1 (0 = Off, 1 = On)
+ GPIO_REL2, // GPIO05 Relay 2 (0 = Off, 1 = On)
+ // GPIO06 (SD_CLK Flash)
+ // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT)
+ // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT)
+ // GPIO09 (SD_DATA2 Flash QIO or ESP8285)
+ // GPIO10 (SD_DATA3 Flash QIO or ESP8285)
+ // GPIO11 (SD_CMD Flash)
+ GPIO_SWT1, // GPIO12 External input 1 (0 = On, 1 = Off)
+ GPIO_SWT2, // GPIO13 External input 2 (0 = On, 1 = Off)
+ GPIO_USER, // GPIO14 Optional sensor / I2C SCL pad
+ GPIO_LED1, // GPIO15 Led (1 = On, 0 = Off) - Link and Power status
0,
- AGPIO(ADC0_USER) // ADC0 A0 Analog input
+ ADC0_USER // ADC0 A0 Analog input
},
- { // KMC_70011 - KMC 70011
- // https://www.amazon.com/KMC-Timing-Monitoring-Network-125V-240V/dp/B06XRX2GTQ
- AGPIO(GPIO_KEY1), // GPIO00 Button
+ { // KMC_70011 - KMC 70011
+ // https://www.amazon.com/KMC-Timing-Monitoring-Network-125V-240V/dp/B06XRX2GTQ
+ GPIO_KEY1, // GPIO00 Button
0, 0, 0,
- AGPIO(GPIO_HLW_CF), // GPIO04 HLW8012 CF power
- AGPIO(GPIO_NRG_CF1), // GPIO05 HLW8012 CF1 voltage / current
- // GPIO06 (SD_CLK Flash)
- // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT)
- // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT)
- // GPIO09 (SD_DATA2 Flash QIO or ESP8285)
- // GPIO10 (SD_DATA3 Flash QIO or ESP8285)
- // GPIO11 (SD_CMD Flash)
- AGPIO(GPIO_NRG_SEL), // GPIO12 HLW8012 SEL (1 = Voltage)
- AGPIO(GPIO_LED1_INV), // GPIO13 Green Led - Link and Power status
- AGPIO(GPIO_REL1), // GPIO14 Relay
+ GPIO_HLW_CF, // GPIO04 HLW8012 CF power
+ GPIO_NRG_CF1, // GPIO05 HLW8012 CF1 voltage / current
+ // GPIO06 (SD_CLK Flash)
+ // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT)
+ // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT)
+ // GPIO09 (SD_DATA2 Flash QIO or ESP8285)
+ // GPIO10 (SD_DATA3 Flash QIO or ESP8285)
+ // GPIO11 (SD_CMD Flash)
+ GPIO_NRG_SEL, // GPIO12 HLW8012 SEL (1 = Voltage)
+ GPIO_LED1_INV, // GPIO13 Green Led - Link and Power status
+ GPIO_REL1, // GPIO14 Relay
0, 0, 0
},
- { // ARILUX_LC01 - Arilux AL-LC01 (ESP8285)
- // https://www.banggood.com/nl/ARILUX-AL-LC01-Super-Mini-LED-WIFI-Smart-RGB-Controller-For-RGB-LED-Strip-Light-DC-9-12V-p-1058603.html
- // (PwmFrequency 1111Hz)
- AGPIO(GPIO_KEY1), // GPIO00 Optional Button
- AGPIO(GPIO_USER), // GPIO01 Serial RXD and Optional sensor
- AGPIO(GPIO_ARIRFSEL), // GPIO02 RF receiver control
- AGPIO(GPIO_USER), // GPIO03 Serial TXD and Optional sensor
- AGPIO(GPIO_ARIRFRCV), // GPIO04 IR or RF receiver (optional)
- AGPIO(GPIO_PWM1), // GPIO05 RGB LED Red
- // GPIO06 (SD_CLK Flash)
- // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT)
- // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT)
- // GPIO09 (SD_DATA2 Flash QIO or ESP8285)
- // GPIO10 (SD_DATA3 Flash QIO or ESP8285)
- // GPIO11 (SD_CMD Flash)
- AGPIO(GPIO_PWM1) +1, // GPIO12 RGB LED Green
- AGPIO(GPIO_PWM1) +2, // GPIO13 RGB LED Blue
- AGPIO(GPIO_USER), // GPIO14 RGBW LED White (optional - set to PWM4 for Cold White or Warm White)
+ { // ARILUX_LC01 - Arilux AL-LC01 (ESP8285)
+ // https://www.banggood.com/nl/ARILUX-AL-LC01-Super-Mini-LED-WIFI-Smart-RGB-Controller-For-RGB-LED-Strip-Light-DC-9-12V-p-1058603.html
+ // (PwmFrequency 1111Hz)
+ GPIO_KEY1, // GPIO00 Optional Button
+ GPIO_USER, // GPIO01 Serial RXD and Optional sensor
+ GPIO_ARIRFSEL, // GPIO02 RF receiver control
+ GPIO_USER, // GPIO03 Serial TXD and Optional sensor
+ GPIO_ARIRFRCV, // GPIO04 IR or RF receiver (optional)
+ GPIO_PWM1, // GPIO05 RGB LED Red
+ // GPIO06 (SD_CLK Flash)
+ // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT)
+ // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT)
+ // GPIO09 (SD_DATA2 Flash QIO or ESP8285)
+ // GPIO10 (SD_DATA3 Flash QIO or ESP8285)
+ // GPIO11 (SD_CMD Flash)
+ GPIO_PWM2, // GPIO12 RGB LED Green
+ GPIO_PWM3, // GPIO13 RGB LED Blue
+ GPIO_USER, // GPIO14 RGBW LED White (optional - set to PWM4 for Cold White or Warm White)
0, 0, 0
},
- { // ARILUX_LC11 - Arilux AL-LC11 (ESP8266)
- // https://www.banggood.com/nl/ARILUX-AL-LC11-Super-Mini-LED-WIFI-APP-Controller-RF-Remote-Control-For-RGBWW-LED-Strip-DC9-28V-p-1085112.html
- // (PwmFrequency 540Hz)
- AGPIO(GPIO_KEY1), // GPIO00 Optional Button
- AGPIO(GPIO_USER), // GPIO01 Serial RXD and Optional sensor
- AGPIO(GPIO_ARIRFSEL), // GPIO02 RF receiver control
- AGPIO(GPIO_USER), // GPIO03 Serial TXD and Optional sensor
- AGPIO(GPIO_PWM1) +1, // GPIO04 RGB LED Green
- AGPIO(GPIO_PWM1), // GPIO05 RGB LED Red
- // GPIO06 (SD_CLK Flash)
- // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT)
- // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT)
- // GPIO09 (SD_DATA2 Flash QIO or ESP8285)
- // GPIO10 (SD_DATA3 Flash QIO or ESP8285)
- // GPIO11 (SD_CMD Flash)
- AGPIO(GPIO_PWM1) +4, // GPIO12 RGBCW LED Warm
- AGPIO(GPIO_PWM1) +3, // GPIO13 RGBW LED Cold
- AGPIO(GPIO_PWM1) +2, // GPIO14 RGB LED Blue
- AGPIO(GPIO_ARIRFRCV), // GPIO15 RF receiver input
+ { // ARILUX_LC11 - Arilux AL-LC11 (ESP8266)
+ // https://www.banggood.com/nl/ARILUX-AL-LC11-Super-Mini-LED-WIFI-APP-Controller-RF-Remote-Control-For-RGBWW-LED-Strip-DC9-28V-p-1085112.html
+ // (PwmFrequency 540Hz)
+ GPIO_KEY1, // GPIO00 Optional Button
+ GPIO_USER, // GPIO01 Serial RXD and Optional sensor
+ GPIO_ARIRFSEL, // GPIO02 RF receiver control
+ GPIO_USER, // GPIO03 Serial TXD and Optional sensor
+ GPIO_PWM2, // GPIO04 RGB LED Green
+ GPIO_PWM1, // GPIO05 RGB LED Red
+ // GPIO06 (SD_CLK Flash)
+ // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT)
+ // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT)
+ // GPIO09 (SD_DATA2 Flash QIO or ESP8285)
+ // GPIO10 (SD_DATA3 Flash QIO or ESP8285)
+ // GPIO11 (SD_CMD Flash)
+ GPIO_PWM5, // GPIO12 RGBCW LED Warm
+ GPIO_PWM4, // GPIO13 RGBW LED Cold
+ GPIO_PWM3, // GPIO14 RGB LED Blue
+ GPIO_ARIRFRCV, // GPIO15 RF receiver input
0, 0
},
- { // ARILUX_LC06 - Arilux AL-LC06 (ESP8285)
- // https://www.banggood.com/ARILUX-AL-LC06-LED-WIFI-Smartphone-Controller-Romote-5-Channels-DC12-24V-For-RGBWW-Strip-light-p-1061476.html
- AGPIO(GPIO_KEY1), // GPIO00 Optional Button
- AGPIO(GPIO_USER), // GPIO01 Serial RXD and Optional sensor
- AGPIO(GPIO_USER), // GPIO02 Empty pad
- AGPIO(GPIO_USER), // GPIO03 Serial TXD and Optional sensor
- AGPIO(GPIO_USER), // GPIO04 W2 - PWM5
+ { // ARILUX_LC06 - Arilux AL-LC06 (ESP8285)
+ // https://www.banggood.com/ARILUX-AL-LC06-LED-WIFI-Smartphone-Controller-Romote-5-Channels-DC12-24V-For-RGBWW-Strip-light-p-1061476.html
+ GPIO_KEY1, // GPIO00 Optional Button
+ GPIO_USER, // GPIO01 Serial RXD and Optional sensor
+ GPIO_USER, // GPIO02 Empty pad
+ GPIO_USER, // GPIO03 Serial TXD and Optional sensor
+ GPIO_USER, // GPIO04 W2 - PWM5
0,
- // GPIO06 (SD_CLK Flash)
- // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT)
- // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT)
- // GPIO09 (SD_DATA2 Flash QIO or ESP8285)
- // GPIO10 (SD_DATA3 Flash QIO or ESP8285)
- // GPIO11 (SD_CMD Flash)
- AGPIO(GPIO_PWM1) +1, // GPIO12 RGB LED Green
- AGPIO(GPIO_PWM1) +2, // GPIO13 RGB LED Blue
- AGPIO(GPIO_PWM1), // GPIO14 RGB LED Red
- AGPIO(GPIO_USER), // GPIO15 RGBW LED White
+ // GPIO06 (SD_CLK Flash)
+ // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT)
+ // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT)
+ // GPIO09 (SD_DATA2 Flash QIO or ESP8285)
+ // GPIO10 (SD_DATA3 Flash QIO or ESP8285)
+ // GPIO11 (SD_CMD Flash)
+ GPIO_PWM2, // GPIO12 RGB LED Green
+ GPIO_PWM3, // GPIO13 RGB LED Blue
+ GPIO_PWM1, // GPIO14 RGB LED Red
+ GPIO_USER, // GPIO15 RGBW LED White
0, 0
},
- { // ZENGGE_ZF_WF017 - Zenggee ZJ-WF017-A (ESP12S))
- // https://www.ebay.com/p/Smartphone-Android-IOS-WiFi-Music-Controller-for-RGB-5050-3528-LED-Strip-Light/534446632?_trksid=p2047675.l2644
- AGPIO(GPIO_KEY1), // GPIO00 Optional Button
+ { // ZENGGE_ZF_WF017 - Zenggee ZJ-WF017-A (ESP12S))
+ // https://www.ebay.com/p/Smartphone-Android-IOS-WiFi-Music-Controller-for-RGB-5050-3528-LED-Strip-Light/534446632?_trksid=p2047675.l2644
+ GPIO_KEY1, // GPIO00 Optional Button
0,
- AGPIO(GPIO_USER), // GPIO02 Empty pad
+ GPIO_USER, // GPIO02 Empty pad
0,
- AGPIO(GPIO_USER), // GPIO04 W2 - PWM5
+ GPIO_USER, // GPIO04 W2 - PWM5
0,
- // GPIO06 (SD_CLK Flash)
- // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT)
- // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT)
- // GPIO09 (SD_DATA2 Flash QIO or ESP8285)
- // GPIO10 (SD_DATA3 Flash QIO or ESP8285)
- // GPIO11 (SD_CMD Flash)
- AGPIO(GPIO_PWM1) +1, // GPIO12 RGB LED Green
- AGPIO(GPIO_PWM1), // GPIO13 RGB LED Red
- AGPIO(GPIO_PWM1) +2, // GPIO14 RGB LED Blue
+ // GPIO06 (SD_CLK Flash)
+ // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT)
+ // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT)
+ // GPIO09 (SD_DATA2 Flash QIO or ESP8285)
+ // GPIO10 (SD_DATA3 Flash QIO or ESP8285)
+ // GPIO11 (SD_CMD Flash)
+ GPIO_PWM2, // GPIO12 RGB LED Green
+ GPIO_PWM1, // GPIO13 RGB LED Red
+ GPIO_PWM3, // GPIO14 RGB LED Blue
0, 0, 0
},
- { // SONOFF_POW_R2 - Sonoff Pow R2 (ESP8285 - CSE7766)
- AGPIO(GPIO_KEY1), // GPIO00 Button
- AGPIO(GPIO_CSE7766_TX), // GPIO01 Serial RXD 4800 baud 8E1 CSE7766 energy sensor
+ { // SONOFF_POW_R2 - Sonoff Pow R2 (ESP8285 - CSE7766)
+ GPIO_KEY1, // GPIO00 Button
+ GPIO_CSE7766_TX, // GPIO01 Serial RXD 4800 baud 8E1 CSE7766 energy sensor
0,
- AGPIO(GPIO_CSE7766_RX), // GPIO03 Serial TXD
+ GPIO_CSE7766_RX, // GPIO03 Serial TXD
0, 0,
- // GPIO06 (SD_CLK Flash)
- // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT)
- // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT)
- // GPIO09 (SD_DATA2 Flash QIO or ESP8285)
- // GPIO10 (SD_DATA3 Flash QIO or ESP8285)
- // GPIO11 (SD_CMD Flash)
- AGPIO(GPIO_REL1), // GPIO12 Red Led and Relay (0 = Off, 1 = On)
- AGPIO(GPIO_LED1_INV), // GPIO13 Blue Led (0 = On, 1 = Off) - Link and Power status
+ // GPIO06 (SD_CLK Flash)
+ // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT)
+ // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT)
+ // GPIO09 (SD_DATA2 Flash QIO or ESP8285)
+ // GPIO10 (SD_DATA3 Flash QIO or ESP8285)
+ // GPIO11 (SD_CMD Flash)
+ GPIO_REL1, // GPIO12 Red Led and Relay (0 = Off, 1 = On)
+ GPIO_LED1_INV, // GPIO13 Blue Led (0 = On, 1 = Off) - Link and Power status
0, 0, 0, 0
},
- { // BLITZWOLF_BWSHP - BlitzWolf BW-SHP2 and BW-SHP6 (ESP8285 - BL0937 or HJL-01 Energy Monitoring)
- // https://www.banggood.com/BlitzWolf-BW-SHP2-Smart-WIFI-Socket-EU-Plug-220V-16A-Work-with-Amazon-Alexa-Google-Assistant-p-1292899.html
- // https://www.amazon.de/Steckdose-Homecube-intelligente-Verbrauchsanzeige-funktioniert/dp/B076Q2LKHG/ref=sr_1_fkmr0_1
- // https://www.amazon.de/Intelligente-Stromverbrauch-Fernsteurung-Schaltbare-Energieklasse/dp/B076WZQS4S/ref=sr_1_1
- // https://www.aliexpress.com/store/product/BlitzWolf-BW-SHP6-EU-Plug-Metering-Version-WIFI-Smart-Socket-220V-240V-10A-Work-with-Amazon/1965360_32945504669.html
- AGPIO(GPIO_LED1_INV), // GPIO00 Red Led (1 = On, 0 = Off) - Power status
- AGPIO(GPIO_USER), // GPIO01 Serial RXD and Optional sensor
- AGPIO(GPIO_LEDLNK_INV), // GPIO02 Blue Led (1 = On, 0 = Off) - Link status
- AGPIO(GPIO_USER), // GPIO03 Serial TXD and Optional sensor
+ { // BLITZWOLF_BWSHP - BlitzWolf BW-SHP2 and BW-SHP6 (ESP8285 - BL0937 or HJL-01 Energy Monitoring)
+ // https://www.banggood.com/BlitzWolf-BW-SHP2-Smart-WIFI-Socket-EU-Plug-220V-16A-Work-with-Amazon-Alexa-Google-Assistant-p-1292899.html
+ // https://www.amazon.de/Steckdose-Homecube-intelligente-Verbrauchsanzeige-funktioniert/dp/B076Q2LKHG/ref=sr_1_fkmr0_1
+ // https://www.amazon.de/Intelligente-Stromverbrauch-Fernsteurung-Schaltbare-Energieklasse/dp/B076WZQS4S/ref=sr_1_1
+ // https://www.aliexpress.com/store/product/BlitzWolf-BW-SHP6-EU-Plug-Metering-Version-WIFI-Smart-Socket-220V-240V-10A-Work-with-Amazon/1965360_32945504669.html
+ GPIO_LED1_INV, // GPIO00 Red Led (1 = On, 0 = Off) - Power status
+ GPIO_USER, // GPIO01 Serial RXD and Optional sensor
+ GPIO_LEDLNK_INV, // GPIO02 Blue Led (1 = On, 0 = Off) - Link status
+ GPIO_USER, // GPIO03 Serial TXD and Optional sensor
0,
- AGPIO(GPIO_HJL_CF), // GPIO05 BL0937 or HJL-01 CF power
- // GPIO06 (SD_CLK Flash)
- // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT)
- // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT)
- // GPIO09 (SD_DATA2 Flash QIO or ESP8285)
- // GPIO10 (SD_DATA3 Flash QIO or ESP8285)
- // GPIO11 (SD_CMD Flash)
- AGPIO(GPIO_NRG_SEL_INV), // GPIO12 BL0937 or HJL-01 Sel output (0 = Voltage)
- AGPIO(GPIO_KEY1), // GPIO13 Button
- AGPIO(GPIO_NRG_CF1), // GPIO14 BL0937 or HJL-01 CF1 current / voltage
- AGPIO(GPIO_REL1), // GPIO15 Relay (0 = Off, 1 = On)
+ GPIO_HJL_CF, // GPIO05 BL0937 or HJL-01 CF power
+ // GPIO06 (SD_CLK Flash)
+ // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT)
+ // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT)
+ // GPIO09 (SD_DATA2 Flash QIO or ESP8285)
+ // GPIO10 (SD_DATA3 Flash QIO or ESP8285)
+ // GPIO11 (SD_CMD Flash)
+ GPIO_NRG_SEL_INV, // GPIO12 BL0937 or HJL-01 Sel output (0 = Voltage)
+ GPIO_KEY1, // GPIO13 Button
+ GPIO_NRG_CF1, // GPIO14 BL0937 or HJL-01 CF1 current / voltage
+ GPIO_REL1, // GPIO15 Relay (0 = Off, 1 = On)
0, 0
},
- { // SHELLY1 - Shelly1 Open Source (ESP8266 - 2MB) - https://shelly.cloud/shelly1-open-source/
- AGPIO(GPIO_USER), // GPIO00 - Can be changed to GPIO_USER, only if Shelly is powered with 12V DC
- AGPIO(GPIO_USER), // GPIO01 Serial RXD - Can be changed to GPIO_USER, only if Shelly is powered with 12V DC
+ { // SHELLY1 - Shelly1 Open Source (ESP8266 - 2MB) - https://shelly.cloud/shelly1-open-source/
+ GPIO_USER, // GPIO00 - Can be changed to GPIO_USER, only if Shelly is powered with 12V DC
+ GPIO_USER, // GPIO01 Serial RXD - Can be changed to GPIO_USER, only if Shelly is powered with 12V DC
0,
- AGPIO(GPIO_USER), // GPIO03 Serial TXD - Can be changed to GPIO_USER, only if Shelly is powered with 12V DC
- AGPIO(GPIO_REL1), // GPIO04 Relay (0 = Off, 1 = On)
- AGPIO(GPIO_SWT1_NP), // GPIO05 SW pin
- // GPIO06 (SD_CLK Flash)
- // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT)
- // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT)
- // GPIO09 (SD_DATA2 Flash QIO or ESP8285)
- // GPIO10 (SD_DATA3 Flash QIO or ESP8285)
- // GPIO11 (SD_CMD Flash)
+ GPIO_USER, // GPIO03 Serial TXD - Can be changed to GPIO_USER, only if Shelly is powered with 12V DC
+ GPIO_REL1, // GPIO04 Relay (0 = Off, 1 = On)
+ GPIO_SWT1_NP, // GPIO05 SW pin
+ // GPIO06 (SD_CLK Flash)
+ // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT)
+ // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT)
+ // GPIO09 (SD_DATA2 Flash QIO or ESP8285)
+ // GPIO10 (SD_DATA3 Flash QIO or ESP8285)
+ // GPIO11 (SD_CMD Flash)
0, 0, 0, 0, 0, 0
},
- { // SHELLY2 - Shelly2 (ESP8266 - 2MB) - https://shelly.cloud/shelly2/
+ { // SHELLY2 - Shelly2 (ESP8266 - 2MB) - https://shelly.cloud/shelly2/
0,
- AGPIO(GPIO_MCP39F5_TX), // GPIO01 MCP39F501 Serial input
+ GPIO_MCP39F5_TX, // GPIO01 MCP39F501 Serial input
0,
- AGPIO(GPIO_MCP39F5_RX), // GPIO03 MCP39F501 Serial output
- AGPIO(GPIO_REL1), // GPIO04
- AGPIO(GPIO_REL1) +1, // GPIO05
- // GPIO06 (SD_CLK Flash)
- // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT)
- // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT)
- // GPIO09 (SD_DATA2 Flash QIO or ESP8285)
- // GPIO10 (SD_DATA3 Flash QIO or ESP8285)
- // GPIO11 (SD_CMD Flash)
- AGPIO(GPIO_SWT1), // GPIO12
+ GPIO_MCP39F5_RX, // GPIO03 MCP39F501 Serial output
+ GPIO_REL1, // GPIO04
+ GPIO_REL2, // GPIO05
+ // GPIO06 (SD_CLK Flash)
+ // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT)
+ // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT)
+ // GPIO09 (SD_DATA2 Flash QIO or ESP8285)
+ // GPIO10 (SD_DATA3 Flash QIO or ESP8285)
+ // GPIO11 (SD_CMD Flash)
+ GPIO_SWT1, // GPIO12
0,
- AGPIO(GPIO_SWT1) +1, // GPIO14
- AGPIO(GPIO_MCP39F5_RST), // GPIO15 MCP39F501 Reset
+ GPIO_SWT2, // GPIO14
+ GPIO_MCP39F5_RST, // GPIO15 MCP39F501 Reset
0,
0
},
- { // PHILIPS - Xiaomi Philips bulb (ESP8266)
+ { // PHILIPS - Xiaomi Philips bulb (ESP8266)
0, 0, 0, 0, 0, 0,
- // GPIO06 (SD_CLK Flash)
- // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT)
- // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT)
- // GPIO09 (SD_DATA2 Flash QIO or ESP8285)
- // GPIO10 (SD_DATA3 Flash QIO or ESP8285)
- // GPIO11 (SD_CMD Flash)
- AGPIO(GPIO_PWM1) +1, // GPIO12 cold/warm light
+ // GPIO06 (SD_CLK Flash)
+ // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT)
+ // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT)
+ // GPIO09 (SD_DATA2 Flash QIO or ESP8285)
+ // GPIO10 (SD_DATA3 Flash QIO or ESP8285)
+ // GPIO11 (SD_CMD Flash)
+ GPIO_PWM2, // GPIO12 cold/warm light
0, 0,
- AGPIO(GPIO_PWM1), // GPIO15 light intensity
+ GPIO_PWM1, // GPIO15 light intensity
0, 0
},
- { // NEO_COOLCAM - Neo Coolcam (ESP8266)
- // https://www.banggood.com/NEO-COOLCAM-WiFi-Mini-Smart-Plug-APP-Remote-Control-Timing-Smart-Socket-EU-Plug-p-1288562.html?cur_warehouse=CN
+ { // NEO_COOLCAM - Neo Coolcam (ESP8266)
+ // https://www.banggood.com/NEO-COOLCAM-WiFi-Mini-Smart-Plug-APP-Remote-Control-Timing-Smart-Socket-EU-Plug-p-1288562.html?cur_warehouse=CN
0, 0, 0, 0,
- AGPIO(GPIO_LED1_INV), // GPIO04 Red Led (0 = On, 1 = Off) - Link and Power status
+ GPIO_LED1_INV, // GPIO04 Red Led (0 = On, 1 = Off) - Link and Power status
0,
- // GPIO06 (SD_CLK Flash)
- // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT)
- // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT)
- // GPIO09 (SD_DATA2 Flash QIO or ESP8285)
- // GPIO10 (SD_DATA3 Flash QIO or ESP8285)
- // GPIO11 (SD_CMD Flash)
- AGPIO(GPIO_REL1), // GPIO12 Red Led and Relay (0 = Off, 1 = On)
- AGPIO(GPIO_KEY1), // GPIO13 Button
+ // GPIO06 (SD_CLK Flash)
+ // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT)
+ // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT)
+ // GPIO09 (SD_DATA2 Flash QIO or ESP8285)
+ // GPIO10 (SD_DATA3 Flash QIO or ESP8285)
+ // GPIO11 (SD_CMD Flash)
+ GPIO_REL1, // GPIO12 Red Led and Relay (0 = Off, 1 = On)
+ GPIO_KEY1, // GPIO13 Button
0, 0, 0, 0
},
- { // ESP_SWITCH - Michael Haustein 4 channel wall switch (ESP07 = ESP8266)
- // Use rules for further actions like - rule on power1#state do publish cmnd/other_device/power %value% endon
- AGPIO(GPIO_KEY1) +1, // GPIO00 Button 2
- AGPIO(GPIO_USER), // GPIO01 Serial RXD and Optional sensor
- AGPIO(GPIO_REL1_INV) +2, // GPIO02 Yellow Led 3 (0 = On, 1 = Off)
- AGPIO(GPIO_USER), // GPIO03 Serial TXD and Optional sensor
- AGPIO(GPIO_KEY1), // GPIO04 Button 1
- AGPIO(GPIO_REL1_INV) +1, // GPIO05 Red Led 2 (0 = On, 1 = Off)
- // GPIO06 (SD_CLK Flash)
- // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT)
- // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT)
- // GPIO09 (SD_DATA2 Flash QIO or ESP8285)
- // GPIO10 (SD_DATA3 Flash QIO or ESP8285)
- // GPIO11 (SD_CMD Flash)
- AGPIO(GPIO_REL1_INV) +3, // GPIO12 Blue Led 4 (0 = On, 1 = Off)
- AGPIO(GPIO_KEY1) +3, // GPIO13 Button 4
- AGPIO(GPIO_KEY1) +2, // GPIO14 Button 3
- AGPIO(GPIO_LED1), // GPIO15 Optional sensor
- AGPIO(GPIO_REL1_INV), // GPIO16 Green Led 1 (0 = On, 1 = Off)
+ { // ESP_SWITCH - Michael Haustein 4 channel wall switch (ESP07 = ESP8266)
+ // Use rules for further actions like - rule on power1#state do publish cmnd/other_device/power %value% endon
+ GPIO_KEY2, // GPIO00 Button 2
+ GPIO_USER, // GPIO01 Serial RXD and Optional sensor
+ GPIO_REL3_INV, // GPIO02 Yellow Led 3 (0 = On, 1 = Off)
+ GPIO_USER, // GPIO03 Serial TXD and Optional sensor
+ GPIO_KEY1, // GPIO04 Button 1
+ GPIO_REL2_INV, // GPIO05 Red Led 2 (0 = On, 1 = Off)
+ // GPIO06 (SD_CLK Flash)
+ // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT)
+ // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT)
+ // GPIO09 (SD_DATA2 Flash QIO or ESP8285)
+ // GPIO10 (SD_DATA3 Flash QIO or ESP8285)
+ // GPIO11 (SD_CMD Flash)
+ GPIO_REL4_INV, // GPIO12 Blue Led 4 (0 = On, 1 = Off)
+ GPIO_KEY4, // GPIO13 Button 4
+ GPIO_KEY3, // GPIO14 Button 3
+ GPIO_LED1, // GPIO15 Optional sensor
+ GPIO_REL1_INV, // GPIO16 Green Led 1 (0 = On, 1 = Off)
0
},
- { // OBI - OBI socket (ESP8266) - https://www.obi.de/hausfunksteuerung/wifi-stecker-schuko/p/2291706
- AGPIO(GPIO_USER), // GPIO00
- AGPIO(GPIO_USER), // GPIO01 Serial RXD
+ { // OBI - OBI socket (ESP8266) - https://www.obi.de/hausfunksteuerung/wifi-stecker-schuko/p/2291706
+ GPIO_USER, // GPIO00
+ GPIO_USER, // GPIO01 Serial RXD
0,
- AGPIO(GPIO_USER), // GPIO03 Serial TXD
- AGPIO(GPIO_LED1), // GPIO04 Blue LED - Link and Power status
- AGPIO(GPIO_REL1), // GPIO05 (Relay OFF, but used as Relay Switch)
- // GPIO06 (SD_CLK Flash)
- // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT)
- // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT)
- // GPIO09 (SD_DATA2 Flash QIO or ESP8285)
- // GPIO10 (SD_DATA3 Flash QIO or ESP8285)
- // GPIO11 (SD_CMD Flash)
- AGPIO(GPIO_LED1) +2, // GPIO12 (Relay ON, but set to LOW, so we can switch with GPIO05)
- AGPIO(GPIO_USER), // GPIO13
- AGPIO(GPIO_KEY1), // GPIO14 Button
+ GPIO_USER, // GPIO03 Serial TXD
+ GPIO_LED1, // GPIO04 Blue LED - Link and Power status
+ GPIO_REL1, // GPIO05 (Relay OFF, but used as Relay Switch)
+ // GPIO06 (SD_CLK Flash)
+ // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT)
+ // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT)
+ // GPIO09 (SD_DATA2 Flash QIO or ESP8285)
+ // GPIO10 (SD_DATA3 Flash QIO or ESP8285)
+ // GPIO11 (SD_CMD Flash)
+ GPIO_LED3, // GPIO12 (Relay ON, but set to LOW, so we can switch with GPIO05)
+ GPIO_USER, // GPIO13
+ GPIO_KEY1, // GPIO14 Button
0,
- AGPIO(GPIO_USER), // GPIO16
- AGPIO(ADC0_USER) // ADC0 A0 Analog input
+ GPIO_USER, // GPIO16
+ ADC0_USER // ADC0 A0 Analog input
},
- { // TECKIN - https://www.amazon.de/gp/product/B07D5V139R
+ { // TECKIN - https://www.amazon.de/gp/product/B07D5V139R
0,
- AGPIO(GPIO_KEY1), // GPIO01 Serial TXD and Button
+ GPIO_KEY1, // GPIO01 Serial TXD and Button
0,
- AGPIO(GPIO_LED1_INV), // GPIO03 Serial RXD and Red Led (0 = On, 1 = Off) - Power status
- AGPIO(GPIO_HJL_CF), // GPIO04 BL0937 or HJL-01 CF power
- AGPIO(GPIO_NRG_CF1), // GPIO05 BL0937 or HJL-01 CF1 current / voltage
- // GPIO06 (SD_CLK Flash)
- // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT)
- // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT)
- // GPIO09 (SD_DATA2 Flash QIO or ESP8285)
- // GPIO10 (SD_DATA3 Flash QIO or ESP8285)
- // GPIO11 (SD_CMD Flash)
- AGPIO(GPIO_NRG_SEL_INV), // GPIO12 BL0937 or HJL-01 Sel output (0 = Voltage)
- AGPIO(GPIO_LEDLNK_INV), // GPIO13 Blue Led (0 = On, 1 = Off) - Link status
- AGPIO(GPIO_REL1), // GPIO14 Relay (0 = Off, 1 = On)
+ GPIO_LED1_INV, // GPIO03 Serial RXD and Red Led (0 = On, 1 = Off) - Power status
+ GPIO_HJL_CF, // GPIO04 BL0937 or HJL-01 CF power
+ GPIO_NRG_CF1, // GPIO05 BL0937 or HJL-01 CF1 current / voltage
+ // GPIO06 (SD_CLK Flash)
+ // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT)
+ // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT)
+ // GPIO09 (SD_DATA2 Flash QIO or ESP8285)
+ // GPIO10 (SD_DATA3 Flash QIO or ESP8285)
+ // GPIO11 (SD_CMD Flash)
+ GPIO_NRG_SEL_INV, // GPIO12 BL0937 or HJL-01 Sel output (0 = Voltage)
+ GPIO_LEDLNK_INV, // GPIO13 Blue Led (0 = On, 1 = Off) - Link status
+ GPIO_REL1, // GPIO14 Relay (0 = Off, 1 = On)
0, 0, 0
},
- { // APLIC_WDP303075 - Aplic WDP 303075 (ESP8285 - HLW8012 Energy Monitoring)
- // https://www.amazon.de/dp/B07CNWVNJ2
+ { // APLIC_WDP303075 - Aplic WDP 303075 (ESP8285 - HLW8012 Energy Monitoring)
+ // https://www.amazon.de/dp/B07CNWVNJ2
0, 0, 0,
- AGPIO(GPIO_KEY1), // GPIO03 Button
- AGPIO(GPIO_HLW_CF), // GPIO04 HLW8012 CF power
- AGPIO(GPIO_NRG_CF1), // GPIO05 HLW8012 CF1 current / voltage
- // GPIO06 (SD_CLK Flash)
- // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT)
- // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT)
- // GPIO09 (SD_DATA2 Flash QIO or ESP8285)
- // GPIO10 (SD_DATA3 Flash QIO or ESP8285)
- // GPIO11 (SD_CMD Flash)
- AGPIO(GPIO_NRG_SEL_INV), // GPIO12 HLW8012 CF Sel output (0 = Voltage)
- AGPIO(GPIO_LED1_INV), // GPIO13 LED (0 = On, 1 = Off) - Link and Power status
- AGPIO(GPIO_REL1), // GPIO14 Relay SRU 5VDC SDA (0 = Off, 1 = On )
+ GPIO_KEY1, // GPIO03 Button
+ GPIO_HLW_CF, // GPIO04 HLW8012 CF power
+ GPIO_NRG_CF1, // GPIO05 HLW8012 CF1 current / voltage
+ // GPIO06 (SD_CLK Flash)
+ // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT)
+ // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT)
+ // GPIO09 (SD_DATA2 Flash QIO or ESP8285)
+ // GPIO10 (SD_DATA3 Flash QIO or ESP8285)
+ // GPIO11 (SD_CMD Flash)
+ GPIO_NRG_SEL_INV, // GPIO12 HLW8012 CF Sel output (0 = Voltage)
+ GPIO_LED1_INV, // GPIO13 LED (0 = On, 1 = Off) - Link and Power status
+ GPIO_REL1, // GPIO14 Relay SRU 5VDC SDA (0 = Off, 1 = On )
0, 0, 0
},
- { // TUYA_DIMMER - Tuya MCU device (ESP8266 w/ separate MCU)
- // https://www.amazon.com/gp/product/B07CTNSZZ8/ref=oh_aui_detailpage_o00_s00?ie=UTF8&psc=1
- AGPIO(GPIO_USER), // Virtual Button (controlled by MCU)
- AGPIO(GPIO_USER), // GPIO01 MCU serial control
- AGPIO(GPIO_USER),
- AGPIO(GPIO_USER), // GPIO03 MCU serial control
- AGPIO(GPIO_USER),
- AGPIO(GPIO_USER),
- // GPIO06 (SD_CLK Flash)
- // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT)
- // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT)
- // GPIO09 (SD_DATA2 Flash QIO or ESP8285)
- // GPIO10 (SD_DATA3 Flash QIO or ESP8285)
- // GPIO11 (SD_CMD Flash)
- AGPIO(GPIO_USER),
- AGPIO(GPIO_USER),
- AGPIO(GPIO_USER), // GPIO14 Green Led
- AGPIO(GPIO_USER),
- AGPIO(GPIO_USER),
+ { // TUYA_DIMMER - Tuya MCU device (ESP8266 w/ separate MCU)
+ // https://www.amazon.com/gp/product/B07CTNSZZ8/ref=oh_aui_detailpage_o00_s00?ie=UTF8&psc=1
+ GPIO_USER, // Virtual Button (controlled by MCU)
+ GPIO_USER, // GPIO01 MCU serial control
+ GPIO_USER,
+ GPIO_USER, // GPIO03 MCU serial control
+ GPIO_USER,
+ GPIO_USER,
+ // GPIO06 (SD_CLK Flash)
+ // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT)
+ // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT)
+ // GPIO09 (SD_DATA2 Flash QIO or ESP8285)
+ // GPIO10 (SD_DATA3 Flash QIO or ESP8285)
+ // GPIO11 (SD_CMD Flash)
+ GPIO_USER,
+ GPIO_USER,
+ GPIO_USER, // GPIO14 Green Led
+ GPIO_USER,
+ GPIO_USER,
0
},
- { // GOSUND - https://www.amazon.de/gp/product/B0777BWS1P
+ { // GOSUND - https://www.amazon.de/gp/product/B0777BWS1P
0,
- AGPIO(GPIO_LEDLNK_INV), // GPIO01 Serial RXD and LED1 (blue) inv - Link status
+ GPIO_LEDLNK_INV, // GPIO01 Serial RXD and LED1 (blue) inv - Link status
0,
- AGPIO(GPIO_KEY1), // GPIO03 Serial TXD and Button
- AGPIO(GPIO_HJL_CF), // GPIO04 BL0937 or HJL-01 CF power
- AGPIO(GPIO_NRG_CF1), // GPIO05 BL0937 or HJL-01 CF1 current / voltage
- // GPIO06 (SD_CLK Flash)
- // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT)
- // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT)
- // GPIO09 (SD_DATA2 Flash QIO or ESP8285)
- // GPIO10 (SD_DATA3 Flash QIO or ESP8285)
- // GPIO11 (SD_CMD Flash)
- AGPIO(GPIO_NRG_SEL_INV), // GPIO12 BL0937 or HJL-01 Sel output (0 = Voltage)
- AGPIO(GPIO_LED1_INV), // GPIO13 LED2 (red) inv - Power status
- AGPIO(GPIO_REL1), // GPIO14 Relay (0 = Off, 1 = On)
+ GPIO_KEY1, // GPIO03 Serial TXD and Button
+ GPIO_HJL_CF, // GPIO04 BL0937 or HJL-01 CF power
+ GPIO_NRG_CF1, // GPIO05 BL0937 or HJL-01 CF1 current / voltage
+ // GPIO06 (SD_CLK Flash)
+ // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT)
+ // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT)
+ // GPIO09 (SD_DATA2 Flash QIO or ESP8285)
+ // GPIO10 (SD_DATA3 Flash QIO or ESP8285)
+ // GPIO11 (SD_CMD Flash)
+ GPIO_NRG_SEL_INV, // GPIO12 BL0937 or HJL-01 Sel output (0 = Voltage)
+ GPIO_LED1_INV, // GPIO13 LED2 (red) inv - Power status
+ GPIO_REL1, // GPIO14 Relay (0 = Off, 1 = On)
0, 0, 0
},
- { // ARMTRONIX_DIMMERS - ARMTRONIX Dimmer, one or two channel (ESP8266 w/ separate MCU dimmer)
- // https://www.tindie.com/products/Armtronix/wifi-ac-dimmer-two-triac-board/
- // https://www.tindie.com/products/Armtronix/wifi-ac-dimmer-esp8266-one-triac-board-alexaecho/
- AGPIO(GPIO_USER),
- AGPIO(GPIO_TXD), // GPIO01 MCU serial control
- AGPIO(GPIO_USER),
- AGPIO(GPIO_RXD), // GPIO03 MCU serial control
- AGPIO(GPIO_USER),
- AGPIO(GPIO_USER),
- // GPIO06 (SD_CLK Flash)
- // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT)
- // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT)
- // GPIO09 (SD_DATA2 Flash QIO or ESP8285)
- // GPIO10 (SD_DATA3 Flash QIO or ESP8285)
- // GPIO11 (SD_CMD Flash)
- AGPIO(GPIO_USER),
- AGPIO(GPIO_USER),
- AGPIO(GPIO_USER),
- AGPIO(GPIO_USER),
- AGPIO(GPIO_USER),
+ { // ARMTRONIX_DIMMERS - ARMTRONIX Dimmer, one or two channel (ESP8266 w/ separate MCU dimmer)
+ // https://www.tindie.com/products/Armtronix/wifi-ac-dimmer-two-triac-board/
+ // https://www.tindie.com/products/Armtronix/wifi-ac-dimmer-esp8266-one-triac-board-alexaecho/
+ GPIO_USER,
+ GPIO_TXD, // GPIO01 MCU serial control
+ GPIO_USER,
+ GPIO_RXD, // GPIO03 MCU serial control
+ GPIO_USER,
+ GPIO_USER,
+ // GPIO06 (SD_CLK Flash)
+ // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT)
+ // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT)
+ // GPIO09 (SD_DATA2 Flash QIO or ESP8285)
+ // GPIO10 (SD_DATA3 Flash QIO or ESP8285)
+ // GPIO11 (SD_CMD Flash)
+ GPIO_USER,
+ GPIO_USER,
+ GPIO_USER,
+ GPIO_USER,
+ GPIO_USER,
0
},
- { // SK03_TUYA - Outdoor smart plug with power monitoring HLW8012 chip - https://www.amazon.com/gp/product/B07CG7MBPV
- AGPIO(GPIO_KEY1), // GPIO00 Button
+ { // SK03_TUYA - Outdoor smart plug with power monitoring HLW8012 chip - https://www.amazon.com/gp/product/B07CG7MBPV
+ GPIO_KEY1, // GPIO00 Button
0, 0, 0,
- AGPIO(GPIO_HLW_CF), // GPIO04 HLW8012 CF power
- AGPIO(GPIO_NRG_CF1), // GPIO05 HLW8012 CF1 current / voltage
- // GPIO06 (SD_CLK Flash)
- // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT)
- // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT)
- // GPIO09 (SD_DATA2 Flash QIO or ESP8285)
- // GPIO10 (SD_DATA3 Flash QIO or ESP8285)
- // GPIO11 (SD_CMD Flash)
- AGPIO(GPIO_NRG_SEL_INV), // GPIO12 HLW8012 CF Sel output (0 = Voltage)
- AGPIO(GPIO_LED1_INV), // GPIO13 Red Led (0 = On, 1 = Off) - Power status
- AGPIO(GPIO_LEDLNK_INV), // GPIO14 Blue Led (0 = On, 1 = Off) - Link status
- AGPIO(GPIO_REL1), // GPIO15 Relay (0 = Off, 1 = On)
+ GPIO_HLW_CF, // GPIO04 HLW8012 CF power
+ GPIO_NRG_CF1, // GPIO05 HLW8012 CF1 current / voltage
+ // GPIO06 (SD_CLK Flash)
+ // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT)
+ // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT)
+ // GPIO09 (SD_DATA2 Flash QIO or ESP8285)
+ // GPIO10 (SD_DATA3 Flash QIO or ESP8285)
+ // GPIO11 (SD_CMD Flash)
+ GPIO_NRG_SEL_INV, // GPIO12 HLW8012 CF Sel output (0 = Voltage)
+ GPIO_LED1_INV, // GPIO13 Red Led (0 = On, 1 = Off) - Power status
+ GPIO_LEDLNK_INV, // GPIO14 Blue Led (0 = On, 1 = Off) - Link status
+ GPIO_REL1, // GPIO15 Relay (0 = Off, 1 = On)
0, 0
},
- { // PS_16_DZ - PS-16-DZ Dimmer (ESP8266 w/ separate Nuvoton MCU dimmer)
- // https://www.aliexpress.com/item/SM-Smart-WIFI-Wall-Dimmer-Light-Switch-US-Ewelink-APP-Remote-Control-Wi-Fi-Wirele-Work/32871151902.html
- AGPIO(GPIO_USER),
- AGPIO(GPIO_TXD), // GPIO01 MCU serial control
- AGPIO(GPIO_USER),
- AGPIO(GPIO_RXD), // GPIO03 MCU serial control
- AGPIO(GPIO_USER),
- AGPIO(GPIO_USER),
- // GPIO06 (SD_CLK Flash)
- // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT)
- // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT)
- // GPIO09 (SD_DATA2 Flash QIO or ESP8285)
- // GPIO10 (SD_DATA3 Flash QIO or ESP8285)
- // GPIO11 (SD_CMD Flash)
- AGPIO(GPIO_USER),
- AGPIO(GPIO_LED1), // GPIO13 WiFi LED - Link and Power status
- AGPIO(GPIO_USER),
- AGPIO(GPIO_USER),
- AGPIO(GPIO_USER),
+ { // PS_16_DZ - PS-16-DZ Dimmer (ESP8266 w/ separate Nuvoton MCU dimmer)
+ // https://www.aliexpress.com/item/SM-Smart-WIFI-Wall-Dimmer-Light-Switch-US-Ewelink-APP-Remote-Control-Wi-Fi-Wirele-Work/32871151902.html
+ GPIO_USER,
+ GPIO_TXD, // GPIO01 MCU serial control
+ GPIO_USER,
+ GPIO_RXD, // GPIO03 MCU serial control
+ GPIO_USER,
+ GPIO_USER,
+ // GPIO06 (SD_CLK Flash)
+ // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT)
+ // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT)
+ // GPIO09 (SD_DATA2 Flash QIO or ESP8285)
+ // GPIO10 (SD_DATA3 Flash QIO or ESP8285)
+ // GPIO11 (SD_CMD Flash)
+ GPIO_USER,
+ GPIO_LED1, // GPIO13 WiFi LED - Link and Power status
+ GPIO_USER,
+ GPIO_USER,
+ GPIO_USER,
0
},
- { // TECKIN_US - Teckin SP20 US with Energy Monitoring
- // https://www.amazon.com/Outlet-Compatible-Monitoring-Function-Required/dp/B079Q5W22B
- // https://www.amazon.com/Outlet-ZOOZEE-Monitoring-Function-Compatible/dp/B07J2LR5KN
- AGPIO(GPIO_LED1_INV), // GPIO00 Red Led (1 = On, 0 = Off) - Power status
+ { // TECKIN_US - Teckin SP20 US with Energy Monitoring
+ // https://www.amazon.com/Outlet-Compatible-Monitoring-Function-Required/dp/B079Q5W22B
+ // https://www.amazon.com/Outlet-ZOOZEE-Monitoring-Function-Compatible/dp/B07J2LR5KN
+ GPIO_LED1_INV, // GPIO00 Red Led (1 = On, 0 = Off) - Power status
0,
- AGPIO(GPIO_LEDLNK_INV), // GPIO02 Blue Led (1 = On, 0 = Off) - Link status
+ GPIO_LEDLNK_INV, // GPIO02 Blue Led (1 = On, 0 = Off) - Link status
0,
- AGPIO(GPIO_REL1), // GPIO04 Relay (0 = Off, 1 = On)
- AGPIO(GPIO_HJL_CF), // GPIO05 BL0937 or HJL-01 CF power
- // GPIO06 (SD_CLK Flash)
- // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT)
- // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT)
- // GPIO09 (SD_DATA2 Flash QIO or ESP8285)
- // GPIO10 (SD_DATA3 Flash QIO or ESP8285)
- // GPIO11 (SD_CMD Flash)
- AGPIO(GPIO_NRG_SEL_INV), // GPIO12 BL0937 or HJL-01 Sel output (0 = Voltage)
- AGPIO(GPIO_KEY1), // GPIO13 Button
- AGPIO(GPIO_NRG_CF1), // GPIO14 BL0937 or HJL-01 CF1 current / voltage
+ GPIO_REL1, // GPIO04 Relay (0 = Off, 1 = On)
+ GPIO_HJL_CF, // GPIO05 BL0937 or HJL-01 CF power
+ // GPIO06 (SD_CLK Flash)
+ // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT)
+ // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT)
+ // GPIO09 (SD_DATA2 Flash QIO or ESP8285)
+ // GPIO10 (SD_DATA3 Flash QIO or ESP8285)
+ // GPIO11 (SD_CMD Flash)
+ GPIO_NRG_SEL_INV, // GPIO12 BL0937 or HJL-01 Sel output (0 = Voltage)
+ GPIO_KEY1, // GPIO13 Button
+ GPIO_NRG_CF1, // GPIO14 BL0937 or HJL-01 CF1 current / voltage
0, 0, 0
},
- { // MANZOKU_EU_4 - "MANZOKU" labeled power strip, EU version
- // https://www.amazon.de/Steckdosenleiste-AOFO-Mehrfachsteckdose-Überspannungsschutz-Sprachsteuerung/dp/B07GBSD11P/
- // https://www.amazon.de/Steckdosenleiste-Geekbes-USB-Anschluss-Kompatibel-gesteuert/dp/B078W23BW9/
- 0, // GPIO00
- 0, // GPIO01 Serial RXD
+ { // MANZOKU_EU_4 - "MANZOKU" labeled power strip, EU version
+ // https://www.amazon.de/Steckdosenleiste-AOFO-Mehrfachsteckdose-Überspannungsschutz-Sprachsteuerung/dp/B07GBSD11P/
+ // https://www.amazon.de/Steckdosenleiste-Geekbes-USB-Anschluss-Kompatibel-gesteuert/dp/B078W23BW9/
+ 0, // GPIO00
+ 0, // GPIO01 Serial RXD
0,
- AGPIO(GPIO_KEY1), // GPIO03 Serial TXD + Button
- AGPIO(GPIO_REL1) +1, // GPIO04 Relay 2
- AGPIO(GPIO_REL1), // GPIO05 Relay 1
- // GPIO06 (SD_CLK Flash)
- // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT)
- // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT)
- // GPIO09 (SD_DATA2 Flash QIO or ESP8285)
- // GPIO10 (SD_DATA3 Flash QIO or ESP8285)
- // GPIO11 (SD_CMD Flash)
- AGPIO(GPIO_REL1) +2, // GPIO12 Relay 3
- AGPIO(GPIO_REL1) +3, // GPIO13 Relay 4
- AGPIO(GPIO_USER), // GPIO14
+ GPIO_KEY1, // GPIO03 Serial TXD + Button
+ GPIO_REL2, // GPIO04 Relay 2
+ GPIO_REL1, // GPIO05 Relay 1
+ // GPIO06 (SD_CLK Flash)
+ // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT)
+ // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT)
+ // GPIO09 (SD_DATA2 Flash QIO or ESP8285)
+ // GPIO10 (SD_DATA3 Flash QIO or ESP8285)
+ // GPIO11 (SD_CMD Flash)
+ GPIO_REL3, // GPIO12 Relay 3
+ GPIO_REL4, // GPIO13 Relay 4
+ GPIO_USER, // GPIO14
0,
- AGPIO(GPIO_USER), // GPIO16
+ GPIO_USER, // GPIO16
0
},
- { // OBI2 - OBI socket (ESP8266) - https://www.obi.de/hausfunksteuerung/wifi-stecker-schuko-2-stueck-weiss/p/4077673
- 0, // GPIO00
- 0, // GPIO01 Serial RXD
+ { // OBI2 - OBI socket (ESP8266) - https://www.obi.de/hausfunksteuerung/wifi-stecker-schuko-2-stueck-weiss/p/4077673
+ 0, // GPIO00
+ 0, // GPIO01 Serial RXD
0,
- 0, // GPIO03 Serial TXD
- AGPIO(GPIO_REL1), // GPIO04 Relay 1
- AGPIO(GPIO_KEY1), // GPIO05 Button
- // GPIO06 (SD_CLK Flash)
- // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT)
- // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT)
- // GPIO09 (SD_DATA2 Flash QIO or ESP8285)
- // GPIO10 (SD_DATA3 Flash QIO or ESP8285)
- // GPIO11 (SD_CMD Flash)
- AGPIO(GPIO_LEDLNK_INV), // GPIO12 Green LED - Link status
- AGPIO(GPIO_LED1), // GPIO13 Red LED - Power status
+ 0, // GPIO03 Serial TXD
+ GPIO_REL1, // GPIO04 Relay 1
+ GPIO_KEY1, // GPIO05 Button
+ // GPIO06 (SD_CLK Flash)
+ // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT)
+ // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT)
+ // GPIO09 (SD_DATA2 Flash QIO or ESP8285)
+ // GPIO10 (SD_DATA3 Flash QIO or ESP8285)
+ // GPIO11 (SD_CMD Flash)
+ GPIO_LEDLNK_INV, // GPIO12 Green LED - Link status
+ GPIO_LED1, // GPIO13 Red LED - Power status
0, 0, 0, 0
},
- { // YTF_IR_BRIDGE - https://www.aliexpress.com/item/Tuya-universal-Smart-IR-Hub-remote-control-Voice-Control-AC-TV-Work-With-Alexa-Google-Home/32951202513.html
- AGPIO(GPIO_USER), // GPIO00
- AGPIO(GPIO_USER), // GPIO01 Serial RXD
- AGPIO(GPIO_USER), // GPIO02
- AGPIO(GPIO_USER), // GPIO03 Serial TXD
- AGPIO(GPIO_LED1_INV), // GPIO04 Blue Led - Link status
- AGPIO(GPIO_IRRECV), // GPIO05 IR Receiver
- // GPIO06 (SD_CLK Flash)
- // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT)
- // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT)
- // GPIO09 (SD_DATA2 Flash QIO or ESP8285)
- // GPIO10 (SD_DATA3 Flash QIO or ESP8285)
- // GPIO11 (SD_CMD Flash)
- 0, // GPIO12
- AGPIO(GPIO_KEY1), // GPIO13 Button
- AGPIO(GPIO_IRSEND), // GPIO14 IR Transmitter
+ { // YTF_IR_BRIDGE - https://www.aliexpress.com/item/Tuya-universal-Smart-IR-Hub-remote-control-Voice-Control-AC-TV-Work-With-Alexa-Google-Home/32951202513.html
+ GPIO_USER, // GPIO00
+ GPIO_USER, // GPIO01 Serial RXD
+ GPIO_USER, // GPIO02
+ GPIO_USER, // GPIO03 Serial TXD
+ GPIO_LED1_INV, // GPIO04 Blue Led - Link status
+ GPIO_IRRECV, // GPIO05 IR Receiver
+ // GPIO06 (SD_CLK Flash)
+ // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT)
+ // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT)
+ // GPIO09 (SD_DATA2 Flash QIO or ESP8285)
+ // GPIO10 (SD_DATA3 Flash QIO or ESP8285)
+ // GPIO11 (SD_CMD Flash)
+ 0, // GPIO12
+ GPIO_KEY1, // GPIO13 Button
+ GPIO_IRSEND, // GPIO14 IR Transmitter
0, 0, 0
},
- { // DIGOO - Digoo DG-SP202
- // https://www.banggood.com/DIGOO-DG-SP202-Dual-EU-Plug-Smart-WIFI-Socket-Individual-Controllable-Energy-Monitor-Remote-Control-Timing-Smart-Home-Outlet-let-p-1375323.html
- AGPIO(GPIO_KEY1), // GPIO00 Button1
- 0, // GPIO01 Serial RXD
- 0, // GPIO02
- 0, // GPIO03 Serial TXD
- AGPIO(GPIO_HJL_CF), // GPIO04 BL0937 or HJL-01 CF power
- AGPIO(GPIO_NRG_CF1), // GPIO05 BL0937 or HJL-01 CF1 current / voltage
- // GPIO06 (SD_CLK Flash)
- // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT)
- // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT)
- // GPIO09 (SD_DATA2 Flash QIO or ESP8285)
- // GPIO10 (SD_DATA3 Flash QIO or ESP8285)
- // GPIO11 (SD_CMD Flash)
- AGPIO(GPIO_NRG_SEL_INV), // GPIO12 BL0937 or HJL-01 Sel output (0 = Voltage)
- AGPIO(GPIO_LED1), // GPIO13 Blue Leds - Link Status
- AGPIO(GPIO_REL1) +1, // GPIO14 Relay2 (0 = Off, 1 = On) and Red Led
- AGPIO(GPIO_REL1), // GPIO15 Relay1 (0 = Off, 1 = On) and Red Led
- AGPIO(GPIO_KEY1_NP) +1, // GPIO16 Button2, externally pulled up
+ { // DIGOO - Digoo DG-SP202
+ // https://www.banggood.com/DIGOO-DG-SP202-Dual-EU-Plug-Smart-WIFI-Socket-Individual-Controllable-Energy-Monitor-Remote-Control-Timing-Smart-Home-Outlet-let-p-1375323.html
+ GPIO_KEY1, // GPIO00 Button1
+ 0, // GPIO01 Serial RXD
+ 0, // GPIO02
+ 0, // GPIO03 Serial TXD
+ GPIO_HJL_CF, // GPIO04 BL0937 or HJL-01 CF power
+ GPIO_NRG_CF1, // GPIO05 BL0937 or HJL-01 CF1 current / voltage
+ // GPIO06 (SD_CLK Flash)
+ // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT)
+ // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT)
+ // GPIO09 (SD_DATA2 Flash QIO or ESP8285)
+ // GPIO10 (SD_DATA3 Flash QIO or ESP8285)
+ // GPIO11 (SD_CMD Flash)
+ GPIO_NRG_SEL_INV, // GPIO12 BL0937 or HJL-01 Sel output (0 = Voltage)
+ GPIO_LED1, // GPIO13 Blue Leds - Link Status
+ GPIO_REL2, // GPIO14 Relay2 (0 = Off, 1 = On) and Red Led
+ GPIO_REL1, // GPIO15 Relay1 (0 = Off, 1 = On) and Red Led
+ GPIO_KEY2_NP, // GPIO16 Button2, externally pulled up
0
},
- { // KA10 - SMANERGY KA10 (ESP8285 - BL0937 Energy Monitoring) - https://www.amazon.es/dp/B07MBTCH2Y
- 0, // GPIO00
- AGPIO(GPIO_LEDLNK_INV), // GPIO01 Blue LED - Link status
- 0, // GPIO02
- AGPIO(GPIO_KEY1), // GPIO03 Button
- AGPIO(GPIO_HJL_CF), // GPIO04 BL0937 CF power
- AGPIO(GPIO_NRG_CF1), // GPIO05 BL0937 CF1 voltage / current
- // GPIO06 (SD_CLK Flash)
- // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT)
- // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT)
- // GPIO09 (SD_DATA2 Flash QIO or ESP8285)
- // GPIO10 (SD_DATA3 Flash QIO or ESP8285)
- // GPIO11 (SD_CMD Flash)
- AGPIO(GPIO_NRG_SEL_INV), // GPIO12 BL0937 Sel output (1 = Voltage)
- AGPIO(GPIO_LED1), // GPIO13 Red LED - Power status
- AGPIO(GPIO_REL1), // GPIO14 Relay 1
+ { // KA10 - SMANERGY KA10 (ESP8285 - BL0937 Energy Monitoring) - https://www.amazon.es/dp/B07MBTCH2Y
+ 0, // GPIO00
+ GPIO_LEDLNK_INV, // GPIO01 Blue LED - Link status
+ 0, // GPIO02
+ GPIO_KEY1, // GPIO03 Button
+ GPIO_HJL_CF, // GPIO04 BL0937 CF power
+ GPIO_NRG_CF1, // GPIO05 BL0937 CF1 voltage / current
+ // GPIO06 (SD_CLK Flash)
+ // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT)
+ // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT)
+ // GPIO09 (SD_DATA2 Flash QIO or ESP8285)
+ // GPIO10 (SD_DATA3 Flash QIO or ESP8285)
+ // GPIO11 (SD_CMD Flash)
+ GPIO_NRG_SEL_INV, // GPIO12 BL0937 Sel output (1 = Voltage)
+ GPIO_LED1, // GPIO13 Red LED - Power status
+ GPIO_REL1, // GPIO14 Relay 1
0, 0, 0
},
- { // ZX2820
- AGPIO(GPIO_KEY1), // GPIO00 Button
+ { // ZX2820
+ GPIO_KEY1, // GPIO00 Button
0, 0, 0,
- AGPIO(GPIO_HLW_CF), // GPIO04 HLW8012 CF power
- AGPIO(GPIO_NRG_CF1), // GPIO05 HLW8012 CF1 voltage / current
- // GPIO06 (SD_CLK Flash)
- // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT)
- // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT)
- // GPIO09 (SD_DATA2 Flash QIO or ESP8285)
- // GPIO10 (SD_DATA3 Flash QIO or ESP8285)
- // GPIO11 (SD_CMD Flash)
- AGPIO(GPIO_NRG_SEL_INV), // GPIO12 HLW8012 SEL (0 = Voltage)
- AGPIO(GPIO_LED1_INV), // GPIO13 Green Led - Link and Power status
- AGPIO(GPIO_REL1), // GPIO14 Relay
+ GPIO_HLW_CF, // GPIO04 HLW8012 CF power
+ GPIO_NRG_CF1, // GPIO05 HLW8012 CF1 voltage / current
+ // GPIO06 (SD_CLK Flash)
+ // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT)
+ // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT)
+ // GPIO09 (SD_DATA2 Flash QIO or ESP8285)
+ // GPIO10 (SD_DATA3 Flash QIO or ESP8285)
+ // GPIO11 (SD_CMD Flash)
+ GPIO_NRG_SEL_INV, // GPIO12 HLW8012 SEL (0 = Voltage)
+ GPIO_LED1_INV, // GPIO13 Green Led - Link and Power status
+ GPIO_REL1, // GPIO14 Relay
0, 0, 0
},
- { // MI_DESK_LAMP - Mi LED Desk Lamp - https://www.mi.com/global/smartlamp/
+ { // MI_DESK_LAMP - Mi LED Desk Lamp - https://www.mi.com/global/smartlamp/
0, 0,
- AGPIO(GPIO_KEY1), // GPIO02 Button
+ GPIO_KEY1, // GPIO02 Button
0,
- AGPIO(GPIO_PWM1), // GPIO04 Cold White
- AGPIO(GPIO_PWM1) +1, // GPIO05 Warm White
- // GPIO06 (SD_CLK Flash)
- // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT)
- // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT)
- // GPIO09 (SD_DATA2 Flash QIO or ESP8285)
- // GPIO10 (SD_DATA3 Flash QIO or ESP8285)
- // GPIO11 (SD_CMD Flash)
- AGPIO(GPIO_ROT1A), // GPIO12 Rotary switch A pin
- AGPIO(GPIO_ROT1B), // GPIO13 Rotary switch B pin
+ GPIO_PWM1, // GPIO04 Cold White
+ GPIO_PWM2, // GPIO05 Warm White
+ // GPIO06 (SD_CLK Flash)
+ // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT)
+ // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT)
+ // GPIO09 (SD_DATA2 Flash QIO or ESP8285)
+ // GPIO10 (SD_DATA3 Flash QIO or ESP8285)
+ // GPIO11 (SD_CMD Flash)
+ GPIO_ROT1A, // GPIO12 Rotary switch A pin
+ GPIO_ROT1B, // GPIO13 Rotary switch B pin
0, 0, 0, 0
},
- { // SP10 - Tuya SP10 (BL0937 Energy Monitoring)
- // https://www.aliexpress.com/item/Smart-Mini-WiFi-Plug-Outlet-Switch-Work-With-ForEcho-Alexa-Google-Home-Remote-EU-Smart-Socket/32963670423.html
- 0, // GPIO00
- AGPIO(GPIO_PWM1), // GPIO01 Nightlight
- 0, // GPIO02
- AGPIO(GPIO_KEY1), // GPIO03 Button
- AGPIO(GPIO_HJL_CF), // GPIO04 BL0937 CF power
- AGPIO(GPIO_NRG_CF1), // GPIO05 BL0937 CF1 voltage / current
- // GPIO06 (SD_CLK Flash)
- // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT)
- // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT)
- // GPIO09 (SD_DATA2 Flash QIO or ESP8285)
- // GPIO10 (SD_DATA3 Flash QIO or ESP8285)
- // GPIO11 (SD_CMD Flash)
- AGPIO(GPIO_NRG_SEL_INV), // GPIO12 BL0937 Sel output (1 = Voltage)
- AGPIO(GPIO_LED1), // GPIO13 Blue LED - Link status
- AGPIO(GPIO_REL1), // GPIO14 Relay and red LED
+ { // SP10 - Tuya SP10 (BL0937 Energy Monitoring)
+ // https://www.aliexpress.com/item/Smart-Mini-WiFi-Plug-Outlet-Switch-Work-With-ForEcho-Alexa-Google-Home-Remote-EU-Smart-Socket/32963670423.html
+ 0, // GPIO00
+ GPIO_PWM1, // GPIO01 Nightlight
+ 0, // GPIO02
+ GPIO_KEY1, // GPIO03 Button
+ GPIO_HJL_CF, // GPIO04 BL0937 CF power
+ GPIO_NRG_CF1, // GPIO05 BL0937 CF1 voltage / current
+ // GPIO06 (SD_CLK Flash)
+ // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT)
+ // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT)
+ // GPIO09 (SD_DATA2 Flash QIO or ESP8285)
+ // GPIO10 (SD_DATA3 Flash QIO or ESP8285)
+ // GPIO11 (SD_CMD Flash)
+ GPIO_NRG_SEL_INV, // GPIO12 BL0937 Sel output (1 = Voltage)
+ GPIO_LED1, // GPIO13 Blue LED - Link status
+ GPIO_REL1, // GPIO14 Relay and red LED
0, 0, 0
},
- { // WAGA - WAGA life CHCZ02MB (HJL-01 Energy Monitoring)
- // https://www.ebay.com/itm/332595697006
- AGPIO(GPIO_LED1_INV), // GPIO00 Red LED
- 0, // GPIO01 Serial RXD
- 0, // GPIO02
- AGPIO(GPIO_NRG_SEL_INV), // GPIO03 HJL-01 Sel output (1 = Voltage)
- 0, // GPIO04
- AGPIO(GPIO_HJL_CF), // GPIO05 HJL-01 CF power
- // GPIO06 (SD_CLK Flash)
- // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT)
- // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT)
- // GPIO09 (SD_DATA2 Flash QIO or ESP8285)
- // GPIO10 (SD_DATA3 Flash QIO or ESP8285)
- // GPIO11 (SD_CMD Flash)
- AGPIO(GPIO_REL1), // GPIO12 Relay
- AGPIO(GPIO_KEY1), // GPIO13 Button
- AGPIO(GPIO_NRG_CF1), // GPIO14 HJL-01 CF1 voltage / current
- AGPIO(GPIO_LEDLNK_INV), // GPIO15 Blue LED - Link status
+ { // WAGA - WAGA life CHCZ02MB (HJL-01 Energy Monitoring)
+ // https://www.ebay.com/itm/332595697006
+ GPIO_LED1_INV, // GPIO00 Red LED
+ 0, // GPIO01 Serial RXD
+ 0, // GPIO02
+ GPIO_NRG_SEL_INV, // GPIO03 HJL-01 Sel output (1 = Voltage)
+ 0, // GPIO04
+ GPIO_HJL_CF, // GPIO05 HJL-01 CF power
+ // GPIO06 (SD_CLK Flash)
+ // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT)
+ // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT)
+ // GPIO09 (SD_DATA2 Flash QIO or ESP8285)
+ // GPIO10 (SD_DATA3 Flash QIO or ESP8285)
+ // GPIO11 (SD_CMD Flash)
+ GPIO_REL1, // GPIO12 Relay
+ GPIO_KEY1, // GPIO13 Button
+ GPIO_NRG_CF1, // GPIO14 HJL-01 CF1 voltage / current
+ GPIO_LEDLNK_INV, // GPIO15 Blue LED - Link status
0, 0
},
- { // SYF05 - Sunyesmart SYF05 (a.k.a. Fcmila) = TYWE3S + SM16726
- // Also works with Merkury 904 RGBW Bulbs with 13 set to GPIO_SM16716_SEL
- // https://www.flipkart.com/fc-mila-bxav-xs-ad-smart-bulb/p/itmf85zgs45fzr7n
- // https://docs.tuya.com/en/hardware/WiFi-module/wifi-e3s-module.html
- // http://www.datasheet-pdf.com/PDF/SM16716-Datasheet-Sunmoon-932771
- AGPIO(GPIO_USER), // GPIO00 N.C.
- 0, // GPIO01 Serial RXD
- AGPIO(GPIO_USER), // GPIO02 N.C.
- 0, // GPIO03 Serial TXD
- AGPIO(GPIO_SM16716_CLK), // GPIO04 SM16716 Clock
- AGPIO(GPIO_PWM1), // GPIO05 White
- // GPIO06 (SD_CLK Flash)
- // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT)
- // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT)
- // GPIO09 (SD_DATA2 Flash QIO or ESP8285)
- // GPIO10 (SD_DATA3 Flash QIO or ESP8285)
- // GPIO11 (SD_CMD Flash)
- AGPIO(GPIO_USER), // GPIO12 Alt. White on some devices
- AGPIO(GPIO_USER), // GPIO13 SM16716 Enable on some devices
- AGPIO(GPIO_SM16716_DAT), // GPIO14 SM16716 Data
- 0, // GPIO15 wired to GND
- AGPIO(GPIO_USER), // GPIO16 N.C.
- AGPIO(ADC0_USER) // ADC0 A0 Analog input
+ { // SYF05 - Sunyesmart SYF05 (a.k.a. Fcmila) = TYWE3S + SM16726
+ // Also works with Merkury 904 RGBW Bulbs with 13 set to GPIO_SM16716_SEL
+ // https://www.flipkart.com/fc-mila-bxav-xs-ad-smart-bulb/p/itmf85zgs45fzr7n
+ // https://docs.tuya.com/en/hardware/WiFi-module/wifi-e3s-module.html
+ // http://www.datasheet-pdf.com/PDF/SM16716-Datasheet-Sunmoon-932771
+ GPIO_USER, // GPIO00 N.C.
+ 0, // GPIO01 Serial RXD
+ GPIO_USER, // GPIO02 N.C.
+ 0, // GPIO03 Serial TXD
+ GPIO_SM16716_CLK, // GPIO04 SM16716 Clock
+ GPIO_PWM1, // GPIO05 White
+ // GPIO06 (SD_CLK Flash)
+ // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT)
+ // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT)
+ // GPIO09 (SD_DATA2 Flash QIO or ESP8285)
+ // GPIO10 (SD_DATA3 Flash QIO or ESP8285)
+ // GPIO11 (SD_CMD Flash)
+ GPIO_USER, // GPIO12 Alt. White on some devices
+ GPIO_USER, // GPIO13 SM16716 Enable on some devices
+ GPIO_SM16716_DAT, // GPIO14 SM16716 Data
+ 0, // GPIO15 wired to GND
+ GPIO_USER, // GPIO16 N.C.
+ ADC0_USER // ADC0 A0 Analog input
},
- { // EXS_DIMMER - EX-Store WiFi Dimmer v4, two channel (ESP8266 w/ separate MCU dimmer)
- // https://ex-store.de/2-Kanal-RS232-WiFi-WLan-Dimmer-Modul-V4-fuer-Unterputzmontage-230V-3A
- // https://ex-store.de/2-Kanal-RS232-WiFi-WLan-Dimmer-Modul-V4-fuer-Unterputzmontage-230V-3A-ESP8266-V12-Stift-und-Buchsenleisten
+ { // EXS_DIMMER - EX-Store WiFi Dimmer v4, two channel (ESP8266 w/ separate MCU dimmer)
+ // https://ex-store.de/2-Kanal-RS232-WiFi-WLan-Dimmer-Modul-V4-fuer-Unterputzmontage-230V-3A
+ // https://ex-store.de/2-Kanal-RS232-WiFi-WLan-Dimmer-Modul-V4-fuer-Unterputzmontage-230V-3A-ESP8266-V12-Stift-und-Buchsenleisten
0,
- AGPIO(GPIO_TXD), // GPIO01 MCU serial control
- AGPIO(GPIO_LEDLNK), // GPIO02 LED Link
- AGPIO(GPIO_RXD), // GPIO03 MCU serial control
- AGPIO(GPIO_USER), // GPIO04
- AGPIO(GPIO_USER), // GPIO05
- // GPIO06 (SD_CLK Flash)
- // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT)
- // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT)
- // GPIO09 (SD_DATA2 Flash QIO or ESP8285)
- // GPIO10 (SD_DATA3 Flash QIO or ESP8285)
- // GPIO11 (SD_CMD Flash)
- AGPIO(GPIO_USER), // GPIO12
- AGPIO(GPIO_EXS_ENABLE), // GPIO13 EXS MCU Enable
- AGPIO(GPIO_USER), // GPIO14
- 0, // GPIO15
+ GPIO_TXD, // GPIO01 MCU serial control
+ GPIO_LEDLNK, // GPIO02 LED Link
+ GPIO_RXD, // GPIO03 MCU serial control
+ GPIO_USER, // GPIO04
+ GPIO_USER, // GPIO05
+ // GPIO06 (SD_CLK Flash)
+ // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT)
+ // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT)
+ // GPIO09 (SD_DATA2 Flash QIO or ESP8285)
+ // GPIO10 (SD_DATA3 Flash QIO or ESP8285)
+ // GPIO11 (SD_CMD Flash)
+ GPIO_USER, // GPIO12
+ GPIO_EXS_ENABLE, // GPIO13 EXS MCU Enable
+ GPIO_USER, // GPIO14
+ 0, // GPIO15
0, 0
},
- { // PWM_DIMMER - Support for Martin Jerry/acenx/Tessan/NTONPOWER SD0x PWM
- // dimmer switches. The brightness of the load for these dimmers is
- // controlled by a PWM GPIO pin. There are typically power, up & down
- // buttons and 4 LED's. Examples are:
- // https://www.amazon.com/dp/B07FXYSVR1
- // https://www.amazon.com/dp/B07V26Q3VD
- // https://www.amazon.com/dp/B07K67D43J
- // https://www.amazon.com/dp/B07TTGFWFM
- AGPIO(GPIO_KEY1) +2, // GPIO00 Up button
- AGPIO(GPIO_KEY1) +1, // GPIO01 Down button
- 0, // GPIO02
- AGPIO(GPIO_LED1_INV) +3, // GPIO03 Level 5 LED
- AGPIO(GPIO_LEDLNK_INV), // GPIO04 LED Link
- AGPIO(GPIO_LED1_INV) +2, // GPIO05 Level 4 LED
- // GPIO06 (SD_CLK Flash)
- // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT)
- // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT)
- // GPIO09 (SD_DATA2 Flash QIO or ESP8285)
- // GPIO10 (SD_DATA3 Flash QIO or ESP8285)
- // GPIO11 (SD_CMD Flash)
- AGPIO(GPIO_LED1_INV) +1, // GPIO12 Level 3 LED
- AGPIO(GPIO_PWM1), // GPIO13 Dimmer PWM
- AGPIO(GPIO_LED1_INV), // GPIO12 Level 2 LED
- AGPIO(GPIO_KEY1_INV), // GPIO15 Power button
- AGPIO(GPIO_REL1_INV), // GPIO16 Power relay/Level 1 LED
+ { // PWM_DIMMER - Support for Martin Jerry/acenx/Tessan/NTONPOWER SD0x PWM
+ // dimmer switches. The brightness of the load for these dimmers is
+ // controlled by a PWM GPIO pin. There are typically power, up & down
+ // buttons and 4 LED's. Examples are:
+ // https://www.amazon.com/dp/B07FXYSVR1
+ // https://www.amazon.com/dp/B07V26Q3VD
+ // https://www.amazon.com/dp/B07K67D43J
+ // https://www.amazon.com/dp/B07TTGFWFM
+ GPIO_KEY3, // GPIO00 Up button
+ GPIO_KEY2, // GPIO01 Down button
+ 0, // GPIO02
+ GPIO_LED4_INV, // GPIO03 Level 5 LED
+ GPIO_LEDLNK_INV, // GPIO04 LED Link
+ GPIO_LED3_INV, // GPIO05 Level 4 LED
+ // GPIO06 (SD_CLK Flash)
+ // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT)
+ // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT)
+ // GPIO09 (SD_DATA2 Flash QIO or ESP8285)
+ // GPIO10 (SD_DATA3 Flash QIO or ESP8285)
+ // GPIO11 (SD_CMD Flash)
+ GPIO_LED2_INV, // GPIO12 Level 3 LED
+ GPIO_PWM1, // GPIO13 Dimmer PWM
+ GPIO_LED1_INV, // GPIO12 Level 2 LED
+ GPIO_KEY1_INV, // GPIO15 Power button
+ GPIO_REL1_INV, // GPIO16 Power relay/Level 1 LED
0
},
- { // SONOFF_ZB_BRIDGE - Sonoff Zigbee Bridge (ESP8266)
- AGPIO(GPIO_LED1_INV), // GPIO00 Green Led (0 = On, 1 = Off) - Traffic between ESP and EFR
- AGPIO(GPIO_ZIGBEE_TX), // GPIO01 Zigbee Serial control
- 0, // GPIO02
- AGPIO(GPIO_ZIGBEE_RX), // GPIO03 Zigbee Serial control
- AGPIO(GPIO_ZIGBEE_RST), // GPIO04 Zigbee Reset
- 0, // GPIO05 EFR32 Bootloader mode (drive Low for Gecko Bootloader, inactive or high for Zigbee EmberZNet)
- // GPIO06 (SD_CLK Flash)
- // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT)
- // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT)
- // GPIO09 (SD_DATA2 Flash QIO or ESP8285)
- // GPIO10 (SD_DATA3 Flash QIO or ESP8285)
- // GPIO11 (SD_CMD Flash)
- AGPIO(GPIO_I2C_SDA), // GPIO12 I2C SDA - connected to 512KB EEPROM
- AGPIO(GPIO_LEDLNK_INV), // GPIO13 Blue Led (0 = On, 1 = Off) - Link status
- AGPIO(GPIO_I2C_SCL), // GPIO14 I2C SCL - connected to 512KB EEPROM
- 0, // GPIO15 connected to IO15 pad, also used for logging
- AGPIO(GPIO_KEY1), // GPIO16 Button
+ { // SONOFF_ZB_BRIDGE - Sonoff Zigbee Bridge (ESP8266)
+ GPIO_LED1_INV, // GPIO00 Green Led (0 = On, 1 = Off) - Traffic between ESP and EFR
+ GPIO_ZIGBEE_TX, // GPIO01 Zigbee Serial control
+ 0, // GPIO02
+ GPIO_ZIGBEE_RX, // GPIO03 Zigbee Serial control
+ GPIO_ZIGBEE_RST, // GPIO04 Zigbee Reset
+ 0, // GPIO05 EFR32 Bootloader mode (drive Low for Gecko Bootloader, inactive or high for Zigbee EmberZNet)
+ // GPIO06 (SD_CLK Flash)
+ // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT)
+ // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT)
+ // GPIO09 (SD_DATA2 Flash QIO or ESP8285)
+ // GPIO10 (SD_DATA3 Flash QIO or ESP8285)
+ // GPIO11 (SD_CMD Flash)
+ GPIO_I2C_SDA, // GPIO12 I2C SDA - connected to 512KB EEPROM
+ GPIO_LEDLNK_INV, // GPIO13 Blue Led (0 = On, 1 = Off) - Link status
+ GPIO_I2C_SCL, // GPIO14 I2C SCL - connected to 512KB EEPROM
+ 0, // GPIO15 connected to IO15 pad, also used for logging
+ GPIO_KEY1, // GPIO16 Button
0
}
};
@@ -2100,113 +2323,113 @@ const mytmplt8266 kModules8266[TMP_MAXMODULE_8285] PROGMEM = {
\*********************************************************************************************/
const mytmplt kModules8285[TMP_MAXMODULE_8266 - TMP_WEMOS] PROGMEM = {
- { // WEMOS - Any ESP8266/ESP8285 device like WeMos and NodeMCU hardware (ESP8266)
- AGPIO(GPIO_USER), // GPIO00 D3 Wemos Button Shield
- AGPIO(GPIO_USER), // GPIO01 TX Serial RXD
- AGPIO(GPIO_USER), // GPIO02 D4 Wemos DHT Shield
- AGPIO(GPIO_USER), // GPIO03 RX Serial TXD and Optional sensor
- AGPIO(GPIO_USER), // GPIO04 D2 Wemos I2C SDA
- AGPIO(GPIO_USER), // GPIO05 D1 Wemos I2C SCL / Wemos Relay Shield (0 = Off, 1 = On) / Wemos WS2812B RGB led Shield
- // GPIO06 (SD_CLK Flash)
- // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT)
- // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT)
- AGPIO(GPIO_USER), // GPIO09 (SD_DATA2 Flash QIO or ESP8285)
- AGPIO(GPIO_USER), // GPIO10 (SD_DATA3 Flash QIO or ESP8285)
- // GPIO11 (SD_CMD Flash)
- AGPIO(GPIO_USER), // GPIO12 D6
- AGPIO(GPIO_USER), // GPIO13 D7
- AGPIO(GPIO_USER), // GPIO14 D5
- AGPIO(GPIO_USER), // GPIO15 D8
- AGPIO(GPIO_USER), // GPIO16 D0 Wemos Wake
- AGPIO(ADC0_USER) // ADC0 A0 Analog input
+ { // WEMOS - Any ESP8266/ESP8285 device like WeMos and NodeMCU hardware (ESP8266)
+ GPIO_USER, // GPIO00 D3 Wemos Button Shield
+ GPIO_USER, // GPIO01 TX Serial RXD
+ GPIO_USER, // GPIO02 D4 Wemos DHT Shield
+ GPIO_USER, // GPIO03 RX Serial TXD and Optional sensor
+ GPIO_USER, // GPIO04 D2 Wemos I2C SDA
+ GPIO_USER, // GPIO05 D1 Wemos I2C SCL / Wemos Relay Shield (0 = Off, 1 = On) / Wemos WS2812B RGB led Shield
+ // GPIO06 (SD_CLK Flash)
+ // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT)
+ // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT)
+ GPIO_USER, // GPIO09 (SD_DATA2 Flash QIO or ESP8285)
+ GPIO_USER, // GPIO10 (SD_DATA3 Flash QIO or ESP8285)
+ // GPIO11 (SD_CMD Flash)
+ GPIO_USER, // GPIO12 D6
+ GPIO_USER, // GPIO13 D7
+ GPIO_USER, // GPIO14 D5
+ GPIO_USER, // GPIO15 D8
+ GPIO_USER, // GPIO16 D0 Wemos Wake
+ ADC0_USER // ADC0 A0 Analog input
},
- { // SONOFF_4CH - Sonoff 4CH (ESP8285)
- AGPIO(GPIO_KEY1), // GPIO00 Button 1
- AGPIO(GPIO_USER), // GPIO01 Serial RXD and Optional sensor
- AGPIO(GPIO_USER), // GPIO02 Optional sensor
- AGPIO(GPIO_USER), // GPIO03 Serial TXD and Optional sensor
- AGPIO(GPIO_REL1) +2, // GPIO04 Sonoff 4CH Red Led and Relay 3 (0 = Off, 1 = On)
- AGPIO(GPIO_REL1) +1, // GPIO05 Sonoff 4CH Red Led and Relay 2 (0 = Off, 1 = On)
- // GPIO06 (SD_CLK Flash)
- // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT)
- // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT)
- AGPIO(GPIO_KEY1) +1, // GPIO09 Button 2
- AGPIO(GPIO_KEY1) +2, // GPIO10 Button 3
- // GPIO11 (SD_CMD Flash)
- AGPIO(GPIO_REL1), // GPIO12 Red Led and Relay 1 (0 = Off, 1 = On) - Link and Power status
- AGPIO(GPIO_LED1_INV), // GPIO13 Blue Led (0 = On, 1 = Off)
- AGPIO(GPIO_KEY1) +3, // GPIO14 Button 4
- AGPIO(GPIO_REL1) +3, // GPIO15 Red Led and Relay 4 (0 = Off, 1 = On)
+ { // SONOFF_4CH - Sonoff 4CH (ESP8285)
+ GPIO_KEY1, // GPIO00 Button 1
+ GPIO_USER, // GPIO01 Serial RXD and Optional sensor
+ GPIO_USER, // GPIO02 Optional sensor
+ GPIO_USER, // GPIO03 Serial TXD and Optional sensor
+ GPIO_REL3, // GPIO04 Sonoff 4CH Red Led and Relay 3 (0 = Off, 1 = On)
+ GPIO_REL2, // GPIO05 Sonoff 4CH Red Led and Relay 2 (0 = Off, 1 = On)
+ // GPIO06 (SD_CLK Flash)
+ // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT)
+ // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT)
+ GPIO_KEY2, // GPIO09 Button 2
+ GPIO_KEY3, // GPIO10 Button 3
+ // GPIO11 (SD_CMD Flash)
+ GPIO_REL1, // GPIO12 Red Led and Relay 1 (0 = Off, 1 = On) - Link and Power status
+ GPIO_LED1_INV, // GPIO13 Blue Led (0 = On, 1 = Off)
+ GPIO_KEY4, // GPIO14 Button 4
+ GPIO_REL4, // GPIO15 Red Led and Relay 4 (0 = Off, 1 = On)
0, 0
},
- { // SONOFF_T12 - Sonoff T1 2CH (ESP8285)
- AGPIO(GPIO_KEY1), // GPIO00 Button 1
- AGPIO(GPIO_USER), // GPIO01 Serial RXD and Optional sensor
- AGPIO(GPIO_USER), // GPIO02 Optional Sensor (J3 Pin 5)
- AGPIO(GPIO_USER), // GPIO03 Serial TXD and Optional sensor
+ { // SONOFF_T12 - Sonoff T1 2CH (ESP8285)
+ GPIO_KEY1, // GPIO00 Button 1
+ GPIO_USER, // GPIO01 Serial RXD and Optional sensor
+ GPIO_USER, // GPIO02 Optional Sensor (J3 Pin 5)
+ GPIO_USER, // GPIO03 Serial TXD and Optional sensor
0,
- AGPIO(GPIO_REL1) +1, // GPIO05 Blue Led and Relay 2 (0 = Off, 1 = On)
- // GPIO06 (SD_CLK Flash)
- // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT)
- // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT)
- AGPIO(GPIO_KEY1) +1, // GPIO09 Button 2
- 0, // GPIO10
- // GPIO11 (SD_CMD Flash)
- AGPIO(GPIO_REL1), // GPIO12 Blue Led and Relay 1 (0 = Off, 1 = On)
- AGPIO(GPIO_LED1_INV), // GPIO13 Blue Led (0 = On, 1 = Off) - Link and Power status
+ GPIO_REL2, // GPIO05 Blue Led and Relay 2 (0 = Off, 1 = On)
+ // GPIO06 (SD_CLK Flash)
+ // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT)
+ // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT)
+ GPIO_KEY2, // GPIO09 Button 2
+ 0, // GPIO10
+ // GPIO11 (SD_CMD Flash)
+ GPIO_REL1, // GPIO12 Blue Led and Relay 1 (0 = Off, 1 = On)
+ GPIO_LED1_INV, // GPIO13 Blue Led (0 = On, 1 = Off) - Link and Power status
0, 0, 0, 0
},
- { // SONOFF_T13 - Sonoff T1 3CH (ESP8285)
- AGPIO(GPIO_KEY1), // GPIO00 Button 1
- AGPIO(GPIO_USER), // GPIO01 Serial RXD and Optional sensor
- AGPIO(GPIO_USER), // GPIO02 Optional Sensor (J3 Pin 5)
- AGPIO(GPIO_USER), // GPIO03 Serial TXD and Optional sensor
- AGPIO(GPIO_REL1) +2, // GPIO04 Blue Led and Relay 3 (0 = Off, 1 = On)
- AGPIO(GPIO_REL1) +1, // GPIO05 Blue Led and Relay 2 (0 = Off, 1 = On)
- // GPIO06 (SD_CLK Flash)
- // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT)
- // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT)
- AGPIO(GPIO_KEY1) +1, // GPIO09 Button 2
- AGPIO(GPIO_KEY1) +2, // GPIO10 Button 3
- // GPIO11 (SD_CMD Flash)
- AGPIO(GPIO_REL1), // GPIO12 Blue Led and Relay 1 (0 = Off, 1 = On)
- AGPIO(GPIO_LED1_INV), // GPIO13 Blue Led (0 = On, 1 = Off) - Link and Power status
+ { // SONOFF_T13 - Sonoff T1 3CH (ESP8285)
+ GPIO_KEY1, // GPIO00 Button 1
+ GPIO_USER, // GPIO01 Serial RXD and Optional sensor
+ GPIO_USER, // GPIO02 Optional Sensor (J3 Pin 5)
+ GPIO_USER, // GPIO03 Serial TXD and Optional sensor
+ GPIO_REL3, // GPIO04 Blue Led and Relay 3 (0 = Off, 1 = On)
+ GPIO_REL2, // GPIO05 Blue Led and Relay 2 (0 = Off, 1 = On)
+ // GPIO06 (SD_CLK Flash)
+ // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT)
+ // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT)
+ GPIO_KEY2, // GPIO09 Button 2
+ GPIO_KEY3, // GPIO10 Button 3
+ // GPIO11 (SD_CMD Flash)
+ GPIO_REL1, // GPIO12 Blue Led and Relay 1 (0 = Off, 1 = On)
+ GPIO_LED1_INV, // GPIO13 Blue Led (0 = On, 1 = Off) - Link and Power status
0, 0, 0, 0
},
- { // SONOFF_DUAL_R2 - Sonoff Dual R2 (ESP8285)
- AGPIO(GPIO_USER), // GPIO00 Button 0 on header (0 = On, 1 = Off)
- AGPIO(GPIO_USER), // GPIO01 Serial RXD and Optional sensor
+ { // SONOFF_DUAL_R2 - Sonoff Dual R2 (ESP8285)
+ GPIO_USER, // GPIO00 Button 0 on header (0 = On, 1 = Off)
+ GPIO_USER, // GPIO01 Serial RXD and Optional sensor
0,
- AGPIO(GPIO_USER), // GPIO03 Serial TXD and Optional sensor
+ GPIO_USER, // GPIO03 Serial TXD and Optional sensor
0,
- AGPIO(GPIO_REL1) +1, // GPIO05 Relay 2 (0 = Off, 1 = On)
- // GPIO06 (SD_CLK Flash)
- // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT)
- // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT)
- AGPIO(GPIO_USER), // GPIO09 Button 1 on header (0 = On, 1 = Off)
- AGPIO(GPIO_KEY1), // GPIO10 Button on casing
- // GPIO11 (SD_CMD Flash)
- AGPIO(GPIO_REL1), // GPIO12 Relay 1 (0 = Off, 1 = On)
- AGPIO(GPIO_LED1_INV), // GPIO13 Blue Led (0 = On, 1 = Off) - Link and Power status
+ GPIO_REL2, // GPIO05 Relay 2 (0 = Off, 1 = On)
+ // GPIO06 (SD_CLK Flash)
+ // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT)
+ // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT)
+ GPIO_USER, // GPIO09 Button 1 on header (0 = On, 1 = Off)
+ GPIO_KEY1, // GPIO10 Button on casing
+ // GPIO11 (SD_CMD Flash)
+ GPIO_REL1, // GPIO12 Relay 1 (0 = Off, 1 = On)
+ GPIO_LED1_INV, // GPIO13 Blue Led (0 = On, 1 = Off) - Link and Power status
0, 0, 0, 0
},
- { // SONOFF_IFAN03 - Sonoff iFan03 (ESP8285)
- AGPIO(GPIO_KEY1), // GPIO00 WIFI_KEY0 Button 1
- AGPIO(GPIO_TXD), // GPIO01 ESP_TXD Serial RXD connection to P0.5 of RF microcontroller
- 0, // GPIO02 ESP_LOG
- AGPIO(GPIO_RXD), // GPIO03 ESP_RXD Serial TXD connection to P0.4 of RF microcontroller
- 0, // GPIO04 DEBUG_RX
- 0, // GPIO05 DEBUG_TX
- // GPIO06 (SD_CLK Flash)
- // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT)
- // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT)
- AGPIO(GPIO_REL1_INV), // GPIO09 WIFI_O0 Relay 1 (0 = Off, 1 = On) controlling the light
- AGPIO(GPIO_BUZZER_INV), // GPIO10 WIFI_O4 Buzzer (0 = Off, 1 = On)
- // GPIO11 (SD_CMD Flash)
- AGPIO(GPIO_REL1) +2, // GPIO12 WIFI_O2 Relay 3 (0 = Off, 1 = On) controlling the fan
- AGPIO(GPIO_LED1_INV), // GPIO13 WIFI_CHK Blue Led on PCA (0 = On, 1 = Off) - Link and Power status
- AGPIO(GPIO_REL1) +1, // GPIO14 WIFI_O1 Relay 2 (0 = Off, 1 = On) controlling the fan
- AGPIO(GPIO_REL1) +3, // GPIO15 WIFI_O3 Relay 4 (0 = Off, 1 = On) controlling the fan
+ { // SONOFF_IFAN03 - Sonoff iFan03 (ESP8285)
+ GPIO_KEY1, // GPIO00 WIFI_KEY0 Button 1
+ GPIO_TXD, // GPIO01 ESP_TXD Serial RXD connection to P0.5 of RF microcontroller
+ 0, // GPIO02 ESP_LOG
+ GPIO_RXD, // GPIO03 ESP_RXD Serial TXD connection to P0.4 of RF microcontroller
+ 0, // GPIO04 DEBUG_RX
+ 0, // GPIO05 DEBUG_TX
+ // GPIO06 (SD_CLK Flash)
+ // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT)
+ // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT)
+ GPIO_REL1_INV, // GPIO09 WIFI_O0 Relay 1 (0 = Off, 1 = On) controlling the light
+ GPIO_BUZZER_INV, // GPIO10 WIFI_O4 Buzzer (0 = Off, 1 = On)
+ // GPIO11 (SD_CMD Flash)
+ GPIO_REL3, // GPIO12 WIFI_O2 Relay 3 (0 = Off, 1 = On) controlling the fan
+ GPIO_LED1_INV, // GPIO13 WIFI_CHK Blue Led on PCA (0 = On, 1 = Off) - Link and Power status
+ GPIO_REL2, // GPIO14 WIFI_O1 Relay 2 (0 = Off, 1 = On) controlling the fan
+ GPIO_REL4, // GPIO15 WIFI_O3 Relay 4 (0 = Off, 1 = On) controlling the fan
0, 0
}
};
From 6eadeb81eae306f46b25ee5099eee725b25fdf2e Mon Sep 17 00:00:00 2001
From: Stephan Hadinger
Date: Mon, 14 Sep 2020 21:48:32 +0200
Subject: [PATCH 048/148] Fix TLS crash
---
tasmota/WiFiClientSecureLightBearSSL.cpp | 2 -
tasmota/xdrv_02_mqtt.ino | 50 +++++++++++++-----------
2 files changed, 28 insertions(+), 24 deletions(-)
diff --git a/tasmota/WiFiClientSecureLightBearSSL.cpp b/tasmota/WiFiClientSecureLightBearSSL.cpp
index d85373688..f0a6bcfbb 100755
--- a/tasmota/WiFiClientSecureLightBearSSL.cpp
+++ b/tasmota/WiFiClientSecureLightBearSSL.cpp
@@ -874,10 +874,8 @@ extern "C" {
// we support only P256 EC curve for AWS IoT, no EC curve for Letsencrypt unless forced
br_ssl_engine_set_ec(&cc->eng, &br_ec_p256_m15); // TODO
#endif
-#ifdef USE_MQTT_AWS_IOT_LIGHT
static const char * alpn_mqtt = "mqtt";
br_ssl_engine_set_protocol_names(&cc->eng, &alpn_mqtt, 1);
-#endif
}
}
diff --git a/tasmota/xdrv_02_mqtt.ino b/tasmota/xdrv_02_mqtt.ino
index bcd71005f..58fd04c2c 100644
--- a/tasmota/xdrv_02_mqtt.ino
+++ b/tasmota/xdrv_02_mqtt.ino
@@ -57,7 +57,7 @@ struct MQTT {
uint8_t initial_connection_state = 2; // MQTT connection messages state
bool connected = false; // MQTT virtual connection status
bool allowed = false; // MQTT enabled and parameters valid
- bool tls_private_key = false; // MQTT require a private key before connecting
+ bool mqtt_tls = false; // MQTT TLS is enabled
} Mqtt;
#ifdef USE_MQTT_TLS
@@ -149,22 +149,24 @@ void MqttInit(void)
// Turn on TLS for port 8883 (TLS) and 8884 (TLS, client certificate)
Settings.flag4.mqtt_tls = true;
}
+ Mqtt.mqtt_tls = Settings.flag4.mqtt_tls; // this flag should not change even if we change the SetOption (until reboot)
// Detect AWS IoT and set default parameters
String host = String(SettingsText(SET_MQTT_HOST));
if (host.indexOf(".iot.") && host.endsWith(".amazonaws.com")) { // look for ".iot." and ".amazonaws.com" in the domain name
Settings.flag4.mqtt_no_retain = true;
- // Mqtt.tls_private_key = true;
}
- if (Settings.flag4.mqtt_tls) {
+ if (Mqtt.mqtt_tls) {
tlsClient = new BearSSL::WiFiClientSecure_light(1024,1024);
#ifdef USE_MQTT_AWS_IOT
loadTlsDir(); // load key and certificate data from Flash
- tlsClient->setClientECCert(AWS_IoT_Client_Certificate,
- AWS_IoT_Private_Key,
- 0xFFFF /* all usages, don't care */, 0);
+ if ((nullptr != AWS_IoT_Private_Key) && (nullptr != AWS_IoT_Client_Certificate)) {
+ tlsClient->setClientECCert(AWS_IoT_Client_Certificate,
+ AWS_IoT_Private_Key,
+ 0xFFFF /* all usages, don't care */, 0);
+ }
#endif
#ifdef USE_MQTT_TLS_CA_CERT
@@ -578,8 +580,8 @@ void MqttReconnect(void)
}
#if defined(USE_MQTT_TLS) && defined(USE_MQTT_AWS_IOT)
// don't enable MQTT for AWS IoT if Private Key or Certificate are not set
- if (Settings.flag4.mqtt_tls && Mqtt.tls_private_key) {
- if (!AWS_IoT_Private_Key || !AWS_IoT_Client_Certificate) {
+ if (Mqtt.mqtt_tls) {
+ if (0 == strlen(SettingsText(SET_MQTT_PWD))) { // we anticipate that an empty password does not make sense with TLS. This avoids failed connections
Mqtt.allowed = false;
}
}
@@ -614,7 +616,7 @@ void MqttReconnect(void)
if (MqttClient.connected()) { MqttClient.disconnect(); }
#ifdef USE_MQTT_TLS
- if (Settings.flag4.mqtt_tls) {
+ if (Mqtt.mqtt_tls) {
tlsClient->stop();
} else {
EspClient = WiFiClient(); // Wifi Client reconnect issue 4497 (https://github.com/esp8266/Arduino/issues/4497)
@@ -632,10 +634,12 @@ void MqttReconnect(void)
MqttClient.setCallback(MqttDataHandler);
#if defined(USE_MQTT_TLS) && defined(USE_MQTT_AWS_IOT)
// re-assign private keys in case it was updated in between
- if (Settings.flag4.mqtt_tls) {
- tlsClient->setClientECCert(AWS_IoT_Client_Certificate,
- AWS_IoT_Private_Key,
- 0xFFFF /* all usages, don't care */, 0);
+ if (Mqtt.mqtt_tls) {
+ if ((nullptr != AWS_IoT_Private_Key) && (nullptr != AWS_IoT_Client_Certificate)) {
+ tlsClient->setClientECCert(AWS_IoT_Client_Certificate,
+ AWS_IoT_Private_Key,
+ 0xFFFF /* all usages, don't care */, 0);
+ }
}
#endif
MqttClient.setServer(SettingsText(SET_MQTT_HOST), Settings.mqtt_port);
@@ -645,7 +649,7 @@ void MqttReconnect(void)
bool allow_all_fingerprints;
bool learn_fingerprint1;
bool learn_fingerprint2;
- if (Settings.flag4.mqtt_tls) {
+ if (Mqtt.mqtt_tls) {
allow_all_fingerprints = false;
learn_fingerprint1 = is_fingerprint_mono_value(Settings.mqtt_fingerprint[0], 0x00);
learn_fingerprint2 = is_fingerprint_mono_value(Settings.mqtt_fingerprint[1], 0x00);
@@ -658,16 +662,18 @@ void MqttReconnect(void)
#endif
bool lwt_retain = Settings.flag4.mqtt_no_retain ? false : true; // no retained last will if "no_retain"
#if defined(USE_MQTT_TLS) && defined(USE_MQTT_AWS_IOT)
- if (Settings.flag4.mqtt_tls && Mqtt.tls_private_key) {
- // If we require private key then we should null user/pwd
- mqtt_user = nullptr;
- mqtt_pwd = nullptr;
+ if (Mqtt.mqtt_tls) {
+ if ((nullptr != AWS_IoT_Private_Key) && (nullptr != AWS_IoT_Client_Certificate)) {
+ // if private key is there, we remove user/pwd
+ mqtt_user = nullptr;
+ mqtt_pwd = nullptr;
+ }
}
#endif
if (MqttClient.connect(mqtt_client, mqtt_user, mqtt_pwd, stopic, 1, lwt_retain, mqtt_data, MQTT_CLEAN_SESSION)) {
#ifdef USE_MQTT_TLS
- if (Settings.flag4.mqtt_tls) {
+ if (Mqtt.mqtt_tls) {
AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_MQTT "TLS connected in %d ms, max ThunkStack used %d"),
millis() - mqtt_connect_time, tlsClient->getMaxThunkStackUse());
if (!tlsClient->getMFLNStatus()) {
@@ -739,7 +745,7 @@ void MqttReconnect(void)
MqttConnected();
} else {
#ifdef USE_MQTT_TLS
- if (Settings.flag4.mqtt_tls) {
+ if (Mqtt.mqtt_tls) {
AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_MQTT "TLS connection error: %d"), tlsClient->getLastError());
}
#endif
@@ -1311,7 +1317,7 @@ void HandleMqttConfiguration(void)
SettingsText(SET_MQTT_HOST),
Settings.mqtt_port,
#ifdef USE_MQTT_TLS
- Settings.flag4.mqtt_tls ? " checked" : "", // SetOption102 - Enable MQTT TLS
+ Mqtt.mqtt_tls ? " checked" : "", // SetOption102 - Enable MQTT TLS
#endif // USE_MQTT_TLS
Format(str, MQTT_CLIENT_ID, sizeof(str)), MQTT_CLIENT_ID, SettingsText(SET_MQTT_CLIENT));
WSContentSend_P(HTTP_FORM_MQTT2,
@@ -1346,7 +1352,7 @@ void MqttSaveSettings(void)
WebGetArg("ml", tmp, sizeof(tmp));
Settings.mqtt_port = (!strlen(tmp)) ? MQTT_PORT : atoi(tmp);
#ifdef USE_MQTT_TLS
- Settings.flag4.mqtt_tls = Webserver->hasArg("b3"); // SetOption102 - Enable MQTT TLS
+ Mqtt.mqtt_tls = Webserver->hasArg("b3"); // SetOption102 - Enable MQTT TLS
#endif
WebGetArg("mc", tmp, sizeof(tmp));
SettingsUpdateText(SET_MQTT_CLIENT, (!strlen(tmp)) ? MQTT_CLIENT_ID : tmp);
From ea85b706853957b94dc73d29da161c3b6f23309f Mon Sep 17 00:00:00 2001
From: Stephan Hadinger
Date: Mon, 14 Sep 2020 22:06:19 +0200
Subject: [PATCH 049/148] Zigbee minor improvements
---
tasmota/support.ino | 24 ++++
tasmota/xdrv_23_zigbee_1_headers.ino | 1 +
tasmota/xdrv_23_zigbee_1z_libs.ino | 19 ++-
tasmota/xdrv_23_zigbee_2_devices.ino | 63 +++++----
tasmota/xdrv_23_zigbee_5_converters.ino | 74 +++++++++++
tasmota/xdrv_23_zigbee_8_parsers.ino | 73 ++++++----
tasmota/xdrv_23_zigbee_9_serial.ino | 2 +
tasmota/xdrv_23_zigbee_A_impl.ino | 170 +++++++++++-------------
8 files changed, 266 insertions(+), 160 deletions(-)
diff --git a/tasmota/support.ino b/tasmota/support.ino
index 8d4044906..eddffeab6 100644
--- a/tasmota/support.ino
+++ b/tasmota/support.ino
@@ -2006,3 +2006,27 @@ String Decompress(const char * compressed, size_t uncompressed_size) {
}
#endif // USE_UNISHOX_COMPRESSION
+
+/*********************************************************************************************\
+ * High entropy hardware random generator
+ * Thanks to DigitalAlchemist
+\*********************************************************************************************/
+// Based on code from https://raw.githubusercontent.com/espressif/esp-idf/master/components/esp32/hw_random.c
+uint32_t HwRandom(void) {
+#if ESP8266
+ // https://web.archive.org/web/20160922031242/http://esp8266-re.foogod.com/wiki/Random_Number_Generator
+ #define _RAND_ADDR 0x3FF20E44UL
+#else // ESP32
+ #define _RAND_ADDR 0x3FF75144UL
+#endif
+ static uint32_t last_ccount = 0;
+ uint32_t ccount;
+ uint32_t result = 0;
+ do {
+ ccount = ESP.getCycleCount();
+ result ^= *(volatile uint32_t *)_RAND_ADDR;
+ } while (ccount - last_ccount < 64);
+ last_ccount = ccount;
+ return result ^ *(volatile uint32_t *)_RAND_ADDR;
+#undef _RAND_ADDR
+}
diff --git a/tasmota/xdrv_23_zigbee_1_headers.ino b/tasmota/xdrv_23_zigbee_1_headers.ino
index 604c53835..c7e3093e2 100644
--- a/tasmota/xdrv_23_zigbee_1_headers.ino
+++ b/tasmota/xdrv_23_zigbee_1_headers.ino
@@ -37,6 +37,7 @@ public:
};
void ZigbeeZCLSend_Raw(const ZigbeeZCLSendMessage &zcl);
+bool ZbAppendWriteBuf(SBuffer & buf, const Z_attribute & attr, bool prepend_status_ok = false);
// get the result as a string (const char*) and nullptr if there is no field or the string is empty
const char * getCaseInsensitiveConstCharNull(const JsonObject &json, const char *needle) {
diff --git a/tasmota/xdrv_23_zigbee_1z_libs.ino b/tasmota/xdrv_23_zigbee_1z_libs.ino
index 0590cff81..1f9012152 100644
--- a/tasmota/xdrv_23_zigbee_1z_libs.ino
+++ b/tasmota/xdrv_23_zigbee_1z_libs.ino
@@ -101,11 +101,13 @@ public:
SBuffer* bval;
char* sval;
} val;
- Za_type type; // uint8_t in size, type of attribute, see above
- bool key_is_str; // is the key a string?
- bool key_is_pmem; // is the string in progmem, so we don't need to make a copy
- bool val_str_raw; // if val is String, it is raw JSON and should not be escaped
- uint8_t key_suffix; // append a suffix to key (default is 1, explicitly output if >1)
+ Za_type type; // uint8_t in size, type of attribute, see above
+ bool key_is_str; // is the key a string?
+ bool key_is_pmem; // is the string in progmem, so we don't need to make a copy
+ bool val_str_raw; // if val is String, it is raw JSON and should not be escaped
+ uint8_t key_suffix; // append a suffix to key (default is 1, explicitly output if >1)
+ uint8_t attr_type; // [opt] type of the attribute, default to Zunk (0xFF)
+ uint8_t attr_multiplier; // [opt] multiplier for attribute, defaults to 0x01 (no change)
// Constructor with all defaults
Z_attribute():
@@ -115,7 +117,9 @@ public:
key_is_str(false),
key_is_pmem(false),
val_str_raw(false),
- key_suffix(1)
+ key_suffix(1),
+ attr_type(0xFF),
+ attr_multiplier(1)
{};
Z_attribute(const Z_attribute & rhs) {
@@ -247,6 +251,7 @@ public:
}
inline bool isNum(void) const { return (type >= Za_type::Za_bool) && (type <= Za_type::Za_float); }
+ inline bool isNone(void) const { return (type == Za_type::Za_none);}
// get num values
float getFloat(void) const {
switch (type) {
@@ -483,6 +488,8 @@ protected:
key_is_str = rhs.key_is_str;
key_is_pmem = rhs.key_is_pmem;
key_suffix = rhs.key_suffix;
+ attr_type = rhs.attr_type;
+ attr_multiplier = rhs.attr_multiplier;
// copy value
copyVal(rhs);
// don't touch next pointer
diff --git a/tasmota/xdrv_23_zigbee_2_devices.ino b/tasmota/xdrv_23_zigbee_2_devices.ino
index 1e2c29704..b84db3e2e 100644
--- a/tasmota/xdrv_23_zigbee_2_devices.ino
+++ b/tasmota/xdrv_23_zigbee_2_devices.ino
@@ -951,44 +951,43 @@ uint16_t Z_Devices::parseDeviceParam(const char * param, bool short_must_be_know
// Display the tracked status for a light
String Z_Devices::dumpLightState(uint16_t shortaddr) const {
- DynamicJsonBuffer jsonBuffer;
- JsonObject& json = jsonBuffer.createObject();
+ Z_attribute_list attr_list;
char hex[8];
const Z_Device & device = findShortAddr(shortaddr);
- if (foundDevice(device)) {
- const char * fname = getFriendlyName(shortaddr);
+ const char * fname = getFriendlyName(shortaddr);
+ bool use_fname = (Settings.flag4.zigbee_use_names) && (fname); // should we replace shortaddr with friendlyname?
+ snprintf_P(hex, sizeof(hex), PSTR("0x%04X"), shortaddr);
- bool use_fname = (Settings.flag4.zigbee_use_names) && (fname); // should we replace shortaddr with friendlyname?
-
- snprintf_P(hex, sizeof(hex), PSTR("0x%04X"), shortaddr);
-
- JsonObject& dev = use_fname ? json.createNestedObject((char*) fname) // casting (char*) forces a copy
- : json.createNestedObject(hex);
- if (use_fname) {
- dev[F(D_JSON_ZIGBEE_DEVICE)] = hex;
- } else if (fname) {
- dev[F(D_JSON_ZIGBEE_NAME)] = (char*) fname;
- }
-
- // expose the last known status of the bulb, for Hue integration
- dev[F(D_JSON_ZIGBEE_LIGHT)] = getHueBulbtype(shortaddr); // sign extend, 0xFF changed as -1
- // dump all known values
- dev[F("Reachable")] = device.getReachable(); // TODO TODO
- if (device.validPower()) { dev[F("Power")] = device.getPower(); }
- if (device.validDimmer()) { dev[F("Dimmer")] = device.dimmer; }
- if (device.validColormode()) { dev[F("Colormode")] = device.colormode; }
- if (device.validCT()) { dev[F("CT")] = device.ct; }
- if (device.validSat()) { dev[F("Sat")] = device.sat; }
- if (device.validHue()) { dev[F("Hue")] = device.hue; }
- if (device.validX()) { dev[F("X")] = device.x; }
- if (device.validY()) { dev[F("Y")] = device.y; }
+ attr_list.addAttribute(F(D_JSON_ZIGBEE_DEVICE)).setStr(hex);
+ if (fname) {
+ attr_list.addAttribute(F(D_JSON_ZIGBEE_NAME)).setStr(fname);
}
- String payload = "";
- payload.reserve(200);
- json.printTo(payload);
- return payload;
+ if (foundDevice(device)) {
+ // expose the last known status of the bulb, for Hue integration
+ attr_list.addAttribute(F(D_JSON_ZIGBEE_LIGHT)).setInt(getHueBulbtype(shortaddr)); // sign extend, 0xFF changed as -1
+ // dump all known values
+ attr_list.addAttribute(F("Reachable")).setBool(device.getReachable());
+ if (device.validPower()) { attr_list.addAttribute(F("Power")).setUInt(device.getPower()); }
+ if (device.validDimmer()) { attr_list.addAttribute(F("Dimmer")).setUInt(device.dimmer); }
+ if (device.validColormode()) { attr_list.addAttribute(F("Colormode")).setUInt(device.colormode); }
+ if (device.validCT()) { attr_list.addAttribute(F("CT")).setUInt(device.ct); }
+ if (device.validSat()) { attr_list.addAttribute(F("Sat")).setUInt(device.sat); }
+ if (device.validHue()) { attr_list.addAttribute(F("Hue")).setUInt(device.hue); }
+ if (device.validX()) { attr_list.addAttribute(F("X")).setUInt(device.x); }
+ if (device.validY()) { attr_list.addAttribute(F("Y")).setUInt(device.y); }
+ }
+
+ Z_attribute_list attr_list_root;
+ Z_attribute * attr_root;
+ if (use_fname) {
+ attr_root = &attr_list_root.addAttribute(fname);
+ } else {
+ attr_root = &attr_list_root.addAttribute(hex);
+ }
+ attr_root->setStrRaw(attr_list.toString(true).c_str());
+ return attr_list_root.toString(true);
}
// Dump the internal memory of Zigbee devices
diff --git a/tasmota/xdrv_23_zigbee_5_converters.ino b/tasmota/xdrv_23_zigbee_5_converters.ino
index edcdb3135..f29c514d5 100644
--- a/tasmota/xdrv_23_zigbee_5_converters.ino
+++ b/tasmota/xdrv_23_zigbee_5_converters.ino
@@ -1664,4 +1664,78 @@ void ZCLFrame::postProcessAttributes(uint16_t shortaddr, Z_attribute_list& attr_
}
}
+//
+// Given an attribute string, retrieve all attribute details.
+// Input: the attribute has a key name, either: / or /% or ""
+// Ex: "0008/0000", "0008/0000%20", "Dimmer"
+// Use:
+// Z_attribute attr;
+// attr.setKeyName("0008/0000%20")
+// if (Z_parseAttributeKey(attr)) {
+// // ok
+// }
+//
+// Output:
+// The `attr` attribute has the correct cluster, attr_id, attr_type, attr_multiplier
+// Note: the attribute value is unchanged and unparsed
+//
+// Note: if the type is specified in the key, the multiplier is not applied, no conversion happens
+bool Z_parseAttributeKey(class Z_attribute & attr) {
+ // check if the name has the format "XXXX/YYYY" where XXXX is the cluster, YYYY the attribute id
+ // alternative "XXXX/YYYY%ZZ" where ZZ is the type (for unregistered attributes)
+ if (attr.key_is_str) {
+ const char * key = attr.key.key;
+ char * delimiter = strchr(key, '/');
+ char * delimiter2 = strchr(key, '%');
+ if (delimiter) {
+ uint16_t attr_id = 0xFFFF;
+ uint16_t cluster_id = 0xFFFF;
+ uint8_t type_id = Zunk;
+
+ cluster_id = strtoul(key, &delimiter, 16);
+ if (!delimiter2) {
+ attr_id = strtoul(delimiter+1, nullptr, 16);
+ } else {
+ attr_id = strtoul(delimiter+1, &delimiter2, 16);
+ type_id = strtoul(delimiter2+1, nullptr, 16);
+ }
+ attr.setKeyId(cluster_id, attr_id);
+ attr.attr_type = type_id;
+ }
+ }
+ // AddLog_P2(LOG_LEVEL_DEBUG, PSTR("cluster_id = 0x%04X, attr_id = 0x%04X"), cluster_id, attr_id);
+
+ // do we already know the type, i.e. attribute and cluster are also known
+ if (Zunk == attr.attr_type) {
+ // scan attributes to find by name, and retrieve type
+ for (uint32_t i = 0; i < ARRAY_SIZE(Z_PostProcess); i++) {
+ const Z_AttributeConverter *converter = &Z_PostProcess[i];
+ bool match = false;
+ uint16_t local_attr_id = pgm_read_word(&converter->attribute);
+ uint16_t local_cluster_id = CxToCluster(pgm_read_byte(&converter->cluster_short));
+ uint8_t local_type_id = pgm_read_byte(&converter->type);
+ int8_t local_multiplier = pgm_read_byte(&converter->multiplier);
+ // AddLog_P2(LOG_LEVEL_DEBUG, PSTR("Try cluster = 0x%04X, attr = 0x%04X, type_id = 0x%02X"), local_cluster_id, local_attr_id, local_type_id);
+
+ if (!attr.key_is_str) {
+ if ((attr.key.id.cluster == local_cluster_id) && (attr.key.id.attr_id == local_attr_id)) {
+ attr.attr_type = local_type_id;
+ break;
+ }
+ } else if (pgm_read_word(&converter->name_offset)) {
+ const char * key = attr.key.key;
+ // AddLog_P2(LOG_LEVEL_DEBUG, PSTR("Comparing '%s' with '%s'"), attr_name, converter->name);
+ if (0 == strcasecmp_P(key, Z_strings + pgm_read_word(&converter->name_offset))) {
+ // match
+ attr.setKeyId(local_cluster_id, local_attr_id);
+ attr.attr_type = local_type_id;
+ attr.attr_multiplier = local_multiplier;
+ break;
+ }
+ }
+ }
+ }
+ return (Zunk != attr.attr_type) ? true : false;
+}
+
#endif // USE_ZIGBEE
diff --git a/tasmota/xdrv_23_zigbee_8_parsers.ino b/tasmota/xdrv_23_zigbee_8_parsers.ino
index c7585134b..c761cabb6 100644
--- a/tasmota/xdrv_23_zigbee_8_parsers.ino
+++ b/tasmota/xdrv_23_zigbee_8_parsers.ino
@@ -1768,20 +1768,21 @@ int32_t Z_State_Ready(uint8_t value) {
//
// Mostly used for routers/end-devices
// json: holds the attributes in JSON format
-void Z_AutoResponder(uint16_t srcaddr, uint16_t cluster, uint8_t endpoint, const uint16_t *attr_list, size_t attr_len) {
- DynamicJsonBuffer jsonBuffer;
- JsonObject& json_out = jsonBuffer.createObject();
+void Z_AutoResponder(uint16_t srcaddr, uint16_t cluster, uint8_t endpoint, const uint16_t *attr_list_ids, size_t attr_len) {
+ Z_attribute_list attr_list;
for (uint32_t i=0; i 0xFEFF) ? uxy[i] : 0xFEFF;
}
- if (0x0000 == attr) { json_out[F("Hue")] = changeUIntScale(hue, 0, 360, 0, 254); }
- if (0x0001 == attr) { json_out[F("Sat")] = changeUIntScale(sat, 0, 255, 0, 254); }
- if (0x0003 == attr) { json_out[F("X")] = uxy[0]; }
- if (0x0004 == attr) { json_out[F("Y")] = uxy[1]; }
- if (0x0007 == attr) { json_out[F("CT")] = LightGetColorTemp(); }
+ if (0x0000 == attr_id) { attr.setUInt(changeUIntScale(hue, 0, 360, 0, 254)); }
+ if (0x0001 == attr_id) { attr.setUInt(changeUIntScale(sat, 0, 255, 0, 254)); }
+ if (0x0003 == attr_id) { attr.setUInt(uxy[0]); }
+ if (0x0004 == attr_id) { attr.setUInt(uxy[1]); }
+ if (0x0007 == attr_id) { attr.setUInt(LightGetColorTemp()); }
}
break;
#endif
case 0x000A0000: // Time
- json_out[F("Time")] = (Rtc.utc_time > (60 * 60 * 24 * 365 * 10)) ? Rtc.utc_time - 946684800 : Rtc.utc_time;
+ attr.setUInt((Rtc.utc_time > (60 * 60 * 24 * 365 * 10)) ? Rtc.utc_time - 946684800 : Rtc.utc_time);
break;
case 0x000AFF00: // TimeEpoch - Tasmota specific
- json_out[F("TimeEpoch")] = Rtc.utc_time;
+ attr.setUInt(Rtc.utc_time);
break;
case 0x000A0001: // TimeStatus
- json_out[F("TimeStatus")] = (Rtc.utc_time > (60 * 60 * 24 * 365 * 10)) ? 0x02 : 0x00; // if time is beyond 2010 then we are synchronized
+ attr.setUInt((Rtc.utc_time > (60 * 60 * 24 * 365 * 10)) ? 0x02 : 0x00); // if time is beyond 2010 then we are synchronized
break;
case 0x000A0002: // TimeZone
- json_out[F("TimeZone")] = Settings.toffset[0] * 60;
+ attr.setUInt(Settings.toffset[0] * 60);
break;
case 0x000A0007: // LocalTime // TODO take DST
- json_out[F("LocalTime")] = Settings.toffset[0] * 60 + ((Rtc.utc_time > (60 * 60 * 24 * 365 * 10)) ? Rtc.utc_time - 946684800 : Rtc.utc_time);
+ attr.setUInt(Settings.toffset[0] * 60 + ((Rtc.utc_time > (60 * 60 * 24 * 365 * 10)) ? Rtc.utc_time - 946684800 : Rtc.utc_time));
break;
}
+ if (!attr.isNone()) {
+ Z_parseAttributeKey(attr);
+ attr_list.addAttribute(cluster, attr_id) = attr;
+ }
}
- if (json_out.size() > 0) {
+ SBuffer buf(200);
+ for (const auto & attr : attr_list) {
+ if (!ZbAppendWriteBuf(buf, attr, true)) { // true = need status indicator in Read Attribute Responses
+ return; // error
+ }
+ }
+
+ if (buf.len() > 0) {
// we have a non-empty output
// log first
- String msg("");
- msg.reserve(100);
- json_out.printTo(msg);
AddLog_P2(LOG_LEVEL_INFO, PSTR("ZIG: Auto-responder: ZbSend {\"Device\":\"0x%04X\""
",\"Cluster\":\"0x%04X\""
",\"Endpoint\":%d"
",\"Response\":%s}"
),
srcaddr, cluster, endpoint,
- msg.c_str());
+ attr_list.toString().c_str());
// send
- const JsonVariant &json_out_v = json_out;
- ZbSendReportWrite(json_out_v, srcaddr, 0 /* group */,cluster, endpoint, 0 /* manuf */, ZCL_READ_ATTRIBUTES_RESPONSE);
+ // all good, send the packet
+ uint8_t seq = zigbee_devices.getNextSeqNumber(srcaddr);
+ ZigbeeZCLSend_Raw(ZigbeeZCLSendMessage({
+ srcaddr,
+ 0x0000,
+ cluster /*cluster*/,
+ endpoint,
+ ZCL_READ_ATTRIBUTES_RESPONSE,
+ 0x0000, /* manuf */
+ false /* not cluster specific */,
+ false /* no response */,
+ seq, /* zcl transaction id */
+ buf.getBuffer(), buf.len()
+ }));
}
}
diff --git a/tasmota/xdrv_23_zigbee_9_serial.ino b/tasmota/xdrv_23_zigbee_9_serial.ino
index fd69ee16a..c14c7f248 100644
--- a/tasmota/xdrv_23_zigbee_9_serial.ino
+++ b/tasmota/xdrv_23_zigbee_9_serial.ino
@@ -595,7 +595,9 @@ int32_t ZigbeeProcessInputEZSP(class SBuffer &buf) {
case EZSP_getNetworkParameters: // 2800
case EZSP_sendUnicast: // 3400
case EZSP_sendBroadcast: // 3600
+ case EZSP_sendMulticast: // 3800
case EZSP_messageSentHandler: // 3F00
+ case EZSP_incomingMessageHandler: // 4500
case EZSP_setConfigurationValue: // 5300
case EZSP_setPolicy: // 5500
case 0x0059: // 5900 - supposedly removed by still happening
diff --git a/tasmota/xdrv_23_zigbee_A_impl.ino b/tasmota/xdrv_23_zigbee_A_impl.ino
index 495db0e1b..5ca3b555e 100644
--- a/tasmota/xdrv_23_zigbee_A_impl.ino
+++ b/tasmota/xdrv_23_zigbee_A_impl.ino
@@ -212,6 +212,34 @@ void ZbApplyMultiplier(double &val_d, int8_t multiplier) {
}
}
+//
+// Send Attribute Write, apply mutlipliers before
+//
+bool ZbAppendWriteBuf(SBuffer & buf, const Z_attribute & attr, bool prepend_status_ok) {
+ double val_d = attr.getFloat();
+ const char * val_str = attr.getStr();
+
+ if (attr.key_is_str) { return false; }
+ if (attr.isNum() && (1 != attr.attr_multiplier)) {
+ ZbApplyMultiplier(val_d, attr.attr_multiplier);
+ }
+
+ // push the value in the buffer
+ buf.add16(attr.key.id.attr_id); // prepend with attribute identifier
+ if (prepend_status_ok) {
+ buf.add8(Z_SUCCESS); // status OK = 0x00
+ }
+ buf.add8(attr.attr_type); // prepend with attribute type
+ int32_t res = encodeSingleAttribute(buf, val_d, val_str, attr.attr_type);
+ if (res < 0) {
+ // remove the attribute type we just added
+ // buf.setLen(buf.len() - (operation == ZCL_READ_ATTRIBUTES_RESPONSE ? 4 : 3));
+ AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_ZIGBEE "Unsupported attribute type %04X/%04X '0x%02X'"), attr.key.id.cluster, attr.key.id.attr_id, attr.attr_type);
+ return false;
+ }
+ return true;
+}
+
// Parse "Report", "Write", "Response" or "Condig" attribute
// Operation is one of: ZCL_REPORT_ATTRIBUTES (0x0A), ZCL_WRITE_ATTRIBUTES (0x02) or ZCL_READ_ATTRIBUTES_RESPONSE (0x01)
void ZbSendReportWrite(const JsonObject &val_pubwrite, uint16_t device, uint16_t groupaddr, uint16_t cluster, uint8_t endpoint, uint16_t manuf, uint8_t operation) {
@@ -226,99 +254,42 @@ void ZbSendReportWrite(const JsonObject &val_pubwrite, uint16_t device, uint16_t
const char *key = it->key;
const JsonVariant &value = it->value;
- uint16_t attr_id = 0xFFFF;
- uint16_t cluster_id = 0xFFFF;
- uint8_t type_id = Znodata;
- int8_t multiplier = 1; // multiplier to adjust the key value
- double val_d = 0; // I try to avoid `double` but this type capture both float and (u)int32_t without prevision loss
- const char* val_str = ""; // variant as string
-
- // check if the name has the format "XXXX/YYYY" where XXXX is the cluster, YYYY the attribute id
- // alternative "XXXX/YYYY%ZZ" where ZZ is the type (for unregistered attributes)
- char * delimiter = strchr(key, '/');
- char * delimiter2 = strchr(key, '%');
- if (delimiter) {
- cluster_id = strtoul(key, &delimiter, 16);
- if (!delimiter2) {
- attr_id = strtoul(delimiter+1, nullptr, 16);
- } else {
- attr_id = strtoul(delimiter+1, &delimiter2, 16);
- type_id = strtoul(delimiter2+1, nullptr, 16);
- }
- }
- // AddLog_P2(LOG_LEVEL_DEBUG, PSTR("cluster_id = 0x%04X, attr_id = 0x%04X"), cluster_id, attr_id);
-
- // do we already know the type, i.e. attribute and cluster are also known
- if (Znodata == type_id) {
- // scan attributes to find by name, and retrieve type
- for (uint32_t i = 0; i < ARRAY_SIZE(Z_PostProcess); i++) {
- const Z_AttributeConverter *converter = &Z_PostProcess[i];
- bool match = false;
- uint16_t local_attr_id = pgm_read_word(&converter->attribute);
- uint16_t local_cluster_id = CxToCluster(pgm_read_byte(&converter->cluster_short));
- uint8_t local_type_id = pgm_read_byte(&converter->type);
- int8_t local_multiplier = pgm_read_byte(&converter->multiplier);
- // AddLog_P2(LOG_LEVEL_DEBUG, PSTR("Try cluster = 0x%04X, attr = 0x%04X, type_id = 0x%02X"), local_cluster_id, local_attr_id, local_type_id);
-
- if (delimiter) {
- if ((cluster_id == local_cluster_id) && (attr_id == local_attr_id)) {
- type_id = local_type_id;
- break;
- }
- } else if (pgm_read_word(&converter->name_offset)) {
- // AddLog_P2(LOG_LEVEL_DEBUG, PSTR("Comparing '%s' with '%s'"), attr_name, converter->name);
- if (0 == strcasecmp_P(key, Z_strings + pgm_read_word(&converter->name_offset))) {
- // match
- cluster_id = local_cluster_id;
- attr_id = local_attr_id;
- type_id = local_type_id;
- multiplier = local_multiplier;
- break;
- }
- }
- }
- }
-
- // Buffer ready, do some sanity checks
- // AddLog_P2(LOG_LEVEL_DEBUG, PSTR("cluster_id = 0x%04X, attr_id = 0x%04X, type_id = 0x%02X"), cluster_id, attr_id, type_id);
- if ((0xFFFF == attr_id) || (0xFFFF == cluster_id)) {
- Response_P(PSTR("{\"%s\":\"%s'%s'\"}"), XdrvMailbox.command, PSTR("Unknown attribute "), key);
- return;
- }
- if (Znodata == type_id) {
- Response_P(PSTR("{\"%s\":\"%s'%s'\"}"), XdrvMailbox.command, PSTR("Unknown attribute type for attribute "), key);
- return;
- }
-
- if (0xFFFF == cluster) {
- cluster = cluster_id; // set the cluster for this packet
- } else if (cluster != cluster_id) {
- ResponseCmndChar_P(PSTR("No more than one cluster id per command"));
- return;
- }
-
- // ////////////////////////////////////////////////////////////////////////////////
- // Split encoding depending on message
- if (operation != ZCL_CONFIGURE_REPORTING) {
- // apply multiplier if needed
- val_d = value.as();
- val_str = value.as();
- ZbApplyMultiplier(val_d, multiplier);
-
- // push the value in the buffer
- buf.add16(attr_id); // prepend with attribute identifier
- if (operation == ZCL_READ_ATTRIBUTES_RESPONSE) {
- buf.add8(Z_SUCCESS); // status OK = 0x00
- }
- buf.add8(type_id); // prepend with attribute type
- int32_t res = encodeSingleAttribute(buf, val_d, val_str, type_id);
- if (res < 0) {
- // remove the attribute type we just added
- // buf.setLen(buf.len() - (operation == ZCL_READ_ATTRIBUTES_RESPONSE ? 4 : 3));
- Response_P(PSTR("{\"%s\":\"%s'%s' 0x%02X\"}"), XdrvMailbox.command, PSTR("Unsupported attribute type "), key, type_id);
+ Z_attribute attr;
+ attr.setKeyName(key);
+ if (Z_parseAttributeKey(attr)) {
+ // Buffer ready, do some sanity checks
+ if (0xFFFF == cluster) {
+ cluster = attr.key.id.cluster; // set the cluster for this packet
+ } else if (cluster != attr.key.id.cluster) {
+ ResponseCmndChar_P(PSTR("No more than one cluster id per command"));
return;
}
+ } else {
+ if (attr.key_is_str) {
+ Response_P(PSTR("{\"%s\":\"%s'%s'\"}"), XdrvMailbox.command, PSTR("Unknown attribute "), key);
+ return;
+ }
+ if (Zunk == attr.attr_type) {
+ Response_P(PSTR("{\"%s\":\"%s'%s'\"}"), XdrvMailbox.command, PSTR("Unknown attribute type for attribute "), key);
+ return;
+ }
+ }
+
+ if (value.is()) {
+ attr.setStr(value.as());
+ } else if (value.is()) {
+ attr.setFloat(value.as());
+ }
+
+ double val_d = 0; // I try to avoid `double` but this type capture both float and (u)int32_t without prevision loss
+ const char* val_str = ""; // variant as string
+ ////////////////////////////////////////////////////////////////////////////////
+ // Split encoding depending on message
+ if (operation != ZCL_CONFIGURE_REPORTING) {
+ if (!ZbAppendWriteBuf(buf, attr, operation == ZCL_READ_ATTRIBUTES_RESPONSE)) {
+ return; // error
+ }
} else {
// ////////////////////////////////////////////////////////////////////////////////
// ZCL_CONFIGURE_REPORTING
@@ -350,7 +321,7 @@ void ZbSendReportWrite(const JsonObject &val_pubwrite, uint16_t device, uint16_t
if (nullptr != &val_attr_rc) {
val_d = val_attr_rc.as();
val_str = val_attr_rc.as();
- ZbApplyMultiplier(val_d, multiplier);
+ ZbApplyMultiplier(val_d, attr.attr_multiplier);
}
// read TimeoutPeriod
@@ -358,22 +329,22 @@ void ZbSendReportWrite(const JsonObject &val_pubwrite, uint16_t device, uint16_t
const JsonVariant &val_attr_timeout = GetCaseInsensitive(attr_config, PSTR("TimeoutPeriod"));
if (nullptr != &val_attr_timeout) { attr_timeout = strToUInt(val_attr_timeout); }
- bool attr_discrete = Z_isDiscreteDataType(type_id);
+ bool attr_discrete = Z_isDiscreteDataType(attr.attr_type);
// all fields are gathered, output the butes into the buffer, ZCL 2.5.7.1
// common bytes
buf.add8(attr_direction ? 0x01 : 0x00);
- buf.add16(attr_id);
+ buf.add16(attr.key.id.attr_id);
if (attr_direction) {
buf.add16(attr_timeout);
} else {
- buf.add8(type_id);
+ buf.add8(attr.attr_type);
buf.add16(attr_min_interval);
buf.add16(attr_max_interval);
if (!attr_discrete) {
- int32_t res = encodeSingleAttribute(buf, val_d, val_str, type_id);
+ int32_t res = encodeSingleAttribute(buf, val_d, val_str, attr.attr_type);
if (res < 0) {
- Response_P(PSTR("{\"%s\":\"%s'%s' 0x%02X\"}"), XdrvMailbox.command, PSTR("Unsupported attribute type "), key, type_id);
+ Response_P(PSTR("{\"%s\":\"%s'%s' 0x%02X\"}"), XdrvMailbox.command, PSTR("Unsupported attribute type "), key, attr.attr_type);
return;
}
}
@@ -1312,6 +1283,13 @@ void CmndZbConfig(void) {
const JsonVariant &val_txradio = GetCaseInsensitive(json, PSTR("TxRadio"));
if (nullptr != &val_txradio) { zb_txradio_dbm = strToUInt(val_txradio); }
+ // if network key is zero, we generate a truly random key with a hardware generator from ESP
+ if ((0 == zb_precfgkey_l) && (0 == zb_precfgkey_h)) {
+ AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_ZIGBEE "generating random Zigbee network key"));
+ zb_precfgkey_l = (uint64_t)HwRandom() << 32 | HwRandom();
+ zb_precfgkey_h = (uint64_t)HwRandom() << 32 | HwRandom();
+ }
+
// Check if a parameter was changed after all
if ( (zb_channel != Settings.zb_channel) ||
(zb_pan_id != Settings.zb_pan_id) ||
From 3368b737531ef5f047b13f00b36b5d13e3eebe5d Mon Sep 17 00:00:00 2001
From: Staars
Date: Tue, 15 Sep 2020 10:45:24 +0200
Subject: [PATCH 050/148] update NimBLE-Arduino to 1.0.2
---
libesp32/NimBLE-Arduino/API_DIFFERENCES.md | 245 -----------
libesp32/NimBLE-Arduino/CHANGELOG.md | 38 ++
libesp32/NimBLE-Arduino/README.md | 96 ++---
.../docs/Improvements_and_updates.md | 136 ++++++
.../NimBLE-Arduino/docs/Migration_guide.md | 398 ++++++++++++++++++
.../NimBLE-Arduino/docs/New_user_guide.md | 339 +++++++++++++++
.../examples/NimBLE_Client/NimBLE_Client.ino | 33 +-
.../examples/NimBLE_Server/NimBLE_Server.ino | 57 +--
.../BLE_notify/BLE_notify.ino | 13 +-
.../BLE_server/BLE_server.ino | 6 +-
.../BLE_server_multiconnect.ino | 13 +-
.../BLE_uart/BLE_uart.ino | 14 +-
libesp32/NimBLE-Arduino/library.properties | 8 +-
libesp32/NimBLE-Arduino/src/FreeRTOS.cpp | 7 +-
libesp32/NimBLE-Arduino/src/FreeRTOS.h | 9 +-
libesp32/NimBLE-Arduino/src/NimBLE2902.cpp | 79 ----
libesp32/NimBLE-Arduino/src/NimBLE2902.h | 59 ---
libesp32/NimBLE-Arduino/src/NimBLEAddress.cpp | 98 +++--
libesp32/NimBLE-Arduino/src/NimBLEAddress.h | 15 +-
.../src/NimBLEAdvertisedDevice.cpp | 353 +++++++++-------
.../src/NimBLEAdvertisedDevice.h | 88 ++--
.../NimBLE-Arduino/src/NimBLEAdvertising.cpp | 174 ++++++--
.../NimBLE-Arduino/src/NimBLEAdvertising.h | 30 +-
libesp32/NimBLE-Arduino/src/NimBLEBeacon.cpp | 70 ++-
.../src/NimBLECharacteristic.cpp | 217 ++++++----
.../NimBLE-Arduino/src/NimBLECharacteristic.h | 25 ++
libesp32/NimBLE-Arduino/src/NimBLEClient.cpp | 221 ++++++----
libesp32/NimBLE-Arduino/src/NimBLEClient.h | 50 ++-
.../NimBLE-Arduino/src/NimBLEDescriptor.h | 6 +
libesp32/NimBLE-Arduino/src/NimBLEDevice.cpp | 223 +++++++---
libesp32/NimBLE-Arduino/src/NimBLEDevice.h | 14 +-
.../NimBLE-Arduino/src/NimBLEEddystoneTLM.cpp | 77 +++-
.../NimBLE-Arduino/src/NimBLEEddystoneURL.cpp | 47 ++-
.../src/NimBLERemoteCharacteristic.cpp | 67 +--
.../src/NimBLERemoteCharacteristic.h | 36 +-
.../src/NimBLERemoteDescriptor.cpp | 36 +-
.../src/NimBLERemoteDescriptor.h | 20 +-
.../src/NimBLERemoteService.cpp | 30 +-
.../NimBLE-Arduino/src/NimBLERemoteService.h | 2 +-
libesp32/NimBLE-Arduino/src/NimBLEScan.cpp | 85 ++--
libesp32/NimBLE-Arduino/src/NimBLEScan.h | 15 +-
.../NimBLE-Arduino/src/NimBLESecurity.cpp | 48 ++-
libesp32/NimBLE-Arduino/src/NimBLESecurity.h | 28 +-
libesp32/NimBLE-Arduino/src/NimBLEServer.cpp | 173 +++++++-
libesp32/NimBLE-Arduino/src/NimBLEServer.h | 64 ++-
libesp32/NimBLE-Arduino/src/NimBLEService.cpp | 164 ++++----
libesp32/NimBLE-Arduino/src/NimBLEService.h | 4 +-
libesp32/NimBLE-Arduino/src/NimBLEUUID.cpp | 55 ++-
libesp32/NimBLE-Arduino/src/NimBLEUUID.h | 17 +-
libesp32/NimBLE-Arduino/src/NimBLEUtils.cpp | 219 +---------
libesp32/NimBLE-Arduino/src/NimBLEUtils.h | 10 +-
.../src/esp-hci/src/esp_nimble_hci.c | 42 +-
.../host/mesh/src/src/ble_hs_resolv_priv.h | 1 +
.../src/nimble/host/src/ble_hs_resolv.c | 59 ++-
.../src/nimble/host/src/ble_hs_resolv_priv.h | 1 +
libesp32/NimBLE-Arduino/src/nimconfig.h | 131 ++++--
.../src/porting/nimble/src/os_mempool.c | 3 +-
57 files changed, 3005 insertions(+), 1563 deletions(-)
delete mode 100644 libesp32/NimBLE-Arduino/API_DIFFERENCES.md
create mode 100644 libesp32/NimBLE-Arduino/CHANGELOG.md
create mode 100644 libesp32/NimBLE-Arduino/docs/Improvements_and_updates.md
create mode 100644 libesp32/NimBLE-Arduino/docs/Migration_guide.md
create mode 100644 libesp32/NimBLE-Arduino/docs/New_user_guide.md
delete mode 100644 libesp32/NimBLE-Arduino/src/NimBLE2902.cpp
delete mode 100644 libesp32/NimBLE-Arduino/src/NimBLE2902.h
diff --git a/libesp32/NimBLE-Arduino/API_DIFFERENCES.md b/libesp32/NimBLE-Arduino/API_DIFFERENCES.md
deleted file mode 100644
index 91b0fd78f..000000000
--- a/libesp32/NimBLE-Arduino/API_DIFFERENCES.md
+++ /dev/null
@@ -1,245 +0,0 @@
-# Server API differnces:
-
-### Characteristics:
-When creating a characteristic the properties are now set with `NIMBLE_PROPERTY::XXXX` instead of `BLECharacteristic::XXXX`.
-
-#### Previous:
-```
-BLECharacteristic::PROPERTY_READ |
-BLECharacteristic::PROPERTY_WRITE
-```
-
-#### Changed to:
-```
-NIMBLE_PROPERTY::READ |
-NIMBLE_PROPERTY::WRITE
-```
-
-#### The full list of properties:
-```
-NIMBLE_PROPERTY::READ
-NIMBLE_PROPERTY::READ_ENC
-NIMBLE_PROPERTY::READ_AUTHEN
-NIMBLE_PROPERTY::READ_AUTHOR
-NIMBLE_PROPERTY::WRITE
-NIMBLE_PROPERTY::WRITE_NR
-NIMBLE_PROPERTY::WRITE_ENC
-NIMBLE_PROPERTY::WRITE_AUTHEN
-NIMBLE_PROPERTY::WRITE_AUTHOR
-NIMBLE_PROPERTY::BROADCAST
-NIMBLE_PROPERTY::NOTIFY
-NIMBLE_PROPERTY::INDICATE
-```
-
-### Descriptors:
-Descriptors are now created using the NimBLEcharacteristic method `createDescriptor()`.
-
-The previous method `addDescriptor()` is now a private function in the library.
-
-This was done because the NimBLE host automatically creates a 0x2902 descriptor if a characteristic has notify or indicate properties applied.
-Due to this fact, this library also creates one automatically for your application.
-The only reason to manually create this descriptor now is to assign callback functions.
-If you do not require this functionality you can safely exclude the manual creation of that descriptor.
-
-
-For any other descriptor, (except 0x2904, see below) it should now be created just as characteristics are
-by invoking the `NimBLECharacteristic::createDescriptor` methods.
-Which are defined as:
-```
-NimBLEDescriptor* createDescriptor(const char* uuid,
- uint32_t properties = NIMBLE_PROPERTY::READ |
- NIMBLE_PROPERTY::WRITE,
- uint16_t max_len = 100);
-
-NimBLEDescriptor* createDescriptor(NimBLEUUID uuid,
- uint32_t properties = NIMBLE_PROPERTY::READ |
- NIMBLE_PROPERTY::WRITE,
- uint16_t max_len = 100);
-```
-##### Example:
-```
-pDescriptor = pCharacteristic->createDescriptor("ABCD",
- NIMBLE_PROPERTY::READ |
- NIMBLE_PROPERTY::WRITE |
- NIMBLE_PROPERTY::WRITE_ENC,
- 25);`
-```
-Would create a descriptor with the UUID 0xABCD, publicly readable but only writable if paired/bonded (encrypted) and has a max value length of 25 bytes.
-
-For the 0x2904 descriptor, there is a special class that is created when you call `createDescriptor("2904")`.
-
-The pointer returned is of the base class `NimBLEDescriptor` but the call will create the derived class of `NimBLE2904` so you must cast the returned pointer to `NimBLE2904*` to access the specific class methods.
-
-##### Example:
-```
-p2904 = (NimBLE2904*)pCharacteristic->createDescriptor("2904");
-```
-
-#### Server Security:
-Security is set on the characteristic or descriptor properties by applying one of the following:
-```
-NIMBLE_PROPERTY::READ_ENC
-NIMBLE_PROPERTY::READ_AUTHEN
-NIMBLE_PROPERTY::READ_AUTHOR
-NIMBLE_PROPERTY::WRITE_ENC
-NIMBLE_PROPERTY::WRITE_AUTHEN
-NIMBLE_PROPERTY::WRITE_AUTHOR
-```
-When a peer wants to read or write a characteristic or descriptor with any of these properties applied
-it will trigger the pairing process. By default the "just-works" pairing will be performed automatically.
-This can be changed to use passkey authentication or numeric confirmation. See below for details.
-
-
-# Client API Differences:
-The `BLEAdvertisedDeviceCallbacks` class `onResult()` method now receives a pointer to the
-`NimBLEAdvertisedDevice` object instead of a copy.
-
-`NimBLEClient::connect()` now takes an extra parameter to indicate if the client should download the services
- database from the peripheral, default value is true.
-
-Defined as:
-```
-bool connect(NimBLEAdvertisedDevice* device, bool refreshServices = true);
-bool connect(NimBLEAddress address, uint8_t type = BLE_ADDR_PUBLIC, bool refreshServices = true);
-```
-If set to false the client will use the services database it retrieved from the peripheral last time it connected.
-This allows for faster connections and power saving if the devices just dropped connection and want to reconnect.
-
-```
-NimBLERemoteCharacteristic::writeValue();
-NimBLERemoteCharacteristic::registerForNotify();
-```
-Now return true or false to indicate success or failure so you can choose to disconnect or try again.
-
-```
-NimBLEClient::getServices()
-NimBLERemoteService::getCharacteristics()
-```
-Now return a pointer to a `std::vector` of the respective object database instead of `std::map`.
-
-`NimBLERemoteService::getCharacteristicsByHandle()`
-Has been removed from the API as it is no longer maintained in the library.
-
-The last two above changes reduce the heap usage significantly with minimal application code adjustments.
-
-**UPDATED** on June 21, 2020
-> ```
-> NimBLEClient::getServices(bool refresh = false)
-> NimBLERemoteService::getCharacteristics(bool refresh = false)
-> NimBLERemoteCharacteristic::getDecriptors(bool refresh = false)
->```
-These methods now take an optional (bool) parameter.
-If true it will clear the respective vector and retrieve all the respective attributes from the peripheral.
-If false(default) it will return the respective vector empty or otherwise with the currently stored attributes.
-
-**Removed:** the automatic discovery of all peripheral attributes as they consumed time and resources for data
-the user may not be interested in.
-
-**Added:** `NimBLEClient::discoverAtrributes()` for the user to discover all the peripheral attributes
-to replace the the former functionality.
-
-
-> ```
->getService(NimBLEUUID)
->getCharacteristic(NimBLEUUID)
->getDescriptor(NimBLEUUID)
->```
-These methods will now check the respective vectors for the attribute object and, if not found, will retrieve (only)
-the specified attribute from the peripheral.
-
-These changes allow more control for the user to manage the resources used for the attributes.
-***
-#### Client Security:
-The client will automatically initiate security when the peripheral responds that it's required.
-The default configuration will use "just-works" pairing with no bonding, if you wish to enable bonding see below.
-
-
-# Security:
-Security callback functions are now incorporated in the client/server Callbacks class.
-However backward compatibility with the `BLESecurity` class is retained to minimize app code changes.
-
-The relevant server callbacks are defined as:
-```
-bool onConfirmPIN(uint32_t pin); // accept or reject the passkey
-void onAuthenticationComplete(ble_gap_conn_desc* desc); // auth complete - details in desc
-bool onPassKeyNotify(uint32_t pass_key); // receive the passkey sent by the client, accept or reject
-```
-The relevant client callbacks are defined as:
-```
-bool onConfirmPIN(uint32_t pin); // accept or reject the passkey
-void onAuthenticationComplete(ble_gap_conn_desc* desc); // auth complete - details in desc
-uint32_t onPassKeyRequest(); // return the passkey to send to the server
-```
-
-Security settings and IO capabilities are now set by the corresponding method of `NimBLEDevice::`.
-```
-static void setSecurityAuth(bool bonding, bool mitm, bool sc);
-static void setSecurityAuth(uint8_t auth_req);
-static void setSecurityIOCap(uint8_t iocap);
-static void setSecurityInitKey(uint8_t init_key);
-static void setSecurityRespKey(uint8_t init_key);
-
-
-/**
- * @brief Set the authorization mode for this device.
- * @param bonding, if true we allow bonding, false no bonding will be performed.
- * @param mitm, if true we are capable of man in the middle protection, false if not.
- * @param sc, if true we will perform secure connection pairing, false we will use legacy pairing.
- */
-void NimBLEDevice::setSecurityAuth(bool bonding, bool mitm, bool sc)
-
-
-
-/**
- * @brief Set the authorization mode for this device.
- * @param A bitmap indicating what modes are supported.
- * The bits are defined as follows:
- ** 0x01 BLE_SM_PAIR_AUTHREQ_BOND
- ** 0x04 BLE_SM_PAIR_AUTHREQ_MITM
- ** 0x08 BLE_SM_PAIR_AUTHREQ_SC
- ** 0x10 BLE_SM_PAIR_AUTHREQ_KEYPRESS - not yet supported.
- ** 0xe2 BLE_SM_PAIR_AUTHREQ_RESERVED - for reference only.
- */
-void NimBLEDevice::setSecurityAuth(uint8_t auth_req)
-
-
-
-/**
- * @brief Set the Input/Output capabilities of this device.
- * @param One of the following:
- ** 0x00 BLE_HS_IO_DISPLAY_ONLY DisplayOnly IO capability
- ** 0x01 BLE_HS_IO_DISPLAY_YESNO DisplayYesNo IO capability
- ** 0x02 BLE_HS_IO_KEYBOARD_ONLY KeyboardOnly IO capability
- ** 0x03 BLE_HS_IO_NO_INPUT_OUTPUT NoInputNoOutput IO capability
- ** 0x04 BLE_HS_IO_KEYBOARD_DISPLAY KeyboardDisplay Only IO capability
- */
-void NimBLEDevice::setSecurityIOCap(uint8_t iocap)
-
-
-
-/**
- * @brief If we are the initiator of the security procedure this sets the keys we will distribute.
- * @param A bitmap indicating which keys to distribute during pairing.
- * The bits are defined as follows:
- ** 0x01: BLE_SM_PAIR_KEY_DIST_ENC - Distribute the encryption key.
- ** 0x02: BLE_SM_PAIR_KEY_DIST_ID - Distribute the ID key (IRK).
- ** 0x04: BLE_SM_PAIR_KEY_DIST_SIGN
- ** 0x08: BLE_SM_PAIR_KEY_DIST_LINK
- */
-void NimBLEDevice::setSecurityInitKey(uint8_t init_key)
-
-
-/**
- * @brief Set the keys we are willing to accept during pairing.
- * @param A bitmap indicating which keys to accept during pairing.
- * The bits are defined as follows:
- ** 0x01: BLE_SM_PAIR_KEY_DIST_ENC - Accept the encryption key.
- ** 0x02: BLE_SM_PAIR_KEY_DIST_ID - Accept the ID key (IRK).
- ** 0x04: BLE_SM_PAIR_KEY_DIST_SIGN
- ** 0x08: BLE_SM_PAIR_KEY_DIST_LINK
- */
-void NimBLEDevice::setSecurityRespKey(uint8_t init_key)
-```
-
- I'm sure there are more things I have forgotten but this is all the majors.
- I will update this document as necessary.
diff --git a/libesp32/NimBLE-Arduino/CHANGELOG.md b/libesp32/NimBLE-Arduino/CHANGELOG.md
new file mode 100644
index 000000000..8dfc5a141
--- /dev/null
+++ b/libesp32/NimBLE-Arduino/CHANGELOG.md
@@ -0,0 +1,38 @@
+# Changelog
+
+All notable changes to this project will be documented in this file.
+
+## [1.0.2] - 2020-09-13
+
+### Changed
+
+- `NimBLEAdvertising::start` Now takes 2 optional parameters, the first is the duration to advertise for (in seconds), the second is a
+callback that is invoked when advertsing ends and takes a pointer to a `NimBLEAdvertising` object (similar to the `NimBLEScan::start` API).
+
+- (Arduino) Maximum BLE connections can now be altered by only changing the value of `CONFIG_BT_NIMBLE_MAX_CONNECTIONS` in `nimconfig.h`.
+Any changes to the controller max connection settings in `sdkconfig.h` will now have no effect when using this library.
+
+- (Arduino) Revert the previous change to fix the advertising start delay. Instead a replacement fix that routes all BLE controller commands from
+a task running on core 0 (same as the controller) has been implemented. This improves response times and reliability for all BLE functions.
+
+## [1.0.1] - 2020-09-02
+
+### Added
+
+- Empty `NimBLEAddress` constructor: `NimBLEAddress()` produces an address of 00:00:00:00:00:00 type 0.
+- Documentation of the difference of NimBLEAddress::getNative vs the original bluedroid library.
+
+### Changed
+
+- notify_callback typedef is now defined as std::function to enable the use of std::bind to call a class member function.
+
+### Fixed
+
+- Fix advertising start delay when first called.
+
+
+## [1.0.0] - 2020-08-22
+
+First stable release.
+
+All the original library functionality is complete and many extras added with full documentation.
diff --git a/libesp32/NimBLE-Arduino/README.md b/libesp32/NimBLE-Arduino/README.md
index 04c3a80c5..120b0c782 100644
--- a/libesp32/NimBLE-Arduino/README.md
+++ b/libesp32/NimBLE-Arduino/README.md
@@ -1,36 +1,19 @@
-# *** UPDATES ***
-**Breaking changes:**
-**NEW** on June 21, 2020
-> ```
-> NimBLEClient::getServices(bool refresh = false)
-> NimBLERemoteService::getCharacteristics(bool refresh = false)
-> NimBLERemoteCharacteristic::getDecriptors(bool refresh = false)
->```
-These methods now take an optional (bool) parameter.
-If true it will clear the respective vector and retrieve all the respective attributes from the peripheral.
-If false(default) it will return the respective vector empty or otherwise with the currently stored attributes.
-
-**NEW** on May 23, 2020
-Client and scan now use `std::vector` instead of `std::map` for storing the remote attribute database.
-
-This change will affect your application code if you use `NimBLEClient::getServices()` or `NimBLERemoteService::getCharacteristics()`
-in your application as they now return a pointer to `std::vector` of the respective attributes.
-
-In addition `NimBLERemoteService::getCharacteristicsByHandle()` has been removed as it is no longer maintained in the library.
-
-These changes were necessary due to the amount of resources required to use `std::map`, it was not justifed by any benfit it provided.
-
-It is expected that there will be minimal impact on most applications, if you need help adjusting your code please create an issue.
+[Latest release 
+](https://github.com/h2zero/NimBLE-Arduino/releases/latest/)
+
# NimBLE-Arduino
-A fork of the NimBLE stack restructured for compilation in the Ardruino IDE with a CPP library for use with ESP32.
+A fork of the NimBLE stack restructured for compilation in the Ardruino IDE with a CPP library for use with ESP32.
+
+**Note for IDF users: This repo will not compile correctly in ESP-IDF. An ESP-IDF component version of this library can be [found here.](https://github.com/h2zero/esp-nimble-cpp)**
This library **significantly** reduces resource usage and improves performance for ESP32 BLE applications as compared
with the bluedroid based library. The goal is to maintain, as much as reasonable, compatibility with the original
library but refactored to use the NimBLE stack. In addition, this library will be more actively developed and maintained
-to provide improved capabilites and stability over the original.
+to provide improved capabilites and stability over the original.
+
-## Resource use improvement:
+## Resource use improvement
### (Original) BLE_client example comparison (Debug):
#### Arduino BLE Library
@@ -50,21 +33,29 @@ Memory after connection: Free Heap: **173300**
Sketch uses **603432** bytes (28%) of program storage space.
Memory after connection: Free Heap: **269792**
-**As shown: there is nearly a 50% reduction in flash use and approx. 100kB less ram consumed!**
-
-
-# Installation:
+**As shown: there is nearly a 50% reduction in flash use and approx. 100kB less ram consumed!**
+
-Download as .zip and extract to Arduino/libraries folder, or in Arduino IDE from Sketch menu -> Include library -> Add .Zip library.
+# Installation
+**Arduino Library manager:** Go to `sketch` -> `Include Library` -> `Manage Libraries` and search for NimBLE and install.
+
+**Alternatively:** Download as .zip and extract to Arduino/libraries folder, or in Arduino IDE from Sketch menu -> Include library -> Add .Zip library.
`#include "NimBLEDevice.h"` at the beginning of your sketch.
-Tested and working with esp32-arduino v1.0.2 and 1.0.4 in Arduino IDE v1.8.12 and platform IO.
+Tested and working with esp32-arduino in Arduino IDE and platform IO.
+
+# Using
+This library is intended to be compatible with the original ESP32 BLE functions and types with minor changes.
-# Usage:
+If you have not used the original Bluedroid library please refer to the [New user guide](docs/New_user_guide.md).
-This library is intended to be compatible with the original ESP32 BLE functions and types with minor changes.
+If you are familiar with the original library, see: [The migration guide](docs/Migration_guide.md) for details about breaking changes and migration.
+
+Also see [Improvements_and_updates](docs/Improvements_and_updates.md) for information about non-breaking changes.
+
+[Full API documentation and class list can be found here.](https://h2zero.github.io/esp-nimble-cpp/)
Check the Refactored_original_examples in the examples folder for highlights of the differences with the original library.
@@ -72,26 +63,25 @@ More advanced examples highlighting many available features are in examples/ Nim
Beacon examples provided by @beegee-tokyo are in examples/ BLE_Beacon_Scanner, BLE_EddystoneTLM_Beacon, BLE_EddystoneURL_Beacon.
-Change the settings in the `nimconfig.h` file to customize NimBLE to your project, such as increasing max connections, default is 3.
+Change the settings in the `src/nimconfig.h` file to customize NimBLE to your project,
+such as increasing max connections, default is 3, absolute maximum connections is 9.
+
+# Development Status
+This Library is tracking the esp-nimble repo, nimble-1.2.0-idf master branch, currently [@95bd864.](https://github.com/espressif/esp-nimble)
-# Continuing development:
-
-This Library is tracking the esp-nimble repo, nimble-1.2.0-idf master branch, currently [@46c1d9f.](https://github.com/espressif/esp-nimble)
-
-Also tracking the NimBLE related changes in esp-idf, master branch, currently [@2ef4890.](https://github.com/espressif/esp-idf/tree/master/components/bt/host/nimble)
-
-# Acknowledgments:
-
-* @nkolban and @chegewara for the [original esp32 BLE library](https://github.com/nkolban/esp32-snippets) this project was derived from.
-* @beegee-tokyo for contributing your time to test/debug and contributing the beacon examples.
-* @Jeroen88 for the amazing help debugging and improving the client code.
-
-
-# Todo:
-
-1. Create documentation.
-2. Add BLE Mesh code.
-3. Expose more NimBLE features.
+Also tracking the NimBLE related changes in ESP-IDF, master branch, currently [@2ef4890.](https://github.com/espressif/esp-idf/tree/master/components/bt/host/nimble)
+
+# Acknowledgments
+* [nkolban](https://github.com/nkolban) and [chegewara](https://github.com/chegewara) for the [original esp32 BLE library](https://github.com/nkolban/esp32-snippets/tree/master/cpp_utils) this project was derived from.
+* [beegee-tokyo](https://github.com/beegee-tokyo) for contributing your time to test/debug and contributing the beacon examples.
+* [Jeroen88](https://github.com/Jeroen88) for the amazing help debugging and improving the client code.
+
+# Todo
+- Improve host reset handler
+- Implement random address handling
+- Implement bond management
+- Add Bluetooth Mesh
+
diff --git a/libesp32/NimBLE-Arduino/docs/Improvements_and_updates.md b/libesp32/NimBLE-Arduino/docs/Improvements_and_updates.md
new file mode 100644
index 000000000..245defd2d
--- /dev/null
+++ b/libesp32/NimBLE-Arduino/docs/Improvements_and_updates.md
@@ -0,0 +1,136 @@
+# Improvements and updates
+
+Many improvements have been made to this library vs the original, this is a brief overview of the most significant changes.
+Refer to the [class documentation](https://h2zero.github.io/esp-nimble-cpp/annotated.html) for futher information on class specifics.
+
+* [Server](#server)
+* [Advertising](#advertising)
+* [Client](#client)
+* [General](#general)
+
+
+
+# Server
+
+`NimBLECharacteristic::setValue(const T &s)`
+`NimBLEDescriptor::setValue(const T &s)`
+
+Now use a template to accomodate standard and custom types/values.
+
+**Example**
+```
+struct my_struct{
+ uint8_t one;
+ uint16_t two;
+ uint32_t four;
+ uint64_t eight;
+ float flt;
+}myStruct;
+
+ myStruct.one = 1;
+ myStruct.two = 2;
+ myStruct.four = 4;
+ myStruct.eight = 8;
+ myStruct.flt = 1234.56;
+
+ pCharacteristic->setValue(myStruct);
+ ```
+This will send the struct to the recieving client when read or a notification sent.
+
+`NimBLECharacteristic::getValue` now takes an optional timestamp parameter which will update it's value with
+the time the last value was recieved. In addition an overloaded template has been added to retrieve the value
+as a type specified by the user.
+
+**Example**
+```
+ time_t timestamp;
+ myStruct = pCharacteristic->getValue(×tamp); // timestamp optional
+```
+
+
+**Advertising will automatically start when a client disconnects.**
+
+A new method `NimBLEServer::advertiseOnDisconnect(bool)` has been implemented to control this, true(default) = enabled.
+
+
+`NimBLEServer::removeService` takes an additional parameter `bool deleteSvc` that if true will delete the service
+and all characteristics / descriptors belonging to it and invalidating any pointers to them.
+
+If false the service is only removed from visibility by clients. The pointers to the service and
+it's characteristics / descriptors will remain valid and the service can be re-added in the future
+using `NimBLEServer::addService`.
+
+
+
+# Advertising
+`NimBLEAdvertising::start`
+
+Now takes 2 optional parameters, the first is the duration to advertise for (in seconds), the second is a callback
+that is invoked when advertsing ends and takes a pointer to a `NimBLEAdvertising` object (similar to the `NimBLEScan::start` API).
+
+This provides an opportunity to update the advertisment data if desired.
+
+
+
+# Client
+
+`NimBLERemoteCharacteristic::readValue(time_t\*, bool)`
+`NimBLERemoteDescriptor::readValue(bool)`
+
+Have been added as templates to allow reading the values as any specified type.
+
+**Example**
+```
+struct my_struct{
+ uint8_t one;
+ uint16_t two;
+ uint32_t four;
+ uint64_t eight;
+ float flt;
+}myStruct;
+
+ time_t timestamp;
+ myStruct = pRemoteCharacteristic->readValue(×tamp); // timestamp optional
+```
+
+
+`NimBLERemoteCharacteristic::registerForNotify`
+Has been **deprecated** as now the internally stored characteristic value is updated when notification/indication is recieved.
+
+`NimBLERemoteCharacteristic::subscribe` and `NimBLERemoteCharacteristic::unsubscribe` have been implemented to replace it.
+A callback is no longer requred to get the most recent value unless timing is important. Instead, the application can call `NimBLERemoteCharacteristic::getValue` to
+get the last updated value any time.
+
+In addition `NimBLERemoteCharacteristic::readValue` and `NimBLERemoteCharacteristic::getValue` take an optional timestamp parameter which will update it's value with
+the time the last value was recieved.
+
+> NimBLEClient::getService
+> NimBLERemoteService::getCharacteristic
+> NimBLERemoteCharacteristic::getDescriptor
+
+These methods will now check the respective vectors for the attribute object and, if not found, will retrieve (only)
+the specified attribute from the peripheral.
+
+These changes allow more control for the user to manage the resources used for the attributes.
+
+
+`NimBLEClient::connect()` can now be called without an address or advertised device parameter. This will connect to the
+device with the address previously set when last connected or set with `NimBLEDevice::setPeerAddress()`.
+
+
+# General
+To reduce resource use all instances of `std::map` have been replaced with `std::vector`.
+
+Use of `FreeRTOS::Semaphore` has been removed as it was consuming too much ram, the related files have been left in place to accomodate application use.
+
+Operators `==`, `!=` and `std::string` have been added to `NimBLEAddress` and `NimBLEUUID` for easier comparison and logging.
+
+New constructor for `NimBLEUUID(uint32_t, uint16_t, uint16_t, uint64_t)` added to lower memory use vs string construction. See: [#21](https://github.com/h2zero/NimBLE-Arduino/pull/21).
+
+Security/pairing operations are now handled in the respective `NimBLEClientCallbacks` and `NimBLEServerCallbacks` classes, `NimBLESecurity`(deprecated) remains for backward compatibility.
+
+Configuration options have been added to add or remove debugging information, when disabled (default) significatly reduces binary size.
+In ESP-IDF the options are in menuconfig: `Main menu -> ESP-NimBLE-cpp configuration`.
+For Arduino the options must be commented / uncommented in nimconfig.h.
+
+
diff --git a/libesp32/NimBLE-Arduino/docs/Migration_guide.md b/libesp32/NimBLE-Arduino/docs/Migration_guide.md
new file mode 100644
index 000000000..69671824a
--- /dev/null
+++ b/libesp32/NimBLE-Arduino/docs/Migration_guide.md
@@ -0,0 +1,398 @@
+# Migrating from Bluedroid to NimBLE
+
+This guide describes the required changes to existing projects migrating from the original bluedroid API to NimBLE.
+
+**The changes listed here are only the required changes that must be made**, and a short overview of options for migrating existing applications.
+
+For more information on the improvements and additions please refer to the [class documentation](https://h2zero.github.io/esp-nimble-cpp/annotated.html) and [Improvements and updates](Improvements_and_updates.md)
+
+* [General Changes](#general-information)
+* [Server](#server-api)
+ * [Services](#services)
+ * [characteristics](#characteristics)
+ * [descriptors](#descriptors)
+ * [Security](#server-security)
+* [Advertising](#advertising-api)
+* [Client](#client-api)
+ * [Remote Services](#remote-services)
+ * [Remote characteristics](#remote-characteristics)
+ * [Security](#client-security)
+* [General Security](#security-api)
+* [Configuration](#arduino-configuration)
+
+
+
+## General Information
+
+### Header Files
+All classes are accessible by including `NimBLEDevice.h` in your application, no further headers need to be included.
+
+(Mainly for Arduino) You may choose to include `NimBLELog.h` in your appplication if you want to use the `NIMBLE_LOGx` macros for debugging.
+These macros are used the same way as the `ESP_LOGx` macros.
+
+
+### Class Names
+Class names remain the same as the original with the addition of a "Nim" prefix.
+For example `BLEDevice` is now `NimBLEDevice` and `BLEServer` is now `NimBLEServer` etc.
+
+For convienience definitions have been added to allow applications to use either name for all classes
+this means **no class names need to be changed in existing code** and makes migrating easier.
+
+
+### BLE Addresses
+`BLEAddress` (`NimBLEAddress`) When constructing an address the constructor now takes an *(optional)* `uint8_t type` paramameter
+to specify the address type. Default is (0) Public static address.
+
+For example `BLEAddress addr(11:22:33:44:55:66, 1)` will create the address object with an address type of: 1 (Random).
+
+As this paramameter is optional no changes to existing code are needed, it is mentioned here for information.
+
+`BLEAddress::getNative` (`NimBLEAddress::getNative`) returns a uint8_t pointer to the native address byte array.
+In this library the address bytes are stored in reverse order from the original library. This is due to the way
+the NimBLE stack expects addresses to be presented to it. All other functions such as `toString` are
+not affected as the endian change is made within them.
+
+
+
+## Server API
+Creating a `BLEServer` instance is the same as original, no changes required.
+For example `BLEDevice::createServer()` will work just as it did before.
+
+`BLEServerCallbacks` (`NimBLEServerCallbacks`) has new methods for handling security operations.
+**Note:** All callback methods have default implementations which allows the application to implement only the methods applicable.
+
+
+
+### Services
+Creating a `BLEService` (`NimBLEService`) instance is the same as original, no changes required.
+For example `BLEServer::createService(SERVICE_UUID)` will work just as it did before.
+
+
+### Characteristics
+`BLEService::createCharacteristic` (`NimBLEService::createCharacteristic`) is used the same way as originally except the properties parameter has changed.
+
+When creating a characteristic the properties are now set with `NIMBLE_PROPERTY::XXXX` instead of `BLECharacteristic::XXXX`.
+
+#### Originally
+> BLECharacteristic::PROPERTY_READ |
+> BLECharacteristic::PROPERTY_WRITE
+
+#### Is Now
+> NIMBLE_PROPERTY::READ |
+> NIMBLE_PROPERTY::WRITE
+
+
+#### The full list of properties
+> NIMBLE_PROPERTY::READ
+> NIMBLE_PROPERTY::READ_ENC
+> NIMBLE_PROPERTY::READ_AUTHEN
+> NIMBLE_PROPERTY::READ_AUTHOR
+> NIMBLE_PROPERTY::WRITE
+> NIMBLE_PROPERTY::WRITE_NR
+> NIMBLE_PROPERTY::WRITE_ENC
+> NIMBLE_PROPERTY::WRITE_AUTHEN
+> NIMBLE_PROPERTY::WRITE_AUTHOR
+> NIMBLE_PROPERTY::BROADCAST
+> NIMBLE_PROPERTY::NOTIFY
+> NIMBLE_PROPERTY::INDICATE
+
+
+**Example:**
+```
+BLECharacteristic *pCharacteristic = pService->createCharacteristic(
+ CHARACTERISTIC_UUID,
+ BLECharacteristic::PROPERTY_READ |
+ BLECharacteristic::PROPERTY_WRITE
+ );
+
+```
+Needs to be changed to:
+```
+BLECharacteristic *pCharacteristic = pService->createCharacteristic(
+ CHARACTERISTIC_UUID,
+ NIMBLE_PROPERTY::READ |
+ NIMBLE_PROPERTY::WRITE
+ );
+```
+
+
+`BLECharacteristicCallbacks` (`NimBLECharacteristicCallbacks`) has a new method `NimBLECharacteristicCallbacks::onSubscribe`
+which is called when a client subscribes to notifications/indications.
+
+**Note:** All callback methods have default implementations which allows the application to implement only the methods applicable.
+
+
+> BLECharacteristic::getData
+
+**Has been removed from the API.**
+Originally this returned a `uint8_t*` to the internal data, which is volatile.
+To prevent possibly throwing exceptions this has been removed and `NimBLECharacteristic::getValue` should be used
+to get a copy of the data first which can then safely be accessed via pointer.
+
+**Example:**
+```
+std::string value = pCharacteristic->getValue();
+uint8_t *pData = (uint8_t*)value.data();
+```
+Alternatively use the `getValue` template:
+```
+my_struct_t myStruct = pChr->getValue();
+```
+
+
+
+### Descriptors
+The previous method `BLECharacteristic::addDescriptor()` has been removed.
+
+Descriptors are now created using the `NimBLECharacteristic::createDescriptor` method.
+
+BLE2902 or NimBLE2902 class has been removed.
+NimBLE automatically creates the 0x2902 descriptor if a characteristic has a notification or indication property assigned to it.
+
+It was no longer useful to have a class for the 0x2902 descriptor as a new callback `NimBLECharacteristicCallbacks::onSubscribe` was added
+to handle callback functionality and the client subscription status is handled internally.
+
+**Note:** Attempting to create a 0x2902 descriptor will trigger an assert to notify the error,
+allowing the creation of it would cause a fault in the NimBLE stack.
+
+All other descriptors are now created just as characteristics are by using the `NimBLECharacteristic::createDescriptor` method (except 0x2904, see below).
+Which are defined as:
+```
+NimBLEDescriptor* createDescriptor(const char* uuid,
+ uint32_t properties =
+ NIMBLE_PROPERTY::READ |
+ NIMBLE_PROPERTY::WRITE,
+ uint16_t max_len = 100);
+
+NimBLEDescriptor* createDescriptor(NimBLEUUID uuid,
+ uint32_t properties =
+ NIMBLE_PROPERTY::READ |
+ NIMBLE_PROPERTY::WRITE,
+ uint16_t max_len = 100);
+```
+##### Example
+```
+pDescriptor = pCharacteristic->createDescriptor("ABCD",
+ NIMBLE_PROPERTY::READ |
+ NIMBLE_PROPERTY::WRITE |
+ NIMBLE_PROPERTY::WRITE_ENC,
+ 25);
+```
+Would create a descriptor with the UUID 0xABCD, publicly readable but only writable if paired/bonded (encrypted) and has a max value length of 25 bytes.
+
+
+For the 0x2904, there is a special class that is created when you call `createDescriptor("2904").
+
+The pointer returned is of the base class `NimBLEDescriptor` but the call will create the derived class of `NimBLE2904` so you must cast the returned pointer to
+`NimBLE2904` to access the specific class methods.
+
+##### Example
+```
+p2904 = (NimBLE2904*)pCharacteristic->createDescriptor("2904");
+```
+
+
+
+### Server Security
+Security is set on the characteristic or descriptor properties by applying one of the following:
+> NIMBLE_PROPERTY::READ_ENC
+> NIMBLE_PROPERTY::READ_AUTHEN
+> NIMBLE_PROPERTY::READ_AUTHOR
+> NIMBLE_PROPERTY::WRITE_ENC
+> NIMBLE_PROPERTY::WRITE_AUTHEN
+> NIMBLE_PROPERTY::WRITE_AUTHOR
+
+When a peer wants to read or write a characteristic or descriptor with any of these properties applied
+it will trigger the pairing process. By default the "just-works" pairing will be performed automatically.
+This can be changed to use passkey authentication or numeric comparison. See [Security API](#security-api) for details.
+
+
+
+## Advertising API
+Advertising works the same as the original API except:
+> BLEAdvertising::setMinPreferred
+> BLEAdvertising::setMaxPreferred
+
+These methods were found to not provide useful functionality and consumed valuable advertising space (6 bytes of 31) if used unknowingly.
+If you wish to advertise these parameters you can still do so manually via `BLEAdvertisementData::addData` (`NimBLEAdvertisementData::addData`).
+
+
+Calling `NimBLEAdvertising::setAdvertisementData` will entirely replace any data set with `NimBLEAdvertising::addServiceUUID`, or
+`NimBLEAdvertising::setAppearance`. You should set all the data you wish to advertise within the `NimBLEAdvertisementData` instead.
+
+Calling `NimBLEAdvertising::setScanResponseData` without also calling `NimBLEAdvertising::setAdvertisementData` will have no effect.
+When using custom scan response data you must also use custom advertisement data.
+
+
+> BLEAdvertising::start (NimBLEAdvertising::start)
+
+Now takes 2 optional parameters, the first is the duration to advertise for (in seconds), the second is a callback
+that is invoked when advertsing ends and takes a pointer to a `NimBLEAdvertising` object (similar to the `NimBLEScan::start` API).
+
+This provides an opportunity to update the advertisment data if desired.
+
+
+
+## Client API
+
+Client instances are created just as before with `BLEDevice::createClient` (`NimBLEDevice::createClient`).
+
+Multiple client instances can be created, up to the maximum number of connections set in the config file (default: 3).
+To delete a client instance you must use `NimBLEDevice::deleteClient`.
+
+`BLEClient::connect`(`NimBLEClient::connect`) Has had it's parameters altered.
+Defined as:
+> NimBLEClient::connect(bool deleteServices = true);
+> NimBLEClient::connect(NimBLEAdvertisedDevice\* device, bool deleteServices = true);
+> NimBLEClient::connect(NimBLEAddress address, bool deleteServices = true);
+
+The type parameter has been removed and a new bool parameter has been added to indicate if the client should
+delete the attribute database previously retrieved (if applicable) for the peripheral, default value is true.
+If set to false the client will use the attribute database it retrieved from the peripheral when previously connected.
+This allows for faster connections and power saving if the devices dropped connection and are reconnecting.
+
+
+> `BLEClient::getServices` (`NimBLEClient::getServices`)
+
+This method now takes an optional (bool) parameter to indicate if the services should be retrieved from the server (true) or
+the currently known database returned (false : default).
+Also now returns a pointer to `std::vector` instead of `std::map`.
+
+
+**Removed:** the automatic discovery of all peripheral attributes as they consumed time and resources for data
+the user may not be interested in.
+
+**Added:** `NimBLEClient::discoverAttributes` for the user to discover all the peripheral attributes
+to replace the the removed automatic functionality.
+
+
+
+### Remote Services
+`BLERemoteService` (`NimBLERemoteService`) Methods remain mostly unchanged with the exceptions of:
+
+> BLERemoteService::getCharacteristicsByHandle
+
+This method has been removed.
+
+
+> `BLERemoteService::getCharacteristics` (`NimBLERemoteService::getCharacteristics`)
+
+This method now takes an optional (bool) parameter to indicate if the characteristics should be retrieved from the server (true) or
+the currently known database returned (false : default).
+Also now returns a pointer to `std::vector` instead of `std::map`.
+
+
+
+### Remote Characteristics
+`BLERemoteCharacteristic` (`NimBLERemoteCharacteristic`) There have been a few changes to the methods in this class:
+
+> `BLERemoteCharacteristic::writeValue` (`NimBLERemoteCharacteristic::writeValue`)
+> `BLERemoteCharacteristic::registerForNotify` (`NimBLERemoteCharacteristic::registerForNotify`)
+
+Now return true or false to indicate success or failure so you can choose to disconnect or try again.
+
+
+> `BLERemoteCharacteristic::registerForNotify` (`NimBLERemoteCharacteristic::registerForNotify`)
+
+Is now **deprecated**.
+> `NimBLERemoteCharacteristic::subscribe`
+> `NimBLERemoteCharacteristic::unsubscribe`
+
+Are the new methods added to replace it.
+
+
+> `BLERemoteCharacteristic::readUInt8` (`NimBLERemoteCharacteristic::readUInt8`)
+> `BLERemoteCharacteristic::readUInt16` (`NimBLERemoteCharacteristic::readUInt16`)
+> `BLERemoteCharacteristic::readUInt32` (`NimBLERemoteCharacteristic::readUInt32`)
+> `BLERemoteCharacteristic::readFloat` (`NimBLERemoteCharacteristic::readFloat`)
+
+Are **deprecated** a template: NimBLERemoteCharacteristic::readValue(time_t\*, bool) has been added to replace them.
+
+
+> `BLERemoteCharacteristic::readRawData`
+
+**Has been removed from the API**
+Originally it stored an unnecessary copy of the data and was returning a `uint8_t` pointer to volatile internal data.
+The user application should use `NimBLERemoteCharacteristic::readValue` or `NimBLERemoteCharacteristic::getValue`.
+To obatain a copy of the data, then cast the returned std::string to the type required such as:
+```
+std::string value = pChr->readValue();
+uint8_t *data = (uint8_t*)value.data();
+```
+Alternatively use the `readValue` template:
+```
+my_struct_t myStruct = pChr->readValue();
+```
+
+
+> `BLERemoteCharacteristic::getDescriptors` (`NimBLERemoteCharacteristic::getDescriptors`)
+
+This method now takes an optional (bool) parameter to indicate if the descriptors should be retrieved from the server (true) or
+the currently known database returned (false : default).
+Also now returns a pointer to `std::vector` instead of `std::map`.
+
+
+
+### Client Security
+The client will automatically initiate security when the peripheral responds that it's required.
+The default configuration will use "just-works" pairing with no bonding, if you wish to enable bonding see below.
+
+
+
+## Security API
+Security operations have been moved to `BLEDevice` (`NimBLEDevice`).
+
+Also security callback methods are now incorporated in the `NimBLEServerCallbacks` / `NimBLEClientCallbacks` classes.
+However backward compatibility with the original `BLESecurity` (`NimBLESecurity`) class is retained to minimize application code changes.
+
+The callback methods are:
+
+> `bool onConfirmPIN(uint32_t pin)`
+
+Receives the pin when using numeric comparison authentication, `return true;` to accept.
+
+
+> `uint32_t onPassKeyRequest()`
+
+For server callback; return the passkey expected from the client.
+For client callback; return the passkey to send to the server.
+
+
+> `void onAuthenticationComplete(ble_gap_conn_desc\* desc)`
+
+Authentication complete, success or failed information is in `desc`.
+
+
+Security settings and IO capabilities are now set by the following methods of NimBLEDevice.
+> `NimBLEDevice::setSecurityAuth(bool bonding, bool mitm, bool sc)`
+> `NimBLEDevice::setSecurityAuth(uint8_t auth_req)`
+
+Sets the authorization mode for this device.
+
+
+> `NimBLEDevice::setSecurityIOCap(uint8_t iocap)`
+
+Sets the Input/Output capabilities of this device.
+
+
+> `NimBLEDevice::setSecurityInitKey(uint8_t init_key)`
+
+If we are the initiator of the security procedure this sets the keys we will distribute.
+
+
+> `NimBLEDevice::setSecurityRespKey(uint8_t resp_key)`
+
+Sets the keys we are willing to accept from the peer during pairing.
+
+
+
+## Arduino Configuration
+
+Unlike the original library pre-packaged in the esp32-arduino, this library has all the configuration
+options that are normally set in menuconfig available in the *src/nimconfig.h* file.
+
+This allows Arduino users to fully customize the build, such as increasing max connections
+or loading the BLE stack into external PSRAM.
+
+For details on the options, they are fully commented in *nimconfig.h*
+
diff --git a/libesp32/NimBLE-Arduino/docs/New_user_guide.md b/libesp32/NimBLE-Arduino/docs/New_user_guide.md
new file mode 100644
index 000000000..9f0eeee7d
--- /dev/null
+++ b/libesp32/NimBLE-Arduino/docs/New_user_guide.md
@@ -0,0 +1,339 @@
+# New User Guide
+
+**Note:** If you are migrating an existing project from the original Bluedroid library please see the [Migration Guide.](Migration_guide.md)
+
+If you are a new user this will guide you through a simple server and client application.
+
+* [Creating a Server](#creating-a-server)
+* [Creating a Client](#creating-a-client)
+
+
+## Include Files
+At the top of your application file add `#include NimBLEDevice.h`, this is the only header required and provides access to all classes.
+
+
+## Using the Library
+In order to perform any BLE tasks you must first initialize the library, this prepares the NimBLE stack to be ready for commands.
+
+To do this you must call `NimBLEDevice::init("your device name here")`, the parameter passed is a character string containing the name you want to advertise.
+If you're not creating a server or do not want to advertise a name, simply pass an empty string for the parameter.
+
+This can be called any time you wish to use BLE functions and does not need to be called from app_main(IDF) or setup(Arduino) but usually is.
+
+
+
+## Creating a Server
+BLE servers perform 2 tasks, they advertise their existance for clients to find them and they provide services which contain information for the connecting client.
+
+After initializing the NimBLE stack we create a server by calling `NimBLEDevice::createServer()`, this will create a server instance and return a pointer to it.
+
+Once we have created the server we need to tell it the services it hosts.
+To do this we call `NimBLEServer::createService(const char* uuid)`. Which returns a pointer to an instance of `NimBLEService`.
+The `uuid` parameter is a hexadecimal string with the uuid we want to give the service, it can be 16, 32, or 128 bits.
+
+For this example we will keep it simple and use a 16 bit value: ABCD.
+
+
+**Example code:**
+```
+#include "NimBLEDevice.h"
+
+// void setup() in Arduino
+void app_main(void)
+{
+ NimBLEDevice::init("NimBLE");
+
+ NimBLEServer *pServer = NimBLEDevice::createServer();
+ NimBLEService *pService = pServer->createService("ABCD");
+}
+```
+
+Now we have NimBLE initialized, a server created and a service assigned to it.
+We can't do much with this yet so now we should add a characteristic to the service to provide some data.
+
+Next we call `NimBLEService::createCharacteristic` which returns a pointer to an instance of `NimBLECharacteristic`, and takes two parameters:
+A `uuid` to specify the UUID of the characteristic and a bitmask of the properties we want applied to it.
+
+Just as with the service UUID we will use a simple 16 bit value: 1234.
+The properties bitmask is a little more involved. It is a combination of NIMBLE_PROPERTY:: values.
+
+Here is the list of options:
+> NIMBLE_PROPERTY\::READ
+> NIMBLE_PROPERTY\::READ_ENC
+> NIMBLE_PROPERTY\::READ_AUTHEN
+> NIMBLE_PROPERTY\::READ_AUTHOR
+> NIMBLE_PROPERTY\::WRITE
+> NIMBLE_PROPERTY\::WRITE_NR
+> NIMBLE_PROPERTY\::WRITE_ENC
+> NIMBLE_PROPERTY\::WRITE_AUTHEN
+> NIMBLE_PROPERTY\::WRITE_AUTHOR
+> NIMBLE_PROPERTY\::BROADCAST
+> NIMBLE_PROPERTY\::NOTIFY
+> NIMBLE_PROPERTY\::INDICATE
+
+For this example we won't need to specify these as the default value is `NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::WRITE`
+which will allow reading and writing values to the characteristic without encryption or security.
+The function call will simply be `pService->createCharacteristic("1234");`
+
+
+**Our example code now is:**
+```
+#include "NimBLEDevice.h"
+
+// void setup() in Arduino
+void app_main(void)
+{
+ NimBLEDevice::init("NimBLE");
+
+ NimBLEServer *pServer = NimBLEDevice::createServer();
+ NimBLEService *pService = pServer->createService("ABCD");
+ NimBLECharacteristic *pCharacteristic = pService->createCharacteristic("1234");
+}
+```
+
+All that's left to do now is start the sevice, give the characteristic a value and start advertising for clients.
+
+Fist we start the service by calling `NimBLEService::start()`.
+
+Next we need to call `NimBLECharacteristic::setValue` to set the characteristic value that the client will read.
+There are many different types you can send as parameters for the value but for this example we will use a simple string.
+`pCharacteristic->setValue("Hello BLE");`
+
+Next we need to advertise for connections.
+To do this we create an instance of `NimBLEAdvertising` add our service to it (optional) and start advertisng.
+
+**The code for this will be:**
+```
+NimBLEAdvertising *pAdvertising = NimBLEDevice::getAdvertising(); // create advertising instance
+pAdvertising->addServiceUUID("ABCD"); // tell advertising the UUID of our service
+pAdvertising->start(); // start advertising
+```
+That's it, this will be enough to create a BLE server with a service and a characteristic and advertise for client connections.
+
+**The full example code:**
+```
+#include "NimBLEDevice.h"
+
+// void setup() in Arduino
+void app_main(void)
+{
+ NimBLEDevice::init("NimBLE");
+
+ NimBLEServer *pServer = NimBLEDevice::createServer();
+ NimBLEService *pService = pServer->createService("ABCD");
+ NimBLECharacteristic *pCharacteristic = pService->createCharacteristic("1234");
+
+ pService->start();
+ pCharacteristic->setValue("Hello BLE");
+
+ NimBLEAdvertising *pAdvertising = NimBLEDevice::getAdvertising();
+ pAdvertising->addServiceUUID("ABCD");
+ pAdvertising->start();
+}
+```
+
+Now if you scan with your phone using nRFConnect or any other BLE app you should see a device named "NimBLE" with a service of "ABCD".
+
+For more advanced features and options please see the server examples in the examples folder.
+
+
+
+## Creating a Client
+
+BLE clients perform 2 tasks, they scan for advertising servers and form connections to them to read and write to their characteristics/descriptors.
+
+After initializing the NimBLE stack we create a scan instance by calling `NimBLEDevice::getScan()`, this will create a `NimBLEScan` instance and return a pointer to it.
+
+Once we have created the scan we can start looking for advertising servers.
+
+To do this we call `NimBLEScan::start(duration)`, the duration parameter is a uint32_t that specifies the number of seconds to scan for,
+passing 0 will scan forever.
+
+In this example we will scan for 10 seconds. This is a blocking function (a non blocking overload is also available).
+This call returns an instance of `NimBLEScanResults` when the scan completes which can be parsed for advertisers we are interested in.
+
+**Example Code:**
+```
+#include "NimBLEDevice.h"
+
+// void setup() in Arduino
+void app_main(void)
+{
+ NimBLEDevice::init("");
+
+ NimBLEScan *pScan = NimBLEDevice::getScan();
+ NimBLEScanResults results = pScan->start(10);
+}
+```
+
+
+Now that we have scanned we need to check the results for any advertisers we are interested in connecting to.
+
+To do this we iterate through the results and check if any of the devices found are advertising the service we want `ABCD`.
+Each result in `NimBLEScanResults` is a `NimBLEAdvertisedDevice` instance that we can access data from.
+
+We will check each device found for the `ABCD` service by calling `NimBLEAdvertisedDevice::isAdvertisingService`.
+This takes an instance of `NimBLEUUID` as a parameter so we will need to create one.
+
+**The code for this looks like:**
+```
+NimBLEUUID serviceUuid("ABCD");
+
+for(int i = 0; i < results.getCount(); i++) {
+ NimBLEAdvertisedDevice device = results.getDevice(i);
+
+ if (device.isAdvertisingService(serviceUuid)) {
+ // create a client and connect
+ }
+}
+```
+
+
+Now that we can scan and parse advertisers we need to be able to create a `NimBLEClient` instance and use it to connect.
+
+To do this we call `NimBLEDevice::createClient` which creates the `NimBLEClient` instance and returns a pointer to it.
+
+After this we call `NimBLEClient::connect` to connect to the advertiser.
+This takes a pointer to the `NimBLEAdvertisedDevice` and returns `true` if successful.
+
+**Lets do that now:**
+```
+NimBLEUUID serviceUuid("ABCD");
+
+for(int i = 0; i < results.getCount(); i++) {
+ NimBLEAdvertisedDevice device = results.getDevice(i);
+
+ if (device.isAdvertisingService(serviceUuid)) {
+ NimBLEClient *pClient = NimBLEDevice::createClient();
+
+ if(pClient->connect(&device)) {
+ //success
+ } else {
+ // failed to connect
+ }
+ }
+}
+```
+As shown, the call to `NimBLEClient::connect` should have it's eturn value tested to make sure it succeeded before proceeding to get data.
+
+
+Next we need to access the servers data by asking it for the service and the characteristic we are interested in, then read the characteristic value.
+
+To do this we call `NimBLEClient::getService`, which takes as a parameter the UUID of the service and returns
+a pointer an instance to `NimBLERemoteService` or `nullptr` if the service was not found.
+
+Next we will call `NimBLERemoteService::getCharateristic` which takes as a parameter the UUID of the service and returns
+a pointer to an instance of `NimBLERemoteCharacteristic` or `nullptr` if not found.
+
+Finally we will read the characteristic value with `NimBLERemoteCharacteristic::readValue()`.
+
+**Here is what that looks like:**
+```
+NimBLEUUID serviceUuid("ABCD");
+
+for(int i = 0; i < results.getCount(); i++) {
+ NimBLEAdvertisedDevice device = results.getDevice(i);
+
+ if (device.isAdvertisingService(serviceUuid)) {
+ NimBLEClient *pClient = NimBLEDevice::createClient();
+
+ if (pClient->connect(&device)) {
+ NimBLERemoteService *pService = pClient->getService(serviceUuid);
+
+ if (pService != nullptr) {
+ NimBLERemoteCharacteristic *pCharacteristic = pService->getCharacteristic("1234");
+
+ if (pCharacteristic != nullptr) {
+ std::string value = pCharacteristic->readValue();
+ // print or do whatever you need with the value
+ }
+ }
+ } else {
+ // failed to connect
+ }
+ }
+}
+```
+
+
+The last thing we should do is clean up once we are done with the connection.
+Because multiple clients are supported and can be created we should delete them when finished with them to conserve resources.
+This is done by calling `NimBLEDevice::deleteClient`.
+
+**Lets add that now:**
+```
+NimBLEUUID serviceUuid("ABCD");
+
+for(int i = 0; i < results.getCount(); i++) {
+ NimBLEAdvertisedDevice device = results.getDevice(i);
+
+ if (device.isAdvertisingService(serviceUuid)) {
+ NimBLEClient *pClient = NimBLEDevice::createClient();
+
+ if (pClient->connect(&device)) {
+ NimBLERemoteService *pService = pClient->getService(serviceUuid);
+
+ if (pService != nullptr) {
+ NimBLERemoteCharacteristic *pCharacteristic = pService->getCharacteristic("1234");
+
+ if (pCharacteristic != nullptr) {
+ std::string value = pCharacteristic->readValue();
+ // print or do whatever you need with the value
+ }
+ }
+ } else {
+ // failed to connect
+ }
+
+ NimBLEDevice::deleteClient(pClient);
+ }
+}
+```
+Note that there is no need to disconnect as that will be done when deleting the client instance.
+
+
+**Here is the full example code:**
+```
+#include "NimBLEDevice.h"
+
+// void setup() in Arduino
+void app_main(void)
+{
+ NimBLEDevice::init("");
+
+ NimBLEScan *pScan = NimBLEDevice::getScan();
+ NimBLEScanResults results = pScan->start(10);
+
+ NimBLEUUID serviceUuid("ABCD");
+
+ for(int i = 0; i < results.getCount(); i++) {
+ NimBLEAdvertisedDevice device = results.getDevice(i);
+
+ if (device.isAdvertisingService(serviceUuid)) {
+ NimBLEClient *pClient = NimBLEDevice::createClient();
+
+ if (pClient->connect(&device)) {
+ NimBLERemoteService *pService = pClient->getService(serviceUuid);
+
+ if (pService != nullptr) {
+ NimBLERemoteCharacteristic *pCharacteristic = pService->getCharacteristic("1234");
+
+ if (pCharacteristic != nullptr) {
+ std::string value = pCharacteristic->readValue();
+ // print or do whatever you need with the value
+ }
+ }
+ } else {
+ // failed to connect
+ }
+
+ NimBLEDevice::deleteClient(pClient);
+ }
+ }
+}
+```
+
+
+For more advanced features and options please see the client examples in the examples folder.
+
+
diff --git a/libesp32/NimBLE-Arduino/examples/NimBLE_Client/NimBLE_Client.ino b/libesp32/NimBLE-Arduino/examples/NimBLE_Client/NimBLE_Client.ino
index 30eeb9fe3..9ef41b91c 100644
--- a/libesp32/NimBLE-Arduino/examples/NimBLE_Client/NimBLE_Client.ino
+++ b/libesp32/NimBLE-Arduino/examples/NimBLE_Client/NimBLE_Client.ino
@@ -107,9 +107,10 @@ class AdvertisedDeviceCallbacks: public NimBLEAdvertisedDeviceCallbacks {
void notifyCB(NimBLERemoteCharacteristic* pRemoteCharacteristic, uint8_t* pData, size_t length, bool isNotify){
std::string str = (isNotify == true) ? "Notification" : "Indication";
str += " from ";
- str += pRemoteCharacteristic->getRemoteService()->getClient()->getPeerAddress().toString();
- str += ": Service = " + pRemoteCharacteristic->getRemoteService()->getUUID().toString();
- str += ", Characteristic = " + pRemoteCharacteristic->getUUID().toString();
+ /** NimBLEAddress and NimBLEUUID have std::string operators */
+ str += std::string(pRemoteCharacteristic->getRemoteService()->getClient()->getPeerAddress());
+ str += ": Service = " + std::string(pRemoteCharacteristic->getRemoteService()->getUUID());
+ str += ", Characteristic = " + std::string(pRemoteCharacteristic->getUUID());
str += ", Value = " + std::string((char*)pData, length);
Serial.println(str.c_str());
}
@@ -228,17 +229,22 @@ bool connectToServer() {
}
}
+ /** registerForNotify() has been deprecated and replaced with subscribe() / unsubscribe().
+ * Subscribe parameter defaults are: notifications=true, notifyCallback=nullptr, response=false.
+ * Unsubscribe parameter defaults are: response=false.
+ */
if(pChr->canNotify()) {
- /** Must send a callback to subscribe, if nullptr it will unsubscribe */
- if(!pChr->registerForNotify(notifyCB)) {
+ //if(!pChr->registerForNotify(notifyCB)) {
+ if(!pChr->subscribe(true, notifyCB)) {
/** Disconnect if subscribe failed */
pClient->disconnect();
return false;
}
}
else if(pChr->canIndicate()) {
- /** Send false as second argument to subscribe to indications instead of notifications */
- if(!pChr->registerForNotify(notifyCB, false)) {
+ /** Send false as first argument to subscribe to indications instead of notifications */
+ //if(!pChr->registerForNotify(notifyCB, false)) {
+ if(!pChr->subscribe(false, notifyCB)) {
/** Disconnect if subscribe failed */
pClient->disconnect();
return false;
@@ -289,17 +295,22 @@ bool connectToServer() {
}
}
+ /** registerForNotify() has been deprecated and replaced with subscribe() / unsubscribe().
+ * Subscribe parameter defaults are: notifications=true, notifyCallback=nullptr, response=false.
+ * Unsubscribe parameter defaults are: response=false.
+ */
if(pChr->canNotify()) {
- /** Must send a callback to subscribe, if nullptr it will unsubscribe */
- if(!pChr->registerForNotify(notifyCB)) {
+ //if(!pChr->registerForNotify(notifyCB)) {
+ if(!pChr->subscribe(true, notifyCB)) {
/** Disconnect if subscribe failed */
pClient->disconnect();
return false;
}
}
else if(pChr->canIndicate()) {
- /** Send false as second argument to subscribe to indications instead of notifications */
- if(!pChr->registerForNotify(notifyCB, false)) {
+ /** Send false as first argument to subscribe to indications instead of notifications */
+ //if(!pChr->registerForNotify(notifyCB, false)) {
+ if(!pChr->subscribe(false, notifyCB)) {
/** Disconnect if subscribe failed */
pClient->disconnect();
return false;
diff --git a/libesp32/NimBLE-Arduino/examples/NimBLE_Server/NimBLE_Server.ino b/libesp32/NimBLE-Arduino/examples/NimBLE_Server/NimBLE_Server.ino
index e0be793d5..f66169ce8 100644
--- a/libesp32/NimBLE-Arduino/examples/NimBLE_Server/NimBLE_Server.ino
+++ b/libesp32/NimBLE-Arduino/examples/NimBLE_Server/NimBLE_Server.ino
@@ -9,8 +9,6 @@
*/
#include
-#include
-#include
static NimBLEServer* pServer;
@@ -102,24 +100,33 @@ class CharacteristicCallbacks: public NimBLECharacteristicCallbacks {
str += NimBLEUtils::returnCodeToString(code);
Serial.println(str);
};
+
+ void onSubscribe(NimBLECharacteristic* pCharacteristic, ble_gap_conn_desc* desc, uint16_t subValue) {
+ String str = "Client ID: ";
+ str += desc->conn_handle;
+ str += " Address: ";
+ str += std::string(NimBLEAddress(desc->peer_ota_addr)).c_str();
+ if(subValue == 0) {
+ str += " Unsubscribed to ";
+ }else if(subValue == 1) {
+ str += " Subscribed to notfications for ";
+ } else if(subValue == 2) {
+ str += " Subscribed to indications for ";
+ } else if(subValue == 3) {
+ str += " Subscribed to notifications and indications for ";
+ }
+ str += std::string(pCharacteristic->getUUID()).c_str();
+
+ Serial.println(str);
+ };
};
/** Handler class for descriptor actions */
class DescriptorCallbacks : public NimBLEDescriptorCallbacks {
void onWrite(NimBLEDescriptor* pDescriptor) {
- if(pDescriptor->getUUID().equals(NimBLEUUID("2902"))) {
- /** Cast to NimBLE2902 to use the class specific functions. **/
- NimBLE2902* p2902 = (NimBLE2902*)pDescriptor;
- if(p2902->getNotifications()) {
- Serial.println("Client Subscribed to notfications");
- } else {
- Serial.println("Client Unubscribed to notfications");
- }
- } else {
- std::string dscVal((char*)pDescriptor->getValue(), pDescriptor->getLength());
- Serial.print("Descriptor witten value:");
- Serial.println(dscVal.c_str());
- }
+ std::string dscVal((char*)pDescriptor->getValue(), pDescriptor->getLength());
+ Serial.print("Descriptor witten value:");
+ Serial.println(dscVal.c_str());
};
void onRead(NimBLEDescriptor* pDescriptor) {
@@ -176,9 +183,9 @@ void setup() {
pBeefCharacteristic->setValue("Burger");
pBeefCharacteristic->setCallbacks(&chrCallbacks);
- /** 2902 and 2904 descriptors are a special case, when createDescriptor is called with
- * either of those uuid's it will create the associated class with the correct properties
- * and sizes. However we must cast the returned reference to the correct type as the method
+ /** 2904 descriptors are a special case, when createDescriptor is called with
+ * 0x2904 a NimBLE2904 class is created with the correct properties and sizes.
+ * However we must cast the returned reference to the correct type as the method
* only returns a pointer to the base NimBLEDescriptor class.
*/
NimBLE2904* pBeef2904 = (NimBLE2904*)pBeefCharacteristic->createDescriptor("2904");
@@ -197,6 +204,10 @@ void setup() {
pFoodCharacteristic->setValue("Fries");
pFoodCharacteristic->setCallbacks(&chrCallbacks);
+ /** Note a 0x2902 descriptor MUST NOT be created as NimBLE will create one automatically
+ * if notification or indication properties are assigned to a characteristic.
+ */
+
/** Custom descriptor: Arguments are UUID, Properties, max length in bytes of the value */
NimBLEDescriptor* pC01Ddsc = pFoodCharacteristic->createDescriptor(
"C01D",
@@ -208,14 +219,6 @@ void setup() {
pC01Ddsc->setValue("Send it back!");
pC01Ddsc->setCallbacks(&dscCallbacks);
- /** Note a 2902 descriptor does NOT need to be created as any chactateristic with
- * notification or indication properties will have one created autmatically.
- * Manually creating it is only useful if you wish to handle callback functions
- * as shown here. Otherwise this can be removed without loss of functionality.
- */
- NimBLE2902* pFood2902 = (NimBLE2902*)pFoodCharacteristic->createDescriptor("2902");
- pFood2902->setCallbacks(&dscCallbacks);
-
/** Start the services when finished creating all Characteristics and Descriptors */
pDeadService->start();
pBaadService->start();
@@ -247,4 +250,4 @@ void loop() {
}
delay(2000);
-}
+}
\ No newline at end of file
diff --git a/libesp32/NimBLE-Arduino/examples/Refactored_original_examples/BLE_notify/BLE_notify.ino b/libesp32/NimBLE-Arduino/examples/Refactored_original_examples/BLE_notify/BLE_notify.ino
index f57c52e2b..83f129b83 100644
--- a/libesp32/NimBLE-Arduino/examples/Refactored_original_examples/BLE_notify/BLE_notify.ino
+++ b/libesp32/NimBLE-Arduino/examples/Refactored_original_examples/BLE_notify/BLE_notify.ino
@@ -102,16 +102,13 @@ void setup() {
// https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.descriptor.gatt.client_characteristic_configuration.xml
// Create a BLE Descriptor
- /*********** New createDescriptor method ************
- NOTE: There is no need to create the 2902 descriptor
- as it will be created automatically if notifications
+ /***************************************************
+ NOTE: DO NOT create a 2902 descriptor.
+ it will be created automatically if notifications
or indications are enabled on a characteristic.
pCharacteristic->addDescriptor(new BLE2902());
****************************************************/
- /** Add properties the same way as characteristics now **/
-
- pCharacteristic->createDescriptor("2902" /** , NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::WRITE **/);
// Start the service
pService->start();
@@ -119,7 +116,9 @@ void setup() {
BLEAdvertising *pAdvertising = BLEDevice::getAdvertising();
pAdvertising->addServiceUUID(SERVICE_UUID);
pAdvertising->setScanResponse(false);
- pAdvertising->setMinPreferred(0x0); // set value to 0x00 to not advertise this parameter
+ /**This method is removed as it was no longer useful and consumed advertising space
+ * pAdvertising->setMinPreferred(0x0); // set value to 0x00 to not advertise this parameter
+ */
BLEDevice::startAdvertising();
Serial.println("Waiting a client connection to notify...");
}
diff --git a/libesp32/NimBLE-Arduino/examples/Refactored_original_examples/BLE_server/BLE_server.ino b/libesp32/NimBLE-Arduino/examples/Refactored_original_examples/BLE_server/BLE_server.ino
index 82aa70aaa..652d77685 100644
--- a/libesp32/NimBLE-Arduino/examples/Refactored_original_examples/BLE_server/BLE_server.ino
+++ b/libesp32/NimBLE-Arduino/examples/Refactored_original_examples/BLE_server/BLE_server.ino
@@ -44,8 +44,10 @@ void setup() {
BLEAdvertising *pAdvertising = BLEDevice::getAdvertising();
pAdvertising->addServiceUUID(SERVICE_UUID);
pAdvertising->setScanResponse(true);
- pAdvertising->setMinPreferred(0x06); // functions that help with iPhone connections issue
- pAdvertising->setMinPreferred(0x12);
+ /**These methods are removed as they are no longer useful and consumed advertising space
+ * pAdvertising->setMinPreferred(0x06); // functions that help with iPhone connections issue
+ * pAdvertising->setMinPreferred(0x12);
+ */
BLEDevice::startAdvertising();
Serial.println("Characteristic defined! Now you can read it in your phone!");
}
diff --git a/libesp32/NimBLE-Arduino/examples/Refactored_original_examples/BLE_server_multiconnect/BLE_server_multiconnect.ino b/libesp32/NimBLE-Arduino/examples/Refactored_original_examples/BLE_server_multiconnect/BLE_server_multiconnect.ino
index 025266650..2ec38c481 100644
--- a/libesp32/NimBLE-Arduino/examples/Refactored_original_examples/BLE_server_multiconnect/BLE_server_multiconnect.ino
+++ b/libesp32/NimBLE-Arduino/examples/Refactored_original_examples/BLE_server_multiconnect/BLE_server_multiconnect.ino
@@ -105,16 +105,13 @@ void setup() {
// https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.descriptor.gatt.client_characteristic_configuration.xml
// Create a BLE Descriptor
- /*********** New createDescriptor method ************
- NOTE: There is no need to create the 2902 descriptor
- as it will be created automatically if notifications
+ /***************************************************
+ NOTE: DO NOT create a 2902 descriptor
+ it will be created automatically if notifications
or indications are enabled on a characteristic.
pCharacteristic->addDescriptor(new BLE2902());
****************************************************/
- /** Add properties the same way as characteristics now **/
-
- pCharacteristic->createDescriptor("2902" /** , NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::WRITE **/);
// Start the service
pService->start();
@@ -123,7 +120,9 @@ void setup() {
BLEAdvertising *pAdvertising = BLEDevice::getAdvertising();
pAdvertising->addServiceUUID(SERVICE_UUID);
pAdvertising->setScanResponse(false);
- pAdvertising->setMinPreferred(0x0); // set value to 0x00 to not advertise this parameter
+ /**This method is removed it was no longer useful and consumed advertising space
+ * pAdvertising->setMinPreferred(0x0); // set value to 0x00 to not advertise this parameter
+ */
BLEDevice::startAdvertising();
Serial.println("Waiting a client connection to notify...");
}
diff --git a/libesp32/NimBLE-Arduino/examples/Refactored_original_examples/BLE_uart/BLE_uart.ino b/libesp32/NimBLE-Arduino/examples/Refactored_original_examples/BLE_uart/BLE_uart.ino
index b83470cf6..0a31ef289 100644
--- a/libesp32/NimBLE-Arduino/examples/Refactored_original_examples/BLE_uart/BLE_uart.ino
+++ b/libesp32/NimBLE-Arduino/examples/Refactored_original_examples/BLE_uart/BLE_uart.ino
@@ -112,15 +112,13 @@ void setup() {
NIMBLE_PROPERTY::NOTIFY
);
- /******* New createDescriptor method ********
- NOTE: There is no need to create the 2902 descriptor
- as it will be created automatically if notifications or
- indications are enabled on a characteristic.
+ /***************************************************
+ NOTE: DO NOT create a 2902 descriptor
+ it will be created automatically if notifications
+ or indications are enabled on a characteristic.
- pCharacteristic->addDescriptor(new BLE2902());
- ********************************************/
- /** Add properties the same way as characteristics now **/
- pTxCharacteristic->createDescriptor("2902" /** , NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::WRITE **/);
+ pCharacteristic->addDescriptor(new BLE2902());
+ ****************************************************/
BLECharacteristic * pRxCharacteristic = pService->createCharacteristic(
CHARACTERISTIC_UUID_RX,
diff --git a/libesp32/NimBLE-Arduino/library.properties b/libesp32/NimBLE-Arduino/library.properties
index 236fcd606..156c098c0 100644
--- a/libesp32/NimBLE-Arduino/library.properties
+++ b/libesp32/NimBLE-Arduino/library.properties
@@ -1,9 +1,9 @@
name=NimBLE-Arduino
-version=0.9.0
-author=H2zero
+version=1.0.2
+author=h2zero
maintainer=h2zero
-sentence=NimBLE library for Arduino
-paragraph=A lighter-weight alternative to the bluedroid library for esp32.
+sentence=Bluetooth low energy (BLE) library for arduino-esp32 based on NimBLE.
+paragraph=This is a more updated and lower resource alternative to the original bluedroid BLE library for esp32. Uses 50% less flash space and approximately 100KB less ram with the same functionality. Nearly 100% compatible with existing application code, migration guide included.
url=https://github.com/h2zero/NimBLE-Arduino
category=Communication
architectures=esp32
diff --git a/libesp32/NimBLE-Arduino/src/FreeRTOS.cpp b/libesp32/NimBLE-Arduino/src/FreeRTOS.cpp
index f6bbe171b..ff1061c10 100644
--- a/libesp32/NimBLE-Arduino/src/FreeRTOS.cpp
+++ b/libesp32/NimBLE-Arduino/src/FreeRTOS.cpp
@@ -114,6 +114,10 @@ bool FreeRTOS::Semaphore::timedWait(std::string owner, uint32_t timeoutMs) {
} // wait
+/**
+ * @brief Construct a semaphore, the semaphore is given when created.
+ * @param [in] name A name string to provide debugging support.
+ */
FreeRTOS::Semaphore::Semaphore(std::string name) {
m_usePthreads = false; // Are we using pThreads or FreeRTOS?
if (m_usePthreads) {
@@ -140,8 +144,7 @@ FreeRTOS::Semaphore::~Semaphore() {
/**
- * @brief Give a semaphore.
- * The Semaphore is given.
+ * @brief Give the semaphore.
*/
void FreeRTOS::Semaphore::give() {
NIMBLE_LOGD(LOG_TAG, "Semaphore giving: %s", toString().c_str());
diff --git a/libesp32/NimBLE-Arduino/src/FreeRTOS.h b/libesp32/NimBLE-Arduino/src/FreeRTOS.h
index a6737b757..2f3d9386c 100644
--- a/libesp32/NimBLE-Arduino/src/FreeRTOS.h
+++ b/libesp32/NimBLE-Arduino/src/FreeRTOS.h
@@ -29,6 +29,9 @@ public:
static uint32_t getTimeSinceStart();
+/**
+ * @brief A binary semaphore class that operates like a mutex, it is already given when constructed.
+ */
class Semaphore {
public:
Semaphore(std::string owner = "");
@@ -42,6 +45,10 @@ public:
std::string toString();
bool timedWait(std::string owner = "", uint32_t timeoutMs = portMAX_DELAY);
uint32_t wait(std::string owner = "");
+ /**
+ * @brief Get the value of the semaphore.
+ * @return The value stored if the semaphore was given with give(value);
+ */
uint32_t value(){ return m_value; };
private:
@@ -57,7 +64,7 @@ public:
/**
- * @brief Ringbuffer.
+ * @brief A wrapper class for a freeRTOS ringbuffer.
*/
class Ringbuffer {
public:
diff --git a/libesp32/NimBLE-Arduino/src/NimBLE2902.cpp b/libesp32/NimBLE-Arduino/src/NimBLE2902.cpp
deleted file mode 100644
index 04a07b6f9..000000000
--- a/libesp32/NimBLE-Arduino/src/NimBLE2902.cpp
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
- * NimBLE2902.cpp
- *
- * Created: on March 10, 2020
- * Author H2zero
- *
- * Originally:
- *
- * BLE2902.cpp
- *
- * Created on: Jun 25, 2017
- * Author: kolban
- */
-
-
-/*
- * See also:
- * https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.descriptor.gatt.client_characteristic_configuration.xml
- */
-#include "sdkconfig.h"
-#if defined(CONFIG_BT_ENABLED)
-
-#include "nimconfig.h"
-#if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
-
-#include "NimBLE2902.h"
-
-NimBLE2902::NimBLE2902(NimBLECharacteristic* pCharacterisitic)
-: NimBLEDescriptor(NimBLEUUID((uint16_t) 0x2902),
- BLE_GATT_CHR_F_READ |
- BLE_GATT_CHR_F_WRITE,
- 2, pCharacterisitic)
-{
- uint8_t data[2] = { 0, 0 };
- setValue(data, 2);
-} // NimBLE2902
-
-
-/**
- * @brief Get the notifications value.
- * @return The notifications value. True if notifications are enabled and false if not.
- */
-bool NimBLE2902::getNotifications() {
- return (getValue()[0] & (1 << 0)) != 0;
-} // getNotifications
-
-
-/**
- * @brief Get the indications value.
- * @return The indications value. True if indications are enabled and false if not.
- */
-bool NimBLE2902::getIndications() {
- return (getValue()[0] & (1 << 1)) != 0;
-} // getIndications
-
-
-/**
- * @brief Set the indications flag.
- * @param [in] flag The indications flag.
- */
-void NimBLE2902::setIndications(bool flag) {
- uint8_t *pValue = getValue();
- if (flag) pValue[0] |= 1 << 1;
- else pValue[0] &= ~(1 << 1);
-} // setIndications
-
-
-/**
- * @brief Set the notifications flag.
- * @param [in] flag The notifications flag.
- */
-void NimBLE2902::setNotifications(bool flag) {
- uint8_t *pValue = getValue();
- if (flag) pValue[0] |= 1 << 0;
- else pValue[0] &= ~(1 << 0);
-} // setNotifications
-
-#endif // #if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
-#endif
diff --git a/libesp32/NimBLE-Arduino/src/NimBLE2902.h b/libesp32/NimBLE-Arduino/src/NimBLE2902.h
deleted file mode 100644
index 2d84b73dc..000000000
--- a/libesp32/NimBLE-Arduino/src/NimBLE2902.h
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * NimBLE2902.h
- *
- * Created: on March 10, 2020
- * Author H2zero
- *
- * Originally:
- *
- * BLE2902.h
- *
- * Created on: Jun 25, 2017
- * Author: kolban
- */
-
-#ifndef MAIN_NIMBLE2902_H_
-#define MAIN_NIMBLE2902_H_
-#include "sdkconfig.h"
-#if defined(CONFIG_BT_ENABLED)
-
-#include "nimconfig.h"
-#if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
-
-#include "NimBLEDescriptor.h"
-
-#include
-
-#define NIMBLE_DESC_FLAG_NOTIFY 0x0001
-#define NIMBLE_DESC_FLAG_INDICATE 0x0002
-
-typedef struct {
- uint16_t conn_id;
- uint16_t sub_val;
-} chr_sub_status_t;
-
-
-/**
- * @brief Descriptor for Client Characteristic Configuration.
- *
- * This is a convenience descriptor for the Client Characteristic Configuration which has a UUID of 0x2902.
- *
- * See also:
- * https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.descriptor.gatt.client_characteristic_configuration.xml
- */
-class NimBLE2902: public NimBLEDescriptor {
-public:
- bool getNotifications();
- bool getIndications();
- void setNotifications(bool flag);
- void setIndications(bool flag);
-private:
- NimBLE2902(NimBLECharacteristic* pCharacterisitic);
- friend class NimBLECharacteristic;
- std::vector m_subscribedVec;
-
-}; // NimBLE2902
-
-#endif // #if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
-#endif /* CONFIG_BT_ENABLED */
-#endif /* MAIN_NIMBLE2902_H_ */
diff --git a/libesp32/NimBLE-Arduino/src/NimBLEAddress.cpp b/libesp32/NimBLE-Arduino/src/NimBLEAddress.cpp
index 8c2c68f79..e1d3e548b 100644
--- a/libesp32/NimBLE-Arduino/src/NimBLEAddress.cpp
+++ b/libesp32/NimBLE-Arduino/src/NimBLEAddress.cpp
@@ -23,17 +23,26 @@
static const char* LOG_TAG = "NimBLEAddress";
/*************************************************
-NOTE: NimBLE addresses are in INVERSE ORDER!
-We will accomodate that fact in these methods.
+ * NOTE: NimBLE address bytes are in INVERSE ORDER!
+ * We will accomodate that fact in these methods.
*************************************************/
/**
- * @brief Create an address from the native ESP32 representation.
- * @param [in] address The native representation.
+ * @brief Create an address from the native NimBLE representation.
+ * @param [in] address The native NimBLE address.
*/
NimBLEAddress::NimBLEAddress(ble_addr_t address) {
memcpy(m_address, address.val, 6);
-} // BLEAddress
+ m_addrType = address.type;
+} // NimBLEAddress
+
+
+/**
+ * @brief Create a blank address, i.e. 00:00:00:00:00:00, type 0.
+ */
+NimBLEAddress::NimBLEAddress() {
+ NimBLEAddress("");
+} // NimBLEAddress
/**
@@ -45,9 +54,12 @@ NimBLEAddress::NimBLEAddress(ble_addr_t address) {
* ```
* which is 17 characters in length.
*
- * @param [in] stringAddress The hex representation of the address.
+ * @param [in] stringAddress The hex string representation of the address.
+ * @param [in] type The type of the address.
*/
-NimBLEAddress::NimBLEAddress(const std::string &stringAddress) {
+NimBLEAddress::NimBLEAddress(const std::string &stringAddress, uint8_t type) {
+ m_addrType = type;
+
if (stringAddress.length() == 0) {
memset(m_address, 0, 6);
return;
@@ -72,24 +84,29 @@ NimBLEAddress::NimBLEAddress(const std::string &stringAddress) {
for(size_t index = 0; index < sizeof m_address; index++) {
m_address[index] = data[index];
}
-} // BLEAddress
-
-
-/**
- * @brief Constructor for compatibility with bluedroid esp library.
- * @param [in] uint8_t[6] or esp_bd_addr_t struct containing the address.
- */
-NimBLEAddress::NimBLEAddress(uint8_t address[6]) {
- std::reverse_copy(address, address + sizeof m_address, m_address);
} // NimBLEAddress
/**
- * @brief Constructor for address using a hex value. Use the same byte order, so use 0xa4c1385def16 for "a4:c1:38:5d:ef:16"
- * @param [in] uint64_t containing the address.
+ * @brief Constructor for compatibility with bluedroid esp library using native ESP representation.
+ * @param [in] address A uint8_t[6] or esp_bd_addr_t containing the address.
+ * @param [in] type The type of the address.
*/
-NimBLEAddress::NimBLEAddress(const uint64_t &address) {
+NimBLEAddress::NimBLEAddress(uint8_t address[6], uint8_t type) {
+ std::reverse_copy(address, address + sizeof m_address, m_address);
+ m_addrType = type;
+} // NimBLEAddress
+
+
+/**
+ * @brief Constructor for address using a hex value.\n
+ * Use the same byte order, so use 0xa4c1385def16 for "a4:c1:38:5d:ef:16"
+ * @param [in] address uint64_t containing the address.
+ * @param [in] type The type of the address.
+ */
+NimBLEAddress::NimBLEAddress(const uint64_t &address, uint8_t type) {
memcpy(m_address, &address, sizeof m_address);
+ m_addrType = type;
} // NimBLEAddress
@@ -104,14 +121,23 @@ bool NimBLEAddress::equals(const NimBLEAddress &otherAddress) const {
/**
- * @brief Return the native representation of the address.
- * @return The native representation of the address.
+ * @brief Get the native representation of the address.
+ * @return a pointer to the uint8_t[6] array of the address.
*/
const uint8_t *NimBLEAddress::getNative() const {
return m_address;
} // getNative
+/**
+ * @brief Get the address type.
+ * @return The address type.
+ */
+uint8_t NimBLEAddress::getType() const {
+ return m_addrType;
+} // getType
+
+
/**
* @brief Convert a BLE address to a string.
*
@@ -122,30 +148,50 @@ const uint8_t *NimBLEAddress::getNative() const {
* ```
*
* @return The string representation of the address.
+ * @deprecated Use std::string() operator instead.
*/
std::string NimBLEAddress::toString() const {
return std::string(*this);
} // toString
+/**
+ * @brief Convienience operator to check if this address is equal to another.
+ */
bool NimBLEAddress::operator ==(const NimBLEAddress & rhs) const {
return memcmp(rhs.m_address, m_address, sizeof m_address) == 0;
-}
+} // operator ==
+
+/**
+ * @brief Convienience operator to check if this address is not equal to another.
+ */
bool NimBLEAddress::operator !=(const NimBLEAddress & rhs) const {
return !this->operator==(rhs);
-}
+} // operator !=
+
+/**
+ * @brief Convienience operator to convert this address to string representation.
+ * @details This allows passing NimBLEAddress to functions
+ * that accept std::string and/or or it's methods as a parameter.
+ */
NimBLEAddress::operator std::string() const {
char buffer[18];
- sprintf(buffer, "%02x:%02x:%02x:%02x:%02x:%02x", m_address[5], m_address[4], m_address[3], m_address[2], m_address[1], m_address[0]);
+ snprintf(buffer, sizeof(buffer), "%02x:%02x:%02x:%02x:%02x:%02x",
+ m_address[5], m_address[4], m_address[3],
+ m_address[2], m_address[1], m_address[0]);
return std::string(buffer);
-}
+} // operator std::string
+
+/**
+ * @brief Convienience operator to convert the native address representation to uint_64.
+ */
NimBLEAddress::operator uint64_t() const {
uint64_t address = 0;
memcpy(&address, m_address, sizeof m_address);
return address;
-}
+} // operator uint64_t
#endif
diff --git a/libesp32/NimBLE-Arduino/src/NimBLEAddress.h b/libesp32/NimBLE-Arduino/src/NimBLEAddress.h
index 778ff8644..50f9231fe 100644
--- a/libesp32/NimBLE-Arduino/src/NimBLEAddress.h
+++ b/libesp32/NimBLE-Arduino/src/NimBLEAddress.h
@@ -33,13 +33,15 @@
*/
class NimBLEAddress {
public:
+ NimBLEAddress();
NimBLEAddress(ble_addr_t address);
- NimBLEAddress(uint8_t address[6]);
- NimBLEAddress(const std::string &stringAddress);
- NimBLEAddress(const uint64_t &address);
- bool equals(const NimBLEAddress &otherAddress) const;
- const uint8_t* getNative() const;
- std::string toString() const;
+ NimBLEAddress(uint8_t address[6], uint8_t type = BLE_ADDR_PUBLIC);
+ NimBLEAddress(const std::string &stringAddress, uint8_t type = BLE_ADDR_PUBLIC);
+ NimBLEAddress(const uint64_t &address, uint8_t type = BLE_ADDR_PUBLIC);
+ bool equals(const NimBLEAddress &otherAddress) const;
+ const uint8_t* getNative() const;
+ std::string toString() const;
+ uint8_t getType() const;
bool operator ==(const NimBLEAddress & rhs) const;
bool operator !=(const NimBLEAddress & rhs) const;
@@ -48,6 +50,7 @@ public:
private:
uint8_t m_address[6];
+ uint8_t m_addrType;
};
#endif /* CONFIG_BT_ENABLED */
diff --git a/libesp32/NimBLE-Arduino/src/NimBLEAdvertisedDevice.cpp b/libesp32/NimBLE-Arduino/src/NimBLEAdvertisedDevice.cpp
index c53bb688b..992b1f377 100644
--- a/libesp32/NimBLE-Arduino/src/NimBLEAdvertisedDevice.cpp
+++ b/libesp32/NimBLE-Arduino/src/NimBLEAdvertisedDevice.cpp
@@ -17,6 +17,7 @@
#include "nimconfig.h"
#if defined(CONFIG_BT_NIMBLE_ROLE_OBSERVER)
+#include "NimBLEDevice.h"
#include "NimBLEAdvertisedDevice.h"
#include "NimBLEUtils.h"
#include "NimBLELog.h"
@@ -30,13 +31,10 @@ static const char* LOG_TAG = "NimBLEAdvertisedDevice";
NimBLEAdvertisedDevice::NimBLEAdvertisedDevice() {
m_advType = 0;
m_appearance = 0;
- m_deviceType = 0;
m_manufacturerData = "";
m_name = "";
m_rssi = -9999;
- m_serviceData = "";
m_txPower = 0;
- m_pScan = nullptr;
m_payloadLength = 0;
m_payload = nullptr;
@@ -53,11 +51,7 @@ NimBLEAdvertisedDevice::NimBLEAdvertisedDevice() {
/**
- * @brief Get the address.
- *
- * Every %BLE device exposes an address that is used to identify it and subsequently connect to it.
- * Call this function to obtain the address of the advertised device.
- *
+ * @brief Get the address of the advertising device.
* @return The address of the advertised device.
*/
NimBLEAddress NimBLEAdvertisedDevice::getAddress() {
@@ -66,13 +60,17 @@ NimBLEAddress NimBLEAdvertisedDevice::getAddress() {
/**
- * @brief Get the advertised type.
- *
- * @return The advertised type of the advertised device.
+ * @brief Get the advertisement type.
+ * @return The advertising type the device is reporting:
+ * * BLE_HCI_ADV_TYPE_ADV_IND (0) - indirect advertising
+ * * BLE_HCI_ADV_TYPE_ADV_DIRECT_IND_HD (1) - direct advertisng - high duty cycle
+ * * BLE_HCI_ADV_TYPE_ADV_SCAN_IND (2) - indirect scan response
+ * * BLE_HCI_ADV_TYPE_ADV_NONCONN_IND (3) - indirect advertisng - not connectable
+ * * BLE_HCI_ADV_TYPE_ADV_DIRECT_IND_LD (4) - direct advertising - low duty cycle
*/
uint8_t NimBLEAdvertisedDevice::getAdvType() {
return m_advType;
-} // getAddress
+} // getAdvType
/**
@@ -98,7 +96,7 @@ std::string NimBLEAdvertisedDevice::getManufacturerData() {
/**
- * @brief Get the name.
+ * @brief Get the advertised name.
* @return The name of the advertised device.
*/
std::string NimBLEAdvertisedDevice::getName() {
@@ -116,54 +114,100 @@ int NimBLEAdvertisedDevice::getRSSI() {
/**
- * @brief Get the scan object that created this advertisement.
+ * @brief Get the scan object that created this advertised device.
* @return The scan object.
*/
NimBLEScan* NimBLEAdvertisedDevice::getScan() {
- return m_pScan;
+ return NimBLEDevice::getScan();
} // getScan
/**
* @brief Get the service data.
- * @return The ServiceData of the advertised device.
+ * @param [in] index The vector index of the service data requested.
+ * @return The advertised service data or empty string if no data.
*/
-std::string NimBLEAdvertisedDevice::getServiceData() {
- return m_serviceData;
+std::string NimBLEAdvertisedDevice::getServiceData(uint8_t index) {
+ if(index > m_serviceDataVec.size()) {
+ NIMBLE_LOGW(LOG_TAG, "getServiceData: index out of range");
+ return "";
+ }
+ return m_serviceDataVec[index].second;
} //getServiceData
/**
- * @brief Get the service data UUID.
- * @return The service data UUID.
+ * @brief Get the service data.
+ * @param [in] uuid The uuid of the service data requested.
+ * @return The advertised service data or empty string if no data.
*/
+std::string NimBLEAdvertisedDevice::getServiceData(const NimBLEUUID &uuid) const {
+ for(auto &it : m_serviceDataVec) {
+ if(it.first == uuid) {
+ return it.second;
+ }
+ }
+ NIMBLE_LOGW(LOG_TAG, "getServiceData: uuid not found");
+ return "";
+} //getServiceData
-NimBLEUUID NimBLEAdvertisedDevice::getServiceDataUUID() {
- return m_serviceDataUUID;
+
+/**
+ * @brief Get the advertised service UUID.
+ * @param [in] index The vector index of the service data UUID requested.
+ * @return The advertised service UUID or an empty UUID if not found.
+ */
+NimBLEUUID NimBLEAdvertisedDevice::getServiceDataUUID(uint8_t index) {
+ if(!haveServiceData() || index > m_serviceDataVec.size()) {
+ NIMBLE_LOGW(LOG_TAG, "getServiceDataUUID: index out of range");
+ return NimBLEUUID("");
+ }
+ return m_serviceDataVec[index].first;
} // getServiceDataUUID
/**
- * @brief Get the Service UUID.
- * @return The Service UUID of the advertised device.
+ * @brief Get the count of advertised service data UUIDS
+ * @return The number of service data UUIDS in the vector.
*/
+size_t NimBLEAdvertisedDevice::getServiceDataCount() {
+ return m_serviceDataVec.size();
+} // getServiceDataCount
-NimBLEUUID NimBLEAdvertisedDevice::getServiceUUID() { //TODO Remove it eventually, is no longer useful
- return m_serviceUUIDs[0];
+
+/**
+ * @brief Get the Service UUID.
+ * @param [in] index The vector index of the service UUID requested.
+ * @return The Service UUID of the advertised service, or an empty UUID if not found.
+ */
+NimBLEUUID NimBLEAdvertisedDevice::getServiceUUID(uint8_t index) {
+ if(!haveServiceUUID() || index > m_serviceUUIDs.size()) {
+ NIMBLE_LOGW(LOG_TAG, "getServiceUUID: index out of range");
+ return NimBLEUUID("");
+ }
+ return m_serviceUUIDs[index];
} // getServiceUUID
/**
- * @brief Check advertised serviced for existence required UUID
+ * @brief Get the number of services advertised
+ * @return The count of services in the advertising packet.
+ */
+size_t NimBLEAdvertisedDevice::getServiceUUIDCount() {
+ return m_serviceUUIDs.size();
+} // getServiceUUIDCount
+
+
+/**
+ * @brief Check advertised services for existance of the required UUID
* @return Return true if service is advertised
*/
-bool NimBLEAdvertisedDevice::isAdvertisingService(const NimBLEUUID &uuid){
+bool NimBLEAdvertisedDevice::isAdvertisingService(const NimBLEUUID &uuid) const {
for (int i = 0; i < m_serviceUUIDs.size(); i++) {
- NIMBLE_LOGI(LOG_TAG, "Comparing UUIDS: %s %s", m_serviceUUIDs[i].toString().c_str(), uuid.toString().c_str());
if (m_serviceUUIDs[i].equals(uuid)) return true;
}
return false;
-}
+} // isAdvertisingService
/**
@@ -250,121 +294,126 @@ bool NimBLEAdvertisedDevice::haveTXPower() {
*
* https://www.bluetooth.com/specifications/assigned-numbers/generic-access-profile
*/
- void NimBLEAdvertisedDevice::parseAdvertisement(ble_hs_adv_fields *fields) {
- //char s[BLE_HS_ADV_MAX_SZ];
- uint8_t *u8p;
- uint8_t length;
- int i;
+ void NimBLEAdvertisedDevice::parseAdvertisement(uint8_t* payload, uint8_t length) {
+ struct ble_hs_adv_fields fields;
+ int rc = ble_hs_adv_parse_fields(&fields, payload, length);
+ if (rc != 0) {
+ NIMBLE_LOGE(LOG_TAG, "Gap Event Parse ERROR.");
+ return;
+ }
- if (fields->uuids16 != NULL) {
- for (i = 0; i < fields->num_uuids16; i++) {
- setServiceUUID(NimBLEUUID(fields->uuids16[i].value));
+ m_payload = payload;
+ m_payloadLength = length;
+
+#if CONFIG_LOG_DEFAULT_LEVEL > 3 || (ARDUINO_ARCH_ESP32 && CORE_DEBUG_LEVEL >= 4)
+ char* pHex = NimBLEUtils::buildHexData(nullptr, m_payload, m_payloadLength);
+ NIMBLE_LOGD(LOG_TAG,"payload: %s", pHex);
+ free(pHex);
+#endif
+
+ if (fields.uuids16 != NULL) {
+ for (int i = 0; i < fields.num_uuids16; i++) {
+ setServiceUUID(NimBLEUUID(fields.uuids16[i].value));
}
}
- if (fields->uuids32 != NULL) {
- for (i = 0; i < fields->num_uuids32; i++) {
- setServiceUUID(NimBLEUUID(fields->uuids32[i].value));
+ if (fields.uuids32 != NULL) {
+ for (int i = 0; i < fields.num_uuids32; i++) {
+ setServiceUUID(NimBLEUUID(fields.uuids32[i].value));
}
}
- if (fields->uuids128 != NULL) {
- for (i = 0; i < fields->num_uuids128; i++) {
- setServiceUUID(NimBLEUUID(&fields->uuids128[i]));
+ if (fields.uuids128 != NULL) {
+ for (int i = 0; i < fields.num_uuids128; i++) {
+ setServiceUUID(NimBLEUUID(&fields.uuids128[i]));
}
}
- if (fields->name != NULL) {
- setName(std::string(reinterpret_cast(fields->name), fields->name_len));
+ if (fields.name != NULL) {
+ setName(std::string(reinterpret_cast(fields.name), fields.name_len));
}
- if (fields->tx_pwr_lvl_is_present) {
- setTXPower(fields->tx_pwr_lvl);
+ if (fields.tx_pwr_lvl_is_present) {
+ setTXPower(fields.tx_pwr_lvl);
}
- if (fields->svc_data_uuid16 != NULL) {
+ if (fields.svc_data_uuid16 != NULL ||
+ fields.svc_data_uuid32 != NULL ||
+ fields.svc_data_uuid128 != NULL)
+ {
+ ble_hs_adv_field *field;
+ uint8_t *data = payload;
+ while(length > 1) {
+ field = (ble_hs_adv_field*)data;
- u8p = fields->svc_data_uuid16;
- length = fields->svc_data_uuid16_len;
-
- if (length < 2) {
- NIMBLE_LOGE(LOG_TAG,"Length too small for ESP_BLE_AD_TYPE_SERVICE_DATA");
- }
- else{
- uint16_t uuid = *(uint16_t*)u8p;
- setServiceDataUUID(NimBLEUUID(uuid));
- if (length > 2) {
- setServiceData(std::string(reinterpret_cast(u8p + 2), length - 2));
+ if(field->length > length) {
+ break;
}
+
+ if(field->type == BLE_HS_ADV_TYPE_SVC_DATA_UUID16) {
+ if(field->length > 2) {
+ uint16_t uuid;
+ memcpy(&uuid, field->value, 2);
+ setServiceData(NimBLEUUID(uuid), std::string(reinterpret_cast(field->value + 2), field->length - 3));
+ }
+ }
+
+ if(field->type == BLE_HS_ADV_TYPE_SVC_DATA_UUID32) {
+ if(field->length > 4) {
+ uint32_t uuid;
+ memcpy(&uuid, field->value, 4);
+ setServiceData(NimBLEUUID(uuid), std::string(reinterpret_cast(field->value + 4), field->length - 5));
+ }
+ }
+
+ if(field->type == BLE_HS_ADV_TYPE_SVC_DATA_UUID128) {
+ if(field->length > 16) {
+ NimBLEUUID uuid(field->value, (size_t)16, false);
+ setServiceData(uuid, std::string(reinterpret_cast(field->value + 16), field->length - 17));
+ }
+ }
+
+ length -= 1 + field->length;
+ data += 1 + field->length;
}
}
- if (fields->svc_data_uuid32 != NULL) {
-
- u8p = fields->svc_data_uuid16;
- length = fields->svc_data_uuid16_len;
-
- if (length < 4) {
- NIMBLE_LOGE(LOG_TAG,"Length too small for ESP_BLE_AD_TYPE_32SERVICE_DATA");
- }
-
- uint32_t uuid = *(uint32_t*) u8p;
- setServiceDataUUID(NimBLEUUID(uuid));
- if (length > 4) {
- setServiceData(std::string(reinterpret_cast(u8p + 4), length - 4));
- }
+ if (fields.appearance_is_present) {
+ setAppearance(fields.appearance);
}
- if (fields->svc_data_uuid128 != NULL) {
-
- u8p = fields->svc_data_uuid16;
- length = fields->svc_data_uuid16_len;
-
- if (length < 16) {
- NIMBLE_LOGE(LOG_TAG,"Length too small for ESP_BLE_AD_TYPE_128SERVICE_DATA");
- }
-
- setServiceDataUUID(NimBLEUUID(u8p, (size_t)16, false));
- if (length > 16) {
- setServiceData(std::string(reinterpret_cast(u8p + 16), length - 16));
- }
+ if (fields.mfg_data != NULL) {
+ setManufacturerData(std::string(reinterpret_cast(fields.mfg_data), fields.mfg_data_len));
}
- if (fields->appearance_is_present) {
- NIMBLE_LOGD(LOG_TAG, " appearance=0x%04x", fields->appearance);
- setAppearance(fields->appearance);
- }
-
-/**** TODO: create storage and fucntions for these parameters
- if (fields->public_tgt_addr != NULL) {
+/* TODO: create storage and fucntions for these parameters
+ if (fields.public_tgt_addr != NULL) {
NIMBLE_LOGD(LOG_TAG, " public_tgt_addr=");
- u8p = fields->public_tgt_addr;
- for (i = 0; i < fields->num_public_tgt_addrs; i++) {
+ u8p = fields.public_tgt_addr;
+ for (i = 0; i < fields.num_public_tgt_addrs; i++) {
NIMBLE_LOGD(LOG_TAG, "public_tgt_addr=%s ", addr_str(u8p));
u8p += BLE_HS_ADV_PUBLIC_TGT_ADDR_ENTRY_LEN;
}
NIMBLE_LOGD(LOG_TAG, "\n");
}
- if (fields->slave_itvl_range != NULL) {
+ if (fields.slave_itvl_range != NULL) {
NIMBLE_LOGD(LOG_TAG, " slave_itvl_range=");
- print_bytes(fields->slave_itvl_range, BLE_HS_ADV_SLAVE_ITVL_RANGE_LEN);
+ print_bytes(fields.slave_itvl_range, BLE_HS_ADV_SLAVE_ITVL_RANGE_LEN);
NIMBLE_LOGD(LOG_TAG, "\n");
}
- if (fields->adv_itvl_is_present) {
- NIMBLE_LOGD(LOG_TAG, " adv_itvl=0x%04x\n", fields->adv_itvl);
+ if (fields.adv_itvl_is_present) {
+ NIMBLE_LOGD(LOG_TAG, " adv_itvl=0x%04x\n", fields.adv_itvl);
}
- if (fields->uri != NULL) {
+ if (fields.uri != NULL) {
NIMBLE_LOGD(LOG_TAG, " uri=");
- print_bytes(fields->uri, fields->uri_len);
+ print_bytes(fields.uri, fields.uri_len);
NIMBLE_LOGD(LOG_TAG, "\n");
}
*/
- if (fields->mfg_data != NULL) {
- setManufacturerData(std::string(reinterpret_cast(fields->mfg_data), fields->mfg_data_len));
- }
+
} //parseAdvertisement
@@ -393,7 +442,6 @@ void NimBLEAdvertisedDevice::setAdvType(uint8_t advType) {
void NimBLEAdvertisedDevice::setAppearance(uint16_t appearance) {
m_appearance = appearance;
m_haveAppearance = true;
- NIMBLE_LOGD(LOG_TAG,"- appearance: %d", m_appearance);
} // setAppearance
@@ -404,10 +452,6 @@ void NimBLEAdvertisedDevice::setAppearance(uint16_t appearance) {
void NimBLEAdvertisedDevice::setManufacturerData(std::string manufacturerData) {
m_manufacturerData = manufacturerData;
m_haveManufacturerData = true;
-
- char* pHex = NimBLEUtils::buildHexData(nullptr, (uint8_t*) m_manufacturerData.data(), (uint8_t) m_manufacturerData.length());
- NIMBLE_LOGD(LOG_TAG,"- manufacturer data: %s", pHex);
- free(pHex);
} // setManufacturerData
@@ -418,7 +462,6 @@ void NimBLEAdvertisedDevice::setManufacturerData(std::string manufacturerData) {
void NimBLEAdvertisedDevice::setName(std::string name) {
m_name = name;
m_haveName = true;
- NIMBLE_LOGD(LOG_TAG,"- setName(): name: %s", m_name.c_str());
} // setName
@@ -429,19 +472,9 @@ void NimBLEAdvertisedDevice::setName(std::string name) {
void NimBLEAdvertisedDevice::setRSSI(int rssi) {
m_rssi = rssi;
m_haveRSSI = true;
- NIMBLE_LOGD(LOG_TAG,"- setRSSI(): rssi: %d", m_rssi);
} // setRSSI
-/**
- * @brief Set the Scan that created this advertised device.
- * @param pScan The Scan that created this advertised device.
- */
-void NimBLEAdvertisedDevice::setScan(NimBLEScan* pScan) {
- m_pScan = pScan;
-} // setScan
-
-
/**
* @brief Set the Service UUID for this device.
* @param [in] serviceUUID The discovered serviceUUID
@@ -459,36 +492,32 @@ void NimBLEAdvertisedDevice::setServiceUUID(const char* serviceUUID) {
void NimBLEAdvertisedDevice::setServiceUUID(NimBLEUUID serviceUUID) {
// Don't add duplicates
for (int i = 0; i < m_serviceUUIDs.size(); i++) {
- if (m_serviceUUIDs[i].equals(serviceUUID)) {
+ if (m_serviceUUIDs[i] == serviceUUID) {
return;
}
}
m_serviceUUIDs.push_back(serviceUUID);
m_haveServiceUUID = true;
- NIMBLE_LOGD(LOG_TAG,"- addServiceUUID(): serviceUUID: %s", serviceUUID.toString().c_str());
} // setServiceUUID
/**
* @brief Set the ServiceData value.
- * @param [in] data ServiceData value.
+ * @param [in] uuid The UUID that the service data belongs to.
+ * @param [in] data The service data.
*/
-void NimBLEAdvertisedDevice::setServiceData(std::string serviceData) {
- m_haveServiceData = true; // Set the flag that indicates we have service data.
- m_serviceData = serviceData; // Save the service data that we received.
+void NimBLEAdvertisedDevice::setServiceData(NimBLEUUID uuid, std::string data) {
+ m_haveServiceData = true;
+ for(auto &it : m_serviceDataVec) {
+ if(it.first == uuid) {
+ it.second = data;
+ return;
+ }
+ }
+ m_serviceDataVec.push_back({uuid, data});
} //setServiceData
-/**
- * @brief Set the ServiceDataUUID value.
- * @param [in] data ServiceDataUUID value.
- */
-void NimBLEAdvertisedDevice::setServiceDataUUID(NimBLEUUID uuid) {
- m_haveServiceData = true; // Set the flag that indicates we have service data.
- m_serviceDataUUID = uuid;
-} // setServiceDataUUID
-
-
/**
* @brief Set the power level for this device.
* @param [in] txPower The discovered power level.
@@ -496,7 +525,6 @@ void NimBLEAdvertisedDevice::setServiceDataUUID(NimBLEUUID uuid) {
void NimBLEAdvertisedDevice::setTXPower(int8_t txPower) {
m_txPower = txPower;
m_haveTXPower = true;
- NIMBLE_LOGD(LOG_TAG,"- txPower: %d", m_txPower);
} // setTXPower
@@ -532,43 +560,60 @@ std::string NimBLEAdvertisedDevice::toString() {
res += val;
}
- res += ", advType: " + std::string(NimBLEUtils::advTypeToString(m_advType));
+ if(haveServiceData()) {
+ size_t count = getServiceDataCount();
+ res += "\nService Data:";
+ for(size_t i = 0; i < count; i++) {
+ res += "\nUUID: " + std::string(getServiceDataUUID(i));
+ res += ", Data: " + getServiceData(i);
+ }
+ }
return res;
} // toString
+/**
+ * @brief Get the payload advertised by the device.
+ * @return The advertisement payload.
+ */
uint8_t* NimBLEAdvertisedDevice::getPayload() {
return m_payload;
-}
+} // getPayload
+/**
+ * @brief Get the advertised device address type.
+ * @return The device address type:
+ * * BLE_ADDR_PUBLIC (0x00)
+ * * BLE_ADDR_RANDOM (0x01)
+ * * BLE_ADDR_PUBLIC_ID (0x02)
+ * * BLE_ADDR_RANDOM_ID (0x03)
+ */
uint8_t NimBLEAdvertisedDevice::getAddressType() {
- return m_addressType;
-}
+ return m_address.getType();
+} // getAddressType
+/**
+ * @brief Get the timeStamp of when the device last advertised.
+ * @return The timeStamp of when the device was last seen.
+ */
time_t NimBLEAdvertisedDevice::getTimestamp() {
return m_timestamp;
-}
-
-
-void NimBLEAdvertisedDevice::setAddressType(uint8_t type) {
- m_addressType = type;
-}
+} // getTimestamp
+/**
+ * @brief Get the length of the payload advertised by the device.
+ * @return The size of the payload in bytes.
+ */
size_t NimBLEAdvertisedDevice::getPayloadLength() {
return m_payloadLength;
-}
+} // getPayloadLength
-void NimBLEAdvertisedDevice::setAdvertisementResult(uint8_t* payload, uint8_t length){
- m_payload = payload;
- m_payloadLength = length;
-}
-
#endif // #if defined( CONFIG_BT_NIMBLE_ROLE_CENTRAL)
#endif /* CONFIG_BT_ENABLED */
diff --git a/libesp32/NimBLE-Arduino/src/NimBLEAdvertisedDevice.h b/libesp32/NimBLE-Arduino/src/NimBLEAdvertisedDevice.h
index c38d7001d..ebdb85c9e 100644
--- a/libesp32/NimBLE-Arduino/src/NimBLEAdvertisedDevice.h
+++ b/libesp32/NimBLE-Arduino/src/NimBLEAdvertisedDevice.h
@@ -46,8 +46,16 @@ public:
uint16_t getAppearance();
std::string getManufacturerData();
+ /**
+ * @brief A template to convert the service data to .
+ * @tparam T The type to convert the data to.
+ * @param [in] skipSizeCheck If true it will skip checking if the data size is less than sizeof().
+ * @return The data converted to or NULL if skipSizeCheck is false and the data is
+ * less than sizeof().
+ * @details Use: getManufacturerData(skipSizeCheck);
+ */
template
- T getManufacturerData(bool skipSizeCheck = false) {
+ T getManufacturerData(bool skipSizeCheck = false) {
std::string data = getManufacturerData();
if(!skipSizeCheck && data.size() < sizeof(T)) return T();
const char *pData = data.data();
@@ -57,51 +65,73 @@ public:
std::string getName();
int getRSSI();
NimBLEScan* getScan();
- std::string getServiceData();
+ size_t getServiceDataCount();
+ std::string getServiceData(uint8_t index = 0);
+ std::string getServiceData(const NimBLEUUID &uuid) const;
+ /**
+ * @brief A template to convert the service data to .
+ * @tparam T The type to convert the data to.
+ * @param [in] index The vector index of the service data requested.
+ * @param [in] skipSizeCheck If true it will skip checking if the data size is less than sizeof().
+ * @return The data converted to or NULL if skipSizeCheck is false and the data is
+ * less than sizeof().
+ * @details Use: getServiceData(skipSizeCheck);
+ */
template
- T getServiceData(bool skipSizeCheck = false) {
- std::string data = getServiceData();
+ T getServiceData(uint8_t index = 0, bool skipSizeCheck = false) {
+ std::string data = getServiceData(index);
if(!skipSizeCheck && data.size() < sizeof(T)) return T();
const char *pData = data.data();
return *((T *)pData);
}
- NimBLEUUID getServiceDataUUID();
- NimBLEUUID getServiceUUID();
+ /**
+ * @brief A template to convert the service data to .
+ * @tparam T The type to convert the data to.
+ * @param [in] uuid The uuid of the service data requested.
+ * @param [in] skipSizeCheck If true it will skip checking if the data size is less than sizeof().
+ * @return The data converted to or NULL if skipSizeCheck is false and the data is
+ * less than sizeof().
+ * @details Use: getServiceData(skipSizeCheck);
+ */
+ template
+ T getServiceData(const NimBLEUUID &uuid, bool skipSizeCheck = false) {
+ std::string data = getServiceData(uuid);
+ if(!skipSizeCheck && data.size() < sizeof(T)) return T();
+ const char *pData = data.data();
+ return *((T *)pData);
+ }
+
+ NimBLEUUID getServiceDataUUID(uint8_t index = 0);
+ NimBLEUUID getServiceUUID(uint8_t index = 0);
+ size_t getServiceUUIDCount();
int8_t getTXPower();
uint8_t* getPayload();
size_t getPayloadLength();
uint8_t getAddressType();
time_t getTimestamp();
- void setAddressType(uint8_t type);
-
-
- bool isAdvertisingService(const NimBLEUUID &uuid);
- bool haveAppearance();
- bool haveManufacturerData();
- bool haveName();
- bool haveRSSI();
- bool haveServiceData();
- bool haveServiceUUID();
- bool haveTXPower();
-
- std::string toString();
+ bool isAdvertisingService(const NimBLEUUID &uuid) const;
+ bool haveAppearance();
+ bool haveManufacturerData();
+ bool haveName();
+ bool haveRSSI();
+ bool haveServiceData();
+ bool haveServiceUUID();
+ bool haveTXPower();
+ std::string toString();
private:
friend class NimBLEScan;
- void parseAdvertisement(ble_hs_adv_fields *fields);
+ void parseAdvertisement(uint8_t* payload, uint8_t length);
void setAddress(NimBLEAddress address);
void setAdvType(uint8_t advType);
- void setAdvertisementResult(uint8_t* payload, uint8_t length);
void setAppearance(uint16_t appearance);
void setManufacturerData(std::string manufacturerData);
void setName(std::string name);
void setRSSI(int rssi);
- void setScan(NimBLEScan* pScan);
- void setServiceData(std::string data);
- void setServiceDataUUID(NimBLEUUID uuid);
+ void setServiceData(NimBLEUUID serviceUUID, std::string data);
void setServiceUUID(const char* serviceUUID);
void setServiceUUID(NimBLEUUID serviceUUID);
void setTXPower(int8_t txPower);
@@ -118,20 +148,17 @@ private:
NimBLEAddress m_address = NimBLEAddress("");
uint8_t m_advType;
uint16_t m_appearance;
- int m_deviceType;
std::string m_manufacturerData;
std::string m_name;
- NimBLEScan* m_pScan;
int m_rssi;
- std::vector m_serviceUUIDs;
int8_t m_txPower;
- std::string m_serviceData;
- NimBLEUUID m_serviceDataUUID;
uint8_t* m_payload;
size_t m_payloadLength;
- uint8_t m_addressType;
time_t m_timestamp;
bool m_callbackSent;
+
+ std::vector m_serviceUUIDs;
+ std::vector>m_serviceDataVec;
};
/**
@@ -150,7 +177,6 @@ public:
* As we are scanning, we will find new devices. When found, this call back is invoked with a reference to the
* device that was found. During any individual scan, a device will only be detected one time.
*/
- //virtual void onResult(NimBLEAdvertisedDevice advertisedDevice) = 0;
virtual void onResult(NimBLEAdvertisedDevice* advertisedDevice) = 0;
};
diff --git a/libesp32/NimBLE-Arduino/src/NimBLEAdvertising.cpp b/libesp32/NimBLE-Arduino/src/NimBLEAdvertising.cpp
index 7e1e07ae3..36bdbf9e9 100644
--- a/libesp32/NimBLE-Arduino/src/NimBLEAdvertising.cpp
+++ b/libesp32/NimBLE-Arduino/src/NimBLEAdvertising.cpp
@@ -31,7 +31,6 @@ static const char* LOG_TAG = "NimBLEAdvertising";
/**
* @brief Construct a default advertising object.
- *
*/
NimBLEAdvertising::NimBLEAdvertising() {
memset(&m_advData, 0, sizeof m_advData);
@@ -55,6 +54,11 @@ NimBLEAdvertising::NimBLEAdvertising() {
m_advParams.itvl_min = 0;
m_advParams.itvl_max = 0;
+ m_customAdvData = false;
+ m_customScanResponseData = false;
+ m_scanResp = true;
+ m_advDataSet = false;
+
} // NimBLEAdvertising
@@ -64,6 +68,7 @@ NimBLEAdvertising::NimBLEAdvertising() {
*/
void NimBLEAdvertising::addServiceUUID(const NimBLEUUID &serviceUUID) {
m_serviceUUIDs.push_back(serviceUUID);
+ m_advDataSet = false;
} // addServiceUUID
@@ -76,45 +81,74 @@ void NimBLEAdvertising::addServiceUUID(const char* serviceUUID) {
} // addServiceUUID
+/**
+ * @brief Add a service uuid to exposed list of services.
+ * @param [in] serviceUUID The UUID of the service to expose.
+ */
+void NimBLEAdvertising::removeServiceUUID(const NimBLEUUID &serviceUUID) {
+ //m_serviceUUIDs.erase(std::remove_if(m_serviceUUIDs.begin(), m_serviceUUIDs.end(),[serviceUUID](const NimBLEUUID &s) {return serviceUUID == s;}), m_serviceUUIDs.end());
+ for(auto it = m_serviceUUIDs.begin(); it != m_serviceUUIDs.end(); ++it) {
+ if((*it) == serviceUUID) {
+ m_serviceUUIDs.erase(it);
+ break;
+ }
+ }
+ m_advDataSet = false;
+} // addServiceUUID
+
+
/**
* @brief Set the device appearance in the advertising data.
- * The appearance attribute is of type 0x19. The codes for distinct appearances can be found here:
+ * The codes for distinct appearances can be found here:\n
* https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.characteristic.gap.appearance.xml.
* @param [in] appearance The appearance of the device in the advertising data.
- * @return N/A.
*/
void NimBLEAdvertising::setAppearance(uint16_t appearance) {
m_advData.appearance = appearance;
m_advData.appearance_is_present = 1;
} // setAppearance
+
+/**
+ * @brief Set the type of advertisment to use.
+ * @param [in] adv_type:
+ * * BLE_HCI_ADV_TYPE_ADV_IND (0) - indirect advertising
+ * * BLE_HCI_ADV_TYPE_ADV_DIRECT_IND_HD (1) - direct advertisng - high duty cycle
+ * * BLE_HCI_ADV_TYPE_ADV_SCAN_IND (2) - indirect scan response
+ * * BLE_HCI_ADV_TYPE_ADV_NONCONN_IND (3) - indirect advertisng - not connectable
+ * * BLE_HCI_ADV_TYPE_ADV_DIRECT_IND_LD (4) - direct advertising - low duty cycle
+ */
void NimBLEAdvertising::setAdvertisementType(uint8_t adv_type){
m_advParams.conn_mode = adv_type;
} // setAdvertisementType
+
+/**
+ * @brief Set the minimum advertising interval.
+ * @param [in] mininterval Minimum value for advertising interval in 0.625ms units, 0 = use default.
+ */
void NimBLEAdvertising::setMinInterval(uint16_t mininterval) {
m_advParams.itvl_min = mininterval;
} // setMinInterval
+
+/**
+ * @brief Set the maximum advertising interval.
+ * @param [in] maxinterval Maximum value for advertising interval in 0.625ms units, 0 = use default.
+ */
void NimBLEAdvertising::setMaxInterval(uint16_t maxinterval) {
m_advParams.itvl_max = maxinterval;
} // setMaxInterval
-/* These are dummy functions for now for compatibility */
-void NimBLEAdvertising::setMinPreferred(uint16_t mininterval) {
- //m_advData.min_interval = mininterval;
-} //
-
-void NimBLEAdvertising::setMaxPreferred(uint16_t maxinterval) {
- //m_advData.max_interval = maxinterval;
-} //
-/*******************************************************/
-
-
+/**
+ * @brief Set if scan response is available.
+ * @param [in] set true = scan response available.
+ */
void NimBLEAdvertising::setScanResponse(bool set) {
m_scanResp = set;
-}
+} // setScanResponse
+
/**
* @brief Set the filtering for the scan filter.
@@ -145,9 +179,13 @@ void NimBLEAdvertising::setScanFilter(bool scanRequestWhitelistOnly, bool connec
}
} // setScanFilter
+
/**
* @brief Set the advertisement data that is to be published in a regular advertisement.
* @param [in] advertisementData The data to be advertised.
+ * @details The use of this function will replace any data set with addServiceUUID\n
+ * or setAppearance. If you wish for these to be advertised you must include them\n
+ * in the advertisementData parameter sent.
*/
void NimBLEAdvertising::setAdvertisementData(NimBLEAdvertisementData& advertisementData) {
@@ -166,6 +204,8 @@ void NimBLEAdvertising::setAdvertisementData(NimBLEAdvertisementData& advertisem
/**
* @brief Set the advertisement data that is to be published in a scan response.
* @param [in] advertisementData The data to be advertised.
+ * @details Calling this without also using setAdvertisementData will have no effect.\n
+ * When using custom scan response data you must also use custom advertisement data.
*/
void NimBLEAdvertising::setScanResponseData(NimBLEAdvertisementData& advertisementData) {
NIMBLE_LOGD(LOG_TAG, ">> setScanResponseData");
@@ -182,10 +222,10 @@ void NimBLEAdvertising::setScanResponseData(NimBLEAdvertisementData& advertiseme
/**
* @brief Start advertising.
- * Start advertising.
- * @return N/A.
+ * @param [in] duration The duration, in seconds, to advertise, 0 == advertise forever.
+ * @param [in] advCompleteCB A pointer to a callback to be invoked when advertising ends.
*/
-void NimBLEAdvertising::start() {
+void NimBLEAdvertising::start(uint32_t duration, void (*advCompleteCB)(NimBLEAdvertising *pAdv)) {
NIMBLE_LOGD(LOG_TAG, ">> Advertising start: customAdvData: %d, customScanResponseData: %d", m_customAdvData, m_customScanResponseData);
// If Host is not synced we cannot start advertising.
@@ -211,6 +251,15 @@ void NimBLEAdvertising::start() {
return;
}
+ if(duration == 0){
+ duration = BLE_HS_FOREVER;
+ }
+ else{
+ duration = duration*1000; // convert duration to milliseconds
+ }
+
+ m_advCompCB = advCompleteCB;
+
int rc = 0;
if (!m_customAdvData && !m_advDataSet) {
@@ -356,13 +405,13 @@ void NimBLEAdvertising::start() {
}
#if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
- rc = ble_gap_adv_start(0, NULL, BLE_HS_FOREVER,
+ rc = ble_gap_adv_start(0, NULL, duration,
&m_advParams,
- (pServer != nullptr) ? NimBLEServer::handleGapEvent : NULL,
- pServer);
+ (pServer != nullptr) ? NimBLEServer::handleGapEvent : NimBLEAdvertising::handleGapEvent,
+ (pServer != nullptr) ? (void*)pServer : (void*)this);
#else
- rc = ble_gap_adv_start(0, NULL, BLE_HS_FOREVER,
- &m_advParams, NULL,NULL);
+ rc = ble_gap_adv_start(0, NULL, duration,
+ &m_advParams, NimBLEAdvertising::handleGapEvent, this);
#endif
if (rc != 0) {
NIMBLE_LOGC(LOG_TAG, "Error enabling advertising; rc=%d, %s", rc, NimBLEUtils::returnCodeToString(rc));
@@ -375,8 +424,6 @@ void NimBLEAdvertising::start() {
/**
* @brief Stop advertising.
- * Stop advertising.
- * @return N/A.
*/
void NimBLEAdvertising::stop() {
NIMBLE_LOGD(LOG_TAG, ">> stop");
@@ -391,6 +438,25 @@ void NimBLEAdvertising::stop() {
/**
+ * @brief Handles the callback when advertising stops.
+ */
+void NimBLEAdvertising::advCompleteCB() {
+ if(m_advCompCB != nullptr) {
+ m_advCompCB(this);
+ }
+}
+
+
+/**
+ * @brief Check if currently advertising.
+ * @return true if advertising is active.
+ */
+bool NimBLEAdvertising::isAdvertising() {
+ return ble_gap_adv_active();
+}
+
+
+/*
* Host reset seems to clear advertising data,
* we need clear the flag so it reloads it.
*/
@@ -399,6 +465,22 @@ void NimBLEAdvertising::onHostReset() {
}
+/**
+ * @brief Handler for gap events when not using peripheral role.
+ * @param [in] event the event data.
+ * @param [in] arg pointer to the advertising instance.
+ */
+/*STATIC*/
+int NimBLEAdvertising::handleGapEvent(struct ble_gap_event *event, void *arg) {
+ NimBLEAdvertising *pAdv = (NimBLEAdvertising*)arg;
+
+ if(event->type == BLE_GAP_EVENT_ADV_COMPLETE) {
+ pAdv->advCompleteCB();
+ }
+ return 0;
+}
+
+
/**
* @brief Add data to the payload to be advertised.
* @param [in] data The data to be added to the payload.
@@ -411,6 +493,19 @@ void NimBLEAdvertisementData::addData(const std::string &data) {
} // addData
+/**
+ * @brief Add data to the payload to be advertised.
+ * @param [in] data The data to be added to the payload.
+ * @param [in] length The size of data to be added to the payload.
+ */
+void NimBLEAdvertisementData::addData(char * data, size_t length){
+ if ((m_payload.length() + length) > BLE_HS_ADV_MAX_SZ) {
+ return;
+ }
+ m_payload.append(data,length);
+} // addData
+
+
/**
* @brief Set the appearance.
* @param [in] appearance The appearance code value.
@@ -427,8 +522,8 @@ void NimBLEAdvertisementData::setAppearance(uint16_t appearance) {
/**
- * @brief Set the complete services.
- * @param [in] uuid The single service to advertise.
+ * @brief Set the complete services to advertise.
+ * @param [in] uuid The UUID of the service.
*/
void NimBLEAdvertisementData::setCompleteServices(const NimBLEUUID &uuid) {
char cdata[2];
@@ -465,16 +560,7 @@ void NimBLEAdvertisementData::setCompleteServices(const NimBLEUUID &uuid) {
/**
* @brief Set the advertisement flags.
- * @param [in] The flags to be set in the advertisement.
- * * ****DO NOT USE THESE****
- * * ESP_BLE_ADV_FLAG_LIMIT_DISC
- * * ESP_BLE_ADV_FLAG_GEN_DISC
- * * ESP_BLE_ADV_FLAG_BREDR_NOT_SPT
- * * ESP_BLE_ADV_FLAG_DMT_CONTROLLER_SPT
- * * ESP_BLE_ADV_FLAG_DMT_HOST_SPT
- * * ESP_BLE_ADV_FLAG_NON_LIMIT_DISC
- * *
- * * ****THESE ARE SUPPORTED****
+ * @param [in] flag The flags to be set in the advertisement.
* * BLE_HS_ADV_F_DISC_LTD
* * BLE_HS_ADV_F_DISC_GEN
* * BLE_HS_ADV_F_BREDR_UNSUP - must always use with NimBLE
@@ -490,7 +576,7 @@ void NimBLEAdvertisementData::setFlags(uint8_t flag) {
/**
* @brief Set manufacturer specific data.
- * @param [in] data Manufacturer data.
+ * @param [in] data The manufacturer data to advertise.
*/
void NimBLEAdvertisementData::setManufacturerData(const std::string &data) {
NIMBLE_LOGD("NimBLEAdvertisementData", ">> setManufacturerData");
@@ -503,8 +589,8 @@ void NimBLEAdvertisementData::setManufacturerData(const std::string &data) {
/**
- * @brief Set the name.
- * @param [in] The complete name of the device.
+ * @brief Set the complete name of this device.
+ * @param [in] name The name to advertise.
*/
void NimBLEAdvertisementData::setName(const std::string &name) {
NIMBLE_LOGD("NimBLEAdvertisementData", ">> setName: %s", name.c_str());
@@ -517,7 +603,7 @@ void NimBLEAdvertisementData::setName(const std::string &name) {
/**
- * @brief Set the partial services.
+ * @brief Set the partial services to advertise.
* @param [in] uuid The single service to advertise.
*/
void NimBLEAdvertisementData::setPartialServices(const NimBLEUUID &uuid) {
@@ -555,8 +641,8 @@ void NimBLEAdvertisementData::setPartialServices(const NimBLEUUID &uuid) {
/**
* @brief Set the service data (UUID + data)
- * @param [in] uuid The UUID to set with the service data. Size of UUID will be used.
- * @param [in] data The data to be associated with the service data advert.
+ * @param [in] uuid The UUID to set with the service data.
+ * @param [in] data The data to be associated with the service data advertised.
*/
void NimBLEAdvertisementData::setServiceData(const NimBLEUUID &uuid, const std::string &data) {
char cdata[2];
@@ -593,7 +679,7 @@ void NimBLEAdvertisementData::setServiceData(const NimBLEUUID &uuid, const std::
/**
* @brief Set the short name.
- * @param [in] The short name of the device.
+ * @param [in] name The short name of the device.
*/
void NimBLEAdvertisementData::setShortName(const std::string &name) {
NIMBLE_LOGD("NimBLEAdvertisementData", ">> setShortName: %s", name.c_str());
diff --git a/libesp32/NimBLE-Arduino/src/NimBLEAdvertising.h b/libesp32/NimBLE-Arduino/src/NimBLEAdvertising.h
index ad76d7eec..2fab71004 100644
--- a/libesp32/NimBLE-Arduino/src/NimBLEAdvertising.h
+++ b/libesp32/NimBLE-Arduino/src/NimBLEAdvertising.h
@@ -57,6 +57,7 @@ public:
void setServiceData(const NimBLEUUID &uuid, const std::string &data);
void setShortName(const std::string &name);
void addData(const std::string &data); // Add data to the payload.
+ void addData(char * data, size_t length);
std::string getPayload(); // Retrieve the current advert payload.
private:
@@ -75,34 +76,35 @@ public:
NimBLEAdvertising();
void addServiceUUID(const NimBLEUUID &serviceUUID);
void addServiceUUID(const char* serviceUUID);
- void start();
+ void removeServiceUUID(const NimBLEUUID &serviceUUID);
+ void start(uint32_t duration = 0, void (*advCompleteCB)(NimBLEAdvertising *pAdv) = nullptr);
void stop();
void setAppearance(uint16_t appearance);
void setAdvertisementType(uint8_t adv_type);
void setMaxInterval(uint16_t maxinterval);
void setMinInterval(uint16_t mininterval);
void setAdvertisementData(NimBLEAdvertisementData& advertisementData);
- void setScanFilter(bool scanRequertWhitelistOnly, bool connectWhitelistOnly);
+ void setScanFilter(bool scanRequestWhitelistOnly, bool connectWhitelistOnly);
void setScanResponseData(NimBLEAdvertisementData& advertisementData);
- void setPrivateAddress(uint8_t type = BLE_ADDR_RANDOM);
-
- void setMinPreferred(uint16_t);
- void setMaxPreferred(uint16_t);
void setScanResponse(bool);
+ void advCompleteCB();
+ bool isAdvertising();
private:
friend class NimBLEDevice;
- void onHostReset();
+ void onHostReset();
+ static int handleGapEvent(struct ble_gap_event *event, void *arg);
- ble_hs_adv_fields m_advData;
- ble_hs_adv_fields m_scanData;
- ble_gap_adv_params m_advParams;
+ ble_hs_adv_fields m_advData;
+ ble_hs_adv_fields m_scanData;
+ ble_gap_adv_params m_advParams;
std::vector m_serviceUUIDs;
- bool m_customAdvData = false; // Are we using custom advertising data?
- bool m_customScanResponseData = false; // Are we using custom scan response data?
- bool m_scanResp = true;
- bool m_advDataSet = false;
+ bool m_customAdvData;
+ bool m_customScanResponseData;
+ bool m_scanResp;
+ bool m_advDataSet;
+ void (*m_advCompCB)(NimBLEAdvertising *pAdv);
};
diff --git a/libesp32/NimBLE-Arduino/src/NimBLEBeacon.cpp b/libesp32/NimBLE-Arduino/src/NimBLEBeacon.cpp
index 718a507f5..8c4574bc2 100644
--- a/libesp32/NimBLE-Arduino/src/NimBLEBeacon.cpp
+++ b/libesp32/NimBLE-Arduino/src/NimBLEBeacon.cpp
@@ -15,6 +15,7 @@
#if defined(CONFIG_BT_ENABLED)
#include
+#include
#include "NimBLEBeacon.h"
#include "NimBLELog.h"
@@ -22,6 +23,10 @@
static const char* LOG_TAG = "NimBLEBeacon";
+
+/**
+ * @brief Construct a default beacon object.
+ */
NimBLEBeacon::NimBLEBeacon() {
m_beaconData.manufacturerId = 0x4c00;
m_beaconData.subType = 0x02;
@@ -32,32 +37,64 @@ NimBLEBeacon::NimBLEBeacon() {
memset(m_beaconData.proximityUUID, 0, sizeof(m_beaconData.proximityUUID));
} // NimBLEBeacon
+
+/**
+ * @brief Retrieve the data that is being advertised.
+ * @return The advertised data.
+ */
std::string NimBLEBeacon::getData() {
return std::string((char*) &m_beaconData, sizeof(m_beaconData));
} // getData
+
+/**
+ * @brief Get the major value being advertised.
+ * @return The major value advertised.
+ */
uint16_t NimBLEBeacon::getMajor() {
return m_beaconData.major;
}
+
+/**
+ * @brief Get the manufacturer ID being advertised.
+ * @return The manufacturer ID value advertised.
+ */
uint16_t NimBLEBeacon::getManufacturerId() {
return m_beaconData.manufacturerId;
}
+
+/**
+ * @brief Get the minor value being advertised.
+ * @return minor value advertised.
+ */
uint16_t NimBLEBeacon::getMinor() {
return m_beaconData.minor;
}
+
+/**
+ * @brief Get the proximity UUID being advertised.
+ * @return The UUID advertised.
+ */
NimBLEUUID NimBLEBeacon::getProximityUUID() {
- return NimBLEUUID(m_beaconData.proximityUUID, 16, false);
+ return NimBLEUUID(m_beaconData.proximityUUID, 16, true);
}
+
+/**
+ * @brief Get the signal power being advertised.
+ * @return signal power level advertised.
+ */
int8_t NimBLEBeacon::getSignalPower() {
return m_beaconData.signalPower;
}
+
/**
- * Set the raw data for the beacon record.
+ * @brief Set the raw data for the beacon record.
+ * @param [in] data The raw beacon data.
*/
void NimBLEBeacon::setData(const std::string &data) {
if (data.length() != sizeof(m_beaconData)) {
@@ -68,24 +105,51 @@ void NimBLEBeacon::setData(const std::string &data) {
memcpy(&m_beaconData, data.data(), sizeof(m_beaconData));
} // setData
+
+/**
+ * @brief Set the major value.
+ * @param [in] major The major value.
+ */
void NimBLEBeacon::setMajor(uint16_t major) {
m_beaconData.major = ENDIAN_CHANGE_U16(major);
} // setMajor
+
+/**
+ * @brief Set the manufacturer ID.
+ * @param [in] manufacturerId The manufacturer ID value.
+ */
void NimBLEBeacon::setManufacturerId(uint16_t manufacturerId) {
m_beaconData.manufacturerId = ENDIAN_CHANGE_U16(manufacturerId);
} // setManufacturerId
+
+/**
+ * @brief Set the minor value.
+ * @param [in] minor The minor value.
+ */
void NimBLEBeacon::setMinor(uint16_t minor) {
m_beaconData.minor = ENDIAN_CHANGE_U16(minor);
} // setMinior
+
+/**
+ * @brief Set the proximity UUID.
+ * @param [in] uuid The proximity UUID.
+ */
void NimBLEBeacon::setProximityUUID(const NimBLEUUID &uuid) {
NimBLEUUID temp_uuid = uuid;
temp_uuid.to128();
- memcpy(m_beaconData.proximityUUID, temp_uuid.getNative()->u128.value, 16);
+ std::reverse_copy(temp_uuid.getNative()->u128.value,
+ temp_uuid.getNative()->u128.value + 16,
+ m_beaconData.proximityUUID);
} // setProximityUUID
+
+/**
+ * @brief Set the signal power.
+ * @param [in] signalPower The signal power value.
+ */
void NimBLEBeacon::setSignalPower(int8_t signalPower) {
m_beaconData.signalPower = signalPower;
} // setSignalPower
diff --git a/libesp32/NimBLE-Arduino/src/NimBLECharacteristic.cpp b/libesp32/NimBLE-Arduino/src/NimBLECharacteristic.cpp
index 0bd11f6ba..6f7d3d7e0 100644
--- a/libesp32/NimBLE-Arduino/src/NimBLECharacteristic.cpp
+++ b/libesp32/NimBLE-Arduino/src/NimBLECharacteristic.cpp
@@ -16,16 +16,18 @@
#if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
#include "NimBLECharacteristic.h"
-#include "NimBLE2902.h"
#include "NimBLE2904.h"
#include "NimBLEDevice.h"
#include "NimBLELog.h"
#define NULL_HANDLE (0xffff)
+#define NIMBLE_SUB_NOTIFY 0x0001
+#define NIMBLE_SUB_INDICATE 0x0002
static NimBLECharacteristicCallbacks defaultCallback;
static const char* LOG_TAG = "NimBLECharacteristic";
+
/**
* @brief Construct a characteristic
* @param [in] uuid - UUID (const char*) for the characteristic.
@@ -58,6 +60,9 @@ NimBLECharacteristic::NimBLECharacteristic(const NimBLEUUID &uuid, uint16_t prop
* @brief Destructor.
*/
NimBLECharacteristic::~NimBLECharacteristic() {
+ for(auto &it : m_dscVec) {
+ delete it;
+ }
} // ~NimBLECharacteristic
@@ -65,6 +70,7 @@ NimBLECharacteristic::~NimBLECharacteristic() {
* @brief Create a new BLE Descriptor associated with this characteristic.
* @param [in] uuid - The UUID of the descriptor.
* @param [in] properties - The properties of the descriptor.
+ * @param [in] max_len - The max length in bytes of the descriptor value.
* @return The new BLE descriptor.
*/
NimBLEDescriptor* NimBLECharacteristic::createDescriptor(const char* uuid, uint32_t properties, uint16_t max_len) {
@@ -76,25 +82,15 @@ NimBLEDescriptor* NimBLECharacteristic::createDescriptor(const char* uuid, uint3
* @brief Create a new BLE Descriptor associated with this characteristic.
* @param [in] uuid - The UUID of the descriptor.
* @param [in] properties - The properties of the descriptor.
+ * @param [in] max_len - The max length in bytes of the descriptor value.
* @return The new BLE descriptor.
*/
NimBLEDescriptor* NimBLECharacteristic::createDescriptor(const NimBLEUUID &uuid, uint32_t properties, uint16_t max_len) {
NimBLEDescriptor* pDescriptor = nullptr;
if(uuid == NimBLEUUID(uint16_t(0x2902))) {
- if(!(m_properties & BLE_GATT_CHR_F_NOTIFY) && !(m_properties & BLE_GATT_CHR_F_INDICATE)) {
- assert(0 && "Cannot create 2902 descriptior without characteristic notification or indication property set");
- }
- // We cannot have more than one 2902 descriptor, if it's already been created just return a pointer to it.
- pDescriptor = getDescriptorByUUID(uuid);
- if(pDescriptor == nullptr) {
- pDescriptor = new NimBLE2902(this);
- } else {
- return pDescriptor;
- }
-
+ assert(0 && "0x2902 descriptors cannot be manually created");
} else if (uuid == NimBLEUUID(uint16_t(0x2904))) {
pDescriptor = new NimBLE2904(this);
-
} else {
pDescriptor = new NimBLEDescriptor(uuid, properties, max_len, this);
}
@@ -106,8 +102,8 @@ NimBLEDescriptor* NimBLECharacteristic::createDescriptor(const NimBLEUUID &uuid,
/**
* @brief Return the BLE Descriptor for the given UUID if associated with this characteristic.
- * @param [in] descriptorUUID The UUID of the descriptor that we wish to retrieve.
- * @return The BLE Descriptor. If no such descriptor is associated with the characteristic, nullptr is returned.
+ * @param [in] uuid The UUID of the descriptor that we wish to retrieve.
+ * @return pointer to the NimBLEDescriptor. If no such descriptor is associated with the characteristic, nullptr is returned.
*/
NimBLEDescriptor* NimBLECharacteristic::getDescriptorByUUID(const char* uuid) {
return getDescriptorByUUID(NimBLEUUID(uuid));
@@ -116,8 +112,8 @@ NimBLEDescriptor* NimBLECharacteristic::getDescriptorByUUID(const char* uuid) {
/**
* @brief Return the BLE Descriptor for the given UUID if associated with this characteristic.
- * @param [in] descriptorUUID The UUID of the descriptor that we wish to retrieve.
- * @return The BLE Descriptor. If no such descriptor is associated with the characteristic, nullptr is returned.
+ * @param [in] uuid The UUID of the descriptor that we wish to retrieve.
+ * @return pointer to the NimBLEDescriptor. If no such descriptor is associated with the characteristic, nullptr is returned.
*/
NimBLEDescriptor* NimBLECharacteristic::getDescriptorByUUID(const NimBLEUUID &uuid) {
for (auto &it : m_dscVec) {
@@ -138,6 +134,10 @@ uint16_t NimBLECharacteristic::getHandle() {
} // getHandle
+/**
+ * @brief Get the properties of the characteristic.
+ * @return The properties of the characteristic.
+ */
uint16_t NimBLECharacteristic::getProperties() {
return m_properties;
} // getProperties
@@ -162,7 +162,7 @@ NimBLEUUID NimBLECharacteristic::getUUID() {
/**
* @brief Retrieve the current value of the characteristic.
- * @return A pointer to storage containing the current characteristic value.
+ * @return A std::string containing the current characteristic value.
*/
std::string NimBLECharacteristic::getValue(time_t *timestamp) {
portENTER_CRITICAL(&m_valMux);
@@ -189,12 +189,16 @@ size_t NimBLECharacteristic::getDataLength() {
}
+/**
+ * @brief STATIC callback to handle events from the NimBLE stack.
+ */
int NimBLECharacteristic::handleGapEvent(uint16_t conn_handle, uint16_t attr_handle,
struct ble_gatt_access_ctxt *ctxt,
void *arg)
{
const ble_uuid_t *uuid;
int rc;
+ struct ble_gap_conn_desc desc;
NimBLECharacteristic* pCharacteristic = (NimBLECharacteristic*)arg;
NIMBLE_LOGD(LOG_TAG, "Characteristic %s %s event", pCharacteristic->getUUID().toString().c_str(),
@@ -207,7 +211,10 @@ int NimBLECharacteristic::handleGapEvent(uint16_t conn_handle, uint16_t attr_han
// If the packet header is only 8 bytes this is a follow up of a long read
// so we don't want to call the onRead() callback again.
if(ctxt->om->om_pkthdr_len > 8) {
+ rc = ble_gap_conn_find(conn_handle, &desc);
+ assert(rc == 0);
pCharacteristic->m_pCallbacks->onRead(pCharacteristic);
+ pCharacteristic->m_pCallbacks->onRead(pCharacteristic, &desc);
}
portENTER_CRITICAL(&pCharacteristic->m_valMux);
@@ -233,14 +240,15 @@ int NimBLECharacteristic::handleGapEvent(uint16_t conn_handle, uint16_t attr_han
if((len + next->om_len) > BLE_ATT_ATTR_MAX_LEN) {
return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN;
}
- memcpy(&buf[len-1], next->om_data, next->om_len);
+ memcpy(&buf[len], next->om_data, next->om_len);
len += next->om_len;
next = SLIST_NEXT(next, om_next);
}
-
+ rc = ble_gap_conn_find(conn_handle, &desc);
+ assert(rc == 0);
pCharacteristic->setValue(buf, len);
pCharacteristic->m_pCallbacks->onWrite(pCharacteristic);
-
+ pCharacteristic->m_pCallbacks->onWrite(pCharacteristic, &desc);
return 0;
}
default:
@@ -253,70 +261,70 @@ int NimBLECharacteristic::handleGapEvent(uint16_t conn_handle, uint16_t attr_han
/**
- * @brief Set the subscribe status for this characteristic.
- * This will maintain a map of subscribed clients and their indicate/notify status.
- * @return N/A
+ * @brief Get the number of clients subscribed to the characteristic.
+ * @returns Number of clients subscribed to notifications / indications.
+ */
+size_t NimBLECharacteristic::getSubscribedCount() {
+ return m_subscribedVec.size();
+}
+
+
+/**
+ * @brief Set the subscribe status for this characteristic.\n
+ * This will maintain a vector of subscribed clients and their indicate/notify status.
*/
void NimBLECharacteristic::setSubscribe(struct ble_gap_event *event) {
- uint16_t subVal = 0;
- if(event->subscribe.cur_notify) {
- subVal |= NIMBLE_DESC_FLAG_NOTIFY;
- }
- if(event->subscribe.cur_indicate) {
- subVal |= NIMBLE_DESC_FLAG_INDICATE;
- }
-
- if(m_pTaskData != nullptr) {
- m_pTaskData->rc = (subVal & NIMBLE_DESC_FLAG_INDICATE) ? 0 :
- NimBLECharacteristicCallbacks::Status::ERROR_INDICATE_DISABLED;
- xTaskNotifyGive(m_pTaskData->task);
- }
-
- NIMBLE_LOGI(LOG_TAG, "New subscribe value for conn: %d val: %d",
- event->subscribe.conn_handle, subVal);
-
- NimBLE2902* p2902 = (NimBLE2902*)getDescriptorByUUID(uint16_t(0x2902));
- if(p2902 == nullptr){
- ESP_LOGE(LOG_TAG, "No 2902 descriptor found for %s",
- std::string(getUUID()).c_str());
+ ble_gap_conn_desc desc;
+ if(ble_gap_conn_find(event->subscribe.conn_handle, &desc) != 0) {
return;
}
- p2902->setNotifications(subVal & NIMBLE_DESC_FLAG_NOTIFY);
- p2902->setIndications(subVal & NIMBLE_DESC_FLAG_INDICATE);
- p2902->m_pCallbacks->onWrite(p2902);
+ uint16_t subVal = 0;
+ if(event->subscribe.cur_notify > 0 && (m_properties & NIMBLE_PROPERTY::NOTIFY)) {
+ subVal |= NIMBLE_SUB_NOTIFY;
+ }
+ if(event->subscribe.cur_indicate && (m_properties & NIMBLE_PROPERTY::INDICATE)) {
+ subVal |= NIMBLE_SUB_INDICATE;
+ }
+ if(m_pTaskData != nullptr) {
+ m_pTaskData->rc = (subVal & NIMBLE_SUB_INDICATE) ? 0 :
+ NimBLECharacteristicCallbacks::Status::ERROR_INDICATE_DISABLED;
+ xTaskNotifyGive(m_pTaskData->task);
+ }
+
+ NIMBLE_LOGI(LOG_TAG, "New subscribe value for conn: %d val: %d",
+ event->subscribe.conn_handle, subVal);
- auto it = p2902->m_subscribedVec.begin();
- for(;it != p2902->m_subscribedVec.end(); ++it) {
- if((*it).conn_id == event->subscribe.conn_handle) {
+ m_pCallbacks->onSubscribe(this, &desc, subVal);
+
+ auto it = m_subscribedVec.begin();
+ for(;it != m_subscribedVec.end(); ++it) {
+ if((*it).first == event->subscribe.conn_handle) {
break;
}
}
if(subVal > 0) {
- if(it == p2902->m_subscribedVec.end()) {
- chr_sub_status_t client_sub;
- client_sub.conn_id = event->subscribe.conn_handle;
- client_sub.sub_val = subVal;
- p2902->m_subscribedVec.push_back(client_sub);
+ if(it == m_subscribedVec.end()) {
+ m_subscribedVec.push_back({event->subscribe.conn_handle, subVal});
return;
}
- (*it).sub_val = subVal;
+ (*it).second = subVal;
- } else if(it != p2902->m_subscribedVec.end()) {
- p2902->m_subscribedVec.erase(it);
- p2902->m_subscribedVec.shrink_to_fit();
+ } else if(it != m_subscribedVec.end()) {
+ m_subscribedVec.erase(it);
+ m_subscribedVec.shrink_to_fit();
}
+
}
/**
- * @brief Send an indication.
- * An indication is a transmission of up to the first 20 bytes of the characteristic value. An indication
- * will block waiting a positive confirmation from the client.
- * @return N/A
+ * @brief Send an indication.\n
+ * An indication is a transmission of up to the first 20 bytes of the characteristic value.\n
+ * An indication will block waiting for a positive confirmation from the client.
*/
void NimBLECharacteristic::indicate() {
NIMBLE_LOGD(LOG_TAG, ">> indicate: length: %d", getDataLength());
@@ -325,23 +333,24 @@ void NimBLECharacteristic::indicate() {
} // indicate
/**
- * @brief Send a notify.
- * A notification is a transmission of up to the first 20 bytes of the characteristic value. An notification
- * will not block; it is a fire and forget.
- * @return N/A.
+ * @brief Send a notification.\n
+ * A notification is a transmission of up to the first 20 bytes of the characteristic value.\n
+ * A notification will not block; it is a fire and forget.
+ * @param[in] is_notification if true sends a notification, false sends an indication.
*/
void NimBLECharacteristic::notify(bool is_notification) {
NIMBLE_LOGD(LOG_TAG, ">> notify: length: %d", getDataLength());
- NimBLE2902* p2902 = (NimBLE2902*)getDescriptorByUUID(uint16_t(0x2902));
- if(p2902 == nullptr) {
+ if(!(m_properties & NIMBLE_PROPERTY::NOTIFY) &&
+ !(m_properties & NIMBLE_PROPERTY::INDICATE))
+ {
NIMBLE_LOGE(LOG_TAG,
"<< notify-Error; Notify/indicate not enabled for characterisitc: %s",
std::string(getUUID()).c_str());
}
- if (p2902->m_subscribedVec.size() == 0) {
+ if (m_subscribedVec.size() == 0) {
NIMBLE_LOGD(LOG_TAG, "<< notify: No clients subscribed.");
return;
}
@@ -355,18 +364,18 @@ void NimBLECharacteristic::notify(bool is_notification) {
(m_properties & BLE_GATT_CHR_F_READ_ENC);
int rc = 0;
- for (auto &it : p2902->m_subscribedVec) {
- uint16_t _mtu = getService()->getServer()->getPeerMTU(it.conn_id);
+ for (auto &it : m_subscribedVec) {
+ uint16_t _mtu = getService()->getServer()->getPeerMTU(it.first);
// check if connected and subscribed
- if(_mtu == 0 || it.sub_val == 0) {
+ if(_mtu == 0 || it.second == 0) {
continue;
}
// check if security requirements are satisfied
if(reqSec) {
struct ble_gap_conn_desc desc;
- rc = ble_gap_conn_find(it.conn_id, &desc);
+ rc = ble_gap_conn_find(it.first, &desc);
if(rc != 0 || !desc.sec_state.encrypted) {
continue;
}
@@ -376,13 +385,13 @@ void NimBLECharacteristic::notify(bool is_notification) {
NIMBLE_LOGW(LOG_TAG, "- Truncating to %d bytes (maximum notify size)", _mtu - 3);
}
- if(is_notification && (!(it.sub_val & NIMBLE_DESC_FLAG_NOTIFY))) {
+ if(is_notification && (!(it.second & NIMBLE_SUB_NOTIFY))) {
NIMBLE_LOGW(LOG_TAG,
"Sending notification to client subscribed to indications, sending indication instead");
is_notification = false;
}
- if(!is_notification && (!(it.sub_val & NIMBLE_DESC_FLAG_INDICATE))) {
+ if(!is_notification && (!(it.second & NIMBLE_SUB_INDICATE))) {
NIMBLE_LOGW(LOG_TAG,
"Sending indication to client subscribed to notification, sending notification instead");
is_notification = true;
@@ -399,7 +408,7 @@ void NimBLECharacteristic::notify(bool is_notification) {
ble_task_data_t taskData = {nullptr, xTaskGetCurrentTaskHandle(),0, nullptr};
m_pTaskData = &taskData;
- rc = ble_gattc_indicate_custom(it.conn_id, m_handle, om);
+ rc = ble_gattc_indicate_custom(it.first, m_handle, om);
if(rc != 0){
statusRC = NimBLECharacteristicCallbacks::Status::ERROR_GATT;
} else {
@@ -418,7 +427,7 @@ void NimBLECharacteristic::notify(bool is_notification) {
statusRC = NimBLECharacteristicCallbacks::Status::ERROR_INDICATE_FAILURE;
}
} else {
- rc = ble_gattc_notify_custom(it.conn_id, m_handle, om);
+ rc = ble_gattc_notify_custom(it.first, m_handle, om);
if(rc == 0) {
statusRC = NimBLECharacteristicCallbacks::Status::SUCCESS_NOTIFY;
} else {
@@ -435,7 +444,8 @@ void NimBLECharacteristic::notify(bool is_notification) {
/**
* @brief Set the callback handlers for this characteristic.
- * @param [in] pCallbacks An instance of a callbacks structure used to define any callbacks for the characteristic.
+ * @param [in] pCallbacks An instance of a NimBLECharacteristicCallbacks class\n
+ * used to define any callbacks for the characteristic.
*/
void NimBLECharacteristic::setCallbacks(NimBLECharacteristicCallbacks* pCallbacks) {
if (pCallbacks != nullptr){
@@ -473,11 +483,9 @@ void NimBLECharacteristic::setValue(const uint8_t* data, size_t length) {
/**
- * @brief Set the value of the characteristic from string data.
- * We set the value of the characteristic from the bytes contained in the
- * string.
- * @param [in] Set the value of the characteristic.
- * @return N/A.
+ * @brief Set the value of the characteristic from string data.\n
+ * We set the value of the characteristic from the bytes contained in the string.
+ * @param [in] value the std::string value of the characteristic.
*/
void NimBLECharacteristic::setValue(const std::string &value) {
setValue((uint8_t*)(value.data()), value.length());
@@ -515,6 +523,14 @@ void NimBLECharacteristicCallbacks::onRead(NimBLECharacteristic* pCharacteristic
NIMBLE_LOGD("NimBLECharacteristicCallbacks", "onRead: default");
} // onRead
+/**
+ * @brief Callback function to support a read request.
+ * @param [in] pCharacteristic The characteristic that is the source of the event.
+ * @param [in] desc The connection description struct that is associated with the peer that performed the read.
+ */
+void NimBLECharacteristicCallbacks::onRead(NimBLECharacteristic* pCharacteristic, ble_gap_conn_desc* desc) {
+ NIMBLE_LOGD("NimBLECharacteristicCallbacks", "onRead: default");
+} // onRead
/**
* @brief Callback function to support a write request.
@@ -524,6 +540,14 @@ void NimBLECharacteristicCallbacks::onWrite(NimBLECharacteristic* pCharacteristi
NIMBLE_LOGD("NimBLECharacteristicCallbacks", "onWrite: default");
} // onWrite
+/**
+ * @brief Callback function to support a write request.
+ * @param [in] pCharacteristic The characteristic that is the source of the event.
+ * @param [in] desc The connection description struct that is associated with the peer that performed the write.
+ */
+void NimBLECharacteristicCallbacks::onWrite(NimBLECharacteristic* pCharacteristic, ble_gap_conn_desc* desc) {
+ NIMBLE_LOGD("NimBLECharacteristicCallbacks", "onWrite: default");
+} // onWrite
/**
* @brief Callback function to support a Notify request.
@@ -537,12 +561,31 @@ void NimBLECharacteristicCallbacks::onNotify(NimBLECharacteristic* pCharacterist
/**
* @brief Callback function to support a Notify/Indicate Status report.
* @param [in] pCharacteristic The characteristic that is the source of the event.
- * @param [in] s Status of the notification/indication
- * @param [in] code Additional code of underlying errors
+ * @param [in] s Status of the notification/indication.
+ * @param [in] code Additional return code from the NimBLE stack.
*/
void NimBLECharacteristicCallbacks::onStatus(NimBLECharacteristic* pCharacteristic, Status s, int code) {
NIMBLE_LOGD("NimBLECharacteristicCallbacks", "onStatus: default");
} // onStatus
+
+/**
+ * @brief Callback function called when a client changes subscription status.
+ * @param [in] pCharacteristic The characteristic that is the source of the event.
+ * @param [in] desc The connection description struct that is associated with the client.
+ * @param [in] subValue The subscription status:
+ * * 0 = Un-Subscribed
+ * * 1 = Notifications
+ * * 2 = Indications
+ * * 3 = Notifications and Indications
+ */
+void NimBLECharacteristicCallbacks::onSubscribe(NimBLECharacteristic* pCharacteristic,
+ ble_gap_conn_desc* desc,
+ uint16_t subValue)
+{
+ NIMBLE_LOGD("NimBLECharacteristicCallbacks", "onSubscribe: default");
+}
+
+
#endif // #if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
#endif /* CONFIG_BT_ENABLED */
diff --git a/libesp32/NimBLE-Arduino/src/NimBLECharacteristic.h b/libesp32/NimBLE-Arduino/src/NimBLECharacteristic.h
index 4474e5f29..1c7418aef 100644
--- a/libesp32/NimBLE-Arduino/src/NimBLECharacteristic.h
+++ b/libesp32/NimBLE-Arduino/src/NimBLECharacteristic.h
@@ -75,6 +75,15 @@ public:
NimBLEUUID getUUID();
std::string getValue(time_t *timestamp = nullptr);
+ /**
+ * @brief A template to convert the characteristic data to .
+ * @tparam T The type to convert the data to.
+ * @param [in] timestamp A pointer to a time_t struct to store the time the value was read.
+ * @param [in] skipSizeCheck If true it will skip checking if the data size is less than sizeof().
+ * @return The data converted to or NULL if skipSizeCheck is false and the data is
+ * less than sizeof().
+ * @details Use: getValue(×tamp, skipSizeCheck);
+ */
template
T getValue(time_t *timestamp = nullptr, bool skipSizeCheck = false) {
std::string value = getValue();
@@ -90,6 +99,10 @@ public:
void setValue(const uint8_t* data, size_t size);
void setValue(const std::string &value);
+ /**
+ * @brief Convenience template to set the characteristic value to val.
+ * @param [in] s The value to set.
+ */
template
void setValue(const T &s) {
setValue((uint8_t*)&s, sizeof(T));
@@ -97,6 +110,7 @@ public:
std::string toString();
uint16_t getHandle();
+ size_t getSubscribedCount();
private:
@@ -132,6 +146,8 @@ private:
ble_task_data_t *m_pTaskData;
portMUX_TYPE m_valMux;
time_t m_timestamp;
+
+ std::vector> m_subscribedVec;
}; // NimBLECharacteristic
@@ -144,6 +160,12 @@ private:
*/
class NimBLECharacteristicCallbacks {
public:
+
+/**
+ * @brief An enum to provide the callback the status of the
+ * notification/indication, implemented for backward compatibility.
+ * @deprecated To be removed in the future as the NimBLE stack return code is also provided.
+ */
typedef enum {
SUCCESS_INDICATE,
SUCCESS_NOTIFY,
@@ -157,9 +179,12 @@ public:
virtual ~NimBLECharacteristicCallbacks();
virtual void onRead(NimBLECharacteristic* pCharacteristic);
+ virtual void onRead(NimBLECharacteristic* pCharacteristic, ble_gap_conn_desc* desc);
virtual void onWrite(NimBLECharacteristic* pCharacteristic);
+ virtual void onWrite(NimBLECharacteristic* pCharacteristic, ble_gap_conn_desc* desc);
virtual void onNotify(NimBLECharacteristic* pCharacteristic);
virtual void onStatus(NimBLECharacteristic* pCharacteristic, Status s, int code);
+ virtual void onSubscribe(NimBLECharacteristic* pCharacteristic, ble_gap_conn_desc* desc, uint16_t subValue);
};
#endif // #if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
diff --git a/libesp32/NimBLE-Arduino/src/NimBLEClient.cpp b/libesp32/NimBLE-Arduino/src/NimBLEClient.cpp
index 71f1c9afc..539a7a016 100644
--- a/libesp32/NimBLE-Arduino/src/NimBLEClient.cpp
+++ b/libesp32/NimBLE-Arduino/src/NimBLEClient.cpp
@@ -48,8 +48,12 @@ static NimBLEClientCallbacks defaultCallbacks;
*
*/
-NimBLEClient::NimBLEClient()
-{
+
+/**
+ * @brief Constructor, private - only callable by NimBLEDevice::createClient
+ * to ensure proper handling of the list of client objects.
+ */
+NimBLEClient::NimBLEClient(const NimBLEAddress &peerAddress) : m_peerAddress(peerAddress) {
m_pClientCallbacks = &defaultCallbacks;
m_conn_id = BLE_HS_CONN_HANDLE_NONE;
m_isConnected = false;
@@ -86,7 +90,7 @@ NimBLEClient::~NimBLEClient() {
/**
- * @brief Delete any existing services.
+ * @brief Delete all service objects created by this client and clear the vector.
*/
void NimBLEClient::deleteServices() {
NIMBLE_LOGD(LOG_TAG, ">> deleteServices");
@@ -123,30 +127,36 @@ size_t NimBLEClient::deleteService(const NimBLEUUID &uuid) {
/**
- * NOT NEEDED
- */
- /*
-void NimBLEClient::onHostReset() {
-
-}
- */
-
-/**
- * Add overloaded function to ease connect to peer device with not public address
- */
-bool NimBLEClient::connect(NimBLEAdvertisedDevice* device, bool refreshServices) {
- NimBLEAddress address(device->getAddress());
- uint8_t type = device->getAddressType();
- return connect(address, type, refreshServices);
-}
-
-
-/**
- * @brief Connect to the partner (BLE Server).
- * @param [in] address The address of the partner.
+ * @brief Connect to the BLE Server.
+ * @param [in] deleteAttibutes If true this will delete any attribute objects this client may already\n
+ * have created and clears the vectors after successful connection.
* @return True on success.
*/
-bool NimBLEClient::connect(const NimBLEAddress &address, uint8_t type, bool refreshServices) {
+bool NimBLEClient::connect(bool deleteAttibutes) {
+ return connect(m_peerAddress, deleteAttibutes);
+}
+
+/**
+ * @brief Connect to an advertising device.
+ * @param [in] device The device to connect to.
+ * @param [in] deleteAttibutes If true this will delete any attribute objects this client may already\n
+ * have created and clears the vectors after successful connection.
+ * @return True on success.
+ */
+bool NimBLEClient::connect(NimBLEAdvertisedDevice* device, bool deleteAttibutes) {
+ NimBLEAddress address(device->getAddress());
+ return connect(address, deleteAttibutes);
+}
+
+
+/**
+ * @brief Connect to the BLE Server.
+ * @param [in] address The address of the server.
+ * @param [in] deleteAttibutes If true this will delete any attribute objects this client may already\n
+ * have created and clears the vectors after successful connection.
+ * @return True on success.
+ */
+bool NimBLEClient::connect(const NimBLEAddress &address, bool deleteAttibutes) {
NIMBLE_LOGD(LOG_TAG, ">> connect(%s)", address.toString().c_str());
if(!NimBLEDevice::m_synced) {
@@ -163,17 +173,23 @@ bool NimBLEClient::connect(const NimBLEAddress &address, uint8_t type, bool refr
return false;
}
- int rc = 0;
- m_peerAddress = address;
+ if(address == NimBLEAddress("")) {
+ NIMBLE_LOGE(LOG_TAG, "Invalid peer address;(NULL)");
+ return false;
+ } else if(m_peerAddress != address) {
+ m_peerAddress = address;
+ }
ble_addr_t peerAddrt;
- memcpy(&peerAddrt.val, address.getNative(),6);
- peerAddrt.type = type;
+ memcpy(&peerAddrt.val, m_peerAddress.getNative(),6);
+ peerAddrt.type = m_peerAddress.getType();
ble_task_data_t taskData = {this, xTaskGetCurrentTaskHandle(), 0, nullptr};
m_pTaskData = &taskData;
- /** Try to connect the the advertiser. Allow 30 seconds (30000 ms) for
+ int rc = 0;
+
+ /* Try to connect the the advertiser. Allow 30 seconds (30000 ms) for
* timeout (default value of m_connectTimeout).
* Loop on BLE_HS_EBUSY if the scan hasn't stopped yet.
*/
@@ -186,10 +202,9 @@ bool NimBLEClient::connect(const NimBLEAddress &address, uint8_t type, bool refr
}while(rc == BLE_HS_EBUSY);
if (rc != 0 && rc != BLE_HS_EDONE) {
- NIMBLE_LOGE(LOG_TAG, "Error: Failed to connect to device; addr_type=%d "
+ NIMBLE_LOGE(LOG_TAG, "Error: Failed to connect to device; "
"addr=%s, rc=%d; %s",
- type,
- m_peerAddress.toString().c_str(),
+ std::string(m_peerAddress).c_str(),
rc, NimBLEUtils::returnCodeToString(rc));
m_pTaskData = nullptr;
m_waitingToConnect = false;
@@ -205,8 +220,7 @@ bool NimBLEClient::connect(const NimBLEAddress &address, uint8_t type, bool refr
return false;
}
- if(refreshServices) {
- NIMBLE_LOGD(LOG_TAG, "Refreshing Services for: (%s)", address.toString().c_str());
+ if(deleteAttibutes) {
deleteServices();
}
@@ -218,33 +232,38 @@ bool NimBLEClient::connect(const NimBLEAddress &address, uint8_t type, bool refr
/**
- * @brief Called when a characteristic or descriptor requires encryption or authentication to access it.
- * This will pair with the device and bond if enabled.
+ * @brief Initiate a secure connection (pair/bond) with the server.\n
+ * Called automatically when a characteristic or descriptor requires encryption or authentication to access it.
* @return True on success.
*/
bool NimBLEClient::secureConnection() {
ble_task_data_t taskData = {this, xTaskGetCurrentTaskHandle(), 0, nullptr};
- m_pTaskData = &taskData;
- int rc = NimBLEDevice::startSecurity(m_conn_id);
- if(rc != 0){
- m_pTaskData = nullptr;
- return false;
- }
+ int retryCount = 1;
- ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
+ do {
+ m_pTaskData = &taskData;
+
+ int rc = NimBLEDevice::startSecurity(m_conn_id);
+ if(rc != 0){
+ m_pTaskData = nullptr;
+ return false;
+ }
+
+ ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
+ } while (taskData.rc == (BLE_HS_ERR_HCI_BASE + BLE_ERR_PINKEY_MISSING) && retryCount--);
if(taskData.rc != 0){
return false;
}
return true;
-}
+} // secureConnection
/**
* @brief Disconnect from the peer.
- * @return N/A.
+ * @return Error code from NimBLE stack, 0 = success.
*/
int NimBLEClient::disconnect(uint8_t reason) {
NIMBLE_LOGD(LOG_TAG, ">> disconnect()");
@@ -264,6 +283,12 @@ int NimBLEClient::disconnect(uint8_t reason) {
/**
* @brief Set the connection paramaters to use when connecting to a server.
+ * @param [in] minInterval minimum connection interval in 0.625ms units.
+ * @param [in] maxInterval maximum connection interval in 0.625ms units.
+ * @param [in] latency number of packets allowed to skip (extends max interval)
+ * @param [in] timeout the timeout time in 10ms units before disconnecting
+ * @param [in] scanInterval the scan interval to use when attempting to connect in 0.625ms units.
+ * @param [in] scanWindow the scan window to use when attempting to connect in 0.625ms units.
*/
void NimBLEClient::setConnectionParams(uint16_t minInterval, uint16_t maxInterval,
uint16_t latency, uint16_t timeout,
@@ -271,12 +296,12 @@ void NimBLEClient::setConnectionParams(uint16_t minInterval, uint16_t maxInterva
uint16_t minConnTime, uint16_t maxConnTime)*/
{
- m_pConnParams.scan_itvl = scanInterval; // Scan interval in 0.625ms units
- m_pConnParams.scan_window = scanWindow; // Scan window in 0.625ms units
- m_pConnParams.itvl_min = minInterval; // min_int = 0x10*1.25ms = 20ms
- m_pConnParams.itvl_max = maxInterval; // max_int = 0x20*1.25ms = 40ms
- m_pConnParams.latency = latency; // number of packets allowed to skip (extends max interval)
- m_pConnParams.supervision_timeout = timeout; // timeout = 400*10ms = 4000ms
+ m_pConnParams.scan_itvl = scanInterval;
+ m_pConnParams.scan_window = scanWindow;
+ m_pConnParams.itvl_min = minInterval;
+ m_pConnParams.itvl_max = maxInterval;
+ m_pConnParams.latency = latency;
+ m_pConnParams.supervision_timeout = timeout;
// These are not used by NimBLE at this time - Must leave at defaults
//m_pConnParams->min_ce_len = minConnTime; // Minimum length of connection event in 0.625ms units
@@ -284,11 +309,16 @@ void NimBLEClient::setConnectionParams(uint16_t minInterval, uint16_t maxInterva
int rc = NimBLEUtils::checkConnParams(&m_pConnParams);
assert(rc == 0 && "Invalid Connection parameters");
-}
+} // setConnectionParams
/**
- * Update connection parameters can be called only after connection has been established
+ * @brief Update the connection parameters:
+ * * Can only be used after a connection has been established.
+ * @param [in] minInterval minimum connection interval in 0.625ms units.
+ * @param [in] maxInterval maximum connection interval in 0.625ms units.
+ * @param [in] latency number of packets allowed to skip (extends max interval)
+ * @param [in] timeout the timeout time in 10ms units before disconnecting
*/
void NimBLEClient::updateConnParams(uint16_t minInterval, uint16_t maxInterval,
uint16_t latency, uint16_t timeout)
@@ -308,16 +338,16 @@ void NimBLEClient::updateConnParams(uint16_t minInterval, uint16_t maxInterval,
NIMBLE_LOGE(LOG_TAG, "Update params error: %d, %s",
rc, NimBLEUtils::returnCodeToString(rc));
}
-}
+} // updateConnParams
/**
- * @brief Set the timeout to wait for connection attempt to complete
- * @params[in] Time to wait in seconds.
+ * @brief Set the timeout to wait for connection attempt to complete.
+ * @param [in] time The number of seconds before timeout.
*/
void NimBLEClient::setConnectTimeout(uint8_t time) {
m_connectTimeout = (uint32_t)(time * 1000);
-}
+} // setConnectTimeout
/**
@@ -334,7 +364,23 @@ uint16_t NimBLEClient::getConnId() {
*/
NimBLEAddress NimBLEClient::getPeerAddress() {
return m_peerAddress;
-} // getAddress
+} // getPeerAddress
+
+
+/**
+ * @brief Set the peer address.
+ * @param [in] address The address of the peer that this client is
+ * connected or should connect to.
+ */
+void NimBLEClient::setPeerAddress(const NimBLEAddress &address) {
+ if(isConnected()) {
+ NIMBLE_LOGE(LOG_TAG, "Cannot set peer address while connected");
+ return;
+ }
+
+ m_peerAddress = address;
+ NIMBLE_LOGD(LOG_TAG, "Peer address set: %s", std::string(m_peerAddress).c_str());
+} // setPeerAddress
/**
@@ -381,7 +427,7 @@ std::vector::iterator NimBLEClient::end() {
/**
* @brief Get the service BLE Remote Service instance corresponding to the uuid.
* @param [in] uuid The UUID of the service being sought.
- * @return A reference to the Service or nullptr if don't know about it.
+ * @return A pointer to the service or nullptr if not found.
*/
NimBLERemoteService* NimBLEClient::getService(const char* uuid) {
return getService(NimBLEUUID(uuid));
@@ -391,7 +437,7 @@ NimBLERemoteService* NimBLEClient::getService(const char* uuid) {
/**
* @brief Get the service object corresponding to the uuid.
* @param [in] uuid The UUID of the service being sought.
- * @return A reference to the Service or nullptr if don't know about it.
+ * @return A pointer to the service or nullptr if not found.
*/
NimBLERemoteService* NimBLEClient::getService(const NimBLEUUID &uuid) {
NIMBLE_LOGD(LOG_TAG, ">> getService: uuid: %s", uuid.toString().c_str());
@@ -416,12 +462,11 @@ NimBLERemoteService* NimBLEClient::getService(const NimBLEUUID &uuid) {
/**
- * @Get a pointer to the vector of found services.
- * @param [in] bool value to indicate if the current vector should be cleared and
- * subsequently all services retrieved from the peripheral.
- * If false the vector will be returned with the currently stored services,
- * If true it will retrieve all services from the peripheral and return the vector with all services
- * @return a pointer to the vector of available services.
+ * @brief Get a pointer to the vector of found services.
+ * @param [in] refresh If true the current services vector will be cleared and\n
+ * all services will be retrieved from the peripheral.\n
+ * If false the vector will be returned with the currently stored services.
+ * @return A pointer to the vector of available services.
*/
std::vector* NimBLEClient::getServices(bool refresh) {
if(refresh) {
@@ -435,11 +480,11 @@ std::vector* NimBLEClient::getServices(bool refresh) {
}
}
return &m_servicesVector;
-}
+} // getServices
/**
- * @ Retrieves the full database of attributes that the peripheral has available.
+ * @brief Retrieves the full database of attributes that the peripheral has available.
*/
void NimBLEClient::discoverAttributes() {
for(auto svc: *getServices(true)) {
@@ -447,14 +492,12 @@ void NimBLEClient::discoverAttributes() {
chr->getDescriptors(true);
}
}
-}
+} // discoverAttributes
/**
- * @brief Ask the remote %BLE server for its services.
- * A %BLE Server exposes a set of services for its partners. Here we ask the server for its set of
- * services and wait until we have received them all.
- * We then ask for the characteristics for each service found and their desciptors.
+ * @brief Ask the remote %BLE server for its services.\n
+ * Here we ask the server for its set of services and wait until we have received them all.
* @return true on success otherwise false if an error occurred
*/
bool NimBLEClient::retrieveServices(const NimBLEUUID *uuid_filter) {
@@ -502,7 +545,7 @@ bool NimBLEClient::retrieveServices(const NimBLEUUID *uuid_filter) {
/**
- * @brief STATIC Callback for the service discovery API function.
+ * @brief STATIC Callback for the service discovery API function.\n
* When a service is found or there is none left or there was an error
* the API will call this and report findings.
*/
@@ -574,6 +617,7 @@ std::string NimBLEClient::getValue(const NimBLEUUID &serviceUUID, const NimBLEUU
* @brief Set the value of a specific characteristic associated with a specific service.
* @param [in] serviceUUID The service that owns the characteristic.
* @param [in] characteristicUUID The characteristic whose value we wish to write.
+ * @param [in] value The value to write to the characteristic.
* @returns true if successful otherwise false
*/
bool NimBLEClient::setValue(const NimBLEUUID &serviceUUID, const NimBLEUUID &characteristicUUID,
@@ -600,17 +644,17 @@ bool NimBLEClient::setValue(const NimBLEUUID &serviceUUID, const NimBLEUUID &cha
/**
* @brief Get the current mtu of this connection.
+ * @returns The MTU value.
*/
uint16_t NimBLEClient::getMTU() {
return ble_att_mtu(m_conn_id);
-}
+} // getMTU
/**
* @brief Handle a received GAP event.
- *
- * @param [in] event
- * @param [in] arg = pointer to the client instance
+ * @param [in] event The event structure sent by the NimBLE stack.
+ * @param [in] arg A pointer to the client instance that registered for this callback.
*/
/*STATIC*/ int NimBLEClient::handleGapEvent(struct ble_gap_event *event, void *arg) {
NimBLEClient* client = (NimBLEClient*)arg;
@@ -783,12 +827,15 @@ uint16_t NimBLEClient::getMTU() {
return 0;
}
- if(event->enc_change.status == 0) {
+ if(event->enc_change.status == 0 || event->enc_change.status == (BLE_HS_ERR_HCI_BASE + BLE_ERR_PINKEY_MISSING)) {
struct ble_gap_conn_desc desc;
- rc = ble_gap_conn_find(event->conn_update.conn_handle, &desc);
+ rc = ble_gap_conn_find(event->enc_change.conn_handle, &desc);
assert(rc == 0);
- if(NimBLEDevice::m_securityCallbacks != nullptr) {
+ if (event->enc_change.status == (BLE_HS_ERR_HCI_BASE + BLE_ERR_PINKEY_MISSING)) {
+ // Key is missing, try deleting.
+ ble_store_util_delete_peer(&desc.peer_id_addr);
+ } else if(NimBLEDevice::m_securityCallbacks != nullptr) {
NimBLEDevice::m_securityCallbacks->onAuthenticationComplete(&desc);
} else {
client->m_pClientCallbacks->onAuthenticationComplete(&desc);
@@ -855,7 +902,7 @@ uint16_t NimBLEClient::getMTU() {
pkey.passkey = NimBLEDevice::m_securityCallbacks->onPassKeyRequest();
/////////////////////////////////////////////
} else {
- client->m_pClientCallbacks->onPassKeyRequest();
+ pkey.passkey = client->m_pClientCallbacks->onPassKeyRequest();
}
rc = ble_sm_inject_io(event->passkey.conn_handle, &pkey);
@@ -893,7 +940,9 @@ bool NimBLEClient::isConnected() {
/**
- * @brief Set the callbacks that will be invoked.
+ * @brief Set the callbacks that will be invoked when events are received.
+ * @param [in] pClientCallbacks A pointer to a class to receive the event callbacks.
+ * @param [in] deleteCallbacks If true this will delete the callback class sent when the client is destructed.
*/
void NimBLEClient::setClientCallbacks(NimBLEClientCallbacks* pClientCallbacks, bool deleteCallbacks) {
if (pClientCallbacks != nullptr){
@@ -938,7 +987,7 @@ uint32_t NimBLEClientCallbacks::onPassKeyRequest(){
NIMBLE_LOGD("NimBLEClientCallbacks", "onPassKeyRequest: default: 123456");
return 123456;
}
-
+/*
void NimBLEClientCallbacks::onPassKeyNotify(uint32_t pass_key){
NIMBLE_LOGD("NimBLEClientCallbacks", "onPassKeyNotify: default: %d", pass_key);
}
@@ -946,7 +995,7 @@ void NimBLEClientCallbacks::onPassKeyNotify(uint32_t pass_key){
bool NimBLEClientCallbacks::onSecurityRequest(){
NIMBLE_LOGD("NimBLEClientCallbacks", "onSecurityRequest: default: true");
return true;
-}
+}*/
void NimBLEClientCallbacks::onAuthenticationComplete(ble_gap_conn_desc* desc){
NIMBLE_LOGD("NimBLEClientCallbacks", "onAuthenticationComplete: default");
}
diff --git a/libesp32/NimBLE-Arduino/src/NimBLEClient.h b/libesp32/NimBLE-Arduino/src/NimBLEClient.h
index 888b42d02..ddeef3cc3 100644
--- a/libesp32/NimBLE-Arduino/src/NimBLEClient.h
+++ b/libesp32/NimBLE-Arduino/src/NimBLEClient.h
@@ -38,11 +38,12 @@ class NimBLEAdvertisedDevice;
*/
class NimBLEClient {
public:
- bool connect(NimBLEAdvertisedDevice* device, bool refreshServices = true);
- bool connect(const NimBLEAddress &address, uint8_t type = BLE_ADDR_PUBLIC,
- bool refreshServices = true);
+ bool connect(NimBLEAdvertisedDevice* device, bool deleteAttibutes = true);
+ bool connect(const NimBLEAddress &address, bool deleteAttibutes = true);
+ bool connect(bool deleteAttibutes = true);
int disconnect(uint8_t reason = BLE_ERR_REM_USER_CONN_TERM);
NimBLEAddress getPeerAddress();
+ void setPeerAddress(const NimBLEAddress &address);
int getRssi();
std::vector* getServices(bool refresh = false);
std::vector::iterator begin();
@@ -70,7 +71,7 @@ public:
void discoverAttributes();
private:
- NimBLEClient();
+ NimBLEClient(const NimBLEAddress &peerAddress);
~NimBLEClient();
friend class NimBLEDevice;
@@ -83,7 +84,7 @@ private:
void *arg);
bool retrieveServices(const NimBLEUUID *uuid_filter = nullptr);
- NimBLEAddress m_peerAddress = NimBLEAddress("");
+ NimBLEAddress m_peerAddress;
uint16_t m_conn_id;
bool m_isConnected;
bool m_waitingToConnect;
@@ -107,13 +108,48 @@ private:
class NimBLEClientCallbacks {
public:
virtual ~NimBLEClientCallbacks() {};
+
+ /**
+ * @brief Called after client connects.
+ * @param [in] pClient A pointer to the calling client object.
+ */
virtual void onConnect(NimBLEClient* pClient);
+
+ /**
+ * @brief Called when disconnected from the server.
+ * @param [in] pClient A pointer to the calling client object.
+ */
virtual void onDisconnect(NimBLEClient* pClient);
+
+ /**
+ * @brief Called when server requests to update the connection parameters.
+ * @param [in] pClient A pointer to the calling client object.
+ * @param [in] params A pointer to the struct containing the connection parameters requested.
+ * @return True to accept the parmeters.
+ */
virtual bool onConnParamsUpdateRequest(NimBLEClient* pClient, const ble_gap_upd_params* params);
+
+ /**
+ * @brief Called when server requests a passkey for pairing.
+ * @return The passkey to be sent to the server.
+ */
virtual uint32_t onPassKeyRequest();
- virtual void onPassKeyNotify(uint32_t pass_key);
- virtual bool onSecurityRequest();
+
+ /*virtual void onPassKeyNotify(uint32_t pass_key);
+ virtual bool onSecurityRequest();*/
+
+ /**
+ * @brief Called when the pairing procedure is complete.
+ * @param [in] desc A pointer to the struct containing the connection information.\n
+ * This can be used to check the status of the connection encryption/pairing.
+ */
virtual void onAuthenticationComplete(ble_gap_conn_desc* desc);
+
+ /**
+ * @brief Called when using numeric comparision for pairing.
+ * @param [in] pin The pin to compare with the server.
+ * @return True to accept the pin.
+ */
virtual bool onConfirmPIN(uint32_t pin);
};
diff --git a/libesp32/NimBLE-Arduino/src/NimBLEDescriptor.h b/libesp32/NimBLE-Arduino/src/NimBLEDescriptor.h
index f4978f570..16b6edceb 100644
--- a/libesp32/NimBLE-Arduino/src/NimBLEDescriptor.h
+++ b/libesp32/NimBLE-Arduino/src/NimBLEDescriptor.h
@@ -52,6 +52,10 @@ public:
void setValue(const std::string &value);
std::string toString();
+ /**
+ * @brief Convenience template to set the descriptor value to val.
+ * @param [in] s The value to set.
+ */
template
void setValue(const T &s) {
setValue((uint8_t*)&s, sizeof(T));
@@ -101,6 +105,8 @@ public:
virtual void onWrite(NimBLEDescriptor* pDescriptor);
};
+#include "NimBLE2904.h"
+
#endif // #if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
#endif /* CONFIG_BT_ENABLED */
#endif /* MAIN_NIMBLEDESCRIPTOR_H_ */
diff --git a/libesp32/NimBLE-Arduino/src/NimBLEDevice.cpp b/libesp32/NimBLE-Arduino/src/NimBLEDevice.cpp
index b3d882ba3..fb36e6e57 100644
--- a/libesp32/NimBLE-Arduino/src/NimBLEDevice.cpp
+++ b/libesp32/NimBLE-Arduino/src/NimBLEDevice.cpp
@@ -90,6 +90,10 @@ NimBLESecurityCallbacks* NimBLEDevice::m_securityCallbacks = nullptr;
#if defined(CONFIG_BT_NIMBLE_ROLE_BROADCASTER)
+/**
+ * @brief Get the instance of the advertising object.
+ * @return A pointer to the advertising object.
+ */
NimBLEAdvertising* NimBLEDevice::getAdvertising() {
if(m_bleAdvertising == nullptr) {
m_bleAdvertising = new NimBLEAdvertising();
@@ -98,11 +102,17 @@ NimBLEAdvertising* NimBLEDevice::getAdvertising() {
}
+/**
+ * @brief Convenience function to begin advertising.
+ */
void NimBLEDevice::startAdvertising() {
getAdvertising()->start();
} // startAdvertising
+/**
+ * @brief Convenience function to stop advertising.
+ */
void NimBLEDevice::stopAdvertising() {
getAdvertising()->stop();
} // stopAdvertising
@@ -123,19 +133,22 @@ void NimBLEDevice::stopAdvertising() {
} // getScan
#endif // #if defined(CONFIG_BT_NIMBLE_ROLE_OBSERVER)
+
/**
* @brief Creates a new client object and maintains a list of all client objects
* each client can connect to 1 peripheral device.
+ * @param [in] peerAddress An optional peer address that is copied to the new client
+ * object, allows for calling NimBLEClient::connect(bool) without a device or address parameter.
* @return A reference to the new client object.
*/
#if defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL)
-/* STATIC */ NimBLEClient* NimBLEDevice::createClient() {
+/* STATIC */ NimBLEClient* NimBLEDevice::createClient(NimBLEAddress peerAddress) {
if(m_cList.size() >= NIMBLE_MAX_CONNECTIONS) {
NIMBLE_LOGW("Number of clients exceeds Max connections. Max=(%d)",
NIMBLE_MAX_CONNECTIONS);
}
- NimBLEClient* pClient = new NimBLEClient();
+ NimBLEClient* pClient = new NimBLEClient(peerAddress);
m_cList.push_back(pClient);
return pClient;
@@ -143,9 +156,9 @@ void NimBLEDevice::stopAdvertising() {
/**
- * @brief Delete the client object and remove it from the list.
- * Check if it is connected or trying to connect and close/stop it first.
- * @param [in] Pointer to the client object.
+ * @brief Delete the client object and remove it from the list.\n
+ * Checks if it is connected or trying to connect and disconnects/stops it first.
+ * @param [in] pClient A pointer to the client object.
*/
/* STATIC */ bool NimBLEDevice::deleteClient(NimBLEClient* pClient) {
if(pClient == nullptr) {
@@ -183,8 +196,8 @@ void NimBLEDevice::stopAdvertising() {
/**
- * @brief get the list of clients.
- * @return a pointer to the list of clients.
+ * @brief Get the list of created client objects.
+ * @return A pointer to the list of clients.
*/
/* STATIC */std::list* NimBLEDevice::getClientList() {
return &m_cList;
@@ -192,8 +205,8 @@ void NimBLEDevice::stopAdvertising() {
/**
- * @brief get the size of the list of clients.
- * @return a pointer to the list of clients.
+ * @brief Get the number of created client objects.
+ * @return Number of client objects created.
*/
/* STATIC */size_t NimBLEDevice::getClientListSize() {
return m_cList.size();
@@ -202,8 +215,8 @@ void NimBLEDevice::stopAdvertising() {
/**
* @brief Get a reference to a client by connection ID.
- * @param [in] The client connection ID to search for.
- * @return A reference pointer to the client with the spcified connection ID.
+ * @param [in] conn_id The client connection ID to search for.
+ * @return A pointer to the client object with the spcified connection ID.
*/
/* STATIC */NimBLEClient* NimBLEDevice::getClientByID(uint16_t conn_id) {
for(auto it = m_cList.cbegin(); it != m_cList.cend(); ++it) {
@@ -218,8 +231,8 @@ void NimBLEDevice::stopAdvertising() {
/**
* @brief Get a reference to a client by peer address.
- * @param [in] a NimBLEAddress of the peer to search for.
- * @return A reference pointer to the client with the peer address.
+ * @param [in] peer_addr The address of the peer to search for.
+ * @return A pointer to the client object with the peer address.
*/
/* STATIC */NimBLEClient* NimBLEDevice::getClientByPeerAddress(const NimBLEAddress &peer_addr) {
for(auto it = m_cList.cbegin(); it != m_cList.cend(); ++it) {
@@ -233,7 +246,7 @@ void NimBLEDevice::stopAdvertising() {
/**
* @brief Finds the first disconnected client in the list.
- * @return A reference pointer to the first client that is not connected to a peer.
+ * @return A pointer to the first client object that is not connected to a peer.
*/
/* STATIC */NimBLEClient* NimBLEDevice::getDisconnectedClient() {
for(auto it = m_cList.cbegin(); it != m_cList.cend(); ++it) {
@@ -249,16 +262,28 @@ void NimBLEDevice::stopAdvertising() {
/**
* @brief Set the transmission power.
- * The power level can be one of:
- * * ESP_PWR_LVL_N12 = 0, !< Corresponding to -12dbm
- * * ESP_PWR_LVL_N9 = 1, !< Corresponding to -9dbm
- * * ESP_PWR_LVL_N6 = 2, !< Corresponding to -6dbm
- * * ESP_PWR_LVL_N3 = 3, !< Corresponding to -3dbm
- * * ESP_PWR_LVL_N0 = 4, !< Corresponding to 0dbm
- * * ESP_PWR_LVL_P3 = 5, !< Corresponding to +3dbm
- * * ESP_PWR_LVL_P6 = 6, !< Corresponding to +6dbm
- * * ESP_PWR_LVL_P9 = 7, !< Corresponding to +9dbm
- * @param [in] powerLevel.
+ * @param [in] powerLevel The power level to set, can be one of:
+ * * ESP_PWR_LVL_N12 = 0, Corresponding to -12dbm
+ * * ESP_PWR_LVL_N9 = 1, Corresponding to -9dbm
+ * * ESP_PWR_LVL_N6 = 2, Corresponding to -6dbm
+ * * ESP_PWR_LVL_N3 = 3, Corresponding to -3dbm
+ * * ESP_PWR_LVL_N0 = 4, Corresponding to 0dbm
+ * * ESP_PWR_LVL_P3 = 5, Corresponding to +3dbm
+ * * ESP_PWR_LVL_P6 = 6, Corresponding to +6dbm
+ * * ESP_PWR_LVL_P9 = 7, Corresponding to +9dbm
+ * @param [in] powerType The BLE function to set the power level for, can be one of:
+ * * ESP_BLE_PWR_TYPE_CONN_HDL0 = 0, For connection handle 0
+ * * ESP_BLE_PWR_TYPE_CONN_HDL1 = 1, For connection handle 1
+ * * ESP_BLE_PWR_TYPE_CONN_HDL2 = 2, For connection handle 2
+ * * ESP_BLE_PWR_TYPE_CONN_HDL3 = 3, For connection handle 3
+ * * ESP_BLE_PWR_TYPE_CONN_HDL4 = 4, For connection handle 4
+ * * ESP_BLE_PWR_TYPE_CONN_HDL5 = 5, For connection handle 5
+ * * ESP_BLE_PWR_TYPE_CONN_HDL6 = 6, For connection handle 6
+ * * ESP_BLE_PWR_TYPE_CONN_HDL7 = 7, For connection handle 7
+ * * ESP_BLE_PWR_TYPE_CONN_HDL8 = 8, For connection handle 8
+ * * ESP_BLE_PWR_TYPE_ADV = 9, For advertising
+ * * ESP_BLE_PWR_TYPE_SCAN = 10, For scan
+ * * ESP_BLE_PWR_TYPE_DEFAULT = 11, For default, if not set other, it will use default value
*/
/* STATIC */ void NimBLEDevice::setPower(esp_power_level_t powerLevel, esp_ble_power_type_t powerType) {
NIMBLE_LOGD(LOG_TAG, ">> setPower: %d (type: %d)", powerLevel, powerType);
@@ -270,6 +295,24 @@ void NimBLEDevice::stopAdvertising() {
} // setPower
+/**
+ * @brief Set the transmission power.
+ * @param [in] powerType The power level to set, can be one of:
+ * * ESP_BLE_PWR_TYPE_CONN_HDL0 = 0, For connection handle 0
+ * * ESP_BLE_PWR_TYPE_CONN_HDL1 = 1, For connection handle 1
+ * * ESP_BLE_PWR_TYPE_CONN_HDL2 = 2, For connection handle 2
+ * * ESP_BLE_PWR_TYPE_CONN_HDL3 = 3, For connection handle 3
+ * * ESP_BLE_PWR_TYPE_CONN_HDL4 = 4, For connection handle 4
+ * * ESP_BLE_PWR_TYPE_CONN_HDL5 = 5, For connection handle 5
+ * * ESP_BLE_PWR_TYPE_CONN_HDL6 = 6, For connection handle 6
+ * * ESP_BLE_PWR_TYPE_CONN_HDL7 = 7, For connection handle 7
+ * * ESP_BLE_PWR_TYPE_CONN_HDL8 = 8, For connection handle 8
+ * * ESP_BLE_PWR_TYPE_ADV = 9, For advertising
+ * * ESP_BLE_PWR_TYPE_SCAN = 10, For scan
+ * * ESP_BLE_PWR_TYPE_DEFAULT = 11, For default, if not set other, it will use default value
+ * @return the power level currently used by the type specified.
+ */
+
/* STATIC */ int NimBLEDevice::getPower(esp_ble_power_type_t powerType) {
switch(esp_ble_tx_power_get(powerType)) {
@@ -302,7 +345,6 @@ void NimBLEDevice::stopAdvertising() {
*/
/* STATIC*/ NimBLEAddress NimBLEDevice::getAddress() {
ble_addr_t addr = {BLE_ADDR_PUBLIC, 0};
- //ble_hs_id_copy_addr(BLE_ADDR_PUBLIC, addr.val, NULL)
if(BLE_HS_ENOADDR == ble_hs_id_copy_addr(BLE_ADDR_PUBLIC, addr.val, NULL)) {
NIMBLE_LOGD(LOG_TAG, "Public address not found, checking random");
@@ -324,9 +366,9 @@ void NimBLEDevice::stopAdvertising() {
/**
- * @brief Setup local mtu that will be used to negotiate mtu during request from client peer
- * @param [in] mtu Value to set local mtu, should be larger than 23 and lower or equal to
- * BLE_ATT_MTU_MAX = 527
+ * @brief Setup local mtu that will be used to negotiate mtu during request from client peer.
+ * @param [in] mtu Value to set local mtu:
+ * * This should be larger than 23 and lower or equal to BLE_ATT_MTU_MAX = 527.
*/
/* STATIC */int NimBLEDevice::setMTU(uint16_t mtu) {
NIMBLE_LOGD(LOG_TAG, ">> setLocalMTU: %d", mtu);
@@ -344,6 +386,7 @@ void NimBLEDevice::stopAdvertising() {
/**
* @brief Get local MTU value set.
+ * @return The current preferred MTU setting.
*/
/* STATIC */uint16_t NimBLEDevice::getMTU() {
return ble_att_preferred_mtu();
@@ -352,6 +395,7 @@ void NimBLEDevice::stopAdvertising() {
/**
* @brief Host reset, we pass the message so we don't make calls until resynced.
+ * @param [in] reason The reason code for the reset.
*/
/* STATIC */ void NimBLEDevice::onReset(int reason)
{
@@ -389,7 +433,7 @@ void NimBLEDevice::stopAdvertising() {
/**
- * @brief Host resynced with controller, all clear to make calls.
+ * @brief Host resynced with controller, all clear to make calls to the stack.
*/
/* STATIC */ void NimBLEDevice::onSync(void)
{
@@ -439,7 +483,7 @@ void NimBLEDevice::stopAdvertising() {
/**
* @brief Initialize the %BLE environment.
- * @param deviceName The device name of the device.
+ * @param [in] deviceName The device name of the device.
*/
/* STATIC */ void NimBLEDevice::init(const std::string &deviceName) {
if(!initialized){
@@ -497,8 +541,10 @@ void NimBLEDevice::stopAdvertising() {
/**
* @brief Shutdown the NimBLE stack/controller.
+ * @param [in] clearAll If true, deletes all server/advertising/scan/client objects after deinitializing.
+ * @note If clearAll is true when called, any references to the created objects become invalid.
*/
-/* STATIC */ void NimBLEDevice::deinit() {
+/* STATIC */ void NimBLEDevice::deinit(bool clearAll) {
int ret = nimble_port_stop();
if (ret == 0) {
nimble_port_deinit();
@@ -510,12 +556,49 @@ void NimBLEDevice::stopAdvertising() {
initialized = false;
m_synced = false;
+
+ if(clearAll) {
+#if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
+ if(NimBLEDevice::m_pServer != nullptr) {
+ delete NimBLEDevice::m_pServer;
+ NimBLEDevice::m_pServer = nullptr;
+ }
+#endif
+
+#if defined(CONFIG_BT_NIMBLE_ROLE_BROADCASTER)
+ if(NimBLEDevice::m_bleAdvertising != nullptr) {
+ delete NimBLEDevice::m_bleAdvertising;
+ NimBLEDevice::m_bleAdvertising = nullptr;
+ }
+#endif
+
+#if defined(CONFIG_BT_NIMBLE_ROLE_OBSERVER)
+ if(NimBLEDevice::m_pScan != nullptr) {
+ delete NimBLEDevice::m_pScan;
+ NimBLEDevice::m_pScan= nullptr;
+ }
+#endif
+
+#if defined( CONFIG_BT_NIMBLE_ROLE_CENTRAL)
+ for(auto &it : m_cList) {
+ deleteClient(it);
+ m_cList.clear();
+ }
+#endif
+
+ m_ignoreList.clear();
+
+ if(m_securityCallbacks != nullptr) {
+ delete m_securityCallbacks;
+ }
+ }
}
} // deinit
/**
* @brief Check if the initialization is complete.
+ * @return true if initialized.
*/
bool NimBLEDevice::getInitialized() {
return initialized;
@@ -524,9 +607,9 @@ bool NimBLEDevice::getInitialized() {
/**
* @brief Set the authorization mode for this device.
- * @param bonding, if true we allow bonding, false no bonding will be performed.
- * @param mitm, if true we are capable of man in the middle protection, false if not.
- * @param sc, if true we will perform secure connection pairing, false we will use legacy pairing.
+ * @param bonding If true we allow bonding, false no bonding will be performed.
+ * @param mitm If true we are capable of man in the middle protection, false if not.
+ * @param sc If true we will perform secure connection pairing, false we will use legacy pairing.
*/
/*STATIC*/ void NimBLEDevice::setSecurityAuth(bool bonding, bool mitm, bool sc) {
NIMBLE_LOGD(LOG_TAG, "Setting bonding: %d, mitm: %d, sc: %d",bonding,mitm,sc);
@@ -538,13 +621,12 @@ bool NimBLEDevice::getInitialized() {
/**
* @brief Set the authorization mode for this device.
- * @param A bitmap indicating what modes are supported.
- * The bits are defined as follows:
- ** 0x01 BLE_SM_PAIR_AUTHREQ_BOND
- ** 0x04 BLE_SM_PAIR_AUTHREQ_MITM
- ** 0x08 BLE_SM_PAIR_AUTHREQ_SC
- ** 0x10 BLE_SM_PAIR_AUTHREQ_KEYPRESS - not yet supported.
- ** 0xe2 BLE_SM_PAIR_AUTHREQ_RESERVED - for reference only.
+ * @param auth_req A bitmap indicating what modes are supported.\n
+ * The available bits are defined as:
+ * * 0x01 BLE_SM_PAIR_AUTHREQ_BOND
+ * * 0x04 BLE_SM_PAIR_AUTHREQ_MITM
+ * * 0x08 BLE_SM_PAIR_AUTHREQ_SC
+ * * 0x10 BLE_SM_PAIR_AUTHREQ_KEYPRESS - not yet supported.
*/
/*STATIC*/void NimBLEDevice::setSecurityAuth(uint8_t auth_req) {
NimBLEDevice::setSecurityAuth((auth_req & BLE_SM_PAIR_AUTHREQ_BOND)>0,
@@ -555,12 +637,12 @@ bool NimBLEDevice::getInitialized() {
/**
* @brief Set the Input/Output capabilities of this device.
- * @param One of the following:
- ** 0x00 BLE_HS_IO_DISPLAY_ONLY DisplayOnly IO capability
- ** 0x01 BLE_HS_IO_DISPLAY_YESNO DisplayYesNo IO capability
- ** 0x02 BLE_HS_IO_KEYBOARD_ONLY KeyboardOnly IO capability
- ** 0x03 BLE_HS_IO_NO_INPUT_OUTPUT NoInputNoOutput IO capability
- ** 0x04 BLE_HS_IO_KEYBOARD_DISPLAY KeyboardDisplay Only IO capability
+ * @param iocap One of the following values:
+ * * 0x00 BLE_HS_IO_DISPLAY_ONLY DisplayOnly IO capability
+ * * 0x01 BLE_HS_IO_DISPLAY_YESNO DisplayYesNo IO capability
+ * * 0x02 BLE_HS_IO_KEYBOARD_ONLY KeyboardOnly IO capability
+ * * 0x03 BLE_HS_IO_NO_INPUT_OUTPUT NoInputNoOutput IO capability
+ * * 0x04 BLE_HS_IO_KEYBOARD_DISPLAY KeyboardDisplay Only IO capability
*/
/*STATIC*/ void NimBLEDevice::setSecurityIOCap(uint8_t iocap) {
ble_hs_cfg.sm_io_cap = iocap;
@@ -569,12 +651,12 @@ bool NimBLEDevice::getInitialized() {
/**
* @brief If we are the initiator of the security procedure this sets the keys we will distribute.
- * @param A bitmap indicating which keys to distribute during pairing.
- * The bits are defined as follows:
- ** 0x01: BLE_SM_PAIR_KEY_DIST_ENC - Distribute the encryption key.
- ** 0x02: BLE_SM_PAIR_KEY_DIST_ID - Distribute the ID key (IRK).
- ** 0x04: BLE_SM_PAIR_KEY_DIST_SIGN
- ** 0x08: BLE_SM_PAIR_KEY_DIST_LINK
+ * @param init_key A bitmap indicating which keys to distribute during pairing.\n
+ * The available bits are defined as:
+ * * 0x01: BLE_SM_PAIR_KEY_DIST_ENC - Distribute the encryption key.
+ * * 0x02: BLE_SM_PAIR_KEY_DIST_ID - Distribute the ID key (IRK).
+ * * 0x04: BLE_SM_PAIR_KEY_DIST_SIGN
+ * * 0x08: BLE_SM_PAIR_KEY_DIST_LINK
*/
/*STATIC*/void NimBLEDevice::setSecurityInitKey(uint8_t init_key) {
ble_hs_cfg.sm_our_key_dist = init_key;
@@ -583,20 +665,21 @@ bool NimBLEDevice::getInitialized() {
/**
* @brief Set the keys we are willing to accept during pairing.
- * @param A bitmap indicating which keys to accept during pairing.
- * The bits are defined as follows:
- ** 0x01: BLE_SM_PAIR_KEY_DIST_ENC - Accept the encryption key.
- ** 0x02: BLE_SM_PAIR_KEY_DIST_ID - Accept the ID key (IRK).
- ** 0x04: BLE_SM_PAIR_KEY_DIST_SIGN
- ** 0x08: BLE_SM_PAIR_KEY_DIST_LINK
+ * @param resp_key A bitmap indicating which keys to accept during pairing.
+ * The available bits are defined as:
+ * * 0x01: BLE_SM_PAIR_KEY_DIST_ENC - Accept the encryption key.
+ * * 0x02: BLE_SM_PAIR_KEY_DIST_ID - Accept the ID key (IRK).
+ * * 0x04: BLE_SM_PAIR_KEY_DIST_SIGN
+ * * 0x08: BLE_SM_PAIR_KEY_DIST_LINK
*/
-/*STATIC*/void NimBLEDevice::setSecurityRespKey(uint8_t init_key) {
- ble_hs_cfg.sm_their_key_dist = init_key;
+/*STATIC*/void NimBLEDevice::setSecurityRespKey(uint8_t resp_key) {
+ ble_hs_cfg.sm_their_key_dist = resp_key;
} // setsSecurityRespKey
/**
- * @brief Set the passkey for pairing.
+ * @brief Set the passkey the server will ask for when pairing.
+ * @param [in] pin The passkey to use.
*/
/*STATIC*/void NimBLEDevice::setSecurityPasskey(uint32_t pin) {
m_passkey = pin;
@@ -604,7 +687,8 @@ bool NimBLEDevice::getInitialized() {
/**
- * @brief Get the passkey for pairing.
+ * @brief Get the current passkey used for pairing.
+ * @return The current passkey.
*/
/*STATIC*/uint32_t NimBLEDevice::getSecurityPasskey() {
return m_passkey;
@@ -613,7 +697,8 @@ bool NimBLEDevice::getInitialized() {
/**
* @brief Set callbacks that will be used to handle encryption negotiation events and authentication events
- * @param [in] cllbacks Pointer to NimBLESecurityCallbacks class
+ * @param [in] callbacks Pointer to NimBLESecurityCallbacks class
+ * @deprecated For backward compatibility, New code should use client/server callback methods.
*/
void NimBLEDevice::setSecurityCallbacks(NimBLESecurityCallbacks* callbacks) {
NimBLEDevice::m_securityCallbacks = callbacks;
@@ -622,8 +707,8 @@ void NimBLEDevice::setSecurityCallbacks(NimBLESecurityCallbacks* callbacks) {
/**
* @brief Start the connection securing and authorization for this connection.
- * @param Connection id of the client.
- * @returns host return code 0 if success.
+ * @param conn_id The connection id of the peer device.
+ * @returns NimBLE stack return code, 0 = success.
*/
/* STATIC */int NimBLEDevice::startSecurity(uint16_t conn_id) {
/* if(m_securityCallbacks != nullptr) {
@@ -641,6 +726,7 @@ void NimBLEDevice::setSecurityCallbacks(NimBLESecurityCallbacks* callbacks) {
/**
* @brief Check if the device address is on our ignore list.
+ * @param [in] address The address to look for.
* @return True if ignoring.
*/
/*STATIC*/ bool NimBLEDevice::isIgnored(const NimBLEAddress &address) {
@@ -656,7 +742,7 @@ void NimBLEDevice::setSecurityCallbacks(NimBLESecurityCallbacks* callbacks) {
/**
* @brief Add a device to the ignore list.
- * @param Address of the device we want to ignore.
+ * @param [in] address The address of the device we want to ignore.
*/
/*STATIC*/ void NimBLEDevice::addIgnored(const NimBLEAddress &address) {
m_ignoreList.push_back(address);
@@ -665,7 +751,7 @@ void NimBLEDevice::setSecurityCallbacks(NimBLESecurityCallbacks* callbacks) {
/**
* @brief Remove a device from the ignore list.
- * @param Address of the device we want to remove from the list.
+ * @param [in] address The address of the device we want to remove from the list.
*/
/*STATIC*/void NimBLEDevice::removeIgnored(const NimBLEAddress &address) {
for(auto it = m_ignoreList.begin(); it != m_ignoreList.end(); ++it) {
@@ -679,6 +765,7 @@ void NimBLEDevice::setSecurityCallbacks(NimBLESecurityCallbacks* callbacks) {
/**
* @brief Set a custom callback for gap events.
+ * @param [in] handler The function to call when gap events occur.
*/
void NimBLEDevice::setCustomGapHandler(gap_event_handler handler) {
m_customGapHandler = handler;
diff --git a/libesp32/NimBLE-Arduino/src/NimBLEDevice.h b/libesp32/NimBLE-Arduino/src/NimBLEDevice.h
index 412647987..252c52afd 100644
--- a/libesp32/NimBLE-Arduino/src/NimBLEDevice.h
+++ b/libesp32/NimBLE-Arduino/src/NimBLEDevice.h
@@ -81,17 +81,17 @@
#define NIMBLE_MAX_CONNECTIONS CONFIG_NIMBLE_MAX_CONNECTIONS
#endif
-/**
- * @brief BLE functions.
- */
- typedef int (*gap_event_handler)(ble_gap_event *event, void *arg);
+typedef int (*gap_event_handler)(ble_gap_event *event, void *arg);
extern "C" void ble_store_config_init(void);
+/**
+ * @brief A model of a %BLE Device from which all the BLE roles are created.
+ */
class NimBLEDevice {
public:
static void init(const std::string &deviceName);
- static void deinit();
+ static void deinit(bool clearAll = false);
static bool getInitialized();
static NimBLEAddress getAddress();
static std::string toString();
@@ -130,7 +130,7 @@ public:
#endif
#if defined( CONFIG_BT_NIMBLE_ROLE_CENTRAL)
- static NimBLEClient* createClient();
+ static NimBLEClient* createClient(NimBLEAddress peerAddress = NimBLEAddress(""));
static bool deleteClient(NimBLEClient* pClient);
static NimBLEClient* getClientByID(uint16_t conn_id);
static NimBLEClient* getClientByPeerAddress(const NimBLEAddress &peer_addr);
@@ -181,8 +181,6 @@ private:
static NimBLESecurityCallbacks* m_securityCallbacks;
static uint32_t m_passkey;
static ble_gap_event_listener m_listener;
-
-public:
static gap_event_handler m_customGapHandler;
};
diff --git a/libesp32/NimBLE-Arduino/src/NimBLEEddystoneTLM.cpp b/libesp32/NimBLE-Arduino/src/NimBLEEddystoneTLM.cpp
index 990a36f52..a07294265 100644
--- a/libesp32/NimBLE-Arduino/src/NimBLEEddystoneTLM.cpp
+++ b/libesp32/NimBLE-Arduino/src/NimBLEEddystoneTLM.cpp
@@ -24,7 +24,9 @@
static const char LOG_TAG[] = "NimBLEEddystoneTLM";
-
+/**
+ * @brief Construct a default EddystoneTLM beacon object.
+ */
NimBLEEddystoneTLM::NimBLEEddystoneTLM() {
beaconUUID = 0xFEAA;
m_eddystoneData.frameType = EDDYSTONE_TLM_FRAME_TYPE;
@@ -35,34 +37,73 @@ NimBLEEddystoneTLM::NimBLEEddystoneTLM() {
m_eddystoneData.tmil = 0;
} // NimBLEEddystoneTLM
+
+/**
+ * @brief Retrieve the data that is being advertised.
+ * @return The advertised data.
+ */
std::string NimBLEEddystoneTLM::getData() {
return std::string((char*) &m_eddystoneData, sizeof(m_eddystoneData));
} // getData
+
+/**
+ * @brief Get the UUID being advertised.
+ * @return The UUID advertised.
+ */
NimBLEUUID NimBLEEddystoneTLM::getUUID() {
return NimBLEUUID(beaconUUID);
} // getUUID
+
+/**
+ * @brief Get the version being advertised.
+ * @return The version number.
+ */
uint8_t NimBLEEddystoneTLM::getVersion() {
return m_eddystoneData.version;
} // getVersion
+
+/**
+ * @brief Get the battery voltage.
+ * @return The battery voltage.
+ */
uint16_t NimBLEEddystoneTLM::getVolt() {
return ENDIAN_CHANGE_U16(m_eddystoneData.volt);
} // getVolt
+
+/**
+ * @brief Get the temperature being advertised.
+ * @return The temperature value.
+ */
float NimBLEEddystoneTLM::getTemp() {
return ENDIAN_CHANGE_U16(m_eddystoneData.temp) / 256.0f;
} // getTemp
+/**
+ * @brief Get the count of advertisments sent.
+ * @return The number of advertisments.
+ */
uint32_t NimBLEEddystoneTLM::getCount() {
return ENDIAN_CHANGE_U32(m_eddystoneData.advCount);
} // getCount
+
+/**
+ * @brief Get the advertisment time.
+ * @return The advertisment time.
+ */
uint32_t NimBLEEddystoneTLM::getTime() {
return (ENDIAN_CHANGE_U32(m_eddystoneData.tmil)) / 10;
} // getTime
+
+/**
+ * @brief Get a string representation of the beacon.
+ * @return The string representation.
+ */
std::string NimBLEEddystoneTLM::toString() {
std::string out = "";
uint32_t rawsec = ENDIAN_CHANGE_U32(m_eddystoneData.tmil);
@@ -113,8 +154,10 @@ std::string NimBLEEddystoneTLM::toString() {
return out;
} // toString
+
/**
- * Set the raw data for the beacon record.
+ * @brief Set the raw data for the beacon advertisment.
+ * @param [in] data The raw data to advertise.
*/
void NimBLEEddystoneTLM::setData(const std::string &data) {
if (data.length() != sizeof(m_eddystoneData)) {
@@ -125,26 +168,56 @@ void NimBLEEddystoneTLM::setData(const std::string &data) {
memcpy(&m_eddystoneData, data.data(), data.length());
} // setData
+
+/**
+ * @brief Set the UUID to advertise.
+ * @param [in] l_uuid The UUID.
+ */
void NimBLEEddystoneTLM::setUUID(const NimBLEUUID &l_uuid) {
beaconUUID = l_uuid.getNative()->u16.value;
} // setUUID
+
+/**
+ * @brief Set the version to advertise.
+ * @param [in] version The version number.
+ */
void NimBLEEddystoneTLM::setVersion(uint8_t version) {
m_eddystoneData.version = version;
} // setVersion
+
+/**
+ * @brief Set the battery voltage to advertise.
+ * @param [in] volt The voltage in millivolts.
+ */
void NimBLEEddystoneTLM::setVolt(uint16_t volt) {
m_eddystoneData.volt = volt;
} // setVolt
+
+/**
+ * @brief Set the temperature to advertise.
+ * @param [in] temp The temperature value.
+ */
void NimBLEEddystoneTLM::setTemp(float temp) {
m_eddystoneData.temp = (uint16_t)temp;
} // setTemp
+
+/**
+ * @brief Set the advertisment count.
+ * @param [in] advCount The advertisment number.
+ */
void NimBLEEddystoneTLM::setCount(uint32_t advCount) {
m_eddystoneData.advCount = advCount;
} // setCount
+
+/**
+ * @brief Set the advertisment time.
+ * @param [in] tmil The advertisment time in milliseconds.
+ */
void NimBLEEddystoneTLM::setTime(uint32_t tmil) {
m_eddystoneData.tmil = tmil;
} // setTime
diff --git a/libesp32/NimBLE-Arduino/src/NimBLEEddystoneURL.cpp b/libesp32/NimBLE-Arduino/src/NimBLEEddystoneURL.cpp
index ce7975958..7c3194c28 100644
--- a/libesp32/NimBLE-Arduino/src/NimBLEEddystoneURL.cpp
+++ b/libesp32/NimBLE-Arduino/src/NimBLEEddystoneURL.cpp
@@ -21,6 +21,10 @@
static const char LOG_TAG[] = "NimBLEEddystoneURL";
+
+/**
+ * @brief Construct a default EddystoneURL beacon object.
+ */
NimBLEEddystoneURL::NimBLEEddystoneURL() {
beaconUUID = 0xFEAA;
lengthURL = 0;
@@ -29,22 +33,47 @@ NimBLEEddystoneURL::NimBLEEddystoneURL() {
memset(m_eddystoneData.url, 0, sizeof(m_eddystoneData.url));
} // BLEEddystoneURL
+
+/**
+ * @brief Retrieve the data that is being advertised.
+ * @return The advertised data.
+ */
std::string NimBLEEddystoneURL::getData() {
return std::string((char*) &m_eddystoneData, sizeof(m_eddystoneData));
} // getData
+
+/**
+ * @brief Get the UUID being advertised.
+ * @return The UUID advertised.
+ */
NimBLEUUID NimBLEEddystoneURL::getUUID() {
return NimBLEUUID(beaconUUID);
} // getUUID
+
+/**
+ * @brief Get the transmit power being advertised.
+ * @return The transmit power.
+ */
int8_t NimBLEEddystoneURL::getPower() {
return m_eddystoneData.advertisedTxPower;
} // getPower
+
+/**
+ * @brief Get the raw URL being advertised.
+ * @return The raw URL.
+ */
std::string NimBLEEddystoneURL::getURL() {
return std::string((char*) &m_eddystoneData.url, sizeof(m_eddystoneData.url));
} // getURL
+
+/**
+ * @brief Get the full URL being advertised.
+ * @return The full URL.
+ */
std::string NimBLEEddystoneURL::getDecodedURL() {
std::string decodedURL = "";
@@ -123,7 +152,8 @@ std::string NimBLEEddystoneURL::getDecodedURL() {
/**
- * Set the raw data for the beacon record.
+ * @brief Set the raw data for the beacon advertisment.
+ * @param [in] data The raw data to advertise.
*/
void NimBLEEddystoneURL::setData(const std::string &data) {
if (data.length() > sizeof(m_eddystoneData)) {
@@ -136,14 +166,29 @@ void NimBLEEddystoneURL::setData(const std::string &data) {
lengthURL = data.length() - (sizeof(m_eddystoneData) - sizeof(m_eddystoneData.url));
} // setData
+
+/**
+ * @brief Set the UUID to advertise.
+ * @param [in] l_uuid The UUID.
+ */
void NimBLEEddystoneURL::setUUID(const NimBLEUUID &l_uuid) {
beaconUUID = l_uuid.getNative()->u16.value;
} // setUUID
+
+/**
+ * @brief Set the transmit power to advertise.
+ * @param [in] advertisedTxPower The transmit power level.
+ */
void NimBLEEddystoneURL::setPower(int8_t advertisedTxPower) {
m_eddystoneData.advertisedTxPower = advertisedTxPower;
} // setPower
+
+/**
+ * @brief Set the URL to advertise.
+ * @param [in] url The URL.
+ */
void NimBLEEddystoneURL::setURL(const std::string &url) {
if (url.length() > sizeof(m_eddystoneData.url)) {
NIMBLE_LOGE(LOG_TAG, "Unable to set the url ... length passed in was %d and max expected %d",
diff --git a/libesp32/NimBLE-Arduino/src/NimBLERemoteCharacteristic.cpp b/libesp32/NimBLE-Arduino/src/NimBLERemoteCharacteristic.cpp
index 23089ca77..154206c73 100644
--- a/libesp32/NimBLE-Arduino/src/NimBLERemoteCharacteristic.cpp
+++ b/libesp32/NimBLE-Arduino/src/NimBLERemoteCharacteristic.cpp
@@ -260,13 +260,12 @@ NimBLERemoteDescriptor* NimBLERemoteCharacteristic::getDescriptor(const NimBLEUU
/**
- * @Get a pointer to the vector of found descriptors.
- * @param [in] bool value to indicate if the current vector should be cleared and
- * subsequently all descriptors for this characteristic retrieved from the peripheral.
- * If false the vector will be returned with the currently stored descriptors,
- * if the vector is empty it will retrieve all descriptors for this characteristic
- * from the peripheral.
- * @return a pointer to the vector of descriptors for this characteristic.
+ * @brief Get a pointer to the vector of found descriptors.
+ * @param [in] refresh If true the current descriptor vector will be cleared and\n
+ * all descriptors for this characteristic retrieved from the peripheral.\n
+ * If false the vector will be returned with the currently stored descriptors
+ * of this characteristic.
+ * @return A pointer to the vector of descriptors for this characteristic.
*/
std::vector* NimBLERemoteCharacteristic::getDescriptors(bool refresh) {
if(refresh) {
@@ -338,6 +337,7 @@ NimBLEUUID NimBLERemoteCharacteristic::getUUID() {
/**
* @brief Get the value of the remote characteristic.
+ * @param [in] timestamp A pointer to a time_t struct to store the time the value was read.
* @return The value of the remote characteristic.
*/
std::string NimBLERemoteCharacteristic::getValue(time_t *timestamp) {
@@ -355,6 +355,7 @@ std::string NimBLERemoteCharacteristic::getValue(time_t *timestamp) {
/**
* @brief Read an unsigned 16 bit value
* @return The unsigned 16 bit value.
+ * @deprecated Use readValue().
*/
uint16_t NimBLERemoteCharacteristic::readUInt16() {
return readValue();
@@ -364,6 +365,7 @@ uint16_t NimBLERemoteCharacteristic::readUInt16() {
/**
* @brief Read an unsigned 32 bit value.
* @return the unsigned 32 bit value.
+ * @deprecated Use readValue().
*/
uint32_t NimBLERemoteCharacteristic::readUInt32() {
return readValue();
@@ -373,6 +375,7 @@ uint32_t NimBLERemoteCharacteristic::readUInt32() {
/**
* @brief Read a byte value
* @return The value as a byte
+ * @deprecated Use readValue().
*/
uint8_t NimBLERemoteCharacteristic::readUInt8() {
return readValue();
@@ -390,6 +393,7 @@ float NimBLERemoteCharacteristic::readFloat() {
/**
* @brief Read the value of the remote characteristic.
+ * @param [in] timestamp A pointer to a time_t struct to store the time the value was read.
* @return The value of the remote characteristic.
*/
std::string NimBLERemoteCharacteristic::readValue(time_t *timestamp) {
@@ -458,7 +462,7 @@ std::string NimBLERemoteCharacteristic::readValue(time_t *timestamp) {
/**
* @brief Callback for characteristic read operation.
- * @return 0 or error code.
+ * @return success == 0 or error code.
*/
int NimBLERemoteCharacteristic::onReadCB(uint16_t conn_handle,
const struct ble_gatt_error *error,
@@ -498,12 +502,13 @@ int NimBLERemoteCharacteristic::onReadCB(uint16_t conn_handle,
/**
* @brief Subscribe or unsubscribe for notifications or indications.
- * @param [in] uint16_t val 0x00 to unsubscribe, 0x01 for notifications, 0x02 for indications.
- * @param [in] notifyCallback A callback to be invoked for a notification. If NULL is provided then no callback
- * is performed for notifications.
+ * @param [in] val 0x00 to unsubscribe, 0x01 for notifications, 0x02 for indications.
+ * @param [in] notifyCallback A callback to be invoked for a notification.
+ * @param [in] response If write response required set this to true.
+ * If NULL is provided then no callback is performed.
* @return true if successful.
*/
-bool NimBLERemoteCharacteristic::setNotify(uint16_t val, bool response, notify_callback notifyCallback) {
+bool NimBLERemoteCharacteristic::setNotify(uint16_t val, notify_callback notifyCallback, bool response) {
NIMBLE_LOGD(LOG_TAG, ">> setNotify(): %s, %02x", toString().c_str(), val);
NimBLERemoteDescriptor* desc = getDescriptor(NimBLEUUID((uint16_t)0x2902));
@@ -522,43 +527,44 @@ bool NimBLERemoteCharacteristic::setNotify(uint16_t val, bool response, notify_c
/**
* @brief Subscribe for notifications or indications.
- * @param [in] bool if true, subscribe for notifications, false subscribe for indications.
- * @param [in] bool if true, require a write response from the descriptor write operation.
- * @param [in] notifyCallback A callback to be invoked for a notification. If NULL is provided then no callback
- * is performed for notifications.
+ * @param [in] notifications If true, subscribe for notifications, false subscribe for indications.
+ * @param [in] notifyCallback A callback to be invoked for a notification.
+ * @param [in] response If true, require a write response from the descriptor write operation.
+ * If NULL is provided then no callback is performed.
* @return true if successful.
*/
-bool NimBLERemoteCharacteristic::subscribe(bool notifications, bool response, notify_callback notifyCallback) {
+bool NimBLERemoteCharacteristic::subscribe(bool notifications, notify_callback notifyCallback, bool response) {
if(notifications) {
- return setNotify(0x01, response, notifyCallback);
+ return setNotify(0x01, notifyCallback, response);
} else {
- return setNotify(0x02, response, notifyCallback);
+ return setNotify(0x02, notifyCallback, response);
}
} // subscribe
/**
* @brief Unsubscribe for notifications or indications.
- * @param [in] bool if true, require a write response from the descriptor write operation.
+ * @param [in] response bool if true, require a write response from the descriptor write operation.
* @return true if successful.
*/
bool NimBLERemoteCharacteristic::unsubscribe(bool response) {
- return setNotify(0x00, response);
+ return setNotify(0x00, nullptr, response);
} // unsubscribe
/**
* @brief backward-compatibility method for subscribe/unsubscribe notifications/indications
- * @param [in] notifyCallback A callback to be invoked for a notification. If NULL is provided then we are
- * unregistering for notifications.
- * @param [in] bool if true, register for notifications, false register for indications.
- * @param [in] bool if true, require a write response from the descriptor write operation.
+ * @param [in] notifyCallback A callback to be invoked for a notification. If NULL is provided then we
+ * will unregister for notifications.
+ * @param [in] notifications If true, register for notifications, false register for indications.
+ * @param [in] response If true, require a write response from the descriptor write operation.
* @return true if successful.
+ * @deprecated Use subscribe() / unsubscribe() instead.
*/
bool NimBLERemoteCharacteristic::registerForNotify(notify_callback notifyCallback, bool notifications, bool response) {
bool success;
if(notifyCallback != nullptr) {
- success = subscribe(notifications, response, notifyCallback);
+ success = subscribe(notifications, notifyCallback, response);
} else {
success = unsubscribe(response);
}
@@ -568,10 +574,9 @@ bool NimBLERemoteCharacteristic::registerForNotify(notify_callback notifyCallbac
/**
* @brief Delete the descriptors in the descriptor vector.
- * We maintain a vector called m_descriptorVector that contains pointers to BLERemoteDescriptors
+ * @details We maintain a vector called m_descriptorVector that contains pointers to NimBLERemoteDescriptors
* object references. Since we allocated these in this class, we are also responsible for deleting
* them. This method does just that.
- * @return N/A.
*/
void NimBLERemoteCharacteristic::deleteDescriptors() {
NIMBLE_LOGD(LOG_TAG, ">> deleteDescriptors");
@@ -587,7 +592,7 @@ void NimBLERemoteCharacteristic::deleteDescriptors() {
/**
* @brief Delete descriptor by UUID
* @param [in] uuid The UUID of the descriptor to be deleted.
- * @return Number of services left.
+ * @return Number of descriptors left in the vector.
*/
size_t NimBLERemoteCharacteristic::deleteDescriptor(const NimBLEUUID &uuid) {
NIMBLE_LOGD(LOG_TAG, ">> deleteDescriptor");
@@ -607,7 +612,7 @@ size_t NimBLERemoteCharacteristic::deleteDescriptor(const NimBLEUUID &uuid) {
/**
- * @brief Convert a BLERemoteCharacteristic to a string representation;
+ * @brief Convert a NimBLERemoteCharacteristic to a string representation;
* @return a String representation.
*/
std::string NimBLERemoteCharacteristic::toString() {
@@ -725,7 +730,7 @@ bool NimBLERemoteCharacteristic::writeValue(const uint8_t* data, size_t length,
/**
* @brief Callback for characteristic write operation.
- * @return 0 or error code.
+ * @return success == 0 or error code.
*/
int NimBLERemoteCharacteristic::onWriteCB(uint16_t conn_handle,
const struct ble_gatt_error *error,
diff --git a/libesp32/NimBLE-Arduino/src/NimBLERemoteCharacteristic.h b/libesp32/NimBLE-Arduino/src/NimBLERemoteCharacteristic.h
index 364efb59d..720a4da29 100644
--- a/libesp32/NimBLE-Arduino/src/NimBLERemoteCharacteristic.h
+++ b/libesp32/NimBLE-Arduino/src/NimBLERemoteCharacteristic.h
@@ -24,13 +24,14 @@
#include "NimBLERemoteDescriptor.h"
#include
+#include
class NimBLERemoteService;
class NimBLERemoteDescriptor;
-typedef void (*notify_callback)(NimBLERemoteCharacteristic* pBLERemoteCharacteristic,
- uint8_t* pData, size_t length, bool isNotify);
+typedef std::function notify_callback;
typedef struct {
const NimBLEUUID *uuid;
@@ -63,6 +64,15 @@ public:
NimBLEUUID getUUID();
std::string readValue(time_t *timestamp = nullptr);
+ /**
+ * @brief A template to convert the remote characteristic data to .
+ * @tparam T The type to convert the data to.
+ * @param [in] timestamp A pointer to a time_t struct to store the time the value was read.
+ * @param [in] skipSizeCheck If true it will skip checking if the data size is less than sizeof().
+ * @return The data converted to or NULL if skipSizeCheck is false and the data is
+ * less than sizeof().
+ * @details Use: readValue(×tamp, skipSizeCheck);
+ */
template
T readValue(time_t *timestamp = nullptr, bool skipSizeCheck = false) {
std::string value = readValue(timestamp);
@@ -77,6 +87,15 @@ public:
float readFloat() __attribute__ ((deprecated("Use template readValue()")));
std::string getValue(time_t *timestamp = nullptr);
+ /**
+ * @brief A template to convert the remote characteristic data to .
+ * @tparam T The type to convert the data to.
+ * @param [in] timestamp A pointer to a time_t struct to store the time the value was read.
+ * @param [in] skipSizeCheck If true it will skip checking if the data size is less than sizeof().
+ * @return The data converted to or NULL if skipSizeCheck is false and the data is
+ * less than sizeof().
+ * @details Use: getValue(×tamp, skipSizeCheck);
+ */
template
T getValue(time_t *timestamp = nullptr, bool skipSizeCheck = false) {
std::string value = getValue(timestamp);
@@ -86,9 +105,9 @@ public:
}
bool subscribe(bool notifications = true,
- bool response = true,
- notify_callback notifyCallback = nullptr);
- bool unsubscribe(bool response = true);
+ notify_callback notifyCallback = nullptr,
+ bool response = false);
+ bool unsubscribe(bool response = false);
bool registerForNotify(notify_callback notifyCallback,
bool notifications = true,
bool response = true)
@@ -98,6 +117,11 @@ public:
bool response = false);
bool writeValue(const std::string &newValue,
bool response = false);
+ /**
+ * @brief Convenience template to set the remote characteristic value to val.
+ * @param [in] s The value to write.
+ * @param [in] response True == request write response.
+ */
template
bool writeValue(const T &s, bool response = false) {
return writeValue((uint8_t*)&s, sizeof(T), response);
@@ -115,7 +139,7 @@ private:
friend class NimBLERemoteDescriptor;
// Private member functions
- bool setNotify(uint16_t val, bool response = true, notify_callback notifyCallback = nullptr);
+ bool setNotify(uint16_t val, notify_callback notifyCallback = nullptr, bool response = true);
bool retrieveDescriptors(const NimBLEUUID *uuid_filter = nullptr);
static int onReadCB(uint16_t conn_handle, const struct ble_gatt_error *error,
struct ble_gatt_attr *attr, void *arg);
diff --git a/libesp32/NimBLE-Arduino/src/NimBLERemoteDescriptor.cpp b/libesp32/NimBLE-Arduino/src/NimBLERemoteDescriptor.cpp
index d17ae6a0e..9281e7df7 100644
--- a/libesp32/NimBLE-Arduino/src/NimBLERemoteDescriptor.cpp
+++ b/libesp32/NimBLE-Arduino/src/NimBLERemoteDescriptor.cpp
@@ -25,8 +25,8 @@ static const char* LOG_TAG = "NimBLERemoteDescriptor";
/**
* @brief Remote descriptor constructor.
- * @param [in] Reference to the Characteristic that this belongs to.
- * @param [in] Reference to the struct that contains the descriptor information.
+ * @param [in] pRemoteCharacteristic A pointer to the Characteristic that this belongs to.
+ * @param [in] dsc A pointer to the struct that contains the descriptor information.
*/
NimBLERemoteDescriptor::NimBLERemoteDescriptor(NimBLERemoteCharacteristic* pRemoteCharacteristic,
const struct ble_gatt_dsc *dsc)
@@ -78,6 +78,11 @@ NimBLEUUID NimBLERemoteDescriptor::getUUID() {
} // getUUID
+/**
+ * @brief Read a byte value
+ * @return The value as a byte
+ * @deprecated Use readValue().
+ */
uint8_t NimBLERemoteDescriptor::readUInt8() {
std::string value = readValue();
if (value.length() >= 1) {
@@ -87,6 +92,11 @@ uint8_t NimBLERemoteDescriptor::readUInt8() {
} // readUInt8
+/**
+ * @brief Read an unsigned 16 bit value
+ * @return The unsigned 16 bit value.
+ * @deprecated Use readValue().
+ */
uint16_t NimBLERemoteDescriptor::readUInt16() {
std::string value = readValue();
if (value.length() >= 2) {
@@ -96,6 +106,11 @@ uint16_t NimBLERemoteDescriptor::readUInt16() {
} // readUInt16
+/**
+ * @brief Read an unsigned 32 bit value.
+ * @return the unsigned 32 bit value.
+ * @deprecated Use readValue().
+ */
uint32_t NimBLERemoteDescriptor::readUInt32() {
std::string value = readValue();
if (value.length() >= 4) {
@@ -105,6 +120,10 @@ uint32_t NimBLERemoteDescriptor::readUInt32() {
} // readUInt32
+/**
+ * @brief Read the value of the remote descriptor.
+ * @return The value of the remote descriptor.
+ */
std::string NimBLERemoteDescriptor::readValue() {
NIMBLE_LOGD(LOG_TAG, ">> Descriptor readValue: %s", toString().c_str());
@@ -161,7 +180,7 @@ std::string NimBLERemoteDescriptor::readValue() {
/**
* @brief Callback for Descriptor read operation.
- * @return 0 or error code.
+ * @return success == 0 or error code.
*/
int NimBLERemoteDescriptor::onReadCB(uint16_t conn_handle,
const struct ble_gatt_error *error,
@@ -200,8 +219,8 @@ int NimBLERemoteDescriptor::onReadCB(uint16_t conn_handle,
/**
- * @brief Return a string representation of this BLE Remote Descriptor.
- * @retun A string representation of this BLE Remote Descriptor.
+ * @brief Return a string representation of this Remote Descriptor.
+ * @return A string representation of this Remote Descriptor.
*/
std::string NimBLERemoteDescriptor::toString() {
std::string res = "Descriptor: uuid: " + getUUID().toString();
@@ -216,7 +235,7 @@ std::string NimBLERemoteDescriptor::toString() {
/**
* @brief Callback for descriptor write operation.
- * @return 0 or error code.
+ * @return success == 0 or error code.
*/
int NimBLERemoteDescriptor::onWriteCB(uint16_t conn_handle,
const struct ble_gatt_error *error,
@@ -242,7 +261,8 @@ int NimBLERemoteDescriptor::onWriteCB(uint16_t conn_handle,
* @brief Write data to the BLE Remote Descriptor.
* @param [in] data The data to send to the remote descriptor.
* @param [in] length The length of the data to send.
- * @param [in] response True if we expect a response.
+ * @param [in] response True if we expect a write response.
+ * @return True if successful
*/
bool NimBLERemoteDescriptor::writeValue(const uint8_t* data, size_t length, bool response) {
@@ -322,11 +342,11 @@ bool NimBLERemoteDescriptor::writeValue(const uint8_t* data, size_t length, bool
* @brief Write data represented as a string to the BLE Remote Descriptor.
* @param [in] newValue The data to send to the remote descriptor.
* @param [in] response True if we expect a response.
+ * @return True if successful
*/
bool NimBLERemoteDescriptor::writeValue(const std::string &newValue, bool response) {
return writeValue((uint8_t*) newValue.data(), newValue.length(), response);
} // writeValue
-
#endif // #if defined( CONFIG_BT_NIMBLE_ROLE_CENTRAL)
#endif /* CONFIG_BT_ENABLED */
diff --git a/libesp32/NimBLE-Arduino/src/NimBLERemoteDescriptor.h b/libesp32/NimBLE-Arduino/src/NimBLERemoteDescriptor.h
index 554e9dc16..b52738ef7 100644
--- a/libesp32/NimBLE-Arduino/src/NimBLERemoteDescriptor.h
+++ b/libesp32/NimBLE-Arduino/src/NimBLERemoteDescriptor.h
@@ -33,6 +33,14 @@ public:
NimBLEUUID getUUID();
std::string readValue();
+ /**
+ * @brief A template to convert the remote descriptor data to .
+ * @tparam T The type to convert the data to.
+ * @param [in] skipSizeCheck If true it will skip checking if the data size is less than sizeof().
+ * @return The data converted to or NULL if skipSizeCheck is false and the data is
+ * less than sizeof().
+ * @details Use: readValue(skipSizeCheck);
+ */
template
T readValue(bool skipSizeCheck = false) {
std::string value = readValue();
@@ -41,12 +49,18 @@ public:
return *((T *)pData);
}
- uint8_t readUInt8() __attribute__ ((deprecated));
- uint16_t readUInt16() __attribute__ ((deprecated));
- uint32_t readUInt32() __attribute__ ((deprecated));
+ uint8_t readUInt8() __attribute__ ((deprecated("Use template readValue()")));
+ uint16_t readUInt16() __attribute__ ((deprecated("Use template readValue()")));
+ uint32_t readUInt32() __attribute__ ((deprecated("Use template readValue()")));
std::string toString(void);
bool writeValue(const uint8_t* data, size_t length, bool response = false);
bool writeValue(const std::string &newValue, bool response = false);
+
+ /**
+ * @brief Convenience template to set the remote descriptor value to val.
+ * @param [in] s The value to write.
+ * @param [in] response True == request write response.
+ */
template
bool writeValue(const T &s, bool response = false) {
return writeValue((uint8_t*)&s, sizeof(T), response);
diff --git a/libesp32/NimBLE-Arduino/src/NimBLERemoteService.cpp b/libesp32/NimBLE-Arduino/src/NimBLERemoteService.cpp
index db9eabca1..8901175dc 100644
--- a/libesp32/NimBLE-Arduino/src/NimBLERemoteService.cpp
+++ b/libesp32/NimBLE-Arduino/src/NimBLERemoteService.cpp
@@ -26,8 +26,8 @@ static const char* LOG_TAG = "NimBLERemoteService";
/**
* @brief Remote Service constructor.
- * @param [in] Reference to the client this belongs to.
- * @param [in] Refernce to the structure with the services' information.
+ * @param [in] pClient A pointer to the client this belongs to.
+ * @param [in] service A pointer to the structure with the service information.
*/
NimBLERemoteService::NimBLERemoteService(NimBLEClient* pClient, const struct ble_gatt_svc* service) {
@@ -55,7 +55,6 @@ NimBLERemoteService::NimBLERemoteService(NimBLEClient* pClient, const struct ble
/**
* @brief When deleting the service make sure we delete all characteristics and descriptors.
- * Also release any semaphores they may be holding.
*/
NimBLERemoteService::~NimBLERemoteService() {
deleteCharacteristics();
@@ -83,7 +82,7 @@ std::vector::iterator NimBLERemoteService::end() {
/**
* @brief Get the remote characteristic object for the characteristic UUID.
* @param [in] uuid Remote characteristic uuid.
- * @return Reference to the remote characteristic object.
+ * @return A pointer to the remote characteristic object.
*/
NimBLERemoteCharacteristic* NimBLERemoteService::getCharacteristic(const char* uuid) {
return getCharacteristic(NimBLEUUID(uuid));
@@ -93,7 +92,7 @@ NimBLERemoteCharacteristic* NimBLERemoteService::getCharacteristic(const char* u
/**
* @brief Get the characteristic object for the UUID.
* @param [in] uuid Characteristic uuid.
- * @return Reference to the characteristic object, or nullptr if not found.
+ * @return A pointer to the characteristic object, or nullptr if not found.
*/
NimBLERemoteCharacteristic* NimBLERemoteService::getCharacteristic(const NimBLEUUID &uuid) {
for(auto &it: m_characteristicVector) {
@@ -114,15 +113,12 @@ NimBLERemoteCharacteristic* NimBLERemoteService::getCharacteristic(const NimBLEU
/**
- * @Get a pointer to the vector of found characteristics.
- * @param [in] bool value to indicate if the current vector should be cleared and
- * subsequently all characteristics for this service retrieved from the peripheral.
- * If false the vector will be returned with the currently stored characteristics,
- * If true it will retrieve all characteristics of this service from the peripheral
- * and return the vector with all characteristics for this service.
- * @return a pointer to the vector of descriptors for this characteristic.
+ * @brief Get a pointer to the vector of found characteristics.
+ * @param [in] refresh If true the current characteristics vector will cleared and
+ * all characteristics for this service retrieved from the peripheral.
+ * If false the vector will be returned with the currently stored characteristics of this service.
+ * @return A pointer to the vector of descriptors for this characteristic.
*/
-
std::vector* NimBLERemoteService::getCharacteristics(bool refresh) {
if(refresh) {
deleteCharacteristics();
@@ -140,6 +136,7 @@ std::vector* NimBLERemoteService::getCharacteristic
/**
* @brief Callback for Characterisic discovery.
+ * @return success == 0 or error code.
*/
int NimBLERemoteService::characteristicDiscCB(uint16_t conn_handle,
const struct ble_gatt_error *error,
@@ -182,7 +179,7 @@ int NimBLERemoteService::characteristicDiscCB(uint16_t conn_handle,
/**
* @brief Retrieve all the characteristics for this service.
* This function will not return until we have all the characteristics.
- * @return N/A
+ * @return True if successful.
*/
bool NimBLERemoteService::retrieveCharacteristics(const NimBLEUUID *uuid_filter) {
NIMBLE_LOGD(LOG_TAG, ">> retrieveCharacteristics() for service: %s", getUUID().toString().c_str());
@@ -299,10 +296,9 @@ bool NimBLERemoteService::setValue(const NimBLEUUID &characteristicUuid, const s
/**
* @brief Delete the characteristics in the characteristics vector.
- * We maintain a vector called m_characteristicsVector that contains pointers to BLERemoteCharacteristic
+ * @details We maintain a vector called m_characteristicsVector that contains pointers to BLERemoteCharacteristic
* object references. Since we allocated these in this class, we are also responsible for deleting
* them. This method does just that.
- * @return N/A.
*/
void NimBLERemoteService::deleteCharacteristics() {
NIMBLE_LOGD(LOG_TAG, ">> deleteCharacteristics");
@@ -316,7 +312,7 @@ void NimBLERemoteService::deleteCharacteristics() {
/**
* @brief Delete characteristic by UUID
- * @param [in] uuid The UUID of the characteristic to be cleared.
+ * @param [in] uuid The UUID of the characteristic to be removed from the local database.
* @return Number of characteristics left.
*/
size_t NimBLERemoteService::deleteCharacteristic(const NimBLEUUID &uuid) {
diff --git a/libesp32/NimBLE-Arduino/src/NimBLERemoteService.h b/libesp32/NimBLE-Arduino/src/NimBLERemoteService.h
index 2d63c5d70..751c9effb 100644
--- a/libesp32/NimBLE-Arduino/src/NimBLERemoteService.h
+++ b/libesp32/NimBLE-Arduino/src/NimBLERemoteService.h
@@ -45,7 +45,7 @@ public:
void deleteCharacteristics();
size_t deleteCharacteristic(const NimBLEUUID &uuid);
NimBLEClient* getClient(void);
- uint16_t getHandle();
+ //uint16_t getHandle();
NimBLEUUID getUUID(void);
std::string getValue(const NimBLEUUID &characteristicUuid);
bool setValue(const NimBLEUUID &characteristicUuid,
diff --git a/libesp32/NimBLE-Arduino/src/NimBLEScan.cpp b/libesp32/NimBLE-Arduino/src/NimBLEScan.cpp
index dbf88741d..dc82de549 100644
--- a/libesp32/NimBLE-Arduino/src/NimBLEScan.cpp
+++ b/libesp32/NimBLE-Arduino/src/NimBLEScan.cpp
@@ -44,6 +44,13 @@ NimBLEScan::NimBLEScan() {
}
+/**
+ * @brief Scan destructor, release any allocated resources.
+ */
+NimBLEScan::~NimBLEScan() {
+ clearResults();
+}
+
/**
* @brief Handle GAP events related to scans.
* @param [in] event The event type for this event.
@@ -52,8 +59,6 @@ NimBLEScan::NimBLEScan() {
/*STATIC*/int NimBLEScan::handleGapEvent(ble_gap_event* event, void* arg) {
NimBLEScan* pScan = (NimBLEScan*)arg;
- struct ble_hs_adv_fields fields;
- int rc = 0;
switch(event->type) {
@@ -63,13 +68,6 @@ NimBLEScan::NimBLEScan() {
return 0;
}
- rc = ble_hs_adv_parse_fields(&fields, event->disc.data,
- event->disc.length_data);
- if (rc != 0) {
- NIMBLE_LOGE(LOG_TAG, "Gap Event Parse ERROR.");
- return 0;
- }
-
NimBLEAddress advertisedAddress(event->disc.addr);
// Examine our list of ignored addresses and stop processing if we don't want to see it or are already connected
@@ -92,7 +90,6 @@ NimBLEScan::NimBLEScan() {
// Otherwise just update the relevant parameters of the already known device.
if(advertisedDevice == nullptr){
advertisedDevice = new NimBLEAdvertisedDevice();
- advertisedDevice->setAddressType(event->disc.addr.type);
advertisedDevice->setAddress(advertisedAddress);
advertisedDevice->setAdvType(event->disc.event_type);
pScan->m_scanResults.m_advertisedDevicesVector.push_back(advertisedDevice);
@@ -102,9 +99,9 @@ NimBLEScan::NimBLEScan() {
NIMBLE_LOGI(LOG_TAG, "UPDATING PREVIOUSLY FOUND DEVICE: %s", advertisedAddress.toString().c_str());
}
advertisedDevice->setRSSI(event->disc.rssi);
- advertisedDevice->parseAdvertisement(&fields);
- advertisedDevice->setScan(pScan);
- advertisedDevice->setAdvertisementResult(event->disc.data, event->disc.length_data);
+ if(event->disc.length_data > 0) {
+ advertisedDevice->parseAdvertisement(event->disc.data, event->disc.length_data);
+ }
advertisedDevice->m_timestamp = time(nullptr);
if (pScan->m_pAdvertisedDeviceCallbacks) {
@@ -151,7 +148,6 @@ NimBLEScan::NimBLEScan() {
* @brief Should we perform an active or passive scan?
* The default is a passive scan. An active scan means that we will wish a scan response.
* @param [in] active If true, we perform an active scan otherwise a passive scan.
- * @return N/A.
*/
void NimBLEScan::setActiveScan(bool active) {
if (active) {
@@ -165,40 +161,40 @@ void NimBLEScan::setActiveScan(bool active) {
/**
* @brief Set whether or not the BLE controller should only report results
* from devices it has not already seen.
- * @param [in] active If true, scanned devices will only be reported once.
+ * @param [in] enabled If true, scanned devices will only be reported once.
* @details The controller has a limited buffer and will start reporting
* dupicate devices once the limit is reached.
*/
-void NimBLEScan::setDuplicateFilter(bool active) {
- m_scan_params.filter_duplicates = active;
+void NimBLEScan::setDuplicateFilter(bool enabled) {
+ m_scan_params.filter_duplicates = enabled;
} // setDuplicateFilter
/**
* @brief Set whether or not the BLE controller only report scan results
* from devices advertising in limited discovery mode, i.e. directed advertising.
- * @param [in] active If true, only limited discovery devices will be in scan results.
+ * @param [in] enabled If true, only limited discovery devices will be in scan results.
*/
-void NimBLEScan::setLimitedOnly(bool active) {
- m_scan_params.limited = active;
+void NimBLEScan::setLimitedOnly(bool enabled) {
+ m_scan_params.limited = enabled;
} // setLimited
/**
* @brief Sets the scan filter policy.
* @param [in] filter Can be one of:
- * BLE_HCI_SCAN_FILT_NO_WL (0)
- * Scanner processes all advertising packets (white list not used) except
+ * * BLE_HCI_SCAN_FILT_NO_WL (0)
+ * Scanner processes all advertising packets (white list not used) except\n
* directed, connectable advertising packets not sent to the scanner.
- * BLE_HCI_SCAN_FILT_USE_WL (1)
- * Scanner processes advertisements from white list only. A connectable,
+ * * BLE_HCI_SCAN_FILT_USE_WL (1)
+ * Scanner processes advertisements from white list only. A connectable,\n
* directed advertisment is ignored unless it contains scanners address.
- * BLE_HCI_SCAN_FILT_NO_WL_INITA (2)
- * Scanner process all advertising packets (white list not used). A
+ * * BLE_HCI_SCAN_FILT_NO_WL_INITA (2)
+ * Scanner process all advertising packets (white list not used). A\n
* connectable, directed advertisement shall not be ignored if the InitA
* is a resolvable private address.
- * BLE_HCI_SCAN_FILT_USE_WL_INITA (3)
- * Scanner process advertisements from white list only. A connectable,
+ * * BLE_HCI_SCAN_FILT_USE_WL_INITA (3)
+ * Scanner process advertisements from white list only. A connectable,\n
* directed advertisement shall not be ignored if the InitA is a
* resolvable private address.
*/
@@ -221,7 +217,7 @@ void NimBLEScan::setAdvertisedDeviceCallbacks(NimBLEAdvertisedDeviceCallbacks* p
/**
* @brief Set the interval to scan.
- * @param [in] The interval in msecs.
+ * @param [in] intervalMSecs The scan interval (how often) in milliseconds.
*/
void NimBLEScan::setInterval(uint16_t intervalMSecs) {
m_scan_params.itvl = intervalMSecs / 0.625;
@@ -237,11 +233,20 @@ void NimBLEScan::setWindow(uint16_t windowMSecs) {
} // setWindow
+/**
+ * @brief Get the status of the scanner.
+ * @return true if scanning or scan starting.
+ */
+bool NimBLEScan::isScanning() {
+ return !m_stopped;
+}
+
+
/**
* @brief Start scanning.
* @param [in] duration The duration in seconds for which to scan.
* @param [in] scanCompleteCB A function to be called when scanning has completed.
- * @param [in] are we continue scan (true) or we want to clear stored devices (false)
+ * @param [in] is_continue Set to true to save previous scan results, false to clear them.
* @return True if scan started or false if there was an error.
*/
bool NimBLEScan::start(uint32_t duration, void (*scanCompleteCB)(NimBLEScanResults), bool is_continue) {
@@ -309,7 +314,8 @@ bool NimBLEScan::start(uint32_t duration, void (*scanCompleteCB)(NimBLEScanResul
/**
* @brief Start scanning and block until scanning has been completed.
* @param [in] duration The duration in seconds for which to scan.
- * @return The BLEScanResults.
+ * @param [in] is_continue Set to true to save previous scan results, false to clear them.
+ * @return The NimBLEScanResults.
*/
NimBLEScanResults NimBLEScan::start(uint32_t duration, bool is_continue) {
if(duration == 0) {
@@ -330,7 +336,7 @@ NimBLEScanResults NimBLEScan::start(uint32_t duration, bool is_continue) {
/**
* @brief Stop an in progress scan.
- * @return N/A.
+ * @return True if successful.
*/
bool NimBLEScan::stop() {
NIMBLE_LOGD(LOG_TAG, ">> stop()");
@@ -343,7 +349,7 @@ bool NimBLEScan::stop() {
m_stopped = true;
- if (m_scanCompleteCB != nullptr) {
+ if (rc != BLE_HS_EALREADY && m_scanCompleteCB != nullptr) {
m_scanCompleteCB(m_scanResults);
}
@@ -356,7 +362,11 @@ bool NimBLEScan::stop() {
} // stop
-// delete peer device from cache after disconnecting, it is required in case we are connecting to devices with not public address
+/**
+ * @brief Delete peer device from the scan results vector.
+ * @param [in] address The address of the device to delete from the results.
+ * @details After disconnecting, it may be required in the case we were connected to a device without a public address.
+ */
void NimBLEScan::erase(const NimBLEAddress &address) {
NIMBLE_LOGI(LOG_TAG, "erase device: %s", address.toString().c_str());
@@ -371,8 +381,7 @@ void NimBLEScan::erase(const NimBLEAddress &address) {
/**
- * @brief If the host reset the scan will have stopped so we should flag it and release the semaphore.
- * @return N/A.
+ * @brief If the host reset the scan will have stopped so we should set the flag as stopped.
*/
void NimBLEScan::onHostReset() {
m_stopped = true;
@@ -411,7 +420,7 @@ void NimBLEScanResults::dump() {
/**
- * @brief Return the count of devices found in the last scan.
+ * @brief Get the count of devices found in the last scan.
* @return The number of devices found in the last scan.
*/
int NimBLEScanResults::getCount() {
@@ -449,7 +458,7 @@ std::vector::iterator NimBLEScanResults::end() {
/**
- * @brief Return a pointer to the specified device at the given address.
+ * @brief Get a pointer to the specified device at the given address.
* If the address is not found a nullptr is returned.
* @param [in] address The address of the device.
* @return A pointer to the device at the specified address.
diff --git a/libesp32/NimBLE-Arduino/src/NimBLEScan.h b/libesp32/NimBLE-Arduino/src/NimBLEScan.h
index 5bc7bcf35..822629025 100644
--- a/libesp32/NimBLE-Arduino/src/NimBLEScan.h
+++ b/libesp32/NimBLE-Arduino/src/NimBLEScan.h
@@ -33,9 +33,9 @@ class NimBLEAdvertisedDeviceCallbacks;
class NimBLEAddress;
/**
- * @brief The result of having performed a scan.
- * When a scan completes, we have a set of found devices. Each device is described
- * by a BLEAdvertisedDevice object. The number of items in the set is given by
+ * @brief A class that contains and operates on the results of a BLE scan.
+ * @details When a scan completes, we have a set of found devices. Each device is described
+ * by a NimBLEAdvertisedDevice object. The number of items in the set is given by
* getCount(). We can retrieve a device by calling getDevice() passing in the
* index (starting at 0) of the desired device.
*/
@@ -62,12 +62,13 @@ class NimBLEScan {
public:
bool start(uint32_t duration, void (*scanCompleteCB)(NimBLEScanResults), bool is_continue = false);
NimBLEScanResults start(uint32_t duration, bool is_continue = false);
+ bool isScanning();
void setAdvertisedDeviceCallbacks(NimBLEAdvertisedDeviceCallbacks* pAdvertisedDeviceCallbacks, bool wantDuplicates = false);
void setActiveScan(bool active);
void setInterval(uint16_t intervalMSecs);
void setWindow(uint16_t windowMSecs);
- void setDuplicateFilter(bool active);
- void setLimitedOnly(bool active);
+ void setDuplicateFilter(bool enabled);
+ void setLimitedOnly(bool enabled);
void setFilterPolicy(uint8_t filter);
bool stop();
void clearResults();
@@ -76,8 +77,10 @@ public:
private:
- NimBLEScan();
friend class NimBLEDevice;
+
+ NimBLEScan();
+ ~NimBLEScan();
static int handleGapEvent(ble_gap_event* event, void* arg);
void onHostReset();
diff --git a/libesp32/NimBLE-Arduino/src/NimBLESecurity.cpp b/libesp32/NimBLE-Arduino/src/NimBLESecurity.cpp
index 8a0dbd952..aa0629698 100644
--- a/libesp32/NimBLE-Arduino/src/NimBLESecurity.cpp
+++ b/libesp32/NimBLE-Arduino/src/NimBLESecurity.cpp
@@ -18,12 +18,6 @@
#include "NimBLESecurity.h"
#include "NimBLEDevice.h"
-/**
- * @brief This class is for backward compatibility with the bluedroid based library.
- * Use the new security functions in NimBLEDevice instead.
- * New callback functions in NimBLEServer and NimBLEClient.
- */
-
NimBLESecurity::NimBLESecurity() {
}
@@ -33,6 +27,15 @@ NimBLESecurity::~NimBLESecurity() {
/**
* @brief Set requested authentication mode
+ * @param [in] auth_req A bitmask containing one or more of:
+ * * ESP_LE_AUTH_NO_BOND 0x00
+ * * ESP_LE_AUTH_BOND 0x01
+ * * ESP_LE_AUTH_REQ_MITM (1 << 2)
+ * * ESP_LE_AUTH_REQ_BOND_MITM (ESP_LE_AUTH_BOND | ESP_LE_AUTH_REQ_MITM)
+ * * ESP_LE_AUTH_REQ_SC_ONLY (1 << 3)
+ * * ESP_LE_AUTH_REQ_SC_BOND (ESP_LE_AUTH_BOND | ESP_LE_AUTH_REQ_SC_ONLY)
+ * * ESP_LE_AUTH_REQ_SC_MITM (ESP_LE_AUTH_REQ_MITM | ESP_LE_AUTH_REQ_SC_ONLY)
+ * * ESP_LE_AUTH_REQ_SC_MITM_BOND (ESP_LE_AUTH_REQ_MITM | ESP_LE_AUTH_REQ_SC_ONLY | ESP_LE_AUTH_BOND)
*/
void NimBLESecurity::setAuthenticationMode(esp_ble_auth_req_t auth_req) {
NimBLEDevice::setSecurityAuth((auth_req & BLE_SM_PAIR_AUTHREQ_BOND)>0,
@@ -42,8 +45,15 @@ void NimBLESecurity::setAuthenticationMode(esp_ble_auth_req_t auth_req) {
/**
- * @brief Set our device IO capability to let end user perform authorization
- * either by displaying or entering generated 6-digits pin code
+ * @brief Set our device IO capability to let end user perform authorization
+ * either by displaying or entering generated 6-digit pin code or use \"just works\".
+ * @param [in] iocap The IO capabilites our device has.\n
+ * Can be set to one of:
+ * * ESP_IO_CAP_OUT 0
+ * * ESP_IO_CAP_IO 1
+ * * ESP_IO_CAP_IN 2
+ * * ESP_IO_CAP_NONE 3
+ * * ESP_IO_CAP_KBDISP 4
*/
void NimBLESecurity::setCapability(esp_ble_io_cap_t iocap) {
NimBLEDevice::setSecurityIOCap(iocap);
@@ -51,8 +61,13 @@ void NimBLESecurity::setCapability(esp_ble_io_cap_t iocap) {
/**
- * @brief Init encryption key by server
- * @param key_size is value between 7 and 16
+ * @brief Sets the keys we will distibute during encryption.
+ * @param [in] init_key A bitmask of the keys we will distibute.\n
+ * Can be one or more of:
+ * * ESP_BLE_ENC_KEY_MASK (1 << 0)
+ * * ESP_BLE_ID_KEY_MASK (1 << 1)
+ * * ESP_BLE_CSR_KEY_MASK (1 << 2)
+ * * ESP_BLE_LINK_KEY_MASK (1 << 3)
*/
void NimBLESecurity::setInitEncryptionKey(uint8_t init_key) {
NimBLEDevice::setSecurityInitKey(init_key);
@@ -60,8 +75,13 @@ void NimBLESecurity::setInitEncryptionKey(uint8_t init_key) {
/**
- * @brief Init encryption key by client
- * @param key_size is value between 7 and 16
+ * @brief Sets the keys we will accept during encryption.
+ * @param [in] resp_key A bitmask of the keys we will accept.\n
+ * Can be one or more of:
+ * * ESP_BLE_ENC_KEY_MASK (1 << 0)
+ * * ESP_BLE_ID_KEY_MASK (1 << 1)
+ * * ESP_BLE_CSR_KEY_MASK (1 << 2)
+ * * ESP_BLE_LINK_KEY_MASK (1 << 3)
*/
void NimBLESecurity::setRespEncryptionKey(uint8_t resp_key) {
NimBLEDevice::setSecurityRespKey(resp_key);
@@ -70,7 +90,6 @@ void NimBLESecurity::setRespEncryptionKey(uint8_t resp_key) {
/**
*@todo Requires implementation
- *
*/
void NimBLESecurity::setKeySize(uint8_t key_size) {
@@ -80,7 +99,8 @@ void NimBLESecurity::setKeySize(uint8_t key_size) {
/**
- * Setup for static PIN connection.
+ * @brief Sets a static PIN used to authenticate/encrypt the connection.
+ * @param [in] pin The 6 digit pin code to accept.
*/
void NimBLESecurity::setStaticPIN(uint32_t pin){
//uint32_t passkey = pin;
diff --git a/libesp32/NimBLE-Arduino/src/NimBLESecurity.h b/libesp32/NimBLE-Arduino/src/NimBLESecurity.h
index 50c732c9b..5a7619f45 100644
--- a/libesp32/NimBLE-Arduino/src/NimBLESecurity.h
+++ b/libesp32/NimBLE-Arduino/src/NimBLESecurity.h
@@ -12,10 +12,6 @@
* Author: chegewara
*/
-/** This class exists for backward compatibility - Should not be used in new code
- * See the security functions in NimBLEDevice and callbacks in NimBLEServer / NimBLEClient
- */
-
#ifndef COMPONENTS_NIMBLESECURITY_H_
#define COMPONENTS_NIMBLESECURITY_H_
#include "sdkconfig.h"
@@ -56,6 +52,12 @@
typedef uint8_t esp_ble_auth_req_t; /*!< combination of the above bit pattern */
typedef uint8_t esp_ble_io_cap_t; /*!< combination of the io capability */
+
+/**
+ * @brief A class to handle BLE security operations.
+ * Deprecated - provided for backward compatibility only.
+ * @deprecated Use the security methods provided in NimBLEDevice instead.
+ */
class NimBLESecurity {
public:
NimBLESecurity();
@@ -78,8 +80,10 @@ private:
}; // BLESecurity
-/*
- * @brief Callbacks to handle GAP events related to authorization
+/**
+ * @brief Callbacks to handle GAP events related to authorization.
+ * Deprecated - provided for backward compatibility only.
+ * @deprecated Use the callbacks provided in NimBLEClientCallbacks and NimBLEServerCallbacks instead.
*/
class NimBLESecurityCallbacks {
public:
@@ -95,21 +99,25 @@ public:
/**
* @brief Provide us 6-digits code to perform authentication.
* It requires that our device is capable to display this code to end user
- * @param
+ * @param [in] pass_key The PIN provided by the peer.
*/
virtual void onPassKeyNotify(uint32_t pass_key) = 0;
/**
* @brief Here we can make decision if we want to let negotiate authorization with peer device or not
- * return Return true if we accept this peer device request
+ * @return Return true if we accept this peer device request
*/
-
virtual bool onSecurityRequest() = 0 ;
/**
- * Provide us information when authentication process is completed
+ * @brief Provides us information when authentication process is completed
*/
virtual void onAuthenticationComplete(ble_gap_conn_desc*) = 0;
+ /**
+ * @brief Called when using numeric comparison for authentication.
+ * @param [in] pin The PIN to compare.
+ * @return True to accept and pair.
+ */
virtual bool onConfirmPIN(uint32_t pin) = 0;
}; // BLESecurityCallbacks
diff --git a/libesp32/NimBLE-Arduino/src/NimBLEServer.cpp b/libesp32/NimBLE-Arduino/src/NimBLEServer.cpp
index 376b02144..8c75192a9 100644
--- a/libesp32/NimBLE-Arduino/src/NimBLEServer.cpp
+++ b/libesp32/NimBLE-Arduino/src/NimBLEServer.cpp
@@ -22,6 +22,10 @@
#include "NimBLEDevice.h"
#include "NimBLELog.h"
+#include "services/gap/ble_svc_gap.h"
+#include "services/gatt/ble_svc_gatt.h"
+
+
static const char* LOG_TAG = "NimBLEServer";
static NimBLEServerCallbacks defaultCallbacks;
@@ -37,9 +41,25 @@ NimBLEServer::NimBLEServer() {
m_pServerCallbacks = &defaultCallbacks;
m_gattsStarted = false;
m_advertiseOnDisconnect = true;
+ m_svcChanged = false;
+ m_deleteCallbacks = true;
} // NimBLEServer
+/**
+ * @brief Destructor: frees all resources / attributes created.
+ */
+NimBLEServer::~NimBLEServer() {
+ for(auto &it : m_svcVec) {
+ delete it;
+ }
+
+ if(m_deleteCallbacks && m_pServerCallbacks != &defaultCallbacks) {
+ delete m_pServerCallbacks;
+ }
+}
+
+
/**
* @brief Create a %BLE Service.
* @param [in] uuid The UUID of the new service.
@@ -71,6 +91,12 @@ NimBLEService* NimBLEServer::createService(const NimBLEUUID &uuid, uint32_t numH
NimBLEService* pService = new NimBLEService(uuid, numHandles, this);
m_svcVec.push_back(pService); // Save a reference to this service being on this server.
+ if(m_gattsStarted) {
+ ble_svc_gatt_changed(0x0001, 0xffff);
+ m_svcChanged = true;
+ resetGATT();
+ }
+
NIMBLE_LOGD(LOG_TAG, "<< createService");
return pService;
} // createService
@@ -146,17 +172,21 @@ void NimBLEServer::start() {
NIMBLE_LOGI(LOG_TAG, "Service changed characterisic handle: %d", m_svcChgChrHdl);
*/
- // Build a vector of characteristics with Notify / Indicate capabilities for event handling
+ // Get the assigned service handles and build a vector of characteristics
+ // with Notify / Indicate capabilities for event handling
for(auto &svc : m_svcVec) {
+ if(svc->m_removed == 0) {
+ rc = ble_gatts_find_svc(&svc->getUUID().getNative()->u, &svc->m_handle);
+ if(rc != 0) {
+ abort();
+ }
+ }
+
for(auto &chr : svc->m_chrVec) {
// if Notify / Indicate is enabled but we didn't create the descriptor
// we do it now.
if((chr->m_properties & BLE_GATT_CHR_F_INDICATE) ||
(chr->m_properties & BLE_GATT_CHR_F_NOTIFY)) {
-
- if(nullptr == chr->getDescriptorByUUID(uint16_t(0x2902))) {
- chr->createDescriptor(uint16_t(0x2902));
- }
m_notifyChrVec.push_back(chr);
}
}
@@ -168,8 +198,8 @@ void NimBLEServer::start() {
/**
* @brief Disconnect the specified client with optional reason.
- * @param [in] Connection Id of the client to disconnect.
- * @param [in] Reason code for disconnecting.
+ * @param [in] connId Connection Id of the client to disconnect.
+ * @param [in] reason code for disconnecting.
* @return NimBLE host return code.
*/
int NimBLEServer::disconnect(uint16_t connId, uint8_t reason) {
@@ -188,7 +218,7 @@ int NimBLEServer::disconnect(uint16_t connId, uint8_t reason) {
/**
* @brief Set the server to automatically start advertising when a client disconnects.
- * @param [in] bool true == advertise, false == don't advertise.
+ * @param [in] aod true == advertise, false == don't advertise.
*/
void NimBLEServer::advertiseOnDisconnect(bool aod) {
m_advertiseOnDisconnect = aod;
@@ -260,6 +290,11 @@ size_t NimBLEServer::getConnectedCount() {
server->m_connectedPeersVec.end(),
event->disconnect.conn.conn_handle),
server->m_connectedPeersVec.end());
+
+ if(server->m_svcChanged) {
+ server->resetGATT();
+ }
+
server->m_pServerCallbacks->onDisconnect(server);
if(server->m_advertiseOnDisconnect) {
@@ -269,9 +304,9 @@ size_t NimBLEServer::getConnectedCount() {
} // BLE_GAP_EVENT_DISCONNECT
case BLE_GAP_EVENT_SUBSCRIBE: {
- NIMBLE_LOGI(LOG_TAG, "subscribe event; cur_notify=%d\n value handle; "
- "val_handle=%d\n",
- event->subscribe.cur_notify, event->subscribe.attr_handle);
+ NIMBLE_LOGI(LOG_TAG, "subscribe event; attr_handle=%d, subscribed: %s",
+ event->subscribe.attr_handle,
+ (event->subscribe.cur_notify ? "true":"false"));
for(auto &it : server->m_notifyChrVec) {
if(it->getHandle() == event->subscribe.attr_handle) {
@@ -318,6 +353,12 @@ size_t NimBLEServer::getConnectedCount() {
return 0;
} // BLE_GAP_EVENT_NOTIFY_TX
+ case BLE_GAP_EVENT_ADV_COMPLETE: {
+ NIMBLE_LOGD(LOG_TAG, "Advertising Complete");
+ NimBLEDevice::getAdvertising()->advCompleteCB();
+ return 0;
+ }
+
case BLE_GAP_EVENT_CONN_UPDATE: {
NIMBLE_LOGD(LOG_TAG, "Connection parameters updated.");
return 0;
@@ -435,16 +476,121 @@ size_t NimBLEServer::getConnectedCount() {
* events are detected.
*
* @param [in] pCallbacks The callbacks to be invoked.
+ * @param [in] deleteCallbacks if true callback class will be deleted when server is destructed.
*/
-void NimBLEServer::setCallbacks(NimBLEServerCallbacks* pCallbacks) {
+void NimBLEServer::setCallbacks(NimBLEServerCallbacks* pCallbacks, bool deleteCallbacks) {
if (pCallbacks != nullptr){
m_pServerCallbacks = pCallbacks;
+ m_deleteCallbacks = deleteCallbacks;
} else {
m_pServerCallbacks = &defaultCallbacks;
}
} // setCallbacks
+/**
+ * @brief Remove a service from the server.
+ *
+ * @details Immediately removes access to the service by clients, sends a service changed indication,
+ * and removes the service (if applicable) from the advertisments.
+ * The service is not deleted unless the deleteSvc parameter is true, otherwise the service remains
+ * available and can be re-added in the future. If desired a removed but not deleted service can
+ * be deleted later by calling this method with deleteSvc set to true.
+ *
+ * @note The service will not be removed from the database until all open connections are closed
+ * as it requires resetting the GATT server. In the interim the service will have it's visibility disabled.
+ *
+ * @note Advertising will need to be restarted by the user after calling this as we must stop
+ * advertising in order to remove the service.
+ *
+ * @param [in] service The service object to remove.
+ * @param [in] deleteSvc true if the service should be deleted.
+ */
+void NimBLEServer::removeService(NimBLEService* service, bool deleteSvc) {
+ // Check if the service was already removed and if so check if this
+ // is being called to delete the object and do so if requested.
+ // Otherwise, ignore the call and return.
+ if(service->m_removed > 0) {
+ if(deleteSvc) {
+ for(auto it = m_svcVec.begin(); it != m_svcVec.end(); ++it) {
+ if ((*it)->getUUID() == service->getUUID()) {
+ delete *it;
+ m_svcVec.erase(it);
+ break;
+ }
+ }
+ }
+
+ return;
+ }
+
+ int rc = ble_gatts_svc_set_visibility(service->getHandle(), 0);
+ if(rc !=0) {
+ return;
+ }
+
+ service->m_removed = deleteSvc ? 2 : 1;
+ m_svcChanged = true;
+
+ ble_svc_gatt_changed(0x0001, 0xffff);
+ resetGATT();
+ NimBLEDevice::getAdvertising()->removeServiceUUID(service->getUUID());
+}
+
+
+/**
+ * @brief Adds a service which was already created, but removed from availability.
+ * @param [in] service The service object to add.
+ * @note If it is desired to advertise the service it must be added by
+ * calling NimBLEAdvertising::addServiceUUID.
+ */
+void NimBLEServer::addService(NimBLEService* service) {
+ // If adding a service that was not removed just return.
+ if(service->m_removed == 0) {
+ return;
+ }
+
+ service->m_removed = 0;
+ m_svcChanged = true;
+
+ ble_svc_gatt_changed(0x0001, 0xffff);
+ resetGATT();
+}
+
+
+/**
+ * @brief Resets the GATT server, used when services are added/removed after initialization.
+ */
+void NimBLEServer::resetGATT() {
+ if(getConnectedCount() > 0) {
+ return;
+ }
+
+ NimBLEDevice::stopAdvertising();
+ ble_gatts_reset();
+ ble_svc_gap_init();
+ ble_svc_gatt_init();
+
+ for(auto it = m_svcVec.begin(); it != m_svcVec.end(); ) {
+ if ((*it)->m_removed > 0) {
+ if ((*it)->m_removed == 2) {
+ delete *it;
+ it = m_svcVec.erase(it);
+ } else {
+ ++it;
+ }
+ continue;
+ }
+
+ (*it)->start();
+ ++it;
+ }
+
+ m_svcChanged = false;
+ m_gattsStarted = false;
+}
+
+
/**
* @brief Start advertising.
*
@@ -516,7 +662,7 @@ uint32_t NimBLEServerCallbacks::onPassKeyRequest(){
NIMBLE_LOGD("NimBLEServerCallbacks", "onPassKeyRequest: default: 123456");
return 123456;
}
-
+/*
void NimBLEServerCallbacks::onPassKeyNotify(uint32_t pass_key){
NIMBLE_LOGD("NimBLEServerCallbacks", "onPassKeyNotify: default: %d", pass_key);
}
@@ -525,6 +671,7 @@ bool NimBLEServerCallbacks::onSecurityRequest(){
NIMBLE_LOGD("NimBLEServerCallbacks", "onSecurityRequest: default: true");
return true;
}
+*/
void NimBLEServerCallbacks::onAuthenticationComplete(ble_gap_conn_desc*){
NIMBLE_LOGD("NimBLEServerCallbacks", "onAuthenticationComplete: default");
}
diff --git a/libesp32/NimBLE-Arduino/src/NimBLEServer.h b/libesp32/NimBLE-Arduino/src/NimBLEServer.h
index cfaa8acc7..1fa24b23c 100644
--- a/libesp32/NimBLE-Arduino/src/NimBLEServer.h
+++ b/libesp32/NimBLE-Arduino/src/NimBLEServer.h
@@ -41,8 +41,11 @@ public:
NimBLEService* createService(const char* uuid);
NimBLEService* createService(const NimBLEUUID &uuid, uint32_t numHandles=15,
uint8_t inst_id=0);
+ void removeService(NimBLEService* service, bool deleteSvc = false);
+ void addService(NimBLEService* service);
NimBLEAdvertising* getAdvertising();
- void setCallbacks(NimBLEServerCallbacks* pCallbacks);
+ void setCallbacks(NimBLEServerCallbacks* pCallbacks,
+ bool deleteCallbacks = true);
void startAdvertising();
void stopAdvertising();
void start();
@@ -54,18 +57,21 @@ public:
uint16_t minInterval, uint16_t maxInterval,
uint16_t latency, uint16_t timeout);
uint16_t getPeerMTU(uint16_t conn_id);
- std::vector getPeerDevices();
+// std::vector getPeerDevices();
void advertiseOnDisconnect(bool);
private:
NimBLEServer();
+ ~NimBLEServer();
friend class NimBLECharacteristic;
friend class NimBLEDevice;
friend class NimBLEAdvertising;
bool m_gattsStarted;
bool m_advertiseOnDisconnect;
+ bool m_svcChanged;
NimBLEServerCallbacks* m_pServerCallbacks;
+ bool m_deleteCallbacks;
std::vector m_connectedPeersVec;
// uint16_t m_svcChgChrHdl; // Future use
@@ -74,6 +80,7 @@ private:
std::vector m_notifyChrVec;
static int handleGapEvent(struct ble_gap_event *event, void *arg);
+ void resetGATT();
}; // NimBLEServer
@@ -83,29 +90,52 @@ private:
class NimBLEServerCallbacks {
public:
virtual ~NimBLEServerCallbacks() {};
+
/**
- * @brief Handle a new client connection.
- *
- * When a new client connects, we are invoked.
- *
- * @param [in] pServer A reference to the %BLE server that received the client connection.
+ * @brief Handle a client connection.
+ * This is called when a client connects.
+ * @param [in] pServer A pointer to the %BLE server that received the client connection.
*/
virtual void onConnect(NimBLEServer* pServer);
- virtual void onConnect(NimBLEServer* pServer, ble_gap_conn_desc* desc);
+
/**
- * @brief Handle an existing client disconnection.
- *
- * When an existing client disconnects, we are invoked.
- *
+ * @brief Handle a client connection.
+ * This is called when a client connects.
+ * @param [in] pServer A pointer to the %BLE server that received the client connection.
+ * @param [in] desc A pointer to the connection description structure containig information
+ * about the connection parameters.
+ */
+ virtual void onConnect(NimBLEServer* pServer, ble_gap_conn_desc* desc);
+
+ /**
+ * @brief Handle a client disconnection.
+ * This is called when a client disconnects.
* @param [in] pServer A reference to the %BLE server that received the existing client disconnection.
*/
virtual void onDisconnect(NimBLEServer* pServer);
- virtual uint32_t onPassKeyRequest(); //{return 0;}
- virtual void onPassKeyNotify(uint32_t pass_key); //{}
- virtual bool onSecurityRequest(); //{return true;}
- virtual void onAuthenticationComplete(ble_gap_conn_desc* desc);//{};
- virtual bool onConfirmPIN(uint32_t pin);//{return true;}
+ /**
+ * @brief Called when a client requests a passkey for pairing.
+ * @return The passkey to be sent to the client.
+ */
+ virtual uint32_t onPassKeyRequest();
+
+ //virtual void onPassKeyNotify(uint32_t pass_key);
+ //virtual bool onSecurityRequest();
+
+ /**
+ * @brief Called when the pairing procedure is complete.
+ * @param [in] desc A pointer to the struct containing the connection information.\n
+ * This can be used to check the status of the connection encryption/pairing.
+ */
+ virtual void onAuthenticationComplete(ble_gap_conn_desc* desc);
+
+ /**
+ * @brief Called when using numeric comparision for pairing.
+ * @param [in] pin The pin to compare with the client.
+ * @return True to accept the pin.
+ */
+ virtual bool onConfirmPIN(uint32_t pin);
}; // NimBLEServerCallbacks
diff --git a/libesp32/NimBLE-Arduino/src/NimBLEService.cpp b/libesp32/NimBLE-Arduino/src/NimBLEService.cpp
index c2631ab36..3420da2c7 100644
--- a/libesp32/NimBLE-Arduino/src/NimBLEService.cpp
+++ b/libesp32/NimBLE-Arduino/src/NimBLEService.cpp
@@ -49,13 +49,35 @@ NimBLEService::NimBLEService(const char* uuid, uint16_t numHandles, NimBLEServer
* @param [in] a pointer to the server instance that this service belongs to.
*/
NimBLEService::NimBLEService(const NimBLEUUID &uuid, uint16_t numHandles, NimBLEServer* pServer) {
- m_uuid = uuid;
- m_handle = NULL_HANDLE;
- m_pServer = pServer;
- m_numHandles = numHandles;
+ m_uuid = uuid;
+ m_handle = NULL_HANDLE;
+ m_pServer = pServer;
+ m_numHandles = numHandles;
+ m_pSvcDef = nullptr;
+ m_removed = 0;
+
} // NimBLEService
+NimBLEService::~NimBLEService() {
+ if(m_pSvcDef != nullptr) {
+ if(m_pSvcDef->characteristics != nullptr) {
+ for(int i=0; m_pSvcDef->characteristics[i].uuid != NULL; ++i) {
+ if(m_pSvcDef->characteristics[i].descriptors) {
+ delete(m_pSvcDef->characteristics[i].descriptors);
+ }
+ }
+ delete(m_pSvcDef->characteristics);
+ }
+
+ delete(m_pSvcDef);
+ }
+
+ for(auto &it : m_chrVec) {
+ delete it;
+ }
+}
+
/**
* @brief Dump details of this BLE GATT service.
* @return N/A.
@@ -94,98 +116,84 @@ NimBLEUUID NimBLEService::getUUID() {
* and registers it with the NimBLE stack.
* @return bool success/failure .
*/
-
bool NimBLEService::start() {
NIMBLE_LOGD(LOG_TAG, ">> start(): Starting service: %s", toString().c_str());
int rc = 0;
- // Nimble requires an array of services to be sent to the api
- // Since we are adding 1 at a time we create an array of 2 and set the type
- // of the second service to 0 to indicate the end of the array.
- ble_gatt_svc_def* svc = new ble_gatt_svc_def[2];
- ble_gatt_chr_def* pChr_a = nullptr;
- ble_gatt_dsc_def* pDsc_a = nullptr;
- svc[0].type = BLE_GATT_SVC_TYPE_PRIMARY;
- svc[0].uuid = &m_uuid.getNative()->u;
- svc[0].includes = NULL;
+ if(m_pSvcDef == nullptr) {
+ // Nimble requires an array of services to be sent to the api
+ // Since we are adding 1 at a time we create an array of 2 and set the type
+ // of the second service to 0 to indicate the end of the array.
+ ble_gatt_svc_def* svc = new ble_gatt_svc_def[2];
+ ble_gatt_chr_def* pChr_a = nullptr;
+ ble_gatt_dsc_def* pDsc_a = nullptr;
- size_t numChrs = m_chrVec.size();
+ svc[0].type = BLE_GATT_SVC_TYPE_PRIMARY;
+ svc[0].uuid = &m_uuid.getNative()->u;
+ svc[0].includes = NULL;
- NIMBLE_LOGD(LOG_TAG,"Adding %d characteristics for service %s", numChrs, toString().c_str());
+ size_t numChrs = m_chrVec.size();
- if(!numChrs){
- svc[0].characteristics = NULL;
- }else{
- // Nimble requires the last characteristic to have it's uuid = 0 to indicate the end
- // of the characteristics for the service. We create 1 extra and set it to null
- // for this purpose.
- pChr_a = new ble_gatt_chr_def[numChrs+1];
- NimBLECharacteristic* pCharacteristic = *m_chrVec.begin();
+ NIMBLE_LOGD(LOG_TAG,"Adding %d characteristics for service %s", numChrs, toString().c_str());
- for(uint8_t i=0; i < numChrs;) {
- uint8_t numDscs = pCharacteristic->m_dscVec.size();
- if(numDscs) {
- // skip 2902 as it's automatically created by NimBLE
- // if Indicate or Notify flags are set
- if(((pCharacteristic->m_properties & BLE_GATT_CHR_F_INDICATE) ||
- (pCharacteristic->m_properties & BLE_GATT_CHR_F_NOTIFY)) &&
- pCharacteristic->getDescriptorByUUID("2902") != nullptr)
- {
- numDscs--;
- }
- }
+ if(!numChrs){
+ svc[0].characteristics = NULL;
+ }else{
+ // Nimble requires the last characteristic to have it's uuid = 0 to indicate the end
+ // of the characteristics for the service. We create 1 extra and set it to null
+ // for this purpose.
+ pChr_a = new ble_gatt_chr_def[numChrs+1];
+ NimBLECharacteristic* pCharacteristic = *m_chrVec.begin();
- if(!numDscs){
- pChr_a[i].descriptors = NULL;
- } else {
- // Must have last descriptor uuid = 0 so we have to create 1 extra
- //NIMBLE_LOGD(LOG_TAG, "Adding %d descriptors", numDscs);
- pDsc_a = new ble_gatt_dsc_def[numDscs+1];
- NimBLEDescriptor* pDescriptor = *pCharacteristic->m_dscVec.begin();
- for(uint8_t d=0; d < numDscs;) {
- // skip 2902
- if(pDescriptor->m_uuid == NimBLEUUID(uint16_t(0x2902))) {
- //NIMBLE_LOGD(LOG_TAG, "Skipped 0x2902");
- pDescriptor = *(pCharacteristic->m_dscVec.begin()+d+1);
- continue;
+ for(uint8_t i=0; i < numChrs;) {
+ uint8_t numDscs = pCharacteristic->m_dscVec.size();
+
+ if(!numDscs){
+ pChr_a[i].descriptors = NULL;
+ } else {
+ // Must have last descriptor uuid = 0 so we have to create 1 extra
+ pDsc_a = new ble_gatt_dsc_def[numDscs+1];
+ NimBLEDescriptor* pDescriptor = *pCharacteristic->m_dscVec.begin();
+ for(uint8_t d=0; d < numDscs;) {
+ pDsc_a[d].uuid = &pDescriptor->m_uuid.getNative()->u;
+ pDsc_a[d].att_flags = pDescriptor->m_properties;
+ pDsc_a[d].min_key_size = 0;
+ pDsc_a[d].access_cb = NimBLEDescriptor::handleGapEvent;
+ pDsc_a[d].arg = pDescriptor;
+ d++;
+ pDescriptor = *(pCharacteristic->m_dscVec.begin() + d);
}
- pDsc_a[d].uuid = &pDescriptor->m_uuid.getNative()->u;
- pDsc_a[d].att_flags = pDescriptor->m_properties;
- pDsc_a[d].min_key_size = 0;
- pDsc_a[d].access_cb = NimBLEDescriptor::handleGapEvent;
- pDsc_a[d].arg = pDescriptor;
- d++;
- pDescriptor = *(pCharacteristic->m_dscVec.begin() + d);
+
+ pDsc_a[numDscs].uuid = NULL;
+ pChr_a[i].descriptors = pDsc_a;
}
- pDsc_a[numDscs].uuid = NULL;
- pChr_a[i].descriptors = pDsc_a;
+ pChr_a[i].uuid = &pCharacteristic->m_uuid.getNative()->u;
+ pChr_a[i].access_cb = NimBLECharacteristic::handleGapEvent;
+ pChr_a[i].arg = pCharacteristic;
+ pChr_a[i].flags = pCharacteristic->m_properties;
+ pChr_a[i].min_key_size = 0;
+ pChr_a[i].val_handle = &pCharacteristic->m_handle;
+ i++;
+ pCharacteristic = *(m_chrVec.begin() + i);
}
- pChr_a[i].uuid = &pCharacteristic->m_uuid.getNative()->u;
- pChr_a[i].access_cb = NimBLECharacteristic::handleGapEvent;
- pChr_a[i].arg = pCharacteristic;
- pChr_a[i].flags = pCharacteristic->m_properties;
- pChr_a[i].min_key_size = 0;
- pChr_a[i].val_handle = &pCharacteristic->m_handle;
- i++;
- pCharacteristic = *(m_chrVec.begin() + i);
+ pChr_a[numChrs].uuid = NULL;
+ svc[0].characteristics = pChr_a;
}
- pChr_a[numChrs].uuid = NULL;
- svc[0].characteristics = pChr_a;
+ // end of services must indicate to api with type = 0
+ svc[1].type = 0;
+ m_pSvcDef = svc;
}
- // end of services must indicate to api with type = 0
- svc[1].type = 0;
-
- rc = ble_gatts_count_cfg((const ble_gatt_svc_def*)svc);
+ rc = ble_gatts_count_cfg((const ble_gatt_svc_def*)m_pSvcDef);
if (rc != 0) {
NIMBLE_LOGE(LOG_TAG, "ble_gatts_count_cfg failed, rc= %d, %s", rc, NimBLEUtils::returnCodeToString(rc));
return false;
}
- rc = ble_gatts_add_svcs((const ble_gatt_svc_def*)svc);
+ rc = ble_gatts_add_svcs((const ble_gatt_svc_def*)m_pSvcDef);
if (rc != 0) {
NIMBLE_LOGE(LOG_TAG, "ble_gatts_add_svcs, rc= %d, %s", rc, NimBLEUtils::returnCodeToString(rc));
return false;
@@ -238,11 +246,21 @@ NimBLECharacteristic* NimBLEService::createCharacteristic(const NimBLEUUID &uuid
} // createCharacteristic
+/**
+ * @brief Get a pointer to the characteristic object with the specified UUID.
+ * @param [in] uuid The UUID of the characteristic.
+ * @return A pointer to the characteristic object or nullptr if not found.
+ */
NimBLECharacteristic* NimBLEService::getCharacteristic(const char* uuid) {
return getCharacteristic(NimBLEUUID(uuid));
}
+/**
+ * @brief Get a pointer to the characteristic object with the specified UUID.
+ * @param [in] uuid The UUID of the characteristic.
+ * @return A pointer to the characteristic object or nullptr if not found.
+ */
NimBLECharacteristic* NimBLEService::getCharacteristic(const NimBLEUUID &uuid) {
for (auto &it : m_chrVec) {
if (it->getUUID() == uuid) {
diff --git a/libesp32/NimBLE-Arduino/src/NimBLEService.h b/libesp32/NimBLE-Arduino/src/NimBLEService.h
index 4c1fa2adf..191d97fa5 100644
--- a/libesp32/NimBLE-Arduino/src/NimBLEService.h
+++ b/libesp32/NimBLE-Arduino/src/NimBLEService.h
@@ -57,6 +57,7 @@ public:
private:
NimBLEService(const char* uuid, uint16_t numHandles, NimBLEServer* pServer);
NimBLEService(const NimBLEUUID &uuid, uint16_t numHandles, NimBLEServer* pServer);
+ ~NimBLEService();
friend class NimBLEServer;
friend class NimBLEDevice;
@@ -65,7 +66,8 @@ private:
NimBLEServer* m_pServer;
NimBLEUUID m_uuid;
uint16_t m_numHandles;
-
+ ble_gatt_svc_def* m_pSvcDef;
+ uint8_t m_removed;
std::vector m_chrVec;
}; // NimBLEService
diff --git a/libesp32/NimBLE-Arduino/src/NimBLEUUID.cpp b/libesp32/NimBLE-Arduino/src/NimBLEUUID.cpp
index 4a9d7e876..1b00a3237 100644
--- a/libesp32/NimBLE-Arduino/src/NimBLEUUID.cpp
+++ b/libesp32/NimBLE-Arduino/src/NimBLEUUID.cpp
@@ -73,15 +73,11 @@ static const char* LOG_TAG = "NimBLEUUID";
/**
* @brief Create a UUID from 16 bytes of memory.
- *
* @param [in] pData The pointer to the start of the UUID.
* @param [in] size The size of the data.
* @param [in] msbFirst Is the MSB first in pData memory?
*/
NimBLEUUID::NimBLEUUID(const uint8_t* pData, size_t size, bool msbFirst) {
-/*** TODO: change this to use the Nimble function for various lenght UUIDs:
- int ble_uuid_init_from_buf(ble_uuid_any_t *uuid, const void *buf, size_t len);
-***/
if (size != 16) {
NIMBLE_LOGE(LOG_TAG,"ERROR: UUID length not 16 bytes");
return;
@@ -99,7 +95,6 @@ NimBLEUUID::NimBLEUUID(const uint8_t* pData, size_t size, bool msbFirst) {
/**
* @brief Create a UUID from the 16bit value.
- *
* @param [in] uuid The 16bit short form UUID.
*/
NimBLEUUID::NimBLEUUID(uint16_t uuid) {
@@ -111,7 +106,6 @@ NimBLEUUID::NimBLEUUID(uint16_t uuid) {
/**
* @brief Create a UUID from the 32bit value.
- *
* @param [in] uuid The 32bit short form UUID.
*/
NimBLEUUID::NimBLEUUID(uint32_t uuid) {
@@ -123,7 +117,6 @@ NimBLEUUID::NimBLEUUID(uint32_t uuid) {
/**
* @brief Create a UUID from the native UUID.
- *
* @param [in] uuid The native UUID.
*/
NimBLEUUID::NimBLEUUID(const ble_uuid128_t* uuid) {
@@ -135,8 +128,8 @@ NimBLEUUID::NimBLEUUID(const ble_uuid128_t* uuid) {
/**
* @brief Create a UUID from the 128bit value using hex parts instead of string,
- * instead of BLEUUID("ebe0ccb0-7a0a-4b0c-8a1a-6ff2997da3a6"), it becomes
- * BLEUUID(0xebe0ccb0, 0x7a0a, 0x4b0c, 0x8a1a6ff2997da3a6)
+ * instead of NimBLEUUID("ebe0ccb0-7a0a-4b0c-8a1a-6ff2997da3a6"), it becomes
+ * NimBLEUUID(0xebe0ccb0, 0x7a0a, 0x4b0c, 0x8a1a6ff2997da3a6)
*
* @param [in] first The first 32bit of the UUID.
* @param [in] second The next 16bit of the UUID.
@@ -153,6 +146,9 @@ NimBLEUUID::NimBLEUUID(uint32_t first, uint16_t second, uint16_t third, uint64_t
}
+/**
+ * @brief Creates an empty UUID.
+ */
NimBLEUUID::NimBLEUUID() {
m_valueSet = false;
} // NimBLEUUID
@@ -180,29 +176,31 @@ bool NimBLEUUID::equals(const NimBLEUUID &uuid) const {
/**
- * Create a BLEUUID from a string of the form:
+ * Create a NimBLEUUID from a string of the form:
* 0xNNNN
* 0xNNNNNNNN
- * 0x
+ * 0x
* NNNN
* NNNNNNNN
- *
+ *
+ *
+ * @param [in] uuid The string to create the UUID from.
*/
-NimBLEUUID NimBLEUUID::fromString(const std::string &_uuid) {
+NimBLEUUID NimBLEUUID::fromString(const std::string &uuid) {
uint8_t start = 0;
- if (strstr(_uuid.c_str(), "0x") != nullptr) { // If the string starts with 0x, skip those characters.
+ if (strstr(uuid.c_str(), "0x") != nullptr) { // If the string starts with 0x, skip those characters.
start = 2;
}
- uint8_t len = _uuid.length() - start; // Calculate the length of the string we are going to use.
+ uint8_t len = uuid.length() - start; // Calculate the length of the string we are going to use.
if(len == 4) {
- uint16_t x = strtoul(_uuid.substr(start, len).c_str(), NULL, 16);
+ uint16_t x = strtoul(uuid.substr(start, len).c_str(), NULL, 16);
return NimBLEUUID(x);
} else if (len == 8) {
- uint32_t x = strtoul(_uuid.substr(start, len).c_str(), NULL, 16);
+ uint32_t x = strtoul(uuid.substr(start, len).c_str(), NULL, 16);
return NimBLEUUID(x);
} else if (len == 36) {
- return NimBLEUUID(_uuid);
+ return NimBLEUUID(uuid);
}
return NimBLEUUID();
} // fromString
@@ -210,8 +208,7 @@ NimBLEUUID NimBLEUUID::fromString(const std::string &_uuid) {
/**
* @brief Get the native UUID value.
- *
- * @return The native UUID value or NULL if not set.
+ * @return The native UUID value or nullptr if not set.
*/
const ble_uuid_any_t* NimBLEUUID::getNative() const {
if (m_valueSet == false) {
@@ -224,9 +221,9 @@ const ble_uuid_any_t* NimBLEUUID::getNative() const {
/**
* @brief Convert a UUID to its 128 bit representation.
- *
- * A UUID can be internally represented as 16bit, 32bit or the full 128bit. This method
+ * @details A UUID can be internally represented as 16bit, 32bit or the full 128bit. This method
* will convert 16 or 32 bit representations to the full 128bit.
+ * @return The NimBLEUUID converted to 128bit.
*/
const NimBLEUUID &NimBLEUUID::to128() {
// If we either don't have a value or are already a 128 bit UUID, nothing further to do.
@@ -248,19 +245,23 @@ const NimBLEUUID &NimBLEUUID::to128() {
/**
* @brief Get a string representation of the UUID.
- *
+ * @details
* The format of a string is:
* 01234567 8901 2345 6789 012345678901
* 0000180d-0000-1000-8000-00805f9b34fb
* 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
*
* @return A string representation of the UUID.
+ * @deprecated Use std::string() operator instead.
*/
std::string NimBLEUUID::toString() const {
return std::string(*this);
} // toString
+/**
+ * @brief Convienience operator to check if this UUID is equal to another.
+ */
bool NimBLEUUID::operator ==(const NimBLEUUID & rhs) const {
if(m_valueSet && rhs.m_valueSet) {
return ble_uuid_cmp(&m_uuid.u, &rhs.m_uuid.u) == 0;
@@ -270,11 +271,19 @@ bool NimBLEUUID::operator ==(const NimBLEUUID & rhs) const {
}
+/**
+ * @brief Convienience operator to check if this UUID is not equal to another.
+ */
bool NimBLEUUID::operator !=(const NimBLEUUID & rhs) const {
return !this->operator==(rhs);
}
+/**
+ * @brief Convienience operator to convert this UUID to string representation.
+ * @details This allows passing NimBLEUUID to functions
+ * that accept std::string and/or or it's methods as a parameter.
+ */
NimBLEUUID::operator std::string() const {
if (!m_valueSet) return std::string(); // If we have no value, nothing to format.
diff --git a/libesp32/NimBLE-Arduino/src/NimBLEUUID.h b/libesp32/NimBLE-Arduino/src/NimBLEUUID.h
index f07bb3df5..982f9c36d 100644
--- a/libesp32/NimBLE-Arduino/src/NimBLEUUID.h
+++ b/libesp32/NimBLE-Arduino/src/NimBLEUUID.h
@@ -3,7 +3,7 @@
*
* Created: on Jan 24 2020
* Author H2zero
- *
+ *
* Originally:
*
* BLEUUID.h
@@ -37,20 +37,21 @@ public:
NimBLEUUID(const uint8_t* pData, size_t size, bool msbFirst);
NimBLEUUID(uint32_t first, uint16_t second, uint16_t third, uint64_t fourth);
NimBLEUUID();
- uint8_t bitSize() const; // Get the number of bits in this uuid.
- bool equals(const NimBLEUUID &uuid) const;
+
+ uint8_t bitSize() const;
+ bool equals(const NimBLEUUID &uuid) const;
const ble_uuid_any_t* getNative() const;
- const NimBLEUUID & to128();
- std::string toString() const;
- static NimBLEUUID fromString(const std::string &uuid); // Create a NimBLEUUID from a string
+ const NimBLEUUID & to128();
+ std::string toString() const;
+ static NimBLEUUID fromString(const std::string &uuid);
bool operator ==(const NimBLEUUID & rhs) const;
bool operator !=(const NimBLEUUID & rhs) const;
operator std::string() const;
private:
- ble_uuid_any_t m_uuid; // The underlying UUID structure that this class wraps.
- bool m_valueSet = false; // Is there a value set for this instance.
+ ble_uuid_any_t m_uuid;
+ bool m_valueSet = false;
}; // NimBLEUUID
#endif /* CONFIG_BT_ENABLED */
#endif /* COMPONENTS_NIMBLEUUID_H_ */
diff --git a/libesp32/NimBLE-Arduino/src/NimBLEUtils.cpp b/libesp32/NimBLE-Arduino/src/NimBLEUtils.cpp
index 1f1f22c25..7a1f55b41 100644
--- a/libesp32/NimBLE-Arduino/src/NimBLEUtils.cpp
+++ b/libesp32/NimBLE-Arduino/src/NimBLEUtils.cpp
@@ -16,6 +16,11 @@
static const char* LOG_TAG = "NimBLEUtils";
+/**
+ * @brief A function for checking validity of connection parameters.
+ * @param [in] params A pointer to the structure containing the parameters to check.
+ * @return valid == 0 or error code.
+ */
int NimBLEUtils::checkConnParams(ble_gap_conn_params* params) {
/* Check connection interval min */
if ((params->itvl_min < BLE_HCI_CONN_ITVL_MIN) ||
@@ -49,6 +54,11 @@ int NimBLEUtils::checkConnParams(ble_gap_conn_params* params) {
}
+/**
+ * @brief Converts a return code from the NimBLE stack to a text string.
+ * @param [in] rc The return code to convert.
+ * @return A string representation of the return code.
+ */
const char* NimBLEUtils::returnCodeToString(int rc) {
#if defined(CONFIG_NIMBLE_CPP_ENABLE_RETURN_CODE_TEXT)
switch(rc) {
@@ -338,9 +348,9 @@ const char* NimBLEUtils::returnCodeToString(int rc) {
/**
- * @brief Convert the BLE Advertising Data flags to a string.
- * @param adFlags The flags to convert
- * @return std::string A string representation of the advertising flags.
+ * @brief Convert the advertising type flag to a string.
+ * @param advType The type to convert.
+ * @return A string representation of the advertising flags.
*/
const char* NimBLEUtils::advTypeToString(uint8_t advType) {
#if defined(CONFIG_NIMBLE_CPP_ENABLE_ADVERTISMENT_TYPE_TEXT)
@@ -367,7 +377,7 @@ const char* NimBLEUtils::advTypeToString(uint8_t advType) {
/**
* @brief Create a hex representation of data.
*
- * @param [in] target Where to write the hex string. If this is null, we malloc storage.
+ * @param [in] target Where to write the hex string. If this is null, we malloc storage.
* @param [in] source The start of the binary data.
* @param [in] length The length of the data to convert.
* @return A pointer to the formatted buffer.
@@ -400,6 +410,11 @@ char* NimBLEUtils::buildHexData(uint8_t* target, const uint8_t* source, uint8_t
} // buildHexData
+/**
+ * @brief Utility function to log the gap event info.
+ * @param [in] event A pointer to the gap event structure.
+ * @param [in] arg Unused.
+ */
void NimBLEUtils::dumpGapEvent(ble_gap_event *event, void *arg){
#if defined(CONFIG_NIMBLE_CPP_ENABLE_GAP_EVENT_CODE_TEXT)
NIMBLE_LOGD(LOG_TAG, "Received a GAP event: %s", gapEventToString(event->type));
@@ -408,7 +423,7 @@ void NimBLEUtils::dumpGapEvent(ble_gap_event *event, void *arg){
/**
- * @brief Convert a BT GAP event type to a string representation.
+ * @brief Convert a GAP event type to a string representation.
* @param [in] eventType The type of event.
* @return A string representation of the event type.
*/
@@ -493,198 +508,4 @@ const char* NimBLEUtils::gapEventToString(uint8_t eventType) {
#endif // #if defined(CONFIG_NIMBLE_CPP_ENABLE_GAP_EVENT_CODE_TEXT)
} // gapEventToString
-
-/**
- * Utility function to log an array of bytes.
- */
-void print_bytes(const uint8_t *bytes, int len)
-{
- int i;
-
- for (i = 0; i < len; i++) {
- MODLOG_DFLT(ERROR, "%s0x%02x", i != 0 ? ":" : "", bytes[i]);
- if(i % 30 == 0){
- MODLOG_DFLT(ERROR, "\n");
- }
- }
-
- MODLOG_DFLT(ERROR, "\n");
-}
-
-void print_mbuf(const struct os_mbuf *om)
-{
- int colon;
-
- colon = 0;
- while (om != NULL) {
- if (colon) {
- MODLOG_DFLT(DEBUG, ":");
- } else {
- colon = 1;
- }
- print_bytes(om->om_data, om->om_len);
- om = SLIST_NEXT(om, om_next);
- }
-}
-
-char *addr_str(const void *addr)
-{
- static char buf[6 * 2 + 5 + 1];
- const uint8_t *u8p;
-
- u8p = (const uint8_t*)addr;
- sprintf(buf, "%02x:%02x:%02x:%02x:%02x:%02x",
- u8p[5], u8p[4], u8p[3], u8p[2], u8p[1], u8p[0]);
-
- return buf;
-}
-
-void print_uuid(const ble_uuid_t *uuid)
-{
- char buf[BLE_UUID_STR_LEN];
-
- MODLOG_DFLT(DEBUG, "%s", ble_uuid_to_str(uuid, buf));
-}
-
-void print_adv_fields(const struct ble_hs_adv_fields *fields)
-{
- char s[BLE_HS_ADV_MAX_SZ];
- const uint8_t *u8p;
- int i;
-
- if (fields->flags != 0) {
- MODLOG_DFLT(DEBUG, " flags=0x%02x\n", fields->flags);
- }
-
- if (fields->uuids16 != NULL) {
- MODLOG_DFLT(DEBUG, " uuids16(%scomplete)=",
- fields->uuids16_is_complete ? "" : "in");
- for (i = 0; i < fields->num_uuids16; i++) {
- print_uuid(&fields->uuids16[i].u);
- MODLOG_DFLT(DEBUG, " ");
- }
- MODLOG_DFLT(DEBUG, "\n");
- }
-
- if (fields->uuids32 != NULL) {
- MODLOG_DFLT(DEBUG, " uuids32(%scomplete)=",
- fields->uuids32_is_complete ? "" : "in");
- for (i = 0; i < fields->num_uuids32; i++) {
- print_uuid(&fields->uuids32[i].u);
- MODLOG_DFLT(DEBUG, " ");
- }
- MODLOG_DFLT(DEBUG, "\n");
- }
-
- if (fields->uuids128 != NULL) {
- MODLOG_DFLT(DEBUG, " uuids128(%scomplete)=",
- fields->uuids128_is_complete ? "" : "in");
- for (i = 0; i < fields->num_uuids128; i++) {
- print_uuid(&fields->uuids128[i].u);
- MODLOG_DFLT(DEBUG, " ");
- }
- MODLOG_DFLT(DEBUG, "\n");
- }
-
- if (fields->name != NULL) {
- assert(fields->name_len < sizeof s - 1);
- memcpy(s, fields->name, fields->name_len);
- s[fields->name_len] = '\0';
- MODLOG_DFLT(DEBUG, " name(%scomplete)=%s\n",
- fields->name_is_complete ? "" : "in", s);
- }
-
- if (fields->tx_pwr_lvl_is_present) {
- MODLOG_DFLT(DEBUG, " tx_pwr_lvl=%d\n", fields->tx_pwr_lvl);
- }
-
- if (fields->slave_itvl_range != NULL) {
- MODLOG_DFLT(DEBUG, " slave_itvl_range=");
- print_bytes(fields->slave_itvl_range, BLE_HS_ADV_SLAVE_ITVL_RANGE_LEN);
- MODLOG_DFLT(DEBUG, "\n");
- }
-
- if (fields->svc_data_uuid16 != NULL) {
- MODLOG_DFLT(DEBUG, " svc_data_uuid16=");
- print_bytes(fields->svc_data_uuid16, fields->svc_data_uuid16_len);
- MODLOG_DFLT(DEBUG, "\n");
- }
-
- if (fields->public_tgt_addr != NULL) {
- MODLOG_DFLT(DEBUG, " public_tgt_addr=");
- u8p = fields->public_tgt_addr;
- for (i = 0; i < fields->num_public_tgt_addrs; i++) {
- MODLOG_DFLT(DEBUG, "public_tgt_addr=%s ", addr_str(u8p));
- u8p += BLE_HS_ADV_PUBLIC_TGT_ADDR_ENTRY_LEN;
- }
- MODLOG_DFLT(DEBUG, "\n");
- }
-
- if (fields->appearance_is_present) {
- MODLOG_DFLT(DEBUG, " appearance=0x%04x\n", fields->appearance);
- }
-
- if (fields->adv_itvl_is_present) {
- MODLOG_DFLT(DEBUG, " adv_itvl=0x%04x\n", fields->adv_itvl);
- }
-
- if (fields->svc_data_uuid32 != NULL) {
- MODLOG_DFLT(DEBUG, " svc_data_uuid32=");
- print_bytes(fields->svc_data_uuid32, fields->svc_data_uuid32_len);
- MODLOG_DFLT(DEBUG, "\n");
- }
-
- if (fields->svc_data_uuid128 != NULL) {
- MODLOG_DFLT(DEBUG, " svc_data_uuid128=");
- print_bytes(fields->svc_data_uuid128, fields->svc_data_uuid128_len);
- MODLOG_DFLT(DEBUG, "\n");
- }
-
- if (fields->uri != NULL) {
- MODLOG_DFLT(DEBUG, " uri=");
- print_bytes(fields->uri, fields->uri_len);
- MODLOG_DFLT(DEBUG, "\n");
- }
-
- if (fields->mfg_data != NULL) {
- MODLOG_DFLT(DEBUG, " mfg_data=");
- print_bytes(fields->mfg_data, fields->mfg_data_len);
- MODLOG_DFLT(DEBUG, "\n");
- }
-}
-
-
- /**
- * Logs information about a connection to the console.
- */
-void print_conn_desc(const struct ble_gap_conn_desc *desc)
-{
- MODLOG_DFLT(DEBUG, "handle=%d our_ota_addr_type=%d our_ota_addr=%s ",
- desc->conn_handle, desc->our_ota_addr.type,
- addr_str(desc->our_ota_addr.val));
- MODLOG_DFLT(DEBUG, "our_id_addr_type=%d our_id_addr=%s ",
- desc->our_id_addr.type, addr_str(desc->our_id_addr.val));
- MODLOG_DFLT(DEBUG, "peer_ota_addr_type=%d peer_ota_addr=%s ",
- desc->peer_ota_addr.type, addr_str(desc->peer_ota_addr.val));
- MODLOG_DFLT(DEBUG, "peer_id_addr_type=%d peer_id_addr=%s ",
- desc->peer_id_addr.type, addr_str(desc->peer_id_addr.val));
- MODLOG_DFLT(DEBUG, "conn_itvl=%d conn_latency=%d supervision_timeout=%d "
- "encrypted=%d authenticated=%d bonded=%d",
- desc->conn_itvl, desc->conn_latency,
- desc->supervision_timeout,
- desc->sec_state.encrypted,
- desc->sec_state.authenticated,
- desc->sec_state.bonded);
-}
-
-
-void print_addr(const void *addr)
-{
- const uint8_t *u8p;
-
- u8p = (uint8_t*)addr;
- MODLOG_DFLT(INFO, "%02x:%02x:%02x:%02x:%02x:%02x",
- u8p[5], u8p[4], u8p[3], u8p[2], u8p[1], u8p[0]);
-}
-
#endif //CONFIG_BT_ENABLED
diff --git a/libesp32/NimBLE-Arduino/src/NimBLEUtils.h b/libesp32/NimBLE-Arduino/src/NimBLEUtils.h
index 891f83596..acbc93e72 100644
--- a/libesp32/NimBLE-Arduino/src/NimBLEUtils.h
+++ b/libesp32/NimBLE-Arduino/src/NimBLEUtils.h
@@ -27,14 +27,10 @@ typedef struct {
std::string *buf;
} ble_task_data_t;
-extern "C"{
-char *addr_str(const void *addr);
-void print_conn_desc(const struct ble_gap_conn_desc *desc);
-void print_adv_fields(const struct ble_hs_adv_fields *fields);
-void print_addr(const void *addr);
-void print_bytes(const uint8_t *bytes, int len);
-}
+/**
+ * @brief A BLE Utility class with methods for debugging and general purpose use.
+ */
class NimBLEUtils {
public:
static void dumpGapEvent(ble_gap_event *event, void *arg);
diff --git a/libesp32/NimBLE-Arduino/src/esp-hci/src/esp_nimble_hci.c b/libesp32/NimBLE-Arduino/src/esp-hci/src/esp_nimble_hci.c
index e4ab99932..0494f0a18 100644
--- a/libesp32/NimBLE-Arduino/src/esp-hci/src/esp_nimble_hci.c
+++ b/libesp32/NimBLE-Arduino/src/esp-hci/src/esp_nimble_hci.c
@@ -30,6 +30,7 @@
#include "esp_bt.h"
#include "freertos/semphr.h"
#include "esp_compiler.h"
+#include "esp_ipc.h"
#define NIMBLE_VHCI_TIMEOUT_MS 2000
@@ -78,21 +79,29 @@ void ble_hci_trans_cfg_hs(ble_hci_trans_rx_cmd_fn *cmd_cb,
ble_hci_rx_acl_hs_arg = acl_arg;
}
+void ble_hci_trans_hs_cmd_tx_on_core_0(void *arg)
+{
+ uint8_t *cmd = arg;
+ uint16_t len = BLE_HCI_CMD_HDR_LEN + cmd[3] + 1;
+ esp_vhci_host_send_packet(cmd, len);
+}
int ble_hci_trans_hs_cmd_tx(uint8_t *cmd)
{
- uint16_t len;
uint8_t rc = 0;
assert(cmd != NULL);
*cmd = BLE_HCI_UART_H4_CMD;
- len = BLE_HCI_CMD_HDR_LEN + cmd[3] + 1;
if (!esp_vhci_host_check_send_available()) {
ESP_LOGD(TAG, "Controller not ready to receive packets");
}
if (xSemaphoreTake(vhci_send_sem, NIMBLE_VHCI_TIMEOUT_MS / portTICK_PERIOD_MS) == pdTRUE) {
- esp_vhci_host_send_packet(cmd, len);
+ if (xPortGetCoreID() != 0) {
+ esp_ipc_call_blocking(0, ble_hci_trans_hs_cmd_tx_on_core_0, cmd);
+ } else {
+ ble_hci_trans_hs_cmd_tx_on_core_0(cmd);
+ }
} else {
rc = BLE_HS_ETIMEOUT_HCI;
}
@@ -111,27 +120,37 @@ int ble_hci_trans_ll_evt_tx(uint8_t *hci_ev)
return rc;
}
+void ble_hci_trans_hs_acl_tx_on_core_0(void *arg)
+{
+ uint8_t data[MYNEWT_VAL(BLE_ACL_BUF_SIZE) + 1];
+ struct os_mbuf *om = arg;
+ uint16_t len = 1 + OS_MBUF_PKTLEN(om);
+
+ data[0] = BLE_HCI_UART_H4_ACL;
+ os_mbuf_copydata(om, 0, OS_MBUF_PKTLEN(om), &data[1]);
+
+ esp_vhci_host_send_packet(data, len);
+}
+
int ble_hci_trans_hs_acl_tx(struct os_mbuf *om)
{
- uint16_t len = 0;
- uint8_t data[MYNEWT_VAL(BLE_ACL_BUF_SIZE) + 1], rc = 0;
+ uint8_t rc = 0;
/* If this packet is zero length, just free it */
if (OS_MBUF_PKTLEN(om) == 0) {
os_mbuf_free_chain(om);
return 0;
}
- data[0] = BLE_HCI_UART_H4_ACL;
- len++;
if (!esp_vhci_host_check_send_available()) {
ESP_LOGD(TAG, "Controller not ready to receive packets");
}
- os_mbuf_copydata(om, 0, OS_MBUF_PKTLEN(om), &data[1]);
- len += OS_MBUF_PKTLEN(om);
-
if (xSemaphoreTake(vhci_send_sem, NIMBLE_VHCI_TIMEOUT_MS / portTICK_PERIOD_MS) == pdTRUE) {
- esp_vhci_host_send_packet(data, len);
+ if (xPortGetCoreID() != 0) {
+ esp_ipc_call_blocking(0, ble_hci_trans_hs_acl_tx_on_core_0, om);
+ } else {
+ ble_hci_trans_hs_acl_tx_on_core_0(om);
+ }
} else {
rc = BLE_HS_ETIMEOUT_HCI;
}
@@ -451,6 +470,7 @@ esp_err_t esp_nimble_hci_and_controller_init(void)
esp_bt_controller_mem_release(ESP_BT_MODE_CLASSIC_BT);
esp_bt_controller_config_t bt_cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT();
+ bt_cfg.ble_max_conn = CONFIG_BT_NIMBLE_MAX_CONNECTIONS;
if ((ret = esp_bt_controller_init(&bt_cfg)) != ESP_OK) {
return ret;
diff --git a/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/src/ble_hs_resolv_priv.h b/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/src/ble_hs_resolv_priv.h
index e76a26a55..7bc8a2fe6 100644
--- a/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/src/ble_hs_resolv_priv.h
+++ b/libesp32/NimBLE-Arduino/src/nimble/host/mesh/src/src/ble_hs_resolv_priv.h
@@ -53,6 +53,7 @@ struct ble_hs_peer_sec {
*/
struct ble_hs_dev_records {
bool rec_used;
+ uint8_t rand_addr_type;
uint8_t pseudo_addr[BLE_DEV_ADDR_LEN];
uint8_t rand_addr[BLE_DEV_ADDR_LEN];
uint8_t identity_addr[BLE_DEV_ADDR_LEN];
diff --git a/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_hs_resolv.c b/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_hs_resolv.c
index 60c77c9b8..46e5577f6 100644
--- a/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_hs_resolv.c
+++ b/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_hs_resolv.c
@@ -181,6 +181,44 @@ is_rpa_resolvable_by_peer_rec(struct ble_hs_dev_records *p_dev_rec, uint8_t *pee
return false;
}
+static void
+ble_rpa_replace_id_with_rand_addr(uint8_t *addr_type, uint8_t *peer_addr)
+{
+ struct ble_hs_dev_records *p_dev_rec;
+ ble_addr_t p_addr = {0};
+ struct ble_hs_conn *conn = NULL;
+
+ p_dev_rec = ble_rpa_find_peer_dev_rec(peer_addr);
+
+ if (p_dev_rec != NULL) {
+ if (memcmp(p_dev_rec->rand_addr, p_dev_rec->identity_addr, BLE_DEV_ADDR_LEN)) {
+ /* OTA address (before resolving) gets saved in RAND_ADDR when the peer
+ * record is fetched from resolving list. Replace peer address
+ * with rand_addr to maintain status quo for new pairing/encryption request. */
+ p_addr.type = *addr_type;
+ memcpy(&p_addr.val[0], peer_addr, BLE_DEV_ADDR_LEN);
+
+ ble_hs_lock();
+
+ conn = ble_hs_conn_find_by_addr(&p_addr);
+ /* Rewrite the peer address history in ble_hs_conn. Need to take
+ * this step to avoid taking wrong address during re-pairing
+ * process */
+ if (conn != NULL) {
+ conn->bhc_peer_rpa_addr.type = p_dev_rec->rand_addr_type;
+ memcpy(&conn->bhc_peer_rpa_addr.val[0], p_dev_rec->rand_addr, BLE_DEV_ADDR_LEN);
+ conn->bhc_peer_addr.type = p_dev_rec->rand_addr_type;
+ memcpy(&conn->bhc_peer_addr.val[0], p_dev_rec->rand_addr, BLE_DEV_ADDR_LEN);
+ BLE_HS_LOG(DEBUG, "\n Replace Identity addr with random addr received at"
+ " start of the connection\n");
+ }
+
+ ble_hs_unlock();
+ }
+ }
+ return;
+}
+
/* Add peer to peer device records.
*
* @return 0 if added successfully,
@@ -227,6 +265,7 @@ ble_rpa_find_rl_from_peer_records(uint8_t *peer_addr, uint8_t *peer_addr_type)
if (is_rpa_resolvable_by_peer_rec(p_dev_rec, peer_addr)) {
memcpy(p_dev_rec->rand_addr, peer_addr, BLE_DEV_ADDR_LEN);
+ p_dev_rec->rand_addr_type = *peer_addr_type;
rl = ble_hs_resolv_list_find(p_dev_rec->identity_addr);
if (rl) {
memcpy(peer_addr, p_dev_rec->identity_addr, BLE_DEV_ADDR_LEN);
@@ -277,13 +316,10 @@ ble_rpa_replace_peer_params_with_rl(uint8_t *peer_addr, uint8_t *addr_type,
if (is_rpa) {
ble_hs_log_flat_buf(peer_addr, BLE_DEV_ADDR_LEN);
- rl_tmp = ble_hs_resolv_list_find(peer_addr);
+ BLE_HS_LOG(DEBUG, "\n");
- /* Try to find from your peer_device records, if RL doesn't
- * exist */
- if (rl_tmp == NULL) {
- rl_tmp = ble_rpa_find_rl_from_peer_records(peer_addr, addr_type);
- }
+ /* Try to find RL from your peer_device records */
+ rl_tmp = ble_rpa_find_rl_from_peer_records(peer_addr, addr_type);
}
if (rl != NULL) {
@@ -571,7 +607,7 @@ ble_hs_resolv_list_add(uint8_t *cmdbuf)
int
ble_hs_resolv_list_rmv(uint8_t addr_type, uint8_t *ident_addr)
{
- int position;
+ int position, rc = BLE_HS_ENOENT;
/* Remove from IRK records */
position = ble_hs_is_on_resolv_list(ident_addr, addr_type);
@@ -583,10 +619,15 @@ ble_hs_resolv_list_rmv(uint8_t addr_type, uint8_t *ident_addr)
ble_hs_resolv_entry));
--g_ble_hs_resolv_data.rl_cnt;
- return 0;
+ rc = 0;
}
- return BLE_HS_ENOENT;
+ /* As we are removing the RL record, it is needed to change
+ * peer_address to its latest received OTA address, this helps when existing bond at
+ * peer side is removed */
+ ble_rpa_replace_id_with_rand_addr(&addr_type, ident_addr);
+
+ return rc;
}
/**
diff --git a/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_hs_resolv_priv.h b/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_hs_resolv_priv.h
index e76a26a55..7bc8a2fe6 100644
--- a/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_hs_resolv_priv.h
+++ b/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_hs_resolv_priv.h
@@ -53,6 +53,7 @@ struct ble_hs_peer_sec {
*/
struct ble_hs_dev_records {
bool rec_used;
+ uint8_t rand_addr_type;
uint8_t pseudo_addr[BLE_DEV_ADDR_LEN];
uint8_t rand_addr[BLE_DEV_ADDR_LEN];
uint8_t identity_addr[BLE_DEV_ADDR_LEN];
diff --git a/libesp32/NimBLE-Arduino/src/nimconfig.h b/libesp32/NimBLE-Arduino/src/nimconfig.h
index 907e6bc26..2f10fa2fc 100644
--- a/libesp32/NimBLE-Arduino/src/nimconfig.h
+++ b/libesp32/NimBLE-Arduino/src/nimconfig.h
@@ -1,12 +1,21 @@
+/** @file
+ *
+ * IGNORE THIS FILE IF USING ESP-IDF, USE MENUCONFIG TO SET NIMBLE OPTIONS.
+ *
+ * The config options here are for Arduino use only.
+ */
+
#pragma once
#include "sdkconfig.h"
-/** For ESP-IDF compatibility
- *
- * Some versions of ESP-IDF used the config name format "CONFIG_NIMBLE_".
- * This converts them to "CONFIG_BT_NIMBLE_" format used in the latest IDF.
+
+/*
+ * For ESP-IDF compatibility
+ * Some versions of ESP-IDF used the config name format "CONFIG_NIMBLE_".
+ * This converts them to "CONFIG_BT_NIMBLE_" format used in the latest IDF.
*/
-/* Detect if using ESP-IDF or Arduino (Arduino won't have these defines in sdkconfig)*/
+
+/* Detect if using ESP-IDF or Arduino (Arduino won't have these defines in sdkconfig) */
#if defined(CONFIG_BT_NIMBLE_TASK_STACK_SIZE) || defined(CONFIG_NIMBLE_TASK_STACK_SIZE)
#if defined(CONFIG_NIMBLE_ENABLED) && !defined(CONFIG_BT_NIMBLE_ENABLED)
@@ -36,99 +45,171 @@
#else // Using Arduino
/***********************************************
- * Arduino config options
+ * Arduino config options start here
**********************************************/
-/** Comment out if not using NimBLE Client functions
+/** @brief Comment out if not using NimBLE Client functions \n
* Reduces flash size by approx. 7kB.
*/
#define CONFIG_BT_NIMBLE_ROLE_CENTRAL
-/** Comment out if not using NimBLE Scan functions
+/** @brief Comment out if not using NimBLE Scan functions \n
* Reduces flash size by approx. 26kB.
*/
#define CONFIG_BT_NIMBLE_ROLE_OBSERVER
-/** Comment out if not using NimBLE Server functions
+/** @brief Comment out if not using NimBLE Server functions \n
* Reduces flash size by approx. 16kB.
*/
// #define CONFIG_BT_NIMBLE_ROLE_PERIPHERAL
-/** Comment out if not using NimBLE Advertising functions
+/** @brief Comment out if not using NimBLE Advertising functions \n
* Reduces flash size by approx. 5kB.
*/
// #define CONFIG_BT_NIMBLE_ROLE_BROADCASTER
-/** Uncomment to see debug log messages from the NimBLE host
+/* Uncomment to see debug log messages from the NimBLE host
* Uses approx. 32kB of flash memory.
*/
// #define CONFIG_BT_NIMBLE_DEBUG
-/** Uncomment to see NimBLE host return codes as text debug log messages.
+/* Uncomment to see NimBLE host return codes as text debug log messages.
* Uses approx. 7kB of flash memory.
*/
// #define CONFIG_NIMBLE_CPP_ENABLE_RETURN_CODE_TEXT
-/** Uncomment to see GAP event codes as text in debug log messages.
+/* Uncomment to see GAP event codes as text in debug log messages.
* Uses approx. 1kB of flash memory.
*/
// #define CONFIG_NIMBLE_CPP_ENABLE_GAP_EVENT_CODE_TEXT
-/** Uncomment to see advertisment types as text while scanning in debug log messages.
+/* Uncomment to see advertisment types as text while scanning in debug log messages.
* Uses approx. 250 bytes of flash memory.
*/
// #define CONFIG_NIMBLE_CPP_ENABLE_ADVERTISMENT_TYPE_TEXT
-/** Sets the core NimBLE host runs on */
+/** @brief Sets the core NimBLE host runs on */
#define CONFIG_BT_NIMBLE_PINNED_TO_CORE 0
-/** Sets the stack size for the NimBLE host task */
+/** @brief Sets the stack size for the NimBLE host task */
#define CONFIG_BT_NIMBLE_TASK_STACK_SIZE 4096
-/** Sets the number of simultaneous connections (esp controller max is 9) */
+/**
+ * @brief Sets the memory pool where NimBLE will be loaded
+ * @details By default NimBLE is loaded in internal ram.\n
+ * To use external PSRAM you must change this to `#define CONFIG_BT_NIMBLE_MEM_ALLOC_MODE_EXTERNAL 1`
+ */
+#define CONFIG_BT_NIMBLE_MEM_ALLOC_MODE_INTERNAL 1
+
+/** @brief Sets the number of simultaneous connections (esp controller max is 9) */
#define CONFIG_BT_NIMBLE_MAX_CONNECTIONS 3
-/** Sets the number of devices allowed to store/bond with */
+/** @brief Sets the number of devices allowed to store/bond with */
#define CONFIG_BT_NIMBLE_MAX_BONDS 3
-/** Sets the number of CCCD's to store per bonded device */
+/** @brief Sets the maximum number of CCCD subscriptions to store */
#define CONFIG_BT_NIMBLE_MAX_CCCDS 8
+/** @brief Set if CCCD's and bond data should be stored in NVS */
#define CONFIG_BT_NIMBLE_NVS_PERSIST 0
+
+/** @brief Allow legacy paring */
#define CONFIG_BT_NIMBLE_SM_LEGACY 1
+
+/** @brief Allow BLE secure connections */
#define CONFIG_BT_NIMBLE_SM_SC 1
+
+/** @brief Default device name */
#define CONFIG_BT_NIMBLE_SVC_GAP_DEVICE_NAME "nimble"
+
+/** @brief Max device name length (bytes) */
#define CONFIG_BT_NIMBLE_GAP_DEVICE_NAME_MAX_LEN 31
+
+/** @brief Default MTU size */
#define CONFIG_BT_NIMBLE_ATT_PREFERRED_MTU 256
+
+/** @brief Default GAP appearance */
#define CONFIG_BT_NIMBLE_SVC_GAP_APPEARANCE 0x0
+
+/** @brief ACL Buffer count */
#define CONFIG_BT_NIMBLE_ACL_BUF_COUNT 12
+
+/** @brief ACL Buffer size */
#define CONFIG_BT_NIMBLE_ACL_BUF_SIZE 255
+
+/** @brief HCI Event Buffer size */
#define CONFIG_BT_NIMBLE_HCI_EVT_BUF_SIZE 70
+
+/** @brief Number of high priority HCI event buffers */
#define CONFIG_BT_NIMBLE_HCI_EVT_HI_BUF_COUNT 30
+
+/** @brief Number of low priority HCI event buffers */
#define CONFIG_BT_NIMBLE_HCI_EVT_LO_BUF_COUNT 8
+
+/**
+ * @brief Sets the number of MSYS buffers available.
+ * @details MSYS is a system level mbuf registry. For prepare write & prepare \n
+ * responses MBUFs are allocated out of msys_1 pool. This may need to be increased if\n
+ * you are sending large blocks of data with a low MTU. E.g: 512 bytes with 23 MTU will fail.
+ */
#define CONFIG_BT_NIMBLE_MSYS1_BLOCK_COUNT 12
+
+
+/** @brief Random address refresh time in seconds */
+#define CONFIG_BT_NIMBLE_RPA_TIMEOUT 900
+
+/** @brief Maximum number of connection oriented channels */
+#define CONFIG_BT_NIMBLE_L2CAP_COC_MAX_NUM 0
+
+/* These should not be altered */
#define CONFIG_BT_NIMBLE_HS_FLOW_CTRL 1
#define CONFIG_BT_NIMBLE_HS_FLOW_CTRL_ITVL 1000
#define CONFIG_BT_NIMBLE_HS_FLOW_CTRL_THRESH 2
#define CONFIG_BT_NIMBLE_HS_FLOW_CTRL_TX_ON_DISCONNECT 1
-#define CONFIG_BT_NIMBLE_RPA_TIMEOUT 900
-#define CONFIG_BT_NIMBLE_L2CAP_COC_MAX_NUM 0
-/** Do not comment out */
#ifndef CONFIG_BT_ENABLED
#define CONFIG_BT_ENABLED
#endif
+
#define CONFIG_BTDM_CONTROLLER_MODE_BLE_ONLY
-#define CONFIG_BT_NIMBLE_MEM_ALLOC_MODE_INTERNAL
#endif // #if defined(CONFIG_BT_NIMBLE_TASK_STACK_SIZE) || defined(CONFIG_NIMBLE_TASK_STACK_SIZE)
-/** Cannot use client without scan */
+/**********************************
+ End Arduino config
+**********************************/
+
+
+/* Cannot use client without scan */
#if defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL) && !defined(CONFIG_BT_NIMBLE_ROLE_OBSERVER)
#define CONFIG_BT_NIMBLE_ROLE_OBSERVER
#endif
-/** Cannot use server without advertise */
+/* Cannot use server without advertise */
#if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL) && !defined(CONFIG_BT_NIMBLE_ROLE_BROADCASTER)
#define CONFIG_BT_NIMBLE_ROLE_BROADCASTER
#endif
+
+
+
+#ifdef _DOXYGEN_
+/** @brief Uncomment to see debug log messages from the NimBLE host \n
+ * Uses approx. 32kB of flash memory.
+ */
+#define CONFIG_BT_NIMBLE_DEBUG
+
+/** @brief Uncomment to see NimBLE host return codes as text debug log messages. \n
+ * Uses approx. 7kB of flash memory.
+ */
+#define CONFIG_NIMBLE_CPP_ENABLE_RETURN_CODE_TEXT
+
+/** @brief Uncomment to see GAP event codes as text in debug log messages. \n
+ * Uses approx. 1kB of flash memory.
+ */
+#define CONFIG_NIMBLE_CPP_ENABLE_GAP_EVENT_CODE_TEXT
+
+/** @brief Uncomment to see advertisment types as text while scanning in debug log messages. \n
+ * Uses approx. 250 bytes of flash memory.
+ */
+#define CONFIG_NIMBLE_CPP_ENABLE_ADVERTISMENT_TYPE_TEXT
+#endif // _DOXYGEN_
diff --git a/libesp32/NimBLE-Arduino/src/porting/nimble/src/os_mempool.c b/libesp32/NimBLE-Arduino/src/porting/nimble/src/os_mempool.c
index f4c712ff1..ba837e7d6 100644
--- a/libesp32/NimBLE-Arduino/src/porting/nimble/src/os_mempool.c
+++ b/libesp32/NimBLE-Arduino/src/porting/nimble/src/os_mempool.c
@@ -346,7 +346,8 @@ os_mempool_info_get_next(struct os_mempool *mp, struct os_mempool_info *omi)
omi->omi_num_blocks = cur->mp_num_blocks;
omi->omi_num_free = cur->mp_num_free;
omi->omi_min_free = cur->mp_min_free;
- strncpy(omi->omi_name, cur->name, sizeof(omi->omi_name));
+ strncpy(omi->omi_name, cur->name, sizeof(omi->omi_name) - 1);
+ omi->omi_name[sizeof(omi->omi_name) - 1] = '\0';
return (cur);
}
From e213461985f40ff6b806ed29777cfc5122e650b0 Mon Sep 17 00:00:00 2001
From: Staars
Date: Tue, 15 Sep 2020 10:46:08 +0200
Subject: [PATCH 051/148] needed API changes
---
tasmota/xsns_62_MI_ESP32.ino | 20 ++++++++++----------
1 file changed, 10 insertions(+), 10 deletions(-)
diff --git a/tasmota/xsns_62_MI_ESP32.ino b/tasmota/xsns_62_MI_ESP32.ino
index 0204b0462..06d1afd05 100644
--- a/tasmota/xsns_62_MI_ESP32.ino
+++ b/tasmota/xsns_62_MI_ESP32.ino
@@ -333,13 +333,13 @@ class MI32SensorCallback : public NimBLEClientCallbacks {
class MI32AdvCallbacks: public NimBLEAdvertisedDeviceCallbacks {
void onResult(NimBLEAdvertisedDevice* advertisedDevice) {
- // AddLog_P2(LOG_LEVEL_DEBUG,PSTR("Advertised Device: %s Buffer: %u"),advertisedDevice->getAddress().toString().c_str(),advertisedDevice->getServiceData().length());
- if (advertisedDevice->getServiceData().length() == 0) {
- // AddLog_P2(LOG_LEVEL_DEBUG,PSTR("No Xiaomi Device: %s Buffer: %u"),advertisedDevice->getAddress().toString().c_str(),advertisedDevice->getServiceData().length());
+ // AddLog_P2(LOG_LEVEL_DEBUG,PSTR("Advertised Device: %s Buffer: %u"),advertisedDevice->getAddress().toString().c_str(),advertisedDevice->getServiceData(0).length());
+ if (advertisedDevice->getServiceDataCount() == 0) {
+ // AddLog_P2(LOG_LEVEL_DEBUG,PSTR("No Xiaomi Device: %s Buffer: %u"),advertisedDevice->getAddress().toString().c_str(),advertisedDevice->getServiceData(0).length());
MI32Scan->erase(advertisedDevice->getAddress());
return;
}
- uint16_t uuid = advertisedDevice->getServiceDataUUID().getNative()->u16.value;
+ uint16_t uuid = advertisedDevice->getServiceDataUUID(0).getNative()->u16.value;
// AddLog_P2(LOG_LEVEL_DEBUG,PSTR("UUID: %x"),uuid);
uint8_t addr[6];
memcpy(addr,advertisedDevice->getAddress().getNative(),6);
@@ -350,14 +350,14 @@ class MI32AdvCallbacks: public NimBLEAdvertisedDeviceCallbacks {
}
// AddLog_P2(LOG_LEVEL_DEBUG,PSTR("RSSI: %d"),rssi); // actually i never got a 0xffff
if(uuid==0xfe95) {
- MI32ParseResponse((char*)advertisedDevice->getServiceData().data(),advertisedDevice->getServiceData().length(), addr, rssi);
+ MI32ParseResponse((char*)advertisedDevice->getServiceData(0).data(),advertisedDevice->getServiceData(0).length(), addr, rssi);
}
else if(uuid==0xfdcd) {
- MI32parseCGD1Packet((char*)advertisedDevice->getServiceData().data(),advertisedDevice->getServiceData().length(), addr, rssi);
+ MI32parseCGD1Packet((char*)advertisedDevice->getServiceData(0).data(),advertisedDevice->getServiceData(0).length(), addr, rssi);
}
else {
MI32Scan->erase(advertisedDevice->getAddress());
- // AddLog_P2(LOG_LEVEL_DEBUG,PSTR("No Xiaomi Device: %s Buffer: %u"),advertisedDevice->getAddress().toString().c_str(),advertisedDevice->getServiceData().length());
+ // AddLog_P2(LOG_LEVEL_DEBUG,PSTR("No Xiaomi Device: %s Buffer: %u"),advertisedDevice->getAddress().toString().c_str(),advertisedDevice->getServiceData(0).length());
}
};
};
@@ -749,12 +749,12 @@ void MI32StartScanTask(){
void MI32ScanTask(void *pvParameters){
if (MI32Scan == nullptr) MI32Scan = NimBLEDevice::getScan();
- DEBUG_SENSOR_LOG(PSTR("%s: Scan Cache Length: %u"),D_CMND_MI32, MI32Scan->getResults().getCount());
+ // DEBUG_SENSOR_LOG(PSTR("%s: Scan Cache Length: %u"),D_CMND_MI32, MI32Scan->getResults().getCount());
MI32Scan->setInterval(70);
MI32Scan->setWindow(50);
MI32Scan->setAdvertisedDeviceCallbacks(&MI32ScanCallbacks,true);
MI32Scan->setActiveScan(false);
- MI32Scan->start(0, MI32scanEndedCB, true); // never stop scanning, will pause automaically while connecting
+ MI32Scan->start(0, MI32scanEndedCB, true); // never stop scanning, will pause automatically while connecting
uint32_t timer = 0;
for(;;){
@@ -841,7 +841,7 @@ bool MI32connectLYWSD03forNotification(){
}
if (pChr){
if(pChr->canNotify()) {
- if(pChr->subscribe(true,false,MI32notifyCB)) {
+ if(pChr->subscribe(true,MI32notifyCB,false)) {
return true;
}
}
From afd197d493e124ac7ca62da995579b3340bb23eb Mon Sep 17 00:00:00 2001
From: Theo Arends <11044339+arendst@users.noreply.github.com>
Date: Tue, 15 Sep 2020 11:44:09 +0200
Subject: [PATCH 052/148] Add command ``SetOption111 1``
Add command ``SetOption111 1`` to enable frequency output for buzzer GPIO (#8994)
---
RELEASENOTES.md | 4 +++-
tasmota/CHANGELOG.md | 4 +++-
tools/decode-status.py | 6 ++++--
3 files changed, 10 insertions(+), 4 deletions(-)
diff --git a/RELEASENOTES.md b/RELEASENOTES.md
index 9b3740986..1cfdd3275 100644
--- a/RELEASENOTES.md
+++ b/RELEASENOTES.md
@@ -60,7 +60,9 @@ The attached binaries can also be downloaded from http://ota.tasmota.com/tasmota
- Fix energy total counters (#9263, #9266)
- Fix crash in ``ZbRestore``
- Fix reset BMP sensors when executing command ``SaveData`` and define USE_DEEPSLEEP enabled (#9300)
-- Add new shutter modes (#9244)
+- Add command ``SetOption110 1`` to disable Zigbee auto-config when pairing new devices
+- Add command ``SetOption111 1`` to enable frequency output for buzzer GPIO (#8994)
- Add ``#define USE_MQTT_AWS_IOT_LIGHT`` for password based AWS IoT authentication
+- Add new shutter modes (#9244)
- Add Zigbee auto-config when pairing
- Add support for MLX90640 IR array temperature sensor by Christian Baars
diff --git a/tasmota/CHANGELOG.md b/tasmota/CHANGELOG.md
index 5c0d36fd2..689b71748 100644
--- a/tasmota/CHANGELOG.md
+++ b/tasmota/CHANGELOG.md
@@ -7,8 +7,10 @@
- Fix energy total counters (#9263, #9266)
- Fix crash in ``ZbRestore``
- Fix reset BMP sensors when executing command ``SaveData`` and define USE_DEEPSLEEP enabled (#9300)
-- Add new shutter modes (#9244)
+- Add command ``SetOption110 1`` to disable Zigbee auto-config when pairing new devices
+- Add command ``SetOption111 1`` to enable frequency output for buzzer GPIO (#8994)
- Add ``#define USE_MQTT_AWS_IOT_LIGHT`` for password based AWS IoT authentication
+- Add new shutter modes (#9244)
- Add Zigbee auto-config when pairing
- Add support for MLX90640 IR array temperature sensor by Christian Baars
diff --git a/tools/decode-status.py b/tools/decode-status.py
index cd32a5402..3a664b28c 100755
--- a/tools/decode-status.py
+++ b/tools/decode-status.py
@@ -163,7 +163,9 @@ a_setoption = [[
"Select virtual White as (0) Warm or (1) Cold",
"Enable Teleinfo telemetry into Tasmota Energy MQTT (0) or Teleinfo only (1)",
"Force gen1 Alexa mode",
- "","","",""
+ "Disable Zigbee auto-config when pairing new devices",
+ "Use frequency output for buzzer pin instead of on/off signal",
+ "",""
],[
"","","","",
"","","","",
@@ -265,7 +267,7 @@ else:
obj = json.load(fp)
def StartDecode():
- print ("\n*** decode-status.py v20200906 by Theo Arends and Jacek Ziolkowski ***")
+ print ("\n*** decode-status.py v20200915 by Theo Arends and Jacek Ziolkowski ***")
# print("Decoding\n{}".format(obj))
From f7ae554394fe7518bfba6da888a23ac9413d5b4d Mon Sep 17 00:00:00 2001
From: Theo Arends <11044339+arendst@users.noreply.github.com>
Date: Tue, 15 Sep 2020 12:02:27 +0200
Subject: [PATCH 053/148] Allow display of date in US format
Allow display of date in US format (#9260)
---
tasmota/xdrv_13_display.ino | 13 ++++++++++---
1 file changed, 10 insertions(+), 3 deletions(-)
diff --git a/tasmota/xdrv_13_display.ino b/tasmota/xdrv_13_display.ino
index 8a0c5d395..7b5b34054 100644
--- a/tasmota/xdrv_13_display.ino
+++ b/tasmota/xdrv_13_display.ino
@@ -624,12 +624,19 @@ void DisplayText(void)
}
}
break;
- case 'T':
+ case 'T': {
+ uint8_t param1 = RtcTime.day_of_month;
+ uint8_t param2 = RtcTime.month;
+ if (*cp=='U') {
+ cp++;
+ param1 = RtcTime.month;
+ param2 = RtcTime.day_of_month;
+ }
if (dp < (linebuf + DISPLAY_BUFFER_COLS) -8) {
- snprintf_P(dp, 9, PSTR("%02d" D_MONTH_DAY_SEPARATOR "%02d" D_YEAR_MONTH_SEPARATOR "%02d"), RtcTime.day_of_month, RtcTime.month, RtcTime.year%2000);
+ snprintf_P(dp, 9, PSTR("%02d" D_MONTH_DAY_SEPARATOR "%02d" D_YEAR_MONTH_SEPARATOR "%02d"), param1, param2, RtcTime.year%2000);
dp += 8;
}
- break;
+ break; }
case 'd':
// force draw grafics buffer
if (renderer) renderer->Updateframe();
From 1418af3f31895a15bf2dfcbcae652b7583181a68 Mon Sep 17 00:00:00 2001
From: halfbakery
Date: Tue, 15 Sep 2020 21:49:03 +0200
Subject: [PATCH 054/148] Fix C2 programmer, incorrect initialization could
brick EFM8 MCUs
---
lib/C2Programmer-1.0.0/src/c2.cpp | 22 +++++++++++++++++++++-
lib/C2Programmer-1.0.0/src/c2.h | 9 ++++++++-
tasmota/xdrv_06_snfbridge.ino | 4 ++--
3 files changed, 31 insertions(+), 4 deletions(-)
diff --git a/lib/C2Programmer-1.0.0/src/c2.cpp b/lib/C2Programmer-1.0.0/src/c2.cpp
index 22bfbaedd..fd7e41af0 100644
--- a/lib/C2Programmer-1.0.0/src/c2.cpp
+++ b/lib/C2Programmer-1.0.0/src/c2.cpp
@@ -204,13 +204,33 @@ uint8_t c2_reset() {
return C2_SUCCESS;
}
-uint8_t c2_programming_init() {
+uint8_t c2_programming_init(uint8_t devid) {
c2_reset();
c2_address_write(C2FPCTL);
C2_DATA_WRITE_AND_CHECK(C2FPCTL_ENABLE0, 1);
C2_DATA_WRITE_AND_CHECK(C2FPCTL_CORE_HALT, 1);
C2_DATA_WRITE_AND_CHECK(C2FPCTL_ENABLE1, 1)
C2_DELAY_MS(21);
+
+ // device specific initialization, see https://www.silabs.com/documents/public/application-notes/AN127.pdf
+ switch (devid) {
+ case C2_DEVID_UNKNOWN:
+ break;
+ case C2_DEVID_EFM8BB1:
+ case C2_DEVID_EFM8BB2:
+ case C2_DEVID_EFM8BB3: // C2_DEVID_EFM8LB1 is the same
+ c2_address_write(0xFF);
+ C2_DATA_WRITE_AND_CHECK(0x80, 1);
+ C2_DELAY_US(5);
+ c2_address_write(0xEF);
+ C2_DATA_WRITE_AND_CHECK(0x02, 1);
+ c2_address_write(0xA9);
+ C2_DATA_WRITE_AND_CHECK(0x00, 1);
+ break;
+ default:
+ return C2_BROKEN_LINK;
+ }
+
return C2_SUCCESS;
}
diff --git a/lib/C2Programmer-1.0.0/src/c2.h b/lib/C2Programmer-1.0.0/src/c2.h
index bc744d02f..0b80c95f3 100644
--- a/lib/C2Programmer-1.0.0/src/c2.h
+++ b/lib/C2Programmer-1.0.0/src/c2.h
@@ -103,6 +103,13 @@ inline void C2D_enable(bool oe) {
#define C2_INBUSY 0x02
#define C2_OUTREADY 0x01
+// Device families (https://www.silabs.com/documents/public/application-notes/AN127.pdf)
+#define C2_DEVID_UNKNOWN 0x00
+#define C2_DEVID_EFM8BB1 0x30
+#define C2_DEVID_EFM8BB2 0x32
+#define C2_DEVID_EFM8BB3 0x34
+#define C2_DEVID_EFM8LB1 0x34
+
// Layer 1: C2 Programmig Interface (PI) Register access
void c2_address_write(uint8_t address);
uint8_t c2_address_read();
@@ -125,7 +132,7 @@ inline uint8_t c2_data_read(uint8_t &d, uint8_t bytes=1) {
// Layer 2: Operations
uint8_t c2_reset();
-uint8_t c2_programming_init();
+uint8_t c2_programming_init(uint8_t devid);
uint8_t c2_block_write(uint32_t address, uint8_t *data, uint8_t len);
uint8_t c2_block_read(uint32_t address, uint8_t *data, uint8_t len);
uint8_t c2_eeprom_read(uint32_t address, uint8_t *data, uint8_t len);
diff --git a/tasmota/xdrv_06_snfbridge.ino b/tasmota/xdrv_06_snfbridge.ino
index af6f928bd..228564a45 100644
--- a/tasmota/xdrv_06_snfbridge.ino
+++ b/tasmota/xdrv_06_snfbridge.ino
@@ -127,7 +127,7 @@ ssize_t rf_decode_and_write(uint8_t *record, size_t size)
uint16_t address = h->address_high * 0x100 + h->address_low;
do {
- err = c2_programming_init();
+ err = c2_programming_init(C2_DEVID_EFM8BB1);
err = c2_block_write(address, h->data, h->len);
} while (err != C2_SUCCESS && retries--);
} else if (h->record_type == IHX_RT_END_OF_FILE) {
@@ -179,7 +179,7 @@ uint8_t rf_erase_flash(void)
uint8_t err;
for (uint32_t i = 0; i < 4; i++) { // HACK: Try multiple times as the command sometimes fails (unclear why)
- err = c2_programming_init();
+ err = c2_programming_init(C2_DEVID_EFM8BB1);
if (err != C2_SUCCESS) {
return 10; // Failed to init RF chip
}
From f0baa04519d221a002080c46f632fb17cfaa3c22 Mon Sep 17 00:00:00 2001
From: Staars
Date: Wed, 16 Sep 2020 08:03:55 +0200
Subject: [PATCH 055/148] MI32: support for ATC custom FW
---
tasmota/xsns_62_MI_ESP32.ino | 44 ++++++++++++++++++++++++++++++++----
1 file changed, 40 insertions(+), 4 deletions(-)
diff --git a/tasmota/xsns_62_MI_ESP32.ino b/tasmota/xsns_62_MI_ESP32.ino
index 06d1afd05..579f8a785 100644
--- a/tasmota/xsns_62_MI_ESP32.ino
+++ b/tasmota/xsns_62_MI_ESP32.ino
@@ -20,6 +20,8 @@
--------------------------------------------------------------------------------------------
Version yyyymmdd Action Description
--------------------------------------------------------------------------------------------
+ 0.9.1.3 20200916 changed - add ATC (custom FW for LYWS03MMC), API adaption for NimBLE-Arduino 1.0.2
+ -------
0.9.1.2 20200802 changed - add MHO-C303
-------
0.9.1.1 20200715 changed - add MHO-C401, refactoring
@@ -164,6 +166,15 @@ union mi_bindKey_t{
uint8_t buf[22];
};
+struct ATCPacket_t{
+ uint8_t MAC[6];
+ int16_t temp; //sadly this is in wrong endianess
+ uint8_t hum;
+ uint8_t batPer;
+ uint16_t batMV;
+ uint8_t frameCnt;
+};
+
#pragma pack(0)
struct mi_sensor_t{
@@ -253,8 +264,9 @@ const char kMI32_Commands[] PROGMEM = "Period|Time|Page|Battery|Unit
#define YEERC 9
#define MHOC401 10
#define MHOC303 11
+#define ATC 12
-#define MI32_TYPES 11 //count this manually
+#define MI32_TYPES 12 //count this manually
const uint16_t kMI32DeviceID[MI32_TYPES]={ 0x0098, // Flora
0x01aa, // MJ_HT_V1
@@ -266,7 +278,8 @@ const uint16_t kMI32DeviceID[MI32_TYPES]={ 0x0098, // Flora
0x07f6, // MJYD2S
0x0153, // yee-rc
0x0387, // MHO-C401
- 0x06d3 // MHO-C303
+ 0x06d3, // MHO-C303
+ 0x0a1c // ATC -> this is a fake ID
};
const char kMI32DeviceType1[] PROGMEM = "Flora";
@@ -280,7 +293,8 @@ const char kMI32DeviceType8[] PROGMEM = "MJYD2S";
const char kMI32DeviceType9[] PROGMEM = "YEERC";
const char kMI32DeviceType10[] PROGMEM ="MHOC401";
const char kMI32DeviceType11[] PROGMEM ="MHOC303";
-const char * kMI32DeviceType[] PROGMEM = {kMI32DeviceType1,kMI32DeviceType2,kMI32DeviceType3,kMI32DeviceType4,kMI32DeviceType5,kMI32DeviceType6,kMI32DeviceType7,kMI32DeviceType8,kMI32DeviceType9,kMI32DeviceType10,kMI32DeviceType11};
+const char kMI32DeviceType12[] PROGMEM ="ATC";
+const char * kMI32DeviceType[] PROGMEM = {kMI32DeviceType1,kMI32DeviceType2,kMI32DeviceType3,kMI32DeviceType4,kMI32DeviceType5,kMI32DeviceType6,kMI32DeviceType7,kMI32DeviceType8,kMI32DeviceType9,kMI32DeviceType10,kMI32DeviceType11,kMI32DeviceType12};
/*********************************************************************************************\
* enumerations
@@ -355,9 +369,12 @@ class MI32AdvCallbacks: public NimBLEAdvertisedDeviceCallbacks {
else if(uuid==0xfdcd) {
MI32parseCGD1Packet((char*)advertisedDevice->getServiceData(0).data(),advertisedDevice->getServiceData(0).length(), addr, rssi);
}
+ else if(uuid==0x181a) { //ATC
+ MI32ParseATCPacket((char*)advertisedDevice->getServiceData(0).data(),advertisedDevice->getServiceData(0).length(), addr, rssi);
+ }
else {
+ // AddLog_P2(LOG_LEVEL_DEBUG,PSTR("No Xiaomi Device: %x: %s Buffer: %u"), uuid, advertisedDevice->getAddress().toString().c_str(),advertisedDevice->getServiceData(0).length());
MI32Scan->erase(advertisedDevice->getAddress());
- // AddLog_P2(LOG_LEVEL_DEBUG,PSTR("No Xiaomi Device: %s Buffer: %u"),advertisedDevice->getAddress().toString().c_str(),advertisedDevice->getServiceData(0).length());
}
};
};
@@ -1251,6 +1268,25 @@ if (MIBLEsensors[_slot].type==NLIGHT){
MI32.mode.shallTriggerTele = 1;
}
+void MI32ParseATCPacket(char * _buf, uint32_t length, uint8_t addr[6], int rssi){
+ ATCPacket_t *_packet = (ATCPacket_t*)_buf;
+ uint32_t _slot = MIBLEgetSensorSlot(_packet->MAC, 0x0a1c, _packet->frameCnt); // This must be a hard-coded fake ID
+ AddLog_P2(LOG_LEVEL_DEBUG,PSTR("%s at slot %u"), kMI32DeviceType[MIBLEsensors[_slot].type-1],_slot);
+ if(_slot==0xff) return;
+
+ MIBLEsensors[_slot].rssi=rssi;
+
+ MIBLEsensors.at(_slot).temp = (float)(__builtin_bswap16(_packet->temp))/10.0f;
+ MIBLEsensors.at(_slot).hum = (float)_packet->hum;
+ MIBLEsensors[_slot].eventType.tempHum = 1;
+ MIBLEsensors.at(_slot).bat = _packet->batPer;
+ MIBLEsensors[_slot].eventType.bat = 1;
+
+ MIBLEsensors[_slot].shallSendMQTT = 1;
+ MI32.mode.shallTriggerTele = 1;
+
+}
+
void MI32parseCGD1Packet(char * _buf, uint32_t length, uint8_t addr[6], int rssi){ // no MiBeacon
uint8_t _addr[6];
memcpy(_addr,addr,6);
From 06e9c423e64bcb5b96137a805b477e66bcffcb40 Mon Sep 17 00:00:00 2001
From: Staars
Date: Wed, 16 Sep 2020 08:06:27 +0200
Subject: [PATCH 056/148] typo
---
tasmota/xsns_62_MI_ESP32.ino | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/tasmota/xsns_62_MI_ESP32.ino b/tasmota/xsns_62_MI_ESP32.ino
index 579f8a785..2cbb653ee 100644
--- a/tasmota/xsns_62_MI_ESP32.ino
+++ b/tasmota/xsns_62_MI_ESP32.ino
@@ -20,7 +20,7 @@
--------------------------------------------------------------------------------------------
Version yyyymmdd Action Description
--------------------------------------------------------------------------------------------
- 0.9.1.3 20200916 changed - add ATC (custom FW for LYWS03MMC), API adaption for NimBLE-Arduino 1.0.2
+ 0.9.1.3 20200916 changed - add ATC (custom FW for LYWSD03MMC), API adaption for NimBLE-Arduino 1.0.2
-------
0.9.1.2 20200802 changed - add MHO-C303
-------
From 27b75e638ed7f02ee406742d24b2eace9e0e992a Mon Sep 17 00:00:00 2001
From: Stephan Hadinger
Date: Wed, 16 Sep 2020 20:32:03 +0200
Subject: [PATCH 057/148] Simplified JSON
---
tasmota/xdrv_23_zigbee_1z_libs.ino | 843 +++++++++++++++------------
tasmota/xdrv_23_zigbee_2_devices.ino | 34 +-
2 files changed, 497 insertions(+), 380 deletions(-)
diff --git a/tasmota/xdrv_23_zigbee_1z_libs.ino b/tasmota/xdrv_23_zigbee_1z_libs.ino
index 1f9012152..8b64baa5b 100644
--- a/tasmota/xdrv_23_zigbee_1z_libs.ino
+++ b/tasmota/xdrv_23_zigbee_1z_libs.ino
@@ -64,6 +64,46 @@ uint16_t Z_GetLastGroup(void) { return gZbLastMessage.groupaddr; }
uint16_t Z_GetLastCluster(void) { return gZbLastMessage.cluster; }
uint8_t Z_GetLastEndpoint(void) { return gZbLastMessage.endpoint; }
+/*********************************************************************************************\
+ *
+ * Class for attribute array of values
+ * This is a helper function to generate a clean list of unsigned ints
+ *
+\*********************************************************************************************/
+
+class Z_json_array {
+public:
+
+ Z_json_array(): val("[]") {} // start with empty array
+ void add(uint32_t uval32) {
+ // remove trailing ']'
+ val.remove(val.length()-1);
+ if (val.length() > 1) { // if not empty, prefix with comma
+ val += ',';
+ }
+ val += uval32;
+ val += ']';
+ }
+ void addStrRaw(const char * sval) {
+ // remove trailing ']'
+ val.remove(val.length()-1);
+ if (val.length() > 1) { // if not empty, prefix with comma
+ val += ',';
+ }
+ val += sval;
+ val += ']';
+ }
+ void addStr(const char * sval) {
+ addStrRaw(EscapeJSONString(sval).c_str());
+ }
+ String &toString(void) {
+ return val;
+ }
+
+private :
+ String val;
+};
+
/*********************************************************************************************\
*
* Class for single attribute
@@ -79,7 +119,10 @@ enum class Za_type : uint8_t {
Za_float, // float 32, uses fval
// non-nummericals
Za_raw, // bytes buffer, uses bval
- Za_str // string, uses sval
+ Za_str, // string, uses sval
+ // sub_objects
+ Za_obj, // json sub-object
+ Za_arr, // array sub-object (string add-only)
};
class Z_attribute {
@@ -90,16 +133,18 @@ public:
struct {
uint16_t cluster;
uint16_t attr_id;
- } id;
- char * key;
+ } id;
+ char * key;
} key;
// attribute value
union {
- uint32_t uval32;
- int32_t ival32;
- float fval;
- SBuffer* bval;
- char* sval;
+ uint32_t uval32;
+ int32_t ival32;
+ float fval;
+ SBuffer* bval;
+ char* sval;
+ class Z_attribute_list * objval;
+ class Z_json_array * arrval;
} val;
Za_type type; // uint8_t in size, type of attribute, see above
bool key_is_str; // is the key a string?
@@ -139,389 +184,67 @@ public:
}
// free any allocated memoruy for values
- void freeVal(void) {
- switch (type) {
- case Za_type::Za_raw:
- if (val.bval) { delete val.bval; val.bval = nullptr; }
- break;
- case Za_type::Za_str:
- if (val.sval) { delete[] val.sval; val.sval = nullptr; }
- break;
- }
- }
+ void freeVal(void);
+
// free any allocated memoruy for keys
- void freeKey(void) {
- if (key_is_str && key.key && !key_is_pmem) { delete[] key.key; }
- key.key = nullptr;
- }
+ void freeKey(void);
// set key name
- void setKeyName(const char * _key, bool pmem = false) {
- freeKey();
- key_is_str = true;
- key_is_pmem = pmem;
- if (pmem) {
- key.key = (char*) _key;
- } else {
- setKeyName(_key, nullptr);
- }
- }
+ void setKeyName(const char * _key, bool pmem = false);
// provide two entries and concat
- void setKeyName(const char * _key, const char * _key2) {
- freeKey();
- key_is_str = true;
- key_is_pmem = false;
- if (_key) {
- size_t key_len = strlen_P(_key);
- if (_key2) {
- key_len += strlen_P(_key2);
- }
- key.key = new char[key_len+1];
- strcpy_P(key.key, _key);
- if (_key2) {
- strcat_P(key.key, _key2);
- }
- }
- }
+ void setKeyName(const char * _key, const char * _key2);
- void setKeyId(uint16_t cluster, uint16_t attr_id) {
- freeKey();
- key_is_str = false;
- key.id.cluster = cluster;
- key.id.attr_id = attr_id;
- }
+ void setKeyId(uint16_t cluster, uint16_t attr_id);
// Setters
- void setNone(void) {
- freeVal(); // free any previously allocated memory
- val.uval32 = 0;
- type = Za_type::Za_none;
- }
- void setUInt(uint32_t _val) {
- freeVal(); // free any previously allocated memory
- val.uval32 = _val;
- type = Za_type::Za_uint;
- }
- void setBool(bool _val) {
- freeVal(); // free any previously allocated memory
- val.uval32 = _val;
- type = Za_type::Za_bool;
- }
- void setInt(int32_t _val) {
- freeVal(); // free any previously allocated memory
- val.ival32 = _val;
- type = Za_type::Za_int;
- }
- void setFloat(float _val) {
- freeVal(); // free any previously allocated memory
- val.fval = _val;
- type = Za_type::Za_float;
- }
+ void setNone(void);
+ void setUInt(uint32_t _val);
+ void setBool(bool _val);
+ void setInt(int32_t _val);
+ void setFloat(float _val);
- void setBuf(const SBuffer &buf, size_t index, size_t len) {
- freeVal();
- if (len) {
- val.bval = new SBuffer(len);
- val.bval->addBuffer(buf.buf(index), len);
- }
- type = Za_type::Za_raw;
- }
+ void setBuf(const SBuffer &buf, size_t index, size_t len);
// set the string value
// PMEM argument is allowed
// string will be copied, so it can be changed later
// nullptr is allowed and considered as empty string
// Note: memory is allocated only if string is non-empty
- void setStr(const char * _val) {
- freeVal(); // free any previously allocated memory
- val_str_raw = false;
- // val.sval is always nullptr after freeVal()
- if (_val) {
- size_t len = strlen_P(_val);
- if (len) {
- val.sval = new char[len+1];
- strcpy_P(val.sval, _val);
- }
- }
- type = Za_type::Za_str;
- }
+ void setStr(const char * _val);
inline void setStrRaw(const char * _val) {
setStr(_val);
val_str_raw = true;
}
+ Z_attribute_list & newAttrList(void);
+ Z_json_array & newJsonArray(void);
+
inline bool isNum(void) const { return (type >= Za_type::Za_bool) && (type <= Za_type::Za_float); }
inline bool isNone(void) const { return (type == Za_type::Za_none);}
// get num values
- float getFloat(void) const {
- switch (type) {
- case Za_type::Za_bool:
- case Za_type::Za_uint: return (float) val.uval32;
- case Za_type::Za_int: return (float) val.ival32;
- case Za_type::Za_float: return val.fval;
- default: return 0.0f;
- }
- }
-
- int32_t getInt(void) const {
- switch (type) {
- case Za_type::Za_bool:
- case Za_type::Za_uint: return (int32_t) val.uval32;
- case Za_type::Za_int: return val.ival32;
- case Za_type::Za_float: return (int32_t) val.fval;
- default: return 0;
- }
- }
-
- uint32_t getUInt(void) const {
- switch (type) {
- case Za_type::Za_bool:
- case Za_type::Za_uint: return val.uval32;
- case Za_type::Za_int: return (uint32_t) val.ival32;
- case Za_type::Za_float: return (uint32_t) val.fval;
- default: return 0;
- }
- }
-
- bool getBool(void) const {
- switch (type) {
- case Za_type::Za_bool:
- case Za_type::Za_uint: return val.uval32 ? true : false;
- case Za_type::Za_int: return val.ival32 ? true : false;
- case Za_type::Za_float: return val.fval ? true : false;
- default: return false;
- }
- }
-
- const SBuffer * getRaw(void) const {
- if (Za_type::Za_raw == type) { return val.bval; }
- return nullptr;
- }
+ float getFloat(void) const;
+ int32_t getInt(void) const;
+ uint32_t getUInt(void) const;
+ bool getBool(void) const;
+ const SBuffer * getRaw(void) const;
// always return a point to a string, if not defined then empty string.
// Never returns nullptr
- const char * getStr(void) const {
- if (Za_type::Za_str == type) { return val.sval; }
- return "";
- }
+ const char * getStr(void) const;
- bool equalsKey(const Z_attribute & attr2, bool ignore_key_suffix = false) const {
- // check if keys are equal
- if (key_is_str != attr2.key_is_str) { return false; }
- if (key_is_str) {
- if (strcmp_PP(key.key, attr2.key.key)) { return false; }
- } else {
- if ((key.id.cluster != attr2.key.id.cluster) ||
- (key.id.attr_id != attr2.key.id.attr_id)) { return false; }
- }
- if (!ignore_key_suffix) {
- if (key_suffix != attr2.key_suffix) { return false; }
- }
- return true;
- }
+ bool equalsKey(const Z_attribute & attr2, bool ignore_key_suffix = false) const;
+ bool equalsKey(uint16_t cluster, uint16_t attr_id, uint8_t suffix = 0) const;
+ bool equalsKey(const char * name, uint8_t suffix = 0) const;
+ bool equalsVal(const Z_attribute & attr2) const;
+ bool equals(const Z_attribute & attr2) const;
- bool equalsKey(uint16_t cluster, uint16_t attr_id, uint8_t suffix = 0) const {
- if (!key_is_str) {
- if ((key.id.cluster == cluster) && (key.id.attr_id == attr_id)) {
- if (suffix) {
- if (key_suffix == suffix) { return true; }
- } else {
- return true;
- }
- }
- }
- return false;
- }
-
- bool equalsKey(const char * name, uint8_t suffix = 0) const {
- if (key_is_str) {
- if (0 == strcmp_PP(key.key, name)) {
- if (suffix) {
- if (key_suffix == suffix) { return true; }
- } else {
- return true;
- }
- }
- }
- return false;
- }
-
- bool equalsVal(const Z_attribute & attr2) const {
- if (type != attr2.type) { return false; }
- if ((type >= Za_type::Za_bool) && (type <= Za_type::Za_float)) {
- // numerical value
- if (val.uval32 != attr2.val.uval32) { return false; }
- } else if (type == Za_type::Za_raw) {
- // compare 2 Static buffers
- return equalsSBuffer(val.bval, attr2.val.bval);
- } else if (type == Za_type::Za_str) {
- // if (val_str_raw != attr2.val_str_raw) { return false; }
- if (strcmp_PP(val.sval, attr2.val.sval)) { return false; }
- }
- return true;
- }
-
- bool equals(const Z_attribute & attr2) const {
- return equalsKey(attr2) && equalsVal(attr2);
- }
-
- String toString(bool prefix_comma = false) const {
- String res("");
- if (prefix_comma) { res += ','; }
- res += '"';
- // compute the attribute name
- if (key_is_str) {
- if (key.key) { res += EscapeJSONString(key.key); }
- else { res += F("null"); } // shouldn't happen
- if (key_suffix > 1) {
- res += key_suffix;
- }
- } else {
- char attr_name[12];
- snprintf_P(attr_name, sizeof(attr_name), PSTR("%04X/%04X"), key.id.cluster, key.id.attr_id);
- res += attr_name;
- if (key_suffix > 1) {
- res += '+';
- res += key_suffix;
- }
- }
- res += F("\":");
- // value part
- switch (type) {
- case Za_type::Za_none:
- res += "null";
- break;
- case Za_type::Za_bool:
- res += val.uval32 ? F("true") : F("false");
- break;
- case Za_type::Za_uint:
- res += val.uval32;
- break;
- case Za_type::Za_int:
- res += val.ival32;
- break;
- case Za_type::Za_float:
- {
- String fstr(val.fval, 2);
- size_t last = fstr.length() - 1;
- // remove trailing zeros
- while (fstr[last] == '0') {
- fstr.remove(last--);
- }
- // remove trailing dot
- if (fstr[last] == '.') {
- fstr.remove(last);
- }
- res += fstr;
- }
- break;
- case Za_type::Za_raw:
- res += '"';
- if (val.bval) {
- size_t blen = val.bval->len();
- // print as HEX
- char hex[2*blen+1];
- ToHex_P(val.bval->getBuffer(), blen, hex, sizeof(hex));
- res += hex;
- }
- res += '"';
- break;
- case Za_type::Za_str:
- if (val_str_raw) {
- if (val.sval) { res += val.sval; }
- } else {
- res += '"';
- if (val.sval) {
- res += EscapeJSONString(val.sval); // escape JSON chars
- }
- res += '"';
- }
- break;
- }
-
- return res;
- }
+ String toString(bool prefix_comma = false) const;
// copy value from one attribute to another, without changing its type
- void copyVal(const Z_attribute & rhs) {
- freeVal();
- // copy value
- val.uval32 = 0x00000000;
- type = rhs.type;
- if (rhs.isNum()) {
- val.uval32 = rhs.val.uval32;
- } else if (rhs.type == Za_type::Za_raw) {
- if (rhs.val.bval) {
- val.bval = new SBuffer(rhs.val.bval->len());
- val.bval->addBuffer(*(rhs.val.bval));
- }
- } else if (rhs.type == Za_type::Za_str) {
- if (rhs.val.sval) {
- size_t s_len = strlen_P(rhs.val.sval);
- val.sval = new char[s_len+1];
- strcpy_P(val.sval, rhs.val.sval);
- }
- }
- val_str_raw = rhs.val_str_raw;
- }
+ void copyVal(const Z_attribute & rhs);
protected:
- void deepCopy(const Z_attribute & rhs) {
- // copy key
- if (!rhs.key_is_str) {
- key.id.cluster = rhs.key.id.cluster;
- key.id.attr_id = rhs.key.id.attr_id;
- } else {
- if (rhs.key_is_pmem) {
- key.key = rhs.key.key; // PMEM, don't copy
- } else {
- key.key = nullptr;
- if (rhs.key.key) {
- size_t key_len = strlen_P(rhs.key.key);
- if (key_len) {
- key.key = new char[key_len+1];
- strcpy_P(key.key, rhs.key.key);
- }
- }
- }
- }
- key_is_str = rhs.key_is_str;
- key_is_pmem = rhs.key_is_pmem;
- key_suffix = rhs.key_suffix;
- attr_type = rhs.attr_type;
- attr_multiplier = rhs.attr_multiplier;
- // copy value
- copyVal(rhs);
- // don't touch next pointer
- }
-};
-
-/*********************************************************************************************\
- *
- * Class for attribute array of values
- * This is a helper function to generate a clean list of unsigned ints
- *
-\*********************************************************************************************/
-
-class Z_json_array {
-public:
-
- Z_json_array(): val("[]") {} // start with empty array
- void add(uint32_t uval32) {
- // remove trailing ']'
- val.remove(val.length()-1);
- if (val.length() > 1) { // if not empty, prefix with comma
- val += ',';
- }
- val += uval32;
- val += ']';
- }
- String &toString(void) {
- return val;
- }
-
-private :
- String val;
+ void deepCopy(const Z_attribute & rhs);
};
/*********************************************************************************************\
@@ -613,6 +336,406 @@ public:
bool mergeList(const Z_attribute_list &list2);
};
+/*********************************************************************************************\
+ *
+ * Implementation for Z_attribute
+ *
+\*********************************************************************************************/
+
+// free any allocated memoruy for keys
+void Z_attribute::freeKey(void) {
+ if (key_is_str && key.key && !key_is_pmem) { delete[] key.key; }
+ key.key = nullptr;
+}
+
+// set key name
+void Z_attribute::setKeyName(const char * _key, bool pmem) {
+ freeKey();
+ key_is_str = true;
+ key_is_pmem = pmem;
+ if (pmem) {
+ key.key = (char*) _key;
+ } else {
+ setKeyName(_key, nullptr);
+ }
+}
+// provide two entries and concat
+void Z_attribute::setKeyName(const char * _key, const char * _key2) {
+ freeKey();
+ key_is_str = true;
+ key_is_pmem = false;
+ if (_key) {
+ size_t key_len = strlen_P(_key);
+ if (_key2) {
+ key_len += strlen_P(_key2);
+ }
+ key.key = new char[key_len+1];
+ strcpy_P(key.key, _key);
+ if (_key2) {
+ strcat_P(key.key, _key2);
+ }
+ }
+}
+
+void Z_attribute::setKeyId(uint16_t cluster, uint16_t attr_id) {
+ freeKey();
+ key_is_str = false;
+ key.id.cluster = cluster;
+ key.id.attr_id = attr_id;
+}
+
+// Setters
+void Z_attribute::setNone(void) {
+ freeVal(); // free any previously allocated memory
+ val.uval32 = 0;
+ type = Za_type::Za_none;
+}
+void Z_attribute::setUInt(uint32_t _val) {
+ freeVal(); // free any previously allocated memory
+ val.uval32 = _val;
+ type = Za_type::Za_uint;
+}
+void Z_attribute::setBool(bool _val) {
+ freeVal(); // free any previously allocated memory
+ val.uval32 = _val;
+ type = Za_type::Za_bool;
+}
+void Z_attribute::setInt(int32_t _val) {
+ freeVal(); // free any previously allocated memory
+ val.ival32 = _val;
+ type = Za_type::Za_int;
+}
+void Z_attribute::setFloat(float _val) {
+ freeVal(); // free any previously allocated memory
+ val.fval = _val;
+ type = Za_type::Za_float;
+}
+
+void Z_attribute::setBuf(const SBuffer &buf, size_t index, size_t len) {
+ freeVal();
+ if (len) {
+ val.bval = new SBuffer(len);
+ val.bval->addBuffer(buf.buf(index), len);
+ }
+ type = Za_type::Za_raw;
+}
+
+// set the string value
+// PMEM argument is allowed
+// string will be copied, so it can be changed later
+// nullptr is allowed and considered as empty string
+// Note: memory is allocated only if string is non-empty
+void Z_attribute::setStr(const char * _val) {
+ freeVal(); // free any previously allocated memory
+ val_str_raw = false;
+ // val.sval is always nullptr after freeVal()
+ if (_val) {
+ size_t len = strlen_P(_val);
+ if (len) {
+ val.sval = new char[len+1];
+ strcpy_P(val.sval, _val);
+ }
+ }
+ type = Za_type::Za_str;
+}
+
+Z_attribute_list & Z_attribute::newAttrList(void) {
+ freeVal();
+ val.objval = new Z_attribute_list();
+ type = Za_type::Za_obj;
+ return *val.objval;
+}
+
+Z_json_array & Z_attribute::newJsonArray(void) {
+ freeVal();
+ val.arrval = new Z_json_array();
+ type = Za_type::Za_arr;
+ return *val.arrval;
+}
+
+// get num values
+float Z_attribute::getFloat(void) const {
+ switch (type) {
+ case Za_type::Za_bool:
+ case Za_type::Za_uint: return (float) val.uval32;
+ case Za_type::Za_int: return (float) val.ival32;
+ case Za_type::Za_float: return val.fval;
+ default: return 0.0f;
+ }
+}
+
+int32_t Z_attribute::getInt(void) const {
+ switch (type) {
+ case Za_type::Za_bool:
+ case Za_type::Za_uint: return (int32_t) val.uval32;
+ case Za_type::Za_int: return val.ival32;
+ case Za_type::Za_float: return (int32_t) val.fval;
+ default: return 0;
+ }
+}
+
+uint32_t Z_attribute::getUInt(void) const {
+ switch (type) {
+ case Za_type::Za_bool:
+ case Za_type::Za_uint: return val.uval32;
+ case Za_type::Za_int: return (uint32_t) val.ival32;
+ case Za_type::Za_float: return (uint32_t) val.fval;
+ default: return 0;
+ }
+}
+
+bool Z_attribute::getBool(void) const {
+ switch (type) {
+ case Za_type::Za_bool:
+ case Za_type::Za_uint: return val.uval32 ? true : false;
+ case Za_type::Za_int: return val.ival32 ? true : false;
+ case Za_type::Za_float: return val.fval ? true : false;
+ default: return false;
+ }
+}
+
+const SBuffer * Z_attribute::getRaw(void) const {
+ if (Za_type::Za_raw == type) { return val.bval; }
+ return nullptr;
+}
+
+// always return a point to a string, if not defined then empty string.
+// Never returns nullptr
+const char * Z_attribute::getStr(void) const {
+ if (Za_type::Za_str == type) { return val.sval; }
+ return "";
+}
+
+bool Z_attribute::equalsKey(const Z_attribute & attr2, bool ignore_key_suffix) const {
+ // check if keys are equal
+ if (key_is_str != attr2.key_is_str) { return false; }
+ if (key_is_str) {
+ if (strcmp_PP(key.key, attr2.key.key)) { return false; }
+ } else {
+ if ((key.id.cluster != attr2.key.id.cluster) ||
+ (key.id.attr_id != attr2.key.id.attr_id)) { return false; }
+ }
+ if (!ignore_key_suffix) {
+ if (key_suffix != attr2.key_suffix) { return false; }
+ }
+ return true;
+}
+
+bool Z_attribute::equalsKey(uint16_t cluster, uint16_t attr_id, uint8_t suffix) const {
+ if (!key_is_str) {
+ if ((key.id.cluster == cluster) && (key.id.attr_id == attr_id)) {
+ if (suffix) {
+ if (key_suffix == suffix) { return true; }
+ } else {
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+bool Z_attribute::equalsKey(const char * name, uint8_t suffix) const {
+ if (key_is_str) {
+ if (0 == strcmp_PP(key.key, name)) {
+ if (suffix) {
+ if (key_suffix == suffix) { return true; }
+ } else {
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+bool Z_attribute::equalsVal(const Z_attribute & attr2) const {
+ if (type != attr2.type) { return false; }
+ if ((type >= Za_type::Za_bool) && (type <= Za_type::Za_float)) {
+ // numerical value
+ if (val.uval32 != attr2.val.uval32) { return false; }
+ } else if (type == Za_type::Za_raw) {
+ // compare 2 Static buffers
+ return equalsSBuffer(val.bval, attr2.val.bval);
+ } else if (type == Za_type::Za_str) {
+ // if (val_str_raw != attr2.val_str_raw) { return false; }
+ if (strcmp_PP(val.sval, attr2.val.sval)) { return false; }
+ } else if (type == Za_type::Za_obj) {
+ return false; // TODO for now we'll assume sub-objects are always different
+ } else if (type == Za_type::Za_arr) {
+ return false; // TODO for now we'll assume sub-objects are always different
+ }
+ return true;
+}
+
+bool Z_attribute::equals(const Z_attribute & attr2) const {
+ return equalsKey(attr2) && equalsVal(attr2);
+}
+
+String Z_attribute::toString(bool prefix_comma) const {
+ String res("");
+ if (prefix_comma) { res += ','; }
+ res += '"';
+ // compute the attribute name
+ if (key_is_str) {
+ if (key.key) { res += EscapeJSONString(key.key); }
+ else { res += F("null"); } // shouldn't happen
+ if (key_suffix > 1) {
+ res += key_suffix;
+ }
+ } else {
+ char attr_name[12];
+ snprintf_P(attr_name, sizeof(attr_name), PSTR("%04X/%04X"), key.id.cluster, key.id.attr_id);
+ res += attr_name;
+ if (key_suffix > 1) {
+ res += '+';
+ res += key_suffix;
+ }
+ }
+ res += F("\":");
+ // value part
+ switch (type) {
+ case Za_type::Za_none:
+ res += "null";
+ break;
+ case Za_type::Za_bool:
+ res += val.uval32 ? F("true") : F("false");
+ break;
+ case Za_type::Za_uint:
+ res += val.uval32;
+ break;
+ case Za_type::Za_int:
+ res += val.ival32;
+ break;
+ case Za_type::Za_float:
+ {
+ String fstr(val.fval, 2);
+ size_t last = fstr.length() - 1;
+ // remove trailing zeros
+ while (fstr[last] == '0') {
+ fstr.remove(last--);
+ }
+ // remove trailing dot
+ if (fstr[last] == '.') {
+ fstr.remove(last);
+ }
+ res += fstr;
+ }
+ break;
+ case Za_type::Za_raw:
+ res += '"';
+ if (val.bval) {
+ size_t blen = val.bval->len();
+ // print as HEX
+ char hex[2*blen+1];
+ ToHex_P(val.bval->getBuffer(), blen, hex, sizeof(hex));
+ res += hex;
+ }
+ res += '"';
+ break;
+ case Za_type::Za_str:
+ if (val_str_raw) {
+ if (val.sval) { res += val.sval; }
+ } else {
+ res += '"';
+ if (val.sval) {
+ res += EscapeJSONString(val.sval); // escape JSON chars
+ }
+ res += '"';
+ }
+ break;
+ case Za_type::Za_obj:
+ res += '{';
+ if (val.objval) {
+ res += val.objval->toString();
+ }
+ res += '}';
+ break;
+ case Za_type::Za_arr:
+ if (val.arrval) {
+ res += val.arrval->toString();
+ } else {
+ res += "[]";
+ }
+ break;
+ }
+
+ return res;
+}
+
+// copy value from one attribute to another, without changing its type
+void Z_attribute::copyVal(const Z_attribute & rhs) {
+ freeVal();
+ // copy value
+ val.uval32 = 0x00000000;
+ type = rhs.type;
+ if (rhs.isNum()) {
+ val.uval32 = rhs.val.uval32;
+ } else if (rhs.type == Za_type::Za_raw) {
+ if (rhs.val.bval) {
+ val.bval = new SBuffer(rhs.val.bval->len());
+ val.bval->addBuffer(*(rhs.val.bval));
+ }
+ } else if (rhs.type == Za_type::Za_str) {
+ if (rhs.val.sval) {
+ size_t s_len = strlen_P(rhs.val.sval);
+ val.sval = new char[s_len+1];
+ strcpy_P(val.sval, rhs.val.sval);
+ }
+ }
+ val_str_raw = rhs.val_str_raw;
+}
+
+// free any allocated memoruy for values
+void Z_attribute::freeVal(void) {
+ switch (type) {
+ case Za_type::Za_raw:
+ if (val.bval) { delete val.bval; val.bval = nullptr; }
+ break;
+ case Za_type::Za_str:
+ if (val.sval) { delete[] val.sval; val.sval = nullptr; }
+ break;
+ case Za_type::Za_obj:
+ if (val.objval) { delete val.objval; val.objval = nullptr; }
+ break;
+ case Za_type::Za_arr:
+ if (val.arrval) { delete val.arrval; val.arrval = nullptr; }
+ break;
+ }
+}
+
+void Z_attribute::deepCopy(const Z_attribute & rhs) {
+ // copy key
+ if (!rhs.key_is_str) {
+ key.id.cluster = rhs.key.id.cluster;
+ key.id.attr_id = rhs.key.id.attr_id;
+ } else {
+ if (rhs.key_is_pmem) {
+ key.key = rhs.key.key; // PMEM, don't copy
+ } else {
+ key.key = nullptr;
+ if (rhs.key.key) {
+ size_t key_len = strlen_P(rhs.key.key);
+ if (key_len) {
+ key.key = new char[key_len+1];
+ strcpy_P(key.key, rhs.key.key);
+ }
+ }
+ }
+ }
+ key_is_str = rhs.key_is_str;
+ key_is_pmem = rhs.key_is_pmem;
+ key_suffix = rhs.key_suffix;
+ attr_type = rhs.attr_type;
+ attr_multiplier = rhs.attr_multiplier;
+ // copy value
+ copyVal(rhs);
+ // don't touch next pointer
+}
+
+/*********************************************************************************************\
+ *
+ * Implementation for Z_attribute_list
+ *
+\*********************************************************************************************/
// add a cluster/attr_id attribute at the end of the list
Z_attribute & Z_attribute_list::addAttribute(uint16_t cluster, uint16_t attr_id, uint8_t suffix) {
Z_attribute & attr = addToLast();
diff --git a/tasmota/xdrv_23_zigbee_2_devices.ino b/tasmota/xdrv_23_zigbee_2_devices.ino
index b84db3e2e..dbc459e38 100644
--- a/tasmota/xdrv_23_zigbee_2_devices.ino
+++ b/tasmota/xdrv_23_zigbee_2_devices.ino
@@ -992,12 +992,9 @@ String Z_Devices::dumpLightState(uint16_t shortaddr) const {
// Dump the internal memory of Zigbee devices
// Mode = 1: simple dump of devices addresses
-// Mode = 2: simple dump of devices addresses and names
-// Mode = 3: Mode 2 + also dump the endpoints, profiles and clusters
+// Mode = 2: simple dump of devices addresses and names, endpoints, light
String Z_Devices::dump(uint32_t dump_mode, uint16_t status_shortaddr) const {
- DynamicJsonBuffer jsonBuffer;
- JsonArray& json = jsonBuffer.createArray();
- JsonArray& devices = json;
+ Z_json_array json_arr;
for (const auto & device : _devices) {
uint16_t shortaddr = device.shortaddr;
@@ -1006,44 +1003,41 @@ String Z_Devices::dump(uint32_t dump_mode, uint16_t status_shortaddr) const {
// ignore non-current device, if device specified
if ((BAD_SHORTADDR != status_shortaddr) && (status_shortaddr != shortaddr)) { continue; }
- JsonObject& dev = devices.createNestedObject();
+ Z_attribute_list attr_list;
snprintf_P(hex, sizeof(hex), PSTR("0x%04X"), shortaddr);
- dev[F(D_JSON_ZIGBEE_DEVICE)] = hex;
+ attr_list.addAttribute(F(D_JSON_ZIGBEE_DEVICE)).setStr(hex);
if (device.friendlyName > 0) {
- dev[F(D_JSON_ZIGBEE_NAME)] = (char*) device.friendlyName;
+ attr_list.addAttribute(F(D_JSON_ZIGBEE_NAME)).setStr(device.friendlyName);
}
if (2 <= dump_mode) {
hex[0] = '0'; // prefix with '0x'
hex[1] = 'x';
Uint64toHex(device.longaddr, &hex[2], 64);
- dev[F("IEEEAddr")] = hex;
+ attr_list.addAttribute(F("IEEEAddr")).setStr(hex);
if (device.modelId) {
- dev[F(D_JSON_MODEL D_JSON_ID)] = device.modelId;
+ attr_list.addAttribute(F(D_JSON_MODEL D_JSON_ID)).setStr(device.modelId);
}
int8_t bulbtype = getHueBulbtype(shortaddr);
if (bulbtype >= 0) {
- dev[F(D_JSON_ZIGBEE_LIGHT)] = bulbtype; // sign extend, 0xFF changed as -1
+ attr_list.addAttribute(F(D_JSON_ZIGBEE_LIGHT)).setInt(bulbtype); // sign extend, 0xFF changed as -1
}
if (device.manufacturerId) {
- dev[F("Manufacturer")] = device.manufacturerId;
+ attr_list.addAttribute(F("Manufacturer")).setStr(device.manufacturerId);
}
- JsonArray& dev_endpoints = dev.createNestedArray(F("Endpoints"));
+ Z_json_array arr_ep;
for (uint32_t i = 0; i < endpoints_max; i++) {
uint8_t endpoint = device.endpoints[i];
if (0x00 == endpoint) { break; }
-
- snprintf_P(hex, sizeof(hex), PSTR("0x%02X"), endpoint);
- dev_endpoints.add(hex);
+ arr_ep.add(endpoint);
}
+ attr_list.addAttribute(F("Endpoints")).setStrRaw(arr_ep.toString().c_str());
}
+ json_arr.addStrRaw(attr_list.toString(true).c_str());
}
- String payload = "";
- payload.reserve(200);
- json.printTo(payload);
- return payload;
+ return json_arr.toString();
}
// Restore a single device configuration based on json export
From e45a5f33234bf05b70427a3b6fee586b2bf3f899 Mon Sep 17 00:00:00 2001
From: Federico Leoni
Date: Wed, 16 Sep 2020 15:45:56 -0300
Subject: [PATCH 058/148] TuyaMCU Update
---
tasmota/settings.h | 6 +-
tasmota/settings.ino | 4 +-
tasmota/tasmota.h | 9 +-
tasmota/xdrv_01_webserver.ino | 72 ++++++
tasmota/xdrv_16_tuyamcu.ino | 453 ++++++++++++++++++++++++++--------
5 files changed, 431 insertions(+), 113 deletions(-)
diff --git a/tasmota/settings.h b/tasmota/settings.h
index 2ceadd523..0803bcca2 100644
--- a/tasmota/settings.h
+++ b/tasmota/settings.h
@@ -129,8 +129,8 @@ typedef union { // Restricted by MISRA-C Rule 18.4 bu
uint32_t virtual_ct_cw : 1; // bit 25 (v8.4.0.1) - SetOption107 - Virtual CT Channel - signals whether the hardware white is cold CW (true) or warm WW (false)
uint32_t teleinfo_rawdata : 1; // bit 26 (v8.4.0.2) - SetOption108 - enable Teleinfo + Tasmota Energy device (0) or Teleinfo raw data only (1)
uint32_t alexa_gen_1 : 1; // bit 27 (v8.4.0.3) - SetOption109 - Alexa gen1 mode - if you only have Echo Dot 2nd gen devices
- uint32_t spare28 : 1; // bit 28
- uint32_t spare29 : 1; // bit 29
+ uint32_t zb_disable_autobind : 1; // bit 28 (v8.5.0.1) - SetOption110 - disable Zigbee auto-config when pairing new devices
+ uint32_t buzzer_freq_mode : 1; // bit 29 (v8.5.0.1) - SetOption111 - Use frequency output for buzzer pin instead of on/off signal
uint32_t spare30 : 1; // bit 30
uint32_t spare31 : 1; // bit 31
};
@@ -389,7 +389,7 @@ struct {
uint8_t display_font; // 312
char ex_state_text[4][11]; // 313
- uint8_t ex_energy_power_delta; // 33F - Free since 6.6.0.20
+ uint8_t tuyamcu_topic; // 33F Manage tuyaSend topic. ex_energy_power_delta on 6.6.0.20, replaced on 8.5.0.1
uint16_t domoticz_update_timer; // 340
uint16_t pwm_range; // 342
diff --git a/tasmota/settings.ino b/tasmota/settings.ino
index a26c89b3d..2c17cbad2 100644
--- a/tasmota/settings.ino
+++ b/tasmota/settings.ino
@@ -1356,8 +1356,8 @@ void SettingsDelta(void)
Settings.ex_sbaudrate = 0;
*/
Settings.flag3.fast_power_cycle_disable = 0;
- Settings.ex2_energy_power_delta = Settings.ex_energy_power_delta;
- Settings.ex_energy_power_delta = 0;
+ Settings.ex2_energy_power_delta = Settings.tuyamcu_topic;
+ Settings.tuyamcu_topic = 0; // replaced ex_energy_power_delta on 8.5.0.1
}
if (Settings.version < 0x06060015) {
if ((EX_WIFI_SMARTCONFIG == Settings.ex_sta_config) || (EX_WIFI_WPSCONFIG == Settings.ex_sta_config)) {
diff --git a/tasmota/tasmota.h b/tasmota/tasmota.h
index 96cd9e49c..cffb9e57c 100644
--- a/tasmota/tasmota.h
+++ b/tasmota/tasmota.h
@@ -353,10 +353,13 @@ const SerConfu8 kTasmotaSerialConfig[] PROGMEM = {
enum TuyaSupportedFunctions { TUYA_MCU_FUNC_NONE, TUYA_MCU_FUNC_SWT1 = 1, TUYA_MCU_FUNC_SWT2, TUYA_MCU_FUNC_SWT3, TUYA_MCU_FUNC_SWT4,
TUYA_MCU_FUNC_REL1 = 11, TUYA_MCU_FUNC_REL2, TUYA_MCU_FUNC_REL3, TUYA_MCU_FUNC_REL4, TUYA_MCU_FUNC_REL5,
- TUYA_MCU_FUNC_REL6, TUYA_MCU_FUNC_REL7, TUYA_MCU_FUNC_REL8, TUYA_MCU_FUNC_DIMMER = 21, TUYA_MCU_FUNC_POWER = 31,
- TUYA_MCU_FUNC_CURRENT, TUYA_MCU_FUNC_VOLTAGE, TUYA_MCU_FUNC_BATTERY_STATE, TUYA_MCU_FUNC_BATTERY_PERCENTAGE,
+ TUYA_MCU_FUNC_REL6, TUYA_MCU_FUNC_REL7, TUYA_MCU_FUNC_REL8, TUYA_MCU_FUNC_DIMMER = 21, TUYA_MCU_FUNC_DIMMER2,
+ TUYA_MCU_FUNC_CT, TUYA_MCU_FUNC_RGB, TUYA_MCU_FUNC_WHITE, TUYA_MCU_FUNC_MODESET, TUYA_MCU_FUNC_REPORT1, TUYA_MCU_FUNC_REPORT2,
+ TUYA_MCU_FUNC_POWER = 31, TUYA_MCU_FUNC_CURRENT, TUYA_MCU_FUNC_VOLTAGE, TUYA_MCU_FUNC_BATTERY_STATE, TUYA_MCU_FUNC_BATTERY_PERCENTAGE,
TUYA_MCU_FUNC_REL1_INV = 41, TUYA_MCU_FUNC_REL2_INV, TUYA_MCU_FUNC_REL3_INV, TUYA_MCU_FUNC_REL4_INV, TUYA_MCU_FUNC_REL5_INV,
- TUYA_MCU_FUNC_REL6_INV, TUYA_MCU_FUNC_REL7_INV, TUYA_MCU_FUNC_REL8_INV, TUYA_MCU_FUNC_LOWPOWER_MODE = 51, TUYA_MCU_FUNC_LAST = 255
+ TUYA_MCU_FUNC_REL6_INV, TUYA_MCU_FUNC_REL7_INV, TUYA_MCU_FUNC_REL8_INV, TUYA_MCU_FUNC_LOWPOWER_MODE = 51,
+ TUYA_MCU_FUNC_FAN3 = 61, TUYA_MCU_FUNC_FAN4, TUYA_MCU_FUNC_FAN5, TUYA_MCU_FUNC_FAN6,
+ TUYA_MCU_FUNC_MOTOR_DIR = 97, TUYA_MCU_FUNC_ERROR = 98 , TUYA_MCU_FUNC_DUMMY = 99, TUYA_MCU_FUNC_LAST = 255
};
#endif // _TASMOTA_H_
diff --git a/tasmota/xdrv_01_webserver.ino b/tasmota/xdrv_01_webserver.ino
index ed15c9e12..8d37f5183 100644
--- a/tasmota/xdrv_01_webserver.ino
+++ b/tasmota/xdrv_01_webserver.ino
@@ -1460,9 +1460,37 @@ void HandleRoot(void)
(set_button) ? SettingsText(SET_BUTTON1 + idx -1) : (devices_present < 5) ? D_BUTTON_TOGGLE : "",
(set_button) ? "" : (devices_present > 1) ? stemp : "");
}
+#ifdef USE_TUYA_MCU
+ if (IsModuleTuya()) {
+ uint8_t modeset = 0;
+ if (AsModuleTuyaMS()) {
+ WSContentSend_P(HTTP_TABLE100);
+ WSContentSend_P(PSTR(""));
+ snprintf_P(stemp, sizeof(stemp), PSTR("" D_JSON_IRHVAC_MODE ""));
+ WSContentSend_P(HTTP_DEVICE_CONTROL, 26, devices_present + 1,
+ (strlen(SettingsText(SET_BUTTON1 + devices_present))) ? SettingsText(SET_BUTTON1 + devices_present) : stemp, "");
+ WSContentSend_P(PSTR("
"));
+ modeset = 1;
+ }
+ if (IsTuyaFanCtrl()) {
+ uint8_t device = devices_present + modeset;
+ WSContentSend_P(HTTP_TABLE100);
+ WSContentSend_P(PSTR(""));
+ for (uint32_t i = device + 1; i <= (TuyaFanSpeeds() + device) + 1; i++) {
+ snprintf_P(stemp, sizeof(stemp), PSTR("%d"), i - (device + 1));
+ WSContentSend_P(HTTP_DEVICE_CONTROL, 16, i,
+ (strlen(SettingsText(SET_BUTTON1 + i))) ? SettingsText(SET_BUTTON1 + i) : stemp, "");
+ }
+ WSContentSend_P(PSTR("
"));
+ }
+ } else {
+#endif // USE_TUYA_MCU
+ }
+ WSContentSend_P(PSTR(""));
#ifdef USE_SONOFF_IFAN
}
#endif // USE_SONOFF_IFAN
+
WSContentSend_P(PSTR(""));
}
#ifdef USE_SONOFF_RF
@@ -1535,6 +1563,33 @@ bool HandleRootStatusRefresh(void)
}
} else {
#endif // USE_SONOFF_IFAN
+#ifdef USE_TUYA_MCU
+ if (IsModuleTuya()) {
+ uint8_t FuncIdx = 0;
+ if (device <= devices_present) {
+ ExecuteCommandPower(device, POWER_TOGGLE, SRC_IGNORE);
+ } else {
+ if (AsModuleTuyaMS() && device == devices_present + 1) {
+ uint8_t dpId = TuyaGetDpId(TUYA_MCU_FUNC_MODESET);
+ snprintf_P(svalue, sizeof(svalue), PSTR("Tuyasend4 %d,%d"), dpId, !TuyaModeSet());
+ ExecuteCommand(svalue, SRC_WEBGUI);
+ }
+ if (IsTuyaFanCtrl()) {
+ uint8_t dpId = 0;
+ for (uint32_t i = 0; i <= 3; i++) { // Tuya Function FAN3 to FAN6
+ if (TuyaGetDpId(TUYA_MCU_FUNC_FAN3 + i) != 0) {
+ dpId = TuyaGetDpId(TUYA_MCU_FUNC_FAN3 + i);
+ }
+ }
+ if ((AsModuleTuyaMS() && device != devices_present + 1) || !AsModuleTuyaMS()) {
+ if (AsModuleTuyaMS()) {FuncIdx = 1;}
+ snprintf_P(svalue, sizeof(svalue), PSTR("Tuyasend2 %d,%d"), dpId, (device - (devices_present + FuncIdx) - 1));
+ ExecuteCommand(svalue, SRC_WEBGUI);
+ }
+ }
+ }
+ } else {
+#endif // USE_TUYA_MCU
#ifdef USE_SHUTTER
int32_t ShutterWebButton;
if (ShutterWebButton = IsShutterWebButton(device)) {
@@ -1549,6 +1604,9 @@ bool HandleRootStatusRefresh(void)
#ifdef USE_SONOFF_IFAN
}
#endif // USE_SONOFF_IFAN
+#ifdef USE_TUYA_MCU
+ }
+#endif // USE_TUYA_MCU
}
#ifdef USE_LIGHT
WebGetArg("d0", tmp, sizeof(tmp)); // 0 - 100 Dimmer value
@@ -1629,8 +1687,22 @@ bool HandleRootStatusRefresh(void)
#ifdef USE_SONOFF_IFAN
}
#endif // USE_SONOFF_IFAN
+
WSContentSend_P(PSTR(""));
}
+#ifdef USE_TUYA_MCU
+ if (IsModuleTuya()) {
+ uint32_t fanspeed = TuyaFanState();
+ uint32_t modeset = TuyaModeSet();
+ if (IsTuyaFanCtrl() && !AsModuleTuyaMS()) {
+ WSContentSend_P(PSTR("" D_JSON_IRHVAC_FANSPEED ": %d
"), fanspeed);
+ } else if (!IsTuyaFanCtrl() && AsModuleTuyaMS()) {
+ WSContentSend_P(PSTR("" D_JSON_IRHVAC_MODE ": %d
"), modeset);
+ } else if (IsTuyaFanCtrl() && AsModuleTuyaMS()) {
+ WSContentSend_P(PSTR("" D_JSON_IRHVAC_MODE ": %d - " D_JSON_IRHVAC_FANSPEED ": %d
"), modeset, fanspeed);
+ }
+ }
+#endif // USE_TUYA_MCU
WSContentEnd();
return true;
diff --git a/tasmota/xdrv_16_tuyamcu.ino b/tasmota/xdrv_16_tuyamcu.ino
index b4b9a5ff4..2c4b9e0d6 100644
--- a/tasmota/xdrv_16_tuyamcu.ino
+++ b/tasmota/xdrv_16_tuyamcu.ino
@@ -1,7 +1,7 @@
/*
xdrv_16_tuyamcu.ino - Tuya MCU support for Tasmota
- Copyright (C) 2020 digiblur, Joel Stein and Theo Arends
+ Copyright (C) 2020 Federico Leoni, digiblur, Joel Stein and Theo Arends
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -55,56 +55,32 @@
TasmotaSerial *TuyaSerial = nullptr;
struct TUYA {
- uint16_t new_dim = 0; // Tuya dimmer value temp
- bool ignore_dim = false; // Flag to skip serial send to prevent looping when processing inbound states from the faceplate interaction
- uint8_t cmd_status = 0; // Current status of serial-read
- uint8_t cmd_checksum = 0; // Checksum of tuya command
- uint8_t data_len = 0; // Data lenght of command
+ uint16_t Levels[5] = {0,0,0,0,0}; // Array to store the values of TuyaMCU channels
+ uint16_t Snapshot[5] = {0,0,0,0,0}; // Array to store a snapshot of Tasmota actual values for channels
+ char HSBColor[13]; // Stores HSB Color string in Hex format
+ uint16_t CTMin = 153; // Minimum CT level allowed - When SetOption82 is enabled will default to 200
+ uint16_t CTMax = 500; // Maximum CT level allowed - When SetOption82 is enabled will default to 380
+ bool ModeSet = false; // Controls 0 - Single Tone light, 1 - RGB Light
+ uint8_t FanState = 0; // Stores the current fan speed
+ bool SuspendTopic = false; // Used to reduce the load at init time or when polling the configuraton on demand
+ uint32_t ignore_topic_timeout = 0; // Suppress the /STAT topic (if enabled) to avoid data overflow until the configuration is over
+ bool ignore_dim = false; // Flag to skip serial send to prevent looping when processing inbound states from the faceplate interaction
+ uint8_t cmd_status = 0; // Current status of serial-read
+ uint8_t cmd_checksum = 0; // Checksum of tuya command
+ uint8_t data_len = 0; // Data lenght of command
uint8_t wifi_state = -2; // Keep MCU wifi-status in sync with WifiState()
- uint8_t heartbeat_timer = 0; // 10 second heartbeat timer for tuya module
+ uint8_t heartbeat_timer = 0; // 10 second heartbeat timer for tuya module
#ifdef USE_ENERGY_SENSOR
- uint32_t lastPowerCheckTime = 0; // Time when last power was checked
+ uint32_t lastPowerCheckTime = 0; // Time when last power was checked
#endif // USE_ENERGY_SENSOR
- char *buffer = nullptr; // Serial receive buffer
- int byte_counter = 0; // Index in serial receive buffer
- bool low_power_mode = false; // Normal or Low power mode protocol
- bool send_success_next_second = false; // Second command success in low power mode
- uint32_t ignore_dimmer_cmd_timeout = 0;// Time until which received dimmer commands should be ignored
+ char *buffer = nullptr; // Serial receive buffer
+ int byte_counter = 0; // Index in serial receive buffer
+ bool low_power_mode = false; // Normal or Low power mode protocol
+ bool send_success_next_second = false; // Second command success in low power mode
+ uint32_t ignore_dimmer_cmd_timeout = 0; // Time until which received dimmer commands should be ignored
+ bool ignore_tuyareceived = false; // When a modeset changes ignore stat
} Tuya;
-
-// enum TuyaSupportedFunctions {
-// TUYA_MCU_FUNC_NONE,
-// TUYA_MCU_FUNC_SWT1 = 1, // Buttons
-// TUYA_MCU_FUNC_SWT2,
-// TUYA_MCU_FUNC_SWT3,
-// TUYA_MCU_FUNC_SWT4,
-// TUYA_MCU_FUNC_REL1 = 11, // Relays
-// TUYA_MCU_FUNC_REL2,
-// TUYA_MCU_FUNC_REL3,
-// TUYA_MCU_FUNC_REL4,
-// TUYA_MCU_FUNC_REL5,
-// TUYA_MCU_FUNC_REL6,
-// TUYA_MCU_FUNC_REL7,
-// TUYA_MCU_FUNC_REL8,
-// TUYA_MCU_FUNC_DIMMER = 21,
-// TUYA_MCU_FUNC_POWER = 31,
-// TUYA_MCU_FUNC_CURRENT,
-// TUYA_MCU_FUNC_VOLTAGE,
-// TUYA_MCU_FUNC_BATTERY_STATE,
-// TUYA_MCU_FUNC_BATTERY_PERCENTAGE,
-// TUYA_MCU_FUNC_REL1_INV = 41, // Inverted Relays
-// TUYA_MCU_FUNC_REL2_INV,
-// TUYA_MCU_FUNC_REL3_INV,
-// TUYA_MCU_FUNC_REL4_INV,
-// TUYA_MCU_FUNC_REL5_INV,
-// TUYA_MCU_FUNC_REL6_INV,
-// TUYA_MCU_FUNC_REL7_INV,
-// TUYA_MCU_FUNC_REL8_INV,
-// TUYA_MCU_FUNC_LOWPOWER_MODE = 51,
-// TUYA_MCU_FUNC_LAST = 255
-// };
-
const char kTuyaCommand[] PROGMEM = "|" // No prefix
D_CMND_TUYA_MCU "|" D_CMND_TUYA_MCU_SEND_STATE;
@@ -112,8 +88,48 @@ void (* const TuyaCommand[])(void) PROGMEM = {
&CmndTuyaMcu, &CmndTuyaSend
};
-/*
+/*********************************************************************************************\
+ * Web Interface
+\*********************************************************************************************/
+bool IsModuleTuya(void)
+{
+ return ((TUYA_DIMMER == my_module_type) || (SK03_TUYA == my_module_type));
+}
+bool AsModuleTuyaMS(void) // ModeSet Layout
+{
+ return ((light_type > LT_RGB) && TuyaGetDpId(TUYA_MCU_FUNC_MODESET) != 0);
+}
+
+bool IsTuyaFanCtrl(void) // Fan Speed Controller Layout
+{
+ return ((TuyaGetDpId(TUYA_MCU_FUNC_FAN3) != 0) || (TuyaGetDpId(TUYA_MCU_FUNC_FAN4) != 0) ||
+ (TuyaGetDpId(TUYA_MCU_FUNC_FAN5) != 0) || (TuyaGetDpId(TUYA_MCU_FUNC_FAN6) != 0));
+}
+
+bool TuyaModeSet(void) // ModeSet Status
+{
+ return Tuya.ModeSet;
+}
+
+uint8_t TuyaFanSpeeds(void) // Number of Fan Speeds for WebUI
+{
+ uint8_t FanSpeeds = 0;
+ for (uint32_t i = 0; i <= 3; i++) {
+ if (TuyaGetDpId(TUYA_MCU_FUNC_FAN3 + i) != 0) {
+ FanSpeeds = i + 2;
+ }
+ }
+ return FanSpeeds;
+}
+
+uint8_t TuyaFanState(void) // Fan Speed Status
+{
+ return Tuya.FanState;
+}
+// Web Interface
+
+/*
TuyaSend dpId,data
TuyaSend0 -> Sends TUYA_CMD_QUERY_STATE
@@ -122,16 +138,20 @@ TuyaSend2 11,100 -> Sends integer (Type 2) data 100 to dpId 11 (Max data length
TuyaSend2 11,0xAABBCCDD -> Sends 4 bytes (Type 2) data to dpId 11 (Max data length 4 bytes)
TuyaSend3 11,ThisIsTheData -> Sends the supplied string (Type 3) to dpId 11 ( Max data length not-known)
TuyaSend4 11,1 -> Sends enum (Type 4) data 0/1/2/3/4/5 to dpId 11 (Max data length 1 bytes)
-
*/
-
void CmndTuyaSend(void) {
- if (XdrvMailbox.index > 4) {
+ if (XdrvMailbox.index > 4 && XdrvMailbox.index < 8) {
return;
}
if (XdrvMailbox.index == 0) {
- TuyaRequestState();
+ TuyaRequestState(0);
+ } else if (XdrvMailbox.index == 8) {
+ TuyaRequestState(8);
+ } else if (XdrvMailbox.index == 9) { // TuyaSend Topic Toggle
+ if (Settings.tuyamcu_topic) { Settings.tuyamcu_topic = 0; } else { Settings.tuyamcu_topic = 1; }
+ AddLog_P2(LOG_LEVEL_INFO, PSTR("TYA: TuyaMCU Stat Topic %s"), (Settings.tuyamcu_topic ? PSTR("enabled") : PSTR("disabled")));
+
} else {
if (XdrvMailbox.data_len > 0) {
char *p;
@@ -161,11 +181,7 @@ void CmndTuyaSend(void) {
ResponseCmndDone();
}
-/*
-
-TuyaMcu fnid,dpid
-
-*/
+// TuyaMcu fnid,dpid
void CmndTuyaMcu(void) {
if (XdrvMailbox.data_len > 0) {
@@ -178,12 +194,28 @@ void CmndTuyaMcu(void) {
}
if (TuyaFuncIdValid(parm[0])) {
+ // TuyaAddMcuFunc(parm[0], parm[1]);
+ // restart_flag = 2;
+ // } else {
+ // AddLog_P2(LOG_LEVEL_ERROR, PSTR("TYA: TuyaMcu Invalid function id=%d"), parm[0]);
+ // }
+ bool DualDim;
+ if (TUYA_MCU_FUNC_DIMMER2 == parm[0] && parm[1] != 0) {
+ if (TuyaGetDpId(TUYA_MCU_FUNC_DIMMER) != 0) { DualDim = true; }
+ } else if (TUYA_MCU_FUNC_DIMMER == parm[0] && parm[1] != 0) {
+ if (TuyaGetDpId(TUYA_MCU_FUNC_DIMMER2) != 0) { DualDim = true; }
+ } else if ((TUYA_MCU_FUNC_DIMMER == parm[0] && parm[1] == 0) || (TUYA_MCU_FUNC_DIMMER2 == parm[0] && parm[1] == 0)) { DualDim = false; };
+ if (DualDim) {
+ if (TuyaGetDpId(TUYA_MCU_FUNC_CT) != 0) { TuyaAddMcuFunc(TUYA_MCU_FUNC_CT, 0); } // If the second dimmer is enabled CT, RGB or WHITE function must be removed
+ if (TuyaGetDpId(TUYA_MCU_FUNC_RGB) != 0) { TuyaAddMcuFunc(TUYA_MCU_FUNC_RGB, 0); }
+ if (TuyaGetDpId(TUYA_MCU_FUNC_WHITE) != 0) { TuyaAddMcuFunc(TUYA_MCU_FUNC_WHITE, 0); }
+ Settings.flag3.pwm_multi_channels = 1;
+ } else { Settings.flag3.pwm_multi_channels = 0; }
TuyaAddMcuFunc(parm[0], parm[1]);
restart_flag = 2;
} else {
AddLog_P2(LOG_LEVEL_ERROR, PSTR("TYA: TuyaMcu Invalid function id=%d"), parm[0]);
}
-
}
Response_P(PSTR("{\"" D_CMND_TUYA_MCU "\":["));
@@ -249,11 +281,13 @@ void UpdateDevices() {
inline bool TuyaFuncIdValid(uint8_t fnId) {
return (fnId >= TUYA_MCU_FUNC_SWT1 && fnId <= TUYA_MCU_FUNC_SWT4) ||
- (fnId >= TUYA_MCU_FUNC_REL1 && fnId <= TUYA_MCU_FUNC_REL8) ||
- fnId == TUYA_MCU_FUNC_DIMMER ||
- (fnId >= TUYA_MCU_FUNC_POWER && fnId <= TUYA_MCU_FUNC_VOLTAGE) ||
- (fnId >= TUYA_MCU_FUNC_REL1_INV && fnId <= TUYA_MCU_FUNC_REL8_INV) ||
- (fnId == TUYA_MCU_FUNC_LOWPOWER_MODE);
+ (fnId >= TUYA_MCU_FUNC_REL1 && fnId <= TUYA_MCU_FUNC_REL8) ||
+ (fnId >= TUYA_MCU_FUNC_DIMMER && fnId <= TUYA_MCU_FUNC_REPORT2) ||
+ (fnId >= TUYA_MCU_FUNC_POWER && fnId <= TUYA_MCU_FUNC_BATTERY_PERCENTAGE) ||
+ (fnId >= TUYA_MCU_FUNC_REL1_INV && fnId <= TUYA_MCU_FUNC_REL8_INV) ||
+ (fnId >= TUYA_MCU_FUNC_FAN3 && fnId <= TUYA_MCU_FUNC_FAN6) ||
+ (fnId >= TUYA_MCU_FUNC_MOTOR_DIR && fnId <= TUYA_MCU_FUNC_DUMMY) ||
+ (fnId == TUYA_MCU_FUNC_LOWPOWER_MODE);
}
uint8_t TuyaGetFuncId(uint8_t dpid) {
@@ -376,38 +410,137 @@ bool TuyaSetPower(void)
bool TuyaSetChannels(void)
{
- LightSerialDuty(((uint8_t*)XdrvMailbox.data)[0]);
- //delay(20); // Hack when power is off and dimmer is set then both commands go too soon to Serial out.
+ uint16_t hue, TuyaData;
+ uint8_t sat, bri;
+ uint8_t TuyaIdx = 0;
+ char hex_char[13];
+ bool noupd = false;
+ bool LightMode = TuyaGetDpId(TUYA_MCU_FUNC_MODESET) != 0;
+ uint8_t idx = 0;
+ snprintf_P(hex_char, sizeof(hex_char), PSTR("000000000000"));
+
+ if (LT_SERIAL1 == light_type) {
+ Tuya.Snapshot[0] = light_state.getDimmer();
+ }
+ if (LT_SERIAL2 == light_type || LT_RGBWC == light_type) {
+ idx = 1;
+ if (LT_SERIAL2 == light_type && Settings.flag3.pwm_multi_channels && (TuyaGetDpId(TUYA_MCU_FUNC_DIMMER2) != 0)) {
+ // Special setup for dual dimmer (like the MOES 2 Way Dimmer) emulating 2 PWM channels
+ Tuya.Snapshot[0] = changeUIntScale(Light.current_color[0], 0, 255, 0, 100);
+ Tuya.Snapshot[1] = changeUIntScale(Light.current_color[1], 0, 255, 0, 100);
+ } else { // CT Light or RGBWC
+ light_state.getCTRange(&Tuya.CTMin, &Tuya.CTMax); // SetOption82 - Reduce the CT range from 153..500 to 200..380 to accomodate with Alexa range
+ Tuya.Snapshot[0] = light_state.getDimmer();
+ Tuya.Snapshot[1] = light_state.getCT();
+ }
+ }
+ if (LT_RGBW == light_type) {
+ idx = 1;
+ Tuya.Snapshot[0] = light_state.getDimmer(1);
+ Tuya.Snapshot[1] = light_state.getDimmer(2);
+ }
+
+ if (light_type > LT_BASIC) {
+
+ if (LT_RGB != light_type) {
+ for (uint8_t i = 0; i <= idx; i++) {
+
+ if (Tuya.Snapshot[i] != Tuya.Levels[i]) {
+ if (i == 0 && LightMode && Tuya.ModeSet ) { noupd = true;}
+ if (!noupd) {
+ LightSerialDuty(Tuya.Snapshot[i], &hex_char[0], i+1);
+ Tuya.Levels[i] = Tuya.Snapshot[i];
+ }
+ noupd = false;
+ }
+ }
+ }
+
+ if (light_type >= LT_RGB) {
+ light_state.getHSB(&hue, &sat, &bri);
+ sat = changeUIntScale(sat, 0, 255, 0, 100);
+ bri = changeUIntScale(bri, 0, 255, 0, 100);
+ if (hue != Tuya.Snapshot[2] || sat != Tuya.Snapshot[3] || bri != Tuya.Snapshot[4]) {
+ if ((LightMode && Tuya.ModeSet) || LT_RGB == light_type) {
+ snprintf_P(hex_char, sizeof(hex_char), PSTR("%04X%04X%04X"), hue, sat * 10, bri * 10); // Create a TuyaMCU readable RGB payload
+ LightSerialDuty(0, &hex_char[0], 3);
+ memcpy_P(Tuya.HSBColor, hex_char, strlen(hex_char));
+ Tuya.Snapshot[2] = hue;
+ Tuya.Snapshot[3] = sat;
+ Tuya.Snapshot[4] = bri;
+ }
+ }
+ }
+ }
return true;
}
-void LightSerialDuty(uint16_t duty)
+void LightSerialDuty(uint16_t duty, char *hex_char, uint8_t TuyaIdx)
{
uint8_t dpid = TuyaGetDpId(TUYA_MCU_FUNC_DIMMER);
- if (duty > 0 && !Tuya.ignore_dim && TuyaSerial && dpid > 0) {
- duty = changeUIntScale(duty, 0, 255, 0, Settings.dimmer_hw_max);
- if (duty < Settings.dimmer_hw_min) { duty = Settings.dimmer_hw_min; } // dimming acts odd below 25(10%) - this mirrors the threshold set on the faceplate itself
- if (Tuya.new_dim != duty) {
- AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TYA: Send dim value=%d (id=%d)"), duty, dpid);
- Tuya.ignore_dimmer_cmd_timeout = millis() + 250; // Ignore serial received dim commands for the next 250ms
- TuyaSendValue(dpid, duty);
+ bool CTLight = false;
+
+ if (TuyaIdx > 0 && TuyaIdx <= 2) {
+
+ if (TuyaIdx == 2) {
+ if (!Settings.flag3.pwm_multi_channels) {
+ CTLight = true;
+ dpid = TuyaGetDpId(TUYA_MCU_FUNC_CT);
+ } else { dpid = TuyaGetDpId(TUYA_MCU_FUNC_DIMMER2); }
}
- } else if (dpid > 0) {
- Tuya.ignore_dim = false; // reset flag
- duty = changeUIntScale(duty, 0, 255, 0, Settings.dimmer_hw_max);
- AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TYA: Send dim skipped value=%d"), duty); // due to 0 or already set
- } else {
- AddLog_P(LOG_LEVEL_DEBUG, PSTR("TYA: Cannot set dimmer. Dimmer Id unknown")); //
+
+ if (duty > 0 && !Tuya.ignore_dim && TuyaSerial && dpid > 0) {
+ if (TuyaIdx == 2 && CTLight) {
+ duty = changeUIntScale(duty, Tuya.CTMin, Tuya.CTMax, Settings.dimmer_hw_max, 0);
+ } else { duty = changeUIntScale(duty, 0, 100, 0, Settings.dimmer_hw_max); }
+
+ if (duty < Settings.dimmer_hw_min) { duty = Settings.dimmer_hw_min; } // dimming acts odd below 25(10%) - this mirrors the threshold set on the faceplate itself
+ Tuya.ignore_dimmer_cmd_timeout = millis() + 250; // Ignore serial received dim commands for the next 250ms
+ if (Tuya.ModeSet && (TuyaGetDpId(TUYA_MCU_FUNC_MODESET) != 0) && light_type > LT_RGB) {
+ TuyaSendEnum(TuyaGetDpId(TUYA_MCU_FUNC_MODESET), 0);
+ }
+ TuyaSendValue(dpid, duty);
+
+ } else if (dpid > 0 && TuyaIdx <= 2) {
+
+ Tuya.ignore_dim = false; // reset flag
+
+ if (TuyaIdx == 2 && CTLight) {
+ duty = changeUIntScale(duty, Tuya.CTMin, Tuya.CTMax, Settings.dimmer_hw_max, 0);
+ } else {
+ duty = changeUIntScale(duty, 0, 100, 0, Settings.dimmer_hw_max);
+ }
+ AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TYA: Send dim skipped value %d for dpid %d"), duty, dpid); // due to 0 or already set
+ } else {
+ AddLog_P(LOG_LEVEL_DEBUG, PSTR("TYA: Cannot set dimmer. Dimmer Id unknown")); //
+ }
+ }
+
+ if (TuyaIdx == 3) {
+ dpid = TuyaGetDpId(TUYA_MCU_FUNC_RGB);
+ if (!Tuya.ModeSet && (TuyaGetDpId(TUYA_MCU_FUNC_MODESET) != 0) && light_type > LT_RGB) {
+ TuyaSendEnum(TuyaGetDpId(TUYA_MCU_FUNC_MODESET), 1);
+ }
+ TuyaSendString(dpid, hex_char);
+ AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TYA: TX RGB hex %s to dpId %d"), hex_char, dpid);
}
}
-void TuyaRequestState(void)
+void TuyaRequestState(uint8_t state_type)
{
if (TuyaSerial) {
// Get current status of MCU
AddLog_P(LOG_LEVEL_DEBUG, PSTR("TYA: Read MCU state"));
-
- TuyaSendCmd(TUYA_CMD_QUERY_STATE);
+ Tuya.SuspendTopic = true;
+ Tuya.ignore_topic_timeout = millis() + 1000; // suppress /STAT topic for 1000ms to limit data
+ switch (state_type) {
+ case 0:
+ TuyaSendCmd(TUYA_CMD_QUERY_STATE);
+ break;
+ case 8:
+ TuyaSendCmd(TUYA_CMD_QUERY_PRODUCT);
+ break;
+ }
}
}
@@ -452,25 +585,64 @@ void TuyaProcessStatePacket(void) {
SwitchHandler(1);
}
}
-
}
else if (Tuya.buffer[dpidStart + 1] == 2) { // Data Type 2
bool tuya_energy_enabled = (XNRG_32 == energy_flg);
uint16_t packetValue = Tuya.buffer[dpidStart + 6] << 8 | Tuya.buffer[dpidStart + 7];
- if (fnId == TUYA_MCU_FUNC_DIMMER) {
- AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TYA: RX Dim State=%d"), packetValue);
- Tuya.new_dim = changeUIntScale(packetValue, 0, Settings.dimmer_hw_max, 0, 100);
- if (Tuya.ignore_dimmer_cmd_timeout < millis()) {
- if ((power || Settings.flag3.tuya_apply_o20) && // SetOption54 - Apply SetOption20 settings to Tuya device
- (Tuya.new_dim > 0) && (abs(Tuya.new_dim - Settings.light_dimmer) > 1)) {
- Tuya.ignore_dim = true;
+ uint8_t dimIndex;
+ if ((fnId == TUYA_MCU_FUNC_FAN3) || (fnId == TUYA_MCU_FUNC_FAN4) ||
+ (fnId == TUYA_MCU_FUNC_FAN5) || (fnId == TUYA_MCU_FUNC_FAN6)) {
+ Tuya.FanState = packetValue;
+ }
- snprintf_P(scmnd, sizeof(scmnd), PSTR(D_CMND_DIMMER "3 %d"), Tuya.new_dim );
+ if ((fnId == TUYA_MCU_FUNC_DIMMER) || (fnId == TUYA_MCU_FUNC_REPORT1)) {
+ dimIndex = 0;
+ }
+
+ if (fnId == TUYA_MCU_FUNC_DIMMER2 || fnId == TUYA_MCU_FUNC_REPORT2 || fnId == TUYA_MCU_FUNC_CT) {
+ dimIndex = 1;
+ if (Settings.flag3.pwm_multi_channels) {
+ Tuya.Levels[dimIndex] = changeUIntScale(packetValue, 0, Settings.dimmer_hw_max, 0, 100);
+ } else {
+ Tuya.Levels[dimIndex] = changeUIntScale(packetValue, 0, Settings.dimmer_hw_max, Tuya.CTMax, Tuya.CTMin);
+ }
+ }
+
+ AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TYA: RX value %d from dpId %d "), packetValue, Tuya.buffer[dpidStart]);
+
+ if (Tuya.ignore_dimmer_cmd_timeout < millis()) {
+
+ if ((power || Settings.flag3.tuya_apply_o20) && ((Tuya.Levels[dimIndex] > 0) && (Tuya.Levels[dimIndex] != Tuya.Snapshot[dimIndex]))) { // SetOption54 - Apply SetOption20 settings to Tuya device
+
+ Tuya.ignore_dim = true;
+ skip_light_fade = true;
+
+ if ((fnId == TUYA_MCU_FUNC_DIMMER) || (fnId == TUYA_MCU_FUNC_REPORT1)) {
+ if (Settings.flag3.pwm_multi_channels && (abs(Tuya.Levels[0] - changeUIntScale(Light.current_color[0], 0, 255, 0, 100))) > 1) {
+ snprintf_P(scmnd, sizeof(scmnd), PSTR(D_CMND_CHANNEL "1 %d"), Tuya.Levels[0]);
+ } else {
+ if ((abs(Tuya.Levels[0] - light_state.getDimmer())) > 1) { snprintf_P(scmnd, sizeof(scmnd), PSTR(D_CMND_DIMMER "3 %d"), Tuya.Levels[0]); }
+ }
+ ExecuteCommand(scmnd, SRC_SWITCH);
+ }
+
+ if (((fnId == TUYA_MCU_FUNC_DIMMER2) || (fnId == TUYA_MCU_FUNC_REPORT2)) &&
+ Settings.flag3.pwm_multi_channels && (abs(Tuya.Levels[1] - changeUIntScale(Light.current_color[1], 0, 255, 0, 100))) > 1) {
+ snprintf_P(scmnd, sizeof(scmnd), PSTR(D_CMND_CHANNEL "2 %d"), Tuya.Levels[1]);
+ ExecuteCommand(scmnd, SRC_SWITCH);
+ }
+
+ if ((fnId == TUYA_MCU_FUNC_CT) && (abs(Tuya.Levels[1] - light_state.getCT())) > 1) {
+ snprintf_P(scmnd, sizeof(scmnd), PSTR(D_CMND_COLORTEMPERATURE " %d"), Tuya.Levels[1]);
+ ExecuteCommand(scmnd, SRC_SWITCH);
+ }
+
+ if ((fnId == TUYA_MCU_FUNC_WHITE) && (abs(Tuya.Levels[1] - light_state.getDimmer(2))) > 1) {
+ snprintf_P(scmnd, sizeof(scmnd), PSTR(D_CMND_WHITE " %d"), Tuya.Levels[1]);
ExecuteCommand(scmnd, SRC_SWITCH);
}
}
}
-
#ifdef USE_ENERGY_SENSOR
else if (tuya_energy_enabled && fnId == TUYA_MCU_FUNC_VOLTAGE) {
Energy.voltage[0] = (float)packetValue / 10;
@@ -489,14 +661,40 @@ void TuyaProcessStatePacket(void) {
Tuya.lastPowerCheckTime = Rtc.utc_time;
}
#endif // USE_ENERGY_SENSOR
-
}
- // } else {
- // AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TYA: Unknown FnId=%s for dpId=%s"), fnId, Tuya.buffer[6]);
- dpidStart += dpDataLen + 4;
+ else if (Tuya.buffer[dpidStart + 1] == 3) { // Data Type 3
+ const unsigned char *dpData = (unsigned char*)&Tuya.buffer[dpidStart + 4];
+ if ((TuyaGetDpId(TUYA_MCU_FUNC_RGB) != 0) && dpDataLen == 12) { //Decode the RGB hex and transmit HSBCOLOR command
+
+ //Tuya.ignore_dim = true;
+ char RgbData[13];
+ snprintf_P(RgbData, sizeof(RgbData), PSTR("%.*s"), dpDataLen, dpData);
+ char HSB1[5], HSB2[5], HSB3[5];
+ snprintf_P(HSB1, sizeof(HSB1), PSTR("%.4s\n"), &RgbData[0]);
+ snprintf_P(HSB2, sizeof(HSB2), PSTR("%.4s\n"), &RgbData[4]);
+ snprintf_P(HSB3, sizeof(HSB3), PSTR("%.4s\n"), &RgbData[8]);
+
+ if ((Tuya.Snapshot[2] != ((int)strtol(HSB1, NULL, 16)) ||
+ Tuya.Snapshot[3] != ((int)strtol(HSB2, NULL, 16)) / 10 || Tuya.Snapshot[4] !=((int)strtol(HSB3, NULL, 16)) / 10)) {
+
+ snprintf_P(scmnd, sizeof(scmnd), PSTR(D_CMND_HSBCOLOR " %d,%d,%d"), ((int)strtol(HSB1, NULL, 16)),
+ ((int)strtol(HSB2, NULL, 16)) / 10, ((int)strtol(HSB3, NULL, 16)) / 10);
+ ExecuteCommand(scmnd, SRC_SWITCH);
+
+ memcpy_P(Tuya.HSBColor, RgbData, strlen(RgbData));
+ }
+ }
+ }
+ else if (Tuya.buffer[dpidStart + 1] == 4) { // Data Type 4
+ const unsigned char *dpData = (unsigned char*)&Tuya.buffer[dpidStart + 4];
+ if (fnId == TUYA_MCU_FUNC_MODESET) { // toggle light type
+ Tuya.ModeSet = dpData[0];
+ Tuya.Levels[3] = dpData[0];
+ }
+ }
+ dpidStart += dpDataLen + 4;
}
}
-
void TuyaLowPowerModePacketProcess(void) {
switch (Tuya.buffer[3]) {
case TUYA_CMD_QUERY_PRODUCT:
@@ -579,7 +777,7 @@ void TuyaNormalPowerModePacketProcess(void)
restart_flag = 2;
}
}
- TuyaRequestState();
+ TuyaRequestState(0);
break;
default:
@@ -615,14 +813,33 @@ bool TuyaModuleSelected(void)
}
}
- if (!relaySet) {
+ if (!relaySet && TuyaGetDpId(TUYA_MCU_FUNC_DUMMY) == 0) { //by default the first relay is created automatically the dummy let remove it if not needed
TuyaAddMcuFunc(TUYA_MCU_FUNC_REL1, 1);
devices_present++;
SettingsSaveAll();
}
+ // Possible combinations for Lights:
+ // 0: NONE = LT_BASIC
+ // 1: DIMMER = LT_SERIAL1 - Common one channel dimmer
+ // 2: DIMMER, DIMMER2 = LT_SERIAL2 - Two channels dimmer (special setup used with SetOption68)
+ // 3: DIMMER, CT = LT_SERIAL2 - Dimmable light and White Color Temperature
+ // 4: DIMMER, RGB = LT_RGB - RGB Light
+ // 5: DIMMER, RGB, CT = LT_RGBWC - RGB LIght and White Color Temperature
+ // 6: DIMMER, RGB, WHITE = LT_RGBW - RGB LIght and White
+
if (TuyaGetDpId(TUYA_MCU_FUNC_DIMMER) != 0) {
- light_type = LT_SERIAL1;
+ if (TuyaGetDpId(TUYA_MCU_FUNC_RGB) != 0) {
+ if (TuyaGetDpId(TUYA_MCU_FUNC_CT) != 0) {
+ light_type = LT_RGBWC;
+ } else if (TuyaGetDpId(TUYA_MCU_FUNC_WHITE) != 0) {
+ light_type = LT_RGBW;
+ } else { light_type = LT_RGB; }
+ } else if (TuyaGetDpId(TUYA_MCU_FUNC_CT) != 0 || TuyaGetDpId(TUYA_MCU_FUNC_DIMMER2) != 0) {
+ if (TuyaGetDpId(TUYA_MCU_FUNC_RGB) != 0) {
+ light_type = LT_RGBWC;
+ } else { light_type = LT_SERIAL2; }
+ } else { light_type = LT_SERIAL1; }
} else {
light_type = LT_BASIC;
}
@@ -647,9 +864,11 @@ void TuyaInit(void)
if (TuyaSerial->begin(baudrate)) {
if (TuyaSerial->hardwareSerial()) { ClaimSerial(); }
// Get MCU Configuration
- AddLog_P(LOG_LEVEL_DEBUG, PSTR("TYA: Request MCU configuration at %d baud rate"));
-
+ Tuya.SuspendTopic = true;
+ Tuya.ignore_topic_timeout = millis() + 1000; // suppress /STAT topic for 1000ms to avoid data overflow
+ AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TYA: Request MCU configuration at %d bps"), baudrate);
TuyaSendCmd(TUYA_CMD_QUERY_PRODUCT);
+
}
}
Tuya.heartbeat_timer = 0; // init heartbeat timer when dimmer init is done
@@ -687,17 +906,24 @@ void TuyaSerialInput(void)
char hex_char[(Tuya.byte_counter * 2) + 2];
uint16_t len = Tuya.buffer[4] << 8 | Tuya.buffer[5];
+
Response_P(PSTR("{\"" D_JSON_TUYA_MCU_RECEIVED "\":{\"Data\":\"%s\",\"Cmnd\":%d"), ToHex_P((unsigned char*)Tuya.buffer, Tuya.byte_counter, hex_char, sizeof(hex_char)), Tuya.buffer[3]);
+ uint16_t DataVal = 0;
+ uint8_t dpId = 0;
+ uint8_t dpDataType = 0;
+ char DataStr[13];
+
if (len > 0) {
ResponseAppend_P(PSTR(",\"CmndData\":\"%s\""), ToHex_P((unsigned char*)&Tuya.buffer[6], len, hex_char, sizeof(hex_char)));
if (TUYA_CMD_STATE == Tuya.buffer[3]) {
//55 AA 03 07 00 0D 01 04 00 01 02 02 02 00 04 00 00 00 1A 40
// 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
uint8_t dpidStart = 6;
+ //snprintf_P(DataStr, sizeof(DataStr), PSTR("000000000000"));
while (dpidStart + 4 < Tuya.byte_counter) {
- uint8_t dpId = Tuya.buffer[dpidStart];
- uint8_t dpDataType = Tuya.buffer[dpidStart + 1];
+ dpId = Tuya.buffer[dpidStart];
+ dpDataType = Tuya.buffer[dpidStart + 1];
uint16_t dpDataLen = Tuya.buffer[dpidStart + 2] << 8 | Tuya.buffer[dpidStart + 3];
const unsigned char *dpData = (unsigned char*)&Tuya.buffer[dpidStart + 4];
const char *dpHexData = ToHex_P(dpData, dpDataLen, hex_char, sizeof(hex_char));
@@ -706,15 +932,20 @@ void TuyaSerialInput(void)
ResponseAppend_P(PSTR(",\"DpType%uId%u\":"), dpDataType, dpId);
if (TUYA_TYPE_BOOL == dpDataType && dpDataLen == 1) {
ResponseAppend_P(PSTR("%u"), dpData[0]);
+ DataVal = dpData[0];
} else if (TUYA_TYPE_VALUE == dpDataType && dpDataLen == 4) {
uint32_t dpValue = (uint32_t)dpData[0] << 24 | (uint32_t)dpData[1] << 16 | (uint32_t)dpData[2] << 8 | (uint32_t)dpData[3] << 0;
ResponseAppend_P(PSTR("%u"), dpValue);
+ DataVal = dpValue;
} else if (TUYA_TYPE_STRING == dpDataType) {
ResponseAppend_P(PSTR("\"%.*s\""), dpDataLen, dpData);
+ snprintf_P(DataStr, sizeof(DataStr), PSTR("%.*s"), dpDataLen, dpData);
} else if (TUYA_TYPE_ENUM == dpDataType && dpDataLen == 1) {
ResponseAppend_P(PSTR("%u"), dpData[0]);
+ DataVal = dpData[0];
} else {
ResponseAppend_P(PSTR("\"0x%s\""), dpHexData);
+ snprintf_P(DataStr, sizeof(DataStr), PSTR("%s"), dpHexData);
}
}
@@ -727,7 +958,6 @@ void TuyaSerialInput(void)
}
}
}
-
ResponseAppend_P(PSTR("}}"));
if (Settings.flag3.tuya_serial_mqtt_publish) { // SetOption66 - Enable TuyaMcuReceived messages over Mqtt
@@ -737,6 +967,17 @@ void TuyaSerialInput(void)
}
XdrvRulesProcess();
+ if (dpId != 0 && Settings.tuyamcu_topic) { // Publish a /STAT Topic ready to use for any home automation system
+ if (!Tuya.SuspendTopic) {
+ char scommand[10];
+ snprintf_P(scommand, sizeof(scommand), PSTR("TuyaSend%d"), dpDataType);
+
+ if (dpDataType != 3 && dpDataType != 5) { Response_P(PSTR("%d,%u"), dpId, DataVal); }
+ else { Response_P(PSTR("%d,%s"), dpId, DataStr); }
+ MqttPublishPrefixTopic_P(STAT, (PSTR("%s"), scommand));
+ }
+ }
+
if (!Tuya.low_power_mode) {
TuyaNormalPowerModePacketProcess();
} else {
@@ -747,7 +988,7 @@ void TuyaSerialInput(void)
Tuya.cmd_status = 0;
Tuya.cmd_checksum = 0;
Tuya.data_len = 0;
- } // read additional packets from TUYA
+ } // read additional packets from TUYA
else if (Tuya.byte_counter < TUYA_BUFFER_SIZE -1) { // add char to string if it still fits
Tuya.buffer[Tuya.byte_counter++] = serial_in_byte;
Tuya.cmd_checksum += serial_in_byte;
@@ -821,6 +1062,7 @@ void TuyaSetTime(void) {
#endif //USE_TUYA_TIME
#ifdef USE_ENERGY_SENSOR
+
/*********************************************************************************************\
* Energy Interface
\*********************************************************************************************/
@@ -887,6 +1129,7 @@ bool Xdrv16(uint8_t function)
} else {
TuyaSendLowPowerSuccessIfNeeded();
}
+ if (Tuya.ignore_topic_timeout < millis()) { Tuya.SuspendTopic = false; }
break;
case FUNC_SET_CHANNELS:
result = TuyaSetChannels();
From 0bca9a2028bab626b1685178225d1a7181ee8312 Mon Sep 17 00:00:00 2001
From: Federico Leoni
Date: Wed, 16 Sep 2020 16:59:10 -0300
Subject: [PATCH 059/148] TuyaMCU update2
oops
---
tasmota/xdrv_01_webserver.ino | 53 +++++++++++++++++------------------
1 file changed, 25 insertions(+), 28 deletions(-)
diff --git a/tasmota/xdrv_01_webserver.ino b/tasmota/xdrv_01_webserver.ino
index 8d37f5183..ef80c4abf 100644
--- a/tasmota/xdrv_01_webserver.ino
+++ b/tasmota/xdrv_01_webserver.ino
@@ -1460,39 +1460,36 @@ void HandleRoot(void)
(set_button) ? SettingsText(SET_BUTTON1 + idx -1) : (devices_present < 5) ? D_BUTTON_TOGGLE : "",
(set_button) ? "" : (devices_present > 1) ? stemp : "");
}
-#ifdef USE_TUYA_MCU
- if (IsModuleTuya()) {
- uint8_t modeset = 0;
- if (AsModuleTuyaMS()) {
- WSContentSend_P(HTTP_TABLE100);
- WSContentSend_P(PSTR(""));
- snprintf_P(stemp, sizeof(stemp), PSTR("" D_JSON_IRHVAC_MODE ""));
- WSContentSend_P(HTTP_DEVICE_CONTROL, 26, devices_present + 1,
- (strlen(SettingsText(SET_BUTTON1 + devices_present))) ? SettingsText(SET_BUTTON1 + devices_present) : stemp, "");
- WSContentSend_P(PSTR("
"));
- modeset = 1;
- }
- if (IsTuyaFanCtrl()) {
- uint8_t device = devices_present + modeset;
- WSContentSend_P(HTTP_TABLE100);
- WSContentSend_P(PSTR(""));
- for (uint32_t i = device + 1; i <= (TuyaFanSpeeds() + device) + 1; i++) {
- snprintf_P(stemp, sizeof(stemp), PSTR("%d"), i - (device + 1));
- WSContentSend_P(HTTP_DEVICE_CONTROL, 16, i,
- (strlen(SettingsText(SET_BUTTON1 + i))) ? SettingsText(SET_BUTTON1 + i) : stemp, "");
- }
- WSContentSend_P(PSTR("
"));
- }
- } else {
-#endif // USE_TUYA_MCU
- }
- WSContentSend_P(PSTR(""));
#ifdef USE_SONOFF_IFAN
}
#endif // USE_SONOFF_IFAN
-
WSContentSend_P(PSTR(""));
}
+#ifdef USE_TUYA_MCU
+ if (IsModuleTuya()) {
+ uint8_t modeset = 0;
+ if (AsModuleTuyaMS()) {
+ WSContentSend_P(HTTP_TABLE100);
+ WSContentSend_P(PSTR(""));
+ snprintf_P(stemp, sizeof(stemp), PSTR("" D_JSON_IRHVAC_MODE ""));
+ WSContentSend_P(HTTP_DEVICE_CONTROL, 26, devices_present + 1,
+ (strlen(SettingsText(SET_BUTTON1 + devices_present))) ? SettingsText(SET_BUTTON1 + devices_present) : stemp, "");
+ WSContentSend_P(PSTR("
"));
+ modeset = 1;
+ }
+ if (IsTuyaFanCtrl()) {
+ uint8_t device = devices_present + modeset;
+ WSContentSend_P(HTTP_TABLE100);
+ WSContentSend_P(PSTR(""));
+ for (uint32_t i = device + 1; i <= (TuyaFanSpeeds() + device) + 1; i++) {
+ snprintf_P(stemp, sizeof(stemp), PSTR("%d"), i - (device + 1));
+ WSContentSend_P(HTTP_DEVICE_CONTROL, 16, i,
+ (strlen(SettingsText(SET_BUTTON1 + i))) ? SettingsText(SET_BUTTON1 + i) : stemp, "");
+ }
+ WSContentSend_P(PSTR("
"));
+ }
+ }
+#endif // USE_TUYA_MCU
#ifdef USE_SONOFF_RF
if (SONOFF_BRIDGE == my_module_type) {
WSContentSend_P(HTTP_TABLE100);
From 74bfad29e19ed48a9a75516cdbc0f8b0c0ece83f Mon Sep 17 00:00:00 2001
From: Justin Monroe
Date: Thu, 3 Sep 2020 02:16:13 +0000
Subject: [PATCH 060/148] Add last seen time to zigbee devices
- Adding last_seen to zigbee devices, and updates to UI to show
- Adding signal strength icon for LQI, instead of just the number
---
tasmota/xdrv_23_zigbee_2_devices.ino | 12 ++-
tasmota/xdrv_23_zigbee_8_parsers.ino | 4 +-
tasmota/xdrv_23_zigbee_A_impl.ino | 133 ++++++++++++++++++++-------
3 files changed, 113 insertions(+), 36 deletions(-)
diff --git a/tasmota/xdrv_23_zigbee_2_devices.ino b/tasmota/xdrv_23_zigbee_2_devices.ino
index dbc459e38..82000b2e1 100644
--- a/tasmota/xdrv_23_zigbee_2_devices.ino
+++ b/tasmota/xdrv_23_zigbee_2_devices.ino
@@ -75,6 +75,7 @@ public:
// powe plug data
uint16_t mains_voltage; // AC voltage
int16_t mains_power; // Active power
+ uint32_t last_seen; // Last seen time (epoch)
// Constructor with all defaults
Z_Device(uint16_t _shortaddr = BAD_SHORTADDR, uint64_t _longaddr = 0x00):
@@ -103,7 +104,8 @@ public:
pressure(0xFFFF),
humidity(0xFF),
mains_voltage(0xFFFF),
- mains_power(-0x8000)
+ mains_power(-0x8000),
+ last_seen(0)
{ };
inline bool valid(void) const { return BAD_SHORTADDR != shortaddr; } // is the device known, valid and found?
@@ -128,6 +130,7 @@ public:
inline bool validTemperature(void) const { return -0x8000 != temperature; }
inline bool validPressure(void) const { return 0xFFFF != pressure; }
inline bool validHumidity(void) const { return 0xFF != humidity; }
+ inline bool validLastSeen(void) const { return 0x0 != last_seen; }
inline bool validMainsVoltage(void) const { return 0xFFFF != mains_voltage; }
inline bool validMainsPower(void) const { return -0x8000 != mains_power; }
@@ -247,6 +250,7 @@ public:
void setReachable(uint16_t shortaddr, bool reachable);
void setLQI(uint16_t shortaddr, uint8_t lqi);
+ void setLastSeenNow(uint16_t shortaddr);
// uint8_t getLQI(uint16_t shortaddr) const;
void setBatteryPercent(uint16_t shortaddr, uint8_t bp);
uint8_t getBatteryPercent(uint16_t shortaddr) const;
@@ -630,6 +634,12 @@ void Z_Devices::setLQI(uint16_t shortaddr, uint8_t lqi) {
getShortAddr(shortaddr).lqi = lqi;
}
+void Z_Devices::setLastSeenNow(uint16_t shortaddr) {
+ if (shortaddr == localShortAddr) { return; }
+ getShortAddr(shortaddr).last_seen= Rtc.utc_time;
+}
+
+
void Z_Devices::setBatteryPercent(uint16_t shortaddr, uint8_t bp) {
getShortAddr(shortaddr).batterypercent = bp;
}
diff --git a/tasmota/xdrv_23_zigbee_8_parsers.ino b/tasmota/xdrv_23_zigbee_8_parsers.ino
index c761cabb6..27eb14801 100644
--- a/tasmota/xdrv_23_zigbee_8_parsers.ino
+++ b/tasmota/xdrv_23_zigbee_8_parsers.ino
@@ -1348,7 +1348,8 @@ void Z_IncomingMessage(class ZCLFrame &zcl_received) {
// log the packet details
zcl_received.log();
- zigbee_devices.setLQI(srcaddr, linkquality); // EFR32 has a different scale for LQI
+ zigbee_devices.setLQI(srcaddr, linkquality != 0xFF ? linkquality : 0xFE); // EFR32 has a different scale for LQI
+ zigbee_devices.setLastSeenNow(srcaddr);
char shortaddr[8];
snprintf_P(shortaddr, sizeof(shortaddr), PSTR("0x%04X"), srcaddr);
@@ -1492,6 +1493,7 @@ int32_t EZ_IncomingMessage(int32_t res, const class SBuffer &buf) {
// ZDO request
// Report LQI
zigbee_devices.setLQI(srcaddr, linkquality);
+ zigbee_devices.setLastSeenNow(srcaddr);
// Since ZDO messages start with a sequence number, we skip it
// but we add the source address in the last 2 bytes
SBuffer zdo_buf(buf.get8(20) - 1 + 2);
diff --git a/tasmota/xdrv_23_zigbee_A_impl.ino b/tasmota/xdrv_23_zigbee_A_impl.ino
index 5ca3b555e..9871f628e 100644
--- a/tasmota/xdrv_23_zigbee_A_impl.ino
+++ b/tasmota/xdrv_23_zigbee_A_impl.ino
@@ -1350,8 +1350,37 @@ extern "C" {
return 1;
}
}
-}
+
+// Convert seconds to a string representing days, hours or minutes present in the n-value.
+// The string will contain the most coarse time only, rounded down (61m == 01h, 01h37m == 01h).
+// Inputs:
+// - n: uint32_t representing some number of seconds
+// - result: a buffer of suitable size (7 bytes would represent the entire solution space
+// for UINT32_MAX including the trailing null-byte, or "49710d")
+// - result_len: A numeric value representing the total length of the result buffer
+// Returns:
+// - The number of characters that would have been written were result sufficiently large
+// - negatve number on encoding error from snprintf
+//
+ int convert_seconds_to_dhm(uint32_t n, char *result, size_t result_len){
+ char fmtstr[] = "%02dmhd"; // Don't want this in progmem, because we mutate it.
+ uint32_t conversions[3] = {24 * 3600, 3600, 60};
+ uint32_t value;
+ for(int i = 0; i < 3; ++i) {
+ value = n / conversions[i];
+ if(value > 0) {
+ fmtstr[4] = fmtstr[6-i];
+ break;
+ }
+ n = n % conversions[i];
+ }
+
+ // Null-terminate the string at the last "valid" index, removing any excess zero values.
+ fmtstr[5] = '\0';
+ return snprintf(result, result_len, fmtstr, value);
+ }
+}
void ZigbeeShow(bool json)
{
if (json) {
@@ -1362,12 +1391,21 @@ void ZigbeeShow(bool json)
if (!zigbee_num) { return; }
if (zigbee_num > 255) { zigbee_num = 255; }
- // Calculate fixed column width for best visual result (Theos opinion)
- const uint8_t px_batt = 30; // Battery icon is 20px, add 10px as separator
- const uint8_t px_lqi = (strlen(D_LQI) + 4) * 10; // LQI 254 = 70px
-
WSContentSend_P(PSTR("{t}")); // Terminate current two column table and open new table
- WSContentSend_P(PSTR(""));
+ WSContentSend_P(PSTR(
+ ""
+ ));
// sort elements by name, then by id
uint8_t sorted_idx[zigbee_num];
@@ -1376,44 +1414,69 @@ void ZigbeeShow(bool json)
}
qsort(sorted_idx, zigbee_num, sizeof(sorted_idx[0]), device_cmp);
+ uint32_t now = Rtc.utc_time;
+
for (uint32_t i = 0; i < zigbee_num; i++) {
const Z_Device &device = zigbee_devices.devicesAt(sorted_idx[i]);
uint16_t shortaddr = device.shortaddr;
- { // exxplicit scope to free up stack allocated strings
- char *name = (char*) device.friendlyName;
- char sdevice[33];
- if (nullptr == name) {
- snprintf_P(sdevice, sizeof(sdevice), PSTR(D_DEVICE " 0x%04X"), shortaddr);
- name = sdevice;
- }
+ char *name = (char*) device.friendlyName;
- char slqi[8];
- snprintf_P(slqi, sizeof(slqi), PSTR("-"));
- if (device.validLqi()) {
- snprintf_P(slqi, sizeof(slqi), PSTR("%d"), device.lqi);
- }
-
- char sbatt[64];
- snprintf_P(sbatt, sizeof(sbatt), PSTR(" "));
- if (device.validBatteryPercent()) {
- snprintf_P(sbatt, sizeof(sbatt), PSTR(""), device.batterypercent, changeUIntScale(device.batterypercent, 0, 100, 0, 14));
- }
-
- if (!i) { // First row needs style info
- WSContentSend_PD(PSTR("%s | %s | " D_LQI " %s{e}"),
- name, px_batt, sbatt, px_lqi, slqi);
- } else { // Following rows don't need style info so reducing ajax package
- WSContentSend_PD(PSTR(" |
%s | %s | " D_LQI " %s{e}"), name, sbatt, slqi);
- }
+ char sdevice[33];
+ if (nullptr == name) {
+ snprintf_P(sdevice, sizeof(sdevice), PSTR(D_DEVICE " 0x%04X"), shortaddr);
+ name = sdevice;
}
- // Sensor
+ char sbatt[64];
+ snprintf_P(sbatt, sizeof(sbatt), PSTR(" "));
+ if (device.validBatteryPercent()) {
+ snprintf_P(sbatt, sizeof(sbatt),
+ PSTR(""),
+ device.batterypercent, changeUIntScale(device.batterypercent, 0, 100, 0, 14)
+ );
+ }
+
+ uint32_t num_bars = 0;
+
+ char slqi[4];
+ slqi[0] = '-';
+ slqi[1] = '\0';
+ if (device.validLqi()){
+ num_bars = changeUIntScale(device.lqi, 0, 254, 0, 4);
+ snprintf_P(slqi, sizeof(slqi), PSTR("%d"), device.lqi);
+ }
+
+ WSContentSend_PD(PSTR(
+ " |
"
+ "%s | " // name
+ "%s | " // sbatt (Battery Indicator)
+ "" // slqi
+ ), name, sbatt, slqi);
+
+ if(device.validLqi()) {
+ for(uint32_t j = 0; j < 4; ++j) {
+ WSContentSend_PD(PSTR(""), j, (num_bars < j) ? PSTR(" o30") : PSTR(""));
+ }
+ }
+ char dhm[16]; // len("🕗" + "49710d" + '\0') == 16
+ snprintf_P(dhm, sizeof(dhm), PSTR(" "));
+ if(device.validLastSeen()){
+ snprintf_P(dhm, sizeof(dhm), PSTR("🕗"));
+ convert_seconds_to_dhm(now - device.last_seen, &dhm[9], 7);
+ }
+
+ WSContentSend_PD(PSTR(
+ " | " // Close LQI
+ "%s{e}" // dhm (Last Seen)
+ ), dhm );
+
+ // Sensors
bool temperature_ok = device.validTemperature();
bool humidity_ok = device.validHumidity();
bool pressure_ok = device.validPressure();
if (temperature_ok || humidity_ok || pressure_ok) {
- WSContentSend_P(PSTR(" |
┆"));
+ WSContentSend_P(PSTR(" |
┆"));
if (temperature_ok) {
char buf[12];
dtostrf(device.temperature / 10.0f, 3, 1, buf);
@@ -1425,6 +1488,7 @@ void ZigbeeShow(bool json)
if (pressure_ok) {
WSContentSend_P(PSTR(" ⛅ %d hPa"), device.pressure);
}
+
WSContentSend_P(PSTR("{e}"));
}
@@ -1433,7 +1497,7 @@ void ZigbeeShow(bool json)
if (power_ok) {
uint8_t channels = device.getLightChannels();
if (0xFF == channels) { channels = 5; } // if number of channel is unknown, display all known attributes
- WSContentSend_P(PSTR(" |
┆ %s"), device.getPower() ? PSTR(D_ON) : PSTR(D_OFF));
+ WSContentSend_P(PSTR(" |
┆ %s"), device.getPower() ? PSTR(D_ON) : PSTR(D_OFF));
if (device.validDimmer() && (channels >= 1)) {
WSContentSend_P(PSTR(" 🔅 %d%%"), changeUIntScale(device.dimmer,0,254,0,100));
}
@@ -1460,6 +1524,7 @@ void ZigbeeShow(bool json)
WSContentSend_P(PSTR(" %dW"), device.mains_power);
}
}
+ WSContentSend_P(PSTR("{e}"));
}
}
From e452f85e391d5e46cdea4a7a28d50b88717dc9d7 Mon Sep 17 00:00:00 2001
From: gemu2015
Date: Thu, 17 Sep 2020 09:11:24 +0200
Subject: [PATCH 061/148] bug fixes and enhancements
---
tasmota/xdrv_10_scripter.ino | 159 ++++++++++++++++++++++++++++++-----
1 file changed, 139 insertions(+), 20 deletions(-)
diff --git a/tasmota/xdrv_10_scripter.ino b/tasmota/xdrv_10_scripter.ino
index ea8223233..32a8c2a3e 100755
--- a/tasmota/xdrv_10_scripter.ino
+++ b/tasmota/xdrv_10_scripter.ino
@@ -79,6 +79,7 @@ uint32_t DecodeLightId(uint32_t hue_id);
#undef LITTLEFS_SCRIPT_SIZE
#undef EEP_SCRIPT_SIZE
#undef USE_SCRIPT_COMPRESSION
+
#if USE_SCRIPT_FATFS==-1
#ifdef ESP32
@@ -250,9 +251,9 @@ SDClass *fsp;
#define FAT_SCRIPT_NAME "script.txt"
#endif
-#if USE_STANDARD_SPI_LIBRARY==0
-#warning ("FATFS standard spi should be used");
-#endif
+//#if USE_STANDARD_SPI_LIBRARY==0
+//#warning ("FATFS standard spi should be used");
+//#endif
#endif // USE_SCRIPT_FATFS
@@ -377,7 +378,11 @@ struct SCRIPT_MEM {
struct T_INDEX *type; // type and index pointer
struct M_FILT *mfilt;
char *glob_vnp; // var name pointer
+#ifdef SCRIPT_LARGE_VNBUFF
+ uint16_t *vnp_offset;
+#else
uint8_t *vnp_offset;
+#endif
char *glob_snp; // string vars pointer
char *scriptptr;
char *section_ptr;
@@ -511,7 +516,7 @@ char *script;
char **snp_p = snp;
uint8_t numperm = 0;
uint8_t numflt = 0;
- uint8_t count;
+ uint16_t count;
glob_script_mem.max_ssize = SCRIPT_SVARSIZE;
glob_script_mem.scriptptr = 0;
@@ -592,7 +597,7 @@ char *script;
} else {
vtypes[vars].bits.is_filter = 0;
}
- *vnp_p ++= vnames_p;
+ *vnp_p++ = vnames_p;
while (lp255) {
+ if (index > MAXVNSIZ) {
free(glob_script_mem.script_mem);
return -5;
}
@@ -760,7 +785,7 @@ char *script;
// copy string variables
char *cp1 = glob_script_mem.glob_snp;
char *sp = strings;
- for (count = 0; count=0
+ if (sel == 0) {
+ result = SD.totalBytes()/1000;
+ } else if (sel == 1) {
+ result = (SD.totalBytes() - SD.usedBytes())/1000;
+ }
+#else
+ if (sel == 0) {
+ result = FFat.totalBytes()/1000;
+ } else if (sel == 1) {
+ result = FFat.freeBytes()/1000;
+ }
+#endif // USE_SCRIPT_FATFS>=0
+#else
+ // ESP8266
+ FSInfo64 fsinfo;
+ fsp->info64(fsinfo);
+ if (sel == 0) {
+ result = fsinfo.totalBytes/1000;
+ } else if (sel == 1) {
+ result = (fsinfo.totalBytes - fsinfo.usedBytes)/1000;
+ }
+#endif // ESP32
+ return result;
+}
+
+// format number with thousand marker
+void form1000(uint32_t number, char *dp, char sc) {
+ char str[32];
+ sprintf(str, "%d", number);
+ char *sp = str;
+ uint32_t inum = strlen(sp)/3;
+ uint32_t fnum = strlen(sp)%3;
+ if (!fnum) inum--;
+ for (uint32_t count=0; count<=inum; count++) {
+ if (fnum){
+ memcpy(dp,sp,fnum);
+ dp+=fnum;
+ sp+=fnum;
+ fnum=0;
+ } else {
+ memcpy(dp,sp,3);
+ dp+=3;
+ sp+=3;
+ }
+ if (count!=inum) {
+ *dp++=sc;
+ }
+ }
+ *dp=0;
+}
+
+#endif //USE_SCRIPT_FATFS
+
#ifdef USE_SCRIPT_GLOBVARS
#define SCRIPT_UDP_BUFFER_SIZE 128
#define SCRIPT_UDP_PORT 1999
IPAddress script_udp_remote_ip;
void Script_Stop_UDP(void) {
- Script_PortUdp.flush();
- Script_PortUdp.stop();
- glob_script_mem.udp_flags.udp_connected = 0;
+ if (!glob_script_mem.udp_flags.udp_used) return;
+ if (glob_script_mem.udp_flags.udp_connected) {
+ Script_PortUdp.flush();
+ Script_PortUdp.stop();
+ glob_script_mem.udp_flags.udp_connected = 0;
+ }
}
void Script_Init_UDP() {
if (global_state.network_down) return;
+ if (!glob_script_mem.udp_flags.udp_used) return;
if (glob_script_mem.udp_flags.udp_connected) return;
if (Script_PortUdp.beginMulticast(WiFi.localIP(), IPAddress(239,255,255,250), SCRIPT_UDP_PORT)) {
@@ -898,6 +985,7 @@ void Script_Init_UDP() {
glob_script_mem.udp_flags.udp_connected = 0;
}
}
+
void Script_PollUdp(void) {
if (global_state.network_down) return;
if (!glob_script_mem.udp_flags.udp_used) return;
@@ -1546,11 +1634,18 @@ chknext:
case 'a':
#ifdef USE_ANGLE_FUNC
if (!strncmp(vname, "acos(", 5)) {
- lp=GetNumericArgument(lp + 5, OPER_EQU, &fvar, 0);
- fvar = acosf(fvar);
- lp++;
- len = 0;
- goto exit;
+ lp=GetNumericArgument(lp + 5, OPER_EQU, &fvar, 0);
+ fvar = acosf(fvar);
+ lp++;
+ len = 0;
+ goto exit;
+ }
+ if (!strncmp(vname, "abs(", 4)) {
+ lp=GetNumericArgument(lp + 4, OPER_EQU, &fvar, 0);
+ fvar = fabs(fvar);
+ lp++;
+ len = 0;
+ goto exit;
}
#endif
if (!strncmp(vname, "asc(", 4)) {
@@ -1982,6 +2077,14 @@ chknext:
len = 0;
goto exit;
}
+
+ if (!strncmp(vname, "fsi(", 4)) {
+ lp = GetNumericArgument(lp + 4, OPER_EQU, &fvar, 0);
+ fvar = get_fsinfo(fvar);
+ lp++;
+ len = 0;
+ goto exit;
+ }
#endif // USE_SCRIPT_FATFS_EXT
if (!strncmp(vname, "fl1(", 4) || !strncmp(vname, "fl2(", 4) ) {
uint8_t lknum = *(lp+2)&3;
@@ -4358,8 +4461,8 @@ const char HTTP_FORM_FILE_UPLOAD[] PROGMEM =
" "
" "
"" D_UPLOAD_STARTED " ... ";
+const char HTTP_FORM_FILE_UPGc[] PROGMEM =
+"total size: %s kB - free: %s kB ";
+
const char HTTP_FORM_SDC_DIRa[] PROGMEM =
"";
const char HTTP_FORM_SDC_DIRb[] PROGMEM =
@@ -4573,6 +4679,11 @@ void Script_FileUploadConfiguration(void) {
WSContentSend_P(HTTP_FORM_FILE_UPLOAD,D_SDCARD_DIR);
WSContentSend_P(HTTP_FORM_FILE_UPG, D_SCRIPT_UPLOAD);
#ifdef SDCARD_DIR
+ char ts[16];
+ char fs[16];
+ form1000(get_fsinfo(0), ts, '.');
+ form1000(get_fsinfo(1), fs, '.');
+ WSContentSend_P(HTTP_FORM_FILE_UPGc, ts, fs);
WSContentSend_P(HTTP_FORM_SDC_DIRa);
if (glob_script_mem.script_sd_found) {
ListDir(path, depth);
@@ -4837,6 +4948,11 @@ void ScriptSaveSettings(void) {
}
void SaveScriptEnd(void) {
+
+#ifdef USE_SCRIPT_GLOBVARS
+ Script_Stop_UDP();
+#endif //USE_SCRIPT_GLOBVARS
+
if (glob_script_mem.script_mem) {
Scripter_save_pvars();
free(glob_script_mem.script_mem);
@@ -4856,6 +4972,9 @@ void SaveScriptEnd(void) {
#endif // USE_SCRIPT_COMPRESSION
if (bitRead(Settings.rule_enabled, 0)) {
+
+
+
int16_t res = Init_Scripter();
if (res) {
AddLog_P2(LOG_LEVEL_INFO, PSTR("script init error: %d"), res);
@@ -7110,8 +7229,8 @@ bool Xdrv10(uint8_t function)
Webserver->on("/exs", HTTP_GET, ScriptExecuteUploadSuccess);
#ifdef USE_SCRIPT_FATFS
- Webserver->on("/u3", HTTP_POST,[]() { Webserver->sendHeader("Location","/u3");Webserver->send(303);}, script_upload);
- Webserver->on("/u3", HTTP_GET, ScriptFileUploadSuccess);
+ Webserver->on("/u13", HTTP_POST,[]() { Webserver->sendHeader("Location","/u13");Webserver->send(303);}, script_upload);
+ Webserver->on("/u13", HTTP_GET, ScriptFileUploadSuccess);
Webserver->on("/upl", HTTP_GET, Script_FileUploadConfiguration);
#endif //USE_SCRIPT_FATFS
break;
From 8038c2446029a3e31ab15b2e60224abfb322d798 Mon Sep 17 00:00:00 2001
From: Jason2866 <24528715+Jason2866@users.noreply.github.com>
Date: Thu, 17 Sep 2020 18:17:26 +0200
Subject: [PATCH 062/148] Use Tasmota stage, core 2.7.4.2
core 2.7.4.2 includes some backports from arduino / master
https://github.com/esp8266/Arduino/pull/7547
https://github.com/esp8266/Arduino/pull/7586
https://github.com/esp8266/Arduino/pull/7585
https://github.com/esp8266/Arduino/pull/7595
---
platformio_override_sample.ini | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/platformio_override_sample.ini b/platformio_override_sample.ini
index ddd74a60d..188e05e7e 100644
--- a/platformio_override_sample.ini
+++ b/platformio_override_sample.ini
@@ -88,7 +88,7 @@ extra_scripts = ${scripts_defaults.extra_scripts}
[tasmota_stage]
; *** Esp8266 core for Arduino version Tasmota stage
platform = espressif8266@2.6.2
-platform_packages = jason2866/framework-arduinoespressif8266
+platform_packages = framework-arduinoespressif8266@https://github.com/Jason2866/Arduino.git#2.7.4.2
build_unflags = ${esp_defaults.build_unflags}
build_flags = ${esp82xx_defaults.build_flags}
From ecf9e4ea65cb1ee053ac307cc4290d0bc8a4b7a0 Mon Sep 17 00:00:00 2001
From: stefanbode
Date: Fri, 18 Sep 2020 08:31:05 +0200
Subject: [PATCH 063/148] Fix int16 overflow on large open times
---
tasmota/xdrv_27_shutter.ino | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/tasmota/xdrv_27_shutter.ino b/tasmota/xdrv_27_shutter.ino
index 19e89f113..a1625c786 100644
--- a/tasmota/xdrv_27_shutter.ino
+++ b/tasmota/xdrv_27_shutter.ino
@@ -560,7 +560,7 @@ int32_t ShutterCalculatePosition(uint32_t i)
if (Shutter[i].direction != 0) {
switch (ShutterGlobal.position_mode) {
case SHT_COUNTER:
- return ((int32_t)RtcSettings.pulse_counter[i]*Shutter[i].direction*STEPS_PER_SECOND*RESOLUTION / ShutterGlobal.open_velocity_max)+Shutter[i].start_position;
+ return ((int32_t)RtcSettings.pulse_counter[i]*Shutter[i].direction*STEPS_PER_SECOND / ShutterGlobal.open_velocity_max * RESOLUTION)+Shutter[i].start_position;
break;
case SHT_TIME:
case SHT_TIME_UP_DOWN:
From aafa31ff8c2991ef99ee3898d8a1a8b4f0503435 Mon Sep 17 00:00:00 2001
From: Staars
Date: Fri, 18 Sep 2020 17:46:58 +0200
Subject: [PATCH 064/148] NRF24: add MHOC and ATC
---
tasmota/xsns_61_MI_NRF24.ino | 196 +++++++++++++++++++++--------------
1 file changed, 120 insertions(+), 76 deletions(-)
diff --git a/tasmota/xsns_61_MI_NRF24.ino b/tasmota/xsns_61_MI_NRF24.ino
index 44456a6a7..bc945ef2a 100644
--- a/tasmota/xsns_61_MI_NRF24.ino
+++ b/tasmota/xsns_61_MI_NRF24.ino
@@ -20,7 +20,8 @@
--------------------------------------------------------------------------------------------
Version yyyymmdd Action Description
--------------------------------------------------------------------------------------------
-
+ 0.9.8.0 20200918 integrate - add MHOC303, ATC-custom FW, allow lower case commands
+ ---
0.9.8.0 20200705 integrate - add YEE-RC, NLIGHT and MJYD2S, add NRFUSE
---
0.9.7.0 20200624 integrate - fix BEARSSL-decryption, remove MBEDTLS, prepare night light sensors
@@ -84,8 +85,10 @@
#define MJYD2S 8
#define YEERC 9
#define MHOC401 10
+#define MHOC303 11
+#define ATC 12
-#define MI_TYPES 10 //count this manually
+#define MI_TYPES 12 //count this manually
#define D_CMND_NRF "NRF"
@@ -121,7 +124,9 @@ const uint16_t kMINRFDeviceID[MI_TYPES]={ 0x0098, // Flora
0x03dd, // NLIGHT
0x07f6, // MJYD2S
0x0153, // yee-rc
- 0x0387 // MHO-C401
+ 0x0387, // MHO-C401
+ 0x06d3, // MHO-C303
+ 0x0a1c // ATC -> this is a fake ID
};
const char kMINRFDeviceType1[] PROGMEM = "Flora";
@@ -134,7 +139,9 @@ const char kMINRFDeviceType7[] PROGMEM = "NLIGHT";
const char kMINRFDeviceType8[] PROGMEM = "MJYD2S";
const char kMINRFDeviceType9[] PROGMEM = "YEERC";
const char kMINRFDeviceType10[] PROGMEM = "MHOC401";
-const char * kMINRFDeviceType[] PROGMEM = {kMINRFDeviceType1,kMINRFDeviceType2,kMINRFDeviceType3,kMINRFDeviceType4,kMINRFDeviceType5,kMINRFDeviceType6,kMINRFDeviceType7,kMINRFDeviceType8,kMINRFDeviceType9,kMINRFDeviceType10};
+const char kMINRFDeviceType11[] PROGMEM = "MHOC303";
+const char kMINRFDeviceType12[] PROGMEM = "ATC";
+const char * kMINRFDeviceType[] PROGMEM = {kMINRFDeviceType1,kMINRFDeviceType2,kMINRFDeviceType3,kMINRFDeviceType4,kMINRFDeviceType5,kMINRFDeviceType6,kMINRFDeviceType7,kMINRFDeviceType8,kMINRFDeviceType9,kMINRFDeviceType10,kMINRFDeviceType11,kMINRFDeviceType12};
// PDU's or different channels 37-39
const uint32_t kMINRFFloPDU[3] = {0x3eaa857d,0xef3b8730,0x71da7b46};
@@ -146,11 +153,13 @@ const uint32_t kMINRFCGGPDU[3] = {0x4760cd6e,0xdbcc0cdb,0x33048dfd};
const uint32_t kMINRFCGDPDU[3] = {0x5da0d752,0xc10c16e7,0x29c497c1};
// const uint32_t kMINRFNLIPDU[3] = {0x4760C56E,0xDBCC04DB,0x0330485FD}; //NLIGHT
const uint32_t kMINRFYRCPDU[3] = {0x216D63E2,0x5C3DD47E,0x0A5D0E96}; //yee-rc - 50 30
+const uint32_t kMINRFATCPDU[3] = {0xA6E4D00A,0xD0CDAD5A,0x8B03FB3A}; //ATC
// start-LSFR for different channels 37-39
const uint8_t kMINRFlsfrList_A[3] = {0x4b,0x17,0x23}; // Flora, LYWSD02
const uint8_t kMINRFlsfrList_B[3] = {0x21,0x72,0x43}; // MJ_HT_V1, LYWSD03, CGx
const uint8_t kMINRFlsfrList_C[3] = {0x38,0x25,0x2e}; // yee-rc
+const uint8_t kMINRFlsfrList_D[3] = {0x26,0x23,0x20}; // ATC
#pragma pack(1) // important!!
@@ -224,6 +233,15 @@ struct mjysd02_Packet_t{
uint8_t data[18];
};
+struct ATCPacket_t{
+ uint8_t MAC[6];
+ int16_t temp; //sadly this is in wrong endianess
+ uint8_t hum;
+ uint8_t batPer;
+ uint16_t batMV;
+ uint8_t frameCnt;
+};
+
union mi_bindKey_t{
struct{
uint8_t key[16];
@@ -363,7 +381,7 @@ bool MINRFinitBLE(uint8_t _mode)
MINRFchangePacketModeTo(_mode);
return true;
}
- // DEBUG_SENSOR_LOG(PSTR("MINRF chip NOT !!!! connected"));
+ // AddLog_P2(LOG_LEVEL_INFO,PSTR("MINRF chip NOT !!!! connected"));
return false;
}
@@ -406,35 +424,38 @@ bool MINRFreceivePacket(void)
MINRFswapbuf((uint8_t*)&MINRF.buffer, sizeof(MINRF.buffer) );
// MINRF_LOG_BUFFER();
- // AddLog_P2(LOG_LEVEL_INFO,PSTR("MINRF: _lsfrlist: %x, chan: %u, mode: %u"),_lsfrlist[MINRF.currentChan],MINRF.currentChan, MINRF.packetMode);
+ // AddLog_P2(LOG_LEVEL_INFO,PSTR("NRF: _lsfrlist: %x, chan: %u, mode: %u"),_lsfrlist[MINRF.currentChan],MINRF.currentChan, MINRF.packetMode);
switch (MINRF.packetMode) {
- case 0: case 7: case 8:
+ case 0: case NLIGHT: case MJYD2S:
MINRFwhiten((uint8_t *)&MINRF.buffer, sizeof(MINRF.buffer), MINRF.channel[MINRF.currentChan] | 0x40); // "BEACON" mode, "NLIGHT" mode, "MJYD2S" mode
break;
- case 1: case 3:
+ case FLORA: case LYWSD02: case MHOC303:
MINRFwhiten((uint8_t *)&MINRF.buffer, sizeof(MINRF.buffer), kMINRFlsfrList_A[MINRF.currentChan]); // "flora" mode, "LYWSD02" mode
break;
- case 2: case 4: case 5: case 6: case MHOC401:
+ case MJ_HT_V1: case LYWSD03: case CGG1: case CGD1: case MHOC401:
MINRFwhiten((uint8_t *)&MINRF.buffer, sizeof(MINRF.buffer), kMINRFlsfrList_B[MINRF.currentChan]); // "MJ_HT_V1" mode, LYWSD03" mode, "CGG1" mode, "CGD1" mode
break;
- case 9:
+ case YEERC:
MINRFwhiten((uint8_t *)&MINRF.buffer, sizeof(MINRF.buffer), kMINRFlsfrList_C[MINRF.currentChan]); // "YEE-RC" mode
break;
+ case ATC:
+ MINRFwhiten((uint8_t *)&MINRF.buffer, sizeof(MINRF.buffer), kMINRFlsfrList_D[MINRF.currentChan]); // ATC
+ break;
}
- // DEBUG_SENSOR_LOG(PSTR("MINRF: LSFR:%x"),_lsfr);
+ // DEBUG_SENSOR_LOG(PSTR("NRF: LSFR:%x"),_lsfr);
// if (_lsfr>254) _lsfr=0;
}
- // DEBUG_SENSOR_LOG(PSTR("MINRF: did read FIFO"));
+ // DEBUG_SENSOR_LOG(PSTR("NRF: did read FIFO"));
return true;
}
// #ifdef DEBUG_TASMOTA_SENSOR
void MINRFshowBuffer(uint8_t (&buf)[32]){ // we use this only for the 32-byte-FIFO-buffer, so 32 is hardcoded
- // DEBUG_SENSOR_LOG(PSTR("MINRF: Buffer: %c %c %c %c %c %c %c %c"
+ // DEBUG_SENSOR_LOG(PSTR("NRF: Buffer: %c %c %c %c %c %c %c %c"
// " %c %c %c %c %c %c %c %c"
// " %c %c %c %c %c %c %c %c"
// " %c %c %c %c %c %c %c %c")
- DEBUG_SENSOR_LOG(PSTR("MINRF: Buffer: %02x %02x %02x %02x %02x %02x %02x %02x "
+ DEBUG_SENSOR_LOG(PSTR("NRF: Buffer: %02x %02x %02x %02x %02x %02x %02x %02x "
"%02x %02x %02x %02x %02x %02x %02x %02x "
"%02x %02x %02x %02x %02x %02x %02x %02x "
"%02x %02x %02x %02x %02x %02x %02x %02x ")
@@ -511,7 +532,7 @@ void MINRFhandleScan(void){
MINRFscanResult.erase(std::remove_if(MINRFscanResult.begin(),
MINRFscanResult.end(),
[&i](scan_entry_t e) {
- if(e.showedUp>2) AddLog_P2(LOG_LEVEL_INFO,PSTR("MINRF: Beacon %02u: %02X%02X%02X%02X%02X%02X Cid: %04X Svc: %04X UUID: %04X"),i,e.MAC[0],e.MAC[1],e.MAC[2],e.MAC[3],e.MAC[4],e.MAC[5],e.cid,e.svc,e.uuid);
+ if(e.showedUp>2) AddLog_P2(LOG_LEVEL_INFO,PSTR("NRF: Beacon %02u: %02X%02X%02X%02X%02X%02X Cid: %04X Svc: %04X UUID: %04X"),i,e.MAC[0],e.MAC[1],e.MAC[2],e.MAC[3],e.MAC[4],e.MAC[5],e.cid,e.svc,e.uuid);
i++;
return ((e.showedUp < 3));
}),
@@ -524,7 +545,7 @@ void MINRFhandleScan(void){
for(uint32_t i=0; i30) break;
uint32_t ADtype = _buf[i+1];
- // AddLog_P2(LOG_LEVEL_DEBUG,PSTR("MINRF: Size: %u AD: %x i:%u"), size, ADtype,i);
+ // AddLog_P2(LOG_LEVEL_DEBUG,PSTR("NRF: Size: %u AD: %x i:%u"), size, ADtype,i);
if (size+i>32+offset) size=32-i+offset-2;
if (size>30) break;
char _stemp[(size*2)];
uint32_t backupSize;
switch(ADtype){
case 0x01:
- AddLog_P2(LOG_LEVEL_DEBUG,PSTR("MINRF: Flags: %02x"), _buf[i+2]);
+ AddLog_P2(LOG_LEVEL_DEBUG,PSTR("NRF: Flags: %02x"), _buf[i+2]);
break;
case 0x02: case 0x03:
entry->uuid = _buf[i+3]*256 + _buf[i+2];
- AddLog_P2(LOG_LEVEL_DEBUG,PSTR("MINRF: UUID: %04x"), entry->uuid);
+ AddLog_P2(LOG_LEVEL_DEBUG,PSTR("NRF: UUID: %04x"), entry->uuid);
success = true;
break;
case 0x08: case 0x09:
backupSize = _buf[i+size+1];
_buf[i+size+1] = 0;
- AddLog_P2(LOG_LEVEL_DEBUG,PSTR("MINRF: Name: %s"), (char*)&_buf[i+2]);
+ AddLog_P2(LOG_LEVEL_DEBUG,PSTR("NRF: Name: %s"), (char*)&_buf[i+2]);
success = true;
_buf[i+size+1] = backupSize;
break;
case 0x0a:
- AddLog_P2(LOG_LEVEL_DEBUG,PSTR("MINRF: TxPow: %02u"), _buf[i+2]);
+ AddLog_P2(LOG_LEVEL_DEBUG,PSTR("NRF: TxPow: %02u"), _buf[i+2]);
break;
case 0xff:
entry->cid = _buf[i+3]*256 + _buf[i+2];
- AddLog_P2(LOG_LEVEL_DEBUG,PSTR("MINRF: Cid: %04x"), entry->cid);
+ AddLog_P2(LOG_LEVEL_DEBUG,PSTR("NRF: Cid: %04x"), entry->cid);
ToHex_P((unsigned char*)&_buf+i+4,size-3,_stemp,(size*2));
AddLog_P2(LOG_LEVEL_DEBUG,PSTR("%s"),_stemp);
success = true;
break;
case 0x16:
entry->svc = _buf[i+3]*256 + _buf[i+2];
- AddLog_P2(LOG_LEVEL_DEBUG,PSTR("MINRF: Svc: %04x"), entry->svc);
+ AddLog_P2(LOG_LEVEL_DEBUG,PSTR("NRF: Svc: %04x"), entry->svc);
ToHex_P((unsigned char*)&_buf+i+4,size-3,_stemp,(size*2));
AddLog_P2(LOG_LEVEL_DEBUG,PSTR("%s"),_stemp);
success = true;
@@ -712,7 +733,7 @@ int MINRFdecryptPacket(char *_buf){
br_ccm_run(&ctx, 0, output, sizeof(packet->payload.cipher));
ret = br_ccm_check_tag(&ctx, packet->payload.tag);
- AddLog_P2(LOG_LEVEL_DEBUG,PSTR("BEARSSL: Err:%i, Decrypted : %02x %02x %02x %02x %02x "), ret, output[0],output[1],output[2],output[3],output[4]);
+ AddLog_P2(LOG_LEVEL_DEBUG,PSTR("NRF: Err:%i, Decrypted : %02x %02x %02x %02x %02x "), ret, output[0],output[1],output[2],output[3],output[4]);
memcpy((uint8_t*)(packet->payload.cipher)+1,output,sizeof(packet->payload.cipher));
return ret;
}
@@ -833,6 +854,7 @@ void MINRFAddKey(char* payload){
*/
void MINRFKeyMACStringToBytes(char* _string,uint8_t _keyMAC[]) { //uppercase
uint32_t index = 0;
+ UpperCase(_string,_string);
while (index < 44) {
char c = _string[index];
uint8_t value = 0;
@@ -843,9 +865,9 @@ void MINRFKeyMACStringToBytes(char* _string,uint8_t _keyMAC[]) { //uppercase
_keyMAC[(index/2)] += value << (((index + 1) % 2) * 4);
index++;
}
- DEBUG_SENSOR_LOG(PSTR("MINRF: %s to:"),_string);
- DEBUG_SENSOR_LOG(PSTR("MINRF: key-array: %02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X"),_keyMAC[0],_keyMAC[1],_keyMAC[2],_keyMAC[3],_keyMAC[4],_keyMAC[5],_keyMAC[6],_keyMAC[7],_keyMAC[8],_keyMAC[9],_keyMAC[10],_keyMAC[11],_keyMAC[12],_keyMAC[13],_keyMAC[14],_keyMAC[15]);
- DEBUG_SENSOR_LOG(PSTR("MINRF: MAC-array: %02X%02X%02X%02X%02X%02X"),_keyMAC[16],_keyMAC[17],_keyMAC[18],_keyMAC[19],_keyMAC[20],_keyMAC[21]);
+ DEBUG_SENSOR_LOG(PSTR("NRF: %s to:"),_string);
+ DEBUG_SENSOR_LOG(PSTR("NRF: key-array: %02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X"),_keyMAC[0],_keyMAC[1],_keyMAC[2],_keyMAC[3],_keyMAC[4],_keyMAC[5],_keyMAC[6],_keyMAC[7],_keyMAC[8],_keyMAC[9],_keyMAC[10],_keyMAC[11],_keyMAC[12],_keyMAC[13],_keyMAC[14],_keyMAC[15]);
+ DEBUG_SENSOR_LOG(PSTR("NRF: MAC-array: %02X%02X%02X%02X%02X%02X"),_keyMAC[16],_keyMAC[17],_keyMAC[18],_keyMAC[19],_keyMAC[20],_keyMAC[21]);
}
#endif //USE_MI_DECRYPTION
/**
@@ -856,6 +878,7 @@ void MINRFKeyMACStringToBytes(char* _string,uint8_t _keyMAC[]) { //uppercase
*/
void MINRFMACStringToBytes(char* _string, uint8_t _MAC[]) { //uppercase
uint32_t index = 0;
+ UpperCase(_string,_string);
while (index < 12) {
char c = _string[index];
uint8_t value = 0;
@@ -866,7 +889,7 @@ void MINRFMACStringToBytes(char* _string, uint8_t _MAC[]) { //uppercase
_MAC[(index/2)] += value << (((index + 1) % 2) * 4);
index++;
}
- // DEBUG_SENSOR_LOG(PSTR("MINRF: %s to MAC-array: %02X%02X%02X%02X%02X%02X"),_string,_MAC[0],_MAC[1],_MAC[2],_MAC[3],_MAC[4],_MAC[5]);
+ // DEBUG_SENSOR_LOG(PSTR("NRF: %s to MAC-array: %02X%02X%02X%02X%02X%02X"),_string,_MAC[0],_MAC[1],_MAC[2],_MAC[3],_MAC[4],_MAC[5]);
}
/**
@@ -876,7 +899,7 @@ void MINRFMACStringToBytes(char* _string, uint8_t _MAC[]) { //uppercase
void MINRFcomputefirstUsedPacketMode(void){
for (uint32_t i = 0; iMI_TYPES) MINRF.firstUsedPacketMode=0;
break;
@@ -913,34 +936,37 @@ void MINRFchangePacketModeTo(uint8_t _mode) {
case 0: // normal BLE advertisement
NRF24radio.openReadingPipe(0,0x6B7D9171); // advertisement address: 0x8E89BED6 (bit-reversed -> 0x6B7D9171)
break;
- case 1: // special flora packet
+ case FLORA: // special flora packet
NRF24radio.openReadingPipe(0,kMINRFFloPDU[_nextchannel]); // 95 fe 71 20 -> flora
break;
- case 2: // special MJ_HT_V1 packet
+ case MJ_HT_V1: // special MJ_HT_V1 packet
NRF24radio.openReadingPipe(0,kMINRFMJPDU[_nextchannel]); // 95 fe 50 20 -> MJ_HT_V1
break;
- case 3: // special LYWSD02 packet
+ case LYWSD02: case MHOC303: // special LYWSD02 packet
NRF24radio.openReadingPipe(0,kMINRFL2PDU[_nextchannel]);// 95 fe 70 20 -> LYWSD02
break;
- case 4: case MHOC401: // special LYWSD03 packet, MHOC401 has the same
+ case LYWSD03: case MHOC401: // special LYWSD03 packet, MHOC401 has the same
NRF24radio.openReadingPipe(0,kMINRFL3PDU[_nextchannel]);// 95 fe 58 58 -> LYWSD03 (= encrypted data message)
break;
- case 5: // special CGG1 packet
+ case CGG1: // special CGG1 packet
NRF24radio.openReadingPipe(0,kMINRFCGGPDU[_nextchannel]); // 95 fe 50 30 -> CGG1
break;
- case 6: // special CGD1 packet
+ case CGD1: // special CGD1 packet
NRF24radio.openReadingPipe(0,kMINRFCGDPDU[_nextchannel]); // cd fd 08 0c -> CGD1
break;
- case 7: case 8:// MAC based LIGHT packet
+ case NLIGHT: case MJYD2S:// MAC based LIGHT packet
if (MIBLElights.size()==0) break;
NRF24radio.openReadingPipe(0,MIBLElights[MINRF.activeLight].PDU[_nextchannel]); // computed from MAC -> NLIGHT and MJYSD2S
MINRF.activeLight++;
break;
- case 9: // YEE-RC packet
+ case YEERC: // YEE-RC packet
NRF24radio.openReadingPipe(0,kMINRFYRCPDU[_nextchannel]);// 95 fe 50 30 -> YEE-RC
break;
+ case ATC:
+ NRF24radio.openReadingPipe(0,kMINRFATCPDU[_nextchannel]);// 10 16 1a 18 -> ATC
+ break;
}
- // DEBUG_SENSOR_LOG(PSTR("MINRF: Change Mode to %u"),_mode);
+ // DEBUG_SENSOR_LOG(PSTR("NRF: Change Mode to %u"),_mode);
MINRF.packetMode = _mode;
}
@@ -953,27 +979,27 @@ void MINRFchangePacketModeTo(uint8_t _mode) {
*/
uint32_t MINRFgetSensorSlot(uint8_t (&_MAC)[6], uint16_t _type){
- DEBUG_SENSOR_LOG(PSTR("MINRF: will test ID-type: %x"), _type);
+ DEBUG_SENSOR_LOG(PSTR("NRF: will test ID-type: %x"), _type);
bool _success = false;
for (uint32_t i=0;itype,MINRF.buffer.miBeacon.type);
+ DEBUG_SENSOR_LOG(PSTR("NRF: %u %u %u"),_slot,_sensorVec->type,MINRF.buffer.miBeacon.type);
float _tempFloat;
int decryptRet;
@@ -1082,7 +1108,7 @@ void MINRFhandleMiBeaconPacket(void){
switch(MINRF.buffer.miBeacon.type){
case 0x1:
if(MINRF.buffer.miBeacon.counter==_sensorVec->lastCnt) break;
- // AddLog_P2(LOG_LEVEL_INFO,PSTR("MINRF: YEE-RC button: %u Long: %u"), MINRF.buffer.miBeacon.Btn.num, MINRF.buffer.miBeacon.Btn.longPress);
+ // AddLog_P2(LOG_LEVEL_INFO,PSTR("NRF: YEE-RC button: %u Long: %u"), MINRF.buffer.miBeacon.Btn.num, MINRF.buffer.miBeacon.Btn.longPress);
_sensorVec->lastCnt=MINRF.buffer.miBeacon.counter;
_sensorVec->btn=MINRF.buffer.miBeacon.Btn.num + (MINRF.buffer.miBeacon.Btn.longPress/2)*6;
_sensorVec->shallSendMQTT = 1;
@@ -1154,7 +1180,7 @@ void MINRFhandleMiBeaconPacket(void){
void MINRFhandleCGD1Packet(void){ // no MiBeacon
MINRFreverseMAC(MINRF.buffer.CGDPacket.MAC);
uint32_t _slot = MINRFgetSensorSlot(MINRF.buffer.CGDPacket.MAC, 0x0576); // This must be hard-coded, no object-id in Cleargrass-packet
- DEBUG_SENSOR_LOG(PSTR("MINRF: Sensor slot: %u"), _slot);
+ DEBUG_SENSOR_LOG(PSTR("NRF: Sensor slot: %u"), _slot);
if(_slot==0xff) return;
switch (MINRF.buffer.CGDPacket.mode){
@@ -1179,7 +1205,7 @@ void MINRFhandleCGD1Packet(void){ // no MiBeacon
}
break;
default:
- DEBUG_SENSOR_LOG(PSTR("MINRF: unexpected CGD1-packet"));
+ DEBUG_SENSOR_LOG(PSTR("NRF: unexpected CGD1-packet"));
MINRF_LOG_BUFFER(MINRF.buffer.raw);
}
}
@@ -1188,10 +1214,10 @@ void MINRFhandleNlightPacket(void){ // no MiBeacon
uint32_t offset = 6;
uint8_t _buf[32+offset];
MINRFrecalcBuffer((uint8_t*)&_buf,offset);
- // AddLog_P2(LOG_LEVEL_INFO,PSTR("MINRF: NLIGHT: %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x"),_buf[0],_buf[1],_buf[2],_buf[3],_buf[4],_buf[5],_buf[6],_buf[7],_buf[8],_buf[9],_buf[10],_buf[11],_buf[12],_buf[13],_buf[14],_buf[15],_buf[16],_buf[17],_buf[18]);
+ // AddLog_P2(LOG_LEVEL_INFO,PSTR("NRF: NLIGHT: %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x"),_buf[0],_buf[1],_buf[2],_buf[3],_buf[4],_buf[5],_buf[6],_buf[7],_buf[8],_buf[9],_buf[10],_buf[11],_buf[12],_buf[13],_buf[14],_buf[15],_buf[16],_buf[17],_buf[18]);
uint32_t _frame_PID = _buf[15]<<24 | _buf[16]<<16 | _buf[17]<<8 | _buf[18];
if(_frame_PID!=0x4030dd03) return; // invalid packet
- // AddLog_P2(LOG_LEVEL_INFO,PSTR("MINRF: NLIGHT:%x"),_frame_PID);
+ // AddLog_P2(LOG_LEVEL_INFO,PSTR("NRF: NLIGHT:%x"),_frame_PID);
uint32_t _idx = MINRF.activeLight-1;
if((millis() - MIBLElights[_idx].lastTime)<1500) return;
if(_buf[19]!=MIBLElights[_idx].lastCnt){
@@ -1199,7 +1225,7 @@ void MINRFhandleNlightPacket(void){ // no MiBeacon
MIBLElights[_idx].events++;
MIBLElights[_idx].shallSendMQTT = 1;
MIBLElights[_idx].lastTime = millis();
- AddLog_P2(LOG_LEVEL_DEBUG,PSTR("MINRF: NLIGHT %u: events: %u, Cnt:%u"), _idx,MIBLElights[_idx].events, MIBLElights[_idx].lastCnt);
+ AddLog_P2(LOG_LEVEL_DEBUG,PSTR("NRF: NLIGHT %u: events: %u, Cnt:%u"), _idx,MIBLElights[_idx].events, MIBLElights[_idx].lastCnt);
}
}
@@ -1209,20 +1235,20 @@ void MINRFhandleMJYD2SPacket(void){ // no MiBeacon
MINRFrecalcBuffer((uint8_t*)&_buf,offset);
mjysd02_Packet_t *_packet = (mjysd02_Packet_t*)&_buf;
if(_packet->PID!=0x07f6) return; // invalid packet
- // AddLog_P2(LOG_LEVEL_INFO,PSTR("MINRF: MJYD2S: %02u %04x %04x %04x %02x"),_packet->payloadSize,_packet->UUID,_packet->frameCtrl,_packet->PID,_packet->frameCnt);
- // AddLog_P2(LOG_LEVEL_INFO,PSTR("MINRF: PAYLOAD: %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x"),_packet->data[0],_packet->data[1],_packet->data[2],_packet->data[3],_packet->data[4],_packet->data[5],_packet->data[6],_packet->data[7],_packet->data[8],_packet->data[9],_packet->data[10],_packet->data[11],_packet->data[12],_packet->data[13],_packet->data[14],_packet->data[15],_packet->data[16],_packet->data[17]);
+ // AddLog_P2(LOG_LEVEL_INFO,PSTR("NRF: MJYD2S: %02u %04x %04x %04x %02x"),_packet->payloadSize,_packet->UUID,_packet->frameCtrl,_packet->PID,_packet->frameCnt);
+ // AddLog_P2(LOG_LEVEL_INFO,PSTR("NRF: PAYLOAD: %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x"),_packet->data[0],_packet->data[1],_packet->data[2],_packet->data[3],_packet->data[4],_packet->data[5],_packet->data[6],_packet->data[7],_packet->data[8],_packet->data[9],_packet->data[10],_packet->data[11],_packet->data[12],_packet->data[13],_packet->data[14],_packet->data[15],_packet->data[16],_packet->data[17]);
uint32_t _idx = MINRF.activeLight-1;
switch(_packet->frameCtrl){
case 0x5910:
if(_packet->frameCnt!=MIBLElights[_idx].lastCnt){
- // AddLog_P2(LOG_LEVEL_INFO,PSTR("MINRF: MJYD2S after motion:%x"),_packet->frameCnt);
+ // AddLog_P2(LOG_LEVEL_INFO,PSTR("NRF: MJYD2S after motion:%x"),_packet->frameCnt);
MIBLElights[_idx].lastCnt = _packet->frameCnt;
if(millis()-MIBLElights[_idx].lastTime>120000){
MIBLElights[_idx].eventType = 1;
MIBLElights[_idx].events++;
MIBLElights[_idx].shallSendMQTT = 1;
MIBLElights[_idx].lastTime = millis();
- AddLog_P2(LOG_LEVEL_DEBUG,PSTR("MINRF: MJYD2S secondary PIR"));
+ AddLog_P2(LOG_LEVEL_DEBUG,PSTR("NRF: MJYD2S secondary PIR"));
}
}
break;
@@ -1238,7 +1264,7 @@ void MINRFhandleMJYD2SPacket(void){ // no MiBeacon
if(millis()-MIBLElights[_idx].lastTime>1000){
MIBLElights[_idx].eventType = 1; //PIR
MIBLElights[_idx].shallSendMQTT = 1;
- AddLog_P2(LOG_LEVEL_DEBUG,PSTR("MINRF: MJYD2S primary PIR"));
+ AddLog_P2(LOG_LEVEL_DEBUG,PSTR("NRF: MJYD2S primary PIR"));
MIBLElights[_idx].events++;
}
MIBLElights[_idx].lastTime = millis();
@@ -1259,23 +1285,23 @@ void MINRFhandleMJYD2SPacket(void){ // no MiBeacon
MIBLElights[_idx].NMT = output[6]<<24 | output[5]<<16 | output[4]<<8 | output[3];
MIBLElights[_idx].eventType = 3; // NMT 0, 120, 300, 600, 1800, ... seconds
MIBLElights[_idx].shallSendMQTT = 1;
- // AddLog_P2(LOG_LEVEL_DEBUG,PSTR("MINRF: MJYD2S NMT: %u"), MIBLElights[_idx].NMT );
+ // AddLog_P2(LOG_LEVEL_DEBUG,PSTR("NRF: MJYD2S NMT: %u"), MIBLElights[_idx].NMT );
break;
}
}
}
- // AddLog_P2(LOG_LEVEL_INFO,PSTR("MINRF: NLIGHT:%x"),_frame_PID);
+ // AddLog_P2(LOG_LEVEL_INFO,PSTR("NRF: NLIGHT:%x"),_frame_PID);
}
void MINRFhandleLightPacket(void){
switch(MIBLElights[MINRF.activeLight-1].type){
case NLIGHT:
- // AddLog_P2(LOG_LEVEL_INFO,PSTR("MINRF: NLIGHT!!"));
+ // AddLog_P2(LOG_LEVEL_INFO,PSTR("NRF: NLIGHT!!"));
MINRFhandleNlightPacket();
break;
case MJYD2S:
- // AddLog_P2(LOG_LEVEL_INFO,PSTR("MINRF: MJYD2S !!"));
+ // AddLog_P2(LOG_LEVEL_INFO,PSTR("NRF: MJYD2S !!"));
MINRFhandleMJYD2SPacket();
break;
}
@@ -1285,7 +1311,7 @@ void MINRFhandleLightPacket(void){
void MINRFaddLight(uint8_t _MAC[], uint8_t _type){ // no MiBeacon
for(uint32_t i=0; iMAC, 0x0a1c); // This must be a hard-coded fake ID
+ // AddLog_P2(LOG_LEVEL_DEBUG,PSTR("known %s at slot %u"), kMINRFDeviceType[MIBLEsensors[_slot].type-1],_slot);
+ // AddLog_P2(LOG_LEVEL_INFO,PSTR("NRF: ATC: %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x"),MINRF.buffer.raw[0],MINRF.buffer.raw[1],MINRF.buffer.raw[2],MINRF.buffer.raw[3],MINRF.buffer.raw[4],MINRF.buffer.raw[5],MINRF.buffer.raw[6],MINRF.buffer.raw[7],MINRF.buffer.raw[8],MINRF.buffer.raw[9],MINRF.buffer.raw[10],MINRF.buffer.raw[11]);
+ if(_slot==0xff) return;
+
+ MIBLEsensors.at(_slot).temp = (float)(__builtin_bswap16(_packet->temp))/10.0f;
+ MIBLEsensors.at(_slot).hum = (float)_packet->hum;
+ MIBLEsensors.at(_slot).bat = _packet->batPer;
+
+ MIBLEsensors[_slot].shallSendMQTT = 1;
}
/*********************************************************************************************\
@@ -1309,14 +1349,15 @@ void MINRFaddLight(uint8_t _MAC[], uint8_t _type){ // no MiBeacon
void MINRF_EVERY_50_MSECOND() { // Every 50mseconds
if(MINRF.timer>6000){ // happens every 6000/20 = 300 seconds
- DEBUG_SENSOR_LOG(PSTR("MINRF: check for FAKE sensors"));
+ DEBUG_SENSOR_LOG(PSTR("NRF: check for FAKE sensors"));
MINRFpurgeFakeSensors();
MINRF.timer=0;
}
MINRF.timer++;
if (!MINRFreceivePacket()){
- // DEBUG_SENSOR_LOG(PSTR("MINRF: nothing received"));
+ // DEBUG_SENSOR_LOG(PSTR("NRF: nothing received"));
+ // if (MINRF.packetMode==ATC) AddLog_P2(LOG_LEVEL_INFO,PSTR("no ATC.."));
}
else {
@@ -1327,7 +1368,7 @@ void MINRF_EVERY_50_MSECOND() { // Every 50mseconds
}
else MINRFhandleScan();
break;
- case FLORA: case MJ_HT_V1: case LYWSD02: case CGG1: case LYWSD03: case YEERC: case MHOC401:
+ case FLORA: case MJ_HT_V1: case LYWSD02: case CGG1: case LYWSD03: case YEERC: case MHOC401: case MHOC303:
MINRFhandleMiBeaconPacket();
break;
case CGD1:
@@ -1336,6 +1377,9 @@ void MINRF_EVERY_50_MSECOND() { // Every 50mseconds
case NLIGHT: //case MJYD2S:
MINRFhandleLightPacket();
break;
+ case ATC:
+ MINRFhandleATCPacket();
+ break;
default:
break;
}
@@ -1534,7 +1578,7 @@ void MINRFShow(bool json)
if (json) {
for (uint32_t i = 0; i < MIBLEsensors.size(); i++) {
if(MIBLEsensors[i].showedUp < 3){
- DEBUG_SENSOR_LOG(PSTR("MINRF: sensor not fully registered yet"));
+ DEBUG_SENSOR_LOG(PSTR("NRF: sensor not fully registered yet"));
if(MIBLEsensors[i].type != YEERC) break; // send every RC code, even if there is a potentially false MAC
}
switch(MIBLEsensors[i].type){
@@ -1647,7 +1691,7 @@ void MINRFShow(bool json)
WSContentSend_PD(HTTP_NRF24NEW, NRF24type, NRF24.chipType, i+1,stemp,MINRF.confirmedSensors);
for (i ; i
Date: Fri, 18 Sep 2020 17:51:20 +0200
Subject: [PATCH 065/148] version correction
---
tasmota/xsns_61_MI_NRF24.ino | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/tasmota/xsns_61_MI_NRF24.ino b/tasmota/xsns_61_MI_NRF24.ino
index bc945ef2a..e9d2841ea 100644
--- a/tasmota/xsns_61_MI_NRF24.ino
+++ b/tasmota/xsns_61_MI_NRF24.ino
@@ -20,7 +20,7 @@
--------------------------------------------------------------------------------------------
Version yyyymmdd Action Description
--------------------------------------------------------------------------------------------
- 0.9.8.0 20200918 integrate - add MHOC303, ATC-custom FW, allow lower case commands
+ 0.9.8.1 20200918 integrate - add MHOC303, ATC-custom FW, allow lower case commands
---
0.9.8.0 20200705 integrate - add YEE-RC, NLIGHT and MJYD2S, add NRFUSE
---
From f6705d6a1a91c4417ef83b1d4287ee5bbb7da2ae Mon Sep 17 00:00:00 2001
From: Federico Leoni
Date: Fri, 18 Sep 2020 18:45:20 -0300
Subject: [PATCH 066/148] Prep for official Hass Discovery
---
tasmota/xdrv_12_home_assistant.ino | 145 +++++++++++++++++++++++++----
1 file changed, 129 insertions(+), 16 deletions(-)
diff --git a/tasmota/xdrv_12_home_assistant.ino b/tasmota/xdrv_12_home_assistant.ino
index ef9d25be9..a9673eb38 100644
--- a/tasmota/xdrv_12_home_assistant.ino
+++ b/tasmota/xdrv_12_home_assistant.ino
@@ -174,6 +174,110 @@ uint8_t hass_init_step = 0;
uint8_t hass_mode = 0;
int hass_tele_period = 0;
+// NEW DISCOVERY
+
+const char HASS_DISCOVER_DEVICE[] PROGMEM = // Basic parameters for Discovery
+ "{\"ip\":\"%s\"," // IP Address
+ "\"dn\":\"%s\"," // Device Name
+ "\"fn\":[%s]," // Friendly Names
+ "\"hn\":\"%s\"," // Host Name
+ "\"mac\":\"%s\"," // Full MAC as Device id
+ "\"md\":\"%s\"," // Module Name
+ "\"ofln\":\"" D_OFFLINE "\"," // Payload Offline
+ "\"onln\":\"" D_ONLINE "\"," // Payload Online
+ "\"state\":[\"%s\",\"%s\",\"%s\",\"%s\"]," // State text for "OFF","ON","TOGGLE","HOLD"
+ "\"sw\":\"%s\"," // Software Version
+ "\"t\":\"%s\"," // Topic
+ "\"ft\":\"%s\"," // Ful Topic
+ "\"tp\":[\"%s\",\"%s\",\"%s\"]," // Topics for command, stat and tele
+ "\"rl\":[%s],\"swc\":[%s],\"btn\":[%s]," // Inputs / Outputs
+ "\"so\":{\"11\":%d,\"13\":%d,\"17\":%d,\"20\":%d," // SetOptions
+ "\"30\":%d,\"37\":%d,\"68\":%d,\"73\":%d,\"80\":%d},"
+ "\"lt_st\":%d,\"ver\":1}"; // Light SubType, and Discovery version
+
+#define D_CMND_HADIS "HaDis"
+
+const char kHAssCommand[] PROGMEM = "|" // No prefix
+ D_CMND_HADIS;
+
+void (* const HAssCommand[])(void) PROGMEM = { &CmndHaDis };
+
+void CmndHaDis(void) { // New discovery manager. Stored on E98 in settings.h
+
+ uint8_t index = XdrvMailbox.index;
+ uint8_t payload1 = XdrvMailbox.payload;
+ if (XdrvMailbox.data_len > 0 && (XdrvMailbox.payload > 0 || XdrvMailbox.payload <= 2)) {
+ if (2 == XdrvMailbox.payload) { payload1 = 0; }
+ char scmnd[20];
+ if (Settings.flag.hass_discovery != payload1) {
+ snprintf_P(scmnd, sizeof(scmnd), PSTR(D_CMND_SETOPTION "19 %d"), payload1);
+ ExecuteCommand(scmnd, SRC_IGNORE);
+ }
+ Settings.hass_new_discovery = XdrvMailbox.payload;
+
+ }
+ Response_P(PSTR("{\"" D_CMND_HADIS "\":\"%d\"}"), Settings.hass_new_discovery);
+}
+
+void NewHAssDiscovery(void)
+{
+ char stopic[TOPSZ];
+ char stemp1[TOPSZ];
+ char stemp2[200];
+ char stemp3[TOPSZ];
+ char stemp4[TOPSZ];
+ char stemp5[TOPSZ];
+ char unique_id[30];
+ char *state_topic = stemp1;
+
+ stemp2[0] = '\0';
+ uint32_t maxfn = (devices_present > MAX_FRIENDLYNAMES) ? MAX_FRIENDLYNAMES : (!devices_present) ? 1 : devices_present;
+ for (uint32_t i = 0; i < MAX_FRIENDLYNAMES; i++) {
+ char fname[TOPSZ];
+ snprintf_P(fname, sizeof(fname), PSTR("\"%s\""), EscapeJSONString(SettingsText(SET_FRIENDLYNAME1 +i)).c_str());
+ snprintf_P(stemp2, sizeof(stemp2), PSTR("%s%s%s"), stemp2, (i > 0 ? "," : ""), (i < maxfn) ? fname : "null");
+ }
+
+ stemp3[0] = '\0';
+
+ uint32_t maxrl = (devices_present > MAX_RELAYS) ? MAX_RELAYS : (!devices_present) ? 1 : devices_present;
+
+ for (uint32_t i = 0; i < MAX_RELAYS; i++) {
+ snprintf_P(stemp3, sizeof(stemp3), PSTR("%s%s%d"), stemp3, (i > 0 ? "," : ""), (i < maxfn) ? (Settings.flag.hass_light ? 2 : 1) : 0);
+ }
+
+ stemp4[0] = '\0';
+ //stemp6[0] = '\0';
+ for (uint32_t i = 0; i < MAX_SWITCHES; i++) {
+ snprintf_P(stemp4, sizeof(stemp4), PSTR("%s%s%d"), stemp4, (i > 0 ? "," : ""), PinUsed(GPIO_SWT1, i) ? Settings.switchmode[i] : -1);
+ }
+ stemp5[0] = '\0';
+ for (uint32_t i = 0; i < MAX_KEYS; i++) {
+ snprintf_P(stemp5, sizeof(stemp5), PSTR("%s%s%d"), stemp5, (i > 0 ? "," : ""), PinUsed(GPIO_KEY1, i));
+ }
+
+ mqtt_data[0] = '\0'; // Clear retained message
+
+ // Full 12 chars MAC address as ID
+ String mac_address = WiFi.macAddress();
+ mac_address.replace(":", "");
+ String mac_part = mac_address.substring(0);
+ snprintf_P(unique_id, sizeof(unique_id), PSTR("%s"), mac_address.c_str());
+
+ snprintf_P(stopic, sizeof(stopic), PSTR(HOME_ASSISTANT_DISCOVERY_PREFIX "/discovery/%s/config"), unique_id);
+ GetTopic_P(state_topic, TELE, mqtt_topic, PSTR(D_RSLT_HASS_STATE));
+
+ Response_P(HASS_DISCOVER_DEVICE, WiFi.localIP().toString().c_str(), SettingsText(SET_DEVICENAME),
+ stemp2, my_hostname, unique_id, ModuleName().c_str(), GetStateText(0), GetStateText(1), GetStateText(2), GetStateText(3),
+ my_version, mqtt_topic, MQTT_FULLTOPIC, SUB_PREFIX, PUB_PREFIX, PUB_PREFIX2, stemp3, stemp4, stemp5, Settings.flag.button_swap,
+ Settings.flag.button_single, Settings.flag.decimal_text, Settings.flag.not_power_linked, Settings.flag.hass_light,
+ light_controller.isCTRGBLinked(), Settings.flag3.pwm_multi_channels, Settings.flag3.mqtt_buttons, Settings.flag3.shutter_mode, Light.subtype);
+ MqttPublish(stopic, true);
+
+}
+
+// NEW DISCOVERY
+
void TryResponseAppend_P(const char *format, ...)
{
va_list args;
@@ -364,7 +468,7 @@ void HAssAnnounceRelayLight(void)
}
}
MqttPublish(stopic, true);
- masterlog_level = 4;
+ masterlog_level = 4; // Restore WebLog state
}
}
@@ -421,7 +525,7 @@ void HAssAnnouncerTriggers(uint8_t device, uint8_t present, uint8_t key, uint8_t
}
}
MqttPublish(stopic, true);
- masterlog_level = 4;
+ masterlog_level = 4; // Restore WebLog state
}
}
@@ -472,7 +576,7 @@ void HAssAnnouncerBinSensors(uint8_t device, uint8_t present, uint8_t dual, uint
}
}
MqttPublish(stopic, true);
- masterlog_level = 4;
+ masterlog_level = 4; // Restore WebLog state
}
void HAssAnnounceSwitches(void)
@@ -784,7 +888,7 @@ void HAssAnnounceShutters(void)
}
MqttPublish(stopic, true);
- masterlog_level = 4;
+ masterlog_level = 4; // Restore WebLog state
}
#endif
}
@@ -823,7 +927,7 @@ void HAssAnnounceDeviceInfoAndStatusSensor(void)
MqttPublish(stopic, true);
if (!Settings.flag.hass_discovery) {
masterlog_level = 0;
- AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_LOG "Home Assistant Discovery disabled. "));
+ AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_LOG "Home Assistant Classic Discovery disabled."));
}
}
@@ -842,19 +946,21 @@ void HAssPublishStatus(void)
void HAssDiscovery(void)
{
// Configure Tasmota for default Home Assistant parameters to keep discovery message as short as possible
- if (Settings.flag.hass_discovery)
- { // SetOption19 - Control Home Assistant automatic discovery (See SetOption59)
- Settings.flag.mqtt_response = 0; // SetOption4 - Switch between MQTT RESULT or COMMAND - Response always as RESULT and not as uppercase command
- Settings.flag.decimal_text = 1; // SetOption17 - Switch between decimal or hexadecimal output - Respond with decimal color values
- Settings.flag3.hass_tele_on_power = 1; // SetOption59 - Send tele/%topic%/STATE in addition to stat/%topic%/RESULT - send tele/STATE message as stat/RESULT
- // the purpose of that is so that if HA is restarted, state in HA will be correct within one teleperiod otherwise state
- // will not be correct until the device state is changed this is why in the patterns for switch and light, we tell HA to trigger on STATE, not RESULT.
- //Settings.light_scheme = 0; // To just control color it needs to be Scheme 0 (on hold due to new light configuration)
+ if (Settings.flag.hass_discovery || Settings.hass_new_discovery > 0)
+ { // SetOption19 - Control Home Assistant automatic discovery (See SetOption59)
+ Settings.flag.mqtt_response = 0; // SetOption4 - Switch between MQTT RESULT or COMMAND - Response always as RESULT and not as uppercase command
+ Settings.flag.decimal_text = 1; // SetOption17 - Switch between decimal or hexadecimal output - Respond with decimal color values
+ if (Settings.hass_new_discovery == 1) { // Official Home Assistant Discovery doesn't need the /STATE topic.
+ Settings.flag3.hass_tele_on_power = 1; // SetOption59 - Send tele/%topic%/STATE in addition to stat/%topic%/RESULT - send tele/STATE message as stat/RESULT
+ // the purpose of that is so that if HA is restarted, state in HA will be correct within one teleperiod otherwise state
+ // will not be correct until the device state is changed this is why in the patterns for switch and light, we tell HA to trigger on STATE, not RESULT.
+ }
+ //Settings.light_scheme = 0; // To just control color it needs to be Scheme 0 (on hold due to new light configuration)
}
if (Settings.flag.hass_discovery || (1 == hass_mode))
{ // SetOption19 - Control Home Assistantautomatic discovery (See SetOption59)
- masterlog_level = 4;
+ masterlog_level = 4; // Restore WebLog state
// Send info about buttons
HAssAnnounceButtons();
@@ -881,11 +987,12 @@ void HAssDiscover(void)
{
hass_mode = 1; // Force discovery
hass_init_step = 1; // Delayed discovery
+ Settings.hass_new_discovery = Settings.flag.hass_discovery;
}
void HAssAnyKey(void)
{
- if (!Settings.flag.hass_discovery)
+ if (!Settings.flag.hass_discovery || Settings.hass_new_discovery == 0)
{
return;
} // SetOption19 - Control Home Assistantautomatic discovery (See SetOption59)
@@ -940,7 +1047,7 @@ void HassLwtSubscribe(bool hasslwt)
{
char htopic[TOPSZ];
snprintf_P(htopic, sizeof(htopic), PSTR(HOME_ASSISTANT_LWT_TOPIC));
- if (hasslwt && Settings.flag.hass_discovery) {
+ if (hasslwt && (Settings.flag.hass_discovery || Settings.hass_new_discovery == 2)) {
MqttSubscribe(htopic);
} else { MqttUnsubscribe(htopic); }
}
@@ -983,6 +1090,9 @@ bool Xdrv12(uint8_t function)
case FUNC_MQTT_INIT:
hass_mode = 0; // Discovery only if Settings.flag.hass_discovery is set
hass_init_step = 2; // Delayed discovery
+ if (!Settings.flag.hass_discovery && Settings.hass_new_discovery == 0) {
+ NewHAssDiscovery();
+ }
break;
case FUNC_MQTT_SUBSCRIBE:
@@ -991,6 +1101,9 @@ bool Xdrv12(uint8_t function)
case FUNC_MQTT_DATA:
result = HAssMqttLWT();
break;
+ case FUNC_COMMAND:
+ result = DecodeCommand(kHAssCommand, HAssCommand);
+ break;
}
}
return result;
From 4e84e331039c646ef973d8cf168da1bc69617abe Mon Sep 17 00:00:00 2001
From: Federico Leoni
Date: Fri, 18 Sep 2020 18:45:58 -0300
Subject: [PATCH 067/148] Prep for new Hass Discovery
---
tasmota/settings.h | 5 +++--
tasmota/settings.ino | 4 ++--
2 files changed, 5 insertions(+), 4 deletions(-)
diff --git a/tasmota/settings.h b/tasmota/settings.h
index 0803bcca2..0ce6b9f57 100644
--- a/tasmota/settings.h
+++ b/tasmota/settings.h
@@ -557,7 +557,7 @@ struct {
uint16_t dimmer_hw_min; // E90
uint16_t dimmer_hw_max; // E92
uint32_t deepsleep; // E94
- uint16_t ex2_energy_power_delta; // E98 - Free since 8.4.0.3
+ uint16_t hass_new_discovery; // E98 - ex2_energy_power_delta on 8.4.0.3, replaced on 8.5.0.1
uint8_t shutter_motordelay[MAX_SHUTTERS]; // E9A
int8_t temp_comp; // E9E
uint8_t weight_change; // E9F
@@ -613,7 +613,8 @@ struct {
uint16_t energy_power_delta[3]; // F44
uint16_t shutter_pwmrange[2][MAX_SHUTTERS]; // F4A
- uint8_t free_f5a[90]; // F5A - Decrement if adding new Setting variables just above and below
+
+ uint8_t free_f5a[89]; // F5A - Decrement if adding new Setting variables just above and below
// Only 32 bit boundary variables below
SysBitfield5 flag5; // FB4
diff --git a/tasmota/settings.ino b/tasmota/settings.ino
index 2c17cbad2..ddc5dfc2d 100644
--- a/tasmota/settings.ino
+++ b/tasmota/settings.ino
@@ -1356,7 +1356,7 @@ void SettingsDelta(void)
Settings.ex_sbaudrate = 0;
*/
Settings.flag3.fast_power_cycle_disable = 0;
- Settings.ex2_energy_power_delta = Settings.tuyamcu_topic;
+ Settings.hass_new_discovery = Settings.tuyamcu_topic; // replaced ex2_energy_power_delta on 8.5.0.1
Settings.tuyamcu_topic = 0; // replaced ex_energy_power_delta on 8.5.0.1
}
if (Settings.version < 0x06060015) {
@@ -1514,7 +1514,7 @@ void SettingsDelta(void)
Settings.fallback_module = FALLBACK_MODULE;
}
if (Settings.version < 0x08040003) {
- Settings.energy_power_delta[0] = Settings.ex2_energy_power_delta;
+ Settings.energy_power_delta[0] = Settings.hass_new_discovery; // replaced ex2_energy_power_delta on 8.5.0.1
Settings.energy_power_delta[1] = 0;
Settings.energy_power_delta[2] = 0;
}
From ee475e833a2ed02d60e734e0c40743140692663b Mon Sep 17 00:00:00 2001
From: Jason2866 <24528715+Jason2866@users.noreply.github.com>
Date: Sat, 19 Sep 2020 12:30:03 +0200
Subject: [PATCH 068/148] Remove not needed, add stage core32
---
platformio_override_sample.ini | 25 +++++++++++++++----------
platformio_tasmota32.ini | 6 +++++-
2 files changed, 20 insertions(+), 11 deletions(-)
diff --git a/platformio_override_sample.ini b/platformio_override_sample.ini
index 188e05e7e..dde700a23 100644
--- a/platformio_override_sample.ini
+++ b/platformio_override_sample.ini
@@ -36,7 +36,6 @@ default_envs =
[common]
-platform = ${core.platform}
platform_packages = ${core.platform_packages}
build_unflags = ${core.build_unflags}
build_flags = ${core.build_flags}
@@ -74,20 +73,17 @@ extra_scripts = ${scripts_defaults.extra_scripts}
[core]
; Activate only (one set) if you want to override the standard core defined in platformio.ini !!!
-;platform = ${tasmota_stage.platform}
;platform_packages = ${tasmota_stage.platform_packages}
;build_unflags = ${tasmota_stage.build_unflags}
;build_flags = ${tasmota_stage.build_flags}
-;platform = ${core_stage.platform}
-;platform_packages = ${core_stage.platform_packages}
-;build_unflags = ${core_stage.build_unflags}
-;build_flags = ${core_stage.build_flags}
+platform_packages = ${core_stage.platform_packages}
+build_unflags = ${core_stage.build_unflags}
+build_flags = ${core_stage.build_flags}
[tasmota_stage]
; *** Esp8266 core for Arduino version Tasmota stage
-platform = espressif8266@2.6.2
platform_packages = framework-arduinoespressif8266@https://github.com/Jason2866/Arduino.git#2.7.4.2
build_unflags = ${esp_defaults.build_unflags}
build_flags = ${esp82xx_defaults.build_flags}
@@ -123,9 +119,8 @@ build_flags = ${esp82xx_defaults.build_flags}
[core_stage]
; *** Esp8266 core version. Tasmota stage or Arduino stage version. Built with GCC 10.1 toolchain
-platform = espressif8266@2.6.2
-platform_packages = framework-arduinoespressif8266 @ https://github.com/Jason2866/platform-espressif8266/releases/download/2.9.0/framework-arduinoespressif8266-3.20900.0.tar.gz
- ;framework-arduinoespressif8266 @ https://github.com/esp8266/Arduino.git
+platform_packages = ;framework-arduinoespressif8266 @ https://github.com/Jason2866/platform-espressif8266/releases/download/2.9.0/framework-arduinoespressif8266-3.20900.0.tar.gz
+ framework-arduinoespressif8266 @ https://github.com/esp8266/Arduino.git
toolchain-xtensa @ ~2.100100.0
build_unflags = ${esp_defaults.build_unflags}
-Wswitch-unreachable
@@ -160,6 +155,16 @@ build_flags = ${esp82xx_defaults.build_flags}
; -lstdc++-exc
+[core32]
+; Activate Stage Core32 by removing ";" in next line, if you want to override the standard core32
+;platform_packages = ${core32_stage.platform_packages}
+
+
+[core32_stage]
+platform_packages = tool-esptoolpy@1.20800.0
+ arduino-esp32@https://github.com/espressif/arduino-esp32.git#esp32s2
+
+
; *** Debug version used for PlatformIO Home Project Inspection
[env:tasmota-debug]
build_type = debug
diff --git a/platformio_tasmota32.ini b/platformio_tasmota32.ini
index 739ec2338..bdd07be81 100644
--- a/platformio_tasmota32.ini
+++ b/platformio_tasmota32.ini
@@ -39,9 +39,13 @@ default_envs = ${build_envs.default_envs}
; tasmota32-UK
-[common32]
+[core32]
platform = espressif32@2.0.0
platform_packages = tool-esptoolpy@1.20800.0
+
+[common32]
+platform = ${core32.platform}
+platform_packages = ${core32.platform_packages}
board = esp32dev
board_build.ldscript = esp32_out.ld
board_build.partitions = esp32_partition_app1984k_spiffs64k.csv
From ae2e54d97b90430df19dacaf4cb627db6f7edbfe Mon Sep 17 00:00:00 2001
From: Federico Leoni
Date: Sat, 19 Sep 2020 08:26:29 -0300
Subject: [PATCH 069/148] TuyaMCU update
---
tasmota/xdrv_16_tuyamcu.ino | 9 +++++----
1 file changed, 5 insertions(+), 4 deletions(-)
diff --git a/tasmota/xdrv_16_tuyamcu.ino b/tasmota/xdrv_16_tuyamcu.ino
index 2c4b9e0d6..c58dc2130 100644
--- a/tasmota/xdrv_16_tuyamcu.ino
+++ b/tasmota/xdrv_16_tuyamcu.ino
@@ -444,7 +444,6 @@ bool TuyaSetChannels(void)
if (LT_RGB != light_type) {
for (uint8_t i = 0; i <= idx; i++) {
-
if (Tuya.Snapshot[i] != Tuya.Levels[i]) {
if (i == 0 && LightMode && Tuya.ModeSet ) { noupd = true;}
if (!noupd) {
@@ -457,6 +456,7 @@ bool TuyaSetChannels(void)
}
if (light_type >= LT_RGB) {
+
light_state.getHSB(&hue, &sat, &bri);
sat = changeUIntScale(sat, 0, 255, 0, 100);
bri = changeUIntScale(bri, 0, 255, 0, 100);
@@ -1114,6 +1114,7 @@ bool Xdrv16(uint8_t function)
result = TuyaButtonPressed();
break;
case FUNC_EVERY_SECOND:
+ TuyaSetChannels();
if (TuyaSerial && Tuya.wifi_state != TuyaGetTuyaWifiState()) { TuyaSetWifiLed(); }
if (!Tuya.low_power_mode) {
Tuya.heartbeat_timer++;
@@ -1131,9 +1132,9 @@ bool Xdrv16(uint8_t function)
}
if (Tuya.ignore_topic_timeout < millis()) { Tuya.SuspendTopic = false; }
break;
- case FUNC_SET_CHANNELS:
- result = TuyaSetChannels();
- break;
+ // case FUNC_SET_CHANNELS:
+ // result = TuyaSetChannels();
+ // break;
case FUNC_COMMAND:
result = DecodeCommand(kTuyaCommand, TuyaCommand);
break;
From a9af3baea0c927a5131d332688f0c41338469e09 Mon Sep 17 00:00:00 2001
From: nicandris
Date: Sat, 19 Sep 2020 13:43:14 +0200
Subject: [PATCH 070/148] Added SetOption112 - Use friendly name in zigbee
topic (use with SetOption89)
---
tasmota/settings.h | 2 +-
tasmota/xdrv_23_zigbee_2_devices.ino | 12 +++++++++---
2 files changed, 10 insertions(+), 4 deletions(-)
diff --git a/tasmota/settings.h b/tasmota/settings.h
index 0ce6b9f57..83f6a1a17 100644
--- a/tasmota/settings.h
+++ b/tasmota/settings.h
@@ -131,7 +131,7 @@ typedef union { // Restricted by MISRA-C Rule 18.4 bu
uint32_t alexa_gen_1 : 1; // bit 27 (v8.4.0.3) - SetOption109 - Alexa gen1 mode - if you only have Echo Dot 2nd gen devices
uint32_t zb_disable_autobind : 1; // bit 28 (v8.5.0.1) - SetOption110 - disable Zigbee auto-config when pairing new devices
uint32_t buzzer_freq_mode : 1; // bit 29 (v8.5.0.1) - SetOption111 - Use frequency output for buzzer pin instead of on/off signal
- uint32_t spare30 : 1; // bit 30
+ uint32_t zb_topic_fname : 1; // bit 30 (v8.5.0.1) - SetOption112 - Use friendly name in zigbee topic (use with SetOption89)
uint32_t spare31 : 1; // bit 31
};
} SysBitfield4;
diff --git a/tasmota/xdrv_23_zigbee_2_devices.ino b/tasmota/xdrv_23_zigbee_2_devices.ino
index 82000b2e1..a5a3ab2f9 100644
--- a/tasmota/xdrv_23_zigbee_2_devices.ino
+++ b/tasmota/xdrv_23_zigbee_2_devices.ino
@@ -896,9 +896,15 @@ void Z_Devices::jsonPublishFlush(uint16_t shortaddr) {
attr_list.reset(); // clear the attributes
if (Settings.flag4.zigbee_distinct_topics) {
- char subtopic[16];
- snprintf_P(subtopic, sizeof(subtopic), PSTR("%04X/" D_RSLT_SENSOR), shortaddr);
- MqttPublishPrefixTopic_P(TELE, subtopic, Settings.flag.mqtt_sensor_retain);
+ if (Settings.flag4.zb_topic_fname && fname) {
+ char frtopic[13];
+ snprintf_P(frtopic, sizeof(frtopic) + strlen(fname), PSTR("tele/%s/" D_RSLT_SENSOR), fname);
+ MqttPublish(frtopic, Settings.flag.mqtt_sensor_retain);
+ } else {
+ char subtopic[16];
+ snprintf_P(subtopic, sizeof(subtopic), PSTR("%04X/" D_RSLT_SENSOR), shortaddr);
+ MqttPublishPrefixTopic_P(TELE, subtopic, Settings.flag.mqtt_sensor_retain);
+ }
} else {
MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_SENSOR), Settings.flag.mqtt_sensor_retain);
}
From 7adab74ed57c9bc847f16b82652325b7a775c3a6 Mon Sep 17 00:00:00 2001
From: nicandris
Date: Sat, 19 Sep 2020 14:02:15 +0200
Subject: [PATCH 071/148] Fix possible buffer overflow
---
tasmota/xdrv_23_zigbee_2_devices.ino | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/tasmota/xdrv_23_zigbee_2_devices.ino b/tasmota/xdrv_23_zigbee_2_devices.ino
index a5a3ab2f9..26667786a 100644
--- a/tasmota/xdrv_23_zigbee_2_devices.ino
+++ b/tasmota/xdrv_23_zigbee_2_devices.ino
@@ -897,8 +897,8 @@ void Z_Devices::jsonPublishFlush(uint16_t shortaddr) {
if (Settings.flag4.zigbee_distinct_topics) {
if (Settings.flag4.zb_topic_fname && fname) {
- char frtopic[13];
- snprintf_P(frtopic, sizeof(frtopic) + strlen(fname), PSTR("tele/%s/" D_RSLT_SENSOR), fname);
+ char frtopic[13 + strlen(fname)];
+ snprintf_P(frtopic, sizeof(frtopic), PSTR("tele/%s/" D_RSLT_SENSOR), fname);
MqttPublish(frtopic, Settings.flag.mqtt_sensor_retain);
} else {
char subtopic[16];
From 0fce283532fefd1860f438ac4ac16c4e2dfef416 Mon Sep 17 00:00:00 2001
From: nicandris
Date: Sat, 19 Sep 2020 14:34:16 +0200
Subject: [PATCH 072/148] included prefix3 in topic
---
tasmota/xdrv_23_zigbee_2_devices.ino | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/tasmota/xdrv_23_zigbee_2_devices.ino b/tasmota/xdrv_23_zigbee_2_devices.ino
index 26667786a..6951f626b 100644
--- a/tasmota/xdrv_23_zigbee_2_devices.ino
+++ b/tasmota/xdrv_23_zigbee_2_devices.ino
@@ -898,7 +898,7 @@ void Z_Devices::jsonPublishFlush(uint16_t shortaddr) {
if (Settings.flag4.zigbee_distinct_topics) {
if (Settings.flag4.zb_topic_fname && fname) {
char frtopic[13 + strlen(fname)];
- snprintf_P(frtopic, sizeof(frtopic), PSTR("tele/%s/" D_RSLT_SENSOR), fname);
+ snprintf_P(frtopic, sizeof(frtopic), PSTR("%s/%s/" D_RSLT_SENSOR), SettingsText(SET_MQTTPREFIX3), fname);
MqttPublish(frtopic, Settings.flag.mqtt_sensor_retain);
} else {
char subtopic[16];
From 03da44b2b4ea346b845f15e6516fdcc2b7981038 Mon Sep 17 00:00:00 2001
From: nicandris
Date: Sat, 19 Sep 2020 15:20:17 +0200
Subject: [PATCH 073/148] Clean up of friendly name before setting it to topic
---
tasmota/xdrv_23_zigbee_2_devices.ino | 9 +++++++--
1 file changed, 7 insertions(+), 2 deletions(-)
diff --git a/tasmota/xdrv_23_zigbee_2_devices.ino b/tasmota/xdrv_23_zigbee_2_devices.ino
index 6951f626b..f8467afd9 100644
--- a/tasmota/xdrv_23_zigbee_2_devices.ino
+++ b/tasmota/xdrv_23_zigbee_2_devices.ino
@@ -897,8 +897,13 @@ void Z_Devices::jsonPublishFlush(uint16_t shortaddr) {
if (Settings.flag4.zigbee_distinct_topics) {
if (Settings.flag4.zb_topic_fname && fname) {
- char frtopic[13 + strlen(fname)];
- snprintf_P(frtopic, sizeof(frtopic), PSTR("%s/%s/" D_RSLT_SENSOR), SettingsText(SET_MQTTPREFIX3), fname);
+ //Clean special characters and check size of friendly name
+ char stemp[TOPSZ];
+ strlcpy(stemp, (!strlen(fname)) ? MQTT_TOPIC : fname, sizeof(stemp));
+ MakeValidMqtt(0, stemp);
+ //Create topic with Prefix3 and cleaned up friendly name
+ char frtopic[13 + strlen(stemp)];
+ snprintf_P(frtopic, sizeof(frtopic), PSTR("%s/%s/" D_RSLT_SENSOR), SettingsText(SET_MQTTPREFIX3), stemp);
MqttPublish(frtopic, Settings.flag.mqtt_sensor_retain);
} else {
char subtopic[16];
From 8a3f3b271a079ac071f0bba2f9a29502f3364d6e Mon Sep 17 00:00:00 2001
From: nicandris
Date: Sat, 19 Sep 2020 16:02:19 +0200
Subject: [PATCH 074/148] use TOPSZ for size of topic
---
tasmota/xdrv_23_zigbee_2_devices.ino | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/tasmota/xdrv_23_zigbee_2_devices.ino b/tasmota/xdrv_23_zigbee_2_devices.ino
index f8467afd9..fa7775d03 100644
--- a/tasmota/xdrv_23_zigbee_2_devices.ino
+++ b/tasmota/xdrv_23_zigbee_2_devices.ino
@@ -902,7 +902,7 @@ void Z_Devices::jsonPublishFlush(uint16_t shortaddr) {
strlcpy(stemp, (!strlen(fname)) ? MQTT_TOPIC : fname, sizeof(stemp));
MakeValidMqtt(0, stemp);
//Create topic with Prefix3 and cleaned up friendly name
- char frtopic[13 + strlen(stemp)];
+ char frtopic[TOPSZ];
snprintf_P(frtopic, sizeof(frtopic), PSTR("%s/%s/" D_RSLT_SENSOR), SettingsText(SET_MQTTPREFIX3), stemp);
MqttPublish(frtopic, Settings.flag.mqtt_sensor_retain);
} else {
From f6ef721e814e1579f0a242b2918e57f55357915e Mon Sep 17 00:00:00 2001
From: Jason2866 <24528715+Jason2866@users.noreply.github.com>
Date: Sun, 20 Sep 2020 18:01:14 +0200
Subject: [PATCH 075/148] Use specific commit for ESP32 stage
because with later ones Tasmota32 does not compile.
---
platformio_override_sample.ini | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/platformio_override_sample.ini b/platformio_override_sample.ini
index dde700a23..444a5097c 100644
--- a/platformio_override_sample.ini
+++ b/platformio_override_sample.ini
@@ -162,7 +162,8 @@ build_flags = ${esp82xx_defaults.build_flags}
[core32_stage]
platform_packages = tool-esptoolpy@1.20800.0
- arduino-esp32@https://github.com/espressif/arduino-esp32.git#esp32s2
+ ; latest working commit
+ framework-arduinoespressif32 @ https://github.com/espressif/arduino-esp32.git#c09ec5bd3d35ba7dfc135755ab300e2b45416def
; *** Debug version used for PlatformIO Home Project Inspection
From 20e8a4cfc542ae7fa7ace47c13b25faa92d41715 Mon Sep 17 00:00:00 2001
From: Federico Leoni
Date: Sun, 20 Sep 2020 22:29:02 -0300
Subject: [PATCH 076/148] MAC address VAR for rules
---
tasmota/xdrv_10_rules.ino | 10 +++++++---
1 file changed, 7 insertions(+), 3 deletions(-)
diff --git a/tasmota/xdrv_10_rules.ino b/tasmota/xdrv_10_rules.ino
index c377c4a91..4476fc2b0 100644
--- a/tasmota/xdrv_10_rules.ino
+++ b/tasmota/xdrv_10_rules.ino
@@ -719,9 +719,13 @@ bool RuleSetProcess(uint8_t rule_set, String &event_saved)
RulesVarReplace(commands, F("%UPTIME%"), String(MinutesUptime()));
RulesVarReplace(commands, F("%TIMESTAMP%"), GetDateAndTime(DT_LOCAL));
RulesVarReplace(commands, F("%TOPIC%"), mqtt_topic);
- char unique_id[7];
- snprintf_P(unique_id, sizeof(unique_id), PSTR("%06X"), ESP_getChipId());
- RulesVarReplace(commands, F("%DEVICEID%"), unique_id);
+ snprintf_P(stemp, sizeof(stemp), PSTR("%06X"), ESP_getChipId());
+ RulesVarReplace(commands, F("%DEVICEID%"), stemp);
+ char macaddr[13];
+ String mac_address = WiFi.macAddress();
+ mac_address.replace(":", "");
+ snprintf_P(macaddr, sizeof(macaddr), PSTR("%s"), mac_address.c_str());
+ RulesVarReplace(commands, F("%MACADDR%"), macaddr);
#if defined(USE_TIMERS) && defined(USE_SUNRISE)
RulesVarReplace(commands, F("%SUNRISE%"), String(SunMinutes(0)));
RulesVarReplace(commands, F("%SUNSET%"), String(SunMinutes(1)));
From 2ff756b3cc76982796fce5583efdfea7f8cd36ff Mon Sep 17 00:00:00 2001
From: Stephan Hadinger
Date: Mon, 21 Sep 2020 21:49:32 +0200
Subject: [PATCH 077/148] Change replace ArduinoJson with JSMN for JSON parsing
---
lib/jsmn-shadinger-1.0/README.md | 181 ++++++
lib/jsmn-shadinger-1.0/library.properties | 8 +
lib/jsmn-shadinger-1.0/src/JsonParser.cpp | 524 ++++++++++++++++++
lib/jsmn-shadinger-1.0/src/JsonParser.h | 260 +++++++++
lib/jsmn-shadinger-1.0/src/JsonParser.hpp.gch | Bin 0 -> 2299028 bytes
lib/jsmn-shadinger-1.0/src/jsmn.cpp | 449 +++++++++++++++
lib/jsmn-shadinger-1.0/src/jsmn.h | 118 ++++
lib/jsmn-shadinger-1.0/test/test-json.cpp | 55 ++
tasmota/CHANGELOG.md | 1 +
tasmota/support.ino | 44 +-
tasmota/support_json.ino | 2 +
tasmota/xdrv_01_webserver.ino | 18 +
tasmota/xdrv_05_irremote.ino | 14 +
tasmota/xdrv_05_irremote_full.ino | 85 ++-
tasmota/xdrv_09_timers.ino | 90 +++
tasmota/xdrv_10_rules.ino | 60 +-
tasmota/xdrv_20_hue.ino | 65 ++-
tasmota/xdrv_23_zigbee_1_headers.ino | 36 --
tasmota/xdrv_23_zigbee_2_devices.ino | 58 +-
tasmota/xdrv_23_zigbee_3_hue.ino | 54 +-
tasmota/xdrv_23_zigbee_A_impl.ino | 311 +++++------
21 files changed, 2077 insertions(+), 356 deletions(-)
create mode 100644 lib/jsmn-shadinger-1.0/README.md
create mode 100644 lib/jsmn-shadinger-1.0/library.properties
create mode 100644 lib/jsmn-shadinger-1.0/src/JsonParser.cpp
create mode 100644 lib/jsmn-shadinger-1.0/src/JsonParser.h
create mode 100644 lib/jsmn-shadinger-1.0/src/JsonParser.hpp.gch
create mode 100644 lib/jsmn-shadinger-1.0/src/jsmn.cpp
create mode 100644 lib/jsmn-shadinger-1.0/src/jsmn.h
create mode 100644 lib/jsmn-shadinger-1.0/test/test-json.cpp
diff --git a/lib/jsmn-shadinger-1.0/README.md b/lib/jsmn-shadinger-1.0/README.md
new file mode 100644
index 000000000..f98503eb1
--- /dev/null
+++ b/lib/jsmn-shadinger-1.0/README.md
@@ -0,0 +1,181 @@
+# JSMN lightweight JSON parser for Tasmota
+
+Intro:
+
+## Benefits
+
+## How to use
+
+`{"Device":"0x1234","Power":true,"Temperature":25.5}`
+
+The JSON above is split into 8 tokens, and requires 32 bytes of dynamic memory.
+
+- Token 0: `OBJECT`, size=3: `{"Device":"0x1234","Power":true,"Temperature":25.5}`
+- Token 1: `KEY`: `Device`
+- Token 2: `STRING`: `0x1234`
+- Token 3: `KEY`: `Power`
+- Token 4: `BOOL_TRUE`: `true`
+- Token 5: `KEY`: `Temperature`
+- Token 6: `FLOAT`: `25.5`
+- Token 7: `INVALID`
+
+If what you need is to parse a JSON Object for values with default values:
+```
+#include "JsonParser.h"
+
+char json_buffer[] = "{\"Device\":\"0x1234\",\"Power\":true,\"Temperature\":25.6}";
+JsonParserObject root = JsonParser(json_buffer).getRootObject();
+if (!root) { ResponseCmndChar_P(PSTR("Invalid JSON")); return; }
+
+uint16_t d = root.getUInt(PSTR("DEVICE"), 0xFFFF); // case insensitive
+bool b = root.getBool(PSTR("Power"), false);
+float f = root.getFloat(PSTR("Temperature), -100);
+```
+
+Alternative pattern, if you want to test the existence of the attribute first:
+```
+#include "JsonParser.h"
+
+char json_buffer[] = "{\"Device\":\"0x1234\",\"Power\":true,\"Temperature\":25.6}";
+JsonParserObject root = JsonParser(json_buffer).getRootObject();
+if (!root) { ResponseCmndChar_P(PSTR("Invalid JSON")); return; }
+
+JsonParserToken val = root[PSTR("DEVICE")];
+if (val) {
+ d = val.getUInt();
+}
+val = root[PSTR("Power")];
+if (val) {
+ b = val.getBool();
+}
+val = root[PSTR("Temperature)];
+if (val) {
+ f = val.getFloat();
+}
+```
+
+## Types and conversion
+
+JSMN relies on the concept of JSON Tokens `JsonParserToken`. Tokens do not hold any data, but point to token boundaries in JSON string instead. Every jsmn token has a type, which indicates the type of corresponding JSON token. JSMN for Tasmota extends the type from JSMN to ease further parsing.
+
+Types are:
+- `INVALID` invalid token or end of stream, see Error Handling below
+- `OBJECT` a JSON sub-object, `size()` contains the number of key/values of the object
+- `ARRAY` a JSON array, `size()` contains the number of sub values
+- `STRING` a JSON string, return the sub-string, unescaped, without surrounding quotes. UTF-8 is supported.
+- `PRIMITIVE` an unrecognized JSON token, consider as an error
+- `KEY` a JSON key in an object as a string
+- `NULL` a JSON `null` value, automatically converted to `0` or `""`
+- `BOOL_FALSE` a JSON `false` value, automatically converted to `0` or `""`
+- `BOOL_TRUE` a JSON `true` value, automatically converted to `1` or `""`
+- `UINT` a JSON unsigned int
+- `INT` a JSON negative int
+- `FLOAT` a JSON floating point value, i.e. a numerical value containing a decimal ot `.`
+
+Note: values are converted in this priority: 1/ `UINT`, 2/ `INT`, 3/ `FLOAT`.
+
+`JsonParserToken` support the following getters:
+- `getBool()`: returns true if 'true' or non-zero int (default false)
+- `getUInt()`: converts to unsigned int (default 0), boolean true is 1
+- `getInt()`: converts to signed int (default 0), boolean true is 1
+- `getULong()`: converts to unsigned 64 bits (default 0), boolean is 1
+- `getStr()`: converts to string (default "")
+
+There are variants of the same function for which you can choose the default values. Remember that using a getter if the token type is INVALID returns the default value.
+
+Conversion to `OBJECT` or `ARRAY`:
+- `getObject()`: converts token to `OBJECT` or `INVALID`
+- `getArray()`: converts token to `ARRAY` or `INVALID`
+
+For `JsonParserKey`:
+- `getValue()`: returns the value token associated to the key
+
+## Checking Token types
+
+Type checking for `JsonParserToken`:
+- `isSingleToken()` returns `true` for a single level token, `false` for `OBJECT`, `ARRAY` and `INVALID`
+- `isKey()` returns `true` for a `KEY` within an `OBJECT`
+- `isStr()` returns `true` for `STRING` (note: other types can still be read as strings with `getStr()`)
+- `isNull()` returns `true` for `NULL`
+- `isBool()` returns `true` for `BOOL_FALSE` or `BOOL_TRUE`
+- `isUInt()` returns `true` for `UINT` (see below for number conversions)
+- `isInt()` returns `true` for `INT` (see below for number conversions)
+- `isFloat()` returns `true` for `FLOAT` (see below for number conversions)
+- `isNum()` returns `true` for any numerical value, i.e. `UINT`, `INT` or `FLOAT`
+- `isObject()` returns `true` for `OBJECT`
+- `isArray()` returns `true` for `ARRAY`
+- `isValid()` returns `true`for any type except `INVALID`
+
+JSMN for Tasmota provides sub-classes:
+- `JsonParserKey` of type `KEY` or `INVALID`, used as a return value for Object iterators
+- `JsonParserObject` of type `OBJECT` or `INVALID`, providing iterators
+- `JsonParserArray` of type `ARRAY` or `INVALID`, providing iterators
+
+Converting from Token to Object or Array is done with `token.getObject()` or `token.getArray()`. If the conversion is invalid, the resulting object has type `INVALID` (see Error Handling).
+
+## Iterators and accessors for Objects and Arrays
+
+The `JsonParserObject` provides an easy to use iterator:
+```
+JsonParserToken obj = <...>
+for (auto key : obj) {
+ // key is of type JsonParserKey
+ JsonParserToken valie = key.getValue(); // retrieve the value associated to key
+}
+```
+
+If the object contains only one element, you can retrieve it with `obj.getFirstElement()`.
+
+You can access any key with `obj["Key"]`. Note: the search is on purpose **case insensitive** as it is the norm in Tasmota. The search string can be in PROGMEM. If the token is not found, it is of type `INVALID`.
+
+The `JsonParserArray` provides an easy to use iterator:
+```
+JsonParserArray arr = <...>
+for (auto elt : arr) {
+ // elt is of type JsonParserToken
+}
+```
+
+You can access any element in the array with the `[]` operator. Ex: `arr[0]` fof first element. If the index is invalid, the token has type `INVALID`.
+
+## Memory
+
+The `JsonParserToken` fits in 32 bits, so it can be freely returned or copied without any penalty of object copying. Hence it doesn't need the `const` modifier either, since it is always passed by value.
+
+## Error handling
+
+This library uses a `zero error` pattern. This means that calls never report any error nor throw exceptions. If something wrong happens (bad JSON, token not found...), function return an **Invalid Token**. You can call any function on an Invalid Token, they will always return the same Invalid Token (aka fixed point).
+
+You can easily test if the current token is invalid with the following:
+
+Short version:
+```
+if (token) { /* token is valid */ }
+```
+
+Long version:
+```
+if (token->isValiid()) { /* token is valid */ }
+```
+
+This pattern allows to cascade calls and check only the final result:
+```
+char json_buffer[] = "";
+JsonParserObject json = JsonParser(json_buffer).getRootObject();
+JsonParserToken zbstatus_tok = json["ZbStatus"];
+JsonParserObject zbstatus = zbstatus_tok.getObject();
+if (zbstatus) { /* do some stuff */
+ // reaching this point means: JSON is valid, there is a root object, there is a `ZbStatus` key and it contains a sub-object
+}
+```
+
+Warning: there is an explicit convert to `bool` to allow the short version. Be careful, `(bool)token` is equivalent to `token->isValid()`, it is **NOT** equivalent to `token->getBool()`.
+
+## Limits
+
+Please keep in mind the current limits for this library:
+- Maximum JSON buffer size 2047 bytes
+- Maximum 63 JSON tokens
+- No support for exponent in floats (i.e. `1.0e3` is invalid)
+
+These limits shouldn't be a problem since buffers in Tasmota are limited to 1.2KB. The support for exponent in floats is commented out and can be added if needed (slight increase in Flash size)
\ No newline at end of file
diff --git a/lib/jsmn-shadinger-1.0/library.properties b/lib/jsmn-shadinger-1.0/library.properties
new file mode 100644
index 000000000..674aa76e7
--- /dev/null
+++ b/lib/jsmn-shadinger-1.0/library.properties
@@ -0,0 +1,8 @@
+name=JSMN JSON parser customized and optimized for ESP8266 and Tasmota
+version=1.0
+author=Serge Zaitsev, Stephan Hadinger
+maintainer=Stephan
+sentence=Lightweight in-place JSON parser
+paragraph=
+url=https://github.com/zserge/jsmn
+architectures=esp8266
diff --git a/lib/jsmn-shadinger-1.0/src/JsonParser.cpp b/lib/jsmn-shadinger-1.0/src/JsonParser.cpp
new file mode 100644
index 000000000..f2ae50a97
--- /dev/null
+++ b/lib/jsmn-shadinger-1.0/src/JsonParser.cpp
@@ -0,0 +1,524 @@
+/*
+ JsonParser.h - lightweight JSON parser
+
+ Copyright (C) 2020 Stephan Hadinger
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see .
+*/
+
+#include "JsonParser.h"
+#include "WSTring.h"
+
+/*********************************************************************************************\
+ * Utilities
+\*********************************************************************************************/
+
+const char * k_current_json_buffer = "";
+
+/*********************************************************************************************\
+ * Lightweight String to Float, because atof() or strtof() takes 10KB
+ *
+ * To remove code, exponents are not parsed
+ * (commented out below, just in case we need them after all)
+\*********************************************************************************************/
+// Inspired from https://searchcode.com/codesearch/view/22115068/
+float json_strtof(const char* s) {
+ const char* p = s;
+ float value = 0.;
+ int32_t sign = +1;
+ float factor;
+ // unsigned int expo;
+
+ while (isspace(*p)){ // skip any leading white-spaces
+ p++;
+ }
+
+ switch (*p) {
+ case '-': sign = -1;
+ case '+': p++;
+ default : break;
+ }
+
+ while ((unsigned int)(*p - '0') < 10u) {
+ value = value*10 + (*p++ - '0');
+ }
+
+ if (*p == '.' ) {
+ factor = 1.0f;
+
+ p++;
+ while ((unsigned int)(*p - '0') < 10u) {
+ factor *= 0.1f;
+ value += (*p++ - '0') * factor;
+ }
+ }
+
+// if ( (*p | 32) == 'e' ) {
+// expo = 0;
+// factor = 10.L;
+
+// switch (*++p) { // ja hier weiß ich nicht, was mindestens nach einem 'E' folgenden MUSS.
+// case '-': factor = 0.1;
+// case '+': p++;
+// break;
+// case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9':
+// break;
+// default : value = 0.L;
+// p = s;
+// goto done;
+// }
+
+// while ( (unsigned int)(*p - '0') < 10u )
+// expo = 10 * expo + (*p++ - '0');
+
+// while ( 1 ) {
+// if ( expo & 1 )
+// value *= factor;
+// if ( (expo >>= 1) == 0 )
+// break;
+// factor *= factor;
+// }
+// }
+
+// done:
+ // if ( endptr != NULL )
+ // *endptr = (char*)p;
+
+ return value * sign;
+}
+
+/*********************************************************************************************\
+ * Read-only JSON token object, fits in 32 bits
+\*********************************************************************************************/
+
+void JsonParserToken::skipToken(void) {
+ // printf("skipToken type = %d %s\n", t->type, json_string + t->start);
+ switch (t->type) {
+ case JSMN_OBJECT:
+ skipObject();
+ break;
+ case JSMN_ARRAY:
+ skipArray();
+ break;
+ case JSMN_STRING:
+ case JSMN_PRIMITIVE:
+ case JSMN_KEY:
+ case JSMN_NULL:
+ case JSMN_BOOL_FALSE:
+ case JSMN_BOOL_TRUE:
+ case JSMN_FLOAT:
+ case JSMN_INT:
+ case JSMN_UINT:
+ t++; // skip 1 token
+ break;
+ case JSMN_INVALID:
+ default:
+ break; // end of stream, stop advancing
+ }
+}
+
+void JsonParserToken::skipArray(void) {
+ if (t->type == JSMN_ARRAY) {
+ size_t obj_size = t->size;
+ t++; // array root
+ if (t->type == JSMN_INVALID) { return; }
+ for (uint32_t i=0; itype == JSMN_OBJECT) {
+ size_t obj_size = t->size;
+ t++; // object root
+ if (t->type == JSMN_INVALID) { return; }
+ for (uint32_t i=0; itype == JSMN_INVALID) { return; }
+ skipToken();
+ }
+ }
+}
+
+/*********************************************************************************************\
+ * JsonParserArray
+\*********************************************************************************************/
+
+JsonParserArray::JsonParserArray(const jsmntok_t * token) : JsonParserToken(token) {
+ if (t->type != JSMN_ARRAY) {
+ t = &token_bad;
+ }
+}
+JsonParserArray::JsonParserArray(const JsonParserToken token) : JsonParserToken(token.t) {
+ if (t->type != JSMN_ARRAY) {
+ t = &token_bad;
+ }
+}
+
+JsonParserArray::const_iterator::const_iterator(const JsonParserArray t): tok(t), remaining(0) {
+ if (tok.t == &token_bad) { tok.t = nullptr; }
+ if (nullptr != tok.t) {
+ // ASSERT type == JSMN_ARRAY by constructor
+ remaining = tok.t->size;
+ tok.nextOne(); // skip array root token
+ }
+}
+
+JsonParserArray::const_iterator JsonParserArray::const_iterator::const_iterator::operator++() {
+ if (remaining == 0) { tok.t = nullptr; }
+ else {
+ remaining--;
+ tok.skipToken(); // munch value
+ if (tok.t->type == JSMN_INVALID) { tok.t = nullptr; } // unexpected end of stream
+ }
+ return *this;
+}
+
+JsonParserToken JsonParserArray::operator[](int32_t i) const {
+ if ((i >= 0) && (i < t->size)) {
+ uint32_t index = 0;
+ for (const auto elt : *this) {
+ if (i == index) {
+ return elt;
+ }
+ index++;
+ }
+ }
+ // fallback
+ return JsonParserToken(&token_bad);
+}
+
+/*********************************************************************************************\
+ * JsonParserObject
+\*********************************************************************************************/
+
+JsonParserObject::JsonParserObject(const jsmntok_t * token) : JsonParserToken(token) {
+ if (t->type != JSMN_OBJECT) {
+ t = &token_bad;
+ }
+}
+JsonParserObject::JsonParserObject(const JsonParserToken token) : JsonParserToken(token.t) {
+ if (t->type != JSMN_OBJECT) {
+ t = &token_bad;
+ }
+}
+
+JsonParserKey JsonParserObject::getFirstElement(void) const {
+ if (t->size > 0) {
+ return JsonParserKey(t+1); // return next element and cast to Key
+ } else {
+ return JsonParserKey(&token_bad);
+ }
+}
+
+JsonParserObject::const_iterator::const_iterator(const JsonParserObject t): tok(t), remaining(0) {
+ if (tok.t == &token_bad) { tok.t = nullptr; }
+ if (nullptr != tok.t) {
+ // ASSERT type == JSMN_OBJECT by constructor
+ remaining = tok.t->size;
+ tok.nextOne();
+ }
+}
+
+JsonParserObject::const_iterator JsonParserObject::const_iterator::operator++() {
+ if (remaining == 0) { tok.t = nullptr; }
+ else {
+ remaining--;
+ tok.nextOne(); // munch key
+ if (tok.t->type == JSMN_INVALID) { tok.t = nullptr; } // unexpected end of stream
+ tok.skipToken(); // munch value
+ if (tok.t->type == JSMN_INVALID) { tok.t = nullptr; } // unexpected end of stream
+ }
+ return *this;
+}
+
+/*********************************************************************************************\
+ * JsonParserKey
+\*********************************************************************************************/
+
+
+JsonParserKey::JsonParserKey(const jsmntok_t * token) : JsonParserToken(token) {
+ if (t->type != JSMN_KEY) {
+ t = &token_bad;
+ }
+}
+JsonParserKey::JsonParserKey(const JsonParserToken token) : JsonParserToken(token.t) {
+ if (t->type != JSMN_KEY) {
+ t = &token_bad;
+ }
+}
+
+JsonParserToken JsonParserKey::getValue(void) const {
+ return JsonParserToken(t+1);
+}
+
+/*********************************************************************************************\
+ * Implementation for JSON Parser
+\*********************************************************************************************/
+
+// fall-back token object when parsing failed
+const jsmntok_t token_bad = { JSMN_INVALID, 0, 0, 0 };
+
+JsonParser::JsonParser(char * json_in) :
+ _size(0),
+ _token_len(0),
+ _tokens(nullptr),
+ _json(nullptr)
+{
+ parse(json_in);
+}
+
+JsonParser::~JsonParser() {
+ this->free();
+}
+
+const JsonParserObject JsonParser::getRootObject(void) const {
+ return JsonParserObject(&_tokens[0]);
+}
+
+const JsonParserToken JsonParser::operator[](int32_t i) const {
+if ((_token_len > 0) && (i < _token_len)) {
+ return JsonParserToken(&_tokens[i]);
+ } else {
+ return JsonParserToken(&token_bad);
+ }
+}
+
+// pointer arithmetic
+// ptrdiff_t JsonParser::index(JsonParserToken token) const {
+// return token.t - _tokens;
+// }
+
+bool JsonParserToken::getBool(bool val) const {
+ if (t->type == JSMN_INVALID) { return val; }
+ if (t->type == JSMN_BOOL_TRUE) return true;
+ if (t->type == JSMN_BOOL_FALSE) return false;
+ if (isSingleToken()) return strtol(&k_current_json_buffer[t->start], nullptr, 0) != 0;
+ return false;
+}
+int32_t JsonParserToken::getInt(int32_t val) const {
+ if (t->type == JSMN_INVALID) { return val; }
+ if (t->type == JSMN_BOOL_TRUE) return 1;
+ if (isSingleToken()) return strtol(&k_current_json_buffer[t->start], nullptr, 0);
+ return 0;
+}
+uint32_t JsonParserToken::getUInt(uint32_t val) const {
+ if (t->type == JSMN_INVALID) { return val; }
+ if (t->type == JSMN_BOOL_TRUE) return 1;
+ if (isSingleToken()) return strtoul(&k_current_json_buffer[t->start], nullptr, 0);
+ return 0;
+}
+uint64_t JsonParserToken::getULong(uint64_t val) const {
+ if (t->type == JSMN_INVALID) { return val; }
+ if (t->type == JSMN_BOOL_TRUE) return 1;
+ if (isSingleToken()) return strtoull(&k_current_json_buffer[t->start], nullptr, 0);
+ return 0;
+}
+float JsonParserToken::getFloat(float val) const {
+ if (t->type == JSMN_INVALID) { return val; }
+ if (t->type == JSMN_BOOL_TRUE) return 1;
+ if (isSingleToken()) return json_strtof(&k_current_json_buffer[t->start]);
+ return 0;
+}
+const char * JsonParserToken::getStr(const char * val) const {
+ if (t->type == JSMN_INVALID) { return val; }
+ if (t->type == JSMN_NULL) return "";
+ return (t->type >= JSMN_STRING) ? &k_current_json_buffer[t->start] : "";
+}
+
+
+JsonParserObject JsonParserToken::getObject(void) const { return JsonParserObject(*this); }
+JsonParserArray JsonParserToken::getArray(void) const { return JsonParserArray(*this); }
+
+
+bool JsonParserToken::getBool(void) const { return getBool(false); }
+int32_t JsonParserToken::getInt(void) const { return getInt(0); }
+uint32_t JsonParserToken::getUInt(void) const { return getUInt(0); }
+uint64_t JsonParserToken::getULong(void) const { return getULong(0); }
+float JsonParserToken::getFloat(void) const { return getFloat(0); }
+const char * JsonParserToken::getStr(void) const { return getStr(""); }
+
+int32_t JsonParserObject::getInt(const char * needle, int32_t val) const {
+ return (*this)[needle].getInt(val);
+}
+uint32_t JsonParserObject::getUInt(const char * needle, uint32_t val) const {
+ return (*this)[needle].getUInt(val);
+}
+uint64_t JsonParserObject::getULong(const char * needle, uint64_t val) const {
+ return (*this)[needle].getULong(val);
+}
+const char * JsonParserObject::getStr(const char * needle, const char * val) const {
+ return (*this)[needle].getStr(val);
+}
+
+void JsonParser::parse(char * json_in) {
+ k_current_json_buffer = "";
+ if (nullptr == json_in) { return; }
+ _json = json_in;
+ k_current_json_buffer = _json;
+ size_t json_len = strlen(json_in);
+ if (_size == 0) {
+ // first run is used to count tokens before allocation
+ jsmn_init(&this->_parser);
+ int32_t _token_len = jsmn_parse(&this->_parser, json_in, json_len, nullptr, 0);
+ if (_token_len <= 0) { return; }
+ _size = _token_len + 1;
+ }
+ allocate();
+ jsmn_init(&this->_parser);
+ _token_len = jsmn_parse(&this->_parser, json_in, json_len, _tokens, _size);
+ // TODO error checking
+ if (_token_len >= 0) {
+ postProcess(json_len);
+ }
+}
+
+// post process the parsing by pre-munching extended types
+void JsonParser::postProcess(size_t json_len) {
+ // add an end marker
+ if (_size > _token_len) {
+ _tokens[_token_len].type = JSMN_INVALID;
+ _tokens[_token_len].start = json_len;
+ _tokens[_token_len].len = 0;
+ _tokens[_token_len].size = 0;
+ }
+ for (uint32_t i=0; i<_token_len; i++) {
+ jsmntok_t & tok = _tokens[i];
+
+ if (tok.type >= JSMN_STRING) {
+ // we modify to null-terminate the primitive
+ _json[tok.start + tok.len] = 0;
+ }
+
+ if (tok.type == JSMN_STRING) {
+ if (tok.size == 1) { tok.type = JSMN_KEY; }
+ else { json_unescape(&_json[tok.start]); }
+ } else if (tok.type == JSMN_PRIMITIVE) {
+ if (tok.len >= 0) {
+ // non-null string
+ char c0 = _json[tok.start];
+ switch (c0) {
+ case 'n':
+ case 'N':
+ tok.type = JSMN_NULL;
+ break;
+ case 't':
+ case 'T':
+ tok.type = JSMN_BOOL_TRUE;
+ break;
+ case 'f':
+ case 'F':
+ tok.type = JSMN_BOOL_FALSE;
+ break;
+ case '-':
+ case '0'...'9':
+ // look if there is a '.' in the string
+ if (nullptr != memchr(&_json[tok.start], '.', tok.len)) {
+ tok.type = JSMN_FLOAT;
+ } else if (c0 == '-') {
+ tok.type = JSMN_INT;
+ } else {
+ tok.type = JSMN_UINT;
+ }
+ break;
+ default:
+ tok.type = JSMN_PRIMITIVE;
+ break;
+ }
+ } else {
+ tok.type = JSMN_PRIMITIVE;
+ }
+ }
+ }
+}
+
+JsonParserToken JsonParserObject::operator[](const char * needle) const {
+ // key can be in PROGMEM
+ if ((!this->isValid()) || (nullptr == needle) || (0 == pgm_read_byte(needle))) {
+ return JsonParserToken(&token_bad);
+ }
+ // if needle == "?" then we return the first valid key
+ bool wildcard = (strcmp_P("?", needle) == 0);
+
+ for (const auto key : *this) {
+ if (wildcard) { return key.getValue(); }
+ if (0 == strcasecmp_P(key.getStr(), needle)) { return key.getValue(); }
+ }
+ // if not found
+ return JsonParserToken(&token_bad);
+}
+
+
+JsonParserToken JsonParserObject::findStartsWith(const char * needle) const {
+ // key can be in PROGMEM
+ if ((!this->isValid()) || (nullptr == needle) || (0 == pgm_read_byte(needle))) {
+ return JsonParserToken(&token_bad);
+ }
+
+ String needle_s((const __FlashStringHelper *)needle);
+ needle_s.toLowerCase();
+
+ for (const auto key : *this) {
+ String key_s(key.getStr());
+ key_s.toLowerCase();
+
+ if (key_s.startsWith(needle_s)) {
+ return key.getValue();
+ }
+ }
+ // if not found
+ return JsonParserToken(&token_bad);
+}
+
+const char * JsonParserObject::findConstCharNull(const char * needle) const {
+ const char * r = (*this)[needle].getStr();
+ if (*r == 0) { r = nullptr; } // if empty string
+ return r;
+}
+
+// JsonParserToken JsonParser::find(JsonParserObject obj, const char *needle, bool case_sensitive) const {
+// // key can be in PROGMEM
+// if ((!obj.isValid()) || (nullptr == needle) || (0 == pgm_read_byte(needle))) {
+// return JsonParserToken(&token_bad);
+// }
+// // if needle == "?" then we return the first valid key
+// bool wildcard = (strcmp_P("?", needle) == 0);
+
+// for (const auto key : obj) {
+// if (wildcard) { return key.getValue(); }
+// if (case_sensitive) {
+// if (0 == strcmp_P(this->getStr(key), needle)) { return key.getValue(); }
+// } else {
+// if (0 == strcasecmp_P(this->getStr(key), needle)) { return key.getValue(); }
+// }
+// }
+// // if not found
+// return JsonParserToken(&token_bad);
+// }
+
+void JsonParser::free(void) {
+ if (nullptr != _tokens) {
+ delete[] _tokens; // TODO
+ _tokens = nullptr;
+ }
+}
+
+void JsonParser::allocate(void) {
+ this->free();
+ if (_size != 0) {
+ _tokens = new jsmntok_t[_size];
+ }
+}
diff --git a/lib/jsmn-shadinger-1.0/src/JsonParser.h b/lib/jsmn-shadinger-1.0/src/JsonParser.h
new file mode 100644
index 000000000..42886d628
--- /dev/null
+++ b/lib/jsmn-shadinger-1.0/src/JsonParser.h
@@ -0,0 +1,260 @@
+/*
+ JsonParser.h - lightweight JSON parser
+
+ Copyright (C) 2020 Stephan Hadinger
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see .
+*/
+
+#ifndef __JSON_PARSER__
+#define __JSON_PARSER__
+
+#include "jsmn.h"
+#include
+#include
+
+// #define strcmp_P(x, y) strcmp(x,y)
+// #define strcasecmp_P(x,y) strcasecmp(x,y)
+// #define pgm_read_byte(x) (*(uint8_t*)(x))
+#ifndef ARRAY_SIZE
+#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
+#endif
+
+/*********************************************************************************************\
+ * Utilities
+\*********************************************************************************************/
+
+// The code uses a zero-error approach. Functions never return an error code nor an exception.
+// In case an operation fails, it returns an "Invalid Token".
+// To know if a token is valid, use the `isValid()` method or just use it in an if statement.
+//
+// Internally, the bad token is a pointer to a constant token of type JSMN_INVALID
+// fall-back token object when parsing failed
+const extern jsmntok_t token_bad;
+
+// To reduce code size, the current buffer is stored in a global variable.
+// This prevents all calls to add this parameter on the stack and reduces code size.
+// The caveat is that code is not re-entrant.
+// If you ever intermix two or more JSON parsers, use `parser->setCurrent()` before further calls
+//
+// the current json buffer being used, for convenience
+// Warning: this makes code non-reentrant.
+extern const char * k_current_json_buffer;
+
+/*********************************************************************************************\
+ * Read-only JSON token object, fits in 32 bits
+\*********************************************************************************************/
+// forward class declarations
+class JsonParserObject;
+class JsonParserArray;
+
+class JsonParserToken {
+public:
+
+ // constructor
+ // If parameter is null, we use the Invalid Token instead.
+ JsonParserToken(const jsmntok_t * token) : t(token) {
+ if (nullptr == t) { t = &token_bad; }
+ }
+ // no explicit destructor (not needed)
+
+ inline bool isValid(void) const { return t->type != JSMN_INVALID; }
+ inline size_t size(void) const { return t->size; }
+
+ inline bool isSingleToken(void) const { return (t->type >= JSMN_STRING); }
+ inline bool isKey(void) const { return (t->type == JSMN_KEY); }
+ inline bool isStr(void) const { return (t->type == JSMN_STRING); }
+ inline bool isNull(void) const { return (t->type == JSMN_NULL); }
+ inline bool isBool(void) const { return (t->type == JSMN_BOOL_TRUE) || (t->type == JSMN_BOOL_FALSE); }
+ inline bool isFloat(void) const { return (t->type == JSMN_FLOAT); }
+ inline bool isInt(void) const { return (t->type == JSMN_INT); }
+ inline bool isUint(void) const { return (t->type == JSMN_UINT); }
+ inline bool isNum(void) const { return (t->type >= JSMN_FLOAT) && (t->type <= JSMN_UINT); }
+ inline bool isObject(void) const { return (t->type == JSMN_OBJECT); }
+ inline bool isArray(void) const { return (t->type == JSMN_ARRAY); }
+
+ // move to token immediately after in the buffer
+ void nextOne(void) { if (t->type != JSMN_INVALID) { t++; } }
+
+ // conversion operators
+ // Warning - bool does not test for Boolean value but for validity, i.e. equivalent to token.valid()
+ inline explicit operator bool() const { return t->type != JSMN_INVALID; };
+
+ // all the following conversion will try to give a meaninful value
+ // if the content is not of the right type or the token is invalid, returns the 'default'
+ bool getBool(void) const; // true if 'true' or non-zero int (default false)
+ int32_t getInt(void) const; // convert to int (default 0)
+ uint32_t getUInt(void) const; // convert to unsigned int (default 0)
+ uint64_t getULong(void) const; // convert to unsigned 64 bits (default 0)
+ float getFloat(void) const; // convert to float (default 0), does not support exponent
+ const char * getStr(void) const; // convert to string (default "")
+
+ // same as above, but you can choose the default value
+ bool getBool(bool val) const;
+ int32_t getInt(int32_t val) const;
+ uint32_t getUInt(uint32_t val) const;
+ uint64_t getULong(uint64_t val) const;
+ float getFloat(float val) const;
+ const char * getStr(const char * val) const;
+
+ // convert to JsonParserObject or JsonParserArray, or Invalid Token if not allowed
+ JsonParserObject getObject(void) const;
+ JsonParserArray getArray(void) const;
+
+public:
+ // the following should be 'protected' but then it can't be accessed by iterators
+ const jsmntok_t * t;
+ // skip the next Token as a whole (i.e. skip an entire array)
+ void skipToken(void);
+
+protected:
+
+ // skip the next token knowing it's an array
+ void skipArray(void);
+
+ // skip the next token knowing it's an object
+ void skipObject(void);
+};
+
+/*********************************************************************************************\
+ * Subclass for Key
+\*********************************************************************************************/
+class JsonParserKey : public JsonParserToken {
+public:
+ JsonParserKey(const jsmntok_t * token);
+ explicit JsonParserKey(const JsonParserToken token);
+
+ // get the value token associated to the key
+ JsonParserToken getValue(void) const;
+};
+
+/*********************************************************************************************\
+ * Subclass for Object
+\*********************************************************************************************/
+class JsonParserObject : public JsonParserToken {
+public:
+ JsonParserObject(const jsmntok_t * token);
+ explicit JsonParserObject(const JsonParserToken token);
+
+ // find key with name, case-insensitive, '?' matches any key. Returns Invalid Token if not found
+ JsonParserToken operator[](const char * needle) const;
+ // find a key starting with `needle`, case insensitive
+ JsonParserToken findStartsWith(const char * needle) const;
+ // find a key, case-insensitive, return nullptr if not found (instead of "")
+ const char * findConstCharNull(const char * needle) const;
+
+ // all-in-one methods: search for key (case insensitive), convert value and set default
+ int32_t getInt(const char *, int32_t) const;
+ uint32_t getUInt(const char *, uint32_t) const;
+ uint64_t getULong(const char *, uint64_t) const;
+ float getFloat(const char *, float) const;
+ const char * getStr(const char *, const char *) const;
+
+ // get first element (key)
+ JsonParserKey getFirstElement(void) const;
+
+ //
+ // const iterator
+ //
+ class const_iterator {
+ public:
+ const_iterator(const JsonParserObject t);
+ const_iterator operator++();
+ bool operator!=(const_iterator & other) const { return tok.t != other.tok.t; }
+ const JsonParserKey operator*() const { return JsonParserKey(tok); }
+ private:
+ JsonParserToken tok;
+ size_t remaining;
+ };
+ const_iterator begin() const { return const_iterator(*this); } // start with 'head'
+ const_iterator end() const { return const_iterator(JsonParserObject(&token_bad)); } // end with null pointer
+};
+
+/*********************************************************************************************\
+ * Subclass for Array
+\*********************************************************************************************/
+class JsonParserArray : public JsonParserToken {
+public:
+ JsonParserArray(const jsmntok_t * token);
+ JsonParserArray(const JsonParserToken token);
+
+ // get the element if index `i` from 0 to `size() - 1`
+ JsonParserToken operator[](int32_t i) const;
+
+ //
+ // const iterator
+ //
+ class const_iterator {
+ public:
+ const_iterator(const JsonParserArray t);
+ const_iterator operator++();
+ bool operator!=(const_iterator & other) const { return tok.t != other.tok.t; }
+ const JsonParserToken operator*() const { return tok; }
+ private:
+ JsonParserToken tok;
+ size_t remaining;
+ };
+ const_iterator begin() const { return const_iterator(*this); } // start with 'head'
+ const_iterator end() const { return const_iterator(JsonParserArray(&token_bad)); } // end with null pointer
+};
+
+/*********************************************************************************************\
+ * JSON Parser
+\*********************************************************************************************/
+
+class JsonParser {
+public:
+ // constructor, parse the json buffer
+ // Warning: the buffer is modified in the process (in-place parsing)
+ // Input: `json_in` can be nullptr, but CANNOT be in PROGMEM (remember we need to change characters in-place)
+ JsonParser(char * json_in);
+
+ // destructor
+ ~JsonParser();
+
+ // set the current buffer for attribute access (i.e. set the global)
+ void setCurrent(void) { k_current_json_buffer = _json; }
+
+ // test if the parsing was successful
+ inline explicit operator bool() const { return _token_len > 0; }
+
+ const JsonParserToken getRoot(void) { return JsonParserToken(&_tokens[0]); }
+ // const JsonParserObject getRootObject(void) { return JsonParserObject(&_tokens[0]); }
+ const JsonParserObject getRootObject(void) const;
+
+ // pointer arithmetic
+ // ptrdiff_t index(JsonParserToken token) const;
+
+protected:
+ uint16_t _size; // size of tokens buffer
+ int16_t _token_len; // how many tokens have been parsed
+ jsmntok_t * _tokens; // pointer to token buffer
+ jsmn_parser _parser; // jmsn_parser structure
+ char * _json; // json buffer
+
+ // disallocate token buffer
+ void free(void);
+
+ // allocate token buffer of size _size
+ void allocate(void);
+
+ // access tokens by index
+ const JsonParserToken operator[](int32_t i) const;
+ // parse
+ void parse(char * json_in);
+ // post-process parsing: insert NULL chars to split strings, compute a more precise token type
+ void postProcess(size_t json_len);
+};
+
+#endif // __JSON_PARSER__
\ No newline at end of file
diff --git a/lib/jsmn-shadinger-1.0/src/JsonParser.hpp.gch b/lib/jsmn-shadinger-1.0/src/JsonParser.hpp.gch
new file mode 100644
index 0000000000000000000000000000000000000000..e3d9ed686152f6e8b238238039f13a6c1089fec5
GIT binary patch
literal 2299028
zcmc#+30zgh_n-Sf(D#goYp#iF?BKqkBA9!Hxn#Kj0xB+uf@@ZYOPWhqu4#&jrn#Zw
zR+{+KL?y*sGE2iPZ84wbR%-pf=geGryhpzu|3ZKF|Z0@x;BLHXZVylo0Q%5U&{qzg@|`mn}|{GAuqRA%5v0UJpZjCK-GmCVQn(RHqtg
zAwHQFpDcr4wq;^{D5qJx(vqR#qpO6r$%h(zXBfO68ldDG)aHOsR*3iF5N{L!C0!8W
z%^7&7SoTFDk<1XkoX4oeXF;-8HlD!ZTgCZ!$Zy8^WmF~v0Fym&cX-k-K
znTKgbk};n~InA^=%$V+B+8AhD8ED#KCDpu?pe6@>GtnhNys2>wewPitbAbA0`Jy}J
zgm`Bg{L(C*=mm3gLcGxXCP7a2L+?rn@j}b)2=Ru}u4JEVOK1k#5iKoy*F&3@4GQe+
zGsobUiq2*UK)2du@Cj=3jBlnT06qF-vJXN^9y(meEc+&Y8RC@@;;FY$0ER4{J1G7ezd0co
z@&TwaSwb`_l|RHABHAVm!iwWH-EycJY-nb{lxL%;E7+0jA7n;^!U
zr(33vHnaIvQ+luwGDg_86yuf@BYN^mYw$?bXT$PtZ;UXmLXQi^sL9!{d2v4qIEjj)
z4+=*;meGMVKkg-MA-^MgVeMhE7l26eF639b%j3R7efMqfs_y+YJ2TKqJ3qqN#
zOfqFqm~Dy`c7qDE#TtM8ZxhIC`))3Hbd0u--D!AIJqDusjGIyT*%j9%qi6b|Pr}B*
z$YvXSCs{nH`P&c3F>VYtZl%gIJ+7%ecm!cGSE`iPYhiF)nm%R*y9kq@fN#dN9(|2~d?(AL#RgxxEnsj}BX~-U>@-
z!h>wFik*9XrY3vOz`*0ST8!A-se>8}LkR<61Z}NT(q6K2e$EDM)a(e!A2PFt+2;oWO>u9VTUwK8`Q%ysg1e6Ld5i9khoJLS7~?Gv+??yCQmxo
z8iUq1C)xL;1tU2Cqa1Zdg9Kw%*$3aC8QOC&FQ`lO2;>EGw#yQL=E+F*#6x8NU9>q`
zRSYQ_zf_OXIN^
z9-Cl~&Cw~YYCzcZ=T+2|sFSR=(s-ot5n)=x2>KnS!PVBV-!)|CP*@BL41N#OPHR|+EjctwX`;o{
z6=uTRLz57OOt5KliphM^Ha6N7BR`C0N}A<7jOJ=}Z4!65jn2Ci8fOK7=#iCJiHP-0|`z1P8a3NFTJVB&i0
zC2_t%wVdb%a)QhCe_bcE6r!93AlDUt?_<#8LhAyH=wV_^Oy)Et7a}!KYw=Uo4LkS
zNQ{hnN<+H@&}r91nB;7jS62(|(!i*v`Nc%jn@wA|9P>Sy?3w3U8_=E6JJI*hL1C*g
zU%`z+C-la&mqM0mU$HMk{HBKZ>@q0V9VRRdRt(g*U_rlnP5z;$gn1$&PRr6~+OdrQX{gaOxY}#lw<;9G5
z_cfWlXok^U#5`}fi6*^9o{B92`}ygD8aOF*hQ%kt;G2%}T0%o}Li{mvKSaqfo$%BW
z)VY{a)R#2mBI-7LVvHWc2Q|q4A-hAU6B>B14--c!jbz`y(QulqG+@F*e5O)$+GgPd
z&z1{i@gzSMrZw4zDm59+=6TsNaity`^PlG~p19zGVlvP*oVMyEKvZ8en)C-B-`@B5
zHrp0hGpCb!nrK8@_jKPCUDpdU5rWgA){HR`r$NqUpCAxU1VTCG@Wa
zme8`tRlUo5J`31yf{6_ZES*msE_P6083mz=(Rf(~uOQE|3W8I6G6ZEPr{rD9o~tdM
z@IYl_E`YU#*^Dlq|F$L1u-dk5f?g`yj^#8-p|qIroJzKwMox-l;(T?(LIVNqM1v{M
zkAU-}x)v%v1I?x<1(zNVCtk;rL@b!B^N3;>pYB
zDu4GZPN!7@|EV0IR|`~v!-&qH4{$snO-n&x)l^<+HF9vt`S3wsR7F{=RnQ;OYYr0N
zWdJJPt|AIfdq`xkX{R9fp4xEqwM(&jPl%5P`aVnHQ
zo*5kj4*vq`AwEHY10TnL>B=FyZho;+UUgv^8RAbRYBDHK(fA3xR9Nv~qq$sVy`0KJ
z)}#8OJHh!2>NbLXD6;jKz)*RZt=x^MFf0yuGa@HjUkk~oJo5I1x;{3jRQu!;!qGOF
zJ66Sg^tFem-w?kHi&K=Yh}2<`JxpoVz;k+va@al0XQ3ue)MG>#x8@pGD3`(giXKTH
zn~&GtPAl~oR^#$ia``ZoVv(>pf_(p#*1+p_QFWUGD}ZI`K8M(tBH>~5tfn4?hdCA(
z=mfMJNyb_bZkIQfo+
zrg5d5-Snm~_o7*q>Q*n1oPtm+bj);2KOy+*jZq%ddAV#zEY;+C3l6%KMhBdmoz|q8
zcnXVWx&=EAZ05K=Ffg&?x@-wZW8)L#o2I8hTR55UUfk#E@iJ%;LY7&N6xvUJ1X*_{
zz4UItk4QTJ-Wc#a@H}C~ChF=Kcpl_zZ$qE62CvsMP@8KOUpyjBtRbG+=&Qc#^ddd>
z0(R;#ph&hFUI=-)k2f7c@8~(PXM~p7fkg?eNZ_#Xrqj#5s;J3wF@$X+Hul{%>zQH2
z12v`*@cVaiSLq)5^6uGuGt%g@@HEy?towHZRnp|yL^462n
zJ9)7;&}cDj%rtIew{a^*SgCis#ot=21#6HA7jK#%lEPv-Stv6-lNYRuifrBZ>9_%%{|b0>*Bx
z!T<3H&I<9v><;%WXnbz|s3<=?&dn(_IN%7w1;Y{vtF^6xa8#-7Cp)Q5DGPx{!CaV^
zUf9L_=}`g^v_FW7hMh7WZ=&}&SX%f`G}Xx&BPj5)9(5J`U!Ee&-J9yI4v)*5rX1R)
z@M4dS=H;NUAM{8Vn6PZL{tYwIh7-nPn9&>+@1_PV8_X^6L4!>gu^6|~zeaz7!9!1h
zW&9LzWHARP{g-Iz7zi+Vh=wVwz$9L2rWc+3viy@|Xob~!HJ-tIlK+{oA%(Fdw?xyD
z{mE|7-co3#b^jrbfhG#fF9Dm;d{gLt+?EKZ$v$-nte{w3EuIee}Qk$Ye<;-qd
z@z_XVi;=loFSKp5<*yBCZ+wEHt$JIk>0Y0%@;6!F0X<3dJgyyhfY>93M}p-@XdUz8
z2H%lH`}pV;FbArUur5RQ#tisvka|*mWWc#aLlm&|>3mx+76u0`Nihion{;cN>T*ZF
zt-KP5!NxuRy=rLxVMV&pJSajhF{~b{4Vm!R%U7NGJ1zA-Y@M*FTWd{mxu+N5@gUd&
zJBn^;HLb$-7{)GlYug>sHL0|~T!Z0h&CJy+l;#vNDSkn{f7g?QHxyV;
zYVTy9AkPN6mTVUDj2HD
zsT1exkzkT&6w-15t_4ljDaMa6f8;B6>KZ*g?o!z8=IM_bDgfgWHXdV}JeSUcyO6oW
zcJLac3V#W+xIVNptnQoglEJ|CPltEPeJaN?@l?SDvT5(6oqBBPsbc9qgLB1t2`OP=
z4ktD!XxQTtU+TEubf`je!?6WpK)r6fwG$8sKY6abf
zo+9skSIQ|pG3oH9Vcl+^O&hcb#+sgFXiYsyOgwrr<{5od145m5j22s*5_Ls)BbE-R
zK6pxK$#vG$Tsd&_V-lUaYBb#US)w={O93Nu5bH~`KCmM*oQ`*O02fsCf
zJS*;t>9_Ty^$GHv`EIA1nMDSM!0=E(76|a)}S5nQa&xWW$RLlyPPFN{fIBU{Jb_wu`4MPY0AAMfd
zKv2YLyE3I1w_(`I7n*WI1hZ4`L3R}eJ1GYZhJc!~)HJ(qt9{|tM!?;t+FJEEy=Mp8
zrH7smM++7Z8<47G(HnI$u4+Gmwhw5J7q1EUVY%wX-Y?VytE!%C3QyAQH#sDLQGn&!
zI;_lR57FlY)ddiVH(&gN25zy7hcN^{FNOS_)gC5zrtopl3(RGP7MMmV>c#?3XPaHk
zqW4%a98Bv{XiT#Gx8~}Fm#ZGy9ZGNx5
znLQfw*XZ=M}XtC$=Cu72EA#`-~gB6%*P8lZ<3eqs!h)hN=dT!B&lR4SaT(3Sa9k}IebngFE3Y(6?JUug_}sqkyD
z31%OUI(odx-2ILG>a_he2`>#XSZICe71ZdtAD1orT&mgi7<3fciOk#d(9?Tye7-WFfiT*FZ#k3$MgkRQ{x-k5!8bd^>!iU8
z?@nnJCBqME!83ovZ9NTDlz0>7%+=_#m^|E#@YmC^z>5gWUs5=Q7-_t?Lhr;>gEd7u
zW~n?^#>3^Vp0FI6s3d6QZB=8GAqn2XMpm>D8(}n`e>2}>Vuu)RngwQ8ZTOQLV|o13
zfqhf*mkPcclCjxC!rl#DfnxBOd%g9a6Hd3_w*uZL4ZcAG|Hxl%SuIFaZn}_JawoUK
z{8j!WlYMrfLs~rNU>ReXxL+?B>c!o-DU6;Ja|~}}xw|~lvsjMNjrJyQk&F%A;?l
zjc|=3FP`+p@A@c^{a@~xV|)hn{7X*^(<#(3qpEe{
z08g6TURgG~mtq()8N8i3!-BccgmSOpQ6b5_cyffOfsBwc-9>cix!Ijt*Catg|HYCb2laz<#A8T*=NJGI=eRoT(HO3sAJnG-bgm0Y)5
zDdt~==(x${Qj&RigzJGy3!GYJPYiM&oa1&q$$X$vc4(8-iA~)H2bzBibAz}qXwJkc
zWu=%9ziw|(c4$+iwtvDVm)}??A$XnEGIwv)Y>)B@R+n`tZWlew7d*_rTk*3zx4U6(
zzgXR@VQ#mqE-S2V*K^!1C{8`46nsd8?@K>9jW|lrO4u^Vdy-w38=(
z?f>jW8fO8mtXTK0Y9HSX^RETw+2%o0w5CLx#muvkV(H
zBx+z}tghE4rhnwfA&LHDq7wBx0V5Me1oVrI3-I?3h>eRHGBP?QAj-!lU{u|Jgv99R
znEw6)k!PD)F)j~>=ZvoY+*8ayJUnVxT>sbs$iG|rRpPtBM@CL~GWk;%HJZ|KWA@PYLgh)K|{r{x>W8)IJ{U;?}H+VKYKKg%9|6GJQ
z?%PCKwq>|h4*jolY`+naBgWeF^Uz^KhepOlcaDvVp)Q{g(6vpcgn$<#qh9XX(?214
zFn4mBHsv(a%Fg@dnb2nka-06!G{+_cgeQ)Tj{!sCO~O%a8MQe5K~Et4x{W9UjEntG
zE|2>4R30avTpo=Ys5~yamp|-V<=K8kQCE69jMN(f^>n)yrypI^>FW*QT=mJMuQx*3
zhEFDay#Z>S8!r~03rw6)6yxjhSnwa%pwC(-N;k5I(v8A|lz^V!=1PEJ4$|FA^KDU#
z-Gs42`wbfs9yug7G660P4X$o6Lj~ng{n;YQBYs#y?3nP#QIWAjBKyS-iA@~KV>r5M
z1+qgCMbS5hMn(;U5mdIwe_-P5^e{B6GjUFeAF*WJM@xuMEW%#lptDnSy
zBVr<>1H$8FKn`TCqd67lQymQ{iaJVo+&|e?V}6xJD6L#Y%A;>l)YPqzf^d}c(`c!^B
zg9a`Qy~WF)E$TDX^=YnUMkuo`+PG|QA-uN4_YTH7_TSDOi89%4F1srT&!S0FH+#`8
zCl`qgZo7NO|NC;bwwrVkU79`IrdfZ=IZ}+e>c6fB+w?nN89E{^atImjNiMr$FdmA%
zUIcmA+KzrvMAY%|U~{VC>+J)bdS!bf3q5xZML
z)N*YR@YLrDs{h!y{=?As8n|wMk@HV^igge(CMqVLcK5LF<~q;kf71R#V*7P-4e)yE
zb&sxxta;o=yN(%Fbn~=KHqmulPZ8U=WS_7iuu)h?jK(14dCqk++t;F=hvH+S*@Dk;
zJx%EsiO**{B}^n(MS_Vj$`zqG;+RA$9GIO#Xk3tn8^6pVeyGP
zIqY|JZAj(hY!Ved1A~M
z>X5fxt27ajsQ09QU+;3=pO?6^u}mW>dr_=$sO$lc9{{ud$S7{uvc^#zw0OUDB9jO4?IgkOrmsFqm7$+4P6vt!#0nR
z%Gf0uH;iXnR|M-SN`FL5!myDeqGEU+FisprZC%vsg~ZsQF{9*?O3X2KC~}(@6BivD
z8RtJR&8T?^8g~Q#eIMc>rzecQpJD9QRKyfb+T5--Y410tcyhUmpzp)~7>osMK={jD
zL&he?ygVYB-cMs6%$QP%^EmVG>qgH*nx*_1T{cEcrv066MPM%?hYT4O73n|lw$Z}=
zNl}m6sKoFQBjb1}E}Tsp*)|k)-`6uJYcf5Nu{9cR$ki6GsAan1+WCHV5&08T`2pcW
zxe@R##B_+;ps4Lybj&C=oNY`ypGf@=#SaW9gKnk^mHsDd5$qjCo8B7Ac}G0Oby!@?
z=tNWITN>>>Jk9m@$V7fQG|6;+$y2ZI@bJWuaIxo@N>%2(vz}so$Hv7b#^Md!xR?>)
zLt+yWVgJ%h=l>`BAGE8RVVctMe^CF?G5tmk*l#-0lKb7$-18a|I}}?^;-u-pZyBF`
zGx-ggf1cKFjl#o6#^K#AjAhh_VF}{0sdi<<(|$(~i+$_J#9{ckjks-c?!ayTH0wWh
zSVI5N(Zbp7O#7$a?|2gQA6VAy{L8%O(&XR!I4Vg|%T4Y*7q#y(v>(n1b!{h#x0}jQ*SfumBkLjyzI8;?V@Tkm86JvYucp9-e;*q
z6Wu=JbzM>Whod5Sw%|4JB)26Mcx~77-}hfyW9aoU$1UTD_-@1c_h;WN&FzkSCmHzf
z%S~%Vs*DV`OfGX#*LM`W+QD+EF7~@UnfH7243x!5H+S;+`6cDQ-3PMW3!wB9;$xzy
zo-Vr`dye~h5xlnwA00)zGu#(%yJc14bi@9An}*vY>}*b_aa82*R7b
z<-61(=sUE2);ndi**b&gshp?i*N2Dq9~l=#FSH_ug!hX~h{5lf&7+%gIj;RL%Ml(}
zAAN9=dEQd?wd0>+S$LI&GdkvMSr1S1{URnc`HB(iD|3xoPk$^%Cg8|NVupE~XHktC
znWF!|{pN%BpZdB9Ylfk*QRqoHYtq0Yw5_>S@m!poIX;qpF(*Xo7{%XE{B6Z2D*le*
z(-nVD@ed>qo~QUi$s-m^p0rf*)a8n=RD8AKYZc!hdBi5kJu)N@-Y$94r|SN0g=b2h
zx=-@VFO+;p@`!B7la49;E6FoYNuGN~a`Bzw-z(0iS5P58NICVIE?kFXXReZeSZ%dv!QSyj)BoCf0xyO47|3KmM6~0Jvu~f+`6#rP^
zYbDR!AbHXz$x|~F-=Xkbl6<Jaez)x%(wgJ*fC$$%DU?JR(Q&6Ot#LR`0e%-SFq#pwVY$vDEvoDOZ0Tte}Mihl_H3~08J7b(6NoZ@_>OzFP4$ihrW`TE*8XzFzSSif>drUGYtdZ&rMZ;u(r>ReYP`+ZErT
z_)f(?Rs1u>cPYMG@y`|Cqj;v`d%>%LEK1&|_rFD*lt=KP&zVIQ7@-ivOzk4aKdB-&FjT;=d_=Tk$)J-&Opc
z;`bH*UGYB@f1vn7#UCmDr{aGpPKS-De4640#hnzVBgu5XnBp#qyMk8*UR=o~lxzg2
z`ZFoKq>`UevKx3H?wb{NSG<(EUs~}pikAha@mo&u@`^vJcm;4OUq!_$DZH|ht0=ju
z;?F5wO>qx!vK!SEuc7do;Pia8z$yLOiq}#6dBr`!>3O{r_g1)%l6{rzr(}P{0~8Nb
zysqN)6tAy%1H~IE-bnGriZ@Zbsp8EPZ?1Td;w=CBNgwbc$DIF;EnWSl-ytO0pOI+Kyd2Mv5M1SMZyOw
zd5DsSf>S?PC_-e)1DE^7!YZYIo_$Lif>o<
zcYu@rPQ^b}{4>RODZU$=+VyiK?*XTBWGZ>D;ugjCDZU?^^bRQg1vr)GpyFBJ)c+1C
zepvA%if4mU`Mw0FbdD;144l%*QSxymf2HITN|Kg
z`8&nWDg3;WzgIk0@e7JyRQ!_SmleOF_z#L-Rs5RbKPvu{;y)|?i{jT6|5foDidz-G
zsrW6$e^dOn;&&9ktN1;|?<@Yh;(sXqK=Fr)KT`Zp#s5;AmVi`mn&Jkr#jBur
zMa3&APD@m}Uq$h%ia)1#HN`!^sT|dnTto4i;M6~BDNakUGp9=M2nze_+t=;lD`yZ2r-2pLF0SB
z$3QUvM>(4F;-L2#qLtC-3>C|W`H>-7B#i@P5H2NyFEUgd5c%dVY3^+qpaoMqj#j*Z
z;Uq)&?{PH%~>0FMH^1QE!}S$LsW6JHrIsv
zN&iO-(K?OBmL^I^ciPL48IT*NRVHxyiwwB~>cLQXkLO=ts1%^u3{}YRjAf`apgs&$
zzJKZ)hROii$xtj%kegT1?m-Mx20-h3O?dZy@d86t
z0Fj+_5#4r7R1$&ZnaPtb4AGw>XMG(_`Bz2A=k>TG8?bN{UGr*dO>0D6E(4?&MW
z{%0zbWZmkxVI|<1r)d|>@*4EO_B4?q(^og2Qv
zPynDt47FM}>lQ;~#ny9~f`4(V%uroGw1Cn?>#(L{7@{$}owY)&je9dxA5b(yp%u1g
zFw_9h8IIUC>D`44(HNhL9GyhFZpAk-)CkZCL~K{KBpM83w=sZT1h}`F%mB@dG!tl|
zL+Xh>3^fJh%23D7%{npE3=pl?HSyy81+w2Yhe*37ns_Pmr~RxIL>dgeQgXCxtQLSG
zxa3_kCP{Pa2z4o1ptj|_}=Cao7&Od*_X$1pn%(dPtX!j6?S_4`S>Xqf<%RmT(
zKrU#X+w#u
z47CFk#s%nCwvK${_JF>CRzKf%_M4lv%t4r~}fA&FS2jbzTNAhD$#*J!LmT
zVSv8lVhkHoJB^_(fNB65wz~dY23`R`9)y#K?-_TRp{{^7fa2S%s>MJz2>p?L{KNjT
zJlz4T2B3)%@t=IjIz0fr&eqvrHqeA2p>HzK7eW&}
ztR^OAwR)SOa6q)jr-?}wB8oE<0Vt7EnmlRiBZeXY^#C+^o#$-^`T@Aj(WZnQ-^x%F
zpal%Qb4_*qXh435rirOJnl#KYfckS0Qg#ez#u57i+Q|`T@9I;Ep#gxJbHusr(_I-F
z2#9t{G%&kk
z-UnZ2XedP5<1~n6wKB>w6bI-dK+B@Pk@Lea0JNW~iIwZhr?XBxAo5=|@$nF`mZ9N*
z$o1638mrSE42=NPk)d@SUFS2D0O%N}wcfLzH1mmo8nD)elufcTjsz6MQ2PCyUL0{0
zAo46Vv3cO;feeiXL{7UwYz=z75<_DEwFb1+dPx>xEPyvSzumQhrO6ows0=s7p8cT@
zIocb5CIWI2nOSXrU}!v`S%5N=tPL1=6Tn?i=Ka=22Ht`Yh@=f-Z-&QqhTaCW0+IIq
zvfwHMNdS(3G+}A7(ubi55PNWm_ARbrWhfcYZ-AV|{@YP->&glj!!$g_yC7Osz7L|+
z;r_JK-*JSAh;S46><^mwE(4PQWRk|N7hYmuGJuv`xGeX06GKw~g#pUS`RY#w-T}}B
zfF=%~>C%Fssem#-nm9H-L)P*%h_qDDL{4^JIWpe`6u=R`x;*_bN1P6*CQ77WQK*JDC6S+;M++b({pw|4b7i#U1onav$T9|0!qP3=+
zIX?u{o1shB_J(rAMS%8k#H(#u{=(2=Ky?_po_1gQq#psIMU^J3oom0$T1x<(g_c3w
ztkqc7!BRjU0=k**+m&@v0sP31{9BV!gBV%{XfBQ
z-Lz}+>Czz3n!zCMXFYt5byfn}1G-<~wzRsdAbi4=@@M*lj;up9X<U%5FGKlgEg`Bi@0HPIK`JG39Vx64;yg8*xV?)|7^eLd@AWc->sYNcAsNqIM*nVets>t{6h9a*WP(rOvzQL_nhB_WU(U=oB3#dJEHHc2d4o_$3TR@ed)oJhU6%2d_
zpdB}A=b0{(7&-^&Wq#%b?=4jm~)I7{7
zT>!KTxpg0|$%elO;5K>Q1LN^x
z*&OXQpvzp7vE}COVCW7YZ`K-ABIgx`$hBF-(2&`mbYSQnpo(0Cp*wHNIqg0mvg(?M
z8(B`Sz^OtqIAZ*?zD^wR4?yp6j>9{YmCyq~e=#(oRF`*I>mi^@{5T1Zwn*#w2+%9g
zN@y`_9qarFfL_sSB5~$~P7M78=m8*u82wDUk__RtScf8vK4i8re^y~vO=#3~HEHP-hCL&YF|$({>xCNa_A2pQ5%>*Eq
zlb(F4)32;k63`wlz?7kBaSS~J$RDJMX?<=kVaN@lnW1-IY$=B#i5K{hr|&)^Coy+G
zM?p?vW-(9M^GZQHhva9z)=fG*v^u33cjnHU6*=iL0H$)8X05Dumm%tz0U%AhH}=>~
zhRQ)K!Np98JucU}KO(0FIn6Rr4Lp31Mt~C}$Evm@g
zSrcMQPJGqmV|Un<*WLj(74#k`1vDR|iPgi~$ttM@F%(e@VqNW)(*39ns2reme&wXA
zUI#!3$RO6o%<<;TpNB}}Wqs_RqYQWgpgnI*Y#4cKKSN~pR`PRg8hTTX6>=#KA?Zz<
zUjBl0d;qipX(HoJvP_z`%3g+!llZLEX&2U_)E#Jx
z-yn8H#$RD55MonA+O_7ow3c-N&}#@yeBLlZ_S8$v9{t^Hi)fGUUidfSknMxuuWs
zY)0$DV@N3nbR4vI(_6CFHN=fX0#~0v>AhniQ;l)PQ#2w-{G+*YMO
z3R?g;3(`dP%*&yid`pPSK?ZSjS;)5xQH=+o+(+vUmGuw|U@#XUXML3H;k1zL03A&n
z*It#A8}$MD37J8h7&`^dE>?=pGl9(l%>~T^eF!?S>4Q?7Pbh?Ioa{-z2A?w22GG|G
zoqDGG3Wi<))E%UWGgs6KpH^$$O3J3#9Y(Mfze`-mK>?E$$$>sznk^65JO
z*o$iWcK1utjpztqH|O@9st3ratuvtZfX=m;vypXP1TcYh&d>C#
z&(KSN>H#`mYQQB1UIy?L08M<~@J(5sFhD0!9)q}0!YT(TS@^M_3m^R@ZSE@&hI7&v
z>(+h5Dbe |