#include <Arduino_FreeRTOS.h>
#include <timers.h>
#include <task.h>

// Định nghĩa các khoảng thời gian cho các timer tính bằng mili giây, chuyển đổi thành ticks
#define mainONE_SHOT_TIMER_PERIOD pdMS_TO_TICKS(3333)
#define mainAUTO_RELOAD_TIMER_PERIOD pdMS_TO_TICKS(500)

// Biến lưu trữ handle của timer cho timer one-shot và auto-reload
TimerHandle_t xAutoReloadTimer, xOneShotTimer;
BaseType_t xTimer1Started, xTimer2Started;

// Khai báo nguyên mẫu hàm cho các callback của timer
static void prvOneShotTimerCallback(TimerHandle_t xTimer);
static void prvAutoReloadTimerCallback(TimerHandle_t xTimer);

// Khai báo nguyên mẫu hàm cho các task
void vChangeTimerPeriodTask(void *pvParameters);
void vResetTimerTask(void *pvParameters);

void setup() {
    Serial.begin(9600); // Bật giao tiếp serial với baud rate là 9600

    // Tạo timer one-shot
    xOneShotTimer = xTimerCreate(
        "OneShot",                      // Tên của timer (không được sử dụng bởi FreeRTOS)
        mainONE_SHOT_TIMER_PERIOD,       // Thời gian chạy của timer tính bằng ticks
        pdFALSE,                        // Không tự động khởi động lại sau khi hết thời gian
        0,                              // Không sử dụng timer id trong ví dụ này
        prvOneShotTimerCallback         // Hàm callback được gọi khi timer kết thúc
    );

    // Tạo timer auto-reload
    xAutoReloadTimer = xTimerCreate(
        "AutoReload",                   // Tên của timer (không được sử dụng bởi FreeRTOS)
        mainAUTO_RELOAD_TIMER_PERIOD,   // Thời gian chạy của timer tính bằng ticks
        pdTRUE,                         // Tự động khởi động lại sau mỗi chu kỳ
        0,                              // Không sử dụng timer id trong ví dụ này
        prvAutoReloadTimerCallback      // Hàm callback được gọi khi timer kết thúc
    );

    // Kiểm tra xem các timer đã được tạo thành công chưa
    if ((xOneShotTimer != NULL) && (xAutoReloadTimer != NULL)) {
        // Khởi động timer one-shot và timer auto-reload
        xTimer1Started = xTimerStart(xOneShotTimer, 0);
        xTimer2Started = xTimerStart(xAutoReloadTimer, 0);

        // Kiểm tra xem việc khởi động timer đã thành công hay chưa
        if ((xTimer1Started == pdPASS) && (xTimer2Started == pdPASS)) {
            // Tạo task để thay đổi thời gian chu kỳ của timer auto-reload
            xTaskCreate(vChangeTimerPeriodTask, "ChangeTimerPeriod", 100, NULL, 1, NULL);
            // Tạo task để reset lại timer
            xTaskCreate(vResetTimerTask, "ResetTimerTask", 100, NULL, 1, NULL);
            // Khởi động bộ lập lịch của FreeRTOS
            vTaskStartScheduler();
        }
    }
}

void loop() {
    // Không có mã nguồn bổ sung trong hàm loop vì mọi task được quản lý bởi FreeRTOS
}

// Callback function cho timer one-shot
static void prvOneShotTimerCallback(TimerHandle_t xTimer) {
    TickType_t xTimeNow;
    xTimeNow = xTaskGetTickCount(); // Lấy số tick hiện tại
    Serial.print("One-shot timer callback executing ");
    Serial.println(xTimeNow / 31); // In ra serial monitor thời điểm callback được gọi
}

// Callback function cho timer auto-reload
static void prvAutoReloadTimerCallback(TimerHandle_t xTimer) {
    TickType_t xTimeNow;
    xTimeNow = xTaskGetTickCount(); // Lấy số tick hiện tại
    Serial.print("Auto-reload timer callback executing ");
    Serial.println(xTimeNow / 31); // In ra serial monitor thời điểm callback được gọi
}

// Task để thay đổi thời gian chu kỳ của timer auto-reload
void vChangeTimerPeriodTask(void *pvParameters) {
    TickType_t xNewPeriod;
    while(1) {
        vTaskDelay(pdMS_TO_TICKS(10000)); // Chờ 10 giây
        xNewPeriod = pdMS_TO_TICKS(1000); // Đổi thời gian chu kỳ thành 1000 ms
        xTimerChangePeriod(xAutoReloadTimer, xNewPeriod, 0);
        Serial.println("Changed auto-reload timer period to 1000 ms");
    }
}

// Task để reset lại timer
void vResetTimerTask(void *pvParameters) {
    while(1) {
        vTaskDelay(pdMS_TO_TICKS(15000)); // Chờ 15 giây
        xTimerReset(xAutoReloadTimer, 0);
        Serial.println("Auto-reload timer reset");
    }
}