#include #include "freertos/FreeRTOS.h" #include "freertos/task.h" #include "freertos/queue.h" #include "driver/gpio.h" #include "esp_log.h" #include "keypad.h" // ───────────── CONFIGURATION ───────────── typedef enum { BUTTON1_GPIO = GPIO_NUM_36, BUTTON2_GPIO = GPIO_NUM_39, } button_gpio_t; #define BUTTON_COUNT 2 // If your buttons are active‐low, set this to 0. If active‐high, set it to 1. #define KEYPAD_INACTIVE 1 #define KEYPAD_ACTIVE 0 // Now we run the task every 50 ms and assume that is enough to debounce: #define DEBOUNCE_INTERVAL_MS 50 // Poll once every 50 ms // No more stable‐count logic—one sample/50 ms is considered “debounced.” // Short vs. long press threshold (in ms): #define LONG_PRESS_MS 1000 // ≥1000 ms = “long press” // Queue length for pending button events: #define BTN_EVT_QUEUE_LEN 10 // ───────────── EVENT ENUM & QUEUE HANDLE ───────────── typedef struct { keycode_t key; gpio_num_t gpio; int prevState; int currentState; TickType_t activeTime; uint32_t event; } button_t; static button_t _buttons[2]; // The queue handle that the debounce task will push events into: static QueueHandle_t s_btn_evt_queue = NULL; // Call this to get the queue, so the main task can xQueueReceive(): QueueHandle_t keypad_getQueue(void) { return s_btn_evt_queue; } // ───────────── INTERNAL STATE ───────────── // Current debounced state: 0 = released, 1 = pressed static int s_debounced[2] = { 0, 0 }; // Tick count when each button was debounced to “pressed” static TickType_t s_press_tick[2] = { 0, 0 }; // If we suppress an individual‐button event (because it was part of a “both” press), this is true static bool s_suppress_event[2] = { false, false }; // Tracks if we are currently in “both buttons pressed” phase: static bool s_was_both = false; // Tick count when “both buttons” first became debounced‐pressed: static TickType_t s_both_press_tick = 0; // ───────────── TAG FOR LOGGING ───────────── static const char *TAG = "btn_debounce"; // ───────────── GPIO SETUP ───────────── static void init_button_gpio(void) { // Configure both as inputs with pull‐up (assuming active‐low buttons) gpio_config_t io_conf = { .pin_bit_mask = (1ULL << BUTTON1_GPIO) | (1ULL << BUTTON2_GPIO), .mode = GPIO_MODE_INPUT, .pull_up_en = GPIO_PULLUP_ENABLE, .pull_down_en = GPIO_PULLDOWN_DISABLE, .intr_type = GPIO_INTR_DISABLE }; ESP_ERROR_CHECK(gpio_config(&io_conf)); _buttons[0].activeTime = 0; _buttons[0].gpio = BUTTON1_GPIO; _buttons[0].key = KEY0; _buttons[0].prevState = KEYPAD_INACTIVE; _buttons[0].currentState = KEYPAD_INACTIVE; _buttons[1].activeTime = 0; _buttons[1].gpio = BUTTON2_GPIO; _buttons[1].key = KEY1; _buttons[1].prevState = KEYPAD_INACTIVE; _buttons[1].currentState = KEYPAD_INACTIVE; } // ───────────── BUTTON DEBOUNCE & PRESS‐TYPE TASK ───────────── static void vButtonDebounceTask(void *arg) { (void)arg; TickType_t now = xTaskGetTickCount(); TickType_t xLastWake = now; const TickType_t long_press_thresh = pdMS_TO_TICKS(LONG_PRESS_MS); while (1) { now = xTaskGetTickCount(); for (int i=0; i < BUTTON_COUNT; ++i) { button_t *button = &_buttons[i]; button->currentState = gpio_get_level(button->gpio); if (button->currentState == KEYPAD_ACTIVE) { // just pressed if (button->prevState == KEYPAD_INACTIVE) { button->activeTime = now; } else { if ((now - button->activeTime) >= long_press_thresh) { if (button->event == 0) { ESP_LOGI(TAG, "%d LONG", button->key); button->event = (button->key << KEY_LONG_PRESS); xQueueSend(s_btn_evt_queue, &button->event, 0); } } } } else if (button->currentState == KEYPAD_INACTIVE) { // just released if (button->prevState == KEYPAD_ACTIVE) { if (button->event == 0) { ESP_LOGI(TAG, "%d SHORT", button->key); button->event = (button->key << KEY_SHORT_PRESS); xQueueSend(s_btn_evt_queue, &button->event, 0); } else { ESP_LOGI(TAG, "%d LONG RELEASE", button->key); } button->event = 0; } button->activeTime = 0; } button->prevState = button->currentState; } // 7) Wait 50 ms and repeat vTaskDelayUntil(&xLastWake, pdMS_TO_TICKS(DEBOUNCE_INTERVAL_MS)); xLastWake = now; } } // ───────────── PUBLIC “START” FUNCTION ───────────── void keypad_start(void) { // 1) Create the queue for button events s_btn_evt_queue = xQueueCreate(BTN_EVT_QUEUE_LEN, sizeof(uint32_t)); if (s_btn_evt_queue == NULL) { ESP_LOGE(TAG, "Failed to create button event queue"); return; } // 2) Initialize GPIOs & initial state init_button_gpio(); // 3) Launch the FreeRTOS task (low priority) xTaskCreate(vButtonDebounceTask, "btn_debounce", 2048, NULL, tskIDLE_PRIORITY + 1, NULL); }