Newer
Older
ESP32-RetroPlay / main / main.c
#include <stdio.h>
#include <inttypes.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/queue.h" // Needed for xQueueCreate
#include "esp_chip_info.h"
#include "esp_flash.h"
#include "esp_system.h"
#include "esp_log.h" // For ESP_LOG macros

// Include our modular task headers
#include "tasks/button_led_comm.h" // Keep this for LED queue
#include "tasks/button_task.h"     // Now expects a game input queue handle
#include "tasks/led_task.h"        // Keep this for LED task
#include "tasks/display_task.h"    // Generic display task
#include "tasks/game/jump_bird_game.h" // Specific game instance header (to get JumpBirdGame)
// #include "tasks/game/jump_bird_game_input_queue.h" // No longer directly included in main.c

static const char *TAG_MAIN = "APP_MAIN";

// The game input queue is now managed internally by the game module.
// No longer globally defined here.
// QueueHandle_t xJumpBirdGameInputQueue;

// Define the global LED control queue here (its definition)
QueueHandle_t xLedControlQueue; // Keep this definition

/**
 * @brief Prints detailed information about the ESP chip and flash.
 */
static void print_chip_info(void)
{
    esp_chip_info_t chip_info;
    uint32_t flash_size;

    esp_chip_info(&chip_info);

    printf("This is %s chip with %d CPU core(s), ", CONFIG_IDF_TARGET, chip_info.cores);

    if (chip_info.features & CHIP_FEATURE_WIFI_BGN)
    {
        printf("WiFi/");
    }
    if (chip_info.features & CHIP_FEATURE_BT)
    {
        printf("BT");
    }
    if (chip_info.features & CHIP_FEATURE_BLE)
    {
        printf("BLE");
    }
    if (chip_info.features & CHIP_FEATURE_IEEE802154)
    {
        printf(", 802.15.4 (Zigbee/Thread)");
    }
    printf(", ");

    unsigned major_rev = chip_info.revision / 100;
    unsigned minor_rev = chip_info.revision % 100;
    printf("silicon revision v%d.%d, ", major_rev, minor_rev);

    if (esp_flash_get_size(NULL, &flash_size) != ESP_OK)
    {
        printf("Get flash size failed\n");
        return;
    }
    printf("%" PRIu32 "MB %s flash\n", flash_size / (uint32_t)(1024 * 1024),
           (chip_info.features & CHIP_FEATURE_EMB_FLASH) ? "embedded" : "external");

    printf("Minimum free heap size: %" PRIu32 " bytes\n", esp_get_minimum_free_heap_size());
}

/**
 * @brief Main application entry point.
 * Initializes the system, prints chip information, and starts the necessary tasks.
 */
void app_main(void)
{
    print_chip_info();
    ESP_LOGI(TAG_MAIN, "Minimum free heap size: %" PRIu32 " bytes", esp_get_minimum_free_heap_size());

    // --- 1. The game input queue is now initialized and managed by the game itself. ---
    // We will initialize the game first to ensure its input queue is ready.
    // This call will also initialize the internal queue for JumpBirdGame.
    if (JumpBirdGame.init_game == NULL) {
        ESP_LOGE(TAG_MAIN, "JumpBirdGame.init_game is NULL! Halting.");
        while(1) { vTaskDelay(100); }
    }
    JumpBirdGame.init_game(); // Initialize the game state and its internal queue
    ESP_LOGI(TAG_MAIN, "JumpBirdGame initialized, including its input queue.");

    // --- 2. Create the LED control queue ---
    xLedControlQueue = xQueueCreate(1, sizeof(bool));
    if (xLedControlQueue == NULL)
    {
        ESP_LOGE(TAG_MAIN, "Failed to create LED control queue! Halting.");
        // No game queue to delete here as it's managed internally by the game
        while (1)
        {
            vTaskDelay(100); // Fatal error, halt
        }
    }
    ESP_LOGI(TAG_MAIN, "LED control queue created successfully.");

    // --- 3. Initialize and start the display task ---
    // The display task will now handle the game loop and rendering
    ESP_LOGI(TAG_MAIN, "Attempting to start display task...");
    // Pass the JumpBirdGame instance to the generic display task
    if (start_display_task(&JumpBirdGame) != ESP_OK)
    {
        ESP_LOGE(TAG_MAIN, "Failed to start display task! Halting.");
        // No game queue to delete here as it's managed internally by the game
        vQueueDelete(xLedControlQueue); // Clean up
        while (1)
        {
            vTaskDelay(100); // Fatal error, halt
        }
    }
    ESP_LOGI(TAG_MAIN, "Display task created successfully.");

    // --- 4. Start the LED control task ---
    ESP_LOGI(TAG_MAIN, "Attempting to start LED control task...");
    if (start_led_control_task() != ESP_OK)
    {
        ESP_LOGE(TAG_MAIN, "Failed to start LED control task! Halting.");
        // No game queue to delete here
        vQueueDelete(xLedControlQueue);
        // Consider stopping display task here too if it's dependent
        while (1)
        {
            vTaskDelay(100); // Fatal error, halt
        }
    }
    ESP_LOGI(TAG_MAIN, "LED control task created successfully.");

    // --- 5. Start the button monitor task ---
    // This task will send jump signals to the game's input queue (obtained generically)
    // AND LED signals to xLedControlQueue.
    QueueHandle_t game_input_queue = NULL;
    if (JumpBirdGame.get_input_queue) {
        game_input_queue = JumpBirdGame.get_input_queue();
    } else {
        ESP_LOGE(TAG_MAIN, "JumpBirdGame.get_input_queue is NULL! Cannot get game input queue.");
        // Proceeding but button input to game will not work.
    }

    ESP_LOGI(TAG_MAIN, "Attempting to start button monitor task...");
    // Pass both the game's input queue and the LED control queue
    if (start_button_monitor_task(game_input_queue, xLedControlQueue) != ESP_OK)
    {
        ESP_LOGE(TAG_MAIN, "Failed to start button monitor task! Halting.");
        // No game queue to delete here
        vQueueDelete(xLedControlQueue);
        // Consider stopping display task and LED task here too if they're dependent
        while (1)
        {
            vTaskDelay(100); // Fatal error, halt
        }
    }
    ESP_LOGI(TAG_MAIN, "Button Monitor task created successfully.");

    // --- 6. Initial Game State or Menu ---
    // Display an initial message on the OLED.
    const char *initial_messages[] = {
        "    JUMP BIRD!    ",
        "                  ",
        "   PRESS BUTTON   ",
        "    TO START      ",
        "                  ",
        "                  ",
        "                  ",
        "                  "};
    display_update_text(initial_messages, 8); // Display on OLED

    // Allow some time for the display to show the message
    vTaskDelay(pdMS_TO_TICKS(1000));

    // At this point, the system is running:
    // - Display task is ready to show graphics/text and will manage the game loop.
    // - Button task is ready to send jump events.
    // - LED task is ready to control the LED.

    ESP_LOGI(TAG_MAIN, "All core tasks and queues initialized. Main idle loop...");

    while (true)
    {
        // This task (app_main) now becomes an idle task or can manage high-level states
        // (e.g., switch between game mode, info mode, etc., based on long button presses or other inputs).
        vTaskDelay(pdMS_TO_TICKS(1000));
    }
}