diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md
index e542181b9..0de14bbf8 100644
--- a/.github/PULL_REQUEST_TEMPLATE.md
+++ b/.github/PULL_REQUEST_TEMPLATE.md
@@ -7,7 +7,7 @@
- [ ] Only relevant files were touched
- [ ] Only one feature/fix was added per PR and the code change compiles without warnings
- [ ] The code change is tested and works on Tasmota core ESP8266 V.2.7.4.9
- - [ ] The code change is tested and works on Tasmota core ESP32 V.1.0.5-rc4
+ - [ ] The code change is tested and works on Tasmota core ESP32 V.1.0.5-rc6
- [ ] I accept the [CLA](https://github.com/arendst/Tasmota/blob/development/CONTRIBUTING.md#contributor-license-agreement-cla).
_NOTE: The code change must pass CI tests. **Your PR cannot be merged unless tests pass**_
diff --git a/.github/workflows/CI_github_ESP32.yml b/.github/workflows/CI_github_ESP32.yml
index ecf8bf561..fbf8beab0 100644
--- a/.github/workflows/CI_github_ESP32.yml
+++ b/.github/workflows/CI_github_ESP32.yml
@@ -64,6 +64,26 @@ jobs:
name: firmware
path: ./build_output/firmware
+ tasmota32-core2:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v1
+ - name: Set up Python
+ uses: actions/setup-python@v1
+ - name: Install dependencies
+ run: |
+ python -m pip install --upgrade pip
+ pip install -U platformio
+ platformio upgrade --dev
+ platformio update
+ - name: Run PlatformIO
+ run: |
+ platformio run -e tasmota32-core2
+ - uses: actions/upload-artifact@v2
+ with:
+ name: firmware
+ path: ./build_output/firmware
+
tasmota32-minimal:
runs-on: ubuntu-latest
steps:
diff --git a/.github/workflows/Tasmota_build.yml b/.github/workflows/Tasmota_build.yml
index 9e8f61219..40a018b58 100644
--- a/.github/workflows/Tasmota_build.yml
+++ b/.github/workflows/Tasmota_build.yml
@@ -898,6 +898,29 @@ jobs:
path: ./build_output/firmware
+ tasmota32-core2:
+ needs: tasmota_pull
+ runs-on: ubuntu-latest
+ continue-on-error: true
+ steps:
+ - uses: actions/checkout@v1
+ - name: Set up Python
+ uses: actions/setup-python@v1
+ - name: Install dependencies
+ run: |
+ python -m pip install --upgrade pip
+ pip install -U platformio
+ platformio upgrade --dev
+ platformio update
+ - name: Run PlatformIO
+ run: |
+ platformio run -e tasmota32-core2
+ - uses: actions/upload-artifact@v2
+ with:
+ name: firmware
+ path: ./build_output/firmware
+
+
tasmota32-knx:
needs: tasmota_pull
runs-on: ubuntu-latest
@@ -1600,11 +1623,10 @@ jobs:
[ ! -f ./mv_firmware/tasmota32-display.* ] || mv ./mv_firmware/tasmota32-display.* ./firmware/tasmota32/
[ ! -f ./mv_firmware/tasmota32-web*.* ] || mv ./mv_firmware/tasmota32-web*.* ./firmware/tasmota32/
[ ! -f ./mv_firmware/tasmota32-odroidgo.* ] || mv ./mv_firmware/tasmota32-odroidgo.* ./firmware/tasmota32/
+ [ ! -f ./mv_firmware/tasmota32-core2.* ] || mv ./mv_firmware/tasmota32-core2.* ./firmware/tasmota32/
[ ! -f ./mv_firmware/tasmota32-knx.* ] || mv ./mv_firmware/tasmota32-knx.* ./firmware/tasmota32/
[ ! -f ./mv_firmware/tasmota32* ] || mv ./mv_firmware/tasmota32* ./firmware/tasmota32/languages/
[ ! -f ./mv_firmware/* ] || mv ./mv_firmware/* ./firmware/tasmota/languages/
- rm ./firmware/tasmota32/*.gz
- rm ./firmware/tasmota32/languages/*.gz
[ ! -f ./tools/Esptool/ESP32/*.* ] || mv ./tools/Esptool/ESP32/*.* ./firmware/tasmota32/ESP32_needed_files/
[ ! -f ./tools/Esptool/Odroid_go/*.* ] || mv ./tools/Esptool/Odroid_go/*.* ./firmware/tasmota32/Odroid_go_needed_files/
[ ! -f ./FIRMWARE.md ] || mv -f ./FIRMWARE.md ./README.md
diff --git a/.github/workflows/Tasmota_build_master.yml b/.github/workflows/Tasmota_build_master.yml
index 8c00d25e3..62dde9232 100644
--- a/.github/workflows/Tasmota_build_master.yml
+++ b/.github/workflows/Tasmota_build_master.yml
@@ -898,6 +898,29 @@ jobs:
path: ./build_output/firmware
+ tasmota32-core2:
+ needs: tasmota_pull
+ runs-on: ubuntu-latest
+ continue-on-error: true
+ steps:
+ - uses: actions/checkout@v1
+ - name: Set up Python
+ uses: actions/setup-python@v1
+ - name: Install dependencies
+ run: |
+ python -m pip install --upgrade pip
+ pip install -U platformio
+ platformio upgrade --dev
+ platformio update
+ - name: Run PlatformIO
+ run: |
+ platformio run -e tasmota32-core2
+ - uses: actions/upload-artifact@v2
+ with:
+ name: firmware
+ path: ./build_output/firmware
+
+
tasmota32-knx:
needs: tasmota_pull
runs-on: ubuntu-latest
@@ -1600,11 +1623,10 @@ jobs:
[ ! -f ./mv_firmware/tasmota32-display.* ] || mv ./mv_firmware/tasmota32-display.* ./firmware/tasmota32/
[ ! -f ./mv_firmware/tasmota32-web*.* ] || mv ./mv_firmware/tasmota32-web*.* ./firmware/tasmota32/
[ ! -f ./mv_firmware/tasmota32-odroidgo.* ] || mv ./mv_firmware/tasmota32-odroidgo.* ./firmware/tasmota32/
+ [ ! -f ./mv_firmware/tasmota32-core2.* ] || mv ./mv_firmware/tasmota32-core2.* ./firmware/tasmota32/
[ ! -f ./mv_firmware/tasmota32-knx.* ] || mv ./mv_firmware/tasmota32-knx.* ./firmware/tasmota32/
[ ! -f ./mv_firmware/tasmota32* ] || mv ./mv_firmware/tasmota32* ./firmware/tasmota32/languages/
[ ! -f ./mv_firmware/* ] || mv ./mv_firmware/* ./firmware/tasmota/languages/
- rm ./firmware/tasmota32/*.gz
- rm ./firmware/tasmota32/languages/*.gz
[ ! -f ./tools/Esptool/ESP32/*.* ] || mv ./tools/Esptool/ESP32/*.* ./firmware/tasmota32/ESP32_needed_files/
[ ! -f ./tools/Esptool/Odroid_go/*.* ] || mv ./tools/Esptool/Odroid_go/*.* ./firmware/tasmota32/Odroid_go_needed_files/
[ ! -f ./FIRMWARE.md ] || mv -f ./RELEASENOTES.md ./README.md
diff --git a/.github/workflows/stale-actions.yml b/.github/workflows/stale-actions.yml
index 4afcc8b01..75decdd3c 100644
--- a/.github/workflows/stale-actions.yml
+++ b/.github/workflows/stale-actions.yml
@@ -2,13 +2,13 @@ name: "Mark or close stale issues and PRs"
on:
schedule:
- - cron: "15 05 * * *"
+ - cron: "30 * * * *"
jobs:
stale:
runs-on: ubuntu-latest
steps:
- - uses: actions/stale@v3.0.14
+ - uses: actions/stale@v3.0.15
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
days-before-stale: 25
diff --git a/BUILDS.md b/BUILDS.md
index c099a443b..e882329d9 100644
--- a/BUILDS.md
+++ b/BUILDS.md
@@ -149,6 +149,7 @@
| USE_EZORGB | - | - | - | - | - | - | - |
| USE_EZORTD | - | - | - | - | - | - | - |
| USE_SEESAW_SOIL | - | - | - | - | - | - | - |
+| USE_TOF10120 | - | - | - | - | - | - | - |
| | | | | | | | |
| Feature or Sensor | minimal | lite | tasmota | knx | sensors | ir | display | Remarks
| USE_SPI | - | - | - | - | - | - | x |
@@ -193,6 +194,7 @@
| USE_RF_SENSOR | - | - | - | - | x | - | - | AlectoV2 only
| USE_HRE | - | - | - | - | x | - | - |
| USE_A4988_STEPPER | - | - | - | - | - | - | - |
+| USE_NEOPOOL | - | - | - | - | - | - | - |
| | | | | | | | |
| Feature or Sensor | minimal | lite | tasmota | knx | sensors | ir | display | Remarks
| USE_DISPLAY | - | - | - | - | - | - | x |
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 79e41be9e..9a591a2c6 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -3,7 +3,23 @@ All notable changes to this project will be documented in this file.
## [Unreleased] - Development
-## [9.2.0.3]
+## [9.2.0.4]
+### Added
+- Function ``AddLog`` to provide logging for up to 128 (LOGSZ) characters to save stack space
+- Commands ``ChannelRemap``, ``MultiPWM``, ``AlexaCTRange``, ``PowerOnFade``, ``PWMCT``, ``WhiteBlend`` and ``VirtualCT`` as synonyms for ``SetOption37, 68, 82, 91, 92, 105`` and ``106``
+- Commands ``ZbNameKey``, ``ZbDeviceTopic``, ``ZbNoPrefix``, ``ZbEndpointSuffix``, ``ZbNoAutoBind`` and ``ZbNameTopic`` as synonyms for ``SetOption83, 89, 100, 101, 110`` and ``112``
+- Commands ``ZbNoAutoBind``, ``ZbReceivedTopic`` and ``ZbOmitDevice`` as synonyms for ``SetOption116, 118`` and ``119``
+- Commands ``BuzzerActive`` and ``BuzzerPwm`` as synonyms for ``SetOption67`` and ``111``
+- Support for ESP32 ``Module 5`` Wireless Tag Eth01 (#9496)
+- Support trailing silence in buzzer tune (#10694)
+- Command ``L1MusicSync <0|Off>|<1|On>|<2|Toggle>, 1..10, 1..100>`` to control Sonoff L1 Music Sync mode sensitivity and speed (#10722)
+- Command ``Speed2`` to control a once off fade (#10741)
+- Zigbee command ``SetOption120 1`` or ``ZbEndpointTopic 1`` to add the endpoint as suffix in topic when using ``SetOption89 1``
+
+### Changed
+- Maximum chars in ``AddLog_P`` logging restored from 128 to 700 (MAX_LOGSZ) to solve broken error messages
+
+## [9.2.0.3] 20210122
### Added
- Support for time proportioned (``#define USE_TIMEPROP``) and optional PID (``#define USE_PID``) relay control (#10412)
- Support rotary encoder on Shelly Dimmer (#10407)
@@ -12,6 +28,14 @@ All notable changes to this project will be documented in this file.
- Support for up to 4 I2C SEESAW_SOIL Capacitance & Temperature sensors by Peter Franck (#10481)
- ESP8266 Support for 2MB and up linker files with 1MB and up LittleFS
- ESP32 support for TLS MQTT using BearSSL (same as ESP8266)
+- Support for 24/26/32/34 bit RFID Wiegand interface (D0/D1) by Sigurd Leuther (#3647)
+- Compile time option ``USE_MQTT_TLS_DROP_OLD_FINGERPRINT`` to drop old (less secure) TLS fingerprint
+- Command ``SetOption40 0..250`` to disable button functionality if activated for over 0.1 second re-introduced
+- Support for SM2135 current selection using GPIO ``SM2135 DAT`` index (#10634)
+- Support for ESP32 ``Module 7`` M5stack core2 16MB binary tasmota32-core2.bin (#10635)
+- Support for Sugar Valley NeoPool Controller by Norbert Richter (#10637)
+- Rule trigger string comparisons for EndsWith ``$>``, StartsWith ``$<`` and Contains ``$|`` (#10538)
+- Support for TOF10120 time of flight sensor by Cyril Pawelko (#10190)
### Breaking Changed
- ESP32 switch from default SPIFFS to default LittleFS file system loosing current (zigbee) files
@@ -20,10 +44,12 @@ All notable changes to this project will be documented in this file.
### Changed
- Force initial default state ``SetOption57 1`` to scan wifi network every 44 minutes for strongest signal (#10395)
- Command ``Sleep 0`` removes any sleep from wifi modem except when ESP32 BLE is active
+- PubSubClient MQTT_SOCKET_TIMEOUT from 15 to 4 seconds
+- Domoticz fixed 2 decimals resolution by user selectable ``TempRes``, ``HumRes`` and ``PressRes`` resolutions
## [9.2.0.2] 20210105
### Added
-- Basic support for ESP32 Odroid Go 16MB binary tasmota32-odroidgo.bin (#8630)
+- Support for ESP32 ``Module 3`` Odroid Go 16MB binary tasmota32-odroidgo.bin (#8630)
- Command ``CTRange`` to specify the visible CT range the bulb is capable of (#10311)
- Command ``VirtualCT`` to simulate or fine tune CT bulbs with 3,4,5 channels (#10311)
- Command ``SetOption118 1`` to move ZbReceived from JSON message and into the subtopic replacing "SENSOR" default (#10353)
@@ -45,7 +71,7 @@ All notable changes to this project will be documented in this file.
- Replaced RA8876 GPIO selection from ``SPI CS`` by ``RA8876 CS``
### Changed
-- Maximum chars in AddLog_P logging reduced from 700 to 128 (LOGSZ) to enhance stability
+- Maximum chars in ``AddLog_P`` logging reduced from 700 to 128 (LOGSZ) to enhance stability
- Disabled ``USE_LIGHT`` light support for ZBBridge saving 17.6kB (#10374)
## [9.2.0.1] 20201229
diff --git a/I2CDEVICES.md b/I2CDEVICES.md
index 301b48111..60ee9608b 100644
--- a/I2CDEVICES.md
+++ b/I2CDEVICES.md
@@ -90,3 +90,4 @@ Index | Define | Driver | Device | Address(es) | Description
55 | USE_EZORGB | xsns_78 | EZORGB | 0x61 - 0x70 | Color sensor
55 | USE_EZOPMP | xsns_78 | EZOPMP | 0x61 - 0x70 | Peristaltic Pump
56 | USE_SEESAW_SOIL | xsns_81 | SEESOIL | 0x36 - 0x39 | Adafruit seesaw soil moisture sensor
+ 57 | USE_TOF10120 | xsns_84 | TOF10120 | 0x52 | Time-of-flight (ToF) distance sensor
diff --git a/RELEASENOTES.md b/RELEASENOTES.md
index b3f8e38a0..58177ee55 100644
--- a/RELEASENOTES.md
+++ b/RELEASENOTES.md
@@ -56,14 +56,22 @@ The attached binaries can also be downloaded from http://ota.tasmota.com/tasmota
[Complete list](BUILDS.md) of available feature and sensors.
-## Changelog v9.2.0.3
+## Changelog v9.2.0.4
### Added
- Command ``CTRange`` to specify the visible CT range the bulb is capable of [#10311](https://github.com/arendst/Tasmota/issues/10311)
+- Command ``L1MusicSync <0|Off>|<1|On>|<2|Toggle>, 1..10, 1..100>`` to control Sonoff L1 Music Sync mode sensitivity and speed [#10722](https://github.com/arendst/Tasmota/issues/10722)
- Command ``RuleTimer0`` to access all RuleTimers at once [#10352](https://github.com/arendst/Tasmota/issues/10352)
+- Command ``Speed2`` to control a once off fade [#10741](https://github.com/arendst/Tasmota/issues/10741)
- Command ``VirtualCT`` to simulate or fine tune CT bulbs with 3,4,5 channels [#10311](https://github.com/arendst/Tasmota/issues/10311)
+- Command ``SetOption40 0..250`` to disable button functionality if activated for over 0.1 second re-introduced
- Command ``SetOption43 1..255`` to control Rotary step (#10407)
- Command ``SetOption118 1`` to move ZbReceived from JSON message and into the subtopic replacing "SENSOR" default [#10353](https://github.com/arendst/Tasmota/issues/10353)
- Command ``SetOption119 1`` to remove the device addr from json payload, can be used with zb_topic_fname where the addr is already known from the topic [#10355](https://github.com/arendst/Tasmota/issues/10355)
+- Zigbee command ``SetOption120 1`` or ``ZbEndpointTopic 1`` to add the zigbee endpoint as suffix in topic when using ``SetOption89 1``
+- Commands ``ChannelRemap``, ``MultiPWM``, ``AlexaCTRange``, ``PowerOnFade``, ``PWMCT``, ``WhiteBlend`` and ``VirtualCT`` as synonyms for ``SetOption37, 68, 82, 91, 92, 105`` and ``106``
+- Commands ``ZbNameKey``, ``ZbDeviceTopic``, ``ZbNoPrefix``, ``ZbEndpointSuffix``, ``ZbNoAutoBind`` and ``ZbNameTopic`` as synonyms for ``SetOption83, 89, 100, 101, 110`` and ``112``
+- Commands ``ZbNoAutoBind``, ``ZbReceivedTopic`` and ``ZbOmitDevice`` as synonyms for ``SetOption116, 118`` and ``119``
+- Commands ``BuzzerActive`` and ``BuzzerPwm`` as synonyms for ``SetOption67`` and ``111``
- Milliseconds to console output [#10152](https://github.com/arendst/Tasmota/issues/10152)
- Gpio ``Option_a1`` enabling PWM2 high impedance if powered off as used by Wyze bulbs [#10196](https://github.com/arendst/Tasmota/issues/10196)
- Rotary No Pullup GPIO selection ``Rotary A/B_n`` [#10407](https://github.com/arendst/Tasmota/issues/10407)
@@ -73,15 +81,24 @@ The attached binaries can also be downloaded from http://ota.tasmota.com/tasmota
- Support for FTC532 8-button touch controller by Peter Franck [#10222](https://github.com/arendst/Tasmota/issues/10222)
- Support for BS814A-2 8-button touch buttons by Peter Franck [#10447](https://github.com/arendst/Tasmota/issues/10447)
- Support for up to 4 I2C SEESAW_SOIL Capacitance & Temperature sensors by Peter Franck [#10481](https://github.com/arendst/Tasmota/issues/10481)
+- Support for TOF10120 time of flight sensor by Cyril Pawelko [#10190](https://github.com/arendst/Tasmota/issues/10190)
- Support for Afrikaans language translations by Christiaan Heerze
- Support for IR inverted leds using ``#define IR_SEND_INVERTED true`` [#10301](https://github.com/arendst/Tasmota/issues/10301)
- Support for disabling 38kHz IR modulation using ``#define IR_SEND_USE_MODULATION false`` [#10301](https://github.com/arendst/Tasmota/issues/10301)
- Support for SPI display driver for ST7789 TFT by Gerhard Mutz [#9037](https://github.com/arendst/Tasmota/issues/9037)
- Support for time proportioned (``#define USE_TIMEPROP``) and optional PID (``#define USE_PID``) relay control [#10412](https://github.com/arendst/Tasmota/issues/10412)
+- Support for 24/26/32/34 bit RFID Wiegand interface (D0/D1) by Sigurd Leuther [#3647](https://github.com/arendst/Tasmota/issues/3647)
+- Support for SM2135 current selection using GPIO ``SM2135 DAT`` index [#10634](https://github.com/arendst/Tasmota/issues/10634)
+- Support for Sugar Valley NeoPool Controller by Norbert Richter [#10637](https://github.com/arendst/Tasmota/issues/10637)
+- Support for ESP32 ``Module 3`` Odroid Go 16MB binary tasmota32-odroidgo.bin [#8630](https://github.com/arendst/Tasmota/issues/8630)
+- Support for ESP32 ``Module 5`` Wireless Tag Eth01 [#9496](https://github.com/arendst/Tasmota/issues/9496)
+- Support for ESP32 ``Module 7`` M5stack core2 16MB binary tasmota32-core2.bin [#10635](https://github.com/arendst/Tasmota/issues/10635)
- Support rotary encoder on Shelly Dimmer [#10407](https://github.com/arendst/Tasmota/issues/10407#issuecomment-756240920)
- Support character `#` to be replaced by `space`-character in command ``Publish`` topic [#10258](https://github.com/arendst/Tasmota/issues/10258)
-- Basic support for ESP32 Odroid Go 16MB binary tasmota32-odroidgo.bin [#8630](https://github.com/arendst/Tasmota/issues/8630)
+- Support trailing silence in buzzer tune [#10694](https://github.com/arendst/Tasmota/issues/10694)
+- Rule trigger string comparisons for EndsWith ``$>``, StartsWith ``$<`` and Contains ``$|`` [#10538](https://github.com/arendst/Tasmota/issues/10538)
- SPI display driver SSD1331 Color oled by Jeroen Vermeulen [#10376](https://github.com/arendst/Tasmota/issues/10376)
+- Compile time option ``USE_MQTT_TLS_DROP_OLD_FINGERPRINT`` to drop old (less secure) TLS fingerprint
### Breaking Changed
- ESP32 switch from default SPIFFS to default LittleFS file system loosing current (zigbee) files
@@ -100,6 +117,8 @@ The attached binaries can also be downloaded from http://ota.tasmota.com/tasmota
- Logging from heap to stack freeing 700 bytes RAM
- Disabled ``USE_LIGHT`` light support for ZBBridge saving 17.6kB [#10374](https://github.com/arendst/Tasmota/issues/10374)
- Force initial default state ``SetOption57 1`` to scan wifi network every 44 minutes for strongest signal [#10395](https://github.com/arendst/Tasmota/issues/10395)
+- PubSubClient MQTT_SOCKET_TIMEOUT from 15 to 4 seconds
+- Domoticz fixed 2 decimals resolution by user selectable ``TempRes``, ``HumRes`` and ``PressRes`` resolutions
### Fixed
- Redesign syslog and mqttlog using log buffer [#10164](https://github.com/arendst/Tasmota/issues/10164)
diff --git a/lib/default/Ext-printf/library.properties b/lib/default/Ext-printf/library.properties
new file mode 100644
index 000000000..bdb00e650
--- /dev/null
+++ b/lib/default/Ext-printf/library.properties
@@ -0,0 +1,7 @@
+name=Ext-printf
+version=1.0
+author=Stephan Hadinger
+maintainer=Stephan
+sentence=Extension of snprintf() and vsnprintf()
+paragraph=This library provides extended types support for snprintf (float, uint64_t)
+architectures=esp8266, esp32
diff --git a/tasmota/support_static_buffer.ino b/lib/default/Ext-printf/src/SBuffer.hpp
similarity index 93%
rename from tasmota/support_static_buffer.ino
rename to lib/default/Ext-printf/src/SBuffer.hpp
index 2c3b8887c..57b662554 100644
--- a/tasmota/support_static_buffer.ino
+++ b/lib/default/Ext-printf/src/SBuffer.hpp
@@ -237,6 +237,21 @@ public:
return buf2;
}
+ // nullptr accepted
+ static bool equalsSBuffer(const class SBuffer * buf1, const class SBuffer * buf2) {
+ if (buf1 == buf2) { return true; }
+ if (!buf1 && (buf2->len() == 0)) { return true; }
+ if (!buf2 && (buf1->len() == 0)) { return true; }
+ if (!buf1 || !buf2) { return false; } // at least one buf is not empty
+ // we know that both buf1 and buf2 are non-null
+ if (buf1->len() != buf2->len()) { return false; }
+ size_t len = buf1->len();
+ for (uint32_t i=0; iget8(i) != buf2->get8(i)) { return false; }
+ }
+ return true;
+ }
+
protected:
static uint8_t asc2byte(char chr) {
@@ -269,18 +284,3 @@ public:
_buf = nullptr;
}
} PreAllocatedSBuffer;
-
-// nullptr accepted
-bool equalsSBuffer(const class SBuffer * buf1, const class SBuffer * buf2) {
- if (buf1 == buf2) { return true; }
- if (!buf1 && (buf2->len() == 0)) { return true; }
- if (!buf2 && (buf1->len() == 0)) { return true; }
- if (!buf1 || !buf2) { return false; } // at least one buf is not empty
- // we know that both buf1 and buf2 are non-null
- if (buf1->len() != buf2->len()) { return false; }
- size_t len = buf1->len();
- for (uint32_t i=0; iget8(i) != buf2->get8(i)) { return false; }
- }
- return true;
-}
\ No newline at end of file
diff --git a/lib/default/Ext-printf/src/ext_printf.cpp b/lib/default/Ext-printf/src/ext_printf.cpp
new file mode 100644
index 000000000..91abf1256
--- /dev/null
+++ b/lib/default/Ext-printf/src/ext_printf.cpp
@@ -0,0 +1,364 @@
+/*
+ ext_printf.ino - Extended printf for Arduino objects
+
+ Copyright (C) 2021 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 "ext_printf.h"
+#include
+#include
+#include
+
+/*********************************************************************************************\
+ * va_list extended support
+ *
+ * va_list allows to get the next argument but not to get the address of this argument in the stack.
+ *
+ * We add `va_cur_ptr(va, TYPE)` to get a pointer to the current argument.
+ * This will allow to modify it in place and call back printf with altered arguments
+\*********************************************************************************************/
+
+// This code is heavily inspired by the gcc implementation of va_list
+// https://github.com/gcc-mirror/gcc/blob/master/gcc/config/xtensa/xtensa.c
+
+// Here is the va_list structure:
+// struct va_list {
+// void * __va_stk; // offset 0 - pointer to arguments on the stack
+// void * __va_reg; // offset 4 - pointer to arguments from registers
+// uint32_t __va_ndx; // offset 8 - index in bytes of the argument (overshoot by sizeof(T))
+// }
+//
+// When `va_start()` is called, the first 6 arguments are passed through registers r2-r7 and
+// are saved on the stack like local variables
+
+// The algorightm used by `va_arg()` is the following:
+// /* Implement `va_arg'. */
+// /* First align __va_ndx if necessary for this arg:
+// orig_ndx = (AP).__va_ndx;
+// if (__alignof__ (TYPE) > 4 )
+// orig_ndx = ((orig_ndx + __alignof__ (TYPE) - 1)
+// & -__alignof__ (TYPE)); */
+// /* Increment __va_ndx to point past the argument:
+// (AP).__va_ndx = orig_ndx + __va_size (TYPE); */
+// /* Check if the argument is in registers:
+// if ((AP).__va_ndx <= __MAX_ARGS_IN_REGISTERS * 4
+// && !must_pass_in_stack (type))
+// __array = (AP).__va_reg; */
+// /* ...otherwise, the argument is on the stack (never split between
+// registers and the stack -- change __va_ndx if necessary):
+// else
+// {
+// if (orig_ndx <= __MAX_ARGS_IN_REGISTERS * 4)
+// (AP).__va_ndx = 32 + __va_size (TYPE);
+// __array = (AP).__va_stk;
+// } */
+// /* Given the base array pointer (__array) and index to the subsequent
+// argument (__va_ndx), find the address:
+// __array + (AP).__va_ndx - (BYTES_BIG_ENDIAN && sizeof (TYPE) < 4
+// ? sizeof (TYPE)
+// : __va_size (TYPE))
+// The results are endian-dependent because values smaller than one word
+// are aligned differently. */
+
+// So we can simply get the argument address
+#define MAX_ARGS_IN_REGISTERS 6 // ESP8266 passes 6 arguments by register, then on stack
+
+// #define va_cur_ptr(va,T) ( (T*) __va_cur_ptr(va,sizeof(T)) ) // we only support 4 bytes aligned arguments, so we don't need this one
+
+// void * __va_cur_ptr(va_list &va, size_t size) {
+// size = (size + 3) & 0xFFFFFFFC; // round to upper 4 bytes boundary
+
+// uintptr_t * va_stk = (uintptr_t*) &va;
+// uintptr_t * va_reg = 1 + (uintptr_t*) &va;
+// uintptr_t * va_ndx = 2 + (uintptr_t*) &va;
+// uintptr_t arr;
+
+// if (*va_ndx <= MAX_ARGS_IN_REGISTERS * 4) {
+// arr = *va_reg;
+// } else {
+// arr = *va_stk;
+// }
+// return (void*) (arr + *va_ndx - size);
+// }
+
+// reduced version when arguments are always 4 bytes
+#define va_cur_ptr4(va,T) ( (T*) __va_cur_ptr4(va) )
+void * __va_cur_ptr4(va_list &va) {
+ uintptr_t * va_stk = (uintptr_t*) &va;
+ uintptr_t * va_reg = 1 + (uintptr_t*) &va;
+ uintptr_t * va_ndx = 2 + (uintptr_t*) &va;
+ uintptr_t arr;
+
+ if (*va_ndx <= MAX_ARGS_IN_REGISTERS * 4) {
+ arr = *va_reg;
+ } else {
+ arr = *va_stk;
+ }
+ return (void*) (arr + *va_ndx - 4);
+}
+
+// Example of logs with 8 arguments (+1 static argument)
+// We see that the first 5 are from low in the stack (local variables)
+// while the last 8 are upper in the stack pushed by caller
+//
+// Note 64 bits arguments cannot be split between registers and stack
+//
+// >>> Reading a_ptr=0x3FFFFD44 *a_ptr=1
+// >>> Reading a_ptr=0x3FFFFD48 *a_ptr=2
+// >>> Reading a_ptr=0x3FFFFD4C *a_ptr=3
+// >>> Reading a_ptr=0x3FFFFD50 *a_ptr=4
+// >>> Reading a_ptr=0x3FFFFD54 *a_ptr=5
+// >>> Reading a_ptr=0x3FFFFD70 *a_ptr=6
+// >>> Reading a_ptr=0x3FFFFD74 *a_ptr=7
+// >>> Reading a_ptr=0x3FFFFD78 *a_ptr=8
+
+/*********************************************************************************************\
+ * Genral function to convert u64 to hex
+\*********************************************************************************************/
+// Simple function to print a 64 bits unsigned int
+char * U64toHex(uint64_t value, char *str) {
+ // str must be at least 17 bytes long
+ str[16] = 0; // end of string
+ for (uint32_t i=0; i<16; i++) { // 16 digits
+ uint32_t n = value & 0x0F;
+ str[15 - i] = (n < 10) ? (char)n+'0' : (char)n-10+'A';
+ value = value >> 4;
+ }
+ return str;
+}
+
+// see https://stackoverflow.com/questions/6357031/how-do-you-convert-a-byte-array-to-a-hexadecimal-string-in-c
+// char* ToHex_P(unsigned char * in, size_t insz, char * out, size_t outsz, char inbetween = '\0'); in tasmota_globals.h
+char* ToHex_P(const unsigned char * in, size_t insz, char * out, size_t outsz, char inbetween = '\0') {
+ // ToHex_P(in, insz, out, outz) -> "12345667"
+ // ToHex_P(in, insz, out, outz, ' ') -> "12 34 56 67"
+ // ToHex_P(in, insz, out, outz, ':') -> "12:34:56:67"
+ static const char * hex PROGMEM = "0123456789ABCDEF";
+ int between = (inbetween) ? 3 : 2;
+ const unsigned char * pin = in;
+ char * pout = out;
+ for (; pin < in+insz; pout += between, pin++) {
+ pout[0] = pgm_read_byte(&hex[(pgm_read_byte(pin)>>4) & 0xF]);
+ pout[1] = pgm_read_byte(&hex[ pgm_read_byte(pin) & 0xF]);
+ if (inbetween) { pout[2] = inbetween; }
+ if (pout + 3 - out > outsz) { break; } // Better to truncate output string than overflow buffer
+ }
+ pout[(inbetween && insz) ? -1 : 0] = 0; // Discard last inbetween if any input
+ return out;
+}
+
+/*********************************************************************************************\
+ * snprintf extended
+ *
+\*********************************************************************************************/
+
+// get a fresh malloc allocated string based on the current pointer (can be in PROGMEM)
+// It is the caller's responsibility to free the memory
+char * copyStr(const char * str) {
+ if (str == nullptr) { return nullptr; }
+ char * cpy = (char*) malloc(strlen_P(str) + 1);
+ strcpy_P(cpy, str);
+ return cpy;
+}
+
+int32_t ext_vsnprintf_P(char * buf, size_t buf_len, const char * fmt_P, va_list va) {
+ va_list va_cpy;
+ va_copy(va_cpy, va);
+
+#if defined(ESP8266) || defined(ESP32) // this works only for xtensa, other platforms needs va_list to be adapted
+ // iterate on fmt to extract arguments and patch them in place
+ char * fmt_cpy = copyStr(fmt_P);
+ if (fmt_cpy == nullptr) { return 0; }
+ char * fmt = fmt_cpy;
+
+ const uint32_t ALLOC_SIZE = 12;
+ static char * allocs[ALLOC_SIZE] = {}; // initialized to zeroes
+ uint32_t alloc_idx = 0;
+ static char hex[20]; // buffer used for 64 bits, favor RAM instead of stack to remove pressure
+
+ for (; *fmt != 0; ++fmt) {
+ int32_t decimals = -2; // default to 2 decimals and remove trailing zeros
+ int32_t * decimals_ptr = nullptr;
+ if (alloc_idx >= ALLOC_SIZE) { break; } // buffer is full, don't continue parsing
+ if (*fmt == '%') {
+ fmt++;
+ char * fmt_start = fmt;
+ if (*fmt == '\0') { break; } // end of string
+ if (*fmt == '%') { continue; } // actual '%' char
+ if (*fmt == '*') {
+ decimals = va_arg(va, int32_t); // skip width argument as int
+ decimals_ptr = va_cur_ptr4(va, int32_t); // pointer to value on stack
+ const char ** cur_val_ptr = va_cur_ptr4(va, const char*); // pointer to value on stack
+ fmt++;
+ // Serial.printf("> decimals=%d, decimals_ptr=0x%08X\n", decimals, decimals_ptr);
+ }
+ if (*fmt < 'A') {
+ decimals = strtol(fmt, nullptr, 10);
+ }
+ while (*fmt < 'A') { // brutal way to munch anything that is not a letter or '-' (or anything else)
+ // while ((*fmt >= '0' && *fmt <= '9') || (*fmt == '.') || (*fmt == '*') || (*fmt == '-' || (*fmt == ' ' || (*fmt == '+') || (*fmt == '#')))) {
+ fmt++;
+ }
+
+ if (*fmt == '_') { // extension
+ if (decimals_ptr) {
+ // Serial.printf(">2 decimals=%d, decimals_ptr=0x%08X\n", decimals, decimals_ptr);
+ *decimals_ptr = 0; // if '*' was used, make sure we replace the value with zero for snprintf()
+ *(fmt_start++) = '-'; // in this case replace with `%-*s`
+ *(fmt_start++) = '*';
+ }
+ for (; fmt_start <= fmt; fmt_start++) {
+ *fmt_start = '0';
+ }
+ // *fmt = '0';
+ fmt++;
+ uint32_t cur_val = va_arg(va, uint32_t); // current value
+ const char ** cur_val_ptr = va_cur_ptr4(va, const char*); // pointer to value on stack
+ char * new_val_str = (char*) "";
+ switch (*fmt) {
+ case 'H': // Hex, decimals indicates the length, default 2
+ {
+ if (decimals < 0) { decimals = 0; }
+ if (decimals > 0) {
+ char * hex_char = (char*) malloc(decimals*2 + 2);
+ ToHex_P((const uint8_t *)cur_val, decimals, hex_char, decimals*2 + 2);
+ new_val_str = hex_char;
+ allocs[alloc_idx++] = new_val_str;
+ // Serial.printf("> hex=%s\n", hex_char);
+ }
+ }
+ break;
+ case 'B': // Pointer to SBuffer
+ {
+ const SBuffer & buf = *(const SBuffer*)cur_val;
+ size_t buf_len = (&buf != nullptr) ? buf.len() : 0;
+ if (buf_len) {
+ char * hex_char = (char*) malloc(buf_len*2 + 2);
+ ToHex_P(buf.getBuffer(), buf_len, hex_char, buf_len*2 + 2);
+ new_val_str = hex_char;
+ allocs[alloc_idx++] = new_val_str;
+ }
+ }
+ break;
+ // case 'D':
+ // decimals = *(int32_t*)cur_val_ptr;
+ // break;
+
+ // `%_I` ouputs an IPv4 32 bits address passed as u32 into a decimal dotted format
+ case 'I': // Input is `uint32_t` 32 bits IP address, output is decimal dotted address
+ {
+ char * ip_str = (char*) malloc(16);
+ snprintf_P(ip_str, 16, PSTR("%u.%u.%u.%u"), cur_val & 0xFF, (cur_val >> 8) & 0xFF, (cur_val >> 16) & 0xFF, (cur_val >> 24) & 0xFF);
+ new_val_str = ip_str;
+ allocs[alloc_idx++] = new_val_str;
+ }
+ break;
+
+ // `%_f` or `%*_f` outputs a float with optionan number of decimals passed as first argument if `*` is present
+ // positive number of decimals means an exact number of decimals, can be `0` terminate
+ // negative number of decimals will suppress
+ // Ex:
+ // char c[128];
+ // float f = 3.141f;
+ // ext_vsnprintf_P(c; szeof(c), "%_f %*_f %*_f", &f, 4, 1f, -4, %f);
+ // --> c will be "3.14 3.1410 3.141"
+ // Note: float MUST be passed by address, because C alsays promoted float to double when in vararg
+ case 'f': // input is `float`, printed to float with 2 decimals
+ {
+ bool truncate = false;
+ if (decimals < 0) {
+ decimals = -decimals;
+ truncate = true;
+ }
+ float number = *(float*)cur_val;
+ if (isnan(number) || isinf(number)) {
+ new_val_str = (char*) "null";
+ } else {
+ dtostrf(*(float*)cur_val, (decimals + 2), decimals, hex);
+
+ if (truncate) {
+ uint32_t last = strlen(hex) - 1;
+ // remove trailing zeros
+ while (hex[last] == '0') {
+ hex[last--] = 0; // remove last char
+ }
+ // remove trailing dot
+ if (hex[last] == '.') {
+ hex[last] = 0;
+ }
+ }
+ new_val_str = copyStr(hex);
+ allocs[alloc_idx++] = new_val_str;
+ }
+ }
+ break;
+ // '%_X' outputs a 64 bits unsigned int to uppercase HEX with 16 digits
+ case 'X': // input is `uint64_t*`, printed as 16 hex digits (no prefix 0x)
+ {
+ U64toHex(*(uint64_t*)cur_val, hex);
+ new_val_str = copyStr(hex);
+ allocs[alloc_idx++] = new_val_str;
+ }
+ break;
+ // Trying to do String allocation alternatives, but not as interesting as I thought in the beginning
+ // case 's':
+ // {
+ // new_val_str = copyStr(((String*)cur_val)->c_str());
+ // allocs[alloc_idx++] = new_val_str;
+ // }
+ // break;
+ // case 'S':
+ // {
+ // funcString_t * func_str = (funcString_t*) cur_val;
+ // new_val_str = copyStr((*func_str)().c_str());
+ // allocs[alloc_idx++] = new_val_str;
+ // }
+ // break;
+ }
+ *cur_val_ptr = new_val_str;
+ *fmt = 's'; // replace `%_X` with `%0s` to display a string instead
+
+ } else {
+ va_arg(va, int32_t); // munch one 32 bits argument and leave it unchanged
+ // we take the hypothesis here that passing 64 bits arguments is always unsupported in ESP8266
+ }
+ }
+ }
+#else // defined(ESP8266) || defined(ESP32)
+ #error "ext_printf is not suppoerted on this platform"
+#endif // defined(ESP8266) || defined(ESP32)
+ // Serial.printf("> format_final=%s\n", fmt_cpy); Serial.flush();
+ int32_t ret = vsnprintf_P(buf, buf_len, fmt_cpy, va_cpy);
+
+ va_end(va_cpy);
+
+ // disallocated all temporary strings
+ for (uint32_t i = 0; i < alloc_idx; i++) {
+ free(allocs[i]); // it is ok to call free() on nullptr so we don't test for nullptr first
+ allocs[i] = nullptr;
+ }
+ free(fmt_cpy); // free the local copy of the format string
+ return ret;
+}
+
+int32_t ext_snprintf_P(char * buf, size_t buf_len, const char * fmt, ...) {
+ va_list va;
+ va_start(va, fmt);
+
+ int32_t ret = ext_vsnprintf_P(buf, buf_len, fmt, va);
+ va_end(va);
+ return ret;
+}
diff --git a/lib/default/Ext-printf/src/ext_printf.h b/lib/default/Ext-printf/src/ext_printf.h
new file mode 100644
index 000000000..82313cfc7
--- /dev/null
+++ b/lib/default/Ext-printf/src/ext_printf.h
@@ -0,0 +1,34 @@
+/*
+ ext_printf.ino - Extended printf for Arduino objects
+
+ Copyright (C) 2021 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 EXT_PRINTF_H
+#define EXT_PRINTF_H
+
+#include
+#include
+#include
+
+int32_t ext_vsnprintf_P(char * buf, size_t buf_len, const char * fmt_P, va_list va);
+int32_t ext_snprintf_P(char * buf, size_t buf_len, const char * fmt, ...);
+
+char* ToHex_P(const unsigned char * in, size_t insz, char * out, size_t outsz, char inbetween);
+
+// void test_ext_snprintf_P(void);
+
+#endif // EXT_PRINTF_H
\ No newline at end of file
diff --git a/lib/default/Ext-printf/test/test_ext_printf.cpp b/lib/default/Ext-printf/test/test_ext_printf.cpp
new file mode 100644
index 000000000..f8b7b2ff9
--- /dev/null
+++ b/lib/default/Ext-printf/test/test_ext_printf.cpp
@@ -0,0 +1,110 @@
+/*
+ ext_printf.ino - Extended printf for Arduino objects
+
+ Copyright (C) 2021 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 "ext_printf.h"
+#include
+
+// DEBUG only
+
+// String test_string(void) {
+// String s("This is the string");
+
+// return s;
+// }
+
+// String f_str(void) { return String("foobar"); }
+// String k_str("foobar");
+
+// void f1(String s) {
+// Serial.printf("> %s\n", s.c_str());
+// }
+// void f2(void) {
+// f1(f_str());
+// }
+
+// void test_snprintf1(void) {
+// char c[100];
+// snprintf_P(c, sizeof(c), PSTR("s1=%s, s2=%s"), k_str.c_str(), f_str().c_str());
+// }
+// void test_snprintf2(void) {
+// char c[100];
+// ext_snprintf_P(c, sizeof(c), PSTR("s1=%_s, s2=%_S"), &k_str, &f_str, &ResponseAppendTHD);
+// }
+void test_ext_snprintf_P(void) {
+// test_snprintf1();
+// test_snprintf2();
+// if (0) {
+ // // testVarArg2("", 1, 2, 3, 4, 5, 6, 7, 8);
+
+ char c[128];
+ float fpi=-3333.1415926535f;
+ float f3 = 3333;
+ float f31 = 3333.1;
+ ext_snprintf_P(c, sizeof(c), "Int1 = %d, ip=%_I", 1, 0x10203040);
+ Serial.printf("--> out=%s\n", c);
+
+ ext_snprintf_P(c, sizeof(c), "Float default=%_f %_f", &f3, &fpi);
+ Serial.printf("--> out=%s\n", c);
+
+ ext_snprintf_P(c, sizeof(c), "Float default=%1_f, int(3)=%4_f, int(3)=%-4_f, int(3)=%-4_f, 6dec=%-8_f", &fpi, &f3, &f3, &f31, &fpi);
+ Serial.printf("--> out=%s\n", c);
+ ext_snprintf_P(c, sizeof(c), "Float default=%*_f, int(3)=%*_f, int(3)=%*_f, int(3)=%*_f, 6dec=%*_f", 1, &fpi, 4, &f3, -4, &f3, -4, &f31, -8, &fpi);
+ Serial.printf("--> out=%s\n", c);
+ uint64_t u641 = 0x1122334455667788LL;
+ uint64_t u642 = 0x0123456789ABCDEFLL;
+ uint64_t u643 = 0xFEDCBA9876543210LL;
+ ext_snprintf_P(c, sizeof(c), "Int64 0x%_X 0x%_X 0x%_X", &u641, &u642, &u643);
+ Serial.printf("--> out=%s\n", c);
+
+ // ext_snprintf_P(c, sizeof(c), "Float default=%*_f, int(3)=%*_f, int(3)=%*_f, int(3)=%*_f, 6dec=%*_f", &fpi, &f3, &f3, &f31, &fpi);
+
+ // String string("Foobar");
+ // ext_snprintf_P(c, sizeof(c), "String 0x%08X %_s", &string, &string);
+ // Serial.printf("--> out=%s\n", c);
+
+ // ext_snprintf_P(c, sizeof(c), "StringFunc 0x%08X %_S", &test_string, &test_string);
+ // Serial.printf("--> out=%s\n", c);
+
+ // uint64_t u64 = 0x123456789ABCDEFLL;
+ // testVarArg2("", u64, 2, 3, 4, 5, 6, 7, 8);
+
+ // // Serial.printf("+++ ld=%ld, lld=%lld\n", 1,2,3,4);
+ // // testVarArg("", 1, 2, 3, 4, 5, 6, 7, 8);
+ // }
+ // tprintf("%s", 12, "14");
+}
+
+
+// void tprintf(const char* format) // base function
+// {
+// Serial.printf("%s\n", format);
+// }
+
+// template
+// void tprintf(const char* format, T value, Targs... Fargs) // recursive variadic function
+// {
+// for ( ; *format != '\0'; format++ ) {
+// if ( *format == '%' ) {
+// Serial.printf("%d", (uint32_t) value);
+// tprintf(format+1, Fargs...); // recursive call
+// return;
+// }
+// Serial.printf("%s", format);
+// }
+// }
diff --git a/lib/default/PubSubClient-EspEasy-2.7.12/library.json b/lib/default/PubSubClient-EspEasy-2.7.12/library.json
index 8a36a1c5e..e675ecff3 100644
--- a/lib/default/PubSubClient-EspEasy-2.7.12/library.json
+++ b/lib/default/PubSubClient-EspEasy-2.7.12/library.json
@@ -10,8 +10,5 @@
"exclude": "tests",
"examples": "examples/*/*.ino",
"frameworks": "arduino",
- "platforms": [
- "atmelavr",
- "espressif"
- ]
+ "platforms": ["espressif8266", "espressif32"]
}
diff --git a/lib/default/PubSubClient-EspEasy-2.7.12/src/PubSubClient.h b/lib/default/PubSubClient-EspEasy-2.7.12/src/PubSubClient.h
index 612029665..19b35160a 100644
--- a/lib/default/PubSubClient-EspEasy-2.7.12/src/PubSubClient.h
+++ b/lib/default/PubSubClient-EspEasy-2.7.12/src/PubSubClient.h
@@ -25,19 +25,20 @@
#ifndef MQTT_MAX_PACKET_SIZE
//#define MQTT_MAX_PACKET_SIZE 128
//#define MQTT_MAX_PACKET_SIZE 1000 // Tasmota v5.11.1c
-#define MQTT_MAX_PACKET_SIZE 1200 // Tasmota v8.1.0.8
+#define MQTT_MAX_PACKET_SIZE 1200 // Tasmota v8.1.0.8
#endif
// MQTT_KEEPALIVE : keepAlive interval in Seconds
// Keepalive timeout for default MQTT Broker is 10s
#ifndef MQTT_KEEPALIVE
//#define MQTT_KEEPALIVE 10
-#define MQTT_KEEPALIVE 30 // Tasmota v6.5.0.14 enabling AWS-iot
+#define MQTT_KEEPALIVE 30 // Tasmota v6.5.0.14 enabling AWS-iot
#endif
// MQTT_SOCKET_TIMEOUT: socket timeout interval in Seconds
#ifndef MQTT_SOCKET_TIMEOUT
-#define MQTT_SOCKET_TIMEOUT 15
+//#define MQTT_SOCKET_TIMEOUT 15
+#define MQTT_SOCKET_TIMEOUT 4 // Tasmota 20210120
#endif
// MQTT_MAX_TRANSFER_SIZE : limit how much data is passed to the network client
diff --git a/lib/default/jsmn-shadinger-1.0/src/JsonGenerator.h b/lib/default/jsmn-shadinger-1.0/src/JsonGenerator.h
index 2f27b846b..1d6c0f969 100644
--- a/lib/default/jsmn-shadinger-1.0/src/JsonGenerator.h
+++ b/lib/default/jsmn-shadinger-1.0/src/JsonGenerator.h
@@ -32,7 +32,7 @@ extern String EscapeJSONString(const char *str);
class JsonGeneratorArray {
public:
- JsonGeneratorArray(): val("[]") {} // start with empty array
+ JsonGeneratorArray(): val(F("[]")) {} // start with empty array
void add(uint32_t uval32);
void add(int32_t uval32);
@@ -53,7 +53,7 @@ protected:
class JsonGeneratorObject {
public:
- JsonGeneratorObject(): val("{}") {} // start with empty object
+ JsonGeneratorObject(): val(F("{}")) {} // start with empty object
void add(const char* key, uint32_t uval32);
void add(const char* key, int32_t uval32);
diff --git a/lib/lib_basic/OneWire-Stickbreaker-20190506-1.1/library.properties b/lib/lib_basic/OneWire-Stickbreaker-20190506-1.1/library.properties
index 2a8b08e53..89313ddaa 100644
--- a/lib/lib_basic/OneWire-Stickbreaker-20190506-1.1/library.properties
+++ b/lib/lib_basic/OneWire-Stickbreaker-20190506-1.1/library.properties
@@ -6,5 +6,5 @@ sentence=Access 1-wire temperature sensors, memory and other chips.
paragraph= Mod of Paul Stoffregen code to support ESP32
category=Communication
url=http://www.pjrc.com/teensy/td_libs_OneWire.html
-architectures=esp32
+architectures=esp8266,esp32
diff --git a/lib/lib_basic/TasmotaModbus-1.2.0/library.json b/lib/lib_basic/TasmotaModbus-1.2.0/library.json
index 6de11d0e0..3c28959ea 100644
--- a/lib/lib_basic/TasmotaModbus-1.2.0/library.json
+++ b/lib/lib_basic/TasmotaModbus-1.2.0/library.json
@@ -11,5 +11,5 @@
"url": "https://github.com/arendst/Tasmota/lib/TasmotaModbus"
},
"frameworks": "arduino",
- "platforms": "espressif8266"
+ "platforms": ["espressif8266", "espressif32"]
}
diff --git a/lib/lib_basic/TasmotaModbus-1.2.0/library.properties b/lib/lib_basic/TasmotaModbus-1.2.0/library.properties
index 9197e7cfe..7ac182843 100644
--- a/lib/lib_basic/TasmotaModbus-1.2.0/library.properties
+++ b/lib/lib_basic/TasmotaModbus-1.2.0/library.properties
@@ -6,4 +6,4 @@ sentence=Basic modbus wrapper for TasmotaSerial for ESP8266.
paragraph=
category=Signal Input/Output
url=
-architectures=esp8266
+architectures=esp8266,esp32
diff --git a/lib/lib_basic/TasmotaModbus-1.2.0/src/TasmotaModbus.cpp b/lib/lib_basic/TasmotaModbus-1.2.0/src/TasmotaModbus.cpp
index b1a8c48f8..8c2180505 100644
--- a/lib/lib_basic/TasmotaModbus-1.2.0/src/TasmotaModbus.cpp
+++ b/lib/lib_basic/TasmotaModbus-1.2.0/src/TasmotaModbus.cpp
@@ -24,7 +24,7 @@ TasmotaModbus::TasmotaModbus(int receive_pin, int transmit_pin) : TasmotaSerial(
mb_address = 0;
}
-uint16_t CalculateCRC(uint8_t *frame, uint8_t num)
+uint16_t TasmotaModbus::CalculateCRC(uint8_t *frame, uint8_t num)
{
uint16_t crc = 0xFFFF;
@@ -81,30 +81,37 @@ bool TasmotaModbus::ReceiveReady()
uint8_t TasmotaModbus::ReceiveBuffer(uint8_t *buffer, uint8_t register_count)
{
mb_len = 0;
- uint32_t last = millis();
- while ((available() > 0) && (mb_len < (register_count *2) + 5) && (millis() - last < 10)) {
- uint8_t data = (uint8_t)read();
- if (!mb_len) { // Skip leading data as provided by hardware serial
- if (mb_address == data) {
+ uint32_t timeout = millis() + 10;
+ while ((mb_len < (register_count *2) + 5) && (millis() < timeout)) {
+ if (available()) {
+ uint8_t data = (uint8_t)read();
+ if (!mb_len) { // Skip leading data as provided by hardware serial
+ if (mb_address == data) {
+ buffer[mb_len++] = data;
+ }
+ } else {
buffer[mb_len++] = data;
- }
- } else {
- buffer[mb_len++] = data;
- if (3 == mb_len) {
- if (buffer[1] & 0x80) { // 01 84 02 f2 f1
- return buffer[2]; // 1 = Illegal Function,
- // 2 = Illegal Data Address,
- // 3 = Illegal Data Value,
- // 4 = Slave Error
- // 5 = Acknowledge but not finished (no error)
- // 6 = Slave Busy
- // 8 = Memory Parity error
- // 10 = Gateway Path Unavailable
- // 11 = Gateway Target device failed to respond
+ if (3 == mb_len) {
+ if (buffer[1] & 0x80) { // 01 84 02 f2 f1
+ if (0 == buffer[2]) {
+ return 3; // 3 = Illegal Data Value,
+ }
+ return buffer[2]; // 1 = Illegal Function,
+ // 2 = Illegal Data Address,
+ // 3 = Illegal Data Value,
+ // 4 = Slave Error
+ // 5 = Acknowledge but not finished (no error)
+ // 6 = Slave Busy
+ // 8 = Memory Parity error
+ // 10 = Gateway Path Unavailable
+ // 11 = Gateway Target device failed to respond
+ }
}
}
+
+ timeout = millis() + 10;
+
}
- last = millis();
}
if (mb_len < 7) { return 7; } // 7 = Not enough data
diff --git a/lib/lib_basic/TasmotaModbus-1.2.0/src/TasmotaModbus.h b/lib/lib_basic/TasmotaModbus-1.2.0/src/TasmotaModbus.h
index 22d4e12f3..d6c0c2883 100644
--- a/lib/lib_basic/TasmotaModbus-1.2.0/src/TasmotaModbus.h
+++ b/lib/lib_basic/TasmotaModbus-1.2.0/src/TasmotaModbus.h
@@ -32,6 +32,8 @@ class TasmotaModbus : public TasmotaSerial {
int Begin(long speed = TM_MODBUS_BAUDRATE, int stop_bits = 1);
+ uint16_t CalculateCRC(uint8_t *frame, uint8_t num);
+
void Send(uint8_t device_address, uint8_t function_code, uint16_t start_address, uint16_t register_count);
bool ReceiveReady();
diff --git a/lib/lib_display/FT5206_Library/library.properties b/lib/lib_display/FT5206_Library/library.properties
index e28709e5c..14b3e2e94 100644
--- a/lib/lib_display/FT5206_Library/library.properties
+++ b/lib/lib_display/FT5206_Library/library.properties
@@ -7,4 +7,3 @@ paragraph=Arduino library for FT5206 chip. Tested with ESP32
category=Communication
url=https://github.com/lewisxhe/FT5206_Library
architectures=*
-architectures=esp32
\ No newline at end of file
diff --git a/lib/lib_display/LiquidCrystal_I2C-1.1.3/library.json b/lib/lib_display/LiquidCrystal_I2C-1.1.3/library.json
index 5a23a3989..1553b6e44 100644
--- a/lib/lib_display/LiquidCrystal_I2C-1.1.3/library.json
+++ b/lib/lib_display/LiquidCrystal_I2C-1.1.3/library.json
@@ -10,6 +10,6 @@
"frameworks": "arduino",
"platforms":
[
- "atmelavr"
+ "*"
]
-}
\ No newline at end of file
+}
diff --git a/lib/lib_display/esp-epaper-29-ws-20171230-gemu-1.1/library.properties b/lib/lib_display/esp-epaper-29-ws-20171230-gemu-1.1/library.properties
index 488cfdda2..29a16c0e7 100644
--- a/lib/lib_display/esp-epaper-29-ws-20171230-gemu-1.1/library.properties
+++ b/lib/lib_display/esp-epaper-29-ws-20171230-gemu-1.1/library.properties
@@ -6,4 +6,4 @@ sentence=ESP8266 library for Waveshare e-paper display.
paragraph=
category=Display
url=https://github.com/gemu2015/Sonoff-Tasmota/tree/displays/lib/esp-epaper-29-ws-20171230-gemu-1.0#
-architectures=esp8266
+architectures=*
diff --git a/lib/lib_i2c/Adafruit_TSL2591_Library/Adafruit_TSL2591.cpp b/lib/lib_i2c/Adafruit_TSL2591_Library/Adafruit_TSL2591.cpp
index 230ef6294..1cd9a9de3 100644
--- a/lib/lib_i2c/Adafruit_TSL2591_Library/Adafruit_TSL2591.cpp
+++ b/lib/lib_i2c/Adafruit_TSL2591_Library/Adafruit_TSL2591.cpp
@@ -338,7 +338,15 @@ float Adafruit_TSL2591::calculateLux(uint16_t ch0, uint16_t ch1)
// Alternate lux calculation 1
// See: https://github.com/adafruit/Adafruit_TSL2591_Library/issues/14
- lux = ( ((float)ch0 - (float)ch1 )) * (1.0F - ((float)ch1/(float)ch0) ) / cpl;
+ if(ch0 > 0)
+ {
+ lux = ( ((float)ch0 - (float)ch1 )) * (1.0F - ((float)ch1/(float)ch0) ) / cpl;
+ }
+ else
+ {
+ lux = 0.0F;
+ }
+
// Alternate lux calculation 2
//lux = ( (float)ch0 - ( 1.7F * (float)ch1 ) ) / cpl;
diff --git a/lib/lib_i2c/BME680_driver-bme680_v3.5.9/library.properties b/lib/lib_i2c/BME680_driver-bme680_v3.5.9/library.properties
index 93954f32d..bf0211d28 100644
--- a/lib/lib_i2c/BME680_driver-bme680_v3.5.9/library.properties
+++ b/lib/lib_i2c/BME680_driver-bme680_v3.5.9/library.properties
@@ -6,4 +6,4 @@ sentence=Sensor driver for BME680 sensor
paragraph=Sensor driver for BME680 sensor
category=Sensor
url=
-architectures=esp8266
+architectures=esp8266,esp32
diff --git a/lib/lib_i2c/I2Cdevlib-Core/.library.json b/lib/lib_i2c/I2Cdevlib-Core/.library.json
index 258acc176..14886c889 100644
--- a/lib/lib_i2c/I2Cdevlib-Core/.library.json
+++ b/lib/lib_i2c/I2Cdevlib-Core/.library.json
@@ -5,7 +5,7 @@
"type": "git"
},
"platforms": [
- "atmelavr"
+ "*"
],
"frameworks": [
"arduino"
@@ -28,4 +28,4 @@
],
"id": 11,
"description": "The I2C Device Library (I2Cdevlib) is a collection of uniform and well-documented classes to provide simple and intuitive interfaces to I2C devices."
-}
\ No newline at end of file
+}
diff --git a/lib/lib_i2c/I2Cdevlib-Core/library.json b/lib/lib_i2c/I2Cdevlib-Core/library.json
index d45609604..5a2125b5b 100644
--- a/lib/lib_i2c/I2Cdevlib-Core/library.json
+++ b/lib/lib_i2c/I2Cdevlib-Core/library.json
@@ -9,5 +9,5 @@
"url": "https://github.com/jrowberg/i2cdevlib.git"
},
"frameworks": "arduino",
- "platforms": "atmelavr"
+ "platforms": "*"
}
diff --git a/lib/lib_rf/cc1101/library.properties b/lib/lib_rf/cc1101/library.properties
index bfb8a65bc..73e393575 100644
--- a/lib/lib_rf/cc1101/library.properties
+++ b/lib/lib_rf/cc1101/library.properties
@@ -6,4 +6,4 @@ sentence=.
paragraph=
category=
url=
-architectures=esp8266
+architectures=esp8266,esp32
diff --git a/lib/lib_ssl/bearssl-esp8266/library.properties b/lib/lib_ssl/bearssl-esp8266/library.properties
index 1d936cb88..032d4d247 100644
--- a/lib/lib_ssl/bearssl-esp8266/library.properties
+++ b/lib/lib_ssl/bearssl-esp8266/library.properties
@@ -6,4 +6,4 @@ sentence=BearSSL implementation of the SSL/TLS protocol optimized for ESP8266 by
paragraph=
category=Other
url=https://github.com/earlephilhower/bearssl-esp8266.git
-architectures=esp8266
+architectures=esp8266,esp32
diff --git a/lib/libesp32/CORE2_Library/library.properties b/lib/libesp32/CORE2_Library/library.properties
new file mode 100644
index 000000000..09baffd9a
--- /dev/null
+++ b/lib/libesp32/CORE2_Library/library.properties
@@ -0,0 +1,9 @@
+name=M5 Stack Core2 library
+version=1.0
+author=Gerhard Mutz
+maintainer=Gerhard Mutz
+sentence=Allows Tasmota to use Core2
+paragraph=Allows Tasmota to Core2 for esp32
+category=ESP32
+url=
+architectures=*
diff --git a/lib/libesp32/NimBLE-Arduino/CHANGELOG.md b/lib/libesp32/NimBLE-Arduino/CHANGELOG.md
index 8dfc5a141..e763a3c6d 100644
--- a/lib/libesp32/NimBLE-Arduino/CHANGELOG.md
+++ b/lib/libesp32/NimBLE-Arduino/CHANGELOG.md
@@ -2,6 +2,75 @@
All notable changes to this project will be documented in this file.
+## [1.1.0] - 2021-01-20
+
+### Added
+- `NimBLEDevice::setOwnAddrType` added to enable the use of random and random-resolvable addresses, by asukiaaa
+
+- New examples for securing and authenticating client/server connections, by mblasee.
+
+- `NimBLEAdvertiseing::SetMinPreferred` and `NimBLEAdvertiseing::SetMinPreferred` re-added.
+
+- Conditional checks added for command line config options in `nimconfig.h` to support custom configuration in platformio.
+
+- `NimBLEClient::setValue` Now takes an extra bool parameter `response` to enable the use of write with response (default = false).
+
+- `NimBLEClient::getCharacteristic(uint16_t handle)` Enabling the use of the characteristic handle to be used to find
+the NimBLERemoteCharacteristic object.
+
+- `NimBLEHIDDevice` class added by wakwak-koba.
+
+- `NimBLEServerCallbacks::onDisconnect` overloaded callback added to provide a ble_gap_conn_desc parameter for the application
+to obtain information about the disconnected client.
+
+- Conditional checks in `nimconfig.h` for command line defined macros to support platformio config settings.
+
+### Changed
+- `NimBLEAdvertising::start` now returns a bool value to indicate success/failure.
+
+- Some asserts were removed in `NimBLEAdvertising::start` and replaced with better return code handling and logging.
+
+- If a host reset event occurs, scanning and advertising will now only be restarted if their previous duration was indefinite.
+
+- `NimBLERemoteCharacteristic::subscribe` and `NimBLERemoteCharacteristic::registerForNotify` will now set the callback
+regardless of the existance of the CCCD and return true unless the descriptor write operation failed.
+
+- Advertising tx power level is now sent in the advertisement packet instead of scan response.
+
+- `NimBLEScan` When the scan ends the scan stopped flag is now set before calling the scan complete callback (if used)
+this allows the starting of a new scan from the callback function.
+
+### Fixed
+- Sometimes `NimBLEClient::connect` would hang on the task block if no event arrived to unblock.
+A time limit has been added to timeout appropriately.
+
+- When getting descriptors for a characterisic the end handle of the service was used as a proxy for the characteristic end
+handle. This would be rejected by some devices and has been changed to use the next characteristic handle as the end when possible.
+
+- An exception could occur when deleting a client instance if a notification arrived while the attribute vectors were being
+deleted. A flag has been added to prevent this.
+
+- An exception could occur after a host reset event when the host re-synced if the tasks that were stopped during the event did
+not finish processing. A yield has been added after re-syncing to allow tasks to finish before proceeding.
+
+- Occasionally the controller would fail to send a disconnected event causing the client to indicate it is connected
+and would be unable to reconnect. A timer has been added to reset the host/controller if it expires.
+
+- Occasionally the call to start scanning would get stuck in a loop on BLE_HS_EBUSY, this loop has been removed.
+
+- 16bit and 32bit UUID's in some cases were not discovered or compared correctly if the device
+advertised them as 16/32bit but resolved them to 128bits. Both are now checked.
+
+- `FreeRTOS` compile errors resolved in latest Ardruino core and IDF v3.3.
+
+- Multiple instances of `time()` called inside critical sections caused sporadic crashes, these have been moved out of critical regions.
+
+- Advertisement type now correctly set when using non-connectable (advertiser only) mode.
+
+- Advertising payload length correction, now accounts for appearance.
+
+- (Arduino) Ensure controller mode is set to BLE Only.
+
## [1.0.2] - 2020-09-13
### Changed
diff --git a/lib/libesp32/NimBLE-Arduino/README.md b/lib/libesp32/NimBLE-Arduino/README.md
index 120b0c782..08e2a69c0 100644
--- a/lib/libesp32/NimBLE-Arduino/README.md
+++ b/lib/libesp32/NimBLE-Arduino/README.md
@@ -1,5 +1,7 @@
[Latest release 
](https://github.com/h2zero/NimBLE-Arduino/releases/latest/)
+
+Need help? Have questions or suggestions? Join the [](https://gitter.im/NimBLE-Arduino/community?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
# NimBLE-Arduino
@@ -57,6 +59,8 @@ Also see [Improvements_and_updates](docs/Improvements_and_updates.md) for inform
[Full API documentation and class list can be found here.](https://h2zero.github.io/esp-nimble-cpp/)
+For added performance and optimizations see [Usage tips](docs/Usage_tips.md).
+
Check the Refactored_original_examples in the examples folder for highlights of the differences with the original library.
More advanced examples highlighting many available features are in examples/ NimBLE_Server, NimBLE_Client.
@@ -68,9 +72,9 @@ such as increasing max connections, default is 3, absolute maximum connections i
# 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)
+This Library is tracking the esp-nimble repo, nimble-1.2.0-idf master branch, currently [@f4ae049.](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)
+Also tracking the NimBLE related changes in ESP-IDF, master branch, currently [@3caa969.](https://github.com/espressif/esp-idf/tree/master/components/bt/host/nimble)
# Acknowledgments
diff --git a/lib/libesp32/NimBLE-Arduino/docs/Command_line_config.md b/lib/libesp32/NimBLE-Arduino/docs/Command_line_config.md
new file mode 100644
index 000000000..813156f76
--- /dev/null
+++ b/lib/libesp32/NimBLE-Arduino/docs/Command_line_config.md
@@ -0,0 +1,93 @@
+# Arduino command line and platformio config options
+
+`CONFIG_BT_NIMBLE_ROLE_CENTRAL_DISABLED`
+
+ If defined, NimBLE Client functions will not be included.
+- Reduces flash size by approx. 7kB.
+
+
+`CONFIG_BT_NIMBLE_ROLE_OBSERVER_DISABLED`
+
+If defined, NimBLE Scan functions will not be included.
+- Reduces flash size by approx. 26kB.
+
+
+`CONFIG_BT_NIMBLE_ROLE_PERIPHERAL_DISABLED`
+
+If defined NimBLE Server functions will not be included.
+- Reduces flash size by approx. 16kB.
+
+
+`CONFIG_BT_NIMBLE_ROLE_BROADCASTER_DISABLED`
+
+If defined, NimBLE Advertising functions will not be included.
+- Reduces flash size by approx. 5kB.
+
+
+`CONFIG_BT_NIMBLE_DEBUG`
+
+If defined, enables debug log messages from the NimBLE host
+- Uses approx. 32kB of flash memory.
+
+
+`CONFIG_NIMBLE_CPP_ENABLE_RETURN_CODE_TEXT`
+
+If defined, NimBLE host return codes will be printed as text in debug log messages.
+- Uses approx. 7kB of flash memory.
+
+
+`CONFIG_NIMBLE_CPP_ENABLE_GAP_EVENT_CODE_TEXT`
+
+If defined, GAP event codes will be printed as text in debug log messages.
+- Uses approx. 1kB of flash memory.
+
+
+`CONFIG_NIMBLE_CPP_ENABLE_ADVERTISMENT_TYPE_TEXT`
+
+If defined, advertisment types will be printed as text while scanning in debug log messages.
+- Uses approx. 250 bytes of flash memory.
+
+
+`CONFIG_BT_NIMBLE_PINNED_TO_CORE`
+
+Sets the core the NimBLE host stack will run on
+- Options: 0 or 1
+
+
+`CONFIG_BT_NIMBLE_TASK_STACK_SIZE`
+
+Set the task stack size for the NimBLE core.
+- Default is 4096
+
+
+
+`CONFIG_BT_NIMBLE_MEM_ALLOC_MODE_EXTERNAL`
+
+Sets the NimBLE stack to use external PSRAM will be loaded
+- Must be defined with a value of 1; Default is CONFIG_BT_NIMBLE_MEM_ALLOC_MODE_INTERNAL 1
+
+
+`CONFIG_BT_NIMBLE_MAX_CONNECTIONS`
+
+Sets the number of simultaneous connections (esp controller max is 9)
+- Default value is 3
+
+
+`CONFIG_BT_NIMBLE_MAX_BONDS`
+
+Sets the number of devices allowed to store/bond with
+- Default value is 3
+
+
+`CONFIG_BT_NIMBLE_MAX_CCCDS`
+
+Sets the maximum number of CCCD subscriptions to store
+- Default value is 8
+
+
+`CONFIG_BT_NIMBLE_SVC_GAP_DEVICE_NAME`
+
+Set the default device name
+- Default value is "nimble"
+
+
diff --git a/lib/libesp32/NimBLE-Arduino/examples/BLE_Beacon_Scanner/BLE_Beacon_Scanner.ino b/lib/libesp32/NimBLE-Arduino/examples/BLE_Beacon_Scanner/BLE_Beacon_Scanner.ino
index fea6b7d50..4161aabf7 100644
--- a/lib/libesp32/NimBLE-Arduino/examples/BLE_Beacon_Scanner/BLE_Beacon_Scanner.ino
+++ b/lib/libesp32/NimBLE-Arduino/examples/BLE_Beacon_Scanner/BLE_Beacon_Scanner.ino
@@ -79,27 +79,24 @@ class MyAdvertisedDeviceCallbacks : public BLEAdvertisedDeviceCallbacks
return;
}
- uint8_t *payLoad = advertisedDevice->getPayload();
+ BLEUUID eddyUUID = (uint16_t)0xfeaa;
- BLEUUID checkUrlUUID = (uint16_t)0xfeaa;
-
- if (advertisedDevice->getServiceUUID().equals(checkUrlUUID))
+ if (advertisedDevice->getServiceUUID().equals(eddyUUID))
{
- if (payLoad[11] == 0x10)
+ std::string serviceData = advertisedDevice->getServiceData(eddyUUID);
+ if (serviceData[0] == 0x10)
{
Serial.println("Found an EddystoneURL beacon!");
BLEEddystoneURL foundEddyURL = BLEEddystoneURL();
- std::string eddyContent((char *)&payLoad[11]); // incomplete EddystoneURL struct!
- foundEddyURL.setData(eddyContent);
+ foundEddyURL.setData(serviceData);
std::string bareURL = foundEddyURL.getURL();
if (bareURL[0] == 0x00)
{
- size_t payLoadLen = advertisedDevice->getPayloadLength();
Serial.println("DATA-->");
- for (int idx = 0; idx < payLoadLen; idx++)
+ for (int idx = 0; idx < serviceData.length(); idx++)
{
- Serial.printf("0x%08X ", payLoad[idx]);
+ Serial.printf("0x%08X ", serviceData[idx]);
}
Serial.println("\nInvalid Data");
return;
@@ -110,23 +107,15 @@ class MyAdvertisedDeviceCallbacks : public BLEAdvertisedDeviceCallbacks
Serial.printf("TX power %d\n", foundEddyURL.getPower());
Serial.println("\n");
}
- else if (payLoad[11] == 0x20)
+ else if (serviceData[0] == 0x20)
{
Serial.println("Found an EddystoneTLM beacon!");
BLEEddystoneTLM foundEddyURL = BLEEddystoneTLM();
- std::string eddyContent((char *)&payLoad[11]); // incomplete EddystoneURL struct!
+ foundEddyURL.setData(serviceData);
- eddyContent = "01234567890123";
-
- for (int idx = 0; idx < 14; idx++)
- {
- eddyContent[idx] = payLoad[idx + 11];
- }
-
- foundEddyURL.setData(eddyContent);
Serial.printf("Reported battery voltage: %dmV\n", foundEddyURL.getVolt());
Serial.printf("Reported temperature from TLM class: %.2fC\n", (double)foundEddyURL.getTemp());
- int temp = (int)payLoad[16] + (int)(payLoad[15] << 8);
+ int temp = (int)serviceData[5] + (int)(serviceData[4] << 8);
float calcTemp = temp / 256.0f;
Serial.printf("Reported temperature from data: %.2fC\n", calcTemp);
Serial.printf("Reported advertise count: %d\n", foundEddyURL.getCount());
diff --git a/lib/libesp32/NimBLE-Arduino/examples/NimBLE_Client/NimBLE_Client.ino b/lib/libesp32/NimBLE-Arduino/examples/NimBLE_Client/NimBLE_Client.ino
index 9ef41b91c..a3899cddc 100644
--- a/lib/libesp32/NimBLE-Arduino/examples/NimBLE_Client/NimBLE_Client.ino
+++ b/lib/libesp32/NimBLE-Arduino/examples/NimBLE_Client/NimBLE_Client.ino
@@ -2,10 +2,10 @@
/** NimBLE_Server Demo:
*
* Demonstrates many of the available features of the NimBLE client library.
- *
+ *
* Created: on March 24 2020
* Author: H2zero
- *
+ *
*/
#include
@@ -19,15 +19,15 @@ static uint32_t scanTime = 0; /** 0 = scan forever */
/** None of these are required as they will be handled by the library with defaults. **
- ** Remove as you see fit for your needs */
+ ** Remove as you see fit for your needs */
class ClientCallbacks : public NimBLEClientCallbacks {
void onConnect(NimBLEClient* pClient) {
Serial.println("Connected");
/** After connection we should change the parameters if we don't need fast response times.
- * These settings are 150ms interval, 0 latency, 450ms timout.
+ * These settings are 150ms interval, 0 latency, 450ms timout.
* Timeout should be a multiple of the interval, minimum is 100ms.
* I find a multiple of 3-5 * the interval works best for quick response/reconnect.
- * Min interval: 120 * 1.25ms = 150, Max interval: 120 * 1.25ms = 150, 0 latency, 60 * 10ms = 600ms timeout
+ * Min interval: 120 * 1.25ms = 150, Max interval: 120 * 1.25ms = 150, 0 latency, 60 * 10ms = 600ms timeout
*/
pClient->updateConnParams(120,120,0,60);
};
@@ -37,9 +37,9 @@ class ClientCallbacks : public NimBLEClientCallbacks {
Serial.println(" Disconnected - Starting scan");
NimBLEDevice::getScan()->start(scanTime, scanEndedCB);
};
-
+
/** Called when the peripheral requests a change to the connection parameters.
- * Return true to accept and apply them or false to reject and keep
+ * Return true to accept and apply them or false to reject and keep
* the currently used parameters. Default will return true.
*/
bool onConnParamsUpdateRequest(NimBLEClient* pClient, const ble_gap_upd_params* params) {
@@ -55,7 +55,7 @@ class ClientCallbacks : public NimBLEClientCallbacks {
return true;
};
-
+
/********************* Security handled here **********************
****** Note: these are the same return values as defaults ********/
uint32_t onPassKeyRequest(){
@@ -85,7 +85,7 @@ class ClientCallbacks : public NimBLEClientCallbacks {
/** Define a class to handle the callbacks when advertisments are received */
class AdvertisedDeviceCallbacks: public NimBLEAdvertisedDeviceCallbacks {
-
+
void onResult(NimBLEAdvertisedDevice* advertisedDevice) {
Serial.print("Advertised Device found: ");
Serial.println(advertisedDevice->toString().c_str());
@@ -94,9 +94,9 @@ class AdvertisedDeviceCallbacks: public NimBLEAdvertisedDeviceCallbacks {
Serial.println("Found Our Service");
/** stop scan before connecting */
NimBLEDevice::getScan()->stop();
- /** Save the device reference in a global for the client to use*/
+ /** Save the device reference in a global for the client to use*/
advDevice = advertisedDevice;
- /** Ready to connect now */
+ /** Ready to connect now */
doConnect = true;
}
};
@@ -105,7 +105,7 @@ class AdvertisedDeviceCallbacks: public NimBLEAdvertisedDeviceCallbacks {
/** Notification / Indication receiving handler callback */
void notifyCB(NimBLERemoteCharacteristic* pRemoteCharacteristic, uint8_t* pData, size_t length, bool isNotify){
- std::string str = (isNotify == true) ? "Notification" : "Indication";
+ std::string str = (isNotify == true) ? "Notification" : "Indication";
str += " from ";
/** NimBLEAddress and NimBLEUUID have std::string operators */
str += std::string(pRemoteCharacteristic->getRemoteService()->getClient()->getPeerAddress());
@@ -128,10 +128,10 @@ static ClientCallbacks clientCB;
/** Handles the provisioning of clients and connects / interfaces with the server */
bool connectToServer() {
NimBLEClient* pClient = nullptr;
-
+
/** Check if we have a client we should reuse first **/
if(NimBLEDevice::getClientListSize()) {
- /** Special case when we already know this device, we send false as the
+ /** Special case when we already know this device, we send false as the
* second argument in connect() to prevent refreshing the service database.
* This saves considerable time and power.
*/
@@ -142,7 +142,7 @@ bool connectToServer() {
return false;
}
Serial.println("Reconnected client");
- }
+ }
/** We don't already have a client that knows this device,
* we will check for a client that is disconnected that we can use.
*/
@@ -150,28 +150,28 @@ bool connectToServer() {
pClient = NimBLEDevice::getDisconnectedClient();
}
}
-
+
/** No client to reuse? Create a new one. */
if(!pClient) {
if(NimBLEDevice::getClientListSize() >= NIMBLE_MAX_CONNECTIONS) {
Serial.println("Max clients reached - no more connections available");
return false;
}
-
+
pClient = NimBLEDevice::createClient();
-
+
Serial.println("New client created");
-
+
pClient->setClientCallbacks(&clientCB, false);
- /** Set initial connection parameters: These settings are 15ms interval, 0 latency, 120ms timout.
- * These settings are safe for 3 clients to connect reliably, can go faster if you have less
+ /** Set initial connection parameters: These settings are 15ms interval, 0 latency, 120ms timout.
+ * These settings are safe for 3 clients to connect reliably, can go faster if you have less
* connections. Timeout should be a multiple of the interval, minimum is 100ms.
- * Min interval: 12 * 1.25ms = 15, Max interval: 12 * 1.25ms = 15, 0 latency, 51 * 10ms = 510ms timeout
+ * Min interval: 12 * 1.25ms = 15, Max interval: 12 * 1.25ms = 15, 0 latency, 51 * 10ms = 510ms timeout
*/
pClient->setConnectionParams(12,12,0,51);
/** Set how long we are willing to wait for the connection to complete (seconds), default is 30. */
pClient->setConnectTimeout(5);
-
+
if (!pClient->connect(advDevice)) {
/** Created a client but failed to connect, don't need to keep it as it has no data */
@@ -179,149 +179,147 @@ bool connectToServer() {
Serial.println("Failed to connect, deleted client");
return false;
}
- }
-
+ }
+
if(!pClient->isConnected()) {
if (!pClient->connect(advDevice)) {
Serial.println("Failed to connect");
return false;
}
}
-
+
Serial.print("Connected to: ");
Serial.println(pClient->getPeerAddress().toString().c_str());
Serial.print("RSSI: ");
Serial.println(pClient->getRssi());
-
+
/** Now we can read/write/subscribe the charateristics of the services we are interested in */
NimBLERemoteService* pSvc = nullptr;
NimBLERemoteCharacteristic* pChr = nullptr;
NimBLERemoteDescriptor* pDsc = nullptr;
-
+
pSvc = pClient->getService("DEAD");
if(pSvc) { /** make sure it's not null */
pChr = pSvc->getCharacteristic("BEEF");
- }
- if(pChr) { /** make sure it's not null */
- if(pChr->canRead()) {
- Serial.print(pChr->getUUID().toString().c_str());
- Serial.print(" Value: ");
- Serial.println(pChr->readValue().c_str());
- }
-
- if(pChr->canWrite()) {
- if(pChr->writeValue("Tasty")) {
- Serial.print("Wrote new value to: ");
- Serial.println(pChr->getUUID().toString().c_str());
- }
- else {
- /** Disconnect if write failed */
- pClient->disconnect();
- return false;
- }
-
+ if(pChr) { /** make sure it's not null */
if(pChr->canRead()) {
- Serial.print("The value of: ");
Serial.print(pChr->getUUID().toString().c_str());
- Serial.print(" is now: ");
+ Serial.print(" Value: ");
Serial.println(pChr->readValue().c_str());
}
- }
-
- /** 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()) {
- //if(!pChr->registerForNotify(notifyCB)) {
- if(!pChr->subscribe(true, notifyCB)) {
- /** Disconnect if subscribe failed */
- pClient->disconnect();
- return false;
+
+ if(pChr->canWrite()) {
+ if(pChr->writeValue("Tasty")) {
+ Serial.print("Wrote new value to: ");
+ Serial.println(pChr->getUUID().toString().c_str());
+ }
+ else {
+ /** Disconnect if write failed */
+ pClient->disconnect();
+ return false;
+ }
+
+ if(pChr->canRead()) {
+ Serial.print("The value of: ");
+ Serial.print(pChr->getUUID().toString().c_str());
+ Serial.print(" is now: ");
+ Serial.println(pChr->readValue().c_str());
+ }
+ }
+
+ /** 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()) {
+ //if(!pChr->registerForNotify(notifyCB)) {
+ if(!pChr->subscribe(true, notifyCB)) {
+ /** Disconnect if subscribe failed */
+ pClient->disconnect();
+ return false;
+ }
+ }
+ else if(pChr->canIndicate()) {
+ /** 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;
+ }
}
}
- else if(pChr->canIndicate()) {
- /** 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;
- }
- }
- }
-
- else{
+
+ } else {
Serial.println("DEAD service not found.");
}
-
+
pSvc = pClient->getService("BAAD");
if(pSvc) { /** make sure it's not null */
pChr = pSvc->getCharacteristic("F00D");
- }
- if(pChr) { /** make sure it's not null */
- if(pChr->canRead()) {
- Serial.print(pChr->getUUID().toString().c_str());
- Serial.print(" Value: ");
- Serial.println(pChr->readValue().c_str());
- }
-
- pDsc = pChr->getDescriptor(NimBLEUUID("C01D"));
- if(pDsc) { /** make sure it's not null */
- Serial.print("Descriptor: ");
- Serial.print(pDsc->getUUID().toString().c_str());
- Serial.print(" Value: ");
- Serial.println(pDsc->readValue().c_str());
- }
-
- if(pChr->canWrite()) {
- if(pChr->writeValue("No tip!")) {
- Serial.print("Wrote new value to: ");
- Serial.println(pChr->getUUID().toString().c_str());
- }
- else {
- /** Disconnect if write failed */
- pClient->disconnect();
- return false;
- }
-
+ if(pChr) { /** make sure it's not null */
if(pChr->canRead()) {
- Serial.print("The value of: ");
Serial.print(pChr->getUUID().toString().c_str());
- Serial.print(" is now: ");
+ Serial.print(" Value: ");
Serial.println(pChr->readValue().c_str());
}
- }
-
- /** 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()) {
- //if(!pChr->registerForNotify(notifyCB)) {
- if(!pChr->subscribe(true, notifyCB)) {
- /** Disconnect if subscribe failed */
- pClient->disconnect();
- return false;
- }
- }
- else if(pChr->canIndicate()) {
- /** 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;
- }
- }
- }
- else{
+ pDsc = pChr->getDescriptor(NimBLEUUID("C01D"));
+ if(pDsc) { /** make sure it's not null */
+ Serial.print("Descriptor: ");
+ Serial.print(pDsc->getUUID().toString().c_str());
+ Serial.print(" Value: ");
+ Serial.println(pDsc->readValue().c_str());
+ }
+
+ if(pChr->canWrite()) {
+ if(pChr->writeValue("No tip!")) {
+ Serial.print("Wrote new value to: ");
+ Serial.println(pChr->getUUID().toString().c_str());
+ }
+ else {
+ /** Disconnect if write failed */
+ pClient->disconnect();
+ return false;
+ }
+
+ if(pChr->canRead()) {
+ Serial.print("The value of: ");
+ Serial.print(pChr->getUUID().toString().c_str());
+ Serial.print(" is now: ");
+ Serial.println(pChr->readValue().c_str());
+ }
+ }
+
+ /** 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()) {
+ //if(!pChr->registerForNotify(notifyCB)) {
+ if(!pChr->subscribe(true, notifyCB)) {
+ /** Disconnect if subscribe failed */
+ pClient->disconnect();
+ return false;
+ }
+ }
+ else if(pChr->canIndicate()) {
+ /** 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;
+ }
+ }
+ }
+
+ } else {
Serial.println("BAAD service not found.");
}
-
+
Serial.println("Done with this device!");
return true;
}
@@ -331,7 +329,7 @@ void setup (){
Serial.println("Starting NimBLE Client");
/** Initialize NimBLE, no device name spcified as we are not advertising */
NimBLEDevice::init("");
-
+
/** Set the IO capabilities of the device, each option will trigger a different pairing method.
* BLE_HS_IO_KEYBOARD_ONLY - Passkey pairing
* BLE_HS_IO_DISPLAY_YESNO - Numeric comparison pairing
@@ -339,37 +337,37 @@ void setup (){
*/
//NimBLEDevice::setSecurityIOCap(BLE_HS_IO_KEYBOARD_ONLY); // use passkey
//NimBLEDevice::setSecurityIOCap(BLE_HS_IO_DISPLAY_YESNO); //use numeric comparison
-
+
/** 2 different ways to set security - both calls achieve the same result.
* no bonding, no man in the middle protection, secure connections.
- *
- * These are the default values, only shown here for demonstration.
- */
- //NimBLEDevice::setSecurityAuth(false, false, true);
+ *
+ * These are the default values, only shown here for demonstration.
+ */
+ //NimBLEDevice::setSecurityAuth(false, false, true);
NimBLEDevice::setSecurityAuth(/*BLE_SM_PAIR_AUTHREQ_BOND | BLE_SM_PAIR_AUTHREQ_MITM |*/ BLE_SM_PAIR_AUTHREQ_SC);
-
+
/** Optional: set the transmit power, default is 3db */
NimBLEDevice::setPower(ESP_PWR_LVL_P9); /** +9db */
-
+
/** Optional: set any devices you don't want to get advertisments from */
- // NimBLEDevice::addIgnored(NimBLEAddress ("aa:bb:cc:dd:ee:ff"));
-
- /** create new scan */
- NimBLEScan* pScan = NimBLEDevice::getScan();
-
+ // NimBLEDevice::addIgnored(NimBLEAddress ("aa:bb:cc:dd:ee:ff"));
+
+ /** create new scan */
+ NimBLEScan* pScan = NimBLEDevice::getScan();
+
/** create a callback that gets called when advertisers are found */
pScan->setAdvertisedDeviceCallbacks(new AdvertisedDeviceCallbacks());
-
+
/** Set scan interval (how often) and window (how long) in milliseconds */
pScan->setInterval(45);
pScan->setWindow(15);
-
+
/** Active scan will gather scan response data from advertisers
* but will use more energy from both devices
*/
pScan->setActiveScan(true);
/** Start scanning for advertisers for the scan time specified (in seconds) 0 = forever
- * Optional callback for when scanning stops.
+ * Optional callback for when scanning stops.
*/
pScan->start(scanTime, scanEndedCB);
}
@@ -380,15 +378,15 @@ void loop (){
while(!doConnect){
delay(1);
}
-
+
doConnect = false;
-
+
/** Found a device we want to connect to, do it now */
if(connectToServer()) {
Serial.println("Success! we should now be getting notifications, scanning for more!");
} else {
Serial.println("Failed to connect, starting scan");
}
-
+
NimBLEDevice::getScan()->start(scanTime,scanEndedCB);
}
diff --git a/lib/libesp32/NimBLE-Arduino/examples/NimBLE_Secure_Client/NimBLE_Secure_Client.ino b/lib/libesp32/NimBLE-Arduino/examples/NimBLE_Secure_Client/NimBLE_Secure_Client.ino
new file mode 100644
index 000000000..6f9af4f74
--- /dev/null
+++ b/lib/libesp32/NimBLE-Arduino/examples/NimBLE_Secure_Client/NimBLE_Secure_Client.ino
@@ -0,0 +1,91 @@
+/** NimBLE_Secure_Client Demo:
+ *
+ * This example demonstrates the secure passkey protected conenction and communication between an esp32 server and an esp32 client.
+ * Please note that esp32 stores auth info in nvs memory. After a successful connection it is possible that a passkey change will be ineffective.
+ * To avoid this clear the memory of the esp32's between security testings. esptool.py is capable of this, example: esptool.py --port /dev/ttyUSB0 erase_flash.
+ *
+ * Created: on Jan 08 2021
+ * Author: mblasee
+ */
+
+#include
+
+class ClientCallbacks : public NimBLEClientCallbacks
+{
+ uint32_t onPassKeyRequest()
+ {
+ Serial.println("Client Passkey Request");
+ /** return the passkey to send to the server */
+ /** Change this to be different from NimBLE_Secure_Server if you want to test what happens on key mismatch */
+ return 123456;
+ };
+};
+static ClientCallbacks clientCB;
+
+void setup()
+{
+ Serial.begin(115200);
+ Serial.println("Starting NimBLE Client");
+
+ NimBLEDevice::init("");
+ NimBLEDevice::setPower(ESP_PWR_LVL_P9);
+ NimBLEDevice::setSecurityAuth(true, true, true);
+ NimBLEDevice::setSecurityIOCap(BLE_HS_IO_KEYBOARD_ONLY);
+ NimBLEScan *pScan = NimBLEDevice::getScan();
+ NimBLEScanResults results = pScan->start(5);
+
+ NimBLEUUID serviceUuid("ABCD");
+
+ for (int i = 0; i < results.getCount(); i++)
+ {
+ NimBLEAdvertisedDevice device = results.getDevice(i);
+ Serial.println(device.getName().c_str());
+
+ if (device.isAdvertisingService(serviceUuid))
+ {
+ NimBLEClient *pClient = NimBLEDevice::createClient();
+ pClient->setClientCallbacks(&clientCB, false);
+
+ if (pClient->connect(&device))
+ {
+ pClient->secureConnection();
+ NimBLERemoteService *pService = pClient->getService(serviceUuid);
+ if (pService != nullptr)
+ {
+ NimBLERemoteCharacteristic *pNonSecureCharacteristic = pService->getCharacteristic("1234");
+
+ if (pNonSecureCharacteristic != nullptr)
+ {
+ // Testing to read a non secured characteristic, you should be able to read this even if you have mismatching passkeys.
+ std::string value = pNonSecureCharacteristic->readValue();
+ // print or do whatever you need with the value
+ Serial.println(value.c_str());
+ }
+
+ NimBLERemoteCharacteristic *pSecureCharacteristic = pService->getCharacteristic("1235");
+
+ if (pSecureCharacteristic != nullptr)
+ {
+ // Testing to read a secured characteristic, you should be able to read this only if you have matching passkeys, otherwise you should
+ // get an error like this. E NimBLERemoteCharacteristic: "<< readValue rc=261"
+ // This means you are trying to do something without the proper permissions.
+ std::string value = pSecureCharacteristic->readValue();
+ // print or do whatever you need with the value
+ Serial.println(value.c_str());
+ }
+ }
+ }
+ else
+ {
+ // failed to connect
+ Serial.println("failed to connect");
+ }
+
+ NimBLEDevice::deleteClient(pClient);
+ }
+ }
+}
+
+void loop()
+{
+}
diff --git a/lib/libesp32/NimBLE-Arduino/examples/NimBLE_Secure_Server/NimBLE_Secure_Server.ino b/lib/libesp32/NimBLE-Arduino/examples/NimBLE_Secure_Server/NimBLE_Secure_Server.ino
new file mode 100644
index 000000000..f63cbc162
--- /dev/null
+++ b/lib/libesp32/NimBLE-Arduino/examples/NimBLE_Secure_Server/NimBLE_Secure_Server.ino
@@ -0,0 +1,37 @@
+/** NimBLE_Secure_Server Demo:
+ *
+ * This example demonstrates the secure passkey protected conenction and communication between an esp32 server and an esp32 client.
+ * Please note that esp32 stores auth info in nvs memory. After a successful connection it is possible that a passkey change will be ineffective.
+ * To avoid this clear the memory of the esp32's between security testings. esptool.py is capable of this, example: esptool.py --port /dev/ttyUSB0 erase_flash.
+ *
+ * Created: on Jan 08 2021
+ * Author: mblasee
+ */
+
+#include
+
+void setup() {
+ Serial.begin(115200);
+ Serial.println("Starting NimBLE Server");
+ NimBLEDevice::init("NimBLE");
+ NimBLEDevice::setPower(ESP_PWR_LVL_P9);
+
+ NimBLEDevice::setSecurityAuth(true, true, true);
+ NimBLEDevice::setSecurityPasskey(123456);
+ NimBLEDevice::setSecurityIOCap(BLE_HS_IO_DISPLAY_ONLY);
+ NimBLEServer *pServer = NimBLEDevice::createServer();
+ NimBLEService *pService = pServer->createService("ABCD");
+ NimBLECharacteristic *pNonSecureCharacteristic = pService->createCharacteristic("1234", NIMBLE_PROPERTY::READ );
+ NimBLECharacteristic *pSecureCharacteristic = pService->createCharacteristic("1235", NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::READ_ENC | NIMBLE_PROPERTY::READ_AUTHEN);
+
+ pService->start();
+ pNonSecureCharacteristic->setValue("Hello Non Secure BLE");
+ pSecureCharacteristic->setValue("Hello Secure BLE");
+
+ NimBLEAdvertising *pAdvertising = NimBLEDevice::getAdvertising();
+ pAdvertising->addServiceUUID("ABCD");
+ pAdvertising->start();
+}
+
+void loop() {
+}
diff --git a/lib/libesp32/NimBLE-Arduino/examples/Refactored_original_examples/BLE_notify/BLE_notify.ino b/lib/libesp32/NimBLE-Arduino/examples/Refactored_original_examples/BLE_notify/BLE_notify.ino
index 83f129b83..cb0488819 100644
--- a/lib/libesp32/NimBLE-Arduino/examples/Refactored_original_examples/BLE_notify/BLE_notify.ino
+++ b/lib/libesp32/NimBLE-Arduino/examples/Refactored_original_examples/BLE_notify/BLE_notify.ino
@@ -116,9 +116,9 @@ void setup() {
BLEAdvertising *pAdvertising = BLEDevice::getAdvertising();
pAdvertising->addServiceUUID(SERVICE_UUID);
pAdvertising->setScanResponse(false);
- /**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
- */
+ /** Note, this could be left out as that is the default value */
+ 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/lib/libesp32/NimBLE-Arduino/examples/Refactored_original_examples/BLE_server/BLE_server.ino b/lib/libesp32/NimBLE-Arduino/examples/Refactored_original_examples/BLE_server/BLE_server.ino
index 652d77685..faa4d88ea 100644
--- a/lib/libesp32/NimBLE-Arduino/examples/Refactored_original_examples/BLE_server/BLE_server.ino
+++ b/lib/libesp32/NimBLE-Arduino/examples/Refactored_original_examples/BLE_server/BLE_server.ino
@@ -44,10 +44,9 @@ void setup() {
BLEAdvertising *pAdvertising = BLEDevice::getAdvertising();
pAdvertising->addServiceUUID(SERVICE_UUID);
pAdvertising->setScanResponse(true);
- /**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);
- */
+ pAdvertising->setMinPreferred(0x06); // functions that help with iPhone connections issue
+ pAdvertising->setMaxPreferred(0x12);
+
BLEDevice::startAdvertising();
Serial.println("Characteristic defined! Now you can read it in your phone!");
}
diff --git a/lib/libesp32/NimBLE-Arduino/examples/Refactored_original_examples/BLE_server_multiconnect/BLE_server_multiconnect.ino b/lib/libesp32/NimBLE-Arduino/examples/Refactored_original_examples/BLE_server_multiconnect/BLE_server_multiconnect.ino
index 2ec38c481..9ae3859c7 100644
--- a/lib/libesp32/NimBLE-Arduino/examples/Refactored_original_examples/BLE_server_multiconnect/BLE_server_multiconnect.ino
+++ b/lib/libesp32/NimBLE-Arduino/examples/Refactored_original_examples/BLE_server_multiconnect/BLE_server_multiconnect.ino
@@ -120,9 +120,9 @@ void setup() {
BLEAdvertising *pAdvertising = BLEDevice::getAdvertising();
pAdvertising->addServiceUUID(SERVICE_UUID);
pAdvertising->setScanResponse(false);
- /**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
- */
+ /** Note, this could be left out as that is the default value */
+ 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/lib/libesp32/NimBLE-Arduino/library.properties b/lib/libesp32/NimBLE-Arduino/library.properties
index 156c098c0..7f2508333 100644
--- a/lib/libesp32/NimBLE-Arduino/library.properties
+++ b/lib/libesp32/NimBLE-Arduino/library.properties
@@ -1,5 +1,5 @@
name=NimBLE-Arduino
-version=1.0.2
+version=1.1.0
author=h2zero
maintainer=h2zero
sentence=Bluetooth low energy (BLE) library for arduino-esp32 based on NimBLE.
diff --git a/lib/libesp32/NimBLE-Arduino/src/FreeRTOS.cpp b/lib/libesp32/NimBLE-Arduino/src/FreeRTOS.cpp
index 4ebab3958..1c398cbf3 100644
--- a/lib/libesp32/NimBLE-Arduino/src/FreeRTOS.cpp
+++ b/lib/libesp32/NimBLE-Arduino/src/FreeRTOS.cpp
@@ -264,10 +264,14 @@ void FreeRTOS::Semaphore::setName(std::string name) {
* @param [in] length The amount of storage to allocate for the ring buffer.
* @param [in] type The type of buffer. One of RINGBUF_TYPE_NOSPLIT, RINGBUF_TYPE_ALLOWSPLIT, RINGBUF_TYPE_BYTEBUF.
*/
-#if defined(ESP_IDF_VERSION) && !defined(ESP_IDF_VERSION_VAL) //Quick hack to detect if using IDF version that replaced ringbuf_type_t, ESP_IDF_VERSION_VAL is for IDF>4.0.0
-Ringbuffer::Ringbuffer(size_t length, RingbufferType_t type) {
+#ifdef ESP_IDF_VERSION //Quick hack to detect if using IDF version that replaced ringbuf_type_t
+#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 0, 0)
+ Ringbuffer::Ringbuffer(size_t length, RingbufferType_t type) {
#else
-Ringbuffer::Ringbuffer(size_t length, ringbuf_type_t type) {
+ Ringbuffer::Ringbuffer(size_t length, ringbuf_type_t type) {
+#endif
+#else
+ Ringbuffer::Ringbuffer(size_t length, ringbuf_type_t type) {
#endif
m_handle = ::xRingbufferCreate(length, type);
} // Ringbuffer
diff --git a/lib/libesp32/NimBLE-Arduino/src/FreeRTOS.h b/lib/libesp32/NimBLE-Arduino/src/FreeRTOS.h
index f93f0b1a0..fa33921fe 100644
--- a/lib/libesp32/NimBLE-Arduino/src/FreeRTOS.h
+++ b/lib/libesp32/NimBLE-Arduino/src/FreeRTOS.h
@@ -68,8 +68,12 @@ public:
*/
class Ringbuffer {
public:
-#if defined(ESP_IDF_VERSION) && !defined(ESP_IDF_VERSION_VAL) //Quick hack to detect if using IDF version that replaced ringbuf_type_t, ESP_IDF_VERSION_VAL is for IDF>4.0.0
+#ifdef ESP_IDF_VERSION //Quick hack to detect if using IDF version that replaced ringbuf_type_t
+#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 0, 0)
Ringbuffer(size_t length, RingbufferType_t type = RINGBUF_TYPE_NOSPLIT);
+#else
+ Ringbuffer(size_t length, ringbuf_type_t type = RINGBUF_TYPE_NOSPLIT);
+#endif
#else
Ringbuffer(size_t length, ringbuf_type_t type = RINGBUF_TYPE_NOSPLIT);
#endif
diff --git a/lib/libesp32/NimBLE-Arduino/src/NimBLE2904.cpp b/lib/libesp32/NimBLE-Arduino/src/NimBLE2904.cpp
index d85cd87e4..80318b5b8 100644
--- a/lib/libesp32/NimBLE-Arduino/src/NimBLE2904.cpp
+++ b/lib/libesp32/NimBLE-Arduino/src/NimBLE2904.cpp
@@ -37,7 +37,7 @@ NimBLE2904::NimBLE2904(NimBLECharacteristic* pCharacterisitic)
m_data.m_unit = 0;
m_data.m_description = 0;
setValue((uint8_t*) &m_data, sizeof(m_data));
-} // BLE2902
+} // BLE2904
/**
diff --git a/lib/libesp32/NimBLE-Arduino/src/NimBLEAdvertising.cpp b/lib/libesp32/NimBLE-Arduino/src/NimBLEAdvertising.cpp
index 36bdbf9e9..a10480410 100644
--- a/lib/libesp32/NimBLE-Arduino/src/NimBLEAdvertising.cpp
+++ b/lib/libesp32/NimBLE-Arduino/src/NimBLEAdvertising.cpp
@@ -32,7 +32,7 @@ static const char* LOG_TAG = "NimBLEAdvertising";
/**
* @brief Construct a default advertising object.
*/
-NimBLEAdvertising::NimBLEAdvertising() {
+NimBLEAdvertising::NimBLEAdvertising() : m_slaveItvl() {
memset(&m_advData, 0, sizeof m_advData);
memset(&m_scanData, 0, sizeof m_scanData);
memset(&m_advParams, 0, sizeof m_advParams);
@@ -41,15 +41,20 @@ NimBLEAdvertising::NimBLEAdvertising() {
m_advData.name = (uint8_t *)name;
m_advData.name_len = strlen(name);
m_advData.name_is_complete = 1;
- m_scanData.tx_pwr_lvl_is_present = 1;
- m_scanData.tx_pwr_lvl = NimBLEDevice::getPower();
+ m_advData.tx_pwr_lvl_is_present = 1;
+ m_advData.tx_pwr_lvl = NimBLEDevice::getPower();
m_advData.flags = (BLE_HS_ADV_F_DISC_GEN | BLE_HS_ADV_F_BREDR_UNSUP);
m_advData.appearance = 0;
m_advData.appearance_is_present = 0;
m_advData.mfg_data_len = 0;
m_advData.mfg_data = nullptr;
+ m_advData.slave_itvl_range = nullptr;
+#if !defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
+ m_advParams.conn_mode = BLE_GAP_CONN_MODE_NON;
+#else
m_advParams.conn_mode = BLE_GAP_CONN_MODE_UND;
+#endif
m_advParams.disc_mode = BLE_GAP_DISC_MODE_GEN;
m_advParams.itvl_min = 0;
m_advParams.itvl_max = 0;
@@ -58,6 +63,8 @@ NimBLEAdvertising::NimBLEAdvertising() {
m_customScanResponseData = false;
m_scanResp = true;
m_advDataSet = false;
+ // Set this to non-zero to prevent auto start if host reset before started by app.
+ m_duration = BLE_HS_FOREVER;
} // NimBLEAdvertising
@@ -86,7 +93,6 @@ void NimBLEAdvertising::addServiceUUID(const char* serviceUUID) {
* @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);
@@ -112,11 +118,9 @@ void NimBLEAdvertising::setAppearance(uint16_t appearance) {
/**
* @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
+ * * BLE_GAP_CONN_MODE_NON (0) - not connectable advertising
+ * * BLE_GAP_CONN_MODE_DIR (1) - directed connectable advertising
+ * * BLE_GAP_CONN_MODE_UND (2) - undirected connectable advertising
*/
void NimBLEAdvertising::setAdvertisementType(uint8_t adv_type){
m_advParams.conn_mode = adv_type;
@@ -141,6 +145,64 @@ void NimBLEAdvertising::setMaxInterval(uint16_t maxinterval) {
} // setMaxInterval
+/**
+ * @brief Set the advertised min connection interval preferred by this device.
+ * @param [in] mininterval the max interval value. Range = 0x0006 to 0x0C80.
+ * @details Values not within the range will cancel advertising of this data.\n
+ * Consumes 6 bytes of advertising space (combined with max interval).
+ */
+void NimBLEAdvertising::setMinPreferred(uint16_t mininterval) {
+ // invalid paramters, set the slave interval to null
+ if(mininterval < 0x0006 || mininterval > 0x0C80) {
+ m_advData.slave_itvl_range = nullptr;
+ return;
+ }
+
+ if(m_advData.slave_itvl_range == nullptr) {
+ m_advData.slave_itvl_range = m_slaveItvl;
+ }
+
+ m_slaveItvl[0] = mininterval;
+ m_slaveItvl[1] = mininterval >> 8;
+
+ uint16_t maxinterval = *(uint16_t*)(m_advData.slave_itvl_range+2);
+
+ // If mininterval is higher than the maxinterval make them the same
+ if(mininterval > maxinterval) {
+ m_slaveItvl[2] = m_slaveItvl[0];
+ m_slaveItvl[3] = m_slaveItvl[1];
+ }
+} // setMinPreferred
+
+
+/**
+ * @brief Set the advertised max connection interval preferred by this device.
+ * @param [in] maxinterval the max interval value. Range = 0x0006 to 0x0C80.
+ * @details Values not within the range will cancel advertising of this data.\n
+ * Consumes 6 bytes of advertising space (combined with min interval).
+ */
+void NimBLEAdvertising::setMaxPreferred(uint16_t maxinterval) {
+ // invalid paramters, set the slave interval to null
+ if(maxinterval < 0x0006 || maxinterval > 0x0C80) {
+ m_advData.slave_itvl_range = nullptr;
+ return;
+ }
+ if(m_advData.slave_itvl_range == nullptr) {
+ m_advData.slave_itvl_range = m_slaveItvl;
+ }
+ m_slaveItvl[2] = maxinterval;
+ m_slaveItvl[3] = maxinterval >> 8;
+
+ uint16_t mininterval = *(uint16_t*)(m_advData.slave_itvl_range);
+
+ // If mininterval is higher than the maxinterval make them the same
+ if(mininterval > maxinterval) {
+ m_slaveItvl[0] = m_slaveItvl[2];
+ m_slaveItvl[1] = m_slaveItvl[3];
+ }
+} // setMaxPreferred
+
+
/**
* @brief Set if scan response is available.
* @param [in] set true = scan response available.
@@ -156,7 +218,8 @@ void NimBLEAdvertising::setScanResponse(bool set) {
* @param [in] connectWhitelistOnly If true, only allow connections from those on the white list.
*/
void NimBLEAdvertising::setScanFilter(bool scanRequestWhitelistOnly, bool connectWhitelistOnly) {
- NIMBLE_LOGD(LOG_TAG, ">> setScanFilter: scanRequestWhitelistOnly: %d, connectWhitelistOnly: %d", scanRequestWhitelistOnly, connectWhitelistOnly);
+ NIMBLE_LOGD(LOG_TAG, ">> setScanFilter: scanRequestWhitelistOnly: %d, connectWhitelistOnly: %d",
+ scanRequestWhitelistOnly, connectWhitelistOnly);
if (!scanRequestWhitelistOnly && !connectWhitelistOnly) {
m_advParams.filter_policy = BLE_HCI_ADV_FILT_NONE;
NIMBLE_LOGD(LOG_TAG, "<< setScanFilter");
@@ -194,7 +257,8 @@ void NimBLEAdvertising::setAdvertisementData(NimBLEAdvertisementData& advertisem
(uint8_t*)advertisementData.getPayload().data(),
advertisementData.getPayload().length());
if (rc != 0) {
- NIMBLE_LOGE(LOG_TAG, "ble_gap_adv_set_data: %d %s", rc, NimBLEUtils::returnCodeToString(rc));
+ NIMBLE_LOGE(LOG_TAG, "ble_gap_adv_set_data: %d %s",
+ rc, NimBLEUtils::returnCodeToString(rc));
}
m_customAdvData = true; // Set the flag that indicates we are using custom advertising data.
NIMBLE_LOGD(LOG_TAG, "<< setAdvertisementData");
@@ -213,7 +277,8 @@ void NimBLEAdvertising::setScanResponseData(NimBLEAdvertisementData& advertiseme
(uint8_t*)advertisementData.getPayload().data(),
advertisementData.getPayload().length());
if (rc != 0) {
- NIMBLE_LOGE(LOG_TAG, "ble_gap_adv_rsp_set_data: %d %s", rc, NimBLEUtils::returnCodeToString(rc));
+ NIMBLE_LOGE(LOG_TAG, "ble_gap_adv_rsp_set_data: %d %s",
+ rc, NimBLEUtils::returnCodeToString(rc));
}
m_customScanResponseData = true; // Set the flag that indicates we are using custom scan response data.
NIMBLE_LOGD(LOG_TAG, "<< setScanResponseData");
@@ -225,13 +290,14 @@ void NimBLEAdvertising::setScanResponseData(NimBLEAdvertisementData& advertiseme
* @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(uint32_t duration, void (*advCompleteCB)(NimBLEAdvertising *pAdv)) {
- NIMBLE_LOGD(LOG_TAG, ">> Advertising start: customAdvData: %d, customScanResponseData: %d", m_customAdvData, m_customScanResponseData);
+bool 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.
if(!NimBLEDevice::m_synced) {
NIMBLE_LOGE(LOG_TAG, "Host reset, wait for sync.");
- return;
+ return false;
}
#if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
@@ -240,17 +306,21 @@ void NimBLEAdvertising::start(uint32_t duration, void (*advCompleteCB)(NimBLEAdv
if(!pServer->m_gattsStarted){
pServer->start();
} else if(pServer->getConnectedCount() >= NIMBLE_MAX_CONNECTIONS) {
- NIMBLE_LOGW(LOG_TAG, "Max connections reached - not advertising");
- return;
+ NIMBLE_LOGE(LOG_TAG, "Max connections reached - not advertising");
+ return false;
}
}
#endif
// If already advertising just return
if(ble_gap_adv_active()) {
- return;
+ NIMBLE_LOGW(LOG_TAG, "Advertising already active");
+ return false;
}
+ // Save the duration incase of host reset so we can restart with the same params
+ m_duration = duration;
+
if(duration == 0){
duration = BLE_HS_FOREVER;
}
@@ -260,16 +330,31 @@ void NimBLEAdvertising::start(uint32_t duration, void (*advCompleteCB)(NimBLEAdv
m_advCompCB = advCompleteCB;
+ m_advParams.disc_mode = BLE_GAP_DISC_MODE_GEN;
+ m_advData.flags = (BLE_HS_ADV_F_DISC_GEN | BLE_HS_ADV_F_BREDR_UNSUP);
+ if(m_advParams.conn_mode == BLE_GAP_CONN_MODE_NON) {
+ if(!m_scanResp) {
+ m_advParams.disc_mode = BLE_GAP_DISC_MODE_NON;
+ m_advData.flags = BLE_HS_ADV_F_BREDR_UNSUP;
+ }
+ }
+
int rc = 0;
if (!m_customAdvData && !m_advDataSet) {
//start with 3 bytes for the flags data
- uint8_t payloadLen = 3;
+ uint8_t payloadLen = (2 + 1);
+ if(m_advData.appearance_is_present)
+ payloadLen += (2 + BLE_HS_ADV_APPEARANCE_LEN);
+ if(m_advData.tx_pwr_lvl_is_present)
+ payloadLen += (2 + 1);
+ if(m_advData.slave_itvl_range != nullptr)
+ payloadLen += (2 + 4);
for(auto &it : m_serviceUUIDs) {
if(it.getNative()->u.type == BLE_UUID_TYPE_16) {
int add = (m_advData.num_uuids16 > 0) ? 2 : 4;
- if((payloadLen + add) > 31){
+ if((payloadLen + add) > BLE_HS_ADV_MAX_SZ){
m_advData.uuids16_is_complete = 0;
continue;
}
@@ -278,7 +363,7 @@ void NimBLEAdvertising::start(uint32_t duration, void (*advCompleteCB)(NimBLEAdv
if(nullptr == (m_advData.uuids16 = (ble_uuid16_t*)realloc(m_advData.uuids16,
(m_advData.num_uuids16 + 1) * sizeof(ble_uuid16_t))))
{
- NIMBLE_LOGE(LOG_TAG, "Error, no mem");
+ NIMBLE_LOGC(LOG_TAG, "Error, no mem");
abort();
}
memcpy(&m_advData.uuids16[m_advData.num_uuids16].value,
@@ -290,7 +375,7 @@ void NimBLEAdvertising::start(uint32_t duration, void (*advCompleteCB)(NimBLEAdv
}
if(it.getNative()->u.type == BLE_UUID_TYPE_32) {
int add = (m_advData.num_uuids32 > 0) ? 4 : 6;
- if((payloadLen + add) > 31){
+ if((payloadLen + add) > BLE_HS_ADV_MAX_SZ){
m_advData.uuids32_is_complete = 0;
continue;
}
@@ -299,7 +384,7 @@ void NimBLEAdvertising::start(uint32_t duration, void (*advCompleteCB)(NimBLEAdv
if(nullptr == (m_advData.uuids32 = (ble_uuid32_t*)realloc(m_advData.uuids32,
(m_advData.num_uuids32 + 1) * sizeof(ble_uuid32_t))))
{
- NIMBLE_LOGE(LOG_TAG, "Error, no mem");
+ NIMBLE_LOGC(LOG_TAG, "Error, no mem");
abort();
}
memcpy(&m_advData.uuids32[m_advData.num_uuids32].value,
@@ -311,7 +396,7 @@ void NimBLEAdvertising::start(uint32_t duration, void (*advCompleteCB)(NimBLEAdv
}
if(it.getNative()->u.type == BLE_UUID_TYPE_128){
int add = (m_advData.num_uuids128 > 0) ? 16 : 18;
- if((payloadLen + add) > 31){
+ if((payloadLen + add) > BLE_HS_ADV_MAX_SZ){
m_advData.uuids128_is_complete = 0;
continue;
}
@@ -320,7 +405,7 @@ void NimBLEAdvertising::start(uint32_t duration, void (*advCompleteCB)(NimBLEAdv
if(nullptr == (m_advData.uuids128 = (ble_uuid128_t*)realloc(m_advData.uuids128,
(m_advData.num_uuids128 + 1) * sizeof(ble_uuid128_t))))
{
- NIMBLE_LOGE(LOG_TAG, "Error, no mem");
+ NIMBLE_LOGC(LOG_TAG, "Error, no mem");
abort();
}
memcpy(&m_advData.uuids128[m_advData.num_uuids128].value,
@@ -333,54 +418,74 @@ void NimBLEAdvertising::start(uint32_t duration, void (*advCompleteCB)(NimBLEAdv
}
// check if there is room for the name, if not put it in scan data
- if((payloadLen + m_advData.name_len) > 29) {
+ if((payloadLen + (2 + m_advData.name_len)) > BLE_HS_ADV_MAX_SZ) {
if(m_scanResp){
m_scanData.name = m_advData.name;
m_scanData.name_len = m_advData.name_len;
- m_scanData.name_is_complete = m_advData.name_is_complete;
+ if(m_scanData.name_len > BLE_HS_ADV_MAX_SZ - 2) {
+ m_scanData.name_len = BLE_HS_ADV_MAX_SZ - 2;
+ m_scanData.name_is_complete = 0;
+ } else {
+ m_scanData.name_is_complete = 1;
+ }
m_advData.name = nullptr;
m_advData.name_len = 0;
+ m_advData.name_is_complete = 0;
} else {
+ if(m_advData.tx_pwr_lvl_is_present) {
+ m_advData.tx_pwr_lvl = 0;
+ m_advData.tx_pwr_lvl_is_present = 0;
+ payloadLen -= (2 + 1);
+ }
// if not using scan response just cut the name down
// leaving 2 bytes for the data specifier.
- m_advData.name_len = (29 - payloadLen);
+ if(m_advData.name_len > (BLE_HS_ADV_MAX_SZ - payloadLen - 2)) {
+ m_advData.name_len = (BLE_HS_ADV_MAX_SZ - payloadLen - 2);
+ m_advData.name_is_complete = 0;
+ }
}
- m_advData.name_is_complete = 0;
- }
-
- if(m_advData.name_len > 0) {
- payloadLen += (m_advData.name_len + 2);
}
if(m_scanResp) {
- // name length + type byte + length byte + tx power type + length + data
- if((m_scanData.name_len + 5) > 31) {
- // prioritize name data over tx power
- m_scanData.tx_pwr_lvl_is_present = 0;
- m_scanData.tx_pwr_lvl = 0;
- // limit name to 29 to leave room for the data specifiers
- if(m_scanData.name_len > 29) {
- m_scanData.name_len = 29;
- m_scanData.name_is_complete = false;
- }
- }
-
rc = ble_gap_adv_rsp_set_fields(&m_scanData);
- if (rc != 0) {
- NIMBLE_LOGC(LOG_TAG, "error setting scan response data; rc=%d, %s", rc, NimBLEUtils::returnCodeToString(rc));
- abort();
+ switch(rc) {
+ case 0:
+ break;
+
+ case BLE_HS_EBUSY:
+ NIMBLE_LOGE(LOG_TAG, "Already advertising");
+ break;
+
+ case BLE_HS_EMSGSIZE:
+ NIMBLE_LOGE(LOG_TAG, "Scan data too long");
+ break;
+
+ default:
+ NIMBLE_LOGE(LOG_TAG, "Error setting scan response data; rc=%d, %s",
+ rc, NimBLEUtils::returnCodeToString(rc));
+ break;
}
- // if not using scan response and there is room,
- // put the tx power data into the advertisment
- } else if (payloadLen < 29) {
- m_advData.tx_pwr_lvl_is_present = 1;
- m_advData.tx_pwr_lvl = NimBLEDevice::getPower();
}
- rc = ble_gap_adv_set_fields(&m_advData);
- if (rc != 0) {
- NIMBLE_LOGC(LOG_TAG, "error setting advertisement data; rc=%d, %s", rc, NimBLEUtils::returnCodeToString(rc));
- abort();
+ if(rc == 0) {
+ rc = ble_gap_adv_set_fields(&m_advData);
+ switch(rc) {
+ case 0:
+ break;
+
+ case BLE_HS_EBUSY:
+ NIMBLE_LOGE(LOG_TAG, "Already advertising");
+ break;
+
+ case BLE_HS_EMSGSIZE:
+ NIMBLE_LOGE(LOG_TAG, "Advertisement data too long");
+ break;
+
+ default:
+ NIMBLE_LOGE(LOG_TAG, "Error setting advertisement data; rc=%d, %s",
+ rc, NimBLEUtils::returnCodeToString(rc));
+ break;
+ }
}
if(m_advData.num_uuids128 > 0) {
@@ -401,24 +506,54 @@ void NimBLEAdvertising::start(uint32_t duration, void (*advCompleteCB)(NimBLEAdv
m_advData.num_uuids16 = 0;
}
+ if(rc !=0) {
+ return false;
+ }
+
m_advDataSet = true;
}
#if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
- rc = ble_gap_adv_start(0, NULL, duration,
+ rc = ble_gap_adv_start(NimBLEDevice::m_own_addr_type, NULL, duration,
&m_advParams,
- (pServer != nullptr) ? NimBLEServer::handleGapEvent : NimBLEAdvertising::handleGapEvent,
+ (pServer != nullptr) ? NimBLEServer::handleGapEvent :
+ NimBLEAdvertising::handleGapEvent,
(pServer != nullptr) ? (void*)pServer : (void*)this);
#else
- rc = ble_gap_adv_start(0, NULL, duration,
+ rc = ble_gap_adv_start(NimBLEDevice::m_own_addr_type, 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));
- abort();
+ switch(rc) {
+ case 0:
+ break;
+
+ case BLE_HS_EINVAL:
+ NIMBLE_LOGE(LOG_TAG, "Unable to advertise - Duration too long");
+ break;
+
+ case BLE_HS_EPREEMPTED:
+ NIMBLE_LOGE(LOG_TAG, "Unable to advertise - busy");
+ break;
+
+ case BLE_HS_ETIMEOUT_HCI:
+ case BLE_HS_EOS:
+ case BLE_HS_ECONTROLLER:
+ case BLE_HS_ENOTSYNCED:
+ NIMBLE_LOGE(LOG_TAG, "Unable to advertise - Host Reset");
+ break;
+
+ default:
+ NIMBLE_LOGE(LOG_TAG, "Error enabling advertising; rc=%d, %s",
+ rc, NimBLEUtils::returnCodeToString(rc));
+ break;
+ }
+
+ if(rc != 0) {
+ return false;
}
NIMBLE_LOGD(LOG_TAG, "<< Advertising start");
+ return true;
} // start
@@ -427,9 +562,11 @@ void NimBLEAdvertising::start(uint32_t duration, void (*advCompleteCB)(NimBLEAdv
*/
void NimBLEAdvertising::stop() {
NIMBLE_LOGD(LOG_TAG, ">> stop");
+
int rc = ble_gap_adv_stop();
if (rc != 0 && rc != BLE_HS_EALREADY) {
- NIMBLE_LOGE(LOG_TAG, "ble_gap_adv_stop rc=%d %s", rc, NimBLEUtils::returnCodeToString(rc));
+ NIMBLE_LOGE(LOG_TAG, "ble_gap_adv_stop rc=%d %s",
+ rc, NimBLEUtils::returnCodeToString(rc));
return;
}
@@ -460,8 +597,17 @@ bool NimBLEAdvertising::isAdvertising() {
* Host reset seems to clear advertising data,
* we need clear the flag so it reloads it.
*/
-void NimBLEAdvertising::onHostReset() {
+void NimBLEAdvertising::onHostSync() {
+ NIMBLE_LOGD(LOG_TAG, "Host re-synced");
+
m_advDataSet = false;
+ // If we were advertising forever, restart it now
+ if(m_duration == 0) {
+ start(m_duration, m_advCompCB);
+ } else {
+ // Otherwise we should tell the app that advertising stopped.
+ advCompleteCB();
+ }
}
@@ -475,6 +621,19 @@ int NimBLEAdvertising::handleGapEvent(struct ble_gap_event *event, void *arg) {
NimBLEAdvertising *pAdv = (NimBLEAdvertising*)arg;
if(event->type == BLE_GAP_EVENT_ADV_COMPLETE) {
+ switch(event->adv_complete.reason) {
+ // Don't call the callback if host reset, we want to
+ // preserve the active flag until re-sync to restart advertising.
+ case BLE_HS_ETIMEOUT_HCI:
+ case BLE_HS_EOS:
+ case BLE_HS_ECONTROLLER:
+ case BLE_HS_ENOTSYNCED:
+ NIMBLE_LOGC(LOG_TAG, "host reset, rc=%d", event->adv_complete.reason);
+ NimBLEDevice::onReset(event->adv_complete.reason);
+ return 0;
+ default:
+ break;
+ }
pAdv->advCompleteCB();
}
return 0;
diff --git a/lib/libesp32/NimBLE-Arduino/src/NimBLEAdvertising.h b/lib/libesp32/NimBLE-Arduino/src/NimBLEAdvertising.h
index 2fab71004..0d97ecbbc 100644
--- a/lib/libesp32/NimBLE-Arduino/src/NimBLEAdvertising.h
+++ b/lib/libesp32/NimBLE-Arduino/src/NimBLEAdvertising.h
@@ -77,7 +77,7 @@ public:
void addServiceUUID(const NimBLEUUID &serviceUUID);
void addServiceUUID(const char* serviceUUID);
void removeServiceUUID(const NimBLEUUID &serviceUUID);
- void start(uint32_t duration = 0, void (*advCompleteCB)(NimBLEAdvertising *pAdv) = nullptr);
+ bool start(uint32_t duration = 0, void (*advCompleteCB)(NimBLEAdvertising *pAdv) = nullptr);
void stop();
void setAppearance(uint16_t appearance);
void setAdvertisementType(uint8_t adv_type);
@@ -87,13 +87,15 @@ public:
void setScanFilter(bool scanRequestWhitelistOnly, bool connectWhitelistOnly);
void setScanResponseData(NimBLEAdvertisementData& advertisementData);
void setScanResponse(bool);
+ void setMinPreferred(uint16_t);
+ void setMaxPreferred(uint16_t);
void advCompleteCB();
bool isAdvertising();
private:
friend class NimBLEDevice;
- void onHostReset();
+ void onHostSync();
static int handleGapEvent(struct ble_gap_event *event, void *arg);
ble_hs_adv_fields m_advData;
@@ -104,8 +106,9 @@ private:
bool m_customScanResponseData;
bool m_scanResp;
bool m_advDataSet;
- void (*m_advCompCB)(NimBLEAdvertising *pAdv);
-
+ void (*m_advCompCB)(NimBLEAdvertising *pAdv);
+ uint8_t m_slaveItvl[4];
+ uint32_t m_duration;
};
#endif // #if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
diff --git a/lib/libesp32/NimBLE-Arduino/src/NimBLECharacteristic.cpp b/lib/libesp32/NimBLE-Arduino/src/NimBLECharacteristic.cpp
index 6f7d3d7e0..e9a5a49c9 100644
--- a/lib/libesp32/NimBLE-Arduino/src/NimBLECharacteristic.cpp
+++ b/lib/libesp32/NimBLE-Arduino/src/NimBLECharacteristic.cpp
@@ -473,9 +473,10 @@ void NimBLECharacteristic::setValue(const uint8_t* data, size_t length) {
return;
}
+ time_t t = time(nullptr);
portENTER_CRITICAL(&m_valMux);
m_value = std::string((char*)data, length);
- m_timestamp = time(nullptr);
+ m_timestamp = t;
portEXIT_CRITICAL(&m_valMux);
NIMBLE_LOGD(LOG_TAG, "<< setValue");
diff --git a/lib/libesp32/NimBLE-Arduino/src/NimBLEClient.cpp b/lib/libesp32/NimBLE-Arduino/src/NimBLEClient.cpp
index 539a7a016..ddeb1de70 100644
--- a/lib/libesp32/NimBLE-Arduino/src/NimBLEClient.cpp
+++ b/lib/libesp32/NimBLE-Arduino/src/NimBLEClient.cpp
@@ -24,6 +24,9 @@
#include
#include
+#include "nimble/nimble_port.h"
+
+
static const char* LOG_TAG = "NimBLEClient";
static NimBLEClientCallbacks defaultCallbacks;
@@ -56,11 +59,10 @@ static NimBLEClientCallbacks defaultCallbacks;
NimBLEClient::NimBLEClient(const NimBLEAddress &peerAddress) : m_peerAddress(peerAddress) {
m_pClientCallbacks = &defaultCallbacks;
m_conn_id = BLE_HS_CONN_HANDLE_NONE;
- m_isConnected = false;
- m_waitingToConnect = false;
m_connectTimeout = 30000;
m_deleteCallbacks = false;
m_pTaskData = nullptr;
+ m_connEstablished = false;
m_pConnParams.scan_itvl = 16; // Scan interval in 0.625ms units (NimBLE Default)
m_pConnParams.scan_window = 16; // Scan window in 0.625ms units (NimBLE Default)
@@ -70,6 +72,9 @@ NimBLEClient::NimBLEClient(const NimBLEAddress &peerAddress) : m_peerAddress(pee
m_pConnParams.supervision_timeout = BLE_GAP_INITIAL_SUPERVISION_TIMEOUT; // timeout = 400*10ms = 4000ms
m_pConnParams.min_ce_len = BLE_GAP_INITIAL_CONN_MIN_CE_LEN; // Minimum length of connection event in 0.625ms units
m_pConnParams.max_ce_len = BLE_GAP_INITIAL_CONN_MAX_CE_LEN; // Maximum length of connection event in 0.625ms units
+
+ ble_npl_callout_init(&m_dcTimer, nimble_port_get_dflt_eventq(),
+ NimBLEClient::dcTimerCb, this);
} // NimBLEClient
@@ -89,6 +94,20 @@ NimBLEClient::~NimBLEClient() {
} // ~NimBLEClient
+/**
+ * @brief If we have asked to disconnect and the event does not
+ * occur within the supervision timeout + added delay, this will
+ * be called to reset the host in the case of a stalled controller.
+ */
+void NimBLEClient::dcTimerCb(ble_npl_event *event) {
+ /* NimBLEClient *pClient = (NimBLEClient*)event->arg;
+ NIMBLE_LOGC(LOG_TAG, "Timed out disconnecting from %s - resetting host",
+ std::string(pClient->getPeerAddress()).c_str());
+ */
+ ble_hs_sched_reset(BLE_HS_ECONTROLLER);
+}
+
+
/**
* @brief Delete all service objects created by this client and clear the vector.
*/
@@ -164,70 +183,119 @@ bool NimBLEClient::connect(const NimBLEAddress &address, bool deleteAttibutes) {
return false;
}
- if(ble_gap_conn_active()) {
- NIMBLE_LOGE(LOG_TAG, "Connection in progress - must wait.");
+ if(isConnected() || m_connEstablished || m_pTaskData != nullptr) {
+ NIMBLE_LOGE(LOG_TAG, "Client busy, connected to %s, id=%d",
+ std::string(m_peerAddress).c_str(), getConnId());
return false;
}
- if(!NimBLEDevice::getScan()->stop()) {
+ ble_addr_t peerAddr_t;
+ memcpy(&peerAddr_t.val, address.getNative(),6);
+ peerAddr_t.type = address.getType();
+ if(ble_gap_conn_find_by_addr(&peerAddr_t, NULL) == 0) {
+ NIMBLE_LOGE(LOG_TAG, "A connection to %s already exists",
+ address.toString().c_str());
return false;
}
if(address == NimBLEAddress("")) {
NIMBLE_LOGE(LOG_TAG, "Invalid peer address;(NULL)");
return false;
- } else if(m_peerAddress != address) {
+ } else {
m_peerAddress = address;
}
- ble_addr_t peerAddrt;
- memcpy(&peerAddrt.val, m_peerAddress.getNative(),6);
- peerAddrt.type = m_peerAddress.getType();
-
ble_task_data_t taskData = {this, xTaskGetCurrentTaskHandle(), 0, nullptr};
m_pTaskData = &taskData;
-
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.
*/
- do{
- rc = ble_gap_connect(BLE_OWN_ADDR_PUBLIC, &peerAddrt, m_connectTimeout, &m_pConnParams,
- NimBLEClient::handleGapEvent, this);
- if(rc == BLE_HS_EBUSY) {
- vTaskDelay(1 / portTICK_PERIOD_MS);
- }
- }while(rc == BLE_HS_EBUSY);
+ do {
+ rc = ble_gap_connect(NimBLEDevice::m_own_addr_type, &peerAddr_t,
+ m_connectTimeout, &m_pConnParams,
+ NimBLEClient::handleGapEvent, this);
+ switch (rc) {
+ case 0:
+ break;
- if (rc != 0 && rc != BLE_HS_EDONE) {
- NIMBLE_LOGE(LOG_TAG, "Error: Failed to connect to device; "
- "addr=%s, rc=%d; %s",
- std::string(m_peerAddress).c_str(),
- rc, NimBLEUtils::returnCodeToString(rc));
+ case BLE_HS_EBUSY:
+ // Scan was still running, stop it and try again
+ if (!NimBLEDevice::getScan()->stop()) {
+ rc = BLE_HS_EUNKNOWN;
+ }
+ break;
+
+ case BLE_HS_EDONE:
+ // A connection to this device already exists, do not connect twice.
+ NIMBLE_LOGE(LOG_TAG, "Already connected to device; addr=%s",
+ std::string(m_peerAddress).c_str());
+ break;
+
+ case BLE_HS_EALREADY:
+ // Already attemting to connect to this device, cancel the previous
+ // attempt and report failure here so we don't get 2 connections.
+ NIMBLE_LOGE(LOG_TAG, "Already attempting to connect to %s - cancelling",
+ std::string(m_peerAddress).c_str());
+ ble_gap_conn_cancel();
+ break;
+
+ default:
+ NIMBLE_LOGE(LOG_TAG, "Failed to connect to %s, rc=%d; %s",
+ std::string(m_peerAddress).c_str(),
+ rc, NimBLEUtils::returnCodeToString(rc));
+ break;
+ }
+
+ } while (rc == BLE_HS_EBUSY);
+
+ if(rc != 0) {
m_pTaskData = nullptr;
- m_waitingToConnect = false;
return false;
}
- m_waitingToConnect = true;
+ // Wait for the connect timeout time +1 second for the connection to complete
+ if(ulTaskNotifyTake(pdTRUE, pdMS_TO_TICKS(m_connectTimeout + 1000)) == pdFALSE) {
+ m_pTaskData = nullptr;
+ // If a connection was made but no response from MTU exchange; disconnect
+ if(isConnected()) {
+ NIMBLE_LOGE(LOG_TAG, "Connect timeout - no response");
+ disconnect();
+ } else {
+ // workaround; if the controller doesn't cancel the connection
+ // at the timeout, cancel it here.
+ NIMBLE_LOGE(LOG_TAG, "Connect timeout - cancelling");
+ ble_gap_conn_cancel();
+ }
- // Wait for the connection to complete.
- ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
-
- if(taskData.rc != 0){
return false;
+
+ } else if(taskData.rc != 0){
+ NIMBLE_LOGE(LOG_TAG, "Connection failed; status=%d %s",
+ taskData.rc,
+ NimBLEUtils::returnCodeToString(taskData.rc));
+ // If the failure was not a result of a disconnection
+ // make sure we disconnect now to avoid dangling connections
+ if(isConnected()) {
+ disconnect();
+ }
+ return false;
+ } else {
+ NIMBLE_LOGI(LOG_TAG, "Connection established");
}
if(deleteAttibutes) {
deleteServices();
}
+ m_connEstablished = true;
m_pClientCallbacks->onConnect(this);
NIMBLE_LOGD(LOG_TAG, "<< connect()");
- return true;
+ // Check if still connected before returning
+ return isConnected();
} // connect
@@ -268,12 +336,39 @@ bool NimBLEClient::secureConnection() {
int NimBLEClient::disconnect(uint8_t reason) {
NIMBLE_LOGD(LOG_TAG, ">> disconnect()");
int rc = 0;
- if(m_isConnected){
- rc = ble_gap_terminate(m_conn_id, reason);
- if(rc != 0){
- NIMBLE_LOGE(LOG_TAG, "ble_gap_terminate failed: rc=%d %s", rc,
- NimBLEUtils::returnCodeToString(rc));
+ if(isConnected()) {
+ // If the timer was already started, ignore this call.
+ if(ble_npl_callout_is_active(&m_dcTimer)) {
+ NIMBLE_LOGI(LOG_TAG, "Already disconnecting, timer started");
+ return BLE_HS_EALREADY;
}
+
+ ble_gap_conn_desc desc;
+ if(ble_gap_conn_find(m_conn_id, &desc) != 0){
+ NIMBLE_LOGI(LOG_TAG, "Connection ID not found");
+ return BLE_HS_EALREADY;
+ }
+
+ // We use a timer to detect a controller error in the event that it does
+ // not inform the stack when disconnection is complete.
+ // This is a common error in certain esp-idf versions.
+ // The disconnect timeout time is the supervison timeout time + 1 second.
+ // In the case that the event happenss shortly after the supervision timeout
+ // we don't want to prematurely reset the host.
+ ble_npl_time_t ticks;
+ ble_npl_time_ms_to_ticks((desc.supervision_timeout + 100) * 10, &ticks);
+ ble_npl_callout_reset(&m_dcTimer, ticks);
+
+ rc = ble_gap_terminate(m_conn_id, reason);
+ if (rc != 0) {
+ if(rc != BLE_HS_EALREADY) {
+ ble_npl_callout_stop(&m_dcTimer);
+ }
+ NIMBLE_LOGE(LOG_TAG, "ble_gap_terminate failed: rc=%d %s",
+ rc, NimBLEUtils::returnCodeToString(rc));
+ }
+ } else {
+ NIMBLE_LOGD(LOG_TAG, "Not connected to any peers");
}
NIMBLE_LOGD(LOG_TAG, "<< disconnect()");
@@ -283,12 +378,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.
+ * @param [in] minInterval The minimum connection interval in 1.25ms units.
+ * @param [in] maxInterval The maximum connection interval in 1.25ms units.
+ * @param [in] latency The 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,
@@ -315,10 +410,10 @@ void NimBLEClient::setConnectionParams(uint16_t minInterval, uint16_t maxInterva
/**
* @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
+ * @param [in] minInterval The minimum connection interval in 1.25ms units.
+ * @param [in] maxInterval The maximum connection interval in 1.25ms units.
+ * @param [in] latency The 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)
@@ -454,6 +549,16 @@ NimBLERemoteService* NimBLEClient::getService(const NimBLEUUID &uuid) {
if(m_servicesVector.size() > prev_size) {
return m_servicesVector.back();
}
+
+ // If the request was successful but 16/32 bit service not found
+ // try again with the 128 bit uuid.
+ if(uuid.bitSize() == BLE_UUID_TYPE_16 ||
+ uuid.bitSize() == BLE_UUID_TYPE_32)
+ {
+ NimBLEUUID uuid128(uuid);
+ uuid128.to128();
+ return getService(uuid128);
+ }
}
NIMBLE_LOGD(LOG_TAG, "<< getService: not found");
@@ -510,7 +615,7 @@ bool NimBLEClient::retrieveServices(const NimBLEUUID *uuid_filter) {
NIMBLE_LOGD(LOG_TAG, ">> retrieveServices");
- if(!m_isConnected){
+ if(!isConnected()){
NIMBLE_LOGE(LOG_TAG, "Disconnected, could not retrieve services -aborting");
return false;
}
@@ -618,10 +723,11 @@ std::string NimBLEClient::getValue(const NimBLEUUID &serviceUUID, const NimBLEUU
* @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.
+ * @param [in] response If true, uses write with response operation.
* @returns true if successful otherwise false
*/
bool NimBLEClient::setValue(const NimBLEUUID &serviceUUID, const NimBLEUUID &characteristicUUID,
- const std::string &value)
+ const std::string &value, bool response)
{
NIMBLE_LOGD(LOG_TAG, ">> setValue: serviceUUID: %s, characteristicUUID: %s",
serviceUUID.toString().c_str(), characteristicUUID.toString().c_str());
@@ -632,7 +738,7 @@ bool NimBLEClient::setValue(const NimBLEUUID &serviceUUID, const NimBLEUUID &cha
if(pService != nullptr) {
NimBLERemoteCharacteristic* pChar = pService->getCharacteristic(characteristicUUID);
if(pChar != nullptr) {
- ret = pChar->writeValue(value);
+ ret = pChar->writeValue(value, response);
}
}
@@ -641,6 +747,31 @@ bool NimBLEClient::setValue(const NimBLEUUID &serviceUUID, const NimBLEUUID &cha
} // setValue
+/**
+ * @brief Get the remote characteristic with the specified handle.
+ * @param [in] handle The handle of the desired characteristic.
+ * @returns The matching remote characteristic, nullptr otherwise.
+ */
+NimBLERemoteCharacteristic* NimBLEClient::getCharacteristic(const uint16_t handle)
+{
+ NimBLERemoteService *pService = nullptr;
+ for(auto it = m_servicesVector.begin(); it != m_servicesVector.end(); ++it) {
+ if ((*it)->getStartHandle() <= handle && handle <= (*it)->getEndHandle()) {
+ pService = *it;
+ break;
+ }
+ }
+
+ if (pService != nullptr) {
+ for (auto it = pService->begin(); it != pService->end(); ++it) {
+ if ((*it)->getHandle() == handle) {
+ return *it;
+ }
+ }
+ }
+
+ return nullptr;
+}
/**
* @brief Get the current mtu of this connection.
@@ -656,7 +787,8 @@ uint16_t NimBLEClient::getMTU() {
* @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) {
+ /*STATIC*/
+ int NimBLEClient::handleGapEvent(struct ble_gap_event *event, void *arg) {
NimBLEClient* client = (NimBLEClient*)arg;
int rc;
@@ -665,61 +797,67 @@ uint16_t NimBLEClient::getMTU() {
switch(event->type) {
case BLE_GAP_EVENT_DISCONNECT: {
- if(!client->m_isConnected)
- return 0;
-
- if(client->m_conn_id != event->disconnect.conn.conn_handle)
- return 0;
-
- client->m_isConnected = false;
- client->m_waitingToConnect=false;
- // Remove the device from ignore list so we will scan it again
- NimBLEDevice::removeIgnored(client->m_peerAddress);
-
- NIMBLE_LOGI(LOG_TAG, "disconnect; reason=%d, %s", event->disconnect.reason,
- NimBLEUtils::returnCodeToString(event->disconnect.reason));
-
+ rc = event->disconnect.reason;
// If Host reset tell the device now before returning to prevent
// any errors caused by calling host functions before resyncing.
- switch(event->disconnect.reason) {
- case BLE_HS_ETIMEOUT_HCI:
- case BLE_HS_EOS:
+ switch(rc) {
case BLE_HS_ECONTROLLER:
+ case BLE_HS_ETIMEOUT_HCI:
case BLE_HS_ENOTSYNCED:
- NIMBLE_LOGC(LOG_TAG, "Disconnect - host reset, rc=%d", event->disconnect.reason);
- NimBLEDevice::onReset(event->disconnect.reason);
+ case BLE_HS_EOS:
+ NIMBLE_LOGC(LOG_TAG, "Disconnect - host reset, rc=%d", rc);
+ NimBLEDevice::onReset(rc);
break;
default:
+ // Check that the event is for this client.
+ if(client->m_conn_id != event->disconnect.conn.conn_handle) {
+ return 0;
+ }
break;
}
- //client->m_conn_id = BLE_HS_CONN_HANDLE_NONE;
+ // Stop the disconnect timer since we are now disconnected.
+ ble_npl_callout_stop(&client->m_dcTimer);
+
+ // Remove the device from ignore list so we will scan it again
+ NimBLEDevice::removeIgnored(client->m_peerAddress);
+
+ // No longer connected, clear the connection ID.
+ client->m_conn_id = BLE_HS_CONN_HANDLE_NONE;
+
+ // If we received a connected event but did not get established (no PDU)
+ // then a disconnect event will be sent but we should not send it to the
+ // app for processing. Instead we will ensure the task is released
+ // and report the error.
+ if(!client->m_connEstablished)
+ break;
+
+ NIMBLE_LOGI(LOG_TAG, "disconnect; reason=%d, %s",
+ rc, NimBLEUtils::returnCodeToString(rc));
+
+ client->m_connEstablished = false;
client->m_pClientCallbacks->onDisconnect(client);
- rc = event->disconnect.reason;
break;
} // BLE_GAP_EVENT_DISCONNECT
case BLE_GAP_EVENT_CONNECT: {
-
- if(!client->m_waitingToConnect)
+ // If we aren't waiting for this connection response
+ // we should drop the connection immediately.
+ if(client->isConnected() || client->m_pTaskData == nullptr) {
+ ble_gap_terminate(event->connect.conn_handle, BLE_ERR_REM_USER_CONN_TERM);
return 0;
+ }
- //if(client->m_conn_id != BLE_HS_CONN_HANDLE_NONE)
- // return 0;
-
- client->m_waitingToConnect=false;
-
- if (event->connect.status == 0) {
- client->m_isConnected = true;
-
- NIMBLE_LOGD(LOG_TAG, "Connection established");
+ rc = event->connect.status;
+ if (rc == 0) {
+ NIMBLE_LOGI(LOG_TAG, "Connected event");
client->m_conn_id = event->connect.conn_handle;
rc = ble_gattc_exchange_mtu(client->m_conn_id, NULL,NULL);
if(rc != 0) {
- NIMBLE_LOGE(LOG_TAG, "ble_gattc_exchange_mtu: rc=%d %s",rc,
- NimBLEUtils::returnCodeToString(rc));
+ NIMBLE_LOGE(LOG_TAG, "MTU exchange error; rc=%d %s",
+ rc, NimBLEUtils::returnCodeToString(rc));
break;
}
@@ -727,14 +865,10 @@ uint16_t NimBLEClient::getMTU() {
// scanning since we are already connected to it
NimBLEDevice::addIgnored(client->m_peerAddress);
} else {
- NIMBLE_LOGE(LOG_TAG, "Error: Connection failed; status=%d %s",
- event->connect.status,
- NimBLEUtils::returnCodeToString(event->connect.status));
-
- client->m_isConnected = false;
- rc = event->connect.status;
+ client->m_conn_id = BLE_HS_CONN_HANDLE_NONE;
break;
}
+
return 0;
} // BLE_GAP_EVENT_CONNECT
@@ -742,7 +876,14 @@ uint16_t NimBLEClient::getMTU() {
if(client->m_conn_id != event->notify_rx.conn_handle)
return 0;
- NIMBLE_LOGD(LOG_TAG, "Notify Recieved for handle: %d",event->notify_rx.attr_handle);
+ // If a notification comes before this flag is set we might
+ // access a vector while it is being cleared in connect()
+ if(!client->m_connEstablished) {
+ return 0;
+ }
+
+ NIMBLE_LOGD(LOG_TAG, "Notify Recieved for handle: %d",
+ event->notify_rx.attr_handle);
for(auto &it: client->m_servicesVector) {
// Dont waste cycles searching services without this handle in its range
@@ -752,8 +893,8 @@ uint16_t NimBLEClient::getMTU() {
auto cVector = &it->m_characteristicVector;
NIMBLE_LOGD(LOG_TAG, "checking service %s for handle: %d",
- it->getUUID().toString().c_str(),
- event->notify_rx.attr_handle);
+ it->getUUID().toString().c_str(),
+ event->notify_rx.attr_handle);
auto characteristic = cVector->cbegin();
for(; characteristic != cVector->cend(); ++characteristic) {
@@ -762,16 +903,19 @@ uint16_t NimBLEClient::getMTU() {
}
if(characteristic != cVector->cend()) {
- NIMBLE_LOGD(LOG_TAG, "Got Notification for characteristic %s", (*characteristic)->toString().c_str());
+ NIMBLE_LOGD(LOG_TAG, "Got Notification for characteristic %s",
+ (*characteristic)->toString().c_str());
+ time_t t = time(nullptr);
portENTER_CRITICAL(&(*characteristic)->m_valMux);
- (*characteristic)->m_value = std::string((char *)event->notify_rx.om->om_data, event->notify_rx.om->om_len);
- (*characteristic)->m_timestamp = time(nullptr);
+ (*characteristic)->m_value = std::string((char *)event->notify_rx.om->om_data,
+ event->notify_rx.om->om_len);
+ (*characteristic)->m_timestamp = t;
portEXIT_CRITICAL(&(*characteristic)->m_valMux);
if ((*characteristic)->m_notifyCallback != nullptr) {
NIMBLE_LOGD(LOG_TAG, "Invoking callback for notification on characteristic %s",
- (*characteristic)->toString().c_str());
+ (*characteristic)->toString().c_str());
(*characteristic)->m_notifyCallback(*characteristic, event->notify_rx.om->om_data,
event->notify_rx.om->om_len,
!event->notify_rx.indication);
@@ -790,10 +934,10 @@ uint16_t NimBLEClient::getMTU() {
}
NIMBLE_LOGD(LOG_TAG, "Peer requesting to update connection parameters");
NIMBLE_LOGD(LOG_TAG, "MinInterval: %d, MaxInterval: %d, Latency: %d, Timeout: %d",
- event->conn_update_req.peer_params->itvl_min,
- event->conn_update_req.peer_params->itvl_max,
- event->conn_update_req.peer_params->latency,
- event->conn_update_req.peer_params->supervision_timeout);
+ event->conn_update_req.peer_params->itvl_min,
+ event->conn_update_req.peer_params->itvl_max,
+ event->conn_update_req.peer_params->latency,
+ event->conn_update_req.peer_params->supervision_timeout);
rc = client->m_pClientCallbacks->onConnParamsUpdateRequest(client,
event->conn_update_req.peer_params) ? 0 : BLE_ERR_CONN_PARMS;
@@ -827,7 +971,9 @@ uint16_t NimBLEClient::getMTU() {
return 0;
}
- if(event->enc_change.status == 0 || event->enc_change.status == (BLE_HS_ERR_HCI_BASE + BLE_ERR_PINKEY_MISSING)) {
+ 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->enc_change.conn_handle, &desc);
assert(rc == 0);
@@ -922,7 +1068,9 @@ uint16_t NimBLEClient::getMTU() {
if(client->m_pTaskData != nullptr) {
client->m_pTaskData->rc = rc;
- xTaskNotifyGive(client->m_pTaskData->task);
+ if(client->m_pTaskData->task) {
+ xTaskNotifyGive(client->m_pTaskData->task);
+ }
client->m_pTaskData = nullptr;
}
@@ -935,7 +1083,7 @@ uint16_t NimBLEClient::getMTU() {
* @return True if we are connected and false if we are not connected.
*/
bool NimBLEClient::isConnected() {
- return m_isConnected;
+ return m_conn_id != BLE_HS_CONN_HANDLE_NONE;
} // isConnected
diff --git a/lib/libesp32/NimBLE-Arduino/src/NimBLEClient.h b/lib/libesp32/NimBLE-Arduino/src/NimBLEClient.h
index ddeef3cc3..a4b847000 100644
--- a/lib/libesp32/NimBLE-Arduino/src/NimBLEClient.h
+++ b/lib/libesp32/NimBLE-Arduino/src/NimBLEClient.h
@@ -30,6 +30,7 @@
#include
class NimBLERemoteService;
+class NimBLERemoteCharacteristic;
class NimBLEClientCallbacks;
class NimBLEAdvertisedDevice;
@@ -54,7 +55,8 @@ public:
size_t deleteService(const NimBLEUUID &uuid);
std::string getValue(const NimBLEUUID &serviceUUID, const NimBLEUUID &characteristicUUID);
bool setValue(const NimBLEUUID &serviceUUID, const NimBLEUUID &characteristicUUID,
- const std::string &value);
+ const std::string &value, bool response = false);
+ NimBLERemoteCharacteristic* getCharacteristic(const uint16_t handle);
bool isConnected();
void setClientCallbacks(NimBLEClientCallbacks *pClientCallbacks,
bool deleteCallbacks = true);
@@ -82,16 +84,17 @@ private:
const struct ble_gatt_error *error,
const struct ble_gatt_svc *service,
void *arg);
+ static void dcTimerCb(ble_npl_event *event);
bool retrieveServices(const NimBLEUUID *uuid_filter = nullptr);
NimBLEAddress m_peerAddress;
uint16_t m_conn_id;
- bool m_isConnected;
- bool m_waitingToConnect;
+ bool m_connEstablished;
bool m_deleteCallbacks;
int32_t m_connectTimeout;
NimBLEClientCallbacks* m_pClientCallbacks;
- ble_task_data_t *m_pTaskData;
+ ble_task_data_t* m_pTaskData;
+ ble_npl_callout m_dcTimer;
std::vector m_servicesVector;
diff --git a/lib/libesp32/NimBLE-Arduino/src/NimBLEDevice.cpp b/lib/libesp32/NimBLE-Arduino/src/NimBLEDevice.cpp
index fb36e6e57..f34a522f7 100644
--- a/lib/libesp32/NimBLE-Arduino/src/NimBLEDevice.cpp
+++ b/lib/libesp32/NimBLE-Arduino/src/NimBLEDevice.cpp
@@ -25,6 +25,7 @@
#include "nimble/nimble_port.h"
#include "nimble/nimble_port_freertos.h"
#include "host/ble_hs.h"
+#include "host/ble_hs_pvcy.h"
#include "host/util/util.h"
#include "services/gap/ble_svc_gap.h"
#include "services/gatt/ble_svc_gatt.h"
@@ -60,6 +61,7 @@ std::list NimBLEDevice::m_cList;
#endif
std::list NimBLEDevice::m_ignoreList;
NimBLESecurityCallbacks* NimBLEDevice::m_securityCallbacks = nullptr;
+uint8_t NimBLEDevice::m_own_addr_type = BLE_OWN_ADDR_PUBLIC;
/**
@@ -144,8 +146,8 @@ void NimBLEDevice::stopAdvertising() {
#if defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL)
/* 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);
+ NIMBLE_LOGW(LOG_TAG,"Number of clients exceeds Max connections. Cur=%d Max=%d",
+ m_cList.size(), NIMBLE_MAX_CONNECTIONS);
}
NimBLEClient* pClient = new NimBLEClient(peerAddress);
@@ -165,26 +167,31 @@ void NimBLEDevice::stopAdvertising() {
return false;
}
+ // Set the connection established flag to false to stop notifications
+ // from accessing the attribute vectors while they are being deleted.
+ pClient->m_connEstablished = false;
int rc =0;
- if(pClient->m_isConnected) {
+ if(pClient->isConnected()) {
rc = pClient->disconnect();
if (rc != 0 && rc != BLE_HS_EALREADY && rc != BLE_HS_ENOTCONN) {
return false;
}
- while(pClient->m_isConnected) {
- vTaskDelay(10);
+ while(pClient->isConnected()) {
+ taskYIELD();
}
- }
+ // Since we set the flag to false the app will not get a callback
+ // in the disconnect event so we call it here for good measure.
+ pClient->m_pClientCallbacks->onDisconnect(pClient);
- if(pClient->m_waitingToConnect) {
+ } else if(pClient->m_pTaskData != nullptr) {
rc = ble_gap_conn_cancel();
if (rc != 0 && rc != BLE_HS_EALREADY) {
return false;
}
- while(pClient->m_waitingToConnect) {
- vTaskDelay(10);
+ while(pClient->m_pTaskData != nullptr) {
+ taskYIELD();
}
}
@@ -405,30 +412,16 @@ void NimBLEDevice::stopAdvertising() {
m_synced = false;
-#if defined(CONFIG_BT_NIMBLE_ROLE_OBSERVER)
- if(m_pScan != nullptr) {
- m_pScan->onHostReset();
- }
-#endif
-
-/* Not needed
- if(m_pServer != nullptr) {
- m_pServer->onHostReset();
- }
-
- for(auto it = m_cList.cbegin(); it != m_cList.cend(); ++it) {
- (*it)->onHostReset();
- }
-*/
-
-#if defined(CONFIG_BT_NIMBLE_ROLE_BROADCASTER)
- if(m_bleAdvertising != nullptr) {
- m_bleAdvertising->onHostReset();
- }
-#endif
-
NIMBLE_LOGC(LOG_TAG, "Resetting state; reason=%d, %s", reason,
NimBLEUtils::returnCodeToString(reason));
+
+#if defined(CONFIG_BT_NIMBLE_ROLE_OBSERVER)
+ if(initialized) {
+ if(m_pScan != nullptr) {
+ m_pScan->onHostReset();
+ }
+ }
+#endif
} // onReset
@@ -448,20 +441,22 @@ void NimBLEDevice::stopAdvertising() {
int rc = ble_hs_util_ensure_addr(0);
assert(rc == 0);
+ // Yield for houskeeping before returning to operations.
+ // Occasionally triggers exception without.
+ taskYIELD();
+
m_synced = true;
if(initialized) {
#if defined(CONFIG_BT_NIMBLE_ROLE_OBSERVER)
if(m_pScan != nullptr) {
- // Restart scanning with the last values sent, allow to clear results.
- m_pScan->start(m_pScan->m_duration, m_pScan->m_scanCompleteCB);
+ m_pScan->onHostSync();
}
#endif
#if defined(CONFIG_BT_NIMBLE_ROLE_BROADCASTER)
if(m_bleAdvertising != nullptr) {
- // Restart advertisng, parameters should already be set.
- m_bleAdvertising->start();
+ m_bleAdvertising->onHostSync();
}
#endif
}
@@ -705,6 +700,35 @@ void NimBLEDevice::setSecurityCallbacks(NimBLESecurityCallbacks* callbacks) {
} // setSecurityCallbacks
+/**
+ * @brief Set the own address type.
+ * @param [in] own_addr_type Own Bluetooth Device address type.\n
+ * The available bits are defined as:
+ * * 0x00: BLE_OWN_ADDR_PUBLIC
+ * * 0x01: BLE_OWN_ADDR_RANDOM
+ * * 0x02: BLE_OWN_ADDR_RPA_PUBLIC_DEFAULT
+ * * 0x03: BLE_OWN_ADDR_RPA_RANDOM_DEFAULT
+ * @param [in] useNRPA If true, and address type is random, uses a non-resolvable random address.
+ */
+void NimBLEDevice::setOwnAddrType(uint8_t own_addr_type, bool useNRPA) {
+ m_own_addr_type = own_addr_type;
+ switch (own_addr_type) {
+ case BLE_OWN_ADDR_PUBLIC:
+ ble_hs_pvcy_rpa_config(NIMBLE_HOST_DISABLE_PRIVACY);
+ break;
+ case BLE_OWN_ADDR_RANDOM:
+ setSecurityInitKey(BLE_SM_PAIR_KEY_DIST_ENC | BLE_SM_PAIR_KEY_DIST_ID);
+ ble_hs_pvcy_rpa_config(useNRPA ? NIMBLE_HOST_ENABLE_NRPA : NIMBLE_HOST_ENABLE_RPA);
+ break;
+ case BLE_OWN_ADDR_RPA_PUBLIC_DEFAULT:
+ case BLE_OWN_ADDR_RPA_RANDOM_DEFAULT:
+ setSecurityInitKey(BLE_SM_PAIR_KEY_DIST_ENC | BLE_SM_PAIR_KEY_DIST_ID);
+ ble_hs_pvcy_rpa_config(NIMBLE_HOST_ENABLE_RPA);
+ break;
+ }
+} // setOwnAddrType
+
+
/**
* @brief Start the connection securing and authorization for this connection.
* @param conn_id The connection id of the peer device.
diff --git a/lib/libesp32/NimBLE-Arduino/src/NimBLEDevice.h b/lib/libesp32/NimBLE-Arduino/src/NimBLEDevice.h
index 252c52afd..2d586bb42 100644
--- a/lib/libesp32/NimBLE-Arduino/src/NimBLEDevice.h
+++ b/lib/libesp32/NimBLE-Arduino/src/NimBLEDevice.h
@@ -116,6 +116,7 @@ public:
static void setSecurityPasskey(uint32_t pin);
static uint32_t getSecurityPasskey();
static void setSecurityCallbacks(NimBLESecurityCallbacks* pCallbacks);
+ static void setOwnAddrType(uint8_t own_addr_type, bool useNRPA=false);
static int startSecurity(uint16_t conn_id);
static int setMTU(uint16_t mtu);
static uint16_t getMTU();
@@ -182,6 +183,7 @@ private:
static uint32_t m_passkey;
static ble_gap_event_listener m_listener;
static gap_event_handler m_customGapHandler;
+ static uint8_t m_own_addr_type;
};
diff --git a/lib/libesp32/NimBLE-Arduino/src/NimBLEHIDDevice.cpp b/lib/libesp32/NimBLE-Arduino/src/NimBLEHIDDevice.cpp
new file mode 100644
index 000000000..a06b75360
--- /dev/null
+++ b/lib/libesp32/NimBLE-Arduino/src/NimBLEHIDDevice.cpp
@@ -0,0 +1,251 @@
+/*
+ * NimBLEHIDDevice.cpp
+ *
+ * Created: on Oct 06 2020
+ * Author wakwak-koba
+ *
+ * Originally:
+ *
+ * BLEHIDDevice.cpp
+ *
+ * Created on: Jan 03, 2018
+ * Author: chegewara
+ */
+#include "sdkconfig.h"
+#if defined(CONFIG_BT_ENABLED)
+
+#include "nimconfig.h"
+#if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
+
+#include "NimBLEHIDDevice.h"
+#include "NimBLE2904.h"
+
+/**
+ * @brief Construct a default NimBLEHIDDevice object.
+ * @param [in] server A pointer to the server instance this HID Device will use.
+ */
+NimBLEHIDDevice::NimBLEHIDDevice(NimBLEServer* server) {
+ /*
+ * Here we create mandatory services described in bluetooth specification
+ */
+ m_deviceInfoService = server->createService(NimBLEUUID((uint16_t) 0x180a));
+ m_hidService = server->createService(NimBLEUUID((uint16_t) 0x1812), 40);
+ m_batteryService = server->createService(NimBLEUUID((uint16_t) 0x180f));
+
+ /*
+ * Mandatory characteristic for device info service
+ */
+ m_pnpCharacteristic = m_deviceInfoService->createCharacteristic((uint16_t) 0x2a50, NIMBLE_PROPERTY::READ);
+
+ /*
+ * Mandatory characteristics for HID service
+ */
+ m_hidInfoCharacteristic = m_hidService->createCharacteristic((uint16_t) 0x2a4a, NIMBLE_PROPERTY::READ);
+ m_reportMapCharacteristic = m_hidService->createCharacteristic((uint16_t) 0x2a4b, NIMBLE_PROPERTY::READ);
+ m_hidControlCharacteristic = m_hidService->createCharacteristic((uint16_t) 0x2a4c, NIMBLE_PROPERTY::WRITE_NR);
+ m_protocolModeCharacteristic = m_hidService->createCharacteristic((uint16_t) 0x2a4e, NIMBLE_PROPERTY::WRITE_NR | NIMBLE_PROPERTY::READ);
+
+ /*
+ * Mandatory battery level characteristic with notification and presence descriptor
+ */
+ m_batteryLevelCharacteristic = m_batteryService->createCharacteristic((uint16_t) 0x2a19, NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::NOTIFY);
+ NimBLE2904* batteryLevelDescriptor = (NimBLE2904*)m_batteryLevelCharacteristic->createDescriptor((uint16_t) 0x2904);
+ batteryLevelDescriptor->setFormat(NimBLE2904::FORMAT_UINT8);
+ batteryLevelDescriptor->setNamespace(1);
+ batteryLevelDescriptor->setUnit(0x27ad);
+
+ /*
+ * This value is setup here because its default value in most usage cases, its very rare to use boot mode
+ * and we want to simplify library using as much as possible
+ */
+ const uint8_t pMode[] = { 0x01 };
+ protocolMode()->setValue((uint8_t*) pMode, 1);
+}
+
+NimBLEHIDDevice::~NimBLEHIDDevice() {
+}
+
+/**
+ * @brief Set the report map data formatting information.
+ * @param [in] map A pointer to an array with the values to set.
+ * @param [in] size The number of values in the array.
+ */
+void NimBLEHIDDevice::reportMap(uint8_t* map, uint16_t size) {
+ m_reportMapCharacteristic->setValue(map, size);
+}
+
+/**
+ * @brief Start the HID device services.\n
+ * This function called when all the services have been created.
+ */
+void NimBLEHIDDevice::startServices() {
+ m_deviceInfoService->start();
+ m_hidService->start();
+ m_batteryService->start();
+}
+
+/**
+ * @brief Create a manufacturer characteristic (this characteristic is optional).
+ */
+NimBLECharacteristic* NimBLEHIDDevice::manufacturer() {
+ m_manufacturerCharacteristic = m_deviceInfoService->createCharacteristic((uint16_t) 0x2a29, NIMBLE_PROPERTY::READ);
+ return m_manufacturerCharacteristic;
+}
+
+/**
+ * @brief Set manufacturer name
+ * @param [in] name The manufacturer name of this HID device.
+ */
+void NimBLEHIDDevice::manufacturer(std::string name) {
+ m_manufacturerCharacteristic->setValue(name);
+}
+
+/**
+ * @brief Sets the Plug n Play characterisc value.
+ * @param [in] sig The vendor ID source number.
+ * @param [in[ vid The vendor ID number.
+ * @param [in] pid The product ID number.
+ * @param [in] version The produce version number.
+ */
+void NimBLEHIDDevice::pnp(uint8_t sig, uint16_t vid, uint16_t pid, uint16_t version) {
+ uint8_t pnp[] = { sig, (uint8_t) (vid >> 8), (uint8_t) vid, (uint8_t) (pid >> 8), (uint8_t) pid, (uint8_t) (version >> 8), (uint8_t) version };
+ m_pnpCharacteristic->setValue(pnp, sizeof(pnp));
+}
+
+/**
+ * @brief Sets the HID Information characteristic value.
+ * @param [in] country The country code for the device.
+ * @param [in] flags The HID Class Specification release number to use.
+ */
+void NimBLEHIDDevice::hidInfo(uint8_t country, uint8_t flags) {
+ uint8_t info[] = { 0x11, 0x1, country, flags };
+ m_hidInfoCharacteristic->setValue(info, sizeof(info));
+}
+
+/**
+ * @brief Create input report characteristic
+ * @param [in] reportID input report ID, the same as in report map for input object related to the characteristic
+ * @return pointer to new input report characteristic
+ */
+NimBLECharacteristic* NimBLEHIDDevice::inputReport(uint8_t reportID) {
+ NimBLECharacteristic* inputReportCharacteristic = m_hidService->createCharacteristic((uint16_t) 0x2a4d, NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::NOTIFY);
+ NimBLEDescriptor* inputReportDescriptor = inputReportCharacteristic->createDescriptor((uint16_t) 0x2908);
+
+ uint8_t desc1_val[] = { reportID, 0x01 };
+ inputReportDescriptor->setValue((uint8_t*) desc1_val, 2);
+
+ return inputReportCharacteristic;
+}
+
+/**
+ * @brief Create output report characteristic
+ * @param [in] reportID Output report ID, the same as in report map for output object related to the characteristic
+ * @return Pointer to new output report characteristic
+ */
+NimBLECharacteristic* NimBLEHIDDevice::outputReport(uint8_t reportID) {
+ NimBLECharacteristic* outputReportCharacteristic = m_hidService->createCharacteristic((uint16_t) 0x2a4d, NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::WRITE | NIMBLE_PROPERTY::WRITE_NR | NIMBLE_PROPERTY::READ_ENC | NIMBLE_PROPERTY::WRITE_ENC);
+ NimBLEDescriptor* outputReportDescriptor = outputReportCharacteristic->createDescriptor((uint16_t) 0x2908);
+
+ uint8_t desc1_val[] = { reportID, 0x02 };
+ outputReportDescriptor->setValue((uint8_t*) desc1_val, 2);
+
+ return outputReportCharacteristic;
+}
+
+/**
+ * @brief Create feature report characteristic.
+ * @param [in] reportID Feature report ID, the same as in report map for feature object related to the characteristic
+ * @return Pointer to new feature report characteristic
+ */
+NimBLECharacteristic* NimBLEHIDDevice::featureReport(uint8_t reportID) {
+ NimBLECharacteristic* featureReportCharacteristic = m_hidService->createCharacteristic((uint16_t) 0x2a4d, NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::WRITE | NIMBLE_PROPERTY::READ_ENC | NIMBLE_PROPERTY::WRITE_ENC);
+ NimBLEDescriptor* featureReportDescriptor = featureReportCharacteristic->createDescriptor((uint16_t) 0x2908);
+
+ uint8_t desc1_val[] = { reportID, 0x03 };
+ featureReportDescriptor->setValue((uint8_t*) desc1_val, 2);
+
+ return featureReportCharacteristic;
+}
+
+/**
+ * @brief Creates a keyboard boot input report characteristic
+ */
+NimBLECharacteristic* NimBLEHIDDevice::bootInput() {
+ return m_hidService->createCharacteristic((uint16_t) 0x2a22, NIMBLE_PROPERTY::NOTIFY);
+}
+
+/**
+ * @brief Create a keyboard boot output report characteristic
+ */
+NimBLECharacteristic* NimBLEHIDDevice::bootOutput() {
+ return m_hidService->createCharacteristic((uint16_t) 0x2a32, NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::WRITE | NIMBLE_PROPERTY::WRITE_NR);
+}
+
+/**
+ * @brief Returns a pointer to the HID control point characteristic.
+ */
+NimBLECharacteristic* NimBLEHIDDevice::hidControl() {
+ return m_hidControlCharacteristic;
+}
+
+/**
+ * @brief Returns a pointer to the protocol mode characteristic.
+ */
+NimBLECharacteristic* NimBLEHIDDevice::protocolMode() {
+ return m_protocolModeCharacteristic;
+}
+
+/**
+ * @brief Set the battery level characteristic value.
+ * @param [in] level The battery level value.
+ */
+void NimBLEHIDDevice::setBatteryLevel(uint8_t level) {
+ m_batteryLevelCharacteristic->setValue(&level, 1);
+}
+/*
+ * @brief Returns battery level characteristic
+ * @ return battery level characteristic
+ *//*
+BLECharacteristic* BLEHIDDevice::batteryLevel() {
+ return m_batteryLevelCharacteristic;
+}
+
+
+
+BLECharacteristic* BLEHIDDevice::reportMap() {
+ return m_reportMapCharacteristic;
+}
+
+BLECharacteristic* BLEHIDDevice::pnp() {
+ return m_pnpCharacteristic;
+}
+
+
+BLECharacteristic* BLEHIDDevice::hidInfo() {
+ return m_hidInfoCharacteristic;
+}
+*/
+
+/**
+ * @brief Returns a pointer to the device information service.
+ */
+NimBLEService* NimBLEHIDDevice::deviceInfo() {
+ return m_deviceInfoService;
+}
+
+/**
+ * @brief Returns a pointer to the HID service.
+ */
+NimBLEService* NimBLEHIDDevice::hidService() {
+ return m_hidService;
+}
+
+/**
+ * @brief @brief Returns a pointer to the battery service.
+ */
+NimBLEService* NimBLEHIDDevice::batteryService() {
+ return m_batteryService;
+}
+
+#endif // #if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
+#endif // #if defined(CONFIG_BT_ENABLED)
diff --git a/lib/libesp32/NimBLE-Arduino/src/NimBLEHIDDevice.h b/lib/libesp32/NimBLE-Arduino/src/NimBLEHIDDevice.h
new file mode 100644
index 000000000..6ed7c2bde
--- /dev/null
+++ b/lib/libesp32/NimBLE-Arduino/src/NimBLEHIDDevice.h
@@ -0,0 +1,89 @@
+/*
+ * NimBLEHIDDevice.h
+ *
+ * Created: on Oct 06 2020
+ * Author wakwak-koba
+ *
+ * Originally:
+ *
+ * BLEHIDDevice.h
+ *
+ * Created on: Jan 03, 2018
+ * Author: chegewara
+ */
+
+#ifndef _BLEHIDDEVICE_H_
+#define _BLEHIDDEVICE_H_
+
+#include "sdkconfig.h"
+#if defined(CONFIG_BT_ENABLED)
+
+#include "nimconfig.h"
+#if defined(CONFIG_BT_NIMBLE_ROLE_BROADCASTER)
+
+#include "NimBLECharacteristic.h"
+#include "NimBLEService.h"
+#include "NimBLEDescriptor.h"
+#include "HIDTypes.h"
+
+#define GENERIC_HID 0x03C0
+#define HID_KEYBOARD 0x03C1
+#define HID_MOUSE 0x03C2
+#define HID_JOYSTICK 0x03C3
+#define HID_GAMEPAD 0x03C4
+#define HID_TABLET 0x03C5
+#define HID_CARD_READER 0x03C6
+#define HID_DIGITAL_PEN 0x03C7
+#define HID_BARCODE 0x03C8
+
+
+/**
+ * @brief A model of a %BLE Human Interface Device.
+ */
+class NimBLEHIDDevice {
+public:
+ NimBLEHIDDevice(NimBLEServer*);
+ virtual ~NimBLEHIDDevice();
+
+ void reportMap(uint8_t* map, uint16_t);
+ void startServices();
+
+ NimBLEService* deviceInfo();
+ NimBLEService* hidService();
+ NimBLEService* batteryService();
+
+ NimBLECharacteristic* manufacturer();
+ void manufacturer(std::string name);
+ //NimBLECharacteristic* pnp();
+ void pnp(uint8_t sig, uint16_t vid, uint16_t pid, uint16_t version);
+ //NimBLECharacteristic* hidInfo();
+ void hidInfo(uint8_t country, uint8_t flags);
+ //NimBLECharacteristic* batteryLevel();
+ void setBatteryLevel(uint8_t level);
+
+
+ //NimBLECharacteristic* reportMap();
+ NimBLECharacteristic* hidControl();
+ NimBLECharacteristic* inputReport(uint8_t reportID);
+ NimBLECharacteristic* outputReport(uint8_t reportID);
+ NimBLECharacteristic* featureReport(uint8_t reportID);
+ NimBLECharacteristic* protocolMode();
+ NimBLECharacteristic* bootInput();
+ NimBLECharacteristic* bootOutput();
+
+private:
+ NimBLEService* m_deviceInfoService; //0x180a
+ NimBLEService* m_hidService; //0x1812
+ NimBLEService* m_batteryService = 0; //0x180f
+
+ NimBLECharacteristic* m_manufacturerCharacteristic; //0x2a29
+ NimBLECharacteristic* m_pnpCharacteristic; //0x2a50
+ NimBLECharacteristic* m_hidInfoCharacteristic; //0x2a4a
+ NimBLECharacteristic* m_reportMapCharacteristic; //0x2a4b
+ NimBLECharacteristic* m_hidControlCharacteristic; //0x2a4c
+ NimBLECharacteristic* m_protocolModeCharacteristic; //0x2a4e
+ NimBLECharacteristic* m_batteryLevelCharacteristic; //0x2a19
+};
+#endif // CONFIG_BT_NIMBLE_ROLE_BROADCASTER
+#endif // CONFIG_BT_ENABLED
+#endif /* _BLEHIDDEVICE_H_ */
diff --git a/lib/libesp32/NimBLE-Arduino/src/NimBLERemoteCharacteristic.cpp b/lib/libesp32/NimBLE-Arduino/src/NimBLERemoteCharacteristic.cpp
index 154206c73..8bc5090b1 100644
--- a/lib/libesp32/NimBLE-Arduino/src/NimBLERemoteCharacteristic.cpp
+++ b/lib/libesp32/NimBLE-Arduino/src/NimBLERemoteCharacteristic.cpp
@@ -38,7 +38,7 @@ static const char* LOG_TAG = "NimBLERemoteCharacteristic";
NimBLERemoteCharacteristic::NimBLERemoteCharacteristic(NimBLERemoteService *pRemoteService,
const struct ble_gatt_chr *chr)
{
-
+ NIMBLE_LOGD(LOG_TAG, ">> NimBLERemoteCharacteristic()");
switch (chr->uuid.u.type) {
case BLE_UUID_TYPE_16:
m_uuid = NimBLEUUID(chr->uuid.u16.value);
@@ -50,7 +50,6 @@ static const char* LOG_TAG = "NimBLERemoteCharacteristic";
m_uuid = NimBLEUUID(const_cast(&chr->uuid.u128));
break;
default:
- m_uuid = nullptr;
break;
}
@@ -61,6 +60,8 @@ static const char* LOG_TAG = "NimBLERemoteCharacteristic";
m_notifyCallback = nullptr;
m_timestamp = 0;
m_valMux = portMUX_INITIALIZER_UNLOCKED;
+
+ NIMBLE_LOGD(LOG_TAG, "<< NimBLERemoteCharacteristic(): %s", m_uuid.toString().c_str());
} // NimBLERemoteCharacteristic
@@ -208,15 +209,21 @@ int NimBLERemoteCharacteristic::descriptorDiscCB(uint16_t conn_handle,
bool NimBLERemoteCharacteristic::retrieveDescriptors(const NimBLEUUID *uuid_filter) {
NIMBLE_LOGD(LOG_TAG, ">> retrieveDescriptors() for characteristic: %s", getUUID().toString().c_str());
+ uint16_t endHandle = getRemoteService()->getEndHandle(this);
+ if(m_handle >= endHandle) {
+ return false;
+ }
+
int rc = 0;
ble_task_data_t taskData = {this, xTaskGetCurrentTaskHandle(), 0, nullptr};
desc_filter_t filter = {uuid_filter, &taskData};
rc = ble_gattc_disc_all_dscs(getRemoteService()->getClient()->getConnId(),
m_handle,
- getRemoteService()->getEndHandle(),
+ endHandle,
NimBLERemoteCharacteristic::descriptorDiscCB,
&filter);
+
if (rc != 0) {
NIMBLE_LOGE(LOG_TAG, "ble_gattc_disc_all_chrs: rc=%d %s", rc, NimBLEUtils::returnCodeToString(rc));
return false;
@@ -225,12 +232,13 @@ bool NimBLERemoteCharacteristic::retrieveDescriptors(const NimBLEUUID *uuid_filt
ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
if(taskData.rc != 0) {
+ NIMBLE_LOGE(LOG_TAG, "ble_gattc_disc_all_chrs: startHandle:%d endHandle:%d taskData.rc=%d %s", m_handle, endHandle, taskData.rc, NimBLEUtils::returnCodeToString(0x0100+taskData.rc));
return false;
}
return true;
NIMBLE_LOGD(LOG_TAG, "<< retrieveDescriptors(): Found %d descriptors.", m_descriptorVector.size());
-} // getDescriptors
+} // retrieveDescriptors
/**
@@ -243,7 +251,7 @@ NimBLERemoteDescriptor* NimBLERemoteCharacteristic::getDescriptor(const NimBLEUU
for(auto &it: m_descriptorVector) {
if(it->getUUID() == uuid) {
- NIMBLE_LOGD(LOG_TAG, "<< getDescriptor: found");
+ NIMBLE_LOGD(LOG_TAG, "<< getDescriptor: found the descriptor with uuid: %s", uuid.toString().c_str());
return it;
}
}
@@ -253,7 +261,18 @@ NimBLERemoteDescriptor* NimBLERemoteCharacteristic::getDescriptor(const NimBLEUU
if(m_descriptorVector.size() > prev_size) {
return m_descriptorVector.back();
}
+
+ // If the request was successful but 16/32 bit descriptor not found
+ // try again with the 128 bit uuid.
+ if(uuid.bitSize() == BLE_UUID_TYPE_16 ||
+ uuid.bitSize() == BLE_UUID_TYPE_32)
+ {
+ NimBLEUUID uuid128(uuid);
+ uuid128.to128();
+ return getDescriptor(uuid128);
+ }
}
+
NIMBLE_LOGD(LOG_TAG, "<< getDescriptor: Not found");
return nullptr;
} // getDescriptor
@@ -447,9 +466,10 @@ std::string NimBLERemoteCharacteristic::readValue(time_t *timestamp) {
}
} while(rc != 0 && retryCount--);
+ time_t t = time(nullptr);
portENTER_CRITICAL(&m_valMux);
m_value = value;
- m_timestamp = time(nullptr);
+ m_timestamp = t;
if(timestamp != nullptr) {
*timestamp = m_timestamp;
}
@@ -506,19 +526,19 @@ int NimBLERemoteCharacteristic::onReadCB(uint16_t conn_handle,
* @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.
+ * @return false if writing to the descriptor failed.
*/
bool NimBLERemoteCharacteristic::setNotify(uint16_t val, notify_callback notifyCallback, bool response) {
NIMBLE_LOGD(LOG_TAG, ">> setNotify(): %s, %02x", toString().c_str(), val);
+ m_notifyCallback = notifyCallback;
+
NimBLERemoteDescriptor* desc = getDescriptor(NimBLEUUID((uint16_t)0x2902));
if(desc == nullptr) {
- NIMBLE_LOGE(LOG_TAG, "<< setNotify(): Could not get descriptor");
- return false;
+ NIMBLE_LOGW(LOG_TAG, "<< setNotify(): Callback set, CCCD not found");
+ return true;
}
- m_notifyCallback = notifyCallback;
-
NIMBLE_LOGD(LOG_TAG, "<< setNotify()");
return desc->writeValue((uint8_t *)&val, 2, response);
@@ -531,7 +551,7 @@ bool NimBLERemoteCharacteristic::setNotify(uint16_t val, notify_callback notifyC
* @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.
+ * @return false if writing to the descriptor failed.
*/
bool NimBLERemoteCharacteristic::subscribe(bool notifications, notify_callback notifyCallback, bool response) {
if(notifications) {
@@ -545,7 +565,7 @@ bool NimBLERemoteCharacteristic::subscribe(bool notifications, notify_callback n
/**
* @brief Unsubscribe for notifications or indications.
* @param [in] response bool if true, require a write response from the descriptor write operation.
- * @return true if successful.
+ * @return false if writing to the descriptor failed.
*/
bool NimBLERemoteCharacteristic::unsubscribe(bool response) {
return setNotify(0x00, nullptr, response);
diff --git a/lib/libesp32/NimBLE-Arduino/src/NimBLERemoteDescriptor.cpp b/lib/libesp32/NimBLE-Arduino/src/NimBLERemoteDescriptor.cpp
index 9281e7df7..fc0f06b67 100644
--- a/lib/libesp32/NimBLE-Arduino/src/NimBLERemoteDescriptor.cpp
+++ b/lib/libesp32/NimBLE-Arduino/src/NimBLERemoteDescriptor.cpp
@@ -31,6 +31,7 @@ static const char* LOG_TAG = "NimBLERemoteDescriptor";
NimBLERemoteDescriptor::NimBLERemoteDescriptor(NimBLERemoteCharacteristic* pRemoteCharacteristic,
const struct ble_gatt_dsc *dsc)
{
+ NIMBLE_LOGD(LOG_TAG, ">> NimBLERemoteDescriptor()");
switch (dsc->uuid.u.type) {
case BLE_UUID_TYPE_16:
m_uuid = NimBLEUUID(dsc->uuid.u16.value);
@@ -42,12 +43,13 @@ NimBLERemoteDescriptor::NimBLERemoteDescriptor(NimBLERemoteCharacteristic* pRemo
m_uuid = NimBLEUUID(const_cast(&dsc->uuid.u128));
break;
default:
- m_uuid = nullptr;
break;
}
m_handle = dsc->handle;
m_pRemoteCharacteristic = pRemoteCharacteristic;
+
+ NIMBLE_LOGD(LOG_TAG, "<< NimBLERemoteDescriptor(): %s", m_uuid.toString().c_str());
}
diff --git a/lib/libesp32/NimBLE-Arduino/src/NimBLERemoteService.cpp b/lib/libesp32/NimBLE-Arduino/src/NimBLERemoteService.cpp
index 8901175dc..cb20e0b3f 100644
--- a/lib/libesp32/NimBLE-Arduino/src/NimBLERemoteService.cpp
+++ b/lib/libesp32/NimBLE-Arduino/src/NimBLERemoteService.cpp
@@ -44,12 +44,11 @@ NimBLERemoteService::NimBLERemoteService(NimBLEClient* pClient, const struct ble
m_uuid = NimBLEUUID(const_cast(&service->uuid.u128));
break;
default:
- m_uuid = nullptr;
break;
}
m_startHandle = service->start_handle;
m_endHandle = service->end_handle;
- NIMBLE_LOGD(LOG_TAG, "<< NimBLERemoteService()");
+ NIMBLE_LOGD(LOG_TAG, "<< NimBLERemoteService(): %s", m_uuid.toString().c_str());
}
@@ -95,8 +94,11 @@ NimBLERemoteCharacteristic* NimBLERemoteService::getCharacteristic(const char* u
* @return A pointer to the characteristic object, or nullptr if not found.
*/
NimBLERemoteCharacteristic* NimBLERemoteService::getCharacteristic(const NimBLEUUID &uuid) {
+ NIMBLE_LOGD(LOG_TAG, ">> getCharacteristic: uuid: %s", uuid.toString().c_str());
+
for(auto &it: m_characteristicVector) {
if(it->getUUID() == uuid) {
+ NIMBLE_LOGD(LOG_TAG, "<< getCharacteristic: found the characteristic with uuid: %s", uuid.toString().c_str());
return it;
}
}
@@ -106,8 +108,19 @@ NimBLERemoteCharacteristic* NimBLERemoteService::getCharacteristic(const NimBLEU
if(m_characteristicVector.size() > prev_size) {
return m_characteristicVector.back();
}
+
+ // If the request was successful but 16/32 bit characteristic not found
+ // try again with the 128 bit uuid.
+ if(uuid.bitSize() == BLE_UUID_TYPE_16 ||
+ uuid.bitSize() == BLE_UUID_TYPE_32)
+ {
+ NimBLEUUID uuid128(uuid);
+ uuid128.to128();
+ return getCharacteristic(uuid128);
+ }
}
+ NIMBLE_LOGD(LOG_TAG, "<< getCharacteristic: not found");
return nullptr;
} // getCharacteristic
@@ -236,6 +249,23 @@ uint16_t NimBLERemoteService::getEndHandle() {
return m_endHandle;
} // getEndHandle
+/**
+ * @brief Get the end handle of specified NimBLERemoteCharacteristic.
+ */
+
+uint16_t NimBLERemoteService::getEndHandle(NimBLERemoteCharacteristic *pCharacteristic) {
+ uint16_t endHandle = m_endHandle;
+
+ for(auto &it: m_characteristicVector) {
+ uint16_t defHandle = it->getDefHandle() - 1;
+ if(defHandle > pCharacteristic->getDefHandle() && endHandle > defHandle) {
+ endHandle = defHandle;
+ }
+ }
+
+ return endHandle;
+} // getEndHandle
+
/**
* @brief Get the service start handle.
diff --git a/lib/libesp32/NimBLE-Arduino/src/NimBLERemoteService.h b/lib/libesp32/NimBLE-Arduino/src/NimBLERemoteService.h
index 751c9effb..4920844e4 100644
--- a/lib/libesp32/NimBLE-Arduino/src/NimBLERemoteService.h
+++ b/lib/libesp32/NimBLE-Arduino/src/NimBLERemoteService.h
@@ -70,6 +70,7 @@ private:
uint16_t getStartHandle();
uint16_t getEndHandle();
+ uint16_t getEndHandle(NimBLERemoteCharacteristic *pCharacteristic);
void releaseSemaphores();
// Properties
diff --git a/lib/libesp32/NimBLE-Arduino/src/NimBLEScan.cpp b/lib/libesp32/NimBLE-Arduino/src/NimBLEScan.cpp
index dc82de549..122ff3332 100644
--- a/lib/libesp32/NimBLE-Arduino/src/NimBLEScan.cpp
+++ b/lib/libesp32/NimBLE-Arduino/src/NimBLEScan.cpp
@@ -30,7 +30,6 @@ static const char* LOG_TAG = "NimBLEScan";
* @brief Scan constuctor.
*/
NimBLEScan::NimBLEScan() {
- m_own_addr_type = 0;
m_scan_params.filter_policy = BLE_HCI_SCAN_FILT_NO_WL;
m_scan_params.passive = 1; // If set, don’t send scan requests to advertisers (i.e., don’t request additional advertising data).
m_scan_params.itvl = 0; // This is defined as the time interval from when the Controller started its last LE scan until it begins the subsequent LE scan. (units=0.625 msec)
@@ -38,9 +37,10 @@ NimBLEScan::NimBLEScan() {
m_scan_params.limited = 0; // If set, only discover devices in limited discoverable mode.
m_scan_params.filter_duplicates = 0; // If set, the controller ignores all but the first advertisement from each device.
m_pAdvertisedDeviceCallbacks = nullptr;
- m_stopped = true;
+ m_ignoreResults = false;
m_wantDuplicates = false;
m_pTaskData = nullptr;
+ m_duration = BLE_HS_FOREVER; // make sure this is non-zero in the event of a host reset
}
@@ -63,8 +63,8 @@ NimBLEScan::~NimBLEScan() {
switch(event->type) {
case BLE_GAP_EVENT_DISC: {
- if(pScan->m_stopped) {
- NIMBLE_LOGE(LOG_TAG, "Scan stop called, ignoring results.");
+ if(pScan->m_ignoreResults) {
+ NIMBLE_LOGE(LOG_TAG, "Scan op in progress - ignoring results");
return 0;
}
@@ -129,7 +129,6 @@ NimBLEScan::~NimBLEScan() {
pScan->m_scanCompleteCB(pScan->m_scanResults);
}
- pScan->m_stopped = true;
if(pScan->m_pTaskData != nullptr) {
pScan->m_pTaskData->rc = event->disc_complete.reason;
xTaskNotifyGive(pScan->m_pTaskData->task);
@@ -238,7 +237,7 @@ void NimBLEScan::setWindow(uint16_t windowMSecs) {
* @return true if scanning or scan starting.
*/
bool NimBLEScan::isScanning() {
- return !m_stopped;
+ return ble_gap_disc_active();
}
@@ -252,25 +251,6 @@ bool NimBLEScan::isScanning() {
bool NimBLEScan::start(uint32_t duration, void (*scanCompleteCB)(NimBLEScanResults), bool is_continue) {
NIMBLE_LOGD(LOG_TAG, ">> start(duration=%d)", duration);
- // If Host is not synced we cannot start scanning.
- if(!NimBLEDevice::m_synced) {
- NIMBLE_LOGC(LOG_TAG, "Host reset, wait for sync.");
- return false;
- }
-
- if(ble_gap_conn_active()) {
- NIMBLE_LOGE(LOG_TAG, "Connection in progress - must wait.");
- return false;
- }
-
- // If we are already scanning don't start again or we will get stuck on the semaphore.
- if(!m_stopped || ble_gap_disc_active()) { // double check - can cause host reset.
- NIMBLE_LOGE(LOG_TAG, "Scan already in progress");
- return false;
- }
-
- m_stopped = false;
-
// Save the callback to be invoked when the scan completes.
m_scanCompleteCB = scanCompleteCB;
// Save the duration in the case that the host is reset so we can reuse it.
@@ -281,32 +261,51 @@ bool NimBLEScan::start(uint32_t duration, void (*scanCompleteCB)(NimBLEScanResul
duration = BLE_HS_FOREVER;
}
else{
- duration = duration*1000; // convert duration to milliseconds
+ // convert duration to milliseconds
+ duration = duration * 1000;
}
- // if we are connecting to devices that are advertising even after being connected, multiconnecting peripherals
- // then we should not clear vector or we will connect the same device few times
+ // Set the flag to ignore the results while we are deleting the vector
if(!is_continue) {
- clearResults();
+ m_ignoreResults = true;
}
- int rc = 0;
- do{
- rc = ble_gap_disc(m_own_addr_type, duration, &m_scan_params,
- NimBLEScan::handleGapEvent, this);
- if(rc == BLE_HS_EBUSY) {
- vTaskDelay(1 / portTICK_PERIOD_MS);
- }
- } while(rc == BLE_HS_EBUSY);
+ int rc = ble_gap_disc(NimBLEDevice::m_own_addr_type, duration, &m_scan_params,
+ NimBLEScan::handleGapEvent, this);
- if (rc != 0 && rc != BLE_HS_EDONE) {
- NIMBLE_LOGE(LOG_TAG, "Error initiating GAP discovery procedure; rc=%d, %s",
- rc, NimBLEUtils::returnCodeToString(rc));
- m_stopped = true;
+ switch(rc) {
+ case 0:
+ if(!is_continue) {
+ clearResults();
+ }
+ break;
+
+ case BLE_HS_EALREADY:
+ break;
+
+ case BLE_HS_EBUSY:
+ NIMBLE_LOGE(LOG_TAG, "Unable to scan - connection in progress.");
+ break;
+
+ case BLE_HS_ETIMEOUT_HCI:
+ case BLE_HS_EOS:
+ case BLE_HS_ECONTROLLER:
+ case BLE_HS_ENOTSYNCED:
+ NIMBLE_LOGC(LOG_TAG, "Unable to scan - Host Reset");
+ break;
+
+ default:
+ NIMBLE_LOGE(LOG_TAG, "Error initiating GAP discovery procedure; rc=%d, %s",
+ rc, NimBLEUtils::returnCodeToString(rc));
+ break;
+ }
+
+ m_ignoreResults = false;
+ NIMBLE_LOGD(LOG_TAG, "<< start()");
+
+ if(rc != 0 && rc != BLE_HS_EALREADY) {
return false;
}
-
- NIMBLE_LOGD(LOG_TAG, "<< start()");
return true;
} // start
@@ -347,8 +346,6 @@ bool NimBLEScan::stop() {
return false;
}
- m_stopped = true;
-
if (rc != BLE_HS_EALREADY && m_scanCompleteCB != nullptr) {
m_scanCompleteCB(m_scanResults);
}
@@ -381,13 +378,25 @@ void NimBLEScan::erase(const NimBLEAddress &address) {
/**
- * @brief If the host reset the scan will have stopped so we should set the flag as stopped.
+ * @brief Called when host reset, we set a flag to stop scanning until synced.
*/
void NimBLEScan::onHostReset() {
- m_stopped = true;
+ m_ignoreResults = true;
}
+/**
+ * @brief If the host reset and re-synced this is called.
+ * If the application was scanning indefinitely with a callback, restart it.
+ */
+void NimBLEScan::onHostSync() {
+ m_ignoreResults = false;
+
+ if(m_duration == 0 && m_pAdvertisedDeviceCallbacks != nullptr) {
+ start(m_duration, m_scanCompleteCB);
+ }
+}
+
/**
* @brief Get the results of the scan.
* @return NimBLEScanResults object.
diff --git a/lib/libesp32/NimBLE-Arduino/src/NimBLEScan.h b/lib/libesp32/NimBLE-Arduino/src/NimBLEScan.h
index 822629025..9007a7dd8 100644
--- a/lib/libesp32/NimBLE-Arduino/src/NimBLEScan.h
+++ b/lib/libesp32/NimBLE-Arduino/src/NimBLEScan.h
@@ -83,12 +83,12 @@ private:
~NimBLEScan();
static int handleGapEvent(ble_gap_event* event, void* arg);
void onHostReset();
+ void onHostSync();
NimBLEAdvertisedDeviceCallbacks* m_pAdvertisedDeviceCallbacks = nullptr;
void (*m_scanCompleteCB)(NimBLEScanResults scanResults);
ble_gap_disc_params m_scan_params;
- uint8_t m_own_addr_type;
- bool m_stopped;
+ bool m_ignoreResults;
bool m_wantDuplicates;
NimBLEScanResults m_scanResults;
uint32_t m_duration;
diff --git a/lib/libesp32/NimBLE-Arduino/src/NimBLEServer.cpp b/lib/libesp32/NimBLE-Arduino/src/NimBLEServer.cpp
index 8c75192a9..fd5d8266b 100644
--- a/lib/libesp32/NimBLE-Arduino/src/NimBLEServer.cpp
+++ b/lib/libesp32/NimBLE-Arduino/src/NimBLEServer.cpp
@@ -296,6 +296,7 @@ size_t NimBLEServer::getConnectedCount() {
}
server->m_pServerCallbacks->onDisconnect(server);
+ server->m_pServerCallbacks->onDisconnect(server, &event->disconnect.conn);
if(server->m_advertiseOnDisconnect) {
server->startAdvertising();
@@ -620,7 +621,13 @@ uint16_t NimBLEServer::getPeerMTU(uint16_t conn_id) {
/**
- * Update connection parameters can be called only after connection has been established
+ * @brief Request an Update the connection parameters:
+ * * Can only be used after a connection has been established.
+ * @param [in] conn_handle The connection handle of the peer to send the request to.
+ * @param [in] minInterval The minimum connection interval in 1.25ms units.
+ * @param [in] maxInterval The maximum connection interval in 1.25ms units.
+ * @param [in] latency The number of packets allowed to skip (extends max interval).
+ * @param [in] timeout The timeout time in 10ms units before disconnecting.
*/
void NimBLEServer::updateConnParams(uint16_t conn_handle,
uint16_t minInterval, uint16_t maxInterval,
@@ -658,6 +665,10 @@ void NimBLEServerCallbacks::onDisconnect(NimBLEServer* pServer) {
NIMBLE_LOGD("NimBLEServerCallbacks", "onDisconnect(): Default");
} // onDisconnect
+void NimBLEServerCallbacks::onDisconnect(NimBLEServer* pServer, ble_gap_conn_desc* desc) {
+ NIMBLE_LOGD("NimBLEServerCallbacks", "onDisconnect(): Default");
+} // onDisconnect
+
uint32_t NimBLEServerCallbacks::onPassKeyRequest(){
NIMBLE_LOGD("NimBLEServerCallbacks", "onPassKeyRequest: default: 123456");
return 123456;
diff --git a/lib/libesp32/NimBLE-Arduino/src/NimBLEServer.h b/lib/libesp32/NimBLE-Arduino/src/NimBLEServer.h
index 1fa24b23c..bedf9cf58 100644
--- a/lib/libesp32/NimBLE-Arduino/src/NimBLEServer.h
+++ b/lib/libesp32/NimBLE-Arduino/src/NimBLEServer.h
@@ -114,6 +114,15 @@ public:
*/
virtual void onDisconnect(NimBLEServer* pServer);
+ /**
+ * @brief Handle a client disconnection.
+ * This is called when a client discconnects.
+ * @param [in] pServer A pointer to the %BLE server that received the client disconnection.
+ * @param [in] desc A pointer to the connection description structure containig information
+ * about the connection.
+ */
+ virtual void onDisconnect(NimBLEServer* pServer, ble_gap_conn_desc* desc);
+
/**
* @brief Called when a client requests a passkey for pairing.
* @return The passkey to be sent to the client.
diff --git a/lib/libesp32/NimBLE-Arduino/src/NimBLEUUID.cpp b/lib/libesp32/NimBLE-Arduino/src/NimBLEUUID.cpp
index 1b00a3237..21ff27047 100644
--- a/lib/libesp32/NimBLE-Arduino/src/NimBLEUUID.cpp
+++ b/lib/libesp32/NimBLE-Arduino/src/NimBLEUUID.cpp
@@ -264,6 +264,37 @@ std::string NimBLEUUID::toString() const {
*/
bool NimBLEUUID::operator ==(const NimBLEUUID & rhs) const {
if(m_valueSet && rhs.m_valueSet) {
+ NIMBLE_LOGD(LOG_TAG,"Comparing UUIDs; type %u to %u; UUID %s to %s",
+ m_uuid.u.type, rhs.m_uuid.u.type,
+ this->toString().c_str(), rhs.toString().c_str());
+
+ if(m_uuid.u.type != rhs.m_uuid.u.type) {
+ uint8_t uuidBase[16] = {
+ 0xfb, 0x34, 0x9b, 0x5f, 0x80, 0x00, 0x00, 0x80,
+ 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+ };
+
+ if(m_uuid.u.type == BLE_UUID_TYPE_128){
+ if(rhs.m_uuid.u.type == BLE_UUID_TYPE_16){
+ memcpy(uuidBase+12, &rhs.m_uuid.u16.value, 2);
+ } else if (rhs.m_uuid.u.type == BLE_UUID_TYPE_32){
+ memcpy(uuidBase+12, &rhs.m_uuid.u32.value, 4);
+ }
+ return memcmp(m_uuid.u128.value,uuidBase,16) == 0;
+
+ } else if(rhs.m_uuid.u.type == BLE_UUID_TYPE_128) {
+ if(m_uuid.u.type == BLE_UUID_TYPE_16){
+ memcpy(uuidBase+12, &m_uuid.u16.value, 2);
+ } else if (m_uuid.u.type == BLE_UUID_TYPE_32){
+ memcpy(uuidBase+12, &m_uuid.u32.value, 4);
+ }
+ return memcmp(rhs.m_uuid.u128.value,uuidBase,16) == 0;
+
+ } else {
+ return false;
+ }
+ }
+
return ble_uuid_cmp(&m_uuid.u, &rhs.m_uuid.u) == 0;
}
diff --git a/lib/libesp32/NimBLE-Arduino/src/esp-hci/src/esp_nimble_hci.c b/lib/libesp32/NimBLE-Arduino/src/esp-hci/src/esp_nimble_hci.c
index 0ba15e9c3..98c133fa5 100644
--- a/lib/libesp32/NimBLE-Arduino/src/esp-hci/src/esp_nimble_hci.c
+++ b/lib/libesp32/NimBLE-Arduino/src/esp-hci/src/esp_nimble_hci.c
@@ -19,6 +19,13 @@
* under the License.
*/
+/*
+ * This file has been modified by Ryan Powell, aka h2zero.
+ * The modifications are for the purpose of improving performance and support
+ * for Esprssif versions used by the ardruino-esp32 core that are less current
+ * than the esp-idf releases.
+ */
+
#include
#include "sysinit/sysinit.h"
#include "nimble/hci_common.h"
@@ -30,8 +37,10 @@
#include "esp_bt.h"
#include "freertos/semphr.h"
#include "esp_compiler.h"
+/* IPC is used to improve performance when calls come from a processor not running the NimBLE stack */
+/* but does not exist for solo */
#ifndef CONFIG_FREERTOS_UNICORE
-#include "esp_ipc.h"
+ #include "esp_ipc.h"
#endif
#define NIMBLE_VHCI_TIMEOUT_MS 2000
@@ -81,31 +90,40 @@ 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)
+/* Added; Called from the core NimBLE is running on, not used for unicore */
+#ifndef CONFIG_FREERTOS_UNICORE
+void ble_hci_trans_hs_cmd_tx_on_core(void *arg)
{
- uint8_t *cmd = arg;
- uint16_t len = BLE_HCI_CMD_HDR_LEN + cmd[3] + 1;
- esp_vhci_host_send_packet(cmd, len);
+ // Ugly but necessary as the arduino core does not provide enough IPC stack for variables.
+ esp_vhci_host_send_packet((uint8_t*)arg, *((uint8_t*)arg + 3) + 1 + BLE_HCI_CMD_HDR_LEN);
}
+#endif
+/* Modified to use ipc calls in arduino to correct performance issues */
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) {
- if (xPortGetCoreID() != 0) {
+/* esp_ipc_call_blocking does not exist for solo */
#ifndef CONFIG_FREERTOS_UNICORE
- esp_ipc_call_blocking(0, ble_hci_trans_hs_cmd_tx_on_core_0, cmd);
-#endif
+ if (xPortGetCoreID() != CONFIG_BT_NIMBLE_PINNED_TO_CORE) {
+ esp_ipc_call_blocking(CONFIG_BT_NIMBLE_PINNED_TO_CORE,
+ ble_hci_trans_hs_cmd_tx_on_core, cmd);
} else {
- ble_hci_trans_hs_cmd_tx_on_core_0(cmd);
+ esp_vhci_host_send_packet(cmd, len);
}
+#else /* Unicore */
+ esp_vhci_host_send_packet(cmd, len);
+#endif
} else {
rc = BLE_HS_ETIMEOUT_HCI;
}
@@ -124,21 +142,21 @@ 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)
+/* Added; Called from the core NimBLE is running on, not used for unicore */
+#ifndef CONFIG_FREERTOS_UNICORE
+void ble_hci_trans_hs_acl_tx_on_core(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);
+ // Ugly but necessary as the arduino core does not provide enough IPC stack for variables.
+ esp_vhci_host_send_packet((uint8_t*)arg + 2, *(uint16_t*)arg);
}
+#endif
+/* Modified to use ipc calls in arduino to correct performance issues */
int ble_hci_trans_hs_acl_tx(struct os_mbuf *om)
{
- uint8_t rc = 0;
+ uint16_t len = 0;
+ uint8_t data[MYNEWT_VAL(BLE_ACL_BUF_SIZE) + 3], rc = 0;
+ bool tx_using_nimble_core = 0;
/* If this packet is zero length, just free it */
if (OS_MBUF_PKTLEN(om) == 0) {
os_mbuf_free_chain(om);
@@ -149,14 +167,36 @@ int ble_hci_trans_hs_acl_tx(struct os_mbuf *om)
ESP_LOGD(TAG, "Controller not ready to receive packets");
}
- if (xSemaphoreTake(vhci_send_sem, NIMBLE_VHCI_TIMEOUT_MS / portTICK_PERIOD_MS) == pdTRUE) {
- if (xPortGetCoreID() != 0) {
+ len = 1 + OS_MBUF_PKTLEN(om);
+/* Don't check core ID if unicore */
#ifndef CONFIG_FREERTOS_UNICORE
- esp_ipc_call_blocking(0, ble_hci_trans_hs_acl_tx_on_core_0, om);
+ tx_using_nimble_core = xPortGetCoreID() != CONFIG_BT_NIMBLE_PINNED_TO_CORE;
+ if (tx_using_nimble_core) {
+ data[0] = len;
+ data[1] = (len >> 8);
+ data[2] = BLE_HCI_UART_H4_ACL;
+ os_mbuf_copydata(om, 0, OS_MBUF_PKTLEN(om), &data[3]);
+ } else {
+ data[0] = BLE_HCI_UART_H4_ACL;
+ os_mbuf_copydata(om, 0, OS_MBUF_PKTLEN(om), &data[1]);
+ }
+#else /* Unicore */
+ data[0] = BLE_HCI_UART_H4_ACL;
+ os_mbuf_copydata(om, 0, OS_MBUF_PKTLEN(om), &data[1]);
#endif
+
+ if (xSemaphoreTake(vhci_send_sem, NIMBLE_VHCI_TIMEOUT_MS / portTICK_PERIOD_MS) == pdTRUE) {
+/* esp_ipc_call_blocking does not exist for solo */
+#ifndef CONFIG_FREERTOS_UNICORE
+ if (tx_using_nimble_core) {
+ esp_ipc_call_blocking(CONFIG_BT_NIMBLE_PINNED_TO_CORE,
+ ble_hci_trans_hs_acl_tx_on_core, data);
} else {
- ble_hci_trans_hs_acl_tx_on_core_0(om);
+ esp_vhci_host_send_packet(data, len);
}
+#else /* Unicore */
+ esp_vhci_host_send_packet(data, len);
+#endif
} else {
rc = BLE_HS_ETIMEOUT_HCI;
}
@@ -367,6 +407,13 @@ static int host_rcv_pkt(uint8_t *data, uint16_t len)
totlen = BLE_HCI_EVENT_HDR_LEN + data[2];
assert(totlen <= UINT8_MAX + BLE_HCI_EVENT_HDR_LEN);
+ if (totlen > MYNEWT_VAL(BLE_HCI_EVT_BUF_SIZE)) {
+ ESP_LOGE(TAG, "Received HCI data length at host (%d) exceeds maximum configured HCI event buffer size (%d).",
+ totlen, MYNEWT_VAL(BLE_HCI_EVT_BUF_SIZE));
+ ble_hs_sched_reset(BLE_HS_ECONTROLLER);
+ return 0;
+ }
+
if (data[1] == BLE_HCI_EVCODE_HW_ERROR) {
assert(0);
}
@@ -476,6 +523,9 @@ 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();
+ /* Added to ensure BLE only mode */
+ bt_cfg.mode = ESP_BT_MODE_BLE;
+ /* Added to set max connections from nimconfig */
bt_cfg.ble_max_conn = CONFIG_BT_NIMBLE_MAX_CONNECTIONS;
if ((ret = esp_bt_controller_init(&bt_cfg)) != ESP_OK) {
@@ -485,6 +535,7 @@ esp_err_t esp_nimble_hci_and_controller_init(void)
if ((ret = esp_bt_controller_enable(ESP_BT_MODE_BLE)) != ESP_OK) {
return ret;
}
+
return esp_nimble_hci_init();
}
diff --git a/lib/libesp32/NimBLE-Arduino/src/esp_nimble_cfg.h b/lib/libesp32/NimBLE-Arduino/src/esp_nimble_cfg.h
index 384ec4a56..bafbeac8f 100644
--- a/lib/libesp32/NimBLE-Arduino/src/esp_nimble_cfg.h
+++ b/lib/libesp32/NimBLE-Arduino/src/esp_nimble_cfg.h
@@ -483,6 +483,10 @@
#define MYNEWT_VAL_BLE_L2CAP_COC_MAX_NUM CONFIG_BT_NIMBLE_L2CAP_COC_MAX_NUM
#endif
+#ifndef MYNEWT_VAL_BLE_L2CAP_COC_MPS
+#define MYNEWT_VAL_BLE_L2CAP_COC_MPS (MYNEWT_VAL_MSYS_1_BLOCK_SIZE - 8)
+#endif
+
#ifndef MYNEWT_VAL_BLE_L2CAP_JOIN_RX_FRAGS
#define MYNEWT_VAL_BLE_L2CAP_JOIN_RX_FRAGS (1)
#endif
diff --git a/lib/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_eddystone.c b/lib/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_eddystone.c
index 7d80d134d..eccb3e988 100644
--- a/lib/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_eddystone.c
+++ b/lib/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_eddystone.c
@@ -76,7 +76,7 @@ ble_eddystone_set_adv_data_gen(struct ble_hs_adv_fields *adv_fields,
if (adv_fields->num_uuids16 > BLE_EDDYSTONE_MAX_UUIDS16) {
return BLE_HS_EINVAL;
}
- if (svc_data_len > BLE_EDDYSTONE_MAX_SVC_DATA_LEN) {
+ if (svc_data_len > (BLE_EDDYSTONE_MAX_SVC_DATA_LEN - BLE_EDDYSTONE_SVC_DATA_BASE_SZ)) {
return BLE_HS_EINVAL;
}
if (adv_fields->num_uuids16 > 0 && !adv_fields->uuids16_is_complete) {
diff --git a/lib/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_gap.c b/lib/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_gap.c
index 09ae27047..d77ff6a87 100644
--- a/lib/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_gap.c
+++ b/lib/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_gap.c
@@ -1017,7 +1017,7 @@ ble_gap_master_failed(int status)
#endif
default:
- BLE_HS_DBG_ASSERT(0);
+ //BLE_HS_DBG_ASSERT(0);
break;
}
}
@@ -1458,8 +1458,8 @@ ble_gap_rx_periodic_adv_rpt(struct hci_le_subev_periodic_adv_rpt *evt)
{
struct ble_hs_periodic_sync *psync;
struct ble_gap_event event;
- ble_gap_event_fn *cb;
- void *cb_arg;
+ ble_gap_event_fn *cb = NULL;
+ void *cb_arg = NULL;
ble_hs_lock();
psync = ble_hs_periodic_sync_find_by_handle(evt->sync_handle);
diff --git a/lib/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_hs_conn.c b/lib/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_hs_conn.c
index eb65e3288..b45128e23 100644
--- a/lib/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_hs_conn.c
+++ b/lib/libesp32/NimBLE-Arduino/src/nimble/host/src/ble_hs_conn.c
@@ -470,29 +470,52 @@ ble_hs_conn_timer(void)
int32_t time_diff;
uint16_t conn_handle;
- conn_handle = BLE_HS_CONN_HANDLE_NONE;
- next_exp_in = BLE_HS_FOREVER;
- now = ble_npl_time_get();
+ for (;;) {
+ conn_handle = BLE_HS_CONN_HANDLE_NONE;
+ next_exp_in = BLE_HS_FOREVER;
+ now = ble_npl_time_get();
- ble_hs_lock();
+ ble_hs_lock();
- /* This loop performs one of two tasks:
- * 1. Determine if any connections need to be terminated due to timeout.
- * If so, break out of the loop and terminate the connection. This
- * function will need to be executed again.
- * 2. Otherwise, determine when the next timeout will occur.
- */
- SLIST_FOREACH(conn, &ble_hs_conns, bhc_next) {
- if (!(conn->bhc_flags & BLE_HS_CONN_F_TERMINATING)) {
+ /* This loop performs one of two tasks:
+ * 1. Determine if any connections need to be terminated due to timeout.
+ * If so, break out of the loop and terminate the connection. This
+ * function will need to be executed again.
+ * 2. Otherwise, determine when the next timeout will occur.
+ */
+ SLIST_FOREACH(conn, &ble_hs_conns, bhc_next) {
+ if (!(conn->bhc_flags & BLE_HS_CONN_F_TERMINATING)) {
#if MYNEWT_VAL(BLE_L2CAP_RX_FRAG_TIMEOUT) != 0
- /* Check each connection's rx fragment timer. If too much time
- * passes after a partial packet is received, the connection is
- * terminated.
- */
- if (conn->bhc_rx_chan != NULL) {
- time_diff = conn->bhc_rx_timeout - now;
+ /* Check each connection's rx fragment timer. If too much time
+ * passes after a partial packet is received, the connection is
+ * terminated.
+ */
+ if (conn->bhc_rx_chan != NULL) {
+ time_diff = conn->bhc_rx_timeout - now;
+ if (time_diff <= 0) {
+ /* ACL reassembly has timed out. Remember the connection
+ * handle so it can be terminated after the mutex is
+ * unlocked.
+ */
+ conn_handle = conn->bhc_handle;
+ break;
+ }
+
+ /* Determine if this connection is the soonest to time out. */
+ if (time_diff < next_exp_in) {
+ next_exp_in = time_diff;
+ }
+ }
+#endif
+
+#if BLE_HS_ATT_SVR_QUEUED_WRITE_TMO
+ /* Check each connection's rx queued write timer. If too much
+ * time passes after a prep write is received, the queue is
+ * cleared.
+ */
+ time_diff = ble_att_svr_ticks_until_tmo(&conn->bhc_att_svr, now);
if (time_diff <= 0) {
/* ACL reassembly has timed out. Remember the connection
* handle so it can be terminated after the mutex is
@@ -506,45 +529,22 @@ ble_hs_conn_timer(void)
if (time_diff < next_exp_in) {
next_exp_in = time_diff;
}
- }
#endif
-
-#if BLE_HS_ATT_SVR_QUEUED_WRITE_TMO
- /* Check each connection's rx queued write timer. If too much
- * time passes after a prep write is received, the queue is
- * cleared.
- */
- time_diff = ble_att_svr_ticks_until_tmo(&conn->bhc_att_svr, now);
- if (time_diff <= 0) {
- /* ACL reassembly has timed out. Remember the connection
- * handle so it can be terminated after the mutex is
- * unlocked.
- */
- conn_handle = conn->bhc_handle;
- break;
}
-
- /* Determine if this connection is the soonest to time out. */
- if (time_diff < next_exp_in) {
- next_exp_in = time_diff;
- }
-#endif
}
+
+ ble_hs_unlock();
+
+ /* If a connection has timed out, terminate it. We need to repeatedly
+ * call this function again to determine when the next timeout is.
+ */
+ if (conn_handle != BLE_HS_CONN_HANDLE_NONE) {
+ ble_gap_terminate(conn_handle, BLE_ERR_REM_USER_CONN_TERM);
+ continue;
+ }
+
+ return next_exp_in;
}
-
- ble_hs_unlock();
-
- /* If a connection has timed out, terminate it. We need to recursively
- * call this function again to determine when the next timeout is. This
- * is a tail-recursive call, so it should be optimized to execute in the
- * same stack frame.
- */
- if (conn_handle != BLE_HS_CONN_HANDLE_NONE) {
- ble_gap_terminate(conn_handle, BLE_ERR_REM_USER_CONN_TERM);
- return ble_hs_conn_timer();
- }
-
- return next_exp_in;
}
int
diff --git a/lib/libesp32/NimBLE-Arduino/src/nimble/host/store/config/src/ble_store_nvs.c b/lib/libesp32/NimBLE-Arduino/src/nimble/host/store/config/src/ble_store_nvs.c
index a6fea44c7..13dae8d2b 100644
--- a/lib/libesp32/NimBLE-Arduino/src/nimble/host/store/config/src/ble_store_nvs.c
+++ b/lib/libesp32/NimBLE-Arduino/src/nimble/host/store/config/src/ble_store_nvs.c
@@ -180,7 +180,9 @@ static int
get_nvs_db_attribute(int obj_type, bool empty, void *value, int num_value)
{
union ble_store_value cur = {0};
+#if MYNEWT_VAL(BLE_HOST_BASED_PRIVACY)
struct ble_hs_dev_records p_dev_rec = {0};
+#endif
esp_err_t err;
int i, count = 0, max_limit = 0;
char key_string[NIMBLE_NVS_STR_NAME_MAX_LEN];
@@ -190,11 +192,15 @@ get_nvs_db_attribute(int obj_type, bool empty, void *value, int num_value)
for (i = 1; i <= max_limit; i++) {
get_nvs_key_string(obj_type, i, key_string);
+#if MYNEWT_VAL(BLE_HOST_BASED_PRIVACY)
if (obj_type != BLE_STORE_OBJ_TYPE_PEER_DEV_REC) {
+#endif
err = get_nvs_db_value(obj_type, key_string, &cur);
+#if MYNEWT_VAL(BLE_HOST_BASED_PRIVACY)
} else {
err = get_nvs_peer_record(key_string, &p_dev_rec);
}
+#endif
/* Check if the user is searching for empty index to write to */
if (err == ESP_ERR_NVS_NOT_FOUND) {
if (empty) {
@@ -206,10 +212,13 @@ get_nvs_db_attribute(int obj_type, bool empty, void *value, int num_value)
/* If user has provided value, then the purpose is to find
* non-matching entry from NVS */
if (value) {
+#if MYNEWT_VAL(BLE_HOST_BASED_PRIVACY)
if (obj_type == BLE_STORE_OBJ_TYPE_PEER_DEV_REC) {
err = get_nvs_matching_index(&p_dev_rec, value, num_value,
sizeof(struct ble_hs_dev_records));
- } else {
+ } else
+#endif
+ {
if (obj_type != BLE_STORE_OBJ_TYPE_CCCD) {
err = get_nvs_matching_index(&cur.sec, value, num_value,
sizeof(struct ble_store_value_sec));
@@ -376,7 +385,9 @@ populate_db_from_nvs(int obj_type, void *dst, int *db_num)
{
uint8_t *db_item = (uint8_t *)dst;
union ble_store_value cur = {0};
+#if MYNEWT_VAL(BLE_HOST_BASED_PRIVACY)
struct ble_hs_dev_records p_dev_rec = {0};
+#endif
esp_err_t err;
int i;
@@ -385,8 +396,9 @@ populate_db_from_nvs(int obj_type, void *dst, int *db_num)
for (i = 1; i <= get_nvs_max_obj_value(obj_type); i++) {
get_nvs_key_string(obj_type, i, key_string);
+#if MYNEWT_VAL(BLE_HOST_BASED_PRIVACY)
if (obj_type != BLE_STORE_OBJ_TYPE_PEER_DEV_REC) {
-
+#endif
err = get_nvs_db_value(obj_type, key_string, &cur);
if (err == ESP_ERR_NVS_NOT_FOUND) {
continue;
@@ -394,6 +406,7 @@ populate_db_from_nvs(int obj_type, void *dst, int *db_num)
ESP_LOGE(TAG, "NVS read operation failed !!");
return -1;
}
+#if MYNEWT_VAL(BLE_HOST_BASED_PRIVACY)
} else {
err = get_nvs_peer_record(key_string, &p_dev_rec);
if (err == ESP_ERR_NVS_NOT_FOUND) {
@@ -410,7 +423,9 @@ populate_db_from_nvs(int obj_type, void *dst, int *db_num)
memcpy(db_item, &p_dev_rec, sizeof(struct ble_hs_dev_records));
db_item += sizeof(struct ble_hs_dev_records);
(*db_num)++;
- } else {
+ } else
+#endif
+ {
if (obj_type == BLE_STORE_OBJ_TYPE_CCCD) {
ESP_LOGD(TAG, "CCCD in RAM is filled up from NVS index = %d", i);
memcpy(db_item, &cur.cccd, sizeof(struct ble_store_value_cccd));
@@ -492,6 +507,11 @@ int ble_store_config_persist_cccds(void)
union ble_store_value val;
nvs_count = get_nvs_db_attribute(BLE_STORE_OBJ_TYPE_CCCD, 0, NULL, 0);
+ if (nvs_count == -1) {
+ ESP_LOGE(TAG, "NVS operation failed while persisting CCCD");
+ return BLE_HS_ESTORE_FAIL;
+ }
+
if (nvs_count < ble_store_config_num_cccds) {
/* NVS db count less than RAM count, write operation */
@@ -518,6 +538,11 @@ int ble_store_config_persist_peer_secs(void)
union ble_store_value val;
nvs_count = get_nvs_db_attribute(BLE_STORE_OBJ_TYPE_PEER_SEC, 0, NULL, 0);
+ if (nvs_count == -1) {
+ ESP_LOGE(TAG, "NVS operation failed while persisting peer sec");
+ return BLE_HS_ESTORE_FAIL;
+ }
+
if (nvs_count < ble_store_config_num_peer_secs) {
/* NVS db count less than RAM count, write operation */
@@ -544,6 +569,11 @@ int ble_store_config_persist_our_secs(void)
union ble_store_value val;
nvs_count = get_nvs_db_attribute(BLE_STORE_OBJ_TYPE_OUR_SEC, 0, NULL, 0);
+ if (nvs_count == -1) {
+ ESP_LOGE(TAG, "NVS operation failed while persisting our sec");
+ return BLE_HS_ESTORE_FAIL;
+ }
+
if (nvs_count < ble_store_config_num_our_secs) {
/* NVS db count less than RAM count, write operation */
@@ -573,7 +603,13 @@ int ble_store_persist_peer_records(void)
struct ble_hs_dev_records *peer_dev_rec = ble_rpa_get_peer_dev_records();
nvs_count = get_nvs_db_attribute(BLE_STORE_OBJ_TYPE_PEER_DEV_REC, 0, NULL, 0);
+ if (nvs_count == -1) {
+ ESP_LOGE(TAG, "NVS operation failed while persisting peer_dev_rec");
+ return BLE_HS_ESTORE_FAIL;
+ }
+
if (nvs_count < ble_store_num_peer_dev_rec) {
+
/* NVS db count less than RAM count, write operation */
ESP_LOGD(TAG, "Persisting peer dev record to NVS...");
peer_rec = peer_dev_rec[ble_store_num_peer_dev_rec - 1];
diff --git a/lib/libesp32/NimBLE-Arduino/src/nimconfig.h b/lib/libesp32/NimBLE-Arduino/src/nimconfig.h
index 2f10fa2fc..d90921fa1 100644
--- a/lib/libesp32/NimBLE-Arduino/src/nimconfig.h
+++ b/lib/libesp32/NimBLE-Arduino/src/nimconfig.h
@@ -15,8 +15,13 @@
* 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) */
-#if defined(CONFIG_BT_NIMBLE_TASK_STACK_SIZE) || defined(CONFIG_NIMBLE_TASK_STACK_SIZE)
+/* Detect if using ESP-IDF or Arduino (Arduino won't have these defines in sdkconfig)
+ *
+ * Note: We do not use #ifdef CONFIG_BT_NIMBLE_ENABLED since we cannot enable NimBLE when using
+ * Arduino as a component and the esp-nimble-compnent, so we check if other config options are defined.
+ * We also need to use a config parameter that must be present and not likely defined in the command line.
+ */
+#if defined(CONFIG_BT_NIMBLE_GAP_DEVICE_NAME_MAX_LEN) || defined(CONFIG_NIMBLE_GAP_DEVICE_NAME_MAX_LEN)
#if defined(CONFIG_NIMBLE_ENABLED) && !defined(CONFIG_BT_NIMBLE_ENABLED)
#define CONFIG_BT_NIMBLE_ENABLED
@@ -51,22 +56,30 @@
/** @brief Comment out if not using NimBLE Client functions \n
* Reduces flash size by approx. 7kB.
*/
+#ifndef CONFIG_BT_NIMBLE_ROLE_CENTRAL_DISABLED
#define CONFIG_BT_NIMBLE_ROLE_CENTRAL
+#endif
/** @brief Comment out if not using NimBLE Scan functions \n
* Reduces flash size by approx. 26kB.
*/
+#ifndef CONFIG_BT_NIMBLE_ROLE_OBSERVER_DISABLED
#define CONFIG_BT_NIMBLE_ROLE_OBSERVER
+#endif
/** @brief Comment out if not using NimBLE Server functions \n
* Reduces flash size by approx. 16kB.
*/
-// #define CONFIG_BT_NIMBLE_ROLE_PERIPHERAL
+#ifndef CONFIG_BT_NIMBLE_ROLE_PERIPHERAL_DISABLED
+#define CONFIG_BT_NIMBLE_ROLE_PERIPHERAL
+#endif
/** @brief Comment out if not using NimBLE Advertising functions \n
* Reduces flash size by approx. 5kB.
*/
-// #define CONFIG_BT_NIMBLE_ROLE_BROADCASTER
+#ifndef CONFIG_BT_NIMBLE_ROLE_BROADCASTER_DISABLED
+#define CONFIG_BT_NIMBLE_ROLE_BROADCASTER
+#endif
/* Uncomment to see debug log messages from the NimBLE host
* Uses approx. 32kB of flash memory.
@@ -89,29 +102,46 @@
// #define CONFIG_NIMBLE_CPP_ENABLE_ADVERTISMENT_TYPE_TEXT
/** @brief Sets the core NimBLE host runs on */
+#ifndef CONFIG_BT_NIMBLE_PINNED_TO_CORE
#define CONFIG_BT_NIMBLE_PINNED_TO_CORE 0
+#endif
/** @brief Sets the stack size for the NimBLE host task */
+#ifndef CONFIG_BT_NIMBLE_TASK_STACK_SIZE
#define CONFIG_BT_NIMBLE_TASK_STACK_SIZE 4096
+#endif
/**
* @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`
*/
+#ifndef CONFIG_BT_NIMBLE_MEM_ALLOC_MODE_EXTERNAL
#define CONFIG_BT_NIMBLE_MEM_ALLOC_MODE_INTERNAL 1
+#endif
/** @brief Sets the number of simultaneous connections (esp controller max is 9) */
+#ifndef CONFIG_BT_NIMBLE_MAX_CONNECTIONS
#define CONFIG_BT_NIMBLE_MAX_CONNECTIONS 3
+#endif
/** @brief Sets the number of devices allowed to store/bond with */
+#ifndef CONFIG_BT_NIMBLE_MAX_BONDS
#define CONFIG_BT_NIMBLE_MAX_BONDS 3
+#endif
/** @brief Sets the maximum number of CCCD subscriptions to store */
+#ifndef CONFIG_BT_NIMBLE_MAX_CCCDS
#define CONFIG_BT_NIMBLE_MAX_CCCDS 8
+#endif
+
+/** @brief Default device name */
+#ifndef CONFIG_BT_NIMBLE_SVC_GAP_DEVICE_NAME
+#define CONFIG_BT_NIMBLE_SVC_GAP_DEVICE_NAME "nimble"
+#endif
/** @brief Set if CCCD's and bond data should be stored in NVS */
-#define CONFIG_BT_NIMBLE_NVS_PERSIST 0
+#define CONFIG_BT_NIMBLE_NVS_PERSIST 1
/** @brief Allow legacy paring */
#define CONFIG_BT_NIMBLE_SM_LEGACY 1
@@ -119,9 +149,6 @@
/** @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
@@ -154,7 +181,6 @@
*/
#define CONFIG_BT_NIMBLE_MSYS1_BLOCK_COUNT 12
-
/** @brief Random address refresh time in seconds */
#define CONFIG_BT_NIMBLE_RPA_TIMEOUT 900
diff --git a/lib/libesp32/rtsp/CRtspSession.cpp b/lib/libesp32/rtsp/CRtspSession.cpp
index 9d5718091..a14610038 100755
--- a/lib/libesp32/rtsp/CRtspSession.cpp
+++ b/lib/libesp32/rtsp/CRtspSession.cpp
@@ -34,8 +34,8 @@ void CRtspSession::Init()
bool CRtspSession::ParseRtspRequest(char const * aRequest, unsigned aRequestSize)
{
- char CmdName[RTSP_PARAM_STRING_MAX];
- static char CurRequest[RTSP_BUFFER_SIZE]; // Note: we assume single threaded, this large buf we keep off of the tiny stack
+ // char CmdName[RTSP_PARAM_STRING_MAX];
+ //char CurRequest[RTSP_BUFFER_SIZE]; // Note: we assume single threaded, this large buf we keep off of the tiny stack
unsigned CurRequestSize;
Init();
@@ -45,7 +45,7 @@ bool CRtspSession::ParseRtspRequest(char const * aRequest, unsigned aRequestSize
// check whether the request contains information about the RTP/RTCP UDP client ports (SETUP command)
char * ClientPortPtr;
char * TmpPtr;
- static char CP[1024];
+ char CP[128]; //static char CP[1024];
char * pCP;
ClientPortPtr = strstr(CurRequest,"client_port");
@@ -230,7 +230,7 @@ RTSP_CMD_TYPES CRtspSession::Handle_RtspRequest(char const * aRequest, unsigned
void CRtspSession::Handle_RtspOPTION()
{
- static char Response[1024]; // Note: we assume single threaded, this large buf we keep off of the tiny stack
+ //static char Response[1024]; // Note: we assume single threaded, this large buf we keep off of the tiny stack
snprintf(Response,sizeof(Response),
"RTSP/1.0 200 OK\r\nCSeq: %s\r\n"
@@ -241,9 +241,9 @@ void CRtspSession::Handle_RtspOPTION()
void CRtspSession::Handle_RtspDESCRIBE()
{
- static char Response[1024]; // Note: we assume single threaded, this large buf we keep off of the tiny stack
- static char SDPBuf[1024];
- static char URLBuf[1024];
+ //static char Response[1024]; // Note: we assume single threaded, this large buf we keep off of the tiny stack
+ char SDPBuf[128]; //static char SDPBuf[1024];
+ char URLBuf[128]; //static char URLBuf[1024];
// check whether we know a stream with the URL which is requested
m_StreamID = -1; // invalid URL
@@ -261,7 +261,7 @@ void CRtspSession::Handle_RtspDESCRIBE()
};
// simulate DESCRIBE server response
- static char OBuf[256];
+ // static char OBuf[256];
char * ColonPtr;
strcpy(OBuf,m_URLHostPort);
ColonPtr = strstr(OBuf,":");
@@ -305,8 +305,8 @@ void CRtspSession::Handle_RtspDESCRIBE()
void CRtspSession::Handle_RtspSETUP()
{
- static char Response[1024];
- static char Transport[255];
+ //static char Response[1024];
+ //static char Transport[255];
// init RTP streamer transport type (UDP or TCP) and ports for UDP transport
m_Streamer->InitTransport(m_ClientRTPPort,m_ClientRTCPPort,m_TcpTransport);
@@ -336,7 +336,7 @@ void CRtspSession::Handle_RtspSETUP()
void CRtspSession::Handle_RtspPLAY()
{
- static char Response[1024];
+ //static char Response[1024];
// simulate SETUP server response
snprintf(Response,sizeof(Response),
@@ -354,10 +354,10 @@ void CRtspSession::Handle_RtspPLAY()
char const * CRtspSession::DateHeader()
{
- static char buf[200];
+ //static char buf[200];
time_t tt = time(NULL);
- strftime(buf, sizeof buf, "Date: %a, %b %d %Y %H:%M:%S GMT", gmtime(&tt));
- return buf;
+ strftime(session_buf, sizeof(session_buf), "Date: %a, %b %d %Y %H:%M:%S GMT", gmtime(&tt));
+ return session_buf;
}
int CRtspSession::GetStreamID()
@@ -375,7 +375,7 @@ bool CRtspSession::handleRequests(uint32_t readTimeoutMs)
if(m_stopped)
return false; // Already closed down
- static char RecvBuf[RTSP_BUFFER_SIZE]; // Note: we assume single threaded, this large buf we keep off of the tiny stack
+ //char RecvBuf[RTSP_BUFFER_SIZE]; // Note: we assume single threaded, this large buf we keep off of the tiny stack
memset(RecvBuf,0x00,sizeof(RecvBuf));
int res = socketread(m_RtspClient,RecvBuf,sizeof(RecvBuf), readTimeoutMs);
diff --git a/lib/libesp32/rtsp/CRtspSession.h b/lib/libesp32/rtsp/CRtspSession.h
index 298bd6a15..79700bd60 100755
--- a/lib/libesp32/rtsp/CRtspSession.h
+++ b/lib/libesp32/rtsp/CRtspSession.h
@@ -70,4 +70,11 @@ private:
char m_CSeq[RTSP_PARAM_STRING_MAX]; // RTSP command sequence number
char m_URLHostPort[MAX_HOSTNAME_LEN]; // host:port part of the URL
unsigned m_ContentLength; // SDP string size
+ char CurRequest[RTSP_BUFFER_SIZE];
+ char RecvBuf[RTSP_BUFFER_SIZE];
+ char session_buf[128];
+ char CmdName[RTSP_PARAM_STRING_MAX];
+ char Transport[255];
+ char Response[1024];
+ char OBuf[256];
};
diff --git a/lib/libesp32/rtsp/CStreamer.cpp b/lib/libesp32/rtsp/CStreamer.cpp
index 26322c0fa..af4519ec7 100755
--- a/lib/libesp32/rtsp/CStreamer.cpp
+++ b/lib/libesp32/rtsp/CStreamer.cpp
@@ -48,7 +48,7 @@ int CStreamer::SendRtpPacket(unsigned const char * jpeg, int jpegLen, int fragme
bool includeQuantTbl = quant0tbl && quant1tbl && fragmentOffset == 0;
uint8_t q = includeQuantTbl ? 128 : 0x5e;
- static char RtpBuf[2048]; // Note: we assume single threaded, this large buf we keep off of the tiny stack
+ //static char RtpBuf[2048]; // Note: we assume single threaded, this large buf we keep off of the tiny stack
int RtpPacketSize = fragmentLen + KRtpHeaderSize + KJpegHeaderSize + (includeQuantTbl ? (4 + 64 * 2) : 0);
memset(RtpBuf,0x00,sizeof(RtpBuf));
diff --git a/lib/libesp32/rtsp/CStreamer.h b/lib/libesp32/rtsp/CStreamer.h
index 78d112b5b..58a9983e2 100755
--- a/lib/libesp32/rtsp/CStreamer.h
+++ b/lib/libesp32/rtsp/CStreamer.h
@@ -39,6 +39,7 @@ private:
u_short m_width; // image data info
u_short m_height;
+ char RtpBuf[2048];
};
diff --git a/lib/libesp32/rtsp/LICENSE b/lib/libesp32/rtsp/LICENSE
new file mode 100755
index 000000000..136bbd239
--- /dev/null
+++ b/lib/libesp32/rtsp/LICENSE
@@ -0,0 +1,7 @@
+Copyright 2018 S. Kevin Hester-Chow, kevinh@geeksville.com (MIT License)
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/lib/libesp32/rtsp/README.md b/lib/libesp32/rtsp/README.md
new file mode 100755
index 000000000..37d6aec65
--- /dev/null
+++ b/lib/libesp32/rtsp/README.md
@@ -0,0 +1,93 @@
+# Micro-RTSP
+
+This is a small library which can be used to serve up RTSP streams from
+resource constrained MCUs. It lets you trivially make a $10 open source
+RTSP video stream camera.
+
+# Usage
+
+This library works for ESP32/arduino targets but also for most any posixish platform.
+
+## Example arduino/ESP32 usage
+
+This library will work standalone, but it is _super_ easy to use if your app is platform.io based.
+Just "pio lib install Micro-RTSP" to pull the latest version from their library server. If you want to use the OV2640
+camera support you'll need to be targeting the espressif32 platform in your project.
+
+See the [example platform.io app](/examples). It should build and run on virtually any of the $10
+ESP32-CAM boards (such as M5CAM). The relevant bit of the code is included below. In short:
+1. Listen for a TCP connection on the RTSP port with accept()
+2. When a connection comes in, create a CRtspSession and OV2640Streamer camera streamer objects.
+3. While the connection remains, call session->handleRequests(0) to handle any incoming client requests.
+4. Every 100ms or so call session->broadcastCurrentFrame() to send new frames to any clients.
+
+```
+void loop()
+{
+ uint32_t msecPerFrame = 100;
+ static uint32_t lastimage = millis();
+
+ // If we have an active client connection, just service that until gone
+ // (FIXME - support multiple simultaneous clients)
+ if(session) {
+ session->handleRequests(0); // we don't use a timeout here,
+ // instead we send only if we have new enough frames
+
+ uint32_t now = millis();
+ if(now > lastimage + msecPerFrame || now < lastimage) { // handle clock rollover
+ session->broadcastCurrentFrame(now);
+ lastimage = now;
+
+ // check if we are overrunning our max frame rate
+ now = millis();
+ if(now > lastimage + msecPerFrame)
+ printf("warning exceeding max frame rate of %d ms\n", now - lastimage);
+ }
+
+ if(session->m_stopped) {
+ delete session;
+ delete streamer;
+ session = NULL;
+ streamer = NULL;
+ }
+ }
+ else {
+ client = rtspServer.accept();
+
+ if(client) {
+ //streamer = new SimStreamer(&client, true); // our streamer for UDP/TCP based RTP transport
+ streamer = new OV2640Streamer(&client, cam); // our streamer for UDP/TCP based RTP transport
+
+ session = new CRtspSession(&client, streamer); // our threads RTSP session and state
+ }
+ }
+}
+```
+## Example posix/linux usage
+
+There is a small standalone example [here](/test/RTSPTestServer.cpp). You can build it by following [these](/test/README.md) directions. The usage of the two key classes (CRtspSession and SimStreamer) are very similar to to the ESP32 usage.
+
+## Supporting new camera devices
+
+Supporting new camera devices is quite simple. See OV2640Streamer for an example and implement streamImage()
+by reading a frame from your camera.
+
+# Structure and design notes
+
+# Issues and sending pull requests
+
+Please report issues and send pull requests. I'll happily reply. ;-)
+
+# Credits
+
+The server code was initially based on a great 2013 [tutorial](https://www.medialan.de/usecase0001.html) by Medialan.
+
+# License
+
+Copyright 2018 S. Kevin Hester-Chow, kevinh@geeksville.com (MIT License)
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/lib/libesp32/rtsp/library.properties b/lib/libesp32/rtsp/library.properties
new file mode 100755
index 000000000..03b5dc224
--- /dev/null
+++ b/lib/libesp32/rtsp/library.properties
@@ -0,0 +1,9 @@
+name=Micro-RTSP
+version=0.1.6
+author=Kevin Hester
+maintainer=Kevin Hester
+sentence=Mikro RTSP server for mikros
+paragraph=A small/efficient RTSP server for ESP32 and other micros
+category=Data Storage
+url=https://github.com/geeksville/Micro-RTSP.git
+architectures=*
diff --git a/pio-tools/gzip-firmware.py b/pio-tools/gzip-firmware.py
index e29c05576..bb1759123 100644
--- a/pio-tools/gzip-firmware.py
+++ b/pio-tools/gzip-firmware.py
@@ -3,26 +3,32 @@ import os
import shutil
import gzip
-OUTPUT_DIR = "build_output{}".format(os.path.sep)
+platform = env.PioPlatform()
+board = env.BoardConfig()
+mcu = board.get("build.mcu", "esp32")
+# gzip only for ESP8266
+if env["PIOPLATFORM"] != "espressif32":
-def bin_gzip(source, target, env):
- 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)
- gzip_file = "{}firmware{}{}.bin.gz".format(OUTPUT_DIR, os.path.sep, variant)
+ OUTPUT_DIR = "build_output{}".format(os.path.sep)
- # check if new target files exist and remove if necessary
- if os.path.isfile(gzip_file): os.remove(gzip_file)
+ def bin_gzip(source, target, env):
+ variant = str(target[0]).split(os.path.sep)[2]
- # write gzip firmware file
- with open(bin_file,"rb") as fp:
- with gzip.open(gzip_file, "wb", compresslevel = 9) as f:
- shutil.copyfileobj(fp, f)
-
- ORG_FIRMWARE_SIZE = os.stat(bin_file).st_size
- GZ_FIRMWARE_SIZE = os.stat(gzip_file).st_size
+ # create string with location and file names based on variant
+ bin_file = "{}firmware{}{}.bin".format(OUTPUT_DIR, os.path.sep, variant)
+ gzip_file = "{}firmware{}{}.bin.gz".format(OUTPUT_DIR, os.path.sep, variant)
- print("Compression reduced firmware size by {:.0f}% (was {} bytes, now {} bytes)".format((GZ_FIRMWARE_SIZE / ORG_FIRMWARE_SIZE) * 100, ORG_FIRMWARE_SIZE, GZ_FIRMWARE_SIZE))
+ # check if new target files exist and remove if necessary
+ if os.path.isfile(gzip_file): os.remove(gzip_file)
-env.AddPostAction("$BUILD_DIR/${PROGNAME}.bin", [bin_gzip])
+ # write gzip firmware file
+ with open(bin_file,"rb") as fp:
+ with gzip.open(gzip_file, "wb", compresslevel = 9) as f:
+ shutil.copyfileobj(fp, f)
+
+ ORG_FIRMWARE_SIZE = os.stat(bin_file).st_size
+ GZ_FIRMWARE_SIZE = os.stat(gzip_file).st_size
+
+ print("Compression reduced firmware size by {:.0f}% (was {} bytes, now {} bytes)".format((GZ_FIRMWARE_SIZE / ORG_FIRMWARE_SIZE) * 100, ORG_FIRMWARE_SIZE, GZ_FIRMWARE_SIZE))
+
+ env.AddPostAction("$BUILD_DIR/${PROGNAME}.bin", [bin_gzip])
diff --git a/platformio_override_sample.ini b/platformio_override_sample.ini
index 4db6a41b3..5bdf1a7f2 100644
--- a/platformio_override_sample.ini
+++ b/platformio_override_sample.ini
@@ -37,6 +37,7 @@ default_envs =
; tasmota32-ircustom
; tasmota32solo1
; tasmota32-odroidgo
+; tasmota32-core2
[common]
@@ -165,7 +166,7 @@ lib_extra_dirs =
; *** EXPERIMENTAL Tasmota version for ESP32solo1 (used in some Xiaomi devices)
[env:tasmota32solo1]
extends = env:tasmota32
-platform_packages = framework-arduinoespressif32 @ https://github.com/Jason2866/esp32-arduino-lib-builder/raw/framework-arduinoespressif32/framework-arduinoespressif32-release_v3.3-solo1-4b325f52e.tar.gz
+platform_packages = framework-arduinoespressif32 @ https://github.com/Jason2866/esp32-arduino-lib-builder/raw/framework-arduinoespressif32/framework-arduinoespressif32-solo1-release_v3.3-7e63061fa.tar.gz
platformio/tool-mklittlefs @ ~1.203.200522
platformio/tool-esptoolpy @ ~1.30000.0
build_unflags = ${esp32_defaults.build_unflags}
diff --git a/platformio_tasmota32.ini b/platformio_tasmota32.ini
index a41222bb4..eebc3d1a7 100644
--- a/platformio_tasmota32.ini
+++ b/platformio_tasmota32.ini
@@ -9,6 +9,7 @@ default_envs = ${build_envs.default_envs}
; tasmota32
; tasmota32-webcam
; tasmota32-odroidgo
+; tasmota32-core2
; tasmota32-minimal
; tasmota32-lite
; tasmota32-knx
@@ -86,11 +87,13 @@ build_flags = ${esp_defaults.build_flags}
-Dsint16_t=int16_t
-Dmemcpy_P=memcpy
-Dmemcmp_P=memcmp
+ ;for TLS we can afford compiling for 4K RSA keys
+ -DUSE_4K_RSA
[core32]
-platform = espressif32 @ 2.1.0
-platform_packages = framework-arduinoespressif32 @ https://github.com/Jason2866/arduino-esp32/releases/download/1.0.5-rc4/esp32-1.0.5-rc4.zip
+platform = espressif32 @ 3.0.0
+platform_packages = framework-arduinoespressif32 @ https://github.com/Jason2866/arduino-esp32/releases/download/1.0.5-rc6/esp32-1.0.5-rc6.zip
platformio/tool-mklittlefs @ ~1.203.200522
build_unflags = ${esp32_defaults.build_unflags}
build_flags = ${esp32_defaults.build_flags}
diff --git a/platformio_tasmota_env32.ini b/platformio_tasmota_env32.ini
index 108792fce..7685d45f3 100644
--- a/platformio_tasmota_env32.ini
+++ b/platformio_tasmota_env32.ini
@@ -40,10 +40,24 @@ lib_extra_dirs = lib/libesp32, lib/lib_basic
extends = env:tasmota32
board = odroid_esp32
board_build.f_cpu = 240000000L
+board_build.flash_mode = qio
+board_build.f_flash = 80000000L
+upload_speed = 2000000
board_build.partitions = esp32_partition_app1984k_spiffs12M.csv
build_flags = ${common32.build_flags} -DBOARD_HAS_PSRAM -mfix-esp32-psram-cache-issue -DFIRMWARE_ODROID_GO
lib_extra_dirs = lib/libesp32, lib/lib_basic, lib/lib_i2c, lib/lib_rf, lib/lib_div, lib/lib_ssl, lib/lib_display
+[env:tasmota32-core2]
+extends = env:tasmota32
+board = odroid_esp32
+board_build.f_cpu = 240000000L
+board_build.flash_mode = qio
+board_build.f_flash = 80000000L
+upload_speed = 2000000
+board_build.partitions = esp32_partition_app1984k_spiffs12M.csv
+build_flags = ${common32.build_flags} -DBOARD_HAS_PSRAM -mfix-esp32-psram-cache-issue -DFIRMWARE_M5STACK_CORE2
+lib_extra_dirs = lib/libesp32, lib/lib_basic, lib/lib_i2c, lib/lib_rf, lib/lib_div, lib/lib_ssl, lib/lib_display, lib/lib_audio
+
[env:tasmota32-minimal]
extends = env:tasmota32
build_flags = ${common32.build_flags} -DFIRMWARE_MINIMAL
diff --git a/tasmota/WiFiClientSecureLightBearSSL.cpp b/tasmota/WiFiClientSecureLightBearSSL.cpp
index 6d7ed37b4..60534c369 100755
--- a/tasmota/WiFiClientSecureLightBearSSL.cpp
+++ b/tasmota/WiFiClientSecureLightBearSSL.cpp
@@ -804,6 +804,7 @@ extern "C" {
return 0;
}
+#ifndef USE_MQTT_TLS_DROP_OLD_FINGERPRINT
// No match under new algorithm, do some basic checking on the key.
//
// RSA keys normally have an e value of 65537, which is three bytes long.
@@ -838,6 +839,9 @@ extern "C" {
pubkeyfingerprint_pubkey_fingerprint(xc, false);
return 0;
+#else // USE_TLS_OLD_FINGERPRINT_COMPAT
+ return 1; // no match, error
+#endif // USE_TLS_OLD_FINGERPRINT_COMPAT
} else {
// Default (no validation at all) or no errors in prior checks = success.
return 0;
diff --git a/tasmota/i18n.h b/tasmota/i18n.h
index 3262bef09..daf55043e 100644
--- a/tasmota/i18n.h
+++ b/tasmota/i18n.h
@@ -156,6 +156,7 @@
#define D_JSON_SERIALRECEIVED "SerialReceived"
#define D_JSON_SET "Set"
#define D_JSON_SIGNAL "Signal"
+#define D_JSON_SIZE "Size"
#define D_JSON_SPEED "Speed"
#define D_JSON_SPEED_UNIT "SpeedUnit"
#define D_JSON_SSID "SSId"
@@ -180,6 +181,7 @@
#define D_JSON_TOTAL_START_TIME "TotalStartTime"
#define D_JSON_TVOC "TVOC"
#define D_JSON_TYPE "Type"
+#define D_JSON_UID "UID"
#define D_JSON_UPTIME "Uptime"
#define D_JSON_UTC_TIME "UTC"
#define D_JSON_UV_INDEX "UvIndex"
@@ -347,7 +349,11 @@
#define D_CMND_CPU_FREQUENCY "CpuFrequency"
#endif // ESP32
-// Commands xdrv_01_mqtt.ino
+// Commands xdrv_02_mqtt.ino
+#define D_SO_MQTTJSONONLY "MqttJSONOnly"
+#define D_SO_MQTTTLS "MqttTLS"
+#define D_SO_MQTTNORETAIN "MqttNoRetain"
+#define D_SO_MQTTDETACHRELAY "MqttDetachRelay"
#define D_CMND_MQTTLOG "MqttLog"
#define D_CMND_MQTTHOST "MqttHost"
#define D_CMND_MQTTPORT "MqttPort"
@@ -373,7 +379,7 @@
#define D_CMND_SENSORRETAIN "SensorRetain"
#define D_CMND_PUBLISH "Publish"
-// Commands xdrv_02_webserver.ino
+// Commands xdrv_01_webserver.ino
#define D_CMND_WEBSERVER "Webserver"
#define D_JSON_WEBSERVER_MODE "WebServerMode"
#define D_JSON_ACTIVE_FOR "Active for"
@@ -418,6 +424,13 @@
#define D_JSON_MAXENERGYREACHED "MaxEnergyReached"
// Commands xdrv_04_light.ino
+#define D_SO_CHANNELREMAP "ChannelRemap" // SO37
+#define D_SO_MULTIPWM "MultiPWM" // SO68
+#define D_SO_ALEXACTRANGE "AlexaCTRange" // SO82
+#define D_SO_POWERONFADE "PowerOnFade" // SO91
+#define D_SO_PWMCT "PWMCT" // SO92
+#define D_SO_WHITEBLEND "WhiteBlend" // SO105
+#define D_SO_VIRTUALCT "VirtualCT" // SO106
#define D_CMND_CHANNEL "Channel"
#define D_CMND_COLOR "Color"
#define D_CMND_COLORTEMPERATURE "CT"
@@ -520,7 +533,16 @@
// Commands xdrv_23_zigbee.ino
#define D_PRFX_ZB "Zb"
-#define D_ZIGBEE_NOT_STARTED "Zigbee not started"
+#define D_SO_ZIGBEE_NAMEKEY "NameKey"
+#define D_SO_ZIGBEE_DEVICETOPIC "DeviceTopic"
+#define D_SO_ZIGBEE_NOPREFIX "NoPrefix"
+#define D_SO_ZIGBEE_ENDPOINTSUFFIX "EndpointSuffix"
+#define D_SO_ZIGBEE_NOAUTOBIND "NoAutoBind"
+#define D_SO_ZIGBEE_NAMETOPIC "NameTopic"
+#define D_SO_ZIGBEE_ENDPOINTTOPIC "EndpointTopic"
+#define D_SO_ZIGBEE_NOAUTOQUERY "NoAutoQuery"
+#define D_SO_ZIGBEE_ZBRECEIVEDTOPIC "ReceivedTopic"
+#define D_SO_ZIGBEE_OMITDEVICE "OmitDevice"
#define D_CMND_ZIGBEE_PERMITJOIN "PermitJoin"
#define D_CMND_ZIGBEE_STATUS "Status"
#define D_CMND_ZIGBEE_RESET "Reset"
@@ -727,7 +749,6 @@ const char S_JSON_COMMAND_INDEX_NVALUE[] PROGMEM = "{\"%s%d\":%d}";
const char S_JSON_COMMAND_INDEX_LVALUE[] PROGMEM = "{\"%s%d\":%lu}";
const char S_JSON_COMMAND_INDEX_SVALUE[] PROGMEM = "{\"%s%d\":\"%s\"}";
const char S_JSON_COMMAND_INDEX_ASTERISK[] PROGMEM = "{\"%s%d\":\"" D_ASTERISK_PWD "\"}";
-const char S_JSON_COMMAND_INDEX_SVALUE_SVALUE[] PROGMEM = "{\"%s%d\":\"%s%s\"}";
const char S_JSON_SENSOR_INDEX_NVALUE[] PROGMEM = "{\"" D_CMND_SENSOR "%d\":%d}";
const char S_JSON_SENSOR_INDEX_SVALUE[] PROGMEM = "{\"" D_CMND_SENSOR "%d\":\"%s\"}";
@@ -737,6 +758,7 @@ const char S_JSON_DRIVER_INDEX_SVALUE[] PROGMEM = "{\"" D_CMND_DRIVE
const char S_JSON_SVALUE_ACTION_SVALUE[] PROGMEM = "{\"%s\":{\"Action\":\"%s\"}}";
+const char JSON_SNS_F_TEMP[] PROGMEM = ",\"%s\":{\"" D_JSON_TEMPERATURE "\":%*_f}";
const char JSON_SNS_TEMP[] PROGMEM = ",\"%s\":{\"" D_JSON_TEMPERATURE "\":%s}";
const char JSON_SNS_ILLUMINANCE[] PROGMEM = ",\"%s\":{\"" D_JSON_ILLUMINANCE "\":%d}";
@@ -769,7 +791,9 @@ const float kSpeedConversionFactor[] = {1, // none
// xdrv_02_webserver.ino
#ifdef USE_WEBSERVER
// {s} = , {m} = | , {e} = |
-const char HTTP_SNS_TEMP[] PROGMEM = "{s}%s " D_TEMPERATURE "{m}%s " D_UNIT_DEGREE "%c{e}";
+const char HTTP_SNS_F_TEMP[] PROGMEM = "{s}%s " D_TEMPERATURE "{m}%*_f " D_UNIT_DEGREE "%c{e}";
+//const char HTTP_SNS_TEMP[] PROGMEM = "{s}%s " D_TEMPERATURE "{m}%s " D_UNIT_DEGREE "%c{e}";
+
const char HTTP_SNS_HUM[] PROGMEM = "{s}%s " D_HUMIDITY "{m}%s " D_UNIT_PERCENT "{e}";
const char HTTP_SNS_DEW[] PROGMEM = "{s}%s " D_DEWPOINT "{m}%s " D_UNIT_DEGREE "%c{e}";
const char HTTP_SNS_PRESSURE[] PROGMEM = "{s}%s " D_PRESSURE "{m}%s " "%s{e}";
@@ -782,6 +806,7 @@ const char HTTP_SNS_GALLONS[] PROGMEM = "{s}%s " D_TOTAL_USAGE "{
const char HTTP_SNS_GPM[] PROGMEM = "{s}%s " D_FLOW_RATE "{m}%s " D_UNIT_GALLONS_PER_MIN "{e}";
const char HTTP_SNS_MOISTURE[] PROGMEM = "{s}%s " D_MOISTURE "{m}%d " D_UNIT_PERCENT "{e}";
const char HTTP_SNS_RANGE[] PROGMEM = "{s}%s " D_RANGE "{m}%d" "{e}";
+const char HTTP_SNS_DISTANCE[] PROGMEM = "{s}%s " D_DISTANCE "{m}%d " D_UNIT_MILLIMETER "{e}";
const char HTTP_SNS_VOLTAGE[] PROGMEM = "{s}" D_VOLTAGE "{m}%s " D_UNIT_VOLT "{e}";
const char HTTP_SNS_CURRENT[] PROGMEM = "{s}" D_CURRENT "{m}%s " D_UNIT_AMPERE "{e}";
const char HTTP_SNS_POWER[] PROGMEM = "{s}" D_POWERUSAGE "{m}%s " D_UNIT_WATT "{e}";
diff --git a/tasmota/language/af_AF.h b/tasmota/language/af_AF.h
index f6fb4a662..6013234df 100644
--- a/tasmota/language/af_AF.h
+++ b/tasmota/language/af_AF.h
@@ -782,6 +782,11 @@
#define D_SENSOR_SSD1331_CS "SSD1331 CS"
#define D_SENSOR_SSD1331_DC "SSD1331 DC"
#define D_SENSOR_SDCARD_CS "SDCard CS"
+#define D_SENSOR_WIEGAND_D0 "Wiegand D0"
+#define D_SENSOR_WIEGAND_D1 "Wiegand D1"
+#define D_SENSOR_NEOPOOL_TX "NeoPool Tx"
+#define D_SENSOR_NEOPOOL_RX "NeoPool Rx"
+
// Units
#define D_UNIT_AMPERE "A"
@@ -953,4 +958,56 @@
#define D_FP_PASSVERIFY "Wagwoord geverifieer" // 0x21 Verify the fingerprint passed
#define D_FP_UNKNOWNERROR "Fout" // Any other error
+// xsns_83_neopool.ino
+#define D_NEOPOOL_MACH_NONE "NeoPool" // Machine names
+#define D_NEOPOOL_MACH_HIDROLIFE "Hidrolife (yellow)"
+#define D_NEOPOOL_MACH_AQUASCENIC "Aquascenic (blue)"
+#define D_NEOPOOL_MACH_OXILIFE "Oxilife (green)"
+#define D_NEOPOOL_MACH_BIONET "Bionet (light blue)"
+#define D_NEOPOOL_MACH_HIDRONISER "Hidroniser (red)"
+#define D_NEOPOOL_MACH_UVSCENIC "UVScenic (lilac)"
+#define D_NEOPOOL_MACH_STATION "Station (orange)"
+#define D_NEOPOOL_MACH_BRILIX "Brilix"
+#define D_NEOPOOL_MACH_GENERIC "Generic"
+#define D_NEOPOOL_MACH_BAYROL "Bayrol"
+#define D_NEOPOOL_MACH_HAY "Hay"
+#define D_NEOPOOL_FILTRATION_MANUAL "Manual" // Filtration modes
+#define D_NEOPOOL_FILTRATION_AUTO "Auto"
+#define D_NEOPOOL_FILTRATION_HEATING "Heating"
+#define D_NEOPOOL_FILTRATION_SMART "Smart"
+#define D_NEOPOOL_FILTRATION_INTELLIGENT "Intelligent"
+#define D_NEOPOOL_FILTRATION_BACKWASH "Backwash"
+#define D_NEOPOOL_FILTRATION_NONE "" // Filtration speed level
+#define D_NEOPOOL_FILTRATION_SLOW "slow"
+#define D_NEOPOOL_FILTRATION_MEDIUM "medium"
+#define D_NEOPOOL_FILTRATION_FAST "fast"
+#define D_NEOPOOL_TYPE "Type" // Sensor & relais names
+#define D_NEOPOOL_REDOX "Redox"
+#define D_NEOPOOL_CHLORINE "Chlorine"
+#define D_NEOPOOL_CONDUCTIVITY "Conductivity"
+#define D_NEOPOOL_IONIZATION "Ionization"
+#define D_NEOPOOL_HYDROLYSIS "Hydrolysis"
+#define D_NEOPOOL_RELAY "Relay"
+#define D_NEOPOOL_RELAY_FILTRATION "Filtration"
+#define D_NEOPOOL_RELAY_LIGHT "Light"
+#define D_NEOPOOL_RELAY_PH_ACID "Acid pump"
+#define D_NEOPOOL_RELAY_PH_BASE "Base pump"
+#define D_NEOPOOL_RELAY_RX "Redox level"
+#define D_NEOPOOL_RELAY_CL "Chlorine pump"
+#define D_NEOPOOL_RELAY_CD "Brine pump"
+#define D_NEOPOOL_TIME "Time"
+#define D_NEOPOOL_FILT_MODE "Filtration"
+#define D_NEOPOOL_POLARIZATION "Pol" // Sensor status
+#define D_NEOPOOL_PR_OFF "PrOff"
+#define D_NEOPOOL_SETPOINT_OK "Ok"
+#define D_NEOPOOL_COVER "Cover"
+#define D_NEOPOOL_SHOCK "Shock"
+#define D_NEOPOOL_ALARM "! "
+#define D_NEOPOOL_LOW "Low"
+#define D_NEOPOOL_FLOW1 "FL1"
+#define D_NEOPOOL_FLOW2 "FL2"
+#define D_NEOPOOL_PH_HIGH "too high" // ph Alarms
+#define D_NEOPOOL_PH_LOW "too low"
+#define D_NEOPOOL_PUMP_TIME_EXCEEDED "pump time exceeded"
+
#endif // _LANGUAGE_AF_AF_H_
diff --git a/tasmota/language/bg_BG.h b/tasmota/language/bg_BG.h
index f00b30d2c..30e64a2ea 100644
--- a/tasmota/language/bg_BG.h
+++ b/tasmota/language/bg_BG.h
@@ -781,6 +781,11 @@
#define D_SENSOR_SSD1331_CS "SSD1331 CS"
#define D_SENSOR_SSD1331_DC "SSD1331 DC"
#define D_SENSOR_SDCARD_CS "SDCard CS"
+#define D_SENSOR_WIEGAND_D0 "Wiegand D0"
+#define D_SENSOR_WIEGAND_D1 "Wiegand D1"
+#define D_SENSOR_NEOPOOL_TX "NeoPool Tx"
+#define D_SENSOR_NEOPOOL_RX "NeoPool Rx"
+
// Units
#define D_UNIT_AMPERE "A"
@@ -952,4 +957,56 @@
#define D_FP_PASSVERIFY "Password verified" // 0x21 Verify the fingerprint passed
#define D_FP_UNKNOWNERROR "Error" // Any other error
+// xsns_83_neopool.ino
+#define D_NEOPOOL_MACH_NONE "NeoPool" // Machine names
+#define D_NEOPOOL_MACH_HIDROLIFE "Hidrolife (yellow)"
+#define D_NEOPOOL_MACH_AQUASCENIC "Aquascenic (blue)"
+#define D_NEOPOOL_MACH_OXILIFE "Oxilife (green)"
+#define D_NEOPOOL_MACH_BIONET "Bionet (light blue)"
+#define D_NEOPOOL_MACH_HIDRONISER "Hidroniser (red)"
+#define D_NEOPOOL_MACH_UVSCENIC "UVScenic (lilac)"
+#define D_NEOPOOL_MACH_STATION "Station (orange)"
+#define D_NEOPOOL_MACH_BRILIX "Brilix"
+#define D_NEOPOOL_MACH_GENERIC "Generic"
+#define D_NEOPOOL_MACH_BAYROL "Bayrol"
+#define D_NEOPOOL_MACH_HAY "Hay"
+#define D_NEOPOOL_FILTRATION_MANUAL "Manual" // Filtration modes
+#define D_NEOPOOL_FILTRATION_AUTO "Auto"
+#define D_NEOPOOL_FILTRATION_HEATING "Heating"
+#define D_NEOPOOL_FILTRATION_SMART "Smart"
+#define D_NEOPOOL_FILTRATION_INTELLIGENT "Intelligent"
+#define D_NEOPOOL_FILTRATION_BACKWASH "Backwash"
+#define D_NEOPOOL_FILTRATION_NONE "" // Filtration speed level
+#define D_NEOPOOL_FILTRATION_SLOW "slow"
+#define D_NEOPOOL_FILTRATION_MEDIUM "medium"
+#define D_NEOPOOL_FILTRATION_FAST "fast"
+#define D_NEOPOOL_TYPE "Type" // Sensor & relais names
+#define D_NEOPOOL_REDOX "Redox"
+#define D_NEOPOOL_CHLORINE "Chlorine"
+#define D_NEOPOOL_CONDUCTIVITY "Conductivity"
+#define D_NEOPOOL_IONIZATION "Ionization"
+#define D_NEOPOOL_HYDROLYSIS "Hydrolysis"
+#define D_NEOPOOL_RELAY "Relay"
+#define D_NEOPOOL_RELAY_FILTRATION "Filtration"
+#define D_NEOPOOL_RELAY_LIGHT "Light"
+#define D_NEOPOOL_RELAY_PH_ACID "Acid pump"
+#define D_NEOPOOL_RELAY_PH_BASE "Base pump"
+#define D_NEOPOOL_RELAY_RX "Redox level"
+#define D_NEOPOOL_RELAY_CL "Chlorine pump"
+#define D_NEOPOOL_RELAY_CD "Brine pump"
+#define D_NEOPOOL_TIME "Time"
+#define D_NEOPOOL_FILT_MODE "Filtration"
+#define D_NEOPOOL_POLARIZATION "Pol" // Sensor status
+#define D_NEOPOOL_PR_OFF "PrOff"
+#define D_NEOPOOL_SETPOINT_OK "Ok"
+#define D_NEOPOOL_COVER "Cover"
+#define D_NEOPOOL_SHOCK "Shock"
+#define D_NEOPOOL_ALARM "! "
+#define D_NEOPOOL_LOW "Low"
+#define D_NEOPOOL_FLOW1 "FL1"
+#define D_NEOPOOL_FLOW2 "FL2"
+#define D_NEOPOOL_PH_HIGH "too high" // ph Alarms
+#define D_NEOPOOL_PH_LOW "too low"
+#define D_NEOPOOL_PUMP_TIME_EXCEEDED "pump time exceeded"
+
#endif // _LANGUAGE_BG_BG_H_
diff --git a/tasmota/language/cs_CZ.h b/tasmota/language/cs_CZ.h
index d529a4e16..005f7d1b6 100644
--- a/tasmota/language/cs_CZ.h
+++ b/tasmota/language/cs_CZ.h
@@ -782,6 +782,11 @@
#define D_SENSOR_SSD1331_CS "SSD1331 CS"
#define D_SENSOR_SSD1331_DC "SSD1331 DC"
#define D_SENSOR_SDCARD_CS "SDCard CS"
+#define D_SENSOR_WIEGAND_D0 "Wiegand D0"
+#define D_SENSOR_WIEGAND_D1 "Wiegand D1"
+#define D_SENSOR_NEOPOOL_TX "NeoPool Tx"
+#define D_SENSOR_NEOPOOL_RX "NeoPool Rx"
+
// Units
#define D_UNIT_AMPERE "A"
@@ -953,4 +958,56 @@
#define D_FP_PASSVERIFY "Password verified" // 0x21 Verify the fingerprint passed
#define D_FP_UNKNOWNERROR "Error" // Any other error
+// xsns_83_neopool.ino
+#define D_NEOPOOL_MACH_NONE "NeoPool" // Machine names
+#define D_NEOPOOL_MACH_HIDROLIFE "Hidrolife (yellow)"
+#define D_NEOPOOL_MACH_AQUASCENIC "Aquascenic (blue)"
+#define D_NEOPOOL_MACH_OXILIFE "Oxilife (green)"
+#define D_NEOPOOL_MACH_BIONET "Bionet (light blue)"
+#define D_NEOPOOL_MACH_HIDRONISER "Hidroniser (red)"
+#define D_NEOPOOL_MACH_UVSCENIC "UVScenic (lilac)"
+#define D_NEOPOOL_MACH_STATION "Station (orange)"
+#define D_NEOPOOL_MACH_BRILIX "Brilix"
+#define D_NEOPOOL_MACH_GENERIC "Generic"
+#define D_NEOPOOL_MACH_BAYROL "Bayrol"
+#define D_NEOPOOL_MACH_HAY "Hay"
+#define D_NEOPOOL_FILTRATION_MANUAL "Manual" // Filtration modes
+#define D_NEOPOOL_FILTRATION_AUTO "Auto"
+#define D_NEOPOOL_FILTRATION_HEATING "Heating"
+#define D_NEOPOOL_FILTRATION_SMART "Smart"
+#define D_NEOPOOL_FILTRATION_INTELLIGENT "Intelligent"
+#define D_NEOPOOL_FILTRATION_BACKWASH "Backwash"
+#define D_NEOPOOL_FILTRATION_NONE "" // Filtration speed level
+#define D_NEOPOOL_FILTRATION_SLOW "slow"
+#define D_NEOPOOL_FILTRATION_MEDIUM "medium"
+#define D_NEOPOOL_FILTRATION_FAST "fast"
+#define D_NEOPOOL_TYPE "Type" // Sensor & relais names
+#define D_NEOPOOL_REDOX "Redox"
+#define D_NEOPOOL_CHLORINE "Chlorine"
+#define D_NEOPOOL_CONDUCTIVITY "Conductivity"
+#define D_NEOPOOL_IONIZATION "Ionization"
+#define D_NEOPOOL_HYDROLYSIS "Hydrolysis"
+#define D_NEOPOOL_RELAY "Relay"
+#define D_NEOPOOL_RELAY_FILTRATION "Filtration"
+#define D_NEOPOOL_RELAY_LIGHT "Light"
+#define D_NEOPOOL_RELAY_PH_ACID "Acid pump"
+#define D_NEOPOOL_RELAY_PH_BASE "Base pump"
+#define D_NEOPOOL_RELAY_RX "Redox level"
+#define D_NEOPOOL_RELAY_CL "Chlorine pump"
+#define D_NEOPOOL_RELAY_CD "Brine pump"
+#define D_NEOPOOL_TIME "Time"
+#define D_NEOPOOL_FILT_MODE "Filtration"
+#define D_NEOPOOL_POLARIZATION "Pol" // Sensor status
+#define D_NEOPOOL_PR_OFF "PrOff"
+#define D_NEOPOOL_SETPOINT_OK "Ok"
+#define D_NEOPOOL_COVER "Cover"
+#define D_NEOPOOL_SHOCK "Shock"
+#define D_NEOPOOL_ALARM "! "
+#define D_NEOPOOL_LOW "Low"
+#define D_NEOPOOL_FLOW1 "FL1"
+#define D_NEOPOOL_FLOW2 "FL2"
+#define D_NEOPOOL_PH_HIGH "too high" // ph Alarms
+#define D_NEOPOOL_PH_LOW "too low"
+#define D_NEOPOOL_PUMP_TIME_EXCEEDED "pump time exceeded"
+
#endif // _LANGUAGE_CS_CZ_H_
diff --git a/tasmota/language/de_DE.h b/tasmota/language/de_DE.h
index 071bfaaa0..50a022b6a 100644
--- a/tasmota/language/de_DE.h
+++ b/tasmota/language/de_DE.h
@@ -782,6 +782,11 @@
#define D_SENSOR_SSD1331_CS "SSD1331 CS"
#define D_SENSOR_SSD1331_DC "SSD1331 DC"
#define D_SENSOR_SDCARD_CS "SDCard CS"
+#define D_SENSOR_WIEGAND_D0 "Wiegand D0"
+#define D_SENSOR_WIEGAND_D1 "Wiegand D1"
+#define D_SENSOR_NEOPOOL_TX "NeoPool Tx"
+#define D_SENSOR_NEOPOOL_RX "NeoPool Rx"
+
// Units
#define D_UNIT_AMPERE "A"
@@ -953,4 +958,56 @@
#define D_FP_PASSVERIFY "Übereinstimmung" // 0x21 Verify the fingerprint passed
#define D_FP_UNKNOWNERROR "Fehler" // Any other error
+// xsns_83_neopool.ino
+#define D_NEOPOOL_MACH_NONE "NeoPool" // Machine names
+#define D_NEOPOOL_MACH_HIDROLIFE "Hidrolife (Gelb)"
+#define D_NEOPOOL_MACH_AQUASCENIC "Aquascenic (Blau)"
+#define D_NEOPOOL_MACH_OXILIFE "Oxilife (Grün)"
+#define D_NEOPOOL_MACH_BIONET "Bionet (Hellblau)"
+#define D_NEOPOOL_MACH_HIDRONISER "Hidroniser (Rot)"
+#define D_NEOPOOL_MACH_UVSCENIC "UVScenic (Lila)"
+#define D_NEOPOOL_MACH_STATION "Station (Orange)"
+#define D_NEOPOOL_MACH_BRILIX "Brilix"
+#define D_NEOPOOL_MACH_GENERIC "Generic"
+#define D_NEOPOOL_MACH_BAYROL "Bayrol"
+#define D_NEOPOOL_MACH_HAY "Hay"
+#define D_NEOPOOL_FILTRATION_MANUAL "Manuell" // Filtration modes
+#define D_NEOPOOL_FILTRATION_AUTO "Auto"
+#define D_NEOPOOL_FILTRATION_HEATING "Heizung"
+#define D_NEOPOOL_FILTRATION_SMART "Smart"
+#define D_NEOPOOL_FILTRATION_INTELLIGENT "Intelligent"
+#define D_NEOPOOL_FILTRATION_BACKWASH "Rückspülung"
+#define D_NEOPOOL_FILTRATION_NONE "" // Filtration speed level
+#define D_NEOPOOL_FILTRATION_SLOW "Langsam"
+#define D_NEOPOOL_FILTRATION_MEDIUM "Mittel"
+#define D_NEOPOOL_FILTRATION_FAST "Schnell"
+#define D_NEOPOOL_TYPE "Typ" // Sensor & relais names
+#define D_NEOPOOL_REDOX "Redox"
+#define D_NEOPOOL_CHLORINE "Chlor"
+#define D_NEOPOOL_CONDUCTIVITY "Konduktivität"
+#define D_NEOPOOL_IONIZATION "Ionisierung"
+#define D_NEOPOOL_HYDROLYSIS "Hydrolyse"
+#define D_NEOPOOL_RELAY "Relais"
+#define D_NEOPOOL_RELAY_FILTRATION "Filtration"
+#define D_NEOPOOL_RELAY_LIGHT "Licht"
+#define D_NEOPOOL_RELAY_PH_ACID "Säurepumpe"
+#define D_NEOPOOL_RELAY_PH_BASE "Laugenpumpe"
+#define D_NEOPOOL_RELAY_RX "Redox Pegel"
+#define D_NEOPOOL_RELAY_CL "Chlorpumpe"
+#define D_NEOPOOL_RELAY_CD "Salzwasserpumpe"
+#define D_NEOPOOL_TIME "Zeit"
+#define D_NEOPOOL_FILT_MODE "Filtration"
+#define D_NEOPOOL_POLARIZATION "Pol" // Sensor status
+#define D_NEOPOOL_PR_OFF "PrAus"
+#define D_NEOPOOL_SETPOINT_OK "Ok"
+#define D_NEOPOOL_COVER "Abdeckung"
+#define D_NEOPOOL_SHOCK "Shock"
+#define D_NEOPOOL_ALARM "! "
+#define D_NEOPOOL_LOW "Niedrig"
+#define D_NEOPOOL_FLOW1 "FL1"
+#define D_NEOPOOL_FLOW2 "FL2"
+#define D_NEOPOOL_PH_HIGH "zu hoch" // ph Alarms
+#define D_NEOPOOL_PH_LOW "zu niedrig"
+#define D_NEOPOOL_PUMP_TIME_EXCEEDED "Pumpzeit überschritten"
+
#endif // _LANGUAGE_DE_DE_H_
diff --git a/tasmota/language/el_GR.h b/tasmota/language/el_GR.h
index 9f1342b0b..c064a5492 100644
--- a/tasmota/language/el_GR.h
+++ b/tasmota/language/el_GR.h
@@ -782,6 +782,11 @@
#define D_SENSOR_SSD1331_CS "SSD1331 CS"
#define D_SENSOR_SSD1331_DC "SSD1331 DC"
#define D_SENSOR_SDCARD_CS "SDCard CS"
+#define D_SENSOR_WIEGAND_D0 "Wiegand D0"
+#define D_SENSOR_WIEGAND_D1 "Wiegand D1"
+#define D_SENSOR_NEOPOOL_TX "NeoPool Tx"
+#define D_SENSOR_NEOPOOL_RX "NeoPool Rx"
+
// Units
#define D_UNIT_AMPERE "A"
@@ -953,4 +958,56 @@
#define D_FP_PASSVERIFY "Password verified" // 0x21 Verify the fingerprint passed
#define D_FP_UNKNOWNERROR "Error" // Any other error
+// xsns_83_neopool.ino
+#define D_NEOPOOL_MACH_NONE "NeoPool" // Machine names
+#define D_NEOPOOL_MACH_HIDROLIFE "Hidrolife (yellow)"
+#define D_NEOPOOL_MACH_AQUASCENIC "Aquascenic (blue)"
+#define D_NEOPOOL_MACH_OXILIFE "Oxilife (green)"
+#define D_NEOPOOL_MACH_BIONET "Bionet (light blue)"
+#define D_NEOPOOL_MACH_HIDRONISER "Hidroniser (red)"
+#define D_NEOPOOL_MACH_UVSCENIC "UVScenic (lilac)"
+#define D_NEOPOOL_MACH_STATION "Station (orange)"
+#define D_NEOPOOL_MACH_BRILIX "Brilix"
+#define D_NEOPOOL_MACH_GENERIC "Generic"
+#define D_NEOPOOL_MACH_BAYROL "Bayrol"
+#define D_NEOPOOL_MACH_HAY "Hay"
+#define D_NEOPOOL_FILTRATION_MANUAL "Manual" // Filtration modes
+#define D_NEOPOOL_FILTRATION_AUTO "Auto"
+#define D_NEOPOOL_FILTRATION_HEATING "Heating"
+#define D_NEOPOOL_FILTRATION_SMART "Smart"
+#define D_NEOPOOL_FILTRATION_INTELLIGENT "Intelligent"
+#define D_NEOPOOL_FILTRATION_BACKWASH "Backwash"
+#define D_NEOPOOL_FILTRATION_NONE "" // Filtration speed level
+#define D_NEOPOOL_FILTRATION_SLOW "slow"
+#define D_NEOPOOL_FILTRATION_MEDIUM "medium"
+#define D_NEOPOOL_FILTRATION_FAST "fast"
+#define D_NEOPOOL_TYPE "Type" // Sensor & relais names
+#define D_NEOPOOL_REDOX "Redox"
+#define D_NEOPOOL_CHLORINE "Chlorine"
+#define D_NEOPOOL_CONDUCTIVITY "Conductivity"
+#define D_NEOPOOL_IONIZATION "Ionization"
+#define D_NEOPOOL_HYDROLYSIS "Hydrolysis"
+#define D_NEOPOOL_RELAY "Relay"
+#define D_NEOPOOL_RELAY_FILTRATION "Filtration"
+#define D_NEOPOOL_RELAY_LIGHT "Light"
+#define D_NEOPOOL_RELAY_PH_ACID "Acid pump"
+#define D_NEOPOOL_RELAY_PH_BASE "Base pump"
+#define D_NEOPOOL_RELAY_RX "Redox level"
+#define D_NEOPOOL_RELAY_CL "Chlorine pump"
+#define D_NEOPOOL_RELAY_CD "Brine pump"
+#define D_NEOPOOL_TIME "Time"
+#define D_NEOPOOL_FILT_MODE "Filtration"
+#define D_NEOPOOL_POLARIZATION "Pol" // Sensor status
+#define D_NEOPOOL_PR_OFF "PrOff"
+#define D_NEOPOOL_SETPOINT_OK "Ok"
+#define D_NEOPOOL_COVER "Cover"
+#define D_NEOPOOL_SHOCK "Shock"
+#define D_NEOPOOL_ALARM "! "
+#define D_NEOPOOL_LOW "Low"
+#define D_NEOPOOL_FLOW1 "FL1"
+#define D_NEOPOOL_FLOW2 "FL2"
+#define D_NEOPOOL_PH_HIGH "too high" // ph Alarms
+#define D_NEOPOOL_PH_LOW "too low"
+#define D_NEOPOOL_PUMP_TIME_EXCEEDED "pump time exceeded"
+
#endif // _LANGUAGE_EL_GR_H_
diff --git a/tasmota/language/en_GB.h b/tasmota/language/en_GB.h
index 11acc25fb..bd3eac4c0 100644
--- a/tasmota/language/en_GB.h
+++ b/tasmota/language/en_GB.h
@@ -782,6 +782,11 @@
#define D_SENSOR_SSD1331_CS "SSD1331 CS"
#define D_SENSOR_SSD1331_DC "SSD1331 DC"
#define D_SENSOR_SDCARD_CS "SDCard CS"
+#define D_SENSOR_WIEGAND_D0 "Wiegand D0"
+#define D_SENSOR_WIEGAND_D1 "Wiegand D1"
+#define D_SENSOR_NEOPOOL_TX "NeoPool Tx"
+#define D_SENSOR_NEOPOOL_RX "NeoPool Rx"
+
// Units
#define D_UNIT_AMPERE "A"
@@ -953,4 +958,56 @@
#define D_FP_PASSVERIFY "Password verified" // 0x21 Verify the fingerprint passed
#define D_FP_UNKNOWNERROR "Error" // Any other error
+// xsns_83_neopool.ino
+#define D_NEOPOOL_MACH_NONE "NeoPool" // Machine names
+#define D_NEOPOOL_MACH_HIDROLIFE "Hidrolife (yellow)"
+#define D_NEOPOOL_MACH_AQUASCENIC "Aquascenic (blue)"
+#define D_NEOPOOL_MACH_OXILIFE "Oxilife (green)"
+#define D_NEOPOOL_MACH_BIONET "Bionet (light blue)"
+#define D_NEOPOOL_MACH_HIDRONISER "Hidroniser (red)"
+#define D_NEOPOOL_MACH_UVSCENIC "UVScenic (lilac)"
+#define D_NEOPOOL_MACH_STATION "Station (orange)"
+#define D_NEOPOOL_MACH_BRILIX "Brilix"
+#define D_NEOPOOL_MACH_GENERIC "Generic"
+#define D_NEOPOOL_MACH_BAYROL "Bayrol"
+#define D_NEOPOOL_MACH_HAY "Hay"
+#define D_NEOPOOL_FILTRATION_MANUAL "Manual" // Filtration modes
+#define D_NEOPOOL_FILTRATION_AUTO "Auto"
+#define D_NEOPOOL_FILTRATION_HEATING "Heating"
+#define D_NEOPOOL_FILTRATION_SMART "Smart"
+#define D_NEOPOOL_FILTRATION_INTELLIGENT "Intelligent"
+#define D_NEOPOOL_FILTRATION_BACKWASH "Backwash"
+#define D_NEOPOOL_FILTRATION_NONE "" // Filtration speed level
+#define D_NEOPOOL_FILTRATION_SLOW "slow"
+#define D_NEOPOOL_FILTRATION_MEDIUM "medium"
+#define D_NEOPOOL_FILTRATION_FAST "fast"
+#define D_NEOPOOL_TYPE "Type" // Sensor & relais names
+#define D_NEOPOOL_REDOX "Redox"
+#define D_NEOPOOL_CHLORINE "Chlorine"
+#define D_NEOPOOL_CONDUCTIVITY "Conductivity"
+#define D_NEOPOOL_IONIZATION "Ionization"
+#define D_NEOPOOL_HYDROLYSIS "Hydrolysis"
+#define D_NEOPOOL_RELAY "Relay"
+#define D_NEOPOOL_RELAY_FILTRATION "Filtration"
+#define D_NEOPOOL_RELAY_LIGHT "Light"
+#define D_NEOPOOL_RELAY_PH_ACID "Acid pump"
+#define D_NEOPOOL_RELAY_PH_BASE "Base pump"
+#define D_NEOPOOL_RELAY_RX "Redox level"
+#define D_NEOPOOL_RELAY_CL "Chlorine pump"
+#define D_NEOPOOL_RELAY_CD "Brine pump"
+#define D_NEOPOOL_TIME "Time"
+#define D_NEOPOOL_FILT_MODE "Filtration"
+#define D_NEOPOOL_POLARIZATION "Pol" // Sensor status
+#define D_NEOPOOL_PR_OFF "PrOff"
+#define D_NEOPOOL_SETPOINT_OK "Ok"
+#define D_NEOPOOL_COVER "Cover"
+#define D_NEOPOOL_SHOCK "Shock"
+#define D_NEOPOOL_ALARM "! "
+#define D_NEOPOOL_LOW "Low"
+#define D_NEOPOOL_FLOW1 "FL1"
+#define D_NEOPOOL_FLOW2 "FL2"
+#define D_NEOPOOL_PH_HIGH "too high" // ph Alarms
+#define D_NEOPOOL_PH_LOW "too low"
+#define D_NEOPOOL_PUMP_TIME_EXCEEDED "pump time exceeded"
+
#endif // _LANGUAGE_EN_GB_H_
diff --git a/tasmota/language/es_ES.h b/tasmota/language/es_ES.h
index bb9bdd4d0..63805281d 100644
--- a/tasmota/language/es_ES.h
+++ b/tasmota/language/es_ES.h
@@ -782,6 +782,11 @@
#define D_SENSOR_SSD1331_CS "SSD1331 CS"
#define D_SENSOR_SSD1331_DC "SSD1331 DC"
#define D_SENSOR_SDCARD_CS "SDCard CS"
+#define D_SENSOR_WIEGAND_D0 "Wiegand D0"
+#define D_SENSOR_WIEGAND_D1 "Wiegand D1"
+#define D_SENSOR_NEOPOOL_TX "NeoPool Tx"
+#define D_SENSOR_NEOPOOL_RX "NeoPool Rx"
+
// Units
#define D_UNIT_AMPERE "A"
@@ -953,4 +958,56 @@
#define D_FP_PASSVERIFY "Clave Correcta" // 0x21 Verify the fingerprint passed
#define D_FP_UNKNOWNERROR "Error" // Any other error
+// xsns_83_neopool.ino
+#define D_NEOPOOL_MACH_NONE "NeoPool" // Machine names
+#define D_NEOPOOL_MACH_HIDROLIFE "Hidrolife (yellow)"
+#define D_NEOPOOL_MACH_AQUASCENIC "Aquascenic (blue)"
+#define D_NEOPOOL_MACH_OXILIFE "Oxilife (green)"
+#define D_NEOPOOL_MACH_BIONET "Bionet (light blue)"
+#define D_NEOPOOL_MACH_HIDRONISER "Hidroniser (red)"
+#define D_NEOPOOL_MACH_UVSCENIC "UVScenic (lilac)"
+#define D_NEOPOOL_MACH_STATION "Station (orange)"
+#define D_NEOPOOL_MACH_BRILIX "Brilix"
+#define D_NEOPOOL_MACH_GENERIC "Generic"
+#define D_NEOPOOL_MACH_BAYROL "Bayrol"
+#define D_NEOPOOL_MACH_HAY "Hay"
+#define D_NEOPOOL_FILTRATION_MANUAL "Manual" // Filtration modes
+#define D_NEOPOOL_FILTRATION_AUTO "Auto"
+#define D_NEOPOOL_FILTRATION_HEATING "Heating"
+#define D_NEOPOOL_FILTRATION_SMART "Smart"
+#define D_NEOPOOL_FILTRATION_INTELLIGENT "Intelligent"
+#define D_NEOPOOL_FILTRATION_BACKWASH "Backwash"
+#define D_NEOPOOL_FILTRATION_NONE "" // Filtration speed level
+#define D_NEOPOOL_FILTRATION_SLOW "slow"
+#define D_NEOPOOL_FILTRATION_MEDIUM "medium"
+#define D_NEOPOOL_FILTRATION_FAST "fast"
+#define D_NEOPOOL_TYPE "Type" // Sensor & relais names
+#define D_NEOPOOL_REDOX "Redox"
+#define D_NEOPOOL_CHLORINE "Chlorine"
+#define D_NEOPOOL_CONDUCTIVITY "Conductivity"
+#define D_NEOPOOL_IONIZATION "Ionization"
+#define D_NEOPOOL_HYDROLYSIS "Hydrolysis"
+#define D_NEOPOOL_RELAY "Relay"
+#define D_NEOPOOL_RELAY_FILTRATION "Filtration"
+#define D_NEOPOOL_RELAY_LIGHT "Light"
+#define D_NEOPOOL_RELAY_PH_ACID "Acid pump"
+#define D_NEOPOOL_RELAY_PH_BASE "Base pump"
+#define D_NEOPOOL_RELAY_RX "Redox level"
+#define D_NEOPOOL_RELAY_CL "Chlorine pump"
+#define D_NEOPOOL_RELAY_CD "Brine pump"
+#define D_NEOPOOL_TIME "Time"
+#define D_NEOPOOL_FILT_MODE "Filtration"
+#define D_NEOPOOL_POLARIZATION "Pol" // Sensor status
+#define D_NEOPOOL_PR_OFF "PrOff"
+#define D_NEOPOOL_SETPOINT_OK "Ok"
+#define D_NEOPOOL_COVER "Cover"
+#define D_NEOPOOL_SHOCK "Shock"
+#define D_NEOPOOL_ALARM "! "
+#define D_NEOPOOL_LOW "Low"
+#define D_NEOPOOL_FLOW1 "FL1"
+#define D_NEOPOOL_FLOW2 "FL2"
+#define D_NEOPOOL_PH_HIGH "too high" // ph Alarms
+#define D_NEOPOOL_PH_LOW "too low"
+#define D_NEOPOOL_PUMP_TIME_EXCEEDED "pump time exceeded"
+
#endif // _LANGUAGE_ES_ES_H_
diff --git a/tasmota/language/fr_FR.h b/tasmota/language/fr_FR.h
index 709788ecc..ece5afba4 100644
--- a/tasmota/language/fr_FR.h
+++ b/tasmota/language/fr_FR.h
@@ -778,6 +778,11 @@
#define D_SENSOR_SSD1331_CS "SSD1331 CS"
#define D_SENSOR_SSD1331_DC "SSD1331 DC"
#define D_SENSOR_SDCARD_CS "CarteSD CS"
+#define D_SENSOR_WIEGAND_D0 "Wiegand D0"
+#define D_SENSOR_WIEGAND_D1 "Wiegand D1"
+#define D_SENSOR_NEOPOOL_TX "NeoPool Tx"
+#define D_SENSOR_NEOPOOL_RX "NeoPool Rx"
+
// Units
#define D_UNIT_AMPERE "A"
@@ -947,4 +952,56 @@
#define D_FP_PASSVERIFY "Mot-de-passe vérifié" // 0x21 Verify the fingerprint passed
#define D_FP_UNKNOWNERROR "Erreur" // Any other error
+// xsns_83_neopool.ino
+#define D_NEOPOOL_MACH_NONE "NeoPool" // Machine names
+#define D_NEOPOOL_MACH_HIDROLIFE "Hidrolife (yellow)"
+#define D_NEOPOOL_MACH_AQUASCENIC "Aquascenic (blue)"
+#define D_NEOPOOL_MACH_OXILIFE "Oxilife (green)"
+#define D_NEOPOOL_MACH_BIONET "Bionet (light blue)"
+#define D_NEOPOOL_MACH_HIDRONISER "Hidroniser (red)"
+#define D_NEOPOOL_MACH_UVSCENIC "UVScenic (lilac)"
+#define D_NEOPOOL_MACH_STATION "Station (orange)"
+#define D_NEOPOOL_MACH_BRILIX "Brilix"
+#define D_NEOPOOL_MACH_GENERIC "Generic"
+#define D_NEOPOOL_MACH_BAYROL "Bayrol"
+#define D_NEOPOOL_MACH_HAY "Hay"
+#define D_NEOPOOL_FILTRATION_MANUAL "Manual" // Filtration modes
+#define D_NEOPOOL_FILTRATION_AUTO "Auto"
+#define D_NEOPOOL_FILTRATION_HEATING "Heating"
+#define D_NEOPOOL_FILTRATION_SMART "Smart"
+#define D_NEOPOOL_FILTRATION_INTELLIGENT "Intelligent"
+#define D_NEOPOOL_FILTRATION_BACKWASH "Backwash"
+#define D_NEOPOOL_FILTRATION_NONE "" // Filtration speed level
+#define D_NEOPOOL_FILTRATION_SLOW "slow"
+#define D_NEOPOOL_FILTRATION_MEDIUM "medium"
+#define D_NEOPOOL_FILTRATION_FAST "fast"
+#define D_NEOPOOL_TYPE "Type" // Sensor & relais names
+#define D_NEOPOOL_REDOX "Redox"
+#define D_NEOPOOL_CHLORINE "Chlorine"
+#define D_NEOPOOL_CONDUCTIVITY "Conductivity"
+#define D_NEOPOOL_IONIZATION "Ionization"
+#define D_NEOPOOL_HYDROLYSIS "Hydrolysis"
+#define D_NEOPOOL_RELAY "Relay"
+#define D_NEOPOOL_RELAY_FILTRATION "Filtration"
+#define D_NEOPOOL_RELAY_LIGHT "Light"
+#define D_NEOPOOL_RELAY_PH_ACID "Acid pump"
+#define D_NEOPOOL_RELAY_PH_BASE "Base pump"
+#define D_NEOPOOL_RELAY_RX "Redox level"
+#define D_NEOPOOL_RELAY_CL "Chlorine pump"
+#define D_NEOPOOL_RELAY_CD "Brine pump"
+#define D_NEOPOOL_TIME "Time"
+#define D_NEOPOOL_FILT_MODE "Filtration"
+#define D_NEOPOOL_POLARIZATION "Pol" // Sensor status
+#define D_NEOPOOL_PR_OFF "PrOff"
+#define D_NEOPOOL_SETPOINT_OK "Ok"
+#define D_NEOPOOL_COVER "Cover"
+#define D_NEOPOOL_SHOCK "Shock"
+#define D_NEOPOOL_ALARM "! "
+#define D_NEOPOOL_LOW "Low"
+#define D_NEOPOOL_FLOW1 "FL1"
+#define D_NEOPOOL_FLOW2 "FL2"
+#define D_NEOPOOL_PH_HIGH "too high" // ph Alarms
+#define D_NEOPOOL_PH_LOW "too low"
+#define D_NEOPOOL_PUMP_TIME_EXCEEDED "pump time exceeded"
+
#endif // _LANGUAGE_FR_FR_H_
diff --git a/tasmota/language/he_HE.h b/tasmota/language/he_HE.h
index 1ed935184..dd5a2777d 100644
--- a/tasmota/language/he_HE.h
+++ b/tasmota/language/he_HE.h
@@ -782,6 +782,11 @@
#define D_SENSOR_SSD1331_CS "SSD1331 CS"
#define D_SENSOR_SSD1331_DC "SSD1331 DC"
#define D_SENSOR_SDCARD_CS "SDCard CS"
+#define D_SENSOR_WIEGAND_D0 "Wiegand D0"
+#define D_SENSOR_WIEGAND_D1 "Wiegand D1"
+#define D_SENSOR_NEOPOOL_TX "NeoPool Tx"
+#define D_SENSOR_NEOPOOL_RX "NeoPool Rx"
+
// Units
#define D_UNIT_AMPERE "A"
@@ -953,4 +958,56 @@
#define D_FP_PASSVERIFY "Password verified" // 0x21 Verify the fingerprint passed
#define D_FP_UNKNOWNERROR "Error" // Any other error
+// xsns_83_neopool.ino
+#define D_NEOPOOL_MACH_NONE "NeoPool" // Machine names
+#define D_NEOPOOL_MACH_HIDROLIFE "Hidrolife (yellow)"
+#define D_NEOPOOL_MACH_AQUASCENIC "Aquascenic (blue)"
+#define D_NEOPOOL_MACH_OXILIFE "Oxilife (green)"
+#define D_NEOPOOL_MACH_BIONET "Bionet (light blue)"
+#define D_NEOPOOL_MACH_HIDRONISER "Hidroniser (red)"
+#define D_NEOPOOL_MACH_UVSCENIC "UVScenic (lilac)"
+#define D_NEOPOOL_MACH_STATION "Station (orange)"
+#define D_NEOPOOL_MACH_BRILIX "Brilix"
+#define D_NEOPOOL_MACH_GENERIC "Generic"
+#define D_NEOPOOL_MACH_BAYROL "Bayrol"
+#define D_NEOPOOL_MACH_HAY "Hay"
+#define D_NEOPOOL_FILTRATION_MANUAL "Manual" // Filtration modes
+#define D_NEOPOOL_FILTRATION_AUTO "Auto"
+#define D_NEOPOOL_FILTRATION_HEATING "Heating"
+#define D_NEOPOOL_FILTRATION_SMART "Smart"
+#define D_NEOPOOL_FILTRATION_INTELLIGENT "Intelligent"
+#define D_NEOPOOL_FILTRATION_BACKWASH "Backwash"
+#define D_NEOPOOL_FILTRATION_NONE "" // Filtration speed level
+#define D_NEOPOOL_FILTRATION_SLOW "slow"
+#define D_NEOPOOL_FILTRATION_MEDIUM "medium"
+#define D_NEOPOOL_FILTRATION_FAST "fast"
+#define D_NEOPOOL_TYPE "Type" // Sensor & relais names
+#define D_NEOPOOL_REDOX "Redox"
+#define D_NEOPOOL_CHLORINE "Chlorine"
+#define D_NEOPOOL_CONDUCTIVITY "Conductivity"
+#define D_NEOPOOL_IONIZATION "Ionization"
+#define D_NEOPOOL_HYDROLYSIS "Hydrolysis"
+#define D_NEOPOOL_RELAY "Relay"
+#define D_NEOPOOL_RELAY_FILTRATION "Filtration"
+#define D_NEOPOOL_RELAY_LIGHT "Light"
+#define D_NEOPOOL_RELAY_PH_ACID "Acid pump"
+#define D_NEOPOOL_RELAY_PH_BASE "Base pump"
+#define D_NEOPOOL_RELAY_RX "Redox level"
+#define D_NEOPOOL_RELAY_CL "Chlorine pump"
+#define D_NEOPOOL_RELAY_CD "Brine pump"
+#define D_NEOPOOL_TIME "Time"
+#define D_NEOPOOL_FILT_MODE "Filtration"
+#define D_NEOPOOL_POLARIZATION "Pol" // Sensor status
+#define D_NEOPOOL_PR_OFF "PrOff"
+#define D_NEOPOOL_SETPOINT_OK "Ok"
+#define D_NEOPOOL_COVER "Cover"
+#define D_NEOPOOL_SHOCK "Shock"
+#define D_NEOPOOL_ALARM "! "
+#define D_NEOPOOL_LOW "Low"
+#define D_NEOPOOL_FLOW1 "FL1"
+#define D_NEOPOOL_FLOW2 "FL2"
+#define D_NEOPOOL_PH_HIGH "too high" // ph Alarms
+#define D_NEOPOOL_PH_LOW "too low"
+#define D_NEOPOOL_PUMP_TIME_EXCEEDED "pump time exceeded"
+
#endif // _LANGUAGE_HE_HE_H_
diff --git a/tasmota/language/hu_HU.h b/tasmota/language/hu_HU.h
index b8d4d5347..83105a3df 100644
--- a/tasmota/language/hu_HU.h
+++ b/tasmota/language/hu_HU.h
@@ -782,6 +782,11 @@
#define D_SENSOR_SSD1331_CS "SSD1331 CS"
#define D_SENSOR_SSD1331_DC "SSD1331 DC"
#define D_SENSOR_SDCARD_CS "SDCard CS"
+#define D_SENSOR_WIEGAND_D0 "Wiegand D0"
+#define D_SENSOR_WIEGAND_D1 "Wiegand D1"
+#define D_SENSOR_NEOPOOL_TX "NeoPool Tx"
+#define D_SENSOR_NEOPOOL_RX "NeoPool Rx"
+
// Units
#define D_UNIT_AMPERE "A"
@@ -953,4 +958,56 @@
#define D_FP_PASSVERIFY "Password verified" // 0x21 Verify the fingerprint passed
#define D_FP_UNKNOWNERROR "Error" // Any other error
+// xsns_83_neopool.ino
+#define D_NEOPOOL_MACH_NONE "NeoPool" // Machine names
+#define D_NEOPOOL_MACH_HIDROLIFE "Hidrolife (yellow)"
+#define D_NEOPOOL_MACH_AQUASCENIC "Aquascenic (blue)"
+#define D_NEOPOOL_MACH_OXILIFE "Oxilife (green)"
+#define D_NEOPOOL_MACH_BIONET "Bionet (light blue)"
+#define D_NEOPOOL_MACH_HIDRONISER "Hidroniser (red)"
+#define D_NEOPOOL_MACH_UVSCENIC "UVScenic (lilac)"
+#define D_NEOPOOL_MACH_STATION "Station (orange)"
+#define D_NEOPOOL_MACH_BRILIX "Brilix"
+#define D_NEOPOOL_MACH_GENERIC "Generic"
+#define D_NEOPOOL_MACH_BAYROL "Bayrol"
+#define D_NEOPOOL_MACH_HAY "Hay"
+#define D_NEOPOOL_FILTRATION_MANUAL "Manual" // Filtration modes
+#define D_NEOPOOL_FILTRATION_AUTO "Auto"
+#define D_NEOPOOL_FILTRATION_HEATING "Heating"
+#define D_NEOPOOL_FILTRATION_SMART "Smart"
+#define D_NEOPOOL_FILTRATION_INTELLIGENT "Intelligent"
+#define D_NEOPOOL_FILTRATION_BACKWASH "Backwash"
+#define D_NEOPOOL_FILTRATION_NONE "" // Filtration speed level
+#define D_NEOPOOL_FILTRATION_SLOW "slow"
+#define D_NEOPOOL_FILTRATION_MEDIUM "medium"
+#define D_NEOPOOL_FILTRATION_FAST "fast"
+#define D_NEOPOOL_TYPE "Type" // Sensor & relais names
+#define D_NEOPOOL_REDOX "Redox"
+#define D_NEOPOOL_CHLORINE "Chlorine"
+#define D_NEOPOOL_CONDUCTIVITY "Conductivity"
+#define D_NEOPOOL_IONIZATION "Ionization"
+#define D_NEOPOOL_HYDROLYSIS "Hydrolysis"
+#define D_NEOPOOL_RELAY "Relay"
+#define D_NEOPOOL_RELAY_FILTRATION "Filtration"
+#define D_NEOPOOL_RELAY_LIGHT "Light"
+#define D_NEOPOOL_RELAY_PH_ACID "Acid pump"
+#define D_NEOPOOL_RELAY_PH_BASE "Base pump"
+#define D_NEOPOOL_RELAY_RX "Redox level"
+#define D_NEOPOOL_RELAY_CL "Chlorine pump"
+#define D_NEOPOOL_RELAY_CD "Brine pump"
+#define D_NEOPOOL_TIME "Time"
+#define D_NEOPOOL_FILT_MODE "Filtration"
+#define D_NEOPOOL_POLARIZATION "Pol" // Sensor status
+#define D_NEOPOOL_PR_OFF "PrOff"
+#define D_NEOPOOL_SETPOINT_OK "Ok"
+#define D_NEOPOOL_COVER "Cover"
+#define D_NEOPOOL_SHOCK "Shock"
+#define D_NEOPOOL_ALARM "! "
+#define D_NEOPOOL_LOW "Low"
+#define D_NEOPOOL_FLOW1 "FL1"
+#define D_NEOPOOL_FLOW2 "FL2"
+#define D_NEOPOOL_PH_HIGH "too high" // ph Alarms
+#define D_NEOPOOL_PH_LOW "too low"
+#define D_NEOPOOL_PUMP_TIME_EXCEEDED "pump time exceeded"
+
#endif // _LANGUAGE_HU_HU_H_
diff --git a/tasmota/language/it_IT.h b/tasmota/language/it_IT.h
index 6791d768a..7d4c8a956 100644
--- a/tasmota/language/it_IT.h
+++ b/tasmota/language/it_IT.h
@@ -1,7 +1,7 @@
/*
it-IT.h - localization for Italian - Italy for Tasmota
- Copyright (C) 2021 Gennaro Tortone - some mods by Antonio Fragola - Updated by bovirus - rev. 09.01.2021
+ Copyright (C) 2021 Gennaro Tortone - some mods by Antonio Fragola - Updated by bovirus - rev. 22.01.2021
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
@@ -782,6 +782,10 @@
#define D_SENSOR_SSD1331_CS "SSD1331 - CS"
#define D_SENSOR_SSD1331_DC "SSD1331 - DC"
#define D_SENSOR_SDCARD_CS "Scheda SD - CS"
+#define D_SENSOR_WIEGAND_D0 "Wiegand - D0"
+#define D_SENSOR_WIEGAND_D1 "Wiegand - D1"
+#define D_SENSOR_NEOPOOL_TX "NeoPool Tx"
+#define D_SENSOR_NEOPOOL_RX "NeoPool Rx"
// Units
#define D_UNIT_AMPERE "A"
@@ -953,4 +957,56 @@
#define D_FP_PASSVERIFY "Password verificata" // 0x21 Verify the fingerprint passed
#define D_FP_UNKNOWNERROR "Errore" // Any other error
+// xsns_83_neopool.ino
+#define D_NEOPOOL_MACH_NONE "NeoPool" // Machine names
+#define D_NEOPOOL_MACH_HIDROLIFE "Hidrolife (giallo)"
+#define D_NEOPOOL_MACH_AQUASCENIC "Aquascenic (blu)"
+#define D_NEOPOOL_MACH_OXILIFE "Oxilife (verde)"
+#define D_NEOPOOL_MACH_BIONET "Bionet (azzurro)"
+#define D_NEOPOOL_MACH_HIDRONISER "Hidroniser (rosso)"
+#define D_NEOPOOL_MACH_UVSCENIC "UVScenic (lilac)"
+#define D_NEOPOOL_MACH_STATION "Station (arancio)"
+#define D_NEOPOOL_MACH_BRILIX "Brilix"
+#define D_NEOPOOL_MACH_GENERIC "Generico"
+#define D_NEOPOOL_MACH_BAYROL "Bayrol"
+#define D_NEOPOOL_MACH_HAY "Hay"
+#define D_NEOPOOL_FILTRATION_MANUAL "Manuale" // Filtration modes
+#define D_NEOPOOL_FILTRATION_AUTO "Automatico"
+#define D_NEOPOOL_FILTRATION_HEATING "Riscaldamento"
+#define D_NEOPOOL_FILTRATION_SMART "Rapido"
+#define D_NEOPOOL_FILTRATION_INTELLIGENT "Intelligente"
+#define D_NEOPOOL_FILTRATION_BACKWASH "Contro lavaggio"
+#define D_NEOPOOL_FILTRATION_NONE "" // Filtration speed level
+#define D_NEOPOOL_FILTRATION_SLOW "lento"
+#define D_NEOPOOL_FILTRATION_MEDIUM "medio"
+#define D_NEOPOOL_FILTRATION_FAST "veloce"
+#define D_NEOPOOL_TYPE "Tipo" // Sensor & relais names
+#define D_NEOPOOL_REDOX "Redox"
+#define D_NEOPOOL_CHLORINE "Cloro"
+#define D_NEOPOOL_CONDUCTIVITY "Conduttività"
+#define D_NEOPOOL_IONIZATION "Ionizzazione"
+#define D_NEOPOOL_HYDROLYSIS "Idrolisi"
+#define D_NEOPOOL_RELAY "Relay"
+#define D_NEOPOOL_RELAY_FILTRATION "Filtrazione"
+#define D_NEOPOOL_RELAY_LIGHT "Luce"
+#define D_NEOPOOL_RELAY_PH_ACID "Pompa per acido"
+#define D_NEOPOOL_RELAY_PH_BASE "Popa base"
+#define D_NEOPOOL_RELAY_RX "Livello Redox"
+#define D_NEOPOOL_RELAY_CL "Pompa cloro"
+#define D_NEOPOOL_RELAY_CD "Pompa salamoia"
+#define D_NEOPOOL_TIME "Orario"
+#define D_NEOPOOL_FILT_MODE "Filtrazione"
+#define D_NEOPOOL_POLARIZATION "Pol" // Sensor status
+#define D_NEOPOOL_PR_OFF "PrOff"
+#define D_NEOPOOL_SETPOINT_OK "OK"
+#define D_NEOPOOL_COVER "Copertura"
+#define D_NEOPOOL_SHOCK "Shock"
+#define D_NEOPOOL_ALARM "! "
+#define D_NEOPOOL_LOW "Low"
+#define D_NEOPOOL_FLOW1 "FL1"
+#define D_NEOPOOL_FLOW2 "FL2"
+#define D_NEOPOOL_PH_HIGH "troppo alto" // ph Alarms
+#define D_NEOPOOL_PH_LOW "troppo basso"
+#define D_NEOPOOL_PUMP_TIME_EXCEEDED "tempo pompa superato"
+
#endif // _LANGUAGE_IT_IT_H_
diff --git a/tasmota/language/ko_KO.h b/tasmota/language/ko_KO.h
index a51333856..149650839 100644
--- a/tasmota/language/ko_KO.h
+++ b/tasmota/language/ko_KO.h
@@ -782,6 +782,11 @@
#define D_SENSOR_SSD1331_CS "SSD1331 CS"
#define D_SENSOR_SSD1331_DC "SSD1331 DC"
#define D_SENSOR_SDCARD_CS "SDCard CS"
+#define D_SENSOR_WIEGAND_D0 "Wiegand D0"
+#define D_SENSOR_WIEGAND_D1 "Wiegand D1"
+#define D_SENSOR_NEOPOOL_TX "NeoPool Tx"
+#define D_SENSOR_NEOPOOL_RX "NeoPool Rx"
+
// Units
#define D_UNIT_AMPERE "A"
@@ -953,4 +958,56 @@
#define D_FP_PASSVERIFY "Password verified" // 0x21 Verify the fingerprint passed
#define D_FP_UNKNOWNERROR "Error" // Any other error
+// xsns_83_neopool.ino
+#define D_NEOPOOL_MACH_NONE "NeoPool" // Machine names
+#define D_NEOPOOL_MACH_HIDROLIFE "Hidrolife (yellow)"
+#define D_NEOPOOL_MACH_AQUASCENIC "Aquascenic (blue)"
+#define D_NEOPOOL_MACH_OXILIFE "Oxilife (green)"
+#define D_NEOPOOL_MACH_BIONET "Bionet (light blue)"
+#define D_NEOPOOL_MACH_HIDRONISER "Hidroniser (red)"
+#define D_NEOPOOL_MACH_UVSCENIC "UVScenic (lilac)"
+#define D_NEOPOOL_MACH_STATION "Station (orange)"
+#define D_NEOPOOL_MACH_BRILIX "Brilix"
+#define D_NEOPOOL_MACH_GENERIC "Generic"
+#define D_NEOPOOL_MACH_BAYROL "Bayrol"
+#define D_NEOPOOL_MACH_HAY "Hay"
+#define D_NEOPOOL_FILTRATION_MANUAL "Manual" // Filtration modes
+#define D_NEOPOOL_FILTRATION_AUTO "Auto"
+#define D_NEOPOOL_FILTRATION_HEATING "Heating"
+#define D_NEOPOOL_FILTRATION_SMART "Smart"
+#define D_NEOPOOL_FILTRATION_INTELLIGENT "Intelligent"
+#define D_NEOPOOL_FILTRATION_BACKWASH "Backwash"
+#define D_NEOPOOL_FILTRATION_NONE "" // Filtration speed level
+#define D_NEOPOOL_FILTRATION_SLOW "slow"
+#define D_NEOPOOL_FILTRATION_MEDIUM "medium"
+#define D_NEOPOOL_FILTRATION_FAST "fast"
+#define D_NEOPOOL_TYPE "Type" // Sensor & relais names
+#define D_NEOPOOL_REDOX "Redox"
+#define D_NEOPOOL_CHLORINE "Chlorine"
+#define D_NEOPOOL_CONDUCTIVITY "Conductivity"
+#define D_NEOPOOL_IONIZATION "Ionization"
+#define D_NEOPOOL_HYDROLYSIS "Hydrolysis"
+#define D_NEOPOOL_RELAY "Relay"
+#define D_NEOPOOL_RELAY_FILTRATION "Filtration"
+#define D_NEOPOOL_RELAY_LIGHT "Light"
+#define D_NEOPOOL_RELAY_PH_ACID "Acid pump"
+#define D_NEOPOOL_RELAY_PH_BASE "Base pump"
+#define D_NEOPOOL_RELAY_RX "Redox level"
+#define D_NEOPOOL_RELAY_CL "Chlorine pump"
+#define D_NEOPOOL_RELAY_CD "Brine pump"
+#define D_NEOPOOL_TIME "Time"
+#define D_NEOPOOL_FILT_MODE "Filtration"
+#define D_NEOPOOL_POLARIZATION "Pol" // Sensor status
+#define D_NEOPOOL_PR_OFF "PrOff"
+#define D_NEOPOOL_SETPOINT_OK "Ok"
+#define D_NEOPOOL_COVER "Cover"
+#define D_NEOPOOL_SHOCK "Shock"
+#define D_NEOPOOL_ALARM "! "
+#define D_NEOPOOL_LOW "Low"
+#define D_NEOPOOL_FLOW1 "FL1"
+#define D_NEOPOOL_FLOW2 "FL2"
+#define D_NEOPOOL_PH_HIGH "too high" // ph Alarms
+#define D_NEOPOOL_PH_LOW "too low"
+#define D_NEOPOOL_PUMP_TIME_EXCEEDED "pump time exceeded"
+
#endif // _LANGUAGE_KO_KO_H_
diff --git a/tasmota/language/nl_NL.h b/tasmota/language/nl_NL.h
index 185e3a0c0..8b9972032 100644
--- a/tasmota/language/nl_NL.h
+++ b/tasmota/language/nl_NL.h
@@ -782,6 +782,11 @@
#define D_SENSOR_SSD1331_CS "SSD1331 CS"
#define D_SENSOR_SSD1331_DC "SSD1331 DC"
#define D_SENSOR_SDCARD_CS "SDCard CS"
+#define D_SENSOR_WIEGAND_D0 "Wiegand D0"
+#define D_SENSOR_WIEGAND_D1 "Wiegand D1"
+#define D_SENSOR_NEOPOOL_TX "NeoPool Tx"
+#define D_SENSOR_NEOPOOL_RX "NeoPool Rx"
+
// Units
#define D_UNIT_AMPERE "A"
@@ -953,4 +958,56 @@
#define D_FP_PASSVERIFY "Password verified" // 0x21 Verify the fingerprint passed
#define D_FP_UNKNOWNERROR "Error" // Any other error
+// xsns_83_neopool.ino
+#define D_NEOPOOL_MACH_NONE "NeoPool" // Machine names
+#define D_NEOPOOL_MACH_HIDROLIFE "Hidrolife (yellow)"
+#define D_NEOPOOL_MACH_AQUASCENIC "Aquascenic (blue)"
+#define D_NEOPOOL_MACH_OXILIFE "Oxilife (green)"
+#define D_NEOPOOL_MACH_BIONET "Bionet (light blue)"
+#define D_NEOPOOL_MACH_HIDRONISER "Hidroniser (red)"
+#define D_NEOPOOL_MACH_UVSCENIC "UVScenic (lilac)"
+#define D_NEOPOOL_MACH_STATION "Station (orange)"
+#define D_NEOPOOL_MACH_BRILIX "Brilix"
+#define D_NEOPOOL_MACH_GENERIC "Generic"
+#define D_NEOPOOL_MACH_BAYROL "Bayrol"
+#define D_NEOPOOL_MACH_HAY "Hay"
+#define D_NEOPOOL_FILTRATION_MANUAL "Manual" // Filtration modes
+#define D_NEOPOOL_FILTRATION_AUTO "Auto"
+#define D_NEOPOOL_FILTRATION_HEATING "Heating"
+#define D_NEOPOOL_FILTRATION_SMART "Smart"
+#define D_NEOPOOL_FILTRATION_INTELLIGENT "Intelligent"
+#define D_NEOPOOL_FILTRATION_BACKWASH "Backwash"
+#define D_NEOPOOL_FILTRATION_NONE "" // Filtration speed level
+#define D_NEOPOOL_FILTRATION_SLOW "slow"
+#define D_NEOPOOL_FILTRATION_MEDIUM "medium"
+#define D_NEOPOOL_FILTRATION_FAST "fast"
+#define D_NEOPOOL_TYPE "Type" // Sensor & relais names
+#define D_NEOPOOL_REDOX "Redox"
+#define D_NEOPOOL_CHLORINE "Chlorine"
+#define D_NEOPOOL_CONDUCTIVITY "Conductivity"
+#define D_NEOPOOL_IONIZATION "Ionization"
+#define D_NEOPOOL_HYDROLYSIS "Hydrolysis"
+#define D_NEOPOOL_RELAY "Relay"
+#define D_NEOPOOL_RELAY_FILTRATION "Filtration"
+#define D_NEOPOOL_RELAY_LIGHT "Light"
+#define D_NEOPOOL_RELAY_PH_ACID "Acid pump"
+#define D_NEOPOOL_RELAY_PH_BASE "Base pump"
+#define D_NEOPOOL_RELAY_RX "Redox level"
+#define D_NEOPOOL_RELAY_CL "Chlorine pump"
+#define D_NEOPOOL_RELAY_CD "Brine pump"
+#define D_NEOPOOL_TIME "Time"
+#define D_NEOPOOL_FILT_MODE "Filtration"
+#define D_NEOPOOL_POLARIZATION "Pol" // Sensor status
+#define D_NEOPOOL_PR_OFF "PrOff"
+#define D_NEOPOOL_SETPOINT_OK "Ok"
+#define D_NEOPOOL_COVER "Cover"
+#define D_NEOPOOL_SHOCK "Shock"
+#define D_NEOPOOL_ALARM "! "
+#define D_NEOPOOL_LOW "Low"
+#define D_NEOPOOL_FLOW1 "FL1"
+#define D_NEOPOOL_FLOW2 "FL2"
+#define D_NEOPOOL_PH_HIGH "too high" // ph Alarms
+#define D_NEOPOOL_PH_LOW "too low"
+#define D_NEOPOOL_PUMP_TIME_EXCEEDED "pump time exceeded"
+
#endif // _LANGUAGE_NL_NL_H_
diff --git a/tasmota/language/pl_PL.h b/tasmota/language/pl_PL.h
index 9da13e702..262e26480 100644
--- a/tasmota/language/pl_PL.h
+++ b/tasmota/language/pl_PL.h
@@ -782,6 +782,11 @@
#define D_SENSOR_SSD1331_CS "SSD1331 CS"
#define D_SENSOR_SSD1331_DC "SSD1331 DC"
#define D_SENSOR_SDCARD_CS "SDCard CS"
+#define D_SENSOR_WIEGAND_D0 "Wiegand D0"
+#define D_SENSOR_WIEGAND_D1 "Wiegand D1"
+#define D_SENSOR_NEOPOOL_TX "NeoPool Tx"
+#define D_SENSOR_NEOPOOL_RX "NeoPool Rx"
+
// Units
#define D_UNIT_AMPERE "A"
@@ -953,4 +958,56 @@
#define D_FP_PASSVERIFY "Password verified" // 0x21 Verify the fingerprint passed
#define D_FP_UNKNOWNERROR "Error" // Any other error
+// xsns_83_neopool.ino
+#define D_NEOPOOL_MACH_NONE "NeoPool" // Machine names
+#define D_NEOPOOL_MACH_HIDROLIFE "Hidrolife (yellow)"
+#define D_NEOPOOL_MACH_AQUASCENIC "Aquascenic (blue)"
+#define D_NEOPOOL_MACH_OXILIFE "Oxilife (green)"
+#define D_NEOPOOL_MACH_BIONET "Bionet (light blue)"
+#define D_NEOPOOL_MACH_HIDRONISER "Hidroniser (red)"
+#define D_NEOPOOL_MACH_UVSCENIC "UVScenic (lilac)"
+#define D_NEOPOOL_MACH_STATION "Station (orange)"
+#define D_NEOPOOL_MACH_BRILIX "Brilix"
+#define D_NEOPOOL_MACH_GENERIC "Generic"
+#define D_NEOPOOL_MACH_BAYROL "Bayrol"
+#define D_NEOPOOL_MACH_HAY "Hay"
+#define D_NEOPOOL_FILTRATION_MANUAL "Manual" // Filtration modes
+#define D_NEOPOOL_FILTRATION_AUTO "Auto"
+#define D_NEOPOOL_FILTRATION_HEATING "Heating"
+#define D_NEOPOOL_FILTRATION_SMART "Smart"
+#define D_NEOPOOL_FILTRATION_INTELLIGENT "Intelligent"
+#define D_NEOPOOL_FILTRATION_BACKWASH "Backwash"
+#define D_NEOPOOL_FILTRATION_NONE "" // Filtration speed level
+#define D_NEOPOOL_FILTRATION_SLOW "slow"
+#define D_NEOPOOL_FILTRATION_MEDIUM "medium"
+#define D_NEOPOOL_FILTRATION_FAST "fast"
+#define D_NEOPOOL_TYPE "Type" // Sensor & relais names
+#define D_NEOPOOL_REDOX "Redox"
+#define D_NEOPOOL_CHLORINE "Chlorine"
+#define D_NEOPOOL_CONDUCTIVITY "Conductivity"
+#define D_NEOPOOL_IONIZATION "Ionization"
+#define D_NEOPOOL_HYDROLYSIS "Hydrolysis"
+#define D_NEOPOOL_RELAY "Relay"
+#define D_NEOPOOL_RELAY_FILTRATION "Filtration"
+#define D_NEOPOOL_RELAY_LIGHT "Light"
+#define D_NEOPOOL_RELAY_PH_ACID "Acid pump"
+#define D_NEOPOOL_RELAY_PH_BASE "Base pump"
+#define D_NEOPOOL_RELAY_RX "Redox level"
+#define D_NEOPOOL_RELAY_CL "Chlorine pump"
+#define D_NEOPOOL_RELAY_CD "Brine pump"
+#define D_NEOPOOL_TIME "Time"
+#define D_NEOPOOL_FILT_MODE "Filtration"
+#define D_NEOPOOL_POLARIZATION "Pol" // Sensor status
+#define D_NEOPOOL_PR_OFF "PrOff"
+#define D_NEOPOOL_SETPOINT_OK "Ok"
+#define D_NEOPOOL_COVER "Cover"
+#define D_NEOPOOL_SHOCK "Shock"
+#define D_NEOPOOL_ALARM "! "
+#define D_NEOPOOL_LOW "Low"
+#define D_NEOPOOL_FLOW1 "FL1"
+#define D_NEOPOOL_FLOW2 "FL2"
+#define D_NEOPOOL_PH_HIGH "too high" // ph Alarms
+#define D_NEOPOOL_PH_LOW "too low"
+#define D_NEOPOOL_PUMP_TIME_EXCEEDED "pump time exceeded"
+
#endif // _LANGUAGE_PL_PL_D_H_
diff --git a/tasmota/language/pt_BR.h b/tasmota/language/pt_BR.h
index f48328e4c..3b91baee5 100644
--- a/tasmota/language/pt_BR.h
+++ b/tasmota/language/pt_BR.h
@@ -782,6 +782,11 @@
#define D_SENSOR_SSD1331_CS "SSD1331 CS"
#define D_SENSOR_SSD1331_DC "SSD1331 DC"
#define D_SENSOR_SDCARD_CS "SDCard CS"
+#define D_SENSOR_WIEGAND_D0 "Wiegand D0"
+#define D_SENSOR_WIEGAND_D1 "Wiegand D1"
+#define D_SENSOR_NEOPOOL_TX "NeoPool Tx"
+#define D_SENSOR_NEOPOOL_RX "NeoPool Rx"
+
// Units
#define D_UNIT_AMPERE "A"
@@ -953,4 +958,56 @@
#define D_FP_PASSVERIFY "Password verified" // 0x21 Verify the fingerprint passed
#define D_FP_UNKNOWNERROR "Error" // Any other error
+// xsns_83_neopool.ino
+#define D_NEOPOOL_MACH_NONE "NeoPool" // Machine names
+#define D_NEOPOOL_MACH_HIDROLIFE "Hidrolife (yellow)"
+#define D_NEOPOOL_MACH_AQUASCENIC "Aquascenic (blue)"
+#define D_NEOPOOL_MACH_OXILIFE "Oxilife (green)"
+#define D_NEOPOOL_MACH_BIONET "Bionet (light blue)"
+#define D_NEOPOOL_MACH_HIDRONISER "Hidroniser (red)"
+#define D_NEOPOOL_MACH_UVSCENIC "UVScenic (lilac)"
+#define D_NEOPOOL_MACH_STATION "Station (orange)"
+#define D_NEOPOOL_MACH_BRILIX "Brilix"
+#define D_NEOPOOL_MACH_GENERIC "Generic"
+#define D_NEOPOOL_MACH_BAYROL "Bayrol"
+#define D_NEOPOOL_MACH_HAY "Hay"
+#define D_NEOPOOL_FILTRATION_MANUAL "Manual" // Filtration modes
+#define D_NEOPOOL_FILTRATION_AUTO "Auto"
+#define D_NEOPOOL_FILTRATION_HEATING "Heating"
+#define D_NEOPOOL_FILTRATION_SMART "Smart"
+#define D_NEOPOOL_FILTRATION_INTELLIGENT "Intelligent"
+#define D_NEOPOOL_FILTRATION_BACKWASH "Backwash"
+#define D_NEOPOOL_FILTRATION_NONE "" // Filtration speed level
+#define D_NEOPOOL_FILTRATION_SLOW "slow"
+#define D_NEOPOOL_FILTRATION_MEDIUM "medium"
+#define D_NEOPOOL_FILTRATION_FAST "fast"
+#define D_NEOPOOL_TYPE "Type" // Sensor & relais names
+#define D_NEOPOOL_REDOX "Redox"
+#define D_NEOPOOL_CHLORINE "Chlorine"
+#define D_NEOPOOL_CONDUCTIVITY "Conductivity"
+#define D_NEOPOOL_IONIZATION "Ionization"
+#define D_NEOPOOL_HYDROLYSIS "Hydrolysis"
+#define D_NEOPOOL_RELAY "Relay"
+#define D_NEOPOOL_RELAY_FILTRATION "Filtration"
+#define D_NEOPOOL_RELAY_LIGHT "Light"
+#define D_NEOPOOL_RELAY_PH_ACID "Acid pump"
+#define D_NEOPOOL_RELAY_PH_BASE "Base pump"
+#define D_NEOPOOL_RELAY_RX "Redox level"
+#define D_NEOPOOL_RELAY_CL "Chlorine pump"
+#define D_NEOPOOL_RELAY_CD "Brine pump"
+#define D_NEOPOOL_TIME "Time"
+#define D_NEOPOOL_FILT_MODE "Filtration"
+#define D_NEOPOOL_POLARIZATION "Pol" // Sensor status
+#define D_NEOPOOL_PR_OFF "PrOff"
+#define D_NEOPOOL_SETPOINT_OK "Ok"
+#define D_NEOPOOL_COVER "Cover"
+#define D_NEOPOOL_SHOCK "Shock"
+#define D_NEOPOOL_ALARM "! "
+#define D_NEOPOOL_LOW "Low"
+#define D_NEOPOOL_FLOW1 "FL1"
+#define D_NEOPOOL_FLOW2 "FL2"
+#define D_NEOPOOL_PH_HIGH "too high" // ph Alarms
+#define D_NEOPOOL_PH_LOW "too low"
+#define D_NEOPOOL_PUMP_TIME_EXCEEDED "pump time exceeded"
+
#endif // _LANGUAGE_PT_BR_H_
diff --git a/tasmota/language/pt_PT.h b/tasmota/language/pt_PT.h
index f7893127c..2ac491443 100644
--- a/tasmota/language/pt_PT.h
+++ b/tasmota/language/pt_PT.h
@@ -782,6 +782,11 @@
#define D_SENSOR_SSD1331_CS "SSD1331 CS"
#define D_SENSOR_SSD1331_DC "SSD1331 DC"
#define D_SENSOR_SDCARD_CS "SDCard CS"
+#define D_SENSOR_WIEGAND_D0 "Wiegand D0"
+#define D_SENSOR_WIEGAND_D1 "Wiegand D1"
+#define D_SENSOR_NEOPOOL_TX "NeoPool Tx"
+#define D_SENSOR_NEOPOOL_RX "NeoPool Rx"
+
// Units
#define D_UNIT_AMPERE "A"
@@ -953,4 +958,56 @@
#define D_FP_PASSVERIFY "Password verified" // 0x21 Verify the fingerprint passed
#define D_FP_UNKNOWNERROR "Error" // Any other error
+// xsns_83_neopool.ino
+#define D_NEOPOOL_MACH_NONE "NeoPool" // Machine names
+#define D_NEOPOOL_MACH_HIDROLIFE "Hidrolife (yellow)"
+#define D_NEOPOOL_MACH_AQUASCENIC "Aquascenic (blue)"
+#define D_NEOPOOL_MACH_OXILIFE "Oxilife (green)"
+#define D_NEOPOOL_MACH_BIONET "Bionet (light blue)"
+#define D_NEOPOOL_MACH_HIDRONISER "Hidroniser (red)"
+#define D_NEOPOOL_MACH_UVSCENIC "UVScenic (lilac)"
+#define D_NEOPOOL_MACH_STATION "Station (orange)"
+#define D_NEOPOOL_MACH_BRILIX "Brilix"
+#define D_NEOPOOL_MACH_GENERIC "Generic"
+#define D_NEOPOOL_MACH_BAYROL "Bayrol"
+#define D_NEOPOOL_MACH_HAY "Hay"
+#define D_NEOPOOL_FILTRATION_MANUAL "Manual" // Filtration modes
+#define D_NEOPOOL_FILTRATION_AUTO "Auto"
+#define D_NEOPOOL_FILTRATION_HEATING "Heating"
+#define D_NEOPOOL_FILTRATION_SMART "Smart"
+#define D_NEOPOOL_FILTRATION_INTELLIGENT "Intelligent"
+#define D_NEOPOOL_FILTRATION_BACKWASH "Backwash"
+#define D_NEOPOOL_FILTRATION_NONE "" // Filtration speed level
+#define D_NEOPOOL_FILTRATION_SLOW "slow"
+#define D_NEOPOOL_FILTRATION_MEDIUM "medium"
+#define D_NEOPOOL_FILTRATION_FAST "fast"
+#define D_NEOPOOL_TYPE "Type" // Sensor & relais names
+#define D_NEOPOOL_REDOX "Redox"
+#define D_NEOPOOL_CHLORINE "Chlorine"
+#define D_NEOPOOL_CONDUCTIVITY "Conductivity"
+#define D_NEOPOOL_IONIZATION "Ionization"
+#define D_NEOPOOL_HYDROLYSIS "Hydrolysis"
+#define D_NEOPOOL_RELAY "Relay"
+#define D_NEOPOOL_RELAY_FILTRATION "Filtration"
+#define D_NEOPOOL_RELAY_LIGHT "Light"
+#define D_NEOPOOL_RELAY_PH_ACID "Acid pump"
+#define D_NEOPOOL_RELAY_PH_BASE "Base pump"
+#define D_NEOPOOL_RELAY_RX "Redox level"
+#define D_NEOPOOL_RELAY_CL "Chlorine pump"
+#define D_NEOPOOL_RELAY_CD "Brine pump"
+#define D_NEOPOOL_TIME "Time"
+#define D_NEOPOOL_FILT_MODE "Filtration"
+#define D_NEOPOOL_POLARIZATION "Pol" // Sensor status
+#define D_NEOPOOL_PR_OFF "PrOff"
+#define D_NEOPOOL_SETPOINT_OK "Ok"
+#define D_NEOPOOL_COVER "Cover"
+#define D_NEOPOOL_SHOCK "Shock"
+#define D_NEOPOOL_ALARM "! "
+#define D_NEOPOOL_LOW "Low"
+#define D_NEOPOOL_FLOW1 "FL1"
+#define D_NEOPOOL_FLOW2 "FL2"
+#define D_NEOPOOL_PH_HIGH "too high" // ph Alarms
+#define D_NEOPOOL_PH_LOW "too low"
+#define D_NEOPOOL_PUMP_TIME_EXCEEDED "pump time exceeded"
+
#endif // _LANGUAGE_PT_PT_H_
diff --git a/tasmota/language/ro_RO.h b/tasmota/language/ro_RO.h
index b4641fca8..15cf67ee6 100644
--- a/tasmota/language/ro_RO.h
+++ b/tasmota/language/ro_RO.h
@@ -782,6 +782,11 @@
#define D_SENSOR_SSD1331_CS "SSD1331 CS"
#define D_SENSOR_SSD1331_DC "SSD1331 DC"
#define D_SENSOR_SDCARD_CS "SDCard CS"
+#define D_SENSOR_WIEGAND_D0 "Wiegand D0"
+#define D_SENSOR_WIEGAND_D1 "Wiegand D1"
+#define D_SENSOR_NEOPOOL_TX "NeoPool Tx"
+#define D_SENSOR_NEOPOOL_RX "NeoPool Rx"
+
// Units
#define D_UNIT_AMPERE "A"
@@ -953,4 +958,56 @@
#define D_FP_PASSVERIFY "Password verified" // 0x21 Verify the fingerprint passed
#define D_FP_UNKNOWNERROR "Error" // Any other error
+// xsns_83_neopool.ino
+#define D_NEOPOOL_MACH_NONE "NeoPool" // Machine names
+#define D_NEOPOOL_MACH_HIDROLIFE "Hidrolife (yellow)"
+#define D_NEOPOOL_MACH_AQUASCENIC "Aquascenic (blue)"
+#define D_NEOPOOL_MACH_OXILIFE "Oxilife (green)"
+#define D_NEOPOOL_MACH_BIONET "Bionet (light blue)"
+#define D_NEOPOOL_MACH_HIDRONISER "Hidroniser (red)"
+#define D_NEOPOOL_MACH_UVSCENIC "UVScenic (lilac)"
+#define D_NEOPOOL_MACH_STATION "Station (orange)"
+#define D_NEOPOOL_MACH_BRILIX "Brilix"
+#define D_NEOPOOL_MACH_GENERIC "Generic"
+#define D_NEOPOOL_MACH_BAYROL "Bayrol"
+#define D_NEOPOOL_MACH_HAY "Hay"
+#define D_NEOPOOL_FILTRATION_MANUAL "Manual" // Filtration modes
+#define D_NEOPOOL_FILTRATION_AUTO "Auto"
+#define D_NEOPOOL_FILTRATION_HEATING "Heating"
+#define D_NEOPOOL_FILTRATION_SMART "Smart"
+#define D_NEOPOOL_FILTRATION_INTELLIGENT "Intelligent"
+#define D_NEOPOOL_FILTRATION_BACKWASH "Backwash"
+#define D_NEOPOOL_FILTRATION_NONE "" // Filtration speed level
+#define D_NEOPOOL_FILTRATION_SLOW "slow"
+#define D_NEOPOOL_FILTRATION_MEDIUM "medium"
+#define D_NEOPOOL_FILTRATION_FAST "fast"
+#define D_NEOPOOL_TYPE "Type" // Sensor & relais names
+#define D_NEOPOOL_REDOX "Redox"
+#define D_NEOPOOL_CHLORINE "Chlorine"
+#define D_NEOPOOL_CONDUCTIVITY "Conductivity"
+#define D_NEOPOOL_IONIZATION "Ionization"
+#define D_NEOPOOL_HYDROLYSIS "Hydrolysis"
+#define D_NEOPOOL_RELAY "Relay"
+#define D_NEOPOOL_RELAY_FILTRATION "Filtration"
+#define D_NEOPOOL_RELAY_LIGHT "Light"
+#define D_NEOPOOL_RELAY_PH_ACID "Acid pump"
+#define D_NEOPOOL_RELAY_PH_BASE "Base pump"
+#define D_NEOPOOL_RELAY_RX "Redox level"
+#define D_NEOPOOL_RELAY_CL "Chlorine pump"
+#define D_NEOPOOL_RELAY_CD "Brine pump"
+#define D_NEOPOOL_TIME "Time"
+#define D_NEOPOOL_FILT_MODE "Filtration"
+#define D_NEOPOOL_POLARIZATION "Pol" // Sensor status
+#define D_NEOPOOL_PR_OFF "PrOff"
+#define D_NEOPOOL_SETPOINT_OK "Ok"
+#define D_NEOPOOL_COVER "Cover"
+#define D_NEOPOOL_SHOCK "Shock"
+#define D_NEOPOOL_ALARM "! "
+#define D_NEOPOOL_LOW "Low"
+#define D_NEOPOOL_FLOW1 "FL1"
+#define D_NEOPOOL_FLOW2 "FL2"
+#define D_NEOPOOL_PH_HIGH "too high" // ph Alarms
+#define D_NEOPOOL_PH_LOW "too low"
+#define D_NEOPOOL_PUMP_TIME_EXCEEDED "pump time exceeded"
+
#endif // _LANGUAGE_RO_RO_H_
diff --git a/tasmota/language/ru_RU.h b/tasmota/language/ru_RU.h
index 9d19762a4..2cb1d295d 100644
--- a/tasmota/language/ru_RU.h
+++ b/tasmota/language/ru_RU.h
@@ -782,6 +782,11 @@
#define D_SENSOR_SSD1331_CS "SSD1331 CS"
#define D_SENSOR_SSD1331_DC "SSD1331 DC"
#define D_SENSOR_SDCARD_CS "SDCard CS"
+#define D_SENSOR_WIEGAND_D0 "Wiegand D0"
+#define D_SENSOR_WIEGAND_D1 "Wiegand D1"
+#define D_SENSOR_NEOPOOL_TX "NeoPool Tx"
+#define D_SENSOR_NEOPOOL_RX "NeoPool Rx"
+
// Units
#define D_UNIT_AMPERE "А"
@@ -953,4 +958,56 @@
#define D_FP_PASSVERIFY "Password verified" // 0x21 Verify the fingerprint passed
#define D_FP_UNKNOWNERROR "Error" // Any other error
+// xsns_83_neopool.ino
+#define D_NEOPOOL_MACH_NONE "NeoPool" // Machine names
+#define D_NEOPOOL_MACH_HIDROLIFE "Hidrolife (yellow)"
+#define D_NEOPOOL_MACH_AQUASCENIC "Aquascenic (blue)"
+#define D_NEOPOOL_MACH_OXILIFE "Oxilife (green)"
+#define D_NEOPOOL_MACH_BIONET "Bionet (light blue)"
+#define D_NEOPOOL_MACH_HIDRONISER "Hidroniser (red)"
+#define D_NEOPOOL_MACH_UVSCENIC "UVScenic (lilac)"
+#define D_NEOPOOL_MACH_STATION "Station (orange)"
+#define D_NEOPOOL_MACH_BRILIX "Brilix"
+#define D_NEOPOOL_MACH_GENERIC "Generic"
+#define D_NEOPOOL_MACH_BAYROL "Bayrol"
+#define D_NEOPOOL_MACH_HAY "Hay"
+#define D_NEOPOOL_FILTRATION_MANUAL "Manual" // Filtration modes
+#define D_NEOPOOL_FILTRATION_AUTO "Auto"
+#define D_NEOPOOL_FILTRATION_HEATING "Heating"
+#define D_NEOPOOL_FILTRATION_SMART "Smart"
+#define D_NEOPOOL_FILTRATION_INTELLIGENT "Intelligent"
+#define D_NEOPOOL_FILTRATION_BACKWASH "Backwash"
+#define D_NEOPOOL_FILTRATION_NONE "" // Filtration speed level
+#define D_NEOPOOL_FILTRATION_SLOW "slow"
+#define D_NEOPOOL_FILTRATION_MEDIUM "medium"
+#define D_NEOPOOL_FILTRATION_FAST "fast"
+#define D_NEOPOOL_TYPE "Type" // Sensor & relais names
+#define D_NEOPOOL_REDOX "Redox"
+#define D_NEOPOOL_CHLORINE "Chlorine"
+#define D_NEOPOOL_CONDUCTIVITY "Conductivity"
+#define D_NEOPOOL_IONIZATION "Ionization"
+#define D_NEOPOOL_HYDROLYSIS "Hydrolysis"
+#define D_NEOPOOL_RELAY "Relay"
+#define D_NEOPOOL_RELAY_FILTRATION "Filtration"
+#define D_NEOPOOL_RELAY_LIGHT "Light"
+#define D_NEOPOOL_RELAY_PH_ACID "Acid pump"
+#define D_NEOPOOL_RELAY_PH_BASE "Base pump"
+#define D_NEOPOOL_RELAY_RX "Redox level"
+#define D_NEOPOOL_RELAY_CL "Chlorine pump"
+#define D_NEOPOOL_RELAY_CD "Brine pump"
+#define D_NEOPOOL_TIME "Time"
+#define D_NEOPOOL_FILT_MODE "Filtration"
+#define D_NEOPOOL_POLARIZATION "Pol" // Sensor status
+#define D_NEOPOOL_PR_OFF "PrOff"
+#define D_NEOPOOL_SETPOINT_OK "Ok"
+#define D_NEOPOOL_COVER "Cover"
+#define D_NEOPOOL_SHOCK "Shock"
+#define D_NEOPOOL_ALARM "! "
+#define D_NEOPOOL_LOW "Low"
+#define D_NEOPOOL_FLOW1 "FL1"
+#define D_NEOPOOL_FLOW2 "FL2"
+#define D_NEOPOOL_PH_HIGH "too high" // ph Alarms
+#define D_NEOPOOL_PH_LOW "too low"
+#define D_NEOPOOL_PUMP_TIME_EXCEEDED "pump time exceeded"
+
#endif // _LANGUAGE_RU_RU_H_
diff --git a/tasmota/language/sk_SK.h b/tasmota/language/sk_SK.h
index 5c46b4b89..8086cf69f 100644
--- a/tasmota/language/sk_SK.h
+++ b/tasmota/language/sk_SK.h
@@ -782,6 +782,11 @@
#define D_SENSOR_SSD1331_CS "SSD1331 CS"
#define D_SENSOR_SSD1331_DC "SSD1331 DC"
#define D_SENSOR_SDCARD_CS "SDCard CS"
+#define D_SENSOR_WIEGAND_D0 "Wiegand D0"
+#define D_SENSOR_WIEGAND_D1 "Wiegand D1"
+#define D_SENSOR_NEOPOOL_TX "NeoPool Tx"
+#define D_SENSOR_NEOPOOL_RX "NeoPool Rx"
+
// Units
#define D_UNIT_AMPERE "A"
@@ -953,4 +958,56 @@
#define D_FP_PASSVERIFY "Password verified" // 0x21 Verify the fingerprint passed
#define D_FP_UNKNOWNERROR "Error" // Any other error
+// xsns_83_neopool.ino
+#define D_NEOPOOL_MACH_NONE "NeoPool" // Machine names
+#define D_NEOPOOL_MACH_HIDROLIFE "Hidrolife (yellow)"
+#define D_NEOPOOL_MACH_AQUASCENIC "Aquascenic (blue)"
+#define D_NEOPOOL_MACH_OXILIFE "Oxilife (green)"
+#define D_NEOPOOL_MACH_BIONET "Bionet (light blue)"
+#define D_NEOPOOL_MACH_HIDRONISER "Hidroniser (red)"
+#define D_NEOPOOL_MACH_UVSCENIC "UVScenic (lilac)"
+#define D_NEOPOOL_MACH_STATION "Station (orange)"
+#define D_NEOPOOL_MACH_BRILIX "Brilix"
+#define D_NEOPOOL_MACH_GENERIC "Generic"
+#define D_NEOPOOL_MACH_BAYROL "Bayrol"
+#define D_NEOPOOL_MACH_HAY "Hay"
+#define D_NEOPOOL_FILTRATION_MANUAL "Manual" // Filtration modes
+#define D_NEOPOOL_FILTRATION_AUTO "Auto"
+#define D_NEOPOOL_FILTRATION_HEATING "Heating"
+#define D_NEOPOOL_FILTRATION_SMART "Smart"
+#define D_NEOPOOL_FILTRATION_INTELLIGENT "Intelligent"
+#define D_NEOPOOL_FILTRATION_BACKWASH "Backwash"
+#define D_NEOPOOL_FILTRATION_NONE "" // Filtration speed level
+#define D_NEOPOOL_FILTRATION_SLOW "slow"
+#define D_NEOPOOL_FILTRATION_MEDIUM "medium"
+#define D_NEOPOOL_FILTRATION_FAST "fast"
+#define D_NEOPOOL_TYPE "Type" // Sensor & relais names
+#define D_NEOPOOL_REDOX "Redox"
+#define D_NEOPOOL_CHLORINE "Chlorine"
+#define D_NEOPOOL_CONDUCTIVITY "Conductivity"
+#define D_NEOPOOL_IONIZATION "Ionization"
+#define D_NEOPOOL_HYDROLYSIS "Hydrolysis"
+#define D_NEOPOOL_RELAY "Relay"
+#define D_NEOPOOL_RELAY_FILTRATION "Filtration"
+#define D_NEOPOOL_RELAY_LIGHT "Light"
+#define D_NEOPOOL_RELAY_PH_ACID "Acid pump"
+#define D_NEOPOOL_RELAY_PH_BASE "Base pump"
+#define D_NEOPOOL_RELAY_RX "Redox level"
+#define D_NEOPOOL_RELAY_CL "Chlorine pump"
+#define D_NEOPOOL_RELAY_CD "Brine pump"
+#define D_NEOPOOL_TIME "Time"
+#define D_NEOPOOL_FILT_MODE "Filtration"
+#define D_NEOPOOL_POLARIZATION "Pol" // Sensor status
+#define D_NEOPOOL_PR_OFF "PrOff"
+#define D_NEOPOOL_SETPOINT_OK "Ok"
+#define D_NEOPOOL_COVER "Cover"
+#define D_NEOPOOL_SHOCK "Shock"
+#define D_NEOPOOL_ALARM "! "
+#define D_NEOPOOL_LOW "Low"
+#define D_NEOPOOL_FLOW1 "FL1"
+#define D_NEOPOOL_FLOW2 "FL2"
+#define D_NEOPOOL_PH_HIGH "too high" // ph Alarms
+#define D_NEOPOOL_PH_LOW "too low"
+#define D_NEOPOOL_PUMP_TIME_EXCEEDED "pump time exceeded"
+
#endif // _LANGUAGE_SK_SK_H_
diff --git a/tasmota/language/sv_SE.h b/tasmota/language/sv_SE.h
index a6e6aa1f6..83c26b3ca 100644
--- a/tasmota/language/sv_SE.h
+++ b/tasmota/language/sv_SE.h
@@ -782,6 +782,11 @@
#define D_SENSOR_SSD1331_CS "SSD1331 CS"
#define D_SENSOR_SSD1331_DC "SSD1331 DC"
#define D_SENSOR_SDCARD_CS "SDCard CS"
+#define D_SENSOR_WIEGAND_D0 "Wiegand D0"
+#define D_SENSOR_WIEGAND_D1 "Wiegand D1"
+#define D_SENSOR_NEOPOOL_TX "NeoPool Tx"
+#define D_SENSOR_NEOPOOL_RX "NeoPool Rx"
+
// Units
#define D_UNIT_AMPERE "A"
@@ -953,4 +958,56 @@
#define D_FP_PASSVERIFY "Password verified" // 0x21 Verify the fingerprint passed
#define D_FP_UNKNOWNERROR "Error" // Any other error
+// xsns_83_neopool.ino
+#define D_NEOPOOL_MACH_NONE "NeoPool" // Machine names
+#define D_NEOPOOL_MACH_HIDROLIFE "Hidrolife (yellow)"
+#define D_NEOPOOL_MACH_AQUASCENIC "Aquascenic (blue)"
+#define D_NEOPOOL_MACH_OXILIFE "Oxilife (green)"
+#define D_NEOPOOL_MACH_BIONET "Bionet (light blue)"
+#define D_NEOPOOL_MACH_HIDRONISER "Hidroniser (red)"
+#define D_NEOPOOL_MACH_UVSCENIC "UVScenic (lilac)"
+#define D_NEOPOOL_MACH_STATION "Station (orange)"
+#define D_NEOPOOL_MACH_BRILIX "Brilix"
+#define D_NEOPOOL_MACH_GENERIC "Generic"
+#define D_NEOPOOL_MACH_BAYROL "Bayrol"
+#define D_NEOPOOL_MACH_HAY "Hay"
+#define D_NEOPOOL_FILTRATION_MANUAL "Manual" // Filtration modes
+#define D_NEOPOOL_FILTRATION_AUTO "Auto"
+#define D_NEOPOOL_FILTRATION_HEATING "Heating"
+#define D_NEOPOOL_FILTRATION_SMART "Smart"
+#define D_NEOPOOL_FILTRATION_INTELLIGENT "Intelligent"
+#define D_NEOPOOL_FILTRATION_BACKWASH "Backwash"
+#define D_NEOPOOL_FILTRATION_NONE "" // Filtration speed level
+#define D_NEOPOOL_FILTRATION_SLOW "slow"
+#define D_NEOPOOL_FILTRATION_MEDIUM "medium"
+#define D_NEOPOOL_FILTRATION_FAST "fast"
+#define D_NEOPOOL_TYPE "Type" // Sensor & relais names
+#define D_NEOPOOL_REDOX "Redox"
+#define D_NEOPOOL_CHLORINE "Chlorine"
+#define D_NEOPOOL_CONDUCTIVITY "Conductivity"
+#define D_NEOPOOL_IONIZATION "Ionization"
+#define D_NEOPOOL_HYDROLYSIS "Hydrolysis"
+#define D_NEOPOOL_RELAY "Relay"
+#define D_NEOPOOL_RELAY_FILTRATION "Filtration"
+#define D_NEOPOOL_RELAY_LIGHT "Light"
+#define D_NEOPOOL_RELAY_PH_ACID "Acid pump"
+#define D_NEOPOOL_RELAY_PH_BASE "Base pump"
+#define D_NEOPOOL_RELAY_RX "Redox level"
+#define D_NEOPOOL_RELAY_CL "Chlorine pump"
+#define D_NEOPOOL_RELAY_CD "Brine pump"
+#define D_NEOPOOL_TIME "Time"
+#define D_NEOPOOL_FILT_MODE "Filtration"
+#define D_NEOPOOL_POLARIZATION "Pol" // Sensor status
+#define D_NEOPOOL_PR_OFF "PrOff"
+#define D_NEOPOOL_SETPOINT_OK "Ok"
+#define D_NEOPOOL_COVER "Cover"
+#define D_NEOPOOL_SHOCK "Shock"
+#define D_NEOPOOL_ALARM "! "
+#define D_NEOPOOL_LOW "Low"
+#define D_NEOPOOL_FLOW1 "FL1"
+#define D_NEOPOOL_FLOW2 "FL2"
+#define D_NEOPOOL_PH_HIGH "too high" // ph Alarms
+#define D_NEOPOOL_PH_LOW "too low"
+#define D_NEOPOOL_PUMP_TIME_EXCEEDED "pump time exceeded"
+
#endif // _LANGUAGE_SV_SE_H_
diff --git a/tasmota/language/tr_TR.h b/tasmota/language/tr_TR.h
index 21ac79065..6179ae17a 100644
--- a/tasmota/language/tr_TR.h
+++ b/tasmota/language/tr_TR.h
@@ -782,6 +782,11 @@
#define D_SENSOR_SSD1331_CS "SSD1331 CS"
#define D_SENSOR_SSD1331_DC "SSD1331 DC"
#define D_SENSOR_SDCARD_CS "SDCard CS"
+#define D_SENSOR_WIEGAND_D0 "Wiegand D0"
+#define D_SENSOR_WIEGAND_D1 "Wiegand D1"
+#define D_SENSOR_NEOPOOL_TX "NeoPool Tx"
+#define D_SENSOR_NEOPOOL_RX "NeoPool Rx"
+
// Units
#define D_UNIT_AMPERE "A"
@@ -953,4 +958,56 @@
#define D_FP_PASSVERIFY "Password verified" // 0x21 Verify the fingerprint passed
#define D_FP_UNKNOWNERROR "Error" // Any other error
+// xsns_83_neopool.ino
+#define D_NEOPOOL_MACH_NONE "NeoPool" // Machine names
+#define D_NEOPOOL_MACH_HIDROLIFE "Hidrolife (yellow)"
+#define D_NEOPOOL_MACH_AQUASCENIC "Aquascenic (blue)"
+#define D_NEOPOOL_MACH_OXILIFE "Oxilife (green)"
+#define D_NEOPOOL_MACH_BIONET "Bionet (light blue)"
+#define D_NEOPOOL_MACH_HIDRONISER "Hidroniser (red)"
+#define D_NEOPOOL_MACH_UVSCENIC "UVScenic (lilac)"
+#define D_NEOPOOL_MACH_STATION "Station (orange)"
+#define D_NEOPOOL_MACH_BRILIX "Brilix"
+#define D_NEOPOOL_MACH_GENERIC "Generic"
+#define D_NEOPOOL_MACH_BAYROL "Bayrol"
+#define D_NEOPOOL_MACH_HAY "Hay"
+#define D_NEOPOOL_FILTRATION_MANUAL "Manual" // Filtration modes
+#define D_NEOPOOL_FILTRATION_AUTO "Auto"
+#define D_NEOPOOL_FILTRATION_HEATING "Heating"
+#define D_NEOPOOL_FILTRATION_SMART "Smart"
+#define D_NEOPOOL_FILTRATION_INTELLIGENT "Intelligent"
+#define D_NEOPOOL_FILTRATION_BACKWASH "Backwash"
+#define D_NEOPOOL_FILTRATION_NONE "" // Filtration speed level
+#define D_NEOPOOL_FILTRATION_SLOW "slow"
+#define D_NEOPOOL_FILTRATION_MEDIUM "medium"
+#define D_NEOPOOL_FILTRATION_FAST "fast"
+#define D_NEOPOOL_TYPE "Type" // Sensor & relais names
+#define D_NEOPOOL_REDOX "Redox"
+#define D_NEOPOOL_CHLORINE "Chlorine"
+#define D_NEOPOOL_CONDUCTIVITY "Conductivity"
+#define D_NEOPOOL_IONIZATION "Ionization"
+#define D_NEOPOOL_HYDROLYSIS "Hydrolysis"
+#define D_NEOPOOL_RELAY "Relay"
+#define D_NEOPOOL_RELAY_FILTRATION "Filtration"
+#define D_NEOPOOL_RELAY_LIGHT "Light"
+#define D_NEOPOOL_RELAY_PH_ACID "Acid pump"
+#define D_NEOPOOL_RELAY_PH_BASE "Base pump"
+#define D_NEOPOOL_RELAY_RX "Redox level"
+#define D_NEOPOOL_RELAY_CL "Chlorine pump"
+#define D_NEOPOOL_RELAY_CD "Brine pump"
+#define D_NEOPOOL_TIME "Time"
+#define D_NEOPOOL_FILT_MODE "Filtration"
+#define D_NEOPOOL_POLARIZATION "Pol" // Sensor status
+#define D_NEOPOOL_PR_OFF "PrOff"
+#define D_NEOPOOL_SETPOINT_OK "Ok"
+#define D_NEOPOOL_COVER "Cover"
+#define D_NEOPOOL_SHOCK "Shock"
+#define D_NEOPOOL_ALARM "! "
+#define D_NEOPOOL_LOW "Low"
+#define D_NEOPOOL_FLOW1 "FL1"
+#define D_NEOPOOL_FLOW2 "FL2"
+#define D_NEOPOOL_PH_HIGH "too high" // ph Alarms
+#define D_NEOPOOL_PH_LOW "too low"
+#define D_NEOPOOL_PUMP_TIME_EXCEEDED "pump time exceeded"
+
#endif // _LANGUAGE_TR_TR_H_
diff --git a/tasmota/language/uk_UA.h b/tasmota/language/uk_UA.h
index b32761e2e..929f2df42 100644
--- a/tasmota/language/uk_UA.h
+++ b/tasmota/language/uk_UA.h
@@ -782,6 +782,11 @@
#define D_SENSOR_SSD1331_CS "SSD1331 CS"
#define D_SENSOR_SSD1331_DC "SSD1331 DC"
#define D_SENSOR_SDCARD_CS "SDCard CS"
+#define D_SENSOR_WIEGAND_D0 "Wiegand D0"
+#define D_SENSOR_WIEGAND_D1 "Wiegand D1"
+#define D_SENSOR_NEOPOOL_TX "NeoPool Tx"
+#define D_SENSOR_NEOPOOL_RX "NeoPool Rx"
+
// Units
#define D_UNIT_AMPERE "А"
@@ -953,4 +958,56 @@
#define D_FP_PASSVERIFY "Password verified" // 0x21 Verify the fingerprint passed
#define D_FP_UNKNOWNERROR "Error" // Any other error
+// xsns_83_neopool.ino
+#define D_NEOPOOL_MACH_NONE "NeoPool" // Machine names
+#define D_NEOPOOL_MACH_HIDROLIFE "Hidrolife (yellow)"
+#define D_NEOPOOL_MACH_AQUASCENIC "Aquascenic (blue)"
+#define D_NEOPOOL_MACH_OXILIFE "Oxilife (green)"
+#define D_NEOPOOL_MACH_BIONET "Bionet (light blue)"
+#define D_NEOPOOL_MACH_HIDRONISER "Hidroniser (red)"
+#define D_NEOPOOL_MACH_UVSCENIC "UVScenic (lilac)"
+#define D_NEOPOOL_MACH_STATION "Station (orange)"
+#define D_NEOPOOL_MACH_BRILIX "Brilix"
+#define D_NEOPOOL_MACH_GENERIC "Generic"
+#define D_NEOPOOL_MACH_BAYROL "Bayrol"
+#define D_NEOPOOL_MACH_HAY "Hay"
+#define D_NEOPOOL_FILTRATION_MANUAL "Manual" // Filtration modes
+#define D_NEOPOOL_FILTRATION_AUTO "Auto"
+#define D_NEOPOOL_FILTRATION_HEATING "Heating"
+#define D_NEOPOOL_FILTRATION_SMART "Smart"
+#define D_NEOPOOL_FILTRATION_INTELLIGENT "Intelligent"
+#define D_NEOPOOL_FILTRATION_BACKWASH "Backwash"
+#define D_NEOPOOL_FILTRATION_NONE "" // Filtration speed level
+#define D_NEOPOOL_FILTRATION_SLOW "slow"
+#define D_NEOPOOL_FILTRATION_MEDIUM "medium"
+#define D_NEOPOOL_FILTRATION_FAST "fast"
+#define D_NEOPOOL_TYPE "Type" // Sensor & relais names
+#define D_NEOPOOL_REDOX "Redox"
+#define D_NEOPOOL_CHLORINE "Chlorine"
+#define D_NEOPOOL_CONDUCTIVITY "Conductivity"
+#define D_NEOPOOL_IONIZATION "Ionization"
+#define D_NEOPOOL_HYDROLYSIS "Hydrolysis"
+#define D_NEOPOOL_RELAY "Relay"
+#define D_NEOPOOL_RELAY_FILTRATION "Filtration"
+#define D_NEOPOOL_RELAY_LIGHT "Light"
+#define D_NEOPOOL_RELAY_PH_ACID "Acid pump"
+#define D_NEOPOOL_RELAY_PH_BASE "Base pump"
+#define D_NEOPOOL_RELAY_RX "Redox level"
+#define D_NEOPOOL_RELAY_CL "Chlorine pump"
+#define D_NEOPOOL_RELAY_CD "Brine pump"
+#define D_NEOPOOL_TIME "Time"
+#define D_NEOPOOL_FILT_MODE "Filtration"
+#define D_NEOPOOL_POLARIZATION "Pol" // Sensor status
+#define D_NEOPOOL_PR_OFF "PrOff"
+#define D_NEOPOOL_SETPOINT_OK "Ok"
+#define D_NEOPOOL_COVER "Cover"
+#define D_NEOPOOL_SHOCK "Shock"
+#define D_NEOPOOL_ALARM "! "
+#define D_NEOPOOL_LOW "Low"
+#define D_NEOPOOL_FLOW1 "FL1"
+#define D_NEOPOOL_FLOW2 "FL2"
+#define D_NEOPOOL_PH_HIGH "too high" // ph Alarms
+#define D_NEOPOOL_PH_LOW "too low"
+#define D_NEOPOOL_PUMP_TIME_EXCEEDED "pump time exceeded"
+
#endif // _LANGUAGE_UK_UA_H_
diff --git a/tasmota/language/vi_VN.h b/tasmota/language/vi_VN.h
index e5d7d6c35..94c953f0c 100644
--- a/tasmota/language/vi_VN.h
+++ b/tasmota/language/vi_VN.h
@@ -782,6 +782,11 @@
#define D_SENSOR_SSD1331_CS "SSD1331 CS"
#define D_SENSOR_SSD1331_DC "SSD1331 DC"
#define D_SENSOR_SDCARD_CS "SDCard CS"
+#define D_SENSOR_WIEGAND_D0 "Wiegand D0"
+#define D_SENSOR_WIEGAND_D1 "Wiegand D1"
+#define D_SENSOR_NEOPOOL_TX "NeoPool Tx"
+#define D_SENSOR_NEOPOOL_RX "NeoPool Rx"
+
// Units
#define D_UNIT_AMPERE "A"
@@ -953,4 +958,56 @@
#define D_FP_PASSVERIFY "Password verified" // 0x21 Verify the fingerprint passed
#define D_FP_UNKNOWNERROR "Error" // Any other error
+// xsns_83_neopool.ino
+#define D_NEOPOOL_MACH_NONE "NeoPool" // Machine names
+#define D_NEOPOOL_MACH_HIDROLIFE "Hidrolife (yellow)"
+#define D_NEOPOOL_MACH_AQUASCENIC "Aquascenic (blue)"
+#define D_NEOPOOL_MACH_OXILIFE "Oxilife (green)"
+#define D_NEOPOOL_MACH_BIONET "Bionet (light blue)"
+#define D_NEOPOOL_MACH_HIDRONISER "Hidroniser (red)"
+#define D_NEOPOOL_MACH_UVSCENIC "UVScenic (lilac)"
+#define D_NEOPOOL_MACH_STATION "Station (orange)"
+#define D_NEOPOOL_MACH_BRILIX "Brilix"
+#define D_NEOPOOL_MACH_GENERIC "Generic"
+#define D_NEOPOOL_MACH_BAYROL "Bayrol"
+#define D_NEOPOOL_MACH_HAY "Hay"
+#define D_NEOPOOL_FILTRATION_MANUAL "Manual" // Filtration modes
+#define D_NEOPOOL_FILTRATION_AUTO "Auto"
+#define D_NEOPOOL_FILTRATION_HEATING "Heating"
+#define D_NEOPOOL_FILTRATION_SMART "Smart"
+#define D_NEOPOOL_FILTRATION_INTELLIGENT "Intelligent"
+#define D_NEOPOOL_FILTRATION_BACKWASH "Backwash"
+#define D_NEOPOOL_FILTRATION_NONE "" // Filtration speed level
+#define D_NEOPOOL_FILTRATION_SLOW "slow"
+#define D_NEOPOOL_FILTRATION_MEDIUM "medium"
+#define D_NEOPOOL_FILTRATION_FAST "fast"
+#define D_NEOPOOL_TYPE "Type" // Sensor & relais names
+#define D_NEOPOOL_REDOX "Redox"
+#define D_NEOPOOL_CHLORINE "Chlorine"
+#define D_NEOPOOL_CONDUCTIVITY "Conductivity"
+#define D_NEOPOOL_IONIZATION "Ionization"
+#define D_NEOPOOL_HYDROLYSIS "Hydrolysis"
+#define D_NEOPOOL_RELAY "Relay"
+#define D_NEOPOOL_RELAY_FILTRATION "Filtration"
+#define D_NEOPOOL_RELAY_LIGHT "Light"
+#define D_NEOPOOL_RELAY_PH_ACID "Acid pump"
+#define D_NEOPOOL_RELAY_PH_BASE "Base pump"
+#define D_NEOPOOL_RELAY_RX "Redox level"
+#define D_NEOPOOL_RELAY_CL "Chlorine pump"
+#define D_NEOPOOL_RELAY_CD "Brine pump"
+#define D_NEOPOOL_TIME "Time"
+#define D_NEOPOOL_FILT_MODE "Filtration"
+#define D_NEOPOOL_POLARIZATION "Pol" // Sensor status
+#define D_NEOPOOL_PR_OFF "PrOff"
+#define D_NEOPOOL_SETPOINT_OK "Ok"
+#define D_NEOPOOL_COVER "Cover"
+#define D_NEOPOOL_SHOCK "Shock"
+#define D_NEOPOOL_ALARM "! "
+#define D_NEOPOOL_LOW "Low"
+#define D_NEOPOOL_FLOW1 "FL1"
+#define D_NEOPOOL_FLOW2 "FL2"
+#define D_NEOPOOL_PH_HIGH "too high" // ph Alarms
+#define D_NEOPOOL_PH_LOW "too low"
+#define D_NEOPOOL_PUMP_TIME_EXCEEDED "pump time exceeded"
+
#endif // _LANGUAGE_VI_VN_H_
diff --git a/tasmota/language/zh_CN.h b/tasmota/language/zh_CN.h
index 2041c1483..999227a0d 100644
--- a/tasmota/language/zh_CN.h
+++ b/tasmota/language/zh_CN.h
@@ -782,6 +782,11 @@
#define D_SENSOR_SSD1331_CS "SSD1331 CS"
#define D_SENSOR_SSD1331_DC "SSD1331 DC"
#define D_SENSOR_SDCARD_CS "SDCard CS"
+#define D_SENSOR_WIEGAND_D0 "Wiegand D0"
+#define D_SENSOR_WIEGAND_D1 "Wiegand D1"
+#define D_SENSOR_NEOPOOL_TX "NeoPool Tx"
+#define D_SENSOR_NEOPOOL_RX "NeoPool Rx"
+
// Units
#define D_UNIT_AMPERE "安"
@@ -953,4 +958,56 @@
#define D_FP_PASSVERIFY "Password verified" // 0x21 Verify the fingerprint passed
#define D_FP_UNKNOWNERROR "Error" // Any other error
+// xsns_83_neopool.ino
+#define D_NEOPOOL_MACH_NONE "NeoPool" // Machine names
+#define D_NEOPOOL_MACH_HIDROLIFE "Hidrolife (yellow)"
+#define D_NEOPOOL_MACH_AQUASCENIC "Aquascenic (blue)"
+#define D_NEOPOOL_MACH_OXILIFE "Oxilife (green)"
+#define D_NEOPOOL_MACH_BIONET "Bionet (light blue)"
+#define D_NEOPOOL_MACH_HIDRONISER "Hidroniser (red)"
+#define D_NEOPOOL_MACH_UVSCENIC "UVScenic (lilac)"
+#define D_NEOPOOL_MACH_STATION "Station (orange)"
+#define D_NEOPOOL_MACH_BRILIX "Brilix"
+#define D_NEOPOOL_MACH_GENERIC "Generic"
+#define D_NEOPOOL_MACH_BAYROL "Bayrol"
+#define D_NEOPOOL_MACH_HAY "Hay"
+#define D_NEOPOOL_FILTRATION_MANUAL "Manual" // Filtration modes
+#define D_NEOPOOL_FILTRATION_AUTO "Auto"
+#define D_NEOPOOL_FILTRATION_HEATING "Heating"
+#define D_NEOPOOL_FILTRATION_SMART "Smart"
+#define D_NEOPOOL_FILTRATION_INTELLIGENT "Intelligent"
+#define D_NEOPOOL_FILTRATION_BACKWASH "Backwash"
+#define D_NEOPOOL_FILTRATION_NONE "" // Filtration speed level
+#define D_NEOPOOL_FILTRATION_SLOW "slow"
+#define D_NEOPOOL_FILTRATION_MEDIUM "medium"
+#define D_NEOPOOL_FILTRATION_FAST "fast"
+#define D_NEOPOOL_TYPE "Type" // Sensor & relais names
+#define D_NEOPOOL_REDOX "Redox"
+#define D_NEOPOOL_CHLORINE "Chlorine"
+#define D_NEOPOOL_CONDUCTIVITY "Conductivity"
+#define D_NEOPOOL_IONIZATION "Ionization"
+#define D_NEOPOOL_HYDROLYSIS "Hydrolysis"
+#define D_NEOPOOL_RELAY "Relay"
+#define D_NEOPOOL_RELAY_FILTRATION "Filtration"
+#define D_NEOPOOL_RELAY_LIGHT "Light"
+#define D_NEOPOOL_RELAY_PH_ACID "Acid pump"
+#define D_NEOPOOL_RELAY_PH_BASE "Base pump"
+#define D_NEOPOOL_RELAY_RX "Redox level"
+#define D_NEOPOOL_RELAY_CL "Chlorine pump"
+#define D_NEOPOOL_RELAY_CD "Brine pump"
+#define D_NEOPOOL_TIME "Time"
+#define D_NEOPOOL_FILT_MODE "Filtration"
+#define D_NEOPOOL_POLARIZATION "Pol" // Sensor status
+#define D_NEOPOOL_PR_OFF "PrOff"
+#define D_NEOPOOL_SETPOINT_OK "Ok"
+#define D_NEOPOOL_COVER "Cover"
+#define D_NEOPOOL_SHOCK "Shock"
+#define D_NEOPOOL_ALARM "! "
+#define D_NEOPOOL_LOW "Low"
+#define D_NEOPOOL_FLOW1 "FL1"
+#define D_NEOPOOL_FLOW2 "FL2"
+#define D_NEOPOOL_PH_HIGH "too high" // ph Alarms
+#define D_NEOPOOL_PH_LOW "too low"
+#define D_NEOPOOL_PUMP_TIME_EXCEEDED "pump time exceeded"
+
#endif // _LANGUAGE_ZH_CN_H_
diff --git a/tasmota/language/zh_TW.h b/tasmota/language/zh_TW.h
index 46814ce24..6d4ced015 100644
--- a/tasmota/language/zh_TW.h
+++ b/tasmota/language/zh_TW.h
@@ -782,6 +782,11 @@
#define D_SENSOR_SSD1331_CS "SSD1331 CS"
#define D_SENSOR_SSD1331_DC "SSD1331 DC"
#define D_SENSOR_SDCARD_CS "SDCard CS"
+#define D_SENSOR_WIEGAND_D0 "Wiegand D0"
+#define D_SENSOR_WIEGAND_D1 "Wiegand D1"
+#define D_SENSOR_NEOPOOL_TX "NeoPool Tx"
+#define D_SENSOR_NEOPOOL_RX "NeoPool Rx"
+
// Units
#define D_UNIT_AMPERE "安培"
@@ -953,4 +958,56 @@
#define D_FP_PASSVERIFY "Password verified" // 0x21 Verify the fingerprint passed
#define D_FP_UNKNOWNERROR "Error" // Any other error
+// xsns_83_neopool.ino
+#define D_NEOPOOL_MACH_NONE "NeoPool" // Machine names
+#define D_NEOPOOL_MACH_HIDROLIFE "Hidrolife (yellow)"
+#define D_NEOPOOL_MACH_AQUASCENIC "Aquascenic (blue)"
+#define D_NEOPOOL_MACH_OXILIFE "Oxilife (green)"
+#define D_NEOPOOL_MACH_BIONET "Bionet (light blue)"
+#define D_NEOPOOL_MACH_HIDRONISER "Hidroniser (red)"
+#define D_NEOPOOL_MACH_UVSCENIC "UVScenic (lilac)"
+#define D_NEOPOOL_MACH_STATION "Station (orange)"
+#define D_NEOPOOL_MACH_BRILIX "Brilix"
+#define D_NEOPOOL_MACH_GENERIC "Generic"
+#define D_NEOPOOL_MACH_BAYROL "Bayrol"
+#define D_NEOPOOL_MACH_HAY "Hay"
+#define D_NEOPOOL_FILTRATION_MANUAL "Manual" // Filtration modes
+#define D_NEOPOOL_FILTRATION_AUTO "Auto"
+#define D_NEOPOOL_FILTRATION_HEATING "Heating"
+#define D_NEOPOOL_FILTRATION_SMART "Smart"
+#define D_NEOPOOL_FILTRATION_INTELLIGENT "Intelligent"
+#define D_NEOPOOL_FILTRATION_BACKWASH "Backwash"
+#define D_NEOPOOL_FILTRATION_NONE "" // Filtration speed level
+#define D_NEOPOOL_FILTRATION_SLOW "slow"
+#define D_NEOPOOL_FILTRATION_MEDIUM "medium"
+#define D_NEOPOOL_FILTRATION_FAST "fast"
+#define D_NEOPOOL_TYPE "Type" // Sensor & relais names
+#define D_NEOPOOL_REDOX "Redox"
+#define D_NEOPOOL_CHLORINE "Chlorine"
+#define D_NEOPOOL_CONDUCTIVITY "Conductivity"
+#define D_NEOPOOL_IONIZATION "Ionization"
+#define D_NEOPOOL_HYDROLYSIS "Hydrolysis"
+#define D_NEOPOOL_RELAY "Relay"
+#define D_NEOPOOL_RELAY_FILTRATION "Filtration"
+#define D_NEOPOOL_RELAY_LIGHT "Light"
+#define D_NEOPOOL_RELAY_PH_ACID "Acid pump"
+#define D_NEOPOOL_RELAY_PH_BASE "Base pump"
+#define D_NEOPOOL_RELAY_RX "Redox level"
+#define D_NEOPOOL_RELAY_CL "Chlorine pump"
+#define D_NEOPOOL_RELAY_CD "Brine pump"
+#define D_NEOPOOL_TIME "Time"
+#define D_NEOPOOL_FILT_MODE "Filtration"
+#define D_NEOPOOL_POLARIZATION "Pol" // Sensor status
+#define D_NEOPOOL_PR_OFF "PrOff"
+#define D_NEOPOOL_SETPOINT_OK "Ok"
+#define D_NEOPOOL_COVER "Cover"
+#define D_NEOPOOL_SHOCK "Shock"
+#define D_NEOPOOL_ALARM "! "
+#define D_NEOPOOL_LOW "Low"
+#define D_NEOPOOL_FLOW1 "FL1"
+#define D_NEOPOOL_FLOW2 "FL2"
+#define D_NEOPOOL_PH_HIGH "too high" // ph Alarms
+#define D_NEOPOOL_PH_LOW "too low"
+#define D_NEOPOOL_PUMP_TIME_EXCEEDED "pump time exceeded"
+
#endif // _LANGUAGE_ZH_TW_H_
diff --git a/tasmota/my_user_config.h b/tasmota/my_user_config.h
index 12f47d073..b88604ed3 100644
--- a/tasmota/my_user_config.h
+++ b/tasmota/my_user_config.h
@@ -401,6 +401,9 @@
// #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_MQTT_TLS_DROP_OLD_FINGERPRINT // If you use fingerprint (i.e. not CA) validation, the algorithm changed to a more secure one.
+ // Any valid fingerprint with the old algo will be automatically updated to the new algo.
+ // Enable this if you want to disable the old algo check, which should be more secure
// for USE_4K_RSA (support for 4096 bits certificates, instead of 2048), you need to uncommend `-DUSE_4K_RSA` in `build_flags` from `platform.ini` or `platform_override.ini`
// -- Telegram Protocol ---------------------------
@@ -555,7 +558,8 @@
// #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 // [I2cDriver54] Enable support for VL53L1X sensor (I2C address 0x29) using Pololu VL53L1X library (+2k9 code)
+// #define USE_VL53L1X // [I2cDriver54] Enable VL53L1X time of flight sensor (I2C address 0x29) using Pololu VL53L1X library (+2k9 code)
+// #define USE_TOF10120 // [I2cDriver57] Enable TOF10120 time of flight sensor (I2C address 0x52) (+0k6 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)
@@ -588,6 +592,7 @@
// #define USE_EZODO // [I2cDriver55] Enable support for EZO's DO sensor (+0k3 code) - Shared EZO code required for any EZO device (+1k2 code)
// #define USE_EZORGB // [I2cDriver55] Enable support for EZO's RGB sensor (+0k5 code) - Shared EZO code required for any EZO device (+1k2 code)
// #define USE_EZOPMP // [I2cDriver55] Enable support for EZO's PMP sensor (+0k3 code) - Shared EZO code required for any EZO device (+1k2 code)
+// #define USE_SEESAW_SOIL // [I2cDriver56] Enable Capacitice Soil Moisture & Temperature Sensor (I2C addresses 0x36 - 0x39) (+1k3 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
@@ -710,6 +715,7 @@
#define MAX31865_REF_RES 430 // Reference resistor (Usually 430Ω for a PT100, 4300Ω for a PT1000)
#define MAX31865_PTD_BIAS 0 // To calibrate your not-so-good PTD
//#define USE_LMT01 // Add support for TI LMT01 temperature sensor, count pulses on single GPIO (+0k5 code)
+//#define USE_WIEGAND // Add support for 24/26/32/34 bit RFID Wiegand interface (D0/D1) (+1k7 code)
// -- IR Remote features - all protocols from IRremoteESP8266 --------------------------
// IR Full Protocols mode is activated through platform.io only.
@@ -754,8 +760,8 @@
// Auto-binding constants, see `Z_autoAttributeReporting`
// Below are the threshold for attribute reporting
- #define USE_ZIGBEE_AUTOBIND_BATTVOLTAGE 0.1 // V
- #define USE_ZIGBEE_AUTOBIND_BATTPERCENT 1 // %
+ #define USE_ZIGBEE_AUTOBIND_BATTVOLTAGE 0.2 // V
+ #define USE_ZIGBEE_AUTOBIND_BATTPERCENT 5 // %
#define USE_ZIGBEE_AUTOBIND_TEMPERATURE 0.5 // °C
#define USE_ZIGBEE_AUTOBIND_HEATDEMAND 10 // %
#define USE_ZIGBEE_AUTOBIND_PRESSURE 1 // hPA
@@ -767,8 +773,6 @@
#define USE_ZIGBEE_MAXTIME_SENSOR 60*60 // 1h
#define USE_ZIGBEE_MAXTIME_LIGHT 60*60 // 1h
-
-
// -- Other sensors/drivers -----------------------
//#define USE_TM1638 // Add support for TM1638 switches copying Switch1 .. Switch8 (+1k code)
@@ -794,6 +798,9 @@
//#define USE_PROMETHEUS // Add support for https://prometheus.io/ metrics exporting over HTTP /metrics endpoint
+//#define USE_NEOPOOL // Add support for Sugar Valley NeoPool Controller - also known under brands Hidrolife, Aquascenic, Oxilife, Bionet, Hidroniser, UVScenic, Station, Brilix, Bayrol and Hay (+6k flash, +60 mem)
+// #define NEOPOOL_MODBUS_ADDRESS 1 // Any modbus address
+
// -- Thermostat control ----------------------------
//#define USE_THERMOSTAT // Add support for Thermostat
#define THERMOSTAT_CONTROLLER_OUTPUTS 1 // Number of outputs to be controlled independently
@@ -842,6 +849,10 @@
#ifdef ESP32
//#define USE_ETHERNET // Add support for ethernet (Currently fixed for Olimex ESP32-PoE)
+// #define USE_WT32_ETH01 // Add support for Wireless-Tag WT32-ETH01
+// #define ETH_TYPE 0 // [EthType] 0 = ETH_PHY_LAN8720, 1 = ETH_PHY_TLK110, 2 = ETH_PHY_IP101
+// #define ETH_ADDR 1 // [EthAddress] 0 = PHY0 .. 31 = PHY31
+// #define ETH_CLKMODE 0 // [EthClockMode] 0 = ETH_CLOCK_GPIO0_IN, 1 = ETH_CLOCK_GPIO0_OUT, 2 = ETH_CLOCK_GPIO16_OUT, 3 = ETH_CLOCK_GPIO17_OUT
// Olimex ESP32-PoE
#define ETH_TYPE 0 // [EthType] 0 = ETH_PHY_LAN8720, 1 = ETH_PHY_TLK110, 2 = ETH_PHY_IP101
#define ETH_ADDR 0 // [EthAddress] 0 = PHY0 .. 31 = PHY31
@@ -849,16 +860,15 @@
// wESP32-PoE
// #define ETH_TYPE 0 // [EthType] 0 = ETH_PHY_LAN8720, 1 = ETH_PHY_TLK110, 2 = ETH_PHY_IP101
// #define ETH_ADDR 0 // [EthAddress] 0 = PHY0 .. 31 = PHY31
-// #define ETH_CLKMODE 0 // [EthClockMode] 0 = ETH_CLOCK_GPIO0_IN, 1 = ETH_CLOCK_GPIO0_OUT, 2 = ETH_CLOCK_GPIO16_OUT, 3 = ETH_CLOCK_GPIO17_OUT
- // Wireless-Tag WT32-ETH01
-// #define ETH_TYPE 0 // [EthType] 0 = ETH_PHY_LAN8720, 1 = ETH_PHY_TLK110, 2 = ETH_PHY_IP101
-// #define ETH_ADDR 1 // [EthAddress] 0 = PHY0 .. 31 = PHY31
// #define ETH_CLKMODE 0 // [EthClockMode] 0 = ETH_CLOCK_GPIO0_IN, 1 = ETH_CLOCK_GPIO0_OUT, 2 = ETH_CLOCK_GPIO16_OUT, 3 = ETH_CLOCK_GPIO17_OUT
#define USE_ADC // Add support for ADC on GPIO32 to GPIO39
//#define USE_SPI // Add support for hardware SPI
#define USE_MI_ESP32 // Add support for ESP32 as a BLE-bridge (+9k2 mem, +292k flash)
+//#define USE_BLE_ESP32 // Add support for ESP32 as a BLE-bridge (+9k2? mem, +292k? flash)
+//#define USE_IBEACON // Add support for bluetooth LE passive scan of ibeacon devices (uses HM17 module)
+//#define USE_IBEACON_ESP32
//#define USE_WEBCAM // Add support for webcam
#endif // ESP32
diff --git a/tasmota/sendemail.ino b/tasmota/sendemail.ino
index 6d075599b..cd7f849ce 100644
--- a/tasmota/sendemail.ino
+++ b/tasmota/sendemail.ino
@@ -80,7 +80,7 @@ uint16_t SendMail(char *buffer) {
cmd=endcmd+1;
#ifdef DEBUG_EMAIL_PORT
- AddLog_P(LOG_LEVEL_INFO, PSTR("mailsize: %d"),blen);
+ AddLog(LOG_LEVEL_INFO, PSTR("mailsize: %d"),blen);
#endif
mserv=strtok(params,":");
@@ -148,7 +148,7 @@ uint16_t SendMail(char *buffer) {
#ifdef DEBUG_EMAIL_PORT
- AddLog_P(LOG_LEVEL_INFO, PSTR("%s - %d - %s - %s"),mserv,port,user,passwd);
+ AddLog(LOG_LEVEL_INFO, PSTR("%s - %d - %s - %s"),mserv,port,user,passwd);
#endif
// 2 seconds timeout
@@ -216,12 +216,12 @@ String buffer;
client->setTimeout(timeout);
// smtp connect
#ifdef DEBUG_EMAIL_PORT
- AddLog_P(LOG_LEVEL_INFO, PSTR("Connecting: %s on port %d"),host.c_str(),port);
+ AddLog(LOG_LEVEL_INFO, PSTR("Connecting: %s on port %d"),host.c_str(),port);
#endif
if (!client->connect(host.c_str(), port)) {
#ifdef DEBUG_EMAIL_PORT
- AddLog_P(LOG_LEVEL_INFO, PSTR("Connection failed"));
+ AddLog(LOG_LEVEL_INFO, PSTR("Connection failed"));
#endif
goto exit;
}
@@ -389,7 +389,7 @@ void xsend_message_txt(char *msg) {
#ifdef DEBUG_EMAIL_PORT
AddLog_P(LOG_LEVEL_INFO, PSTR("%s"),msg);
#endif
-#if (defined(USE_SCRIPT_FATFS) && defined(USE_SCRIPT)) || defined(UFILESYSTEM)
+#if (defined(USE_SCRIPT_FATFS) && defined(USE_SCRIPT)) || defined(USE_UFILESYS)
if (*msg=='@') {
msg++;
attach_File(msg);
@@ -415,7 +415,7 @@ void xsend_message_txt(char *msg) {
#endif
}
-#if (defined(USE_SCRIPT_FATFS) && defined(USE_SCRIPT)) || defined(UFILESYSTEM)
+#if (defined(USE_SCRIPT_FATFS) && defined(USE_SCRIPT)) || defined(USE_UFILESYS)
#include
extern FS *ufsp;
@@ -458,7 +458,7 @@ void attach_Array(char *aname) {
g_client->print(F("Content-Type: text/plain\r\n"));
if (array && alen) {
#ifdef DEBUG_EMAIL_PORT
- AddLog_P(LOG_LEVEL_INFO, PSTR("array found %d"),alen);
+ AddLog(LOG_LEVEL_INFO, PSTR("array found %d"),alen);
#endif
char buff[64];
sprintf_P(buff,PSTR("Content-Disposition: attachment; filename=\"%s.txt\"\r\n\r\n"), aname);
@@ -545,7 +545,7 @@ uint16_t SendMail(char *buffer) {
// return if not enough memory
uint32_t mem=ESP.getFreeHeap();
- //AddLog_P(LOG_LEVEL_INFO, PSTR("heap: %d"),mem);
+ //AddLog(LOG_LEVEL_INFO, PSTR("heap: %d"),mem);
if (mem 0x0606000A)) ? 3584 : sizeof(Settings);
return GetCfgCrc16((uint8_t*)&Settings, size);
}
-uint32_t GetCfgCrc32(uint8_t *bytes, uint32_t size)
-{
+uint32_t GetCfgCrc32(uint8_t *bytes, uint32_t size) {
// https://create.stephan-brumme.com/crc32/#bitwise
uint32_t crc = 0;
@@ -295,13 +281,11 @@ uint32_t GetCfgCrc32(uint8_t *bytes, uint32_t size)
return ~crc;
}
-uint32_t GetSettingsCrc32(void)
-{
+uint32_t GetSettingsCrc32(void) {
return GetCfgCrc32((uint8_t*)&Settings, sizeof(Settings) -4); // Skip crc32
}
-void SettingsSaveAll(void)
-{
+void SettingsSaveAll(void) {
if (Settings.flag.save_state) {
Settings.power = TasmotaGlobal.power;
} else {
@@ -338,7 +322,7 @@ void UpdateQuickPowerCycle(bool update) {
} else {
qpc_buffer[0] = 0;
ESP.flashWrite(qpc_location + (counter * 4), (uint32*)&qpc_buffer, 4);
- AddLog_P(LOG_LEVEL_INFO, PSTR("QPC: Count %d"), counter);
+ AddLog(LOG_LEVEL_INFO, PSTR("QPC: Count %d"), counter);
}
}
else if ((qpc_buffer[0] != QPC_SIGNATURE) || (0 == qpc_buffer[1])) {
@@ -346,7 +330,7 @@ void UpdateQuickPowerCycle(bool update) {
// Assume flash is default all ones and setting a bit to zero does not need an erase
if (ESP.flashEraseSector(qpc_sector)) {
ESP.flashWrite(qpc_location, (uint32*)&qpc_buffer, 4);
- AddLog_P(LOG_LEVEL_INFO, PSTR("QPC: Reset"));
+ AddLog(LOG_LEVEL_INFO, PSTR("QPC: Reset"));
}
}
#endif // ESP8266
@@ -363,13 +347,13 @@ void UpdateQuickPowerCycle(bool update) {
} else {
pc_register = 0xFFA55AF0 | counter;
QPCWrite(&pc_register, sizeof(pc_register));
- AddLog_P(LOG_LEVEL_INFO, PSTR("QPC: Count %d"), counter);
+ AddLog(LOG_LEVEL_INFO, PSTR("QPC: Count %d"), counter);
}
}
else if (pc_register != QPC_SIGNATURE) {
pc_register = QPC_SIGNATURE;
QPCWrite(&pc_register, sizeof(pc_register));
- AddLog_P(LOG_LEVEL_INFO, PSTR("QPC: Reset"));
+ AddLog(LOG_LEVEL_INFO, PSTR("QPC: Reset"));
}
#endif // ESP32
@@ -430,12 +414,12 @@ bool SettingsUpdateText(uint32_t index, const char* replace_me) {
uint32_t current_len = end_pos - start_pos;
int diff = replace_len - current_len;
-// AddLog_P(LOG_LEVEL_DEBUG, PSTR("TST: start %d, end %d, len %d, current %d, replace %d, diff %d"),
+// AddLog(LOG_LEVEL_DEBUG, PSTR("TST: start %d, end %d, len %d, current %d, replace %d, diff %d"),
// start_pos, end_pos, char_len, current_len, replace_len, diff);
int too_long = (char_len + diff) - settings_text_size;
if (too_long > 0) {
- AddLog_P(LOG_LEVEL_INFO, PSTR(D_LOG_CONFIG "Text overflow by %d char(s)"), too_long);
+ AddLog(LOG_LEVEL_INFO, PSTR(D_LOG_CONFIG "Text overflow by %d char(s)"), too_long);
return false; // Replace text too long
}
@@ -457,16 +441,15 @@ bool SettingsUpdateText(uint32_t index, const char* replace_me) {
}
#ifdef DEBUG_FUNC_SETTINGSUPDATETEXT
- AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_CONFIG "CR %d/%d, Busy %d, Id %02d = \"%s\""), GetSettingsTextLen(), settings_text_size, settings_text_busy_count, index_save, replace);
+ AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_CONFIG "CR %d/%d, Busy %d, Id %02d = \"%s\""), GetSettingsTextLen(), settings_text_size, settings_text_busy_count, index_save, replace);
#else
- AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_CONFIG "CR %d/%d, Busy %d"), GetSettingsTextLen(), settings_text_size, settings_text_busy_count);
+ AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_CONFIG "CR %d/%d, Busy %d"), GetSettingsTextLen(), settings_text_size, settings_text_busy_count);
#endif
return true;
}
-char* SettingsText(uint32_t index)
-{
+char* SettingsText(uint32_t index) {
char* position = Settings.text_pool;
if (index >= SET_MAX) {
@@ -484,19 +467,16 @@ char* SettingsText(uint32_t index)
* Config Save - Save parameters to Flash ONLY if any parameter has changed
\*********************************************************************************************/
-void UpdateBackwardCompatibility(void)
-{
+void UpdateBackwardCompatibility(void) {
// Perform updates for backward compatibility
strlcpy(Settings.user_template_name, SettingsText(SET_TEMPLATE_NAME), sizeof(Settings.user_template_name));
}
-uint32_t GetSettingsAddress(void)
-{
+uint32_t GetSettingsAddress(void) {
return settings_location * SPI_FLASH_SEC_SIZE;
}
-void SettingsSave(uint8_t rotate)
-{
+void SettingsSave(uint8_t rotate) {
/* Save configuration in eeprom or one of 7 slots below
*
* rotate 0 = Save in next flash slot
@@ -508,22 +488,20 @@ void SettingsSave(uint8_t rotate)
#ifndef FIRMWARE_MINIMAL
UpdateBackwardCompatibility();
if ((GetSettingsCrc32() != settings_crc32) || rotate) {
- if (1 == rotate) { // Use eeprom flash slot only and disable flash rotate from now on (upgrade)
+ if (1 == rotate) { // Use eeprom flash slot only and disable flash rotate from now on (upgrade)
TasmotaGlobal.stop_flash_rotate = 1;
}
- if (2 == rotate) { // Use eeprom flash slot and erase next flash slots if stop_flash_rotate is off (default)
- settings_location = FLASH_EEPROM_START;
- }
- if (TasmotaGlobal.stop_flash_rotate) {
- settings_location = FLASH_EEPROM_START;
- } else {
- if (settings_location == FLASH_EEPROM_START) {
+
+ if (TasmotaGlobal.stop_flash_rotate || (2 == rotate)) { // Use eeprom flash slot and erase next flash slots if stop_flash_rotate is off (default)
+ settings_location = EEPROM_LOCATION;
+ } else { // Rotate flash slots
+ if (settings_location == EEPROM_LOCATION) {
settings_location = SETTINGS_LOCATION;
} else {
settings_location--;
}
if (settings_location <= (SETTINGS_LOCATION - CFG_ROTATES)) {
- settings_location = FLASH_EEPROM_START;
+ settings_location = EEPROM_LOCATION;
}
}
@@ -534,7 +512,7 @@ void SettingsSave(uint8_t rotate)
Settings.cfg_timestamp++;
}
Settings.cfg_size = sizeof(Settings);
- Settings.cfg_crc = GetSettingsCrc(); // Keep for backward compatibility in case of fall-back just after upgrade
+ Settings.cfg_crc = GetSettingsCrc(); // Keep for backward compatibility in case of fall-back just after upgrade
Settings.cfg_crc32 = GetSettingsCrc32();
#ifdef ESP8266
@@ -545,17 +523,17 @@ void SettingsSave(uint8_t rotate)
ESP.flashWrite(settings_location * SPI_FLASH_SEC_SIZE, (uint32*)&Settings, sizeof(Settings));
}
- if (!TasmotaGlobal.stop_flash_rotate && rotate) {
+ if (!TasmotaGlobal.stop_flash_rotate && rotate) { // SetOption12 - (Settings) Switch between dynamic (0) or fixed (1) slot flash save location
for (uint32_t i = 0; i < CFG_ROTATES; i++) {
- ESP.flashEraseSector(SETTINGS_LOCATION -i); // Delete previous configurations by resetting to 0xFF
+ ESP.flashEraseSector(SETTINGS_LOCATION -i); // Delete previous configurations by resetting to 0xFF
delay(1);
}
}
- AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_CONFIG D_SAVED_TO_FLASH_AT " %X, " D_COUNT " %d, " D_BYTES " %d"), settings_location, Settings.save_flag, sizeof(Settings));
+ AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_CONFIG D_SAVED_TO_FLASH_AT " %X, " D_COUNT " %d, " D_BYTES " %d"), settings_location, Settings.save_flag, sizeof(Settings));
#endif // ESP8266
#ifdef ESP32
SettingsWrite(&Settings, sizeof(Settings));
- AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_CONFIG "Saved, " D_COUNT " %d, " D_BYTES " %d"), Settings.save_flag, sizeof(Settings));
+ AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_CONFIG "Saved, " D_COUNT " %d, " D_BYTES " %d"), Settings.save_flag, sizeof(Settings));
#endif // ESP32
settings_crc32 = Settings.cfg_crc32;
@@ -581,7 +559,7 @@ void SettingsLoad(void) {
#endif // USE_UFILESYS
while (slot <= max_slots) { // Read all config pages in search of valid and latest
if (slot > 0) {
- flash_location = (1 == slot) ? FLASH_EEPROM_START : (2 == slot) ? SETTINGS_LOCATION : flash_location -1;
+ flash_location = (1 == slot) ? EEPROM_LOCATION : (2 == slot) ? SETTINGS_LOCATION : flash_location -1;
ESP.flashRead(flash_location * SPI_FLASH_SEC_SIZE, (uint32*)&Settings, sizeof(Settings));
}
if ((Settings.cfg_crc32 != 0xFFFFFFFF) && (Settings.cfg_crc32 != 0x00000000) && (Settings.cfg_crc32 == GetSettingsCrc32())) {
@@ -600,19 +578,19 @@ void SettingsLoad(void) {
#ifdef USE_UFILESYS
if (1 == settings_location) {
TfsLoadFile(TASM_FILE_SETTINGS, (uint8_t*)&Settings, sizeof(Settings));
- AddLog_P(LOG_LEVEL_NONE, PSTR(D_LOG_CONFIG "Loaded from File, " D_COUNT " %lu"), Settings.save_flag);
+ AddLog(LOG_LEVEL_NONE, PSTR(D_LOG_CONFIG "Loaded from File, " D_COUNT " %lu"), Settings.save_flag);
} else
#endif // USE_UFILESYS
{
ESP.flashRead(settings_location * SPI_FLASH_SEC_SIZE, (uint32*)&Settings, sizeof(Settings));
- AddLog_P(LOG_LEVEL_NONE, PSTR(D_LOG_CONFIG D_LOADED_FROM_FLASH_AT " %X, " D_COUNT " %lu"), settings_location, Settings.save_flag);
+ AddLog(LOG_LEVEL_NONE, PSTR(D_LOG_CONFIG D_LOADED_FROM_FLASH_AT " %X, " D_COUNT " %lu"), settings_location, Settings.save_flag);
}
}
#endif // ESP8266
#ifdef ESP32
uint32_t source = SettingsRead(&Settings, sizeof(Settings));
if (source) { settings_location = 1; }
- AddLog_P(LOG_LEVEL_NONE, PSTR(D_LOG_CONFIG "Loaded from %s, " D_COUNT " %lu"), (source)?"File":"Nvm", Settings.save_flag);
+ AddLog(LOG_LEVEL_NONE, PSTR(D_LOG_CONFIG "Loaded from %s, " D_COUNT " %lu"), (source)?"File":"Nvm", Settings.save_flag);
#endif // ESP32
#ifndef FIRMWARE_MINIMAL
@@ -620,7 +598,7 @@ void SettingsLoad(void) {
#ifdef USE_UFILESYS
if (TfsLoadFile(TASM_FILE_SETTINGS_LKG, (uint8_t*)&Settings, sizeof(Settings)) && (Settings.cfg_crc32 == GetSettingsCrc32())) {
settings_location = 1;
- AddLog_P(LOG_LEVEL_NONE, PSTR(D_LOG_CONFIG "Loaded from LKG File, " D_COUNT " %lu"), Settings.save_flag);
+ AddLog(LOG_LEVEL_NONE, PSTR(D_LOG_CONFIG "Loaded from LKG File, " D_COUNT " %lu"), Settings.save_flag);
} else
#endif // USE_UFILESYS
{
@@ -639,8 +617,7 @@ uint32_t CfgTime(void) {
}
#ifdef ESP8266
-void SettingsErase(uint8_t type)
-{
+void SettingsErase(uint8_t type) {
/*
For Arduino core and SDK:
Erase only works from flash start address to SDK recognized flash end address (flashchip->chip_size = ESP.getFlashChipSize).
@@ -672,7 +649,7 @@ void SettingsErase(uint8_t type)
#endif
*/
EsptoolErase(_sectorStart, FLASH_FS_START);
- _sectorStart = FLASH_EEPROM_START;
+ _sectorStart = EEPROM_LOCATION;
_sectorEnd = ESP.getFlashChipSize() / SPI_FLASH_SEC_SIZE; // Flash size as seen by SDK
}
else if (3 == type) { // QPC Reached = QPC and Tasmota and SDK parameter area (0x0F3xxx - 0x0FFFFF)
@@ -680,11 +657,11 @@ void SettingsErase(uint8_t type)
TfsDeleteFile(TASM_FILE_SETTINGS);
#endif
EsptoolErase(SETTINGS_LOCATION - CFG_ROTATES, SETTINGS_LOCATION +1);
- _sectorStart = FLASH_EEPROM_START;
+ _sectorStart = EEPROM_LOCATION;
_sectorEnd = ESP.getFlashChipSize() / SPI_FLASH_SEC_SIZE; // Flash size as seen by SDK
}
else if (4 == type) { // WIFI_FORCE_RF_CAL_ERASE = SDK wifi calibration
- _sectorStart = FLASH_EEPROM_START +1; // SDK phy area and Core calibration sector (0x0XFC000)
+ _sectorStart = EEPROM_LOCATION +1; // SDK phy area and Core calibration sector (0x0XFC000)
_sectorEnd = _sectorStart +1; // SDK end of phy area and Core calibration sector (0x0XFCFFF)
}
@@ -693,8 +670,7 @@ void SettingsErase(uint8_t type)
}
#endif // ESP8266
-void SettingsSdkErase(void)
-{
+void SettingsSdkErase(void) {
WiFi.disconnect(false); // Delete SDK wifi config
SettingsErase(1);
delay(1000);
@@ -702,16 +678,14 @@ void SettingsSdkErase(void)
/********************************************************************************************/
-void SettingsDefault(void)
-{
- AddLog_P(LOG_LEVEL_NONE, PSTR(D_LOG_CONFIG D_USE_DEFAULTS));
+void SettingsDefault(void) {
+ AddLog(LOG_LEVEL_NONE, PSTR(D_LOG_CONFIG D_USE_DEFAULTS));
SettingsDefaultSet1();
SettingsDefaultSet2();
SettingsSave(2);
}
-void SettingsDefaultSet1(void)
-{
+void SettingsDefaultSet1(void) {
memset(&Settings, 0x00, sizeof(Settings));
Settings.cfg_holder = (uint16_t)CFG_HOLDER;
@@ -726,8 +700,7 @@ void SettingsDefaultSet1(void)
const uint8_t default_fingerprint1[] PROGMEM = { MQTT_FINGERPRINT1 };
const uint8_t default_fingerprint2[] PROGMEM = { MQTT_FINGERPRINT2 };
-void SettingsDefaultSet2(void)
-{
+void SettingsDefaultSet2(void) {
memset((char*)&Settings +16, 0x00, sizeof(Settings) -16);
// this little trick allows GCC to optimize the assignment by grouping values and doing only ORs
@@ -812,10 +785,10 @@ void SettingsDefaultSet2(void)
flag3.use_wifi_rescan |= WIFI_SCAN_REGULARLY;
Settings.wifi_output_power = 170;
Settings.param[P_ARP_GRATUITOUS] = WIFI_ARP_INTERVAL;
- ParseIp(&Settings.ip_address[0], WIFI_IP_ADDRESS);
- ParseIp(&Settings.ip_address[1], WIFI_GATEWAY);
- ParseIp(&Settings.ip_address[2], WIFI_SUBNETMASK);
- ParseIp(&Settings.ip_address[3], WIFI_DNS);
+ ParseIPv4(&Settings.ipv4_address[0], PSTR(WIFI_IP_ADDRESS));
+ ParseIPv4(&Settings.ipv4_address[1], PSTR(WIFI_GATEWAY));
+ ParseIPv4(&Settings.ipv4_address[2], PSTR(WIFI_SUBNETMASK));
+ ParseIPv4(&Settings.ipv4_address[3], PSTR(WIFI_DNS));
Settings.sta_config = WIFI_CONFIG_TOOL;
// Settings.sta_active = 0;
SettingsUpdateText(SET_STASSID1, PSTR(STA_SSID1));
@@ -865,22 +838,22 @@ void SettingsDefaultSet2(void)
flag3.grouptopic_mode |= MQTT_GROUPTOPIC_FORMAT;
SettingsUpdateText(SET_MQTT_HOST, MQTT_HOST);
Settings.mqtt_port = MQTT_PORT;
- SettingsUpdateText(SET_MQTT_CLIENT, MQTT_CLIENT_ID);
- SettingsUpdateText(SET_MQTT_USER, MQTT_USER);
- SettingsUpdateText(SET_MQTT_PWD, MQTT_PASS);
- SettingsUpdateText(SET_MQTT_TOPIC, MQTT_TOPIC);
- SettingsUpdateText(SET_MQTT_BUTTON_TOPIC, MQTT_BUTTON_TOPIC);
- SettingsUpdateText(SET_MQTT_SWITCH_TOPIC, MQTT_SWITCH_TOPIC);
- SettingsUpdateText(SET_MQTT_GRP_TOPIC, MQTT_GRPTOPIC);
- SettingsUpdateText(SET_MQTT_FULLTOPIC, MQTT_FULLTOPIC);
+ SettingsUpdateText(SET_MQTT_CLIENT, PSTR(MQTT_CLIENT_ID));
+ SettingsUpdateText(SET_MQTT_USER, PSTR(MQTT_USER));
+ SettingsUpdateText(SET_MQTT_PWD, PSTR(MQTT_PASS));
+ SettingsUpdateText(SET_MQTT_TOPIC, PSTR(MQTT_TOPIC));
+ SettingsUpdateText(SET_MQTT_BUTTON_TOPIC, PSTR(MQTT_BUTTON_TOPIC));
+ SettingsUpdateText(SET_MQTT_SWITCH_TOPIC, PSTR(MQTT_SWITCH_TOPIC));
+ SettingsUpdateText(SET_MQTT_GRP_TOPIC, PSTR(MQTT_GRPTOPIC));
+ SettingsUpdateText(SET_MQTT_FULLTOPIC, PSTR(MQTT_FULLTOPIC));
Settings.mqtt_retry = MQTT_RETRY_SECS;
- SettingsUpdateText(SET_MQTTPREFIX1, SUB_PREFIX);
- SettingsUpdateText(SET_MQTTPREFIX2, PUB_PREFIX);
- SettingsUpdateText(SET_MQTTPREFIX3, PUB_PREFIX2);
- SettingsUpdateText(SET_STATE_TXT1, MQTT_STATUS_OFF);
- SettingsUpdateText(SET_STATE_TXT2, MQTT_STATUS_ON);
- SettingsUpdateText(SET_STATE_TXT3, MQTT_CMND_TOGGLE);
- SettingsUpdateText(SET_STATE_TXT4, MQTT_CMND_HOLD);
+ SettingsUpdateText(SET_MQTTPREFIX1, PSTR(SUB_PREFIX));
+ SettingsUpdateText(SET_MQTTPREFIX2, PSTR(PUB_PREFIX));
+ SettingsUpdateText(SET_MQTTPREFIX3, PSTR(PUB_PREFIX2));
+ SettingsUpdateText(SET_STATE_TXT1, PSTR(MQTT_STATUS_OFF));
+ SettingsUpdateText(SET_STATE_TXT2, PSTR(MQTT_STATUS_ON));
+ SettingsUpdateText(SET_STATE_TXT3, PSTR(MQTT_CMND_TOGGLE));
+ SettingsUpdateText(SET_STATE_TXT4, PSTR(MQTT_CMND_HOLD));
memcpy_P(Settings.mqtt_fingerprint[0], default_fingerprint1, sizeof(default_fingerprint1));
memcpy_P(Settings.mqtt_fingerprint[1], default_fingerprint2, sizeof(default_fingerprint2));
Settings.tele_period = TELE_PERIOD;
@@ -1099,8 +1072,7 @@ void SettingsDefaultSet2(void)
/********************************************************************************************/
-void SettingsResetStd(void)
-{
+void SettingsResetStd(void) {
Settings.tflag[0].hemis = TIME_STD_HEMISPHERE;
Settings.tflag[0].week = TIME_STD_WEEK;
Settings.tflag[0].dow = TIME_STD_DAY;
@@ -1109,8 +1081,7 @@ void SettingsResetStd(void)
Settings.toffset[0] = TIME_STD_OFFSET;
}
-void SettingsResetDst(void)
-{
+void SettingsResetDst(void) {
Settings.tflag[1].hemis = TIME_DST_HEMISPHERE;
Settings.tflag[1].week = TIME_DST_WEEK;
Settings.tflag[1].dow = TIME_DST_DAY;
@@ -1119,16 +1090,14 @@ void SettingsResetDst(void)
Settings.toffset[1] = TIME_DST_OFFSET;
}
-void SettingsDefaultWebColor(void)
-{
+void SettingsDefaultWebColor(void) {
char scolor[10];
for (uint32_t i = 0; i < COL_LAST; i++) {
WebHexCode(i, GetTextIndexed(scolor, sizeof(scolor), i, kWebColors));
}
}
-void SettingsEnableAllI2cDrivers(void)
-{
+void SettingsEnableAllI2cDrivers(void) {
Settings.i2c_drivers[0] = 0xFFFFFFFF;
Settings.i2c_drivers[1] = 0xFFFFFFFF;
Settings.i2c_drivers[2] = 0xFFFFFFFF;
@@ -1136,8 +1105,7 @@ void SettingsEnableAllI2cDrivers(void)
/********************************************************************************************/
-void SettingsDelta(void)
-{
+void SettingsDelta(void) {
if (Settings.version != VERSION) { // Fix version dependent changes
#ifdef ESP8266
diff --git a/tasmota/support.ino b/tasmota/support.ino
index 0d5ce5b94..b0b90b981 100644
--- a/tasmota/support.ino
+++ b/tasmota/support.ino
@@ -49,10 +49,10 @@ void OsWatchTicker(void)
#ifdef DEBUG_THEO
int32_t rssi = WiFi.RSSI();
- AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_APPLICATION D_OSWATCH " FreeRam %d, rssi %d %% (%d dBm), last_run %d"), ESP_getFreeHeap(), WifiGetRssiAsQuality(rssi), rssi, last_run);
+ AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_APPLICATION D_OSWATCH " FreeRam %d, rssi %d %% (%d dBm), last_run %d"), ESP_getFreeHeap(), WifiGetRssiAsQuality(rssi), rssi, last_run);
#endif // DEBUG_THEO
if (last_run >= (OSWATCH_RESET_TIME * 1000)) {
-// AddLog_P(LOG_LEVEL_INFO, PSTR(D_LOG_APPLICATION D_OSWATCH " " D_BLOCKED_LOOP ". " D_RESTARTING)); // Save iram space
+// AddLog(LOG_LEVEL_INFO, PSTR(D_LOG_APPLICATION D_OSWATCH " " D_BLOCKED_LOOP ". " D_RESTARTING)); // Save iram space
RtcSettings.oswatch_blocked_loop = 1;
RtcSettingsSave();
@@ -252,22 +252,59 @@ uint32_t ChrCount(const char *str, const char *delim) {
return count;
}
-// Function to return a substring defined by a delimiter at an index
-char* subStr(char* dest, char* str, const char *delim, int index)
-{
- char *act;
- char *sub = nullptr;
- char *ptr;
- int i;
+uint32_t ArgC(void) {
+ return (XdrvMailbox.data_len > 0) ? ChrCount(XdrvMailbox.data, ",") +1 : 0;
+}
- // Since strtok consumes the first arg, make a copy
- strncpy(dest, str, strlen(str)+1);
- for (i = 1, act = dest; i <= index; i++, act = nullptr) {
- sub = strtok_r(act, delim, &ptr);
- if (sub == nullptr) break;
+// Function to return a substring defined by a delimiter at an index
+char* subStr(char* dest, char* str, const char *delim, int index) {
+ char* write = dest;
+ char* read = str;
+ char ch = '.';
+
+ while (index && (ch != '\0')) {
+ ch = *read++;
+ if (strchr(delim, ch)) {
+ index--;
+ if (index) { write = dest; }
+ } else {
+ *write++ = ch;
+ }
}
- sub = Trim(sub);
- return sub;
+ *write = '\0';
+ dest = Trim(dest);
+ return dest;
+}
+
+char* ArgV(char* dest, int index) {
+ return subStr(dest, XdrvMailbox.data, ",", index);
+}
+
+uint32_t ArgVul(uint32_t *args, uint32_t count) {
+ uint32_t argc = ArgC();
+ if (argc > count) { argc = count; }
+ count = argc;
+ if (argc) {
+ char argument[XdrvMailbox.data_len];
+ for (uint32_t i = 0; i < argc; i++) {
+ if (strlen(ArgV(argument, i +1))) {
+ args[i] = strtoul(argument, nullptr, 0);
+ } else {
+ count--;
+ }
+ }
+ }
+ return count;
+}
+
+uint32_t ParseParameters(uint32_t count, uint32_t *params) {
+ // Destroys XdrvMailbox.data
+ char *p;
+ uint32_t i = 0;
+ for (char *str = strtok_r(XdrvMailbox.data, ", ", &p); str && i < count; str = strtok_r(nullptr, ", ", &p), i++) {
+ params[i] = strtoul(str, nullptr, 0);
+ }
+ return i;
}
float CharToFloat(const char *str)
@@ -322,69 +359,10 @@ int TextToInt(char *str)
return strtol(str, &p, radix);
}
-char* ulltoa(unsigned long long value, char *str, int radix)
-{
- char digits[64];
- char *dst = str;
- int i = 0;
-
-// if (radix < 2 || radix > 36) { radix = 10; }
-
- do {
- int n = value % radix;
- digits[i++] = (n < 10) ? (char)n+'0' : (char)n-10+'A';
- value /= radix;
- } while (value != 0);
-
- while (i > 0) { *dst++ = digits[--i]; }
-
- *dst = 0;
- return str;
-}
-
-// see https://stackoverflow.com/questions/6357031/how-do-you-convert-a-byte-array-to-a-hexadecimal-string-in-c
-// char* ToHex_P(unsigned char * in, size_t insz, char * out, size_t outsz, char inbetween = '\0'); in tasmota_globals.h
-char* ToHex_P(const unsigned char * in, size_t insz, char * out, size_t outsz, char inbetween)
-{
- // ToHex_P(in, insz, out, outz) -> "12345667"
- // ToHex_P(in, insz, out, outz, ' ') -> "12 34 56 67"
- // ToHex_P(in, insz, out, outz, ':') -> "12:34:56:67"
- static const char * hex = "0123456789ABCDEF";
- int between = (inbetween) ? 3 : 2;
- const unsigned char * pin = in;
- char * pout = out;
- for (; pin < in+insz; pout += between, pin++) {
- pout[0] = hex[(pgm_read_byte(pin)>>4) & 0xF];
- pout[1] = hex[ pgm_read_byte(pin) & 0xF];
- if (inbetween) { pout[2] = inbetween; }
- if (pout + 3 - out > outsz) { break; } // Better to truncate output string than overflow buffer
- }
- pout[(inbetween && insz) ? -1 : 0] = 0; // Discard last inbetween if any input
- return out;
-}
-
-char* Uint64toHex(uint64_t value, char *str, uint16_t bits)
-{
- ulltoa(value, str, 16); // Get 64bit value
-
- int fill = 8;
- if ((bits > 3) && (bits < 65)) {
- fill = bits / 4; // Max 16
- if (bits % 4) { fill++; }
- }
- int len = strlen(str);
- fill -= len;
- if (fill > 0) {
- memmove(str + fill, str, len +1);
- memset(str, '0', fill);
- }
- return str;
-}
-
char* dtostrfd(double number, unsigned char prec, char *s)
{
if ((isnan(number)) || (isinf(number))) { // Fix for JSON output (https://stackoverflow.com/questions/1423081/json-left-out-infinity-and-nan-json-status-in-ecmascript)
- strcpy(s, "null");
+ strcpy_P(s, PSTR("null"));
return s;
} else {
return dtostrf(number, 1, prec, s);
@@ -655,16 +633,17 @@ uint8_t Shortcut(void)
bool ValidIpAddress(const char* str)
{
- const char* p = str;
-
- while (*p && ((*p == '.') || ((*p >= '0') && (*p <= '9')))) { p++; }
- return (*p == '\0');
+ IPAddress ip_address;
+ return ip_address.fromString(str);
}
-bool ParseIp(uint32_t* addr, const char* str)
+bool ParseIPv4(uint32_t* addr, const char* str_p)
{
uint8_t *part = (uint8_t*)addr;
uint8_t i;
+ char str_r[strlen_P(str_p)+1];
+ char * str = &str_r[0];
+ strcpy_P(str, str_p);
*addr = 0;
for (i = 0; i < 4; i++) {
@@ -678,16 +657,6 @@ bool ParseIp(uint32_t* addr, const char* str)
return (3 == i);
}
-uint32_t ParseParameters(uint32_t count, uint32_t *params)
-{
- char *p;
- uint32_t i = 0;
- for (char *str = strtok_r(XdrvMailbox.data, ", ", &p); str && i < count; str = strtok_r(nullptr, ", ", &p), i++) {
- params[i] = strtoul(str, nullptr, 0);
- }
- return i;
-}
-
// Function to parse & check if version_str is newer than our currently installed version.
bool NewerVersion(char* version_str)
{
@@ -861,7 +830,7 @@ float ConvertPressureForSeaLevel(float pressure)
String PressureUnit(void)
{
- return (Settings.flag.pressure_conversion) ? String(D_UNIT_MILLIMETER_MERCURY) : String(D_UNIT_PRESSURE);
+ return (Settings.flag.pressure_conversion) ? String(F(D_UNIT_MILLIMETER_MERCURY)) : String(F(D_UNIT_PRESSURE));
}
float ConvertSpeed(float s)
@@ -974,8 +943,8 @@ int GetCommandCode(char* destination, size_t destination_size, const char* needl
return result;
}
-bool DecodeCommand(const char* haystack, void (* const MyCommand[])(void))
-{
+bool DecodeCommand(const char* haystack, void (* const MyCommand[])(void), const uint8_t *synonyms = nullptr);
+bool DecodeCommand(const char* haystack, void (* const MyCommand[])(void), const uint8_t *synonyms) {
GetTextIndexed(XdrvMailbox.command, CMDSZ, 0, haystack); // Get prefix if available
int prefix_length = strlen(XdrvMailbox.command);
if (prefix_length) {
@@ -985,10 +954,18 @@ bool DecodeCommand(const char* haystack, void (* const MyCommand[])(void))
return false; // Prefix not in command
}
}
+ size_t syn_count = synonyms ? pgm_read_byte(synonyms) : 0;
int command_code = GetCommandCode(XdrvMailbox.command + prefix_length, CMDSZ, XdrvMailbox.topic + prefix_length, haystack);
if (command_code > 0) { // Skip prefix
- XdrvMailbox.command_code = command_code -1;
- MyCommand[XdrvMailbox.command_code]();
+ if (command_code > syn_count) {
+ // We passed the synonyms zone, it's a regular command
+ XdrvMailbox.command_code = command_code - 1 - syn_count;
+ MyCommand[XdrvMailbox.command_code]();
+ } else {
+ // We have a SetOption synonym
+ XdrvMailbox.index = pgm_read_byte(synonyms + command_code);
+ CmndSetoptionBase(0);
+ }
return true;
}
return false;
@@ -1024,11 +1001,11 @@ String GetSerialConfig(void) {
// b00000x00 - 1 or 2 stop bits
// b000xx000 - None, Even or Odd parity
- const char kParity[] = "NEOI";
+ const static char kParity[] PROGMEM = "NEOI";
char config[4];
config[0] = '5' + (Settings.serial_config & 0x3);
- config[1] = kParity[(Settings.serial_config >> 3) & 0x3];
+ config[1] = pgm_read_byte(&kParity[(Settings.serial_config >> 3) & 0x3]);
config[2] = '1' + ((Settings.serial_config >> 2) & 0x1);
config[3] = '\0';
return String(config);
@@ -1040,7 +1017,7 @@ uint32_t GetSerialBaudrate(void) {
void SetSerialBegin(void) {
TasmotaGlobal.baudrate = Settings.baudrate * 300;
- AddLog_P(LOG_LEVEL_INFO, PSTR(D_LOG_SERIAL "Set to %s %d bit/s"), GetSerialConfig().c_str(), TasmotaGlobal.baudrate);
+ AddLog(LOG_LEVEL_INFO, PSTR(D_LOG_SERIAL "Set to %s %d bit/s"), GetSerialConfig().c_str(), TasmotaGlobal.baudrate);
Serial.flush();
#ifdef ESP8266
Serial.begin(TasmotaGlobal.baudrate, (SerialConfig)pgm_read_byte(kTasmotaSerialConfig + Settings.serial_config));
@@ -1083,7 +1060,7 @@ void SetSerial(uint32_t baudrate, uint32_t serial_config) {
void ClaimSerial(void) {
TasmotaGlobal.serial_local = true;
- AddLog_P(LOG_LEVEL_INFO, PSTR("SNS: Hardware Serial"));
+ AddLog(LOG_LEVEL_INFO, PSTR("SNS: Hardware Serial"));
SetSeriallog(LOG_LEVEL_NONE);
TasmotaGlobal.baudrate = GetSerialBaudrate();
Settings.baudrate = TasmotaGlobal.baudrate / 300;
@@ -1130,7 +1107,7 @@ void ShowSource(uint32_t source)
{
if ((source > 0) && (source < SRC_MAX)) {
char stemp1[20];
- AddLog_P(LOG_LEVEL_DEBUG, PSTR("SRC: %s"), GetTextIndexed(stemp1, sizeof(stemp1), source, kCommandSource));
+ AddLog(LOG_LEVEL_DEBUG, PSTR("SRC: %s"), GetTextIndexed(stemp1, sizeof(stemp1), source, kCommandSource));
}
}
@@ -1228,7 +1205,7 @@ int Response_P(const char* format, ...) // Content send snprintf_P char d
// This uses char strings. Be aware of sending %% if % is needed
va_list args;
va_start(args, format);
- int len = vsnprintf_P(TasmotaGlobal.mqtt_data, sizeof(TasmotaGlobal.mqtt_data), format, args);
+ int len = ext_vsnprintf_P(TasmotaGlobal.mqtt_data, sizeof(TasmotaGlobal.mqtt_data), format, args);
va_end(args);
return len;
}
@@ -1242,7 +1219,7 @@ int ResponseTime_P(const char* format, ...) // Content send snprintf_P char d
ResponseGetTime(Settings.flag2.time_format, TasmotaGlobal.mqtt_data);
int mlen = strlen(TasmotaGlobal.mqtt_data);
- int len = vsnprintf_P(TasmotaGlobal.mqtt_data + mlen, sizeof(TasmotaGlobal.mqtt_data) - mlen, format, args);
+ int len = ext_vsnprintf_P(TasmotaGlobal.mqtt_data + mlen, sizeof(TasmotaGlobal.mqtt_data) - mlen, format, args);
va_end(args);
return len + mlen;
}
@@ -1253,7 +1230,7 @@ int ResponseAppend_P(const char* format, ...) // Content send snprintf_P char d
va_list args;
va_start(args, format);
int mlen = strlen(TasmotaGlobal.mqtt_data);
- int len = vsnprintf_P(TasmotaGlobal.mqtt_data + mlen, sizeof(TasmotaGlobal.mqtt_data) - mlen, format, args);
+ int len = ext_vsnprintf_P(TasmotaGlobal.mqtt_data + mlen, sizeof(TasmotaGlobal.mqtt_data) - mlen, format, args);
va_end(args);
return len + mlen;
}
@@ -1271,14 +1248,11 @@ int ResponseAppendTime(void)
int ResponseAppendTHD(float f_temperature, float f_humidity)
{
- char temperature[FLOATSZ];
- dtostrfd(f_temperature, Settings.flag2.temperature_resolution, temperature);
- char humidity[FLOATSZ];
- dtostrfd(f_humidity, Settings.flag2.humidity_resolution, humidity);
- char dewpoint[FLOATSZ];
- dtostrfd(CalcTempHumToDew(f_temperature, f_humidity), Settings.flag2.temperature_resolution, dewpoint);
-
- return ResponseAppend_P(PSTR("\"" D_JSON_TEMPERATURE "\":%s,\"" D_JSON_HUMIDITY "\":%s,\"" D_JSON_DEWPOINT "\":%s"), temperature, humidity, dewpoint);
+ float dewpoint = CalcTempHumToDew(f_temperature, f_humidity);
+ return ResponseAppend_P(PSTR("\"" D_JSON_TEMPERATURE "\":%*_f,\"" D_JSON_HUMIDITY "\":%*_f,\"" D_JSON_DEWPOINT "\":%*_f"),
+ Settings.flag2.temperature_resolution, &f_temperature,
+ Settings.flag2.humidity_resolution, &f_humidity,
+ Settings.flag2.temperature_resolution, &dewpoint);
}
int ResponseJsonEnd(void)
@@ -1319,7 +1293,7 @@ void TemplateConvert(uint8_t template8[], uint16_t template16[]) {
}
template16[(sizeof(mytmplt) / 2) -2] = Adc0Convert(template8[sizeof(mytmplt8285) -1]);
-// AddLog_P(LOG_LEVEL_DEBUG, PSTR("FNC: TemplateConvert"));
+// AddLog(LOG_LEVEL_DEBUG, PSTR("FNC: TemplateConvert"));
// AddLogBuffer(LOG_LEVEL_DEBUG, template8, sizeof(mytmplt8285));
// AddLogBufferSize(LOG_LEVEL_DEBUG, (uint8_t*)template16, sizeof(mytmplt) / 2, 2);
}
@@ -1335,7 +1309,7 @@ void ConvertGpios(void) {
Settings.my_gp.io[(sizeof(myio) / 2) -1] = Adc0Convert(Settings.ex_my_adc0);
Settings.gpio16_converted = 0xF5A0;
-// AddLog_P(LOG_LEVEL_DEBUG, PSTR("FNC: ConvertGpios"));
+// AddLog(LOG_LEVEL_DEBUG, PSTR("FNC: ConvertGpios"));
// AddLogBuffer(LOG_LEVEL_DEBUG, (uint8_t *)&Settings.ex_my_gp8.io, sizeof(myio8));
// AddLogBufferSize(LOG_LEVEL_DEBUG, (uint8_t *)&Settings.my_gp.io, sizeof(myio) / 2, 2);
}
@@ -1486,7 +1460,7 @@ String ModuleName(void)
void GetInternalTemplate(void* ptr, uint32_t module, uint32_t option) {
uint8_t module_template = pgm_read_byte(kModuleTemplateList + module);
-// AddLog_P(LOG_LEVEL_DEBUG, PSTR("DBG: Template %d, Option %d"), module_template, option);
+// AddLog(LOG_LEVEL_DEBUG, PSTR("DBG: Template %d, Option %d"), module_template, option);
// template8 = GPIO 0,1,2,3,4,5,9,10,12,13,14,15,16,Adc
uint8_t template8[sizeof(mytmplt8285)] = { GPIO_NONE };
@@ -1518,7 +1492,7 @@ void GetInternalTemplate(void* ptr, uint32_t module, uint32_t option) {
}
memcpy(ptr, &template16[index], size);
-// AddLog_P(LOG_LEVEL_DEBUG, PSTR("FNC: GetInternalTemplate option %d"), option);
+// AddLog(LOG_LEVEL_DEBUG, PSTR("FNC: GetInternalTemplate option %d"), option);
// AddLogBufferSize(LOG_LEVEL_DEBUG, (uint8_t *)ptr, size / 2, 2);
}
#endif // ESP8266
@@ -1672,7 +1646,7 @@ bool JsonTemplate(char* dataBuf)
}
if (old_template) {
- AddLog_P(LOG_LEVEL_DEBUG, PSTR("TPL: Converting template ..."));
+ AddLog(LOG_LEVEL_DEBUG, PSTR("TPL: Converting template ..."));
val = root[PSTR(D_JSON_FLAG)];
if (val) {
@@ -1706,7 +1680,7 @@ bool JsonTemplate(char* dataBuf)
Settings.user_template_base = base -1; // Default WEMOS
}
-// AddLog_P(LOG_LEVEL_DEBUG, PSTR("TPL: Converted"));
+// AddLog(LOG_LEVEL_DEBUG, PSTR("TPL: Converted"));
// AddLogBufferSize(LOG_LEVEL_DEBUG, (uint8_t*)&Settings.user_template, sizeof(Settings.user_template) / 2, 2);
return true;
@@ -1714,7 +1688,7 @@ bool JsonTemplate(char* dataBuf)
void TemplateJson(void)
{
-// AddLog_P(LOG_LEVEL_DEBUG, PSTR("TPL: Show"));
+// AddLog(LOG_LEVEL_DEBUG, PSTR("TPL: Show"));
// AddLogBufferSize(LOG_LEVEL_DEBUG, (uint8_t*)&Settings.user_template, sizeof(Settings.user_template) / 2, 2);
Response_P(PSTR("{\"" D_JSON_NAME "\":\"%s\",\"" D_JSON_GPIO "\":["), SettingsText(SET_TEMPLATE_NAME));
@@ -1987,7 +1961,7 @@ void I2cResetActive(uint32_t addr, uint32_t count = 1)
i2c_active[addr / 32] &= ~(1 << (addr % 32));
addr++;
}
-// AddLog_P(LOG_LEVEL_DEBUG, PSTR("I2C: Active %08X,%08X,%08X,%08X"), i2c_active[0], i2c_active[1], i2c_active[2], i2c_active[3]);
+// AddLog(LOG_LEVEL_DEBUG, PSTR("I2C: Active %08X,%08X,%08X,%08X"), i2c_active[0], i2c_active[1], i2c_active[2], i2c_active[3]);
}
void I2cSetActive(uint32_t addr, uint32_t count = 1)
@@ -1998,13 +1972,13 @@ void I2cSetActive(uint32_t addr, uint32_t count = 1)
i2c_active[addr / 32] |= (1 << (addr % 32));
addr++;
}
-// AddLog_P(LOG_LEVEL_DEBUG, PSTR("I2C: Active %08X,%08X,%08X,%08X"), i2c_active[0], i2c_active[1], i2c_active[2], i2c_active[3]);
+// AddLog(LOG_LEVEL_DEBUG, PSTR("I2C: Active %08X,%08X,%08X,%08X"), i2c_active[0], i2c_active[1], i2c_active[2], i2c_active[3]);
}
void I2cSetActiveFound(uint32_t addr, const char *types)
{
I2cSetActive(addr);
- AddLog_P(LOG_LEVEL_INFO, S_LOG_I2C_FOUND_AT, types, addr);
+ AddLog(LOG_LEVEL_INFO, S_LOG_I2C_FOUND_AT, types, addr);
}
bool I2cActive(uint32_t addr)
@@ -2031,7 +2005,7 @@ bool I2cSetDevice(uint32_t addr)
* Syslog
*
* Example:
- * AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_LOG "Any value %d"), value);
+ * AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_LOG "Any value %d"), value);
*
\*********************************************************************************************/
@@ -2072,7 +2046,7 @@ void SyslogAsync(bool refresh) {
if (!PortUdp.beginPacket(syslog_host_addr, Settings.syslog_port)) {
TasmotaGlobal.syslog_level = 0;
TasmotaGlobal.syslog_timer = SYSLOG_TIMER;
- AddLog_P(LOG_LEVEL_INFO, PSTR(D_LOG_APPLICATION D_SYSLOG_HOST_NOT_FOUND ". " D_RETRY_IN " %d " D_UNIT_SECOND), SYSLOG_TIMER);
+ AddLog(LOG_LEVEL_INFO, PSTR(D_LOG_APPLICATION D_SYSLOG_HOST_NOT_FOUND ". " D_RETRY_IN " %d " D_UNIT_SECOND), SYSLOG_TIMER);
return;
}
char log_data[len +72]; // Hostname + Id + log data
@@ -2202,13 +2176,13 @@ void AddLogData(uint32_t loglevel, const char* log_data) {
}
}
-void AddLog_P(uint32_t loglevel, PGM_P formatP, ...)
-{
+void AddLog(uint32_t loglevel, PGM_P formatP, ...) {
+ // To save stack space support logging for max text length of 128 characters
char log_data[LOGSZ +4];
va_list arg;
va_start(arg, formatP);
- uint32_t len = vsnprintf_P(log_data, LOGSZ +1, formatP, arg);
+ uint32_t len = ext_vsnprintf_P(log_data, LOGSZ +1, formatP, arg);
va_end(arg);
if (len > LOGSZ) { strcat(log_data, "..."); } // Actual data is more
@@ -2217,31 +2191,34 @@ void AddLog_P(uint32_t loglevel, PGM_P formatP, ...)
static uint32_t max_len = 0;
if (len > max_len) {
max_len = len;
- Serial.printf("PRF: AddLog_P %d\n", max_len);
+ Serial.printf("PRF: AddLog %d\n", max_len);
}
#endif
AddLogData(loglevel, log_data);
}
+void AddLog_P(uint32_t loglevel, PGM_P formatP, ...) {
+ // Use more stack space to support logging for max text length of 700 characters
+ char log_data[MAX_LOGSZ];
+
+ va_list arg;
+ va_start(arg, formatP);
+ uint32_t len = ext_vsnprintf_P(log_data, sizeof(log_data), formatP, arg);
+ va_end(arg);
+
+ AddLogData(loglevel, log_data);
+}
+
void AddLog_Debug(PGM_P formatP, ...)
{
char log_data[MAX_LOGSZ];
va_list arg;
va_start(arg, formatP);
- uint32_t len = vsnprintf_P(log_data, sizeof(log_data), formatP, arg);
+ uint32_t len = ext_vsnprintf_P(log_data, sizeof(log_data), formatP, arg);
va_end(arg);
-#ifdef DEBUG_TASMOTA_CORE
- // Profile max_len
- static uint32_t max_len = 0;
- if (len > max_len) {
- max_len = len;
- Serial.printf("PRF: AddLog_Debug %d\n", max_len);
- }
-#endif
-
AddLogData(LOG_LEVEL_DEBUG, log_data);
}
@@ -2258,7 +2235,7 @@ void AddLogSerial(uint32_t loglevel)
void AddLogMissed(const char *sensor, uint32_t misses)
{
- AddLog_P(LOG_LEVEL_DEBUG, PSTR("SNS: %s missed %d"), sensor, SENSOR_MAX_MISS - misses);
+ AddLog(LOG_LEVEL_DEBUG, PSTR("SNS: %s missed %d"), sensor, SENSOR_MAX_MISS - misses);
}
void AddLogBufferSize(uint32_t loglevel, uint8_t *buffer, uint32_t count, uint32_t size) {
@@ -2281,15 +2258,15 @@ void AddLogSpi(bool hardware, uint32_t clk, uint32_t mosi, uint32_t miso) {
uint32_t enabled = (hardware) ? TasmotaGlobal.spi_enabled : TasmotaGlobal.soft_spi_enabled;
switch(enabled) {
case SPI_MOSI:
- AddLog_P(LOG_LEVEL_INFO, PSTR("SPI: %s using GPIO%02d(CLK) and GPIO%02d(MOSI)"),
+ AddLog(LOG_LEVEL_INFO, PSTR("SPI: %s using GPIO%02d(CLK) and GPIO%02d(MOSI)"),
(hardware) ? PSTR("Hardware") : PSTR("Software"), clk, mosi);
break;
case SPI_MISO:
- AddLog_P(LOG_LEVEL_INFO, PSTR("SPI: %s using GPIO%02d(CLK) and GPIO%02d(MISO)"),
+ AddLog(LOG_LEVEL_INFO, PSTR("SPI: %s using GPIO%02d(CLK) and GPIO%02d(MISO)"),
(hardware) ? PSTR("Hardware") : PSTR("Software"), clk, miso);
break;
case SPI_MOSI_MISO:
- AddLog_P(LOG_LEVEL_INFO, PSTR("SPI: %s using GPIO%02d(CLK), GPIO%02d(MOSI) and GPIO%02d(MISO)"),
+ AddLog(LOG_LEVEL_INFO, PSTR("SPI: %s using GPIO%02d(CLK), GPIO%02d(MOSI) and GPIO%02d(MISO)"),
(hardware) ? PSTR("Hardware") : PSTR("Software"), clk, mosi, miso);
break;
}
diff --git a/tasmota/support_button.ino b/tasmota/support_button.ino
index 53fb218ba..d5262f517 100644
--- a/tasmota/support_button.ino
+++ b/tasmota/support_button.ino
@@ -146,7 +146,7 @@ void ButtonHandler(void) {
if (!button_index && ((SONOFF_DUAL == TasmotaGlobal.module_type) || (CH4 == TasmotaGlobal.module_type))) {
button_present = 1;
if (Button.dual_code) {
- AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_APPLICATION D_BUTTON " " D_CODE " %04X"), Button.dual_code);
+ AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_APPLICATION D_BUTTON " " D_CODE " %04X"), Button.dual_code);
button = PRESSED;
if (0xF500 == Button.dual_code) { // Button hold
Button.hold_timer[button_index] = (loops_per_second * Settings.param[P_HOLD_TIME] / 10) -1; // SetOption32 (40)
@@ -176,7 +176,7 @@ void ButtonHandler(void) {
Button.touch_hits[button_index] = 0;
}
if (bitRead(TOUCH_BUTTON.calibration, button_index+1)) {
- AddLog_P(LOG_LEVEL_INFO, PSTR("PLOT: %u, %u, %u,"), button_index+1, _value, Button.touch_hits[button_index]); // Button number (1..4), value, continuous hits under threshold
+ AddLog(LOG_LEVEL_INFO, PSTR("PLOT: %u, %u, %u,"), button_index+1, _value, Button.touch_hits[button_index]); // Button number (1..4), value, continuous hits under threshold
}
} else
#endif // ESP32
@@ -206,12 +206,12 @@ void ButtonHandler(void) {
bool button_pressed = false;
if ((PRESSED == button) && (NOT_PRESSED == Button.last_state[button_index])) {
- AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_APPLICATION D_BUTTON "%d " D_LEVEL_10), button_index +1);
+ AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_APPLICATION D_BUTTON "%d " D_LEVEL_10), button_index +1);
Button.hold_timer[button_index] = loops_per_second;
button_pressed = true;
}
if ((NOT_PRESSED == button) && (PRESSED == Button.last_state[button_index])) {
- AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_APPLICATION D_BUTTON "%d " D_LEVEL_01), button_index +1);
+ AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_APPLICATION D_BUTTON "%d " D_LEVEL_01), button_index +1);
if (!Button.hold_timer[button_index]) { button_pressed = true; } // Do not allow within 1 second
}
if (button_pressed) {
@@ -230,7 +230,7 @@ void ButtonHandler(void) {
if (Settings.flag.button_single) { // SetOption13 (0) - Allow only single button press for immediate action,
if (!Settings.flag3.mqtt_buttons) { // SetOption73 (0) - Decouple button from relay and send just mqtt topic
- AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_APPLICATION D_BUTTON "%d " D_IMMEDIATE), button_index +1);
+ AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_APPLICATION D_BUTTON "%d " D_IMMEDIATE), button_index +1);
if (!SendKey(KEY_BUTTON, button_index +1, POWER_TOGGLE)) { // Execute Toggle command via MQTT if ButtonTopic is set
ExecuteCommandPower(button_index +1, POWER_TOGGLE, SRC_BUTTON); // Execute Toggle command internally
}
@@ -239,7 +239,7 @@ void ButtonHandler(void) {
}
} else {
Button.press_counter[button_index] = (Button.window_timer[button_index]) ? Button.press_counter[button_index] +1 : 1;
- AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_APPLICATION D_BUTTON "%d " D_MULTI_PRESS " %d"), button_index +1, Button.press_counter[button_index]);
+ AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_APPLICATION D_BUTTON "%d " D_MULTI_PRESS " %d"), button_index +1, Button.press_counter[button_index]);
Button.window_timer[button_index] = loops_per_second / 2; // 0.5 second multi press window
}
TasmotaGlobal.blinks = 201;
@@ -263,7 +263,14 @@ void ButtonHandler(void) {
SendKey(KEY_BUTTON, button_index +1, POWER_HOLD); // Execute Hold command via MQTT if ButtonTopic is set
}
} else {
- if (!Settings.flag.button_restrict) { // SetOption1 - Control button multipress
+ if (Settings.flag.button_restrict) { // SetOption1 (0) - Control button multipress
+ if (Settings.param[P_HOLD_IGNORE] > 0) { // SetOption40 (0) - Do not ignore button hold
+ if (Button.hold_timer[button_index] > loops_per_second * Settings.param[P_HOLD_IGNORE] / 10) {
+ Button.hold_timer[button_index] = 0; // Reset button hold counter to stay below hold trigger
+ Button.press_counter[button_index] = 0; // Discard button press to disable functionality
+ }
+ }
+ } else {
if ((Button.hold_timer[button_index] == loops_per_second * hold_time_extent * Settings.param[P_HOLD_TIME] / 10)) { // SetOption32 (40) - Button held for factor times longer
Button.press_counter[button_index] = 0;
snprintf_P(scmnd, sizeof(scmnd), PSTR(D_CMND_RESET " 1"));
@@ -320,7 +327,7 @@ void ButtonHandler(void) {
#endif // ESP8266
if ((Button.press_counter[button_index] > 1) && valid_relay && (Button.press_counter[button_index] <= MAX_RELAY_BUTTON1)) {
ExecuteCommandPower(button_index + Button.press_counter[button_index], POWER_TOGGLE, SRC_BUTTON); // Execute Toggle command internally
-// AddLog_P(LOG_LEVEL_DEBUG, PSTR("DBG: Relay%d found on GPIO%d"), Button.press_counter[button_index], Pin(GPIO_REL1, Button.press_counter[button_index]-1));
+// AddLog(LOG_LEVEL_DEBUG, PSTR("DBG: Relay%d found on GPIO%d"), Button.press_counter[button_index], Pin(GPIO_REL1, Button.press_counter[button_index]-1));
}
}
}
diff --git a/tasmota/support_command.ino b/tasmota/support_command.ino
index 1c5e32474..e2942945b 100644
--- a/tasmota/support_command.ino
+++ b/tasmota/support_command.ino
@@ -83,9 +83,7 @@ void ResponseCmndNumber(int value)
void ResponseCmndFloat(float value, uint32_t decimals)
{
- char stemp1[TOPSZ];
- dtostrfd(value, decimals, stemp1);
- Response_P(S_JSON_COMMAND_XVALUE, XdrvMailbox.command, stemp1); // Return float value without quotes
+ Response_P(PSTR("{\"%s\":%*_f}"), XdrvMailbox.command, decimals, &value); // Return float value without quotes
}
void ResponseCmndIdxNumber(int value)
@@ -110,7 +108,7 @@ void ResponseCmndStateText(uint32_t value)
void ResponseCmndDone(void)
{
- ResponseCmndChar(D_JSON_DONE);
+ ResponseCmndChar(PSTR(D_JSON_DONE));
}
void ResponseCmndIdxChar(const char* value)
@@ -227,7 +225,7 @@ void CommandHandler(char* topicBuf, char* dataBuf, uint32_t data_len)
type[i] = '\0';
}
- AddLog_P(LOG_LEVEL_DEBUG, PSTR("CMD: " D_GROUP " %d, " D_INDEX " %d, " D_COMMAND " \"%s\", " D_DATA " \"%s\""), grpflg, index, type, dataBuf);
+ AddLog(LOG_LEVEL_DEBUG, PSTR("CMD: " D_GROUP " %d, " D_INDEX " %d, " D_COMMAND " \"%s\", " D_DATA " \"%s\""), grpflg, index, type, dataBuf);
if (type != nullptr) {
Response_P(PSTR("{\"" D_JSON_COMMAND "\":\"" D_JSON_ERROR "\"}"));
@@ -352,7 +350,7 @@ void CmndBacklog(void)
#else
TasmotaGlobal.backlog_pointer = TasmotaGlobal.backlog_index;
#endif
- ResponseCmndChar(blflag ? D_JSON_EMPTY : D_JSON_ABORTED);
+ ResponseCmndChar(blflag ? PSTR(D_JSON_EMPTY) : PSTR(D_JSON_ABORTED));
}
}
@@ -507,12 +505,12 @@ void CmndStatus(void)
}
if ((0 == payload) || (5 == payload)) {
- Response_P(PSTR("{\"" D_CMND_STATUS D_STATUS5_NETWORK "\":{\"" D_CMND_HOSTNAME "\":\"%s\",\"" D_CMND_IPADDRESS "\":\"%s\",\"" D_JSON_GATEWAY "\":\"%s\",\""
- D_JSON_SUBNETMASK "\":\"%s\",\"" D_JSON_DNSSERVER "\":\"%s\",\"" D_JSON_MAC "\":\"%s\",\""
- D_CMND_WEBSERVER "\":%d,\"" D_CMND_WIFICONFIG "\":%d,\"" D_CMND_WIFIPOWER "\":%s}}"),
- NetworkHostname(), NetworkAddress().toString().c_str(), IPAddress(Settings.ip_address[1]).toString().c_str(),
- IPAddress(Settings.ip_address[2]).toString().c_str(), IPAddress(Settings.ip_address[3]).toString().c_str(), NetworkMacAddress().c_str(),
- Settings.webserver, Settings.sta_config, WifiGetOutputPower().c_str());
+ Response_P(PSTR("{\"" D_CMND_STATUS D_STATUS5_NETWORK "\":{\"" D_CMND_HOSTNAME "\":\"%s\",\"" D_CMND_IPADDRESS "\":\"%_I\",\""
+ D_JSON_GATEWAY "\":\"%_I\",\"" D_JSON_SUBNETMASK "\":\"%_I\",\"" D_JSON_DNSSERVER "\":\"%_I\",\""
+ D_JSON_MAC "\":\"%s\",\"" D_CMND_WEBSERVER "\":%d,\"" D_CMND_WIFICONFIG "\":%d,\"" D_CMND_WIFIPOWER "\":%s}}"),
+ NetworkHostname(), (uint32_t)NetworkAddress(),
+ Settings.ipv4_address[1], Settings.ipv4_address[2], Settings.ipv4_address[3],
+ NetworkMacAddress().c_str(), Settings.webserver, Settings.sta_config, WifiGetOutputPower().c_str());
MqttPublishPrefixTopic_P(STAT, PSTR(D_CMND_STATUS "5"));
}
@@ -744,7 +742,7 @@ void CmndRestart(void)
CmndBlockedLoop();
break;
case 99:
- AddLog_P(LOG_LEVEL_INFO, PSTR(D_LOG_APPLICATION D_RESTARTING));
+ AddLog(LOG_LEVEL_INFO, PSTR(D_LOG_APPLICATION D_RESTARTING));
EspRestart();
break;
default:
@@ -835,10 +833,17 @@ void CmndSavedata(void)
ResponseCmndChar((Settings.save_data > 1) ? stemp1 : GetStateText(Settings.save_data));
}
-void CmndSetoption(void)
-{
+void CmndSetoption(void) {
snprintf_P(XdrvMailbox.command, CMDSZ, PSTR(D_CMND_SETOPTION)); // Rename result shortcut command SO to SetOption
+ CmndSetoptionBase(1);
+}
+void CmndSetoptionBase(bool indexed) {
+ // Allow a command to access a single SetOption by it's command name
+ // indexed = 0 : No index will be returned attached to the command
+ // {"ClockDirection":"OFF"}
+ // indexed = 1 : The SetOption index will be returned with the command
+ // {"SetOption16":"OFF"}
if (XdrvMailbox.index < 146) {
uint32_t ptype;
uint32_t pindex;
@@ -981,7 +986,11 @@ void CmndSetoption(void)
if (ptype < 99) {
if (1 == ptype) {
- ResponseCmndIdxNumber(Settings.param[pindex]);
+ if (indexed) {
+ ResponseCmndIdxNumber(Settings.param[pindex]);
+ } else {
+ ResponseCmndNumber(Settings.param[pindex]);
+ }
} else {
uint32_t flag = Settings.flag.data;
if (3 == ptype) {
@@ -993,7 +1002,11 @@ void CmndSetoption(void)
else if (5 == ptype) {
flag = Settings.flag5.data;
}
- ResponseCmndIdxChar(GetStateText(bitRead(flag, pindex)));
+ if (indexed) {
+ ResponseCmndIdxChar(GetStateText(bitRead(flag, pindex)));
+ } else {
+ ResponseCmndChar(GetStateText(bitRead(flag, pindex)));
+ }
}
}
}
@@ -1198,18 +1211,21 @@ void CmndGpio(void)
sensor_names = kSensorNamesFixed;
}
char stemp1[TOPSZ];
- if ((ResponseAppend_P(PSTR("\"" D_CMND_GPIO "%d\":{\"%d\":\"%s%s\"}"), i, sensor_type, GetTextIndexed(stemp1, sizeof(stemp1), sensor_name_idx, sensor_names), sindex) > (MAX_LOGSZ - TOPSZ)) || (i == ARRAY_SIZE(Settings.my_gp.io) -1)) {
+ if ((ResponseAppend_P(PSTR("\"" D_CMND_GPIO "%d\":{\"%d\":\"%s%s\"}"), i, sensor_type, GetTextIndexed(stemp1, sizeof(stemp1), sensor_name_idx, sensor_names), sindex) > (MAX_LOGSZ - TOPSZ))) {
ResponseJsonEnd();
MqttPublishPrefixTopic_P(RESULT_OR_STAT, XdrvMailbox.command);
+ ResponseClear();
jsflg2 = true;
jsflg = false;
}
}
}
- if (jsflg2) {
- ResponseClear();
+ if (jsflg) {
+ ResponseJsonEnd();
} else {
- ResponseCmndChar(PSTR(D_JSON_NOT_SUPPORTED));
+ if (!jsflg2) {
+ ResponseCmndChar(PSTR(D_JSON_NOT_SUPPORTED));
+ }
}
}
}
@@ -1282,7 +1298,7 @@ void CmndTemplate(void)
if (Settings.module != USER_MODULE) {
ModuleDefault(Settings.module);
}
- SettingsUpdateText(SET_TEMPLATE_NAME, "Merged");
+ SettingsUpdateText(SET_TEMPLATE_NAME, PSTR("Merged"));
uint32_t j = 0;
for (uint32_t i = 0; i < ARRAY_SIZE(Settings.user_template.gp.io); i++) {
if (6 == i) { j = 9; }
@@ -1492,23 +1508,20 @@ void CmndLogport(void)
void CmndIpAddress(void)
{
if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= 4)) {
+ char network_address[22];
+ ext_snprintf_P(network_address, sizeof(network_address), PSTR(" (%_I)"), (uint32_t)NetworkAddress());
if (!XdrvMailbox.usridx) {
- char stemp1[TOPSZ];
- snprintf_P(stemp1, sizeof(stemp1), PSTR(" %s"), NetworkAddress().toString().c_str());
ResponseClear();
for (uint32_t i = 0; i < 4; i++) {
- ResponseAppend_P(PSTR("%c\"%s%d\":\"%s%s\""), (i) ? ',' : '{', XdrvMailbox.command, i +1, IPAddress(Settings.ip_address[i]).toString().c_str(), (0 == i) ? stemp1:"");
+ ResponseAppend_P(PSTR("%c\"%s%d\":\"%_I%s\""), (i)?',':'{', XdrvMailbox.command, i +1, Settings.ipv4_address[i], (0 == i)?network_address:"");
}
ResponseJsonEnd();
} else {
- uint32_t address;
- if (ParseIp(&address, XdrvMailbox.data)) {
- Settings.ip_address[XdrvMailbox.index -1] = address;
-// TasmotaGlobal.restart_flag = 2;
+ uint32_t ipv4_address;
+ if (ParseIPv4(&ipv4_address, XdrvMailbox.data)) {
+ Settings.ipv4_address[XdrvMailbox.index -1] = ipv4_address;
}
- char stemp1[TOPSZ];
- snprintf_P(stemp1, sizeof(stemp1), PSTR(" %s"), NetworkAddress().toString().c_str());
- Response_P(S_JSON_COMMAND_INDEX_SVALUE_SVALUE, XdrvMailbox.command, XdrvMailbox.index, IPAddress(Settings.ip_address[XdrvMailbox.index -1]).toString().c_str(), (1 == XdrvMailbox.index) ? stemp1:"");
+ Response_P(PSTR("{\"%s%d\":\"%_I%s\"}"), XdrvMailbox.command, XdrvMailbox.index, Settings.ipv4_address[XdrvMailbox.index -1], (1 == XdrvMailbox.index)?network_address:"");
}
}
}
@@ -2117,7 +2130,7 @@ void CmndTouchCal(void)
}
Response_P(PSTR("{\"" D_CMND_TOUCH_CAL "\": %u"), TOUCH_BUTTON.calibration);
ResponseJsonEnd();
- AddLog_P(LOG_LEVEL_INFO, PSTR("Button Touchvalue Hits,"));
+ AddLog(LOG_LEVEL_INFO, PSTR("Button Touchvalue Hits,"));
}
void CmndTouchThres(void)
diff --git a/tasmota/support_device_groups.ino b/tasmota/support_device_groups.ino
index e5b00cba2..dc79de635 100644
--- a/tasmota/support_device_groups.ino
+++ b/tasmota/support_device_groups.ino
@@ -136,7 +136,7 @@ void DeviceGroupsInit(void)
// Initialize the device information for each device group.
device_groups = (struct device_group *)calloc(device_group_count, sizeof(struct device_group));
if (!device_groups) {
- AddLog_P(LOG_LEVEL_ERROR, PSTR("DGR: Error allocating %u-element array"), device_group_count);
+ AddLog(LOG_LEVEL_ERROR, PSTR("DGR: Error allocating %u-element array"), device_group_count);
return;
}
@@ -176,7 +176,7 @@ void DeviceGroupsStart()
// Subscribe to device groups multicasts.
if (!device_groups_udp.beginMulticast(WiFi.localIP(), IPAddress(DEVICE_GROUPS_ADDRESS), DEVICE_GROUPS_PORT)) {
- AddLog_P(LOG_LEVEL_ERROR, PSTR("DGR: Error subscribing"));
+ AddLog(LOG_LEVEL_ERROR, PSTR("DGR: Error subscribing"));
return;
}
device_groups_up = true;
@@ -192,7 +192,7 @@ void DeviceGroupsStart()
device_group->initial_status_requests_remaining = 10;
device_group->next_ack_check_time = next_check_time;
}
- AddLog_P(LOG_LEVEL_DEBUG, PSTR("DGR: (Re)discovering members"));
+ AddLog(LOG_LEVEL_DEBUG, PSTR("DGR: (Re)discovering members"));
}
}
@@ -325,7 +325,7 @@ void SendReceiveDeviceGroupMessage(struct device_group * device_group, struct de
case DGR_ITEM_LIGHT_CHANNELS:
break;
default:
- AddLog_P(LOG_LEVEL_ERROR, PSTR("DGR: *** Invalid item=%u"), item);
+ AddLog(LOG_LEVEL_ERROR, PSTR("DGR: *** Invalid item=%u"), item);
}
#endif // DEVICE_GROUPS_DEBUG
@@ -446,12 +446,12 @@ write_log:
}
delay(10);
}
- if (attempt > 5) AddLog_P(LOG_LEVEL_ERROR, PSTR("DGR: Error sending message"));
+ if (attempt > 5) AddLog(LOG_LEVEL_ERROR, PSTR("DGR: Error sending message"));
}
goto cleanup;
badmsg:
- AddLog_P(LOG_LEVEL_ERROR, PSTR("%s ** incorrect length"), log_buffer);
+ AddLog(LOG_LEVEL_ERROR, PSTR("%s ** incorrect length"), log_buffer);
cleanup:
if (received) {
@@ -483,7 +483,7 @@ bool _SendDeviceGroupMessage(uint8_t device_group_index, DevGroupMessageType mes
// Load the message header, sequence and flags.
#ifdef DEVICE_GROUPS_DEBUG
- AddLog_P(LOG_LEVEL_DEBUG, PSTR("DGR: Building %s %spacket"), device_group->group_name, (message_type == DGR_MSGTYP_FULL_STATUS ? PSTR("full status ") : PSTR("")));
+ AddLog(LOG_LEVEL_DEBUG, PSTR("DGR: Building %s %spacket"), device_group->group_name, (message_type == DGR_MSGTYP_FULL_STATUS ? PSTR("full status ") : PSTR("")));
#endif // DEVICE_GROUPS_DEBUG
uint16_t original_sequence = device_group->outgoing_sequence;
uint16_t flags = 0;
@@ -505,7 +505,12 @@ bool _SendDeviceGroupMessage(uint8_t device_group_index, DevGroupMessageType mes
building_status_message = true;
// Call the drivers to build the status update.
- SendDeviceGroupMessage(device_group_index, DGR_MSGTYP_PARTIAL_UPDATE, DGR_ITEM_POWER, TasmotaGlobal.power);
+ power_t power = TasmotaGlobal.power;
+ if (Settings.flag4.multiple_device_groups) { // SetOption88 - Enable relays in separate device groups
+ power >>= device_group_index;
+ power &= 1;
+ }
+ SendDeviceGroupMessage(device_group_index, DGR_MSGTYP_PARTIAL_UPDATE, DGR_ITEM_POWER, power);
XdrvMailbox.index = 0;
if (device_group_index == 0 && first_device_group_is_local) XdrvMailbox.index = DGR_FLAG_LOCAL;
XdrvMailbox.command_code = DGR_ITEM_STATUS;
@@ -654,7 +659,7 @@ bool _SendDeviceGroupMessage(uint8_t device_group_index, DevGroupMessageType mes
previous_message_ptr += value;
}
#ifdef DEVICE_GROUPS_DEBUG
- AddLog_P(LOG_LEVEL_DEBUG, PSTR("DGR: %u items carried over"), kept_item_count);
+ AddLog(LOG_LEVEL_DEBUG, PSTR("DGR: %u items carried over"), kept_item_count);
#endif // DEVICE_GROUPS_DEBUG
}
@@ -772,12 +777,12 @@ void ProcessDeviceGroupMessage(uint8_t * message, int message_length)
if (!device_group_member) {
device_group_member = (struct device_group_member *)calloc(1, sizeof(struct device_group_member));
if (device_group_member == nullptr) {
- AddLog_P(LOG_LEVEL_ERROR, PSTR("DGR: Error allocating member block"));
+ AddLog(LOG_LEVEL_ERROR, PSTR("DGR: Error allocating member block"));
return;
}
device_group_member->ip_address = remote_ip;
*flink = device_group_member;
- AddLog_P(LOG_LEVEL_DEBUG, PSTR("DGR: Member %s added"), IPAddressToString(remote_ip));
+ AddLog(LOG_LEVEL_DEBUG, PSTR("DGR: Member %s added"), IPAddressToString(remote_ip));
break;
}
else if (device_group_member->ip_address == remote_ip) {
@@ -824,7 +829,7 @@ void DeviceGroupsLoop(void)
// If it's time to check on things, iterate through the device groups.
if ((long)(now - next_check_time) >= 0) {
#ifdef DEVICE_GROUPS_DEBUG
-AddLog_P(LOG_LEVEL_DEBUG, PSTR("DGR: Checking next_check_time=%u, now=%u"), next_check_time, now);
+AddLog(LOG_LEVEL_DEBUG, PSTR("DGR: Checking next_check_time=%u, now=%u"), next_check_time, now);
#endif // DEVICE_GROUPS_DEBUG
next_check_time = now + DGR_ANNOUNCEMENT_INTERVAL * 2;
@@ -841,7 +846,7 @@ AddLog_P(LOG_LEVEL_DEBUG, PSTR("DGR: Checking next_check_time=%u, now=%u"), next
if (device_group->initial_status_requests_remaining) {
if (--device_group->initial_status_requests_remaining) {
#ifdef DEVICE_GROUPS_DEBUG
- AddLog_P(LOG_LEVEL_DEBUG, PSTR("DGR: Sending initial status request for group %s"), device_group->group_name);
+ AddLog(LOG_LEVEL_DEBUG, PSTR("DGR: Sending initial status request for group %s"), device_group->group_name);
#endif // DEVICE_GROUPS_DEBUG
SendReceiveDeviceGroupMessage(device_group, nullptr, device_group->message, device_group->message_length, false);
device_group->message[device_group->message_header_length + 2] = DGR_FLAG_STATUS_REQUEST; // The reset flag is on only for the first packet - turn it off now
@@ -859,7 +864,7 @@ AddLog_P(LOG_LEVEL_DEBUG, PSTR("DGR: Checking next_check_time=%u, now=%u"), next
// If we're done initializing, iterate through the group memebers, ...
else {
#ifdef DEVICE_GROUPS_DEBUG
- AddLog_P(LOG_LEVEL_DEBUG, PSTR("DGR: Checking for ack's"));
+ AddLog(LOG_LEVEL_DEBUG, PSTR("DGR: Checking for ack's"));
#endif // DEVICE_GROUPS_DEBUG
bool acked = true;
struct device_group_member ** flink = &device_group->device_group_members;
@@ -874,7 +879,7 @@ AddLog_P(LOG_LEVEL_DEBUG, PSTR("DGR: Checking next_check_time=%u, now=%u"), next
if ((long)(now - device_group->member_timeout_time) >= 0) {
*flink = device_group_member->flink;
free(device_group_member);
- AddLog_P(LOG_LEVEL_DEBUG, PSTR("DGR: Member %s removed"), IPAddressToString(device_group_member->ip_address));
+ AddLog(LOG_LEVEL_DEBUG, PSTR("DGR: Member %s removed"), IPAddressToString(device_group_member->ip_address));
continue;
}
@@ -912,7 +917,7 @@ AddLog_P(LOG_LEVEL_DEBUG, PSTR("DGR: Checking next_check_time=%u, now=%u"), next
// announcement interval plus a random number of milliseconds so that even if all the devices
// booted at the same time, they don't all multicast their announcements at the same time.
#ifdef DEVICE_GROUPS_DEBUG
- AddLog_P(LOG_LEVEL_DEBUG, PSTR("DGR: next_announcement_time=%u, now=%u"), device_group->next_announcement_time, now);
+ AddLog(LOG_LEVEL_DEBUG, PSTR("DGR: next_announcement_time=%u, now=%u"), device_group->next_announcement_time, now);
#endif // DEVICE_GROUPS_DEBUG
if ((long)(now - device_group->next_announcement_time) >= 0) {
SendReceiveDeviceGroupMessage(device_group, nullptr, device_group->message, BeginDeviceGroupMessage(device_group, DGR_FLAG_ANNOUNCEMENT, true) - device_group->message, false);
diff --git a/tasmota/support_esp32.ino b/tasmota/support_esp32.ino
index 7bbad6468..8e6415bbd 100644
--- a/tasmota/support_esp32.ino
+++ b/tasmota/support_esp32.ino
@@ -150,13 +150,13 @@ void SettingsErase(uint8_t type) {
r1 = NvmErase("qpc");
r2 = NvmErase("main");
r3 = TfsDeleteFile(TASM_FILE_SETTINGS);
- AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_APPLICATION D_ERASE " Tasmota data (%d,%d,%d)"), r1, r2, r3);
+ AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_APPLICATION D_ERASE " Tasmota data (%d,%d,%d)"), r1, r2, r3);
break;
case 1: // Reset 3 = SDK parameter area
case 4: // WIFI_FORCE_RF_CAL_ERASE = SDK parameter area
r1 = esp_phy_erase_cal_data_in_nvs();
// r1 = NvmErase("cal_data");
- AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_APPLICATION D_ERASE " PHY data (%d)"), r1);
+ AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_APPLICATION D_ERASE " PHY data (%d)"), r1);
break;
case 3: // QPC Reached = QPC, Tasmota and SDK parameter area (0x0F3xxx - 0x0FFFFF)
// nvs_flash_erase(); // Erase RTC, PHY, sta.mac, ap.sndchan, ap.mac, Tasmota etc.
@@ -164,9 +164,9 @@ void SettingsErase(uint8_t type) {
r2 = NvmErase("main");
// r3 = esp_phy_erase_cal_data_in_nvs();
// r3 = NvmErase("cal_data");
-// AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_APPLICATION D_ERASE " Tasmota (%d,%d) and PHY data (%d)"), r1, r2, r3);
+// AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_APPLICATION D_ERASE " Tasmota (%d,%d) and PHY data (%d)"), r1, r2, r3);
r3 = TfsDeleteFile(TASM_FILE_SETTINGS);
- AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_APPLICATION D_ERASE " Tasmota data (%d,%d,%d)"), r1, r2, r3);
+ AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_APPLICATION D_ERASE " Tasmota data (%d,%d,%d)"), r1, r2, r3);
break;
}
}
@@ -196,7 +196,7 @@ void QPCWrite(const void *pSettings, unsigned nSettingsLen) {
void NvsInfo(void) {
nvs_stats_t nvs_stats;
nvs_get_stats(NULL, &nvs_stats);
- AddLog_P(LOG_LEVEL_INFO, PSTR("NVS: Used %d/%d entries, NameSpaces %d"),
+ AddLog(LOG_LEVEL_INFO, PSTR("NVS: Used %d/%d entries, NameSpaces %d"),
nvs_stats.used_entries, nvs_stats.total_entries, nvs_stats.namespace_count);
}
@@ -240,7 +240,7 @@ uint8_t* EspFlashMmap(uint32_t address) {
int32_t err = spi_flash_mmap(address, 5 * SPI_FLASH_MMU_PAGE_SIZE, SPI_FLASH_MMAP_DATA, (const void **)&data, &handle);
/*
- AddLog_P(LOG_LEVEL_DEBUG, PSTR("DBG: Spi_flash_map %d"), err);
+ AddLog(LOG_LEVEL_DEBUG, PSTR("DBG: Spi_flash_map %d"), err);
spi_flash_mmap_dump();
*/
@@ -258,7 +258,7 @@ int32_t EspPartitionMmap(uint32_t action) {
if (!partition) { return 0; }
err = esp_partition_mmap(partition, 0, 4 * SPI_FLASH_MMU_PAGE_SIZE, SPI_FLASH_MMAP_DATA, (const void **)&TasmotaGlobal_mmap_data, &handle);
- AddLog_P(LOG_LEVEL_DEBUG, PSTR("DBG: Partition start 0x%08X, Partition end 0x%08X, Mmap data 0x%08X"), partition->address, partition->size, TasmotaGlobal_mmap_data);
+ AddLog(LOG_LEVEL_DEBUG, PSTR("DBG: Partition start 0x%08X, Partition end 0x%08X, Mmap data 0x%08X"), partition->address, partition->size, TasmotaGlobal_mmap_data);
} else {
spi_flash_munmap(handle);
@@ -407,7 +407,7 @@ uint8_t* FlashDirectAccess(void) {
uint32_t address = FlashWriteStartSector() * SPI_FLASH_SEC_SIZE;
uint8_t* data = EspFlashMmap(address);
/*
- AddLog_P(LOG_LEVEL_DEBUG, PSTR("DBG: Flash start address 0x%08X, Mmap address 0x%08X"), address, data);
+ AddLog(LOG_LEVEL_DEBUG, PSTR("DBG: Flash start address 0x%08X, Mmap address 0x%08X"), address, data);
uint8_t buf[32];
memcpy(buf, data, sizeof(buf));
diff --git a/tasmota/support_esptool.ino b/tasmota/support_esptool.ino
index a066838af..ebae51c75 100644
--- a/tasmota/support_esptool.ino
+++ b/tasmota/support_esptool.ino
@@ -95,7 +95,7 @@ bool EsptoolEraseSector(uint32_t sector)
void EsptoolErase(uint32_t start_sector, uint32_t end_sector)
{
- AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_APPLICATION D_ERASE " from 0x%08X to 0x%08X"), start_sector * SPI_FLASH_SEC_SIZE, (end_sector * SPI_FLASH_SEC_SIZE) -1);
+ AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_APPLICATION D_ERASE " from 0x%08X to 0x%08X"), start_sector * SPI_FLASH_SEC_SIZE, (end_sector * SPI_FLASH_SEC_SIZE) -1);
int next_erase_sector = start_sector;
int remaining_erase_sector = end_sector - start_sector;
diff --git a/tasmota/support_features.ino b/tasmota/support_features.ino
index 3a3597f2a..1d17193e5 100644
--- a/tasmota/support_features.ino
+++ b/tasmota/support_features.ino
@@ -661,7 +661,6 @@ void ResponseAppendFeatures(void)
#ifdef USE_AS608
feature7 |= 0x00000800; // xsns_79_as608.ino
#endif
-
#if defined(USE_SHELLY_DIMMER)
feature7 |= 0x00001000; // xdrv_45_shelly_dimmer.ino
#endif
@@ -701,20 +700,68 @@ void ResponseAppendFeatures(void)
#ifdef USE_BS814A2
feature7 |= 0x01000000; // xdrv_51_bs814a2.ino
#endif
-#ifdef USE_SEESAW_SOIL
+#if defined(USE_I2C) && defined(USE_SEESAW_SOIL)
feature7 |= 0x02000000; // xsns_81_seesaw_soil.ino
#endif
-// feature7 |= 0x04000000;
-// feature7 |= 0x08000000;
-
-// feature7 |= 0x10000000;
+#ifdef USE_WIEGAND
+ feature7 |= 0x04000000; // xsns_82_wiegand.ino
+#endif
+#ifdef USE_NEOPOOL
+ feature7 |= 0x08000000; // xsns_83_neopool.ino
+#endif
+#if defined(USE_I2C) && defined(USE_TOF10120)
+ feature7 |= 0x10000000; // xsns_84_tof10120
+#endif
// feature7 |= 0x20000000;
// feature7 |= 0x40000000;
// feature7 |= 0x80000000;
}
+ static uint32_t feature8 = 0x00000000;
+ if (!feature8) { // Only fill this once
+// feature8 |= 0x00000001;
+// feature8 |= 0x00000002;
+// feature8 |= 0x00000004;
+// feature8 |= 0x00000008;
+
+// feature8 |= 0x00000010;
+// feature8 |= 0x00000020;
+// feature8 |= 0x00000040;
+// feature8 |= 0x00000080;
+
+// feature8 |= 0x00000100;
+// feature8 |= 0x00000200;
+// feature8 |= 0x00000400;
+// feature8 |= 0x00000800;
+
+// feature8 |= 0x00001000;
+// feature8 |= 0x00002000;
+// feature8 |= 0x00004000;
+// feature8 |= 0x00008000;
+
+// feature8 |= 0x00010000;
+// feature8 |= 0x00020000;
+// feature8 |= 0x00040000;
+// feature8 |= 0x00080000;
+
+// feature8 |= 0x00100000;
+// feature8 |= 0x00200000;
+// feature8 |= 0x00400000;
+// feature8 |= 0x00800000;
+
+// feature8 |= 0x01000000;
+// feature8 |= 0x02000000;
+// feature8 |= 0x04000000;
+// feature8 |= 0x08000000;
+
+// feature8 |= 0x10000000;
+// feature8 |= 0x20000000;
+// feature8 |= 0x40000000;
+// feature8 |= 0x80000000;
+ }
+
/*********************************************************************************************/
- ResponseAppend_P(PSTR(",\"" D_JSON_FEATURES "\":[\"%08X\",\"%08X\",\"%08X\",\"%08X\",\"%08X\",\"%08X\",\"%08X\",\"%08X\"]"),
- LANGUAGE_LCID, feature1, feature2, feature3, feature4, feature5, feature6, feature7);
+ ResponseAppend_P(PSTR(",\"" D_JSON_FEATURES "\":[\"%08X\",\"%08X\",\"%08X\",\"%08X\",\"%08X\",\"%08X\",\"%08X\",\"%08X\",\"%08X\"]"),
+ LANGUAGE_LCID, feature1, feature2, feature3, feature4, feature5, feature6, feature7, feature8);
}
diff --git a/tasmota/support_float.ino b/tasmota/support_float.ino
index 2bdf32ef2..e2877e5a3 100644
--- a/tasmota/support_float.ino
+++ b/tasmota/support_float.ino
@@ -136,7 +136,7 @@ double TaylorLog(double x)
dtostrfd(log1, 8, log1s);
char log2s[33];
dtostrfd(totalValue, 8, log2s);
- AddLog_P(LOG_LEVEL_DEBUG, PSTR("input %s, log %s, taylor %s"), logxs, log1s, log2s);
+ AddLog(LOG_LEVEL_DEBUG, PSTR("input %s, log %s, taylor %s"), logxs, log1s, log2s);
*/
return totalValue;
}
diff --git a/tasmota/support_network.ino b/tasmota/support_network.ino
index b2bb73bf0..242c03121 100644
--- a/tasmota/support_network.ino
+++ b/tasmota/support_network.ino
@@ -32,13 +32,13 @@ void StartMdns(void) {
if (Settings.flag3.mdns_enabled) { // SetOption55 - Control mDNS service
if (!Mdns.begun) {
// if (mdns_delayed_start) {
-// AddLog_P(LOG_LEVEL_INFO, PSTR(D_LOG_MDNS D_ATTEMPTING_CONNECTION));
+// AddLog(LOG_LEVEL_INFO, PSTR(D_LOG_MDNS D_ATTEMPTING_CONNECTION));
// mdns_delayed_start--;
// } else {
// mdns_delayed_start = Settings.param[P_MDNS_DELAYED_START];
MDNS.end(); // close existing or MDNS.begin will fail
Mdns.begun = (uint8_t)MDNS.begin(TasmotaGlobal.hostname);
- AddLog_P(LOG_LEVEL_INFO, PSTR(D_LOG_MDNS "%s"), (Mdns.begun) ? D_INITIALIZED : D_FAILED);
+ AddLog(LOG_LEVEL_INFO, PSTR(D_LOG_MDNS "%s"), (Mdns.begun) ? PSTR(D_INITIALIZED) : PSTR(D_FAILED));
// }
}
}
@@ -51,7 +51,7 @@ void MqttDiscoverServer(void)
int n = MDNS.queryService("mqtt", "tcp"); // Search for mqtt service
- AddLog_P(LOG_LEVEL_INFO, PSTR(D_LOG_MDNS D_QUERY_DONE " %d"), n);
+ AddLog(LOG_LEVEL_INFO, PSTR(D_LOG_MDNS D_QUERY_DONE " %d"), n);
if (n > 0) {
uint32_t i = 0; // If the hostname isn't set, use the first record found.
@@ -65,7 +65,7 @@ void MqttDiscoverServer(void)
SettingsUpdateText(SET_MQTT_HOST, MDNS.hostname(i).c_str());
Settings.mqtt_port = MDNS.port(i);
- AddLog_P(LOG_LEVEL_INFO, PSTR(D_LOG_MDNS D_MQTT_SERVICE_FOUND " %s," D_PORT " %d"), SettingsText(SET_MQTT_HOST), Settings.mqtt_port);
+ AddLog(LOG_LEVEL_INFO, PSTR(D_LOG_MDNS D_MQTT_SERVICE_FOUND " %s," D_PORT " %d"), SettingsText(SET_MQTT_HOST), Settings.mqtt_port);
}
}
#endif // MQTT_HOST_DISCOVERY
@@ -84,7 +84,7 @@ void MdnsUpdate(void) {
if (2 == Mdns.begun) {
MDNS.update(); // this is basically passpacket like a webserver
// being called in main loop so no logging
- // AddLog_P(LOG_LEVEL_DEBUG_MORE, PSTR(D_LOG_MDNS "MDNS.update"));
+ // AddLog(LOG_LEVEL_DEBUG_MORE, PSTR(D_LOG_MDNS "MDNS.update"));
}
}
#endif // ESP8266
diff --git a/tasmota/support_rotary.ino b/tasmota/support_rotary.ino
index 7e32647cb..4f5eda5a6 100644
--- a/tasmota/support_rotary.ino
+++ b/tasmota/support_rotary.ino
@@ -209,7 +209,7 @@ void RotaryHandler(void) {
bool button_pressed = (Button.hold_timer[index]); // Button is pressed: set color temperature
if (button_pressed) { Encoder[index].changed = true; }
-// AddLog_P(LOG_LEVEL_DEBUG, PSTR("ROT: Button1 %d, Position %d"), button_pressed, rotary_position);
+// AddLog(LOG_LEVEL_DEBUG, PSTR("ROT: Button1 %d, Position %d"), button_pressed, rotary_position);
#ifdef USE_LIGHT
if (!Settings.flag4.rotary_uses_rules) { // SetOption98 - Use rules instead of light control
diff --git a/tasmota/support_rtc.ino b/tasmota/support_rtc.ino
index 08f7cfc3d..7355ae60b 100644
--- a/tasmota/support_rtc.ino
+++ b/tasmota/support_rtc.ino
@@ -33,8 +33,8 @@ const uint32_t MINS_PER_HOUR = 60UL;
Ticker TickerRtc;
-static const uint8_t kDaysInMonth[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; // API starts months from 1, this array starts from 0
-static const char kMonthNamesEnglish[] = "JanFebMarAprMayJunJulAugSepOctNovDec";
+static const uint8_t kDaysInMonth[] PROGMEM = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; // API starts months from 1, this array starts from 0
+static const char kMonthNamesEnglish[] PROGMEM = "JanFebMarAprMayJunJulAugSepOctNovDec";
struct RTC {
uint32_t utc_time = 0;
@@ -88,7 +88,9 @@ String GetBuildDateAndTime(void)
// "2017-03-07T11:08:02" - ISO8601:2004
char bdt[21];
char *p;
- char mdate[] = __DATE__; // "Mar 7 2017"
+ static const char mdate_P[] PROGMEM = __DATE__; // "Mar 7 2017"
+ char mdate[strlen_P(mdate_P)+1]; // copy on stack first
+ strcpy_P(mdate, mdate_P);
char *smonth = mdate;
int day = 0;
int year = 0;
@@ -107,8 +109,10 @@ String GetBuildDateAndTime(void)
year = atoi(str);
}
}
- int month = (strstr(kMonthNamesEnglish, smonth) -kMonthNamesEnglish) /3 +1;
- snprintf_P(bdt, sizeof(bdt), PSTR("%d" D_YEAR_MONTH_SEPARATOR "%02d" D_MONTH_DAY_SEPARATOR "%02d" D_DATE_TIME_SEPARATOR "%s"), year, month, day, __TIME__);
+ char MonthNamesEnglish[sizeof(kMonthNamesEnglish)];
+ strcpy_P(MonthNamesEnglish, kMonthNamesEnglish);
+ int month = (strstr(MonthNamesEnglish, smonth) -MonthNamesEnglish) /3 +1;
+ snprintf_P(bdt, sizeof(bdt), PSTR("%d" D_YEAR_MONTH_SEPARATOR "%02d" D_MONTH_DAY_SEPARATOR "%02d" D_DATE_TIME_SEPARATOR "%s"), year, month, day, PSTR(__TIME__));
return String(bdt); // 2017-03-07T11:08:02
}
@@ -290,7 +294,7 @@ void BreakTime(uint32_t time_input, TIME_T &tm)
month_length = 28;
}
} else {
- month_length = kDaysInMonth[month];
+ month_length = pgm_read_byte(&kDaysInMonth[month]);
}
if (time >= month_length) {
@@ -326,7 +330,7 @@ uint32_t MakeTime(TIME_T &tm)
if ((2 == i) && LEAP_YEAR(tm.year)) {
seconds += SECS_PER_DAY * 29;
} else {
- seconds += SECS_PER_DAY * kDaysInMonth[i-1]; // monthDay array starts from 0
+ seconds += SECS_PER_DAY * pgm_read_byte(&kDaysInMonth[i-1]); // monthDay array starts from 0
}
}
seconds+= (tm.day_of_month - 1) * SECS_PER_DAY;
@@ -391,8 +395,7 @@ void RtcSecond(void)
Rtc.daylight_saving_time = RuleToTime(Settings.tflag[1], RtcTime.year);
Rtc.standard_time = RuleToTime(Settings.tflag[0], RtcTime.year);
- // Do not use AddLog_P( here (interrupt routine) if syslog or mqttlog is enabled. UDP/TCP will force exception 9
- AddLog_P(LOG_LEVEL_DEBUG, PSTR("RTC: " D_UTC_TIME " %s, " D_DST_TIME " %s, " D_STD_TIME " %s"),
+ AddLog(LOG_LEVEL_DEBUG, PSTR("RTC: " D_UTC_TIME " %s, " D_DST_TIME " %s, " D_STD_TIME " %s"),
GetDateAndTime(DT_UTC).c_str(), GetDateAndTime(DT_DST).c_str(), GetDateAndTime(DT_STD).c_str());
if (Rtc.local_time < START_VALID_TIME) { // 2016-01-01
@@ -406,8 +409,7 @@ void RtcSecond(void)
Rtc.millis = millis();
if ((Rtc.utc_time > (2 * 60 * 60)) && (last_sync < Rtc.utc_time - (2 * 60 * 60))) { // Every two hours a warning
- // Do not use AddLog_P( here (interrupt routine) if syslog or mqttlog is enabled. UDP/TCP will force exception 9
- AddLog_P(LOG_LEVEL_DEBUG, PSTR("RTC: Not synced"));
+ AddLog(LOG_LEVEL_DEBUG, PSTR("RTC: Not synced"));
last_sync = Rtc.utc_time;
}
@@ -472,7 +474,7 @@ void RtcSecond(void)
void RtcSync(void) {
Rtc.time_synced = true;
RtcSecond();
-// AddLog_P(LOG_LEVEL_DEBUG, PSTR("RTC: Synced"));
+// AddLog(LOG_LEVEL_DEBUG, PSTR("RTC: Synced"));
}
void RtcSetTime(uint32_t epoch) {
diff --git a/tasmota/support_tasmota.ino b/tasmota/support_tasmota.ino
index e4ae34177..0761e504d 100644
--- a/tasmota/support_tasmota.ino
+++ b/tasmota/support_tasmota.ino
@@ -20,10 +20,12 @@
const char kSleepMode[] PROGMEM = "Dynamic|Normal";
const char kPrefixes[] PROGMEM = D_CMND "|" D_STAT "|" D_TELE;
-char* Format(char* output, const char* input, int size)
+char* Format(char* output, const char* input_p, int size)
{
char *token;
uint32_t digits = 0;
+ char input[strlen_P(input_p)+1]; // copy from PMEM to RAM
+ strcpy_P(input, input_p);
if (strchr(input, '%') != nullptr) {
strlcpy(output, input, size);
@@ -102,7 +104,7 @@ char* GetTopic_P(char *stopic, uint32_t prefix, char *topic, const char* subtopi
fulltopic += TasmotaGlobal.mqtt_client;
fulltopic += F("_fb"); // cmnd/_fb
} else {
- fulltopic += topic; // cmnd/
+ fulltopic += (const __FlashStringHelper *)topic; // cmnd/
}
} else {
fulltopic = SettingsText(SET_MQTT_FULLTOPIC);
@@ -118,7 +120,7 @@ char* GetTopic_P(char *stopic, uint32_t prefix, char *topic, const char* subtopi
}
fulltopic.replace(FPSTR(MQTT_TOKEN_PREFIX), SettingsText(SET_MQTTPREFIX1 + prefix));
- fulltopic.replace(FPSTR(MQTT_TOKEN_TOPIC), topic);
+ fulltopic.replace(FPSTR(MQTT_TOKEN_TOPIC), (const __FlashStringHelper *)topic);
fulltopic.replace(F("%hostname%"), TasmotaGlobal.hostname);
String token_id = WiFi.macAddress();
token_id.replace(":", "");
@@ -486,7 +488,7 @@ bool SendKey(uint32_t key, uint32_t device, uint32_t state)
#endif // USE_DOMOTICZ
result = !Settings.flag3.button_switch_force_local; // SetOption61 - Force local operation when button/switch topic is set
} else {
- Response_P(PSTR("{\"%s%d\":{\"State\":%d}}"), (key) ? "Switch" : "Button", device, state);
+ Response_P(PSTR("{\"%s%d\":{\"State\":%d}}"), (key) ? PSTR("Switch") : PSTR("Button"), device, state);
result = XdrvRulesProcess();
}
#ifdef USE_PWM_DIMMER
@@ -742,7 +744,8 @@ void TempHumDewShow(bool json, bool pass_on, const char *types, float f_temperat
String GetSwitchText(uint32_t i) {
String switch_text = SettingsText(SET_SWITCH_TXT1 + i);
if ('\0' == switch_text[0]) {
- switch_text = D_JSON_SWITCH + String(i +1);
+ switch_text = F(D_JSON_SWITCH);
+ switch_text += String(i+1);
}
return switch_text;
}
@@ -812,7 +815,7 @@ void PerformEverySecond(void)
if (!(DeepSleepEnabled() && !Settings.flag3.bootcount_update)) {
#endif
Settings.bootcount++; // Moved to here to stop flash writes during start-up
- AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_APPLICATION D_BOOT_COUNT " %d"), Settings.bootcount);
+ AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_APPLICATION D_BOOT_COUNT " %d"), Settings.bootcount);
#ifdef USE_DEEPSLEEP
}
#endif
@@ -829,7 +832,7 @@ void PerformEverySecond(void)
TasmotaGlobal.seriallog_timer--;
if (!TasmotaGlobal.seriallog_timer) {
if (TasmotaGlobal.seriallog_level) {
- AddLog_P(LOG_LEVEL_INFO, PSTR(D_LOG_APPLICATION D_SERIAL_LOGGING_DISABLED));
+ AddLog(LOG_LEVEL_INFO, PSTR(D_LOG_APPLICATION D_SERIAL_LOGGING_DISABLED));
}
TasmotaGlobal.seriallog_level = 0;
}
@@ -840,7 +843,7 @@ void PerformEverySecond(void)
if (!TasmotaGlobal.syslog_timer) {
TasmotaGlobal.syslog_level = Settings.syslog_level;
if (Settings.syslog_level) {
- AddLog_P(LOG_LEVEL_INFO, PSTR(D_LOG_APPLICATION D_SYSLOG_LOGGING_REENABLED)); // Might trigger disable again (on purpose)
+ AddLog(LOG_LEVEL_INFO, PSTR(D_LOG_APPLICATION D_SYSLOG_LOGGING_REENABLED)); // Might trigger disable again (on purpose)
}
}
}
@@ -941,6 +944,11 @@ void Every100mSeconds(void)
* Every 0.25 second
\*-------------------------------------------------------------------------------------------*/
+#ifdef USE_BLE_ESP32
+ // declare the fn
+ int ExtStopBLE();
+#endif // USE_BLE_ESP32
+
void Every250mSeconds(void)
{
// As the max amount of sleep = 250 mSec this loop should always be taken...
@@ -1010,6 +1018,9 @@ void Every250mSeconds(void)
SettingsSave(1); // Free flash for OTA update
}
if (TasmotaGlobal.ota_state_flag <= 0) {
+#ifdef USE_BLE_ESP32
+ ExtStopBLE();
+#endif // USE_BLE_ESP32
#ifdef USE_COUNTER
CounterInterruptDisable(true); // Prevent OTA failures on 100Hz counter interrupts
#endif // USE_COUNTER
@@ -1048,7 +1059,7 @@ void Every250mSeconds(void)
char *ech = strchr(bch, '.'); // Find file type in filename (none, .ino.bin, .ino.bin.gz, .bin, .bin.gz or .gz)
if (ech == nullptr) { ech = TasmotaGlobal.mqtt_data + strlen(TasmotaGlobal.mqtt_data); } // Point to '/0' at end of mqtt_data becoming an empty string
-//AddLog_P(LOG_LEVEL_DEBUG, PSTR("OTA: File type [%s]"), ech);
+//AddLog(LOG_LEVEL_DEBUG, PSTR("OTA: File type [%s]"), ech);
char ota_url_type[strlen(ech) +1];
strncpy(ota_url_type, ech, sizeof(ota_url_type)); // Either empty, .ino.bin, .ino.bin.gz, .bin, .bin.gz or .gz
@@ -1059,7 +1070,7 @@ void Every250mSeconds(void)
snprintf_P(TasmotaGlobal.mqtt_data, sizeof(TasmotaGlobal.mqtt_data), PSTR("%s-" D_JSON_MINIMAL "%s"), TasmotaGlobal.mqtt_data, ota_url_type); // Minimal filename must be filename-minimal
}
#endif // FIRMWARE_MINIMAL
- AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_UPLOAD "%s"), TasmotaGlobal.mqtt_data);
+ AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_UPLOAD "%s"), TasmotaGlobal.mqtt_data);
WiFiClient OTAclient;
ota_result = (HTTP_UPDATE_FAILED != ESPhttpUpdate.update(OTAclient, TasmotaGlobal.mqtt_data));
if (!ota_result) {
@@ -1178,7 +1189,7 @@ void Every250mSeconds(void)
}
TasmotaGlobal.restart_flag--;
if (TasmotaGlobal.restart_flag <= 0) {
- AddLog_P(LOG_LEVEL_INFO, PSTR(D_LOG_APPLICATION "%s"), (TasmotaGlobal.restart_halt) ? PSTR("Halted") : PSTR(D_RESTARTING));
+ AddLog(LOG_LEVEL_INFO, PSTR(D_LOG_APPLICATION "%s"), (TasmotaGlobal.restart_halt) ? PSTR("Halted") : PSTR(D_RESTARTING));
EspRestart();
}
}
@@ -1289,7 +1300,7 @@ void ArduinoOTAInit(void)
if (Settings.flag.mqtt_enabled) {
MqttDisconnect(); // SetOption3 - Enable MQTT
}
- AddLog_P(LOG_LEVEL_INFO, PSTR(D_LOG_UPLOAD "Arduino OTA " D_UPLOAD_STARTED));
+ AddLog(LOG_LEVEL_INFO, PSTR(D_LOG_UPLOAD "Arduino OTA " D_UPLOAD_STARTED));
arduino_ota_triggered = true;
arduino_ota_progress_dot_count = 0;
delay(100); // Allow time for message xfer
@@ -1320,19 +1331,19 @@ void ArduinoOTAInit(void)
default:
snprintf_P(error_str, sizeof(error_str), PSTR(D_UPLOAD_ERROR_CODE " %d"), error);
}
- AddLog_P(LOG_LEVEL_INFO, PSTR(D_LOG_UPLOAD "Arduino OTA %s. " D_RESTARTING), error_str);
+ AddLog(LOG_LEVEL_INFO, PSTR(D_LOG_UPLOAD "Arduino OTA %s. " D_RESTARTING), error_str);
EspRestart();
});
ArduinoOTA.onEnd([]()
{
if ((LOG_LEVEL_DEBUG <= TasmotaGlobal.seriallog_level)) { Serial.println(); }
- AddLog_P(LOG_LEVEL_INFO, PSTR(D_LOG_UPLOAD "Arduino OTA " D_SUCCESSFUL ". " D_RESTARTING));
+ AddLog(LOG_LEVEL_INFO, PSTR(D_LOG_UPLOAD "Arduino OTA " D_SUCCESSFUL ". " D_RESTARTING));
EspRestart();
});
ArduinoOTA.begin();
- AddLog_P(LOG_LEVEL_INFO, PSTR(D_LOG_UPLOAD "Arduino OTA " D_ENABLED " " D_PORT " 8266"));
+ AddLog(LOG_LEVEL_INFO, PSTR(D_LOG_UPLOAD "Arduino OTA " D_ENABLED " " D_PORT " 8266"));
}
void ArduinoOtaLoop(void)
@@ -1440,7 +1451,7 @@ void SerialInput(void)
TasmotaGlobal.serial_in_buffer[TasmotaGlobal.serial_in_byte_counter] = 0; // Serial data completed
TasmotaGlobal.seriallog_level = (Settings.seriallog_level < LOG_LEVEL_INFO) ? (uint8_t)LOG_LEVEL_INFO : Settings.seriallog_level;
if (serial_buffer_overrun) {
- AddLog_P(LOG_LEVEL_INFO, PSTR(D_LOG_COMMAND "Serial buffer overrun"));
+ AddLog(LOG_LEVEL_INFO, PSTR(D_LOG_COMMAND "Serial buffer overrun"));
} else {
AddLog_P(LOG_LEVEL_INFO, PSTR(D_LOG_COMMAND "%s"), TasmotaGlobal.serial_in_buffer);
ExecuteCommand(TasmotaGlobal.serial_in_buffer, SRC_SERIAL);
@@ -1508,7 +1519,7 @@ void GpioInit(void)
}
SetModuleType();
-// AddLog_P(LOG_LEVEL_DEBUG, PSTR("DBG: Used GPIOs %d"), GPIO_SENSOR_END);
+// AddLog(LOG_LEVEL_DEBUG, PSTR("DBG: Used GPIOs %d"), GPIO_SENSOR_END);
#ifdef ESP8266
ConvertGpios();
@@ -1674,7 +1685,7 @@ void GpioInit(void)
for (uint32_t i = 0; i < ARRAY_SIZE(TasmotaGlobal.my_module.io); i++) {
uint32_t mpin = ValidPin(i, TasmotaGlobal.my_module.io[i]);
-// AddLog_P(LOG_LEVEL_DEBUG, PSTR("INI: gpio pin %d, mpin %d"), i, mpin);
+// AddLog(LOG_LEVEL_DEBUG, PSTR("INI: gpio pin %d, mpin %d"), i, mpin);
if (AGPIO(GPIO_OUTPUT_HI) == mpin) {
pinMode(i, OUTPUT);
digitalWrite(i, 1);
diff --git a/tasmota/support_udp.ino b/tasmota/support_udp.ino
index 07aa6736e..f9e89d7ab 100644
--- a/tasmota/support_udp.ino
+++ b/tasmota/support_udp.ino
@@ -71,7 +71,7 @@ bool UdpDisconnect(void)
// stop all
WiFiUDP::stopAll();
#endif // !USE_DEVICE_GROUPS
- AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_UPNP D_MULTICAST_DISABLED));
+ AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_UPNP D_MULTICAST_DISABLED));
udp_connected = false;
}
return udp_connected;
@@ -87,18 +87,18 @@ bool UdpConnect(void)
ip_addr_t addr = IPADDR4_INIT(INADDR_ANY);
if (UdpCtx.listen(&addr, 1900)) { // port 1900
// OK
- AddLog_P(LOG_LEVEL_INFO, PSTR(D_LOG_UPNP D_MULTICAST_REJOINED));
+ AddLog(LOG_LEVEL_INFO, PSTR(D_LOG_UPNP D_MULTICAST_REJOINED));
udp_connected = true;
}
#endif // ESP8266
#ifdef ESP32
if (PortUdp.beginMulticast(WiFi.localIP(), IPAddress(239,255,255,250), 1900)) {
- AddLog_P(LOG_LEVEL_INFO, PSTR(D_LOG_UPNP D_MULTICAST_REJOINED));
+ AddLog(LOG_LEVEL_INFO, PSTR(D_LOG_UPNP D_MULTICAST_REJOINED));
udp_connected = true;
#endif // ESP32
}
if (!udp_connected) { // if connection failed
- AddLog_P(LOG_LEVEL_INFO, PSTR(D_LOG_UPNP D_MULTICAST_JOIN_FAILED));
+ AddLog(LOG_LEVEL_INFO, PSTR(D_LOG_UPNP D_MULTICAST_JOIN_FAILED));
}
}
return udp_connected;
@@ -120,7 +120,7 @@ void PollUdp(void)
packet->buf[packet->len] = 0; // add NULL at the end of the packet
char * packet_buffer = (char*) &packet->buf;
int32_t len = packet->len;
- AddLog_P(LOG_LEVEL_DEBUG_MORE, PSTR("UDP: Packet (%d)"), len);
+ AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("UDP: Packet (%d)"), len);
#endif // ESP8266
#ifdef ESP32
while (uint32_t pack_len = PortUdp.parsePacket()) {
@@ -129,7 +129,7 @@ void PollUdp(void)
int32_t len = PortUdp.read(packet_buffer, UDP_BUFFER_SIZE -1);
packet_buffer[len] = 0;
PortUdp.flush();
- AddLog_P(LOG_LEVEL_DEBUG_MORE, PSTR("UDP: Packet (%d/%d)"), len, pack_len);
+ AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("UDP: Packet (%d/%d)"), len, pack_len);
#endif // ESP32
// AddLog_P(LOG_LEVEL_DEBUG_MORE, PSTR("\n%s"), packet_buffer);
@@ -151,8 +151,8 @@ void PollUdp(void)
udp_remote_port = PortUdp.remotePort();
#endif
- // AddLog_P(LOG_LEVEL_DEBUG_MORE, PSTR("UDP: M-SEARCH Packet from %s:%d\n%s"),
- // udp_remote_ip.toString().c_str(), udp_remote_port, packet_buffer);
+ // AddLog_P(LOG_LEVEL_DEBUG_MORE, PSTR("UDP: M-SEARCH Packet from %_I:%d\n%s"),
+ // (uint32_t)udp_remote_ip, udp_remote_port, packet_buffer);
LowerCase(packet_buffer, packet_buffer);
RemoveSpace(packet_buffer);
@@ -175,7 +175,7 @@ void PollUdp(void)
#ifdef USE_EMULATION_HUE
if (!udp_proccessed && (EMUL_HUE == Settings.flag2.emulation)) {
- AddLog_P(LOG_LEVEL_DEBUG_MORE, PSTR("UDP: HUE"));
+ AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("UDP: HUE"));
if ((strstr_P(packet_buffer, PSTR(":device:basic:1")) != nullptr) ||
(strstr_P(packet_buffer, UPNP_ROOTDEVICE) != nullptr) ||
(strstr_P(packet_buffer, SSDPSEARCH_ALL) != nullptr) ||
diff --git a/tasmota/support_wifi.ino b/tasmota/support_wifi.ino
index 15f5e10c2..804070103 100644
--- a/tasmota/support_wifi.ino
+++ b/tasmota/support_wifi.ino
@@ -102,11 +102,11 @@ void WifiConfig(uint8_t type)
TasmotaGlobal.restart_flag = 2;
}
else if (WIFI_SERIAL == Wifi.config_type) {
- AddLog_P(LOG_LEVEL_INFO, PSTR(D_LOG_WIFI D_WCFG_6_SERIAL " " D_ACTIVE_FOR_3_MINUTES));
+ AddLog(LOG_LEVEL_INFO, PSTR(D_LOG_WIFI D_WCFG_6_SERIAL " " D_ACTIVE_FOR_3_MINUTES));
}
#ifdef USE_WEBSERVER
else if (WIFI_MANAGER == Wifi.config_type || WIFI_MANAGER_RESET_ONLY == Wifi.config_type) {
- AddLog_P(LOG_LEVEL_INFO, PSTR(D_LOG_WIFI D_WCFG_2_WIFIMANAGER " " D_ACTIVE_FOR_3_MINUTES));
+ AddLog(LOG_LEVEL_INFO, PSTR(D_LOG_WIFI D_WCFG_2_WIFIMANAGER " " D_ACTIVE_FOR_3_MINUTES));
WifiManagerBegin(WIFI_MANAGER_RESET_ONLY == Wifi.config_type);
}
#endif // USE_WEBSERVER
@@ -125,7 +125,7 @@ void WifiSetMode(WiFiMode_t wifi_mode)
uint32_t retry = 2;
while (!WiFi.mode(wifi_mode) && retry--) {
- AddLog_P(LOG_LEVEL_INFO, PSTR(D_LOG_WIFI "Retry set Mode..."));
+ AddLog(LOG_LEVEL_INFO, PSTR(D_LOG_WIFI "Retry set Mode..."));
delay(100);
}
@@ -176,7 +176,7 @@ void WiFiSetSleepMode(void)
void WifiBegin(uint8_t flag, uint8_t channel)
{
- const char kWifiPhyMode[] = " bgnl";
+ const static char kWifiPhyMode[] PROGMEM = " bgnl";
#ifdef USE_EMULATION
UdpDisconnect();
@@ -203,8 +203,8 @@ void WifiBegin(uint8_t flag, uint8_t channel)
if (!strlen(SettingsText(SET_STASSID1 + Settings.sta_active))) {
Settings.sta_active ^= 1; // Skip empty SSID
}
- if (Settings.ip_address[0]) {
- WiFi.config(Settings.ip_address[0], Settings.ip_address[1], Settings.ip_address[2], Settings.ip_address[3]); // Set static IP
+ if (Settings.ipv4_address[0]) {
+ WiFi.config(Settings.ipv4_address[0], Settings.ipv4_address[1], Settings.ipv4_address[2], Settings.ipv4_address[3]); // Set static IP
}
WiFi.hostname(TasmotaGlobal.hostname);
@@ -217,15 +217,15 @@ void WifiBegin(uint8_t flag, uint8_t channel)
} else {
WiFi.begin(SettingsText(SET_STASSID1 + Settings.sta_active), SettingsText(SET_STAPWD1 + Settings.sta_active));
}
- AddLog_P(LOG_LEVEL_INFO, PSTR(D_LOG_WIFI D_CONNECTING_TO_AP "%d %s%s " D_IN_MODE " 11%c " D_AS " %s..."),
- Settings.sta_active +1, SettingsText(SET_STASSID1 + Settings.sta_active), stemp, kWifiPhyMode[WiFi.getPhyMode() & 0x3], TasmotaGlobal.hostname);
+ AddLog(LOG_LEVEL_INFO, PSTR(D_LOG_WIFI D_CONNECTING_TO_AP "%d %s%s " D_IN_MODE " 11%c " D_AS " %s..."),
+ Settings.sta_active +1, SettingsText(SET_STASSID1 + Settings.sta_active), stemp, pgm_read_byte(&kWifiPhyMode[WiFi.getPhyMode() & 0x3]), TasmotaGlobal.hostname);
#if LWIP_IPV6
for (bool configured = false; !configured;) {
uint16_t cfgcnt = 0;
for (auto addr : addrList) {
if ((configured = !addr.isLocal() && addr.isV6()) || cfgcnt==30) {
- AddLog_P(LOG_LEVEL_INFO, PSTR(D_LOG_WIFI "Got IPv6 global address %s"), addr.toString().c_str());
+ AddLog(LOG_LEVEL_INFO, PSTR(D_LOG_WIFI "Got IPv6 global address %s"), addr.toString().c_str());
break; // IPv6 is mandatory but stop after 15 seconds
}
delay(500); // Loop until real IPv6 address is aquired or too many tries failed
@@ -260,7 +260,7 @@ void WifiBeginAfterScan(void)
if (WiFi.scanComplete() != WIFI_SCAN_RUNNING) {
WiFi.scanNetworks(true); // Start wifi scan async
Wifi.scan_state++;
- AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_WIFI "Network (re)scan started..."));
+ AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_WIFI "Network (re)scan started..."));
return;
}
}
@@ -308,7 +308,7 @@ void WifiBeginAfterScan(void)
}
}
char hex_char[18];
- AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_WIFI "Network %d, AP%c, SSId %s, Channel %d, BSSId %s, RSSI %d, Encryption %d"),
+ AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_WIFI "Network %d, AP%c, SSId %s, Channel %d, BSSId %s, RSSI %d, Encryption %d"),
i,
(known) ? (j) ? '2' : '1' : '-',
ssid_scan.c_str(),
@@ -361,16 +361,6 @@ void WifiSetState(uint8_t state)
}
#if LWIP_IPV6
-bool WifiCheckIPv6(void)
-{
- bool ipv6_global=false;
-
- for (auto a : addrList) {
- if(!a.isLocal() && a.isV6()) ipv6_global=true;
- }
- return ipv6_global;
-}
-
String WifiGetIPv6(void)
{
for (auto a : addrList) {
@@ -378,35 +368,30 @@ String WifiGetIPv6(void)
}
return "";
}
-
-bool WifiCheckIPAddrStatus(void) // Return false for 169.254.x.x or fe80::/64
-{
- bool ip_global=false;
-
- for (auto a : addrList) {
- if(!a.isLocal()) ip_global=true;
- }
- return ip_global;
-}
#endif // LWIP_IPV6=1
+// Check to see if we have any routable IP address
+inline bool WifiCheck_hasIP(IPAddress const & ip_address)
+{
+#ifdef LWIP2_IPV6
+ return !a.isLocal();
+#else
+ return static_cast(ip_address) != 0;
+#endif
+}
+
void WifiCheckIp(void)
{
-#if LWIP_IPV6
- if(WifiCheckIPAddrStatus()) {
- Wifi.status = WL_CONNECTED;
-#else
- if ((WL_CONNECTED == WiFi.status()) && (static_cast(WiFi.localIP()) != 0)) {
-#endif // LWIP_IPV6=1
+ if ((WL_CONNECTED == WiFi.status()) && WifiCheck_hasIP(WiFi.localIP())) {
WifiSetState(1);
Wifi.counter = WIFI_CHECK_SEC;
Wifi.retry = Wifi.retry_init;
if (Wifi.status != WL_CONNECTED) {
- AddLog_P(LOG_LEVEL_INFO, PSTR(D_LOG_WIFI D_CONNECTED));
-// AddLog_P(LOG_LEVEL_INFO, PSTR("Wifi: Set IP addresses"));
- Settings.ip_address[1] = (uint32_t)WiFi.gatewayIP();
- Settings.ip_address[2] = (uint32_t)WiFi.subnetMask();
- Settings.ip_address[3] = (uint32_t)WiFi.dnsIP();
+ AddLog(LOG_LEVEL_INFO, PSTR(D_LOG_WIFI D_CONNECTED));
+// AddLog(LOG_LEVEL_INFO, PSTR("Wifi: Set IP addresses"));
+ Settings.ipv4_address[1] = (uint32_t)WiFi.gatewayIP();
+ Settings.ipv4_address[2] = (uint32_t)WiFi.subnetMask();
+ Settings.ipv4_address[3] = (uint32_t)WiFi.dnsIP();
// Save current AP parameters for quick reconnect
Settings.wifi_channel = WiFi.channel();
@@ -420,12 +405,12 @@ void WifiCheckIp(void)
Wifi.status = WiFi.status();
switch (Wifi.status) {
case WL_CONNECTED:
- AddLog_P(LOG_LEVEL_INFO, PSTR(D_LOG_WIFI D_CONNECT_FAILED_NO_IP_ADDRESS));
+ AddLog(LOG_LEVEL_INFO, PSTR(D_LOG_WIFI D_CONNECT_FAILED_NO_IP_ADDRESS));
Wifi.status = 0;
Wifi.retry = Wifi.retry_init;
break;
case WL_NO_SSID_AVAIL:
- AddLog_P(LOG_LEVEL_INFO, PSTR(D_LOG_WIFI D_CONNECT_FAILED_AP_NOT_REACHED));
+ AddLog(LOG_LEVEL_INFO, PSTR(D_LOG_WIFI D_CONNECT_FAILED_AP_NOT_REACHED));
Settings.wifi_channel = 0; // Disable stored AP
if (WIFI_WAIT == Settings.sta_config) {
Wifi.retry = Wifi.retry_init;
@@ -439,7 +424,7 @@ void WifiCheckIp(void)
}
break;
case WL_CONNECT_FAILED:
- AddLog_P(LOG_LEVEL_INFO, PSTR(D_LOG_WIFI D_CONNECT_FAILED_WRONG_PASSWORD));
+ AddLog(LOG_LEVEL_INFO, PSTR(D_LOG_WIFI D_CONNECT_FAILED_WRONG_PASSWORD));
Settings.wifi_channel = 0; // Disable stored AP
if (Wifi.retry > (Wifi.retry_init / 2)) {
Wifi.retry = Wifi.retry_init / 2;
@@ -450,7 +435,7 @@ void WifiCheckIp(void)
break;
default: // WL_IDLE_STATUS and WL_DISCONNECTED
if (!Wifi.retry || ((Wifi.retry_init / 2) == Wifi.retry)) {
- AddLog_P(LOG_LEVEL_INFO, PSTR(D_LOG_WIFI D_CONNECT_FAILED_AP_TIMEOUT));
+ AddLog(LOG_LEVEL_INFO, PSTR(D_LOG_WIFI D_CONNECT_FAILED_AP_TIMEOUT));
Settings.wifi_channel = 0; // Disable stored AP
} else {
if (!strlen(SettingsText(SET_STASSID1)) && !strlen(SettingsText(SET_STASSID2))) {
@@ -458,7 +443,7 @@ void WifiCheckIp(void)
wifi_config_tool = WIFI_MANAGER; // Skip empty SSIDs and start Wifi config tool
Wifi.retry = 0;
} else {
- AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_WIFI D_ATTEMPTING_CONNECTION));
+ AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_WIFI D_ATTEMPTING_CONNECTION));
}
}
}
@@ -506,7 +491,7 @@ void WifiCheck(uint8_t param)
SettingsUpdateText(SET_STAPWD1, WiFi.psk().c_str());
}
Settings.sta_active = 0;
- AddLog_P(LOG_LEVEL_INFO, PSTR(D_LOG_WIFI D_WCFG_2_WIFIMANAGER D_CMND_SSID "1 %s"), SettingsText(SET_STASSID1));
+ AddLog(LOG_LEVEL_INFO, PSTR(D_LOG_WIFI D_WCFG_2_WIFIMANAGER D_CMND_SSID "1 %s"), SettingsText(SET_STASSID1));
}
}
if (!Wifi.config_counter) {
@@ -517,15 +502,11 @@ void WifiCheck(uint8_t param)
if (Wifi.scan_state) { WifiBeginAfterScan(); }
if (Wifi.counter <= 0) {
- AddLog_P(LOG_LEVEL_DEBUG_MORE, PSTR(D_LOG_WIFI D_CHECKING_CONNECTION));
+ AddLog(LOG_LEVEL_DEBUG_MORE, PSTR(D_LOG_WIFI D_CHECKING_CONNECTION));
Wifi.counter = WIFI_CHECK_SEC;
WifiCheckIp();
}
-#if LWIP_IPV6
- if (WifiCheckIPAddrStatus()) {
-#else
- if ((WL_CONNECTED == WiFi.status()) && (static_cast(WiFi.localIP()) != 0) && !Wifi.config_type) {
-#endif // LWIP_IPV6=1
+ if ((WL_CONNECTED == WiFi.status()) && WifiCheck_hasIP(WiFi.localIP()) && !Wifi.config_type) {
WifiSetState(1);
if (Settings.flag3.use_wifi_rescan) { // SetOption57 - Scan wifi network every 44 minutes for configured AP's
if (!(TasmotaGlobal.uptime % (60 * WIFI_RESCAN_MINUTES))) {
@@ -603,7 +584,7 @@ void WifiConnect(void)
#ifdef WIFI_RF_PRE_INIT
if (rf_pre_init_flag) {
- AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_WIFI "Pre-init done"));
+ AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_WIFI "Pre-init done"));
}
#endif // WIFI_RF_PRE_INIT
}
@@ -675,7 +656,7 @@ extern "C" {
}
void stationKeepAliveNow(void) {
- AddLog_P(LOG_LEVEL_DEBUG_MORE, PSTR(D_LOG_WIFI "Sending Gratuitous ARP"));
+ AddLog(LOG_LEVEL_DEBUG_MORE, PSTR(D_LOG_WIFI "Sending Gratuitous ARP"));
for (netif* interface = netif_list; interface != nullptr; interface = interface->next)
if (
(interface->flags & NETIF_FLAG_LINK_UP)
@@ -763,11 +744,11 @@ uint32_t WifiGetNtp(void) {
ntp_server_id++;
}
if (!resolved_ip) {
-// AddLog_P(LOG_LEVEL_DEBUG, PSTR("NTP: No server found"));
+// AddLog(LOG_LEVEL_DEBUG, PSTR("NTP: No server found"));
return 0;
}
-// AddLog_P(LOG_LEVEL_DEBUG, PSTR("NTP: Name %s, IP %s"), ntp_server, time_server_ip.toString().c_str());
+// AddLog(LOG_LEVEL_DEBUG, PSTR("NTP: Name %s, IP %_I"), ntp_server, (uint32_t)time_server_ip);
WiFiUDP udp;
@@ -817,7 +798,7 @@ uint32_t WifiGetNtp(void) {
if ((packet_buffer[0] & 0b11000000) == 0b11000000) {
// Leap-Indicator: unknown (clock unsynchronized)
// See: https://github.com/letscontrolit/ESPEasy/issues/2886#issuecomment-586656384
- AddLog_P(LOG_LEVEL_DEBUG, PSTR("NTP: IP %s unsynched"), time_server_ip.toString().c_str());
+ AddLog(LOG_LEVEL_DEBUG, PSTR("NTP: IP %_I unsynched"), (uint32_t)time_server_ip);
ntp_server_id++; // Next server next time
return 0;
}
@@ -837,7 +818,7 @@ uint32_t WifiGetNtp(void) {
delay(10);
}
// Timeout.
- AddLog_P(LOG_LEVEL_DEBUG, PSTR("NTP: No reply"));
+ AddLog(LOG_LEVEL_DEBUG, PSTR("NTP: No reply"));
udp.stop();
ntp_server_id++; // Next server next time
return 0;
diff --git a/tasmota/tasmota.ino b/tasmota/tasmota.ino
index 2407e61b1..027ee61d6 100644
--- a/tasmota/tasmota.ino
+++ b/tasmota/tasmota.ino
@@ -52,6 +52,8 @@
#include // Ota
#include // Ota
#include // Webserver, Updater
+#include
+#include
#include
#include
#ifdef USE_ARDUINO_OTA
@@ -67,9 +69,28 @@
#include // I2C support library
//#endif // USE_I2C
#ifdef USE_SPI
- #include // SPI support, TFT
+ #include // SPI support, TFT, SDcard
#endif // USE_SPI
+#ifdef USE_UFILESYS
+#ifdef ESP8266
+#include
+#include
+#ifdef USE_SDCARD
+#include
+#include
+#endif // USE_SDCARD
+#endif // ESP8266
+#ifdef ESP32
+#include
+#ifdef USE_SDCARD
+#include
+#endif // USE_SDCARD
+#include "FFat.h"
+#include "FS.h"
+#endif // ESP32
+#endif // USE_UFILESYS
+
// Structs
#include "settings.h"
@@ -293,7 +314,7 @@ void setup(void) {
Settings.module = Settings.fallback_module; // Reset module to fallback module
// Settings.last_module = Settings.fallback_module;
}
- AddLog_P(LOG_LEVEL_INFO, PSTR("FRC: " D_LOG_SOME_SETTINGS_RESET " (%d)"), RtcReboot.fast_reboot_count);
+ AddLog(LOG_LEVEL_INFO, PSTR("FRC: " D_LOG_SOME_SETTINGS_RESET " (%d)"), RtcReboot.fast_reboot_count);
}
}
@@ -302,7 +323,7 @@ void setup(void) {
snprintf_P(TasmotaGlobal.version, sizeof(TasmotaGlobal.version), PSTR("%s.%d"), TasmotaGlobal.version, VERSION & 0xff);
}
// Thehackbox inserts "release" or "commit number" before compiling using sed -i -e 's/PSTR("(%s)")/PSTR("(85cff52-%s)")/g' tasmota.ino
- snprintf_P(TasmotaGlobal.image_name, sizeof(TasmotaGlobal.image_name), PSTR("(%s)"), CODE_IMAGE_STR); // Results in (85cff52-tasmota) or (release-tasmota)
+ snprintf_P(TasmotaGlobal.image_name, sizeof(TasmotaGlobal.image_name), PSTR("(%s)"), PSTR(CODE_IMAGE_STR)); // Results in (85cff52-tasmota) or (release-tasmota)
Format(TasmotaGlobal.mqtt_client, SettingsText(SET_MQTT_CLIENT), sizeof(TasmotaGlobal.mqtt_client));
Format(TasmotaGlobal.mqtt_topic, SettingsText(SET_MQTT_TOPIC), sizeof(TasmotaGlobal.mqtt_topic));
@@ -320,10 +341,10 @@ void setup(void) {
SetPowerOnState();
- AddLog_P(LOG_LEVEL_INFO, PSTR(D_PROJECT " %s %s " D_VERSION " %s%s-" ARDUINO_CORE_RELEASE "(%s)"),
- PROJECT, SettingsText(SET_DEVICENAME), TasmotaGlobal.version, TasmotaGlobal.image_name, GetBuildDateAndTime().c_str());
+ AddLog(LOG_LEVEL_INFO, PSTR(D_PROJECT " %s %s " D_VERSION " %s%s-" ARDUINO_CORE_RELEASE "(%s)"),
+ PSTR(PROJECT), SettingsText(SET_DEVICENAME), TasmotaGlobal.version, TasmotaGlobal.image_name, GetBuildDateAndTime().c_str());
#ifdef FIRMWARE_MINIMAL
- AddLog_P(LOG_LEVEL_INFO, PSTR(D_WARNING_MINIMAL_VERSION));
+ AddLog(LOG_LEVEL_INFO, PSTR(D_WARNING_MINIMAL_VERSION));
#endif // FIRMWARE_MINIMAL
RtcInit();
diff --git a/tasmota/tasmota_configurations.h b/tasmota/tasmota_configurations.h
index 5dcce7502..de60b84e1 100644
--- a/tasmota/tasmota_configurations.h
+++ b/tasmota/tasmota_configurations.h
@@ -109,7 +109,8 @@
//#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 // [I2cDriver54] Enable support for VL53L1X sensor (I2C address 0x29) using Pololu VL53L1X library (+2k9 code)
+//#define USE_VL53L1X // [I2cDriver54] Enable VL53L1X time of flight sensor (I2C address 0x29) using Pololu VL53L1X library (+2k9 code)
+//#define USE_TOF10120 // [I2cDriver57] Enable TOF10120 time of flight sensor (I2C address 0x52) (+0k6 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)
@@ -140,6 +141,7 @@
//#define USE_EZODO // [I2cDriver55] Enable support for EZO's DO sensor (+0k3 code) - Shared EZO code required for any EZO device (+1k2 code)
//#define USE_EZORGB // [I2cDriver55] Enable support for EZO's RGB sensor (+0k5 code) - Shared EZO code required for any EZO device (+1k2 code)
//#define USE_EZOPMP // [I2cDriver55] Enable support for EZO's PMP sensor (+0k3 code) - Shared EZO code required for any EZO device (+1k2 code)
+//#define USE_SEESAW_SOIL // [I2cDriver56] Enable Capacitice Soil Moisture & Temperature Sensor (I2C addresses 0x36 - 0x39) (+1k3 code)
//#define USE_SPI // Hardware SPI using GPIO12(MISO), GPIO13(MOSI) and GPIO14(CLK) in addition to two user selectable GPIOs(CS and DC)
//#define USE_RC522 // Add support for MFRC522 13.56Mhz Rfid reader (+6k code)
@@ -168,7 +170,10 @@
#define USE_IBEACON // Add support for bluetooth LE passive scan of ibeacon devices (uses HM17 module)
//#define USE_GPS // Add support for GPS and NTP Server for becoming Stratus 1 Time Source (+ 3.1kb flash, +132 bytes RAM)
#define USE_HM10 // (ESP8266 only) Add support for HM-10 as a BLE-bridge for the LYWSD03 (+5k1 code)
-#define USE_MI_ESP32 // (ESP32 only) Add support for ESP32 as a BLE-bridge (+9k2 mem, +292k flash)
+#ifdef ESP32
+ #define USE_BLE_ESP32 // (ESP32 only) Add support for native BLE on ESP32 - use new driver
+ #define USE_MI_ESP32 // (ESP32 only) Add support for ESP32 as a BLE-bridge (+9k2 mem, +292k flash)
+#endif
#define USE_HRXL // Add support for MaxBotix HRXL-MaxSonar ultrasonic range finders (+0k7)
//#define USE_TASMOTA_CLIENT // Add support for Arduino Uno/Pro Mini via serial interface including flashing (+2k3 code, 44 mem)
//#define USE_OPENTHERM // Add support for OpenTherm (+15k code)
@@ -194,8 +199,8 @@
//#define USE_MAX31865 // Add support for MAX31865 RTD sensors using softSPI
#define USE_IR_REMOTE // Send IR remote commands using library IRremoteESP8266 and ArduinoJson (+4k code, 0k3 mem, 48 iram)
#define USE_IR_RECEIVE // Support for IR receiver (+5k5 code, 264 iram)
-
#define USE_LMT01 // Add support for TI LMT01 temperature sensor, count pulses on single GPIO (+0k5 code)
+//#define USE_WIEGAND // Add support for 24/26/32/34 bit RFID Wiegand interface (D0/D1) (+1k7 code)
#define USE_TM1638 // Add support for TM1638 switches copying Switch1 .. Switch8 (+1k code)
#define USE_HX711 // Add support for HX711 load cell (+1k5 code)
//#define USE_HX711_GUI // Add optional web GUI to HX711 as scale (+1k8 code)
@@ -231,6 +236,7 @@
#undef USE_EMULATION_WEMO // Disable Belkin WeMo emulation for Alexa (+6k code, +2k mem common)
#undef USE_DEEPSLEEP // Disable support for deepsleep (+1k code)
#undef USE_DEVICE_GROUPS // Disable support for device groups (+3k5 code)
+#undef USE_BLE_ESP32 // (ESP32 only) Disable support for native BLE on ESP32 - use new driver
#undef USE_MI_ESP32 // (ESP32 only) Disable support for ESP32 as a BLE-bridge (+9k2 mem, +292k flash)
#undef USE_PWM_DIMMER_REMOTE // Disbale support for remote switches to PWM Dimmer
@@ -290,6 +296,7 @@
#undef USE_TELEINFO // Disable support for French Energy Provider metering telemetry
#undef USE_IEM3000 // Disable support for Schneider Electric iEM3000-Modbus series energy monitor (+0k8 code)
#undef USE_WE517 // Disable support for Orno WE517-Modbus energy monitor (+1k code)
+#undef USE_BLE_ESP32 // (ESP32 only) Disable support for native BLE on ESP32 - use new driver
#undef USE_MI_ESP32 // (ESP32 only) Disable support for ESP32 as a BLE-bridge (+9k2 mem, +292k flash)
@@ -414,6 +421,7 @@
#undef USE_IBEACON // Disable support for bluetooth LE passive scan of ibeacon devices (uses HM17 module)
#undef USE_GPS // Disable support for GPS and NTP Server for becoming Stratus 1 Time Source (+ 3.1kb flash, +132 bytes RAM)
#undef USE_HM10 // (ESP8266 only) Disable support for HM-10 as a BLE-bridge for the LYWSD03 (+5k1 code)
+#undef USE_BLE_ESP32 // (ESP32 only) Disable support for native BLE on ESP32 - use new driver
#undef USE_MI_ESP32 // (ESP32 only) Disable support for ESP32 as a BLE-bridge (+9k2 mem, +292k flash)
#undef USE_HRXL // Disable support for MaxBotix HRXL-MaxSonar ultrasonic range finders (+0k7)
#undef USE_TASMOTA_CLIENT // Disable support for Arduino Uno/Pro Mini via serial interface including flashing (+2k3 code, 44 mem)
@@ -542,6 +550,7 @@
#undef USE_IBEACON // Disable support for bluetooth LE passive scan of ibeacon devices (uses HM17 module)
#undef USE_GPS // Disable support for GPS and NTP Server for becoming Stratus 1 Time Source (+ 3.1kb flash, +132 bytes RAM)
#undef USE_HM10 // (ESP8266 only) Disable support for HM-10 as a BLE-bridge for the LYWSD03 (+5k1 code)
+#undef USE_BLE_ESP32 // (ESP32 only) Disable support for native BLE on ESP32 - use new driver
#undef USE_MI_ESP32 // (ESP32 only) Disable support for ESP32 as a BLE-bridge (+9k2 mem, +292k flash)
#undef USE_HRXL // Disable support for MaxBotix HRXL-MaxSonar ultrasonic range finders (+0k7)
#undef USE_TASMOTA_CLIENT // Disable support for Arduino Uno/Pro Mini via serial interface including flashing (+2k3 code, 44 mem)
@@ -679,6 +688,7 @@
#undef USE_IBEACON // Disable support for bluetooth LE passive scan of ibeacon devices (uses HM17 module)
#undef USE_GPS // Disable support for GPS and NTP Server for becoming Stratus 1 Time Source (+ 3.1kb flash, +132 bytes RAM)
#undef USE_HM10 // (ESP8266 only) Disable support for HM-10 as a BLE-bridge for the LYWSD03 (+5k1 code)
+#undef USE_BLE_ESP32 // (ESP32 only) Disable support for native BLE on ESP32 - use new driver
#undef USE_MI_ESP32 // (ESP32 only) Disable support for ESP32 as a BLE-bridge (+9k2 mem, +292k flash)
#undef USE_HRXL // Disable support for MaxBotix HRXL-MaxSonar ultrasonic range finders (+0k7)
#undef USE_TASMOTA_CLIENT // Disable support for Arduino Uno/Pro Mini via serial interface including flashing (+2k3 code, 44 mem)
@@ -818,6 +828,7 @@
#undef USE_IBEACON // Disable support for bluetooth LE passive scan of ibeacon devices (uses HM17 module)
#undef USE_GPS // Disable support for GPS and NTP Server for becoming Stratus 1 Time Source (+ 3.1kb flash, +132 bytes RAM)
#undef USE_HM10 // (ESP8266 only) Disable support for HM-10 as a BLE-bridge for the LYWSD03 (+5k1 code)
+#undef USE_BLE_ESP32 // (ESP32 only) Disable support for native BLE on ESP32 - use new driver
#undef USE_MI_ESP32 // (ESP32 only) Disable support for ESP32 as a BLE-bridge (+9k2 mem, +292k flash)
#undef USE_HRXL // Disable support for MaxBotix HRXL-MaxSonar ultrasonic range finders (+0k7)
#undef USE_TASMOTA_CLIENT // Disable support for Arduino Uno/Pro Mini via serial interface including flashing (+2k3 code, 44 mem)
diff --git a/tasmota/tasmota_configurations_ESP32.h b/tasmota/tasmota_configurations_ESP32.h
index 0a507dc2b..af6d0418f 100644
--- a/tasmota/tasmota_configurations_ESP32.h
+++ b/tasmota/tasmota_configurations_ESP32.h
@@ -38,7 +38,7 @@
/*********************************************************************************************\
* [tasmota32-odroidgo.bin]
- * Provide an image with useful supported sensors enabled
+ * Provide an image with useful supported sensors enabled for Odroid Go
\*********************************************************************************************/
#ifdef FIRMWARE_ODROID_GO
@@ -52,14 +52,71 @@
#define FALLBACK_MODULE ODROID_GO // [Module2] Select default module on fast reboot where USER_MODULE is user template
#define USE_ODROID_GO // Add support for Odroid Go
+#define USE_UFILESYS
#define USE_SDCARD
+ #define GUI_TRASH_FILE
#define USE_ADC
#define USE_SPI
#define USE_DISPLAY // Add SPI Display Support (+2k code)
#define USE_DISPLAY_ILI9341 // [DisplayModel 4] Enable ILI9341 Tft 480x320 display (+19k code)
+#define USE_BLE_ESP32 // Enable new BLE driver
#define USE_MI_ESP32 // (ESP32 only) Add support for ESP32 as a BLE-bridge (+9k2 mem, +292k flash)
#endif // FIRMWARE_ODROID_GO
+/*********************************************************************************************\
+ * [tasmota32-core2.bin]
+ * Provide an image with useful supported sensors enabled for M5stack core2
+\*********************************************************************************************/
+
+#ifdef FIRMWARE_M5STACK_CORE2
+
+#undef CODE_IMAGE_STR
+#define CODE_IMAGE_STR "core2"
+
+#undef MODULE
+#define MODULE M5STACK_CORE2 // [Module] Select default module from tasmota_template.h
+#undef FALLBACK_MODULE
+#define FALLBACK_MODULE M5STACK_CORE2 // [Module2] Select default module on fast reboot where USER_MODULE is user template
+
+#define USE_M5STACK_CORE2 // Add support for M5Stack Core2
+ #define SAY_TIME
+ #define USE_WEBRADIO
+ #define USE_MPU6886
+#define USE_UFILESYS
+#define USE_SDCARD
+ #define GUI_TRASH_FILE
+#define USE_I2C
+ #define USE_BMA423
+#define USE_SPI
+ #define USE_DISPLAY
+ #define USE_DISPLAY_ILI9342
+ #define JPEG_PICTS
+ #define USE_FT5206
+ #define USE_TOUCH_BUTTONS
+ #define MAX_TOUCH_BUTTONS 16
+#define USE_SENDMAIL
+#define USE_ESP32MAIL
+
+//#define USE_SCRIPT // Add support for script (+17k code)
+// Script related defines
+#ifdef USE_SCRIPT
+ #undef USE_RULES
+ #define MAXVARS 75
+ #define MAXSVARS 15
+ #define MAXFILT 10
+ #define UFSYS_SIZE 8192
+ #define USE_SCRIPT_TASK
+ #define LARGE_ARRAYS
+ #define SCRIPT_LARGE_VNBUFF
+ #define USE_SCRIPT_GLOBVARS
+ #define USE_SCRIPT_SUB_COMMAND
+ #define USE_ANGLE_FUNC
+ #define SCRIPT_FULL_WEBPAGE
+ #define SCRIPT_GET_HTTPS_JP
+ #define USE_GOOGLE_CHARTS
+#endif // USE_SCRIPT
+#endif // FIRMWARE_M5STACK_CORE2
+
#endif // ESP32
#endif // _TASMOTA_CONFIGURATIONS_ESP32_H_
diff --git a/tasmota/tasmota_globals.h b/tasmota/tasmota_globals.h
index 4524410dd..cb3f4547b 100644
--- a/tasmota/tasmota_globals.h
+++ b/tasmota/tasmota_globals.h
@@ -225,6 +225,12 @@ const uint16_t LOG_BUFFER_SIZE = 4000; // Max number of characters in lo
#error "Arduino ESP8266 Core versions before 2.7.1 are not supported"
#endif
+#define UFS_FILE_WRITE "w"
+#define UFS_FILE_READ "r"
+#define FS_FILE_WRITE "w"
+#define FS_FILE_READ "r"
+#define FS_FILE_APPEND "a"
+
#define TASM_FILE_SETTINGS "/.settings" // Settings binary blob
#define TASM_FILE_SETTINGS_LKG "/.settings.lkg" // Last Known Good Settings binary blob
#define TASM_FILE_ZIGBEE "/zb" // Zigbee settings blob as used by CC2530 on ESP32
@@ -466,6 +472,43 @@ bool first_device_group_is_local = true;
#define DEBUG_TRACE_LOG(...)
#endif
+
+/*********************************************************************************************\
+ * Macro for SetOption synonyms
+ *
+ * SetOption synonyms come first in the list of commands, right after the prefix.
+ * They don't need any matching function pointer, since they are handled internally.
+ * So don't forget to NOT put pointers in the functions pointers list.
+ *
+ * The additionnal list of unsigned bytes contains the Option numbers of each synonyms
+ * in the same order. The first byte of the list contains the number of synonyms
+ * (not including the number itself). The is the number of names to skip to find the first command.
+ *
+ * As it si cumbersome to calculate the first byte (number of synonyms), we provide the following
+ * macro `SO_SYNONYMS(, )`
+ *
+ * For example:
+ * ```
+ * SO_SYNONYMS(kLightSynonyms,
+ * 37, 68, 82, 91, 92, 105,
+ * 106,
+ * );
+ * ```
+ *
+ * is equivalent to:
+ * ```
+ * const static uint8_t kLightSynonyms[] PROGMEM = {
+ * 7, // number of synonyms, automatically calculated
+ * 37, 68, 82, 91, 92, 105,
+ * 106,
+ * };
+ * ```
+ *
+ * This comes very handy if you need to adjust the number of synonyms depending on #defines
+\*********************************************************************************************/
+
+#define SO_SYNONYMS(N,...) const static uint8_t __syn_array_len_ ## N[] = { __VA_ARGS__ }; /* this first array will not be kept by linker, just used for sizeof() */ const static uint8_t N[] PROGMEM = { sizeof(__syn_array_len_ ## N), __VA_ARGS__ };
+
/*********************************************************************************************/
#endif // _TASMOTA_GLOBALS_H_
diff --git a/tasmota/tasmota_template.h b/tasmota/tasmota_template.h
index f2dbae6c4..dbdc41cce 100644
--- a/tasmota/tasmota_template.h
+++ b/tasmota/tasmota_template.h
@@ -146,6 +146,8 @@ enum UserSelectablePins {
GPIO_ROT1A_NP, GPIO_ROT1B_NP, // Rotary switch
GPIO_ADC_PH, // Analog PH Sensor
GPIO_BS814_CLK, GPIO_BS814_DAT, // Holtek BS814A2 touch ctrlr
+ GPIO_WIEGAND_D0, GPIO_WIEGAND_D1, // Wiegand Data lines
+ GPIO_NEOPOOL_TX, GPIO_NEOPOOL_RX, // Sugar Valley RS485 interface
GPIO_SENSOR_END };
enum ProgramSelectablePins {
@@ -312,6 +314,8 @@ const char kSensorNames[] PROGMEM =
D_SENSOR_ROTARY " A_n|" D_SENSOR_ROTARY " B_n|"
D_SENSOR_ADC_PH "|"
D_SENSOR_BS814_CLK "|" D_SENSOR_BS814_DAT "|"
+ D_SENSOR_WIEGAND_D0 "|" D_SENSOR_WIEGAND_D1 "|"
+ D_SENSOR_NEOPOOL_TX "|" D_SENSOR_NEOPOOL_RX "|"
;
const char kSensorNamesFixed[] PROGMEM =
@@ -321,7 +325,7 @@ const char kSensorNamesFixed[] PROGMEM =
#define MAX_A4988_MSS 3
#define MAX_WEBCAM_DATA 8
#define MAX_WEBCAM_HSD 3
-#define MAX_SM2135_DAT 4
+#define MAX_SM2135_DAT 6
const uint16_t kGpioNiceList[] PROGMEM = {
GPIO_NONE, // Not used
@@ -735,6 +739,14 @@ const uint16_t kGpioNiceList[] PROGMEM = {
AGPIO(GPIO_MIEL_HVAC_TX), // Mitsubishi Electric HVAC TX pin
AGPIO(GPIO_MIEL_HVAC_RX), // Mitsubishi Electric HVAC RX pin
#endif
+#ifdef USE_WIEGAND
+ AGPIO(GPIO_WIEGAND_D0), // Date line D0 of Wiegand devices
+ AGPIO(GPIO_WIEGAND_D1), // Date line D1 of Wiegand devices
+#endif
+#ifdef USE_NEOPOOL
+ AGPIO(GPIO_NEOPOOL_TX), // Sugar Valley RS485 Interface
+ AGPIO(GPIO_NEOPOOL_RX), // Sugar Valley RS485 Interface
+#endif
/*-------------------------------------------------------------------------------------------*\
* ESP32 specifics
@@ -2338,22 +2350,6 @@ enum SupportedModules {
M5STACK_CORE2,
MAXMODULE };
-const char kModuleNames[] PROGMEM =
- "ESP32-DevKit|"
-#ifdef USE_WEBCAM
- "ESP32-Cam|"
-#endif // USE_WEBCAM
-#ifdef USE_ODROID_GO
- "Odroid Go|"
-#endif // USE_ODROID_GO
-// "ESP32-Solo|"
-// "WT32-Eth01|"
-// "TTGO Watch|"
-#ifdef USE_M5STACK_CORE2
- "M5Stack Core2|"
-#endif // USE_M5STACK_CORE2
- ;
-
// Default module settings
const uint8_t kModuleNiceList[] PROGMEM = {
WEMOS,
@@ -2363,16 +2359,43 @@ const uint8_t kModuleNiceList[] PROGMEM = {
#ifdef USE_ODROID_GO
ODROID_GO,
#endif // USE_ODROID_GO
-// ESP32_SOLO,
-// WT32_ETH01,
-// TTGO_WATCH,
+#ifdef USE_ESP32_SOLO
+// ESP32_SOLO, // To be defined
+#endif // USE_ESP32_SOLO
+#ifdef USE_WT32_ETH01
+ WT32_ETH01,
+#endif // USE_WT32_ETH01
+#ifdef USE_TTGO_WATCH
+// TTGO_WATCH, // To be defined
+#endif // USE_TTGO_WATCH
#ifdef USE_M5STACK_CORE2
M5STACK_CORE2,
#endif // USE_M5STACK_CORE2
};
-const mytmplt kModules[] PROGMEM =
-{
+const char kModuleNames[] PROGMEM =
+ "ESP32-DevKit|"
+#ifdef USE_WEBCAM
+ "ESP32-Cam|"
+#endif // USE_WEBCAM
+#ifdef USE_ODROID_GO
+ "Odroid Go|"
+#endif // USE_ODROID_GO
+#ifdef USE_ESP32_SOLO
+// "ESP32-Solo|" // To be defined
+#endif // USE_ESP32_SOLO
+#ifdef USE_WT32_ETH01
+ "WT32-Eth01|"
+#endif // USE_WT32_ETH01
+#ifdef USE_TTGO_WATCH
+// "TTGO Watch|" // To be defined
+#endif // USE_TTGO_WATCH
+#ifdef USE_M5STACK_CORE2
+ "M5Stack Core2|"
+#endif // USE_M5STACK_CORE2
+ ;
+
+const mytmplt kModules[] PROGMEM = {
{ // WEMOS - Espressif ESP32-DevKitC - Any ESP32 device like WeMos and NodeMCU hardware (ESP32)
AGPIO(GPIO_USER), // 0 (I)O GPIO0, ADC2_CH1, TOUCH1, RTC_GPIO11, CLK_OUT1, EMAC_TX_CLK
AGPIO(GPIO_USER), // 1 IO TXD0 GPIO1, U0TXD, CLK_OUT3, EMAC_RXD2
@@ -2416,6 +2439,7 @@ const mytmplt kModules[] PROGMEM =
AGPIO(GPIO_USER), // 39 I NO PULLUP GPIO39, SENSOR_VN, ADC1_CH3, ADC_H, RTC_GPIO3
0 // Flag
},
+
#ifdef USE_WEBCAM
{ // ESP32_CAM_AITHINKER - Any ESP32 device with webcam (ESP32)
AGPIO(GPIO_WEBCAM_XCLK), // 0 (I)O GPIO0, CAM_XCLK
@@ -2461,6 +2485,7 @@ const mytmplt kModules[] PROGMEM =
0 // Flag
},
#endif // USE_WEBCAM
+
#ifdef USE_ODROID_GO
{ // ODROID_GO - (ESP32)
AGPIO(GPIO_KEY1), // 0 (I)O GPIO0, BTN-VOLUME
@@ -2506,6 +2531,63 @@ const mytmplt kModules[] PROGMEM =
0 // Flag
},
#endif // USE_ODROID_GO
+
+#ifdef USE_ESP32_SOLO
+// { // ESP32 Solo (ESP32) - To be defined
+// },
+#endif // USE_ESP32_SOLO
+
+#ifdef USE_WT32_ETH01
+ { // WT32_ETH01 - (ESP32)
+ 0, // 0 (I)O GPIO0, Ethernet EMAC_REF_CLK
+ AGPIO(GPIO_USER), // 1 IO TXD0 GPIO1, U0TXD, CLK_OUT3, EMAC_RXD2
+ AGPIO(GPIO_USER), // 2 IO GPIO2, ADC2_CH2, TOUCH2, RTC_GPIO12, HSPIWP, HS2_DATA0, SD_DATA0
+ AGPIO(GPIO_USER), // 3 IO RXD0 GPIO3, U0RXD, CLK_OUT2
+ AGPIO(GPIO_USER), // 4 IO GPIO4, ADC2_CH0, TOUCH0, RTC_GPIO10, HSPIHD, HS2_DATA1, SD_DATA1, EMAC_TX_ER
+ AGPIO(GPIO_USER), // 5 IO GPIO5, RXD Led green
+ // 6 IO GPIO6, Flash CLK
+ // 7 IO GPIO7, Flash D0
+ // 8 IO GPIO8, Flash D1
+ 0, // 9 IO GPIO9, Flash D2, U1RXD
+ 0, // 10 IO GPIO10, Flash D3, U1TXD
+ // 11 IO GPIO11, Flash CMD
+ AGPIO(GPIO_USER), // 12 (I)O GPIO12, ADC2_CH5, TOUCH5, RTC_GPIO15, MTDI, HSPIQ, HS2_DATA2, SD_DATA2, EMAC_TXD3 (If driven High, flash voltage (VDD_SDIO) is 1.8V not default 3.3V. Has internal pull-down, so unconnected = Low = 3.3V. May prevent flashing and/or booting if 3.3V flash is connected and pulled high. See ESP32 datasheet for more details.)
+ 0, // 13 IO GPIO13, Ethernet EMAC_RX_ER
+ AGPIO(GPIO_USER), // 14 IO GPIO14, ADC2_CH6, TOUCH6, RTC_GPIO16, MTMS, HSPICLK, HS2_CLK, SD_CLK, EMAC_TXD2
+ AGPIO(GPIO_USER), // 15 (I)O GPIO15, ADC2_CH3, TOUCH3, MTDO, HSPICS0, RTC_GPIO13, HS2_CMD, SD_CMD, EMAC_RXD3 (If driven Low, silences boot messages from normal boot. Has internal pull-up, so unconnected = High = normal output.)
+ AGPIO(GPIO_OUTPUT_HI), // 16 IO GPIO16, Ethernet OSC_ENA
+ AGPIO(GPIO_LEDLNK_INV), // 17 IO GPIO17, Network link led (green)
+ AGPIO(GPIO_ETH_PHY_MDIO), // 18 IO GPIO18, Ethernet MDIO
+ 0, // 19 IO GPIO19, Ethernet TXD0
+ 0, // 20
+ 0, // 21 IO GPIO21, Ethernet EMAC_TX_EN
+ 0, // 22 IO LED GPIO22, Ethernet EMAC_TXD1
+ AGPIO(GPIO_ETH_PHY_MDC), // 23 IO GPIO23, Ethernet MDC
+ 0, // 24
+ 0, // 25 IO GPIO25, Ethernet EMAC_RXD0
+ 0, // 26 IO GPIO26, Ethernet EMAC_RXD1
+ 0, // 27 IO GPIO27, Ethernet EMAC_RX_DV
+ 0, // 28
+ 0, // 29
+ 0, // 30
+ 0, // 31
+ AGPIO(GPIO_USER), // 32 IO GPIO32, CFG
+ AGPIO(GPIO_USER), // 33 IO GPIO33, 485_EN
+ 0, // 34 I NO PULLUP GPIO34, ADC1_CH6, RTC_GPIO4
+ AGPIO(GPIO_USER), // 35 I NO PULLUP GPIO35, ADC1_CH7, RTC_GPIO5
+ AGPIO(GPIO_USER), // 36 I NO PULLUP GPIO36, SENSOR_VP, ADC_H, ADC1_CH0, RTC_GPIO0
+ 0, // 37 NO PULLUP
+ 0, // 38 NO PULLUP
+ AGPIO(GPIO_USER), // 39 I NO PULLUP GPIO39, SENSOR_VN, ADC1_CH3, ADC_H, RTC_GPIO3
+ 0 // Flag
+ },
+#endif // USE_WT32_ETH01
+
+#ifdef USE_TTGO_WATCH
+// { // TTGO Watch (ESP32) - To be defined
+// },
+#endif // USE_TTGO_WATCH
+
#ifdef USE_M5STACK_CORE2
{ // M5STACK CORE2 - (ESP32)
AGPIO(GPIO_USER), // 0 (I)O GPIO0, SPKR_LRCK
diff --git a/tasmota/tasmota_version.h b/tasmota/tasmota_version.h
index d4b504f4f..f0a110311 100644
--- a/tasmota/tasmota_version.h
+++ b/tasmota/tasmota_version.h
@@ -20,6 +20,6 @@
#ifndef _TASMOTA_VERSION_H_
#define _TASMOTA_VERSION_H_
-const uint32_t VERSION = 0x09020003;
+const uint32_t VERSION = 0x09020004;
#endif // _TASMOTA_VERSION_H_
diff --git a/tasmota/user_config_override_sample.h b/tasmota/user_config_override_sample.h
index d33e5fc75..cf5f27a25 100644
--- a/tasmota/user_config_override_sample.h
+++ b/tasmota/user_config_override_sample.h
@@ -69,17 +69,17 @@ Examples :
#ifdef MY_IP
#undef WIFI_IP_ADDRESS
-#define WIFI_IP_ADDRESS MY_IP // Set to 0.0.0.0 for using DHCP or enter a static IP address
+#define WIFI_IP_ADDRESS MY_IP // Set to 0.0.0.0 for using DHCP or enter a static IP address
#endif
#ifdef MY_GW
#undef WIFI_GATEWAY
-#define WIFI_GATEWAY MY_GW // if not using DHCP set Gateway IP address
+#define WIFI_GATEWAY MY_GW // if not using DHCP set Gateway IP address
#endif
#ifdef MY_DNS
#undef WIFI_DNS
-#define WIFI_DNS MY_DNS // If not using DHCP set DNS IP address (might be equal to WIFI_GATEWAY)
+#define WIFI_DNS MY_DNS // If not using DHCP set DNS IP address (might be equal to WIFI_GATEWAY)
#endif
*/
diff --git a/tasmota/xdrv_01_webserver.ino b/tasmota/xdrv_01_webserver.ino
index d68cf9cd0..2598a295d 100644
--- a/tasmota/xdrv_01_webserver.ino
+++ b/tasmota/xdrv_01_webserver.ino
@@ -376,9 +376,10 @@ struct WEB {
} Web;
// Helper function to avoid code duplication (saves 4k Flash)
+// arg can be in PROGMEM
static void WebGetArg(const char* arg, char* out, size_t max)
{
- String s = Webserver->arg(arg);
+ String s = Webserver->arg((const __FlashStringHelper *)arg);
strlcpy(out, s.c_str(), max);
// out[max-1] = '\0'; // Ensure terminating NUL
}
@@ -391,7 +392,7 @@ void ShowWebSource(uint32_t source)
{
if ((source > 0) && (source < SRC_MAX)) {
char stemp1[20];
- AddLog_P(LOG_LEVEL_DEBUG, PSTR("SRC: %s from %s"), GetTextIndexed(stemp1, sizeof(stemp1), source, kCommandSource), Webserver->client().remoteIP().toString().c_str());
+ AddLog(LOG_LEVEL_DEBUG, PSTR("SRC: %s from %_I"), GetTextIndexed(stemp1, sizeof(stemp1), source, kCommandSource), (uint32_t)Webserver->client().remoteIP());
}
}
@@ -460,7 +461,7 @@ void StartWebserver(int type, IPAddress ipweb)
WebServer_on(uri, line.handler, pgm_read_byte(&line.method));
}
Webserver->onNotFound(HandleNotFound);
- Webserver->on("/u2", HTTP_POST, HandleUploadDone, HandleUploadLoop); // this call requires 2 functions so we keep a direct call
+ Webserver->on(F("/u2"), HTTP_POST, HandleUploadDone, HandleUploadLoop); // this call requires 2 functions so we keep a direct call
#ifndef FIRMWARE_MINIMAL
XdrvCall(FUNC_WEB_ADD_HANDLER);
XsnsCall(FUNC_WEB_ADD_HANDLER);
@@ -474,15 +475,15 @@ void StartWebserver(int type, IPAddress ipweb)
#if LWIP_IPV6
String ipv6_addr = WifiGetIPv6();
if (ipv6_addr!="") {
- AddLog_P(LOG_LEVEL_INFO, PSTR(D_LOG_HTTP D_WEBSERVER_ACTIVE_ON " %s%s " D_WITH_IP_ADDRESS " %s and IPv6 global address %s "),
- NetworkHostname(), (Mdns.begun) ? PSTR(".local") : "", ipweb.toString().c_str(), ipv6_addr.c_str());
+ AddLog(LOG_LEVEL_INFO, PSTR(D_LOG_HTTP D_WEBSERVER_ACTIVE_ON " %s%s " D_WITH_IP_ADDRESS " %_I and IPv6 global address %s "),
+ NetworkHostname(), (Mdns.begun) ? PSTR(".local") : "", (uint32_t)ipweb, ipv6_addr.c_str());
} else {
- AddLog_P(LOG_LEVEL_INFO, PSTR(D_LOG_HTTP D_WEBSERVER_ACTIVE_ON " %s%s " D_WITH_IP_ADDRESS " %s"),
- NetworkHostname(), (Mdns.begun) ? PSTR(".local") : "", ipweb.toString().c_str());
+ AddLog(LOG_LEVEL_INFO, PSTR(D_LOG_HTTP D_WEBSERVER_ACTIVE_ON " %s%s " D_WITH_IP_ADDRESS " %_I"),
+ NetworkHostname(), (Mdns.begun) ? PSTR(".local") : "", (uint32_t)ipweb);
}
#else
- AddLog_P(LOG_LEVEL_INFO, PSTR(D_LOG_HTTP D_WEBSERVER_ACTIVE_ON " %s%s " D_WITH_IP_ADDRESS " %s"),
- NetworkHostname(), (Mdns.begun) ? PSTR(".local") : "", ipweb.toString().c_str());
+ AddLog(LOG_LEVEL_INFO, PSTR(D_LOG_HTTP D_WEBSERVER_ACTIVE_ON " %s%s " D_WITH_IP_ADDRESS " %_I"),
+ NetworkHostname(), (Mdns.begun) ? PSTR(".local") : "", (uint32_t)ipweb);
#endif // LWIP_IPV6 = 1
TasmotaGlobal.rules_flag.http_init = 1;
}
@@ -494,7 +495,7 @@ void StopWebserver(void)
if (Web.state) {
Webserver->close();
Web.state = HTTP_OFF;
- AddLog_P(LOG_LEVEL_INFO, PSTR(D_LOG_HTTP D_WEBSERVER_STOPPED));
+ AddLog(LOG_LEVEL_INFO, PSTR(D_LOG_HTTP D_WEBSERVER_STOPPED));
}
}
@@ -504,11 +505,11 @@ void WifiManagerBegin(bool reset_only)
if (!TasmotaGlobal.global_state.wifi_down) {
// WiFi.mode(WIFI_AP_STA);
WifiSetMode(WIFI_AP_STA);
- AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_WIFI D_WIFIMANAGER_SET_ACCESSPOINT_AND_STATION));
+ AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_WIFI D_WIFIMANAGER_SET_ACCESSPOINT_AND_STATION));
} else {
// WiFi.mode(WIFI_AP);
WifiSetMode(WIFI_AP);
- AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_WIFI D_WIFIMANAGER_SET_ACCESSPOINT));
+ AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_WIFI D_WIFIMANAGER_SET_ACCESSPOINT));
}
StopWebserver();
@@ -623,7 +624,7 @@ void _WSContentSendBuffer(void)
return;
}
else if (len == sizeof(TasmotaGlobal.mqtt_data)) {
- AddLog_P(LOG_LEVEL_INFO, PSTR("HTP: Content too large"));
+ AddLog(LOG_LEVEL_INFO, PSTR("HTP: Content too large"));
}
else if (len < CHUNKED_BUFFER_SIZE) { // Append chunk buffer with small content
Web.chunk_buffer += TasmotaGlobal.mqtt_data;
@@ -643,7 +644,7 @@ void WSContentSend_P(const char* formatP, ...) // Content send snprintf_P ch
// This uses char strings. Be aware of sending %% if % is needed
va_list arg;
va_start(arg, formatP);
- int len = vsnprintf_P(TasmotaGlobal.mqtt_data, sizeof(TasmotaGlobal.mqtt_data), formatP, arg);
+ int len = ext_vsnprintf_P(TasmotaGlobal.mqtt_data, sizeof(TasmotaGlobal.mqtt_data), formatP, arg);
va_end(arg);
#ifdef DEBUG_TASMOTA_CORE
@@ -661,7 +662,7 @@ void WSContentSend_PD(const char* formatP, ...) // Content send snprintf_P ch
// This uses char strings. Be aware of sending %% if % is needed
va_list arg;
va_start(arg, formatP);
- int len = vsnprintf_P(TasmotaGlobal.mqtt_data, sizeof(TasmotaGlobal.mqtt_data), formatP, arg);
+ int len = ext_vsnprintf_P(TasmotaGlobal.mqtt_data, sizeof(TasmotaGlobal.mqtt_data), formatP, arg);
va_end(arg);
#ifdef DEBUG_TASMOTA_CORE
@@ -691,7 +692,7 @@ void WSContentStart_P(const char* title, bool auth)
WSContentBegin(200, CT_HTML);
if (title != nullptr) {
- WSContentSend_P(HTTP_HEADER1, D_HTML_LANGUAGE, SettingsText(SET_DEVICENAME), title);
+ WSContentSend_P(HTTP_HEADER1, PSTR(D_HTML_LANGUAGE), SettingsText(SET_DEVICENAME), title);
}
}
@@ -721,7 +722,7 @@ void WSContentSendStyle_P(const char* formatP, ...)
// This uses char strings. Be aware of sending %% if % is needed
va_list arg;
va_start(arg, formatP);
- int len = vsnprintf_P(TasmotaGlobal.mqtt_data, sizeof(TasmotaGlobal.mqtt_data), formatP, arg);
+ int len = ext_vsnprintf_P(TasmotaGlobal.mqtt_data, sizeof(TasmotaGlobal.mqtt_data), formatP, arg);
va_end(arg);
#ifdef DEBUG_TASMOTA_CORE
@@ -735,10 +736,10 @@ void WSContentSendStyle_P(const char* formatP, ...)
}
WSContentSend_P(HTTP_HEAD_STYLE3, WebColor(COL_TEXT),
#ifdef FIRMWARE_MINIMAL
- WebColor(COL_TEXT_WARNING),
+ WebColor(COL_TEXT_WARNING),
#endif
- WebColor(COL_TITLE),
- ModuleName().c_str(), SettingsText(SET_DEVICENAME));
+ WebColor(COL_TITLE),
+ ModuleName().c_str(), SettingsText(SET_DEVICENAME));
if (Settings.flag3.gui_hostname_ip) { // SetOption53 - Show hostanme and IP address in GUI main menu
bool lip = (static_cast(WiFi.localIP()) != 0);
bool sip = (static_cast(WiFi.softAPIP()) != 0);
@@ -767,7 +768,7 @@ void WSContentButton(uint32_t title_index)
WSContentSend_P(PSTR("
"),
GetTextIndexed(action, sizeof(action), title_index, kButtonAction),
GetTextIndexed(confirm, sizeof(confirm), title_index, kButtonConfirm),
- (!title_index) ? "rst" : "non",
+ (!title_index) ? PSTR("rst") : PSTR("non"),
GetTextIndexed(title, sizeof(title), title_index, kButtonTitle));
} else {
WSContentSend_P(PSTR(""),
@@ -782,11 +783,15 @@ void WSContentSpaceButton(uint32_t title_index)
WSContentButton(title_index);
}
+void WSContentSend_Temp(const char *types, float f_temperature) {
+ WSContentSend_PD(HTTP_SNS_F_TEMP, types, Settings.flag2.temperature_resolution, &f_temperature, TempUnit());
+}
+
void WSContentSend_THD(const char *types, float f_temperature, float f_humidity)
{
+ WSContentSend_Temp(types, f_temperature);
+
char parameter[FLOATSZ];
- dtostrfd(f_temperature, Settings.flag2.temperature_resolution, parameter);
- WSContentSend_PD(HTTP_SNS_TEMP, types, parameter, TempUnit());
dtostrfd(f_humidity, Settings.flag2.humidity_resolution, parameter);
WSContentSend_PD(HTTP_SNS_HUM, types, parameter);
dtostrfd(CalcTempHumToDew(f_temperature, f_humidity), Settings.flag2.temperature_resolution, parameter);
@@ -818,7 +823,7 @@ void WebRestart(uint32_t type)
// type 0 = restart
// type 1 = restart after config change
// type 2 = restart after config change with possible ip address change too
- AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_HTTP D_RESTART));
+ AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_HTTP D_RESTART));
bool reset_only = (HTTP_MANAGER_RESET_ONLY == Web.state);
@@ -866,8 +871,8 @@ void HandleWifiLogin(void)
void WebSliderColdWarm(void)
{
WSContentSend_P(HTTP_MSG_SLIDER_GRADIENT, // Cold Warm
- "a", // a - Unique HTML id
- "#eff", "#f81", // 6500k in RGB (White) to 2500k in RGB (Warm Yellow)
+ PSTR("a"), // a - Unique HTML id
+ PSTR("#eff"), PSTR("#f81"), // 6500k in RGB (White) to 2500k in RGB (Warm Yellow)
1, // sl1
153, 500, // Range color temperature
LightGetColorTemp(),
@@ -879,17 +884,17 @@ void HandleRoot(void)
{
if (CaptivePortal()) { return; } // If captive portal redirect instead of displaying the page.
- if (Webserver->hasArg("rst")) {
+ if (Webserver->hasArg(F("rst"))) {
WebRestart(0);
return;
}
if (WifiIsInManagerMode()) {
#ifndef FIRMWARE_MINIMAL
- if (strlen(SettingsText(SET_WEBPWD)) && !(Webserver->hasArg("USER1")) && !(Webserver->hasArg("PASS1")) && HTTP_MANAGER_RESET_ONLY != Web.state) {
+ if (strlen(SettingsText(SET_WEBPWD)) && !(Webserver->hasArg(F("USER1"))) && !(Webserver->hasArg(F("PASS1"))) && HTTP_MANAGER_RESET_ONLY != Web.state) {
HandleWifiLogin();
} else {
- if (!strlen(SettingsText(SET_WEBPWD)) || (((Webserver->arg("USER1") == WEB_USERNAME ) && (Webserver->arg("PASS1") == SettingsText(SET_WEBPWD) )) || HTTP_MANAGER_RESET_ONLY == Web.state)) {
+ if (!strlen(SettingsText(SET_WEBPWD)) || (((Webserver->arg(F("USER1")) == WEB_USERNAME ) && (Webserver->arg(F("PASS1")) == SettingsText(SET_WEBPWD) )) || HTTP_MANAGER_RESET_ONLY == Web.state)) {
HandleWifiConfiguration();
} else {
// wrong user and pass
@@ -904,7 +909,7 @@ void HandleRoot(void)
return;
}
- AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_HTTP D_MAIN_MENU));
+ AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_HTTP D_MAIN_MENU));
char stemp[33];
@@ -936,8 +941,8 @@ void HandleRoot(void)
LightGetHSB(&hue, &sat, nullptr);
WSContentSend_P(HTTP_MSG_SLIDER_GRADIENT, // Hue
- "b", // b - Unique HTML id
- "#800", PSTR("#f00 5%,#ff0 20%,#0f0 35%,#0ff 50%,#00f 65%,#f0f 80%,#f00 95%,#800"), // Hue colors
+ PSTR("b"), // b - Unique HTML id
+ PSTR("#800"), PSTR("#f00 5%,#ff0 20%,#0f0 35%,#0ff 50%,#00f 65%,#f0f 80%,#f00 95%,#800"), // Hue colors
2, // sl2 - Unique range HTML id - Used as source for Saturation end color
1, 359, // Range valid Hue
hue,
@@ -951,7 +956,7 @@ void HandleRoot(void)
snprintf_P(stemp, sizeof(stemp), PSTR("#%02X%02X%02X"), red, green, blue); // Saturation end color
WSContentSend_P(HTTP_MSG_SLIDER_GRADIENT, // Saturation
- "s", // s - Unique HTML id related to eb('s').style.background='linear-gradient(to right,rgb('+sl+'%%,'+sl+'%%,'+sl+'%%),hsl('+eb('sl2').value+',100%%,50%%))';
+ PSTR("s"), // s - Unique HTML id related to eb('s').style.background='linear-gradient(to right,rgb('+sl+'%%,'+sl+'%%,'+sl+'%%),hsl('+eb('sl2').value+',100%%,50%%))';
scolor, stemp, // Brightness to max current color
3, // sl3 - Unique range HTML id - Not used
0, 100, // Range 0 to 100%
@@ -960,8 +965,8 @@ void HandleRoot(void)
}
WSContentSend_P(HTTP_MSG_SLIDER_GRADIENT, // Brightness - Black to White
- "c", // c - Unique HTML id
- "#000", "#fff", // Black to White
+ PSTR("c"), // c - Unique HTML id
+ PSTR("#000"), PSTR("#fff"), // Black to White
4, // sl4 - Unique range HTML id - Used as source for Saturation begin color
Settings.flag3.slider_dimmer_stay_on, 100, // Range 0/1 to 100% (SetOption77 - Do not power off if slider moved to far left)
Settings.light_dimmer,
@@ -972,8 +977,8 @@ void HandleRoot(void)
WebSliderColdWarm();
}
WSContentSend_P(HTTP_MSG_SLIDER_GRADIENT, // White brightness - Black to White
- "f", // f - Unique HTML id
- "#000", "#fff", // Black to White
+ PSTR("f"), // f - Unique HTML id
+ PSTR("#000"), PSTR("#fff"), // Black to White
5, // sl5 - Unique range HTML id - Not used
Settings.flag3.slider_dimmer_stay_on, 100, // Range 0/1 to 100% (SetOption77 - Do not power off if slider moved to far left)
LightGetDimmer(2),
@@ -987,7 +992,7 @@ void HandleRoot(void)
WSContentSend_P(HTTP_MSG_SLIDER_GRADIENT, // Channel brightness - Black to White
stemp, // e1 to e5 - Unique HTML id
- "#000", "#fff", // Black to White
+ PSTR("#000"), PSTR("#fff"), // Black to White
i+1, // sl1 to sl5 - Unique range HTML id - Not used
1, 100, // Range 1 to 100%
changeUIntScale(Settings.light_color[i], 0, 255, 0, 100),
@@ -1008,7 +1013,7 @@ void HandleRoot(void)
#ifdef USE_SONOFF_IFAN
if (IsModuleIfan()) {
WSContentSend_P(HTTP_DEVICE_CONTROL, 36, 1,
- (strlen(SettingsText(SET_BUTTON1))) ? SettingsText(SET_BUTTON1) : D_BUTTON_TOGGLE,
+ (strlen(SettingsText(SET_BUTTON1))) ? SettingsText(SET_BUTTON1) : PSTR(D_BUTTON_TOGGLE),
"");
for (uint32_t i = 0; i < MaxFanspeed(); i++) {
snprintf_P(stemp, sizeof(stemp), PSTR("%d"), i);
@@ -1031,7 +1036,7 @@ void HandleRoot(void)
#endif // USE_SHUTTER
snprintf_P(stemp, sizeof(stemp), PSTR(" %d"), idx);
WSContentSend_P(HTTP_DEVICE_CONTROL, 100 / TasmotaGlobal.devices_present, idx,
- (set_button) ? SettingsText(SET_BUTTON1 + idx -1) : (TasmotaGlobal.devices_present < 5) ? D_BUTTON_TOGGLE : "",
+ (set_button) ? SettingsText(SET_BUTTON1 + idx -1) : (TasmotaGlobal.devices_present < 5) ? PSTR(D_BUTTON_TOGGLE) : "",
(set_button) ? "" : (TasmotaGlobal.devices_present > 1) ? stemp : "");
}
#ifdef USE_SONOFF_IFAN
@@ -1107,7 +1112,7 @@ bool HandleRootStatusRefresh(void)
char svalue[32]; // Command and number parameter
char webindex[5]; // WebGetArg name
- WebGetArg("o", tmp, sizeof(tmp)); // 1 - 16 Device number for button Toggle or Fanspeed
+ WebGetArg(PSTR("o"), tmp, sizeof(tmp)); // 1 - 16 Device number for button Toggle or Fanspeed
if (strlen(tmp)) {
ShowWebSource(SRC_WEBGUI);
uint32_t device = atoi(tmp);
@@ -1153,12 +1158,12 @@ bool HandleRootStatusRefresh(void)
#endif // USE_TUYA_MCU
}
#ifdef USE_LIGHT
- WebGetArg("d0", tmp, sizeof(tmp)); // 0 - 100 Dimmer value
+ WebGetArg(PSTR("d0"), tmp, sizeof(tmp)); // 0 - 100 Dimmer value
if (strlen(tmp)) {
snprintf_P(svalue, sizeof(svalue), PSTR(D_CMND_DIMMER " %s"), tmp);
ExecuteWebCommand(svalue, SRC_WEBGUI);
}
- WebGetArg("w0", tmp, sizeof(tmp)); // 0 - 100 White value
+ WebGetArg(PSTR("w0"), tmp, sizeof(tmp)); // 0 - 100 White value
if (strlen(tmp)) {
snprintf_P(svalue, sizeof(svalue), PSTR(D_CMND_WHITE " %s"), tmp);
ExecuteWebCommand(svalue, SRC_WEBGUI);
@@ -1173,17 +1178,17 @@ bool HandleRootStatusRefresh(void)
ExecuteWebCommand(svalue, SRC_WEBGUI);
}
}
- WebGetArg("t0", tmp, sizeof(tmp)); // 153 - 500 Color temperature
+ WebGetArg(PSTR("t0"), tmp, sizeof(tmp)); // 153 - 500 Color temperature
if (strlen(tmp)) {
snprintf_P(svalue, sizeof(svalue), PSTR(D_CMND_COLORTEMPERATURE " %s"), tmp);
ExecuteWebCommand(svalue, SRC_WEBGUI);
}
- WebGetArg("h0", tmp, sizeof(tmp)); // 0 - 359 Hue value
+ WebGetArg(PSTR("h0"), tmp, sizeof(tmp)); // 0 - 359 Hue value
if (strlen(tmp)) {
snprintf_P(svalue, sizeof(svalue), PSTR(D_CMND_HSBCOLOR "1 %s"), tmp);
ExecuteWebCommand(svalue, SRC_WEBGUI);
}
- WebGetArg("n0", tmp, sizeof(tmp)); // 0 - 99 Saturation value
+ WebGetArg(PSTR("n0"), tmp, sizeof(tmp)); // 0 - 99 Saturation value
if (strlen(tmp)) {
snprintf_P(svalue, sizeof(svalue), PSTR(D_CMND_HSBCOLOR "2 %s"), tmp);
ExecuteWebCommand(svalue, SRC_WEBGUI);
@@ -1200,19 +1205,19 @@ bool HandleRootStatusRefresh(void)
}
#endif // USE_SHUTTER
#ifdef USE_SONOFF_RF
- WebGetArg("k", tmp, sizeof(tmp)); // 1 - 16 Pre defined RF keys
+ WebGetArg(PSTR("k"), tmp, sizeof(tmp)); // 1 - 16 Pre defined RF keys
if (strlen(tmp)) {
snprintf_P(svalue, sizeof(svalue), PSTR(D_CMND_RFKEY "%s"), tmp);
ExecuteWebCommand(svalue, SRC_WEBGUI);
}
#endif // USE_SONOFF_RF
#ifdef USE_ZIGBEE
- WebGetArg("zbj", tmp, sizeof(tmp));
+ WebGetArg(PSTR("zbj"), tmp, sizeof(tmp));
if (strlen(tmp)) {
snprintf_P(svalue, sizeof(svalue), PSTR("ZbPermitJoin"));
ExecuteWebCommand(svalue, SRC_WEBGUI);
}
- WebGetArg("zbr", tmp, sizeof(tmp));
+ WebGetArg(PSTR("zbr"), tmp, sizeof(tmp));
if (strlen(tmp)) {
snprintf_P(svalue, sizeof(svalue), PSTR("ZbMap"));
ExecuteWebCommand(svalue, SRC_WEBGUI);
@@ -1283,7 +1288,7 @@ void HandleConfiguration(void)
{
if (!HttpCheckPriviledgedAccess()) { return; }
- AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_HTTP D_CONFIGURATION));
+ AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_HTTP D_CONFIGURATION));
WSContentStart_P(PSTR(D_CONFIGURATION));
WSContentSendStyle();
@@ -1315,7 +1320,7 @@ void WSContentSendNiceLists(uint32_t option) {
char stemp[30]; // Template number and Sensor name
for (uint32_t i = 0; i < ARRAY_SIZE(kGpioNiceList); i++) { // GPIO: }2'0'>None (0)}3}2'17'>Button1 (17)}3...
if (option && (1 == i)) {
- WSContentSend_P(HTTP_MODULE_TEMPLATE_REPLACE_NO_INDEX, AGPIO(GPIO_USER), D_SENSOR_USER); // }2'255'>User}3
+ WSContentSend_P(HTTP_MODULE_TEMPLATE_REPLACE_NO_INDEX, AGPIO(GPIO_USER), PSTR(D_SENSOR_USER)); // }2'255'>User}3
}
uint32_t ridx = pgm_read_word(kGpioNiceList + i) & 0xFFE0;
uint32_t midx = BGPIO(ridx);
@@ -1356,7 +1361,7 @@ void WSContentSendAdcNiceList(uint32_t option) {
WSContentSend_P(PSTR("os=\""));
for (uint32_t i = 0; i < ARRAY_SIZE(kAdcNiceList); i++) { // GPIO: }2'0'>None}3}2'17'>Analog}3...
if (option && (1 == i)) {
- WSContentSend_P(HTTP_MODULE_TEMPLATE_REPLACE_NO_INDEX, AGPIO(GPIO_USER), D_SENSOR_USER); // }2'15'>User}3
+ WSContentSend_P(HTTP_MODULE_TEMPLATE_REPLACE_NO_INDEX, AGPIO(GPIO_USER), PSTR(D_SENSOR_USER)); // }2'15'>User}3
}
uint32_t ridx = pgm_read_word(kAdcNiceList + i) & 0xFFE0;
uint32_t midx = BGPIO(ridx);
@@ -1372,7 +1377,7 @@ void HandleTemplateConfiguration(void)
{
if (!HttpCheckPriviledgedAccess()) { return; }
- if (Webserver->hasArg("save")) {
+ if (Webserver->hasArg(F("save"))) {
TemplateSaveSettings();
WebRestart(1);
return;
@@ -1380,7 +1385,7 @@ void HandleTemplateConfiguration(void)
char stemp[30]; // Template number and Sensor name
- WebGetArg("t", stemp, sizeof(stemp)); // 0 - 69 Template number
+ WebGetArg(PSTR("t"), stemp, sizeof(stemp)); // 0 - 69 Template number
if (strlen(stemp)) {
uint32_t module = atoi(stemp);
uint32_t module_save = Settings.module;
@@ -1402,7 +1407,7 @@ void HandleTemplateConfiguration(void)
return;
}
- AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_HTTP D_CONFIGURE_TEMPLATE));
+ AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_HTTP D_CONFIGURE_TEMPLATE));
WSContentStart_P(PSTR(D_CONFIGURE_TEMPLATE));
WSContentSend_P(HTTP_SCRIPT_MODULE_TEMPLATE);
@@ -1474,7 +1479,7 @@ void TemplateSaveSettings(void)
char tmp[TOPSZ]; // WebGetArg NAME and GPIO/BASE/FLAG byte value
char svalue[300]; // Template command string
- WebGetArg("s1", tmp, sizeof(tmp)); // NAME
+ WebGetArg(PSTR("s1"), tmp, sizeof(tmp)); // NAME
snprintf_P(svalue, sizeof(svalue), PSTR(D_CMND_TEMPLATE " {\"" D_JSON_NAME "\":\"%s\",\"" D_JSON_GPIO "\":["), tmp);
uint32_t j = 0;
@@ -1492,7 +1497,7 @@ void TemplateSaveSettings(void)
uint32_t state = Webserver->hasArg(webindex) << i; // FLAG
flag += state;
}
- WebGetArg("g99", tmp, sizeof(tmp)); // BASE
+ WebGetArg(PSTR("g99"), tmp, sizeof(tmp)); // BASE
uint32_t base = atoi(tmp) +1;
snprintf_P(svalue, sizeof(svalue), PSTR("%s],\"" D_JSON_FLAG "\":%d,\"" D_JSON_BASE "\":%d}"), svalue, flag, base);
@@ -1505,13 +1510,13 @@ void HandleModuleConfiguration(void)
{
if (!HttpCheckPriviledgedAccess()) { return; }
- if (Webserver->hasArg("save")) {
+ if (Webserver->hasArg(F("save"))) {
ModuleSaveSettings();
WebRestart(1);
return;
}
- AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_HTTP D_CONFIGURE_MODULE));
+ AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_HTTP D_CONFIGURE_MODULE));
char stemp[30]; // Sensor name
uint32_t midx;
@@ -1572,7 +1577,7 @@ void ModuleSaveSettings(void)
{
char tmp[8]; // WebGetArg numbers only
- WebGetArg("g99", tmp, sizeof(tmp));
+ WebGetArg(PSTR("g99"), tmp, sizeof(tmp));
uint32_t new_module = (!strlen(tmp)) ? MODULE : atoi(tmp);
Settings.last_module = Settings.module;
Settings.module = new_module;
@@ -1586,7 +1591,7 @@ void ModuleSaveSettings(void)
} else {
if (ValidGPIO(i, template_gp.io[i])) {
Settings.my_gp.io[i] = WebGetGpioArg(i);
- gpios += F(", "); gpios += String(i); gpios += F(" "); gpios += String(Settings.my_gp.io[i]);
+ gpios += F(", IO"); gpios += String(i); gpios += F(" "); gpios += String(Settings.my_gp.io[i]);
}
}
}
@@ -1622,9 +1627,9 @@ void HandleWifiConfiguration(void)
{
if (!HttpCheckPriviledgedAccess(!WifiIsInManagerMode())) { return; }
- AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_HTTP D_CONFIGURE_WIFI));
+ AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_HTTP D_CONFIGURE_WIFI));
- if (Webserver->hasArg("save") && HTTP_MANAGER_RESET_ONLY != Web.state) {
+ if (Webserver->hasArg(F("save")) && HTTP_MANAGER_RESET_ONLY != Web.state) {
WifiSaveSettings();
WebRestart(2);
return;
@@ -1639,15 +1644,15 @@ void HandleWifiConfiguration(void)
#endif // USE_ENHANCED_GUI_WIFI_SCAN
if (HTTP_MANAGER_RESET_ONLY != Web.state) {
- if (Webserver->hasArg("scan")) {
+ if (Webserver->hasArg(F("scan"))) {
#ifdef USE_EMULATION
UdpDisconnect();
#endif // USE_EMULATION
int n = WiFi.scanNetworks();
- AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_WIFI D_SCAN_DONE));
+ AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_WIFI D_SCAN_DONE));
if (0 == n) {
- AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_WIFI D_NO_NETWORKS_FOUND));
+ AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_WIFI D_NO_NETWORKS_FOUND));
WSContentSend_P(PSTR(D_NO_NETWORKS_FOUND));
WSContentSend_P(PSTR(". " D_REFRESH_TO_SCAN_AGAIN "."));
} else {
@@ -1709,10 +1714,9 @@ void HandleWifiConfiguration(void)
}
#else // No USE_ENHANCED_GUI_WIFI_SCAN
// remove duplicates ( must be RSSI sorted )
- String cssid;
for (uint32_t i = 0; i < n; i++) {
if (-1 == indices[i]) { continue; }
- cssid = WiFi.SSID(indices[i]);
+ String cssid = WiFi.SSID(indices[i]);
uint32_t cschn = WiFi.channel(indices[i]);
for (uint32_t j = i + 1; j < n; j++) {
if ((cssid == WiFi.SSID(indices[j])) && (cschn == WiFi.channel(indices[j]))) {
@@ -1776,20 +1780,20 @@ void WifiSaveSettings(void)
{
char tmp[TOPSZ]; // Max length is currently 150
- WebGetArg("h", tmp, sizeof(tmp));
+ WebGetArg(PSTR("h"), tmp, sizeof(tmp));
SettingsUpdateText(SET_HOSTNAME, (!strlen(tmp)) ? WIFI_HOSTNAME : tmp);
if (strchr(SettingsText(SET_HOSTNAME), '%') != nullptr) {
SettingsUpdateText(SET_HOSTNAME, WIFI_HOSTNAME);
}
- WebGetArg("c", tmp, sizeof(tmp));
+ WebGetArg(PSTR("c"), tmp, sizeof(tmp));
SettingsUpdateText(SET_CORS, (!strlen(tmp)) ? CORS_DOMAIN : tmp);
- WebGetArg("s1", tmp, sizeof(tmp));
+ WebGetArg(PSTR("s1"), tmp, sizeof(tmp));
SettingsUpdateText(SET_STASSID1, (!strlen(tmp)) ? STA_SSID1 : tmp);
- WebGetArg("s2", tmp, sizeof(tmp));
+ WebGetArg(PSTR("s2"), tmp, sizeof(tmp));
SettingsUpdateText(SET_STASSID2, (!strlen(tmp)) ? STA_SSID2 : tmp);
- WebGetArg("p1", tmp, sizeof(tmp));
+ WebGetArg(PSTR("p1"), tmp, sizeof(tmp));
SettingsUpdateText(SET_STAPWD1, (!strlen(tmp)) ? "" : (strlen(tmp) < 5) ? SettingsText(SET_STAPWD1) : tmp);
- WebGetArg("p2", tmp, sizeof(tmp));
+ WebGetArg(PSTR("p2"), tmp, sizeof(tmp));
SettingsUpdateText(SET_STAPWD2, (!strlen(tmp)) ? "" : (strlen(tmp) < 5) ? SettingsText(SET_STAPWD2) : tmp);
AddLog_P(LOG_LEVEL_INFO, PSTR(D_LOG_WIFI D_CMND_HOSTNAME " %s, " D_CMND_SSID "1 %s, " D_CMND_SSID "2 %s, " D_CMND_CORS " %s"),
SettingsText(SET_HOSTNAME), SettingsText(SET_STASSID1), SettingsText(SET_STASSID2), SettingsText(SET_CORS));
@@ -1801,7 +1805,7 @@ void HandleLoggingConfiguration(void)
{
if (!HttpCheckPriviledgedAccess()) { return; }
- AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_HTTP D_CONFIGURE_LOGGING));
+ AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_HTTP D_CONFIGURE_LOGGING));
if (Webserver->hasArg("save")) {
LoggingSaveSettings();
@@ -1839,19 +1843,19 @@ void LoggingSaveSettings(void)
{
char tmp[TOPSZ]; // Max length is currently 33
- WebGetArg("l0", tmp, sizeof(tmp));
+ WebGetArg(PSTR("l0"), tmp, sizeof(tmp));
SetSeriallog((!strlen(tmp)) ? SERIAL_LOG_LEVEL : atoi(tmp));
- WebGetArg("l1", tmp, sizeof(tmp));
+ WebGetArg(PSTR("l1"), tmp, sizeof(tmp));
Settings.weblog_level = (!strlen(tmp)) ? WEB_LOG_LEVEL : atoi(tmp);
- WebGetArg("l2", tmp, sizeof(tmp));
+ WebGetArg(PSTR("l2"), tmp, sizeof(tmp));
Settings.mqttlog_level = (!strlen(tmp)) ? MQTT_LOG_LEVEL : atoi(tmp);
- WebGetArg("l3", tmp, sizeof(tmp));
+ WebGetArg(PSTR("l3"), tmp, sizeof(tmp));
SetSyslog((!strlen(tmp)) ? SYS_LOG_LEVEL : atoi(tmp));
- WebGetArg("lh", tmp, sizeof(tmp));
+ WebGetArg(PSTR("lh"), tmp, sizeof(tmp));
SettingsUpdateText(SET_SYSLOG_HOST, (!strlen(tmp)) ? SYS_LOG_HOST : tmp);
- WebGetArg("lp", tmp, sizeof(tmp));
+ WebGetArg(PSTR("lp"), tmp, sizeof(tmp));
Settings.syslog_port = (!strlen(tmp)) ? SYS_LOG_PORT : atoi(tmp);
- WebGetArg("lt", tmp, sizeof(tmp));
+ WebGetArg(PSTR("lt"), tmp, sizeof(tmp));
Settings.tele_period = (!strlen(tmp)) ? TELE_PERIOD : atoi(tmp);
if ((Settings.tele_period > 0) && (Settings.tele_period < 10)) {
Settings.tele_period = 10; // Do not allow periods < 10 seconds
@@ -1866,9 +1870,9 @@ void HandleOtherConfiguration(void)
{
if (!HttpCheckPriviledgedAccess()) { return; }
- AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_HTTP D_CONFIGURE_OTHER));
+ AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_HTTP D_CONFIGURE_OTHER));
- if (Webserver->hasArg("save")) {
+ if (Webserver->hasArg(F("save"))) {
OtherSaveSettings();
WebRestart(1);
return;
@@ -1932,15 +1936,15 @@ void OtherSaveSettings(void)
char friendlyname[TOPSZ];
char message[MAX_LOGSZ];
- WebGetArg("dn", tmp, sizeof(tmp));
+ WebGetArg(PSTR("dn"), tmp, sizeof(tmp));
SettingsUpdateText(SET_DEVICENAME, (!strlen(tmp)) ? "" : (!strcmp(tmp,"1")) ? SettingsText(SET_FRIENDLYNAME1) : tmp);
- WebGetArg("wp", tmp, sizeof(tmp));
+ WebGetArg(PSTR("wp"), tmp, sizeof(tmp));
SettingsUpdateText(SET_WEBPWD, (!strlen(tmp)) ? "" : (strchr(tmp,'*')) ? SettingsText(SET_WEBPWD) : tmp);
- Settings.flag.mqtt_enabled = Webserver->hasArg("b1"); // SetOption3 - Enable MQTT
+ Settings.flag.mqtt_enabled = Webserver->hasArg(F("b1")); // SetOption3 - Enable MQTT
#ifdef USE_EMULATION
UdpDisconnect();
#if defined(USE_EMULATION_WEMO) || defined(USE_EMULATION_HUE)
- WebGetArg("b2", tmp, sizeof(tmp));
+ WebGetArg(PSTR("b2"), tmp, sizeof(tmp));
Settings.flag2.emulation = (!strlen(tmp)) ? 0 : atoi(tmp);
#endif // USE_EMULATION_WEMO || USE_EMULATION_HUE
#endif // USE_EMULATION
@@ -1951,14 +1955,14 @@ void OtherSaveSettings(void)
snprintf_P(webindex, sizeof(webindex), PSTR("a%d"), i);
WebGetArg(webindex, tmp, sizeof(tmp));
snprintf_P(friendlyname, sizeof(friendlyname), PSTR(FRIENDLY_NAME"%d"), i +1);
- SettingsUpdateText(SET_FRIENDLYNAME1 +i, (!strlen(tmp)) ? (i) ? friendlyname : FRIENDLY_NAME : tmp);
+ SettingsUpdateText(SET_FRIENDLYNAME1 +i, (!strlen(tmp)) ? (i) ? friendlyname : PSTR(FRIENDLY_NAME) : tmp);
snprintf_P(message, sizeof(message), PSTR("%s%s %s"), message, (i) ? "," : "", SettingsText(SET_FRIENDLYNAME1 +i));
}
AddLogData(LOG_LEVEL_INFO, message);
- WebGetArg("t1", tmp, sizeof(tmp));
+ WebGetArg(PSTR("t1"), tmp, sizeof(tmp));
if (strlen(tmp)) { // {"NAME":"12345678901234","GPIO":[255,255,255,255,255,255,255,255,255,255,255,255,255],"FLAG":255,"BASE":255}
- snprintf_P(message, sizeof(message), PSTR(D_CMND_BACKLOG " " D_CMND_TEMPLATE " %s%s"), tmp, (Webserver->hasArg("t2")) ? "; " D_CMND_MODULE " 0" : "");
+ snprintf_P(message, sizeof(message), PSTR(D_CMND_BACKLOG " " D_CMND_TEMPLATE " %s%s"), tmp, (Webserver->hasArg(F("t2"))) ? PSTR("; " D_CMND_MODULE " 0") : "");
ExecuteWebCommand(message, SRC_WEBGUI);
}
}
@@ -1969,7 +1973,7 @@ void HandleBackupConfiguration(void)
{
if (!HttpCheckPriviledgedAccess()) { return; }
- AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_HTTP D_BACKUP_CONFIGURATION));
+ AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_HTTP D_BACKUP_CONFIGURATION));
if (!SettingsBufferAlloc()) { return; }
@@ -2011,7 +2015,7 @@ void HandleResetConfiguration(void)
{
if (!HttpCheckPriviledgedAccess(!WifiIsInManagerMode())) { return; }
- AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_HTTP D_RESET_CONFIGURATION));
+ AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_HTTP D_RESET_CONFIGURATION));
WSContentStart_P(PSTR(D_RESET_CONFIGURATION), !WifiIsInManagerMode());
WSContentSendStyle();
@@ -2029,12 +2033,12 @@ void HandleRestoreConfiguration(void)
{
if (!HttpCheckPriviledgedAccess()) { return; }
- AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_HTTP D_RESTORE_CONFIGURATION));
+ AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_HTTP D_RESTORE_CONFIGURATION));
WSContentStart_P(PSTR(D_RESTORE_CONFIGURATION));
WSContentSendStyle();
WSContentSend_P(HTTP_FORM_RST);
- WSContentSend_P(HTTP_FORM_RST_UPG, D_RESTORE);
+ WSContentSend_P(HTTP_FORM_RST_UPG, PSTR(D_RESTORE));
if (WifiIsInManagerMode()) {
WSContentSpaceButton(BUTTON_MAIN);
} else {
@@ -2051,7 +2055,7 @@ void HandleInformation(void)
{
if (!HttpCheckPriviledgedAccess()) { return; }
- AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_HTTP D_INFORMATION));
+ AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_HTTP D_INFORMATION));
char stopic[TOPSZ];
@@ -2088,7 +2092,7 @@ void HandleInformation(void)
if (static_cast(EthernetLocalIP()) != 0) {
WSContentSend_P(PSTR("}1" D_HOSTNAME "}2%s%s"), EthernetHostname(), (Mdns.begun) ? PSTR(".local") : "");
WSContentSend_P(PSTR("}1" D_MAC_ADDRESS "}2%s"), EthernetMacAddress().c_str());
- WSContentSend_P(PSTR("}1" D_IP_ADDRESS " (eth)}2%s"), EthernetLocalIP().toString().c_str());
+ WSContentSend_P(PSTR("}1" D_IP_ADDRESS " (eth)}2%_I"), (uint32_t)EthernetLocalIP());
WSContentSend_P(PSTR("}1
}2
"));
}
#endif
@@ -2105,20 +2109,20 @@ void HandleInformation(void)
#endif
if (static_cast(WiFi.localIP()) != 0) {
WSContentSend_P(PSTR("}1" D_MAC_ADDRESS "}2%s"), WiFi.macAddress().c_str());
- WSContentSend_P(PSTR("}1" D_IP_ADDRESS " (wifi)}2%s"), WiFi.localIP().toString().c_str());
+ WSContentSend_P(PSTR("}1" D_IP_ADDRESS " (wifi)}2%_I"), (uint32_t)WiFi.localIP());
WSContentSend_P(PSTR("}1
}2
"));
}
}
if (!TasmotaGlobal.global_state.network_down) {
- WSContentSend_P(PSTR("}1" D_GATEWAY "}2%s"), IPAddress(Settings.ip_address[1]).toString().c_str());
- WSContentSend_P(PSTR("}1" D_SUBNET_MASK "}2%s"), IPAddress(Settings.ip_address[2]).toString().c_str());
- WSContentSend_P(PSTR("}1" D_DNS_SERVER "}2%s"), IPAddress(Settings.ip_address[3]).toString().c_str());
+ WSContentSend_P(PSTR("}1" D_GATEWAY "}2%_I"), Settings.ipv4_address[1]);
+ WSContentSend_P(PSTR("}1" D_SUBNET_MASK "}2%_I"), Settings.ipv4_address[2]);
+ WSContentSend_P(PSTR("}1" D_DNS_SERVER "}2%_I"), Settings.ipv4_address[3]);
}
if ((WiFi.getMode() >= WIFI_AP) && (static_cast(WiFi.softAPIP()) != 0)) {
WSContentSend_P(PSTR("}1
}2
"));
WSContentSend_P(PSTR("}1" D_MAC_ADDRESS "}2%s"), WiFi.softAPmacAddress().c_str());
- WSContentSend_P(PSTR("}1" D_IP_ADDRESS " (AP)}2%s"), WiFi.softAPIP().toString().c_str());
- WSContentSend_P(PSTR("}1" D_GATEWAY "}2%s"), WiFi.softAPIP().toString().c_str());
+ WSContentSend_P(PSTR("}1" D_IP_ADDRESS " (AP)}2%_I"), (uint32_t)WiFi.softAPIP());
+ WSContentSend_P(PSTR("}1" D_GATEWAY "}2%_I"), (uint32_t)WiFi.softAPIP());
}
WSContentSend_P(PSTR("}1}2 ")); // Empty line
if (Settings.flag.mqtt_enabled) { // SetOption3 - Enable MQTT
@@ -2126,7 +2130,7 @@ void HandleInformation(void)
WSContentSend_P(PSTR("}1" D_MQTT_PORT "}2%d"), Settings.mqtt_port);
#ifdef USE_MQTT_TLS
WSContentSend_P(PSTR("}1" D_MQTT_TLS_ENABLE "}2%s"), Settings.flag4.mqtt_tls ? PSTR(D_ENABLED) : PSTR(D_DISABLED));
-#endif // USE_MQTT_TLS
+#endif // USE_MQTT_TLS
WSContentSend_P(PSTR("}1" D_MQTT_USER "}2%s"), SettingsText(SET_MQTT_USER));
WSContentSend_P(PSTR("}1" D_MQTT_CLIENT "}2%s"), TasmotaGlobal.mqtt_client);
WSContentSend_P(PSTR("}1" D_MQTT_TOPIC "}2%s"), SettingsText(SET_MQTT_TOPIC));
@@ -2143,14 +2147,13 @@ void HandleInformation(void)
} else {
WSContentSend_P(PSTR("}1" D_MQTT "}2" D_DISABLED));
}
- WSContentSend_P(PSTR("}1}2 ")); // Empty line
+#if defined(USE_EMULATION) || defined(USE_DISCOVERY)
+ WSContentSend_P(PSTR("}1}2 ")); // Empty line
+#endif // USE_EMULATION or USE_DISCOVERY
#ifdef USE_EMULATION
WSContentSend_P(PSTR("}1" D_EMULATION "}2%s"), GetTextIndexed(stopic, sizeof(stopic), Settings.flag2.emulation, kEmulationOptions));
-#else
- WSContentSend_P(PSTR("}1" D_EMULATION "}2" D_DISABLED));
-#endif // USE_EMULATION
-
+#endif // USE_EMULATION
#ifdef USE_DISCOVERY
WSContentSend_P(PSTR("}1" D_MDNS_DISCOVERY "}2%s"), (Settings.flag3.mdns_enabled) ? D_ENABLED : D_DISABLED); // SetOption55 - Control mDNS service
if (Settings.flag3.mdns_enabled) { // SetOption55 - Control mDNS service
@@ -2158,11 +2161,9 @@ void HandleInformation(void)
WSContentSend_P(PSTR("}1" D_MDNS_ADVERTISE "}2" D_WEB_SERVER));
#else
WSContentSend_P(PSTR("}1" D_MDNS_ADVERTISE "}2" D_DISABLED));
-#endif // WEBSERVER_ADVERTISE
+#endif // WEBSERVER_ADVERTISE
}
-#else
- WSContentSend_P(PSTR("}1" D_MDNS_DISCOVERY "}2" D_DISABLED));
-#endif // USE_DISCOVERY
+#endif // USE_DISCOVERY
WSContentSend_P(PSTR("}1}2 ")); // Empty line
WSContentSend_P(PSTR("}1" D_ESP_CHIP_ID "}2%d"), ESP_getChipId());
@@ -2244,12 +2245,12 @@ uint32_t BUploadWriteBuffer(uint8_t *buf, size_t size) {
void HandleUpgradeFirmware(void) {
if (!HttpCheckPriviledgedAccess()) { return; }
- AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_HTTP D_FIRMWARE_UPGRADE));
+ AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_HTTP D_FIRMWARE_UPGRADE));
WSContentStart_P(PSTR(D_FIRMWARE_UPGRADE));
WSContentSendStyle();
WSContentSend_P(HTTP_FORM_UPG, SettingsText(SET_OTAURL));
- WSContentSend_P(HTTP_FORM_RST_UPG, D_UPGRADE);
+ WSContentSend_P(HTTP_FORM_RST_UPG, PSTR(D_UPGRADE));
WSContentSpaceButton(BUTTON_MAIN);
WSContentStop();
@@ -2261,11 +2262,11 @@ void HandleUpgradeFirmwareStart(void) {
char command[TOPSZ + 10]; // OtaUrl
- AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_HTTP D_UPGRADE_STARTED));
+ AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_HTTP D_UPGRADE_STARTED));
WifiConfigCounter();
char otaurl[TOPSZ];
- WebGetArg("o", otaurl, sizeof(otaurl));
+ WebGetArg(PSTR("o"), otaurl, sizeof(otaurl));
if (strlen(otaurl)) {
snprintf_P(command, sizeof(command), PSTR(D_CMND_OTAURL " %s"), otaurl);
ExecuteWebCommand(command, SRC_WEBGUI);
@@ -2296,7 +2297,7 @@ void HandleUploadDone(void) {
}
#endif // USE_ZIGBEE_EZSP
- AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_HTTP D_UPLOAD_DONE));
+ AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_HTTP D_UPLOAD_DONE));
WifiConfigCounter();
UploadServices(1);
@@ -2331,14 +2332,18 @@ void HandleUploadDone(void) {
WSContentStop();
}
+#ifdef USE_BLE_ESP32
+ // declare the fn
+ int ExtStopBLE();
+#endif
+
void UploadServices(uint32_t start_service) {
if (Web.upload_services_stopped != start_service) { return; }
Web.upload_services_stopped = !start_service;
if (start_service) {
-// AddLog_P(LOG_LEVEL_DEBUG, PSTR("UPL: Services enabled"));
+// AddLog(LOG_LEVEL_DEBUG, PSTR("UPL: Services enabled"));
- TasmotaGlobal.restart_flag = 0;
/*
MqttRetryCounter(0);
*/
@@ -2353,8 +2358,11 @@ void UploadServices(uint32_t start_service) {
#endif // USE_EMULATION
} else {
-// AddLog_P(LOG_LEVEL_DEBUG, PSTR("UPL: Services disabled"));
+// AddLog(LOG_LEVEL_DEBUG, PSTR("UPL: Services disabled"));
+#ifdef USE_BLE_ESP32
+ ExtStopBLE();
+#endif
#ifdef USE_EMULATION
UdpDisconnect();
#endif // USE_EMULATION
@@ -2370,7 +2378,6 @@ void UploadServices(uint32_t start_service) {
MqttDisconnect();
}
*/
- TasmotaGlobal.restart_flag = 120; // Set restart watchdog after 2 minutes
}
}
@@ -2386,7 +2393,7 @@ void HandleUploadLoop(void) {
if (UPL_TASMOTA == Web.upload_file_type) { Update.end(); }
UploadServices(1);
-// AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_UPLOAD "Upload error %d"), Web.upload_error);
+// AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_UPLOAD "Upload error %d"), Web.upload_error);
upload_error_signalled = true;
}
@@ -2409,7 +2416,7 @@ void HandleUploadLoop(void) {
}
SettingsSave(1); // Free flash for upload
- AddLog_P(LOG_LEVEL_INFO, PSTR(D_LOG_UPLOAD D_FILE " %s"), upload.filename.c_str());
+ AddLog(LOG_LEVEL_INFO, PSTR(D_LOG_UPLOAD D_FILE " %s"), upload.filename.c_str());
if (UPL_SETTINGS == Web.upload_file_type) {
if (!SettingsBufferAlloc()) {
@@ -2423,7 +2430,6 @@ void HandleUploadLoop(void) {
Web.upload_error = 2;
return;
}
- TasmotaGlobal.restart_flag = 0;
}
#endif // USE_UFILESYS
}
@@ -2489,7 +2495,7 @@ void HandleUploadLoop(void) {
return;
}
}
- AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_UPLOAD "File type %d"), Web.upload_file_type);
+ AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_UPLOAD "File type %d"), Web.upload_file_type);
} // First block received
if (UPL_SETTINGS == Web.upload_file_type) {
@@ -2511,7 +2517,7 @@ void HandleUploadLoop(void) {
#ifdef USE_WEB_FW_UPGRADE
else if (BUpload.active) {
// Write a block
-// AddLog_P(LOG_LEVEL_DEBUG, PSTR("DBG: Size %d"), upload.currentSize);
+// AddLog(LOG_LEVEL_DEBUG, PSTR("DBG: Size %d"), upload.currentSize);
// AddLogBuffer(LOG_LEVEL_DEBUG, upload.buf, 32);
Web.upload_error = BUploadWriteBuffer(upload.buf, upload.currentSize);
if (Web.upload_error != 0) { return; }
@@ -2522,7 +2528,7 @@ void HandleUploadLoop(void) {
return;
}
if (upload.totalSize && !(upload.totalSize % 102400)) {
- AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_UPLOAD "Progress %d kB"), upload.totalSize / 1024);
+ AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_UPLOAD "Progress %d kB"), upload.totalSize / 1024);
}
}
@@ -2579,12 +2585,12 @@ void HandleUploadLoop(void) {
// Done writing the data to SPI flash
BUpload.active = false;
- AddLog_P(LOG_LEVEL_INFO, PSTR(D_LOG_UPLOAD "Transfer %u bytes"), upload.totalSize);
+ AddLog(LOG_LEVEL_INFO, PSTR(D_LOG_UPLOAD "Transfer %u bytes"), upload.totalSize);
uint8_t* data = FlashDirectAccess();
// uint32_t* values = (uint32_t*)(data); // Only 4-byte access allowed
-// AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_UPLOAD "Head 0x%08X"), values[0]);
+// AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_UPLOAD "Head 0x%08X"), values[0]);
uint32_t error = 0;
#ifdef USE_RF_FLASH
@@ -2613,7 +2619,7 @@ void HandleUploadLoop(void) {
}
#endif // USE_ZIGBEE_EZSP
if (error != 0) {
-// AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_UPLOAD "Transfer error %d"), error);
+// AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_UPLOAD "Transfer error %d"), error);
Web.upload_error = error + (100 * Web.upload_file_type); // Add offset to discriminate transfer errors
return;
}
@@ -2623,7 +2629,7 @@ void HandleUploadLoop(void) {
Web.upload_error = 6; // Upload failed. Enable logging 3
return;
}
- AddLog_P(LOG_LEVEL_INFO, PSTR(D_LOG_UPLOAD D_SUCCESSFUL " %u bytes"), upload.totalSize);
+ AddLog(LOG_LEVEL_INFO, PSTR(D_LOG_UPLOAD D_SUCCESSFUL " %u bytes"), upload.totalSize);
}
// ***** Step4: Abort upload file
@@ -2632,7 +2638,9 @@ void HandleUploadLoop(void) {
Web.upload_error = 7; // Upload aborted
if (UPL_TASMOTA == Web.upload_file_type) { Update.end(); }
}
- delay(0);
+ // do actually wait a little to allow ESP32 tasks to tick
+ // fixes task timeout in ESP32Solo1 style unicore code.
+ delay(10);
OsWatchLoop();
// Scheduler(); // Feed OsWatch timer to prevent restart on long uploads
}
@@ -2653,14 +2661,14 @@ void HandleHttpCommand(void)
{
if (!HttpCheckPriviledgedAccess(false)) { return; }
- AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_HTTP D_COMMAND));
+ AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_HTTP D_COMMAND));
if (!WebAuthenticate()) {
// Prefer authorization via HTTP header (Basic auth), if it fails, use legacy method via GET parameters
char tmp1[33];
- WebGetArg("user", tmp1, sizeof(tmp1));
+ WebGetArg(PSTR("user"), tmp1, sizeof(tmp1));
char tmp2[strlen(SettingsText(SET_WEBPWD)) + 1];
- WebGetArg("password", tmp2, sizeof(tmp2));
+ WebGetArg(PSTR("password"), tmp2, sizeof(tmp2));
if (!(!strcmp(tmp1, WEB_USERNAME) && !strcmp(tmp2, SettingsText(SET_WEBPWD)))) {
WSContentBegin(401, CT_JSON);
@@ -2671,7 +2679,7 @@ void HandleHttpCommand(void)
}
WSContentBegin(200, CT_JSON);
- String svalue = Webserver->arg("cmnd");
+ String svalue = Webserver->arg(F("cmnd"));
if (svalue.length() && (svalue.length() < MQTT_MAX_PACKET_SIZE)) {
uint32_t curridx = TasmotaGlobal.log_buffer_pointer;
TasmotaGlobal.templog_level = LOG_LEVEL_INFO;
@@ -2707,12 +2715,12 @@ void HandleConsole(void)
{
if (!HttpCheckPriviledgedAccess()) { return; }
- if (Webserver->hasArg("c2")) { // Console refresh requested
+ if (Webserver->hasArg(F("c2"))) { // Console refresh requested
HandleConsoleRefresh();
return;
}
- AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_HTTP D_CONSOLE));
+ AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_HTTP D_CONSOLE));
WSContentStart_P(PSTR(D_CONSOLE));
WSContentSend_P(HTTP_SCRIPT_CONSOL, Settings.web_refresh);
@@ -2724,14 +2732,14 @@ void HandleConsole(void)
void HandleConsoleRefresh(void)
{
- String svalue = Webserver->arg("c1");
+ String svalue = Webserver->arg(F("c1"));
if (svalue.length() && (svalue.length() < MQTT_MAX_PACKET_SIZE)) {
AddLog_P(LOG_LEVEL_INFO, PSTR(D_LOG_COMMAND "%s"), svalue.c_str());
ExecuteWebCommand((char*)svalue.c_str(), SRC_WEBCONSOLE);
}
char stmp[8];
- WebGetArg("c2", stmp, sizeof(stmp));
+ WebGetArg(PSTR("c2"), stmp, sizeof(stmp));
uint32_t index = 0; // Initial start, dump all
if (strlen(stmp)) { index = atoi(stmp); }
@@ -2748,7 +2756,7 @@ void HandleConsoleRefresh(void)
if (len > sizeof(TasmotaGlobal.mqtt_data) -2) { len = sizeof(TasmotaGlobal.mqtt_data); }
char stemp[len +1];
strlcpy(stemp, line, len);
- WSContentSend_P(PSTR("%s%s"), (cflg) ? "\n" : "", stemp);
+ WSContentSend_P(PSTR("%s%s"), (cflg) ? PSTR("\n") : "", stemp);
cflg = true;
}
WSContentSend_P(PSTR("}1"));
@@ -2759,21 +2767,21 @@ void HandleConsoleRefresh(void)
void HandleNotFound(void)
{
-// AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_HTTP "Not found (%s)"), Webserver->uri().c_str());
+// AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_HTTP "Not found (%s)"), Webserver->uri().c_str());
if (CaptivePortal()) { return; } // If captive portal redirect instead of displaying the error page.
#ifdef USE_EMULATION
#ifdef USE_EMULATION_HUE
String path = Webserver->uri();
- if ((EMUL_HUE == Settings.flag2.emulation) && (path.startsWith("/api"))) {
+ if ((EMUL_HUE == Settings.flag2.emulation) && (path.startsWith(F("/api")))) {
HandleHueApi(&path);
} else
#endif // USE_EMULATION_HUE
#endif // USE_EMULATION
{
WSContentBegin(404, CT_PLAIN);
- WSContentSend_P(PSTR(D_FILE_NOT_FOUND "\n\nURI: %s\nMethod: %s\nArguments: %d\n"), Webserver->uri().c_str(), (Webserver->method() == HTTP_GET) ? "GET" : "POST", Webserver->args());
+ WSContentSend_P(PSTR(D_FILE_NOT_FOUND "\n\nURI: %s\nMethod: %s\nArguments: %d\n"), Webserver->uri().c_str(), (Webserver->method() == HTTP_GET) ? PSTR("GET") : PSTR("POST"), Webserver->args());
for (uint32_t i = 0; i < Webserver->args(); i++) {
WSContentSend_P(PSTR(" %s: %s\n"), Webserver->argName(i).c_str(), Webserver->arg(i).c_str());
}
@@ -2786,7 +2794,7 @@ bool CaptivePortal(void)
{
// Possible hostHeader: connectivitycheck.gstatic.com or 192.168.4.1
if ((WifiIsInManagerMode()) && !ValidIpAddress(Webserver->hostHeader().c_str())) {
- AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_HTTP D_REDIRECTED));
+ AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_HTTP D_REDIRECTED));
Webserver->sendHeader(F("Location"), String(F("http://")) + Webserver->client().localIP().toString(), true);
WSSend(302, CT_PLAIN, ""); // Empty content inhibits Content-length header so we have to close the socket ourselves.
@@ -2967,8 +2975,8 @@ void CmndWebServer(void)
Settings.webserver = XdrvMailbox.payload;
}
if (Settings.webserver) {
- Response_P(PSTR("{\"" D_CMND_WEBSERVER "\":\"" D_JSON_ACTIVE_FOR " %s " D_JSON_ON_DEVICE " %s " D_JSON_WITH_IP_ADDRESS " %s\"}"),
- (2 == Settings.webserver) ? D_ADMIN : D_USER, NetworkHostname(), NetworkAddress().toString().c_str());
+ Response_P(PSTR("{\"" D_CMND_WEBSERVER "\":\"" D_JSON_ACTIVE_FOR " %s " D_JSON_ON_DEVICE " %s " D_JSON_WITH_IP_ADDRESS " %_I\"}"),
+ (2 == Settings.webserver) ? PSTR(D_ADMIN) : PSTR(D_USER), NetworkHostname(), (uint32_t)NetworkAddress());
} else {
ResponseCmndStateText(0);
}
diff --git a/tasmota/xdrv_02_mqtt.ino b/tasmota/xdrv_02_mqtt.ino
index 9a0acba75..646df7037 100644
--- a/tasmota/xdrv_02_mqtt.ino
+++ b/tasmota/xdrv_02_mqtt.ino
@@ -32,6 +32,13 @@
WiFiClient EspClient; // Wifi Client - non-TLS
const char kMqttCommands[] PROGMEM = "|" // No prefix
+ // SetOption synonyms
+ D_SO_MQTTJSONONLY "|"
+#ifdef USE_MQTT_TLS
+ D_SO_MQTTTLS "|"
+#endif
+ D_SO_MQTTNORETAIN "|" D_SO_MQTTDETACHRELAY "|"
+ // regular commands
#if defined(USE_MQTT_TLS) && !defined(USE_MQTT_TLS_CA_CERT)
D_CMND_MQTTFINGERPRINT "|"
#endif
@@ -43,6 +50,19 @@ const char kMqttCommands[] PROGMEM = "|" // No prefix
D_CMND_FULLTOPIC "|" D_CMND_PREFIX "|" D_CMND_GROUPTOPIC "|" D_CMND_TOPIC "|" D_CMND_PUBLISH "|" D_CMND_MQTTLOG "|"
D_CMND_BUTTONTOPIC "|" D_CMND_SWITCHTOPIC "|" D_CMND_BUTTONRETAIN "|" D_CMND_SWITCHRETAIN "|" D_CMND_POWERRETAIN "|" D_CMND_SENSORRETAIN ;
+SO_SYNONYMS(kMqttSynonyms,
+ 90,
+#ifdef USE_MQTT_TLS
+ 103,
+#endif
+ 104, 114
+);
+
+// const uint8_t kMqttSynonyms[] PROGMEM = {
+// 4, // number of synonyms
+// 90, 103, 104, 114,
+// };
+
void (* const MqttCommand[])(void) PROGMEM = {
#if defined(USE_MQTT_TLS) && !defined(USE_MQTT_TLS_CA_CERT)
&CmndMqttFingerprint,
@@ -156,7 +176,7 @@ void MqttInit(void) {
// 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
+ if (host.indexOf(F(".iot.")) && host.endsWith(F(".amazonaws.com"))) { // look for ".iot." and ".amazonaws.com" in the domain name
Settings.flag4.mqtt_no_retain = true;
}
@@ -273,12 +293,12 @@ void MqttRetryCounter(uint8_t value) {
}
void MqttSubscribe(const char *topic) {
- AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_MQTT D_SUBSCRIBE_TO " %s"), topic);
+ AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_MQTT D_SUBSCRIBE_TO " %s"), topic);
MqttSubscribeLib(topic);
}
void MqttUnsubscribe(const char *topic) {
- AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_MQTT D_UNSUBSCRIBE_FROM " %s"), topic);
+ AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_MQTT D_UNSUBSCRIBE_FROM " %s"), topic);
MqttUnsubscribeLib(topic);
}
@@ -381,7 +401,7 @@ void MqttPublishPrefixTopic_P(uint32_t prefix, const char* subtopic, bool retain
free(mqtt_save);
bool result = MqttClient.publish(romram, TasmotaGlobal.mqtt_data, false);
- AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_MQTT "Updated shadow: %s"), romram);
+ AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_MQTT "Updated shadow: %s"), romram);
yield(); // #3313
}
#endif // USE_MQTT_AWS_IOT
@@ -475,7 +495,7 @@ void MqttDisconnected(int state) {
MqttClient.disconnect();
- AddLog_P(LOG_LEVEL_INFO, PSTR(D_LOG_MQTT D_CONNECT_FAILED_TO " %s:%d, rc %d. " D_RETRY_IN " %d " D_UNIT_SECOND), SettingsText(SET_MQTT_HOST), Settings.mqtt_port, state, Mqtt.retry_counter);
+ AddLog(LOG_LEVEL_INFO, PSTR(D_LOG_MQTT D_CONNECT_FAILED_TO " %s:%d, rc %d. " D_RETRY_IN " %d " D_UNIT_SECOND), SettingsText(SET_MQTT_HOST), Settings.mqtt_port, state, Mqtt.retry_counter);
TasmotaGlobal.rules_flag.mqtt_disconnected = 1;
}
@@ -483,7 +503,7 @@ void MqttConnected(void) {
char stopic[TOPSZ];
if (Mqtt.allowed) {
- AddLog_P(LOG_LEVEL_INFO, PSTR(D_LOG_MQTT D_CONNECTED));
+ AddLog(LOG_LEVEL_INFO, PSTR(D_LOG_MQTT D_CONNECTED));
Mqtt.connected = true;
Mqtt.retry_counter = 0;
Mqtt.retry_counter_delay = 1;
@@ -527,10 +547,10 @@ void MqttConnected(void) {
if (Settings.webserver) {
#if LWIP_IPV6
Response_P(PSTR("{\"" D_JSON_WEBSERVER_MODE "\":\"%s\",\"" D_CMND_HOSTNAME "\":\"%s\",\"" D_CMND_IPADDRESS "\":\"%s\",\"IPv6Address\":\"%s\"}"),
- (2 == Settings.webserver) ? D_ADMIN : D_USER, NetworkHostname(), NetworkAddress().toString().c_str(), WifiGetIPv6().c_str());
+ (2 == Settings.webserver) ? PSTR(D_ADMIN) : PSTR(D_USER), NetworkHostname(), NetworkAddress().toString().c_str(), WifiGetIPv6().c_str());
#else
Response_P(PSTR("{\"" D_JSON_WEBSERVER_MODE "\":\"%s\",\"" D_CMND_HOSTNAME "\":\"%s\",\"" D_CMND_IPADDRESS "\":\"%s\"}"),
- (2 == Settings.webserver) ? D_ADMIN : D_USER, NetworkHostname(), NetworkAddress().toString().c_str());
+ (2 == Settings.webserver) ? PSTR(D_ADMIN) : PSTR(D_USER), NetworkHostname(), NetworkAddress().toString().c_str());
#endif // LWIP_IPV6 = 1
MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_INFO "2"));
}
@@ -604,7 +624,7 @@ void MqttReconnect(void) {
#endif
#endif
- AddLog_P(LOG_LEVEL_INFO, PSTR(D_LOG_MQTT D_ATTEMPTING_CONNECTION));
+ AddLog(LOG_LEVEL_INFO, PSTR(D_LOG_MQTT D_ATTEMPTING_CONNECTION));
char *mqtt_user = nullptr;
char *mqtt_pwd = nullptr;
@@ -680,21 +700,21 @@ void MqttReconnect(void) {
#ifdef USE_MQTT_TLS
if (Mqtt.mqtt_tls) {
#ifdef ESP8266
- AddLog_P(LOG_LEVEL_INFO, PSTR(D_LOG_MQTT "TLS connected in %d ms, max ThunkStack used %d"),
+ AddLog(LOG_LEVEL_INFO, PSTR(D_LOG_MQTT "TLS connected in %d ms, max ThunkStack used %d"),
millis() - mqtt_connect_time, tlsClient->getMaxThunkStackUse());
#elif defined(ESP32)
- AddLog_P(LOG_LEVEL_INFO, PSTR(D_LOG_MQTT "TLS connected in %d ms, stack low mark %d"),
+ AddLog(LOG_LEVEL_INFO, PSTR(D_LOG_MQTT "TLS connected in %d ms, stack low mark %d"),
millis() - mqtt_connect_time, uxTaskGetStackHighWaterMark(nullptr));
#endif
if (!tlsClient->getMFLNStatus()) {
- AddLog_P(LOG_LEVEL_INFO, PSTR(D_LOG_MQTT "MFLN not supported by TLS server"));
+ AddLog(LOG_LEVEL_INFO, PSTR(D_LOG_MQTT "MFLN not supported by TLS server"));
}
#ifndef USE_MQTT_TLS_CA_CERT // don't bother with fingerprints if using CA validation
const uint8_t *recv_fingerprint = tlsClient->getRecvPubKeyFingerprint();
// create a printable version of the fingerprint received
char buf_fingerprint[64];
ToHex_P(recv_fingerprint, 20, buf_fingerprint, sizeof(buf_fingerprint), ' ');
- AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_MQTT "Server fingerprint: %s"), buf_fingerprint);
+ AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_MQTT "Server fingerprint: %s"), buf_fingerprint);
bool learned = false;
@@ -713,7 +733,7 @@ void MqttReconnect(void) {
}
if (learned) {
- AddLog_P(LOG_LEVEL_INFO, PSTR(D_LOG_MQTT "Fingerprint learned: %s"), buf_fingerprint);
+ AddLog(LOG_LEVEL_INFO, PSTR(D_LOG_MQTT "Fingerprint learned: %s"), buf_fingerprint);
SettingsSaveAll(); // save settings
}
@@ -724,7 +744,7 @@ void MqttReconnect(void) {
} else {
#ifdef USE_MQTT_TLS
if (Mqtt.mqtt_tls) {
- AddLog_P(LOG_LEVEL_INFO, PSTR(D_LOG_MQTT "TLS connection error: %d"), tlsClient->getLastError());
+ AddLog(LOG_LEVEL_INFO, PSTR(D_LOG_MQTT "TLS connection error: %d"), tlsClient->getLastError());
}
#endif
MqttDisconnected(MqttClient.state()); // status codes are documented here http://pubsubclient.knolleary.net/api.html#state
@@ -787,7 +807,7 @@ void CmndMqttFingerprint(void) {
void CmndMqttUser(void) {
if (XdrvMailbox.data_len > 0) {
- SettingsUpdateText(SET_MQTT_USER, (SC_CLEAR == Shortcut()) ? "" : (SC_DEFAULT == Shortcut()) ? MQTT_USER : XdrvMailbox.data);
+ SettingsUpdateText(SET_MQTT_USER, (SC_CLEAR == Shortcut()) ? "" : (SC_DEFAULT == Shortcut()) ? PSTR(MQTT_USER) : XdrvMailbox.data);
TasmotaGlobal.restart_flag = 2;
}
ResponseCmndChar(SettingsText(SET_MQTT_USER));
@@ -795,7 +815,7 @@ void CmndMqttUser(void) {
void CmndMqttPassword(void) {
if (XdrvMailbox.data_len > 0) {
- SettingsUpdateText(SET_MQTT_PWD, (SC_CLEAR == Shortcut()) ? "" : (SC_DEFAULT == Shortcut()) ? MQTT_PASS : XdrvMailbox.data);
+ SettingsUpdateText(SET_MQTT_PWD, (SC_CLEAR == Shortcut()) ? "" : (SC_DEFAULT == Shortcut()) ? PSTR(MQTT_PASS) : XdrvMailbox.data);
ResponseCmndChar(SettingsText(SET_MQTT_PWD));
TasmotaGlobal.restart_flag = 2;
} else {
@@ -852,7 +872,7 @@ void CmndStateText(void) {
void CmndMqttClient(void) {
if (!XdrvMailbox.grpflg && (XdrvMailbox.data_len > 0)) {
- SettingsUpdateText(SET_MQTT_CLIENT, (SC_DEFAULT == Shortcut()) ? MQTT_CLIENT_ID : XdrvMailbox.data);
+ SettingsUpdateText(SET_MQTT_CLIENT, (SC_DEFAULT == Shortcut()) ? PSTR(MQTT_CLIENT_ID) : XdrvMailbox.data);
TasmotaGlobal.restart_flag = 2;
}
ResponseCmndChar(SettingsText(SET_MQTT_CLIENT));
@@ -882,7 +902,7 @@ void CmndPrefix(void) {
if (XdrvMailbox.data_len > 0) {
MakeValidMqtt(0, XdrvMailbox.data);
SettingsUpdateText(SET_MQTTPREFIX1 + XdrvMailbox.index -1,
- (SC_DEFAULT == Shortcut()) ? (1==XdrvMailbox.index) ? SUB_PREFIX : (2==XdrvMailbox.index) ? PUB_PREFIX : PUB_PREFIX2 : XdrvMailbox.data);
+ (SC_DEFAULT == Shortcut()) ? (1==XdrvMailbox.index) ? PSTR(SUB_PREFIX) : (2==XdrvMailbox.index) ? PSTR(PUB_PREFIX) : PSTR(PUB_PREFIX2) : XdrvMailbox.data);
TasmotaGlobal.restart_flag = 2;
}
ResponseCmndIdxChar(SettingsText(SET_MQTTPREFIX1 + XdrvMailbox.index -1));
@@ -918,7 +938,7 @@ void CmndGroupTopic(void) {
uint32_t settings_text_index = (1 == XdrvMailbox.index) ? SET_MQTT_GRP_TOPIC : SET_MQTT_GRP_TOPIC2 + XdrvMailbox.index - 2;
MakeValidMqtt(0, XdrvMailbox.data);
if (!strcmp(XdrvMailbox.data, TasmotaGlobal.mqtt_client)) { SetShortcutDefault(); }
- SettingsUpdateText(settings_text_index, (SC_CLEAR == Shortcut()) ? "" : (SC_DEFAULT == Shortcut()) ? MQTT_GRPTOPIC : XdrvMailbox.data);
+ SettingsUpdateText(settings_text_index, (SC_CLEAR == Shortcut()) ? "" : (SC_DEFAULT == Shortcut()) ? PSTR(MQTT_GRPTOPIC) : XdrvMailbox.data);
// Eliminate duplicates, have at least one and fill from index 1
char stemp[MAX_GROUP_TOPICS][TOPSZ];
@@ -940,7 +960,7 @@ void CmndGroupTopic(void) {
}
}
if (0 == read_index) {
- SettingsUpdateText(SET_MQTT_GRP_TOPIC, MQTT_GRPTOPIC);
+ SettingsUpdateText(SET_MQTT_GRP_TOPIC, PSTR(MQTT_GRPTOPIC));
} else {
uint32_t write_index = 0;
uint32_t real_index = SET_MQTT_GRP_TOPIC;
@@ -998,7 +1018,7 @@ void CmndSwitchTopic(void) {
switch (Shortcut()) {
case SC_CLEAR: SettingsUpdateText(SET_MQTT_SWITCH_TOPIC, ""); break;
case SC_DEFAULT: SettingsUpdateText(SET_MQTT_SWITCH_TOPIC, TasmotaGlobal.mqtt_topic); break;
- case SC_USER: SettingsUpdateText(SET_MQTT_SWITCH_TOPIC, MQTT_SWITCH_TOPIC); break;
+ case SC_USER: SettingsUpdateText(SET_MQTT_SWITCH_TOPIC, PSTR(MQTT_SWITCH_TOPIC)); break;
default: SettingsUpdateText(SET_MQTT_SWITCH_TOPIC, XdrvMailbox.data);
}
}
@@ -1065,7 +1085,7 @@ void CmndSensorRetain(void) {
\*********************************************************************************************/
#if defined(USE_MQTT_TLS) && defined(USE_MQTT_AWS_IOT)
-// const static uint16_t tls_spi_start_sector = FLASH_EEPROM_START + 4; // 0xXXFF
+// const static uint16_t tls_spi_start_sector = EEPROM_LOCATION + 4; // 0xXXFF
// const static uint8_t* tls_spi_start = (uint8_t*) ((tls_spi_start_sector * SPI_FLASH_SEC_SIZE) + 0x40200000); // 0x40XFF000
const static uint16_t tls_spi_start_sector = 0xFF; // Force last bank of first MB
const static uint8_t* tls_spi_start = (uint8_t*) 0x402FF000; // 0x402FF000
@@ -1128,7 +1148,7 @@ void CmndTlsKey(void) {
// first copy SPI buffer into ram
uint8_t *spi_buffer = (uint8_t*) malloc(tls_spi_len);
if (!spi_buffer) {
- AddLog_P(LOG_LEVEL_ERROR, ALLOCATE_ERROR);
+ AddLog(LOG_LEVEL_ERROR, ALLOCATE_ERROR);
return;
}
memcpy_P(spi_buffer, tls_spi_start, tls_spi_len);
@@ -1142,7 +1162,7 @@ void CmndTlsKey(void) {
if (bin_len > 0) {
bin_buf = (uint8_t*) malloc(bin_len + 4);
if (!bin_buf) {
- AddLog_P(LOG_LEVEL_ERROR, ALLOCATE_ERROR);
+ AddLog(LOG_LEVEL_ERROR, ALLOCATE_ERROR);
free(spi_buffer);
return;
}
@@ -1163,7 +1183,7 @@ void CmndTlsKey(void) {
if (bin_len > 0) {
if (bin_len != 32) {
// no private key was previously stored, abort
- AddLog_P(LOG_LEVEL_INFO, PSTR("TLSKey: Certificate must be 32 bytes: %d."), bin_len);
+ AddLog(LOG_LEVEL_INFO, PSTR("TLSKey: Certificate must be 32 bytes: %d."), bin_len);
free(spi_buffer);
free(bin_buf);
return;
@@ -1180,14 +1200,14 @@ void CmndTlsKey(void) {
// Try to write Certificate
if (TLS_NAME_SKEY != tls_dir.entry[0].name) {
// no private key was previously stored, abort
- AddLog_P(LOG_LEVEL_INFO, PSTR("TLSKey: cannot store Cert if no Key previously stored."));
+ AddLog(LOG_LEVEL_INFO, PSTR("TLSKey: cannot store Cert if no Key previously stored."));
free(spi_buffer);
free(bin_buf);
return;
}
if (bin_len <= 256) {
// Certificate lenght too short
- AddLog_P(LOG_LEVEL_INFO, PSTR("TLSKey: Certificate length too short: %d."), bin_len);
+ AddLog(LOG_LEVEL_INFO, PSTR("TLSKey: Certificate length too short: %d."), bin_len);
free(spi_buffer);
free(bin_buf);
return;
@@ -1264,9 +1284,9 @@ void HandleMqttConfiguration(void)
{
if (!HttpCheckPriviledgedAccess()) { return; }
- AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_HTTP D_CONFIGURE_MQTT));
+ AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_HTTP D_CONFIGURE_MQTT));
- if (Webserver->hasArg("save")) {
+ if (Webserver->hasArg(F("save"))) {
MqttSaveSettings();
WebRestart(1);
return;
@@ -1282,11 +1302,11 @@ void HandleMqttConfiguration(void)
#ifdef USE_MQTT_TLS
Mqtt.mqtt_tls ? PSTR(" checked") : "", // SetOption102 - Enable MQTT TLS
#endif // USE_MQTT_TLS
- Format(str, MQTT_CLIENT_ID, sizeof(str)), MQTT_CLIENT_ID, SettingsText(SET_MQTT_CLIENT));
+ Format(str, PSTR(MQTT_CLIENT_ID), sizeof(str)), PSTR(MQTT_CLIENT_ID), SettingsText(SET_MQTT_CLIENT));
WSContentSend_P(HTTP_FORM_MQTT2,
(!strlen(SettingsText(SET_MQTT_USER))) ? "0" : SettingsText(SET_MQTT_USER),
- Format(str, MQTT_TOPIC, sizeof(str)), MQTT_TOPIC, SettingsText(SET_MQTT_TOPIC),
- MQTT_FULLTOPIC, MQTT_FULLTOPIC, SettingsText(SET_MQTT_FULLTOPIC));
+ Format(str, PSTR(MQTT_TOPIC), sizeof(str)), PSTR(MQTT_TOPIC), SettingsText(SET_MQTT_TOPIC),
+ PSTR(MQTT_FULLTOPIC), PSTR(MQTT_FULLTOPIC), SettingsText(SET_MQTT_FULLTOPIC));
WSContentSend_P(HTTP_FORM_END);
WSContentSpaceButton(BUTTON_CONFIGURATION);
WSContentStop();
@@ -1298,10 +1318,10 @@ void MqttSaveSettings(void)
char stemp[TOPSZ];
char stemp2[TOPSZ];
- WebGetArg("mt", tmp, sizeof(tmp));
+ WebGetArg(PSTR("mt"), tmp, sizeof(tmp));
strlcpy(stemp, (!strlen(tmp)) ? MQTT_TOPIC : tmp, sizeof(stemp));
MakeValidMqtt(0, stemp);
- WebGetArg("mf", tmp, sizeof(tmp));
+ WebGetArg(PSTR("mf"), tmp, sizeof(tmp));
strlcpy(stemp2, (!strlen(tmp)) ? MQTT_FULLTOPIC : tmp, sizeof(stemp2));
MakeValidMqtt(1, stemp2);
if ((strcmp(stemp, SettingsText(SET_MQTT_TOPIC))) || (strcmp(stemp2, SettingsText(SET_MQTT_FULLTOPIC)))) {
@@ -1310,18 +1330,18 @@ void MqttSaveSettings(void)
}
SettingsUpdateText(SET_MQTT_TOPIC, stemp);
SettingsUpdateText(SET_MQTT_FULLTOPIC, stemp2);
- WebGetArg("mh", tmp, sizeof(tmp));
- SettingsUpdateText(SET_MQTT_HOST, (!strlen(tmp)) ? MQTT_HOST : (!strcmp(tmp,"0")) ? "" : tmp);
- WebGetArg("ml", tmp, sizeof(tmp));
+ WebGetArg(PSTR("mh"), tmp, sizeof(tmp));
+ SettingsUpdateText(SET_MQTT_HOST, (!strlen(tmp)) ? PSTR(MQTT_HOST) : (!strcmp(tmp,"0")) ? "" : tmp);
+ WebGetArg(PSTR("ml"), tmp, sizeof(tmp));
Settings.mqtt_port = (!strlen(tmp)) ? MQTT_PORT : atoi(tmp);
#ifdef USE_MQTT_TLS
- Mqtt.mqtt_tls = Webserver->hasArg("b3"); // SetOption102 - Enable MQTT TLS
+ Settings.flag4.mqtt_tls = Webserver->hasArg(F("b3")); // SetOption102 - Enable MQTT TLS
#endif
- WebGetArg("mc", tmp, sizeof(tmp));
- SettingsUpdateText(SET_MQTT_CLIENT, (!strlen(tmp)) ? MQTT_CLIENT_ID : tmp);
- WebGetArg("mu", tmp, sizeof(tmp));
- SettingsUpdateText(SET_MQTT_USER, (!strlen(tmp)) ? MQTT_USER : (!strcmp(tmp,"0")) ? "" : tmp);
- WebGetArg("mp", tmp, sizeof(tmp));
+ WebGetArg(PSTR("mc"), tmp, sizeof(tmp));
+ SettingsUpdateText(SET_MQTT_CLIENT, (!strlen(tmp)) ? PSTR(MQTT_CLIENT_ID) : tmp);
+ WebGetArg(PSTR("mu"), tmp, sizeof(tmp));
+ SettingsUpdateText(SET_MQTT_USER, (!strlen(tmp)) ? PSTR(MQTT_USER) : (!strcmp(tmp,"0")) ? "" : tmp);
+ WebGetArg(PSTR("mp"), tmp, sizeof(tmp));
SettingsUpdateText(SET_MQTT_PWD, (!strlen(tmp)) ? "" : (!strcmp(tmp, D_ASTERISK_PWD)) ? SettingsText(SET_MQTT_PWD) : tmp);
AddLog_P(LOG_LEVEL_INFO, PSTR(D_LOG_MQTT D_CMND_MQTTHOST " %s, " D_CMND_MQTTPORT " %d, " D_CMND_MQTTCLIENT " %s, " D_CMND_MQTTUSER " %s, " D_CMND_TOPIC " %s, " D_CMND_FULLTOPIC " %s"),
SettingsText(SET_MQTT_HOST), Settings.mqtt_port, SettingsText(SET_MQTT_CLIENT), SettingsText(SET_MQTT_USER), SettingsText(SET_MQTT_TOPIC), SettingsText(SET_MQTT_FULLTOPIC));
@@ -1353,7 +1373,7 @@ bool Xdrv02(uint8_t function)
break;
#endif // USE_WEBSERVER
case FUNC_COMMAND:
- result = DecodeCommand(kMqttCommands, MqttCommand);
+ result = DecodeCommand(kMqttCommands, MqttCommand, kMqttSynonyms);
break;
}
}
diff --git a/tasmota/xdrv_03_energy.ino b/tasmota/xdrv_03_energy.ino
index f57431203..f253838aa 100644
--- a/tasmota/xdrv_03_energy.ino
+++ b/tasmota/xdrv_03_energy.ino
@@ -225,9 +225,7 @@ void EnergyUpdateToday(void)
void EnergyUpdateTotal(float value, bool kwh)
{
-// char energy_total_chr[FLOATSZ];
-// dtostrfd(value, 4, energy_total_chr);
-// AddLog_P(LOG_LEVEL_DEBUG, PSTR("NRG: Energy Total %s %sWh"), energy_total_chr, (kwh) ? "k" : "");
+// AddLog(LOG_LEVEL_DEBUG, PSTR("NRG: Energy Total %4_f %sWh"), &value, (kwh) ? "k" : "");
uint32_t multiplier = (kwh) ? 100000 : 100; // kWh or Wh to deca milli Wh
@@ -244,7 +242,7 @@ void EnergyUpdateTotal(float value, bool kwh)
Settings.energy_kWhtotal = RtcSettings.energy_kWhtotal;
Energy.total = (float)(RtcSettings.energy_kWhtotal + Energy.kWhtoday_offset + Energy.kWhtoday) / 100000;
Settings.energy_kWhtotal_time = (!Energy.kWhtoday_offset) ? LocalTime() : Midnight();
-// AddLog_P(LOG_LEVEL_DEBUG, PSTR("NRG: Energy Total updated with hardware value"));
+// AddLog(LOG_LEVEL_DEBUG, PSTR("NRG: Energy Total updated with hardware value"));
}
EnergyUpdateToday();
}
@@ -338,7 +336,7 @@ void EnergyMarginCheck(void)
for (uint32_t phase = 0; phase < Energy.phase_count; phase++) {
uint16_t active_power = (uint16_t)(Energy.active_power[phase]);
-// AddLog_P(LOG_LEVEL_DEBUG, PSTR("NRG: APower %d, HPower0 %d, HPower1 %d, HPower2 %d"), active_power, Energy.power_history[phase][0], Energy.power_history[phase][1], Energy.power_history[phase][2]);
+// AddLog(LOG_LEVEL_DEBUG, PSTR("NRG: APower %d, HPower0 %d, HPower1 %d, HPower2 %d"), active_power, Energy.power_history[phase][0], Energy.power_history[phase][1], Energy.power_history[phase][2]);
if (Settings.energy_power_delta[phase]) {
power_diff[phase] = active_power - Energy.power_history[phase][0];
@@ -476,9 +474,7 @@ void EnergyMarginCheck(void)
}
else if ((1 == Energy.max_energy_state ) && (energy_daily_u >= Settings.energy_max_energy)) {
Energy.max_energy_state = 2;
- char stemp[FLOATSZ];
- dtostrfd(Energy.daily, 3, stemp);
- ResponseTime_P(PSTR(",\"" D_JSON_MAXENERGYREACHED "\":%s}"), stemp);
+ ResponseTime_P(PSTR(",\"" D_JSON_MAXENERGYREACHED "\":%3_f}"), &Energy.daily);
MqttPublishPrefixTopic_P(STAT, S_RSLT_WARNING);
EnergyMqttShow();
SetAllPower(POWER_ALL_OFF, SRC_MAXENERGY);
@@ -507,9 +503,7 @@ void EnergyEverySecond(void)
if (TasmotaGlobal.global_update) {
if (TasmotaGlobal.power && !isnan(TasmotaGlobal.temperature_celsius) && (TasmotaGlobal.temperature_celsius > (float)Settings.param[P_OVER_TEMP])) { // Device overtemp, turn off relays
- char temperature[33];
- dtostrfd(TasmotaGlobal.temperature_celsius, 1, temperature);
- AddLog_P(LOG_LEVEL_DEBUG, PSTR("NRG: GlobTemp %s"), temperature);
+ AddLog(LOG_LEVEL_DEBUG, PSTR("NRG: GlobTemp %1_f"), &TasmotaGlobal.temperature_celsius);
SetAllPower(POWER_ALL_OFF, SRC_OVERTEMP);
}
@@ -536,8 +530,8 @@ void EnergyEverySecond(void)
}
}
if (!data_valid) {
- Energy.start_energy = 0;
- AddLog_P(LOG_LEVEL_DEBUG, PSTR("NRG: Energy reset by " STR(ENERGY_WATCHDOG) " seconds invalid data"));
+ //Energy.start_energy = 0;
+ AddLog(LOG_LEVEL_DEBUG, PSTR("NRG: Energy reset by " STR(ENERGY_WATCHDOG) " seconds invalid data"));
XnrgCall(FUNC_ENERGY_RESET);
}
@@ -628,25 +622,21 @@ void CmndEnergyReset(void)
}
Energy.total = (float)(RtcSettings.energy_kWhtotal + Energy.kWhtoday_offset + Energy.kWhtoday) / 100000;
+ float energy_kWhyesterday = (float)Settings.energy_kWhyesterday / 100000;
+ float usage1_kWhtotal = (float)Settings.energy_usage.usage1_kWhtotal / 100000;
+ float usage2_kWhtotal = (float)Settings.energy_usage.usage2_kWhtotal / 100000;
+ float return1_kWhtotal = (float)Settings.energy_usage.return1_kWhtotal / 100000;
+ float return2_kWhtotal = (float)Settings.energy_usage.return2_kWhtotal / 100000;
- char energy_total_chr[FLOATSZ];
- dtostrfd(Energy.total, Settings.flag2.energy_resolution, energy_total_chr);
- char energy_daily_chr[FLOATSZ];
- dtostrfd(Energy.daily, Settings.flag2.energy_resolution, energy_daily_chr);
- char energy_yesterday_chr[FLOATSZ];
- dtostrfd((float)Settings.energy_kWhyesterday / 100000, Settings.flag2.energy_resolution, energy_yesterday_chr);
-
- char energy_usage1_chr[FLOATSZ];
- dtostrfd((float)Settings.energy_usage.usage1_kWhtotal / 100000, Settings.flag2.energy_resolution, energy_usage1_chr);
- char energy_usage2_chr[FLOATSZ];
- dtostrfd((float)Settings.energy_usage.usage2_kWhtotal / 100000, Settings.flag2.energy_resolution, energy_usage2_chr);
- char energy_return1_chr[FLOATSZ];
- dtostrfd((float)Settings.energy_usage.return1_kWhtotal / 100000, Settings.flag2.energy_resolution, energy_return1_chr);
- char energy_return2_chr[FLOATSZ];
- dtostrfd((float)Settings.energy_usage.return2_kWhtotal / 100000, Settings.flag2.energy_resolution, energy_return2_chr);
-
- Response_P(PSTR("{\"%s\":{\"" D_JSON_TOTAL "\":%s,\"" D_JSON_YESTERDAY "\":%s,\"" D_JSON_TODAY "\":%s,\"" D_JSON_USAGE "\":[%s,%s],\"" D_JSON_EXPORT "\":[%s,%s]}}"),
- XdrvMailbox.command, energy_total_chr, energy_yesterday_chr, energy_daily_chr, energy_usage1_chr, energy_usage2_chr, energy_return1_chr, energy_return2_chr);
+ Response_P(PSTR("{\"%s\":{\"" D_JSON_TOTAL "\":%*_f,\"" D_JSON_YESTERDAY "\":%*_f,\"" D_JSON_TODAY "\":%*_f,\"" D_JSON_USAGE "\":[%*_f,%*_f],\"" D_JSON_EXPORT "\":[%*_f,%*_f]}}"),
+ XdrvMailbox.command,
+ Settings.flag2.energy_resolution, &Energy.total,
+ Settings.flag2.energy_resolution, &energy_kWhyesterday,
+ Settings.flag2.energy_resolution, &Energy.daily,
+ Settings.flag2.energy_resolution, &usage1_kWhtotal,
+ Settings.flag2.energy_resolution, &usage2_kWhtotal,
+ Settings.flag2.energy_resolution, &return1_kWhtotal,
+ Settings.flag2.energy_resolution, &return2_kWhtotal);
}
void CmndTariff(void)
diff --git a/tasmota/xdrv_04_light.ino b/tasmota/xdrv_04_light.ino
index 90d94ffb7..bdc334d0e 100644
--- a/tasmota/xdrv_04_light.ino
+++ b/tasmota/xdrv_04_light.ino
@@ -129,6 +129,10 @@ enum LightSchemes { LS_POWER, LS_WAKEUP, LS_CYCLEUP, LS_CYCLEDN, LS_RANDOM, LS_M
const uint8_t LIGHT_COLOR_SIZE = 25; // Char array scolor size
const char kLightCommands[] PROGMEM = "|" // No prefix
+ // SetOptions synonyms
+ D_SO_CHANNELREMAP "|" D_SO_MULTIPWM "|" D_SO_ALEXACTRANGE "|" D_SO_POWERONFADE "|" D_SO_PWMCT "|"
+ D_SO_WHITEBLEND "|" D_SO_VIRTUALCT "|"
+ // Other commands
D_CMND_COLOR "|" D_CMND_COLORTEMPERATURE "|" D_CMND_DIMMER "|" D_CMND_DIMMER_RANGE "|" D_CMND_DIMMER_STEP "|" D_CMND_LEDTABLE "|" D_CMND_FADE "|"
D_CMND_RGBWWTABLE "|" D_CMND_SCHEME "|" D_CMND_SPEED "|" D_CMND_WAKEUP "|" D_CMND_WAKEUPDURATION "|"
D_CMND_WHITE "|" D_CMND_CHANNEL "|" D_CMND_HSBCOLOR
@@ -144,6 +148,11 @@ const char kLightCommands[] PROGMEM = "|" // No prefix
#endif // USE_DGR_LIGHT_SEQUENCE
"|UNDOCA" ;
+SO_SYNONYMS(kLightSynonyms,
+ 37, 68, 82, 91, 92,
+ 105, 106,
+);
+
void (* const LightCommand[])(void) PROGMEM = {
&CmndColor, &CmndColorTemperature, &CmndDimmer, &CmndDimmerRange, &CmndDimmerStep, &CmndLedTable, &CmndFade,
&CmndRgbwwTable, &CmndScheme, &CmndSpeed, &CmndWakeup, &CmndWakeupDuration,
@@ -242,6 +251,10 @@ struct LIGHT {
uint16_t fade_end_10[LST_MAX]; // 10 bits resolution target channel values
uint16_t fade_duration = 0; // duration of fade in milliseconds
uint32_t fade_start = 0; // fade start time in milliseconds, compared to millis()
+ bool fade_once_enabled = false; // override fade a single time
+ bool fade_once_value = false; // override fade a single time
+ bool speed_once_enabled = false; // override speed a single time
+ uint8_t speed_once_value = 0; // override speed a single time
uint16_t pwm_min = 0; // minimum value for PWM, from DimmerRange, 0..1023
uint16_t pwm_max = 1023; // maxumum value for PWM, from DimmerRange, 0..1023
@@ -324,7 +337,7 @@ class LightStateClass {
public:
LightStateClass() {
- //AddLog_P(LOG_LEVEL_DEBUG_MORE, "LightStateClass::Constructor RGB raw (%d %d %d) HS (%d %d) bri (%d)", _r, _g, _b, _hue, _sat, _bri);
+ //AddLog(LOG_LEVEL_DEBUG_MORE, "LightStateClass::Constructor RGB raw (%d %d %d) HS (%d %d) bri (%d)", _r, _g, _b, _hue, _sat, _bri);
}
void setSubType(uint8_t sub_type) {
@@ -371,7 +384,7 @@ class LightStateClass {
if (0 == _briCT) { _briCT = maxbri; }
}
#ifdef DEBUG_LIGHT
- AddLog_P(LOG_LEVEL_DEBUG_MORE, "LightStateClass::setColorMode prev_cm (%d) req_cm (%d) new_cm (%d)", prev_cm, cm, _color_mode);
+ AddLog(LOG_LEVEL_DEBUG_MORE, "LightStateClass::setColorMode prev_cm (%d) req_cm (%d) new_cm (%d)", prev_cm, cm, _color_mode);
#endif
return prev_cm;
}
@@ -484,7 +497,7 @@ class LightStateClass {
setBriRGB(_color_mode & LCM_RGB ? bri : 0);
setBriCT(_color_mode & LCM_CT ? bri : 0);
#ifdef DEBUG_LIGHT
- AddLog_P(LOG_LEVEL_DEBUG_MORE, "LightStateClass::setBri RGB raw (%d %d %d) HS (%d %d) bri (%d)", _r, _g, _b, _hue, _sat, _briRGB);
+ AddLog(LOG_LEVEL_DEBUG_MORE, "LightStateClass::setBri RGB raw (%d %d %d) HS (%d %d) bri (%d)", _r, _g, _b, _hue, _sat, _briRGB);
#endif
}
@@ -530,7 +543,7 @@ class LightStateClass {
addCTMode();
}
#ifdef DEBUG_LIGHT
- AddLog_P(LOG_LEVEL_DEBUG_MORE, "LightStateClass::setCT RGB raw (%d %d %d) HS (%d %d) briRGB (%d) briCT (%d) CT (%d)", _r, _g, _b, _hue, _sat, _briRGB, _briCT, _ct);
+ AddLog(LOG_LEVEL_DEBUG_MORE, "LightStateClass::setCT RGB raw (%d %d %d) HS (%d %d) briRGB (%d) briCT (%d) CT (%d)", _r, _g, _b, _hue, _sat, _briRGB, _briCT, _ct);
#endif
}
@@ -570,7 +583,7 @@ class LightStateClass {
if (_color_mode & LCM_CT) { _briCT = free_range ? max : (sum > 255 ? 255 : sum); }
}
#ifdef DEBUG_LIGHT
- AddLog_P(LOG_LEVEL_DEBUG_MORE, "LightStateClass::setCW CW (%d %d) CT (%d) briCT (%d)", c, w, _ct, _briCT);
+ AddLog(LOG_LEVEL_DEBUG_MORE, "LightStateClass::setCW CW (%d %d) CT (%d) briCT (%d)", c, w, _ct, _briCT);
#endif
}
@@ -579,7 +592,7 @@ class LightStateClass {
uint16_t hue;
uint8_t sat;
#ifdef DEBUG_LIGHT
- AddLog_P(LOG_LEVEL_DEBUG_MORE, "LightStateClass::setRGB RGB input (%d %d %d)", r, g, b);
+ AddLog(LOG_LEVEL_DEBUG_MORE, "LightStateClass::setRGB RGB input (%d %d %d)", r, g, b);
#endif
uint32_t max = (r > g && r > b) ? r : (g > b) ? g : b; // 0..255
@@ -607,7 +620,7 @@ class LightStateClass {
_hue = hue;
_sat = sat;
#ifdef DEBUG_LIGHT
- AddLog_P(LOG_LEVEL_DEBUG_MORE, "LightStateClass::setRGB RGB raw (%d %d %d) HS (%d %d) bri (%d)", _r, _g, _b, _hue, _sat, _briRGB);
+ AddLog(LOG_LEVEL_DEBUG_MORE, "LightStateClass::setRGB RGB raw (%d %d %d) HS (%d %d) bri (%d)", _r, _g, _b, _hue, _sat, _briRGB);
#endif
return max;
}
@@ -622,8 +635,8 @@ class LightStateClass {
_sat = sat;
addRGBMode();
#ifdef DEBUG_LIGHT
- AddLog_P(LOG_LEVEL_DEBUG_MORE, "LightStateClass::setHS HS (%d %d) rgb (%d %d %d)", hue, sat, r, g, b);
- AddLog_P(LOG_LEVEL_DEBUG_MORE, "LightStateClass::setHS RGB raw (%d %d %d) HS (%d %d) bri (%d)", _r, _g, _b, _hue, _sat, _briRGB);
+ AddLog(LOG_LEVEL_DEBUG_MORE, "LightStateClass::setHS HS (%d %d) rgb (%d %d %d)", hue, sat, r, g, b);
+ AddLog(LOG_LEVEL_DEBUG_MORE, "LightStateClass::setHS RGB raw (%d %d %d) HS (%d %d) bri (%d)", _r, _g, _b, _hue, _sat, _briRGB);
#endif
}
@@ -644,10 +657,10 @@ class LightStateClass {
setRGB(channels[0], channels[1], channels[2]);
setCW(channels[3], channels[4], true); // free range for WC and WW
#ifdef DEBUG_LIGHT
- AddLog_P(LOG_LEVEL_DEBUG_MORE, "LightStateClass::setChannels (%d %d %d %d %d)",
+ AddLog(LOG_LEVEL_DEBUG_MORE, "LightStateClass::setChannels (%d %d %d %d %d)",
channels[0], channels[1], channels[2], channels[3], channels[4]);
- AddLog_P(LOG_LEVEL_DEBUG_MORE, "LightStateClass::setChannels CT (%d) briRGB (%d) briCT (%d)", _ct, _briRGB, _briCT);
- AddLog_P(LOG_LEVEL_DEBUG_MORE, "LightStateClass::setChannels Actuals (%d %d %d %d %d)",
+ AddLog(LOG_LEVEL_DEBUG_MORE, "LightStateClass::setChannels CT (%d) briRGB (%d) briCT (%d)", _ct, _briRGB, _briCT);
+ AddLog(LOG_LEVEL_DEBUG_MORE, "LightStateClass::setChannels Actuals (%d %d %d %d %d)",
_r, _g, _b, _wc, _ww);
#endif
}
@@ -704,9 +717,9 @@ public:
void debugLogs() {
uint8_t r,g,b,c,w;
_state->getActualRGBCW(&r,&g,&b,&c,&w);
- AddLog_P(LOG_LEVEL_DEBUG_MORE, "LightControllerClass::debugLogs rgb (%d %d %d) cw (%d %d)",
+ AddLog(LOG_LEVEL_DEBUG_MORE, "LightControllerClass::debugLogs rgb (%d %d %d) cw (%d %d)",
r, g, b, c, w);
- AddLog_P(LOG_LEVEL_DEBUG_MORE, "LightControllerClass::debugLogs lightCurrent (%d %d %d %d %d)",
+ AddLog(LOG_LEVEL_DEBUG_MORE, "LightControllerClass::debugLogs lightCurrent (%d %d %d %d %d)",
Light.current_color[0], Light.current_color[1], Light.current_color[2],
Light.current_color[3], Light.current_color[4]);
}
@@ -714,10 +727,10 @@ public:
void loadSettings() {
#ifdef DEBUG_LIGHT
- AddLog_P(LOG_LEVEL_DEBUG_MORE, "LightControllerClass::loadSettings Settings.light_color (%d %d %d %d %d - %d)",
+ AddLog(LOG_LEVEL_DEBUG_MORE, "LightControllerClass::loadSettings Settings.light_color (%d %d %d %d %d - %d)",
Settings.light_color[0], Settings.light_color[1], Settings.light_color[2],
Settings.light_color[3], Settings.light_color[4], Settings.light_dimmer);
- AddLog_P(LOG_LEVEL_DEBUG_MORE, "LightControllerClass::loadSettings light_type/sub (%d %d)",
+ AddLog(LOG_LEVEL_DEBUG_MORE, "LightControllerClass::loadSettings light_type/sub (%d %d)",
TasmotaGlobal.light_type, Light.subtype);
#endif
if (_pwm_multi_channels) {
@@ -892,7 +905,7 @@ public:
}
}
#ifdef DEBUG_LIGHT
- AddLog_P(LOG_LEVEL_DEBUG_MORE, "LightControllerClass::saveSettings Settings.light_color (%d %d %d %d %d - %d)",
+ AddLog(LOG_LEVEL_DEBUG_MORE, "LightControllerClass::saveSettings Settings.light_color (%d %d %d %d %d - %d)",
Settings.light_color[0], Settings.light_color[1], Settings.light_color[2],
Settings.light_color[3], Settings.light_color[4], Settings.light_dimmer);
#endif
@@ -1074,7 +1087,7 @@ void LightCalcPWMRange(void) {
Light.pwm_min = pwm_min;
Light.pwm_max = pwm_max;
- //AddLog_P(LOG_LEVEL_DEBUG_MORE, PSTR("LightCalcPWMRange %d %d - %d %d"), Settings.dimmer_hw_min, Settings.dimmer_hw_max, Light.pwm_min, Light.pwm_max);
+ //AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("LightCalcPWMRange %d %d - %d %d"), Settings.dimmer_hw_min, Settings.dimmer_hw_max, Light.pwm_min, Light.pwm_max);
}
void LightInit(void)
@@ -1189,7 +1202,7 @@ void LightUpdateColorMapping(void)
Light.color_remap[4] = tmp[1-param];
Light.update = true;
- //AddLog_P(LOG_LEVEL_DEBUG, PSTR("%d colors: %d %d %d %d %d") ,Settings.param[P_RGB_REMAP], Light.color_remap[0],Light.color_remap[1],Light.color_remap[2],Light.color_remap[3],Light.color_remap[4]);
+ //AddLog(LOG_LEVEL_DEBUG, PSTR("%d colors: %d %d %d %d %d") ,Settings.param[P_RGB_REMAP], Light.color_remap[0],Light.color_remap[1],Light.color_remap[2],Light.color_remap[3],Light.color_remap[4]);
}
uint8_t LightGetDimmer(uint8_t dimmer) {
@@ -1306,7 +1319,7 @@ void LightSetSignal(uint16_t lo, uint16_t hi, uint16_t value)
*/
if (Settings.flag.light_signal) { // SetOption18 - Pair light signal with CO2 sensor
uint16_t signal = changeUIntScale(value, lo, hi, 0, 255); // 0..255
-// AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "Light signal %d"), signal);
+// AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "Light signal %d"), signal);
light_controller.changeRGB(signal, 255 - signal, 0, true); // keep bri
Settings.light_scheme = 0;
if (0 == light_state.getBri()) {
@@ -1423,7 +1436,7 @@ void ResponseLightState(uint8_t append)
void LightPreparePower(power_t channels = 0xFFFFFFFF) { // 1 = only RGB, 2 = only CT, 3 = both RGB and CT
#ifdef DEBUG_LIGHT
- AddLog_P(LOG_LEVEL_DEBUG, "LightPreparePower power=%d Light.power=%d", TasmotaGlobal.power, Light.power);
+ AddLog(LOG_LEVEL_DEBUG, "LightPreparePower power=%d Light.power=%d", TasmotaGlobal.power, Light.power);
#endif
// If multi-channels, then we only switch off channels with a value of zero
if (Light.pwm_multi_channels) {
@@ -1486,7 +1499,7 @@ void LightPreparePower(power_t channels = 0xFFFFFFFF) { // 1 = only RGB, 2 =
}
#ifdef DEBUG_LIGHT
- AddLog_P(LOG_LEVEL_DEBUG, "LightPreparePower End power=%d Light.power=%d", TasmotaGlobal.power, Light.power);
+ AddLog(LOG_LEVEL_DEBUG, "LightPreparePower End power=%d Light.power=%d", TasmotaGlobal.power, Light.power);
#endif
Light.power = TasmotaGlobal.power >> (Light.device - 1); // reset next state, works also with unlinked RGB/CT
ResponseLightState(0);
@@ -1543,7 +1556,7 @@ void LightCycleColor(int8_t direction)
(Light.random > Light.wheel +128) ? 0 : 1; // Increment or Decrement and roll-over
Light.random = (Light.random & 0xFE) | my_dir;
-// AddLog_P(LOG_LEVEL_DEBUG, PSTR("LGT: random %d"), Light.random);
+// AddLog(LOG_LEVEL_DEBUG, PSTR("LGT: random %d"), Light.random);
}
// direction = (Light.random < Light.wheel) ? -1 : 1;
direction = (Light.random &0x01) ? 1 : -1;
@@ -1554,7 +1567,7 @@ void LightCycleColor(int8_t direction)
Light.wheel += direction;
uint16_t hue = changeUIntScale(Light.wheel, 0, 255, 0, 359); // Scale to hue to keep amount of steps low (max 255 instead of 359)
-// AddLog_P(LOG_LEVEL_DEBUG, PSTR("LGT: random %d, wheel %d, hue %d"), Light.random, Light.wheel, hue);
+// AddLog(LOG_LEVEL_DEBUG, PSTR("LGT: random %d, wheel %d, hue %d"), Light.random, Light.wheel, hue);
if (!Light.pwm_multi_channels) {
uint8_t sat;
@@ -1598,6 +1611,16 @@ void LightSetPower(void)
LightAnimate();
}
+bool LightGetFadeSetting(void) {
+ if (Light.fade_once_enabled) return Light.fade_once_value;
+ return Settings.light_fade;
+}
+
+uint8_t LightGetSpeedSetting(void) {
+ if (Light.speed_once_enabled) return Light.speed_once_value;
+ return Settings.light_speed;
+}
+
// On entry Light.new_color[5] contains the color to be displayed
// and Light.last_color[5] the color currently displayed
// Light.power tells which lights or channels (SetOption68) are on/off
@@ -1696,7 +1719,7 @@ void LightAnimate(void)
// Apply power modifiers to Light.new_color
LightApplyPower(Light.new_color, Light.power);
- // AddLog_P(LOG_LEVEL_INFO, PSTR("last_color (%02X%02X%02X%02X%02X) new_color (%02X%02X%02X%02X%02X) power %d"),
+ // AddLog(LOG_LEVEL_INFO, PSTR("last_color (%02X%02X%02X%02X%02X) new_color (%02X%02X%02X%02X%02X) power %d"),
// Light.last_color[0], Light.last_color[1], Light.last_color[2], Light.last_color[3], Light.last_color[4],
// Light.new_color[0], Light.new_color[1], Light.new_color[2], Light.new_color[3], Light.new_color[4],
// Light.power
@@ -1724,9 +1747,9 @@ void LightAnimate(void)
if (Light.pwm_multi_channels) {
calcGammaMultiChannels(cur_col_10);
} else {
- // AddLog_P(LOG_LEVEL_INFO, PSTR(">>> calcGammaBulbs In %03X,%03X,%03X,%03X,%03X"), cur_col_10[0], cur_col_10[1], cur_col_10[2], cur_col_10[3], cur_col_10[4]);
+ // AddLog(LOG_LEVEL_INFO, PSTR(">>> calcGammaBulbs In %03X,%03X,%03X,%03X,%03X"), cur_col_10[0], cur_col_10[1], cur_col_10[2], cur_col_10[3], cur_col_10[4]);
rgbwwtable_applied_white = calcGammaBulbs(cur_col_10); // true means that one PWM channel is used for CT
- // AddLog_P(LOG_LEVEL_INFO, PSTR(">>> calcGammaBulbs Out %03X,%03X,%03X,%03X,%03X"), cur_col_10[0], cur_col_10[1], cur_col_10[2], cur_col_10[3], cur_col_10[4]);
+ // AddLog(LOG_LEVEL_INFO, PSTR(">>> calcGammaBulbs Out %03X,%03X,%03X,%03X,%03X"), cur_col_10[0], cur_col_10[1], cur_col_10[2], cur_col_10[3], cur_col_10[4]);
}
// Apply RGBWWTable only if not Settings.flag4.white_blend_mode
@@ -1748,12 +1771,14 @@ void LightAnimate(void)
cur_col_10[i] = orig_col_10bits[Light.color_remap[i]];
}
- if (!Settings.light_fade || TasmotaGlobal.skip_light_fade || power_off || (!Light.fade_initialized)) { // no fade
+ if (!LightGetFadeSetting() || TasmotaGlobal.skip_light_fade || power_off || (!Light.fade_initialized)) { // no fade
// record the current value for a future Fade
memcpy(Light.fade_start_10, cur_col_10, sizeof(Light.fade_start_10));
// push the final values at 8 and 10 bits resolution to the PWMs
LightSetOutputs(cur_col_10);
Light.fade_initialized = true; // it is now ok to fade
+ Light.fade_once_enabled = false; // light has been set, reset fade_once_enabled
+ Light.speed_once_enabled = false; // light has been set, reset speed_once_enabled
} else { // fade on
if (Light.fade_running) {
// if fade is running, we take the curring value as the start for the next fade
@@ -1763,12 +1788,13 @@ void LightAnimate(void)
Light.fade_running = true;
Light.fade_duration = 0; // set the value to zero to force a recompute
Light.fade_start = 0;
+ Light.fade_once_enabled = false; // fade will be applied, reset fade_once_enabled
// Fade will applied immediately below
}
}
if (Light.fade_running) {
if (LightApplyFade()) {
- // AddLog_P(LOG_LEVEL_INFO, PSTR("LightApplyFade %d %d %d %d %d"),
+ // AddLog(LOG_LEVEL_INFO, PSTR("LightApplyFade %d %d %d %d %d"),
// Light.fade_cur_10[0], Light.fade_cur_10[1], Light.fade_cur_10[2], Light.fade_cur_10[3], Light.fade_cur_10[4]);
LightSetOutputs(Light.fade_cur_10);
@@ -1847,14 +1873,15 @@ bool LightApplyFade(void) { // did the value chanegd and needs to be applied
// compute the duration of the animation
// Note: Settings.light_speed is the number of half-seconds for a 100% fade,
// i.e. light_speed=1 means 1024 steps in 500ms
- Light.fade_duration = Settings.light_speed * 500;
+ Light.fade_duration = LightGetSpeedSetting() * 500;
+ Light.speed_once_enabled = false; // The once off speed value has been read, reset it
if (!Settings.flag5.fade_fixed_duration) {
Light.fade_duration = (distance * Light.fade_duration) / 1023; // time is proportional to distance, except with SO117
}
if (Settings.save_data) {
// Also postpone the save_data for the duration of the Fade (in seconds)
uint32_t delay_seconds = 1 + (Light.fade_duration + 999) / 1000; // add one more second
- // AddLog_P(LOG_LEVEL_INFO, PSTR("delay_seconds %d, save_data_counter %d"), delay_seconds, TasmotaGlobal.save_data_counter);
+ // AddLog(LOG_LEVEL_INFO, PSTR("delay_seconds %d, save_data_counter %d"), delay_seconds, TasmotaGlobal.save_data_counter);
if (TasmotaGlobal.save_data_counter < delay_seconds) {
TasmotaGlobal.save_data_counter = delay_seconds; // pospone
}
@@ -1904,7 +1931,7 @@ void LightApplyPower(uint8_t new_color[LST_MAX], power_t power) {
}
}
// #ifdef DEBUG_LIGHT
- // AddLog_P(LOG_LEVEL_DEBUG_MORE, "Animate>> Light.power=%d Light.new_color=[%d,%d,%d,%d,%d]",
+ // AddLog(LOG_LEVEL_DEBUG_MORE, "Animate>> Light.power=%d Light.new_color=[%d,%d,%d,%d,%d]",
// Light.power, Light.new_color[0], Light.new_color[1], Light.new_color[2],
// Light.new_color[3], Light.new_color[4]);
// #endif
@@ -1930,7 +1957,7 @@ void LightSetOutputs(const uint16_t *cur_col_10) {
if (TasmotaGlobal.light_type < LT_PWM6) { // only for direct PWM lights, not for Tuya, Armtronix...
for (uint32_t i = 0; i < (Light.subtype - Light.pwm_offset); i++) {
if (PinUsed(GPIO_PWM1, i)) {
- //AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_APPLICATION "Cur_Col%d 10 bits %d"), i, cur_col_10[i]);
+ //AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_APPLICATION "Cur_Col%d 10 bits %d"), i, cur_col_10[i]);
uint16_t cur_col = cur_col_10[i + Light.pwm_offset];
if (!isChannelCT(i)) { // if CT don't use pwm_min and pwm_max
cur_col = cur_col > 0 ? changeUIntScale(cur_col, 0, Settings.pwm_range, Light.pwm_min, Light.pwm_max) : 0; // shrink to the range of pwm_min..pwm_max
@@ -1942,7 +1969,7 @@ void LightSetOutputs(const uint16_t *cur_col_10) {
}
}
// char msg[24];
-// AddLog_P(LOG_LEVEL_DEBUG, PSTR("LGT: Channels %s"), ToHex_P((const unsigned char *)cur_col_10, 10, msg, sizeof(msg)));
+// AddLog(LOG_LEVEL_DEBUG, PSTR("LGT: Channels %s"), ToHex_P((const unsigned char *)cur_col_10, 10, msg, sizeof(msg)));
// Some devices need scaled RGB like Sonoff L1
uint32_t max = (cur_col_10[0] > cur_col_10[1] && cur_col_10[0] > cur_col_10[2]) ? cur_col_10[0] : (cur_col_10[1] > cur_col_10[2]) ? cur_col_10[1] : cur_col_10[2]; // 0..1023
@@ -1950,7 +1977,7 @@ void LightSetOutputs(const uint16_t *cur_col_10) {
for (uint32_t i = 0; i < 3; i++) {
scale_col[i] = (0 == max) ? 255 : changeUIntScale(cur_col_10[i], 0, max, 0, 255);
}
-// AddLog_P(LOG_LEVEL_DEBUG, PSTR("LGT: CurCol %03X %03X %03X, ScaleCol %02X %02X %02X, Max %02X"),
+// AddLog(LOG_LEVEL_DEBUG, PSTR("LGT: CurCol %03X %03X %03X, ScaleCol %02X %02X %02X, Max %02X"),
// cur_col_10[0], cur_col_10[1], cur_col_10[2], scale_col[0], scale_col[1], scale_col[2], max);
uint8_t cur_col[LST_MAX];
@@ -2110,10 +2137,10 @@ bool calcGammaBulbs(uint16_t cur_col_10[5]) {
calcGammaBulb5Channels_8(*(pivot+1), to10);
vct_pivot_t *pivot1 = pivot + 1;
- // AddLog_P(LOG_LEVEL_INFO, PSTR("+++ from_ct %d, to_ct %d [%03X,%03X,%03X,%03X,%03X] - [%03X,%03X,%03X,%03X,%03X]"),
+ // AddLog(LOG_LEVEL_INFO, PSTR("+++ from_ct %d, to_ct %d [%03X,%03X,%03X,%03X,%03X] - [%03X,%03X,%03X,%03X,%03X]"),
// *from_ct, *(from_ct+1), (*pivot)[0], (*pivot)[1], (*pivot)[2], (*pivot)[3], (*pivot)[4],
// (*pivot1)[0], (*pivot1)[1], (*pivot1)[2], (*pivot1)[3], (*pivot1)[4]);
- // AddLog_P(LOG_LEVEL_INFO, PSTR("+++ from10 [%03X,%03X,%03X,%03X,%03X] - to 10 [%03X,%03X,%03X,%03X,%03X]"),
+ // AddLog(LOG_LEVEL_INFO, PSTR("+++ from10 [%03X,%03X,%03X,%03X,%03X] - to 10 [%03X,%03X,%03X,%03X,%03X]"),
// from10[0],from10[0],from10[0],from10[0],from10[4],
// to10[0],to10[0],to10[0],to10[0],to10[4]);
@@ -2625,7 +2652,7 @@ void CmndWakeup(void)
Light.wakeup_active = 3;
Settings.light_scheme = LS_WAKEUP;
LightPowerOn();
- ResponseCmndChar(D_JSON_STARTED);
+ ResponseCmndChar(PSTR(D_JSON_STARTED));
}
void CmndColorTemperature(void)
@@ -2825,14 +2852,23 @@ void CmndFade(void)
#ifdef USE_DEVICE_GROUPS
if (XdrvMailbox.payload >= 0 && XdrvMailbox.payload <= 2) SendDeviceGroupMessage(Light.device_group_index, DGR_MSGTYP_UPDATE, DGR_ITEM_LIGHT_FADE, Settings.light_fade);
#endif // USE_DEVICE_GROUPS
-#ifdef USE_LIGHT
if (!Settings.light_fade) { Light.fade_running = false; }
-#endif // USE_LIGHT
ResponseCmndStateText(Settings.light_fade);
}
void CmndSpeed(void)
{
+ if (XdrvMailbox.index == 2) {
+ if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 40)) {
+ Light.fade_once_enabled = true;
+ Light.fade_once_value = XdrvMailbox.payload > 0;
+ Light.speed_once_enabled = true;
+ Light.speed_once_value = XdrvMailbox.payload;
+ if (!Light.fade_once_value) { Light.fade_running = false; }
+ }
+ return;
+ }
+
// Speed 1 - Fast
// Speed 40 - Very slow
// Speed + - Increment Speed
@@ -3078,7 +3114,7 @@ bool Xdrv04(uint8_t function)
LightSetPower();
break;
case FUNC_COMMAND:
- result = DecodeCommand(kLightCommands, LightCommand);
+ result = DecodeCommand(kLightCommands, LightCommand, kLightSynonyms);
if (!result) {
result = XlgtCall(FUNC_COMMAND);
}
diff --git a/tasmota/xdrv_04_light_utils.ino b/tasmota/xdrv_04_light_utils.ino
index 8ab379d62..90c2f1dd3 100644
--- a/tasmota/xdrv_04_light_utils.ino
+++ b/tasmota/xdrv_04_light_utils.ino
@@ -132,7 +132,7 @@ void RgbToHsb(uint8_t ir, uint8_t ig, uint8_t ib, uint16_t *r_hue, uint8_t *r_sa
if (r_hue) *r_hue = hue;
if (r_sat) *r_sat = sat;
if (r_bri) *r_bri = bri;
- //AddLog_P(LOG_LEVEL_DEBUG_MORE, "RgbToHsb rgb (%d %d %d) hsb (%d %d %d)", r, g, b, hue, sat, bri);
+ //AddLog(LOG_LEVEL_DEBUG_MORE, "RgbToHsb rgb (%d %d %d) hsb (%d %d %d)", r, g, b, hue, sat, bri);
}
void HsToRgb(uint16_t hue, uint8_t sat, uint8_t *r_r, uint8_t *r_g, uint8_t *r_b) {
diff --git a/tasmota/xdrv_05_irremote.ino b/tasmota/xdrv_05_irremote.ino
index 44304c550..0cacb2161 100644
--- a/tasmota/xdrv_05_irremote.ino
+++ b/tasmota/xdrv_05_irremote.ino
@@ -76,6 +76,44 @@ const char kIrRemoteCommands[] PROGMEM = "|" D_CMND_IRSEND ;
void (* const IrRemoteCommand[])(void) PROGMEM = {
&CmndIrSend };
+char* ulltoa(unsigned long long value, char *str, int radix)
+{
+ char digits[64];
+ char *dst = str;
+ int i = 0;
+
+// if (radix < 2 || radix > 36) { radix = 10; }
+
+ do {
+ int n = value % radix;
+ digits[i++] = (n < 10) ? (char)n+'0' : (char)n-10+'A';
+ value /= radix;
+ } while (value != 0);
+
+ while (i > 0) { *dst++ = digits[--i]; }
+
+ *dst = 0;
+ return str;
+}
+
+char* Uint64toHex(uint64_t value, char *str, uint16_t bits)
+{
+ ulltoa(value, str, 16); // Get 64bit value
+
+ int fill = 8;
+ if ((bits > 3) && (bits < 65)) {
+ fill = bits / 4; // Max 16
+ if (bits % 4) { fill++; }
+ }
+ int len = strlen(str);
+ fill -= len;
+ if (fill > 0) {
+ memmove(str + fill, str, len +1);
+ memset(str, '0', fill);
+ }
+ return str;
+}
+
/*********************************************************************************************\
* Class used to make a compact IR Raw format.
*
@@ -161,7 +199,7 @@ void IrReceiveInit(void)
irrecv->setUnknownThreshold(Settings.param[P_IR_UNKNOW_THRESHOLD]);
irrecv->enableIRIn(); // Start the receiver
- // AddLog_P(LOG_LEVEL_DEBUG, PSTR("IrReceive initialized"));
+ // AddLog(LOG_LEVEL_DEBUG, PSTR("IrReceive initialized"));
}
void IrReceiveCheck(void)
@@ -190,7 +228,7 @@ void IrReceiveCheck(void)
Uint64toHex(results.value, hvalue, 32); // UNKNOWN is always 32 bits hash
}
- AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_IRR "RawLen %d, Overflow %d, Bits %d, Value 0x%s, Decode %d"),
+ AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_IRR "RawLen %d, Overflow %d, Bits %d, Value 0x%s, Decode %d"),
results.rawlen, results.overflow, results.bits, hvalue, results.decode_type);
unsigned long now = millis();
@@ -291,10 +329,10 @@ uint32_t IrRemoteCmndIrSendJson(void)
char protocol_text[20];
int protocol_code = GetCommandCode(protocol_text, sizeof(protocol_text), protocol, kIrRemoteProtocols);
- char dvalue[64];
- char hvalue[20];
- AddLog_P(LOG_LEVEL_DEBUG, PSTR("IRS: protocol_text %s, protocol %s, bits %d, data %s (0x%s), repeat %d, protocol_code %d"),
- protocol_text, protocol, bits, ulltoa(data, dvalue, 10), Uint64toHex(data, hvalue, bits), repeat, protocol_code);
+ // char dvalue[64];
+ // char hvalue[20];
+ // AddLog(LOG_LEVEL_DEBUG, PSTR("IRS: protocol_text %s, protocol %s, bits %d, data %s (0x%s), repeat %d, protocol_code %d"),
+ // protocol_text, protocol, bits, ulltoa(data, dvalue, 10), Uint64toHex(data, hvalue, bits), repeat, protocol_code);
#ifdef USE_IR_RECEIVE
if (!IR_RCV_WHILE_SENDING && (irrecv != nullptr)) { irrecv->disableIRIn(); }
diff --git a/tasmota/xdrv_05_irremote_full.ino b/tasmota/xdrv_05_irremote_full.ino
index bed0e8199..c88c19d8a 100644
--- a/tasmota/xdrv_05_irremote_full.ino
+++ b/tasmota/xdrv_05_irremote_full.ino
@@ -235,57 +235,36 @@ String sendACJsonState(const stdAc::state_t &state) {
return payload;
}
-String sendIRJsonState(const struct decode_results &results) {
- String json("{");
- json += "\"" D_JSON_IR_PROTOCOL "\":\"";
- json += typeToString(results.decode_type);
- json += "\",\"" D_JSON_IR_BITS "\":";
- json += results.bits;
+void sendIRJsonState(const struct decode_results &results) {
+ Response_P(PSTR("\"" D_JSON_IR_PROTOCOL "\":\"%s\",\"" D_JSON_IR_BITS "\":%d"),
+ typeToString(results.decode_type).c_str(),
+ results.bits);
if (hasACState(results.decode_type)) {
- json += ",\"" D_JSON_IR_DATA "\":\"";
- json += resultToHexidecimal(&results);
- json += "\"";
+ ResponseAppend_P(PSTR(",\"" D_JSON_IR_DATA "\":\"%s\""),
+ resultToHexidecimal(&results).c_str());
} else {
- if (UNKNOWN != results.decode_type) {
- json += ",\"" D_JSON_IR_DATA "\":";
- } else {
- json += ",\"" D_JSON_IR_HASH "\":";
- }
+ ResponseAppend_P(PSTR(",\"%s\":"), UNKNOWN != results.decode_type ? PSTR(D_JSON_IR_DATA) : PSTR(D_JSON_IR_HASH));
if (Settings.flag.ir_receive_decimal) { // SetOption29 - IR receive data format
- char svalue[32];
- ulltoa(results.value, svalue, 10);
- json += svalue;
+ ResponseAppend_P(PSTR("%u"), (uint32_t) results.value);
} else {
- char hvalue[64];
if (UNKNOWN != results.decode_type) {
- Uint64toHex(results.value, hvalue, results.bits); // Get 64bit value as hex 0x00123456
- json += "\"0x";
- json += hvalue;
- json += "\",\"" D_JSON_IR_DATALSB "\":\"0x";
- Uint64toHex(reverseBitsInBytes64(results.value), hvalue, results.bits); // Get 64bit value as hex 0x00123456, LSB
- json += hvalue;
- json += "\"";
+ uint64_t reverse = reverseBitsInBytes64(results.value);
+ ResponseAppend_P(PSTR("\"0x%_X\",\"" D_JSON_IR_DATALSB "\":\"0x%_X\""),
+ &results.value, &reverse);
} else { // UNKNOWN
- Uint64toHex(results.value, hvalue, 32); // Unknown is always 32 bits
- json += "\"0x";
- json += hvalue;
- json += "\"";
+ ResponseAppend_P(PSTR("\"0x08X\""), (uint32_t) results.value); // Unknown is always 32 bits
}
}
}
- json += ",\"" D_JSON_IR_REPEAT "\":";
- json += results.repeat;
+ ResponseAppend_P(PSTR(",\"" D_JSON_IR_REPEAT "\":%d"), results.repeat);
stdAc::state_t new_state;
if (IRAcUtils::decodeToState(&results, &new_state, irhvac_stateful && irac_prev_state.protocol == results.decode_type ? &irac_prev_state : nullptr)) {
// we have a decoded state
- json += ",\"" D_CMND_IRHVAC "\":";
- json += sendACJsonState(new_state);
+ ResponseAppend_P(PSTR(",\"" D_CMND_IRHVAC "\":%s"), sendACJsonState(new_state).c_str());
irac_prev_state = new_state; // store for next time
}
-
- return json;
}
void IrReceiveCheck(void)
@@ -298,7 +277,8 @@ void IrReceiveCheck(void)
// if ((now - ir_lasttime > IR_TIME_AVOID_DUPLICATE) && (UNKNOWN != results.decode_type) && (results.bits > 0)) {
if (now - ir_lasttime > IR_TIME_AVOID_DUPLICATE) {
ir_lasttime = now;
- Response_P(PSTR("{\"" D_JSON_IRRECEIVED "\":%s"), sendIRJsonState(results).c_str());
+ Response_P(PSTR("{\"" D_JSON_IRRECEIVED "\":{"));
+ sendIRJsonState(results);
IRRawTable raw_table;
bool prev_number = false; // was the previous value a number, meaning we may need a comma prefix
@@ -446,7 +426,7 @@ uint32_t IrRemoteCmndIrHvacJson(void)
if (val = root[PSTR(D_JSON_IRHVAC_SWINGV)]) { state.swingv = IRac::strToSwingV(val.getStr()); }
if (val = root[PSTR(D_JSON_IRHVAC_SWINGH)]) { state.swingh = IRac::strToSwingH(val.getStr()); }
state.degrees = root.getFloat(PSTR(D_JSON_IRHVAC_TEMP), state.degrees);
- // AddLog_P(LOG_LEVEL_DEBUG, PSTR("model %d, mode %d, fanspeed %d, swingv %d, swingh %d"),
+ // AddLog(LOG_LEVEL_DEBUG, PSTR("model %d, mode %d, fanspeed %d, swingv %d, swingh %d"),
// state.model, state.mode, state.fanspeed, state.swingv, state.swingh);
// if and how we should handle the state for IRremote
@@ -532,7 +512,7 @@ uint32_t IrRemoteCmndIrSendJson(void)
char dvalue[32];
char hvalue[32];
- // AddLog_P(LOG_LEVEL_DEBUG, PSTR("IRS: protocol %d, bits %d, data 0x%s (%s), repeat %d"),
+ // AddLog(LOG_LEVEL_DEBUG, PSTR("IRS: protocol %d, bits %d, data 0x%s (%s), repeat %d"),
// protocol, bits, ulltoa(data, dvalue, 10), Uint64toHex(data, hvalue, bits), repeat);
if (!IR_RCV_WHILE_SENDING && (irrecv != nullptr)) { irrecv->disableIRIn(); }
@@ -617,7 +597,7 @@ uint32_t IrRemoteSendRawFormatted(char ** pp, uint32_t count, uint32_t repeat) {
}
if (!IR_RCV_WHILE_SENDING && (irrecv != nullptr)) { irrecv->disableIRIn(); }
for (uint32_t r = 0; r <= repeat; r++) {
- // AddLog_P(LOG_LEVEL_DEBUG, PSTR("sendRaw count=%d, space=%d, mark=%d, freq=%d"), count, space, mark, freq);
+ // AddLog(LOG_LEVEL_DEBUG, PSTR("sendRaw count=%d, space=%d, mark=%d, freq=%d"), count, space, mark, freq);
irsend->sendRaw(raw_array, i, freq);
if (r < repeat) { // if it's not the last message
irsend->space(40000); // since we don't know the inter-message gap, place an arbitrary 40ms gap
@@ -644,7 +624,7 @@ uint32_t IrRemoteSendRawFormatted(char ** pp, uint32_t count, uint32_t repeat) {
raw_array[i++] = parm[2]; // Trailing mark
if (!IR_RCV_WHILE_SENDING && (irrecv != nullptr)) { irrecv->disableIRIn(); }
for (uint32_t r = 0; r <= repeat; r++) {
- // AddLog_P(LOG_LEVEL_DEBUG, PSTR("sendRaw %d %d %d %d %d %d"), raw_array[0], raw_array[1], raw_array[2], raw_array[3], raw_array[4], raw_array[5]);
+ // AddLog(LOG_LEVEL_DEBUG, PSTR("sendRaw %d %d %d %d %d %d"), raw_array[0], raw_array[1], raw_array[2], raw_array[3], raw_array[4], raw_array[5]);
irsend->sendRaw(raw_array, i, freq);
if (r < repeat) { // if it's not the last message
irsend->space(inter_message); // since we don't know the inter-message gap, place an arbitrary 40ms gap
@@ -710,13 +690,13 @@ uint32_t IrRemoteSendRawStandard(char ** pp, uint16_t freq, uint32_t count, uint
} else {
count++;
}
- // AddLog_P(LOG_LEVEL_DEBUG, PSTR("IrRemoteSendRawStandard: count_1 = %d"), count);
+ // AddLog(LOG_LEVEL_DEBUG, PSTR("IrRemoteSendRawStandard: count_1 = %d"), count);
arr = (uint16_t*) malloc(count * sizeof(uint16_t));
if (nullptr == arr) { return IE_MEMORY; }
count = IrRemoteParseRawCompact(*pp, arr, count);
- // AddLog_P(LOG_LEVEL_DEBUG, PSTR("IrRemoteSendRawStandard: count_2 = %d"), count);
- // AddLog_P(LOG_LEVEL_DEBUG, PSTR("Arr %d %d %d %d %d %d %d %d"), arr[0], arr[1], arr[2], arr[3], arr[4], arr[5], arr[6], arr[7]);
+ // AddLog(LOG_LEVEL_DEBUG, PSTR("IrRemoteSendRawStandard: count_2 = %d"), count);
+ // AddLog(LOG_LEVEL_DEBUG, PSTR("Arr %d %d %d %d %d %d %d %d"), arr[0], arr[1], arr[2], arr[3], arr[4], arr[5], arr[6], arr[7]);
if (0 == count) { return IE_INVALID_RAWDATA; }
if (!IR_RCV_WHILE_SENDING && (irrecv != nullptr)) { irrecv->disableIRIn(); }
diff --git a/tasmota/xdrv_06_snfbridge.ino b/tasmota/xdrv_06_snfbridge.ino
index bb77d64c2..405e5e74b 100644
--- a/tasmota/xdrv_06_snfbridge.ino
+++ b/tasmota/xdrv_06_snfbridge.ino
@@ -147,7 +147,7 @@ uint32_t SnfBrUpdateFirmware(uint8_t* data, uint32_t size) {
uint32_t error = rf_erase_flash(); // 10, 11
if (error) { return error; }
-// AddLog_P(LOG_LEVEL_DEBUG, PSTR("RFB: Erased"));
+// AddLog(LOG_LEVEL_DEBUG, PSTR("RFB: Erased"));
return rf_search_and_write(data, size);
}
diff --git a/tasmota/xdrv_07_domoticz.ino b/tasmota/xdrv_07_domoticz.ino
index 82b769963..d3a16ed86 100644
--- a/tasmota/xdrv_07_domoticz.ino
+++ b/tasmota/xdrv_07_domoticz.ino
@@ -219,7 +219,7 @@ bool DomoticzMqttData(void) {
uint32_t idx = domoticz.getUInt(PSTR("idx"), 0);
int16_t nvalue = domoticz.getInt(PSTR("nvalue"), -1);
- AddLog_P(LOG_LEVEL_DEBUG_MORE, PSTR(D_LOG_DOMOTICZ "idx %d, nvalue %d"), idx, nvalue);
+ AddLog(LOG_LEVEL_DEBUG_MORE, PSTR(D_LOG_DOMOTICZ "idx %d, nvalue %d"), idx, nvalue);
bool found = false;
if ((idx > 0) && (nvalue >= 0) && (nvalue <= 15)) {
@@ -371,7 +371,7 @@ void DomoticzSendData(uint32_t sensor_idx, uint32_t idx, char *data) {
nvalue = position < 2 ? 0 : (position == 100 ? 1 : 2);
}
#endif // USE_SHUTTER
- Response_P(DOMOTICZ_MESSAGE,
+ Response_P(DOMOTICZ_MESSAGE, // "{\"idx\":%d,\"nvalue\":%d,\"svalue\":\"%s\",\"Battery\":%d,\"RSSI\":%d}"
idx, nvalue, data, DomoticzBatteryQuality(), DomoticzRssiQuality());
}
MqttPublish(domoticz_in_topic);
@@ -397,17 +397,36 @@ void DomoticzSensor(uint8_t idx, uint32_t value) {
DomoticzSensor(idx, data);
}
+void DomoticzFloatSensor(uint8_t idx, float value) {
+ uint32_t resolution = 1;
+/*
+ switch (idx) {
+ case DZ_TEMP: resolution = Settings.flag2.temperature_resolution; break;
+ case DZ_POWER_ENERGY: resolution = Settings.flag2.wattage_resolution; break;
+ case DZ_VOLTAGE: resolution = Settings.flag2.voltage_resolution; break;
+ case DZ_CURRENT: resolution = Settings.flag2.current_resolution; break;
+ }
+*/
+ if (DZ_TEMP == idx) { resolution = Settings.flag2.temperature_resolution; }
+ else if (DZ_POWER_ENERGY == idx) { resolution = Settings.flag2.wattage_resolution; }
+ else if (DZ_VOLTAGE == idx) { resolution = Settings.flag2.voltage_resolution; }
+ else if (DZ_CURRENT == idx) { resolution = Settings.flag2.current_resolution; }
+ char data[FLOATSZ];
+ dtostrfd(value, resolution, data);
+ DomoticzSensor(idx, data);
+}
+
//void DomoticzTempHumPressureSensor(float temp, float hum, float baro = -1);
void DomoticzTempHumPressureSensor(float temp, float hum, float baro) {
char temperature[FLOATSZ];
- dtostrfd(temp, 2, temperature);
+ dtostrfd(temp, Settings.flag2.temperature_resolution, temperature);
char humidity[FLOATSZ];
- dtostrfd(hum, 2, humidity);
+ dtostrfd(hum, Settings.flag2.humidity_resolution, humidity);
char data[32];
if (baro > -1) {
char pressure[FLOATSZ];
- dtostrfd(baro, 2, pressure);
+ dtostrfd(baro, Settings.flag2.pressure_resolution, pressure);
snprintf_P(data, sizeof(data), PSTR("%s;%s;%d;%s;5"), temperature, humidity, DomoticzHumidityState(hum), pressure);
DomoticzSensor(DZ_TEMP_HUM_BARO, data);
@@ -544,9 +563,9 @@ const char HTTP_FORM_DOMOTICZ_TIMER[] PROGMEM =
void HandleDomoticzConfiguration(void) {
if (!HttpCheckPriviledgedAccess()) { return; }
- AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_HTTP D_CONFIGURE_DOMOTICZ));
+ AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_HTTP D_CONFIGURE_DOMOTICZ));
- if (Webserver->hasArg("save")) {
+ if (Webserver->hasArg(F("save"))) {
DomoticzSaveSettings();
WebRestart(1);
return;
@@ -605,7 +624,7 @@ void DomoticzSaveSettings(void) {
Settings.domoticz_sensor_idx[i] = (!strlen(tmp)) ? 0 : atoi(tmp);
snprintf_P(ssensor_indices, sizeof(ssensor_indices), PSTR("%s%s%d"), ssensor_indices, (strlen(ssensor_indices)) ? "," : "", Settings.domoticz_sensor_idx[i]);
}
- WebGetArg("ut", tmp, sizeof(tmp));
+ WebGetArg(PSTR("ut"), tmp, sizeof(tmp));
Settings.domoticz_update_timer = (!strlen(tmp)) ? DOMOTICZ_UPDATE_TIMER : atoi(tmp);
AddLog_P(LOG_LEVEL_INFO, PSTR(D_LOG_DOMOTICZ D_CMND_IDX " %d,%d,%d,%d, " D_CMND_KEYIDX " %d,%d,%d,%d, " D_CMND_SWITCHIDX " %d,%d,%d,%d, " D_CMND_SENSORIDX " %s, " D_CMND_UPDATETIMER " %d"),
diff --git a/tasmota/xdrv_09_timers.ino b/tasmota/xdrv_09_timers.ino
index 0f6292e13..81b454cf5 100644
--- a/tasmota/xdrv_09_timers.ino
+++ b/tasmota/xdrv_09_timers.ino
@@ -839,9 +839,9 @@ void HandleTimerConfiguration(void)
{
if (!HttpCheckPriviledgedAccess()) { return; }
- AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_HTTP D_CONFIGURE_TIMER));
+ AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_HTTP D_CONFIGURE_TIMER));
- if (Webserver->hasArg("save")) {
+ if (Webserver->hasArg(F("save"))) {
TimerSaveSettings();
HandleConfiguration();
return;
@@ -883,8 +883,8 @@ void TimerSaveSettings(void)
char message[32 + (MAX_TIMERS *11)]; // MQT: Timers 0,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000
Timer timer;
- Settings.flag3.timers_enable = Webserver->hasArg("e0"); // CMND_TIMERS
- WebGetArg("t0", tmp, sizeof(tmp));
+ Settings.flag3.timers_enable = Webserver->hasArg(F("e0")); // CMND_TIMERS
+ WebGetArg(PSTR("t0"), tmp, sizeof(tmp));
char *p = tmp;
snprintf_P(message, sizeof(message), PSTR(D_LOG_MQTT D_CMND_TIMERS " %d"), Settings.flag3.timers_enable); // CMND_TIMERS
for (uint32_t i = 0; i < MAX_TIMERS; i++) {
diff --git a/tasmota/xdrv_10_rules.ino b/tasmota/xdrv_10_rules.ino
index 637a3b963..c1dfa277e 100644
--- a/tasmota/xdrv_10_rules.ino
+++ b/tasmota/xdrv_10_rules.ino
@@ -87,17 +87,20 @@
#define D_JSON_INITIATED "Initiated"
-#define COMPARE_OPERATOR_NONE -1
-#define COMPARE_OPERATOR_EQUAL 0
-#define COMPARE_OPERATOR_BIGGER 1
-#define COMPARE_OPERATOR_SMALLER 2
-#define COMPARE_OPERATOR_EXACT_DIVISION 3
-#define COMPARE_OPERATOR_NUMBER_EQUAL 4
-#define COMPARE_OPERATOR_NOT_EQUAL 5
-#define COMPARE_OPERATOR_BIGGER_EQUAL 6
-#define COMPARE_OPERATOR_SMALLER_EQUAL 7
-#define MAXIMUM_COMPARE_OPERATOR COMPARE_OPERATOR_SMALLER_EQUAL
-const char kCompareOperators[] PROGMEM = "=\0>\0<\0|\0==!=>=<=";
+#define COMPARE_OPERATOR_NONE -1
+#define COMPARE_OPERATOR_EQUAL 0
+#define COMPARE_OPERATOR_BIGGER 1
+#define COMPARE_OPERATOR_SMALLER 2
+#define COMPARE_OPERATOR_EXACT_DIVISION 3
+#define COMPARE_OPERATOR_NUMBER_EQUAL 4
+#define COMPARE_OPERATOR_NOT_EQUAL 5
+#define COMPARE_OPERATOR_BIGGER_EQUAL 6
+#define COMPARE_OPERATOR_SMALLER_EQUAL 7
+#define COMPARE_OPERATOR_STRING_ENDS_WITH 8
+#define COMPARE_OPERATOR_STRING_STARTS_WITH 9
+#define COMPARE_OPERATOR_STRING_CONTAINS 10
+#define MAXIMUM_COMPARE_OPERATOR COMPARE_OPERATOR_STRING_CONTAINS
+const char kCompareOperators[] PROGMEM = "=\0>\0<\0|\0==!=>=<=$>$<$|";
#ifdef USE_EXPRESSION
#include // Import LinkedList library
@@ -355,7 +358,7 @@ int32_t SetRule(uint32_t idx, const char *content, bool append = false) {
len_uncompressed = strlen(Settings.rules[idx]);
len_compressed = compressor.unishox_compress(Settings.rules[idx], len_uncompressed, nullptr /* dry-run */, MAX_RULE_SIZE + 8);
- AddLog_P(LOG_LEVEL_INFO, PSTR("RUL: Stored uncompressed, would compress from %d to %d (-%d%%)"), len_uncompressed, len_compressed, 100 - changeUIntScale(len_compressed, 0, len_uncompressed, 0, 100));
+ AddLog(LOG_LEVEL_INFO, PSTR("RUL: Stored uncompressed, would compress from %d to %d (-%d%%)"), len_uncompressed, len_compressed, 100 - changeUIntScale(len_compressed, 0, len_uncompressed, 0, 100));
}
#endif // USE_UNISHOX_COMPRESSION
@@ -384,9 +387,9 @@ int32_t SetRule(uint32_t idx, const char *content, bool append = false) {
Settings.rules[idx][1] = (len_in + 7) / 8; // store original length in first bytes (4 bytes chuks)
memcpy(&Settings.rules[idx][2], buf_out, len_compressed);
Settings.rules[idx][len_compressed + 2] = 0; // add NULL termination
- AddLog_P(LOG_LEVEL_INFO, PSTR("RUL: Compressed from %d to %d (-%d%%)"), len_in, len_compressed, 100 - changeUIntScale(len_compressed, 0, len_in, 0, 100));
- // AddLog_P(LOG_LEVEL_INFO, PSTR("RUL: First bytes: %02X%02X%02X%02X"), Settings.rules[idx][0], Settings.rules[idx][1], Settings.rules[idx][2], Settings.rules[idx][3]);
- // AddLog_P(LOG_LEVEL_INFO, PSTR("RUL: GetRuleLenStorage = %d"), GetRuleLenStorage(idx));
+ AddLog(LOG_LEVEL_INFO, PSTR("RUL: Compressed from %d to %d (-%d%%)"), len_in, len_compressed, 100 - changeUIntScale(len_compressed, 0, len_in, 0, 100));
+ // AddLog(LOG_LEVEL_INFO, PSTR("RUL: First bytes: %02X%02X%02X%02X"), Settings.rules[idx][0], Settings.rules[idx][1], Settings.rules[idx][2], Settings.rules[idx][3]);
+ // AddLog(LOG_LEVEL_INFO, PSTR("RUL: GetRuleLenStorage = %d"), GetRuleLenStorage(idx));
} else {
len_compressed = -1; // failed
// clear rule cache, so it will be reloaded from Settings
@@ -416,7 +419,7 @@ bool RulesRuleMatch(uint8_t rule_set, String &event, String &rule, bool stop_all
// Step1: Analyse rule
String rule_expr = rule; // "TELE-INA219#CURRENT>0.100"
if (Rules.teleperiod) {
- int ppos = rule_expr.indexOf("TELE-"); // "TELE-INA219#CURRENT>0.100" or "INA219#CURRENT>0.100"
+ int ppos = rule_expr.indexOf(F("TELE-")); // "TELE-INA219#CURRENT>0.100" or "INA219#CURRENT>0.100"
if (ppos == -1) { return false; } // No pre-amble in rule
rule_expr = rule.substring(5); // "INA219#CURRENT>0.100" or "SYSTEM#BOOT"
}
@@ -494,7 +497,7 @@ bool RulesRuleMatch(uint8_t rule_set, String &event, String &rule, bool stop_all
// Step2: Search rule_name
int pos;
int rule_name_idx = 0;
- if ((pos = rule_name.indexOf("[")) > 0) { // "SUBTYPE1#CURRENT[1]"
+ if ((pos = rule_name.indexOf(F("["))) > 0) { // "SUBTYPE1#CURRENT[1]"
rule_name_idx = rule_name.substring(pos +1).toInt();
if ((rule_name_idx < 1) || (rule_name_idx > 6)) { // Allow indexes 1 to 6
rule_name_idx = 1;
@@ -510,12 +513,12 @@ bool RulesRuleMatch(uint8_t rule_set, String &event, String &rule, bool stop_all
JsonParserObject obj = parser.getRootObject();
if (!obj) {
// AddLog_P(LOG_LEVEL_DEBUG, PSTR("RUL: Event too long (%d)"), event.length());
- AddLog_P(LOG_LEVEL_DEBUG, PSTR("RUL: No valid JSON (%s)"), buf.c_str());
+ AddLog(LOG_LEVEL_DEBUG, PSTR("RUL: No valid JSON (%s)"), buf.c_str());
return false; // No valid JSON data
}
String subtype;
uint32_t i = 0;
- while ((pos = rule_name.indexOf("#")) > 0) { // "SUBTYPE1#SUBTYPE2#CURRENT"
+ while ((pos = rule_name.indexOf(F("#"))) > 0) { // "SUBTYPE1#SUBTYPE2#CURRENT"
subtype = rule_name.substring(0, pos);
obj = obj[subtype.c_str()].getObject();
if (!obj) { return false; } // not found
@@ -553,6 +556,7 @@ bool RulesRuleMatch(uint8_t rule_set, String &event, String &rule, bool stop_all
value = CharToFloat((char*)str_value);
int int_value = int(value);
int int_rule_value = int(rule_value);
+ String str_str_value = String(str_value);
switch (compareOperator) {
case COMPARE_OPERATOR_EXACT_DIVISION:
match = (int_rule_value && (int_value % int_rule_value) == 0);
@@ -578,6 +582,15 @@ bool RulesRuleMatch(uint8_t rule_set, String &event, String &rule, bool stop_all
case COMPARE_OPERATOR_SMALLER_EQUAL:
match = (value <= rule_value);
break;
+ case COMPARE_OPERATOR_STRING_ENDS_WITH:
+ match = str_str_value.endsWith(rule_svalue);
+ break;
+ case COMPARE_OPERATOR_STRING_STARTS_WITH:
+ match = str_str_value.startsWith(rule_svalue);
+ break;
+ case COMPARE_OPERATOR_STRING_CONTAINS:
+ match = (str_str_value.indexOf(rule_svalue) > 0);
+ break;
default:
match = true;
}
@@ -686,14 +699,14 @@ bool RuleSetProcess(uint8_t rule_set, String &event_saved)
String rule = rules;
rule.toUpperCase(); // "ON INA219#CURRENT>0.100 DO BACKLOG DIMMER 10;COLOR 100000 ENDON"
- if (!rule.startsWith("ON ")) { return serviced; } // Bad syntax - Nothing to start on
+ if (!rule.startsWith(F("ON "))) { return serviced; } // Bad syntax - Nothing to start on
- int pevt = rule.indexOf(" DO ");
+ int pevt = rule.indexOf(F(" DO "));
if (pevt == -1) { return serviced; } // Bad syntax - Nothing to do
String event_trigger = rule.substring(3, pevt); // "INA219#CURRENT>0.100"
- plen = rule.indexOf(" ENDON");
- plen2 = rule.indexOf(" BREAK");
+ plen = rule.indexOf(F(" ENDON"));
+ plen2 = rule.indexOf(F(" BREAK"));
if ((plen == -1) && (plen2 == -1)) { return serviced; } // Bad syntax - No ENDON neither BREAK
if (plen == -1) { plen = 9999; }
@@ -717,10 +730,10 @@ bool RuleSetProcess(uint8_t rule_set, String &event_saved)
// if (!ucommand.startsWith("BACKLOG")) { commands = "backlog " + commands; } // Always use Backlog to prevent power race exception
// Use Backlog with event to prevent rule event loop exception unless IF is used which uses an implicit backlog
- if ((ucommand.indexOf("IF ") == -1) &&
- (ucommand.indexOf("EVENT ") != -1) &&
- (ucommand.indexOf("BACKLOG ") == -1)) {
- commands = "backlog " + commands;
+ if ((ucommand.indexOf(F("IF ")) == -1) &&
+ (ucommand.indexOf(F("EVENT ")) != -1) &&
+ (ucommand.indexOf(F("BACKLOG ")) == -1)) {
+ commands = String(F("backlog ")) + commands;
}
RulesVarReplace(commands, F("%VALUE%"), Rules.event_value);
@@ -757,7 +770,7 @@ bool RuleSetProcess(uint8_t rule_set, String &event_saved)
char command[commands.length() +1];
strlcpy(command, commands.c_str(), sizeof(command));
- AddLog_P(LOG_LEVEL_INFO, PSTR("RUL: %s performs \"%s\""), event_trigger.c_str(), command);
+ AddLog(LOG_LEVEL_INFO, PSTR("RUL: %s performs \"%s\""), event_trigger.c_str(), command);
// Response_P(S_JSON_COMMAND_SVALUE, D_CMND_RULE, D_JSON_INITIATED);
// MqttPublishPrefixTopic_P(RESULT_OR_STAT, PSTR(D_CMND_RULE));
@@ -1614,6 +1627,15 @@ bool evaluateComparisonExpression(const char *expression, int len)
case COMPARE_OPERATOR_SMALLER_EQUAL:
bResult = (leftValue <= rightValue);
break;
+ case COMPARE_OPERATOR_STRING_ENDS_WITH:
+ bResult = leftExpr.endsWith(rightExpr);
+ break;
+ case COMPARE_OPERATOR_STRING_STARTS_WITH:
+ bResult = leftExpr.startsWith(rightExpr);
+ break;
+ case COMPARE_OPERATOR_STRING_CONTAINS:
+ bResult = (leftExpr.indexOf(rightExpr) > 0);
+ break;
}
return bResult;
}
@@ -2081,7 +2103,7 @@ void CmndRule(void)
}
int32_t res = SetRule(index - 1, ('"' == XdrvMailbox.data[0]) ? "" : XdrvMailbox.data, append);
if (res < 0) {
- AddLog_P(LOG_LEVEL_ERROR, PSTR("RUL: Not enough space"));
+ AddLog(LOG_LEVEL_ERROR, PSTR("RUL: Not enough space"));
}
}
Rules.triggers[index -1] = 0; // Reset once flag
@@ -2101,7 +2123,7 @@ void CmndRule(void)
} else {
last_index = rule_len; // until the end of the rule
}
- AddLog_P(LOG_LEVEL_INFO, PSTR("RUL: Rule%d %s%s"),
+ AddLog(LOG_LEVEL_INFO, PSTR("RUL: Rule%d %s%s"),
index, 0 == start_index ? PSTR("") : PSTR("+"),
rule.substring(start_index, last_index).c_str());
start_index = last_index + 1;
@@ -2261,14 +2283,14 @@ void CmndScale(void)
{
if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_RULE_VARS)) {
if (XdrvMailbox.data_len > 0) {
- if (strchr(XdrvMailbox.data, ',') != nullptr) { // Process parameter entry
- char sub_string[XdrvMailbox.data_len +1];
+ if (ArgC() > 1) { // Process parameter entry
+ char argument[XdrvMailbox.data_len];
- float valueIN = CharToFloat(subStr(sub_string, XdrvMailbox.data, ",", 1));
- float fromLow = CharToFloat(subStr(sub_string, XdrvMailbox.data, ",", 2));
- float fromHigh = CharToFloat(subStr(sub_string, XdrvMailbox.data, ",", 3));
- float toLow = CharToFloat(subStr(sub_string, XdrvMailbox.data, ",", 4));
- float toHigh = CharToFloat(subStr(sub_string, XdrvMailbox.data, ",", 5));
+ float valueIN = CharToFloat(ArgV(argument, 1));
+ float fromLow = CharToFloat(ArgV(argument, 2));
+ float fromHigh = CharToFloat(ArgV(argument, 3));
+ float toLow = CharToFloat(ArgV(argument, 4));
+ float toHigh = CharToFloat(ArgV(argument, 5));
float value = map_double(valueIN, fromLow, fromHigh, toLow, toHigh);
dtostrfd(value, Settings.flag2.calc_resolution, rules_vars[XdrvMailbox.index -1]);
bitSet(Rules.vars_event, XdrvMailbox.index -1);
diff --git a/tasmota/xdrv_10_scripter.ino b/tasmota/xdrv_10_scripter.ino
index cd07098f8..c42d8163c 100755
--- a/tasmota/xdrv_10_scripter.ino
+++ b/tasmota/xdrv_10_scripter.ino
@@ -196,12 +196,6 @@ void alt_eeprom_readBytes(uint32_t adr, uint32_t len, uint8_t *buf) {
#include "FS.h"
-#define FS_FILE_WRITE "w"
-#define FS_FILE_READ "r"
-#define FS_FILE_APPEND "a"
-
-
-
#if USE_SCRIPT_FATFS==-1
#ifdef ESP32
//#include "FS.h"
@@ -306,7 +300,7 @@ extern FS *ufsp;
#ifdef USE_DISPLAY
#ifdef USE_TOUCH_BUTTONS
#include
-extern VButton *buttons[MAXBUTTONS];
+extern VButton *buttons[MAX_TOUCH_BUTTONS];
#endif
#endif
@@ -477,13 +471,6 @@ bool event_handeled = false;
IPAddress last_udp_ip;
WiFiUDP Script_PortUdp;
-#ifndef USE_DEVICE_GROUPS
-char * IPAddressToString(const IPAddress& ip_address) {
- static char ipbuffer[16];
- sprintf_P(ipbuffer, PSTR("%u.%u.%u.%u"), ip_address[0], ip_address[1], ip_address[2], ip_address[3]);
- return ipbuffer;
-}
-#endif //USE_DEVICE_GROUPS
#endif //USE_SCRIPT_GLOBVARS
int16_t last_findex;
@@ -493,17 +480,6 @@ uint8_t fast_script=0;
uint8_t glob_script=0;
uint32_t script_lastmillis;
-void Script_AddLog_P(uint32_t loglevel, PGM_P formatP, ...) {
- char log_data[128];
-
- va_list arg;
- va_start(arg, formatP);
- vsnprintf_P(log_data, sizeof(log_data), formatP, arg);
- va_end(arg);
-
- AddLogData(loglevel, log_data);
-}
-
void flt2char(float num, char *nbuff) {
dtostrfd(num, glob_script_mem.script_dprec, nbuff);
}
@@ -918,7 +894,7 @@ char *script;
}
// variables usage info
- Script_AddLog_P(LOG_LEVEL_INFO, PSTR("Script: nv=%d, tv=%d, vns=%d, ram=%d"), nvars, svars, index, glob_script_mem.script_mem_size);
+ AddLog(LOG_LEVEL_INFO, PSTR("Script: nv=%d, tv=%d, vns=%d, ram=%d"), nvars, svars, index, glob_script_mem.script_mem_size);
// copy string variables
char *cp1 = glob_script_mem.glob_snp;
@@ -1038,10 +1014,10 @@ void Script_Init_UDP() {
if (glob_script_mem.udp_flags.udp_connected) return;
if (Script_PortUdp.beginMulticast(WiFi.localIP(), IPAddress(239,255,255,250), SCRIPT_UDP_PORT)) {
- Script_AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_UPNP "SCRIPT UDP started"));
+ AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_UPNP "SCRIPT UDP started"));
glob_script_mem.udp_flags.udp_connected = 1;
} else {
- Script_AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_UPNP "SCRIPT UDP failed"));
+ AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_UPNP "SCRIPT UDP failed"));
glob_script_mem.udp_flags.udp_connected = 0;
}
}
@@ -1055,7 +1031,7 @@ void Script_PollUdp(void) {
int32_t len = Script_PortUdp.read(packet_buffer, SCRIPT_UDP_BUFFER_SIZE - 1);
packet_buffer[len] = 0;
script_udp_remote_ip = Script_PortUdp.remoteIP();
- Script_AddLog_P(LOG_LEVEL_DEBUG, PSTR("UDP: Packet %s - %d - %s"), packet_buffer, len, script_udp_remote_ip.toString().c_str());
+ AddLog(LOG_LEVEL_DEBUG, PSTR("UDP: Packet %s - %d - %_I"), packet_buffer, len, (uint32_t)script_udp_remote_ip);
char *lp=packet_buffer;
if (!strncmp(lp,"=>", 2)) {
lp += 2;
@@ -1074,10 +1050,10 @@ void Script_PollUdp(void) {
uint32_t index;
uint32_t res = match_vars(vnam, &fp, &sp, &index);
if (res == NUM_RES) {
- Script_AddLog_P(LOG_LEVEL_DEBUG, PSTR("num var found - %s - %d - %d"), vnam, res, index);
+ AddLog(LOG_LEVEL_DEBUG, PSTR("num var found - %s - %d - %d"), vnam, res, index);
*fp=CharToFloat(cp + 1);
} else if (res == STR_RES) {
- Script_AddLog_P(LOG_LEVEL_DEBUG, PSTR("string var found - %s - %d - %d"), vnam, res, index);
+ AddLog(LOG_LEVEL_DEBUG, PSTR("string var found - %s - %d - %d"), vnam, res, index);
strlcpy(sp, cp + 1, SCRIPT_MAXSSIZE);
} else {
// error var not found
@@ -1111,10 +1087,10 @@ void script_udp_sendvar(char *vname,float *fp,char *sp) {
char flstr[16];
dtostrfd(*fp, 8, flstr);
strcat(sbuf, flstr);
- Script_AddLog_P(LOG_LEVEL_DEBUG, PSTR("num var updated - %s"), sbuf);
+ AddLog(LOG_LEVEL_DEBUG, PSTR("num var updated - %s"), sbuf);
} else {
strcat(sbuf, sp);
- Script_AddLog_P(LOG_LEVEL_DEBUG, PSTR("string var updated - %s"), sbuf);
+ AddLog(LOG_LEVEL_DEBUG, PSTR("string var updated - %s"), sbuf);
}
Script_PortUdp.beginPacket(IPAddress(239, 255, 255, 250), SCRIPT_UDP_PORT);
// Udp.print(String("RET UC: ") + String(recv_Packet));
@@ -1983,6 +1959,9 @@ chknext:
case 11:
fvar = Energy.daily;
break;
+ case 12:
+ fvar = (float)Settings.energy_kWhyesterday/100000.0;
+ break;
default:
fvar = 99999;
@@ -2024,7 +2003,7 @@ chknext:
if (!glob_script_mem.file_flags[cnt].is_open) {
if (mode==0) {
#ifdef DEBUG_FS
- Script_AddLog_P(LOG_LEVEL_INFO, PSTR("open file for read %d"), cnt);
+ AddLog(LOG_LEVEL_INFO, PSTR("open file for read %d"), cnt);
#endif
glob_script_mem.files[cnt] = ufsp->open(str, FS_FILE_READ);
if (glob_script_mem.files[cnt].isDirectory()) {
@@ -2038,12 +2017,12 @@ chknext:
if (mode==1) {
glob_script_mem.files[cnt] = ufsp->open(str,FS_FILE_WRITE);
#ifdef DEBUG_FS
- Script_AddLog_P(LOG_LEVEL_INFO, PSTR("open file for write %d"), cnt);
+ AddLog(LOG_LEVEL_INFO, PSTR("open file for write %d"), cnt);
#endif
} else {
glob_script_mem.files[cnt] = ufsp->open(str,FS_FILE_APPEND);
#ifdef DEBUG_FS
- Script_AddLog_P(LOG_LEVEL_INFO, PSTR("open file for append %d"), cnt);
+ AddLog(LOG_LEVEL_INFO, PSTR("open file for append %d"), cnt);
#endif
}
}
@@ -2051,7 +2030,7 @@ chknext:
fvar = cnt;
glob_script_mem.file_flags[cnt].is_open = 1;
} else {
- Script_AddLog_P(LOG_LEVEL_INFO, PSTR("file open failed"));
+ AddLog(LOG_LEVEL_INFO, PSTR("file open failed"));
}
break;
}
@@ -2066,7 +2045,7 @@ chknext:
uint8_t ind = fvar;
if (ind>=SFS_MAX) ind = SFS_MAX - 1;
#ifdef DEBUG_FS
- Script_AddLog_P(LOG_LEVEL_INFO, PSTR("closing file %d"), ind);
+ AddLog(LOG_LEVEL_INFO, PSTR("closing file %d"), ind);
#endif
glob_script_mem.files[ind].close();
glob_script_mem.file_flags[ind].is_open = 0;
@@ -2202,7 +2181,7 @@ chknext:
} else {
fvar = 0;
}
- //Script_AddLog_P(LOG_LEVEL_INFO, PSTR("picture save: %d"), len);
+ //AddLog(LOG_LEVEL_INFO, PSTR("picture save: %d"), len);
} else {
fvar = 0;
}
@@ -2525,7 +2504,7 @@ chknext:
}
#ifdef USE_SCRIPT_GLOBVARS
if (!strncmp(vname, "luip", 4)) {
- if (sp) strlcpy(sp, IPAddressToString(last_udp_ip), glob_script_mem.max_ssize);
+ if (sp) strlcpy(sp, last_udp_ip.toString().c_str(), glob_script_mem.max_ssize);
goto strexit;
}
#endif //USE_SCRIPT_GLOBVARS
@@ -2790,6 +2769,12 @@ chknext:
fvar = GetStack();
goto exit;
}
+#ifdef ESP32
+ if (!strncmp(vname, "stkwm", 5)) {
+ fvar = uxTaskGetStackHighWaterMark(NULL);
+ goto exit;
+ }
+#endif // ESP32
if (!strncmp(vname, "slen", 4)) {
fvar = strlen(glob_script_mem.script_ram);
goto exit;
@@ -3048,7 +3033,7 @@ chknext:
if (!strncmp(vname, "tbut[", 5)) {
GetNumericArgument(vname + 5, OPER_EQU, &fvar, 0);
uint8_t index = fvar;
- if (index<1 || index>MAXBUTTONS) index = 1;
+ if (index<1 || index>MAX_TOUCH_BUTTONS) index = 1;
index--;
if (buttons[index]) {
fvar = buttons[index]->vpower.on_off;
@@ -3061,6 +3046,30 @@ chknext:
#endif //USE_TOUCH_BUTTONS
#endif //USE_DISPLAY
+#if 1
+ if (!strncmp(vname, "test(", 5)) {
+ lp = GetNumericArgument(lp + 5, OPER_EQU, &fvar, 0);
+ uint32_t cycles;
+ uint64_t accu=0;
+ char sbuffer[32];
+ // PSTR performance test
+ // this is best case since everything will be in cache
+ // PSTR at least 3 times slower here, will be much slower if cache missed
+ for (uint16 cnt=0; cnt<1000; cnt++) {
+ cycles=ESP.getCycleCount();
+ if (fvar>0) {
+ snprintf_P(sbuffer, sizeof(sbuffer), PSTR("1234"));
+ } else {
+ snprintf(sbuffer, sizeof(sbuffer), "1234");
+ }
+ accu += ESP.getCycleCount()-cycles;
+ }
+ lp++;
+ len = 0;
+ fvar = accu / 1000;
+ goto exit;
+ }
+#endif
break;
case 'u':
if (!strncmp(vname, "uptime", 6)) {
@@ -3596,7 +3605,7 @@ void Replace_Cmd_Vars(char *srcbuf, uint32_t srcsize, char *dstbuf, uint32_t dst
void toLog(const char *str) {
if (!str) return;
- Script_AddLog_P(LOG_LEVEL_INFO, str);
+ AddLog(LOG_LEVEL_INFO, str);
}
@@ -4308,7 +4317,7 @@ int16_t Run_script_sub(const char *type, int8_t tlen, JsonParserObject *jo) {
}
cmd[count] = *lp++;
}
- //Script_AddLog_P(LOG_LEVEL_INFO, tmp);
+ //AddLog(LOG_LEVEL_INFO, tmp);
// replace vars in cmd
char *tmp = cmdmem + SCRIPT_CMDMEM / 2;
Replace_Cmd_Vars(cmd, 0, tmp, SCRIPT_CMDMEM / 2);
@@ -4320,7 +4329,7 @@ int16_t Run_script_sub(const char *type, int8_t tlen, JsonParserObject *jo) {
} else {
if (!sflag) {
tasm_cmd_activ = 1;
- Script_AddLog_P(glob_script_mem.script_loglevel&0x7f, PSTR("Script: performs \"%s\""), tmp);
+ AddLog(glob_script_mem.script_loglevel&0x7f, PSTR("Script: performs \"%s\""), tmp);
} else if (sflag==2) {
// allow recursive call
} else {
@@ -4552,7 +4561,7 @@ int16_t Run_script_sub(const char *type, int8_t tlen, JsonParserObject *jo) {
ctype += tlen;
char nxttok = '(';
char *argptr = ctype+tlen;
-
+
lp += tlen;
do {
if (*ctype==nxttok && *lp==nxttok) {
@@ -4816,13 +4825,13 @@ uint8_t sc_state;
// upload script and start immediately
void script_upload_start(void) {
- //Script_AddLog_P(LOG_LEVEL_INFO, PSTR("HTP: file upload execute"));
+ //AddLog(LOG_LEVEL_INFO, PSTR("HTP: file upload execute"));
HTTPUpload& upload = Webserver->upload();
if (upload.status == UPLOAD_FILE_START) {
- //Script_AddLog_P(LOG_LEVEL_INFO, PSTR("HTP: upload start"));
+ //AddLog(LOG_LEVEL_INFO, PSTR("HTP: upload start"));
script_ex_ptr = (uint8_t*)glob_script_mem.script_ram;
- //Script_AddLog_P(LOG_LEVEL_INFO, PSTR("HTP: upload file %s, %d"),upload.filename.c_str(),upload.totalSize);
+ //AddLog(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;
@@ -4840,7 +4849,7 @@ void script_upload_start(void) {
bitWrite(Settings.rule_enabled, 0, 0);
} else if(upload.status == UPLOAD_FILE_WRITE) {
- //Script_AddLog_P(LOG_LEVEL_INFO, PSTR("HTP: upload write"));
+ //AddLog(LOG_LEVEL_INFO, PSTR("HTP: upload write"));
uint32_t csiz = upload.currentSize;
uint32_t tsiz = glob_script_mem.script_size - 1;
if (uplsizeexists(file)) {
- Script_AddLog_P(LOG_LEVEL_INFO,PSTR("file not found"));
+ AddLog(LOG_LEVEL_INFO,PSTR("file not found"));
return 0;
}
download_file = ufsp->open(file, FS_FILE_READ);
if (!download_file) {
- Script_AddLog_P(LOG_LEVEL_INFO,PSTR("could not open file"));
+ AddLog(LOG_LEVEL_INFO,PSTR("could not open file"));
return 0;
}
@@ -4960,7 +4969,7 @@ void HandleScriptConfiguration(void) {
if (!HttpCheckPriviledgedAccess()) { return; }
- Script_AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_HTTP D_CONFIGURE_SCRIPT));
+ AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_HTTP D_CONFIGURE_SCRIPT));
#ifdef USE_SCRIPT_FATFS
if (Webserver->hasArg("d1")) {
@@ -5060,7 +5069,7 @@ void ScriptSaveSettings(void) {
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') {
- Script_AddLog_P(LOG_LEVEL_INFO, PSTR("script error: must start with >D"));
+ AddLog(LOG_LEVEL_INFO, PSTR("script error: must start with >D"));
bitWrite(Settings.rule_enabled, 0, 0);
}
@@ -5073,14 +5082,14 @@ void ScriptSaveSettings(void) {
//
uint32_t script_compress(char *dest, uint32_t size) {
- //Script_AddLog_P(LOG_LEVEL_INFO,PSTR("in string: %s len = %d"),glob_script_mem.script_ram,strlen(glob_script_mem.script_ram));
+ //AddLog(LOG_LEVEL_INFO,PSTR("in string: %s len = %d"),glob_script_mem.script_ram,strlen(glob_script_mem.script_ram));
uint32_t len_compressed = SCRIPT_COMPRESS(glob_script_mem.script_ram, strlen(glob_script_mem.script_ram), dest, size);
if (len_compressed > 0) {
dest[len_compressed] = 0;
- Script_AddLog_P(LOG_LEVEL_INFO,PSTR("script compressed to %d bytes = %d %%"),len_compressed,len_compressed * 100 / strlen(glob_script_mem.script_ram));
+ AddLog(LOG_LEVEL_INFO,PSTR("script compressed to %d bytes = %d %%"),len_compressed,len_compressed * 100 / strlen(glob_script_mem.script_ram));
return 0;
} else {
- Script_AddLog_P(LOG_LEVEL_INFO, PSTR("script compress error: %d"), len_compressed);
+ AddLog(LOG_LEVEL_INFO, PSTR("script compress error: %d"), len_compressed);
return 1;
}
}
@@ -5103,7 +5112,7 @@ void SaveScriptEnd(void) {
int16_t res = Init_Scripter();
if (res) {
- Script_AddLog_P(LOG_LEVEL_INFO, PSTR("script init error: %d"), res);
+ AddLog(LOG_LEVEL_INFO, PSTR("script init error: %d"), res);
return;
}
@@ -5429,7 +5438,7 @@ void Script_Check_Hue(String *response) {
}
*response += String(EncodeLightId(hue_devs + TasmotaGlobal.devices_present + 1))+"\":";
Script_HueStatus(response, hue_devs);
- //Script_AddLog_P(LOG_LEVEL_INFO, PSTR("Hue: %s - %d "),response->c_str(), hue_devs);
+ //AddLog(LOG_LEVEL_INFO, PSTR("Hue: %s - %d "),response->c_str(), hue_devs);
}
hue_devs++;
@@ -5444,7 +5453,7 @@ void Script_Check_Hue(String *response) {
}
#if 0
if (response) {
- Script_AddLog_P(LOG_LEVEL_DEBUG, PSTR("Hue: %d"), hue_devs);
+ AddLog(LOG_LEVEL_DEBUG, PSTR("Hue: %d"), hue_devs);
toLog(">>>>");
toLog(response->c_str());
toLog(response->c_str()+MAX_LOGSZ);
@@ -5589,7 +5598,7 @@ void Script_Handle_Hue(String *path) {
} else {
response = FPSTR(sHUE_ERROR_JSON);
}
- Script_AddLog_P(LOG_LEVEL_DEBUG_MORE, PSTR(D_LOG_HTTP D_HUE " Result (%s)"), response.c_str());
+ AddLog(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);
@@ -5603,7 +5612,7 @@ bool Script_SubCmd(void) {
if (!bitRead(Settings.rule_enabled, 0)) return false;
if (tasm_cmd_activ) return false;
- //Script_AddLog_P(LOG_LEVEL_INFO,PSTR(">> %s, %s, %d, %d "),XdrvMailbox.topic, XdrvMailbox.data, XdrvMailbox.payload, XdrvMailbox.index);
+ //AddLog(LOG_LEVEL_INFO,PSTR(">> %s, %s, %d, %d "),XdrvMailbox.topic, XdrvMailbox.data, XdrvMailbox.payload, XdrvMailbox.index);
char command[CMDSZ];
strlcpy(command, XdrvMailbox.topic, CMDSZ);
@@ -5631,7 +5640,7 @@ bool Script_SubCmd(void) {
}
//toLog(cmdbuff);
uint32_t res = Run_Scripter(cmdbuff, tlen + 1, 0);
- //Script_AddLog_P(LOG_LEVEL_INFO,">>%d",res);
+ //AddLog(LOG_LEVEL_INFO,">>%d",res);
if (res) {
return false;
}
@@ -5772,7 +5781,7 @@ uint32_t JsonParsePath(JsonParserObject *jobj, const char *spath, char delim, fl
uint32_t res = 0;
const char *cp = spath;
#ifdef DEBUG_MQTT_EVENT
-// Script_AddLog_P(LOG_LEVEL_INFO, PSTR("Script: parsing json key: %s from json: %s"), cp, jpath);
+// AddLog(LOG_LEVEL_INFO, PSTR("Script: parsing json key: %s from json: %s"), cp, jpath);
#endif
JsonParserObject obj = *jobj;
JsonParserObject lastobj = obj;
@@ -5790,7 +5799,7 @@ uint32_t JsonParsePath(JsonParserObject *jobj, const char *spath, char delim, fl
selem[sp] = *cp++;
}
#ifdef DEBUG_MQTT_EVENT
- Script_AddLog_P(LOG_LEVEL_INFO, PSTR("Script: cmp current key: %s"), selem);
+ AddLog(LOG_LEVEL_INFO, PSTR("Script: cmp current key: %s"), selem);
#endif
// check for array
char *sp = strchr(selem,'[');
@@ -5803,7 +5812,7 @@ uint32_t JsonParsePath(JsonParserObject *jobj, const char *spath, char delim, fl
obj = obj[selem];
if (!obj.isValid()) {
#ifdef DEBUG_MQTT_EVENT
- Script_AddLog_P(LOG_LEVEL_INFO, PSTR("Script: obj invalid: %s"), selem);
+ AddLog(LOG_LEVEL_INFO, PSTR("Script: obj invalid: %s"), selem);
#endif
JsonParserToken tok = lastobj[selem];
if (tok.isValid()) {
@@ -5828,7 +5837,7 @@ uint32_t JsonParsePath(JsonParserObject *jobj, const char *spath, char delim, fl
}
#ifdef DEBUG_MQTT_EVENT
- Script_AddLog_P(LOG_LEVEL_INFO, PSTR("Script: token invalid: %s"), selem);
+ AddLog(LOG_LEVEL_INFO, PSTR("Script: token invalid: %s"), selem);
#endif
break;
}
@@ -5871,7 +5880,7 @@ bool ScriptMqttData(void)
String sData = XdrvMailbox.data;
#ifdef DEBUG_MQTT_EVENT
- Script_AddLog_P(LOG_LEVEL_INFO, PSTR("Script: MQTT Topic %s, Event %s"), XdrvMailbox.topic, XdrvMailbox.data);
+ AddLog(LOG_LEVEL_INFO, PSTR("Script: MQTT Topic %s, Event %s"), XdrvMailbox.topic, XdrvMailbox.data);
#endif
MQTT_Subscription event_item;
@@ -5881,7 +5890,7 @@ bool ScriptMqttData(void)
uint8_t json_valid = 0;
#ifdef DEBUG_MQTT_EVENT
- Script_AddLog_P(LOG_LEVEL_INFO, PSTR("Script: Match MQTT message Topic %s with subscription topic %s and key %s"), sTopic.c_str(), event_item.Topic.c_str(),event_item.Key.c_str());
+ AddLog(LOG_LEVEL_INFO, PSTR("Script: Match MQTT message Topic %s with subscription topic %s and key %s"), sTopic.c_str(), event_item.Topic.c_str(),event_item.Key.c_str());
#endif
if (sTopic.startsWith(event_item.Topic)) {
//This topic is subscribed by us, so serve it
@@ -5938,7 +5947,7 @@ bool ScriptMqttData(void)
snprintf_P(sbuffer, sizeof(sbuffer), PSTR(">%s=\"%s\"\n"), event_item.Event.c_str(), value.c_str());
}
#ifdef DEBUG_MQTT_EVENT
- Script_AddLog_P(LOG_LEVEL_INFO, PSTR("Script: setting script var %s"), sbuffer);
+ AddLog(LOG_LEVEL_INFO, PSTR("Script: setting script var %s"), sbuffer);
#endif
//toLog(sbuffer);
execute_script(sbuffer);
@@ -5987,7 +5996,7 @@ String ScriptSubscribe(const char *data, int data_len)
}
}
}
- //Script_AddLog_P(LOG_LEVEL_DEBUG, PSTR("Script: Subscribe command with parameters: %s, %s, %s."), event_name.c_str(), topic.c_str(), key.c_str());
+ //AddLog(LOG_LEVEL_DEBUG, PSTR("Script: Subscribe command with parameters: %s, %s, %s."), event_name.c_str(), topic.c_str(), key.c_str());
//event_name.toUpperCase();
if (event_name.length() > 0 && topic.length() > 0) {
//Search all subscriptions
@@ -6008,7 +6017,7 @@ String ScriptSubscribe(const char *data, int data_len)
topic.concat("/#");
}
}
- // Script_AddLog_P(LOG_LEVEL_DEBUG, PSTR("Script: New topic: %s."), topic.c_str());
+ // AddLog(LOG_LEVEL_DEBUG, PSTR("Script: New topic: %s."), topic.c_str());
//MQTT Subscribe
subscription_item.Event = event_name;
subscription_item.Topic = topic.substring(0, topic.length() - 2); //Remove "/#" so easy to match
@@ -7181,7 +7190,7 @@ bool RulesProcessEvent(char *json_event) {
#ifdef USE_SCRIPT_TASK
#ifndef STASK_STACK
-#define STASK_STACK 8192
+#define STASK_STACK 8192-2048
#endif
#if 1
@@ -7374,7 +7383,7 @@ bool Xdrv10(uint8_t function)
#ifdef USE_UFILESYS
if (ufs_type) {
// we have a file system
- Script_AddLog_P(LOG_LEVEL_INFO,PSTR("UFILESYSTEM OK!"));
+ AddLog(LOG_LEVEL_INFO,PSTR("UFILESYSTEM OK!"));
char *script;
script = (char*)calloc(UFSYS_SIZE + 4, 1);
if (!script) break;
@@ -7393,7 +7402,7 @@ bool Xdrv10(uint8_t function)
// indicates scripter use no compression
bitWrite(Settings.rule_once, 6, 0);
} else {
- Script_AddLog_P(LOG_LEVEL_INFO,PSTR("UFILESYSTEM fail, using compression!"));
+ AddLog(LOG_LEVEL_INFO,PSTR("UFILESYSTEM fail, using compression!"));
int32_t len_decompressed;
sprt = (char*)calloc(UNISHOXRSIZE + 8,1);
if (!sprt) { break; }
@@ -7465,7 +7474,7 @@ bool Xdrv10(uint8_t function)
#endif
-#endif // UFILESYSTEM
+#endif // USE_UFILESYS
// indicates scripter enabled (use rules[][] as single array)
diff --git a/tasmota/xdrv_11_knx.ino b/tasmota/xdrv_11_knx.ino
index 0a8442e3b..afddce071 100644
--- a/tasmota/xdrv_11_knx.ino
+++ b/tasmota/xdrv_11_knx.ino
@@ -255,7 +255,7 @@ void KNX_ADD_GA( uint8_t GAop, uint8_t GA_FNUM, uint8_t GA_AREA, uint8_t GA_FDEF
Settings.knx_GA_registered++;
- AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_KNX D_ADD " GA #%d: %s " D_TO " %d/%d/%d"),
+ AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_KNX D_ADD " GA #%d: %s " D_TO " %d/%d/%d"),
Settings.knx_GA_registered,
device_param_ga[GAop-1],
GA_FNUM, GA_AREA, GA_FDEF );
@@ -304,7 +304,7 @@ void KNX_DEL_GA( uint8_t GAnum )
Settings.knx_GA_registered--;
- AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_KNX D_DELETE " GA #%d"),
+ AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_KNX D_DELETE " GA #%d"),
GAnum );
}
@@ -336,7 +336,7 @@ void KNX_ADD_CB( uint8_t CBop, uint8_t CB_FNUM, uint8_t CB_AREA, uint8_t CB_FDEF
Settings.knx_CB_registered++;
- AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_KNX D_ADD " CB #%d: %d/%d/%d " D_TO " %s"),
+ AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_KNX D_ADD " CB #%d: %d/%d/%d " D_TO " %s"),
Settings.knx_CB_registered,
CB_FNUM, CB_AREA, CB_FDEF,
device_param_cb[CBop-1] );
@@ -392,7 +392,7 @@ void KNX_DEL_CB( uint8_t CBnum )
device_param[oldparam-1].CB_id = KNX_Empty;
}
- AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_KNX D_DELETE " CB #%d"), CBnum );
+ AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_KNX D_DELETE " CB #%d"), CBnum );
}
@@ -449,7 +449,7 @@ bool KNX_CONFIG_NOT_MATCH(void)
void KNXStart(void)
{
knx.start(nullptr);
- AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_KNX D_START));
+ AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_KNX D_START));
}
@@ -522,7 +522,7 @@ void KNX_INIT(void)
if (KNX_CONFIG_NOT_MATCH()) {
Settings.knx_GA_registered = 0;
Settings.knx_CB_registered = 0;
- AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_KNX D_DELETE " " D_KNX_PARAMETERS));
+ AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_KNX D_DELETE " " D_KNX_PARAMETERS));
}
// Register Group Addresses to listen to
@@ -564,7 +564,7 @@ void KNX_CB_Action(message_t const &msg, void *arg)
float tempvar = knx.data_to_4byte_float(msg.data);
dtostrfd(tempvar,2,tempchar);
}
- AddLog_P(LOG_LEVEL_INFO, PSTR(D_LOG_KNX D_RECEIVED_FROM " %d.%d.%d " D_COMMAND " %s: %s " D_TO " %s"),
+ AddLog(LOG_LEVEL_INFO, PSTR(D_LOG_KNX D_RECEIVED_FROM " %d.%d.%d " D_COMMAND " %s: %s " D_TO " %s"),
msg.received_on.ga.area, msg.received_on.ga.line, msg.received_on.ga.member,
(msg.ct == KNX_CT_WRITE) ? D_KNX_COMMAND_WRITE : (msg.ct == KNX_CT_READ) ? D_KNX_COMMAND_READ : D_KNX_COMMAND_OTHER,
tempchar,
@@ -748,7 +748,7 @@ void KnxUpdatePowerState(uint8_t device, power_t state)
knx.write_1bit(KNX_addr, device_param[device -1].last_state);
}
- AddLog_P(LOG_LEVEL_INFO, PSTR(D_LOG_KNX "%s = %d " D_SENT_TO " %d.%d.%d"),
+ AddLog(LOG_LEVEL_INFO, PSTR(D_LOG_KNX "%s = %d " D_SENT_TO " %d.%d.%d"),
device_param_ga[device -1], device_param[device -1].last_state,
KNX_addr.ga.area, KNX_addr.ga.line, KNX_addr.ga.member);
@@ -782,7 +782,7 @@ void KnxSendButtonPower(void)
knx.write_1bit(KNX_addr, !(state == 0));
}
- AddLog_P(LOG_LEVEL_INFO, PSTR(D_LOG_KNX "%s = %d " D_SENT_TO " %d.%d.%d"),
+ AddLog(LOG_LEVEL_INFO, PSTR(D_LOG_KNX "%s = %d " D_SENT_TO " %d.%d.%d"),
device_param_ga[device + 7], !(state == 0),
KNX_addr.ga.area, KNX_addr.ga.line, KNX_addr.ga.member);
@@ -813,7 +813,7 @@ void KnxSensor(uint8_t sensor_type, float value)
knx.write_4byte_float(KNX_addr, value);
}
- AddLog_P(LOG_LEVEL_INFO, PSTR(D_LOG_KNX "%s " D_SENT_TO " %d.%d.%d "),
+ AddLog(LOG_LEVEL_INFO, PSTR(D_LOG_KNX "%s " D_SENT_TO " %d.%d.%d "),
device_param_ga[sensor_type -1],
KNX_addr.ga.area, KNX_addr.ga.line, KNX_addr.ga.member);
@@ -886,7 +886,7 @@ void HandleKNXConfiguration(void)
{
if (!HttpCheckPriviledgedAccess()) { return; }
- AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_HTTP D_CONFIGURE_KNX));
+ AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_HTTP D_CONFIGURE_KNX));
char tmp[100];
String stmp;
@@ -1038,7 +1038,7 @@ void KNX_Save_Settings(void)
Settings.flag.knx_enabled = Webserver->hasArg("b1");
Settings.flag.knx_enable_enhancement = Webserver->hasArg("b2");
- AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_KNX D_ENABLED ": %d, " D_KNX_ENHANCEMENT ": %d"),
+ AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_KNX D_ENABLED ": %d, " D_KNX_ENHANCEMENT ": %d"),
Settings.flag.knx_enabled, Settings.flag.knx_enable_enhancement );
stmp = Webserver->arg("area");
@@ -1049,26 +1049,26 @@ void KNX_Save_Settings(void)
KNX_addr.pa.member = stmp.toInt();
Settings.knx_physsical_addr = KNX_addr.value;
knx.physical_address_set( KNX_addr ); // Set Physical KNX Address of the device
- AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_KNX D_KNX_PHYSICAL_ADDRESS ": %d.%d.%d "),
+ AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_KNX D_KNX_PHYSICAL_ADDRESS ": %d.%d.%d "),
KNX_addr.pa.area, KNX_addr.pa.line, KNX_addr.pa.member );
- AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_KNX "GA: %d"),
+ AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_KNX "GA: %d"),
Settings.knx_GA_registered );
for (uint32_t i = 0; i < Settings.knx_GA_registered ; ++i)
{
KNX_addr.value = Settings.knx_GA_addr[i];
- AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_KNX "GA #%d: %s " D_TO " %d/%d/%d"),
+ AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_KNX "GA #%d: %s " D_TO " %d/%d/%d"),
i+1, device_param_ga[Settings.knx_GA_param[i]-1],
KNX_addr.ga.area, KNX_addr.ga.line, KNX_addr.ga.member );
}
- AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_KNX "CB: %d"),
+ AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_KNX "CB: %d"),
Settings.knx_CB_registered );
for (uint32_t i = 0; i < Settings.knx_CB_registered ; ++i)
{
KNX_addr.value = Settings.knx_CB_addr[i];
- AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_KNX "CB #%d: %d/%d/%d " D_TO " %s"),
+ AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_KNX "CB #%d: %d/%d/%d " D_TO " %s"),
i+1,
KNX_addr.ga.area, KNX_addr.ga.line, KNX_addr.ga.member,
device_param_cb[Settings.knx_CB_param[i]-1] );
@@ -1097,7 +1097,7 @@ void CmndKnxTxCmnd(void)
knx.write_1bit(KNX_addr, !(XdrvMailbox.payload == 0));
}
- AddLog_P(LOG_LEVEL_INFO, PSTR(D_LOG_KNX "%s = %d " D_SENT_TO " %d.%d.%d"),
+ AddLog(LOG_LEVEL_INFO, PSTR(D_LOG_KNX "%s = %d " D_SENT_TO " %d.%d.%d"),
device_param_ga[XdrvMailbox.index + KNX_SLOT1 -2], !(XdrvMailbox.payload == 0),
KNX_addr.ga.area, KNX_addr.ga.line, KNX_addr.ga.member);
@@ -1126,7 +1126,7 @@ void CmndKnxTxVal(void)
knx.write_4byte_float(KNX_addr, tempvar);
}
- AddLog_P(LOG_LEVEL_INFO, PSTR(D_LOG_KNX "%s = %s " D_SENT_TO " %d.%d.%d"),
+ AddLog(LOG_LEVEL_INFO, PSTR(D_LOG_KNX "%s = %s " D_SENT_TO " %d.%d.%d"),
device_param_ga[XdrvMailbox.index + KNX_SLOT1 -2], XdrvMailbox.data,
KNX_addr.ga.area, KNX_addr.ga.line, KNX_addr.ga.member);
@@ -1153,7 +1153,7 @@ void CmndKnxTxScene(void)
knx.write_1byte_uint(KNX_addr, tempvar);
}
- AddLog_P(LOG_LEVEL_INFO, PSTR(D_LOG_KNX "%s = %s " D_SENT_TO " %d.%d.%d"),
+ AddLog(LOG_LEVEL_INFO, PSTR(D_LOG_KNX "%s = %s " D_SENT_TO " %d.%d.%d"),
device_param_ga[KNX_SCENE-1], XdrvMailbox.data,
KNX_addr.ga.area, KNX_addr.ga.line, KNX_addr.ga.member);
ResponseCmndIdxChar (XdrvMailbox.data);
@@ -1208,13 +1208,13 @@ void CmndKnxGa(void)
{
if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_KNX_GA)) {
if (XdrvMailbox.data_len) {
- if (strchr(XdrvMailbox.data, ',') != nullptr) { // Process parameter entry
- char sub_string[XdrvMailbox.data_len];
+ if (ArgC() > 1) { // Process parameter entry
+ char argument[XdrvMailbox.data_len];
- int ga_option = atoi(subStr(sub_string, XdrvMailbox.data, ",", 1));
- int ga_area = atoi(subStr(sub_string, XdrvMailbox.data, ",", 2));
- int ga_line = atoi(subStr(sub_string, XdrvMailbox.data, ",", 3));
- int ga_member = atoi(subStr(sub_string, XdrvMailbox.data, ",", 4));
+ int ga_option = atoi(ArgV(argument, 1));
+ int ga_area = atoi(ArgV(argument, 2));
+ int ga_line = atoi(ArgV(argument, 3));
+ int ga_member = atoi(ArgV(argument, 4));
if ( ((ga_area == 0) && (ga_line == 0) && (ga_member == 0))
|| (ga_area > 31) || (ga_line > 7) || (ga_member > 255)
@@ -1259,13 +1259,13 @@ void CmndKnxCb(void)
{
if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_KNX_CB)) {
if (XdrvMailbox.data_len) {
- if (strchr(XdrvMailbox.data, ',') != nullptr) { // Process parameter entry
- char sub_string[XdrvMailbox.data_len];
+ if (ArgC() > 1) { // Process parameter entry
+ char argument[XdrvMailbox.data_len];
- int cb_option = atoi(subStr(sub_string, XdrvMailbox.data, ",", 1));
- int cb_area = atoi(subStr(sub_string, XdrvMailbox.data, ",", 2));
- int cb_line = atoi(subStr(sub_string, XdrvMailbox.data, ",", 3));
- int cb_member = atoi(subStr(sub_string, XdrvMailbox.data, ",", 4));
+ int cb_option = atoi(ArgV(argument, 1));
+ int cb_area = atoi(ArgV(argument, 2));
+ int cb_line = atoi(ArgV(argument, 3));
+ int cb_member = atoi(ArgV(argument, 4));
if ( ((cb_area == 0) && (cb_line == 0) && (cb_member == 0))
|| (cb_area > 31) || (cb_line > 7) || (cb_member > 255)
diff --git a/tasmota/xdrv_12_home_assistant.ino b/tasmota/xdrv_12_home_assistant.ino
index bf4664886..07dcf9f6b 100644
--- a/tasmota/xdrv_12_home_assistant.ino
+++ b/tasmota/xdrv_12_home_assistant.ino
@@ -181,7 +181,7 @@ int hass_tele_period = 0;
// NEW DISCOVERY
const char HASS_DISCOVER_DEVICE[] PROGMEM = // Basic parameters for Discovery
- "{\"ip\":\"%s\"," // IP Address
+ "{\"ip\":\"%_I\"," // IP Address
"\"dn\":\"%s\"," // Device Name
"\"fn\":[%s]," // Friendly Names
"\"hn\":\"%s\"," // Host Name
@@ -294,7 +294,7 @@ void NewHAssDiscovery(void)
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");
+ snprintf_P(stemp2, sizeof(stemp2), PSTR("%s%s%s"), stemp2, (i > 0 ? "," : ""), (i < maxfn) ? fname : PSTR("null"));
}
stemp3[0] = '\0';
@@ -304,7 +304,7 @@ void NewHAssDiscovery(void)
char sname[TOPSZ];
snprintf_P(sname, sizeof(sname), PSTR("\"%s\""), GetSwitchText(i).c_str());
snprintf_P(stemp3, sizeof(stemp3), PSTR("%s%s%d"), stemp3, (i > 0 ? "," : ""), (PinUsed(GPIO_SWT1, i) & Settings.flag5.mqtt_switches) ? Settings.switchmode[i] : -1);
- snprintf_P(stemp4, sizeof(stemp4), PSTR("%s%s%s"), stemp4, (i > 0 ? "," : ""), (PinUsed(GPIO_SWT1, i) & Settings.flag5.mqtt_switches) ? sname : "null");
+ snprintf_P(stemp4, sizeof(stemp4), PSTR("%s%s%s"), stemp4, (i > 0 ? "," : ""), (PinUsed(GPIO_SWT1, i) & Settings.flag5.mqtt_switches) ? sname : PSTR("null"));
}
stemp5[0] = '\0';
@@ -336,9 +336,9 @@ void NewHAssDiscovery(void)
// Send empty message if new discovery is disabled
TasmotaGlobal.masterlog_level = 4; // Hide topic on clean and remove use weblog 4 to show it
if (!Settings.flag.hass_discovery) { // HassDiscoveryRelays(relays)
- Response_P(HASS_DISCOVER_DEVICE, WiFi.localIP().toString().c_str(), SettingsText(SET_DEVICENAME),
+ Response_P(HASS_DISCOVER_DEVICE, (uint32_t)WiFi.localIP(), SettingsText(SET_DEVICENAME),
stemp2, TasmotaGlobal.hostname, unique_id, ModuleName().c_str(), TuyaMod, iFanMod, GetStateText(0), GetStateText(1), GetStateText(2), GetStateText(3),
- TasmotaGlobal.version, TasmotaGlobal.mqtt_topic, SettingsText(SET_MQTT_FULLTOPIC), SUB_PREFIX, PUB_PREFIX, PUB_PREFIX2, Hass.RelLst, stemp3, stemp4,
+ TasmotaGlobal.version, TasmotaGlobal.mqtt_topic, SettingsText(SET_MQTT_FULLTOPIC), PSTR(SUB_PREFIX), PSTR(PUB_PREFIX), PSTR(PUB_PREFIX2), Hass.RelLst, stemp3, stemp4,
stemp5, Settings.flag.mqtt_response, Settings.flag.button_swap, Settings.flag.button_single, Settings.flag.decimal_text, Settings.flag.not_power_linked,
Settings.flag.hass_light, Settings.flag3.pwm_multi_channels, Settings.flag3.mqtt_buttons, Settings.flag4.alexa_ct_range, Settings.flag5.mqtt_switches,
Settings.flag5.fade_fixed_duration, light_controller.isCTRGBLinked(), Light.subtype, stemp6);
@@ -1025,7 +1025,7 @@ void HAssAnnounceDeviceInfoAndStatusSensor(void)
if (!Settings.flag.hass_discovery) {
TasmotaGlobal.masterlog_level = 0;
- AddLog_P(LOG_LEVEL_INFO, PSTR(D_LOG_LOG "Home Assistant MQTT Discovery disabled."));
+ AddLog(LOG_LEVEL_INFO, PSTR(D_LOG_LOG "Home Assistant MQTT Discovery disabled."));
}
}
@@ -1033,10 +1033,10 @@ void HAssPublishStatus(void)
{
Response_P(PSTR("{\"" D_JSON_VERSION "\":\"%s%s\",\"" D_JSON_BUILDDATETIME "\":\"%s\",\"" D_CMND_MODULE " or " D_CMND_TEMPLATE"\":\"%s\","
"\"" D_JSON_RESTARTREASON "\":\"%s\",\"" D_JSON_UPTIME "\":\"%s\",\"" D_CMND_HOSTNAME "\":\"%s\","
- "\"" D_CMND_IPADDRESS "\":\"%s\",\"" D_JSON_RSSI "\":\"%d\",\"" D_JSON_SIGNAL " (dBm)""\":\"%d\","
+ "\"" D_CMND_IPADDRESS "\":\"%_I\",\"" D_JSON_RSSI "\":\"%d\",\"" D_JSON_SIGNAL " (dBm)""\":\"%d\","
"\"WiFi " D_JSON_LINK_COUNT "\":%d,\"WiFi " D_JSON_DOWNTIME "\":\"%s\",\"" D_JSON_MQTT_COUNT "\":%d,\"LoadAvg\":%lu}"),
TasmotaGlobal.version, TasmotaGlobal.image_name, GetBuildDateAndTime().c_str(), ModuleName().c_str(), GetResetReason().c_str(),
- GetUptime().c_str(), TasmotaGlobal.hostname, WiFi.localIP().toString().c_str(), WifiGetRssiAsQuality(WiFi.RSSI()),
+ GetUptime().c_str(), TasmotaGlobal.hostname, (uint32_t)WiFi.localIP(), WifiGetRssiAsQuality(WiFi.RSSI()),
WiFi.RSSI(), WifiLinkCount(), WifiDowntime().c_str(), MqttConnectCount(), TasmotaGlobal.loop_load_avg);
MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_HASS_STATE));
}
diff --git a/tasmota/xdrv_13_display.ino b/tasmota/xdrv_13_display.ino
index a6e81b711..a9b9f7d33 100755
--- a/tasmota/xdrv_13_display.ino
+++ b/tasmota/xdrv_13_display.ino
@@ -28,12 +28,12 @@ Renderer *renderer;
enum ColorType { COLOR_BW, COLOR_COLOR };
-#ifndef MAXBUTTONS
-#define MAXBUTTONS 16
+#ifndef MAX_TOUCH_BUTTONS
+#define MAX_TOUCH_BUTTONS 16
#endif
#ifdef USE_TOUCH_BUTTONS
-VButton *buttons[MAXBUTTONS];
+VButton *buttons[MAX_TOUCH_BUTTONS];
#endif
// drawing color is WHITE
@@ -41,7 +41,7 @@ VButton *buttons[MAXBUTTONS];
uint16_t fg_color = 1;
uint16_t bg_color = 0;
uint8_t color_type = COLOR_BW;
-uint8_t auto_draw=1;
+uint8_t auto_draw = 1;
const uint8_t DISPLAY_MAX_DRIVERS = 16; // Max number of display drivers/models supported by xdsp_interface.ino
const uint8_t DISPLAY_MAX_COLS = 64; // Max number of columns allowed with command DisplayCols
@@ -497,7 +497,7 @@ void DisplayText(void)
cp += var;
linebuf[fill] = 0;
break;
-#if (defined(USE_SCRIPT_FATFS) && defined(USE_SCRIPT)) || defined(UFILESYSTEM)
+#if (defined(USE_SCRIPT_FATFS) && defined(USE_SCRIPT)) || defined(USE_UFILESYS)
case 'P':
{ char *ep=strchr(cp,':');
if (ep) {
@@ -682,7 +682,7 @@ void DisplayText(void)
RedrawGraph(temp,temp1);
break;
}
-#if (defined(USE_SCRIPT_FATFS) && defined(USE_SCRIPT)) || defined(UFILESYSTEM)
+#if (defined(USE_SCRIPT_FATFS) && defined(USE_SCRIPT)) || defined(USE_UFILESYS)
if (*cp=='s') {
cp++;
var=atoiv(cp,&temp);
@@ -772,7 +772,7 @@ void DisplayText(void)
if (*cp=='d') dis=1;
cp++;
var=atoiv(cp,&num);
- num=num%MAXBUTTONS;
+ num=num%MAX_TOUCH_BUTTONS;
cp+=var;
if (buttons[num]) {
buttons[num]->vpower.disable=dis;
@@ -791,7 +791,7 @@ void DisplayText(void)
cp+=var;
cp++;
uint8_t bflags=num>>8;
- num=num%MAXBUTTONS;
+ num=num%MAX_TOUCH_BUTTONS;
var=atoiv(cp,&gxp);
cp+=var;
cp++;
@@ -1045,7 +1045,7 @@ void DisplayLogBufferInit(void)
DisplayLogBufferAdd(buffer);
snprintf_P(buffer, sizeof(buffer), PSTR(D_JSON_MAC " %s"), NetworkMacAddress().c_str());
DisplayLogBufferAdd(buffer);
- snprintf_P(buffer, sizeof(buffer), PSTR("IP %s"), NetworkAddress().toString().c_str());
+ ext_snprintf_P(buffer, sizeof(buffer), PSTR("IP %_I"), (uint32_t)NetworkAddress());
DisplayLogBufferAdd(buffer);
if (!TasmotaGlobal.global_state.wifi_down) {
snprintf_P(buffer, sizeof(buffer), PSTR(D_JSON_SSID " %s"), SettingsText(SET_STASSID1 + Settings.sta_active));
@@ -1284,10 +1284,12 @@ void DisplayInitDriver(void)
if (renderer) {
renderer->setTextFont(Settings.display_font);
renderer->setTextSize(Settings.display_size);
+ // force opaque mode
+ renderer->setDrawMode(0);
}
-// AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "Display model %d"), Settings.display_model);
+// AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "Display model %d"), Settings.display_model);
if (Settings.display_model) {
TasmotaGlobal.devices_present++;
@@ -1310,7 +1312,7 @@ void DisplaySetPower(void)
{
disp_power = bitRead(XdrvMailbox.index, disp_device -1);
-//AddLog_P(LOG_LEVEL_DEBUG, PSTR("DSP: Power %d"), disp_power);
+//AddLog(LOG_LEVEL_DEBUG, PSTR("DSP: Power %d"), disp_power);
if (Settings.display_model) {
if (!renderer) {
@@ -1573,7 +1575,7 @@ char get_jpeg_size(unsigned char* data, unsigned int data_size, unsigned short *
#endif // JPEG_PICTS
#endif // ESP32
-#if (defined(USE_SCRIPT_FATFS) && defined(USE_SCRIPT)) || defined(UFILESYSTEM)
+#if (defined(USE_SCRIPT_FATFS) && defined(USE_SCRIPT)) || defined(USE_UFILESYS)
extern FS *ufsp;
#define XBUFF_LEN 128
void Draw_RGB_Bitmap(char *file,uint16_t xp, uint16_t yp, bool inverted ) {
@@ -1918,7 +1920,7 @@ void DisplayCheckGraph() {
}
-#if (defined(USE_SCRIPT_FATFS) && defined(USE_SCRIPT)) || defined(UFILESYSTEM)
+#if (defined(USE_SCRIPT_FATFS) && defined(USE_SCRIPT)) || defined(USE_UFILESYS)
#ifdef ESP32
#include
#endif
@@ -2174,7 +2176,7 @@ uint8_t vbutt=0;
if (!(tbstate[tbut] & 1)) {
// pressed
tbstate[tbut] |= 1;
- //AddLog_P(LOG_LEVEL_INFO, PSTR("tbut: %d pressed"), tbut);
+ //AddLog(LOG_LEVEL_INFO, PSTR("tbut: %d pressed"), tbut);
Touch_MQTT(tbut, "BIB", tbstate[tbut] & 1);
}
}
@@ -2184,9 +2186,9 @@ uint8_t vbutt=0;
rotconvert(&pLoc.x, &pLoc.y);
- //AddLog_P(LOG_LEVEL_INFO, PSTR("touch %d - %d"), pLoc.x, pLoc.y);
+ //AddLog(LOG_LEVEL_INFO, PSTR("touch %d - %d"), pLoc.x, pLoc.y);
// now must compare with defined buttons
- for (uint8_t count=0; countvpower.disable) {
if (buttons[count]->contains(pLoc.x, pLoc.y)) {
// did hit
@@ -2231,11 +2233,11 @@ uint8_t vbutt=0;
// released
tbstate[tbut] &= 0xfe;
Touch_MQTT(tbut, "BIB", tbstate[tbut] & 1);
- //AddLog_P(LOG_LEVEL_INFO, PSTR("tbut: %d released"), tbut);
+ //AddLog(LOG_LEVEL_INFO, PSTR("tbut: %d released"), tbut);
}
}
#endif
- for (uint8_t count=0; countpress(false);
if (buttons[count]->justReleased()) {
diff --git a/tasmota/xdrv_14_mp3.ino b/tasmota/xdrv_14_mp3.ino
index b0af75e57..2df04da58 100644
--- a/tasmota/xdrv_14_mp3.ino
+++ b/tasmota/xdrv_14_mp3.ino
@@ -19,6 +19,14 @@
--------------------------------------------------------------------------------------------
Version yyyymmdd Action Description
--------------------------------------------------------------------------------------------
+ 1.0.0.5 20210121 added - support for DY_SV17F Player (#define USE_DY_SV17F)
+ - cmds supported:
+ - track
+ - stop
+ - volume
+ - play
+ - play /path
+
1.0.0.4 20181003 added - MP3Reset command in case that the player do rare things
- and needs a reset, the default volume will be set again too
added - MP3_CMD_RESET_VALUE for the player reset function
@@ -148,6 +156,60 @@ void MP3PlayerInit(void) {
return;
}
+
+#ifdef USE_DY_SV17F
+
+/*********************************************************************************************\
+ * specific for DY_SV17F
+ * create the MP3 commands payload, and send it via serial interface to the MP3 player
+ * only track,play,stop and volume supported
+\*********************************************************************************************/
+
+void MP3_SendCmd(uint8_t *scmd, uint8_t len) {
+uint16_t sum = 0;
+ for (uint32_t cnt = 0; cnt < len; cnt++) {
+ sum += scmd[cnt];
+ }
+ scmd[len] = sum;
+ MP3Player->write(scmd, len + 1);
+}
+
+void MP3_CMD(uint8_t mp3cmd, uint16_t val) {
+ uint8_t scmd[8];
+ uint8_t len = 0;
+ scmd[0]=0xAA;
+ switch (mp3cmd) {
+ case MP3_CMD_TRACK:
+ scmd[1]=0x07;
+ scmd[2]=0x02;
+ scmd[3]=val>>8;
+ scmd[4]=val;
+ MP3_SendCmd(scmd, 5);
+ case MP3_CMD_PLAY:
+ scmd[1]=0x02;
+ scmd[2]=0x00;
+ scmd[3]=0xAC;
+ len = 4;
+ break;
+ case MP3_CMD_STOP:
+ scmd[1]=0x10;
+ scmd[2]=0x00;
+ scmd[3]=0xBA;
+ len = 4;
+ break;
+ case MP3_CMD_VOLUME:
+ scmd[1]=0x13;
+ scmd[2]=0x01;
+ scmd[3]=val;
+ len = 4;
+ break;
+ default:
+ return;
+ }
+ MP3_SendCmd(scmd, len);
+}
+
+#else
/*********************************************************************************************\
* create the MP3 commands payload, and send it via serial interface to the MP3 player
* data length is 6 = 6 bytes [FF 06 09 00 00 00] but not counting the start, end, and verification.
@@ -173,7 +235,7 @@ void MP3_CMD(uint8_t mp3cmd,uint16_t val) {
}
return;
}
-
+#endif // USE_DY_SV17F
/*********************************************************************************************\
* check the MP3 commands
\*********************************************************************************************/
@@ -202,7 +264,9 @@ bool MP3PlayerCmd(void) {
}
Response_P(S_JSON_MP3_COMMAND_NVALUE, command, XdrvMailbox.payload);
break;
+#ifndef USE_DY_SV17F
case CMND_MP3_PLAY:
+#endif // USE_DY_SV17F
case CMND_MP3_PAUSE:
case CMND_MP3_STOP:
case CMND_MP3_RESET:
@@ -213,6 +277,32 @@ bool MP3PlayerCmd(void) {
if (command_code == CMND_MP3_RESET) { MP3_CMD(MP3_CMD_RESET, 0); }
Response_P(S_JSON_MP3_COMMAND, command, XdrvMailbox.payload);
break;
+
+#ifdef USE_DY_SV17F
+ case CMND_MP3_PLAY:
+ if (XdrvMailbox.data_len > 0) {
+ uint8_t scmd[64];
+ scmd[0] = 0xAA;
+ scmd[1] = 0x08;
+ scmd[2] = XdrvMailbox.data_len + 1;
+ scmd[3] = 2;
+ char *cp = XdrvMailbox.data;
+ scmd[4] = *cp;
+ for (uint8_t i = 1; i < XdrvMailbox.data_len; i++) {
+ if (cp[i]=='.') {
+ scmd[i + 4] = '*';
+ } else {
+ scmd[i + 4] = toupper(cp[i]);
+ }
+ }
+ MP3_SendCmd(scmd, XdrvMailbox.data_len + 4);
+ Response_P(S_JSON_COMMAND_SVALUE, command, XdrvMailbox.data);
+ } else {
+ MP3_CMD(MP3_CMD_PLAY, 0);
+ Response_P(S_JSON_MP3_COMMAND, command, XdrvMailbox.payload);
+ }
+ break;
+#endif // USE_DY_SV17F
default:
// else for Unknown command
serviced = false;
diff --git a/tasmota/xdrv_15_pca9685.ino b/tasmota/xdrv_15_pca9685.ino
index 950a7df2b..254103d0a 100644
--- a/tasmota/xdrv_15_pca9685.ino
+++ b/tasmota/xdrv_15_pca9685.ino
@@ -119,20 +119,20 @@ bool PCA9685_Command(void)
serviced = false;
return serviced;
}
- char sub_string[XdrvMailbox.data_len];
+ char argument[XdrvMailbox.data_len];
for (uint32_t ca=0;ca 1) {
- uint16_t new_freq = atoi(subStr(sub_string, XdrvMailbox.data, ",", 2));
+ uint16_t new_freq = atoi(ArgV(argument, 2));
if ((new_freq >= 24) && (new_freq <= 1526)) {
PCA9685_SetPWMfreq(new_freq);
Response_P(PSTR("{\"PCA9685\":{\"PWMF\":%i, \"Result\":\"OK\"}}"),new_freq);
@@ -143,23 +143,23 @@ bool PCA9685_Command(void)
return serviced;
}
}
- if (!strcmp(subStr(sub_string, XdrvMailbox.data, ",", 1),"PWM")) {
+ if (!strcmp(ArgV(argument, 1),"PWM")) {
if (paramcount > 1) {
- uint8_t pin = atoi(subStr(sub_string, XdrvMailbox.data, ",", 2));
+ uint8_t pin = atoi(ArgV(argument, 2));
if (paramcount > 2) {
- if (!strcmp(subStr(sub_string, XdrvMailbox.data, ",", 3), "ON")) {
+ if (!strcmp(ArgV(argument, 3), "ON")) {
PCA9685_SetPWM(pin, 4096, false);
Response_P(PSTR("{\"PCA9685\":{\"PIN\":%i,\"PWM\":%i}}"),pin,4096);
serviced = true;
return serviced;
}
- if (!strcmp(subStr(sub_string, XdrvMailbox.data, ",", 3), "OFF")) {
+ if (!strcmp(ArgV(argument, 3), "OFF")) {
PCA9685_SetPWM(pin, 0, false);
Response_P(PSTR("{\"PCA9685\":{\"PIN\":%i,\"PWM\":%i}}"),pin,0);
serviced = true;
return serviced;
}
- uint16_t pwm = atoi(subStr(sub_string, XdrvMailbox.data, ",", 3));
+ uint16_t pwm = atoi(ArgV(argument, 3));
if ((pin >= 0 && pin <= 15) && (pwm >= 0 && pwm <= 4096)) {
PCA9685_SetPWM(pin, pwm, false);
Response_P(PSTR("{\"PCA9685\":{\"PIN\":%i,\"PWM\":%i}}"),pin,pwm);
diff --git a/tasmota/xdrv_16_tuyamcu.ino b/tasmota/xdrv_16_tuyamcu.ino
index a291d2e11..1f8da77e1 100644
--- a/tasmota/xdrv_16_tuyamcu.ino
+++ b/tasmota/xdrv_16_tuyamcu.ino
@@ -148,7 +148,7 @@ void CmndTuyaSend(void) {
TuyaRequestState(8);
} else if (XdrvMailbox.index == 9) { // TuyaSend Topic Toggle
Settings.tuyamcu_topic = !Settings.tuyamcu_topic;
- AddLog_P(LOG_LEVEL_INFO, PSTR("TYA: TuyaMCU Stat Topic %s"), (Settings.tuyamcu_topic ? PSTR("enabled") : PSTR("disabled")));
+ AddLog(LOG_LEVEL_INFO, PSTR("TYA: TuyaMCU Stat Topic %s"), (Settings.tuyamcu_topic ? PSTR("enabled") : PSTR("disabled")));
} else {
if (XdrvMailbox.data_len > 0) {
@@ -207,7 +207,7 @@ void CmndTuyaMcu(void) {
TuyaAddMcuFunc(parm[0], parm[1]);
TasmotaGlobal.restart_flag = 2;
} else {
- AddLog_P(LOG_LEVEL_ERROR, PSTR("TYA: TuyaMcu Invalid function id=%d"), parm[0]);
+ AddLog(LOG_LEVEL_ERROR, PSTR("TYA: TuyaMcu Invalid function id=%d"), parm[0]);
}
}
@@ -626,9 +626,9 @@ void LightSerialDuty(uint16_t duty, char *hex_char, uint8_t TuyaIdx)
} else {
duty = changeUIntScale(duty, 0, 100, 0, Settings.dimmer_hw_max);
}
- AddLog_P(LOG_LEVEL_DEBUG, PSTR("TYA: Send dim skipped value %d for dpid %d"), duty, dpid); // due to 0 or already set
+ AddLog(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"));
+ AddLog(LOG_LEVEL_DEBUG, PSTR("TYA: Cannot set dimmer. Dimmer Id unknown"));
}
}
@@ -638,7 +638,7 @@ void LightSerialDuty(uint16_t duty, char *hex_char, uint8_t TuyaIdx)
TuyaSendEnum(TuyaGetDpId(TUYA_MCU_FUNC_MODESET), 1);
}
TuyaSendString(dpid, hex_char);
- AddLog_P(LOG_LEVEL_DEBUG, PSTR("TYA: TX RGB hex %s to dpId %d"), hex_char, dpid);
+ AddLog(LOG_LEVEL_DEBUG, PSTR("TYA: TX RGB hex %s to dpId %d"), hex_char, dpid);
}
}
@@ -646,7 +646,7 @@ void TuyaRequestState(uint8_t state_type)
{
if (TuyaSerial) {
// Get current status of MCU
- AddLog_P(LOG_LEVEL_DEBUG, PSTR("TYA: Read MCU state"));
+ AddLog(LOG_LEVEL_DEBUG, PSTR("TYA: Read MCU state"));
Tuya.SuspendTopic = true;
Tuya.ignore_topic_timeout = millis() + 1000; // suppress /STAT topic for 1000ms to limit data
switch (state_type) {
@@ -680,23 +680,23 @@ void TuyaProcessStatePacket(void) {
dpDataLen = Tuya.buffer[dpidStart + 2] << 8 | Tuya.buffer[dpidStart + 3];
fnId = TuyaGetFuncId(Tuya.buffer[dpidStart]);
- AddLog_P(LOG_LEVEL_DEBUG, PSTR("TYA: fnId=%d is set for dpId=%d"), fnId, Tuya.buffer[dpidStart]);
+ AddLog(LOG_LEVEL_DEBUG, PSTR("TYA: fnId=%d is set for dpId=%d"), fnId, Tuya.buffer[dpidStart]);
if (Tuya.buffer[dpidStart + 1] == 1) { // Data Type 1
if (fnId >= TUYA_MCU_FUNC_REL1 && fnId <= TUYA_MCU_FUNC_REL8) {
- AddLog_P(LOG_LEVEL_DEBUG, PSTR("TYA: RX Relay-%d --> MCU State: %s Current State:%s"), fnId - TUYA_MCU_FUNC_REL1 + 1, Tuya.buffer[dpidStart + 4]?"On":"Off",bitRead(TasmotaGlobal.power, fnId - TUYA_MCU_FUNC_REL1)?"On":"Off");
+ AddLog(LOG_LEVEL_DEBUG, PSTR("TYA: RX Relay-%d --> MCU State: %s Current State:%s"), fnId - TUYA_MCU_FUNC_REL1 + 1, Tuya.buffer[dpidStart + 4]?"On":"Off",bitRead(TasmotaGlobal.power, fnId - TUYA_MCU_FUNC_REL1)?"On":"Off");
if ((TasmotaGlobal.power || Settings.light_dimmer > 0) && (Tuya.buffer[dpidStart + 4] != bitRead(TasmotaGlobal.power, fnId - TUYA_MCU_FUNC_REL1))) {
if (!Tuya.buffer[dpidStart + 4]) { PowerOff = true; }
ExecuteCommandPower(fnId - TUYA_MCU_FUNC_REL1 + 1, Tuya.buffer[dpidStart + 4], SRC_SWITCH); // send SRC_SWITCH? to use as flag to prevent loop from inbound states from faceplate interaction
}
} else if (fnId >= TUYA_MCU_FUNC_REL1_INV && fnId <= TUYA_MCU_FUNC_REL8_INV) {
- AddLog_P(LOG_LEVEL_DEBUG, PSTR("TYA: RX Relay-%d-Inverted --> MCU State: %s Current State:%s"), fnId - TUYA_MCU_FUNC_REL1_INV + 1, Tuya.buffer[dpidStart + 4]?"Off":"On",bitRead(TasmotaGlobal.power, fnId - TUYA_MCU_FUNC_REL1_INV) ^ 1?"Off":"On");
+ AddLog(LOG_LEVEL_DEBUG, PSTR("TYA: RX Relay-%d-Inverted --> MCU State: %s Current State:%s"), fnId - TUYA_MCU_FUNC_REL1_INV + 1, Tuya.buffer[dpidStart + 4]?"Off":"On",bitRead(TasmotaGlobal.power, fnId - TUYA_MCU_FUNC_REL1_INV) ^ 1?"Off":"On");
if (Tuya.buffer[dpidStart + 4] != bitRead(TasmotaGlobal.power, fnId - TUYA_MCU_FUNC_REL1_INV) ^ 1) {
ExecuteCommandPower(fnId - TUYA_MCU_FUNC_REL1_INV + 1, Tuya.buffer[dpidStart + 4] ^ 1, SRC_SWITCH); // send SRC_SWITCH? to use as flag to prevent loop from inbound states from faceplate interaction
if (Tuya.buffer[dpidStart + 4]) { PowerOff = true; }
}
} else if (fnId >= TUYA_MCU_FUNC_SWT1 && fnId <= TUYA_MCU_FUNC_SWT4) {
- AddLog_P(LOG_LEVEL_DEBUG, PSTR("TYA: RX Switch-%d --> MCU State: %d Current State:%d"),fnId - TUYA_MCU_FUNC_SWT1 + 1,Tuya.buffer[dpidStart + 4], SwitchGetVirtual(fnId - TUYA_MCU_FUNC_SWT1));
+ AddLog(LOG_LEVEL_DEBUG, PSTR("TYA: RX Switch-%d --> MCU State: %d Current State:%d"),fnId - TUYA_MCU_FUNC_SWT1 + 1,Tuya.buffer[dpidStart + 4], SwitchGetVirtual(fnId - TUYA_MCU_FUNC_SWT1));
if (SwitchGetVirtual(fnId - TUYA_MCU_FUNC_SWT1) != Tuya.buffer[dpidStart + 4]) {
SwitchSetVirtual(fnId - TUYA_MCU_FUNC_SWT1, Tuya.buffer[dpidStart + 4]);
@@ -747,7 +747,7 @@ void TuyaProcessStatePacket(void) {
Tuya.Levels[dimIndex] = changeUIntScale(packetValue, 0, Settings.dimmer_hw_max, 0, 100);
}
- AddLog_P(LOG_LEVEL_DEBUG, PSTR("TYA: RX value %d from dpId %d "), packetValue, Tuya.buffer[dpidStart]);
+ AddLog(LOG_LEVEL_DEBUG, PSTR("TYA: RX value %d from dpId %d "), packetValue, Tuya.buffer[dpidStart]);
if ((fnId == TUYA_MCU_FUNC_DIMMER) || (fnId == TUYA_MCU_FUNC_REPORT1) ||
(fnId == TUYA_MCU_FUNC_DIMMER2) || (fnId == TUYA_MCU_FUNC_REPORT2) ||
@@ -787,13 +787,13 @@ void TuyaProcessStatePacket(void) {
#ifdef USE_ENERGY_SENSOR
else if (tuya_energy_enabled && fnId == TUYA_MCU_FUNC_VOLTAGE) {
Energy.voltage[0] = (float)packetValue / 10;
- AddLog_P(LOG_LEVEL_DEBUG, PSTR("TYA: Rx ID=%d Voltage=%d"), Tuya.buffer[dpidStart], packetValue);
+ AddLog(LOG_LEVEL_DEBUG, PSTR("TYA: Rx ID=%d Voltage=%d"), Tuya.buffer[dpidStart], packetValue);
} else if (tuya_energy_enabled && fnId == TUYA_MCU_FUNC_CURRENT) {
Energy.current[0] = (float)packetValue / 1000;
- AddLog_P(LOG_LEVEL_DEBUG, PSTR("TYA: Rx ID=%d Current=%d"), Tuya.buffer[dpidStart], packetValue);
+ AddLog(LOG_LEVEL_DEBUG, PSTR("TYA: Rx ID=%d Current=%d"), Tuya.buffer[dpidStart], packetValue);
} else if (tuya_energy_enabled && fnId == TUYA_MCU_FUNC_POWER) {
Energy.active_power[0] = (float)packetValue / 10;
- AddLog_P(LOG_LEVEL_DEBUG, PSTR("TYA: Rx ID=%d Active_Power=%d"), Tuya.buffer[dpidStart], packetValue);
+ AddLog(LOG_LEVEL_DEBUG, PSTR("TYA: Rx ID=%d Active_Power=%d"), Tuya.buffer[dpidStart], packetValue);
if (RtcTime.valid) {
if (Tuya.lastPowerCheckTime != 0 && Energy.active_power[0] > 0) {
@@ -882,7 +882,7 @@ void TuyaLowPowerModePacketProcess(void) {
void TuyaHandleProductInfoPacket(void) {
uint16_t dataLength = Tuya.buffer[4] << 8 | Tuya.buffer[5];
char *data = &Tuya.buffer[6];
- AddLog_P(LOG_LEVEL_INFO, PSTR("TYA: MCU Product ID: %.*s"), dataLength, data);
+ AddLog(LOG_LEVEL_INFO, PSTR("TYA: MCU Product ID: %.*s"), dataLength, data);
}
void TuyaSendLowPowerSuccessIfNeeded(void) {
@@ -903,9 +903,9 @@ void TuyaNormalPowerModePacketProcess(void)
break;
case TUYA_CMD_HEARTBEAT:
- AddLog_P(LOG_LEVEL_DEBUG, PSTR("TYA: Heartbeat"));
+ AddLog(LOG_LEVEL_DEBUG, PSTR("TYA: Heartbeat"));
if (Tuya.buffer[6] == 0) {
- AddLog_P(LOG_LEVEL_DEBUG, PSTR("TYA: Detected MCU restart"));
+ AddLog(LOG_LEVEL_DEBUG, PSTR("TYA: Detected MCU restart"));
Tuya.wifi_state = -2;
}
break;
@@ -916,17 +916,17 @@ void TuyaNormalPowerModePacketProcess(void)
case TUYA_CMD_WIFI_RESET:
case TUYA_CMD_WIFI_SELECT:
- AddLog_P(LOG_LEVEL_DEBUG, PSTR("TYA: RX WiFi Reset"));
+ AddLog(LOG_LEVEL_DEBUG, PSTR("TYA: RX WiFi Reset"));
TuyaResetWifi();
break;
case TUYA_CMD_WIFI_STATE:
- AddLog_P(LOG_LEVEL_DEBUG, PSTR("TYA: RX WiFi LED set ACK"));
+ AddLog(LOG_LEVEL_DEBUG, PSTR("TYA: RX WiFi LED set ACK"));
Tuya.wifi_state = TuyaGetTuyaWifiState();
break;
case TUYA_CMD_MCU_CONF:
- AddLog_P(LOG_LEVEL_DEBUG, PSTR("TYA: RX MCU configuration Mode=%d"), Tuya.buffer[5]);
+ AddLog(LOG_LEVEL_DEBUG, PSTR("TYA: RX MCU configuration Mode=%d"), Tuya.buffer[5]);
if (Tuya.buffer[5] == 2) { // Processing by ESP module mode
uint8_t led1_gpio = Tuya.buffer[6];
@@ -954,7 +954,7 @@ void TuyaNormalPowerModePacketProcess(void)
break;
#endif
default:
- AddLog_P(LOG_LEVEL_DEBUG, PSTR("TYA: RX unknown command"));
+ AddLog(LOG_LEVEL_DEBUG, PSTR("TYA: RX unknown command"));
}
}
@@ -1039,7 +1039,7 @@ void TuyaInit(void)
// Get MCU Configuration
Tuya.SuspendTopic = true;
Tuya.ignore_topic_timeout = millis() + 1000; // suppress /STAT topic for 1000ms to avoid data overflow
- AddLog_P(LOG_LEVEL_DEBUG, PSTR("TYA: Request MCU configuration at %d bps"), baudrate);
+ AddLog(LOG_LEVEL_DEBUG, PSTR("TYA: Request MCU configuration at %d bps"), baudrate);
}
@@ -1175,7 +1175,7 @@ void TuyaSerialInput(void)
bool TuyaButtonPressed(void)
{
if (!XdrvMailbox.index && ((PRESSED == XdrvMailbox.payload) && (NOT_PRESSED == Button.last_state[XdrvMailbox.index]))) {
- AddLog_P(LOG_LEVEL_DEBUG, PSTR("TYA: Reset GPIO triggered"));
+ AddLog(LOG_LEVEL_DEBUG, PSTR("TYA: Reset GPIO triggered"));
TuyaResetWifi();
return true; // Reset GPIO served here
}
@@ -1204,7 +1204,7 @@ uint8_t TuyaGetTuyaWifiState(void) {
void TuyaSetWifiLed(void)
{
Tuya.wifi_state = TuyaGetTuyaWifiState();
- AddLog_P(LOG_LEVEL_DEBUG, PSTR("TYA: Set WiFi LED %d (%d)"), Tuya.wifi_state, WifiState());
+ AddLog(LOG_LEVEL_DEBUG, PSTR("TYA: Set WiFi LED %d (%d)"), Tuya.wifi_state, WifiState());
if (Tuya.low_power_mode) {
TuyaSendCmd(TUYA_LOW_POWER_CMD_WIFI_STATE, &Tuya.wifi_state, 1);
@@ -1295,7 +1295,7 @@ void TuyaSensorsShow(bool json)
GetTextIndexed(sname, sizeof(sname), (sensor-71), kTuyaSensors);
ResponseAppend_P(PSTR("\"%s\":%s"), sname,
- (Tuya.SensorsValid[sensor-71] ? dtostrfd(Tuya.Sensors[sensor-71], res, tempval) : "null"));
+ (Tuya.SensorsValid[sensor-71] ? dtostrfd(Tuya.Sensors[sensor-71], res, tempval) : PSTR("null")));
added = true;
}
#ifdef USE_WEBSERVER
@@ -1303,7 +1303,7 @@ void TuyaSensorsShow(bool json)
if (TuyaGetDpId(sensor) != 0) {
switch (sensor) {
case 71:
- WSContentSend_PD(HTTP_SNS_TEMP, "", dtostrfd(Tuya.Sensors[0], Settings.flag2.temperature_resolution, tempval), TempUnit());
+ WSContentSend_Temp("", Tuya.Sensors[0]);
break;
case 72:
WSContentSend_PD(PSTR("{s}" D_TEMPERATURE " Set{m}%s " D_UNIT_DEGREE "%c{e}"),
diff --git a/tasmota/xdrv_17_rcswitch.ino b/tasmota/xdrv_17_rcswitch.ino
index 8877544df..402929b19 100644
--- a/tasmota/xdrv_17_rcswitch.ino
+++ b/tasmota/xdrv_17_rcswitch.ino
@@ -56,7 +56,7 @@ void RfReceiveCheck(void) {
int protocol = mySwitch.getReceivedProtocol();
int delay = mySwitch.getReceivedDelay();
- AddLog_P(LOG_LEVEL_DEBUG, PSTR("RFR: Data 0x%lX (%u), Bits %d, Protocol %d, Delay %d"), data, data, bits, protocol, delay);
+ AddLog(LOG_LEVEL_DEBUG, PSTR("RFR: Data 0x%lX (%u), Bits %d, Protocol %d, Delay %d"), data, data, bits, protocol, delay);
uint32_t now = millis();
if ((now - rf_lasttime > RF_TIME_AVOID_DUPLICATE) && (data > 0)) {
@@ -130,7 +130,7 @@ void CmndRfProtocol(void) {
}
}
mySwitch.setReceiveProtocolMask(Settings.rf_protocol_mask);
-// AddLog_P(LOG_LEVEL_INFO, PSTR("RFR: CmndRfProtocol:: Start responce"));
+// AddLog(LOG_LEVEL_INFO, PSTR("RFR: CmndRfProtocol:: Start responce"));
Response_P(PSTR("{\"" D_CMND_RFPROTOCOL "\":\""));
bool gotone = false;
thisdat = 1;
diff --git a/tasmota/xdrv_18_armtronix_dimmers.ino b/tasmota/xdrv_18_armtronix_dimmers.ino
index 14fba8c7b..185cb98c0 100644
--- a/tasmota/xdrv_18_armtronix_dimmers.ino
+++ b/tasmota/xdrv_18_armtronix_dimmers.ino
@@ -61,11 +61,11 @@ void LightSerial2Duty(uint8_t duty1, uint8_t duty2)
ArmtronixSerial->print("\nDimmer2:");
ArmtronixSerial->println(duty2);
- AddLog_P(LOG_LEVEL_DEBUG, PSTR("ARM: Send Serial Packet Dim Values=%d,%d"), Armtronix.dim_state[0],Armtronix.dim_state[1]);
+ AddLog(LOG_LEVEL_DEBUG, PSTR("ARM: Send Serial Packet Dim Values=%d,%d"), Armtronix.dim_state[0],Armtronix.dim_state[1]);
} else {
Armtronix.ignore_dim = false;
- AddLog_P(LOG_LEVEL_DEBUG, PSTR("ARM: Send Dim Level skipped due to already set. Value=%d,%d"), Armtronix.dim_state[0],Armtronix.dim_state[1]);
+ AddLog(LOG_LEVEL_DEBUG, PSTR("ARM: Send Dim Level skipped due to already set. Value=%d,%d"), Armtronix.dim_state[0],Armtronix.dim_state[1]);
}
}
@@ -74,7 +74,7 @@ void ArmtronixRequestState(void)
{
if (ArmtronixSerial) {
// Get current status of MCU
- AddLog_P(LOG_LEVEL_DEBUG, PSTR("ARM: Request MCU state"));
+ AddLog(LOG_LEVEL_DEBUG, PSTR("ARM: Request MCU state"));
ArmtronixSerial->println("Status");
}
@@ -124,7 +124,7 @@ void ArmtronixSerialInput(void)
Armtronix.ignore_dim = true;
snprintf_P(scmnd, sizeof(scmnd), PSTR(D_CMND_CHANNEL "%d %d"),i+1, temp);
ExecuteCommand(scmnd,SRC_SWITCH);
- AddLog_P(LOG_LEVEL_DEBUG, PSTR("ARM: Send CMND_CHANNEL=%s"), scmnd );
+ AddLog(LOG_LEVEL_DEBUG, PSTR("ARM: Send CMND_CHANNEL=%s"), scmnd );
}
commaIndex = answer.indexOf(',',commaIndex+1);
}
@@ -148,7 +148,7 @@ void ArmtronixSetWifiLed(void)
break;
}
- AddLog_P(LOG_LEVEL_DEBUG, PSTR("ARM: Set WiFi LED to state %d (%d)"), wifi_state, WifiState());
+ AddLog(LOG_LEVEL_DEBUG, PSTR("ARM: Set WiFi LED to state %d (%d)"), wifi_state, WifiState());
char state = '0' + ((wifi_state & 1) > 0);
ArmtronixSerial->print("Setled:");
diff --git a/tasmota/xdrv_19_ps16dz_dimmer.ino b/tasmota/xdrv_19_ps16dz_dimmer.ino
index b44a6da1b..5c32168a2 100644
--- a/tasmota/xdrv_19_ps16dz_dimmer.ino
+++ b/tasmota/xdrv_19_ps16dz_dimmer.ino
@@ -43,7 +43,7 @@ struct PS16DZ {
void PS16DZSerialSend(const char *tx_buffer)
{
-// AddLog_P(LOG_LEVEL_DEBUG, PSTR("PSZ: Send %s"), tx_buffer);
+// AddLog(LOG_LEVEL_DEBUG, PSTR("PSZ: Send %s"), tx_buffer);
PS16DZSerial->print(tx_buffer);
PS16DZSerial->write(0x1B);
@@ -96,7 +96,7 @@ void PS16DZSerialInput(void)
Ps16dz.rx_buffer[Ps16dz.byte_counter++] = 0x00;
// AT+RESULT="sequence":"1554682835320"
-// AddLog_P(LOG_LEVEL_DEBUG, PSTR("PSZ: Rcvd %s"), Ps16dz.rx_buffer);
+// AddLog(LOG_LEVEL_DEBUG, PSTR("PSZ: Rcvd %s"), Ps16dz.rx_buffer);
if (!strncmp(Ps16dz.rx_buffer+3, "RESULT", 6)) {
@@ -118,7 +118,7 @@ void PS16DZSerialInput(void)
if (!strncmp(token2, "\"switch\"", 8)) {
bool switch_state = !strncmp(token3, "\"on\"", 4) ? true : false;
-// AddLog_P(LOG_LEVEL_DEBUG, PSTR("PSZ: Switch %d"), switch_state);
+// AddLog(LOG_LEVEL_DEBUG, PSTR("PSZ: Switch %d"), switch_state);
is_switch_change = (switch_state != TasmotaGlobal.power);
if (is_switch_change) {
@@ -128,7 +128,7 @@ void PS16DZSerialInput(void)
else if (!strncmp(token2, "\"bright\"", 8)) {
Ps16dz.dimmer = atoi(token3);
-// AddLog_P(LOG_LEVEL_DEBUG, PSTR("PSZ: Brightness %d"), Ps16dz.dimmer);
+// AddLog(LOG_LEVEL_DEBUG, PSTR("PSZ: Brightness %d"), Ps16dz.dimmer);
is_brightness_change = Ps16dz.dimmer != Settings.light_dimmer;
if (TasmotaGlobal.power && (Ps16dz.dimmer > 0) && is_brightness_change) {
@@ -138,7 +138,7 @@ void PS16DZSerialInput(void)
}
else if (!strncmp(token2, "\"sequence\"", 10)) {
-// AddLog_P(LOG_LEVEL_DEBUG, PSTR("PSZ: Sequence %s"), token3);
+// AddLog(LOG_LEVEL_DEBUG, PSTR("PSZ: Sequence %s"), token3);
}
token = strtok_r(nullptr, ",", &end_str);
@@ -146,7 +146,7 @@ void PS16DZSerialInput(void)
if (!is_brightness_change) {
-// AddLog_P(LOG_LEVEL_DEBUG, PSTR("PSZ: Update"));
+// AddLog(LOG_LEVEL_DEBUG, PSTR("PSZ: Update"));
PS16DZSerialSendOk();
}
diff --git a/tasmota/xdrv_20_hue.ino b/tasmota/xdrv_20_hue.ino
index 4da625847..4e677157d 100644
--- a/tasmota/xdrv_20_hue.ino
+++ b/tasmota/xdrv_20_hue.ino
@@ -222,8 +222,7 @@ void HueRespondToMSearch(void)
} else {
snprintf_P(message, sizeof(message), PSTR(D_FAILED_TO_SEND_RESPONSE));
}
- // Do not use AddLog_P( here (interrupt routine) if syslog or mqttlog is enabled. UDP/TCP will force exception 9
- AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_UPNP D_HUE " %s " D_TO " %s:%d"),
+ AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_UPNP D_HUE " %s " D_TO " %s:%d"),
message, udp_remote_ip.toString().c_str(), udp_remote_port);
}
@@ -427,31 +426,31 @@ String GetHueUserId(void)
void HandleUpnpSetupHue(void)
{
- AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_HTTP D_HUE_BRIDGE_SETUP));
+ AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_HTTP D_HUE_BRIDGE_SETUP));
String description_xml = Decompress(HUE_DESCRIPTION_XML_COMPRESSED,HUE_DESCRIPTION_XML_SIZE);
- description_xml.replace("{x1", WiFi.localIP().toString());
- description_xml.replace("{x2", HueUuid());
- description_xml.replace("{x3", HueSerialnumber());
+ description_xml.replace(F("{x1"), WiFi.localIP().toString());
+ description_xml.replace(F("{x2"), HueUuid());
+ description_xml.replace(F("{x3"), HueSerialnumber());
WSSend(200, CT_XML, description_xml);
}
void HueNotImplemented(String *path)
{
- AddLog_P(LOG_LEVEL_DEBUG_MORE, PSTR(D_LOG_HTTP D_HUE_API_NOT_IMPLEMENTED " (%s)"), path->c_str());
+ AddLog(LOG_LEVEL_DEBUG_MORE, PSTR(D_LOG_HTTP D_HUE_API_NOT_IMPLEMENTED " (%s)"), path->c_str());
- WSSend(200, CT_JSON, "{}");
+ WSSend(200, CT_JSON, PSTR("{}"));
}
void HueConfigResponse(String *response)
{
*response += Decompress(HueConfigResponse_JSON, HueConfigResponse_JSON_SIZE);
- response->replace("{ma", WiFi.macAddress());
- response->replace("{ip", WiFi.localIP().toString());
- response->replace("{ms", WiFi.subnetMask().toString());
- response->replace("{gw", WiFi.gatewayIP().toString());
- response->replace("{br", HueBridgeId());
- response->replace("{dt", GetDateAndTime(DT_UTC));
- response->replace("{id", GetHueUserId());
+ response->replace(F("{ma"), WiFi.macAddress());
+ response->replace(F("{ip"), WiFi.localIP().toString());
+ response->replace(F("{ms"), WiFi.subnetMask().toString());
+ response->replace(F("{gw"), WiFi.gatewayIP().toString());
+ response->replace(F("{br"), HueBridgeId());
+ response->replace(F("{dt"), GetDateAndTime(DT_UTC));
+ response->replace(F("{id"), GetHueUserId());
}
void HueConfig(String *path)
@@ -534,13 +533,13 @@ void HueLightStatus1(uint8_t device, String *response)
char * buf = (char*) malloc(buf_size); // temp buffer for strings, avoid stack
UnishoxStrings msg(HUE_LIGHTS);
- snprintf_P(buf, buf_size, PSTR("{\"on\":%s,"), (TasmotaGlobal.power & (1 << (device-1))) ? "true" : "false");
+ snprintf_P(buf, buf_size, PSTR("{\"on\":%s,"), (TasmotaGlobal.power & (1 << (device-1))) ? PSTR("true") : PSTR("false"));
// Brightness for all devices with PWM
if ((1 == echo_gen) || (LST_SINGLE <= local_light_subtype)) { // force dimmer for 1st gen Echo
snprintf_P(buf, buf_size, PSTR("%s\"bri\":%d,"), buf, bri);
}
if (LST_COLDWARM <= local_light_subtype) {
- snprintf_P(buf, buf_size, PSTR("%s\"colormode\":\"%s\","), buf, g_gotct ? "ct" : "hs");
+ snprintf_P(buf, buf_size, PSTR("%s\"colormode\":\"%s\","), buf, g_gotct ? PSTR("ct") : PSTR("hs"));
}
if (LST_RGB <= local_light_subtype) { // colors
if (prev_x_str[0] && prev_y_str[0]) {
@@ -680,7 +679,7 @@ void HueGlobalConfig(String *path) {
#endif // USE_ZIGBEE
response += F("},\"groups\":{},\"schedules\":{},\"config\":");
HueConfigResponse(&response);
- response += "}";
+ response += F("}");
WSSend(200, CT_JSON, response);
}
@@ -690,7 +689,7 @@ void HueAuthentication(String *path)
snprintf_P(response, sizeof(response), PSTR("[{\"success\":{\"username\":\"%s\"}}]"), GetHueUserId().c_str());
WSSend(200, CT_JSON, response);
- AddLog_P(LOG_LEVEL_DEBUG_MORE, PSTR(D_LOG_HTTP D_HUE " Authentication Result (%s)"), response);
+ AddLog(LOG_LEVEL_DEBUG_MORE, PSTR(D_LOG_HTTP D_HUE " Authentication Result (%s)"), response);
}
#ifdef USE_LIGHT
@@ -700,7 +699,7 @@ void CheckHue(String * response, bool &appending) {
for (uint32_t i = 1; i <= maxhue; i++) {
if (HueActive(i)) {
if (appending) { *response += ","; }
- *response += "\"";
+ *response += F("\"");
*response += EncodeLightId(i);
*response += F("\":{\"state\":");
HueLightStatus1(i, response);
@@ -735,7 +734,7 @@ void HueLightsCommand(uint8_t device, uint32_t device_id, String &response) {
on = hue_on.getBool();
snprintf_P(buf, buf_size,
msg[HUE_RESP_ON],
- device_id, on ? "true" : "false");
+ device_id, on ? PSTR("true") : PSTR("false"));
#ifdef USE_SHUTTER
if (ShutterState(device)) {
@@ -778,7 +777,7 @@ void HueLightsCommand(uint8_t device, uint32_t device_id, String &response) {
if (resp) { response += ","; }
snprintf_P(buf, buf_size,
msg[HUE_RESP_NUM],
- device_id, "bri", bri);
+ device_id, PSTR("bri"), bri);
response += buf;
if (LST_SINGLE <= Light.subtype) {
// extend bri value if set to max
@@ -805,7 +804,7 @@ void HueLightsCommand(uint8_t device, uint32_t device_id, String &response) {
RgbToHsb(rr, gg, bb, &hue, &sat, nullptr);
prev_hue = changeUIntScale(hue, 0, 360, 0, 65535); // calculate back prev_hue
prev_sat = (sat > 254 ? 254 : sat);
- //AddLog_P(LOG_LEVEL_DEBUG_MORE, "XY RGB (%d %d %d) HS (%d %d)", rr,gg,bb,hue,sat);
+ //AddLog(LOG_LEVEL_DEBUG_MORE, "XY RGB (%d %d %d) HS (%d %d)", rr,gg,bb,hue,sat);
if (resp) { response += ","; }
snprintf_P(buf, buf_size,
msg[HUE_RESP_XY],
@@ -824,7 +823,7 @@ void HueLightsCommand(uint8_t device, uint32_t device_id, String &response) {
if (resp) { response += ","; }
snprintf_P(buf, buf_size,
msg[HUE_RESP_NUM],
- device_id, "hue", hue);
+ device_id, PSTR("hue"), hue);
response += buf;
if (LST_RGB <= Light.subtype) {
// change range from 0..65535 to 0..360
@@ -843,7 +842,7 @@ void HueLightsCommand(uint8_t device, uint32_t device_id, String &response) {
if (resp) { response += ","; }
snprintf_P(buf, buf_size,
msg[HUE_RESP_NUM],
- device_id, "sat", sat);
+ device_id, PSTR("sat"), sat);
response += buf;
if (LST_RGB <= Light.subtype) {
// extend sat value if set to max
@@ -862,7 +861,7 @@ void HueLightsCommand(uint8_t device, uint32_t device_id, String &response) {
if (resp) { response += ","; }
snprintf_P(buf, buf_size,
msg[HUE_RESP_NUM],
- device_id, "ct", ct);
+ device_id, PSTR("ct"), ct);
response += buf;
if ((LST_COLDWARM == Light.subtype) || (LST_RGBW <= Light.subtype)) {
g_gotct = true;
@@ -874,7 +873,7 @@ void HueLightsCommand(uint8_t device, uint32_t device_id, String &response) {
if (change) {
#ifdef USE_SHUTTER
if (ShutterState(device)) {
- AddLog_P(LOG_LEVEL_DEBUG, PSTR("Settings.shutter_invert: %d"), Settings.shutter_options[device-1] & 1);
+ AddLog(LOG_LEVEL_DEBUG, PSTR("Settings.shutter_invert: %d"), Settings.shutter_options[device-1] & 1);
ShutterSetPosition(device, bri * 100.0f );
} else
#endif
@@ -923,7 +922,7 @@ void HueLights(String *path)
path->remove(0,path->indexOf(F("/lights"))); // Remove until /lights
if (path->endsWith(F("/lights"))) { // Got /lights
- response = "{";
+ response = F("{");
bool appending = false;
#ifdef USE_LIGHT
CheckHue(&response, appending);
@@ -934,7 +933,7 @@ void HueLights(String *path)
#ifdef USE_SCRIPT_HUE
Script_Check_Hue(&response);
#endif
- response += "}";
+ response += F("}");
}
else if (path->endsWith(F("/state"))) { // Got ID/state
path->remove(0,8); // Remove /lights/
@@ -962,7 +961,7 @@ void HueLights(String *path)
}
else if(path->indexOf(F("/lights/")) >= 0) { // Got /lights/ID
- AddLog_P(LOG_LEVEL_DEBUG_MORE, PSTR("/lights path=%s"), path->c_str());
+ AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("/lights path=%s"), path->c_str());
path->remove(0,8); // Remove /lights/
device_id = atoi(path->c_str());
device = DecodeLightId(device_id);
@@ -992,11 +991,11 @@ void HueLights(String *path)
#endif // USE_LIGHT
}
else {
- response = "{}";
+ response = F("{}");
code = 406;
}
exit:
- AddLog_P(LOG_LEVEL_DEBUG_MORE, PSTR(D_LOG_HTTP D_HUE " Result (%s)"), response.c_str());
+ AddLog(LOG_LEVEL_DEBUG_MORE, PSTR(D_LOG_HTTP D_HUE " Result (%s)"), response.c_str());
WSSend(code, CT_JSON, response);
}
@@ -1005,31 +1004,31 @@ void HueGroups(String *path)
/*
* http://tasmota/api/username/groups?1={"name":"Woonkamer","lights":[],"type":"Room","class":"Living room"})
*/
- String response = "{}";
+ String response(F("{}"));
uint8_t maxhue = (TasmotaGlobal.devices_present > MAX_HUE_DEVICES) ? MAX_HUE_DEVICES : TasmotaGlobal.devices_present;
- //AddLog_P(LOG_LEVEL_DEBUG_MORE, PSTR(D_LOG_HTTP D_HUE " HueGroups (%s)"), path->c_str());
+ //AddLog(LOG_LEVEL_DEBUG_MORE, PSTR(D_LOG_HTTP D_HUE " HueGroups (%s)"), path->c_str());
- if (path->endsWith("/0")) {
+ if (path->endsWith(F("/0"))) {
UnishoxStrings msg(HUE_LIGHTS);
response = msg[HUE_GROUP0_STATUS_JSON];
String lights = F("\"1\"");
for (uint32_t i = 2; i <= maxhue; i++) {
- lights += ",\"";
+ lights += F(",\"");
lights += EncodeLightId(i);
- lights += "\"";
+ lights += F("\"");
}
#ifdef USE_ZIGBEE
ZigbeeHueGroups(&response);
#endif // USE_ZIGBEE
- response.replace("{l1", lights);
+ response.replace(F("{l1"), lights);
#ifdef USE_LIGHT
HueLightStatus1(1, &response);
#endif // USE_LIGHT
response += F("}");
}
- AddLog_P(LOG_LEVEL_DEBUG_MORE, PSTR(D_LOG_HTTP D_HUE " HueGroups Result (%s)"), path->c_str());
+ AddLog(LOG_LEVEL_DEBUG_MORE, PSTR(D_LOG_HTTP D_HUE " HueGroups Result (%s)"), path->c_str());
WSSend(200, CT_JSON, response);
}
@@ -1051,10 +1050,10 @@ void HandleHueApi(String *path)
path->remove(0, 4); // remove /api
uint16_t apilen = path->length();
- AddLog_P(LOG_LEVEL_DEBUG_MORE, PSTR(D_LOG_HTTP D_HUE_API " (%s) from %s"), path->c_str(), Webserver->client().remoteIP().toString().c_str()); // HTP: Hue API (//lights/1/state) from 192.168.1.20
+ AddLog(LOG_LEVEL_DEBUG_MORE, PSTR(D_LOG_HTTP D_HUE_API " (%s) from %s"), path->c_str(), Webserver->client().remoteIP().toString().c_str()); // HTP: Hue API (//lights/1/state) from 192.168.1.20
for (args = 0; args < Webserver->args(); args++) {
String json = Webserver->arg(args);
- AddLog_P(LOG_LEVEL_DEBUG_MORE, PSTR(D_LOG_HTTP D_HUE_POST_ARGS " (%s)"), json.c_str()); // HTP: Hue POST args ({"on":false})
+ AddLog(LOG_LEVEL_DEBUG_MORE, PSTR(D_LOG_HTTP D_HUE_POST_ARGS " (%s)"), json.c_str()); // HTP: Hue POST args ({"on":false})
}
UnishoxStrings msg(HUE_API);
diff --git a/tasmota/xdrv_21_wemo.ino b/tasmota/xdrv_21_wemo.ino
index 18f793824..43ddd164d 100644
--- a/tasmota/xdrv_21_wemo.ino
+++ b/tasmota/xdrv_21_wemo.ino
@@ -73,8 +73,7 @@ void WemoRespondToMSearch(int echo_type)
} else {
snprintf_P(message, sizeof(message), PSTR(D_FAILED_TO_SEND_RESPONSE));
}
- // Do not use AddLog_P( here (interrupt routine) if syslog or mqttlog is enabled. UDP/TCP will force exception 9
- AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_UPNP D_WEMO " " D_JSON_TYPE " %d, %s " D_TO " %s:%d"),
+ AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_UPNP D_WEMO " " D_JSON_TYPE " %d, %s " D_TO " %s:%d"),
echo_type, message, udp_remote_ip.toString().c_str(), udp_remote_port);
}
@@ -265,7 +264,7 @@ const char WEMO_SETUP_XML[] PROGMEM =
/********************************************************************************************/
void LogUpnpWithClient(const char *msg) {
- AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_HTTP "%s from %s"), msg, Webserver->client().remoteIP().toString().c_str());
+ AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_HTTP "%s from %s"), msg, Webserver->client().remoteIP().toString().c_str());
}
void HandleUpnpEvent(void)
@@ -275,7 +274,7 @@ void HandleUpnpEvent(void)
char event[500];
strlcpy(event, Webserver->arg(0).c_str(), sizeof(event));
-// AddLog_P(LOG_LEVEL_DEBUG_MORE, PSTR("\n%s"), event);
+// AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("\n%s"), event);
//differentiate get and set state
char state = 'G';
diff --git a/tasmota/xdrv_21_wemo_multi.ino b/tasmota/xdrv_21_wemo_multi.ino
index 1a0b3ac6c..d6edfe5c0 100644
--- a/tasmota/xdrv_21_wemo_multi.ino
+++ b/tasmota/xdrv_21_wemo_multi.ino
@@ -256,7 +256,7 @@ public:
_deviceId = deviceId;
_webServer = webServer;
#ifdef USE_EMULATION_WEMO_DEBUG
- AddLog_P(LOG_LEVEL_DEBUG, PSTR("WMO: Device #%d listening on port %d"), _deviceId, _localPort);
+ AddLog(LOG_LEVEL_DEBUG, PSTR("WMO: Device #%d listening on port %d"), _deviceId, _localPort);
#endif
}
@@ -264,7 +264,7 @@ public:
_deviceId = deviceId;
_localPort = localPort;
#ifdef USE_EMULATION_WEMO_DEBUG
- AddLog_P(LOG_LEVEL_DEBUG, PSTR("WMO: Device #%d listening on port %d"), _deviceId, _localPort);
+ AddLog(LOG_LEVEL_DEBUG, PSTR("WMO: Device #%d listening on port %d"), _deviceId, _localPort);
#endif
_webServer = new ESP8266WebServer(_localPort);
@@ -278,7 +278,7 @@ public:
}
void WemoRespondToMSearch(int echo_type) {
-// AddLog_P(LOG_LEVEL_DEBUG, PSTR("WMO: WemoRespondToMSearch device #%d: %d"), _deviceId, echo_type);
+// AddLog(LOG_LEVEL_DEBUG, PSTR("WMO: WemoRespondToMSearch device #%d: %d"), _deviceId, echo_type);
char message[TOPSZ];
@@ -294,14 +294,14 @@ public:
PortUdp.write(response);
PortUdp.endPacket();
-// AddLog_P(LOG_LEVEL_DEBUG, PSTR("WMO: Sending packet device %d: %s"), _deviceId, response);
+// AddLog(LOG_LEVEL_DEBUG, PSTR("WMO: Sending packet device %d: %s"), _deviceId, response);
snprintf_P(message, sizeof(message), PSTR(D_RESPONSE_SENT));
} else {
snprintf_P(message, sizeof(message), PSTR(D_FAILED_TO_SEND_RESPONSE));
}
// Do not use AddLog_P here (interrupt routine) if syslog or mqttlog is enabled. UDP/TCP will force exception 9
- AddLog_P(LOG_LEVEL_DEBUG, PSTR("WMO: WeMo Type %d, %s to %s:%d"),
+ AddLog(LOG_LEVEL_DEBUG, PSTR("WMO: WeMo Type %d, %s to %s:%d"),
echo_type, message, udp_remote_ip.toString().c_str(), udp_remote_port);
}
@@ -319,7 +319,7 @@ private:
}
void LogUpnpWithClient(const char *msg) {
- AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_HTTP "%s from %s"), msg, _webServer->client().remoteIP().toString().c_str());
+ AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_HTTP "%s from %s"), msg, _webServer->client().remoteIP().toString().c_str());
}
void HandleUpnpEvent() {
@@ -329,8 +329,8 @@ private:
strlcpy(event, _webServer->arg(0).c_str(), sizeof(event));
#ifdef USE_EMULATION_WEMO_DEBUG
- //AddLog_P(LOG_LEVEL_DEBUG_MORE, PSTR("\n%s"), event);
- AddLog_P(LOG_LEVEL_DEBUG, PSTR("WMO: HandleUpnpEvent for deviceId %d: %s"), _deviceId, event);
+ //AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("\n%s"), event);
+ AddLog(LOG_LEVEL_DEBUG, PSTR("WMO: HandleUpnpEvent for deviceId %d: %s"), _deviceId, event);
#endif
//differentiate get and set state
@@ -362,7 +362,7 @@ private:
void HandleUpnpService(void) {
#ifdef USE_EMULATION_WEMO_DEBUG
- AddLog_P(LOG_LEVEL_DEBUG, PSTR("WMO: HandleUpnpService"));
+ AddLog(LOG_LEVEL_DEBUG, PSTR("WMO: HandleUpnpService"));
#endif
LogUpnpWithClient(PSTR(D_WEMO_EVENT_SERVICE));
@@ -375,7 +375,7 @@ private:
void HandleUpnpMetaService(void) {
#ifdef USE_EMULATION_WEMO_DEBUG
- AddLog_P(LOG_LEVEL_DEBUG, PSTR("WMO: HandleUpnpMetaService"));
+ AddLog(LOG_LEVEL_DEBUG, PSTR("WMO: HandleUpnpMetaService"));
#endif
LogUpnpWithClient(PSTR(D_WEMO_META_SERVICE));
@@ -399,14 +399,14 @@ private:
setup_xml.replace("{x3", WemoSerialnumber());
InternalWSSend(200, CT_XML, setup_xml);
#ifdef USE_EMULATION_WEMO_DEBUG
- AddLog_P(LOG_LEVEL_DEBUG, PSTR("WMO: Sending device #%d: %s"), _deviceId, setup_xml.c_str());
+ AddLog(LOG_LEVEL_DEBUG, PSTR("WMO: Sending device #%d: %s"), _deviceId, setup_xml.c_str());
#endif
}
public:
void RegisterHandlers(void) {
#ifdef USE_EMULATION_WEMO_DEBUG
- AddLog_P(LOG_LEVEL_DEBUG, PSTR("WMO: Register device #%d"), _deviceId);
+ AddLog(LOG_LEVEL_DEBUG, PSTR("WMO: Register device #%d"), _deviceId);
#endif
_webServer->on(F("/upnp/control/basicevent1"), [&]() { HandleUpnpEvent(); });
_webServer->on(F("/eventservice.xml"), [&]() { HandleUpnpService(); });
@@ -441,7 +441,7 @@ bool Xdrv21(uint8_t function)
break;
case FUNC_WEB_ADD_HANDLER:
#ifdef USE_EMULATION_WEMO_DEBUG
- AddLog_P(LOG_LEVEL_DEBUG, PSTR("WMO: Adding handlers for %d devices"), TasmotaGlobal.devices_present);
+ AddLog(LOG_LEVEL_DEBUG, PSTR("WMO: Adding handlers for %d devices"), TasmotaGlobal.devices_present);
#endif
// For the first device use the current webserver, for the others.. create a new one listening on a different PortUdp
wemoDevice[numOfWemoSwitch] = new WemoSwitch(1, Webserver);
diff --git a/tasmota/xdrv_23_zigbee_1_headers.ino b/tasmota/xdrv_23_zigbee_1_headers.ino
index 27f2dd895..d88bd80d9 100644
--- a/tasmota/xdrv_23_zigbee_1_headers.ino
+++ b/tasmota/xdrv_23_zigbee_1_headers.ino
@@ -45,7 +45,7 @@ public:
};
typedef int32_t (*ZB_Func)(uint8_t value);
-typedef int32_t (*ZB_RecvMsgFunc)(int32_t res, const class SBuffer &buf);
+typedef int32_t (*ZB_RecvMsgFunc)(int32_t res, const SBuffer &buf);
// Labels used in the State Machine -- internal only
const uint8_t ZIGBEE_LABEL_RESTART = 1; // Restart the state_machine in a different mode
@@ -105,7 +105,12 @@ public:
ZB_RecvMsgFunc recv_func = nullptr; // function to call when message is expected
ZB_RecvMsgFunc recv_unexpected = nullptr; // function called when unexpected message is received
+#ifdef USE_ZIGBEE_EZSP
uint32_t permit_end_time = 0; // timestamp when permit join ends
+ uint16_t ezsp_version = 0;
+#elif defined(USE_ZIGBEE_ZNP)
+ bool permit_end_time = false; // in ZNP mode it's only a boolean
+#endif
#ifdef USE_ZIGBEE_EZSP
Eeprom24C512 eeprom; // takes only 1 bytes of RAM
@@ -114,6 +119,7 @@ public:
struct ZigbeeStatus zigbee;
SBuffer *zigbee_buffer = nullptr;
+void zigbeeZCLSendCmd(const ZigbeeZCLSendMessage &msg);
void ZigbeeZCLSend_Raw(const ZigbeeZCLSendMessage &zcl);
bool ZbAppendWriteBuf(SBuffer & buf, const Z_attribute & attr, bool prepend_status_ok = false);
@@ -129,16 +135,4 @@ uint32_t parseHex(const char **data, size_t max_len = 8) {
return ret;
}
-// Since v9.2.0.2 Log buffer was reduced from 700 bytes to 120. This version is specific to Zigbee and restores the 700 bytes limit
-void AddLogZ_P(uint32_t loglevel, PGM_P formatP, ...) {
- char log_data[MAX_LOGSZ];
-
- va_list arg;
- va_start(arg, formatP);
- vsnprintf_P(log_data, sizeof(log_data), formatP, arg);
- va_end(arg);
-
- AddLogData(loglevel, log_data);
-}
-
#endif // USE_ZIGBEE
diff --git a/tasmota/xdrv_23_zigbee_1z_libs.ino b/tasmota/xdrv_23_zigbee_1z_libs.ino
index 4e018a21e..c2510a01e 100644
--- a/tasmota/xdrv_23_zigbee_1z_libs.ino
+++ b/tasmota/xdrv_23_zigbee_1z_libs.ino
@@ -399,9 +399,7 @@ void Z_attribute::setHex32(uint32_t _val) {
}
void Z_attribute::setHex64(uint64_t _val) {
char hex[22];
- hex[0] = '0'; // prefix with '0x'
- hex[1] = 'x';
- Uint64toHex(_val, &hex[2], 64);
+ ext_snprintf_P(hex, sizeof(hex), PSTR("0x%_X"), &_val);
setStr(hex);
}
@@ -539,7 +537,7 @@ bool Z_attribute::equalsVal(const Z_attribute & attr2) const {
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);
+ return SBuffer::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; }
@@ -579,7 +577,7 @@ String Z_attribute::toString(bool prefix_comma) const {
// value part
switch (type) {
case Za_type::Za_none:
- res += "null";
+ res += F("null");
break;
case Za_type::Za_bool:
res += val.uval32 ? F("true") : F("false");
@@ -638,7 +636,9 @@ String Z_attribute::toString(bool prefix_comma) const {
if (val.arrval) {
res += val.arrval->toString();
} else {
- res += "[]";
+ // res += '[';
+ // res += ']';
+ res += F("[]");
}
break;
}
diff --git a/tasmota/xdrv_23_zigbee_2_devices.ino b/tasmota/xdrv_23_zigbee_2_devices.ino
index d2515cd14..6a6b60655 100644
--- a/tasmota/xdrv_23_zigbee_2_devices.ino
+++ b/tasmota/xdrv_23_zigbee_2_devices.ino
@@ -236,6 +236,11 @@ public:
inline void setX(uint16_t _x) { x = _x; }
inline void setY(uint16_t _y) { y = _y; }
+ void toRGBAttributes(Z_attribute_list & attr_list) const ;
+ static void toRGBAttributesHSB(Z_attribute_list & attr_list, uint16_t hue, uint8_t sat, uint8_t brightness);
+ static void toRGBAttributesXYB(Z_attribute_list & attr_list, uint16_t x, uint16_t y, uint8_t brightness);
+ static void toRGBAttributesRGBb(Z_attribute_list & attr_list, uint8_t r, uint8_t g, uint8_t b, uint8_t brightness);
+
static const Z_Data_Type type = Z_Data_Type::Z_Light;
// 12 bytes
uint8_t colormode; // 0x00: Hue/Sat, 0x01: XY, 0x02: CT | 0xFF not set, default 0x01
@@ -632,7 +637,7 @@ Z_Data & Z_Data_Set::getByType(Z_Data_Type type, uint8_t ep) {
// Byte 3: Power
Z_Data & Z_Data_Set::createFromBuffer(const SBuffer & buf, uint32_t start, uint32_t len) {
if (len < sizeof(Z_Data)) {
- AddLog_P(LOG_LEVEL_INFO, PSTR(D_LOG_ZIGBEE "Invalid len (<4) %d"), len);
+ AddLog(LOG_LEVEL_INFO, PSTR(D_LOG_ZIGBEE "Invalid len (<4) %d"), len);
return *(Z_Data*)nullptr;
}
@@ -646,7 +651,7 @@ Z_Data & Z_Data_Set::createFromBuffer(const SBuffer & buf, uint32_t start, uint3
memcpy(&elt, buf.buf(start), len);
} else {
memcpy(&elt, buf.buf(start), sizeof(Z_Data));
- AddLog_P(LOG_LEVEL_INFO, PSTR(D_LOG_ZIGBEE "buffer len overflow %d > %d"), len, expected_len);
+ AddLog(LOG_LEVEL_INFO, PSTR(D_LOG_ZIGBEE "buffer len overflow %d > %d"), len, expected_len);
}
return elt;
}
@@ -960,6 +965,7 @@ public:
Z_Device & devicesAt(size_t i) const;
// Remove device from list
+ void clearDeviceRouterInfo(void); // reset all router flags, done just before ZbMap
bool removeDevice(uint16_t shortaddr);
// Mark data as 'dirty' and requiring to save in Flash
diff --git a/tasmota/xdrv_23_zigbee_2a_devices_impl.ino b/tasmota/xdrv_23_zigbee_2a_devices_impl.ino
index ec7d773e4..775621ffa 100644
--- a/tasmota/xdrv_23_zigbee_2a_devices_impl.ino
+++ b/tasmota/xdrv_23_zigbee_2a_devices_impl.ino
@@ -218,6 +218,15 @@ Z_Device & Z_Devices::updateDevice(uint16_t shortaddr, uint64_t longaddr) {
return device_unk;
}
+// Clear the router flag for each device, called at the beginning of ZbMap
+void Z_Devices::clearDeviceRouterInfo(void) {
+ for (Z_Device & device : zigbee_devices._devices) {
+ if (device.valid()) {
+ device.setRouter(false);
+ }
+ }
+}
+
//
// Clear all endpoints
//
@@ -552,11 +561,16 @@ void Z_Device::jsonPublishAttrList(const char * json_prefix, const Z_attribute_l
} else {
snprintf_P(subtopic, sizeof(subtopic), PSTR("%s/%04X"), TasmotaGlobal.mqtt_topic, shortaddr);
}
+ if (Settings.flag5.zb_topic_endpoint) {
+ if (attr_list.isValidSrcEp()) {
+ snprintf_P(subtopic, sizeof(subtopic), PSTR("%s_%d"), subtopic, attr_list.src_ep);
+ }
+ }
char stopic[TOPSZ];
if (Settings.flag5.zb_received_as_subtopic)
GetTopic_P(stopic, TELE, subtopic, json_prefix);
else
- GetTopic_P(stopic, TELE, subtopic, D_RSLT_SENSOR);
+ GetTopic_P(stopic, TELE, subtopic, PSTR(D_RSLT_SENSOR));
MqttPublish(stopic, Settings.flag.mqtt_sensor_retain);
} else {
MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_SENSOR), Settings.flag.mqtt_sensor_retain);
@@ -695,6 +709,9 @@ void Z_Device::jsonAddDataAttributes(Z_attribute_list & attr_list) const {
// show internal data - mostly last known values
for (auto & data_elt : data) {
data_elt.toAttributes(attr_list);
+ if (data_elt.getType() == Z_Data_Type::Z_Light) { // since we don't have virtual methods, do an explicit test
+ ((Z_Data_Light&)data_elt).toRGBAttributes(attr_list);
+ }
}
}
// Add "BatteryPercentage", "LastSeen", "LastSeenEpoch", "LinkQuality"
@@ -728,6 +745,7 @@ void Z_Device::jsonLightState(Z_attribute_list & attr_list) const {
if (light.validHue()) {
attr_list.findOrCreateAttribute(PSTR("Hue")).setUInt(light.getHue());
}
+ light.toRGBAttributes(attr_list);
}
attr_list.addAttributePMEM(PSTR("Light")).setInt(light_mode);
}
@@ -763,7 +781,7 @@ String Z_Devices::dumpCoordinator(void) const {
attr_list.addAttributePMEM(PSTR("IEEEAddr")).setHex64(localIEEEAddr);
attr_list.addAttributePMEM(PSTR("TotalDevices")).setUInt(zigbee_devices.devicesSize());
- return attr_list.toString();
+ return attr_list.toString(true);
}
// If &device == nullptr, then dump all
diff --git a/tasmota/xdrv_23_zigbee_3_hue.ino b/tasmota/xdrv_23_zigbee_3_hue.ino
index a77c8b67f..e2a060ef8 100644
--- a/tasmota/xdrv_23_zigbee_3_hue.ino
+++ b/tasmota/xdrv_23_zigbee_3_hue.ino
@@ -62,13 +62,13 @@ void HueLightStatus1Zigbee(uint16_t shortaddr, uint8_t local_light_subtype, Stri
const size_t buf_size = 256;
char * buf = (char*) malloc(buf_size); // temp buffer for strings, avoid stack
- snprintf_P(buf, buf_size, PSTR("{\"on\":%s,"), power ? "true" : "false");
+ snprintf_P(buf, buf_size, PSTR("{\"on\":%s,"), power ? PSTR("true") : PSTR("false"));
// Brightness for all devices with PWM
if ((1 == echo_gen) || (LST_SINGLE <= local_light_subtype)) { // force dimmer for 1st gen Echo
snprintf_P(buf, buf_size, PSTR("%s\"bri\":%d,"), buf, bri);
}
if (LST_COLDWARM <= local_light_subtype) {
- snprintf_P(buf, buf_size, PSTR("%s\"colormode\":\"%s\","), buf, (0 == colormode) ? "hs" : (1 == colormode) ? "xy" : "ct");
+ snprintf_P(buf, buf_size, PSTR("%s\"colormode\":\"%s\","), buf, (0 == colormode) ? PSTR("hs") : (1 == colormode) ? PSTR("xy") : PSTR("ct"));
}
if (LST_RGB <= local_light_subtype) { // colors
if (prev_x_str[0] && prev_y_str[0]) {
@@ -83,7 +83,7 @@ void HueLightStatus1Zigbee(uint16_t shortaddr, uint8_t local_light_subtype, Stri
if (LST_COLDWARM == local_light_subtype || LST_RGBW <= local_light_subtype) { // white temp
snprintf_P(buf, buf_size, PSTR("%s\"ct\":%d,"), buf, ct > 0 ? ct : 284);
}
- snprintf_P(buf, buf_size, HUE_LIGHTS_STATUS_JSON1_SUFFIX_ZIGBEE, buf, reachable ? "true" : "false");
+ snprintf_P(buf, buf_size, HUE_LIGHTS_STATUS_JSON1_SUFFIX_ZIGBEE, buf, reachable ? PSTR("true") : PSTR("false"));
*response += buf;
free(buf);
@@ -151,59 +151,94 @@ void ZigbeeHueGroups(String * lights) {
}
}
+void ZigbeeSendHue(uint16_t shortaddr, uint16_t cluster, uint8_t cmd, const SBuffer & s) {
+ zigbeeZCLSendCmd(ZigbeeZCLSendMessage({
+ shortaddr,
+ 0 /* groupaddr */,
+ cluster /*cluster*/,
+ 0 /* endpoint */,
+ cmd /* cmd */,
+ 0, /* manuf */
+ true /* cluster specific */,
+ true /* response */,
+ false /* discover route */,
+ 0, /* zcl transaction id */
+ (&s != nullptr) ? s.getBuffer() : nullptr,
+ (&s != nullptr) ? s.len() : 0
+ }));
+}
+
// Send commands
// Power On/Off
void ZigbeeHuePower(uint16_t shortaddr, bool power) {
- zigbeeZCLSendStr(shortaddr, 0, 0, true, 0, 0x0006, power ? 1 : 0, "");
+ ZigbeeSendHue(shortaddr, 0x0006, power ? 1 : 0, *(SBuffer*)nullptr);
+// zigbeeZCLSendStr(shortaddr, 0, 0, true, 0, 0x0006, power ? 1 : 0, "");
zigbee_devices.getShortAddr(shortaddr).setPower(power, 0);
}
// Dimmer
void ZigbeeHueDimmer(uint16_t shortaddr, uint8_t dimmer) {
if (dimmer > 0xFE) { dimmer = 0xFE; }
- char param[8];
- snprintf_P(param, sizeof(param), PSTR("%02X0A00"), dimmer);
- zigbeeZCLSendStr(shortaddr, 0, 0, true, 0, 0x0008, 0x04, param);
+ SBuffer s(4);
+ s.add8(dimmer);
+ s.add16(0x000A); // transition time = 1s
+ ZigbeeSendHue(shortaddr, 0x0008, 0x04, s);
+ // char param[8];
+ // snprintf_P(param, sizeof(param), PSTR("%02X0A00"), dimmer);
+ // zigbeeZCLSendStr(shortaddr, 0, 0, true, 0, 0x0008, 0x04, param);
zigbee_devices.getLight(shortaddr).setDimmer(dimmer);
}
// CT
void ZigbeeHueCT(uint16_t shortaddr, uint16_t ct) {
if (ct > 0xFEFF) { ct = 0xFEFF; }
- AddLog_P(LOG_LEVEL_INFO, PSTR("ZigbeeHueCT 0x%04X - %d"), shortaddr, ct);
- char param[12];
- snprintf_P(param, sizeof(param), PSTR("%02X%02X0A00"), ct & 0xFF, ct >> 8);
- uint8_t colormode = 2; // "ct"
- zigbeeZCLSendStr(shortaddr, 0, 0, true, 0, 0x0300, 0x0A, param);
+ // AddLog(LOG_LEVEL_INFO, PSTR("ZigbeeHueCT 0x%04X - %d"), shortaddr, ct);
+ SBuffer s(4);
+ s.add16(ct);
+ s.add16(0x000A); // transition time = 1s
+ ZigbeeSendHue(shortaddr, 0x0300, 0x0A, s);
+ // char param[12];
+ // snprintf_P(param, sizeof(param), PSTR("%02X%02X0A00"), ct & 0xFF, ct >> 8);
+ // zigbeeZCLSendStr(shortaddr, 0, 0, true, 0, 0x0300, 0x0A, param);
Z_Data_Light & light = zigbee_devices.getLight(shortaddr);
- light.setColorMode(colormode);
+ light.setColorMode(2); // "ct"
light.setCT(ct);
}
// XY
void ZigbeeHueXY(uint16_t shortaddr, uint16_t x, uint16_t y) {
- char param[16];
if (x > 0xFEFF) { x = 0xFEFF; }
if (y > 0xFEFF) { y = 0xFEFF; }
- snprintf_P(param, sizeof(param), PSTR("%02X%02X%02X%02X0A00"), x & 0xFF, x >> 8, y & 0xFF, y >> 8);
- uint8_t colormode = 1; // "xy"
- zigbeeZCLSendStr(shortaddr, 0, 0, true, 0, 0x0300, 0x07, param);
+ SBuffer s(8);
+ s.add16(x);
+ s.add16(y);
+ s.add16(0x000A); // transition time = 1s
+ ZigbeeSendHue(shortaddr, 0x0300, 0x07, s);
+ // char param[16];
+ // snprintf_P(param, sizeof(param), PSTR("%02X%02X%02X%02X0A00"), x & 0xFF, x >> 8, y & 0xFF, y >> 8);
+ // uint8_t colormode = 1; // "xy"
+ // zigbeeZCLSendStr(shortaddr, 0, 0, true, 0, 0x0300, 0x07, param);
Z_Data_Light & light = zigbee_devices.getLight(shortaddr);
- light.setColorMode(colormode);
+ light.setColorMode(1); // "xy"
light.setX(x);
light.setY(y);
}
// HueSat
void ZigbeeHueHS(uint16_t shortaddr, uint16_t hue, uint8_t sat) {
- char param[16];
uint8_t hue8 = changeUIntScale(hue, 0, 360, 0, 254);
if (sat > 0xFE) { sat = 0xFE; }
- snprintf_P(param, sizeof(param), PSTR("%02X%02X0000"), hue8, sat);
- uint8_t colormode = 0; // "hs"
- zigbeeZCLSendStr(shortaddr, 0, 0, true, 0, 0x0300, 0x06, param);
+ SBuffer s(4);
+ s.add8(hue);
+ s.add8(sat);
+ s.add16(0);
+ ZigbeeSendHue(shortaddr, 0x0300, 0x06, s);
+ // char param[16];
+ // snprintf_P(param, sizeof(param), PSTR("%02X%02X0000"), hue8, sat);
+ // uint8_t colormode = 0; // "hs"
+ // zigbeeZCLSendStr(shortaddr, 0, 0, true, 0, 0x0300, 0x06, param);
Z_Data_Light & light = zigbee_devices.getLight(shortaddr);
- light.setColorMode(colormode);
+ light.setColorMode(0); // "hs"
light.setSat(sat);
light.setHue(hue);
}
@@ -233,7 +268,7 @@ void ZigbeeHandleHue(uint16_t shortaddr, uint32_t device_id, String &response) {
on = hue_on.getBool();
snprintf_P(buf, buf_size,
msg[HUE_RESP_ON],
- device_id, on ? "true" : "false");
+ device_id, on ? PSTR("true") : PSTR("false"));
if (on) {
ZigbeeHuePower(shortaddr, 0x01);
@@ -252,7 +287,7 @@ void ZigbeeHandleHue(uint16_t shortaddr, uint32_t device_id, String &response) {
if (resp) { response += ","; }
snprintf_P(buf, buf_size,
msg[HUE_RESP_NUM],
- device_id, "bri", bri);
+ device_id, PSTR("bri"), bri);
response += buf;
if (LST_SINGLE <= bulbtype) {
// extend bri value if set to max
@@ -293,7 +328,7 @@ void ZigbeeHandleHue(uint16_t shortaddr, uint32_t device_id, String &response) {
if (resp) { response += ","; }
snprintf_P(buf, buf_size,
msg[HUE_RESP_NUM],
- device_id, "hue", hue);
+ device_id, PSTR("hue"), hue);
response += buf;
if (LST_RGB <= bulbtype) {
// change range from 0..65535 to 0..360
@@ -311,7 +346,7 @@ void ZigbeeHandleHue(uint16_t shortaddr, uint32_t device_id, String &response) {
if (resp) { response += ","; }
snprintf_P(buf, buf_size,
msg[HUE_RESP_NUM],
- device_id, "sat", sat);
+ device_id, PSTR("sat"), sat);
response += buf;
if (LST_RGB <= bulbtype) {
// extend sat value if set to max
@@ -332,7 +367,7 @@ void ZigbeeHandleHue(uint16_t shortaddr, uint32_t device_id, String &response) {
if (resp) { response += ","; }
snprintf_P(buf, buf_size,
msg[HUE_RESP_NUM],
- device_id, "ct", ct);
+ device_id, PSTR("ct"), ct);
response += buf;
if ((LST_COLDWARM == bulbtype) || (LST_RGBW <= bulbtype)) {
ZigbeeHueCT(shortaddr, ct);
diff --git a/tasmota/xdrv_23_zigbee_4_persistence.ino b/tasmota/xdrv_23_zigbee_4_persistence.ino
index dc7c385e3..5f21c3103 100644
--- a/tasmota/xdrv_23_zigbee_4_persistence.ino
+++ b/tasmota/xdrv_23_zigbee_4_persistence.ino
@@ -136,7 +136,7 @@ bool hibernateDeviceConfiguration(SBuffer & buf, const class Z_Data_Set & data,
return found;
}
-class SBuffer hibernateDevicev2(const struct Z_Device &device) {
+SBuffer hibernateDevicev2(const struct Z_Device &device) {
SBuffer buf(128);
buf.add8(0x00); // overall length, will be updated later
@@ -178,7 +178,7 @@ class SBuffer hibernateDevicev2(const struct Z_Device &device) {
return buf;
}
-class SBuffer hibernateDevices(void) {
+SBuffer hibernateDevices(void) {
SBuffer buf(2048);
size_t devices_size = zigbee_devices.devicesSize();
@@ -301,7 +301,7 @@ void loadZigbeeDevices(bool dump_only = false) {
// first copy SPI buffer into ram
uint8_t *spi_buffer = (uint8_t*) malloc(z_spi_len);
if (!spi_buffer) {
- AddLog_P(LOG_LEVEL_ERROR, PSTR(D_LOG_ZIGBEE "Cannot allocate 4KB buffer"));
+ AddLog(LOG_LEVEL_ERROR, PSTR(D_LOG_ZIGBEE "Cannot allocate 4KB buffer"));
return;
}
#ifdef USE_UFILESYS
@@ -311,8 +311,8 @@ void loadZigbeeDevices(bool dump_only = false) {
#endif // ESP32
Z_Flashentry flashdata;
memcpy_P(&flashdata, z_dev_start, sizeof(Z_Flashentry));
-// AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_ZIGBEE "Memory %d"), ESP_getFreeHeap());
- // AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_ZIGBEE "Zigbee signature in Flash: %08X - %d"), flashdata.name, flashdata.len);
+// AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_ZIGBEE "Memory %d"), ESP_getFreeHeap());
+ // AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_ZIGBEE "Zigbee signature in Flash: %08X - %d"), flashdata.name, flashdata.len);
// Check the signature
if ( ((flashdata.name == ZIGB_NAME1) || (flashdata.name == ZIGB_NAME2))
@@ -322,7 +322,7 @@ void loadZigbeeDevices(bool dump_only = false) {
// parse what seems to be a valid entry
SBuffer buf(buf_len);
buf.addBuffer(z_dev_start + sizeof(Z_Flashentry), buf_len);
- AddLog_P(LOG_LEVEL_INFO, PSTR(D_LOG_ZIGBEE "Zigbee device information in %s (%d bytes)"), PSTR("Flash"), buf_len);
+ AddLog(LOG_LEVEL_INFO, PSTR(D_LOG_ZIGBEE "Zigbee device information in %s (%d bytes)"), PSTR("Flash"), buf_len);
if (dump_only) {
size_t buf_len = buf.len();
@@ -336,7 +336,7 @@ void loadZigbeeDevices(bool dump_only = false) {
zigbee_devices.clean(); // don't write back to Flash what we just loaded
}
} else {
- AddLog_P(LOG_LEVEL_INFO, PSTR(D_LOG_ZIGBEE "No Zigbee device information in %s"), PSTR("Flash"));
+ AddLog(LOG_LEVEL_INFO, PSTR(D_LOG_ZIGBEE "No Zigbee device information in %s"), PSTR("Flash"));
}
#ifdef ESP32
free(spi_buffer);
@@ -354,14 +354,14 @@ void saveZigbeeDevices(void) {
SBuffer buf = hibernateDevices();
size_t buf_len = buf.len();
if (buf_len > 2040) {
- AddLog_P(LOG_LEVEL_ERROR, PSTR(D_LOG_ZIGBEE "Buffer too big to fit in Flash (%d bytes)"), buf_len);
+ AddLog(LOG_LEVEL_ERROR, PSTR(D_LOG_ZIGBEE "Buffer too big to fit in Flash (%d bytes)"), buf_len);
return;
}
// first copy SPI buffer into ram
uint8_t *spi_buffer = (uint8_t*) malloc(z_spi_len);
if (!spi_buffer) {
- AddLog_P(LOG_LEVEL_ERROR, PSTR(D_LOG_ZIGBEE "Cannot allocate 4KB buffer"));
+ AddLog(LOG_LEVEL_ERROR, PSTR(D_LOG_ZIGBEE "Cannot allocate 4KB buffer"));
return;
}
// copy the flash into RAM to make local change, and write back the whole buffer
@@ -386,13 +386,13 @@ void saveZigbeeDevices(void) {
if (ESP.flashEraseSector(z_spi_start_sector)) {
ESP.flashWrite(z_spi_start_sector * SPI_FLASH_SEC_SIZE, (uint32_t*) spi_buffer, SPI_FLASH_SEC_SIZE);
}
- AddLog_P(LOG_LEVEL_INFO, PSTR(D_LOG_ZIGBEE "Zigbee Devices Data store in Flash (0x%08X - %d bytes)"), z_dev_start, buf_len);
+ AddLog(LOG_LEVEL_INFO, PSTR(D_LOG_ZIGBEE "Zigbee Devices Data store in Flash (0x%08X - %d bytes)"), z_dev_start, buf_len);
#endif // ESP8266
#ifdef ESP32
#ifdef USE_UFILESYS
TfsSaveFile(TASM_FILE_ZIGBEE, spi_buffer, z_spi_len);
#endif
- AddLog_P(LOG_LEVEL_INFO, PSTR(D_LOG_ZIGBEE "Zigbee Devices Data saved in %s (%d bytes)"), PSTR("Flash"), buf_len);
+ AddLog(LOG_LEVEL_INFO, PSTR(D_LOG_ZIGBEE "Zigbee Devices Data saved in %s (%d bytes)"), PSTR("Flash"), buf_len);
#endif // ESP32
free(spi_buffer);
}
@@ -407,7 +407,7 @@ void eraseZigbeeDevices(void) {
// first copy SPI buffer into ram
uint8_t *spi_buffer = (uint8_t*) malloc(z_spi_len);
if (!spi_buffer) {
- AddLog_P(LOG_LEVEL_ERROR, PSTR(D_LOG_ZIGBEE "Cannot allocate 4KB buffer"));
+ AddLog(LOG_LEVEL_ERROR, PSTR(D_LOG_ZIGBEE "Cannot allocate 4KB buffer"));
return;
}
// copy the flash into RAM to make local change, and write back the whole buffer
@@ -422,13 +422,13 @@ void eraseZigbeeDevices(void) {
}
free(spi_buffer);
- AddLog_P(LOG_LEVEL_INFO, PSTR(D_LOG_ZIGBEE "Zigbee Devices Data erased in %s"), PSTR("Flash"));
+ AddLog(LOG_LEVEL_INFO, PSTR(D_LOG_ZIGBEE "Zigbee Devices Data erased in %s"), PSTR("Flash"));
#endif // ESP8266
#ifdef ESP32
#ifdef USE_UFILESYS
TfsInitFile(TASM_FILE_ZIGBEE, z_block_len, 0xFF);
#endif
- AddLog_P(LOG_LEVEL_INFO, PSTR(D_LOG_ZIGBEE "Zigbee Devices Data erased (%d bytes)"), z_block_len);
+ AddLog(LOG_LEVEL_INFO, PSTR(D_LOG_ZIGBEE "Zigbee Devices Data erased (%d bytes)"), z_block_len);
#endif // ESP32
}
@@ -436,9 +436,7 @@ void restoreDumpAllDevices(void) {
for (const auto & device : zigbee_devices.getDevices()) {
const SBuffer buf = hibernateDevicev2(device);
if (buf.len() > 0) {
- char hex_char[buf.len()*2+2];
- Response_P(PSTR("{\"" D_PRFX_ZB D_CMND_ZIGBEE_RESTORE "\":\"ZbRestore %s\"}"),
- ToHex_P(buf.buf(0), buf.len(), hex_char, sizeof(hex_char)));
+ Response_P(PSTR("{\"" D_PRFX_ZB D_CMND_ZIGBEE_RESTORE "\":\"ZbRestore %_B\"}"), &buf);
MqttPublishPrefixTopicRulesProcess_P(RESULT_OR_STAT, PSTR(D_PRFX_ZB D_CMND_ZIGBEE_DATA));
}
}
diff --git a/tasmota/xdrv_23_zigbee_4a_nano_fs.ino b/tasmota/xdrv_23_zigbee_4a_nano_fs.ino
index bdc1ab342..54cbb3b54 100644
--- a/tasmota/xdrv_23_zigbee_4a_nano_fs.ino
+++ b/tasmota/xdrv_23_zigbee_4a_nano_fs.ino
@@ -20,7 +20,7 @@
#ifdef USE_ZIGBEE
#ifdef USE_ZIGBEE_EZSP
-#define Z_EEPROM_DEBUG
+// #define Z_EEPROM_DEBUG
// The EEPROM is 64KB in size with individually writable bytes.
// They are conveniently organized in pages of 128 bytes to accelerate
@@ -291,7 +291,7 @@ bool ZFS::findFileEntry(uint32_t name, ZFS_File_Entry & entry, uint8_t * _entry_
#ifdef Z_EEPROM_DEBUG
// {
// char hex_char[(sizeof(ZFS_File_Entry) * 2) + 2];
- // AddLog_P(LOG_LEVEL_INFO, PSTR(D_LOG_ZIGBEE "Read entry %d at address 0x%04X contains %s"), entry_idx, entry_addr, ToHex_P((uint8_t*)&entry, sizeof(entry), hex_char, sizeof(hex_char)));
+ // AddLog_P(LOG_LEVEL_INFO, PSTR(D_LOG_ZIGBEE "Read entry %d at address 0x%04X contains %*_H"), entry_idx, entry_addr, sizeof(entry), &entry);
// }
#endif
if (entry.name == name) {
@@ -363,15 +363,15 @@ void ZFS::initOrFormat(void) {
byte map[256];
char hex_char[(256 * 2) + 2];
zigbee.eeprom.readBytes(0x0000, 256, map);
- AddLogZ_P(LOG_LEVEL_INFO, PSTR(D_LOG_ZIGBEE "BLK 00 %s"), ToHex_P(map, sizeof(map), hex_char, sizeof(hex_char)));
+ AddLog_P(LOG_LEVEL_INFO, PSTR(D_LOG_ZIGBEE "BLK 00 %s"), ToHex_P(map, sizeof(map), hex_char, sizeof(hex_char)));
// zigbee.eeprom.readBytes(0x0100, 256, map);
- // AddLogZ_P(LOG_LEVEL_INFO, PSTR(D_LOG_ZIGBEE "BLK 01 %s"), ToHex_P(map, sizeof(map), hex_char, sizeof(hex_char)));
+ // AddLog_P(LOG_LEVEL_INFO, PSTR(D_LOG_ZIGBEE "BLK 01 %s"), ToHex_P(map, sizeof(map), hex_char, sizeof(hex_char)));
zigbee.eeprom.readBytes(0x0200, 256, map);
- AddLogZ_P(LOG_LEVEL_INFO, PSTR(D_LOG_ZIGBEE "BLK 02 %s"), ToHex_P(map, sizeof(map), hex_char, sizeof(hex_char)));
+ AddLog_P(LOG_LEVEL_INFO, PSTR(D_LOG_ZIGBEE "BLK 02 %s"), ToHex_P(map, sizeof(map), hex_char, sizeof(hex_char)));
zigbee.eeprom.readBytes(0x2100, 256, map);
- AddLogZ_P(LOG_LEVEL_INFO, PSTR(D_LOG_ZIGBEE "BLK 21 %s"), ToHex_P(map, sizeof(map), hex_char, sizeof(hex_char)));
+ AddLog_P(LOG_LEVEL_INFO, PSTR(D_LOG_ZIGBEE "BLK 21 %s"), ToHex_P(map, sizeof(map), hex_char, sizeof(hex_char)));
// zigbee.eeprom.readBytes(0xFF00, 256, map);
- // AddLogZ_P(LOG_LEVEL_INFO, PSTR(D_LOG_ZIGBEE "BLK FF %s"), ToHex_P(map, sizeof(map), hex_char, sizeof(hex_char)));
+ // AddLog_P(LOG_LEVEL_INFO, PSTR(D_LOG_ZIGBEE "BLK FF %s"), ToHex_P(map, sizeof(map), hex_char, sizeof(hex_char)));
}
#endif
diff --git a/tasmota/xdrv_23_zigbee_4b_eeprom.ino b/tasmota/xdrv_23_zigbee_4b_eeprom.ino
index 88fa1e391..b42b26a75 100644
--- a/tasmota/xdrv_23_zigbee_4b_eeprom.ino
+++ b/tasmota/xdrv_23_zigbee_4b_eeprom.ino
@@ -41,7 +41,7 @@
int32_t hydrateDeviceWideData(class Z_Device & device, const SBuffer & buf, size_t start, size_t len) {
size_t segment_len = buf.get8(start);
if ((segment_len < 6) || (segment_len > len)) {
- AddLog_P(LOG_LEVEL_INFO, PSTR(D_LOG_ZIGBEE "invalid device wide data length=%d"), segment_len);
+ AddLog(LOG_LEVEL_INFO, PSTR(D_LOG_ZIGBEE "invalid device wide data length=%d"), segment_len);
return -1;
}
device.last_seen = buf.get32(start+1);
@@ -74,23 +74,22 @@ bool hydrateDeviceData(class Z_Device & device, const SBuffer & buf, size_t star
// negative means error
// positive is the segment length
-int32_t hydrateSingleDevice(const class SBuffer & buf, size_t start, size_t len) {
+int32_t hydrateSingleDevice(const SBuffer & buf, size_t start, size_t len) {
uint8_t segment_len = buf.get8(start);
if ((segment_len < 4) || (start + segment_len > len)) {
- AddLog_P(LOG_LEVEL_INFO, PSTR(D_LOG_ZIGBEE "invalid segment_len=%d"), segment_len);
+ AddLog(LOG_LEVEL_INFO, PSTR(D_LOG_ZIGBEE "invalid segment_len=%d"), segment_len);
return -1;
}
// read shortaddr
uint16_t shortaddr = buf.get16(start + 1);
if (shortaddr >= 0xFFF0) {
- AddLog_P(LOG_LEVEL_INFO, PSTR(D_LOG_ZIGBEE "invalid shortaddr=0x%04X"), shortaddr);
+ AddLog(LOG_LEVEL_INFO, PSTR(D_LOG_ZIGBEE "invalid shortaddr=0x%04X"), shortaddr);
return -1;
}
#ifdef Z_EEPROM_DEBUG
{
if (segment_len > 3) {
- char hex_char[((segment_len+1) * 2) + 2];
- AddLog_P(LOG_LEVEL_INFO, PSTR(D_LOG_ZIGBEE "ZbData 0x%04X,%s"), shortaddr, ToHex_P(buf.buf(start+3), segment_len+1-3, hex_char, sizeof(hex_char)));
+ AddLog_P(LOG_LEVEL_INFO, PSTR(D_LOG_ZIGBEE "ZbData 0x%04X,%*_H"), shortaddr, segment_len+1-3, buf.buf(start+3));
}
}
#endif
@@ -118,9 +117,9 @@ bool hydrateDevicesDataFromEEPROM(void) {
if (!zigbee.eeprom_ready) { return false; }
int32_t file_length = ZFS::getLength(ZIGB_DATA2);
if (file_length > 0) {
- AddLog_P(LOG_LEVEL_INFO, PSTR(D_LOG_ZIGBEE "Zigbee device data in EEPROM (%d bytes)"), file_length);
+ AddLog(LOG_LEVEL_INFO, PSTR(D_LOG_ZIGBEE "Zigbee device data in EEPROM (%d bytes)"), file_length);
} else {
- AddLog_P(LOG_LEVEL_INFO, PSTR(D_LOG_ZIGBEE "No Zigbee device data in EEPROM"));
+ AddLog(LOG_LEVEL_INFO, PSTR(D_LOG_ZIGBEE "No Zigbee device data in EEPROM"));
return false;
}
@@ -154,7 +153,7 @@ bool hydrateDevicesDataFromEEPROM(void) {
#endif // USE_ZIGBEE_EZSP
}
-class SBuffer hibernateDeviceData(const struct Z_Device & device, bool mqtt = false) {
+SBuffer hibernateDeviceData(const struct Z_Device & device, bool mqtt = false) {
SBuffer buf(192);
// If we have zero information about the device, just skip ir
@@ -182,16 +181,14 @@ class SBuffer hibernateDeviceData(const struct Z_Device & device, bool mqtt = fa
buf.set8(0, buf.len() - 1);
{
- size_t buf_len = buf.len() - 3;
- char hex[2*buf_len + 1];
// skip first 3 bytes
- ToHex_P(buf.buf(3), buf_len, hex, sizeof(hex));
+ size_t buf_len = buf.len() - 3;
if (mqtt) {
- Response_P(PSTR("{\"" D_PRFX_ZB D_CMND_ZIGBEE_DATA "\":\"ZbData 0x%04X,%s\"}"), device.shortaddr, hex);
+ Response_P(PSTR("{\"" D_PRFX_ZB D_CMND_ZIGBEE_DATA "\":\"ZbData 0x%04X,%*_H\"}"), device.shortaddr, buf_len, buf.buf(3));
MqttPublishPrefixTopicRulesProcess_P(RESULT_OR_STAT, PSTR(D_PRFX_ZB D_CMND_ZIGBEE_DATA));
} else {
- AddLog_P(LOG_LEVEL_INFO, PSTR(D_LOG_ZIGBEE "ZbData 0x%04X,%s"), device.shortaddr, hex);
+ AddLog_P(LOG_LEVEL_INFO, PSTR(D_LOG_ZIGBEE "ZbData 0x%04X,%*_H"), device.shortaddr, buf_len, buf.buf(3));
}
}
}
@@ -222,7 +219,7 @@ void hibernateAllData(void) {
}
int32_t ret = write_data.close();
#ifdef Z_EEPROM_DEBUG
- AddLog_P(LOG_LEVEL_INFO, PSTR(D_LOG_ZIGBEE "ZbData - %d bytes written to EEPROM"), ret);
+ AddLog(LOG_LEVEL_INFO, PSTR(D_LOG_ZIGBEE "ZbData - %d bytes written to EEPROM"), ret);
#endif
#endif // USE_ZIGBEE_EZSP
}
@@ -270,10 +267,10 @@ bool hibernateDevicesInEEPROM(void) {
int32_t ret = write_data.close();
if (ret < 0) {
- AddLog_P(LOG_LEVEL_ERROR, PSTR(D_LOG_ZIGBEE "Error writing Devices to EEPROM"));
+ AddLog(LOG_LEVEL_ERROR, PSTR(D_LOG_ZIGBEE "Error writing Devices to EEPROM"));
return false;
} else {
- AddLog_P(LOG_LEVEL_INFO, PSTR(D_LOG_ZIGBEE "Zigbee Devices Data saved in %s (%d bytes)"), PSTR("EEPROM"), ret);
+ AddLog(LOG_LEVEL_INFO, PSTR(D_LOG_ZIGBEE "Zigbee Devices Data saved in %s (%d bytes)"), PSTR("EEPROM"), ret);
}
return true;
}
@@ -288,10 +285,10 @@ bool loadZigbeeDevicesFromEEPROM(void) {
ZFS::readBytes(ZIGB_NAME2, &num_devices, sizeof(num_devices), 0, sizeof(num_devices));
if ((file_len < 10) || (num_devices == 0x00) || (num_devices == 0xFF)) { // No data
- AddLog_P(LOG_LEVEL_INFO, PSTR(D_LOG_ZIGBEE "No Zigbee device information in %s"), PSTR("EEPROM"));
+ AddLog(LOG_LEVEL_INFO, PSTR(D_LOG_ZIGBEE "No Zigbee device information in %s"), PSTR("EEPROM"));
return false;
}
- AddLog_P(LOG_LEVEL_INFO, PSTR(D_LOG_ZIGBEE "Zigbee device information in %s (%d bytes)"), PSTR("EEPROM"), file_len);
+ AddLog(LOG_LEVEL_INFO, PSTR(D_LOG_ZIGBEE "Zigbee device information in %s (%d bytes)"), PSTR("EEPROM"), file_len);
uint32_t k = 1; // byte index in global buffer
for (uint32_t i = 0; (i < num_devices) && (k < file_len); i++) {
@@ -301,7 +298,7 @@ bool loadZigbeeDevicesFromEEPROM(void) {
buf.setLen(dev_record_len);
ret = ZFS::readBytes(ZIGB_NAME2, buf.getBuffer(), dev_record_len, k, dev_record_len);
if (ret != dev_record_len) {
- AddLog_P(LOG_LEVEL_INFO, PSTR(D_LOG_ZIGBEE "File too short when reading EEPROM"));
+ AddLog(LOG_LEVEL_INFO, PSTR(D_LOG_ZIGBEE "File too short when reading EEPROM"));
return false;
}
@@ -318,7 +315,7 @@ bool loadZigbeeDevicesFromEEPROM(void) {
void ZFS_Erase(void) {
if (zigbee.eeprom_present) {
ZFS::erase();
- AddLog_P(LOG_LEVEL_INFO, PSTR(D_LOG_ZIGBEE "Zigbee Devices Data erased in %s"), PSTR("EEPROM"));
+ AddLog(LOG_LEVEL_INFO, PSTR(D_LOG_ZIGBEE "Zigbee Devices Data erased in %s"), PSTR("EEPROM"));
}
}
diff --git a/tasmota/xdrv_23_zigbee_5__constants.ino b/tasmota/xdrv_23_zigbee_5__constants.ino
index 0a3926db7..fc8b95661 100644
--- a/tasmota/xdrv_23_zigbee_5__constants.ino
+++ b/tasmota/xdrv_23_zigbee_5__constants.ino
@@ -278,6 +278,7 @@ const char Z_strings[] PROGMEM =
"LastMessageLQI" "\x00"
"LastMessageRSSI" "\x00"
"LastSetTime" "\x00"
+ "LidlPower" "\x00"
"LocalTemperature" "\x00"
"LocalTemperatureCalibration" "\x00"
"LocalTime" "\x00"
@@ -379,6 +380,8 @@ const char Z_strings[] PROGMEM =
"ProductRevision" "\x00"
"ProductURL" "\x00"
"QualityMeasure" "\x00"
+ "RGB" "\x00"
+ "RGBb" "\x00"
"RMSCurrent" "\x00"
"RMSVoltage" "\x00"
"ReactivePower" "\x00"
@@ -699,221 +702,224 @@ enum Z_offsets {
Zo_LastMessageLQI = 3308,
Zo_LastMessageRSSI = 3323,
Zo_LastSetTime = 3339,
- Zo_LocalTemperature = 3351,
- Zo_LocalTemperatureCalibration = 3368,
- Zo_LocalTime = 3396,
- Zo_LocationAge = 3406,
- Zo_LocationMethod = 3418,
- Zo_LocationType = 3433,
- Zo_LockState = 3446,
- Zo_LockType = 3456,
- Zo_LongPollInterval = 3465,
- Zo_LongPollIntervalMin = 3482,
- Zo_MainsFrequency = 3502,
- Zo_MainsVoltage = 3517,
- Zo_Manufacturer = 3530,
- Zo_MaxTempExperienced = 3543,
- Zo_MeterTypeID = 3562,
- Zo_MinTempExperienced = 3574,
- Zo_Mode = 3593,
- Zo_Model = 3598,
- Zo_ModelId = 3604,
- Zo_MotorStepSize = 3612,
- Zo_Movement = 3626,
- Zo_MullerLightMode = 3635,
- Zo_MultiApplicationType = 3651,
- Zo_MultiDescription = 3672,
- Zo_MultiInApplicationType = 3689,
- Zo_MultiInDescription = 3712,
- Zo_MultiInNumberOfStates = 3731,
- Zo_MultiInOutOfService = 3753,
- Zo_MultiInReliability = 3773,
- Zo_MultiInStatusFlags = 3792,
- Zo_MultiInValue = 3811,
- Zo_MultiNumberOfStates = 3824,
- Zo_MultiOutApplicationType = 3844,
- Zo_MultiOutDescription = 3868,
- Zo_MultiOutNumberOfStates = 3888,
- Zo_MultiOutOfService = 3911,
- Zo_MultiOutOutOfService = 3929,
- Zo_MultiOutReliability = 3950,
- Zo_MultiOutRelinquishDefault = 3970,
- Zo_MultiOutStatusFlags = 3996,
- Zo_MultiOutValue = 4016,
- Zo_MultiReliability = 4030,
- Zo_MultiRelinquishDefault = 4047,
- Zo_MultiStatusFlags = 4070,
- Zo_MultiValue = 4087,
- Zo_MultipleScheduling = 4098,
- Zo_NumberOfDevices = 4117,
- Zo_NumberOfPrimaries = 4133,
- Zo_NumberOfResets = 4151,
- Zo_NumberofActuationsLift = 4166,
- Zo_NumberofActuationsTilt = 4189,
- Zo_Occupancy = 4212,
- Zo_OccupancySensorType = 4222,
- Zo_OccupiedCoolingSetpoint = 4242,
- Zo_OccupiedHeatingSetpoint = 4266,
- Zo_OnOffTransitionTime = 4290,
- Zo_OpenPeriod = 4310,
- Zo_OppleMode = 4321,
- Zo_OutdoorTemperature = 4331,
- Zo_OverTempTotalDwell = 4350,
- Zo_PICoolingDemand = 4369,
- Zo_PIHeatingDemand = 4385,
- Zo_POD = 4401,
- Zo_Panic = 4405,
- Zo_PartNumber = 4411,
- Zo_PersistentMemoryWrites = 4422,
- Zo_PersonalAlarm = 4445,
- Zo_PhysicalClosedLimit = 4459,
- Zo_PhysicalClosedLimitLift = 4479,
- Zo_PhysicalClosedLimitTilt = 4503,
- Zo_Power = 4527,
- Zo_Power2 = 4533,
- Zo_Power3 = 4540,
- Zo_Power4 = 4547,
- Zo_PowerOffEffect = 4554,
- Zo_PowerOnRecall = 4569,
- Zo_PowerOnTimer = 4583,
- Zo_PowerSource = 4596,
- Zo_PowerThreshold = 4608,
- Zo_Pressure = 4623,
- Zo_PressureMaxMeasuredValue = 4632,
- Zo_PressureMaxScaledValue = 4657,
- Zo_PressureMinMeasuredValue = 4680,
- Zo_PressureMinScaledValue = 4705,
- Zo_PressureScale = 4728,
- Zo_PressureScaledTolerance = 4742,
- Zo_PressureScaledValue = 4766,
- Zo_PressureTolerance = 4786,
- Zo_Primary1Intensity = 4804,
- Zo_Primary1X = 4822,
- Zo_Primary1Y = 4832,
- Zo_Primary2Intensity = 4842,
- Zo_Primary2X = 4860,
- Zo_Primary2Y = 4870,
- Zo_Primary3Intensity = 4880,
- Zo_Primary3X = 4898,
- Zo_Primary3Y = 4908,
- Zo_ProductCode = 4918,
- Zo_ProductRevision = 4930,
- Zo_ProductURL = 4946,
- Zo_QualityMeasure = 4957,
- Zo_RMSCurrent = 4972,
- Zo_RMSVoltage = 4983,
- Zo_ReactivePower = 4994,
- Zo_RecallScene = 5008,
- Zo_RemainingTime = 5020,
- Zo_RemoteSensing = 5034,
- Zo_RemoveAllGroups = 5048,
- Zo_RemoveAllScenes = 5064,
- Zo_RemoveGroup = 5080,
- Zo_RemoveScene = 5092,
- Zo_ResetAlarm = 5104,
- Zo_ResetAllAlarms = 5115,
- Zo_SWBuildID = 5130,
- Zo_Sat = 5140,
- Zo_SatMove = 5144,
- Zo_SatStep = 5152,
- Zo_SceneCount = 5160,
- Zo_SceneValid = 5171,
- Zo_ScheduleMode = 5182,
- Zo_SeaPressure = 5195,
- Zo_ShortPollInterval = 5207,
- Zo_Shutter = 5225,
- Zo_ShutterClose = 5233,
- Zo_ShutterLift = 5246,
- Zo_ShutterOpen = 5258,
- Zo_ShutterStop = 5270,
- Zo_ShutterTilt = 5282,
- Zo_SoftwareRevision = 5294,
- Zo_StackVersion = 5311,
- Zo_StandardTime = 5324,
- Zo_StartUpOnOff = 5337,
- Zo_Status = 5350,
- Zo_StoreScene = 5357,
- Zo_SwitchType = 5368,
- Zo_SystemMode = 5379,
- Zo_TRVBoost = 5390,
- Zo_TRVChildProtection = 5399,
- Zo_TRVMirrorDisplay = 5418,
- Zo_TRVMode = 5435,
- Zo_TRVWindowOpen = 5443,
- Zo_TempTarget = 5457,
- Zo_Temperature = 5468,
- Zo_TemperatureMaxMeasuredValue = 5480,
- Zo_TemperatureMinMeasuredValue = 5508,
- Zo_TemperatureTolerance = 5536,
- Zo_TerncyDuration = 5557,
- Zo_TerncyRotate = 5572,
- Zo_ThSetpoint = 5585,
- Zo_Time = 5596,
- Zo_TimeEpoch = 5601,
- Zo_TimeStatus = 5611,
- Zo_TimeZone = 5622,
- Zo_TotalProfileNum = 5631,
- Zo_TuyaAutoLock = 5647,
- Zo_TuyaAwayDays = 5660,
- Zo_TuyaAwayTemp = 5673,
- Zo_TuyaBattery = 5686,
- Zo_TuyaBoostTime = 5698,
- Zo_TuyaChildLock = 5712,
- Zo_TuyaComfortTemp = 5726,
- Zo_TuyaEcoTemp = 5742,
- Zo_TuyaFanMode = 5754,
- Zo_TuyaForceMode = 5766,
- Zo_TuyaMaxTemp = 5780,
- Zo_TuyaMinTemp = 5792,
- Zo_TuyaPreset = 5804,
- Zo_TuyaScheduleHolidays = 5815,
- Zo_TuyaScheduleWorkdays = 5836,
- Zo_TuyaTempTarget = 5857,
- Zo_TuyaValveDetection = 5872,
- Zo_TuyaValvePosition = 5891,
- Zo_TuyaWeekSelect = 5909,
- Zo_TuyaWindowDetection = 5924,
- Zo_UnoccupiedCoolingSetpoint = 5944,
- Zo_UnoccupiedHeatingSetpoint = 5970,
- Zo_UtilityName = 5996,
- Zo_ValidUntilTime = 6008,
- Zo_ValvePosition = 6023,
- Zo_VelocityLift = 6037,
- Zo_ViewGroup = 6050,
- Zo_ViewScene = 6060,
- Zo_Water = 6070,
- Zo_WhitePointX = 6076,
- Zo_WhitePointY = 6088,
- Zo_WindowCoveringType = 6100,
- Zo_X = 6119,
- Zo_Y = 6121,
- Zo_ZCLVersion = 6123,
- Zo_ZoneState = 6134,
- Zo_ZoneStatus = 6144,
- Zo_ZoneStatusChange = 6155,
- Zo_ZoneType = 6172,
- Zo_xx = 6181,
- Zo_xx000A00 = 6184,
- Zo_xx0A = 6193,
- Zo_xx0A00 = 6198,
- Zo_xx19 = 6205,
- Zo_xx190A = 6210,
- Zo_xx190A00 = 6217,
- Zo_xxxx = 6226,
- Zo_xxxx00 = 6231,
- Zo_xxxx0A00 = 6238,
- Zo_xxxxyy = 6247,
- Zo_xxxxyyyy = 6254,
- Zo_xxxxyyyy0A00 = 6263,
- Zo_xxxxyyzz = 6276,
- Zo_xxyy = 6285,
- Zo_xxyy0A00 = 6290,
- Zo_xxyyyy = 6299,
- Zo_xxyyyy000000000000 = 6306,
- Zo_xxyyyy0A0000000000 = 6325,
- Zo_xxyyyyzz = 6344,
- Zo_xxyyyyzzzz = 6353,
- Zo_xxyyzzzz = 6364,
+ Zo_LidlPower = 3351,
+ Zo_LocalTemperature = 3361,
+ Zo_LocalTemperatureCalibration = 3378,
+ Zo_LocalTime = 3406,
+ Zo_LocationAge = 3416,
+ Zo_LocationMethod = 3428,
+ Zo_LocationType = 3443,
+ Zo_LockState = 3456,
+ Zo_LockType = 3466,
+ Zo_LongPollInterval = 3475,
+ Zo_LongPollIntervalMin = 3492,
+ Zo_MainsFrequency = 3512,
+ Zo_MainsVoltage = 3527,
+ Zo_Manufacturer = 3540,
+ Zo_MaxTempExperienced = 3553,
+ Zo_MeterTypeID = 3572,
+ Zo_MinTempExperienced = 3584,
+ Zo_Mode = 3603,
+ Zo_Model = 3608,
+ Zo_ModelId = 3614,
+ Zo_MotorStepSize = 3622,
+ Zo_Movement = 3636,
+ Zo_MullerLightMode = 3645,
+ Zo_MultiApplicationType = 3661,
+ Zo_MultiDescription = 3682,
+ Zo_MultiInApplicationType = 3699,
+ Zo_MultiInDescription = 3722,
+ Zo_MultiInNumberOfStates = 3741,
+ Zo_MultiInOutOfService = 3763,
+ Zo_MultiInReliability = 3783,
+ Zo_MultiInStatusFlags = 3802,
+ Zo_MultiInValue = 3821,
+ Zo_MultiNumberOfStates = 3834,
+ Zo_MultiOutApplicationType = 3854,
+ Zo_MultiOutDescription = 3878,
+ Zo_MultiOutNumberOfStates = 3898,
+ Zo_MultiOutOfService = 3921,
+ Zo_MultiOutOutOfService = 3939,
+ Zo_MultiOutReliability = 3960,
+ Zo_MultiOutRelinquishDefault = 3980,
+ Zo_MultiOutStatusFlags = 4006,
+ Zo_MultiOutValue = 4026,
+ Zo_MultiReliability = 4040,
+ Zo_MultiRelinquishDefault = 4057,
+ Zo_MultiStatusFlags = 4080,
+ Zo_MultiValue = 4097,
+ Zo_MultipleScheduling = 4108,
+ Zo_NumberOfDevices = 4127,
+ Zo_NumberOfPrimaries = 4143,
+ Zo_NumberOfResets = 4161,
+ Zo_NumberofActuationsLift = 4176,
+ Zo_NumberofActuationsTilt = 4199,
+ Zo_Occupancy = 4222,
+ Zo_OccupancySensorType = 4232,
+ Zo_OccupiedCoolingSetpoint = 4252,
+ Zo_OccupiedHeatingSetpoint = 4276,
+ Zo_OnOffTransitionTime = 4300,
+ Zo_OpenPeriod = 4320,
+ Zo_OppleMode = 4331,
+ Zo_OutdoorTemperature = 4341,
+ Zo_OverTempTotalDwell = 4360,
+ Zo_PICoolingDemand = 4379,
+ Zo_PIHeatingDemand = 4395,
+ Zo_POD = 4411,
+ Zo_Panic = 4415,
+ Zo_PartNumber = 4421,
+ Zo_PersistentMemoryWrites = 4432,
+ Zo_PersonalAlarm = 4455,
+ Zo_PhysicalClosedLimit = 4469,
+ Zo_PhysicalClosedLimitLift = 4489,
+ Zo_PhysicalClosedLimitTilt = 4513,
+ Zo_Power = 4537,
+ Zo_Power2 = 4543,
+ Zo_Power3 = 4550,
+ Zo_Power4 = 4557,
+ Zo_PowerOffEffect = 4564,
+ Zo_PowerOnRecall = 4579,
+ Zo_PowerOnTimer = 4593,
+ Zo_PowerSource = 4606,
+ Zo_PowerThreshold = 4618,
+ Zo_Pressure = 4633,
+ Zo_PressureMaxMeasuredValue = 4642,
+ Zo_PressureMaxScaledValue = 4667,
+ Zo_PressureMinMeasuredValue = 4690,
+ Zo_PressureMinScaledValue = 4715,
+ Zo_PressureScale = 4738,
+ Zo_PressureScaledTolerance = 4752,
+ Zo_PressureScaledValue = 4776,
+ Zo_PressureTolerance = 4796,
+ Zo_Primary1Intensity = 4814,
+ Zo_Primary1X = 4832,
+ Zo_Primary1Y = 4842,
+ Zo_Primary2Intensity = 4852,
+ Zo_Primary2X = 4870,
+ Zo_Primary2Y = 4880,
+ Zo_Primary3Intensity = 4890,
+ Zo_Primary3X = 4908,
+ Zo_Primary3Y = 4918,
+ Zo_ProductCode = 4928,
+ Zo_ProductRevision = 4940,
+ Zo_ProductURL = 4956,
+ Zo_QualityMeasure = 4967,
+ Zo_RGB = 4982,
+ Zo_RGBb = 4986,
+ Zo_RMSCurrent = 4991,
+ Zo_RMSVoltage = 5002,
+ Zo_ReactivePower = 5013,
+ Zo_RecallScene = 5027,
+ Zo_RemainingTime = 5039,
+ Zo_RemoteSensing = 5053,
+ Zo_RemoveAllGroups = 5067,
+ Zo_RemoveAllScenes = 5083,
+ Zo_RemoveGroup = 5099,
+ Zo_RemoveScene = 5111,
+ Zo_ResetAlarm = 5123,
+ Zo_ResetAllAlarms = 5134,
+ Zo_SWBuildID = 5149,
+ Zo_Sat = 5159,
+ Zo_SatMove = 5163,
+ Zo_SatStep = 5171,
+ Zo_SceneCount = 5179,
+ Zo_SceneValid = 5190,
+ Zo_ScheduleMode = 5201,
+ Zo_SeaPressure = 5214,
+ Zo_ShortPollInterval = 5226,
+ Zo_Shutter = 5244,
+ Zo_ShutterClose = 5252,
+ Zo_ShutterLift = 5265,
+ Zo_ShutterOpen = 5277,
+ Zo_ShutterStop = 5289,
+ Zo_ShutterTilt = 5301,
+ Zo_SoftwareRevision = 5313,
+ Zo_StackVersion = 5330,
+ Zo_StandardTime = 5343,
+ Zo_StartUpOnOff = 5356,
+ Zo_Status = 5369,
+ Zo_StoreScene = 5376,
+ Zo_SwitchType = 5387,
+ Zo_SystemMode = 5398,
+ Zo_TRVBoost = 5409,
+ Zo_TRVChildProtection = 5418,
+ Zo_TRVMirrorDisplay = 5437,
+ Zo_TRVMode = 5454,
+ Zo_TRVWindowOpen = 5462,
+ Zo_TempTarget = 5476,
+ Zo_Temperature = 5487,
+ Zo_TemperatureMaxMeasuredValue = 5499,
+ Zo_TemperatureMinMeasuredValue = 5527,
+ Zo_TemperatureTolerance = 5555,
+ Zo_TerncyDuration = 5576,
+ Zo_TerncyRotate = 5591,
+ Zo_ThSetpoint = 5604,
+ Zo_Time = 5615,
+ Zo_TimeEpoch = 5620,
+ Zo_TimeStatus = 5630,
+ Zo_TimeZone = 5641,
+ Zo_TotalProfileNum = 5650,
+ Zo_TuyaAutoLock = 5666,
+ Zo_TuyaAwayDays = 5679,
+ Zo_TuyaAwayTemp = 5692,
+ Zo_TuyaBattery = 5705,
+ Zo_TuyaBoostTime = 5717,
+ Zo_TuyaChildLock = 5731,
+ Zo_TuyaComfortTemp = 5745,
+ Zo_TuyaEcoTemp = 5761,
+ Zo_TuyaFanMode = 5773,
+ Zo_TuyaForceMode = 5785,
+ Zo_TuyaMaxTemp = 5799,
+ Zo_TuyaMinTemp = 5811,
+ Zo_TuyaPreset = 5823,
+ Zo_TuyaScheduleHolidays = 5834,
+ Zo_TuyaScheduleWorkdays = 5855,
+ Zo_TuyaTempTarget = 5876,
+ Zo_TuyaValveDetection = 5891,
+ Zo_TuyaValvePosition = 5910,
+ Zo_TuyaWeekSelect = 5928,
+ Zo_TuyaWindowDetection = 5943,
+ Zo_UnoccupiedCoolingSetpoint = 5963,
+ Zo_UnoccupiedHeatingSetpoint = 5989,
+ Zo_UtilityName = 6015,
+ Zo_ValidUntilTime = 6027,
+ Zo_ValvePosition = 6042,
+ Zo_VelocityLift = 6056,
+ Zo_ViewGroup = 6069,
+ Zo_ViewScene = 6079,
+ Zo_Water = 6089,
+ Zo_WhitePointX = 6095,
+ Zo_WhitePointY = 6107,
+ Zo_WindowCoveringType = 6119,
+ Zo_X = 6138,
+ Zo_Y = 6140,
+ Zo_ZCLVersion = 6142,
+ Zo_ZoneState = 6153,
+ Zo_ZoneStatus = 6163,
+ Zo_ZoneStatusChange = 6174,
+ Zo_ZoneType = 6191,
+ Zo_xx = 6200,
+ Zo_xx000A00 = 6203,
+ Zo_xx0A = 6212,
+ Zo_xx0A00 = 6217,
+ Zo_xx19 = 6224,
+ Zo_xx190A = 6229,
+ Zo_xx190A00 = 6236,
+ Zo_xxxx = 6245,
+ Zo_xxxx00 = 6250,
+ Zo_xxxx0A00 = 6257,
+ Zo_xxxxyy = 6266,
+ Zo_xxxxyyyy = 6273,
+ Zo_xxxxyyyy0A00 = 6282,
+ Zo_xxxxyyzz = 6295,
+ Zo_xxyy = 6304,
+ Zo_xxyy0A00 = 6309,
+ Zo_xxyyyy = 6318,
+ Zo_xxyyyy000000000000 = 6325,
+ Zo_xxyyyy0A0000000000 = 6344,
+ Zo_xxyyyyzz = 6363,
+ Zo_xxyyyyzzzz = 6372,
+ Zo_xxyyzzzz = 6383,
};
diff --git a/tasmota/xdrv_23_zigbee_5_converters.ino b/tasmota/xdrv_23_zigbee_5_converters.ino
index fe4d219c0..f91b50498 100644
--- a/tasmota/xdrv_23_zigbee_5_converters.ino
+++ b/tasmota/xdrv_23_zigbee_5_converters.ino
@@ -723,8 +723,6 @@ public:
void log(void) {
- char hex_char[_payload.len()*2+2];
- ToHex_P((unsigned char*)_payload.getBuffer(), _payload.len(), hex_char, sizeof(hex_char));
Response_P(PSTR("{\"" D_JSON_ZIGBEEZCL_RECEIVED "\":{"
"\"groupid\":%d," "\"clusterid\":\"0x%04X\"," "\"srcaddr\":\"0x%04X\","
"\"srcendpoint\":%d," "\"dstendpoint\":%d," "\"wasbroadcast\":%d,"
@@ -732,18 +730,18 @@ public:
"\"fc\":\"0x%02X\","
"\"frametype\":%d,\"direction\":%d,\"disableresp\":%d,"
"\"manuf\":\"0x%04X\",\"transact\":%d,"
- "\"cmdid\":\"0x%02X\",\"payload\":\"%s\"}}"),
+ "\"cmdid\":\"0x%02X\",\"payload\":\"%_B\"}}"),
_groupaddr, _cluster_id, _srcaddr,
_srcendpoint, _dstendpoint, _wasbroadcast,
_linkquality, _securityuse, _seqnumber,
_frame_control,
_frame_control.b.frame_type, _frame_control.b.direction, _frame_control.b.disable_def_resp,
_manuf_code, _transact_seq, _cmd_id,
- hex_char);
+ &_payload);
if (Settings.flag3.tuya_serial_mqtt_publish) {
MqttPublishPrefixTopicRulesProcess_P(TELE, PSTR(D_RSLT_SENSOR));
} else {
- AddLogZ_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_ZIGBEE "%s"), TasmotaGlobal.mqtt_data);
+ AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_ZIGBEE "%s"), TasmotaGlobal.mqtt_data);
}
}
@@ -869,7 +867,7 @@ uint8_t toPercentageCR2032(uint32_t voltage) {
// Adds to buf:
// - n bytes: value (typically between 1 and 4 bytes, or bigger for strings)
// returns number of bytes of attribute, or <0 if error
-int32_t encodeSingleAttribute(class SBuffer &buf, double val_d, const char *val_str, uint8_t attrtype) {
+int32_t encodeSingleAttribute(SBuffer &buf, double val_d, const char *val_str, uint8_t attrtype) {
uint32_t len = Z_getDatatypeLen(attrtype); // pre-compute length, overloaded for variable length attributes
uint32_t u32 = val_d;
int32_t i32 = val_d;
@@ -1286,6 +1284,7 @@ void ZCLFrame::removeInvalidAttributes(Z_attribute_list& attr_list) {
// Note: both function are now split to compute on extracted attributes
//
void ZCLFrame::computeSyntheticAttributes(Z_attribute_list& attr_list) {
+ const Z_Device & device = zigbee_devices.findShortAddr(_srcaddr);
const char * model_c = zigbee_devices.getModelId(_srcaddr); // null if unknown
String modelId((char*) model_c);
// scan through attributes and apply specific converters
@@ -1332,7 +1331,37 @@ void ZCLFrame::computeSyntheticAttributes(Z_attribute_list& attr_list) {
}
}
break;
- case 0x04030000: // Pressure
+ case 0x03000000: // Hue
+ case 0x03000001: // Sat
+ case 0x03000003: // X
+ case 0x03000004: // Y
+ { // generate synthetic RGB
+ const Z_attribute * attr_rgb = attr_list.findAttribute(PSTR("RGB"));
+ if (attr_rgb == nullptr) { // make sure we didn't already computed it
+ uint8_t brightness = 255;
+ if (device.valid()) {
+ const Z_Data_Light & light = device.data.find(_srcendpoint);
+ if ((&light != nullptr) && (light.validDimmer())) {
+ // Dimmer has a valid value
+ brightness = changeUIntScale(light.getDimmer(), 0, 254, 0, 255); // range is 0..255
+ }
+ }
+
+ const Z_attribute * attr_hue = attr_list.findAttribute(0x0300, 0x0000);
+ const Z_attribute * attr_sat = attr_list.findAttribute(0x0300, 0x0001);
+ const Z_attribute * attr_x = attr_list.findAttribute(0x0300, 0x0003);
+ const Z_attribute * attr_y = attr_list.findAttribute(0x0300, 0x0004);
+ if (attr_hue && attr_sat) {
+ uint8_t sat = changeUIntScale(attr_sat->getUInt(), 0, 254, 0, 255);
+ uint16_t hue = changeUIntScale(attr_hue->getUInt(), 0, 254, 0, 360);
+ Z_Data_Light::toRGBAttributesHSB(attr_list, hue, sat, brightness);
+ } else if (attr_x && attr_y) {
+ Z_Data_Light::toRGBAttributesXYB(attr_list, attr_x->getUInt(), attr_y->getUInt(), brightness);
+ }
+ }
+ }
+ break;
+ case 0x04030000: // SeaPressure
{
int16_t pressure = attr.getInt();
int16_t pressure_sealevel = (pressure / FastPrecisePow(1.0 - ((float)Settings.altitude / 44330.0f), 5.255f)) - 21.6f;
@@ -1625,7 +1654,7 @@ void ZCLFrame::parseClusterSpecificCommand(Z_attribute_list& attr_list) {
Z_Device & device = zigbee_devices.getShortAddr(_srcaddr);
if ((device.debounce_endpoint != 0) && (device.debounce_endpoint == _srcendpoint) && (device.debounce_transact == _transact_seq)) {
// this is a duplicate, drop the packet
- AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_ZIGBEE "Discarding duplicate command from 0x%04X, endpoint %d"), _srcaddr, _srcendpoint);
+ AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_ZIGBEE "Discarding duplicate command from 0x%04X, endpoint %d"), _srcaddr, _srcendpoint);
} else {
// reset the duplicate marker, parse the packet normally, and set a timer to reset the marker later (which will discard any existing timer for the same device/endpoint)
device.debounce_endpoint = _srcendpoint;
@@ -1642,6 +1671,27 @@ void ZCLFrame::parseClusterSpecificCommand(Z_attribute_list& attr_list) {
}
}
}
+ // Send Default Response to acknowledge the attribute reporting
+ if (0 == _frame_control.b.disable_def_resp) {
+ // the device expects a default response
+ SBuffer buf(2);
+ buf.add8(_cmd_id);
+ buf.add8(0x00); // Status = OK
+
+ ZigbeeZCLSend_Raw(ZigbeeZCLSendMessage({
+ _srcaddr,
+ 0x0000,
+ _cluster_id,
+ _srcendpoint,
+ ZCL_DEFAULT_RESPONSE,
+ _manuf_code,
+ false /* not cluster specific */,
+ false /* noresponse */,
+ true /* direct no retry */,
+ _transact_seq, /* zcl transaction id */
+ buf.getBuffer(), buf.len()
+ }));
+ }
}
// ======================================================================
@@ -2181,6 +2231,58 @@ void Z_Data::toAttributes(Z_attribute_list & attr_list) const {
}
}
+// Add both attributes RGB and RGBb based on the inputs
+// r,g,b are expected to be 100% brightness
+// brightness is expected 0..255
+void Z_Data_Light::toRGBAttributesRGBb(Z_attribute_list & attr_list, uint8_t r, uint8_t g, uint8_t b, uint8_t brightness) {
+ SBuffer rgb(3);
+ rgb.add8(r);
+ rgb.add8(g);
+ rgb.add8(b);
+ attr_list.addAttribute(PSTR("RGB"), true).setBuf(rgb, 0, 3);
+ // now blend with brightness
+ r = changeUIntScale(r, 0, 255, 0, brightness);
+ g = changeUIntScale(g, 0, 255, 0, brightness);
+ b = changeUIntScale(b, 0, 255, 0, brightness);
+ rgb.set8(0, r);
+ rgb.set8(1, g);
+ rgb.set8(2, b);
+ attr_list.addAttribute(PSTR("RGBb"), true).setBuf(rgb, 0, 3);
+}
+
+// Convert from Hue/Sat + Brightness to RGB+RGBb
+// sat: 0..255
+// hue: 0..359
+// brightness: 0..255
+void Z_Data_Light::toRGBAttributesHSB(Z_attribute_list & attr_list, uint16_t hue, uint8_t sat, uint8_t brightness) {
+ uint8_t r,g,b;
+ HsToRgb(hue, sat, &r, &g, &b);
+ Z_Data_Light::toRGBAttributesRGBb(attr_list, r, g, b, brightness);
+}
+
+// Convert X/Y to RGB and RGBb
+// X: 0..65535
+// Y: 0..65535
+// brightness: 0..255
+void Z_Data_Light::toRGBAttributesXYB(Z_attribute_list & attr_list, uint16_t x, uint16_t y, uint8_t brightness) {
+ uint8_t r,g,b;
+ XyToRgb(x / 65535.0f, y / 65535.0f, &r, &g, &b);
+ Z_Data_Light::toRGBAttributesRGBb(attr_list, r, g, b, brightness);
+}
+
+void Z_Data_Light::toRGBAttributes(Z_attribute_list & attr_list) const {
+ uint8_t brightness = 255;
+ if (validDimmer()) {
+ brightness = changeUIntScale(getDimmer(), 0, 254, 0, 255); // range is 0..255
+ }
+ if (validHue() && validSat()) {
+ uint8_t sat = changeUIntScale(getSat(), 0, 254, 0, 255);
+ Z_Data_Light::toRGBAttributesHSB(attr_list, getHue(), sat, brightness);
+ } else if (validX() && validY()) {
+ Z_Data_Light::toRGBAttributesXYB(attr_list, getX(), getY(), brightness);
+ }
+}
+
//
// Check if this device needs Battery reporting
// This is useful for IKEA or Philips devices that tend to drain battery quickly when Battery reporting is set
diff --git a/tasmota/xdrv_23_zigbee_6_commands.ino b/tasmota/xdrv_23_zigbee_6_commands.ino
index 89e3a6e65..ea0196368 100644
--- a/tasmota/xdrv_23_zigbee_6_commands.ino
+++ b/tasmota/xdrv_23_zigbee_6_commands.ino
@@ -69,6 +69,7 @@ const Z_CommandConverter Z_Commands[] PROGMEM = {
{ Z_(PowerOffEffect), 0x0006, 0x40, 0x81, Z_(xxyy) }, // Power Off With Effect
{ Z_(PowerOnRecall), 0x0006, 0x41, 0x81, Z_() }, // Power On With Recall Global Scene
{ Z_(PowerOnTimer), 0x0006, 0x42, 0x81, Z_(xxyyyyzzzz) }, // Power On with Timed Off
+ { Z_(LidlPower), 0x0006, 0xFD, 0x01, Z_(xx) }, // Lidl specific encoding
{ Z_(Power), 0x0006, 0xFF, 0x01, Z_() }, // 0=Off, 1=On, 2=Toggle
{ Z_(Dimmer), 0x0008, 0x04, 0x01, Z_(xx0A00) }, // Move to Level with On/Off, xx=0..254 (255 is invalid)
{ Z_(DimmerUp), 0x0008, 0x06, 0x01, Z_(00190200) }, // Step up by 10%, 0.2 secs
@@ -81,6 +82,7 @@ const Z_CommandConverter Z_Commands[] PROGMEM = {
{ Z_(HueSat), 0x0300, 0x06, 0x01, Z_(xxyy0A00) }, // Hue, Sat
{ Z_(Color), 0x0300, 0x07, 0x01, Z_(xxxxyyyy0A00) }, // x, y (uint16)
{ Z_(CT), 0x0300, 0x0A, 0x01, Z_(xxxx0A00) }, // Color Temperature Mireds (uint16)
+ { Z_(RGB), 0x0300, 0xF0, 0x81, Z_() }, // synthetic commands converting RGB to XY
{ Z_(ShutterOpen), 0x0102, 0x00, 0x01, Z_() },
{ Z_(ShutterClose), 0x0102, 0x01, 0x01, Z_() },
{ Z_(ShutterStop), 0x0102, 0x02, 0x01, Z_() },
@@ -281,7 +283,7 @@ void convertClusterSpecific(class Z_attribute_list &attr_list, uint16_t cluster,
uint8_t conv_direction;
Z_XYZ_Var xyz;
-//AddLog_P(LOG_LEVEL_INFO, PSTR(">>> len = %d - %02X%02X%02X"), payload.len(), payload.get8(0), payload.get8(1), payload.get8(2));
+ //AddLog_P(LOG_LEVEL_INFO, PSTR(">>> len = %d - %02X%02X%02X"), payload.len(), payload.get8(0), payload.get8(1), payload.get8(2));
for (uint32_t i = 0; i < sizeof(Z_Commands) / sizeof(Z_Commands[0]); i++) {
const Z_CommandConverter *conv = &Z_Commands[i];
uint16_t conv_cluster = pgm_read_word(&conv->cluster);
@@ -297,18 +299,18 @@ void convertClusterSpecific(class Z_attribute_list &attr_list, uint16_t cluster,
// - payload exactly matches conv->param (conv->param may be longer)
// - payload matches conv->param until 'x', 'y' or 'z'
const char * p = Z_strings + pgm_read_word(&conv->param_offset);
- //AddLog_P(LOG_LEVEL_INFO, PSTR(">>>++1 param = %s"), p);
+ //AddLog_P(LOG_LEVEL_INFO, PSTR(">>>++1 param = %s"), p);
bool match = true;
for (uint8_t i = 0; i < payload.len(); i++) {
const char c1 = pgm_read_byte(p);
// const char c2 = pgm_read_byte(p+1);
- //AddLog_P(LOG_LEVEL_INFO, PSTR(">>>++2 c1 = %c, c2 = %c"), c1, c2);
+ //AddLog_P(LOG_LEVEL_INFO, PSTR(">>>++2 c1 = %c, c2 = %c"), c1, c2);
if ((0x00 == c1) || isXYZ(c1)) {
break;
}
const char * p2 = p;
uint32_t nextbyte = parseHex_P(&p2, 2);
- //AddLog_P(LOG_LEVEL_INFO, PSTR(">>>++3 parseHex_P = %02X"), nextbyte);
+ //AddLog_P(LOG_LEVEL_INFO, PSTR(">>>++3 parseHex_P = %02X"), nextbyte);
if (nextbyte != payload.get8(i)) {
match = false;
break;
@@ -460,6 +462,12 @@ void convertClusterSpecific(class Z_attribute_list &attr_list, uint16_t cluster,
}
}
+/*********************************************************************************************\
+ *
+ * Tuya Zigbee specific protocol
+ *
+\*********************************************************************************************/
+// Parse a sinlge attribute value and convert to human readable JSON attribute value
void parseSingleTuyaAttribute(Z_attribute & attr, const SBuffer &buf,
uint32_t i, uint32_t len, int32_t attrtype) {
@@ -518,6 +526,11 @@ bool convertTuyaSpecificCluster(class Z_attribute_list &attr_list, uint16_t clus
return false;
}
+/*********************************************************************************************\
+ *
+ * Find commands
+ *
+\*********************************************************************************************/
// Find the command details by command name
// Only take commands outgoing, i.e. direction == 0
// If not found:
@@ -603,4 +616,11 @@ uint32_t ZigbeeAliasOrNumber(const char *state_text) {
}
}
+
+/*********************************************************************************************\
+ *
+ * Send Zigbee commands
+ *
+\*********************************************************************************************/
+
#endif // USE_ZIGBEE
diff --git a/tasmota/xdrv_23_zigbee_7_0_statemachine.ino b/tasmota/xdrv_23_zigbee_7_0_statemachine.ino
index cc23c7a92..fcacc81c2 100644
--- a/tasmota/xdrv_23_zigbee_7_0_statemachine.ino
+++ b/tasmota/xdrv_23_zigbee_7_0_statemachine.ino
@@ -132,7 +132,7 @@ const char kZigbeeStarted[] PROGMEM = D_LOG_ZIGBEE "Zigbee started";
const char kResetting[] PROGMEM = "Resetting configuration";
const char kResettingDevice[] PROGMEM = D_LOG_ZIGBEE "Resetting EZSP device";
const char kReconfiguringDevice[] PROGMEM = D_LOG_ZIGBEE "Factory reset EZSP device";
-const char kZNP12[] PROGMEM = "Only ZNP 1.2 is currently supported";
+const char kZNP123[] PROGMEM = "Only ZNP 1.2 and 3.x are currently supported";
const char kEZ8[] PROGMEM = "Only EZSP protocol v8 is currently supported";
const char kAbort[] PROGMEM = "Abort";
const char kZigbeeAbort[] PROGMEM = D_LOG_ZIGBEE "Abort";
@@ -194,6 +194,7 @@ ZBM(ZBS_LOGTYPE_DEVICE, Z_SRSP | Z_SAPI, SAPI_READ_CONFIGURATION, Z_SUCCESS, CON
// Write configuration - write success
ZBM(ZBR_W_OK, Z_SRSP | Z_SAPI, SAPI_WRITE_CONFIGURATION, Z_SUCCESS ) // 660500 - Write Configuration
ZBM(ZBR_WNV_OK, Z_SRSP | Z_SYS, SYS_OSAL_NV_WRITE, Z_SUCCESS ) // 610900 - NV Write
+ZBM(ZBR_WNV_OKE, Z_SRSP | Z_SYS, SYS_OSAL_NV_WRITE ) // 6109xx - NV Write, OK or error
// Factory reset
ZBM(ZBS_FACTRES, Z_SREQ | Z_SAPI, SAPI_WRITE_CONFIGURATION, CONF_STARTUP_OPTION, 0x01 /* len */, 0x03 ) // 2605030103
@@ -415,7 +416,7 @@ static const Zigbee_Instruction zb_prog[] PROGMEM = {
// Z_ZDO:startupFromApp
//ZI_LOG(LOG_LEVEL_INFO, D_LOG_ZIGBEE "starting zigbee coordinator")
ZI_SEND(ZBS_STARTUPFROMAPP) // start coordinator
- ZI_WAIT_RECV(2000, ZBR_STARTUPFROMAPP) // wait for sync ack of command
+ ZI_WAIT_RECV(5000, ZBR_STARTUPFROMAPP) // wait for sync ack of command
ZI_WAIT_UNTIL_FUNC(10000, AREQ_STARTUPFROMAPP, &ZNP_ReceiveStateChange) // wait for async message that coordinator started
ZI_SEND(ZBS_GETDEVICEINFO) // GetDeviceInfo
ZI_WAIT_RECV_FUNC(2000, ZBR_GETDEVICEINFO, &ZNP_ReceiveDeviceInfo)
@@ -472,7 +473,7 @@ static const Zigbee_Instruction zb_prog[] PROGMEM = {
ZI_SEND(ZBS_W_PFGKEN) // write PRECFGKEY Enable
ZI_WAIT_RECV(1000, ZBR_W_OK)
ZI_SEND(ZBS_WNV_SECMODE) // write Security Mode
- ZI_WAIT_RECV(1000, ZBR_WNV_OK)
+ ZI_WAIT_RECV(1000, ZBR_WNV_OKE) // Tolerate error for ZNP 3.x
ZI_SEND(ZBS_W_ZDODCB) // write Z_ZDO Direct CB
ZI_WAIT_RECV(1000, ZBR_W_OK)
// Now mark the device as ready, writing 0x55 in memory slot 0x0F00
@@ -558,7 +559,7 @@ static const Zigbee_Instruction zb_prog[] PROGMEM = {
// Error: version of Z-Stack is not supported
ZI_LABEL(ZIGBEE_LABEL_UNSUPPORTED_VERSION)
- ZI_MQTT_STATE(ZIGBEE_STATUS_UNSUPPORTED_VERSION, kZNP12)
+ ZI_MQTT_STATE(ZIGBEE_STATUS_UNSUPPORTED_VERSION, kZNP123)
ZI_GOTO(ZIGBEE_LABEL_ABORT)
// Abort state machine, general error
@@ -789,38 +790,38 @@ static const Zigbee_Instruction zb_prog[] PROGMEM = {
// configure EFR32
ZI_MQTT_STATE(ZIGBEE_STATUS_STARTING, kConfiguredCoord)
- ZI_SEND(ZBS_SET_ADDR_TABLE) ZI_WAIT_RECV(500, ZBR_SET_OK) // Address table size
- ZI_SEND(ZBS_SET_MCAST_TABLE) ZI_WAIT_RECV(500, ZBR_SET_OK)
- ZI_SEND(ZBS_SET_STK_PROF) ZI_WAIT_RECV(500, ZBR_SET_OK)
- ZI_SEND(ZBS_SET_SEC_LEVEL) ZI_WAIT_RECV(500, ZBR_SET_OK)
- ZI_SEND(ZBS_SET_MAX_DEVICES) ZI_WAIT_RECV(500, ZBR_SET_OK)
- ZI_SEND(ZBS_SET_INDIRECT_TMO) ZI_WAIT_RECV(500, ZBR_SET_OK)
- ZI_SEND(ZBS_SET_TC_CACHE) ZI_WAIT_RECV(500, ZBR_SET_OK)
- ZI_SEND(ZBS_SET_ROUTE_TBL) ZI_WAIT_RECV(500, ZBR_SET_OK)
- ZI_SEND(ZBS_SET_KEY_TBL) ZI_WAIT_RECV(500, ZBR_SET_OK)
- ZI_SEND(ZBS_SET_PANID_CNFLCT) ZI_WAIT_RECV(500, ZBR_SET_OK)
- ZI_SEND(ZBS_SET_ZDO_REQ) ZI_WAIT_RECV(500, ZBR_SET_OK)
- ZI_SEND(ZBS_SET_NETWORKS) ZI_WAIT_RECV(500, ZBR_SET_OK)
- ZI_SEND(ZBS_SET_PACKET_BUF) ZI_WAIT_RECV(500, ZBR_SET_OK2)
+ ZI_SEND(ZBS_SET_ADDR_TABLE) ZI_WAIT_RECV(2500, ZBR_SET_OK) // Address table size
+ ZI_SEND(ZBS_SET_MCAST_TABLE) ZI_WAIT_RECV(2500, ZBR_SET_OK)
+ ZI_SEND(ZBS_SET_STK_PROF) ZI_WAIT_RECV(2500, ZBR_SET_OK)
+ ZI_SEND(ZBS_SET_SEC_LEVEL) ZI_WAIT_RECV(2500, ZBR_SET_OK)
+ ZI_SEND(ZBS_SET_MAX_DEVICES) ZI_WAIT_RECV(2500, ZBR_SET_OK)
+ ZI_SEND(ZBS_SET_INDIRECT_TMO) ZI_WAIT_RECV(2500, ZBR_SET_OK)
+ ZI_SEND(ZBS_SET_TC_CACHE) ZI_WAIT_RECV(2500, ZBR_SET_OK)
+ ZI_SEND(ZBS_SET_ROUTE_TBL) ZI_WAIT_RECV(2500, ZBR_SET_OK)
+ ZI_SEND(ZBS_SET_KEY_TBL) ZI_WAIT_RECV(2500, ZBR_SET_OK)
+ ZI_SEND(ZBS_SET_PANID_CNFLCT) ZI_WAIT_RECV(2500, ZBR_SET_OK)
+ ZI_SEND(ZBS_SET_ZDO_REQ) ZI_WAIT_RECV(2500, ZBR_SET_OK)
+ ZI_SEND(ZBS_SET_NETWORKS) ZI_WAIT_RECV(2500, ZBR_SET_OK)
+ ZI_SEND(ZBS_SET_PACKET_BUF) ZI_WAIT_RECV(2500, ZBR_SET_OK2)
// read configuration
// TODO - not sure it's useful
- //ZI_SEND(ZBS_GET_APS_UNI) ZI_WAIT_RECV_FUNC(500, ZBR_GET_OK, &EZ_ReadAPSUnicastMessage)
+ //ZI_SEND(ZBS_GET_APS_UNI) ZI_WAIT_RECV_FUNC(2500, ZBR_GET_OK, &EZ_ReadAPSUnicastMessage)
// add endpoint 0x01 and 0x0B
- ZI_SEND(ZBS_ADD_ENDPOINT1) ZI_WAIT_RECV(500, ZBR_ADD_ENDPOINT)
- ZI_SEND(ZBS_ADD_ENDPOINTB) ZI_WAIT_RECV(500, ZBR_ADD_ENDPOINT)
+ ZI_SEND(ZBS_ADD_ENDPOINT1) ZI_WAIT_RECV(2500, ZBR_ADD_ENDPOINT)
+ ZI_SEND(ZBS_ADD_ENDPOINTB) ZI_WAIT_RECV(2500, ZBR_ADD_ENDPOINT)
// set Concentrator
- ZI_SEND(ZBS_SET_CONCENTRATOR) ZI_WAIT_RECV(500, ZBR_SET_CONCENTRATOR)
+ ZI_SEND(ZBS_SET_CONCENTRATOR) ZI_WAIT_RECV(2500, ZBR_SET_CONCENTRATOR)
// setInitialSecurityState
- ZI_SEND(ZBS_SET_POLICY_00) ZI_WAIT_RECV(500, ZBR_SET_POLICY_XX)
- ZI_SEND(ZBS_SET_POLICY_02) ZI_WAIT_RECV(500, ZBR_SET_POLICY_XX)
- ZI_SEND(ZBS_SET_POLICY_03) ZI_WAIT_RECV(500, ZBR_SET_POLICY_XX)
- // ZI_SEND(ZBS_SET_POLICY_04) ZI_WAIT_RECV(500, ZBR_SET_POLICY_XX)
- ZI_SEND(ZBS_SET_POLICY_05) ZI_WAIT_RECV(500, ZBR_SET_POLICY_XX)
- ZI_SEND(ZBS_SET_POLICY_06) ZI_WAIT_RECV(500, ZBR_SET_POLICY_XX)
+ ZI_SEND(ZBS_SET_POLICY_00) ZI_WAIT_RECV(2500, ZBR_SET_POLICY_XX)
+ ZI_SEND(ZBS_SET_POLICY_02) ZI_WAIT_RECV(2500, ZBR_SET_POLICY_XX)
+ ZI_SEND(ZBS_SET_POLICY_03) ZI_WAIT_RECV(2500, ZBR_SET_POLICY_XX)
+ // ZI_SEND(ZBS_SET_POLICY_04) ZI_WAIT_RECV(2500, ZBR_SET_POLICY_XX)
+ ZI_SEND(ZBS_SET_POLICY_05) ZI_WAIT_RECV(2500, ZBR_SET_POLICY_XX)
+ ZI_SEND(ZBS_SET_POLICY_06) ZI_WAIT_RECV(2500, ZBR_SET_POLICY_XX)
// Decide whether we try 'networkInit()' to restore configuration, or create a new network
ZI_CALL(&EZ_GotoIfResetConfig, ZIGBEE_LABEL_CONFIGURE_EZSP) // goto ZIGBEE_LABEL_CONFIGURE_EZSP if reset_config is set
@@ -830,12 +831,12 @@ static const Zigbee_Instruction zb_prog[] PROGMEM = {
// Try networkInit to restore settings, and check if network comes up
ZI_ON_TIMEOUT_GOTO(ZIGBEE_LABEL_BAD_CONFIG) //
ZI_ON_ERROR_GOTO(ZIGBEE_LABEL_BAD_CONFIG)
- ZI_SEND(ZBS_NETWORK_INIT) ZI_WAIT_RECV(500, ZBR_NETWORK_INIT)
+ ZI_SEND(ZBS_NETWORK_INIT) ZI_WAIT_RECV(2500, ZBR_NETWORK_INIT)
ZI_WAIT_RECV(1500, ZBR_NETWORK_UP) // wait for network to start
// check if configuration is ok
- ZI_SEND(ZBS_GET_KEY_NWK) ZI_WAIT_RECV_FUNC(500, ZBR_GET_KEY_NWK, &EZ_CheckKeyNWK)
- ZI_SEND(ZBS_GET_EUI64) ZI_WAIT_RECV_FUNC(500, ZBR_GET_EUI64, &EZ_GetEUI64)
- ZI_SEND(ZBS_GET_NETW_PARM) ZI_WAIT_RECV_FUNC(500, ZBR_CHECK_NETW_PARM, &EZ_NetworkParameters)
+ ZI_SEND(ZBS_GET_KEY_NWK) ZI_WAIT_RECV_FUNC(2500, ZBR_GET_KEY_NWK, &EZ_CheckKeyNWK)
+ ZI_SEND(ZBS_GET_EUI64) ZI_WAIT_RECV_FUNC(2500, ZBR_GET_EUI64, &EZ_GetEUI64)
+ ZI_SEND(ZBS_GET_NETW_PARM) ZI_WAIT_RECV_FUNC(2500, ZBR_CHECK_NETW_PARM, &EZ_NetworkParameters)
// all ok, proceed to next step
ZI_GOTO(ZIGBEE_LABEL_NETWORK_CONFIGURED)
@@ -851,9 +852,9 @@ static const Zigbee_Instruction zb_prog[] PROGMEM = {
ZI_ON_TIMEOUT_GOTO(ZIGBEE_LABEL_ABORT)
ZI_ON_ERROR_GOTO(ZIGBEE_LABEL_ABORT)
// set encryption keys
- ZI_SEND(ZBS_SET_SECURITY) ZI_WAIT_RECV(500, ZBR_SET_SECURITY)
+ ZI_SEND(ZBS_SET_SECURITY) ZI_WAIT_RECV(2500, ZBR_SET_SECURITY)
// formNetwork
- ZI_SEND(ZBS_FORM_NETWORK) ZI_WAIT_RECV(500, ZBR_FORM_NETWORK)
+ ZI_SEND(ZBS_FORM_NETWORK) ZI_WAIT_RECV(2500, ZBR_FORM_NETWORK)
ZI_WAIT_RECV(5000, ZBR_NETWORK_UP) // wait for network to start
ZI_LABEL(ZIGBEE_LABEL_NETWORK_CONFIGURED)
@@ -861,11 +862,11 @@ static const Zigbee_Instruction zb_prog[] PROGMEM = {
ZI_ON_TIMEOUT_GOTO(ZIGBEE_LABEL_ABORT)
ZI_ON_ERROR_GOTO(ZIGBEE_LABEL_ABORT)
// Query device information
- ZI_SEND(ZBS_GET_EUI64) ZI_WAIT_RECV_FUNC(500, ZBR_GET_EUI64, &EZ_GetEUI64)
- ZI_SEND(ZBS_GET_NODEID) ZI_WAIT_RECV_FUNC(500, ZBR_GET_NODEID, &EZ_GetNodeId)
+ ZI_SEND(ZBS_GET_EUI64) ZI_WAIT_RECV_FUNC(2500, ZBR_GET_EUI64, &EZ_GetEUI64)
+ ZI_SEND(ZBS_GET_NODEID) ZI_WAIT_RECV_FUNC(2500, ZBR_GET_NODEID, &EZ_GetNodeId)
// auto-register multicast group 0x0000
ZI_LOG(LOG_LEVEL_INFO, kZigbeeGroup0)
- ZI_SEND(ZBS_SET_MCAST_ENTRY) ZI_WAIT_RECV(500, ZBR_SET_MCAST_ENTRY)
+ ZI_SEND(ZBS_SET_MCAST_ENTRY) ZI_WAIT_RECV(2500, ZBR_SET_MCAST_ENTRY)
// ZI_LABEL(ZIGBEE_LABEL_READY)
ZI_MQTT_STATE(ZIGBEE_STATUS_OK, kStarted)
@@ -932,12 +933,12 @@ void ZigbeeGotoLabel(uint8_t label) {
}
// no label found, abort
- AddLog_P(LOG_LEVEL_ERROR, PSTR(D_LOG_ZIGBEE "Goto label not found, label=%d pc=%d"), label, zigbee.pc);
+ AddLog(LOG_LEVEL_ERROR, PSTR(D_LOG_ZIGBEE "Goto label not found, label=%d pc=%d"), label, zigbee.pc);
if (ZIGBEE_LABEL_ABORT != label) {
// if not already looking for ZIGBEE_LABEL_ABORT, goto ZIGBEE_LABEL_ABORT
ZigbeeGotoLabel(ZIGBEE_LABEL_ABORT);
} else {
- AddLog_P(LOG_LEVEL_ERROR, PSTR(D_LOG_ZIGBEE "Label Abort (%d) not present, aborting Zigbee"), ZIGBEE_LABEL_ABORT);
+ AddLog(LOG_LEVEL_ERROR, PSTR(D_LOG_ZIGBEE "Label Abort (%d) not present, aborting Zigbee"), ZIGBEE_LABEL_ABORT);
zigbee.state_machine = false;
zigbee.active = false;
}
@@ -955,7 +956,7 @@ void ZigbeeStateMachine_Run(void) {
// checking if timeout expired
if ((zigbee.next_timeout) && (now > zigbee.next_timeout)) { // if next_timeout == 0 then wait forever
if (!zigbee.state_no_timeout) {
- AddLog_P(LOG_LEVEL_INFO, PSTR(D_LOG_ZIGBEE "timeout, goto label %d"), zigbee.on_timeout_goto);
+ AddLog(LOG_LEVEL_INFO, PSTR(D_LOG_ZIGBEE "timeout, goto label %d"), zigbee.on_timeout_goto);
ZigbeeGotoLabel(zigbee.on_timeout_goto);
} else {
zigbee.state_waiting = false; // simply stop waiting
@@ -971,7 +972,7 @@ void ZigbeeStateMachine_Run(void) {
zigbee.state_no_timeout = false; // reset the no_timeout for next instruction
if (zigbee.pc > ARRAY_SIZE(zb_prog)) {
- AddLog_P(LOG_LEVEL_ERROR, PSTR(D_LOG_ZIGBEE "Invalid pc: %d, aborting"), zigbee.pc);
+ AddLog(LOG_LEVEL_ERROR, PSTR(D_LOG_ZIGBEE "Invalid pc: %d, aborting"), zigbee.pc);
zigbee.pc = -1;
}
if (zigbee.pc < 0) {
@@ -1020,7 +1021,7 @@ void ZigbeeStateMachine_Run(void) {
case ZGB_INSTR_STOP:
zigbee.state_machine = false;
if (cur_d8) {
- AddLog_P(LOG_LEVEL_ERROR, PSTR(D_LOG_ZIGBEE "Stopping (%d)"), cur_d8);
+ AddLog(LOG_LEVEL_ERROR, PSTR(D_LOG_ZIGBEE "Stopping (%d)"), cur_d8);
}
break;
case ZGB_INSTR_CALL:
@@ -1094,7 +1095,7 @@ void ZigbeeStateMachine_Run(void) {
//
// Process a bytes buffer and call any callback that matches the received message
//
-int32_t ZigbeeProcessInput(class SBuffer &buf) {
+int32_t ZigbeeProcessInput(SBuffer &buf) {
if (!zigbee.state_machine) { return -1; } // if state machine is stopped, send 'ignore' message
// apply the receive filter, acts as 'startsWith()'
diff --git a/tasmota/xdrv_23_zigbee_7_5_map.ino b/tasmota/xdrv_23_zigbee_7_5_map.ino
index c9013b665..f6a50e974 100644
--- a/tasmota/xdrv_23_zigbee_7_5_map.ino
+++ b/tasmota/xdrv_23_zigbee_7_5_map.ino
@@ -76,7 +76,7 @@ public:
edges()
{}
- void reset(void) { edges.reset(); }
+ void reset(void) { edges.reset(); zigbee_devices.clearDeviceRouterInfo(); }
Z_Mapper_Edge & findEdge(const Z_Mapper_Edge & edge2);
bool addEdge(const Z_Mapper_Edge & edge2);
diff --git a/tasmota/xdrv_23_zigbee_8_parsers.ino b/tasmota/xdrv_23_zigbee_8_parsers.ino
index c85b1a7f0..a714d01af 100644
--- a/tasmota/xdrv_23_zigbee_8_parsers.ino
+++ b/tasmota/xdrv_23_zigbee_8_parsers.ino
@@ -74,7 +74,7 @@ void EZ_ERROR(uint8_t error_code) {
MqttPublishPrefixTopicRulesProcess_P(RESULT_OR_TELE, PSTR(D_JSON_ZIGBEE_STATE));
}
-int32_t EZ_ReadAPSUnicastMessage(int32_t res, class SBuffer &buf) {
+int32_t EZ_ReadAPSUnicastMessage(int32_t res, SBuffer &buf) {
// Called when receiving a response from getConfigurationValue
// Value is in bytes 2+3
// uint16_t value = buf.get16(2);
@@ -88,7 +88,7 @@ int32_t EZ_ReadAPSUnicastMessage(int32_t res, class SBuffer &buf) {
//
// Handle a "getEui64" incoming message
//
-int32_t EZ_GetEUI64(int32_t res, class SBuffer &buf) {
+int32_t EZ_GetEUI64(int32_t res, SBuffer &buf) {
localIEEEAddr = buf.get64(2);
return res;
}
@@ -96,7 +96,7 @@ int32_t EZ_GetEUI64(int32_t res, class SBuffer &buf) {
//
// Handle a "getEui64" incoming message
//
-int32_t EZ_GetNodeId(int32_t res, class SBuffer &buf) {
+int32_t EZ_GetNodeId(int32_t res, SBuffer &buf) {
localShortAddr = buf.get8(2);
return res;
}
@@ -104,7 +104,7 @@ int32_t EZ_GetNodeId(int32_t res, class SBuffer &buf) {
//
// Handle a "getNetworkParameters" incoming message
//
-int32_t EZ_NetworkParameters(int32_t res, class SBuffer &buf) {
+int32_t EZ_NetworkParameters(int32_t res, SBuffer &buf) {
uint8_t node_type = buf.get8(3);
// ext panid: 4->11
// panid: 12->13
@@ -115,12 +115,10 @@ int32_t EZ_NetworkParameters(int32_t res, class SBuffer &buf) {
// localIEEEAddr = long_adr;
// localShortAddr = short_adr;
- char hex[20];
- Uint64toHex(localIEEEAddr, hex, 64);
Response_P(PSTR("{\"" D_JSON_ZIGBEE_STATE "\":{"
- "\"Status\":%d,\"IEEEAddr\":\"0x%s\",\"ShortAddr\":\"0x%04X\""
+ "\"Status\":%d,\"IEEEAddr\":\"0x%_X\",\"ShortAddr\":\"0x%04X\""
",\"DeviceType\":%d}}"),
- ZIGBEE_STATUS_EZ_INFO, hex, localShortAddr, node_type);
+ ZIGBEE_STATUS_EZ_INFO, &localIEEEAddr, localShortAddr, node_type);
MqttPublishPrefixTopicRulesProcess_P(RESULT_OR_TELE, PSTR(D_JSON_ZIGBEE_STATE));
@@ -130,7 +128,7 @@ int32_t EZ_NetworkParameters(int32_t res, class SBuffer &buf) {
//
// Analyze response to "getKey" and check NWK key
//
-int32_t EZ_CheckKeyNWK(int32_t res, class SBuffer &buf) {
+int32_t EZ_CheckKeyNWK(int32_t res, SBuffer &buf) {
// uint8_t status = buf.get8(2);
// uint16_t bitmask = buf.get16(3);
uint8_t key_type = buf.get8(5);
@@ -149,7 +147,7 @@ int32_t EZ_CheckKeyNWK(int32_t res, class SBuffer &buf) {
//
// Handle a "incomingRouteErrorHandler" incoming message
//
-int32_t EZ_RouteError(int32_t res, const class SBuffer &buf) {
+int32_t EZ_RouteError(int32_t res, const SBuffer &buf) {
uint8_t status = buf.get8(2);
uint16_t shortaddr = buf.get16(3);
@@ -165,7 +163,7 @@ int32_t EZ_RouteError(int32_t res, const class SBuffer &buf) {
//
// Handle a "permitJoining" incoming message
//
-int32_t EZ_PermitJoinRsp(int32_t res, const class SBuffer &buf) {
+int32_t EZ_PermitJoinRsp(int32_t res, const SBuffer &buf) {
uint8_t status = buf.get8(2);
if (status) { // only report if there is an error
@@ -189,13 +187,13 @@ void Z_PermitJoinDisable(void) {
//
// We normally ignore the message, but it's a way to sniff group ids for IKEA remote
// In case of a multicast message sent to 0xFFFD with non-null group id, we log the group id
-int32_t EZ_MessageSent(int32_t res, const class SBuffer &buf) {
+int32_t EZ_MessageSent(int32_t res, const SBuffer &buf) {
uint8_t message_type = buf.get8(2);
uint16_t dst_addr = buf.get16(3);
uint16_t group_addr = buf.get16(13);
if ((EMBER_OUTGOING_MULTICAST == message_type) && (0xFFFD == dst_addr)) {
- AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_ZIGBEE "Sniffing group 0x%04X"), group_addr);
+ AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_ZIGBEE "Sniffing group 0x%04X"), group_addr);
}
return -1; // ignore
}
@@ -229,7 +227,7 @@ void Z_Send_State_or_Map(uint16_t shortaddr, uint8_t index, uint16_t zdo_cmd) {
// This callback is registered to send ZbMap(s) to each device one at a time
void Z_Map(uint16_t shortaddr, uint16_t groupaddr, uint16_t cluster, uint8_t endpoint, uint32_t value) {
if (BAD_SHORTADDR != shortaddr) {
- AddLog_P(LOG_LEVEL_INFO, PSTR(D_LOG_ZIGBEE "sending `ZbMap 0x%04X`"), shortaddr);
+ AddLog(LOG_LEVEL_INFO, PSTR(D_LOG_ZIGBEE "sending `ZbMap 0x%04X`"), shortaddr);
#ifdef USE_ZIGBEE_ZNP
Z_Send_State_or_Map(shortaddr, value, ZDO_MGMT_LQI_REQ);
#endif // USE_ZIGBEE_ZNP
@@ -237,7 +235,7 @@ void Z_Map(uint16_t shortaddr, uint16_t groupaddr, uint16_t cluster, uint8_t end
Z_Send_State_or_Map(shortaddr, value, ZDO_Mgmt_Lqi_req);
#endif // USE_ZIGBEE_EZSP
} else {
- AddLog_P(LOG_LEVEL_INFO, PSTR(D_LOG_ZIGBEE "ZbMap done"));
+ AddLog(LOG_LEVEL_INFO, PSTR(D_LOG_ZIGBEE "ZbMap done"));
zigbee.mapping_in_progress = false;
zigbee.mapping_ready = true;
}
@@ -249,7 +247,7 @@ void Z_Map(uint16_t shortaddr, uint16_t groupaddr, uint16_t cluster, uint8_t end
//
// Handle a "getEui64" incoming message
//
-int32_t Z_EZSPGetEUI64(int32_t res, class SBuffer &buf) {
+int32_t Z_EZSPGetEUI64(int32_t res, SBuffer &buf) {
localIEEEAddr = buf.get64(2);
return res;
}
@@ -257,7 +255,7 @@ int32_t Z_EZSPGetEUI64(int32_t res, class SBuffer &buf) {
//
// Handle a "getEui64" incoming message
//
-int32_t Z_EZSPGetNodeId(int32_t res, class SBuffer &buf) {
+int32_t Z_EZSPGetNodeId(int32_t res, SBuffer &buf) {
localShortAddr = buf.get8(2);
return res;
}
@@ -265,7 +263,7 @@ int32_t Z_EZSPGetNodeId(int32_t res, class SBuffer &buf) {
//
// Handle a "getNetworkParameters" incoming message
//
-int32_t Z_EZSPNetworkParameters(int32_t res, class SBuffer &buf) {
+int32_t Z_EZSPNetworkParameters(int32_t res, SBuffer &buf) {
uint8_t node_type = buf.get8(3);
// ext panid: 4->11
// panid: 12->13
@@ -276,12 +274,10 @@ int32_t Z_EZSPNetworkParameters(int32_t res, class SBuffer &buf) {
// localIEEEAddr = long_adr;
// localShortAddr = short_adr;
- char hex[20];
- Uint64toHex(localIEEEAddr, hex, 64);
Response_P(PSTR("{\"" D_JSON_ZIGBEE_STATE "\":{"
- "\"Status\":%d,\"IEEEAddr\":\"0x%s\",\"ShortAddr\":\"0x%04X\""
+ "\"Status\":%d,\"IEEEAddr\":\"0x%_X\",\"ShortAddr\":\"0x%04X\""
",\"DeviceType\":%d}}"),
- ZIGBEE_STATUS_EZ_INFO, hex, localShortAddr, node_type);
+ ZIGBEE_STATUS_EZ_INFO, &localIEEEAddr, localShortAddr, node_type);
MqttPublishPrefixTopicRulesProcess_P(RESULT_OR_TELE, PSTR(D_JSON_ZIGBEE_STATE));
@@ -295,7 +291,7 @@ int32_t Z_EZSPNetworkParameters(int32_t res, class SBuffer &buf) {
//
// Handle a "Receive Device Info" incoming message
//
-int32_t ZNP_ReceiveDeviceInfo(int32_t res, class SBuffer &buf) {
+int32_t ZNP_ReceiveDeviceInfo(int32_t res, SBuffer &buf) {
// Ex= 6700.00.6263151D004B1200.0000.07.09.02.83869991
// IEEE Adr (8 bytes) = 0x00124B001D156362
// Short Addr (2 bytes) = 0x0000
@@ -313,13 +309,11 @@ int32_t ZNP_ReceiveDeviceInfo(int32_t res, class SBuffer &buf) {
localIEEEAddr = long_adr;
localShortAddr = short_adr;
- char hex[20];
- Uint64toHex(long_adr, hex, 64);
Response_P(PSTR("{\"" D_JSON_ZIGBEE_STATE "\":{"
- "\"Status\":%d,\"IEEEAddr\":\"0x%s\",\"ShortAddr\":\"0x%04X\""
+ "\"Status\":%d,\"IEEEAddr\":\"0x%_X\",\"ShortAddr\":\"0x%04X\""
",\"DeviceType\":%d,\"DeviceState\":%d"
",\"NumAssocDevices\":%d"),
- ZIGBEE_STATUS_CC_INFO, hex, short_adr, device_type, device_state,
+ ZIGBEE_STATUS_CC_INFO, &long_adr, short_adr, device_type, device_state,
device_associated);
if (device_associated > 0) { // If there are devices registered in CC2530, print the list
@@ -339,7 +333,7 @@ int32_t ZNP_ReceiveDeviceInfo(int32_t res, class SBuffer &buf) {
return res;
}
-int32_t ZNP_CheckNVWrite(int32_t res, class SBuffer &buf) {
+int32_t ZNP_CheckNVWrite(int32_t res, SBuffer &buf) {
// Check the status after NV Init "ZNP Has Configured"
// Good response should be 610700 or 610709 (Success or Created)
// We only filter the response on 6107 and check the code in this function
@@ -351,7 +345,7 @@ int32_t ZNP_CheckNVWrite(int32_t res, class SBuffer &buf) {
}
}
-int32_t ZNP_Reboot(int32_t res, class SBuffer &buf) {
+int32_t ZNP_Reboot(int32_t res, SBuffer &buf) {
// print information about the reboot of device
// 4180.02.02.00.02.06.03
//
@@ -378,15 +372,15 @@ int32_t ZNP_Reboot(int32_t res, class SBuffer &buf) {
MqttPublishPrefixTopicRulesProcess_P(RESULT_OR_TELE, PSTR(D_JSON_ZIGBEE_STATE));
- if ((0x02 == major_rel) && (0x06 == minor_rel)) {
- return 0; // version 2.6.x is ok
+ if ((0x02 == major_rel) && ((0x06 == minor_rel) || (0x07 == minor_rel))) {
+ return 0; // version 2.6.x and 2.7.x are ok
} else {
return ZIGBEE_LABEL_UNSUPPORTED_VERSION; // abort
}
}
#ifdef USE_ZIGBEE_ZNP
-int32_t ZNP_ReceiveCheckVersion(int32_t res, class SBuffer &buf) {
+int32_t ZNP_ReceiveCheckVersion(int32_t res, SBuffer &buf) {
// check that the version is supported
// typical version for ZNP 1.2
// 61020200-02.06.03.D9143401.0200000000
@@ -409,8 +403,8 @@ int32_t ZNP_ReceiveCheckVersion(int32_t res, class SBuffer &buf) {
MqttPublishPrefixTopicRulesProcess_P(RESULT_OR_TELE, PSTR(D_JSON_ZIGBEE_STATE));
- if ((0x02 == major_rel) && (0x06 == minor_rel)) {
- return 0; // version 2.6.x is ok
+ if ((0x02 == major_rel) && ((0x06 == minor_rel) || (0x07 == minor_rel))) {
+ return 0; // version 2.6.x and 2.7.x are ok
} else {
return ZIGBEE_LABEL_UNSUPPORTED_VERSION; // abort
}
@@ -418,19 +412,19 @@ int32_t ZNP_ReceiveCheckVersion(int32_t res, class SBuffer &buf) {
#endif // USE_ZIGBEE_ZNP
#ifdef USE_ZIGBEE_EZSP
-int32_t EZ_ReceiveCheckVersion(int32_t res, class SBuffer &buf) {
+int32_t EZ_ReceiveCheckVersion(int32_t res, SBuffer &buf) {
uint8_t protocol_version = buf.get8(2);
uint8_t stack_type = buf.get8(3);
- uint16_t stack_version = buf.get16(4);
+ zigbee.ezsp_version = buf.get16(4);
Response_P(PSTR("{\"" D_JSON_ZIGBEE_STATE "\":{"
"\"Status\":%d,\"Version\":\"%d.%d.%d.%d\",\"Protocol\":%d"
",\"Stack\":%d}}"),
ZIGBEE_STATUS_EZ_VERSION,
- (stack_version & 0xF000) >> 12,
- (stack_version & 0x0F00) >> 8,
- (stack_version & 0x00F0) >> 4,
- stack_version & 0x000F,
+ (zigbee.ezsp_version & 0xF000) >> 12,
+ (zigbee.ezsp_version & 0x0F00) >> 8,
+ (zigbee.ezsp_version & 0x00F0) >> 4,
+ zigbee.ezsp_version & 0x000F,
protocol_version,
stack_type
);
@@ -438,7 +432,7 @@ int32_t EZ_ReceiveCheckVersion(int32_t res, class SBuffer &buf) {
MqttPublishPrefixTopicRulesProcess_P(RESULT_OR_TELE, PSTR(D_JSON_ZIGBEE_STATE));
if (0x08 == protocol_version) {
- if ((stack_version & 0xFF00) == 0x6700) {
+ if ((zigbee.ezsp_version & 0xFF00) == 0x6700) {
// If v6.7 there is a bug so we need to change the response
ZBW(ZBR_SET_OK2, 0x00, 0x00 /*high*/, 0x00 /*ok*/)
}
@@ -469,7 +463,7 @@ int32_t EZ_GotoIfResetConfig(uint8_t value) {
// If coordinator continue
// If router goto ZIGBEE_LABEL_START_ROUTER
// If device goto ZIGBEE_LABEL_START_DEVICE
-int32_t Z_SwitchDeviceType(int32_t res, class SBuffer &buf) {
+int32_t Z_SwitchDeviceType(int32_t res, SBuffer &buf) {
switch (Settings.zb_pan_id) {
case 0xFFFF: return ZIGBEE_LABEL_INIT_ROUTER;
case 0xFFFE: return ZIGBEE_LABEL_INIT_DEVICE;
@@ -480,7 +474,7 @@ int32_t Z_SwitchDeviceType(int32_t res, class SBuffer &buf) {
//
// Helper function, checks if the incoming buffer matches the 2-bytes prefix, i.e. message type in PMEM
//
-bool Z_ReceiveMatchPrefix(const class SBuffer &buf, const uint8_t *match) {
+bool Z_ReceiveMatchPrefix(const SBuffer &buf, const uint8_t *match) {
if ( (pgm_read_byte(&match[0]) == buf.get8(0)) &&
(pgm_read_byte(&match[1]) == buf.get8(1)) ) {
return true;
@@ -492,7 +486,7 @@ bool Z_ReceiveMatchPrefix(const class SBuffer &buf, const uint8_t *match) {
//
// Handle Permit Join response
//
-int32_t ZNP_ReceivePermitJoinStatus(int32_t res, const class SBuffer &buf) {
+int32_t ZNP_ReceivePermitJoinStatus(int32_t res, const SBuffer &buf) {
// we received a PermitJoin status change
uint8_t duration = buf.get8(2);
uint8_t status_code;
@@ -501,12 +495,15 @@ int32_t ZNP_ReceivePermitJoinStatus(int32_t res, const class SBuffer &buf) {
if (0xFF == duration) {
status_code = ZIGBEE_STATUS_PERMITJOIN_OPEN_XX;
message = PSTR("Enable Pairing mode until next boot");
+ zigbee.permit_end_time = true; // In ZNP mode, declare permitjoin open
} else if (duration > 0) {
status_code = ZIGBEE_STATUS_PERMITJOIN_OPEN_60;
message = PSTR("Enable Pairing mode for %d seconds");
+ zigbee.permit_end_time = true; // In ZNP mode, declare permitjoin open
} else {
status_code = ZIGBEE_STATUS_PERMITJOIN_CLOSE;
message = PSTR("Disable Pairing mode");
+ zigbee.permit_end_time = false; // In ZNP mode, declare permitjoin closed
}
Response_P(PSTR("{\"" D_JSON_ZIGBEE_STATE "\":{"
"\"Status\":%d,\"Message\":\""),
@@ -521,7 +518,7 @@ int32_t ZNP_ReceivePermitJoinStatus(int32_t res, const class SBuffer &buf) {
//
// ZNP only
//
-int32_t ZNP_ReceiveNodeDesc(int32_t res, const class SBuffer &buf) {
+int32_t ZNP_ReceiveNodeDesc(int32_t res, const SBuffer &buf) {
// Received ZDO_NODE_DESC_RSP
// Z_ShortAddress srcAddr = buf.get16(2);
uint8_t status = buf.get8(4);
@@ -563,7 +560,7 @@ int32_t ZNP_ReceiveNodeDesc(int32_t res, const class SBuffer &buf) {
//
// Porcess Receive Active Endpoint
//
-int32_t Z_ReceiveActiveEp(int32_t res, const class SBuffer &buf) {
+int32_t Z_ReceiveActiveEp(int32_t res, const SBuffer &buf) {
// Received ZDO_ACTIVE_EP_RSP
#ifdef USE_ZIGBEE_ZNP
// Z_ShortAddress srcAddr = buf.get16(2);
@@ -675,7 +672,7 @@ void Z_AutoBindDefer(uint16_t shortaddr, uint8_t endpoint, const SBuffer &buf,
}
}
-int32_t Z_ReceiveSimpleDesc(int32_t res, const class SBuffer &buf) {
+int32_t Z_ReceiveSimpleDesc(int32_t res, const SBuffer &buf) {
#ifdef USE_ZIGBEE_ZNP
// Received ZDO_SIMPLE_DESC_RSP
// Z_ShortAddress srcAddr = buf.get16(2);
@@ -750,7 +747,7 @@ int32_t Z_ReceiveSimpleDesc(int32_t res, const class SBuffer &buf) {
// Handle IEEEAddr incoming message
//
// Same works for both ZNP and EZSP
-int32_t Z_ReceiveIEEEAddr(int32_t res, const class SBuffer &buf) {
+int32_t Z_ReceiveIEEEAddr(int32_t res, const SBuffer &buf) {
#ifdef USE_ZIGBEE_ZNP
uint8_t status = buf.get8(2);
Z_IEEEAddress ieeeAddr = buf.get64(3);
@@ -769,13 +766,11 @@ int32_t Z_ReceiveIEEEAddr(int32_t res, const class SBuffer &buf) {
if (0 == status) { // SUCCESS
zigbee_devices.updateDevice(nwkAddr, ieeeAddr);
zigbee_devices.deviceWasReached(nwkAddr);
- char hex[20];
- Uint64toHex(ieeeAddr, hex, 64);
// Ping response
const char * friendlyName = zigbee_devices.getFriendlyName(nwkAddr);
Response_P(PSTR("{\"" D_JSON_ZIGBEE_PING "\":{\"" D_JSON_ZIGBEE_DEVICE "\":\"0x%04X\""
- ",\"" D_JSON_ZIGBEE_IEEE "\":\"0x%s\""), nwkAddr, hex);
+ ",\"" D_JSON_ZIGBEE_IEEE "\":\"0x%_X\""), nwkAddr, &ieeeAddr);
if (friendlyName) {
ResponseAppend_P(PSTR(",\"" D_JSON_ZIGBEE_NAME "\":\"%s\""), friendlyName);
}
@@ -789,7 +784,7 @@ int32_t Z_ReceiveIEEEAddr(int32_t res, const class SBuffer &buf) {
// Report any AF_DATA_CONFIRM message
// Ex: {"ZbConfirm":{"Endpoint":1,"Status":0,"StatusMessage":"SUCCESS"}}
//
-int32_t ZNP_DataConfirm(int32_t res, const class SBuffer &buf) {
+int32_t ZNP_DataConfirm(int32_t res, const SBuffer &buf) {
uint8_t status = buf.get8(2);
uint8_t endpoint = buf.get8(3);
//uint8_t transId = buf.get8(4); // unused
@@ -820,7 +815,7 @@ int32_t ZNP_DataConfirm(int32_t res, const class SBuffer &buf) {
// 0x08: Starting as ZigBee Coordinator
// 0x09: Started as ZigBee Coordinator
// 0x0A: Device has lost information about its parent
-int32_t ZNP_ReceiveStateChange(int32_t res, const class SBuffer &buf) {
+int32_t ZNP_ReceiveStateChange(int32_t res, const SBuffer &buf) {
uint8_t state = buf.get8(2);
const char * msg = nullptr;
@@ -870,7 +865,7 @@ int32_t ZNP_ReceiveStateChange(int32_t res, const class SBuffer &buf) {
// This message is also received when a previously paired device is powered up
// Send back Active Ep Req message
//
-int32_t Z_ReceiveEndDeviceAnnonce(int32_t res, const class SBuffer &buf) {
+int32_t Z_ReceiveEndDeviceAnnonce(int32_t res, const SBuffer &buf) {
#ifdef USE_ZIGBEE_ZNP
// Z_ShortAddress srcAddr = buf.get16(2);
Z_ShortAddress nwkAddr = buf.get16(4);
@@ -884,16 +879,22 @@ int32_t Z_ReceiveEndDeviceAnnonce(int32_t res, const class SBuffer &buf) {
uint8_t capabilities = buf.get8(10);
#endif
+ // record if we already knew the ieeeAddr for this device
+ // this will influence the decision whether we do auto-binding or not
+ const Z_Device & device_before = zigbee_devices.findShortAddr(nwkAddr);
+ bool ieee_already_known = false;
+ if (device_before.valid() && (device_before.longaddr != 0) && (device_before.longaddr == ieeeAddr)) {
+ ieee_already_known = true;
+ }
+
zigbee_devices.updateDevice(nwkAddr, ieeeAddr);
// device is reachable
zigbee_devices.deviceWasReached(nwkAddr);
- char hex[20];
- Uint64toHex(ieeeAddr, hex, 64);
Response_P(PSTR("{\"" D_JSON_ZIGBEE_STATE "\":{"
- "\"Status\":%d,\"IEEEAddr\":\"0x%s\",\"ShortAddr\":\"0x%04X\""
+ "\"Status\":%d,\"IEEEAddr\":\"0x%_X\",\"ShortAddr\":\"0x%04X\""
",\"PowerSource\":%s,\"ReceiveWhenIdle\":%s,\"Security\":%s}}"),
- ZIGBEE_STATUS_DEVICE_ANNOUNCE, hex, nwkAddr,
+ ZIGBEE_STATUS_DEVICE_ANNOUNCE, &ieeeAddr, nwkAddr,
(capabilities & 0x04) ? PSTR("true") : PSTR("false"),
(capabilities & 0x08) ? PSTR("true") : PSTR("false"),
(capabilities & 0x40) ? PSTR("true") : PSTR("false")
@@ -903,7 +904,10 @@ int32_t Z_ReceiveEndDeviceAnnonce(int32_t res, const class SBuffer &buf) {
Z_Query_Bulb(nwkAddr, wait_ms);
MqttPublishPrefixTopicRulesProcess_P(RESULT_OR_TELE, PSTR(D_JSON_ZIGBEEZCL_RECEIVED));
- Z_SendActiveEpReq(nwkAddr);
+ // Continue the discovery process and auto-binding only if the device was unknown or if PermitJoin is ongoing
+ if (!ieee_already_known || zigbee.permit_end_time) {
+ Z_SendActiveEpReq(nwkAddr);
+ }
return -1;
}
@@ -911,7 +915,7 @@ int32_t Z_ReceiveEndDeviceAnnonce(int32_t res, const class SBuffer &buf) {
// Handle Receive TC Dev Ind incoming message
// 45CA
//
-int32_t ZNP_ReceiveTCDevInd(int32_t res, const class SBuffer &buf) {
+int32_t ZNP_ReceiveTCDevInd(int32_t res, const SBuffer &buf) {
Z_ShortAddress srcAddr = buf.get16(2);
Z_IEEEAddress ieeeAddr = buf.get64(4);
Z_ShortAddress parentNw = buf.get16(12);
@@ -920,12 +924,10 @@ int32_t ZNP_ReceiveTCDevInd(int32_t res, const class SBuffer &buf) {
// device is reachable
zigbee_devices.deviceWasReached(srcAddr);
- char hex[20];
- Uint64toHex(ieeeAddr, hex, 64);
Response_P(PSTR("{\"" D_JSON_ZIGBEE_STATE "\":{"
- "\"Status\":%d,\"IEEEAddr\":\"0x%s\",\"ShortAddr\":\"0x%04X\""
+ "\"Status\":%d,\"IEEEAddr\":\"0x%_X\",\"ShortAddr\":\"0x%04X\""
",\"ParentNetwork\":\"0x%04X\"}}"),
- ZIGBEE_STATUS_DEVICE_INDICATION, hex, srcAddr, parentNw
+ ZIGBEE_STATUS_DEVICE_INDICATION, &ieeeAddr, srcAddr, parentNw
);
MqttPublishPrefixTopicRulesProcess_P(RESULT_OR_TELE, PSTR(D_JSON_ZIGBEEZCL_RECEIVED));
@@ -935,7 +937,7 @@ int32_t ZNP_ReceiveTCDevInd(int32_t res, const class SBuffer &buf) {
//
// Handle Bind Rsp incoming message
//
-int32_t Z_BindRsp(int32_t res, const class SBuffer &buf) {
+int32_t Z_BindRsp(int32_t res, const SBuffer &buf) {
#ifdef USE_ZIGBEE_ZNP
Z_ShortAddress nwkAddr = buf.get16(2);
uint8_t status = buf.get8(4);
@@ -968,7 +970,7 @@ int32_t Z_BindRsp(int32_t res, const class SBuffer &buf) {
//
// Handle Unbind Rsp incoming message
//
-int32_t Z_UnbindRsp(int32_t res, const class SBuffer &buf) {
+int32_t Z_UnbindRsp(int32_t res, const SBuffer &buf) {
#ifdef USE_ZIGBEE_ZNP
Z_ShortAddress nwkAddr = buf.get16(2);
uint8_t status = buf.get8(4);
@@ -1001,7 +1003,7 @@ int32_t Z_UnbindRsp(int32_t res, const class SBuffer &buf) {
//
// Handle MgMt Bind Rsp incoming message
//
-int32_t Z_MgmtBindRsp(int32_t res, const class SBuffer &buf) {
+int32_t Z_MgmtBindRsp(int32_t res, const SBuffer &buf) {
return Z_Mgmt_Lqi_Bind_Rsp(res, buf, false);
}
@@ -1041,7 +1043,7 @@ const char * Z_DeviceType(uint32_t value) {
//
// If the response has a follow-up, send more requests automatically
//
-int32_t Z_Mgmt_Lqi_Bind_Rsp(int32_t res, const class SBuffer &buf, boolean lqi) {
+int32_t Z_Mgmt_Lqi_Bind_Rsp(int32_t res, const SBuffer &buf, boolean lqi) {
#ifdef USE_ZIGBEE_ZNP
uint16_t shortaddr = buf.get16(2);
uint8_t status = buf.get8(4);
@@ -1173,9 +1175,7 @@ int32_t Z_Mgmt_Lqi_Bind_Rsp(int32_t res, const class SBuffer &buf, boolean lqi)
if (Z_Addr_Group == addrmode) { // Group address mode
ResponseAppend_P(PSTR("\"ToGroup\":%d}"), group);
} else if (Z_Addr_IEEEAddress == addrmode) { // IEEE address mode
- char hex[20];
- Uint64toHex(dstaddr, hex, 64);
- ResponseAppend_P(PSTR("\"ToDevice\":\"0x%s\",\"ToEndpoint\":%d}"), hex, dstep);
+ ResponseAppend_P(PSTR("\"ToDevice\":\"0x%_X\",\"ToEndpoint\":%d}"), &dstaddr, dstep);
}
}
@@ -1202,7 +1202,7 @@ int32_t Z_Mgmt_Lqi_Bind_Rsp(int32_t res, const class SBuffer &buf, boolean lqi)
//
// Handle MgMt Bind Rsp incoming message
//
-int32_t Z_MgmtLqiRsp(int32_t res, const class SBuffer &buf) {
+int32_t Z_MgmtLqiRsp(int32_t res, const SBuffer &buf) {
return Z_Mgmt_Lqi_Bind_Rsp(res, buf, true);
}
@@ -1211,7 +1211,7 @@ int32_t Z_MgmtLqiRsp(int32_t res, const class SBuffer &buf) {
// Handle Parent Annonce Rsp incoming message
//
// rsp: true = ZDO_Parent_annce_rsp, false = ZDO_Parent_annce
-int32_t EZ_ParentAnnceRsp(int32_t res, const class SBuffer &buf, bool rsp) {
+int32_t EZ_ParentAnnceRsp(int32_t res, const SBuffer &buf, bool rsp) {
size_t prefix_len;
uint8_t status;
uint8_t num_children;
@@ -1254,9 +1254,7 @@ int32_t EZ_ParentAnnceRsp(int32_t res, const class SBuffer &buf, bool rsp) {
if (i > 0) {
ResponseAppend_P(PSTR(","));
}
- char hex[20];
- Uint64toHex(child_ieee, hex, 64);
- ResponseAppend_P(PSTR("\"0x%s\""), hex);
+ ResponseAppend_P(PSTR("\"0x%_X\""), &child_ieee);
}
ResponseAppend_P(PSTR("]}}"));
@@ -1379,7 +1377,7 @@ void Z_WriteCIEAddress(uint16_t shortaddr, uint16_t groupaddr, uint16_t cluster,
buf.add8(ZEUI64);
buf.add64(localIEEEAddr);
- AddLog_P(LOG_LEVEL_INFO, PSTR(D_LOG_ZIGBEE "Writing CIE address"));
+ AddLog(LOG_LEVEL_INFO, PSTR(D_LOG_ZIGBEE "Writing CIE address"));
ZigbeeZCLSend_Raw(ZigbeeZCLSendMessage({
shortaddr,
0x0000, /* group */
@@ -1403,7 +1401,7 @@ void Z_SendCIEZoneEnrollResponse(uint16_t shortaddr, uint16_t groupaddr, uint16_
uint8_t transacid = zigbee_devices.getNextSeqNumber(shortaddr);
uint8_t EnrollRSP[2] = { 0x00 /* Sucess */, Z_B0(value) /* ZoneID */ };
- AddLog_P(LOG_LEVEL_INFO, PSTR(D_LOG_ZIGBEE "Sending Enroll Zone %d"), Z_B0(value));
+ AddLog(LOG_LEVEL_INFO, PSTR(D_LOG_ZIGBEE "Sending Enroll Zone %d"), Z_B0(value));
ZigbeeZCLSend_Raw(ZigbeeZCLSendMessage({
shortaddr,
0x0000, /* group */
@@ -1425,7 +1423,7 @@ void Z_SendCIEZoneEnrollResponse(uint16_t shortaddr, uint16_t groupaddr, uint16_
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);
- AddLogZ_P(LOG_LEVEL_INFO, PSTR(D_LOG_ZIGBEE "auto-bind `ZbBind {\"Device\":\"0x%04X\",\"Endpoint\":%d,\"Cluster\":\"0x%04X\"}`"),
+ AddLog_P(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);
@@ -1535,7 +1533,7 @@ void Z_AutoConfigReportingForCluster(uint16_t shortaddr, uint16_t groupaddr, uin
// encode value
int32_t res = encodeSingleAttribute(buf, report_change, "", attr_type);
if (res < 0) {
- AddLog_P(LOG_LEVEL_ERROR, PSTR(D_LOG_ZIGBEE "internal error, unsupported attribute type"));
+ AddLog(LOG_LEVEL_ERROR, PSTR(D_LOG_ZIGBEE "internal error, unsupported attribute type"));
} else {
Z_attribute attr;
attr.setKeyName(PSTR("ReportableChange"), true); // true because in PMEM
@@ -1550,7 +1548,7 @@ void Z_AutoConfigReportingForCluster(uint16_t shortaddr, uint16_t groupaddr, uin
ResponseAppend_P(PSTR("}}"));
if (buf.len() > 0) {
- AddLogZ_P(LOG_LEVEL_INFO, PSTR(D_LOG_ZIGBEE "auto-bind `%s`"), TasmotaGlobal.mqtt_data);
+ AddLog_P(LOG_LEVEL_INFO, PSTR(D_LOG_ZIGBEE "auto-bind `%s`"), TasmotaGlobal.mqtt_data);
ZigbeeZCLSend_Raw(ZigbeeZCLSendMessage({
shortaddr,
0x0000, /* group */
@@ -1572,7 +1570,7 @@ void Z_AutoConfigReportingForCluster(uint16_t shortaddr, uint16_t groupaddr, uin
// 2400
//
#ifdef USE_ZIGBEE_EZSP
-int32_t EZ_ReceiveTCJoinHandler(int32_t res, const class SBuffer &buf) {
+int32_t EZ_ReceiveTCJoinHandler(int32_t res, const SBuffer &buf) {
uint16_t srcAddr = buf.get16(2);
uint64_t ieeeAddr = buf.get64(4);
uint8_t status = buf.get8(12);
@@ -1582,14 +1580,12 @@ int32_t EZ_ReceiveTCJoinHandler(int32_t res, const class SBuffer &buf) {
if (EMBER_DEVICE_LEFT != status) { // ignore message if the device is leaving
zigbee_devices.updateDevice(srcAddr, ieeeAddr);
- char hex[20];
- Uint64toHex(ieeeAddr, hex, 64);
Response_P(PSTR("{\"" D_JSON_ZIGBEE_STATE "\":{"
- "\"Status\":%d,\"IEEEAddr\":\"0x%s\",\"ShortAddr\":\"0x%04X\""
+ "\"Status\":%d,\"IEEEAddr\":\"0x%_X\",\"ShortAddr\":\"0x%04X\""
",\"ParentNetwork\":\"0x%04X\""
",\"JoinStatus\":%d,\"Decision\":%d"
"}}"),
- ZIGBEE_STATUS_DEVICE_INDICATION, hex, srcAddr, parentNw,
+ ZIGBEE_STATUS_DEVICE_INDICATION, &ieeeAddr, srcAddr, parentNw,
status, decision
);
@@ -1658,11 +1654,11 @@ void Z_IncomingMessage(class ZCLFrame &zcl_received) {
zcl_received.parseClusterSpecificCommand(attr_list);
}
- AddLogZ_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_ZIGBEE D_JSON_ZIGBEEZCL_RAW_RECEIVED ": {\"0x%04X\":{%s}}"), srcaddr, attr_list.toString().c_str());
+ AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_ZIGBEE D_JSON_ZIGBEEZCL_RAW_RECEIVED ": {\"0x%04X\":{%s}}"), srcaddr, attr_list.toString().c_str());
// discard the message if it was sent by us (broadcast or group loopback)
if (srcaddr == localShortAddr) {
- AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_ZIGBEE "loopback message, ignoring"));
+ AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_ZIGBEE "loopback message, ignoring"));
return; // abort the rest of message management
}
@@ -1747,7 +1743,7 @@ void EZ_SendZDO(uint16_t shortaddr, uint16_t cmd, const unsigned char *payload,
* Send specific EZSP messages
\*********************************************************************************************/
-int32_t EZ_IncomingMessage(int32_t res, const class SBuffer &buf) {
+int32_t EZ_IncomingMessage(int32_t res, const SBuffer &buf) {
uint8_t msgtype = buf.get8(2); // see EZSP_EmberIncomingMessageType
bool wasbroadcast = (msgtype >= EMBER_INCOMING_MULTICAST) && (msgtype <= EMBER_INCOMING_BROADCAST_LOOPBACK);
uint16_t profileid = buf.get16(3); // HA = 0x0104, ZDO = 0x0000
@@ -1841,7 +1837,7 @@ int32_t EZ_Reset_Device(uint8_t value) {
* Default resolver
\*********************************************************************************************/
-int32_t EZ_Recv_Default(int32_t res, const class SBuffer &buf) {
+int32_t EZ_Recv_Default(int32_t res, const SBuffer &buf) {
// Default message handler for new messages
if (zigbee.init_phase) {
// if still during initialization phase, ignore any unexpected message
@@ -1911,7 +1907,7 @@ int32_t ZNP_Reset_Device(uint8_t value) {
return 0; // continue
}
-int32_t ZNP_ReceiveAfIncomingMessage(int32_t res, const class SBuffer &buf) {
+int32_t ZNP_ReceiveAfIncomingMessage(int32_t res, const SBuffer &buf) {
uint16_t groupid = buf.get16(2);
uint16_t clusterid = buf.get16(4);
uint16_t srcaddr = buf.get16(6);
@@ -1972,7 +1968,7 @@ const Z_Dispatcher Z_DispatchTable[] PROGMEM = {
* Default resolver
\*********************************************************************************************/
-int32_t ZNP_Recv_Default(int32_t res, const class SBuffer &buf) {
+int32_t ZNP_Recv_Default(int32_t res, const SBuffer &buf) {
// Default message handler for new messages
if (zigbee.init_phase) {
// if still during initialization phase, ignore any unexpected message
@@ -2143,7 +2139,7 @@ void ZCLFrame::autoResponder(const uint16_t *attr_list_ids, size_t attr_len) {
// we have a non-empty output
// log first
- AddLogZ_P(LOG_LEVEL_INFO, PSTR("ZIG: Auto-responder: ZbSend {\"Device\":\"0x%04X\""
+ AddLog_P(LOG_LEVEL_INFO, PSTR("ZIG: Auto-responder: ZbSend {\"Device\":\"0x%04X\""
",\"Cluster\":\"0x%04X\""
",\"Endpoint\":%d"
",\"Response\":%s}"
diff --git a/tasmota/xdrv_23_zigbee_9_serial.ino b/tasmota/xdrv_23_zigbee_9_serial.ino
index 42f0683b4..84a560703 100644
--- a/tasmota/xdrv_23_zigbee_9_serial.ino
+++ b/tasmota/xdrv_23_zigbee_9_serial.ino
@@ -101,14 +101,14 @@ void ZigbeeInputLoop(void) {
// in this case the first bit (lsb) is missed and Tasmota receives 0xFF instead of 0xFE
// We forgive this mistake, and next bytes are automatically resynchronized
if (ZIGBEE_SOF_ALT == zigbee_in_byte) {
- AddLog_P(LOG_LEVEL_INFO, PSTR("ZbInput forgiven first byte %02X (only for statistics)"), zigbee_in_byte);
+ AddLog(LOG_LEVEL_INFO, PSTR("ZbInput forgiven first byte %02X (only for statistics)"), zigbee_in_byte);
zigbee_in_byte = ZIGBEE_SOF;
}
}
if ((0 == zigbee_buffer->len()) && (ZIGBEE_SOF != zigbee_in_byte)) {
// waiting for SOF (Start Of Frame) byte, discard anything else
- AddLog_P(LOG_LEVEL_INFO, PSTR("ZbInput discarding byte %02X"), zigbee_in_byte);
+ AddLog(LOG_LEVEL_INFO, PSTR("ZbInput discarding byte %02X"), zigbee_in_byte);
continue; // discard
}
@@ -134,29 +134,25 @@ void ZigbeeInputLoop(void) {
}
if (zigbee_buffer->len() && (millis() > (zigbee_polling_window + ZIGBEE_POLLING))) {
- char hex_char[(zigbee_buffer->len() * 2) + 2];
- ToHex_P((unsigned char*)zigbee_buffer->getBuffer(), zigbee_buffer->len(), hex_char, sizeof(hex_char));
-
// AddLog_P(LOG_LEVEL_DEBUG_MORE, PSTR(D_LOG_ZIGBEE "Bytes follow_read_metric = %0d"), ZigbeeSerial->getLoopReadMetric());
// buffer received, now check integrity
if (zigbee_buffer->len() != zigbee_frame_len) {
// Len is not correct, log and reject frame
- AddLog_P(LOG_LEVEL_INFO, PSTR(D_JSON_ZIGBEEZNPRECEIVED ": received frame of wrong size %s, len %d, expected %d"), hex_char, zigbee_buffer->len(), zigbee_frame_len);
+ AddLog_P(LOG_LEVEL_INFO, PSTR(D_JSON_ZIGBEEZNPRECEIVED ": received frame of wrong size %_B, len %d, expected %d"), &zigbee_buffer, zigbee_buffer->len(), zigbee_frame_len);
} else if (0x00 != fcs) {
// FCS is wrong, packet is corrupt, log and reject frame
- AddLog_P(LOG_LEVEL_INFO, PSTR(D_JSON_ZIGBEEZNPRECEIVED ": received bad FCS frame %s, %d"), hex_char, fcs);
+ AddLog_P(LOG_LEVEL_INFO, PSTR(D_JSON_ZIGBEEZNPRECEIVED ": received bad FCS frame %_B, %d"), &zigbee_buffer, fcs);
} else {
// frame is correct
//AddLog_P(LOG_LEVEL_DEBUG_MORE, PSTR(D_JSON_ZIGBEEZNPRECEIVED ": received correct frame %s"), hex_char);
SBuffer znp_buffer = zigbee_buffer->subBuffer(2, zigbee_frame_len - 3); // remove SOF, LEN and FCS
- ToHex_P((unsigned char*)znp_buffer.getBuffer(), znp_buffer.len(), hex_char, sizeof(hex_char));
- Response_P(PSTR("{\"" D_JSON_ZIGBEEZNPRECEIVED "\":\"%s\"}"), hex_char);
+ Response_P(PSTR("{\"" D_JSON_ZIGBEEZNPRECEIVED "\":\"%_B\"}"), &znp_buffer);
if (Settings.flag3.tuya_serial_mqtt_publish) {
MqttPublishPrefixTopicRulesProcess_P(TELE, PSTR(D_RSLT_SENSOR));
} else {
- AddLogZ_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_ZIGBEE "%s"), TasmotaGlobal.mqtt_data);
+ AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_ZIGBEE "%s"), TasmotaGlobal.mqtt_data);
}
// now process the message
ZigbeeProcessInput(znp_buffer);
@@ -226,9 +222,6 @@ void ZigbeeInputLoop(void) {
uint32_t frame_len = zigbee_buffer->len();
if (frame_complete || (frame_len && (millis() > (zigbee_polling_window + ZIGBEE_POLLING)))) {
- char hex_char[frame_len * 2 + 2];
- ToHex_P((unsigned char*)zigbee_buffer->getBuffer(), zigbee_buffer->len(), hex_char, sizeof(hex_char));
-
// AddLog_P(LOG_LEVEL_DEBUG_MORE, PSTR(D_LOG_ZIGBEE "Bytes follow_read_metric = %0d"), ZigbeeSerial->getLoopReadMetric());
if ((frame_complete) && (frame_len >= 3)) {
// frame received and has at least 3 bytes (without EOF), checking CRC
@@ -250,7 +243,7 @@ void ZigbeeInputLoop(void) {
// remove 2 last bytes
if (crc_received != crc) {
- AddLog_P(LOG_LEVEL_INFO, PSTR(D_JSON_ZIGBEE_EZSP_RECEIVED ": bad crc (received 0x%04X, computed 0x%04X) %s"), crc_received, crc, hex_char);
+ AddLog_P(LOG_LEVEL_INFO, PSTR(D_JSON_ZIGBEE_EZSP_RECEIVED ": bad crc (received 0x%04X, computed 0x%04X) %_B"), crc_received, crc, &zigbee_buffer);
} else {
// copy buffer
SBuffer ezsp_buffer = zigbee_buffer->subBuffer(0, frame_len - 2); // CRC
@@ -266,14 +259,13 @@ void ZigbeeInputLoop(void) {
}
}
- ToHex_P((unsigned char*)ezsp_buffer.getBuffer(), ezsp_buffer.len(), hex_char, sizeof(hex_char));
- AddLogZ_P(LOG_LEVEL_DEBUG_MORE, PSTR(D_LOG_ZIGBEE "{\"" D_JSON_ZIGBEE_EZSP_RECEIVED "2\":\"%s\"}"), hex_char);
+ AddLog_P(LOG_LEVEL_DEBUG_MORE, PSTR(D_LOG_ZIGBEE "{\"" D_JSON_ZIGBEE_EZSP_RECEIVED "2\":\"%_B\"}"), &ezsp_buffer);
// now process the message
ZigbeeProcessInputRaw(ezsp_buffer);
}
} else {
// the buffer timed-out, print error and discard
- AddLog_P(LOG_LEVEL_INFO, PSTR(D_JSON_ZIGBEE_EZSP_RECEIVED ": time-out, discarding %s, %d"), hex_char);
+ AddLog_P(LOG_LEVEL_INFO, PSTR(D_JSON_ZIGBEE_EZSP_RECEIVED ": time-out, discarding %s, %_B"), &zigbee_buffer);
}
zigbee_buffer->setLen(0); // empty buffer
escape = false;
@@ -356,9 +348,7 @@ void ZigbeeZNPSend(const uint8_t *msg, size_t len) {
//AddLog_P(LOG_LEVEL_DEBUG_MORE, PSTR("ZNPSend FCS %02X"), fcs);
}
// Now send a MQTT message to report the sent message
- char hex_char[(len * 2) + 2];
- AddLogZ_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_ZIGBEE D_JSON_ZIGBEEZNPSENT " %s"),
- ToHex_P(msg, len, hex_char, sizeof(hex_char)));
+ AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_ZIGBEE D_JSON_ZIGBEEZNPSENT " %*_H"), len, msg);
}
//
@@ -490,17 +480,13 @@ void ZigbeeEZSPSendRaw(const uint8_t *msg, size_t len, bool send_cancel) {
}
// Now send a MQTT message to report the sent message
- char hex_char[(len * 2) + 2];
- AddLogZ_P(LOG_LEVEL_DEBUG_MORE, PSTR(D_LOG_ZIGBEE D_JSON_ZIGBEE_EZSP_SENT_RAW " %s"),
- ToHex_P(msg, len, hex_char, sizeof(hex_char)));
+ AddLog_P(LOG_LEVEL_DEBUG_MORE, PSTR(D_LOG_ZIGBEE D_JSON_ZIGBEE_EZSP_SENT_RAW " %*_H"), len, msg);
}
// Send an EZSP command and data
// Ex: Version with min v8 = 000008
void ZigbeeEZSPSendCmd(const uint8_t *msg, size_t len) {
- char hex_char[len*2 + 2];
- ToHex_P(msg, len, hex_char, sizeof(hex_char));
- AddLogZ_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_ZIGBEE "ZbEZSPSend %s"), hex_char);
+ AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_ZIGBEE "ZbEZSPSend %*_H"), len, msg);
SBuffer cmd(len+3); // prefix with seq number (1 byte) and frame control bytes (2 bytes)
@@ -552,7 +538,7 @@ void ZigbeeEZSPSendDATA(const uint8_t *msg, size_t len) {
}
// Receive a high-level EZSP command/response, starting with 16-bits frame ID
-void ZigbeeProcessInputEZSP(class SBuffer &buf) {
+void ZigbeeProcessInputEZSP(SBuffer &buf) {
// verify errors in first 2 bytes.
// TODO
// uint8_t sequence_num = buf.get8(0);
@@ -571,11 +557,8 @@ void ZigbeeProcessInputEZSP(class SBuffer &buf) {
}
buf.setLen(buf.len() - 3);
- char hex_char[buf.len()*2 + 2];
-
// log message
- ToHex_P((unsigned char*)buf.getBuffer(), buf.len(), hex_char, sizeof(hex_char));
- Response_P(PSTR("{\"" D_JSON_ZIGBEE_EZSP_RECEIVED "\":\"%s\"}"), hex_char);
+ Response_P(PSTR("{\"" D_JSON_ZIGBEE_EZSP_RECEIVED "\":\"%_B\"}"), &buf);
if (Settings.flag3.tuya_serial_mqtt_publish) {
MqttPublishPrefixTopicRulesProcess_P(TELE, PSTR(D_RSLT_SENSOR));
} else {
@@ -607,7 +590,7 @@ void ZigbeeProcessInputEZSP(class SBuffer &buf) {
log_level = LOG_LEVEL_DEBUG;
break;
}
- AddLogZ_P(log_level, PSTR(D_LOG_ZIGBEE "%s"), TasmotaGlobal.mqtt_data); // TODO move to LOG_LEVEL_DEBUG when stable
+ AddLog_P(log_level, PSTR(D_LOG_ZIGBEE "%s"), TasmotaGlobal.mqtt_data); // TODO move to LOG_LEVEL_DEBUG when stable
}
// Pass message to state machine
@@ -632,7 +615,7 @@ void EZSP_HandleAck(uint8_t new_ack) {
}
// Receive raw ASH frame (CRC was removed, data unstuffed) but still contains frame numbers
-void ZigbeeProcessInputRaw(class SBuffer &buf) {
+void ZigbeeProcessInputRaw(SBuffer &buf) {
uint8_t control_byte = buf.get8(0);
uint8_t ack_num = control_byte & 0x07; // keep 3 LSB
if (control_byte & 0x80) { // non DATA frame
diff --git a/tasmota/xdrv_23_zigbee_9a_upload.ino b/tasmota/xdrv_23_zigbee_9a_upload.ino
index b946848a3..0bc60223d 100644
--- a/tasmota/xdrv_23_zigbee_9a_upload.ino
+++ b/tasmota/xdrv_23_zigbee_9a_upload.ino
@@ -81,6 +81,13 @@ char ZigbeeUploadFlashRead(void) {
ZbUpload.byte_counter++;
if (ZbUpload.byte_counter > ZbUpload.ota_size) {
+
+// static bool padding = true;
+// if (padding) {
+// AddLog(LOG_LEVEL_DEBUG, PSTR("XMD: Start padding from %d"), ZbUpload.byte_counter);
+// padding = false;
+// }
+
// When the source device reaches the last XModem data block, it should be padded to 128 bytes
// of data using SUB (ASCII 0x1A) characters.
data = XM_SUB;
@@ -107,8 +114,8 @@ struct XMODEM {
uint32_t delay = 0;
uint32_t flush_delay = 0xFFFFFFFF;
uint32_t filepos = 0;
+ uint32_t packet_no = 1;
int crcBuf = 0;
- uint8_t packetNo = 1;
uint8_t checksumBuf = 0;
bool oldChecksum;
} XModem;
@@ -142,6 +149,11 @@ char XModemWaitACK(void)
if (i > 200) { return -1; }
}
in_char = ZigbeeSerial->read();
+
+// if (in_char != XM_ACK) {
+// AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("XMD: Rcvd3 0x%02X"), in_char);
+// }
+
if (XM_CAN == in_char) { return XM_CAN; }
} while ((in_char != XM_NAK) && (in_char != XM_ACK) && (in_char != 'C'));
return in_char;
@@ -162,10 +174,12 @@ bool XModemSendPacket(uint32_t packet_no) {
XModem.checksumBuf = 0x00;
XModem.crcBuf = 0x00;
+ uint8_t packet_num = packet_no;
+
// Try to send packet, so header first
ZigbeeSerial->write(XM_SOH);
- ZigbeeSerial->write(packet_no);
- ZigbeeSerial->write(~packet_no);
+ ZigbeeSerial->write(packet_num);
+ ZigbeeSerial->write(~packet_num);
for (uint32_t i = 0; i < XMODEM_PACKET_SIZE; i++) {
in_char = ZigbeeUploadFlashRead();
XModemOutputByte(in_char);
@@ -220,6 +234,13 @@ bool ZigbeeUploadBootloaderPrompt(void) {
yield();
char bootloader_byte = ZigbeeSerial->read();
+ // [cr][lf]
+ // Gecko Bootloader v1.A.3 or Gecko Bootloader v1.9.1.04[cr][lf]
+ // 1. upload gbl[cr][lf]
+ // 2. run[cr][lf]
+ // 3. ebl info[cr][lf]
+ // BL >
+
if (((uint8_t)bootloader_byte >=0) && (buf_len < sizeof(serial_buffer) -2)) {
serial_buffer[buf_len++] = bootloader_byte;
}
@@ -243,9 +264,7 @@ bool ZigbeeUploadBootloaderPrompt(void) {
}
if (buf_len) {
- char hex_char[256];
- ToHex_P(serial_buffer, buf_len, hex_char, 256);
- AddLog_P(LOG_LEVEL_DEBUG_MORE, PSTR("XMD: Rcvd %s"), hex_char);
+ AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("XMD: Rcvd %*_H"), buf_len, serial_buffer);
}
return ((4 == ZbUpload.byte_counter) && (millis() > XModem.flush_delay));
@@ -258,7 +277,7 @@ bool ZigbeeUploadXmodem(void) {
}
#ifdef ZIGBEE_BOOTLOADER_SOFTWARE_RESET_FIRST
case ZBU_INIT: { // *** Init ESF32 bootloader
- AddLog_P(LOG_LEVEL_DEBUG, PSTR("XMD: Init bootloader"));
+ AddLog(LOG_LEVEL_DEBUG, PSTR("XMD: Init bootloader"));
ZbUpload.ota_step = ZBU_SOFTWARE_RESET;
return false; // Keep Zigbee serial active
}
@@ -273,7 +292,7 @@ bool ZigbeeUploadXmodem(void) {
}
case ZBU_SOFTWARE_SEND: {
if (millis() > XModem.timeout) {
- AddLog_P(LOG_LEVEL_DEBUG, PSTR("XMD: Bootloader software reset send timeout"));
+ AddLog(LOG_LEVEL_DEBUG, PSTR("XMD: Bootloader software reset send timeout"));
ZbUpload.ota_step = ZBU_HARDWARE_RESET;
return true;
}
@@ -298,17 +317,17 @@ bool ZigbeeUploadXmodem(void) {
case ZBU_PROMPT: { // *** Wait for prompt and select option upload ebl
if (millis() > XModem.timeout) {
if (ZBU_SOFTWARE_RESET == ZbUpload.bootloader) {
- AddLog_P(LOG_LEVEL_DEBUG, PSTR("XMD: Bootloader software reset timeout"));
+ AddLog(LOG_LEVEL_DEBUG, PSTR("XMD: Bootloader software reset timeout"));
ZbUpload.ota_step = ZBU_HARDWARE_RESET;
} else {
- AddLog_P(LOG_LEVEL_DEBUG, PSTR("XMD: Bootloader hardware reset timeout"));
+ AddLog(LOG_LEVEL_DEBUG, PSTR("XMD: Bootloader hardware reset timeout"));
ZbUpload.ota_step = ZBU_ERROR;
}
return true;
}
#else // No ZIGBEE_BOOTLOADER_SOFTWARE_RESET_FIRST
case ZBU_INIT: { // *** Init ESF32 bootloader
- AddLog_P(LOG_LEVEL_DEBUG, PSTR("XMD: Init bootloader"));
+ AddLog(LOG_LEVEL_DEBUG, PSTR("XMD: Init bootloader"));
ZigbeeUploadSetBootloader(0); // Reboot MCU EFR32 which returns below text
XModem.timeout = millis() + (30 * 1000); // Allow 30 seconds to receive EBL prompt
XModem.delay = millis() + (2 * XMODEM_FLUSH_DELAY);
@@ -318,7 +337,7 @@ bool ZigbeeUploadXmodem(void) {
}
case ZBU_PROMPT: { // *** Wait for prompt and select option upload ebl
if (millis() > XModem.timeout) {
- AddLog_P(LOG_LEVEL_DEBUG, PSTR("XMD: Bootloader timeout"));
+ AddLog(LOG_LEVEL_DEBUG, PSTR("XMD: Bootloader timeout"));
ZbUpload.ota_step = ZBU_ERROR;
return true;
}
@@ -332,17 +351,18 @@ bool ZigbeeUploadXmodem(void) {
}
} else {
// After the bootloader receives a carriage return from the target device, it displays a menu
- // Gecko Bootloader v1.A.3
- // 1. upload gbl
- // 2. run
- // 3. ebl info
+ // [cr][lf]
+ // Gecko Bootloader v1.A.3 or Gecko Bootloader v1.9.1.04[cr][lf]
+ // 1. upload gbl[cr][lf]
+ // 2. run[cr][lf]
+ // 3. ebl info[cr][lf]
// BL >
if (ZigbeeUploadBootloaderPrompt()) {
- AddLog_P(LOG_LEVEL_DEBUG, PSTR("XMD: Init sync"));
+ AddLog(LOG_LEVEL_DEBUG, PSTR("XMD: Init sync"));
ZigbeeSerial->flush();
ZigbeeSerial->write('1'); // upload ebl
if (TasmotaGlobal.sleep > 0) {
- TasmotaGlobal.sleep = 1; // Speed up loop used for xmodem upload
+ TasmotaGlobal.sleep = 1; // Speed up loop used for xmodem upload
}
XModem.timeout = millis() + (XMODEM_SYNC_TIMEOUT * 1000);
ZbUpload.ota_step = ZBU_SYNC;
@@ -352,32 +372,41 @@ bool ZigbeeUploadXmodem(void) {
}
case ZBU_SYNC: { // *** Handle file upload using XModem - sync
if (millis() > XModem.timeout) {
- AddLog_P(LOG_LEVEL_DEBUG, PSTR("XMD: SYNC timeout"));
+ AddLog(LOG_LEVEL_DEBUG, PSTR("XMD: SYNC timeout"));
ZbUpload.ota_step = ZBU_ERROR;
return true;
}
// Wait for either C or NACK as a sync packet. Determines protocol details, checksum algorithm.
if (ZigbeeSerial->available()) {
+ // [cr][lf]
+ // begin upload[cr][lf]
+ // C
char xmodem_sync = ZigbeeSerial->read();
+
+// AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("XMD: Rcvd2 0x%02X"), xmodem_sync);
+
if (('C' == xmodem_sync) || (XM_NAK == xmodem_sync)) {
// Determine which checksum algorithm to use
XModem.oldChecksum = (xmodem_sync == XM_NAK);
- XModem.packetNo = 1;
+ XModem.packet_no = 1;
ZbUpload.byte_counter = 0;
ZbUpload.ota_step = ZBU_UPLOAD;
- AddLog_P(LOG_LEVEL_DEBUG, PSTR("XMD: Init packet send"));
+ AddLog(LOG_LEVEL_DEBUG, PSTR("XMD: Init packet send"));
}
}
break;
}
case ZBU_UPLOAD: { // *** Handle file upload using XModem - upload
if (ZigbeeUploadAvailable()) {
- if (!XModemSendPacket(XModem.packetNo)) {
- AddLog_P(LOG_LEVEL_DEBUG, PSTR("XMD: Packet send failed"));
+ if (ZbUpload.byte_counter && !(ZbUpload.byte_counter % 10240)) { // Show progress every 10kB
+ AddLog(LOG_LEVEL_DEBUG, PSTR("XMD: Progress %d kB"), ZbUpload.byte_counter / 1024);
+ }
+ if (!XModemSendPacket(XModem.packet_no)) {
+ AddLog(LOG_LEVEL_DEBUG, PSTR("XMD: Packet %d send failed"), XModem.packet_no);
ZbUpload.ota_step = ZBU_ERROR;
return true;
}
- XModem.packetNo++;
+ XModem.packet_no++;
} else {
// Once the last block is ACKed by the target, the transfer should be finalized by an
// EOT (ASCII 0x04) packet from the source. Once this packet is confirmed via XModem ACK
@@ -385,6 +414,7 @@ bool ZigbeeUploadXmodem(void) {
ZigbeeSerial->write(XM_EOT);
XModem.timeout = millis() + (30 * 1000); // Allow 30 seconds to receive EOT ACK
ZbUpload.ota_step = ZBU_EOT;
+ AddLog(LOG_LEVEL_DEBUG, PSTR("XMD: Transferred %d bytes"), ZbUpload.ota_size);
}
break;
}
@@ -395,14 +425,19 @@ bool ZigbeeUploadXmodem(void) {
// its XModem state machine waits a sufficient amount of time to allow this checksum process
// to occur without timing out on the response just before the EOT is sent.
if (millis() > XModem.timeout) {
- AddLog_P(LOG_LEVEL_DEBUG, PSTR("XMD: EOT ACK timeout"));
+ AddLog(LOG_LEVEL_DEBUG, PSTR("XMD: EOT ACK timeout"));
ZbUpload.ota_step = ZBU_ERROR;
return true;
}
if (ZigbeeSerial->available()) {
char xmodem_ack = XModemWaitACK();
- if (XM_ACK == xmodem_ack) {
- AddLog_P(LOG_LEVEL_DEBUG, PSTR("XMD: " D_SUCCESSFUL));
+ if (XM_CAN == xmodem_ack) {
+ AddLog(LOG_LEVEL_DEBUG, PSTR("XMD: Transfer invalid"));
+ ZbUpload.ota_step = ZBU_ERROR;
+ return true;
+ }
+ else if (XM_ACK == xmodem_ack) {
+ AddLog(LOG_LEVEL_DEBUG, PSTR("XMD: " D_SUCCESSFUL));
XModem.timeout = millis() + (30 * 1000); // Allow 30 seconds to receive EBL prompt
ZbUpload.byte_counter = 0;
ZbUpload.ota_step = ZBU_COMPLETE;
@@ -412,34 +447,39 @@ bool ZigbeeUploadXmodem(void) {
}
case ZBU_COMPLETE: { // *** Wait for Serial upload complete EBL prompt
if (millis() > XModem.timeout) {
- AddLog_P(LOG_LEVEL_DEBUG, PSTR("XMD: Bootloader timeout"));
+ AddLog(LOG_LEVEL_DEBUG, PSTR("XMD: Bootloader timeout"));
ZbUpload.ota_step = ZBU_ERROR;
return true;
} else {
// After an image successfully uploads, the XModem transaction completes and the bootloader displays
// ‘Serial upload complete’ before redisplaying the menu
- // Serial upload complete
- // Gecko Bootloader v1.A.3
- // 1. upload gbl
- // 2. run
- // 3. ebl info
+ //
+ // [cr][lf]
+ // Serial upload complete[cr][lf]
+ // [cr][lf]
+ // Gecko Bootloader v1.A.3 or Gecko Bootloader v1.9.1.04[cr][lf]
+ // 1. upload gbl[cr][lf]
+ // 2. run[cr][lf]
+ // 3. ebl info[cr][lf]
// BL >
if (ZigbeeUploadBootloaderPrompt()) {
ZbUpload.state = ZBU_COMPLETE;
ZbUpload.ota_step = ZBU_DONE;
+ AddLog(LOG_LEVEL_DEBUG, PSTR("XMD: " D_RESTARTING));
}
}
break;
}
case ZBU_ERROR:
ZbUpload.state = ZBU_ERROR;
+ AddLog(LOG_LEVEL_DEBUG, PSTR("XMD: " D_FAILED));
case ZBU_DONE: { // *** Clean up and restart to disable bootloader and use new firmware
- AddLog_P(LOG_LEVEL_DEBUG, PSTR("XMD: " D_RESTARTING));
ZigbeeUploadSetBootloader(1); // Disable bootloader and reset MCU - should happen at restart
if (1 == TasmotaGlobal.sleep) {
TasmotaGlobal.sleep = Settings.sleep; // Restore loop sleep
}
// TasmotaGlobal.restart_flag = 2; // Restart to disable bootloader and use new firmware
+ if (ZbUpload.buffer) { free(ZbUpload.buffer); }
ZbUpload.ota_step = ZBU_FINISH; // Never return to zero without a restart to get a sane Zigbee environment
break;
}
@@ -472,10 +512,6 @@ void ZigbeeUploadStep1Done(uint32_t data, size_t size) {
ZbUpload.state = ZBU_UPLOAD; // Signal upload done and ready for delayed upload to MCU EFR32
}
-bool ZigbeeUploadFinish(void) {
- return (ZBU_FINISH == ZbUpload.ota_step);
-}
-
#define WEB_HANDLE_ZIGBEE_XFER "zx"
const char HTTP_SCRIPT_XFER_STATE[] PROGMEM =
@@ -500,16 +536,16 @@ void HandleZigbeeXfer(void) {
if (!HttpCheckPriviledgedAccess()) { return; }
if (Webserver->hasArg("z")) { // Status refresh requested
- WSContentBegin(200, CT_PLAIN);
- WSContentSend_P(PSTR("%d"), ZbUpload.state);
- WSContentEnd();
if (ZBU_ERROR == ZbUpload.state) {
Web.upload_error = 7; // Upload aborted (xmodem transfer failed)
}
+ WSContentBegin(200, CT_PLAIN);
+ WSContentSend_P(PSTR("%d"), ZbUpload.state);
+ WSContentEnd();
return;
}
- AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_HTTP D_UPLOAD_TRANSFER));
+ AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_HTTP D_UPLOAD_TRANSFER));
WSContentStart_P(PSTR(D_INFORMATION));
WSContentSend_P(HTTP_SCRIPT_XFER_STATE);
diff --git a/tasmota/xdrv_23_zigbee_A_impl.ino b/tasmota/xdrv_23_zigbee_A_impl.ino
index ec9a454ac..3c7e2f49c 100644
--- a/tasmota/xdrv_23_zigbee_A_impl.ino
+++ b/tasmota/xdrv_23_zigbee_A_impl.ino
@@ -24,6 +24,9 @@
#include "UnishoxStrings.h"
const char kZbCommands[] PROGMEM = D_PRFX_ZB "|" // prefix
+ // SetOption synonyms
+ D_SO_ZIGBEE_NAMEKEY "|" D_SO_ZIGBEE_DEVICETOPIC "|" D_SO_ZIGBEE_NOPREFIX "|" D_SO_ZIGBEE_ENDPOINTSUFFIX "|" D_SO_ZIGBEE_NOAUTOBIND "|"
+ D_SO_ZIGBEE_NAMETOPIC "|" D_SO_ZIGBEE_ENDPOINTTOPIC "|" D_SO_ZIGBEE_NOAUTOQUERY "|" D_SO_ZIGBEE_ZBRECEIVEDTOPIC "|" D_SO_ZIGBEE_OMITDEVICE "|"
#ifdef USE_ZIGBEE_ZNP
D_CMND_ZIGBEEZNPSEND "|" D_CMND_ZIGBEEZNPRECEIVE "|"
#endif // USE_ZIGBEE_ZNP
@@ -39,6 +42,11 @@ const char kZbCommands[] PROGMEM = D_PRFX_ZB "|" // prefix
D_CMND_ZIGBEE_CONFIG "|" D_CMND_ZIGBEE_DATA
;
+SO_SYNONYMS(kZbSynonyms,
+ 83, 89, 100, 101, 110,
+ 112, 120, 116, 118, 119,
+);
+
void (* const ZigbeeCommand[])(void) PROGMEM = {
#ifdef USE_ZIGBEE_ZNP
&CmndZbZNPSend, &CmndZbZNPReceive,
@@ -67,7 +75,7 @@ void ZigbeeInit(void)
// Check if settings in Flash are set
if (PinUsed(GPIO_ZIGBEE_RX) && PinUsed(GPIO_ZIGBEE_TX)) {
if (0 == Settings.zb_channel) {
- AddLog_P(LOG_LEVEL_INFO, PSTR(D_LOG_ZIGBEE D_ZIGBEE_RANDOMIZING_ZBCONFIG));
+ AddLog(LOG_LEVEL_INFO, PSTR(D_LOG_ZIGBEE D_ZIGBEE_RANDOMIZING_ZBCONFIG));
uint64_t mac64 = 0; // stuff mac address into 64 bits
WiFi.macAddress((uint8_t*) &mac64);
uint32_t esp_id = ESP_getChipId();
@@ -103,7 +111,7 @@ void ZigbeeInit(void)
Wire.beginTransmission(USE_ZIGBEE_ZBBRIDGE_EEPROM);
uint8_t error = Wire.endTransmission();
if (0 == error) {
- AddLog_P(LOG_LEVEL_INFO, PSTR(D_LOG_ZIGBEE D_ZIGBEE_EEPROM_FOUND_AT_ADDRESS " 0x%02X"), USE_ZIGBEE_ZBBRIDGE_EEPROM);
+ AddLog(LOG_LEVEL_INFO, PSTR(D_LOG_ZIGBEE D_ZIGBEE_EEPROM_FOUND_AT_ADDRESS " 0x%02X"), USE_ZIGBEE_ZBBRIDGE_EEPROM);
zigbee.eeprom_present = true;
}
#endif
@@ -183,23 +191,7 @@ void zigbeeZCLSendStr(uint16_t shortaddr, uint16_t groupaddr, uint8_t endpoint,
}
}
- if ((0 == endpoint) && (BAD_SHORTADDR != shortaddr)) {
- // endpoint is not specified, let's try to find it from shortAddr, unless it's a group address
- endpoint = zigbee_devices.findFirstEndpoint(shortaddr);
- //AddLog_P(LOG_LEVEL_DEBUG, PSTR("ZbSend: guessing endpoint 0x%02X"), endpoint);
- }
- AddLogZ_P(LOG_LEVEL_DEBUG, PSTR("ZbSend: shortaddr 0x%04X, groupaddr 0x%04X, cluster 0x%04X, endpoint 0x%02X, cmd 0x%02X, data %s"),
- shortaddr, groupaddr, cluster, endpoint, cmd, param);
-
- if ((0 == endpoint) && (BAD_SHORTADDR != shortaddr)) { // endpoint null is ok for group address
- AddLog_P(LOG_LEVEL_INFO, PSTR("ZbSend: unspecified endpoint"));
- return;
- }
-
- // everything is good, we can send the command
-
- uint8_t seq = zigbee_devices.getNextSeqNumber(shortaddr);
- ZigbeeZCLSend_Raw(ZigbeeZCLSendMessage({
+ zigbeeZCLSendCmd(ZigbeeZCLSendMessage({
shortaddr,
groupaddr,
cluster /*cluster*/,
@@ -209,14 +201,37 @@ void zigbeeZCLSendStr(uint16_t shortaddr, uint16_t groupaddr, uint8_t endpoint,
clusterSpecific /* not cluster specific */,
true /* response */,
false /* discover route */,
- seq, /* zcl transaction id */
+ 0, /* zcl transaction id */
buf.getBuffer(), buf.len()
}));
+}
+
+void zigbeeZCLSendCmd(const class ZigbeeZCLSendMessage &msg_const) {
+ ZigbeeZCLSendMessage msg = msg_const; // copy to a modifiable variable
+
+ if ((0 == msg.endpoint) && (BAD_SHORTADDR != msg.shortaddr)) {
+ // endpoint is not specified, let's try to find it from shortAddr, unless it's a group address
+ msg.endpoint = zigbee_devices.findFirstEndpoint(msg.shortaddr);
+ //AddLog_P(LOG_LEVEL_DEBUG, PSTR("ZbSend: guessing endpoint 0x%02X"), endpoint);
+ }
+
+ AddLog_P(LOG_LEVEL_DEBUG, PSTR("ZbSend: shortaddr 0x%04X, groupaddr 0x%04X, cluster 0x%04X, endpoint 0x%02X, cmd 0x%02X, data %*_H"),
+ msg.shortaddr, msg.groupaddr, msg.cluster, msg.endpoint, msg.cmd, msg.len, msg.msg);
+
+ if ((0 == msg.endpoint) && (BAD_SHORTADDR != msg.shortaddr)) { // endpoint null is ok for group address
+ AddLog_P(LOG_LEVEL_INFO, PSTR("ZbSend: unspecified endpoint"));
+ return;
+ }
+
+ // everything is good, we can send the command
+
+ msg.transacId = zigbee_devices.getNextSeqNumber(msg.shortaddr);
+ ZigbeeZCLSend_Raw(msg);
// now set the timer, if any, to read back the state later
- if (clusterSpecific) {
+ if (msg.clusterSpecific) {
if (!Settings.flag5.zb_disable_autoquery) {
// read back attribute value unless it is disabled
- sendHueUpdate(shortaddr, groupaddr, cluster, endpoint);
+ sendHueUpdate(msg.shortaddr, msg.groupaddr, msg.cluster, msg.endpoint);
}
}
}
@@ -463,7 +478,7 @@ void ZbSendSend(class JsonParserToken val_cmd, uint16_t device, uint16_t groupad
const char *cmd_s = ""; // pointer to payload string
bool clusterSpecific = true;
- static char delim[] = ", "; // delimiters for parameters
+ static const char delim[] = ", "; // delimiters for parameters
// probe the type of the argument
// If JSON object, it's high level commands
// If String, it's a low level command
@@ -580,7 +595,7 @@ void ZbSendSend(class JsonParserToken val_cmd, uint16_t device, uint16_t groupad
// we have an unsupported command type, just ignore it and fallback to missing command
}
- AddLogZ_P(LOG_LEVEL_DEBUG, PSTR("ZigbeeZCLSend device: 0x%04X, group: 0x%04X, endpoint:%d, cluster:0x%04X, cmd:0x%02X, send:\"%s\""),
+ AddLog_P(LOG_LEVEL_DEBUG, PSTR("ZigbeeZCLSend device: 0x%04X, group: 0x%04X, endpoint:%d, cluster:0x%04X, cmd:0x%02X, send:\"%s\""),
device, groupaddr, endpoint, cluster, cmd, cmd_s);
zigbeeZCLSendStr(device, groupaddr, endpoint, clusterSpecific, manuf, cluster, cmd, cmd_s);
ResponseCmndDone();
@@ -1643,25 +1658,18 @@ void CmndZbConfig(void) {
}
// display the current or new configuration
- char hex_ext_panid[20] = "0x";
- Uint64toHex(zb_ext_panid, &hex_ext_panid[2], 64);
- char hex_precfgkey_l[20] = "0x";
- Uint64toHex(zb_precfgkey_l, &hex_precfgkey_l[2], 64);
- char hex_precfgkey_h[20] = "0x";
- Uint64toHex(zb_precfgkey_h, &hex_precfgkey_h[2], 64);
-
// {"ZbConfig":{"Channel":11,"PanID":"0x1A63","ExtPanID":"0xCCCCCCCCCCCCCCCC","KeyL":"0x0F0D0B0907050301L","KeyH":"0x0D0C0A0806040200L"}}
Response_P(PSTR("{\"" D_PRFX_ZB D_JSON_ZIGBEE_CONFIG "\":{"
"\"Channel\":%d"
",\"PanID\":\"0x%04X\""
- ",\"ExtPanID\":\"%s\""
- ",\"KeyL\":\"%s\""
- ",\"KeyH\":\"%s\""
+ ",\"ExtPanID\":\"0x%_X\""
+ ",\"KeyL\":\"0x%_X\""
+ ",\"KeyH\":\"0x%_X\""
",\"TxRadio\":%d"
"}}"),
zb_channel, zb_pan_id,
- hex_ext_panid,
- hex_precfgkey_l, hex_precfgkey_h,
+ &zb_ext_panid,
+ &zb_precfgkey_l, &zb_precfgkey_h,
zb_txradio_dbm);
}
@@ -2092,7 +2100,7 @@ void ZigbeeMapRefresh(void) {
if ((!zigbee.init_phase) && (!zigbee.mapping_in_progress)) {
ZigbeeMapAllDevices();
}
- Webserver->sendHeader("Location","/zbm"); // Add a header to respond with a new location for the browser to go to the home page again
+ Webserver->sendHeader(F("Location"),F("/zbm")); // Add a header to respond with a new location for the browser to go to the home page again
Webserver->send(302);
}
@@ -2182,7 +2190,7 @@ bool Xdrv23(uint8_t function)
ZigbeeInit();
break;
case FUNC_COMMAND:
- result = DecodeCommand(kZbCommands, ZigbeeCommand);
+ result = DecodeCommand(kZbCommands, ZigbeeCommand, kZbSynonyms);
break;
case FUNC_SAVE_BEFORE_RESTART:
#ifdef USE_ZIGBEE_EZSP
diff --git a/tasmota/xdrv_24_buzzer.ino b/tasmota/xdrv_24_buzzer.ino
index e6665e21f..a2849065e 100644
--- a/tasmota/xdrv_24_buzzer.ino
+++ b/tasmota/xdrv_24_buzzer.ino
@@ -32,44 +32,41 @@ 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;
+ uint8_t tune_size = 0;
+ uint8_t size = 0;
} Buzzer;
/*********************************************************************************************/
-void BuzzerSet(uint8_t state)
-{
+void BuzzerSet(uint32_t state) {
if (Buzzer.inverted) {
state = !state;
}
- if (Buzzer.freq_mode == 1) {
+ if (Settings.flag4.buzzer_freq_mode) { // SetOption111 - Enable frequency output mode for buzzer
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
- }
+ // Set 50% duty cycle for frequency output
+ // Set 0% (or 100% for inverted PWM) duty cycle which turns off frequency output either way
+ analogWrite(Pin(GPIO_BUZZER), (state) ? Settings.pwm_range / 2 : 0); // set duty cycle for frequency output
last_state = state;
}
+ } else {
+ DigitalWrite(GPIO_BUZZER, 0, state); // Buzzer On/Off
}
- 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);
-void BuzzerBeep(uint32_t count, uint32_t on, uint32_t off, uint32_t tune, uint32_t mode)
-{
- Buzzer.set[0] = off; // off duration in 100 mSec steps
- Buzzer.set[1] = on; // on duration in 100 mSec steps
- Buzzer.duration = 1; // Start buzzer on first step
+void BuzzerBeep(uint32_t count, uint32_t on, uint32_t off, uint32_t tune, uint32_t mode) {
+ Buzzer.set[0] = off; // Off duration in 100 mSec steps
+ Buzzer.set[1] = on; // On duration in 100 mSec steps
+ Buzzer.duration = 1; // Start buzzer on first step
+ Buzzer.size = 0;
+ Buzzer.tune_size = 0;
+ Buzzer.tune = 0;
Buzzer.tune_reload = 0;
Buzzer.mode = mode;
@@ -78,65 +75,55 @@ void BuzzerBeep(uint32_t count, uint32_t on, uint32_t off, uint32_t tune, uint32
uint32_t tune2 = tune;
for (uint32_t i = 0; i < 32; i++) {
if (!(tune2 & 0x80000000)) {
- tune2 <<= 1; // Skip leading silence
+ tune2 <<= 1; // Skip leading silence
} else {
- Buzzer.tune_reload <<= 1; // Add swapped tune
+ Buzzer.tune_size++; // Allow trailing silence
+ Buzzer.tune_reload <<= 1; // Add swapped tune
Buzzer.tune_reload |= tune1 & 1;
tune1 >>= 1;
}
}
+ Buzzer.size = Buzzer.tune_size;
Buzzer.tune = Buzzer.tune_reload;
}
- Buzzer.count = count * 2; // Start buzzer
+ Buzzer.count = count * 2; // Start buzzer
- // We can use PWM mode for buzzer output if enabled.
- if (Settings.flag4.buzzer_freq_mode) { // SetOption111 - Enable frequency output mode for buzzer
- Buzzer.freq_mode = 1;
- }
- else {
- Buzzer.freq_mode = 0;
- }
-
- AddLog_P(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);
+ AddLog(LOG_LEVEL_DEBUG, PSTR("BUZ: Count %d(%d), Time %d/%d, Tune 0x%08X(0x%08X), Size %d, Mode %d"),
+ count, Buzzer.count, on, off, tune, Buzzer.tune, Buzzer.tune_size, Settings.flag4.buzzer_freq_mode);
Buzzer.enable = (Buzzer.count > 0);
if (Buzzer.enable) {
if (Settings.sleep > PWM_MAX_SLEEP) {
- TasmotaGlobal.sleep = PWM_MAX_SLEEP; // set a maxumum value of 10 milliseconds to ensure that buzzer periods are a bit more accurate
+ TasmotaGlobal.sleep = PWM_MAX_SLEEP; // Set a maxumum value of 10 milliseconds to ensure that buzzer periods are a bit more accurate
} else {
- TasmotaGlobal.sleep = Settings.sleep; // or keep the current sleep if it's lower than 10
+ TasmotaGlobal.sleep = Settings.sleep; // Or keep the current sleep if it's lower than 10
}
- }
- else {
- TasmotaGlobal.sleep = Settings.sleep; // restore original sleep
+ } else {
+ TasmotaGlobal.sleep = Settings.sleep; // Restore original sleep
BuzzerSet(0);
}
}
-void BuzzerSetStateToLed(uint32_t state)
-{
+void BuzzerSetStateToLed(uint32_t state) {
if (Buzzer.enable && (2 == Buzzer.mode)) {
Buzzer.state = (state != 0);
BuzzerSet(Buzzer.state);
}
}
-void BuzzerBeep(uint32_t count)
-{
+void BuzzerBeep(uint32_t count) {
BuzzerBeep(count, 1, 1, 0, 0);
}
-void BuzzerEnabledBeep(uint32_t count, uint32_t duration)
-{
- if (Settings.flag3.buzzer_enable) { // SetOption67 - Enable buzzer when available
+void BuzzerEnabledBeep(uint32_t count, uint32_t duration) {
+ if (Settings.flag3.buzzer_enable) { // SetOption67 - Enable buzzer when available
BuzzerBeep(count, duration, 1, 0, 0);
}
}
/*********************************************************************************************/
-bool BuzzerPinState(void)
-{
+bool BuzzerPinState(void) {
if (XdrvMailbox.index == AGPIO(GPIO_BUZZER_INV)) {
Buzzer.inverted = 1;
XdrvMailbox.index -= (AGPIO(GPIO_BUZZER_INV) - AGPIO(GPIO_BUZZER));
@@ -145,8 +132,7 @@ bool BuzzerPinState(void)
return false;
}
-void BuzzerInit(void)
-{
+void BuzzerInit(void) {
if (PinUsed(GPIO_BUZZER)) {
pinMode(Pin(GPIO_BUZZER), OUTPUT);
BuzzerSet(0);
@@ -155,17 +141,18 @@ void BuzzerInit(void)
}
}
-void BuzzerEvery100mSec(void)
-{
+void BuzzerEvery100mSec(void) {
if (Buzzer.enable && (Buzzer.mode != 2)) {
if (Buzzer.count) {
if (Buzzer.duration) {
Buzzer.duration--;
if (!Buzzer.duration) {
- if (Buzzer.tune) {
+ if (Buzzer.size) {
+ Buzzer.size--;
Buzzer.state = Buzzer.tune & 1;
Buzzer.tune >>= 1;
} else {
+ Buzzer.size = Buzzer.tune_size;
Buzzer.tune = Buzzer.tune_reload;
Buzzer.count -= (Buzzer.tune_reload) ? 2 : 1;
Buzzer.state = Buzzer.count & 1;
@@ -187,37 +174,43 @@ void BuzzerEvery100mSec(void)
* Commands
\*********************************************************************************************/
-const char kBuzzerCommands[] PROGMEM = "|" // No prefix
- "Buzzer" ;
+const char kBuzzerCommands[] PROGMEM = "Buzzer|" // Prefix
+ "Active|Pwm||" ;
+
+SO_SYNONYMS(kBuzzerSynonyms,
+ 67, 111
+);
void (* const BuzzerCommand[])(void) PROGMEM = {
&CmndBuzzer };
-void CmndBuzzer(void)
-{
+void CmndBuzzer(void) {
// Buzzer ,,,
// All parameters are optional
//
- // Buzzer = Buzzer 1,1,1 = Beep once with both duration and pause set to 100mS
- // Buzzer 0 = Stop active beep cycle
- // Buzzer 2 = Beep twice with duration 200mS and pause 100mS
- // Buzzer 2,3 = Beep twice with duration 300mS and pause 100mS
- // Buzzer 2,3,4 = Beep twice with duration 300mS and pause 400mS
- // Buzzer 2,3,4,0xF54 = Beep a sequence twice indicated by 0xF54 = 1111 0101 01 with duration 300mS and pause 400mS
- // Buzzer -1 = Beep infinite
- // Buzzer -2 = Beep following link led
+ // Buzzer = Buzzer 1,1,1 = Beep once with both duration and pause set to 100mS
+ // Buzzer 0 = Stop active beep cycle
+ // Buzzer 2 = Beep twice with duration 200mS and pause 100mS
+ // Buzzer 2,3 = Beep twice with duration 300mS and pause 100mS
+ // Buzzer 2,3,4 = Beep twice with duration 300mS and pause 400mS
+ // Buzzer 2,3,4,0x0F54 = Beep a sequence twice indicated by 0x0F54 = 1111 0101 0100 with duration 300mS and pause 400mS
+ // Notice skipped leading zeroes but valid trailing zeroes
+ // Buzzer -1 = Beep infinite
+ // Buzzer -2 = Beep following link led
if (XdrvMailbox.data_len > 0) {
if (XdrvMailbox.payload != 0) {
uint32_t parm[4] = { 0 };
- uint32_t mode = 0;
ParseParameters(4, parm);
- if (XdrvMailbox.payload <= 0) {
- parm[0] = 1; // Default Count
- mode = -XdrvMailbox.payload; // 0, 1 or 2
+ uint32_t mode = 0;
+ if (XdrvMailbox.payload < 0) {
+ parm[0] = 1; // Default Count
+ if (XdrvMailbox.payload > -3) {
+ mode = -XdrvMailbox.payload; // 0, 1 or 2
+ }
}
for (uint32_t i = 1; i < 3; i++) {
- if (parm[i] < 1) { parm[i] = 1; } // Default On time, Off time
+ if (parm[i] < 1) { parm[i] = 1; } // Default On time, Off time
}
BuzzerBeep(parm[0], parm[1], parm[2], parm[3], mode);
} else {
@@ -233,8 +226,7 @@ void CmndBuzzer(void)
* Interface
\*********************************************************************************************/
-bool Xdrv24(uint8_t function)
-{
+bool Xdrv24(uint8_t function) {
bool result = false;
if (Buzzer.active) {
@@ -243,7 +235,7 @@ bool Xdrv24(uint8_t function)
BuzzerEvery100mSec();
break;
case FUNC_COMMAND:
- result = DecodeCommand(kBuzzerCommands, BuzzerCommand);
+ result = DecodeCommand(kBuzzerCommands, BuzzerCommand, kBuzzerSynonyms);
break;
case FUNC_PRE_INIT:
BuzzerInit();
diff --git a/tasmota/xdrv_26_ariluxrf.ino b/tasmota/xdrv_26_ariluxrf.ino
index fca693c8d..0246e6a8f 100644
--- a/tasmota/xdrv_26_ariluxrf.ino
+++ b/tasmota/xdrv_26_ariluxrf.ino
@@ -98,7 +98,7 @@ void AriluxRfHandler(void)
uint16_t stored_hostcode = Settings.rf_code[1][6] << 8 | Settings.rf_code[1][7];
// DEBUG_DRIVER_LOG(PSTR(D_LOG_RFR D_HOST D_CODE " 0x%04X, " D_RECEIVED " 0x%06X"), stored_hostcode, Arilux.rf_received_value);
- AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_RFR D_HOST D_CODE " 0x%04X, " D_RECEIVED " 0x%06X"), stored_hostcode, Arilux.rf_received_value);
+ AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_RFR D_HOST D_CODE " 0x%04X, " D_RECEIVED " 0x%06X"), stored_hostcode, Arilux.rf_received_value);
if (hostcode == stored_hostcode) {
char command[33];
diff --git a/tasmota/xdrv_27_shutter.ino b/tasmota/xdrv_27_shutter.ino
index cd54878d7..8ac9f2940 100644
--- a/tasmota/xdrv_27_shutter.ino
+++ b/tasmota/xdrv_27_shutter.ino
@@ -144,7 +144,7 @@ void ShutterRtc50mS(void)
case SHT_COUNTER:
if (Shutter[i].accelerator) {
- //AddLog_P(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Accelerator i=%d -> %d"),i, Shutter[i].accelerator);
+ //AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Accelerator i=%d -> %d"),i, Shutter[i].accelerator);
ShutterUpdateVelocity(i);
analogWriteFreq(Shutter[i].pwm_velocity);
analogWrite(Pin(GPIO_PWM1, i), 50);
@@ -164,7 +164,7 @@ int32_t ShutterPercentToRealPosition(int16_t percent, uint32_t index)
// check against DIV 0
for (uint32_t j = 0; j < 5; j++) {
if (0 == Settings.shuttercoeff[j][index]) {
- AddLog_P(LOG_LEVEL_ERROR, PSTR("SHT: RESET/INIT CALIBRATION MATRIX DIV 0"));
+ AddLog(LOG_LEVEL_ERROR, PSTR("SHT: RESET/INIT CALIBRATION MATRIX DIV 0"));
for (uint32_t k = 0; k < 5; k++) {
Settings.shuttercoeff[k][index] = SHT_DIV_ROUND(calibrate_pos[k+1] * 1000, calibrate_pos[5]);
}
@@ -251,7 +251,7 @@ void ShutterInit(void)
if (Settings.shutter_mode == SHT_UNDEF) {
bool relay_in_interlock = false;
- AddLog_P(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Mode undef.. calculate..."));
+ AddLog(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_P(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);
@@ -400,7 +400,7 @@ void ShutterDecellerateForStop(uint8_t i)
int16_t missing_steps;
Shutter[i].accelerator = -(ShutterGlobal.open_velocity_max / (Shutter[i].motordelay>4 ? (Shutter[i].motordelay*11)/10 : 4) );
while (Shutter[i].pwm_velocity > -2*Shutter[i].accelerator ) {
- AddLog_P(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Velocity %ld, Delta %d"), Shutter[i].pwm_velocity, Shutter[i].accelerator );
+ AddLog(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);
@@ -408,19 +408,18 @@ void ShutterDecellerateForStop(uint8_t 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_P(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Remain steps %d, Counter %d, Freq %d"), missing_steps, RtcSettings.pulse_counter[i] ,Shutter[i].pwm_velocity);
+ AddLog(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[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[i].real_position = ShutterCalculatePosition(i);
- AddLog_P(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Real %d, Pulsecount %d, Start %d"), Shutter[i].real_position,RtcSettings.pulse_counter[i], Shutter[i].start_position);
+ AddLog(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[i].direction = 0;
@@ -430,7 +429,7 @@ void ShutterDecellerateForStop(uint8_t i)
}
void ShutterPowerOff(uint8_t i) {
- AddLog_P(LOG_LEVEL_DEBUG, PSTR("SHT: Stop Shutter %d. Switchmode %d"), i,Shutter[i].switch_mode);
+ AddLog(LOG_LEVEL_DEBUG, PSTR("SHT: Stop Shutter %d. Switchmode %d"), i,Shutter[i].switch_mode);
ShutterDecellerateForStop(i);
switch (Shutter[i].switch_mode) {
case SHT_SWITCH:
@@ -521,14 +520,14 @@ void ShutterAllowPreStartProcedure(uint8_t i)
{
#ifdef USE_RULES
uint32_t uptime_Local=0;
- AddLog_P(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Delay Start. var%d <99>=<%s>, max10s?"),i+i, rules_vars[i]);
+ AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Delay Start. var%d <99>=<%s>, max10s?"),i+i, rules_vars[i]);
TasmotaGlobal.rules_flag.shutter_moving = 1;
XdrvRulesProcess();
uptime_Local = TasmotaGlobal.uptime;
while (uptime_Local+10 > TasmotaGlobal.uptime && (String)rules_vars[i] == "99") {
loop();
}
- AddLog_P(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Delay Start. Done"));
+ AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Delay Start. Done"));
#endif // USE_RULES
}
@@ -658,7 +657,7 @@ void ShutterRelayChanged(void)
switch (powerstate_local) {
case 1:
ShutterStartInit(i, Shutter[i].lastdirection*-1 , Shutter[i].lastdirection == 1 ? 0 : Shutter[i].open_max);
- AddLog_P(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Shtr%d Garage. NewTarget %d"), i, Shutter[i].target_position);
+ AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Shtr%d Garage. NewTarget %d"), i, Shutter[i].target_position);
break;
default:
Shutter[i].target_position = Shutter[i].real_position;
@@ -666,7 +665,7 @@ void ShutterRelayChanged(void)
} // switch (ShutterGlobal.position_mode)
- AddLog_P(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Shtr%d, Target %ld, Powerstatelocal %d"), i+1, Shutter[i].target_position, powerstate_local);
+ AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Shtr%d, Target %ld, Powerstatelocal %d"), i+1, Shutter[i].target_position, powerstate_local);
} // if (manual_relays_changed)
} // for (uint32_t i = 0; i < TasmotaGlobal.shutters_present; i++)
}
@@ -761,12 +760,12 @@ void ShutterButtonHandler(void)
Settings.shutter_button[i], shutter_index, Button.press_counter[i] , min_shutterbutton_press_counter, i);
if ((button_index != i) && (Settings.shutter_button[i] & (1<<31)) && ((Settings.shutter_button[i] & 0x03) == shutter_index) && (i != button_index) && (Button.press_counter[i] < min_shutterbutton_press_counter)) {
min_shutterbutton_press_counter = Button.press_counter[i];
- AddLog_P(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: minShutterButtonPressCounter %d"), min_shutterbutton_press_counter);
+ AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: minShutterButtonPressCounter %d"), min_shutterbutton_press_counter);
}
}
if (min_shutterbutton_press_counter == Button.press_counter[button_index]) {
// simultaneous shutter button press detected
- AddLog_P(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Simultanous press detected"));
+ AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Simultanous press detected"));
press_index = Button.press_counter[button_index];
for (uint32_t i = 0; i < MAX_KEYS; i++)
if ((Settings.shutter_button[i] & (1<<31)) && ((Settings.shutter_button[i] & 0x03) != shutter_index))
@@ -872,7 +871,7 @@ void ShutterSetPosition(uint32_t device, uint32_t position)
void ShutterToggle(bool dir)
{
- AddLog_P(LOG_LEVEL_DEBUG, PSTR("SHT: Payload toggle: %d, i %d, dir %d"), XdrvMailbox.payload, XdrvMailbox.index, dir);
+ AddLog(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;
}
@@ -896,7 +895,7 @@ void ShutterToggle(bool dir)
void CmndShutterOpen(void)
{
- //AddLog_P(LOG_LEVEL_DEBUG, PSTR("SHT: Payload open: %d, i %d"), XdrvMailbox.payload, XdrvMailbox.index);
+ //AddLog(LOG_LEVEL_DEBUG, PSTR("SHT: Payload open: %d, i %d"), XdrvMailbox.payload, XdrvMailbox.index);
if ((1 == XdrvMailbox.index) && (XdrvMailbox.payload != -99)) {
XdrvMailbox.index = XdrvMailbox.payload;
}
@@ -919,7 +918,7 @@ void CmndShutterStopOpen(void)
void CmndShutterClose(void)
{
- //AddLog_P(LOG_LEVEL_DEBUG, PSTR("SHT: Payload close: %d, i %d"), XdrvMailbox.payload, XdrvMailbox.index);
+ //AddLog(LOG_LEVEL_DEBUG, PSTR("SHT: Payload close: %d, i %d"), XdrvMailbox.payload, XdrvMailbox.index);
if ((1 == XdrvMailbox.index) && (XdrvMailbox.payload != -99)) {
XdrvMailbox.index = XdrvMailbox.payload;
}
@@ -985,7 +984,7 @@ void CmndShutterStop(void)
uint32_t i = XdrvMailbox.index -1;
if (Shutter[i].direction != 0) {
- AddLog_P(LOG_LEVEL_DEBUG, PSTR("SHT: Stop moving %d: dir: %d"), XdrvMailbox.index, Shutter[i].direction);
+ AddLog(LOG_LEVEL_DEBUG, PSTR("SHT: Stop moving %d: dir: %d"), XdrvMailbox.index, Shutter[i].direction);
Shutter[i].target_position = Shutter[i].real_position;
}
if (XdrvMailbox.command)
@@ -1017,7 +1016,7 @@ void CmndShutterPosition(void)
if (!(Settings.shutter_options[XdrvMailbox.index-1] & 2)) {
uint32_t index = XdrvMailbox.index-1;
//limit the payload
- AddLog_P(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Pos. in: payload %s (%d), payload %d, idx %d, src %d"), XdrvMailbox.data , XdrvMailbox.data_len, XdrvMailbox.payload , XdrvMailbox.index, TasmotaGlobal.last_source );
+ AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Pos. in: payload %s (%d), payload %d, idx %d, src %d"), XdrvMailbox.data , XdrvMailbox.data_len, XdrvMailbox.payload , XdrvMailbox.index, TasmotaGlobal.last_source );
// value 0 with data_len > 0 can mean Open
// special handling fo UP,DOWN,TOGGLE,STOP command comming with payload -99
@@ -1054,7 +1053,7 @@ void CmndShutterPosition(void)
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_P(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: lastsource %d:, real %d, target %d, payload %d"), TasmotaGlobal.last_source, Shutter[index].real_position ,Shutter[index].target_position,target_pos_percent);
+ AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: lastsource %d:, real %d, target %d, payload %d"), TasmotaGlobal.last_source, Shutter[index].real_position ,Shutter[index].target_position,target_pos_percent);
}
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) {
@@ -1091,7 +1090,7 @@ void CmndShutterPosition(void)
case SHT_TIME_GARAGE:
if (!ShutterGlobal.skip_relay_change) {
if (new_shutterdirection == Shutter[index].lastdirection) {
- AddLog_P(LOG_LEVEL_INFO, PSTR("SHT: Garage not move in this direction: %d"), Shutter[index].switch_mode == SHT_PULSE);
+ AddLog(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);
@@ -1401,7 +1400,7 @@ void CmndShutterPwmRange(void)
}
Settings.shutter_pwmrange[i][XdrvMailbox.index -1] = field;
}
- AddLog_P(LOG_LEVEL_DEBUG, PSTR("SHT: Shtr%d Init1. pwmmin %d, pwmmax %d"), XdrvMailbox.index , Settings.shutter_pwmrange[0][XdrvMailbox.index -1], Settings.shutter_pwmrange[1][XdrvMailbox.index -1]);
+ AddLog(LOG_LEVEL_DEBUG, PSTR("SHT: Shtr%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 {
@@ -1434,7 +1433,7 @@ void CmndShutterCalibration(void)
Settings.shutter_set50percent[XdrvMailbox.index -1] = 50;
for (i = 0; i < 5; i++) {
Settings.shuttercoeff[i][XdrvMailbox.index -1] = SHT_DIV_ROUND((uint32_t)messwerte[i] * 1000, messwerte[4]);
- AddLog_P(LOG_LEVEL_DEBUG, PSTR("SHT: Shuttercoeff %d, i %d, Value %d, MeasuredValue %d"), i,XdrvMailbox.index -1,Settings.shuttercoeff[i][XdrvMailbox.index -1], messwerte[i]);
+ AddLog(LOG_LEVEL_DEBUG, PSTR("SHT: Shuttercoeff %d, i %d, Value %d, MeasuredValue %d"), i,XdrvMailbox.index -1,Settings.shuttercoeff[i][XdrvMailbox.index -1], messwerte[i]);
}
ShutterInit();
ResponseCmndIdxChar(XdrvMailbox.data);
@@ -1515,7 +1514,7 @@ bool Xdrv27(uint8_t function)
char stemp1[10];
// extract the number of the relay that was switched and save for later in Update Position.
ShutterGlobal.RelayCurrentMask = XdrvMailbox.index ^ ShutterGlobal.RelayOldMask;
- AddLog_P(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Switched relay %d by %s"), ShutterGlobal.RelayCurrentMask,GetTextIndexed(stemp1, sizeof(stemp1), TasmotaGlobal.last_source, kCommandSource));
+ AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Switched relay %d by %s"), ShutterGlobal.RelayCurrentMask,GetTextIndexed(stemp1, sizeof(stemp1), TasmotaGlobal.last_source, kCommandSource));
ShutterRelayChanged();
ShutterGlobal.RelayOldMask = XdrvMailbox.index;
break;
@@ -1528,10 +1527,10 @@ bool Xdrv27(uint8_t function)
}
ShutterGlobal.RelayCurrentMask >>= 1;
}
- //AddLog_P(LOG_LEVEL_ERROR, PSTR("SHT: Skip relay change %d"), i+1);
+ //AddLog(LOG_LEVEL_ERROR, PSTR("SHT: Skip relay change %d"), i+1);
result = true;
ShutterGlobal.skip_relay_change = 0;
- AddLog_P(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Skipping switch off relay %d"), i);
+ AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Skipping switch off relay %d"), i);
ExecuteCommandPowerShutter(i+1, 0, SRC_SHUTTER);
}
break;
diff --git a/tasmota/xdrv_28_pcf8574.ino b/tasmota/xdrv_28_pcf8574.ino
index b79f3caa6..231476ba2 100644
--- a/tasmota/xdrv_28_pcf8574.ino
+++ b/tasmota/xdrv_28_pcf8574.ino
@@ -48,14 +48,14 @@ void Pcf8574SwitchRelay(void)
for (uint32_t i = 0; i < TasmotaGlobal.devices_present; i++) {
uint8_t relay_state = bitRead(XdrvMailbox.index, i);
- //AddLog_P(LOG_LEVEL_DEBUG, PSTR("PCF: Pcf8574.max_devices %d requested pin %d"), Pcf8574.max_devices,Pcf8574.pin[i]);
+ //AddLog(LOG_LEVEL_DEBUG, PSTR("PCF: Pcf8574.max_devices %d requested pin %d"), Pcf8574.max_devices,Pcf8574.pin[i]);
if (Pcf8574.max_devices > 0 && Pcf8574.pin[i] < 99) {
uint8_t board = Pcf8574.pin[i]>>3;
uint8_t oldpinmask = Pcf8574.pin_mask[board];
uint8_t _val = bitRead(TasmotaGlobal.rel_inverted, i) ? !relay_state : relay_state;
- //AddLog_P(LOG_LEVEL_DEBUG, PSTR("PCF: Pcf8574SwitchRelay %d on pin %d"), i,state);
+ //AddLog(LOG_LEVEL_DEBUG, PSTR("PCF: Pcf8574SwitchRelay %d on pin %d"), i,state);
if (_val) {
Pcf8574.pin_mask[board] |= _val << (Pcf8574.pin[i]&0x7);
@@ -79,7 +79,7 @@ void Pcf8574Init(void)
#ifdef USE_MCP230xx_ADDR
if (USE_MCP230xx_ADDR == pcf8574_address) {
- AddLog_P(LOG_LEVEL_INFO, PSTR("PCF: Address 0x%02x reserved for MCP320xx skipped"), pcf8574_address);
+ AddLog(LOG_LEVEL_INFO, PSTR("PCF: Address 0x%02x reserved for MCP320xx skipped"), pcf8574_address);
pcf8574_address++;
if ((PCF8574_ADDR1 +7) == pcf8574_address) { // Support I2C addresses 0x20 to 0x26 and 0x39 to 0x3F
pcf8574_address = PCF8574_ADDR2 +1;
@@ -87,7 +87,7 @@ void Pcf8574Init(void)
}
#endif
- // AddLog_P(LOG_LEVEL_DEBUG, PSTR("PCF: Probing addr: 0x%x for PCF8574"), pcf8574_address);
+ // AddLog(LOG_LEVEL_DEBUG, PSTR("PCF: Probing addr: 0x%x for PCF8574"), pcf8574_address);
if (I2cSetDevice(pcf8574_address)) {
Pcf8574.type = true;
@@ -115,7 +115,7 @@ void Pcf8574Init(void)
Pcf8574.max_connected_ports = 0; // reset no of devices to avoid duplicate ports on duplicate init.
for (uint32_t idx = 0; idx < Pcf8574.max_devices; idx++) { // suport up to 8 boards PCF8574
- AddLog_P(LOG_LEVEL_DEBUG, PSTR("PCF: Device %d config 0x%02x"), idx +1, Settings.pcf8574_config[idx]);
+ AddLog(LOG_LEVEL_DEBUG, PSTR("PCF: Device %d config 0x%02x"), idx +1, Settings.pcf8574_config[idx]);
for (uint32_t i = 0; i < 8; i++) {
uint8_t _result = Settings.pcf8574_config[idx] >> i &1;
@@ -128,7 +128,7 @@ void Pcf8574Init(void)
}
}
}
- AddLog_P(LOG_LEVEL_INFO, PSTR("PCF: Total devices %d, PCF8574 output ports %d"), Pcf8574.max_devices, Pcf8574.max_connected_ports);
+ AddLog(LOG_LEVEL_INFO, PSTR("PCF: Total devices %d, PCF8574 output ports %d"), Pcf8574.max_devices, Pcf8574.max_connected_ports);
}
}
@@ -158,7 +158,7 @@ void HandlePcf8574(void)
{
if (!HttpCheckPriviledgedAccess()) { return; }
- AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_HTTP D_CONFIGURE_PCF8574));
+ AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_HTTP D_CONFIGURE_PCF8574));
if (Webserver->hasArg("save")) {
Pcf8574SaveSettings();
@@ -193,7 +193,7 @@ void Pcf8574SaveSettings(void)
char stemp[7];
char tmp[100];
- //AddLog_P(LOG_LEVEL_DEBUG, PSTR("PCF: Start working on Save arguements: inverted:%d")), Webserver->hasArg("b1");
+ //AddLog(LOG_LEVEL_DEBUG, PSTR("PCF: Start working on Save arguements: inverted:%d")), Webserver->hasArg("b1");
Settings.flag3.pcf8574_ports_inverted = Webserver->hasArg("b1"); // SetOption81 - Invert all ports on PCF8574 devices
for (byte idx = 0; idx < Pcf8574.max_devices; idx++) {
@@ -219,7 +219,7 @@ void Pcf8574SaveSettings(void)
}
}
//Settings.pcf8574_config[0] = (!strlen(webServer->arg("i2cs0").c_str())) ? 0 : atoi(webServer->arg("i2cs0").c_str());
- //AddLog_P(LOG_LEVEL_INFO, PSTR("PCF: I2C Board: %d, Config: %2x")), idx, Settings.pcf8574_config[idx];
+ //AddLog(LOG_LEVEL_INFO, PSTR("PCF: I2C Board: %d, Config: %2x")), idx, Settings.pcf8574_config[idx];
}
}
diff --git a/tasmota/xdrv_29_deepsleep.ino b/tasmota/xdrv_29_deepsleep.ino
index 3e28a9d31..45da79cb2 100644
--- a/tasmota/xdrv_29_deepsleep.ino
+++ b/tasmota/xdrv_29_deepsleep.ino
@@ -68,7 +68,7 @@ void DeepSleepReInit(void)
if ((RtcSettings.ultradeepsleep > DEEPSLEEP_MAX_CYCLE) && (RtcSettings.ultradeepsleep < 1700000000)) {
// Go back to sleep after 60 minutes if requested deepsleep has not been reached
RtcSettings.ultradeepsleep = RtcSettings.ultradeepsleep - DEEPSLEEP_MAX_CYCLE;
- AddLog_P(LOG_LEVEL_ERROR, PSTR("DSL: Remain DeepSleep %d"), RtcSettings.ultradeepsleep);
+ AddLog(LOG_LEVEL_ERROR, PSTR("DSL: Remain DeepSleep %d"), RtcSettings.ultradeepsleep);
RtcSettingsSave();
RtcRebootReset();
#ifdef ESP8266
@@ -96,7 +96,7 @@ void DeepSleepPrepare(void)
(RtcSettings.deepsleep_slip < 9000) ||
(RtcSettings.deepsleep_slip > 11000) ||
(RtcSettings.nextwakeup > (UtcTime() + Settings.deepsleep))) {
- AddLog_P(LOG_LEVEL_ERROR, PSTR("DSL: Reset wrong settings wakeup: %ld, slip %ld"), RtcSettings.nextwakeup, RtcSettings.deepsleep_slip );
+ AddLog(LOG_LEVEL_ERROR, PSTR("DSL: Reset wrong settings wakeup: %ld, slip %ld"), RtcSettings.nextwakeup, RtcSettings.deepsleep_slip );
RtcSettings.nextwakeup = 0;
RtcSettings.deepsleep_slip = 10000;
}
@@ -137,7 +137,7 @@ void DeepSleepPrepare(void)
void DeepSleepStart(void)
{
- AddLog_P(LOG_LEVEL_INFO, PSTR(D_LOG_APPLICATION "Sleeping")); // Won't show in GUI
+ AddLog(LOG_LEVEL_INFO, PSTR(D_LOG_APPLICATION "Sleeping")); // Won't show in GUI
WifiShutdown();
RtcSettings.ultradeepsleep = RtcSettings.nextwakeup - UtcTime();
diff --git a/tasmota/xdrv_30_exs_dimmer.ino b/tasmota/xdrv_30_exs_dimmer.ino
index b704d616d..ffab61d5d 100644
--- a/tasmota/xdrv_30_exs_dimmer.ino
+++ b/tasmota/xdrv_30_exs_dimmer.ino
@@ -124,7 +124,7 @@ void ExsSerialSend(const uint8_t data[] = nullptr, uint16_t len = 0)
char rc;
#ifdef EXS_DEBUG
- AddLog_P(LOG_LEVEL_DEBUG_MORE, PSTR("EXS: Tx Packet:"));
+ AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("EXS: Tx Packet:"));
AddLogBuffer(LOG_LEVEL_DEBUG_MORE, (uint8_t *)data, len);
#endif
@@ -145,7 +145,7 @@ void ExsSerialSend(const uint8_t data[] = nullptr, uint16_t len = 0)
{
// timeout
#ifdef EXS_DEBUG
- AddLog_P(LOG_LEVEL_DEBUG, PSTR("ESX: serial send timeout"));
+ AddLog(LOG_LEVEL_DEBUG, PSTR("ESX: serial send timeout"));
#endif
continue;
}
@@ -215,9 +215,9 @@ void ExsSetBri(uint8_t device, uint8_t bri)
void ExsSyncState(uint8_t device)
{
#ifdef EXS_DEBUG
- AddLog_P(LOG_LEVEL_DEBUG, PSTR("EXS: Channel %d Power Want %d, Is %d"),
+ AddLog(LOG_LEVEL_DEBUG, PSTR("EXS: Channel %d Power Want %d, Is %d"),
device, bitRead(Exs.power, device), Exs.dimmer.channel[device].dimm);
- AddLog_P(LOG_LEVEL_DEBUG, PSTR("EXS: Set Channel %d Brightness Want %d, Is %d"),
+ AddLog(LOG_LEVEL_DEBUG, PSTR("EXS: Set Channel %d Brightness Want %d, Is %d"),
device, Exs.dimm[device], Exs.dimmer.channel[device].bright_tbl);
#endif
@@ -236,7 +236,7 @@ void ExsSyncState(uint8_t device)
bool ExsSyncState()
{
#ifdef EXS_DEBUG
- AddLog_P(LOG_LEVEL_DEBUG, PSTR("EXS: Serial %p, Cmd %d"), ExsSerial, Exs.cmd_status);
+ AddLog(LOG_LEVEL_DEBUG, PSTR("EXS: Serial %p, Cmd %d"), ExsSerial, Exs.cmd_status);
#endif
if (!ExsSerial || Exs.cmd_status != 0)
@@ -363,7 +363,7 @@ bool ExsModuleSelected(void)
bool ExsSetChannels(void)
{
#ifdef EXS_DEBUG
- AddLog_P(LOG_LEVEL_DEBUG_MORE, PSTR("EXS: SetChannels:"));
+ AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("EXS: SetChannels:"));
AddLogBuffer(LOG_LEVEL_DEBUG_MORE, (uint8_t *)XdrvMailbox.data, XdrvMailbox.data_len);
#endif
@@ -374,7 +374,7 @@ bool ExsSetChannels(void)
bool ExsSetPower(void)
{
- AddLog_P(LOG_LEVEL_INFO, PSTR("EXS: Set Power, Device %d, Power 0x%02x"),
+ AddLog(LOG_LEVEL_INFO, PSTR("EXS: Set Power, Device %d, Power 0x%02x"),
TasmotaGlobal.active_device, XdrvMailbox.index);
Exs.power = XdrvMailbox.index;
@@ -386,7 +386,7 @@ void EsxMcuStart(void)
int retries = 3;
#ifdef EXS_DEBUG
- AddLog_P(LOG_LEVEL_DEBUG, PSTR("EXS: Request MCU configuration, PIN %d to Low"), Pin(GPIO_EXS_ENABLE));
+ AddLog(LOG_LEVEL_DEBUG, PSTR("EXS: Request MCU configuration, PIN %d to Low"), Pin(GPIO_EXS_ENABLE));
#endif
pinMode(Pin(GPIO_EXS_ENABLE), OUTPUT);
@@ -404,7 +404,7 @@ void EsxMcuStart(void)
void ExsInit(void)
{
#ifdef EXS_DEBUG
- AddLog_P(LOG_LEVEL_INFO, PSTR("EXS: Starting Tx %d Rx %d"), Pin(GPIO_TXD), Pin(GPIO_RXD));
+ AddLog(LOG_LEVEL_INFO, PSTR("EXS: Starting Tx %d Rx %d"), Pin(GPIO_TXD), Pin(GPIO_RXD));
#endif
Exs.buffer = (uint8_t *)malloc(EXS_BUFFER_SIZE);
@@ -432,7 +432,7 @@ void ExsSerialInput(void)
yield();
uint8_t serial_in_byte = ExsSerial->read();
- AddLog_P(LOG_LEVEL_INFO, PSTR("EXS: Serial In Byte 0x%02x"), serial_in_byte);
+ AddLog(LOG_LEVEL_INFO, PSTR("EXS: Serial In Byte 0x%02x"), serial_in_byte);
if (Exs.cmd_status == 0 &&
serial_in_byte == 0x7B)
@@ -456,7 +456,7 @@ void ExsSerialInput(void)
Exs.cmd_status = 0;
#ifdef EXS_DEBUG
- AddLog_P(LOG_LEVEL_DEBUG_MORE, PSTR("EXS: CRC: 0x%02x, RX Packet:"), crc);
+ AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("EXS: CRC: 0x%02x, RX Packet:"), crc);
AddLogBuffer(LOG_LEVEL_DEBUG_MORE, (uint8_t *)Exs.buffer, Exs.byte_counter);
#endif
diff --git a/tasmota/xdrv_31_tasmota_client.ino b/tasmota/xdrv_31_tasmota_client.ino
index bf4426b61..54082d827 100644
--- a/tasmota/xdrv_31_tasmota_client.ino
+++ b/tasmota/xdrv_31_tasmota_client.ino
@@ -238,14 +238,14 @@ uint8_t TasmotaClient_receiveData(char* buffer, int size) {
}
if (255 == index) { index = 0; }
-// AddLog_P(LOG_LEVEL_DEBUG, PSTR("TCL: ReceiveData"));
+// AddLog(LOG_LEVEL_DEBUG, PSTR("TCL: ReceiveData"));
// AddLogBuffer(LOG_LEVEL_DEBUG, (uint8_t*)buffer, index);
return index;
}
uint8_t TasmotaClient_sendBytes(uint8_t* bytes, int count) {
-// AddLog_P(LOG_LEVEL_DEBUG, PSTR("TCL: SendBytes"));
+// AddLog(LOG_LEVEL_DEBUG, PSTR("TCL: SendBytes"));
// AddLogBuffer(LOG_LEVEL_DEBUG, (uint8_t*)&bytes, count);
TasmotaClient_Serial->write(bytes, count);
@@ -298,7 +298,7 @@ uint32_t TasmotaClient_Flash(uint8_t* data, size_t size) {
}
if (timeout > 50) { return 1; } // Error: Bootloader could not be found
- AddLog_P(LOG_LEVEL_INFO, PSTR("TCL: Found bootloader"));
+ AddLog(LOG_LEVEL_INFO, PSTR("TCL: Found bootloader"));
uint8_t ProgParams[] = {0x86, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x03, 0xff, 0xff, 0xff, 0xff, 0x00, 0x80, 0x04, 0x00, 0x00, 0x00, 0x80, 0x00};
if (!TasmotaClient_execParam(CMND_STK_SET_DEVICE, ProgParams, sizeof(ProgParams))) {
@@ -368,7 +368,7 @@ uint32_t TasmotaClient_Flash(uint8_t* data, size_t size) {
}
else if (0x0A != flash_buffer[ca]) {
if (!isalnum(flash_buffer[ca])) {
-// AddLog_P(LOG_LEVEL_DEBUG, PSTR("DBG: Size %d, Processed %d"), size, processed);
+// AddLog(LOG_LEVEL_DEBUG, PSTR("DBG: Size %d, Processed %d"), size, processed);
error = 7; // Error: Invalid data
break;
}
@@ -408,7 +408,7 @@ void TasmotaClient_Init(void) {
pinMode(Pin(GPIO_TASMOTACLIENT_RST), OUTPUT);
TClient.SerialEnabled = true;
TasmotaClient_Reset();
- AddLog_P(LOG_LEVEL_INFO, PSTR("TCL: Enabled"));
+ AddLog(LOG_LEVEL_INFO, PSTR("TCL: Enabled"));
}
}
}
@@ -421,10 +421,10 @@ void TasmotaClient_Init(void) {
memcpy(&TClientSettings, &buffer, sizeof(TClientSettings));
if (TASMOTA_CLIENT_LIB_VERSION == TClientSettings.features_version) {
TClient.type = true;
- AddLog_P(LOG_LEVEL_INFO, PSTR("TCL: Version %u"), TClientSettings.features_version);
+ AddLog(LOG_LEVEL_INFO, PSTR("TCL: Version %u"), TClientSettings.features_version);
} else {
if ((!TClient.unsupported) && (TClientSettings.features_version > 0)) {
- AddLog_P(LOG_LEVEL_INFO, PSTR("TCL: Version %u not supported!"), TClientSettings.features_version);
+ AddLog(LOG_LEVEL_INFO, PSTR("TCL: Version %u not supported!"), TClientSettings.features_version);
TClient.unsupported = true;
}
}
@@ -456,7 +456,7 @@ void TasmotaClient_sendCmnd(uint8_t cmnd, uint8_t param) {
memcpy(&buffer[1], &TClientCommand, sizeof(TClientCommand));
buffer[sizeof(TClientCommand)+1] = CMND_END;
-// AddLog_P(LOG_LEVEL_DEBUG, PSTR("TCL: SendCmnd"));
+// AddLog(LOG_LEVEL_DEBUG, PSTR("TCL: SendCmnd"));
// AddLogBuffer(LOG_LEVEL_DEBUG, (uint8_t*)&buffer, sizeof(buffer));
for (uint32_t ca = 0; ca < sizeof(buffer); ca++) {
diff --git a/tasmota/xdrv_33_nrf24l01.ino b/tasmota/xdrv_33_nrf24l01.ino
index e992c398b..dfe690de2 100644
--- a/tasmota/xdrv_33_nrf24l01.ino
+++ b/tasmota/xdrv_33_nrf24l01.ino
@@ -67,10 +67,10 @@ void NRF24Detect(void) {
if (PinUsed(GPIO_NRF24_CS) && PinUsed(GPIO_NRF24_DC) && TasmotaGlobal.spi_enabled) {
if (NRF24initRadio()) {
NRF24.chipType = 32; // SPACE
- AddLog_P(LOG_LEVEL_INFO, PSTR("NRF: Model 24L01 initialized"));
+ AddLog(LOG_LEVEL_INFO, PSTR("NRF: Model 24L01 initialized"));
if (NRF24radio.isPVariant()) {
NRF24.chipType = 43; // +
- AddLog_P(LOG_LEVEL_INFO, PSTR("NRF: Model 24L01+ detected"));
+ AddLog(LOG_LEVEL_INFO, PSTR("NRF: Model 24L01+ detected"));
}
}
}
diff --git a/tasmota/xdrv_35_pwm_dimmer.ino b/tasmota/xdrv_35_pwm_dimmer.ino
index 3bca88477..3bce167a2 100644
--- a/tasmota/xdrv_35_pwm_dimmer.ino
+++ b/tasmota/xdrv_35_pwm_dimmer.ino
@@ -254,6 +254,12 @@ void PWMDimmerHandleDevGroupItem(void)
#endif // USE_PWM_DIMMER_REMOTE
SendLocalDeviceGroupMessage(DGR_MSGTYP_UPDATE, DGR_ITEM_BRI_POWER_ON, Settings.bri_power_on,
DGR_ITEM_BRI_PRESET_LOW, Settings.bri_preset_low, DGR_ITEM_BRI_PRESET_HIGH, Settings.bri_preset_high);
+#ifdef USE_PWM_DIMMER_REMOTE
+ else
+ SendDeviceGroupMessage(device_group_index, DGR_MSGTYP_UPDATE, DGR_ITEM_POWER, remote_pwm_dimmer->power_on,
+ DGR_ITEM_BRI_POWER_ON, remote_pwm_dimmer->bri_power_on, DGR_ITEM_BRI_PRESET_LOW, remote_pwm_dimmer->bri_preset_low,
+ DGR_ITEM_BRI_PRESET_HIGH, remote_pwm_dimmer->bri_preset_high);
+#endif // USE_PWM_DIMMER_REMOTE
break;
}
}
@@ -613,13 +619,13 @@ void PWMDimmerHandleButton(uint32_t button_index, bool pressed)
if (mqtt_trigger) {
char topic[TOPSZ];
sprintf_P(TasmotaGlobal.mqtt_data, PSTR("Trigger%u"), mqtt_trigger);
-#ifdef USE_PWM_DIMMER_REMOTE
- if (active_remote_pwm_dimmer) {
+#ifdef USE_DEVICE_GROUPS
+ if (Settings.flag4.device_groups_enabled) {
snprintf_P(topic, sizeof(topic), PSTR("cmnd/%s/EVENT"), device_groups[power_button_index].group_name);
MqttPublish(topic);
}
else
-#endif // USE_PWM_DIMMER_REMOTE
+#endif // USE_DEVICE_GROUPS
MqttPublishPrefixTopic_P(CMND, PSTR("EVENT"));
}
diff --git a/tasmota/xdrv_36_keeloq.ino b/tasmota/xdrv_36_keeloq.ino
index 4a0f33a83..0703190ca 100644
--- a/tasmota/xdrv_36_keeloq.ino
+++ b/tasmota/xdrv_36_keeloq.ino
@@ -97,7 +97,7 @@ void GenerateDeviceCryptKey()
jaroliftDevice.device_key_msb = k.decrypt(jaroliftDevice.serial | 0x60000000L);
jaroliftDevice.device_key_lsb = k.decrypt(jaroliftDevice.serial | 0x20000000L);
- AddLog_P(LOG_LEVEL_DEBUG, PSTR("generated device keys: %08x %08x"), jaroliftDevice.device_key_msb, jaroliftDevice.device_key_lsb);
+ AddLog(LOG_LEVEL_DEBUG, PSTR("generated device keys: %08x %08x"), jaroliftDevice.device_key_msb, jaroliftDevice.device_key_lsb);
}
void CmdSendButton(void)
@@ -114,7 +114,7 @@ void CmdSendButton(void)
DEBUG_DRIVER_LOG(LOG_LEVEL_DEBUG_MORE, PSTR("lsb: %08x"), jaroliftDevice.device_key_lsb);
DEBUG_DRIVER_LOG(LOG_LEVEL_DEBUG_MORE, PSTR("serial: %08x"), jaroliftDevice.serial);
DEBUG_DRIVER_LOG(LOG_LEVEL_DEBUG_MORE, PSTR("disc: %08x"), jaroliftDevice.disc);
- AddLog_P(LOG_LEVEL_DEBUG, PSTR("KLQ: count: %08x"), jaroliftDevice.count);
+ AddLog(LOG_LEVEL_DEBUG, PSTR("KLQ: count: %08x"), jaroliftDevice.count);
CreateKeeloqPacket();
jaroliftDevice.count++;
@@ -236,8 +236,8 @@ void CreateKeeloqPacket()
jaroliftDevice.enc = k.encrypt(result);
jaroliftDevice.pack |= jaroliftDevice.enc;
- AddLog_P(LOG_LEVEL_DEBUG_MORE, PSTR("pack high: %08x"), jaroliftDevice.pack>>32);
- AddLog_P(LOG_LEVEL_DEBUG_MORE, PSTR("pack low: %08x"), jaroliftDevice.pack);
+ AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("pack high: %08x"), jaroliftDevice.pack>>32);
+ AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("pack low: %08x"), jaroliftDevice.pack);
}
void KeeloqInit()
@@ -248,7 +248,7 @@ void KeeloqInit()
DEBUG_DRIVER_LOG(LOG_LEVEL_DEBUG_MORE, PSTR("cc1101.init()"));
delay(100);
cc1101.init();
- AddLog_P(LOG_LEVEL_DEBUG_MORE, PSTR("CC1101 done."));
+ AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("CC1101 done."));
cc1101.setSyncWord(SYNC_WORD, false);
cc1101.setCarrierFreq(CFREQ_433);
cc1101.disableAddressCheck();
@@ -272,7 +272,7 @@ bool Xdrv36(uint8_t function)
switch (function) {
case FUNC_COMMAND:
- AddLog_P(LOG_LEVEL_DEBUG_MORE, PSTR("calling command"));
+ AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("calling command"));
result = DecodeCommand(kJaroliftCommands, jaroliftCommand);
break;
case FUNC_INIT:
diff --git a/tasmota/xdrv_37_sonoff_d1.ino b/tasmota/xdrv_37_sonoff_d1.ino
index 5abefd452..435bd28e5 100644
--- a/tasmota/xdrv_37_sonoff_d1.ino
+++ b/tasmota/xdrv_37_sonoff_d1.ino
@@ -50,7 +50,7 @@ void SonoffD1Received(void)
if (action != SnfD1.power) {
SnfD1.power = action;
-// AddLog_P(LOG_LEVEL_DEBUG, PSTR("SD1: Remote power (%d, %d)"), SnfD1.power, SnfD1.dimmer);
+// AddLog(LOG_LEVEL_DEBUG, PSTR("SD1: Remote power (%d, %d)"), SnfD1.power, SnfD1.dimmer);
ExecuteCommandPower(1, action, SRC_SWITCH);
}
@@ -59,7 +59,7 @@ void SonoffD1Received(void)
if (dimmer != SnfD1.dimmer) {
SnfD1.dimmer = dimmer;
-// AddLog_P(LOG_LEVEL_DEBUG, PSTR("SD1: Remote dimmer (%d, %d)"), SnfD1.power, SnfD1.dimmer);
+// AddLog(LOG_LEVEL_DEBUG, PSTR("SD1: Remote dimmer (%d, %d)"), SnfD1.power, SnfD1.dimmer);
char scmnd[20];
snprintf_P(scmnd, sizeof(scmnd), PSTR(D_CMND_DIMMER " %d"), SnfD1.dimmer);
@@ -137,7 +137,7 @@ bool SonoffD1SendPower(void)
if (action != SnfD1.power) {
SnfD1.power = action;
-// AddLog_P(LOG_LEVEL_DEBUG, PSTR("SD1: Tasmota power (%d, %d)"), SnfD1.power, SnfD1.dimmer);
+// AddLog(LOG_LEVEL_DEBUG, PSTR("SD1: Tasmota power (%d, %d)"), SnfD1.power, SnfD1.dimmer);
SonoffD1Send();
}
@@ -152,7 +152,7 @@ bool SonoffD1SendDimmer(void)
if (dimmer != SnfD1.dimmer) {
SnfD1.dimmer = dimmer;
-// AddLog_P(LOG_LEVEL_DEBUG, PSTR("SD1: Tasmota dimmer (%d, %d)"), SnfD1.power, SnfD1.dimmer);
+// AddLog(LOG_LEVEL_DEBUG, PSTR("SD1: Tasmota dimmer (%d, %d)"), SnfD1.power, SnfD1.dimmer);
SonoffD1Send();
}
diff --git a/tasmota/xdrv_38_ping.ino b/tasmota/xdrv_38_ping.ino
index 3f87a54bb..0974697fe 100644
--- a/tasmota/xdrv_38_ping.ino
+++ b/tasmota/xdrv_38_ping.ino
@@ -303,7 +303,7 @@ void PingResponsePoll(void) {
",\"AvgTime\":%d"
"}}}"),
ping->hostname.c_str(),
- success ? "true" : "false",
+ success ? PSTR("true") : PSTR("false"),
ip & 0xFF, (ip >> 8) & 0xFF, (ip >> 16) & 0xFF, ip >> 24,
success,
ping->timeout_count,
diff --git a/tasmota/xdrv_39_thermostat.ino b/tasmota/xdrv_39_thermostat.ino
index 455e45f13..2078fd829 100644
--- a/tasmota/xdrv_39_thermostat.ino
+++ b/tasmota/xdrv_39_thermostat.ino
@@ -1272,58 +1272,58 @@ void ThermostatVirtualSwitchCtrState(uint8_t ctr_output)
void ThermostatDebug(uint8_t ctr_output)
{
char result_chr[FLOATSZ];
- AddLog_P(LOG_LEVEL_DEBUG, PSTR(""));
- AddLog_P(LOG_LEVEL_DEBUG, PSTR("------ Thermostat Start ------"));
+ AddLog(LOG_LEVEL_DEBUG, PSTR(""));
+ AddLog(LOG_LEVEL_DEBUG, PSTR("------ Thermostat Start ------"));
dtostrfd(Thermostat[ctr_output].status.counter_seconds, 0, result_chr);
- AddLog_P(LOG_LEVEL_DEBUG, PSTR("Thermostat[ctr_output].status.counter_seconds: %s"), result_chr);
+ AddLog(LOG_LEVEL_DEBUG, PSTR("Thermostat[ctr_output].status.counter_seconds: %s"), result_chr);
dtostrfd(Thermostat[ctr_output].status.thermostat_mode, 0, result_chr);
- AddLog_P(LOG_LEVEL_DEBUG, PSTR("Thermostat[ctr_output].status.thermostat_mode: %s"), result_chr);
+ AddLog(LOG_LEVEL_DEBUG, PSTR("Thermostat[ctr_output].status.thermostat_mode: %s"), result_chr);
dtostrfd(Thermostat[ctr_output].diag.state_emergency, 0, result_chr);
- AddLog_P(LOG_LEVEL_DEBUG, PSTR("Thermostat[ctr_output].diag.state_emergency: %s"), result_chr);
+ AddLog(LOG_LEVEL_DEBUG, PSTR("Thermostat[ctr_output].diag.state_emergency: %s"), result_chr);
dtostrfd(Thermostat[ctr_output].diag.output_inconsist_ctr, 0, result_chr);
- AddLog_P(LOG_LEVEL_DEBUG, PSTR("Thermostat[ctr_output].diag.output_inconsist_ctr: %s"), result_chr);
+ AddLog(LOG_LEVEL_DEBUG, PSTR("Thermostat[ctr_output].diag.output_inconsist_ctr: %s"), result_chr);
dtostrfd(Thermostat[ctr_output].status.controller_mode, 0, result_chr);
- AddLog_P(LOG_LEVEL_DEBUG, PSTR("Thermostat[ctr_output].status.controller_mode: %s"), result_chr);
+ AddLog(LOG_LEVEL_DEBUG, PSTR("Thermostat[ctr_output].status.controller_mode: %s"), result_chr);
dtostrfd(Thermostat[ctr_output].status.command_output, 0, result_chr);
- AddLog_P(LOG_LEVEL_DEBUG, PSTR("Thermostat[ctr_output].status.command_output: %s"), result_chr);
+ AddLog(LOG_LEVEL_DEBUG, PSTR("Thermostat[ctr_output].status.command_output: %s"), result_chr);
dtostrfd(Thermostat[ctr_output].status.status_output, 0, result_chr);
- AddLog_P(LOG_LEVEL_DEBUG, PSTR("Thermostat[ctr_output].status.status_output: %s"), result_chr);
+ AddLog(LOG_LEVEL_DEBUG, PSTR("Thermostat[ctr_output].status.status_output: %s"), result_chr);
dtostrfd(Thermostat[ctr_output].status.status_input, 0, result_chr);
- AddLog_P(LOG_LEVEL_DEBUG, PSTR("Thermostat[ctr_output].status.status_input: %s"), result_chr);
+ AddLog(LOG_LEVEL_DEBUG, PSTR("Thermostat[ctr_output].status.status_input: %s"), result_chr);
dtostrfd(Thermostat[ctr_output].status.phase_hybrid_ctr, 0, result_chr);
- AddLog_P(LOG_LEVEL_DEBUG, PSTR("Thermostat[ctr_output].status.phase_hybrid_ctr: %s"), result_chr);
+ AddLog(LOG_LEVEL_DEBUG, PSTR("Thermostat[ctr_output].status.phase_hybrid_ctr: %s"), result_chr);
dtostrfd(Thermostat[ctr_output].status.sensor_alive, 0, result_chr);
- AddLog_P(LOG_LEVEL_DEBUG, PSTR("Thermostat[ctr_output].status.sensor_alive: %s"), result_chr);
+ AddLog(LOG_LEVEL_DEBUG, PSTR("Thermostat[ctr_output].status.sensor_alive: %s"), result_chr);
dtostrfd(Thermostat[ctr_output].status.status_cycle_active, 0, result_chr);
- AddLog_P(LOG_LEVEL_DEBUG, PSTR("Thermostat[ctr_output].status.status_cycle_active: %s"), result_chr);
+ AddLog(LOG_LEVEL_DEBUG, PSTR("Thermostat[ctr_output].status.status_cycle_active: %s"), result_chr);
dtostrfd(Thermostat[ctr_output].temp_pi_error, 0, result_chr);
- AddLog_P(LOG_LEVEL_DEBUG, PSTR("Thermostat[ctr_output].temp_pi_error: %s"), result_chr);
+ AddLog(LOG_LEVEL_DEBUG, PSTR("Thermostat[ctr_output].temp_pi_error: %s"), result_chr);
dtostrfd(Thermostat[ctr_output].temp_pi_accum_error, 0, result_chr);
- AddLog_P(LOG_LEVEL_DEBUG, PSTR("Thermostat[ctr_output].temp_pi_accum_error: %s"), result_chr);
+ AddLog(LOG_LEVEL_DEBUG, PSTR("Thermostat[ctr_output].temp_pi_accum_error: %s"), result_chr);
dtostrfd(Thermostat[ctr_output].time_proportional_pi, 0, result_chr);
- AddLog_P(LOG_LEVEL_DEBUG, PSTR("Thermostat[ctr_output].time_proportional_pi: %s"), result_chr);
+ AddLog(LOG_LEVEL_DEBUG, PSTR("Thermostat[ctr_output].time_proportional_pi: %s"), result_chr);
dtostrfd(Thermostat[ctr_output].time_integral_pi, 0, result_chr);
- AddLog_P(LOG_LEVEL_DEBUG, PSTR("Thermostat[ctr_output].time_integral_pi: %s"), result_chr);
+ AddLog(LOG_LEVEL_DEBUG, PSTR("Thermostat[ctr_output].time_integral_pi: %s"), result_chr);
dtostrfd(Thermostat[ctr_output].time_total_pi, 0, result_chr);
- AddLog_P(LOG_LEVEL_DEBUG, PSTR("Thermostat[ctr_output].time_total_pi: %s"), result_chr);
+ AddLog(LOG_LEVEL_DEBUG, PSTR("Thermostat[ctr_output].time_total_pi: %s"), result_chr);
dtostrfd(Thermostat[ctr_output].temp_measured_gradient, 0, result_chr);
- AddLog_P(LOG_LEVEL_DEBUG, PSTR("Thermostat[ctr_output].temp_measured_gradient: %s"), result_chr);
+ AddLog(LOG_LEVEL_DEBUG, PSTR("Thermostat[ctr_output].temp_measured_gradient: %s"), result_chr);
dtostrfd(Thermostat[ctr_output].time_rampup_deadtime, 0, result_chr);
- AddLog_P(LOG_LEVEL_DEBUG, PSTR("Thermostat[ctr_output].time_rampup_deadtime: %s"), result_chr);
+ AddLog(LOG_LEVEL_DEBUG, PSTR("Thermostat[ctr_output].time_rampup_deadtime: %s"), result_chr);
dtostrfd(Thermostat[ctr_output].temp_rampup_meas_gradient, 0, result_chr);
- AddLog_P(LOG_LEVEL_DEBUG, PSTR("Thermostat[ctr_output].temp_rampup_meas_gradient: %s"), result_chr);
+ AddLog(LOG_LEVEL_DEBUG, PSTR("Thermostat[ctr_output].temp_rampup_meas_gradient: %s"), result_chr);
dtostrfd(Thermostat[ctr_output].time_ctr_changepoint, 0, result_chr);
- AddLog_P(LOG_LEVEL_DEBUG, PSTR("Thermostat[ctr_output].time_ctr_changepoint: %s"), result_chr);
+ AddLog(LOG_LEVEL_DEBUG, PSTR("Thermostat[ctr_output].time_ctr_changepoint: %s"), result_chr);
dtostrfd(Thermostat[ctr_output].temp_rampup_output_off, 0, result_chr);
- AddLog_P(LOG_LEVEL_DEBUG, PSTR("Thermostat[ctr_output].temp_rampup_output_off: %s"), result_chr);
+ AddLog(LOG_LEVEL_DEBUG, PSTR("Thermostat[ctr_output].temp_rampup_output_off: %s"), result_chr);
dtostrfd(Thermostat[ctr_output].time_ctr_checkpoint, 0, result_chr);
- AddLog_P(LOG_LEVEL_DEBUG, PSTR("Thermostat[ctr_output].time_ctr_checkpoint: %s"), result_chr);
+ AddLog(LOG_LEVEL_DEBUG, PSTR("Thermostat[ctr_output].time_ctr_checkpoint: %s"), result_chr);
dtostrfd(TasmotaGlobal.uptime, 0, result_chr);
- AddLog_P(LOG_LEVEL_DEBUG, PSTR("uptime: %s"), result_chr);
+ AddLog(LOG_LEVEL_DEBUG, PSTR("uptime: %s"), result_chr);
dtostrfd(TasmotaGlobal.power, 0, result_chr);
- AddLog_P(LOG_LEVEL_DEBUG, PSTR("power: %s"), result_chr);
- AddLog_P(LOG_LEVEL_DEBUG, PSTR("------ Thermostat End ------"));
- AddLog_P(LOG_LEVEL_DEBUG, PSTR(""));
+ AddLog(LOG_LEVEL_DEBUG, PSTR("power: %s"), result_chr);
+ AddLog(LOG_LEVEL_DEBUG, PSTR("------ Thermostat End ------"));
+ AddLog(LOG_LEVEL_DEBUG, PSTR(""));
}
#endif // DEBUG_THERMOSTAT
diff --git a/tasmota/xdrv_40_telegram.ino b/tasmota/xdrv_40_telegram.ino
index ec230e792..3e4cc6f0a 100644
--- a/tasmota/xdrv_40_telegram.ino
+++ b/tasmota/xdrv_40_telegram.ino
@@ -99,7 +99,7 @@ bool TelegramInit(void) {
Telegram.next_update_id = 0; // Code of last read Message
Telegram.message[0].text = "";
- AddLog_P(LOG_LEVEL_INFO, PSTR("TGM: Started"));
+ AddLog(LOG_LEVEL_INFO, PSTR("TGM: Started"));
}
init_done = true;
}
@@ -115,7 +115,7 @@ String TelegramConnectToTelegram(String command) {
uint32_t tls_connect_time = millis();
if (telegramClient->connect("api.telegram.org", 443)) {
-// AddLog_P(LOG_LEVEL_DEBUG, PSTR("TGM: Connected in %d ms, max ThunkStack used %d"), millis() - tls_connect_time, telegramClient->getMaxThunkStackUse());
+// AddLog(LOG_LEVEL_DEBUG, PSTR("TGM: Connected in %d ms, max ThunkStack used %d"), millis() - tls_connect_time, telegramClient->getMaxThunkStackUse());
telegramClient->println("GET /"+command);
@@ -144,7 +144,7 @@ String TelegramConnectToTelegram(String command) {
}
void TelegramGetUpdates(uint32_t offset) {
- AddLog_P(LOG_LEVEL_DEBUG_MORE, PSTR("TGM: getUpdates"));
+ AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("TGM: getUpdates"));
if (!TelegramInit()) { return; }
@@ -241,15 +241,15 @@ void TelegramGetUpdates(uint32_t offset) {
AddLog_P(LOG_LEVEL_DEBUG_MORE, PSTR("TGM: Parsed update_id %d, chat_id %d, text \"%s\""), Telegram.message[i].update_id, Telegram.message[i].chat_id, Telegram.message[i].text.c_str());
}
} else {
-// AddLog_P(LOG_LEVEL_DEBUG, PSTR("TGM: No new messages"));
+// AddLog(LOG_LEVEL_DEBUG, PSTR("TGM: No new messages"));
}
} else {
-// AddLog_P(LOG_LEVEL_DEBUG, PSTR("TGM: Failed to update"));
+// AddLog(LOG_LEVEL_DEBUG, PSTR("TGM: Failed to update"));
}
}
bool TelegramSendMessage(int32_t chat_id, String text) {
- AddLog_P(LOG_LEVEL_DEBUG_MORE, PSTR("TGM: sendMessage"));
+ AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("TGM: sendMessage"));
if (!TelegramInit()) { return false; }
@@ -262,7 +262,7 @@ bool TelegramSendMessage(int32_t chat_id, String text) {
// AddLog_P(LOG_LEVEL_DEBUG_MORE, PSTR("TGM: Response %s"), response.c_str());
if (response.startsWith("{\"ok\":true")) {
-// AddLog_P(LOG_LEVEL_DEBUG, PSTR("TGM: Message sent"));
+// AddLog(LOG_LEVEL_DEBUG, PSTR("TGM: Message sent"));
sent = true;
}
}
@@ -272,7 +272,7 @@ bool TelegramSendMessage(int32_t chat_id, String text) {
/*
void TelegramSendGetMe(void) {
- AddLog_P(LOG_LEVEL_DEBUG_MORE, PSTR("TGM: getMe"));
+ AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("TGM: getMe"));
if (!TelegramInit()) { return; }
@@ -434,14 +434,14 @@ void CmndTmChatId(void) {
void CmndTmSend(void) {
if (!Telegram.send_enable || !strlen(SettingsText(SET_TELEGRAM_CHATID))) {
- ResponseCmndChar(D_JSON_FAILED);
+ ResponseCmndChar(PSTR(D_JSON_FAILED));
return;
}
if (XdrvMailbox.data_len > 0) {
String message = XdrvMailbox.data;
String chat_id = SettingsText(SET_TELEGRAM_CHATID);
if (!TelegramSendMessage(chat_id.toInt(), message)) {
- ResponseCmndChar(D_JSON_FAILED);
+ ResponseCmndChar(PSTR(D_JSON_FAILED));
return;
}
}
diff --git a/tasmota/xdrv_41_tcp_bridge.ino b/tasmota/xdrv_41_tcp_bridge.ino
index 7533e7d6d..aeadfde61 100644
--- a/tasmota/xdrv_41_tcp_bridge.ino
+++ b/tasmota/xdrv_41_tcp_bridge.ino
@@ -90,9 +90,7 @@ void TCPLoop(void)
}
}
if (buf_len > 0) {
- char hex_char[TCP_BRIDGE_BUF_SIZE+1];
- ToHex_P(tcp_buf, buf_len, hex_char, 256);
- AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_TCP "from MCU: %s"), hex_char);
+ AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_TCP "from MCU: %*_H"), buf_len, tcp_buf);
for (uint32_t i=0; i 0) {
- char hex_char[TCP_BRIDGE_BUF_SIZE+1];
- ToHex_P(tcp_buf, buf_len, hex_char, 256);
- AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_TCP "to MCU/%d: %s"), i+1, hex_char);
+ AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_TCP "to MCU/%d: %*_H"), i+1, buf_len, tcp_buf);
TCPSerial->write(tcp_buf, buf_len);
}
}
@@ -127,7 +123,7 @@ void TCPLoop(void)
void TCPInit(void) {
if (PinUsed(GPIO_TCP_RX) && PinUsed(GPIO_TCP_TX)) {
tcp_buf = (uint8_t*) malloc(TCP_BRIDGE_BUF_SIZE);
- if (!tcp_buf) { AddLog_P(LOG_LEVEL_ERROR, PSTR(D_LOG_TCP "could not allocate buffer")); return; }
+ if (!tcp_buf) { AddLog(LOG_LEVEL_ERROR, PSTR(D_LOG_TCP "could not allocate buffer")); return; }
if (!Settings.tcp_baudrate) { Settings.tcp_baudrate = 115200 / 1200; }
TCPSerial = new TasmotaSerial(Pin(GPIO_TCP_RX), Pin(GPIO_TCP_TX), TasmotaGlobal.seriallog_level ? 1 : 2, 0, TCP_BRIDGE_BUF_SIZE); // set a receive buffer of 256 bytes
@@ -151,7 +147,7 @@ void CmndTCPStart(void) {
int32_t tcp_port = XdrvMailbox.payload;
if (server_tcp) {
- AddLog_P(LOG_LEVEL_INFO, PSTR(D_LOG_TCP "Stopping TCP server"));
+ AddLog(LOG_LEVEL_INFO, PSTR(D_LOG_TCP "Stopping TCP server"));
server_tcp->stop();
delete server_tcp;
server_tcp = nullptr;
@@ -162,7 +158,7 @@ void CmndTCPStart(void) {
}
}
if (tcp_port > 0) {
- AddLog_P(LOG_LEVEL_INFO, PSTR(D_LOG_TCP "Starting TCP server on port %d"), tcp_port);
+ AddLog(LOG_LEVEL_INFO, PSTR(D_LOG_TCP "Starting TCP server on port %d"), tcp_port);
server_tcp = new WiFiServer(tcp_port);
server_tcp->begin(); // start TCP server
server_tcp->setNoDelay(true);
diff --git a/tasmota/xdrv_42_i2s_audio.ino b/tasmota/xdrv_42_i2s_audio.ino
index 0567e3591..359308cba 100644
--- a/tasmota/xdrv_42_i2s_audio.ino
+++ b/tasmota/xdrv_42_i2s_audio.ino
@@ -94,7 +94,7 @@ uint32_t retryms = 0;
#ifdef SAY_TIME
AudioGeneratorTalkie *talkie = nullptr;
-#endif
+#endif // SAY_TIME
//! MAX98357A + INMP441 DOUBLE I2S BOARD
#ifdef ESP8266
@@ -111,22 +111,22 @@ AudioGeneratorTalkie *talkie = nullptr;
#ifndef DAC_IIS_BCK
#undef DAC_IIS_BCK
#define DAC_IIS_BCK 26
-#endif
+#endif // DAC_IIS_BCK
#ifndef DAC_IIS_WS
#undef DAC_IIS_WS
#define DAC_IIS_WS 25
-#endif
+#endif // DAC_IIS_WS
#ifndef DAC_IIS_DOUT
#undef DAC_IIS_DOUT
#define DAC_IIS_DOUT 33
-#endif
+#endif // DAC_IIS_DOUT
#ifndef DAC_IIS_DIN
#undef DAC_IIS_DIN
#define DAC_IIS_DIN 34
-#endif
+#endif // DAC_IIS_DIN
#endif // ESP32
@@ -229,7 +229,7 @@ void sayTime(int hour, int minutes, AudioGeneratorTalkie *talkie) {
out->stop();
AUDIO_PWR_OFF
}
-#endif
+#endif // SAY_TIME
// should be in settings
uint8_t is2_volume;
@@ -379,10 +379,10 @@ uint32_t i2s_record(char *path, uint32_t secs) {
if (data_offset >= mic_size-DATA_SIZE) break;
delay(0);
}
- //AddLog_P(LOG_LEVEL_INFO, PSTR("rectime: %d ms"), millis()-stime);
+ //AddLog(LOG_LEVEL_INFO, PSTR("rectime: %d ms"), millis()-stime);
SpeakerMic(MODE_SPK);
// save to path
- SaveWav(mic_path, mic_buff, mic_size);
+ SaveWav(path, mic_buff, mic_size);
free(mic_buff);
return 0;
}
@@ -393,7 +393,8 @@ static const uint8_t wavHTemplate[] PROGMEM = { // Hardcoded simple WAV header w
0x64, 0x61, 0x74, 0x61, 0xff, 0xff, 0xff, 0xff };
bool SaveWav(char *path, uint8_t *buff, uint32_t size) {
- File fwp = ufsp->open(path, FILE_WRITE);
+ File fwp = ufsp->open(path, "w");
+ if (!fwp) return false;
uint8_t wavHeader[sizeof(wavHTemplate)];
memcpy_P(wavHeader, wavHTemplate, sizeof(wavHTemplate));
@@ -446,7 +447,7 @@ void mp3_task(void *arg) {
}
}
}
-#endif
+#endif // ESP32
#ifdef USE_WEBRADIO
void MDCallback(void *cbData, const char *type, bool isUnicode, const char *str) {
@@ -456,7 +457,7 @@ void MDCallback(void *cbData, const char *type, bool isUnicode, const char *str)
if (strstr_P(type, PSTR("Title"))) {
strncpy(wr_title, str, sizeof(wr_title));
wr_title[sizeof(wr_title)-1] = 0;
- //AddLog_P(LOG_LEVEL_INFO,PSTR("WR-Title: %s"),wr_title);
+ //AddLog(LOG_LEVEL_INFO,PSTR("WR-Title: %s"),wr_title);
} else {
// Who knows what to do? Not me!
}
@@ -541,6 +542,23 @@ void Cmd_WebRadio(void) {
}
+#ifdef USE_M5STACK_CORE2
+void Cmd_MicRec(void) {
+ if (XdrvMailbox.data_len > 0) {
+ uint16 time = 10;
+ char *cp = strchr(XdrvMailbox.data, ':');
+ if (cp) {
+ time = atoi(cp + 1);
+ *cp = 0;
+ }
+ if (time<10) time = 10;
+ if (time>30) time = 30;
+ i2s_record(XdrvMailbox.data, time);
+ ResponseCmndChar(XdrvMailbox.data);
+ }
+}
+#endif // USE_M5STACK_CORE2
+
const char HTTP_WEBRADIO[] PROGMEM =
"{s}" "I2S_WR-Title" "{m}%s{e}";
@@ -554,7 +572,7 @@ void I2S_WR_Show(void) {
#ifdef ESP32
void Play_mp3(const char *path) {
-#if (defined(USE_SCRIPT_FATFS) && defined(USE_SCRIPT)) || defined(UFILESYSTEM)
+#if (defined(USE_SCRIPT_FATFS) && defined(USE_SCRIPT)) || defined(USE_UFILESYS)
if (decoder || mp3) return;
if (!out) return;
@@ -626,8 +644,11 @@ const char kI2SAudio_Commands[] PROGMEM = "I2S|"
"|Play"
#ifdef USE_WEBRADIO
"|WR"
-#endif
-#endif
+#endif // USE_WEBRADIO
+#ifdef USE_M5STACK_CORE2
+ "|REC"
+#endif // USE_M5STACK_CORE2
+#endif // ESP32
;
void (* const I2SAudio_Command[])(void) PROGMEM = {
@@ -636,8 +657,11 @@ void (* const I2SAudio_Command[])(void) PROGMEM = {
,&Cmd_Play
#ifdef USE_WEBRADIO
,&Cmd_WebRadio
-#endif
-#endif
+#endif // USE_WEBRADIO
+#ifdef USE_M5STACK_CORE2
+ ,&Cmd_MicRec
+#endif // USE_M5STACK_CORE2
+#endif // ESP32
};
@@ -669,7 +693,7 @@ void Cmd_Say(void) {
void Cmd_Time(void) {
#ifdef SAY_TIME
sayTime(RtcTime.hour, RtcTime.minute, talkie);
-#endif
+#endif // SAY_TIME
ResponseCmndDone();
}
@@ -692,8 +716,8 @@ bool Xdrv42(uint8_t function) {
case FUNC_WEB_SENSOR:
I2S_WR_Show();
break;
-#endif
-#endif
+#endif // USE_WEBRADIO
+#endif // USE_WEBSERVER
}
return result;
}
diff --git a/tasmota/xdrv_43_mlx90640.ino b/tasmota/xdrv_43_mlx90640.ino
index 53cbcfe07..ea2b60f43 100644
--- a/tasmota/xdrv_43_mlx90640.ino
+++ b/tasmota/xdrv_43_mlx90640.ino
@@ -413,7 +413,7 @@ void MLX90640HandleWebGuiResponse(void){
WebGetArg("ul", tmp, sizeof(tmp)); // update line
if (strlen(tmp)) {
uint8_t _line = atoi(tmp);
- // AddLog_P(LOG_LEVEL_DEBUG, "MLX90640: send line %u", _line);
+ // AddLog(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;}
@@ -431,9 +431,9 @@ void MLX90640HandleWebGuiResponse(void){
uint32_t _poiNum = (_poi-(_poi%10000))/10000;
MLX90640.pois[_poiNum*2] = (_poi%10000)/100;
MLX90640.pois[(_poiNum*2)+1] = _poi%100;
- // AddLog_P(LOG_LEVEL_DEBUG, PSTR("RAW: %u, POI-%u: x: %u, y: %u"),_poi,_poiNum,MLX90640.pois[_poiNum],MLX90640.pois[_poiNum+1]);
+ // AddLog(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;i23) MLX90640.pois[_idx+1]=23;
}
- AddLog_P(LOG_LEVEL_INFO, PSTR("POI-%u = x:%u,y:%u"),XdrvMailbox.index,MLX90640.pois[_idx],MLX90640.pois[_idx+1]);
+ AddLog(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:
@@ -494,10 +494,10 @@ void MLX90640init()
if(!MLX90640.dumpedEE){
status = MLX90640_DumpEE(MLX90640_ADDRESS, MLX90640.Frame);
if (status != 0){
- AddLog_P(LOG_LEVEL_INFO, PSTR("Failed to load system parameters"));
+ AddLog(LOG_LEVEL_INFO, PSTR("Failed to load system parameters"));
}
else {
- AddLog_P(LOG_LEVEL_INFO, PSTR("MLX90640: started"));
+ AddLog(LOG_LEVEL_INFO, PSTR("MLX90640: started"));
MLX90640.type = true;
}
MLX90640.params = new paramsMLX90640;
@@ -514,11 +514,11 @@ void MLX90640every100msec(){
if(!MLX90640.extractedParams){
static uint32_t _chunk = 0;
- AddLog_P(LOG_LEVEL_DEBUG, PSTR("MLX90640: will read chunk: %u"), _chunk);
+ AddLog(LOG_LEVEL_DEBUG, PSTR("MLX90640: will read chunk: %u"), _chunk);
_time = millis();
status = MLX90640_ExtractParameters(MLX90640.Frame, MLX90640.params, _chunk);
if (status == 0){
- AddLog_P(LOG_LEVEL_DEBUG, PSTR("MLX90640: parameter received after: %u msec, status: %u"), TimePassedSince(_time), status);
+ AddLog(LOG_LEVEL_DEBUG, PSTR("MLX90640: parameter received after: %u msec, status: %u"), TimePassedSince(_time), status);
}
if (_chunk == 5) MLX90640.extractedParams = true;
_chunk++;
@@ -529,12 +529,12 @@ void MLX90640every100msec(){
case 0:
if(MLX90640_SynchFrame(MLX90640_ADDRESS)!=0){
_job=-1;
- AddLog_P(LOG_LEVEL_DEBUG, PSTR("MLX90640: frame not ready"));
+ AddLog(LOG_LEVEL_DEBUG, PSTR("MLX90640: frame not ready"));
break;
}
// _time = millis();
status = MLX90640_GetFrameData(MLX90640_ADDRESS, MLX90640.Frame);
- // AddLog_P(LOG_LEVEL_DEBUG, PSTR("MLX90640: got frame 0 in %u msecs, status: %i"), TimePassedSince(_time), status);
+ // AddLog(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);
@@ -542,7 +542,7 @@ void MLX90640every100msec(){
case 2:
// _time = millis();
MLX90640_CalculateTo(MLX90640.Frame, MLX90640.params, 0.95f, MLX90640.Ta - 8, MLX90640.To, 0);
- // AddLog_P(LOG_LEVEL_DEBUG, PSTR("MLX90640: calculated temperatures in %u msecs"), TimePassedSince(_time));
+ // AddLog(LOG_LEVEL_DEBUG, PSTR("MLX90640: calculated temperatures in %u msecs"), TimePassedSince(_time));
break;
case 5:
if(MLX90640_SynchFrame(MLX90640_ADDRESS)!=0){
@@ -551,12 +551,12 @@ void MLX90640every100msec(){
}
// _time = millis();
status = MLX90640_GetFrameData(MLX90640_ADDRESS, MLX90640.Frame);
- // // AddLog_P(LOG_LEVEL_DEBUG, PSTR("MLX90640: got frame 1 in %u msecs, status: %i"), TimePassedSince(_time), status);
+ // // AddLog(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_P(LOG_LEVEL_DEBUG, PSTR("MLX90640: calculated temperatures in %u msecs"), TimePassedSince(_time));
+ // AddLog(LOG_LEVEL_DEBUG, PSTR("MLX90640: calculated temperatures in %u msecs"), TimePassedSince(_time));
break;
default:
break;
@@ -580,8 +580,8 @@ void MLX90640Show(uint8_t json)
char obj_tstr[FLOATSZ];
dtostrfd(MLX90640.To[MLX90640.pois[i*2]+(MLX90640.pois[(i*2)+1]*32)], Settings.flag2.temperature_resolution, obj_tstr);
ResponseAppend_P(PSTR(",%s"),obj_tstr);
- // AddLog_P(LOG_LEVEL_DEBUG, PSTR("Array pos: %u"),MLX90640.pois[i*2]+(MLX90640.pois[(i*2)+1]*32));
- AddLog_P(LOG_LEVEL_DEBUG, PSTR("POI-%u: x: %u, y: %u"),i+1,MLX90640.pois[i*2],MLX90640.pois[(i*2)+1]);
+ // AddLog(LOG_LEVEL_DEBUG, PSTR("Array pos: %u"),MLX90640.pois[i*2]+(MLX90640.pois[(i*2)+1]*32));
+ AddLog(LOG_LEVEL_DEBUG, PSTR("POI-%u: x: %u, y: %u"),i+1,MLX90640.pois[i*2],MLX90640.pois[(i*2)+1]);
}
ResponseAppend_P(PSTR("]}"));
}
diff --git a/tasmota/xdrv_44_miel_hvac.ino b/tasmota/xdrv_44_miel_hvac.ino
index 680d9d72f..189b00d1a 100644
--- a/tasmota/xdrv_44_miel_hvac.ino
+++ b/tasmota/xdrv_44_miel_hvac.ino
@@ -366,7 +366,7 @@ miel_hvac_parse(struct miel_hvac_softc *sc, uint8_t byte)
case MIEL_HVAC_P_MIDDLE1:
if (byte != MIEL_HVAC_H_MIDDLE1) {
- AddLog_P(LOG_LEVEL_DEBUG, PSTR(MIEL_HVAC_LOGNAME
+ AddLog(LOG_LEVEL_DEBUG, PSTR(MIEL_HVAC_LOGNAME
": parse state MIDDLE1 expected %02x got %02x"
", restarting"), MIEL_HVAC_H_MIDDLE1, byte);
return (MIEL_HVAC_P_START);
@@ -377,7 +377,7 @@ miel_hvac_parse(struct miel_hvac_softc *sc, uint8_t byte)
case MIEL_HVAC_P_MIDDLE2:
if (byte != MIEL_HVAC_H_MIDDLE2) {
- AddLog_P(LOG_LEVEL_DEBUG, PSTR(MIEL_HVAC_LOGNAME
+ AddLog(LOG_LEVEL_DEBUG, PSTR(MIEL_HVAC_LOGNAME
": parse state MIDDLE2 expected %02x got %02x"
", restarting"), MIEL_HVAC_H_MIDDLE2, byte);
return (MIEL_HVAC_P_START);
@@ -388,7 +388,7 @@ miel_hvac_parse(struct miel_hvac_softc *sc, uint8_t byte)
case MIEL_HVAC_P_LEN:
if (byte == 0) {
- AddLog_P(LOG_LEVEL_DEBUG, PSTR(MIEL_HVAC_LOGNAME
+ AddLog(LOG_LEVEL_DEBUG, PSTR(MIEL_HVAC_LOGNAME
": skipping 0 byte message type 0x%02x"),
p->p_type);
return (MIEL_HVAC_P_SKIP_CKSUM);
@@ -403,14 +403,14 @@ miel_hvac_parse(struct miel_hvac_softc *sc, uint8_t byte)
case MIEL_HVAC_H_TYPE_UPDATED:
break;
default:
- AddLog_P(LOG_LEVEL_DEBUG, PSTR(MIEL_HVAC_LOGNAME
+ AddLog(LOG_LEVEL_DEBUG, PSTR(MIEL_HVAC_LOGNAME
": skipping unknown message type 0x%02x"),
p->p_type);
return (MIEL_HVAC_P_SKIP);
}
if (byte > sizeof(p->p_data)) {
- AddLog_P(LOG_LEVEL_DEBUG, PSTR(MIEL_HVAC_LOGNAME
+ AddLog(LOG_LEVEL_DEBUG, PSTR(MIEL_HVAC_LOGNAME
": skipping %u data bytes of message type 0x%02x"),
p->p_len, p->p_type);
return (MIEL_HVAC_P_SKIP);
@@ -427,7 +427,7 @@ miel_hvac_parse(struct miel_hvac_softc *sc, uint8_t byte)
case MIEL_HVAC_P_CKSUM:
if (miel_hvac_cksum_fini(p->p_sum) != byte) {
- AddLog_P(LOG_LEVEL_DEBUG, PSTR(MIEL_HVAC_LOGNAME
+ AddLog(LOG_LEVEL_DEBUG, PSTR(MIEL_HVAC_LOGNAME
": checksum failed, restarting"));
return (MIEL_HVAC_P_START);
}
@@ -845,7 +845,7 @@ static void
miel_hvac_input_connected(struct miel_hvac_softc *sc,
const void *buf, size_t len)
{
- AddLog_P(LOG_LEVEL_INFO,
+ AddLog(LOG_LEVEL_INFO,
PSTR(MIEL_HVAC_LOGNAME ": connected to Mitsubishi Electric HVAC"));
sc->sc_connected = 1;
}
@@ -973,7 +973,7 @@ miel_hvac_input_data(struct miel_hvac_softc *sc,
miel_hvac_log_bytes(sc, "data", buf, len);
if (len < sizeof(*d)) {
- AddLog_P(LOG_LEVEL_DEBUG, PSTR(MIEL_HVAC_LOGNAME
+ AddLog(LOG_LEVEL_DEBUG, PSTR(MIEL_HVAC_LOGNAME
": short data response (%zu < %zu)"), len, sizeof(*d));
return;
}
@@ -1021,7 +1021,7 @@ miel_hvac_pre_init(void)
sc = (struct miel_hvac_softc *)malloc(sizeof(*sc));
if (sc == NULL) {
- AddLog_P(LOG_LEVEL_ERROR,
+ AddLog(LOG_LEVEL_ERROR,
PSTR(MIEL_HVAC_LOGNAME ": unable to allocate state"));
return;
}
@@ -1033,7 +1033,7 @@ miel_hvac_pre_init(void)
Pin(GPIO_MIEL_HVAC_TX), 2);
if (!sc->sc_serial->begin(baudrate, 2)) {
- AddLog_P(LOG_LEVEL_ERROR,
+ AddLog(LOG_LEVEL_ERROR,
PSTR(MIEL_HVAC_LOGNAME ": unable to begin serial "
"(baudrate %d)"), baudrate);
goto del;
diff --git a/tasmota/xdrv_45_shelly_dimmer.ino b/tasmota/xdrv_45_shelly_dimmer.ino
index 0b44cde96..8e6e00e5c 100644
--- a/tasmota/xdrv_45_shelly_dimmer.ino
+++ b/tasmota/xdrv_45_shelly_dimmer.ino
@@ -111,7 +111,7 @@ struct SHD
void ShdResetToDFUMode()
{
#ifdef SHELLY_DIMMER_DEBUG
- AddLog_P(LOG_LEVEL_DEBUG, PSTR(SHD_LOGNAME "Request co-processor reset in dfu mode"));
+ AddLog(LOG_LEVEL_DEBUG, PSTR(SHD_LOGNAME "Request co-processor reset in dfu mode"));
#endif // SHELLY_DIMMER_DEBUG
pinMode(Pin(GPIO_SHELLY_DIMMER_RST_INV), OUTPUT);
@@ -133,7 +133,7 @@ void ShdResetToDFUMode()
bool ShdUpdateFirmware(uint8_t* data, uint32_t size)
{
#ifdef SHELLY_DIMMER_DEBUG
- AddLog_P(LOG_LEVEL_DEBUG, PSTR(SHD_LOGNAME "Update firmware"));
+ AddLog(LOG_LEVEL_DEBUG, PSTR(SHD_LOGNAME "Update firmware"));
#endif // SHELLY_DIMMER_DEBUG
bool ret = true;
@@ -148,7 +148,7 @@ bool ShdUpdateFirmware(uint8_t* data, uint32_t size)
stm32_err_t s_err;
#ifdef SHELLY_DIMMER_DEBUG
- AddLog_P(LOG_LEVEL_DEBUG, PSTR(SHD_LOGNAME "STM32 erase memory"));
+ AddLog(LOG_LEVEL_DEBUG, PSTR(SHD_LOGNAME "STM32 erase memory"));
#endif // SHELLY_DIMMER_DEBUG
stm32_erase_memory(stm, 0, STM32_MASS_ERASE);
@@ -190,7 +190,7 @@ bool ShdPresent(void) {
uint32_t ShdFlash(uint8_t* data, size_t size) {
#ifdef SHELLY_DIMMER_DEBUG
- AddLog_P(LOG_LEVEL_INFO, PSTR(SHD_LOGNAME "Updating firmware v%u.%u with %u bytes"), Shd.dimmer.version_major, Shd.dimmer.version_minor, size);
+ AddLog(LOG_LEVEL_INFO, PSTR(SHD_LOGNAME "Updating firmware v%u.%u with %u bytes"), Shd.dimmer.version_major, Shd.dimmer.version_minor, size);
#endif // SHELLY_DIMMER_DEBUG
Serial.end();
@@ -244,7 +244,7 @@ int check_byte()
if (chksm != chksm_calc)
{
#ifdef SHELLY_DIMMER_DEBUG
- AddLog_P(LOG_LEVEL_DEBUG, PSTR(SHD_LOGNAME "Checksum: %x calculated: %x"), chksm, chksm_calc);
+ AddLog(LOG_LEVEL_DEBUG, PSTR(SHD_LOGNAME "Checksum: %x calculated: %x"), chksm, chksm_calc);
#endif // SHELLY_DIMMER_DEBUG
return 0;
}
@@ -267,7 +267,7 @@ bool ShdSerialSend(const uint8_t data[] = nullptr, uint16_t len = 0)
int retries = 3;
#ifdef SHELLY_DIMMER_DEBUG
- AddLog_P(LOG_LEVEL_DEBUG_MORE, PSTR(SHD_LOGNAME "Tx Packet:"));
+ AddLog(LOG_LEVEL_DEBUG_MORE, PSTR(SHD_LOGNAME "Tx Packet:"));
AddLogBuffer(LOG_LEVEL_DEBUG_MORE, (uint8_t*)data, len);
#endif // SHELLY_DIMMER_DEBUG
@@ -287,7 +287,7 @@ bool ShdSerialSend(const uint8_t data[] = nullptr, uint16_t len = 0)
}
// timeout
- AddLog_P(LOG_LEVEL_DEBUG_MORE, PSTR(SHD_LOGNAME "Serial send timeout"));
+ AddLog(LOG_LEVEL_DEBUG_MORE, PSTR(SHD_LOGNAME "Serial send timeout"));
}
return false;
}
@@ -426,9 +426,9 @@ void ShdSendCalibration(uint16_t brightness, uint16_t func, uint16_t fade_rate)
bool ShdSyncState()
{
#ifdef SHELLY_DIMMER_DEBUG
- AddLog_P(LOG_LEVEL_DEBUG, PSTR(SHD_LOGNAME "Serial %p"), ShdSerial);
- AddLog_P(LOG_LEVEL_DEBUG, PSTR(SHD_LOGNAME "Set Brightness Want %d, Is %d"), Shd.req_brightness, Shd.dimmer.brightness);
- AddLog_P(LOG_LEVEL_DEBUG, PSTR(SHD_LOGNAME "Set Fade Want %d, Is %d"), Settings.light_speed, Shd.dimmer.fade_rate);
+ AddLog(LOG_LEVEL_DEBUG, PSTR(SHD_LOGNAME "Serial %p"), ShdSerial);
+ AddLog(LOG_LEVEL_DEBUG, PSTR(SHD_LOGNAME "Set Brightness Want %d, Is %d"), Shd.req_brightness, Shd.dimmer.brightness);
+ AddLog(LOG_LEVEL_DEBUG, PSTR(SHD_LOGNAME "Set Fade Want %d, Is %d"), Settings.light_speed, Shd.dimmer.fade_rate);
#endif // SHELLY_DIMMER_DEBUG
if (!ShdSerial)
@@ -456,7 +456,7 @@ bool ShdSyncState()
void ShdDebugState()
{
#ifdef SHELLY_DIMMER_DEBUG
- AddLog_P(LOG_LEVEL_DEBUG, PSTR(SHD_LOGNAME "MCU v%d.%d, Brightness:%d(%d%%), Power:%d, Fade:%d"),
+ AddLog(LOG_LEVEL_DEBUG, PSTR(SHD_LOGNAME "MCU v%d.%d, Brightness:%d(%d%%), Power:%d, Fade:%d"),
Shd.dimmer.version_major, Shd.dimmer.version_minor,
Shd.dimmer.brightness,
changeUIntScale(Shd.dimmer.brightness, 0, 1000, 0, 100),
@@ -538,7 +538,7 @@ bool ShdPacketProcess(void)
{
float kWhused = (float)Energy.active_power[0] * (Rtc.utc_time - Shd.last_power_check) / 36;
#ifdef SHELLY_DIMMER_DEBUG
- AddLog_P(LOG_LEVEL_DEBUG, PSTR(SHD_LOGNAME "Adding %i mWh to todays usage from %lu to %lu"), (int)(kWhused * 10), Shd.last_power_check, Rtc.utc_time);
+ AddLog(LOG_LEVEL_DEBUG, PSTR(SHD_LOGNAME "Adding %i mWh to todays usage from %lu to %lu"), (int)(kWhused * 10), Shd.last_power_check, Rtc.utc_time);
#endif // USE_ENERGY_SENSOR
Energy.kWhtoday += kWhused;
EnergyUpdateToday();
@@ -547,7 +547,7 @@ bool ShdPacketProcess(void)
#endif // USE_ENERGY_SENSOR
#ifdef SHELLY_DIMMER_DEBUG
- AddLog_P(LOG_LEVEL_DEBUG, PSTR(SHD_LOGNAME "ShdPacketProcess: Brightness:%d Power:%lu Voltage:%lu Current:%lu Fade:%d"), brightness, wattage_raw, voltage_raw, current_raw, fade_rate);
+ AddLog(LOG_LEVEL_DEBUG, PSTR(SHD_LOGNAME "ShdPacketProcess: Brightness:%d Power:%lu Voltage:%lu Current:%lu Fade:%d"), brightness, wattage_raw, voltage_raw, current_raw, fade_rate);
#endif // SHELLY_DIMMER_DEBUG
Shd.dimmer.brightness = brightness;
Shd.dimmer.power = wattage_raw;
@@ -557,7 +557,7 @@ bool ShdPacketProcess(void)
case SHD_VERSION_CMD:
{
#ifdef SHELLY_DIMMER_DEBUG
- AddLog_P(LOG_LEVEL_DEBUG, PSTR(SHD_LOGNAME "ShdPacketProcess: Version: %u.%u"), Shd.buffer[pos + 1], Shd.buffer[pos]);
+ AddLog(LOG_LEVEL_DEBUG, PSTR(SHD_LOGNAME "ShdPacketProcess: Version: %u.%u"), Shd.buffer[pos + 1], Shd.buffer[pos]);
#endif // SHELLY_DIMMER_DEBUG
Shd.dimmer.version_minor = Shd.buffer[pos];
Shd.dimmer.version_major = Shd.buffer[pos + 1];
@@ -581,7 +581,7 @@ bool ShdPacketProcess(void)
void ShdResetToAppMode()
{
#ifdef SHELLY_DIMMER_DEBUG
- AddLog_P(LOG_LEVEL_DEBUG, PSTR(SHD_LOGNAME "Request co-processor reset in app mode"));
+ AddLog(LOG_LEVEL_DEBUG, PSTR(SHD_LOGNAME "Request co-processor reset in app mode"));
#endif // SHELLY_DIMMER_DEBUG
pinMode(Pin(GPIO_SHELLY_DIMMER_RST_INV), OUTPUT);
@@ -603,7 +603,7 @@ void ShdResetToAppMode()
void ShdPoll(void)
{
#ifdef SHELLY_DIMMER_DEBUG
- AddLog_P(LOG_LEVEL_DEBUG, PSTR(SHD_LOGNAME "Poll"));
+ AddLog(LOG_LEVEL_DEBUG, PSTR(SHD_LOGNAME "Poll"));
#endif // SHELLY_DIMMER_DEBUG
if (!ShdSerial)
@@ -616,7 +616,7 @@ void ShdPoll(void)
bool ShdSendVersion(void)
{
#ifdef SHELLY_DIMMER_DEBUG
- AddLog_P(LOG_LEVEL_INFO, PSTR(SHD_LOGNAME "Sending version command"));
+ AddLog(LOG_LEVEL_INFO, PSTR(SHD_LOGNAME "Sending version command"));
#endif // SHELLY_DIMMER_DEBUG
return ShdSendCmd(SHD_VERSION_CMD, 0, 0);
}
@@ -632,7 +632,7 @@ void ShdGetSettings(void)
if (strstr(SettingsText(SET_SHD_PARAM), ",") != nullptr)
{
#ifdef SHELLY_DIMMER_DEBUG
- AddLog_P(LOG_LEVEL_INFO, PSTR(SHD_LOGNAME "Loading params: %s"), SettingsText(SET_SHD_PARAM));
+ AddLog(LOG_LEVEL_INFO, PSTR(SHD_LOGNAME "Loading params: %s"), SettingsText(SET_SHD_PARAM));
#endif // SHELLY_DIMMER_DEBUG
// Shd.req_brightness = atoi(subStr(parameters, SettingsText(SET_SHD_PARAM), ",", 1));
Shd.leading_edge = atoi(subStr(parameters, SettingsText(SET_SHD_PARAM), ",", 2));
@@ -653,7 +653,7 @@ void ShdSaveSettings(void)
void ShdInit(void)
{
#ifdef SHELLY_DIMMER_DEBUG
- AddLog_P(LOG_LEVEL_INFO, PSTR(SHD_LOGNAME "Shelly Dimmer Driver Starting Tx %d Rx %d"), Pin(GPIO_TXD), Pin(GPIO_RXD));
+ AddLog(LOG_LEVEL_INFO, PSTR(SHD_LOGNAME "Shelly Dimmer Driver Starting Tx %d Rx %d"), Pin(GPIO_TXD), Pin(GPIO_RXD));
#endif // SHELLY_DIMMER_DEBUG
Shd.buffer = (uint8_t *)malloc(SHD_BUFFER_SIZE);
@@ -669,7 +669,7 @@ void ShdInit(void)
ShdResetToAppMode();
bool got_version = ShdSendVersion();
- AddLog_P(LOG_LEVEL_INFO, PSTR(SHD_LOGNAME "Shelly Dimmer Co-processor Version v%u.%u"), Shd.dimmer.version_major, Shd.dimmer.version_minor);
+ AddLog(LOG_LEVEL_INFO, PSTR(SHD_LOGNAME "Shelly Dimmer Co-processor Version v%u.%u"), Shd.dimmer.version_major, Shd.dimmer.version_minor);
ShdGetSettings();
ShdSaveSettings();
@@ -694,7 +694,7 @@ bool ShdSerialInput(void)
// finished
#ifdef SHELLY_DIMMER_DEBUG
Shd.byte_counter++;
- AddLog_P(LOG_LEVEL_DEBUG_MORE, PSTR(SHD_LOGNAME "Rx Packet:"));
+ AddLog(LOG_LEVEL_DEBUG_MORE, PSTR(SHD_LOGNAME "Rx Packet:"));
AddLogBuffer(LOG_LEVEL_DEBUG_MORE, Shd.buffer, Shd.byte_counter);
#endif // SHELLY_DIMMER_DEBUG
Shd.byte_counter = 0;
@@ -707,7 +707,7 @@ bool ShdSerialInput(void)
{
// wrong data
#ifdef SHELLY_DIMMER_DEBUG
- AddLog_P(LOG_LEVEL_DEBUG, PSTR(SHD_LOGNAME "Byte %i of received data frame is invalid. Rx Packet:"), Shd.byte_counter);
+ AddLog(LOG_LEVEL_DEBUG, PSTR(SHD_LOGNAME "Byte %i of received data frame is invalid. Rx Packet:"), Shd.byte_counter);
Shd.byte_counter++;
AddLogBuffer(LOG_LEVEL_DEBUG_MORE, Shd.buffer, Shd.byte_counter);
#endif // SHELLY_DIMMER_DEBUG
@@ -739,7 +739,7 @@ bool ShdModuleSelected(void) {
bool ShdSetChannels(void)
{
#ifdef SHELLY_DIMMER_DEBUG
- AddLog_P(LOG_LEVEL_DEBUG_MORE, PSTR(SHD_LOGNAME "SetChannels:"));
+ AddLog(LOG_LEVEL_DEBUG_MORE, PSTR(SHD_LOGNAME "SetChannels:"));
AddLogBuffer(LOG_LEVEL_DEBUG_MORE, (uint8_t *)XdrvMailbox.data, XdrvMailbox.data_len);
#endif // SHELLY_DIMMER_DEBUG
@@ -757,7 +757,7 @@ bool ShdSetChannels(void)
bool ShdSetPower(void)
{
#ifdef SHELLY_DIMMER_DEBUG
- AddLog_P(LOG_LEVEL_INFO, PSTR(SHD_LOGNAME "Set Power, Power 0x%02x"), XdrvMailbox.index);
+ AddLog(LOG_LEVEL_INFO, PSTR(SHD_LOGNAME "Set Power, Power 0x%02x"), XdrvMailbox.index);
#endif // SHELLY_DIMMER_DEBUG
Shd.req_on = (bool)XdrvMailbox.index;
@@ -784,9 +784,9 @@ void CmndShdLeadingEdge(void)
Settings.shd_leading_edge = XdrvMailbox.payload;
#ifdef SHELLY_DIMMER_DEBUG
if (Shd.leading_edge == 1)
- AddLog_P(LOG_LEVEL_DEBUG, PSTR(SHD_LOGNAME "Set to trailing edge"));
+ AddLog(LOG_LEVEL_DEBUG, PSTR(SHD_LOGNAME "Set to trailing edge"));
else
- AddLog_P(LOG_LEVEL_DEBUG, PSTR(SHD_LOGNAME "Set to leading edge"));
+ AddLog(LOG_LEVEL_DEBUG, PSTR(SHD_LOGNAME "Set to leading edge"));
#endif // SHELLY_DIMMER_DEBUG
ShdSendSettings();
}
@@ -801,7 +801,7 @@ void CmndShdWarmupBrightness(void)
Shd.warmup_brightness = XdrvMailbox.payload * 10;
Settings.shd_warmup_brightness = XdrvMailbox.payload;
#ifdef SHELLY_DIMMER_DEBUG
- AddLog_P(LOG_LEVEL_DEBUG, PSTR(SHD_LOGNAME "Set warmup brightness to %d%%"), XdrvMailbox.payload);
+ AddLog(LOG_LEVEL_DEBUG, PSTR(SHD_LOGNAME "Set warmup brightness to %d%%"), XdrvMailbox.payload);
#endif // SHELLY_DIMMER_DEBUG
ShdSendSettings();
}
@@ -816,7 +816,7 @@ void CmndShdWarmupTime(void)
Shd.warmup_time = XdrvMailbox.payload;
Settings.shd_warmup_time = XdrvMailbox.payload;
#ifdef SHELLY_DIMMER_DEBUG
- AddLog_P(LOG_LEVEL_DEBUG, PSTR(SHD_LOGNAME "Set warmup time to %dms"), XdrvMailbox.payload);
+ AddLog(LOG_LEVEL_DEBUG, PSTR(SHD_LOGNAME "Set warmup time to %dms"), XdrvMailbox.payload);
#endif // SHELLY_DIMMER_DEBUG
ShdSendSettings();
}
diff --git a/tasmota/xdrv_46_ccloader.ino b/tasmota/xdrv_46_ccloader.ino
index 3675ca676..3b2afc84b 100644
--- a/tasmota/xdrv_46_ccloader.ino
+++ b/tasmota/xdrv_46_ccloader.ino
@@ -533,7 +533,7 @@ void CCLProgrammerInit(void)
bool CCLoaderinit()
{
CCLProgrammerInit();
- AddLog_P(LOG_LEVEL_INFO,PSTR("CCL: programmer init"));
+ AddLog(LOG_LEVEL_INFO,PSTR("CCL: programmer init"));
CCL.init = true;
return true;
}
@@ -564,17 +564,17 @@ void CCLoaderLoop() {
switch(step) {
case 0:
CCLdebug_init();
- AddLog_P(LOG_LEVEL_INFO,PSTR("CCL: debug init"));
+ AddLog(LOG_LEVEL_INFO,PSTR("CCL: debug init"));
step++;
break;
case 1:
CCLread_chip_id();
if((CCL.chip.ID!=0)) {
- AddLog_P(LOG_LEVEL_INFO,PSTR("CCL: found chip with ID: %x, Rev: %x -> %s"), CCL.chip.ID, CCL.chip.rev, CCLChipName(CCL.chip.ID).c_str());
+ AddLog(LOG_LEVEL_INFO,PSTR("CCL: found chip with ID: %x, Rev: %x -> %s"), CCL.chip.ID, CCL.chip.rev, CCLChipName(CCL.chip.ID).c_str());
step++;
}
else {
- AddLog_P(LOG_LEVEL_INFO,PSTR("CCL: no chip found"));
+ AddLog(LOG_LEVEL_INFO,PSTR("CCL: no chip found"));
return;
}
break;
@@ -587,7 +587,7 @@ bool CLLFlashFirmware(uint8_t* data, uint32_t size)
bool ret = true;
unsigned char debug_config = 0;
unsigned char Verify = 0;
- AddLog_P(LOG_LEVEL_INFO,PSTR("CCL: .bin file downloaded with size: %u blocks"), size/512);
+ AddLog(LOG_LEVEL_INFO,PSTR("CCL: .bin file downloaded with size: %u blocks"), size/512);
if (CCL.chip.ID!=0)
{
CCLRunDUP();
@@ -608,7 +608,7 @@ bool CLLFlashFirmware(uint8_t* data, uint32_t size)
unsigned char rxBuf[512];
uint32_t block = 0;
unsigned int addr = 0x0000;
- AddLog_P(LOG_LEVEL_INFO,PSTR("CCL: will flash ...."));
+ AddLog(LOG_LEVEL_INFO,PSTR("CCL: will flash ...."));
AddLogBuffer(LOG_LEVEL_DEBUG,data,16); // quick check to compare with a hex editor
while((block*512)= FTC532_DEBOUNCE) {
#endif // FTC532_DEBOUNCE > 1
#ifdef DEBUG_FTC532
- AddLog_P(LOG_LEVEL_DEBUG, PSTR("FTC: SAM=%04X KEY=%X OLD=%X INV=%u NOI=%u FRM=%u OK=%u TIME=%lu Pin=%u"),
+ AddLog(LOG_LEVEL_DEBUG, PSTR("FTC: SAM=%04X KEY=%X OLD=%X INV=%u NOI=%u FRM=%u OK=%u TIME=%lu Pin=%u"),
Ftc532.sample, Ftc532.keys, Ftc532.old_keys, Ftc532.e_inv, Ftc532.e_noise, Ftc532.e_frame,
Ftc532.valid, Ftc532.rxtime, Pin(GPIO_FTC532));
#endif // DEBUG_FTC532
@@ -197,7 +197,7 @@ void ftc532_update(void) { // Usually called every 50 m
#ifdef DEBUG_FTC532
else {
++Ftc532.e_inv;
- AddLog_P(LOG_LEVEL_DEBUG, PSTR("FTC: ILL SAM=%04X"), Ftc532.sample);
+ AddLog(LOG_LEVEL_DEBUG, PSTR("FTC: ILL SAM=%04X"), Ftc532.sample);
}
#endif // DEBUG_FTC532
}
diff --git a/tasmota/xdrv_48_timeprop.ino b/tasmota/xdrv_48_timeprop.ino
index 5589868de..e2a699832 100644
--- a/tasmota/xdrv_48_timeprop.ino
+++ b/tasmota/xdrv_48_timeprop.ino
@@ -136,7 +136,7 @@ void TimepropSetPower(int index, float power) {
}
void TimepropInit(void) {
- // AddLog_P(LOG_LEVEL_INFO, PSTR("TPR: Timeprop Init"));
+ // AddLog(LOG_LEVEL_INFO, PSTR("TPR: Timeprop Init"));
int cycleTimes[TIMEPROP_NUM_OUTPUTS] = {TIMEPROP_CYCLETIMES};
int deadTimes[TIMEPROP_NUM_OUTPUTS] = {TIMEPROP_DEADTIMES};
int opInverts[TIMEPROP_NUM_OUTPUTS] = {TIMEPROP_OPINVERTS};
@@ -184,29 +184,26 @@ bool TimepropCommand()
bool serviced = true;
uint8_t ua_prefix_len = strlen(D_CMND_TIMEPROP); // to detect prefix of command
/*
- snprintf_P(log_data, sizeof(log_data), "Command called: "
- "index: %d data_len: %d payload: %d topic: %s data: %s\n",
+ AddLog_P(LOG_LEVEL_INFO, PSTR("Command called: "
+ "index: %d data_len: %d payload: %d topic: %s data: %s"),
XdrvMailbox.index,
XdrvMailbox.data_len,
XdrvMailbox.payload,
(XdrvMailbox.payload >= 0 ? XdrvMailbox.topic : ""),
(XdrvMailbox.data_len >= 0 ? XdrvMailbox.data : ""));
-
- AddLog(LOG_LEVEL_INFO);
*/
if (0 == strncasecmp_P(XdrvMailbox.topic, PSTR(D_CMND_TIMEPROP), ua_prefix_len)) {
// command starts with timeprop_
int command_code = GetCommandCode(command, sizeof(command), XdrvMailbox.topic + ua_prefix_len, kTimepropCommands);
if (CMND_TIMEPROP_SETPOWER == command_code) {
/*
- snprintf_P(log_data, sizeof(log_data), "Timeprop command timeprop_setpower: "
- "index: %d data_len: %d payload: %d topic: %s data: %s",
+ AddLog_P(LOG_LEVEL_INFO, PSTR("Timeprop command timeprop_setpower: "
+ "index: %d data_len: %d payload: %d topic: %s data: %s"),
XdrvMailbox.index,
XdrvMailbox.data_len,
XdrvMailbox.payload,
(XdrvMailbox.payload >= 0 ? XdrvMailbox.topic : ""),
(XdrvMailbox.data_len >= 0 ? XdrvMailbox.data : ""));
- AddLog(LOG_LEVEL_INFO);
*/
if (XdrvMailbox.index >=0 && XdrvMailbox.index < TIMEPROP_NUM_OUTPUTS) {
timeprops[XdrvMailbox.index].setPower( atof(XdrvMailbox.data), Tprop.current_time_secs );
diff --git a/tasmota/xdrv_49_pid.ino b/tasmota/xdrv_49_pid.ino
index 15097106b..5ba61b004 100644
--- a/tasmota/xdrv_49_pid.ino
+++ b/tasmota/xdrv_49_pid.ino
@@ -246,7 +246,7 @@ void PIDShowSensor() {
Pid.run_pid_now = true;
}
} else {
- AddLog_P(LOG_LEVEL_ERROR, PSTR("PID: No local temperature sensor found"));
+ AddLog(LOG_LEVEL_ERROR, PSTR("PID: No local temperature sensor found"));
}
}
diff --git a/tasmota/xdrv_50_filesystem.ino b/tasmota/xdrv_50_filesystem.ino
index 0b7fc53d2..141af6215 100644
--- a/tasmota/xdrv_50_filesystem.ino
+++ b/tasmota/xdrv_50_filesystem.ino
@@ -50,9 +50,8 @@ ufsfree free size in kB
#define UFS_TFAT 2
#define UFS_TLFS 3
-#define UFS_FILE_WRITE "w"
-#define UFS_FILE_READ "r"
-
+/*
+// In tasmota.ino
#ifdef ESP8266
#include
#include
@@ -61,9 +60,7 @@ ufsfree free size in kB
#include
#endif // USE_SDCARD
#endif // ESP8266
-
#ifdef ESP32
-#define FFS_2
#include
#ifdef USE_SDCARD
#include
@@ -71,6 +68,7 @@ ufsfree free size in kB
#include "FFat.h"
#include "FS.h"
#endif // ESP32
+*/
// Global file system pointer
FS *ufsp;
@@ -133,7 +131,7 @@ void UfsInitOnce(void) {
void UfsInit(void) {
UfsInitOnce();
if (ufs_type) {
- AddLog_P(LOG_LEVEL_INFO, PSTR("UFS: FlashFS mounted with %d kB free"), UfsInfo(1, 0));
+ AddLog(LOG_LEVEL_INFO, PSTR("UFS: FlashFS mounted with %d kB free"), UfsInfo(1, 0));
}
}
@@ -167,10 +165,10 @@ void UfsCheckSDCardInit(void) {
// make sd card the global filesystem
#ifdef ESP8266
// on esp8266 sdcard info takes several seconds !!!, so we ommit it here
- AddLog_P(LOG_LEVEL_INFO, PSTR("UFS: SDCard mounted"));
+ AddLog(LOG_LEVEL_INFO, PSTR("UFS: SDCard mounted"));
#endif // ESP8266
#ifdef ESP32
- AddLog_P(LOG_LEVEL_INFO, PSTR("UFS: SDCard mounted with %d kB free"), UfsInfo(1, 0));
+ AddLog(LOG_LEVEL_INFO, PSTR("UFS: SDCard mounted with %d kB free"), UfsInfo(1, 0));
#endif // ESP32
}
}
@@ -278,7 +276,7 @@ bool TfsFileExists(const char *fname){
bool yes = ffsp->exists(fname);
if (!yes) {
- AddLog_P(LOG_LEVEL_INFO, PSTR("TFS: File not found"));
+ AddLog(LOG_LEVEL_INFO, PSTR("TFS: File not found"));
}
return yes;
}
@@ -288,7 +286,7 @@ bool TfsSaveFile(const char *fname, const uint8_t *buf, uint32_t len) {
File file = ffsp->open(fname, "w");
if (!file) {
- AddLog_P(LOG_LEVEL_INFO, PSTR("TFS: Save failed"));
+ AddLog(LOG_LEVEL_INFO, PSTR("TFS: Save failed"));
return false;
}
@@ -302,7 +300,7 @@ bool TfsInitFile(const char *fname, uint32_t len, uint8_t init_value) {
File file = ffsp->open(fname, "w");
if (!file) {
- AddLog_P(LOG_LEVEL_INFO, PSTR("TFS: Erase failed"));
+ AddLog(LOG_LEVEL_INFO, PSTR("TFS: Erase failed"));
return false;
}
@@ -319,7 +317,7 @@ bool TfsLoadFile(const char *fname, uint8_t *buf, uint32_t len) {
File file = ffsp->open(fname, "r");
if (!file) {
- AddLog_P(LOG_LEVEL_INFO, PSTR("TFS: File not found"));
+ AddLog(LOG_LEVEL_INFO, PSTR("TFS: File not found"));
return false;
}
@@ -332,7 +330,7 @@ bool TfsDeleteFile(const char *fname) {
if (!ffs_type) { return false; }
if (!ffsp->remove(fname)) {
- AddLog_P(LOG_LEVEL_INFO, PSTR("TFS: Delete failed"));
+ AddLog(LOG_LEVEL_INFO, PSTR("TFS: Delete failed"));
return false;
}
return true;
@@ -391,7 +389,7 @@ void UFSDelete(void) {
result = (ufs_type && ufsp->remove(XdrvMailbox.data));
}
if (!result) {
- ResponseCmndChar(D_JSON_FAILED);
+ ResponseCmndChar(PSTR(D_JSON_FAILED));
} else {
ResponseCmndDone();
}
@@ -402,6 +400,7 @@ void UFSDelete(void) {
* Web support
\*********************************************************************************************/
+
#ifdef USE_WEBSERVER
const char UFS_WEB_DIR[] PROGMEM =
@@ -414,7 +413,7 @@ const char UFS_FORM_FILE_UPGc[] PROGMEM =
"" D_FS_SIZE " %s MB - " D_FS_FREE " %s MB";
const char UFS_FORM_FILE_UPGc1[] PROGMEM =
- "
%s";
+ "
%s";
const char UFS_FORM_FILE_UPGc2[] PROGMEM =
"
";
@@ -437,33 +436,35 @@ const char UFS_FORM_SDC_DIRd[] PROGMEM =
const char UFS_FORM_SDC_DIRb[] PROGMEM =
"%s %s %8d %s
";
const char UFS_FORM_SDC_HREF[] PROGMEM =
- "http://%s/ufsd?download=%s/%s";
+ "http://%_I/ufsd?download=%s/%s";
#ifdef GUI_TRASH_FILE
const char UFS_FORM_SDC_HREFdel[] PROGMEM =
- //"🗑";
- "🔥"; // 🔥
+ //"🗑";
+ "🔥"; // 🔥
#endif // GUI_TRASH_FILE
void UfsDirectory(void) {
if (!HttpCheckPriviledgedAccess()) { return; }
- AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_HTTP D_MANAGE_FILE_SYSTEM));
+ AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_HTTP D_MANAGE_FILE_SYSTEM));
uint8_t depth = 0;
strcpy(ufs_path, "/");
- if (Webserver->hasArg("download")) {
- String stmp = Webserver->arg("download");
+ if (Webserver->hasArg(F("download"))) {
+ String stmp = Webserver->arg(F("download"));
char *cp = (char*)stmp.c_str();
if (UfsDownloadFile(cp)) {
// is directory
strcpy(ufs_path, cp);
+ } else {
+ return;
}
}
- if (Webserver->hasArg("dir")) {
- String stmp = Webserver->arg("dir");
+ if (Webserver->hasArg(F("dir"))) {
+ String stmp = Webserver->arg(F("dir"));
ufs_dir = atoi(stmp.c_str());
if (ufs_dir == 1) {
dfsp = ufsp;
@@ -474,8 +475,8 @@ void UfsDirectory(void) {
}
}
- if (Webserver->hasArg("delete")) {
- String stmp = Webserver->arg("delete");
+ if (Webserver->hasArg(F("delete"))) {
+ String stmp = Webserver->arg(F("delete"));
char *cp = (char*)stmp.c_str();
dfsp->remove(cp);
}
@@ -491,11 +492,11 @@ void UfsDirectory(void) {
WSContentSend_PD(UFS_FORM_FILE_UPGc, WebColor(COL_TEXT), ts, fs);
if (ufs_dir) {
- WSContentSend_P(UFS_FORM_FILE_UPGc1, WiFi.localIP().toString().c_str(), (ufs_dir == 1)?2:1, (ufs_dir == 1)?PSTR("SDCard"):PSTR("FlashFS"));
+ WSContentSend_P(UFS_FORM_FILE_UPGc1, (uint32_t)WiFi.localIP(), (ufs_dir == 1)?2:1, (ufs_dir == 1)?PSTR("SDCard"):PSTR("FlashFS"));
}
WSContentSend_P(UFS_FORM_FILE_UPGc2);
- WSContentSend_P(UFS_FORM_FILE_UPG, D_SCRIPT_UPLOAD);
+ WSContentSend_P(UFS_FORM_FILE_UPG, PSTR(D_SCRIPT_UPLOAD));
WSContentSend_P(UFS_FORM_SDC_DIRa);
if (ufs_type) {
@@ -513,13 +514,13 @@ void UfsListDir(char *path, uint8_t depth) {
char name[32];
char npath[128];
char format[12];
- sprintf(format, "%%-%ds", 24 - depth);
+ sprintf(format, PSTR("%%-%ds"), 24 - depth);
File dir = dfsp->open(path, UFS_FILE_READ);
if (dir) {
dir.rewindDirectory();
if (strlen(path)>1) {
- snprintf_P(npath, sizeof(npath), PSTR("http://%s/ufsd?download=%s"), WiFi.localIP().toString().c_str(), path);
+ ext_snprintf_P(npath, sizeof(npath), PSTR("http://%_I/ufsd?download=%s"), (uint32_t)WiFi.localIP(), path);
for (uint32_t cnt = strlen(npath) - 1; cnt > 0; cnt--) {
if (npath[cnt] == '/') {
if (npath[cnt - 1] == '=') {
@@ -530,7 +531,7 @@ void UfsListDir(char *path, uint8_t depth) {
break;
}
}
- WSContentSend_P(UFS_FORM_SDC_DIRd, npath, path, "..");
+ WSContentSend_P(UFS_FORM_SDC_DIRd, npath, path, PSTR(".."));
}
char *ep;
while (true) {
@@ -561,7 +562,7 @@ void UfsListDir(char *path, uint8_t depth) {
sprintf(cp, format, ep);
if (entry.isDirectory()) {
- snprintf_P(npath, sizeof(npath), UFS_FORM_SDC_HREF, WiFi.localIP().toString().c_str(), pp, ep);
+ ext_snprintf_P(npath, sizeof(npath), UFS_FORM_SDC_HREF, (uint32_t)WiFi.localIP(), pp, ep);
WSContentSend_P(UFS_FORM_SDC_DIRd, npath, ep, name);
uint8_t plen = strlen(path);
if (plen > 1) {
@@ -573,12 +574,12 @@ void UfsListDir(char *path, uint8_t depth) {
} else {
#ifdef GUI_TRASH_FILE
char delpath[128];
- snprintf_P(delpath, sizeof(delpath), UFS_FORM_SDC_HREFdel, WiFi.localIP().toString().c_str(), pp, ep);
+ ext_snprintf_P(delpath, sizeof(delpath), UFS_FORM_SDC_HREFdel, (uint32_t)WiFi.localIP(), pp, ep);
#else
char delpath[2];
delpath[0]=0;
#endif // GUI_TRASH_FILE
- snprintf_P(npath, sizeof(npath), UFS_FORM_SDC_HREF, WiFi.localIP().toString().c_str(), pp, ep);
+ ext_snprintf_P(npath, sizeof(npath), UFS_FORM_SDC_HREF, (uint32_t)WiFi.localIP(), pp, ep);
WSContentSend_P(UFS_FORM_SDC_DIRb, npath, ep, name, tstr.c_str(), entry.size(), delpath);
}
}
@@ -588,19 +589,21 @@ void UfsListDir(char *path, uint8_t depth) {
}
}
-
+#ifdef ESP32
+#define ESP32_DOWNLOAD_TASK
+#endif // ESP32
uint8_t UfsDownloadFile(char *file) {
File download_file;
if (!dfsp->exists(file)) {
- AddLog_P(LOG_LEVEL_INFO, PSTR("UFS: File not found"));
+ AddLog(LOG_LEVEL_INFO, PSTR("UFS: File not found"));
return 0;
}
download_file = dfsp->open(file, UFS_FILE_READ);
if (!download_file) {
- AddLog_P(LOG_LEVEL_INFO, PSTR("UFS: Could not open file"));
+ AddLog(LOG_LEVEL_INFO, PSTR("UFS: Could not open file"));
return 0;
}
@@ -609,7 +612,7 @@ uint8_t UfsDownloadFile(char *file) {
return 1;
}
-#ifdef ESP8266
+#ifndef ESP32_DOWNLOAD_TASK
WiFiClient download_Client;
uint32_t flen = download_file.size();
@@ -649,14 +652,14 @@ uint8_t UfsDownloadFile(char *file) {
}
download_file.close();
download_Client.stop();
-#endif // esp8266
+#endif // ESP32_DOWNLOAD_TASK
-#ifdef ESP32
+#ifdef ESP32_DOWNLOAD_TASK
download_file.close();
if (download_busy == true) {
- AddLog_P(LOG_LEVEL_INFO, PSTR("UFS: Download is busy"));
+ AddLog(LOG_LEVEL_INFO, PSTR("UFS: Download is busy"));
return 0;
}
@@ -664,16 +667,16 @@ uint8_t UfsDownloadFile(char *file) {
char *path = (char*)malloc(128);
strcpy(path,file);
xTaskCreatePinnedToCore(donload_task, "DT", 6000, (void*)path, 3, NULL, 1);
-#endif // ESP32
+#endif // ESP32_DOWNLOAD_TASK
return 0;
}
-#ifdef ESP32
+#ifdef ESP32_DOWNLOAD_TASK
#ifndef DOWNLOAD_SIZE
#define DOWNLOAD_SIZE 4096
-#endif
+#endif // DOWNLOAD_SIZE
void donload_task(void *path) {
File download_file;
WiFiClient download_Client;
@@ -714,7 +717,7 @@ void donload_task(void *path) {
download_busy = false;
vTaskDelete( NULL );
}
-#endif // ESP32
+#endif // ESP32_DOWNLOAD_TASK
bool UfsUploadFileOpen(const char* upload_filename) {
@@ -759,13 +762,13 @@ bool Xdrv50(uint8_t function) {
#ifdef USE_WEBSERVER
case FUNC_WEB_ADD_MANAGEMENT_BUTTON:
if (ufs_type) {
- WSContentSend_PD(UFS_WEB_DIR, D_MANAGE_FILE_SYSTEM);
+ WSContentSend_PD(UFS_WEB_DIR, PSTR(D_MANAGE_FILE_SYSTEM));
}
break;
case FUNC_WEB_ADD_HANDLER:
- Webserver->on("/ufsd", UfsDirectory);
- Webserver->on("/ufsu", HTTP_GET, UfsDirectory);
- Webserver->on("/ufsu", HTTP_POST,[](){Webserver->sendHeader("Location","/ufsu");Webserver->send(303);}, HandleUploadLoop);
+ Webserver->on(F("/ufsd"), UfsDirectory);
+ Webserver->on(F("/ufsu"), HTTP_GET, UfsDirectory);
+ Webserver->on(F("/ufsu"), HTTP_POST,[](){Webserver->sendHeader(F("Location"),F("/ufsu"));Webserver->send(303);}, HandleUploadLoop);
break;
#endif // USE_WEBSERVER
}
diff --git a/tasmota/xdrv_51_bs814a2.ino b/tasmota/xdrv_51_bs814a2.ino
index 1a10eab9a..aa1abc4bb 100644
--- a/tasmota/xdrv_51_bs814a2.ino
+++ b/tasmota/xdrv_51_bs814a2.ino
@@ -34,7 +34,7 @@
THE PROTOCOL [tm]:
==================
see Holtek-Semicon BS814A-2 datasheet for details
-
+
*********************
* About this driver *
*********************
@@ -100,7 +100,7 @@ void bs814_read(void) { // Poll touch keys
uint8_t checksum = 0;
uint8_t bitp;
bool bitval;
-
+
// generate clock signal & sample frame
for (bitp = 0; bitp < 2 * BS814_KEYS_MAX; ++bitp) {
digitalWrite(Pin(GPIO_BS814_CLK), LOW);
@@ -123,8 +123,8 @@ void bs814_read(void) { // Poll touch keys
// validate frame
if (BS814_KEYS_MAX - checksum != ((frame >> 4) & 0x7)) { // checksum error
#ifdef DEBUG_BS814_DRIVER
- ++Bs814.e_cksum;
- AddLog_P(LOG_LEVEL_DEBUG, PSTR("CKS=%02X CFR=%02X"), checksum, (frame >> 4) & 0x7);
+ ++Bs814.e_cksum;
+ AddLog(LOG_LEVEL_DEBUG, PSTR("CKS=%02X CFR=%02X"), checksum, (frame >> 4) & 0x7);
#endif // DEBUG_BS814_DRIVER
return;
}
@@ -144,7 +144,7 @@ void bs814_update(void) { // Usually called every 50 ms
bs814_read();
if ((Bs814.keys & 0xF) != (Bs814.keys >> 4)) {
#ifdef DEBUG_BS814_DRIVER
- AddLog_P(LOG_LEVEL_DEBUG, PSTR("BS814: KEY=%0X OLD=%0X LVL=%u CKS=%u STP=%u OK=%u CLK=%u DAT=%u"),
+ AddLog(LOG_LEVEL_DEBUG, PSTR("BS814: KEY=%0X OLD=%0X LVL=%u CKS=%u STP=%u OK=%u CLK=%u DAT=%u"),
Bs814.keys & 0xF, Bs814.keys >> 4, Bs814.e_level, Bs814.e_cksum, Bs814.e_stp,
Bs814.valid, Pin(GPIO_BS814_CLK), Pin(GPIO_BS814_DAT));
#endif // DEBUG_BS814_DRIVER
diff --git a/tasmota/xdrv_79_esp32_ble.ino b/tasmota/xdrv_79_esp32_ble.ino
new file mode 100644
index 000000000..9c96da9b0
--- /dev/null
+++ b/tasmota/xdrv_79_esp32_ble.ino
@@ -0,0 +1,3579 @@
+/*
+ xdrv_79_esp32_ble.ino - BLE via ESP32 support for Tasmota
+
+ Copyright (C) 2020 Christian Baars and Theo Arends and Simon Hailes
+
+ 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
+ --------------------------------------------------------------------------------------------
+*/
+
+// TEMPORARILY define ESP32 and USE_BLE_ESP32 so VSCODE shows highlighting....
+//#define VSCODE_DEV
+#ifdef VSCODE_DEV
+#define ESP32
+#define USE_BLE_ESP32
+#endif
+
+#ifdef ESP32 // ESP32 only. Use define USE_HM10 for ESP8266 support
+#ifdef USE_BLE_ESP32
+
+/*
+ xdrv_79:
+ This driver uses the ESP32 BLE functionality to hopefully provide enough
+ BLE functionality to implement specific drivers on top of it.
+
+ As a generic driver, it can:
+ Be asked to
+ connect/write to a MAC/Service/Characteristic
+ connect/read from a MAC/Service/Characteristic
+ connect/write/awaitnotify from a MAC/Service/Characteristic/NotifyCharacteristic
+ connect/read/awaitnotify from a MAC/Service/Characteristic/NotifyCharacteristic
+
+ Cmnds:
+ BLEPeriod
+ BLEAdv
+ BLEOp
+ BLEMode
+ BLEDetails
+ BLEScan
+ BLEAlias
+ BLEName
+ BLEDebug
+ BLEDevices
+ BLEMaxAge
+ BLEAddrFilter
+
+ Other drivers can add callbacks to receive advertisements
+ Other drivers can add 'operations' to be performed and receive callbacks from the operation's success or failure
+
+Example BLEOp:
+Write and request next notify:
+BLEOp M:4C65A8DAF43A s:00001530-1212-efde-1523-785feabcd123 n:00001531-1212-efde-1523-785feabcd123 c:00001531-1212-efde-1523-785feabcd123 w:00 go
+12:45:12 MQT: tele/tasmota_esp32/BLE = {"BLEOperation":{"opid":"11","stat":"7","state":"DONENOTIFIED","MAC":"4C65A8DAF43A","svc":"00001530-1212-efde-1523-785feabcd123","char":"00001531-1212-efde-1523-785feabcd123","notifychar":"00001531-1212-efde-1523-785feabcd123","write":"00","notify":"100003"}}
+
+
+The driver can also be used by other drivers, using the functions:
+
+void registerForAdvertismentCallbacks(char *loggingtag, ADVERTISMENT_CALLBACK* pFn);
+void registerForOpCallbacks(char *loggingtag, OPCOMPLETE_CALLBACK* pFn);
+bool extQueueOperation(generic_sensor_t** op);
+
+These allow other code to
+ receive advertisements
+ receive operation callbacks.
+ create and start an operation, and get a callback when done/failed.
+
+i.e. the Bluetooth of the ESP can be shared without conflict.
+
+*/
+
+#define BLE_ESP32_ALIASES
+
+// uncomment for more diagnostic/information messages - + more flash use.
+//#define BLE_ESP32_DEBUG
+
+#define XDRV_79 79
+#define USE_MI_DECRYPTION
+
+#include
+#include
+#include
+#include
+#ifdef USE_MI_DECRYPTION
+#include
+#endif //USE_MI_DECRYPTION
+
+#include
+#include
+#include "NimBLEEddystoneURL.h"
+#include "NimBLEEddystoneTLM.h"
+#include "NimBLEBeacon.h"
+
+// from ble_gap.c
+extern "C" void ble_gap_conn_broken(uint16_t conn_handle, int reason);
+
+void installExamples();
+void sendExample();
+
+
+namespace BLE_ESP32 {
+
+
+// generic sensor type used as during
+// connect/read/wrtie/notify operations
+// only one operation will happen at a time
+
+#pragma pack( push, 0 ) // aligned structures for speed. but be sepcific
+
+/////////////////////////////////////////////////////
+// states for runTaskDoneOperation
+#define GEN_STATE_IDLE 0
+#define GEN_STATE_START 1
+#define GEN_STATE_STARTED 2
+
+#define GEN_STATE_READDONE 3
+#define GEN_STATE_WRITEDONE 4
+#define GEN_STATE_WAITNOTIFY 5
+#define GEN_STATE_WAITINDICATE 6
+
+#define GEN_STATE_NOTIFIED 7
+
+
+// Errors are all base on 0x100
+#define GEN_STATE_FAILED -1
+#define GEN_STATE_FAILED_CANTNOTIFYORINDICATE -2
+#define GEN_STATE_FAILED_CANTREAD -3
+#define GEN_STATE_FAILED_CANTWRITE -4
+#define GEN_STATE_FAILED_NOSERVICE -5
+#define GEN_STATE_FAILED_NO_RW_CHAR -6
+#define GEN_STATE_FAILED_NONOTIFYCHAR -7
+#define GEN_STATE_FAILED_NOTIFYTIMEOUT -8
+#define GEN_STATE_FAILED_READ -9
+#define GEN_STATE_FAILED_WRITE -10
+#define GEN_STATE_FAILED_CONNECT -11
+#define GEN_STATE_FAILED_NOTIFY -12
+#define GEN_STATE_FAILED_INDICATE -13
+#define GEN_STATE_FAILED_NODEVICE -14
+#define GEN_STATE_FAILED_NOREADWRITE -15
+#define GEN_STATE_FAILED_CANCEL -16
+//
+/////////////////////////////////////////////////////
+
+#define BLE_ESP32_MAXNAMELEN 32
+#define BLE_ESP32_MAXALIASLEN 20
+
+
+#define MAX_BLE_DATA_LEN 100
+struct generic_sensor_t {
+ int16_t state;
+ uint32_t opid; // incrementing id so we can find them
+ uint64_t notifytimer;
+
+ // uint8_t cancel;
+ // uint8_t requestType;
+ NimBLEAddress addr;
+ NimBLEUUID serviceUUID;
+ NimBLEUUID characteristicUUID;
+ NimBLEUUID notificationCharacteristicUUID;
+ uint8_t dataToWrite[MAX_BLE_DATA_LEN];
+ uint8_t writelen;
+ uint8_t dataRead[MAX_BLE_DATA_LEN];
+ uint8_t readlen;
+ uint8_t readtruncated;
+ uint8_t dataNotify[MAX_BLE_DATA_LEN];
+ uint8_t notifylen;
+ uint8_t notifytruncated;
+
+ // NOTE!!!: this callback is called DIRECTLY from the operation task, so be careful about cross-thread access of data
+ // if is called after read, so that you can do a read/modify/write operation on a characteristic.
+ // i.e. modify dataToWrite and writelen according to what you see in readData and readlen.
+ // for a normal read, please use the OPCOMPLETE_CALLBACK 'completecallback'
+ // normally null
+ void *readmodifywritecallback; // READ_CALLBACK function, used by external drivers
+
+ void *completecallback; // OPCOMPLETE_CALLBACK function, used by external drivers
+ void *context; // opaque context, used by external drivers, or can be set to a long for MQTT
+};
+
+
+////////////////////////////////////////////////////////////////
+// structure for callbacks from other drivers from advertisements.
+struct ble_advertisment_t {
+ BLEAdvertisedDevice *advertisedDevice; // the full NimBLE advertisment, in case people need MORE info.
+ uint32_t totalCount;
+
+ uint8_t addr[6];
+ uint8_t addrtype;
+ int8_t RSSI;
+ char name[BLE_ESP32_MAXNAMELEN+1];
+};
+
+struct ble_alias_t {
+ uint8_t addr[6];
+ char name[BLE_ESP32_MAXALIASLEN+1];
+};
+
+/*
+This is probabyl what you are looking for:
+ble_gap_addr_t gap_addr;
+gap_addr.addr_type = BLE_GAP_ADDR_TYPE_PUBLIC; //Public address 0x00
+gap_addr.addr_type = BLE_GAP_ADDR_TYPE_RANDOM_STATIC; //Random static address 0x01
+gap_addr.addr_type = BLE_GAP_ADDR_TYPE_RANDOM_PRIVATE_RESOLVABLE; //Random private resolvable address 0x02
+gap_addr.addr_type = BLE_GAP_ADDR_TYPE_RANDOM_PRIVATE_NON_RESOLVABLE; //Random private non-resolvable address 0x03
+*/
+
+#pragma pack( pop ) // byte-aligned structures to read the sensor data
+
+////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////
+// External interface to this driver for use by others.
+//
+// callback types to be used by external drivers
+//
+// returns -
+// 0 = let others see this,
+// 1 = I processed this, no need to give it to the next callback
+// 2 = I want this device erased from the scan
+typedef int ADVERTISMENT_CALLBACK(BLE_ESP32::ble_advertisment_t *pStruct);
+// returns - 0 = let others see this, 1 = I processed this, no need to give it to the next callback, or post on MQTT
+typedef int OPCOMPLETE_CALLBACK(BLE_ESP32::generic_sensor_t *pStruct);
+
+// NOTE!!!: this callback is called DIRECTLY from the operation task, so be careful about cross-thread access of data
+// if is called after read, so that you can do a read/modify/write operation on a characteristic.
+typedef int READ_CALLBACK(BLE_ESP32::generic_sensor_t *pStruct);
+
+typedef int SCANCOMPLETE_CALLBACK(NimBLEScanResults results);
+
+// tag is just a name for logging
+void registerForAdvertismentCallbacks(const char *tag, BLE_ESP32::ADVERTISMENT_CALLBACK* pFn);
+void registerForOpCallbacks(const char *tag, BLE_ESP32::OPCOMPLETE_CALLBACK* pFn);
+void registerForScanCallbacks(const char *tag, BLE_ESP32::SCANCOMPLETE_CALLBACK* pFn);
+
+////////////////////////////////////////////////////
+// BLE operations: these are currently 'new'ed and 'delete'ed.
+// in the future, they may be allocated from some constant menory store to avoid fragmentation.
+// so PLEASE don't create or destroy them yourselves.
+// create a new BLE operation.
+int newOperation(BLE_ESP32::generic_sensor_t** op);
+// free a BLE operation - this should be done if you did not call extQueueOperation for some reason
+int freeOperation(BLE_ESP32::generic_sensor_t** op);
+// queue a BLE operation - it will happen some time in the future.
+// Note: you do not need to free an operation once it have been queued. it will be freed by the driver.
+int extQueueOperation(BLE_ESP32::generic_sensor_t** op);
+const char * getStateString(int state);
+///////////////////////////////////////////////////////////////////////
+
+#define USE_NATIVE_LOGGING
+
+
+// a temporay safe logging mechanism. This has a max of 40 chars, and a max of 15 slots per 50ms
+//int SafeAddLog_P(uint32_t loglevel, PGM_P formatP, ...);
+
+static void BLEDiag();
+const char *getAlias(const uint8_t *addr);
+//void BLEAliasMqttList();
+void BLEAliasListResp();
+////////////////////////////////////////////////////////////////////////
+// utilities
+// dump a binary to hex
+char * dump(char *dest, int maxchars, const uint8_t *src, int len);
+
+
+
+
+struct BLE_simple_device_t {
+ uint8_t mac[6];
+ uint8_t addrtype;
+ char name[BLE_ESP32_MAXNAMELEN+1];
+ int8_t RSSI;
+ uint64_t lastseen; // last seen us
+ uint16_t maxAge; // maximum observed age of this device
+};
+
+
+
+// this protects our queues, which can be accessed by multiple tasks
+SemaphoreHandle_t BLEOperationsRecursiveMutex;
+SemaphoreHandle_t BLEDevicesMutex;
+
+
+// only run from main thread, because it deletes things that were newed there...
+static void mainThreadOpCallbacks();
+static void mainThreadBLETimeouts();
+
+int addOperation(std::deque *ops, BLE_ESP32::generic_sensor_t** op);
+BLE_ESP32::generic_sensor_t* nextOperation(std::deque *ops);
+std::string BLETriggerResponse(BLE_ESP32::generic_sensor_t *toSend);
+static void BLEscanEndedCB(NimBLEScanResults results);
+static void BLEGenNotifyCB(NimBLERemoteCharacteristic* pRemoteCharacteristic, uint8_t* pData, size_t length, bool isNotify);
+
+// this is called from the advert callback, be careful
+void BLEPostAdvert(ble_advertisment_t *Advertisment);
+static void BLEPostMQTTSeenDevices(int type);
+
+static void BLEShowStats();
+static void BLEPostMQTT(bool json);
+static void BLEStartOperationTask();
+
+// these are only run from the run task
+static void BLETaskRunCurrentOperation(BLE_ESP32::generic_sensor_t** pCurrentOperation, NimBLEClient **ppClient);
+static void BLETaskRunTaskDoneOperation(BLE_ESP32::generic_sensor_t** op, NimBLEClient **ppClient);
+int BLETaskStartScan(int time);
+
+
+// these are run from main thread
+static int StartBLE(void);
+static int StopBLE(void);
+
+// called from advert callback
+void setDetails(ble_advertisment_t *ad);
+
+#undef EXAMPLE_ADVERTISMENT_CALLBACK
+#undef EXAMPLE_OPERATION_CALLBACK
+
+#ifdef EXAMPLE_ADVERTISMENT_CALLBACK
+int myAdvertCallback(BLE_ESP32::ble_advertisment_t *pStruct);
+#endif
+#ifdef EXAMPLE_OPERATION_CALLBACK
+int myOpCallback(BLE_ESP32::generic_sensor_t *pStruct);
+int myOpCallback2(BLE_ESP32::generic_sensor_t *pStruct);
+#endif
+
+
+// single storage for advert callbacks....
+static ble_advertisment_t BLEAdvertisment;
+
+
+//////////////////////////////////////////////////
+// general variables for running the driver
+TaskHandle_t TasmotaMainTask;
+
+
+static int BLEMasterEnable = 0;
+static int BLEInitState = 0;
+static int BLERunningScan = 0;
+static uint32_t BLEScanCount = 0;
+static uint8_t BLEScanActiveMode = 0;
+static uint32_t BLELoopCount = 0;
+static uint32_t BLEOpCount = 0;
+
+static int BLEPublishDevices = 0; // causes MQTT publish of device list (each scan end)
+static BLEScan* ble32Scan = nullptr;
+bool BLERunning = false;
+// time we last started a scan in uS using esp_timer_get_time();
+// used to setect a scan which did not call back?
+uint64_t BLEScanStartedAt = 0;
+uint64_t BLEScanToEndBefore = 0;
+uint8_t BLEStopScan = 0;
+uint8_t BLEOtaStallBLE = 0;
+uint8_t BLEDebugMode = 0;
+int BLEMaxTaskLoopTime = 120; // we expect the task to NOT take > 120s per loop!!!
+uint64_t BLELastLoopTime = 0;
+int BLEScanTimeS = 20; // scan duraiton in S
+int BLEMaxTimeBetweenAdverts = 120; // we expect an advert at least this frequently, else restart BLE (in S)
+uint64_t BLEScanLastAdvertismentAt = 0;
+uint32_t lastopid = 0; // incrementing uinique opid
+uint32_t BLEResets = 0;
+// controls request of details about one device
+uint8_t BLEDetailsRequest = 0;
+uint8_t BLEDetailsMac[6];
+uint8_t BLEAliasListTrigger = 0;
+// triggers send for ALL operations known about
+uint8_t BLEPostMQTTTrigger = 0;
+int BLEMaxAge = 60*10; // 10 minutes
+int BLEAddressFilter = 0;
+
+
+//////////////////////////////////////////////////
+
+
+// operation being prepared through commands
+BLE_ESP32::generic_sensor_t* prepOperation = nullptr;
+
+// operations which have been queued
+std::deque queuedOperations;
+// operations in progress (at the moment, only one)
+std::deque currentOperations;
+// operations which have completed or failed, ready to send to MQTT
+std::deque completedOperations;
+
+// seen devices
+#define MAX_BLE_DEVICES_LOGGED 80
+std::deque seenDevices;
+std::deque freeDevices;
+
+
+
+// list of registered callbacks for advertisements
+// register using void registerForAdvertismentCallbacks(const char *somename ADVERTISMENT_CALLBACK* pFN);
+std::deque advertismentCallbacks;
+
+std::deque operationsCallbacks;
+
+std::deque scancompleteCallbacks;
+
+
+#ifdef BLE_ESP32_ALIASES
+std::deque aliases;
+#endif
+
+
+/*********************************************************************************************\
+ * constants
+\*********************************************************************************************/
+
+#define D_CMND_BLE "BLE"
+
+const char kBLE_Commands[] PROGMEM = D_CMND_BLE "|"
+ "Period|Adv|Op|Mode|Details|Scan|Alias|Name|Debug|Devices|MaxAge|AddrFilter";
+
+static void CmndBLEPeriod(void);
+static void CmndBLEAdv(void);
+static void CmndBLEOperation(void);
+static void CmndBLEMode(void);
+static void CmndBLEDetails(void);
+static void CmndBLEScan(void);
+static void CmndBLEAlias(void);
+static void CmndBLEName(void);
+static void CmndBLEDebug(void);
+static void CmndBLEDevices(void);
+static void CmndBLEMaxAge(void);
+static void CmndBLEAddrFilter(void);
+
+void (*const BLE_Commands[])(void) PROGMEM = {
+ &BLE_ESP32::CmndBLEPeriod,
+ &BLE_ESP32::CmndBLEAdv,
+ &BLE_ESP32::CmndBLEOperation,
+ &BLE_ESP32::CmndBLEMode,
+ &BLE_ESP32::CmndBLEDetails,
+ &BLE_ESP32::CmndBLEScan,
+ &BLE_ESP32::CmndBLEAlias,
+ &BLE_ESP32::CmndBLEName,
+ &BLE_ESP32::CmndBLEDebug,
+ &BLE_ESP32::CmndBLEDevices,
+ &BLE_ESP32::CmndBLEMaxAge,
+ &BLE_ESP32::CmndBLEAddrFilter
+};
+
+const char *successStates[] PROGMEM = {
+ PSTR("IDLE"), // 0
+ PSTR("START"),
+ PSTR("STARTED"),
+ PSTR("DONEREAD"),
+ PSTR("DONEWRITE"),
+ PSTR("WAITNOTIFY"),
+ PSTR("WAITINDICATE"),
+ PSTR("DONENOTIFIED") // 7
+};
+
+const char *failStates[] PROGMEM = {
+ PSTR("IDLE"), //0
+ PSTR("FAILED"), //-1
+ PSTR("FAILCANTNOTIFYORINDICATE"),
+ PSTR("FAILCANTREAD"),
+ PSTR("FAILCANTWRITE"),
+ PSTR("FAILNOSERVICE"),
+ PSTR("FAILNORWCHAR"), //-6
+ PSTR("FAILNONOTIFYCHAR"),
+ PSTR("FAILNOTIFYTIMEOUT"),
+ PSTR("FAILEREAD"),
+ PSTR("FAILWRITE"),
+ PSTR("FAILCONNECT"),
+ PSTR("FAILNOTIFY"),
+ PSTR("FAILINDICATE"),
+ PSTR("FAILNODEVICE"),
+ PSTR("FAILNOREADWRITE"),
+ PSTR("FAILCANCEL")// -16
+};
+
+const char * getStateString(int state){
+ if ((state >= 0) && (state < sizeof(successStates)/sizeof(*successStates))){
+ return successStates[state];
+ }
+
+ state = -state;
+ if ((state >= 0) && (state < sizeof(failStates)/sizeof(*failStates))){
+ return failStates[state];
+ }
+
+ return PSTR("STATEINVALID");
+}
+
+/*********************************************************************************************\
+ * enumerations
+\*********************************************************************************************/
+
+enum BLE_Commands { // commands useable in console or rules
+ CMND_BLE_PERIOD, // set period like TELE-period in seconds between read-cycles
+ CMND_BLE_ADV, // change advertisment options at runtime
+ CMND_BLE_OP, // connect/read/write/notify operations
+ CMND_BLE_MODE, // change mode of ble - BLE_MODES
+ CMND_BLE_DETAILS, // get details for one device's adverts
+ CMND_BLE_SCAN // Scan control
+ };
+
+enum {
+ BLEModeDisabled = 0, // BLE is disabled
+ BLEModeScanByCommand = 1, // BLE is activeated by commands only
+ BLEModeRegularScan = 2, // BLE is scanning all the time
+} BLE_SCAN_MODES;
+
+// values of BLEAdvertMode
+enum {
+ BLE_NO_ADV_SEND = 0, // driver is silent on MQTT regarding adverts
+ BLE_ADV_TELE = 1, // driver sends a summary at tele period
+ //BLE_ADV_ALL = 2, // driver sends every advert with full data to MQTT
+} BLEADVERTMODE;
+
+
+uint8_t BLEMode = BLEModeRegularScan;
+//uint8_t BLEMode = BLEModeScanByCommand;
+uint8_t BLETriggerScan = 0;
+uint8_t BLEAdvertMode = BLE_ADV_TELE;
+uint8_t BLEdeviceLimitReached = 0;
+
+uint8_t BLEStop = 0;
+uint64_t BLEStopAt = 0;
+
+uint8_t BLERestartTasmota = 0;
+uint8_t BLERestartNimBLE = 0;
+const char *BLE_RESTART_TEAMOTA_REASON_UNKNOWN = PSTR("unknown");
+const char *BLE_RESTART_TEAMOTA_REASON_RESTARTING_BLE_TIMEOUT = PSTR("restarting BLE took > 5s");
+const char *BLE_RESTART_TEAMOTA_REASON_BLE_LOOP_STALLED = PSTR("BLE loop stalled > 120s");
+const char *BLE_RESTART_TEAMOTA_REASON_BLE_DISCONNECT_FAIL = PSTR("BLE disconnect taking > 60s");
+const char *BLERestartTasmotaReason = BLE_RESTART_TEAMOTA_REASON_UNKNOWN;
+
+const char *BLE_RESTART_BLE_REASON_UNKNOWN = PSTR("unknown");
+const char *BLE_RESTART_BLE_REASON_ADVERT_BLE_TIMEOUT = PSTR("no adverts in 120s");
+const char *BLE_RESTART_BLE_REASON_CONN_LIMIT = PSTR("connect failed with connection limit reached");
+const char *BLE_RESTART_BLE_REASON_CONN_EXISTS = PSTR("connect failed with connection exists");
+const char *BLERestartBLEReason = nullptr;
+
+
+/*********************************************************************************************\
+ * log of all devices present
+\*********************************************************************************************/
+
+void initSeenDevices(){
+ /* added dynamically below, but never removed.
+ for (int i = 0; i < MAX_BLE_DEVICES_LOGGED; i++){
+ BLE_ESP32::BLE_simple_device_t* dev = new BLE_ESP32::BLE_simple_device_t;
+ freeDevices.push_back(dev);
+ }
+ */
+ return;
+}
+
+int addSeenDevice(const uint8_t *mac, uint8_t addrtype, const char *name, int8_t RSSI){
+ int res = 0;
+ uint64_t now = esp_timer_get_time();
+ TasAutoMutex localmutex(&BLEDevicesMutex, "BLEAdd");
+
+ int devicefound = 0;
+ // do we already know this device?
+ for (int i = 0; i < seenDevices.size(); i++){
+ if (!memcmp(seenDevices[i]->mac, mac, 6)){
+ seenDevices[i]->lastseen = now;
+ seenDevices[i]->addrtype = addrtype;
+ seenDevices[i]->RSSI = RSSI;
+ if ((!seenDevices[i]->name[0]) && name[0]){
+ strncpy(seenDevices[i]->name, name, sizeof(seenDevices[i]->name));
+ seenDevices[i]->name[sizeof(seenDevices[i]->name)-1] = 0;
+ }
+ devicefound = 1;
+ break;
+ }
+ }
+ if (!devicefound){
+ // if no free slots, add one if we have not reached our limit
+ if (!freeDevices.size()){
+ int total = seenDevices.size();
+ if (total < MAX_BLE_DEVICES_LOGGED){
+#ifdef BLE_ESP32_DEBUG
+ if (BLEDebugMode > 0) AddLog(LOG_LEVEL_INFO,PSTR("BLE: New seendev slot %d"), total);
+#endif
+ BLE_ESP32::BLE_simple_device_t* dev = new BLE_ESP32::BLE_simple_device_t;
+ freeDevices.push_back(dev);
+ } else {
+ // flag we hit the limit
+ BLEdeviceLimitReached ++;
+ if (BLEdeviceLimitReached >= 254){
+ BLEdeviceLimitReached = 254;
+ }
+ }
+ }
+
+ // get a new device from the free list
+ if (freeDevices.size()){
+ BLE_ESP32::BLE_simple_device_t* dev = freeDevices[0];
+ freeDevices.erase(freeDevices.begin());
+ memcpy(dev->mac, mac, 6);
+ strncpy(dev->name, name, sizeof(dev->name));
+ dev->name[sizeof(dev->name)-1] = 0;
+ dev->lastseen = now;
+ dev->addrtype = addrtype;
+ dev->RSSI = RSSI;
+ dev->maxAge = 1;
+ seenDevices.push_back(dev);
+ res = 2; // added
+ }
+ } else {
+ res = 1; // already there
+ }
+ return res;
+}
+
+// remove devices from the seen list by age, and add them to the free list
+// set ageS to 0 to delete all...
+int deleteSeenDevices(int ageS = 0){
+ int res = 0;
+ uint64_t now = esp_timer_get_time();
+ now = now/1000L;
+ now = now/1000L;
+ uint32_t nowS = (uint32_t)now;
+ uint32_t mintime = nowS - ageS;
+
+ {
+ TasAutoMutex localmutex(&BLEDevicesMutex, "BLEDel");
+
+ for (int i = seenDevices.size()-1; i >= 0; i--){
+ BLE_ESP32::BLE_simple_device_t* dev = seenDevices[i];
+ uint64_t lastseen = dev->lastseen/1000L;
+ lastseen = lastseen/1000L;
+ uint32_t lastseenS = (uint32_t) lastseen;
+ uint32_t del_at = lastseenS + ageS;
+ uint32_t devAge = nowS - lastseenS;
+ if (dev->maxAge < devAge){
+ dev->maxAge = devAge;
+ }
+
+ uint8_t filter = 0;
+ if (dev->addrtype > BLEAddressFilter){
+ filter = 1;
+ }
+
+ if ((del_at < nowS) || (ageS == 0) || filter){
+#ifdef BLE_ESP32_DEBUG
+ char addr[20];
+ dump(addr, 20, dev->mac, 6);
+ const char *alias = getAlias(dev->mac);
+ if (!filter){
+ AddLog(LOG_LEVEL_INFO,PSTR("BLE: Delete device %s(%s) by age lastseen %u + maxage %u < now %u."),
+ addr, alias, lastseenS, ageS, nowS);
+ } else {
+ AddLog(LOG_LEVEL_INFO,PSTR("BLE: Delete device %s(%s) by addrtype filter %d > %d."),
+ addr, alias, dev->addrtype, BLEAddressFilter);
+ }
+#endif
+ seenDevices.erase(seenDevices.begin()+i);
+ freeDevices.push_back(dev);
+ res++;
+ }
+ }
+ }
+ if (res){
+#ifdef BLE_ESP32_DEBUG
+ AddLog(LOG_LEVEL_INFO,PSTR("BLE: Deleted %d devices"), res);
+#endif
+ }
+ return res;
+}
+
+int deleteSeenDevice(uint8_t *mac){
+ int res = 0;
+ TasAutoMutex localmutex(&BLEDevicesMutex, "BLEDel2");
+ for (int i = 0; i < seenDevices.size(); i++){
+ if (!memcmp(seenDevices[i]->mac, mac, 6)){
+ BLE_ESP32::BLE_simple_device_t* dev = seenDevices[i];
+ seenDevices.erase(seenDevices.begin()+i);
+ freeDevices.push_back(dev);
+ res = 1;
+ break;
+ }
+ }
+ return res;
+}
+
+
+void checkDeviceTimouts(){
+ if (BLEMaxAge){
+ deleteSeenDevices(BLEMaxAge);
+ }
+}
+
+
+///////////////////////////////////////////////////////
+// returns age of device or 0. if age IS0, returns 1s
+uint32_t devicePresent(uint8_t *mac){
+ int res = 0;
+ uint64_t now = esp_timer_get_time();
+ now = now/1000L;
+ now = now/1000L;
+ uint32_t nowS = (uint32_t)now;
+
+ TasAutoMutex localmutex(&BLEDevicesMutex, "BLEPRes");
+ for (int i = 0; i < seenDevices.size(); i++){
+ if (!memcmp(seenDevices[i]->mac, mac, 6)){
+ uint64_t lastseen = seenDevices[i]->lastseen/1000L;
+ lastseen = lastseen/1000L;
+ uint32_t lastseenS = (uint32_t) lastseen;
+ uint32_t ageS = nowS-lastseenS;
+ if (!ageS) ageS++;
+ res = ageS;
+ break;
+ }
+ }
+ return res;
+}
+
+
+// the MAX we could expect.
+#define MAX_DEV_JSON_NAME_LEN BLE_ESP32_MAXNAMELEN
+#define MAX_DEV_JSON_RSSI_LEN 3
+#define MAX_DEV_JSON_INDEX_LEN 3
+#define MAX_DEV_JSON_ALIAS_LEN BLE_ESP32_MAXALIASLEN
+// "001122334455":{"i":123,"n":"01234567890123456789","r":-77}\0
+#define MIN_REQUIRED_DEVJSON_LEN \
+ (1+12+1 + 1 + 1 + \
+ +4 + MAX_DEV_JSON_INDEX_LEN \
+ +1 + 4 + MAX_DEV_JSON_NAME_LEN + 2 \
+ +1 + 4 + MAX_DEV_JSON_RSSI_LEN + 2 \
+ +1 + 4 + MAX_DEV_JSON_ALIAS_LEN + 2 \
+ +1 +1 \
+ )
+int getSeenDeviceToJson(int index, BLE_ESP32::BLE_simple_device_t* dev, char **dest, int *maxlen){
+ char *p = *dest;
+ // add 20 to be sure
+ if (*maxlen < MIN_REQUIRED_DEVJSON_LEN+20){
+ return 0;
+ }
+ // add mac as key
+ *((*dest)++) = '"';
+ dump((*dest), 20, dev->mac, 6);
+ (*dest) += 12;
+ *((*dest)++) = '"';
+ *((*dest)++) = ':';
+
+ // add a structure, so we COULD add more than name later
+ *((*dest)++) = '{';
+ *((*dest)++) = '"';
+ *((*dest)++) = 'i'; // index
+ *((*dest)++) = '"';
+ *((*dest)++) = ':';
+ sprintf((*dest), "%d", index);
+ (*dest) += strlen((*dest));
+
+ if (dev->name[0]){
+ *((*dest)++) = ',';
+ *((*dest)++) = '"';
+ *((*dest)++) = 'n';
+ *((*dest)++) = '"';
+ *((*dest)++) = ':';
+ *((*dest)++) = '"';
+ *(*dest) = 0; // must term, else it adds to the *end* of old data!
+ strncat((*dest), dev->name, MAX_DEV_JSON_NAME_LEN);
+ (*dest) += strlen((*dest));
+ *((*dest)++) = '"';
+ }
+ *((*dest)++) = ',';
+ *((*dest)++) = '"';
+ *((*dest)++) = 'r';
+ *((*dest)++) = '"';
+ *((*dest)++) = ':';
+ sprintf((*dest), "%d", dev->RSSI);
+ (*dest) += strlen((*dest));
+
+ const char *alias = getAlias(dev->mac);
+ if (alias && alias[0]){
+ *((*dest)++) = ',';
+ *((*dest)++) = '"';
+ *((*dest)++) = 'a';
+ *((*dest)++) = '"';
+ *((*dest)++) = ':';
+ *((*dest)++) = '"';
+ sprintf((*dest), "%s", alias);
+ (*dest) += strlen((*dest));
+ *((*dest)++) = '"';
+ }
+
+ *((*dest)++) = '}';
+ *maxlen -= (*dest - p);
+ return 1;
+}
+
+
+int nextSeenDev = 0;
+
+int getSeenDevicesToJson(char *dest, int maxlen){
+
+ if ((nextSeenDev == 0) || (nextSeenDev >= seenDevices.size())){
+ nextSeenDev = 0;
+ }
+
+ // deliberate test of SafeAddLog_P from main thread...
+ //AddLog(LOG_LEVEL_INFO,PSTR("BLE: getSeen %d"), seenDevices.size());
+
+
+ int len;
+ if (!maxlen) return 0;
+ strcpy((dest), ",\"BLEDevices\":{");
+ len = strlen(dest);
+ dest += len;
+ maxlen -= len;
+
+ int added = 0;
+ TasAutoMutex localmutex(&BLEDevicesMutex, "BLEGet");
+
+ snprintf((dest), maxlen-5, "\"total\":%d", seenDevices.size());
+ len = strlen(dest);
+ dest += len;
+ maxlen -= len;
+ added = 1; // trigger ','
+
+ for (; nextSeenDev < seenDevices.size(); nextSeenDev++){
+ if (maxlen > MIN_REQUIRED_DEVJSON_LEN + 3){
+ if (added){
+ *(dest++) = ',';
+ maxlen--;
+ }
+ int res = getSeenDeviceToJson(nextSeenDev, seenDevices[nextSeenDev], &dest, &maxlen);
+ if (res) {
+ added++;
+ } else {
+ if (added){
+ dest--; // reverse out comma it the string did not get added
+ maxlen++;
+ break;
+ }
+ }
+ } else {
+ break;
+ }
+ }
+ *(dest++) = '}';
+ *(dest++) = '}';
+ *(dest++) = 0;
+ int remains = (seenDevices.size() - nextSeenDev);
+ return remains;
+}
+
+
+
+
+/*********************************************************************************************\
+ * Mutex protected logging - max 5 logs of 40 chars
+\*********************************************************************************************/
+
+/*
+#ifdef BLE_ESP32_DEBUG
+ #define MAX_SAFELOG_LEN 40
+ #define MAX_SAFELOG_COUNT 25
+#else
+ #define MAX_SAFELOG_LEN 20
+ #define MAX_SAFELOG_COUNT 5
+#endif
+
+struct safelogdata {
+ int level;
+ char log_data[MAX_SAFELOG_LEN];
+};
+
+std::deque freelogs;
+std::deque filledlogs;
+uint8_t filledlogsOverflows = 0;
+SemaphoreHandle_t SafeLogMutex;
+
+
+void initSafeLog(){
+ TasmotaMainTask = xTaskGetCurrentTaskHandle();
+ SafeLogMutex = xSemaphoreCreateMutex();
+
+ for (int i = 0; i < MAX_SAFELOG_COUNT; i++){
+ BLE_ESP32::safelogdata* logdata = new BLE_ESP32::safelogdata;
+ freelogs.push_back(logdata);
+ }
+}
+
+int SafeAddLog_P(uint32_t loglevel, PGM_P formatP, ...) {
+ TaskHandle_t thistask = xTaskGetCurrentTaskHandle();
+ int added = 0;
+
+ // if the log would not be output do nothing here.
+ if ((loglevel > Settings.weblog_level) &&
+ (loglevel > TasmotaGlobal.seriallog_level) &&
+ (loglevel > Settings.mqttlog_level) &&
+ (loglevel > TasmotaGlobal.syslog_level)){
+ return added;
+ }
+
+ char BLE_temp_log_data[MAX_SAFELOG_LEN];
+ // as these are'expensive', let's not bother unless they are lower than the serial log level
+#ifndef USE_NATIVE_LOGGING
+ xSemaphoreTake(SafeLogMutex, portMAX_DELAY);
+#endif
+ int maxlen = sizeof(BLE_temp_log_data)-3;
+ if (thistask == TasmotaMainTask){
+ maxlen -= 13; // room for "-!MAINTHREAD!"
+ }
+ // assume this is thread safe - it may not be
+ va_list arg;
+ va_start(arg, formatP);
+ vsnprintf_P(BLE_temp_log_data, maxlen, formatP, arg);
+ va_end(arg);
+#ifdef USE_NATIVE_LOGGING
+ AddLog_P(loglevel, PSTR("%s"), BLE_temp_log_data);
+ return 1;
+#else
+ if (thistask == TasmotaMainTask){
+ loglevel = LOG_LEVEL_ERROR;
+ snprintf(BLE_temp_log_data + strlen(BLE_temp_log_data), 13, "-!MAINTHREAD!");
+ xSemaphoreGive(SafeLogMutex); // release mutex
+ AddLog_P(loglevel, PSTR("%s"), BLE_temp_log_data);
+ return 0;
+ }
+
+ if (freelogs.size()){
+ BLE_ESP32::safelogdata* logdata = (freelogs)[0];
+ freelogs.pop_front();
+ logdata->level = loglevel;
+ memcpy(logdata->log_data, BLE_temp_log_data, sizeof(logdata->log_data));
+ filledlogs.push_back(logdata);
+ added = 1;
+ } else {
+ // can't log it?
+ filledlogsOverflows++;
+ }
+ xSemaphoreGive(SafeLogMutex); // release mutex
+ return added;
+#endif
+}
+
+BLE_ESP32::safelogdata* GetSafeLog() {
+ xSemaphoreTake(SafeLogMutex, portMAX_DELAY);
+ if (filledlogs.size()){
+ BLE_ESP32::safelogdata* logdata = (filledlogs)[0];
+ filledlogs.pop_front();
+ xSemaphoreGive(SafeLogMutex); // release mutex
+ return logdata;
+ }
+ xSemaphoreGive(SafeLogMutex); // release mutex
+ return nullptr;
+}
+
+void ReleaseSafeLog(BLE_ESP32::safelogdata* logdata){
+ xSemaphoreTake(SafeLogMutex, portMAX_DELAY);
+ freelogs.push_back(logdata);
+ xSemaphoreGive(SafeLogMutex); // release mutex
+}
+*/
+
+/*********************************************************************************************\
+ * Helper functions
+\*********************************************************************************************/
+
+/**
+ * @brief Simple pair of functions to dump to a hex string.
+ *
+ */
+static const char h[] PROGMEM = "0123456789ABCDEF";
+void hex(char *dest, uint8_t v){
+ *(dest++) = h[(v>>4)&0xf];
+ *(dest++) = h[v&0xf];
+ *(dest) = 0;
+}
+
+// convert from binary to hex.
+// add a '+' on the end if not enough room.
+char * dump(char *dest, int maxchars, const uint8_t *src, int len){
+ int lenmax = (maxchars-1)/2;
+ int actuallen = 0;
+ for (actuallen = 0; actuallen < lenmax && actuallen < len; actuallen++){
+ if (actuallen < lenmax){
+ hex(dest+actuallen*2, src[actuallen]);
+ }
+ }
+ if (actuallen != len){
+ *(dest+(actuallen*2)) = '+';
+ *(dest+(actuallen*2)+1) = 0;
+ }
+ return dest;
+}
+
+// convert from a hex string to binary
+int fromHex(uint8_t *dest, const char *src, int maxlen){
+ int srclen = strlen(src)/2;
+ if (srclen > maxlen){
+ return 0;
+ }
+
+ for (int i = 0; i < srclen; i++){
+ char t[3];
+ if (!isalnum(src[i*2])){
+ return 0;
+ }
+ if (!isalnum(src[i*2 + 1])){
+ return 0;
+ }
+
+ t[0] = src[i*2];
+ t[1] = src[i*2 + 1];
+ t[2] = 0;
+
+ int byte = strtol(t, NULL, 16);
+ *dest++ = byte;
+ }
+ return srclen;
+}
+
+
+/**
+ * @brief Reverse an array of 6 bytes
+ *
+ * @param _mac a byte array of size 6 (typicalliy representing a MAC address)
+ */
+void ReverseMAC(uint8_t _mac[]){
+ uint8_t _reversedMAC[6];
+ for (uint8_t i=0; i<6; i++){
+ _reversedMAC[5-i] = _mac[i];
+ }
+ memcpy(_mac,_reversedMAC, sizeof(_reversedMAC));
+}
+
+
+
+
+/*********************************************************************************************\
+ * Advertisment details
+\*********************************************************************************************/
+
+//ble_advertisment_t BLEAdvertismentDetails;
+#define MAX_ADVERT_DETAILS 200
+char BLEAdvertismentDetailsJson[MAX_ADVERT_DETAILS];
+uint8_t BLEAdvertismentDetailsJsonSet = 0;
+uint8_t BLEAdvertismentDetailsJsonLost = 0;
+
+
+void setDetails(ble_advertisment_t *ad){
+ TasAutoMutex localmutex(&BLEOperationsRecursiveMutex, "BLESetDet");
+ if (BLEAdvertismentDetailsJsonSet){
+ BLEAdvertismentDetailsJsonLost = 1;
+ return;
+ }
+ char *p = BLEAdvertismentDetailsJson;
+ int maxlen = sizeof(BLEAdvertismentDetailsJson);
+ // just in case someone tries to read whilst we are writing
+ BLEAdvertismentDetailsJson[sizeof(BLEAdvertismentDetailsJson)-1] = 0;
+
+ *(p++) = '{';
+ maxlen--;
+ strcpy(p, "\"details\":{");
+ int len = strlen(p);
+ p += len;
+ maxlen -= len;
+
+ strcpy(p, "\"mac\":\"");
+ len = strlen(p);
+ p += len;
+ maxlen -= len;
+ dump(p, 14, ad->addr, 6);
+ len = strlen(p);
+ p += len;
+ maxlen -= len;
+ *(p++) = '\"'; maxlen--;
+
+ if (BLEAdvertismentDetailsJsonLost){
+ BLEAdvertismentDetailsJsonLost = 0;
+ strcpy(p, ",\"lost\":true");
+ len = strlen(p);
+ p += len;
+ maxlen -= len;
+ }
+
+ BLEAdvertisedDevice *advertisedDevice = ad->advertisedDevice;
+
+ uint8_t* payload = advertisedDevice->getPayload();
+ size_t payloadlen = advertisedDevice->getPayloadLength();
+ if (payloadlen && (maxlen > 30)){ // will truncate if not enough space
+ strcpy(p, ",\"p\":\"");
+ p += 6;
+ maxlen -= 6;
+ dump(p, maxlen-10, payload, payloadlen);
+ int len = strlen(p);
+ p += len;
+ maxlen -= len;
+ *(p++) = '\"'; maxlen--;
+ }
+
+ int svcdataCount = advertisedDevice->getServiceDataCount();
+ if (svcdataCount){
+ for (int i = 0; i < svcdataCount; i++){
+ NimBLEUUID UUID = advertisedDevice->getServiceDataUUID(i);//.getNative()->u16.value;
+ std::string ServiceData = advertisedDevice->getServiceData(i);
+
+ size_t ServiceDataLength = ServiceData.length();
+ const uint8_t *serviceData = (const uint8_t *)ServiceData.data();
+
+ //char svcuuidstr[20];
+ std::string strUUID = UUID;
+
+ int svclen = strUUID.length();
+ svclen++; // ,
+ svclen += 3; // "":
+ svclen += ServiceDataLength*2;
+ svclen += 3; // ""}
+
+ if (maxlen -10 > svclen){
+ *(p++) = ',';
+ *(p++) = '\"';
+ strcpy(p, strUUID.c_str());
+ p += strUUID.length();
+ *(p++) = '\"';
+ *(p++) = ':';
+ *(p++) = '\"';
+ dump(p, ServiceDataLength*2+2, (uint8_t*)serviceData, ServiceDataLength);
+ int len = strlen(p);
+ p += len;
+ *(p++) = '\"';
+ maxlen -= len;
+ }
+ }
+ }
+
+ *(p++) = '}'; maxlen--;
+ *(p++) = '}'; maxlen--;
+ *(p++) = 0; maxlen--;
+
+ BLEAdvertismentDetailsJsonSet = 1;
+}
+
+
+// call from main thread only!
+// post advertisment detail if available, then clear.
+void postAdvertismentDetails(){
+// if (TasmotaGlobal.ota_state_flag) return;
+
+ TasAutoMutex localmutex(&BLEOperationsRecursiveMutex, "BLEPostAdd");
+ if (BLEAdvertismentDetailsJsonSet){
+ strncpy(TasmotaGlobal.mqtt_data, BLEAdvertismentDetailsJson, sizeof(TasmotaGlobal.mqtt_data));
+ TasmotaGlobal.mqtt_data[sizeof(TasmotaGlobal.mqtt_data)-1] = 0;
+ BLEAdvertismentDetailsJsonSet = 0;
+ // we got the data, give before MQTT call.
+ localmutex.give();
+ // no retain - this is present devices, not historic
+ MqttPublishPrefixTopic_P(TELE, PSTR("BLE"), 0);
+ } else {
+ }
+}
+
+
+
+/*********************************************************************************************\
+ * Classes
+\*********************************************************************************************/
+
+// does not really take any action
+class BLESensorCallback : public NimBLEClientCallbacks {
+ void onConnect(NimBLEClient* pClient) {
+#ifdef BLE_ESP32_DEBUG
+ if (BLEDebugMode > 0) AddLog(LOG_LEVEL_DEBUG,PSTR("BLE: onConnect %s"), ((std::string)pClient->getPeerAddress()).c_str());
+#endif
+ }
+ void onDisconnect(NimBLEClient* pClient) {
+#ifdef BLE_ESP32_DEBUG
+ if (BLEDebugMode > 0) AddLog(LOG_LEVEL_DEBUG,PSTR("BLE: onDisconnect %s"), ((std::string)pClient->getPeerAddress()).c_str());
+#endif
+ }
+ bool onConnParamsUpdateRequest(NimBLEClient* pClient, const ble_gap_upd_params* params) {
+#ifdef BLE_ESP32_DEBUG
+ if (BLEDebugMode > 0) AddLog(LOG_LEVEL_DEBUG,PSTR("BLE: onConnParamsUpdateRequest %s"), ((std::string)pClient->getPeerAddress()).c_str());
+#endif
+
+// if(params->itvl_min < 24) { /** 1.25ms units */
+// return false;
+// } else if(params->itvl_max > 300) { /** 1.25ms units */
+// return false;
+// } else if(params->latency > 2) { /** Number of intervals allowed to skip */
+// return false;
+// } else if(params->supervision_timeout > 6000) { /** 10ms units */
+// return false;
+// }
+
+/*
+ if(params->itvl_min < 24) { // 1.25ms units
+ return false;
+ } else if(params->itvl_max > 40) { // 1.25ms units
+ return false;
+ } else if(params->latency > 2) { // Number of intervals allowed to skip
+ return false;
+ } else if(params->supervision_timeout > 200) { /// 10ms units
+ return false;
+ }
+
+ return true;
+*/
+ // just always reject thiers, and use ours.
+ return false;
+
+ }
+};
+
+static BLESensorCallback clientCB;
+
+
+class BLEAdvCallbacks: public NimBLEAdvertisedDeviceCallbacks {
+ void onResult(NimBLEAdvertisedDevice* advertisedDevice) {
+ TasAutoMutex localmutex(&BLEOperationsRecursiveMutex, "BLEAddCB");
+ uint64_t now = esp_timer_get_time();
+ BLEScanLastAdvertismentAt = now; // note the time of the last advertisment
+
+ uint32_t totalCount = BLEAdvertisment.totalCount;
+ memset(&BLEAdvertisment, 0, sizeof(BLEAdvertisment));
+ BLEAdvertisment.totalCount = totalCount+1;
+
+ BLEAdvertisment.advertisedDevice = advertisedDevice;
+
+ // keep sign - char seems unsigned
+ int8_t RSSI = (char)advertisedDevice->getRSSI();
+ NimBLEAddress address = advertisedDevice->getAddress();
+
+ BLEAdvertisment.addrtype = address.getType();
+
+ memcpy(BLEAdvertisment.addr, address.getNative(), 6);
+ ReverseMAC(BLEAdvertisment.addr);
+
+ BLEAdvertisment.RSSI = RSSI;
+
+ char addrstr[20];
+ dump(addrstr, 20, BLEAdvertisment.addr, 6);
+
+ // this mjust survive the scope of the callbacks
+ std::string name = "";
+ const char *namestr = name.c_str();
+ if (advertisedDevice->haveName()){
+ name = advertisedDevice->getName();
+ namestr = name.c_str();
+ strncpy(BLEAdvertisment.name, namestr, sizeof(BLEAdvertisment.name)-1);
+ BLEAdvertisment.name[sizeof(BLEAdvertisment.name)-1] = 0;
+ }
+
+
+ // log this device safely
+ if (BLEAdvertisment.addrtype <= BLEAddressFilter){
+ addSeenDevice(BLEAdvertisment.addr, BLEAdvertisment.addrtype, BLEAdvertisment.name, BLEAdvertisment.RSSI);
+ }
+
+ if (BLEDetailsRequest){
+ switch (BLEDetailsRequest){
+ case 1:{ // one advert for one device
+ BLEDetailsRequest = 0; // only one requested if 2, it's a request all
+ if (!memcmp(BLEDetailsMac, BLEAdvertisment.addr, 6)){
+ setDetails(&BLEAdvertisment);
+ }
+ } break;
+ case 2:{ // all adverts for one device - may not get them all
+ if (!memcmp(BLEDetailsMac, BLEAdvertisment.addr, 6)){
+ setDetails(&BLEAdvertisment);
+ }
+ } break;
+ case 3:{ // all adverts for ALL DEVICES - may not get them all
+ // ignore from here on if filtered on addrtype
+ if (BLEAdvertisment.addrtype > BLEAddressFilter){
+ return;
+ }
+ setDetails(&BLEAdvertisment);
+ } break;
+ }
+ }
+
+ // ignore from here on if filtered on addrtype
+ if (BLEAdvertisment.addrtype > BLEAddressFilter){
+ return;
+ }
+
+ // call anyone who asked about advertisements
+ for (int i = 0; i < advertismentCallbacks.size(); i++) {
+ try {
+ ADVERTISMENT_CALLBACK* pFN;
+ pFN = advertismentCallbacks[i];
+ int res = pFN(&BLEAdvertisment);
+
+ // if this callback wants to stop here, then do so.
+ if (1 == res) break;
+
+ // if this callback wants to kill this device
+ if (2 == res) {
+ //BLEScan->erase(address);
+ }
+ } catch(const std::exception& e){
+#ifdef BLE_ESP32_DEBUG
+ AddLog(LOG_LEVEL_ERROR,PSTR("BLE: exception in advertismentCallbacks"));
+#endif
+ }
+ }
+
+ }
+};
+
+
+static BLEAdvCallbacks BLEScanCallbacks;
+static BLESensorCallback BLESensorCB;
+
+/*********************************************************************************************\
+ * BLE callback functions
+\*********************************************************************************************/
+
+static void BLEscanEndedCB(NimBLEScanResults results){
+
+#ifdef BLE_ESP32_DEBUG
+ if (BLEDebugMode > 0) AddLog(LOG_LEVEL_DEBUG,PSTR("BLE: Scan ended"));
+#endif
+ for (int i = 0; i < scancompleteCallbacks.size(); i++){
+ try {
+ SCANCOMPLETE_CALLBACK *pFn = scancompleteCallbacks[i];
+ int callbackres = pFn(results);
+#ifdef BLE_ESP32_DEBUG
+ if (BLEDebugMode > 0) AddLog(LOG_LEVEL_DEBUG,PSTR("BLE: scancompleteCallbacks %d %d"), i, callbackres);
+#endif
+ } catch(const std::exception& e){
+#ifdef BLE_ESP32_DEBUG
+ AddLog(LOG_LEVEL_ERROR,PSTR("BLE: exception in operationsCallbacks"));
+#endif
+ }
+ }
+
+ BLERunningScan = 2;
+ BLEScanToEndBefore = 0L;
+ BLEScanCount++;
+}
+
+
+///////////////////////////////////////////////////////////////////////
+// !!!!!!!!!!@@@@@@@@@@@@@@@@
+// NOTE: this can callback BEFORE the write is completed.
+// so we should not do any actions against the device if we can help it
+// this COULD be the reason for the BLE stack hanging up....
+///////////////////////////////////////////////////////////////////////
+static void BLEGenNotifyCB(NimBLERemoteCharacteristic* pRemoteCharacteristic, uint8_t* pData, size_t length, bool isNotify){
+ NimBLEClient *pRClient;
+
+ if (!pRemoteCharacteristic){
+#ifdef BLE_ESP32_DEBUG
+ AddLog(LOG_LEVEL_DEBUG,PSTR("BLE: Notify: no remote char!!??"));
+#endif
+ return;
+ }
+
+
+#ifdef BLE_ESP32_DEBUG
+ if (BLEDebugMode > 0) AddLog(LOG_LEVEL_DEBUG,PSTR("BLE: Notified length: %u"),length);
+#endif
+ // find the operation this is associated with
+ NimBLERemoteService *pSvc = pRemoteCharacteristic->getRemoteService();
+
+ if (!pSvc){
+#ifdef BLE_ESP32_DEBUG
+ AddLog(LOG_LEVEL_ERROR,PSTR("BLE: Notify: no remote service found"));
+#endif
+ return;
+ }
+
+ pRClient = pSvc->getClient();
+ if (!pRClient){
+#ifdef BLE_ESP32_DEBUG
+ AddLog(LOG_LEVEL_ERROR,PSTR("BLE: Notify: no remote client!!??"));
+#endif
+ return;
+ }
+ NimBLEAddress devaddr = pRClient->getPeerAddress();
+
+ generic_sensor_t *thisop = nullptr;
+ {
+ // make sure we are not disturbed
+ TasAutoMutex localmutex(&BLEOperationsRecursiveMutex, "BLENotif");
+
+ for (int i = 0; i < currentOperations.size(); i++){
+ generic_sensor_t *op = currentOperations[i];
+ if (!op){
+#ifdef BLE_ESP32_DEBUG
+ AddLog(LOG_LEVEL_ERROR,PSTR("BLE: Notify: null op in currentOperations!!??"));
+#endif
+ } else {
+ if (devaddr == op->addr){
+ thisop = op;
+ break;
+ }
+ }
+ }
+ }
+
+ // we'll try without
+ //pRemoteCharacteristic->unsubscribe();
+
+ if (!thisop){
+#ifdef BLE_ESP32_DEBUG
+ AddLog(LOG_LEVEL_DEBUG,PSTR("BLE: no op for notify"));
+#endif
+ return;
+ }
+
+ for (int i = 0; i < length && i < sizeof(thisop->dataNotify); i++){
+ thisop->dataNotify[i] = pData[i];
+ }
+ thisop->notifylen = length;
+ if (length > sizeof(thisop->dataNotify)){
+ thisop->notifytruncated = 1;
+ } else {
+ thisop->notifytruncated = 0;
+ }
+ // we will NOT change the state here...
+ // rely on thisop->notifylen as a flag notify is complete
+ //thisop->state = GEN_STATE_NOTIFIED;
+
+ // this triggers our notify complete, either at the end of read/write, or next 1s cycle.
+ thisop->notifytimer = 0;
+
+}
+
+
+
+
+/*********************************************************************************************\
+ * functions for registering callbacks against the driver
+\*********************************************************************************************/
+
+void registerForAdvertismentCallbacks(const char *tag, BLE_ESP32::ADVERTISMENT_CALLBACK* pFn){
+#ifdef BLE_ESP32_DEBUG
+ AddLog(LOG_LEVEL_INFO,PSTR("BLE: registerForAdvertismentCallbacks %s:%x"), tag, (uint32_t) pFn);
+#endif
+ advertismentCallbacks.push_back(pFn);
+}
+
+void registerForOpCallbacks(const char *tag, BLE_ESP32::OPCOMPLETE_CALLBACK* pFn){
+#ifdef BLE_ESP32_DEBUG
+ AddLog(LOG_LEVEL_INFO,PSTR("BLE: registerForOpCallbacks %s:%x"), tag, (uint32_t) pFn);
+#endif
+ operationsCallbacks.push_back(pFn);
+}
+
+void registerForScanCallbacks(const char *tag, BLE_ESP32::SCANCOMPLETE_CALLBACK* pFn){
+#ifdef BLE_ESP32_DEBUG
+ AddLog(LOG_LEVEL_INFO,PSTR("BLE: registerForScnCallbacks %s:%x"), tag, (uint32_t) pFn);
+#endif
+ scancompleteCallbacks.push_back(pFn);
+}
+
+
+/*********************************************************************************************\
+ * init NimBLE
+\*********************************************************************************************/
+static void BLEPreInit(void) {
+ BLEInitState = 0;
+ prepOperation = nullptr;
+}
+
+
+static void BLEInit(void) {
+ if (BLEMode == BLEModeDisabled) return;
+
+ if (BLEInitState) { return; }
+
+ if (TasmotaGlobal.global_state.wifi_down) { return; }
+
+ TasmotaGlobal.wifi_stay_asleep = true;
+ if (WiFi.getSleep() == false) {
+ AddLog(LOG_LEVEL_DEBUG,PSTR("%s: Put WiFi modem in sleep mode"),"BLE");
+ WiFi.setSleep(true); // Sleep
+ }
+
+ // this is only for testing, does nothin if examples are undefed
+ installExamples();
+ //initSafeLog();
+ initSeenDevices();
+
+ uint64_t now = esp_timer_get_time();
+ BLEScanLastAdvertismentAt = now; // initialise the time of the last advertisment
+ BLELastLoopTime = now;
+
+ BLEInitState = 1;
+
+ // dont start of disabled
+ BLEMasterEnable = Settings.flag5.mi32_enable;
+ if (!BLEMasterEnable) return;
+
+
+ StartBLE();
+
+ return;
+}
+
+/*********************************************************************************************\
+ * Task section
+\*********************************************************************************************/
+
+static void BLEOperationTask(void *pvParameters);
+
+static void BLEStartOperationTask(){
+ if (BLERunning == false){
+#ifdef BLE_ESP32_DEBUG
+ AddLog(LOG_LEVEL_DEBUG,PSTR("BLE: %s: Start operations"),D_CMND_BLE);
+#endif
+ BLERunning = true;
+
+ xTaskCreatePinnedToCore(
+ BLE_ESP32::BLEOperationTask, /* Function to implement the task */
+ "BLEOperationTask", /* Name of the task */
+ 4096, /* Stack size in bytes */
+ NULL, /* Task input parameter */
+ 0, /* Priority of the task */
+ NULL, /* Task handle. */
+#ifdef CONFIG_FREERTOS_UNICORE
+ 0); /* Core where the task should run */
+#else
+ 1); /* Core where the task should run */
+#endif
+ }
+}
+
+
+static void BLETaskStopStartNimBLE(NimBLEClient **ppClient, bool start = true){
+
+ if (*ppClient){
+ AddLog(LOG_LEVEL_ERROR,PSTR("BLE: Task:Stopping NimBLE"));
+
+ (*ppClient)->setClientCallbacks(nullptr, false);
+
+ try {
+ if ((*ppClient)->isConnected()){
+#ifdef BLE_ESP32_DEBUG
+ AddLog(LOG_LEVEL_INFO,PSTR("BLE: disconnecting connected client"));
+#endif
+ (*ppClient)->disconnect();
+ }
+ NimBLEDevice::deleteClient((*ppClient));
+ (*ppClient) = nullptr;
+#ifdef BLE_ESP32_DEBUG
+ AddLog(LOG_LEVEL_INFO,PSTR("BLE: deleted client"));
+#endif
+ } catch(const std::exception& e){
+#ifdef BLE_ESP32_DEBUG
+ AddLog(LOG_LEVEL_ERROR,PSTR("BLE: Stopping NimBLE:exception in delete client"));
+#endif
+ }
+
+ if (ble32Scan){
+ ble32Scan->setAdvertisedDeviceCallbacks(nullptr,true);
+ ble32Scan->stop();
+ ble32Scan = nullptr;
+ }
+
+ // wait second
+ vTaskDelay(100/ portTICK_PERIOD_MS);
+ NimBLEDevice::deinit(true);
+ }
+ BLERunningScan = 0;
+
+ if (start){
+ AddLog(LOG_LEVEL_INFO,PSTR("BLE: BLETask:Starting NimBLE"));
+ NimBLEDevice::init("BLE_ESP32");
+
+ *ppClient = NimBLEDevice::createClient();
+ (*ppClient)->setClientCallbacks(&clientCB, false);
+ /** Set initial connection parameters: These settings are 15ms interval, 0 latency, 120ms timout.
+ * These settings are safe for 3 clients to connect reliably, can go faster if you have less
+ * connections. Timeout should be a multiple of the interval, minimum is 100ms.
+ * Min interval: 12 * 1.25ms = 15, Max interval: 12 * 1.25ms = 15, 0 latency, 51 * 10ms = 510ms timeout
+ */
+ (*ppClient)->setConnectionParams(12,12,0,51);
+ /** Set how long we are willing to wait for the connection to complete (seconds), default is 30. */
+ (*ppClient)->setConnectTimeout(15);
+ }
+
+ uint64_t now = esp_timer_get_time();
+
+ // don't restart because of these for a while
+ BLELastLoopTime = now; // initialise the time of the last advertisment
+ BLEScanLastAdvertismentAt = now; // initialise the time of the last advertisment
+
+}
+
+int BLETaskStartScan(int time){
+ if (!ble32Scan) return -1;
+ if (BLEMode == BLEModeDisabled) return -4;
+ // don't scan whilst OTA in progress
+ if (BLEOtaStallBLE) return -5;
+ if (currentOperations.size()) return -3;
+
+ if (BLERunningScan) {
+ // if we hit 2, wait one more time before starting
+ if (BLERunningScan == 2){
+ // wait 100ms
+ vTaskDelay(100/ portTICK_PERIOD_MS);
+ BLERunningScan = 0;
+ }
+ return -2;
+ }
+
+#ifdef BLE_ESP32_DEBUG
+ if (BLEDebugMode > 0) AddLog(LOG_LEVEL_DEBUG,PSTR("BLE: BLETask: Startscan"));
+#endif
+ //vTaskDelay(500/ portTICK_PERIOD_MS);
+ ble32Scan->setActiveScan(BLEScanActiveMode ? 1: 0);
+
+
+ // seems we could get the callback within the start call....
+ // so set these before starting
+ BLERunningScan = 1;
+ BLEScanStartedAt = esp_timer_get_time();
+ if (BLETriggerScan){
+ time = BLETriggerScan;
+ BLETriggerScan = 0;
+ }
+ ble32Scan->start(time, BLEscanEndedCB, (BLEScanActiveMode == 2)); // 20s scans, restarted when then finish
+ //vTaskDelay(500/ portTICK_PERIOD_MS);
+ return 0;
+}
+
+// this runs one operation
+// if the passed pointer is empty, it tries to get a next one.
+static void BLETaskRunCurrentOperation(BLE_ESP32::generic_sensor_t** pCurrentOperation, NimBLEClient **ppClient){
+ if (!pCurrentOperation) return;
+
+ NimBLEClient *pClient = *ppClient;
+ if (!*pCurrentOperation) {
+ *pCurrentOperation = nextOperation(&queuedOperations);
+ if (*pCurrentOperation){
+#ifdef BLE_ESP32_DEBUG
+ if (BLEDebugMode > 0) AddLog(LOG_LEVEL_DEBUG,PSTR("BLE: BLETask: new currentOperation"));
+#endif
+ BLEOpCount++;
+ generic_sensor_t* temp = *pCurrentOperation;
+ //this will null it out, so save and restore.
+ addOperation(¤tOperations, pCurrentOperation);
+ *pCurrentOperation = temp;
+ }
+ }
+ if (!*pCurrentOperation) return;
+
+
+
+ // if awaiting notification
+ if ((*pCurrentOperation)->notifytimer){
+ // if it took too long, then disconnect
+ uint64_t now = esp_timer_get_time();
+ uint64_t diff = now - (*pCurrentOperation)->notifytimer;
+ diff = diff/1000;
+ if (diff > 20000){ // 20s
+#ifdef BLE_ESP32_DEBUG
+ AddLog(LOG_LEVEL_DEBUG,PSTR("BLE: BLETask: notify timeout"));
+#endif
+ (*pCurrentOperation)->state = GEN_STATE_FAILED_NOTIFYTIMEOUT;
+ (*pCurrentOperation)->notifytimer = 0;
+ }
+ // we can't process any further, because op will be at state readdone or writedone
+ return;
+ }
+
+
+ switch((*pCurrentOperation)->state){
+ case GEN_STATE_WAITINDICATE:
+ case GEN_STATE_WAITNOTIFY:
+ //(*pCurrentOperation)->notifytimer == 0 at this point, so must be done
+ (*pCurrentOperation)->state = GEN_STATE_NOTIFIED;
+ // just stay here until this is removed by the main thread
+#ifdef BLE_ESP32_DEBUG
+ if (BLEDebugMode > 0) AddLog(LOG_LEVEL_DEBUG,PSTR("BLE: BLETask: notify operation complete"));
+#endif
+ BLE_ESP32::BLETaskRunTaskDoneOperation(pCurrentOperation, ppClient);
+ pClient = *ppClient;
+ return;
+ break;
+ case GEN_STATE_READDONE:
+ case GEN_STATE_WRITEDONE:
+ case GEN_STATE_NOTIFIED: // - may have completed DURING our read/write to get here
+ // just stay here until this is removed by the main thread
+#ifdef BLE_ESP32_DEBUG
+ if (BLEDebugMode > 0) AddLog(LOG_LEVEL_DEBUG,PSTR("BLE: BLETask: operation complete"));
+#endif
+ BLE_ESP32::BLETaskRunTaskDoneOperation(pCurrentOperation, ppClient);
+ pClient = *ppClient;
+ return;
+ break;
+
+ case GEN_STATE_START:
+ // continue to start the process here.
+ break;
+
+ default:
+ break;
+ }
+
+
+ if (!*pCurrentOperation) return;
+
+ if ((*pCurrentOperation)->state <= GEN_STATE_FAILED){
+#ifdef BLE_ESP32_DEBUG
+ AddLog(LOG_LEVEL_ERROR,PSTR("BLE: BLETask: op failed %d"), (*pCurrentOperation)->state);
+#endif
+ BLE_ESP32::BLETaskRunTaskDoneOperation(pCurrentOperation, ppClient);
+ pClient = *ppClient;
+ return;
+ }
+
+ if ((*pCurrentOperation)->state != GEN_STATE_START){
+ return;
+ }
+
+ if (pClient->isConnected()){
+ // don't do anything if we are still connected
+#ifdef BLE_ESP32_DEBUG
+ AddLog(LOG_LEVEL_DEBUG,PSTR("BLE: BLETask: still connected"));
+#endif
+ return;
+ }
+
+
+ // if we managed to run operations back to back with long connection timeouts,
+ // then we may NOT see advertisements.
+ // so to prevent triggering of the advert timeout restart mechanism,
+ // set the last advert time each time we start an operation
+ uint64_t now = esp_timer_get_time();
+ BLEScanLastAdvertismentAt = now; // initialise the time of the last advertisment
+
+
+ generic_sensor_t* op = *pCurrentOperation;
+
+ int newstate = GEN_STATE_STARTED;
+ op->state = GEN_STATE_STARTED;
+
+#ifdef BLE_ESP32_DEBUG
+ if (BLEDebugMode > 0) AddLog(LOG_LEVEL_DEBUG,PSTR("BLE: BLETask: attempt connect %s"), ((std::string)op->addr).c_str());
+#endif
+
+ if (!op->serviceUUID.bitSize()){
+ op->state = GEN_STATE_FAILED_NOSERVICE;
+ return;
+ }
+
+ if (pClient->connect(op->addr, true)) {
+
+#ifdef BLE_ESP32_DEBUG
+ if (BLEDebugMode > 0) AddLog(LOG_LEVEL_DEBUG,PSTR("BLE: connected %s -> getservice"), ((std::string)op->addr).c_str());
+#endif
+ NimBLERemoteService *pService = pClient->getService(op->serviceUUID);
+ int waitNotify = false;
+ int notifystate = 0;
+ op->notifytimer = 0L;
+
+ if (pService != nullptr) {
+#ifdef BLE_ESP32_DEBUG
+ if (BLEDebugMode > 0) AddLog(LOG_LEVEL_DEBUG,PSTR("BLE: got service"));
+#endif
+ // pre-set to fail if no operations requested
+ //newstate = GEN_STATE_FAILED_NOREADWRITE;
+
+ ///////////////////////////////////////////////////////////////////////
+ // !!!!!!!!!!@@@@@@@@@@@@@@@@
+ // NOTE: Notify callback can happen BEFORE the read/write is completed.
+ // this COULD be the reason for the BLE stack hanging up....
+ ///////////////////////////////////////////////////////////////////////
+
+ // if we have been asked to get a notification
+ if (op->notificationCharacteristicUUID.bitSize()) {
+ NimBLERemoteCharacteristic *pNCharacteristic =
+ pService->getCharacteristic(op->notificationCharacteristicUUID);
+ if (pNCharacteristic != nullptr) {
+#ifdef BLE_ESP32_DEBUG
+ if (BLEDebugMode > 0) AddLog(LOG_LEVEL_DEBUG,PSTR("BLE: got notify characteristic"));
+#endif
+ op->notifylen = 0;
+ if(pNCharacteristic->canNotify()) {
+ if(pNCharacteristic->subscribe(true, BLE_ESP32::BLEGenNotifyCB)) {
+#ifdef BLE_ESP32_DEBUG
+ if (BLEDebugMode > 0) AddLog(LOG_LEVEL_DEBUG,PSTR("BLE: subscribe for notify"));
+#endif
+ uint64_t now = esp_timer_get_time();
+ op->notifytimer = now;
+ // this will get changed to read or write,
+ // but here in case it's notify only (can that happen?)
+ notifystate = GEN_STATE_WAITNOTIFY;
+ waitNotify = true;
+ } else {
+#ifdef BLE_ESP32_DEBUG
+ AddLog(LOG_LEVEL_ERROR,PSTR("BLE: failed subscribe for notify"));
+#endif
+ newstate = GEN_STATE_FAILED_NOTIFY;
+ }
+ } else {
+ if(pNCharacteristic->canIndicate()) {
+ if(pNCharacteristic->subscribe(false, BLE_ESP32::BLEGenNotifyCB)) {
+#ifdef BLE_ESP32_DEBUG
+ AddLog(LOG_LEVEL_DEBUG,PSTR("BLE: subscribe for indicate"));
+#endif
+ notifystate = GEN_STATE_WAITINDICATE;
+ uint64_t now = esp_timer_get_time();
+ op->notifytimer = now;
+ waitNotify = true;
+ } else {
+#ifdef BLE_ESP32_DEBUG
+ AddLog(LOG_LEVEL_ERROR,PSTR("BLE: failed subscribe for indicate"));
+#endif
+ newstate = GEN_STATE_FAILED_INDICATE;
+ }
+ } else {
+ newstate = GEN_STATE_FAILED_CANTNOTIFYORINDICATE;
+#ifdef BLE_ESP32_DEBUG
+ AddLog(LOG_LEVEL_ERROR,PSTR("BLE: characteristic can't notify"));
+#endif
+ }
+ }
+ } else {
+ newstate = GEN_STATE_FAILED_NONOTIFYCHAR;
+#ifdef BLE_ESP32_DEBUG
+ AddLog(LOG_LEVEL_ERROR,PSTR("BLE: notify characteristic not found"));
+#endif
+ }
+
+ // force the 'error' of the notify coming in before the read/write for testing
+ //vTaskDelay(1000/ portTICK_PERIOD_MS);
+ } // no supplied notify char is ok
+
+ // this will only happen if you ask for a notify char which is not there?
+ if (!(newstate <= GEN_STATE_FAILED)){
+ if (op->characteristicUUID.bitSize()) {
+ // read or write characteristic - we always need this?
+ NimBLERemoteCharacteristic *pCharacteristic = nullptr;
+
+ pCharacteristic = pService->getCharacteristic(op->characteristicUUID);
+ if (pCharacteristic != nullptr) {
+#ifdef BLE_ESP32_DEBUG
+ if (BLEDebugMode > 0) AddLog(LOG_LEVEL_DEBUG,PSTR("BLE: got read/write characteristic"));
+#endif
+ newstate = GEN_STATE_FAILED_NOREADWRITE; // overwritten on failure
+
+ if (op->readlen){
+ if(pCharacteristic->canRead()) {
+ std::string value = pCharacteristic->readValue();
+ op->readlen = value.length();
+ memcpy(op->dataRead, value.data(),
+ (op->readlen > sizeof(op->dataRead))?
+ sizeof(op->dataRead):
+ op->readlen);
+ if (op->readlen > sizeof(op->dataRead)){
+ op->readtruncated = 1;
+ } else {
+ op->readtruncated = 0;
+ }
+ if (op->readmodifywritecallback){
+ READ_CALLBACK *pFn = (READ_CALLBACK *)op->readmodifywritecallback;
+#ifdef BLE_ESP32_DEBUG
+ if (BLEDebugMode > 0) AddLog(LOG_LEVEL_DEBUG,PSTR("BLE: read characteristic with readmodifywritecallback"));
+#endif
+ pFn(op);
+ } else {
+#ifdef BLE_ESP32_DEBUG
+ if (BLEDebugMode > 0) AddLog(LOG_LEVEL_DEBUG,PSTR("BLE: read characteristic"));
+#endif
+ }
+
+ // only change it to a 'finished' state if we really are
+ if (!waitNotify && !op->writelen) newstate = GEN_STATE_READDONE;
+
+ } else {
+ newstate = GEN_STATE_FAILED_CANTREAD;
+ }
+ }
+ if (op->writelen){
+ if(pCharacteristic->canWrite()) {
+ if (!pCharacteristic->writeValue(op->dataToWrite, op->writelen, true)){
+ newstate = GEN_STATE_FAILED_WRITE;
+#ifdef BLE_ESP32_DEBUG
+ AddLog(LOG_LEVEL_DEBUG,PSTR("BLE: characteristic write fail"));
+#endif
+ } else {
+ if (!waitNotify) newstate = GEN_STATE_WRITEDONE;
+#ifdef BLE_ESP32_DEBUG
+ if (BLEDebugMode > 0) AddLog(LOG_LEVEL_DEBUG,PSTR("BLE: write characteristic"));
+#endif
+ }
+ } else {
+ newstate = GEN_STATE_FAILED_CANTWRITE;
+ }
+ }
+ // print or do whatever you need with the value
+
+ } else {
+ newstate = GEN_STATE_FAILED_NO_RW_CHAR;
+#ifdef BLE_ESP32_DEBUG
+ AddLog(LOG_LEVEL_DEBUG,PSTR("BLE: r/w characteristic not found"));
+#endif
+ }
+ }
+ }
+
+
+ // disconnect if not waiting for notify,
+ if (!op->notifytimer){
+ if (waitNotify){
+ vTaskDelay(50/ portTICK_PERIOD_MS);
+ // must have completed during our read/write operation
+ newstate = GEN_STATE_NOTIFIED;
+ }
+ } else {
+ newstate = notifystate;
+ }
+ } else {
+ newstate = GEN_STATE_FAILED_NOSERVICE;
+ // failed to get a service
+#ifdef BLE_ESP32_DEBUG
+ AddLog(LOG_LEVEL_DEBUG,PSTR("BLE: failed - svc not on device?"));
+#endif
+ }
+
+ } else { // connect itself failed
+ newstate = GEN_STATE_FAILED_CONNECT;
+#ifdef NIMBLE_CLIENT_HAS_GETRESULT
+ int rc = pClient->getResult();
+
+ switch (rc){
+ case (0x0200+BLE_ERR_CONN_LIMIT ):
+#ifdef BLE_ESP32_DEBUG
+ AddLog(LOG_LEVEL_ERROR,PSTR("BLE: Hit connection limit? - restarting NimBLE"));
+#endif
+ BLERestartNimBLE = 1;
+ BLERestartBLEReason = BLE_RESTART_BLE_REASON_CONN_LIMIT;
+ break;
+ case (0x0200+BLE_ERR_ACL_CONN_EXISTS):
+#ifdef BLE_ESP32_DEBUG
+ AddLog(LOG_LEVEL_ERROR,PSTR("BLE: Connection exists? - restarting NimBLE"));
+#endif
+ BLERestartNimBLE = 1;
+ BLERestartBLEReason = BLE_RESTART_BLE_REASON_CONN_EXISTS;
+ break;
+ }
+#endif
+
+ // failed to connect
+#ifdef BLE_ESP32_DEBUG
+ AddLog(LOG_LEVEL_DEBUG,PSTR("BLE: failed to connect to device %d"), rc);
+#endif
+ }
+ op->state = newstate;
+}
+
+
+
+// this disconnects from a device if necessary, and then
+// moves the operation from 'currentOperations' to 'completedOperations'.
+
+// for safety's sake, only call from the run task
+static void BLETaskRunTaskDoneOperation(BLE_ESP32::generic_sensor_t** op, NimBLEClient **ppClient){
+ try {
+ if ((*ppClient)->isConnected()){
+#ifdef BLE_ESP32_DEBUG
+ if (BLEDebugMode > 0) AddLog(LOG_LEVEL_DEBUG,PSTR("BLE: runTaskDoneOperation: disconnecting connected client"));
+#endif
+ (*ppClient)->disconnect();
+ // wait for 1/2 second after disconnect
+ int waits = 0;
+ do {
+ vTaskDelay(500/ portTICK_PERIOD_MS);
+ if (waits) {
+ //(*ppClient)->disconnect();
+ // we will stall here forever!!! - as testing
+#ifdef BLE_ESP32_DEBUG
+ AddLog(LOG_LEVEL_ERROR,PSTR("BLE: wait discon%d"), waits);
+#endif
+ vTaskDelay(500/ portTICK_PERIOD_MS);
+ }
+ waits++;
+ if (waits == 5){
+ int conn_id = (*ppClient)->getConnId();
+ ble_gap_conn_broken(conn_id, -1);
+#ifdef BLE_ESP32_DEBUG
+ AddLog(LOG_LEVEL_ERROR,PSTR("BLE: wait discon%d - kill connection"), waits);
+#endif
+ }
+ if (waits == 60){
+ AddLog(LOG_LEVEL_ERROR,PSTR("BLE: >60s waiting -> BLE Failed, restart Tasmota %d"), waits);
+ BLEStop = 1;
+ BLEStopAt = esp_timer_get_time();
+
+ BLERestartTasmota = 10;
+ BLERestartTasmotaReason = BLE_RESTART_TEAMOTA_REASON_BLE_DISCONNECT_FAIL;
+ break;
+ }
+ } while ((*ppClient)->isConnected());
+ }
+ } catch(const std::exception& e){
+#ifdef BLE_ESP32_DEBUG
+ AddLog(LOG_LEVEL_ERROR,PSTR("BLE: runTaskDoneOperation: exception in disconnect"));
+#endif
+ }
+
+
+ {
+ TasAutoMutex localmutex(&BLEOperationsRecursiveMutex, "BLEDoneOp");
+
+ // find this operation in currentOperations, and remove it.
+ for (int i = 0; i < currentOperations.size(); i++){
+ if (currentOperations[i]->opid == (*op)->opid){
+ currentOperations.erase(currentOperations.begin() + i);
+ break;
+ }
+ }
+ }
+
+
+ // by adding it to this list, this will cause it to be sent to MQTT
+#ifdef BLE_ESP32_DEBUG
+ if (BLEDebugMode > 0) AddLog(LOG_LEVEL_DEBUG,PSTR("BLE: runTaskDoneOperation: add to completedOperations"));
+#endif
+ addOperation(&completedOperations, op);
+ return;
+}
+
+
+
+
+// this IS as task.
+// we MAY be able to run a few of these simultaneously, but this is not yet tested.
+// and probably not required. But everything is there to do so....
+static void BLEOperationTask(void *pvParameters){
+
+ BLELoopCount = 0;
+ BLEOpCount = 0;;
+
+ uint32_t timer = 0;
+ // operation which is currently in progress in THIS TASK
+ generic_sensor_t* currentOperation = nullptr;
+
+ NimBLEClient *pClient = nullptr;
+ BLE_ESP32::BLETaskStopStartNimBLE(&pClient);
+
+ for(;;){
+ BLELastLoopTime = esp_timer_get_time();
+ BLELoopCount++;
+
+ BLE_ESP32::BLETaskRunCurrentOperation(¤tOperation, &pClient);
+
+ // start a scan if possible
+ if ((BLEMode == BLEModeRegularScan) || (BLETriggerScan)){
+ BLEScan* lastScan = ble32Scan;
+ ble32Scan = NimBLEDevice::getScan();
+ if (lastScan != ble32Scan){
+ //ble32Scan->setInterval(70);
+ //ble32Scan->setWindow(50);
+ ble32Scan->setInterval(0x40);
+ ble32Scan->setWindow(0x20);
+ ble32Scan->setAdvertisedDeviceCallbacks(&BLEScanCallbacks,true);
+ }
+
+ BLE_ESP32::BLETaskStartScan(20);
+ }
+
+ if (BLEStopScan){
+ ble32Scan->stop();
+ BLEStopScan = 0;
+ }
+
+ // come around every 1/10s
+ vTaskDelay(100/ portTICK_PERIOD_MS);
+
+ if (BLEStop == 1){
+ break;
+ }
+
+ if (BLERestartNimBLE){
+ BLERestartNimBLE = 0;
+ BLERestartTasmota = 10;
+ BLERestartTasmotaReason = BLE_RESTART_TEAMOTA_REASON_RESTARTING_BLE_TIMEOUT;
+ AddLog(LOG_LEVEL_ERROR,PSTR("BLE: BLETask: Restart NimBLE - restart Tasmota in 10 if not complt"));
+ BLE_ESP32::BLETaskStopStartNimBLE(&pClient);
+ BLERestartTasmotaReason = BLE_RESTART_TEAMOTA_REASON_UNKNOWN;
+ BLERestartTasmota = 0;
+ BLEResets ++;
+ }
+ }
+
+ BLE_ESP32::BLETaskStopStartNimBLE(&pClient, false);
+
+ // wait 1/10 second
+ vTaskDelay(100/ portTICK_PERIOD_MS);
+
+#ifdef BLE_ESP32_DEBUG
+ AddLog(LOG_LEVEL_DEBUG,PSTR("BLE: BLEOperationTask: Left task"));
+#endif
+ deleteSeenDevices();
+
+ BLEStop = 2;
+ BLERunning = false;
+ vTaskDelete( NULL );
+}
+
+
+
+
+
+/***********************************************************************\
+ * Regular Tasmota called functions
+ *
+\***********************************************************************/
+void BLEEvery50mSecond(){
+/* if (BLEAliasListTrigger){
+ BLEAliasListTrigger = 0;
+ BLEAliasMqttList();
+ }*/
+ postAdvertismentDetails();
+}
+
+
+
+/**
+ * @brief Main loop of the driver, "high level"-loop
+ *
+ */
+
+static void BLEEverySecond(bool restart){
+
+ BLEDiag();
+
+ checkDeviceTimouts();
+
+
+ if (Settings.flag5.mi32_enable != BLEMasterEnable){
+ if (Settings.flag5.mi32_enable){
+ if (StartBLE()){
+ BLEMasterEnable = Settings.flag5.mi32_enable;
+ }
+ } else {
+ if (StopBLE()){
+ BLEMasterEnable = Settings.flag5.mi32_enable;
+ }
+ }
+ AddLog(LOG_LEVEL_INFO,PSTR("BLE: MasterEnable->%d"), BLEMasterEnable);
+ }
+
+
+ // check for application callbacks here.
+ // this may remove complete items.
+ BLE_ESP32::mainThreadOpCallbacks();
+
+ // post any MQTT data if we completed anything in the last second
+ if (completedOperations.size()){
+ BLE_ESP32::BLEPostMQTT(true); // send only completed
+ }
+
+ // request send of ALL oeprations prepped, queued, in progress -
+ // in separate MQTT messages.
+ if (BLEPostMQTTTrigger){
+ BLEPostMQTTTrigger = 0;
+ BLE_ESP32::BLEPostMQTT(false); // show all operations, not just completed
+ }
+
+ if (BLEPublishDevices){
+ BLEPostMQTTSeenDevices(BLEPublishDevices);
+ BLEShowStats();
+ BLEPublishDevices = 0;
+ }
+
+ // we have been asked to restart in this many seconds....
+ if (BLERestartTasmota){
+ BLERestartTasmota--;
+ // 2 seconds to go, post to BLE topic on MQTT our reason
+ if (BLERestartTasmota == 2){
+ if (!BLERestartTasmotaReason) BLERestartTasmotaReason = BLE_RESTART_TEAMOTA_REASON_UNKNOWN;
+ snprintf_P(TasmotaGlobal.mqtt_data, sizeof(TasmotaGlobal.mqtt_data), PSTR("{\"reboot\":\"%s\"}"), BLERestartTasmotaReason);
+ MqttPublishPrefixTopic_P(TELE, PSTR("BLE"), Settings.flag.mqtt_sensor_retain);
+ AddLog(LOG_LEVEL_ERROR,PSTR("BLE: Failure! Restarting Tasmota in %d seconds because %s"), BLERestartTasmota, BLERestartTasmotaReason);
+ }
+
+ if (!BLERestartTasmota){
+ AddLog(LOG_LEVEL_ERROR,PSTR("BLE: Failure! Restarting Tasmota because %s"), BLERestartTasmotaReason);
+ // just a normal restart
+ TasmotaGlobal.restart_flag = 1;
+ }
+ }
+
+ if (BLERestartBLEReason){ // just use the ptr as the trigger to send MQTT
+ snprintf_P(TasmotaGlobal.mqtt_data, sizeof(TasmotaGlobal.mqtt_data), PSTR("{\"blerestart\":\"%s\"}"), BLERestartBLEReason);
+ MqttPublishPrefixTopic_P(TELE, PSTR("BLE"), Settings.flag.mqtt_sensor_retain);
+ AddLog(LOG_LEVEL_ERROR,PSTR("BLE: Failure! Restarting BLE Stack because %s"), BLERestartBLEReason);
+ BLERestartBLEReason = nullptr;
+ }
+
+
+ BLE_ESP32::mainThreadBLETimeouts();
+ if (!BLEMasterEnable){
+ return;
+ }
+
+}
+
+
+
+
+
+/*********************************************************************************************\
+ * Operations queue functions - all to do with read/write and notify for a device
+\*********************************************************************************************/
+
+// this retrievs the next operation from the passed list, and removes it from the list.
+// or returns null if none.
+generic_sensor_t* nextOperation(std::deque *ops){
+ generic_sensor_t* op = nullptr;
+ if (ops->size()){
+ TasAutoMutex localmutex(&BLEOperationsRecursiveMutex, "BLENExtOp");
+ op = (*ops)[0];
+ ops->pop_front();
+ }
+ return op;
+}
+
+// this adds an operation to the end of passed list.
+// it also sets the op pointer passed to null.
+int addOperation(std::deque *ops, generic_sensor_t** op){
+ int res = 0;
+ {
+ TasAutoMutex localmutex(&BLEOperationsRecursiveMutex, "BLEAddOp");
+ if (ops->size() < 10){
+ ops->push_back(*op);
+ *op = nullptr;
+ res = 1;
+ }
+ }
+ if (res){
+ //AddLog(LOG_LEVEL_DEBUG,PSTR("BLE: added operation"));
+ } else {
+ AddLog(LOG_LEVEL_ERROR,PSTR("BLE: op - no room"));
+ }
+ return res;
+}
+
+
+int newOperation(BLE_ESP32::generic_sensor_t** op){
+ if (!op) {
+ AddLog(LOG_LEVEL_ERROR,PSTR("BLE: op inv in newOperation"));
+ return 0;
+ }
+
+ BLE_ESP32::generic_sensor_t *o = new BLE_ESP32::generic_sensor_t;
+
+ // clear to zeros, but not the NimBLE classes
+ o->state = 0;
+ o->opid = 0; // incrementing id so we can find them
+ o->notifytimer = 0L;
+ //uint8_t writeRead[MAX_BLE_DATA_LEN];
+ o->writelen = 0;
+ //uint8_t dataRead[MAX_BLE_DATA_LEN];
+ o->readlen = 0;
+ o->readtruncated = 0;
+ //uint8_t dataNotify[MAX_BLE_DATA_LEN];
+ o->notifylen = 0;
+ o->notifytruncated = 0;
+ o->readmodifywritecallback = nullptr; // READ_CALLBACK function, used by external drivers
+ o->completecallback = nullptr; // OPCOMPLETE_CALLBACK function, used by external drivers
+ o->context = nullptr; // opaque context, used by external drivers, or can be set to a long for MQTT
+
+ (*op) = o;
+ return 1;
+}
+
+int freeOperation(BLE_ESP32::generic_sensor_t** op){
+ if (!op) {
+ return 0;
+ }
+
+ delete (*op);
+ (*op) = nullptr;
+ return 1;
+}
+
+
+int extQueueOperation(BLE_ESP32::generic_sensor_t** op){
+ if (!op || !(*op)) {
+ AddLog(LOG_LEVEL_ERROR,PSTR("BLE: op invalid"));
+ return 0;
+ }
+ (*op)->state = GEN_STATE_START; // trigger request later
+ (*op)->opid = lastopid++;
+
+ int res = addOperation(&queuedOperations, op);
+ if (!res){
+ AddLog(LOG_LEVEL_ERROR,PSTR("BLE: extQueueOperation: op added id %d failed"), (lastopid-1));
+ }
+ return res;
+}
+
+
+/*********************************************************************************************\
+ * BLE Name alisaes
+\*********************************************************************************************/
+#ifdef BLE_ESP32_ALIASES
+int addAlias( uint8_t *addr, char *name){
+ if (!addr || !name){
+ return 0;
+ }
+
+ int count = aliases.size();
+ // replace name for existing address
+ for (int i = 0; i < count; i++){
+ if (!memcmp(aliases[i]->addr, addr, 6)){
+ strncpy(aliases[i]->name, name, sizeof(aliases[i]->name));
+ aliases[i]->name[sizeof(aliases[i]->name)-1] = 0;
+ return 2;
+ }
+ }
+
+ // replace addr for existing name
+ for (int i = 0; i < count; i++){
+ if (!strcmp(aliases[i]->name, name)){
+ memcpy(aliases[i]->addr, addr, 6);
+ return 2;
+ }
+ }
+
+ BLE_ESP32::ble_alias_t *alias = new BLE_ESP32::ble_alias_t;
+ memcpy(alias->addr, addr, 6);
+ strncpy(alias->name, name, sizeof(alias->name));
+ alias->name[sizeof(alias->name)-1] = 0;
+ aliases.push_back(alias);
+ return 1;
+}
+#endif
+
+/**
+ * @brief Remove all colons from null terminated char array
+ *
+ * @param _string Typically representing a MAC-address like AA:BB:CC:DD:EE:FF
+ */
+void stripColon(char* _string){
+ uint32_t _length = strlen(_string);
+ uint32_t _index = 0;
+ while (_index < _length) {
+ char c = _string[_index];
+ if(c==':'){
+ memmove(_string+_index,_string+_index+1,_length-_index);
+ }
+ _index++;
+ }
+ _string[_index] = 0;
+}
+
+
+//////////////////////////////////////////////////
+// use this for address interpretaton from string
+// it looks for aliases, and converts AABBCCDDEEFF and AA:BB:CC:DD:EE:FF
+int getAddr(uint8_t *dest, char *src){
+ if (!dest || !src){
+ return 0;
+ }
+#ifdef BLE_ESP32_ALIASES
+ for (int i = 0; i < aliases.size(); i++){
+ if (!strcmp(aliases[i]->name, src)){
+ memcpy(dest, aliases[i]->addr, 6);
+ return 2; //was an alias
+ }
+ }
+#endif
+
+ char tmp[12+5+1];
+ if (strlen(src) == 12+5){
+ strcpy(tmp, src);
+ stripColon(tmp);
+ src = tmp;
+ }
+
+ int len = fromHex(dest, src, 6);
+ if (len == 6){
+ return 1;
+ }
+ // not found
+ return 0;
+}
+
+static const char *noAlias = PSTR("");
+
+////////////////////////////////////////////
+// use to display the alias name if required
+const char *getAlias(const uint8_t *addr){
+ if (!addr){
+ return noAlias;
+ }
+#ifdef BLE_ESP32_ALIASES
+ for (int i = 0; i < aliases.size(); i++){
+ if (!memcmp(aliases[i]->addr, addr, 6)){
+ return aliases[i]->name; //was an alias
+ }
+ }
+#endif
+ return noAlias;
+}
+
+
+/*********************************************************************************************\
+ * Highest level BLE task control functions
+\*********************************************************************************************/
+
+static int StartBLE(void) {
+ if (BLEStop != 1){
+ BLE_ESP32::BLEStartOperationTask();
+ return 1;
+ }
+ AddLog(LOG_LEVEL_ERROR,PSTR("BLE: StartBLE - wait as BLEStop==1"));
+
+ return 0;
+}
+
+static int StopBLE(void){
+ if (BLERunning){
+ if (BLEStop != 1){
+ BLEStop = 1;
+ AddLog(LOG_LEVEL_INFO,PSTR("BLE: StopBLE - BLEStop->1"));
+ BLEStopAt = esp_timer_get_time();
+ // give a little time for it to stop.
+ vTaskDelay(1000/ portTICK_PERIOD_MS);
+ return 1;
+ }
+ AddLog(LOG_LEVEL_ERROR,PSTR("BLE: StopBLE - wait as BLEStop==1"));
+ return 0;
+ } else {
+ AddLog(LOG_LEVEL_ERROR,PSTR("BLE: StopBLE - was not running"));
+ return 1;
+ }
+}
+
+
+/*********************************************************************************************\
+ * Commands
+\*********************************************************************************************/
+
+static void CmndBLEPeriod(void) {
+ //ResponseCmndNumber(BLE.period);
+ ResponseCmndDone();
+}
+
+
+//////////////////////////////////////////////////////////////
+// Determine what to do with advertismaents
+// BLEAdv0 -> suppress MQTT about devices found
+// BLEAdv1 -> send MQTT about devices found after each scan
+void CmndBLEAdv(void){
+ switch(XdrvMailbox.index){
+ case 0:
+ BLEAdvertMode = BLE_ESP32::BLE_NO_ADV_SEND;
+ break;
+ case 1:
+ BLEAdvertMode = BLE_ESP32::BLE_ADV_TELE;
+ break;
+ /*case 2:
+ BLEAdvertMode = BLE_ADV_ALL;
+ break;*/
+ case 3:
+ break;
+ }
+
+ ResponseCmndNumber(BLEAdvertMode);
+}
+
+
+//////////////////////////////////////////////////////////////
+// Determine what to do with advertismaents
+// BLEAdv0 -> suppress MQTT about devices found
+// BLEAdv1 -> send MQTT about devices found after each scan
+void CmndBLEDebug(void){
+ BLEDebugMode = XdrvMailbox.index;
+ ResponseCmndNumber(BLEDebugMode);
+}
+
+void CmndBLEDevices(void){
+ switch(XdrvMailbox.index){
+ case 0:{
+ // clear devices delete
+ deleteSeenDevices();
+ } break;
+ case 1:{
+ BLEPublishDevices = 2; // mqtt publish as 'STAT'
+ } break;
+ }
+ ResponseCmndDone();
+}
+
+void CmndBLEMaxAge(void){
+ switch(XdrvMailbox.index){
+ case 1:{
+ if (XdrvMailbox.data_len > 0) {
+ BLEMaxAge = XdrvMailbox.payload;
+ }
+ } break;
+ }
+ ResponseCmndIdxNumber(BLEMaxAge);
+ if (BLEMaxAge) deleteSeenDevices(BLEMaxAge);
+}
+
+void CmndBLEAddrFilter(void){
+ switch(XdrvMailbox.index){
+ case 1:{
+ if (XdrvMailbox.data_len > 0) {
+ BLEAddressFilter = XdrvMailbox.payload;
+ }
+ } break;
+ }
+ ResponseCmndIdxNumber(BLEAddressFilter);
+}
+
+
+//////////////////////////////////////////////////////////////
+// Scan options
+// BLEScan0 -> do a scan now if BLEMode == BLEModeScanByCommand
+// BLEScan0 -> do a scan now if BLEMode == BLEModeScanByCommand for timesec seconds
+// BLEScan1 0 -> Scans are passive
+// BLEScan1 1 -> Scans are active
+// more options could be added...
+void CmndBLEScan(void){
+ switch(XdrvMailbox.index){
+ case 0:{
+ if (XdrvMailbox.data_len > 0) {
+ BLEScanActiveMode = XdrvMailbox.payload;
+ ResponseCmndNumber(BLEScanActiveMode);
+ } else {
+ ResponseCmndChar("Invalid");
+ }
+ } break;
+
+ case 1: // do a manual scan now
+ switch (BLEMode){
+ case BLEModeScanByCommand: {
+ int time = 20;
+ if (XdrvMailbox.data_len > 0) {
+ time = XdrvMailbox.payload;
+ if (time < 2) time = 2;
+ if (time > 40) time = 40;
+ }
+ BLETriggerScan = time;
+ ResponseCmndNumber(time); // -ve for fail for a few reasons
+ } break;
+ case BLEModeDisabled:
+ ResponseCmndChar("BLEDisabled");
+ break;
+ case BLEModeRegularScan:
+ ResponseCmndChar("BLEActive");
+ break;
+ }
+ break;
+ default:
+ ResponseCmndChar("Invalid");
+ break;
+ }
+}
+
+
+//////////////////////////////////////////////////////////////
+// Determine what to do with advertismaents
+// BLEMode0 -> kill BLE completely
+// BLEMode1 -> start BLE, scan by command
+// BLEMode2 -> start BLE, regular scan
+void CmndBLEMode(void){
+ int val = XdrvMailbox.index;
+ if (XdrvMailbox.data_len > 0) {
+ val = XdrvMailbox.payload;
+ }
+
+ switch(val){
+ case BLEModeDisabled:{
+ if (BLEMode != BLEModeDisabled){
+ BLEMode = BLEModeDisabled;
+ StopBLE();
+ ResponseCmndChar("StoppingBLE");
+ } else {
+ ResponseCmndChar("Disabled");
+ }
+ } break;
+ case BLEModeScanByCommand:{
+ uint64_t now = esp_timer_get_time();
+ switch(BLEMode){
+ // when changing from regular to by command,
+ // stop the scan next loop
+ case BLEModeRegularScan: {
+ BLEMode = BLEModeScanByCommand;
+ BLEStopScan = 1;
+ ResponseCmndChar("BLEStopScan");
+ } break;
+ case BLEModeDisabled: {
+ BLEMode = BLEModeScanByCommand;
+ StartBLE();
+ ResponseCmndChar("StartingBLE");
+ } break;
+ case BLEModeScanByCommand:{
+ ResponseCmndChar("BLERunning");
+ } break;
+ }
+ BLEScanLastAdvertismentAt = now; // note the time of the last advertisment
+ } break;
+ case BLEModeRegularScan:{
+ uint64_t now = esp_timer_get_time();
+ switch(BLEMode){
+ case BLEModeDisabled: {
+ BLEMode = BLEModeRegularScan;
+ StartBLE();
+ ResponseCmndChar("StartingBLE");
+ } break;
+ case BLEModeScanByCommand:{
+ BLEMode = BLEModeRegularScan;
+ ResponseCmndChar("BLEEnableScan");
+ } break;
+ case BLEModeRegularScan:{
+ BLEMode = BLEModeRegularScan;
+ ResponseCmndChar("BLERunning");
+ } break;
+ }
+ BLEScanLastAdvertismentAt = now; // note the time of the last advertisment
+ } break;
+ default:
+ ResponseCmndChar("InvalidIndex");
+ break;
+ }
+}
+
+
+//////////////////////////////////////////
+// get more drtails for a single MAC address
+// BLEDetails0 -> don;t send me anything
+// BLEDetails1 -> send me details for once
+// BLEDetails2 -> send me details for every advert if possible
+// example: BLEDetails1 001A22092C9A
+// details look like:
+// MQT: tele/tasmota_esp32/BLE = {"details":{"mac":"001A22092C9A","p":"0C0943432D52542D4D2D424C450CFF0000000000000000000000"}}
+// and incliude mac, complete advert payload, plus optional ,"lost":true if an advert was not captured because MQTT we already
+// had one waiting to be sent
+void CmndBLEDetails(void){
+ switch(XdrvMailbox.index){
+ case 0:
+ BLEDetailsRequest = 0;
+ ResponseCmndNumber(BLEDetailsRequest);
+ break;
+
+ case 1:
+ case 2:{
+ BLEDetailsRequest = 0;
+ if (getAddr(BLEDetailsMac, XdrvMailbox.data)){
+ BLEDetailsRequest = XdrvMailbox.index;
+ ResponseCmndIdxChar(XdrvMailbox.data);
+ } else {
+ ResponseCmndChar("InvalidMac");
+ }
+ } break;
+
+ case 3:{
+ BLEDetailsRequest = XdrvMailbox.index;
+ ResponseCmndNumber(BLEDetailsRequest);
+ } break;
+
+ default:
+ ResponseCmndChar("InvalidIndex");
+ break;
+ }
+}
+
+
+void CmndBLEAlias(void){
+#ifdef BLE_ESP32_ALIASES
+ int op = XdrvMailbox.index;
+ if (BLEDebugMode > 0) AddLog(LOG_LEVEL_DEBUG,PSTR("BLE: Alias %d %s"), op, XdrvMailbox.data);
+
+ int res = -1;
+ switch(op){
+ case 0:
+ case 1:{
+ char *p = strtok(XdrvMailbox.data, " ,=");
+ bool trigger = false;
+ int added = 0;
+
+ do {
+ if (!p || !(*p)){
+ break;
+ }
+
+ uint8_t addr[6];
+ char *mac = p;
+ int len = fromHex(addr, p, sizeof(addr));
+ if (len != 6){
+ AddLog(LOG_LEVEL_ERROR,PSTR("BLE: Alias invalid mac %s"), p);
+ ResponseCmndChar("invalidmac");
+ return;
+ }
+
+ p = strtok(nullptr, " ,=");
+ char *name = p;
+ if (!p || !(*p)){
+ int i = 0;
+ for (i = 0; i < aliases.size(); i++){
+ BLE_ESP32::ble_alias_t *alias = aliases[i];
+ if (!memcmp(alias->addr, addr, 6)){
+ aliases.erase(aliases.begin() + i);
+ BLEAliasListResp();
+ return;
+ }
+ }
+ ResponseCmndChar("invalidmac");
+ return;
+ }
+
+ AddLog(LOG_LEVEL_ERROR,PSTR("BLE: Add Alias mac %s = name %s"), mac, p);
+ if (addAlias( addr, name )){
+ added++;
+ }
+ p = strtok(nullptr, " ,=");
+ } while (p);
+
+ if (added){
+ if (BLEDebugMode > 0) AddLog(LOG_LEVEL_DEBUG,PSTR("BLE: Added %d Aliases"), added);
+ BLEAliasListResp();
+ } else {
+ BLEAliasListResp();
+ }
+ return;
+ } break;
+ case 2:{ // clear
+ if (BLEDebugMode > 0) AddLog(LOG_LEVEL_DEBUG,PSTR("BLE: Alias clearing %d"), aliases.size());
+ for (int i = aliases.size()-1; i >= 0; i--){
+ BLE_ESP32::ble_alias_t *alias = aliases[i];
+ aliases.pop_back();
+ delete alias;
+ }
+ BLEAliasListResp();
+ return;
+ } break;
+ }
+ ResponseCmndChar("invalididx");
+#endif
+}
+
+
+// SET the BLE name for a device -
+// uses s:1800 c:2a00 and writes name to DEVICE
+void CmndBLEName(void) {
+ char *p = strtok(XdrvMailbox.data, " ");
+
+ if (!p || !(*p)){
+ ResponseCmndIdxChar(PSTR("invalid"));
+ return;
+ }
+
+ uint8_t addrbin[6];
+ int addrres = BLE_ESP32::getAddr(addrbin, p);
+ NimBLEAddress addr(addrbin);
+
+ if (addrres){
+ if (addrres == 2){
+ if (BLEDebugMode > 0) AddLog(LOG_LEVEL_DEBUG,PSTR("BLE: addr used alias: %s"), p);
+ }
+
+//#ifdef EQ3_DEBUG
+ if (BLEDebugMode > 0) AddLog(LOG_LEVEL_INFO,PSTR("BLE: cmd addr: %s -> %s"), p, addr.toString().c_str());
+//#endif
+ } else {
+ AddLog(LOG_LEVEL_ERROR,PSTR("BLE: addr invalid: %s"), p);
+ ResponseCmndIdxChar(PSTR("invalidaddr"));
+ return;
+ }
+
+ BLE_ESP32::generic_sensor_t *op = nullptr;
+ // ALWAYS use this function to create a new one.
+ int res = BLE_ESP32::newOperation(&op);
+ if (!res){
+ AddLog(LOG_LEVEL_ERROR,PSTR("BLE: Can't get a newOperation"));
+ ResponseCmndChar(PSTR("FAIL"));
+ return;
+ } else {
+ if (BLEDebugMode > 0) AddLog(LOG_LEVEL_DEBUG,PSTR("BLE: got a newOperation from BLE"));
+ }
+
+ op->addr = addr;
+ op->serviceUUID = NimBLEUUID("1800");
+ op->characteristicUUID = NimBLEUUID("2A00");
+
+ // get next part of cmd
+ char *name = strtok(nullptr, " ");
+ bool write = false;
+ if (name && *name){
+ if (BLEDebugMode > 0) AddLog(LOG_LEVEL_DEBUG,PSTR("BLE: write name %s"), name);
+ op->writelen = strlen(name);
+ memcpy(op->dataToWrite, name, op->writelen);
+ write = true;
+ } else {
+ if (BLEDebugMode > 0) AddLog(LOG_LEVEL_DEBUG,PSTR("BLE: read name"));
+ op->readlen = 1;
+ }
+
+ res = BLE_ESP32::extQueueOperation(&op);
+ if (BLEDebugMode > 0) AddLog(LOG_LEVEL_DEBUG,PSTR("BLE: queue res %d"), res);
+ if (!res){
+ // if it fails to add to the queue, do please delete it
+ BLE_ESP32::freeOperation(&op);
+ AddLog(LOG_LEVEL_ERROR,PSTR("BLE: Failed to queue new operation - deleted"));
+ ResponseCmndChar(PSTR("QUEUEFAIL"));
+ return;
+ }
+
+ if (write){
+ if (BLEDebugMode > 0) AddLog(LOG_LEVEL_DEBUG, PSTR("BLE: will write name"));
+ } else {
+ if (BLEDebugMode > 0) AddLog(LOG_LEVEL_DEBUG, PSTR("BLE: will read name"));
+ }
+ ResponseCmndDone();
+ return;
+}
+
+
+
+//////////////////////////////////////////////////////////////////////////
+// Command to cause BLE read/write/notify operations to be run.
+//////////////////////////////////////////////////////////////////////////
+
+// we expect BLEOp0 - poll state
+// we expect BLEOp1 m:MAC s:svc
+// we expect BLEOp2 trigger queue of op. return is opid
+
+// returns: Done|FailCreate|FailNoOp|FailQueue|InvalidIndex|
+
+// BLEop0/1/2 will cause an MQTT send of ops currently known.
+// on op complete/op fail, a MQTT send is triggered of all known ops, and the completed/failed op removed.
+
+// example:
+// BLEOp1 M:001A22092CDB s:3e135142-654f-9090-134a-a6ff5bb77046 c:3fa4585a-ce4a-3bad-db4b-b8df8179ea09 w:03 n:d0e8434d-cd29-0996-af41-6c90f4e0eb2a go
+// requests write of 03, and request wait for notify.
+
+// You may queue up operations. they are currently processed serially.
+void CmndBLEOperation(void){
+
+ int op = XdrvMailbox.index;
+
+ //AddLog(LOG_LEVEL_INFO,PSTR("BLE: op %d"), op);
+
+ int res = -1;
+
+ // if in progress, only op 0 or 11 are allowed
+ switch(op) {
+ case 0:
+#ifdef BLE_ESP32_DEBUG
+ if (BLEDebugMode > 0) AddLog(LOG_LEVEL_INFO,PSTR("BLE: preview"));
+#endif
+ BLEPostMQTTTrigger = 1;
+ break;
+ case 1: {
+ if (prepOperation){
+ BLE_ESP32::freeOperation(&prepOperation);
+ }
+ int opres = BLE_ESP32::newOperation(&prepOperation);
+ if (!opres){
+#ifdef BLE_ESP32_DEBUG
+ AddLog(LOG_LEVEL_ERROR,PSTR("BLE: Could not create new operation"));
+#endif
+ ResponseCmndChar("FailCreate");
+ return;
+ }
+ // expect m:MAC s:svc
+ // < > are optional
+ char *p = strtok(XdrvMailbox.data, " ,");
+ bool trigger = false;
+
+ while (p){
+ switch(*p | 0x20){
+ case 'm':{
+ uint8_t addr[6];
+ if (getAddr(addr, p+2)){
+ prepOperation->addr = NimBLEAddress(addr);
+ } else {
+ prepOperation->addr = NimBLEAddress();
+ }
+ } break;
+ case 's':{
+ prepOperation->serviceUUID = NimBLEUUID(p+2);
+ } break;
+ case 'c':
+ prepOperation->characteristicUUID = NimBLEUUID(p+2);
+ //strncpy(prepOperation->characteristicStr, p+2, sizeof(prepOperation->characteristicStr)-1);
+ break;
+ case 'n':
+ prepOperation->notificationCharacteristicUUID = NimBLEUUID(p+2);
+ //strncpy(prepOperation->notificationCharacteristicStr, p+2, sizeof(prepOperation->notificationCharacteristicStr)-1);
+ break;
+ case 'w':
+ prepOperation->writelen = fromHex(prepOperation->dataToWrite, p+2, sizeof(prepOperation->dataToWrite));
+ break;
+ case 'u': // 'unique' context for this request
+ prepOperation->context = (void *)atoi(p+2);
+ break;
+ case 'r':
+ prepOperation->readlen = 1;
+ break;
+ case 'g':
+ if ((*(p+1))|0x20 == 'o'){
+ trigger = true;
+ }
+ break;
+ }
+
+ p = strtok(nullptr, " ,");
+ }
+
+ if (trigger){
+ int u = (int)prepOperation->context;
+ int opres = BLE_ESP32::extQueueOperation(&prepOperation);
+ if (!opres){
+ // NOTE: prepOperation will NOT have been deleted.
+ // this means you could retry with another BLEOp10.
+ // it WOULD be deleted if you sent another BELOP1
+#ifdef BLE_ESP32_DEBUG
+ AddLog(LOG_LEVEL_ERROR,PSTR("BLE: Could not queue new operation"));
+#endif
+ ResponseCmndChar("FailQueue");
+ return;
+ } else {
+ // NOTE: prepOperation has been set to null if we queued sucessfully.
+#ifdef BLE_ESP32_DEBUG
+ if (BLEDebugMode > 0) AddLog(LOG_LEVEL_INFO,PSTR("BLE: Operations queued:%d"), queuedOperations.size());
+#endif
+ char temp[40];
+ sprintf(temp, "{\"opid\":%d,\"u\":%d}", lastopid-1, u);
+ Response_P(S_JSON_COMMAND_XVALUE, XdrvMailbox.command, temp);
+ // don't do this here... overwrites response
+ //BLE_ESP32::BLEPostMQTT(false);
+ return;
+ }
+ } else {
+ ResponseCmndChar("Prepared");
+ //BLE_ESP32::BLEPostMQTT(false);
+ return;
+ }
+ } break;
+
+ case 2: {
+ if (!prepOperation) {
+ ResponseCmndChar("FailNoOp");
+ return;
+ }
+ //prepOperation->requestType = atoi(XdrvMailbox.data);
+ int u = (int)prepOperation->context;
+ int opres = BLE_ESP32::extQueueOperation(&prepOperation);
+ if (!opres){
+ // NOTE: prepOperation will NOT have been deleted.
+ // this means you could retry with another BLEOp10.
+ // it WOULD be deleted if you sent another BELOP1
+#ifdef BLE_ESP32_DEBUG
+ AddLog(LOG_LEVEL_ERROR,PSTR("BLE: Could not queue new operation"));
+#endif
+ ResponseCmndChar("FailQueue");
+ } else {
+ // NOTE: prepOperation has been set to null if we queued sucessfully.
+#ifdef BLE_ESP32_DEBUG
+ if (BLEDebugMode > 0) AddLog(LOG_LEVEL_INFO,PSTR("BLE: Operations queued:%d"), queuedOperations.size());
+#endif
+ char temp[40];
+ sprintf(temp, "{\"opid\":%d,\"u\":%d}", lastopid-1, u);
+ Response_P(S_JSON_COMMAND_XVALUE, XdrvMailbox.command, temp);
+ }
+ return;
+ } break;
+
+ default:
+ ResponseCmndChar("InvalidIndex");
+ return;
+ }
+
+ ResponseCmndDone();
+ return;
+}
+
+
+/*********************************************************************************************\
+ * Presentation
+\*********************************************************************************************/
+static void BLEPostMQTTSeenDevices(int type) {
+ int remains = 0;
+ nextSeenDev = 0;
+
+ memset(TasmotaGlobal.mqtt_data, 0, sizeof(TasmotaGlobal.mqtt_data));
+ ResponseTime_P(PSTR(""));
+ int timelen = strlen(TasmotaGlobal.mqtt_data);
+ char *dest = TasmotaGlobal.mqtt_data + timelen;
+ int maxlen = (sizeof(TasmotaGlobal.mqtt_data)-20) - timelen;
+
+// if (!TasmotaGlobal.ota_state_flag){
+ do {
+ remains = getSeenDevicesToJson(dest, maxlen);
+ // no retain - this is present devices, not historic
+ if (type == 1){
+ MqttPublishPrefixTopic_P(TELE, PSTR("BLE"), 0);
+ } else {
+ MqttPublishPrefixTopic_P(STAT, PSTR("BLE"), 0);
+ }
+ } while (remains);
+// }
+}
+
+static void BLEPostMQTT(bool onlycompleted) {
+// if (TasmotaGlobal.ota_state_flag) return;
+
+
+ if (prepOperation || completedOperations.size() || queuedOperations.size() || currentOperations.size()){
+#ifdef BLE_ESP32_DEBUG
+ if (BLEDebugMode > 0) AddLog(LOG_LEVEL_INFO,PSTR("BLE: some to show"));
+#endif
+ if (prepOperation && !onlycompleted){
+ std::string out = BLETriggerResponse(prepOperation);
+ snprintf_P(TasmotaGlobal.mqtt_data, sizeof(TasmotaGlobal.mqtt_data), PSTR("%s"), out.c_str());
+ MqttPublishPrefixTopic_P(TELE, PSTR("BLE"), Settings.flag.mqtt_sensor_retain);
+#ifdef BLE_ESP32_DEBUG
+ if (BLEDebugMode > 0) AddLog(LOG_LEVEL_INFO,PSTR("BLE: prep sent %s"), out.c_str());
+#endif
+ }
+
+ if (queuedOperations.size() && !onlycompleted){
+#ifdef BLE_ESP32_DEBUG
+ if (BLEDebugMode > 0) AddLog(LOG_LEVEL_INFO,PSTR("BLE: queued %d"), queuedOperations.size());
+#endif
+ for (int i = 0; i < queuedOperations.size(); i++){
+ TasAutoMutex localmutex(&BLEOperationsRecursiveMutex, "BLEPost1");
+
+ generic_sensor_t *toSend = queuedOperations[i];
+ if (!toSend) {
+ break;
+ } else {
+ std::string out = BLETriggerResponse(toSend);
+ localmutex.give();
+ snprintf_P(TasmotaGlobal.mqtt_data, sizeof(TasmotaGlobal.mqtt_data), PSTR("%s"), out.c_str());
+ MqttPublishPrefixTopic_P(TELE, PSTR("BLE"), Settings.flag.mqtt_sensor_retain);
+#ifdef BLE_ESP32_DEBUG
+ if (BLEDebugMode > 0) AddLog(LOG_LEVEL_INFO,PSTR("BLE: queued %d sent %s"), i, out.c_str());
+#endif
+ //break;
+ }
+ }
+ }
+
+ if (currentOperations.size() && !onlycompleted){
+#ifdef BLE_ESP32_DEBUG
+ if (BLEDebugMode > 0) AddLog(LOG_LEVEL_INFO,PSTR("BLE: current %d"), currentOperations.size());
+#endif
+ for (int i = 0; i < currentOperations.size(); i++){
+ TasAutoMutex localmutex(&BLEOperationsRecursiveMutex, "BLEPost2");
+ generic_sensor_t *toSend = currentOperations[i];
+ if (!toSend) {
+ break;
+ } else {
+ std::string out = BLETriggerResponse(toSend);
+ localmutex.give();
+ snprintf_P(TasmotaGlobal.mqtt_data, sizeof(TasmotaGlobal.mqtt_data), PSTR("%s"), out.c_str());
+ MqttPublishPrefixTopic_P(TELE, PSTR("BLE"), Settings.flag.mqtt_sensor_retain);
+#ifdef BLE_ESP32_DEBUG
+ if (BLEDebugMode > 0) AddLog(LOG_LEVEL_INFO,PSTR("BLE: curr %d sent %s"), i, out.c_str());
+#endif
+ //break;
+ }
+ }
+ }
+
+ if (completedOperations.size()){
+#ifdef BLE_ESP32_DEBUG
+ if (BLEDebugMode > 0) AddLog(LOG_LEVEL_INFO,PSTR("BLE: completed %d"), completedOperations.size());
+#endif
+ do {
+ generic_sensor_t *toSend = nextOperation(&completedOperations);
+ if (!toSend) {
+ break; // break from while loop
+ } else {
+#ifdef BLE_ESP32_DEBUG
+ if (BLEDebugMode > 0) AddLog(LOG_LEVEL_DEBUG,PSTR("BLE: completedOperation removed"));
+#endif
+ std::string out = BLETriggerResponse(toSend);
+ snprintf_P(TasmotaGlobal.mqtt_data, sizeof(TasmotaGlobal.mqtt_data), PSTR("%s"), out.c_str());
+ MqttPublishPrefixTopic_P(TELE, PSTR("BLE"), Settings.flag.mqtt_sensor_retain);
+ // we alreayd removed this from the queues, so now delete
+ delete toSend;
+ //break;
+ }
+ //break;
+ } while (1);
+ }
+ } else {
+ snprintf_P(TasmotaGlobal.mqtt_data, sizeof(TasmotaGlobal.mqtt_data), PSTR("{\"BLEOperation\":{}}"));
+ MqttPublishPrefixTopic_P(TELE, PSTR("BLE"), Settings.flag.mqtt_sensor_retain);
+ }
+}
+
+static void mainThreadBLETimeouts() {
+ uint64_t now = esp_timer_get_time();
+
+ if (!BLERunning){
+ BLELastLoopTime = now; // initialise the time of the last advertisment
+ BLEScanLastAdvertismentAt = now; // initialise the time of the last advertisment
+ return;
+ }
+
+ if (BLEStop == 1){
+ if (BLEStopAt + 30L*1000L*1000L < now){ // if asked to stop > 30s ago...
+ AddLog(LOG_LEVEL_ERROR,PSTR("BLE: Stop Timeout - restart Tasmota"));
+ BLERestartTasmota = 2;
+ BLEStopAt = now;
+ }
+ AddLog(LOG_LEVEL_ERROR,PSTR("BLE: Awaiting BLEStop"));
+ return;
+ }
+
+ // if no adverts for 120s, and BLE is running, retsart NimBLE.
+ // belt and braces....
+ uint64_t adTimeout = ((uint64_t)BLEMaxTimeBetweenAdverts)*1000L*1000L;
+ if (BLEScanLastAdvertismentAt + adTimeout < now){
+ BLEScanLastAdvertismentAt = now; // initialise the time of the last advertisment
+ BLERestartNimBLE = 1;
+ AddLog(LOG_LEVEL_ERROR,PSTR("BLE: scan stall? no adverts > 120s, restart BLE"));
+
+ BLERestartBLEReason = BLE_RESTART_BLE_REASON_ADVERT_BLE_TIMEOUT;
+ }
+
+ // if stuck and have not done task for 120s, something is seriously wrong.
+ // restart Tasmota completely. (belt and braces)
+ uint64_t bleLoopTimeout = ((uint64_t)BLEMaxTaskLoopTime)*1000L*1000L;
+ if (BLELastLoopTime + bleLoopTimeout < now){
+ BLELastLoopTime = now; // initialise the time of the last advertisment
+ BLERestartTasmota = 10;
+ AddLog(LOG_LEVEL_DEBUG,PSTR("BLE: BLETask stall > 120s, restart Tasmota in 10s"));
+ BLERestartTasmotaReason = BLE_RESTART_TEAMOTA_REASON_BLE_LOOP_STALLED;
+ }
+}
+
+
+static void mainThreadOpCallbacks() {
+ if (completedOperations.size()){
+ //AddLog(LOG_LEVEL_INFO,PSTR("BLE: completed %d"), completedOperations.size());
+ TasAutoMutex localmutex(&BLEOperationsRecursiveMutex, "BLEMainCB");
+
+ // find this operation in currentOperations, and remove it.
+ // in reverse so we can erase them safely.
+ for (int i = completedOperations.size()-1; i >= 0 ; i--){
+ generic_sensor_t *op = completedOperations[i];
+
+ bool callbackres = false;
+
+ if (op->completecallback){
+ try {
+ OPCOMPLETE_CALLBACK *pFn = (OPCOMPLETE_CALLBACK *)(op->completecallback);
+ callbackres = pFn(op);
+#ifdef BLE_ESP32_DEBUG
+ if (BLEDebugMode > 0) AddLog(LOG_LEVEL_DEBUG,PSTR("BLE: op->completecallback %d"), callbackres);
+#endif
+ } catch(const std::exception& e){
+#ifdef BLE_ESP32_DEBUG
+ AddLog(LOG_LEVEL_ERROR,PSTR("BLE: exception in op->completecallback"));
+#endif
+ }
+ }
+
+ if (!callbackres){
+ for (int i = 0; i < operationsCallbacks.size(); i++){
+ try {
+ OPCOMPLETE_CALLBACK *pFn = operationsCallbacks[i];
+ callbackres = pFn(op);
+#ifdef BLE_ESP32_DEBUG
+ if (BLEDebugMode > 0) AddLog(LOG_LEVEL_DEBUG,PSTR("BLE: operationsCallbacks %d %d"), i, callbackres);
+#endif
+ if (callbackres){
+ break; // this callback ate the op.
+ }
+ } catch(const std::exception& e){
+#ifdef BLE_ESP32_DEBUG
+ AddLog(LOG_LEVEL_ERROR,PSTR("BLE: exception in operationsCallbacks"));
+#endif
+ }
+ }
+ }
+
+ // if some callback told us not to send on MQTT, then remove from completed and delete the data
+ if (callbackres){
+#ifdef BLE_ESP32_DEBUG
+ if (BLEDebugMode > 0) AddLog(LOG_LEVEL_DEBUG,PSTR("BLE: callbackres true -> delete op"));
+#endif
+ completedOperations.erase(completedOperations.begin() + i);
+ delete op;
+ }
+ }
+ }
+}
+
+static void BLEShowStats(){
+ uint32_t totalCount = BLEAdvertisment.totalCount;
+ uint32_t deviceCount = seenDevices.size();
+ ResponseTime_P(PSTR(""));
+ ResponseAppend_P(PSTR(",\"BLE\":{\"scans\":%u,\"adverts\":%u,\"devices\":%u,\"resets\":%u}}"), BLEScanCount, totalCount, deviceCount, BLEResets);
+ MqttPublishPrefixTopic_P(TELE, PSTR("BLE"), 0);
+}
+
+
+/*void BLEAliasMqttList(){
+ ResponseTime_P(PSTR(",\"BLEAlias\":["));
+ for (int i = 0; i < aliases.size(); i++){
+ if (i){
+ ResponseAppend_P(PSTR(","));
+ }
+ char tmp[20];
+ ToHex_P(aliases[i]->addr,6,tmp,20,0);
+ ResponseAppend_P(PSTR("{\"%s\":\"%s\"}"), tmp, aliases[i]->name);
+ }
+ ResponseAppend_P(PSTR("]}"));
+ MqttPublishPrefixTopic_P(TELE, PSTR("BLE"), Settings.flag.mqtt_sensor_retain);
+}*/
+
+void BLEAliasListResp(){
+ Response_P(PSTR("{\"BLEAlias\":{"));
+ for (int i = 0; i < aliases.size(); i++){
+ if (i){
+ ResponseAppend_P(PSTR(","));
+ }
+ char tmp[20];
+ ToHex_P(aliases[i]->addr,6,tmp,20,0);
+ ResponseAppend_P(PSTR("\"%s\":\"%s\""), tmp, aliases[i]->name);
+ }
+ ResponseAppend_P(PSTR("}}"));
+}
+
+
+static void BLEDiag()
+{
+ uint32_t totalCount = BLEAdvertisment.totalCount;
+ uint32_t deviceCount = seenDevices.size();
+#ifdef BLE_ESP32_DEBUG
+ if (BLEDebugMode > 0) AddLog_P(LOG_LEVEL_INFO,PSTR("BLE: scans:%u,advertisements:%u,devices:%u,resets:%u,BLEStop:%d,BLERunning:%d,BLERunningScan:%d,BLELoopCount:%u,BLEOpCount:%u"), BLEScanCount, totalCount, deviceCount, BLEResets, BLEStop, BLERunning, BLERunningScan, BLELoopCount, BLEOpCount);
+#endif
+}
+
+/**
+ * @brief creates a JSON representing a single operation.
+ *
+ */
+std::string BLETriggerResponse(generic_sensor_t *toSend){
+ char temp[100];
+ if (!toSend) return "";
+ std::string out = "{\"BLEOperation\":{\"opid\":\"";
+ sprintf(temp, "%d", toSend->opid); // note only 10 long!
+ out = out + temp;
+/* out = out + "\",\"state\":\"";
+ sprintf(t, "%d", toSend->state);
+ out = out + t;*/
+ out = out + "\",\"stat\":\"";
+ sprintf(temp, "%d", toSend->state);
+ out = out + temp;
+ out = out + "\",\"state\":\"";
+ out = out + getStateString(toSend->state);
+
+ if (toSend->addr != NimBLEAddress()){
+ out = out + "\",\"MAC\":\"";
+ uint8_t addrrev[6];
+ memcpy(addrrev, toSend->addr.getNative(), 6);
+ ReverseMAC(addrrev);
+ dump(temp, 13, addrrev, 6);
+ out = out + temp;
+ }
+ if (toSend->context){
+ out = out + "\",\"u\":\"";
+ sprintf(temp, "%d", (int32_t)toSend->context);
+ out = out + temp;
+ }
+ if (toSend->serviceUUID.bitSize()){
+ out = out + "\",\"svc\":\"";
+ out = out + toSend->serviceUUID.toString();
+ }
+ if (toSend->characteristicUUID.bitSize()){
+ out = out + "\",\"char\":\"";
+ out = out + toSend->characteristicUUID.toString();
+ }
+ if (toSend->notificationCharacteristicUUID.bitSize()){
+ out = out + "\",\"notifychar\":\"";
+ out = out + toSend->notificationCharacteristicUUID.toString();
+ }
+ out = out + "\"";
+ if (toSend->readlen){
+ dump(temp, 99, toSend->dataRead, toSend->readlen);
+ if (toSend->readtruncated){
+ strcat(temp, "+");
+ }
+ out = out + ",\"read\":\"";
+ out = out + temp;
+ out = out + "\"";
+ }
+ if (toSend->writelen){
+ dump(temp, 99, toSend->dataToWrite, toSend->writelen);
+ out = out + ",\"write\":\"";
+ out = out + temp;
+ out = out + "\"";
+ }
+ if (toSend->notifylen){
+ dump(temp, 99, toSend->dataNotify, toSend->notifylen);
+ if (toSend->notifytruncated){
+ strcat(temp, "+");
+ }
+ out = out + ",\"notify\":\"";
+ out = out + temp;
+ out = out + "\"";
+ }
+ out = out + "}}";
+ return out;
+}
+
+#ifdef USE_WEBSERVER
+
+#define WEB_HANDLE_BLE "ble"
+#define D_CONFIGURE_BLE "Configure BLE"
+#define D_BLE_PARAMETERS "Bluetooth Settings"
+#define D_MQTT_BLE_ENABLE "Enable Bluetooth"
+#define D_MQTT_BLE_ACTIVESCAN "Enable Active Scan(*)"
+#define D_BLE_DEVICES "Devices Seen"
+
+const char HTTP_BTN_MENU_BLE[] PROGMEM =
+ "";
+
+const char HTTP_FORM_BLE[] PROGMEM =
+ "