// button_task.c #include <stdio.h> #include "freertos/FreeRTOS.h" #include "freertos/task.h" #include "freertos/queue.h" #include "driver/gpio.h" #include "esp_log.h" // For ESP_LOG macros #include "button_task.h" // For start_button_monitor_task declaration #include "button_led_comm.h" // For LED_COMMAND_ON/OFF and send_led_command static const char *TAG = "BUTTON_TASK"; #define BUTTON_GPIO_LEFT 37 // Original button for general action, now Left #define BUTTON_GPIO_RIGHT 36 // New button for Right movement /** * @brief Structure to pass multiple parameters to the FreeRTOS task */ typedef struct { QueueHandle_t game_input_queue; QueueHandle_t led_control_queue; } button_task_params_t; /** * @brief FreeRTOS task to monitor button states and send input signals to the game * and control signals to the LED task. * * This task configures both button GPIOs as inputs with internal pull-down * resistors. It continuously reads the buttons' states. It determines the * desired paddle movement (-1 for left, 1 for right, 0 for no movement) * and sends this value to the game's input queue every frame, ensuring * continuous movement while a button is held. It also toggles the LED * state on any button press (rising edge). * * @param pvParameters A pointer to a button_task_params_t structure containing * the game input queue handle and the LED control queue handle. */ static void button_monitor_task(void *pvParameters) { button_task_params_t *params = (button_task_params_t *)pvParameters; QueueHandle_t game_input_queue = params->game_input_queue; QueueHandle_t led_control_queue = params->led_control_queue; // --- Button Configuration --- gpio_reset_pin(BUTTON_GPIO_LEFT); gpio_set_direction(BUTTON_GPIO_LEFT, GPIO_MODE_INPUT); gpio_set_pull_mode(BUTTON_GPIO_LEFT, GPIO_PULLDOWN_ONLY); gpio_reset_pin(BUTTON_GPIO_RIGHT); gpio_set_direction(BUTTON_GPIO_RIGHT, GPIO_MODE_INPUT); gpio_set_pull_mode(BUTTON_GPIO_RIGHT, GPIO_PULLDOWN_ONLY); int last_button_left_state = gpio_get_level(BUTTON_GPIO_LEFT); int last_button_right_state = gpio_get_level(BUTTON_GPIO_RIGHT); int current_button_left_state; int current_button_right_state; bool led_state = false; // Initial LED state for toggling ESP_LOGI(TAG, "Button Monitor Task started. Monitoring GPIO %d (Left) and %d (Right).", BUTTON_GPIO_LEFT, BUTTON_GPIO_RIGHT); while (1) { current_button_left_state = gpio_get_level(BUTTON_GPIO_LEFT); current_button_right_state = gpio_get_level(BUTTON_GPIO_RIGHT); int game_input_signal = 0; // Default: no movement // Determine continuous game input signal if (current_button_left_state == 1) { // Left button is currently pressed game_input_signal = -1; // Signal for left movement } else if (current_button_right_state == 1) { // Right button is currently pressed game_input_signal = 1; // Signal for right movement } else { game_input_signal = 0; // No button pressed, no movement } // Send game input signal to the queue (overwrite if full to ensure latest state) if (game_input_queue != NULL) { // Use xQueueOverwrite to ensure the latest input is always available if (xQueueOverwrite(game_input_queue, &game_input_signal) != pdTRUE) { ESP_LOGW(TAG, "Failed to overwrite game input signal to queue (should not happen with overwrite)."); } // No success log here as it's continuous and would spam logs } else { ESP_LOGE(TAG, "Game input queue is NULL! Cannot send game input signal."); } // Toggle LED on any button press (rising edge detection for LED only) if ((current_button_left_state == 1 && last_button_left_state == 0) || (current_button_right_state == 1 && last_button_right_state == 0)) { ESP_LOGI(TAG, "Any Button PRESSED detected (rising edge)."); if (led_control_queue != NULL) { led_state = !led_state; // Toggle LED state if (xQueueSend(led_control_queue, &led_state, 0) != pdTRUE) { ESP_LOGW(TAG, "Failed to send LED control signal to queue (queue full?)."); } else { ESP_LOGI(TAG, "LED control signal SENT: %s", led_state ? "ON" : "OFF"); } } else { ESP_LOGE(TAG, "LED control queue is NULL! Cannot send LED signal."); } } last_button_left_state = current_button_left_state; // Update the last known state last_button_right_state = current_button_right_state; // Update the last known state vTaskDelay(pdMS_TO_TICKS(20)); // Check buttons every 20ms for smoother response } // If the task ever exits (which it shouldn't in an embedded loop), free allocated memory free(params); vTaskDelete(NULL); } /** * @brief Initializes and starts the button monitoring task. * @param game_input_queue Handle to the queue for sending game input signals. * @param led_control_queue Handle to the queue for sending LED control signals. * @return ESP_OK on success, ESP_FAIL on failure. */ esp_err_t start_button_monitor_task(QueueHandle_t game_input_queue, QueueHandle_t led_control_queue) { // Allocate memory for task parameters button_task_params_t *params = (button_task_params_t *)malloc(sizeof(button_task_params_t)); if (params == NULL) { ESP_LOGE(TAG, "Failed to allocate memory for button task parameters!"); return ESP_FAIL; } params->game_input_queue = game_input_queue; params->led_control_queue = led_control_queue; // Create the button monitor task, passing the parameters struct if (xTaskCreate(button_monitor_task, "Button_Monitor_Task", configMINIMAL_STACK_SIZE * 3, (void*)params, 5, NULL) != pdPASS) { ESP_LOGE(TAG, "Failed to create Button Monitor task!"); free(params); // Free allocated memory on failure return ESP_FAIL; } ESP_LOGI(TAG, "Button Monitor task created successfully!"); return ESP_OK; }