Let the page buttons keep up with the finger: Next, Next, Next reaches page 4 (0.2.72)
The Previous and Next bars ran their tap through the tile guard, whose 600 ms same-tile window (so one contact never fires a tile twice) dropped the second and third tap of a quick Next, Next, Next. They now use accept_repeat, the -/+ keys' rule since 0.2.25: every clean tap counts, only a contact within 150 ms of the last accepted tap on the same button is a bounce. The page fill already dropped a fill under way when the next page was asked for. Firmware 0.2.61; test_cyd_ui.cpp covers the page buttons; host harness and check builds in docs/TEST_RESULTS_0272.md. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
@@ -73,8 +73,9 @@ class TouchGuard {
|
|||||||
if (moved_) { reject_ = MOVED; return false; }
|
if (moved_) { reject_ = MOVED; return false; }
|
||||||
return accept_slider(now, tile);
|
return accept_slider(now, tile);
|
||||||
}
|
}
|
||||||
// For -/+ keys: every clean tap counts, even the third within a second, so a
|
// For -/+ keys and the page buttons: every clean tap counts, even the third within a second,
|
||||||
// setpoint moves several steps in one go. Only bounce (same key within `gap`) is dropped.
|
// so a setpoint moves several steps in one go and Next, Next, Next reaches page 4 without
|
||||||
|
// waiting for each page to draw. Only bounce (same key within `gap`) is dropped.
|
||||||
bool accept_repeat(uint32_t now, int tile, uint32_t gap = 150) {
|
bool accept_repeat(uint32_t now, int tile, uint32_t gap = 150) {
|
||||||
if (moved_) { reject_ = MOVED; return false; }
|
if (moved_) { reject_ = MOVED; return false; }
|
||||||
if (accepted_) { reject_ = USED; return false; }
|
if (accepted_) { reject_ = USED; return false; }
|
||||||
|
|||||||
@@ -176,6 +176,21 @@ icons sit off-center in the browser); run `generate_packages.py` afterward.
|
|||||||
The `MDI_GLYPH_*` substitutions are retired: `TILEn_ICON` in manual
|
The `MDI_GLYPH_*` substitutions are retired: `TILEn_ICON` in manual
|
||||||
profiles must come from the set. No changed preferences or keys.
|
profiles must come from the set. No changed preferences or keys.
|
||||||
|
|
||||||
|
### Compatibility 0.2.72 / firmware 0.2.61
|
||||||
|
|
||||||
|
Firmware only (both board profiles; `cyd_ui.h` gains a comment); the app raises `FIRMWARE_VERSION`. No protocol,
|
||||||
|
storage or editor change, and an older app drives this firmware as before.
|
||||||
|
|
||||||
|
- The `< Previous` and `Next >` bars on `home_page` call `cyd::touch_guard.accept_repeat(millis(), 11 / 12)` instead
|
||||||
|
of `accept()`. `accept()` keeps a 600 ms window per tile so one contact can never fire a tile's action twice; on the
|
||||||
|
page bar that window dropped the second and third tap of Next, Next, Next, so page 4 took three waits of about
|
||||||
|
half a second. `accept_repeat`, the -/+ keys' rule since 0.2.25, still drops a contact shorter than 40 ms (20 ms on
|
||||||
|
the Guition), a contact that moved, a contact that already counted, and a tap within 150 ms of the last accepted
|
||||||
|
tap on the same button (bounce). `show_page` already drops a fill still under way when the next page is asked for,
|
||||||
|
so nothing else changed. `tests/test_cyd_ui.cpp` covers the page buttons.
|
||||||
|
- The settings page's own pager (`settings_screen.h`) never used the guard and is unchanged. Tile taps, cards, keys,
|
||||||
|
chips and sliders keep `accept()` and its window.
|
||||||
|
|
||||||
### Compatibility 0.2.71 / firmware 0.2.60
|
### Compatibility 0.2.71 / firmware 0.2.60
|
||||||
|
|
||||||
Firmware only (`runtime_model.h`, `runtime_tiles.h`); the app raises `FIRMWARE_VERSION`. No protocol, storage or
|
Firmware only (`runtime_model.h`, `runtime_tiles.h`); the app raises `FIRMWARE_VERSION`. No protocol, storage or
|
||||||
|
|||||||
@@ -0,0 +1,76 @@
|
|||||||
|
# Test results app 0.2.72 / firmware 0.2.61 (2026-09-18)
|
||||||
|
|
||||||
|
Max: "als ik snel naar pagina 4 wil, kan ik niet snel doorklikken, dan moet je steeds wachten totdat de pagina geladen
|
||||||
|
is". Next, Next, Next on the page bar took three waits of about half a second each. This release lets the page buttons
|
||||||
|
keep up with the finger. See CHANGELOG 0.2.72 and the compatibility note in docs/RELEASING.md. Built on 0.2.71
|
||||||
|
(`2cb3479`).
|
||||||
|
|
||||||
|
## What happened before
|
||||||
|
|
||||||
|
- The `< Previous` and `Next >` bars ran their tap through `cyd::touch_guard.accept()`, the guard of every tile: it
|
||||||
|
drops a second tap on the same tile within 600 ms as a bounce, so one contact can never switch a light twice. On
|
||||||
|
the page bar that window dropped the second and third tap of Next, Next, Next. The page itself was never the
|
||||||
|
problem: since 0.2.42 a page switch places the new page in the next frame and fills its cards two per refresh, and
|
||||||
|
a new switch drops a fill still under way.
|
||||||
|
- The -/+ keys of the setpoint pill had the same complaint in 0.2.25 and got `accept_repeat()`: every clean tap
|
||||||
|
counts, only a contact within 150 ms of the last one on the same key is a bounce. The page buttons now use it.
|
||||||
|
|
||||||
|
## Automated
|
||||||
|
|
||||||
|
- C++ (`clang++ -std=c++17 -Wall -Wextra -Werror -I.`): 17/17 PASS in a clean export of the committed tree plus this
|
||||||
|
round's changes. `tests/test_cyd_ui.cpp` now holds the page buttons: three Next taps 250 ms apart all count, the
|
||||||
|
old `accept()` still refuses within its window, and Previous right after Next counts.
|
||||||
|
- Python (`.venv-portal`): 386 tests OK, the same set as 0.2.71 (firmware only; the app raises `FIRMWARE_VERSION`).
|
||||||
|
- `generate_packages.py --check` green.
|
||||||
|
- The working tree also carried another session's uncommitted `runtime_model.h` / `runtime_tiles.h` work (the
|
||||||
|
48-tile round, 0.2.73); every build and test below ran from a clean export with only this round's changes.
|
||||||
|
|
||||||
|
## Firmware on the Mac (host builds, driven over the ESPHome API)
|
||||||
|
|
||||||
|
`pager_host.py` (scratchpad, after `wake_sleep_host.py`): the living room layout of the demo home on 8099 (17 tiles,
|
||||||
|
four pages), a `pager_taps` action that taps the page bar from an LVGL timer (`count` taps `gap_ms` apart, each a
|
||||||
|
90 ms contact ending in `LV_EVENT_SHORT_CLICKED` on the button, as the touch layer produces it), an `old_rule` mode
|
||||||
|
that taps the way firmware 0.2.60 did, and a probe that logs the page, the page number label and the fill state.
|
||||||
|
**Guition 12/12, CYD 12/12** (the first run judged the bounce and old-rule bursts by the spacing asked for, and
|
||||||
|
the host's LVGL loop stalls about a second on the first frame of a burst, macOS throttling the SDL window, so
|
||||||
|
two taps meant to be 80 ms apart landed 1040 ms apart; the checks now judge every tap against the spacing that
|
||||||
|
really happened, and the second run passed on both boards).
|
||||||
|
|
||||||
|
| Check | Guition | CYD |
|
||||||
|
|---|---|---|
|
||||||
|
| The living room layout shows four pages, page 1 drawn | pass | pass |
|
||||||
|
| Next, Next, Next 200 ms apart lands on page 4 (4 / 4) | pass | pass |
|
||||||
|
| Every one of those taps moved a page: 2, 3, 4, none refused | pass | pass |
|
||||||
|
| Page 4 finishes drawing afterwards | pass | pass |
|
||||||
|
| Two more Next taps on the last page change nothing | pass | pass |
|
||||||
|
| Previous, Previous, Previous 200 ms apart lands on page 1 | pass | pass |
|
||||||
|
| Three Next taps 160 ms apart (six a second) land on page 4 | pass | pass |
|
||||||
|
| Three Previous taps 160 ms apart land on page 1 | pass | pass |
|
||||||
|
| Bounce: a Next within 150 ms of the last accepted tap is refused, a slower one counts and moves a page | pass | pass |
|
||||||
|
| The old rule (0.2.60): a Next within 600 ms of the last accepted tap was refused | pass | pass |
|
||||||
|
| Back on page 1 afterwards | pass | pass |
|
||||||
|
| No errors in the log | pass | pass |
|
||||||
|
|
||||||
|
The bounce burst on the Guition landed at 98, 94 and 81 ms and was accepted, refused, accepted, refused, as the
|
||||||
|
rule says (the third tap came 192 ms after the first accepted one); the CYD's at 84, 84 and 96 ms the same way. The
|
||||||
|
old-rule burst on the Guition landed at 208, 202 and 203 ms and was accepted, refused, refused, accepted: that is
|
||||||
|
what Max felt, one page per 600 ms, however fast he tapped.
|
||||||
|
|
||||||
|
## Builds (ESPHome 2026.6.2)
|
||||||
|
|
||||||
|
Check profiles with the generated packages and local components; no errors or compiler warnings.
|
||||||
|
|
||||||
|
| Profile | RAM | Flash | vs 0.2.71 |
|
||||||
|
|---|---|---|---|
|
||||||
|
| Guition | 25.7% (84,372 B) | 21.0% (1,708,943 B) | same RAM, -24 B flash |
|
||||||
|
| CYD | 24.0% (78,796 B) | 79.2% (1,453,647 B) | same RAM, -36 B flash |
|
||||||
|
|
||||||
|
The check builds carry a device name of their own (`check-<board>-f9`) and placeholder Wi-Fi, API and OTA values, so
|
||||||
|
the sizes differ from the bench profiles by the length of those strings; the code is the same.
|
||||||
|
|
||||||
|
## Not tested
|
||||||
|
|
||||||
|
- Real hardware: neither bench screen ran this firmware before the release. Max's own screens get it through Update;
|
||||||
|
the tap that started this is the one to try there, on the Guition and on the CYD.
|
||||||
|
- A resistive panel's lift-off bounce on the CYD is covered by the 40 ms minimum contact and the 150 ms gap, the same
|
||||||
|
net the -/+ keys have had since 0.2.25 (physically tested then), not by a new measurement.
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
# Source profile for packages/guition.yaml (tools/generate_packages.py); see docs/GUITION.md.
|
# Source profile for packages/guition.yaml (tools/generate_packages.py); see docs/GUITION.md.
|
||||||
# A screen gets its tiles from the ESP Screen Manager add-on while it runs. The CYD is its own profile.
|
# A screen gets its tiles from the ESP Screen Manager add-on while it runs. The CYD is its own profile.
|
||||||
substitutions:
|
substitutions:
|
||||||
SCREEN_FIRMWARE_VERSION: "0.2.60"
|
SCREEN_FIRMWARE_VERSION: "0.2.61"
|
||||||
# 0: no drift limit; LVGL decides, releasing inside the tile is a tap, leaving it is not
|
# 0: no drift limit; LVGL decides, releasing inside the tile is a tap, leaving it is not
|
||||||
# (like iOS). The CYD keeps a limit because its resistive panel jumps.
|
# (like iOS). The CYD keeps a limit because its resistive panel jumps.
|
||||||
TOUCH_MOVE_LIMIT_PX: "0"
|
TOUCH_MOVE_LIMIT_PX: "0"
|
||||||
@@ -2438,7 +2438,7 @@ lvgl:
|
|||||||
on_short_click:
|
on_short_click:
|
||||||
- if:
|
- if:
|
||||||
condition:
|
condition:
|
||||||
lambda: 'return cyd::touch_guard.accept(millis(), 11);'
|
lambda: 'return cyd::touch_guard.accept_repeat(millis(), 11);'
|
||||||
then:
|
then:
|
||||||
- lambda: 'id(tile_page) = std::max(0, id(tile_page) - 1);'
|
- lambda: 'id(tile_page) = std::max(0, id(tile_page) - 1);'
|
||||||
- script.execute: show_tile_page
|
- script.execute: show_tile_page
|
||||||
@@ -2479,7 +2479,7 @@ lvgl:
|
|||||||
on_short_click:
|
on_short_click:
|
||||||
- if:
|
- if:
|
||||||
condition:
|
condition:
|
||||||
lambda: 'return cyd::touch_guard.accept(millis(), 12);'
|
lambda: 'return cyd::touch_guard.accept_repeat(millis(), 12);'
|
||||||
then:
|
then:
|
||||||
- lambda: 'id(tile_page) += 1;'
|
- lambda: 'id(tile_page) += 1;'
|
||||||
- script.execute: show_tile_page
|
- script.execute: show_tile_page
|
||||||
|
|||||||
@@ -17,7 +17,7 @@
|
|||||||
############################################################
|
############################################################
|
||||||
|
|
||||||
substitutions:
|
substitutions:
|
||||||
SCREEN_FIRMWARE_VERSION: "0.2.60"
|
SCREEN_FIRMWARE_VERSION: "0.2.61"
|
||||||
# A tap survives a finger that drifts up to this far (320 px over 58 mm: 1 cm); further is a swipe.
|
# A tap survives a finger that drifts up to this far (320 px over 58 mm: 1 cm); further is a swipe.
|
||||||
TOUCH_MOVE_LIMIT_PX: "56"
|
TOUCH_MOVE_LIMIT_PX: "56"
|
||||||
# Shortest contact that counts as a tap; a resistive panel bounces, a capacitive one does not.
|
# Shortest contact that counts as a tap; a resistive panel bounces, a capacitive one does not.
|
||||||
@@ -2351,7 +2351,7 @@ lvgl:
|
|||||||
on_short_click:
|
on_short_click:
|
||||||
- if:
|
- if:
|
||||||
condition:
|
condition:
|
||||||
lambda: 'return cyd::touch_guard.accept(millis(), 11);'
|
lambda: 'return cyd::touch_guard.accept_repeat(millis(), 11);'
|
||||||
then:
|
then:
|
||||||
- lambda: 'id(tile_page) = std::max(0, id(tile_page) - 1);'
|
- lambda: 'id(tile_page) = std::max(0, id(tile_page) - 1);'
|
||||||
- script.execute: show_tile_page
|
- script.execute: show_tile_page
|
||||||
@@ -2392,7 +2392,7 @@ lvgl:
|
|||||||
on_short_click:
|
on_short_click:
|
||||||
- if:
|
- if:
|
||||||
condition:
|
condition:
|
||||||
lambda: 'return cyd::touch_guard.accept(millis(), 12);'
|
lambda: 'return cyd::touch_guard.accept_repeat(millis(), 12);'
|
||||||
then:
|
then:
|
||||||
- lambda: 'id(tile_page) += 1;'
|
- lambda: 'id(tile_page) += 1;'
|
||||||
- script.execute: show_tile_page
|
- script.execute: show_tile_page
|
||||||
|
|||||||
+3
-3
@@ -18,7 +18,7 @@
|
|||||||
############################################################
|
############################################################
|
||||||
|
|
||||||
substitutions:
|
substitutions:
|
||||||
SCREEN_FIRMWARE_VERSION: "0.2.60"
|
SCREEN_FIRMWARE_VERSION: "0.2.61"
|
||||||
# A tap survives a finger that drifts up to this far (320 px over 58 mm: 1 cm); further is a swipe.
|
# A tap survives a finger that drifts up to this far (320 px over 58 mm: 1 cm); further is a swipe.
|
||||||
TOUCH_MOVE_LIMIT_PX: "56"
|
TOUCH_MOVE_LIMIT_PX: "56"
|
||||||
# Shortest contact that counts as a tap; a resistive panel bounces, a capacitive one does not.
|
# Shortest contact that counts as a tap; a resistive panel bounces, a capacitive one does not.
|
||||||
@@ -2340,7 +2340,7 @@ lvgl:
|
|||||||
on_short_click:
|
on_short_click:
|
||||||
- if:
|
- if:
|
||||||
condition:
|
condition:
|
||||||
lambda: 'return cyd::touch_guard.accept(millis(), 11);'
|
lambda: 'return cyd::touch_guard.accept_repeat(millis(), 11);'
|
||||||
then:
|
then:
|
||||||
- lambda: 'id(tile_page) = std::max(0, id(tile_page) - 1);'
|
- lambda: 'id(tile_page) = std::max(0, id(tile_page) - 1);'
|
||||||
- script.execute: show_tile_page
|
- script.execute: show_tile_page
|
||||||
@@ -2381,7 +2381,7 @@ lvgl:
|
|||||||
on_short_click:
|
on_short_click:
|
||||||
- if:
|
- if:
|
||||||
condition:
|
condition:
|
||||||
lambda: 'return cyd::touch_guard.accept(millis(), 12);'
|
lambda: 'return cyd::touch_guard.accept_repeat(millis(), 12);'
|
||||||
then:
|
then:
|
||||||
- lambda: 'id(tile_page) += 1;'
|
- lambda: 'id(tile_page) += 1;'
|
||||||
- script.execute: show_tile_page
|
- script.execute: show_tile_page
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
# Source profile for packages/guition.yaml (tools/generate_packages.py); see docs/GUITION.md.
|
# Source profile for packages/guition.yaml (tools/generate_packages.py); see docs/GUITION.md.
|
||||||
# A screen gets its tiles from the ESP Screen Manager add-on while it runs. The CYD is its own profile.
|
# A screen gets its tiles from the ESP Screen Manager add-on while it runs. The CYD is its own profile.
|
||||||
substitutions:
|
substitutions:
|
||||||
SCREEN_FIRMWARE_VERSION: "0.2.60"
|
SCREEN_FIRMWARE_VERSION: "0.2.61"
|
||||||
# 0: no drift limit; LVGL decides, releasing inside the tile is a tap, leaving it is not
|
# 0: no drift limit; LVGL decides, releasing inside the tile is a tap, leaving it is not
|
||||||
# (like iOS). The CYD keeps a limit because its resistive panel jumps.
|
# (like iOS). The CYD keeps a limit because its resistive panel jumps.
|
||||||
TOUCH_MOVE_LIMIT_PX: "0"
|
TOUCH_MOVE_LIMIT_PX: "0"
|
||||||
@@ -2427,7 +2427,7 @@ lvgl:
|
|||||||
on_short_click:
|
on_short_click:
|
||||||
- if:
|
- if:
|
||||||
condition:
|
condition:
|
||||||
lambda: 'return cyd::touch_guard.accept(millis(), 11);'
|
lambda: 'return cyd::touch_guard.accept_repeat(millis(), 11);'
|
||||||
then:
|
then:
|
||||||
- lambda: 'id(tile_page) = std::max(0, id(tile_page) - 1);'
|
- lambda: 'id(tile_page) = std::max(0, id(tile_page) - 1);'
|
||||||
- script.execute: show_tile_page
|
- script.execute: show_tile_page
|
||||||
@@ -2468,7 +2468,7 @@ lvgl:
|
|||||||
on_short_click:
|
on_short_click:
|
||||||
- if:
|
- if:
|
||||||
condition:
|
condition:
|
||||||
lambda: 'return cyd::touch_guard.accept(millis(), 12);'
|
lambda: 'return cyd::touch_guard.accept_repeat(millis(), 12);'
|
||||||
then:
|
then:
|
||||||
- lambda: 'id(tile_page) += 1;'
|
- lambda: 'id(tile_page) += 1;'
|
||||||
- script.execute: show_tile_page
|
- script.execute: show_tile_page
|
||||||
|
|||||||
@@ -1,3 +1,11 @@
|
|||||||
|
## 0.2.72 (firmware 0.2.61)
|
||||||
|
|
||||||
|
Next, Next, Next: the page buttons keep up with your finger.
|
||||||
|
|
||||||
|
- **Tap through the pages as fast as you like.** Tap Next three times in a row and you are on page 4, whether or not the pages in between had finished drawing. Before, a second tap on the same button within 600 ms was dropped as a bounce, the rule that keeps a light from switching twice on one tap, so every page made you wait before the next tap counted. The page buttons now follow the rule of the -/+ keys: every clean tap counts, and only a contact within 150 ms of the last one on the same button is taken for a bounce.
|
||||||
|
- **Previous does the same**, and tapping past the first or last page still changes nothing.
|
||||||
|
- Needs firmware 0.2.61: press **Update** on the screen. Includes everything from 0.2.71.
|
||||||
|
|
||||||
## 0.2.71 (firmware 0.2.60)
|
## 0.2.71 (firmware 0.2.60)
|
||||||
|
|
||||||
A small slider stays where you leave it while the lights fade towards it.
|
A small slider stays where you leave it while the lights fade towards it.
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ WEEKDAYS = ['Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa', 'Su']
|
|||||||
REPO = 'https://github.com/MaxGramser/homeassistant_espscreen'
|
REPO = 'https://github.com/MaxGramser/homeassistant_espscreen'
|
||||||
REFS = {'cyd': 'main', 'guition': 'main'}
|
REFS = {'cyd': 'main', 'guition': 'main'}
|
||||||
# Firmware shipped with this app release; screens below it get an update offer.
|
# Firmware shipped with this app release; screens below it get an update offer.
|
||||||
FIRMWARE_VERSION = '0.2.60'
|
FIRMWARE_VERSION = '0.2.61'
|
||||||
# The Auto standby switch a screen offers Home Assistant automations.
|
# The Auto standby switch a screen offers Home Assistant automations.
|
||||||
AUTO_STANDBY_MIN_FIRMWARE = '0.2.41'
|
AUTO_STANDBY_MIN_FIRMWARE = '0.2.41'
|
||||||
# The settings page the screen opens itself, and the screen.settings tile that opens it.
|
# The settings page the screen opens itself, and the screen.settings tile that opens it.
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
name: ESP Screen Manager
|
name: ESP Screen Manager
|
||||||
version: "0.2.71"
|
version: "0.2.72"
|
||||||
slug: esp_screen_manager
|
slug: esp_screen_manager
|
||||||
description: Choose tiles for your CYD and Guition touchscreen, without reflashing.
|
description: Choose tiles for your CYD and Guition touchscreen, without reflashing.
|
||||||
url: https://github.com/MaxGramser/homeassistant_espscreen
|
url: https://github.com/MaxGramser/homeassistant_espscreen
|
||||||
|
|||||||
@@ -21,6 +21,14 @@ int main() {
|
|||||||
r.begin(1300); assert(!r.accept_repeat(1340, 7)); // 80 ms after the last: bounce
|
r.begin(1300); assert(!r.accept_repeat(1340, 7)); // 80 ms after the last: bounce
|
||||||
r.begin(1300); r.update(40, 0); assert(!r.accept_repeat(1500, 7)); // a swipe is never a step
|
r.begin(1300); r.update(40, 0); assert(!r.accept_repeat(1500, 7)); // a swipe is never a step
|
||||||
r.begin(1600); assert(r.accept_repeat(1660, 8)); // the other key right away
|
r.begin(1600); assert(r.accept_repeat(1660, 8)); // the other key right away
|
||||||
|
// Page buttons (0.2.72): Next, Next, Next at a finger's pace all count, so page 4 is three taps away
|
||||||
|
// while the pages are still drawing; accept() would have dropped the second and third (600 ms).
|
||||||
|
cyd::TouchGuard pager;
|
||||||
|
pager.begin(2000); assert(pager.accept_repeat(2080, 12));
|
||||||
|
pager.begin(2250); assert(pager.accept_repeat(2330, 12)); // 250 ms after the last
|
||||||
|
pager.begin(2500); assert(pager.accept_repeat(2580, 12)); // and again
|
||||||
|
pager.begin(2600); assert(!pager.accept(2680, 12)); // the old rule: bounce window
|
||||||
|
pager.begin(2700); assert(pager.accept_repeat(2780, 11)); // Previous right after Next
|
||||||
cyd::TouchGuard g;
|
cyd::TouchGuard g;
|
||||||
g.begin(100);
|
g.begin(100);
|
||||||
assert(!g.accept(120, 1)); // resistive noise pulse
|
assert(!g.accept(120, 1)); // resistive noise pulse
|
||||||
|
|||||||
Reference in New Issue
Block a user