From 9f5694b9ebae7ddd7e248e43083a16135c85268a Mon Sep 17 00:00:00 2001 From: Max Gramser Date: Fri, 18 Sep 2026 07:29:23 +0200 Subject: [PATCH] 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 --- components/smart_display/cyd_ui.h | 5 +- docs/RELEASING.md | 15 ++++++ docs/TEST_RESULTS_0272.md | 76 +++++++++++++++++++++++++++++++ guition-4848s040.yaml | 6 +-- home-like-2432s028.yaml | 6 +-- packages/cyd.yaml | 6 +-- packages/guition.yaml | 6 +-- screen_manager/CHANGELOG.md | 8 ++++ screen_manager/app/core.py | 2 +- screen_manager/config.yaml | 2 +- tests/test_cyd_ui.cpp | 8 ++++ 11 files changed, 124 insertions(+), 16 deletions(-) create mode 100644 docs/TEST_RESULTS_0272.md diff --git a/components/smart_display/cyd_ui.h b/components/smart_display/cyd_ui.h index bc6aad8..870a846 100644 --- a/components/smart_display/cyd_ui.h +++ b/components/smart_display/cyd_ui.h @@ -73,8 +73,9 @@ class TouchGuard { if (moved_) { reject_ = MOVED; return false; } return accept_slider(now, tile); } - // For -/+ keys: every clean tap counts, even the third within a second, so a - // setpoint moves several steps in one go. Only bounce (same key within `gap`) is dropped. + // For -/+ keys and the page buttons: every clean tap counts, even the third within a second, + // 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) { if (moved_) { reject_ = MOVED; return false; } if (accepted_) { reject_ = USED; return false; } diff --git a/docs/RELEASING.md b/docs/RELEASING.md index 9212ef2..235fc9c 100644 --- a/docs/RELEASING.md +++ b/docs/RELEASING.md @@ -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 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 Firmware only (`runtime_model.h`, `runtime_tiles.h`); the app raises `FIRMWARE_VERSION`. No protocol, storage or diff --git a/docs/TEST_RESULTS_0272.md b/docs/TEST_RESULTS_0272.md new file mode 100644 index 0000000..11ea687 --- /dev/null +++ b/docs/TEST_RESULTS_0272.md @@ -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--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. diff --git a/guition-4848s040.yaml b/guition-4848s040.yaml index ec0920e..50abf96 100644 --- a/guition-4848s040.yaml +++ b/guition-4848s040.yaml @@ -2,7 +2,7 @@ # 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. 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 # (like iOS). The CYD keeps a limit because its resistive panel jumps. TOUCH_MOVE_LIMIT_PX: "0" @@ -2438,7 +2438,7 @@ lvgl: on_short_click: - if: condition: - lambda: 'return cyd::touch_guard.accept(millis(), 11);' + lambda: 'return cyd::touch_guard.accept_repeat(millis(), 11);' then: - lambda: 'id(tile_page) = std::max(0, id(tile_page) - 1);' - script.execute: show_tile_page @@ -2479,7 +2479,7 @@ lvgl: on_short_click: - if: condition: - lambda: 'return cyd::touch_guard.accept(millis(), 12);' + lambda: 'return cyd::touch_guard.accept_repeat(millis(), 12);' then: - lambda: 'id(tile_page) += 1;' - script.execute: show_tile_page diff --git a/home-like-2432s028.yaml b/home-like-2432s028.yaml index b199602..fc2d40d 100644 --- a/home-like-2432s028.yaml +++ b/home-like-2432s028.yaml @@ -17,7 +17,7 @@ ############################################################ 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. TOUCH_MOVE_LIMIT_PX: "56" # Shortest contact that counts as a tap; a resistive panel bounces, a capacitive one does not. @@ -2351,7 +2351,7 @@ lvgl: on_short_click: - if: condition: - lambda: 'return cyd::touch_guard.accept(millis(), 11);' + lambda: 'return cyd::touch_guard.accept_repeat(millis(), 11);' then: - lambda: 'id(tile_page) = std::max(0, id(tile_page) - 1);' - script.execute: show_tile_page @@ -2392,7 +2392,7 @@ lvgl: on_short_click: - if: condition: - lambda: 'return cyd::touch_guard.accept(millis(), 12);' + lambda: 'return cyd::touch_guard.accept_repeat(millis(), 12);' then: - lambda: 'id(tile_page) += 1;' - script.execute: show_tile_page diff --git a/packages/cyd.yaml b/packages/cyd.yaml index 9465000..a204295 100644 --- a/packages/cyd.yaml +++ b/packages/cyd.yaml @@ -18,7 +18,7 @@ ############################################################ 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. TOUCH_MOVE_LIMIT_PX: "56" # Shortest contact that counts as a tap; a resistive panel bounces, a capacitive one does not. @@ -2340,7 +2340,7 @@ lvgl: on_short_click: - if: condition: - lambda: 'return cyd::touch_guard.accept(millis(), 11);' + lambda: 'return cyd::touch_guard.accept_repeat(millis(), 11);' then: - lambda: 'id(tile_page) = std::max(0, id(tile_page) - 1);' - script.execute: show_tile_page @@ -2381,7 +2381,7 @@ lvgl: on_short_click: - if: condition: - lambda: 'return cyd::touch_guard.accept(millis(), 12);' + lambda: 'return cyd::touch_guard.accept_repeat(millis(), 12);' then: - lambda: 'id(tile_page) += 1;' - script.execute: show_tile_page diff --git a/packages/guition.yaml b/packages/guition.yaml index dc7d01e..b80a47d 100644 --- a/packages/guition.yaml +++ b/packages/guition.yaml @@ -3,7 +3,7 @@ # 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. 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 # (like iOS). The CYD keeps a limit because its resistive panel jumps. TOUCH_MOVE_LIMIT_PX: "0" @@ -2427,7 +2427,7 @@ lvgl: on_short_click: - if: condition: - lambda: 'return cyd::touch_guard.accept(millis(), 11);' + lambda: 'return cyd::touch_guard.accept_repeat(millis(), 11);' then: - lambda: 'id(tile_page) = std::max(0, id(tile_page) - 1);' - script.execute: show_tile_page @@ -2468,7 +2468,7 @@ lvgl: on_short_click: - if: condition: - lambda: 'return cyd::touch_guard.accept(millis(), 12);' + lambda: 'return cyd::touch_guard.accept_repeat(millis(), 12);' then: - lambda: 'id(tile_page) += 1;' - script.execute: show_tile_page diff --git a/screen_manager/CHANGELOG.md b/screen_manager/CHANGELOG.md index 7008be1..7fd4ed3 100644 --- a/screen_manager/CHANGELOG.md +++ b/screen_manager/CHANGELOG.md @@ -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) A small slider stays where you leave it while the lights fade towards it. diff --git a/screen_manager/app/core.py b/screen_manager/app/core.py index 6119746..cc69e06 100644 --- a/screen_manager/app/core.py +++ b/screen_manager/app/core.py @@ -20,7 +20,7 @@ WEEKDAYS = ['Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa', 'Su'] REPO = 'https://github.com/MaxGramser/homeassistant_espscreen' REFS = {'cyd': 'main', 'guition': 'main'} # 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. AUTO_STANDBY_MIN_FIRMWARE = '0.2.41' # The settings page the screen opens itself, and the screen.settings tile that opens it. diff --git a/screen_manager/config.yaml b/screen_manager/config.yaml index c633659..75d1390 100644 --- a/screen_manager/config.yaml +++ b/screen_manager/config.yaml @@ -1,5 +1,5 @@ name: ESP Screen Manager -version: "0.2.71" +version: "0.2.72" slug: esp_screen_manager description: Choose tiles for your CYD and Guition touchscreen, without reflashing. url: https://github.com/MaxGramser/homeassistant_espscreen diff --git a/tests/test_cyd_ui.cpp b/tests/test_cyd_ui.cpp index 5561260..b021e17 100644 --- a/tests/test_cyd_ui.cpp +++ b/tests/test_cyd_ui.cpp @@ -21,6 +21,14 @@ int main() { 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(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; g.begin(100); assert(!g.accept(120, 1)); // resistive noise pulse