/* Task_Watchdog Example

   This example code is in the Public Domain (or CC0 licensed, at your option.)

   Unless required by applicable law or agreed to in writing, this
   software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
   CONDITIONS OF ANY KIND, either express or implied.
*/

#include "sdkconfig.h"
#include <stdio.h>
#include <stdlib.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_err.h"
#include "esp_task_wdt.h"

#define TWDT_TIMEOUT_MS         3000
#define TASK_RESET_PERIOD_MS    2000
#define MAIN_DELAY_MS           10000

static volatile bool run_loop;
static esp_task_wdt_user_handle_t func_a_twdt_user_hdl;
static esp_task_wdt_user_handle_t func_b_twdt_user_hdl;

static void func_a(void)
{
    esp_task_wdt_reset_user(func_a_twdt_user_hdl);
}

static void func_b(void)
{
    esp_task_wdt_reset_user(func_b_twdt_user_hdl);
}

void task_func(void *arg)
{
    // Subscribe this task to TWDT, then check if it is subscribed
    ESP_ERROR_CHECK(esp_task_wdt_add(NULL));
    ESP_ERROR_CHECK(esp_task_wdt_status(NULL));

    // Subscribe func_a and func_b as users of the the TWDT
    ESP_ERROR_CHECK(esp_task_wdt_add_user("func_a", &func_a_twdt_user_hdl));
    ESP_ERROR_CHECK(esp_task_wdt_add_user("func_b", &func_b_twdt_user_hdl));

    printf("Subscribed to TWDT\n");

    while (run_loop) {
        // Reset the task and each user periodically
        /*
        Note: Comment out any one of the calls below to trigger the TWDT
        */
        esp_task_wdt_reset();
        func_a();
        func_b();

        vTaskDelay(pdMS_TO_TICKS(TASK_RESET_PERIOD_MS));
    }

    // Unsubscribe this task, func_a, and func_b
    ESP_ERROR_CHECK(esp_task_wdt_delete_user(func_a_twdt_user_hdl));
    ESP_ERROR_CHECK(esp_task_wdt_delete_user(func_b_twdt_user_hdl));
    ESP_ERROR_CHECK(esp_task_wdt_delete(NULL));

    printf("Unsubscribed from TWDT\n");

    // Notify main task of deletion
    xTaskNotifyGive((TaskHandle_t)arg);
    vTaskDelete(NULL);
}

void app_main(void)
{
#if !CONFIG_ESP_TASK_WDT_INIT
    // If the TWDT was not initialized automatically on startup, manually intialize it now
    esp_task_wdt_config_t twdt_config = {
        .timeout_ms = TWDT_TIMEOUT_MS,
        .idle_core_mask = (1 << portNUM_PROCESSORS) - 1,    // Bitmask of all cores
        .trigger_panic = false,
    };
    ESP_ERROR_CHECK(esp_task_wdt_init(&twdt_config));
    printf("TWDT initialized\n");
#endif // CONFIG_ESP_TASK_WDT_INIT

    // Create a task
    run_loop = true;
    xTaskCreatePinnedToCore(task_func, "task", 2048, xTaskGetCurrentTaskHandle(), 10, NULL, 0);

    // Let the created task run for a while
    printf("Delay for %d seconds\n", MAIN_DELAY_MS/1000);
    vTaskDelay(pdMS_TO_TICKS(MAIN_DELAY_MS));

    // Stop the created task
    run_loop = false;
    ulTaskNotifyTake(pdTRUE, portMAX_DELAY);

#if !CONFIG_ESP_TASK_WDT_INIT
    // If we manually initialized the TWDT, deintialize it now
    ESP_ERROR_CHECK(esp_task_wdt_deinit());
    printf("TWDT deinitialized\n");
#endif // CONFIG_ESP_TASK_WDT_INIT
    printf("Example complete\n");
}