mirror of
https://github.com/arendst/Tasmota.git
synced 2025-07-24 11:16:34 +00:00
Merge branch 'development' into pre-release
This commit is contained in:
commit
0404adf4a2
2
.github/PULL_REQUEST_TEMPLATE.md
vendored
2
.github/PULL_REQUEST_TEMPLATE.md
vendored
@ -6,7 +6,7 @@
|
||||
- [ ] The pull request is done against the latest dev branch
|
||||
- [ ] Only relevant files were touched
|
||||
- [ ] Only one feature/fix was added per PR.
|
||||
- [ ] The code change is tested and works on core ESP8266 V.2.7.0
|
||||
- [ ] The code change is tested and works on core ESP8266 V.2.7.1
|
||||
- [ ] The code change is tested and works on core ESP32 V.1.12.0
|
||||
- [ ] I accept the [CLA](https://github.com/arendst/Tasmota/blob/development/CONTRIBUTING.md#contributor-license-agreement-cla).
|
||||
|
||||
|
@ -56,7 +56,8 @@ The following binary downloads have been compiled with ESP8266/Arduino library c
|
||||
|
||||
- Breaking Change Device Groups multicast address and port (#8270)
|
||||
- Change PWM implementation to Arduino #7231 removing support for Core versions before 2.6.3
|
||||
- Change default PWM Frequency to 223 Hz instead of 880 Hz for less interrupt pressure
|
||||
- Change default PWM Frequency to 977 Hz from 880 Hz
|
||||
- Change minimum PWM Frequency from 100 Hz to 40 Hz
|
||||
- Change flash access removing support for any Core before 2.6.3
|
||||
- Change HM-10 sensor type detection and add features (#7962)
|
||||
- Change light scheme 2,3,4 cycle time speed from 24,48,72,... seconds to 4,6,12,24,36,48,... seconds (#8034)
|
||||
@ -100,4 +101,7 @@ The following binary downloads have been compiled with ESP8266/Arduino library c
|
||||
- Add more accuracy to GPS NTP server (#8088)
|
||||
- Add support for analog anemometer by Matteo Albinola (#8283)
|
||||
- Add support for OpenTherm by Yuriy Sannikov (#8373)
|
||||
- Add support for Thermostat control by arijav (#8212)
|
||||
- Add experimental basic support for Tasmota on ESP32 based on work by Jörg Schüler-Maroldt
|
||||
- Add automatic compression of Rules to achieve ~60% compression, added ``SetOption93 1`` to control caching of rules
|
||||
- Add rule trigger at root level like ``on loadavg<50 do power 2 endon`` after ``state`` command
|
||||
|
165
lib/Unishox-1.0-shadinger/generator/generator.c
Normal file
165
lib/Unishox-1.0-shadinger/generator/generator.c
Normal file
@ -0,0 +1,165 @@
|
||||
/*
|
||||
* Copyright (C) 2019 Siara Logics (cc)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
* @author Arundale R.
|
||||
*
|
||||
*/
|
||||
|
||||
// Pre-compute c_95[] and l_95[]
|
||||
|
||||
#include <time.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <ctype.h>
|
||||
#include <stdint.h>
|
||||
|
||||
typedef unsigned char byte;
|
||||
|
||||
enum {SHX_SET1 = 0, SHX_SET1A, SHX_SET1B, SHX_SET2, SHX_SET3, SHX_SET4, SHX_SET4A};
|
||||
char us_vcodes[] = {0, 2, 3, 4, 10, 11, 12, 13, 14, 30, 31};
|
||||
char us_vcode_lens[] = {2, 3, 3, 3, 4, 4, 4, 4, 4, 5, 5};
|
||||
char us_sets[][11] =
|
||||
{{ 0, ' ', 'e', 0, 't', 'a', 'o', 'i', 'n', 's', 'r'},
|
||||
{ 0, 'l', 'c', 'd', 'h', 'u', 'p', 'm', 'b', 'g', 'w'},
|
||||
{'f', 'y', 'v', 'k', 'q', 'j', 'x', 'z', 0, 0, 0},
|
||||
{ 0, '9', '0', '1', '2', '3', '4', '5', '6', '7', '8'},
|
||||
{'.', ',', '-', '/', '?', '+', ' ', '(', ')', '$', '@'},
|
||||
{';', '#', ':', '<', '^', '*', '"', '{', '}', '[', ']'},
|
||||
{'=', '%', '\'', '>', '&', '_', '!', '\\', '|', '~', '`'}};
|
||||
// {{ 0, ' ', 'e', 0, 't', 'a', 'o', 'i', 'n', 's', 'r'},
|
||||
// { 0, 'l', 'c', 'd', 'h', 'u', 'p', 'm', 'b', 'g', 'w'},
|
||||
// {'f', 'y', 'v', 'k', 'q', 'j', 'x', 'z', 0, 0, 0},
|
||||
// { 0, '9', '0', '1', '2', '3', '4', '5', '6', '7', '8'},
|
||||
// {'.', ',', '-', '/', '=', '+', ' ', '(', ')', '$', '%'},
|
||||
// {'&', ';', ':', '<', '>', '*', '"', '{', '}', '[', ']'},
|
||||
// {'@', '?', '\'', '^', '#', '_', '!', '\\', '|', '~', '`'}};
|
||||
|
||||
unsigned int c_95[95] ;
|
||||
unsigned char l_95[95] ;
|
||||
|
||||
|
||||
void init_coder() {
|
||||
for (int i = 0; i < 7; i++) {
|
||||
for (int j = 0; j < 11; j++) {
|
||||
char c = us_sets[i][j];
|
||||
if (c != 0 && c != 32) {
|
||||
int ascii = c - 32;
|
||||
//int prev_code = c_95[ascii];
|
||||
//int prev_code_len = l_95[ascii];
|
||||
switch (i) {
|
||||
case SHX_SET1: // just us_vcode
|
||||
c_95[ascii] = (us_vcodes[j] << (16 - us_vcode_lens[j]));
|
||||
l_95[ascii] = us_vcode_lens[j];
|
||||
//checkPreus_vcodes(c, prev_code, prev_code_len, c_95[ascii], l_95[ascii]);
|
||||
if (c >= 'a' && c <= 'z') {
|
||||
ascii -= ('a' - 'A');
|
||||
//prev_code = c_95[ascii];
|
||||
//prev_code_len = l_95[ascii];
|
||||
c_95[ascii] = (2 << 12) + (us_vcodes[j] << (12 - us_vcode_lens[j]));
|
||||
l_95[ascii] = 4 + us_vcode_lens[j];
|
||||
}
|
||||
break;
|
||||
case SHX_SET1A: // 000 + us_vcode
|
||||
c_95[ascii] = 0 + (us_vcodes[j] << (13 - us_vcode_lens[j]));
|
||||
l_95[ascii] = 3 + us_vcode_lens[j];
|
||||
//checkPreus_vcodes(c, prev_code, prev_code_len, c_95[ascii], l_95[ascii]);
|
||||
if (c >= 'a' && c <= 'z') {
|
||||
ascii -= ('a' - 'A');
|
||||
//prev_code = c_95[ascii];
|
||||
//prev_code_len = l_95[ascii];
|
||||
c_95[ascii] = (2 << 12) + 0 + (us_vcodes[j] << (9 - us_vcode_lens[j]));
|
||||
l_95[ascii] = 4 + 3 + us_vcode_lens[j];
|
||||
}
|
||||
break;
|
||||
case SHX_SET1B: // 00110 + us_vcode
|
||||
c_95[ascii] = (6 << 11) + (us_vcodes[j] << (11 - us_vcode_lens[j]));
|
||||
l_95[ascii] = 5 + us_vcode_lens[j];
|
||||
//checkPreus_vcodes(c, prev_code, prev_code_len, c_95[ascii], l_95[ascii]);
|
||||
if (c >= 'a' && c <= 'z') {
|
||||
ascii -= ('a' - 'A');
|
||||
//prev_code = c_95[ascii];
|
||||
//prev_code_len = l_95[ascii];
|
||||
c_95[ascii] = (2 << 12) + (6 << 7) + (us_vcodes[j] << (7 - us_vcode_lens[j]));
|
||||
l_95[ascii] = 4 + 5 + us_vcode_lens[j];
|
||||
}
|
||||
break;
|
||||
case SHX_SET2: // 0011100 + us_vcode
|
||||
c_95[ascii] = (28 << 9) + (us_vcodes[j] << (9 - us_vcode_lens[j]));
|
||||
l_95[ascii] = 7 + us_vcode_lens[j];
|
||||
break;
|
||||
case SHX_SET3: // 0011101 + us_vcode
|
||||
c_95[ascii] = (29 << 9) + (us_vcodes[j] << (9 - us_vcode_lens[j]));
|
||||
l_95[ascii] = 7 + us_vcode_lens[j];
|
||||
break;
|
||||
case SHX_SET4: // 0011110 + us_vcode
|
||||
c_95[ascii] = (30 << 9) + (us_vcodes[j] << (9 - us_vcode_lens[j]));
|
||||
l_95[ascii] = 7 + us_vcode_lens[j];
|
||||
break;
|
||||
case SHX_SET4A: // 0011111 + us_vcode
|
||||
c_95[ascii] = (31 << 9) + (us_vcodes[j] << (9 - us_vcode_lens[j]));
|
||||
l_95[ascii] = 7 + us_vcode_lens[j];
|
||||
}
|
||||
//checkPreus_vcodes(c, prev_code, prev_code_len, c_95[ascii], l_95[ascii]);
|
||||
}
|
||||
}
|
||||
}
|
||||
c_95[0] = 16384;
|
||||
l_95[0] = 3;
|
||||
|
||||
}
|
||||
|
||||
int main(int argv, char *args[]) {
|
||||
init_coder();
|
||||
|
||||
printf("uint16_t c_95[95] PROGMEM = {");
|
||||
for (uint8_t i = 0; i<95; i++) {
|
||||
if (i) { printf(", "); }
|
||||
printf("0x%04X", c_95[i]);
|
||||
}
|
||||
printf(" };\n");
|
||||
|
||||
printf("uint8_t l_95[95] PROGMEM = {");
|
||||
for (uint8_t i = 0; i<95; i++) {
|
||||
if (i) { printf(", "); }
|
||||
printf("%6d", l_95[i]);
|
||||
}
|
||||
printf(" };\n");
|
||||
|
||||
printf("\n\n");
|
||||
|
||||
printf("uint16_t c_95[95] PROGMEM = {");
|
||||
for (uint8_t i = 0; i<95; i++) {
|
||||
if (i) { printf(", "); }
|
||||
printf("%5d", c_95[i]);
|
||||
}
|
||||
printf(" };\n");
|
||||
|
||||
printf("uint8_t l_95[95] PROGMEM = {");
|
||||
for (uint8_t i = 0; i<95; i++) {
|
||||
if (i) { printf(", "); }
|
||||
printf("%5d", l_95[i]);
|
||||
}
|
||||
printf(" };\n");
|
||||
|
||||
|
||||
printf("uint16_t cl_95[95] PROGMEM = {");
|
||||
for (uint8_t i = 0; i<95; i++) {
|
||||
if (i) { printf(", "); }
|
||||
printf("0x%04X + %2d", c_95[i], l_95[i]);
|
||||
}
|
||||
printf(" };\n");
|
||||
|
||||
}
|
BIN
lib/Unishox-1.0-shadinger/generator/remapping.xlsx
Normal file
BIN
lib/Unishox-1.0-shadinger/generator/remapping.xlsx
Normal file
Binary file not shown.
8
lib/Unishox-1.0-shadinger/library.properties.txt
Normal file
8
lib/Unishox-1.0-shadinger/library.properties.txt
Normal file
@ -0,0 +1,8 @@
|
||||
name=Unishox Compressor Decompressor highly customized and optimized for ESP8266 and Tasmota
|
||||
version=1.0
|
||||
author=Arundale Ramanathan, Stephan Hadinger
|
||||
maintainer=Arun <arun@siara.cc>, Stephan <stephan.hadinger@gmail.com>
|
||||
sentence=Unishox compression for Tasmota Rules
|
||||
paragraph=It is based on Unishox hybrid encoding technique. This version has specific Unicode code removed for size.
|
||||
url=https://github.com/siara-cc/Unishox
|
||||
architectures=esp8266
|
602
lib/Unishox-1.0-shadinger/src/unishox.cpp
Normal file
602
lib/Unishox-1.0-shadinger/src/unishox.cpp
Normal file
@ -0,0 +1,602 @@
|
||||
/*
|
||||
* Copyright (C) 2019 Siara Logics (cc)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
* @author Arundale R.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
*
|
||||
* This is a highly modified and optimized version of Unishox
|
||||
* for Tasmota, aimed at compressing `Rules` which are typically
|
||||
* short strings from 50 to 500 bytes.
|
||||
*
|
||||
* - moved to C++ (but still C-style)
|
||||
* - c_95[] and l_95[] are pre-computed
|
||||
* - all arrays in PROGMEM
|
||||
* - removed all Unicode specific code to get code smaller, Unicode is rare in rules and encoded as pure binary
|
||||
* - removed prev_lines management to reduce code size, we don't track previous encodings
|
||||
* - using C++ const instead of #define
|
||||
* - reusing the Unicode market to encode pure binary, which is 3 bits instead of 9
|
||||
* - reverse binary encoding to 255-byte, favoring short encoding for values above 127, typical of Unicode
|
||||
* - remove 2 bits encoding for Counts, since it could lead to a series of more than 8 consecutive 0-bits and output NULL char.
|
||||
* Minimum encoding is 5 bits, which means spending 3+1=4 more bits for values in the range 0..3
|
||||
* - removed CRLF encoding and reusing entry for RPT, saving 3 bits for repeats. Note: any CR will be binary encded
|
||||
* - add safeguard to the output size (len_out), note that the compress buffer needs to be 4 bytes larger than actual compressed output.
|
||||
* This is needed to avoid crash, since output can have ~30 bits
|
||||
* - combined c_95[] and l_95[] to a single array to save space
|
||||
* - Changed mapping of some characters in Set3, Set4 and Set4A, favoring frequent characters in rules and javascript
|
||||
* - Added escape mechanism to ensure we never output NULL char. The marker is 0x2A which looked rare in preliminary tests
|
||||
*
|
||||
* @author Stephan Hadinger
|
||||
*
|
||||
*/
|
||||
|
||||
#include <time.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <ctype.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <pgmspace.h>
|
||||
#include "unishox.h"
|
||||
|
||||
typedef unsigned char byte;
|
||||
// we squeeze both c_95[] and l_95[] in a sinle array.
|
||||
// c_95[] uses only the 3 upper nibbles (or 12 most signifcant bits), while the last nibble encodes length (3..13)
|
||||
uint16_t cl_95[95] PROGMEM = {0x4000 + 3, 0x3F80 + 11, 0x3D80 + 11, 0x3C80 + 10, 0x3BE0 + 12, 0x3E80 + 10, 0x3F40 + 11, 0x3EC0 + 10, 0x3BA0 + 11, 0x3BC0 + 11, 0x3D60 + 11, 0x3B60 + 11, 0x3A80 + 10, 0x3AC0 + 10, 0x3A00 + 9, 0x3B00 + 10, 0x38C0 + 10, 0x3900 + 10, 0x3940 + 11, 0x3960 + 11, 0x3980 + 11, 0x39A0 + 11, 0x39C0 + 11, 0x39E0 + 12, 0x39F0 + 12, 0x3880 + 10, 0x3CC0 + 10, 0x3C00 + 9, 0x3D00 + 10, 0x3E00 + 9, 0x3F00 + 10, 0x3B40 + 11, 0x3BF0 + 12, 0x2B00 + 8, 0x21C0 + 11, 0x20C0 + 10, 0x2100 + 10, 0x2600 + 7, 0x2300 + 11, 0x21E0 + 12, 0x2140 + 11, 0x2D00 + 8, 0x2358 + 13, 0x2340 + 12, 0x2080 + 10, 0x21A0 + 11, 0x2E00 + 8, 0x2C00 + 8, 0x2180 + 11, 0x2350 + 13, 0x2F80 + 9, 0x2F00 + 9, 0x2A00 + 8, 0x2160 + 11, 0x2330 + 12, 0x21F0 + 12, 0x2360 + 13, 0x2320 + 12, 0x2368 + 13, 0x3DE0 + 12, 0x3FA0 + 11, 0x3DF0 + 12, 0x3D40 + 11, 0x3F60 + 11, 0x3FF0 + 12, 0xB000 + 4, 0x1C00 + 7, 0x0C00 + 6, 0x1000 + 6, 0x6000 + 3, 0x3000 + 7, 0x1E00 + 8, 0x1400 + 7, 0xD000 + 4, 0x3580 + 9, 0x3400 + 8, 0x0800 + 6, 0x1A00 + 7, 0xE000 + 4, 0xC000 + 4, 0x1800 + 7, 0x3500 + 9, 0xF800 + 5, 0xF000 + 5, 0xA000 + 4, 0x1600 + 7, 0x3300 + 8, 0x1F00 + 8, 0x3600 + 9, 0x3200 + 8, 0x3680 + 9, 0x3DA0 + 11, 0x3FC0 + 11, 0x3DC0 + 11, 0x3FE0 + 12 };
|
||||
// Original version with c/l separate
|
||||
// uint16_t c_95[95] PROGMEM = {0x4000, 0x3F80, 0x3D80, 0x3C80, 0x3BE0, 0x3E80, 0x3F40, 0x3EC0, 0x3BA0, 0x3BC0, 0x3D60, 0x3B60, 0x3A80, 0x3AC0, 0x3A00, 0x3B00, 0x38C0, 0x3900, 0x3940, 0x3960, 0x3980, 0x39A0, 0x39C0, 0x39E0, 0x39F0, 0x3880, 0x3CC0, 0x3C00, 0x3D00, 0x3E00, 0x3F00, 0x3B40, 0x3BF0, 0x2B00, 0x21C0, 0x20C0, 0x2100, 0x2600, 0x2300, 0x21E0, 0x2140, 0x2D00, 0x2358, 0x2340, 0x2080, 0x21A0, 0x2E00, 0x2C00, 0x2180, 0x2350, 0x2F80, 0x2F00, 0x2A00, 0x2160, 0x2330, 0x21F0, 0x2360, 0x2320, 0x2368, 0x3DE0, 0x3FA0, 0x3DF0, 0x3D40, 0x3F60, 0x3FF0, 0xB000, 0x1C00, 0x0C00, 0x1000, 0x6000, 0x3000, 0x1E00, 0x1400, 0xD000, 0x3580, 0x3400, 0x0800, 0x1A00, 0xE000, 0xC000, 0x1800, 0x3500, 0xF800, 0xF000, 0xA000, 0x1600, 0x3300, 0x1F00, 0x3600, 0x3200, 0x3680, 0x3DA0, 0x3FC0, 0x3DC0, 0x3FE0 };
|
||||
// uint8_t l_95[95] PROGMEM = { 3, 11, 11, 10, 12, 10, 11, 10, 11, 11, 11, 11, 10, 10, 9, 10, 10, 10, 11, 11, 11, 11, 11, 12, 12, 10, 10, 9, 10, 9, 10, 11, 12, 8, 11, 10, 10, 7, 11, 12, 11, 8, 13, 12, 10, 11, 8, 8, 11, 13, 9, 9, 8, 11, 12, 12, 13, 12, 13, 12, 11, 12, 11, 11, 12, 4, 7, 6, 6, 3, 7, 8, 7, 4, 9, 8, 6, 7, 4, 4, 7, 9, 5, 5, 4, 7, 8, 8, 9, 8, 9, 11, 11, 11, 12 };
|
||||
|
||||
enum {SHX_STATE_1 = 1, SHX_STATE_2}; // removed Unicode state
|
||||
|
||||
enum {SHX_SET1 = 0, SHX_SET1A, SHX_SET1B, SHX_SET2, SHX_SET3, SHX_SET4, SHX_SET4A};
|
||||
// changed mapping in Set3, Set4, Set4A to accomodate frequencies in Rules and Javascript
|
||||
char sets[][11] PROGMEM =
|
||||
{{ 0, ' ', 'e', 0, 't', 'a', 'o', 'i', 'n', 's', 'r'},
|
||||
{ 0, 'l', 'c', 'd', 'h', 'u', 'p', 'm', 'b', 'g', 'w'},
|
||||
{'f', 'y', 'v', 'k', 'q', 'j', 'x', 'z', 0, 0, 0},
|
||||
{ 0, '9', '0', '1', '2', '3', '4', '5', '6', '7', '8'},
|
||||
{'.', ',', '-', '/', '?', '+', ' ', '(', ')', '$', '@'},
|
||||
{';', '#', ':', '<', '^', '*', '"', '{', '}', '[', ']'},
|
||||
{'=', '%', '\'', '>', '&', '_', '!', '\\', '|', '~', '`'}};
|
||||
// {{ 0, ' ', 'e', 0, 't', 'a', 'o', 'i', 'n', 's', 'r'},
|
||||
// { 0, 'l', 'c', 'd', 'h', 'u', 'p', 'm', 'b', 'g', 'w'},
|
||||
// {'f', 'y', 'v', 'k', 'q', 'j', 'x', 'z', 0, 0, 0},
|
||||
// { 0, '9', '0', '1', '2', '3', '4', '5', '6', '7', '8'},
|
||||
// {'.', ',', '-', '/', '=', '+', ' ', '(', ')', '$', '%'},
|
||||
// {'&', ';', ':', '<', '>', '*', '"', '{', '}', '[', ']'},
|
||||
// {'@', '?', '\'', '^', '#', '_', '!', '\\', '|', '~', '`'}};
|
||||
|
||||
// Decoder is designed for using less memory, not speed
|
||||
// Decode lookup table for code index and length
|
||||
// First 2 bits 00, Next 3 bits indicate index of code from 0,
|
||||
// last 3 bits indicate code length in bits
|
||||
// 0, 1, 2, 3, 4,
|
||||
char us_vcode[32] PROGMEM =
|
||||
{2 + (0 << 3), 3 + (3 << 3), 3 + (1 << 3), 4 + (6 << 3), 0,
|
||||
// 5, 6, 7, 8, 9, 10
|
||||
4 + (4 << 3), 3 + (2 << 3), 4 + (8 << 3), 0, 0, 0,
|
||||
// 11, 12, 13, 14, 15
|
||||
4 + (7 << 3), 0, 4 + (5 << 3), 0, 5 + (9 << 3),
|
||||
// 16, 17, 18, 19, 20, 21, 22, 23
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
// 24, 25, 26, 27, 28, 29, 30, 31
|
||||
0, 0, 0, 0, 0, 0, 0, 5 + (10 << 3)};
|
||||
// 0, 1, 2, 3, 4, 5, 6, 7,
|
||||
char us_hcode[32] PROGMEM =
|
||||
{1 + (1 << 3), 2 + (0 << 3), 0, 3 + (2 << 3), 0, 0, 0, 5 + (3 << 3),
|
||||
// 8, 9, 10, 11, 12, 13, 14, 15,
|
||||
0, 0, 0, 0, 0, 0, 0, 5 + (5 << 3),
|
||||
// 16, 17, 18, 19, 20, 21, 22, 23
|
||||
0, 0, 0, 0, 0, 0, 0, 5 + (4 << 3),
|
||||
// 24, 25, 26, 27, 28, 29, 30, 31
|
||||
0, 0, 0, 0, 0, 0, 0, 5 + (6 << 3)};
|
||||
|
||||
const char ESCAPE_MARKER = 0x2A; // Escape any null char
|
||||
|
||||
const uint16_t TERM_CODE = 0x37C0; // 0b0011011111000000
|
||||
const uint16_t TERM_CODE_LEN = 10;
|
||||
const uint16_t DICT_CODE = 0x0000;
|
||||
const uint16_t DICT_CODE_LEN = 5;
|
||||
const uint16_t DICT_OTHER_CODE = 0x0000; // not used
|
||||
const uint16_t DICT_OTHER_CODE_LEN = 6;
|
||||
// const uint16_t RPT_CODE = 0x2370;
|
||||
// const uint16_t RPT_CODE_LEN = 13;
|
||||
const uint16_t RPT_CODE_TASMOTA = 0x3780;
|
||||
const uint16_t RPT_CODE_TASMOTA_LEN = 10;
|
||||
const uint16_t BACK2_STATE1_CODE = 0x2000; // 0010 = back to lower case
|
||||
const uint16_t BACK2_STATE1_CODE_LEN = 4;
|
||||
const uint16_t BACK_FROM_UNI_CODE = 0xFE00;
|
||||
const uint16_t BACK_FROM_UNI_CODE_LEN = 8;
|
||||
// const uint16_t CRLF_CODE = 0x3780;
|
||||
// const uint16_t CRLF_CODE_LEN = 10;
|
||||
const uint16_t LF_CODE = 0x3700;
|
||||
const uint16_t LF_CODE_LEN = 9;
|
||||
const uint16_t TAB_CODE = 0x2400;
|
||||
const uint16_t TAB_CODE_LEN = 7;
|
||||
// const uint16_t UNI_CODE = 0x8000; // Unicode disabled
|
||||
// const uint16_t UNI_CODE_LEN = 3;
|
||||
// const uint16_t UNI_STATE_SPL_CODE = 0xF800;
|
||||
// const uint16_t UNI_STATE_SPL_CODE_LEN = 5;
|
||||
// const uint16_t UNI_STATE_DICT_CODE = 0xFC00;
|
||||
// const uint16_t UNI_STATE_DICT_CODE_LEN = 7;
|
||||
// const uint16_t CONT_UNI_CODE = 0x2800;
|
||||
// const uint16_t CONT_UNI_CODE_LEN = 7;
|
||||
const uint16_t ALL_UPPER_CODE = 0x2200;
|
||||
const uint16_t ALL_UPPER_CODE_LEN = 8;
|
||||
const uint16_t SW2_STATE2_CODE = 0x3800;
|
||||
const uint16_t SW2_STATE2_CODE_LEN = 7;
|
||||
const uint16_t ST2_SPC_CODE = 0x3B80;
|
||||
const uint16_t ST2_SPC_CODE_LEN = 11;
|
||||
const uint16_t BIN_CODE_TASMOTA = 0x8000;
|
||||
const uint16_t BIN_CODE_TASMOTA_LEN = 3;
|
||||
// const uint16_t BIN_CODE = 0x2000;
|
||||
// const uint16_t BIN_CODE_LEN = 9;
|
||||
|
||||
#define NICE_LEN 5
|
||||
|
||||
// uint16_t mask[] PROGMEM = {0x8000, 0xC000, 0xE000, 0xF000, 0xF800, 0xFC00, 0xFE00, 0xFF00};
|
||||
uint8_t mask[] PROGMEM = {0x80, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC, 0xFE, 0xFF};
|
||||
|
||||
int append_bits(char *out, size_t ol, unsigned int code, int clen, byte state) {
|
||||
|
||||
byte cur_bit;
|
||||
byte blen;
|
||||
unsigned char a_byte;
|
||||
|
||||
if (state == SHX_STATE_2) {
|
||||
// remove change state prefix
|
||||
if ((code >> 9) == 0x1C) {
|
||||
code <<= 7;
|
||||
clen -= 7;
|
||||
}
|
||||
//if (code == 14272 && clen == 10) {
|
||||
// code = 9084;
|
||||
// clen = 14;
|
||||
//}
|
||||
}
|
||||
while (clen > 0) {
|
||||
cur_bit = ol % 8;
|
||||
blen = (clen > 8 ? 8 : clen);
|
||||
// a_byte = (code & pgm_read_word(&mask[blen - 1])) >> 8;
|
||||
// a_byte = (code & (pgm_read_word(&mask[blen - 1]) << 8)) >> 8;
|
||||
a_byte = (code >> 8) & pgm_read_word(&mask[blen - 1]);
|
||||
a_byte >>= cur_bit;
|
||||
if (blen + cur_bit > 8)
|
||||
blen = (8 - cur_bit);
|
||||
if (out) { // if out == nullptr, then we are in dry-run mode
|
||||
if (cur_bit == 0)
|
||||
out[ol / 8] = a_byte;
|
||||
else
|
||||
out[ol / 8] |= a_byte;
|
||||
}
|
||||
code <<= blen;
|
||||
ol += blen;
|
||||
if ((out) && (0 == ol % 8)) { // if out == nullptr, dry-run mode. We miss the escaping of characters in the length
|
||||
// we completed a full byte
|
||||
char last_c = out[(ol / 8) - 1];
|
||||
if ((0 == last_c) || (ESCAPE_MARKER == last_c)) {
|
||||
out[ol / 8] = 1 + last_c; // increment to 0x01 or 0x2B
|
||||
out[(ol / 8) -1] = ESCAPE_MARKER; // replace old value with marker
|
||||
ol += 8; // add one full byte
|
||||
}
|
||||
}
|
||||
clen -= blen;
|
||||
}
|
||||
return ol;
|
||||
}
|
||||
|
||||
// First five bits are code and Last three bits of codes represent length
|
||||
// removing last 2 bytes, unused, we will never have values above 600 bytes
|
||||
// const byte codes[7] = {0x01, 0x82, 0xC3, 0xE5, 0xED, 0xF5, 0xFD};
|
||||
// const byte bit_len[7] = {2, 5, 7, 9, 12, 16, 17};
|
||||
// const uint16_t adder[7] = {0, 4, 36, 164, 676, 4772, 0};
|
||||
byte codes[] PROGMEM = { 0x82, 0xC3, 0xE5, 0xED, 0xF5 };
|
||||
byte bit_len[] PROGMEM = { 5, 7, 9, 12, 16 };
|
||||
// uint16_t adder[7] PROGMEM = { 0, 32, 160, 672, 4768 }; // no more used
|
||||
|
||||
int encodeCount(char *out, int ol, int count) {
|
||||
int till = 0;
|
||||
int base = 0;
|
||||
for (int i = 0; i < sizeof(bit_len); i++) {
|
||||
uint32_t bit_len_i = pgm_read_byte(&bit_len[i]);
|
||||
till += (1 << bit_len_i);
|
||||
if (count < till) {
|
||||
byte codes_i = pgm_read_byte(&codes[i]);
|
||||
ol = append_bits(out, ol, (codes_i & 0xF8) << 8, codes_i & 0x07, 1);
|
||||
// ol = append_bits(out, ol, (count - pgm_read_word(&adder[i])) << (16 - bit_len_i), bit_len_i, 1);
|
||||
ol = append_bits(out, ol, (count - base) << (16 - bit_len_i), bit_len_i, 1);
|
||||
return ol;
|
||||
}
|
||||
base = till;
|
||||
}
|
||||
return ol;
|
||||
}
|
||||
|
||||
int matchOccurance(const char *in, int len, int l, char *out, int *ol, byte *state, byte *is_all_upper) {
|
||||
int j, k;
|
||||
int longest_dist = 0;
|
||||
int longest_len = 0;
|
||||
for (j = l - NICE_LEN; j >= 0; j--) {
|
||||
for (k = l; k < len && j + k - l < l; k++) {
|
||||
if (in[k] != in[j + k - l])
|
||||
break;
|
||||
}
|
||||
// while ((((unsigned char) in[k]) >> 6) == 2)
|
||||
// k--; // Skip partial UTF-8 matches
|
||||
//if ((in[k - 1] >> 3) == 0x1E || (in[k - 1] >> 4) == 0x0E || (in[k - 1] >> 5) == 0x06)
|
||||
// k--;
|
||||
if (k - l > NICE_LEN - 1) {
|
||||
int match_len = k - l - NICE_LEN;
|
||||
int match_dist = l - j - NICE_LEN + 1;
|
||||
if (match_len > longest_len) {
|
||||
longest_len = match_len;
|
||||
longest_dist = match_dist;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (longest_len) {
|
||||
if (*state == SHX_STATE_2 || *is_all_upper) {
|
||||
*is_all_upper = 0;
|
||||
*state = SHX_STATE_1;
|
||||
*ol = append_bits(out, *ol, BACK2_STATE1_CODE, BACK2_STATE1_CODE_LEN, *state);
|
||||
}
|
||||
*ol = append_bits(out, *ol, DICT_CODE, DICT_CODE_LEN, 1);
|
||||
*ol = encodeCount(out, *ol, longest_len);
|
||||
*ol = encodeCount(out, *ol, longest_dist);
|
||||
l += (longest_len + NICE_LEN);
|
||||
l--;
|
||||
return l;
|
||||
}
|
||||
return -l;
|
||||
}
|
||||
|
||||
// Compress a buffer.
|
||||
// Inputs:
|
||||
// - in: non-null pointer to a buffer of bytes to be compressed. Progmem is not valid. Null bytes are valid.
|
||||
// - len: size of the input buffer. 0 is valid for empty buffer
|
||||
// - out: pointer to output buffer. out is nullptr, the compressor does a dry-run and reports the compressed size without writing bytes
|
||||
// - len_out: length in bytes of the output buffer.
|
||||
// Output:
|
||||
// - if >= 0: size of the compressed buffer. The output buffer does not contain NULL bytes, and it is not NULL terminated
|
||||
// - if < 0: an error occured, most certainly the output buffer was not large enough
|
||||
int32_t unishox_compress(const char *in, size_t len, char *out, size_t len_out) {
|
||||
|
||||
char *ptr;
|
||||
byte bits;
|
||||
byte state;
|
||||
|
||||
int l, ll, ol;
|
||||
char c_in, c_next;
|
||||
byte is_upper, is_all_upper;
|
||||
|
||||
ol = 0;
|
||||
state = SHX_STATE_1;
|
||||
is_all_upper = 0;
|
||||
for (l=0; l<len; l++) {
|
||||
|
||||
c_in = in[l];
|
||||
|
||||
if (l && l < len - 4) {
|
||||
if (c_in == in[l - 1] && c_in == in[l + 1] && c_in == in[l + 2] && c_in == in[l + 3]) { // check for repeat
|
||||
int rpt_count = l + 4;
|
||||
while (rpt_count < len && in[rpt_count] == c_in)
|
||||
rpt_count++;
|
||||
rpt_count -= l;
|
||||
|
||||
if (state == SHX_STATE_2 || is_all_upper) {
|
||||
is_all_upper = 0;
|
||||
state = SHX_STATE_1;
|
||||
ol = append_bits(out, ol, BACK2_STATE1_CODE, BACK2_STATE1_CODE_LEN, state); // back to lower case and Set1
|
||||
}
|
||||
// ol = append_bits(out, ol, RPT_CODE, RPT_CODE_LEN, 1);
|
||||
ol = append_bits(out, ol, RPT_CODE_TASMOTA, RPT_CODE_TASMOTA_LEN, 1); // reusing CRLF for RPT
|
||||
ol = encodeCount(out, ol, rpt_count - 4);
|
||||
l += rpt_count;
|
||||
l--;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (l < (len - NICE_LEN + 1)) {
|
||||
l = matchOccurance(in, len, l, out, &ol, &state, &is_all_upper);
|
||||
if (l > 0) {
|
||||
continue;
|
||||
}
|
||||
l = -l;
|
||||
}
|
||||
if (state == SHX_STATE_2) { // if Set2
|
||||
if ((c_in >= ' ' && c_in <= '@') ||
|
||||
(c_in >= '[' && c_in <= '`') ||
|
||||
(c_in >= '{' && c_in <= '~')) {
|
||||
} else {
|
||||
state = SHX_STATE_1; // back to Set1 and lower case
|
||||
ol = append_bits(out, ol, BACK2_STATE1_CODE, BACK2_STATE1_CODE_LEN, state);
|
||||
}
|
||||
}
|
||||
|
||||
is_upper = 0;
|
||||
if (c_in >= 'A' && c_in <= 'Z')
|
||||
is_upper = 1;
|
||||
else {
|
||||
if (is_all_upper) {
|
||||
is_all_upper = 0;
|
||||
ol = append_bits(out, ol, BACK2_STATE1_CODE, BACK2_STATE1_CODE_LEN, state);
|
||||
}
|
||||
}
|
||||
|
||||
c_next = 0;
|
||||
if (l+1 < len)
|
||||
c_next = in[l+1];
|
||||
|
||||
if (c_in >= 32 && c_in <= 126) {
|
||||
if (is_upper && !is_all_upper) {
|
||||
for (ll=l+5; ll>=l && ll<len; ll--) {
|
||||
if (in[ll] < 'A' || in[ll] > 'Z')
|
||||
break;
|
||||
}
|
||||
if (ll == l-1) {
|
||||
ol = append_bits(out, ol, ALL_UPPER_CODE, ALL_UPPER_CODE_LEN, state); // CapsLock
|
||||
is_all_upper = 1;
|
||||
}
|
||||
}
|
||||
if (state == SHX_STATE_1 && c_in >= '0' && c_in <= '9') {
|
||||
ol = append_bits(out, ol, SW2_STATE2_CODE, SW2_STATE2_CODE_LEN, state); // Switch to sticky Set2
|
||||
state = SHX_STATE_2;
|
||||
}
|
||||
c_in -= 32;
|
||||
if (is_all_upper && is_upper)
|
||||
c_in += 32;
|
||||
if (c_in == 0 && state == SHX_STATE_2)
|
||||
ol = append_bits(out, ol, ST2_SPC_CODE, ST2_SPC_CODE_LEN, state); // space from Set2 ionstead of Set1
|
||||
else {
|
||||
// ol = append_bits(out, ol, pgm_read_word(&c_95[c_in]), pgm_read_byte(&l_95[c_in]), state); // original version with c/l in split arrays
|
||||
uint16_t cl = pgm_read_word(&cl_95[c_in]);
|
||||
ol = append_bits(out, ol, cl & 0xFFF0, cl & 0x000F, state);
|
||||
}
|
||||
} else
|
||||
// if (c_in == 13 && c_next == 10) { // CRLF disabled
|
||||
// ol = append_bits(out, ol, CRLF_CODE, CRLF_CODE_LEN, state); // CRLF
|
||||
// l++;
|
||||
// } else
|
||||
if (c_in == 10) {
|
||||
ol = append_bits(out, ol, LF_CODE, LF_CODE_LEN, state); // LF
|
||||
} else
|
||||
if (c_in == '\t') {
|
||||
ol = append_bits(out, ol, TAB_CODE, TAB_CODE_LEN, state); // TAB
|
||||
} else {
|
||||
ol = append_bits(out, ol, BIN_CODE_TASMOTA, BIN_CODE_TASMOTA_LEN, state); // Binary, we reuse the Unicode marker which 3 bits instead of 9
|
||||
ol = encodeCount(out, ol, (unsigned char) 255 - c_in);
|
||||
}
|
||||
|
||||
// check that we have some headroom in the output buffer
|
||||
if (ol / 8 >= len_out - 4) {
|
||||
return -1; // we risk overflow and crash
|
||||
}
|
||||
}
|
||||
|
||||
bits = ol % 8;
|
||||
if (bits) {
|
||||
ol = append_bits(out, ol, TERM_CODE, 8 - bits, 1); // 0011 0111 1100 0000 TERM = 0011 0111 11
|
||||
}
|
||||
return ol/8+(ol%8?1:0);
|
||||
}
|
||||
|
||||
int getBitVal(const char *in, int bit_no, int count) {
|
||||
char c_in = in[bit_no >> 3];
|
||||
if ((bit_no >> 3) && (ESCAPE_MARKER == in[(bit_no >> 3) - 1])) { // if previous byte is a marker, decrement
|
||||
c_in--;
|
||||
}
|
||||
return (c_in & (0x80 >> (bit_no % 8)) ? 1 << count : 0);
|
||||
}
|
||||
|
||||
// Returns:
|
||||
// 0..11
|
||||
// or -1 if end of stream
|
||||
int getCodeIdx(char *code_type, const char *in, int len, int *bit_no_p) {
|
||||
int code = 0;
|
||||
int count = 0;
|
||||
do {
|
||||
// detect marker
|
||||
if (ESCAPE_MARKER == in[*bit_no_p >> 3]) {
|
||||
*bit_no_p += 8; // skip marker
|
||||
}
|
||||
if (*bit_no_p >= len)
|
||||
return -1; // invalid state
|
||||
code += getBitVal(in, *bit_no_p, count);
|
||||
(*bit_no_p)++;
|
||||
count++;
|
||||
uint8_t code_type_code = pgm_read_byte(&code_type[code]);
|
||||
if (code_type_code && (code_type_code & 0x07) == count) {
|
||||
return code_type_code >> 3;
|
||||
}
|
||||
} while (count < 5);
|
||||
return 1; // skip if code not found
|
||||
}
|
||||
|
||||
int getNumFromBits(const char *in, int bit_no, int count) {
|
||||
int ret = 0;
|
||||
while (count--) {
|
||||
if (ESCAPE_MARKER == in[bit_no >> 3]) {
|
||||
bit_no += 8; // skip marker
|
||||
}
|
||||
ret += getBitVal(in, bit_no++, count);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
// const byte bit_len[7] = {5, 2, 7, 9, 12, 16, 17};
|
||||
// const uint16_t adder[7] = {4, 0, 36, 164, 676, 4772, 0};
|
||||
|
||||
// byte bit_len[7] PROGMEM = { 5, 7, 9, 12, 16 };
|
||||
// byte bit_len_read[7] PROGMEM = {5, 2, 7, 9, 12, 16 };
|
||||
// uint16_t adder_read[7] PROGMEM = {4, 0, 36, 164, 676, 4772, 0};
|
||||
// uint16_t adder_read[] PROGMEM = {0, 0, 32, 160, 672, 4768 };
|
||||
|
||||
// byte bit_len[7] PROGMEM = { 5, 7, 9, 12, 16 };
|
||||
// uint16_t adder_read[] PROGMEM = {0, 32, 160, 672, 4768 };
|
||||
|
||||
// Code size optimized, recalculate adder[] like in encodeCount
|
||||
int readCount(const char *in, int *bit_no_p, int len) {
|
||||
int idx = getCodeIdx(us_hcode, in, len, bit_no_p);
|
||||
if (idx >= 1) idx--; // we skip v = 1 (code '0') since we no more accept 2 bits encoding
|
||||
if ((idx >= sizeof(bit_len)) || (idx < 0)) return 0; // unsupported or end of stream
|
||||
|
||||
int base;
|
||||
int till = 0;
|
||||
byte bit_len_idx; // bit_len[0]
|
||||
for (uint32_t i = 0; i <= idx; i++) {
|
||||
base = till;
|
||||
bit_len_idx = pgm_read_byte(&bit_len[i]);
|
||||
till += (1 << bit_len_idx);
|
||||
}
|
||||
int count = getNumFromBits(in, *bit_no_p, bit_len_idx) + base;
|
||||
|
||||
(*bit_no_p) += bit_len_idx;
|
||||
return count;
|
||||
}
|
||||
|
||||
int decodeRepeat(const char *in, int len, char *out, int ol, int *bit_no) {
|
||||
int dict_len = readCount(in, bit_no, len) + NICE_LEN;
|
||||
int dist = readCount(in, bit_no, len) + NICE_LEN - 1;
|
||||
memcpy(out + ol, out + ol - dist, dict_len);
|
||||
ol += dict_len;
|
||||
|
||||
return ol;
|
||||
}
|
||||
|
||||
int32_t unishox_decompress(const char *in, size_t len, char *out, size_t len_out) {
|
||||
|
||||
int dstate;
|
||||
int bit_no;
|
||||
byte is_all_upper;
|
||||
|
||||
int ol = 0;
|
||||
bit_no = 0;
|
||||
dstate = SHX_SET1;
|
||||
is_all_upper = 0;
|
||||
|
||||
len <<= 3; // *8, len in bits
|
||||
out[ol] = 0;
|
||||
while (bit_no < len) {
|
||||
int h, v;
|
||||
char c = 0;
|
||||
byte is_upper = is_all_upper;
|
||||
int orig_bit_no = bit_no;
|
||||
v = getCodeIdx(us_vcode, in, len, &bit_no); // read vCode
|
||||
if (v < 0) break; // end of stream
|
||||
h = dstate; // Set1 or Set2
|
||||
if (v == 0) { // Switch which is common to Set1 and Set2, first entry
|
||||
h = getCodeIdx(us_hcode, in, len, &bit_no); // read hCode
|
||||
if (h < 0) break; // end of stream
|
||||
if (h == SHX_SET1) { // target is Set1
|
||||
if (dstate == SHX_SET1) { // Switch from Set1 to Set1 us UpperCase
|
||||
if (is_all_upper) { // if CapsLock, then back to LowerCase
|
||||
is_upper = is_all_upper = 0;
|
||||
continue;
|
||||
}
|
||||
v = getCodeIdx(us_vcode, in, len, &bit_no); // read again vCode
|
||||
if (v < 0) break; // end of stream
|
||||
if (v == 0) {
|
||||
h = getCodeIdx(us_hcode, in, len, &bit_no); // read second hCode
|
||||
if (h < 0) break; // end of stream
|
||||
if (h == SHX_SET1) { // If double Switch Set1, the CapsLock
|
||||
is_all_upper = 1;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
is_upper = 1; // anyways, still uppercase
|
||||
} else {
|
||||
dstate = SHX_SET1; // if Set was not Set1, switch to Set1
|
||||
continue;
|
||||
}
|
||||
} else
|
||||
if (h == SHX_SET2) { // If Set2, switch dstate to Set2
|
||||
if (dstate == SHX_SET1) // TODO: is this test useful, there are only 2 states possible
|
||||
dstate = SHX_SET2;
|
||||
continue;
|
||||
}
|
||||
if (h != SHX_SET1) { // all other Sets (why not else)
|
||||
v = getCodeIdx(us_vcode, in, len, &bit_no); // we changed set, now read vCode for char
|
||||
if (v < 0) break; // end of stream
|
||||
}
|
||||
}
|
||||
|
||||
if (v == 0 && h == SHX_SET1A) {
|
||||
if (is_upper) {
|
||||
out[ol++] = 255 - readCount(in, &bit_no, len); // binary
|
||||
} else {
|
||||
ol = decodeRepeat(in, len, out, ol, &bit_no); // dist
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (h == SHX_SET1 && v == 3) {
|
||||
// was Unicode, will do Binary instead
|
||||
out[ol++] = 255 - readCount(in, &bit_no, len); // binary
|
||||
continue;
|
||||
}
|
||||
if (h < 7 && v < 11) // TODO: are these the actual limits? Not 11x7 ?
|
||||
c = pgm_read_byte(&sets[h][v]);
|
||||
if (c >= 'a' && c <= 'z') {
|
||||
if (is_upper)
|
||||
c -= 32; // go to UpperCase for letters
|
||||
} else { // handle all other cases
|
||||
if (is_upper && dstate == SHX_SET1 && v == 1)
|
||||
c = '\t'; // If UpperCase Space, change to TAB
|
||||
if (h == SHX_SET1B) {
|
||||
if (8 == v) { // was LF or RPT, now only LF
|
||||
// if (is_upper) { // rpt
|
||||
// int count = readCount(in, &bit_no, len);
|
||||
// count += 4;
|
||||
// char rpt_c = out[ol - 1];
|
||||
// while (count--)
|
||||
// out[ol++] = rpt_c;
|
||||
// } else {
|
||||
out[ol++] = '\n';
|
||||
// }
|
||||
continue;
|
||||
}
|
||||
if (9 == v) { // was CRLF, now RPT
|
||||
// out[ol++] = '\r'; // CRLF removed
|
||||
// out[ol++] = '\n';
|
||||
int count = readCount(in, &bit_no, len);
|
||||
count += 4;
|
||||
if (ol + count >= len_out) {
|
||||
return -1; // overflow
|
||||
}
|
||||
char rpt_c = out[ol - 1];
|
||||
while (count--)
|
||||
out[ol++] = rpt_c;
|
||||
continue;
|
||||
}
|
||||
if (10 == v) {
|
||||
break; // TERM, stop decoding
|
||||
}
|
||||
}
|
||||
}
|
||||
out[ol++] = c;
|
||||
|
||||
if (ol >= len_out) {
|
||||
return -1; // overflow
|
||||
}
|
||||
}
|
||||
|
||||
return ol;
|
||||
|
||||
}
|
26
lib/Unishox-1.0-shadinger/src/unishox.h
Normal file
26
lib/Unishox-1.0-shadinger/src/unishox.h
Normal file
@ -0,0 +1,26 @@
|
||||
/*
|
||||
* Copyright (C) 2019 Siara Logics (cc)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
* @author Arundale R.
|
||||
*
|
||||
*/
|
||||
#ifndef unishox
|
||||
#define unishox
|
||||
|
||||
extern int32_t unishox_compress(const char *in, size_t len, char *out, size_t len_out);
|
||||
extern int32_t unishox_decompress(const char *in, size_t len, char *out, size_t len_out);
|
||||
|
||||
#endif
|
||||
|
@ -99,6 +99,16 @@ build_flags = ${esp_defaults.build_flags}
|
||||
-D NDEBUG
|
||||
-mtarget-align
|
||||
-DFP_IN_IROM
|
||||
-DBEARSSL_SSL_BASIC
|
||||
; NONOSDK22x_190703 = 2.2.2-dev(38a443e)
|
||||
-DPIO_FRAMEWORK_ARDUINO_ESPRESSIF_SDK22x_190703
|
||||
; lwIP 2 - Higher Bandwidth no Features
|
||||
-DPIO_FRAMEWORK_ARDUINO_LWIP2_HIGHER_BANDWIDTH_LOW_FLASH
|
||||
; VTABLES in Flash
|
||||
-DVTABLES_IN_FLASH
|
||||
; No exception code in firmware
|
||||
-fno-exceptions
|
||||
-lstdc++
|
||||
; the following removes the 4-bytes alignment for PSTR(), waiting for a cleaner flag from Arduino Core
|
||||
-DPSTR\(s\)=\(__extension__\(\{static\ const\ char\ __c\[\]\ __attribute__\(\(__aligned__\(1\)\)\)\ __attribute__\(\(section\(\ \"\\\\\".irom0.pstr.\"\ __FILE__\ \".\"\ __STRINGIZE\(__LINE__\)\ \".\"\ \ __STRINGIZE\(__COUNTER__\)\ \"\\\\\"\,\ \\\\\"aSM\\\\\"\,\ \@progbits\,\ 1\ \#\"\)\)\)\ =\ \(s\)\;\ \&__c\[0\]\;\}\)\)
|
||||
|
||||
@ -113,17 +123,7 @@ platform_packages = ${tasmota_core.platform_packages}
|
||||
build_flags = ${tasmota_core.build_flags}
|
||||
|
||||
[tasmota_core]
|
||||
; *** Esp8266 Arduino core 2.7.0
|
||||
platform = espressif8266@2.5.0
|
||||
platform_packages = framework-arduinoespressif8266 @ https://github.com/tasmota/Arduino/releases/download/2.7.0/esp8266-2.7.0.zip
|
||||
; *** Esp8266 Arduino core 2.7.1
|
||||
platform = espressif8266@2.5.1
|
||||
platform_packages =
|
||||
build_flags = ${esp82xx_defaults.build_flags}
|
||||
-DBEARSSL_SSL_BASIC
|
||||
; NONOSDK22x_190703 = 2.2.2-dev(38a443e)
|
||||
-DPIO_FRAMEWORK_ARDUINO_ESPRESSIF_SDK22x_190703
|
||||
; lwIP 2 - Higher Bandwidth no Features
|
||||
-DPIO_FRAMEWORK_ARDUINO_LWIP2_HIGHER_BANDWIDTH_LOW_FLASH
|
||||
; VTABLES in Flash
|
||||
-DVTABLES_IN_FLASH
|
||||
; No exception code in firmware
|
||||
-fno-exceptions
|
||||
-lstdc++
|
||||
|
@ -83,16 +83,13 @@ extra_scripts = ${scripts_defaults.extra_scripts}
|
||||
|
||||
[tasmota_stage]
|
||||
; *** Esp8266 core for Arduino version Tasmota stage
|
||||
platform = espressif8266@2.5.0
|
||||
platform_packages = framework-arduinoespressif8266 @ https://github.com/esp8266/Arduino.git#36e047e908cfa6eafaaf824988070b49f2c2ff2a
|
||||
build_flags = ${esp82xx_defaults.build_flags}
|
||||
-DBEARSSL_SSL_BASIC
|
||||
extends = tasmota_core
|
||||
platform_packages = framework-arduinoespressif8266 @ https://github.com/esp8266/Arduino.git#a5432625d93f60d7e28cfdc5ed8abb3e0151951d
|
||||
; *********** Alternative Options, enable only if you know exactly what you do ********
|
||||
; NONOSDK221
|
||||
; -DPIO_FRAMEWORK_ARDUINO_ESPRESSIF_SDK221
|
||||
; NONOSDK22x_190313
|
||||
; -DPIO_FRAMEWORK_ARDUINO_ESPRESSIF_SDK22x_190313
|
||||
; NONOSDK22x_190703 = 2.2.1+100-dev(38a443e) (Tasmota default) (Firmware 2K smaller than NONOSDK22x_191105)
|
||||
-DPIO_FRAMEWORK_ARDUINO_ESPRESSIF_SDK22x_190703
|
||||
; NONOSDK22x_191024 = 2.2.1+111-dev(5ab15d1)
|
||||
; -DPIO_FRAMEWORK_ARDUINO_ESPRESSIF_SDK22x_191024
|
||||
; NONOSDK22x_191105 = 2.2.1+113-dev(bb83b9b)
|
||||
@ -109,34 +106,24 @@ build_flags = ${esp82xx_defaults.build_flags}
|
||||
; -DPIO_FRAMEWORK_ARDUINO_LWIP2_HIGHER_BANDWIDTH
|
||||
; lwIP 2 - Higher Bandwidth Low Memory no Features
|
||||
; -DPIO_FRAMEWORK_ARDUINO_LWIP2_LOW_MEMORY_LOW_FLASH
|
||||
; lwIP 2 - Higher Bandwidth no Features (Tasmota default)
|
||||
-DPIO_FRAMEWORK_ARDUINO_LWIP2_HIGHER_BANDWIDTH_LOW_FLASH
|
||||
; VTABLES in Flash (Tasmota default)
|
||||
-DVTABLES_IN_FLASH
|
||||
; VTABLES in Heap
|
||||
; -DVTABLES_IN_DRAM
|
||||
; VTABLES in IRAM
|
||||
; -DVTABLES_IN_IRAM
|
||||
; enable one option set -> No exception recommended
|
||||
; No exception code in firmware
|
||||
-fno-exceptions
|
||||
-lstdc++
|
||||
; Exception code in firmware /needs much space! 90k
|
||||
; Exception code in firmware /needs much space!
|
||||
; -fexceptions
|
||||
; -lstdc++-exc
|
||||
|
||||
[core_stage]
|
||||
; *** Esp8266 core for Arduino version latest development version
|
||||
platform = espressif8266@2.5.0
|
||||
extends = tasmota_core
|
||||
platform_packages = framework-arduinoespressif8266 @ https://github.com/esp8266/Arduino.git
|
||||
build_flags = ${esp82xx_defaults.build_flags}
|
||||
-DBEARSSL_SSL_BASIC
|
||||
;
|
||||
; *********** Alternative Options, enable only if you know exactly what you do ********
|
||||
; NONOSDK221
|
||||
; -DPIO_FRAMEWORK_ARDUINO_ESPRESSIF_SDK221
|
||||
; NONOSDK22x_190313
|
||||
; -DPIO_FRAMEWORK_ARDUINO_ESPRESSIF_SDK22x_190313
|
||||
; NONOSDK22x_190703 = 2.2.1+100-dev(38a443e) (Tasmota default) (Firmware 2K smaller than NONOSDK22x_191105)
|
||||
-DPIO_FRAMEWORK_ARDUINO_ESPRESSIF_SDK22x_190703
|
||||
; NONOSDK22x_191024 = 2.2.1+111-dev(5ab15d1)
|
||||
; -DPIO_FRAMEWORK_ARDUINO_ESPRESSIF_SDK22x_191024
|
||||
; NONOSDK22x_191105 = 2.2.1+113-dev(bb83b9b)
|
||||
@ -153,19 +140,11 @@ build_flags = ${esp82xx_defaults.build_flags}
|
||||
; -DPIO_FRAMEWORK_ARDUINO_LWIP2_HIGHER_BANDWIDTH
|
||||
; lwIP 2 - Higher Bandwidth Low Memory no Features
|
||||
; -DPIO_FRAMEWORK_ARDUINO_LWIP2_LOW_MEMORY_LOW_FLASH
|
||||
; lwIP 2 - Higher Bandwidth no Features (Tasmota default)
|
||||
-DPIO_FRAMEWORK_ARDUINO_LWIP2_HIGHER_BANDWIDTH_LOW_FLASH
|
||||
; VTABLES in Flash (Tasmota default)
|
||||
-DVTABLES_IN_FLASH
|
||||
; VTABLES in Heap
|
||||
; -DVTABLES_IN_DRAM
|
||||
; VTABLES in IRAM
|
||||
; -DVTABLES_IN_IRAM
|
||||
; enable one option set -> No exception recommended
|
||||
; No exception code in firmware
|
||||
-fno-exceptions
|
||||
-lstdc++
|
||||
; Exception code in firmware /needs much space! 90k
|
||||
; Exception code in firmware /needs much space!
|
||||
; -fexceptions
|
||||
; -lstdc++-exc
|
||||
|
||||
|
@ -11,8 +11,14 @@
|
||||
- Add experimental basic support for Tasmota on ESP32 based on work by Jörg Schüler-Maroldt
|
||||
- Add support for analog anemometer by Matteo Albinola (#8283)
|
||||
- Add support for OpenTherm by Yuriy Sannikov (#8373)
|
||||
- Add support for Thermostat control by arijav (#8212)
|
||||
- Add automatic compression of Rules to achieve ~60% compression, added ``SetOption93 1`` to control caching of rules
|
||||
- Add rule trigger at root level like ``on loadavg<50 do power 2 endon`` after ``state`` command
|
||||
- Change flash access removing support for any Core before 2.6.3
|
||||
- Change HAss discovery by Federico Leoni (#8370)
|
||||
- Change default PWM Frequency to 977 Hz from 223 Hz
|
||||
- Change minimum PWM Frequency from 100 Hz to 40 Hz
|
||||
- Change PWM updated to the latest version of Arduino PR #7231
|
||||
|
||||
### 8.2.0.5 20200425
|
||||
|
||||
|
@ -47,29 +47,23 @@
|
||||
extern "C" {
|
||||
|
||||
// Internal-only calls, not for applications
|
||||
extern void _setPWMPeriodCC(uint32_t cc);
|
||||
extern void _setPWMFreq(uint32_t freq);
|
||||
extern bool _stopPWM(int pin);
|
||||
extern bool _setPWM(int pin, uint32_t cc);
|
||||
extern bool _setPWM(int pin, uint32_t val, uint32_t range);
|
||||
extern int startWaveformClockCycles(uint8_t pin, uint32_t timeHighCycles, uint32_t timeLowCycles, uint32_t runTimeCycles);
|
||||
|
||||
// Maximum delay between IRQs
|
||||
#define MAXIRQUS (10000)
|
||||
|
||||
// Set/clear GPIO 0-15 by bitmask
|
||||
#define SetGPIO(a) do { GPOS = a; } while (0)
|
||||
#define ClearGPIO(a) do { GPOC = a; } while (0)
|
||||
|
||||
// Waveform generator can create tones, PWM, and servos
|
||||
typedef struct {
|
||||
uint32_t nextServiceCycle; // ESP cycle timer when a transition required
|
||||
uint32_t expiryCycle; // For time-limited waveform, the cycle when this waveform must stop
|
||||
uint32_t timeHighCycles; // Currently running waveform period
|
||||
uint32_t timeHighCycles; // Actual running waveform period (adjusted using desiredCycles)
|
||||
uint32_t timeLowCycles; //
|
||||
uint32_t desiredHighCycles; // Currently running waveform period
|
||||
uint32_t desiredLowCycles; //
|
||||
uint32_t gotoTimeHighCycles; // Copied over on the next period to preserve phase
|
||||
uint32_t gotoTimeLowCycles; //
|
||||
uint32_t lastEdge; //
|
||||
uint32_t desiredHighCycles; // Ideal waveform period to drive the error signal
|
||||
uint32_t desiredLowCycles; //
|
||||
uint32_t lastEdge; // Cycle when this generator last changed
|
||||
} Waveform;
|
||||
|
||||
class WVFState {
|
||||
@ -82,7 +76,7 @@ public:
|
||||
uint32_t waveformToEnable = 0; // Message to the NMI handler to start a waveform on a inactive pin
|
||||
uint32_t waveformToDisable = 0; // Message to the NMI handler to disable a pin from waveform generation
|
||||
|
||||
int32_t waveformToChange = -1;
|
||||
uint32_t waveformToChange = 0; // Mask of pin to change. One bit set in main app, cleared when effected in the NMI
|
||||
uint32_t waveformNewHigh = 0;
|
||||
uint32_t waveformNewLow = 0;
|
||||
|
||||
@ -91,8 +85,8 @@ public:
|
||||
// Optimize the NMI inner loop by keeping track of the min and max GPIO that we
|
||||
// are generating. In the common case (1 PWM) these may be the same pin and
|
||||
// we can avoid looking at the other pins.
|
||||
int startPin = 0;
|
||||
int endPin = 0;
|
||||
uint16_t startPin = 0;
|
||||
uint16_t endPin = 0;
|
||||
};
|
||||
static WVFState wvfState;
|
||||
|
||||
@ -107,7 +101,7 @@ static WVFState wvfState;
|
||||
static ICACHE_RAM_ATTR void timer1Interrupt();
|
||||
static bool timerRunning = false;
|
||||
|
||||
static void initTimer() {
|
||||
static __attribute__((noinline)) void initTimer() {
|
||||
if (!timerRunning) {
|
||||
timer1_disable();
|
||||
ETS_FRC_TIMER1_INTR_ATTACH(NULL, NULL);
|
||||
@ -138,21 +132,22 @@ static ICACHE_RAM_ATTR void forceTimerInterrupt() {
|
||||
constexpr int maxPWMs = 8;
|
||||
|
||||
// PWM machine state
|
||||
typedef struct {
|
||||
typedef struct PWMState {
|
||||
uint32_t mask; // Bitmask of active pins
|
||||
uint32_t cnt; // How many entries
|
||||
uint32_t idx; // Where the state machine is along the list
|
||||
uint8_t pin[maxPWMs + 1];
|
||||
uint32_t delta[maxPWMs + 1];
|
||||
uint32_t nextServiceCycle; // Clock cycle for next step
|
||||
struct PWMState *pwmUpdate; // Set by main code, cleared by ISR
|
||||
} PWMState;
|
||||
|
||||
static PWMState pwmState;
|
||||
static PWMState *pwmUpdate = nullptr; // Set by main code, cleared by ISR
|
||||
static uint32_t pwmPeriod = microsecondsToClockCycles(1000000UL) / 1000;
|
||||
|
||||
static uint32_t _pwmPeriod = microsecondsToClockCycles(1000000UL) / 1000;
|
||||
|
||||
|
||||
// If there are no more scheduled activities, shut down Timer 1.
|
||||
// Otherwise, do nothing.
|
||||
static ICACHE_RAM_ATTR void disableIdleTimer() {
|
||||
if (timerRunning && !wvfState.waveformEnabled && !pwmState.cnt && !wvfState.timer1CB) {
|
||||
ETS_FRC_TIMER1_NMI_INTR_ATTACH(NULL);
|
||||
@ -162,62 +157,78 @@ static ICACHE_RAM_ATTR void disableIdleTimer() {
|
||||
}
|
||||
}
|
||||
|
||||
// Notify the NMI that a new PWM state is available through the mailbox.
|
||||
// Wait for mailbox to be emptied (either busy or delay() as needed)
|
||||
static ICACHE_RAM_ATTR void _notifyPWM(PWMState *p, bool idle) {
|
||||
p->pwmUpdate = nullptr;
|
||||
pwmState.pwmUpdate = p;
|
||||
MEMBARRIER();
|
||||
forceTimerInterrupt();
|
||||
while (pwmState.pwmUpdate) {
|
||||
if (idle) {
|
||||
delay(0);
|
||||
}
|
||||
MEMBARRIER();
|
||||
}
|
||||
}
|
||||
|
||||
static void _addPWMtoList(PWMState &p, int pin, uint32_t val, uint32_t range);
|
||||
|
||||
// Called when analogWriteFreq() changed to update the PWM total period
|
||||
void _setPWMPeriodCC(uint32_t cc) {
|
||||
if (cc == pwmPeriod) {
|
||||
return;
|
||||
void _setPWMFreq(uint32_t freq) {
|
||||
// Convert frequency into clock cycles
|
||||
uint32_t cc = microsecondsToClockCycles(1000000UL) / freq;
|
||||
|
||||
// Simple static adjustment to bring period closer to requested due to overhead
|
||||
#if F_CPU == 80000000
|
||||
cc -= microsecondsToClockCycles(2);
|
||||
#else
|
||||
cc -= microsecondsToClockCycles(1);
|
||||
#endif
|
||||
|
||||
if (cc == _pwmPeriod) {
|
||||
return; // No change
|
||||
}
|
||||
|
||||
_pwmPeriod = cc;
|
||||
|
||||
if (pwmState.cnt) {
|
||||
// Adjust any running ones to the best of our abilities by scaling them
|
||||
// Used FP math for speed and code size
|
||||
uint64_t oldCC64p0 = ((uint64_t)pwmPeriod);
|
||||
uint64_t newCC64p16 = ((uint64_t)cc) << 16;
|
||||
uint64_t ratio64p16 = (newCC64p16 / oldCC64p0);
|
||||
PWMState p; // The working copy since we can't edit the one in use
|
||||
p = pwmState;
|
||||
uint32_t ttl = 0;
|
||||
for (uint32_t i = 0; i < p.cnt; i++) {
|
||||
uint64_t val64p16 = ((uint64_t)p.delta[i]) << 16;
|
||||
uint64_t newVal64p32 = val64p16 * ratio64p16;
|
||||
p.delta[i] = newVal64p32 >> 32;
|
||||
ttl += p.delta[i];
|
||||
p.cnt = 0;
|
||||
for (uint32_t i = 0; i < pwmState.cnt; i++) {
|
||||
auto pin = pwmState.pin[i];
|
||||
_addPWMtoList(p, pin, wvfState.waveform[pin].desiredHighCycles, wvfState.waveform[pin].desiredLowCycles);
|
||||
}
|
||||
p.delta[p.cnt] = cc - ttl; // Final cleanup exactly cc total cycles
|
||||
// Update and wait for mailbox to be emptied
|
||||
pwmUpdate = &p;
|
||||
MEMBARRIER();
|
||||
forceTimerInterrupt();
|
||||
while (pwmUpdate) {
|
||||
delay(0);
|
||||
// No mem barrier. The external function call guarantees it's re-read
|
||||
}
|
||||
initTimer();
|
||||
_notifyPWM(&p, true);
|
||||
disableIdleTimer();
|
||||
}
|
||||
pwmPeriod = cc;
|
||||
}
|
||||
|
||||
// Helper routine to remove an entry from the state machine
|
||||
static ICACHE_RAM_ATTR void _removePWMEntry(int pin, PWMState *p) {
|
||||
uint32_t i;
|
||||
|
||||
// Find the pin to pull out...
|
||||
for (i = 0; p->pin[i] != pin; i++) { /* no-op */ }
|
||||
auto delta = p->delta[i];
|
||||
|
||||
// Add the removed previous pin delta to preserve absolute position
|
||||
p->delta[i+1] += delta;
|
||||
|
||||
// Move everything back one
|
||||
for (i++; i <= p->cnt; i++) {
|
||||
p->pin[i-1] = p->pin[i];
|
||||
p->delta[i-1] = p->delta[i];
|
||||
// and clean up any marked-off entries
|
||||
static void _cleanAndRemovePWM(PWMState *p, int pin) {
|
||||
uint32_t leftover = 0;
|
||||
uint32_t in, out;
|
||||
for (in = 0, out = 0; in < p->cnt; in++) {
|
||||
if ((p->pin[in] != pin) && (p->mask & (1<<p->pin[in]))) {
|
||||
p->pin[out] = p->pin[in];
|
||||
p->delta[out] = p->delta[in] + leftover;
|
||||
leftover = 0;
|
||||
out++;
|
||||
} else {
|
||||
leftover += p->delta[in];
|
||||
p->mask &= ~(1<<p->pin[in]);
|
||||
}
|
||||
}
|
||||
// Remove the pin from the active list
|
||||
p->mask &= ~(1<<pin);
|
||||
p->cnt--;
|
||||
p->cnt = out;
|
||||
// Final pin is never used: p->pin[out] = 0xff;
|
||||
p->delta[out] = p->delta[in] + leftover;
|
||||
}
|
||||
|
||||
// Called by analogWrite(0/100%) to disable PWM on a specific pin
|
||||
|
||||
// Disable PWM on a specific pin (i.e. when a digitalWrite or analogWrite(0%/100%))
|
||||
ICACHE_RAM_ATTR bool _stopPWM(int pin) {
|
||||
if (!((1<<pin) & pwmState.mask)) {
|
||||
return false; // Pin not actually active
|
||||
@ -225,67 +236,88 @@ ICACHE_RAM_ATTR bool _stopPWM(int pin) {
|
||||
|
||||
PWMState p; // The working copy since we can't edit the one in use
|
||||
p = pwmState;
|
||||
_removePWMEntry(pin, &p);
|
||||
|
||||
// Update and wait for mailbox to be emptied
|
||||
pwmUpdate = &p;
|
||||
MEMBARRIER();
|
||||
forceTimerInterrupt();
|
||||
while (pwmUpdate) {
|
||||
MEMBARRIER();
|
||||
/* Busy wait, could be in ISR */
|
||||
// In _stopPWM we just clear the mask but keep everything else
|
||||
// untouched to save IRAM. The main startPWM will handle cleanup.
|
||||
p.mask &= ~(1<<pin);
|
||||
if (!p.mask) {
|
||||
// If all have been stopped, then turn PWM off completely
|
||||
p.cnt = 0;
|
||||
}
|
||||
|
||||
// Update and wait for mailbox to be emptied, no delay (could be in ISR)
|
||||
_notifyPWM(&p, false);
|
||||
// Possibly shut down the timer completely if we're done
|
||||
disableIdleTimer();
|
||||
return true;
|
||||
}
|
||||
|
||||
// Called by analogWrite(1...99%) to set the PWM duty in clock cycles
|
||||
bool _setPWM(int pin, uint32_t cc) {
|
||||
stopWaveform(pin);
|
||||
PWMState p; // Working copy
|
||||
p = pwmState;
|
||||
// Get rid of any entries for this pin
|
||||
if ((1<<pin) & p.mask) {
|
||||
_removePWMEntry(pin, &p);
|
||||
static void _addPWMtoList(PWMState &p, int pin, uint32_t val, uint32_t range) {
|
||||
// Stash the val and range so we can re-evaluate the fraction
|
||||
// should the user change PWM frequency. This allows us to
|
||||
// give as great a precision as possible. We know by construction
|
||||
// that the waveform for this pin will be inactive so we can borrow
|
||||
// memory from that structure.
|
||||
wvfState.waveform[pin].desiredHighCycles = val; // Numerator == high
|
||||
wvfState.waveform[pin].desiredLowCycles = range; // Denominator == low
|
||||
|
||||
uint32_t cc = (_pwmPeriod * val) / range;
|
||||
|
||||
if (cc == 0) {
|
||||
_stopPWM(pin);
|
||||
digitalWrite(pin, LOW);
|
||||
return;
|
||||
} else if (cc >= _pwmPeriod) {
|
||||
_stopPWM(pin);
|
||||
digitalWrite(pin, HIGH);
|
||||
return;
|
||||
}
|
||||
// And add it to the list, in order
|
||||
if (p.cnt >= maxPWMs) {
|
||||
return false; // No space left
|
||||
} else if (p.cnt == 0) {
|
||||
|
||||
if (p.cnt == 0) {
|
||||
// Starting up from scratch, special case 1st element and PWM period
|
||||
p.pin[0] = pin;
|
||||
p.delta[0] = cc;
|
||||
p.pin[1] = 0xff;
|
||||
p.delta[1] = pwmPeriod - cc;
|
||||
p.cnt = 1;
|
||||
p.mask = 1<<pin;
|
||||
// Final pin is never used: p.pin[1] = 0xff;
|
||||
p.delta[1] = _pwmPeriod - cc;
|
||||
} else {
|
||||
uint32_t ttl=0;
|
||||
uint32_t ttl = 0;
|
||||
uint32_t i;
|
||||
// Skip along until we're at the spot to insert
|
||||
for (i=0; (i <= p.cnt) && (ttl + p.delta[i] < cc); i++) {
|
||||
ttl += p.delta[i];
|
||||
}
|
||||
// Shift everything out by one to make space for new edge
|
||||
memmove(&p.pin[i + 1], &p.pin[i], (1 + p.cnt - i) * sizeof(p.pin[0]));
|
||||
memmove(&p.delta[i + 1], &p.delta[i], (1 + p.cnt - i) * sizeof(p.delta[0]));
|
||||
for (int32_t j = p.cnt; j >= (int)i; j--) {
|
||||
p.pin[j + 1] = p.pin[j];
|
||||
p.delta[j + 1] = p.delta[j];
|
||||
}
|
||||
int off = cc - ttl; // The delta from the last edge to the one we're inserting
|
||||
p.pin[i] = pin;
|
||||
p.delta[i] = off; // Add the delta to this new pin
|
||||
p.delta[i + 1] -= off; // And subtract it from the follower to keep sum(deltas) constant
|
||||
p.cnt++;
|
||||
p.mask |= 1<<pin;
|
||||
}
|
||||
p.cnt++;
|
||||
p.mask |= 1<<pin;
|
||||
}
|
||||
|
||||
// Called by analogWrite(1...99%) to set the PWM duty in clock cycles
|
||||
bool _setPWM(int pin, uint32_t val, uint32_t range) {
|
||||
stopWaveform(pin);
|
||||
PWMState p; // Working copy
|
||||
p = pwmState;
|
||||
// Get rid of any entries for this pin
|
||||
_cleanAndRemovePWM(&p, pin);
|
||||
// And add it to the list, in order
|
||||
if (p.cnt >= maxPWMs) {
|
||||
return false; // No space left
|
||||
}
|
||||
|
||||
_addPWMtoList(p, pin, val, range);
|
||||
|
||||
// Set mailbox and wait for ISR to copy it over
|
||||
pwmUpdate = &p;
|
||||
MEMBARRIER();
|
||||
initTimer();
|
||||
forceTimerInterrupt();
|
||||
while (pwmUpdate) {
|
||||
delay(0);
|
||||
}
|
||||
_notifyPWM(&p, true);
|
||||
disableIdleTimer();
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -311,22 +343,22 @@ int startWaveformClockCycles(uint8_t pin, uint32_t timeHighCycles, uint32_t time
|
||||
uint32_t mask = 1<<pin;
|
||||
MEMBARRIER();
|
||||
if (wvfState.waveformEnabled & mask) {
|
||||
wvfState.waveformNewHigh = timeHighCycles;
|
||||
wvfState.waveformNewLow = timeLowCycles;
|
||||
MEMBARRIER();
|
||||
wvfState.waveformToChange = pin;
|
||||
while (wvfState.waveformToChange >= 0) {
|
||||
// Make sure no waveform changes are waiting to be applied
|
||||
while (wvfState.waveformToChange) {
|
||||
delay(0); // Wait for waveform to update
|
||||
// No mem barrier here, the call to a global function implies global state updated
|
||||
}
|
||||
wvfState.waveformNewHigh = timeHighCycles;
|
||||
wvfState.waveformNewLow = timeLowCycles;
|
||||
MEMBARRIER();
|
||||
wvfState.waveformToChange = mask;
|
||||
// The waveform will be updated some time in the future on the next period for the signal
|
||||
} else { // if (!(wvfState.waveformEnabled & mask)) {
|
||||
wave->timeHighCycles = timeHighCycles;
|
||||
wave->desiredHighCycles = timeHighCycles;
|
||||
wave->timeLowCycles = timeLowCycles;
|
||||
wave->desiredHighCycles = wave->timeHighCycles;
|
||||
wave->desiredLowCycles = wave->timeLowCycles;
|
||||
wave->desiredLowCycles = timeLowCycles;
|
||||
wave->lastEdge = 0;
|
||||
wave->gotoTimeHighCycles = wave->timeHighCycles;
|
||||
wave->gotoTimeLowCycles = wave->timeLowCycles; // Actually set the pin high or low in the IRQ service to guarantee times
|
||||
wave->nextServiceCycle = ESP.getCycleCount() + microsecondsToClockCycles(1);
|
||||
wvfState.waveformToEnable |= mask;
|
||||
MEMBARRIER();
|
||||
@ -355,10 +387,10 @@ void setTimer1Callback(uint32_t (*fn)()) {
|
||||
|
||||
// Speed critical bits
|
||||
#pragma GCC optimize ("O2")
|
||||
|
||||
// Normally would not want two copies like this, but due to different
|
||||
// optimization levels the inline attribute gets lost if we try the
|
||||
// other version.
|
||||
|
||||
static inline ICACHE_RAM_ATTR uint32_t GetCycleCountIRQ() {
|
||||
uint32_t ccount;
|
||||
__asm__ __volatile__("rsr %0,ccount":"=a"(ccount));
|
||||
@ -380,8 +412,13 @@ int ICACHE_RAM_ATTR stopWaveform(uint8_t pin) {
|
||||
}
|
||||
// If user sends in a pin >16 but <32, this will always point to a 0 bit
|
||||
// If they send >=32, then the shift will result in 0 and it will also return false
|
||||
if (wvfState.waveformEnabled & (1UL << pin)) {
|
||||
wvfState.waveformToDisable = 1UL << pin;
|
||||
uint32_t mask = 1<<pin;
|
||||
if (wvfState.waveformEnabled & mask) {
|
||||
wvfState.waveformToDisable = mask;
|
||||
// Cancel any pending updates for this waveform, too.
|
||||
if (wvfState.waveformToChange & mask) {
|
||||
wvfState.waveformToChange = 0;
|
||||
}
|
||||
forceTimerInterrupt();
|
||||
while (wvfState.waveformToDisable) {
|
||||
MEMBARRIER(); // If it wasn't written yet, it has to be by now
|
||||
@ -407,15 +444,6 @@ int ICACHE_RAM_ATTR stopWaveform(uint8_t pin) {
|
||||
#define adjust(x) ((x) >> (turbo ? 0 : 1))
|
||||
#endif
|
||||
|
||||
#define ENABLE_ADJUST // Adjust takes 36 bytes
|
||||
#define ENABLE_FEEDBACK // Feedback costs 68 bytes
|
||||
#define ENABLE_PWM // PWM takes 160 bytes
|
||||
|
||||
#ifndef ENABLE_ADJUST
|
||||
#undef adjust
|
||||
#define adjust(x) (x)
|
||||
#endif
|
||||
|
||||
|
||||
static ICACHE_RAM_ATTR void timer1Interrupt() {
|
||||
// Flag if the core is at 160 MHz, for use by adjust()
|
||||
@ -435,19 +463,12 @@ static ICACHE_RAM_ATTR void timer1Interrupt() {
|
||||
wvfState.startPin = __builtin_ffs(wvfState.waveformEnabled) - 1;
|
||||
// Find the last bit by subtracting off GCC's count-leading-zeros (no offset in this one)
|
||||
wvfState.endPin = 32 - __builtin_clz(wvfState.waveformEnabled);
|
||||
#ifdef ENABLE_PWM
|
||||
} else if (!pwmState.cnt && pwmUpdate) {
|
||||
} else if (!pwmState.cnt && pwmState.pwmUpdate) {
|
||||
// Start up the PWM generator by copying from the mailbox
|
||||
pwmState.cnt = 1;
|
||||
pwmState.idx = 1; // Ensure copy this cycle, cause it to start at t=0
|
||||
pwmState.nextServiceCycle = GetCycleCountIRQ(); // Do it this loop!
|
||||
// No need for mem barrier here. Global must be written by IRQ exit
|
||||
#endif
|
||||
} else if (wvfState.waveformToChange >= 0) {
|
||||
wvfState.waveform[wvfState.waveformToChange].gotoTimeHighCycles = wvfState.waveformNewHigh;
|
||||
wvfState.waveform[wvfState.waveformToChange].gotoTimeLowCycles = wvfState.waveformNewLow;
|
||||
wvfState.waveformToChange = -1;
|
||||
// No need for memory barrier here. The global has to be written before exit the ISR.
|
||||
}
|
||||
|
||||
bool done = false;
|
||||
@ -455,35 +476,32 @@ static ICACHE_RAM_ATTR void timer1Interrupt() {
|
||||
do {
|
||||
nextEventCycles = microsecondsToClockCycles(MAXIRQUS);
|
||||
|
||||
#ifdef ENABLE_PWM
|
||||
// PWM state machine implementation
|
||||
if (pwmState.cnt) {
|
||||
uint32_t now = GetCycleCountIRQ();
|
||||
int32_t cyclesToGo = pwmState.nextServiceCycle - now;
|
||||
int32_t cyclesToGo = pwmState.nextServiceCycle - GetCycleCountIRQ();
|
||||
if (cyclesToGo < 0) {
|
||||
if (pwmState.idx == pwmState.cnt) { // Start of pulses, possibly copy new
|
||||
if (pwmUpdate) {
|
||||
// Do the memory copy from temp to global and clear mailbox
|
||||
pwmState = *(PWMState*)pwmUpdate;
|
||||
pwmUpdate = nullptr;
|
||||
}
|
||||
GPOS = pwmState.mask; // Set all active pins high
|
||||
// GPIO16 isn't the same as the others
|
||||
if (pwmState.mask & (1<<16)) {
|
||||
GP16O = 1;
|
||||
}
|
||||
pwmState.idx = 0;
|
||||
if (pwmState.pwmUpdate) {
|
||||
// Do the memory copy from temp to global and clear mailbox
|
||||
pwmState = *(PWMState*)pwmState.pwmUpdate;
|
||||
}
|
||||
GPOS = pwmState.mask; // Set all active pins high
|
||||
if (pwmState.mask & (1<<16)) {
|
||||
GP16O = 1;
|
||||
}
|
||||
pwmState.idx = 0;
|
||||
} else {
|
||||
do {
|
||||
// Drop the pin at this edge
|
||||
GPOC = 1<<pwmState.pin[pwmState.idx];
|
||||
// GPIO16 still needs manual work
|
||||
if (pwmState.pin[pwmState.idx] == 16) {
|
||||
GP16O = 0;
|
||||
}
|
||||
pwmState.idx++;
|
||||
// Any other pins at this same PWM value will have delta==0, drop them too.
|
||||
} while (pwmState.delta[pwmState.idx] == 0);
|
||||
do {
|
||||
// Drop the pin at this edge
|
||||
if (pwmState.mask & (1<<pwmState.pin[pwmState.idx])) {
|
||||
GPOC = 1<<pwmState.pin[pwmState.idx];
|
||||
if (pwmState.pin[pwmState.idx] == 16) {
|
||||
GP16O = 0;
|
||||
}
|
||||
}
|
||||
pwmState.idx++;
|
||||
// Any other pins at this same PWM value will have delta==0, drop them too.
|
||||
} while (pwmState.delta[pwmState.idx] == 0);
|
||||
}
|
||||
// Preserve duty cycle over PWM period by using now+xxx instead of += delta
|
||||
cyclesToGo = adjust(pwmState.delta[pwmState.idx]);
|
||||
@ -491,7 +509,6 @@ static ICACHE_RAM_ATTR void timer1Interrupt() {
|
||||
}
|
||||
nextEventCycles = min_u32(nextEventCycles, cyclesToGo);
|
||||
}
|
||||
#endif
|
||||
|
||||
for (auto i = wvfState.startPin; i <= wvfState.endPin; i++) {
|
||||
uint32_t mask = 1<<i;
|
||||
@ -509,12 +526,11 @@ static ICACHE_RAM_ATTR void timer1Interrupt() {
|
||||
int32_t expiryToGo = wave->expiryCycle - now;
|
||||
if (expiryToGo < 0) {
|
||||
// Done, remove!
|
||||
wvfState.waveformEnabled &= ~mask;
|
||||
if (i == 16) {
|
||||
GP16O = 0;
|
||||
} else {
|
||||
ClearGPIO(mask);
|
||||
}
|
||||
}
|
||||
GPOC = mask;
|
||||
wvfState.waveformEnabled &= ~mask;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
@ -528,39 +544,33 @@ static ICACHE_RAM_ATTR void timer1Interrupt() {
|
||||
wvfState.waveformState ^= mask;
|
||||
if (wvfState.waveformState & mask) {
|
||||
if (i == 16) {
|
||||
GP16O = 1; // GPIO16 write slow as it's RMW
|
||||
} else {
|
||||
SetGPIO(mask);
|
||||
GP16O = 1;
|
||||
}
|
||||
if (wave->gotoTimeHighCycles) {
|
||||
GPOS = mask;
|
||||
|
||||
if (wvfState.waveformToChange & mask) {
|
||||
// Copy over next full-cycle timings
|
||||
wave->timeHighCycles = wave->gotoTimeHighCycles;
|
||||
wave->desiredHighCycles = wave->gotoTimeHighCycles;
|
||||
wave->timeLowCycles = wave->gotoTimeLowCycles;
|
||||
wave->desiredLowCycles = wave->gotoTimeLowCycles;
|
||||
wave->gotoTimeHighCycles = 0;
|
||||
} else {
|
||||
#ifdef ENABLE_FEEDBACK
|
||||
if (wave->lastEdge) {
|
||||
desired = wave->desiredLowCycles;
|
||||
timeToUpdate = &wave->timeLowCycles;
|
||||
}
|
||||
wave->timeHighCycles = wvfState.waveformNewHigh;
|
||||
wave->desiredHighCycles = wvfState.waveformNewHigh;
|
||||
wave->timeLowCycles = wvfState.waveformNewLow;
|
||||
wave->desiredLowCycles = wvfState.waveformNewLow;
|
||||
wave->lastEdge = 0;
|
||||
wvfState.waveformToChange = 0;
|
||||
}
|
||||
if (wave->lastEdge) {
|
||||
desired = wave->desiredLowCycles;
|
||||
timeToUpdate = &wave->timeLowCycles;
|
||||
}
|
||||
#endif
|
||||
nextEdgeCycles = wave->timeHighCycles;
|
||||
} else {
|
||||
if (i == 16) {
|
||||
GP16O = 0; // GPIO16 write slow as it's RMW
|
||||
} else {
|
||||
ClearGPIO(mask);
|
||||
GP16O = 0;
|
||||
}
|
||||
#ifdef ENABLE_FEEDBACK
|
||||
GPOC = mask;
|
||||
desired = wave->desiredHighCycles;
|
||||
timeToUpdate = &wave->timeHighCycles;
|
||||
#endif
|
||||
nextEdgeCycles = wave->timeLowCycles;
|
||||
}
|
||||
#ifdef ENABLE_FEEDBACK
|
||||
if (desired) {
|
||||
desired = adjust(desired);
|
||||
int32_t err = desired - (now - wave->lastEdge);
|
||||
@ -569,7 +579,6 @@ static ICACHE_RAM_ATTR void timer1Interrupt() {
|
||||
*timeToUpdate += err;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
nextEdgeCycles = adjust(nextEdgeCycles);
|
||||
wave->nextServiceCycle = now + nextEdgeCycles;
|
||||
nextEventCycles = min_u32(nextEventCycles, nextEdgeCycles);
|
||||
@ -599,7 +608,6 @@ static ICACHE_RAM_ATTR void timer1Interrupt() {
|
||||
|
||||
// Do it here instead of global function to save time and because we know it's edge-IRQ
|
||||
T1L = nextEventCycles >> (turbo ? 1 : 0);
|
||||
TEIE |= TEIE1; // Edge int enable
|
||||
}
|
||||
|
||||
};
|
||||
|
@ -34,9 +34,9 @@
|
||||
extern "C" {
|
||||
|
||||
// Internal-only calls, not for applications
|
||||
extern void _setPWMPeriodCC(uint32_t cc);
|
||||
extern void _setPWMFreq(uint32_t freq);
|
||||
extern bool _stopPWM(int pin);
|
||||
extern bool _setPWM(int pin, uint32_t cc);
|
||||
extern bool _setPWM(int pin, uint32_t val, uint32_t range);
|
||||
extern void resetPins();
|
||||
|
||||
volatile uint32_t* const esp8266_gpioToFn[16] PROGMEM = { &GPF0, &GPF1, &GPF2, &GPF3, &GPF4, &GPF5, &GPF6, &GPF7, &GPF8, &GPF9, &GPF10, &GPF11, &GPF12, &GPF13, &GPF14, &GPF15 };
|
||||
|
@ -29,13 +29,11 @@
|
||||
extern "C" {
|
||||
|
||||
// Internal-only calls, not for applications
|
||||
extern void _setPWMPeriodCC(uint32_t cc);
|
||||
extern void _setPWMFreq(uint32_t freq);
|
||||
extern bool _stopPWM(int pin);
|
||||
extern bool _setPWM(int pin, uint32_t cc);
|
||||
extern bool _setPWM(int pin, uint32_t val, uint32_t range);
|
||||
|
||||
static uint32_t analogMap = 0;
|
||||
static int32_t analogScale = PWMRANGE;
|
||||
static uint16_t analogFreq = 1000;
|
||||
|
||||
extern void __analogWriteRange(uint32_t range) {
|
||||
if (range > 0) {
|
||||
@ -43,17 +41,15 @@ extern void __analogWriteRange(uint32_t range) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
extern void __analogWriteFreq(uint32_t freq) {
|
||||
if (freq < 100) {
|
||||
analogFreq = 100;
|
||||
if (freq < 40) {
|
||||
freq = 40;
|
||||
} else if (freq > 60000) {
|
||||
analogFreq = 60000;
|
||||
freq = 60000;
|
||||
} else {
|
||||
analogFreq = freq;
|
||||
freq = freq;
|
||||
}
|
||||
uint32_t analogPeriod = microsecondsToClockCycles(1000000UL) / analogFreq;
|
||||
_setPWMPeriodCC(analogPeriod);
|
||||
_setPWMFreq(freq);
|
||||
}
|
||||
|
||||
extern void __analogWrite(uint8_t pin, int val) {
|
||||
@ -61,28 +57,14 @@ extern void __analogWrite(uint8_t pin, int val) {
|
||||
return;
|
||||
}
|
||||
|
||||
uint32_t analogPeriod = microsecondsToClockCycles(1000000UL) / analogFreq;
|
||||
_setPWMPeriodCC(analogPeriod);
|
||||
if (val < 0) {
|
||||
val = 0;
|
||||
} else if (val > analogScale) {
|
||||
val = analogScale;
|
||||
}
|
||||
|
||||
analogMap &= ~(1 << pin);
|
||||
uint32_t high = (analogPeriod * val) / analogScale;
|
||||
uint32_t low = analogPeriod - high;
|
||||
pinMode(pin, OUTPUT);
|
||||
if (low == 0) {
|
||||
_stopPWM(pin);
|
||||
digitalWrite(pin, HIGH);
|
||||
} else if (high == 0) {
|
||||
_stopPWM(pin);
|
||||
digitalWrite(pin, LOW);
|
||||
} else {
|
||||
_setPWM(pin, high);
|
||||
analogMap |= (1 << pin);
|
||||
}
|
||||
_setPWM(pin, val, analogScale);
|
||||
}
|
||||
|
||||
extern void analogWrite(uint8_t pin, int val) __attribute__((weak, alias("__analogWrite")));
|
||||
|
@ -690,40 +690,39 @@ const float kSpeedConversionFactor[] = {1, // none
|
||||
// xdrv_02_webserver.ino
|
||||
#ifdef USE_WEBSERVER
|
||||
// {s} = <tr><th>, {m} = </th><td>, {e} = </td></tr>
|
||||
const char HTTP_SNS_TEMP[] PROGMEM = "{s}%s " D_TEMPERATURE "{m}%s°%c{e}";
|
||||
const char HTTP_SNS_HUM[] PROGMEM = "{s}%s " D_HUMIDITY "{m}%s%%{e}";
|
||||
const char HTTP_SNS_DEW[] PROGMEM = "{s}%s " D_DEWPOINT "{m}%s°%c{e}";
|
||||
const char HTTP_SNS_PRESSURE[] PROGMEM = "{s}%s " D_PRESSURE "{m}%s %s{e}";
|
||||
const char HTTP_SNS_SEAPRESSURE[] PROGMEM = "{s}%s " D_PRESSUREATSEALEVEL "{m}%s %s{e}";
|
||||
const char HTTP_SNS_ANALOG[] PROGMEM = "{s}%s " D_ANALOG_INPUT "%d{m}%d{e}";
|
||||
const char HTTP_SNS_ILLUMINANCE[] PROGMEM = "{s}%s " D_ILLUMINANCE "{m}%d " D_UNIT_LUX "{e}";
|
||||
const char HTTP_SNS_CO2[] PROGMEM = "{s}%s " D_CO2 "{m}%d " D_UNIT_PARTS_PER_MILLION "{e}";
|
||||
const char HTTP_SNS_CO2EAVG[] PROGMEM = "{s}%s " D_ECO2 "{m}%d " D_UNIT_PARTS_PER_MILLION "{e}";
|
||||
const char HTTP_SNS_GALLONS[] PROGMEM = "{s}%s " D_TOTAL_USAGE "{m}%s " D_UNIT_GALLONS " {e}";
|
||||
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 %%{e}";
|
||||
const char HTTP_SNS_RANGE[] PROGMEM = "{s}%s " D_RANGE "{m}%d{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}";
|
||||
const char HTTP_SNS_SEAPRESSURE[] PROGMEM = "{s}%s " D_PRESSUREATSEALEVEL "{m}%s " "%s{e}";
|
||||
const char HTTP_SNS_ANALOG[] PROGMEM = "{s}%s " D_ANALOG_INPUT "%d{m}%d" "{e}";
|
||||
const char HTTP_SNS_ILLUMINANCE[] PROGMEM = "{s}%s " D_ILLUMINANCE "{m}%d " D_UNIT_LUX "{e}";
|
||||
const char HTTP_SNS_CO2[] PROGMEM = "{s}%s " D_CO2 "{m}%d " D_UNIT_PARTS_PER_MILLION "{e}";
|
||||
const char HTTP_SNS_CO2EAVG[] PROGMEM = "{s}%s " D_ECO2 "{m}%d " D_UNIT_PARTS_PER_MILLION "{e}";
|
||||
const char HTTP_SNS_GALLONS[] PROGMEM = "{s}%s " D_TOTAL_USAGE "{m}%s " D_UNIT_GALLONS "{e}";
|
||||
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_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}";
|
||||
const char HTTP_SNS_ENERGY_TOTAL[] PROGMEM = "{s}" D_ENERGY_TOTAL "{m}%s " D_UNIT_KILOWATTHOUR "{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}";
|
||||
const char HTTP_SNS_ENERGY_TOTAL[] PROGMEM = "{s}" D_ENERGY_TOTAL "{m}%s " D_UNIT_KILOWATTHOUR "{e}";
|
||||
|
||||
const char S_MAIN_MENU[] PROGMEM = D_MAIN_MENU;
|
||||
const char S_CONFIGURATION[] PROGMEM = D_CONFIGURATION;
|
||||
const char S_CONFIGURE_TEMPLATE[] PROGMEM = D_CONFIGURE_TEMPLATE;
|
||||
const char S_CONFIGURE_MODULE[] PROGMEM = D_CONFIGURE_MODULE;
|
||||
const char S_CONFIGURE_WIFI[] PROGMEM = D_CONFIGURE_WIFI;
|
||||
const char S_NO_NETWORKS_FOUND[] PROGMEM = D_NO_NETWORKS_FOUND;
|
||||
const char S_CONFIGURE_LOGGING[] PROGMEM = D_CONFIGURE_LOGGING;
|
||||
const char S_CONFIGURE_OTHER[] PROGMEM = D_CONFIGURE_OTHER;
|
||||
const char S_SAVE_CONFIGURATION[] PROGMEM = D_SAVE_CONFIGURATION;
|
||||
const char S_RESET_CONFIGURATION[] PROGMEM = D_RESET_CONFIGURATION;
|
||||
const char S_RESTORE_CONFIGURATION[] PROGMEM = D_RESTORE_CONFIGURATION;
|
||||
const char S_FIRMWARE_UPGRADE[] PROGMEM = D_FIRMWARE_UPGRADE;
|
||||
const char S_CONSOLE[] PROGMEM = D_CONSOLE;
|
||||
const char S_INFORMATION[] PROGMEM = D_INFORMATION;
|
||||
const char S_RESTART[] PROGMEM = D_RESTART;
|
||||
const char S_MAIN_MENU[] PROGMEM = D_MAIN_MENU;
|
||||
const char S_CONFIGURATION[] PROGMEM = D_CONFIGURATION;
|
||||
const char S_CONFIGURE_TEMPLATE[] PROGMEM = D_CONFIGURE_TEMPLATE;
|
||||
const char S_CONFIGURE_MODULE[] PROGMEM = D_CONFIGURE_MODULE;
|
||||
const char S_CONFIGURE_WIFI[] PROGMEM = D_CONFIGURE_WIFI;
|
||||
const char S_NO_NETWORKS_FOUND[] PROGMEM = D_NO_NETWORKS_FOUND;
|
||||
const char S_CONFIGURE_LOGGING[] PROGMEM = D_CONFIGURE_LOGGING;
|
||||
const char S_CONFIGURE_OTHER[] PROGMEM = D_CONFIGURE_OTHER;
|
||||
const char S_SAVE_CONFIGURATION[] PROGMEM = D_SAVE_CONFIGURATION;
|
||||
const char S_RESET_CONFIGURATION[] PROGMEM = D_RESET_CONFIGURATION;
|
||||
const char S_RESTORE_CONFIGURATION[] PROGMEM = D_RESTORE_CONFIGURATION;
|
||||
const char S_FIRMWARE_UPGRADE[] PROGMEM = D_FIRMWARE_UPGRADE;
|
||||
const char S_CONSOLE[] PROGMEM = D_CONSOLE;
|
||||
const char S_INFORMATION[] PROGMEM = D_INFORMATION;
|
||||
const char S_RESTART[] PROGMEM = D_RESTART;
|
||||
#endif // USE_WEBSERVER
|
||||
|
||||
const uint32_t MARKER_START = 0x5AA55AA5;
|
||||
|
@ -28,7 +28,7 @@
|
||||
* Use online command StateText to translate ON, OFF, HOLD and TOGGLE.
|
||||
* Use online command Prefix to translate cmnd, stat and tele.
|
||||
*
|
||||
* Updated until v7.1.2.4
|
||||
* Updated until v8.2.0.6
|
||||
\*********************************************************************/
|
||||
|
||||
//#define LANGUAGE_MODULE_NAME // Enable to display "Module Generic" (ie Spanish), Disable to display "Generic Module" (ie English)
|
||||
@ -115,7 +115,7 @@
|
||||
#define D_LIGHT "Светлина"
|
||||
#define D_LWT "LWT"
|
||||
#define D_MODULE "Модул"
|
||||
#define D_MOISTURE "Moisture"
|
||||
#define D_MOISTURE "Влага"
|
||||
#define D_MQTT "MQTT"
|
||||
#define D_MULTI_PRESS "неколкократно натискане"
|
||||
#define D_NOISE "Шум"
|
||||
@ -138,7 +138,7 @@
|
||||
#define D_PROGRAM_SIZE "Размер на програмата"
|
||||
#define D_PROJECT "Проект"
|
||||
#define D_RAIN "Дъжд"
|
||||
#define D_RANGE "Range"
|
||||
#define D_RANGE "Обхват"
|
||||
#define D_RECEIVED "Получено"
|
||||
#define D_RESTART "Рестарт"
|
||||
#define D_RESTARTING "Рестартиране"
|
||||
@ -357,7 +357,7 @@
|
||||
#define D_UPLOAD_ERR_11 "Грешка при изтриване на RF чипа"
|
||||
#define D_UPLOAD_ERR_12 "Грешка при записване в RF чипа"
|
||||
#define D_UPLOAD_ERR_13 "Грешка при декодиране на RF фърмуера"
|
||||
#define D_UPLOAD_ERR_14 "Not compatible"
|
||||
#define D_UPLOAD_ERR_14 "Несъвместим"
|
||||
#define D_UPLOAD_ERROR_CODE "Код на грешка при зареждането"
|
||||
|
||||
#define D_ENTER_COMMAND "Въвеждане на команда"
|
||||
@ -484,11 +484,11 @@
|
||||
|
||||
// xsns_27_apds9960.ino
|
||||
#define D_GESTURE "Жест"
|
||||
#define D_COLOR_RED "Red"
|
||||
#define D_COLOR_GREEN "Грийн"
|
||||
#define D_COLOR_BLUE "син"
|
||||
#define D_COLOR_RED "Червен"
|
||||
#define D_COLOR_GREEN "Зелен"
|
||||
#define D_COLOR_BLUE "Син"
|
||||
#define D_CCT "CCT"
|
||||
#define D_PROXIMITY "близост"
|
||||
#define D_PROXIMITY "Близост"
|
||||
|
||||
// xsns_32_mpu6050.ino
|
||||
#define D_AX_AXIS "Ускорение - ос X"
|
||||
@ -514,7 +514,7 @@
|
||||
//xsns_35_tx20.ino
|
||||
#define D_TX20_WIND_DIRECTION "Посока на вятъра"
|
||||
#define D_TX20_WIND_SPEED "Скорост на вятъра"
|
||||
#define D_TX20_WIND_SPEED_MIN "Мини. скорост на вятъра"
|
||||
#define D_TX20_WIND_SPEED_MIN "Мин. скорост на вятъра"
|
||||
#define D_TX20_WIND_SPEED_MAX "Макс. скорост на вятъра"
|
||||
#define D_TX20_NORTH "С"
|
||||
#define D_TX20_EAST "И"
|
||||
@ -522,24 +522,24 @@
|
||||
#define D_TX20_WEST "З"
|
||||
|
||||
// xsns_53_sml.ino
|
||||
#define D_TPWRIN "Energy Total-In"
|
||||
#define D_TPWROUT "Energy Total-Out"
|
||||
#define D_TPWRCURR "Active Power-In/Out"
|
||||
#define D_TPWRCURR1 "Active Power-In p1"
|
||||
#define D_TPWRCURR2 "Active Power-In p2"
|
||||
#define D_TPWRCURR3 "Active Power-In p3"
|
||||
#define D_Strom_L1 "Current L1"
|
||||
#define D_Strom_L2 "Current L2"
|
||||
#define D_Strom_L3 "Current L3"
|
||||
#define D_Spannung_L1 "Voltage L1"
|
||||
#define D_Spannung_L2 "Voltage L2"
|
||||
#define D_Spannung_L3 "Voltage L3"
|
||||
#define D_METERNR "Meter_number"
|
||||
#define D_METERSID "Service ID"
|
||||
#define D_GasIN "Counter"
|
||||
#define D_H2oIN "Counter"
|
||||
#define D_StL1L2L3 "Current L1+L2+L3"
|
||||
#define D_SpL1L2L3 "Voltage L1+L2+L3/3"
|
||||
#define D_TPWRIN "Общо енергия - IN"
|
||||
#define D_TPWROUT "Общо енергия - OUT"
|
||||
#define D_TPWRCURR "Активна мощност - In/Out"
|
||||
#define D_TPWRCURR1 "Активна мощност - In p1"
|
||||
#define D_TPWRCURR2 "Активна мощност - In p2"
|
||||
#define D_TPWRCURR3 "Активна мощност - In p3"
|
||||
#define D_Strom_L1 "Ток L1"
|
||||
#define D_Strom_L2 "Ток L2"
|
||||
#define D_Strom_L3 "Ток L3"
|
||||
#define D_Spannung_L1 "Напрежение L1"
|
||||
#define D_Spannung_L2 "Напрежение L2"
|
||||
#define D_Spannung_L3 "Напрежение L3"
|
||||
#define D_METERNR "Номер_електромер"
|
||||
#define D_METERSID "ID на услугата"
|
||||
#define D_GasIN "Брояч"
|
||||
#define D_H2oIN "Брояч"
|
||||
#define D_StL1L2L3 "Ток L1+L2+L3"
|
||||
#define D_SpL1L2L3 "Напрежение L1+L2+L3/3"
|
||||
|
||||
// tasmota_template.h - keep them as short as possible to be able to fit them in GUI drop down box
|
||||
#define D_SENSOR_NONE "Няма"
|
||||
@ -636,8 +636,8 @@
|
||||
#define D_SENSOR_HRE_CLOCK "HRE Clock"
|
||||
#define D_SENSOR_HRE_DATA "HRE Data"
|
||||
#define D_SENSOR_ADE7953_IRQ "ADE7953 IRQ"
|
||||
#define D_SENSOR_BUZZER "Buzzer"
|
||||
#define D_SENSOR_OLED_RESET "OLED Reset"
|
||||
#define D_SENSOR_BUZZER "Зумер"
|
||||
#define D_SENSOR_OLED_RESET "Нулиране OLED"
|
||||
#define D_SENSOR_ZIGBEE_TXD "Zigbee Tx"
|
||||
#define D_SENSOR_ZIGBEE_RXD "Zigbee Rx"
|
||||
#define D_SENSOR_SOLAXX1_TX "SolaxX1 Tx"
|
||||
@ -690,13 +690,17 @@
|
||||
|
||||
// Units
|
||||
#define D_UNIT_AMPERE "A"
|
||||
#define D_UNIT_CELSIUS "C"
|
||||
#define D_UNIT_CENTIMETER "cm"
|
||||
#define D_UNIT_DEGREE "°"
|
||||
#define D_UNIT_FAHRENHEIT "F"
|
||||
#define D_UNIT_HERTZ "Hz"
|
||||
#define D_UNIT_HOUR "h"
|
||||
#define D_UNIT_GALLONS "gal"
|
||||
#define D_UNIT_GALLONS_PER_MIN "gal/min"
|
||||
#define D_UNIT_KILOGRAM "kg"
|
||||
#define D_UNIT_INCREMENTS "inc"
|
||||
#define D_UNIT_KELVIN "K"
|
||||
#define D_UNIT_KILOMETER "km"
|
||||
#define D_UNIT_KILOMETER_PER_HOUR "km/h"
|
||||
#define D_UNIT_KILOOHM "kΩ"
|
||||
@ -713,6 +717,7 @@
|
||||
#define D_UNIT_PARTS_PER_BILLION "ppb"
|
||||
#define D_UNIT_PARTS_PER_DECILITER "ppd"
|
||||
#define D_UNIT_PARTS_PER_MILLION "ppm"
|
||||
#define D_UNIT_PERCENT "%%"
|
||||
#define D_UNIT_PRESSURE "hPa"
|
||||
#define D_UNIT_SECOND "s"
|
||||
#define D_UNIT_SECTORS "сектори"
|
||||
@ -732,7 +737,7 @@
|
||||
#define D_TOTAL_REACTIVE "Общо реактивна мощност"
|
||||
#define D_UNIT_KWARH "kVArh"
|
||||
#define D_UNIT_ANGLE "°"
|
||||
#define D_TOTAL_ACTIVE "Total Active"
|
||||
#define D_TOTAL_ACTIVE "Общо активна мощност"
|
||||
|
||||
//SOLAXX1
|
||||
#define D_PV1_VOLTAGE "Напрежение на PV1"
|
||||
@ -759,40 +764,40 @@
|
||||
#define D_SOLAX_ERROR_8 "Грешка - друго оборудване"
|
||||
|
||||
//xdrv_10_scripter.ino
|
||||
#define D_CONFIGURE_SCRIPT "Edit script"
|
||||
#define D_SCRIPT "edit script"
|
||||
#define D_SDCARD_UPLOAD "file upload"
|
||||
#define D_SDCARD_DIR "sd card directory"
|
||||
#define D_UPL_DONE "Done"
|
||||
#define D_SCRIPT_CHARS_LEFT "chars left"
|
||||
#define D_SCRIPT_CHARS_NO_MORE "no more chars"
|
||||
#define D_SCRIPT_DOWNLOAD "Download"
|
||||
#define D_SCRIPT_ENABLE "script enable"
|
||||
#define D_SCRIPT_UPLOAD "Upload"
|
||||
#define D_SCRIPT_UPLOAD_FILES "Upload files"
|
||||
#define D_CONFIGURE_SCRIPT "Редакция на скрипт"
|
||||
#define D_SCRIPT "редактирай скрипт"
|
||||
#define D_SDCARD_UPLOAD "изпрати файл"
|
||||
#define D_SDCARD_DIR "директория на SD картата"
|
||||
#define D_UPL_DONE "Готово"
|
||||
#define D_SCRIPT_CHARS_LEFT "оставащи символи"
|
||||
#define D_SCRIPT_CHARS_NO_MORE "няма повече символи"
|
||||
#define D_SCRIPT_DOWNLOAD "Изтегляне"
|
||||
#define D_SCRIPT_ENABLE "активирай скрипт"
|
||||
#define D_SCRIPT_UPLOAD "Изпращане"
|
||||
#define D_SCRIPT_UPLOAD_FILES "Изпращане на файлове"
|
||||
|
||||
//xsns_67_as3935.ino
|
||||
#define D_AS3935_GAIN "gain:"
|
||||
#define D_AS3935_ENERGY "energy:"
|
||||
#define D_AS3935_DISTANCE "distance:"
|
||||
#define D_AS3935_DISTURBER "disturber:"
|
||||
#define D_AS3935_GAIN "усилване:"
|
||||
#define D_AS3935_ENERGY "енергия:"
|
||||
#define D_AS3935_DISTANCE "разстояние:"
|
||||
#define D_AS3935_DISTURBER "смущение:"
|
||||
#define D_AS3935_VRMS "µVrms:"
|
||||
#define D_AS3935_APRX "aprx.:"
|
||||
#define D_AS3935_AWAY "away"
|
||||
#define D_AS3935_LIGHT "lightning"
|
||||
#define D_AS3935_OUT "lightning out of range"
|
||||
#define D_AS3935_NOT "distance not determined"
|
||||
#define D_AS3935_ABOVE "lightning overhead"
|
||||
#define D_AS3935_NOISE "noise detected"
|
||||
#define D_AS3935_DISTDET "disturber detected"
|
||||
#define D_AS3935_INTNOEV "Interrupt with no Event!"
|
||||
#define D_AS3935_NOMESS "listening..."
|
||||
#define D_AS3935_ON "On"
|
||||
#define D_AS3935_OFF "Off"
|
||||
#define D_AS3935_INDOORS "Indoors"
|
||||
#define D_AS3935_OUTDOORS "Outdoors"
|
||||
#define D_AS3935_CAL_FAIL "calibration failed"
|
||||
#define D_AS3935_CAL_OK "calibration set to:"
|
||||
#define D_AS3935_APRX "прибл.:"
|
||||
#define D_AS3935_AWAY "далече"
|
||||
#define D_AS3935_LIGHT "осветление"
|
||||
#define D_AS3935_OUT "осветление извън обхват"
|
||||
#define D_AS3935_NOT "неопределено разстояние"
|
||||
#define D_AS3935_ABOVE "околно осветление"
|
||||
#define D_AS3935_NOISE "открит шум"
|
||||
#define D_AS3935_DISTDET "открито смущение"
|
||||
#define D_AS3935_INTNOEV "Прекъсване без Събитие!"
|
||||
#define D_AS3935_NOMESS "слушане..."
|
||||
#define D_AS3935_ON "Вкл."
|
||||
#define D_AS3935_OFF "Изкл."
|
||||
#define D_AS3935_INDOORS "На закрито"
|
||||
#define D_AS3935_OUTDOORS "На открито"
|
||||
#define D_AS3935_CAL_FAIL "калибрирането е неуспешно"
|
||||
#define D_AS3935_CAL_OK "калибрирането е зададено на:"
|
||||
|
||||
//xsns_68_opentherm.ino
|
||||
#define D_SENSOR_BOILER_OT_RX "OpenTherm RX"
|
||||
|
@ -690,13 +690,17 @@
|
||||
|
||||
// Units
|
||||
#define D_UNIT_AMPERE "A"
|
||||
#define D_UNIT_CELSIUS "C"
|
||||
#define D_UNIT_CENTIMETER "cm"
|
||||
#define D_UNIT_DEGREE "°"
|
||||
#define D_UNIT_FAHRENHEIT "F"
|
||||
#define D_UNIT_HERTZ "Hz"
|
||||
#define D_UNIT_HOUR "hod"
|
||||
#define D_UNIT_GALLONS "gal"
|
||||
#define D_UNIT_GALLONS_PER_MIN "g/m"
|
||||
#define D_UNIT_KILOGRAM "kg"
|
||||
#define D_UNIT_INCREMENTS "inc"
|
||||
#define D_UNIT_KELVIN "K"
|
||||
#define D_UNIT_KILOMETER "km"
|
||||
#define D_UNIT_KILOMETER_PER_HOUR "km/h" // or "km/h"
|
||||
#define D_UNIT_KILOOHM "kΩ"
|
||||
@ -713,6 +717,7 @@
|
||||
#define D_UNIT_PARTS_PER_BILLION "ppb"
|
||||
#define D_UNIT_PARTS_PER_DECILITER "ppd"
|
||||
#define D_UNIT_PARTS_PER_MILLION "ppm"
|
||||
#define D_UNIT_PERCENT "%%"
|
||||
#define D_UNIT_PRESSURE "hPa"
|
||||
#define D_UNIT_SECOND "sec"
|
||||
#define D_UNIT_SECTORS "sektory"
|
||||
|
@ -690,12 +690,16 @@
|
||||
|
||||
// Units
|
||||
#define D_UNIT_AMPERE "A"
|
||||
#define D_UNIT_CELSIUS "C"
|
||||
#define D_UNIT_CENTIMETER "cm"
|
||||
#define D_UNIT_DEGREE "°"
|
||||
#define D_UNIT_FAHRENHEIT "F"
|
||||
#define D_UNIT_HERTZ "Hz"
|
||||
#define D_UNIT_HOUR "h"
|
||||
#define D_UNIT_GALLONS "gal"
|
||||
#define D_UNIT_GALLONS_PER_MIN "g/m"
|
||||
#define D_UNIT_INCREMENTS "inc"
|
||||
#define D_UNIT_KELVIN "K"
|
||||
#define D_UNIT_KILOMETER "km"
|
||||
#define D_UNIT_KILOGRAM "kg"
|
||||
#define D_UNIT_KILOMETER_PER_HOUR "km/h"
|
||||
@ -713,6 +717,7 @@
|
||||
#define D_UNIT_PARTS_PER_BILLION "ppb"
|
||||
#define D_UNIT_PARTS_PER_DECILITER "ppd"
|
||||
#define D_UNIT_PARTS_PER_MILLION "ppm"
|
||||
#define D_UNIT_PERCENT "%%"
|
||||
#define D_UNIT_PRESSURE "hPa"
|
||||
#define D_UNIT_SECOND "s"
|
||||
#define D_UNIT_SECTORS "Sektoren"
|
||||
|
@ -690,12 +690,16 @@
|
||||
|
||||
// Units
|
||||
#define D_UNIT_AMPERE "A"
|
||||
#define D_UNIT_CELSIUS "C"
|
||||
#define D_UNIT_CENTIMETER "cm"
|
||||
#define D_UNIT_DEGREE "°"
|
||||
#define D_UNIT_FAHRENHEIT "F"
|
||||
#define D_UNIT_HERTZ "Hz"
|
||||
#define D_UNIT_HOUR "h"
|
||||
#define D_UNIT_GALLONS "gal"
|
||||
#define D_UNIT_GALLONS_PER_MIN "g/m"
|
||||
#define D_UNIT_INCREMENTS "inc"
|
||||
#define D_UNIT_KELVIN "K"
|
||||
#define D_UNIT_KILOMETER "km"
|
||||
#define D_UNIT_KILOGRAM "kg"
|
||||
#define D_UNIT_KILOMETER_PER_HOUR "km/h" // or "km/h"
|
||||
@ -713,6 +717,7 @@
|
||||
#define D_UNIT_PARTS_PER_BILLION "ppb"
|
||||
#define D_UNIT_PARTS_PER_DECILITER "ppd"
|
||||
#define D_UNIT_PARTS_PER_MILLION "ppm"
|
||||
#define D_UNIT_PERCENT "%%"
|
||||
#define D_UNIT_PRESSURE "hPa"
|
||||
#define D_UNIT_SECOND "sec"
|
||||
#define D_UNIT_SECTORS "sectors"
|
||||
|
@ -690,13 +690,16 @@
|
||||
|
||||
// Units
|
||||
#define D_UNIT_AMPERE "A"
|
||||
#define D_UNIT_CELSIUS "C"
|
||||
#define D_UNIT_CENTIMETER "cm"
|
||||
#define D_UNIT_DEGREE "°"
|
||||
#define D_UNIT_FAHRENHEIT "F"
|
||||
#define D_UNIT_HERTZ "Hz"
|
||||
#define D_UNIT_HOUR "h"
|
||||
#define D_UNIT_GALLONS "gal"
|
||||
#define D_UNIT_GALLONS_PER_MIN "g/m"
|
||||
#define D_UNIT_INCREMENTS "inc"
|
||||
#define D_UNIT_KELVIN "°K"
|
||||
#define D_UNIT_KELVIN "K"
|
||||
#define D_UNIT_KILOMETER "km"
|
||||
#define D_UNIT_KILOGRAM "kg"
|
||||
#define D_UNIT_KILOMETER_PER_HOUR "km/h" // or "km/h"
|
||||
@ -714,6 +717,7 @@
|
||||
#define D_UNIT_PARTS_PER_BILLION "ppb"
|
||||
#define D_UNIT_PARTS_PER_DECILITER "ppd"
|
||||
#define D_UNIT_PARTS_PER_MILLION "ppm"
|
||||
#define D_UNIT_PERCENT "%%"
|
||||
#define D_UNIT_PRESSURE "hPa"
|
||||
#define D_UNIT_SECOND "sec"
|
||||
#define D_UNIT_SECTORS "sectors"
|
||||
|
@ -690,12 +690,16 @@
|
||||
|
||||
// Units
|
||||
#define D_UNIT_AMPERE "A"
|
||||
#define D_UNIT_CELSIUS "C"
|
||||
#define D_UNIT_CENTIMETER "cm"
|
||||
#define D_UNIT_DEGREE "°"
|
||||
#define D_UNIT_FAHRENHEIT "F"
|
||||
#define D_UNIT_HERTZ "Hz"
|
||||
#define D_UNIT_HOUR "h"
|
||||
#define D_UNIT_GALLONS "gal"
|
||||
#define D_UNIT_GALLONS_PER_MIN "g/m"
|
||||
#define D_UNIT_INCREMENTS "inc"
|
||||
#define D_UNIT_KELVIN "K"
|
||||
#define D_UNIT_KILOMETER "km"
|
||||
#define D_UNIT_KILOGRAM "kg"
|
||||
#define D_UNIT_KILOMETER_PER_HOUR "km/h" // or "km/h"
|
||||
@ -713,6 +717,7 @@
|
||||
#define D_UNIT_PARTS_PER_BILLION "ppb"
|
||||
#define D_UNIT_PARTS_PER_DECILITER "ppd"
|
||||
#define D_UNIT_PARTS_PER_MILLION "ppm"
|
||||
#define D_UNIT_PERCENT "%%"
|
||||
#define D_UNIT_PRESSURE "hPa"
|
||||
#define D_UNIT_SECOND "seg"
|
||||
#define D_UNIT_SECTORS "sectores"
|
||||
|
@ -79,7 +79,7 @@
|
||||
#define D_DATA "Donnée"
|
||||
#define D_DARKLIGHT "Sombre"
|
||||
#define D_DEBUG "Debug"
|
||||
#define D_DEWPOINT "Dew point"
|
||||
#define D_DEWPOINT "Point de rosée"
|
||||
#define D_DISABLED "Désactivé"
|
||||
#define D_DISTANCE "Distance"
|
||||
#define D_DNS_SERVER "Serveur DNS"
|
||||
@ -138,7 +138,7 @@
|
||||
#define D_PROGRAM_SIZE "Taille programme"
|
||||
#define D_PROJECT "Projet"
|
||||
#define D_RAIN "Pluie"
|
||||
#define D_RANGE "Range"
|
||||
#define D_RANGE "Intervalle"
|
||||
#define D_RECEIVED "Reçu"
|
||||
#define D_RESTART "Redémarrage"
|
||||
#define D_RESTARTING "Redémarre"
|
||||
@ -189,8 +189,8 @@
|
||||
|
||||
// tasmota.ino
|
||||
#define D_WARNING_MINIMAL_VERSION "ATTENTION Cette version ne supporte pas les réglages persistants"
|
||||
#define D_LEVEL_10 "level 1-0"
|
||||
#define D_LEVEL_01 "level 0-1"
|
||||
#define D_LEVEL_10 "niveau 1-0"
|
||||
#define D_LEVEL_01 "niveau 0-1"
|
||||
#define D_SERIAL_LOGGING_DISABLED "Journalisation série désactivée"
|
||||
#define D_SYSLOG_LOGGING_REENABLED "Jounalisation SysLog réactivée"
|
||||
|
||||
@ -402,7 +402,7 @@
|
||||
#define D_DOMOTICZ_TEMP_HUM "Temp,Hum"
|
||||
#define D_DOMOTICZ_TEMP_HUM_BARO "Temp,Hum,Baro"
|
||||
#define D_DOMOTICZ_POWER_ENERGY "Puissance,Énergie"
|
||||
#define D_DOMOTICZ_ILLUMINANCE "Illuminance"
|
||||
#define D_DOMOTICZ_ILLUMINANCE "Éclairement"
|
||||
#define D_DOMOTICZ_COUNT "Compteur/PM1"
|
||||
#define D_DOMOTICZ_VOLTAGE "Tension/PM2,5"
|
||||
#define D_DOMOTICZ_CURRENT "Courant/PM10"
|
||||
@ -536,8 +536,8 @@
|
||||
#define D_Spannung_L3 "Voltage L3"
|
||||
#define D_METERNR "Meter_number"
|
||||
#define D_METERSID "Service ID"
|
||||
#define D_GasIN "Counter"
|
||||
#define D_H2oIN "Counter"
|
||||
#define D_GasIN "Compteur"
|
||||
#define D_H2oIN "Compteur"
|
||||
#define D_StL1L2L3 "Current L1+L2+L3"
|
||||
#define D_SpL1L2L3 "Voltage L1+L2+L3/3"
|
||||
|
||||
@ -663,8 +663,8 @@
|
||||
#define D_SENSOR_SLAVE_TX "Esclave TX"
|
||||
#define D_SENSOR_SLAVE_RX "Esclave RX"
|
||||
#define D_SENSOR_SLAVE_RESET "Esclave Rst"
|
||||
#define D_SENSOR_GPS_TX "GPS TX"
|
||||
#define D_SENSOR_GPS_RX "GPS RX"
|
||||
#define D_SENSOR_GPS_TX "GPS TX"
|
||||
#define D_SENSOR_HM10_RX "HM10 RX"
|
||||
#define D_SENSOR_HM10_TX "HM10 TX"
|
||||
#define D_SENSOR_LE01MR_RX "LE-01MR Rx"
|
||||
@ -690,12 +690,16 @@
|
||||
|
||||
// Units
|
||||
#define D_UNIT_AMPERE "A"
|
||||
#define D_UNIT_CELSIUS "C"
|
||||
#define D_UNIT_CENTIMETER "cm"
|
||||
#define D_UNIT_DEGREE "°"
|
||||
#define D_UNIT_FAHRENHEIT "F"
|
||||
#define D_UNIT_HERTZ "Hz"
|
||||
#define D_UNIT_HOUR "h"
|
||||
#define D_UNIT_GALLONS "gal"
|
||||
#define D_UNIT_GALLONS_PER_MIN "gal/mn"
|
||||
#define D_UNIT_INCREMENTS "inc"
|
||||
#define D_UNIT_KELVIN "K"
|
||||
#define D_UNIT_KILOMETER "km"
|
||||
#define D_UNIT_KILOGRAM "kg"
|
||||
#define D_UNIT_KILOMETER_PER_HOUR "km/h"
|
||||
@ -709,10 +713,11 @@
|
||||
#define D_UNIT_MILLIMETER "mm"
|
||||
#define D_UNIT_MILLIMETER_MERCURY "mmHg"
|
||||
#define D_UNIT_MILLISECOND "ms"
|
||||
#define D_UNIT_MINUTE "mn"
|
||||
#define D_UNIT_MINUTE "min" // https://fr.wikipedia.org/wiki/Minute_(temps)#Symbole%20et%20d%C3%A9finition
|
||||
#define D_UNIT_PARTS_PER_BILLION "ppb"
|
||||
#define D_UNIT_PARTS_PER_DECILITER "ppd"
|
||||
#define D_UNIT_PARTS_PER_MILLION "ppm"
|
||||
#define D_UNIT_PERCENT "%%"
|
||||
#define D_UNIT_PRESSURE "hPa"
|
||||
#define D_UNIT_SECOND "s"
|
||||
#define D_UNIT_SECTORS "secteurs"
|
||||
|
@ -690,12 +690,16 @@
|
||||
|
||||
// Units
|
||||
#define D_UNIT_AMPERE "A"
|
||||
#define D_UNIT_CELSIUS "C"
|
||||
#define D_UNIT_CENTIMETER "cm"
|
||||
#define D_UNIT_DEGREE "°"
|
||||
#define D_UNIT_FAHRENHEIT "F"
|
||||
#define D_UNIT_HERTZ "Hz"
|
||||
#define D_UNIT_HOUR "h"
|
||||
#define D_UNIT_GALLONS "gal"
|
||||
#define D_UNIT_GALLONS_PER_MIN "g/m"
|
||||
#define D_UNIT_INCREMENTS "inc"
|
||||
#define D_UNIT_KELVIN "K"
|
||||
#define D_UNIT_KILOMETER "km"
|
||||
#define D_UNIT_KILOGRAM "kg"
|
||||
#define D_UNIT_KILOMETER_PER_HOUR "km/h" // or "km/h"
|
||||
@ -713,6 +717,7 @@
|
||||
#define D_UNIT_PARTS_PER_BILLION "ppb"
|
||||
#define D_UNIT_PARTS_PER_DECILITER "ppd"
|
||||
#define D_UNIT_PARTS_PER_MILLION "ppm"
|
||||
#define D_UNIT_PERCENT "%%"
|
||||
#define D_UNIT_PRESSURE "hPa"
|
||||
#define D_UNIT_SECOND "sec"
|
||||
#define D_UNIT_SECTORS "sectors"
|
||||
|
@ -690,12 +690,16 @@
|
||||
|
||||
// Units
|
||||
#define D_UNIT_AMPERE "A"
|
||||
#define D_UNIT_CELSIUS "C"
|
||||
#define D_UNIT_CENTIMETER "cm"
|
||||
#define D_UNIT_DEGREE "°"
|
||||
#define D_UNIT_FAHRENHEIT "F"
|
||||
#define D_UNIT_HERTZ "Hz"
|
||||
#define D_UNIT_HOUR "h"
|
||||
#define D_UNIT_GALLONS "gal"
|
||||
#define D_UNIT_GALLONS_PER_MIN "g/m"
|
||||
#define D_UNIT_INCREMENTS "inc"
|
||||
#define D_UNIT_KELVIN "K"
|
||||
#define D_UNIT_KILOMETER "km"
|
||||
#define D_UNIT_KILOGRAM "kg"
|
||||
#define D_UNIT_KILOMETER_PER_HOUR "km/h" // or "km/h"
|
||||
@ -713,6 +717,7 @@
|
||||
#define D_UNIT_PARTS_PER_BILLION "ppb"
|
||||
#define D_UNIT_PARTS_PER_DECILITER "ppd"
|
||||
#define D_UNIT_PARTS_PER_MILLION "ppm"
|
||||
#define D_UNIT_PERCENT "%%"
|
||||
#define D_UNIT_PRESSURE "hPa"
|
||||
#define D_UNIT_SECOND "s"
|
||||
#define D_UNIT_SECTORS "szektorok"
|
||||
|
@ -674,7 +674,7 @@
|
||||
#define D_SENSOR_HRXL_RX "HRXL - RX"
|
||||
#define D_SENSOR_ELECTRIQ_MOODL "MOODL - TX"
|
||||
#define D_SENSOR_AS3935 "AS3935"
|
||||
#define D_SENSOR_WINDMETER_SPEED "WindMeter Spd"
|
||||
#define D_SENSOR_WINDMETER_SPEED "Velocità vento"
|
||||
#define D_GPIO_WEBCAM_PWDN "CAM_PWDN"
|
||||
#define D_GPIO_WEBCAM_RESET "CAM_RESET"
|
||||
#define D_GPIO_WEBCAM_XCLK "CAM_XCLK"
|
||||
@ -690,12 +690,16 @@
|
||||
|
||||
// Units
|
||||
#define D_UNIT_AMPERE "A"
|
||||
#define D_UNIT_CELSIUS "C"
|
||||
#define D_UNIT_CENTIMETER "cm"
|
||||
#define D_UNIT_DEGREE "°"
|
||||
#define D_UNIT_FAHRENHEIT "F"
|
||||
#define D_UNIT_HERTZ "Hz"
|
||||
#define D_UNIT_HOUR "o"
|
||||
#define D_UNIT_GALLONS "gal"
|
||||
#define D_UNIT_GALLONS_PER_MIN "g/m"
|
||||
#define D_UNIT_INCREMENTS "inc"
|
||||
#define D_UNIT_KELVIN "K"
|
||||
#define D_UNIT_KILOMETER "km"
|
||||
#define D_UNIT_KILOGRAM "kg"
|
||||
#define D_UNIT_KILOMETER_PER_HOUR "km/h" // or "km/h"
|
||||
@ -713,6 +717,7 @@
|
||||
#define D_UNIT_PARTS_PER_BILLION "ppb"
|
||||
#define D_UNIT_PARTS_PER_DECILITER "ppd"
|
||||
#define D_UNIT_PARTS_PER_MILLION "ppm"
|
||||
#define D_UNIT_PERCENT "%%"
|
||||
#define D_UNIT_PRESSURE "hPa"
|
||||
#define D_UNIT_SECOND "sec"
|
||||
#define D_UNIT_SECTORS "settori"
|
||||
@ -795,7 +800,7 @@
|
||||
#define D_AS3935_CAL_OK "calibrazione impostata a:"
|
||||
|
||||
//xsns_68_opentherm.ino
|
||||
#define D_SENSOR_BOILER_OT_RX "OpenTherm RX"
|
||||
#define D_SENSOR_BOILER_OT_TX "OpenTherm TX"
|
||||
#define D_SENSOR_BOILER_OT_RX "OpenTherm - RX"
|
||||
#define D_SENSOR_BOILER_OT_TX "OpenTherm - TX"
|
||||
|
||||
#endif // _LANGUAGE_IT_IT_H_
|
||||
|
@ -690,12 +690,16 @@
|
||||
|
||||
// Units
|
||||
#define D_UNIT_AMPERE "A"
|
||||
#define D_UNIT_CELSIUS "C"
|
||||
#define D_UNIT_CENTIMETER "cm"
|
||||
#define D_UNIT_DEGREE "°"
|
||||
#define D_UNIT_FAHRENHEIT "F"
|
||||
#define D_UNIT_HERTZ "Hz"
|
||||
#define D_UNIT_HOUR "시"
|
||||
#define D_UNIT_GALLONS "gal"
|
||||
#define D_UNIT_GALLONS_PER_MIN "g/m"
|
||||
#define D_UNIT_INCREMENTS "inc"
|
||||
#define D_UNIT_KELVIN "K"
|
||||
#define D_UNIT_KILOMETER "km"
|
||||
#define D_UNIT_KILOGRAM "kg"
|
||||
#define D_UNIT_KILOMETER_PER_HOUR "km/h" // or "km/h"
|
||||
@ -713,6 +717,7 @@
|
||||
#define D_UNIT_PARTS_PER_BILLION "ppb"
|
||||
#define D_UNIT_PARTS_PER_DECILITER "ppd"
|
||||
#define D_UNIT_PARTS_PER_MILLION "ppm"
|
||||
#define D_UNIT_PERCENT "%%"
|
||||
#define D_UNIT_PRESSURE "hPa"
|
||||
#define D_UNIT_SECOND "초"
|
||||
#define D_UNIT_SECTORS "섹터"
|
||||
|
@ -690,12 +690,16 @@
|
||||
|
||||
// Units
|
||||
#define D_UNIT_AMPERE "A"
|
||||
#define D_UNIT_CELSIUS "C"
|
||||
#define D_UNIT_CENTIMETER "cm"
|
||||
#define D_UNIT_DEGREE "°"
|
||||
#define D_UNIT_FAHRENHEIT "F"
|
||||
#define D_UNIT_HERTZ "Hz"
|
||||
#define D_UNIT_HOUR "h"
|
||||
#define D_UNIT_GALLONS "gal"
|
||||
#define D_UNIT_GALLONS_PER_MIN "g/m"
|
||||
#define D_UNIT_INCREMENTS "inc"
|
||||
#define D_UNIT_KELVIN "K"
|
||||
#define D_UNIT_KILOMETER "km"
|
||||
#define D_UNIT_KILOGRAM "kg"
|
||||
#define D_UNIT_KILOMETER_PER_HOUR "km/h" // or "km/h"
|
||||
@ -713,6 +717,7 @@
|
||||
#define D_UNIT_PARTS_PER_BILLION "ppb"
|
||||
#define D_UNIT_PARTS_PER_DECILITER "ppd"
|
||||
#define D_UNIT_PARTS_PER_MILLION "ppm"
|
||||
#define D_UNIT_PERCENT "%%"
|
||||
#define D_UNIT_PRESSURE "hPa"
|
||||
#define D_UNIT_SECOND "sec"
|
||||
#define D_UNIT_SECTORS "sectoren"
|
||||
|
@ -690,12 +690,16 @@
|
||||
|
||||
// Units
|
||||
#define D_UNIT_AMPERE "A"
|
||||
#define D_UNIT_CELSIUS "C"
|
||||
#define D_UNIT_CENTIMETER "cm"
|
||||
#define D_UNIT_DEGREE "°"
|
||||
#define D_UNIT_FAHRENHEIT "F"
|
||||
#define D_UNIT_HERTZ "Hz"
|
||||
#define D_UNIT_HOUR "Godz"
|
||||
#define D_UNIT_GALLONS "gal"
|
||||
#define D_UNIT_GALLONS_PER_MIN "g/m"
|
||||
#define D_UNIT_INCREMENTS "inc"
|
||||
#define D_UNIT_KELVIN "K"
|
||||
#define D_UNIT_KILOMETER "km"
|
||||
#define D_UNIT_KILOGRAM "kg"
|
||||
#define D_UNIT_KILOMETER_PER_HOUR "km/h" // or "km/h"
|
||||
@ -713,6 +717,7 @@
|
||||
#define D_UNIT_PARTS_PER_BILLION "ppb"
|
||||
#define D_UNIT_PARTS_PER_DECILITER "ppd"
|
||||
#define D_UNIT_PARTS_PER_MILLION "ppm"
|
||||
#define D_UNIT_PERCENT "%%"
|
||||
#define D_UNIT_PRESSURE "hPa"
|
||||
#define D_UNIT_SECOND "sec"
|
||||
#define D_UNIT_SECTORS "sektory"
|
||||
|
@ -690,12 +690,16 @@
|
||||
|
||||
// Units
|
||||
#define D_UNIT_AMPERE "A"
|
||||
#define D_UNIT_CELSIUS "C"
|
||||
#define D_UNIT_CENTIMETER "cm"
|
||||
#define D_UNIT_DEGREE "°"
|
||||
#define D_UNIT_FAHRENHEIT "F"
|
||||
#define D_UNIT_HERTZ "Hz"
|
||||
#define D_UNIT_HOUR "H"
|
||||
#define D_UNIT_GALLONS "gal"
|
||||
#define D_UNIT_GALLONS_PER_MIN "g/m"
|
||||
#define D_UNIT_INCREMENTS "inc"
|
||||
#define D_UNIT_KELVIN "K"
|
||||
#define D_UNIT_KILOMETER "km"
|
||||
#define D_UNIT_KILOGRAM "kg"
|
||||
#define D_UNIT_KILOMETER_PER_HOUR "km/h" // or "km/h"
|
||||
@ -713,6 +717,7 @@
|
||||
#define D_UNIT_PARTS_PER_BILLION "ppb"
|
||||
#define D_UNIT_PARTS_PER_DECILITER "ppd"
|
||||
#define D_UNIT_PARTS_PER_MILLION "ppm"
|
||||
#define D_UNIT_PERCENT "%%"
|
||||
#define D_UNIT_PRESSURE "hPa"
|
||||
#define D_UNIT_SECOND "s"
|
||||
#define D_UNIT_SECTORS "setores"
|
||||
|
@ -690,12 +690,16 @@
|
||||
|
||||
// Units
|
||||
#define D_UNIT_AMPERE "A"
|
||||
#define D_UNIT_CELSIUS "C"
|
||||
#define D_UNIT_CENTIMETER "cm"
|
||||
#define D_UNIT_DEGREE "°"
|
||||
#define D_UNIT_FAHRENHEIT "F"
|
||||
#define D_UNIT_HERTZ "Hz"
|
||||
#define D_UNIT_HOUR "h"
|
||||
#define D_UNIT_GALLONS "gal"
|
||||
#define D_UNIT_GALLONS_PER_MIN "g/m"
|
||||
#define D_UNIT_INCREMENTS "inc"
|
||||
#define D_UNIT_KELVIN "K"
|
||||
#define D_UNIT_KILOMETER "km"
|
||||
#define D_UNIT_KILOGRAM "kg"
|
||||
#define D_UNIT_KILOMETER_PER_HOUR "km/h" // or "km/h"
|
||||
@ -713,6 +717,7 @@
|
||||
#define D_UNIT_PARTS_PER_BILLION "ppb"
|
||||
#define D_UNIT_PARTS_PER_DECILITER "ppd"
|
||||
#define D_UNIT_PARTS_PER_MILLION "ppm"
|
||||
#define D_UNIT_PERCENT "%%"
|
||||
#define D_UNIT_PRESSURE "hPa"
|
||||
#define D_UNIT_SECOND "sec"
|
||||
#define D_UNIT_SECTORS "setores"
|
||||
|
@ -690,12 +690,16 @@
|
||||
|
||||
// Units
|
||||
#define D_UNIT_AMPERE "A"
|
||||
#define D_UNIT_CELSIUS "C"
|
||||
#define D_UNIT_CENTIMETER "cm"
|
||||
#define D_UNIT_DEGREE "°"
|
||||
#define D_UNIT_FAHRENHEIT "F"
|
||||
#define D_UNIT_HERTZ "Hz"
|
||||
#define D_UNIT_HOUR "h"
|
||||
#define D_UNIT_GALLONS "gal"
|
||||
#define D_UNIT_GALLONS_PER_MIN "g/m"
|
||||
#define D_UNIT_INCREMENTS "inc"
|
||||
#define D_UNIT_KELVIN "K"
|
||||
#define D_UNIT_KILOMETER "km"
|
||||
#define D_UNIT_KILOGRAM "kg"
|
||||
#define D_UNIT_KILOMETER_PER_HOUR "km/h" // or "km/h"
|
||||
@ -713,6 +717,7 @@
|
||||
#define D_UNIT_PARTS_PER_BILLION "ppb"
|
||||
#define D_UNIT_PARTS_PER_DECILITER "ppd"
|
||||
#define D_UNIT_PARTS_PER_MILLION "ppm"
|
||||
#define D_UNIT_PERCENT "%%"
|
||||
#define D_UNIT_PRESSURE "hPa"
|
||||
#define D_UNIT_SECOND "sec"
|
||||
#define D_UNIT_SECTORS "sectors"
|
||||
|
@ -690,12 +690,16 @@
|
||||
|
||||
// Units
|
||||
#define D_UNIT_AMPERE "А"
|
||||
#define D_UNIT_CELSIUS "C"
|
||||
#define D_UNIT_CENTIMETER "cm"
|
||||
#define D_UNIT_DEGREE "°"
|
||||
#define D_UNIT_FAHRENHEIT "F"
|
||||
#define D_UNIT_HERTZ "Hz"
|
||||
#define D_UNIT_HOUR "Ч"
|
||||
#define D_UNIT_GALLONS "gal"
|
||||
#define D_UNIT_GALLONS_PER_MIN "g/m"
|
||||
#define D_UNIT_INCREMENTS "inc"
|
||||
#define D_UNIT_KELVIN "K"
|
||||
#define D_UNIT_KILOMETER "km"
|
||||
#define D_UNIT_KILOGRAM "kg"
|
||||
#define D_UNIT_KILOMETER_PER_HOUR "km/h" // or "km/h"
|
||||
@ -713,6 +717,7 @@
|
||||
#define D_UNIT_PARTS_PER_BILLION "ppb"
|
||||
#define D_UNIT_PARTS_PER_DECILITER "ppd"
|
||||
#define D_UNIT_PARTS_PER_MILLION "ppm"
|
||||
#define D_UNIT_PERCENT "%%"
|
||||
#define D_UNIT_PRESSURE "гПа"
|
||||
#define D_UNIT_SECOND "сек"
|
||||
#define D_UNIT_SECTORS "секторов"
|
||||
|
@ -690,13 +690,17 @@
|
||||
|
||||
// Units
|
||||
#define D_UNIT_AMPERE "A"
|
||||
#define D_UNIT_CELSIUS "C"
|
||||
#define D_UNIT_CENTIMETER "cm"
|
||||
#define D_UNIT_DEGREE "°"
|
||||
#define D_UNIT_FAHRENHEIT "F"
|
||||
#define D_UNIT_HERTZ "Hz"
|
||||
#define D_UNIT_HOUR "hod"
|
||||
#define D_UNIT_GALLONS "gal"
|
||||
#define D_UNIT_GALLONS_PER_MIN "g/m"
|
||||
#define D_UNIT_KILOGRAM "kg"
|
||||
#define D_UNIT_INCREMENTS "inc"
|
||||
#define D_UNIT_KELVIN "K"
|
||||
#define D_UNIT_KILOMETER "km"
|
||||
#define D_UNIT_KILOMETER_PER_HOUR "km/h" // or "km/h"
|
||||
#define D_UNIT_KILOOHM "kΩ"
|
||||
@ -713,6 +717,7 @@
|
||||
#define D_UNIT_PARTS_PER_BILLION "ppb"
|
||||
#define D_UNIT_PARTS_PER_DECILITER "ppd"
|
||||
#define D_UNIT_PARTS_PER_MILLION "ppm"
|
||||
#define D_UNIT_PERCENT "%%"
|
||||
#define D_UNIT_PRESSURE "hPa"
|
||||
#define D_UNIT_SECOND "sek"
|
||||
#define D_UNIT_SECTORS "sektory"
|
||||
|
@ -690,12 +690,16 @@
|
||||
|
||||
// Units
|
||||
#define D_UNIT_AMPERE "A"
|
||||
#define D_UNIT_CELSIUS "C"
|
||||
#define D_UNIT_CENTIMETER "cm"
|
||||
#define D_UNIT_DEGREE "°"
|
||||
#define D_UNIT_FAHRENHEIT "F"
|
||||
#define D_UNIT_HERTZ "Hz"
|
||||
#define D_UNIT_HOUR "Tim"
|
||||
#define D_UNIT_GALLONS "gal"
|
||||
#define D_UNIT_GALLONS_PER_MIN "g/m"
|
||||
#define D_UNIT_INCREMENTS "ink"
|
||||
#define D_UNIT_KELVIN "K"
|
||||
#define D_UNIT_KILOMETER "km"
|
||||
#define D_UNIT_KILOGRAM "kg"
|
||||
#define D_UNIT_KILOMETER_PER_HOUR "km/h" // or "km/h"
|
||||
@ -713,6 +717,7 @@
|
||||
#define D_UNIT_PARTS_PER_BILLION "ppb"
|
||||
#define D_UNIT_PARTS_PER_DECILITER "ppd"
|
||||
#define D_UNIT_PARTS_PER_MILLION "ppm"
|
||||
#define D_UNIT_PERCENT "%%"
|
||||
#define D_UNIT_PRESSURE "hPa"
|
||||
#define D_UNIT_SECOND "sek"
|
||||
#define D_UNIT_SECTORS "sektorer"
|
||||
|
@ -690,12 +690,16 @@
|
||||
|
||||
// Units
|
||||
#define D_UNIT_AMPERE "A"
|
||||
#define D_UNIT_CELSIUS "C"
|
||||
#define D_UNIT_CENTIMETER "cm"
|
||||
#define D_UNIT_DEGREE "°"
|
||||
#define D_UNIT_FAHRENHEIT "F"
|
||||
#define D_UNIT_HERTZ "Hz"
|
||||
#define D_UNIT_HOUR "h"
|
||||
#define D_UNIT_GALLONS "gal"
|
||||
#define D_UNIT_GALLONS_PER_MIN "g/m"
|
||||
#define D_UNIT_INCREMENTS "inc"
|
||||
#define D_UNIT_KELVIN "K"
|
||||
#define D_UNIT_KILOMETER "km"
|
||||
#define D_UNIT_KILOGRAM "kg"
|
||||
#define D_UNIT_KILOMETER_PER_HOUR "km/h" // or "km/h"
|
||||
@ -713,6 +717,7 @@
|
||||
#define D_UNIT_PARTS_PER_BILLION "ppb"
|
||||
#define D_UNIT_PARTS_PER_DECILITER "ppd"
|
||||
#define D_UNIT_PARTS_PER_MILLION "ppm"
|
||||
#define D_UNIT_PERCENT "%%"
|
||||
#define D_UNIT_PRESSURE "hPa"
|
||||
#define D_UNIT_SECOND "sec"
|
||||
#define D_UNIT_SECTORS "sectors"
|
||||
|
@ -690,13 +690,17 @@
|
||||
|
||||
// Units
|
||||
#define D_UNIT_AMPERE "А"
|
||||
#define D_UNIT_CELSIUS "C"
|
||||
#define D_UNIT_CENTIMETER "cм"
|
||||
#define D_UNIT_DEGREE "°"
|
||||
#define D_UNIT_FAHRENHEIT "F"
|
||||
#define D_UNIT_HERTZ "Гц"
|
||||
#define D_UNIT_HOUR "г"
|
||||
#define D_UNIT_GALLONS "гал"
|
||||
#define D_UNIT_GALLONS_PER_MIN "гал/хв"
|
||||
#define D_UNIT_INCREMENTS "інк"
|
||||
#define D_UNIT_KILOMETER "km"
|
||||
#define D_UNIT_KELVIN "K"
|
||||
#define D_UNIT_KILOMETER "km"
|
||||
#define D_UNIT_KILOGRAM "кг"
|
||||
#define D_UNIT_KILOMETER_PER_HOUR "км/г" // or "km/h"
|
||||
#define D_UNIT_KILOOHM "㏀"
|
||||
@ -713,6 +717,7 @@
|
||||
#define D_UNIT_PARTS_PER_BILLION "млрд⁻¹"
|
||||
#define D_UNIT_PARTS_PER_DECILITER "децилітр⁻¹"
|
||||
#define D_UNIT_PARTS_PER_MILLION "млн⁻¹"
|
||||
#define D_UNIT_PERCENT "%%"
|
||||
#define D_UNIT_PRESSURE "гПа"
|
||||
#define D_UNIT_SECOND "сек"
|
||||
#define D_UNIT_SECTORS "секторів"
|
||||
|
@ -690,12 +690,16 @@
|
||||
|
||||
// Units
|
||||
#define D_UNIT_AMPERE "安"
|
||||
#define D_UNIT_CELSIUS "C"
|
||||
#define D_UNIT_CENTIMETER "厘米"
|
||||
#define D_UNIT_DEGREE "°"
|
||||
#define D_UNIT_FAHRENHEIT "F"
|
||||
#define D_UNIT_HERTZ "赫兹"
|
||||
#define D_UNIT_HOUR "时"
|
||||
#define D_UNIT_GALLONS "gal"
|
||||
#define D_UNIT_GALLONS_PER_MIN "g/m"
|
||||
#define D_UNIT_INCREMENTS "inc"
|
||||
#define D_UNIT_KELVIN "K"
|
||||
#define D_UNIT_KILOMETER "km"
|
||||
#define D_UNIT_KILOGRAM "千克"
|
||||
#define D_UNIT_KILOMETER_PER_HOUR "公里/时" // or "km/h"
|
||||
@ -713,6 +717,7 @@
|
||||
#define D_UNIT_PARTS_PER_BILLION "ppb"
|
||||
#define D_UNIT_PARTS_PER_DECILITER "每分升"
|
||||
#define D_UNIT_PARTS_PER_MILLION "ppm"
|
||||
#define D_UNIT_PERCENT "%%"
|
||||
#define D_UNIT_PRESSURE "百帕"
|
||||
#define D_UNIT_SECOND "秒"
|
||||
#define D_UNIT_SECTORS "扇区"
|
||||
|
@ -690,12 +690,16 @@
|
||||
|
||||
// Units
|
||||
#define D_UNIT_AMPERE "安"
|
||||
#define D_UNIT_CELSIUS "C"
|
||||
#define D_UNIT_CENTIMETER "cm"
|
||||
#define D_UNIT_DEGREE "°"
|
||||
#define D_UNIT_FAHRENHEIT "F"
|
||||
#define D_UNIT_HERTZ "Hz"
|
||||
#define D_UNIT_HOUR "時"
|
||||
#define D_UNIT_GALLONS "gal"
|
||||
#define D_UNIT_GALLONS_PER_MIN "g/m"
|
||||
#define D_UNIT_INCREMENTS "inc"
|
||||
#define D_UNIT_KELVIN "K"
|
||||
#define D_UNIT_KILOMETER "km"
|
||||
#define D_UNIT_KILOGRAM "kg"
|
||||
#define D_UNIT_KILOMETER_PER_HOUR "km/h" // or "km/h"
|
||||
@ -713,6 +717,7 @@
|
||||
#define D_UNIT_PARTS_PER_BILLION "ppb"
|
||||
#define D_UNIT_PARTS_PER_DECILITER "每分升"
|
||||
#define D_UNIT_PARTS_PER_MILLION "ppm"
|
||||
#define D_UNIT_PERCENT "%%"
|
||||
#define D_UNIT_PRESSURE "百帕"
|
||||
#define D_UNIT_SECOND "秒"
|
||||
#define D_UNIT_SECTORS "扇區"
|
||||
|
@ -397,6 +397,7 @@
|
||||
// -- Rules or Script ----------------------------
|
||||
// Select none or only one of the below defines
|
||||
#define USE_RULES // Add support for rules (+8k code)
|
||||
// #define USE_RULES_COMPRESSION // Compresses rules in Flash at about ~50% (+3.8k code)
|
||||
//#define USE_SCRIPT // Add support for script (+17k code)
|
||||
//#define USE_SCRIPT_FATFS 4 // Script: Add FAT FileSystem Support
|
||||
|
||||
|
@ -112,7 +112,7 @@ typedef union { // Restricted by MISRA-C Rule 18.4 bu
|
||||
uint32_t only_json_message : 1; // bit 8 (v8.2.0.3) - SetOption90 - Disable non-json MQTT response
|
||||
uint32_t fade_at_startup : 1; // bit 9 (v8.2.0.3) - SetOption91 - Enable light fading at start/power on
|
||||
uint32_t pwm_ct_mode : 1; // bit 10 (v8.2.0.4) - SetOption92 - Set PWM Mode from regular PWM to ColorTemp control (Xiaomi Philips ...)
|
||||
uint32_t spare11 : 1;
|
||||
uint32_t compress_rules_cpu : 1; // bit 11 (v8.2.0.6) - SetOption93 - Keep uncompressed rules in memory to avoid CPU load of uncompressing at each tick
|
||||
uint32_t spare12 : 1;
|
||||
uint32_t spare13 : 1;
|
||||
uint32_t spare14 : 1;
|
||||
@ -182,6 +182,35 @@ typedef union {
|
||||
};
|
||||
} Timer;
|
||||
|
||||
typedef union { // Restricted by MISRA-C Rule 18.4 but so useful...
|
||||
uint32_t data;
|
||||
struct {
|
||||
uint32_t stream : 1;
|
||||
uint32_t mirror : 1;
|
||||
uint32_t flip : 1;
|
||||
uint32_t spare3 : 1;
|
||||
uint32_t spare4 : 1;
|
||||
uint32_t spare5 : 1;
|
||||
uint32_t spare6 : 1;
|
||||
uint32_t spare7 : 1;
|
||||
uint32_t spare8 : 1;
|
||||
uint32_t spare9 : 1;
|
||||
uint32_t spare10 : 1;
|
||||
uint32_t spare11 : 1;
|
||||
uint32_t spare12 : 1;
|
||||
uint32_t spare13 : 1;
|
||||
uint32_t spare14 : 1;
|
||||
uint32_t spare15 : 1;
|
||||
uint32_t spare16 : 1;
|
||||
uint32_t spare17 : 1;
|
||||
uint32_t spare18 : 1;
|
||||
uint32_t contrast : 3;
|
||||
uint32_t brightness : 3;
|
||||
uint32_t saturation : 3;
|
||||
uint32_t resolution : 4;
|
||||
};
|
||||
} WebCamCfg;
|
||||
|
||||
typedef union {
|
||||
uint16_t data;
|
||||
struct {
|
||||
@ -365,9 +394,11 @@ struct {
|
||||
myio my_gp; // 3AC - 2 x 40 bytes (ESP32)
|
||||
mytmplt user_template; // 3FC - 2 x 37 bytes (ESP32)
|
||||
|
||||
uint8_t free_esp32_446[10]; // 446
|
||||
uint8_t free_esp32_446[6]; // 446
|
||||
|
||||
uint8_t esp32_webcam_resolution; // 450
|
||||
WebCamCfg webcam_config; // 44C
|
||||
|
||||
uint8_t free_esp32_450[1]; // 450
|
||||
#endif // ESP8266 - ESP32
|
||||
|
||||
char serial_delimiter; // 451
|
||||
|
@ -1404,6 +1404,10 @@ void SettingsDelta(void)
|
||||
Settings.module = WEMOS;
|
||||
ModuleDefault(WEMOS);
|
||||
#endif // ESP32
|
||||
// make sure the empty rules have two consecutive NULLs, to be compatible with compressed rules
|
||||
if (Settings.rules[0][0] == 0) { Settings.rules[0][1] = 0; }
|
||||
if (Settings.rules[1][0] == 0) { Settings.rules[1][1] = 0; }
|
||||
if (Settings.rules[2][0] == 0) { Settings.rules[2][1] = 0; }
|
||||
}
|
||||
|
||||
Settings.version = VERSION;
|
||||
|
@ -620,7 +620,8 @@ float ConvertTempToCelsius(float c)
|
||||
|
||||
char TempUnit(void)
|
||||
{
|
||||
return (Settings.flag.temperature_conversion) ? 'F' : 'C'; // SetOption8 - Switch between Celsius or Fahrenheit
|
||||
// SetOption8 - Switch between Celsius or Fahrenheit
|
||||
return (Settings.flag.temperature_conversion) ? D_UNIT_FAHRENHEIT[0] : D_UNIT_CELSIUS[0];
|
||||
}
|
||||
|
||||
float ConvertHumidity(float h)
|
||||
|
@ -561,6 +561,11 @@ void CmndStatus(void)
|
||||
#ifdef USE_SCRIPT_STATUS
|
||||
if (bitRead(Settings.rule_enabled, 0)) Run_Scripter(">U",2,mqtt_data);
|
||||
#endif
|
||||
|
||||
if (payload) {
|
||||
XdrvRulesProcess(); // Allow rule processing on single Status command only
|
||||
}
|
||||
|
||||
mqtt_data[0] = '\0';
|
||||
}
|
||||
|
||||
|
@ -422,3 +422,26 @@ uint16_t changeUIntScale(uint16_t inum, uint16_t ifrom_min, uint16_t ifrom_max,
|
||||
}
|
||||
return (uint32_t) (result > to_max ? to_max : (result < to_min ? to_min : result));
|
||||
}
|
||||
|
||||
// Force a float value between two ranges, and adds or substract the range until we fit
|
||||
float ModulusRangef(float f, float a, float b) {
|
||||
if (b <= a) { return a; } // inconsistent, do what we can
|
||||
float range = b - a;
|
||||
float x = f - a; // now range of x should be 0..range
|
||||
x = fmodf(x, range); // actual range is now -range..range
|
||||
if (x < 0.0f) { x += range; } // actual range is now 0..range
|
||||
return x + a; // returns range a..b
|
||||
}
|
||||
|
||||
// Compute a n-degree polynomial for value x and an array of coefficient (by increasing order)
|
||||
// Ex:
|
||||
// For factors = { f0, f1, f2, f3 }
|
||||
// Returns : f0 + f1 x + f2 x^2, + f3 x^3
|
||||
// Internally computed as : f0 + x (f1 + x (f2 + x f3))
|
||||
float Polynomialf(const float *factors, uint32_t degree, float x) {
|
||||
float r = 0.0f;
|
||||
for (uint32_t i = degree - 1; i >= 0; i--) {
|
||||
r = r * x + factors[i];
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
@ -104,9 +104,9 @@ const uint16_t WS2812_MAX_LEDS = 512; // Max number of LEDs
|
||||
const uint32_t PWM_RANGE = 1023; // 255..1023 needs to be devisible by 256
|
||||
//const uint16_t PWM_FREQ = 1000; // 100..1000 Hz led refresh
|
||||
//const uint16_t PWM_FREQ = 910; // 100..1000 Hz led refresh (iTead value)
|
||||
const uint16_t PWM_FREQ = 223; // 100..4000 Hz led refresh
|
||||
const uint16_t PWM_FREQ = 977; // 100..4000 Hz led refresh
|
||||
const uint16_t PWM_MAX = 4000; // [PWM_MAX] Maximum frequency - Default: 4000
|
||||
const uint16_t PWM_MIN = 100; // [PWM_MIN] Minimum frequency - Default: 100
|
||||
const uint16_t PWM_MIN = 40; // [PWM_MIN] Minimum frequency - Default: 40
|
||||
// For Dimmers use double of your mains AC frequecy (100 for 50Hz and 120 for 60Hz)
|
||||
// For Controlling Servos use 50 and also set PWM_FREQ as 50 (DO NOT USE THESE VALUES FOR DIMMERS)
|
||||
|
||||
|
@ -505,7 +505,7 @@
|
||||
#undef USE_ARDUINO_SLAVE // Disable support for Arduino Uno/Pro Mini via serial interface including flashing (+2k3 code, 44 mem)
|
||||
#undef DEBUG_THEO // Disable debug code
|
||||
#undef USE_DEBUG_DRIVER // Disable debug code
|
||||
#endif // FIRMWARE_BASIC
|
||||
#endif // FIRMWARE_LITE
|
||||
|
||||
/*********************************************************************************************\
|
||||
* [tasmota-minimal.bin]
|
||||
|
@ -110,7 +110,8 @@ void DomoticzUpdateFanState(void)
|
||||
void MqttPublishDomoticzPowerState(uint8_t device)
|
||||
{
|
||||
if (Settings.flag.mqtt_enabled) { // SetOption3 - Enable MQTT
|
||||
if ((device < 1) || (device > devices_present) || (device > MAX_DOMOTICZ_IDX)) { device = 1; }
|
||||
if (device < 1) { device = 1; }
|
||||
if ((device > devices_present) || (device > MAX_DOMOTICZ_IDX)) { return; }
|
||||
if (Settings.domoticz_relay_idx[device -1]) {
|
||||
#ifdef USE_SHUTTER
|
||||
if (domoticz_is_shutter) {
|
||||
|
@ -68,61 +68,61 @@ const float pi2 = TWO_PI;
|
||||
const float pi = PI;
|
||||
const float RAD = DEG_TO_RAD;
|
||||
|
||||
float JulianischesDatum(void)
|
||||
{
|
||||
// Gregorianischer Kalender
|
||||
int Gregor;
|
||||
int Jahr = RtcTime.year;
|
||||
int Monat = RtcTime.month;
|
||||
int Tag = RtcTime.day_of_month;
|
||||
// Compute the Julian date from the Calendar date, using only unsigned ints for code compactness
|
||||
// Warning this formula works only from 2000 to 2099, after 2100 we get 1 day off per century. If ever Tasmota survives until then.
|
||||
uint32_t JulianDate(const struct TIME_T &now) {
|
||||
// https://en.wikipedia.org/wiki/Julian_day
|
||||
|
||||
if (Monat <= 2) {
|
||||
Monat += 12;
|
||||
Jahr -= 1;
|
||||
uint32_t Year = now.year; // Year ex:2020
|
||||
uint32_t Month = now.month; // 1..12
|
||||
uint32_t Day = now.day_of_month; // 1..31
|
||||
uint32_t Julian; // Julian day number
|
||||
|
||||
if (Month <= 2) {
|
||||
Month += 12;
|
||||
Year -= 1;
|
||||
}
|
||||
Gregor = (Jahr / 400) - (Jahr / 100) + (Jahr / 4); // Gregorianischer Kalender
|
||||
return 2400000.5f + 365.0f*Jahr - 679004.0f + Gregor + (int)(30.6001f * (Monat +1)) + Tag + 0.5f;
|
||||
// Warning, this formula works only for the 20th century, afterwards be are off by 1 day - which does not impact Sunrise much
|
||||
// Julian = (1461 * Year + 6884472) / 4 + (153 * Month - 457) / 5 + Day -1 -13;
|
||||
Julian = (1461 * Year + 6884416) / 4 + (153 * Month - 457) / 5 + Day; // -1 -13 included in 6884472 - 14*4 = 6884416
|
||||
return Julian;
|
||||
}
|
||||
|
||||
// Force value in the 0..pi2 range
|
||||
float InPi(float x)
|
||||
{
|
||||
int n = (int)(x / pi2);
|
||||
x = x - n*pi2;
|
||||
if (x < 0) x += pi2;
|
||||
return x;
|
||||
return ModulusRangef(x, 0.0f, pi2);
|
||||
}
|
||||
|
||||
float eps(float T)
|
||||
{
|
||||
// Neigung der Erdachse
|
||||
return RAD * (23.43929111f + (-46.8150f*T - 0.00059f*T*T + 0.001813f*T*T*T)/3600.0f);
|
||||
}
|
||||
// Time formula
|
||||
// Tdays is the number of days since Jan 1 2000, and replaces T as the Tropical Century. T = Tdays / 36525.0
|
||||
float TimeFormula(float *DK, uint32_t Tdays) {
|
||||
float RA_Mean = 18.71506921f + (2400.0513369f / 36525.0f) * Tdays; // we keep only first order value as T is between 0.20 and 0.30
|
||||
float M = InPi( (pi2 * 0.993133f) + (pi2 * 99.997361f / 36525.0f) * Tdays);
|
||||
float L = InPi( (pi2 * 0.7859453f) + M + (6893.0f * sinf(M) + 72.0f * sinf(M+M) + (6191.2f / 36525.0f) * Tdays) * (pi2 / 1296.0e3f));
|
||||
|
||||
float BerechneZeitgleichung(float *DK,float T)
|
||||
{
|
||||
float RA_Mittel = 18.71506921f + 2400.0513369f*T +(2.5862e-5f - 1.72e-9f*T)*T*T;
|
||||
float M = InPi(pi2 * (0.993133f + 99.997361f*T));
|
||||
float L = InPi(pi2 * (0.7859453f + M/pi2 + (6893.0f*sinf(M)+72.0f*sinf(2.0f*M)+6191.2f*T) / 1296.0e3f));
|
||||
float e = eps(T);
|
||||
float RA = atanf(tanf(L)*cosf(e));
|
||||
if (RA < 0.0) RA += pi;
|
||||
float eps = 0.40904f; // we take this angle as constant over the next decade
|
||||
float cos_eps = 0.91750f; // precompute cos(eps)
|
||||
float sin_eps = 0.39773f; // precompute sin(eps)
|
||||
|
||||
float RA = atanf(tanf(L) * cos_eps);
|
||||
if (RA < 0.0f) RA += pi;
|
||||
if (L > pi) RA += pi;
|
||||
RA = 24.0*RA/pi2;
|
||||
*DK = asinf(sinf(e)*sinf(L));
|
||||
// Damit 0<=RA_Mittel<24
|
||||
RA_Mittel = 24.0f * InPi(pi2*RA_Mittel/24.0f)/pi2;
|
||||
float dRA = RA_Mittel - RA;
|
||||
if (dRA < -12.0f) dRA += 24.0f;
|
||||
if (dRA > 12.0f) dRA -= 24.0f;
|
||||
RA = RA * (24.0f/pi2);
|
||||
*DK = asinf(sin_eps * sinf(L));
|
||||
RA_Mean = ModulusRangef(RA_Mean, 0.0f, 24.0f);
|
||||
float dRA = ModulusRangef(RA_Mean - RA, -12.0f, 12.0f);
|
||||
dRA = dRA * 1.0027379f;
|
||||
return dRA;
|
||||
}
|
||||
|
||||
void DuskTillDawn(uint8_t *hour_up,uint8_t *minute_up, uint8_t *hour_down, uint8_t *minute_down)
|
||||
{
|
||||
float JD2000 = 2451545.0f;
|
||||
float JD = JulianischesDatum();
|
||||
float T = (JD - JD2000) / 36525.0f;
|
||||
const uint32_t JD2000 = 2451545;
|
||||
uint32_t JD = JulianDate(RtcTime);
|
||||
uint32_t Tdays = JD - JD2000; // number of days since Jan 1 2000
|
||||
|
||||
// ex 2458977 (2020 May 7) - 2451545 -> 7432 -> 0,2034
|
||||
float DK;
|
||||
/*
|
||||
h (D) = -0.8333 normaler SA & SU-Gang
|
||||
@ -130,56 +130,33 @@ void DuskTillDawn(uint8_t *hour_up,uint8_t *minute_up, uint8_t *hour_down, uint8
|
||||
h (D) = -12.0 nautische Dämmerung
|
||||
h (D) = -18.0 astronomische Dämmerung
|
||||
*/
|
||||
// double h = -50/60.0*RAD;
|
||||
float h = SUNRISE_DAWN_ANGLE *RAD;
|
||||
float B = (((float)Settings.latitude)/1000000) * RAD; // geographische Breite
|
||||
const float h = SUNRISE_DAWN_ANGLE * RAD;
|
||||
const float sin_h = sinf(h); // let GCC pre-compute the sin() at compile time
|
||||
|
||||
float B = Settings.latitude / (1000000.0f / RAD); // geographische Breite
|
||||
//float B = (((float)Settings.latitude)/1000000) * RAD; // geographische Breite
|
||||
float GeographischeLaenge = ((float)Settings.longitude)/1000000;
|
||||
// double Zeitzone = 0; //Weltzeit
|
||||
// double Zeitzone = 1; //Winterzeit
|
||||
// double Zeitzone = 2.0; //Sommerzeit
|
||||
float Zeitzone = ((float)Rtc.time_timezone) / 60;
|
||||
float Zeitgleichung = BerechneZeitgleichung(&DK, T);
|
||||
float Zeitdifferenz = 12.0f*acosf((sinf(h) - sinf(B)*sinf(DK)) / (cosf(B)*cosf(DK)))/pi;
|
||||
float Zeitgleichung = TimeFormula(&DK, Tdays);
|
||||
float Zeitdifferenz = acosf((sin_h - sinf(B)*sinf(DK)) / (cosf(B)*cosf(DK))) * (12.0f / pi);
|
||||
float AufgangOrtszeit = 12.0f - Zeitdifferenz - Zeitgleichung;
|
||||
float UntergangOrtszeit = 12.0f + Zeitdifferenz - Zeitgleichung;
|
||||
float AufgangWeltzeit = AufgangOrtszeit - GeographischeLaenge / 15.0f;
|
||||
float UntergangWeltzeit = UntergangOrtszeit - GeographischeLaenge / 15.0f;
|
||||
float Aufgang = AufgangWeltzeit + Zeitzone; // In Stunden
|
||||
if (Aufgang < 0.0f) {
|
||||
Aufgang += 24.0f;
|
||||
} else {
|
||||
if (Aufgang >= 24.0f) Aufgang -= 24.0f;
|
||||
}
|
||||
float Untergang = UntergangWeltzeit + Zeitzone;
|
||||
if (Untergang < 0.0f) {
|
||||
Untergang += 24.0f;
|
||||
} else {
|
||||
if (Untergang >= 24.0f) Untergang -= 24.0f;
|
||||
}
|
||||
int AufgangMinuten = (int)(60.0f*(Aufgang - (int)Aufgang)+0.5f);
|
||||
float Aufgang = AufgangWeltzeit + Zeitzone + (1/120.0f); // In Stunden, with rounding to nearest minute (1/60 * .5)
|
||||
|
||||
Aufgang = ModulusRangef(Aufgang, 0.0f, 24.0f); // force 0 <= x < 24.0
|
||||
int AufgangStunden = (int)Aufgang;
|
||||
if (AufgangMinuten >= 60.0f) {
|
||||
AufgangMinuten -= 60.0f;
|
||||
AufgangStunden++;
|
||||
} else {
|
||||
if (AufgangMinuten < 0.0f) {
|
||||
AufgangMinuten += 60.0f;
|
||||
AufgangStunden--;
|
||||
if (AufgangStunden < 0.0f) AufgangStunden += 24.0f;
|
||||
}
|
||||
}
|
||||
int UntergangMinuten = (int)(60.0f*(Untergang - (int)Untergang)+0.5f);
|
||||
int AufgangMinuten = (int)(60.0f * fmodf(Aufgang, 1.0f));
|
||||
float Untergang = UntergangWeltzeit + Zeitzone;
|
||||
|
||||
Untergang = ModulusRangef(Untergang, 0.0f, 24.0f);
|
||||
int UntergangStunden = (int)Untergang;
|
||||
if (UntergangMinuten >= 60.0f) {
|
||||
UntergangMinuten -= 60.0f;
|
||||
UntergangStunden++;
|
||||
} else {
|
||||
if (UntergangMinuten<0) {
|
||||
UntergangMinuten += 60.0f;
|
||||
UntergangStunden--;
|
||||
if (UntergangStunden < 0.0f) UntergangStunden += 24.0f;
|
||||
}
|
||||
}
|
||||
int UntergangMinuten = (int)(60.0f * fmodf(Untergang, 1.0f));
|
||||
|
||||
*hour_up = AufgangStunden;
|
||||
*minute_up = AufgangMinuten;
|
||||
*hour_down = UntergangStunden;
|
||||
|
@ -47,6 +47,7 @@
|
||||
* on button1#state do publish cmnd/ring2/power %value% endon on button2#state do publish cmnd/strip1/power %value% endon
|
||||
* on switch1#state do power2 %value% endon
|
||||
* on analog#a0div10 do publish cmnd/ring2/dimmer %value% endon
|
||||
* on root#loadavg<50 do power 2 endon
|
||||
*
|
||||
* Notes:
|
||||
* Spaces after <on>, around <do> and before <endon> are mandatory
|
||||
@ -66,6 +67,8 @@
|
||||
|
||||
#define XDRV_10 10
|
||||
|
||||
#include <unishox.h>
|
||||
|
||||
#define D_CMND_RULE "Rule"
|
||||
#define D_CMND_RULETIMER "RuleTimer"
|
||||
#define D_CMND_EVENT "Event"
|
||||
@ -178,6 +181,230 @@ char rules_vars[MAX_RULE_VARS][33] = {{ 0 }};
|
||||
#error MAX_RULE_MEMS is bigger than 16
|
||||
#endif
|
||||
|
||||
|
||||
/*******************************************************************************************/
|
||||
/*
|
||||
* Add Unishox compression to Rules
|
||||
*
|
||||
* New compression for Rules, depends on SetOption93
|
||||
*
|
||||
* To avoid memory corruption when downgrading, the format is as follows:
|
||||
* - If `SetOption93 0`
|
||||
* Rule[x][] = 511 char max NULL terminated string (512 with trailing NULL)
|
||||
* Rule[x][0] = 0 if the Rule<x> is empty
|
||||
* New: in case the string is empty we also enforce:
|
||||
* Rule[x][1] = 0 (i.e. we have two conseutive NULLs)
|
||||
*
|
||||
* - If `SetOption93 1`
|
||||
* If the rule is smaller than 511, it is stored uncompressed. Rule[x][0] is not null.
|
||||
* If the rule is empty, Rule[x][0] = 0 and Rule[x][1] = 0;
|
||||
* If the rule is bigger than 511, it is stored compressed
|
||||
* The first byte of each Rule is always NULL.
|
||||
* Rule[x][0] = 0, if firmware is downgraded, the rule will be considered as empty
|
||||
*
|
||||
* The second byte contains the size of uncompressed rule in 8-bytes blocks (i.e. (len+7)/8 )
|
||||
* Maximum rule size si 2KB (2048 bytes per rule), although there is little chances compression ratio will go down to 75%
|
||||
* Rule[x][1] = size uncompressed in dwords. If zero, the rule is empty.
|
||||
*
|
||||
* The remaining bytes contain the compressed rule, NULL terminated
|
||||
*/
|
||||
/*******************************************************************************************/
|
||||
|
||||
#ifdef USE_RULES_COMPRESSION
|
||||
// Statically allocate one String per rule
|
||||
String k_rules[MAX_RULE_SETS] = { String(), String(), String() }; // Strings are created empty
|
||||
#endif // USE_RULES_COMPRESSION
|
||||
|
||||
// Returns whether the rule is uncompressed, which means the first byte is not NULL
|
||||
inline bool IsRuleUncompressed(uint32_t idx) {
|
||||
#ifdef USE_RULES_COMPRESSION
|
||||
return Settings.rules[idx][0] ? true : false; // first byte not NULL, the rule is not empty and not compressed
|
||||
#else
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
|
||||
// Returns whether the rule is empty, which requires two consecutive NULL
|
||||
inline bool IsRuleEmpty(uint32_t idx) {
|
||||
#ifdef USE_RULES_COMPRESSION
|
||||
return (Settings.rules[idx][0] == 0) && (Settings.rules[idx][1] == 0) ? true : false;
|
||||
#else
|
||||
return (Settings.rules[idx][0] == 0) ? true : false;
|
||||
#endif
|
||||
}
|
||||
|
||||
// Returns the approximate (+3-0) length of the rule, not counting the trailing NULL
|
||||
size_t GetRuleLen(uint32_t idx) {
|
||||
// no need to use #ifdef USE_RULES_COMPRESSION, the compiler will optimize since first test is always true
|
||||
if (IsRuleUncompressed(idx)) {
|
||||
return strlen(Settings.rules[idx]);
|
||||
} else { // either empty or compressed
|
||||
return Settings.rules[idx][1] * 8; // cheap calculation, but not byte accurate (may overshoot by 7)
|
||||
}
|
||||
}
|
||||
|
||||
// Returns the actual Flash storage for the Rule, including trailing NULL
|
||||
size_t GetRuleLenStorage(uint32_t idx) {
|
||||
#ifdef USE_RULES_COMPRESSION
|
||||
if (Settings.rules[idx][0] || !Settings.rules[idx][1]) { // if first byte is non-NULL it is uncompressed, if second byte is NULL, then it's either uncompressed or empty
|
||||
return 1 + strlen(Settings.rules[idx]); // uncompressed or empty
|
||||
} else {
|
||||
return 2 + strlen(&Settings.rules[idx][1]); // skip first byte and get len of the compressed rule
|
||||
}
|
||||
#else
|
||||
return 1 + strlen(Settings.rules[idx]);
|
||||
#endif
|
||||
}
|
||||
|
||||
// internal function, do the actual decompression
|
||||
void GetRule_decompress(String &rule, const char *rule_head) {
|
||||
size_t buf_len = 1 + *rule_head * 8; // the first byte contains size of buffer for uncompressed rule / 8, buf_len may overshoot by 7
|
||||
rule_head++; // advance to the actual compressed buffer
|
||||
|
||||
// We use a nasty trick here. To avoid allocating twice the buffer,
|
||||
// we first extend the buffer of the String object to the target size (maybe overshooting by 7 bytes)
|
||||
// then we decompress in this buffer,
|
||||
// and finally assign the raw string to the String, which happens to work: String uses memmove(), so overlapping works
|
||||
rule.reserve(buf_len);
|
||||
char* buf = rule.begin();
|
||||
|
||||
int32_t len_decompressed = unishox_decompress(rule_head, strlen(rule_head), buf, buf_len);
|
||||
buf[len_decompressed] = 0; // add NULL terminator
|
||||
|
||||
// AddLog_P2(LOG_LEVEL_INFO, PSTR("RUL: Rawdecompressed: %d"), len_decompressed);
|
||||
rule = buf; // assign the raw string to the String object (in reality re-writing the same data in the same place)
|
||||
}
|
||||
|
||||
//
|
||||
// Read rule in memory, uncompress if needed
|
||||
//
|
||||
// Returns: String() object containing a copy of the rule (rule processing is destructive and will change the String)
|
||||
String GetRule(uint32_t idx) {
|
||||
if (IsRuleUncompressed(idx)) {
|
||||
return String(Settings.rules[idx]);
|
||||
} else {
|
||||
#ifdef USE_RULES_COMPRESSION // we still do #ifdef to make sure we don't link unnecessary code
|
||||
|
||||
String rule("");
|
||||
if (Settings.rules[idx][1] == 0) { return rule; } // the rule is empty
|
||||
|
||||
// If the cache is empty, we need to decompress from Settings
|
||||
if (0 == k_rules[idx].length() ) {
|
||||
GetRule_decompress(rule, &Settings.rules[idx][1]);
|
||||
if (!Settings.flag4.compress_rules_cpu) {
|
||||
k_rules[idx] = rule; // keep a copy for next time
|
||||
}
|
||||
} else {
|
||||
// we have a valid copy
|
||||
rule = k_rules[idx];
|
||||
}
|
||||
return rule;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef USE_RULES_COMPRESSION
|
||||
// internal function, comrpess rule and store a cached version uncompressed (except if SetOption94 1)
|
||||
// If out == nullptr, we are in dry-run mode, so don't keep rule in cache
|
||||
int32_t SetRule_compress(uint32_t idx, const char *in, size_t in_len, char *out, size_t out_len) {
|
||||
int32_t len_compressed;
|
||||
len_compressed = unishox_compress(in, in_len, out, out_len);
|
||||
|
||||
if (len_compressed >= 0) { // negative means compression failed because of buffer too small, we leave the rule untouched
|
||||
// check if we need to store in cache
|
||||
k_rules[idx] = (const char*) nullptr; // Assign the String to nullptr, clears previous string and disallocate internal buffers of String object
|
||||
if ((!Settings.flag4.compress_rules_cpu) && out) { // if out == nullptr, don't store cache
|
||||
// keep copy in cache
|
||||
k_rules[idx] = in;
|
||||
}
|
||||
}
|
||||
return len_compressed;
|
||||
}
|
||||
#endif // USE_RULES_COMPRESSION
|
||||
|
||||
// Returns:
|
||||
// >= 0 : the actual stored size
|
||||
// <0 : not enough space
|
||||
int32_t SetRule(uint32_t idx, const char *content, bool append = false) {
|
||||
if (nullptr == content) { content = ""; } // if nullptr, use empty string
|
||||
size_t len_in = strlen(content);
|
||||
bool needsCompress = false;
|
||||
size_t offset = 0;
|
||||
|
||||
if (len_in >= MAX_RULE_SIZE) { // if input is more than 512, it will not fit uncompressed
|
||||
needsCompress = true;
|
||||
}
|
||||
if (append) {
|
||||
if (IsRuleUncompressed(idx) || IsRuleEmpty(idx)) { // if already uncompressed (so below 512) and append mode, check if it still fits uncompressed
|
||||
offset = strlen(Settings.rules[idx]);
|
||||
if (len_in + offset >= MAX_RULE_SIZE) {
|
||||
needsCompress = true;
|
||||
}
|
||||
} else {
|
||||
needsCompress = true; // we append to a non-empty compressed rule, so it won't fit uncompressed
|
||||
}
|
||||
}
|
||||
|
||||
if (!needsCompress) { // the rule fits uncompressed, so just copy it
|
||||
strlcpy(Settings.rules[idx] + offset, content, sizeof(Settings.rules[idx]));
|
||||
if (0 == Settings.rules[idx][0]) {
|
||||
Settings.rules[idx][1] = 0;
|
||||
}
|
||||
|
||||
#ifdef USE_RULES_COMPRESSION
|
||||
if (0 != len_in + offset) {
|
||||
// do a dry-run compression to display how much it would be compressed
|
||||
int32_t len_compressed, len_uncompressed;
|
||||
|
||||
len_uncompressed = strlen(Settings.rules[idx]);
|
||||
len_compressed = unishox_compress(Settings.rules[idx], len_uncompressed, nullptr /* dry-run */, MAX_RULE_SIZE + 8);
|
||||
AddLog_P2(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_RULES_COMPRESSION
|
||||
|
||||
return len_in + offset;
|
||||
} else {
|
||||
#ifdef USE_RULES_COMPRESSION
|
||||
int32_t len_compressed;
|
||||
// allocate temp buffer so we don't nuke the rule if it's too big to fit
|
||||
char *buf_out = (char*) malloc(MAX_RULE_SIZE + 8); // take some margin
|
||||
if (!buf_out) { return -1; } // fail if couldn't allocate
|
||||
|
||||
// compress
|
||||
if (append) {
|
||||
String content_append = GetRule(idx); // get original Rule and decompress it if needed
|
||||
content_append += content; // concat new content
|
||||
len_in = content_append.length(); // adjust length
|
||||
len_compressed = SetRule_compress(idx, content_append.c_str(), len_in, buf_out, MAX_RULE_SIZE + 8);
|
||||
} else {
|
||||
len_compressed = SetRule_compress(idx, content, len_in, buf_out, MAX_RULE_SIZE + 8);
|
||||
}
|
||||
|
||||
if ((len_compressed >= 0) && (len_compressed < MAX_RULE_SIZE - 2)) {
|
||||
// size is ok, copy to Settings
|
||||
Settings.rules[idx][0] = 0; // clear first byte to mark as compressed
|
||||
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_P2(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_P2(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_P2(LOG_LEVEL_INFO, PSTR("RUL: GetRuleLenStorage = %d"), GetRuleLenStorage(idx));
|
||||
} else {
|
||||
len_compressed = -1; // failed
|
||||
// clear rule cache, so it will be reloaded from Settings
|
||||
k_rules[idx] = (const char *) nullptr;
|
||||
}
|
||||
free(buf_out);
|
||||
return len_compressed;
|
||||
|
||||
#else // USE_RULES_COMPRESSION
|
||||
return -1; // the rule does not fit and we can't compress
|
||||
#endif // USE_RULES_COMPRESSION
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/*******************************************************************************************/
|
||||
|
||||
bool RulesRuleMatch(uint8_t rule_set, String &event, String &rule)
|
||||
@ -190,19 +417,20 @@ bool RulesRuleMatch(uint8_t rule_set, String &event, String &rule)
|
||||
char stemp[10];
|
||||
|
||||
// Step1: Analyse rule
|
||||
int pos = rule.indexOf('#');
|
||||
if (pos == -1) { return false; } // No # sign in rule
|
||||
|
||||
String rule_task = rule.substring(0, pos); // "INA219" or "SYSTEM"
|
||||
String rule_expr = rule; // "TELE-INA219#CURRENT>0.100"
|
||||
if (Rules.teleperiod) {
|
||||
int ppos = rule_task.indexOf("TELE-"); // "TELE-INA219" or "INA219"
|
||||
int ppos = rule_expr.indexOf("TELE-"); // "TELE-INA219#CURRENT>0.100" or "INA219#CURRENT>0.100"
|
||||
if (ppos == -1) { return false; } // No pre-amble in rule
|
||||
rule_task = rule.substring(5, pos); // "INA219" or "SYSTEM"
|
||||
rule_expr = rule.substring(5); // "INA219#CURRENT>0.100" or "SYSTEM#BOOT"
|
||||
}
|
||||
|
||||
String rule_expr = rule.substring(pos +1); // "CURRENT>0.100" or "BOOT" or "%var1%" or "MINUTE|5"
|
||||
String rule_name, rule_param;
|
||||
int8_t compareOperator = parseCompareExpression(rule_expr, rule_name, rule_param); //Parse the compare expression.Return operator and the left, right part of expression
|
||||
int8_t compareOperator = parseCompareExpression(rule_expr, rule_name, rule_param); // Parse the compare expression.Return operator and the left, right part of expression
|
||||
|
||||
// rule_name = "INA219#CURRENT"
|
||||
// rule_param = "0.100" or "%VAR1%"
|
||||
|
||||
//AddLog_P2(LOG_LEVEL_DEBUG, PSTR("RUL: expr %s, name %s, param %s"), rule_expr.c_str(), rule_name.c_str(), rule_param.c_str());
|
||||
|
||||
char rule_svalue[80] = { 0 };
|
||||
float rule_value = 0;
|
||||
@ -250,11 +478,12 @@ bool RulesRuleMatch(uint8_t rule_set, String &event, String &rule)
|
||||
if (temp_value > -1) {
|
||||
rule_value = temp_value;
|
||||
} else {
|
||||
rule_value = CharToFloat((char*)rule_svalue); // 0.1 - This saves 9k code over toFLoat()!
|
||||
rule_value = CharToFloat((char*)rule_svalue); // 0.1 - This saves 9k code over toFLoat()!
|
||||
}
|
||||
}
|
||||
|
||||
// Step2: Search rule_task and rule_name
|
||||
// Step2: Search rule_name
|
||||
int pos;
|
||||
int rule_name_idx = 0;
|
||||
if ((pos = rule_name.indexOf("[")) > 0) { // "SUBTYPE1#CURRENT[1]"
|
||||
rule_name_idx = rule_name.substring(pos +1).toInt();
|
||||
@ -267,10 +496,7 @@ bool RulesRuleMatch(uint8_t rule_set, String &event, String &rule)
|
||||
StaticJsonBuffer<1024> jsonBuf;
|
||||
JsonObject &root = jsonBuf.parseObject(event);
|
||||
if (!root.success()) { return false; } // No valid JSON data
|
||||
if (!root[rule_task].success()) { return false; } // No rule_task in JSON data
|
||||
|
||||
JsonObject &obj1 = root[rule_task];
|
||||
JsonObject *obj = &obj1;
|
||||
JsonObject *obj = &root;
|
||||
String subtype;
|
||||
uint32_t i = 0;
|
||||
while ((pos = rule_name.indexOf("#")) > 0) { // "SUBTYPE1#SUBTYPE2#CURRENT"
|
||||
@ -289,8 +515,8 @@ bool RulesRuleMatch(uint8_t rule_set, String &event, String &rule)
|
||||
str_value = (*obj)[rule_name]; // "CURRENT"
|
||||
}
|
||||
|
||||
//AddLog_P2(LOG_LEVEL_DEBUG, PSTR("RUL: Task %s, Name %s, Value |%s|, TrigCnt %d, TrigSt %d, Source %s, Json %s"),
|
||||
// rule_task.c_str(), rule_name.c_str(), rule_svalue, Rules.trigger_count[rule_set], bitRead(Rules.triggers[rule_set], Rules.trigger_count[rule_set]), event.c_str(), (str_value) ? str_value : "none");
|
||||
//AddLog_P2(LOG_LEVEL_DEBUG, PSTR("RUL: Name %s, Value |%s|, TrigCnt %d, TrigSt %d, Source %s, Json %s"),
|
||||
// rule_name.c_str(), rule_svalue, Rules.trigger_count[rule_set], bitRead(Rules.triggers[rule_set], Rules.trigger_count[rule_set]), event.c_str(), (str_value) ? str_value : "none");
|
||||
|
||||
Rules.event_value = str_value; // Prepare %value%
|
||||
|
||||
@ -419,7 +645,7 @@ bool RuleSetProcess(uint8_t rule_set, String &event_saved)
|
||||
|
||||
//AddLog_P2(LOG_LEVEL_DEBUG, PSTR("RUL: Event = %s, Rule = %s"), event_saved.c_str(), Settings.rules[rule_set]);
|
||||
|
||||
String rules = Settings.rules[rule_set];
|
||||
String rules = GetRule(rule_set);
|
||||
|
||||
Rules.trigger_count[rule_set] = 0;
|
||||
int plen = 0;
|
||||
@ -531,7 +757,7 @@ bool RulesProcessEvent(char *json_event)
|
||||
//AddLog_P2(LOG_LEVEL_DEBUG, PSTR("RUL: Event %s"), event_saved.c_str());
|
||||
|
||||
for (uint32_t i = 0; i < MAX_RULE_SETS; i++) {
|
||||
if (strlen(Settings.rules[i]) && bitRead(Settings.rule_enabled, i)) {
|
||||
if (GetRuleLen(i) && bitRead(Settings.rule_enabled, i)) {
|
||||
if (RuleSetProcess(i, event_saved)) { serviced = true; }
|
||||
}
|
||||
}
|
||||
@ -547,7 +773,7 @@ void RulesInit(void)
|
||||
{
|
||||
rules_flag.data = 0;
|
||||
for (uint32_t i = 0; i < MAX_RULE_SETS; i++) {
|
||||
if (Settings.rules[i][0] == '\0') {
|
||||
if (0 == GetRuleLen(i)) {
|
||||
bitWrite(Settings.rule_enabled, i, 0);
|
||||
bitWrite(Settings.rule_once, i, 0);
|
||||
}
|
||||
@ -1727,7 +1953,8 @@ void CmndRule(void)
|
||||
{
|
||||
uint8_t index = XdrvMailbox.index;
|
||||
if ((index > 0) && (index <= MAX_RULE_SETS)) {
|
||||
if ((XdrvMailbox.data_len > 0) && (XdrvMailbox.data_len < sizeof(Settings.rules[index -1]))) {
|
||||
// if ((XdrvMailbox.data_len > 0) && (XdrvMailbox.data_len < sizeof(Settings.rules[index -1]))) { // TODO postpone size calculation
|
||||
if (XdrvMailbox.data_len > 0) { // TODO postpone size calculation
|
||||
if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 10)) {
|
||||
switch (XdrvMailbox.payload) {
|
||||
case 0: // Off
|
||||
@ -1753,24 +1980,24 @@ void CmndRule(void)
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
int offset = 0;
|
||||
bool append = false;
|
||||
if ('+' == XdrvMailbox.data[0]) {
|
||||
offset = strlen(Settings.rules[index -1]);
|
||||
if (XdrvMailbox.data_len < (sizeof(Settings.rules[index -1]) - offset -1)) { // Check free space
|
||||
XdrvMailbox.data[0] = ' '; // Remove + and make sure at least one space is inserted
|
||||
} else {
|
||||
offset = -1; // Not enough space so skip it
|
||||
}
|
||||
XdrvMailbox.data[0] = ' '; // Remove + and make sure at least one space is inserted
|
||||
append = true;
|
||||
}
|
||||
if (offset != -1) {
|
||||
strlcpy(Settings.rules[index -1] + offset, ('"' == XdrvMailbox.data[0]) ? "" : XdrvMailbox.data, sizeof(Settings.rules[index -1]));
|
||||
int32_t res = SetRule(index - 1, ('"' == XdrvMailbox.data[0]) ? "" : XdrvMailbox.data, append);
|
||||
if (res < 0) {
|
||||
AddLog_P2(LOG_LEVEL_ERROR, PSTR("RUL: not enough space"));
|
||||
}
|
||||
}
|
||||
Rules.triggers[index -1] = 0; // Reset once flag
|
||||
}
|
||||
// snprintf_P (mqtt_data, sizeof(mqtt_data), PSTR("{\"%s%d\":\"%s\",\"Once\":\"%s\",\"StopOnError\":\"%s\",\"Free\":%d,\"Rules\":\"%s\"}"),
|
||||
// XdrvMailbox.command, index, GetStateText(bitRead(Settings.rule_enabled, index -1)), GetStateText(bitRead(Settings.rule_once, index -1)),
|
||||
// GetStateText(bitRead(Settings.rule_stop, index -1)), sizeof(Settings.rules[index -1]) - strlen(Settings.rules[index -1]) -1, Settings.rules[index -1]);
|
||||
snprintf_P (mqtt_data, sizeof(mqtt_data), PSTR("{\"%s%d\":\"%s\",\"Once\":\"%s\",\"StopOnError\":\"%s\",\"Free\":%d,\"Rules\":\"%s\"}"),
|
||||
XdrvMailbox.command, index, GetStateText(bitRead(Settings.rule_enabled, index -1)), GetStateText(bitRead(Settings.rule_once, index -1)),
|
||||
GetStateText(bitRead(Settings.rule_stop, index -1)), sizeof(Settings.rules[index -1]) - strlen(Settings.rules[index -1]) -1, Settings.rules[index -1]);
|
||||
GetStateText(bitRead(Settings.rule_stop, index -1)), sizeof(Settings.rules[0]) - GetRuleLenStorage(index - 1), GetRule(index - 1).c_str());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -703,7 +703,7 @@ const Z_AttributeConverter Z_PostProcess[] PROGMEM = {
|
||||
{ Zuint16, Cx0001, 0x0000, Z(MainsVoltage), &Z_Copy },
|
||||
{ Zuint8, Cx0001, 0x0001, Z(MainsFrequency), &Z_Copy },
|
||||
{ Zuint8, Cx0001, 0x0020, Z(BatteryVoltage), &Z_FloatDiv10 },
|
||||
{ Zuint8, Cx0001, 0x0021, Z(BatteryPercentage), &Z_Copy },
|
||||
{ Zuint8, Cx0001, 0x0021, Z(BatteryPercentage), &Z_FloatDiv2 },
|
||||
|
||||
// Device Temperature Configuration cluster
|
||||
{ Zint16, Cx0002, 0x0000, Z(CurrentTemperature), &Z_Copy },
|
||||
|
@ -230,11 +230,19 @@ uint32_t wc_setup(int32_t fsiz) {
|
||||
|
||||
sensor_t * wc_s = esp_camera_sensor_get();
|
||||
// initial sensors are flipped vertically and colors are a bit saturated
|
||||
/*
|
||||
if (OV3660_PID == wc_s->id.PID) {
|
||||
wc_s->set_vflip(wc_s, 1); // flip it back
|
||||
wc_s->set_brightness(wc_s, 1); // up the brightness just a bit
|
||||
wc_s->set_saturation(wc_s, -2); // lower the saturation
|
||||
}
|
||||
*/
|
||||
wc_s->set_vflip(wc_s, Settings.webcam_config.flip);
|
||||
wc_s->set_hmirror(wc_s, Settings.webcam_config.mirror);
|
||||
wc_s->set_brightness(wc_s, Settings.webcam_config.brightness -2); // up the brightness just a bit
|
||||
wc_s->set_saturation(wc_s, Settings.webcam_config.saturation -2); // lower the saturation
|
||||
wc_s->set_contrast(wc_s, Settings.webcam_config.contrast -2); // keep contrast
|
||||
|
||||
// drop down frame size for higher initial frame rate
|
||||
wc_s->set_framesize(wc_s, (framesize_t)fsiz);
|
||||
|
||||
@ -243,7 +251,6 @@ uint32_t wc_setup(int32_t fsiz) {
|
||||
wc_height = wc_fb->height;
|
||||
esp_camera_fb_return(wc_fb);
|
||||
|
||||
|
||||
#ifdef USE_FACE_DETECT
|
||||
fd_init();
|
||||
#endif
|
||||
@ -461,11 +468,11 @@ WiFiClient client;
|
||||
|
||||
void handleMjpeg(void) {
|
||||
AddLog_P2(WC_LOGLEVEL, PSTR("CAM: Handle camserver"));
|
||||
//if (!wc_stream_active) {
|
||||
if (!wc_stream_active) {
|
||||
wc_stream_active = 1;
|
||||
client = CamServer->client();
|
||||
AddLog_P2(WC_LOGLEVEL, PSTR("CAM: Create client"));
|
||||
//}
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef USE_FACE_DETECT
|
||||
@ -613,11 +620,9 @@ void handleMjpeg_task(void) {
|
||||
bool jpeg_converted = false;
|
||||
|
||||
if (!client.connected()) {
|
||||
wc_stream_active = 0;
|
||||
AddLog_P2(WC_LOGLEVEL, PSTR("CAM: Client fail"));
|
||||
goto exit;
|
||||
wc_stream_active = 0;
|
||||
}
|
||||
|
||||
if (1 == wc_stream_active) {
|
||||
client.flush();
|
||||
client.setTimeout(3);
|
||||
@ -626,15 +631,15 @@ void handleMjpeg_task(void) {
|
||||
"Content-Type: multipart/x-mixed-replace;boundary=" BOUNDARY "\r\n"
|
||||
"\r\n");
|
||||
wc_stream_active = 2;
|
||||
} else {
|
||||
}
|
||||
if (2 == wc_stream_active) {
|
||||
wc_fb = esp_camera_fb_get();
|
||||
if (!wc_fb) {
|
||||
wc_stream_active = 0;
|
||||
AddLog_P2(WC_LOGLEVEL, PSTR("CAM: Frame fail"));
|
||||
goto exit;
|
||||
wc_stream_active = 0;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
if (2 == wc_stream_active) {
|
||||
if (wc_fb->format != PIXFORMAT_JPEG) {
|
||||
jpeg_converted = frame2jpg(wc_fb, 80, &_jpg_buf, &_jpg_buf_len);
|
||||
if (!jpeg_converted){
|
||||
@ -673,13 +678,11 @@ void handleMjpeg_task(void) {
|
||||
if (jpeg_converted) { free(_jpg_buf); }
|
||||
esp_camera_fb_return(wc_fb);
|
||||
//AddLog_P2(WC_LOGLEVEL, PSTR("CAM: send frame"));
|
||||
|
||||
exit:
|
||||
if (!wc_stream_active) {
|
||||
AddLog_P2(WC_LOGLEVEL, PSTR("CAM: Stream exit"));
|
||||
client.flush();
|
||||
client.stop();
|
||||
}
|
||||
}
|
||||
if (0 == wc_stream_active) {
|
||||
AddLog_P2(WC_LOGLEVEL, PSTR("CAM: Stream exit"));
|
||||
client.flush();
|
||||
client.stop();
|
||||
}
|
||||
}
|
||||
|
||||
@ -750,13 +753,6 @@ void detect_motion(void) {
|
||||
}
|
||||
}
|
||||
|
||||
void wc_show_stream(void) {
|
||||
if (CamServer) {
|
||||
WSContentSend_P(PSTR("<p></p><center><img src='http://%s:81/stream' alt='Webcam stream' style='width:99%%;'></center><p></p>"),
|
||||
WiFi.localIP().toString().c_str());
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t wc_set_streamserver(uint32_t flag) {
|
||||
if (global_state.wifi_down) { return 0; }
|
||||
|
||||
@ -783,17 +779,30 @@ uint32_t wc_set_streamserver(uint32_t flag) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
void WcStreamControl(uint32_t resolution) {
|
||||
wc_set_streamserver(resolution);
|
||||
/*if (0 == resolution) {
|
||||
resolution=-1;
|
||||
}*/
|
||||
void WcStreamControl() {
|
||||
wc_set_streamserver(Settings.webcam_config.stream);
|
||||
int resolution = (!Settings.webcam_config.stream) ? -1 : Settings.webcam_config.resolution;
|
||||
wc_setup(resolution);
|
||||
}
|
||||
|
||||
void WcShowStream(void) {
|
||||
if (Settings.webcam_config.stream) {
|
||||
if (!CamServer) {
|
||||
WcStreamControl();
|
||||
delay(50); // Give the webcam webserver some time to prepare the stream
|
||||
}
|
||||
if (CamServer) {
|
||||
WSContentSend_P(PSTR("<p></p><center><img src='http://%s:81/stream' alt='Webcam stream' style='width:99%%;'></center><p></p>"),
|
||||
WiFi.localIP().toString().c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void wc_loop(void) {
|
||||
if (CamServer) { CamServer->handleClient(); }
|
||||
if (wc_stream_active) { handleMjpeg_task(); }
|
||||
if (CamServer) {
|
||||
CamServer->handleClient();
|
||||
if (wc_stream_active) { handleMjpeg_task(); }
|
||||
}
|
||||
if (motion_detect) { detect_motion(); }
|
||||
#ifdef USE_FACE_DETECT
|
||||
if (face_detect_time) { detect_face(); }
|
||||
@ -833,34 +842,103 @@ red led = gpio 33
|
||||
*/
|
||||
|
||||
void WcInit(void) {
|
||||
if (Settings.esp32_webcam_resolution > 10) {
|
||||
Settings.esp32_webcam_resolution = 0;
|
||||
if (!Settings.webcam_config.data) {
|
||||
Settings.webcam_config.stream = 1;
|
||||
Settings.webcam_config.resolution = 5;
|
||||
Settings.webcam_config.flip = 0;
|
||||
Settings.webcam_config.mirror = 0;
|
||||
Settings.webcam_config.saturation = 0; // -2
|
||||
Settings.webcam_config.brightness = 3; // 1
|
||||
Settings.webcam_config.contrast = 2; // 0
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*********************************************************************************************\
|
||||
* Commands
|
||||
\*********************************************************************************************/
|
||||
|
||||
#define D_CMND_WEBCAM "Webcam"
|
||||
#define D_PRFX_WEBCAM "WC"
|
||||
#define D_CMND_WC_STREAM "Stream"
|
||||
#define D_CMND_WC_RESOLUTION "Resolution"
|
||||
#define D_CMND_WC_MIRROR "Mirror"
|
||||
#define D_CMND_WC_FLIP "Flip"
|
||||
#define D_CMND_WC_SATURATION "Saturation"
|
||||
#define D_CMND_WC_BRIGHTNESS "Brightness"
|
||||
#define D_CMND_WC_CONTRAST "Contrast"
|
||||
|
||||
const char kWCCommands[] PROGMEM = "|" // no prefix
|
||||
D_CMND_WEBCAM
|
||||
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
|
||||
;
|
||||
|
||||
void (* const WCCommand[])(void) PROGMEM = {
|
||||
&CmndWebcam,
|
||||
&CmndWebcam, &CmndWebcamStream, &CmndWebcamResolution, &CmndWebcamMirror, &CmndWebcamFlip,
|
||||
&CmndWebcamSaturation, &CmndWebcamBrightness, &CmndWebcamContrast
|
||||
};
|
||||
|
||||
void CmndWebcam(void) {
|
||||
uint32_t flag = 0;
|
||||
if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 10)) {
|
||||
Settings.esp32_webcam_resolution=XdrvMailbox.payload;
|
||||
WcStreamControl(Settings.esp32_webcam_resolution);
|
||||
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}}"),
|
||||
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);
|
||||
}
|
||||
|
||||
void CmndWebcamStream(void) {
|
||||
if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 1)) {
|
||||
Settings.webcam_config.stream = XdrvMailbox.payload;
|
||||
if (!Settings.webcam_config.stream) { WcStreamControl(); } // Stop stream
|
||||
}
|
||||
if (CamServer) { flag = 1; }
|
||||
Response_P(PSTR("{\"" D_CMND_WEBCAM "\":{\"Streaming\":\"%s\"}"),GetStateText(flag));
|
||||
ResponseCmndStateText(Settings.webcam_config.stream);
|
||||
}
|
||||
|
||||
void CmndWebcamResolution(void) {
|
||||
if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 10)) {
|
||||
Settings.webcam_config.resolution = XdrvMailbox.payload;
|
||||
wc_set_options(0, Settings.webcam_config.resolution);
|
||||
}
|
||||
ResponseCmndNumber(Settings.webcam_config.resolution);
|
||||
}
|
||||
|
||||
void CmndWebcamMirror(void) {
|
||||
if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 1)) {
|
||||
Settings.webcam_config.mirror = XdrvMailbox.payload;
|
||||
wc_set_options(3, Settings.webcam_config.mirror);
|
||||
}
|
||||
ResponseCmndStateText(Settings.webcam_config.mirror);
|
||||
}
|
||||
|
||||
void CmndWebcamFlip(void) {
|
||||
if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 1)) {
|
||||
Settings.webcam_config.flip = XdrvMailbox.payload;
|
||||
wc_set_options(2, Settings.webcam_config.flip);
|
||||
}
|
||||
ResponseCmndStateText(Settings.webcam_config.flip);
|
||||
}
|
||||
|
||||
void CmndWebcamSaturation(void) {
|
||||
if ((XdrvMailbox.payload >= -2) && (XdrvMailbox.payload <= 2)) {
|
||||
Settings.webcam_config.saturation = XdrvMailbox.payload +2;
|
||||
wc_set_options(6, Settings.webcam_config.saturation -2);
|
||||
}
|
||||
ResponseCmndNumber(Settings.webcam_config.saturation -2);
|
||||
}
|
||||
|
||||
void CmndWebcamBrightness(void) {
|
||||
if ((XdrvMailbox.payload >= -2) && (XdrvMailbox.payload <= 2)) {
|
||||
Settings.webcam_config.brightness = XdrvMailbox.payload +2;
|
||||
wc_set_options(5, Settings.webcam_config.brightness -2);
|
||||
}
|
||||
ResponseCmndNumber(Settings.webcam_config.brightness -2);
|
||||
}
|
||||
|
||||
void CmndWebcamContrast(void) {
|
||||
if ((XdrvMailbox.payload >= -2) && (XdrvMailbox.payload <= 2)) {
|
||||
Settings.webcam_config.contrast = XdrvMailbox.payload +2;
|
||||
wc_set_options(4, Settings.webcam_config.contrast -2);
|
||||
}
|
||||
ResponseCmndNumber(Settings.webcam_config.contrast -2);
|
||||
}
|
||||
|
||||
/*********************************************************************************************\
|
||||
@ -878,13 +956,7 @@ bool Xdrv81(uint8_t function) {
|
||||
wc_pic_setup();
|
||||
break;
|
||||
case FUNC_WEB_ADD_MAIN_BUTTON:
|
||||
if (Settings.esp32_webcam_resolution) {
|
||||
//#ifndef USE_SCRIPT
|
||||
WcStreamControl(Settings.esp32_webcam_resolution);
|
||||
delay(50); // Give the webcam webserver some time to prepare the stream
|
||||
wc_show_stream();
|
||||
//#endif
|
||||
}
|
||||
WcShowStream();
|
||||
break;
|
||||
case FUNC_COMMAND:
|
||||
result = DecodeCommand(kWCCommands, WCCommand);
|
||||
|
Loading…
x
Reference in New Issue
Block a user