diff --git a/sonoff/_changelog.ino b/sonoff/_changelog.ino index e12138049..16b3f7f6b 100644 --- a/sonoff/_changelog.ino +++ b/sonoff/_changelog.ino @@ -1,4 +1,7 @@ -/* 6.1.1.11 20180826 +/* 6.1.1.12 20180827 + * Add commands ButtonDebounce 40..1000 and SwitchDebounce 40..1000 to have more control over debounce timing. Default is 50mS (#3594) + * + * 6.1.1.11 20180826 * Change scheduler phase 1/3 - Fixed when sleep is enabled: Uptime, Delay, PulseTime and TelePeriod (#3581) * Change scheduler phase 2/3 - Fixed when sleep is enabled: Blinktime (#3581) * Change scheduler phase 3/3 - Some sensor update timings: AdcEvery 200 -> 250, Senseair 300 -> 250, SDM120 300 -> 250, SDM630 300 -> 250 diff --git a/sonoff/i18n.h b/sonoff/i18n.h index 235f0cc04..3d63f73f5 100644 --- a/sonoff/i18n.h +++ b/sonoff/i18n.h @@ -192,6 +192,8 @@ #define D_CMND_COUNTER "Counter" #define D_CMND_COUNTERTYPE "CounterType" #define D_CMND_COUNTERDEBOUNCE "CounterDebounce" +#define D_CMND_BUTTONDEBOUNCE "ButtonDebounce" +#define D_CMND_SWITCHDEBOUNCE "SwitchDebounce" #define D_CMND_SLEEP "Sleep" #define D_CMND_UPLOAD "Upload" #define D_CMND_UPGRADE "Upgrade" diff --git a/sonoff/settings.h b/sonoff/settings.h index bb3c7711b..b918b67ee 100644 --- a/sonoff/settings.h +++ b/sonoff/settings.h @@ -297,9 +297,7 @@ struct SYSCFG { char ntp_server[3][33]; // 4CE byte ina219_mode; // 531 uint16_t pulse_timer[MAX_PULSETIMERS]; // 532 - - byte free542[2]; // 542 - + uint16_t button_debounce; // 542 uint32_t ip_address[4]; // 544 unsigned long energy_kWhtotal; // 554 char mqtt_fulltopic[100]; // 558 @@ -309,8 +307,9 @@ struct SYSCFG { uint16_t pulse_counter_debounce; // 5D2 uint8_t rf_code[17][9]; // 5D4 - byte free_66d[3]; // 66D + byte free_66d[1]; // 66D + uint16_t switch_debounce; // 66E Timer timer[MAX_TIMERS]; // 670 int latitude; // 6B0 int longitude; // 6B4 @@ -321,7 +320,9 @@ struct SYSCFG { byte knx_CB_param[MAX_KNX_CB]; // 6EC Type of Output (set relay, toggle relay, reply sensor value) Mcp230xxCfg mcp230xx_config[16]; // 6F6 uint8_t mcp230xx_int_prio; // 716 - byte free_717; // 717 + + byte free_717[1]; // 717 + uint16_t mcp230xx_int_timer; // 718 byte free_71A[180]; // 71A diff --git a/sonoff/settings.ino b/sonoff/settings.ino index 7ba1b53f8..e0a40a17c 100644 --- a/sonoff/settings.ino +++ b/sonoff/settings.ino @@ -588,6 +588,9 @@ void SettingsDefaultSet2() Settings.latitude = (int)((double)LATITUDE * 1000000); Settings.longitude = (int)((double)LONGITUDE * 1000000); SettingsDefaultSet_5_13_1c(); // Time STD/DST settings + + Settings.button_debounce = KEY_DEBOUNCE_TIME; + Settings.switch_debounce = SWITCH_DEBOUNCE_TIME; } /********************************************************************************************/ @@ -787,6 +790,10 @@ void SettingsDelta() if (Settings.version < 0x06010103) { Settings.flag3.timers_enable = 1; } + if (Settings.version < 0x0601010C) { + Settings.button_debounce = KEY_DEBOUNCE_TIME; + Settings.switch_debounce = SWITCH_DEBOUNCE_TIME; + } Settings.version = VERSION; SettingsSave(1); diff --git a/sonoff/sonoff.ino b/sonoff/sonoff.ino index 6281dabaf..4feec1a9f 100755 --- a/sonoff/sonoff.ino +++ b/sonoff/sonoff.ino @@ -79,7 +79,7 @@ enum TasmotaCommands { CMND_BLINKTIME, CMND_BLINKCOUNT, CMND_SENSOR, CMND_SAVEDATA, CMND_SETOPTION, CMND_TEMPERATURE_RESOLUTION, CMND_HUMIDITY_RESOLUTION, CMND_PRESSURE_RESOLUTION, CMND_POWER_RESOLUTION, CMND_VOLTAGE_RESOLUTION, CMND_CURRENT_RESOLUTION, CMND_ENERGY_RESOLUTION, CMND_MODULE, CMND_MODULES, CMND_GPIO, CMND_GPIOS, CMND_PWM, CMND_PWMFREQUENCY, CMND_PWMRANGE, CMND_COUNTER, CMND_COUNTERTYPE, - CMND_COUNTERDEBOUNCE, CMND_SLEEP, CMND_UPGRADE, CMND_UPLOAD, CMND_OTAURL, CMND_SERIALLOG, CMND_SYSLOG, + CMND_COUNTERDEBOUNCE, CMND_BUTTONDEBOUNCE, CMND_SWITCHDEBOUNCE, CMND_SLEEP, CMND_UPGRADE, CMND_UPLOAD, CMND_OTAURL, CMND_SERIALLOG, CMND_SYSLOG, CMND_LOGHOST, CMND_LOGPORT, CMND_IPADDRESS, CMND_NTPSERVER, CMND_AP, CMND_SSID, CMND_PASSWORD, CMND_HOSTNAME, CMND_WIFICONFIG, CMND_FRIENDLYNAME, CMND_SWITCHMODE, CMND_TELEPERIOD, CMND_RESTART, CMND_RESET, CMND_TIMEZONE, CMND_TIMESTD, CMND_TIMEDST, CMND_ALTITUDE, CMND_LEDPOWER, CMND_LEDSTATE, @@ -89,7 +89,7 @@ const char kTasmotaCommands[] PROGMEM = D_CMND_BLINKTIME "|" D_CMND_BLINKCOUNT "|" D_CMND_SENSOR "|" D_CMND_SAVEDATA "|" D_CMND_SETOPTION "|" D_CMND_TEMPERATURE_RESOLUTION "|" D_CMND_HUMIDITY_RESOLUTION "|" D_CMND_PRESSURE_RESOLUTION "|" D_CMND_POWER_RESOLUTION "|" D_CMND_VOLTAGE_RESOLUTION "|" D_CMND_CURRENT_RESOLUTION "|" D_CMND_ENERGY_RESOLUTION "|" D_CMND_MODULE "|" D_CMND_MODULES "|" D_CMND_GPIO "|" D_CMND_GPIOS "|" D_CMND_PWM "|" D_CMND_PWMFREQUENCY "|" D_CMND_PWMRANGE "|" D_CMND_COUNTER "|" D_CMND_COUNTERTYPE "|" - D_CMND_COUNTERDEBOUNCE "|" D_CMND_SLEEP "|" D_CMND_UPGRADE "|" D_CMND_UPLOAD "|" D_CMND_OTAURL "|" D_CMND_SERIALLOG "|" D_CMND_SYSLOG "|" + D_CMND_COUNTERDEBOUNCE "|" D_CMND_BUTTONDEBOUNCE "|" D_CMND_SWITCHDEBOUNCE "|" D_CMND_SLEEP "|" D_CMND_UPGRADE "|" D_CMND_UPLOAD "|" D_CMND_OTAURL "|" D_CMND_SERIALLOG "|" D_CMND_SYSLOG "|" D_CMND_LOGHOST "|" D_CMND_LOGPORT "|" D_CMND_IPADDRESS "|" D_CMND_NTPSERVER "|" D_CMND_AP "|" D_CMND_SSID "|" D_CMND_PASSWORD "|" D_CMND_HOSTNAME "|" D_CMND_WIFICONFIG "|" D_CMND_FRIENDLYNAME "|" D_CMND_SWITCHMODE "|" D_CMND_TELEPERIOD "|" D_CMND_RESTART "|" D_CMND_RESET "|" D_CMND_TIMEZONE "|" D_CMND_TIMESTD "|" D_CMND_TIMEDST "|" D_CMND_ALTITUDE "|" D_CMND_LEDPOWER "|" D_CMND_LEDSTATE "|" @@ -102,20 +102,28 @@ unsigned long feature_drv1; // Compiled driver feature map unsigned long feature_drv2; // Compiled driver feature map unsigned long feature_sns1; // Compiled sensor feature map unsigned long feature_sns2; // Compiled sensor feature map +unsigned long serial_polling_window = 0; // Serial polling window +unsigned long state_second = 0; // State second timer +unsigned long state_50msecond = 0; // State 50msecond timer +unsigned long state_100msecond = 0; // State 100msecond timer +unsigned long state_250msecond = 0; // State 250msecond timer +unsigned long pulse_timer[MAX_PULSETIMERS] = { 0 }; // Power off timer +unsigned long blink_timer = 0; // Power cycle timer +unsigned long backlog_delay = 0; // Command backlog delay +unsigned long button_debounce = 0; // Button debounce timer +unsigned long switch_debounce = 0; // Switch debounce timer + +power_t power = 0; // Current copy of Settings.power + int baudrate = APP_BAUDRATE; // Serial interface baud rate SerialConfig serial_config = SERIAL_8N1; // Serial interface configuration 8 data bits, No parity, 1 stop bit byte serial_in_byte; // Received byte uint8_t serial_local = 0; // Handle serial locally; -unsigned long serial_polling_window = 0; // Serial polling window int serial_in_byte_counter = 0; // Index in receive buffer byte dual_hex_code = 0; // Sonoff dual input flag uint16_t dual_button_code = 0; // Sonoff dual received code int16_t save_data_counter; // Counter and flag for config save to Flash uint8_t fallback_topic_flag = 0; // Use Topic or FallbackTopic -unsigned long state_second = 0; // State second timer -unsigned long state_50msecond = 0; // State 50msecond timer -unsigned long state_100msecond = 0; // State 100msecond timer -unsigned long state_250msecond = 0; // State 250msecond timer uint8_t state_250mS = 0; // State 250msecond per second flag int ota_state_flag = 0; // OTA state flag int ota_result = 0; // OTA result @@ -130,8 +138,6 @@ byte web_log_index = 1; // Index in Web log buffer (should n byte reset_web_log_flag = 0; // Reset web console log byte devices_present = 0; // Max number of devices supported int status_update_timer = 0; // Refresh initial status -unsigned long pulse_timer[MAX_PULSETIMERS] = { 0 }; // Power off timer -unsigned long blink_timer = 0; // Power cycle timer uint16_t blink_counter = 0; // Number of blink cycles power_t blink_power; // Blink power state power_t blink_mask = 0; // Blink relay active mask @@ -142,7 +148,6 @@ uint8_t latching_relay_pulse = 0; // Latching relay pulse timer uint8_t backlog_index = 0; // Command backlog index uint8_t backlog_pointer = 0; // Command backlog pointer uint8_t backlog_mutex = 0; // Command backlog pending -unsigned long backlog_delay = 0; // Command backlog delay uint8_t interlock_mutex = 0; // Interlock power command pending #ifdef USE_MQTT_TLS @@ -153,7 +158,6 @@ uint8_t interlock_mutex = 0; // Interlock power command pending WiFiUDP PortUdp; // UDP Syslog and Alexa -power_t power = 0; // Current copy of Settings.power byte syslog_level; // Current copy of Settings.syslog_level uint16_t syslog_timer = 0; // Timer to re-enable syslog_level byte seriallog_level; // Current copy of Settings.seriallog_level @@ -947,6 +951,18 @@ void MqttDataHandler(char* topic, byte* data, unsigned int data_len) } snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_NVALUE, command, Settings.pulse_counter_debounce); } + else if (CMND_BUTTONDEBOUNCE == command_code) { + if ((payload > 39) && (payload < 1001)) { + Settings.button_debounce = payload16; + } + snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_NVALUE, command, Settings.button_debounce); + } + else if (CMND_SWITCHDEBOUNCE == command_code) { + if ((payload > 39) && (payload < 1001)) { + Settings.switch_debounce = payload16; + } + snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_NVALUE, command, Settings.switch_debounce); + } else if (CMND_BAUDRATE == command_code) { if (payload32 > 0) { payload32 /= 1200; // Make it a valid baudrate @@ -1631,7 +1647,8 @@ void ButtonHandler() { uint8_t button = NOT_PRESSED; uint8_t button_present = 0; - uint8_t hold_time_extent = IMMINENT_RESET_FACTOR; // Extent hold time factor in case of iminnent Reset command + uint8_t hold_time_extent = IMMINENT_RESET_FACTOR; // Extent hold time factor in case of iminnent Reset command + uint16_t loops_per_second = 1000 / Settings.button_debounce; char scmnd[20]; uint8_t maxdev = (devices_present > MAX_KEYS) ? MAX_KEYS : devices_present; @@ -1645,8 +1662,8 @@ void ButtonHandler() snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_APPLICATION D_BUTTON " " D_CODE " %04X"), dual_button_code); AddLog(LOG_LEVEL_DEBUG); button = PRESSED; - if (0xF500 == dual_button_code) { // Button hold - holdbutton[button_index] = (Settings.param[P_HOLD_TIME] * (STATES / 10)) -1; + if (0xF500 == dual_button_code) { // Button hold + holdbutton[button_index] = (loops_per_second * Settings.param[P_HOLD_TIME] / 10) -1; hold_time_extent = 1; } dual_button_code = 0; @@ -1660,28 +1677,28 @@ void ButtonHandler() if (button_present) { if (SONOFF_4CHPRO == Settings.module) { - if (holdbutton[button_index]) holdbutton[button_index]--; + if (holdbutton[button_index]) { holdbutton[button_index]--; } boolean button_pressed = false; if ((PRESSED == button) && (NOT_PRESSED == lastbutton[button_index])) { snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_APPLICATION D_BUTTON "%d " D_LEVEL_10), button_index +1); AddLog(LOG_LEVEL_DEBUG); - holdbutton[button_index] = STATES; + holdbutton[button_index] = loops_per_second; button_pressed = true; } if ((NOT_PRESSED == button) && (PRESSED == lastbutton[button_index])) { snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_APPLICATION D_BUTTON "%d " D_LEVEL_01), button_index +1); AddLog(LOG_LEVEL_DEBUG); - if (!holdbutton[button_index]) button_pressed = true; // Do not allow within 1 second + if (!holdbutton[button_index]) { button_pressed = true; } // Do not allow within 1 second } if (button_pressed) { - if (!SendKey(0, button_index +1, POWER_TOGGLE)) { // Execute Toggle command via MQTT if ButtonTopic is set + if (!SendKey(0, 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 { if ((PRESSED == button) && (NOT_PRESSED == lastbutton[button_index])) { - if (Settings.flag.button_single) { // Allow only single button press for immediate action + if (Settings.flag.button_single) { // Allow only single button press for immediate action snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_APPLICATION D_BUTTON "%d " D_IMMEDIATE), button_index +1); AddLog(LOG_LEVEL_DEBUG); if (!SendKey(0, button_index +1, POWER_TOGGLE)) { // Execute Toggle command via MQTT if ButtonTopic is set @@ -1691,7 +1708,7 @@ void ButtonHandler() multipress[button_index] = (multiwindow[button_index]) ? multipress[button_index] +1 : 1; snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_APPLICATION D_BUTTON "%d " D_MULTI_PRESS " %d"), button_index +1, multipress[button_index]); AddLog(LOG_LEVEL_DEBUG); - multiwindow[button_index] = STATES /2; // 0.5 second multi press window + multiwindow[button_index] = loops_per_second / 2; // 0.5 second multi press window } blinks = 201; } @@ -1700,20 +1717,20 @@ void ButtonHandler() holdbutton[button_index] = 0; } else { holdbutton[button_index]++; - if (Settings.flag.button_single) { // Allow only single button press for immediate action - if (holdbutton[button_index] == Settings.param[P_HOLD_TIME] * (STATES / 10) * hold_time_extent) { // Button held for factor times longer + if (Settings.flag.button_single) { // Allow only single button press for immediate action + if (holdbutton[button_index] == loops_per_second * hold_time_extent * Settings.param[P_HOLD_TIME] / 10) { // Button held for factor times longer // Settings.flag.button_single = 0; snprintf_P(scmnd, sizeof(scmnd), PSTR(D_CMND_SETOPTION "13 0")); // Disable single press only ExecuteCommand(scmnd, SRC_BUTTON); } } else { - if (Settings.flag.button_restrict) { // Button restriction - if (holdbutton[button_index] == Settings.param[P_HOLD_TIME] * (STATES / 10)) { // Button hold + if (Settings.flag.button_restrict) { // Button restriction + if (holdbutton[button_index] == loops_per_second * Settings.param[P_HOLD_TIME] / 10) { // Button hold multipress[button_index] = 0; - SendKey(0, button_index +1, 3); // Execute Hold command via MQTT if ButtonTopic is set + SendKey(0, button_index +1, 3); // Execute Hold command via MQTT if ButtonTopic is set } } else { - if (holdbutton[button_index] == (Settings.param[P_HOLD_TIME] * (STATES / 10)) * hold_time_extent) { // Button held for factor times longer + if (holdbutton[button_index] == loops_per_second * hold_time_extent * Settings.param[P_HOLD_TIME] / 10) { // Button held for factor times longer multipress[button_index] = 0; snprintf_P(scmnd, sizeof(scmnd), PSTR(D_CMND_RESET " 1")); ExecuteCommand(scmnd, SRC_BUTTON); @@ -1722,13 +1739,13 @@ void ButtonHandler() } } - if (!Settings.flag.button_single) { // Allow multi-press + if (!Settings.flag.button_single) { // Allow multi-press if (multiwindow[button_index]) { multiwindow[button_index]--; } else { if (!restart_flag && !holdbutton[button_index] && (multipress[button_index] > 0) && (multipress[button_index] < MAX_BUTTON_COMMANDS +3)) { boolean single_press = false; - if (multipress[button_index] < 3) { // Single or Double press + if (multipress[button_index] < 3) { // Single or Double press if ((SONOFF_DUAL_R2 == Settings.module) || (SONOFF_DUAL == Settings.module) || (CH4 == Settings.module)) { single_press = true; } else { @@ -1739,13 +1756,13 @@ void ButtonHandler() if (single_press && SendKey(0, button_index + multipress[button_index], POWER_TOGGLE)) { // Execute Toggle command via MQTT if ButtonTopic is set // Success } else { - if (multipress[button_index] < 3) { // Single or Double press - if (WifiState()) { // WPSconfig, Smartconfig or Wifimanager active + if (multipress[button_index] < 3) { // Single or Double press + if (WifiState()) { // WPSconfig, Smartconfig or Wifimanager active restart_flag = 1; } else { ExecuteCommandPower(button_index + multipress[button_index], POWER_TOGGLE, SRC_BUTTON); // Execute Toggle command internally } - } else { // 3 - 7 press + } else { // 3 - 7 press if (!Settings.flag.button_restrict) { snprintf_P(scmnd, sizeof(scmnd), kCommands[multipress[button_index] -3]); ExecuteCommand(scmnd, SRC_BUTTON); @@ -1770,6 +1787,7 @@ void SwitchHandler(byte mode) { uint8_t button = NOT_PRESSED; uint8_t switchflag; + uint16_t loops_per_second = 1000 / Settings.switch_debounce; for (byte i = 0; i < MAX_SWITCHES; i++) { if ((pin[GPIO_SWT1 +i] < 99) || (mode)) { @@ -1777,7 +1795,7 @@ void SwitchHandler(byte mode) if (holdwallswitch[i]) { holdwallswitch[i]--; if (0 == holdwallswitch[i]) { - SendKey(1, i +1, 3); // Execute command via MQTT + SendKey(1, i +1, 3); // Execute command via MQTT } } @@ -1816,7 +1834,7 @@ void SwitchHandler(byte mode) break; case PUSHBUTTONHOLD: if ((PRESSED == button) && (NOT_PRESSED == lastwallswitch[i])) { - holdwallswitch[i] = Settings.param[P_HOLD_TIME] * (STATES / 10); + holdwallswitch[i] = loops_per_second * Settings.param[P_HOLD_TIME] / 10; } if ((NOT_PRESSED == button) && (PRESSED == lastwallswitch[i]) && (holdwallswitch[i])) { holdwallswitch[i] = 0; @@ -1825,18 +1843,18 @@ void SwitchHandler(byte mode) break; case PUSHBUTTONHOLD_INV: if ((NOT_PRESSED == button) && (PRESSED == lastwallswitch[i])) { - holdwallswitch[i] = Settings.param[P_HOLD_TIME] * (STATES / 10); + holdwallswitch[i] = loops_per_second * Settings.param[P_HOLD_TIME] / 10; } if ((PRESSED == button) && (NOT_PRESSED == lastwallswitch[i]) && (holdwallswitch[i])) { holdwallswitch[i] = 0; - switchflag = 2; // Toggle with pushbutton to Gnd + switchflag = 2; // Toggle with pushbutton to Gnd } break; } if (switchflag < 3) { if (!SendKey(1, i +1, switchflag)) { // Execute command via MQTT - ExecuteCommandPower(i +1, switchflag, SRC_SWITCH); // Execute command internally (if i < devices_present) + ExecuteCommandPower(i +1, switchflag, SRC_SWITCH); // Execute command internally (if i < devices_present) } } @@ -1849,15 +1867,6 @@ void SwitchHandler(byte mode) /*********************************************************************************************\ * State loops \*********************************************************************************************/ - -void Every50mSeconds() -{ - // As the max amount of sleep = 250 mSec this loop will shift in time... - - ButtonHandler(); - SwitchHandler(0); -} - /*-------------------------------------------------------------------------------------------*\ * Every 0.1 second \*-------------------------------------------------------------------------------------------*/ @@ -2607,9 +2616,18 @@ void loop() OsWatchLoop(); + if (TimeReached(button_debounce)) { + SetNextTimeInterval(button_debounce, Settings.button_debounce); + ButtonHandler(); + } + + if (TimeReached(switch_debounce)) { + SetNextTimeInterval(switch_debounce, Settings.switch_debounce); + SwitchHandler(0); + } + if (TimeReached(state_50msecond)) { SetNextTimeInterval(state_50msecond, 50); - Every50mSeconds(); XdrvCall(FUNC_EVERY_50_MSECOND); XsnsCall(FUNC_EVERY_50_MSECOND); } diff --git a/sonoff/sonoff_version.h b/sonoff/sonoff_version.h index 47c6bb893..c498dccec 100644 --- a/sonoff/sonoff_version.h +++ b/sonoff/sonoff_version.h @@ -20,7 +20,7 @@ #ifndef _SONOFF_VERSION_H_ #define _SONOFF_VERSION_H_ -#define VERSION 0x0601010B +#define VERSION 0x0601010C #define D_PROGRAMNAME "Sonoff-Tasmota" #define D_AUTHOR "Theo Arends" diff --git a/sonoff/user_config.h b/sonoff/user_config.h index 0dd11520e..3988bd8be 100644 --- a/sonoff/user_config.h +++ b/sonoff/user_config.h @@ -167,7 +167,9 @@ #define APP_BLINKCOUNT 10 // [BlinkCount] Number of blinks (0 = 32000) #define APP_SLEEP 0 // [Sleep] Sleep time to lower energy consumption (0 = Off, 1 - 250 mSec) +#define KEY_DEBOUNCE_TIME 50 // [ButtonDebounce] Number of mSeconds button press debounce time #define KEY_HOLD_TIME 40 // [SetOption32] Number of 0.1 seconds to hold Button or external Pushbutton before sending HOLD message +#define SWITCH_DEBOUNCE_TIME 50 // [SwitchDebounce] Number of mSeconds switch press debounce time #define SWITCH_MODE TOGGLE // [SwitchMode] TOGGLE, FOLLOW, FOLLOW_INV, PUSHBUTTON, PUSHBUTTON_INV, PUSHBUTTONHOLD, PUSHBUTTONHOLD_INV, PUSHBUTTON_TOGGLE (the wall switch state) #define WS2812_LEDS 30 // [Pixels] Number of WS2812 LEDs to start with (max is 512)