Files
ESP32C3-0.42OLED-OS/components/sys_input/sys_input.c
T

154 lines
4.9 KiB
C

#include "os_input.h"
#include "os_event.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_log.h"
#include "esp_timer.h"
static const char *TAG = "SYS_INPUT";
#define DEBOUNCE_TIME_MS 12
#define DOUBLE_CLICK_GAP_MS 260
#define LONG_PRESS_TIME_MS 380
#define HOME_PRESS_TIME_MS 1400
typedef struct {
sys_input_handler_t handler;
void *user_arg;
} input_listener_t;
#define MAX_INPUT_LISTENERS 8
static input_listener_t s_listeners[MAX_INPUT_LISTENERS];
static size_t s_listener_count = 0;
static gpio_num_t s_button_pin = GPIO_NUM_9;
static void dispatch_action(input_action_t action)
{
sys_event_id_t evt_id = SYS_EVENT_CUSTOM;
switch (action) {
case INPUT_ACTION_CLICK:
evt_id = SYS_EVENT_INPUT_SHORT_PRESS;
break;
case INPUT_ACTION_DOUBLE_CLICK:
evt_id = SYS_EVENT_INPUT_DOUBLE_CLICK;
break;
case INPUT_ACTION_TRIPLE_CLICK:
case INPUT_ACTION_HOLD_HOME:
evt_id = SYS_EVENT_INPUT_EXTRA_LONG_PRESS;
break;
case INPUT_ACTION_LONG_PRESS:
evt_id = SYS_EVENT_INPUT_LONG_PRESS;
break;
default:
return;
}
sys_event_post(evt_id, (int32_t)action, NULL);
for (size_t i = 0; i < s_listener_count; i++) {
if (s_listeners[i].handler) {
s_listeners[i].handler(action, s_listeners[i].user_arg);
}
}
}
void sys_input_inject(input_action_t action)
{
dispatch_action(action);
}
esp_err_t sys_input_register_handler(sys_input_handler_t handler, void *user_arg)
{
if (!handler) return ESP_ERR_INVALID_ARG;
if (s_listener_count >= MAX_INPUT_LISTENERS) return ESP_ERR_NO_MEM;
s_listeners[s_listener_count].handler = handler;
s_listeners[s_listener_count].user_arg = user_arg;
s_listener_count++;
return ESP_OK;
}
static void button_scanner_task(void *pvParameters)
{
bool last_state = false;
uint32_t press_start_ms = 0;
uint32_t last_release_time_ms = 0;
uint8_t tap_count = 0;
bool long_press_fired = false;
bool home_press_fired = false;
while (1) {
vTaskDelay(pdMS_TO_TICKS(5)); // High-rate 200 Hz scan loop for zero input lag
uint32_t now_ms = (uint32_t)(esp_timer_get_time() / 1000ULL);
bool current_state = (gpio_get_level(s_button_pin) == 0);
if (current_state && !last_state) {
// Button pressed down
press_start_ms = now_ms;
long_press_fired = false;
home_press_fired = false;
} else if (current_state && last_state) {
// Button held down
uint32_t held_duration = now_ms - press_start_ms;
if (!home_press_fired && held_duration >= HOME_PRESS_TIME_MS) {
home_press_fired = true;
tap_count = 0;
dispatch_action(INPUT_ACTION_HOLD_HOME);
} else if (!long_press_fired && !home_press_fired && held_duration >= LONG_PRESS_TIME_MS) {
long_press_fired = true;
tap_count = 0;
dispatch_action(INPUT_ACTION_LONG_PRESS);
}
} else if (!current_state && last_state) {
// Button released
uint32_t press_duration = now_ms - press_start_ms;
if (press_duration >= DEBOUNCE_TIME_MS && !long_press_fired && !home_press_fired) {
if ((now_ms - last_release_time_ms) <= DOUBLE_CLICK_GAP_MS) {
tap_count++;
} else {
tap_count = 1;
}
last_release_time_ms = now_ms;
if (tap_count == 1) {
dispatch_action(INPUT_ACTION_CLICK);
} else if (tap_count == 2) {
dispatch_action(INPUT_ACTION_DOUBLE_CLICK);
} else if (tap_count >= 3) {
dispatch_action(INPUT_ACTION_TRIPLE_CLICK);
tap_count = 0;
last_release_time_ms = 0;
}
}
}
last_state = current_state;
}
}
esp_err_t sys_input_init(gpio_num_t button_gpio)
{
s_button_pin = button_gpio;
gpio_config_t io_conf = {
.pin_bit_mask = (1ULL << s_button_pin),
.mode = GPIO_MODE_INPUT,
.pull_up_en = GPIO_PULLUP_ENABLE,
.pull_down_en = GPIO_PULLDOWN_DISABLE,
.intr_type = GPIO_INTR_DISABLE,
};
esp_err_t err = gpio_config(&io_conf);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to configure button GPIO %d: %s", (int)s_button_pin, esp_err_to_name(err));
return err;
}
BaseType_t ret = xTaskCreate(button_scanner_task, "btn_scan_task", 3072, NULL, 12, NULL);
if (ret != pdPASS) {
ESP_LOGE(TAG, "Failed to create button scanner task");
return ESP_FAIL;
}
ESP_LOGI(TAG, "Input gesture engine started on GPIO %d (Instant-Response Mode)", (int)s_button_pin);
return ESP_OK;
}