/* DHT11 Temperature and Humidity Sensor Driver Implementation */ #include "dht11_driver.h" #include "esp_log.h" #include "esp_timer.h" #include "freertos/FreeRTOS.h" #include "freertos/task.h" #include "rom/ets_sys.h" static const char *TAG = "DHT11"; static gpio_num_t dht_gpio; static int64_t last_read_time = -2000000; // Inicializa para permitir primeira leitura /* Timeout para espera de resposta (em microsegundos) */ #define DHT11_TIMEOUT_US 85 /* Tempo mínimo entre leituras (1 segundo) */ #define DHT11_MIN_INTERVAL_US 1000000 /** * @brief Espera o pino mudar para o nível especificado * * @param level Nível esperado (0 ou 1) * @param timeout_us Timeout em microsegundos * @return Tempo de espera em us, ou -1 se timeout */ static int dht11_wait_for_level(int level, int timeout_us) { int elapsed = 0; while (gpio_get_level(dht_gpio) != level) { if (elapsed++ > timeout_us) { return -1; } ets_delay_us(1); } return elapsed; } /** * @brief Envia sinal de start para o DHT11 */ static esp_err_t dht11_send_start_signal(void) { /* Configura GPIO como saída */ gpio_set_direction(dht_gpio, GPIO_MODE_OUTPUT_OD); /* Envia sinal de start: LOW por 18ms */ gpio_set_level(dht_gpio, 0); ets_delay_us(20000); // 20ms para garantir /* Libera o barramento (pull-up externo fará HIGH) */ gpio_set_level(dht_gpio, 1); ets_delay_us(40); /* Configura GPIO como entrada */ gpio_set_direction(dht_gpio, GPIO_MODE_INPUT); return ESP_OK; } /** * @brief Lê 1 byte de dados do DHT11 */ static uint8_t dht11_read_byte(void) { uint8_t byte = 0; for (int i = 0; i < 8; i++) { /* Espera sinal LOW (início do bit) */ if (dht11_wait_for_level(0, DHT11_TIMEOUT_US) < 0) { return 0; } /* Espera sinal HIGH (duração determina bit 0 ou 1) */ if (dht11_wait_for_level(1, DHT11_TIMEOUT_US) < 0) { return 0; } /* Mede duração do HIGH */ ets_delay_us(30); /* Se ainda HIGH após 30us, é bit 1, senão é bit 0 */ if (gpio_get_level(dht_gpio) == 1) { byte |= (1 << (7 - i)); } } return byte; } esp_err_t dht11_init(gpio_num_t gpio_num) { dht_gpio = gpio_num; /* Configura GPIO com pull-up */ gpio_config_t io_conf = { .intr_type = GPIO_INTR_DISABLE, .mode = GPIO_MODE_INPUT, .pin_bit_mask = (1ULL << dht_gpio), .pull_down_en = GPIO_PULLDOWN_DISABLE, .pull_up_en = GPIO_PULLUP_ENABLE, }; esp_err_t ret = gpio_config(&io_conf); if (ret != ESP_OK) { ESP_LOGE(TAG, "Failed to configure GPIO %d", dht_gpio); return ret; } ESP_LOGI(TAG, "DHT11 initialized on GPIO %d", dht_gpio); /* Aguarda sensor estabilizar (1 segundo) */ vTaskDelay(pdMS_TO_TICKS(1000)); return ESP_OK; } esp_err_t dht11_read(dht11_reading_t *reading) { if (reading == NULL) { return ESP_ERR_INVALID_ARG; } /* Inicializa leitura como inválida */ reading->valid = false; reading->temperature = 0.0f; reading->humidity = 0.0f; /* Verifica intervalo mínimo entre leituras */ int64_t current_time = esp_timer_get_time(); if ((current_time - last_read_time) < DHT11_MIN_INTERVAL_US) { ESP_LOGW(TAG, "Read too soon, wait at least 1 second between reads"); return ESP_ERR_INVALID_STATE; } /* Desabilita interrupções temporariamente para timing preciso */ portDISABLE_INTERRUPTS(); /* Envia sinal de start */ dht11_send_start_signal(); /* Espera resposta do sensor (LOW por 80us) */ if (dht11_wait_for_level(0, DHT11_TIMEOUT_US) < 0) { portENABLE_INTERRUPTS(); ESP_LOGE(TAG, "Timeout waiting for response LOW"); return ESP_ERR_TIMEOUT; } /* Espera HIGH de resposta (80us) */ if (dht11_wait_for_level(1, DHT11_TIMEOUT_US) < 0) { portENABLE_INTERRUPTS(); ESP_LOGE(TAG, "Timeout waiting for response HIGH"); return ESP_ERR_TIMEOUT; } /* Espera início dos dados (LOW) */ if (dht11_wait_for_level(0, DHT11_TIMEOUT_US) < 0) { portENABLE_INTERRUPTS(); ESP_LOGE(TAG, "Timeout waiting for data start"); return ESP_ERR_TIMEOUT; } /* Lê 5 bytes de dados (umidade_int, umidade_dec, temp_int, temp_dec, checksum) */ uint8_t data[5] = {0}; for (int i = 0; i < 5; i++) { data[i] = dht11_read_byte(); } portENABLE_INTERRUPTS(); /* Atualiza timestamp da última leitura */ last_read_time = esp_timer_get_time(); /* Verifica checksum */ uint8_t checksum = data[0] + data[1] + data[2] + data[3]; if (checksum != data[4]) { ESP_LOGE(TAG, "Checksum error: calculated=0x%02X, received=0x%02X", checksum, data[4]); return ESP_ERR_INVALID_CRC; } /* DHT11 retorna valores inteiros (sem parte decimal) */ reading->humidity = (float)data[0]; reading->temperature = (float)data[2]; reading->valid = true; ESP_LOGI(TAG, "Temperature: %.1f°C, Humidity: %.1f%%", reading->temperature, reading->humidity); return ESP_OK; } esp_err_t dht11_read_temperature(float *temperature) { dht11_reading_t reading; esp_err_t ret = dht11_read(&reading); if (ret == ESP_OK && reading.valid) { *temperature = reading.temperature; return ESP_OK; } return ret; } esp_err_t dht11_read_humidity(float *humidity) { dht11_reading_t reading; esp_err_t ret = dht11_read(&reading); if (ret == ESP_OK && reading.valid) { *humidity = reading.humidity; return ESP_OK; } return ret; }