Add long press

This commit is contained in:
Armel FAUVEAU committed 2026-02-25 03:08:16 +01:00
1 parent f072b3eb02
commit a244a25513
4 files changed
+72 -46

No files matched your search

+31 -12
View File
@@ -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;
}
+4 -4
View File
@@ -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);
+13 -6
View File
@@ -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 <keycode>
// Check if the next 3 bytes are available and match
// Possible start of a key packet: 0xAA 0x55 <type> <keycode>
// 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;
}
}
+24 -24
View File
@@ -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 <key> → inject key
// Keepalive: 0x55 0xAA 0x00 0x00 → viewer alive
// Short key press: 0xAA 0x55 0x03 <key> → inject short press
// Long key press: 0xAA 0x55 0x04 <key> → 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 → <b> → 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 → <b> → InjectKey(b), IDLE
// KEY_3L → <b> → 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
}