diff --git a/.github/ISSUE_TEMPLATE/Bug_report.md b/.github/ISSUE_TEMPLATE/Bug_report.md
index f9622222f..4b4027bf8 100644
--- a/.github/ISSUE_TEMPLATE/Bug_report.md
+++ b/.github/ISSUE_TEMPLATE/Bug_report.md
@@ -38,31 +38,31 @@ _Make sure your have performed every step and checked the applicable boxes befor
- [ ] Self-compiled
- [ ] IDE / Compiler used: _____
- [ ] Flashing tools used: _____
-- [ ] Provide the output of command: ``Backlog Template; Module; GPIO 255``:
- ```
+- [ ] Provide the output of command: `Backlog Template; Module; GPIO 255`:
+```
Configuration output here:
- ```
-- [ ] If using rules, provide the output of this command: ``Backlog Rule1; Rule2; Rule3``:
- ```
+```
+- [ ] If using rules, provide the output of this command: `Backlog Rule1; Rule2; Rule3`:
+```
Rules output here:
- ```
-- [ ] Provide the output of this command: ``Status 0``:
- ```
+```
+- [ ] Provide the output of this command: `Status 0`:
+```
STATUS 0 output here:
- ```
+```
- [ ] Provide the output of the Console log output when you experience your issue; if applicable:
- _(Please use_ ``weblog 4`` _for more debug information)_
- ```
+ _(Please use_ `weblog 4` _for more debug information)_
+```
Console output here:
- ```
+```
### TO REPRODUCE
_Steps to reproduce the behavior:_
diff --git a/README.md b/README.md
index 7b101df63..acb6430d5 100644
--- a/README.md
+++ b/README.md
@@ -20,7 +20,7 @@ In addition to the [release webpage](https://github.com/arendst/Tasmota/releases
## Development
-[](https://github.com/arendst/Tasmota)
+[](https://github.com/arendst/Tasmota)
[](http://thehackbox.org/tasmota/)
[](https://travis-ci.org/arendst/Tasmota)
diff --git a/RELEASENOTES.md b/RELEASENOTES.md
index 1ea030e6a..0baecb42a 100644
--- a/RELEASENOTES.md
+++ b/RELEASENOTES.md
@@ -47,9 +47,10 @@ The following binary downloads have been compiled with ESP8266/Arduino library c
## Changelog
-### Version 7.1.1 Betty
+### Version 7.1.2 Betty
-- Fix slider for devices with one or two channels like only white or white/yellow
-- Fix TasmotaSlave buffer overrun on Tele
-- Fix light scheme 4 speed (#7072)
-- Add support for TasmotaSlave executing commands on Tasmota
+- Fix lost functionality of GPIO9 and GPIO10 on some devices (#7080)
+- Fix Zigbee uses Hardware Serial if GPIO 1/3 or GPIO 13/15 and SerialLog 0 (#7071)
+- Fix WS2812 power control (#7090)
+- Change light color schemes 2, 3 and 4 from color wheel to Hue driven with user Saturation control
+- Change log buffer size from 520 to 700 characters accomodating full rule text (#7110)
diff --git a/platformio_override_sample.ini b/platformio_override_sample.ini
index 9827447dd..6cbd749c4 100644
--- a/platformio_override_sample.ini
+++ b/platformio_override_sample.ini
@@ -28,6 +28,7 @@ default_envs =
[common]
platform = ${core_active.platform}
+platform_packages = ${core_active.platform_packages}
build_flags = ${core_active.build_flags}
; *** Use settings from file user_config_override.h
-DUSE_CONFIG_OVERRIDE
@@ -72,6 +73,7 @@ build_flags = ${core_2_6_1.build_flags}
;platform = ${core_2_6_2.platform}
;build_flags = ${core_2_6_2.build_flags}
;platform = ${core_stage.platform}
+;platform_packages = ${core_stage.platform_packages}
;build_flags = ${core_stage.build_flags}
@@ -85,11 +87,11 @@ build_flags = ${esp82xx_defaults.build_flags}
; -DPIO_FRAMEWORK_ARDUINO_ESPRESSIF_SDK221
; NONOSDK22x_190313
; -DPIO_FRAMEWORK_ARDUINO_ESPRESSIF_SDK22x_190313
-; NONOSDK22x_190703 = 2.2.2-dev(38a443e) (Tasmota default)
+; 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.2-dev(5ab15d1)
+; NONOSDK22x_191024 = 2.2.1+111-dev(5ab15d1)
; -DPIO_FRAMEWORK_ARDUINO_ESPRESSIF_SDK22x_191024
-; NONOSDK22x_191105 = 2.2.2-dev(bb83b9b)
+; NONOSDK22x_191105 = 2.2.1+113-dev(bb83b9b)
; -DPIO_FRAMEWORK_ARDUINO_ESPRESSIF_SDK22x_191105
; NONOSDK3V0 (known issues)
; -DPIO_FRAMEWORK_ARDUINO_ESPRESSIF_SDK3
@@ -129,11 +131,11 @@ build_flags = ${esp82xx_defaults.build_flags}
; -DPIO_FRAMEWORK_ARDUINO_ESPRESSIF_SDK221
; NONOSDK22x_190313
; -DPIO_FRAMEWORK_ARDUINO_ESPRESSIF_SDK22x_190313
-; NONOSDK22x_190703 = 2.2.2-dev(38a443e) (Tasmota default)
+; 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.2-dev(5ab15d1)
+; NONOSDK22x_191024 = 2.2.1+111-dev(5ab15d1)
; -DPIO_FRAMEWORK_ARDUINO_ESPRESSIF_SDK22x_191024
-; NONOSDK22x_191105 = 2.2.2-dev(bb83b9b)
+; NONOSDK22x_191105 = 2.2.1+113-dev(bb83b9b)
; -DPIO_FRAMEWORK_ARDUINO_ESPRESSIF_SDK22x_191105
; NONOSDK3V0 (known issues)
; -DPIO_FRAMEWORK_ARDUINO_ESPRESSIF_SDK3
@@ -165,7 +167,8 @@ build_flags = ${esp82xx_defaults.build_flags}
[core_stage]
; *** Esp8266 core for Arduino version latest beta
-platform = https://github.com/platformio/platform-espressif8266.git#feature/stage
+platform = espressif8266@2.3.1
+platform_packages = framework-arduinoespressif8266 @ https://github.com/esp8266/Arduino.git
build_flags = ${esp82xx_defaults.build_flags}
-Wl,-Teagle.flash.1m.ld
-DBEARSSL_SSL_BASIC
@@ -173,12 +176,14 @@ build_flags = ${esp82xx_defaults.build_flags}
; -DPIO_FRAMEWORK_ARDUINO_ESPRESSIF_SDK221
; NONOSDK22x_190313
; -DPIO_FRAMEWORK_ARDUINO_ESPRESSIF_SDK22x_190313
-; NONOSDK22x_190703 (Tasmota default)
+; NONOSDK22x_190703 = 2.2.1+100-dev(38a443e) (Tasmota default) (Firmware 2K smaller than NONOSDK22x_191105)
-DPIO_FRAMEWORK_ARDUINO_ESPRESSIF_SDK22x_190703
-; NONOSDK22x_191024
+; NONOSDK22x_191024 = 2.2.1+111-dev(5ab15d1)
; -DPIO_FRAMEWORK_ARDUINO_ESPRESSIF_SDK22x_191024
-; NONOSDK22x_191105
+; NONOSDK22x_191105 = 2.2.1+113-dev(bb83b9b)
; -DPIO_FRAMEWORK_ARDUINO_ESPRESSIF_SDK22x_191105
+; NONOSDK22x_191122 = 2.2.1+119-dev(a58da79)
+; -DPIO_FRAMEWORK_ARDUINO_ESPRESSIF_SDK22x_191122
; NONOSDK3V0 (known issues)
; -DPIO_FRAMEWORK_ARDUINO_ESPRESSIF_SDK3
; lwIP 1.4
diff --git a/platformio_tasmota_env.ini b/platformio_tasmota_env.ini
index 46e71d7af..38aaf2223 100644
--- a/platformio_tasmota_env.ini
+++ b/platformio_tasmota_env.ini
@@ -1,5 +1,6 @@
[env]
platform = ${common.platform}
+platform_packages = ${common.platform_packages}
framework = ${common.framework}
board = ${common.board}
board_build.flash_mode = ${common.board_build.flash_mode}
diff --git a/tasmota/CHANGELOG.md b/tasmota/CHANGELOG.md
index 112df96ba..9d8e5cb72 100644
--- a/tasmota/CHANGELOG.md
+++ b/tasmota/CHANGELOG.md
@@ -2,6 +2,18 @@
## Released
+### 7.1.2 20191206
+
+- Maintenance Release
+
+### 7.1.1.1 20191201
+
+- Fix lost functionality of GPIO9 and GPIO10 on some devices (#7080)
+- Fix Zigbee uses Hardware Serial if GPIO 1/3 or GPIO 13/15 and SerialLog 0 (#7071)
+- Fix WS2812 power control (#7090)
+- Change light color schemes 2, 3 and 4 from color wheel to Hue driven with user Saturation control
+- Change log buffer size from 520 to 700 characters accomodating full rule text (#7110)
+
### 7.1.1 20191201
- Maintenance Release
diff --git a/tasmota/support.ino b/tasmota/support.ino
index 55c01b71d..8ce7f2840 100644
--- a/tasmota/support.ino
+++ b/tasmota/support.ino
@@ -521,7 +521,7 @@ char* GetPowerDevice(char* dest, uint32_t idx, size_t size)
return GetPowerDevice(dest, idx, size, 0);
}
-bool IsEsp8285(void)
+void GetEspHardwareType(void)
{
// esptool.py get_efuses
uint32_t efuse1 = *(uint32_t*)(0x3FF00050);
@@ -529,17 +529,16 @@ bool IsEsp8285(void)
// uint32_t efuse3 = *(uint32_t*)(0x3FF00058);
// uint32_t efuse4 = *(uint32_t*)(0x3FF0005C);
- bool is_8285 = ( (efuse1 & (1 << 4)) || (efuse2 & (1 << 16)) );
+ is_8285 = ( (efuse1 & (1 << 4)) || (efuse2 & (1 << 16)) );
if (is_8285 && (ESP.getFlashChipRealSize() > 1048576)) {
is_8285 = false; // ESP8285 can only have 1M flash
}
- return is_8285;
}
String GetDeviceHardware(void)
{
char buff[10];
- if (IsEsp8285()) {
+ if (is_8285) {
strcpy_P(buff, PSTR("ESP8285"));
} else {
strcpy_P(buff, PSTR("ESP8266EX"));
@@ -1034,18 +1033,18 @@ bool FlashPin(uint32_t pin)
uint8_t ValidPin(uint32_t pin, uint32_t gpio)
{
- uint8_t result = gpio;
-
if (FlashPin(pin)) {
- result = GPIO_NONE; // Disable flash pins GPIO6, GPIO7, GPIO8 and GPIO11
+ return GPIO_NONE; // Disable flash pins GPIO6, GPIO7, GPIO8 and GPIO11
}
- if (!IsEsp8285() && !Settings.flag3.user_esp8285_enable) { // SetOption51 - Enable ESP8285 user GPIO's
+
+// if (!is_8285 && !Settings.flag3.user_esp8285_enable) { // SetOption51 - Enable ESP8285 user GPIO's
+ if ((WEMOS == Settings.module) && !Settings.flag3.user_esp8285_enable) { // SetOption51 - Enable ESP8285 user GPIO's
if ((pin == 9) || (pin == 10)) {
- result = GPIO_NONE; // Disable possible flash GPIO9 and GPIO10
+ return GPIO_NONE; // Disable possible flash GPIO9 and GPIO10
}
}
- return result;
+ return gpio;
}
bool ValidGPIO(uint32_t pin, uint32_t gpio)
diff --git a/tasmota/support_tasmota.ino b/tasmota/support_tasmota.ino
new file mode 100644
index 000000000..e7099affd
--- /dev/null
+++ b/tasmota/support_tasmota.ino
@@ -0,0 +1,1339 @@
+/*
+ support_tasmota.ino - Core support for Tasmota
+
+ Copyright (C) 2019 Theo Arends
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see .
+*/
+
+const char kSleepMode[] PROGMEM = "Dynamic|Normal";
+const char kPrefixes[] PROGMEM = D_CMND "|" D_STAT "|" D_TELE;
+
+char* Format(char* output, const char* input, int size)
+{
+ char *token;
+ uint32_t digits = 0;
+
+ if (strstr(input, "%") != nullptr) {
+ strlcpy(output, input, size);
+ token = strtok(output, "%");
+ if (strstr(input, "%") == input) {
+ output[0] = '\0';
+ } else {
+ token = strtok(nullptr, "");
+ }
+ if (token != nullptr) {
+ digits = atoi(token);
+ if (digits) {
+ char tmp[size];
+ if (strchr(token, 'd')) {
+ snprintf_P(tmp, size, PSTR("%s%c0%dd"), output, '%', digits);
+ snprintf_P(output, size, tmp, ESP.getChipId() & 0x1fff); // %04d - short chip ID in dec, like in hostname
+ } else {
+ snprintf_P(tmp, size, PSTR("%s%c0%dX"), output, '%', digits);
+ snprintf_P(output, size, tmp, ESP.getChipId()); // %06X - full chip ID in hex
+ }
+ } else {
+ if (strchr(token, 'd')) {
+ snprintf_P(output, size, PSTR("%s%d"), output, ESP.getChipId()); // %d - full chip ID in dec
+ digits = 8;
+ }
+ }
+ }
+ }
+ if (!digits) {
+ strlcpy(output, input, size);
+ }
+ return output;
+}
+
+char* GetOtaUrl(char *otaurl, size_t otaurl_size)
+{
+ if (strstr(Settings.ota_url, "%04d") != nullptr) { // OTA url contains placeholder for chip ID
+ snprintf(otaurl, otaurl_size, Settings.ota_url, ESP.getChipId() & 0x1fff);
+ }
+ else if (strstr(Settings.ota_url, "%d") != nullptr) { // OTA url contains placeholder for chip ID
+ snprintf_P(otaurl, otaurl_size, Settings.ota_url, ESP.getChipId());
+ }
+ else {
+ strlcpy(otaurl, Settings.ota_url, otaurl_size);
+ }
+ return otaurl;
+}
+
+char* GetTopic_P(char *stopic, uint32_t prefix, char *topic, const char* subtopic)
+{
+ /* prefix 0 = Cmnd
+ prefix 1 = Stat
+ prefix 2 = Tele
+ prefix 4 = Cmnd fallback
+ prefix 5 = Stat fallback
+ prefix 6 = Tele fallback
+ prefix 8 = Cmnd topic
+ prefix 9 = Stat topic
+ prefix 10 = Tele topic
+ */
+ char romram[CMDSZ];
+ String fulltopic;
+
+ snprintf_P(romram, sizeof(romram), subtopic);
+ if (fallback_topic_flag || (prefix > 3)) {
+ bool fallback = (prefix < 8);
+ prefix &= 3;
+ char stemp[11];
+ fulltopic = GetTextIndexed(stemp, sizeof(stemp), prefix, kPrefixes);
+ fulltopic += F("/");
+ if (fallback) {
+ fulltopic += mqtt_client;
+ fulltopic += F("_fb"); // cmnd/_fb
+ } else {
+ fulltopic += topic; // cmnd/
+ }
+ } else {
+ fulltopic = Settings.mqtt_fulltopic;
+ if ((0 == prefix) && (-1 == fulltopic.indexOf(FPSTR(MQTT_TOKEN_PREFIX)))) {
+ fulltopic += F("/");
+ fulltopic += FPSTR(MQTT_TOKEN_PREFIX); // Need prefix for commands to handle mqtt topic loops
+ }
+ for (uint32_t i = 0; i < 3; i++) {
+ if ('\0' == Settings.mqtt_prefix[i][0]) {
+ GetTextIndexed(Settings.mqtt_prefix[i], sizeof(Settings.mqtt_prefix[i]), i, kPrefixes);
+ }
+ }
+ fulltopic.replace(FPSTR(MQTT_TOKEN_PREFIX), Settings.mqtt_prefix[prefix]);
+ fulltopic.replace(FPSTR(MQTT_TOKEN_TOPIC), topic);
+ fulltopic.replace(F("%hostname%"), my_hostname);
+ String token_id = WiFi.macAddress();
+ token_id.replace(":", "");
+ fulltopic.replace(F("%id%"), token_id);
+ }
+ fulltopic.replace(F("#"), "");
+ fulltopic.replace(F("//"), "/");
+ if (!fulltopic.endsWith("/")) {
+ fulltopic += "/";
+ }
+ snprintf_P(stopic, TOPSZ, PSTR("%s%s"), fulltopic.c_str(), romram);
+ return stopic;
+}
+
+char* GetGroupTopic_P(char *stopic, const char* subtopic)
+{
+ // SetOption75 0: %prefix%/nothing/%topic% = cmnd/nothing//#
+ // SetOption75 1: cmnd/
+ return GetTopic_P(stopic, (Settings.flag3.grouptopic_mode) ? CMND +8 : CMND, Settings.mqtt_grptopic, subtopic); // SetOption75 - GroupTopic replaces %topic% (0) or fixed topic cmnd/grouptopic (1)
+}
+
+char* GetFallbackTopic_P(char *stopic, const char* subtopic)
+{
+ return GetTopic_P(stopic, CMND +4, nullptr, subtopic);
+}
+
+char* GetStateText(uint32_t state)
+{
+ if (state > 3) {
+ state = 1;
+ }
+ return Settings.state_text[state];
+}
+
+/********************************************************************************************/
+
+void SetLatchingRelay(power_t lpower, uint32_t state)
+{
+ // power xx00 - toggle REL1 (Off) and REL3 (Off) - device 1 Off, device 2 Off
+ // power xx01 - toggle REL2 (On) and REL3 (Off) - device 1 On, device 2 Off
+ // power xx10 - toggle REL1 (Off) and REL4 (On) - device 1 Off, device 2 On
+ // power xx11 - toggle REL2 (On) and REL4 (On) - device 1 On, device 2 On
+
+ if (state && !latching_relay_pulse) { // Set latching relay to power if previous pulse has finished
+ latching_power = lpower;
+ latching_relay_pulse = 2; // max 200mS (initiated by stateloop())
+ }
+
+ for (uint32_t i = 0; i < devices_present; i++) {
+ uint32_t port = (i << 1) + ((latching_power >> i) &1);
+ if (pin[GPIO_REL1 +port] < 99) {
+ digitalWrite(pin[GPIO_REL1 +port], bitRead(rel_inverted, port) ? !state : state);
+ }
+ }
+}
+
+void SetDevicePower(power_t rpower, uint32_t source)
+{
+ ShowSource(source);
+ last_source = source;
+
+ if (POWER_ALL_ALWAYS_ON == Settings.poweronstate) { // All on and stay on
+ power = (1 << devices_present) -1;
+ rpower = power;
+ }
+
+ if (Settings.flag.interlock) { // Allow only one or no relay set - CMND_INTERLOCK - Enable/disable interlock
+ for (uint32_t i = 0; i < MAX_INTERLOCKS; i++) {
+ power_t mask = 1;
+ uint32_t count = 0;
+ for (uint32_t j = 0; j < devices_present; j++) {
+ if ((Settings.interlock[i] & mask) && (rpower & mask)) {
+ count++;
+ }
+ mask <<= 1;
+ }
+ if (count > 1) {
+ mask = ~Settings.interlock[i]; // Turn interlocked group off as there would be multiple relays on
+ power &= mask;
+ rpower &= mask;
+ }
+ }
+ }
+
+ if (rpower) { // Any power set
+ last_power = rpower;
+ }
+
+ XdrvMailbox.index = rpower;
+ XdrvCall(FUNC_SET_POWER); // Signal power state
+
+ XdrvMailbox.index = rpower;
+ XdrvMailbox.payload = source;
+ if (XdrvCall(FUNC_SET_DEVICE_POWER)) { // Set power state and stop if serviced
+ // Serviced
+ }
+ else if ((SONOFF_DUAL == my_module_type) || (CH4 == my_module_type)) {
+ Serial.write(0xA0);
+ Serial.write(0x04);
+ Serial.write(rpower &0xFF);
+ Serial.write(0xA1);
+ Serial.write('\n');
+ Serial.flush();
+ }
+ else if (EXS_RELAY == my_module_type) {
+ SetLatchingRelay(rpower, 1);
+ }
+ else {
+ for (uint32_t i = 0; i < devices_present; i++) {
+ power_t state = rpower &1;
+ if ((i < MAX_RELAYS) && (pin[GPIO_REL1 +i] < 99)) {
+ digitalWrite(pin[GPIO_REL1 +i], bitRead(rel_inverted, i) ? !state : state);
+ }
+ rpower >>= 1;
+ }
+ }
+}
+
+void RestorePower(bool publish_power, uint32_t source)
+{
+ if (power != last_power) {
+ SetDevicePower(last_power, source);
+ if (publish_power) {
+ MqttPublishAllPowerState();
+ }
+ }
+}
+
+void SetAllPower(uint32_t state, uint32_t source)
+{
+// state 0 = POWER_OFF = Relay Off
+// state 1 = POWER_ON = Relay On (turn off after Settings.pulse_timer * 100 mSec if enabled)
+// state 2 = POWER_TOGGLE = Toggle relay
+// state 8 = POWER_OFF_NO_STATE = Relay Off and no publishPowerState
+// state 9 = POWER_ON_NO_STATE = Relay On and no publishPowerState
+// state 10 = POWER_TOGGLE_NO_STATE = Toggle relay and no publishPowerState
+// state 16 = POWER_SHOW_STATE = Show power state
+
+ bool publish_power = true;
+ if ((state >= POWER_OFF_NO_STATE) && (state <= POWER_TOGGLE_NO_STATE)) {
+ state &= 3; // POWER_OFF, POWER_ON or POWER_TOGGLE
+ publish_power = false;
+ }
+ if ((state >= POWER_OFF) && (state <= POWER_TOGGLE)) {
+ power_t all_on = (1 << devices_present) -1;
+ switch (state) {
+ case POWER_OFF:
+ power = 0;
+ break;
+ case POWER_ON:
+ power = all_on;
+ break;
+ case POWER_TOGGLE:
+ power ^= all_on; // Complement current state
+ }
+ SetDevicePower(power, source);
+ }
+ if (publish_power) {
+ MqttPublishAllPowerState();
+ }
+}
+
+void SetLedPowerIdx(uint32_t led, uint32_t state)
+{
+ if ((99 == pin[GPIO_LEDLNK]) && (0 == led)) { // Legacy - LED1 is link led only if LED2 is present
+ if (pin[GPIO_LED2] < 99) {
+ led = 1;
+ }
+ }
+ if (pin[GPIO_LED1 + led] < 99) {
+ uint32_t mask = 1 << led;
+ if (state) {
+ state = 1;
+ led_power |= mask;
+ } else {
+ led_power &= (0xFF ^ mask);
+ }
+ digitalWrite(pin[GPIO_LED1 + led], bitRead(led_inverted, led) ? !state : state);
+ }
+}
+
+void SetLedPower(uint32_t state)
+{
+ if (99 == pin[GPIO_LEDLNK]) { // Legacy - Only use LED1 and/or LED2
+ SetLedPowerIdx(0, state);
+ } else {
+ power_t mask = 1;
+ for (uint32_t i = 0; i < leds_present; i++) { // Map leds to power
+ bool tstate = (power & mask);
+ SetLedPowerIdx(i, tstate);
+ mask <<= 1;
+ }
+ }
+}
+
+void SetLedPowerAll(uint32_t state)
+{
+ for (uint32_t i = 0; i < leds_present; i++) {
+ SetLedPowerIdx(i, state);
+ }
+}
+
+void SetLedLink(uint32_t state)
+{
+ uint32_t led_pin = pin[GPIO_LEDLNK];
+ uint32_t led_inv = ledlnk_inverted;
+ if (99 == led_pin) { // Legacy - LED1 is status
+ led_pin = pin[GPIO_LED1];
+ led_inv = bitRead(led_inverted, 0);
+ }
+ if (led_pin < 99) {
+ if (state) { state = 1; }
+ digitalWrite(led_pin, (led_inv) ? !state : state);
+ }
+}
+
+void SetPulseTimer(uint32_t index, uint32_t time)
+{
+ pulse_timer[index] = (time > 111) ? millis() + (1000 * (time - 100)) : (time > 0) ? millis() + (100 * time) : 0L;
+}
+
+uint32_t GetPulseTimer(uint32_t index)
+{
+ long time = TimePassedSince(pulse_timer[index]);
+ if (time < 0) {
+ time *= -1;
+ return (time > 11100) ? (time / 1000) + 100 : (time > 0) ? time / 100 : 0;
+ }
+ return 0;
+}
+
+/********************************************************************************************/
+
+bool SendKey(uint32_t key, uint32_t device, uint32_t state)
+{
+// key 0 = KEY_BUTTON = button_topic
+// key 1 = KEY_SWITCH = switch_topic
+// state 0 = POWER_OFF = off
+// state 1 = POWER_ON = on
+// state 2 = POWER_TOGGLE = toggle
+// state 3 = POWER_HOLD = hold
+// state 9 = CLEAR_RETAIN = clear retain flag
+
+ char stopic[TOPSZ];
+ char scommand[CMDSZ];
+ char key_topic[sizeof(Settings.button_topic)];
+ bool result = false;
+
+ char *tmp = (key) ? Settings.switch_topic : Settings.button_topic;
+ Format(key_topic, tmp, sizeof(key_topic));
+ if (Settings.flag.mqtt_enabled && MqttIsConnected() && (strlen(key_topic) != 0) && strcmp(key_topic, "0")) { // SetOption3 - Enable MQTT
+ if (!key && (device > devices_present)) {
+ device = 1; // Only allow number of buttons up to number of devices
+ }
+ GetTopic_P(stopic, CMND, key_topic,
+ GetPowerDevice(scommand, device, sizeof(scommand), (key + Settings.flag.device_index_enable))); // cmnd/switchtopic/POWERx - SetOption26 - Switch between POWER or POWER1
+ if (CLEAR_RETAIN == state) {
+ mqtt_data[0] = '\0';
+ } else {
+ if ((Settings.flag3.button_switch_force_local || // SetOption61 - Force local operation when button/switch topic is set
+ !strcmp(mqtt_topic, key_topic) ||
+ !strcmp(Settings.mqtt_grptopic, key_topic)) &&
+ (POWER_TOGGLE == state)) {
+ state = ~(power >> (device -1)) &1; // POWER_OFF or POWER_ON
+ }
+ snprintf_P(mqtt_data, sizeof(mqtt_data), GetStateText(state));
+ }
+#ifdef USE_DOMOTICZ
+ if (!(DomoticzSendKey(key, device, state, strlen(mqtt_data)))) {
+#endif // USE_DOMOTICZ
+ MqttPublishDirect(stopic, ((key) ? Settings.flag.mqtt_switch_retain // CMND_SWITCHRETAIN
+ : Settings.flag.mqtt_button_retain) && // CMND_BUTTONRETAIN
+ (state != POWER_HOLD || !Settings.flag3.no_hold_retain)); // SetOption62 - Don't use retain flag on HOLD messages
+#ifdef USE_DOMOTICZ
+ }
+#endif // USE_DOMOTICZ
+ result = !Settings.flag3.button_switch_force_local; // SetOption61 - Force local operation when button/switch topic is set
+ } else {
+ Response_P(PSTR("{\"%s%d\":{\"State\":%d}}"), (key) ? "Switch" : "Button", device, state);
+ result = XdrvRulesProcess();
+ }
+ int32_t payload_save = XdrvMailbox.payload;
+ XdrvMailbox.payload = key << 16 | state << 8 | device;
+ XdrvCall(FUNC_ANY_KEY);
+ XdrvMailbox.payload = payload_save;
+ return result;
+}
+
+void ExecuteCommandPower(uint32_t device, uint32_t state, uint32_t source)
+{
+// device = Relay number 1 and up
+// state 0 = POWER_OFF = Relay Off
+// state 1 = POWER_ON = Relay On (turn off after Settings.pulse_timer * 100 mSec if enabled)
+// state 2 = POWER_TOGGLE = Toggle relay
+// state 3 = POWER_BLINK = Blink relay
+// state 4 = POWER_BLINK_STOP = Stop blinking relay
+// state 8 = POWER_OFF_NO_STATE = Relay Off and no publishPowerState
+// state 9 = POWER_ON_NO_STATE = Relay On and no publishPowerState
+// state 10 = POWER_TOGGLE_NO_STATE = Toggle relay and no publishPowerState
+// state 16 = POWER_SHOW_STATE = Show power state
+
+// ShowSource(source);
+
+#ifdef USE_SONOFF_IFAN
+ if (IsModuleIfan()) {
+ blink_mask &= 1; // No blinking on the fan relays
+ Settings.flag.interlock = 0; // No interlock mode as it is already done by the microcontroller - CMND_INTERLOCK - Enable/disable interlock
+ Settings.pulse_timer[1] = 0; // No pulsetimers on the fan relays
+ Settings.pulse_timer[2] = 0;
+ Settings.pulse_timer[3] = 0;
+ }
+#endif // USE_SONOFF_IFAN
+
+ bool publish_power = true;
+ if ((state >= POWER_OFF_NO_STATE) && (state <= POWER_TOGGLE_NO_STATE)) {
+ state &= 3; // POWER_OFF, POWER_ON or POWER_TOGGLE
+ publish_power = false;
+ }
+
+ if ((device < 1) || (device > devices_present)) {
+ device = 1;
+ }
+ active_device = device;
+
+ if (device <= MAX_PULSETIMERS) {
+ SetPulseTimer(device -1, 0);
+ }
+ power_t mask = 1 << (device -1); // Device to control
+ if (state <= POWER_TOGGLE) {
+ if ((blink_mask & mask)) {
+ blink_mask &= (POWER_MASK ^ mask); // Clear device mask
+ MqttPublishPowerBlinkState(device);
+ }
+
+ if (Settings.flag.interlock && // CMND_INTERLOCK - Enable/disable interlock
+ !interlock_mutex &&
+ ((POWER_ON == state) || ((POWER_TOGGLE == state) && !(power & mask)))
+ ) {
+ interlock_mutex = true; // Clear all but masked relay in interlock group if new set requested
+ for (uint32_t i = 0; i < MAX_INTERLOCKS; i++) {
+ if (Settings.interlock[i] & mask) { // Find interlock group
+ for (uint32_t j = 0; j < devices_present; j++) {
+ power_t imask = 1 << j;
+ if ((Settings.interlock[i] & imask) && (power & imask) && (mask != imask)) {
+ ExecuteCommandPower(j +1, POWER_OFF, SRC_IGNORE);
+ delay(50); // Add some delay to make sure never have more than one relay on
+ }
+ }
+ break; // An interlocked relay is only present in one group so quit
+ }
+ }
+ interlock_mutex = false;
+ }
+
+ switch (state) {
+ case POWER_OFF: {
+ power &= (POWER_MASK ^ mask);
+ break; }
+ case POWER_ON:
+ power |= mask;
+ break;
+ case POWER_TOGGLE:
+ power ^= mask;
+ }
+ SetDevicePower(power, source);
+#ifdef USE_DOMOTICZ
+ DomoticzUpdatePowerState(device);
+#endif // USE_DOMOTICZ
+#ifdef USE_KNX
+ KnxUpdatePowerState(device, power);
+#endif // USE_KNX
+ if (publish_power && Settings.flag3.hass_tele_on_power) { // SetOption59 - Send tele/%topic%/STATE in addition to stat/%topic%/RESULT
+ MqttPublishTeleState();
+ }
+ if (device <= MAX_PULSETIMERS) { // Restart PulseTime if powered On
+ SetPulseTimer(device -1, (((POWER_ALL_OFF_PULSETIME_ON == Settings.poweronstate) ? ~power : power) & mask) ? Settings.pulse_timer[device -1] : 0);
+ }
+ }
+ else if (POWER_BLINK == state) {
+ if (!(blink_mask & mask)) {
+ blink_powersave = (blink_powersave & (POWER_MASK ^ mask)) | (power & mask); // Save state
+ blink_power = (power >> (device -1))&1; // Prep to Toggle
+ }
+ blink_timer = millis() + 100;
+ blink_counter = ((!Settings.blinkcount) ? 64000 : (Settings.blinkcount *2)) +1;
+ blink_mask |= mask; // Set device mask
+ MqttPublishPowerBlinkState(device);
+ return;
+ }
+ else if (POWER_BLINK_STOP == state) {
+ bool flag = (blink_mask & mask);
+ blink_mask &= (POWER_MASK ^ mask); // Clear device mask
+ MqttPublishPowerBlinkState(device);
+ if (flag) {
+ ExecuteCommandPower(device, (blink_powersave >> (device -1))&1, SRC_IGNORE); // Restore state
+ }
+ return;
+ }
+ if (publish_power) {
+ MqttPublishPowerState(device);
+ }
+}
+
+void StopAllPowerBlink(void)
+{
+ power_t mask;
+
+ for (uint32_t i = 1; i <= devices_present; i++) {
+ mask = 1 << (i -1);
+ if (blink_mask & mask) {
+ blink_mask &= (POWER_MASK ^ mask); // Clear device mask
+ MqttPublishPowerBlinkState(i);
+ ExecuteCommandPower(i, (blink_powersave >> (i -1))&1, SRC_IGNORE); // Restore state
+ }
+ }
+}
+
+void MqttShowPWMState(void)
+{
+ ResponseAppend_P(PSTR("\"" D_CMND_PWM "\":{"));
+ bool first = true;
+ for (uint32_t i = 0; i < MAX_PWMS; i++) {
+ if (pin[GPIO_PWM1 + i] < 99) {
+ ResponseAppend_P(PSTR("%s\"" D_CMND_PWM "%d\":%d"), first ? "" : ",", i+1, Settings.pwm_value[i]);
+ first = false;
+ }
+ }
+ ResponseJsonEnd();
+}
+
+void MqttShowState(void)
+{
+ char stemp1[33];
+
+ ResponseAppendTime();
+ ResponseAppend_P(PSTR(",\"" D_JSON_UPTIME "\":\"%s\",\"UptimeSec\":%u"), GetUptime().c_str(), UpTime());
+
+#ifdef USE_ADC_VCC
+ dtostrfd((double)ESP.getVcc()/1000, 3, stemp1);
+ ResponseAppend_P(PSTR(",\"" D_JSON_VCC "\":%s"), stemp1);
+#endif
+
+ ResponseAppend_P(PSTR(",\"" D_JSON_HEAPSIZE "\":%d,\"SleepMode\":\"%s\",\"Sleep\":%u,\"LoadAvg\":%u,\"MqttCount\":%u"),
+ ESP.getFreeHeap()/1024, GetTextIndexed(stemp1, sizeof(stemp1), Settings.flag3.sleep_normal, kSleepMode), // SetOption60 - Enable normal sleep instead of dynamic sleep
+ sleep, loop_load_avg, MqttConnectCount());
+
+ for (uint32_t i = 1; i <= devices_present; i++) {
+#ifdef USE_LIGHT
+ if ((LightDevice()) && (i >= LightDevice())) {
+ if (i == LightDevice()) { LightState(1); } // call it only once
+ } else {
+#endif
+ ResponseAppend_P(PSTR(",\"%s\":\"%s\""), GetPowerDevice(stemp1, i, sizeof(stemp1), Settings.flag.device_index_enable), // SetOption26 - Switch between POWER or POWER1
+ GetStateText(bitRead(power, i-1)));
+#ifdef USE_SONOFF_IFAN
+ if (IsModuleIfan()) {
+ ResponseAppend_P(PSTR(",\"" D_CMND_FANSPEED "\":%d"), GetFanspeed());
+ break;
+ }
+#endif // USE_SONOFF_IFAN
+#ifdef USE_LIGHT
+ }
+#endif
+ }
+
+ if (pwm_present) {
+ ResponseAppend_P(PSTR(","));
+ MqttShowPWMState();
+ }
+
+ ResponseAppend_P(PSTR(",\"" D_JSON_WIFI "\":{\"" D_JSON_AP "\":%d,\"" D_JSON_SSID "\":\"%s\",\"" D_JSON_BSSID "\":\"%s\",\"" D_JSON_CHANNEL "\":%d,\"" D_JSON_RSSI "\":%d,\"" D_JSON_LINK_COUNT "\":%d,\"" D_JSON_DOWNTIME "\":\"%s\"}}"),
+ Settings.sta_active +1, Settings.sta_ssid[Settings.sta_active], WiFi.BSSIDstr().c_str(), WiFi.channel(), WifiGetRssiAsQuality(WiFi.RSSI()), WifiLinkCount(), WifiDowntime().c_str());
+}
+
+void MqttPublishTeleState(void)
+{
+ mqtt_data[0] = '\0';
+ MqttShowState();
+ MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_STATE), MQTT_TELE_RETAIN);
+#ifdef USE_SCRIPT
+ RulesTeleperiod(); // Allow rule based HA messages
+#endif // USE_SCRIPT
+}
+
+bool MqttShowSensor(void)
+{
+ ResponseAppendTime();
+
+ int json_data_start = strlen(mqtt_data);
+ for (uint32_t i = 0; i < MAX_SWITCHES; i++) {
+#ifdef USE_TM1638
+ if ((pin[GPIO_SWT1 +i] < 99) || ((pin[GPIO_TM16CLK] < 99) && (pin[GPIO_TM16DIO] < 99) && (pin[GPIO_TM16STB] < 99))) {
+#else
+ if (pin[GPIO_SWT1 +i] < 99) {
+#endif // USE_TM1638
+ bool swm = ((FOLLOW_INV == Settings.switchmode[i]) || (PUSHBUTTON_INV == Settings.switchmode[i]) || (PUSHBUTTONHOLD_INV == Settings.switchmode[i]));
+ ResponseAppend_P(PSTR(",\"" D_JSON_SWITCH "%d\":\"%s\""), i +1, GetStateText(swm ^ SwitchLastState(i)));
+ }
+ }
+ XsnsCall(FUNC_JSON_APPEND);
+ XdrvCall(FUNC_JSON_APPEND);
+
+ bool json_data_available = (strlen(mqtt_data) - json_data_start);
+ if (strstr_P(mqtt_data, PSTR(D_JSON_PRESSURE)) != nullptr) {
+ ResponseAppend_P(PSTR(",\"" D_JSON_PRESSURE_UNIT "\":\"%s\""), PressureUnit().c_str());
+ }
+ if (strstr_P(mqtt_data, PSTR(D_JSON_TEMPERATURE)) != nullptr) {
+ ResponseAppend_P(PSTR(",\"" D_JSON_TEMPERATURE_UNIT "\":\"%c\""), TempUnit());
+ }
+ ResponseJsonEnd();
+
+ if (json_data_available) { XdrvCall(FUNC_SHOW_SENSOR); }
+ return json_data_available;
+}
+
+void MqttPublishSensor(void)
+{
+ mqtt_data[0] = '\0';
+ if (MqttShowSensor()) {
+ MqttPublishTeleSensor();
+ }
+}
+
+/********************************************************************************************/
+
+void PerformEverySecond(void)
+{
+ uptime++;
+
+ if (ntp_synced_message) {
+ // Moved here to fix syslog UDP exception 9 during RtcSecond
+ AddLog_P2(LOG_LEVEL_DEBUG, PSTR("NTP: Drift %d, (" D_UTC_TIME ") %s, (" D_DST_TIME ") %s, (" D_STD_TIME ") %s"),
+ DriftTime(), GetTime(0).c_str(), GetTime(2).c_str(), GetTime(3).c_str());
+ ntp_synced_message = false;
+ }
+
+ if (POWER_CYCLE_TIME == uptime) {
+ UpdateQuickPowerCycle(false);
+ }
+
+ if (BOOT_LOOP_TIME == uptime) {
+ RtcRebootReset();
+
+#ifdef USE_DEEPSLEEP
+ if (!(DeepSleepEnabled() && !Settings.flag3.bootcount_update)) {
+#endif
+ Settings.bootcount++; // Moved to here to stop flash writes during start-up
+ AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_APPLICATION D_BOOT_COUNT " %d"), Settings.bootcount);
+#ifdef USE_DEEPSLEEP
+ }
+#endif
+ }
+
+ if (seriallog_timer) {
+ seriallog_timer--;
+ if (!seriallog_timer) {
+ if (seriallog_level) {
+ AddLog_P(LOG_LEVEL_INFO, PSTR(D_LOG_APPLICATION D_SERIAL_LOGGING_DISABLED));
+ }
+ seriallog_level = 0;
+ }
+ }
+
+ if (syslog_timer) { // Restore syslog level
+ syslog_timer--;
+ if (!syslog_timer) {
+ syslog_level = Settings.syslog_level;
+ if (Settings.syslog_level) {
+ AddLog_P(LOG_LEVEL_INFO, PSTR(D_LOG_APPLICATION D_SYSLOG_LOGGING_REENABLED)); // Might trigger disable again (on purpose)
+ }
+ }
+ }
+
+ ResetGlobalValues();
+
+ if (Settings.tele_period) {
+ if (tele_period >= 9999) {
+ if (!global_state.wifi_down) {
+ tele_period = 0; // Allow teleperiod once wifi is connected
+ }
+ } else {
+ tele_period++;
+ if (tele_period >= Settings.tele_period) {
+ tele_period = 0;
+
+ MqttPublishTeleState();
+
+ mqtt_data[0] = '\0';
+ if (MqttShowSensor()) {
+ MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_SENSOR), Settings.flag.mqtt_sensor_retain); // CMND_SENSORRETAIN
+#if defined(USE_RULES) || defined(USE_SCRIPT)
+ RulesTeleperiod(); // Allow rule based HA messages
+#endif // USE_RULES
+ }
+
+ XdrvCall(FUNC_AFTER_TELEPERIOD);
+ }
+ }
+ }
+}
+
+/*********************************************************************************************\
+ * State loops
+\*********************************************************************************************/
+/*-------------------------------------------------------------------------------------------*\
+ * Every 0.1 second
+\*-------------------------------------------------------------------------------------------*/
+
+void Every100mSeconds(void)
+{
+ // As the max amount of sleep = 250 mSec this loop will shift in time...
+ power_t power_now;
+
+ if (latching_relay_pulse) {
+ latching_relay_pulse--;
+ if (!latching_relay_pulse) SetLatchingRelay(0, 0);
+ }
+
+ for (uint32_t i = 0; i < MAX_PULSETIMERS; i++) {
+ if (pulse_timer[i] != 0L) { // Timer active?
+ if (TimeReached(pulse_timer[i])) { // Timer finished?
+ pulse_timer[i] = 0L; // Turn off this timer
+ ExecuteCommandPower(i +1, (POWER_ALL_OFF_PULSETIME_ON == Settings.poweronstate) ? POWER_ON : POWER_OFF, SRC_PULSETIMER);
+ }
+ }
+ }
+
+ if (blink_mask) {
+ if (TimeReached(blink_timer)) {
+ SetNextTimeInterval(blink_timer, 100 * Settings.blinktime);
+ blink_counter--;
+ if (!blink_counter) {
+ StopAllPowerBlink();
+ } else {
+ blink_power ^= 1;
+ power_now = (power & (POWER_MASK ^ blink_mask)) | ((blink_power) ? blink_mask : 0);
+ SetDevicePower(power_now, SRC_IGNORE);
+ }
+ }
+ }
+}
+
+/*-------------------------------------------------------------------------------------------*\
+ * Every 0.25 second
+\*-------------------------------------------------------------------------------------------*/
+
+void Every250mSeconds(void)
+{
+// As the max amount of sleep = 250 mSec this loop should always be taken...
+
+ uint32_t blinkinterval = 1;
+
+ state_250mS++;
+ state_250mS &= 0x3;
+
+ if (mqtt_cmnd_publish) mqtt_cmnd_publish--; // Clean up
+
+ if (!Settings.flag.global_state) { // Problem blinkyblinky enabled - SetOption31 - Control link led blinking
+ if (global_state.data) { // Any problem
+ if (global_state.mqtt_down) { blinkinterval = 7; } // MQTT problem so blink every 2 seconds (slowest)
+ if (global_state.wifi_down) { blinkinterval = 3; } // Wifi problem so blink every second (slow)
+ blinks = 201; // Allow only a single blink in case the problem is solved
+ }
+ }
+ if (blinks || restart_flag || ota_state_flag) {
+ if (restart_flag || ota_state_flag) { // Overrule blinks and keep led lit
+ blinkstate = true; // Stay lit
+ } else {
+ blinkspeed--;
+ if (!blinkspeed) {
+ blinkspeed = blinkinterval; // Set interval to 0.2 (default), 1 or 2 seconds
+ blinkstate ^= 1; // Blink
+ }
+ }
+ if ((!(Settings.ledstate &0x08)) && ((Settings.ledstate &0x06) || (blinks > 200) || (blinkstate))) {
+ SetLedLink(blinkstate); // Set led on or off
+ }
+ if (!blinkstate) {
+ blinks--;
+ if (200 == blinks) blinks = 0; // Disable blink
+ }
+ }
+ if (Settings.ledstate &1 && (pin[GPIO_LEDLNK] < 99 || !(blinks || restart_flag || ota_state_flag)) ) {
+ bool tstate = power & Settings.ledmask;
+ if ((SONOFF_TOUCH == my_module_type) || (SONOFF_T11 == my_module_type) || (SONOFF_T12 == my_module_type) || (SONOFF_T13 == my_module_type)) {
+ tstate = (!power) ? 1 : 0; // As requested invert signal for Touch devices to find them in the dark
+ }
+ SetLedPower(tstate);
+ }
+
+/*-------------------------------------------------------------------------------------------*\
+ * Every second at 0.25 second interval
+\*-------------------------------------------------------------------------------------------*/
+
+ switch (state_250mS) {
+ case 0: // Every x.0 second
+ if (ota_state_flag && BACKLOG_EMPTY) {
+ ota_state_flag--;
+ if (2 == ota_state_flag) {
+ ota_url = Settings.ota_url;
+ RtcSettings.ota_loader = 0; // Try requested image first
+ ota_retry_counter = OTA_ATTEMPTS;
+ ESPhttpUpdate.rebootOnUpdate(false);
+ SettingsSave(1); // Free flash for OTA update
+ }
+ if (ota_state_flag <= 0) {
+#ifdef USE_WEBSERVER
+ if (Settings.webserver) StopWebserver();
+#endif // USE_WEBSERVER
+#ifdef USE_ARILUX_RF
+ AriluxRfDisable(); // Prevent restart exception on Arilux Interrupt routine
+#endif // USE_ARILUX_RF
+ ota_state_flag = 92;
+ ota_result = 0;
+ ota_retry_counter--;
+ if (ota_retry_counter) {
+ strlcpy(mqtt_data, GetOtaUrl(log_data, sizeof(log_data)), sizeof(mqtt_data));
+#ifndef FIRMWARE_MINIMAL
+ if (RtcSettings.ota_loader) {
+ char *bch = strrchr(mqtt_data, '/'); // Only consider filename after last backslash prevent change of urls having "-" in it
+ char *pch = strrchr((bch != nullptr) ? bch : mqtt_data, '-'); // Change from filename-DE.bin into filename-minimal.bin
+ char *ech = strrchr((bch != nullptr) ? bch : mqtt_data, '.'); // Change from filename.bin into filename-minimal.bin
+ if (!pch) { pch = ech; }
+ if (pch) {
+ mqtt_data[pch - mqtt_data] = '\0';
+ char *ech = strrchr(Settings.ota_url, '.'); // Change from filename.bin into filename-minimal.bin
+ snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s-" D_JSON_MINIMAL "%s"), mqtt_data, ech); // Minimal filename must be filename-minimal
+ }
+ }
+#endif // FIRMWARE_MINIMAL
+ AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_UPLOAD "%s"), mqtt_data);
+#if defined(ARDUINO_ESP8266_RELEASE_2_3_0) || defined(ARDUINO_ESP8266_RELEASE_2_4_0) || defined(ARDUINO_ESP8266_RELEASE_2_4_1) || defined(ARDUINO_ESP8266_RELEASE_2_4_2)
+ ota_result = (HTTP_UPDATE_FAILED != ESPhttpUpdate.update(mqtt_data));
+#else
+ // If using core stage or 2.5.0+ the syntax has changed
+ WiFiClient OTAclient;
+ ota_result = (HTTP_UPDATE_FAILED != ESPhttpUpdate.update(OTAclient, mqtt_data));
+#endif
+ if (!ota_result) {
+#ifndef FIRMWARE_MINIMAL
+ int ota_error = ESPhttpUpdate.getLastError();
+ DEBUG_CORE_LOG(PSTR("OTA: Error %d"), ota_error);
+ if ((HTTP_UE_TOO_LESS_SPACE == ota_error) || (HTTP_UE_BIN_FOR_WRONG_FLASH == ota_error)) {
+ RtcSettings.ota_loader = 1; // Try minimal image next
+ }
+#endif // FIRMWARE_MINIMAL
+ ota_state_flag = 2; // Upgrade failed - retry
+ }
+ }
+ }
+ if (90 == ota_state_flag) { // Allow MQTT to reconnect
+ ota_state_flag = 0;
+ if (ota_result) {
+// SetFlashModeDout(); // Force DOUT for both ESP8266 and ESP8285
+ Response_P(PSTR(D_JSON_SUCCESSFUL ". " D_JSON_RESTARTING));
+ } else {
+ Response_P(PSTR(D_JSON_FAILED " %s"), ESPhttpUpdate.getLastErrorString().c_str());
+ }
+ restart_flag = 2; // Restart anyway to keep memory clean webserver
+ MqttPublishPrefixTopic_P(STAT, PSTR(D_CMND_UPGRADE));
+ }
+ }
+ break;
+ case 1: // Every x.25 second
+ if (MidnightNow()) {
+ XsnsCall(FUNC_SAVE_AT_MIDNIGHT);
+ }
+ if (save_data_counter && BACKLOG_EMPTY) {
+ save_data_counter--;
+ if (save_data_counter <= 0) {
+ if (Settings.flag.save_state) { // SetOption0 - Save power state and use after restart
+ power_t mask = POWER_MASK;
+ for (uint32_t i = 0; i < MAX_PULSETIMERS; i++) {
+ if ((Settings.pulse_timer[i] > 0) && (Settings.pulse_timer[i] < 30)) { // 3 seconds
+ mask &= ~(1 << i);
+ }
+ }
+ if (!((Settings.power &mask) == (power &mask))) {
+ Settings.power = power;
+ }
+ } else {
+ Settings.power = 0;
+ }
+ SettingsSave(0);
+ save_data_counter = Settings.save_data;
+ }
+ }
+ if (restart_flag && BACKLOG_EMPTY) {
+ if ((214 == restart_flag) || (215 == restart_flag) || (216 == restart_flag)) {
+ char storage_wifi[sizeof(Settings.sta_ssid) +
+ sizeof(Settings.sta_pwd)];
+ char storage_mqtt[sizeof(Settings.mqtt_host) +
+ sizeof(Settings.mqtt_port) +
+ sizeof(Settings.mqtt_client) +
+ sizeof(Settings.mqtt_user) +
+ sizeof(Settings.mqtt_pwd) +
+ sizeof(Settings.mqtt_topic)];
+ memcpy(storage_wifi, Settings.sta_ssid, sizeof(storage_wifi)); // Backup current SSIDs and Passwords
+ if (216 == restart_flag) {
+ memcpy(storage_mqtt, Settings.mqtt_host, sizeof(storage_mqtt)); // Backup mqtt host, port, client, username and password
+ }
+ if ((215 == restart_flag) || (216 == restart_flag)) {
+ SettingsErase(0); // Erase all flash from program end to end of physical flash
+ }
+ SettingsDefault();
+ memcpy(Settings.sta_ssid, storage_wifi, sizeof(storage_wifi)); // Restore current SSIDs and Passwords
+ if (216 == restart_flag) {
+ memcpy(Settings.mqtt_host, storage_mqtt, sizeof(storage_mqtt)); // Restore the mqtt host, port, client, username and password
+ strlcpy(Settings.mqtt_client, MQTT_CLIENT_ID, sizeof(Settings.mqtt_client)); // Set client to default
+ }
+ restart_flag = 2;
+ }
+ else if (213 == restart_flag) {
+ SettingsSdkErase(); // Erase flash SDK parameters
+ restart_flag = 2;
+ }
+ else if (212 == restart_flag) {
+ SettingsErase(0); // Erase all flash from program end to end of physical flash
+ restart_flag = 211;
+ }
+ if (211 == restart_flag) {
+ SettingsDefault();
+ restart_flag = 2;
+ }
+ if (2 == restart_flag) {
+ SettingsSaveAll();
+ }
+ restart_flag--;
+ if (restart_flag <= 0) {
+ AddLog_P(LOG_LEVEL_INFO, PSTR(D_LOG_APPLICATION D_RESTARTING));
+ EspRestart();
+ }
+ }
+ break;
+ case 2: // Every x.5 second
+ WifiCheck(wifi_state_flag);
+ wifi_state_flag = WIFI_RESTART;
+ break;
+ case 3: // Every x.75 second
+ if (!global_state.wifi_down) { MqttCheck(); }
+ break;
+ }
+}
+
+#ifdef USE_ARDUINO_OTA
+/*********************************************************************************************\
+ * Allow updating via the Arduino OTA-protocol.
+ *
+ * - Once started disables current wifi clients and udp
+ * - Perform restart when done to re-init wifi clients
+\*********************************************************************************************/
+
+bool arduino_ota_triggered = false;
+uint16_t arduino_ota_progress_dot_count = 0;
+
+void ArduinoOTAInit(void)
+{
+ ArduinoOTA.setPort(8266);
+ ArduinoOTA.setHostname(my_hostname);
+ if (Settings.web_password[0] !=0) { ArduinoOTA.setPassword(Settings.web_password); }
+
+ ArduinoOTA.onStart([]()
+ {
+ SettingsSave(1); // Free flash for OTA update
+#ifdef USE_WEBSERVER
+ if (Settings.webserver) { StopWebserver(); }
+#endif // USE_WEBSERVER
+#ifdef USE_ARILUX_RF
+ AriluxRfDisable(); // Prevent restart exception on Arilux Interrupt routine
+#endif // USE_ARILUX_RF
+ if (Settings.flag.mqtt_enabled) {
+ MqttDisconnect(); // SetOption3 - Enable MQTT
+ }
+ AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_UPLOAD "Arduino OTA " D_UPLOAD_STARTED));
+ arduino_ota_triggered = true;
+ arduino_ota_progress_dot_count = 0;
+ delay(100); // Allow time for message xfer
+ });
+
+ ArduinoOTA.onProgress([](unsigned int progress, unsigned int total)
+ {
+ if ((LOG_LEVEL_DEBUG <= seriallog_level)) {
+ arduino_ota_progress_dot_count++;
+ Serial.printf(".");
+ if (!(arduino_ota_progress_dot_count % 80)) { Serial.println(); }
+ }
+ });
+
+ ArduinoOTA.onError([](ota_error_t error)
+ {
+ /*
+ From ArduinoOTA.h:
+ typedef enum { OTA_AUTH_ERROR, OTA_BEGIN_ERROR, OTA_CONNECT_ERROR, OTA_RECEIVE_ERROR, OTA_END_ERROR } ota_error_t;
+ */
+ char error_str[100];
+
+ if ((LOG_LEVEL_DEBUG <= seriallog_level) && arduino_ota_progress_dot_count) { Serial.println(); }
+ switch (error) {
+ case OTA_BEGIN_ERROR: strncpy_P(error_str, PSTR(D_UPLOAD_ERR_2), sizeof(error_str)); break;
+ case OTA_RECEIVE_ERROR: strncpy_P(error_str, PSTR(D_UPLOAD_ERR_5), sizeof(error_str)); break;
+ case OTA_END_ERROR: strncpy_P(error_str, PSTR(D_UPLOAD_ERR_7), sizeof(error_str)); break;
+ default:
+ snprintf_P(error_str, sizeof(error_str), PSTR(D_UPLOAD_ERROR_CODE " %d"), error);
+ }
+ AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_UPLOAD "Arduino OTA %s. " D_RESTARTING), error_str);
+ EspRestart();
+ });
+
+ ArduinoOTA.onEnd([]()
+ {
+ if ((LOG_LEVEL_DEBUG <= seriallog_level)) { Serial.println(); }
+ AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_UPLOAD "Arduino OTA " D_SUCCESSFUL ". " D_RESTARTING));
+ EspRestart();
+ });
+
+ ArduinoOTA.begin();
+ AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_UPLOAD "Arduino OTA " D_ENABLED " " D_PORT " 8266"));
+}
+#endif // USE_ARDUINO_OTA
+
+/********************************************************************************************/
+
+void SerialInput(void)
+{
+ while (Serial.available()) {
+// yield();
+ delay(0);
+ serial_in_byte = Serial.read();
+
+/*-------------------------------------------------------------------------------------------*\
+ * Sonoff dual and ch4 19200 baud serial interface
+\*-------------------------------------------------------------------------------------------*/
+ if ((SONOFF_DUAL == my_module_type) || (CH4 == my_module_type)) {
+ serial_in_byte = ButtonSerial(serial_in_byte);
+ }
+
+/*-------------------------------------------------------------------------------------------*/
+
+ if (XdrvCall(FUNC_SERIAL)) {
+ serial_in_byte_counter = 0;
+ Serial.flush();
+ return;
+ }
+
+/*-------------------------------------------------------------------------------------------*/
+
+ if (serial_in_byte > 127 && !Settings.flag.mqtt_serial_raw) { // Discard binary data above 127 if no raw reception allowed - CMND_SERIALSEND3
+ serial_in_byte_counter = 0;
+ Serial.flush();
+ return;
+ }
+ if (!Settings.flag.mqtt_serial) { // SerialSend active - CMND_SERIALSEND and CMND_SERIALLOG
+ if (isprint(serial_in_byte)) { // Any char between 32 and 127
+ if (serial_in_byte_counter < INPUT_BUFFER_SIZE -1) { // Add char to string if it still fits
+ serial_in_buffer[serial_in_byte_counter++] = serial_in_byte;
+ } else {
+ serial_in_byte_counter = 0;
+ }
+ }
+ } else {
+ if (serial_in_byte || Settings.flag.mqtt_serial_raw) { // Any char between 1 and 127 or any char (0 - 255) - CMND_SERIALSEND3
+ if ((serial_in_byte_counter < INPUT_BUFFER_SIZE -1) && // Add char to string if it still fits and ...
+ ((isprint(serial_in_byte) && (128 == Settings.serial_delimiter)) || // Any char between 32 and 127
+ ((serial_in_byte != Settings.serial_delimiter) && (128 != Settings.serial_delimiter)) || // Any char between 1 and 127 and not being delimiter
+ Settings.flag.mqtt_serial_raw)) { // Any char between 0 and 255 - CMND_SERIALSEND3
+ serial_in_buffer[serial_in_byte_counter++] = serial_in_byte;
+ serial_polling_window = millis();
+ } else {
+ serial_polling_window = 0; // Reception done - send mqtt
+ break;
+ }
+ }
+ }
+
+#ifdef USE_SONOFF_SC
+/*-------------------------------------------------------------------------------------------*\
+ * Sonoff SC 19200 baud serial interface
+\*-------------------------------------------------------------------------------------------*/
+ if (SONOFF_SC == my_module_type) {
+ if (serial_in_byte == '\x1B') { // Sonoff SC status from ATMEGA328P
+ serial_in_buffer[serial_in_byte_counter] = 0; // Serial data completed
+ SonoffScSerialInput(serial_in_buffer);
+ serial_in_byte_counter = 0;
+ Serial.flush();
+ return;
+ }
+ } else
+#endif // USE_SONOFF_SC
+/*-------------------------------------------------------------------------------------------*/
+
+ if (!Settings.flag.mqtt_serial && (serial_in_byte == '\n')) { // CMND_SERIALSEND and CMND_SERIALLOG
+ serial_in_buffer[serial_in_byte_counter] = 0; // Serial data completed
+ seriallog_level = (Settings.seriallog_level < LOG_LEVEL_INFO) ? (uint8_t)LOG_LEVEL_INFO : Settings.seriallog_level;
+ AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_COMMAND "%s"), serial_in_buffer);
+ ExecuteCommand(serial_in_buffer, SRC_SERIAL);
+ serial_in_byte_counter = 0;
+ serial_polling_window = 0;
+ Serial.flush();
+ return;
+ }
+ }
+
+ if (Settings.flag.mqtt_serial && serial_in_byte_counter && (millis() > (serial_polling_window + SERIAL_POLLING))) { // CMND_SERIALSEND and CMND_SERIALLOG
+ serial_in_buffer[serial_in_byte_counter] = 0; // Serial data completed
+ char hex_char[(serial_in_byte_counter * 2) + 2];
+ Response_P(PSTR("{\"" D_JSON_SERIALRECEIVED "\":\"%s\"}"),
+ (Settings.flag.mqtt_serial_raw) ? ToHex_P((unsigned char*)serial_in_buffer, serial_in_byte_counter, hex_char, sizeof(hex_char)) : serial_in_buffer);
+ MqttPublishPrefixTopic_P(RESULT_OR_TELE, PSTR(D_JSON_SERIALRECEIVED));
+ XdrvRulesProcess();
+ serial_in_byte_counter = 0;
+ }
+}
+
+/********************************************************************************************/
+
+void GpioInit(void)
+{
+ uint32_t mpin;
+
+ if (!ValidModule(Settings.module)) {
+ uint32_t module = MODULE;
+ if (!ValidModule(MODULE)) { module = SONOFF_BASIC; }
+ Settings.module = module;
+ Settings.last_module = module;
+ }
+ SetModuleType();
+
+ if (Settings.module != Settings.last_module) {
+ baudrate = APP_BAUDRATE;
+ }
+
+ for (uint32_t i = 0; i < sizeof(Settings.user_template.gp); i++) {
+ if ((Settings.user_template.gp.io[i] >= GPIO_SENSOR_END) && (Settings.user_template.gp.io[i] < GPIO_USER)) {
+ Settings.user_template.gp.io[i] = GPIO_USER; // Fix not supported sensor ids in template
+ }
+ }
+
+ myio def_gp;
+ ModuleGpios(&def_gp);
+ for (uint32_t i = 0; i < sizeof(Settings.my_gp); i++) {
+ if ((Settings.my_gp.io[i] >= GPIO_SENSOR_END) && (Settings.my_gp.io[i] < GPIO_USER)) {
+ Settings.my_gp.io[i] = GPIO_NONE; // Fix not supported sensor ids in module
+ }
+ else if (Settings.my_gp.io[i] > GPIO_NONE) {
+ my_module.io[i] = Settings.my_gp.io[i]; // Set User selected Module sensors
+ }
+ if ((def_gp.io[i] > GPIO_NONE) && (def_gp.io[i] < GPIO_USER)) {
+ my_module.io[i] = def_gp.io[i]; // Force Template override
+ }
+ }
+ if ((Settings.my_adc0 >= ADC0_END) && (Settings.my_adc0 < ADC0_USER)) {
+ Settings.my_adc0 = ADC0_NONE; // Fix not supported sensor ids in module
+ }
+ else if (Settings.my_adc0 > ADC0_NONE) {
+ my_adc0 = Settings.my_adc0; // Set User selected Module sensors
+ }
+ my_module_flag = ModuleFlag();
+ uint32_t template_adc0 = my_module_flag.data &15;
+ if ((template_adc0 > ADC0_NONE) && (template_adc0 < ADC0_USER)) {
+ my_adc0 = template_adc0; // Force Template override
+ }
+
+ for (uint32_t i = 0; i < GPIO_MAX; i++) {
+ pin[i] = 99;
+ }
+ for (uint32_t i = 0; i < sizeof(my_module.io); i++) {
+ mpin = ValidPin(i, my_module.io[i]);
+
+ DEBUG_CORE_LOG(PSTR("INI: gpio pin %d, mpin %d"), i, mpin);
+
+ if (mpin) {
+ XdrvMailbox.index = mpin;
+ XdrvMailbox.payload = i;
+
+ if ((mpin >= GPIO_SWT1_NP) && (mpin < (GPIO_SWT1_NP + MAX_SWITCHES))) {
+ SwitchPullupFlag(mpin - GPIO_SWT1_NP);
+ mpin -= (GPIO_SWT1_NP - GPIO_SWT1);
+ }
+ else if ((mpin >= GPIO_KEY1_NP) && (mpin < (GPIO_KEY1_NP + MAX_KEYS))) {
+ ButtonPullupFlag(mpin - GPIO_KEY1_NP); // 0 .. 3
+ mpin -= (GPIO_KEY1_NP - GPIO_KEY1);
+ }
+ else if ((mpin >= GPIO_KEY1_INV) && (mpin < (GPIO_KEY1_INV + MAX_KEYS))) {
+ ButtonInvertFlag(mpin - GPIO_KEY1_INV); // 0 .. 3
+ mpin -= (GPIO_KEY1_INV - GPIO_KEY1);
+ }
+ else if ((mpin >= GPIO_KEY1_INV_NP) && (mpin < (GPIO_KEY1_INV_NP + MAX_KEYS))) {
+ ButtonPullupFlag(mpin - GPIO_KEY1_INV_NP); // 0 .. 3
+ ButtonInvertFlag(mpin - GPIO_KEY1_INV_NP); // 0 .. 3
+ mpin -= (GPIO_KEY1_INV_NP - GPIO_KEY1);
+ }
+ else if ((mpin >= GPIO_REL1_INV) && (mpin < (GPIO_REL1_INV + MAX_RELAYS))) {
+ bitSet(rel_inverted, mpin - GPIO_REL1_INV);
+ mpin -= (GPIO_REL1_INV - GPIO_REL1);
+ }
+ else if ((mpin >= GPIO_LED1_INV) && (mpin < (GPIO_LED1_INV + MAX_LEDS))) {
+ bitSet(led_inverted, mpin - GPIO_LED1_INV);
+ mpin -= (GPIO_LED1_INV - GPIO_LED1);
+ }
+ else if (mpin == GPIO_LEDLNK_INV) {
+ ledlnk_inverted = 1;
+ mpin -= (GPIO_LEDLNK_INV - GPIO_LEDLNK);
+ }
+ else if ((mpin >= GPIO_PWM1_INV) && (mpin < (GPIO_PWM1_INV + MAX_PWMS))) {
+ bitSet(pwm_inverted, mpin - GPIO_PWM1_INV);
+ mpin -= (GPIO_PWM1_INV - GPIO_PWM1);
+ }
+ else if (XdrvCall(FUNC_PIN_STATE)) {
+ mpin = XdrvMailbox.index;
+ }
+ else if (XsnsCall(FUNC_PIN_STATE)) {
+ mpin = XdrvMailbox.index;
+ };
+ }
+ if (mpin) pin[mpin] = i;
+ }
+
+ if ((2 == pin[GPIO_TXD]) || (H801 == my_module_type)) { Serial.set_tx(2); }
+
+ analogWriteRange(Settings.pwm_range); // Default is 1023 (Arduino.h)
+ analogWriteFreq(Settings.pwm_frequency); // Default is 1000 (core_esp8266_wiring_pwm.c)
+
+#ifdef USE_SPI
+ spi_flg = ((((pin[GPIO_SPI_CS] < 99) && (pin[GPIO_SPI_CS] > 14)) || (pin[GPIO_SPI_CS] < 12)) || (((pin[GPIO_SPI_DC] < 99) && (pin[GPIO_SPI_DC] > 14)) || (pin[GPIO_SPI_DC] < 12)));
+ if (spi_flg) {
+ for (uint32_t i = 0; i < GPIO_MAX; i++) {
+ if ((pin[i] >= 12) && (pin[i] <=14)) pin[i] = 99;
+ }
+ my_module.io[12] = GPIO_SPI_MISO;
+ pin[GPIO_SPI_MISO] = 12;
+ my_module.io[13] = GPIO_SPI_MOSI;
+ pin[GPIO_SPI_MOSI] = 13;
+ my_module.io[14] = GPIO_SPI_CLK;
+ pin[GPIO_SPI_CLK] = 14;
+ }
+ soft_spi_flg = ((pin[GPIO_SSPI_CS] < 99) && (pin[GPIO_SSPI_SCLK] < 99) && ((pin[GPIO_SSPI_MOSI] < 99) || (pin[GPIO_SSPI_MOSI] < 99)));
+#endif // USE_SPI
+
+#ifdef USE_I2C
+ i2c_flg = ((pin[GPIO_I2C_SCL] < 99) && (pin[GPIO_I2C_SDA] < 99));
+ if (i2c_flg) {
+ Wire.begin(pin[GPIO_I2C_SDA], pin[GPIO_I2C_SCL]);
+ }
+#endif // USE_I2C
+
+ devices_present = 0;
+ light_type = LT_BASIC; // Use basic PWM control if SetOption15 = 0
+ if (XdrvCall(FUNC_MODULE_INIT)) {
+ // Serviced
+ }
+ else if (YTF_IR_BRIDGE == my_module_type) {
+ ClaimSerial(); // Stop serial loopback mode
+// devices_present = 1;
+ }
+ else if (SONOFF_DUAL == my_module_type) {
+ Settings.flag.mqtt_serial = 0; // CMND_SERIALSEND and CMND_SERIALLOG
+ devices_present = 2;
+ baudrate = 19200;
+ }
+ else if (CH4 == my_module_type) {
+ Settings.flag.mqtt_serial = 0; // CMND_SERIALSEND and CMND_SERIALLOG
+ devices_present = 4;
+ baudrate = 19200;
+ }
+#ifdef USE_SONOFF_SC
+ else if (SONOFF_SC == my_module_type) {
+ Settings.flag.mqtt_serial = 0; // CMND_SERIALSEND and CMND_SERIALLOG
+ devices_present = 0;
+ baudrate = 19200;
+ }
+#endif // USE_SONOFF_SC
+
+ if (!light_type) {
+ for (uint32_t i = 0; i < MAX_PWMS; i++) { // Basic PWM control only
+ if (pin[GPIO_PWM1 +i] < 99) {
+ pwm_present = true;
+ pinMode(pin[GPIO_PWM1 +i], OUTPUT);
+ analogWrite(pin[GPIO_PWM1 +i], bitRead(pwm_inverted, i) ? Settings.pwm_range - Settings.pwm_value[i] : Settings.pwm_value[i]);
+ }
+ }
+ }
+ for (uint32_t i = 0; i < MAX_RELAYS; i++) {
+ if (pin[GPIO_REL1 +i] < 99) {
+ pinMode(pin[GPIO_REL1 +i], OUTPUT);
+ devices_present++;
+ if (EXS_RELAY == my_module_type) {
+ digitalWrite(pin[GPIO_REL1 +i], bitRead(rel_inverted, i) ? 1 : 0);
+ if (i &1) { devices_present--; }
+ }
+ }
+ }
+
+ for (uint32_t i = 0; i < MAX_LEDS; i++) {
+ if (pin[GPIO_LED1 +i] < 99) {
+#ifdef USE_ARILUX_RF
+ if ((3 == i) && (leds_present < 2) && (99 == pin[GPIO_ARIRFSEL])) {
+ pin[GPIO_ARIRFSEL] = pin[GPIO_LED4]; // Legacy support where LED4 was Arilux RF enable
+ pin[GPIO_LED4] = 99;
+ } else {
+#endif
+ pinMode(pin[GPIO_LED1 +i], OUTPUT);
+ leds_present++;
+ digitalWrite(pin[GPIO_LED1 +i], bitRead(led_inverted, i));
+#ifdef USE_ARILUX_RF
+ }
+#endif
+ }
+ }
+ if (pin[GPIO_LEDLNK] < 99) {
+ pinMode(pin[GPIO_LEDLNK], OUTPUT);
+ digitalWrite(pin[GPIO_LEDLNK], ledlnk_inverted);
+ }
+
+ ButtonInit();
+ SwitchInit();
+#ifdef ROTARY_V1
+ RotaryInit();
+#endif
+
+ SetLedPower(Settings.ledstate &8);
+ SetLedLink(Settings.ledstate &8);
+
+ XdrvCall(FUNC_PRE_INIT);
+}
diff --git a/tasmota/support_wifi.ino b/tasmota/support_wifi.ino
index c920c8a63..49faa0663 100644
--- a/tasmota/support_wifi.ino
+++ b/tasmota/support_wifi.ino
@@ -118,8 +118,10 @@ void WifiSetMode(WiFiMode_t wifi_mode)
delay(100);
}
- if (!WiFi.mode(wifi_mode)) {
- AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_WIFI "Cannot set Mode"));
+ uint32_t retry = 2;
+ while (!WiFi.mode(wifi_mode) && retry--) {
+ AddLog_P(LOG_LEVEL_INFO, S_LOG_WIFI, PSTR("Retry set Mode..."));
+ delay(100);
}
if (wifi_mode == WIFI_OFF) {
diff --git a/tasmota/tasmota.h b/tasmota/tasmota.h
index d9e55bd15..cefef3a06 100644
--- a/tasmota/tasmota.h
+++ b/tasmota/tasmota.h
@@ -119,7 +119,7 @@ const uint16_t INPUT_BUFFER_SIZE = 520; // Max number of characters in (seri
const uint16_t FLOATSZ = 16; // Max number of characters in float result from dtostrfd (max 32)
const uint16_t CMDSZ = 24; // Max number of characters in command
const uint16_t TOPSZ = 100; // Max number of characters in topic string
-const uint16_t LOGSZ = 520; // Max number of characters in log
+const uint16_t LOGSZ = 700; // Max number of characters in log
const uint16_t MIN_MESSZ = 893; // Min number of characters in MQTT message
const uint8_t SENSOR_MAX_MISS = 5; // Max number of missed sensor reads before deciding it's offline
diff --git a/tasmota/tasmota.ino b/tasmota/tasmota.ino
index 64a1125c6..e10dfdb63 100644
--- a/tasmota/tasmota.ino
+++ b/tasmota/tasmota.ino
@@ -37,7 +37,6 @@
#include "i18n.h" // Language support configured by my_user_config.h
#include "tasmota_template.h" // Hardware configuration
-
#ifdef ARDUINO_ESP8266_RELEASE_2_4_0
#include "lwip/init.h"
#if LWIP_VERSION_MAJOR != 1
@@ -69,11 +68,12 @@
// Structs
#include "settings.h"
-const char kSleepMode[] PROGMEM = "Dynamic|Normal";
-const char kPrefixes[] PROGMEM = D_CMND "|" D_STAT "|" D_TELE;
const char kCodeImage[] PROGMEM = "tasmota|minimal|sensors|knx|basic|display|ir";
-// Global variables
+/*********************************************************************************************\
+ * Global variables
+\*********************************************************************************************/
+
SerialConfig serial_config = SERIAL_8N1; // Serial interface configuration 8 data bits, No parity, 1 stop bit
WiFiUDP PortUdp; // UDP Syslog and Alexa
@@ -157,6 +157,7 @@ bool spi_flg = false; // SPI configured
bool soft_spi_flg = false; // Software SPI configured
bool ntp_force_sync = false; // Force NTP sync
bool ntp_synced_message = false; // NTP synced message flag
+bool is_8285 = false; // Hardware device ESP8266EX (0) or ESP8285 (1)
myio my_module; // Active copy of Module GPIOs (17 x 8 bits)
gpio_flag my_module_flag; // Active copy of Template GPIO flags
StateBitfield global_state; // Global states (currently Wifi and Mqtt) (8 bits)
@@ -180,1325 +181,9 @@ char web_log[WEB_LOG_SIZE] = {'\0'}; // Web log buffer
#define BACKLOG_EMPTY (backlog_pointer == backlog_index)
#endif
-/********************************************************************************************/
-
-char* Format(char* output, const char* input, int size)
-{
- char *token;
- uint32_t digits = 0;
-
- if (strstr(input, "%") != nullptr) {
- strlcpy(output, input, size);
- token = strtok(output, "%");
- if (strstr(input, "%") == input) {
- output[0] = '\0';
- } else {
- token = strtok(nullptr, "");
- }
- if (token != nullptr) {
- digits = atoi(token);
- if (digits) {
- char tmp[size];
- if (strchr(token, 'd')) {
- snprintf_P(tmp, size, PSTR("%s%c0%dd"), output, '%', digits);
- snprintf_P(output, size, tmp, ESP.getChipId() & 0x1fff); // %04d - short chip ID in dec, like in hostname
- } else {
- snprintf_P(tmp, size, PSTR("%s%c0%dX"), output, '%', digits);
- snprintf_P(output, size, tmp, ESP.getChipId()); // %06X - full chip ID in hex
- }
- } else {
- if (strchr(token, 'd')) {
- snprintf_P(output, size, PSTR("%s%d"), output, ESP.getChipId()); // %d - full chip ID in dec
- digits = 8;
- }
- }
- }
- }
- if (!digits) {
- strlcpy(output, input, size);
- }
- return output;
-}
-
-char* GetOtaUrl(char *otaurl, size_t otaurl_size)
-{
- if (strstr(Settings.ota_url, "%04d") != nullptr) { // OTA url contains placeholder for chip ID
- snprintf(otaurl, otaurl_size, Settings.ota_url, ESP.getChipId() & 0x1fff);
- }
- else if (strstr(Settings.ota_url, "%d") != nullptr) { // OTA url contains placeholder for chip ID
- snprintf_P(otaurl, otaurl_size, Settings.ota_url, ESP.getChipId());
- }
- else {
- strlcpy(otaurl, Settings.ota_url, otaurl_size);
- }
- return otaurl;
-}
-
-char* GetTopic_P(char *stopic, uint32_t prefix, char *topic, const char* subtopic)
-{
- /* prefix 0 = Cmnd
- prefix 1 = Stat
- prefix 2 = Tele
- prefix 4 = Cmnd fallback
- prefix 5 = Stat fallback
- prefix 6 = Tele fallback
- prefix 8 = Cmnd topic
- prefix 9 = Stat topic
- prefix 10 = Tele topic
- */
- char romram[CMDSZ];
- String fulltopic;
-
- snprintf_P(romram, sizeof(romram), subtopic);
- if (fallback_topic_flag || (prefix > 3)) {
- bool fallback = (prefix < 8);
- prefix &= 3;
- char stemp[11];
- fulltopic = GetTextIndexed(stemp, sizeof(stemp), prefix, kPrefixes);
- fulltopic += F("/");
- if (fallback) {
- fulltopic += mqtt_client;
- fulltopic += F("_fb"); // cmnd/_fb
- } else {
- fulltopic += topic; // cmnd/
- }
- } else {
- fulltopic = Settings.mqtt_fulltopic;
- if ((0 == prefix) && (-1 == fulltopic.indexOf(FPSTR(MQTT_TOKEN_PREFIX)))) {
- fulltopic += F("/");
- fulltopic += FPSTR(MQTT_TOKEN_PREFIX); // Need prefix for commands to handle mqtt topic loops
- }
- for (uint32_t i = 0; i < 3; i++) {
- if ('\0' == Settings.mqtt_prefix[i][0]) {
- GetTextIndexed(Settings.mqtt_prefix[i], sizeof(Settings.mqtt_prefix[i]), i, kPrefixes);
- }
- }
- fulltopic.replace(FPSTR(MQTT_TOKEN_PREFIX), Settings.mqtt_prefix[prefix]);
- fulltopic.replace(FPSTR(MQTT_TOKEN_TOPIC), topic);
- fulltopic.replace(F("%hostname%"), my_hostname);
- String token_id = WiFi.macAddress();
- token_id.replace(":", "");
- fulltopic.replace(F("%id%"), token_id);
- }
- fulltopic.replace(F("#"), "");
- fulltopic.replace(F("//"), "/");
- if (!fulltopic.endsWith("/")) {
- fulltopic += "/";
- }
- snprintf_P(stopic, TOPSZ, PSTR("%s%s"), fulltopic.c_str(), romram);
- return stopic;
-}
-
-char* GetGroupTopic_P(char *stopic, const char* subtopic)
-{
- // SetOption75 0: %prefix%/nothing/%topic% = cmnd/nothing//#
- // SetOption75 1: cmnd/
- return GetTopic_P(stopic, (Settings.flag3.grouptopic_mode) ? CMND +8 : CMND, Settings.mqtt_grptopic, subtopic); // SetOption75 - GroupTopic replaces %topic% (0) or fixed topic cmnd/grouptopic (1)
-}
-
-char* GetFallbackTopic_P(char *stopic, const char* subtopic)
-{
- return GetTopic_P(stopic, CMND +4, nullptr, subtopic);
-}
-
-char* GetStateText(uint32_t state)
-{
- if (state > 3) {
- state = 1;
- }
- return Settings.state_text[state];
-}
-
-/********************************************************************************************/
-
-void SetLatchingRelay(power_t lpower, uint32_t state)
-{
- // power xx00 - toggle REL1 (Off) and REL3 (Off) - device 1 Off, device 2 Off
- // power xx01 - toggle REL2 (On) and REL3 (Off) - device 1 On, device 2 Off
- // power xx10 - toggle REL1 (Off) and REL4 (On) - device 1 Off, device 2 On
- // power xx11 - toggle REL2 (On) and REL4 (On) - device 1 On, device 2 On
-
- if (state && !latching_relay_pulse) { // Set latching relay to power if previous pulse has finished
- latching_power = lpower;
- latching_relay_pulse = 2; // max 200mS (initiated by stateloop())
- }
-
- for (uint32_t i = 0; i < devices_present; i++) {
- uint32_t port = (i << 1) + ((latching_power >> i) &1);
- if (pin[GPIO_REL1 +port] < 99) {
- digitalWrite(pin[GPIO_REL1 +port], bitRead(rel_inverted, port) ? !state : state);
- }
- }
-}
-
-void SetDevicePower(power_t rpower, uint32_t source)
-{
- ShowSource(source);
- last_source = source;
-
- if (POWER_ALL_ALWAYS_ON == Settings.poweronstate) { // All on and stay on
- power = (1 << devices_present) -1;
- rpower = power;
- }
-
- if (Settings.flag.interlock) { // Allow only one or no relay set - CMND_INTERLOCK - Enable/disable interlock
- for (uint32_t i = 0; i < MAX_INTERLOCKS; i++) {
- power_t mask = 1;
- uint32_t count = 0;
- for (uint32_t j = 0; j < devices_present; j++) {
- if ((Settings.interlock[i] & mask) && (rpower & mask)) {
- count++;
- }
- mask <<= 1;
- }
- if (count > 1) {
- mask = ~Settings.interlock[i]; // Turn interlocked group off as there would be multiple relays on
- power &= mask;
- rpower &= mask;
- }
- }
- }
-
- if (rpower) { // Any power set
- last_power = rpower;
- }
-
- XdrvMailbox.index = rpower;
- XdrvCall(FUNC_SET_POWER); // Signal power state
-
- XdrvMailbox.index = rpower;
- XdrvMailbox.payload = source;
- if (XdrvCall(FUNC_SET_DEVICE_POWER)) { // Set power state and stop if serviced
- // Serviced
- }
- else if ((SONOFF_DUAL == my_module_type) || (CH4 == my_module_type)) {
- Serial.write(0xA0);
- Serial.write(0x04);
- Serial.write(rpower &0xFF);
- Serial.write(0xA1);
- Serial.write('\n');
- Serial.flush();
- }
- else if (EXS_RELAY == my_module_type) {
- SetLatchingRelay(rpower, 1);
- }
- else {
- for (uint32_t i = 0; i < devices_present; i++) {
- power_t state = rpower &1;
- if ((i < MAX_RELAYS) && (pin[GPIO_REL1 +i] < 99)) {
- digitalWrite(pin[GPIO_REL1 +i], bitRead(rel_inverted, i) ? !state : state);
- }
- rpower >>= 1;
- }
- }
-}
-
-void RestorePower(bool publish_power, uint32_t source)
-{
- if (power != last_power) {
- SetDevicePower(last_power, source);
- if (publish_power) {
- MqttPublishAllPowerState();
- }
- }
-}
-
-void SetAllPower(uint32_t state, uint32_t source)
-{
-// state 0 = POWER_OFF = Relay Off
-// state 1 = POWER_ON = Relay On (turn off after Settings.pulse_timer * 100 mSec if enabled)
-// state 2 = POWER_TOGGLE = Toggle relay
-// state 8 = POWER_OFF_NO_STATE = Relay Off and no publishPowerState
-// state 9 = POWER_ON_NO_STATE = Relay On and no publishPowerState
-// state 10 = POWER_TOGGLE_NO_STATE = Toggle relay and no publishPowerState
-// state 16 = POWER_SHOW_STATE = Show power state
-
- bool publish_power = true;
- if ((state >= POWER_OFF_NO_STATE) && (state <= POWER_TOGGLE_NO_STATE)) {
- state &= 3; // POWER_OFF, POWER_ON or POWER_TOGGLE
- publish_power = false;
- }
- if ((state >= POWER_OFF) && (state <= POWER_TOGGLE)) {
- power_t all_on = (1 << devices_present) -1;
- switch (state) {
- case POWER_OFF:
- power = 0;
- break;
- case POWER_ON:
- power = all_on;
- break;
- case POWER_TOGGLE:
- power ^= all_on; // Complement current state
- }
- SetDevicePower(power, source);
- }
- if (publish_power) {
- MqttPublishAllPowerState();
- }
-}
-
-void SetLedPowerIdx(uint32_t led, uint32_t state)
-{
- if ((99 == pin[GPIO_LEDLNK]) && (0 == led)) { // Legacy - LED1 is link led only if LED2 is present
- if (pin[GPIO_LED2] < 99) {
- led = 1;
- }
- }
- if (pin[GPIO_LED1 + led] < 99) {
- uint32_t mask = 1 << led;
- if (state) {
- state = 1;
- led_power |= mask;
- } else {
- led_power &= (0xFF ^ mask);
- }
- digitalWrite(pin[GPIO_LED1 + led], bitRead(led_inverted, led) ? !state : state);
- }
-}
-
-void SetLedPower(uint32_t state)
-{
- if (99 == pin[GPIO_LEDLNK]) { // Legacy - Only use LED1 and/or LED2
- SetLedPowerIdx(0, state);
- } else {
- power_t mask = 1;
- for (uint32_t i = 0; i < leds_present; i++) { // Map leds to power
- bool tstate = (power & mask);
- SetLedPowerIdx(i, tstate);
- mask <<= 1;
- }
- }
-}
-
-void SetLedPowerAll(uint32_t state)
-{
- for (uint32_t i = 0; i < leds_present; i++) {
- SetLedPowerIdx(i, state);
- }
-}
-
-void SetLedLink(uint32_t state)
-{
- uint32_t led_pin = pin[GPIO_LEDLNK];
- uint32_t led_inv = ledlnk_inverted;
- if (99 == led_pin) { // Legacy - LED1 is status
- led_pin = pin[GPIO_LED1];
- led_inv = bitRead(led_inverted, 0);
- }
- if (led_pin < 99) {
- if (state) { state = 1; }
- digitalWrite(led_pin, (led_inv) ? !state : state);
- }
-}
-
-void SetPulseTimer(uint32_t index, uint32_t time)
-{
- pulse_timer[index] = (time > 111) ? millis() + (1000 * (time - 100)) : (time > 0) ? millis() + (100 * time) : 0L;
-}
-
-uint32_t GetPulseTimer(uint32_t index)
-{
- long time = TimePassedSince(pulse_timer[index]);
- if (time < 0) {
- time *= -1;
- return (time > 11100) ? (time / 1000) + 100 : (time > 0) ? time / 100 : 0;
- }
- return 0;
-}
-
-/********************************************************************************************/
-
-bool SendKey(uint32_t key, uint32_t device, uint32_t state)
-{
-// key 0 = KEY_BUTTON = button_topic
-// key 1 = KEY_SWITCH = switch_topic
-// state 0 = POWER_OFF = off
-// state 1 = POWER_ON = on
-// state 2 = POWER_TOGGLE = toggle
-// state 3 = POWER_HOLD = hold
-// state 9 = CLEAR_RETAIN = clear retain flag
-
- char stopic[TOPSZ];
- char scommand[CMDSZ];
- char key_topic[sizeof(Settings.button_topic)];
- bool result = false;
-
- char *tmp = (key) ? Settings.switch_topic : Settings.button_topic;
- Format(key_topic, tmp, sizeof(key_topic));
- if (Settings.flag.mqtt_enabled && MqttIsConnected() && (strlen(key_topic) != 0) && strcmp(key_topic, "0")) { // SetOption3 - Enable MQTT
- if (!key && (device > devices_present)) {
- device = 1; // Only allow number of buttons up to number of devices
- }
- GetTopic_P(stopic, CMND, key_topic,
- GetPowerDevice(scommand, device, sizeof(scommand), (key + Settings.flag.device_index_enable))); // cmnd/switchtopic/POWERx - SetOption26 - Switch between POWER or POWER1
- if (CLEAR_RETAIN == state) {
- mqtt_data[0] = '\0';
- } else {
- if ((Settings.flag3.button_switch_force_local || // SetOption61 - Force local operation when button/switch topic is set
- !strcmp(mqtt_topic, key_topic) ||
- !strcmp(Settings.mqtt_grptopic, key_topic)) &&
- (POWER_TOGGLE == state)) {
- state = ~(power >> (device -1)) &1; // POWER_OFF or POWER_ON
- }
- snprintf_P(mqtt_data, sizeof(mqtt_data), GetStateText(state));
- }
-#ifdef USE_DOMOTICZ
- if (!(DomoticzSendKey(key, device, state, strlen(mqtt_data)))) {
-#endif // USE_DOMOTICZ
- MqttPublishDirect(stopic, ((key) ? Settings.flag.mqtt_switch_retain // CMND_SWITCHRETAIN
- : Settings.flag.mqtt_button_retain) && // CMND_BUTTONRETAIN
- (state != POWER_HOLD || !Settings.flag3.no_hold_retain)); // SetOption62 - Don't use retain flag on HOLD messages
-#ifdef USE_DOMOTICZ
- }
-#endif // USE_DOMOTICZ
- result = !Settings.flag3.button_switch_force_local; // SetOption61 - Force local operation when button/switch topic is set
- } else {
- Response_P(PSTR("{\"%s%d\":{\"State\":%d}}"), (key) ? "Switch" : "Button", device, state);
- result = XdrvRulesProcess();
- }
- int32_t payload_save = XdrvMailbox.payload;
- XdrvMailbox.payload = key << 16 | state << 8 | device;
- XdrvCall(FUNC_ANY_KEY);
- XdrvMailbox.payload = payload_save;
- return result;
-}
-
-void ExecuteCommandPower(uint32_t device, uint32_t state, uint32_t source)
-{
-// device = Relay number 1 and up
-// state 0 = POWER_OFF = Relay Off
-// state 1 = POWER_ON = Relay On (turn off after Settings.pulse_timer * 100 mSec if enabled)
-// state 2 = POWER_TOGGLE = Toggle relay
-// state 3 = POWER_BLINK = Blink relay
-// state 4 = POWER_BLINK_STOP = Stop blinking relay
-// state 8 = POWER_OFF_NO_STATE = Relay Off and no publishPowerState
-// state 9 = POWER_ON_NO_STATE = Relay On and no publishPowerState
-// state 10 = POWER_TOGGLE_NO_STATE = Toggle relay and no publishPowerState
-// state 16 = POWER_SHOW_STATE = Show power state
-
-// ShowSource(source);
-
-#ifdef USE_SONOFF_IFAN
- if (IsModuleIfan()) {
- blink_mask &= 1; // No blinking on the fan relays
- Settings.flag.interlock = 0; // No interlock mode as it is already done by the microcontroller - CMND_INTERLOCK - Enable/disable interlock
- Settings.pulse_timer[1] = 0; // No pulsetimers on the fan relays
- Settings.pulse_timer[2] = 0;
- Settings.pulse_timer[3] = 0;
- }
-#endif // USE_SONOFF_IFAN
-
- bool publish_power = true;
- if ((state >= POWER_OFF_NO_STATE) && (state <= POWER_TOGGLE_NO_STATE)) {
- state &= 3; // POWER_OFF, POWER_ON or POWER_TOGGLE
- publish_power = false;
- }
-
- if ((device < 1) || (device > devices_present)) {
- device = 1;
- }
- active_device = device;
-
- if (device <= MAX_PULSETIMERS) {
- SetPulseTimer(device -1, 0);
- }
- power_t mask = 1 << (device -1); // Device to control
- if (state <= POWER_TOGGLE) {
- if ((blink_mask & mask)) {
- blink_mask &= (POWER_MASK ^ mask); // Clear device mask
- MqttPublishPowerBlinkState(device);
- }
-
- if (Settings.flag.interlock && // CMND_INTERLOCK - Enable/disable interlock
- !interlock_mutex &&
- ((POWER_ON == state) || ((POWER_TOGGLE == state) && !(power & mask)))
- ) {
- interlock_mutex = true; // Clear all but masked relay in interlock group if new set requested
- for (uint32_t i = 0; i < MAX_INTERLOCKS; i++) {
- if (Settings.interlock[i] & mask) { // Find interlock group
- for (uint32_t j = 0; j < devices_present; j++) {
- power_t imask = 1 << j;
- if ((Settings.interlock[i] & imask) && (power & imask) && (mask != imask)) {
- ExecuteCommandPower(j +1, POWER_OFF, SRC_IGNORE);
- delay(50); // Add some delay to make sure never have more than one relay on
- }
- }
- break; // An interlocked relay is only present in one group so quit
- }
- }
- interlock_mutex = false;
- }
-
- switch (state) {
- case POWER_OFF: {
- power &= (POWER_MASK ^ mask);
- break; }
- case POWER_ON:
- power |= mask;
- break;
- case POWER_TOGGLE:
- power ^= mask;
- }
- SetDevicePower(power, source);
-#ifdef USE_DOMOTICZ
- DomoticzUpdatePowerState(device);
-#endif // USE_DOMOTICZ
-#ifdef USE_KNX
- KnxUpdatePowerState(device, power);
-#endif // USE_KNX
- if (publish_power && Settings.flag3.hass_tele_on_power) { // SetOption59 - Send tele/%topic%/STATE in addition to stat/%topic%/RESULT
- MqttPublishTeleState();
- }
- if (device <= MAX_PULSETIMERS) { // Restart PulseTime if powered On
- SetPulseTimer(device -1, (((POWER_ALL_OFF_PULSETIME_ON == Settings.poweronstate) ? ~power : power) & mask) ? Settings.pulse_timer[device -1] : 0);
- }
- }
- else if (POWER_BLINK == state) {
- if (!(blink_mask & mask)) {
- blink_powersave = (blink_powersave & (POWER_MASK ^ mask)) | (power & mask); // Save state
- blink_power = (power >> (device -1))&1; // Prep to Toggle
- }
- blink_timer = millis() + 100;
- blink_counter = ((!Settings.blinkcount) ? 64000 : (Settings.blinkcount *2)) +1;
- blink_mask |= mask; // Set device mask
- MqttPublishPowerBlinkState(device);
- return;
- }
- else if (POWER_BLINK_STOP == state) {
- bool flag = (blink_mask & mask);
- blink_mask &= (POWER_MASK ^ mask); // Clear device mask
- MqttPublishPowerBlinkState(device);
- if (flag) {
- ExecuteCommandPower(device, (blink_powersave >> (device -1))&1, SRC_IGNORE); // Restore state
- }
- return;
- }
- if (publish_power) {
- MqttPublishPowerState(device);
- }
-}
-
-void StopAllPowerBlink(void)
-{
- power_t mask;
-
- for (uint32_t i = 1; i <= devices_present; i++) {
- mask = 1 << (i -1);
- if (blink_mask & mask) {
- blink_mask &= (POWER_MASK ^ mask); // Clear device mask
- MqttPublishPowerBlinkState(i);
- ExecuteCommandPower(i, (blink_powersave >> (i -1))&1, SRC_IGNORE); // Restore state
- }
- }
-}
-
-void MqttShowPWMState(void)
-{
- ResponseAppend_P(PSTR("\"" D_CMND_PWM "\":{"));
- bool first = true;
- for (uint32_t i = 0; i < MAX_PWMS; i++) {
- if (pin[GPIO_PWM1 + i] < 99) {
- ResponseAppend_P(PSTR("%s\"" D_CMND_PWM "%d\":%d"), first ? "" : ",", i+1, Settings.pwm_value[i]);
- first = false;
- }
- }
- ResponseJsonEnd();
-}
-
-void MqttShowState(void)
-{
- char stemp1[33];
-
- ResponseAppendTime();
- ResponseAppend_P(PSTR(",\"" D_JSON_UPTIME "\":\"%s\",\"UptimeSec\":%u"), GetUptime().c_str(), UpTime());
-
-#ifdef USE_ADC_VCC
- dtostrfd((double)ESP.getVcc()/1000, 3, stemp1);
- ResponseAppend_P(PSTR(",\"" D_JSON_VCC "\":%s"), stemp1);
-#endif
-
- ResponseAppend_P(PSTR(",\"" D_JSON_HEAPSIZE "\":%d,\"SleepMode\":\"%s\",\"Sleep\":%u,\"LoadAvg\":%u,\"MqttCount\":%u"),
- ESP.getFreeHeap()/1024, GetTextIndexed(stemp1, sizeof(stemp1), Settings.flag3.sleep_normal, kSleepMode), // SetOption60 - Enable normal sleep instead of dynamic sleep
- sleep, loop_load_avg, MqttConnectCount());
-
- for (uint32_t i = 1; i <= devices_present; i++) {
-#ifdef USE_LIGHT
- if ((LightDevice()) && (i >= LightDevice())) {
- if (i == LightDevice()) { LightState(1); } // call it only once
- } else {
-#endif
- ResponseAppend_P(PSTR(",\"%s\":\"%s\""), GetPowerDevice(stemp1, i, sizeof(stemp1), Settings.flag.device_index_enable), // SetOption26 - Switch between POWER or POWER1
- GetStateText(bitRead(power, i-1)));
-#ifdef USE_SONOFF_IFAN
- if (IsModuleIfan()) {
- ResponseAppend_P(PSTR(",\"" D_CMND_FANSPEED "\":%d"), GetFanspeed());
- break;
- }
-#endif // USE_SONOFF_IFAN
-#ifdef USE_LIGHT
- }
-#endif
- }
-
- if (pwm_present) {
- ResponseAppend_P(PSTR(","));
- MqttShowPWMState();
- }
-
- ResponseAppend_P(PSTR(",\"" D_JSON_WIFI "\":{\"" D_JSON_AP "\":%d,\"" D_JSON_SSID "\":\"%s\",\"" D_JSON_BSSID "\":\"%s\",\"" D_JSON_CHANNEL "\":%d,\"" D_JSON_RSSI "\":%d,\"" D_JSON_LINK_COUNT "\":%d,\"" D_JSON_DOWNTIME "\":\"%s\"}}"),
- Settings.sta_active +1, Settings.sta_ssid[Settings.sta_active], WiFi.BSSIDstr().c_str(), WiFi.channel(), WifiGetRssiAsQuality(WiFi.RSSI()), WifiLinkCount(), WifiDowntime().c_str());
-}
-
-void MqttPublishTeleState(void)
-{
- mqtt_data[0] = '\0';
- MqttShowState();
- MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_STATE), MQTT_TELE_RETAIN);
-#ifdef USE_SCRIPT
- RulesTeleperiod(); // Allow rule based HA messages
-#endif // USE_SCRIPT
-}
-
-bool MqttShowSensor(void)
-{
- ResponseAppendTime();
-
- int json_data_start = strlen(mqtt_data);
- for (uint32_t i = 0; i < MAX_SWITCHES; i++) {
-#ifdef USE_TM1638
- if ((pin[GPIO_SWT1 +i] < 99) || ((pin[GPIO_TM16CLK] < 99) && (pin[GPIO_TM16DIO] < 99) && (pin[GPIO_TM16STB] < 99))) {
-#else
- if (pin[GPIO_SWT1 +i] < 99) {
-#endif // USE_TM1638
- bool swm = ((FOLLOW_INV == Settings.switchmode[i]) || (PUSHBUTTON_INV == Settings.switchmode[i]) || (PUSHBUTTONHOLD_INV == Settings.switchmode[i]));
- ResponseAppend_P(PSTR(",\"" D_JSON_SWITCH "%d\":\"%s\""), i +1, GetStateText(swm ^ SwitchLastState(i)));
- }
- }
- XsnsCall(FUNC_JSON_APPEND);
- XdrvCall(FUNC_JSON_APPEND);
-
- bool json_data_available = (strlen(mqtt_data) - json_data_start);
- if (strstr_P(mqtt_data, PSTR(D_JSON_PRESSURE)) != nullptr) {
- ResponseAppend_P(PSTR(",\"" D_JSON_PRESSURE_UNIT "\":\"%s\""), PressureUnit().c_str());
- }
- if (strstr_P(mqtt_data, PSTR(D_JSON_TEMPERATURE)) != nullptr) {
- ResponseAppend_P(PSTR(",\"" D_JSON_TEMPERATURE_UNIT "\":\"%c\""), TempUnit());
- }
- ResponseJsonEnd();
-
- if (json_data_available) { XdrvCall(FUNC_SHOW_SENSOR); }
- return json_data_available;
-}
-
-void MqttPublishSensor(void)
-{
- mqtt_data[0] = '\0';
- if (MqttShowSensor()) {
- MqttPublishTeleSensor();
- }
-}
-
-/********************************************************************************************/
-
-void PerformEverySecond(void)
-{
- uptime++;
-
- if (ntp_synced_message) {
- // Moved here to fix syslog UDP exception 9 during RtcSecond
- AddLog_P2(LOG_LEVEL_DEBUG, PSTR("NTP: Drift %d, (" D_UTC_TIME ") %s, (" D_DST_TIME ") %s, (" D_STD_TIME ") %s"),
- DriftTime(), GetTime(0).c_str(), GetTime(2).c_str(), GetTime(3).c_str());
- ntp_synced_message = false;
- }
-
- if (POWER_CYCLE_TIME == uptime) {
- UpdateQuickPowerCycle(false);
- }
-
- if (BOOT_LOOP_TIME == uptime) {
- RtcRebootReset();
-
-#ifdef USE_DEEPSLEEP
- if (!(DeepSleepEnabled() && !Settings.flag3.bootcount_update)) {
-#endif
- Settings.bootcount++; // Moved to here to stop flash writes during start-up
- AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_APPLICATION D_BOOT_COUNT " %d"), Settings.bootcount);
-#ifdef USE_DEEPSLEEP
- }
-#endif
- }
-
- if (seriallog_timer) {
- seriallog_timer--;
- if (!seriallog_timer) {
- if (seriallog_level) {
- AddLog_P(LOG_LEVEL_INFO, PSTR(D_LOG_APPLICATION D_SERIAL_LOGGING_DISABLED));
- }
- seriallog_level = 0;
- }
- }
-
- if (syslog_timer) { // Restore syslog level
- syslog_timer--;
- if (!syslog_timer) {
- syslog_level = Settings.syslog_level;
- if (Settings.syslog_level) {
- AddLog_P(LOG_LEVEL_INFO, PSTR(D_LOG_APPLICATION D_SYSLOG_LOGGING_REENABLED)); // Might trigger disable again (on purpose)
- }
- }
- }
-
- ResetGlobalValues();
-
- if (Settings.tele_period) {
- if (tele_period >= 9999) {
- if (!global_state.wifi_down) {
- tele_period = 0; // Allow teleperiod once wifi is connected
- }
- } else {
- tele_period++;
- if (tele_period >= Settings.tele_period) {
- tele_period = 0;
-
- MqttPublishTeleState();
-
- mqtt_data[0] = '\0';
- if (MqttShowSensor()) {
- MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_SENSOR), Settings.flag.mqtt_sensor_retain); // CMND_SENSORRETAIN
-#if defined(USE_RULES) || defined(USE_SCRIPT)
- RulesTeleperiod(); // Allow rule based HA messages
-#endif // USE_RULES
- }
-
- XdrvCall(FUNC_AFTER_TELEPERIOD);
- }
- }
- }
-}
-
/*********************************************************************************************\
- * State loops
+ * Main
\*********************************************************************************************/
-/*-------------------------------------------------------------------------------------------*\
- * Every 0.1 second
-\*-------------------------------------------------------------------------------------------*/
-
-void Every100mSeconds(void)
-{
- // As the max amount of sleep = 250 mSec this loop will shift in time...
- power_t power_now;
-
- if (latching_relay_pulse) {
- latching_relay_pulse--;
- if (!latching_relay_pulse) SetLatchingRelay(0, 0);
- }
-
- for (uint32_t i = 0; i < MAX_PULSETIMERS; i++) {
- if (pulse_timer[i] != 0L) { // Timer active?
- if (TimeReached(pulse_timer[i])) { // Timer finished?
- pulse_timer[i] = 0L; // Turn off this timer
- ExecuteCommandPower(i +1, (POWER_ALL_OFF_PULSETIME_ON == Settings.poweronstate) ? POWER_ON : POWER_OFF, SRC_PULSETIMER);
- }
- }
- }
-
- if (blink_mask) {
- if (TimeReached(blink_timer)) {
- SetNextTimeInterval(blink_timer, 100 * Settings.blinktime);
- blink_counter--;
- if (!blink_counter) {
- StopAllPowerBlink();
- } else {
- blink_power ^= 1;
- power_now = (power & (POWER_MASK ^ blink_mask)) | ((blink_power) ? blink_mask : 0);
- SetDevicePower(power_now, SRC_IGNORE);
- }
- }
- }
-}
-
-/*-------------------------------------------------------------------------------------------*\
- * Every 0.25 second
-\*-------------------------------------------------------------------------------------------*/
-
-void Every250mSeconds(void)
-{
-// As the max amount of sleep = 250 mSec this loop should always be taken...
-
- uint32_t blinkinterval = 1;
-
- state_250mS++;
- state_250mS &= 0x3;
-
- if (mqtt_cmnd_publish) mqtt_cmnd_publish--; // Clean up
-
- if (!Settings.flag.global_state) { // Problem blinkyblinky enabled - SetOption31 - Control link led blinking
- if (global_state.data) { // Any problem
- if (global_state.mqtt_down) { blinkinterval = 7; } // MQTT problem so blink every 2 seconds (slowest)
- if (global_state.wifi_down) { blinkinterval = 3; } // Wifi problem so blink every second (slow)
- blinks = 201; // Allow only a single blink in case the problem is solved
- }
- }
- if (blinks || restart_flag || ota_state_flag) {
- if (restart_flag || ota_state_flag) { // Overrule blinks and keep led lit
- blinkstate = true; // Stay lit
- } else {
- blinkspeed--;
- if (!blinkspeed) {
- blinkspeed = blinkinterval; // Set interval to 0.2 (default), 1 or 2 seconds
- blinkstate ^= 1; // Blink
- }
- }
- if ((!(Settings.ledstate &0x08)) && ((Settings.ledstate &0x06) || (blinks > 200) || (blinkstate))) {
- SetLedLink(blinkstate); // Set led on or off
- }
- if (!blinkstate) {
- blinks--;
- if (200 == blinks) blinks = 0; // Disable blink
- }
- }
- if (Settings.ledstate &1 && (pin[GPIO_LEDLNK] < 99 || !(blinks || restart_flag || ota_state_flag)) ) {
- bool tstate = power & Settings.ledmask;
- if ((SONOFF_TOUCH == my_module_type) || (SONOFF_T11 == my_module_type) || (SONOFF_T12 == my_module_type) || (SONOFF_T13 == my_module_type)) {
- tstate = (!power) ? 1 : 0; // As requested invert signal for Touch devices to find them in the dark
- }
- SetLedPower(tstate);
- }
-
-/*-------------------------------------------------------------------------------------------*\
- * Every second at 0.25 second interval
-\*-------------------------------------------------------------------------------------------*/
-
- switch (state_250mS) {
- case 0: // Every x.0 second
- if (ota_state_flag && BACKLOG_EMPTY) {
- ota_state_flag--;
- if (2 == ota_state_flag) {
- ota_url = Settings.ota_url;
- RtcSettings.ota_loader = 0; // Try requested image first
- ota_retry_counter = OTA_ATTEMPTS;
- ESPhttpUpdate.rebootOnUpdate(false);
- SettingsSave(1); // Free flash for OTA update
- }
- if (ota_state_flag <= 0) {
-#ifdef USE_WEBSERVER
- if (Settings.webserver) StopWebserver();
-#endif // USE_WEBSERVER
-#ifdef USE_ARILUX_RF
- AriluxRfDisable(); // Prevent restart exception on Arilux Interrupt routine
-#endif // USE_ARILUX_RF
- ota_state_flag = 92;
- ota_result = 0;
- ota_retry_counter--;
- if (ota_retry_counter) {
- strlcpy(mqtt_data, GetOtaUrl(log_data, sizeof(log_data)), sizeof(mqtt_data));
-#ifndef FIRMWARE_MINIMAL
- if (RtcSettings.ota_loader) {
- char *bch = strrchr(mqtt_data, '/'); // Only consider filename after last backslash prevent change of urls having "-" in it
- char *pch = strrchr((bch != nullptr) ? bch : mqtt_data, '-'); // Change from filename-DE.bin into filename-minimal.bin
- char *ech = strrchr((bch != nullptr) ? bch : mqtt_data, '.'); // Change from filename.bin into filename-minimal.bin
- if (!pch) { pch = ech; }
- if (pch) {
- mqtt_data[pch - mqtt_data] = '\0';
- char *ech = strrchr(Settings.ota_url, '.'); // Change from filename.bin into filename-minimal.bin
- snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s-" D_JSON_MINIMAL "%s"), mqtt_data, ech); // Minimal filename must be filename-minimal
- }
- }
-#endif // FIRMWARE_MINIMAL
- AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_UPLOAD "%s"), mqtt_data);
-#if defined(ARDUINO_ESP8266_RELEASE_2_3_0) || defined(ARDUINO_ESP8266_RELEASE_2_4_0) || defined(ARDUINO_ESP8266_RELEASE_2_4_1) || defined(ARDUINO_ESP8266_RELEASE_2_4_2)
- ota_result = (HTTP_UPDATE_FAILED != ESPhttpUpdate.update(mqtt_data));
-#else
- // If using core stage or 2.5.0+ the syntax has changed
- WiFiClient OTAclient;
- ota_result = (HTTP_UPDATE_FAILED != ESPhttpUpdate.update(OTAclient, mqtt_data));
-#endif
- if (!ota_result) {
-#ifndef FIRMWARE_MINIMAL
- int ota_error = ESPhttpUpdate.getLastError();
- DEBUG_CORE_LOG(PSTR("OTA: Error %d"), ota_error);
- if ((HTTP_UE_TOO_LESS_SPACE == ota_error) || (HTTP_UE_BIN_FOR_WRONG_FLASH == ota_error)) {
- RtcSettings.ota_loader = 1; // Try minimal image next
- }
-#endif // FIRMWARE_MINIMAL
- ota_state_flag = 2; // Upgrade failed - retry
- }
- }
- }
- if (90 == ota_state_flag) { // Allow MQTT to reconnect
- ota_state_flag = 0;
- if (ota_result) {
-// SetFlashModeDout(); // Force DOUT for both ESP8266 and ESP8285
- Response_P(PSTR(D_JSON_SUCCESSFUL ". " D_JSON_RESTARTING));
- } else {
- Response_P(PSTR(D_JSON_FAILED " %s"), ESPhttpUpdate.getLastErrorString().c_str());
- }
- restart_flag = 2; // Restart anyway to keep memory clean webserver
- MqttPublishPrefixTopic_P(STAT, PSTR(D_CMND_UPGRADE));
- }
- }
- break;
- case 1: // Every x.25 second
- if (MidnightNow()) {
- XsnsCall(FUNC_SAVE_AT_MIDNIGHT);
- }
- if (save_data_counter && BACKLOG_EMPTY) {
- save_data_counter--;
- if (save_data_counter <= 0) {
- if (Settings.flag.save_state) { // SetOption0 - Save power state and use after restart
- power_t mask = POWER_MASK;
- for (uint32_t i = 0; i < MAX_PULSETIMERS; i++) {
- if ((Settings.pulse_timer[i] > 0) && (Settings.pulse_timer[i] < 30)) { // 3 seconds
- mask &= ~(1 << i);
- }
- }
- if (!((Settings.power &mask) == (power &mask))) {
- Settings.power = power;
- }
- } else {
- Settings.power = 0;
- }
- SettingsSave(0);
- save_data_counter = Settings.save_data;
- }
- }
- if (restart_flag && BACKLOG_EMPTY) {
- if ((214 == restart_flag) || (215 == restart_flag) || (216 == restart_flag)) {
- char storage_wifi[sizeof(Settings.sta_ssid) +
- sizeof(Settings.sta_pwd)];
- char storage_mqtt[sizeof(Settings.mqtt_host) +
- sizeof(Settings.mqtt_port) +
- sizeof(Settings.mqtt_client) +
- sizeof(Settings.mqtt_user) +
- sizeof(Settings.mqtt_pwd) +
- sizeof(Settings.mqtt_topic)];
- memcpy(storage_wifi, Settings.sta_ssid, sizeof(storage_wifi)); // Backup current SSIDs and Passwords
- if (216 == restart_flag) {
- memcpy(storage_mqtt, Settings.mqtt_host, sizeof(storage_mqtt)); // Backup mqtt host, port, client, username and password
- }
- if ((215 == restart_flag) || (216 == restart_flag)) {
- SettingsErase(0); // Erase all flash from program end to end of physical flash
- }
- SettingsDefault();
- memcpy(Settings.sta_ssid, storage_wifi, sizeof(storage_wifi)); // Restore current SSIDs and Passwords
- if (216 == restart_flag) {
- memcpy(Settings.mqtt_host, storage_mqtt, sizeof(storage_mqtt)); // Restore the mqtt host, port, client, username and password
- strlcpy(Settings.mqtt_client, MQTT_CLIENT_ID, sizeof(Settings.mqtt_client)); // Set client to default
- }
- restart_flag = 2;
- }
- else if (213 == restart_flag) {
- SettingsSdkErase(); // Erase flash SDK parameters
- restart_flag = 2;
- }
- else if (212 == restart_flag) {
- SettingsErase(0); // Erase all flash from program end to end of physical flash
- restart_flag = 211;
- }
- if (211 == restart_flag) {
- SettingsDefault();
- restart_flag = 2;
- }
- if (2 == restart_flag) {
- SettingsSaveAll();
- }
- restart_flag--;
- if (restart_flag <= 0) {
- AddLog_P(LOG_LEVEL_INFO, PSTR(D_LOG_APPLICATION D_RESTARTING));
- EspRestart();
- }
- }
- break;
- case 2: // Every x.5 second
- WifiCheck(wifi_state_flag);
- wifi_state_flag = WIFI_RESTART;
- break;
- case 3: // Every x.75 second
- if (!global_state.wifi_down) { MqttCheck(); }
- break;
- }
-}
-
-#ifdef USE_ARDUINO_OTA
-/*********************************************************************************************\
- * Allow updating via the Arduino OTA-protocol.
- *
- * - Once started disables current wifi clients and udp
- * - Perform restart when done to re-init wifi clients
-\*********************************************************************************************/
-
-bool arduino_ota_triggered = false;
-uint16_t arduino_ota_progress_dot_count = 0;
-
-void ArduinoOTAInit(void)
-{
- ArduinoOTA.setPort(8266);
- ArduinoOTA.setHostname(my_hostname);
- if (Settings.web_password[0] !=0) { ArduinoOTA.setPassword(Settings.web_password); }
-
- ArduinoOTA.onStart([]()
- {
- SettingsSave(1); // Free flash for OTA update
-#ifdef USE_WEBSERVER
- if (Settings.webserver) { StopWebserver(); }
-#endif // USE_WEBSERVER
-#ifdef USE_ARILUX_RF
- AriluxRfDisable(); // Prevent restart exception on Arilux Interrupt routine
-#endif // USE_ARILUX_RF
- if (Settings.flag.mqtt_enabled) {
- MqttDisconnect(); // SetOption3 - Enable MQTT
- }
- AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_UPLOAD "Arduino OTA " D_UPLOAD_STARTED));
- arduino_ota_triggered = true;
- arduino_ota_progress_dot_count = 0;
- delay(100); // Allow time for message xfer
- });
-
- ArduinoOTA.onProgress([](unsigned int progress, unsigned int total)
- {
- if ((LOG_LEVEL_DEBUG <= seriallog_level)) {
- arduino_ota_progress_dot_count++;
- Serial.printf(".");
- if (!(arduino_ota_progress_dot_count % 80)) { Serial.println(); }
- }
- });
-
- ArduinoOTA.onError([](ota_error_t error)
- {
- /*
- From ArduinoOTA.h:
- typedef enum { OTA_AUTH_ERROR, OTA_BEGIN_ERROR, OTA_CONNECT_ERROR, OTA_RECEIVE_ERROR, OTA_END_ERROR } ota_error_t;
- */
- char error_str[100];
-
- if ((LOG_LEVEL_DEBUG <= seriallog_level) && arduino_ota_progress_dot_count) { Serial.println(); }
- switch (error) {
- case OTA_BEGIN_ERROR: strncpy_P(error_str, PSTR(D_UPLOAD_ERR_2), sizeof(error_str)); break;
- case OTA_RECEIVE_ERROR: strncpy_P(error_str, PSTR(D_UPLOAD_ERR_5), sizeof(error_str)); break;
- case OTA_END_ERROR: strncpy_P(error_str, PSTR(D_UPLOAD_ERR_7), sizeof(error_str)); break;
- default:
- snprintf_P(error_str, sizeof(error_str), PSTR(D_UPLOAD_ERROR_CODE " %d"), error);
- }
- AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_UPLOAD "Arduino OTA %s. " D_RESTARTING), error_str);
- EspRestart();
- });
-
- ArduinoOTA.onEnd([]()
- {
- if ((LOG_LEVEL_DEBUG <= seriallog_level)) { Serial.println(); }
- AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_UPLOAD "Arduino OTA " D_SUCCESSFUL ". " D_RESTARTING));
- EspRestart();
- });
-
- ArduinoOTA.begin();
- AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_UPLOAD "Arduino OTA " D_ENABLED " " D_PORT " 8266"));
-}
-#endif // USE_ARDUINO_OTA
-
-/********************************************************************************************/
-
-void SerialInput(void)
-{
- while (Serial.available()) {
-// yield();
- delay(0);
- serial_in_byte = Serial.read();
-
-/*-------------------------------------------------------------------------------------------*\
- * Sonoff dual and ch4 19200 baud serial interface
-\*-------------------------------------------------------------------------------------------*/
- if ((SONOFF_DUAL == my_module_type) || (CH4 == my_module_type)) {
- serial_in_byte = ButtonSerial(serial_in_byte);
- }
-
-/*-------------------------------------------------------------------------------------------*/
-
- if (XdrvCall(FUNC_SERIAL)) {
- serial_in_byte_counter = 0;
- Serial.flush();
- return;
- }
-
-/*-------------------------------------------------------------------------------------------*/
-
- if (serial_in_byte > 127 && !Settings.flag.mqtt_serial_raw) { // Discard binary data above 127 if no raw reception allowed - CMND_SERIALSEND3
- serial_in_byte_counter = 0;
- Serial.flush();
- return;
- }
- if (!Settings.flag.mqtt_serial) { // SerialSend active - CMND_SERIALSEND and CMND_SERIALLOG
- if (isprint(serial_in_byte)) { // Any char between 32 and 127
- if (serial_in_byte_counter < INPUT_BUFFER_SIZE -1) { // Add char to string if it still fits
- serial_in_buffer[serial_in_byte_counter++] = serial_in_byte;
- } else {
- serial_in_byte_counter = 0;
- }
- }
- } else {
- if (serial_in_byte || Settings.flag.mqtt_serial_raw) { // Any char between 1 and 127 or any char (0 - 255) - CMND_SERIALSEND3
- if ((serial_in_byte_counter < INPUT_BUFFER_SIZE -1) && // Add char to string if it still fits and ...
- ((isprint(serial_in_byte) && (128 == Settings.serial_delimiter)) || // Any char between 32 and 127
- ((serial_in_byte != Settings.serial_delimiter) && (128 != Settings.serial_delimiter)) || // Any char between 1 and 127 and not being delimiter
- Settings.flag.mqtt_serial_raw)) { // Any char between 0 and 255 - CMND_SERIALSEND3
- serial_in_buffer[serial_in_byte_counter++] = serial_in_byte;
- serial_polling_window = millis();
- } else {
- serial_polling_window = 0; // Reception done - send mqtt
- break;
- }
- }
- }
-
-#ifdef USE_SONOFF_SC
-/*-------------------------------------------------------------------------------------------*\
- * Sonoff SC 19200 baud serial interface
-\*-------------------------------------------------------------------------------------------*/
- if (SONOFF_SC == my_module_type) {
- if (serial_in_byte == '\x1B') { // Sonoff SC status from ATMEGA328P
- serial_in_buffer[serial_in_byte_counter] = 0; // Serial data completed
- SonoffScSerialInput(serial_in_buffer);
- serial_in_byte_counter = 0;
- Serial.flush();
- return;
- }
- } else
-#endif // USE_SONOFF_SC
-/*-------------------------------------------------------------------------------------------*/
-
- if (!Settings.flag.mqtt_serial && (serial_in_byte == '\n')) { // CMND_SERIALSEND and CMND_SERIALLOG
- serial_in_buffer[serial_in_byte_counter] = 0; // Serial data completed
- seriallog_level = (Settings.seriallog_level < LOG_LEVEL_INFO) ? (uint8_t)LOG_LEVEL_INFO : Settings.seriallog_level;
- AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_COMMAND "%s"), serial_in_buffer);
- ExecuteCommand(serial_in_buffer, SRC_SERIAL);
- serial_in_byte_counter = 0;
- serial_polling_window = 0;
- Serial.flush();
- return;
- }
- }
-
- if (Settings.flag.mqtt_serial && serial_in_byte_counter && (millis() > (serial_polling_window + SERIAL_POLLING))) { // CMND_SERIALSEND and CMND_SERIALLOG
- serial_in_buffer[serial_in_byte_counter] = 0; // Serial data completed
- char hex_char[(serial_in_byte_counter * 2) + 2];
- Response_P(PSTR("{\"" D_JSON_SERIALRECEIVED "\":\"%s\"}"),
- (Settings.flag.mqtt_serial_raw) ? ToHex_P((unsigned char*)serial_in_buffer, serial_in_byte_counter, hex_char, sizeof(hex_char)) : serial_in_buffer);
- MqttPublishPrefixTopic_P(RESULT_OR_TELE, PSTR(D_JSON_SERIALRECEIVED));
- XdrvRulesProcess();
- serial_in_byte_counter = 0;
- }
-}
-
-/********************************************************************************************/
-
-void GpioInit(void)
-{
- uint32_t mpin;
-
- if (!ValidModule(Settings.module)) {
- uint32_t module = MODULE;
- if (!ValidModule(MODULE)) { module = SONOFF_BASIC; }
- Settings.module = module;
- Settings.last_module = module;
- }
- SetModuleType();
-
- if (Settings.module != Settings.last_module) {
- baudrate = APP_BAUDRATE;
- }
-
- for (uint32_t i = 0; i < sizeof(Settings.user_template.gp); i++) {
- if ((Settings.user_template.gp.io[i] >= GPIO_SENSOR_END) && (Settings.user_template.gp.io[i] < GPIO_USER)) {
- Settings.user_template.gp.io[i] = GPIO_USER; // Fix not supported sensor ids in template
- }
- }
-
- myio def_gp;
- ModuleGpios(&def_gp);
- for (uint32_t i = 0; i < sizeof(Settings.my_gp); i++) {
- if ((Settings.my_gp.io[i] >= GPIO_SENSOR_END) && (Settings.my_gp.io[i] < GPIO_USER)) {
- Settings.my_gp.io[i] = GPIO_NONE; // Fix not supported sensor ids in module
- }
- else if (Settings.my_gp.io[i] > GPIO_NONE) {
- my_module.io[i] = Settings.my_gp.io[i]; // Set User selected Module sensors
- }
- if ((def_gp.io[i] > GPIO_NONE) && (def_gp.io[i] < GPIO_USER)) {
- my_module.io[i] = def_gp.io[i]; // Force Template override
- }
- }
- if ((Settings.my_adc0 >= ADC0_END) && (Settings.my_adc0 < ADC0_USER)) {
- Settings.my_adc0 = ADC0_NONE; // Fix not supported sensor ids in module
- }
- else if (Settings.my_adc0 > ADC0_NONE) {
- my_adc0 = Settings.my_adc0; // Set User selected Module sensors
- }
- my_module_flag = ModuleFlag();
- uint32_t template_adc0 = my_module_flag.data &15;
- if ((template_adc0 > ADC0_NONE) && (template_adc0 < ADC0_USER)) {
- my_adc0 = template_adc0; // Force Template override
- }
-
- for (uint32_t i = 0; i < GPIO_MAX; i++) {
- pin[i] = 99;
- }
- for (uint32_t i = 0; i < sizeof(my_module.io); i++) {
- mpin = ValidPin(i, my_module.io[i]);
-
- DEBUG_CORE_LOG(PSTR("INI: gpio pin %d, mpin %d"), i, mpin);
-
- if (mpin) {
- XdrvMailbox.index = mpin;
- XdrvMailbox.payload = i;
-
- if ((mpin >= GPIO_SWT1_NP) && (mpin < (GPIO_SWT1_NP + MAX_SWITCHES))) {
- SwitchPullupFlag(mpin - GPIO_SWT1_NP);
- mpin -= (GPIO_SWT1_NP - GPIO_SWT1);
- }
- else if ((mpin >= GPIO_KEY1_NP) && (mpin < (GPIO_KEY1_NP + MAX_KEYS))) {
- ButtonPullupFlag(mpin - GPIO_KEY1_NP); // 0 .. 3
- mpin -= (GPIO_KEY1_NP - GPIO_KEY1);
- }
- else if ((mpin >= GPIO_KEY1_INV) && (mpin < (GPIO_KEY1_INV + MAX_KEYS))) {
- ButtonInvertFlag(mpin - GPIO_KEY1_INV); // 0 .. 3
- mpin -= (GPIO_KEY1_INV - GPIO_KEY1);
- }
- else if ((mpin >= GPIO_KEY1_INV_NP) && (mpin < (GPIO_KEY1_INV_NP + MAX_KEYS))) {
- ButtonPullupFlag(mpin - GPIO_KEY1_INV_NP); // 0 .. 3
- ButtonInvertFlag(mpin - GPIO_KEY1_INV_NP); // 0 .. 3
- mpin -= (GPIO_KEY1_INV_NP - GPIO_KEY1);
- }
- else if ((mpin >= GPIO_REL1_INV) && (mpin < (GPIO_REL1_INV + MAX_RELAYS))) {
- bitSet(rel_inverted, mpin - GPIO_REL1_INV);
- mpin -= (GPIO_REL1_INV - GPIO_REL1);
- }
- else if ((mpin >= GPIO_LED1_INV) && (mpin < (GPIO_LED1_INV + MAX_LEDS))) {
- bitSet(led_inverted, mpin - GPIO_LED1_INV);
- mpin -= (GPIO_LED1_INV - GPIO_LED1);
- }
- else if (mpin == GPIO_LEDLNK_INV) {
- ledlnk_inverted = 1;
- mpin -= (GPIO_LEDLNK_INV - GPIO_LEDLNK);
- }
- else if ((mpin >= GPIO_PWM1_INV) && (mpin < (GPIO_PWM1_INV + MAX_PWMS))) {
- bitSet(pwm_inverted, mpin - GPIO_PWM1_INV);
- mpin -= (GPIO_PWM1_INV - GPIO_PWM1);
- }
- else if (XdrvCall(FUNC_PIN_STATE)) {
- mpin = XdrvMailbox.index;
- }
- else if (XsnsCall(FUNC_PIN_STATE)) {
- mpin = XdrvMailbox.index;
- };
- }
- if (mpin) pin[mpin] = i;
- }
-
- if ((2 == pin[GPIO_TXD]) || (H801 == my_module_type)) { Serial.set_tx(2); }
-
- analogWriteRange(Settings.pwm_range); // Default is 1023 (Arduino.h)
- analogWriteFreq(Settings.pwm_frequency); // Default is 1000 (core_esp8266_wiring_pwm.c)
-
-#ifdef USE_SPI
- spi_flg = ((((pin[GPIO_SPI_CS] < 99) && (pin[GPIO_SPI_CS] > 14)) || (pin[GPIO_SPI_CS] < 12)) || (((pin[GPIO_SPI_DC] < 99) && (pin[GPIO_SPI_DC] > 14)) || (pin[GPIO_SPI_DC] < 12)));
- if (spi_flg) {
- for (uint32_t i = 0; i < GPIO_MAX; i++) {
- if ((pin[i] >= 12) && (pin[i] <=14)) pin[i] = 99;
- }
- my_module.io[12] = GPIO_SPI_MISO;
- pin[GPIO_SPI_MISO] = 12;
- my_module.io[13] = GPIO_SPI_MOSI;
- pin[GPIO_SPI_MOSI] = 13;
- my_module.io[14] = GPIO_SPI_CLK;
- pin[GPIO_SPI_CLK] = 14;
- }
- soft_spi_flg = ((pin[GPIO_SSPI_CS] < 99) && (pin[GPIO_SSPI_SCLK] < 99) && ((pin[GPIO_SSPI_MOSI] < 99) || (pin[GPIO_SSPI_MOSI] < 99)));
-#endif // USE_SPI
-
-#ifdef USE_I2C
- i2c_flg = ((pin[GPIO_I2C_SCL] < 99) && (pin[GPIO_I2C_SDA] < 99));
- if (i2c_flg) {
- Wire.begin(pin[GPIO_I2C_SDA], pin[GPIO_I2C_SCL]);
- }
-#endif // USE_I2C
-
- devices_present = 0;
- light_type = LT_BASIC; // Use basic PWM control if SetOption15 = 0
- if (XdrvCall(FUNC_MODULE_INIT)) {
- // Serviced
- }
- else if (YTF_IR_BRIDGE == my_module_type) {
- ClaimSerial(); // Stop serial loopback mode
-// devices_present = 1;
- }
- else if (SONOFF_DUAL == my_module_type) {
- Settings.flag.mqtt_serial = 0; // CMND_SERIALSEND and CMND_SERIALLOG
- devices_present = 2;
- baudrate = 19200;
- }
- else if (CH4 == my_module_type) {
- Settings.flag.mqtt_serial = 0; // CMND_SERIALSEND and CMND_SERIALLOG
- devices_present = 4;
- baudrate = 19200;
- }
-#ifdef USE_SONOFF_SC
- else if (SONOFF_SC == my_module_type) {
- Settings.flag.mqtt_serial = 0; // CMND_SERIALSEND and CMND_SERIALLOG
- devices_present = 0;
- baudrate = 19200;
- }
-#endif // USE_SONOFF_SC
-
- if (!light_type) {
- for (uint32_t i = 0; i < MAX_PWMS; i++) { // Basic PWM control only
- if (pin[GPIO_PWM1 +i] < 99) {
- pwm_present = true;
- pinMode(pin[GPIO_PWM1 +i], OUTPUT);
- analogWrite(pin[GPIO_PWM1 +i], bitRead(pwm_inverted, i) ? Settings.pwm_range - Settings.pwm_value[i] : Settings.pwm_value[i]);
- }
- }
- }
- for (uint32_t i = 0; i < MAX_RELAYS; i++) {
- if (pin[GPIO_REL1 +i] < 99) {
- pinMode(pin[GPIO_REL1 +i], OUTPUT);
- devices_present++;
- if (EXS_RELAY == my_module_type) {
- digitalWrite(pin[GPIO_REL1 +i], bitRead(rel_inverted, i) ? 1 : 0);
- if (i &1) { devices_present--; }
- }
- }
- }
-
- for (uint32_t i = 0; i < MAX_LEDS; i++) {
- if (pin[GPIO_LED1 +i] < 99) {
-#ifdef USE_ARILUX_RF
- if ((3 == i) && (leds_present < 2) && (99 == pin[GPIO_ARIRFSEL])) {
- pin[GPIO_ARIRFSEL] = pin[GPIO_LED4]; // Legacy support where LED4 was Arilux RF enable
- pin[GPIO_LED4] = 99;
- } else {
-#endif
- pinMode(pin[GPIO_LED1 +i], OUTPUT);
- leds_present++;
- digitalWrite(pin[GPIO_LED1 +i], bitRead(led_inverted, i));
-#ifdef USE_ARILUX_RF
- }
-#endif
- }
- }
- if (pin[GPIO_LEDLNK] < 99) {
- pinMode(pin[GPIO_LEDLNK], OUTPUT);
- digitalWrite(pin[GPIO_LEDLNK], ledlnk_inverted);
- }
-
- ButtonInit();
- SwitchInit();
-#ifdef ROTARY_V1
- RotaryInit();
-#endif
-
- SetLedPower(Settings.ledstate &8);
- SetLedLink(Settings.ledstate &8);
-
- XdrvCall(FUNC_PRE_INIT);
-}
void setup(void)
{
@@ -1591,6 +276,7 @@ void setup(void)
snprintf_P(my_hostname, sizeof(my_hostname)-1, Settings.hostname);
}
+ GetEspHardwareType();
GpioInit();
SetSerialBaudrate(baudrate);
diff --git a/tasmota/tasmota_version.h b/tasmota/tasmota_version.h
index eac271bd0..f8eee8abd 100644
--- a/tasmota/tasmota_version.h
+++ b/tasmota/tasmota_version.h
@@ -20,6 +20,6 @@
#ifndef _TASMOTA_VERSION_H_
#define _TASMOTA_VERSION_H_
-const uint32_t VERSION = 0x07010100;
+const uint32_t VERSION = 0x07010200;
#endif // _TASMOTA_VERSION_H_
diff --git a/tasmota/xdrv_04_light.ino b/tasmota/xdrv_04_light.ino
index b50d8e4df..7f1e66fc6 100644
--- a/tasmota/xdrv_04_light.ino
+++ b/tasmota/xdrv_04_light.ino
@@ -1510,33 +1510,6 @@ void LightPreparePower(power_t channels = 0xFFFFFFFF) { // 1 = only RGB, 2 =
LightState(0);
}
-void LightWheel(uint8_t wheel_pos)
-{
- wheel_pos = 255 - wheel_pos;
- if (wheel_pos < 85) {
- Light.entry_color[0] = 255 - wheel_pos * 3;
- Light.entry_color[1] = 0;
- Light.entry_color[2] = wheel_pos * 3;
- } else if (wheel_pos < 170) {
- wheel_pos -= 85;
- Light.entry_color[0] = 0;
- Light.entry_color[1] = wheel_pos * 3;
- Light.entry_color[2] = 255 - wheel_pos * 3;
- } else {
- wheel_pos -= 170;
- Light.entry_color[0] = wheel_pos * 3;
- Light.entry_color[1] = 255 - wheel_pos * 3;
- Light.entry_color[2] = 0;
- }
- Light.entry_color[3] = 0;
- Light.entry_color[4] = 0;
- float dimmer = 100 / (float)Settings.light_dimmer;
- for (uint32_t i = 0; i < LST_RGB; i++) {
- float temp = (float)Light.entry_color[i] / dimmer + 0.5f;
- Light.entry_color[i] = (uint8_t)temp;
- }
-}
-
void LightCycleColor(int8_t direction)
{
if (Light.strip_timer_counter % (Settings.light_speed * 2)) {
@@ -1547,12 +1520,17 @@ void LightCycleColor(int8_t direction)
if (Light.random == Light.wheel) {
Light.random = random(255);
}
- Light.wheel += (Light.random < Light.wheel) ? -1 : 1;
- } else {
- Light.wheel += direction;
+ direction = (Light.random < Light.wheel) ? -1 : 1;
}
- LightWheel(Light.wheel);
- memcpy(Light.new_color, Light.entry_color, sizeof(Light.new_color));
+ Light.wheel += direction;
+ uint16_t hue = changeUIntScale(Light.wheel, 0, 255, 0, 359); // Scale to hue to keep amount of steps low (max 255 instead of 359)
+
+// AddLog_P2(LOG_LEVEL_DEBUG, PSTR("DBG: random %d, wheel %d, hue %d"), Light.random, Light.wheel, hue);
+
+ uint8_t sat;
+ light_state.getHSB(nullptr, &sat, nullptr); // Allow user control over Saturation
+ light_state.setHS(hue, sat);
+ light_controller.calcLevels(Light.new_color);
}
void LightSetPower(void)
@@ -1593,6 +1571,7 @@ void LightAnimate(void)
{
uint8_t cur_col[LST_MAX];
uint16_t light_still_on = 0;
+ bool power_off = false;
Light.strip_timer_counter++;
if (!Light.power) { // All channels powered off
@@ -1600,6 +1579,9 @@ void LightAnimate(void)
if (!Light.fade_running) {
sleep = Settings.sleep;
}
+ if (Settings.light_scheme >= LS_MAX) {
+ power_off = true;
+ }
} else {
if (Settings.sleep > PWM_MAX_SLEEP) {
sleep = PWM_MAX_SLEEP; // set a maxumum value of 50 milliseconds to ensure that animations are smooth
@@ -1651,7 +1633,7 @@ void LightAnimate(void)
}
}
- if (Settings.light_scheme < LS_MAX) { // exclude WS281X Neopixel
+ if ((Settings.light_scheme < LS_MAX) || power_off) { // exclude WS281X Neopixel schemes
// Apply power modifiers to Light.new_color
LightApplyPower(Light.new_color, Light.power);
@@ -1736,7 +1718,7 @@ void LightAnimate(void)
cur_col_10bits[i] = orig_col_10bits[Light.color_remap[i]];
}
- if (!Settings.light_fade) { // no fade
+ if (!Settings.light_fade || power_off) { // no fade
// record the current value for a future Fade
memcpy(Light.fade_start_8, cur_col, sizeof(Light.fade_start_8));
memcpy(Light.fade_start_10, cur_col_10bits, sizeof(Light.fade_start_10));
diff --git a/tasmota/xdrv_23_zigbee_9_impl.ino b/tasmota/xdrv_23_zigbee_9_impl.ino
index 1cd3955ce..cb08eeaee 100644
--- a/tasmota/xdrv_23_zigbee_9_impl.ino
+++ b/tasmota/xdrv_23_zigbee_9_impl.ino
@@ -25,16 +25,8 @@ const uint32_t ZIGBEE_BUFFER_SIZE = 256; // Max ZNP frame is SOF+LEN+CMD1+CMD2+
const uint8_t ZIGBEE_SOF = 0xFE;
const uint8_t ZIGBEE_SOF_ALT = 0xFF;
-//#define Z_USE_SOFTWARE_SERIAL
-
-#ifdef Z_USE_SOFTWARE_SERIAL
-#include
-SoftwareSerial *ZigbeeSerial = nullptr;
-#else
#include
TasmotaSerial *ZigbeeSerial = nullptr;
-#endif
-
const char kZigbeeCommands[] PROGMEM = "|"
D_CMND_ZIGBEEZNPSEND "|" D_CMND_ZIGBEE_PERMITJOIN "|"
@@ -185,9 +177,7 @@ void ZigbeeInput(void)
char hex_char[(zigbee_buffer->len() * 2) + 2];
ToHex_P((unsigned char*)zigbee_buffer->getBuffer(), zigbee_buffer->len(), hex_char, sizeof(hex_char));
-#ifndef Z_USE_SOFTWARE_SERIAL
AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR(D_LOG_ZIGBEE "Bytes follow_read_metric = %0d"), ZigbeeSerial->getLoopReadMetric());
-#endif
// buffer received, now check integrity
if (zigbee_buffer->len() != zigbee_frame_len) {
// Len is not correct, log and reject frame
@@ -224,21 +214,16 @@ void ZigbeeInit(void)
zigbee.active = false;
if ((pin[GPIO_ZIGBEE_RX] < 99) && (pin[GPIO_ZIGBEE_TX] < 99)) {
AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("Zigbee: GPIOs Rx:%d Tx:%d"), pin[GPIO_ZIGBEE_RX], pin[GPIO_ZIGBEE_TX]);
-#ifdef Z_USE_SOFTWARE_SERIAL
- ZigbeeSerial = new SoftwareSerial();
- ZigbeeSerial->begin(115200, pin[GPIO_ZIGBEE_RX], pin[GPIO_ZIGBEE_TX], SWSERIAL_8N1, false, 256); // ZNP is 115200, RTS/CTS (ignored), 8N1
- ZigbeeSerial->enableIntTx(false);
- zigbee_buffer = new SBuffer(ZIGBEE_BUFFER_SIZE);
-#else
- ZigbeeSerial = new TasmotaSerial(pin[GPIO_ZIGBEE_RX], pin[GPIO_ZIGBEE_TX], 0, 0, 256); // set a receive buffer of 256 bytes
+ // if seriallog_level is 0, we allow GPIO 13/15 to switch to Hardware Serial
+ ZigbeeSerial = new TasmotaSerial(pin[GPIO_ZIGBEE_RX], pin[GPIO_ZIGBEE_TX], seriallog_level ? 1 : 2, 0, 256); // set a receive buffer of 256 bytes
ZigbeeSerial->begin(115200);
if (ZigbeeSerial->hardwareSerial()) {
ClaimSerial();
- zigbee_buffer = new PreAllocatedSBuffer(sizeof(serial_in_buffer), serial_in_buffer);
+ uint32_t aligned_buffer = ((uint32_t)serial_in_buffer + 3) & ~3;
+ zigbee_buffer = new PreAllocatedSBuffer(sizeof(serial_in_buffer) - 3, (char*) aligned_buffer);
} else {
zigbee_buffer = new SBuffer(ZIGBEE_BUFFER_SIZE);
}
-#endif
zigbee.active = true;
zigbee.init_phase = true; // start the state machine
zigbee.state_machine = true; // start the state machine
diff --git a/tools/fw_efm8bb1/RF-Bridge-EFM8BB1-20191006.hex b/tools/fw_efm8bb1/RF-Bridge-EFM8BB1-20191006.hex
deleted file mode 100644
index 1575f3ec7..000000000
--- a/tools/fw_efm8bb1/RF-Bridge-EFM8BB1-20191006.hex
+++ /dev/null
@@ -1,491 +0,0 @@
-:020000040000FA
-:100000000210D8A2029290A2029280AD40AC3F7F33
-:100010000A7E0012002E12001DA202B322E59120DA
-:10002000E2FB220210487597A5222202141B8E4182
-:100030008F428C438D4412163112188EE54424BF32
-:100040009000E8F0E54334FF9000E7F09000E3E52E
-:1000500041F0A3E542F043910422220211671219F4
-:100060002253D87853DAFE1218F3E4900087F02276
-:10007000D2DE2202146E1217C0C290C296C280E471
-:10008000FBFD7F101218DA12053174A4F0D2AF1202
-:10009000170AD2969000ECE004F070069000EBE0B6
-:1000A00004F09000EBE0B427E9A3E0B410E4C296BA
-:1000B0001200263003091216B18E228F2380067596
-:1000C0002201752300E5237004E522640170469047
-:1000D00000DEE0700612170B0202959000EBE4754B
-:1000E000F001120807FED3E5F09410EE94274002C9
-:1000F000D296D39000ECE094309000EBE0947550F1
-:1001000003020295E4F0A3F09000DEF0900099F075
-:10011000C29602029512170A9000DEE014602A14BB
-:1001200070030202591470030202171470030202D2
-:100130003024046003020295E52364AA60030202EE
-:10014000959000DE04F0020295E523900099F0906E
-:1001500000DE7402F0E52312092F0201A0017FA145
-:10016000018BA501A9A601BDA701C6A801DDA901B2
-:10017000CCB001D5B1019DC00295FF0000020C1268
-:10018000051EE490008AF07FA1806512054E900064
-:10019000EA7408F0E4F52575240902029512054E6B
-:1001A000E4F52575240202029590008A7401F07F1F
-:1001B000A61215A690007974A6F0020295120531D8
-:1001C00074A4F00202959000EA7408F09000DE74C6
-:1001D00003F00202957FB112056202029512051E1C
-:1001E00090008AE09000E9F090008A7401F07FA905
-:1001F0001205627D307C757F017E00121797020226
-:100200009512056B900079EFF0E48005E490009979
-:10021000F09000DEF0807EE4F525E523F524E5246A
-:10022000D39400402C12005E9000DE7404F08065D0
-:1002300074032525F582E43400F583E523F00525D4
-:10024000E525B52402800AE525C39470404775244E
-:10025000709000DE7402F0803CE5236455703690A7
-:1002600000DEF0C203900099E02460601B24FC6073
-:100270001224FE600E14600B24F760101460042436
-:100280001070127FA0121875D2038009900004E04C
-:10029000049000EAF0900099E012092F02BEA10339
-:1002A00033A40377A50333A60447A802BEA9047CA0
-:1002B000B004E5B1041EC00442FF000000B090008D
-:1002C00086E030E73F7DC87C0012053F900099E052
-:1002D0002457600E2408701F12056B7FA31215ECC3
-:1002E00080159000E9E090008AF0900079E0FF121C
-:1002F00015A67FAB12057CE4900086F043DA01D2AC
-:10030000030200B01218EB50207DE87C0312053F79
-:10031000900099E02457600C240860030200B07F2D
-:10032000A20204D77FAA0204D71205744003020078
-:10033000B0802E900086E030E71F900099E0245AAC
-:10034000600F240260030204F17FA41215EC020482
-:10035000F17FA612057C0204F1120574400302002D
-:10036000B01216E7C006C0071216F69200D007D0EA
-:10037000061209810200B09000DEE060030200B0C6
-:100380001217127003020411240560030200B01258
-:100390000558900005E0FFA3E090008BCFF0A3EF9D
-:1003A000F0900007E0FFA3E090008DCFF0A3EFF006
-:1003B000900003E0FFA3E090008FCFF0A3EFF07B6D
-:1003C000017A00798B901A0EE493FC740193FD90EE
-:1003D0001A10E493F52E1217028E2FF530901A138F
-:1003E000E493F5311217028E32F533901A16E49326
-:1003F000F5341217028E35F536901A19E493F53755
-:10040000A3E493F538753900753A09120EBC020061
-:10041000B09000EAE07063C2807FA00204D7900031
-:10042000DEE060030200B0900003E0FCA3E0FD7F8B
-:10043000017E00121797D29612001DC2967FA0026D
-:1004400004D77F030204D79000DEE060030200B00F
-:100450001217126019240560030200B012055890AB
-:100460000003E0FF7C007D041213570200B09000EF
-:10047000EAE07006C2807FA0805D80619000DEE0CF
-:1004800060030200B01217126043240560030200EB
-:10049000B09000EAE014F012005E7E007F05C00616
-:1004A000C007900003E0FB25E0FFE433FE74052F56
-:1004B000F58274003EAD82FCEB25E0FFE52424FECE
-:1004C000C39FFBD007D006120FA70200B09000EA2E
-:1004D000E0700AC2807FA01218680200B0E49000A9
-:1004E00087F00200B0900086E030E7107FB1121173
-:1004F000ECE4900086F043DA010200B07E007FED6C
-:1005000012151040030200B01216E7C006C0071211
-:1005100016F69200D007D006120D160200B07D32FA
-:100520007C007F017E00121797D29612001DC296A2
-:1005300022E490008AF07FA41215A6900079227F11
-:10054000017E00121797D29612001DC29622120049
-:100550005E9000DE7404F0229000EAE014F01200D5
-:100560005E221215A6900079EFF022900079E0FF4C
-:100570001215A6227E007FED12151022900086E053
-:10058000547FFD12155B228F29E4F536EF25E02517
-:10059000E02468F8E6701EEF25E025E02469F8768F
-:1005A000087E007F707D007B017A00790312095577
-:1005B000E490007AF0AB2CAA2DA92EC001120B6793
-:1005C0002466F9E7540F120CA3120B86D001121403
-:1005D000C1503D120B672466F9E7540FFF120CA3BC
-:1005E000120C90300101B34031EF700A900077E5B2
-:1005F0002AF0A3E52BF0120B67120CAF2401FFEFDA
-:10060000FEEC54F04EFEEDFF120B67120C9A800ABE
-:10061000120B672466F874F056F6AB2CAA2DA92E9F
-:10062000C001120B67120CBB120BD1120CC7120BBC
-:1006300086D0011214C15052120B67120CBB120B60
-:10064000D1FF120CC75408FE120C92300101B340C6
-:1006500043EF700A900088E52AF0A3E52BF0120B17
-:1006600067120BC22401FFE433FEEFC4F8540FC835
-:1006700068FFEEC454F048FEED540FFDEC4EFEED65
-:100680004FFF120B67120C9A800A120B672467F84F
-:10069000740F56F6120B67120CAFFB700F120BCAD9
-:1006A000700A120B4D2469F8E4F6C322120B67247A
-:1006B00068F8E53514667003753601C3E531953683
-:1006C0006B7020D290120B672466F874F056F674A3
-:1006D0000F0856120B662468F806120B672469F897
-:1006E00016804C120B67120BC2FFC3E53495366FB0
-:1006F000703DD290120B672466F8EC54F0FEED5476
-:100700000FA60608120B662468F806120B67246908
-:10071000F816120B67120BD8E0FF120B672469F86A
-:10072000E6FE7401A806088002C333D8FC4FF0121D
-:100730000B672469F8E67017120B67120BD8120CBE
-:100740004CFF12182DEFF0120B672469F87608128F
-:100750000B672468F8E6C3953540311218E350055D
-:10076000E4900085F0120C4960181218F37D207C8B
-:10077000037F017E0012176E120CD3A3E529F0440B
-:1007800080F0120B4D2469F8E4F6D322C322BB019A
-:100790000689828A83F0225002F722BBFE01F322EF
-:1007A000EF8DF0A4A8F0CF8CF0A428CE8DF0A42E6D
-:1007B000FE22BC000BBE0029EF8DF084FFADF022BD
-:1007C000E4CCF875F008EF2FFFEE33FEEC33FCEECF
-:1007D0009DEC984005FCEE9DFE0FD5F0E9E4CEFDC2
-:1007E00022EDF8F5F0EE8420D21CFEADF075F00895
-:1007F000EF2FFFED33FD4007985006D5F0F222C3EE
-:1008000098FD0FD5F0EA22C5F0F8A3E028F0C5F076
-:10081000F8E582158270021583E038F022BB0110E2
-:10082000E58229F582E5833AF583E0F5F0A3E0223D
-:100830005009E92582F886F008E622BBFE0AE92580
-:1008400082F8E2F5F008E222E5832AF583E993F5E0
-:10085000F0A3E9932275F008758200EF2FFFEE33C5
-:10086000FECD33CDCC33CCC58233C5829BED9AEC23
-:1008700099E58298400CF582EE9BFEED9AFDEC998D
-:10088000FC0FD5F0D6E4CEFBE4CDFAE4CCF9A88297
-:1008900022B800C1B90059BA002DEC8BF084CFCE3C
-:1008A000CDFCE5F0CBF97818EF2FFFEE33FEED33FA
-:1008B000FDEC33FCEB33FB10D703994004EB99FBC1
-:1008C0000FD8E5E4F9FA227818EF2FFFEE33FEEDAA
-:1008D00033FDEC33FCC933C910D7059BE99A4007B7
-:1008E000EC9BFCE99AF90FD8E0E4C9FAE4CCFB22CE
-:1008F00075F010EF2FFFEE33FEED33FDCC33CCC897
-:1009000033C810D7079BEC9AE899400AED9BFDECA1
-:100910009AFCE899F80FD5F0DAE4CDFBE4CCFAE4E0
-:10092000C8F922A42582F582E5F03583F58322D02B
-:1009300083D082F8E4937012740193700DA3A39393
-:10094000F8740193F5828883E4737402936860EF0E
-:10095000A3A3A380DFEF4E6012EF60010EEDBB0199
-:100960000B89828A83F0A3DFFCDEFA2289F050072C
-:10097000F709DFFCA9F022BBFEFCF309DFFCA9F0BC
-:10098000228E268F27E4F528C3E5279464E5269474
-:10099000005017E4F528E528120B922469F8E4F6D4
-:1009A0000528E528B40FEFC2902290008AE0147069
-:1009B00003020A6F046003020B4C7866E6C4540F0E
-:1009C000F97065D3E5279494E52694115003020B42
-:1009D0004C300003020B4CE9120C32E6540FFC08B9
-:1009E000E6FDEC4E18F6ED08F618120C29EC540F43
-:1009F0004E18F6ED08F6900001E526F0A3E527F085
-:100A0000AE26FF7C007D1F1207B290008BEEF0A394
-:100A1000EFF07C007D031207A0A3EEF0A3EFF0A39C
-:100A2000E526F0A3E527F0227866E6C4540F6402B9
-:100A30006003020B4C120CEB752C01752D00752E0A
-:100A40008B901BD793FE7401938E2FF530901BD99A
-:100A5000E493F531A3120BBB8E32F533901BDCE42B
-:100A600093F534901BE0E493F535E4FF020587E449
-:100A7000F528120BA8120D0CFFE528120C54F9EF03
-:100A8000C399506EE528120C16F5828C832FF582DF
-:100A9000E43583F583E493FF120C90300001B350EA
-:100AA00003020B40E528C454F024D1F582E4341B42
-:100AB000120C20F5828C83EF540775F00212092383
-:100AC000120C22FDAF27AE261214CBE528501925B3
-:100AD000E025E02466F8120C29EC540F4EFEEDFFE1
-:100AE000120BA8120C9A8058120B922469F8E4F6A3
-:100AF000804E120BA8120D0C697045120CEBE52804
-:100B0000120BAFAA06752CFF8A2DF52EE528120CC4
-:100B1000F6120BB98E2FF530E528120C63F531E58E
-:100B200028120D01120BB98E32F533E528120C7222
-:100B3000F534E528120C81F535AF28120587400CF5
-:100B40000528E528C3940F5003020A7222C290E5DB
-:100B50002925E025E02466F8E4F608F6E52925E0F5
-:100B600025E02468F8E4F6E52925E025E022F58370
-:100B7000E493FF5408FE131313541F24FF9202AB97
-:100B800029AA2AA92BEF540775F002A4F58285F053
-:100B9000832225E025E02466F8E4F608F6E528251A
-:100BA000E025E02468F8E4F6E52825E025E022C405
-:100BB00054F024D1F582E4341BF583E493FE7401F0
-:100BC00093222466F8E6FC08E6FDECC4F854F0C86D
-:100BD000EDC4540F48540F222468F8E6141313137D
-:100BE000541F2403F582E43400F58322E529252BE4
-:100BF000F582E43528F583E022A200E433C43333E0
-:100C00003354804526FFE527900074CFF0A3EFF022
-:100C1000E490007BF022C454F024D4F582E4341B29
-:100C2000F583E493FC74019322E6FC08E6FDECC432
-:100C3000540F2401FFEFC454F0FE22E52E25E024DA
-:100C40008BF582E43400F58322900085E0FF90006C
-:100C50007AE06F22C454F024D6F582E4341BF58385
-:100C6000E49322C454F024D9F582E4341BF583E4E0
-:100C70009322C454F024DCF582E4341BF583E4931E
-:100C800022C454F024E0F582E4341BF583E493227B
-:100C90005408131313541F24FF222466F8A60608D1
-:100CA000A607222530F582E4352FF583E49322242C
-:100CB00066F8E6FC08E6FDEC540F222466F9E7C46A
-:100CC000F854F0C809E7222533F582E43532F5837C
-:100CD000E4932290007AE0900085F053DAFE22251A
-:100CE000E0247DF582E43400F58322A200920185A0
-:100CF000262A85272B22C454F024D7F582E4341BFE
-:100D000022C454F024DAF582E4341B222466F8E687
-:100D1000FEEEC4540F228E268F27C3E5279464E588
-:100D20002694005003020EAA900087E024FE60255E
-:100D3000147003020DB724036003020EAFC290AF1C
-:100D400027AE2612185A4003020EAF120BF990007C
-:100D5000877402F022120EB0503090007BE09404B1
-:100D60004021D290E4900000F0900073F0C2049013
-:100D7000007CF090007AF0900003F09000877403FC
-:100D8000F08025E4900087F0801E900074E0547F8E
-:100D9000FEA3E0FFD3E5279FE5269E4005120BF951
-:100DA000800690007BE004F090007BE0C394E0506C
-:100DB00003020EAF020EAA90007CE004F090007BCC
-:100DC000E0FFA3E0D39F4003020E587B007A007936
-:100DD00028AF27AE261212DF4022900000E0FF125B
-:100DE0000C3DE526F0A3E527F08F28900000E004F5
-:100DF000F0E0D394074005E4900087F030041DA292
-:100E000000E433C43333335480FFE528C454F04F37
-:100E1000FF900073E0120BE2EFF08039900073E076
-:100E2000FF120BE2E0FEA200E43333333354F84503
-:100E300028FDEE4DF074032F120BE4120C4CFF1240
-:100E4000182DEFF0900073E004F0E0D394704005AB
-:100E5000E4900087F0B20422120EB0504D1218E355
-:100E60005005E4900085F0120C49603A1218F37DA9
-:100E7000207C037F017E0012176E120CD3900003BA
-:100E8000E0FD900074E05480FF900000E0C454F056
-:100E90004FFFED4F900003F0900074E0547FF0900E
-:100EA0000086E04480F0C2908000E4900087F02249
-:100EB000A2009201AF27AE26121744228B298A2A5C
-:100EC000892B8C2C8D2DE4F53D753E80F53BE53B63
-:100ED000C3952E5010E52D253BF582E4352C120FDD
-:100EE0006C053B80E9E4F53BE53BC395385057E59D
-:100EF0003A253DF582E43539F583E0553E7019F524
-:100F00003CE53CC39531502DE530253CF582E43578
-:100F10002F120F6C053C80E9E4F53CE53CC39534A9
-:100F20005013E533253CF582E43532120B6E120F77
-:100F30009B053C80E6E53EC313F53E7005053D7517
-:100F40003E80053B80A2E4F53BE53BC3953750135B
-:100F5000E536253BF582E43535120B6E120F9B0505
-:100F60003B80E6C2909000877405F022F583E493FD
-:100F7000FF5408FE131313541F24FF9202AB29AA37
-:100F80002AA92BEF540775F002A4F58285F083128D
-:100F9000081DF54085F03F1200032212081DF540A0
-:100FA00085F03F120003228E268F278C288D298BF7
-:100FB0002AD200C201852982852883E05488D394EF
-:100FC000004003D38001C39201E4F52BE52BC395C8
-:100FD0002A505930010D120BECC413131354012481
-:100FE000FF8002A2009202852782852683C083C0EB
-:100FF00082120BECFFC45407D082D083121035301C
-:10100000010C120BECFF131313541F138002A200E8
-:101010009202852782852683C083C082120BEC54FE
-:1010200007D082D083121035052B80A0C29090008B
-:10103000877405F02275F002120923E0F53FA3E062
-:10104000F540120003920022C0E0C0F0C083C082CD
-:10105000C0D075D000C000C001C002C003C004C031
-:1010600005C006C007E5985403F545F45298E545D8
-:1010700030E01712192B9000DD121739EFF09000B5
-:10108000DDE004F0E0B44002E4F0E54530E12E900C
-:1010900000E0E0D39400401A9000DCE02446F8E63B
-:1010A000FF1219289000DCE004F09000E0E014F05A
-:1010B0008002D2059000DCE0B42002E4F0D007D03A
-:1010C00006D005D004D003D002D001D000D0D0D0BB
-:1010D00082D083D0F0D0E03212005A787FE4F6D884
-:1010E000FD7581A1021122020076E493A3F8E49336
-:1010F000A34003F68001F208DFF48029E493A3F80B
-:101100005407240CC8C333C4540F4420C88340047C
-:10111000F456800146F6DFE4800B0102040810203B
-:101120004080901266E47E019360BCA3FF543F3080
-:10113000E509541FFEE493A360010ECF54C025E0DF
-:1011400060A840B8E493A3FAE493A3F8E493A3C897
-:10115000C582C8CAC583CAF0A3C8C582C8CAC58328
-:10116000CADFE9DEE780BEC0E0C0F0C083C082C055
-:10117000D075D000C000C001C002C003C004C005CB
-:10118000C006C007E5D85487F521F452D8E5F730FA
-:10119000E508E5F730E60312193253F7DFE52130B1
-:1011A000E708E5D930E003121931E52130E008E520
-:1011B000DA30E003121675E52130E108E5DB30E0B6
-:1011C00003121933E52130E208E5DC30E00312199F
-:1011D00034D007D006D005D004D003D002D001D03F
-:1011E00000D0D0D082D083D0F0D0E032AE07E4F58A
-:1011F0002612180A900000E004FF12181112125F64
-:10120000900000E0FFE526C39F501412172BE05416
-:101210007FFF12181112172B121725052680E19057
-:101220000074E0547FFF12181190007412172512F9
-:10123000125FE4F526900073E0FFE526C39F501788
-:10124000740325261217190526E526541F70E61289
-:10125000192512191E80DE7F551218110219251248
-:10126000192512191E224200E500004200E100008B
-:101270004200E700004200E30000C1834100860015
-:101280004100870041008A004100790042000100CE
-:101290000042008800004200770000C10441007352
-:1012A000004100850041007A004100000048007DB7
-:1012B0000000000000000000410076004100DD0059
-:1012C0004100DF004100DB004100DC004100E000A4
-:1012D0004100DA00C1054100DE0041009900008EA6
-:1012E000298F2A8B2B8A2C892DE4F52E900000E083
-:1012F000FFE52EC39F505EE52AAE297803CEC313C7
-:10130000CE13D8F9FDAC06E52AAE297802CEC31378
-:10131000CE13D8F92DFFEE3CAB07FA120C3BE0FEE2
-:10132000A3E0FFC39BEE9A50028004AE02AF03AA73
-:1013300006AB07120C3BE0FCA3E0FDAF2AAE29127E
-:1013400017E7500DAB2BAA2CA92DE52E12078ED333
-:1013500022052E8097C3228F268C278D28EF120B13
-:10136000AFAA06F97BFFEF120C16FDEF120C54F535
-:101370002EEF120CF6120BB98E2FF530EF120C6314
-:10138000F531EF120D01120BB98E32F533EF120C5D
-:1013900072F534EFC454F024DDF582E4341B120BF3
-:1013A000B98E35F536EFC454F024DFF582E4341BF2
-:1013B000F583E493F537EF120C81F53885273985ED
-:1013C000283A020EBC900076E0FDC4540F2401FBC5
-:1013D000E433FAED540FF96B7001EA603DE97010E7
-:1013E000E0C4540F2401FDE433FCED64044C602A96
-:1013F000900076E0C4540FFD540F120CDFEEF0A302
-:10140000EFF0ED04C454F049900076F0E0FFC454CE
-:101410000FC394044004EF540FF022C0E0C083C017
-:1014200082C0D075D000C004C005C006C00753C834
-:101430007F9000E5E0FEA3E0FF4E700353C8FB90F1
-:1014400000E112166A50099000E5E4F0A3F0800D67
-:10145000C39000E6E09DF09000E5E09CF0D007D05E
-:1014600006D005D004D0D0D082D083D0E032C0E006
-:10147000C083C082C0D075D000C004C005C006C003
-:101480000753917F9000E7E0FEA3E0FF4E70035307
-:1014900091FB9000E312166A50099000E7E4F0A374
-:1014A000F0800DC39000E8E09DF09000E7E09CF034
-:1014B000D007D006D005D004D0D0D082D083D0E0E1
-:1014C0003212081DFDACF0AF2BAE2A8F828E83AF97
-:1014D00005AE041218A6AB07AA06D3EB94F4EA945F
-:1014E0000140067E017FF48004AE02AF03AA06AB82
-:1014F00007C3EB9464EA940050067E007F64800486
-:10150000AE02AF03AA06AB07AF82AE831217E72283
-:10151000AD07AC06ABDA900076E0FEC4540FFFEEE8
-:10152000540FFEB50702C32253DAFE900076E0FAAC
-:10153000EE120CDFE0FFA3E08D828C83CFF0A3EFEF
-:10154000F0EA54F0FF900076E0044FF0540FC3949B
-:10155000044004E054F0F08BDAD322AE05AD07E48A
-:10156000FCFB7FAA121811AF05121811EEC454F03B
-:1015700024A6F582E4341DF583E493FFECC39F5069
-:101580000774082CFC0B80F4EB04FF12180CE4FC2D
-:10159000ECC39B500974032C1217190C80F27F5571
-:1015A0001218110219258F26900079E0F5277E0088
-:1015B0007F3C7D007B007A0079661209557F0B1213
-:1015C000192E43DA011200707D0A7C007F017E0033
-:1015D00012179712001DE4900087F0900086F0909B
-:1015E0000099E526F0900079F0AF2722AE07E4FDE0
-:1015F000F52612180A900001E0FF12181190000160
-:10160000121725900077E0FF12181190007712173B
-:1016100025900088E0FF1218119000881217257499
-:10162000032D1217190DBD03F67F5512181102195B
-:1016300025AB07AA06E4F9F87F407E427D0FFC1235
-:101640000891A804A905AA06AB077F207ED77D755F
-:101650007C01120891C3E49FFFE49EFE22AB07AA1F
-:1016600006E4F9F87FE87E03FD22E0FCA3E0FDC379
-:10167000EF9DEE9C22AFFBAEFC7C007D0A1207A022
-:10168000AD07AC06AFD953D9BFE4F5FAF5F98FD958
-:10169000C3EC948050157F002093027F01EFC43388
-:1016A000333354804CFEEDFF0213C5E4900076F016
-:1016B000229000DDE0FF9000DBE0B507057E017FB2
-:1016C00000229000DB121739E0FD7C009000DBE087
-:1016D00004F0E0B44002E4F09000DAE0FEEE4204F0
-:1016E000E4F0AE04AF05229000EDE0FCA3E0FDECD9
-:1016F000547FFEAF0522EC5480C41313135401240D
-:10170000FF22A3E493FE74019322E49000EBF0A384
-:10171000F022900087E024FB22F582E43400F58378
-:10172000E0FF021811A3E0FF021811E52625E024CE
-:101730008BF582E43400F58322E0249AF582E434C8
-:1017400000F58322AD07AC06900074E0FAA3E0FB3D
-:10175000EA5480C413131354017005300102C322EC
-:10176000AF05AE04EA547FFCAD031214CB228E37D2
-:101770008F388C398D3A12165D12163E12188290EF
-:1017800000E5E539F0A3E53AF09000E1E537F0A394
-:10179000E538F043C804228E288F298C2A8D2B121D
-:1017A000165D12163E12188E9000E7E52AF0A3E5AA
-:1017B0002BF09000E3E528F0A3E529F04391042203
-:1017C00012002A1218FA1219011218B2121916125E
-:1017D00018441218D01218BC1218C612189A1219EE
-:1017E0000812191A02190FC3ED9BF582EC9AF583C2
-:1017F000C3E5829FE5839E500FED2BFDEC3AFCC3C1
-:10180000EF9DEE9C50028001C3227FAA121811AFF7
-:1018100006C2059000DFE0B42002E4F09000DFE0B3
-:101820002446F8A607E004F0A3E004F0227E1DE4BD
-:10183000FDEF30E70625E06EFF8004EF25E0FF0DA9
-:10184000BD08EE22AF885388AF758CA0758DCBEFA5
-:101850005440FEEF54104E428822C3EF942CEE9475
-:10186000014003D38001C322121875D2039000797E
-:10187000E0FF0215A6AE0712180A7F5512181102D2
-:101880001925AD07AC06ECF5CBAF058FCA22AD0725
-:10189000AC06ECF593AF058F9222C2DE75D90575C3
-:1018A000F9FF75960122EF7802CEC313CE13D8F953
-:1018B000FF2275E34075E10175E20122E5915404D0
-:1018C0005391FB429122758E547589224388502290
-:1018D000E5C8540453C8FB42C82253984FEB4F4D00
-:1018E000F59822E5C8C320E201D322E591C320E2A6
-:1018F00001D32253C8FB53C87F2275A41175D4CFDE
-:101900002275A54175D5772253F77F75DA30227598
-:10191000E69075A8B022E4F5A9224398102230057C
-:10192000FD22C2DE22D299228F9922AF99228F8C7A
-:101930002222222222015E041A2A620802080109D8
-:1019400000019004B00BB81C520A03080109000101
-:10195000C20384286E020800090108014A02762A9F
-:101960004E020800090108028A1E82071C0F8C081B
-:101970000108020803016802D012C005DC0A03084E
-:10198000010900024E05DC01AE348A0A0308010990
-:1019900000012C0A00008C047E27F60801080308C9
-:1019A0000208020803080401040A9A051428A00882
-:1019B000010800080208020800080301720438192F
-:1019C00082020801080009029407D00FA02328080A
-:1019D00003080108020801061802D000D208010A13
-:1019E0000109020A0101D603FC0BFE0208010800EE
-:1019F00009019002D0132E010A00090108023007E4
-:101A0000760F322274030801080208193503193BC6
-:101A100002193D02193F020000001819410419493A
-:101A200002194B02194D0200000018194F031955F5
-:101A30000219570219590200000018195B031961B5
-:101A4000021963021965020000000C196704196F7E
-:101A50000219710219730200000028197504197D1A
-:101A600002197F0219810200000028198304198BD2
-:101A700002198D02198F0200000018199105199B97
-:101A800002199D0419A10419A5022019A70419AF70
-:101A90000219B10419B50419B9022019BB0319C1FF
-:101AA0000219C30219C5020000001119C70419CF99
-:101AB0000219D10219D30219D5022419D70319DD4D
-:101AC0000219DF0219E10219E3022819E50319EBF3
-:101AD0000219ED0219EF020000001419F10319F7C1
-:101AE0000219F90219FB020000002719FD041A056A
-:101AF000021A07021A090200000024015E041A2AD1
-:101B000062080208010900019004B00BB81C520AD7
-:101B1000030801090001C20384286E0208000901BC
-:101B200008014A02762A4E020800090108028A1EAC
-:101B300082071C0F8C080108020803016802D012FA
-:101B4000C005DC0A0308010900024E05DC01AE34C1
-:101B50008A0A0308010900012C0A00008C047E2770
-:101B6000F608010803080208020803080401040A31
-:101B70009A051428A00801080008020802080008B5
-:101B8000030172043819820208010800090294074F
-:101B9000D00FA02328080308010802080106180234
-:101BA000D000D208010A0109020A0101D603FC0B88
-:101BB000FE020801080009019002D0132E010A005C
-:101BC000090108023007760F322274030801080267
-:101BD000081AFB031B01021B03021B050200000085
-:101BE000181B07041B0F021B11021B13020000002D
-:101BF000181B15031B1B021B1D021B1F02000000EC
-:101C0000181B21031B27021B29021B2B02000000AB
-:101C10000C1B2D041B35021B37021B390200000070
-:101C2000281B3B041B43021B45021B47020000000C
-:101C3000281B49041B51021B53021B5502000000C4
-:101C4000181B57051B61021B63041B67041B6B02F7
-:101C5000201B6D041B75021B77041B7B041B7F027A
-:101C6000201B81031B87021B89021B8B02000000C3
-:101C7000111B8D041B95021B97021B99021B9B02D3
-:101C8000241B9D031BA3021BA5021BA7021BA90269
-:101C9000281BAB031BB1021BB3021BB502000000E3
-:101CA000141BB7031BBD021BBF021BC102000000B7
-:101CB000271BC3041BCB021BCD021BCF020000005D
-:101CC00024015E041A2A6208020801090001900436
-:101CD000B00BB81C520A030801090001C203842892
-:101CE0006E020800090108014A02762A4E02080025
-:101CF000090108028A1E82071C0F8C0801080208CD
-:101D000003016802D012C005DC0A030801090002C1
-:101D10004E05DC01AE348A0A0308010900012C0AD1
-:101D200000008C047E27F608010803080208020858
-:101D300003080401040A9A051428A00801080008F1
-:101D4000020802080008030172043819820208011F
-:101D5000080009029407D00FA023280803080108EF
-:101D6000020801061802D000D208010A0109020A7D
-:101D70000101D603FC0BFE020801080009019002D4
-:101D8000D0132E010A00090108023007760F322213
-:101D9000740308010802081CC1031CC7021CC90205
-:101DA0001CCB02000000181CCD041CD5021CD7025D
-:101DB0001CD902000000181CDB031CE1021CE3021A
-:101DC0001CE502000000181CE7031CED021CEF02DA
-:101DD0001CF1020000000C1CF3041CFB021CFD02A1
-:101DE0001CFF02000000281D01041D09021D0B023A
-:101DF0001D0D02000000281D0F041D17021D1902F1
-:101E00001D1B02000000181D1D051D27021D2904B1
-:101E10001D2D041D3102201D33041D3B021D3D04F8
-:101E20001D41041D4502201D47031D4D021D4F028B
-:101E30001D5102000000111D53041D5B021D5D02B7
-:101E40001D5F021D6102241D63031D69021D6B02DB
-:101E50001D6D021D6F02281D71031D77021D790281
-:101E60001D7B02000000141D7D031D83021D8502E1
-:101E70001D8702000000271D89041D91021D930289
-:071E80001D95020000002483
-:00000001FF