From a244a2551362f61ffde5bdd3b946e76fb2ca6f47 Mon Sep 17 00:00:00 2001 From: Armel FAUVEAU Date: Wed, 25 Feb 2026 03:08:16 +0100 Subject: [PATCH] Add long press --- App/driver/keyboard.c | 43 +++++++++++++++++++++++++++----------- App/driver/keyboard.h | 8 ++++---- App/driver/uart.c | 19 +++++++++++------ App/driver/vcp.c | 48 +++++++++++++++++++++---------------------- 4 files changed, 72 insertions(+), 46 deletions(-) diff --git a/App/driver/keyboard.c b/App/driver/keyboard.c index 4d0a2532..c7f3875c 100644 --- a/App/driver/keyboard.c +++ b/App/driver/keyboard.c @@ -28,22 +28,37 @@ uint16_t gDebounceCounter = 0; bool gWasFKeyPressed = false; #ifdef ENABLE_FEAT_F4HWN_SCREENSHOT -// Number of consecutive KEYBOARD_Poll() calls that return the injected key. -// Must exceed key_debounce_10ms (typically 2) to pass the debounce gate in app.c. -// After HOLD polls the key is cleared, which triggers a natural release cycle. -#define SERIAL_KEY_HOLD_POLLS 5 +// Short press: hold key for SERIAL_KEY_SHORT_POLLS calls. +// Must exceed key_debounce_10ms (2) to trigger ProcessKey(key, true, false). +#define SERIAL_KEY_SHORT_POLLS 5 + +// Long press: hold key for SERIAL_KEY_LONG_POLLS calls. +// Must exceed key_repeat_delay_10ms (40) to trigger ProcessKey(key, true, true). +#define SERIAL_KEY_LONG_POLLS 45 volatile KEY_Code_t gKeyFromSerial = KEY_INVALID; static uint8_t gSerialKeyHoldCount = 0; +static uint8_t gSerialKeyLong = 0; // 0 = short press, 1 = long press -// Inject a key received from serial (UART or VCP). -// keyCode is the raw packet byte; validated against KEY_Code_e range before use. +// Inject a short press from serial (UART or VCP). // KEY_PTT is explicitly blocked — PTT release cannot be guaranteed over serial. void KEYBOARD_InjectKey(uint8_t keyCode) { if (keyCode < KEY_INVALID && keyCode != KEY_PTT) { gKeyFromSerial = (KEY_Code_t)keyCode; gSerialKeyHoldCount = 0; + gSerialKeyLong = 0; + } +} + +// Inject a long press from serial (UART or VCP). +// KEY_PTT is explicitly blocked — PTT release cannot be guaranteed over serial. +void KEYBOARD_InjectKeyLong(uint8_t keyCode) +{ + if (keyCode < KEY_INVALID && keyCode != KEY_PTT) { + gKeyFromSerial = (KEY_Code_t)keyCode; + gSerialKeyHoldCount = 0; + gSerialKeyLong = 1; } } #endif @@ -100,15 +115,19 @@ static const KEY_Code_t keyboard[5][4] = { KEY_Code_t KEYBOARD_Poll(void) { #ifdef ENABLE_FEAT_F4HWN_SCREENSHOT - // Serial-injected key: hold it for SERIAL_KEY_HOLD_POLLS consecutive calls - // so the debounce counter in app.c has time to reach key_debounce_10ms. - // Once the hold count is exhausted we clear it — the next call returns - // KEY_INVALID, which triggers the release path in app.c naturally. + // Serial-injected key: hold it for SHORT or LONG polls depending on press type, + // so the debounce counter in app.c reaches the right threshold: + // - Short: key_debounce_10ms (2) → ProcessKey(key, true, false) + // - Long: key_repeat_delay_10ms (40) → ProcessKey(key, true, true) + // Once the hold count is exhausted we clear it — next call returns KEY_INVALID, + // which triggers the release path in app.c naturally. if (gKeyFromSerial != KEY_INVALID) { - KEY_Code_t injected = gKeyFromSerial; - if (++gSerialKeyHoldCount >= SERIAL_KEY_HOLD_POLLS) { + KEY_Code_t injected = gKeyFromSerial; + uint8_t threshold = gSerialKeyLong ? SERIAL_KEY_LONG_POLLS : SERIAL_KEY_SHORT_POLLS; + if (++gSerialKeyHoldCount >= threshold) { gKeyFromSerial = KEY_INVALID; gSerialKeyHoldCount = 0; + gSerialKeyLong = 0; } return injected; } diff --git a/App/driver/keyboard.h b/App/driver/keyboard.h index 3eef61af..1decc46b 100644 --- a/App/driver/keyboard.h +++ b/App/driver/keyboard.h @@ -52,13 +52,13 @@ extern bool gWasFKeyPressed; #ifdef ENABLE_FEAT_F4HWN_SCREENSHOT // Serial-injected key (written by UART/VCP parser, consumed by KEYBOARD_Poll). -// Held for SERIAL_KEY_HOLD_POLLS calls to satisfy the debounce logic in app.c. extern volatile KEY_Code_t gKeyFromSerial; -// Inject a key received from serial (UART or VCP), bypasses physical matrix. -// keyCode is the raw byte from the serial packet — validated against KEY_Code_e range. -// KEY_PTT is explicitly blocked — PTT release cannot be guaranteed over serial. +// Inject a short press received from serial (UART or VCP). void KEYBOARD_InjectKey(uint8_t keyCode); + +// Inject a long press received from serial (UART or VCP). +void KEYBOARD_InjectKeyLong(uint8_t keyCode); #endif KEY_Code_t KEYBOARD_Poll(void); diff --git a/App/driver/uart.c b/App/driver/uart.c index d3e70079..de99a5e7 100644 --- a/App/driver/uart.c +++ b/App/driver/uart.c @@ -25,8 +25,9 @@ #ifdef ENABLE_FEAT_F4HWN_SCREENSHOT #include "driver/keyboard.h" -// Packet type for serial key injection (K5Viewer → radio) -#define UART_TYPE_KEY 0x03 +// Packet types for serial key injection (K5Viewer → radio) +#define UART_TYPE_KEY 0x03 +#define UART_TYPE_KEY_LONG 0x04 #endif #define USARTx USART1 @@ -165,15 +166,17 @@ void UART_LogSend(const void *pBuffer, uint32_t Size) connected = true; } else if (b == 0xAA) { - // Possible start of a key packet: 0xAA 0x55 0x03 - // Check if the next 3 bytes are available and match + // Possible start of a key packet: 0xAA 0x55 + // type 0x03 = short press, 0x04 = long press size_t i1 = (i + 1) % sizeof(UART_DMA_Buffer); size_t i2 = (i + 2) % sizeof(UART_DMA_Buffer); size_t i3 = (i + 3) % sizeof(UART_DMA_Buffer); if (UART_DMA_Buffer[i1] == 0x55 && - UART_DMA_Buffer[i2] == UART_TYPE_KEY) + (UART_DMA_Buffer[i2] == UART_TYPE_KEY || + UART_DMA_Buffer[i2] == UART_TYPE_KEY_LONG)) { + uint8_t type = UART_DMA_Buffer[i2]; uint8_t keyCode = UART_DMA_Buffer[i3]; // Consume all 4 bytes @@ -182,7 +185,11 @@ void UART_LogSend(const void *pBuffer, uint32_t Size) UART_DMA_Buffer[i2] = 0x00; UART_DMA_Buffer[i3] = 0x00; - KEYBOARD_InjectKey(keyCode); + if (type == UART_TYPE_KEY_LONG) + KEYBOARD_InjectKeyLong(keyCode); + else + KEYBOARD_InjectKey(keyCode); + connected = true; } } diff --git a/App/driver/vcp.c b/App/driver/vcp.c index 27d9613a..2ef8ffe0 100644 --- a/App/driver/vcp.c +++ b/App/driver/vcp.c @@ -20,8 +20,9 @@ #ifdef ENABLE_FEAT_F4HWN_SCREENSHOT #include "driver/keyboard.h" -// Packet type for serial key injection (K5Viewer → radio) -#define VCP_TYPE_KEY 0x03 +// Packet types for serial key injection (K5Viewer → radio) +#define VCP_TYPE_KEY 0x03 +#define VCP_TYPE_KEY_LONG 0x04 #endif uint8_t VCP_RxBuf[VCP_RX_BUF_SIZE]; @@ -46,21 +47,23 @@ void VCP_Init() bool VCP_ScreenshotPing(void) { -#ifdef ENABLE_FEAT_F4HWN_SCREENSHOT // State machine for parsing incoming packets: - // Keepalive: 0x55 0xAA 0x00 0x00 → viewer alive - // Key packet: 0xAA 0x55 0x03 → inject key + // Keepalive: 0x55 0xAA 0x00 0x00 → viewer alive + // Short key press: 0xAA 0x55 0x03 → inject short press + // Long key press: 0xAA 0x55 0x04 → inject long press // // State transitions: // IDLE → 0x55 → KA_1 - // KA_1 → 0xAA → KA_2 (else IDLE) - // KA_2 → 0x00 → KA_3 (else IDLE) + // KA_1 → 0xAA → KA_2 (else IDLE) + // KA_2 → 0x00 → KA_3 (else IDLE) // KA_3 → 0x00 → keepalive OK, IDLE // - // IDLE → 0xAA → KEY_1 - // KEY_1 → 0x55 → KEY_2 (else IDLE) - // KEY_2 → 0x03 → KEY_3 (else IDLE) - // KEY_3 → → inject key, IDLE + // IDLE → 0xAA → KEY_1 + // KEY_1 → 0x55 → KEY_2 (else IDLE) + // KEY_2 → 0x03 → KEY_3 (short press, else check long) + // KEY_2 → 0x04 → KEY_3L (long press, else IDLE) + // KEY_3 → → InjectKey(b), IDLE + // KEY_3L → → InjectKeyLong(b), IDLE typedef enum { STATE_IDLE = 0, @@ -70,6 +73,7 @@ bool VCP_ScreenshotPing(void) STATE_KEY_1, STATE_KEY_2, STATE_KEY_3, + STATE_KEY_3L, } ParseState_t; static uint32_t read_ptr = 0; @@ -110,7 +114,9 @@ bool VCP_ScreenshotPing(void) break; case STATE_KEY_2: - state = (b == VCP_TYPE_KEY) ? STATE_KEY_3 : STATE_IDLE; + if (b == VCP_TYPE_KEY) state = STATE_KEY_3; + else if (b == VCP_TYPE_KEY_LONG) state = STATE_KEY_3L; + else state = STATE_IDLE; break; case STATE_KEY_3: @@ -119,6 +125,12 @@ bool VCP_ScreenshotPing(void) state = STATE_IDLE; break; + case STATE_KEY_3L: + KEYBOARD_InjectKeyLong(b); + connected = true; + state = STATE_IDLE; + break; + default: state = STATE_IDLE; break; @@ -126,16 +138,4 @@ bool VCP_ScreenshotPing(void) } return connected; - -#else - // Simple ping: just detect any incoming byte from the viewer - static uint32_t read_ptr = 0; - uint32_t write_ptr = VCP_RxBufPointer; - - if (read_ptr != write_ptr) { - read_ptr = write_ptr; - return true; - } - return false; -#endif } \ No newline at end of file