Stop touch auto-toggle with hysteresis and button hitboxes.
Require solid press samples, cooldown between actions, and only fire REC/PLAY inside real button bounds instead of half-screen. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
74
src/main.cpp
74
src/main.cpp
@@ -93,6 +93,16 @@ static const char *basename_of(const char *path) {
|
||||
return p ? p + 1 : path;
|
||||
}
|
||||
|
||||
/* Absolute UI hit boxes filled after create_ui (screen coords). */
|
||||
static lv_area_t hit_rec;
|
||||
static lv_area_t hit_play;
|
||||
static lv_area_t hit_bl;
|
||||
static uint32_t last_action_ms = 0;
|
||||
|
||||
static bool pt_in(const lv_area_t *a, int16_t x, int16_t y) {
|
||||
return x >= a->x1 && x <= a->x2 && y >= a->y1 && y <= a->y2;
|
||||
}
|
||||
|
||||
static void touchscreen_read(lv_indev_t *indev, lv_indev_data_t *data) {
|
||||
static bool was_pressed = false;
|
||||
static uint32_t press_ms = 0;
|
||||
@@ -101,44 +111,48 @@ static void touchscreen_read(lv_indev_t *indev, lv_indev_data_t *data) {
|
||||
|
||||
touch_read_cb(indev, data);
|
||||
|
||||
/* Touch already reports landscape UI coords (320×240). LVGL still applies
|
||||
* ROTATION_270 transform on indev — undo it so widgets + hit-test match. */
|
||||
/* Touch reports landscape UI coords. Convert to native for LVGL ROTATION_270. */
|
||||
if (data->state == LV_INDEV_STATE_PRESSED) {
|
||||
const int16_t ux = data->point.x;
|
||||
const int16_t uy = data->point.y;
|
||||
/* Inverse of LVGL ROTATION_270: UI(ux,uy) → native(nx,ny) for indev feed */
|
||||
data->point.x = uy;
|
||||
data->point.y = (int16_t)(UI_HOR_RES - 1 - ux);
|
||||
|
||||
if (!was_pressed) {
|
||||
was_pressed = true;
|
||||
press_ms = millis();
|
||||
/* Freeze first solid sample — never track finger/noise to release. */
|
||||
press_ux = ux;
|
||||
press_uy = uy;
|
||||
Serial.printf("UI press (%d,%d)\n", (int)ux, (int)uy);
|
||||
} else {
|
||||
press_ux = ux;
|
||||
press_uy = uy;
|
||||
}
|
||||
} else if (was_pressed) {
|
||||
was_pressed = false;
|
||||
uint32_t held = millis() - press_ms;
|
||||
if (held < 700) {
|
||||
/* Hit-test only (no LV_EVENT_CLICKED) — avoids double toggle.
|
||||
* Top strip = backlight; everything else is half-screen REC | PLAY
|
||||
* (touch Y cal is coarse on SoftSPI CYD — don't require exact button box). */
|
||||
if (press_uy < 36) {
|
||||
set_backlight((bright_level + 1) & 0x03);
|
||||
char buf[24];
|
||||
lv_snprintf(buf, sizeof(buf), "brightness %u/3", (unsigned)bright_level);
|
||||
show_hint(buf, 900);
|
||||
} else if (press_ux < (UI_HOR_RES / 2)) {
|
||||
Serial.println("Hit → REC");
|
||||
do_rec_toggle();
|
||||
} else {
|
||||
Serial.println("Hit → PLAY");
|
||||
do_play_toggle();
|
||||
}
|
||||
const uint32_t held = millis() - press_ms;
|
||||
const uint32_t now = millis();
|
||||
/* Min hold filters spikes; max ignores long rests; cooldown stops auto-toggle. */
|
||||
if (held < 40 || held > 800) return;
|
||||
if (now - last_action_ms < 900) {
|
||||
Serial.println("Touch ignored (cooldown)");
|
||||
return;
|
||||
}
|
||||
|
||||
if (pt_in(&hit_bl, press_ux, press_uy)) {
|
||||
last_action_ms = now;
|
||||
set_backlight((bright_level + 1) & 0x03);
|
||||
char buf[24];
|
||||
lv_snprintf(buf, sizeof(buf), "brightness %u/3", (unsigned)bright_level);
|
||||
show_hint(buf, 900);
|
||||
} else if (pt_in(&hit_rec, press_ux, press_uy)) {
|
||||
last_action_ms = now;
|
||||
Serial.println("Hit → REC");
|
||||
do_rec_toggle();
|
||||
} else if (pt_in(&hit_play, press_ux, press_uy)) {
|
||||
last_action_ms = now;
|
||||
Serial.println("Hit → PLAY");
|
||||
do_play_toggle();
|
||||
} else {
|
||||
Serial.printf("Touch miss (%d,%d)\n", (int)press_ux, (int)press_uy);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -339,6 +353,20 @@ static void create_ui(void) {
|
||||
lv_obj_set_style_text_font(hint2, &lv_font_montserrat_12, 0);
|
||||
lv_obj_set_style_text_color(hint2, COL_MUTED, 0);
|
||||
lv_obj_align(hint2, LV_ALIGN_BOTTOM_MID, 0, 0);
|
||||
|
||||
/* Force layout then capture absolute hit boxes (+pad). */
|
||||
lv_obj_update_layout(scr);
|
||||
lv_obj_get_coords(btn_rec, &hit_rec);
|
||||
lv_obj_get_coords(btn_play, &hit_play);
|
||||
hit_rec.x1 -= 8; hit_rec.y1 -= 8; hit_rec.x2 += 8; hit_rec.y2 += 12;
|
||||
hit_play.x1 -= 8; hit_play.y1 -= 8; hit_play.x2 += 8; hit_play.y2 += 12;
|
||||
hit_bl.x1 = 0;
|
||||
hit_bl.y1 = 0;
|
||||
hit_bl.x2 = UI_HOR_RES - 1;
|
||||
hit_bl.y2 = 28;
|
||||
Serial.printf("Hit REC(%d..%d,%d..%d) PLAY(%d..%d,%d..%d)\n",
|
||||
(int)hit_rec.x1, (int)hit_rec.x2, (int)hit_rec.y1, (int)hit_rec.y2,
|
||||
(int)hit_play.x1, (int)hit_play.x2, (int)hit_play.y1, (int)hit_play.y2);
|
||||
}
|
||||
|
||||
static void refresh_ui(void) {
|
||||
|
||||
@@ -129,15 +129,23 @@ void touch_read_cb(lv_indev_t * /*indev*/, lv_indev_data_t *data) {
|
||||
|
||||
#include <CYD28_TouchscreenR.h>
|
||||
|
||||
/* Native panel size — LVGL create(240,320) + ROTATION_270. */
|
||||
/* Library thresh for XY update; our down uses hysteresis above that.
|
||||
* Real presses on this panel are often z≈45–100; idle ≈5–15. */
|
||||
static constexpr int16_t Z_DOWN = 55; /* press latch */
|
||||
static constexpr int16_t Z_UP = 28; /* release */
|
||||
static constexpr int16_t RAW_MIN = 150;
|
||||
|
||||
static CYD28_TouchR ts(SCREEN_WIDTH, SCREEN_HEIGHT);
|
||||
static bool s_down = false;
|
||||
static uint8_t s_down_streak = 0;
|
||||
static int16_t s_ux = 0, s_uy = 0;
|
||||
|
||||
void touch_init() {
|
||||
pinMode(SD_CS, OUTPUT);
|
||||
digitalWrite(SD_CS, HIGH);
|
||||
ts.begin();
|
||||
ts.setThreshold(40); /* idle z~10; press usually >>100 */
|
||||
Serial.println("Touch: CYD28 SoftSPI bitbang (SD-safe, poll) RNT map");
|
||||
ts.setThreshold(45); /* XY update only when clearly above idle */
|
||||
Serial.println("Touch: CYD28 SoftSPI + hysteresis (SD-safe)");
|
||||
}
|
||||
|
||||
void touch_read_cb(lv_indev_t * /*indev*/, lv_indev_data_t *data) {
|
||||
@@ -145,32 +153,45 @@ void touch_read_cb(lv_indev_t * /*indev*/, lv_indev_data_t *data) {
|
||||
ts.isrWake = true;
|
||||
|
||||
CYD28_TS_Point p = ts.getPointRaw();
|
||||
/* z threshold only — x/y stay 0 until press updates them in library */
|
||||
const bool down = (p.z >= 40);
|
||||
const bool raw_ok = (p.x >= RAW_MIN && p.y >= RAW_MIN);
|
||||
|
||||
static uint32_t last_log = 0;
|
||||
if (millis() - last_log > (down ? 120u : 500u)) {
|
||||
last_log = millis();
|
||||
Serial.printf("Touch raw(%d,%d) z=%d down=%d\n", (int)p.x, (int)p.y,
|
||||
(int)p.z, (int)down);
|
||||
if (s_down) {
|
||||
if (p.z < Z_UP) {
|
||||
s_down = false;
|
||||
s_down_streak = 0;
|
||||
}
|
||||
} else if (p.z >= Z_DOWN && raw_ok) {
|
||||
if (s_down_streak < 255) s_down_streak++;
|
||||
/* Need 3 solid samples — kills SD/EMI spikes that auto-toggle REC */
|
||||
if (s_down_streak >= 3) {
|
||||
s_down = true;
|
||||
}
|
||||
} else {
|
||||
s_down_streak = 0;
|
||||
}
|
||||
|
||||
if (!down) {
|
||||
static uint32_t last_log = 0;
|
||||
if (millis() - last_log > (s_down ? 200u : 800u)) {
|
||||
last_log = millis();
|
||||
Serial.printf("Touch raw(%d,%d) z=%d down=%d\n", (int)p.x, (int)p.y,
|
||||
(int)p.z, (int)s_down);
|
||||
}
|
||||
|
||||
if (!s_down || !raw_ok) {
|
||||
data->state = LV_INDEV_STATE_RELEASED;
|
||||
data->point.x = s_ux;
|
||||
data->point.y = s_uy;
|
||||
return;
|
||||
}
|
||||
|
||||
/* Map raw ADC → landscape UI 320×240 directly (USB-left / ROTATION_270).
|
||||
* Do NOT feed portrait+let-LVGL-rotate: LVGL 9.2 90/270 indev math is
|
||||
* swapped on many CYD builds (see lvgl#10052). Invert Y raw→X so left
|
||||
* physical taps land on left UI half. */
|
||||
/* Landscape UI 320×240 (USB on left). Y-raw→X inverted for this panel. */
|
||||
int16_t ux = (int16_t)map(p.y, 3800, 240, 0, UI_HOR_RES - 1);
|
||||
int16_t uy = (int16_t)map(p.x, 200, 3700, 0, UI_VER_RES - 1);
|
||||
ux = clampi(ux, 0, UI_HOR_RES - 1);
|
||||
uy = clampi(uy, 0, UI_VER_RES - 1);
|
||||
s_ux = clampi(ux, 0, UI_HOR_RES - 1);
|
||||
s_uy = clampi(uy, 0, UI_VER_RES - 1);
|
||||
|
||||
data->point.x = ux;
|
||||
data->point.y = uy;
|
||||
data->point.x = s_ux;
|
||||
data->point.y = s_uy;
|
||||
data->state = LV_INDEV_STATE_PRESSED;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user