//Function declaration
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/queue.h"
#include "driver/gpio.h"

/**
https://programming.vip/docs/6220285c6023b.html

 * Brief:
 * This test code shows how to configure gpio and how to use gpio interrupt.  //How to configure gpio and how to use gpio interrupt.
 *
 * GPIO status:
 * GPIO18: output                                                             //GP1018 Push pull output
 * GPIO19: output                                                             //GPI019 Push pull output
 * GPIO4:  input, pulled up, interrupt from rising edge and falling edge      //GPI04 Input, pull-up and interrupt from rising and falling edges
 * GPIO5:  input, pulled up, interrupt from rising edge.                      //GPIO5 Input, pull up, interrupt from rising edge
 *
 * Test:
 * Connect GPIO18 with GPIO4                                                  //Connect GP1018 and GPIO4
 * Connect GPIO19 with GPIO5                                                  //Connect GP1019 and GPIO5
 * Generate pulses on GPIO18/19, that triggers interrupt on GPIO4/5           //Connect GP1019 and GPIO5 and generate pulses on GPI018/19 to trigger interrupts on GPIO4/5
 *
 */

#define GPIO_OUTPUT_IO_0 18 / / define GPIO18 as output pin 0
#define GPIO_OUTPUT_IO_1 19 / / define GPIO19 as output pin 1
#define GPIO_ OUTPUT_ PIN_ Sel ((1ull < < gpio_output_io_0) | (1ull < < gpio_output_io_1)) / / mixed
#define GPIO_INPUT_IO_0 4 / / define GPIO4 as input pin 0
#define GPIO_INPUT_IO_1 5 / / define GPIO5 as input pin 1
#define GPIO_ INPUT_ PIN_ Sel ((1ull < < gpio_input_io_0) | (1ull < < gpio_input_io_1)) / / mixing
#define ESP_INTR_FLAG_DEFAULT 0 / / the interrupt flag is cleared

// Set the message queue, which is used to deliver the information of interruption and return variables
static xQueueHandle gpio_evt_queue = NULL; //Empty variable gpio_evt_queue is defined as a queue (xQueueHandle)

//Interrupt handling function
static void IRAM_ATTR gpio_isr_handler(void *arg)
{
    uint32_t gpio_num = (uint32_t)arg;                  //Get the pin number of the GPIO passed
    xQueueSendFromISR(gpio_evt_queue, &gpio_num, NULL); //Deliver data to the message queue, and the message content is the pin number of GPIO
}

// GPIO interrupt message processing task
static void gpio_task_example(void *arg)
{
    uint32_t io_num;
    for (;;)
    {
        // API function: receive queue message function xQueueReceive(). In the task of FreeRTOS, you can receive the interrupt message of obtaining the chip through function xQueueReceive, because this function can set the timeout wait until there is a message stored in the message queue or the set timeout overflows.
        //Accept the gpio queue and delete the queue after reading
        if (xQueueReceive(gpio_evt_queue, &io_num, portMAX_DELAY)) //Obtain the interrupt message of the chip, the value of the enable pin and the delay time through the xQueueReceive() function.
        {
            printf("GPIO[%d] intr, val: %d\n", io_num, gpio_get_level(io_num)); //Print GPIO pin and pin output values
        }
    }
}

//Main function
void app_main(void)
{
    //Initialize GPIO18/19
    gpio_config_t io_conf1 = {
        .intr_type = GPIO_INTR_DISABLE,      // GPIO interrupt disable
        .mode = GPIO_MODE_OUTPUT,            // GPIO set to output mode
        .pin_bit_mask = GPIO_OUTPUT_PIN_SEL, //Set GPIO18 as output pin 0 and GPIO19 as output pin 1 as mixed mode
        .pull_down_en = 0,                   //Do not set GPIO drop-down
        .pull_up_en = 0,                     //Do not set GPIO pull-up
    };
    gpio_config(&io_conf1); // gpio_ The function of config is to configure GPIO pin parameters

    //Initialize GPIO4/5
    gpio_config_t io_conf2;
    io_conf2.intr_type = GPIO_INTR_POSEDGE;     //Rising edge, lower rising edge: GPIO_INTR_NEGEDGE
    io_conf2.pin_bit_mask = GPIO_INPUT_PIN_SEL; //Set GPIO4 as output pin 0 and GPIO5 as output pin 1 as mixed mode
    io_conf2.mode = GPIO_MODE_INPUT;            // GPIO set to input mode
    io_conf2.pull_up_en = 1;                    //Enable pull-up
    gpio_config(&io_conf2);                     // gpio_ The function of config is to configure GPIO pin parameters

    //Interrupt on rising and falling edge of gpio pin (any one)
    gpio_set_intr_type(GPIO_INPUT_IO_0, GPIO_INTR_ANYEDGE);

    gpio_evt_queue = xQueueCreate(10, sizeof(uint32_t));                       //Create a variable to store queue data
    xTaskCreate(gpio_task_example, "gpio_task_example", 2048, NULL, 10, NULL); //Create tasks that enable GPIO interrupts

    //Install gpio interrupt driver
    gpio_install_isr_service(ESP_INTR_FLAG_DEFAULT);
    //Bind the interrupt service function to the specified gpio
    gpio_isr_handler_add(GPIO_INPUT_IO_0, gpio_isr_handler, (void *)GPIO_INPUT_IO_0);
    gpio_isr_handler_add(GPIO_INPUT_IO_1, gpio_isr_handler, (void *)GPIO_INPUT_IO_1);

    //Delete the interrupt service function of the corresponding GPIO pin
    gpio_isr_handler_remove(GPIO_INPUT_IO_0);
    //Bind the interrupt service function to the specified gpio again
    gpio_isr_handler_add(GPIO_INPUT_IO_0, gpio_isr_handler, (void *)GPIO_INPUT_IO_0);

    //Print memory usage
    printf("Minimum free heap size: %d bytes\n", esp_get_minimum_free_heap_size());

    int cnt = 0;
    while (1)
    {
        printf("cnt: %d\n", cnt++);
        vTaskDelay(1000 / portTICK_RATE_MS);       //Delay 1s
        gpio_set_level(GPIO_OUTPUT_IO_0, cnt % 2); //Set output low level
        gpio_set_level(GPIO_OUTPUT_IO_1, cnt % 2); //Set output low level
    }
}