#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/gpio.h"
#include "esp_timer.h"

// INÍCIO DA BIBLIOTECA DO DHT -------------------------------------------

#define DHT_OK            0
#define DHT_TIMEOUT       1
#define DHT_CHECKSUM_FAIL 2
#define DHT_NOT_READY     3

typedef struct {
  uint8_t gpio;
  uint64_t time;
  uint64_t buffer;
} dht_handle_t;

uint8_t dht_get_raw(dht_handle_t * dht) {
  uint8_t count = 0;
  uint64_t pulse_start;
  uint8_t checksum;

  if (dht->time > esp_timer_get_time()) {
    dht->time = esp_timer_get_time();
  }

  if (esp_timer_get_time() - dht->time > 2e6) {
    dht->buffer = 0;

    gpio_set_direction(dht->gpio, GPIO_MODE_OUTPUT);
    gpio_set_level(dht->gpio, 0);
    vTaskDelay(20 / portTICK_PERIOD_MS);
    gpio_set_level(dht->gpio, 1);
    gpio_set_direction(dht->gpio, GPIO_MODE_INPUT);
    gpio_pullup_en(dht->gpio);

    dht->time = esp_timer_get_time();

    while (count < 41) {
      while (!gpio_get_level(dht->gpio)) {
        if (esp_timer_get_time() - dht->time > 12000) {
          return DHT_TIMEOUT;
        }
      }

      pulse_start = esp_timer_get_time();

      while (gpio_get_level(dht->gpio)) {
        if (esp_timer_get_time() - dht->time > 12000) {
          return DHT_TIMEOUT;
        }
      }

      if (count && (esp_timer_get_time() - pulse_start > 60)) {
        dht->buffer |= (1LL) << (40 - count);
      }

      count++;
    }

    checksum = 0;
    for (uint8_t i = 8; i < 40; i += 8) {
      checksum += (dht->buffer >> i) & 0xFF;
    }

    if (checksum == (dht->buffer & 0xFF)) {
      return DHT_OK;
    }

    else {
      return DHT_CHECKSUM_FAIL;
    }

    dht->time = esp_timer_get_time();
  }

  return DHT_NOT_READY;
}

float dht_get_temperature(dht_handle_t * dht) {
  if ((dht->buffer >> 16) & 0x80) {
    return ((dht->buffer >> 8) & 0x7FFF) / -10.0;
  }

  else {
    return ((dht->buffer >> 8) & 0x7FFF) / 10.0;
  }
}

float dht_get_humidity(dht_handle_t * dht) {
  dht_get_raw(dht);

  return ((dht->buffer >> 24) & 0xFFFF) / 10.0;
}

// FIM DA BIBLIOTECA DO DHT ----------------------------------------------

void app_main() {
  esp_timer_early_init(); // Necessário para temporizar leitura do DHT

  dht_handle_t dht22 = {
    .gpio = 21,
    .time = 0
  };
  
  while (1) {
    if (dht_get_raw(&dht22) == DHT_OK) {
      printf("Temperatura: %.1f°C\n", dht_get_temperature(&dht22));
      printf("Umidade: %.1f%%\n", dht_get_humidity(&dht22));
      printf("--------------------\n");
    }
    vTaskDelay(100 / portTICK_PERIOD_MS);
  }
}