#include <stdio.h>
#include <math.h>
#include <stdio.h>
#include <driver/adc.h>
#include "driver/ledc.h"
#include "driver/gpio.h"
#include "esp_spi_flash.h"
#include "esp_system.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "sdkconfig.h"
#include "hal/ledc_types.h"

#define LED_PWM_CHANNEL_1 LEDC_CHANNEL_1
#define LED_PWM_CHANNEL_2 LEDC_CHANNEL_2
#define LED_PWM_TIMER     LEDC_TIMER_1
#define LED_PWM_BIT_NUM   LEDC_TIMER_8_BIT

#define LED_PWM_PIN 5

void led_pwm_init(void)
{
    ledc_channel_config_t ledc_channel1 = {0}, ledc_channel2 = {0};
    ledc_channel1.gpio_num = LED_PWM_PIN;
    ledc_channel1.speed_mode = LEDC_LOW_SPEED_MODE;
    ledc_channel1.channel = LED_PWM_CHANNEL_1;
    ledc_channel1.intr_type = LEDC_INTR_DISABLE;
    ledc_channel1.timer_sel = LED_PWM_TIMER;
    ledc_channel1.duty = 0;

    ledc_timer_config_t ledc_timer;
	
    ledc_timer.speed_mode = LEDC_LOW_SPEED_MODE;
    ledc_timer.duty_resolution = LED_PWM_BIT_NUM;
    ledc_timer.timer_num = LED_PWM_TIMER;
    ledc_timer.freq_hz = 25000;

    ESP_ERROR_CHECK(ledc_channel_config(&ledc_channel1));
    ESP_ERROR_CHECK(ledc_channel_config(&ledc_channel2));
    ESP_ERROR_CHECK(ledc_timer_config(&ledc_timer));
}

float map(uint32_t x,
          uint32_t in_min, uint32_t in_max,
          uint32_t out_min, uint32_t out_max)
{
   return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
}

void led_pwm_set(float duty_fraction)
{
    uint32_t max_duty = (1 << LED_PWM_BIT_NUM) - 1;
    uint32_t duty = lroundf(duty_fraction * (float)max_duty);

    ESP_ERROR_CHECK(ledc_set_duty(LEDC_LOW_SPEED_MODE, LED_PWM_CHANNEL_1, duty));
    ESP_ERROR_CHECK(ledc_update_duty(LEDC_LOW_SPEED_MODE, LED_PWM_CHANNEL_1));
}

void app_main()
{
    // Init LED PWM
    led_pwm_init();

    // Set ADC resolution to 13-bit
    adc1_config_width(ADC_WIDTH_BIT_13);

    // Set the ADC Channel to Channel 0
    adc1_config_channel_atten(ADC1_CHANNEL_0, ADC_ATTEN_DB_11);

    while (true) {
        // Function of reading the analog value of the ADC1 which is the GPIO1
        uint32_t sensorValue = adc1_get_raw(ADC1_CHANNEL_0);
        uint32_t dutyCycle = map(sensorValue, 0, 8191, 0, 255);

        // Print the values ​​on the serial monitor
        printf("Analog Input: %d \tDuty Cycle: %d\n", sensorValue, dutyCycle);

        // Changing the LED brightness with PWM
        led_pwm_set(dutyCycle);

        // Wait 1 second
        vTaskDelay(1000 / portTICK_PERIOD_MS);

        // Flush output data buffers
        fflush(stdout);
    }
}