Fix ESP32 touch button multi-press and hold detection

Fix ESP32 touch button multi-press and hold detection (#16596)
This commit is contained in:
Theo Arends 2022-09-27 14:31:21 +02:00
parent 2f61040891
commit 3a4a82ba0a
7 changed files with 64 additions and 56 deletions

View File

@ -3,7 +3,17 @@ All notable changes to this project will be documented in this file.
## [Unreleased] - Development
## [12.1.1.2]
## [12.1.1.3]
### Added
### Changed
### Fixed
- ESP32 touch button multi-press and hold detection (#16596)
### Removed
## [12.1.1.2] 20220927
### Added
- Berry has persistent MQTT subscriptions: auto-subscribe at (re)connection
- Berry automated solidification of code
@ -22,10 +32,6 @@ All notable changes to this project will be documented in this file.
- Zigbee report unprocessed attributes
- Platformio one Platform for all Tasmota frameworks Core32 2.0.5 (#16644)
### Fixed
### Removed
## [12.1.1.1] 20220910
### Added
- Support for SGP40 gas and air quality sensor (#16341)

View File

@ -107,7 +107,7 @@ The latter links can be used for OTA upgrades too like ``OtaUrl http://ota.tasmo
[Complete list](BUILDS.md) of available feature and sensors.
## Changelog v12.1.1.2
## Changelog v12.1.1.3
### Added
- Command ``SetOption46 0..255`` to add 0..255 * 10 milliseconds power on delay before initializing I/O [#15438](https://github.com/arendst/Tasmota/issues/15438)
- Command ``SetOption146 1`` to enable display of ESP32 internal temperature
@ -148,5 +148,6 @@ The latter links can be used for OTA upgrades too like ``OtaUrl http://ota.tasmo
- Button response delay regression from v12.0.2.4 [#16319](https://github.com/arendst/Tasmota/issues/16319)
- Lost module name in GUI regression from v12.0.2.4 - 20220803 [#16324](https://github.com/arendst/Tasmota/issues/16324)
- Removed whitespace from JSON values with no decimals [#16365](https://github.com/arendst/Tasmota/issues/16365)
- ESP32 touch button multi-press and hold detection [#16596](https://github.com/arendst/Tasmota/issues/16596)
### Removed

View File

@ -465,7 +465,9 @@ const uint16_t kGpioNiceList[] PROGMEM = {
AGPIO(GPIO_KEY1_INV_NP) + MAX_KEYS,
#ifdef ESP32
AGPIO(GPIO_KEY1_INV_PD) + MAX_KEYS,
#if defined(SOC_TOUCH_VERSION_1) || defined(SOC_TOUCH_VERSION_2)
AGPIO(GPIO_KEY1_TC) + MAX_KEYS, // Touch button
#endif // ESP32 SOC_TOUCH_VERSION_1 or SOC_TOUCH_VERSION_2
#endif
AGPIO(GPIO_SWT1) + MAX_SWITCHES, // User connected external switches
AGPIO(GPIO_SWT1_NP) + MAX_SWITCHES,

View File

@ -20,6 +20,6 @@
#ifndef _TASMOTA_VERSION_H_
#define _TASMOTA_VERSION_H_
const uint32_t VERSION = 0x0C010102; // 12.1.1.2
const uint32_t VERSION = 0x0C010103; // 12.1.1.3
#endif // _TASMOTA_VERSION_H_

View File

@ -46,9 +46,6 @@ struct BUTTON {
uint32_t no_pullup_mask = 0; // key no pullup flag (1 = no pullup)
uint32_t pulldown_mask = 0; // key pulldown flag (1 = pulldown)
uint32_t inverted_mask = 0; // Key inverted flag (1 = inverted)
#ifdef ESP32
uint32_t touch_mask = 0; // Touch flag (1 = inverted)
#endif // ESP32
uint16_t hold_timer[MAX_KEYS] = { 0 }; // Timer for button hold
uint16_t dual_code = 0; // Sonoff dual received code
uint8_t state[MAX_KEYS] = { 0 };
@ -57,20 +54,19 @@ struct BUTTON {
uint8_t window_timer[MAX_KEYS] = { 0 }; // Max time between button presses to record press count
uint8_t press_counter[MAX_KEYS] = { 0 }; // Number of button presses within Button.window_timer
uint8_t dual_receive_count = 0; // Sonoff dual input flag
#ifdef ESP32
uint8_t touch_hits[MAX_KEYS] = { 0 }; // Hits in a row to filter out noise
#endif // ESP32
uint8_t first_change = 0;
uint8_t present = 0; // Number of buttons found flag
uint8_t mutex;
} Button;
#ifdef ESP32
#if defined(SOC_TOUCH_VERSION_1) || defined(SOC_TOUCH_VERSION_2)
struct TOUCH_BUTTON {
uint32_t touch_mask = 0; // Touch flag (1 = enabled)
uint32_t calibration = 0; // Bitfield
uint32_t pin_threshold = TOUCH_PIN_THRESHOLD;
uint8_t hit_threshold = TOUCH_HIT_THRESHOLD;
uint8_t hits[MAX_KEYS] = { 0 }; // Hits in a row to filter out noise
} TOUCH_BUTTON;
#endif // ESP32
#endif // ESP32 SOC_TOUCH_VERSION_1 or SOC_TOUCH_VERSION_2
/********************************************************************************************/
@ -86,16 +82,17 @@ void ButtonInvertFlag(uint32_t button_bit) {
bitSet(Button.inverted_mask, button_bit);
}
#ifdef ESP32
#if defined(SOC_TOUCH_VERSION_1) || defined(SOC_TOUCH_VERSION_2)
void ButtonTouchFlag(uint32_t button_bit) {
bitSet(Button.touch_mask, button_bit);
bitSet(TOUCH_BUTTON.touch_mask, button_bit);
}
#endif // ESP32
#endif // ESP32 SOC_TOUCH_VERSION_1 or SOC_TOUCH_VERSION_2
/*********************************************************************************************/
void ButtonProbe(void) {
if (TasmotaGlobal.uptime < 4) { return; } // Block GPIO for 4 seconds after poweron to workaround Wemos D1 / Obi RTS circuit
if (Button.mutex || (TasmotaGlobal.uptime < 4)) { return; } // Block GPIO for 4 seconds after poweron to workaround Wemos D1 / Obi RTS circuit
Button.mutex = 1;
uint32_t state_filter;
uint32_t first_change = Button.first_change;
@ -119,8 +116,17 @@ void ButtonProbe(void) {
for (uint32_t i = 0; i < MAX_KEYS; i++) {
if (!PinUsed(GPIO_KEY1, i)) { continue; }
// Olimex user_switch2.c code to fix 50Hz induced pulses
if (digitalRead(Pin(GPIO_KEY1, i)) != bitRead(Button.inverted_mask, i)) {
bool button_not_activated;
#if defined(SOC_TOUCH_VERSION_1) || defined(SOC_TOUCH_VERSION_2)
if (bitRead(TOUCH_BUTTON.touch_mask, i)) {
if (ac_detect || bitRead(TOUCH_BUTTON.calibration, i +1)) { continue; } // Touch is slow. Takes 21mS to read
uint32_t value = touchRead(Pin(GPIO_KEY1, i));
button_not_activated = ((value == 0) || (value > TOUCH_BUTTON.pin_threshold));
} else
#endif // ESP32 SOC_TOUCH_VERSION_1 or SOC_TOUCH_VERSION_2
button_not_activated = (digitalRead(Pin(GPIO_KEY1, i)) != bitRead(Button.inverted_mask, i));
if (button_not_activated) {
if (ac_detect) { // Enabled with ButtonDebounce x9
Button.state[i] |= 0x80;
@ -194,6 +200,7 @@ void ButtonProbe(void) {
}
}
}
Button.mutex = 0;
}
void ButtonInit(void) {
@ -295,36 +302,23 @@ void ButtonHandler(void) {
} else
#endif // ESP8266
if (PinUsed(GPIO_KEY1, button_index)) {
button_present = 1;
#ifdef ESP32
#ifndef CONFIG_IDF_TARGET_ESP32C3
if (bitRead(Button.touch_mask, button_index)) { // Touch
#if defined(SOC_TOUCH_VERSION_1) || defined(SOC_TOUCH_VERSION_2)
if (bitRead(TOUCH_BUTTON.touch_mask, button_index) && bitRead(TOUCH_BUTTON.calibration, button_index +1)) { // Touch
uint32_t _value = touchRead(Pin(GPIO_KEY1, button_index));
button = NOT_PRESSED;
if (_value != 0) { // Probably read-error
if (_value < TOUCH_BUTTON.pin_threshold) {
if (++Button.touch_hits[button_index] > TOUCH_BUTTON.hit_threshold) {
if (!bitRead(TOUCH_BUTTON.calibration, button_index+1)) {
button = PRESSED;
}
}
if ((_value > 0) && (_value < TOUCH_BUTTON.pin_threshold)) { // Probably read-error (0)
TOUCH_BUTTON.hits[button_index]++;
} else {
Button.touch_hits[button_index] = 0;
}
} else {
Button.touch_hits[button_index] = 0;
}
if (bitRead(TOUCH_BUTTON.calibration, button_index+1)) {
AddLog(LOG_LEVEL_INFO, PSTR("PLOT: %u, %u, %u,"), button_index+1, _value, Button.touch_hits[button_index]); // Button number (1..4), value, continuous hits under threshold
TOUCH_BUTTON.hits[button_index] = 0;
}
AddLog(LOG_LEVEL_INFO, PSTR("PLOT: %u, %u, %u,"), button_index +1, _value, TOUCH_BUTTON.hits[button_index]); // Button number (1..4), value, continuous hits under threshold
continue;
} else
#endif // not ESP32C3
#endif // ESP32
{ // Normal button
// button = (digitalRead(Pin(GPIO_KEY1, button_index)) != bitRead(Button.inverted_mask, button_index));
#endif // ESP32 SOC_TOUCH_VERSION_1 or SOC_TOUCH_VERSION_2
button_present = 1;
button = Button.virtual_state[button_index];
}
}
#ifdef USE_ADC
else if (PinUsed(GPIO_ADC_BUTTON, button_index)) {
button_present = 1;

View File

@ -46,7 +46,11 @@ const char kTasmotaCommands[] PROGMEM = "|" // No prefix
#endif // USE_DEVICE_GROUPS
D_CMND_SETSENSOR "|" D_CMND_SENSOR "|" D_CMND_DRIVER "|" D_CMND_JSON
#ifdef ESP32
"|Info|" D_CMND_TOUCH_CAL "|" D_CMND_TOUCH_THRES "|" D_CMND_TOUCH_NUM "|" D_CMND_CPU_FREQUENCY
"|Info|"
#if defined(SOC_TOUCH_VERSION_1) || defined(SOC_TOUCH_VERSION_2)
D_CMND_TOUCH_CAL "|" D_CMND_TOUCH_THRES "|"
#endif // ESP32 SOC_TOUCH_VERSION_1 or SOC_TOUCH_VERSION_2
D_CMND_CPU_FREQUENCY
#endif // ESP32
#endif //FIRMWARE_MINIMAL_ONLY
;
@ -81,7 +85,11 @@ void (* const TasmotaCommand[])(void) PROGMEM = {
#endif // USE_DEVICE_GROUPS
&CmndSetSensor, &CmndSensor, &CmndDriver, &CmndJson
#ifdef ESP32
, &CmndInfo, &CmndTouchCal, &CmndTouchThres, &CmndTouchNum, &CmndCpuFrequency
, &CmndInfo,
#if defined(SOC_TOUCH_VERSION_1) || defined(SOC_TOUCH_VERSION_2)
&CmndTouchCal, &CmndTouchThres,
#endif // ESP32 SOC_TOUCH_VERSION_1 or SOC_TOUCH_VERSION_2
&CmndCpuFrequency
#endif // ESP32
#endif //FIRMWARE_MINIMAL_ONLY
};
@ -2608,6 +2616,7 @@ void CmndCpuFrequency(void) {
ResponseCmndNumber(getCpuFrequencyMhz());
}
#if defined(SOC_TOUCH_VERSION_1) || defined(SOC_TOUCH_VERSION_2)
void CmndTouchCal(void) {
if (XdrvMailbox.payload >= 0) {
if (XdrvMailbox.payload == 0) {
@ -2630,12 +2639,6 @@ void CmndTouchThres(void) {
}
ResponseCmndNumber(TOUCH_BUTTON.pin_threshold);
}
void CmndTouchNum(void) {
if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < 32)) {
TOUCH_BUTTON.hit_threshold = XdrvMailbox.payload;
}
ResponseCmndNumber(TOUCH_BUTTON.hit_threshold);
}
#endif // ESP32 SOC_TOUCH_VERSION_1 or SOC_TOUCH_VERSION_2
#endif // ESP32

View File

@ -1993,10 +1993,12 @@ void GpioInit(void)
ButtonInvertFlag(mpin - AGPIO(GPIO_KEY1_INV_PD)); // 0 .. 3
mpin -= (AGPIO(GPIO_KEY1_INV_PD) - AGPIO(GPIO_KEY1));
}
#if defined(SOC_TOUCH_VERSION_1) || defined(SOC_TOUCH_VERSION_2)
else if ((mpin >= AGPIO(GPIO_KEY1_TC)) && (mpin < (AGPIO(GPIO_KEY1_TC) + MAX_KEYS))) {
ButtonTouchFlag(mpin - AGPIO(GPIO_KEY1_TC)); // 0 .. 3
mpin -= (AGPIO(GPIO_KEY1_TC) - AGPIO(GPIO_KEY1));
}
#endif // ESP32 SOC_TOUCH_VERSION_1 or SOC_TOUCH_VERSION_2
#endif //ESP32
else if ((mpin >= AGPIO(GPIO_REL1_INV)) && (mpin < (AGPIO(GPIO_REL1_INV) + MAX_RELAYS))) {
bitSet(TasmotaGlobal.rel_inverted, mpin - AGPIO(GPIO_REL1_INV));