Newer
Older
ESP32-RetroPlay / main / tasks / button_task.c
// 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

// Removed game-specific includes, as the queue is now passed generically
// #include "game/jump_bird_game.h"
// #include "game/jump_bird_game_input_queue.h"

static const char *TAG = "BUTTON_TASK";

#define BUTTON_GPIO 37

// 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 the button state and send jump signals to the game
 * and control signals to the LED task.
 *
 * This task configures the button GPIO as an input with an internal pull-down
 * resistor. It continuously reads the button's state. When the button
 * is pressed (goes HIGH), it sends a 'true' signal to the game_input_queue,
 * triggering a jump action. It also sends a command to the led_control_queue
 * to toggle the LED state. It specifically detects a button press (rising edge)
 * to ensure a single jump/LED toggle per press.
 *
 * @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);
    gpio_set_direction(BUTTON_GPIO, GPIO_MODE_INPUT);
    gpio_set_pull_mode(BUTTON_GPIO, GPIO_PULLDOWN_ONLY);

    int last_button_state = gpio_get_level(BUTTON_GPIO); // Initialize with current state
    int current_button_state;
    bool jump_signal = true; // Value to send to the game queue for a jump event
    bool led_state = false; // Initial LED state for toggling

    ESP_LOGI(TAG, "Button Monitor Task started. Monitoring GPIO %d.", BUTTON_GPIO);

    while (1)
    {
        current_button_state = gpio_get_level(BUTTON_GPIO);

        // Detect a button press (rising edge: went from LOW to HIGH)
        if (current_button_state == 1 && last_button_state == 0) // Button was just pressed
        {
            ESP_LOGI(TAG, "Button PRESSED detected (rising edge).");

            // --- Game Input Logic ---
            if (game_input_queue != NULL)
            {
                // Attempt to send the signal to the game queue (non-blocking)
                if (xQueueSend(game_input_queue, &jump_signal, 0) != pdTRUE)
                {
                    ESP_LOGW(TAG, "Failed to send jump signal to game input queue (queue full?).");
                }
                else
                {
                    ESP_LOGI(TAG, "Jump signal SENT successfully!");
                }
            }
            else
            {
                ESP_LOGE(TAG, "Game input queue is NULL! Cannot send jump signal.");
            }
            // --- END Game Input Logic ---

            // --- LED Control Logic ---
            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.");
            }
            // --- END LED Control Logic ---
        }

        last_button_state = current_button_state; // Update the last known state

        // A small delay is still recommended to prevent busy-waiting,
        // even without debounce logic. This allows other tasks to run.
        vTaskDelay(pdMS_TO_TICKS(50)); // Check button every 50ms
    }
    // 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;
}