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 ![Release Version](https://img.shields.io/github/release/h2zero/NimBLE-Arduino.svg?style=plastic) ![Release Date](https://img.shields.io/github/release-date/h2zero/NimBLE-Arduino.svg?style=plastic)](https://github.com/h2zero/NimBLE-Arduino/releases/latest/) + +Need help? Have questions or suggestions? Join the [![Gitter](https://badges.gitter.im/NimBLE-Arduino/community.svg)](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 = + "
 " D_BLE_PARAMETERS " " + "
" + "

" + "

" + "

items marked (*) are not stored in config

"; + + +const char HTTP_BLE_DEV_STYLE[] PROGMEM = "th, td { padding-left:5px; }"; +const char HTTP_BLE_DEV_START[] PROGMEM = + "
 " D_BLE_DEVICES " " + ""; +const char HTTP_BLE_DEV[] PROGMEM = + ""; +const char HTTP_BLE_DEV_END[] PROGMEM = + "
"; + +void HandleBleConfiguration(void) +{ + +#ifdef BLE_ESP32_DEBUG + AddLog(LOG_LEVEL_DEBUG, PSTR("BLE: HandleBleConfiguration")); +#endif + + if (!HttpCheckPriviledgedAccess()) { +#ifdef BLE_ESP32_DEBUG + AddLog(LOG_LEVEL_DEBUG, PSTR("BLE: !HttpCheckPriviledgedAccess()")); +#endif + return; + } + +#ifdef BLE_ESP32_DEBUG + AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_HTTP D_CONFIGURE_BLE)); +#endif + + char tmp[20]; + WebGetArg("en", tmp, sizeof(tmp)); + +#ifdef BLE_ESP32_DEBUG + if (BLEDebugMode > 0) AddLog(LOG_LEVEL_DEBUG, PSTR("BLE: arg en is %s"), tmp); +#endif + + if (Webserver->hasArg("save")) { +#ifdef BLE_ESP32_DEBUG + AddLog(LOG_LEVEL_DEBUG, PSTR("BLE: SETTINGS SAVE")); +#endif + Settings.flag5.mi32_enable = Webserver->hasArg("e0"); // + BLEScanActiveMode = (Webserver->hasArg("e1")?1:0); // + + SettingsSaveAll(); + HandleConfiguration(); + return; + } +#ifdef BLE_ESP32_DEBUG + AddLog(LOG_LEVEL_DEBUG, PSTR("BLE: !SAVE")); +#endif + char str[TOPSZ]; + + WSContentStart_P(PSTR(D_CONFIGURE_BLE)); + WSContentSendStyle_P(HTTP_BLE_DEV_STYLE); + //WSContentSendStyle(); + WSContentSend_P(HTTP_FORM_BLE, + (Settings.flag5.mi32_enable) ? " checked" : "", + (BLEScanActiveMode) ? " checked" : "" + ); + WSContentSend_P(HTTP_FORM_END); + + + { + //TasAutoMutex localmutex(&BLEOperationsRecursiveMutex, "BLEConf"); + int number = seenDevices.size(); + if (number){ + WSContentSend_P(HTTP_BLE_DEV_START); + uint64_t now = esp_timer_get_time(); + now = now/1000L; + now = now/1000L; + uint32_t nowS = (uint32_t)now; + + for (int i = 0; i < number; i++){ + BLE_ESP32::BLE_simple_device_t* dev = seenDevices[i]; + char addr[20]; + dump(addr, 20, dev->mac, 6); + uint8_t addrtype = dev->addrtype; + const char *alias = getAlias(dev->mac); + uint64_t lastseen = dev->lastseen/1000L; + lastseen = lastseen/1000L; + uint32_t lastseenS = (uint32_t) lastseen; + uint32_t ageS = nowS-lastseenS; + + WSContentSend_P(HTTP_BLE_DEV, addr, addrtype, alias, dev->name, dev->RSSI, ageS, dev->maxAge); + } + WSContentSend_P(HTTP_BLE_DEV_END); + } + } + + WSContentSpaceButton(BUTTON_CONFIGURATION); + WSContentStop(); + +} +#endif + + +} // end namespace BLE_ESP32 + +/*********************************************************************************************\ + * Interface +\*********************************************************************************************/ + +int ExtStopBLE(){ + AddLog(LOG_LEVEL_INFO, PSTR("BLE: Stopping if active")); + BLE_ESP32::BLEMode = BLE_ESP32::BLEModeDisabled; + BLE_ESP32::StopBLE(); + return 0; +} + +bool Xdrv79(uint8_t function) +{ + //if (!Settings.flag5.mi32_enable) { return false; } // SetOption115 - Enable ESP32 BLE BLE + + bool result = false; + + if (FUNC_INIT == function){ + BLE_ESP32::BLEPreInit(); + } + + if (!BLE_ESP32::BLEInitState) { + if (function == FUNC_EVERY_250_MSECOND) { + BLE_ESP32::BLEInit(); + } + return result; + } + switch (function) { + case FUNC_EVERY_50_MSECOND: + BLE_ESP32::BLEEvery50mSecond(); + //############################# DEBUG + TasmotaGlobal.seriallog_timer = 0; + break; + case FUNC_EVERY_SECOND: + BLE_ESP32::BLEEverySecond(false); + break; + case FUNC_COMMAND: + result = DecodeCommand(BLE_ESP32::kBLE_Commands, BLE_ESP32::BLE_Commands); + break; + case FUNC_JSON_APPEND: + break; + + // next second, we will publish to our MQTT topic. + case FUNC_AFTER_TELEPERIOD: + BLE_ESP32::BLEPublishDevices = 1; // mqtt publish as 'TELE' + break; + +#ifdef USE_WEBSERVER + case FUNC_WEB_ADD_BUTTON: + WSContentSend_P(BLE_ESP32::HTTP_BTN_MENU_BLE); + break; + case FUNC_WEB_ADD_HANDLER: + WebServer_on(PSTR("/" WEB_HANDLE_BLE), BLE_ESP32::HandleBleConfiguration); + break; +#endif // USE_WEBSERVER + } + return result; +} + + + +/*********************************************************************************************\ + * Example Advertisment callback +\*********************************************************************************************/ + +#ifdef EXAMPLE_ADVERTISMENT_CALLBACK + +// match ADVERTISMENT_CALLBACK +int myAdvertCallback(BLE_ESP32::ble_advertisment_t *pStruct) { + + // indicate others can also hear this + // to say 'I want this exclusively', return true. + return 0; + +} +#endif +/*********************************************************************************************\ + * End of Example Advertisment callback +\*********************************************************************************************/ + + +/*********************************************************************************************\ + * Example Operations callbacks +\*********************************************************************************************/ +#ifdef EXAMPLE_OPERATION_CALLBACK + +// this one is used to demonstrate processing ALL operations +int myOpCallback(BLE_ESP32::generic_sensor_t *pStruct){ + AddLog(LOG_LEVEL_INFO,PSTR("BLE: myOpCallback")); + return 0; // return true to block MQTT broadcast +} + +// this one is used to demonstrate processing of ONE specific operation +int myOpCallback2(BLE_ESP32::generic_sensor_t *pStruct){ + AddLog(LOG_LEVEL_INFO,PSTR("BLE: myOpCallback2")); + return 1; // return true to block MQTT broadcast +} +#endif +/*********************************************************************************************\ + * End of Example Operations callbacks +\*********************************************************************************************/ + +void installExamples(){ +#ifdef EXAMPLE_ADVERTISMENT_CALLBACK + BLE_ESP32::registerForAdvertismentCallbacks((const char *)"test myOpCallback", &myAdvertCallback); +#endif + +#ifdef EXAMPLE_OPERATION_CALLBACK + BLE_ESP32:registerForOpCallbacks((const char *)"test myOpCallback", &myOpCallback); +#endif +} + +void sendExample(){ +#ifdef EXAMPLE_OPERATION_CALLBACK + BLE_ESP32::generic_sensor_t *op = nullptr; + int res = BLE_ESP32::newOperation(&op); + if (!res){ + AddLog(LOG_LEVEL_ERROR,PSTR("BLE: Could not create new operation")); + return; + } + strncpy(op->MAC, "001A22092EE0", sizeof(op->MAC)); + strncpy(op->serviceStr, "3e135142-654f-9090-134a-a6ff5bb77046", sizeof(op->serviceStr)); + strncpy(op->characteristicStr, "3fa4585a-ce4a-3bad-db4b-b8df8179ea09", sizeof(op->characteristicStr)); + strncpy(op->notificationCharacteristicStr, "d0e8434d-cd29-0996-af41-6c90f4e0eb2a", sizeof(op->notificationCharacteristicStr)); + op->writelen = BLE_ESP32::fromHex(op->dataToWrite, (char *)"4040", sizeof(op->dataToWrite)); + + // this op will call us back on complete or failure. + op->completecallback = (void *)myOpCallback2; + res = BLE_ESP32::extQueueOperation(&op); + 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")); + return; + } + +#endif +} + + + +#endif +#endif // ESP32 + + diff --git a/tasmota/xdrv_80_odroidgo.ino b/tasmota/xdrv_80_esp32_odroidgo.ino similarity index 91% rename from tasmota/xdrv_80_odroidgo.ino rename to tasmota/xdrv_80_esp32_odroidgo.ino index e1d7ed412..bd7fd8cc1 100644 --- a/tasmota/xdrv_80_odroidgo.ino +++ b/tasmota/xdrv_80_esp32_odroidgo.ino @@ -1,7 +1,7 @@ /* - xdrv_81_webcam.ino - ESP32 webcam support for Tasmota + xdrv_81_esp32_odroidgo.ino - ESP32 odroid go support for Tasmota - Copyright (C) 2021 Gerhard Mutz and Theo Arends + Copyright (C) 2021 Theo Arends This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/tasmota/xdrv_81_webcam.ino b/tasmota/xdrv_81_esp32_webcam.ino old mode 100755 new mode 100644 similarity index 75% rename from tasmota/xdrv_81_webcam.ino rename to tasmota/xdrv_81_esp32_webcam.ino index 179655dd4..f7a5a5713 --- a/tasmota/xdrv_81_webcam.ino +++ b/tasmota/xdrv_81_esp32_webcam.ino @@ -1,5 +1,5 @@ /* - xdrv_81_webcam.ino - ESP32 webcam support for Tasmota + xdrv_81_esp32_webcam.ino - ESP32 webcam support for Tasmota Copyright (C) 2021 Gerhard Mutz and Theo Arends @@ -44,6 +44,8 @@ * WcSaturation = Set picture Saturation -2 ... +2 * WcBrightness = Set picture Brightness -2 ... +2 * WcContrast = Set picture Contrast -2 ... +2 + * WcInit = Init Camera Interface + * WcRtsp = Control RTSP Server, 0=disable, 1=enable (forces restart) (if defined ENABLE_RTSPSERVER) * * Only boards with PSRAM should be used. To enable PSRAM board should be se set to esp32cam in common32 of platform_override.ini * board = esp32cam @@ -54,6 +56,7 @@ * not tolerate any capictive load * flash led = gpio 4 * red led = gpio 33 + * optional rtsp url: rtsp://xxx.xxx.xxx.xxx:8554/mjpeg/1 */ /*********************************************************************************************/ @@ -69,11 +72,8 @@ bool HttpCheckPriviledgedAccess(bool); extern ESP8266WebServer *Webserver; -ESP8266WebServer *CamServer; #define BOUNDARY "e8b8c539-047d-4777-a985-fbba6edff11e" -WiFiClient client; - // CAMERA_MODEL_AI_THINKER default template pins #define PWDN_GPIO_NUM 32 @@ -94,29 +94,50 @@ WiFiClient client; #define HREF_GPIO_NUM 23 #define PCLK_GPIO_NUM 22 -struct { - uint8_t up; - uint16_t width; - uint16_t height; - uint8_t stream_active; -#ifdef USE_FACE_DETECT - uint8_t faces; - uint16_t face_detect_time; +#ifndef MAX_PICSTORE +#define MAX_PICSTORE 4 #endif -} Wc; +struct PICSTORE { + uint8_t *buff; + uint32_t len; +}; #ifdef ENABLE_RTSPSERVER #include #include #include #include -WiFiServer rtspServer(8554); -CStreamer *rtsp_streamer; -CRtspSession *rtsp_session; -WiFiClient rtsp_client; -uint8_t rtsp_start; -OV2640 cam; -#endif +#ifndef RTSP_FRAME_TIME +#define RTSP_FRAME_TIME 100 +#endif // RTSP_FRAME_TIME +#endif // ENABLE_RTSPSERVER + +struct { + uint8_t up; + uint16_t width; + uint16_t height; + uint8_t stream_active; + WiFiClient client; + ESP8266WebServer *CamServer; + struct PICSTORE picstore[MAX_PICSTORE]; +#ifdef USE_FACE_DETECT + uint8_t faces; + uint16_t face_detect_time; + uint32_t face_ltime; + mtmn_config_t mtmn_config = {0}; +#endif // USE_FACE_DETECT +#ifdef ENABLE_RTSPSERVER + WiFiServer *rtspp; + CStreamer *rtsp_streamer; + CRtspSession *rtsp_session; + WiFiClient rtsp_client; + uint8_t rtsp_start; + OV2640 cam; + uint32_t rtsp_lastframe_time; +#endif // ENABLE_RTSPSERVER +} Wc; + + /*********************************************************************************************/ @@ -153,7 +174,7 @@ uint32_t WcSetup(int32_t fsiz) { if (Wc.up) { esp_camera_deinit(); - AddLog_P(LOG_LEVEL_DEBUG, PSTR("CAM: Deinit")); + AddLog(LOG_LEVEL_DEBUG, PSTR("CAM: Deinit")); //return Wc.up; } Wc.up = 0; @@ -186,7 +207,7 @@ uint32_t WcSetup(int32_t fsiz) { config.pin_pwdn = (PinUsed(GPIO_WEBCAM_PWDN)) ? Pin(GPIO_WEBCAM_PWDN) : -1; // PWDN_GPIO_NUM; config.pin_reset = (PinUsed(GPIO_WEBCAM_RESET)) ? Pin(GPIO_WEBCAM_RESET) : -1; // RESET_GPIO_NUM; - AddLog_P(LOG_LEVEL_DEBUG, PSTR("CAM: User template")); + AddLog(LOG_LEVEL_DEBUG, PSTR("CAM: User template")); } else { // defaults to AI THINKER config.pin_d0 = Y2_GPIO_NUM; @@ -205,7 +226,7 @@ uint32_t WcSetup(int32_t fsiz) { config.pin_sscb_scl = SIOC_GPIO_NUM; config.pin_pwdn = PWDN_GPIO_NUM; config.pin_reset = RESET_GPIO_NUM; - AddLog_P(LOG_LEVEL_DEBUG, PSTR("CAM: Default template")); + AddLog(LOG_LEVEL_DEBUG, PSTR("CAM: Default template")); } //ESP.getPsramSize() @@ -220,15 +241,15 @@ uint32_t WcSetup(int32_t fsiz) { config.frame_size = FRAMESIZE_UXGA; config.jpeg_quality = 10; config.fb_count = 2; - AddLog_P(LOG_LEVEL_DEBUG, PSTR("CAM: PSRAM found")); + AddLog(LOG_LEVEL_DEBUG, PSTR("CAM: PSRAM found")); } else { config.frame_size = FRAMESIZE_VGA; config.jpeg_quality = 12; config.fb_count = 1; - AddLog_P(LOG_LEVEL_DEBUG, PSTR("CAM: PSRAM not found")); + AddLog(LOG_LEVEL_DEBUG, PSTR("CAM: PSRAM not found")); } -// AddLog_P(LOG_LEVEL_INFO, PSTR("CAM: heap check 1: %d"),ESP_getFreeHeap()); +// AddLog(LOG_LEVEL_INFO, PSTR("CAM: heap check 1: %d"),ESP_getFreeHeap()); // stupid workaround camera diver eats up static ram should prefer PSRAM // so we steal static ram to force driver to alloc PSRAM @@ -239,11 +260,11 @@ uint32_t WcSetup(int32_t fsiz) { if (x) { free(x); } if (err != ESP_OK) { - AddLog_P(LOG_LEVEL_INFO, PSTR("CAM: Init failed with error 0x%x"), err); + AddLog(LOG_LEVEL_INFO, PSTR("CAM: Init failed with error 0x%x"), err); return 0; } -// AddLog_P(LOG_LEVEL_INFO, PSTR("CAM: heap check 2: %d"),ESP_getFreeHeap()); +// AddLog(LOG_LEVEL_INFO, PSTR("CAM: heap check 2: %d"),ESP_getFreeHeap()); sensor_t * wc_s = esp_camera_sensor_get(); @@ -258,7 +279,7 @@ uint32_t WcSetup(int32_t fsiz) { camera_fb_t *wc_fb = esp_camera_fb_get(); if (!wc_fb) { - AddLog_P(LOG_LEVEL_INFO, PSTR("CAM: Init failed to get the frame on time")); + AddLog(LOG_LEVEL_INFO, PSTR("CAM: Init failed to get the frame on time")); return 0; } Wc.width = wc_fb->width; @@ -269,7 +290,7 @@ uint32_t WcSetup(int32_t fsiz) { fd_init(); #endif - AddLog_P(LOG_LEVEL_INFO, PSTR("CAM: Initialized")); + AddLog(LOG_LEVEL_INFO, PSTR("CAM: Initialized")); Wc.up = 1; if (psram) { Wc.up = 2; } @@ -336,18 +357,21 @@ uint32_t WcGetHeight(void) { /*********************************************************************************************/ +struct WC_Motion { uint16_t motion_detect; uint32_t motion_ltime; uint32_t motion_trigger; uint32_t motion_brightness; uint8_t *last_motion_buffer; +} wc_motion; + uint32_t WcSetMotionDetect(int32_t value) { - if (value >= 0) { motion_detect = value; } + if (value >= 0) { wc_motion.motion_detect = value; } if (-1 == value) { - return motion_trigger; + return wc_motion.motion_trigger; } else { - return motion_brightness; + return wc_motion.motion_brightness; } } @@ -356,22 +380,22 @@ void WcDetectMotion(void) { camera_fb_t *wc_fb; uint8_t *out_buf = 0; - if ((millis()-motion_ltime) > motion_detect) { - motion_ltime = millis(); + if ((millis()-wc_motion.motion_ltime) > wc_motion.motion_detect) { + wc_motion.motion_ltime = millis(); wc_fb = esp_camera_fb_get(); if (!wc_fb) { return; } - if (!last_motion_buffer) { - last_motion_buffer=(uint8_t *)heap_caps_malloc((wc_fb->width*wc_fb->height)+4, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT); + if (!wc_motion.last_motion_buffer) { + wc_motion.last_motion_buffer = (uint8_t *)heap_caps_malloc((wc_fb->width*wc_fb->height) + 4, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT); } - if (last_motion_buffer) { + if (wc_motion.last_motion_buffer) { if (PIXFORMAT_JPEG == wc_fb->format) { out_buf = (uint8_t *)heap_caps_malloc((wc_fb->width*wc_fb->height*3)+4, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT); if (out_buf) { fmt2rgb888(wc_fb->buf, wc_fb->len, wc_fb->format, out_buf); uint32_t x, y; uint8_t *pxi = out_buf; - uint8_t *pxr = last_motion_buffer; + uint8_t *pxr = wc_motion.last_motion_buffer; // convert to bw uint64_t accu = 0; uint64_t bright = 0; @@ -386,8 +410,8 @@ void WcDetectMotion(void) { bright += gray; } } - motion_trigger = accu / ((wc_fb->height * wc_fb->width) / 100); - motion_brightness = bright / ((wc_fb->height * wc_fb->width) / 100); + wc_motion.motion_trigger = accu / ((wc_fb->height * wc_fb->width) / 100); + wc_motion.motion_brightness = bright / ((wc_fb->height * wc_fb->width) / 100); free(out_buf); } } @@ -400,22 +424,20 @@ void WcDetectMotion(void) { #ifdef USE_FACE_DETECT -static mtmn_config_t mtmn_config = {0}; - void fd_init(void) { - mtmn_config.type = FAST; - mtmn_config.min_face = 80; - mtmn_config.pyramid = 0.707; - mtmn_config.pyramid_times = 4; - mtmn_config.p_threshold.score = 0.6; - mtmn_config.p_threshold.nms = 0.7; - mtmn_config.p_threshold.candidate_number = 20; - mtmn_config.r_threshold.score = 0.7; - mtmn_config.r_threshold.nms = 0.7; - mtmn_config.r_threshold.candidate_number = 10; - mtmn_config.o_threshold.score = 0.7; - mtmn_config.o_threshold.nms = 0.7; - mtmn_config.o_threshold.candidate_number = 1; + Wc.mtmn_config.type = FAST; + Wc.mtmn_config.min_face = 80; + Wc.mtmn_config.pyramid = 0.707; + Wc.mtmn_config.pyramid_times = 4; + Wc.mtmn_config.p_threshold.score = 0.6; + Wc.mtmn_config.p_threshold.nms = 0.7; + Wc.mtmn_config.p_threshold.candidate_number = 20; + Wc.mtmn_config.r_threshold.score = 0.7; + Wc.mtmn_config.r_threshold.nms = 0.7; + Wc.mtmn_config.r_threshold.candidate_number = 10; + Wc.mtmn_config.o_threshold.score = 0.7; + Wc.mtmn_config.o_threshold.nms = 0.7; + Wc.mtmn_config.o_threshold.candidate_number = 1; } #define FACE_COLOR_WHITE 0x00FFFFFF @@ -473,8 +495,6 @@ uint32_t WcSetFaceDetect(int32_t value) { return Wc.faces; } -uint32_t face_ltime; - uint32_t WcDetectFace(void); uint32_t WcDetectFace(void) { @@ -486,14 +506,14 @@ uint32_t WcDetectFace(void) { int face_id = 0; camera_fb_t *fb; - if ((millis() - face_ltime) > Wc.face_detect_time) { - face_ltime = millis(); + if ((millis() - Wc.face_ltime) > Wc.face_detect_time) { + Wc.face_ltime = millis(); fb = esp_camera_fb_get(); if (!fb) { return ESP_FAIL; } image_matrix = dl_matrix3du_alloc(1, fb->width, fb->height, 3); if (!image_matrix) { - AddLog_P(LOG_LEVEL_DEBUG, PSTR("CAM: dl_matrix3du_alloc failed")); + AddLog(LOG_LEVEL_DEBUG, PSTR("CAM: dl_matrix3du_alloc failed")); esp_camera_fb_return(fb); return ESP_FAIL; } @@ -507,11 +527,11 @@ uint32_t WcDetectFace(void) { esp_camera_fb_return(fb); if (!s){ dl_matrix3du_free(image_matrix); - AddLog_P(LOG_LEVEL_DEBUG, PSTR("CAM: to rgb888 failed")); + AddLog(LOG_LEVEL_DEBUG, PSTR("CAM: to rgb888 failed")); return ESP_FAIL; } - box_array_t *net_boxes = face_detect(image_matrix, &mtmn_config); + box_array_t *net_boxes = face_detect(image_matrix, &Wc.mtmn_config); if (net_boxes){ detected = true; Wc.faces = net_boxes->len; @@ -536,15 +556,6 @@ uint32_t WcDetectFace(void) { /*********************************************************************************************/ -#ifndef MAX_PICSTORE -#define MAX_PICSTORE 4 -#endif -struct PICSTORE { - uint8_t *buff; - uint32_t len; -}; - -struct PICSTORE picstore[MAX_PICSTORE]; #ifdef COPYFRAME struct PICSTORE tmp_picstore; @@ -552,8 +563,8 @@ struct PICSTORE tmp_picstore; uint32_t WcGetPicstore(int32_t num, uint8_t **buff) { if (num<0) { return MAX_PICSTORE; } - *buff = picstore[num].buff; - return picstore[num].len; + *buff = Wc.picstore[num].buff; + return Wc.picstore[num].len; } uint32_t WcGetFrame(int32_t bnum) { @@ -566,8 +577,8 @@ uint32_t WcGetFrame(int32_t bnum) { if (bnum < -MAX_PICSTORE) { bnum=-1; } bnum = -bnum; bnum--; - if (picstore[bnum].buff) { free(picstore[bnum].buff); } - picstore[bnum].len = 0; + if (Wc.picstore[bnum].buff) { free(Wc.picstore[bnum].buff); } + Wc.picstore[bnum].len = 0; return 0; } @@ -583,7 +594,7 @@ uint32_t WcGetFrame(int32_t bnum) { wc_fb = esp_camera_fb_get(); if (!wc_fb) { - AddLog_P(LOG_LEVEL_DEBUG, PSTR("CAM: Can't get frame")); + AddLog(LOG_LEVEL_DEBUG, PSTR("CAM: Can't get frame")); return 0; } if (!bnum) { @@ -608,18 +619,18 @@ uint32_t WcGetFrame(int32_t bnum) { pcopy: if ((bnum < 1) || (bnum > MAX_PICSTORE)) { bnum = 1; } bnum--; - if (picstore[bnum].buff) { free(picstore[bnum].buff); } - picstore[bnum].buff = (uint8_t *)heap_caps_malloc(_jpg_buf_len+4, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT); - if (picstore[bnum].buff) { - memcpy(picstore[bnum].buff, _jpg_buf, _jpg_buf_len); - picstore[bnum].len = _jpg_buf_len; + if (Wc.picstore[bnum].buff) { free(Wc.picstore[bnum].buff); } + Wc.picstore[bnum].buff = (uint8_t *)heap_caps_malloc(_jpg_buf_len+4, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT); + if (Wc.picstore[bnum].buff) { + memcpy(Wc.picstore[bnum].buff, _jpg_buf, _jpg_buf_len); + Wc.picstore[bnum].len = _jpg_buf_len; } else { - AddLog_P(LOG_LEVEL_DEBUG, PSTR("CAM: Can't allocate picstore")); - picstore[bnum].len = 0; + AddLog(LOG_LEVEL_DEBUG, PSTR("CAM: Can't allocate picstore")); + Wc.picstore[bnum].len = 0; } if (wc_fb) { esp_camera_fb_return(wc_fb); } if (jpeg_converted) { free(_jpg_buf); } - if (!picstore[bnum].buff) { return 0; } + if (!Wc.picstore[bnum].buff) { return 0; } return _jpg_buf_len; } @@ -657,24 +668,24 @@ void HandleImage(void) { if (wc_fb) { esp_camera_fb_return(wc_fb); } } else { bnum--; - if (!picstore[bnum].len) { - AddLog_P(LOG_LEVEL_DEBUG, PSTR("CAM: No image #: %d"), bnum); + if (!Wc.picstore[bnum].len) { + AddLog(LOG_LEVEL_DEBUG, PSTR("CAM: No image #: %d"), bnum); return; } - client.write((char *)picstore[bnum].buff, picstore[bnum].len); + client.write((char *)Wc.picstore[bnum].buff, Wc.picstore[bnum].len); } client.stop(); - AddLog_P(LOG_LEVEL_DEBUG_MORE, PSTR("CAM: Sending image #: %d"), bnum+1); + AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("CAM: Sending image #: %d"), bnum+1); } void HandleImageBasic(void) { if (!HttpCheckPriviledgedAccess()) { return; } - AddLog_P(LOG_LEVEL_DEBUG_MORE, PSTR(D_LOG_HTTP "Capture image")); + AddLog(LOG_LEVEL_DEBUG_MORE, PSTR(D_LOG_HTTP "Capture image")); if (Settings.webcam_config.stream) { - if (!CamServer) { + if (!Wc.CamServer) { WcStreamControl(); } } @@ -682,7 +693,7 @@ void HandleImageBasic(void) { camera_fb_t *wc_fb; wc_fb = esp_camera_fb_get(); // Acquire frame if (!wc_fb) { - AddLog_P(LOG_LEVEL_DEBUG, PSTR("CAM: Frame buffer could not be acquired")); + AddLog(LOG_LEVEL_DEBUG, PSTR("CAM: Frame buffer could not be acquired")); return; } @@ -709,16 +720,16 @@ void HandleImageBasic(void) { esp_camera_fb_return(wc_fb); // Free frame buffer - AddLog_P(LOG_LEVEL_DEBUG_MORE, PSTR("CAM: Image sent")); + AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("CAM: Image sent")); } void HandleWebcamMjpeg(void) { - AddLog_P(LOG_LEVEL_DEBUG, PSTR("CAM: Handle camserver")); + AddLog(LOG_LEVEL_DEBUG, PSTR("CAM: Handle camserver")); // if (!Wc.stream_active) { // always restart stream Wc.stream_active = 1; - client = CamServer->client(); - AddLog_P(LOG_LEVEL_DEBUG, PSTR("CAM: Create client")); + Wc.client = Wc.CamServer->client(); + AddLog(LOG_LEVEL_DEBUG, PSTR("CAM: Create client")); // } } @@ -731,15 +742,15 @@ void HandleWebcamMjpegTask(void) { uint32_t tlen; bool jpeg_converted = false; - if (!client.connected()) { - AddLog_P(LOG_LEVEL_DEBUG, PSTR("CAM: Client fail")); + if (!Wc.client.connected()) { + AddLog(LOG_LEVEL_DEBUG, PSTR("CAM: Client fail")); Wc.stream_active = 0; } if (1 == Wc.stream_active) { - client.flush(); - client.setTimeout(3); - AddLog_P(LOG_LEVEL_DEBUG, PSTR("CAM: Start stream")); - client.print("HTTP/1.1 200 OK\r\n" + Wc.client.flush(); + Wc.client.setTimeout(3); + AddLog(LOG_LEVEL_DEBUG, PSTR("CAM: Start stream")); + Wc.client.print("HTTP/1.1 200 OK\r\n" "Content-Type: multipart/x-mixed-replace;boundary=" BOUNDARY "\r\n" "\r\n"); Wc.stream_active = 2; @@ -747,7 +758,7 @@ void HandleWebcamMjpegTask(void) { if (2 == Wc.stream_active) { wc_fb = esp_camera_fb_get(); if (!wc_fb) { - AddLog_P(LOG_LEVEL_DEBUG, PSTR("CAM: Frame fail")); + AddLog(LOG_LEVEL_DEBUG, PSTR("CAM: Frame fail")); Wc.stream_active = 0; } } @@ -755,7 +766,7 @@ void HandleWebcamMjpegTask(void) { if (wc_fb->format != PIXFORMAT_JPEG) { jpeg_converted = frame2jpg(wc_fb, 80, &_jpg_buf, &_jpg_buf_len); if (!jpeg_converted){ - AddLog_P(LOG_LEVEL_DEBUG, PSTR("CAM: JPEG compression failed")); + AddLog(LOG_LEVEL_DEBUG, PSTR("CAM: JPEG compression failed")); _jpg_buf_len = wc_fb->len; _jpg_buf = wc_fb->buf; } @@ -764,17 +775,17 @@ void HandleWebcamMjpegTask(void) { _jpg_buf = wc_fb->buf; } - client.printf("Content-Type: image/jpeg\r\n" + Wc.client.printf("Content-Type: image/jpeg\r\n" "Content-Length: %d\r\n" "\r\n", static_cast(_jpg_buf_len)); - tlen = client.write(_jpg_buf, _jpg_buf_len); + tlen = Wc.client.write(_jpg_buf, _jpg_buf_len); /* if (tlen!=_jpg_buf_len) { esp_camera_fb_return(wc_fb); Wc.stream_active=0; - AddLog_P(LOG_LEVEL_DEBUG, PSTR("CAM: Send fail")); + AddLog(LOG_LEVEL_DEBUG, PSTR("CAM: Send fail")); }*/ - client.print("\r\n--" BOUNDARY "\r\n"); + Wc.client.print("\r\n--" BOUNDARY "\r\n"); #ifdef COPYFRAME if (tmp_picstore.buff) { free(tmp_picstore.buff); } @@ -789,20 +800,20 @@ void HandleWebcamMjpegTask(void) { if (jpeg_converted) { free(_jpg_buf); } esp_camera_fb_return(wc_fb); - //AddLog_P(LOG_LEVEL_DEBUG, PSTR("CAM: send frame")); + //AddLog(LOG_LEVEL_DEBUG, PSTR("CAM: send frame")); } if (0 == Wc.stream_active) { - AddLog_P(LOG_LEVEL_DEBUG, PSTR("CAM: Stream exit")); - client.flush(); - client.stop(); + AddLog(LOG_LEVEL_DEBUG, PSTR("CAM: Stream exit")); + Wc.client.flush(); + Wc.client.stop(); } } void HandleWebcamRoot(void) { //CamServer->redirect("http://" + String(ip) + ":81/cam.mjpeg"); - CamServer->sendHeader("Location", WiFi.localIP().toString() + ":81/cam.mjpeg"); - CamServer->send(302, "", ""); - AddLog_P(LOG_LEVEL_DEBUG, PSTR("CAM: Root called")); + Wc.CamServer->sendHeader("Location", WiFi.localIP().toString() + ":81/cam.mjpeg"); + Wc.CamServer->send(302, "", ""); + AddLog(LOG_LEVEL_DEBUG, PSTR("CAM: Root called")); } /*********************************************************************************************/ @@ -813,21 +824,21 @@ uint32_t WcSetStreamserver(uint32_t flag) { Wc.stream_active = 0; if (flag) { - if (!CamServer) { - CamServer = new ESP8266WebServer(81); - CamServer->on("/", HandleWebcamRoot); - CamServer->on("/cam.mjpeg", HandleWebcamMjpeg); - CamServer->on("/cam.jpg", HandleWebcamMjpeg); - CamServer->on("/stream", HandleWebcamMjpeg); - AddLog_P(LOG_LEVEL_DEBUG, PSTR("CAM: Stream init")); - CamServer->begin(); + if (!Wc.CamServer) { + Wc.CamServer = new ESP8266WebServer(81); + Wc.CamServer->on("/", HandleWebcamRoot); + Wc.CamServer->on("/cam.mjpeg", HandleWebcamMjpeg); + Wc.CamServer->on("/cam.jpg", HandleWebcamMjpeg); + Wc.CamServer->on("/stream", HandleWebcamMjpeg); + AddLog(LOG_LEVEL_DEBUG, PSTR("CAM: Stream init")); + Wc.CamServer->begin(); } } else { - if (CamServer) { - CamServer->stop(); - delete CamServer; - CamServer = NULL; - AddLog_P(LOG_LEVEL_DEBUG, PSTR("CAM: Stream exit")); + if (Wc.CamServer) { + Wc.CamServer->stop(); + delete Wc.CamServer; + Wc.CamServer = NULL; + AddLog(LOG_LEVEL_DEBUG, PSTR("CAM: Stream exit")); } } return 0; @@ -839,61 +850,58 @@ void WcStreamControl() { } /*********************************************************************************************/ -#ifdef ENABLE_RTSPSERVER -static uint32_t rtsp_lastframe_time; -#ifndef RTSP_FRAME_TIME -#define RTSP_FRAME_TIME 100 -#endif -#endif + void WcLoop(void) { - if (CamServer) { - CamServer->handleClient(); + if (Wc.CamServer) { + Wc.CamServer->handleClient(); if (Wc.stream_active) { HandleWebcamMjpegTask(); } } - if (motion_detect) { WcDetectMotion(); } + if (wc_motion.motion_detect) { WcDetectMotion(); } #ifdef USE_FACE_DETECT if (Wc.face_detect_time) { WcDetectFace(); } #endif #ifdef ENABLE_RTSPSERVER + if (Settings.webcam_config.rtsp && !TasmotaGlobal.global_state.wifi_down && Wc.up) { + if (!Wc.rtsp_start) { + Wc.rtspp = new WiFiServer(8554); + Wc.rtspp->begin(); + Wc.rtsp_start = 1; + AddLog(LOG_LEVEL_INFO, PSTR("CAM: RTSP init")); + Wc.rtsp_lastframe_time = millis(); + } - if (!rtsp_start && !TasmotaGlobal.global_state.wifi_down && Wc.up) { - rtspServer.begin(); - rtsp_start = 1; - AddLog_P(LOG_LEVEL_INFO, PSTR("CAM: RTSP init")); - rtsp_lastframe_time = millis(); - } - - // If we have an active client connection, just service that until gone - if (rtsp_session) { - rtsp_session->handleRequests(0); // we don't use a timeout here, + // If we have an active client connection, just service that until gone + if (Wc.rtsp_session) { + Wc.rtsp_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-rtsp_lastframe_time) > RTSP_FRAME_TIME) { - rtsp_session->broadcastCurrentFrame(now); - rtsp_lastframe_time = now; - // AddLog_P(LOG_LEVEL_INFO, PSTR("CAM: RTSP session frame")); + if ((now-Wc.rtsp_lastframe_time) > RTSP_FRAME_TIME) { + Wc.rtsp_session->broadcastCurrentFrame(now); + Wc.rtsp_lastframe_time = now; + // AddLog(LOG_LEVEL_INFO, PSTR("CAM: RTSP session frame")); } - if (rtsp_session->m_stopped) { - delete rtsp_session; - delete rtsp_streamer; - rtsp_session = NULL; - rtsp_streamer = NULL; - AddLog_P(LOG_LEVEL_INFO, PSTR("CAM: RTSP stopped")); + if (Wc.rtsp_session->m_stopped) { + delete Wc.rtsp_session; + delete Wc.rtsp_streamer; + Wc.rtsp_session = NULL; + Wc.rtsp_streamer = NULL; + AddLog(LOG_LEVEL_INFO, PSTR("CAM: RTSP stopped")); } - } - else { - rtsp_client = rtspServer.accept(); - if (rtsp_client) { - rtsp_streamer = new OV2640Streamer(&rtsp_client, cam); // our streamer for UDP/TCP based RTP transport - rtsp_session = new CRtspSession(&rtsp_client, rtsp_streamer); // our threads RTSP session and state - AddLog_P(LOG_LEVEL_INFO, PSTR("CAM: RTSP stream created")); + } + else { + Wc.rtsp_client = Wc.rtspp->accept(); + if (Wc.rtsp_client) { + Wc.rtsp_streamer = new OV2640Streamer(&Wc.rtsp_client, Wc.cam); // our streamer for UDP/TCP based RTP transport + Wc.rtsp_session = new CRtspSession(&Wc.rtsp_client, Wc.rtsp_streamer); // our threads RTSP session and state + AddLog(LOG_LEVEL_INFO, PSTR("CAM: RTSP stream created")); } + } } -#endif +#endif // ENABLE_RTSPSERVER } void WcPicSetup(void) { @@ -904,14 +912,14 @@ void WcPicSetup(void) { void WcShowStream(void) { if (Settings.webcam_config.stream) { -// if (!CamServer || !Wc.up) { - if (!CamServer) { +// if (!Wc.CamServer || !Wc.up) { + if (!Wc.CamServer) { WcStreamControl(); delay(50); // Give the webcam webserver some time to prepare the stream } - if (CamServer && Wc.up) { - WSContentSend_P(PSTR("

Webcam stream

"), - WiFi.localIP().toString().c_str()); + if (Wc.CamServer && Wc.up) { + WSContentSend_P(PSTR("

Webcam stream

"), + (uint32_t)WiFi.localIP()); } } } @@ -941,24 +949,39 @@ void WcInit(void) { #define D_CMND_WC_BRIGHTNESS "Brightness" #define D_CMND_WC_CONTRAST "Contrast" #define D_CMND_WC_INIT "Init" +#define D_CMND_RTSP "Rtsp" const char kWCCommands[] PROGMEM = D_PRFX_WEBCAM "|" // Prefix "|" D_CMND_WC_STREAM "|" D_CMND_WC_RESOLUTION "|" D_CMND_WC_MIRROR "|" D_CMND_WC_FLIP "|" D_CMND_WC_SATURATION "|" D_CMND_WC_BRIGHTNESS "|" D_CMND_WC_CONTRAST "|" D_CMND_WC_INIT +#ifdef ENABLE_RTSPSERVER + "|" D_CMND_RTSP +#endif // ENABLE_RTSPSERVER ; void (* const WCCommand[])(void) PROGMEM = { &CmndWebcam, &CmndWebcamStream, &CmndWebcamResolution, &CmndWebcamMirror, &CmndWebcamFlip, &CmndWebcamSaturation, &CmndWebcamBrightness, &CmndWebcamContrast, &CmndWebcamInit +#ifdef ENABLE_RTSPSERVER + , &CmndWebRtsp +#endif // ENABLE_RTSPSERVER }; void CmndWebcam(void) { Response_P(PSTR("{\"" D_PRFX_WEBCAM "\":{\"" D_CMND_WC_STREAM "\":%d,\"" D_CMND_WC_RESOLUTION "\":%d,\"" D_CMND_WC_MIRROR "\":%d,\"" D_CMND_WC_FLIP "\":%d,\"" - D_CMND_WC_SATURATION "\":%d,\"" D_CMND_WC_BRIGHTNESS "\":%d,\"" D_CMND_WC_CONTRAST "\":%d}}"), + D_CMND_WC_SATURATION "\":%d,\"" D_CMND_WC_BRIGHTNESS "\":%d,\"" D_CMND_WC_CONTRAST "\":%d" +#ifdef ENABLE_RTSPSERVER + ",\"" D_CMND_RTSP "\":%d" +#endif // ENABLE_RTSPSERVER + "}}"), Settings.webcam_config.stream, Settings.webcam_config.resolution, Settings.webcam_config.mirror, Settings.webcam_config.flip, - Settings.webcam_config.saturation -2, Settings.webcam_config.brightness -2, Settings.webcam_config.contrast -2); + Settings.webcam_config.saturation -2, Settings.webcam_config.brightness -2, Settings.webcam_config.contrast -2 +#ifdef ENABLE_RTSPSERVER + , Settings.webcam_config.rtsp +#endif // ENABLE_RTSPSERVER + ); } void CmndWebcamStream(void) { @@ -1022,6 +1045,17 @@ void CmndWebcamInit(void) { ResponseCmndDone(); } +#ifdef ENABLE_RTSPSERVER +void CmndWebRtsp(void) { + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 1)) { + Settings.webcam_config.rtsp = XdrvMailbox.payload; + TasmotaGlobal.restart_flag = 2; + } + ResponseCmndStateText(Settings.webcam_config.rtsp); +} +#endif // ENABLE_RTSPSERVER + + /*********************************************************************************************\ * Interface \*********************************************************************************************/ diff --git a/tasmota/xdrv_82_ethernet.ino b/tasmota/xdrv_82_esp32_ethernet.ino similarity index 86% rename from tasmota/xdrv_82_ethernet.ino rename to tasmota/xdrv_82_esp32_ethernet.ino index 7d7ae3c59..1eae96991 100644 --- a/tasmota/xdrv_82_ethernet.ino +++ b/tasmota/xdrv_82_esp32_ethernet.ino @@ -1,5 +1,5 @@ /* - xdrv_82_ethernet.ino - ESP32 (PoE) ethernet support for Tasmota + xdrv_82_esp32_ethernet.ino - ESP32 (PoE) ethernet support for Tasmota Copyright (C) 2021 Theo Arends @@ -85,27 +85,27 @@ char eth_hostname[sizeof(TasmotaGlobal.hostname)]; void EthernetEvent(WiFiEvent_t event) { switch (event) { case SYSTEM_EVENT_ETH_START: - AddLog_P(LOG_LEVEL_DEBUG, PSTR("ETH: " D_ATTEMPTING_CONNECTION)); + AddLog(LOG_LEVEL_DEBUG, PSTR("ETH: " D_ATTEMPTING_CONNECTION)); ETH.setHostname(eth_hostname); break; case SYSTEM_EVENT_ETH_CONNECTED: - AddLog_P(LOG_LEVEL_INFO, PSTR("ETH: " D_CONNECTED " at %dMbps%s"), + AddLog(LOG_LEVEL_INFO, PSTR("ETH: " D_CONNECTED " at %dMbps%s"), ETH.linkSpeed(), (ETH.fullDuplex()) ? " Full Duplex" : ""); break; case SYSTEM_EVENT_ETH_GOT_IP: - AddLog_P(LOG_LEVEL_DEBUG, PSTR("ETH: Mac %s, IPAddress %s, Hostname %s"), - ETH.macAddress().c_str(), ETH.localIP().toString().c_str(), eth_hostname); - Settings.ip_address[1] = (uint32_t)ETH.gatewayIP(); - Settings.ip_address[2] = (uint32_t)ETH.subnetMask(); - Settings.ip_address[3] = (uint32_t)ETH.dnsIP(); + AddLog(LOG_LEVEL_DEBUG, PSTR("ETH: Mac %s, IPAddress %_I, Hostname %s"), + ETH.macAddress().c_str(), (uint32_t)ETH.localIP(), eth_hostname); + Settings.ipv4_address[1] = (uint32_t)ETH.gatewayIP(); + Settings.ipv4_address[2] = (uint32_t)ETH.subnetMask(); + Settings.ipv4_address[3] = (uint32_t)ETH.dnsIP(); TasmotaGlobal.global_state.eth_down = 0; break; case SYSTEM_EVENT_ETH_DISCONNECTED: - AddLog_P(LOG_LEVEL_INFO, PSTR("ETH: Disconnected")); + AddLog(LOG_LEVEL_INFO, PSTR("ETH: Disconnected")); TasmotaGlobal.global_state.eth_down = 1; break; case SYSTEM_EVENT_ETH_STOP: - AddLog_P(LOG_LEVEL_DEBUG, PSTR("ETH: Stopped")); + AddLog(LOG_LEVEL_DEBUG, PSTR("ETH: Stopped")); TasmotaGlobal.global_state.eth_down = 1; break; default: @@ -116,10 +116,16 @@ void EthernetEvent(WiFiEvent_t event) { void EthernetInit(void) { if (!Settings.flag4.network_ethernet) { return; } if (!PinUsed(GPIO_ETH_PHY_MDC) && !PinUsed(GPIO_ETH_PHY_MDIO)) { - AddLog_P(LOG_LEVEL_DEBUG, PSTR("ETH: No ETH MDC and/or ETH MDIO GPIO defined")); + AddLog(LOG_LEVEL_DEBUG, PSTR("ETH: No ETH MDC and/or ETH MDIO GPIO defined")); return; } + if (WT32_ETH01 == TasmotaGlobal.module_type) { + Settings.eth_address = 1; // EthAddress + Settings.eth_type = ETH_PHY_LAN8720; // EthType + Settings.eth_clk_mode = ETH_CLOCK_GPIO0_IN; // EthClockMode + } + // snprintf_P(Eth.hostname, sizeof(Eth.hostname), PSTR("%s_eth"), TasmotaGlobal.hostname); strlcpy(eth_hostname, TasmotaGlobal.hostname, sizeof(eth_hostname) -5); // Make sure there is room for "_eth" strcat(eth_hostname, "_eth"); @@ -130,7 +136,7 @@ void EthernetInit(void) { int eth_mdc = Pin(GPIO_ETH_PHY_MDC); int eth_mdio = Pin(GPIO_ETH_PHY_MDIO); if (!ETH.begin(Settings.eth_address, eth_power, eth_mdc, eth_mdio, (eth_phy_type_t)Settings.eth_type, (eth_clock_mode_t)Settings.eth_clk_mode)) { - AddLog_P(LOG_LEVEL_DEBUG, PSTR("ETH: Bad PHY type or init error")); + AddLog(LOG_LEVEL_DEBUG, PSTR("ETH: Bad PHY type or init error")); }; } diff --git a/tasmota/xdrv_83_esp32watch.ino b/tasmota/xdrv_83_esp32_watch.ino similarity index 98% rename from tasmota/xdrv_83_esp32watch.ino rename to tasmota/xdrv_83_esp32_watch.ino index ef564813e..70cc1a912 100644 --- a/tasmota/xdrv_83_esp32watch.ino +++ b/tasmota/xdrv_83_esp32_watch.ino @@ -1,5 +1,5 @@ /* - xdrv_83_esp32watch.ino - ESP32 TTGO watch support for Tasmota + xdrv_83_esp32_watch.ino - ESP32 TTGO watch support for Tasmota Copyright (C) 2021 Gerhard Mutz and Theo Arends @@ -378,11 +378,11 @@ uint8_t data; if (ttgo_globs.bma->isDoubleClick()) { ttgo_globs.bma_double_click = true; - //AddLog_P(LOG_LEVEL_INFO, PSTR("double click")); + //AddLog(LOG_LEVEL_INFO, PSTR("double click")); } if (ttgo_globs.bma->isAnyNoMotion()) { ttgo_globs.bma_click = true; - //AddLog_P(LOG_LEVEL_INFO, PSTR("click")); + //AddLog(LOG_LEVEL_INFO, PSTR("click")); } //! setp counter @@ -408,7 +408,7 @@ uint8_t data; } if (ttgo_globs.ttgo_power->isPEKShortPressIRQ()) { ttgo_globs.bma_button = true; - //AddLog_P(LOG_LEVEL_INFO, PSTR("button press")); + //AddLog(LOG_LEVEL_INFO, PSTR("button press")); } ttgo_globs.ttgo_power->clearIRQ(); break; diff --git a/tasmota/xdrv_84_core2.ino b/tasmota/xdrv_84_esp32_core2.ino similarity index 86% rename from tasmota/xdrv_84_core2.ino rename to tasmota/xdrv_84_esp32_core2.ino index 22b80a1b7..dc196536c 100644 --- a/tasmota/xdrv_84_core2.ino +++ b/tasmota/xdrv_84_esp32_core2.ino @@ -1,5 +1,5 @@ /* - xdrv_84_core2.ino - ESP32 m5stack core2 support for Tasmota + xdrv_84_esp32_core2.ino - ESP32 m5stack core2 support for Tasmota Copyright (C) 2021 Gerhard Mutz and Theo Arends @@ -34,7 +34,6 @@ rtc better sync #include #include #include -#include #include #define XDRV_84 84 @@ -49,7 +48,6 @@ struct CORE2_globs { uint8_t wakeup_hour; uint8_t wakeup_minute; uint8_t shutdowndelay; - bool timesynced; } core2_globs; struct CORE2_ADC { @@ -98,7 +96,7 @@ void CORE2_Init(void) { BreakTime(Rtc.utc_time, tmpTime); Rtc.daylight_saving_time = RuleToTime(Settings.tflag[1], RtcTime.year); Rtc.standard_time = RuleToTime(Settings.tflag[0], RtcTime.year); - AddLog_P(LOG_LEVEL_INFO, PSTR("Set time from BM8563 to RTC (" D_UTC_TIME ") %s, (" D_DST_TIME ") %s, (" D_STD_TIME ") %s"), + AddLog(LOG_LEVEL_INFO, PSTR("Set time from BM8563 to 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 TasmotaGlobal.rules_flag.time_init = 1; @@ -262,44 +260,6 @@ uint16_t voltage = 2200; } -/* -void SetRtc(void) { - RTC_TimeTypeDef RTCtime; - RTCtime.Hours = RtcTime.hour; - RTCtime.Minutes = RtcTime.minute; - RTCtime.Seconds = RtcTime.second; - core2_globs.Rtc.SetTime(&RTCtime); - - RTC_DateTypeDef RTCdate; - RTCdate.WeekDay = RtcTime.day_of_week; - RTCdate.Month = RtcTime.month; - RTCdate.Date = RtcTime.day_of_month; - RTCdate.Year = RtcTime.year; - core2_globs.Rtc.SetDate(&RTCdate); -} -*/ - - -// needed for sd card time -void Sync_RTOS_TIME(void) { - - if (Rtc.local_time < START_VALID_TIME || core2_globs.timesynced) return; - - core2_globs.timesynced = 1; -// Set freertos time for sd card - - struct timeval tv; - //tv.tv_sec = Rtc.utc_time; - tv.tv_sec = Rtc.local_time; - tv.tv_usec = 0; - - //struct timezone tz; - //tz.tz_minuteswest = 0; - //tz.tz_dsttime = 0; - //settimeofday(&tv, &tz); - - settimeofday(&tv, NULL); -} void GetRtc(void) { RTC_TimeTypeDef RTCtime; @@ -316,8 +276,8 @@ void GetRtc(void) { RtcTime.day_of_month = RTCdate.Date; RtcTime.year = RTCdate.Year; - AddLog_P(LOG_LEVEL_INFO, PSTR("RTC: %02d:%02d:%02d"), RTCtime.Hours, RTCtime.Minutes, RTCtime.Seconds); - AddLog_P(LOG_LEVEL_INFO, PSTR("RTC: %02d.%02d.%04d"), RTCdate.Date, RTCdate.Month, RTCdate.Year); + AddLog(LOG_LEVEL_INFO, PSTR("RTC: %02d:%02d:%02d"), RTCtime.Hours, RTCtime.Minutes, RTCtime.Seconds); + AddLog(LOG_LEVEL_INFO, PSTR("RTC: %02d.%02d.%04d"), RTCdate.Date, RTCdate.Month, RTCdate.Year); } @@ -361,7 +321,7 @@ void CORE2_EverySecond(void) { if (Rtc.utc_time > START_VALID_TIME && core2_globs.tset==false && abs(Rtc.utc_time - Get_utc()) > 3) { Set_utc(Rtc.utc_time); - AddLog_P(LOG_LEVEL_INFO, PSTR("Write Time TO BM8563 from NTP (" D_UTC_TIME ") %s, (" D_DST_TIME ") %s, (" D_STD_TIME ") %s"), + AddLog(LOG_LEVEL_INFO, PSTR("Write Time TO BM8563 from NTP (" 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()); core2_globs.tset = true; } @@ -372,8 +332,6 @@ void CORE2_EverySecond(void) { CORE2_DoShutdown(); } } - - Sync_RTOS_TIME(); } } diff --git a/tasmota/xdrv_99_debug.ino b/tasmota/xdrv_99_debug.ino index d7b786781..f99815d36 100644 --- a/tasmota/xdrv_99_debug.ino +++ b/tasmota/xdrv_99_debug.ino @@ -174,11 +174,11 @@ void CpuLoadLoop(void) #if defined(F_CPU) && (F_CPU == 160000000L) int CPU_load = 100 - ( (CPU_loops*(1 + 30*TasmotaGlobal.sleep)) / (CPU_load_check *800) ); CPU_loops = CPU_loops / CPU_load_check; - AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "FreeRam %d, CPU %d%%(160MHz), Loops/sec %d"), ESP.getFreeHeap(), CPU_load, CPU_loops); + AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "FreeRam %d, CPU %d%%(160MHz), Loops/sec %d"), ESP.getFreeHeap(), CPU_load, CPU_loops); #else int CPU_load = 100 - ( (CPU_loops*(1 + 30*TasmotaGlobal.sleep)) / (CPU_load_check *400) ); CPU_loops = CPU_loops / CPU_load_check; - AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "FreeRam %d, CPU %d%%(80MHz), Loops/sec %d"), ESP.getFreeHeap(), CPU_load, CPU_loops); + AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "FreeRam %d, CPU %d%%(80MHz), Loops/sec %d"), ESP.getFreeHeap(), CPU_load, CPU_loops); #endif CPU_last_millis = CPU_last_loop_time; CPU_loops = 0; @@ -202,7 +202,7 @@ void DebugFreeMem(void) { register uint32_t *sp asm("a1"); - AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "FreeRam %d, FreeStack %d (%s)"), ESP.getFreeHeap(), 4 * (sp - g_pcont->stack), XdrvMailbox.data); + AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "FreeRam %d, FreeStack %d (%s)"), ESP.getFreeHeap(), 4 * (sp - g_pcont->stack), XdrvMailbox.data); } #endif // ESP8266 @@ -212,7 +212,7 @@ void DebugFreeMem(void) { register uint8_t *sp asm("a1"); - AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "FreeRam %d, FreeStack %d (%s)"), ESP.getFreeHeap(), sp - pxTaskGetStackStart(NULL), XdrvMailbox.data); + AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "FreeRam %d, FreeStack %d (%s)"), ESP.getFreeHeap(), sp - pxTaskGetStackStart(NULL), XdrvMailbox.data); } #endif // ESP8266 - ESP32 @@ -246,7 +246,7 @@ void DebugRtcDump(char* parms) uint16_t srow = strtol(parms, &p, 16) / CFG_COLS; uint16_t mrow = strtol(p, &p, 10); -// AddLog_P(LOG_LEVEL_DEBUG, PSTR("Cnfg: Parms %s, Start row %d, rows %d"), parms, srow, mrow); +// AddLog(LOG_LEVEL_DEBUG, PSTR("Cnfg: Parms %s, Start row %d, rows %d"), parms, srow, mrow); if (0 == mrow) { // Default only 8 lines mrow = 8; @@ -298,7 +298,7 @@ void DebugDump(uint32_t start, uint32_t size) { uint32_t srow = 0; uint32_t mrow = maxrow; -// AddLog_P(LOG_LEVEL_DEBUG, PSTR("Cnfg: Parms %s, Start row %d, rows %d"), parms, srow, mrow); +// AddLog(LOG_LEVEL_DEBUG, PSTR("Cnfg: Parms %s, Start row %d, rows %d"), parms, srow, mrow); if (0 == mrow) { // Default only 8 lines mrow = 8; @@ -349,7 +349,7 @@ void DebugCfgDump(char* parms) uint16_t srow = strtol(parms, &p, 16) / CFG_COLS; uint16_t mrow = strtol(p, &p, 10); -// AddLog_P(LOG_LEVEL_DEBUG, PSTR("Cnfg: Parms %s, Start row %d, rows %d"), parms, srow, mrow); +// AddLog(LOG_LEVEL_DEBUG, PSTR("Cnfg: Parms %s, Start row %d, rows %d"), parms, srow, mrow); if (0 == mrow) { // Default only 8 lines mrow = 8; @@ -428,7 +428,7 @@ void DebugCfgPoke(char* parms) uint32_t ndata32 = (buffer[address +3] << 24) + (buffer[address +2] << 16) + (buffer[address +1] << 8) + buffer[address]; - AddLog_P(LOG_LEVEL_INFO, PSTR("%03X: 0x%0LX (%lu) poked to 0x%0LX (%lu)"), address, data32, data32, ndata32, ndata32); + AddLog(LOG_LEVEL_INFO, PSTR("%03X: 0x%0LX (%lu) poked to 0x%0LX (%lu)"), address, data32, data32, ndata32, ndata32); } void SetFlashMode(uint8_t mode) @@ -574,7 +574,7 @@ void CmndFlashDump(void) // FlashDump 0xFC000 10 const uint32_t flash_start = 0x40200000; // Start address flash const uint8_t bytes_per_cols = 0x20; - const uint32_t max = (FLASH_EEPROM_START + 5) * SPI_FLASH_SEC_SIZE; // 0x100000 for 1M flash, 0x400000 for 4M flash + const uint32_t max = (EEPROM_LOCATION + 5) * SPI_FLASH_SEC_SIZE; // 0x100000 for 1M flash, 0x400000 for 4M flash uint32_t start = flash_start; uint32_t rows = 8; @@ -594,7 +594,7 @@ void CmndFlashDump(void) for (uint32_t pos = start; pos < end; pos += bytes_per_cols) { uint32_t* values = (uint32_t*)(pos); - AddLog_P(LOG_LEVEL_INFO, PSTR("%06X: %08X %08X %08X %08X %08X %08X %08X %08X"), pos - flash_start, + AddLog(LOG_LEVEL_INFO, PSTR("%06X: %08X %08X %08X %08X %08X %08X %08X %08X"), pos - flash_start, DebugSwap32(values[0]), DebugSwap32(values[1]), DebugSwap32(values[2]), DebugSwap32(values[3]), DebugSwap32(values[4]), DebugSwap32(values[5]), DebugSwap32(values[6]), DebugSwap32(values[7])); } @@ -626,7 +626,7 @@ void CmndI2cWrite(void) Wire.write(buffer[i]); } int result = Wire.endTransmission(); - AddLog_P(LOG_LEVEL_INFO, PSTR("I2C: Result %d"), result); + AddLog(LOG_LEVEL_INFO, PSTR("I2C: Result %d"), result); } } ResponseCmndDone(); diff --git a/tasmota/xdsp_01_lcd.ino b/tasmota/xdsp_01_lcd.ino index 3db1c48e3..8f3a9a779 100644 --- a/tasmota/xdsp_01_lcd.ino +++ b/tasmota/xdsp_01_lcd.ino @@ -147,7 +147,7 @@ bool LcdPrintLog(void) strlcpy(disp_screen_buffer[last_row], txt, disp_screen_buffer_cols); DisplayFillScreen(last_row); - AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "[%s]"), disp_screen_buffer[last_row]); + AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "[%s]"), disp_screen_buffer[last_row]); lcd->setCursor(0, last_row); lcd->print(disp_screen_buffer[last_row]); diff --git a/tasmota/xdsp_02_ssd1306.ino b/tasmota/xdsp_02_ssd1306.ino index d9184b790..a3e9bb8f5 100644 --- a/tasmota/xdsp_02_ssd1306.ino +++ b/tasmota/xdsp_02_ssd1306.ino @@ -124,7 +124,7 @@ void Ssd1306PrintLog(void) strlcpy(disp_screen_buffer[last_row], txt, disp_screen_buffer_cols); DisplayFillScreen(last_row); - AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "[%s]"), disp_screen_buffer[last_row]); + AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "[%s]"), disp_screen_buffer[last_row]); renderer->println(disp_screen_buffer[last_row]); renderer->Updateframe(); diff --git a/tasmota/xdsp_03_matrix.ino b/tasmota/xdsp_03_matrix.ino index e82c0586b..3b0155a8f 100644 --- a/tasmota/xdsp_03_matrix.ino +++ b/tasmota/xdsp_03_matrix.ino @@ -95,7 +95,7 @@ void MatrixScrollLeft(char* txt, int loop) // Horiz. position of text -- starts off right edge mtx_x = 8 * mtx_matrices; - AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "[%s]"), txt); + AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "[%s]"), txt); disp_refresh = Settings.display_refresh; case 2: @@ -265,7 +265,7 @@ void MatrixPrintLog(uint8_t direction) i++; } - AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_APPLICATION "[%s]"), mtx_buffer); + AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_APPLICATION "[%s]"), mtx_buffer); mtx_done = 1; } diff --git a/tasmota/xdsp_04_ili9341.ino b/tasmota/xdsp_04_ili9341.ino index 088f545d5..e6b38ca7d 100644 --- a/tasmota/xdsp_04_ili9341.ino +++ b/tasmota/xdsp_04_ili9341.ino @@ -118,7 +118,7 @@ void Ili9341InitDriver(void) { Ili9341InitMode(); tft_init_done = true; - AddLog_P(LOG_LEVEL_INFO, PSTR("DSP: ILI9341")); + AddLog(LOG_LEVEL_INFO, PSTR("DSP: ILI9341")); } } @@ -195,7 +195,7 @@ void Ili9341PrintLog(void) { DisplayFillScreen(last_row); tft->print(disp_screen_buffer[last_row]); } - AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_APPLICATION "[%s]"), txt); + AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_APPLICATION "[%s]"), txt); } } } diff --git a/tasmota/xdsp_05_epaper_29.ino b/tasmota/xdsp_05_epaper_29.ino index d9c35435f..4c670e00b 100644 --- a/tasmota/xdsp_05_epaper_29.ino +++ b/tasmota/xdsp_05_epaper_29.ino @@ -92,7 +92,7 @@ void EpdInitDriver29(void) { #endif epd_init_done = true; - AddLog_P(LOG_LEVEL_INFO, PSTR("DSP: E-Paper 2.9")); + AddLog(LOG_LEVEL_INFO, PSTR("DSP: E-Paper 2.9")); } } @@ -134,7 +134,7 @@ void EpdPrintLog29(void) renderer->DrawStringAt(0, epd_scroll, disp_screen_buffer[last_row], COLORED, 0); // EpdDisplayFrame(); - AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_APPLICATION "[%s]"), txt); + AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_APPLICATION "[%s]"), txt); } } } diff --git a/tasmota/xdsp_06_epaper_42.ino b/tasmota/xdsp_06_epaper_42.ino index d5783668d..1b9f7b1c5 100644 --- a/tasmota/xdsp_06_epaper_42.ino +++ b/tasmota/xdsp_06_epaper_42.ino @@ -96,7 +96,7 @@ void EpdInitDriver42() { #endif epd42_init_done = true; - AddLog_P(LOG_LEVEL_INFO, PSTR("DSP: E-Paper 4.2")); + AddLog(LOG_LEVEL_INFO, PSTR("DSP: E-Paper 4.2")); } } diff --git a/tasmota/xdsp_07_sh1106.ino b/tasmota/xdsp_07_sh1106.ino index ea09a958c..3ca925932 100644 --- a/tasmota/xdsp_07_sh1106.ino +++ b/tasmota/xdsp_07_sh1106.ino @@ -119,7 +119,7 @@ void SH1106PrintLog(void) strlcpy(disp_screen_buffer[last_row], txt, disp_screen_buffer_cols); DisplayFillScreen(last_row); - AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "[%s]"), disp_screen_buffer[last_row]); + AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "[%s]"), disp_screen_buffer[last_row]); renderer->println(disp_screen_buffer[last_row]); renderer->Updateframe(); diff --git a/tasmota/xdsp_08_ILI9488.ino b/tasmota/xdsp_08_ILI9488.ino index 513150af7..bb423097c 100644 --- a/tasmota/xdsp_08_ILI9488.ino +++ b/tasmota/xdsp_08_ILI9488.ino @@ -95,7 +95,7 @@ void ILI9488_InitDriver(void) { #endif ili9488_init_done = true; - AddLog_P(LOG_LEVEL_INFO, PSTR("DSP: ILI9488")); + AddLog(LOG_LEVEL_INFO, PSTR("DSP: ILI9488")); } } diff --git a/tasmota/xdsp_09_SSD1351.ino b/tasmota/xdsp_09_SSD1351.ino index f3ad53b29..0a1957e3a 100644 --- a/tasmota/xdsp_09_SSD1351.ino +++ b/tasmota/xdsp_09_SSD1351.ino @@ -84,7 +84,7 @@ void SSD1351_InitDriver() { color_type = COLOR_COLOR; ssd1351_init_done = true; - AddLog_P(LOG_LEVEL_INFO, PSTR("DSP: SSD1351")); + AddLog(LOG_LEVEL_INFO, PSTR("DSP: SSD1351")); } } @@ -110,7 +110,7 @@ void SSD1351PrintLog(void) { strlcpy(disp_screen_buffer[last_row], txt, disp_screen_buffer_cols); DisplayFillScreen(last_row); - AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "[%s]"), disp_screen_buffer[last_row]); + AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "[%s]"), disp_screen_buffer[last_row]); renderer->println(disp_screen_buffer[last_row]); renderer->Updateframe(); diff --git a/tasmota/xdsp_10_RA8876.ino b/tasmota/xdsp_10_RA8876.ino index a56f3ebf4..877a088d0 100644 --- a/tasmota/xdsp_10_RA8876.ino +++ b/tasmota/xdsp_10_RA8876.ino @@ -82,7 +82,7 @@ void RA8876_InitDriver(void) { #endif ra8876_init_done = true; - AddLog_P(LOG_LEVEL_INFO, PSTR("DSP: RA8876")); + AddLog(LOG_LEVEL_INFO, PSTR("DSP: RA8876")); } } diff --git a/tasmota/xdsp_12_ST7789.ino b/tasmota/xdsp_12_ST7789.ino index be3a40da9..9b285418f 100644 --- a/tasmota/xdsp_12_ST7789.ino +++ b/tasmota/xdsp_12_ST7789.ino @@ -136,7 +136,7 @@ void ST7789_InitDriver(void) { #endif // ESP32 st7789_init_done = true; - AddLog_P(LOG_LEVEL_INFO, PSTR("DSP: ST7789")); + AddLog(LOG_LEVEL_INFO, PSTR("DSP: ST7789")); } } @@ -190,7 +190,7 @@ bool Xdsp12(uint8_t function) { bool result = false; -//AddLog_P(LOG_LEVEL_INFO, PSTR("touch %d - %d"), FT5206_found, function); +//AddLog(LOG_LEVEL_INFO, PSTR("touch %d - %d"), FT5206_found, function); if (FUNC_DISPLAY_INIT_DRIVER == function) { ST7789_InitDriver(); diff --git a/tasmota/xdsp_13_ILI9341-2.ino b/tasmota/xdsp_13_ILI9341-2.ino index cb0e4616e..785dfc51f 100644 --- a/tasmota/xdsp_13_ILI9341-2.ino +++ b/tasmota/xdsp_13_ILI9341-2.ino @@ -205,7 +205,7 @@ void ILI9341_2_PrintLog(bool withDateTime) { strlcpy(disp_screen_buffer[last_row], txt, disp_screen_buffer_cols); DisplayFillScreen(last_row); - AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "[%s]"), disp_screen_buffer[last_row]); + AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "[%s]"), disp_screen_buffer[last_row]); renderer->println(disp_screen_buffer[last_row]); renderer->Updateframe(); diff --git a/tasmota/xdsp_14_SSD1331.ino b/tasmota/xdsp_14_SSD1331.ino index a7e48eae8..4d297cab8 100644 --- a/tasmota/xdsp_14_SSD1331.ino +++ b/tasmota/xdsp_14_SSD1331.ino @@ -93,7 +93,7 @@ void SSD1331_InitDriver() { color_type = COLOR_COLOR; ssd1331_init_done = true; - AddLog_P(LOG_LEVEL_INFO, PSTR("DSP: SSD1331")); + AddLog(LOG_LEVEL_INFO, PSTR("DSP: SSD1331")); } } @@ -128,7 +128,7 @@ void SSD1331PrintLog(bool withDateTime) { strlcpy(disp_screen_buffer[last_row], txt, disp_screen_buffer_cols); DisplayFillScreen(last_row); - AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "[%s]"), disp_screen_buffer[last_row]); + AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "[%s]"), disp_screen_buffer[last_row]); renderer->println(disp_screen_buffer[last_row]); renderer->Updateframe(); diff --git a/tasmota/xlgt_01_ws2812.ino b/tasmota/xlgt_01_ws2812.ino index e479d3573..fcd685931 100644 --- a/tasmota/xlgt_01_ws2812.ino +++ b/tasmota/xlgt_01_ws2812.ino @@ -171,6 +171,11 @@ struct WS2812 { /********************************************************************************************/ +// For some reason map fails to compile so renamed to wsmap +long wsmap(long x, long in_min, long in_max, long out_min, long out_max) { + return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min; +} + void Ws2812StripShow(void) { #if (USE_WS2812_CTYPE > NEO_3LED) @@ -276,9 +281,9 @@ void Ws2812GradientColor(uint32_t schemenr, struct WsColor* mColor, uint32_t ran end = (scheme.count -1) - end; } float dimmer = 100 / (float)Settings.light_dimmer; - float fmyRed = (float)map(rangeIndex % gradRange, 0, gradRange, scheme.colors[start].red, scheme.colors[end].red) / dimmer; - float fmyGrn = (float)map(rangeIndex % gradRange, 0, gradRange, scheme.colors[start].green, scheme.colors[end].green) / dimmer; - float fmyBlu = (float)map(rangeIndex % gradRange, 0, gradRange, scheme.colors[start].blue, scheme.colors[end].blue) / dimmer; + float fmyRed = (float)wsmap(rangeIndex % gradRange, 0, gradRange, scheme.colors[start].red, scheme.colors[end].red) / dimmer; + float fmyGrn = (float)wsmap(rangeIndex % gradRange, 0, gradRange, scheme.colors[start].green, scheme.colors[end].green) / dimmer; + float fmyBlu = (float)wsmap(rangeIndex % gradRange, 0, gradRange, scheme.colors[start].blue, scheme.colors[end].blue) / dimmer; mColor->red = (uint8_t)fmyRed; mColor->green = (uint8_t)fmyGrn; mColor->blue = (uint8_t)fmyBlu; @@ -316,9 +321,9 @@ void Ws2812Gradient(uint32_t schemenr) Ws2812GradientColor(schemenr, ¤tColor, range, gradRange, i + offset + 1); } // Blend old and current color based on time for smooth movement. - c.R = map(Light.strip_timer_counter % speed, 0, speed, oldColor.red, currentColor.red); - c.G = map(Light.strip_timer_counter % speed, 0, speed, oldColor.green, currentColor.green); - c.B = map(Light.strip_timer_counter % speed, 0, speed, oldColor.blue, currentColor.blue); + c.R = wsmap(Light.strip_timer_counter % speed, 0, speed, oldColor.red, currentColor.red); + c.G = wsmap(Light.strip_timer_counter % speed, 0, speed, oldColor.green, currentColor.green); + c.B = wsmap(Light.strip_timer_counter % speed, 0, speed, oldColor.blue, currentColor.blue); strip->SetPixelColor(i, c); oldColor = currentColor; } diff --git a/tasmota/xlgt_02_my92x1.ino b/tasmota/xlgt_02_my92x1.ino index 412cfd771..fe8ca1270 100644 --- a/tasmota/xlgt_02_my92x1.ino +++ b/tasmota/xlgt_02_my92x1.ino @@ -138,7 +138,7 @@ void My92x1ModuleSelected(void) LightMy92x1Init(); TasmotaGlobal.light_driver = XLGT_02; - AddLog_P(LOG_LEVEL_DEBUG, PSTR("DBG: MY29x1 Found")); + AddLog(LOG_LEVEL_DEBUG, PSTR("DBG: MY29x1 Found")); } } diff --git a/tasmota/xlgt_03_sm16716.ino b/tasmota/xlgt_03_sm16716.ino index 129f64e1a..66c714075 100644 --- a/tasmota/xlgt_03_sm16716.ino +++ b/tasmota/xlgt_03_sm16716.ino @@ -123,7 +123,7 @@ bool Sm16716SetChannels(void) // handle any PWM pins, skipping the first 3 values for sm16716 for (uint32_t i = 3; i < Light.subtype; i++) { if (PinUsed(GPIO_PWM1, i-3)) { - //AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_APPLICATION "Cur_Col%d 10 bits %d, Pwm%d %d"), i, cur_col[i], i+1, curcol); + //AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_APPLICATION "Cur_Col%d 10 bits %d, Pwm%d %d"), i, cur_col[i], i+1, curcol); analogWrite(Pin(GPIO_PWM1, i-3), bitRead(TasmotaGlobal.pwm_inverted, i-3) ? Settings.pwm_range - cur_col_10bits[i] : cur_col_10bits[i]); } } @@ -177,7 +177,7 @@ void Sm16716ModuleSelected(void) LightPwmOffset(LST_RGB); // Handle any PWM pins, skipping the first 3 color values for sm16716 TasmotaGlobal.light_type += LST_RGB; // Add RGB to be controlled by sm16716 TasmotaGlobal.light_driver = XLGT_03; - AddLog_P(LOG_LEVEL_DEBUG, PSTR("DBG: SM16716 Found")); + AddLog(LOG_LEVEL_DEBUG, PSTR("DBG: SM16716 Found")); } } diff --git a/tasmota/xlgt_04_sm2135.ino b/tasmota/xlgt_04_sm2135.ino index c5cf52195..a43336265 100644 --- a/tasmota/xlgt_04_sm2135.ino +++ b/tasmota/xlgt_04_sm2135.ino @@ -58,7 +58,7 @@ #define SM2135_55MA 0x09 #define SM2135_60MA 0x0A -enum Sm2135Color { SM2135_WCGRB, SM2135_WCBGR, SM2135_WCGRBHI, SM2135_WCBGRHI }; +enum Sm2135Color { SM2135_WCGRB, SM2135_WCBGR, SM2135_WCGRBHI, SM2135_WCBGRHI, SM2135_WCGRB15W, SM2135_WCBGR15W }; struct SM2135 { uint8_t clk = 0; @@ -139,7 +139,7 @@ bool Sm2135SetChannels(void) { uint8_t data[6]; uint32_t light_type = 3; // RGB and CW - if (Sm2135.model < 2) { + if (Sm2135.model < 2) { // Only allow one of two options due to power supply if ((0 == cur_col[0]) && (0 == cur_col[1]) && (0 == cur_col[2])) { light_type = 1; // CW only } else { @@ -182,25 +182,35 @@ void Sm2135ModuleSelected(void) Sm2135.clk = Pin(GPIO_SM2135_CLK); Sm2135.data = Pin(GPIO_SM2135_DAT, GPIO_ANY); - Sm2135.model = GetPin(Sm2135.data) - AGPIO(GPIO_SM2135_DAT); // 0 .. 3 + // See #define MAX_SM2135_DAT 6 in tasmota_template.h + Sm2135.model = GetPin(Sm2135.data) - AGPIO(GPIO_SM2135_DAT); // 0 .. 5 + + // Legacy support of model selection if (PinUsed(GPIO_SWT1)) { Sm2135.model = SM2135_WCBGR; pinMode(Pin(GPIO_SWT1), INPUT); // Discard GPIO_SWT functionality SetPin(Pin(GPIO_SWT1), AGPIO(GPIO_NONE)); } -// RGB current CW current + // SM2135 Dat 1/2 + // RGB current CW current Sm2135.current = (SM2135_20MA << 4) | SM2135_15MA; // See https://github.com/arendst/Tasmota/issues/6495#issuecomment-549121683 - if (Sm2135.model > SM2135_WCBGR) { - Sm2135.current = (SM2135_20MA << 4) | SM2135_30MA; + switch (Sm2135.model) { + case SM2135_WCGRBHI: // SM2135 Dat 3 + case SM2135_WCBGRHI: // SM2135 Dat 4 + Sm2135.current = (SM2135_20MA << 4) | SM2135_30MA; + break; + case SM2135_WCGRB15W: // SM2135 Dat 5 + case SM2135_WCBGR15W: // SM2135 Dat 6 + Sm2135.current = (SM2135_45MA << 4) | SM2135_60MA; + break; } Sm2135Init(); TasmotaGlobal.light_type = LT_RGBWC; TasmotaGlobal.light_driver = XLGT_04; - AddLog_P(LOG_LEVEL_DEBUG, PSTR("LGT: SM2135 (%s-%s current) Found"), - (SM2135_WCBGR == (Sm2135.model &1)) ? PSTR("BGR") : PSTR("GRB"), (Sm2135.model > SM2135_WCBGR) ? PSTR("High") : PSTR("Low")); + AddLog(LOG_LEVEL_DEBUG, PSTR("LGT: SM2135 %s Found"), (SM2135_WCBGR == (Sm2135.model &1)) ? PSTR("BGR") : PSTR("GRB")); } } diff --git a/tasmota/xlgt_05_sonoff_l1.ino b/tasmota/xlgt_05_sonoff_l1.ino index 227d8f1c9..23ed21e8d 100644 --- a/tasmota/xlgt_05_sonoff_l1.ino +++ b/tasmota/xlgt_05_sonoff_l1.ino @@ -25,11 +25,11 @@ #define XLGT_05 5 -//#define SONOFF_L1_START_DELAY // Sync Nuvotron power state with Tasmota on power up +#define SONOFF_L1_START_DELAY // Sync Nuvotron power state with Tasmota on power up //#define SONOFF_L1_ALLOW_REMOTE_INTERRUPT // During schemes 2..4 #define SONOFF_L1_DEBUG1 // Add send and receive logging -#define SONOFF_L1_BUFFER_SIZE 140 +#define SONOFF_L1_BUFFER_SIZE 170 #define SONOFF_L1_MODE_COLORFUL 1 // [Color key] Colorful (static color) #define SONOFF_L1_MODE_COLORFUL_GRADIENT 2 // [SMOOTH] Colorful Gradient @@ -45,18 +45,26 @@ #define SONOFF_L1_MODE_SYNC_TO_MUSIC 12 // Sync to music [Speed 1- 100, sensitivity 1 - 10] struct SNFL1 { + char *buffer; #ifdef SONOFF_L1_ALLOW_REMOTE_INTERRUPT uint32_t unlock = 0; bool receive_ready = true; -#endif -#ifdef SONOFF_L1_START_DELAY - char buffer[SONOFF_L1_BUFFER_SIZE]; #endif uint8_t color[3]; uint8_t dimmer; uint8_t power; + uint8_t old_music_sync = 0; + uint8_t music_sync = 0; + uint8_t sensitive; + uint8_t speed; } Snfl1; +const char kL1Commands[] PROGMEM = "L1|" // Prefix + "MusicSync"; + +void (* const L1Command[])(void) PROGMEM = { + &CmndMusicSync }; + /********************************************************************************************/ #ifdef SONOFF_L1_START_DELAY @@ -66,9 +74,9 @@ Ticker SnfL1StartDelay; void SnfL1SendDelayed(void) { SnfL1Send(); } +#endif // SONOFF_L1_START_DELAY -void SnfL1Send(void) -{ +void SnfL1Send(void) { #ifdef SONOFF_L1_DEBUG1 AddLog_P(LOG_LEVEL_DEBUG, PSTR("SL1: Send %s"), Snfl1.buffer); #endif @@ -77,34 +85,13 @@ void SnfL1Send(void) Serial.flush(); } -void SnfL1SerialSendOk(void) -{ - snprintf_P(Snfl1.buffer, sizeof(Snfl1.buffer), PSTR("AT+SEND=ok")); +void SnfL1SerialSendOk(void) { + snprintf_P(Snfl1.buffer, SONOFF_L1_BUFFER_SIZE, PSTR("AT+SEND=ok")); SnfL1Send(); } -#else -void SnfL1Send(const char *buffer) -{ -#ifdef SONOFF_L1_DEBUG1 - AddLog_P(LOG_LEVEL_DEBUG, PSTR("SL1: Send %s"), buffer); -#endif - Serial.print(buffer); - Serial.write(0x1B); - Serial.flush(); -} -void SnfL1SerialSendOk(void) -{ - char buffer[16]; - snprintf_P(buffer, sizeof(buffer), PSTR("AT+SEND=ok")); - - SnfL1Send(buffer); -} -#endif // SONOFF_L1_START_DELAY - -bool SnfL1SerialInput(void) -{ +bool SnfL1SerialInput(void) { if (TasmotaGlobal.serial_in_byte != 0x1B) { if (TasmotaGlobal.serial_in_byte_counter >= SONOFF_L1_BUFFER_SIZE) { TasmotaGlobal.serial_in_byte_counter = 0; @@ -147,7 +134,7 @@ bool SnfL1SerialInput(void) if (!strncmp(token2, "\"sequence\"", 10)) { -// AddLog_P(LOG_LEVEL_DEBUG, PSTR("SL1: Rcvd sequence %s"), token3); +// AddLog(LOG_LEVEL_DEBUG, PSTR("SL1: Rcvd sequence %s"), token3); token = nullptr; } @@ -155,7 +142,7 @@ bool SnfL1SerialInput(void) else if (!strncmp(token2, "\"switch\"", 8)) { switch_state = !strncmp(token3, "\"on\"", 4) ? true : false; -// AddLog_P(LOG_LEVEL_DEBUG, PSTR("SL1: Rcvd switch %d (%d)"), switch_state, Light.power); +// AddLog(LOG_LEVEL_DEBUG, PSTR("SL1: Rcvd switch %d (%d)"), switch_state, Light.power); is_power_change = (switch_state != Light.power); } @@ -178,7 +165,7 @@ bool SnfL1SerialInput(void) bool all_color_channels_updated = color_updated[0] && color_updated[1] && color_updated[2]; if (all_color_channels_updated) { -// AddLog_P(LOG_LEVEL_DEBUG, PSTR("SL1: Rcvd color R%d G%d B%d (R%d G%d B%d)"), +// AddLog(LOG_LEVEL_DEBUG, PSTR("SL1: Rcvd color R%d G%d B%d (R%d G%d B%d)"), // Snfl1.color[0], Snfl1.color[1], Snfl1.color[2], // Settings.light_color[0], Settings.light_color[1], Settings.light_color[2]); @@ -190,7 +177,7 @@ bool SnfL1SerialInput(void) else if (!strncmp(token2, "\"bright\"", 8)) { uint8_t dimmer = atoi(token3); -// AddLog_P(LOG_LEVEL_DEBUG, PSTR("SL1: Rcvd dimmer %d (%d)"), dimmer, Settings.light_dimmer); +// AddLog(LOG_LEVEL_DEBUG, PSTR("SL1: Rcvd dimmer %d (%d)"), dimmer, Settings.light_dimmer); is_brightness_change = (Light.power && (dimmer > 0) && (dimmer != Settings.light_dimmer)); snprintf_P(cmnd_dimmer, sizeof(cmnd_dimmer), PSTR(D_CMND_DIMMER " %d"), dimmer); @@ -240,8 +227,7 @@ bool SnfL1SerialInput(void) /********************************************************************************************/ -bool SnfL1SetChannels(void) -{ +bool SnfL1SetChannels(void) { #ifdef SONOFF_L1_ALLOW_REMOTE_INTERRUPT if (Snfl1.receive_ready || TimeReached(Snfl1.unlock)) { #endif @@ -263,34 +249,30 @@ bool SnfL1SetChannels(void) Snfl1.color[i] = scale_col[i]; } } - if (!power_changed && !dimmer_changed && !color_changed) { return true; } + if (!power_changed && !dimmer_changed && !color_changed && (Snfl1.old_music_sync == Snfl1.music_sync)) { return true; } -#ifdef SONOFF_L1_START_DELAY - snprintf_P(Snfl1.buffer, sizeof(Snfl1.buffer), PSTR("AT+UPDATE=\"sequence\":\"%d%03d\",\"switch\":\"%s\",\"light_type\":1,\"colorR\":%d,\"colorG\":%d,\"colorB\":%d,\"bright\":%d,\"mode\":%d"), + uint32_t mode = SONOFF_L1_MODE_COLORFUL; + if (Snfl1.music_sync) { + mode = SONOFF_L1_MODE_SYNC_TO_MUSIC; + } + + snprintf_P(Snfl1.buffer, SONOFF_L1_BUFFER_SIZE, PSTR("AT+UPDATE=\"sequence\":\"%d%03d\",\"switch\":\"%s\",\"light_type\":1,\"colorR\":%d,\"colorG\":%d,\"colorB\":%d,\"bright\":%d,\"mode\":%d,\"sensitive\":%d,\"speed\":%d"), LocalTime(), millis()%1000, Snfl1.power ? "on" : "off", Snfl1.color[0], Snfl1.color[1], Snfl1.color[2], Snfl1.dimmer, - SONOFF_L1_MODE_COLORFUL); + mode, + Snfl1.sensitive, + Snfl1.speed); +#ifdef SONOFF_L1_START_DELAY static bool first_call = true; if (first_call) { SnfL1StartDelay.once_ms(900, SnfL1SendDelayed); // Allow startup time for Nuvotron microcontroller first_call = false; - } else { - SnfL1Send(); - } -#else - char buffer[SONOFF_L1_BUFFER_SIZE]; - snprintf_P(buffer, sizeof(buffer), PSTR("AT+UPDATE=\"sequence\":\"%d%03d\",\"switch\":\"%s\",\"light_type\":1,\"colorR\":%d,\"colorG\":%d,\"colorB\":%d,\"bright\":%d,\"mode\":%d"), - LocalTime(), millis()%1000, - Snfl1.power ? "on" : "off", - Snfl1.color[0], Snfl1.color[1], Snfl1.color[2], - Snfl1.dimmer, - SONOFF_L1_MODE_COLORFUL); - - SnfL1Send(buffer); + } else #endif // SONOFF_L1_START_DELAY + SnfL1Send(); #ifdef SONOFF_L1_ALLOW_REMOTE_INTERRUPT Snfl1.unlock = millis() + 500; // Allow time for the RC @@ -300,24 +282,53 @@ bool SnfL1SetChannels(void) return true; } -bool SnfL1ModuleSelected(void) -{ +bool SnfL1ModuleSelected(void) { if (SONOFF_L1 == TasmotaGlobal.module_type) { if (PinUsed(GPIO_RXD) && PinUsed(GPIO_TXD)) { - SetSerial(19200, TS_SERIAL_8N1); + Snfl1.buffer = (char*)malloc(SONOFF_L1_BUFFER_SIZE); + if (Snfl1.buffer) { + SetSerial(19200, TS_SERIAL_8N1); - Snfl1.power = !Light.power; - Snfl1.dimmer = !light_state.getDimmer(); + Snfl1.power = !Light.power; + Snfl1.dimmer = !light_state.getDimmer(); + Snfl1.music_sync = 0; + Snfl1.sensitive = 5; // 1..10 + Snfl1.speed = 50; // 1..100 - TasmotaGlobal.light_type = LT_RGB; - TasmotaGlobal.light_driver = XLGT_05; - AddLog_P(LOG_LEVEL_DEBUG, PSTR("LGT: Sonoff L1 Found")); - return true; + TasmotaGlobal.light_type = LT_RGB; + TasmotaGlobal.light_driver = XLGT_05; + AddLog(LOG_LEVEL_DEBUG, PSTR("LGT: Sonoff L1 Found")); + return true; + } } } return false; } +void CmndMusicSync(void) { + // Format is L1MusicSync on/off/toggle, sensitivity, speed + // sensitivity 1..10, speed 1..100 + if (XdrvMailbox.data_len > 0) { + Snfl1.old_music_sync = Snfl1.music_sync; + uint32_t parm[3] = { 0 }; + ParseParameters(3, parm); + if (2 == parm[0]) { + Snfl1.music_sync ^= 1; // Toggle + } else { + Snfl1.music_sync = parm[0] & 1; // On or Off + } + if ((parm[1] > 0) && (parm[1] < 11)) { + Snfl1.sensitive = parm[1]; // 1..10 + } + if ((parm[2] > 0) && (parm[2] < 101)) { + Snfl1.speed = parm[2]; // 1..100 + } + SnfL1SetChannels(); + } + Response_P(PSTR("{\"%s\":{\"Mode\":\"%s\",\"Sensitive\":%d,\"Speed\":%d}}"), + XdrvMailbox.command, GetStateText(Snfl1.music_sync), Snfl1.sensitive, Snfl1.speed); +} + /*********************************************************************************************\ * Interface \*********************************************************************************************/ @@ -336,6 +347,9 @@ bool Xlgt05(uint8_t function) case FUNC_MODULE_INIT: result = SnfL1ModuleSelected(); break; + case FUNC_COMMAND: + result = DecodeCommand(kL1Commands, L1Command); + break; } return result; } diff --git a/tasmota/xlgt_06_electriq_moodl.ino b/tasmota/xlgt_06_electriq_moodl.ino index 3cb07a7e6..f6bef1ff5 100644 --- a/tasmota/xlgt_06_electriq_moodl.ino +++ b/tasmota/xlgt_06_electriq_moodl.ino @@ -74,7 +74,7 @@ void ElectriqMoodLModuleSelected(void) SetSerial(9600, TS_SERIAL_8N1); TasmotaGlobal.light_type = LT_RGBW; TasmotaGlobal.light_driver = XLGT_06; - AddLog_P(LOG_LEVEL_DEBUG, PSTR("LGT: ElectriQ Mood Lamp Found")); + AddLog(LOG_LEVEL_DEBUG, PSTR("LGT: ElectriQ Mood Lamp Found")); } } diff --git a/tasmota/xnrg_01_hlw8012.ino b/tasmota/xnrg_01_hlw8012.ino index a174699a0..ff6b40bf5 100644 --- a/tasmota/xnrg_01_hlw8012.ino +++ b/tasmota/xnrg_01_hlw8012.ino @@ -164,7 +164,7 @@ void HlwEvery200ms(void) } } uint32_t median = Hlw.debug[(Hlw.cf1_pulse_counter +1) / 2]; - AddLog_P(LOG_LEVEL_DEBUG, PSTR("NRG: power %d, ui %d, cnt %d, smpl%s, sum %d, mean %d, median %d"), + AddLog(LOG_LEVEL_DEBUG, PSTR("NRG: power %d, ui %d, cnt %d, smpl%s, sum %d, mean %d, median %d"), Hlw.cf_power_pulse_length , Hlw.select_ui_flag, Hlw.cf1_pulse_counter, stemp, Hlw.cf1_summed_pulse_length, cf1_pulse_length, median); #endif diff --git a/tasmota/xnrg_02_cse7766.ino b/tasmota/xnrg_02_cse7766.ino index e70b0addb..24c7fcc09 100644 --- a/tasmota/xnrg_02_cse7766.ino +++ b/tasmota/xnrg_02_cse7766.ino @@ -68,7 +68,7 @@ void CseReceived(void) { uint8_t header = Cse.rx_buffer[0]; if ((header & 0xFC) == 0xFC) { - AddLog_P(LOG_LEVEL_DEBUG, PSTR("CSE: Abnormal hardware")); + AddLog(LOG_LEVEL_DEBUG, PSTR("CSE: Abnormal hardware")); return; } @@ -165,7 +165,7 @@ void CseSerialInput(void) { Cse.byte_counter--; } while ((Cse.byte_counter > 2) && (0x5A != Cse.rx_buffer[1])); if (0x5A != Cse.rx_buffer[1]) { - AddLog_P(LOG_LEVEL_DEBUG, PSTR("CSE: " D_CHECKSUM_FAILURE)); + AddLog(LOG_LEVEL_DEBUG, PSTR("CSE: " D_CHECKSUM_FAILURE)); Cse.received = false; Cse.byte_counter = 0; } @@ -208,7 +208,7 @@ void CseEverySecond(void) { Energy.kWhtoday_delta += delta; } else { - AddLog_P(LOG_LEVEL_DEBUG, PSTR("CSE: Overload")); + AddLog(LOG_LEVEL_DEBUG, PSTR("CSE: Overload")); Cse.cf_pulses_last_time = CSE_PULSES_NOT_INITIALIZED; } EnergyUpdateToday(); diff --git a/tasmota/xnrg_03_pzem004t.ino b/tasmota/xnrg_03_pzem004t.ino index 39a382a41..692621cfc 100644 --- a/tasmota/xnrg_03_pzem004t.ino +++ b/tasmota/xnrg_03_pzem004t.ino @@ -138,15 +138,15 @@ bool PzemRecieve(uint8_t resp, float *data) AddLogBuffer(LOG_LEVEL_DEBUG_MORE, buffer, len); if (len != sizeof(PZEMCommand)) { -// AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "Pzem comms timeout")); +// AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "Pzem comms timeout")); return false; } if (buffer[6] != PzemCrc(buffer)) { -// AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "Pzem crc error")); +// AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "Pzem crc error")); return false; } if (buffer[0] != resp) { -// AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "Pzem bad response")); +// AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "Pzem bad response")); return false; } @@ -208,7 +208,7 @@ void PzemEvery250ms(void) Pzem.read_state = 1; } -// AddLog_P(LOG_LEVEL_DEBUG, PSTR("PZM: Retry %d"), 5 - Pzem.send_retry); +// AddLog(LOG_LEVEL_DEBUG, PSTR("PZM: Retry %d"), 5 - Pzem.send_retry); } } @@ -220,7 +220,7 @@ void PzemEvery250ms(void) Pzem.phase--; } -// AddLog_P(LOG_LEVEL_DEBUG, PSTR("PZM: Probing address %d, Max phases %d"), Pzem.phase +1, Energy.phase_count); +// AddLog(LOG_LEVEL_DEBUG, PSTR("PZM: Probing address %d, Max phases %d"), Pzem.phase +1, Energy.phase_count); } if (Pzem.address) { diff --git a/tasmota/xnrg_04_mcp39f501.ino b/tasmota/xnrg_04_mcp39f501.ino index bfe7c48fd..ce6f2e455 100644 --- a/tasmota/xnrg_04_mcp39f501.ino +++ b/tasmota/xnrg_04_mcp39f501.ino @@ -484,15 +484,15 @@ void McpSerialInput(void) AddLogBuffer(LOG_LEVEL_DEBUG_MORE, (uint8_t*)mcp_buffer, mcp_byte_counter); if (MCP_BUFFER_SIZE == mcp_byte_counter) { -// AddLog_P(LOG_LEVEL_DEBUG, PSTR("MCP: Overflow")); +// AddLog(LOG_LEVEL_DEBUG, PSTR("MCP: Overflow")); } else if (1 == mcp_byte_counter) { if (MCP_ERROR_CRC == mcp_buffer[0]) { -// AddLog_P(LOG_LEVEL_DEBUG, PSTR("MCP: Send " D_CHECKSUM_FAILURE)); +// AddLog(LOG_LEVEL_DEBUG, PSTR("MCP: Send " D_CHECKSUM_FAILURE)); mcp_timeout = 0; } else if (MCP_ERROR_NAK == mcp_buffer[0]) { -// AddLog_P(LOG_LEVEL_DEBUG, PSTR("MCP: NAck")); +// AddLog(LOG_LEVEL_DEBUG, PSTR("MCP: NAck")); mcp_timeout = 0; } } @@ -500,7 +500,7 @@ void McpSerialInput(void) if (mcp_byte_counter == mcp_buffer[1]) { if (McpChecksum((uint8_t *)mcp_buffer) != mcp_buffer[mcp_byte_counter -1]) { - AddLog_P(LOG_LEVEL_DEBUG, PSTR("MCP: " D_CHECKSUM_FAILURE)); + AddLog(LOG_LEVEL_DEBUG, PSTR("MCP: " D_CHECKSUM_FAILURE)); } else { if (5 == mcp_buffer[1]) { McpAddressReceive(); } if (25 == mcp_buffer[1]) { McpParseData(); } diff --git a/tasmota/xnrg_05_pzem_ac.ino b/tasmota/xnrg_05_pzem_ac.ino index 2c79ae2f6..2c386d104 100644 --- a/tasmota/xnrg_05_pzem_ac.ino +++ b/tasmota/xnrg_05_pzem_ac.ino @@ -63,7 +63,7 @@ void PzemAcEverySecond(void) AddLogBuffer(LOG_LEVEL_DEBUG_MORE, buffer, PzemAcModbus->ReceiveCount()); if (error) { - AddLog_P(LOG_LEVEL_DEBUG, PSTR("PAC: PzemAc %d error %d"), PZEM_AC_DEVICE_ADDRESS + PzemAc.phase, error); + AddLog(LOG_LEVEL_DEBUG, PSTR("PAC: PzemAc %d error %d"), PZEM_AC_DEVICE_ADDRESS + PzemAc.phase, error); } else { Energy.data_valid[PzemAc.phase] = 0; if (10 == registers) { diff --git a/tasmota/xnrg_06_pzem_dc.ino b/tasmota/xnrg_06_pzem_dc.ino index 8e5b13c54..5d9a11c69 100644 --- a/tasmota/xnrg_06_pzem_dc.ino +++ b/tasmota/xnrg_06_pzem_dc.ino @@ -62,7 +62,7 @@ void PzemDcEverySecond(void) AddLogBuffer(LOG_LEVEL_DEBUG_MORE, buffer, PzemDcModbus->ReceiveCount()); if (error) { - AddLog_P(LOG_LEVEL_DEBUG, PSTR("PDC: PzemDc %d error %d"), PZEM_DC_DEVICE_ADDRESS + PzemDc.channel, error); + AddLog(LOG_LEVEL_DEBUG, PSTR("PDC: PzemDc %d error %d"), PZEM_DC_DEVICE_ADDRESS + PzemDc.channel, error); } else { Energy.data_valid[PzemDc.channel] = 0; if (8 == registers) { diff --git a/tasmota/xnrg_07_ade7953.ino b/tasmota/xnrg_07_ade7953.ino index a9c8acc5f..55e2446a8 100644 --- a/tasmota/xnrg_07_ade7953.ino +++ b/tasmota/xnrg_07_ade7953.ino @@ -131,7 +131,7 @@ void Ade7953GetData(void) reg[i >> 2][i &3] = value; } } - AddLog_P(LOG_LEVEL_DEBUG_MORE, PSTR("ADE: %d, %d, [%d, %d, %d, %d], [%d, %d, %d, %d]"), + AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("ADE: %d, %d, [%d, %d, %d, %d], [%d, %d, %d, %d]"), Ade7953.voltage_rms, Ade7953.period, reg[0][0], reg[0][1], reg[0][2], reg[0][3], reg[1][0], reg[1][1], reg[1][2], reg[1][3]); @@ -154,7 +154,7 @@ void Ade7953GetData(void) uint32_t current_rms_sum = Ade7953.current_rms[0] + Ade7953.current_rms[1]; uint32_t active_power_sum = Ade7953.active_power[0] + Ade7953.active_power[1]; - AddLog_P(LOG_LEVEL_DEBUG, PSTR("ADE: U %d, C %d, I %d + %d = %d, P %d + %d = %d"), + AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("ADE: U %d, C %d, I %d + %d = %d, P %d + %d = %d"), Ade7953.voltage_rms, Ade7953.period, Ade7953.current_rms[0], Ade7953.current_rms[1], current_rms_sum, Ade7953.active_power[0], Ade7953.active_power[1], active_power_sum); @@ -289,4 +289,4 @@ bool Xnrg07(uint8_t function) #endif // USE_ADE7953 #endif // USE_ENERGY_SENSOR -#endif // USE_I2C \ No newline at end of file +#endif // USE_I2C diff --git a/tasmota/xnrg_08_sdm120.ino b/tasmota/xnrg_08_sdm120.ino index 5ba6242f6..c532deafc 100644 --- a/tasmota/xnrg_08_sdm120.ino +++ b/tasmota/xnrg_08_sdm120.ino @@ -83,7 +83,7 @@ void SDM120Every250ms(void) AddLogBuffer(LOG_LEVEL_DEBUG_MORE, buffer, Sdm120Modbus->ReceiveCount()); if (error) { - AddLog_P(LOG_LEVEL_DEBUG, PSTR("SDM: SDM120 error %d"), error); + AddLog(LOG_LEVEL_DEBUG, PSTR("SDM: SDM120 error %d"), error); } else { Energy.data_valid[0] = 0; diff --git a/tasmota/xnrg_09_dds2382.ino b/tasmota/xnrg_09_dds2382.ino index 25ae5cf35..02a1d7673 100644 --- a/tasmota/xnrg_09_dds2382.ino +++ b/tasmota/xnrg_09_dds2382.ino @@ -50,7 +50,7 @@ void Dds2382EverySecond(void) AddLogBuffer(LOG_LEVEL_DEBUG_MORE, buffer, Dds2382Modbus->ReceiveCount()); if (error) { - AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "DDS2382 response error %d"), error); + AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "DDS2382 response error %d"), error); } else { Energy.data_valid[0] = 0; diff --git a/tasmota/xnrg_10_sdm630.ino b/tasmota/xnrg_10_sdm630.ino index 7dcf478e4..46c14ffc4 100644 --- a/tasmota/xnrg_10_sdm630.ino +++ b/tasmota/xnrg_10_sdm630.ino @@ -86,7 +86,7 @@ void SDM630Every250ms(void) AddLogBuffer(LOG_LEVEL_DEBUG_MORE, buffer, Sdm630Modbus->ReceiveCount()); if (error) { - AddLog_P(LOG_LEVEL_DEBUG, PSTR("SDM: SDM630 error %d"), error); + AddLog(LOG_LEVEL_DEBUG, PSTR("SDM: SDM630 error %d"), error); } else { Energy.data_valid[0] = 0; Energy.data_valid[1] = 0; diff --git a/tasmota/xnrg_11_ddsu666.ino b/tasmota/xnrg_11_ddsu666.ino index dc52cff5a..6b2d8dfb9 100644 --- a/tasmota/xnrg_11_ddsu666.ino +++ b/tasmota/xnrg_11_ddsu666.ino @@ -67,7 +67,7 @@ void DDSU666Every250ms(void) AddLogBuffer(LOG_LEVEL_DEBUG_MORE, buffer, Ddsu666Modbus->ReceiveCount()); if (error) { - AddLog_P(LOG_LEVEL_DEBUG, PSTR("SDM: Ddsu666 error %d"), error); + AddLog(LOG_LEVEL_DEBUG, PSTR("SDM: Ddsu666 error %d"), error); } else { Energy.data_valid[0] = 0; diff --git a/tasmota/xnrg_12_solaxX1.ino b/tasmota/xnrg_12_solaxX1.ino index 04d873deb..76b699b03 100644 --- a/tasmota/xnrg_12_solaxX1.ino +++ b/tasmota/xnrg_12_solaxX1.ino @@ -1,529 +1,504 @@ -/* - xnrg_12_solaxX1.ino - Solax X1 inverter RS485 support for Tasmota - - Copyright (C) 2021 Pablo Zerón - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . -*/ - -#ifdef USE_ENERGY_SENSOR -#ifdef USE_SOLAX_X1 -/*********************************************************************************************\ - * Solax X1 Inverter -\*********************************************************************************************/ - -#define XNRG_12 12 - -#ifndef SOLAXX1_SPEED -#define SOLAXX1_SPEED 9600 // default solax rs485 speed -#endif - -#define INVERTER_ADDRESS 0x0A - -#define D_SOLAX_X1 "SolaxX1" - -#include - -enum solaxX1_Error -{ - solaxX1_ERR_NO_ERROR, - solaxX1_ERR_CRC_ERROR -}; - -union { - uint32_t ErrMessage; - struct { - //BYTE0 - uint8_t TzProtectFault:1;//0 - uint8_t MainsLostFault:1;//1 - uint8_t GridVoltFault:1;//2 - uint8_t GridFreqFault:1;//3 - uint8_t PLLLostFault:1;//4 - uint8_t BusVoltFault:1;//5 - uint8_t ErrBit06:1;//6 - uint8_t OciFault:1;//7 - //BYTE1 - uint8_t Dci_OCP_Fault:1;//8 - uint8_t ResidualCurrentFault:1;//9 - uint8_t PvVoltFault:1;//10 - uint8_t Ac10Mins_Voltage_Fault:1;//11 - uint8_t IsolationFault:1;//12 - uint8_t TemperatureOverFault:1;//13 - uint8_t FanFault:1;//14 - uint8_t ErrBit15:1;//15 - //BYTE2 - uint8_t SpiCommsFault:1;//16 - uint8_t SciCommsFault:1;//17 - uint8_t ErrBit18:1;//18 - uint8_t InputConfigFault:1;//19 - uint8_t EepromFault:1;//20 - uint8_t RelayFault:1;//21 - uint8_t SampleConsistenceFault:1;//22 - uint8_t ResidualCurrent_DeviceFault:1;//23 - //BYTE3 - uint8_t ErrBit24:1;//24 - uint8_t ErrBit25:1;//25 - uint8_t ErrBit26:1;//26 - uint8_t ErrBit27:1;//27 - uint8_t ErrBit28:1;//28 - uint8_t DCI_DeviceFault:1;//29 - uint8_t OtherDeviceFault:1;//30 - uint8_t ErrBit31:1;//31 - }; -} ErrCode; - -const char kSolaxMode[] PROGMEM = D_WAITING "|" D_CHECKING "|" D_WORKING "|" D_FAILURE; - -const char kSolaxError[] PROGMEM = - D_SOLAX_ERROR_0 "|" D_SOLAX_ERROR_1 "|" D_SOLAX_ERROR_2 "|" D_SOLAX_ERROR_3 "|" D_SOLAX_ERROR_4 "|" D_SOLAX_ERROR_5 "|" - D_SOLAX_ERROR_6 "|" D_SOLAX_ERROR_7 "|" D_SOLAX_ERROR_8; - -/*********************************************************************************************/ - -TasmotaSerial *solaxX1Serial; - -uint8_t solaxX1_Init = 1; - -struct SOLAXX1 { - float temperature = 0; - float energy_today = 0; - float dc1_voltage = 0; - float dc2_voltage = 0; - float dc1_current = 0; - float dc2_current = 0; - float energy_total = 0; - float runtime_total = 0; - float dc1_power = 0; - float dc2_power = 0; - - uint8_t status = 0; - uint32_t errorCode = 0; -} solaxX1; - -union { - uint8_t status; - struct { - uint8_t freeBit7:1; // Bit7 - uint8_t freeBit6:1; // Bit6 - uint8_t freeBit5:1; // Bit5 - uint8_t queryOffline:1; // Bit4 - uint8_t queryOfflineSend:1; // Bit3 - uint8_t hasAddress:1; // Bit2 - uint8_t inverterAddressSend:1; // Bit1 - uint8_t inverterSnReceived:1; // Bit0 - }; -} protocolStatus; - -uint8_t header[2] = {0xAA, 0x55}; -uint8_t source[2] = {0x00, 0x00}; -uint8_t destination[2] = {0x00, 0x00}; -uint8_t controlCode[1] = {0x00}; -uint8_t functionCode[1] = {0x00}; -uint8_t dataLength[1] = {0x00}; -uint8_t data[16] = {0}; - -uint8_t message[30]; - -/*********************************************************************************************/ - -bool solaxX1_RS485ReceiveReady(void) -{ - return (solaxX1Serial->available() > 1); -} - -void solaxX1_RS485Send(uint16_t msgLen) -{ - memcpy(message, header, 2); - memcpy(message + 2, source, 2); - memcpy(message + 4, destination, 2); - memcpy(message + 6, controlCode, 1); - memcpy(message + 7, functionCode, 1); - memcpy(message + 8, dataLength, 1); - memcpy(message + 9, data, sizeof(data)); - uint16_t crc = solaxX1_calculateCRC(message, msgLen); // calculate out crc bytes - - while (solaxX1Serial->available() > 0) - { // read serial if any old data is available - solaxX1Serial->read(); - } - - solaxX1Serial->flush(); - solaxX1Serial->write(message, msgLen); - solaxX1Serial->write(highByte(crc)); - solaxX1Serial->write(lowByte(crc)); - AddLogBuffer(LOG_LEVEL_DEBUG_MORE, message, msgLen); -} - -uint8_t solaxX1_RS485Receive(uint8_t *value) -{ - uint8_t len = 0; - - while (solaxX1Serial->available() > 0) - { - value[len++] = (uint8_t)solaxX1Serial->read(); - } - - AddLogBuffer(LOG_LEVEL_DEBUG_MORE, value, len); - - uint16_t crc = solaxX1_calculateCRC(value, len - 2); // calculate out crc bytes - - if (value[len - 1] == lowByte(crc) && value[len - 2] == highByte(crc)) - { // check calc crc with received crc - return solaxX1_ERR_NO_ERROR; - } - else - { - return solaxX1_ERR_CRC_ERROR; - } -} - -uint16_t solaxX1_calculateCRC(uint8_t *bExternTxPackage, uint8_t bLen) -{ - uint8_t i; - uint16_t wChkSum; - wChkSum = 0; - - for (i = 0; i < bLen; i++) - { - wChkSum = wChkSum + bExternTxPackage[i]; - } - return wChkSum; -} - -void solaxX1_SendInverterAddress(void) -{ - source[0] = 0x00; - destination[0] = 0x00; - destination[1] = 0x00; - controlCode[0] = 0x10; - functionCode[0] = 0x01; - dataLength[0] = 0x0F; - data[14] = INVERTER_ADDRESS; // Inverter Address, It must be unique in case of more inverters in the same rs485 net. - solaxX1_RS485Send(24); -} - -void solaxX1_QueryLiveData(void) -{ - source[0] = 0x01; - destination[0] = 0x00; - destination[1] = INVERTER_ADDRESS; - controlCode[0] = 0x11; - functionCode[0] = 0x02; - dataLength[0] = 0x00; - solaxX1_RS485Send(9); -} - -uint8_t solaxX1_ParseErrorCode(uint32_t code){ - ErrCode.ErrMessage = code; - - if (code == 0) return 0; - if (ErrCode.MainsLostFault) return 1; - if (ErrCode.GridVoltFault) return 2; - if (ErrCode.GridFreqFault) return 3; - if (ErrCode.PvVoltFault) return 4; - if (ErrCode.IsolationFault) return 5; - if (ErrCode.TemperatureOverFault) return 6; - if (ErrCode.FanFault) return 7; - if (ErrCode.OtherDeviceFault) return 8; - return 0; -} - -/*********************************************************************************************/ - -uint8_t solaxX1_send_retry = 0; -uint8_t solaxX1_nodata_count = 0; - -void solaxX1250MSecond(void) // Every Second -{ - uint8_t value[61] = {0}; - bool data_ready = solaxX1_RS485ReceiveReady(); - - if (protocolStatus.hasAddress && (data_ready || solaxX1_send_retry == 0)) - { - if (data_ready) - { - uint8_t error = solaxX1_RS485Receive(value); - if (error) - { - DEBUG_SENSOR_LOG(PSTR("SX1: Data response CRC error")); - } - else - { - solaxX1_nodata_count = 0; - solaxX1_send_retry = 12; - Energy.data_valid[0] = 0; - - solaxX1.temperature = (float)((value[9] << 8) | value[10]); // Temperature - solaxX1.energy_today = (float)((value[11] << 8) | value[12]) * 0.1f; // Energy Today - solaxX1.dc1_voltage = (float)((value[13] << 8) | value[14]) * 0.1f; // PV1 Voltage - solaxX1.dc2_voltage = (float)((value[15] << 8) | value[16]) * 0.1f; // PV2 Voltage - solaxX1.dc1_current = (float)((value[17] << 8) | value[18]) * 0.1f; // PV1 Current - solaxX1.dc2_current = (float)((value[19] << 8) | value[20]) * 0.1f; // PV2 Current - Energy.current[0] = (float)((value[21] << 8) | value[22]) * 0.1f; // AC Current - Energy.voltage[0] = (float)((value[23] << 8) | value[24]) * 0.1f; // AC Voltage - Energy.frequency[0] = (float)((value[25] << 8) | value[26]) * 0.01f; // AC Frequency - Energy.active_power[0] = (float)((value[27] << 8) | value[28]); // AC Power - //temporal = (float)((value[29] << 8) | value[30]) * 0.1f; // Not Used - solaxX1.energy_total = (float)((value[31] << 8) | (value[32] << 8) | (value[33] << 8) | value[34]) * 0.1f; // Energy Total - solaxX1.runtime_total = (float)((value[35] << 8) | (value[36] << 8) | (value[37] << 8) | value[38]); // Work Time Total - solaxX1.status = (uint8_t)((value[39] << 8) | value[40]); // Work mode - //temporal = (float)((value[41] << 8) | value[42]); // Grid voltage fault value 0.1V - //temporal = (float)((value[43] << 8) | value[44]); // Gird frequency fault value 0.01Hz - //temporal = (float)((value[45] << 8) | value[46]); // Dc injection fault value 1mA - //temporal = (float)((value[47] << 8) | value[48]); // Temperature fault value - //temporal = (float)((value[49] << 8) | value[50]); // Pv1 voltage fault value 0.1V - //temporal = (float)((value[51] << 8) | value[52]); // Pv2 voltage fault value 0.1V - //temporal = (float)((value[53] << 8) | value[54]); // GFC fault value - solaxX1.errorCode = (uint32_t)((value[58] << 8) | (value[57] << 8) | (value[56] << 8) | value[55]); // Error Code - - solaxX1.dc1_power = solaxX1.dc1_voltage * solaxX1.dc1_current; - solaxX1.dc2_power = solaxX1.dc2_voltage * solaxX1.dc2_current; - - solaxX1_QueryLiveData(); - EnergyUpdateTotal(solaxX1.energy_total, true); // 484.708 kWh - } - } // End data Ready - - if (0 == solaxX1_send_retry && 255 != solaxX1_nodata_count) { - solaxX1_send_retry = 12; - solaxX1_QueryLiveData(); - } - - // While the inverter has not stable ambient light, will send an address adquired but go offline again, - // so no data will be received when the query is send, then we start the countdown to set the inverter as offline again. - if (255 == solaxX1_nodata_count) { - solaxX1_nodata_count = 0; - solaxX1_send_retry = 12; - } - } // end hasAddress && (data_ready || solaxX1_send_retry == 0) - else - { - if ((solaxX1_nodata_count % 4) == 0) { DEBUG_SENSOR_LOG(PSTR("SX1: No Data count: %d"), solaxX1_nodata_count); } - if (solaxX1_nodata_count < 10 * 4) // max. seconds without data - { - solaxX1_nodata_count++; - } - else if (255 != solaxX1_nodata_count) - { - // no data from RS485, reset values to 0 and set inverter as offline - solaxX1_nodata_count = 255; - solaxX1_send_retry = 12; - protocolStatus.status = 0b00001000; // queryOffline - Energy.data_valid[0] = ENERGY_WATCHDOG; - - solaxX1.temperature = solaxX1.dc1_voltage = solaxX1.dc2_voltage = solaxX1.dc1_current = solaxX1.dc2_current = solaxX1.dc1_power = 0; - solaxX1.dc2_power = solaxX1.status = Energy.current[0] = Energy.voltage[0] = Energy.frequency[0] = Energy.active_power[0] = 0; - //solaxX1.energy_today = solaxX1.energy_total = solaxX1.runtime_total = 0; - } - } - - if (!protocolStatus.hasAddress && (data_ready || solaxX1_send_retry == 0)) - { - if (data_ready) - { - // check address confirmation from inverter - if (protocolStatus.inverterAddressSend) - { - uint8_t error = solaxX1_RS485Receive(value); - if (error) - { - DEBUG_SENSOR_LOG(PSTR("SX1: Address confirmation response CRC error")); - } - else - { - if (value[6] == 0x10 && value[7] == 0x81 && value[9] == 0x06) - { - DEBUG_SENSOR_LOG(PSTR("SX1: Set hasAddress")); - protocolStatus.status = 0b00100000; // hasAddress - } - } - } - - // Check inverter serial number and send the set address request - if (protocolStatus.queryOfflineSend) - { - uint8_t error = solaxX1_RS485Receive(value); - if (error) - { - DEBUG_SENSOR_LOG(PSTR("SX1: Query Offline response CRC error")); - } - else - { - // Serial number from query response - if (value[6] == 0x10 && value[7] == 0x80 && protocolStatus.inverterSnReceived == false) - { - for (uint8_t i = 9; i <= 22; i++) - { - data[i - 9] = value[i]; - } - solaxX1_SendInverterAddress(); - protocolStatus.status = 0b1100000; // inverterSnReceived and inverterAddressSend - DEBUG_SENSOR_LOG(PSTR("SX1: Set inverterSnReceived and inverterAddressSend")); - } - } - } - } // End data ready - - if (solaxX1_send_retry == 0) - { - if (protocolStatus.queryOfflineSend) - { - protocolStatus.status = 0b00001000; // queryOffline - DEBUG_SENSOR_LOG(PSTR("SX1: Set Query Offline")); - } - solaxX1_send_retry = 12; - } - - // request to the inverter the serial number if offline - if (protocolStatus.queryOffline) - { - // We sent the message to query inverters in offline status - source[0] = 0x01; - destination[1] = 0x00; - controlCode[0] = 0x10; - functionCode[0] = 0x00; - dataLength[0] = 0x00; - solaxX1_RS485Send(9); - protocolStatus.status = 0b00010000; // queryOfflineSend - DEBUG_SENSOR_LOG(PSTR("SX1: Query Offline Send")); - } - } // end !hasAddress && (data_ready || solaxX1_send_retry == 0) - - if (!data_ready) - solaxX1_send_retry--; -} - -void solaxX1SnsInit(void) -{ - AddLog_P(LOG_LEVEL_DEBUG, PSTR("SX1: Solax X1 Inverter Init")); - DEBUG_SENSOR_LOG(PSTR("SX1: RX pin: %d, TX pin: %d"), Pin(GPIO_SOLAXX1_RX), Pin(GPIO_SOLAXX1_TX)); - protocolStatus.status = 0b00100000; // hasAddress - - solaxX1Serial = new TasmotaSerial(Pin(GPIO_SOLAXX1_RX), Pin(GPIO_SOLAXX1_TX), 1); - if (solaxX1Serial->begin(SOLAXX1_SPEED)) { - if (solaxX1Serial->hardwareSerial()) { ClaimSerial(); } - } else { - TasmotaGlobal.energy_driver = ENERGY_NONE; - } -} - -void solaxX1DrvInit(void) -{ - if (PinUsed(GPIO_SOLAXX1_RX) && PinUsed(GPIO_SOLAXX1_TX)) { - TasmotaGlobal.energy_driver = XNRG_12; - } -} - -#ifdef USE_WEBSERVER -const char HTTP_SNS_solaxX1_DATA1[] PROGMEM = - "{s}" D_SOLAX_X1 " " D_SOLAR_POWER "{m}%s " D_UNIT_WATT "{e}" - "{s}" D_SOLAX_X1 " " D_PV1_VOLTAGE "{m}%s " D_UNIT_VOLT "{e}" - "{s}" D_SOLAX_X1 " " D_PV1_CURRENT "{m}%s " D_UNIT_AMPERE "{e}" - "{s}" D_SOLAX_X1 " " D_PV1_POWER "{m}%s " D_UNIT_WATT "{e}"; -#ifdef SOLAXX1_PV2 -const char HTTP_SNS_solaxX1_DATA2[] PROGMEM = - "{s}" D_SOLAX_X1 " " D_PV2_VOLTAGE "{m}%s " D_UNIT_VOLT "{e}" - "{s}" D_SOLAX_X1 " " D_PV2_CURRENT "{m}%s " D_UNIT_AMPERE "{e}" - "{s}" D_SOLAX_X1 " " D_PV2_POWER "{m}%s " D_UNIT_WATT "{e}"; -#endif -const char HTTP_SNS_solaxX1_DATA3[] PROGMEM = - "{s}" D_SOLAX_X1 " " D_UPTIME "{m}%s " D_UNIT_HOUR "{e}" - "{s}" D_SOLAX_X1 " " D_STATUS "{m}%s" - "{s}" D_SOLAX_X1 " " D_ERROR "{m}%s"; -#endif // USE_WEBSERVER - -void solaxX1Show(bool json) -{ - char solar_power[33]; - dtostrfd(solaxX1.dc1_power + solaxX1.dc2_power, Settings.flag2.wattage_resolution, solar_power); - char pv1_voltage[33]; - dtostrfd(solaxX1.dc1_voltage, Settings.flag2.voltage_resolution, pv1_voltage); - char pv1_current[33]; - dtostrfd(solaxX1.dc1_current, Settings.flag2.current_resolution, pv1_current); - char pv1_power[33]; - dtostrfd(solaxX1.dc1_power, Settings.flag2.wattage_resolution, pv1_power); -#ifdef SOLAXX1_PV2 - char pv2_voltage[33]; - dtostrfd(solaxX1.dc2_voltage, Settings.flag2.voltage_resolution, pv2_voltage); - char pv2_current[33]; - dtostrfd(solaxX1.dc2_current, Settings.flag2.current_resolution, pv2_current); - char pv2_power[33]; - dtostrfd(solaxX1.dc2_power, Settings.flag2.wattage_resolution, pv2_power); -#endif - char temperature[33]; - dtostrfd(solaxX1.temperature, Settings.flag2.temperature_resolution, temperature); - char runtime[33]; - dtostrfd(solaxX1.runtime_total, 0, runtime); - char status[33]; - GetTextIndexed(status, sizeof(status), solaxX1.status, kSolaxMode); - - if (json) - { - ResponseAppend_P(PSTR(",\"" D_JSON_SOLAR_POWER "\":%s,\"" D_JSON_PV1_VOLTAGE "\":%s,\"" D_JSON_PV1_CURRENT "\":%s,\"" D_JSON_PV1_POWER "\":%s"), - solar_power, pv1_voltage, pv1_current, pv1_power); -#ifdef SOLAXX1_PV2 - ResponseAppend_P(PSTR(",\"" D_JSON_PV2_VOLTAGE "\":%s,\"" D_JSON_PV2_CURRENT "\":%s,\"" D_JSON_PV2_POWER "\":%s"), - pv2_voltage, pv2_current, pv2_power); -#endif - ResponseAppend_P(PSTR(",\"" D_JSON_TEMPERATURE "\":%s,\"" D_JSON_RUNTIME "\":%s,\"" D_JSON_STATUS "\":\"%s\",\"" D_JSON_ERROR "\":%d"), - temperature, runtime, status, solaxX1.errorCode); - -#ifdef USE_WEBSERVER - } - else - { - WSContentSend_PD(HTTP_SNS_solaxX1_DATA1, solar_power, pv1_voltage, pv1_current, pv1_power); -#ifdef SOLAXX1_PV2 - WSContentSend_PD(HTTP_SNS_solaxX1_DATA2, pv2_voltage, pv2_current, pv2_power); -#endif - WSContentSend_PD(HTTP_SNS_TEMP, D_SOLAX_X1, temperature, TempUnit()); - char errorCodeString[33]; - WSContentSend_PD(HTTP_SNS_solaxX1_DATA3, runtime, status, - GetTextIndexed(errorCodeString, sizeof(errorCodeString), solaxX1_ParseErrorCode(solaxX1.errorCode), kSolaxError)); -#endif // USE_WEBSERVER - } -} - -/*********************************************************************************************\ - * Interface -\*********************************************************************************************/ - -bool Xnrg12(uint8_t function) -{ - bool result = false; - - switch (function) { - case FUNC_EVERY_250_MSECOND: - solaxX1250MSecond(); - break; - case FUNC_JSON_APPEND: - solaxX1Show(1); - break; -#ifdef USE_WEBSERVER - case FUNC_WEB_SENSOR: - solaxX1Show(0); - break; -#endif // USE_WEBSERVER - case FUNC_INIT: - solaxX1SnsInit(); - break; - case FUNC_PRE_INIT: - solaxX1DrvInit(); - break; - } - return result; -} - -#endif // USE_SOLAX_X1_NRG +/* + xnrg_12_solaxX1.ino - Solax X1 inverter RS485 support for Tasmota + + Copyright (C) 2021 Pablo Zerón + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifdef USE_ENERGY_SENSOR +#ifdef USE_SOLAX_X1 +/*********************************************************************************************\ + * Solax X1 Inverter +\*********************************************************************************************/ + +#define XNRG_12 12 + +#ifndef SOLAXX1_SPEED +#define SOLAXX1_SPEED 9600 // default solax rs485 speed +#endif + +#define INVERTER_ADDRESS 0x0A + +#define D_SOLAX_X1 "SolaxX1" + +#include + +enum solaxX1_Error +{ + solaxX1_ERR_NO_ERROR, + solaxX1_ERR_CRC_ERROR +}; + +union { + uint32_t ErrMessage; + struct { + //BYTE0 + uint8_t TzProtectFault:1;//0 + uint8_t MainsLostFault:1;//1 + uint8_t GridVoltFault:1;//2 + uint8_t GridFreqFault:1;//3 + uint8_t PLLLostFault:1;//4 + uint8_t BusVoltFault:1;//5 + uint8_t ErrBit06:1;//6 + uint8_t OciFault:1;//7 + //BYTE1 + uint8_t Dci_OCP_Fault:1;//8 + uint8_t ResidualCurrentFault:1;//9 + uint8_t PvVoltFault:1;//10 + uint8_t Ac10Mins_Voltage_Fault:1;//11 + uint8_t IsolationFault:1;//12 + uint8_t TemperatureOverFault:1;//13 + uint8_t FanFault:1;//14 + uint8_t ErrBit15:1;//15 + //BYTE2 + uint8_t SpiCommsFault:1;//16 + uint8_t SciCommsFault:1;//17 + uint8_t ErrBit18:1;//18 + uint8_t InputConfigFault:1;//19 + uint8_t EepromFault:1;//20 + uint8_t RelayFault:1;//21 + uint8_t SampleConsistenceFault:1;//22 + uint8_t ResidualCurrent_DeviceFault:1;//23 + //BYTE3 + uint8_t ErrBit24:1;//24 + uint8_t ErrBit25:1;//25 + uint8_t ErrBit26:1;//26 + uint8_t ErrBit27:1;//27 + uint8_t ErrBit28:1;//28 + uint8_t DCI_DeviceFault:1;//29 + uint8_t OtherDeviceFault:1;//30 + uint8_t ErrBit31:1;//31 + }; +} ErrCode; + +const char kSolaxMode[] PROGMEM = D_WAITING "|" D_CHECKING "|" D_WORKING "|" D_FAILURE; + +const char kSolaxError[] PROGMEM = + D_SOLAX_ERROR_0 "|" D_SOLAX_ERROR_1 "|" D_SOLAX_ERROR_2 "|" D_SOLAX_ERROR_3 "|" D_SOLAX_ERROR_4 "|" D_SOLAX_ERROR_5 "|" + D_SOLAX_ERROR_6 "|" D_SOLAX_ERROR_7 "|" D_SOLAX_ERROR_8; + +/*********************************************************************************************/ + +TasmotaSerial *solaxX1Serial; + +struct SOLAXX1 { + float temperature = 0; + float energy_today = 0; + float dc1_voltage = 0; + float dc2_voltage = 0; + float dc1_current = 0; + float dc2_current = 0; + uint32_t energy_total = 0; + uint32_t runtime_total = 0; + float dc1_power = 0; + float dc2_power = 0; + + uint8_t status = 0; + uint32_t errorCode = 0; +} solaxX1; + +union { + uint8_t status; + struct { + uint8_t freeBit7:1; // Bit7 + uint8_t freeBit6:1; // Bit6 + uint8_t freeBit5:1; // Bit5 + uint8_t queryOffline:1; // Bit4 + uint8_t queryOfflineSend:1; // Bit3 + uint8_t hasAddress:1; // Bit2 + uint8_t inverterAddressSend:1; // Bit1 + uint8_t inverterSnReceived:1; // Bit0 + }; +} protocolStatus; + +uint8_t header[2] = {0xAA, 0x55}; +uint8_t source[2] = {0x00, 0x00}; +uint8_t destination[2] = {0x00, 0x00}; +uint8_t controlCode[1] = {0x00}; +uint8_t functionCode[1] = {0x00}; +uint8_t dataLength[1] = {0x00}; +uint8_t data[16] = {0}; + +uint8_t message[30]; + +/*********************************************************************************************/ + +bool solaxX1_RS485ReceiveReady(void) +{ + return (solaxX1Serial->available() > 1); +} + +void solaxX1_RS485Send(uint16_t msgLen) +{ + memcpy(message, header, 2); + memcpy(message + 2, source, 2); + memcpy(message + 4, destination, 2); + memcpy(message + 6, controlCode, 1); + memcpy(message + 7, functionCode, 1); + memcpy(message + 8, dataLength, 1); + memcpy(message + 9, data, sizeof(data)); + uint16_t crc = solaxX1_calculateCRC(message, msgLen); // calculate out crc bytes + + while (solaxX1Serial->available() > 0) + { // read serial if any old data is available + solaxX1Serial->read(); + } + + solaxX1Serial->flush(); + solaxX1Serial->write(message, msgLen); + solaxX1Serial->write(highByte(crc)); + solaxX1Serial->write(lowByte(crc)); + AddLogBuffer(LOG_LEVEL_DEBUG_MORE, message, msgLen); +} + +uint8_t solaxX1_RS485Receive(uint8_t *value) +{ + uint8_t len = 0; + + while (solaxX1Serial->available() > 0) + { + value[len++] = (uint8_t)solaxX1Serial->read(); + } + + AddLogBuffer(LOG_LEVEL_DEBUG_MORE, value, len); + + uint16_t crc = solaxX1_calculateCRC(value, len - 2); // calculate out crc bytes + + if (value[len - 1] == lowByte(crc) && value[len - 2] == highByte(crc)) + { // check calc crc with received crc + return solaxX1_ERR_NO_ERROR; + } + else + { + return solaxX1_ERR_CRC_ERROR; + } +} + +uint16_t solaxX1_calculateCRC(uint8_t *bExternTxPackage, uint8_t bLen) +{ + uint8_t i; + uint16_t wChkSum; + wChkSum = 0; + + for (i = 0; i < bLen; i++) + { + wChkSum = wChkSum + bExternTxPackage[i]; + } + return wChkSum; +} + +void solaxX1_SendInverterAddress(void) +{ + source[0] = 0x00; + destination[0] = 0x00; + destination[1] = 0x00; + controlCode[0] = 0x10; + functionCode[0] = 0x01; + dataLength[0] = 0x0F; + data[14] = INVERTER_ADDRESS; // Inverter Address, It must be unique in case of more inverters in the same rs485 net. + solaxX1_RS485Send(24); +} + +void solaxX1_QueryLiveData(void) +{ + source[0] = 0x01; + destination[0] = 0x00; + destination[1] = INVERTER_ADDRESS; + controlCode[0] = 0x11; + functionCode[0] = 0x02; + dataLength[0] = 0x00; + solaxX1_RS485Send(9); +} + +uint8_t solaxX1_ParseErrorCode(uint32_t code){ + ErrCode.ErrMessage = code; + + if (code == 0) return 0; + if (ErrCode.MainsLostFault) return 1; + if (ErrCode.GridVoltFault) return 2; + if (ErrCode.GridFreqFault) return 3; + if (ErrCode.PvVoltFault) return 4; + if (ErrCode.IsolationFault) return 5; + if (ErrCode.TemperatureOverFault) return 6; + if (ErrCode.FanFault) return 7; + if (ErrCode.OtherDeviceFault) return 8; + return 0; +} + +/*********************************************************************************************/ + +uint8_t solaxX1_send_retry = 20; +uint8_t solaxX1_queryData_count = 0; + +void solaxX1250MSecond(void) // Every 250 milliseconds +{ + uint8_t value[61] = {0}; + bool data_ready = solaxX1_RS485ReceiveReady(); + + if (data_ready) + { + if (protocolStatus.hasAddress) + { + uint8_t error = solaxX1_RS485Receive(value); + if (error) + { + DEBUG_SENSOR_LOG(PSTR("SX1: Data response CRC error")); + } + else + { + solaxX1_send_retry = 20; + Energy.data_valid[0] = 0; + + solaxX1.temperature = (float)((value[9] << 8) | value[10]); // Temperature + solaxX1.energy_today = (float)((value[11] << 8) | value[12]) * 0.1f; // Energy Today + solaxX1.dc1_voltage = (float)((value[13] << 8) | value[14]) * 0.1f; // PV1 Voltage + solaxX1.dc2_voltage = (float)((value[15] << 8) | value[16]) * 0.1f; // PV2 Voltage + solaxX1.dc1_current = (float)((value[17] << 8) | value[18]) * 0.1f; // PV1 Current + solaxX1.dc2_current = (float)((value[19] << 8) | value[20]) * 0.1f; // PV2 Current + Energy.current[0] = (float)((value[21] << 8) | value[22]) * 0.1f; // AC Current + Energy.voltage[0] = (float)((value[23] << 8) | value[24]) * 0.1f; // AC Voltage + Energy.frequency[0] = (float)((value[25] << 8) | value[26]) * 0.01f; // AC Frequency + Energy.active_power[0] = (float)((value[27] << 8) | value[28]); // AC Power + //temporal = (float)((value[29] << 8) | value[30]) * 0.1f; // Not Used + solaxX1.energy_total = ((value[31] << 24) | (value[32] << 16) | (value[33] << 8) | value[34]); // Energy Total + solaxX1.runtime_total = ((value[35] << 24) | (value[36] << 16) | (value[37] << 8) | value[38]); // Work Time Total + solaxX1.status = (uint8_t)((value[39] << 8) | value[40]); // Work mode + //temporal = (float)((value[41] << 8) | value[42]); // Grid voltage fault value 0.1V + //temporal = (float)((value[43] << 8) | value[44]); // Gird frequency fault value 0.01Hz + //temporal = (float)((value[45] << 8) | value[46]); // Dc injection fault value 1mA + //temporal = (float)((value[47] << 8) | value[48]); // Temperature fault value + //temporal = (float)((value[49] << 8) | value[50]); // Pv1 voltage fault value 0.1V + //temporal = (float)((value[51] << 8) | value[52]); // Pv2 voltage fault value 0.1V + //temporal = (float)((value[53] << 8) | value[54]); // GFC fault value + solaxX1.errorCode = ((value[58] << 24) | (value[57] << 16) | (value[56] << 8) | value[55]); // Error Code + + solaxX1.dc1_power = solaxX1.dc1_voltage * solaxX1.dc1_current; + solaxX1.dc2_power = solaxX1.dc2_voltage * solaxX1.dc2_current; + + EnergyUpdateTotal((float)solaxX1.energy_total * 0.1f, true); // 484.708 kWh + } + } else { // end hasAddress + // check address confirmation from inverter + if (protocolStatus.inverterAddressSend) + { + uint8_t error = solaxX1_RS485Receive(value); + if (error) + { + DEBUG_SENSOR_LOG(PSTR("SX1: Address confirmation response CRC error")); + } + else + { + if (value[6] == 0x10 && value[7] == 0x81 && value[9] == 0x06) + { + DEBUG_SENSOR_LOG(PSTR("SX1: Set hasAddress")); + protocolStatus.status = 0b00100000; // hasAddress + } + } + } + + // Check inverter serial number and send the set address request + if (protocolStatus.queryOfflineSend) + { + uint8_t error = solaxX1_RS485Receive(value); + if (error) + { + DEBUG_SENSOR_LOG(PSTR("SX1: Query Offline response CRC error")); + } + else + { + // Serial number from query response + if (value[6] == 0x10 && value[7] == 0x80 && protocolStatus.inverterSnReceived == false) + { + for (uint8_t i = 9; i <= 22; i++) + { + data[i - 9] = value[i]; + } + solaxX1_SendInverterAddress(); + protocolStatus.status = 0b1100000; // inverterSnReceived and inverterAddressSend + DEBUG_SENSOR_LOG(PSTR("SX1: Set inverterSnReceived and inverterAddressSend")); + } + } + } + } + } + + if (protocolStatus.hasAddress) { + if (solaxX1_queryData_count <= 0) { + solaxX1_queryData_count = 5; + DEBUG_SENSOR_LOG(PSTR("SX1: Send Retry count: %d"), solaxX1_send_retry); + solaxX1_QueryLiveData(); + } + solaxX1_queryData_count--; + } + + // request to the inverter the serial number if offline + if (protocolStatus.queryOffline) + { + // We sent the message to query inverters in offline status + source[0] = 0x01; + destination[1] = 0x00; + controlCode[0] = 0x10; + functionCode[0] = 0x00; + dataLength[0] = 0x00; + solaxX1_RS485Send(9); + protocolStatus.status = 0b00010000; // queryOfflineSend + solaxX1_send_retry = 20; + DEBUG_SENSOR_LOG(PSTR("SX1: Query Offline Send")); + } + + if (solaxX1_send_retry == 0) { + if (protocolStatus.hasAddress) { + protocolStatus.status = 0b00001000; // queryOffline + Energy.data_valid[0] = ENERGY_WATCHDOG; + + solaxX1.temperature = solaxX1.dc1_voltage = solaxX1.dc2_voltage = solaxX1.dc1_current = solaxX1.dc2_current = solaxX1.dc1_power = 0; + solaxX1.dc2_power = solaxX1.status = Energy.current[0] = Energy.voltage[0] = Energy.frequency[0] = Energy.active_power[0] = 0; + //solaxX1.energy_today = solaxX1.energy_total = solaxX1.runtime_total = 0; + } else { + if (protocolStatus.queryOfflineSend) { + protocolStatus.status = 0b00001000; // queryOffline + DEBUG_SENSOR_LOG(PSTR("SX1: Set Query Offline")); + } + solaxX1_send_retry = 20; + } + } + + if (!data_ready && solaxX1_send_retry > 0) { solaxX1_send_retry--; } +} + +void solaxX1SnsInit(void) +{ + AddLog(LOG_LEVEL_DEBUG, PSTR("SX1: Solax X1 Inverter Init")); + DEBUG_SENSOR_LOG(PSTR("SX1: RX pin: %d, TX pin: %d"), Pin(GPIO_SOLAXX1_RX), Pin(GPIO_SOLAXX1_TX)); + protocolStatus.status = 0b00100000; // hasAddress + + solaxX1Serial = new TasmotaSerial(Pin(GPIO_SOLAXX1_RX), Pin(GPIO_SOLAXX1_TX), 1); + if (solaxX1Serial->begin(SOLAXX1_SPEED)) { + if (solaxX1Serial->hardwareSerial()) { ClaimSerial(); } + } else { + TasmotaGlobal.energy_driver = ENERGY_NONE; + } +} + +void solaxX1DrvInit(void) +{ + if (PinUsed(GPIO_SOLAXX1_RX) && PinUsed(GPIO_SOLAXX1_TX)) { + TasmotaGlobal.energy_driver = XNRG_12; + } +} + +#ifdef USE_WEBSERVER +const char HTTP_SNS_solaxX1_DATA1[] PROGMEM = + "{s}" D_SOLAX_X1 " " D_SOLAR_POWER "{m}%s " D_UNIT_WATT "{e}" + "{s}" D_SOLAX_X1 " " D_PV1_VOLTAGE "{m}%s " D_UNIT_VOLT "{e}" + "{s}" D_SOLAX_X1 " " D_PV1_CURRENT "{m}%s " D_UNIT_AMPERE "{e}" + "{s}" D_SOLAX_X1 " " D_PV1_POWER "{m}%s " D_UNIT_WATT "{e}"; +#ifdef SOLAXX1_PV2 +const char HTTP_SNS_solaxX1_DATA2[] PROGMEM = + "{s}" D_SOLAX_X1 " " D_PV2_VOLTAGE "{m}%s " D_UNIT_VOLT "{e}" + "{s}" D_SOLAX_X1 " " D_PV2_CURRENT "{m}%s " D_UNIT_AMPERE "{e}" + "{s}" D_SOLAX_X1 " " D_PV2_POWER "{m}%s " D_UNIT_WATT "{e}"; +#endif +const char HTTP_SNS_solaxX1_DATA3[] PROGMEM = + "{s}" D_SOLAX_X1 " " D_UPTIME "{m}%s " D_UNIT_HOUR "{e}" + "{s}" D_SOLAX_X1 " " D_STATUS "{m}%s" + "{s}" D_SOLAX_X1 " " D_ERROR "{m}%s"; +#endif // USE_WEBSERVER + +void solaxX1Show(bool json) +{ + char solar_power[33]; + dtostrfd(solaxX1.dc1_power + solaxX1.dc2_power, Settings.flag2.wattage_resolution, solar_power); + char pv1_voltage[33]; + dtostrfd(solaxX1.dc1_voltage, Settings.flag2.voltage_resolution, pv1_voltage); + char pv1_current[33]; + dtostrfd(solaxX1.dc1_current, Settings.flag2.current_resolution, pv1_current); + char pv1_power[33]; + dtostrfd(solaxX1.dc1_power, Settings.flag2.wattage_resolution, pv1_power); +#ifdef SOLAXX1_PV2 + char pv2_voltage[33]; + dtostrfd(solaxX1.dc2_voltage, Settings.flag2.voltage_resolution, pv2_voltage); + char pv2_current[33]; + dtostrfd(solaxX1.dc2_current, Settings.flag2.current_resolution, pv2_current); + char pv2_power[33]; + dtostrfd(solaxX1.dc2_power, Settings.flag2.wattage_resolution, pv2_power); +#endif + char runtime[33]; + dtostrfd(solaxX1.runtime_total, 0, runtime); + char status[33]; + GetTextIndexed(status, sizeof(status), solaxX1.status, kSolaxMode); + + if (json) + { + ResponseAppend_P(PSTR(",\"" D_JSON_SOLAR_POWER "\":%s,\"" D_JSON_PV1_VOLTAGE "\":%s,\"" D_JSON_PV1_CURRENT "\":%s,\"" D_JSON_PV1_POWER "\":%s"), + solar_power, pv1_voltage, pv1_current, pv1_power); +#ifdef SOLAXX1_PV2 + ResponseAppend_P(PSTR(",\"" D_JSON_PV2_VOLTAGE "\":%s,\"" D_JSON_PV2_CURRENT "\":%s,\"" D_JSON_PV2_POWER "\":%s"), + pv2_voltage, pv2_current, pv2_power); +#endif + ResponseAppend_P(PSTR(",\"" D_JSON_TEMPERATURE "\":%*_f,\"" D_JSON_RUNTIME "\":%s,\"" D_JSON_STATUS "\":\"%s\",\"" D_JSON_ERROR "\":%d"), + Settings.flag2.temperature_resolution, &solaxX1.temperature, runtime, status, solaxX1.errorCode); + +#ifdef USE_DOMOTICZ + // Avoid bad temperature report at beginning of the day (spikes of 1200 celsius degrees) + if (0 == TasmotaGlobal.tele_period && solaxX1.temperature < 100) { DomoticzFloatSensor(DZ_TEMP, solaxX1.temperature); } +#endif // USE_DOMOTICZ + +#ifdef USE_WEBSERVER + } else { + WSContentSend_PD(HTTP_SNS_solaxX1_DATA1, solar_power, pv1_voltage, pv1_current, pv1_power); +#ifdef SOLAXX1_PV2 + WSContentSend_PD(HTTP_SNS_solaxX1_DATA2, pv2_voltage, pv2_current, pv2_power); +#endif + WSContentSend_Temp(D_SOLAX_X1, solaxX1.temperature); + char errorCodeString[33]; + WSContentSend_PD(HTTP_SNS_solaxX1_DATA3, runtime, status, + GetTextIndexed(errorCodeString, sizeof(errorCodeString), solaxX1_ParseErrorCode(solaxX1.errorCode), kSolaxError)); +#endif // USE_WEBSERVER + } +} + +/*********************************************************************************************\ + * Interface +\*********************************************************************************************/ + +bool Xnrg12(uint8_t function) +{ + bool result = false; + + switch (function) { + case FUNC_EVERY_250_MSECOND: + solaxX1250MSecond(); + break; + case FUNC_JSON_APPEND: + solaxX1Show(1); + break; +#ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: + solaxX1Show(0); + break; +#endif // USE_WEBSERVER + case FUNC_INIT: + solaxX1SnsInit(); + break; + case FUNC_PRE_INIT: + solaxX1DrvInit(); + break; + } + return result; +} + +#endif // USE_SOLAX_X1_NRG #endif // USE_ENERGY_SENSOR \ No newline at end of file diff --git a/tasmota/xnrg_13_fif_le01mr.ino b/tasmota/xnrg_13_fif_le01mr.ino index 8678e0a6c..727f6530a 100644 --- a/tasmota/xnrg_13_fif_le01mr.ino +++ b/tasmota/xnrg_13_fif_le01mr.ino @@ -123,7 +123,7 @@ void FifLEEvery250ms(void) AddLogBuffer(LOG_LEVEL_DEBUG_MORE, buffer, FifLEModbus->ReceiveCount()); if (error) { - AddLog_P(LOG_LEVEL_DEBUG, PSTR("FiF-LE: LE01MR Modbus error %d"), error); + AddLog(LOG_LEVEL_DEBUG, PSTR("FiF-LE: LE01MR Modbus error %d"), error); } else { Energy.data_valid[0] = 0; diff --git a/tasmota/xnrg_14_bl0940.ino b/tasmota/xnrg_14_bl0940.ino index 3237c485d..81a9d4791 100644 --- a/tasmota/xnrg_14_bl0940.ino +++ b/tasmota/xnrg_14_bl0940.ino @@ -86,7 +86,7 @@ void Bl0940Received(void) { if ((Bl0940.rx_buffer[0] != BL0940_PACKET_HEADER) || // Bad header (Bl0940.tps1 && ((tps1 < (Bl0940.tps1 -10)) || (tps1 > (Bl0940.tps1 +10)))) // Invalid temperature change ) { - AddLog_P(LOG_LEVEL_DEBUG, PSTR("BL9: Invalid data")); + AddLog(LOG_LEVEL_DEBUG, PSTR("BL9: Invalid data")); return; } @@ -101,7 +101,7 @@ void Bl0940Received(void) { int32_t cf_cnt = Bl0940.rx_buffer[24] << 24 | Bl0940.rx_buffer[23] << 16 | Bl0940.rx_buffer[22] << 8; // CF_CNT signed Bl0940.cf_pulses = abs(cf_cnt) >> 8; - AddLog_P(LOG_LEVEL_DEBUG, PSTR("BL9: U %d, I %d, P %d, C %d, T %d"), + AddLog(LOG_LEVEL_DEBUG, PSTR("BL9: U %d, I %d, P %d, C %d, T %d"), Bl0940.voltage, Bl0940.current, Bl0940.power, Bl0940.cf_pulses, Bl0940.tps1); if (Energy.power_on) { // Powered on @@ -149,7 +149,7 @@ void Bl0940SerialInput(void) { Bl0940.byte_counter--; } while ((Bl0940.byte_counter > 1) && (BL0940_PACKET_HEADER != Bl0940.rx_buffer[0])); if (BL0940_PACKET_HEADER != Bl0940.rx_buffer[0]) { - AddLog_P(LOG_LEVEL_DEBUG, PSTR("BL9: " D_CHECKSUM_FAILURE)); + AddLog(LOG_LEVEL_DEBUG, PSTR("BL9: " D_CHECKSUM_FAILURE)); Bl0940.received = false; Bl0940.byte_counter = 0; } @@ -191,7 +191,7 @@ void Bl0940EverySecond(void) { Bl0940.cf_pulses_last_time = Bl0940.cf_pulses; Energy.kWhtoday_delta += delta; } else { - AddLog_P(LOG_LEVEL_DEBUG, PSTR("BL9: Overload")); + AddLog(LOG_LEVEL_DEBUG, PSTR("BL9: Overload")); Bl0940.cf_pulses_last_time = BL0940_PULSES_NOT_INITIALIZED; } EnergyUpdateToday(); @@ -200,7 +200,7 @@ void Bl0940EverySecond(void) { } -// AddLog_P(LOG_LEVEL_DEBUG, PSTR("BL9: Poll")); +// AddLog(LOG_LEVEL_DEBUG, PSTR("BL9: Poll")); Bl0940Serial->flush(); Bl0940Serial->write(BL0940_READ_COMMAND); @@ -268,14 +268,11 @@ bool Bl0940Command(void) { } void Bl0940Show(bool json) { - char temperature[33]; - dtostrfd(Bl0940.temperature, Settings.flag2.temperature_resolution, temperature); - if (json) { - ResponseAppend_P(JSON_SNS_TEMP, "BL0940", temperature); + ResponseAppend_P(JSON_SNS_F_TEMP, "BL0940", Settings.flag2.temperature_resolution, &Bl0940.temperature); if (0 == TasmotaGlobal.tele_period) { #ifdef USE_DOMOTICZ - DomoticzSensor(DZ_TEMP, temperature); + DomoticzFloatSensor(DZ_TEMP, Bl0940.temperature); #endif // USE_DOMOTICZ #ifdef USE_KNX KnxSensor(KNX_TEMPERATURE, Bl0940.temperature); @@ -283,8 +280,9 @@ void Bl0940Show(bool json) { } #ifdef USE_WEBSERVER } else { - WSContentSend_PD(HTTP_SNS_TEMP, "", temperature, TempUnit()); + WSContentSend_Temp("", Bl0940.temperature); #endif // USE_WEBSERVER + } } diff --git a/tasmota/xnrg_15_teleinfo.ino b/tasmota/xnrg_15_teleinfo.ino index e2dfe6195..7b5c52a43 100755 --- a/tasmota/xnrg_15_teleinfo.ino +++ b/tasmota/xnrg_15_teleinfo.ino @@ -158,7 +158,7 @@ void ADPSCallback(uint8_t phase) // Publish adding ADCO serial number into the topic MqttPublishPrefixTopic_P(RESULT_OR_TELE, serialNumber, false); - AddLog_P(LOG_LEVEL_INFO, PSTR("ADPS on phase %d"), phase); + AddLog(LOG_LEVEL_INFO, PSTR("ADPS on phase %d"), phase); } /* ====================================================================== @@ -187,7 +187,7 @@ void DataCallback(struct _ValueList * me, uint8_t flags) if (flags & TINFO_FLAGS_ADDED) { c = '#'; } if (flags & TINFO_FLAGS_UPDATED) { c = '*'; } - AddLog_P(LOG_LEVEL_DEBUG, PSTR("TIC: [%d]%c %s=%s"), ilabel, c , me->name, me->value); + AddLog(LOG_LEVEL_DEBUG, PSTR("TIC: [%d]%c %s=%s"), ilabel, c , me->name, me->value); if (ilabelvalue, tarif); + AddLog(LOG_LEVEL_DEBUG, PSTR("TIC: Tarif changed, now '%s' (%d)"), me->value, tarif); } // Current tariff (standard is in clear text in value) else if (ilabel == LABEL_LTARF) { - AddLog_P(LOG_LEVEL_DEBUG, PSTR("TIC: Tarif name changed, now '%s'"), me->value); + AddLog(LOG_LEVEL_DEBUG, PSTR("TIC: Tarif name changed, now '%s'"), me->value); } // Current tariff (standard index is is in clear text in value) else if (ilabel == LABEL_NTARF) { tarif = atoi(me->value); - AddLog_P(LOG_LEVEL_DEBUG, PSTR("TIC: Tarif index changed, now '%d'"), tarif); + AddLog(LOG_LEVEL_DEBUG, PSTR("TIC: Tarif index changed, now '%d'"), tarif); } @@ -223,21 +223,21 @@ void DataCallback(struct _ValueList * me, uint8_t flags) { Energy.voltage_available = true; Energy.voltage[0] = (float) atoi(me->value); - AddLog_P(LOG_LEVEL_DEBUG, PSTR("TIC: Voltage %s, now %d"), me->value, (int) Energy.voltage[0]); + AddLog(LOG_LEVEL_DEBUG, PSTR("TIC: Voltage %s, now %d"), me->value, (int) Energy.voltage[0]); } // Current I else if (ilabel == LABEL_IINST || ilabel == LABEL_IRMS1) { Energy.current[0] = (float) atoi(me->value); - AddLog_P(LOG_LEVEL_DEBUG, PSTR("TIC: Current %s, now %d"), me->value, (int) Energy.current[0]); + AddLog(LOG_LEVEL_DEBUG, PSTR("TIC: Current %s, now %d"), me->value, (int) Energy.current[0]); } // Power P else if (ilabel == LABEL_PAPP || ilabel == LABEL_SINSTS) { Energy.active_power[0] = (float) atoi(me->value);; - AddLog_P(LOG_LEVEL_DEBUG, PSTR("TIC: Power %s, now %d"), me->value, (int) Energy.active_power[0]); + AddLog(LOG_LEVEL_DEBUG, PSTR("TIC: Power %s, now %d"), me->value, (int) Energy.active_power[0]); } // Wh indexes (legacy) @@ -251,7 +251,7 @@ void DataCallback(struct _ValueList * me, uint8_t flags) // Base, un seul index if (ilabel == LABEL_BASE) { total = atoi(me->value); - AddLog_P(LOG_LEVEL_DEBUG, PSTR("TIC: Base:%u"), total); + AddLog(LOG_LEVEL_DEBUG, PSTR("TIC: Base:%u"), total); // Heures creuses/pleines calculer total } else { // Heures creuses get heures pleines @@ -269,7 +269,7 @@ void DataCallback(struct _ValueList * me, uint8_t flags) } } total = hc + hp; - AddLog_P(LOG_LEVEL_DEBUG, PSTR("TIC: HC:%u HP:%u Total:%u"), hc, hp, total); + AddLog(LOG_LEVEL_DEBUG, PSTR("TIC: HC:%u HP:%u Total:%u"), hc, hp, total); } if (!Settings.flag4.teleinfo_rawdata) { @@ -284,17 +284,17 @@ void DataCallback(struct _ValueList * me, uint8_t flags) if (!Settings.flag4.teleinfo_rawdata) { EnergyUpdateTotal(total/1000.0f, true); } - AddLog_P(LOG_LEVEL_DEBUG, PSTR("TIC: Total:%uWh"), total); + AddLog(LOG_LEVEL_DEBUG, PSTR("TIC: Total:%uWh"), total); } // Wh indexes (standard) else if ( ilabel == LABEL_EASF01) { - AddLog_P(LOG_LEVEL_DEBUG, PSTR("TIC: HC:%u"), atoi(me->value)); + AddLog(LOG_LEVEL_DEBUG, PSTR("TIC: HC:%u"), atoi(me->value)); } else if ( ilabel == LABEL_EASF02) { - AddLog_P(LOG_LEVEL_DEBUG, PSTR("TIC: HP:%u"), atoi(me->value)); + AddLog(LOG_LEVEL_DEBUG, PSTR("TIC: HP:%u"), atoi(me->value)); } // Contract subscribed (legacy) @@ -308,26 +308,26 @@ void DataCallback(struct _ValueList * me, uint8_t flags) break; } } - AddLog_P(LOG_LEVEL_DEBUG, PSTR("TIC: Contract changed, now '%s' (%d)"), me->value, contrat); + AddLog(LOG_LEVEL_DEBUG, PSTR("TIC: Contract changed, now '%s' (%d)"), me->value, contrat); } // Contract subscribed (standard is in clear text in value) else if (ilabel == LABEL_NGTF) { - AddLog_P(LOG_LEVEL_DEBUG, PSTR("TIC: Contract changed, now '%s'"), me->value); + AddLog(LOG_LEVEL_DEBUG, PSTR("TIC: Contract changed, now '%s'"), me->value); } // Contract subscribed (Power) else if (ilabel == LABEL_ISOUSC || ilabel == LABEL_PREF) { isousc = atoi( me->value); - AddLog_P(LOG_LEVEL_DEBUG, PSTR("TIC: ISousc set to %d"), isousc); + AddLog(LOG_LEVEL_DEBUG, PSTR("TIC: ISousc set to %d"), isousc); } // Serial Number of device else if (ilabel == LABEL_ADCO || ilabel == LABEL_ADSC) { strcpy(serialNumber, me->value); - AddLog_P(LOG_LEVEL_DEBUG, PSTR("TIC: %s set to %s"), me->name, serialNumber); + AddLog(LOG_LEVEL_DEBUG, PSTR("TIC: %s set to %s"), me->name, serialNumber); } } @@ -447,20 +447,20 @@ void TInfoInit(void) tinfo_mode = TINFO_MODE_HISTORIQUE; } - AddLog_P(LOG_LEVEL_DEBUG, PSTR("TIC: inferface speed %d bps"),baudrate); + AddLog(LOG_LEVEL_DEBUG, PSTR("TIC: inferface speed %d bps"),baudrate); if (PinUsed(GPIO_TELEINFO_RX)) { uint8_t rx_pin = Pin(GPIO_TELEINFO_RX); - AddLog_P(LOG_LEVEL_INFO, PSTR("TIC: RX on GPIO%d"), rx_pin); + AddLog(LOG_LEVEL_INFO, PSTR("TIC: RX on GPIO%d"), rx_pin); // Enable Teleinfo pin used, control it if (PinUsed(GPIO_TELEINFO_ENABLE)) { uint8_t en_pin = Pin(GPIO_TELEINFO_ENABLE); pinMode(en_pin, OUTPUT); digitalWrite(en_pin, HIGH); - AddLog_P(LOG_LEVEL_INFO, PSTR("TIC: Enable with GPIO%d"), en_pin); + AddLog(LOG_LEVEL_INFO, PSTR("TIC: Enable with GPIO%d"), en_pin); } else { - AddLog_P(LOG_LEVEL_INFO, PSTR("TIC: always enabled")); + AddLog(LOG_LEVEL_INFO, PSTR("TIC: always enabled")); } #ifdef ESP8266 @@ -488,13 +488,13 @@ void TInfoInit(void) //SetSerialConfig(TS_SERIAL_7E1); //TInfoSerial->setTimeout(TINFO_READ_TIMEOUT); - AddLog_P(LOG_LEVEL_INFO, PSTR("TIC: using hardware serial")); + AddLog(LOG_LEVEL_INFO, PSTR("TIC: using hardware serial")); } else { - AddLog_P(LOG_LEVEL_INFO, PSTR("TIC: using software serial")); + AddLog(LOG_LEVEL_INFO, PSTR("TIC: using software serial")); } #endif // ESP8266 #ifdef ESP32 - AddLog_P(LOG_LEVEL_INFO, PSTR("TIC: using ESP32 hardware serial")); + AddLog(LOG_LEVEL_INFO, PSTR("TIC: using ESP32 hardware serial")); #endif // ESP32 // Init teleinfo tinfo.init(tinfo_mode); @@ -504,7 +504,7 @@ void TInfoInit(void) tinfo.attachNewFrame(NewFrameCallback); tinfo_found = true; - AddLog_P(LOG_LEVEL_INFO, PSTR("TIC: Ready")); + AddLog(LOG_LEVEL_INFO, PSTR("TIC: Ready")); } } } diff --git a/tasmota/xnrg_16_iem3000.ino b/tasmota/xnrg_16_iem3000.ino index c5ad359fe..459b45a08 100644 --- a/tasmota/xnrg_16_iem3000.ino +++ b/tasmota/xnrg_16_iem3000.ino @@ -76,7 +76,7 @@ void IEM3000Every250ms(void) AddLogBuffer(LOG_LEVEL_DEBUG_MORE, buffer, Iem3000Modbus->ReceiveCount()); if (error) { - AddLog_P(LOG_LEVEL_DEBUG, PSTR("SDM: Iem3000 error %d"), error); + AddLog(LOG_LEVEL_DEBUG, PSTR("SDM: Iem3000 error %d"), error); } else { Energy.data_valid[0] = 0; diff --git a/tasmota/xnrg_17_ornowe517.ino b/tasmota/xnrg_17_ornowe517.ino index d77114002..9dccd8b94 100644 --- a/tasmota/xnrg_17_ornowe517.ino +++ b/tasmota/xnrg_17_ornowe517.ino @@ -82,7 +82,7 @@ void WE517Every250ms(void) AddLogBuffer(LOG_LEVEL_DEBUG_MORE, buffer, We517Modbus->ReceiveCount()); if (error) { - AddLog_P(LOG_LEVEL_DEBUG, PSTR("ORNO: WE517 error %d"), error); + AddLog(LOG_LEVEL_DEBUG, PSTR("ORNO: WE517 error %d"), error); } else { Energy.data_valid[0] = 0; Energy.data_valid[1] = 0; @@ -188,7 +188,7 @@ void We517SnsInit(void) uint8_t result = We517Modbus->Begin(WE517_SPEED); if (result) { if (2 == result) { - AddLog_P(LOG_LEVEL_DEBUG, PSTR("ORNO: WE517 HW serial init 8E1 at %d baud"), WE517_SPEED); + AddLog(LOG_LEVEL_DEBUG, PSTR("ORNO: WE517 HW serial init 8E1 at %d baud"), WE517_SPEED); Serial.begin(WE517_SPEED, SERIAL_8E1); ClaimSerial(); } diff --git a/tasmota/xsns_01_counter.ino b/tasmota/xsns_01_counter.ino index 77d630851..ef3b13e4c 100644 --- a/tasmota/xsns_01_counter.ino +++ b/tasmota/xsns_01_counter.ino @@ -264,7 +264,7 @@ void SyncACDimmer(void) #ifdef ESP32 analogWrite(Pin(GPIO_PWM1, i), 5); #endif // ESP32 - //AddLog_P(LOG_LEVEL_DEBUG_MORE, PSTR("CNT: [%d] dimm_time_CCs %d, current_cycle_CC: %d, timelag %lu, lastcc %lu, currentSteps %d, curr %d"), i, ac_zero_cross_dimmer.dimm_timeClockCycles,ac_zero_cross_dimmer.current_cycle_ClockCycles , timelag_ClockCycles, ac_zero_cross_dimmer.currentCycleCount, ac_zero_cross_dimmer.currentSteps, Light.fade_cur_10[i]); + //AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("CNT: [%d] dimm_time_CCs %d, current_cycle_CC: %d, timelag %lu, lastcc %lu, currentSteps %d, curr %d"), i, ac_zero_cross_dimmer.dimm_timeClockCycles,ac_zero_cross_dimmer.current_cycle_ClockCycles , timelag_ClockCycles, ac_zero_cross_dimmer.currentCycleCount, ac_zero_cross_dimmer.currentSteps, Light.fade_cur_10[i]); } } } diff --git a/tasmota/xsns_02_analog.ino b/tasmota/xsns_02_analog.ino index 239f461c5..f939c8d9e 100644 --- a/tasmota/xsns_02_analog.ino +++ b/tasmota/xsns_02_analog.ino @@ -105,10 +105,10 @@ #define ANALOG_PH_CALSOLUTION_LOW_PH 4.0 #define ANALOG_PH_CALSOLUTION_LOW_ANALOG_VALUE 282 // Default values for calibration solution with higher PH -#define ANALOG_PH_CALSOLUTION_HIGH_PH 9.18 +#define ANALOG_PH_CALSOLUTION_HIGH_PH 9.18 #define ANALOG_PH_CALSOLUTION_HIGH_ANALOG_VALUE 435 -// Multiplier used to store pH with 2 decimal places in a non decimal datatype +// Multiplier used to store pH with 2 decimal places in a non decimal datatype #define ANALOG_PH_DECIMAL_MULTIPLIER 100.0 struct { @@ -343,14 +343,14 @@ float AdcGetPh(uint32_t idx) { float m = (y2 - y1) / (x2 - x1); float ph = m * (adc - x1) + y1; - - + + char phLow_chr[6]; char phHigh_chr[6]; dtostrfd(y1, 2, phLow_chr); dtostrfd(y2, 2, phHigh_chr); - AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_APPLICATION "Analog pH read. ADC-RAW: %d, cal-low(pH=ADC): %s=%d, cal-high(pH=ADC): %s=%d"), adc, phLow_chr, x1, phHigh_chr,x2); - + AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_APPLICATION "Analog pH read. ADC-RAW: %d, cal-low(pH=ADC): %s=%d, cal-high(pH=ADC): %s=%d"), adc, phLow_chr, x1, phHigh_chr,x2); + return ph; } @@ -457,15 +457,12 @@ void AdcShow(bool json) { break; } case ADC_TEMP: { - char temperature[33]; - dtostrfd(Adc[idx].temperature, Settings.flag2.temperature_resolution, temperature); - if (json) { AdcShowContinuation(&jsonflg); - ResponseAppend_P(PSTR("\"" D_JSON_TEMPERATURE "%s\":%s"), adc_idx, temperature); + ResponseAppend_P(PSTR("\"" D_JSON_TEMPERATURE "%s\":%*_f"), adc_idx, Settings.flag2.temperature_resolution, &Adc[idx].temperature); if ((0 == TasmotaGlobal.tele_period) && (!domo_flag[ADC_TEMP])) { #ifdef USE_DOMOTICZ - DomoticzSensor(DZ_TEMP, temperature); + DomoticzFloatSensor(DZ_TEMP, Adc[idx].temperature); domo_flag[ADC_TEMP] = true; #endif // USE_DOMOTICZ #ifdef USE_KNX @@ -474,7 +471,7 @@ void AdcShow(bool json) { } #ifdef USE_WEBSERVER } else { - WSContentSend_PD(HTTP_SNS_TEMP, adc_name, temperature, TempUnit()); + WSContentSend_Temp(adc_name, Adc[idx].temperature); #endif // USE_WEBSERVER } break; @@ -560,7 +557,7 @@ void AdcShow(bool json) { char ph_chr[6]; dtostrfd(ph, 2, ph_chr); - + if (json) { AdcShowContinuation(&jsonflg); ResponseAppend_P(PSTR("\"pH%d\":%s"), idx + offset, ph_chr); @@ -594,8 +591,8 @@ void CmndAdcParam(void) { if (XdrvMailbox.data_len) { if (XdrvMailbox.payload > ADC_INPUT) { AdcGetSettings(idx); - if (ChrCount(XdrvMailbox.data, ",") > 2) { // Process parameter entry - char sub_string[XdrvMailbox.data_len +1]; + if (ArgC() > 3) { // Process parameter entry + char argument[XdrvMailbox.data_len]; // AdcParam 2, 32000, 10000, 3350 // AdcParam 3, 10000, 12518931, -1.405 // AdcParam 4, 128, 0, 0 @@ -604,29 +601,24 @@ void CmndAdcParam(void) { // AdcParam 7, 0, 2146, 0.23 // AdcParam 8, 1000, 0, 0 Adc[idx].type = XdrvMailbox.payload; - Adc[idx].param1 = strtol(subStr(sub_string, XdrvMailbox.data, ",", 2), nullptr, 10); - Adc[idx].param2 = strtol(subStr(sub_string, XdrvMailbox.data, ",", 3), nullptr, 10); + Adc[idx].param1 = strtol(ArgV(argument, 2), nullptr, 10); + Adc[idx].param2 = strtol(ArgV(argument, 3), nullptr, 10); if (ADC_RANGE == XdrvMailbox.payload) { - Adc[idx].param3 = abs(strtol(subStr(sub_string, XdrvMailbox.data, ",", 4), nullptr, 10)); - Adc[idx].param4 = abs(strtol(subStr(sub_string, XdrvMailbox.data, ",", 5), nullptr, 10)); + Adc[idx].param3 = abs(strtol(ArgV(argument, 4), nullptr, 10)); + Adc[idx].param4 = abs(strtol(ArgV(argument, 5), nullptr, 10)); } else { - Adc[idx].param3 = (int)(CharToFloat(subStr(sub_string, XdrvMailbox.data, ",", 4)) * 10000); + Adc[idx].param3 = (int)(CharToFloat(ArgV(argument, 4)) * 10000); } - if (ADC_PH == XdrvMailbox.payload) { - char *phLow_chr = subStr(sub_string, XdrvMailbox.data, ",", 2); - char *phHigh_chr = subStr(sub_string, XdrvMailbox.data, ",", 4); - float phLow = CharToFloat(phLow_chr); - float phHigh = CharToFloat(phHigh_chr); - + float phLow = CharToFloat(ArgV(argument, 2)); + float phHigh = CharToFloat(ArgV(argument, 4)); Adc[idx].param1 = phLow * ANALOG_PH_DECIMAL_MULTIPLIER; - Adc[idx].param2 = strtol(subStr(sub_string, XdrvMailbox.data, ",", 3), nullptr, 10); + Adc[idx].param2 = strtol(ArgV(argument, 3), nullptr, 10); Adc[idx].param3 = phHigh * ANALOG_PH_DECIMAL_MULTIPLIER; - Adc[idx].param4 = strtol(subStr(sub_string, XdrvMailbox.data, ",", 5), nullptr, 10); - - AddLog_P(LOG_LEVEL_INFO, PSTR(D_LOG_APPLICATION "Analog pH probe calibrated. cal-low(pH=ADC): %s=%d, cal-high(pH=ADC): %s=%d"), phLow_chr, Adc[idx].param2, phHigh_chr, Adc[idx].param4); + Adc[idx].param4 = strtol(ArgV(argument, 5), nullptr, 10); + AddLog(LOG_LEVEL_INFO, PSTR(D_LOG_APPLICATION "Analog pH probe calibrated. cal-low(pH=ADC) %2_f=%d, cal-high(pH=ADC) %2_f=%d"), + &phLow, Adc[idx].param2, &phHigh, Adc[idx].param4); } - if (ADC_CT_POWER == XdrvMailbox.payload) { if (((1 == Adc[idx].param1) & CT_FLAG_ENERGY_RESET) > 0) { for (uint32_t idx = 0; idx < MAX_ADCS; idx++) { diff --git a/tasmota/xsns_04_snfsc.ino b/tasmota/xsns_04_snfsc.ino index 38f693c30..545c39e7a 100644 --- a/tasmota/xsns_04_snfsc.ino +++ b/tasmota/xsns_04_snfsc.ino @@ -62,7 +62,7 @@ void SonoffScSend(const char *data) { Serial.write(data); Serial.write('\x1B'); - AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_SERIAL D_TRANSMIT " %s"), data); + AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_SERIAL D_TRANSMIT " %s"), data); } void SonoffScInit(void) @@ -78,7 +78,7 @@ void SonoffScSerialInput(char *rcvstat) char *str; uint16_t value[5] = { 0 }; - AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_SERIAL D_RECEIVED " %s"), rcvstat); + AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_SERIAL D_RECEIVED " %s"), rcvstat); if (!strncasecmp_P(rcvstat, PSTR("AT+UPDATE="), 10)) { int8_t i = -1; diff --git a/tasmota/xsns_05_ds18x20.ino b/tasmota/xsns_05_ds18x20.ino index eafe668de..f5cd6a3f9 100644 --- a/tasmota/xsns_05_ds18x20.ino +++ b/tasmota/xsns_05_ds18x20.ino @@ -344,7 +344,7 @@ void Ds18x20Init(void) } } } - AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_DSB D_SENSORS_FOUND " %d"), ds18x20_sensors); + AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_DSB D_SENSORS_FOUND " %d"), ds18x20_sensors); } void Ds18x20Convert(void) @@ -429,7 +429,7 @@ bool Ds18x20Read(uint8_t sensor) } } } - AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_DSB D_SENSOR_CRC_ERROR)); + AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_DSB D_SENSOR_CRC_ERROR)); return false; } @@ -499,9 +499,6 @@ void Ds18x20Show(bool json) uint8_t index = ds18x20_sensor[i].index; if (ds18x20_sensor[index].valid) { // Check for valid temperature - char temperature[33]; - dtostrfd(ds18x20_sensor[index].temperature, Settings.flag2.temperature_resolution, temperature); - Ds18x20Name(i); if (json) { @@ -509,10 +506,11 @@ void Ds18x20Show(bool json) for (uint32_t j = 0; j < 6; j++) { sprintf(address+2*j, "%02X", ds18x20_sensor[index].address[6-j]); // Skip sensor type and crc } - ResponseAppend_P(PSTR(",\"%s\":{\"" D_JSON_ID "\":\"%s\",\"" D_JSON_TEMPERATURE "\":%s}"), ds18x20_types, address, temperature); + ResponseAppend_P(PSTR(",\"%s\":{\"" D_JSON_ID "\":\"%s\",\"" D_JSON_TEMPERATURE "\":%*_f}"), + ds18x20_types, address, Settings.flag2.temperature_resolution, &ds18x20_sensor[index].temperature); #ifdef USE_DOMOTICZ if ((0 == TasmotaGlobal.tele_period) && (0 == i)) { - DomoticzSensor(DZ_TEMP, temperature); + DomoticzFloatSensor(DZ_TEMP, ds18x20_sensor[index].temperature); } #endif // USE_DOMOTICZ #ifdef USE_KNX @@ -522,7 +520,7 @@ void Ds18x20Show(bool json) #endif // USE_KNX #ifdef USE_WEBSERVER } else { - WSContentSend_PD(HTTP_SNS_TEMP, ds18x20_types, temperature, TempUnit()); + WSContentSend_Temp(ds18x20_types, ds18x20_sensor[index].temperature); #endif // USE_WEBSERVER } } diff --git a/tasmota/xsns_05_ds18x20_esp32.ino b/tasmota/xsns_05_ds18x20_esp32.ino index ec0226c9b..4f591b36a 100644 --- a/tasmota/xsns_05_ds18x20_esp32.ino +++ b/tasmota/xsns_05_ds18x20_esp32.ino @@ -58,7 +58,7 @@ void Ds18x20Init(void) { ds = new OneWire(Pin(GPIO_DSB)); Ds18x20Search(); - AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_DSB D_SENSORS_FOUND " %d"), ds18x20_sensors); + AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_DSB D_SENSORS_FOUND " %d"), ds18x20_sensors); } void Ds18x20Search(void) { @@ -144,7 +144,7 @@ bool Ds18x20Read(uint8_t sensor, float &t) } } } - AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_DSB D_SENSOR_CRC_ERROR)); + AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_DSB D_SENSOR_CRC_ERROR)); return false; } @@ -200,9 +200,6 @@ void Ds18x20Show(bool json) uint8_t dsxflg = 0; for (uint32_t i = 0; i < ds18x20_sensors; i++) { if (Ds18x20Read(i, t)) { // Check if read failed - char temperature[33]; - dtostrfd(t, Settings.flag2.temperature_resolution, temperature); - Ds18x20Name(i); if (json) { @@ -210,11 +207,12 @@ void Ds18x20Show(bool json) for (uint32_t j = 0; j < 6; j++) { sprintf(address+2*j, "%02X", ds18x20_address[ds18x20_index[i]][6-j]); // Skip sensor type and crc } - ResponseAppend_P(PSTR(",\"%s\":{\"" D_JSON_ID "\":\"%s\",\"" D_JSON_TEMPERATURE "\":%s}"), ds18x20_types, address, temperature); + ResponseAppend_P(PSTR(",\"%s\":{\"" D_JSON_ID "\":\"%s\",\"" D_JSON_TEMPERATURE "\":%*_f}"), + ds18x20_types, address, Settings.flag2.temperature_resolution, &t); dsxflg++; #ifdef USE_DOMOTICZ if ((0 == TasmotaGlobal.tele_period) && (1 == dsxflg)) { - DomoticzSensor(DZ_TEMP, temperature); + DomoticzFloatSensor(DZ_TEMP, t); } #endif // USE_DOMOTICZ #ifdef USE_KNX @@ -224,7 +222,7 @@ void Ds18x20Show(bool json) #endif // USE_KNX #ifdef USE_WEBSERVER } else { - WSContentSend_PD(HTTP_SNS_TEMP, ds18x20_types, temperature, TempUnit()); + WSContentSend_Temp(ds18x20_types, t); #endif // USE_WEBSERVER } } diff --git a/tasmota/xsns_06_dht.ino b/tasmota/xsns_06_dht.ino index 3ae2ecc9d..c9eec0955 100644 --- a/tasmota/xsns_06_dht.ino +++ b/tasmota/xsns_06_dht.ino @@ -52,7 +52,7 @@ bool DhtWaitState(uint32_t sensor, uint32_t level) unsigned long timeout = micros() + 100; while (digitalRead(Dht[sensor].pin) != level) { if (TimeReachedUsec(timeout)) { - AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_DHT D_TIMEOUT_WAITING_FOR " %s " D_PULSE), + AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_DHT D_TIMEOUT_WAITING_FOR " %s " D_PULSE), (level) ? D_START_SIGNAL_HIGH : D_START_SIGNAL_LOW); return false; } @@ -149,7 +149,7 @@ bool DhtRead(uint32_t sensor) uint8_t checksum = (dht_data[0] + dht_data[1] + dht_data[2] + dht_data[3]) & 0xFF; if (dht_data[4] != checksum) { char hex_char[15]; - AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_DHT D_CHECKSUM_FAILURE " %s =? %02X"), + AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_DHT D_CHECKSUM_FAILURE " %s =? %02X"), ToHex_P(dht_data, 5, hex_char, sizeof(hex_char), ' '), checksum); return false; } @@ -171,7 +171,7 @@ bool DhtRead(uint32_t sensor) break; } if (isnan(temperature) || isnan(humidity)) { - AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_DHT "Invalid NAN reading")); + AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_DHT "Invalid NAN reading")); return false; } @@ -220,7 +220,7 @@ void DhtInit(void) snprintf_P(Dht[i].stype, sizeof(Dht[i].stype), PSTR("%s%c%02d"), Dht[i].stype, IndexSeparator(), Dht[i].pin); } } - AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_DHT "(v5) " D_SENSORS_FOUND " %d"), dht_sensors); + AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_DHT "(v5) " D_SENSORS_FOUND " %d"), dht_sensors); } else { dht_active = false; } diff --git a/tasmota/xsns_07_sht1x.ino b/tasmota/xsns_07_sht1x.ino index ed7bf3a1c..7d3bc06e7 100644 --- a/tasmota/xsns_07_sht1x.ino +++ b/tasmota/xsns_07_sht1x.ino @@ -86,7 +86,7 @@ bool ShtSendCommand(const uint8_t cmd) } if (ackerror) { // sht_type = 0; - AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_SHT1 D_SENSOR_DID_NOT_ACK_COMMAND)); + AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_SHT1 D_SENSOR_DID_NOT_ACK_COMMAND)); } return (!ackerror); } @@ -100,7 +100,7 @@ bool ShtAwaitResult(void) } delay(20); } - AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_SHT1 D_SENSOR_BUSY)); + AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_SHT1 D_SENSOR_BUSY)); // sht_type = 0; return false; } @@ -163,7 +163,7 @@ void ShtDetect(void) sht_scl_pin = Pin(GPIO_I2C_SCL); if (ShtRead()) { sht_type = 1; - AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_I2C D_SHT1X_FOUND)); + AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_I2C D_SHT1X_FOUND)); } else { Wire.begin(sht_sda_pin, sht_scl_pin); sht_type = 0; diff --git a/tasmota/xsns_09_bmp.ino b/tasmota/xsns_09_bmp.ino index b30615107..3b472dceb 100644 --- a/tasmota/xsns_09_bmp.ino +++ b/tasmota/xsns_09_bmp.ino @@ -527,8 +527,6 @@ void BmpShow(bool json) snprintf_P(name, sizeof(name), PSTR("%s%c%02X"), name, IndexSeparator(), bmp_sensors[bmp_idx].bmp_address); // BMXXXX-XX } - char temperature[33]; - dtostrfd(bmp_temperature, Settings.flag2.temperature_resolution, temperature); char pressure[33]; dtostrfd(bmp_pressure, Settings.flag2.pressure_resolution, pressure); char sea_pressure[33]; @@ -554,16 +552,16 @@ void BmpShow(bool json) char json_gas[40]; snprintf_P(json_gas, sizeof(json_gas), PSTR(",\"" D_JSON_GAS "\":%s"), gas_resistance); - ResponseAppend_P(PSTR(",\"%s\":{\"" D_JSON_TEMPERATURE "\":%s%s,\"" D_JSON_PRESSURE "\":%s%s%s}"), + ResponseAppend_P(PSTR(",\"%s\":{\"" D_JSON_TEMPERATURE "\":%*_f%s,\"" D_JSON_PRESSURE "\":%s%s%s}"), name, - temperature, + Settings.flag2.temperature_resolution, &bmp_temperature, (bmp_sensors[bmp_idx].bmp_model >= 2) ? json_humidity : "", pressure, (Settings.altitude != 0) ? json_sealevel : "", (bmp_sensors[bmp_idx].bmp_model >= 3) ? json_gas : ""); #else - ResponseAppend_P(PSTR(",\"%s\":{\"" D_JSON_TEMPERATURE "\":%s%s,\"" D_JSON_PRESSURE "\":%s%s}"), - name, temperature, (bmp_sensors[bmp_idx].bmp_model >= 2) ? json_humidity : "", pressure, (Settings.altitude != 0) ? json_sealevel : ""); + ResponseAppend_P(PSTR(",\"%s\":{\"" D_JSON_TEMPERATURE "\":%*_f%s,\"" D_JSON_PRESSURE "\":%s%s}"), + name, Settings.flag2.temperature_resolution, &bmp_temperature, (bmp_sensors[bmp_idx].bmp_model >= 2) ? json_humidity : "", pressure, (Settings.altitude != 0) ? json_sealevel : ""); #endif // USE_BME680 #ifdef USE_DOMOTICZ @@ -584,7 +582,7 @@ void BmpShow(bool json) #ifdef USE_WEBSERVER } else { - WSContentSend_PD(HTTP_SNS_TEMP, name, temperature, TempUnit()); + WSContentSend_Temp(name, bmp_temperature); if (bmp_sensors[bmp_idx].bmp_model >= 2) { WSContentSend_PD(HTTP_SNS_HUM, name, humidity); WSContentSend_PD(HTTP_SNS_DEW, name, dewpoint, TempUnit()); diff --git a/tasmota/xsns_11_veml6070.ino b/tasmota/xsns_11_veml6070.ino index d49b229d1..83f889b09 100644 --- a/tasmota/xsns_11_veml6070.ino +++ b/tasmota/xsns_11_veml6070.ino @@ -155,11 +155,11 @@ void Veml6070UvTableInit(void) uv_risk_map[i] = ( (USE_VEML6070_RSET / VEML6070_TABLE_COEFFCIENT) / VEML6070_UV_MAX_DEFAULT ) * (i+1); } else { uv_risk_map[i] = ( (VEML6070_RSET_DEFAULT / VEML6070_TABLE_COEFFCIENT) / VEML6070_UV_MAX_DEFAULT ) * (i+1); - AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "VEML6070 resistor error %d"), USE_VEML6070_RSET); + AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "VEML6070 resistor error %d"), USE_VEML6070_RSET); } #else uv_risk_map[i] = ( (VEML6070_RSET_DEFAULT / VEML6070_TABLE_COEFFCIENT) / VEML6070_UV_MAX_DEFAULT ) * (i+1); - AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "VEML6070 resistor default used %d"), VEML6070_RSET_DEFAULT); + AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "VEML6070 resistor default used %d"), VEML6070_RSET_DEFAULT); #endif } } @@ -187,7 +187,7 @@ void Veml6070ModeCmd(bool mode_cmd) uint8_t status = Wire.endTransmission(); // action on status if (!status) { - AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "VEML6070 mode_cmd")); + AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "VEML6070 mode_cmd")); } } @@ -231,7 +231,7 @@ double Veml6070UvRiskLevel(uint16_t uv_level) // out of range and much to high - it must be outerspace or sensor damaged snprintf_P(str_uvrisk_text, sizeof(str_uvrisk_text), D_UV_INDEX_7); return ( risk = 99 ); - AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "VEML6070 out of range %d"), risk); + AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "VEML6070 out of range %d"), risk); } } diff --git a/tasmota/xsns_12_ads1115.ino b/tasmota/xsns_12_ads1115.ino index 6afb1b028..2fb7d259e 100644 --- a/tasmota/xsns_12_ads1115.ino +++ b/tasmota/xsns_12_ads1115.ino @@ -245,14 +245,14 @@ void Ads1115Show(bool json) int16_t values[4]; for (uint32_t t = 0; t < sizeof(Ads1115.addresses); t++) { - //AddLog_P(LOG_LEVEL_INFO, "Logging ADS1115 %02x", Ads1115.addresses[t]); + //AddLog(LOG_LEVEL_INFO, "Logging ADS1115 %02x", Ads1115.addresses[t]); if (Ads1115.found[t]) { uint8_t old_address = Ads1115.address; Ads1115.address = Ads1115.addresses[t]; for (uint32_t i = 0; i < 4; i++) { values[i] = Ads1115GetConversion(i); - //AddLog_P(LOG_LEVEL_INFO, "Logging ADS1115 %02x (%i) = %i", Ads1115.address, i, values[i] ); + //AddLog(LOG_LEVEL_INFO, "Logging ADS1115 %02x (%i) = %i", Ads1115.address, i, values[i] ); } Ads1115.address = old_address; diff --git a/tasmota/xsns_15_mhz19.ino b/tasmota/xsns_15_mhz19.ino index a2afb267f..1536fbd25 100644 --- a/tasmota/xsns_15_mhz19.ino +++ b/tasmota/xsns_15_mhz19.ino @@ -74,7 +74,7 @@ const char kMhzModels[] PROGMEM = "|B"; const char ABC_ENABLED[] = "ABC is Enabled"; const char ABC_DISABLED[] = "ABC is Disabled"; -enum MhzCommands { MHZ_CMND_READPPM, MHZ_CMND_ABCENABLE, MHZ_CMND_ABCDISABLE, MHZ_CMND_ZEROPOINT, MHZ_CMND_RESET, MHZ_CMND_RANGE_1000, MHZ_CMND_RANGE_2000, MHZ_CMND_RANGE_3000, MHZ_CMND_RANGE_5000, MHZ_CMND_RANGE_10000 }; +enum MhzCommands { MHZ_CMND_READPPM, MHZ_CMND_ABCENABLE, MHZ_CMND_ABCDISABLE, MHZ_CMND_ZEROPOINT, MHZ_CMND_RESET, MHZ_CMND_RANGE_1000, MHZ_CMND_RANGE_2000, MHZ_CMND_RANGE_3000, MHZ_CMND_RANGE_5000 }; const uint8_t kMhzCommands[][4] PROGMEM = { // 2 3 6 7 {0x86,0x00,0x00,0x00}, // mhz_cmnd_read_ppm @@ -85,8 +85,7 @@ const uint8_t kMhzCommands[][4] PROGMEM = { {0x99,0x00,0x03,0xE8}, // mhz_cmnd_set_range_1000 {0x99,0x00,0x07,0xD0}, // mhz_cmnd_set_range_2000 {0x99,0x00,0x0B,0xB8}, // mhz_cmnd_set_range_3000 - {0x99,0x00,0x13,0x88}, // mhz_cmnd_set_range_5000 - {0x99,0x00,0x27,0x10}}; // mhz_cmnd_set_range_10000 + {0x99,0x00,0x13,0x88}}; // mhz_cmnd_set_range_5000 uint8_t mhz_type = 1; uint16_t mhz_last_ppm = 0; @@ -136,7 +135,7 @@ bool MhzCheckAndApplyFilter(uint16_t ppm, uint8_t s) if (1 == s) { return false; // S==1 => "A" version sensor bootup, do not use values. } - if (mhz_last_ppm < 400 || mhz_last_ppm > 10000) { + if (mhz_last_ppm < 400 || mhz_last_ppm > 5000) { // Prevent unrealistic values during start-up with filtering enabled. // Just assume the entered value is correct. mhz_last_ppm = ppm; @@ -201,17 +200,17 @@ void MhzEverySecond(void) AddLogBuffer(LOG_LEVEL_DEBUG_MORE, mhz_response, counter); if (counter < 9) { -// AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "MH-Z19 comms timeout")); +// AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "MH-Z19 comms timeout")); return; } uint8_t crc = MhzCalculateChecksum(mhz_response); if (mhz_response[8] != crc) { -// AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "MH-Z19 crc error")); +// AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "MH-Z19 crc error")); return; } if (0xFF != mhz_response[0] || 0x86 != mhz_response[1]) { -// AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "MH-Z19 bad response")); +// AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "MH-Z19 bad response")); return; } @@ -270,7 +269,6 @@ void MhzEverySecond(void) #define D_JSON_RANGE_2000 "2000 ppm range" #define D_JSON_RANGE_3000 "3000 ppm range" #define D_JSON_RANGE_5000 "5000 ppm range" -#define D_JSON_RANGE_10000 "10000 ppm range" bool MhzCommandSensor(void) { @@ -311,10 +309,6 @@ bool MhzCommandSensor(void) MhzSendCmd(MHZ_CMND_RANGE_5000); Response_P(S_JSON_SENSOR_INDEX_SVALUE, XSNS_15, D_JSON_RANGE_5000); break; - case 10000: - MhzSendCmd(MHZ_CMND_RANGE_10000); - Response_P(S_JSON_SENSOR_INDEX_SVALUE, XSNS_15, D_JSON_RANGE_10000); - break; default: if (!Settings.SensorBits1.mhz19b_abc_disable) { Response_P(S_JSON_SENSOR_INDEX_SVALUE, XSNS_15, ABC_ENABLED); @@ -344,23 +338,22 @@ void MhzInit(void) void MhzShow(bool json) { char types[7] = "MHZ19B"; // MHZ19B for legacy reasons. Prefered is MHZ19 - char temperature[33]; - dtostrfd(mhz_temperature, Settings.flag2.temperature_resolution, temperature); char model[3]; GetTextIndexed(model, sizeof(model), mhz_type -1, kMhzModels); if (json) { - ResponseAppend_P(PSTR(",\"%s\":{\"" D_JSON_MODEL "\":\"%s\",\"" D_JSON_CO2 "\":%d,\"" D_JSON_TEMPERATURE "\":%s}"), types, model, mhz_last_ppm, temperature); + ResponseAppend_P(PSTR(",\"%s\":{\"" D_JSON_MODEL "\":\"%s\",\"" D_JSON_CO2 "\":%d,\"" D_JSON_TEMPERATURE "\":%*_f}"), + types, model, mhz_last_ppm, Settings.flag2.temperature_resolution, &mhz_temperature); #ifdef USE_DOMOTICZ if (0 == TasmotaGlobal.tele_period) { DomoticzSensor(DZ_AIRQUALITY, mhz_last_ppm); - DomoticzSensor(DZ_TEMP, temperature); + DomoticzFloatSensor(DZ_TEMP, mhz_temperature); } #endif // USE_DOMOTICZ #ifdef USE_WEBSERVER } else { WSContentSend_PD(HTTP_SNS_CO2, types, mhz_last_ppm); - WSContentSend_PD(HTTP_SNS_TEMP, types, temperature, TempUnit()); + WSContentSend_Temp(types, mhz_temperature); #endif // USE_WEBSERVER } } diff --git a/tasmota/xsns_17_senseair.ino b/tasmota/xsns_17_senseair.ino index 8d705648b..ea6ba683b 100644 --- a/tasmota/xsns_17_senseair.ino +++ b/tasmota/xsns_17_senseair.ino @@ -70,16 +70,16 @@ void Senseair250ms(void) // Every 250 mSec if (data_ready) { uint8_t error = SenseairModbus->Receive16BitRegister(&value); if (error) { - AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "SenseAir response error %d"), error); + AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "SenseAir response error %d"), error); } else { switch(senseair_read_state) { case 0: // 0x1A (26) READ_TYPE_LOW - S8: fe 04 02 01 77 ec 92 senseair_type = 2; - AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "SenseAir type id low %04X"), value); + AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "SenseAir type id low %04X"), value); break; case 1: // 0x00 (0) READ_ERRORLOG - fe 04 02 00 00 ad 24 if (value) { - AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "SenseAir error %04X"), value); + AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "SenseAir error %04X"), value); } break; case 2: // 0x03 (3) READ_CO2 - fe 04 02 06 2c af 59 @@ -97,11 +97,11 @@ void Senseair250ms(void) // Every 250 mSec case 5: // 0x1C (28) READ_RELAY_STATE - S8: fe 04 02 01 54 ad 4b - firmware version { bool relay_state = value >> 8 & 1; - AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "SenseAir relay state %d"), relay_state); + AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "SenseAir relay state %d"), relay_state); break; } case 6: // 0x0A (10) READ_TEMP_ADJUSTMENT - S8: fe 84 02 f2 f1 - Illegal Data Address - AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "SenseAir temp adjustment %d"), value); + AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "SenseAir temp adjustment %d"), value); break; } } diff --git a/tasmota/xsns_18_pms5003.ino b/tasmota/xsns_18_pms5003.ino index 1126aeb95..d9b37e80a 100644 --- a/tasmota/xsns_18_pms5003.ino +++ b/tasmota/xsns_18_pms5003.ino @@ -98,7 +98,7 @@ bool PmsReadData(void) PmsSerial->read(); } #ifdef PMS_MODEL_PMS3003 - if (PmsSerial->available() < 22) { + if (PmsSerial->available() < 24) { #else if (PmsSerial->available() < 32) { #endif // PMS_MODEL_PMS3003 @@ -106,8 +106,8 @@ bool PmsReadData(void) } #ifdef PMS_MODEL_PMS3003 - uint8_t buffer[22]; - PmsSerial->readBytes(buffer, 22); + uint8_t buffer[24]; + PmsSerial->readBytes(buffer, 24); #else uint8_t buffer[32]; PmsSerial->readBytes(buffer, 32); @@ -116,14 +116,14 @@ bool PmsReadData(void) PmsSerial->flush(); // Make room for another burst #ifdef PMS_MODEL_PMS3003 - AddLogBuffer(LOG_LEVEL_DEBUG_MORE, buffer, 22); + AddLogBuffer(LOG_LEVEL_DEBUG_MORE, buffer, 24); #else AddLogBuffer(LOG_LEVEL_DEBUG_MORE, buffer, 32); #endif // PMS_MODEL_PMS3003 // get checksum ready #ifdef PMS_MODEL_PMS3003 - for (uint32_t i = 0; i < 20; i++) { + for (uint32_t i = 0; i < 22; i++) { #else for (uint32_t i = 0; i < 30; i++) { #endif // PMS_MODEL_PMS3003 @@ -131,8 +131,8 @@ bool PmsReadData(void) } // The data comes in endian'd, this solves it so it works on all platforms #ifdef PMS_MODEL_PMS3003 - uint16_t buffer_u16[10]; - for (uint32_t i = 0; i < 10; i++) { + uint16_t buffer_u16[12]; + for (uint32_t i = 0; i < 12; i++) { #else uint16_t buffer_u16[15]; for (uint32_t i = 0; i < 15; i++) { @@ -141,16 +141,16 @@ bool PmsReadData(void) buffer_u16[i] += (buffer[2 + i*2] << 8); } #ifdef PMS_MODEL_PMS3003 - if (sum != buffer_u16[9]) { + if (sum != buffer_u16[10]) { #else if (sum != buffer_u16[14]) { #endif // PMS_MODEL_PMS3003 - AddLog_P(LOG_LEVEL_DEBUG, PSTR("PMS: " D_CHECKSUM_FAILURE)); + AddLog(LOG_LEVEL_DEBUG, PSTR("PMS: " D_CHECKSUM_FAILURE)); return false; } #ifdef PMS_MODEL_PMS3003 - memcpy((void *)&pms_data, (void *)buffer_u16, 20); + memcpy((void *)&pms_data, (void *)buffer_u16, 22); #else memcpy((void *)&pms_data, (void *)buffer_u16, 30); #endif // PMS_MODEL_PMS3003 diff --git a/tasmota/xsns_20_novasds.ino b/tasmota/xsns_20_novasds.ino index 8263f8a65..6f036c743 100644 --- a/tasmota/xsns_20_novasds.ino +++ b/tasmota/xsns_20_novasds.ino @@ -82,7 +82,7 @@ bool NovaSdsCommand(uint8_t byte1, uint8_t byte2, uint8_t byte3, uint16_t sensor } // char hex_char[60]; -// AddLog_P(LOG_LEVEL_DEBUG, PSTR("SDS: Send %s"), ToHex_P((unsigned char*)novasds_cmnd, 19, hex_char, sizeof(hex_char), ' ')); +// AddLog(LOG_LEVEL_DEBUG, PSTR("SDS: Send %s"), ToHex_P((unsigned char*)novasds_cmnd, 19, hex_char, sizeof(hex_char), ' ')); // send cmnd NovaSdsSerial->write(novasds_cmnd, sizeof(novasds_cmnd)); @@ -115,7 +115,7 @@ bool NovaSdsCommand(uint8_t byte1, uint8_t byte2, uint8_t byte3, uint16_t sensor // checksum & tail check if ((0xAB != recbuf[9] ) || (recbuf[8] != ((recbuf[2] + recbuf[3] + recbuf[4] + recbuf[5] + recbuf[6] + recbuf[7]) & 0xFF))) { - AddLog_P(LOG_LEVEL_DEBUG, PSTR("SDS: " D_CHECKSUM_FAILURE)); + AddLog(LOG_LEVEL_DEBUG, PSTR("SDS: " D_CHECKSUM_FAILURE)); return false; } diff --git a/tasmota/xsns_21_sgp30.ino b/tasmota/xsns_21_sgp30.ino index 11059d5d4..73b6e2d78 100644 --- a/tasmota/xsns_21_sgp30.ino +++ b/tasmota/xsns_21_sgp30.ino @@ -47,7 +47,7 @@ void sgp30_Init(void) if (sgp.begin()) { sgp30_type = true; -// AddLog_P(LOG_LEVEL_DEBUG, PSTR("SGP: Serialnumber 0x%04X-0x%04X-0x%04X"), sgp.serialnumber[0], sgp.serialnumber[1], sgp.serialnumber[2]); +// AddLog(LOG_LEVEL_DEBUG, PSTR("SGP: Serialnumber 0x%04X-0x%04X-0x%04X"), sgp.serialnumber[0], sgp.serialnumber[1], sgp.serialnumber[2]); I2cSetActiveFound(SGP30_ADDRESS, "SGP30"); } } @@ -97,7 +97,7 @@ void Sgp30Update(void) // Perform every second to ensure proper operation of th uint16_t eCO2_base; if (!sgp.getIAQBaseline(&eCO2_base, &TVOC_base)) return; // Failed to get baseline readings -// AddLog_P(LOG_LEVEL_DEBUG, PSTR("SGP: Baseline values eCO2 0x%04X, TVOC 0x%04X"), eCO2_base, TVOC_base); +// AddLog(LOG_LEVEL_DEBUG, PSTR("SGP: Baseline values eCO2 0x%04X, TVOC 0x%04X"), eCO2_base, TVOC_base); } } diff --git a/tasmota/xsns_22_sr04.ino b/tasmota/xsns_22_sr04.ino index 3a075e1e1..f2659f294 100644 --- a/tasmota/xsns_22_sr04.ino +++ b/tasmota/xsns_22_sr04.ino @@ -71,7 +71,7 @@ uint8_t Sr04TModeDetect(void) } } - AddLog_P(LOG_LEVEL_INFO,PSTR("SR04: Mode %d"), sr04_type); + AddLog(LOG_LEVEL_INFO,PSTR("SR04: Mode %d"), sr04_type); return sr04_type; } @@ -123,7 +123,7 @@ uint16_t Sr04TMode2Distance(void) //check crc sum if (crc != sonar_serial->read()) { - AddLog_P(LOG_LEVEL_ERROR,PSTR("SR04: Reading CRC error.")); + AddLog(LOG_LEVEL_ERROR,PSTR("SR04: Reading CRC error.")); return NO_ECHO; } //DEBUG_SENSOR_LOG(PSTR("SR04: Distance: %d"), distance); @@ -156,7 +156,7 @@ void Sr04TReading(void) { } #ifdef USE_WEBSERVER -const char HTTP_SNS_DISTANCE[] PROGMEM = +const char HTTP_SNS_DISTANCE_CM[] PROGMEM = "{s}SR04 " D_DISTANCE "{m}%s" D_UNIT_CENTIMETER "{e}"; // {s} = , {m} = , {e} = #endif // USE_WEBSERVER @@ -176,7 +176,7 @@ void Sr04Show(bool json) #endif // USE_DOMOTICZ #ifdef USE_WEBSERVER } else { - WSContentSend_PD(HTTP_SNS_DISTANCE, distance_chr); + WSContentSend_PD(HTTP_SNS_DISTANCE_CM, distance_chr); #endif // USE_WEBSERVER } } diff --git a/tasmota/xsns_26_lm75ad.ino b/tasmota/xsns_26_lm75ad.ino index 4f65f3d51..73c6140cc 100644 --- a/tasmota/xsns_26_lm75ad.ino +++ b/tasmota/xsns_26_lm75ad.ino @@ -85,17 +85,15 @@ float LM75ADGetTemp(void) void LM75ADShow(bool json) { float t = LM75ADGetTemp(); - char temperature[33]; - dtostrfd(t, Settings.flag2.temperature_resolution, temperature); if (json) { - ResponseAppend_P(JSON_SNS_TEMP, "LM75AD", temperature); + ResponseAppend_P(JSON_SNS_F_TEMP, "LM75AD", Settings.flag2.temperature_resolution, &t); #ifdef USE_DOMOTICZ - if (0 == TasmotaGlobal.tele_period) DomoticzSensor(DZ_TEMP, temperature); + if (0 == TasmotaGlobal.tele_period) DomoticzFloatSensor(DZ_TEMP, t); #endif // USE_DOMOTICZ #ifdef USE_WEBSERVER } else { - WSContentSend_PD(HTTP_SNS_TEMP, "LM75AD", temperature, TempUnit()); + WSContentSend_Temp("LM75AD", t); #endif // USE_WEBSERVER } } diff --git a/tasmota/xsns_27_apds9960.ino b/tasmota/xsns_27_apds9960.ino index 2489329ac..2138015fd 100644 --- a/tasmota/xsns_27_apds9960.ino +++ b/tasmota/xsns_27_apds9960.ino @@ -1437,7 +1437,7 @@ int16_t readGesture(void) { if (gesture_loop_counter == APDS9960_MAX_GESTURE_CYCLES) { // We will escape after a few loops disableGestureSensor(); // stop the sensor to prevent problems with power consumption/blocking and return to the main loop APDS9960_overload = true; // we report this as "long"-gesture - AddLog_P(LOG_LEVEL_DEBUG, PSTR("Sensor overload")); + AddLog(LOG_LEVEL_DEBUG, PSTR("Sensor overload")); } gesture_loop_counter += 1; @@ -1453,7 +1453,7 @@ int16_t readGesture(void) { fifo_level = I2cRead8(APDS9960_I2C_ADDR, APDS9960_GFLVL); #ifdef USE_DEBUG_DRIVER - AddLog_P(LOG_LEVEL_DEBUG, PSTR("DRV: FIFO Level : %d"), fifo_level); + AddLog(LOG_LEVEL_DEBUG, PSTR("DRV: FIFO Level : %d"), fifo_level); #endif // USE_DEBUG_DRIVER /* If there's stuff in the FIFO, read it into our data block */ @@ -1471,7 +1471,7 @@ int16_t readGesture(void) { for ( i = 0; i < bytes_read; i++ ) { ptr += sprintf(ptr, "%02X", fifo_data[i]); } - AddLog_P(LOG_LEVEL_DEBUG, PSTR("DRV: FIFO Dump : %s"), output); + AddLog(LOG_LEVEL_DEBUG, PSTR("DRV: FIFO Dump : %s"), output); #endif // USE_DEBUG_DRIVER /* If at least 1 set of data, sort the data into U/D/L/R */ @@ -1745,27 +1745,27 @@ void handleGesture(void) { if (isGestureAvailable()) { switch (readGesture()) { case DIR_UP: - AddLog_P(LOG_LEVEL_DEBUG, GESTURE_UP); + AddLog(LOG_LEVEL_DEBUG, GESTURE_UP); snprintf_P(currentGesture, sizeof(currentGesture), GESTURE_UP); break; case DIR_DOWN: - AddLog_P(LOG_LEVEL_DEBUG, GESTURE_DOWN); + AddLog(LOG_LEVEL_DEBUG, GESTURE_DOWN); snprintf_P(currentGesture, sizeof(currentGesture), GESTURE_DOWN); break; case DIR_LEFT: - AddLog_P(LOG_LEVEL_DEBUG, GESTURE_LEFT); + AddLog(LOG_LEVEL_DEBUG, GESTURE_LEFT); snprintf_P(currentGesture, sizeof(currentGesture), GESTURE_LEFT); break; case DIR_RIGHT: - AddLog_P(LOG_LEVEL_DEBUG, GESTURE_RIGHT); + AddLog(LOG_LEVEL_DEBUG, GESTURE_RIGHT); snprintf_P(currentGesture, sizeof(currentGesture), GESTURE_RIGHT); break; default: if (APDS9960_overload) { - AddLog_P(LOG_LEVEL_DEBUG, GESTURE_LONG); + AddLog(LOG_LEVEL_DEBUG, GESTURE_LONG); snprintf_P(currentGesture, sizeof(currentGesture), GESTURE_LONG); } else { - AddLog_P(LOG_LEVEL_DEBUG, GESTURE_NONE); + AddLog(LOG_LEVEL_DEBUG, GESTURE_NONE); snprintf_P(currentGesture, sizeof(currentGesture), GESTURE_NONE); } break; @@ -1812,7 +1812,7 @@ void APDS9960_detect(void) { #ifdef USE_DEBUG_DRIVER // Debug new chip - AddLog_P(LOG_LEVEL_DEBUG, PSTR("DRV: %s Chip %X"), APDS9960_TAG, APDS9960_type); + AddLog(LOG_LEVEL_DEBUG, PSTR("DRV: %s Chip %X"), APDS9960_TAG, APDS9960_type); #endif // USE_DEBUG_DRIVER if (APDS9960_type == APDS9960_CHIPID_1 || APDS9960_type == APDS9960_CHIPID_2 || APDS9960_type == APDS9960_CHIPID_3) { diff --git a/tasmota/xsns_29_mcp230xx.ino b/tasmota/xsns_29_mcp230xx.ino index 56427278d..b450d63d7 100644 --- a/tasmota/xsns_29_mcp230xx.ino +++ b/tasmota/xsns_29_mcp230xx.ino @@ -451,25 +451,25 @@ bool MCP230xx_Command(void) serviced = false; return serviced; } - char sub_string[XdrvMailbox.data_len]; + char argument[XdrvMailbox.data_len]; for (uint32_t ca=0;ca 1) { - uint8_t intpri = atoi(subStr(sub_string, XdrvMailbox.data, ",", 2)); + uint8_t intpri = atoi(ArgV(argument, 2)); if ((intpri >= 0) && (intpri <= 20)) { Settings.mcp230xx_int_prio = intpri; Response_P(MCP230XX_INTCFG_RESPONSE,"PRI",99,Settings.mcp230xx_int_prio); // "{\"MCP230xx_INT%s\":{\"D_%i\":%i}}"; @@ -481,9 +481,9 @@ bool MCP230xx_Command(void) } } - if (!strcmp(subStr(sub_string, XdrvMailbox.data, ",", 1),"INTTIMER")) { + if (!strcmp(ArgV(argument, 1),"INTTIMER")) { if (paramcount > 1) { - uint8_t inttim = atoi(subStr(sub_string, XdrvMailbox.data, ",", 2)); + uint8_t inttim = atoi(ArgV(argument, 2)); if ((inttim >= 0) && (inttim <= 3600)) { Settings.mcp230xx_int_timer = inttim; MCP230xx_CheckForIntCounter(); // update register on whether or not we should be counting interrupts @@ -496,25 +496,25 @@ bool MCP230xx_Command(void) } } - if (!strcmp(subStr(sub_string, XdrvMailbox.data, ",", 1),"INTDEF")) { + if (!strcmp(ArgV(argument, 1),"INTDEF")) { if (paramcount > 1) { - uint8_t pin = atoi(subStr(sub_string, XdrvMailbox.data, ",", 2)); + uint8_t pin = atoi(ArgV(argument, 2)); if (pin < mcp230xx_pincount) { if (pin == 0) { - if (!strcmp(subStr(sub_string, XdrvMailbox.data, ",", 2), "0")) validpin=true; + if (!strcmp(ArgV(argument, 2), "0")) validpin=true; } else { validpin = true; } } if (validpin) { if (paramcount > 2) { - uint8_t intdef = atoi(subStr(sub_string, XdrvMailbox.data, ",", 3)); + uint8_t intdef = atoi(ArgV(argument, 3)); if ((intdef >= 0) && (intdef <= 15)) { Settings.mcp230xx_config[pin].int_report_defer=intdef; if (Settings.mcp230xx_config[pin].int_count_en) { Settings.mcp230xx_config[pin].int_count_en=0; MCP230xx_CheckForIntCounter(); - AddLog_P(LOG_LEVEL_INFO, PSTR("*** WARNING *** - Disabled INTCNT for pin D%i"),pin); + AddLog(LOG_LEVEL_INFO, PSTR("*** WARNING *** - Disabled INTCNT for pin D%i"),pin); } Response_P(MCP230XX_INTCFG_RESPONSE,"DEF",pin,Settings.mcp230xx_config[pin].int_report_defer); // "{\"MCP230xx_INT%s\":{\"D_%i\":%i}}"; return serviced; @@ -535,31 +535,31 @@ bool MCP230xx_Command(void) } } - if (!strcmp(subStr(sub_string, XdrvMailbox.data, ",", 1),"INTCNT")) { + if (!strcmp(ArgV(argument, 1),"INTCNT")) { if (paramcount > 1) { - uint8_t pin = atoi(subStr(sub_string, XdrvMailbox.data, ",", 2)); + uint8_t pin = atoi(ArgV(argument, 2)); if (pin < mcp230xx_pincount) { if (pin == 0) { - if (!strcmp(subStr(sub_string, XdrvMailbox.data, ",", 2), "0")) validpin=true; + if (!strcmp(ArgV(argument, 2), "0")) validpin=true; } else { validpin = true; } } if (validpin) { if (paramcount > 2) { - uint8_t intcnt = atoi(subStr(sub_string, XdrvMailbox.data, ",", 3)); + uint8_t intcnt = atoi(ArgV(argument, 3)); if ((intcnt >= 0) && (intcnt <= 1)) { Settings.mcp230xx_config[pin].int_count_en=intcnt; if (Settings.mcp230xx_config[pin].int_report_defer) { Settings.mcp230xx_config[pin].int_report_defer=0; - AddLog_P(LOG_LEVEL_INFO, PSTR("*** WARNING *** - Disabled INTDEF for pin D%i"),pin); + AddLog(LOG_LEVEL_INFO, PSTR("*** WARNING *** - Disabled INTDEF for pin D%i"),pin); } if (Settings.mcp230xx_config[pin].int_report_mode < 3) { Settings.mcp230xx_config[pin].int_report_mode=3; - AddLog_P(LOG_LEVEL_INFO, PSTR("*** WARNING *** - Disabled immediate interrupt/telemetry reporting for pin D%i"),pin); + AddLog(LOG_LEVEL_INFO, PSTR("*** WARNING *** - Disabled immediate interrupt/telemetry reporting for pin D%i"),pin); } if ((Settings.mcp230xx_config[pin].int_count_en) && (!Settings.mcp230xx_int_timer)) { - AddLog_P(LOG_LEVEL_INFO, PSTR("*** WARNING *** - INTCNT enabled for pin D%i but global INTTIMER is disabled!"),pin); + AddLog(LOG_LEVEL_INFO, PSTR("*** WARNING *** - INTCNT enabled for pin D%i but global INTTIMER is disabled!"),pin); } MCP230xx_CheckForIntCounter(); // update register on whether or not we should be counting interrupts Response_P(MCP230XX_INTCFG_RESPONSE,"CNT",pin,Settings.mcp230xx_config[pin].int_count_en); // "{\"MCP230xx_INT%s\":{\"D_%i\":%i}}"; @@ -581,19 +581,19 @@ bool MCP230xx_Command(void) } } - if (!strcmp(subStr(sub_string, XdrvMailbox.data, ",", 1),"INTRETAIN")) { + if (!strcmp(ArgV(argument, 1),"INTRETAIN")) { if (paramcount > 1) { - uint8_t pin = atoi(subStr(sub_string, XdrvMailbox.data, ",", 2)); + uint8_t pin = atoi(ArgV(argument, 2)); if (pin < mcp230xx_pincount) { if (pin == 0) { - if (!strcmp(subStr(sub_string, XdrvMailbox.data, ",", 2), "0")) validpin=true; + if (!strcmp(ArgV(argument, 2), "0")) validpin=true; } else { validpin = true; } } if (validpin) { if (paramcount > 2) { - uint8_t int_retain = atoi(subStr(sub_string, XdrvMailbox.data, ",", 3)); + uint8_t int_retain = atoi(ArgV(argument, 3)); if ((int_retain >= 0) && (int_retain <= 1)) { Settings.mcp230xx_config[pin].int_retain_flag=int_retain; Response_P(MCP230XX_INTCFG_RESPONSE,"INT_RETAIN",pin,Settings.mcp230xx_config[pin].int_retain_flag); @@ -616,17 +616,17 @@ bool MCP230xx_Command(void) } } - uint8_t pin = atoi(subStr(sub_string, XdrvMailbox.data, ",", 1)); + uint8_t pin = atoi(ArgV(argument, 1)); if (pin < mcp230xx_pincount) { if (0 == pin) { - if (!strcmp(subStr(sub_string, XdrvMailbox.data, ",", 1), "0")) validpin=true; + if (!strcmp(ArgV(argument, 1), "0")) validpin=true; } else { validpin=true; } } if (validpin && (paramcount > 1)) { - if (!strcmp(subStr(sub_string, XdrvMailbox.data, ",", 2), "?")) { + if (!strcmp(ArgV(argument, 2), "?")) { uint8_t port = 0; if (pin > 7) { port = 1; } uint8_t portdata = MCP230xx_readGPIO(port); @@ -652,15 +652,15 @@ bool MCP230xx_Command(void) if ( mcp230xx_outpinmapping[relay_no] == pin) break; } relay_no = TasmotaGlobal.devices_present - mcp230xx_oldoutpincount + relay_no +1; - if ((!strcmp(subStr(sub_string, XdrvMailbox.data, ",", 2), "ON")) || (!strcmp(subStr(sub_string, XdrvMailbox.data, ",", 2), "1"))) { + if ((!strcmp(ArgV(argument, 2), "ON")) || (!strcmp(ArgV(argument, 2), "1"))) { ExecuteCommandPower(relay_no, 1, SRC_IGNORE); return serviced; } - if ((!strcmp(subStr(sub_string, XdrvMailbox.data, ",", 2), "OFF")) || (!strcmp(subStr(sub_string, XdrvMailbox.data, ",", 2), "0"))) { + if ((!strcmp(ArgV(argument, 2), "OFF")) || (!strcmp(ArgV(argument, 2), "0"))) { ExecuteCommandPower(relay_no, 0, SRC_IGNORE); return serviced; } - if ((!strcmp(subStr(sub_string, XdrvMailbox.data, ",", 2), "T")) || (!strcmp(subStr(sub_string, XdrvMailbox.data, ",", 2), "2"))) { + if ((!strcmp(ArgV(argument, 2), "T")) || (!strcmp(ArgV(argument, 2), "2"))) { ExecuteCommandPower(relay_no, 2, SRC_IGNORE); return serviced; } @@ -670,13 +670,13 @@ bool MCP230xx_Command(void) uint8_t pullup = 0; uint8_t intmode = 0; if (paramcount > 1) { - pinmode = atoi(subStr(sub_string, XdrvMailbox.data, ",", 2)); + pinmode = atoi(ArgV(argument, 2)); } if (paramcount > 2) { - pullup = atoi(subStr(sub_string, XdrvMailbox.data, ",", 3)); + pullup = atoi(ArgV(argument, 3)); } if (paramcount > 3) { - intmode = atoi(subStr(sub_string, XdrvMailbox.data, ",", 4)); + intmode = atoi(ArgV(argument, 4)); } #ifdef USE_MCP230xx_OUTPUT if ((pin < mcp230xx_pincount) && (pinmode > 0) && (pinmode < 7) && (pullup < 2) && (paramcount > 2)) { @@ -803,7 +803,7 @@ void MCP230xx_SwitchRelay() { uint8_t pin = mcp230xx_outpinmapping[i - (TasmotaGlobal.devices_present - mcp230xx_oldoutpincount)]; uint8_t pincmd = Settings.mcp230xx_config[pin].pinmode - 5; uint8_t relay_state = bitRead(XdrvMailbox.index, i); - AddLog_P(LOG_LEVEL_DEBUG, PSTR("MCP: relay %d pin_no %d state %d"), i,pin, relay_state); + AddLog(LOG_LEVEL_DEBUG, PSTR("MCP: relay %d pin_no %d state %d"), i,pin, relay_state); switch (relay_state) { case 1: MCP230xx_SetOutPin(pin,abs(pincmd-1)); diff --git a/tasmota/xsns_30_mpr121.ino b/tasmota/xsns_30_mpr121.ino index 9053f39bd..8523c5bdd 100644 --- a/tasmota/xsns_30_mpr121.ino +++ b/tasmota/xsns_30_mpr121.ino @@ -293,7 +293,7 @@ void Mpr121Init(struct mpr121 *pS, bool initial) // Check if sensor is running pS->running[i] = (0x00 != I2cRead8(pS->i2c_addr[i], MPR121_ECR_REG)); - AddLog_P(LOG_LEVEL_INFO, PSTR(D_LOG_I2C "MPR121%c: %sRunning"), pS->id[i], (pS->running[i]) ? "" : "NOT"); + AddLog(LOG_LEVEL_INFO, PSTR(D_LOG_I2C "MPR121%c: %sRunning"), pS->id[i], (pS->running[i]) ? "" : "NOT"); } else { @@ -305,7 +305,7 @@ void Mpr121Init(struct mpr121 *pS, bool initial) // Display no sensor found message if (!(pS->connected[0] || pS->connected[1] || pS->connected[2] || pS->connected[3])) { - AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_I2C "MPR121: No sensors found")); + AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_I2C "MPR121: No sensors found")); } } // void Mpr121Init(struct mpr121 *s) @@ -334,7 +334,7 @@ void Mpr121Show(struct mpr121 *pS, uint8_t function) // Read data if (!I2cValidRead16LE(&pS->current[i], pS->i2c_addr[i], MPR121_ELEX_REG)) { - AddLog_P(LOG_LEVEL_ERROR, PSTR(D_LOG_I2C "MPR121%c: ERROR: Cannot read data!"), pS->id[i]); + AddLog(LOG_LEVEL_ERROR, PSTR(D_LOG_I2C "MPR121%c: ERROR: Cannot read data!"), pS->id[i]); Mpr121Init(pS, false); return; } @@ -343,7 +343,7 @@ void Mpr121Show(struct mpr121 *pS, uint8_t function) // Clear OVCF bit I2cWrite8(pS->i2c_addr[i], MPR121_ELEX_REG, 0x00); - AddLog_P(LOG_LEVEL_ERROR, PSTR(D_LOG_I2C "MPR121%c: ERROR: Excess current detected! Fix circuits if it happens repeatedly! Soft-resetting MPR121 ..."), pS->id[i]); + AddLog(LOG_LEVEL_ERROR, PSTR(D_LOG_I2C "MPR121%c: ERROR: Excess current detected! Fix circuits if it happens repeatedly! Soft-resetting MPR121 ..."), pS->id[i]); Mpr121Init(pS, false); return; } diff --git a/tasmota/xsns_32_mpu6050.ino b/tasmota/xsns_32_mpu6050.ino index b8d4c4d62..e0e738417 100644 --- a/tasmota/xsns_32_mpu6050.ino +++ b/tasmota/xsns_32_mpu6050.ino @@ -182,8 +182,6 @@ void MPU_6050Show(bool json) MPU_6050PerformReading(); float tempConv = ConvertTemp(MPU_6050_temperature / 340.0 + 35.53); - char temperature[33]; - dtostrfd(tempConv, Settings.flag2.temperature_resolution, temperature); char axis_ax[33]; dtostrfd(MPU_6050_ax, Settings.flag2.axis_resolution, axis_ax); char axis_ay[33]; @@ -225,19 +223,19 @@ void MPU_6050Show(bool json) snprintf_P(json_ypr_p, sizeof(json_ypr_p), PSTR(",\"" D_JSON_PITCH "\":%s"), axis_pitch); char json_ypr_r[25]; snprintf_P(json_ypr_r, sizeof(json_ypr_r), PSTR(",\"" D_JSON_ROLL "\":%s"), axis_roll); - ResponseAppend_P(PSTR(",\"%s\":{\"" D_JSON_TEMPERATURE "\":%s%s%s%s%s%s%s%s%s%s}"), - D_SENSOR_MPU6050, temperature, json_axis_ax, json_axis_ay, json_axis_az, json_axis_gx, json_axis_gy, json_axis_gz, + ResponseAppend_P(PSTR(",\"%s\":{\"" D_JSON_TEMPERATURE "\":%*_f%s%s%s%s%s%s%s%s%s}"), + D_SENSOR_MPU6050, Settings.flag2.temperature_resolution, &tempConv, json_axis_ax, json_axis_ay, json_axis_az, json_axis_gx, json_axis_gy, json_axis_gz, json_ypr_y, json_ypr_p, json_ypr_r); #else - ResponseAppend_P(PSTR(",\"%s\":{\"" D_JSON_TEMPERATURE "\":%s%s%s%s%s%s%s}"), - D_SENSOR_MPU6050, temperature, json_axis_ax, json_axis_ay, json_axis_az, json_axis_gx, json_axis_gy, json_axis_gz); + ResponseAppend_P(PSTR(",\"%s\":{\"" D_JSON_TEMPERATURE "\":%*_f%s%s%s%s%s%s}"), + D_SENSOR_MPU6050, Settings.flag2.temperature_resolution, &tempConv, json_axis_ax, json_axis_ay, json_axis_az, json_axis_gx, json_axis_gy, json_axis_gz); #endif // USE_MPU6050_DMP #ifdef USE_DOMOTICZ - DomoticzSensor(DZ_TEMP, temperature); + DomoticzFloatSensor(DZ_TEMP, tempConv); #endif // USE_DOMOTICZ #ifdef USE_WEBSERVER } else { - WSContentSend_PD(HTTP_SNS_TEMP, D_SENSOR_MPU6050, temperature, TempUnit()); + WSContentSend_Temp(D_SENSOR_MPU6050, tempConv); WSContentSend_PD(HTTP_SNS_AXIS, axis_ax, axis_ay, axis_az, axis_gx, axis_gy, axis_gz); #ifdef USE_MPU6050_DMP WSContentSend_PD(HTTP_SNS_YPR, axis_yaw, axis_pitch, axis_roll); diff --git a/tasmota/xsns_33_ds3231.ino b/tasmota/xsns_33_ds3231.ino index 92254268c..a667d3ad1 100644 --- a/tasmota/xsns_33_ds3231.ino +++ b/tasmota/xsns_33_ds3231.ino @@ -142,7 +142,7 @@ void DS3231EverySecond(void) RtcTime.year = tmpTime.year + 1970; Rtc.daylight_saving_time = RuleToTime(Settings.tflag[1], RtcTime.year); Rtc.standard_time = RuleToTime(Settings.tflag[0], RtcTime.year); - AddLog_P(LOG_LEVEL_INFO, PSTR("Set time from DS3231 to RTC (" D_UTC_TIME ") %s, (" D_DST_TIME ") %s, (" D_STD_TIME ") %s"), + AddLog(LOG_LEVEL_INFO, PSTR("Set time from DS3231 to 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 TasmotaGlobal.rules_flag.time_init = 1; @@ -151,7 +151,7 @@ void DS3231EverySecond(void) } } else if (!ds3231WriteStatus && Rtc.utc_time > START_VALID_TIME && abs(Rtc.utc_time - ReadFromDS3231()) > 60) {//if time is valid and is drift from RTC in more that 60 second - AddLog_P(LOG_LEVEL_INFO, PSTR("Write Time TO DS3231 from NTP (" D_UTC_TIME ") %s, (" D_DST_TIME ") %s, (" D_STD_TIME ") %s"), + AddLog(LOG_LEVEL_INFO, PSTR("Write Time TO DS3231 from NTP (" 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()); SetDS3231Time (Rtc.utc_time); //update the DS3231 time ds3231WriteStatus = true; diff --git a/tasmota/xsns_34_hx711.ino b/tasmota/xsns_34_hx711.ino index 4f22d7cca..db89f432e 100644 --- a/tasmota/xsns_34_hx711.ino +++ b/tasmota/xsns_34_hx711.ino @@ -193,7 +193,7 @@ bool HxCommand(void) { bool serviced = true; bool show_parms = false; - char sub_string[XdrvMailbox.data_len +1]; + char argument[XdrvMailbox.data_len]; for (uint32_t ca = 0; ca < XdrvMailbox.data_len; ca++) { if ((' ' == XdrvMailbox.data[ca]) || ('=' == XdrvMailbox.data[ca])) { XdrvMailbox.data[ca] = ','; } @@ -206,7 +206,7 @@ bool HxCommand(void) break; case 2: // Calibrate if (strchr(XdrvMailbox.data, ',') != nullptr) { - Settings.weight_reference = strtol(subStr(sub_string, XdrvMailbox.data, ",", 2), nullptr, 10); + Settings.weight_reference = strtol(ArgV(argument, 2), nullptr, 10); } Hx.scale = 1; HxReset(); @@ -216,42 +216,42 @@ bool HxCommand(void) break; case 3: // WeightRef to user reference if (strchr(XdrvMailbox.data, ',') != nullptr) { - Settings.weight_reference = strtol(subStr(sub_string, XdrvMailbox.data, ",", 2), nullptr, 10); + Settings.weight_reference = strtol(ArgV(argument, 2), nullptr, 10); } show_parms = true; break; case 4: // WeightCal to user calculated value if (strchr(XdrvMailbox.data, ',') != nullptr) { - Settings.weight_calibration = strtol(subStr(sub_string, XdrvMailbox.data, ",", 2), nullptr, 10); + Settings.weight_calibration = strtol(ArgV(argument, 2), nullptr, 10); Hx.scale = Settings.weight_calibration; } show_parms = true; break; case 5: // WeightMax if (strchr(XdrvMailbox.data, ',') != nullptr) { - Settings.weight_max = strtol(subStr(sub_string, XdrvMailbox.data, ",", 2), nullptr, 10) / 1000; + Settings.weight_max = strtol(ArgV(argument, 2), nullptr, 10) / 1000; } show_parms = true; break; case 6: // WeightItem if (strchr(XdrvMailbox.data, ',') != nullptr) { - Settings.weight_item = (unsigned long)(CharToFloat(subStr(sub_string, XdrvMailbox.data, ",", 2)) * 10); + Settings.weight_item = (unsigned long)(CharToFloat(ArgV(argument, 2)) * 10); } show_parms = true; break; case 7: // WeightSave Settings.energy_frequency_calibration = Hx.weight; - Response_P(S_JSON_SENSOR_INDEX_SVALUE, XSNS_34, D_JSON_DONE); + Response_P(S_JSON_SENSOR_INDEX_SVALUE, XSNS_34, PSTR(D_JSON_DONE)); break; case 8: // Json on weight change if (strchr(XdrvMailbox.data, ',') != nullptr) { - Settings.SensorBits1.hx711_json_weight_change = strtol(subStr(sub_string, XdrvMailbox.data, ",", 2), nullptr, 10) & 1; + Settings.SensorBits1.hx711_json_weight_change = strtol(ArgV(argument, 2), nullptr, 10) & 1; } show_parms = true; break; case 9: // WeightDelta if (strchr(XdrvMailbox.data, ',') != nullptr) { - Settings.weight_change = strtol(subStr(sub_string, XdrvMailbox.data, ",", 2), nullptr, 10); + Settings.weight_change = strtol(ArgV(argument, 2), nullptr, 10); SetWeightDelta(); } show_parms = true; @@ -490,7 +490,7 @@ void HandleHxAction(void) { if (!HttpCheckPriviledgedAccess()) { return; } - AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_HTTP D_CONFIGURE_HX711)); + AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_HTTP D_CONFIGURE_HX711)); if (Webserver->hasArg("save")) { HxSaveSettings(); @@ -549,7 +549,7 @@ void HxLogUpdates(void) char weigth_item_chr[33]; dtostrfd((float)Settings.weight_item / 10000, 4, weigth_item_chr); - AddLog_P(LOG_LEVEL_INFO, PSTR(D_LOG_WIFI D_JSON_WEIGHT_REF " %s, " D_JSON_WEIGHT_ITEM " %s"), weigth_ref_chr, weigth_item_chr); + AddLog(LOG_LEVEL_INFO, PSTR(D_LOG_WIFI D_JSON_WEIGHT_REF " %s, " D_JSON_WEIGHT_ITEM " %s"), weigth_ref_chr, weigth_item_chr); } #endif // USE_HX711_GUI diff --git a/tasmota/xsns_35_tx20.ino b/tasmota/xsns_35_tx20.ino index bf3e4a8ef..87c707350 100644 --- a/tasmota/xsns_35_tx20.ino +++ b/tasmota/xsns_35_tx20.ino @@ -80,16 +80,16 @@ extern "C" { #define D_TX20_WIND_ANGLE "∠" #define D_TX20_WIND_DEGREE "°" const char HTTP_SNS_TX2X[] PROGMEM = - "{s}" D_TX2x_NAME " " D_TX20_WIND_SPEED "{m}%s %s{e}" + "{s}" D_TX2x_NAME " " D_TX20_WIND_SPEED "{m}%1_f %s{e}" #ifndef USE_TX2X_WIND_SENSOR_NOSTATISTICS - "{s}" D_TX2x_NAME " " D_TX20_WIND_SPEED " " D_TX20_WIND_AVG "{m}%s %s{e}" - "{s}" D_TX2x_NAME " " D_TX20_WIND_SPEED_MIN "{m}%s %s{e}" - "{s}" D_TX2x_NAME " " D_TX20_WIND_SPEED_MAX "{m}%s %s{e}" + "{s}" D_TX2x_NAME " " D_TX20_WIND_SPEED " " D_TX20_WIND_AVG "{m}%1_f %s{e}" + "{s}" D_TX2x_NAME " " D_TX20_WIND_SPEED_MIN "{m}%1_f %s{e}" + "{s}" D_TX2x_NAME " " D_TX20_WIND_SPEED_MAX "{m}%1_f %s{e}" #endif // USE_TX2X_WIND_SENSOR_NOSTATISTICS - "{s}" D_TX2x_NAME " " D_TX20_WIND_DIRECTION "{m}%s %s" D_TX20_WIND_DEGREE "{e}" + "{s}" D_TX2x_NAME " " D_TX20_WIND_DIRECTION "{m}%s %1_f" D_TX20_WIND_DEGREE "{e}" #ifndef USE_TX2X_WIND_SENSOR_NOSTATISTICS - "{s}" D_TX2x_NAME " " D_TX20_WIND_DIRECTION " " D_TX20_WIND_AVG "{m}%s %s" D_TX20_WIND_DEGREE "{e}" - "{s}" D_TX2x_NAME " " D_TX20_WIND_DIRECTION " " D_TX20_WIND_ANGLE "{m}%s" D_TX20_WIND_DEGREE " (%s,%s)" D_TX20_WIND_DEGREE; + "{s}" D_TX2x_NAME " " D_TX20_WIND_DIRECTION " " D_TX20_WIND_AVG "{m}%s %1_f" D_TX20_WIND_DEGREE "{e}" + "{s}" D_TX2x_NAME " " D_TX20_WIND_DIRECTION " " D_TX20_WIND_ANGLE "{m}%1_f" D_TX20_WIND_DEGREE " (%1_f,%1_f)" D_TX20_WIND_DEGREE; #endif // USE_TX2X_WIND_SENSOR_NOSTATISTICS ; #endif // USE_WEBSERVER @@ -412,19 +412,13 @@ void Tx2xRead(void) } #ifdef DEBUG_TASMOTA_SENSOR - char diravg[FLOATSZ]; - dtostrfd(tx2x_wind_direction_avg, 1, diravg); - char cosx[FLOATSZ]; - dtostrfd(tx2x_wind_direction_avg_x, 1, cosx); - char siny[FLOATSZ]; - dtostrfd(tx2x_wind_direction_avg_y, 1, siny); - DEBUG_SENSOR_LOG(PSTR(D_TX2x_NAME ": dir stat - counter=%ld, actint=%ld, avgint=%ld, avg=%s (cosx=%s, siny=%s), min %d, max %d"), + DEBUG_SENSOR_LOG(PSTR(D_TX2x_NAME ": dir stat - counter=%ld, actint=%ld, avgint=%ld, avg=%1_f (cosx=%1_f, siny=%1_f), min %d, max %d"), (TasmotaGlobal.uptime-tx2x_last_uptime), tx2x_wind_direction, tx2x_wind_direction_avg_int, - diravg, - cosx, - siny, + &tx2x_wind_direction_avg, + &tx2x_wind_direction_avg_x, + &tx2x_wind_direction_avg_y, tx2x_wind_direction_min, tx2x_wind_direction_max ); @@ -484,86 +478,77 @@ void Tx2xShow(bool json) { if (!Tx2xAvailable()) { return; } - char wind_speed_string[FLOATSZ]; - dtostrfd(ConvertSpeed(tx2x_wind_speed)/10, 1, wind_speed_string); - char wind_direction_string[FLOATSZ]; - dtostrfd(tx2x_wind_direction*22.5, 1, wind_direction_string); + float wind_speed_float = ConvertSpeed(tx2x_wind_speed) / 10; + float wind_direction_float = tx2x_wind_direction * 22.5; char wind_direction_cardinal_string[TX2X_DIRECTIONS_MAXSIZE+1]; GetTextIndexed(wind_direction_cardinal_string, sizeof(wind_direction_cardinal_string), tx2x_wind_direction, kTx2xDirections); #ifndef USE_TX2X_WIND_SENSOR_NOSTATISTICS - char wind_speed_min_string[FLOATSZ]; - dtostrfd(ConvertSpeed(tx2x_wind_speed_min)/10, 1, wind_speed_min_string); - char wind_speed_max_string[FLOATSZ]; - dtostrfd(ConvertSpeed(tx2x_wind_speed_max)/10, 1, wind_speed_max_string); - char wind_speed_avg_string[FLOATSZ]; - dtostrfd(ConvertSpeed(tx2x_wind_speed_avg)/10, 1, wind_speed_avg_string); - char wind_direction_avg_string[FLOATSZ]; - dtostrfd(tx2x_wind_direction_avg, 1, wind_direction_avg_string); + float wind_speed_min_float = ConvertSpeed(tx2x_wind_speed_min) / 10; + float wind_speed_max_float = ConvertSpeed(tx2x_wind_speed_max) / 10; + float wind_speed_avg_float = ConvertSpeed(tx2x_wind_speed_avg) / 10; + float wind_direction_avg_float = tx2x_wind_direction_avg; char wind_direction_avg_cardinal_string[4]; GetTextIndexed(wind_direction_avg_cardinal_string, sizeof(wind_direction_avg_cardinal_string), int((tx2x_wind_direction_avg/22.5f)+0.5f) % 16, kTx2xDirections); - char wind_direction_range_string[FLOATSZ]; - dtostrfd(Tx2xNormalize(tx2x_wind_direction_max-tx2x_wind_direction_min)*22.5, 1, wind_direction_range_string); - char wind_direction_min_string[FLOATSZ]; - dtostrfd(Tx2xNormalize(tx2x_wind_direction_min)*22.5, 1, wind_direction_min_string); - char wind_direction_max_string[FLOATSZ]; - dtostrfd(Tx2xNormalize(tx2x_wind_direction_max)*22.5, 1, wind_direction_max_string); + float wind_direction_range_float = (tx2x_wind_direction_max-tx2x_wind_direction_min) * 22.5; + float wind_direction_min_float = Tx2xNormalize(tx2x_wind_direction_min) * 22.5; + float wind_direction_max_float = tx2x_wind_direction_max * 22.5; #endif // USE_TX2X_WIND_SENSOR_NOSTATISTICS if (json) { #ifndef USE_TX2X_WIND_SENSOR_NOSTATISTICS #ifdef USE_TX2x_LEGACY_JSON - ResponseAppend_P(PSTR(",\"" D_TX2x_NAME "\":{\"" D_JSON_SPEED "\":%s,\"SpeedAvg\":%s,\"SpeedMax\":%s,\"Direction\":\"%s\",\"Degree\":%s}"), - wind_speed_string, - wind_speed_avg_string, - wind_speed_max_string, + ResponseAppend_P(PSTR(",\"" D_TX2x_NAME "\":{\"" D_JSON_SPEED "\":%1_f,\"SpeedAvg\":%1_f,\"SpeedMax\":%1_f,\"Direction\":\"%s\",\"Degree\":%1_f}"), + &wind_speed_float, + &wind_speed_avg_float, + &wind_speed_max_float, wind_direction_cardinal_string, - wind_direction_string + &wind_direction_float ); #else // USE_TX2x_LEGACY_JSON - ResponseAppend_P(PSTR(",\"" D_TX2x_NAME "\":{\"" D_JSON_SPEED "\":{\"Act\":%s,\"Avg\":%s,\"Min\":%s,\"Max\":%s},\"Dir\":{\"Card\":\"%s\",\"Deg\":%s,\"Avg\":%s,\"AvgCard\":\"%s\",\"Min\":%s,\"Max\":%s,\"Range\":%s}}"), - wind_speed_string, - wind_speed_avg_string, - wind_speed_min_string, - wind_speed_max_string, + ResponseAppend_P(PSTR(",\"" D_TX2x_NAME "\":{\"" D_JSON_SPEED "\":{\"Act\":%1_f,\"Avg\":%1_f,\"Min\":%1_f,\"Max\":%1_f},\"Dir\":{\"Card\":\"%s\",\"Deg\":%1_f,\"Avg\":%1_f,\"AvgCard\":\"%s\",\"Min\":%1_f,\"Max\":%1_f,\"Range\":%1_f}}"), + &wind_speed_float, + &wind_speed_avg_float, + &wind_speed_min_float, + &wind_speed_max_float, wind_direction_cardinal_string, - wind_direction_string, - wind_direction_avg_string, + &wind_direction_float, + &wind_direction_avg_float, wind_direction_avg_cardinal_string, - wind_direction_min_string, - wind_direction_max_string, - wind_direction_range_string + &wind_direction_min_float, + &wind_direction_max_float, + &wind_direction_range_float ); #endif // USE_TX2x_LEGACY_JSON #else // USE_TX2X_WIND_SENSOR_NOSTATISTICS #ifdef USE_TX2x_LEGACY_JSON - ResponseAppend_P(PSTR(",\"" D_TX2x_NAME "\":{\"" D_JSON_SPEED "\":%s,\"Direction\":\"%s\",\"Degree\":%s}"), - wind_speed_string, wind_direction_cardinal_string, wind_direction_string); + ResponseAppend_P(PSTR(",\"" D_TX2x_NAME "\":{\"" D_JSON_SPEED "\":%1_f,\"Direction\":\"%s\",\"Degree\":%1_f}"), + &wind_speed_float, wind_direction_cardinal_string, &wind_direction_float); #else // USE_TX2x_LEGACY_JSON - ResponseAppend_P(PSTR(",\"" D_TX2x_NAME "\":{\"" D_JSON_SPEED "\":{\"Act\":%s},\"Dir\":{\"Card\":\"%s\",\"Deg\":%s}}"), - wind_speed_string, wind_direction_cardinal_string, wind_direction_string); + ResponseAppend_P(PSTR(",\"" D_TX2x_NAME "\":{\"" D_JSON_SPEED "\":{\"Act\":%1_f},\"Dir\":{\"Card\":\"%s\",\"Deg\":%1_f}}"), + &wind_speed_float, wind_direction_cardinal_string, &wind_direction_float); #endif // USE_TX2x_LEGACY_JSON #endif // USE_TX2X_WIND_SENSOR_NOSTATISTICS #ifdef USE_WEBSERVER } else { WSContentSend_PD(HTTP_SNS_TX2X, - wind_speed_string, + &wind_speed_float, SpeedUnit().c_str(), #ifndef USE_TX2X_WIND_SENSOR_NOSTATISTICS - wind_speed_avg_string, + &wind_speed_avg_float, SpeedUnit().c_str(), - wind_speed_min_string, + &wind_speed_min_float, SpeedUnit().c_str(), - wind_speed_max_string, + &wind_speed_max_float, SpeedUnit().c_str(), #endif // USE_TX2X_WIND_SENSOR_NOSTATISTICS wind_direction_cardinal_string, - wind_direction_string + &wind_direction_float #ifndef USE_TX2X_WIND_SENSOR_NOSTATISTICS ,wind_direction_avg_cardinal_string, - wind_direction_avg_string, - wind_direction_range_string, - wind_direction_min_string, - wind_direction_max_string + &wind_direction_avg_float, + &wind_direction_range_float, + &wind_direction_min_float, + &wind_direction_max_float #endif // USE_TX2X_WIND_SENSOR_NOSTATISTICS ); #endif // USE_WEBSERVER diff --git a/tasmota/xsns_36_mgc3130.ino b/tasmota/xsns_36_mgc3130.ino index c68714419..584b214d2 100644 --- a/tasmota/xsns_36_mgc3130.ino +++ b/tasmota/xsns_36_mgc3130.ino @@ -247,31 +247,31 @@ void MGC3130_handleGesture(){ } switch(MGC_data.out.gestureInfo.gestureCode){ case MGC3130_GESTURE_GARBAGE: - //AddLog_P(LOG_LEVEL_DEBUG, PSTR("NONE")); + //AddLog(LOG_LEVEL_DEBUG, PSTR("NONE")); snprintf_P(MGC3130_currentGesture, sizeof(MGC3130_currentGesture), PSTR("NONE")); break; case MGC3130_FLICK_WEST_EAST: - //AddLog_P(LOG_LEVEL_DEBUG, PSTR("%sFL_WE"), edge); + //AddLog(LOG_LEVEL_DEBUG, PSTR("%sFL_WE"), edge); snprintf_P(MGC3130_currentGesture, sizeof(MGC3130_currentGesture), PSTR("%sFL_WE"), edge); break; case MGC3130_FLICK_EAST_WEST: - //AddLog_P(LOG_LEVEL_DEBUG, PSTR("%sFL_EW"), edge); + //AddLog(LOG_LEVEL_DEBUG, PSTR("%sFL_EW"), edge); snprintf_P(MGC3130_currentGesture, sizeof(MGC3130_currentGesture), PSTR("%sFL_EW"), edge); break; case MGC3130_FLICK_SOUTH_NORTH: - //AddLog_P(LOG_LEVEL_DEBUG, PSTR("%sFL_SN"), edge); + //AddLog(LOG_LEVEL_DEBUG, PSTR("%sFL_SN"), edge); snprintf_P(MGC3130_currentGesture, sizeof(MGC3130_currentGesture), PSTR("%sFL_SN"), edge); break; case MGC3130_FLICK_NORTH_SOUTH: - //AddLog_P(LOG_LEVEL_DEBUG, PSTR("%sFL_NS"), edge); + //AddLog(LOG_LEVEL_DEBUG, PSTR("%sFL_NS"), edge); snprintf_P(MGC3130_currentGesture, sizeof(MGC3130_currentGesture), PSTR("%sFL_NS"), edge); break; case MGC3130_CIRCLE_CLOCKWISE: - //AddLog_P(LOG_LEVEL_DEBUG, PSTR("CW")); + //AddLog(LOG_LEVEL_DEBUG, PSTR("CW")); snprintf_P(MGC3130_currentGesture, sizeof(MGC3130_currentGesture), PSTR("CW")); break; case MGC3130_CIRCLE_CCLOCKWISE: - //AddLog_P(LOG_LEVEL_DEBUG, PSTR("CCW")); + //AddLog(LOG_LEVEL_DEBUG, PSTR("CCW")); snprintf_P(MGC3130_currentGesture, sizeof(MGC3130_currentGesture), PSTR("CCW")); break; } @@ -280,101 +280,101 @@ void MGC3130_handleGesture(){ bool MGC3130_handleTouch(){ bool success = false; // if we find a touch of higher order, we are done if (MGC_data.out.touchInfo.doubleTapCentre && !success){ - //AddLog_P(LOG_LEVEL_DEBUG, PSTR("DTAP_CENTRE")); + //AddLog(LOG_LEVEL_DEBUG, PSTR("DTAP_CENTRE")); snprintf_P(MGC3130_currentGesture, sizeof(MGC3130_currentGesture), PSTR("DT_C")); MGC3130_touchTimeout = 5; success = true; MGC3130_touchCounter = 1; } else if (MGC_data.out.touchInfo.doubleTapEast && !success){ - //AddLog_P(LOG_LEVEL_DEBUG, PSTR("DTAP_EAST")); + //AddLog(LOG_LEVEL_DEBUG, PSTR("DTAP_EAST")); snprintf_P(MGC3130_currentGesture, sizeof(MGC3130_currentGesture), PSTR("DT_E")); MGC3130_touchTimeout = 5; success = true; MGC3130_touchCounter = 1; } else if (MGC_data.out.touchInfo.doubleTapNorth && !success){ - //AddLog_P(LOG_LEVEL_DEBUG, PSTR("DTAP_NORTH")); + //AddLog(LOG_LEVEL_DEBUG, PSTR("DTAP_NORTH")); snprintf_P(MGC3130_currentGesture, sizeof(MGC3130_currentGesture), PSTR("DT_N")); MGC3130_touchTimeout = 5; success = true; MGC3130_touchCounter = 1; } else if (MGC_data.out.touchInfo.doubleTapWest && !success){ - //AddLog_P(LOG_LEVEL_DEBUG, PSTR("DTAP_WEST")); + //AddLog(LOG_LEVEL_DEBUG, PSTR("DTAP_WEST")); snprintf_P(MGC3130_currentGesture, sizeof(MGC3130_currentGesture), PSTR("DT_W")); MGC3130_touchTimeout = 5; success = true; MGC3130_touchCounter = 1; } else if (MGC_data.out.touchInfo.doubleTapSouth && !success){ - //AddLog_P(LOG_LEVEL_DEBUG, PSTR("DTAP_SOUTH")); + //AddLog(LOG_LEVEL_DEBUG, PSTR("DTAP_SOUTH")); snprintf_P(MGC3130_currentGesture, sizeof(MGC3130_currentGesture), PSTR("DT_S")); MGC3130_touchTimeout = 5; success = true; MGC3130_touchCounter = 1; } if (MGC_data.out.touchInfo.tapCentre && !success){ - //AddLog_P(LOG_LEVEL_DEBUG, PSTR("TAP_CENTRE")); + //AddLog(LOG_LEVEL_DEBUG, PSTR("TAP_CENTRE")); snprintf_P(MGC3130_currentGesture, sizeof(MGC3130_currentGesture), PSTR("TP_C")); MGC3130_touchTimeout = 2; success = true; MGC3130_touchCounter = 1; } else if (MGC_data.out.touchInfo.tapEast && !success){ - //AddLog_P(LOG_LEVEL_DEBUG, PSTR("TAP_EAST")); + //AddLog(LOG_LEVEL_DEBUG, PSTR("TAP_EAST")); snprintf_P(MGC3130_currentGesture, sizeof(MGC3130_currentGesture), PSTR("TP_E")); MGC3130_touchTimeout = 2; success = true; MGC3130_touchCounter = 1; } else if (MGC_data.out.touchInfo.tapNorth && !success){ - //AddLog_P(LOG_LEVEL_DEBUG, PSTR("TAP_NORTH")); + //AddLog(LOG_LEVEL_DEBUG, PSTR("TAP_NORTH")); snprintf_P(MGC3130_currentGesture, sizeof(MGC3130_currentGesture), PSTR("TP_N")); MGC3130_touchTimeout = 2; success = true; MGC3130_touchCounter = 1; } else if (MGC_data.out.touchInfo.tapWest && !success){ - //AddLog_P(LOG_LEVEL_DEBUG, PSTR("TAP_WEST")); + //AddLog(LOG_LEVEL_DEBUG, PSTR("TAP_WEST")); snprintf_P(MGC3130_currentGesture, sizeof(MGC3130_currentGesture), PSTR("TP_W")); MGC3130_touchTimeout = 2; success = true; MGC3130_touchCounter = 1; } else if (MGC_data.out.touchInfo.tapSouth && !success){ - //AddLog_P(LOG_LEVEL_DEBUG, PSTR("TAP_SOUTH")); + //AddLog(LOG_LEVEL_DEBUG, PSTR("TAP_SOUTH")); snprintf_P(MGC3130_currentGesture, sizeof(MGC3130_currentGesture), PSTR("TP_S")); MGC3130_touchTimeout = 2; success = true; MGC3130_touchCounter = 1; } else if (MGC_data.out.touchInfo.touchCentre && !success){ - //AddLog_P(LOG_LEVEL_DEBUG, PSTR("TOUCH_CENTRE")); + //AddLog(LOG_LEVEL_DEBUG, PSTR("TOUCH_CENTRE")); snprintf_P(MGC3130_currentGesture, sizeof(MGC3130_currentGesture), PSTR("TH_C")); success = true; MGC3130_touchCounter++; // This will reset to 0 after touching for approx. 1h and 50 minutes ;) } else if (MGC_data.out.touchInfo.touchEast && !success){ - //AddLog_P(LOG_LEVEL_DEBUG, PSTR("TOUCH_EAST")); + //AddLog(LOG_LEVEL_DEBUG, PSTR("TOUCH_EAST")); snprintf_P(MGC3130_currentGesture, sizeof(MGC3130_currentGesture), PSTR("TH_E")); success = true; MGC3130_touchCounter++; } else if (MGC_data.out.touchInfo.touchNorth && !success){ - //AddLog_P(LOG_LEVEL_DEBUG, PSTR("TOUCH_NORTH")); + //AddLog(LOG_LEVEL_DEBUG, PSTR("TOUCH_NORTH")); snprintf_P(MGC3130_currentGesture, sizeof(MGC3130_currentGesture), PSTR("TH_N")); success = true; MGC3130_touchCounter++; } else if (MGC_data.out.touchInfo.touchWest && !success){ - //AddLog_P(LOG_LEVEL_DEBUG, PSTR("TOUCH_WEST")); + //AddLog(LOG_LEVEL_DEBUG, PSTR("TOUCH_WEST")); snprintf_P(MGC3130_currentGesture, sizeof(MGC3130_currentGesture), PSTR("TH_W")); success = true; MGC3130_touchCounter++; } else if (MGC_data.out.touchInfo.touchSouth && !success){ - //AddLog_P(LOG_LEVEL_DEBUG, PSTR("TOUCH_SOUTH")); + //AddLog(LOG_LEVEL_DEBUG, PSTR("TOUCH_SOUTH")); snprintf_P(MGC3130_currentGesture, sizeof(MGC3130_currentGesture), PSTR("TH_S")); success = true; MGC3130_touchCounter++; @@ -396,7 +396,7 @@ void MGC3130_handleAirWheel(){ } void MGC3130_handleSystemStatus(){ - AddLog_P(LOG_LEVEL_DEBUG,PSTR("MGC3130: system_status: response to ID:%02x, error code: %04x"),MGC_data.status.id, MGC_data.status.error); + AddLog(LOG_LEVEL_DEBUG,PSTR("MGC3130: system_status: response to ID:%02x, error code: %04x"),MGC_data.status.id, MGC_data.status.error); } bool MGC3130_receiveMessage(){ @@ -414,7 +414,7 @@ bool MGC3130_receiveMessage(){ loaderVersion[1] = MGC_data.fw.loaderVersion[0]; loaderVersion[0] = MGC_data.fw.loaderVersion[1]; loaderPlatform = MGC_data.fw.loaderPlatform; - AddLog_P(LOG_LEVEL_INFO,PSTR("MGC3130: GestIC:%s"),MGC_data.fw.fwVersion); + AddLog(LOG_LEVEL_INFO,PSTR("MGC3130: GestIC:%s"),MGC_data.fw.fwVersion); break; } MGC_data.out.id = 0; @@ -443,7 +443,7 @@ bool MGC3130_readData() uint8_t _mismatch = MGC_data.out.counter - _lastCounter; if(_mismatch != 1){ if(i>4 && MGC_data.out.id != MGC3130_FW_VERSION){ - AddLog_P(LOG_LEVEL_DEBUG,PSTR("MGC3130: missed a packet, mismatch: %u"), _mismatch - 1); + AddLog(LOG_LEVEL_DEBUG,PSTR("MGC3130: missed a packet, mismatch: %u"), _mismatch - 1); AddLogBuffer(LOG_LEVEL_DEBUG,MGC_data.buffer,i); } } diff --git a/tasmota/xsns_37_rfsensor.ino b/tasmota/xsns_37_rfsensor.ino index aee5f6641..424928a4a 100644 --- a/tasmota/xsns_37_rfsensor.ino +++ b/tasmota/xsns_37_rfsensor.ino @@ -272,22 +272,21 @@ void RfSnsTheoV2Show(bool json) sensor, GetDT(rfsns_theo_v2_t1[i].time).c_str(), voltage); } } else { - char temperature[33]; - dtostrfd(ConvertTemp((float)rfsns_theo_v2_t1[i].temp / 100), Settings.flag2.temperature_resolution, temperature); + float temp = ConvertTemp((float)rfsns_theo_v2_t1[i].temp / 100) if (json) { - ResponseAppend_P(PSTR(",\"%s\":{\"" D_JSON_TEMPERATURE "\":%s,\"" D_JSON_ILLUMINANCE "\":%d,\"" D_JSON_VOLTAGE "\":%s}"), - sensor, temperature, rfsns_theo_v2_t1[i].lux, voltage); + ResponseAppend_P(PSTR(",\"%s\":{\"" D_JSON_TEMPERATURE "\":%*_f,\"" D_JSON_ILLUMINANCE "\":%d,\"" D_JSON_VOLTAGE "\":%s}"), + sensor, Settings.flag2.temperature_resolution, &temp, rfsns_theo_v2_t1[i].lux, voltage); #ifdef USE_DOMOTICZ if ((0 == TasmotaGlobal.tele_period) && !sensor_once) { - DomoticzSensor(DZ_TEMP, temperature); + DomoticzFloatSensor(DZ_TEMP, temp); DomoticzSensor(DZ_ILLUMINANCE, rfsns_theo_v2_t1[i].lux); sensor_once = true; } #endif // USE_DOMOTICZ #ifdef USE_WEBSERVER } else { - WSContentSend_PD(HTTP_SNS_TEMP, sensor, temperature, TempUnit()); + WSContentSend_Temp(sensor, temp); WSContentSend_PD(HTTP_SNS_ILLUMINANCE, sensor, rfsns_theo_v2_t1[i].lux); #endif // USE_WEBSERVER } @@ -619,7 +618,7 @@ void RfSnsInit(void) void RfSnsAnalyzeRawSignal(void) { - AddLog_P(LOG_LEVEL_DEBUG, PSTR("RFS: Pulses %d"), (int)rfsns_raw_signal->Number); + AddLog(LOG_LEVEL_DEBUG, PSTR("RFS: Pulses %d"), (int)rfsns_raw_signal->Number); #ifdef USE_THEO_V2 RfSnsAnalyzeTheov2(); diff --git a/tasmota/xsns_38_az7798.ino b/tasmota/xsns_38_az7798.ino index b063dc166..1aed89816 100644 --- a/tasmota/xsns_38_az7798.ino +++ b/tasmota/xsns_38_az7798.ino @@ -166,14 +166,14 @@ void AzEverySecond(void) AddLogBuffer(LOG_LEVEL_DEBUG_MORE, az_response, counter); if (!az_received) { - AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "AZ7798 comms timeout")); + AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "AZ7798 comms timeout")); return; } i = 0; while((az_response[i] != 'T') && (i < counter)) {i++;} // find the start of response if(az_response[i] != 'T') { - AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "AZ7798 failed to find start of response")); + AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "AZ7798 failed to find start of response")); return; } i++; // advance to start of temperature value @@ -183,7 +183,7 @@ void AzEverySecond(void) response_substr[j++] = az_response[i++]; } if((az_response[i] != 'C') && (az_response[i] != 'F')){ - AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "AZ7798 failed to find end of temperature")); + AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "AZ7798 failed to find end of temperature")); return; } response_substr[j] = 0; // add null terminator @@ -195,12 +195,12 @@ void AzEverySecond(void) } i++; // advance to first delimiter if(az_response[i] != ':') { - AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "AZ7798 error first delimiter")); + AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "AZ7798 error first delimiter")); return; } i++; // advance to start of CO2 if(az_response[i] != 'C') { - AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "AZ7798 error start of CO2")); + AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "AZ7798 error start of CO2")); return; } i++; // advance to start of CO2 value @@ -210,7 +210,7 @@ void AzEverySecond(void) response_substr[j++] = az_response[i++]; } if(az_response[i] != 'p') { - AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "AZ7798 failed to find end of CO2")); + AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "AZ7798 failed to find end of CO2")); return; } response_substr[j] = 0; // add null terminator @@ -220,12 +220,12 @@ void AzEverySecond(void) #endif // USE_LIGHT i += 3; // advance to second delimiter if(az_response[i] != ':') { - AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "AZ7798 error second delimiter")); + AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "AZ7798 error second delimiter")); return; } i++; // advance to start of humidity if(az_response[i] != 'H') { - AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "AZ7798 error start of humidity")); + AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "AZ7798 error start of humidity")); return; } i++; // advance to start of humidity value @@ -235,7 +235,7 @@ void AzEverySecond(void) response_substr[j++] = az_response[i++]; } if(az_response[i] != '%') { - AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "AZ7798 failed to find end of humidity")); + AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "AZ7798 failed to find end of humidity")); return; } response_substr[j] = 0; // add null terminator @@ -256,7 +256,7 @@ void AzEverySecond(void) } } while(((millis() - start) < AZ_READ_TIMEOUT)); az_clock_update = AZ_CLOCK_UPDATE_INTERVAL; - AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "AZ7798 clock updated")); + AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "AZ7798 clock updated")); } else { az_clock_update--; } diff --git a/tasmota/xsns_39_max31855.ino b/tasmota/xsns_39_max31855.ino index 2a6304ce1..fafa9f0de 100644 --- a/tasmota/xsns_39_max31855.ino +++ b/tasmota/xsns_39_max31855.ino @@ -139,20 +139,18 @@ void MAX31855_GetResult(void) { } void MAX31855_Show(bool Json) { - char probetemp[33]; - char referencetemp[33]; - dtostrfd(MAX31855_Result.ProbeTemperature, Settings.flag2.temperature_resolution, probetemp); - dtostrfd(MAX31855_Result.ReferenceTemperature, Settings.flag2.temperature_resolution, referencetemp); - char sensor_name[10]; GetTextIndexed(sensor_name, sizeof(sensor_name), Settings.flag4.max6675, kMax31855Types); if (Json) { - ResponseAppend_P(PSTR(",\"%s\":{\"" D_JSON_PROBETEMPERATURE "\":%s,\"" D_JSON_REFERENCETEMPERATURE "\":%s,\"" D_JSON_ERROR "\":%d}"), \ - sensor_name, probetemp, referencetemp, MAX31855_Result.ErrorCode); + ResponseAppend_P(PSTR(",\"%s\":{\"" D_JSON_PROBETEMPERATURE "\":%*_f,\"" D_JSON_REFERENCETEMPERATURE "\":%*_f,\"" D_JSON_ERROR "\":%d}"), \ + sensor_name, + Settings.flag2.temperature_resolution, &MAX31855_Result.ProbeTemperature, + Settings.flag2.temperature_resolution, &MAX31855_Result.ReferenceTemperature, + MAX31855_Result.ErrorCode); #ifdef USE_DOMOTICZ if (0 == TasmotaGlobal.tele_period) { - DomoticzSensor(DZ_TEMP, probetemp); + DomoticzFloatSensor(DZ_TEMP, MAX31855_Result.ProbeTemperature); } #endif // USE_DOMOTICZ #ifdef USE_KNX @@ -162,7 +160,7 @@ void MAX31855_Show(bool Json) { #endif // USE_KNX #ifdef USE_WEBSERVER } else { - WSContentSend_PD(HTTP_SNS_TEMP, sensor_name, probetemp, TempUnit()); + WSContentSend_Temp(sensor_name, MAX31855_Result.ProbeTemperature); #endif // USE_WEBSERVER } } diff --git a/tasmota/xsns_40_pn532.ino b/tasmota/xsns_40_pn532.ino index 4e9bbdcc4..03eba20c7 100644 --- a/tasmota/xsns_40_pn532.ino +++ b/tasmota/xsns_40_pn532.ino @@ -80,7 +80,7 @@ void PN532_Init(void) { ToHex_P((unsigned char*)empty_uid, sizeof(empty_uid), Pn532.uids, sizeof(Pn532.uids)); PN532_setPassiveActivationRetries(0xFF); PN532_SAMConfig(); - AddLog_P(LOG_LEVEL_INFO,"NFC: PN532 NFC Reader detected v%u.%u",(ver>>16) & 0xFF, (ver>>8) & 0xFF); + AddLog(LOG_LEVEL_INFO,"NFC: PN532 NFC Reader detected v%u.%u",(ver>>16) & 0xFF, (ver>>8) & 0xFF); Pn532.present = true; } } @@ -429,7 +429,7 @@ void PN532_ScanForTag(void) { } if (mifareclassic_WriteDataBlock(1, card_data)) { erase_success = true; - AddLog_P(LOG_LEVEL_INFO, PSTR("NFC: PN532 NFC - Erase success")); + AddLog(LOG_LEVEL_INFO, PSTR("NFC: PN532 NFC - Erase success")); memcpy(&card_datas,&card_data,sizeof(card_data)); // Cast block 1 to a string } } @@ -438,7 +438,7 @@ void PN532_ScanForTag(void) { memcpy(&card_data,&Pn532.newdata,sizeof(card_data)); if (mifareclassic_WriteDataBlock(1, card_data)) { set_success = true; - AddLog_P(LOG_LEVEL_INFO, PSTR("NFC: PN532 NFC - Data write successful")); + AddLog(LOG_LEVEL_INFO, PSTR("NFC: PN532 NFC - Data write successful")); memcpy(&card_datas,&card_data,sizeof(card_data)); // Cast block 1 to a string } #else @@ -453,11 +453,11 @@ void PN532_ScanForTag(void) { card_data[Pn532.newdata_len] = '\0'; // Enforce null termination if (mifareclassic_WriteDataBlock(1, card_data)) { set_success = true; - AddLog_P(LOG_LEVEL_INFO, PSTR("NFC: PN532 NFC - Data write successful")); + AddLog(LOG_LEVEL_INFO, PSTR("NFC: PN532 NFC - Data write successful")); memcpy(&card_datas,&card_data,sizeof(card_data)); // Cast block 1 to a string } } else { - AddLog_P(LOG_LEVEL_INFO, PSTR("NFC: PN532 NFC - Data must be alphanumeric")); + AddLog(LOG_LEVEL_INFO, PSTR("NFC: PN532 NFC - Data must be alphanumeric")); } #endif // USE_PN532_DATA_RAW } @@ -468,12 +468,12 @@ void PN532_ScanForTag(void) { switch (Pn532.function) { case 0x01: if (!erase_success) { - AddLog_P(LOG_LEVEL_INFO, PSTR("NFC: PN532 NFC - Erase fail - exiting erase mode")); + AddLog(LOG_LEVEL_INFO, PSTR("NFC: PN532 NFC - Erase fail - exiting erase mode")); } break; case 0x02: if (!set_success) { - AddLog_P(LOG_LEVEL_INFO, PSTR("NFC: PN532 NFC - Write failed - exiting set mode")); + AddLog(LOG_LEVEL_INFO, PSTR("NFC: PN532 NFC - Write failed - exiting set mode")); } default: break; @@ -500,32 +500,31 @@ bool PN532_Command(void) { serviced = false; return serviced; } - char sub_string[XdrvMailbox.data_len]; - char sub_string_tmp[XdrvMailbox.data_len]; + char argument[XdrvMailbox.data_len]; for (uint32_t ca=0;ca 1) { if (XdrvMailbox.data[XdrvMailbox.data_len-1] == ',') { serviced = false; return serviced; } - sprintf(sub_string_tmp,subStr(sub_string, XdrvMailbox.data, ",", 2)); - Pn532.newdata_len = strlen(sub_string_tmp); + ArgV(argument, 2); + Pn532.newdata_len = strlen(argument); if (Pn532.newdata_len > 15) { Pn532.newdata_len = 15; } - memcpy(&Pn532.newdata,&sub_string_tmp,Pn532.newdata_len); + memcpy(&Pn532.newdata,&argument,Pn532.newdata_len); Pn532.newdata[Pn532.newdata_len] = 0x00; // Null terminate the string Pn532.function = 2; - AddLog_P(LOG_LEVEL_INFO, PSTR("NFC: PN532 NFC - Next scanned tag data block 1 will be set to '%s'"), Pn532.newdata); + AddLog(LOG_LEVEL_INFO, PSTR("NFC: PN532 NFC - Next scanned tag data block 1 will be set to '%s'"), Pn532.newdata); ResponseTime_P(PSTR(",\"PN532\":{\"COMMAND\":\"S\"}}")); return serviced; } diff --git a/tasmota/xsns_41_max44009.ino b/tasmota/xsns_41_max44009.ino index 7eccb043e..7eaf1322d 100644 --- a/tasmota/xsns_41_max44009.ino +++ b/tasmota/xsns_41_max44009.ino @@ -75,7 +75,7 @@ void Max4409Detect(void) if ((I2cValidRead8(&buffer1, max44009_address, REG_LOWER_THRESHOLD)) && (I2cValidRead8(&buffer2, max44009_address, REG_THRESHOLD_TIMER))) { - //AddLog_P(LOG_LEVEL_DEBUG_MORE, PSTR("MAX44009 %x: %x, %x"), max44009_address, (int)buffer1, (int)buffer2); + //AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("MAX44009 %x: %x, %x"), max44009_address, (int)buffer1, (int)buffer2); if ((0x00 == buffer1) && (0xFF == buffer2)) { diff --git a/tasmota/xsns_42_scd30.ino b/tasmota/xsns_42_scd30.ino index 458e046aa..671b29903 100644 --- a/tasmota/xsns_42_scd30.ino +++ b/tasmota/xsns_42_scd30.ino @@ -93,7 +93,7 @@ void Scd30Detect(void) I2cSetActiveFound(SCD30_ADDRESS, "SCD30"); scd30Found = true; - AddLog_P(LOG_LEVEL_DEBUG, PSTR("SCD: FW v%d.%d"), major, minor); + AddLog(LOG_LEVEL_DEBUG, PSTR("SCD: FW v%d.%d"), major, minor); } // gets data from the sensor every 3 seconds or so to give the sensor time to gather new data diff --git a/tasmota/xsns_43_hre.ino b/tasmota/xsns_43_hre.ino index 68299b066..98e52b0ab 100644 --- a/tasmota/xsns_43_hre.ino +++ b/tasmota/xsns_43_hre.ino @@ -142,7 +142,7 @@ void hreEvery50ms(void) sync_run = 0; sync_counter = 0; hre_state = hre_syncing; - AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_HRE "hre_state:hre_syncing")); + AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_HRE "hre_state:hre_syncing")); break; case hre_syncing: @@ -165,19 +165,19 @@ void hreEvery50ms(void) if (sync_counter > 1000) { hre_state = hre_sleep; - AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_HRE D_ERROR)); + AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_HRE D_ERROR)); } break; // Start reading the data block case hre_read: - AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_HRE "sync_run:%d, sync_counter:%d"), sync_run, sync_counter); + AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_HRE "sync_run:%d, sync_counter:%d"), sync_run, sync_counter); read_counter = 0; parity_errors = 0; curr_start = TasmotaGlobal.uptime; memset(buff, 0, sizeof(buff)); hre_state = hre_reading; - AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_HRE "hre_state:hre_reading")); + AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_HRE "hre_state:hre_reading")); // So this is intended to fall through to the hre_reading section. // it seems that if there is much of a delay between getting the sync // bits and starting the read, the HRE won't output the message we @@ -190,7 +190,7 @@ void hreEvery50ms(void) if (read_counter == 46) { - AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_HRE "pe:%d, re:%d, buff:%s"), + AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_HRE "pe:%d, re:%d, buff:%s"), parity_errors, hre_read_errors, buff); if (parity_errors == 0) { @@ -218,7 +218,7 @@ void hreEvery50ms(void) case hre_sleep: hre_usage_time = curr_start; hre_state = hre_sleeping; - AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_HRE "hre_state:hre_sleeping")); + AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_HRE "hre_state:hre_sleeping")); case hre_sleeping: // If there isn't some delay between readings, rate calculations diff --git a/tasmota/xsns_44_sps30.ino b/tasmota/xsns_44_sps30.ino index 9dd73dbcf..831b0bdba 100644 --- a/tasmota/xsns_44_sps30.ino +++ b/tasmota/xsns_44_sps30.ino @@ -143,7 +143,7 @@ void SPS30_Detect(void) uint8_t dcode[32]; sps30_get_data(SPS_CMD_GET_SERIAL,dcode,sizeof(dcode)); - AddLog_P(LOG_LEVEL_DEBUG, PSTR("sps30 found with serial: %s"),dcode); + AddLog(LOG_LEVEL_DEBUG, PSTR("sps30 found with serial: %s"),dcode); sps30_cmd(SPS_CMD_START_MEASUREMENT); sps30_running = 1; sps30_ready = 1; diff --git a/tasmota/xsns_45_vl53l0x.ino b/tasmota/xsns_45_vl53l0x.ino index 54c49534c..1d377bd6d 100644 --- a/tasmota/xsns_45_vl53l0x.ino +++ b/tasmota/xsns_45_vl53l0x.ino @@ -25,8 +25,11 @@ * I2C Addres: 0x29 \*********************************************************************************************/ -#define XSNS_45 45 -#define XI2C_31 31 // See I2CDEVICES.md +#define XSNS_45 45 +#define XI2C_31 31 // See I2CDEVICES.md + +#define USE_VL_MEDIAN +#define USE_VL_MEDIAN_SIZE 5 // Odd number of samples median detection #include #include "VL53L0X.h" @@ -44,7 +47,6 @@ struct { void Vl53l0Detect(void) { if (!I2cSetDevice(0x29)) { return; } - if (!sensor.init()) { return; } I2cSetActiveFound(sensor.getAddress(), "VL53L0X"); @@ -61,15 +63,7 @@ void Vl53l0Detect(void) { Vl53l0x.index = 0; } -#ifdef USE_WEBSERVER -const char HTTP_SNS_VL53L0X[] PROGMEM = - "{s}VL53L0X " D_DISTANCE "{m}%d" D_UNIT_MILLIMETER "{e}"; // {s} = , {m} = , {e} = -#endif // USE_WEBSERVER - -#define USE_VL_MEDIAN - void Vl53l0Every_250MSecond(void) { - // every 200 ms uint16_t dist = sensor.readRangeContinuousMillimeters(); if ((0 == dist) || (dist > 2000)) { dist = 9999; @@ -79,18 +73,18 @@ void Vl53l0Every_250MSecond(void) { // store in ring buffer Vl53l0x.buffer[Vl53l0x.index] = dist; Vl53l0x.index++; - if (Vl53l0x.index >= 5) { + if (Vl53l0x.index >= USE_VL_MEDIAN_SIZE) { Vl53l0x.index = 0; } // sort list and take median - uint16_t tbuff[5]; + uint16_t tbuff[USE_VL_MEDIAN_SIZE]; memmove(tbuff, Vl53l0x.buffer, sizeof(tbuff)); uint16_t tmp; uint8_t flag; - for (uint32_t ocnt = 0; ocnt < 5; ocnt++) { + for (uint32_t ocnt = 0; ocnt < USE_VL_MEDIAN_SIZE; ocnt++) { flag = 0; - for (uint32_t count = 0; count < 4; count++) { + for (uint32_t count = 0; count < USE_VL_MEDIAN_SIZE -1; count++) { if (tbuff[count] > tbuff[count +1]) { tmp = tbuff[count]; tbuff[count] = tbuff[count +1]; @@ -100,7 +94,7 @@ void Vl53l0Every_250MSecond(void) { } if (!flag) { break; } } - Vl53l0x.distance = tbuff[2]; + Vl53l0x.distance = tbuff[(USE_VL_MEDIAN_SIZE -1) / 2]; #else Vl53l0x.distance = dist; #endif @@ -125,7 +119,7 @@ void Vl53l0Show(boolean json) { #endif // USE_DOMOTICZ #ifdef USE_WEBSERVER } else { - WSContentSend_PD(HTTP_SNS_VL53L0X, Vl53l0x.distance); + WSContentSend_PD(HTTP_SNS_DISTANCE, PSTR("VL53L0X"), Vl53l0x.distance); #endif } } @@ -134,8 +128,7 @@ void Vl53l0Show(boolean json) { * Interface \*********************************************************************************************/ -bool Xsns45(byte function) -{ +bool Xsns45(byte function) { if (!I2cEnabled(XI2C_31)) { return false; } bool result = false; diff --git a/tasmota/xsns_47_max31865.ino b/tasmota/xsns_47_max31865.ino index 83bef5e9a..544778d92 100644 --- a/tasmota/xsns_47_max31865.ino +++ b/tasmota/xsns_47_max31865.ino @@ -79,7 +79,7 @@ void MAX31865_GetResult(void) { rtd = max31865[i].readRTD(); MAX31865_Result[i].Rtd = rtd; MAX31865_Result[i].PtdResistance = max31865[i].rtd_to_resistance(rtd, MAX31865_REF_RES); - MAX31865_Result[i].PtdTemp = max31865[i].rtd_to_temperature(rtd, MAX31865_PTD_RES, MAX31865_REF_RES) + MAX31865_PTD_BIAS; + MAX31865_Result[i].PtdTemp = ConvertTemp(max31865[i].rtd_to_temperature(rtd, MAX31865_PTD_RES, MAX31865_REF_RES) + MAX31865_PTD_BIAS); } } } @@ -88,18 +88,15 @@ void MAX31865_Show(bool Json) { uint8_t report_once = 0; for (uint32_t i = 0; i < MAX_MAX31865S; i++) { if (max31865_pins_used & (1 << i)) { - char temperature[33]; - char resistance[33]; - - dtostrfd(MAX31865_Result[i].PtdResistance, Settings.flag2.temperature_resolution, resistance); - dtostrfd(MAX31865_Result[i].PtdTemp, Settings.flag2.temperature_resolution, temperature); - if (Json) { - ResponseAppend_P(PSTR(",\"MAX31865%c%d\":{\"" D_JSON_TEMPERATURE "\":%s,\"" D_JSON_RESISTANCE "\":%s,\"" D_JSON_ERROR "\":%d}"), \ - IndexSeparator(), i, temperature, resistance, MAX31865_Result[i].ErrorCode); + ResponseAppend_P(PSTR(",\"MAX31865%c%d\":{\"" D_JSON_TEMPERATURE "\":%*_f,\"" D_JSON_RESISTANCE "\":%*_f,\"" D_JSON_ERROR "\":%d}"), \ + IndexSeparator(), i, + Settings.flag2.temperature_resolution, &MAX31865_Result[i].PtdTemp, + Settings.flag2.temperature_resolution, &MAX31865_Result[i].PtdResistance, + MAX31865_Result[i].ErrorCode); if ((0 == TasmotaGlobal.tele_period) && (!report_once)) { #ifdef USE_DOMOTICZ - DomoticzSensor(DZ_TEMP, temperature); + DomoticzFloatSensor(DZ_TEMP, MAX31865_Result[i].PtdTemp); #endif // USE_DOMOTICZ #ifdef USE_KNX KnxSensor(KNX_TEMPERATURE, MAX31865_Result[i].PtdTemp); @@ -110,7 +107,7 @@ void MAX31865_Show(bool Json) { } else { char sensorname[33]; sprintf(sensorname, "MAX31865%c%d", IndexSeparator(), i); - WSContentSend_PD(HTTP_SNS_TEMP, sensorname, temperature, TempUnit()); + WSContentSend_Temp(sensorname, MAX31865_Result[i].PtdTemp); #endif // USE_WEBSERVER } } diff --git a/tasmota/xsns_48_chirp.ino b/tasmota/xsns_48_chirp.ino index 1e92d6c79..bfe1dc5a5 100644 --- a/tasmota/xsns_48_chirp.ino +++ b/tasmota/xsns_48_chirp.ino @@ -216,13 +216,13 @@ bool ChirpSet(uint8_t addr) { chirp_timeout_count = 10; chirp_next_job = 0; if(chirp_sensor[chirp_current].version == 255){ // this should be Chirp! and it seems to need a power cycle (or RESET to GND) - AddLog_P(LOG_LEVEL_INFO, PSTR("CHIRP: wrote new address %u, please power off device"), addr); + AddLog(LOG_LEVEL_INFO, PSTR("CHIRP: wrote new address %u, please power off device"), addr); chirp_sensor[chirp_current].version == 0; // make it "invisible" } return true; } } - AddLog_P(LOG_LEVEL_INFO, PSTR("CHIRP: address %u incorrect and not used"), addr); + AddLog(LOG_LEVEL_INFO, PSTR("CHIRP: address %u incorrect and not used"), addr); return false; } @@ -241,13 +241,13 @@ bool ChirpScan() I2cSetActiveFound(address, "CHIRP"); if (chirp_found_sensors 0); } @@ -387,7 +387,7 @@ void ChirpEvery100MSecond(void) DEBUG_SENSOR_LOG(PSTR("CHIRP: timeout 1/10 sec: %u, tele: %u"), chirp_timeout_count, Settings.tele_period); } else{ - AddLog_P(LOG_LEVEL_INFO, PSTR("CHIRP: TELEPERIOD must be > 16 seconds !")); + AddLog(LOG_LEVEL_INFO, PSTR("CHIRP: TELEPERIOD must be > 16 seconds !")); // we could overwrite it to i.e. 20 seconds here } chirp_next_job = 1; // back to step 1 @@ -417,10 +417,8 @@ void ChirpShow(bool json) { for (uint32_t i = 0; i < chirp_found_sensors; i++) { if (chirp_sensor[i].version) { - // convert double values to string - char str_temperature[33]; - double t_temperature = ((double) chirp_sensor[i].temperature )/10.0; - dtostrfd(t_temperature, Settings.flag2.temperature_resolution, str_temperature); + float t_temperature = ConvertTemp(((float)chirp_sensor[i].temperature )/10.0); + char str_light[33]; dtostrfd(chirp_sensor[i].light, 0, str_light); char str_version[7]; @@ -435,7 +433,7 @@ void ChirpShow(bool json) if(!chirp_sensor[i].explicitSleep) { ResponseAppend_P(PSTR(",\"%s%u\":{\"" D_JSON_MOISTURE "\":%d"), chirp_name, i, chirp_sensor[i].moisture); if(chirp_sensor[i].temperature!=-1){ // this is the error code -> no temperature - ResponseAppend_P(PSTR(",\"" D_JSON_TEMPERATURE "\":%s"),str_temperature); + ResponseAppend_P(PSTR(",\"" D_JSON_TEMPERATURE "\":%*_f"), Settings.flag2.temperature_resolution, &t_temperature); } ResponseAppend_P(PSTR(",\"" D_JSON_DARKNESS "\":%s}"),str_light); } @@ -458,7 +456,7 @@ void ChirpShow(bool json) WSContentSend_PD(HTTP_SNS_MOISTURE, "", chirp_sensor[i].moisture); WSContentSend_PD(HTTP_SNS_DARKNESS, str_light); if (chirp_sensor[i].temperature!=-1) { // this is the error code -> no temperature - WSContentSend_PD(HTTP_SNS_TEMP, "", str_temperature, TempUnit()); + WSContentSend_Temp("", t_temperature); } } diff --git a/tasmota/xsns_50_paj7620.ino b/tasmota/xsns_50_paj7620.ino index 174febc7d..9df5d152e 100644 --- a/tasmota/xsns_50_paj7620.ino +++ b/tasmota/xsns_50_paj7620.ino @@ -317,7 +317,7 @@ void PAJ7620Detect(void) uint8_t PAJ7620_ver = I2cRead8(PAJ7620_ADDR,2); if (0x7620 == PAJ7620_id) { // this device ID makes sense ;) I2cSetActiveFound(PAJ7620_ADDR, PAJ7620_name); - AddLog_P(LOG_LEVEL_DEBUG, PSTR("PAJ: ID: 0x%x and VER: %u"), PAJ7620_id, PAJ7620_ver); + AddLog(LOG_LEVEL_DEBUG, PSTR("PAJ: ID: 0x%x and VER: %u"), PAJ7620_id, PAJ7620_ver); PAJ7620_next_job = 1; // now init } else { diff --git a/tasmota/xsns_52_esp32_ibeacon_ble.ino b/tasmota/xsns_52_esp32_ibeacon_ble.ino new file mode 100644 index 000000000..d509332c5 --- /dev/null +++ b/tasmota/xsns_52_esp32_ibeacon_ble.ino @@ -0,0 +1,956 @@ +/* + xsns_52_esp32_ibeacon_ble.ino + if (!USE_IBEACON_ESP32 && USE_BLE_ESP32) + - Support for HM17 BLE Module + ibeacon reader on Tasmota (untested?) + if (USE_IBEACON_ESP32 && USE_BLE_ESP32) + - Support for BLE_ESP32 ibeacon reader on Tasmota + + Copyright (C) 2020 Gerhard Mutz and Theo Arends + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +// for testing of BLE_ESP32, we remove xsns_52_ibeacon.ino completely, and instead add this modified xsns_52_ibeacon_BLE_ESP32.ino +// in the future this may be more fine-grained, e.g. to allow hm17 for this, and BLE-ESP32 for other +#ifdef USE_BLE_ESP32 + +#ifdef USE_IBEACON_ESP32 + +#ifdef USE_IBEACON + +#define XSNS_52 52 + +// keyfob expires after N seconds +#define IB_TIMEOUT_INTERVAL 30 +// does a passive scan every N seconds +#define IB_UPDATE_TIME_INTERVAL 10 + +// should be in Settings +#if 1 + uint8_t ib_upd_interval,ib_tout_interval; + #define IB_UPDATE_TIME ib_upd_interval + #define IB_TIMEOUT_TIME ib_tout_interval +#else + #undef IB_UPDATE_TIME + #undef IB_TIMEOUT_TIME + #define IB_UPDATE_TIME Settings.ib_upd_interval + #define IB_TIMEOUT_TIME Settings.ib_tout_interval +#endif + +char ib_mac[14]; + + + struct { + union { + struct { + uint32_t init:1; + }; + uint32_t all = 0; + } mode; + } ESP32BLE; + + void *beaconmutex = nullptr; + + #define ENDIAN_CHANGE_U16(x) ((((x)&0xFF00) >> 8) + (((x)&0xFF) << 8)) + +#else + + #include + + #define TMSBSIZ52 512 + + #define HM17_BAUDRATE 9600 + + #define IBEACON_DEBUG + + // use this for Version 110 + #define HM17_V110 + + TasmotaSerial *IBEACON_Serial = nullptr; + + uint8_t hm17_found,hm17_cmd,hm17_flag; + + #ifdef IBEACON_DEBUG + uint8_t hm17_debug=0; + #endif + + // 78 is max serial response + #define HM17_BSIZ 128 + char hm17_sbuffer[HM17_BSIZ]; + uint8_t hm17_sindex,hm17_result,hm17_scanning,hm17_connecting; + uint32_t hm17_lastms; + + enum {HM17_TEST,HM17_ROLE,HM17_IMME,HM17_DISI,HM17_IBEA,HM17_SCAN,HM17_DISC,HM17_RESET,HM17_RENEW,HM17_CON}; + #define HM17_SUCESS 99 + +#endif + +struct IBEACON { + char FACID[8]; + char UID[32]; + char MAJOR[4]; + char MINOR[4]; + char PWR[2]; + char MAC[12]; + char RSSI[4]; +#ifdef USE_IBEACON_ESP32 + char NAME[16]; +#endif +}; + +#ifdef USE_IBEACON_ESP32 + #define MAX_IBEACONS 32 +#else + #define MAX_IBEACONS 16 +#endif + +struct IBEACON_UID { + char MAC[12]; + char RSSI[4]; + char UID[32]; + char MAJOR[4]; + char MINOR[4]; + uint8_t FLAGS; + uint8_t TIME; +#ifdef USE_IBEACON_ESP32 + uint8_t REPORTED; + uint8_t REPTIME; + char NAME[16]; +#endif +} ibeacons[MAX_IBEACONS]; + +#ifdef USE_IBEACON_ESP32 + +uint32_t ibeacon_add(struct IBEACON *ib); + +void ESP32BLE_ReverseStr(uint8_t _mac[], uint8_t len=6){ + uint8_t _reversedMAC[len]; + for (uint8_t i=0; i>4) & 0xF]; + pout[1] = hex[ pgm_read_byte(pin) & 0xF]; + } +} + +int advertismentCallback(BLE_ESP32::ble_advertisment_t *pStruct) +{ + struct IBEACON ib; + BLEAdvertisedDevice *advertisedDevice = pStruct->advertisedDevice; + + char sRSSI[6]; + itoa(pStruct->RSSI,sRSSI,10); + const uint8_t *MAC = pStruct->addr; + + int manufacturerDataLen = 0; + std::string data; + if (advertisedDevice->haveManufacturerData()){ + data = advertisedDevice->getManufacturerData(); + manufacturerDataLen = data.length(); + } + if (manufacturerDataLen){ + const uint8_t *manufacturerData = (const uint8_t *)data.data(); + DumpHex(manufacturerData, 2, ib.FACID); + if (manufacturerDataLen == 25 && + manufacturerData[0] == 0x4C && + manufacturerData[1] == 0x00) + { + BLEBeacon oBeacon = BLEBeacon(); + oBeacon.setData(std::string((char *)manufacturerData, manufacturerDataLen)); + uint8_t UUID[16]; + memcpy(UUID,oBeacon.getProximityUUID().getNative()->u128.value,16); + ESP32BLE_ReverseStr(UUID,16); + + uint16_t Major = ENDIAN_CHANGE_U16(oBeacon.getMajor()); + uint16_t Minor = ENDIAN_CHANGE_U16(oBeacon.getMinor()); + uint8_t PWR = oBeacon.getSignalPower(); + + DumpHex((const unsigned char*)&UUID,16,ib.UID); + DumpHex((const unsigned char*)&Major,2,ib.MAJOR); + DumpHex((const unsigned char*)&Minor,2,ib.MINOR); + DumpHex((const unsigned char*)&PWR,1,ib.PWR); + DumpHex((const unsigned char*)MAC,6,ib.MAC); + memcpy(ib.RSSI,sRSSI,4); + memset(ib.NAME,0x0,16); + + // if we added it + if (ibeacon_add(&ib) == 1){ + AddLog(LOG_LEVEL_DEBUG, PSTR("%s: MAC: %s Major: %d Minor: %d UUID: %s Power: %d RSSI: %d"), + "iBeacon", + advertisedDevice->getAddress().toString().c_str(), + Major, Minor, + oBeacon.getProximityUUID().toString().c_str(), + PWR, pStruct->RSSI); + } + return 0; + } + } + + // no manufacturer data, or not recognised. + // still have an RSSi.... + memset(ib.UID,'0',32); + memset(ib.MAJOR,'0',4); + memset(ib.MINOR,'0',4); + memset(ib.PWR,'0',2); + DumpHex((const unsigned char*)MAC,6,ib.MAC); + memcpy(ib.RSSI,sRSSI,4); + + if (advertisedDevice->haveName()) { + strncpy(ib.NAME,advertisedDevice->getName().c_str(),16); + } else { + memset(ib.NAME,0x0,16); + } + + ibeacon_add(&ib); + return 0; +} + +void ESP32Init() { + + if (!ESP32BLE.mode.init) { + ESP32BLE.mode.init = 1; + IB_UPDATE_TIME=IB_UPDATE_TIME_INTERVAL; + IB_TIMEOUT_TIME=IB_TIMEOUT_INTERVAL; + } +} + + +#endif + +void IBEACON_Init() { + + +#ifdef USE_IBEACON_ESP32 + BLE_ESP32::registerForAdvertismentCallbacks((const char *)"iBeacon", advertismentCallback); +#else + + hm17_found=0; + +// actually doesnt work reliably with software serial + if (PinUsed(GPIO_IBEACON_RX) && PinUsed(GPIO_IBEACON_TX)) { + IBEACON_Serial = new TasmotaSerial(Pin(GPIO_IBEACON_RX), Pin(GPIO_IBEACON_TX),1,0,TMSBSIZ52); + if (IBEACON_Serial->begin(HM17_BAUDRATE)) { + if (IBEACON_Serial->hardwareSerial()) { + ClaimSerial(); + } + hm17_sendcmd(HM17_TEST); + hm17_lastms=millis(); + // in case of using Settings this has to be moved + IB_UPDATE_TIME=IB_UPDATE_TIME_INTERVAL; + IB_TIMEOUT_TIME=IB_TIMEOUT_INTERVAL; + } + } + +#endif + +} + +#ifdef USE_IBEACON_ESP32 + +void esp32_every_second(void) { + for (uint32_t cnt=0; cnt < MAX_IBEACONS; cnt++) { + if (ibeacons[cnt].FLAGS) { + uint8_t mac[6]; + char tmp[13]; + memcpy(tmp, ibeacons[cnt].MAC, 12); + tmp[12] = 0; + BLE_ESP32::fromHex(mac, tmp, 6); + // use global device timeouts from BLE_ESP32. + + uint32_t ageS = BLE_ESP32::devicePresent(mac); + + // if device not present at all. + if (!ageS){ + //AddLog(LOG_LEVEL_INFO, PSTR("iBeacon no device %s %02x%02x%02x%02x%02x%02x"),tmp, mac[0],mac[1], mac[2],mac[3], mac[4],mac[5]); + ibeacons[cnt].FLAGS=0; + ibeacon_mqtt(ibeacons[cnt].MAC,"0000",ibeacons[cnt].UID,ibeacons[cnt].MAJOR,ibeacons[cnt].MINOR,ibeacons[cnt].NAME); + } else { + //AddLog(LOG_LEVEL_INFO, PSTR("iBeacon device %s %02x%02x%02x%02x%02x%02x"),tmp, mac[0],mac[1], mac[2],mac[3], mac[4],mac[5]); + } + //ibeacons[cnt].TIME++; + ibeacons[cnt].REPTIME++; // counter used to send mqtt for a dev regularly + } + } +} + +#else + +void hm17_every_second(void) { + if (!IBEACON_Serial) return; + + if (hm17_found) { + if (IB_UPDATE_TIME && (TasmotaGlobal.uptime%IB_UPDATE_TIME==0)) { + if (hm17_cmd!=99) { + if (hm17_flag&2) { + ib_sendbeep(); + } else { + if (!hm17_connecting) { + hm17_sendcmd(HM17_DISI); + } + } + } + } + for (uint32_t cnt=0;cntIB_TIMEOUT_TIME) { + ibeacons[cnt].FLAGS=0; + ibeacon_mqtt(ibeacons[cnt].MAC,"0000",ibeacons[cnt].UID,ibeacons[cnt].MAJOR,ibeacons[cnt].MINOR); + } + } + } + } else { + if (TasmotaGlobal.uptime%20==0) { + hm17_sendcmd(HM17_TEST); + } + } +} + +void hm17_sbclr(void) { + memset(hm17_sbuffer,0,HM17_BSIZ); + hm17_sindex=0; + //IBEACON_Serial->flush(); +} + +void hm17_sendcmd(uint8_t cmd) { + hm17_sbclr(); + hm17_cmd=cmd; +#ifdef IBEACON_DEBUG + if (hm17_debug) AddLog(LOG_LEVEL_INFO, PSTR("hm17cmd %d"),cmd); +#endif + switch (cmd) { + case HM17_TEST: + IBEACON_Serial->write("AT"); + break; + case HM17_ROLE: + IBEACON_Serial->write("AT+ROLE1"); + break; + case HM17_IMME: + IBEACON_Serial->write("AT+IMME1"); + break; + case HM17_DISI: + IBEACON_Serial->write("AT+DISI?"); + hm17_scanning=1; + break; + case HM17_IBEA: + IBEACON_Serial->write("AT+IBEA1"); + break; + case HM17_RESET: + IBEACON_Serial->write("AT+RESET"); + break; + case HM17_RENEW: + IBEACON_Serial->write("AT+RENEW"); + break; + case HM17_SCAN: + IBEACON_Serial->write("AT+SCAN5"); + break; + case HM17_DISC: + IBEACON_Serial->write("AT+DISC?"); + hm17_scanning=1; + break; + case HM17_CON: + IBEACON_Serial->write((const uint8_t*)"AT+CON",6); + IBEACON_Serial->write((const uint8_t*)ib_mac,12); + hm17_connecting=1; + break; + } +} + +#endif + +uint32_t ibeacon_add(struct IBEACON *ib) { +/* if (!strncmp(ib->MAJOR,"4B1C",4)) { + return 0; + } + */ + if (!strncmp(ib->RSSI,"0",1)) { + return 0; + } + + // don't bother protecting this. + //TasAutoMutex localmutex(&beaconmutex, "iBeacAdd"); + + // keyfob starts with ffff, ibeacon has valid facid + if (!strncmp(ib->MAC,"FFFF",4) || strncmp(ib->FACID,"00000000",8)) { + for (uint32_t cnt=0;cntUID,PSTR("00000000000000000000000000000000"),32)) { + if (!strncmp(ibeacons[cnt].MAC,ib->MAC,12)) { + // exists + memcpy(ibeacons[cnt].RSSI,ib->RSSI,4); + ibeacons[cnt].TIME=0; +#ifdef USE_IBEACON_ESP32 + if (ibeacons[cnt].REPTIME >= IB_UPDATE_TIME) { + ibeacons[cnt].REPTIME = 0; + ibeacons[cnt].REPORTED = 0; + } +#endif + return 2; + } + } else { + if (!strncmp(ibeacons[cnt].UID,ib->UID,32)) { + // exists + memcpy(ibeacons[cnt].RSSI,ib->RSSI,4); + ibeacons[cnt].TIME=0; +#ifdef USE_IBEACON_ESP32 + if (ibeacons[cnt].REPTIME >= IB_UPDATE_TIME) { + ibeacons[cnt].REPTIME = 0; + ibeacons[cnt].REPORTED = 0; + } +#endif + return 2; + } + } + } + } + for (uint32_t cnt=0;cntMAC,12); + memcpy(ibeacons[cnt].RSSI,ib->RSSI,4); + memcpy(ibeacons[cnt].UID,ib->UID,32); + memcpy(ibeacons[cnt].MAJOR,ib->MAJOR,4); + memcpy(ibeacons[cnt].MINOR,ib->MINOR,4); + ibeacons[cnt].FLAGS=1; + ibeacons[cnt].TIME=0; +#ifdef USE_IBEACON_ESP32 + memcpy(ibeacons[cnt].NAME,ib->NAME,16); + ibeacons[cnt].REPTIME = 0; + ibeacons[cnt].REPORTED = 0; +#endif + return 1; + } + } + } + return 0; +} + +#ifndef USE_IBEACON_ESP32 + +void hm17_decode(void) { + struct IBEACON ib; + switch (hm17_cmd) { + case HM17_TEST: + if (!strncmp(hm17_sbuffer,"OK",2)) { +#ifdef IBEACON_DEBUG + if (hm17_debug) AddLog(LOG_LEVEL_INFO, PSTR("AT OK")); +#endif + hm17_sbclr(); + hm17_result=HM17_SUCESS; + hm17_found=1; + } + break; + case HM17_ROLE: + if (!strncmp(hm17_sbuffer,"OK+Set:1",8)) { +#ifdef IBEACON_DEBUG + if (hm17_debug) AddLog(LOG_LEVEL_INFO, PSTR("ROLE OK")); +#endif + hm17_sbclr(); + hm17_result=HM17_SUCESS; + } + break; + case HM17_IMME: + if (!strncmp(hm17_sbuffer,"OK+Set:1",8)) { +#ifdef IBEACON_DEBUG + if (hm17_debug) AddLog(LOG_LEVEL_INFO, PSTR("IMME OK")); +#endif + hm17_sbclr(); + hm17_result=HM17_SUCESS; + } + break; + case HM17_IBEA: + if (!strncmp(hm17_sbuffer,"OK+Set:1",8)) { +#ifdef IBEACON_DEBUG + if (hm17_debug) AddLog(LOG_LEVEL_INFO, PSTR("IBEA OK")); +#endif + hm17_sbclr(); + hm17_result=HM17_SUCESS; + } + break; + case HM17_SCAN: + if (!strncmp(hm17_sbuffer,"OK+Set:5",8)) { +#ifdef IBEACON_DEBUG + if (hm17_debug) AddLog(LOG_LEVEL_INFO, PSTR("SCAN OK")); +#endif + hm17_sbclr(); + hm17_result=HM17_SUCESS; + } + break; + case HM17_RESET: + if (!strncmp(hm17_sbuffer,"OK+RESET",8)) { +#ifdef IBEACON_DEBUG + if (hm17_debug) AddLog(LOG_LEVEL_INFO, PSTR("RESET OK")); +#endif + hm17_sbclr(); + hm17_result=HM17_SUCESS; + } + break; + case HM17_RENEW: + if (!strncmp(hm17_sbuffer,"OK+RENEW",8)) { +#ifdef IBEACON_DEBUG + if (hm17_debug) AddLog(LOG_LEVEL_INFO, PSTR("RENEW OK")); +#endif + hm17_sbclr(); + hm17_result=HM17_SUCESS; + } + break; + case HM17_CON: + if (!strncmp(hm17_sbuffer,"OK+CONNA",8)) { + hm17_sbclr(); +#ifdef IBEACON_DEBUG + if (hm17_debug) AddLog(LOG_LEVEL_INFO, PSTR("CONNA OK")); +#endif + hm17_connecting=2; + break; + } + if (!strncmp(hm17_sbuffer,"OK+CONNE",8)) { + hm17_sbclr(); +#ifdef IBEACON_DEBUG + if (hm17_debug) AddLog(LOG_LEVEL_INFO, PSTR("CONNE ERROR")); +#endif + break; + } + if (!strncmp(hm17_sbuffer,"OK+CONNF",8)) { + hm17_sbclr(); +#ifdef IBEACON_DEBUG + if (hm17_debug) AddLog(LOG_LEVEL_INFO, PSTR("CONNF ERROR")); +#endif + break; + } + if (hm17_connecting==2 && !strncmp(hm17_sbuffer,"OK+CONN",7)) { + hm17_sbclr(); +#ifdef IBEACON_DEBUG + if (hm17_debug) AddLog(LOG_LEVEL_INFO, PSTR("CONN OK")); +#endif + hm17_connecting=3; + hm17_sendcmd(HM17_TEST); + hm17_connecting=0; + break; + } + break; + + case HM17_DISI: + case HM17_DISC: + if (!strncmp(hm17_sbuffer,"OK+DISCS",8)) { + hm17_sbclr(); + hm17_result=1; +#ifdef IBEACON_DEBUG + if (hm17_debug) AddLog(LOG_LEVEL_INFO, PSTR("DISCS OK")); +#endif + break; + } + if (!strncmp(hm17_sbuffer,"OK+DISIS",8)) { + hm17_sbclr(); + hm17_result=1; +#ifdef IBEACON_DEBUG + if (hm17_debug) AddLog(LOG_LEVEL_INFO, PSTR("DISIS OK")); +#endif + break; + } + if (!strncmp(hm17_sbuffer,"OK+DISCE",8)) { + hm17_sbclr(); + hm17_result=HM17_SUCESS; +#ifdef IBEACON_DEBUG + if (hm17_debug) AddLog(LOG_LEVEL_INFO, PSTR("DISCE OK")); +#endif + hm17_scanning=0; + break; + } + if (!strncmp(hm17_sbuffer,"OK+NAME:",8)) { + if (hm17_sbuffer[hm17_sindex-1]=='\n') { + hm17_result=HM17_SUCESS; +#ifdef IBEACON_DEBUG + if (hm17_debug) { + AddLog(LOG_LEVEL_INFO, PSTR("NAME OK")); + AddLog(LOG_LEVEL_INFO, PSTR(">>%s"),&hm17_sbuffer[8]); + } +#endif + hm17_sbclr(); + } + break; + } + if (!strncmp(hm17_sbuffer,"OK+DIS0:",8)) { + if (hm17_cmd==HM17_DISI) { +#ifdef HM17_V110 + goto hm17_v110; +#endif + } else { + if (hm17_sindex==20) { + hm17_result=HM17_SUCESS; +#ifdef IBEACON_DEBUG + if (hm17_debug) { + AddLog(LOG_LEVEL_INFO, PSTR("DIS0 OK")); + AddLog(LOG_LEVEL_INFO, PSTR(">>%s"),&hm17_sbuffer[8]); + } +#endif + hm17_sbclr(); + } + } + break; + } + if (!strncmp(hm17_sbuffer,"OK+DISC:",8)) { +hm17_v110: + if (hm17_cmd==HM17_DISI) { + if (hm17_sindex==78) { +#ifdef IBEACON_DEBUG + if (hm17_debug) { + AddLog(LOG_LEVEL_INFO, PSTR("DISC: OK")); + //OK+DISC:4C 000C0E:003 A9144081A8 3B16849611 862EC1005: 0B1CE7485D :4DB4E940F C0E:-078 + AddLog(LOG_LEVEL_INFO, PSTR(">>%s"),&hm17_sbuffer[8]); + } +#endif + memcpy(ib.FACID,&hm17_sbuffer[8],8); + memcpy(ib.UID,&hm17_sbuffer[8+8+1],32); + memcpy(ib.MAJOR,&hm17_sbuffer[8+8+1+32+1],4); + memcpy(ib.MINOR,&hm17_sbuffer[8+8+1+32+1+4],4); + memcpy(ib.PWR,&hm17_sbuffer[8+8+1+32+1+4+4],2); + memcpy(ib.MAC,&hm17_sbuffer[8+8+1+32+1+4+4+2+1],12); + memcpy(ib.RSSI,&hm17_sbuffer[8+8+1+32+1+4+4+2+1+12+1],4); + + if (ibeacon_add(&ib)) { + ibeacon_mqtt(ib.MAC,ib.RSSI,ib.UID,ib.MAJOR,ib.MINOR); + } + hm17_sbclr(); + hm17_result=1; + } + } else { +#ifdef IBEACON_DEBUG + if (hm17_debug) AddLog(LOG_LEVEL_INFO, PSTR(">->%s"),&hm17_sbuffer[8]); +#endif + } + break; + } + } +} + +#endif + +void IBEACON_loop() { + +#ifdef USE_IBEACON_ESP32 + //TasAutoMutex localmutex(&beaconmutex, "iBeacLoop"); + for (uint32_t cnt=0;cntavailable()) { + hm17_lastms=millis(); + // shift in + if (hm17_sindexread(); + hm17_sindex++; + hm17_decode(); + } else { + hm17_sindex=0; + break; + } + } + + if (hm17_cmd==99) { + if (hm17_sindex>=HM17_BSIZ-2 || (hm17_sindex && (difftime>100))) { + AddLog(LOG_LEVEL_INFO, PSTR("%s"),hm17_sbuffer); + hm17_sbclr(); + } + } + +#endif + +} + +#ifdef USE_WEBSERVER +const char HTTP_IBEACON_HL[] PROGMEM = "{s}
{m}
{e}"; +const char HTTP_IBEACON_mac[] PROGMEM = + "{s}IBEACON-MAC : %s" " {m} RSSI : %s" "{e}"; +const char HTTP_IBEACON_uid[] PROGMEM = + "{s}IBEACON-UID : %s" " {m} RSSI : %s" "{e}"; +#ifdef USE_IBEACON_ESP32 +const char HTTP_IBEACON_name[] PROGMEM = + "{s}IBEACON-NAME : %s (%s)" " {m} RSSI : %s" "{e}"; +#endif +void IBEACON_Show(void) { + char mac[14]; + char rssi[6]; + char uid[34]; +#ifdef USE_IBEACON_ESP32 + char name[18]; + //TasAutoMutex localmutex(&beaconmutex, "iBeacShow"); +#endif + int total = 0; + + for (uint32_t cnt=0;cnt 0) { + char *cp=XdrvMailbox.data; + if (*cp=='u') { + cp++; + if (*cp) IB_UPDATE_TIME=atoi(cp); + Response_P(S_JSON_IBEACON, XSNS_52,"uintv",IB_UPDATE_TIME); + } else if (*cp=='t') { + cp++; + if (*cp) IB_TIMEOUT_TIME=atoi(cp); + Response_P(S_JSON_IBEACON, XSNS_52,"lintv",IB_TIMEOUT_TIME); + } else if (*cp=='c') { + for (uint32_t cnt=0;cnt='0' && *cp<='8') { + hm17_sendcmd(*cp&7); + Response_P(S_JSON_IBEACON, XSNS_52,"hm17cmd",*cp&7); + } else if (*cp=='s') { + cp++; + len--; + while (*cp==' ') { + len--; + cp++; + } + IBEACON_Serial->write((uint8_t*)cp,len); + hm17_cmd=99; + Response_P(S_JSON_IBEACON1, XSNS_52,"hm17cmd",cp); + } +#endif +#ifdef IBEACON_DEBUG + else if (*cp=='d') { + cp++; + hm17_debug=atoi(cp); + Response_P(S_JSON_IBEACON, XSNS_52,"debug",hm17_debug); + } +#endif + } else { + serviced=false; + } + return serviced; +} + +#define D_CMND_IBEACON "IBEACON" + +#ifndef USE_IBEACON_ESP32 +//"IBEACON_FFFF3D1B1E9D_RSSI", Data "99" causes TAG to beep +bool ibeacon_cmd(void) { + ib_mac[0]=0; + int16_t rssi=0; + const char S_JSON_IBEACON[] = "{\"" D_CMND_IBEACON "_%s_RSSI\":%d}"; + uint8_t cmd_len = strlen(D_CMND_IBEACON); + if (!strncasecmp_P(XdrvMailbox.topic, PSTR(D_CMND_IBEACON), cmd_len)) { + // IBEACON prefix + rssi = XdrvMailbox.payload; + if (rssi==99) { + memcpy(ib_mac,XdrvMailbox.topic+cmd_len+1,12); + ib_mac[12]=0; + if (hm17_scanning) { + // postpone sendbeep + hm17_flag|=2; + } else { + ib_sendbeep(); + } + } + Response_P(S_JSON_IBEACON,ib_mac,rssi); + return true; + } + return false; +} + +void ib_sendbeep(void) { + hm17_flag=0; + hm17_sendcmd(HM17_CON); +} + +#endif + +#ifdef USE_IBEACON_ESP32 +void ibeacon_mqtt(const char *mac,const char *rssi,const char *uid,const char *major,const char *minor, const char *name) { +#else +void ibeacon_mqtt(const char *mac,const char *rssi,const char *uid,const char *major,const char *minor) { +#endif + char s_mac[14]; + char s_uid[34]; + char s_major[6]; + char s_minor[6]; + char s_rssi[6]; +#ifdef USE_IBEACON_ESP32 + char *s_state; +#endif + char s_name[18]; + memcpy(s_mac,mac,12); + s_mac[12]=0; + memcpy(s_uid,uid,32); + s_uid[32]=0; + memcpy(s_major,major,4); + s_major[4]=0; + memcpy(s_minor,minor,4); + s_minor[4]=0; + memcpy(s_rssi,rssi,4); + s_rssi[4]=0; + int16_t n_rssi=atoi(s_rssi); +#ifdef USE_IBEACON_ESP32 + if (n_rssi) { + s_state=(char *)"ON"; + } else { + s_state=(char *)"OFF"; + } +#endif + // if uid == all zeros, take mac + if (!strncmp_P(s_uid,PSTR("00000000000000000000000000000000"),32)) { +#ifdef USE_IBEACON_ESP32 + if (name[0]) { + memcpy(s_name,name,16); + s_name[16]=0; + ResponseTime_P(PSTR(",\"" D_CMND_IBEACON "\":{\"MAC\":\"%s\",\"NAME\":\"%s\",\"RSSI\":%d,\"STATE\":\"%s\"}}"),s_mac,s_name,n_rssi,s_state); + } else { + ResponseTime_P(PSTR(",\"" D_CMND_IBEACON "\":{\"MAC\":\"%s\",\"RSSI\":%d,\"STATE\":\"%s\"}}"),s_mac,n_rssi,s_state); + } +#else + ResponseTime_P(PSTR(",\"" D_CMND_IBEACON "\":{\"MAC\":\"%s\",\"UID\":\"%s\",\"MAJOR\":\"%s\",\"MINOR\":\"%s\",\"RSSI\":%d}}"),s_mac,s_uid,s_major,s_minor,n_rssi); +#endif + } else { +#ifdef USE_IBEACON_ESP32 + ResponseTime_P(PSTR(",\"" D_CMND_IBEACON "\":{\"UID\":\"%s\",\"MAJOR\":\"%s\",\"MINOR\":\"%s\",\"MAC\":\"%s\",\"RSSI\":%d,\"STATE\":\"%s\"}}"),s_uid,s_major,s_minor,s_mac,n_rssi,s_state); +#else + ResponseTime_P(PSTR(",\"" D_CMND_IBEACON "\":{\"UID\":\"%s\",\"MAJOR\":\"%s\",\"MINOR\":\"%s\",\"MAC\":\"%s\",\"RSSI\":%d}}"),s_uid,s_major,s_minor,s_mac,n_rssi); +#endif + } + + MqttPublishTeleSensor(); +} + + +/*********************************************************************************************\ + * Interface +\*********************************************************************************************/ + +bool Xsns52(byte function) +{ + bool result = false; + + switch (function) { + case FUNC_INIT: + IBEACON_Init(); + break; +#ifdef USE_IBEACON_ESP32 + case FUNC_EVERY_250_MSECOND: + if (!ESP32BLE.mode.init) { + ESP32Init(); + } + break; +#endif + case FUNC_LOOP: + IBEACON_loop(); + break; + case FUNC_EVERY_SECOND: +#ifdef USE_IBEACON_ESP32 + esp32_every_second(); +#else + hm17_every_second(); +#endif + break; + case FUNC_COMMAND_SENSOR: + if (XSNS_52 == XdrvMailbox.index) { + result = xsns52_cmd(); + } + break; +#ifndef USE_IBEACON_ESP32 + case FUNC_COMMAND: + result=ibeacon_cmd(); + break; +#endif +#ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: +#ifndef USE_IBEACON_ESP32 + if (hm17_found) IBEACON_Show(); +#else + IBEACON_Show(); +#endif + break; +#endif // USE_WEBSERVER + } + return result; +} + +#endif // USE_IBEACON + +#endif \ No newline at end of file diff --git a/tasmota/xsns_52_ibeacon.ino b/tasmota/xsns_52_ibeacon.ino index 723446700..188ed2941 100755 --- a/tasmota/xsns_52_ibeacon.ino +++ b/tasmota/xsns_52_ibeacon.ino @@ -17,6 +17,10 @@ along with this program. If not, see . */ +// for testing of BLE_ESP32, we remove this completely, and instead add the modified xsns_52_ibeacon_BLE_ESP32.ino +// in the future this may be more fine-grained, e.g. to allow hm17 for this, and BLE-ESP32 for other +#ifndef USE_BLE_ESP32 + #ifdef USE_IBEACON #define XSNS_52 52 @@ -188,7 +192,7 @@ class ESP32BLEScanCallback : public BLEAdvertisedDeviceCallbacks uint8_t PWR = oBeacon.getSignalPower(); - AddLog_P(LOG_LEVEL_DEBUG, PSTR("%s: MAC: %s Major: %d Minor: %d UUID: %s Power: %d RSSI: %d"), + AddLog(LOG_LEVEL_DEBUG, PSTR("%s: MAC: %s Major: %d Minor: %d UUID: %s Power: %d RSSI: %d"), "BLE", advertisedDevice->getAddress().toString().c_str(), Major, Minor, @@ -236,7 +240,7 @@ void ESP32StartScanTask(){ 0, /* Priority of the task */ NULL, /* Task handle. */ 0); /* Core where the task should run */ - AddLog_P(LOG_LEVEL_DEBUG,PSTR("%s: Start scanning"),"BLE"); + AddLog(LOG_LEVEL_DEBUG,PSTR("%s: Start scanning"),"BLE"); } void ESP32scanEndedCB(NimBLEScanResults results); @@ -254,25 +258,25 @@ void ESP32ScanTask(void *pvParameters){ for (;;) { vTaskDelay(10000/ portTICK_PERIOD_MS); ESP32BLEScan->clearResults(); - AddLog_P(LOG_LEVEL_DEBUG,PSTR("%s: Clear scanning results"),"BLE"); + AddLog(LOG_LEVEL_DEBUG,PSTR("%s: Clear scanning results"),"BLE"); } } void ESP32scanEndedCB(NimBLEScanResults results) { ESP32BLE.mode.runningScan = 0; - AddLog_P(LOG_LEVEL_DEBUG,PSTR("%s: Stop scanning"),"BLE"); + AddLog(LOG_LEVEL_DEBUG,PSTR("%s: Stop scanning"),"BLE"); } void ESP32StopScanTask() { ESP32BLEScan->stop(); - AddLog_P(LOG_LEVEL_DEBUG, PSTR("%s: Pausing scanner task"),"BLE"); + AddLog(LOG_LEVEL_DEBUG, PSTR("%s: Pausing scanner task"),"BLE"); } void ESP32ResumeScanTask() { ESP32BLE.mode.runningScan = 1; ESP32BLEScan->start(0, ESP32scanEndedCB, false); - AddLog_P(LOG_LEVEL_DEBUG, PSTR("%s: Resumed scanner task"),"BLE"); + AddLog(LOG_LEVEL_DEBUG, PSTR("%s: Resumed scanner task"),"BLE"); } void ESP32Init() { @@ -281,11 +285,11 @@ void ESP32Init() { TasmotaGlobal.wifi_stay_asleep = true; if (WiFi.getSleep() == false) { - AddLog_P(LOG_LEVEL_DEBUG,PSTR("%s: Put WiFi modem in sleep mode"),"BLE"); + AddLog(LOG_LEVEL_DEBUG,PSTR("%s: Put WiFi modem in sleep mode"),"BLE"); WiFi.setSleep(true); // Sleep } - AddLog_P(LOG_LEVEL_DEBUG,PSTR("%s: Initializing Bluetooth..."),"BLE"); + AddLog(LOG_LEVEL_DEBUG,PSTR("%s: Initializing Bluetooth..."),"BLE"); if (!ESP32BLE.mode.init) { NimBLEDevice::init(""); @@ -340,12 +344,12 @@ void esp32_every_second(void) { if (TasmotaGlobal.ota_state_flag) { if (ESP32BLE.mode.runningScan) { - AddLog_P(LOG_LEVEL_DEBUG,PSTR("%s: Upgrade procedure started"),"BLE"); + AddLog(LOG_LEVEL_DEBUG,PSTR("%s: Upgrade procedure started"),"BLE"); ESP32StopScanTask(); } } else { if (!ESP32BLE.mode.runningScan) { - AddLog_P(LOG_LEVEL_DEBUG,PSTR("%s: Resuming scan"),"BLE"); + AddLog(LOG_LEVEL_DEBUG,PSTR("%s: Resuming scan"),"BLE"); ESP32ResumeScanTask(); } } @@ -405,7 +409,7 @@ void hm17_sendcmd(uint8_t cmd) { hm17_sbclr(); hm17_cmd=cmd; #ifdef IBEACON_DEBUG - if (hm17_debug) AddLog_P(LOG_LEVEL_INFO, PSTR("hm17cmd %d"),cmd); + if (hm17_debug) AddLog(LOG_LEVEL_INFO, PSTR("hm17cmd %d"),cmd); #endif switch (cmd) { case HM17_TEST: @@ -518,7 +522,7 @@ void hm17_decode(void) { case HM17_TEST: if (!strncmp(hm17_sbuffer,"OK",2)) { #ifdef IBEACON_DEBUG - if (hm17_debug) AddLog_P(LOG_LEVEL_INFO, PSTR("AT OK")); + if (hm17_debug) AddLog(LOG_LEVEL_INFO, PSTR("AT OK")); #endif hm17_sbclr(); hm17_result=HM17_SUCESS; @@ -528,7 +532,7 @@ void hm17_decode(void) { case HM17_ROLE: if (!strncmp(hm17_sbuffer,"OK+Set:1",8)) { #ifdef IBEACON_DEBUG - if (hm17_debug) AddLog_P(LOG_LEVEL_INFO, PSTR("ROLE OK")); + if (hm17_debug) AddLog(LOG_LEVEL_INFO, PSTR("ROLE OK")); #endif hm17_sbclr(); hm17_result=HM17_SUCESS; @@ -537,7 +541,7 @@ void hm17_decode(void) { case HM17_IMME: if (!strncmp(hm17_sbuffer,"OK+Set:1",8)) { #ifdef IBEACON_DEBUG - if (hm17_debug) AddLog_P(LOG_LEVEL_INFO, PSTR("IMME OK")); + if (hm17_debug) AddLog(LOG_LEVEL_INFO, PSTR("IMME OK")); #endif hm17_sbclr(); hm17_result=HM17_SUCESS; @@ -546,7 +550,7 @@ void hm17_decode(void) { case HM17_IBEA: if (!strncmp(hm17_sbuffer,"OK+Set:1",8)) { #ifdef IBEACON_DEBUG - if (hm17_debug) AddLog_P(LOG_LEVEL_INFO, PSTR("IBEA OK")); + if (hm17_debug) AddLog(LOG_LEVEL_INFO, PSTR("IBEA OK")); #endif hm17_sbclr(); hm17_result=HM17_SUCESS; @@ -555,7 +559,7 @@ void hm17_decode(void) { case HM17_SCAN: if (!strncmp(hm17_sbuffer,"OK+Set:5",8)) { #ifdef IBEACON_DEBUG - if (hm17_debug) AddLog_P(LOG_LEVEL_INFO, PSTR("SCAN OK")); + if (hm17_debug) AddLog(LOG_LEVEL_INFO, PSTR("SCAN OK")); #endif hm17_sbclr(); hm17_result=HM17_SUCESS; @@ -564,7 +568,7 @@ void hm17_decode(void) { case HM17_RESET: if (!strncmp(hm17_sbuffer,"OK+RESET",8)) { #ifdef IBEACON_DEBUG - if (hm17_debug) AddLog_P(LOG_LEVEL_INFO, PSTR("RESET OK")); + if (hm17_debug) AddLog(LOG_LEVEL_INFO, PSTR("RESET OK")); #endif hm17_sbclr(); hm17_result=HM17_SUCESS; @@ -573,7 +577,7 @@ void hm17_decode(void) { case HM17_RENEW: if (!strncmp(hm17_sbuffer,"OK+RENEW",8)) { #ifdef IBEACON_DEBUG - if (hm17_debug) AddLog_P(LOG_LEVEL_INFO, PSTR("RENEW OK")); + if (hm17_debug) AddLog(LOG_LEVEL_INFO, PSTR("RENEW OK")); #endif hm17_sbclr(); hm17_result=HM17_SUCESS; @@ -583,7 +587,7 @@ void hm17_decode(void) { if (!strncmp(hm17_sbuffer,"OK+CONNA",8)) { hm17_sbclr(); #ifdef IBEACON_DEBUG - if (hm17_debug) AddLog_P(LOG_LEVEL_INFO, PSTR("CONNA OK")); + if (hm17_debug) AddLog(LOG_LEVEL_INFO, PSTR("CONNA OK")); #endif hm17_connecting=2; break; @@ -591,21 +595,21 @@ void hm17_decode(void) { if (!strncmp(hm17_sbuffer,"OK+CONNE",8)) { hm17_sbclr(); #ifdef IBEACON_DEBUG - if (hm17_debug) AddLog_P(LOG_LEVEL_INFO, PSTR("CONNE ERROR")); + if (hm17_debug) AddLog(LOG_LEVEL_INFO, PSTR("CONNE ERROR")); #endif break; } if (!strncmp(hm17_sbuffer,"OK+CONNF",8)) { hm17_sbclr(); #ifdef IBEACON_DEBUG - if (hm17_debug) AddLog_P(LOG_LEVEL_INFO, PSTR("CONNF ERROR")); + if (hm17_debug) AddLog(LOG_LEVEL_INFO, PSTR("CONNF ERROR")); #endif break; } if (hm17_connecting==2 && !strncmp(hm17_sbuffer,"OK+CONN",7)) { hm17_sbclr(); #ifdef IBEACON_DEBUG - if (hm17_debug) AddLog_P(LOG_LEVEL_INFO, PSTR("CONN OK")); + if (hm17_debug) AddLog(LOG_LEVEL_INFO, PSTR("CONN OK")); #endif hm17_connecting=3; hm17_sendcmd(HM17_TEST); @@ -620,7 +624,7 @@ void hm17_decode(void) { hm17_sbclr(); hm17_result=1; #ifdef IBEACON_DEBUG - if (hm17_debug) AddLog_P(LOG_LEVEL_INFO, PSTR("DISCS OK")); + if (hm17_debug) AddLog(LOG_LEVEL_INFO, PSTR("DISCS OK")); #endif break; } @@ -628,7 +632,7 @@ void hm17_decode(void) { hm17_sbclr(); hm17_result=1; #ifdef IBEACON_DEBUG - if (hm17_debug) AddLog_P(LOG_LEVEL_INFO, PSTR("DISIS OK")); + if (hm17_debug) AddLog(LOG_LEVEL_INFO, PSTR("DISIS OK")); #endif break; } @@ -636,7 +640,7 @@ void hm17_decode(void) { hm17_sbclr(); hm17_result=HM17_SUCESS; #ifdef IBEACON_DEBUG - if (hm17_debug) AddLog_P(LOG_LEVEL_INFO, PSTR("DISCE OK")); + if (hm17_debug) AddLog(LOG_LEVEL_INFO, PSTR("DISCE OK")); #endif hm17_scanning=0; break; @@ -646,8 +650,8 @@ void hm17_decode(void) { hm17_result=HM17_SUCESS; #ifdef IBEACON_DEBUG if (hm17_debug) { - AddLog_P(LOG_LEVEL_INFO, PSTR("NAME OK")); - AddLog_P(LOG_LEVEL_INFO, PSTR(">>%s"),&hm17_sbuffer[8]); + AddLog(LOG_LEVEL_INFO, PSTR("NAME OK")); + AddLog(LOG_LEVEL_INFO, PSTR(">>%s"),&hm17_sbuffer[8]); } #endif hm17_sbclr(); @@ -664,8 +668,8 @@ void hm17_decode(void) { hm17_result=HM17_SUCESS; #ifdef IBEACON_DEBUG if (hm17_debug) { - AddLog_P(LOG_LEVEL_INFO, PSTR("DIS0 OK")); - AddLog_P(LOG_LEVEL_INFO, PSTR(">>%s"),&hm17_sbuffer[8]); + AddLog(LOG_LEVEL_INFO, PSTR("DIS0 OK")); + AddLog(LOG_LEVEL_INFO, PSTR(">>%s"),&hm17_sbuffer[8]); } #endif hm17_sbclr(); @@ -679,9 +683,9 @@ hm17_v110: if (hm17_sindex==78) { #ifdef IBEACON_DEBUG if (hm17_debug) { - AddLog_P(LOG_LEVEL_INFO, PSTR("DISC: OK")); + AddLog(LOG_LEVEL_INFO, PSTR("DISC: OK")); //OK+DISC:4C 000C0E:003 A9144081A8 3B16849611 862EC1005: 0B1CE7485D :4DB4E940F C0E:-078 - AddLog_P(LOG_LEVEL_INFO, PSTR(">>%s"),&hm17_sbuffer[8]); + AddLog(LOG_LEVEL_INFO, PSTR(">>%s"),&hm17_sbuffer[8]); } #endif memcpy(ib.FACID,&hm17_sbuffer[8],8); @@ -700,7 +704,7 @@ hm17_v110: } } else { #ifdef IBEACON_DEBUG - if (hm17_debug) AddLog_P(LOG_LEVEL_INFO, PSTR(">->%s"),&hm17_sbuffer[8]); + if (hm17_debug) AddLog(LOG_LEVEL_INFO, PSTR(">->%s"),&hm17_sbuffer[8]); #endif } break; @@ -742,7 +746,7 @@ uint32_t difftime=millis()-hm17_lastms; if (hm17_cmd==99) { if (hm17_sindex>=HM17_BSIZ-2 || (hm17_sindex && (difftime>100))) { - AddLog_P(LOG_LEVEL_INFO, PSTR("%s"),hm17_sbuffer); + AddLog(LOG_LEVEL_INFO, PSTR("%s"),hm17_sbuffer); hm17_sbclr(); } } @@ -1012,3 +1016,5 @@ bool Xsns52(byte function) } #endif // USE_IBEACON + +#endif // USE_BLE_ESP32 diff --git a/tasmota/xsns_53_sml.ino b/tasmota/xsns_53_sml.ino index 7792de29b..dca878b29 100755 --- a/tasmota/xsns_53_sml.ino +++ b/tasmota/xsns_53_sml.ino @@ -1258,7 +1258,7 @@ void sml_shift_in(uint32_t meters,uint32_t shard) { SML_Decode(meters); } else { // crc error - //AddLog_P(LOG_LEVEL_INFO, PSTR("ebus crc error")); + //AddLog(LOG_LEVEL_INFO, PSTR("ebus crc error")); } } meter_spos[meters]=0; @@ -1539,16 +1539,16 @@ void SML_Decode(uint8_t index) { #ifdef ED300L g_mindex=mindex; #endif - if (*mp=='#') { + if (*mp == '#') { // get string value mp++; - if (meter_desc_p[mindex].type=='o') { - for (uint8_t p=0;pconfig = config; // Full scale current in tenths of an amp - //AddLog_P( LOG_LEVEL_NONE, "Full Scale I in tenths of an amp: %u", Settings.ina226_i_fs[i]); + //AddLog( LOG_LEVEL_NONE, "Full Scale I in tenths of an amp: %u", Settings.ina226_i_fs[i]); p->i_lsb = (((float) Settings.ina226_i_fs[i])/10.0f)/32768.0f; //_debug_fval("i_lsb: %s", p->i_lsb, 7); // Get shunt resistor value in micro ohms uint32_t r_shunt_uohms = _expand_r_shunt(Settings.ina226_r_shunt[i]); - //AddLog_P( LOG_LEVEL_NONE, "Shunt R in micro-ohms: %u", r_shunt_uohms); + //AddLog( LOG_LEVEL_NONE, "Shunt R in micro-ohms: %u", r_shunt_uohms); p->calibrationValue = ((uint16_t) (0.00512/(p->i_lsb * r_shunt_uohms/1000000.0f))); // Device present p->present = true; - //AddLog_P( LOG_LEVEL_NONE, "INA226 Device %d calibration value: %04X", i, p->calibrationValue); + //AddLog( LOG_LEVEL_NONE, "INA226 Device %d calibration value: %04X", i, p->calibrationValue); Ina226SetCalibration(i); @@ -336,11 +336,11 @@ float Ina226ReadPower_w(uint8_t device) void Ina226Read(uint8_t device) { - //AddLog_P( LOG_LEVEL_NONE, "Ina226Read"); + //AddLog( LOG_LEVEL_NONE, "Ina226Read"); voltages[device] = Ina226ReadBus_v(device); currents[device] = Ina226ReadShunt_i(device); powers[device] = Ina226ReadPower_w(device); - //AddLog_P( LOG_LEVEL_NONE, "INA226 Device %d", device ); + //AddLog( LOG_LEVEL_NONE, "INA226 Device %d", device ); //_debug_fval("Voltage", voltages[device]); //_debug_fval("Current", currents[device]); //_debug_fval("Power", powers[device]); @@ -352,7 +352,7 @@ void Ina226Read(uint8_t device) void Ina226EverySecond() { - //AddLog_P( LOG_LEVEL_NONE, "Ina226EverySecond"); + //AddLog( LOG_LEVEL_NONE, "Ina226EverySecond"); for (uint8_t device = 0; device < INA226_MAX_ADDRESSES; device++){ // If there are Ina226s, and the device was present, and the device still is present, read its registers if (Ina226sFound && Ina226Info[device].present && Ina226TestPresence(device)){ @@ -363,7 +363,7 @@ void Ina226EverySecond() // If device was present, note that it dropped off here //if(Ina226Info[device].present){ //reinit_count[device]++; - //AddLog_P( LOG_LEVEL_DEBUG, "INA226 Device %d dropped off, count: %d", device, reinit_count[device]); + //AddLog( LOG_LEVEL_DEBUG, "INA226 Device %d dropped off, count: %d", device, reinit_count[device]); //} // Device no longer present Ina226Info[device].present = false; @@ -384,8 +384,8 @@ bool Ina226CommandSensor() uint8_t i, param_count, device, p1 = XdrvMailbox.payload; uint32_t r_shunt_uohms; uint16_t compact_r_shunt_uohms; - //AddLog_P( LOG_LEVEL_NONE, "Command received: %d", XdrvMailbox.payload); - //AddLog_P( LOG_LEVEL_NONE, "Command data received: %s", XdrvMailbox.data); + //AddLog( LOG_LEVEL_NONE, "Command received: %d", XdrvMailbox.payload); + //AddLog( LOG_LEVEL_NONE, "Command data received: %s", XdrvMailbox.data); // Make a copy of the data and add another terminator @@ -401,7 +401,7 @@ bool Ina226CommandSensor() if (param_str[i] == ' ' || param_str[i] == ',' || param_str[i] == 0){ param_str[i] = 0; params[param_count] = cp; - //AddLog_P( LOG_LEVEL_NONE, "INA226 Command parameter: %d, value: %s", param_count, params[param_count]); + //AddLog( LOG_LEVEL_NONE, "INA226 Command parameter: %d, value: %s", param_count, params[param_count]); param_count++; cp = param_str + i + 1; } @@ -437,7 +437,7 @@ bool Ina226CommandSensor() r_shunt_uohms = (uint32_t) ((CharToFloat(params[1])) * 1000000.0f); - //AddLog_P( LOG_LEVEL_NONE, "r_shunt_uohms: %d", r_shunt_uohms); + //AddLog( LOG_LEVEL_NONE, "r_shunt_uohms: %d", r_shunt_uohms); if (r_shunt_uohms > 32767){ uint32_t r_shunt_mohms = r_shunt_uohms/1000UL; Settings.ina226_r_shunt[device] = (uint16_t) (r_shunt_mohms | 0x8000); @@ -445,13 +445,13 @@ bool Ina226CommandSensor() else Settings.ina226_r_shunt[device] = (uint16_t) r_shunt_uohms; - //AddLog_P( LOG_LEVEL_NONE, "r_shunt_compacted: %04X", Settings.ina226_r_shunt[device]); + //AddLog( LOG_LEVEL_NONE, "r_shunt_compacted: %04X", Settings.ina226_r_shunt[device]); show_config = true; break; case 2: // Set full scale current in tenths of amps from user input in Amps Settings.ina226_i_fs[device] = (uint16_t) ((CharToFloat(params[1])) * 10.0f); - //AddLog_P( LOG_LEVEL_NONE, "i_fs: %d", Settings.ina226_i_fs[device]); + //AddLog( LOG_LEVEL_NONE, "i_fs: %d", Settings.ina226_i_fs[device]); show_config = true; break; diff --git a/tasmota/xsns_58_dht12.ino b/tasmota/xsns_58_dht12.ino index 4bab87bb3..966794ce5 100644 --- a/tasmota/xsns_58_dht12.ino +++ b/tasmota/xsns_58_dht12.ino @@ -57,7 +57,7 @@ bool Dht12Read(void) uint8_t checksum = Wire.read(); Dht12.humidity = ConvertHumidity( (float) humidity + (float) humidityTenth/(float) 10.0 ); - Dht12.temperature = ConvertTemp( (float) temp + (float) tempTenth/(float) 10.0 ); + Dht12.temperature = ConvertTemp( ((float)temp + (float)(tempTenth & 0x7F) / (float) 10.0) * (tempTenth & 0x80) ? -1.0 : 1.0 ); if (isnan(Dht12.temperature) || isnan(Dht12.humidity)) { return false; } diff --git a/tasmota/xsns_59_ds1624.ino b/tasmota/xsns_59_ds1624.ino index ccc079f4c..7afb4e679 100644 --- a/tasmota/xsns_59_ds1624.ino +++ b/tasmota/xsns_59_ds1624.ino @@ -69,7 +69,7 @@ void DS1624_Restart(uint8_t config, uint32_t idx) { config &= ~(DS1621_CFG_DONE|DS1621_CFG_1SHOT); I2cWrite8(addr, DS1624_CONF_REGISTER, config); // 1shot off delay(10); // by spec after writing - AddLog_P(LOG_LEVEL_ERROR, "%s addr %x is reset, reconfig: %x", ds1624_sns[idx].name, addr, config); + AddLog(LOG_LEVEL_ERROR, "%s addr %x is reset, reconfig: %x", ds1624_sns[idx].name, addr, config); } I2cValidRead(addr, DS1624_START_REGISTER, 1); } @@ -94,7 +94,7 @@ void DS1624_HotPlugUp(uint32_t idx) ds1624_sns[idx].errcnt = 0; ds1624_sns[idx].misscnt = 0; DS1624_Restart(config,idx); - AddLog_P(LOG_LEVEL_INFO, "Hot Plug %s addr %x config: %x", ds1624_sns[idx].name, addr, config); + AddLog(LOG_LEVEL_INFO, "Hot Plug %s addr %x config: %x", ds1624_sns[idx].name, addr, config); } } @@ -104,7 +104,7 @@ void DS1624_HotPlugDown(int idx) if (!I2cActive(addr)) { return; } I2cResetActive(addr); ds1624_sns[idx].valid = false; - AddLog_P(LOG_LEVEL_INFO, "Hot UnPlug %s", ds1624_sns[idx].name); + AddLog(LOG_LEVEL_INFO, "Hot UnPlug %s", ds1624_sns[idx].name); } bool DS1624GetTemp(float *value, int idx) @@ -114,13 +114,13 @@ bool DS1624GetTemp(float *value, int idx) uint8_t config; if (!I2cValidRead8(&config, addr, DS1624_CONF_REGISTER)) { ds1624_sns[idx].misscnt++; - AddLog_P(LOG_LEVEL_INFO, "%s device missing (errors: %i)", ds1624_sns[idx].name, ds1624_sns[idx].misscnt); + AddLog(LOG_LEVEL_INFO, "%s device missing (errors: %i)", ds1624_sns[idx].name, ds1624_sns[idx].misscnt); return false; } ds1624_sns[idx].misscnt=0; if (config & (DS1621_CFG_1SHOT|DS1621_CFG_DONE)) { ds1624_sns[idx].errcnt++; - AddLog_P(LOG_LEVEL_INFO, "%s config error, restart... (errors: %i)", ds1624_sns[idx].name, ds1624_sns[idx].errcnt); + AddLog(LOG_LEVEL_INFO, "%s config error, restart... (errors: %i)", ds1624_sns[idx].name, ds1624_sns[idx].errcnt); DS1624_Restart(config, idx); return false; } @@ -176,18 +176,16 @@ void DS1624EverySecond(void) void DS1624Show(bool json) { - char temperature[33]; bool once = true; for (uint32_t i = 0; i < DS1624_MAX_SENSORS; i++) { if (!ds1624_sns[i].valid) { continue; } - dtostrfd(ds1624_sns[i].value, Settings.flag2.temperature_resolution, temperature); if (json) { - ResponseAppend_P(JSON_SNS_TEMP, ds1624_sns[i].name, temperature); + ResponseAppend_P(JSON_SNS_F_TEMP, ds1624_sns[i].name, Settings.flag2.temperature_resolution, &ds1624_sns[i].value); if ((0 == TasmotaGlobal.tele_period) && once) { #ifdef USE_DOMOTICZ - DomoticzSensor(DZ_TEMP, temperature); + DomoticzFloatSensor(DZ_TEMP, ds1624_sns[i].value); #endif // USE_DOMOTICZ #ifdef USE_KNX KnxSensor(KNX_TEMPERATURE, ds1624_sns[i].value); @@ -196,7 +194,7 @@ void DS1624Show(bool json) } #ifdef USE_WEBSERVER } else { - WSContentSend_PD(HTTP_SNS_TEMP, ds1624_sns[i].name, temperature, TempUnit()); + WSContentSend_Temp(ds1624_sns[i].name, ds1624_sns[i].value); #endif // USE_WEBSERVER } } diff --git a/tasmota/xsns_60_GPS.ino b/tasmota/xsns_60_GPS.ino index 10e02efc2..56c6f4f10 100644 --- a/tasmota/xsns_60_GPS.ino +++ b/tasmota/xsns_60_GPS.ino @@ -573,17 +573,17 @@ void UBXSelectMode(uint16_t mode) break; case 4: Flog->startRecording(true); - AddLog_P(LOG_LEVEL_INFO, PSTR("UBX: start recording - appending")); + AddLog(LOG_LEVEL_INFO, PSTR("UBX: start recording - appending")); break; case 5: Flog->startRecording(false); - AddLog_P(LOG_LEVEL_INFO, PSTR("UBX: start recording - new log")); + AddLog(LOG_LEVEL_INFO, PSTR("UBX: start recording - new log")); break; case 6: if(Flog->recording == true){ Flog->stopRecording(); } - AddLog_P(LOG_LEVEL_INFO, PSTR("UBX: stop recording")); + AddLog(LOG_LEVEL_INFO, PSTR("UBX: stop recording")); break; #endif //USE_FLOG case 7: @@ -689,7 +689,7 @@ void UBXHandleTIME() gpsTime.second = UBX.Message.navTime.sec; UBX.rec_buffer.values.time = MakeTime(gpsTime); if (UBX.mode.forceUTCupdate || Rtc.user_time_entry == false){ - AddLog_P(LOG_LEVEL_INFO, PSTR("UBX: UTC-Time is valid, set system time")); + AddLog(LOG_LEVEL_INFO, PSTR("UBX: UTC-Time is valid, set system time")); Rtc.utc_time = UBX.rec_buffer.values.time; } Rtc.user_time_entry = true; @@ -702,7 +702,7 @@ void UBXHandleOther(void) if (UBX.state.non_empty_loops>6) { // we expect only 4-5 non-empty loops in a row, could change with other sensor speed (Hz) if(UBX.mode.runningVPort) return; UBXinitCFG(); // this should only happen with lots of NMEA-messages, but it is only a guess!! - AddLog_P(LOG_LEVEL_ERROR, PSTR("UBX: possible device-reset, will re-init")); + AddLog(LOG_LEVEL_ERROR, PSTR("UBX: possible device-reset, will re-init")); UBXSerial->flush(); UBX.state.non_empty_loops = 0; } diff --git a/tasmota/xsns_61_MI_NRF24.ino b/tasmota/xsns_61_MI_NRF24.ino index 8dff44920..f66d2de35 100644 --- a/tasmota/xsns_61_MI_NRF24.ino +++ b/tasmota/xsns_61_MI_NRF24.ino @@ -419,7 +419,7 @@ void MINRFinit(void) { MINRF.option.minimalSummary = 0; MINRF.option.directBridgeMode = 0; - AddLog_P(LOG_LEVEL_INFO, PSTR("NRF: Started")); + AddLog(LOG_LEVEL_INFO, PSTR("NRF: Started")); } } @@ -452,7 +452,7 @@ bool MINRFinitBLE(uint8_t _mode) MINRFchangePacketModeTo(_mode); return true; } - // AddLog_P(LOG_LEVEL_INFO,PSTR("MINRF chip NOT !!!! connected")); + // AddLog(LOG_LEVEL_INFO,PSTR("MINRF chip NOT !!!! connected")); return false; } @@ -495,7 +495,7 @@ bool MINRFreceivePacket(void) MINRFswapbuf((uint8_t*)&MINRF.buffer, sizeof(MINRF.buffer) ); // MINRF_LOG_BUFFER(); - // AddLog_P(LOG_LEVEL_INFO,PSTR("NRF: _lsfrlist: %x, chan: %u, mode: %u"),_lsfrlist[MINRF.currentChan],MINRF.currentChan, MINRF.packetMode); + // AddLog(LOG_LEVEL_INFO,PSTR("NRF: _lsfrlist: %x, chan: %u, mode: %u"),_lsfrlist[MINRF.currentChan],MINRF.currentChan, MINRF.packetMode); switch (MINRF.packetMode) { case 0: case NLIGHT: case MJYD2S: MINRFwhiten((uint8_t *)&MINRF.buffer, sizeof(MINRF.buffer), MINRF.channel[MINRF.currentChan] | 0x40); // "BEACON" mode, "NLIGHT" mode, "MJYD2S" mode @@ -603,7 +603,7 @@ void MINRFhandleScan(void){ MINRFscanResult.erase(std::remove_if(MINRFscanResult.begin(), MINRFscanResult.end(), [&i](scan_entry_t e) { - if(e.showedUp>2) AddLog_P(LOG_LEVEL_INFO,PSTR("NRF: Beacon %02u: %02X%02X%02X%02X%02X%02X Cid: %04X Svc: %04X UUID: %04X"),i,e.MAC[0],e.MAC[1],e.MAC[2],e.MAC[3],e.MAC[4],e.MAC[5],e.cid,e.svc,e.uuid); + if(e.showedUp>2) AddLog(LOG_LEVEL_INFO,PSTR("NRF: Beacon %02u: %02X%02X%02X%02X%02X%02X Cid: %04X Svc: %04X UUID: %04X"),i,e.MAC[0],e.MAC[1],e.MAC[2],e.MAC[3],e.MAC[4],e.MAC[5],e.cid,e.svc,e.uuid); i++; return ((e.showedUp < 3)); }), @@ -616,7 +616,7 @@ void MINRFhandleScan(void){ for(uint32_t i=0; i30) break; uint32_t ADtype = _buf[i+1]; - // AddLog_P(LOG_LEVEL_DEBUG,PSTR("NRF: Size: %u AD: %x i:%u"), size, ADtype,i); + // AddLog(LOG_LEVEL_DEBUG,PSTR("NRF: Size: %u AD: %x i:%u"), size, ADtype,i); if (size+i>32+offset) size=32-i+offset-2; if (size>30) break; char _stemp[(size*2)]; uint32_t backupSize; switch(ADtype){ case 0x01: - AddLog_P(LOG_LEVEL_DEBUG,PSTR("NRF: Flags: %02x"), _buf[i+2]); + AddLog(LOG_LEVEL_DEBUG,PSTR("NRF: Flags: %02x"), _buf[i+2]); break; case 0x02: case 0x03: entry->uuid = _buf[i+3]*256 + _buf[i+2]; - AddLog_P(LOG_LEVEL_DEBUG,PSTR("NRF: UUID: %04x"), entry->uuid); + AddLog(LOG_LEVEL_DEBUG,PSTR("NRF: UUID: %04x"), entry->uuid); success = true; break; case 0x08: case 0x09: backupSize = _buf[i+size+1]; _buf[i+size+1] = 0; - AddLog_P(LOG_LEVEL_DEBUG,PSTR("NRF: Name: %s"), (char*)&_buf[i+2]); + AddLog(LOG_LEVEL_DEBUG,PSTR("NRF: Name: %s"), (char*)&_buf[i+2]); success = true; _buf[i+size+1] = backupSize; break; case 0x0a: - AddLog_P(LOG_LEVEL_DEBUG,PSTR("NRF: TxPow: %02u"), _buf[i+2]); + AddLog(LOG_LEVEL_DEBUG,PSTR("NRF: TxPow: %02u"), _buf[i+2]); break; case 0xff: entry->cid = _buf[i+3]*256 + _buf[i+2]; - AddLog_P(LOG_LEVEL_DEBUG,PSTR("NRF: Cid: %04x"), entry->cid); + AddLog(LOG_LEVEL_DEBUG,PSTR("NRF: Cid: %04x"), entry->cid); ToHex_P((unsigned char*)&_buf+i+4,size-3,_stemp,(size*2)); - AddLog_P(LOG_LEVEL_DEBUG,PSTR("%s"),_stemp); + AddLog(LOG_LEVEL_DEBUG,PSTR("%s"),_stemp); success = true; break; case 0x16: entry->svc = _buf[i+3]*256 + _buf[i+2]; - AddLog_P(LOG_LEVEL_DEBUG,PSTR("NRF: Svc: %04x"), entry->svc); + AddLog(LOG_LEVEL_DEBUG,PSTR("NRF: Svc: %04x"), entry->svc); ToHex_P((unsigned char*)&_buf+i+4,size-3,_stemp,(size*2)); - AddLog_P(LOG_LEVEL_DEBUG,PSTR("%s"),_stemp); + AddLog(LOG_LEVEL_DEBUG,PSTR("%s"),_stemp); success = true; break; default: ToHex_P((unsigned char*)&_buf+i+2,size-1,_stemp,(size*2)); - AddLog_P(LOG_LEVEL_DEBUG,PSTR("%s"),_stemp); + AddLog(LOG_LEVEL_DEBUG,PSTR("%s"),_stemp); } i+=size; } @@ -732,7 +732,7 @@ void MINRFbeaconCounter(void) { /* char stemp[20]; snprintf_P(stemp, sizeof(stemp),PSTR("{%s:{\"Beacon\": %u}}"),D_CMND_NRF, MINRF.beacon.time); - AddLog_P(LOG_LEVEL_DEBUG, stemp); + AddLog(LOG_LEVEL_DEBUG, stemp); RulesProcessEvent(stemp); */ Response_P(PSTR("{%s:{\"Beacon\":%u}}"), D_CMND_NRF, MINRF.beacon.time); @@ -761,9 +761,9 @@ void MINRFcomputeBeaconPDU(uint8_t (&_MAC)[6], uint32_t (&PDU)[3], uint32_t offs #ifdef USE_MI_DECRYPTION int MINRFdecryptPacket(char *_buf){ encPacket_t *packet = (encPacket_t*)_buf; - // AddLog_P(LOG_LEVEL_DEBUG,PSTR("to decrypt: %02x %02x %02x %02x %02x %02x %02x %02x"),(uint8_t)_buf[0],(uint8_t)_buf[1],(uint8_t)_buf[2],(uint8_t)_buf[3],(uint8_t)_buf[4],(uint8_t)_buf[5],(uint8_t)_buf[6],(uint8_t)_buf[7]); - // AddLog_P(LOG_LEVEL_DEBUG,PSTR(" : %02x %02x %02x %02x %02x %02x %02x %02x"),(uint8_t)_buf[8],(uint8_t)_buf[9],(uint8_t)_buf[10],(uint8_t)_buf[11],(uint8_t)_buf[12],(uint8_t)_buf[13],(uint8_t)_buf[14],(uint8_t)_buf[15]); - // AddLog_P(LOG_LEVEL_DEBUG,PSTR(" : %02x %02x %02x %02x %02x "),(uint8_t)_buf[16],(uint8_t)_buf[17],(uint8_t)_buf[18],(uint8_t)_buf[19],(uint8_t)_buf[20]); + // AddLog(LOG_LEVEL_DEBUG,PSTR("to decrypt: %02x %02x %02x %02x %02x %02x %02x %02x"),(uint8_t)_buf[0],(uint8_t)_buf[1],(uint8_t)_buf[2],(uint8_t)_buf[3],(uint8_t)_buf[4],(uint8_t)_buf[5],(uint8_t)_buf[6],(uint8_t)_buf[7]); + // AddLog(LOG_LEVEL_DEBUG,PSTR(" : %02x %02x %02x %02x %02x %02x %02x %02x"),(uint8_t)_buf[8],(uint8_t)_buf[9],(uint8_t)_buf[10],(uint8_t)_buf[11],(uint8_t)_buf[12],(uint8_t)_buf[13],(uint8_t)_buf[14],(uint8_t)_buf[15]); + // AddLog(LOG_LEVEL_DEBUG,PSTR(" : %02x %02x %02x %02x %02x "),(uint8_t)_buf[16],(uint8_t)_buf[17],(uint8_t)_buf[18],(uint8_t)_buf[19],(uint8_t)_buf[20]); int ret = 0; unsigned char output[16] = {0}; @@ -781,13 +781,13 @@ int MINRFdecryptPacket(char *_buf){ uint8_t _bindkey[16] = {0x0}; for(uint32_t i=0; iMAC,MIBLEbindKeys[i].MAC,sizeof(packet->MAC))==0){ - // AddLog_P(LOG_LEVEL_DEBUG,PSTR("have key")); + // AddLog(LOG_LEVEL_DEBUG,PSTR("have key")); memcpy(_bindkey,MIBLEbindKeys[i].key,sizeof(_bindkey)); break; } // else{ - // AddLog_P(LOG_LEVEL_DEBUG,PSTR("MAC in packet: %02x %02x %02x %02x %02x %02x"), packet->MAC[0], packet->MAC[1], packet->MAC[2], packet->MAC[3], packet->MAC[4], packet->MAC[5]); - // AddLog_P(LOG_LEVEL_DEBUG,PSTR("MAC in vector: %02x %02x %02x %02x %02x %02x"), MIBLEbindKeys[i].MAC[0], MIBLEbindKeys[i].MAC[1], MIBLEbindKeys[i].MAC[2], MIBLEbindKeys[i].MAC[3], MIBLEbindKeys[i].MAC[4], MIBLEbindKeys[i].MAC[5]); + // AddLog(LOG_LEVEL_DEBUG,PSTR("MAC in packet: %02x %02x %02x %02x %02x %02x"), packet->MAC[0], packet->MAC[1], packet->MAC[2], packet->MAC[3], packet->MAC[4], packet->MAC[5]); + // AddLog(LOG_LEVEL_DEBUG,PSTR("MAC in vector: %02x %02x %02x %02x %02x %02x"), MIBLEbindKeys[i].MAC[0], MIBLEbindKeys[i].MAC[1], MIBLEbindKeys[i].MAC[2], MIBLEbindKeys[i].MAC[3], MIBLEbindKeys[i].MAC[4], MIBLEbindKeys[i].MAC[5]); // } } @@ -804,7 +804,7 @@ int MINRFdecryptPacket(char *_buf){ br_ccm_run(&ctx, 0, output, sizeof(packet->payload.cipher)); ret = br_ccm_check_tag(&ctx, packet->payload.tag); - AddLog_P(LOG_LEVEL_DEBUG,PSTR("NRF: Err:%i, Decrypted : %02x %02x %02x %02x %02x "), ret, output[0],output[1],output[2],output[3],output[4]); + AddLog(LOG_LEVEL_DEBUG,PSTR("NRF: Err:%i, Decrypted : %02x %02x %02x %02x %02x "), ret, output[0],output[1],output[2],output[3],output[4]); memcpy((uint8_t*)(packet->payload.cipher)+1,output,sizeof(packet->payload.cipher)); return ret; } @@ -823,22 +823,22 @@ int MINRFdecryptMJYD2SPacket(char *_buf, uint8_t _light, char* _output){ memcpy((uint8_t*)&nonce+6,(uint8_t*)&packet->PID,2); nonce[8] = packet->frameCnt; memcpy((uint8_t*)&nonce+9,(uint8_t*)&packet->padding[0] + packet->payloadSize + 5, 3); - // AddLog_P(LOG_LEVEL_DEBUG,PSTR("nonce: %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x"), nonce[0], nonce[1], nonce[2], nonce[3], nonce[4], nonce[5], nonce[6], nonce[7], nonce[8], nonce[9], nonce[10], nonce[11]); + // AddLog(LOG_LEVEL_DEBUG,PSTR("nonce: %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x"), nonce[0], nonce[1], nonce[2], nonce[3], nonce[4], nonce[5], nonce[6], nonce[7], nonce[8], nonce[9], nonce[10], nonce[11]); uint8_t _bindkey[16]; for(uint32_t i=0; iMAC[0], packet->MAC[1], packet->MAC[2], packet->MAC[3], packet->MAC[4], packet->MAC[5]); - // AddLog_P(LOG_LEVEL_DEBUG,PSTR("MAC in vector: %02x %02x %02x %02x %02x %02x"), MIBLEbindKeys[i].MAC[0], MIBLEbindKeys[i].MAC[1], MIBLEbindKeys[i].MAC[2], MIBLEbindKeys[i].MAC[3], MIBLEbindKeys[i].MAC[4], MIBLEbindKeys[i].MAC[5]); + // AddLog(LOG_LEVEL_DEBUG,PSTR("MAC in packet: %02x %02x %02x %02x %02x %02x"), packet->MAC[0], packet->MAC[1], packet->MAC[2], packet->MAC[3], packet->MAC[4], packet->MAC[5]); + // AddLog(LOG_LEVEL_DEBUG,PSTR("MAC in vector: %02x %02x %02x %02x %02x %02x"), MIBLEbindKeys[i].MAC[0], MIBLEbindKeys[i].MAC[1], MIBLEbindKeys[i].MAC[2], MIBLEbindKeys[i].MAC[3], MIBLEbindKeys[i].MAC[4], MIBLEbindKeys[i].MAC[5]); // } } - // AddLog_P(LOG_LEVEL_DEBUG,PSTR("size %u"),packet->payloadSize); + // AddLog(LOG_LEVEL_DEBUG,PSTR("size %u"),packet->payloadSize); uint32_t _size; int32_t _offset; uint32_t _tagSize; @@ -862,9 +862,9 @@ int MINRFdecryptMJYD2SPacket(char *_buf, uint8_t _light, char* _output){ return 0; break; } - // AddLog_P(LOG_LEVEL_DEBUG,PSTR("size %u , offset %u"),_size,_offset); + // AddLog(LOG_LEVEL_DEBUG,PSTR("size %u , offset %u"),_size,_offset); memcpy(_output,(uint8_t*)&packet->padding[0] + packet->payloadSize - _offset, _size); - // AddLog_P(LOG_LEVEL_DEBUG,PSTR("BEARSSL: Output : %02x %02x %02x %02x %02x %02x %02x"), _output[0], _output[1],_output[2],_output[3],_output[4],_output[5],_output[6]); + // AddLog(LOG_LEVEL_DEBUG,PSTR("BEARSSL: Output : %02x %02x %02x %02x %02x %02x %02x"), _output[0], _output[1],_output[2],_output[3],_output[4],_output[5],_output[6]); br_aes_small_ctrcbc_keys keyCtx; br_aes_small_ctrcbc_init(&keyCtx, _bindkey, sizeof(_bindkey)); @@ -875,7 +875,7 @@ int MINRFdecryptMJYD2SPacket(char *_buf, uint8_t _light, char* _output){ br_ccm_aad_inject(&ctx, authData, sizeof(authData)); br_ccm_flip(&ctx); br_ccm_run(&ctx, 0, _output, _size); - // AddLog_P(LOG_LEVEL_DEBUG,PSTR("BEARSSL: Err:%i, Decrypted : %02x %02x %02x %02x %02x %02x %02x"), ret, _output[0], _output[1],_output[2],_output[3],_output[4],_output[5],_output[6]); + // AddLog(LOG_LEVEL_DEBUG,PSTR("BEARSSL: Err:%i, Decrypted : %02x %02x %02x %02x %02x %02x %02x"), ret, _output[0], _output[1],_output[2],_output[3],_output[4],_output[5],_output[6]); br_ccm_get_tag(&ctx, tag); ret = memcmp(tag,(uint8_t*)&packet->padding[0] + packet->payloadSize + 8, _tagSize); @@ -1124,7 +1124,7 @@ uint32_t MINRFgetSensorSlot(uint8_t (&_MAC)[6], uint16_t _type){ break; } MIBLEsensors.push_back(_newSensor); - AddLog_P(LOG_LEVEL_INFO,PSTR("NRF: new %s at slot: %u"),kMINRFDeviceType[_type-1], MIBLEsensors.size()-1); + AddLog(LOG_LEVEL_INFO,PSTR("NRF: new %s at slot: %u"),kMINRFDeviceType[_type-1], MIBLEsensors.size()-1); return (MIBLEsensors.size()-1); }; @@ -1203,7 +1203,7 @@ void MINRFhandleMiBeaconPacket(void){ switch(MINRF.buffer.miBeacon.type){ case 0x1: if(MINRF.buffer.miBeacon.counter==_sensorVec->lastCnt) break; - // AddLog_P(LOG_LEVEL_INFO,PSTR("NRF: YEE-RC button: %u Long: %u"), MINRF.buffer.miBeacon.Btn.num, MINRF.buffer.miBeacon.Btn.longPress); + // AddLog(LOG_LEVEL_INFO,PSTR("NRF: YEE-RC button: %u Long: %u"), MINRF.buffer.miBeacon.Btn.num, MINRF.buffer.miBeacon.Btn.longPress); _sensorVec->lastCnt=MINRF.buffer.miBeacon.counter; _sensorVec->Btn=MINRF.buffer.miBeacon.Btn.num + (MINRF.buffer.miBeacon.Btn.longPress/2)*6; _sensorVec->eventType.Btn = 1; @@ -1323,10 +1323,10 @@ void MINRFhandleNlightPacket(void){ // no MiBeacon uint32_t offset = 6; uint8_t _buf[32+offset]; MINRFrecalcBuffer((uint8_t*)&_buf,offset); - // AddLog_P(LOG_LEVEL_INFO,PSTR("NRF: NLIGHT: %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x"),_buf[0],_buf[1],_buf[2],_buf[3],_buf[4],_buf[5],_buf[6],_buf[7],_buf[8],_buf[9],_buf[10],_buf[11],_buf[12],_buf[13],_buf[14],_buf[15],_buf[16],_buf[17],_buf[18]); + // AddLog(LOG_LEVEL_INFO,PSTR("NRF: NLIGHT: %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x"),_buf[0],_buf[1],_buf[2],_buf[3],_buf[4],_buf[5],_buf[6],_buf[7],_buf[8],_buf[9],_buf[10],_buf[11],_buf[12],_buf[13],_buf[14],_buf[15],_buf[16],_buf[17],_buf[18]); uint32_t _frame_PID = _buf[15]<<24 | _buf[16]<<16 | _buf[17]<<8 | _buf[18]; if(_frame_PID!=0x4030dd03) return; // invalid packet - // AddLog_P(LOG_LEVEL_INFO,PSTR("NRF: NLIGHT:%x"),_frame_PID); + // AddLog(LOG_LEVEL_INFO,PSTR("NRF: NLIGHT:%x"),_frame_PID); uint32_t _idx = MINRF.activeLight-1; if((millis() - MIBLElights[_idx].lastTime)<1500) return; if(_buf[19]!=MIBLElights[_idx].lastCnt){ @@ -1334,7 +1334,7 @@ void MINRFhandleNlightPacket(void){ // no MiBeacon MIBLElights[_idx].events++; MIBLElights[_idx].shallSendMQTT = 1; MIBLElights[_idx].lastTime = millis(); - AddLog_P(LOG_LEVEL_DEBUG,PSTR("NRF: NLIGHT %u: events: %u, Cnt:%u"), _idx,MIBLElights[_idx].events, MIBLElights[_idx].lastCnt); + AddLog(LOG_LEVEL_DEBUG,PSTR("NRF: NLIGHT %u: events: %u, Cnt:%u"), _idx,MIBLElights[_idx].events, MIBLElights[_idx].lastCnt); } } @@ -1344,20 +1344,20 @@ void MINRFhandleMJYD2SPacket(void){ // no MiBeacon MINRFrecalcBuffer((uint8_t*)&_buf,offset); mjysd02_Packet_t *_packet = (mjysd02_Packet_t*)&_buf; if(_packet->PID!=0x07f6) return; // invalid packet - // AddLog_P(LOG_LEVEL_INFO,PSTR("NRF: MJYD2S: %02u %04x %04x %04x %02x"),_packet->payloadSize,_packet->UUID,_packet->frameCtrl,_packet->PID,_packet->frameCnt); - // AddLog_P(LOG_LEVEL_INFO,PSTR("NRF: PAYLOAD: %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x"),_packet->data[0],_packet->data[1],_packet->data[2],_packet->data[3],_packet->data[4],_packet->data[5],_packet->data[6],_packet->data[7],_packet->data[8],_packet->data[9],_packet->data[10],_packet->data[11],_packet->data[12],_packet->data[13],_packet->data[14],_packet->data[15],_packet->data[16],_packet->data[17]); + // AddLog(LOG_LEVEL_INFO,PSTR("NRF: MJYD2S: %02u %04x %04x %04x %02x"),_packet->payloadSize,_packet->UUID,_packet->frameCtrl,_packet->PID,_packet->frameCnt); + // AddLog(LOG_LEVEL_INFO,PSTR("NRF: PAYLOAD: %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x"),_packet->data[0],_packet->data[1],_packet->data[2],_packet->data[3],_packet->data[4],_packet->data[5],_packet->data[6],_packet->data[7],_packet->data[8],_packet->data[9],_packet->data[10],_packet->data[11],_packet->data[12],_packet->data[13],_packet->data[14],_packet->data[15],_packet->data[16],_packet->data[17]); uint32_t _idx = MINRF.activeLight-1; switch(_packet->frameCtrl){ case 0x5910: if(_packet->frameCnt!=MIBLElights[_idx].lastCnt){ - // AddLog_P(LOG_LEVEL_INFO,PSTR("NRF: MJYD2S after motion:%x"),_packet->frameCnt); + // AddLog(LOG_LEVEL_INFO,PSTR("NRF: MJYD2S after motion:%x"),_packet->frameCnt); MIBLElights[_idx].lastCnt = _packet->frameCnt; if(millis()-MIBLElights[_idx].lastTime>120000){ MIBLElights[_idx].eventType = 1; MIBLElights[_idx].events++; MIBLElights[_idx].shallSendMQTT = 1; MIBLElights[_idx].lastTime = millis(); - AddLog_P(LOG_LEVEL_DEBUG,PSTR("NRF: MJYD2S secondary PIR")); + AddLog(LOG_LEVEL_DEBUG,PSTR("NRF: MJYD2S secondary PIR")); } } break; @@ -1373,7 +1373,7 @@ void MINRFhandleMJYD2SPacket(void){ // no MiBeacon if(millis()-MIBLElights[_idx].lastTime>1000){ MIBLElights[_idx].eventType = 1; //PIR MIBLElights[_idx].shallSendMQTT = 1; - AddLog_P(LOG_LEVEL_DEBUG,PSTR("NRF: MJYD2S primary PIR")); + AddLog(LOG_LEVEL_DEBUG,PSTR("NRF: MJYD2S primary PIR")); MIBLElights[_idx].events++; } MIBLElights[_idx].lastTime = millis(); @@ -1394,23 +1394,23 @@ void MINRFhandleMJYD2SPacket(void){ // no MiBeacon MIBLElights[_idx].NMT = output[6]<<24 | output[5]<<16 | output[4]<<8 | output[3]; MIBLElights[_idx].eventType = 3; // NMT 0, 120, 300, 600, 1800, ... seconds MIBLElights[_idx].shallSendMQTT = 1; - // AddLog_P(LOG_LEVEL_DEBUG,PSTR("NRF: MJYD2S NMT: %u"), MIBLElights[_idx].NMT ); + // AddLog(LOG_LEVEL_DEBUG,PSTR("NRF: MJYD2S NMT: %u"), MIBLElights[_idx].NMT ); break; } } } - // AddLog_P(LOG_LEVEL_INFO,PSTR("NRF: NLIGHT:%x"),_frame_PID); + // AddLog(LOG_LEVEL_INFO,PSTR("NRF: NLIGHT:%x"),_frame_PID); } void MINRFhandleLightPacket(void){ switch(MIBLElights[MINRF.activeLight-1].type){ case NLIGHT: - // AddLog_P(LOG_LEVEL_INFO,PSTR("NRF: NLIGHT!!")); + // AddLog(LOG_LEVEL_INFO,PSTR("NRF: NLIGHT!!")); MINRFhandleNlightPacket(); break; case MJYD2S: - // AddLog_P(LOG_LEVEL_INFO,PSTR("NRF: MJYD2S !!")); + // AddLog(LOG_LEVEL_INFO,PSTR("NRF: MJYD2S !!")); MINRFhandleMJYD2SPacket(); break; } @@ -1420,7 +1420,7 @@ void MINRFhandleLightPacket(void){ void MINRFaddLight(uint8_t _MAC[], uint8_t _type){ // no MiBeacon for(uint32_t i=0; iMAC, 0x0a1c); // This must be a hard-coded fake ID - // AddLog_P(LOG_LEVEL_DEBUG,PSTR("known %s at slot %u"), kMINRFDeviceType[MIBLEsensors[_slot].type-1],_slot); - // AddLog_P(LOG_LEVEL_INFO,PSTR("NRF: ATC: %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x"),MINRF.buffer.raw[0],MINRF.buffer.raw[1],MINRF.buffer.raw[2],MINRF.buffer.raw[3],MINRF.buffer.raw[4],MINRF.buffer.raw[5],MINRF.buffer.raw[6],MINRF.buffer.raw[7],MINRF.buffer.raw[8],MINRF.buffer.raw[9],MINRF.buffer.raw[10],MINRF.buffer.raw[11]); + // AddLog(LOG_LEVEL_DEBUG,PSTR("known %s at slot %u"), kMINRFDeviceType[MIBLEsensors[_slot].type-1],_slot); + // AddLog(LOG_LEVEL_INFO,PSTR("NRF: ATC: %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x"),MINRF.buffer.raw[0],MINRF.buffer.raw[1],MINRF.buffer.raw[2],MINRF.buffer.raw[3],MINRF.buffer.raw[4],MINRF.buffer.raw[5],MINRF.buffer.raw[6],MINRF.buffer.raw[7],MINRF.buffer.raw[8],MINRF.buffer.raw[9],MINRF.buffer.raw[10],MINRF.buffer.raw[11]); if(_slot==0xff) return; MIBLEsensors[_slot].temp = (float)(__builtin_bswap16(_packet->temp))/10.0f; @@ -1472,7 +1472,7 @@ void MINRF_EVERY_50_MSECOND() { // Every 50mseconds MINRF.mode.shallTriggerTele = 0; } // DEBUG_SENSOR_LOG(PSTR("NRF: nothing received")); - // if (MINRF.packetMode==ATC) AddLog_P(LOG_LEVEL_INFO,PSTR("no ATC..")); + // if (MINRF.packetMode==ATC) AddLog(LOG_LEVEL_INFO,PSTR("no ATC..")); } else { @@ -1738,9 +1738,8 @@ void MINRFShow(bool json) ||(hass_mode==2) #endif //USE_HOME_ASSISTANT ) { - char temperature[FLOATSZ]; - dtostrfd(MIBLEsensors[i].temp, Settings.flag2.temperature_resolution, temperature); - ResponseAppend_P(PSTR(",\"" D_JSON_TEMPERATURE "\":%s"), temperature); + ResponseAppend_P(PSTR(",\"" D_JSON_TEMPERATURE "\":%*_f"), + Settings.flag2.temperature_resolution, &MIBLEsensors[i].temp); } } } @@ -1897,9 +1896,7 @@ void MINRFShow(bool json) if (MIBLEsensors[i].type==YEERC) continue; if (MIBLEsensors[i].type==FLORA){ if(!isnan(MIBLEsensors[i].temp)){ - char temperature[FLOATSZ]; - dtostrfd(MIBLEsensors[i].temp, Settings.flag2.temperature_resolution, temperature); - WSContentSend_PD(HTTP_SNS_TEMP, kMINRFDeviceType[MIBLEsensors[i].type-1], temperature, TempUnit()); + WSContentSend_Temp(kMINRFDeviceType[MIBLEsensors[i].type-1], MIBLEsensors[i].temp); } if(MIBLEsensors[i].lux!=0xffffffff){ // this is the error code -> no valid value WSContentSend_PD(HTTP_SNS_ILLUMINANCE, kMINRFDeviceType[MIBLEsensors[i].type-1], MIBLEsensors[i].lux); diff --git a/tasmota/xsns_62_MI_HM10.ino b/tasmota/xsns_62_MI_HM10.ino index af3ce56ca..02298380e 100644 --- a/tasmota/xsns_62_MI_HM10.ino +++ b/tasmota/xsns_62_MI_HM10.ino @@ -624,7 +624,7 @@ uint32_t MIBLEgetSensorSlot(uint8_t (&_MAC)[6], uint16_t _type, int _rssi){ break; } MIBLEsensors.push_back(_newSensor); - AddLog_P(LOG_LEVEL_DEBUG,PSTR("%s: new %s at slot: %u"),D_CMND_HM10, kHM10DeviceType[_type-1],MIBLEsensors.size()-1); + AddLog(LOG_LEVEL_DEBUG,PSTR("%s: new %s at slot: %u"),D_CMND_HM10, kHM10DeviceType[_type-1],MIBLEsensors.size()-1); return MIBLEsensors.size()-1; }; @@ -638,7 +638,7 @@ void HM10SerialInit(void) { HM10.serialSpeed = HM10_BAUDRATE; HM10Serial = new TasmotaSerial(Pin(GPIO_HM10_RX), Pin(GPIO_HM10_TX), 1, 0, HM10_MAX_RX_BUF); if (HM10Serial->begin(HM10.serialSpeed)) { - AddLog_P(LOG_LEVEL_DEBUG, PSTR("%s start serial communication fixed to 115200 baud"),D_CMND_HM10); + AddLog(LOG_LEVEL_DEBUG, PSTR("%s start serial communication fixed to 115200 baud"),D_CMND_HM10); if (HM10Serial->hardwareSerial()) { ClaimSerial(); DEBUG_SENSOR_LOG(PSTR("%s: claim HW"),D_CMND_HM10); @@ -681,7 +681,7 @@ void HM10parseMiBeacon(char * _buf, uint32_t _slot){ HM10_ReverseMAC(_beacon.MAC); // if(memcmp(_beacon.MAC,MIBLEsensors[_slot].MAC,sizeof(_beacon.MAC))!=0){ // if (MIBLEsensors[_slot].showedUp>3) return; // probably false alarm from a damaged packet - // AddLog_P(LOG_LEVEL_DEBUG, PSTR("%s: remove garbage sensor"),D_CMND_HM10); + // AddLog(LOG_LEVEL_DEBUG, PSTR("%s: remove garbage sensor"),D_CMND_HM10); // DEBUG_SENSOR_LOG(PSTR("%s i: %x %x %x %x %x %x"),D_CMND_HM10, MIBLEsensors[_slot].MAC[5], MIBLEsensors[_slot].MAC[4],MIBLEsensors[_slot].MAC[3],MIBLEsensors[_slot].MAC[2],MIBLEsensors[_slot].MAC[1],MIBLEsensors[_slot].MAC[0]); // DEBUG_SENSOR_LOG(PSTR("%s n: %x %x %x %x %x %x"),D_CMND_HM10, _beacon.MAC[5], _beacon.MAC[4], _beacon.MAC[3],_beacon.MAC[2],_beacon.MAC[1],_beacon.MAC[0]); // MIBLEsensors.erase(MIBLEsensors.begin()+_slot); @@ -697,7 +697,7 @@ void HM10parseMiBeacon(char * _buf, uint32_t _slot){ DEBUG_SENSOR_LOG(PSTR("LYWSD03 and CGD1 no support for MiBeacon, type %u"),MIBLEsensors[_slot].type); return; } - AddLog_P(LOG_LEVEL_DEBUG, PSTR("%s: %s mibeacon type: %x"),D_CMND_HM10, kHM10DeviceType[MIBLEsensors[_slot].type-1], _beacon.type); + AddLog(LOG_LEVEL_DEBUG, PSTR("%s: %s mibeacon type: %x"),D_CMND_HM10, kHM10DeviceType[MIBLEsensors[_slot].type-1], _beacon.type); DEBUG_SENSOR_LOG(PSTR("%s at slot %u"), kHM10DeviceType[MIBLEsensors[_slot].type-1],_slot); switch(_beacon.type){ @@ -848,7 +848,7 @@ void HM10readHT_LY(char *_buf){ if(_buf[0]==0x4f && _buf[1]==0x4b) return; // "OK" if(_buf[0] != 0 && _buf[1] != 0){ LYWSD0x_HT_t *packet = (LYWSD0x_HT_t*)_buf; - AddLog_P(LOG_LEVEL_DEBUG, PSTR("%s: T * 100: %u, H: %u"),D_CMND_HM10,packet->temp,packet->hum); + AddLog(LOG_LEVEL_DEBUG, PSTR("%s: T * 100: %u, H: %u"),D_CMND_HM10,packet->temp,packet->hum); uint32_t _slot = HM10.state.sensor; DEBUG_SENSOR_LOG(PSTR("MIBLE: Sensor slot: %u"), _slot); @@ -881,7 +881,7 @@ void HM10readHT_CGD1(char *_buf){ if(_buf[0] == 0){ if(_buf[1]==0 && _buf[2]==0 && _buf[3]==0 && _buf[4]==0) return; CGD1_HT_t *_packet = (CGD1_HT_t*)_buf; - AddLog_P(LOG_LEVEL_DEBUG, PSTR("%s: T * 100: %u, H * 100: %u"),D_CMND_HM10,_packet->temp,_packet->hum); + AddLog(LOG_LEVEL_DEBUG, PSTR("%s: T * 100: %u, H * 100: %u"),D_CMND_HM10,_packet->temp,_packet->hum); uint32_t _slot = HM10.state.sensor; DEBUG_SENSOR_LOG(PSTR("MIBLE: Sensor slot: %u"), _slot); @@ -911,7 +911,7 @@ void HM10readHT_MJ_HT_V1(char *_buf){ // 0123456789012 uint32_t _temp = (atoi(_buf+2) * 10) + atoi(_buf+5); uint32_t _hum = (atoi(_buf+9) * 10) + atoi(_buf+12); - AddLog_P(LOG_LEVEL_DEBUG, PSTR("%s: T * 10: %u, H * 10: %u"),D_CMND_HM10,_temp,_hum); + AddLog(LOG_LEVEL_DEBUG, PSTR("%s: T * 10: %u, H * 10: %u"),D_CMND_HM10,_temp,_hum); uint32_t _slot = HM10.state.sensor; DEBUG_SENSOR_LOG(PSTR("MIBLE: Sensor slot: %u"), _slot); @@ -937,7 +937,7 @@ void HM10readTLMF(char *_buf){ AddLogBuffer(LOG_LEVEL_DEBUG, (uint8_t*)_buf,16); Flora_TLMF_t *_packet = (Flora_TLMF_t*)_buf; if(_packet->ID==0xFB003C02){ // this is a magic word ... hopefully independent of FW version - AddLog_P(LOG_LEVEL_DEBUG, PSTR("%s: T * 10: %u, L: %u, M: %u, F: %u"),D_CMND_HM10,_packet->temp,_packet->lux,_packet->moist,_packet->fert); + AddLog(LOG_LEVEL_DEBUG, PSTR("%s: T * 10: %u, L: %u, M: %u, F: %u"),D_CMND_HM10,_packet->temp,_packet->lux,_packet->moist,_packet->fert); uint32_t _slot = HM10.state.sensor; DEBUG_SENSOR_LOG(PSTR("MIBLE: Sensor slot: %u"), _slot); MIBLEsensors[_slot].showedUp=255; // this sensor is real @@ -968,7 +968,7 @@ bool HM10readBat(char *_buf){ // if (MIBLEsensors[_slot].type == LYWSD03MMC || MIBLEsensors[_slot].type == MHOC401) return true; // } if(_buf[0] != 0){ - AddLog_P(LOG_LEVEL_DEBUG,PSTR("%s: Battery: %u"),D_CMND_HM10,_buf[0]); + AddLog(LOG_LEVEL_DEBUG,PSTR("%s: Battery: %u"),D_CMND_HM10,_buf[0]); DEBUG_SENSOR_LOG(PSTR("MIBLE: Sensor slot: %u"), _slot); if(_buf[0]<101){ MIBLEsensors[_slot].bat=_buf[0]; @@ -1008,13 +1008,13 @@ void HM10HandleGenericBeacon(void){ } // else handle scan if(MIBLEscanResult.size()>19) { - AddLog_P(LOG_LEVEL_INFO,PSTR("HM10: Scan buffer full")); + AddLog(LOG_LEVEL_INFO,PSTR("HM10: Scan buffer full")); HM10.state.beaconScanCounter = 1; return; } for(auto _scanResult : MIBLEscanResult){ if(memcmp(HM10.rxAdvertisement.MAC,_scanResult.MAC,6)==0){ - // AddLog_P(LOG_LEVEL_INFO,PSTR("HM10: known device")); + // AddLog(LOG_LEVEL_INFO,PSTR("HM10: known device")); return; } } @@ -1036,12 +1036,12 @@ void HM10addBeacon(uint8_t index, char* data){ _new.time = 0; if(memcmp(_empty,_new.MAC,6) == 0){ _new.active = false; - AddLog_P(LOG_LEVEL_INFO,PSTR("HM10: beacon%u deactivated"), index); + AddLog(LOG_LEVEL_INFO,PSTR("HM10: beacon%u deactivated"), index); } else{ _new.active = true; HM10.mode.activeBeacon = 1; - AddLog_P(LOG_LEVEL_INFO,PSTR("HM10: beacon added with MAC: %s"), _MAC); + AddLog(LOG_LEVEL_INFO,PSTR("HM10: beacon added with MAC: %s"), _MAC); } } @@ -1105,7 +1105,7 @@ bool HM10SerialHandleFeedback(){ // every 50 milliseconds *_rx= HM10Serial->read(); if(i==18){ if(memcmp(HM10.rxBuffer+4,"ISA:",4)==0){ //last 4 bytes of "OK+DISA:" should be safe enough - // AddLog_P(LOG_LEVEL_DEBUG, PSTR("%s packet size: %u"),D_CMND_HM10,HM10.rxBuffer[16]); + // AddLog(LOG_LEVEL_DEBUG, PSTR("%s packet size: %u"),D_CMND_HM10,HM10.rxBuffer[16]); _targetsize = HM10.rxBuffer[16] + 19; // this is the size byte according to HM-10 docs if(_targetsize>64) _targetsize=64; memcpy(HM10.rxAdvertisement.MAC,HM10.rxBuffer+8,6); @@ -1180,7 +1180,7 @@ bool HM10SerialHandleFeedback(){ // every 50 milliseconds HM10HandleGenericBeacon(); } uint16_t _type = (uint8_t)HM10.rxAdvertisement.svcData[5]*256 + (uint8_t)HM10.rxAdvertisement.svcData[4]; - // AddLog_P(LOG_LEVEL_DEBUG, PSTR("%04x %02x %04x %04x %04x"),HM10.rxAdvertisement.UUID,HM10.rxAdvertisement.TX,HM10.rxAdvertisement.CID,HM10.rxAdvertisement.SVC, _type); + // AddLog(LOG_LEVEL_DEBUG, PSTR("%04x %02x %04x %04x %04x"),HM10.rxAdvertisement.UUID,HM10.rxAdvertisement.TX,HM10.rxAdvertisement.CID,HM10.rxAdvertisement.SVC, _type); if(HM10.rxAdvertisement.SVC==0x181a) _type = 0xa1c; else if(HM10.rxAdvertisement.SVC==0xfdcd) _type = 0x0576; uint16_t _slot = MIBLEgetSensorSlot(HM10.rxAdvertisement.MAC, _type, HM10.rxAdvertisement.RSSI); @@ -1199,9 +1199,9 @@ bool HM10SerialHandleFeedback(){ // every 50 milliseconds break; case none: if(success) { - AddLog_P(LOG_LEVEL_DEBUG, PSTR("%s: response: %s"),D_CMND_HM10, (char *)HM10.rxBuffer); + AddLog(LOG_LEVEL_DEBUG, PSTR("%s: response: %s"),D_CMND_HM10, (char *)HM10.rxBuffer); // for(uint32_t j = 0; jwrite("AT+ROLE1"); break; case TASK_HM10_IMME1: - AddLog_P(LOG_LEVEL_DEBUG, PSTR("%s: set imme to 1"),D_CMND_HM10); + AddLog(LOG_LEVEL_DEBUG, PSTR("%s: set imme to 1"),D_CMND_HM10); HM10.current_task_delay = 5; // set task delay HM10_TaskReplaceInSlot(TASK_HM10_FEEDBACK,i); runningTaskLoop = false; HM10Serial->write("AT+IMME1"); break; case TASK_HM10_DISC: - AddLog_P(LOG_LEVEL_DEBUG, PSTR("%s: start discovery"),D_CMND_HM10); + AddLog(LOG_LEVEL_DEBUG, PSTR("%s: start discovery"),D_CMND_HM10); HM10.current_task_delay = 90; // set task delay HM10_TaskReplaceInSlot(TASK_HM10_FEEDBACK,i); runningTaskLoop = false; @@ -1247,14 +1247,14 @@ void HM10_TaskEvery100ms(){ HM10Serial->write("AT+DISA?"); break; case TASK_HM10_VERSION: - AddLog_P(LOG_LEVEL_DEBUG, PSTR("%s: read version"),D_CMND_HM10); + AddLog(LOG_LEVEL_DEBUG, PSTR("%s: read version"),D_CMND_HM10); HM10.current_task_delay = 5; // set task delay HM10_TaskReplaceInSlot(TASK_HM10_FEEDBACK,i); runningTaskLoop = false; HM10Serial->write("AT+VERR?"); break; case TASK_HM10_NAME: - AddLog_P(LOG_LEVEL_DEBUG, PSTR("%s: read name"),D_CMND_HM10); + AddLog(LOG_LEVEL_DEBUG, PSTR("%s: read name"),D_CMND_HM10); HM10.current_task_delay = 5; // set task delay HM10_TaskReplaceInSlot(TASK_HM10_FEEDBACK,i); runningTaskLoop = false; @@ -1263,7 +1263,7 @@ void HM10_TaskEvery100ms(){ case TASK_HM10_CONN: char _con[20]; sprintf_P(_con,"AT+CON%02x%02x%02x%02x%02x%02x",MIBLEsensors[HM10.state.sensor].MAC[0],MIBLEsensors[HM10.state.sensor].MAC[1],MIBLEsensors[HM10.state.sensor].MAC[2],MIBLEsensors[HM10.state.sensor].MAC[3],MIBLEsensors[HM10.state.sensor].MAC[4],MIBLEsensors[HM10.state.sensor].MAC[5]); - AddLog_P(LOG_LEVEL_DEBUG, PSTR("%s: %s connect %s"),D_CMND_HM10,kHM10DeviceType[MIBLEsensors[HM10.state.sensor].type-1],_con); + AddLog(LOG_LEVEL_DEBUG, PSTR("%s: %s connect %s"),D_CMND_HM10,kHM10DeviceType[MIBLEsensors[HM10.state.sensor].type-1],_con); HM10.current_task_delay = 2; // set task delay HM10_TaskReplaceInSlot(TASK_HM10_FEEDBACK,i); runningTaskLoop = false; @@ -1272,21 +1272,21 @@ void HM10_TaskEvery100ms(){ HM10.mode.connected = true; break; case TASK_HM10_DISCONN: - AddLog_P(LOG_LEVEL_DEBUG, PSTR("%s: disconnect"),D_CMND_HM10); + AddLog(LOG_LEVEL_DEBUG, PSTR("%s: disconnect"),D_CMND_HM10); HM10.current_task_delay = 5; // set task delay HM10_TaskReplaceInSlot(TASK_HM10_FEEDBACK,i); runningTaskLoop = false; HM10Serial->write("AT"); break; case TASK_HM10_RESET: - AddLog_P(LOG_LEVEL_DEBUG, PSTR("%s: Reset Device"),D_CMND_HM10); + AddLog(LOG_LEVEL_DEBUG, PSTR("%s: Reset Device"),D_CMND_HM10); HM10Serial->write("AT+RESET"); HM10.current_task_delay = 5; // set task delay HM10_TaskReplaceInSlot(TASK_HM10_FEEDBACK,i); runningTaskLoop = false; break; case TASK_HM10_SUB_L3: - AddLog_P(LOG_LEVEL_DEBUG, PSTR("%s: subscribe"),D_CMND_HM10); + AddLog(LOG_LEVEL_DEBUG, PSTR("%s: subscribe"),D_CMND_HM10); HM10.current_task_delay = 25; // set task delay HM10_TaskReplaceInSlot(TASK_HM10_FEEDBACK,i); HM10.mode.awaiting = tempHumLY; @@ -1294,7 +1294,7 @@ void HM10_TaskEvery100ms(){ HM10Serial->write("AT+NOTIFY_ON0037"); break; case TASK_HM10_UN_L3: - AddLog_P(LOG_LEVEL_DEBUG, PSTR("%s: un-subscribe"),D_CMND_HM10); + AddLog(LOG_LEVEL_DEBUG, PSTR("%s: un-subscribe"),D_CMND_HM10); HM10.current_task_delay = 5; // set task delay HM10_TaskReplaceInSlot(TASK_HM10_FEEDBACK,i); runningTaskLoop = false; @@ -1302,7 +1302,7 @@ void HM10_TaskEvery100ms(){ HM10Serial->write("AT+NOTIFYOFF0037"); break; case TASK_HM10_SUB_L2: - AddLog_P(LOG_LEVEL_DEBUG, PSTR("%s: subscribe"),D_CMND_HM10); + AddLog(LOG_LEVEL_DEBUG, PSTR("%s: subscribe"),D_CMND_HM10); HM10.current_task_delay = 85; // set task delay HM10.mode.awaiting = tempHumLY; HM10_TaskReplaceInSlot(TASK_HM10_FEEDBACK,i); @@ -1311,7 +1311,7 @@ void HM10_TaskEvery100ms(){ else HM10Serial->write("AT+NOTIFY_ON004B"); //MHO-C303 break; case TASK_HM10_UN_L2: - AddLog_P(LOG_LEVEL_DEBUG, PSTR("%s: un-subscribe"),D_CMND_HM10); + AddLog(LOG_LEVEL_DEBUG, PSTR("%s: un-subscribe"),D_CMND_HM10); HM10.current_task_delay = 5; // set task delay HM10_TaskReplaceInSlot(TASK_HM10_FEEDBACK,i); runningTaskLoop = false; @@ -1320,7 +1320,7 @@ void HM10_TaskEvery100ms(){ else HM10Serial->write("AT+NOTIFY_OFF004B"); //MHO-C303 break; case TASK_HM10_TIME_L2: - AddLog_P(LOG_LEVEL_DEBUG, PSTR("%s: set time"),D_CMND_HM10); + AddLog(LOG_LEVEL_DEBUG, PSTR("%s: set time"),D_CMND_HM10); HM10.current_task_delay = 5; // set task delay HM10_TaskReplaceInSlot(TASK_HM10_FEEDBACK,i); runningTaskLoop = false; @@ -1328,10 +1328,10 @@ void HM10_TaskEvery100ms(){ HM10Serial->write("AT+SEND_DATAWR002F"); HM10Serial->write(HM10.timebuf,4); HM10Serial->write(Rtc.time_timezone / 60); - AddLog_P(LOG_LEVEL_DEBUG,PSTR("%s Time-string: %x%x%x%x%x"),D_CMND_HM10, HM10.timebuf[0],HM10.timebuf[1],HM10.timebuf[2],HM10.timebuf[3],(Rtc.time_timezone /60)); + AddLog(LOG_LEVEL_DEBUG,PSTR("%s Time-string: %x%x%x%x%x"),D_CMND_HM10, HM10.timebuf[0],HM10.timebuf[1],HM10.timebuf[2],HM10.timebuf[3],(Rtc.time_timezone /60)); break; // case TASK_HM10_READ_BT_L3: - // AddLog_P(LOG_LEVEL_DEBUG, PSTR("%s: read handle 003A"),D_CMND_HM10); + // AddLog(LOG_LEVEL_DEBUG, PSTR("%s: read handle 003A"),D_CMND_HM10); // HM10.current_task_delay = 2; // set task delay // HM10_TaskReplaceInSlot(TASK_HM10_FEEDBACK,i); // runningTaskLoop = false; @@ -1339,7 +1339,7 @@ void HM10_TaskEvery100ms(){ // HM10.mode.awaiting = bat; // break; case TASK_HM10_READ_BT_L2: - AddLog_P(LOG_LEVEL_DEBUG, PSTR("%s: read handle 0043"),D_CMND_HM10); + AddLog(LOG_LEVEL_DEBUG, PSTR("%s: read handle 0043"),D_CMND_HM10); HM10.current_task_delay = 2; // set task delay HM10_TaskReplaceInSlot(TASK_HM10_FEEDBACK,i); runningTaskLoop = false; @@ -1348,7 +1348,7 @@ void HM10_TaskEvery100ms(){ HM10.mode.awaiting = bat; break; case TASK_HM10_READ_BF_FL: - AddLog_P(LOG_LEVEL_DEBUG, PSTR("%s: read handle 0038"),D_CMND_HM10); + AddLog(LOG_LEVEL_DEBUG, PSTR("%s: read handle 0038"),D_CMND_HM10); HM10.current_task_delay = 2; // set task delay HM10_TaskReplaceInSlot(TASK_HM10_FEEDBACK,i); runningTaskLoop = false; @@ -1356,7 +1356,7 @@ void HM10_TaskEvery100ms(){ HM10.mode.awaiting = bat; break; case TASK_HM10_CALL_TLMF_FL: - AddLog_P(LOG_LEVEL_DEBUG, PSTR("%s: write to handle 0033"),D_CMND_HM10); + AddLog(LOG_LEVEL_DEBUG, PSTR("%s: write to handle 0033"),D_CMND_HM10); HM10.current_task_delay = 5; // set task delay HM10_TaskReplaceInSlot(TASK_HM10_READ_TLMF_FL,i); runningTaskLoop = false; @@ -1366,7 +1366,7 @@ void HM10_TaskEvery100ms(){ HM10.mode.awaiting = none; break; case TASK_HM10_READ_TLMF_FL: - AddLog_P(LOG_LEVEL_DEBUG, PSTR("%s: read handle 0035"),D_CMND_HM10); + AddLog(LOG_LEVEL_DEBUG, PSTR("%s: read handle 0035"),D_CMND_HM10); HM10.current_task_delay = 2; // set task delay HM10_TaskReplaceInSlot(TASK_HM10_FEEDBACK,i); runningTaskLoop = false; @@ -1374,7 +1374,7 @@ void HM10_TaskEvery100ms(){ HM10.mode.awaiting = TLMF; break; case TASK_HM10_READ_B_CGD1: - AddLog_P(LOG_LEVEL_DEBUG, PSTR("%s: read handle 0011"),D_CMND_HM10); + AddLog(LOG_LEVEL_DEBUG, PSTR("%s: read handle 0011"),D_CMND_HM10); HM10.current_task_delay = 2; // set task delay HM10_TaskReplaceInSlot(TASK_HM10_FEEDBACK,i); runningTaskLoop = false; @@ -1382,7 +1382,7 @@ void HM10_TaskEvery100ms(){ HM10.mode.awaiting = bat; break; case TASK_HM10_SUB_HT_CGD1: - AddLog_P(LOG_LEVEL_DEBUG, PSTR("%s: subscribe 4b"),D_CMND_HM10); + AddLog(LOG_LEVEL_DEBUG, PSTR("%s: subscribe 4b"),D_CMND_HM10); HM10.current_task_delay = 5; // set task delay HM10_TaskReplaceInSlot(TASK_HM10_FEEDBACK,i); runningTaskLoop = false; @@ -1390,7 +1390,7 @@ void HM10_TaskEvery100ms(){ HM10Serial->write("AT+NOTIFY_ON004b"); break; case TASK_HM10_UN_HT_CGD1: - AddLog_P(LOG_LEVEL_DEBUG, PSTR("%s: un-subscribe 4b"),D_CMND_HM10); + AddLog(LOG_LEVEL_DEBUG, PSTR("%s: un-subscribe 4b"),D_CMND_HM10); HM10.current_task_delay = 5; // set task delay HM10_TaskReplaceInSlot(TASK_HM10_FEEDBACK,i); runningTaskLoop = false; @@ -1398,14 +1398,14 @@ void HM10_TaskEvery100ms(){ HM10Serial->write("AT+NOTIFYOFF004b"); break; case TASK_HM10_SCAN9: - AddLog_P(LOG_LEVEL_DEBUG, PSTR("%s: scan time to 9"),D_CMND_HM10); + AddLog(LOG_LEVEL_DEBUG, PSTR("%s: scan time to 9"),D_CMND_HM10); HM10.current_task_delay = 2; // set task delay HM10_TaskReplaceInSlot(TASK_HM10_FEEDBACK,i); runningTaskLoop = false; HM10Serial->write("AT+SCAN9"); break; case TASK_HM10_READ_B_MJ: - AddLog_P(LOG_LEVEL_DEBUG, PSTR("%s: read handle 0x18"),D_CMND_HM10); + AddLog(LOG_LEVEL_DEBUG, PSTR("%s: read handle 0x18"),D_CMND_HM10); HM10.current_task_delay = 2; // set task delay HM10_TaskReplaceInSlot(TASK_HM10_FEEDBACK,i); runningTaskLoop = false; @@ -1413,7 +1413,7 @@ void HM10_TaskEvery100ms(){ HM10.mode.awaiting = bat; break; case TASK_HM10_SUB_HT_MJ: - AddLog_P(LOG_LEVEL_DEBUG, PSTR("%s: subscribe to 0x0f"),D_CMND_HM10); + AddLog(LOG_LEVEL_DEBUG, PSTR("%s: subscribe to 0x0f"),D_CMND_HM10); HM10.current_task_delay = 10; // set task delay HM10_TaskReplaceInSlot(TASK_HM10_FEEDBACK,i); runningTaskLoop = false; @@ -1421,22 +1421,22 @@ void HM10_TaskEvery100ms(){ HM10.mode.awaiting = tempHumMJ; break; case TASK_HM10_FEEDBACK: - AddLog_P(LOG_LEVEL_DEBUG, PSTR("%s: get response"),D_CMND_HM10); + AddLog(LOG_LEVEL_DEBUG, PSTR("%s: get response"),D_CMND_HM10); HM10SerialHandleFeedback(); HM10.current_task_delay = HM10_TASK_LIST[i+1][1];; // set task delay HM10_TASK_LIST[i][0] = TASK_HM10_DONE; // no feedback for reset runningTaskLoop = false; break; case TASK_HM10_STATUS_EVENT: - AddLog_P(LOG_LEVEL_DEBUG, PSTR("%s: show status"),D_CMND_HM10); + AddLog(LOG_LEVEL_DEBUG, PSTR("%s: show status"),D_CMND_HM10); HM10StatusInfo(); HM10.current_task_delay = HM10_TASK_LIST[i+1][1];; // set task delay HM10_TASK_LIST[i][0] = TASK_HM10_DONE; // no feedback for reset runningTaskLoop = false; break; case TASK_HM10_DONE: // this entry was already handled - // AddLog_P(LOG_LEVEL_DEBUG, PSTR("%sFound done HM10_TASK"),D_CMND_HM10); - // AddLog_P(LOG_LEVEL_DEBUG, PSTR("%snext slot:%u, i: %u"),D_CMND_HM10, HM10_TASK_LIST[i+1][0],i); + // AddLog(LOG_LEVEL_DEBUG, PSTR("%sFound done HM10_TASK"),D_CMND_HM10); + // AddLog(LOG_LEVEL_DEBUG, PSTR("%snext slot:%u, i: %u"),D_CMND_HM10, HM10_TASK_LIST[i+1][0],i); if(HM10_TASK_LIST[i+1][0] == TASK_HM10_NOTASK) { // check the next entry and if there is none DEBUG_SENSOR_LOG(PSTR("%sno Tasks left"),D_CMND_HM10); DEBUG_SENSOR_LOG(PSTR("%sHM10_TASK_DONE current slot %u"),D_CMND_HM10, i); @@ -1462,7 +1462,7 @@ void HM10StatusInfo() { /* char stemp[20]; snprintf_P(stemp, sizeof(stemp),PSTR("{%s:{\"found\": %u}}"),D_CMND_HM10, MIBLEsensors.size()); - AddLog_P(LOG_LEVEL_INFO, stemp); + AddLog(LOG_LEVEL_INFO, stemp); RulesProcessEvent(stemp); */ Response_P(PSTR("{\"%s\":{\"found\":%u}}"), D_CMND_HM10, MIBLEsensors.size()); @@ -1688,7 +1688,7 @@ void CmndHM10Block(void){ switch (XdrvMailbox.index) { case 0: MIBLEBlockList.clear(); - // AddLog_P(LOG_LEVEL_INFO,PSTR("HM10: size of ilist: %u"), MIBLEBlockList.size()); + // AddLog(LOG_LEVEL_INFO,PSTR("HM10: size of ilist: %u"), MIBLEBlockList.size()); ResponseCmndIdxChar(PSTR("block list cleared")); break; case 1: @@ -1718,7 +1718,7 @@ void CmndHM10Block(void){ ResponseCmndIdxChar(XdrvMailbox.data); HM10removeMIBLEsensor(_MACasBytes.buf); } - // AddLog_P(LOG_LEVEL_INFO,PSTR("HM10: size of ilist: %u"), MIBLEBlockList.size()); + // AddLog(LOG_LEVEL_INFO,PSTR("HM10: size of ilist: %u"), MIBLEBlockList.size()); break; } } @@ -1812,9 +1812,8 @@ void HM10Show(bool json) ||(hass_mode!=-1) #endif //USE_HOME_ASSISTANT ) { - char temperature[FLOATSZ]; - dtostrfd(MIBLEsensors[i].temp, Settings.flag2.temperature_resolution, temperature); - ResponseAppend_P(PSTR(",\"" D_JSON_TEMPERATURE "\":%s"), temperature); + ResponseAppend_P(PSTR(",\"" D_JSON_TEMPERATURE "\":%*_f"), + Settings.flag2.temperature_resolution, &MIBLEsensors[i].temp); } } } @@ -1967,9 +1966,7 @@ void HM10Show(bool json) WSContentSend_PD(HTTP_HM10_MAC, kHM10DeviceType[MIBLEsensors[i].type-1], D_MAC_ADDRESS, _MAC); if (MIBLEsensors[i].type==FLORA){ if(!isnan(MIBLEsensors[i].temp)){ - char temperature[FLOATSZ]; - dtostrfd(MIBLEsensors[i].temp, Settings.flag2.temperature_resolution, temperature); - WSContentSend_PD(HTTP_SNS_TEMP, kHM10DeviceType[MIBLEsensors[i].type-1], temperature, TempUnit()); + WSContentSend_Temp(kHM10DeviceType[MIBLEsensors[i].type-1], MIBLEsensors[i].temp); } if(MIBLEsensors[i].lux!=0x00ffffff){ // this is the error code -> no valid value WSContentSend_PD(HTTP_SNS_ILLUMINANCE, kHM10DeviceType[MIBLEsensors[i].type-1], MIBLEsensors[i].lux); diff --git a/tasmota/xsns_62_MI_ESP32.ino b/tasmota/xsns_62_esp32_mi.ino similarity index 91% rename from tasmota/xsns_62_MI_ESP32.ino rename to tasmota/xsns_62_esp32_mi.ino index 689b5a72a..8665d6905 100644 --- a/tasmota/xsns_62_MI_ESP32.ino +++ b/tasmota/xsns_62_esp32_mi.ino @@ -1,5 +1,7 @@ /* - xsns_62_MI_ESP32.ino - MI-BLE-sensors via ESP32 support for Tasmota + xsns_62_esp32_mi.ino - MI-BLE-sensors via ESP32 support for Tasmota + enabled by ESP32 && !USE_BLE_ESP32 + if (ESP32 && USE_BLE_ESP32) then xsns_62_esp32_mi_ble.ino is used Copyright (C) 2021 Christian Baars and Theo Arends @@ -45,6 +47,7 @@ forked - from arendst/tasmota - https://github.com/arendst/Tasmota */ +#ifndef USE_BLE_ESP32 #ifdef ESP32 // ESP32 only. Use define USE_HM10 for ESP8266 support #ifdef USE_MI_ESP32 @@ -384,14 +387,14 @@ enum MI32_BEACON_CMND { class MI32SensorCallback : public NimBLEClientCallbacks { void onConnect(NimBLEClient* pclient) { - AddLog_P(LOG_LEVEL_DEBUG,PSTR("connected %s"), kMI32DeviceType[(MIBLEsensors[MI32.state.sensor].type)-1]); + AddLog(LOG_LEVEL_DEBUG,PSTR("connected %s"), kMI32DeviceType[(MIBLEsensors[MI32.state.sensor].type)-1]); MI32.mode.willConnect = 0; MI32.mode.connected = 1; } void onDisconnect(NimBLEClient* pclient) { MI32.mode.connected = 0; MI32.mode.willReadBatt = 0; - AddLog_P(LOG_LEVEL_DEBUG,PSTR("disconnected %s"), kMI32DeviceType[(MIBLEsensors[MI32.state.sensor].type)-1]); + AddLog(LOG_LEVEL_DEBUG,PSTR("disconnected %s"), kMI32DeviceType[(MIBLEsensors[MI32.state.sensor].type)-1]); } bool onConnParamsUpdateRequest(NimBLEClient* MI32Client, const ble_gap_upd_params* params) { if(params->itvl_min < 24) { /** 1.25ms units */ @@ -409,13 +412,13 @@ class MI32SensorCallback : public NimBLEClientCallbacks { class MI32AdvCallbacks: public NimBLEAdvertisedDeviceCallbacks { void onResult(NimBLEAdvertisedDevice* advertisedDevice) { - // AddLog_P(LOG_LEVEL_DEBUG,PSTR("Advertised Device: %s Buffer: %u"),advertisedDevice->getAddress().toString().c_str(),advertisedDevice->getServiceData(0).length()); + // AddLog(LOG_LEVEL_DEBUG,PSTR("Advertised Device: %s Buffer: %u"),advertisedDevice->getAddress().toString().c_str(),advertisedDevice->getServiceData(0).length()); int RSSI = advertisedDevice->getRSSI(); uint8_t addr[6]; memcpy(addr,advertisedDevice->getAddress().getNative(),6); MI32_ReverseMAC(addr); if (advertisedDevice->getServiceDataCount() == 0) { - // AddLog_P(LOG_LEVEL_DEBUG,PSTR("No Xiaomi Device: %s Buffer: %u"),advertisedDevice->getAddress().toString().c_str(),advertisedDevice->getServiceData(0).length()); + // AddLog(LOG_LEVEL_DEBUG,PSTR("No Xiaomi Device: %s Buffer: %u"),advertisedDevice->getAddress().toString().c_str(),advertisedDevice->getServiceData(0).length()); if(MI32.state.beaconScanCounter==0 && !MI32.mode.activeBeacon){ MI32Scan->erase(advertisedDevice->getAddress()); return; @@ -427,7 +430,7 @@ class MI32AdvCallbacks: public NimBLEAdvertisedDeviceCallbacks { } uint16_t UUID = advertisedDevice->getServiceDataUUID(0).getNative()->u16.value; - // AddLog_P(LOG_LEVEL_DEBUG,PSTR("UUID: %x"),UUID); + // AddLog(LOG_LEVEL_DEBUG,PSTR("UUID: %x"),UUID); size_t ServiceDataLength = advertisedDevice->getServiceData(0).length(); if(UUID==0xfe95) { @@ -446,7 +449,7 @@ class MI32AdvCallbacks: public NimBLEAdvertisedDeviceCallbacks { if(MI32.state.beaconScanCounter!=0 || MI32.mode.activeBeacon){ MI32HandleGenericBeacon(advertisedDevice->getPayload(), advertisedDevice->getPayloadLength(), RSSI, addr); } - // AddLog_P(LOG_LEVEL_DEBUG,PSTR("No Xiaomi Device: %x: %s Buffer: %u"), UUID, advertisedDevice->getAddress().toString().c_str(),advertisedDevice->getServiceData(0).length()); + // AddLog(LOG_LEVEL_DEBUG,PSTR("No Xiaomi Device: %x: %s Buffer: %u"), UUID, advertisedDevice->getAddress().toString().c_str(),advertisedDevice->getServiceData(0).length()); MI32Scan->erase(advertisedDevice->getAddress()); } }; @@ -462,12 +465,12 @@ static NimBLEClient* MI32Client; \*********************************************************************************************/ void MI32scanEndedCB(NimBLEScanResults results){ - AddLog_P(LOG_LEVEL_DEBUG,PSTR("Scan ended")); + AddLog(LOG_LEVEL_DEBUG,PSTR("Scan ended")); MI32.mode.runningScan = 0; } void MI32notifyCB(NimBLERemoteCharacteristic* pRemoteCharacteristic, uint8_t* pData, size_t length, bool isNotify){ - AddLog_P(LOG_LEVEL_DEBUG,PSTR("Notified length: %u"),length); + AddLog(LOG_LEVEL_DEBUG,PSTR("Notified length: %u"),length); switch(MIBLEsensors[MI32.state.sensor].type){ case LYWSD03MMC: case LYWSD02: case MHOC401: MI32readHT_LY((char*)pData); @@ -545,12 +548,12 @@ void MI32AddKey(char* payload){ bool unknownKey = true; for(uint32_t i=0; iPID,2); nonce[8] = packet->frameCnt; memcpy((uint8_t*)&nonce+9,(uint8_t*)&_buf[_bufSize-9],3); - // AddLog_P(LOG_LEVEL_DEBUG,PSTR("nonceCnt1 and 2: %02x %02x %02x"),nonce[9],nonce[10],nonce[11]); + // AddLog(LOG_LEVEL_DEBUG,PSTR("nonceCnt1 and 2: %02x %02x %02x"),nonce[9],nonce[10],nonce[11]); memcpy((uint8_t*)&tag,(uint8_t*)&_buf[_bufSize-6],4); - // AddLog_P(LOG_LEVEL_DEBUG,PSTR("tag: %02x %02x %02x %02x"),tag[0],tag[1],tag[2],tag[3]); + // AddLog(LOG_LEVEL_DEBUG,PSTR("tag: %02x %02x %02x %02x"),tag[0],tag[1],tag[2],tag[3]); MI32_ReverseMAC(packet->MAC); uint8_t _bindkey[16] = {0x0}; bool foundNoKey = true; - AddLog_P(LOG_LEVEL_DEBUG,PSTR("M32: Search key for MAC: %02x %02x %02x %02x %02x %02x"), packet->MAC[0], packet->MAC[1], packet->MAC[2], packet->MAC[3], packet->MAC[4], packet->MAC[5]); + AddLog(LOG_LEVEL_DEBUG,PSTR("M32: Search key for MAC: %02x %02x %02x %02x %02x %02x"), packet->MAC[0], packet->MAC[1], packet->MAC[2], packet->MAC[3], packet->MAC[4], packet->MAC[5]); for(uint32_t i=0; iMAC,MIBLEbindKeys[i].MAC,sizeof(packet->MAC))==0){ memcpy(_bindkey,MIBLEbindKeys[i].key,sizeof(_bindkey)); - AddLog_P(LOG_LEVEL_DEBUG,PSTR("M32: Decryption Key found")); + AddLog(LOG_LEVEL_DEBUG,PSTR("M32: Decryption Key found")); foundNoKey = false; break; } } if(foundNoKey){ - AddLog_P(LOG_LEVEL_DEBUG,PSTR("M32: No Key found !!")); + AddLog(LOG_LEVEL_DEBUG,PSTR("M32: No Key found !!")); return -2; } @@ -619,7 +622,7 @@ int MI32_decryptPacket(char *_buf, uint16_t _bufSize, uint32_t _type){ ret = br_ccm_check_tag(&ctx, &tag); - AddLog_P(LOG_LEVEL_DEBUG,PSTR("M32: Err:%i, Decrypted : %02x %02x %02x %02x %02x "), ret, packet->payload[1],packet->payload[2],packet->payload[3],packet->payload[4],packet->payload[5]); + AddLog(LOG_LEVEL_DEBUG,PSTR("M32: Err:%i, Decrypted : %02x %02x %02x %02x %02x "), ret, packet->payload[1],packet->payload[2],packet->payload[3],packet->payload[4],packet->payload[5]); return ret-1; } #endif // USE_MI_DECRYPTION @@ -674,9 +677,9 @@ uint32_t MIBLEgetSensorSlot(uint8_t (&_MAC)[6], uint16_t _type, uint8_t counter) for(uint32_t i=0; isetClientCallbacks(&MI32SensorCB , false); MI32Client->setConnectionParams(12,12,0,48); MI32Client->setConnectTimeout(30); - // AddLog_P(LOG_LEVEL_DEBUG,PSTR("%s: did create new client"),D_CMND_MI32); + // AddLog(LOG_LEVEL_DEBUG,PSTR("%s: did create new client"),D_CMND_MI32); } vTaskDelay(300/ portTICK_PERIOD_MS); if (!MI32Client->connect(_address,false)) { MI32.mode.willConnect = 0; // NimBLEDevice::deleteClient(MI32Client); - // AddLog_P(LOG_LEVEL_DEBUG,PSTR("%s: did not connect client"),D_CMND_MI32); + // AddLog(LOG_LEVEL_DEBUG,PSTR("%s: did not connect client"),D_CMND_MI32); return false; } return true; @@ -898,7 +901,7 @@ void MI32StartScanTask(){ 0, /* Priority of the task */ NULL, /* Task handle. */ 0); /* Core where the task should run */ - AddLog_P(LOG_LEVEL_DEBUG,PSTR("%s: Start scanning"),D_CMND_MI32); + AddLog(LOG_LEVEL_DEBUG,PSTR("%s: Start scanning"),D_CMND_MI32); } void MI32ScanTask(void *pvParameters){ @@ -939,8 +942,8 @@ void MI32StartSensorTask(){ 15, /* Priority of the task */ NULL, /* Task handle. */ 0); /* Core where the task should run */ - AddLog_P(LOG_LEVEL_DEBUG,PSTR("%s: Start sensor connections"),D_CMND_MI32); - AddLog_P(LOG_LEVEL_DEBUG,PSTR("%s: with sensor: %u"),D_CMND_MI32, MI32.state.sensor); + AddLog(LOG_LEVEL_DEBUG,PSTR("%s: Start sensor connections"),D_CMND_MI32); + AddLog(LOG_LEVEL_DEBUG,PSTR("%s: with sensor: %u"),D_CMND_MI32, MI32.state.sensor); } void MI32SensorTask(void *pvParameters){ @@ -1013,8 +1016,8 @@ void MI32StartTimeTask(){ 15, /* Priority of the task */ NULL, /* Task handle. */ 0); /* Core where the task should run */ - // AddLog_P(LOG_LEVEL_DEBUG,PSTR("%s: Start time set"),D_CMND_MI32); - // AddLog_P(LOG_LEVEL_DEBUG,PSTR("%s: with sensor: %u"),D_CMND_MI32, MI32.state.sensor); + // AddLog(LOG_LEVEL_DEBUG,PSTR("%s: Start time set"),D_CMND_MI32); + // AddLog(LOG_LEVEL_DEBUG,PSTR("%s: with sensor: %u"),D_CMND_MI32, MI32.state.sensor); } void MI32TimeTask(void *pvParameters){ @@ -1078,8 +1081,8 @@ void MI32StartUnitTask(){ 15, /* Priority of the task */ NULL, /* Task handle. */ 0); /* Core where the task should run */ - // AddLog_P(LOG_LEVEL_DEBUG,PSTR("%s: Start unit set"),D_CMND_MI32); - // AddLog_P(LOG_LEVEL_DEBUG,PSTR("%s: with sensor: %u"),D_CMND_MI32, MI32.state.sensor); + // AddLog(LOG_LEVEL_DEBUG,PSTR("%s: Start unit set"),D_CMND_MI32); + // AddLog(LOG_LEVEL_DEBUG,PSTR("%s: with sensor: %u"),D_CMND_MI32, MI32.state.sensor); } void MI32UnitTask(void *pvParameters){ @@ -1269,13 +1272,13 @@ void MI32parseMiBeacon(char * _buf, uint32_t _slot, uint16_t _bufSize){ else return; // 0x3058 holds no data, TODO: check for unpaired devices, that need connections break; case MJYD2S: - AddLog_P(LOG_LEVEL_DEBUG,PSTR("MJYD2S: %x"),_beacon.frame); + AddLog(LOG_LEVEL_DEBUG,PSTR("MJYD2S: %x"),_beacon.frame); if (_beacon.frame == 0x5948){ // Now let's build/recreate a special MiBeacon memmove((uint8_t*)&_beacon.MAC+6,(uint8_t*)&_beacon.MAC, _bufSize); // shift payload by the size of the MAC = 6 bytes memcpy((uint8_t*)&_beacon.MAC,MIBLEsensors[_slot].MAC,6); // now insert the real MAC from our internal vector _bufSize+=6; // the packet has grown MI32_ReverseMAC(_beacon.MAC); // payload MAC is always reversed - AddLog_P(LOG_LEVEL_DEBUG,PSTR("MJYD2S: special packet")); + AddLog(LOG_LEVEL_DEBUG,PSTR("MJYD2S: special packet")); } if (_beacon.frame != 0x5910){ decryptRet = MI32_decryptPacket((char*)&_beacon.productID,_bufSize,MJYD2S); //start with PID @@ -1283,7 +1286,7 @@ void MI32parseMiBeacon(char * _buf, uint32_t _slot, uint16_t _bufSize){ break; } if(decryptRet!=0){ - AddLog_P(LOG_LEVEL_DEBUG,PSTR("M32: Decryption failed with error: %d"),decryptRet); + AddLog(LOG_LEVEL_DEBUG,PSTR("M32: Decryption failed with error: %d"),decryptRet); return; } #endif //USE_MI_DECRYPTION @@ -1292,13 +1295,13 @@ if(decryptRet!=0){ DEBUG_SENSOR_LOG(PSTR("CGD1 no support for MiBeacon, type %u"),MIBLEsensors[_slot].type); return; } - AddLog_P(LOG_LEVEL_DEBUG,PSTR("%s at slot %u with payload type: %02x"), kMI32DeviceType[MIBLEsensors[_slot].type-1],_slot,_beacon.type); + AddLog(LOG_LEVEL_DEBUG,PSTR("%s at slot %u with payload type: %02x"), kMI32DeviceType[MIBLEsensors[_slot].type-1],_slot,_beacon.type); switch(_beacon.type){ case 0x01: MIBLEsensors[_slot].Btn=_beacon.Btn.num + (_beacon.Btn.longPress/2)*6; MIBLEsensors[_slot].eventType.Btn = 1; MI32.mode.shallTriggerTele = 1; - // AddLog_P(LOG_LEVEL_DEBUG,PSTR("Mode 1: U16: %u Button"), MIBLEsensors[_slot].Btn ); + // AddLog(LOG_LEVEL_DEBUG,PSTR("Mode 1: U16: %u Button"), MIBLEsensors[_slot].Btn ); break; case 0x04: _tempFloat=(float)(_beacon.temp)/10.0f; @@ -1307,7 +1310,7 @@ if(decryptRet!=0){ MIBLEsensors[_slot].eventType.temp = 1; DEBUG_SENSOR_LOG(PSTR("Mode 4: temp updated")); } - // AddLog_P(LOG_LEVEL_DEBUG,PSTR("Mode 4: U16: %u Temp"), _beacon.temp ); + // AddLog(LOG_LEVEL_DEBUG,PSTR("Mode 4: U16: %u Temp"), _beacon.temp ); break; case 0x06: _tempFloat=(float)(_beacon.hum)/10.0f; @@ -1316,7 +1319,7 @@ if(decryptRet!=0){ MIBLEsensors[_slot].eventType.hum = 1; DEBUG_SENSOR_LOG(PSTR("Mode 6: hum updated")); } - // AddLog_P(LOG_LEVEL_DEBUG,PSTR("Mode 6: U16: %u Hum"), _beacon.hum); + // AddLog(LOG_LEVEL_DEBUG,PSTR("Mode 6: U16: %u Hum"), _beacon.hum); break; case 0x07: MIBLEsensors[_slot].lux=_beacon.lux & 0x00ffffff; @@ -1324,19 +1327,19 @@ if(decryptRet!=0){ MIBLEsensors[_slot].eventType.noMotion = 1; } MIBLEsensors[_slot].eventType.lux = 1; - // AddLog_P(LOG_LEVEL_DEBUG,PSTR("Mode 7: U24: %u Lux"), _beacon.lux & 0x00ffffff); + // AddLog(LOG_LEVEL_DEBUG,PSTR("Mode 7: U24: %u Lux"), _beacon.lux & 0x00ffffff); break; case 0x08: MIBLEsensors[_slot].moisture=_beacon.moist; MIBLEsensors[_slot].eventType.moist = 1; DEBUG_SENSOR_LOG(PSTR("Mode 8: moisture updated")); - // AddLog_P(LOG_LEVEL_DEBUG,PSTR("Mode 8: U8: %u Moisture"), _beacon.moist); + // AddLog(LOG_LEVEL_DEBUG,PSTR("Mode 8: U8: %u Moisture"), _beacon.moist); break; case 0x09: MIBLEsensors[_slot].fertility=_beacon.fert; MIBLEsensors[_slot].eventType.fert = 1; DEBUG_SENSOR_LOG(PSTR("Mode 9: fertility updated")); - // AddLog_P(LOG_LEVEL_DEBUG,PSTR("Mode 9: U16: %u Fertility"), _beacon.fert); + // AddLog(LOG_LEVEL_DEBUG,PSTR("Mode 9: U16: %u Fertility"), _beacon.fert); break; case 0x0a: if(MI32.option.ignoreBogusBattery){ @@ -1349,7 +1352,7 @@ if(decryptRet!=0){ MIBLEsensors[_slot].eventType.bat = 1; DEBUG_SENSOR_LOG(PSTR("Mode a: bat updated")); } - // AddLog_P(LOG_LEVEL_DEBUG,PSTR("Mode a: U8: %u %%"), _beacon.bat); + // AddLog(LOG_LEVEL_DEBUG,PSTR("Mode a: U8: %u %%"), _beacon.bat); break; case 0x0d: _tempFloat=(float)(_beacon.HT.temp)/10.0f; @@ -1363,7 +1366,7 @@ if(decryptRet!=0){ DEBUG_SENSOR_LOG(PSTR("Mode d: hum updated")); } MIBLEsensors[_slot].eventType.tempHum = 1; - // AddLog_P(LOG_LEVEL_DEBUG,PSTR("Mode d: U16: %x Temp U16: %x Hum"), _beacon.HT.temp, _beacon.HT.hum); + // AddLog(LOG_LEVEL_DEBUG,PSTR("Mode d: U16: %x Temp U16: %x Hum"), _beacon.HT.temp, _beacon.HT.hum); break; #ifdef USE_MI_DECRYPTION case 0x0f: @@ -1375,13 +1378,13 @@ if(decryptRet!=0){ MIBLEsensors[_slot].eventType.lux = 1; MIBLEsensors[_slot].NMT = 0; MI32.mode.shallTriggerTele = 1; - // AddLog_P(LOG_LEVEL_DEBUG,PSTR("PIR: primary"),MIBLEsensors[_slot].lux ); + // AddLog(LOG_LEVEL_DEBUG,PSTR("PIR: primary"),MIBLEsensors[_slot].lux ); break; case 0x17: MIBLEsensors[_slot].NMT = _beacon.NMT; MIBLEsensors[_slot].eventType.NMT = 1; MI32.mode.shallTriggerTele = 1; - // AddLog_P(LOG_LEVEL_DEBUG,PSTR("Mode 17: NMT: %u seconds"), _beacon.NMT); + // AddLog(LOG_LEVEL_DEBUG,PSTR("Mode 17: NMT: %u seconds"), _beacon.NMT); break; #endif //USE_MI_DECRYPTION default: @@ -1391,7 +1394,7 @@ if(decryptRet!=0){ MIBLEsensors[_slot].NMT = 0; MIBLEsensors[_slot].lastTime = millis(); MI32.mode.shallTriggerTele = 1; - // AddLog_P(LOG_LEVEL_DEBUG,PSTR("PIR: primary"),MIBLEsensors[_slot].lux ); + // AddLog(LOG_LEVEL_DEBUG,PSTR("PIR: primary"),MIBLEsensors[_slot].lux ); } else{ AddLogBuffer(LOG_LEVEL_DEBUG,(uint8_t*)_buf,_bufSize); @@ -1406,7 +1409,7 @@ if(decryptRet!=0){ void MI32ParseATCPacket(char * _buf, uint32_t length, uint8_t addr[6], int RSSI){ ATCPacket_t *_packet = (ATCPacket_t*)_buf; uint32_t _slot = MIBLEgetSensorSlot(_packet->MAC, 0x0a1c, _packet->frameCnt); // This must be a hard-coded fake ID - AddLog_P(LOG_LEVEL_DEBUG,PSTR("%s at slot %u"), kMI32DeviceType[MIBLEsensors[_slot].type-1],_slot); + AddLog(LOG_LEVEL_DEBUG,PSTR("%s at slot %u"), kMI32DeviceType[MIBLEsensors[_slot].type-1],_slot); if(_slot==0xff) return; MIBLEsensors[_slot].RSSI=RSSI; @@ -1426,7 +1429,7 @@ void MI32parseCGD1Packet(char * _buf, uint32_t length, uint8_t addr[6], int RSSI uint8_t _addr[6]; memcpy(_addr,addr,6); uint32_t _slot = MIBLEgetSensorSlot(_addr, 0x0576, 0); // This must be hard-coded, no object-id in Cleargrass-packet, we have no packet counter too - AddLog_P(LOG_LEVEL_DEBUG,PSTR("%s at slot %u"), kMI32DeviceType[MIBLEsensors[_slot].type-1],_slot); + AddLog(LOG_LEVEL_DEBUG,PSTR("%s at slot %u"), kMI32DeviceType[MIBLEsensors[_slot].type-1],_slot); if(_slot==0xff) return; MIBLEsensors[_slot].RSSI=RSSI; cg_packet_t _packet; @@ -1468,7 +1471,7 @@ void MI32ParseResponse(char *buf, uint16_t bufsize, uint8_t addr[6], int RSSI) { return; } uint16_t _type= buf[3]*256 + buf[2]; - // AddLog_P(LOG_LEVEL_INFO, PSTR("%02x %02x %02x %02x"),(uint8_t)buf[0], (uint8_t)buf[1],(uint8_t)buf[2],(uint8_t)buf[3]); + // AddLog(LOG_LEVEL_INFO, PSTR("%02x %02x %02x %02x"),(uint8_t)buf[0], (uint8_t)buf[1],(uint8_t)buf[2],(uint8_t)buf[3]); uint8_t _addr[6]; memcpy(_addr,addr,6); uint16_t _slot = MIBLEgetSensorSlot(_addr, _type, buf[4]); @@ -1488,36 +1491,36 @@ void MI32ParseResponse(char *buf, uint16_t bufsize, uint8_t addr[6], int RSSI) { * @param UUID */ void MI32ParseGenericBeacon(uint8_t* payload, size_t payloadLength, uint16_t* CID, uint16_t*SVC, uint16_t* UUID){ - AddLog_P(LOG_LEVEL_DEBUG_MORE,PSTR("M32: Beacon:____________")); + AddLog(LOG_LEVEL_DEBUG_MORE,PSTR("M32: Beacon:____________")); for (uint32_t i = 0; i19) { - AddLog_P(LOG_LEVEL_INFO,PSTR("M32: Scan buffer full")); + AddLog(LOG_LEVEL_INFO,PSTR("M32: Scan buffer full")); MI32.state.beaconScanCounter = 1; return; } for(auto _scanResult : MIBLEscanResult){ if(memcmp(addr,_scanResult.MAC,6)==0){ - // AddLog_P(LOG_LEVEL_INFO,PSTR("M32: known device")); + // AddLog(LOG_LEVEL_INFO,PSTR("M32: known device")); return; } } @@ -1582,12 +1585,12 @@ void MI32addBeacon(uint8_t index, char* data){ _new.time = 0; if(memcmp(_empty,_new.MAC,6) == 0){ _new.active = false; - AddLog_P(LOG_LEVEL_INFO,PSTR("M32: Beacon%u deactivated"), index); + AddLog(LOG_LEVEL_INFO,PSTR("M32: Beacon%u deactivated"), index); } else{ _new.active = true; MI32.mode.activeBeacon = 1; - AddLog_P(LOG_LEVEL_INFO,PSTR("M32: Beacon added with MAC: %s"), _MAC); + AddLog(LOG_LEVEL_INFO,PSTR("M32: Beacon added with MAC: %s"), _MAC); } } @@ -1642,7 +1645,7 @@ void MI32readHT_LY(char *_buf){ DEBUG_SENSOR_LOG(PSTR("%s: raw data: %x%x%x%x%x%x%x"),D_CMND_MI32,_buf[0],_buf[1],_buf[2],_buf[3],_buf[4],_buf[5],_buf[6]); if(_buf[0] != 0 && _buf[1] != 0){ memcpy(&LYWSD0x_HT,(void *)_buf,sizeof(LYWSD0x_HT)); - AddLog_P(LOG_LEVEL_DEBUG, PSTR("%s: T * 100: %u, H: %u, V: %u"),D_CMND_MI32,LYWSD0x_HT.temp,LYWSD0x_HT.hum, LYWSD0x_HT.volt); + AddLog(LOG_LEVEL_DEBUG, PSTR("%s: T * 100: %u, H: %u, V: %u"),D_CMND_MI32,LYWSD0x_HT.temp,LYWSD0x_HT.hum, LYWSD0x_HT.volt); uint32_t _slot = MI32.state.sensor; DEBUG_SENSOR_LOG(PSTR("MIBLE: Sensor slot: %u"), _slot); @@ -1671,7 +1674,7 @@ void MI32readHT_LY(char *_buf){ bool MI32readBat(char *_buf){ DEBUG_SENSOR_LOG(PSTR("%s: raw data: %x%x%x%x%x%x%x"),D_CMND_MI32,_buf[0],_buf[1],_buf[2],_buf[3],_buf[4],_buf[5],_buf[6]); if(_buf[0] != 0){ - AddLog_P(LOG_LEVEL_DEBUG,PSTR("%s: Battery: %u"),D_CMND_MI32,_buf[0]); + AddLog(LOG_LEVEL_DEBUG,PSTR("%s: Battery: %u"),D_CMND_MI32,_buf[0]); uint32_t _slot = MI32.state.sensor; DEBUG_SENSOR_LOG(PSTR("MIBLE: Sensor slot: %u"), _slot); if(_buf[0]<101){ @@ -1679,7 +1682,7 @@ bool MI32readBat(char *_buf){ if(MIBLEsensors[_slot].type==FLORA){ memcpy(MIBLEsensors[_slot].firmware, _buf+2, 5); MIBLEsensors[_slot].firmware[5] = '\0'; - AddLog_P(LOG_LEVEL_DEBUG,PSTR("%s: Firmware: %s"),D_CMND_MI32,MIBLEsensors[_slot].firmware); + AddLog(LOG_LEVEL_DEBUG,PSTR("%s: Firmware: %s"),D_CMND_MI32,MIBLEsensors[_slot].firmware); } MIBLEsensors[_slot].eventType.bat = 1; MIBLEsensors[_slot].shallSendMQTT = 1; @@ -1794,8 +1797,8 @@ void MI32EverySecond(bool restart){ if(MI32.mode.connected == 0) { if (MI32.mode.shallReadBatt) { //TODO: decide automatically, which sensor can not work without connections - AddLog_P(LOG_LEVEL_DEBUG,PSTR("%s: active sensor now: %u of %u"),D_CMND_MI32, MI32.state.sensor, MIBLEsensors.size()-1); - AddLog_P(LOG_LEVEL_DEBUG, PSTR("will connect to %s"),kMI32DeviceType[MIBLEsensors[MI32.state.sensor].type-1] ); + AddLog(LOG_LEVEL_DEBUG,PSTR("%s: active sensor now: %u of %u"),D_CMND_MI32, MI32.state.sensor, MIBLEsensors.size()-1); + AddLog(LOG_LEVEL_DEBUG, PSTR("will connect to %s"),kMI32DeviceType[MIBLEsensors[MI32.state.sensor].type-1] ); MI32StartTask(MI32_TASK_BATT); } @@ -1842,7 +1845,7 @@ void CmndMi32Time(void) { if (XdrvMailbox.data_len > 0) { if (MIBLEsensors.size() > XdrvMailbox.payload) { if ((LYWSD02 == MIBLEsensors[XdrvMailbox.payload].type) || (MHOC303 == MIBLEsensors[XdrvMailbox.payload].type)) { - AddLog_P(LOG_LEVEL_DEBUG, PSTR("M32: Will set Time")); + AddLog(LOG_LEVEL_DEBUG, PSTR("M32: Will set Time")); MI32.state.sensor = XdrvMailbox.payload; MI32.mode.canScan = 0; MI32.mode.canConnect = 0; @@ -1872,7 +1875,7 @@ void CmndMi32Unit(void) { if (XdrvMailbox.data_len > 0) { if (MIBLEsensors.size() > XdrvMailbox.payload) { if ((LYWSD02 == MIBLEsensors[XdrvMailbox.payload].type) || (MHOC303 == MIBLEsensors[XdrvMailbox.payload].type)) { - AddLog_P(LOG_LEVEL_DEBUG,PSTR("M32: Will set Unit")); + AddLog(LOG_LEVEL_DEBUG,PSTR("M32: Will set Unit")); MI32.state.sensor = XdrvMailbox.payload; MI32.mode.canScan = 0; MI32.mode.canConnect = 0; @@ -1922,7 +1925,7 @@ void CmndMi32Block(void){ switch (XdrvMailbox.index) { case 0: MIBLEBlockList.clear(); - // AddLog_P(LOG_LEVEL_INFO,PSTR("M32: Size of ilist: %u"), MIBLEBlockList.size()); + // AddLog(LOG_LEVEL_INFO,PSTR("M32: Size of ilist: %u"), MIBLEBlockList.size()); ResponseCmndIdxChar(PSTR("Block list cleared")); break; case 1: @@ -1952,7 +1955,7 @@ void CmndMi32Block(void){ ResponseCmndIdxChar(XdrvMailbox.data); MI32removeMIBLEsensor(_MACasBytes.buf); } - // AddLog_P(LOG_LEVEL_INFO,PSTR("M32: Size of ilist: %u"), MIBLEBlockList.size()); + // AddLog(LOG_LEVEL_INFO,PSTR("M32: Size of ilist: %u"), MIBLEBlockList.size()); break; } } @@ -2051,9 +2054,8 @@ void MI32Show(bool json) ||(hass_mode!=-1) #endif //USE_HOME_ASSISTANT ) { - char temperature[FLOATSZ]; - dtostrfd(MIBLEsensors[i].temp, Settings.flag2.temperature_resolution, temperature); - ResponseAppend_P(PSTR(",\"" D_JSON_TEMPERATURE "\":%s"), temperature); + ResponseAppend_P(PSTR(",\"" D_JSON_TEMPERATURE "\":%*_f"), + Settings.flag2.temperature_resolution, &MIBLEsensors[i].temp); } } } @@ -2210,9 +2212,7 @@ void MI32Show(bool json) WSContentSend_PD(HTTP_RSSI, kMI32DeviceType[MIBLEsensors[i].type-1], MIBLEsensors[i].RSSI); if (MIBLEsensors[i].type==FLORA) { if (!isnan(MIBLEsensors[i].temp)) { - char temperature[FLOATSZ]; - dtostrfd(MIBLEsensors[i].temp, Settings.flag2.temperature_resolution, temperature); - WSContentSend_PD(HTTP_SNS_TEMP, kMI32DeviceType[MIBLEsensors[i].type-1], temperature, TempUnit()); + WSContentSend_Temp(kMI32DeviceType[MIBLEsensors[i].type-1], MIBLEsensors[i].temp); } if (MIBLEsensors[i].moisture!=0xff) { WSContentSend_PD(HTTP_SNS_MOISTURE, kMI32DeviceType[MIBLEsensors[i].type-1], MIBLEsensors[i].moisture); @@ -2319,3 +2319,4 @@ bool Xsns62(uint8_t function) } #endif // USE_MI_ESP32 #endif // ESP32 +#endif // USE_BLE_ESP32 \ No newline at end of file diff --git a/tasmota/xsns_62_esp32_mi_ble.ino b/tasmota/xsns_62_esp32_mi_ble.ino new file mode 100644 index 000000000..528953e99 --- /dev/null +++ b/tasmota/xsns_62_esp32_mi_ble.ino @@ -0,0 +1,2952 @@ +/* + xsns_62_esp32_mi_ble.ino - MI-BLE-sensors via ESP32 support for Tasmota + enabled by ESP32 && USE_BLE_ESP32 + if (ESP32 && !USE_BLE_ESP32) then xsns_62_esp32_mi.ino is used - the older driver + + + Copyright (C) 2020 Christian Baars and Theo Arends + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + + + -------------------------------------------------------------------------------------------- + Version yyyymmdd Action Description + -------------------------------------------------------------------------------------------- + 0.9.2.0 20210127 changed - Officially includes as the mi driver when using USE_BLE_ESP32. + ------- + 0.9.1.9 20201226 changed - All change now. + ------- + 0.9.1.7 20201116 changed - small bugfixes, add BLOCK and OPTION command, send BLE scan via MQTT + ------- + 0.9.1.6 20201022 changed - Beacon support, RSSI at TELEPERIOD, refactoring + ------- + 0.9.1.5 20201021 changed - HASS related ('null', hold back discovery), number of found sensors for RULES + ------- + 0.9.1.4 20201020 changed - use BearSSL for decryption, revert to old TELEPERIOD-cycle as default + ------- + 0.9.1.3 20200926 changed - Improve HA discovery, make key+MAC case insensitive + ------- + 0.9.1.3 20200916 changed - add ATC (custom FW for LYWSD03MMC), API adaption for NimBLE-Arduino 1.0.2 + ------- + 0.9.1.2 20200802 changed - add MHO-C303 + ------- + 0.9.1.1 20200715 changed - add MHO-C401, refactoring + ------- + 0.9.1.0 20200712 changed - add lights and yeerc, add pure passive mode with decryption, + lots of refactoring + ------- + 0.9.0.1 20200706 changed - adapt to new NimBLE-API, tweak scan process + ------- + 0.9.0.0 20200413 started - initial development by Christian Baars + forked - from arendst/tasmota - https://github.com/arendst/Tasmota + +*/ +//#define VSCODE_DEV + +/* +#ifdef VSCODE_DEV +#define ESP32 +#define USE_BLE_ESP32 +#define USE_MI_ESP32 +#endif +*/ +//#undef USE_MI_ESP32 + +// for testing of BLE_ESP32, we remove xsns_62_MI_ESP32.ino completely, and instead add this modified xsns_52_ibeacon_BLE_ESP32.ino +#ifdef USE_BLE_ESP32 + +#ifdef ESP32 // ESP32 only. Use define USE_HM10 for ESP8266 support + +#ifdef USE_MI_ESP32 + +#define XSNS_62 62 +#define USE_MI_DECRYPTION + +#include +#ifdef USE_MI_DECRYPTION +#include +#endif //USE_MI_DECRYPTION + +void MI32scanEndedCB(NimBLEScanResults results); +void MI32notifyCB(NimBLERemoteCharacteristic* pRemoteCharacteristic, uint8_t* pData, size_t length, bool isNotify); + +struct { + uint16_t perPage = 4; + uint8_t mqttCurrentSlot = 0; + uint8_t mqttCurrentSingleSlot = 0; + uint32_t period; // set manually in addition to TELE-period, is set to TELE-period after start + int secondsCounter = 0; // counts up in MI32EverySecond to period + int secondsCounter2 = 0; // counts up in MI32EverySecond to period + union { + struct { + uint32_t init:1; + uint32_t shallClearResults:1; // BLE scan results + uint32_t shallShowStatusInfo:1; // react to amount of found sensors via RULES + uint32_t firstAutodiscoveryDone:1; + uint32_t shallTriggerTele:1; + uint32_t triggeredTele:1; + }; + uint32_t all = 0; + } mode; + + struct { + // the slot currently having it's battery read + // set to 0 to start a battery read cycle + uint8_t slot = 255; + uint8_t active = 0; + } batteryreader; + + struct { + // the slot currently having it's battery read + // set to 0 to start a battery read cycle + uint8_t slot = 255; + uint8_t active = 0; + } sensorreader; + + struct { + uint32_t allwaysAggregate:1; // always show all known values of one sensor in brdigemode + uint32_t noSummary:1; // no sensor values at TELE-period + uint32_t directBridgeMode:1; // send every received BLE-packet as a MQTT-message in real-time + uint32_t holdBackFirstAutodiscovery:1; // allows to trigger it later + uint32_t showRSSI:1; + uint32_t ignoreBogusBattery:1; + uint32_t minimalSummary:1; // DEPRECATED!! + uint32_t onlyAliased:1; // only include sensors that are aliased + } option; +} MI32; + +#pragma pack(1) // byte-aligned structures to read the sensor data + + struct { + int16_t temp; + uint8_t hum; + uint16_t volt; // LYWSD03 only + } LYWSD0x_HT; + struct { + uint8_t spare; + int16_t temp; + uint16_t hum; + } CGD1_HT; + struct { + int16_t temp; + uint8_t spare; + uint32_t lux; + uint8_t moist; + uint16_t fert; + } Flora_TLMF; // temperature, lux, moisture, fertility + + +//////////////////////////////////////////////////////////// +// from https://github.com/Magalex2x14/LYWSD03MMC-info +struct mi_beacon_frame_data_t{ + // data from byte 0 - e.g. 30 + uint8_t meshflag; //Byte 0: x....... + uint8_t dataflag; //Byte 0: .x...... + uint8_t compatibilityflag; //Byte 0: ..x..... - indicates compatibility data present + uint8_t MACFlag; //Byte 0: ...x.... + uint8_t isencrypted; //Byte 0: ....x... + uint8_t reserved; //Byte 0: .....xxx + + // data from byte 1 - e.g. 58 + uint8_t version; //Byte 0: xxxx.... + uint8_t authMode; //Byte 0: ....xx.. // e.g. 2 + uint8_t bindingvalidreq; //Byte 0: ......x. + uint8_t registeredflag; //Byte 0: .......x +}; + +struct mi_beacon_compatibility_data_t{ // e.g. 28/08 + uint8_t reserved; //Byte 0: xx...... + uint8_t IOcap; //Byte 0: ..x..... + uint8_t bondability; //Byte 0: ...xx... + uint8_t unused; //Byte 0: .....xxx + uint16_t IOCapability; // bytes 1-2, e.g. 01 00 -> 0001 +}; +struct mi_beacon_mac_data_t{ // e.g. 28/08 + uint8_t mac[6]; +}; +struct mi_beacon_payload_data_t{ // + uint8_t type; + uint8_t ten; + uint8_t size; + uint8_t data[16]; +}; + +struct mi_beacon_data_t { // + mi_beacon_frame_data_t framedata; + uint16_t devicetype; + uint8_t framecnt; + mi_beacon_mac_data_t macdata; + mi_beacon_compatibility_data_t compatibility; + uint8_t payloadpresent; + uint8_t needkey; // we need a (new) encryption key? + + mi_beacon_payload_data_t payload; +}; + +struct mi_beacon_data_payload_data_t { // + union { + struct{ //01 + uint16_t num; + uint8_t longPress; + } Btn; + + int16_t temp; //04 + uint16_t hum; //06 + uint32_t lux; //07 + uint8_t moist; //08 + uint16_t fert; //09 + uint8_t bat; //0a + struct{ //0d + int16_t temp; + uint16_t hum; + } HT; + uint32_t NMT; //17 + }; +}; + + + +/////////////////////////////////////////////////////////// + + + +union mi_bindKey_t{ + struct{ + uint8_t key[16]; + uint8_t MAC[6]; + }; + uint8_t buf[22]; +}; + +struct ATCPacket_t{ + //uint8_t size; // = 16? + //uint8_t uid; // = 0x16, 16-bit UUID + //uint16_t UUID; // = 0x181A, GATT Service 0x181A Environmental Sensing + uint8_t MAC[6]; // [0] - hi, .. [6] - lo digits + uint16_t temp; //sadly this is in wrong endianess + uint8_t hum; + uint8_t batPer; + uint16_t batMV; + uint8_t frameCnt; +}; + +// GATT Service 0x181A Environmental Sensing +// All data little-endian +struct PVVXPacket_t { + //uint8_t size; // = 19 + //uint8_t uid; // = 0x16, 16-bit UUID + //uint16_t UUID; // = 0x181A, GATT Service 0x181A Environmental Sensing + uint8_t MAC[6]; // [0] - lo, .. [6] - hi digits + int16_t temperature; // x 0.1 degree + uint16_t humidity; // x 0.01 % + uint16_t battery_mv; // mV + uint8_t battery_level; // 0..100 % + uint8_t counter; // measurement count + uint8_t flags; +}; + +#pragma pack(0) + +struct mi_sensor_t{ + uint8_t type; //MI_Flora = 1; MI_MI-HT_V1=2; MI_LYWSD02=3; MI_LYWSD03=4; MI_CGG1=5; MI_CGD1=6 + uint8_t needkey; // tells http to display needkey message with link + uint8_t lastCnt; //device generated counter of the packet + uint8_t nextDiscoveryData; // used to lkimit discovery to one MQTT per sec + + uint8_t shallSendMQTT; + uint8_t MAC[6]; + union { + struct { + uint32_t temp:1; + uint32_t hum:1; + uint32_t tempHum:1; //every hum sensor has temp too, easier to use Tasmota dew point functions + uint32_t lux:1; + uint32_t moist:1; + uint32_t fert:1; + uint32_t bat:1; + uint32_t NMT:1; + uint32_t PIR:1; + uint32_t Btn:1; + }; + uint32_t raw; + } feature; + union { + struct { + uint32_t temp:1; + uint32_t hum:1; + uint32_t tempHum:1; //can be combined from the sensor + uint32_t lux:1; + uint32_t moist:1; + uint32_t fert:1; + uint32_t bat:1; + uint32_t NMT:1; + uint32_t motion:1; + uint32_t noMotion:1; + uint32_t Btn:1; + uint32_t PairBtn:1; + }; + uint32_t raw; + } eventType; + + int RSSI; + uint8_t pairing; + uint32_t lastTime; + uint32_t lux; + float temp; //Flora, MJ_HT_V1, LYWSD0x, CGx + union { + struct { + uint8_t moisture; + uint16_t fertility; + char firmware[6]; // actually only for FLORA but hopefully we can add for more devices + }; // Flora + struct { + float hum; + }; // MJ_HT_V1, LYWSD0x + struct { + uint16_t events; //"alarms" since boot + uint32_t NMT; // no motion time in seconds for the MJYD2S + }; + uint16_t Btn; + }; + union { + uint8_t bat; // many values seem to be hard-coded garbage (LYWSD0x, GCD1) + }; +}; + +struct MAC_t { + uint8_t buf[6]; +}; + +std::vector MIBLEsensors; +std::vector MIBLEbindKeys; +std::vector MIBLEBlockList; + +void *slotmutex = nullptr; + +/*********************************************************************************************\ + * constants +\*********************************************************************************************/ + +#define D_CMND_MI32 "MI32" + +const char kMI32_Commands[] PROGMEM = D_CMND_MI32 "|" +#ifdef USE_MI_DECRYPTION + "Key|" + "Keys|" +#endif // USE_MI_DECRYPTION + "Period|Time|Page|Battery|Unit|Block|Option"; + +void (*const MI32_Commands[])(void) PROGMEM = { +#ifdef USE_MI_DECRYPTION + &CmndMi32Key, + &CmndMi32Keys, +#endif // USE_MI_DECRYPTION + &CmndMi32Period, &CmndMi32Time, &CmndMi32Page, &CmndMi32Battery, &CmndMi32Unit, &CmndMi32Block, &CmndMi32Option }; + + +#define MI_UNKOWN 1 +#define MI_FLORA 2 +#define MI_MJ_HT_V1 3 +#define MI_LYWSD02 4 +#define MI_LYWSD03MMC 5 +#define MI_CGG1 6 +#define MI_CGD1 7 +#define MI_NLIGHT 8 +#define MI_MJYD2S 9 +#define MI_YEERC 10 +#define MI_MHOC401 11 +#define MI_MHOC303 12 +#define MI_ATC 13 + +#define MI_MI32_TYPES 13 //count this manually + +const uint16_t kMI32DeviceID[MI_MI32_TYPES]={ + 0x0000, // Unkown + 0x0098, // Flora + 0x01aa, // MJ_HT_V1 + 0x045b, // LYWSD02 + 0x055b, // LYWSD03 + 0x0347, // CGG1 + 0x0576, // CGD1 + 0x03dd, // NLIGHT + 0x07f6, // MJYD2S + 0x0153, // yee-rc + 0x0387, // MHO-C401 + 0x06d3, // MHO-C303 + 0x0a1c // ATC -> this is a fake ID +}; + +const char kMI32DeviceType0[] PROGMEM = "Unknown"; +const char kMI32DeviceType1[] PROGMEM = "Flora"; +const char kMI32DeviceType2[] PROGMEM = "MJ_HT_V1"; +const char kMI32DeviceType3[] PROGMEM = "LYWSD02"; +const char kMI32DeviceType4[] PROGMEM = "LYWSD03"; +const char kMI32DeviceType5[] PROGMEM = "CGG1"; +const char kMI32DeviceType6[] PROGMEM = "CGD1"; +const char kMI32DeviceType7[] PROGMEM = "NLIGHT"; +const char kMI32DeviceType8[] PROGMEM = "MJYD2S"; +const char kMI32DeviceType9[] PROGMEM = "YEERC"; +const char kMI32DeviceType10[] PROGMEM ="MHOC401"; +const char kMI32DeviceType11[] PROGMEM ="MHOC303"; +const char kMI32DeviceType12[] PROGMEM ="ATC"; +const char * kMI32DeviceType[] PROGMEM = {kMI32DeviceType0,kMI32DeviceType1,kMI32DeviceType2,kMI32DeviceType3,kMI32DeviceType4,kMI32DeviceType5,kMI32DeviceType6,kMI32DeviceType7,kMI32DeviceType8,kMI32DeviceType9,kMI32DeviceType10,kMI32DeviceType11,kMI32DeviceType12}; + +typedef int BATREAD_FUNCTION(int slot); +typedef int UNITWRITE_FUNCTION(int slot, int unit); +typedef int TIMEWRITE_FUNCTION(int slot); + +int genericOpCompleteFn(BLE_ESP32::generic_sensor_t *pStruct); +int genericBatReadFn(int slot); +int genericUnitWriteFn(int slot, int unit); +int genericTimeWriteFn(int slot); +int MI32scanCompleteCallback(NimBLEScanResults results); + +const char LYWSD02_Svc[] PROGMEM = "EBE0CCB0-7A0A-4B0C-8A1A-6FF2997DA3A6"; +const char LYWSD02_BattChar[] PROGMEM = "EBE0CCC4-7A0A-4B0C-8A1A-6FF2997DA3A6"; +const char LYWSD02_UnitChar[] PROGMEM = "EBE0CCBE-7A0A-4B0C-8A1A-6FF2997DA3A6"; +const char LYWSD02_TimeChar[] PROGMEM = "EBE0CCB7-7A0A-4B0C-8A1A-6FF2997DA3A6"; +const char LYWSD02_BattNotifyChar[] PROGMEM = "EBE0CCC1-7A0A-4B0C-8A1A-6FF2997DA3A6"; + +const char *LYWSD03_Svc = LYWSD02_Svc; +const char *LYWSD03_BattNotifyChar = LYWSD02_BattNotifyChar; + +const char *MHOC303_Svc = LYWSD02_Svc; +const char *MHOC303_UnitChar = LYWSD02_UnitChar; +const char *MHOC303_TimeChar = LYWSD02_TimeChar; + +const char *MHOC401_Svc = LYWSD02_Svc; +const char *MHOC401_BattNotifyChar = LYWSD02_BattNotifyChar; + +const char CGD1_Svc[] PROGMEM = "180F"; +const char CGD1_BattChar[] PROGMEM = "2A19"; + +const char FLORA_Svc[] PROGMEM = "00001204-0000-1000-8000-00805F9B34FB"; +const char FLORA_BattChar[] PROGMEM = "00001A02-0000-1000-8000-00805F9B34FB"; + + + +/*********************************************************************************************\ + * enumerations +\*********************************************************************************************/ + +// types of operation performed, included in context +enum MI32_MI_OP_TYPES { + OP_TIME_WRITE = 0, + OP_BATT_READ = 1, + OP_UNIT_WRITE = 2, + OP_UNIT_READ = 3, + OP_UNIT_TOGGLE = 4, + OP_READ_HT_LY = 5, +}; + + +enum MI32_MI_KEY_REQ { + KEY_REQUIREMENT_UNKNOWN = 0, // we don't know if a key is needed + KEY_NOT_REQUIRED = 1, // we got an unencrypted payload + KEY_REQUIRED_BUT_NOT_FOUND = 2, // we got an encrypted packet, but had not key + KEY_REQUIRED_AND_FOUND = 3, // we got an encrypted packet, and could decrypt + KEY_REQUIRED_AND_INVALID = 4, // we got an encrypted packet, and could not decrypt +}; + +/*********************************************************************************************\ + * Classes +\*********************************************************************************************/ + + +// fn type READ_CALLBACK +// 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. +int toggleUnit(BLE_ESP32::generic_sensor_t *op){ + uint32_t context = (uint32_t) op->context; + int opType = context >> 24; + // we only need to op type + int devType = (context >> 16) & 0xff; + int slot = (context) & 0xff; + switch (opType){ + case OP_UNIT_TOGGLE:{ + uint8_t curUnit = 0; + if( op->dataRead[0] != 0 && op->dataRead[0] < 101 ){ + curUnit = op->dataRead[0]; + } + + curUnit = curUnit == 0x01?0xFF:0x01; // C/F + // copy in ALL of the data, because we don't know how long this is from the existing src code. + memcpy(op->dataToWrite, op->dataRead, op->readlen); + op->writelen = op->readlen; + op->dataToWrite[0] = curUnit; + } break; + case OP_UNIT_WRITE:{ + uint8_t curUnit = op->dataToWrite[0]; + // copy in ALL of the data, because we don't know how long this is from the existing src code. + memcpy(op->dataToWrite, op->dataRead, op->readlen); + op->writelen = op->readlen; + op->dataToWrite[0] = curUnit; + } break; + } + return 0; +} + +bool MI32Operation(int slot, int optype, const char *svc, const char *charactistic, const char *notifychar = nullptr, const uint8_t *data = nullptr, int datalen = 0, uint8_t *addr = nullptr ) { + if (!svc || !svc[0]){ + AddLog(LOG_LEVEL_ERROR, PSTR("M32: Op inv svc")); + return 0; + } + + 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("M32: Can't get a newOperation")); + return 0; + } else { + if (BLE_ESP32::BLEDebugMode > 0) AddLog(LOG_LEVEL_DEBUG, PSTR("M32: Got a newOperation")); + } + + if (slot >= 0){ + op->addr = NimBLEAddress(MIBLEsensors[slot].MAC); + } else { + if (!addr){ + AddLog(LOG_LEVEL_ERROR, PSTR("M32: No addr")); + BLE_ESP32::freeOperation(&op); + return 0; + } + op->addr = NimBLEAddress(addr); + } + + bool havechar = false; + op->serviceUUID = NimBLEUUID(svc); + + if (!op->serviceUUID.bitSize()){ + BLE_ESP32::freeOperation(&op); + AddLog(LOG_LEVEL_ERROR, PSTR("M32: MI Bad service string %s"), svc); + return 0; + } + + + if (charactistic && charactistic[0]){ + havechar = true; + op->characteristicUUID = NimBLEUUID(charactistic); + if (!op->characteristicUUID.bitSize()){ + BLE_ESP32::freeOperation(&op); + AddLog(LOG_LEVEL_ERROR, PSTR("M32: MI Bad characteristic string %s"), charactistic); + return 0; + } + } + if (notifychar && notifychar[0]){ + op->notificationCharacteristicUUID = NimBLEUUID(notifychar); + if (!op->notificationCharacteristicUUID.bitSize()){ + BLE_ESP32::freeOperation(&op); + AddLog(LOG_LEVEL_ERROR, PSTR("M32: MI Bad notifycharacteristic string %s"), notifychar); + return 0; + } + } + + if (data && datalen) { + op->writelen = datalen; + memcpy(op->dataToWrite, data, datalen); + } else { + if (!datalen && havechar){ + op->readlen = 1; // if we don't set readlen, then it won't read + } + } + + // the only times we intercept between read abnd write + if ((optype == OP_UNIT_WRITE) || (optype == OP_UNIT_TOGGLE)){ + op->readlen = 1; // if we don't set readlen, then it won't read + op->readmodifywritecallback = (void *)toggleUnit; + } + + // this op will call us back on complete or failure. + op->completecallback = (void *)genericOpCompleteFn; + uint32_t context = (optype << 24) | (MIBLEsensors[slot].type << 16) | slot; + op->context = (void *)context; + + if (BLE_ESP32::BLEDebugMode > 0) AddLog(LOG_LEVEL_DEBUG, PSTR("M32: MI s:%d op:%s"), slot, BLE_ESP32::BLETriggerResponse(op).c_str()); + + res = BLE_ESP32::extQueueOperation(&op); + if (!res){ + // if it fails to add to the queue, do please delete it + BLE_ESP32::freeOperation(&op); + AddLog(LOG_LEVEL_ERROR, PSTR("M32: Failed to queue new operation - deleted")); + } + + return res; +} + + + +int genericBatReadFn(int slot){ + int res = 0; + + switch(MIBLEsensors[slot].type) { + // these use notify for battery read, and it comes in the temp packet + case MI_LYWSD03MMC: + res = MI32Operation(slot, OP_BATT_READ, LYWSD03_Svc, nullptr, LYWSD03_BattNotifyChar); + break; + case MI_MHOC401: + res = MI32Operation(slot, OP_BATT_READ, MHOC401_Svc, nullptr, MHOC401_BattNotifyChar); + break; + + // these read a characteristic + case MI_FLORA: + res = MI32Operation(slot, OP_BATT_READ, FLORA_Svc, FLORA_BattChar); + break; + case MI_LYWSD02: + res = MI32Operation(slot, OP_BATT_READ, LYWSD02_Svc, LYWSD02_BattChar); + break; + case MI_CGD1: + res = MI32Operation(slot, OP_BATT_READ, CGD1_Svc, CGD1_BattChar); + break; + +// this was for testing only - it does work, but no need to read as we get good bat in advert +// case MI_MJ_HT_V1: +// res = MI32Operation(slot, OP_BATT_READ, CGD1_Svc, CGD1_BattChar); +// break; + + default: + res = -10; // no need to read + break; + } + if (res > 0){ + if (BLE_ESP32::BLEDebugMode > 0) AddLog(LOG_LEVEL_INFO, PSTR("M32: Req batt read slot %d type %d queued"), slot, MIBLEsensors[slot].type); + } else { + if (BLE_ESP32::BLEDebugMode > 0) AddLog(LOG_LEVEL_INFO, PSTR("M32: Req batt read slot %d type %d non-queued res %d"), slot, MIBLEsensors[slot].type, res); + } + return res; +} + + + +int genericSensorReadFn(int slot, int force){ + int res = 0; + switch(MIBLEsensors[slot].type) { +/* seen notify timeout consistently with MI_LYWSD02, + so although the characteristic seems to exist, it does not work? + further dev required with sensor to hand. + case MI_LYWSD02: + // don't read if key present and we've decoded at least one advert + if (MIBLEsensors[slot].needkey == KEY_REQUIRED_AND_FOUND) return -2; + res = MI32Operation(slot, OP_READ_HT_LY, LYWSD02_Svc, nullptr, LYWSD02_BattNotifyChar); + break;*/ + case MI_LYWSD03MMC: + // don't read if key present and we've decoded at least one advert + if (MIBLEsensors[slot].needkey == KEY_REQUIRED_AND_FOUND && !force) return -2; + res = MI32Operation(slot, OP_READ_HT_LY, LYWSD03_Svc, nullptr, LYWSD03_BattNotifyChar); + break; + case MI_MHOC401: + // don't read if key present and we've decoded at least one advert + if (MIBLEsensors[slot].needkey == KEY_REQUIRED_AND_FOUND && !force) return -2; + res = MI32Operation(slot, OP_READ_HT_LY, MHOC401_Svc, nullptr, MHOC401_BattNotifyChar); + break; + + default: + res = -1; + break; + } + return res; +} + + +// called once per second +int readOneSensor(){ + if (MI32.sensorreader.active){ + AddLog(LOG_LEVEL_DEBUG, PSTR("M32: readOneSensor - already active reading %d"), MI32.sensorreader.slot-1); + return 0; + } + + // loop if the sensor at the slot does not need to be read + // i.e. drop out of loop when we start a read, or hit the end + int res = -1; + do { + // MI32.sensorreader.slot is reset to zero to trigger a read sequence + if (MI32.sensorreader.slot >= MIBLEsensors.size()){ + //AddLog(LOG_LEVEL_DEBUG, PSTR("BLE: readOneSensor past end of slots - %d > %d"), MI32.sensorreader.slot, MIBLEsensors.size()); + return 0; + } + + res = genericSensorReadFn(MI32.sensorreader.slot, 0); + if (BLE_ESP32::BLEDebugMode > 0) AddLog(LOG_LEVEL_DEBUG, PSTR("M32: genericSensorReadFn slot %d res %d"), MI32.sensorreader.slot, res); + + // if this sensor in this slot does not need to be read via notify, just move on top the next one + if (res < 0){ + MI32.sensorreader.slot++; + } else { + break; + } + } while (1); + + if (res == 0){ + // can't read at the moment (no operations available?) + AddLog(LOG_LEVEL_DEBUG, PSTR("M32: readOneSensor no ops available slot %d res %d"), MI32.sensorreader.slot, res); + return 0; + } + + // setup next slot to read + MI32.sensorreader.slot++; + // and make it wait until the read/notify is complete + // this is cleared in the response callback. + MI32.sensorreader.active = 1; + if (BLE_ESP32::BLEDebugMode > 0) AddLog(LOG_LEVEL_DEBUG, PSTR("M32: readOneSensor reading for slot %d res %d"), MI32.sensorreader.slot-1, res); + + // started one + return 1; +} + + + +// called once per second +int readOneBat(){ + if (MI32.batteryreader.active){ + return 0; + } + + //MI32.batteryreader.slot is rest to zero to trigger a read... + if (MI32.batteryreader.slot >= MIBLEsensors.size()){ + return 0; + } + + int res = genericBatReadFn(MI32.batteryreader.slot); + + // if this sensor in this slot does not support battery read, just move on top the next one + if (res < 0){ + MI32.batteryreader.slot++; + if (MI32.batteryreader.slot >= MIBLEsensors.size()){ + if (BLE_ESP32::BLEDebugMode > 0) AddLog(LOG_LEVEL_INFO, PSTR("M32: Batt loop complete at %d"), MI32.batteryreader.slot); + } + return 0; + } + + if (res == 0){ + // can't read at the moment (no operations available?) + return 0; + } + + // setup next slot to read + MI32.batteryreader.slot++; + // and make it wait until the read/notify is complete + // this is cleared in the response callback. + MI32.batteryreader.active = 1; + if (MI32.batteryreader.slot >= MIBLEsensors.size()){ + if (BLE_ESP32::BLEDebugMode > 0) AddLog(LOG_LEVEL_INFO, PSTR("M32: Batt loop will complete at %d"), MI32.batteryreader.slot); + } + // started one + return 1; +} + + + +///////////////////////////////////////////////////// +// change the unit of measurement? +// call with unit == -1 to cause the unit to be toggled. +int genericUnitWriteFn(int slot, int unit){ + int res = 0; + int op = OP_UNIT_WRITE; + if (unit == -1){ + op = OP_UNIT_TOGGLE; + } + uint8_t writeData[1]; + writeData[0] = unit; + switch (MIBLEsensors[slot].type){ + case MI_LYWSD02: + res = MI32Operation(slot, op, LYWSD02_Svc, LYWSD02_UnitChar, nullptr, writeData, 1); + break; + case MI_MHOC303: // actually, EXACTLY the same as above, including the sevice and characteristic... + res = MI32Operation(slot, op, MHOC303_Svc, MHOC303_UnitChar, nullptr, writeData, 1); + break; + default: + res = -1; + break; + } + return res; +} + +///////////////////////////////////////////////////// +// read the unit of measurement. genericOpCompleteFn +int genericUnitReadFn(int slot){ + int res = 0; + switch (MIBLEsensors[slot].type){ + case MI_LYWSD02: + res = MI32Operation(slot, OP_UNIT_READ, LYWSD02_Svc, LYWSD02_UnitChar); + break; + case MI_MHOC303: // actually, EXACTLY the same as above, including the sevice and characteristic... + res = MI32Operation(slot, OP_UNIT_READ, MHOC303_Svc, MHOC303_UnitChar); + break; + default: + res = -1; + break; + } + return res; +} + + +///////////////////////////////////////////////////// +// write time to a device. genericOpCompleteFn +int genericTimeWriteFn(int slot){ + int res = 0; + switch (MIBLEsensors[slot].type){ + case MI_LYWSD02: { + union { + uint8_t buf[5]; + uint32_t time; + } _utc; + _utc.time = Rtc.utc_time; + _utc.buf[4] = Rtc.time_timezone / 60; + res = MI32Operation(slot, OP_TIME_WRITE, LYWSD02_Svc, LYWSD02_TimeChar, nullptr, _utc.buf, sizeof(_utc.buf)); + } break; + case MI_MHOC303: // actually, EXACTLY the same as above, including the sevice and characteristic... + union { + uint8_t buf[5]; + uint32_t time; + } _utc; + _utc.time = Rtc.utc_time; + _utc.buf[4] = Rtc.time_timezone / 60; + res = MI32Operation(slot, OP_TIME_WRITE, MHOC303_Svc, MHOC303_TimeChar, nullptr, _utc.buf, sizeof(_utc.buf)); + break; + default: + res = -1; + break; + } + return res; +} + + +int genericOpCompleteFn(BLE_ESP32::generic_sensor_t *op){ + uint32_t context = (uint32_t) op->context; + + if (BLE_ESP32::BLEDebugMode > 0) AddLog(LOG_LEVEL_DEBUG, PSTR("M32: MI op complete context %x"), context); + + int opType = context >> 24; + int devType = (context >> 16) & 0xff; + int slot = (context) & 0xff; + + char slotMAC[13]; + BLE_ESP32::dump(slotMAC, sizeof(slotMAC), MIBLEsensors[slot].MAC, 6) ; + uint8_t addrrev[6]; + memcpy(addrrev, MIBLEsensors[slot].MAC, 6); + //BLE_ESP32::ReverseMAC(addrrev); + NimBLEAddress addr(addrrev); + + bool fail = false; + if (op->addr != addr){ + // slot changed during operation? + AddLog(LOG_LEVEL_ERROR, PSTR("M32: Slot mac changed during an operation")); + fail = true; + } + + if (op->state <= GEN_STATE_FAILED){ + AddLog(LOG_LEVEL_ERROR, PSTR("M32: Operation failed %d for %s"), op->state, slotMAC); + fail = true; + } + + if (fail){ + switch(opType){ + case OP_BATT_READ:{ + // allow another... + MI32.batteryreader.active = 0; + } break; + case OP_READ_HT_LY: { + // allow another... + MI32.sensorreader.active = 0; + } break; + } + return 0; + } + + switch(opType){ + case OP_TIME_WRITE: + AddLog(LOG_LEVEL_DEBUG, PSTR("M32: Time write for %s complete"), slotMAC); + return 0; // nothing to do + case OP_BATT_READ:{ + uint8_t *data = nullptr; + int len = 0; + if (op->notifylen){ + data = op->dataNotify; + len = op->notifylen; + // note: the only thingas that have battery in notify FOR THE MOMENT read it like this. + MI32notifyHT_LY(slot, (char*)op->dataNotify, op->notifylen); + } + if (op->readlen){ + data = op->dataRead; + len = op->readlen; + MIParseBatt(slot, data, len); + } + + // allow another... + MI32.batteryreader.active = 0; + AddLog(LOG_LEVEL_INFO, PSTR("M32: Batt read slot %d done state %x"), slot, op->state); + + } return 0; + + case OP_UNIT_WRITE: // nothing more to do? + AddLog(LOG_LEVEL_DEBUG, PSTR("M32: Unit write for %s complete"), slotMAC); + return 0; + + case OP_UNIT_READ: { + uint8_t currUnit = op->dataRead[0]; + AddLog(LOG_LEVEL_DEBUG, PSTR("M32: Unit read for %s complete %d"), slotMAC, currUnit); + } return 0; + + case OP_UNIT_TOGGLE: { + uint8_t currUnit = op->dataToWrite[0]; + AddLog(LOG_LEVEL_DEBUG, PSTR("M32: Unit toggle for %s complete %d->%d; datasize was %d"), slotMAC, op->dataRead[0], op->dataToWrite[0], op->readlen); + } return 0; + + case OP_READ_HT_LY: { + // allow another... + MI32.sensorreader.active = 0; + MI32notifyHT_LY(slot, (char*)op->dataNotify, op->notifylen); + if (BLE_ESP32::BLEDebugMode > 0) AddLog(LOG_LEVEL_DEBUG, PSTR("M32: HT_LY notify for %s complete"), slotMAC); + } return 0; + + default: + AddLog(LOG_LEVEL_ERROR, PSTR("M32: OpType %d not recognised?"), opType); + return 0; + } + + return 0; +} + +int MI32advertismentCallback(BLE_ESP32::ble_advertisment_t *pStruct) +{ + // we will try not to use this... + BLEAdvertisedDevice *advertisedDevice = pStruct->advertisedDevice; + + // AddLog(LOG_LEVEL_DEBUG, PSTR("M32: Advertised Device: %s Buffer: %u"),advertisedDevice->getAddress().toString().c_str(),advertisedDevice->getServiceData(0).length()); + int RSSI = pStruct->RSSI; + const uint8_t *addr = pStruct->addr; + if(MI32isInBlockList(addr) == true) return 0; + if (MI32.option.onlyAliased){ + const char *alias = BLE_ESP32::getAlias(addr); + if (!alias || !(*alias)){ + return 0; + } + } + + int svcdataCount = advertisedDevice->getServiceDataCount(); + + if (svcdataCount == 0) { + return 0; + } + + NimBLEUUID UUIDBig = advertisedDevice->getServiceDataUUID(0);//.getNative()->u16.value; + + const ble_uuid_any_t* native = UUIDBig.getNative(); + if (native->u.type != 16){ + //not interested in 128 bit; + return 0; + } + uint16_t UUID = native->u16.value; + + char temp[60]; + BLE_ESP32::dump(temp, 13, addr, 6); + + if (BLE_ESP32::BLEDebugMode > 0) AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("M32: MI:%s svc[0] UUID (%x)"), temp, UUID); + std::string ServiceDataStr = advertisedDevice->getServiceData(0); + + uint32_t ServiceDataLength = ServiceDataStr.length(); + const uint8_t *ServiceData = (const uint8_t *)ServiceDataStr.data(); + BLE_ESP32::dump(temp, 60, ServiceData, ServiceDataLength); + if (BLE_ESP32::BLEDebugMode > 0) AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("M32: MI:%s"), temp); + + + if (UUID){ + // this will take and keep the mutex until the function is over + TasAutoMutex localmutex(&slotmutex, "Mi32AdCB2"); + switch(UUID){ + case 0xfe95: // std MI? + case 0xfdcd: // CGD1? + { + MI32ParseResponse(ServiceData, ServiceDataLength, addr, RSSI); + } break; + case 0x181a: { //ATC + MI32ParseATCPacket(ServiceData, ServiceDataLength, addr, RSSI); + } break; + + default:{ + } break; + } + } + return 0; +} + + +/*********************************************************************************************\ + * Helper functions +\*********************************************************************************************/ + +/** + * @brief Remove all colons from null terminated char array + * + * @param _string Typically representing a MAC-address like AA:BB:CC:DD:EE:FF + */ +void MI32stripColon(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; +} + +/** + * @brief Convert string that repesents a hexadecimal number to a byte array + * + * @param _string input string in format: AABBCCDDEEFF or AA:BB:CC:DD:EE:FF, caseinsensitive + * @param _mac target byte array must match the correct size (i.e. AA:BB -> uint8_t bytes[2]) + */ + +void MI32HexStringToBytes(char* _string, uint8_t* _byteArray) { + MI32stripColon(_string); + UpperCase(_string,_string); + uint32_t index = 0; + uint32_t _end = strlen(_string); + memset(_byteArray,0,_end/2); + while (index < _end) { + char c = _string[index]; + uint8_t value = 0; + if(c >= '0' && c <= '9') + value = (c - '0'); + else if (c >= 'A' && c <= 'F') + value = (10 + (c - 'A')); + _byteArray[(index/2)] += value << (((index + 1) % 2) * 4); + index++; + } +} + +/** + * @brief Reverse an array of 6 bytes + * + * @param _mac a byte array of size 6 (typicalliy representing a MAC address) + */ +void MI32_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)); +} + +#ifdef USE_MI_DECRYPTION +int MI32AddKey(char* payload, char* key = nullptr){ + mi_bindKey_t keyMAC; + + if (!key){ + MI32HexStringToBytes(payload,keyMAC.buf); + } else { + MI32HexStringToBytes(payload,keyMAC.MAC); + MI32HexStringToBytes(key,keyMAC.key); + } + + bool unknownKey = true; + for(uint32_t i=0; i 0) AddLog(LOG_LEVEL_DEBUG, PSTR("M32: Search key for MAC: %02x%02x%02x%02x%02x%02x"), mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); + for(uint32_t i=0; i 0) AddLog(LOG_LEVEL_DEBUG,PSTR("M32: Decryption Key found")); + foundNoKey = false; + break; + } + } + if(foundNoKey){ + AddLog(LOG_LEVEL_DEBUG,PSTR("M32: No Key found")); + return -2; // indicates needs key + } + + br_aes_small_ctrcbc_keys keyCtx; + br_aes_small_ctrcbc_init(&keyCtx, _bindkey, 16); + + br_ccm_context ctx; + br_ccm_init(&ctx, &keyCtx.vtable); + br_ccm_reset(&ctx, nonce, 12, 1, len, 4); + br_ccm_aad_inject(&ctx, authData, 1); + br_ccm_flip(&ctx); + + memcpy(payload, data, len); //we want to be sure about 4-byte alignement + br_ccm_run(&ctx, 0, payload, len); + memcpy(data, payload, len); //back to the packet + + + // crashed in here - why?, so give it more space to work with? + // returns 1 if matched, else 0 + int ret = br_ccm_check_tag(&ctx, &tag); + + if (BLE_ESP32::BLEDebugMode > 0) AddLog(LOG_LEVEL_DEBUG,PSTR("M32: Err:%i, Decrypted : %02x %02x %02x %02x %02x"), ret, payload[1],payload[2],payload[3],payload[4],payload[5]); + return ret-1; // -> -1=fail, 0=success +} + +#endif // USE_MI_DECRYPTION + + +// packet examples: +// MJ_HT_V1 +// 5020 AA01 41 3AF4DAA8654C 0A100109 +// 5020 AA01 43 3AF4DAA8654C 061002E901 +// 5020 AA01 48 3AF4DAA8654C 041002BF00 +// 5020 AA01 4A 3AF4DAA8654C 0D1004BF00E901 +// 7122 AA01 15 3AF4DAA8654C 0D 0200020D10 + +// LYWSD03 encrypted data: +// 5858 5B05 2F B3E30838C1A4 [69A9FBDF67] ,060000 0791C39A - 23bytes +// 23-9 = 14 +// -> nonce B3E30838C1A4|5B02|2F|060000 +// 23-6 = 17 +// -> tag 0791C39A +// datalen = 23 - 9 - 4 - 3 - 1 - 1 = 5 + +// CGD1 reconstructed from src: (svcdata on fdcd) +// xxyy FFEEDDCCBBAA MMMM TTTTHHHH|BB +// xxyy FFEEDDCCBBAA 0104 TTTTHHHH +// xxyy FFEEDDCCBBAA 0201 BB + + +int MIParsePacket(const uint8_t* slotmac, struct mi_beacon_data_t *parsed, const uint8_t *datain, int len){ + uint8_t data[32]; + memcpy(data, datain, len); + if (!parsed){ + return 0; + } + if (len < 5){ + return 0; + } + + int byteindex = 0; + + // 58 58 = 0x5858 = data|comp|mac|enc, v5|auth2 + // 30 58 = 0x5830 = comp|mac, v5|auth2 + // 30 50 = 0x5030 = comp|mac, v5|auth0 + // 48 59 = 0x5948 = data|enc, v5|auth2|registered + // 10 59 = 0x5910 = mac, v5|auth2|registered + // 71 22 = 0x2271 = data|comp|mac v2|bind + // 50 20 = 0x2050 = data|mac v2 - MJ_HT_V1 data + // 71 22 = 0x2271 = data|comp|mac|reserved1 v2|bind - MJ_HT_V1 pair + + // data from byte 0 - e.g. 30 + parsed->framedata.meshflag = (data[byteindex] & 0x80)>>7; //Byte 0: x....... + parsed->framedata.dataflag = (data[byteindex] & 0x40)>>6; //Byte 0: .x...... + parsed->framedata.compatibilityflag = (data[byteindex] & 0x20)>>5; //Byte 0: ..x..... - indicates compatibility data present + parsed->framedata.MACFlag = (data[byteindex] & 0x10)>>4; //Byte 0: ...x.... + parsed->framedata.isencrypted = (data[byteindex] & 0x08)>>3; //Byte 0: ....x... + parsed->framedata.reserved = (data[byteindex] & 0x03)>>6; //Byte 0: .....xxx + + // data from byte 1 - e.g. 58 + byteindex++; + parsed->framedata.version = (data[byteindex] & 0xf0)>>4; //Byte 0: xxxx.... + parsed->framedata.authMode = (data[byteindex] & 0x0C)>>6; //Byte 0: ....xx.. // e.g. 2 + parsed->framedata.bindingvalidreq = (data[byteindex] & 0x02)>>1; //Byte 0: ......x. + parsed->framedata.registeredflag = (data[byteindex] & 0x01); //Byte 0: .......x + + byteindex++; + + parsed->devicetype = *((uint16_t *)(data + byteindex)); + byteindex += 2; + parsed->framecnt = data[byteindex]; + //if (BLE_ESP32::BLEDebugMode > 0) AddLog(LOG_LEVEL_DEBUG, PSTR("M32: MI frame %d"), parsed->framecnt); + byteindex++; + + + if (parsed->framedata.version <= 3){ + // e.g. MJ_HT_V1 + } + + if (parsed->framedata.MACFlag){ + if (len < byteindex + 6){ + return 0; + } + memcpy(parsed->macdata.mac, &data[byteindex], 6); + byteindex += 6; + } + + int decres = 1; + // everything after MAC is encrypted if specified? + if (parsed->framedata.isencrypted){ + if (len < byteindex + 3+4+1){ + return 0; + } + const uint8_t* mac = slotmac; + if (parsed->framedata.MACFlag){ + mac = parsed->macdata.mac; + } + uint8_t nonce[12]; + uint8_t *p = nonce; + memcpy(p, mac, 6); + p += 6; + memcpy(p, &parsed->devicetype, 2); + p += 2; + *(p++) = parsed->framecnt; + uint8_t *extCnt = data +(len-7); + memcpy(p, extCnt, 3); + p += 3; + uint32_t tag = *(uint32_t *)(data + (len-4)); + + // decrypt the data in place + decres = MIDecryptPayload(mac, nonce, tag, data + byteindex, len - byteindex - 7); + // no longer need the nonce data. + len -= 7; + } + + switch(decres){ + case 1: // decrypt not requested + break; + case 0: // suceeded + parsed->needkey = KEY_REQUIRED_AND_FOUND; + if (BLE_ESP32::BLEDebugMode > 0) AddLog(LOG_LEVEL_DEBUG,PSTR("M32: Payload decrypted")); + break; + case -1: // key failed to work + parsed->needkey = KEY_REQUIRED_AND_INVALID; + AddLog(LOG_LEVEL_ERROR,PSTR("M32: Payload decrypt failed")); + parsed->payloadpresent = 0; + return 0; + break; + case -2: // key not present + parsed->needkey = KEY_REQUIRED_BUT_NOT_FOUND; + AddLog(LOG_LEVEL_ERROR,PSTR("M32: Payload encrypted but no key")); + parsed->payloadpresent = 0; + return 0; + break; + } + + // if set, there could be 1 or 3 bytes here. + if (parsed->framedata.compatibilityflag) { + if (len < byteindex + 1){ + return 0; + } + // e.g. in pair: 7122 AA01 15 3AF4DAA8654C [0D] 0200020D10 -> bond|unused2 + parsed->compatibility.reserved = (data[byteindex] & 0xc0) >> 6; //Byte 0: xx...... + parsed->compatibility.IOcap = (data[byteindex] & 0x20) >> 5; //Byte 0: ..x..... + parsed->compatibility.bondability = (data[byteindex] & 0x18) >> 3; //Byte 0: ...xx... + parsed->compatibility.unused = (data[byteindex] & 0x07) >> 0; //Byte 0: .....xxx + byteindex ++; + + if (parsed->compatibility.IOcap) { + if (len < byteindex + 2){ + return 0; + } + parsed->compatibility.IOCapability = *((uint16_t *)(data + byteindex)); // bytes 1-2, e.g. 01 00 -> 0001 + byteindex += 2; + } + } + + // rest is payload + int rem = (len - byteindex); + if (rem > sizeof(parsed->payload)){ + rem = sizeof(parsed->payload); + return 0; + } + + if ((len - byteindex) == 0){ + if (BLE_ESP32::BLEDebugMode > 0) AddLog(LOG_LEVEL_DEBUG,PSTR("M32: No payload")); + parsed->payload.size = 0; + parsed->payloadpresent = 0; + return 0; + } + + // we have payload which did not need decrypt. + if (decres == 1){ + parsed->needkey = KEY_NOT_REQUIRED; + if (BLE_ESP32::BLEDebugMode > 0) AddLog(LOG_LEVEL_DEBUG,PSTR("M32: Payload unencrypted")); + } + + // already decrypted if required + parsed->payloadpresent = 1; + memcpy(&parsed->payload, (data + byteindex), (len - byteindex)); + if (parsed->payload.size != (len - byteindex) - 3){ + AddLog(LOG_LEVEL_DEBUG,PSTR("M32: Payload length mismatch")); + } + + return 1; +} + + + + +#ifdef USE_HOME_ASSISTANT +/** + * @brief For HASS only, changes last entry of JSON in mqtt_data to 'null' + */ + +void MI32nullifyEndOfMQTT_DATA(){ + char *p = TasmotaGlobal.mqtt_data + strlen(TasmotaGlobal.mqtt_data); + while(true){ + *p--; + if(p[0]==':'){ + p[1] = 0; + break; + } + } + ResponseAppend_P(PSTR("null")); +} +#endif // USE_HOME_ASSISTANT + +/*********************************************************************************************\ + * common functions +\*********************************************************************************************/ + + +/** + * @brief Return the slot number of a known sensor or return create new sensor slot + * + * @param _MAC BLE address of the sensor + * @param _type Type number of the sensor + * @return uint32_t Known or new slot in the sensors-vector + */ +uint32_t MIBLEgetSensorSlot(const uint8_t *mac, uint16_t _type, uint8_t counter){ + + //AddLog(LOG_LEVEL_DEBUG_MORE,PSTR("M32: %s: will test ID-type: %x"),D_CMND_MI32, _type); + bool _success = false; + for (uint32_t i=0; i < MI_MI32_TYPES; i++){ // i < sizeof(kMI32DeviceID) gives compiler warning + if(_type == kMI32DeviceID[i]){ + _type = i+1; + _success = true; + break; + } + else { + //AddLog(LOG_LEVEL_DEBUG_MORE,PSTR("M32: %s: ID-type is not: %x"),D_CMND_MI32,kMI32DeviceID[i]); + } + } + if(!_success) { + _type = 1; // unknown + } + + //AddLog(LOG_LEVEL_DEBUG_MORE,PSTR("M32: %s: vector size %u"),D_CMND_MI32, MIBLEsensors.size()); + for(uint32_t i=0; i 0) AddLog(LOG_LEVEL_DEBUG_MORE,PSTR("M32: %s: slot: %u/%u - ign repeat"),D_CMND_MI32, i, MIBLEsensors.size()); + //return 0xff; // packet received before, stop here + } + if (BLE_ESP32::BLEDebugMode > 0) AddLog(LOG_LEVEL_DEBUG,PSTR("M32: Frame %d, last %d"), counter, MIBLEsensors[i].lastCnt); + MIBLEsensors[i].lastCnt = counter; + if (BLE_ESP32::BLEDebugMode > 0) AddLog(LOG_LEVEL_DEBUG_MORE,PSTR("M32: %s: slot: %u/%u"),D_CMND_MI32, i, MIBLEsensors.size()); + + if (MIBLEsensors[i].type != _type){ + // this happens on incorrectly configured pvvx ATC firmware + AddLog(LOG_LEVEL_ERROR,PSTR("M32: %s: slot: %u - device type 0x%04x(%s) -> 0x%04x(%s) - check device is only sending one type of advert."),D_CMND_MI32, i, + kMI32DeviceID[MIBLEsensors[i].type-1], kMI32DeviceType[MIBLEsensors[i].type-1], kMI32DeviceID[_type-1], kMI32DeviceType[_type-1]); + MIBLEsensors[i].type = _type; + } + + return i; + } + //AddLog(LOG_LEVEL_DEBUG_MORE,PSTR("M32: %s: i: %x %x %x %x %x %x"),D_CMND_MI32, MIBLEsensors[i].MAC[5], MIBLEsensors[i].MAC[4],MIBLEsensors[i].MAC[3],MIBLEsensors[i].MAC[2],MIBLEsensors[i].MAC[1],MIBLEsensors[i].MAC[0]); + //AddLog(LOG_LEVEL_DEBUG_MORE,PSTR("M32: %s: n: %x %x %x %x %x %x"),D_CMND_MI32, mac[5], mac[4], mac[3],mac[2],mac[1],mac[0]); + } + //AddLog(LOG_LEVEL_DEBUG_MORE,PSTR("M32: %s: new sensor -> slot: %u"),D_CMND_MI32, MIBLEsensors.size()); + //AddLog(LOG_LEVEL_DEBUG_MORE,PSTR("M32: %s: found new sensor"),D_CMND_MI32); + mi_sensor_t _newSensor; + memset(&_newSensor, 0 , sizeof(_newSensor)); + memcpy(_newSensor.MAC, mac, 6); + _newSensor.type = _type; + _newSensor.eventType.raw = 0; + _newSensor.feature.raw = 0; + _newSensor.temp = NAN; + _newSensor.needkey = KEY_REQUIREMENT_UNKNOWN; + _newSensor.bat = 0x00; + _newSensor.RSSI = 0xffff; + _newSensor.lux = 0x00ffffff; + + switch (_type) + { + case MI_FLORA: + _newSensor.moisture =0xff; + _newSensor.fertility =0xffff; + _newSensor.firmware[0]='\0'; + _newSensor.feature.temp=1; + _newSensor.feature.moist=1; + _newSensor.feature.fert=1; + _newSensor.feature.lux=1; + _newSensor.feature.bat=1; + break; + case MI_NLIGHT: + _newSensor.events=0x00; + _newSensor.feature.PIR=1; + _newSensor.feature.NMT=1; + break; + case MI_MJYD2S: + _newSensor.NMT=0; + _newSensor.events=0x00; + _newSensor.feature.PIR=1; + _newSensor.feature.NMT=1; + _newSensor.feature.lux=1; + _newSensor.feature.bat=1; + break; + case MI_YEERC: + _newSensor.feature.Btn=1; + break; + default: + _newSensor.hum=NAN; + _newSensor.feature.temp=1; + _newSensor.feature.hum=1; + _newSensor.feature.tempHum=1; + _newSensor.feature.bat=1; + break; + } + MIBLEsensors.push_back(_newSensor); + AddLog(LOG_LEVEL_DEBUG,PSTR("M32: %s: new %s at slot: %u"),D_CMND_MI32, kMI32DeviceType[_type-1],MIBLEsensors.size()-1); + MI32.mode.shallShowStatusInfo = 1; + return MIBLEsensors.size()-1; +}; + +/** + * @brief trigger real-time message for PIR or RC + * + */ +void MI32triggerTele(void){ + MI32.mode.triggeredTele = 1; + MI32ShowTriggeredSensors(); + MI32.mode.triggeredTele = 0; +} + +/** + * @brief Is called after every finding of new BLE sensor + * + */ +void MI32StatusInfo() { + MI32.mode.shallShowStatusInfo = 0; + Response_P(PSTR("{\"%s\":{\"found\":%u}}"), D_CMND_MI32, MIBLEsensors.size()); + XdrvRulesProcess(); +} + +/*********************************************************************************************\ + * BLE callbacks section + * These are called from main thread only. +\*********************************************************************************************/ + + +int MI32scanCompleteCallback(NimBLEScanResults results){ + // we actually don't need to do anything here.... + if (BLE_ESP32::BLEDebugMode > 0) AddLog(LOG_LEVEL_DEBUG,PSTR("M32: Scan complete")); + return 0; +} + + +/*********************************************************************************************\ + * init BLE_32 +\*********************************************************************************************/ + + +void MI32Init(void) { + MIBLEsensors.reserve(10); + MIBLEbindKeys.reserve(10); + MI32.mode.init = false; + + //test section for options + MI32.option.allwaysAggregate = 1; + MI32.option.noSummary = 0; + MI32.option.minimalSummary = 0; + MI32.option.directBridgeMode = 0; + MI32.option.showRSSI = 1; + MI32.option.ignoreBogusBattery = 1; // from advertisements + MI32.option.holdBackFirstAutodiscovery = 1; + + BLE_ESP32::registerForAdvertismentCallbacks((const char *)"MI32", MI32advertismentCallback); + BLE_ESP32::registerForScanCallbacks((const char *)"MI32", MI32scanCompleteCallback); + // note: for operations, we will set individual callbacks in the operations we request + //void registerForOpCallbacks(const char *tag, BLE_ESP32::OPCOMPLETE_CALLBACK* pFn); + + AddLog(LOG_LEVEL_INFO,PSTR("M32: init: request callbacks")); + MI32.period = Settings.tele_period; + MI32.mode.init = 1; + return; +} + + +/*********************************************************************************************\ + * Task section +\*********************************************************************************************/ + + + + +int MIParseBatt(int slot, uint8_t *data, int len){ + int value = data[0]; + char slotMAC[13]; + BLE_ESP32::dump(slotMAC, sizeof(slotMAC), MIBLEsensors[slot].MAC, 6) ; + + if ((value != 0) && (value < 101)){ + MIBLEsensors[slot].bat = value; + if(MIBLEsensors[slot].type==MI_FLORA){ + if (len < 7){ + AddLog(LOG_LEVEL_ERROR,PSTR("M32: FLORA: not enough bytes read for firmware?")); + } else { + memcpy(MIBLEsensors[slot].firmware, data+2, 5); + MIBLEsensors[slot].firmware[5] = '\0'; + AddLog(LOG_LEVEL_DEBUG,PSTR("M32: %s: FLORA Firmware: %s"),D_CMND_MI32,MIBLEsensors[slot].firmware); + } + } + MIBLEsensors[slot].eventType.bat = 1; + MIBLEsensors[slot].shallSendMQTT = 1; + MI32.mode.shallTriggerTele = 1; + if (BLE_ESP32::BLEDebugMode > 0) AddLog(LOG_LEVEL_DEBUG,PSTR("M32: Batt read for %s complete %d"), slotMAC, value); + } else { + AddLog(LOG_LEVEL_ERROR,PSTR("M32: Batt read for %s complete but out of range 1-101 (%d)"), slotMAC, value); + } + + return 0; +} + +/*********************************************************************************************\ + * parse the response from advertisements +\*********************************************************************************************/ + + +void MI32ParseATCPacket(const uint8_t * _buf, uint32_t length, const uint8_t *addr, int RSSI){ + ATCPacket_t *_packet = (ATCPacket_t*)_buf; + PVVXPacket_t *ppv_packet = (PVVXPacket_t*)_buf; + + + if (length == 15){ // 19-1-1-2 + uint8_t addrrev[6]; + memcpy(addrrev, addr, 6); + MI32_ReverseMAC(addrrev); + if (!memcmp(addrrev, ppv_packet->MAC, 6)){ + //int16_t temperature; // x 0.1 degree + //uint16_t humidity; // x 0.01 % + //uint16_t battery_mv; // mV + //uint8_t battery_level; // 0..100 % + //uint8_t counter; // measurement count + //uint8_t flags; + + uint32_t _slot = MIBLEgetSensorSlot(addr, 0x0a1c, ppv_packet->counter); // This must be a hard-coded fake ID + if(_slot==0xff) return; + + if ((_slot >= 0) && (_slot < MIBLEsensors.size())){ + if (BLE_ESP32::BLEDebugMode > 0) AddLog(LOG_LEVEL_DEBUG,PSTR("M32: %s:pvvx at slot %u"), kMI32DeviceType[MIBLEsensors[_slot].type-1],_slot); + MIBLEsensors[_slot].RSSI=RSSI; + MIBLEsensors[_slot].needkey=KEY_NOT_REQUIRED; + + MIBLEsensors[_slot].temp = (float)(ppv_packet->temperature)/100.0f; + MIBLEsensors[_slot].hum = (float)(ppv_packet->humidity)/100.0f; + MIBLEsensors[_slot].eventType.tempHum = 1; + MIBLEsensors[_slot].bat = ppv_packet->battery_level; + MIBLEsensors[_slot].eventType.bat = 1; + + if(MI32.option.directBridgeMode) { + MIBLEsensors[_slot].shallSendMQTT = 1; + MI32.mode.shallTriggerTele = 1; + } + } + return; + } else { + AddLog(LOG_LEVEL_ERROR, PSTR("M32: PVVX packet mac mismatch - ignored?")); + return; + } + } + + + uint8_t addrrev[6]; + memcpy(addrrev, addr, 6); + //MI32_ReverseMAC(addrrev); + + // if packet tell a different address to origin, use the different address + if (memcmp(addrrev, _packet->MAC, 6)){ + MI32_ReverseMAC(_packet->MAC); + if (!memcmp(addrrev, _packet->MAC, 6)){ + AddLog(LOG_LEVEL_ERROR, PSTR("M32: ATC packet with reversed MAC addr?")); + } else { + AddLog(LOG_LEVEL_ERROR, PSTR("M32: ATC packet with MAC addr mismatch - is this mesh?")); + memcpy(addrrev, _packet->MAC, 6); + } + addr = addrrev; + } + + uint32_t _slot = MIBLEgetSensorSlot(addr, 0x0a1c, _packet->frameCnt); // This must be a hard-coded fake ID + + if(_slot==0xff) return; + + if ((_slot >= 0) && (_slot < MIBLEsensors.size())){ + if (BLE_ESP32::BLEDebugMode > 0) AddLog(LOG_LEVEL_DEBUG,PSTR("M32: %s at slot %u"), kMI32DeviceType[MIBLEsensors[_slot].type-1],_slot); + MIBLEsensors[_slot].RSSI=RSSI; + MIBLEsensors[_slot].needkey=KEY_NOT_REQUIRED; + + MIBLEsensors[_slot].temp = (float)(int16_t(__builtin_bswap16(_packet->temp)))/10.0f; + MIBLEsensors[_slot].hum = (float)_packet->hum; + MIBLEsensors[_slot].eventType.tempHum = 1; + MIBLEsensors[_slot].bat = _packet->batPer; + MIBLEsensors[_slot].eventType.bat = 1; + + if(MI32.option.directBridgeMode) { + MIBLEsensors[_slot].shallSendMQTT = 1; + MI32.mode.shallTriggerTele = 1; + } + } else { + + } +} + +//////////////////////////////////////////////////////////// +// this SHOULD parse any MI payload. +int MI32parseMiPayload(int _slot, struct mi_beacon_data_t *parsed){ + struct mi_beacon_data_payload_data_t *pld = + (struct mi_beacon_data_payload_data_t *) &parsed->payload.data; + int res = 1; + + if (!parsed->payloadpresent){ + return 0; + } + + char tmp[20]; + BLE_ESP32::dump(tmp, 20, (uint8_t*)&(parsed->payload), parsed->payload.size+3); + if (BLE_ESP32::BLEDebugMode > 0) AddLog(LOG_LEVEL_DEBUG_MORE,PSTR("M32: MI%d payload %s"), _slot, tmp); + + switch(parsed->payload.type){ + case 0x01: // button press + MIBLEsensors[_slot].Btn = pld->Btn.num + (pld->Btn.longPress/2)*6; + MIBLEsensors[_slot].eventType.Btn = 1; + MI32.mode.shallTriggerTele = 1; + break; + case 0x02: + res = 0; + break; + case 0x03: {// motion? 1 byte + uint8_t motion = parsed->payload.data[0]; + res = 0; + }break; + case 0x04:{ + float _tempFloat=(float)(pld->temp)/10.0f; + if(_tempFloat<60){ + MIBLEsensors[_slot].temp=_tempFloat; + MIBLEsensors[_slot].eventType.temp = 1; + if (BLE_ESP32::BLEDebugMode > 0) AddLog(LOG_LEVEL_DEBUG_MORE,PSTR("M32: Mode 4: temp updated")); + } else { + if (BLE_ESP32::BLEDebugMode > 0) AddLog(LOG_LEVEL_DEBUG_MORE,PSTR("M32: Mode 4: temp ignored > 60 (%f)"), _tempFloat); + } + // AddLog(LOG_LEVEL_DEBUG,PSTR("M32: Mode 4: U16: %u Temp"), _beacon.temp ); + } break; + case 0x06: { + float _tempFloat=(float)(pld->hum)/10.0f; + if(_tempFloat<101){ + MIBLEsensors[_slot].hum=_tempFloat; + MIBLEsensors[_slot].eventType.hum = 1; + if (BLE_ESP32::BLEDebugMode > 0) AddLog(LOG_LEVEL_DEBUG_MORE,PSTR("M32: Mode 6: hum updated")); + } else { + if (BLE_ESP32::BLEDebugMode > 0) AddLog(LOG_LEVEL_DEBUG_MORE,PSTR("M32: Mode 6: hum ignored > 101 (%f)"), _tempFloat); + } + // AddLog(LOG_LEVEL_DEBUG,PSTR("M32: Mode 6: U16: %u Hum"), _beacon.hum); + } break; + case 0x07: + MIBLEsensors[_slot].lux=pld->lux & 0x00ffffff; + if(MIBLEsensors[_slot].type==MI_MJYD2S){ + MIBLEsensors[_slot].eventType.noMotion = 1; + } + MIBLEsensors[_slot].eventType.lux = 1; + // AddLog(LOG_LEVEL_DEBUG,PSTR("M32: Mode 7: U24: %u Lux"), _beacon.lux & 0x00ffffff); + break; + case 0x08: + MIBLEsensors[_slot].moisture=pld->moist; + MIBLEsensors[_slot].eventType.moist = 1; + if (BLE_ESP32::BLEDebugMode > 0) AddLog(LOG_LEVEL_DEBUG_MORE,PSTR("M32: Mode 8: moisture updated")); + // AddLog(LOG_LEVEL_DEBUG,PSTR("M32: Mode 8: U8: %u Moisture"), _beacon.moist); + break; + case 0x09: // 'conductivity' + MIBLEsensors[_slot].fertility=pld->fert; + MIBLEsensors[_slot].eventType.fert = 1; + if (BLE_ESP32::BLEDebugMode > 0) AddLog(LOG_LEVEL_DEBUG_MORE,PSTR("M32: Mode 9: fertility updated")); + // AddLog(LOG_LEVEL_DEBUG,PSTR("M32: Mode 9: U16: %u Fertility"), _beacon.fert); + break; + case 0x0a: + if(MI32.option.ignoreBogusBattery){ + if(MIBLEsensors[_slot].type==MI_LYWSD03MMC || MIBLEsensors[_slot].type==MI_MHOC401){ + res = 0; + break; + } + } + if(pld->bat<101){ + MIBLEsensors[_slot].bat = pld->bat; + MIBLEsensors[_slot].eventType.bat = 1; + if (BLE_ESP32::BLEDebugMode > 0) AddLog(LOG_LEVEL_DEBUG_MORE,PSTR("M32: Mode a: bat updated")); + } else { + MIBLEsensors[_slot].bat = 100; + MIBLEsensors[_slot].eventType.bat = 1; + if (BLE_ESP32::BLEDebugMode > 0) AddLog(LOG_LEVEL_DEBUG_MORE,PSTR("M32: Mode a: bat > 100 (%d)"), pld->bat); + } + // AddLog(LOG_LEVEL_DEBUG,PSTR("M32: Mode a: U8: %u %%"), _beacon.bat); + break; + case 0x0d:{ + float _tempFloat=(float)(pld->HT.temp)/10.0f; + if(_tempFloat < 60){ + MIBLEsensors[_slot].temp = _tempFloat; + if (BLE_ESP32::BLEDebugMode > 0) AddLog(LOG_LEVEL_DEBUG_MORE,PSTR("M32: Mode d: temp updated")); + } else { + if (BLE_ESP32::BLEDebugMode > 0) AddLog(LOG_LEVEL_DEBUG_MORE,PSTR("M32: Mode d: temp ignored > 60 (%f)"), _tempFloat); + } + _tempFloat=(float)(pld->HT.hum)/10.0f; + if(_tempFloat < 100){ + MIBLEsensors[_slot].hum = _tempFloat; + if (BLE_ESP32::BLEDebugMode > 0) AddLog(LOG_LEVEL_DEBUG_MORE,PSTR("M32: Mode d: hum updated")); + } else { + if (BLE_ESP32::BLEDebugMode > 0) AddLog(LOG_LEVEL_DEBUG_MORE,PSTR("M32: Mode d: hum ignored > 100 (%f)"), _tempFloat); + } + MIBLEsensors[_slot].eventType.tempHum = 1; + // AddLog(LOG_LEVEL_DEBUG,PSTR("M32: Mode d: U16: %x Temp U16: %x Hum"), _beacon.HT.temp, _beacon.HT.hum); + } break; + case 0x0f: + if (parsed->payload.ten != 0) break; + MIBLEsensors[_slot].eventType.motion = 1; + MIBLEsensors[_slot].lastTime = millis(); + MIBLEsensors[_slot].events++; + MIBLEsensors[_slot].lux = pld->lux; + MIBLEsensors[_slot].eventType.lux = 1; + MIBLEsensors[_slot].NMT = 0; + MI32.mode.shallTriggerTele = 1; + // AddLog(LOG_LEVEL_DEBUG,PSTR("M32: PIR: primary"),MIBLEsensors[_slot].lux ); + break; + case 0x10:{ // 'formaldehide' + const uint16_t f = uint16_t(parsed->payload.data[0]) | (uint16_t(parsed->payload.data[1]) << 8); + float formaldehyde = (float)f / 100.0f; + res = 0; + } break; + case 0x12:{ // 'active' + int active = parsed->payload.data[0]; + res = 0; + } break; + case 0x13:{ //mosquito tablet + int tablet = parsed->payload.data[0]; + res = 0; + } break; + case 0x17:{ + const uint32_t idle_time = + uint32_t(parsed->payload.data[0]) | (uint32_t(parsed->payload.data[1]) << 8) | (uint32_t(parsed->payload.data[2]) << 16) | (uint32_t(parsed->payload.data[2]) << 24); + float idlemins = (float)idle_time / 60.0f; + int has_motion = (idle_time) ? 0 : 0; + + MIBLEsensors[_slot].NMT = pld->NMT; + MIBLEsensors[_slot].eventType.NMT = 1; + MI32.mode.shallTriggerTele = 1; + // AddLog(LOG_LEVEL_DEBUG,PSTR("M32: Mode 17: NMT: %u seconds"), _beacon.NMT); + } break; + + default: { + AddLog(LOG_LEVEL_DEBUG,PSTR("M32: Unknown MI pld")); + res = 0; + } break; + } + + if(res && MI32.option.directBridgeMode) { + MIBLEsensors[_slot].shallSendMQTT = 1; + MI32.mode.shallTriggerTele = 1; + } + + return res; +} + +//////////////////////////////////////////////////////////// +// this SHOULD parse any MI packet, including encrypted. +void MI32ParseResponse(const uint8_t *buf, uint16_t bufsize, const uint8_t* addr, int RSSI) { + struct mi_beacon_data_t parsed; + memset(&parsed, 0, sizeof(parsed)); + int res = MIParsePacket(addr, &parsed, buf, bufsize); + + uint8_t addrrev[6]; + memcpy(addrrev, addr, 6); + MI32_ReverseMAC(addrrev); + + if (memcmp(addrrev, parsed.macdata.mac, 6)){ + AddLog(LOG_LEVEL_ERROR, PSTR("M32: MI packet with MAC addr mismatch - is this mesh?")); + memcpy(addrrev, parsed.macdata.mac, 6); + MI32_ReverseMAC(addrrev); + addr = addrrev; + } + + uint16_t _slot = MIBLEgetSensorSlot( addr, parsed.devicetype, parsed.framecnt ); + if(_slot==0xff) return; + if ((_slot >= 0) && (_slot < MIBLEsensors.size())){ + if (parsed.needkey != KEY_REQUIREMENT_UNKNOWN){ + MIBLEsensors[_slot].needkey = parsed.needkey; + } + MIBLEsensors[_slot].RSSI=RSSI; + if (!res){ // - if the payload is not valid + if (BLE_ESP32::BLEDebugMode > 0) AddLog(LOG_LEVEL_DEBUG, PSTR("M32: MIParsePacket returned %d"), res); + return; + } else { + } + MI32parseMiPayload(_slot, &parsed); + } +} + +bool MI32isInBlockList(const uint8_t* MAC){ + bool isBlocked = false; + for(auto &_blockedMAC : MIBLEBlockList){ + if(memcmp(_blockedMAC.buf,MAC,6) == 0) isBlocked = true; + } + return isBlocked; +} + +void MI32removeMIBLEsensor(uint8_t* MAC){ + // this will take and keep the mutex until the function is over + TasAutoMutex localmutex(&slotmutex, "Mi32Rem"); + + MIBLEsensors.erase( std::remove_if( MIBLEsensors.begin() , MIBLEsensors.end(), [MAC]( mi_sensor_t _sensor )->bool + { return (memcmp(_sensor.MAC,MAC,6) == 0); } + ), end( MIBLEsensors ) ); +} +/***********************************************************************\ + * Read data from connections +\***********************************************************************/ + +void MI32notifyHT_LY(int slot, char *_buf, int len){ + if (BLE_ESP32::BLEDebugMode > 0) AddLog(LOG_LEVEL_DEBUG_MORE,PSTR("M32: %s: raw data: %x%x%x%x%x%x%x"),D_CMND_MI32,_buf[0],_buf[1],_buf[2],_buf[3],_buf[4],_buf[5],_buf[6]); + // the value 0b00 is 28.16 C? + if(_buf[0] != 0 || _buf[1] != 0){ + memcpy(&LYWSD0x_HT,(void *)_buf,sizeof(LYWSD0x_HT)); + if (BLE_ESP32::BLEDebugMode > 0) AddLog(LOG_LEVEL_DEBUG, PSTR("M32: %s: T * 100: %u, H: %u, V: %u"),D_CMND_MI32,LYWSD0x_HT.temp,LYWSD0x_HT.hum, LYWSD0x_HT.volt); + uint32_t _slot = slot; + + if (BLE_ESP32::BLEDebugMode > 0) AddLog(LOG_LEVEL_DEBUG_MORE,PSTR("M32: MIBLE: Sensor slot: %u"), _slot); + static float _tempFloat; + _tempFloat=(float)(LYWSD0x_HT.temp)/100.0f; + if(_tempFloat<60){ + MIBLEsensors[_slot].temp=_tempFloat; + // MIBLEsensors[_slot].showedUp=255; // this sensor is real + } + _tempFloat=(float)LYWSD0x_HT.hum; + if(_tempFloat<100){ + MIBLEsensors[_slot].hum = _tempFloat; + if (BLE_ESP32::BLEDebugMode > 0) AddLog(LOG_LEVEL_DEBUG_MORE,PSTR("M32: LYWSD0x: hum updated")); + } + MIBLEsensors[_slot].eventType.tempHum = 1; + if (MIBLEsensors[_slot].type == MI_LYWSD03MMC || MIBLEsensors[_slot].type == MI_MHOC401){ + // ok, so CR2032 is 3.0v, but drops immediately to ~2.9. + // so we'll go with the 2.1 min, 2.95 max. + float minVolts = 2100.0; + //float maxVolts = 2950.0; + //float range = maxVolts - minVolts; + //float divisor = range/100; // = 8.5 + float percent = (((float)LYWSD0x_HT.volt) - minVolts)/ 8.5; //divisor; + if (percent > 100) percent = 100; + + MIBLEsensors[_slot].bat = (int) percent; + MIBLEsensors[_slot].eventType.bat = 1; + } + if(MI32.option.directBridgeMode) { + MIBLEsensors[_slot].shallSendMQTT = 1; + MI32.mode.shallTriggerTele = 1; + } + } +} + + +/** + * @brief Launch functions from Core 1 to make race conditions less likely + * + */ + +void MI32Every50mSecond(){ + + if(MI32.mode.shallTriggerTele){ + MI32.mode.shallTriggerTele = 0; + MI32triggerTele(); + } +} + +/** + * @brief Main loop of the driver, "high level"-loop + * + */ + +void MI32EverySecond(bool restart){ + +// AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("M32: onesec")); + MI32TimeoutSensors(); + + MI32ShowSomeSensors(); + + MI32DiscoveryOneMISensor(); + MI32ShowOneMISensor(); + + // read a battery if + // MI32.batteryreader.slot < filled and !MI32.batteryreader.active + readOneBat(); + + + // read a sensor if + // MI32.sensorreader.slot < filled and !MI32.sensorreader.active + // for sensors which need to get data through notify... + readOneSensor(); + + if (MI32.secondsCounter >= MI32.period){ + // only if we finished the last read + if (MI32.sensorreader.slot >= MIBLEsensors.size()){ + AddLog(LOG_LEVEL_DEBUG,PSTR("M32: Kick off readOneSensor")); + // kick off notification sensor reading every period. + MI32.sensorreader.slot = 0; + MI32.secondsCounter = 0; + } + } + MI32.secondsCounter ++; + + if (MI32.secondsCounter2 >= MI32.period){ + if (MI32.mqttCurrentSlot >= MIBLEsensors.size()){ + AddLog(LOG_LEVEL_DEBUG,PSTR("M32: Kick off tele sending")); + MI32.mqttCurrentSlot = 0; + MI32.secondsCounter2 = 0; + MI32.mqttCurrentSingleSlot = 0; + } else { + AddLog(LOG_LEVEL_DEBUG,PSTR("M32: Hit tele time, restarted but not finished last - lost from slot %d")+MI32.mqttCurrentSlot); + MI32.mqttCurrentSlot = 0; + MI32.secondsCounter2 = 0; + MI32.mqttCurrentSingleSlot = 0; + } + } + MI32.secondsCounter2++; + + static uint32_t _counter = MI32.period - 15; + static uint32_t _nextSensorSlot = 0; + uint32_t _idx = 0; + + int numsensors = MIBLEsensors.size(); + for (uint32_t i = 0; i < numsensors; i++) { + if(MIBLEsensors[i].type==MI_NLIGHT || MIBLEsensors[i].type==MI_MJYD2S){ + MIBLEsensors[i].NMT++; + } + } + + if(MI32.mode.shallShowStatusInfo == 1){ + MI32StatusInfo(); + } +} + +/*********************************************************************************************\ + * Commands +\*********************************************************************************************/ + +void CmndMi32Period(void) { + if (XdrvMailbox.data_len > 0) { + if (1 == XdrvMailbox.payload) { + MI32EverySecond(true); + } else { + MI32.period = XdrvMailbox.payload; + } + } + ResponseCmndNumber(MI32.period); +} + +int findSlot(char *addrOrAlias){ + uint8_t mac[6]; + int res = BLE_ESP32::getAddr(mac, addrOrAlias); + if (!res) return -1; + + for (int i = MIBLEsensors.size()-1; i >= 0 ; i--) { + if (!memcmp(MIBLEsensors[i].MAC, mac, 6)){ + return i; + } + } + return -1; +} + + +void CmndMi32Time(void) { + if (XdrvMailbox.data_len > 0) { + int slot = findSlot(XdrvMailbox.data); + if (slot < 0) { + slot = XdrvMailbox.payload; + } + if (MIBLEsensors.size() > slot) { + int res = genericTimeWriteFn(slot); + if (res > 0){ + if (BLE_ESP32::BLEDebugMode > 0) AddLog(LOG_LEVEL_DEBUG, PSTR("M32: will set Time")); + ResponseCmndNumber(slot); + return; + } + if (res < 0) { + AddLog(LOG_LEVEL_ERROR, PSTR("M32: cannot set Time on sensor type")); + } + if (res == 0) { + AddLog(LOG_LEVEL_ERROR, PSTR("M32: cannot set Time right now")); + } + } + } + ResponseCmndChar_P("fail"); +} + +void CmndMi32Page(void) { + if (XdrvMailbox.payload > 0) { + MI32.perPage = XdrvMailbox.payload; + } + ResponseCmndNumber(MI32.perPage); +} + +// read ALL battery values where we can? +void CmndMi32Battery(void) { + // trigger a read cycle + MI32.batteryreader.slot = 0; + ResponseCmndDone(); +} + + +void CmndMi32Unit(void) { + if (XdrvMailbox.data_len > 0) { + int slot = findSlot(XdrvMailbox.data); + if (slot < 0) { + slot = XdrvMailbox.payload; + } + + if (MIBLEsensors.size() > slot) { + // TOGGLE unit? + int res = genericUnitWriteFn(slot, -1); + if (res > 0){ + if (BLE_ESP32::BLEDebugMode > 0) AddLog(LOG_LEVEL_DEBUG, PSTR("M32: will toggle Unit")); + ResponseCmndNumber(slot); + return; + } + if (res < 0) { + AddLog(LOG_LEVEL_ERROR, PSTR("M32: cannot toggle Unit on sensor type")); + } + if (res == 0) { + AddLog(LOG_LEVEL_ERROR, PSTR("M32: cannot toggle Unit right now")); + } + } + } + ResponseCmndIdxChar(PSTR("Invalid")); +} + +#ifdef USE_MI_DECRYPTION +void CmndMi32Key(void) { + if (44 == XdrvMailbox.data_len) { // a KEY-MAC-string + MI32AddKey(XdrvMailbox.data, nullptr); + MI32KeyListResp(); + } else { + ResponseCmndIdxChar(PSTR("Invalid")); + } +} +#endif // USE_MI_DECRYPTION + +void MI32BlockListResp(){ + Response_P(PSTR("{\"MI32Block\":{")); + for (int i = 0; i < MIBLEBlockList.size(); i++){ + if (i){ + ResponseAppend_P(PSTR(",")); + } + char tmp[20]; + ToHex_P(MIBLEBlockList[i].buf,6,tmp,20,0); + ResponseAppend_P(PSTR("\"%s\":1"), tmp); + } + ResponseAppend_P(PSTR("}}")); +} + + +void CmndMi32Block(void){ + if (XdrvMailbox.data_len == 0) { + switch (XdrvMailbox.index) { + case 0: { + //TasAutoMutex localmutex(&slotmutex, "Mi32Block1"); + MIBLEBlockList.clear(); + } break; + default: + case 1: + break; + } + MI32BlockListResp(); + return; + } + + MAC_t _MACasBytes; + int res = BLE_ESP32::getAddr(_MACasBytes.buf, XdrvMailbox.data); + if (!res){ + ResponseCmndIdxChar(PSTR("Addr invalid")); + return; + } + + //MI32HexStringToBytes(XdrvMailbox.data,_MACasBytes.buf); + switch (XdrvMailbox.index) { + case 0: { + //TasAutoMutex localmutex(&slotmutex, "Mi32Block2"); + MIBLEBlockList.erase( std::remove_if( begin( MIBLEBlockList ), end( MIBLEBlockList ), [_MACasBytes]( MAC_t& _entry )->bool + { return (memcmp(_entry.buf,_MACasBytes.buf,6) == 0); } + ), end( MIBLEBlockList ) ); + } break; + case 1: { + //TasAutoMutex localmutex(&slotmutex, "Mi32Block3"); + bool _notYetInList = true; + for (auto &_entry : MIBLEBlockList) { + if (memcmp(_entry.buf,_MACasBytes.buf,6) == 0){ + _notYetInList = false; + } + } + if (_notYetInList) { + MIBLEBlockList.push_back(_MACasBytes); + MI32removeMIBLEsensor(_MACasBytes.buf); + } + // AddLog(LOG_LEVEL_INFO,PSTR("M32: size of ilist: %u"), MIBLEBlockList.size()); + } break; + } + MI32BlockListResp(); +} + +void CmndMi32Option(void){ + bool onOff = atoi(XdrvMailbox.data); + switch(XdrvMailbox.index) { + case 0: + MI32.option.allwaysAggregate = onOff; + break; + case 1: + MI32.option.noSummary = onOff; + break; + case 2: + MI32.option.directBridgeMode = onOff; + break; + case 4:{ + MI32.option.ignoreBogusBattery = onOff; + } break; + case 5:{ + MI32.option.onlyAliased = onOff; + if (MI32.option.onlyAliased){ + // discard all sensors for a restart + MIBLEsensors.clear(); + } + } break; + } + ResponseCmndDone(); +} + +void MI32KeyListResp(){ + Response_P(PSTR("{\"MIKeys\":{")); + for (int i = 0; i < MIBLEbindKeys.size(); i++){ + if (i){ + ResponseAppend_P(PSTR(",")); + } + char tmp[20]; + ToHex_P(MIBLEbindKeys[i].MAC,6,tmp,20,0); + char key[16*2+1]; + ToHex_P(MIBLEbindKeys[i].key,16,key,33,0); + + ResponseAppend_P(PSTR("\"%s\":\"%s\""), tmp, key); + } + ResponseAppend_P(PSTR("}}")); +} + + +void CmndMi32Keys(void){ +#ifdef BLE_ESP32_ALIASES + int op = XdrvMailbox.index; + if (BLE_ESP32::BLEDebugMode > 0) AddLog(LOG_LEVEL_DEBUG,PSTR("M32: Key %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 addrres = BLE_ESP32::getAddr(addr, p); + if (!addrres){ + ResponseCmndChar("invalidmac"); + return; + } + + p = strtok(nullptr, " ,="); + char *key = p; + if (!p || !(*p)){ + int i = 0; + for (i = 0; i < MIBLEbindKeys.size(); i++){ + mi_bindKey_t *key = &MIBLEbindKeys[i]; + if (!memcmp(key->MAC, addr, 6)){ + MIBLEbindKeys.erase(MIBLEbindKeys.begin() + i); + MI32KeyListResp(); + return; + } + } + ResponseCmndChar("invalidmac"); + return; + } + + AddLog(LOG_LEVEL_ERROR,PSTR("M32: Add key mac %s = key %s"), mac, key); + char tmp[20]; + // convert mac back to string + ToHex_P(addr,6,tmp,20,0); + if (MI32AddKey(tmp, key)){ + added++; + } + p = strtok(nullptr, " ,="); + } while (p); + + if (added){ + if (BLE_ESP32::BLEDebugMode > 0) AddLog(LOG_LEVEL_DEBUG,PSTR("M32: Added %d Keys"), added); + MI32KeyListResp(); + } else { + MI32KeyListResp(); + } + return; + } break; + case 2:{ // clear + if (BLE_ESP32::BLEDebugMode > 0) AddLog(LOG_LEVEL_DEBUG,PSTR("M32: Keys clearing %d"), MIBLEbindKeys.size()); + for (int i = MIBLEbindKeys.size()-1; i >= 0; i--){ + MIBLEbindKeys.pop_back(); + } + MI32KeyListResp(); + return; + } break; + } + ResponseCmndChar("invalididx"); +#endif +} + + +/*********************************************************************************************\ + * Presentation +\*********************************************************************************************/ + +const char HTTP_MI32[] PROGMEM = "{s}MI ESP32 v0920{m}%u%s / %u{e}"; +const char HTTP_MI32_ALIAS[] PROGMEM = "{s}%s Alias {m}%s{e}"; +const char HTTP_MI32_MAC[] PROGMEM = "{s}%s %s{m}%s{e}"; +const char HTTP_RSSI[] PROGMEM = "{s}%s " D_RSSI "{m}%d dBm{e}"; +const char HTTP_BATTERY[] PROGMEM = "{s}%s" " Battery" "{m}%u %%{e}"; +const char HTTP_LASTBUTTON[] PROGMEM = "{s}%s Last Button{m}%u {e}"; +const char HTTP_EVENTS[] PROGMEM = "{s}%s Events{m}%u {e}"; +const char HTTP_NMT[] PROGMEM = "{s}%s No motion{m}> %u seconds{e}"; +const char HTTP_MI32_FLORA_DATA[] PROGMEM = "{s}%s" " Fertility" "{m}%u us/cm{e}"; +const char HTTP_MI32_HL[] PROGMEM = "{s}
{m}
{e}"; + +//const char HTTP_NEEDKEY[] PROGMEM = "{s}%s %s{m} {e}"; + +//const char HTTP_NEEDKEY[] PROGMEM = "{s}%s %s{m} {e}"; +const char HTTP_NEEDKEY[] PROGMEM = "{s}%s %s{m} {e}"; + + +const char HTTP_PAIRING[] PROGMEM = "{s}%s Pair Button Pressed{m} {e}"; + + +const char HTTP_KEY_ERROR[] PROGMEM = "Key error %s"; +const char HTTP_MAC_ERROR[] PROGMEM = "MAC error %s"; +const char HTTP_KEY_ADDED[] PROGMEM = "Cmnd: MI32Keys %s=%s"; +const char HTTP_MI_KEY_STYLE[] PROGMEM = ""; + + +#define D_MI32_KEY "MI32 Set Key" + +void HandleMI32Key(){ + AddLog(LOG_LEVEL_DEBUG, PSTR("M32: HandleMI32Key hit")); + if (!HttpCheckPriviledgedAccess()) { + AddLog(LOG_LEVEL_DEBUG, PSTR("M32: !HttpCheckPriviledgedAccess()")); + return; + } + WSContentStart_P(PSTR(D_MI32_KEY)); + WSContentSendStyle_P(HTTP_MI_KEY_STYLE); + + char key[64] = {0}; + WebGetArg("key", key, sizeof(key)); + + if (strlen(key) != 16*2){ + WSContentSend_P(HTTP_KEY_ERROR, key); + WSContentStop(); + return; + } + + char mac[13] = {0}; + WebGetArg("mac", mac, sizeof(mac)); + if (strlen(mac) != 12){ + WSContentSend_P(HTTP_MAC_ERROR, mac); + WSContentStop(); + return; + } + + WSContentSend_P(HTTP_KEY_ADDED, mac, key); + + strncat(key, mac, sizeof(key)); + MI32AddKey(key, nullptr); + +// WSContentSpaceButton(BUTTON_CONFIGURATION); + WSContentStop(); +} + + +void MI32TimeoutSensors(){ + // whatever, this function access all the arrays.... + // so block for as long as it takes. + + // PROBLEM: when we take this, it hangs the BLE loop. + // BUT, devicePresent uses the + // remove devices for which the adverts have timed out + for (int i = MIBLEsensors.size()-1; i >= 0 ; i--) { + //if (MIBLEsensors[i].MAC[2] || MIBLEsensors[i].MAC[3] || MIBLEsensors[i].MAC[4] || MIBLEsensors[i].MAC[5]){ + if (!BLE_ESP32::devicePresent(MIBLEsensors[i].MAC)){ + uint8_t *mac = MIBLEsensors[i].MAC; + AddLog(LOG_LEVEL_DEBUG,PSTR("M32: Dev no longer present MAC: %02x%02x%02x%02x%02x%02x"), mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); + TasAutoMutex localmutex(&slotmutex, "Mi32Timeout"); + MIBLEsensors.erase(MIBLEsensors.begin() + i); + } + //} + } +} + + +// this assumes that we're adding to a ResponseTime_P +void MI32GetOneSensorJson(int slot, int hidename){ + mi_sensor_t *p; + p = &MIBLEsensors[slot]; + + // remove hyphen - make it difficult to configure HASS + if (!hidename) { + ResponseAppend_P(PSTR("\"%s%02x%02x%02x\":{"), + kMI32DeviceType[p->type-1], + p->MAC[3], p->MAC[4], p->MAC[5]); + } + + ResponseAppend_P(PSTR("\"mac\":\"%02x%02x%02x%02x%02x%02x\""), + p->MAC[0], p->MAC[1], p->MAC[2], + p->MAC[3], p->MAC[4], p->MAC[5]); + + if((!MI32.mode.triggeredTele && !MI32.option.minimalSummary)||MI32.mode.triggeredTele){ + bool tempHumSended = false; + if(p->feature.tempHum){ + if(p->eventType.tempHum || !MI32.mode.triggeredTele || MI32.option.allwaysAggregate){ + if (!isnan(p->hum) && !isnan(p->temp) +#ifdef USE_HOME_ASSISTANT + ||(hass_mode!=-1) +#endif //USE_HOME_ASSISTANT + ) { + ResponseAppend_P(PSTR(",")); + ResponseAppendTHD(p->temp, p->hum); + tempHumSended = true; + } + } + } + if(p->feature.temp && !tempHumSended){ + if(p->eventType.temp || !MI32.mode.triggeredTele || MI32.option.allwaysAggregate) { + if (!isnan(p->temp) +#ifdef USE_HOME_ASSISTANT + ||(hass_mode!=-1) +#endif //USE_HOME_ASSISTANT + ) { + ResponseAppend_P(PSTR(",\"" D_JSON_TEMPERATURE "\":%*_f"), + Settings.flag2.temperature_resolution, &p->temp); + } + } + } + if(p->feature.hum && !tempHumSended){ + if(p->eventType.hum || !MI32.mode.triggeredTele || MI32.option.allwaysAggregate) { + if (!isnan(p->hum) +#ifdef USE_HOME_ASSISTANT + ||(hass_mode!=-1) +#endif //USE_HOME_ASSISTANT + ) { + char hum[FLOATSZ]; + dtostrfd(p->hum, Settings.flag2.humidity_resolution, hum); + ResponseAppend_P(PSTR(",\"" D_JSON_HUMIDITY "\":%s"), hum); + } + } + } + if (p->feature.lux){ + if(p->eventType.lux || !MI32.mode.triggeredTele || MI32.option.allwaysAggregate){ + if (p->lux!=0x0ffffff +#ifdef USE_HOME_ASSISTANT + ||(hass_mode!=-1) +#endif //USE_HOME_ASSISTANT + ) { // this is the error code -> no lux + ResponseAppend_P(PSTR(",\"" D_JSON_ILLUMINANCE "\":%u"), p->lux); +#ifdef USE_HOME_ASSISTANT + if (p->lux==0x0ffffff) MI32nullifyEndOfMQTT_DATA(); +#endif //USE_HOME_ASSISTANT + } + } + } + if (p->feature.moist){ + if(p->eventType.moist || !MI32.mode.triggeredTele || MI32.option.allwaysAggregate){ + if (p->moisture!=0xff +#ifdef USE_HOME_ASSISTANT + ||(hass_mode!=-1) +#endif //USE_HOME_ASSISTANT + ) { + ResponseAppend_P(PSTR(",\"" D_JSON_MOISTURE "\":%u"), p->moisture); +#ifdef USE_HOME_ASSISTANT + if (p->moisture==0xff) MI32nullifyEndOfMQTT_DATA(); +#endif //USE_HOME_ASSISTANT + } + } + } + if (p->feature.fert){ + if(p->eventType.fert || !MI32.mode.triggeredTele || MI32.option.allwaysAggregate){ + if (p->fertility!=0xffff +#ifdef USE_HOME_ASSISTANT + ||(hass_mode!=-1) +#endif //USE_HOME_ASSISTANT + ) { + ResponseAppend_P(PSTR(",\"Fertility\":%u"), p->fertility); +#ifdef USE_HOME_ASSISTANT + if (p->fertility==0xffff) MI32nullifyEndOfMQTT_DATA(); +#endif //USE_HOME_ASSISTANT + } + } + } + if (p->feature.Btn){ + if(p->eventType.Btn +#ifdef USE_HOME_ASSISTANT + ||(hass_mode==2) +#endif //USE_HOME_ASSISTANT + ){ + ResponseAppend_P(PSTR(",\"Btn\":%u"),p->Btn); + } + } + if(p->eventType.PairBtn && p->pairing){ + ResponseAppend_P(PSTR(",\"Pair\":%u"),p->pairing); + } + } // minimal summary + + + if (p->feature.PIR){ + if(p->eventType.motion || !MI32.mode.triggeredTele){ + if(MI32.mode.triggeredTele) ResponseAppend_P(PSTR(",\"PIR\":1")); // only real-time + ResponseAppend_P(PSTR(",\"Events\":%u"),p->events); + } + else if(p->eventType.noMotion && MI32.mode.triggeredTele){ + ResponseAppend_P(PSTR(",\"PIR\":0")); + } + } + + if (p->type == MI_FLORA && !MI32.mode.triggeredTele) { + if (p->firmware[0] != '\0') { // this is the error code -> no firmware + ResponseAppend_P(PSTR(",\"Firmware\":\"%s\""), p->firmware); + } + } + + if (p->feature.NMT || !MI32.mode.triggeredTele){ + if(p->eventType.NMT){ + ResponseAppend_P(PSTR(",\"NMT\":%u"), p->NMT); + } + } + if (p->feature.bat){ + if(p->eventType.bat || !MI32.mode.triggeredTele || MI32.option.allwaysAggregate){ + if (p->bat != 0x00 +#ifdef USE_HOME_ASSISTANT + ||(hass_mode!=-1) +#endif //USE_HOME_ASSISTANT + ) { // this is the error code -> no battery + ResponseAppend_P(PSTR(",\"Battery\":%u"), p->bat); +#ifdef USE_HOME_ASSISTANT + if (p->bat == 0x00) MI32nullifyEndOfMQTT_DATA(); +#endif //USE_HOME_ASSISTANT + } + } + } + if (MI32.option.showRSSI) ResponseAppend_P(PSTR(",\"RSSI\":%d"), p->RSSI); + + if (!hidename) { + ResponseAppend_P(PSTR("}")); + } + p->eventType.raw = 0; + p->shallSendMQTT = 0; + +} + + +/////////////////////////////////////////////// +// starts a completely fresh MQTT message. +// sends up to 4 sensors +// triggered by setting MI32.mqttCurrentSlot = 0 +void MI32ShowSomeSensors(){ + // don't detect half-added ones here + int numsensors = MIBLEsensors.size(); + if (MI32.mqttCurrentSlot >= numsensors){ + // if we got to the end of the sensors, then don't send more + return; + } + +#ifdef USE_HOME_ASSISTANT + bool _noSummarySave = MI32.option.noSummary; + bool _minimalSummarySave = MI32.option.minimalSummary; + if(hass_mode==2){ + if(MI32.option.holdBackFirstAutodiscovery){ + if(!MI32.mode.firstAutodiscoveryDone){ + MI32.mode.firstAutodiscoveryDone = 1; + return; + } + } + MI32.option.noSummary = false; + MI32.option.minimalSummary = false; + } +#endif //USE_HOME_ASSISTANT + + ResponseTime_P(PSTR("")); + int cnt = 0; + for (; (MI32.mqttCurrentSlot < numsensors) && (cnt < 4); MI32.mqttCurrentSlot++, cnt++) { + ResponseAppend_P(PSTR(",")); + MI32GetOneSensorJson(MI32.mqttCurrentSlot, 0); + int mlen = strlen(TasmotaGlobal.mqtt_data); + + // if we ran out of room, leave here. + if (sizeof(TasmotaGlobal.mqtt_data) - mlen < 100){ + MI32.mqttCurrentSlot++; + break; + } + } + ResponseAppend_P(PSTR("}")); + MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_SENSOR), Settings.flag.mqtt_sensor_retain); + //AddLog(LOG_LEVEL_DEBUG,PSTR("M32: %s: show some %d %s"),D_CMND_MI32, MI32.mqttCurrentSlot, TasmotaGlobal.mqtt_data); + +#ifdef USE_RULES + RulesTeleperiod(); // Allow rule based HA messages +#endif // USE_RULES + +#ifdef USE_HOME_ASSISTANT + if(hass_mode==2){ + MI32.option.noSummary = _noSummarySave; + MI32.option.minimalSummary = _minimalSummarySave; + } +#endif //USE_HOME_ASSISTANT +} + + +/////////////////////////////////////////////// +// starts a completely fresh MQTT message. +// sends ONE sensor on a dedicated topic NOT related to this TAS +// triggered by setting MI32.mqttCurrentSingleSlot = 0 +void MI32ShowOneMISensor(){ + // don't detect half-added ones here + int numsensors = MIBLEsensors.size(); + if (MI32.mqttCurrentSingleSlot >= numsensors){ + // if we got to the end of the sensors, then don't send more + return; + } + +#ifdef USE_HOME_ASSISTANT + if(Settings.flag.hass_discovery){ + + ResponseTime_P(PSTR(",")); + MI32GetOneSensorJson(MI32.mqttCurrentSingleSlot, 1); + mi_sensor_t *p; + p = &MIBLEsensors[MI32.mqttCurrentSingleSlot]; + + ResponseAppend_P(PSTR("}")); + + char idstr[32]; + const char *alias = BLE_ESP32::getAlias(p->MAC); + const char *id = idstr; + if (alias && *alias){ + id = alias; + } else { + sprintf(idstr, PSTR("%s%02x%02x%02x"), + kMI32DeviceType[p->type-1], + p->MAC[3], p->MAC[4], p->MAC[5]); + } + char SensorTopic[60]; + sprintf(SensorTopic, "tele/tasmota_ble/%s", + id); + + MqttPublish(SensorTopic); + //AddLog_P(LOG_LEVEL_DEBUG,PSTR("M32: %s: show some %d %s"),D_CMND_MI32, MI32.mqttCurrentSlot, TasmotaGlobal.mqtt_data); + } +#endif //USE_HOME_ASSISTANT + MI32.mqttCurrentSingleSlot++; +} + + +/////////////////////////////////////////////// +// starts a completely fresh MQTT message. +// sends ONE sensor's worth of HA discovery msg +const char MI_HA_DISCOVERY_TEMPLATE[] PROGMEM = + "{\"availability\":[],\"device\":" + "{\"identifiers\":[\"BLE%s\"]," + "\"name\":\"%s\"," + "\"manufacturer\":\"tas\"," + "\"model\":\"%s\"," + "\"via_device\":\"%s\"" + "}," + "\"dev_cla\":\"%s\"," + "\"expire_after\":600," + "\"json_attr_t\":\"%s\"," + "\"name\":\"%s_%s\"," + "\"state_topic\":\"%s\"," + "\"uniq_id\":\"%s_%s\"," + "\"unit_of_meas\":\"%s\"," + "\"val_tpl\":\"{{ value_json.%s }}\"}"; + +void MI32DiscoveryOneMISensor(){ + // don't detect half-added ones here + int numsensors = MIBLEsensors.size(); + if (MI32.mqttCurrentSingleSlot >= numsensors){ + // if we got to the end of the sensors, then don't send more + return; + } + +#ifdef USE_HOME_ASSISTANT + if(Settings.flag.hass_discovery){ + mi_sensor_t *p; + p = &MIBLEsensors[MI32.mqttCurrentSingleSlot]; + + + // careful - a missing comma causes a crash!!!! + // because of the way we loop? + const char *classes[] = { + "temperature", + "Temperature", + "°C", + "humidity", + "Humidity", + "%", + "temperature", + "DewPoint", + "°C", + "battery", + "Battery", + "%", + "signal_strength", + "RSSI", + "dB" + }; + + int datacount = (sizeof(classes)/sizeof(*classes))/3; + + if (p->nextDiscoveryData >= datacount){ + p->nextDiscoveryData = 0; + } + + //int i = p->nextDiscoveryData*3; + for (int i = 0; i < datacount*3; i += 3){ + if (!classes[i] || !classes[i+1] || !classes[i+2]){ + return; + } + + char idstr[32]; + const char *alias = BLE_ESP32::getAlias(p->MAC); + const char *id = idstr; + if (alias && *alias){ + id = alias; + } else { + sprintf(idstr, PSTR("%s%02x%02x%02x"), + kMI32DeviceType[p->type-1], + p->MAC[3], p->MAC[4], p->MAC[5]); + } + + char SensorTopic[60]; + sprintf(SensorTopic, "tele/tasmota_ble/%s", + id); + + + ResponseClear(); + + /* + {"availability":[],"device":{"identifiers":["TasmotaBLEa4c1387fc1e1"],"manufacturer":"simon","model":"someBLEsensor","name":"TASBLEa4c1387fc1e1","sw_version":"0.0.0"},"dev_cla":"temperature","json_attr_t":"tele/tasmota_esp32/SENSOR","name":"TASLYWSD037fc1e1Temp","state_topic":"tele/tasmota_esp32/SENSOR","uniq_id":"Tasmotaa4c1387fc1e1temp","unit_of_meas":"°C","val_tpl":"{{ value_json.LYWSD037fc1e1.Temperature }}"} + {"availability":[],"device":{"identifiers":["TasmotaBLEa4c1387fc1e1"], + "name":"TASBLEa4c1387fc1e1"},"dev_cla":"temperature", + "json_attr_t":"tele/tasmota_esp32/SENSOR", + "name":"TASLYWSD037fc1e1Temp","state_topic": "tele/tasmota_esp32/SENSOR", + "uniq_id":"Tasmotaa4c1387fc1e1temp","unit_of_meas":"°C", + "val_tpl":"{{ value_json.LYWSD037fc1e1.Temperature }}"} + */ + + ResponseAppend_P(MI_HA_DISCOVERY_TEMPLATE, + //"{\"identifiers\":[\"BLE%s\"]," + id, + //"\"name\":\"%s\"}," + id, + //\"model\":\"%s\", + kMI32DeviceType[p->type-1], + //\"via_device\":\"%s\" + NetworkHostname(), + //"\"dev_cla\":\"%s\"," + classes[i], + //"\"json_attr_t\":\"%s\"," - the topic the sensor publishes on + SensorTopic, + //"\"name\":\"%s_%s\"," - the name of this DATA + id, classes[i+1], + //"\"state_topic\":\"%s\"," - the topic the sensor publishes on? + SensorTopic, + //"\"uniq_id\":\"%s_%s\"," - unique for this data, + id, classes[i+1], + //"\"unit_of_meas\":\"%s\"," - the measure of this type of data + classes[i+2], + //"\"val_tpl\":\"{{ value_json.%s }}") // e.g. Temperature + classes[i+1] + // + ); + + char DiscoveryTopic[80]; + sprintf(DiscoveryTopic, "homeassistant/sensor/%s/%s/config", + id, classes[i+1]); + + MqttPublish(DiscoveryTopic); + p->nextDiscoveryData++; + //vTaskDelay(100/ portTICK_PERIOD_MS); + } + } // end if hass discovery + //AddLog_P(LOG_LEVEL_DEBUG,PSTR("M32: %s: show some %d %s"),D_CMND_MI32, MI32.mqttCurrentSlot, TasmotaGlobal.mqtt_data); +#endif //USE_HOME_ASSISTANT + +} + +/////////////////////////////////////////////// +// starts a completely fresh MQTT message. +// sends up to 4 sensors pe5r msg +// sends only those which are raw and triggered. +// triggered by setting MI32.mode.triggeredTele = 1 +void MI32ShowTriggeredSensors(){ + if (!MI32.mode.triggeredTele) return; // none to show + MI32.mode.triggeredTele = 0; + + // don't detect half-added ones here + int numsensors = MIBLEsensors.size(); + + int sensor = 0; + + do { + ResponseTime_P(PSTR("")); + int cnt = 0; + for (; (sensor < numsensors) && (cnt < 4); sensor++) { + mi_sensor_t *p; + p = &MIBLEsensors[sensor]; + if(p->eventType.raw == 0) continue; + if(p->shallSendMQTT==0) continue; + + cnt++; + ResponseAppend_P(PSTR(",")); + MI32GetOneSensorJson(sensor, 0); + int mlen = strlen(TasmotaGlobal.mqtt_data); + + // if we ran out of room, leave here. + if (sizeof(TasmotaGlobal.mqtt_data) - mlen < 100){ + sensor++; + break; + } + } + if (cnt){ // if we got one, then publish + ResponseAppend_P(PSTR("}")); + MqttPublishPrefixTopic_P(STAT, PSTR(D_RSLT_SENSOR), Settings.flag.mqtt_sensor_retain); + AddLog(LOG_LEVEL_DEBUG,PSTR("M32: %s: triggered %d %s"),D_CMND_MI32, sensor, TasmotaGlobal.mqtt_data); + +#ifdef USE_RULES + RulesTeleperiod(); // Allow rule based HA messages +#endif // USE_RULES + + } else { // else don't and clear + ResponseClear(); + } + } while (sensor < numsensors); +} + + +void MI32Show(bool json) +{ + // don't detect half-added ones here + int numsensors = MIBLEsensors.size(); + + if (json) { + // TELE JSON messages now do nothing here, apart from set MI32.mqttCurrentSlot + // which will trigger send next second of up to 4 sensors, then the next four in the next second, etc. + //MI32.mqttCurrentSlot = 0; + +#ifdef USE_WEBSERVER + } else { + static uint16_t _page = 0; + static uint16_t _counter = 0; + int32_t i = _page * MI32.perPage; + uint32_t j = i + MI32.perPage; + + if (j+1 > numsensors){ + j = numsensors; + } + char stemp[5] ={0}; + if (numsensors-(_page*MI32.perPage)>1 && MI32.perPage!=1) { + sprintf_P(stemp,"-%u",j); + } + if (numsensors==0) i=-1; // only for the GUI + + WSContentSend_PD(HTTP_MI32, i+1,stemp,numsensors); + for (i; itype-1]; + const char *alias = BLE_ESP32::getAlias(p->MAC); + if (alias && *alias){ + WSContentSend_PD(HTTP_MI32_ALIAS, typeName, alias); + } + char _MAC[18]; + ToHex_P(p->MAC,6,_MAC,18);//,':'); + WSContentSend_PD(HTTP_MI32_MAC, typeName, D_MAC_ADDRESS, _MAC); + WSContentSend_PD(HTTP_RSSI, typeName, p->RSSI); + + + // for some reason, display flora differently + switch(p->type){ + case MI_FLORA:{ + if (!isnan(p->temp)) { + WSContentSend_Temp(typeName, p->temp); + } + if (p->moisture!=0xff) { + WSContentSend_PD(HTTP_SNS_MOISTURE, typeName, p->moisture); + } + if (p->fertility!=0xffff) { + WSContentSend_PD(HTTP_MI32_FLORA_DATA, typeName, p->fertility); + } + } break; + default:{ + if (!isnan(p->hum) && !isnan(p->temp)) { + WSContentSend_THD(typeName, p->temp, p->hum); + } + } + } + +#ifdef USE_MI_DECRYPTION + bool showkey = false; + char tmp[40]; + strcpy(tmp, PSTR("KeyRqd")); + switch(p->needkey) { + default:{ + snprintf(tmp, 39, PSTR("?%d?"), p->needkey ); + showkey = true; + } break; + case KEY_REQUIREMENT_UNKNOWN: { + strcpy(tmp, PSTR("WAIT")); + showkey = true; + } break; + case KEY_NOT_REQUIRED: { + strcpy(tmp, PSTR("NOTKEY")); + //showkey = true; + } break; + case KEY_REQUIRED_BUT_NOT_FOUND: { + strcpy(tmp, PSTR("NoKey")); + showkey = true; + } break; + case KEY_REQUIRED_AND_FOUND: { + strcpy(tmp, PSTR("KeyOk")); + showkey = true; + } break; + case KEY_REQUIRED_AND_INVALID: { + strcpy(tmp, PSTR("KeyInv")); + showkey = true; + } break; + } + + // adds the link to get the key. + // provides mac and callback address to receive the key, if we had a website which did this + // (future work) + if (showkey){ + BLE_ESP32::dump(_MAC, 13, p->MAC,6); + WSContentSend_PD(HTTP_NEEDKEY, typeName, _MAC, Webserver->client().localIP().toString().c_str(), tmp ); + } + + if (p->type==MI_NLIGHT || p->type==MI_MJYD2S) { +#else + if (p->type==MI_NLIGHT) { +#endif //USE_MI_DECRYPTION + WSContentSend_PD(HTTP_EVENTS, typeName, p->events); + if(p->NMT>0) WSContentSend_PD(HTTP_NMT, typeName, p->NMT); + } + + if (p->lux!=0x00ffffff) { // this is the error code -> no valid value + WSContentSend_PD(HTTP_SNS_ILLUMINANCE, typeName, p->lux); + } + if(p->bat!=0x00){ + WSContentSend_PD(HTTP_BATTERY, typeName, p->bat); + } + if (p->type==MI_YEERC){ + WSContentSend_PD(HTTP_LASTBUTTON, typeName, p->Btn); + } + if (p->pairing){ + WSContentSend_PD(HTTP_PAIRING, typeName); + } + } + _counter++; + if(_counter>3) { + _page++; + _counter=0; + } + if (MIBLEsensors.size()%MI32.perPage==0 && _page==MIBLEsensors.size()/MI32.perPage) { _page = 0; } + if (_page>MIBLEsensors.size()/MI32.perPage) { _page = 0; } +#endif // USE_WEBSERVER + } +} + +/*********************************************************************************************\ + * Interface +\*********************************************************************************************/ +#define WEB_HANDLE_MI32 "mikey" + +bool Xsns62(uint8_t function) +{ +// if (!Settings.flag5.mi32_enable) { return false; } // SetOption115 - Enable ESP32 MI32 BLE +// return false; + + bool result = false; + + switch (function) { + case FUNC_INIT: + MI32Init(); + break; + case FUNC_EVERY_50_MSECOND: + MI32Every50mSecond(); + break; + case FUNC_EVERY_SECOND: + MI32EverySecond(false); + break; + case FUNC_COMMAND: + result = DecodeCommand(kMI32_Commands, MI32_Commands); + break; + case FUNC_JSON_APPEND: + // we are not in control of when this is called... + //MI32Show(1); + break; +#ifdef USE_WEBSERVER + case FUNC_WEB_ADD_HANDLER: + WebServer_on(PSTR("/" WEB_HANDLE_MI32), HandleMI32Key); + break; + case FUNC_WEB_SENSOR: + MI32Show(0); + break; +#endif // USE_WEBSERVER + } + return result; +} +#endif // USE_MI_ESP32 +#endif // ESP32 + +#endif \ No newline at end of file diff --git a/tasmota/xsns_65_hdc1080.ino b/tasmota/xsns_65_hdc1080.ino index d20241ab8..a684f36ec 100644 --- a/tasmota/xsns_65_hdc1080.ino +++ b/tasmota/xsns_65_hdc1080.ino @@ -176,7 +176,7 @@ bool HdcTriggerRead(void) { hdc_next_read = millis() + HDC1080_CONV_TIME; if(status) { - AddLog_P(LOG_LEVEL_DEBUG, PSTR("HdcTriggerRead: failed to open the transaction for HDC_REG_TEMP. Status = %d"), status); + AddLog(LOG_LEVEL_DEBUG, PSTR("HdcTriggerRead: failed to open the transaction for HDC_REG_TEMP. Status = %d"), status); return false; } @@ -205,7 +205,7 @@ bool HdcRead(void) { status = HdcTransactionClose(HDC1080_ADDR, sensor_data, 4); if(status) { - AddLog_P(LOG_LEVEL_DEBUG, PSTR("HdcRead: failed to read HDC_REG_TEMP. Status = %d"), status); + AddLog(LOG_LEVEL_DEBUG, PSTR("HdcRead: failed to read HDC_REG_TEMP. Status = %d"), status); return false; } @@ -213,7 +213,7 @@ bool HdcRead(void) { temp_data = (uint16_t) ((sensor_data[0] << 8) | sensor_data[1]); rh_data = (uint16_t) ((sensor_data[2] << 8) | sensor_data[3]); - AddLog_P(LOG_LEVEL_DEBUG, PSTR("HdcRead: temperature raw data: 0x%04x; humidity raw data: 0x%04x"), temp_data, rh_data); + AddLog(LOG_LEVEL_DEBUG, PSTR("HdcRead: temperature raw data: 0x%04x; humidity raw data: 0x%04x"), temp_data, rh_data); // read the temperature from the first 16 bits of the result @@ -237,7 +237,7 @@ bool HdcRead(void) { void HdcDetect(void) { if (I2cActive(HDC1080_ADDR)) { -// AddLog_P(LOG_LEVEL_DEBUG, PSTR("HdcDetect: Address = 0x%02X already in use."), HDC1080_ADDR); +// AddLog(LOG_LEVEL_DEBUG, PSTR("HdcDetect: Address = 0x%02X already in use."), HDC1080_ADDR); return; } @@ -245,7 +245,7 @@ void HdcDetect(void) { hdc_manufacturer_id = HdcReadManufacturerId(); hdc_device_id = HdcReadDeviceId(); - AddLog_P(LOG_LEVEL_DEBUG, PSTR("HdcDetect: detected device with manufacturerId = 0x%04X and deviceId = 0x%04X"), hdc_manufacturer_id, hdc_device_id); + AddLog(LOG_LEVEL_DEBUG, PSTR("HdcDetect: detected device with manufacturerId = 0x%04X and deviceId = 0x%04X"), hdc_manufacturer_id, hdc_device_id); if (hdc_device_id == HDC1080_DEV_ID) { HdcInit(); @@ -284,7 +284,7 @@ void HdcShow(bool json) { bool Xsns65(uint8_t function) { if (!I2cEnabled(XI2C_45)) { -// AddLog_P(LOG_LEVEL_DEBUG, PSTR("Xsns65: I2C driver not enabled for this device.")); +// AddLog(LOG_LEVEL_DEBUG, PSTR("Xsns65: I2C driver not enabled for this device.")); return false; } diff --git a/tasmota/xsns_66_iAQ.ino b/tasmota/xsns_66_iAQ.ino index fc3dff2da..0cbf17276 100644 --- a/tasmota/xsns_66_iAQ.ino +++ b/tasmota/xsns_66_iAQ.ino @@ -69,7 +69,7 @@ void IAQ_Read(void) { for( uint32_t i=0; i<9; i++ ) { buf[i]= Wire.read(); } - // AddLog_P(LOG_LEVEL_DEBUG, "iAQ: buffer %x %x %x %x %x %x %x %x %x ", buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7], buf[8]); + // AddLog(LOG_LEVEL_DEBUG, "iAQ: buffer %x %x %x %x %x %x %x %x %x ", buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7], buf[8]); iAQ.pred = (buf[0]<<8) + buf[1]; iAQ.status = buf[2]; iAQ.resistance = ((uint32_t)buf[3]<<24) + ((uint32_t)buf[4]<<16) + ((uint32_t)buf[5]<<8) + (uint32_t)buf[6]; @@ -95,7 +95,7 @@ void IAQ_Show(uint8_t json) if (json) { if (iAQ.status!=IAQ_STATUS_OK){ - AddLog_P(LOG_LEVEL_INFO, PSTR("iAQ: " D_ERROR " %x" ),iAQ.status); + AddLog(LOG_LEVEL_INFO, PSTR("iAQ: " D_ERROR " %x" ),iAQ.status); return; } else { diff --git a/tasmota/xsns_67_as3935.ino b/tasmota/xsns_67_as3935.ino index 030581768..10df27a6e 100644 --- a/tasmota/xsns_67_as3935.ino +++ b/tasmota/xsns_67_as3935.ino @@ -218,7 +218,7 @@ bool AS3935AutoTuneCaps(uint8_t irqpin) { // functions bool AS3935CalRCOResult(void) { if(AS3935ReadRegister(CAL_SRCO_NOK) || AS3935ReadRegister(CAL_TRCO_NOK)) { - AddLog_P(LOG_LEVEL_INFO, PSTR("I2C: AS3935 Fatal Failure of TRCO or SRCO calibration")); + AddLog(LOG_LEVEL_INFO, PSTR("I2C: AS3935 Fatal Failure of TRCO or SRCO calibration")); return false; } return true; @@ -514,7 +514,7 @@ void AS3935Detect(void) { if (!AS3935Setup()) return; as3935_sensor.active = true; } else { - AddLog_P(LOG_LEVEL_INFO, PSTR("I2C: AS3935 GPIO Pin not defined!")); + AddLog(LOG_LEVEL_INFO, PSTR("I2C: AS3935 GPIO Pin not defined!")); } } } diff --git a/tasmota/xsns_68_windmeter.ino b/tasmota/xsns_68_windmeter.ino index 6b552ee38..758a176ab 100644 --- a/tasmota/xsns_68_windmeter.ino +++ b/tasmota/xsns_68_windmeter.ino @@ -79,7 +79,7 @@ void ICACHE_RAM_ATTR WindMeterUpdateSpeed(void) if (time_diff > Settings.windmeter_pulse_debounce * 1000) { WindMeter.counter_time = time; WindMeter.counter++; -// AddLog_P(LOG_LEVEL_DEBUG, PSTR("WMET: Counter %d"), WindMeter.counter); +// AddLog(LOG_LEVEL_DEBUG, PSTR("WMET: Counter %d"), WindMeter.counter); } } @@ -119,7 +119,7 @@ void WindMeterEverySecond(void) { //uint32_t time = micros(); //uint32_t delta_time = time - WindMeter.speed_time; - //AddLog_P(LOG_LEVEL_INFO, PSTR("delta_time: %d"), delta_time); + //AddLog(LOG_LEVEL_INFO, PSTR("delta_time: %d"), delta_time); // speed = ( (pulses / pulses_per_rotation) * (2 * pi * radius) ) / delta_time WindMeter.speed = ((WindMeter.counter / Settings.windmeter_pulses_x_rot) * (windmeter_2pi * ((float)Settings.windmeter_radius / 1000))) * ((float)Settings.windmeter_speed_factor / 1000); @@ -131,7 +131,7 @@ void WindMeterEverySecond(void) //dtostrfd(WindMeter.speed, 2, speed_string); //char uspeed_string[FLOATSZ]; //dtostrfd(ConvertSpeed(WindMeter.speed), 2, uspeed_string); - //AddLog_P(LOG_LEVEL_DEBUG, PSTR("WMET: Speed %s [m/s] - %s [unit]"), speed_string, uspeed_string); + //AddLog(LOG_LEVEL_DEBUG, PSTR("WMET: Speed %s [m/s] - %s [unit]"), speed_string, uspeed_string); #ifndef USE_WINDMETER_NOSTATISTICS if (WindMeter.speed < WindMeter.speed_min) { @@ -289,48 +289,36 @@ void WindMeterTriggerTele(void) bool Xsns68Cmnd(void) { - bool serviced = true; - bool show_parms = true; - char sub_string[XdrvMailbox.data_len +1]; - switch (XdrvMailbox.payload) { - case 1: - if (strchr(XdrvMailbox.data, ',') != nullptr) { - Settings.windmeter_radius = (uint16_t)strtol(subStr(sub_string, XdrvMailbox.data, ",", 2), nullptr, 10); - } - break; - case 2: - if (strchr(XdrvMailbox.data, ',') != nullptr) { - Settings.windmeter_pulses_x_rot = (uint8_t)strtol(subStr(sub_string, XdrvMailbox.data, ",", 2), nullptr, 10); - } - break; - case 3: - if (strchr(XdrvMailbox.data, ',') != nullptr) { - Settings.windmeter_pulse_debounce = (uint16_t)strtol(subStr(sub_string, XdrvMailbox.data, ",", 2), nullptr, 10); - } - break; - case 4: - if (strchr(XdrvMailbox.data, ',') != nullptr) { - Settings.windmeter_speed_factor = (int16_t)(CharToFloat(subStr(sub_string, XdrvMailbox.data, ",", 2)) * 1000); - } - break; - case 5: - if (strchr(XdrvMailbox.data, ',') != nullptr) { - Settings.windmeter_tele_pchange = (uint8_t)strtol(subStr(sub_string, XdrvMailbox.data, ",", 2), nullptr, 10); - } - break; + if (ArgC() > 1) { + char argument[XdrvMailbox.data_len]; + switch (XdrvMailbox.payload) { + case 1: + Settings.windmeter_radius = (uint16_t)strtol(ArgV(argument, 2), nullptr, 10); + break; + case 2: + Settings.windmeter_pulses_x_rot = (uint8_t)strtol(ArgV(argument, 2), nullptr, 10); + break; + case 3: + Settings.windmeter_pulse_debounce = (uint16_t)strtol(ArgV(argument, 2), nullptr, 10); + break; + case 4: + Settings.windmeter_speed_factor = (int16_t)(CharToFloat(ArgV(argument, 2)) * 1000); + break; + case 5: + Settings.windmeter_tele_pchange = (uint8_t)strtol(ArgV(argument, 2), nullptr, 10); + break; + } } - if (show_parms) { - char speed_factor_string[FLOATSZ]; - dtostrfd((float)Settings.windmeter_speed_factor / 1000, 3, speed_factor_string); - char tele_pchange_string[4] = "off"; - if (Settings.windmeter_tele_pchange <= 100) { - itoa(Settings.windmeter_tele_pchange, tele_pchange_string, 10); - } - Response_P(PSTR("{\"" D_WINDMETER_NAME "\":{\"Radius\":%d,\"PulsesPerRot\":%d,\"PulseDebounce\":%d,\"SpeedFactor\":%s,\"TeleTriggerMin%Change\":%s}}"), - Settings.windmeter_radius, Settings.windmeter_pulses_x_rot, Settings.windmeter_pulse_debounce, speed_factor_string, tele_pchange_string); + float speed_factor = (float)Settings.windmeter_speed_factor / 1000; + char tele_pchange_string[4] = "off"; + if (Settings.windmeter_tele_pchange <= 100) { + itoa(Settings.windmeter_tele_pchange, tele_pchange_string, 10); } - return serviced; + Response_P(PSTR("{\"" D_WINDMETER_NAME "\":{\"Radius\":%d,\"PulsesPerRot\":%d,\"PulseDebounce\":%d,\"SpeedFactor\":%3_f,\"TeleTriggerMin%Change\":%s}}"), + Settings.windmeter_radius, Settings.windmeter_pulses_x_rot, Settings.windmeter_pulse_debounce, &speed_factor, tele_pchange_string); + + return true; } /*********************************************************************************************\ diff --git a/tasmota/xsns_69_opentherm.ino b/tasmota/xsns_69_opentherm.ino index adcef07b0..1f8bd1609 100644 --- a/tasmota/xsns_69_opentherm.ino +++ b/tasmota/xsns_69_opentherm.ino @@ -165,7 +165,7 @@ void ICACHE_RAM_ATTR sns_opentherm_handleInterrupt() void sns_opentherm_processResponseCallback(unsigned long response, int st) { OpenThermResponseStatus status = (OpenThermResponseStatus)st; - AddLog_P(LOG_LEVEL_DEBUG_MORE, + AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("[OTH]: Processing response. Status=%s, Response=0x%lX"), sns_ot_master->statusToString(status), response); @@ -311,7 +311,7 @@ void sns_ot_start_handshake() return; } - AddLog_P(LOG_LEVEL_DEBUG, PSTR("[OTH]: perform handshake")); + AddLog(LOG_LEVEL_DEBUG, PSTR("[OTH]: perform handshake")); sns_ot_master->sendRequestAync( OpenTherm::buildRequest(OpenThermMessageType::READ_DATA, OpenThermMessageID::SConfigSMemberIDcode, 0)); @@ -325,14 +325,14 @@ void sns_ot_process_handshake(unsigned long response, int st) if (status != OpenThermResponseStatus::SUCCESS || !sns_ot_master->isValidResponse(response)) { - AddLog_P(LOG_LEVEL_ERROR, + AddLog(LOG_LEVEL_ERROR, PSTR("[OTH]: getSlaveConfiguration failed. Status=%s"), sns_ot_master->statusToString(status)); sns_ot_connection_status = OpenThermConnectionStatus::OTC_DISCONNECTED; return; } - AddLog_P(LOG_LEVEL_DEBUG, PSTR("[OTH]: getLastResponseStatus SUCCESS. Slave Cfg: %lX"), response); + AddLog(LOG_LEVEL_DEBUG, PSTR("[OTH]: getLastResponseStatus SUCCESS. Slave Cfg: %lX"), response); sns_ot_boiler_status.m_slave_flags = (response & 0xFF00) >> 8; diff --git a/tasmota/xsns_69_opentherm_protocol.ino b/tasmota/xsns_69_opentherm_protocol.ino index 6158be9c7..f08d6b8a6 100644 --- a/tasmota/xsns_69_opentherm_protocol.ino +++ b/tasmota/xsns_69_opentherm_protocol.ino @@ -168,7 +168,7 @@ unsigned long sns_opentherm_set_slave_flags(struct OpenThermCommandT *self, stru } if (self->m_results[1].m_bool != centralHeatingIsOn) { - AddLog_P(LOG_LEVEL_INFO, + AddLog(LOG_LEVEL_INFO, PSTR("[OTH]: Central Heating transitioning from %s to %s"), self->m_results[1].m_bool ? "on" : "off", status->m_enableCentralHeating ? "on" : "off"); @@ -219,7 +219,7 @@ unsigned long sns_opentherm_set_boiler_temperature(struct OpenThermCommandT *sel { return -1; } - AddLog_P(LOG_LEVEL_INFO, + AddLog(LOG_LEVEL_INFO, PSTR("[OTH]: Setting Boiler Temp. Old: %d, New: %d"), (int)self->m_results[0].m_float, (int)status->m_boilerSetpoint); @@ -258,7 +258,7 @@ unsigned long sns_opentherm_set_boiler_dhw_temperature(struct OpenThermCommandT { return -1; } - AddLog_P(LOG_LEVEL_INFO, + AddLog(LOG_LEVEL_INFO, PSTR("[OTH]: Setting Hot Water Temp. Old: %d, New: %d"), (int)self->m_results[0].m_float, (int)status->m_hotWaterSetpoint); @@ -400,7 +400,7 @@ void sns_opentherm_check_retry_request() if (!canRetry && !cmd->m_flags.supported) { cmd->m_flags.notSupported = true; - AddLog_P(LOG_LEVEL_ERROR, + AddLog(LOG_LEVEL_ERROR, PSTR("[OTH]: command %s is not supported by the boiler. Last status: %s"), cmd->m_command_name, sns_ot_master->statusToString(sns_ot_master->getLastResponseStatus())); diff --git a/tasmota/xsns_72_mcp9808.ino b/tasmota/xsns_72_mcp9808.ino index 98fc48066..4b1c15696 100644 --- a/tasmota/xsns_72_mcp9808.ino +++ b/tasmota/xsns_72_mcp9808.ino @@ -75,9 +75,6 @@ void MCP9808EverySecond(void) { void MCP9808Show(bool json) { for (uint32_t i = 0; i < mcp9808_cfg.count; i++) { - char temperature[33]; - dtostrfd(mcp9808_sensors[i].temperature, Settings.flag2.temperature_resolution, temperature); - char sensor_name[11]; strlcpy(sensor_name, mcp9808_cfg.types, sizeof(sensor_name)); if (mcp9808_cfg.count > 1) { @@ -85,10 +82,10 @@ void MCP9808Show(bool json) { } if (json) { - ResponseAppend_P(JSON_SNS_TEMP, sensor_name, temperature); + ResponseAppend_P(JSON_SNS_F_TEMP, sensor_name, Settings.flag2.temperature_resolution, &mcp9808_sensors[i].temperature); if ((0 == TasmotaGlobal.tele_period) && (0 == i)) { #ifdef USE_DOMOTICZ - DomoticzSensor(DZ_TEMP, temperature); + DomoticzFloatSensor(DZ_TEMP, mcp9808_sensors[i].temperature); #endif // USE_DOMOTICZ #ifdef USE_KNX KnxSensor(KNX_TEMPERATURE, mcp9808_sensors[i].temperature); @@ -96,7 +93,7 @@ void MCP9808Show(bool json) { } #ifdef USE_WEBSERVER } else { - WSContentSend_PD(HTTP_SNS_TEMP, sensor_name, temperature, TempUnit()); + WSContentSend_Temp(sensor_name, mcp9808_sensors[i].temperature); #endif // USE_WEBSERVER } } diff --git a/tasmota/xsns_73_hp303b.ino b/tasmota/xsns_73_hp303b.ino index 50589e4d5..c04758190 100644 --- a/tasmota/xsns_73_hp303b.ino +++ b/tasmota/xsns_73_hp303b.ino @@ -107,15 +107,14 @@ void HP303B_Show(bool json) { float sealevel = ConvertPressureForSeaLevel(hp303b_sensor[i].pressure); - char str_temperature[33]; - dtostrfd(hp303b_sensor[i].temperature, Settings.flag2.temperature_resolution, str_temperature); char str_pressure[33]; dtostrfd(hp303b_sensor[i].pressure, Settings.flag2.pressure_resolution, str_pressure); char sea_pressure[33]; dtostrfd(sealevel, Settings.flag2.pressure_resolution, sea_pressure); if (json) { - ResponseAppend_P(PSTR(",\"%s\":{\"" D_JSON_TEMPERATURE "\":%s,\"" D_JSON_PRESSURE "\":%s"), sensor_name, str_temperature, str_pressure); + ResponseAppend_P(PSTR(",\"%s\":{\"" D_JSON_TEMPERATURE "\":%*_f,\"" D_JSON_PRESSURE "\":%s"), + sensor_name, Settings.flag2.temperature_resolution, &hp303b_sensor[i].temperature, str_pressure); if (Settings.altitude != 0) { ResponseAppend_P(PSTR(",\"" D_JSON_PRESSUREATSEALEVEL "\":%s"), sea_pressure); } @@ -123,12 +122,12 @@ void HP303B_Show(bool json) { #ifdef USE_DOMOTICZ // Domoticz and knx only support one temp sensor if ((0 == TasmotaGlobal.tele_period) && (0 == i)) { - DomoticzSensor(DZ_TEMP, hp303b_sensor[i].temperature); + DomoticzFloatSensor(DZ_TEMP, hp303b_sensor[i].temperature); } #endif // USE_DOMOTICZ #ifdef USE_WEBSERVER } else { - WSContentSend_PD(HTTP_SNS_TEMP, sensor_name, str_temperature, TempUnit()); + WSContentSend_Temp(sensor_name, hp303b_sensor[i].temperature); WSContentSend_PD(HTTP_SNS_PRESSURE, sensor_name, str_pressure, PressureUnit().c_str()); if (Settings.altitude != 0) { WSContentSend_PD(HTTP_SNS_SEAPRESSURE, sensor_name, sea_pressure, PressureUnit().c_str()); diff --git a/tasmota/xsns_74_lmt01.ino b/tasmota/xsns_74_lmt01.ino index 8c6cfdb92..cce7288db 100644 --- a/tasmota/xsns_74_lmt01.ino +++ b/tasmota/xsns_74_lmt01.ino @@ -85,14 +85,11 @@ int LMT01_getPulses(void) { } void LMT01_Show(bool Json) { - char temp[33]; - dtostrfd(lmt01_temperature, Settings.flag2.temperature_resolution, temp); - if (Json) { - ResponseAppend_P(JSON_SNS_TEMP, "LMT01", temp); + ResponseAppend_P(JSON_SNS_F_TEMP, "LMT01", Settings.flag2.temperature_resolution, &lmt01_temperature); #ifdef USE_DOMOTICZ if (0 == TasmotaGlobal.tele_period) { - DomoticzSensor(DZ_TEMP, temp); + DomoticzFloatSensor(DZ_TEMP, lmt01_temperature); } #endif // USE_DOMOTICZ #ifdef USE_KNX @@ -102,7 +99,7 @@ void LMT01_Show(bool Json) { #endif // USE_KNX #ifdef USE_WEBSERVER } else { - WSContentSend_PD(HTTP_SNS_TEMP, "LMT01", temp, TempUnit()); + WSContentSend_Temp("LMT01", lmt01_temperature); #endif // USE_WEBSERVER } } diff --git a/tasmota/xsns_75_prometheus.ino b/tasmota/xsns_75_prometheus.ino index 0dac5ed9f..bbdb303c5 100644 --- a/tasmota/xsns_75_prometheus.ino +++ b/tasmota/xsns_75_prometheus.ino @@ -63,7 +63,7 @@ String FormatMetricName(const char *metric) { // cleanup spaces and uppercases void HandleMetrics(void) { if (!HttpCheckPriviledgedAccess()) { return; } - AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_HTTP "Prometheus")); + AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_HTTP "Prometheus")); WSContentBegin(200, CT_PLAIN); diff --git a/tasmota/xsns_77_vl53l1x.ino b/tasmota/xsns_77_vl53l1x.ino index f245fda86..76908ad6e 100644 --- a/tasmota/xsns_77_vl53l1x.ino +++ b/tasmota/xsns_77_vl53l1x.ino @@ -36,8 +36,8 @@ VL53L1X vl53l1x = VL53L1X(); // create object copy #define VL53L1X_ADDRESS 0x29 struct { - bool ready = false; uint16_t distance = 0; + bool ready = false; } vl53l1x_sensors; /********************************************************************************************/ @@ -54,13 +54,7 @@ void Vl53l1Detect(void) { vl53l1x_sensors.ready = true; } -#ifdef USE_WEBSERVER -const char HTTP_SNS_VL53L1X[] PROGMEM = - "{s}VL53L1X " D_DISTANCE "{m}%d" D_UNIT_MILLIMETER "{e}"; // {s} = , {m} = , {e} = -#endif // USE_WEBSERVER - void Vl53l1Every_250MSecond(void) { - // every 250 ms uint16_t dist = vl53l1x.read(); if (!dist || dist > 4000) { dist = 9999; @@ -78,15 +72,15 @@ void Vl53l1Every_Second(void) { void Vl53l1Show(bool json) { if (json) { + ResponseAppend_P(PSTR(",\"VL53L1X\":{\"" D_JSON_DISTANCE "\":%d}"), vl53l1x_sensors.distance); #ifdef USE_DOMOTICZ if (0 == TasmotaGlobal.tele_period) { Vl53l1Every_Second(); } #endif // USE_DOMOTICZ - ResponseAppend_P(PSTR(",\"VL53L1X\":{\"" D_JSON_DISTANCE "\":%d}"), vl53l1x_sensors.distance); #ifdef USE_WEBSERVER } else { - WSContentSend_PD(HTTP_SNS_VL53L1X, vl53l1x_sensors.distance); + WSContentSend_PD(HTTP_SNS_DISTANCE, PSTR("VL53L1X"), vl53l1x_sensors.distance); #endif } } @@ -95,9 +89,9 @@ void Vl53l1Show(bool json) { * Interface \*********************************************************************************************/ -bool Xsns77(uint8_t function) -{ +bool Xsns77(uint8_t function) { if (!I2cEnabled(XI2C_54)) { return false; } + bool result = false; if (FUNC_INIT == function) { diff --git a/tasmota/xsns_78_ezortd.ino b/tasmota/xsns_78_ezortd.ino index cf731a346..c9d37fa68 100644 --- a/tasmota/xsns_78_ezortd.ino +++ b/tasmota/xsns_78_ezortd.ino @@ -35,15 +35,14 @@ struct EZORTD : public EZOStruct { virtual void Show(bool json, const char *name) { - char str[10]; - dtostrfd(ConvertTemp(temperature), Settings.flag2.temperature_resolution, str); + float temp = ConvertTemp(temperature); if (json) { - ResponseAppend_P(PSTR(",\"%s\":{\"" D_JSON_TEMPERATURE "\":%s}"), name, str); + ResponseAppend_P(JSON_SNS_F_TEMP, name, Settings.flag2.temperature_resolution, &temp); } #ifdef USE_WEBSERVER else { - WSContentSend_PD(HTTP_SNS_TEMP, name, str, TempUnit()); + WSContentSend_Temp(name, temp); #endif // USE_WEBSERVER } } diff --git a/tasmota/xsns_79_as608.ino b/tasmota/xsns_79_as608.ino index 7bff7736c..fba9e8afc 100644 --- a/tasmota/xsns_79_as608.ino +++ b/tasmota/xsns_79_as608.ino @@ -103,7 +103,7 @@ void As608Init(void) { if (As608Finger->verifyPassword()) { As608Finger->getTemplateCount(); - AddLog_P(LOG_LEVEL_INFO, PSTR("AS6: Detected with %d fingerprint(s) stored"), As608Finger->templateCount); + AddLog(LOG_LEVEL_INFO, PSTR("AS6: Detected with %d fingerprint(s) stored"), As608Finger->templateCount); As608.selected = true; } } @@ -147,7 +147,7 @@ void As608Loop(void) { // p = As608Finger->fingerFastSearch(); // Match found - fails on R503 p = As608Finger->fingerSearch(); // Match found if (p != FINGERPRINT_OK) { -// AddLog_P(LOG_LEVEL_DEBUG, PSTR("AS6: No matching finger")); +// AddLog(LOG_LEVEL_DEBUG, PSTR("AS6: No matching finger")); return; } diff --git a/tasmota/xsns_80_mfrc522.ino b/tasmota/xsns_80_mfrc522.ino index 9a9b19e21..29207819c 100644 --- a/tasmota/xsns_80_mfrc522.ino +++ b/tasmota/xsns_80_mfrc522.ino @@ -25,7 +25,7 @@ * Connections: * MFRC522 ESP8266 Tasmota * ------- -------------- ---------- - * SDA GPIO0..5,15,16 SPI CS + * SDA GPIO0..5,15,16 RC522 CS * SCK GPIO14 SPI CLK * MOSI GPIO13 SPI MOSI * MISO GPIO12 SPI MISO @@ -113,7 +113,7 @@ void RC522Init(void) { } uint8_t empty_uid[4] = { 0 }; ToHex_P((unsigned char*)empty_uid, sizeof(empty_uid), Rc522.uids, sizeof(Rc522.uids)); - AddLog_P(LOG_LEVEL_INFO, PSTR("MFR: RC522 Rfid Reader detected %s"), ver); + AddLog(LOG_LEVEL_INFO, PSTR("MFR: RC522 Rfid Reader detected %s"), ver); Rc522.present = true; // } // Mfrc522->PCD_Init(); // Re-init as SelfTest blows init diff --git a/tasmota/xsns_81_seesaw_soil.ino b/tasmota/xsns_81_seesaw_soil.ino index 87c679a1a..23962275d 100644 --- a/tasmota/xsns_81_seesaw_soil.ino +++ b/tasmota/xsns_81_seesaw_soil.ino @@ -159,7 +159,7 @@ void seeSoilDetect(void) { // detect sensors buf = (uint8_t) Wire.read(); if (buf != SEESAW_HW_ID_CODE) { // check hardware id #ifdef DEBUG_SEESAW_SOIL - AddLog_P(LOG_LEVEL_DEBUG, PSTR("SEE: HWID mismatch ADDR=%X, ID=%X"), addr, buf); + AddLog(LOG_LEVEL_DEBUG, PSTR("SEE: HWID mismatch ADDR=%X, ID=%X"), addr, buf); #endif // DEBUG_SEESAW_SOIL continue; } @@ -173,7 +173,7 @@ void seeSoilDetect(void) { // detect sensors SeeSoil.count++; SeeSoil.present = true; #ifdef DEBUG_SEESAW_SOIL - AddLog_P(LOG_LEVEL_DEBUG, PSTR("SEE: FOUND sensor %u at %02X"), i, addr); + AddLog(LOG_LEVEL_DEBUG, PSTR("SEE: FOUND sensor %u at %02X"), i, addr); #endif // DEBUG_SEESAW_SOIL } } @@ -201,7 +201,7 @@ void seeSoilCommand(uint32_t command) { // issue commands to sensors break; default: #ifdef DEBUG_SEESAW_SOIL - AddLog_P(LOG_LEVEL_DEBUG, PSTR("SEE: ILL CMD:%02X"), command); + AddLog(LOG_LEVEL_DEBUG, PSTR("SEE: ILL CMD:%02X"), command); #endif // DEBUG_SEESAW_SOIL return; } @@ -212,7 +212,7 @@ void seeSoilCommand(uint32_t command) { // issue commands to sensors Wire.write((uint8_t) regLow); uint32_t err = Wire.endTransmission(); #ifdef DEBUG_SEESAW_SOIL - AddLog_P(LOG_LEVEL_DEBUG, PSTR("SEE: SNS=%u ADDR=%02X CMD=%02X ERR=%u"), i, addr, command, err); + AddLog(LOG_LEVEL_DEBUG, PSTR("SEE: SNS=%u ADDR=%02X CMD=%02X ERR=%u"), i, addr, command, err); #endif // DEBUG_SEESAW_SOIL } } @@ -242,7 +242,7 @@ void seeSoilRead(uint32_t command) { // read values from sensors #endif // SEESAW_SOIL_RAW } #ifdef DEBUG_SEESAW_SOIL - AddLog_P(LOG_LEVEL_DEBUG, PSTR("SEE: READ #%u ADDR=%02X NUM=%u RET=%X"), i, SeeSoilSNS[i].address, num, ret); + AddLog(LOG_LEVEL_DEBUG, PSTR("SEE: READ #%u ADDR=%02X NUM=%u RET=%X"), i, SeeSoilSNS[i].address, num, ret); #endif // DEBUG_SEESAW_SOIL } } @@ -272,11 +272,9 @@ void seeSoilEverySecond(void) { // update sensor values and publ #endif // SEESAW_SOIL_PUBLISH void seeSoilShow(bool json) { - char temperature[FLOATSZ]; char sensor_name[sizeof(SeeSoil.name) + 3]; for (uint32_t i = 0; i < SeeSoil.count; i++) { - dtostrfd(SeeSoilSNS[i].temperature, Settings.flag2.temperature_resolution, temperature); seeSoilName(i, sensor_name, sizeof(sensor_name)); if (json) { ResponseAppend_P(PSTR(",")); // compose tele json @@ -296,20 +294,20 @@ void seeSoilShow(bool json) { WSContentSend_PD(HTTP_SNS_ANALOG, sensor_name, 0, SeeSoilSNS[i].capacitance); #endif // SEESAW_SOIL_RAW WSContentSend_PD(HTTP_SNS_MOISTURE, sensor_name, (uint32_t) SeeSoilSNS[i].moisture); - WSContentSend_PD(HTTP_SNS_TEMP, sensor_name, temperature, TempUnit()); + WSContentSend_Temp(sensor_name, SeeSoilSNS[i].temperature); #endif // USE_WEBSERVER } } // for each sensor connected } void seeSoilJson(int no) { // common json - char temperature[FLOATSZ]; char sensor_name[sizeof(SeeSoil.name) + 3]; - seeSoilName(no, sensor_name, sizeof(sensor_name)); - dtostrfd(SeeSoilSNS[no].temperature, Settings.flag2.temperature_resolution, temperature); - ResponseAppend_P(PSTR ("\"%s\":{\"" D_JSON_ID "\":\"%02X\",\"" D_JSON_TEMPERATURE "\":%s,\"" D_JSON_MOISTURE "\":%u}"), - sensor_name, SeeSoilSNS[no].address, temperature, (uint32_t) SeeSoilSNS[no].moisture); + + ResponseAppend_P(PSTR ("\"%s\":{\"" D_JSON_ID "\":\"%02X\",\"" D_JSON_TEMPERATURE "\":%*_f,\"" D_JSON_MOISTURE "\":%u}"), + sensor_name, SeeSoilSNS[no].address, + Settings.flag2.temperature_resolution, &SeeSoilSNS[no].temperature, + (uint32_t) SeeSoilSNS[no].moisture); } void seeSoilName(int no, char *name, int len) // generates a sensor name diff --git a/tasmota/xsns_82_wiegand.ino b/tasmota/xsns_82_wiegand.ino new file mode 100644 index 000000000..b0607ef07 --- /dev/null +++ b/tasmota/xsns_82_wiegand.ino @@ -0,0 +1,426 @@ +/* + xsns_82_wiegand.ino - Support for Wiegand Interface 125kHz Rfid Tag Reader for Tasmota + + Copyright (C) 2021 Sigurd Leuther and Theo Arends + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifdef USE_WIEGAND +/*********************************************************************************************\ + * Wiegand 24, 26, 32, 34 bit Rfid reader 125 kHz + * + * Wire connections for https://www.benselectronics.nl/wiegand-2634-bit-rfid-reader-125khze248d29925d602d.html + * Red Vdc + * Black Gnd + * Green D0 + * White D1 + * Yellow/Grey Sound Buzzer if connected to Gnd + * Blue Light Led if connected to Gnd + * Grey/Purple 34-bit if connected to Gnd + * + * MQTT: + * %prefix%/%topic%/SENSOR = {"Time":"2021-01-21T16:04:12","Wiegand":{"UID":7748328,"Size":26}} + * %prefix%/%topic%/SENSOR = {"Time":"2021-01-21T15:48:49","Wiegand":{"UID":4302741608,"Size":34}} + * + * Rule: + * on wiegand#uid=4302741608 do publish cmnd/ailight/power 2 endon +\*********************************************************************************************/ +#warning **** Wiegand interface enabled **** + +#define XSNS_82 82 + +#define WIEGAND_BIT_TIMEOUT 25 // Time in mSec to be wait after last bit detected. + +// Use only a randomly generate RFID for testing. using #define will save some space in the final code +// DEV_WIEGAND_TEST_MODE 1 : testing with random rfid without hardware connected, but GPIOs set correctly +// DEV_WIEGAND_TEST_MODE 2 : testing with hardware correctly connected. +#define DEV_WIEGAND_TEST_MODE 0 + +#ifdef DEV_WIEGAND_TEST_MODE + #if (DEV_WIEGAND_TEST_MODE==0) + #elif (DEV_WIEGAND_TEST_MODE==1) + #warning "Wiegand Interface compiled with 'DEV_WIEGAND_TEST_MODE' 1 (Random RFID)" + #elif (DEV_WIEGAND_TEST_MODE==2) + #warning "Wiegand Interface compiled with 'DEV_WIEGAND_TEST_MODE' 2 (Hardware connected)" + #else + #warning "Wiegand Interface compiled with unknown mode" + #endif +#endif + +class Wiegand { + public: + + Wiegand(void); + void Init(void); + void ScanForTag(void); +#ifdef USE_WEBSERVER + void Show(void); +#endif // USE_WEBSERVER + + bool isInit = false; + uint8_t scanDelay; + + private: + + uint64_t HexStringToDec(uint64_t); + uint64_t CheckAndConvertRfid(uint64_t,uint16_t); + char translateEnterEscapeKeyPress(char); + uint8_t CalculateParities(uint64_t, int); + bool WiegandConversion (void); + static void handleD0Interrupt(void); + static void handleD1Interrupt(void); + + uint64_t rfid; + uint8_t tagSize; + + static volatile uint64_t rfidBuffer; + static volatile uint16_t bitCount; + static volatile uint32_t lastFoundTime; + static volatile uint8_t timeOut; + +#if (DEV_WIEGAND_TEST_MODE)==1 + uint64_t GetRandomRfid(uint8_t); +#endif // DEV_WIEGAND_TEST_MODE==1 +}; + +Wiegand* oWiegand = new Wiegand(); + +volatile uint64_t Wiegand::rfidBuffer; +volatile uint16_t Wiegand::bitCount; +volatile uint32_t Wiegand::lastFoundTime; +volatile uint8_t Wiegand::timeOut; + +Wiegand::Wiegand() { + rfid = 0; + lastFoundTime = 0; + tagSize = 0; + rfidBuffer = 0; + bitCount = 0 ; + timeOut = 0; + isInit = false; + scanDelay = 1; +} + +#if (DEV_WIEGAND_TEST_MODE)==1 +uint64_t Wiegand::GetRandomRfid(uint8_t tag_size=34) { + // Todo add support for 4 and 8 bit keyboard "tags" + uint64_t result = (uint32_t)HwRandom(); + uint8_t parities = 0; + bitCount = tag_size; + timeOut = millis() - WIEGAND_BIT_TIMEOUT; + result = result << 32; + result += HwRandom(); + + switch (tag_size){ + case 24: + result = (result & 0x7FFFFE) >>1; + break; + case 26: + result = (result & 0x1FFFFFE) >>1; + break; + case 32: + result = (result & 0x7FFFFFFE) >>1; + break; + case 34: + result = (result & 0x3FFFFFFFE) >>1; + break; + default: + break; + } + parities = CalculateParities(result, tag_size); + + result = (result << 1) | (parities & 0x01); // Set LSB parity + if (parities & 0x80) { // MSB parity is 1 + switch (tag_size) { + case 24: + result |= 0x800000; + break; + case 26: + result |= 0x2000000; + break; + case 32: + result |= 0x80000000; + break; + case 34: + result |= 0x400000000; + break; + default: + break; + } + } + + return result; +} +#endif // DEV_WIEGAND_TEST_MODE==1 + +void ICACHE_RAM_ATTR Wiegand::handleD1Interrupt() { // Receive a 1 bit. (D0=high & D1=low) + rfidBuffer = (rfidBuffer << 1) | 1; // Leftshift + 1 bit + bitCount++; // Increment the counter + lastFoundTime = millis(); // Last time bit found +} + +void ICACHE_RAM_ATTR Wiegand::handleD0Interrupt() { // Receive a 0 bit. (D0=low & D1=high) + rfidBuffer = rfidBuffer << 1; // Leftshift the 0 bit is now at the end of rfidBuffer + bitCount++; // Increment the counter + lastFoundTime = millis(); // Last time bit found +} + +void Wiegand::Init() { + isInit = false; + if (PinUsed(GPIO_WIEGAND_D0) && PinUsed(GPIO_WIEGAND_D1)) { // Only start, if the Wiegang pins are selected +#if (DEV_WIEGAND_TEST_MODE)>0 + AddLog(LOG_LEVEL_INFO, PSTR("WIE: Init()")); +#endif // DEV_WIEGAND_TEST_MODE>0 + pinMode(Pin(GPIO_WIEGAND_D0), INPUT_PULLUP); + pinMode(Pin(GPIO_WIEGAND_D1), INPUT_PULLUP); + attachInterrupt(Pin(GPIO_WIEGAND_D0), handleD0Interrupt, FALLING); + attachInterrupt(Pin(GPIO_WIEGAND_D1), handleD1Interrupt, FALLING); + isInit = true; // Helps to run only if correctly setup +#if (DEV_WIEGAND_TEST_MODE)>0 + AddLog(LOG_LEVEL_INFO, PSTR("WIE: Testmode, D0:%u, D1:%u"), Pin(GPIO_WIEGAND_D0), Pin(GPIO_WIEGAND_D1)); // For tests without reader attaiched +#else + AddLog(LOG_LEVEL_INFO, PSTR("WIE: Wiegand Rfid Reader detected")); +#endif // DEV_WIEGAND_TEST_MODE>0 + } +#if (DEV_WIEGAND_TEST_MODE)>0 + else { + AddLog(LOG_LEVEL_INFO, PSTR("WIE: no GPIOs.")); + } +#endif // DEV_WIEGAND_TEST_MODE>0 +} + +uint64_t Wiegand::CheckAndConvertRfid(uint64_t rfidIn, uint16_t bitcount) { + uint8_t evenParityBit = 0; + uint8_t oddParityBit = (uint8_t) (rfidIn & 0x1); // Last bit = odd parity + uint8_t calcParity = 0; + switch (bitcount) { + case 24: + evenParityBit = (rfidIn & 0x800000) ? 0x80 : 0; + rfidIn = (rfidIn & 0x7FFFFE) >>1; + break; + + case 26: + evenParityBit = (rfidIn & 0x2000000) ? 0x80 : 0; + rfidIn = (rfidIn & 0x1FFFFFE) >>1; + break; + + case 32: + evenParityBit = (rfidIn & 0x80000000) ? 0x80 : 0; + rfidIn = (rfidIn & 0x7FFFFFFE) >>1; + break; + + case 34: + evenParityBit = (rfidIn & 0x400000000) ? 0x80 : 0; + rfidIn = (rfidIn & 0x3FFFFFFFE) >>1; + break; + + default: + break; + } + calcParity = CalculateParities(rfidIn, bitCount); // Check result on http://www.ccdesignworks.com/wiegand_calc.htm with raw tag as input + if (calcParity != (evenParityBit | oddParityBit)) { // Parity bit is wrong + rfidIn=0; + AddLog(LOG_LEVEL_DEBUG, PSTR("WIE: %llu parity error"), rfidIn); + } +#if (DEV_WIEGAND_TEST_MODE)>0 + AddLog(LOG_LEVEL_INFO, PSTR("WIE: even (left) parity: %u "), (evenParityBit>>7)); + AddLog(LOG_LEVEL_INFO, PSTR("WIE: even (calc) parity: %u "), (calcParity & 0x80)>>7); + AddLog(LOG_LEVEL_INFO, PSTR("WIE: odd (right) parity: %u "), oddParityBit); + AddLog(LOG_LEVEL_INFO, PSTR("WIE: odd (calc) parity: %u "), (calcParity & 0x01)); +#endif // DEV_WIEGAND_TEST_MODE>0 + return rfidIn; +} + +uint8_t Wiegand::CalculateParities(uint64_t tagWithoutParities, int tag_size=26) { + // tag_size is the size of the final tag including the 2 parity bits + // So length if the tagWithoutParities should be (tag_size-2) !! That will be not profed and + // lead to wrong results if the input value is larger! + // Calculated start parity (even) will be returned as bit 8 + // calculated end parity (odd) will be returned as bit 1 + uint8_t retValue=0; + tag_size -= 2; + if (tag_size <= 0) { return retValue; } // Prohibit div zero exception and other wrong inputs + uint8_t parity = 1; // Check for odd parity on LSB + for (uint8_t i = 0; i < (tag_size / 2); i++) { + parity ^= (tagWithoutParities & 1); + tagWithoutParities >>= 1; + } + retValue |= parity; + + parity = 0; // Check for even parity on MSB + while (tagWithoutParities) { + parity ^= (tagWithoutParities & 1); + tagWithoutParities >>= 1; + } + retValue |= (parity << 7); + + return retValue; +} + +char Wiegand::translateEnterEscapeKeyPress(char oKeyPressed) { + switch(oKeyPressed) { + case 0x0b: // 11 or * key + return 0x0d; // 13 or ASCII ENTER + + case 0x0a: // 10 or # key + return 0x1b; // 27 or ASCII ESCAPE + + default: + return oKeyPressed; + } +} + +bool Wiegand::WiegandConversion () { + bool bRet = false; + unsigned long nowTick = millis(); + // Add a maximum wait time for new bits + unsigned long diffTicks = nowTick - lastFoundTime; + if ((diffTicks > WIEGAND_BIT_TIMEOUT) && (diffTicks >= 5000 )) { // Max. 5 secs between 2 bits comming in + bitCount = 0; + rfidBuffer = 0; + lastFoundTime = nowTick; + return bRet; + } + if (diffTicks > WIEGAND_BIT_TIMEOUT) { // Last bit found is WIEGAND_BIT_TIMEOUT ms ago +#if (DEV_WIEGAND_TEST_MODE)>0 + AddLog(LOG_LEVEL_INFO, PSTR("WIE: Raw tag %llu, Bit count %u"), rfidBuffer, bitCount); +#endif // DEV_WIEGAND_TEST_MODE>0 + if ((24 == bitCount) || (26 == bitCount) || (32 == bitCount) || (34 == bitCount)) { + // 24, 26, 32, 34-bit Wiegand codes + rfid = CheckAndConvertRfid(rfidBuffer, bitCount); + tagSize = bitCount; + bRet = true; + } + else if (4 == bitCount) { + // 4-bit Wiegand codes for keypads + rfid = (int)translateEnterEscapeKeyPress(rfidBuffer & 0x0000000F); + tagSize = bitCount; + bRet = true; + } + else if (8 == bitCount) { + // 8-bit Wiegand codes for keypads with integrity + // 8-bit Wiegand keyboard data, high nibble is the "NOT" of low nibble + // eg if key 1 pressed, data=E1 in binary 11100001 , high nibble=1110 , low nibble = 0001 + char highNibble = (rfidBuffer & 0xf0) >>4; + char lowNibble = (rfidBuffer & 0x0f); + if (lowNibble == (~highNibble & 0x0f)) { // Check if low nibble matches the "NOT" of high nibble. + rfid = (int)translateEnterEscapeKeyPress(lowNibble); + bRet = true; + } else { + lastFoundTime = nowTick; + bRet = false; + } + tagSize = bitCount; + } else { + // Time reached but unknown bitCount, clear and start again + lastFoundTime = nowTick; + bRet = false; + } + bitCount = 0; + rfidBuffer = 0; + } else { + bRet = false; // Watching time not finished + } +#if (DEV_WIEGAND_TEST_MODE)>0 + AddLog(LOG_LEVEL_INFO, PSTR("WIE: Tag out %llu, tag size %u "), rfid, tagSize); +#endif // DEV_WIEGAND_TEST_MODE>0 + return bRet; +} + +void Wiegand::ScanForTag() { +#if (DEV_WIEGAND_TEST_MODE)>0 + AddLog(LOG_LEVEL_INFO, PSTR("WIE: ScanForTag().")); +#if (DEV_WIEGAND_TEST_MODE==1) + switch (millis() %4) { + case 0: + rfidBuffer = GetRandomRfid(24); + break; + case 1: + rfidBuffer = GetRandomRfid(26); + break; + case 2: + rfidBuffer = GetRandomRfid(32); + break; + case 3: + rfidBuffer = GetRandomRfid(34); + break; + default: + rfidBuffer = GetRandomRfid(34); + break; + } + AddLog(LOG_LEVEL_INFO, PSTR("WIE: Raw generated %lX"), rfidBuffer); // For tests without reader attaiched +#endif // DEV_WIEGAND_TEST_MODE==1 +#endif // DEV_WIEGAND_TEST_MODE>0 + if (bitCount > 0) { + uint64_t oldTag = rfid; + bool validKey = WiegandConversion(); +#if (DEV_WIEGAND_TEST_MODE)>0 + AddLog(LOG_LEVEL_INFO, PSTR("WIE: Previous tag %llu"), oldTag); +#endif // DEV_WIEGAND_TEST_MODE>0 + if (validKey) { // Only in case of valid key do action. Issue#10585 + if (oldTag == rfid) { + AddLog(LOG_LEVEL_DEBUG, PSTR("WIE: Old tag")); + } + ResponseTime_P(PSTR(",\"Wiegand\":{\"UID\":%0llu,\"" D_JSON_SIZE "\":%u}}"), rfid, tagSize); + MqttPublishTeleSensor(); + } + } +} + +#ifdef USE_WEBSERVER +void Wiegand::Show(void) { + WSContentSend_PD(PSTR("{s}Wiegand UID{m}%llu {e}"), rfid); +#if (DEV_WIEGAND_TEST_MODE)>0 + AddLog(LOG_LEVEL_INFO, PSTR("WIE: Tag %llu, Bits %u"), rfid, bitCount); +#endif // DEV_WIEGAND_TEST_MODE>0 +} +#endif // USE_WEBSERVER + +/*********************************************************************************************\ + * Interface +\*********************************************************************************************/ + +bool Xsns82(byte function) { + bool result = false; + + if (FUNC_INIT == function) { + oWiegand->Init(); + } + else if (oWiegand->isInit) { + switch (function) { + case FUNC_EVERY_250_MSECOND: // Some tags need more time, don't try shorter period +#if (DEV_WIEGAND_TEST_MODE)==1 + if (oWiegand->scanDelay >= 4) // Give a second because of the log entries to be send. +#else + if (oWiegand->scanDelay >= 2) // Only run every (delay * 250 ms) (every 250ms is too fast for some tags) +#endif + { + oWiegand->ScanForTag(); + oWiegand->scanDelay = 1; + } else { + oWiegand->scanDelay++; + } + break; +#ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: + oWiegand->Show(); + break; +#endif // USE_WEBSERVER + } + } + return result; +} + +#endif // USE_WIEGAND \ No newline at end of file diff --git a/tasmota/xsns_83_neopool.ino b/tasmota/xsns_83_neopool.ino new file mode 100644 index 000000000..2bf87acd0 --- /dev/null +++ b/tasmota/xsns_83_neopool.ino @@ -0,0 +1,1348 @@ +/* + xsns_83_neopool.ino - Sugar Valley NeoPool Control System Modbus support for Tasmota + + Copyright (C) 2021 Norbert Richter + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifdef USE_NEOPOOL +/*********************************************************************************************\ + * Sugar Valley NeoPool electronic pool control and water treatment system, also known as brand + * Hidrolife (yellow case) + * Aquascenic (blue case) + * Oxilife (green case) + * Bionet (light blue case) + * Hidroniser (red case) + * UVScenic (lilca case) + * Station (orange case) + * Brilix + * Bayrol + * Hay + * + * Sugar Valley RS485 connector inside (Display/Wifi/External) + * pins (from top to bottom): + * + * RS485 MODBUS + * ___ + * 1 |* |- +12V + * 2 |* |- + * 3 |* |- Modbus A+ + * 4 |* |- Modbus B- + * 5 |*__|- Modbus GND + * + * RS485 Parameter: 19200 Baud / 1 Stopbit / Parity None + * + * Hardware serial will be selected if GPIO1 = [NeoPool Rx] and GPIO3 = [NeoPool Tx] +\*********************************************************************************************/ + +#define XSNS_83 83 + +#ifndef NEOPOOL_MODBUS_SPEED +#define NEOPOOL_MODBUS_SPEED 19200 +#endif + +#ifndef NEOPOOL_MODBUS_ADDRESS +#define NEOPOOL_MODBUS_ADDRESS 1 // Modbus address +#endif + + +#define NEOPOOL_READ_REGISTER 0x03 // Function code used to read register: Read Holding Registers +#define NEOPOOL_WRITE_REGISTER 0x10 // Function code used to write register: Write Multiple Registers +#define NEOPOOL_READ_TIMEOUT 25 // read data timeout in ms + +//#define NEOPOOL_OPTIMIZE_READINGS // Optimize modbus readings by considering of MBF_NOTIFICATION register - Note: Does not work on all systems! + +/*********************************************************************************************\ + * Sugar Valley Modbus Register (* register are currently used) + * (see https://downloads.vodnici.net/uploads/wpforo/attachments/69/171-Modbus-registers.pdf) +\*********************************************************************************************/ +enum NeoPoolRegister { + // addr Unit Description + // ------ ----- ------------------------------------------------------------ + // MODBUS page (0x0000 - 0x002E - unknown - for internal use only) + + // MEASURE page (0x01xx) + MBF_ION_CURRENT=0x0100, // 0x0100* Current measured in the ionization system + MBF_HIDRO_CURRENT, // 0x0101* Intensity level currently measured in the hydrolysissystem + MBF_MEASURE_PH, // 0x0102* ph Level measured in hundredths (700=7.00) + MBF_MEASURE_RX, // 0x0103* ppm Redox level in hundredths of ppm (100=1.00 ppm) + MBF_MEASURE_CL, // 0x0104* ppm Level measured in hundredths of chlorine ppm (100=1.00 ppm) + MBF_MEASURE_CONDUCTIVITY, // 0x0105 % Level of conductivity measured in the water. + MBF_MEASURE_TEMPERATURE, // 0x0106* °C Water temperature sensor (100=10.0°C) + MBF_PH_STATUS, // 0x0107* mask Status of the module control pH + MBF_RX_STATUS, // 0x0108* mask Status of the Rx-module + MBF_CL_STATUS, // 0x0109* mask Status of the Chlorine-module + MBF_CD_STATUS, // 0x010A mask Status of the Conductivity-module + MBF_ION_STATUS=0x010C, // 0x010C* mask Status of the Ionization-module + MBF_HIDRO_STATUS, // 0x010D* mask Status of the Hydrolysis-module + MBF_RELAY_STATE, // 0x010E* mask Status of each configurable relays + MBF_HIDRO_SWITCH_VALUE, // 0x010F INTERNAL - contains the opening of the hydrolysis PWM. + MBF_NOTIFICATION, // 0x0110* mask Reports whether a page of properties has changed since the last time it was consulted. + MBF_HIDRO_VOLTAGE, // 0x0111 Reports on the stress applied to the hydrolysis cell. This register, together with that of MBF_HIDRO_CURRENT allows extrapolating the salinity of the water. + + // GLOBAL page (0x02xx) + MBF_SAVE_TO_EEPROM=0x02F0, // 0x02F0 A write operation to this register starts a EEPROM storage operation immediately. During the EEPROM storage procedure, the system may be unresponsive to MODBUS requests. The operation will last always less than 1 second. + + // FACTORY page (0x03xx) + MBF_PAR_VERSION=0x0300, // 0x0300* Software version of the PowerBox (unused) + MBF_PAR_MODEL, // 0x0301* mask System model options + MBF_PAR_SERNUM, // 0x0302 Serial number of the PowerBox (unused) + MBF_PAR_ION_NOM, // 0x0303 Ionization maximum production level (DO NOT WRITE!) + MBF_PAR_HIDRO_NOM=0x0306, // 0x0306 Hydrolysis maximum production level. (DO NOT WRITE!) If the hydrolysis is set to work in percent mode, this value will be 100. If the hydrolysis module is set to work in g/h production, this module will contain the maximum amount of production in g/h units. (DO NOT WRITE!) + MBF_PAR_SAL_AMPS=0x030A, // 0x030A Current command in regulation for which we are going to measure voltage + MBF_PAR_SAL_CELLK, // 0x030B Specifies the relationship between the resistance obtained in the measurement process and its equivalence in g / l (grams per liter) + MBF_PAR_SAL_TCOMP, // 0x030C Specifies the deviation in temperature from the conductivity. + MBF_PAR_HIDRO_MAX_VOLTAGE=0x0322, // 0x0322 Allows setting the maximum voltage value that can be reached with the hydrolysis current regulation. The value is specified in tenths of a volt. The default value of this register when the EEPROM is cleared is 80, which is equivalent to a maximum cell operating voltage of 8 volts. + MBF_PAR_HIDRO_FLOW_SIGNAL, // 0x0323 Allows to select the operation of the flow detection signal associated with the operation of the hydrolysis (see MBV_PAR_HIDRO_FLOW_SIGNAL*). The default value for this register is 0 (standard detection). + MBF_PAR_HIDRO_MAX_PWM_STEP_UP, // 0x0324 This register sets the PWM ramp up of the hydrolysis in pulses per duty cycle. This register makes it possible to adjust the rate at which the power delivered to the cell increases, allowing a gradual rise in power so that the operation of the switching source of the equipment is not saturated. Default 150 + MBF_PAR_HIDRO_MAX_PWM_STEP_DOWN, // 0x0325 This register sets the PWM down ramp of the hydrolysis in pulses per duty cycle. This register allows adjusting the rate at which the power delivered to the cell decreases, allowing a gradual drop in power so that the switched source of the equipment is not disconnected due to lack of consumption. This gradual fall must be in accordance with the type of cell used, since said cell stores charge once the current stimulus has ceased. Default 20 + + // INSTALLER page (0x04xx) + MBF_PAR_ION_POL0=0x0400, // 0x0400 Time in min the team must spend working on positive polarization in copper-silver ionization. + MBF_PAR_ION_POL1, // 0x0401 Time in min the team must spend working on positive polarization in copper-silver ionization. + MBF_PAR_ION_POL2, // 0x0402 Time in min the team must spend working on positive polarization in copper-silver ionization. + MBF_PAR_HIDRO_ION_CAUDAL, // 0x0403 mask Bitmask register regulates the external control mode of ionization, hydrolysis and pumps. + MBF_PAR_HIDRO_MODE, // 0x0404 Regulates the external control mode of hydrolysis from the modules of measure. 0: no control, 1: standard control (on / off), 2: with timed pump + MBF_PAR_HIDRO_POL0, // 0x0405 Time must spend working on positive polarization in hydrolysis / electrolysis. Time is stored in minutes. + MBF_PAR_HIDRO_POL1, // 0x0406 Time must spend working on positive polarization in hydrolysis / electrolysis. Time is stored in minutes. + MBF_PAR_HIDRO_POL2, // 0x0407 Time must spend working on positive polarization in hydrolysis / electrolysis. Time is stored in minutes. + MBF_PAR_TIME_LOW, // 0x0408* System timestamp (32 bit unixtime) - low word + MBF_PAR_TIME_HIGH, // 0x0409* System timestamp (32 bit unixtime) - high word + MBF_PAR_PH_ACID_RELAY_GPIO, // 0x040A* Relay number assigned to the acid pump function (only with pH module). + MBF_PAR_PH_BASE_RELAY_GPIO, // 0x040B* Relay number assigned to the base pump function (only with pH module). + MBF_PAR_RX_RELAY_GPIO, // 0x040C* Relay number assigned to the Redox level regulation function. If the value is 0, there is no relay assigned, and therefore there is no pump function (ON / OFF should not be displayed) + MBF_PAR_CL_RELAY_GPIO, // 0x040D* Relay number assigned to the chlorine pump function (only with free chlorine measuring modules). + MBF_PAR_CD_RELAY_GPIO, // 0x040E* Relay number assigned to the conductivity (brine) pump function (only with conductivity measurement modules). + MBF_PAR_TEMPERATURE_ACTIVE, // 0x040F* Indicates whether the equipment has a temperature measurement or not. + MBF_PAR_LIGHTING_GPIO, // 0x0410* Relay number assigned to the lighting function. 0: inactive. + MBF_PAR_FILT_MODE, // 0x0411* Filtration mode (see MBV_PAR_FILT_*) + MBF_PAR_FILT_GPIO, // 0x0412* Relay selected to perform the filtering function (by default it is relay 2). When this value is at zero, there is no relay assigned and therefore it is understood that the equipment does not control the filtration. In this case, the filter option does not appear in the user menu. + MBF_PAR_FILT_MANUAL_STATE, // 0x0413 Filtration status in manual mode (on = 1; off = 0) + MBF_PAR_HEATING_MODE, // 0x0414 Heating mode. 0: the equipment is not heated. 1: the equipment is heating. + MBF_PAR_HEATING_GPIO, // 0x0415 Relay selected to perform the heating function (by default it is relay 7). When this value is at zero, there is no relay assigned and therefore it is understood that the equipment does not control the heating. In this case, the filter modes associated with heating will not be displayed. + MBF_PAR_HEATING_TEMP, // 0x0416 Heating setpoint temperature + MBF_PAR_CLIMA_ONOFF, // 0x0417 Activation of the air conditioning mode (0 inactive; 1 active. + MBF_PAR_SMART_TEMP_HIGH, // 0x0418 Superior temperature of the Smart mode + MBF_PAR_SMART_TEMP_LOW, // 0x0419 Lower temperature of the Smart mode + MBF_PAR_SMART_ANTI_FREEZE, // 0x041A Antifreeze mode activated (1) or not (0). This adjustment is only available in the Smart filtration mode. + MBF_PAR_SMART_INTERVAL_REDUCTION, // 0x041B This register is read-only and reports to the outside what percentage (0 to 100%) is being applied to the nominal filtration time. 100% means that the total programmed time is being filtered. + MBF_PAR_INTELLIGENT_TEMP, // 0x041C Setpoint temperature for smart mode + MBF_PAR_INTELLIGENT_FILT_MIN_TIME, // 0x041D Minimum filtration time in minutes + MBF_PAR_INTELLIGENT_BONUS_TIME, // 0x041E Bonus time for the current set of intervals + MBF_PAR_INTELLIGENT_TT_NEXT_INTERVAL,//0x041F Time to next filtration interval. When it reaches 0 an interval is started and the number of seconds is reloaded for the next interval (2x3600) + MBF_PAR_INTELLIGENT_INTERVALS, // 0x0420 Number of started intervals. When it reaches 12 it is reset to 0 and the bonus time is reloaded with the value of MBF_PAR_INTELLIGENT_FILT_MIN_TIME + MBF_PAR_FILTRATION_STATE, // 0x0421 Filtration function state: 0 is off and 1 is on. The filtration state is regulated according to the MBF_PAR_FILT_MANUAL_STATE register if the filtration mode held in register MBF_PAR_FILT_MODE is set to FILT_MODE_MANUAL (0). + MBF_PAR_HEATING_DELAY_TIME, // 0x0422 // Timer in seconds that counts up when the heating is to be enabled. Once this counter reaches 60 seconds, the heating is then enabled. This counter is for internal use only. + MBF_PAR_FILTERING_TIME_LOW, // 0x0423 32-bit value: + MBF_PAR_FILTERING_TIME_HIGH, // 0x0424 Internal timer for the intelligent filtering mode. It counts the filtering time done during a given day. This register is only for internal use and should not be modified by the user. + MBF_PAR_INTELLIGENT_INTERVAL_TIME_LOW,// 0x0425 32-bit value: + MBF_PAR_INTELLIGENT_INTERVAL_TIME_HIGH,// 0x0426 Internal timer that counts the filtration interval assigned to the the intelligent mode. This register is only for internal use and should not be modified by the user. + MBF_PAR_UV_MODE, // 0x0427 UV mode active or not - see MBV_PAR_UV_MODE*. To enable UV support for a given device, add the mask MBMSK_MODEL_UV to the MBF_PAR_MODEL register. + MBF_PAR_UV_HIDE_WARN, // 0x0428 mask Suppression for warning messages in the UV mode. + MBF_PAR_UV_RELAY_GPIO, // 0x0429 Relay number assigned to the UV function. + MBF_PAR_PH_PUMP_REP_TIME_ON, // 0x042A mask Time that the pH pump will be turn on in the repetitive mode (see MBMSK_PH_PUMP_*). Contains a special time format, see desc for MBMSK_PH_PUMP_TIME. + MBF_PAR_PH_PUMP_REP_TIME_OFF, // 0x042B Time that the pH pump will be turn off in the repetitive mode. Contains a special time format, see desc for MBMSK_PH_PUMP_TIME, has no upper configuration bit 0x8000 + MBF_PAR_HIDRO_COVER_ENABLE, // 0x042C Options for the hydrolysis/electrolysis module (see MBMSK_HIDRO_*) + MBF_PAR_HIDRO_COVER_REDUCTION, // 0x042D Configured levels for the cover reduction and the hydrolysis shutdown temperature options: LSB=Percentage for the cover reduction, MSB=Temperature level for the hydrolysis shutdown (see MBMSK_HIDRO_*) + MBF_PAR_PUMP_RELAY_TIME_OFF, // 0x042E Time level in minutes or seconds that the dosing pump must remain off when the temporized pump mode is selected. This time level register applies to all pumps except pH. Contains a special time format, see desc for MBMSK_PH_PUMP_TIME, has no upper configuration bit 0x8000 + MBF_PAR_PUMP_RELAY_TIME_ON, // 0x042F Time level in minutes or seconds that the dosing pump must remain on when the temporized pump mode is selected. This time level register applies to all pumps except pH. Contains a special time format, see desc for MBMSK_PH_PUMP_TIME, has no upper configuration bit 0x8000 + MBF_PAR_RELAY_PH, // 0x0430 Determine what pH regulation configuration the equipment has (see MBV_PAR_RELAY_PH_*) + MBF_PAR_RELAY_MAX_TIME, // 0x0431 Maximum amount of time, in seconds, that a dosing pump can operate before rising an alarm signal. The behavior of the system when the dosing time is exceeded is regulated by the type of action stored in the MBF_PAR_RELAY_MODE register. + MBF_PAR_RELAY_MODE, // 0x0432 Behavior of the system when the dosing time is exceeded (see MBMSK_PAR_RELAY_MODE_* and MBV_PAR_RELAY_MODE_*) + MBF_PAR_RELAY_ACTIVATION_DELAY, // 0x0433 Delay time in seconds for the pH pump when the measured pH value is outside the allowable pH setpoints. The system internally adds an extra time of 10 seconds to the value stored here. The pump starts the dosing operation once the condition of pH out of valid interval is maintained during the time specified in this register. + MBF_PAR_TIMER_BLOCK_BASE, // 0x0434 This block of 180 registers holds the configuration of the system timers. The system has a set of 12 fully configurable timers, each one assigned to a specific function, described below: + MBF_PAR_TIMER_BLOCK_FILT_INT1=0x0434,//0x0434 Filtration interval 1 (15 register - see PAR_TIMER_BLOCK_OFF* for desc) + MBF_PAR_TIMER_BLOCK_FILT_INT2=0x0443,//0x0443 Filtration interval 2 (15 register - see PAR_TIMER_BLOCK_OFF* for desc) + MBF_PAR_TIMER_BLOCK_FILT_INT3=0x0452,//0x0452 Filtration interval 3 (15 register - see PAR_TIMER_BLOCK_OFF* for desc) + MBF_PAR_TIMER_BLOCK_AUX1_INT2=0x0461,//0x0461 Auxiliary relay 1 - 2. interval (15 register - see PAR_TIMER_BLOCK_OFF* for desc) + MBF_PAR_TIMER_BLOCK_LIGHT_INT=0x0470,//0x0470 Lighting interval (15 register - see PAR_TIMER_BLOCK_OFF* for desc) + MBF_PAR_TIMER_BLOCK_AUX2_INT2=0x047F,//0x047F Auxiliary relay 2 - 2. interval (15 register - see PAR_TIMER_BLOCK_OFF* for desc) + MBF_PAR_TIMER_BLOCK_AUX3_INT2=0x048E,//0x048E Auxiliary relay 3 - 2. interval (15 register - see PAR_TIMER_BLOCK_OFF* for desc) + MBF_PAR_TIMER_BLOCK_AUX4_INT2=0x049D,//0x049D Auxiliary relay 4 - 2. interval (15 register - see PAR_TIMER_BLOCK_OFF* for desc) + MBF_PAR_TIMER_BLOCK_AUX1_INT1=0x04AC,//0x04AC Auxiliary relay 1 - 1. interval (15 register - see PAR_TIMER_BLOCK_OFF* for desc) + MBF_PAR_TIMER_BLOCK_AUX2_INT1=0x04BB,//0x04BB Auxiliary relay 2 - 1. interval (15 register - see PAR_TIMER_BLOCK_OFF* for desc) + MBF_PAR_TIMER_BLOCK_AUX3_INT1=0x04CA,//0x04CA Auxiliary relay 3 - 1. interval (15 register - see PAR_TIMER_BLOCK_OFF* for desc) + MBF_PAR_TIMER_BLOCK_AUX4_INT1=0x04D9,//0x04D9 Auxiliary relay 4 - 1. interval (15 register - see PAR_TIMER_BLOCK_OFF* for desc) + MBF_PAR_FILTVALVE_ENABLE=0x04E8, // 0x04E8 Filter cleaning functionality mode (0=off, 1=Besgo) + MBF_PAR_FILTVALVE_MODE, // 0x04E9 Filter cleaning valve timing mode, possible modes: MBV_PAR_CTIMER_ENABLED, MBV_PAR_CTIMER_ALWAYS_ON, MBV_PAR_CTIMER_ALWAYS_OFF + MBF_PAR_FILTVALVE_GPIO, // 0x04EA Relay associated with the filter cleaning function. default AUX2 (value 5) + MBF_PAR_FILTVALVE_START_LOW, // 0x04EB start timestamp of filter cleaning (32-bit) + MBF_PAR_FILTVALVE_START_HIGH, // 0x04EC " + MBF_PAR_FILTVALVE_PERIOD_MINUTES, // 0x04ED Period in minutes between cleaning actions. For example, if a value of 60 is stored in this registry, a cleanup action will occur every hour. + MBF_PAR_FILTVALVE_INTERVAL, // 0x04EE Cleaning action duration in seconds. + MBF_PAR_FILTVALVE_REMAINING, // 0x04EF Time remaining for the current cleaning action in seconds. If this register is 0, it means that there is no cleaning function running. When a cleanup function is started, the contents of the MBF_PAR_FILTVALVE_INTERVAL register are copied to this register, then decremented once per second. The display uses this log to track the progress of the cleaning function. + MBF_ACTION_COPY_TO_RTC, // 0x04F0 A write (any value) forces the writing of the RTC time registers MBF_PAR_TIME_LOW (0x0408) and MBF_PAR_TIME_HIGH (0x0409) into the RTC internal microcontroller clock management registers. + + // USER page (0x05xx) To make the modification of this register persistent, execute the EEPROM storage procedure described in global register MBF_SAVE_TO_EEPROM. + MBF_PAR_ION=0x0500, // 0x0500 Ionization target production level. The value adjusted in this register must not exceed the value set in the MBF_PAR_ION_NOM factory register. + MBF_PAR_ION_PR, // 0x0501 Amount of time in minutes that the ionization must be activated each time that the filtration starts. + MBF_PAR_HIDRO, // 0x0502 Hydrolisis target production level. When the hydrolysis production is to be set in percent values, this value will contain the percent of production. If the hydrolysis module is set to work in g/h production, this module will contain the desired amount of production in g/h units. The value adjusted in this register must not exceed the value set in the MBF_PAR_HIDRO_NOM factory register. + MBF_PAR_PH1=0x0504, // 0x0504 Higher limit of the pH regulation system. The value set in this register is multiplied by 100. This means that if we want to set a value of 7.5, the numerical content that we must write in this register is 750. This register must be always higher than MBF_PAR_PH2. + MBF_PAR_PH2, // 0x0505 Lower limit of the pH regulation system. The value set in this register is multiplied by 100. This means that if we want to set a value of 7.0, the numerical content that we must write in this register is 700. This register must be always lower than MBF_PAR_PH1. + MBF_PAR_RX1=0x0508, // 0x0508 Set point for the redox regulation system. This value must be in the range of 0 to 1000. + MBF_PAR_CL1=0x050A, // 0x050A Set point for the chlorine regulation system. The value stored in this register is multiplied by 100. This mean that if we want to set a value of 1.5 ppm, we will have to write a numerical value of 150. This value stored in this register must be in the range of 0 to 1000. + MBF_PAR_FUNCTION_DEPENDENCY=0x051B, // 0x051B mask Specification for the dependency of different functions, such as heating, from external events like FL1 (see MBMSK_FCTDEP_HEATING/MBMSK_DEPENDENCY_*) + + // MISC page (0x06xx) + MBF_PAR_UICFG_MACHINE=0x0600, // 0x0600* Machine type (see MBV_PAR_MACH_* and kNeoPoolMachineNames[]) + MBF_PAR_UICFG_LANGUAGE, // 0x0601 Selected language (see MBV_PAR_LANG_*) + MBF_PAR_UICFG_BACKLIGHT, // 0x0602 Display backlight (see MBV_PAR_BACKLIGHT_*) + MBF_PAR_UICFG_SOUND, // 0x0603 mask Audible alerts (see MBMSK_PAR_SOUND_*) + MBF_PAR_UICFG_PASSWORD, // 0x0604 System password encoded in BCD + MBF_PAR_UICFG_VISUAL_OPTIONS, // 0x0605 mask Stores the different display options for the user interface menus (bitmask). Some bits allow you to hide options that are normally visible (bits 0 to 3) while other bits allow you to show options that are normally hidden (bits 9 to 15) + MBF_PAR_UICFG_VISUAL_OPTIONS_EXT, // 0x0606 mask This register stores additional display options for the user interface menus, see MBMSK_VOE_* + MBF_PAR_UICFG_MACH_VISUAL_STYLE, // 0x0607 mask This register is an expansion of register 0x0600 and 0x0605. The lower part of the register (8 bits LSB) is used to store the type of color selected when in register 0x600 has been specified that the machine is of type "generic". Colors and styles correspond to those listed in record 0x600 MBF_PAR_UICFG_MACHINE. The upper part (8-bit MSB) contains extra bits MBMSK_VS_FORCE_UNITS_GRH, MBMSK_VS_FORCE_UNITS_PERCENTAGE and MBMSK_ELECTROLISIS + MBF_PAR_UICFG_MACH_NAME_BOLD_0, // 0x0608 This set of 4 registers stores an ASCIIZ string of up to 8 characters that is used to specify the bold part of the title to be displayed at startup if the specified machine type is generic. Note: only lowercase letters (a-z) can be used. + MBF_PAR_UICFG_MACH_NAME_BOLD_1, // 0x0609 + MBF_PAR_UICFG_MACH_NAME_BOLD_2, // 0x060A + MBF_PAR_UICFG_MACH_NAME_BOLD_3, // 0x060B + MBF_PAR_UICFG_MACH_NAME_LIGHT_0, // 0x060C This set of 4 registers stores an ASCIIZ string of up to 8 characters that is used to specify the normal intensity part of the title that will be displayed at startup if the specified machine type is generic. Note: Only lowercase letters (a-z) can be used. + MBF_PAR_UICFG_MACH_NAME_LIGHT_1, // 0x060D + MBF_PAR_UICFG_MACH_NAME_LIGHT_2, // 0x060E + MBF_PAR_UICFG_MACH_NAME_LIGHT_3, // 0x060F +}; + +// Sugar Valley register constants and bit masks +enum NeoPoolConstAndBitMask { + // MBF_PH_STATUS + MBMSK_PH_STATUS_ALARM = 0x000F, // PH alarm. The possible alarm values are depending on the regulation model: + // Valid alarm values for pH regulation with acid and base: + MBV_PH_ACID_BASE_ALARM0 = 0, // no alarm + MBV_PH_ACID_BASE_ALARM1 = 1, // pH too high; the pH value is 0.8 points higher than the setpoint value set in PH1 + MBV_PH_ACID_BASE_ALARM2 = 2, // pH too low: the pH value is 0.8 points lower than the set point value set in PH2 + MBV_PH_ACID_BASE_ALARM3 = 3, // pH pump (acid or base, it does not matter) has exceeded the working time set by the MBF_PAR_RELAY_PH_MAX_TIME parameter and has stopped. + MBV_PH_ACID_BASE_ALARM4 = 4, // pH higher than the set point indicated in PH1 + MBV_PH_ACID_BASE_ALARM5 = 5, // pH lower than the set point indicated in PH2 + // Valid alarm values for pH regulation with acid only: + MBV_PH_ACID_ALARM0 = 0, // no alarm + MBV_PH_ACID_ALARM1 = 1, // pH too high; the pH value is 0.8 points higher than the setpoint value set in PH1 + MBV_PH_ACID_ALARM2 = 2, // pH too low: the pH value is 0.8 points lower than the setpoint value set in PH1 + MBV_PH_ACID_ALARM3 = 3, // pH pump acid has exceeded the working time set by the MBF_PAR_RELAY_PH_MAX_TIME parameter and has stopped. + MBV_PH_ACID_ALARM4 = 4, // pH higher than the setpoint indicated in PH1 by 0.1 + MBV_PH_ACID_ALARM5 = 5, // pH lower than the set point indicated in PH1 by 0.3 + // Valid alarm values for pH regulation with base only: + MBV_PH_BASE_ALARM0 = 0, // no alarm + MBV_PH_BASE_ALARM1 = 1, // pH too high; the pH value is 0.8 points higher than the set point value set in PH2 + MBV_PH_BASE_ALARM2 = 2, // pH too low: the pH value is 0.8 points lower than the set point value set in PH2 + MBV_PH_BASE_ALARM3 = 3, // pH pump has exceeded the working time set by the MBF_PAR_RELAY_PH_MAX_TIME parameter and has stopped. + MBV_PH_BASE_ALARM4 = 4, // pH higher than the set point indicated in PH2 by 0.1 + MBV_PH_BASE_ALARM5 = 5, // pH lower than the set point indicated in PH2 by 0.3 + + MBMSK_PH_STATUS_CTRL_BY_FL = 0x0400, // Control status of the pH module by flow detection (if enabled by MBF_PAR_HIDRO_ION_CAUDAL) + MBMSK_PH_STATUS_ACID_PUMP_ACTIVE = 0x0800, // Acid pH pump relay on (pump on) + MBMSK_PH_STATUS_BASE_PUMP_ACTIVE = 0x1000, // Base pH Pump Relay On (Pump On) + MBMSK_PH_STATUS_CTRL_ACTIVE = 0x2000, // Active pH control module and controlling pumps + MBMSK_PH_STATUS_MEASURE_ACTIVE = 0x4000, // Active pH measurement module and making measurements. If this bit is at 1, the pH bar should be displayed. + MBMSK_PH_STATUS_MODULE_PRESENT = 0x8000, // Detected pH measurement module + + // MBF_RX_STATUS + MBMSK_RX_STATUS_RX_PUMP_ACTIVE = 0x1000, // Redox pump relay on (pump activated) + MBMSK_RX_STATUS_CTRL_ACTIVE = 0x2000, // Active Redox control module and controlling pump + MBMSK_RX_STATUS_MEASURE_ACTIVE = 0x4000, // Active Redox measurement module and performing measurements. If this bit is at 1, the Redox bar should be displayed on the screen. + MBMSK_RX_STATUS_MODULE_PRESENT = 0x8000, // Redox measurement module detected in the system + + // MBF_CL_STATUS + MBMSK_CL_STATUS_CHLORINE_FLOW = 0x0008, // Chlorine Probe Flow Sensor. This sensor is built into the probe itself and serves to detect whether there is water passing through the chlorine measurement probe. In case the sensor is at 0, the chlorine measurement will not be valid. + MBMSK_CL_STATUS_CL_PUMP_ACTIVE = 0x1000, // Chlorine pump relay on (pump on) + MBMSK_CL_STATUS_CTRL_ACTIVE = 0x2000, // Active chlorine control module and controlling pump + MBMSK_CL_STATUS_MEASURE_ACTIVE = 0x4000, // Active chlorine measurement module and taking measurements. If this bit is 1, the chlorine bar should be displayed on the screen. + MBMSK_CL_STATUS_MODULE_PRESENT = 0x8000, // Chlorine measurement module detected in the system + + // MBF_CD_STATUS + MBMSK_CD_STATUS_RX_PUMP_ACTIVE = 0x1000, // Conductivity pump relay on (pump active) + MBMSK_CD_STATUS_CTRL_ACTIVE = 0x2000, // Active conductivity control module and controlling pump + MBMSK_CD_STATUS_MEASURE_ACTIVE = 0x4000, // Active conductivity measurement module and making measurements. If this bit is 1, the conditionality bar should be displayed on the screen. + MBMSK_CD_STATUS_MODULE_PRESENT = 0x8000, // Conductivity measurement module detected in the system + + // MBF_ION_STATUS + MBMSK_ION_STATUS_ON_TARGET = 0x0001, // On Target - the system has reached the set point. + MBMSK_ION_STATUS_LOW = 0x0002, // Low - Ionization cannot reach the set point. + MBMSK_ION_STATUS_RESERVED = 0x0004, + MBMSK_ION_STATUS_PROGTIME_EXCEEDED = 0x0008, // Pr off - The programmed ionization time has been exceeded + MBMSK_ION_STATUS_POLOFF = 0x1000, // Ion Pol off - Ionization in dead time + MBMSK_ION_STATUS_POL1 = 0x2000, // Ion Pol 1 - Ionization working in polarization 1 + MBMSK_ION_STATUS_POL2 = 0x4000, // Ion Pol 2 - Ionization working in polarization 2 + + // MBF_HIDRO_STATUS + MBMSK_HIDRO_STATUS_ON_TARGET = 0x0001, // On Target - the system has reached the set point. + MBMSK_HIDRO_STATUS_LOW = 0x0002, // Low - Hydrolysis cannot reach the set point. + MBMSK_HIDRO_STATUS_RESERVED = 0x0004, + MBMSK_HIDRO_STATUS_FL1 = 0x0008, // Flow - Hydrolysis cell flow indicator (FL1) + MBMSK_HIDRO_STATUS_COVER = 0x0010, // Cover - Cover input activated + MBMSK_HIDRO_STATUS_MODULE_ACTIVE = 0x0020, // Active - Active Module hydrolysis (hidroEnable) + MBMSK_HIDRO_STATUS_CTRL_ACTIVE = 0x0040, // Control - Hydrolysis module working with regulation (hydroControlEnable) + MBMSK_HIDRO_STATUS_REDOX_ENABLED = 0x0080, // Redox enable - Activation of hydrolysis by the redox module + MBMSK_HIDRO_STATUS_SHOCK_ENABLED = 0x0100, // Hydro shock enabled - Chlorine shock mode enabled + MBMSK_HIDRO_STATUS_FL2 = 0x0200, // FL2 - Chlorine probe flow indicator, if present + MBMSK_HIDRO_STATUS_ENABLED_BY_CHLORINE = 0x0400, // Cl enable - Activation of hydrolysis by the chlorine module + MBMSK_HIDRO_STATUS_POLOFF = 0x1000, // Ion Pol off - Ionization in dead time + MBMSK_HIDRO_STATUS_POL1 = 0x2000, // Ion Pol 1 - Ionization working in polarization 1 + MBMSK_HIDRO_STATUS_POL2 = 0x4000, // Ion Pol 2 - Ionization working in polarization 2 + + // MBF_RELAY_STATE + MBMSK_RELAY_STATE1 = 0x0001, // Relay 1 state (1 on; 0 off) (normally assigned to ph) + MBMSK_RELAY_STATE2 = 0x0002, // Relay 2 state (1 on; 0 off) (normally assigned to filtering) + MBMSK_RELAY_STATE3 = 0x0004, // Relay 3 status (1 on; 0 off) (normally assigned to lighting) + MBMSK_RELAY_STATE4 = 0x0008, // Relay 4 status (1 on; 0 off) + MBMSK_RELAY_STATE5 = 0x0010, // Relay 5 status (1 on; 0 off) + MBMSK_RELAY_STATE6 = 0x0020, // Relay 6 status (1 on; 0 off) + MBMSK_RELAY_STATE7 = 0x0040, // Relay 7 status (1 on; 0 off) + MBMSK_RELAY_FILTSPEED_LOW = 0x0100, // Filtration low speed + MBMSK_RELAY_FILTSPEED_MID = 0x0200, // Filtration mid speed + MBMSK_RELAY_FILTSPEED_HIGH = 0x0400, // Filtration high speed + + // MBF_NOTIFICATION + MBMSK_NOTIF_MODBUS_CHANGED = 0x0001, + MBMSK_NOTIF_GLOBAL_CHANGED = 0x0002, + MBMSK_NOTIF_FACTORY_CHANGED = 0x0004, + MBMSK_NOTIF_INSTALLER_CHANGED = 0x0008, + MBMSK_NOTIF_USER_CHANGED = 0x0010, + MBMSK_NOTIF_MISC_CHANGED = 0x0020, + + // MBF_PAR_MODEL + MBMSK_MODEL_ION = 0x0001, // The equipment includes ionization control + MBMSK_MODEL_HIDRO = 0x0002, // The equipment includes hydrolysis or electrolysis + MBMSK_MODEL_UV = 0x0004, // The equipment includes disinfection control by ultraviolet lamp + MBMSK_MODEL_SALINITY = 0x0008, // The equipment includes measurement of salinity (Fanless equipment only) + + // MBF_PAR_HIDRO_FLOW_SIGNAL + MBV_PAR_HIDRO_FLOW_SIGNAL_STD = 0, // Standard detection based on conduction between an auxiliary electrode and either of the two electrodes of the cell. + MBV_PAR_HIDRO_FLOW_SIGNAL_ALWAYS_ON = 1, // Always connected. This value allows forcing the generation of the hydrolysis current even if no flow is detected in the sensor. + MBV_PAR_HIDRO_FLOW_SIGNAL_PADDLE = 2, // Detection based on the paddle switch, associated with the FL1 input + MBV_PAR_HIDRO_FLOW_SIGNAL_PADDLE_AND_STD= 3, // Detection based on the paddle switch, associated with the FL1 input, and the standard detector. The system will understand that there is flow when both elements detect flow. If either one opens, the hydrolysis will stop. + MBV_PAR_HIDRO_FLOW_SIGNAL_PADDLE_OR_STD = 4, // Detection based on the paddle switch, associated with the FL1 input, or the standard detector. The system will understand that there is flow when either of the two elements detects flow. Hydrolysis will stop only if both detectors detect no flow. + + // MBF_PAR_HIDRO_ION_CAUDAL + MBMSK_HIDRO_ION_CAUDAL_FL1_CTRL = 0x0001, // If the FL1 signal is detected to be inactive, the actuation of the different elements of the system is disabled. + MBMSK_HIDRO_ION_CAUDAL_FL2_CTRL = 0x0002, // If the FL2 signal is detected to be inactive, the actuation of the different elements of the system is disabled. + MBMSK_HIDRO_ION_CAUDAL_FULL_CL_HIDRO_CTRL=0x0004, // If there is a chlorine module installed and it is detected that its flow sensor is inactive, the action of the different elements of the system is disabled. + MBMSK_HIDRO_ION_CAUDAL_SLAVE = 0x0008, // The value of the slave input is taken and if it is inactive, the action of the different elements of the system is disabled. + MBMSK_HIDRO_ION_CAUDAL_PADDLE_SWITCH = 0x0010, + MBMSK_HIDRO_ION_CAUDAL_PADDLE_SWITCH_INV= 0x0020, + MBMSK_HIDRO_ION_CAUDAL_INVERSION = 0x0080, // This bit determines if active means open or closed for the input electrical signals, and allows to reverse the operation for example to implement a paddle switch that closes when there is no flow. + + // MBF_PAR_FILT_MODE + MBV_PAR_FILT_MANUAL = 0, // This mode allows to turn the filtration (and all other systems that depend on it) on and off manually. + MBV_PAR_FILT_AUTO = 1, // This mode allows filtering to be turned on and off according to the settings of the TIMER1, TIMER2 and TIMER3 timers. + MBV_PAR_FILT_HEADING = 2, // This mode is similar to the AUTO mode, but includes setting the temperature for the heating function. This mode is activated only if the MBF_PAR_HEATING_MODE register is at 1 and there is a heating relay assigned. + MBV_PAR_FILT_SMART = 3, // This filtration mode adjusts the pump operating times depending on the temperature. This mode is activated only if the MBF_PAR_TEMPERATURE_ACTIVE register is at 1. + MBV_PAR_FILT_INTELLIGENT = 4, // This mode performs an intelligent filtration process in combination with the heating function. This mode is activated only if the MBF_PAR_HEATING_MODE register is at 1 and there is a heating relay assigned. + MBV_PAR_FILT_BACKWASH = 13, // This filter mode is started when the backwash operation is activated. + + // MBF_PAR_UV_MODE + MBV_PAR_UV_MODE0 = 0, // UV is switched off and it will not turn on when filtration starts + MBV_PAR_UV_MODE1 = 1, // UV is switched on and it will turn on when filtration starts. Time counter for the UV lamp will be incremented. + + // MBF_PAR_UV_HIDE_WARN + MBMSK_UV_HIDE_WARN_CLEAN = 0x0001, + MBMSK_UV_HIDE_WARN_REPLACE = 0x0002, + + // MBF_PAR_PH_PUMP_REP_TIME_ON + MBMSK_PH_PUMP_TIME = 0x7FFF, // Time level for the pump: The time level has a special coding format. It can cover periods of 1 to 180 seconds with 1 second granularity and from 3 to 999 minutes with 1 minute granularity. f the value is set to 30 for example, a 30 second time will be considered. If we have the value 200, we will have an on time of (200-180+3) = 23 minutes. + MBMSK_PH_PUMP_REPETITIVE = 0x8000, // pH pump in repetitive mode (1) + + // MBF_PAR_HIDRO_COVER_ENABLE + MBMSK_HIDRO_COVER_ENABLE = 0x0001, // If enabled, the hydrolysis/electrolysis production will be reduced by a given percentage specified in the lower half of the MBF_PAR_HIDRO_COVER_REDUCTION register when the cover input is detected. + MBMSK_HIDRO_TEMPERATURE_SHUTDOWN_ENABLE = 0x0002, // If enabled, the hydrolysis/electrolysis production will stop when the temperature falls below a given temperature threshold specified in higher part of the MBF_PAR_HIDRO_COVER_REDUCTION register. + // MBF_PAR_HIDRO_COVER_REDUCTION + MBMSK_HIDRO_COVER_REDUCTION = 0x00FF, // Percentage for the cover reduction + MBMSK_HIDRO_SHUTDOWN_TEMPERATURE = 0xFF00, // Temperature level for the hydrolysis shutdown + + // MBF_PAR_RELAY_PH + MBV_PAR_RELAY_PH_ACID_AND_BASE = 0, // The equipment works with an acid and base pump + MBV_PAR_RELAY_PH_ACID_ONLY = 1, // The equipment works with acid pump only + MBV_PAR_RELAY_PH_BASE_ONLY = 2, // The equipment works with base pump only + + //MBF_PAR_RELAY_MODE + MBMSK_PAR_RELAY_MODE_PH = 0x0003, // Behavior for the pH module (MBV_PAR_RELAY_MODE_*) + MBMSK_PAR_RELAY_MODE_RX = 0x000C, // Behavior for the Redox module (MBV_PAR_RELAY_MODE_*) + MBMSK_PAR_RELAY_MODE_CL = 0x0030, // Behavior for the Chlorine module (MBV_PAR_RELAY_MODE_*) + MBMSK_PAR_RELAY_MODE_CD = 0x00C0, // Behavior for the Conductivity module (MBV_PAR_RELAY_MODE_*) + MBMSK_PAR_RELAY_MODE_TURBIDITY = 0x0300, // Behavior for the Turbidity module (MBV_PAR_RELAY_MODE_*) + MBV_PAR_RELAY_MODE_IGNORE = 0, // The system simply ignores the alarm and dosing continues. + MBV_PAR_RELAY_MODE_SHOW_ONLY = 1, // The system only shows the alarm on screen, but the dosing continues. + MBV_PAR_RELAY_MODE_SHOW_AND_STOP = 2, // The system shows the alarm on screen and stops the dosing pump + + // MBF_PAR_FUNCTION_DEPENDENCY + MBMSK_FCTDEP_HEATING = 0x0007, // Heating function dependency: + MBMSK_DEPENDENCY_FL1_PADDLE = 0x0001, + MBMSK_DEPENDENCY_FL2 = 0x0002, + MBMSK_DEPENDENCY_SLAVE = 0x0004, + + // MBF_PAR_UICFG_MACHINE + MBV_PAR_MACH_NONE = 0, // No machine assigned + MBV_PAR_MACH_HIDROLIFE = 1, // Hidrolife (yellow) + MBV_PAR_MACH_AQUASCENIC = 2, // Aquascenic (blue) + MBV_PAR_MACH_OXILIFE = 3, // Oxilife (green) + MBV_PAR_MACH_BIONET = 4, // Bionet (light blue) + MBV_PAR_MACH_HIDRONISER = 5, // Hidroniser (red) + MBV_PAR_MACH_UVSCENIC = 6, // UVScenic (lilac) + MBV_PAR_MACH_STATION = 7, // Station (orange) + MBV_PAR_MACH_BRILIX = 8, // Brilix + MBV_PAR_MACH_GENERIC = 9, // Generic + MBV_PAR_MACH_BAYROL = 10, // Bayrol + MBV_PAR_MACH_HAY = 11, // Hay + + // MBF_PAR_UICFG_LANGUAGE + MBV_PAR_LANG_SPANISH = 0, + MBV_PAR_LANG_ENGLISH = 1, + MBV_PAR_LANG_FRENCH = 2, + MBV_PAR_LANG_GERMAN = 3, + MBV_PAR_LANG_ITALIAN = 4, + MBV_PAR_LANG_TURKISH = 5, + MBV_PAR_LANG_CZECH = 6, + MBV_PAR_LANG_PORTUGUESE = 7, + MBV_PAR_LANG_DUTCH = 8, + MBV_PAR_LANG_POLISH = 9, + MBV_PAR_LANG_HUNGARIAN = 10, + MBV_PAR_LANG_RUSSIAN = 11, + + // MBF_PAR_UICFG_BACKLIGHT + MBV_PAR_BACKLIGHT_15SEC = 0, // Backlight off after 15 sec + MBV_PAR_BACKLIGHT_30SEC = 1, // Backlight off after 30 sec + MBV_PAR_BACKLIGHT_60SEC = 2, // Backlight off after 60 sec + MBV_PAR_BACKLIGHT_5MIN = 3, // Backlight off after 5 min + MBV_PAR_BACKLIGHT_ON = 4, // Backlight never turns off + + // MBF_PAR_TIMER_BLOCK_BASE + MBV_TIMER_OFFMB_TIMER_ENABLE = 0, // Enables the timer function in the selected working mode, see MBF_PAR_CTIMER_* + MBV_TIMER_OFFMB_TIMER_ON = 1, // Timer start (32-bit timestamp, LSB first) + MBV_TIMER_OFFMB_TIMER_OFF = 3, // Timer stop (32-bit timestamp, LSB first) - not used + MBV_TIMER_OFFMB_TIMER_PERIOD = 5, // Time in seconds between starting points (32-bit, LSB first), e.g. 86400 means daily + MBV_TIMER_OFFMB_TIMER_INTERVAL = 7, // Time in seconds that the timer has to run when started (32-bit, LSB first) + MBV_TIMER_OFFMB_TIMER_COUNTDOWN = 9, // Time remaining in seconds for the countdown mode (32-bit, LSB first) + MBV_TIMER_OFFMB_TIMER_FUNCTION = 11, // Function assigned to this timer, see + MBV_TIMER_OFFMB_TIMER_WORK_TIME = 13, // Number of seconds that the timer has been operating + // MBV_TIMER_OFFMB_TIMER_ENABLE working modes: + MBV_PAR_CTIMER_DISABLE = 0, // Timer disabled + MBV_PAR_CTIMER_ENABLED = 1, // Timer enabled and independent + MBV_PAR_CTIMER_ENABLED_LINKED = 2, // Timer enabled and linked to relay from timer 0 + MBV_PAR_CTIMER_ALWAYS_ON = 3, // Relay assigned to this timer always on + MBV_PAR_CTIMER_ALWAYS_OFF = 4, // Relay assigned to this timer always off + MBV_PAR_CTIMER_COUNTDOWN = 5, // Timer in countdown mode + // MBV_TIMER_OFFMB_TIMER_FUNCTION codes: + MBV_PAR_CTIMER_FCT_FILTRATION = 0x0001, // Filtration function of the system + MBV_PAR_CTIMER_FCT_LIGHTING = 0x0002, // Lighting function of the system + MBV_PAR_CTIMER_FCT_HEATING = 0x0004, // Heating function of the system + MBV_PAR_CTIMER_FCT_AUXREL1 = 0x0100, // Auxiliary function assigned to relay 1 + MBV_PAR_CTIMER_FCT_AUXREL2 = 0x0200, // Auxiliary function assigned to relay 2 + MBV_PAR_CTIMER_FCT_AUXREL3 = 0x0400, // Auxiliary function assigned to relay 3 + MBV_PAR_CTIMER_FCT_AUXREL4 = 0x0800, // Auxiliary function assigned to relay 4 + MBV_PAR_CTIMER_FCT_AUXREL5 = 0x1000, // Auxiliary function assigned to relay 5 + MBV_PAR_CTIMER_FCT_AUXREL6 = 0x2000, // Auxiliary function assigned to relay 6 + MBV_PAR_CTIMER_FCT_AUXREL7 = 0x4000, // Auxiliary function assigned to relay 7 + + // MBF_PAR_UICFG_SOUND + MBMSK_PAR_SOUND_CLICK = 0x0001, // Click sounds every time a key is pressed + MBMSK_PAR_SOUND_POPUP = 0x0002, // Sound plays each time a pop-up message appears + MBMSK_PAR_SOUND_ALERTS = 0x0004, // An alarm sounds when there is an alert on the equipment (AL3) + MBMSK_PAR_SOUND_FILTRATION = 0x0008, // Audible warning every time the filtration is started + + // MBF_PAR_UICFG_VISUAL_OPTIONS + MBMSK_HIDE_TEMPERATURE = 0x0001, // Hide temperature measurement from main menu + MBMSK_HIDE_FILTRATION = 0x0002, // Hide filter status from main menu + MBMSK_HIDE_LIGHTING = 0x0004, // Hide lighting status from main menu + MBMSK_HIDE_AUX_RELAYS = 0x0008, // Hide auxiliary relay status from main menu. + MBMSK_VO_HIDE_EXTRA_REGS = 0x0010, // Hide the option to adjust additional registers in the installer menu + MBMSK_VO_HIDE_RELAY_CONFIG = 0x0020, // Hide the relay configuration option in the installer menu. + MBMSK_VO_SLOW_FILTER_HIDRO_LEVEL = 0x0040, // This option enables the slow hydrolysis level filtering option when the pH module is installed. This is especially important when the acid / base dosing is done very close to the hydrolysis probe. + MBMSK_VO_HIDE_SALINITY_MAIN_WINDOW = 0x0080, // Hides the salinity measurement from main screen. + MBMSK_VO_SHOW_SPECIAL_REGS = 0x0100, // Displays the special register set configuration menu in the installer menu. + MBMSK_SHOW_HID_SHUTDOWN_BY_TEMPERATURE = 0x0200, // Displays the option to turn off hydrolysis by temperature. + MBMSK_SHOW_CELL_SELECTION = 0x0400, // Enables access to the cell selection menu from the service menu option of the configuration menu. + MBMSK_SHOW_PUMP_TYPE = 0x0800, // Displays the option for selecting the type of filtration pump (normal, three speeds, etc.). + MBMSK_SHOW_QUICK_MENU = 0x1000, // Displays the quick access menu instead of the conventional menu, when the SET key is pressed from the main display screen. Filtration (normal, three speeds, etc). + MBMSK_SHOW_OXI_MAIN_DATA_SCREEN = 0x2000, // Displays main screen shown with a particular style called OXI + MBMSK_SHOW_INSTALLER_MENU = 0x4000, // Shows access to the installer menu in the main menu without the need for a password. + MBMSK_SHOW_FACTORY_MENU = 0x8000, // Shows access to the factory menu in the main menu without the need for a password. + + // MBF_PAR_UICFG_VISUAL_OPTIONS_EXT + MBMSK_VOE_SHOW_PNEUMATIC_VALVE = 0x0001, // Shows the pneumatic valve + MBMSK_VOE_HIDE_AUX_REL_DEPENDENCY = 0x0002, // Hides the auxiliary relay dependency + MBMSK_VOE_SHOW_BESGO_NAME = 0x0004, // Show “Besgo” instead of “Pneumatic” for the pneumatic valve titles. + + // MBF_PAR_UICFG_MACH_VISUAL_STYLE + MBMSK_VS_FORCE_UNITS_GRH = 0x2000, // Display the hydrolysis/electrolysis in units of grams per hour (gr/h). + MBMSK_VS_FORCE_UNITS_PERCENTAGE = 0x4000, // Display the hydrolysis/electrolysis in percentage units (%). + MBMSK_ELECTROLISIS = 0x8000, // Display the word electrolysis instead of hydrolysis in generic mode. +}; + +#include +TasmotaModbus *NeoPoolModbus; + +bool neopool_active = false; +volatile bool neopool_poll = true; + +uint8_t neopool_read_state = 0; +uint8_t neopool_send_retry = 0; +uint8_t neopool_failed = 0; +uint8_t neopool_failed_count = 0; +#ifdef NEOPOOL_OPTIMIZE_READINGS +bool neopool_first_read = true; +#endif // NEOPOOL_OPTIMIZE_READINGS +bool neopool_error = true; + +// Modbus register set to read +struct NEOPOOL_REG { + const uint16_t addr; + const uint16_t cnt; + uint16_t *data; +} NeoPoolReg[] = { + {MBF_ION_CURRENT, MBF_NOTIFICATION - MBF_ION_CURRENT + 1, nullptr}, + {MBF_PAR_VERSION, MBF_PAR_MODEL - MBF_PAR_VERSION + 1, nullptr}, + {MBF_PAR_TIME_LOW, MBF_PAR_FILT_GPIO - MBF_PAR_TIME_LOW + 1, nullptr}, + {MBF_PAR_UICFG_MACHINE, MBF_PAR_UICFG_MACHINE - MBF_PAR_UICFG_MACHINE + 1, nullptr} +}; + +// NeoPool modbus function errors +#define NEOPOOL_OK 0 +#define NEOPOOL_ERROR_RW_DATA 1 +#define NEOPOOL_ERROR_TIMEOUT 2 +#define NEOPOOL_ERROR_OUT_OF_MEM 3 +#define NEOPOOL_ERROR_DEADLOCK 4 + + +#define D_NEOPOOL_NAME "NeoPool" + +const char kNeoPoolMachineNames[] PROGMEM = + D_NEOPOOL_MACH_NONE "|" + D_NEOPOOL_MACH_HIDROLIFE "|" + D_NEOPOOL_MACH_AQUASCENIC "|" + D_NEOPOOL_MACH_OXILIFE "|" + D_NEOPOOL_MACH_BIONET "|" + D_NEOPOOL_MACH_HIDRONISER "|" + D_NEOPOOL_MACH_UVSCENIC "|" + D_NEOPOOL_MACH_STATION "|" + D_NEOPOOL_MACH_BRILIX "|" + D_NEOPOOL_MACH_GENERIC "|" + D_NEOPOOL_MACH_BAYROL "|" + D_NEOPOOL_MACH_HAY + ; + +const char kNeoPoolFiltrationMode[] PROGMEM = + D_NEOPOOL_FILTRATION_MANUAL "|" + D_NEOPOOL_FILTRATION_AUTO "|" + D_NEOPOOL_FILTRATION_HEATING "|" + D_NEOPOOL_FILTRATION_SMART "|" + D_NEOPOOL_FILTRATION_INTELLIGENT "|" + D_NEOPOOL_FILTRATION_BACKWASH + ; + +const char kNeoPoolFiltrationSpeed[] PROGMEM = + D_NEOPOOL_FILTRATION_NONE "|" + D_NEOPOOL_FILTRATION_SLOW "|" + D_NEOPOOL_FILTRATION_MEDIUM "|" + D_NEOPOOL_FILTRATION_FAST + ; + +const char kNeoPoolpHAlarms[] PROGMEM = + D_NEOPOOL_SETPOINT_OK "|" + D_NEOPOOL_PH_HIGH "|" + D_NEOPOOL_PH_LOW "|" + D_NEOPOOL_PUMP_TIME_EXCEEDED + ; + +#ifndef D_STR_BIT +#define D_STR_BIT "Bit" +#endif // D_STR_BIT + +const char HTTP_SNS_NEOPOOL_PH[] PROGMEM = "{s}%s " D_PH "{m}%s " "%s%s" "{e}"; +const char HTTP_SNS_NEOPOOL_TIME[] PROGMEM = "{s}%s " D_NEOPOOL_TIME "{m}%s" "{e}"; +const char HTTP_SNS_NEOPOOL_PPM_REDOX[] PROGMEM = "{s}%s " D_NEOPOOL_REDOX "{m}%s " D_UNIT_PARTS_PER_MILLION "{e}"; +const char HTTP_SNS_NEOPOOL_PPM_CHLORINE[] PROGMEM = "{s}%s " D_NEOPOOL_CHLORINE "{m}%s " D_UNIT_PARTS_PER_MILLION "{e}"; +const char HTTP_SNS_NEOPOOL_CONDUCTIVITY[] PROGMEM = "{s}%s " D_NEOPOOL_CONDUCTIVITY "{m}%s " D_UNIT_PERCENT "{e}"; +const char HTTP_SNS_NEOPOOL_IONIZATION[] PROGMEM = "{s}%s " D_NEOPOOL_IONIZATION "{m}%s " "%s%s" "{e}"; +const char HTTP_SNS_NEOPOOL_HYDROLYSIS[] PROGMEM = "{s}%s " D_NEOPOOL_HYDROLYSIS "{m}%s " "%s%s" "{e}"; +const char HTTP_SNS_NEOPOOL_FILT_MODE[] PROGMEM = "{s}%s " D_NEOPOOL_FILT_MODE "{m}%s" "{e}"; +const char HTTP_SNS_NEOPOOL_RELAY[] PROGMEM = "{s}%s " D_NEOPOOL_RELAY " %d %s" "{m}%s" "{e}"; + + +void NeoPool250ms(void) // Every 250 mSec +{ + if (!neopool_poll) { + return; + }; + + bool data_ready = NeoPoolModbus->ReceiveReady(); + + if (data_ready && nullptr != NeoPoolReg[neopool_read_state].data) { + uint8_t *buffer = (uint8_t *)malloc(5+(NeoPoolReg[neopool_read_state].cnt)*2); + + if( nullptr != buffer) { + uint8_t error = NeoPoolModbus->ReceiveBuffer(buffer, NeoPoolReg[neopool_read_state].cnt); // cnt x 16bit register + + if (0 == error) { + neopool_failed_count = 0; + neopool_error = false; + for(uint32_t i=0; i> 8)-1))) { +#endif // NEOPOOL_OPTIMIZE_READINGS +#ifdef DEBUG_TASMOTA_SENSOR + AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("NEO: modbus send(%d, %d, 0x%04X, %d)"), NEOPOOL_MODBUS_ADDRESS, NEOPOOL_READ_REGISTER, NeoPoolReg[neopool_read_state].addr, NeoPoolReg[neopool_read_state].cnt); +#endif // DEBUG_TASMOTA_SENSOR + NeoPoolModbus->Send(NEOPOOL_MODBUS_ADDRESS, NEOPOOL_READ_REGISTER, NeoPoolReg[neopool_read_state].addr, NeoPoolReg[neopool_read_state].cnt); +#ifdef NEOPOOL_OPTIMIZE_READINGS + } + else { + // search next addr block having notification + while( (NeoPoolReg[neopool_read_state].addr & 0x0F00) != 0x100 || (NeoPoolGetData(MBF_NOTIFICATION) & (1 << (NeoPoolReg[neopool_read_state].addr >> 8)-1)) ) { +#ifdef DEBUG_TASMOTA_SENSOR + AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("NEO: notify 0x%04X - addr block 0x%04X ignored"), NeoPoolGetData(MBF_NOTIFICATION), NeoPoolReg[neopool_read_state].addr); +#endif // DEBUG_TASMOTA_SENSOR + ++neopool_read_state %= ARRAY_SIZE(NeoPoolReg); + // neopool_read_state++; + // if (ARRAY_SIZE(NeoPoolReg) <= neopool_read_state) { + // neopool_read_state = 0; + // } + } + } +#endif // NEOPOOL_OPTIMIZE_READINGS + } + else { + if (1 == neopool_send_retry) { + neopool_failed_count++; + if (neopool_failed_count > 2) { + neopool_failed_count = 0; + neopool_error = true; + NeoPoolInitData(); + } + } + neopool_send_retry--; + } + } +} + +/*********************************************************************************************/ + +void NeoPoolInit(void) { + neopool_active = false; + if (PinUsed(GPIO_NEOPOOL_RX) && PinUsed(GPIO_NEOPOOL_TX)) { + NeoPoolModbus = new TasmotaModbus(Pin(GPIO_NEOPOOL_RX), Pin(GPIO_NEOPOOL_TX)); + uint8_t result = NeoPoolModbus->Begin(NEOPOOL_MODBUS_SPEED); + if (result) { + if (2 == result) { + ClaimSerial(); + } +#ifdef NEOPOOL_OPTIMIZE_READINGS + neopool_first_read = true; +#endif // NEOPOOL_OPTIMIZE_READINGS + if (NeoPoolInitData()) { // Claims heap space + neopool_active = true; + } + } + } +} + +bool NeoPoolInitData(void) +{ + bool res = false; + + neopool_error = true; + for(uint32_t i=0; iSend(NEOPOOL_MODBUS_ADDRESS, NEOPOOL_READ_REGISTER, addr, cnt); + timeoutMS = millis() + cnt * NEOPOOL_READ_TIMEOUT; // Max delay before we timeout + while (!(data_ready = NeoPoolModbus->ReceiveReady()) && millis() < timeoutMS) { delay(1); } + if (data_ready) { + uint8_t *buffer = (uint8_t*)malloc(5+cnt*2); + if (buffer != nullptr) { + uint8_t error = NeoPoolModbus->ReceiveBuffer(buffer, cnt); + if (error) { +#ifdef DEBUG_TASMOTA_SENSOR + AddLog(LOG_LEVEL_DEBUG, PSTR("NEO: addr 0x%04X read data error %d"), addr, error); +#endif // DEBUG_TASMOTA_SENSOR + neopool_poll = true; + return NEOPOOL_ERROR_RW_DATA; + } + for(uint32_t i=0; i> 8); // addr MSB + frame[3] = (uint8_t)(addr); // addr LSB + frame[4] = (uint8_t)(cnt >> 8); // register quantity MSB + frame[5] = (uint8_t)(cnt); // register quantity LSB + frame[6] = (uint8_t)(cnt*2); // byte count + for(uint32_t i=0; i> 8); // data MSB + frame[8+i*2] = (uint8_t)(data[i]); // data LSB + } + uint16_t crc = NeoPoolModbus->CalculateCRC(frame, numbytes); + frame[numbytes] = (uint8_t)(crc); + frame[numbytes+1] = (uint8_t)(crc >> 8); + + NeoPoolModbus->flush(); + NeoPoolModbus->write(frame, numbytes+2); + free(frame); + + timeoutMS = millis() + 1 * NEOPOOL_READ_TIMEOUT; // Max delay before we timeout + while (!(data_ready = NeoPoolModbus->ReceiveReady()) && millis() < timeoutMS) { delay(1); } + if (data_ready) { + uint8_t buffer[9]; + uint8_t error = NeoPoolModbus->ReceiveBuffer(buffer, 1); + if (0!=error && 9!=error) { // ReceiveBuffer can't handle 0x10 code result +#ifdef DEBUG_TASMOTA_SENSOR + AddLog(LOG_LEVEL_DEBUG, PSTR("NEO: addr 0x%04X write data response error %d"), addr, error); +#endif // DEBUG_TASMOTA_SENSOR + neopool_poll = true; + return NEOPOOL_ERROR_RW_DATA; + } + if (9==error) { + // clear buffer before we leave + while (NeoPoolModbus->available()) { + NeoPoolModbus->read(); + } + } + neopool_poll = true; + // delay(2); + return NEOPOOL_OK; + } +#ifdef DEBUG_TASMOTA_SENSOR + AddLog(LOG_LEVEL_DEBUG, PSTR("NEO: addr 0x%04X write data response timeout"), addr); +#endif // DEBUG_TASMOTA_SENSOR + neopool_poll = true; + return NEOPOOL_ERROR_TIMEOUT; +} + +uint16_t NeoPoolGetData(uint16_t addr) +{ + for(uint32_t i=0; i= NeoPoolReg[i].addr && addr < NeoPoolReg[i].addr+NeoPoolReg[i].cnt) { + return NeoPoolReg[i].data[addr - NeoPoolReg[i].addr]; + } + } + return 0; +} + +uint32_t NeoPoolGetSpeedIndex(uint16_t speedvalue) +{ + if (speedvalue>=4) { + return 3; + } + if (speedvalue>=2) { + return 2; + } + if (speedvalue>=1) { + return 1; + } + return 0; +} + +void NeoPoolShow(bool json) +{ + char parameter[FLOATSZ]; + char *neopool_type; + char stemp[128]; + char smachine[32]; + + if (neopool_error) { + return; + } + + neopool_type = GetTextIndexed(smachine, sizeof(smachine), NeoPoolGetData(MBF_PAR_UICFG_MACHINE), kNeoPoolMachineNames); + + if (json) { + char delimiter[] = {'\0','\0'}; + + ResponseAppend_P(PSTR(",\"" D_NEOPOOL_NAME "\":{")); + +#ifndef NEOPOOL_OPTIMIZE_READINGS + // Time + ResponseAppend_P(PSTR("%s\"" D_NEOPOOL_TIME "\":\"%s\""), delimiter, + GetDT((uint32_t)NeoPoolGetData(MBF_PAR_TIME_LOW) + ((uint32_t)NeoPoolGetData(MBF_PAR_TIME_HIGH) << 16)).c_str()); + *delimiter = ','; +#endif // NEOPOOL_OPTIMIZE_READINGS + + // TODO: Add alarm infos + + // Type + ResponseAppend_P(PSTR("%s\"" D_NEOPOOL_TYPE "\":\"%s\""), delimiter, neopool_type); + *delimiter = ','; + + // Temperature + if (NeoPoolGetData(MBF_PAR_TEMPERATURE_ACTIVE)) { +/* + dtostrfd(Settings.flag.temperature_conversion ? + (float)NeoPoolGetData(MBF_MEASURE_TEMPERATURE)/10 * 1.8 + 32 : + (float)NeoPoolGetData(MBF_MEASURE_TEMPERATURE)/10, Settings.flag2.temperature_resolution, parameter); + ResponseAppend_P(PSTR("%s\"" D_TEMPERATURE "\":%s"), delimiter, parameter); +*/ + float temp = ConvertTemp((float)NeoPoolGetData(MBF_MEASURE_TEMPERATURE) / 10); + ResponseAppend_P(PSTR("%s\"" D_TEMPERATURE "\":%*_f"), delimiter, Settings.flag2.temperature_resolution, &temp); + *delimiter = ','; + } + + // pH + if (NeoPoolGetData(MBF_PH_STATUS) & MBMSK_PH_STATUS_MEASURE_ACTIVE) { + dtostrfd((float)NeoPoolGetData(MBF_MEASURE_PH)/100, 2, parameter); + ResponseAppend_P(PSTR("%s\"" D_PH """\":%s"), delimiter, parameter); + *delimiter = ','; + } + + // Redox + if (NeoPoolGetData(MBF_RX_STATUS) & MBMSK_RX_STATUS_MEASURE_ACTIVE) { + dtostrfd((float)NeoPoolGetData(MBF_MEASURE_RX)/100, 2, parameter); + ResponseAppend_P(PSTR("%s\"" D_NEOPOOL_REDOX "\":%s"), delimiter, parameter); + *delimiter = ','; + } + + // Chlorine + if (NeoPoolGetData(MBF_CL_STATUS) & MBMSK_CL_STATUS_MEASURE_ACTIVE) { + dtostrfd((float)NeoPoolGetData(MBF_MEASURE_CL)/100, 2, parameter); + ResponseAppend_P(PSTR("%s\"" D_NEOPOOL_CHLORINE "\":%s"), delimiter, parameter); + } + + // Conductivity + if (NeoPoolGetData(MBF_CD_STATUS) & MBMSK_CD_STATUS_MEASURE_ACTIVE) { + ResponseAppend_P(PSTR("%s\"" D_NEOPOOL_CONDUCTIVITY "\":%d"), delimiter, NeoPoolGetData(MBF_MEASURE_CONDUCTIVITY)); + } + + // Ionization + if (NeoPoolGetData(MBF_PAR_MODEL) & MBMSK_MODEL_ION) { + dtostrfd((float)NeoPoolGetData(MBF_ION_CURRENT), 1, parameter); + ResponseAppend_P(PSTR("%s\"" D_NEOPOOL_IONIZATION "\":%s"), delimiter, parameter); + } + + // Hydrolysis + if ((NeoPoolGetData(MBF_PAR_MODEL) & MBMSK_MODEL_HIDRO) && (NeoPoolGetData(MBF_HIDRO_STATUS) & MBMSK_HIDRO_STATUS_MODULE_ACTIVE)) { + dtostrfd((float)NeoPoolGetData(MBF_HIDRO_CURRENT), 1, parameter); + ResponseAppend_P(PSTR("%s\"" D_NEOPOOL_HYDROLYSIS "\":%s"), delimiter, parameter); + } + + // Filtration + ResponseAppend_P(PSTR("%s\"" D_NEOPOOL_RELAY_FILTRATION "\":%d"), delimiter, (NeoPoolGetData(MBF_RELAY_STATE) >>1) & 0x02); + ResponseAppend_P(PSTR("%s\"" D_NEOPOOL_RELAY_FILTRATION D_JSON_SPEED "\":%d"), delimiter, + (NeoPoolGetData(MBF_RELAY_STATE) >> 8) & 0x7 + ); + + // Light + ResponseAppend_P(PSTR("%s\"" D_NEOPOOL_RELAY_LIGHT "\":%d"), delimiter, (NeoPoolGetData(MBF_RELAY_STATE) >>2) & 0x01); + + // Relays + ResponseAppend_P(PSTR("%s\"" D_NEOPOOL_RELAY "\":%d"), delimiter, NeoPoolGetData(MBF_RELAY_STATE)); + + ResponseJsonEnd(); + +#ifdef USE_WEBSERVER + } + else { +#ifndef NEOPOOL_OPTIMIZE_READINGS + // Time + char dt[20]; + TIME_T tmpTime; + BreakTime((uint32_t)NeoPoolGetData(MBF_PAR_TIME_LOW) + ((uint32_t)NeoPoolGetData(MBF_PAR_TIME_HIGH) << 16), tmpTime); + snprintf_P(dt, sizeof(dt), PSTR("%04d-%02d-%02d %02d:%02d"), + tmpTime.year +1970, tmpTime.month, tmpTime.day_of_month, tmpTime.hour, tmpTime.minute); + WSContentSend_PD(HTTP_SNS_NEOPOOL_TIME, neopool_type, dt); +#endif // NEOPOOL_OPTIMIZE_READINGS + + // Temperature + if (NeoPoolGetData(MBF_PAR_TEMPERATURE_ACTIVE)) { +/* + dtostrfd(Settings.flag.temperature_conversion?(float)NeoPoolGetData(MBF_MEASURE_TEMPERATURE)/10 * 1.8 + 32:(float)NeoPoolGetData(MBF_MEASURE_TEMPERATURE)/10, Settings.flag2.temperature_resolution, parameter); + WSContentSend_PD(HTTP_SNS_TEMP, neopool_type, parameter, TempUnit()); +*/ + float temp = ConvertTemp((float)NeoPoolGetData(MBF_MEASURE_TEMPERATURE) / 10); + WSContentSend_Temp(neopool_type, temp); + } + + // pH + if (NeoPoolGetData(MBF_PH_STATUS) & MBMSK_PH_STATUS_MEASURE_ACTIVE) { + dtostrfd((float)NeoPoolGetData(MBF_MEASURE_PH)/100, 2, parameter); + *stemp = 0; + if ((NeoPoolGetData(MBF_PH_STATUS) & MBMSK_PH_STATUS_ALARM) >=1 && (NeoPoolGetData(MBF_PH_STATUS) & MBMSK_PH_STATUS_ALARM) <= 3) { + GetTextIndexed(stemp, sizeof(stemp), NeoPoolGetData(MBF_PH_STATUS) & MBMSK_PH_STATUS_ALARM, kNeoPoolpHAlarms); + } + WSContentSend_PD(HTTP_SNS_NEOPOOL_PH, neopool_type, parameter, *stemp ? PSTR(" " D_NEOPOOL_ALARM) : PSTR(""), stemp); + } + + // Redox + if (NeoPoolGetData(MBF_RX_STATUS) & MBMSK_RX_STATUS_MEASURE_ACTIVE) { + dtostrfd((float)NeoPoolGetData(MBF_MEASURE_RX)/100, 2, parameter); + WSContentSend_PD(HTTP_SNS_NEOPOOL_PPM_REDOX, neopool_type, parameter); + } + + // Chlorine + if (NeoPoolGetData(MBF_CL_STATUS) & MBMSK_CL_STATUS_MEASURE_ACTIVE) { + dtostrfd((float)NeoPoolGetData(MBF_MEASURE_CL)/100, 2, parameter); + WSContentSend_PD(HTTP_SNS_NEOPOOL_PPM_CHLORINE, neopool_type, parameter); + } + + // Conductivity + if (NeoPoolGetData(MBF_CD_STATUS) & MBMSK_CD_STATUS_MEASURE_ACTIVE) { + dtostrfd((float)NeoPoolGetData(MBF_MEASURE_CONDUCTIVITY), 0, parameter); + WSContentSend_PD(HTTP_SNS_NEOPOOL_PPM_CHLORINE, neopool_type, parameter); + } + + // Ionization + if (NeoPoolGetData(MBF_PAR_MODEL) & MBMSK_MODEL_ION) { + dtostrfd((float)NeoPoolGetData(MBF_ION_CURRENT), 1, parameter); + char spol[32]; + sprintf_P(spol,PSTR(" " D_NEOPOOL_POLARIZATION "%d"),NeoPoolGetData(MBF_ION_STATUS)>>13); + sprintf_P(stemp, PSTR("%s%s%s"), + NeoPoolGetData(MBF_ION_STATUS)>>13?spol:PSTR(""), + NeoPoolGetData(MBF_ION_STATUS) & MBMSK_ION_STATUS_ON_TARGET ? PSTR(" " D_NEOPOOL_SETPOINT_OK) : PSTR(""), + NeoPoolGetData(MBF_ION_STATUS) & MBMSK_ION_STATUS_PROGTIME_EXCEEDED ? PSTR(" " D_NEOPOOL_PR_OFF) : PSTR("") + ); + WSContentSend_PD(HTTP_SNS_NEOPOOL_IONIZATION, neopool_type, parameter, NeoPoolGetData(MBF_ION_STATUS)>>13, NeoPoolGetData(MBF_ION_STATUS)&0x0002?" Low":""); + } + + // Hydrolysis +#ifdef DEBUG_TASMOTA_SENSOR + //AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("NEO: MBF_PAR_MODEL 0x%04X MBF_HIDRO_STATUS 0x%04X"), NeoPoolGetData(MBF_PAR_MODEL), NeoPoolGetData(MBF_HIDRO_STATUS)); +#endif // DEBUG_TASMOTA_SENSOR + if ((NeoPoolGetData(MBF_PAR_MODEL) & MBMSK_MODEL_HIDRO) && (NeoPoolGetData(MBF_HIDRO_STATUS) & MBMSK_HIDRO_STATUS_MODULE_ACTIVE)) { + dtostrfd((float)NeoPoolGetData(MBF_HIDRO_CURRENT), 1, parameter); + char spol[32]; + sprintf_P(spol,PSTR(" " D_NEOPOOL_POLARIZATION "%d"),NeoPoolGetData(MBF_HIDRO_STATUS)>>13); + sprintf_P(stemp, PSTR("%s%s%s%s%s%s%s%s"), + NeoPoolGetData(MBF_HIDRO_STATUS)>>13?spol:PSTR(""), + NeoPoolGetData(MBF_HIDRO_STATUS) & MBMSK_HIDRO_STATUS_ON_TARGET ? PSTR(" " D_NEOPOOL_SETPOINT_OK) : PSTR(""), + NeoPoolGetData(MBF_HIDRO_STATUS) & MBMSK_HIDRO_STATUS_COVER ? PSTR(" " D_NEOPOOL_COVER) : PSTR(""), + NeoPoolGetData(MBF_HIDRO_STATUS) & MBMSK_HIDRO_STATUS_SHOCK_ENABLED ? PSTR(" " D_NEOPOOL_SHOCK) : PSTR(""), + (NeoPoolGetData(MBF_HIDRO_STATUS) ^(MBMSK_HIDRO_STATUS_FL1|MBMSK_HIDRO_STATUS_FL2)) & (MBMSK_HIDRO_STATUS_LOW|MBMSK_HIDRO_STATUS_FL1|MBMSK_HIDRO_STATUS_FL2) ? PSTR(" " D_NEOPOOL_ALARM) : PSTR(""), + NeoPoolGetData(MBF_HIDRO_STATUS) & MBMSK_HIDRO_STATUS_LOW ? PSTR(" " D_NEOPOOL_LOW) : PSTR(""), + !(NeoPoolGetData(MBF_HIDRO_STATUS) & MBMSK_HIDRO_STATUS_FL1) ? PSTR(" " D_NEOPOOL_FLOW1) : PSTR(""), + !(NeoPoolGetData(MBF_HIDRO_STATUS) & MBMSK_HIDRO_STATUS_FL2) ? PSTR(" " D_NEOPOOL_FLOW2) : PSTR("") + ); + WSContentSend_PD(HTTP_SNS_NEOPOOL_HYDROLYSIS, neopool_type, parameter, *stemp ? PSTR("/") : PSTR(""), stemp); + } + GetTextIndexed(stemp, sizeof(stemp), NeoPoolGetData(MBF_PAR_FILT_MODE) < MBV_PAR_FILT_INTELLIGENT ? NeoPoolGetData(MBF_PAR_FILT_MODE) : 5, kNeoPoolFiltrationMode); + WSContentSend_PD(HTTP_SNS_NEOPOOL_FILT_MODE, neopool_type, stemp); + + // Relays + for(uint32_t i=0; i<8; i++) { + char sdesc[24]; + memset(sdesc, 0, ARRAY_SIZE(sdesc)); + memset(stemp, 0, ARRAY_SIZE(stemp)); + if (0 != NeoPoolGetData(MBF_PAR_PH_ACID_RELAY_GPIO) && i == NeoPoolGetData(MBF_PAR_PH_ACID_RELAY_GPIO)-1) { + strncpy_P(sdesc, PSTR(D_NEOPOOL_RELAY_PH_ACID), sizeof(sdesc)); + } + else if (0 != NeoPoolGetData(MBF_PAR_PH_BASE_RELAY_GPIO) && i == NeoPoolGetData(MBF_PAR_PH_BASE_RELAY_GPIO)-1) { + strncpy_P(sdesc, PSTR(D_NEOPOOL_RELAY_PH_BASE), sizeof(sdesc)); + } + else if (0 != NeoPoolGetData(MBF_PAR_RX_RELAY_GPIO) && i == NeoPoolGetData(MBF_PAR_RX_RELAY_GPIO)-1) { + strncpy_P(sdesc, PSTR(D_NEOPOOL_RELAY_RX), sizeof(sdesc)); + } + else if (0 != NeoPoolGetData(MBF_PAR_CL_RELAY_GPIO) && i == NeoPoolGetData(MBF_PAR_CL_RELAY_GPIO)-1) { + strncpy_P(sdesc, PSTR(D_NEOPOOL_RELAY_CL), sizeof(sdesc)); + } + else if (0 != NeoPoolGetData(MBF_PAR_CD_RELAY_GPIO) && i == NeoPoolGetData(MBF_PAR_CD_RELAY_GPIO)-1) { + strncpy_P(sdesc, PSTR(D_NEOPOOL_RELAY_CD), sizeof(sdesc)); + } + else if (0 != NeoPoolGetData(MBF_PAR_FILT_GPIO) && i == NeoPoolGetData(MBF_PAR_FILT_GPIO)-1) { + char smotorspeed[32]; + // Filtration + strncpy_P(sdesc, PSTR(D_NEOPOOL_RELAY_FILTRATION), sizeof(sdesc)); + GetTextIndexed(smotorspeed, sizeof(smotorspeed), NeoPoolGetSpeedIndex((NeoPoolGetData(MBF_RELAY_STATE) >> 8) & 0x7), kNeoPoolFiltrationSpeed); + sprintf_P(stemp, PSTR("%s%s%s%s"), ((NeoPoolGetData(MBF_RELAY_STATE) & (1< () + * read 16-bit register (cnt=1..30, cnt=1 if omitted) + * + * Sensor83 2 () + * read/write register bit (bit=0..15, data=0|1) + * read if param is omitted otherwise set to new + * + * Sensor83 3 (...) + * write 16-bit register (data=0..65535, max 8 times) + * + * Sensor83 4 () + * get/set manual filtration (state=0|1) + * get filtration state if param is omitted otherwise set new state + * + * Sensor83 5 () + * get/set filtration mode (mode=0..4|13) + * get mode if param is omitted otherwise set new mode + * + * Sensor83 6 (