From 7662593a30319809aa5160fa382689b675615f15 Mon Sep 17 00:00:00 2001 From: Alexey Zholtikov Date: Sun, 23 Jun 2024 09:49:31 +0300 Subject: [PATCH] Version 2.0.0 Added support AM2320 sensor. Major main code refactoring. --- README.md | 79 ++++++++++-- include/zh_dht.h | 49 +++++--- version.txt | 2 +- zh_dht.c | 307 +++++++++++++++++++++++++++++++++-------------- 4 files changed, 318 insertions(+), 119 deletions(-) diff --git a/README.md b/README.md index 5ed89f8..5498e56 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# ESP32 ESP-IDF and ESP8266 RTOS SDK component for DHT11/DHT22(AM2302) humidity & temperature sensor +# ESP32 ESP-IDF and ESP8266 RTOS SDK component for DHT11/DHT22/AM2302/AM2320 humidity & temperature sensor ## Tested on @@ -22,24 +22,77 @@ In the application, add the component: ## Example -Reading the sensor: +Reading the sensor with 1-wire connection (DHT11, DHT22, AM2302, AM2320): ```c #include "zh_dht.h" void app_main(void) { - esp_log_level_set("zh_dht", ESP_LOG_NONE); - zh_dht_handle_t dht_handle = zh_dht_init(ZH_DHT22, GPIO_NUM_5); - float humidity = 0.0; - float temperature = 0.0; - for (;;) - { - zh_dht_read(&dht_handle, &humidity, &temperature); - printf("Humidity %0.2f\n", humidity); - printf("Temperature %0.2f\n", temperature); - vTaskDelay(5000 / portTICK_PERIOD_MS); - } + esp_log_level_set("zh_dht", ESP_LOG_NONE); + zh_dht_init_config_t dht_init_config = ZH_DHT_INIT_CONFIG_DEFAULT(); + dht_init_config.sensor_pin = GPIO_NUM_5; + zh_dht_init(&dht_init_config); + float humidity = 0.0; + float temperature = 0.0; + for (;;) + { + zh_dht_read(&humidity, &temperature); + printf("Humidity %0.2f\n", humidity); + printf("Temperature %0.2f\n", temperature); + vTaskDelay(5000 / portTICK_PERIOD_MS); + } +} +``` + +Reading the sensor with I2C connection (AM2320 only): + +```c +#include "zh_dht.h" + +#define I2C_PORT (I2C_NUM_MAX - 1) + +void app_main(void) +{ + esp_log_level_set("zh_dht", ESP_LOG_NONE); + #ifdef CONFIG_IDF_TARGET_ESP8266 + i2c_config_t i2c_config = { + .mode = I2C_MODE_MASTER, + .sda_io_num = GPIO_NUM_4, // In accordance with used chip. + .sda_pullup_en = GPIO_PULLUP_ENABLE, + .scl_io_num = GPIO_NUM_5, // In accordance with used chip. + .scl_pullup_en = GPIO_PULLUP_ENABLE, + }; + i2c_driver_install(I2C_PORT, i2c_config.mode); + i2c_param_config(I2C_PORT, &i2c_config); +#else + i2c_master_bus_config_t i2c_bus_config = { + .clk_source = I2C_CLK_SRC_DEFAULT, + .i2c_port = I2C_PORT, + .scl_io_num = GPIO_NUM_22, // In accordance with used chip. + .sda_io_num = GPIO_NUM_21, // In accordance with used chip. + .glitch_ignore_cnt = 7, + .flags.enable_internal_pullup = true, + }; + i2c_master_bus_handle_t i2c_bus_handle; + i2c_new_master_bus(&i2c_bus_config, &i2c_bus_handle); +#endif + zh_dht_init_config_t dht_init_config = ZH_DHT_INIT_CONFIG_DEFAULT(); +#ifdef CONFIG_IDF_TARGET_ESP8266 + dht_init_config.i2c_port = I2C_PORT; +#else + dht_init_config.i2c_handle = i2c_bus_handle; +#endif + zh_dht_init(&dht_init_config); + float humidity = 0.0; + float temperature = 0.0; + for (;;) + { + zh_dht_read(&humidity, &temperature); + printf("Humidity %0.2f\n", humidity); + printf("Temperature %0.2f\n", temperature); + vTaskDelay(5000 / portTICK_PERIOD_MS); + } } ``` diff --git a/include/zh_dht.h b/include/zh_dht.h index 4d08eb4..7fbd99a 100644 --- a/include/zh_dht.h +++ b/include/zh_dht.h @@ -5,51 +5,68 @@ #include "esp_err.h" #include "esp_log.h" #include "driver/gpio.h" +#ifdef CONFIG_IDF_TARGET_ESP8266 +#include "driver/i2c.h" +#else +#include "driver/i2c_master.h" +#endif #include "freertos/FreeRTOS.h" #include "freertos/task.h" +#define ZH_DHT_INIT_CONFIG_DEFAULT() \ + { \ + .sensor_pin = 0xFF, \ + .i2c_port = 0 \ + } + #ifdef __cplusplus extern "C" { #endif - typedef enum // Enumeration of supported sensor types. + typedef struct { - ZH_DHT11, // Sensor type DHT11. - ZH_DHT22 // Sensor type DHT22 or AM2302. - } zh_dht_sensor_type_t; - - typedef struct // Unique handle of the sensor. - { - uint8_t sensor_pin; // Sensor GPIO connection. - zh_dht_sensor_type_t sensor_type; // Sensor type. - } zh_dht_handle_t; + uint8_t sensor_pin; // Sensor GPIO connection. + bool i2c_port; // I2C port. +#ifndef CONFIG_IDF_TARGET_ESP8266 + i2c_master_bus_handle_t i2c_handle; // Unique I2C bus handle. +#endif + } zh_dht_init_config_t; /** * @brief Initialize DHT sensor. * - * @param[in] sensor_type Sensor type. - * @param[in] sensor_pin Sensor connection gpio. + * @param[in] config Pointer to DHT initialized configuration structure. Can point to a temporary variable. * - * @return Handle of the sensor + * @attention I2C driver must be initialized first (for I2C connection only). + * + * @note Before initialize the sensor recommend initialize zh_dht_init_config_t structure with default values. + * + * @code zh_dht_init_config_t config = ZH_DHT_INIT_CONFIG_DEFAULT() @endcode + * + * @return + * - ESP_OK if initialization was success + * - ESP_ERR_INVALID_ARG if parameter error + * - ESP_ERR_NOT_FOUND if sensor not connected or not responded (for I2C connection only) */ - zh_dht_handle_t zh_dht_init(const zh_dht_sensor_type_t sensor_type, const uint8_t sensor_pin); + esp_err_t zh_dht_init(const zh_dht_init_config_t *config); /** * @brief Read DHT sensor. * - * @param[in] dht_handle Pointer for handle of the sensor. * @param[out] humidity Pointer for DHT sensor reading data of humidity. * @param[out] temperature Pointer for DHT sensor reading data of temperature. * * @return * - ESP_OK if read was success * - ESP_ERR_INVALID_ARG if parameter error + * - ESP_ERR_NOT_FOUND if DHT is not initialized * - ESP_ERR_INVALID_RESPONSE if the bus is busy + * - ESP_ERR_INVALID_STATE if I2C driver not installed or not in master mode * - ESP_ERR_TIMEOUT if operation timeout * - ESP_ERR_INVALID_CRC if check CRC is fail */ - esp_err_t zh_dht_read(const zh_dht_handle_t *dht_handle, float *humidity, float *temperature); + esp_err_t zh_dht_read(float *humidity, float *temperature); #ifdef __cplusplus } diff --git a/version.txt b/version.txt index afaf360..359a5b9 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -1.0.0 \ No newline at end of file +2.0.0 \ No newline at end of file diff --git a/zh_dht.c b/zh_dht.c index b7ea2a3..d24c888 100644 --- a/zh_dht.c +++ b/zh_dht.c @@ -1,11 +1,14 @@ #include "zh_dht.h" -#define BIT_1_TRANSFER_MAX_DURATION 75 // Signal "1" high time. -#define BIT_0_TRANSFER_MAX_DURATION 30 // Signal "0" high time. -#define DATA_BIT_START_TRANSFER_MAX_DURATION 55 // Signal "0", "1" low time. -#define RESPONSE_MAX_DURATION 85 // Response to low time. Response to high time. -#define MASTER_RELEASE_MAX_DURATION 200 // Bus master has released time. -#define DATA_SIZE 40 +#define BIT_1_TRANSFER_MAX_DURATION 75 // Signal "1" high time for 1-wire connection. +#define BIT_0_TRANSFER_MAX_DURATION 30 // Signal "0" high time for 1-wire connection. +#define DATA_BIT_START_TRANSFER_MAX_DURATION 55 // Signal "0", "1" low time for 1-wire connection. +#define RESPONSE_MAX_DURATION 85 // Response to low time. Response to high time. For 1-wire connection. +#define MASTER_RELEASE_MAX_DURATION 200 // Bus master has released time for 1-wire connection. +#define ONE_WIRE_DATA_SIZE 40 // Sensor data size for 1-wire connection (in bits). +#define I2C_DATA_SIZE 8 // Sensor data size for I2C connection (in bytes). +#define I2C_ADDRESS 0x5C // Sensor address for I2C connection. +#define I2C_DATA_READ_COMMAND 0x03, 0x00, 0x04 // Command for read sensor data (temperature and humidity) for I2C connection. #ifdef CONFIG_IDF_TARGET_ESP8266 #define esp_delay_us(x) os_delay_us(x) @@ -13,131 +16,235 @@ #define esp_delay_us(x) esp_rom_delay_us(x) #endif +static zh_dht_init_config_t _init_config = {0}; +static bool _is_initialized = false; +#ifndef CONFIG_IDF_TARGET_ESP8266 +static i2c_master_dev_handle_t _dht_handle = {0}; +#endif + static const char *TAG = "zh_dht"; -static esp_err_t _read_bit(const zh_dht_handle_t *dht_handle, bool *bit); +static esp_err_t _read_bit(bool *bit); +static uint16_t _calc_crc(const uint8_t *buf, size_t len); -zh_dht_handle_t zh_dht_init(const zh_dht_sensor_type_t sensor_type, const uint8_t sensor_pin) +esp_err_t zh_dht_init(const zh_dht_init_config_t *config) { ESP_LOGI(TAG, "DHT initialization begin."); - zh_dht_handle_t zh_dht_handle = { - .sensor_type = sensor_type, - .sensor_pin = sensor_pin}; - gpio_config_t config = {0}; - config.intr_type = GPIO_INTR_DISABLE; - config.mode = GPIO_MODE_INPUT; - config.pin_bit_mask = (1ULL << sensor_pin); - config.pull_down_en = GPIO_PULLDOWN_DISABLE; - config.pull_up_en = GPIO_PULLUP_ENABLE; - if (gpio_config(&config) != ESP_OK) + if (config == NULL) { - zh_dht_handle.sensor_pin = 0xFF; - ESP_LOGE(TAG, "DHT initialization fail. Incorrect GPIO number."); - return zh_dht_handle; + ESP_LOGE(TAG, "DHT initialization fail. Invalid argument."); + return ESP_ERR_INVALID_ARG; } - ESP_LOGI(TAG, "DHT initialization success."); - return zh_dht_handle; + _init_config = *config; + if (_init_config.sensor_pin != 0xFF) + { + gpio_config_t one_wire_config = {0}; + one_wire_config.intr_type = GPIO_INTR_DISABLE; + one_wire_config.mode = GPIO_MODE_INPUT; + one_wire_config.pin_bit_mask = (1ULL << _init_config.sensor_pin); + one_wire_config.pull_down_en = GPIO_PULLDOWN_DISABLE; + one_wire_config.pull_up_en = GPIO_PULLUP_ENABLE; + if (gpio_config(&one_wire_config) != ESP_OK) + { + ESP_LOGE(TAG, "DHT initialization fail. Incorrect GPIO number."); + return ESP_ERR_INVALID_ARG; + } + ESP_LOGI(TAG, "DHT initialization success. 1-wire connection."); + } + else + { +#ifndef CONFIG_IDF_TARGET_ESP8266 + i2c_device_config_t dht_config = { + .dev_addr_length = I2C_ADDR_BIT_LEN_7, + .device_address = I2C_ADDRESS, + .scl_speed_hz = 100000, + }; + i2c_master_bus_add_device(_init_config.i2c_handle, &dht_config, &_dht_handle); + if (i2c_master_probe(_init_config.i2c_handle, I2C_ADDRESS, 1000 / portTICK_PERIOD_MS) != ESP_OK) + { + ESP_LOGE(TAG, "DHT initialization fail. Sensor not connected or not responded."); + return ESP_ERR_NOT_FOUND; + } +#endif + ESP_LOGI(TAG, "DHT initialization success. I2C connection."); + } + _is_initialized = true; + return ESP_OK; } -esp_err_t zh_dht_read(const zh_dht_handle_t *dht_handle, float *humidity, float *temperature) +esp_err_t zh_dht_read(float *humidity, float *temperature) { ESP_LOGI(TAG, "DHT read begin."); - if (dht_handle == NULL || humidity == NULL || temperature == NULL || dht_handle->sensor_pin == 0xFF) + if (humidity == NULL || temperature == NULL) { ESP_LOGE(TAG, "DHT read fail. Invalid argument."); return ESP_ERR_INVALID_ARG; } - if (gpio_get_level(dht_handle->sensor_pin) != 1) + if (_is_initialized == false) { - ESP_LOGE(TAG, "DHT read fail. Bus is busy."); - return ESP_ERR_INVALID_RESPONSE; + ESP_LOGE(TAG, "DHT read fail. DHT not initialized."); + return ESP_ERR_NOT_FOUND; } - gpio_set_direction(dht_handle->sensor_pin, GPIO_MODE_OUTPUT); - gpio_set_level(dht_handle->sensor_pin, 0); - vTaskDelay(10 / portTICK_PERIOD_MS); - gpio_set_level(dht_handle->sensor_pin, 1); - gpio_set_direction(dht_handle->sensor_pin, GPIO_MODE_INPUT); - uint8_t time = 0; - while (gpio_get_level(dht_handle->sensor_pin) == 1) + if (_init_config.sensor_pin != 0xFF) { - if (time > MASTER_RELEASE_MAX_DURATION) + if (gpio_get_level(_init_config.sensor_pin) != 1) { - ESP_LOGE(TAG, "DHT read fail. Timeout exceeded."); - return ESP_ERR_TIMEOUT; + ESP_LOGE(TAG, "DHT read fail. Bus is busy."); + return ESP_ERR_INVALID_RESPONSE; } - ++time; - esp_delay_us(1); - } - time = 0; - while (gpio_get_level(dht_handle->sensor_pin) == 0) - { - if (time > RESPONSE_MAX_DURATION) + gpio_set_direction(_init_config.sensor_pin, GPIO_MODE_OUTPUT); + gpio_set_level(_init_config.sensor_pin, 0); + vTaskDelay(10 / portTICK_PERIOD_MS); + gpio_set_level(_init_config.sensor_pin, 1); + gpio_set_direction(_init_config.sensor_pin, GPIO_MODE_INPUT); + uint8_t time = 0; + while (gpio_get_level(_init_config.sensor_pin) == 1) { - ESP_LOGE(TAG, "DHT read fail. Timeout exceeded."); - return ESP_ERR_TIMEOUT; + if (time > MASTER_RELEASE_MAX_DURATION) + { + ESP_LOGE(TAG, "DHT read fail. Timeout exceeded."); + return ESP_ERR_TIMEOUT; + } + ++time; + esp_delay_us(1); } - ++time; - esp_delay_us(1); - } - time = 0; - while (gpio_get_level(dht_handle->sensor_pin) == 1) - { - if (time > RESPONSE_MAX_DURATION) + time = 0; + while (gpio_get_level(_init_config.sensor_pin) == 0) { - ESP_LOGE(TAG, "DHT read fail. Timeout exceeded."); - return ESP_ERR_TIMEOUT; + if (time > RESPONSE_MAX_DURATION) + { + ESP_LOGE(TAG, "DHT read fail. Timeout exceeded."); + return ESP_ERR_TIMEOUT; + } + ++time; + esp_delay_us(1); } - ++time; - esp_delay_us(1); - } - uint8_t dht_data[DATA_SIZE / 8] = {0}; - uint8_t byte_index = 0; - uint8_t bit_index = 7; - for (uint8_t i = 0; i < 40; ++i) - { - bool bit = 0; - if (_read_bit(dht_handle, &bit) != ESP_OK) + time = 0; + while (gpio_get_level(_init_config.sensor_pin) == 1) { - ESP_LOGE(TAG, "DHT read fail. Timeout exceeded."); - return ESP_ERR_TIMEOUT; + if (time > RESPONSE_MAX_DURATION) + { + ESP_LOGE(TAG, "DHT read fail. Timeout exceeded."); + return ESP_ERR_TIMEOUT; + } + ++time; + esp_delay_us(1); } - dht_data[byte_index] |= (bit << bit_index); - if (bit_index == 0) + uint8_t dht_data[ONE_WIRE_DATA_SIZE / 8] = {0}; + uint8_t byte_index = 0; + uint8_t bit_index = 7; + for (uint8_t i = 0; i < ONE_WIRE_DATA_SIZE; ++i) { - bit_index = 7; - ++byte_index; + bool bit = 0; + if (_read_bit(&bit) != ESP_OK) + { + ESP_LOGE(TAG, "DHT read fail. Timeout exceeded."); + return ESP_ERR_TIMEOUT; + } + dht_data[byte_index] |= (bit << bit_index); + if (bit_index == 0) + { + bit_index = 7; + ++byte_index; + } + else + { + --bit_index; + } } - else + if (dht_data[4] != ((dht_data[0] + dht_data[1] + dht_data[2] + dht_data[3]))) { - --bit_index; + ESP_LOGE(TAG, "DHT read fail. Invalid CRC."); + return ESP_ERR_INVALID_CRC; } - } - if (dht_data[4] != ((dht_data[0] + dht_data[1] + dht_data[2] + dht_data[3]))) - { - ESP_LOGE(TAG, "DHT read fail. Invalid CRC."); - return ESP_ERR_INVALID_CRC; - } - *humidity = (dht_data[0] << 8 | dht_data[1]) / 10.0; - if (dht_handle->sensor_type == ZH_DHT22) - { - *temperature = ((dht_data[2] & 0b01111111) << 8 | dht_data[3]) / 10.0; - if ((dht_data[2] & 0b10000000) != 0) + *humidity = (dht_data[0] << 8 | dht_data[1]) / 10.0; + *temperature = ((dht_data[2] & 0x7F) << 8 | dht_data[3]) / 10.0; + if ((dht_data[2] & 0x80) != 0) { *temperature *= -1; } } else { - *temperature = (dht_data[2] << 8 | dht_data[3]) / 10.0; + esp_err_t esp_err = ESP_OK; + uint8_t dht_data[I2C_DATA_SIZE] = {0}; + uint8_t read_command[] = {I2C_DATA_READ_COMMAND}; +#ifdef CONFIG_IDF_TARGET_ESP8266 + i2c_cmd_handle_t i2c_cmd_handle = i2c_cmd_link_create(); + i2c_master_start(i2c_cmd_handle); + i2c_master_write_byte(i2c_cmd_handle, I2C_ADDRESS << 1 | I2C_MASTER_WRITE, false); + i2c_master_stop(i2c_cmd_handle); + esp_err = i2c_master_cmd_begin(_init_config.i2c_port, i2c_cmd_handle, 1000 / portTICK_PERIOD_MS); + i2c_cmd_link_delete(i2c_cmd_handle); + if (esp_err != ESP_OK) + { + ESP_LOGE(TAG, "DHT read fail. I2C driver error."); + return esp_err; + } + i2c_cmd_handle = i2c_cmd_link_create(); + i2c_master_start(i2c_cmd_handle); + i2c_master_write_byte(i2c_cmd_handle, I2C_ADDRESS << 1 | I2C_MASTER_WRITE, true); + for (uint8_t i = 0; i < sizeof(read_command); ++i) + { + i2c_master_write_byte(i2c_cmd_handle, read_command[i], true); + } + i2c_master_stop(i2c_cmd_handle); + esp_err = i2c_master_cmd_begin(_init_config.i2c_port, i2c_cmd_handle, 1000 / portTICK_PERIOD_MS); + i2c_cmd_link_delete(i2c_cmd_handle); +#else + uint8_t wakeup_command = {0}; + esp_err = i2c_master_transmit(_dht_handle, &wakeup_command, sizeof(wakeup_command), 1000 / portTICK_PERIOD_MS); + if (esp_err != ESP_OK) + { + ESP_LOGE(TAG, "DHT read fail. I2C driver error."); + return esp_err; + } + esp_err = i2c_master_transmit(_dht_handle, read_command, sizeof(read_command), 1000 / portTICK_PERIOD_MS); +#endif + if (esp_err != ESP_OK) + { + ESP_LOGE(TAG, "DHT read fail. I2C driver error."); + return esp_err; + } +#ifdef CONFIG_IDF_TARGET_ESP8266 + i2c_cmd_handle = i2c_cmd_link_create(); + i2c_master_start(i2c_cmd_handle); + i2c_master_write_byte(i2c_cmd_handle, I2C_ADDRESS << 1 | I2C_MASTER_READ, true); + for (uint8_t i = 0; i < sizeof(dht_data); ++i) + { + i2c_master_read_byte(i2c_cmd_handle, &dht_data[i], i == (sizeof(dht_data) - 1) ? I2C_MASTER_NACK : I2C_MASTER_ACK); + } + i2c_master_stop(i2c_cmd_handle); + esp_err = i2c_master_cmd_begin(_init_config.i2c_port, i2c_cmd_handle, 1000 / portTICK_PERIOD_MS); + i2c_cmd_link_delete(i2c_cmd_handle); +#else + esp_err = i2c_master_receive(_dht_handle, dht_data, sizeof(dht_data), 1000 / portTICK_PERIOD_MS); +#endif + if (esp_err != ESP_OK) + { + ESP_LOGE(TAG, "DHT read fail. I2C driver error."); + return esp_err; + } + if (_calc_crc(dht_data, I2C_DATA_SIZE - 2) != (dht_data[7] << 8 | dht_data[6])) + { + ESP_LOGE(TAG, "DHT read fail. Invalid CRC."); + return ESP_ERR_INVALID_CRC; + } + *humidity = (dht_data[2] << 8 | dht_data[3]) / 10.0; + *temperature = ((dht_data[4] & 0x7F) << 8 | dht_data[5]) / 10.0; + if ((dht_data[4] & 0x80) != 0) + { + *temperature *= -1; + } } ESP_LOGI(TAG, "DHT read success."); return ESP_OK; } -static esp_err_t _read_bit(const zh_dht_handle_t *dht_handle, bool *bit) +static esp_err_t _read_bit(bool *bit) { uint8_t time = 0; - while (gpio_get_level(dht_handle->sensor_pin) == 0) + while (gpio_get_level(_init_config.sensor_pin) == 0) { if (time > DATA_BIT_START_TRANSFER_MAX_DURATION) { @@ -147,7 +254,7 @@ static esp_err_t _read_bit(const zh_dht_handle_t *dht_handle, bool *bit) esp_delay_us(1); } time = 0; - while (gpio_get_level(dht_handle->sensor_pin) == 1) + while (gpio_get_level(_init_config.sensor_pin) == 1) { if (time > BIT_1_TRANSFER_MAX_DURATION) { @@ -158,4 +265,26 @@ static esp_err_t _read_bit(const zh_dht_handle_t *dht_handle, bool *bit) } *bit = (time > BIT_0_TRANSFER_MAX_DURATION) ? 1 : 0; return ESP_OK; +} + +static uint16_t _calc_crc(const uint8_t *buf, size_t len) +{ + uint16_t crc = 0xFFFF; + while (len--) + { + crc ^= (uint16_t)*buf++; + for (unsigned i = 0; i < 8; i++) + { + if (crc & 0x0001) + { + crc >>= 1; + crc ^= 0xA001; + } + else + { + crc >>= 1; + } + } + } + return crc; } \ No newline at end of file