Merge branch 'development' of https://github.com/arendst/Tasmota into development

This commit is contained in:
Christopher Tremblay 2020-10-23 03:48:37 -07:00
commit 8eba0c9426
22 changed files with 280 additions and 90 deletions

View File

@ -8,7 +8,7 @@ All notable changes to this project will be documented in this file.
- Support for Vietnamese language translations by Tâm.NT
- Support for timers in case of no-sunset permanent day by cybermaus (#9543)
- Command ``NoDelay`` for immediate backlog command execution by Erik Montnemery (#9544)
- Command ``SwitchMode 15`` sending only MQTT message on switch change (#9596)
- Command ``SwitchMode 15`` sending only MQTT message on switch change (#9593)
- Support for EZO Ph and ORP sensors by Christopher Tremblay (#9567)
- Support for EZO RTD sensors by Christopher Tremblay (#9585)
- On ZigbeeBridge support for glowing led when permit join is active (#9581)
@ -20,6 +20,7 @@ All notable changes to this project will be documented in this file.
- Management of serial baudrate (#9554)
- ``#define MQTT_FINGERPRINT`` from string to hexnumbers (#9570)
- Rotary driver adjusted accordingly if Mi Desk Lamp module is selected (#9399)
- Tasmota Arduino Core v2.7.4.5 allowing webpassword over 47 characters (#9687)
### Fixed
- Convert AdcParam parameters from versions before v9.0.0.2

View File

@ -60,7 +60,7 @@ The attached binaries can also be downloaded from http://ota.tasmota.com/tasmota
## Changelog v9.0.0.2
### Added
- Command ``NoDelay`` for immediate backlog command execution by Erik Montnemery (#9544)
- Command ``SwitchMode 15`` sending only MQTT message on switch change (#9596)
- Command ``SwitchMode 15`` sending only MQTT message on switch change (#9593)
- Zigbee command ``ZbData`` for better support of device specific data
- Optional support for Mitsubishi Electric HVAC by David Gwynne (#9237)
- Optional support for Orno WE517-Modbus energy meter by Maxime Vincent (#9353)
@ -89,6 +89,7 @@ The attached binaries can also be downloaded from http://ota.tasmota.com/tasmota
- NeoPixelBus library from v2.5.0.09 to v2.6.0
- Management of serial baudrate (#9554)
- Rotary driver adjusted accordingly if Mi Desk Lamp module is selected (#9399)
- Tasmota Arduino Core v2.7.4.5 allowing webpassword over 47 characters (#9687)
### Fixed
- Ledlink blink when no network connected regression from v8.3.1.4 (#9292)

View File

@ -0,0 +1,9 @@
name=EEPROM 24C128
version=
author=Julien Le Sech
maintainer=Julien Le Sech - www.idreammicro.com
sentence=EEPROM 24C128 / 24C256 memory driver.
paragraph=EEPROM 24C128 / 24C256 memory driver.
category=
url=
architectures=*

View File

@ -0,0 +1,9 @@
name=Adafruit SH1106-gemu-1.0
version=1.0
author=Adafruit
maintainer=Adafruit <info@adafruit.com>
sentence=SH1106
paragraph=SH1106
category=Display
url=
architectures=*

View File

@ -0,0 +1,9 @@
name=Adafruit TSL2591
version=
author=Adafruit
maintainer=Adafruit <info@adafruit.com>
sentence=Library for Adafruit TSL2591
paragraph=Library for Adafruit TSL2591
category=
url=https://www.adafruit.com/products/1980
architectures=*

View File

@ -0,0 +1,9 @@
name=NTP Library
version=
author=Mooneer Salem
maintainer=Mooneer Salem <mooneer@gmail.com>
sentence=NTP
paragraph=NTP
category=
url=
architectures=*

View File

@ -0,0 +1,9 @@
name=BME680
version=
author=
maintainer=Bosch Sensortec GmbH
sentence=Sensor driver for BME680 sensor
paragraph=Sensor driver for BME680 sensor
category=Sensor
url=
architectures=esp8266

View File

@ -40,7 +40,7 @@ bool AudioFileSourceHTTPStream::open(const char *url)
http.begin(client, url);
http.setReuse(true);
#ifndef ESP32
http.setFollowRedirects(true);
http.setFollowRedirects(HTTPC_STRICT_FOLLOW_REDIRECTS);
#endif
int code = http.GET();
if (code != HTTP_CODE_OK) {

View File

@ -0,0 +1,9 @@
name=FrogmoreScd30
version=
author=Frogmore42
maintainer=Frogmore42
sentence=SCD30
paragraph=SCD30
category=Sensor
url=
architectures=esp8266,esp32

View File

@ -0,0 +1,9 @@
name=MPU6050 I2C
version=
author=Jeff Rowberg
maintainer=Jeff Rowberg <jeff@rowberg.net>
sentence=This is a library for the MPU6050.
paragraph=This is a library for the MPU6050.
category=Sensors
url=https://github.com/jrowberg/i2cdevlib
architectures=*

View File

@ -0,0 +1,9 @@
name=LOLIN_HP303B
version=1.0.0
author=WEMOS.CC <support@wemos.cc>
maintainer=WEMOS.CC
sentence=Library for the <a href="https://www.wemos.cc">HP303B.</a>.
paragraph=LOLIN HP303B
category=Device Control
url=https://github.com/wemos/LOLIN_HP303B_Library
architectures=*

View File

@ -0,0 +1,9 @@
name=cc1101
version=
author=Daniel Berenguer
maintainer=panStamp <contact@panstamp.com>
sentence=.
paragraph=
category=
url=
architectures=esp8266

View File

@ -0,0 +1,9 @@
name=MLX90640
version=
author=Melexis N.V.
maintainer=
sentence=
paragraph=MLX90640
category=Sensor
url=
architectures=*

View File

@ -54,6 +54,7 @@ default_envs =
[platformio]
description = Provide ESP8266 / ESP32 based devices with Web, MQTT and OTA firmware
src_dir = tasmota
lib_dir = lib
build_cache_dir = .cache
extra_configs = platformio_tasmota32.ini
platformio_tasmota_env.ini
@ -80,6 +81,8 @@ upload_speed = 115200
upload_resetmethod = nodemcu
upload_port = COM5
extra_scripts = ${scripts_defaults.extra_scripts}
shared_libdeps_dir = lib
lib_ignore = ${esp82xx_defaults.lib_ignore}
[scripts_defaults]
extra_scripts = pio/strip-floats.py
@ -121,6 +124,21 @@ build_flags = ${esp_defaults.build_flags}
; restrict to minimal mime-types
-DMIMETYPE_MINIMAL
lib_ignore = Servo(esp8266)
ESP8266AVRISP
ESP8266LLMNR
ESP8266NetBIOS
ESP8266SSDP
ESP8266WiFiMesh
Ethernet(esp8266)
GDBStub
TFT_Touch_Shield_V2
ESP8266WiFiMesh
EspSoftwareSerial
SPISlave
Hash
[irremoteesp_full]
build_flags = -DUSE_IR_REMOTE_FULL
-U_IR_ENABLE_DEFAULT_

View File

@ -14,11 +14,72 @@ upload_port = ${common.upload_port}
upload_resetmethod = ${common.upload_resetmethod}
upload_speed = ${common.upload_speed}
extra_scripts = ${common.extra_scripts}
lib_extra_dirs = ${common.shared_libdeps_dir}
lib_ignore = ${common.lib_ignore}
[env:tasmota]
[env:tasmota-minimal]
build_flags = ${common.build_flags} -DFIRMWARE_MINIMAL
lib_ignore = ${common.lib_ignore}
LittleFS(esp8266)
A4988_Stepper
Adafruit BusIO
Adafruit CCS811 Library
Adafruit GFX Library
Arduino ST7789 Library
Adafruit ILI9341
ILI9488
RA8876
Adafruit LED Backpack Library
Waveshare esp 2.9 inch e-paper display driver
LiquidCrystal_I2C
Adafruit MAX31865 library
Adafruit MCP9808 Library
Adafruit SGP30 Sensor
Adafruit SH1106-gemu-1.0
Adafruit SSD1306
Adafruit TSL2591
Joba Tsl2561 Library
Joba_Tsl2561
Adafruit VEML7700 Library
SSD3115
NTP Library
base64
C2Programmer
;ESP KNX IP Library
I2Cdevlib-Core
MPU6050 I2C
LibTeleinfo
LinkedList
LOLIN_HP303B
Mutichannel_Gas_Sensor
NewPing
OneWire
OpenTherm Library
rc-switch
RF24
TasmotaModbus
TasmotaSerial
UdpListener
VL53L0X
VL53L1X
MLX90640
FrogmoreScd30
cc1101
EEPROM 24C128
BME680
FrogmoreScd30
FT5206_Library
HPMA115S0 Arduino Library
Grove - Multichannel Gas Sensor
BearSSL
OneWire
IRremoteESP8266
ESP8266Audio
ESP8266SAM
KeeloqLib
NeoPixelBus
[env:tasmota-lite]
build_flags = ${common.build_flags} -DFIRMWARE_LITE

View File

@ -48,7 +48,7 @@ bool knx_started = false;
void OsWatchTicker(void)
{
uint32_t t = millis();
uint32_t last_run = abs(t - oswatch_last_loop_time);
uint32_t last_run = t - oswatch_last_loop_time;
#ifdef DEBUG_THEO
int32_t rssi = WiFi.RSSI();

View File

@ -36,33 +36,36 @@
#ifndef ROTARY_MAX_STEPS
#define ROTARY_MAX_STEPS 10 // Rotary step boundary
#endif
#ifndef ROTARY_TIMEOUT
#define ROTARY_TIMEOUT 2 // 2 * RotaryHandler() call which is usually 2 * 0.05 seconds
#endif
#ifndef ROTARY_DEBOUNCE
#define ROTARY_DEBOUNCE 10 // Debounce time in milliseconds
#endif
// (0) = Mi Desk lamp (1) = Normal rotary
// ---------------------------- ----------------------
const uint8_t rotary_dimmer_increment[2] = { 100 / (ROTARY_MAX_STEPS * 3), 100 / ROTARY_MAX_STEPS }; // Dimmer 1..100 = 100
const uint8_t rotary_ct_increment[2] = { 350 / (ROTARY_MAX_STEPS * 3), 350 / ROTARY_MAX_STEPS }; // Ct 153..500 = 347
const uint8_t rotary_color_increment[2] = { 360 / (ROTARY_MAX_STEPS * 3), 360 / ROTARY_MAX_STEPS }; // Hue 0..359 = 360
const uint8_t ROTARY_TIMEOUT = 10; // 10 * RotaryHandler() call which is usually 10 * 0.05 seconds
const uint8_t ROTARY_DEBOUNCE = 10; // Debounce time in milliseconds
const uint8_t rotary_offset = 128;
const int8_t rotary_state_pos[16] = { 0, 1, -1, 2, -1, 0, -2, 1, 1, -2, 0, -1, 2, -1, 1, 0 };
struct ROTARY {
uint8_t model = 1;
bool present = false;
uint8_t model;
bool present;
} Rotary;
struct tEncoder {
volatile uint32_t debounce = 0;
volatile int8_t direction = 0; // Control consistent direction
volatile uint8_t state = 0;
volatile uint8_t position = 128;
uint8_t last_position = 128;
volatile uint8_t position;
volatile int8_t direction = 0; // Control consistent direction
volatile int8_t pina;
volatile int8_t pinb;
uint8_t timeout = 0; // Disallow direction change within 0.5 second
int8_t abs_position[2] = { 0 };
int8_t pina = -1;
int8_t pinb = -1;
bool changed = false;
volatile bool busy = false;
};
tEncoder Encoder[MAX_ROTARIES];
@ -93,34 +96,17 @@ bool RotaryButtonPressed(uint32_t button_index) {
void ICACHE_RAM_ATTR RotaryIsrArgMiDesk(void *arg) {
tEncoder* encoder = static_cast<tEncoder*>(arg);
if (encoder->busy) { return; }
// https://github.com/PaulStoffregen/Encoder/blob/master/Encoder.h
uint8_t state = encoder->state & 3;
uint32_t state = encoder->state & 3;
if (digitalRead(encoder->pina)) { state |= 4; }
if (digitalRead(encoder->pinb)) { state |= 8; }
encoder->position += rotary_state_pos[state];
encoder->state = (state >> 2);
switch (state) {
case 1: case 7: case 8: case 14:
encoder->position++;
return;
case 2: case 4: case 11: case 13:
encoder->position--;
return;
case 3: case 12:
encoder->position += 2;
return;
case 6: case 9:
encoder->position -= 2;
return;
}
}
void ICACHE_RAM_ATTR RotaryIsrArg(void *arg) {
tEncoder* encoder = static_cast<tEncoder*>(arg);
if (encoder->busy) { return; }
// Theo Arends
uint32_t time = millis();
if ((encoder->debounce < time) || (encoder->debounce > time + ROTARY_DEBOUNCE)) {
@ -144,9 +130,10 @@ void RotaryInit(void) {
for (uint32_t index = 0; index < MAX_ROTARIES; index++) {
Encoder[index].pinb = -1;
if (PinUsed(GPIO_ROT1A, index) && PinUsed(GPIO_ROT1B, index)) {
Encoder[index].position = rotary_offset;
Encoder[index].pina = Pin(GPIO_ROT1A, index);
pinMode(Encoder[index].pina, INPUT_PULLUP);
Encoder[index].pinb = Pin(GPIO_ROT1B, index);
pinMode(Encoder[index].pina, INPUT_PULLUP);
pinMode(Encoder[index].pinb, INPUT_PULLUP);
if (0 == Rotary.model) {
attachInterruptArg(Encoder[index].pina, RotaryIsrArgMiDesk, &Encoder[index], CHANGE);
@ -181,12 +168,14 @@ void RotaryHandler(void) {
Encoder[index].direction = 0;
}
}
if (Encoder[index].last_position == Encoder[index].position) { continue; }
Encoder[index].busy = true;
if (rotary_offset == Encoder[index].position) { continue; }
Encoder[index].timeout = ROTARY_TIMEOUT; // Prevent fast direction changes within 0.5 second
int rotary_position = Encoder[index].position - Encoder[index].last_position;
noInterrupts();
int rotary_position = Encoder[index].position - rotary_offset;
Encoder[index].position = rotary_offset;
interrupts();
if (Settings.save_data && (save_data_counter < 2)) {
save_data_counter = 3; // Postpone flash writes while rotary is turned
@ -232,10 +221,6 @@ void RotaryHandler(void) {
#ifdef USE_LIGHT
}
#endif // USE_LIGHT
Encoder[index].last_position = 128;
Encoder[index].position = 128;
Encoder[index].busy = false;
}
}

View File

@ -420,8 +420,11 @@ struct SCRIPT_MEM {
#ifdef USE_SCRIPT_GLOBVARS
UDP_FLAGS udp_flags;
#endif
char web_mode;
} glob_script_mem;
bool event_handeled = false;
@ -768,8 +771,14 @@ char *script;
script_mem += size;
#ifdef SCRIPT_LARGE_VNBUFF
glob_script_mem.vnp_offset = (uint16_t*)script_mem;
uint32_t alignedmem = (uint32_t)script_mem;
if (alignedmem&1) {
alignedmem++;
size = vars*sizeof(uint16_t)+1;
} else {
size = vars*sizeof(uint16_t);
}
glob_script_mem.vnp_offset = (uint16_t*)alignedmem;
#else
glob_script_mem.vnp_offset = (uint8_t*)script_mem;
size = vars*sizeof(uint8_t);
@ -3107,7 +3116,10 @@ chknext:
goto exit;
}
#endif // USE_FT5206
if (!strncmp(vname, "wm", 2)) {
fvar = glob_script_mem.web_mode;
goto exit;
}
if (!strncmp(vname, "wday", 4)) {
fvar = RtcTime.day_of_week;
goto exit;
@ -3718,6 +3730,29 @@ void esp32_beep(int32_t freq ,uint32_t len) {
//#define IFTHEN_DEBUG
char *scripter_sub(char *lp, uint8_t fromscriptcmd) {
lp += 1;
char *slp = lp;
uint8_t plen = 0;
while (*lp) {
if (*lp=='\n'|| *lp=='\r'|| *lp=='(') {
break;
}
lp++;
plen++;
}
if (fromscriptcmd) {
char *sp = glob_script_mem.scriptptr;
glob_script_mem.scriptptr = glob_script_mem.scriptptr_bu;
Run_Scripter(slp, plen, 0);
glob_script_mem.scriptptr = sp;
} else {
Run_Scripter(slp, plen, 0);
}
lp = slp;
return lp;
}
#define IF_NEST 8
// execute section of scripter
int16_t Run_Scripter(const char *type, int8_t tlen, char *js) {
@ -4107,7 +4142,16 @@ int16_t Run_script_sub(const char *type, int8_t tlen, JsonParserObject *jo) {
goto next_line;
}
#endif //ESP32
else if (!strncmp(lp, "wcs", 3)) {
lp+=4;
// skip one space after cmd
char tmp[256];
Replace_Cmd_Vars(lp ,1 , tmp, sizeof(tmp));
WSContentFlush();
WSContentSend_P(PSTR("%s"),tmp);
WSContentFlush();
goto next_line;
}
else if (!strncmp(lp,"=>",2) || !strncmp(lp,"->",2) || !strncmp(lp,"+>",2) || !strncmp(lp,"print",5)) {
// execute cmd
uint8_t sflag = 0,pflg = 0,svmqtt,swll;
@ -4170,25 +4214,7 @@ int16_t Run_script_sub(const char *type, int8_t tlen, JsonParserObject *jo) {
goto next_line;
} else if (!strncmp(lp, "=#", 2)) {
// subroutine
lp += 1;
char *slp = lp;
uint8_t plen = 0;
while (*lp) {
if (*lp=='\n'|| *lp=='\r'|| *lp=='(') {
break;
}
lp++;
plen++;
}
if (fromscriptcmd) {
char *sp = glob_script_mem.scriptptr;
glob_script_mem.scriptptr = glob_script_mem.scriptptr_bu;
Run_Scripter(slp, plen, 0);
glob_script_mem.scriptptr = sp;
} else {
Run_Scripter(slp, plen, 0);
}
lp = slp;
lp = scripter_sub(lp, fromscriptcmd);
goto next_line;
} else if (!strncmp(lp, "=(", 2)) {
lp += 2;
@ -6474,10 +6500,10 @@ uint32_t cnt;
}
void ScriptWebShow(char mc) {
uint8_t web_script,xflg = 0;
uint8_t web_script;
glob_script_mem.web_mode = mc;
if (mc=='w' || mc=='x') {
if (mc=='x') {
xflg = 1;
mc='$';
}
web_script = Run_Scripter(">w", -2, 0);
@ -6543,11 +6569,22 @@ void ScriptWebShow(char mc) {
} else {
goto nextwebline;
}
} else if (!strncmp(lp, "%=#", 3)) {
// subroutine
lp = scripter_sub(lp + 1, 0);
goto nextwebline;
}
Replace_Cmd_Vars(lp, 1, tmp, sizeof(tmp));
char *lin = tmp;
if ((!mc && (*lin!='$')) || (mc=='w' && (*lin!='$'))) {
/*if (!mc || mc=='w') {
if (*lin=='$') {
lin++;
if (!strncmp(lin,"gc(", 3)) {
goto exgc;
}
}*/
// normal web section
if (*lin=='@') {
lin++;

View File

@ -316,7 +316,6 @@ void NewHAssDiscovery(void)
mac_address.replace(":", "");
snprintf_P(unique_id, sizeof(unique_id), PSTR("%s"), mac_address.c_str());
snprintf_P(stopic, sizeof(stopic), PSTR("tasmota/discovery/%s/config"), unique_id);
GetTopic_P(state_topic, TELE, mqtt_topic, PSTR(D_RSLT_HASS_STATE));
// Send empty message if new discovery is disabled
masterlog_level = 4; // Hide topic on clean and remove use weblog 4 to see it
@ -1071,8 +1070,7 @@ void HAssDiscover(void)
void HAssAnyKey(void)
{
if (!Settings.flag.hass_discovery) { return; } // SetOption19 - Control Home Assistantautomatic discovery (See SetOption59)
uint32_t key = (XdrvMailbox.payload >> 16) & 0xFF; // 0 = Button, 1 = Switch
uint32_t key = (XdrvMailbox.payload >> 16) & 0xFF; // 0 = KEY_BUTTON, 1 = KEY_SWITCH
uint32_t device = XdrvMailbox.payload & 0xFF; // Device number or 1 if more Buttons than Devices
uint32_t state = (XdrvMailbox.payload >> 8) & 0xFF; // 0 = Off, 1 = On, 2 = Toggle, 3 = Hold, 10,11,12,13 and 14 for Button Multipress
@ -1095,12 +1093,14 @@ void HAssAnyKey(void)
char stopic[TOPSZ];
if (!key) {
if (state == 3) {
snprintf_P(trg_state, sizeof(trg_state), GetStateText(3));
} else {
if (state == 2) { state = 10; }
GetTextIndexed(trg_state, sizeof(trg_state), state -9, kHAssTriggerStringButtons);
}
}
GetTopic_P(stopic, STAT, mqtt_topic, scommand);
Response_P(S_JSON_COMMAND_SVALUE, (evkey) ? "TRIG" : PSTR(D_RSLT_STATE), (key) ? GetStateText(state) : trg_state);

View File

@ -595,17 +595,14 @@ void TuyaProcessStatePacket(void) {
Tuya.FanState = packetValue;
}
if ((fnId == TUYA_MCU_FUNC_DIMMER) || (fnId == TUYA_MCU_FUNC_REPORT1)) {
dimIndex = 0;
}
if (fnId == TUYA_MCU_FUNC_DIMMER || fnId == TUYA_MCU_FUNC_REPORT1) { dimIndex = 0; }
if ((fnId == TUYA_MCU_FUNC_DIMMER2) || (fnId == TUYA_MCU_FUNC_REPORT2) || (fnId == TUYA_MCU_FUNC_CT)) {
dimIndex = 1;
if (Settings.flag3.pwm_multi_channels) {
Tuya.Levels[dimIndex] = changeUIntScale(packetValue, 0, Settings.dimmer_hw_max, 0, 100);
} else {
if (fnId == TUYA_MCU_FUNC_DIMMER2 || fnId == TUYA_MCU_FUNC_REPORT2 || fnId == TUYA_MCU_FUNC_CT) { dimIndex = 1; }
if (dimIndex == 1 && !Settings.flag3.pwm_multi_channels) {
Tuya.Levels[dimIndex] = changeUIntScale(packetValue, 0, Settings.dimmer_hw_max, Tuya.CTMax, Tuya.CTMin);
}
} else {
Tuya.Levels[dimIndex] = changeUIntScale(packetValue, 0, Settings.dimmer_hw_max, 0, 100);
}
AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TYA: RX value %d from dpId %d "), packetValue, Tuya.buffer[dpidStart]);

View File

@ -1824,7 +1824,7 @@ void ZCLFrame::postProcessAttributes(uint16_t shortaddr, Z_attribute_list& attr_
switch (ccccaaaa) {
case 0x00000004: zigbee_devices.setManufId(shortaddr, attr.getStr()); break;
case 0x00000005: zigbee_devices.setModelId(shortaddr, attr.getStr()); break;
case 0x00010021: zigbee_devices.setBatteryPercent(shortaddr, uval16); break;
case 0x00010021: zigbee_devices.setBatteryPercent(shortaddr, uval16 / 2); break;
case 0x00060000:
case 0x00068000: device.setPower(attr.getBool(), src_ep); break;
}

View File

@ -32,7 +32,7 @@ const uint32_t ARILUX_RF_SEPARATION_LIMIT = 4300; // Microseconds
const uint32_t ARILUX_RF_RECEIVE_TOLERANCE = 60; // Percentage
struct ARILUX {
unsigned int rf_timings[ARILUX_RF_MAX_CHANGES];
int rf_timings[ARILUX_RF_MAX_CHANGES];
unsigned long rf_received_value = 0;
unsigned long rf_last_received_value = 0;
@ -52,15 +52,15 @@ void AriluxRfInterrupt(void) ICACHE_RAM_ATTR; // As iram is tight and it works
void AriluxRfInterrupt(void)
{
unsigned long time = micros();
unsigned int duration = time - Arilux.rf_lasttime;
int duration = time - Arilux.rf_lasttime;
if (duration > ARILUX_RF_SEPARATION_LIMIT) {
if (abs(duration - Arilux.rf_timings[0]) < 200) {
Arilux.rf_repeat_count++;
if (Arilux.rf_repeat_count == 2) {
unsigned long code = 0;
const unsigned int delay = Arilux.rf_timings[0] / 31;
const unsigned int delayTolerance = delay * ARILUX_RF_RECEIVE_TOLERANCE / 100;
const int delay = Arilux.rf_timings[0] / 31;
const int delayTolerance = delay * ARILUX_RF_RECEIVE_TOLERANCE / 100;
for (unsigned int i = 1; i < Arilux.rf_change_count -1; i += 2) {
code <<= 1;
if (abs(Arilux.rf_timings[i] - (delay *3)) < delayTolerance && abs(Arilux.rf_timings[i +1] - delay) < delayTolerance) {