diff --git a/tasmota/tasmota_support/support_button_v4.ino b/tasmota/tasmota_support/support_button_v4.ino index 1c8483b54..3b7850ed7 100644 --- a/tasmota/tasmota_support/support_button_v4.ino +++ b/tasmota/tasmota_support/support_button_v4.ino @@ -53,7 +53,6 @@ struct BUTTON { uint8_t press_counter[MAX_KEYS_SET] = { 0 }; // Number of button presses within Button.window_timer uint8_t dual_receive_count = 0; // Sonoff dual input flag uint8_t first_change = 0; - uint8_t present = 0; // Number of buttons found flag bool probe_mutex; } Button; @@ -92,6 +91,11 @@ void ButtonSetVirtualPinState(uint32_t index, uint32_t state) { bitWrite(Button.virtual_pin, index, state); } +uint8_t ButtonGetState(uint32_t index) { + // Get current state + return Button.debounced_state[index]; +} + uint8_t ButtonLastState(uint32_t index) { // Get last state return Button.last_state[index]; @@ -228,22 +232,16 @@ void ButtonProbe(void) { void ButtonInit(void) { bool ac_detect = (Settings->button_debounce % 10 == 9); - - Button.present = 0; Button.used = 0; - -#ifdef ESP8266 - if ((SONOFF_DUAL == TasmotaGlobal.module_type) || (CH4 == TasmotaGlobal.module_type)) { - Button.present++; - } -#endif // ESP8266 - for (uint32_t i = 0; i < MAX_KEYS_SET; i++) { Button.last_state[i] = NOT_PRESSED; - bool used = false; - +#ifdef ESP8266 + if ((0 == i) && ((SONOFF_DUAL == TasmotaGlobal.module_type) || (CH4 == TasmotaGlobal.module_type))) { + bitSet(Button.used, i); // This pin is used + } else +#endif // ESP8266 if (PinUsed(GPIO_KEY1, i)) { - Button.present++; + bitSet(Button.used, i); // This pin is used #ifdef ESP8266 pinMode(Pin(GPIO_KEY1, i), bitRead(Button.no_pullup_mask, i) ? INPUT : ((16 == Pin(GPIO_KEY1, i)) ? INPUT_PULLDOWN_16 : INPUT_PULLUP)); #endif // ESP8266 @@ -252,11 +250,14 @@ void ButtonInit(void) { #endif // ESP32 // Set global now so doesn't change the saved power state on first button check Button.last_state[i] = (digitalRead(Pin(GPIO_KEY1, i)) != bitRead(Button.inverted_mask, i)); - used = true; + if (ac_detect) { + Button.state[i] = 0x80 + 2 * BUTTON_AC_PERIOD; + Button.last_state[i] = 0; // Will set later in the debouncing code + } } #ifdef USE_ADC else if (PinUsed(GPIO_ADC_BUTTON, i) || PinUsed(GPIO_ADC_BUTTON_INV, i)) { - Button.present++; + bitSet(Button.used, i); // This pin is used } #endif // USE_ADC else { @@ -268,7 +269,6 @@ void ButtonInit(void) { At exit: XdrvMailbox.index bit 0 = current state */ - Button.present++; bitSet(Button.used, i); // This pin is used bool state = (XdrvMailbox.index &1); ButtonSetVirtualPinState(i, state); // Virtual hardware pin state @@ -276,22 +276,15 @@ void ButtonInit(void) { // last_state[i] must be 1 to indicate no button pressed Button.last_state[i] = (bitRead(Button.virtual_pin, i) != bitRead(Button.inverted_mask, i)); - AddLog(LOG_LEVEL_DEBUG, PSTR("BTN: Add vButton%d, State %d"), Button.present, Button.last_state[i]); - - used = true; + AddLog(LOG_LEVEL_DEBUG, PSTR("BTN: Add vButton%d, State %d"), i +1, Button.last_state[i]); } } - - if (used && ac_detect) { - Button.state[i] = 0x80 + 2 * BUTTON_AC_PERIOD; - Button.last_state[i] = 0; // Will set later in the debouncing code - } Button.debounced_state[i] = Button.last_state[i]; } // AddLog(LOG_LEVEL_DEBUG, PSTR("BTN: vPinUsed %08X, State %08X, Invert %08X"), Button.used, Button.virtual_pin, Button.inverted_mask); - if (Button.present) { + if (Button.used) { // Any bit set Button.first_change = true; TickerButton.attach_ms((ac_detect) ? BUTTON_FAST_PROBE_INTERVAL : BUTTON_PROBE_INTERVAL, ButtonProbe); } @@ -338,12 +331,12 @@ void ButtonHandler(void) { char scmnd[20]; for (uint32_t button_index = 0; button_index < MAX_KEYS_SET; button_index++) { - uint8_t button = NOT_PRESSED; - uint8_t button_present = 0; + if (!bitRead(Button.used, button_index)) { return; } + + uint8_t button = Button.debounced_state[button_index]; #ifdef ESP8266 if (!button_index && ((SONOFF_DUAL == TasmotaGlobal.module_type) || (CH4 == TasmotaGlobal.module_type))) { - button_present = 1; if (Button.dual_code) { AddLog(LOG_LEVEL_DEBUG, PSTR("BTN: Code %04X"), Button.dual_code); button = PRESSED; @@ -352,6 +345,8 @@ void ButtonHandler(void) { hold_time_extent = 1; } Button.dual_code = 0; + } else { + button = NOT_PRESSED; } } else #endif // ESP8266 @@ -371,193 +366,183 @@ void ButtonHandler(void) { } AddLog(LOG_LEVEL_INFO, PSTR("PLOT: %u, %u, %u,"), button_index +1, _value, TouchButton.hits[button_index]); // Button number (1..4), value, continuous hits under threshold continue; - } else + } #endif // ESP32 SOC_TOUCH_VERSION_1 or SOC_TOUCH_VERSION_2 - button_present = 1; - button = Button.debounced_state[button_index]; } #ifdef USE_ADC else if (PinUsed(GPIO_ADC_BUTTON, button_index)) { - button_present = 1; button = AdcGetButton(Pin(GPIO_ADC_BUTTON, button_index)); } else if (PinUsed(GPIO_ADC_BUTTON_INV, button_index)) { - button_present = 1; button = AdcGetButton(Pin(GPIO_ADC_BUTTON_INV, button_index)); } #endif // USE_ADC - else if (bitRead(Button.used, button_index)) { - button_present = 1; - button = Button.debounced_state[button_index]; + + XdrvMailbox.index = button_index; + XdrvMailbox.payload = button; + XdrvMailbox.command_code = Button.last_state[button_index]; + if (XdrvCall(FUNC_BUTTON_PRESSED)) { + // Serviced } - - if (button_present) { - XdrvMailbox.index = button_index; - XdrvMailbox.payload = button; - XdrvMailbox.command_code = Button.last_state[button_index]; - if (XdrvCall(FUNC_BUTTON_PRESSED)) { - // Serviced - } #ifdef ESP8266 - else if (SONOFF_4CHPRO == TasmotaGlobal.module_type) { - if (Button.hold_timer[button_index]) { Button.hold_timer[button_index]--; } + else if (SONOFF_4CHPRO == TasmotaGlobal.module_type) { + if (Button.hold_timer[button_index]) { Button.hold_timer[button_index]--; } - bool button_pressed = false; - if ((PRESSED == button) && (NOT_PRESSED == Button.last_state[button_index])) { - AddLog(LOG_LEVEL_DEBUG, PSTR("BTN: Button%d level 1-0"), button_index +1); - Button.hold_timer[button_index] = loops_per_second; - button_pressed = true; + bool button_pressed = false; + if ((PRESSED == button) && (NOT_PRESSED == Button.last_state[button_index])) { + AddLog(LOG_LEVEL_DEBUG, PSTR("BTN: Button%d level 1-0"), button_index +1); + Button.hold_timer[button_index] = loops_per_second; + button_pressed = true; + } + if ((NOT_PRESSED == button) && (PRESSED == Button.last_state[button_index])) { + AddLog(LOG_LEVEL_DEBUG, PSTR("BTN: Button%d level 0-1"), button_index +1); + if (!Button.hold_timer[button_index]) { button_pressed = true; } // Do not allow within 1 second + } + if (button_pressed) { + if (!Settings->flag3.mqtt_buttons) { // SetOption73 (0) - Decouple button from relay and send just mqtt topic + if (!SendKey(KEY_BUTTON, button_index +1, POWER_TOGGLE)) { // Execute Toggle command via MQTT if ButtonTopic is set + ExecuteCommandPower(button_index +1, POWER_TOGGLE, SRC_BUTTON); // Execute Toggle command internally + } + } else { + MqttButtonTopic(button_index +1, 1, 0); // SetOption73 (0) - Decouple button from relay and send just mqtt topic } - if ((NOT_PRESSED == button) && (PRESSED == Button.last_state[button_index])) { - AddLog(LOG_LEVEL_DEBUG, PSTR("BTN: Button%d level 0-1"), button_index +1); - if (!Button.hold_timer[button_index]) { button_pressed = true; } // Do not allow within 1 second - } - if (button_pressed) { - if (!Settings->flag3.mqtt_buttons) { // SetOption73 (0) - Decouple button from relay and send just mqtt topic + } + } +#endif // ESP8266 + else { + if ((PRESSED == button) && (NOT_PRESSED == Button.last_state[button_index])) { + + if (Settings->flag.button_single) { // SetOption13 (0) - Allow only single button press for immediate action, + if (!Settings->flag3.mqtt_buttons) { // SetOption73 (0) - Decouple button from relay and send just mqtt topic + AddLog(LOG_LEVEL_DEBUG, PSTR("BTN: Button%d immediate"), button_index +1); if (!SendKey(KEY_BUTTON, button_index +1, POWER_TOGGLE)) { // Execute Toggle command via MQTT if ButtonTopic is set ExecuteCommandPower(button_index +1, POWER_TOGGLE, SRC_BUTTON); // Execute Toggle command internally } } else { - MqttButtonTopic(button_index +1, 1, 0); // SetOption73 (0) - Decouple button from relay and send just mqtt topic + MqttButtonTopic(button_index +1, 1, 0); // SetOption73 1 - Decouple button from relay and send just mqtt topic + } + } else { + Button.press_counter[button_index] = (Button.window_timer[button_index]) ? Button.press_counter[button_index] +1 : 1; + AddLog(LOG_LEVEL_DEBUG, PSTR("BTN: Button%d multi-press %d"), button_index +1, Button.press_counter[button_index]); + Button.window_timer[button_index] = loops_per_second / 2; // 0.5 second multi press window + } + TasmotaGlobal.blinks = 201; + } + + if (NOT_PRESSED == button) { + Button.hold_timer[button_index] = 0; + if (Settings->flag3.mqtt_buttons && (PRESSED == Button.last_state[button_index]) && !Button.press_counter[button_index]) { // SetOption73 (0) - Decouple button from relay and send just mqtt topic + MqttButtonTopic(button_index +1, 6, 0); + } + } else { + Button.hold_timer[button_index]++; + if (Settings->flag.button_single) { // SetOption13 (0) - Allow only single button press for immediate action + if (Button.hold_timer[button_index] == loops_per_second * hold_time_extent * Settings->param[P_HOLD_TIME] / 10) { // SetOption32 (40) - Button held for factor times longer + snprintf_P(scmnd, sizeof(scmnd), PSTR(D_CMND_SETOPTION "13 0")); // Disable single press only + ExecuteCommand(scmnd, SRC_BUTTON); + } + } else { + if (Button.hold_timer[button_index] == loops_per_second * Settings->param[P_HOLD_TIME] / 10) { // SetOption32 (40) - Button hold + Button.press_counter[button_index] = 0; + if (Settings->flag3.mqtt_buttons) { // SetOption73 (0) - Decouple button from relay and send just mqtt topic + MqttButtonTopic(button_index +1, 3, 1); + } else { + SendKey(KEY_BUTTON, button_index +1, POWER_HOLD); // Execute Hold command via MQTT if ButtonTopic is set + } + } else { + if (Settings->flag.button_restrict) { // SetOption1 (0) - Control button multipress + if (Settings->param[P_HOLD_IGNORE] > 0) { // SetOption40 (0) - Do not ignore button hold + if (Button.hold_timer[button_index] > loops_per_second * Settings->param[P_HOLD_IGNORE] / 10) { + Button.hold_timer[button_index] = 0; // Reset button hold counter to stay below hold trigger + Button.press_counter[button_index] = 0; // Discard button press to disable functionality + } + } + } else { + if ((Button.hold_timer[button_index] == loops_per_second * hold_time_extent * Settings->param[P_HOLD_TIME] / 10)) { // SetOption32 (40) - Button held for factor times longer + Button.press_counter[button_index] = 0; + snprintf_P(scmnd, sizeof(scmnd), PSTR(D_CMND_RESET " 1")); + ExecuteCommand(scmnd, SRC_BUTTON); + } + } } } } -#endif // ESP8266 - else { - if ((PRESSED == button) && (NOT_PRESSED == Button.last_state[button_index])) { - if (Settings->flag.button_single) { // SetOption13 (0) - Allow only single button press for immediate action, - if (!Settings->flag3.mqtt_buttons) { // SetOption73 (0) - Decouple button from relay and send just mqtt topic - AddLog(LOG_LEVEL_DEBUG, PSTR("BTN: Button%d immediate"), button_index +1); - if (!SendKey(KEY_BUTTON, button_index +1, POWER_TOGGLE)) { // Execute Toggle command via MQTT if ButtonTopic is set - ExecuteCommandPower(button_index +1, POWER_TOGGLE, SRC_BUTTON); // Execute Toggle command internally - } - } else { - MqttButtonTopic(button_index +1, 1, 0); // SetOption73 1 - Decouple button from relay and send just mqtt topic - } - } else { - Button.press_counter[button_index] = (Button.window_timer[button_index]) ? Button.press_counter[button_index] +1 : 1; - AddLog(LOG_LEVEL_DEBUG, PSTR("BTN: Button%d multi-press %d"), button_index +1, Button.press_counter[button_index]); - Button.window_timer[button_index] = loops_per_second / 2; // 0.5 second multi press window - } - TasmotaGlobal.blinks = 201; - } - - if (NOT_PRESSED == button) { - Button.hold_timer[button_index] = 0; - if (Settings->flag3.mqtt_buttons && (PRESSED == Button.last_state[button_index]) && !Button.press_counter[button_index]) { // SetOption73 (0) - Decouple button from relay and send just mqtt topic - MqttButtonTopic(button_index +1, 6, 0); - } + if (!Settings->flag.button_single) { // SetOption13 (0) - Allow multi-press + if (Button.window_timer[button_index]) { + Button.window_timer[button_index]--; } else { - Button.hold_timer[button_index]++; - if (Settings->flag.button_single) { // SetOption13 (0) - Allow only single button press for immediate action - if (Button.hold_timer[button_index] == loops_per_second * hold_time_extent * Settings->param[P_HOLD_TIME] / 10) { // SetOption32 (40) - Button held for factor times longer - snprintf_P(scmnd, sizeof(scmnd), PSTR(D_CMND_SETOPTION "13 0")); // Disable single press only - ExecuteCommand(scmnd, SRC_BUTTON); - } - } else { - if (Button.hold_timer[button_index] == loops_per_second * Settings->param[P_HOLD_TIME] / 10) { // SetOption32 (40) - Button hold - Button.press_counter[button_index] = 0; - if (Settings->flag3.mqtt_buttons) { // SetOption73 (0) - Decouple button from relay and send just mqtt topic - MqttButtonTopic(button_index +1, 3, 1); - } else { - SendKey(KEY_BUTTON, button_index +1, POWER_HOLD); // Execute Hold command via MQTT if ButtonTopic is set - } - } else { - if (Settings->flag.button_restrict) { // SetOption1 (0) - Control button multipress - if (Settings->param[P_HOLD_IGNORE] > 0) { // SetOption40 (0) - Do not ignore button hold - if (Button.hold_timer[button_index] > loops_per_second * Settings->param[P_HOLD_IGNORE] / 10) { - Button.hold_timer[button_index] = 0; // Reset button hold counter to stay below hold trigger - Button.press_counter[button_index] = 0; // Discard button press to disable functionality - } - } - } else { - if ((Button.hold_timer[button_index] == loops_per_second * hold_time_extent * Settings->param[P_HOLD_TIME] / 10)) { // SetOption32 (40) - Button held for factor times longer - Button.press_counter[button_index] = 0; - snprintf_P(scmnd, sizeof(scmnd), PSTR(D_CMND_RESET " 1")); - ExecuteCommand(scmnd, SRC_BUTTON); - } - } - } - } - } + if (!TasmotaGlobal.restart_flag && !Button.hold_timer[button_index] && (Button.press_counter[button_index] > 0) && (Button.press_counter[button_index] < 7)) { - if (!Settings->flag.button_single) { // SetOption13 (0) - Allow multi-press - if (Button.window_timer[button_index]) { - Button.window_timer[button_index]--; - } else { - if (!TasmotaGlobal.restart_flag && !Button.hold_timer[button_index] && (Button.press_counter[button_index] > 0) && (Button.press_counter[button_index] < 7)) { - - bool single_press = false; - if (Button.press_counter[button_index] < 3) { // Single or Double press + bool single_press = false; + if (Button.press_counter[button_index] < 3) { // Single or Double press #ifdef ESP8266 - if ((SONOFF_DUAL_R2 == TasmotaGlobal.module_type) || (SONOFF_DUAL == TasmotaGlobal.module_type) || (CH4 == TasmotaGlobal.module_type)) { - single_press = true; - } else + if ((SONOFF_DUAL_R2 == TasmotaGlobal.module_type) || (SONOFF_DUAL == TasmotaGlobal.module_type) || (CH4 == TasmotaGlobal.module_type)) { + single_press = true; + } else #endif // ESP8266 - { - single_press = (Settings->flag.button_swap +1 == Button.press_counter[button_index]); // SetOption11 (0) - if ((1 == Button.present) && (2 == TasmotaGlobal.devices_present)) { // Single Button with two devices only - if (Settings->flag.button_swap) { // SetOption11 (0) - Button.press_counter[button_index] = (single_press) ? 1 : 2; - } + { + single_press = (Settings->flag.button_swap +1 == Button.press_counter[button_index]); // SetOption11 (0) + if ((1 == Button.used) && (2 == TasmotaGlobal.devices_present)) { // Single Button with two devices only + if (Settings->flag.button_swap) { // SetOption11 (0) + Button.press_counter[button_index] = (single_press) ? 1 : 2; } } } + } - XdrvMailbox.index = button_index; - XdrvMailbox.payload = Button.press_counter[button_index]; - if (XdrvCall(FUNC_BUTTON_MULTI_PRESSED)) { - // Serviced + XdrvMailbox.index = button_index; + XdrvMailbox.payload = Button.press_counter[button_index]; + if (XdrvCall(FUNC_BUTTON_MULTI_PRESSED)) { + // Serviced // AddLog(LOG_LEVEL_DEBUG, PSTR("BTN: FUNC_BUTTON_MULTI_PRESSED serviced")); - } else + } else #ifdef ROTARY_V1 - if (!RotaryButtonPressed(button_index)) { + if (!RotaryButtonPressed(button_index)) { #endif - if (!Settings->flag3.mqtt_buttons && single_press && SendKey(KEY_BUTTON, button_index + Button.press_counter[button_index], POWER_TOGGLE)) { // Execute Toggle command via MQTT if ButtonTopic is set - // Success - } else { - if (Button.press_counter[button_index] < 6) { // Single to Penta press + if (!Settings->flag3.mqtt_buttons && single_press && SendKey(KEY_BUTTON, button_index + Button.press_counter[button_index], POWER_TOGGLE)) { // Execute Toggle command via MQTT if ButtonTopic is set + // Success + } else { + if (Button.press_counter[button_index] < 6) { // Single to Penta press // if (WifiState() > WIFI_RESTART) { // Wifimanager active // TasmotaGlobal.restart_flag = 1; // } - if (!Settings->flag3.mqtt_buttons) { // SetOption73 - Detach buttons from relays and enable MQTT action state for multipress - if (Button.press_counter[button_index] == 1) { // By default first press always send a TOGGLE (2) - ExecuteCommandPower(button_index + Button.press_counter[button_index], POWER_TOGGLE, SRC_BUTTON); - } else { - SendKey(KEY_BUTTON, button_index +1, Button.press_counter[button_index] +9); // 2,3,4 and 5 press send just the key value (11,12,13 and 14) for rules - if (0 == button_index) { // BUTTON1 can toggle up to 5 relays if present. If a relay is not present will send out the key value (2,11,12,13 and 14) for rules - uint32_t max_device = (TasmotaGlobal.devices_present < MAX_RELAY_BUTTON1) ? TasmotaGlobal.devices_present : MAX_RELAY_BUTTON1; - if ((Button.press_counter[button_index] > 1) && (Button.press_counter[button_index] <= max_device)) { - ExecuteCommandPower(button_index + Button.press_counter[button_index], POWER_TOGGLE, SRC_BUTTON); // Execute Toggle command internally - } + if (!Settings->flag3.mqtt_buttons) { // SetOption73 - Detach buttons from relays and enable MQTT action state for multipress + if (Button.press_counter[button_index] == 1) { // By default first press always send a TOGGLE (2) + ExecuteCommandPower(button_index + Button.press_counter[button_index], POWER_TOGGLE, SRC_BUTTON); + } else { + SendKey(KEY_BUTTON, button_index +1, Button.press_counter[button_index] +9); // 2,3,4 and 5 press send just the key value (11,12,13 and 14) for rules + if (0 == button_index) { // BUTTON1 can toggle up to 5 relays if present. If a relay is not present will send out the key value (2,11,12,13 and 14) for rules + uint32_t max_device = (TasmotaGlobal.devices_present < MAX_RELAY_BUTTON1) ? TasmotaGlobal.devices_present : MAX_RELAY_BUTTON1; + if ((Button.press_counter[button_index] > 1) && (Button.press_counter[button_index] <= max_device)) { + ExecuteCommandPower(button_index + Button.press_counter[button_index], POWER_TOGGLE, SRC_BUTTON); // Execute Toggle command internally } } } - - } else { // 6 press start wificonfig 2 - if (!Settings->flag.button_restrict) { // SetOption1 - Control button multipress - snprintf_P(scmnd, sizeof(scmnd), PSTR(D_CMND_WIFICONFIG " 2")); - ExecuteCommand(scmnd, SRC_BUTTON); - } } - if (Settings->flag3.mqtt_buttons) { // SetOption73 (0) - Decouple button from relay and send just mqtt topic - if (Button.press_counter[button_index] >= 1 && Button.press_counter[button_index] <= 5) { - MqttButtonTopic(button_index +1, Button.press_counter[button_index], 0); - } + + } else { // 6 press start wificonfig 2 + if (!Settings->flag.button_restrict) { // SetOption1 - Control button multipress + snprintf_P(scmnd, sizeof(scmnd), PSTR(D_CMND_WIFICONFIG " 2")); + ExecuteCommand(scmnd, SRC_BUTTON); + } + } + if (Settings->flag3.mqtt_buttons) { // SetOption73 (0) - Decouple button from relay and send just mqtt topic + if (Button.press_counter[button_index] >= 1 && Button.press_counter[button_index] <= 5) { + MqttButtonTopic(button_index +1, Button.press_counter[button_index], 0); } } -#ifdef ROTARY_V1 } -#endif - Button.press_counter[button_index] = 0; +#ifdef ROTARY_V1 } +#endif + Button.press_counter[button_index] = 0; } } - } + } Button.last_state[button_index] = button; } @@ -576,7 +561,7 @@ void MqttButtonTopic(uint32_t button_id, uint32_t action, uint32_t hold) { } void ButtonLoop(void) { - if (Button.present) { + if (Button.used) { if (TimeReached(Button.debounce)) { SetNextTimeInterval(Button.debounce, Settings->button_debounce); // ButtonDebounce (50) ButtonHandler(); diff --git a/tasmota/tasmota_support/support_switch_v4.ino b/tasmota/tasmota_support/support_switch_v4.ino index 4e45a9fed..d2871ca08 100644 --- a/tasmota/tasmota_support/support_switch_v4.ino +++ b/tasmota/tasmota_support/support_switch_v4.ino @@ -76,7 +76,10 @@ void SwitchSetVirtualPinState(uint32_t index, uint32_t state) { void SwitchSetState(uint32_t index, uint32_t state) { // Set debounced pin state to be used by late detected switches - bitSet(Switch.used, index); // Force use bit as call maybe late + if (!bitRead(Switch.used, index)) { + bitSet(Switch.used, index); + AddLog(LOG_LEVEL_DEBUG, PSTR("SWT: Add vSwitch%d, State %d"), index +1, state); + } Switch.debounced_state[index] = state; } @@ -218,16 +221,14 @@ void SwitchProbe(void) { } } } - Switch.probe_mutex = false; } void SwitchInit(void) { bool ac_detect = (Settings->switch_debounce % 10 == 9); - Switch.used = 0; for (uint32_t i = 0; i < MAX_SWITCHES_SET; i++) { - Switch.last_state[i] = NOT_PRESSED; // Init global to virtual switch state; + Switch.last_state[i] = NOT_PRESSED; if (PinUsed(GPIO_SWT1, i)) { bitSet(Switch.used, i); // This pin is used #ifdef ESP8266 @@ -237,6 +238,10 @@ void SwitchInit(void) { pinMode(Pin(GPIO_SWT1, i), bitRead(Switch.pulldown_mask, i) ? INPUT_PULLDOWN : bitRead(Switch.no_pullup_mask, i) ? INPUT : INPUT_PULLUP); #endif // ESP32 Switch.last_state[i] = digitalRead(Pin(GPIO_SWT1, i)); // Set global now so doesn't change the saved power state on first switch check + if (ac_detect) { + Switch.state[i] = 0x80 + 2 * SWITCH_AC_PERIOD; + Switch.last_state[i] = 0; // Will set later in the debouncing code + } } else { XdrvMailbox.index = i; @@ -255,11 +260,6 @@ void SwitchInit(void) { AddLog(LOG_LEVEL_DEBUG, PSTR("SWT: Add vSwitch%d, State %d"), i +1, Switch.last_state[i]); } } - - if (bitRead(Switch.used, i) && ac_detect) { - Switch.state[i] = 0x80 + 2 * SWITCH_AC_PERIOD; - Switch.last_state[i] = 0; // Will set later in the debouncing code - } Switch.debounced_state[i] = Switch.last_state[i]; } @@ -281,212 +281,212 @@ void SwitchHandler(void) { uint32_t loops_per_second = 1000 / Settings->switch_debounce; for (uint32_t i = 0; i < MAX_SWITCHES_SET; i++) { - if (bitRead(Switch.used, i)) { - uint32_t button = Switch.debounced_state[i]; - uint32_t switchflag = POWER_TOGGLE +1; - uint32_t mqtt_action = POWER_NONE; - uint32_t switchmode = Settings->switchmode[i]; + if (!bitRead(Switch.used, i)) { return; } - if (Switch.hold_timer[i] & (((switchmode == PUSHHOLDMULTI) | (switchmode == PUSHHOLDMULTI_INV)) ? SM_TIMER_MASK: SM_NO_TIMER_MASK)) { - Switch.hold_timer[i]--; - if ((Switch.hold_timer[i] & SM_TIMER_MASK) == loops_per_second * Settings->param[P_HOLD_TIME] / 25) { - if ((switchmode == PUSHHOLDMULTI) | (switchmode == PUSHHOLDMULTI_INV)){ - if (((switchmode == PUSHHOLDMULTI) & (NOT_PRESSED == Switch.last_state[i])) | ((switchmode == PUSHHOLDMULTI_INV) & (PRESSED == Switch.last_state[i]))) { - SendKey(KEY_SWITCH, i +1, POWER_INCREMENT); // Execute command via MQTT - } - else if ((Switch.hold_timer[i] & ~SM_TIMER_MASK) == SM_FIRST_PRESS) { - SendKey(KEY_SWITCH, i +1, POWER_DELAYED); // Execute command via MQTT - mqtt_action = POWER_DELAYED; - Switch.hold_timer[i] = 0; - } + uint32_t button = Switch.debounced_state[i]; + uint32_t switchflag = POWER_TOGGLE +1; + uint32_t mqtt_action = POWER_NONE; + uint32_t switchmode = Settings->switchmode[i]; + + if (Switch.hold_timer[i] & (((switchmode == PUSHHOLDMULTI) | (switchmode == PUSHHOLDMULTI_INV)) ? SM_TIMER_MASK: SM_NO_TIMER_MASK)) { + Switch.hold_timer[i]--; + if ((Switch.hold_timer[i] & SM_TIMER_MASK) == loops_per_second * Settings->param[P_HOLD_TIME] / 25) { + if ((switchmode == PUSHHOLDMULTI) | (switchmode == PUSHHOLDMULTI_INV)){ + if (((switchmode == PUSHHOLDMULTI) & (NOT_PRESSED == Switch.last_state[i])) | ((switchmode == PUSHHOLDMULTI_INV) & (PRESSED == Switch.last_state[i]))) { + SendKey(KEY_SWITCH, i +1, POWER_INCREMENT); // Execute command via MQTT } - } - if (0 == (Switch.hold_timer[i] & (((switchmode == PUSHHOLDMULTI) | (switchmode == PUSHHOLDMULTI_INV)) ? SM_TIMER_MASK: SM_NO_TIMER_MASK))) { - switch (switchmode) { - case TOGGLEMULTI: - switchflag = POWER_TOGGLE; // Toggle after hold - break; - case FOLLOWMULTI: - switchflag = button &1; // Follow wall switch state after hold - break; - case FOLLOWMULTI_INV: - switchflag = ~button &1; // Follow inverted wall switch state after hold - break; - case PUSHHOLDMULTI: - if (NOT_PRESSED == button) { - Switch.hold_timer[i] = loops_per_second * Settings->param[P_HOLD_TIME] / 25; - SendKey(KEY_SWITCH, i +1, POWER_INCREMENT); // Execute command via MQTT - mqtt_action = POWER_INCREMENT; - } else { - Switch.hold_timer[i]= 0; - SendKey(KEY_SWITCH, i +1, POWER_CLEAR); // Execute command via MQTT - mqtt_action = POWER_CLEAR; - } - break; - case PUSHHOLDMULTI_INV: - if (PRESSED == button) { - Switch.hold_timer[i] = loops_per_second * Settings->param[P_HOLD_TIME] / 25; - SendKey(KEY_SWITCH, i +1, POWER_INCREMENT); // Execute command via MQTT - mqtt_action = POWER_INCREMENT; - } else { - Switch.hold_timer[i]= 0; - SendKey(KEY_SWITCH, i +1, POWER_CLEAR); // Execute command via MQTT - mqtt_action = POWER_CLEAR; - } - break; - default: - SendKey(KEY_SWITCH, i +1, POWER_HOLD); // Execute command via MQTT - mqtt_action = POWER_HOLD; - break; + else if ((Switch.hold_timer[i] & ~SM_TIMER_MASK) == SM_FIRST_PRESS) { + SendKey(KEY_SWITCH, i +1, POWER_DELAYED); // Execute command via MQTT + mqtt_action = POWER_DELAYED; + Switch.hold_timer[i] = 0; } } } - - if (button != Switch.last_state[i]) { // This implies if ((PRESSED == button) then (NOT_PRESSED == Switch.last_state[i])) + if (0 == (Switch.hold_timer[i] & (((switchmode == PUSHHOLDMULTI) | (switchmode == PUSHHOLDMULTI_INV)) ? SM_TIMER_MASK: SM_NO_TIMER_MASK))) { switch (switchmode) { - case TOGGLE: - case PUSHBUTTON_TOGGLE: - switchflag = POWER_TOGGLE; // Toggle - break; - case FOLLOW: - switchflag = button &1; // Follow wall switch state - break; - case FOLLOW_INV: - switchflag = ~button &1; // Follow inverted wall switch state - break; - case PUSHBUTTON: - if (PRESSED == button) { - switchflag = POWER_TOGGLE; // Toggle with pushbutton to Gnd - } - break; - case PUSHBUTTON_INV: - if (NOT_PRESSED == button) { - switchflag = POWER_TOGGLE; // Toggle with releasing pushbutton from Gnd - } - break; - case PUSHBUTTONHOLD: - if (PRESSED == button) { - Switch.hold_timer[i] = loops_per_second * Settings->param[P_HOLD_TIME] / 10; // Start timer on button press - } - if ((NOT_PRESSED == button) && (Switch.hold_timer[i])) { - Switch.hold_timer[i] = 0; // Button released and hold timer not expired : stop timer... - switchflag = POWER_TOGGLE; // ...and Toggle - } - break; - case PUSHBUTTONHOLD_INV: - if (NOT_PRESSED == button) { - Switch.hold_timer[i] = loops_per_second * Settings->param[P_HOLD_TIME] / 10; // Start timer on button press... - } - if ((PRESSED == button) && (Switch.hold_timer[i])) { - Switch.hold_timer[i] = 0; // Button released and hold timer not expired : stop timer. - switchflag = POWER_TOGGLE; // ...and Toggle - } - break; - case TOGGLEMULTI: - case FOLLOWMULTI: - case FOLLOWMULTI_INV: - if (Switch.hold_timer[i]) { - Switch.hold_timer[i] = 0; - SendKey(KEY_SWITCH, i +1, POWER_HOLD); // Execute command via MQTT - mqtt_action = POWER_HOLD; - } else { - Switch.hold_timer[i] = loops_per_second / 2; // 0.5 second multi press window - } - break; - case PUSHHOLDMULTI: - if (NOT_PRESSED == button) { - if ((Switch.hold_timer[i] & SM_TIMER_MASK) != 0) { - Switch.hold_timer[i] = ((Switch.hold_timer[i] & ~SM_TIMER_MASK) == SM_FIRST_PRESS) ? SM_SECOND_PRESS : 0; - SendKey(KEY_SWITCH, i +1, POWER_INV); // Execute command via MQTT - mqtt_action = POWER_INV; - } - } else { - if ((Switch.hold_timer[i] & SM_TIMER_MASK) > loops_per_second * Settings->param[P_HOLD_TIME] / 25) { - if ((Switch.hold_timer[i] & ~SM_TIMER_MASK) != SM_SECOND_PRESS) { - Switch.hold_timer[i]= SM_FIRST_PRESS; - switchflag = POWER_TOGGLE; // Toggle with pushbutton - } - else{ - SendKey(KEY_SWITCH, i +1, POWER_100); // Execute command via MQTT - mqtt_action = POWER_100; - Switch.hold_timer[i]= 0; - } + case TOGGLEMULTI: + switchflag = POWER_TOGGLE; // Toggle after hold + break; + case FOLLOWMULTI: + switchflag = button &1; // Follow wall switch state after hold + break; + case FOLLOWMULTI_INV: + switchflag = ~button &1; // Follow inverted wall switch state after hold + break; + case PUSHHOLDMULTI: + if (NOT_PRESSED == button) { + Switch.hold_timer[i] = loops_per_second * Settings->param[P_HOLD_TIME] / 25; + SendKey(KEY_SWITCH, i +1, POWER_INCREMENT); // Execute command via MQTT + mqtt_action = POWER_INCREMENT; } else { Switch.hold_timer[i]= 0; - SendKey(KEY_SWITCH, i +1, POWER_RELEASE); // Execute command via MQTT - mqtt_action = POWER_RELEASE; + SendKey(KEY_SWITCH, i +1, POWER_CLEAR); // Execute command via MQTT + mqtt_action = POWER_CLEAR; } - } - Switch.hold_timer[i] = (Switch.hold_timer[i] & ~SM_TIMER_MASK) | loops_per_second * Settings->param[P_HOLD_TIME] / 10; - break; - case PUSHHOLDMULTI_INV: - if (PRESSED == button) { - if ((Switch.hold_timer[i] & SM_TIMER_MASK) != 0) { - Switch.hold_timer[i] = ((Switch.hold_timer[i] & ~SM_TIMER_MASK) == SM_FIRST_PRESS) ? SM_SECOND_PRESS : 0; - SendKey(KEY_SWITCH, i +1, POWER_INV); // Execute command via MQTT - mqtt_action = POWER_INV; - } - } else { - if ((Switch.hold_timer[i] & SM_TIMER_MASK)> loops_per_second * Settings->param[P_HOLD_TIME] / 25) { - if ((Switch.hold_timer[i] & ~SM_TIMER_MASK) != SM_SECOND_PRESS) { - Switch.hold_timer[i]= SM_FIRST_PRESS; - switchflag = POWER_TOGGLE; // Toggle with pushbutton - } - else{ - SendKey(KEY_SWITCH, i +1, POWER_100); // Execute command via MQTT - mqtt_action = POWER_100; - Switch.hold_timer[i]= 0; - } + break; + case PUSHHOLDMULTI_INV: + if (PRESSED == button) { + Switch.hold_timer[i] = loops_per_second * Settings->param[P_HOLD_TIME] / 25; + SendKey(KEY_SWITCH, i +1, POWER_INCREMENT); // Execute command via MQTT + mqtt_action = POWER_INCREMENT; } else { Switch.hold_timer[i]= 0; - SendKey(KEY_SWITCH, i +1, POWER_RELEASE); // Execute command via MQTT - mqtt_action = POWER_RELEASE; + SendKey(KEY_SWITCH, i +1, POWER_CLEAR); // Execute command via MQTT + mqtt_action = POWER_CLEAR; } - } - Switch.hold_timer[i] = (Switch.hold_timer[i] & ~SM_TIMER_MASK) | loops_per_second * Settings->param[P_HOLD_TIME] / 10; - break; - case PUSHON: - if (PRESSED == button) { - switchflag = POWER_ON; // Power ON with pushbutton to Gnd - } - break; - case PUSHON_INV: - if (NOT_PRESSED == button) { - switchflag = POWER_ON; // Power ON with releasing pushbutton from Gnd - } - break; - case PUSH_IGNORE: - case PUSH_IGNORE_INV: - Switch.last_state[i] = button; // Update switch state before publishing - MqttPublishSensor(); + break; + default: + SendKey(KEY_SWITCH, i +1, POWER_HOLD); // Execute command via MQTT + mqtt_action = POWER_HOLD; break; } - Switch.last_state[i] = button; } - if (switchflag <= POWER_TOGGLE) { - if (!Settings->flag5.mqtt_switches) { // SetOption114 (0) - Detach Switches from relays and enable MQTT action state for all the SwitchModes - if (!SendKey(KEY_SWITCH, i +1, switchflag)) { // Execute command via MQTT - ExecuteCommandPower(i +1, switchflag, SRC_SWITCH); // Execute command internally (if i < TasmotaGlobal.devices_present) + } + + if (button != Switch.last_state[i]) { // This implies if ((PRESSED == button) then (NOT_PRESSED == Switch.last_state[i])) + switch (switchmode) { + case TOGGLE: + case PUSHBUTTON_TOGGLE: + switchflag = POWER_TOGGLE; // Toggle + break; + case FOLLOW: + switchflag = button &1; // Follow wall switch state + break; + case FOLLOW_INV: + switchflag = ~button &1; // Follow inverted wall switch state + break; + case PUSHBUTTON: + if (PRESSED == button) { + switchflag = POWER_TOGGLE; // Toggle with pushbutton to Gnd + } + break; + case PUSHBUTTON_INV: + if (NOT_PRESSED == button) { + switchflag = POWER_TOGGLE; // Toggle with releasing pushbutton from Gnd + } + break; + case PUSHBUTTONHOLD: + if (PRESSED == button) { + Switch.hold_timer[i] = loops_per_second * Settings->param[P_HOLD_TIME] / 10; // Start timer on button press + } + if ((NOT_PRESSED == button) && (Switch.hold_timer[i])) { + Switch.hold_timer[i] = 0; // Button released and hold timer not expired : stop timer... + switchflag = POWER_TOGGLE; // ...and Toggle + } + break; + case PUSHBUTTONHOLD_INV: + if (NOT_PRESSED == button) { + Switch.hold_timer[i] = loops_per_second * Settings->param[P_HOLD_TIME] / 10; // Start timer on button press... + } + if ((PRESSED == button) && (Switch.hold_timer[i])) { + Switch.hold_timer[i] = 0; // Button released and hold timer not expired : stop timer. + switchflag = POWER_TOGGLE; // ...and Toggle + } + break; + case TOGGLEMULTI: + case FOLLOWMULTI: + case FOLLOWMULTI_INV: + if (Switch.hold_timer[i]) { + Switch.hold_timer[i] = 0; + SendKey(KEY_SWITCH, i +1, POWER_HOLD); // Execute command via MQTT + mqtt_action = POWER_HOLD; + } else { + Switch.hold_timer[i] = loops_per_second / 2; // 0.5 second multi press window + } + break; + case PUSHHOLDMULTI: + if (NOT_PRESSED == button) { + if ((Switch.hold_timer[i] & SM_TIMER_MASK) != 0) { + Switch.hold_timer[i] = ((Switch.hold_timer[i] & ~SM_TIMER_MASK) == SM_FIRST_PRESS) ? SM_SECOND_PRESS : 0; + SendKey(KEY_SWITCH, i +1, POWER_INV); // Execute command via MQTT + mqtt_action = POWER_INV; } - } else { mqtt_action = switchflag; } - } - if ((mqtt_action != POWER_NONE) && Settings->flag5.mqtt_switches) { // SetOption114 (0) - Detach Switches from relays and enable MQTT action state for all the SwitchModes - if (!Settings->flag.hass_discovery) { // SetOption19 - Control Home Assistant automatic discovery (See SetOption59) - char mqtt_state_str[16]; - char *mqtt_state = mqtt_state_str; - if (mqtt_action <= 3) { - if (mqtt_action != 3) { SendKey(KEY_SWITCH, i +1, mqtt_action); } - mqtt_state = SettingsText(SET_STATE_TXT1 + mqtt_action); + } else { + if ((Switch.hold_timer[i] & SM_TIMER_MASK) > loops_per_second * Settings->param[P_HOLD_TIME] / 25) { + if ((Switch.hold_timer[i] & ~SM_TIMER_MASK) != SM_SECOND_PRESS) { + Switch.hold_timer[i]= SM_FIRST_PRESS; + switchflag = POWER_TOGGLE; // Toggle with pushbutton + } + else{ + SendKey(KEY_SWITCH, i +1, POWER_100); // Execute command via MQTT + mqtt_action = POWER_100; + Switch.hold_timer[i]= 0; + } } else { - GetTextIndexed(mqtt_state_str, sizeof(mqtt_state_str), mqtt_action, kSwitchPressStates); + Switch.hold_timer[i]= 0; + SendKey(KEY_SWITCH, i +1, POWER_RELEASE); // Execute command via MQTT + mqtt_action = POWER_RELEASE; } - Response_P(S_JSON_SVALUE_ACTION_SVALUE, GetSwitchText(i).c_str(), mqtt_state); - char scommand[10]; - snprintf_P(scommand, sizeof(scommand), PSTR(D_JSON_SWITCH "%d"), i +1); - MqttPublishPrefixTopicRulesProcess_P(RESULT_OR_STAT, scommand); } - mqtt_action = POWER_NONE; + Switch.hold_timer[i] = (Switch.hold_timer[i] & ~SM_TIMER_MASK) | loops_per_second * Settings->param[P_HOLD_TIME] / 10; + break; + case PUSHHOLDMULTI_INV: + if (PRESSED == button) { + if ((Switch.hold_timer[i] & SM_TIMER_MASK) != 0) { + Switch.hold_timer[i] = ((Switch.hold_timer[i] & ~SM_TIMER_MASK) == SM_FIRST_PRESS) ? SM_SECOND_PRESS : 0; + SendKey(KEY_SWITCH, i +1, POWER_INV); // Execute command via MQTT + mqtt_action = POWER_INV; + } + } else { + if ((Switch.hold_timer[i] & SM_TIMER_MASK)> loops_per_second * Settings->param[P_HOLD_TIME] / 25) { + if ((Switch.hold_timer[i] & ~SM_TIMER_MASK) != SM_SECOND_PRESS) { + Switch.hold_timer[i]= SM_FIRST_PRESS; + switchflag = POWER_TOGGLE; // Toggle with pushbutton + } + else{ + SendKey(KEY_SWITCH, i +1, POWER_100); // Execute command via MQTT + mqtt_action = POWER_100; + Switch.hold_timer[i]= 0; + } + } else { + Switch.hold_timer[i]= 0; + SendKey(KEY_SWITCH, i +1, POWER_RELEASE); // Execute command via MQTT + mqtt_action = POWER_RELEASE; + } + } + Switch.hold_timer[i] = (Switch.hold_timer[i] & ~SM_TIMER_MASK) | loops_per_second * Settings->param[P_HOLD_TIME] / 10; + break; + case PUSHON: + if (PRESSED == button) { + switchflag = POWER_ON; // Power ON with pushbutton to Gnd + } + break; + case PUSHON_INV: + if (NOT_PRESSED == button) { + switchflag = POWER_ON; // Power ON with releasing pushbutton from Gnd + } + break; + case PUSH_IGNORE: + case PUSH_IGNORE_INV: + Switch.last_state[i] = button; // Update switch state before publishing + MqttPublishSensor(); + break; } + Switch.last_state[i] = button; + } + if (switchflag <= POWER_TOGGLE) { + if (!Settings->flag5.mqtt_switches) { // SetOption114 (0) - Detach Switches from relays and enable MQTT action state for all the SwitchModes + if (!SendKey(KEY_SWITCH, i +1, switchflag)) { // Execute command via MQTT + ExecuteCommandPower(i +1, switchflag, SRC_SWITCH); // Execute command internally (if i < TasmotaGlobal.devices_present) + } + } else { mqtt_action = switchflag; } + } + if ((mqtt_action != POWER_NONE) && Settings->flag5.mqtt_switches) { // SetOption114 (0) - Detach Switches from relays and enable MQTT action state for all the SwitchModes + if (!Settings->flag.hass_discovery) { // SetOption19 - Control Home Assistant automatic discovery (See SetOption59) + char mqtt_state_str[16]; + char *mqtt_state = mqtt_state_str; + if (mqtt_action <= 3) { + if (mqtt_action != 3) { SendKey(KEY_SWITCH, i +1, mqtt_action); } + mqtt_state = SettingsText(SET_STATE_TXT1 + mqtt_action); + } else { + GetTextIndexed(mqtt_state_str, sizeof(mqtt_state_str), mqtt_action, kSwitchPressStates); + } + Response_P(S_JSON_SVALUE_ACTION_SVALUE, GetSwitchText(i).c_str(), mqtt_state); + char scommand[10]; + snprintf_P(scommand, sizeof(scommand), PSTR(D_JSON_SWITCH "%d"), i +1); + MqttPublishPrefixTopicRulesProcess_P(RESULT_OR_STAT, scommand); + } + mqtt_action = POWER_NONE; } } }