307 lines
8.6 KiB
C
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(¤t_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, ¤t_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(¤t_config, 0, sizeof(dht22_config_t));
|
|
|
|
ESP_LOGI(TAG, "DHT22 sensor deinitialized");
|
|
return ESP_OK;
|
|
} |