Files
dht22_sensor/dht22_sensor.c
Andrey Aleksandrov 236d5e4a8c Initial commit
2025-12-30 21:48:48 +02:00

307 lines
8.6 KiB
C

#include "dht22_sensor.h"
#include "esp_log.h"
#include "esp_rom_sys.h"
#include "freertos/FreeRTOS.h"
#include "freertos/portmacro.h"
#include <string.h>
static const char *TAG = "DHT22_SENSOR";
// Default DHT22 timing constants (microseconds)
#define DHT22_DEFAULT_START_SIGNAL_US 18000 // 18ms start signal
#define DHT22_DEFAULT_RESPONSE_TIMEOUT_US 500 // 500us timeout for response
#define DHT22_DEFAULT_DATA_TIMEOUT_US 150 // 150us timeout for data bits
#define DHT22_DEFAULT_BIT_THRESHOLD_US 40 // Threshold for 0/1 detection
// Global configuration
static dht22_config_t current_config = {0};
static bool is_initialized = false;
// Internal function to read from DHT22
static dht22_result_t dht22_read_internal(int gpio_pin, float *temperature, float *humidity, const dht22_config_t *config);
esp_err_t dht22_init(int gpio_pin)
{
dht22_config_t default_config = {
.gpio_pin = gpio_pin,
.start_signal_us = DHT22_DEFAULT_START_SIGNAL_US,
.response_timeout_us = DHT22_DEFAULT_RESPONSE_TIMEOUT_US,
.data_timeout_us = DHT22_DEFAULT_DATA_TIMEOUT_US,
.bit_threshold_us = DHT22_DEFAULT_BIT_THRESHOLD_US};
return dht22_init_with_config(&default_config);
}
esp_err_t dht22_init_with_config(const dht22_config_t *config)
{
if (config == NULL)
{
ESP_LOGE(TAG, "DHT22 config cannot be NULL");
return ESP_ERR_INVALID_ARG;
}
if (config->gpio_pin < 0)
{
ESP_LOGE(TAG, "Invalid GPIO pin: %d", config->gpio_pin);
return ESP_ERR_INVALID_ARG;
}
ESP_LOGI(TAG, "Initializing DHT22 on GPIO %d", config->gpio_pin);
// Store configuration
memcpy(&current_config, config, sizeof(dht22_config_t));
is_initialized = true;
ESP_LOGI(TAG, "DHT22 initialized successfully");
return ESP_OK;
}
dht22_result_t dht22_read(float *temperature, float *humidity)
{
if (!is_initialized)
{
ESP_LOGE(TAG, "DHT22 not initialized");
return DHT22_TIMEOUT_ERROR;
}
return dht22_read_internal(current_config.gpio_pin, temperature, humidity, &current_config);
}
dht22_result_t dht22_read_data(dht22_data_t *data)
{
if (data == NULL)
{
ESP_LOGE(TAG, "DHT22 data pointer cannot be NULL");
return DHT22_TIMEOUT_ERROR;
}
return dht22_read(&data->temperature, &data->humidity);
}
dht22_result_t dht22_read_from_pin(int gpio_pin, float *temperature, float *humidity)
{
dht22_config_t temp_config = {
.gpio_pin = gpio_pin,
.start_signal_us = DHT22_DEFAULT_START_SIGNAL_US,
.response_timeout_us = DHT22_DEFAULT_RESPONSE_TIMEOUT_US,
.data_timeout_us = DHT22_DEFAULT_DATA_TIMEOUT_US,
.bit_threshold_us = DHT22_DEFAULT_BIT_THRESHOLD_US};
return dht22_read_internal(gpio_pin, temperature, humidity, &temp_config);
}
static dht22_result_t dht22_read_internal(int gpio_pin, float *temperature, float *humidity, const dht22_config_t *config)
{
if (temperature == NULL || humidity == NULL)
{
ESP_LOGE(TAG, "Temperature and humidity pointers cannot be NULL");
return DHT22_TIMEOUT_ERROR;
}
uint8_t data[5] = {0};
int counter = 0;
ESP_LOGD(TAG, "Reading DHT22 from GPIO %d", gpio_pin);
// Disable interrupts during critical timing
portDISABLE_INTERRUPTS();
// Configure GPIO
gpio_config_t io_conf = {
.pin_bit_mask = (1ULL << gpio_pin),
.mode = GPIO_MODE_OUTPUT_OD,
.pull_up_en = GPIO_PULLUP_ENABLE,
.pull_down_en = GPIO_PULLDOWN_DISABLE,
.intr_type = GPIO_INTR_DISABLE,
};
gpio_config(&io_conf);
// Send start signal
gpio_set_level(gpio_pin, 0);
esp_rom_delay_us(config->start_signal_us);
gpio_set_level(gpio_pin, 1);
esp_rom_delay_us(40);
// Switch to input mode
io_conf.mode = GPIO_MODE_INPUT;
gpio_config(&io_conf);
// Wait for DHT22 response (should go low then high)
counter = 0;
while (gpio_get_level(gpio_pin) == 1)
{
esp_rom_delay_us(1);
if (++counter > config->response_timeout_us)
{
portENABLE_INTERRUPTS();
ESP_LOGD(TAG, "DHT22 timeout waiting for initial response");
return DHT22_TIMEOUT_ERROR;
}
}
// Wait for response signal to go high
counter = 0;
while (gpio_get_level(gpio_pin) == 0)
{
esp_rom_delay_us(1);
if (++counter > config->response_timeout_us)
{
portENABLE_INTERRUPTS();
ESP_LOGD(TAG, "DHT22 timeout waiting for response high");
return DHT22_TIMEOUT_ERROR;
}
}
// Wait for response signal to go low (start of data)
counter = 0;
while (gpio_get_level(gpio_pin) == 1)
{
esp_rom_delay_us(1);
if (++counter > config->response_timeout_us)
{
portENABLE_INTERRUPTS();
ESP_LOGD(TAG, "DHT22 timeout waiting for data start");
return DHT22_TIMEOUT_ERROR;
}
}
// Read 40 bits of data
for (int i = 0; i < 40; i++)
{
// Wait for signal to go high
counter = 0;
while (gpio_get_level(gpio_pin) == 0)
{
esp_rom_delay_us(1);
if (++counter > config->data_timeout_us)
{
portENABLE_INTERRUPTS();
ESP_LOGD(TAG, "DHT22 timeout waiting for bit %d high signal", i);
return DHT22_TIMEOUT_ERROR;
}
}
// Measure high pulse duration
counter = 0;
while (gpio_get_level(gpio_pin) == 1)
{
esp_rom_delay_us(1);
if (++counter > config->data_timeout_us)
{
portENABLE_INTERRUPTS();
ESP_LOGD(TAG, "DHT22 timeout during bit %d high pulse", i);
return DHT22_TIMEOUT_ERROR;
}
}
// DHT22 protocol: '0' = ~26-28us high, '1' = ~70us high
data[i / 8] <<= 1;
if (counter > config->bit_threshold_us)
{
data[i / 8] |= 1;
}
// Debug first few bits if we're getting all zeros
if (i < 8)
{
ESP_LOGD(TAG, "Bit %d: pulse=%dus, value=%d", i, counter, (counter > config->bit_threshold_us) ? 1 : 0);
}
}
// Re-enable interrupts
portENABLE_INTERRUPTS();
// Verify checksum
uint8_t checksum = data[0] + data[1] + data[2] + data[3];
// Debug output for problematic readings
if ((data[0] == 0 && data[1] == 0 && data[2] == 0 && data[3] == 0) || checksum != data[4])
{
ESP_LOGD(TAG, "DHT22 raw data: %02X %02X %02X %02X %02X",
data[0], data[1], data[2], data[3], data[4]);
ESP_LOGD(TAG, "Calculated checksum: %02X, Received: %02X", checksum, data[4]);
}
if (checksum != data[4])
{
ESP_LOGD(TAG, "DHT22 checksum mismatch");
return DHT22_CHECKSUM_ERROR;
}
// Check for all-zero data (sensor not responding properly)
if (data[0] == 0 && data[1] == 0 && data[2] == 0 && data[3] == 0)
{
ESP_LOGD(TAG, "DHT22 returned all zeros - sensor may not be connected");
return DHT22_TIMEOUT_ERROR; // Treat as timeout to trigger retry
}
// Convert data to temperature and humidity
*humidity = ((data[0] << 8) | data[1]) / 10.0f;
int temp_raw = (data[2] << 8) | data[3];
if (temp_raw & 0x8000)
{
*temperature = -(temp_raw & 0x7FFF) / 10.0f;
}
else
{
*temperature = temp_raw / 10.0f;
}
ESP_LOGD(TAG, "DHT22 read successful: T=%.2f°C, H=%.2f%%", *temperature, *humidity);
return DHT22_OK;
}
const char *dht22_get_error_string(dht22_result_t result)
{
switch (result)
{
case DHT22_OK:
return "Success";
case DHT22_TIMEOUT_ERROR:
return "Timeout error";
case DHT22_CHECKSUM_ERROR:
return "Checksum error";
default:
return "Unknown error";
}
}
bool dht22_test_connection(void)
{
if (!is_initialized)
{
ESP_LOGW(TAG, "DHT22 not initialized for connection test");
return false;
}
float temp, humid;
dht22_result_t result = dht22_read(&temp, &humid);
if (result == DHT22_OK)
{
ESP_LOGI(TAG, "DHT22 connection test successful: T=%.2f°C, H=%.2f%%", temp, humid);
return true;
}
else
{
ESP_LOGW(TAG, "DHT22 connection test failed: %s", dht22_get_error_string(result));
return false;
}
}
esp_err_t dht22_deinit(void)
{
if (!is_initialized)
{
ESP_LOGW(TAG, "DHT22 not initialized");
return ESP_OK;
}
ESP_LOGI(TAG, "Deinitializing DHT22 sensor");
is_initialized = false;
memset(&current_config, 0, sizeof(dht22_config_t));
ESP_LOGI(TAG, "DHT22 sensor deinitialized");
return ESP_OK;
}