diff --git a/components/sensors/humiture/aht20/CHANGELOG.md b/components/sensors/humiture/aht20/CHANGELOG.md index 4c1a110ec..d24bde119 100644 --- a/components/sensors/humiture/aht20/CHANGELOG.md +++ b/components/sensors/humiture/aht20/CHANGELOG.md @@ -1,5 +1,8 @@ # ChangeLog +## v1.1.1 (2025-05-28) +* Replace the i2c interface with i2c_bus. + ## v1.0.0 (2024-08-09) * Added description of AHT30 diff --git a/components/sensors/humiture/aht20/CMakeLists.txt b/components/sensors/humiture/aht20/CMakeLists.txt index 0ebacc8d7..6e4a1594e 100644 --- a/components/sensors/humiture/aht20/CMakeLists.txt +++ b/components/sensors/humiture/aht20/CMakeLists.txt @@ -1,9 +1,10 @@ -idf_component_register( - SRCS "aht20.c" - INCLUDE_DIRS "include" - PRIV_INCLUDE_DIRS "priv_include" - REQUIRES "driver" -) +idf_component_register(SRCS "aht20.c" + INCLUDE_DIRS "include") + + +if(CONFIG_SENSOR_INCLUDED_HUMITURE) + target_link_libraries(${COMPONENT_LIB} INTERFACE "-u humiture_aht20_init") +endif() include(package_manager) cu_pkg_define_version(${CMAKE_CURRENT_LIST_DIR}) diff --git a/components/sensors/humiture/aht20/Kconfig b/components/sensors/humiture/aht20/Kconfig new file mode 100644 index 000000000..d3a98e524 --- /dev/null +++ b/components/sensors/humiture/aht20/Kconfig @@ -0,0 +1,9 @@ +menu "AHT20 : CONFIGURATION" + + config AHT20_CHECK_CRC + bool "perform crc check on AHT20 readings" + help + CRC check to be performed on results or not?. + default n + +endmenu diff --git a/components/sensors/humiture/aht20/README.md b/components/sensors/humiture/aht20/README.md index 11af7422c..8669c48b9 100644 --- a/components/sensors/humiture/aht20/README.md +++ b/components/sensors/humiture/aht20/README.md @@ -1,32 +1,89 @@ -[![Component Registry](https://components.espressif.com/components/espressif/aht20/badge.svg)](https://components.espressif.com/components/espressif/aht20) - # Component: AHT20 -I2C driver and definition of AHT20 humidity and temperature sensor. +I2C driver with Sensor Hub support for Aosong AHT20 humidity and temperature sensor using esp-idf. +Tested with AHT20 using ESP32 and ESP32-S3 devkits. + +# Features + + Temperature and humidity measurement + + Thread-safe via esp-i2c-driver + + CRC checksum verification (optional, only via menuconfig) -Components compatible with AHT30 and AHT21 (AHT21 is deprecated). + Configurable I2C clock speed (pre-compilation, not runtime, only via menuconfig) -See [AHT20 datasheet](http://www.aosong.com/en/products-32.html), [AHT30 datasheet](http://www.aosong.com/en/products-131.html). + Unit tested + + + ┌───────────────────┐ + │ Application │ + └────────┬──────────┘ + │ + ▼ + ┌───────────────────┐ + │ AHT20 Driver │ + │ (this component) │ + └────────┬──────────┘ + │ + ▼ + ┌───────────────────┐ + │ i2c_bus component │ + └────────┬──────────┘ + │ + ▼ + ┌───────────────────┐ + │ I2C Bus │ + └────────┬──────────┘ + │ + ▼ + ┌────────────────────────────┐ + │ AHT20 Temperature/Humidity │ + │ Sensor │ + └────────────────────────────┘ -## Usage -### Initialization -> Note: Note: You need to initialize the I2C bus first. +# How To Use + +All public APIs are documented in aht20.h. + +## Driver + +Following are the general guidelines. ```c - aht20_i2c_config_t i2c_conf = { - .i2c_port = I2C_MASTER_NUM, - .i2c_addr = AHT20_ADDRRES_0, - }; - aht20_new_sensor(&i2c_conf, &handle); + //create a AHT20 device object and receive a device handle for it + // my_i2c_bus_handle here is a preintialized i2c_bus_handle_t i2c_bus object + aht20_handle_t aht20_handle = aht20_create( my_i2c_bus_handle, AHT20_ADDRESS_LOW ); //addresses are in aht20.h + + //use the previously created AHT20 device handle for initializing the AHT20 + aht20_init(aht20_handle); + + float_t temperature; + + aht20_read_temperature( aht20_handle, &temperature); + + printf("Temperature = %.2f°C\n", temperature); + + vTaskDelay(pdMS_TO_TICKS(2000)); + + float_t temperature; + + aht20_read_temperature( aht20_handle, &temperature); + + printf("Temperature = %.2f°C\n", temperature); ``` -### Read data -> The user can periodically call the aht20_read_temp_hum API to retrieve real-time data. -```c - uint32_t temp_raw, hum_raw; - float temp, hum; - aht20_read_temp_hum(aht20, &temp_raw, &temp, &hum_raw, &hum); - ESP_LOGI(TAG, "Humidity : %2.2f %%", hum); - ESP_LOGI(TAG, "Temperature : %2.2f degC", temp); -``` \ No newline at end of file +## How to Configure CRC +Additionally, select in menuconfig under Component Config → AHT20; to use CRC(default is not used) +or change the clock speed of device (default is 100KHz). + +Note : It is recommended to use clock speeds in upper ranges of 100kHz to 200kHz. +Higher clock speeds may cause occasional data inconsistencies depending on your board layout and wiring. + +![image](https://github.com/user-attachments/assets/58a07cc9-5d87-4afe-9675-637b3e776faa) + + +or +In sdkconfig under Component Config → AHT20, + diff --git a/components/sensors/humiture/aht20/aht20.c b/components/sensors/humiture/aht20/aht20.c index c34ee320f..04941d78a 100644 --- a/components/sensors/humiture/aht20/aht20.c +++ b/components/sensors/humiture/aht20/aht20.c @@ -1,160 +1,409 @@ /* - * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ -#include #include -#include + +#include "iot_sensor_hub.h" + #include "aht20.h" -#include "driver/gpio.h" -#include "esp_log.h" -#include "esp_check.h" -#include "aht20_reg.h" +/******************************************** Private *********************************************/ -const static char *TAG = "AHT20"; +static const char *s_TAG = "AHT20"; + +/** + * @brief AHT20 device object + */ +typedef struct { + i2c_bus_device_handle_t i2c_dev; /*!< i2c device handle. */ +} aht20_dev_config_t; +/** + *@brief AHT20 raw result + */ typedef struct { - i2c_port_t i2c_port; - uint8_t i2c_addr; -} aht20_dev_t; - -static esp_err_t aht20_write_reg(aht20_dev_handle_t dev, uint8_t reg_addr, uint8_t *data, uint8_t len) -{ - aht20_dev_t *sens = (aht20_dev_t *) dev; - esp_err_t ret; - - i2c_cmd_handle_t cmd = i2c_cmd_link_create(); - ret = i2c_master_start(cmd); - assert(ESP_OK == ret); - ret = i2c_master_write_byte(cmd, sens->i2c_addr | I2C_MASTER_WRITE, true); - assert(ESP_OK == ret); - ret = i2c_master_write_byte(cmd, reg_addr, true); - assert(ESP_OK == ret); - if (len) { - ret = i2c_master_write(cmd, data, len, true); - assert(ESP_OK == ret); + uint32_t humidity; /*!< raw humidity reading. */ + uint32_t temperature; /*!< raw temperature reading. */ +} aht20_raw_reading_t; + +/** +* @brief a function used to handle resetting of registers of the device, if not found calibrated when initialized +* +* @param[in] aht20_handle AHT20 device handle +* +* @param[in] read_buffer storage for read values +* +* @param[in] read_size data size to read +* +*/ +static esp_err_t aht20_read_reg(aht20_dev_config_t * aht20_handle, uint8_t * read_buffer, uint8_t read_size); + +/** +* @brief a function used to handle resetting of registers of the device, if not found calibrated when initialized +* +* @param[in] aht20_handle AHT20 device handle +* +* @param[in] cmd AHT20 command to be written +* +* @param[in] write_size data size to write +* +*/ +static esp_err_t aht20_write_reg(aht20_dev_config_t * aht20_handle, uint8_t * cmd, uint8_t write_size); + +/** +* @brief a function used to handle reinitialization of registers of the device, if not found calibrated when initialized +* +* @param[in] aht20_handle AHT20 device handle +* +*/ +static esp_err_t aht20_Start_Init(aht20_dev_config_t * aht20_handle); + +/** +* @brief a function used to handle resetting of registers of the device, if not found calibrated when initialized +* +* @param[in] aht20_handle AHT20 device handle +* +* @param[in] addr AHT20 internal register, undocumented in datasheet +* +*/ +static esp_err_t aht20_JH_Reset_REG(aht20_dev_config_t * aht20_handle, uint8_t addr); + +/** +* @brief check crc validity of response received +* +* @param[in] message AHT reading +* +* +* @param[in] num Data bytes to check in message for crc +* +* @return crc calculated from the provided message +* +*/ +static uint8_t calc_CRC8(uint8_t *message, uint8_t Num); + +/** +* @brief check AHT20 measurement status +* +* @param[in] aht20_handle AHT20 device handle +* +* @param[out]busy busy in measurement if value is true +* +* @return +* - ESP_OK: successfully read AHT20 busy status +* - other error codes : failure in reading AHT20 busy status +* +*/ +static esp_err_t aht20_busy_status(aht20_dev_config_t * aht20_handle, bool *busy); + +static esp_err_t aht20_read_reg(aht20_dev_config_t * aht20_handle, uint8_t * read_buffer, uint8_t read_size) +{ + + ESP_RETURN_ON_ERROR(i2c_bus_read_bytes(aht20_handle->i2c_dev, NULL_I2C_MEM_ADDR, read_size, read_buffer), + s_TAG, "unable to read from aht20"); + + return ESP_OK; +} + +static esp_err_t aht20_write_reg(aht20_dev_config_t * aht20_handle, uint8_t * cmd, uint8_t write_size) +{ + ESP_RETURN_ON_ERROR(i2c_bus_write_bytes(aht20_handle->i2c_dev, NULL_I2C_MEM_ADDR, write_size, cmd), + s_TAG, "unable to set mode for AHT20\n"); + + return ESP_OK; +} + +static esp_err_t aht20_busy_status(aht20_dev_config_t * aht20_handle, bool *busy) +{ + ESP_RETURN_ON_FALSE((aht20_handle != NULL), ESP_ERR_INVALID_ARG, + s_TAG, "empty handle, initialize AHT20 handle"); + + ESP_RETURN_ON_FALSE((busy != NULL), ESP_ERR_INVALID_ARG, + s_TAG, "provide a variable to store status value"); + uint8_t read_status; + ESP_RETURN_ON_ERROR(aht20_read_reg(aht20_handle, &read_status, sizeof(read_status)), + s_TAG, "unable to read status"); + + if (read_status & BIT7) { + *busy = true; + } else { + *busy = false; } - ret = i2c_master_stop(cmd); - assert(ESP_OK == ret); - ret = i2c_master_cmd_begin(sens->i2c_port, cmd, 5000 / portTICK_PERIOD_MS); - i2c_cmd_link_delete(cmd); - return ret; + return ESP_OK; } -static esp_err_t aht20_read_reg(aht20_dev_handle_t dev, uint8_t *data, size_t len) +static esp_err_t aht20_JH_Reset_REG(aht20_dev_config_t * aht20_handle, uint8_t addr) { - aht20_dev_t *sens = (aht20_dev_t *) dev; - esp_err_t ret; - i2c_cmd_handle_t cmd = i2c_cmd_link_create(); - ret = i2c_master_start(cmd); - assert(ESP_OK == ret); - ret = i2c_master_write_byte(cmd, sens->i2c_addr | I2C_MASTER_READ, true); - assert(ESP_OK == ret); - ret = i2c_master_read(cmd, data, len, I2C_MASTER_LAST_NACK); - assert(ESP_OK == ret); - ret = i2c_master_stop(cmd); - assert(ESP_OK == ret); - ret = i2c_master_cmd_begin(sens->i2c_port, cmd, 1000 / portTICK_PERIOD_MS); - i2c_cmd_link_delete(cmd); - return ret; + uint8_t reset_cmd[] = {addr, 0x00, 0x00}, read_bytes[3]; + + ESP_RETURN_ON_ERROR(aht20_write_reg(aht20_handle, reset_cmd, sizeof(reset_cmd)), + s_TAG, "unable to reset, check log"); + + ESP_RETURN_ON_ERROR(aht20_read_reg(aht20_handle, read_bytes, sizeof(read_bytes)), + s_TAG, "unable to reset, check log"); + + vTaskDelay(10 / portTICK_PERIOD_MS); + reset_cmd[0] = 0xB0 | addr; + reset_cmd[1] = read_bytes[1]; + reset_cmd[2] = read_bytes[2]; + + ESP_RETURN_ON_ERROR(aht20_write_reg(aht20_handle, reset_cmd, sizeof(reset_cmd)), + s_TAG, "unable to reset, check log"); + + return ESP_OK; } -static uint8_t aht20_calc_crc(uint8_t *data, uint8_t len) +static esp_err_t aht20_Start_Init(aht20_dev_config_t * aht20_handle) +{ + ESP_RETURN_ON_ERROR(aht20_JH_Reset_REG(aht20_handle, 0x1b), "", ""); + ESP_RETURN_ON_ERROR(aht20_JH_Reset_REG(aht20_handle, 0x1c), "", ""); + ESP_RETURN_ON_ERROR(aht20_JH_Reset_REG(aht20_handle, 0x1e), "", ""); + + return ESP_OK; +} + +static uint8_t calc_CRC8(uint8_t *message, uint8_t Num) { uint8_t i; uint8_t byte; uint8_t crc = 0xFF; - - for (byte = 0; byte < len; byte++) { - crc ^= data[byte]; + for (byte = 0; byte < Num; byte++) { + crc ^= (message[byte]); for (i = 8; i > 0; --i) { - if ((crc & 0x80) != 0) { + if (crc & 0x80) { crc = (crc << 1) ^ 0x31; } else { - crc = crc << 1; + crc = (crc << 1); } } } - return crc; } -esp_err_t aht20_read_temperature_humidity(aht20_dev_handle_t handle, - uint32_t *temperature_raw, float *temperature, - uint32_t *humidity_raw, float *humidity) +/**************************************************************************************************/ + +/******************************************** Public *********************************************/ + +static esp_err_t aht20_read_raw(aht20_dev_config_t * aht20_handle, aht20_raw_reading_t *raw_read) { - uint8_t status; - uint8_t buf[7]; - uint32_t raw_data; + ESP_RETURN_ON_FALSE((aht20_handle != NULL), ESP_ERR_INVALID_ARG, s_TAG, "empty handle, provide a valid AHT20 handle"); - ESP_RETURN_ON_FALSE(handle, ESP_ERR_INVALID_ARG, TAG, "invalid device handle pointer"); + uint8_t measure_cmd[] = {AHT20_MEASURE_CYC, 0x33, 0x00}; - buf[0] = 0x33; - buf[1] = 0x00; - ESP_RETURN_ON_ERROR(aht20_write_reg(handle, AHT20_START_MEASURMENT_CMD, buf, 2), TAG, "I2C read/write error"); + ESP_RETURN_ON_ERROR(aht20_write_reg(aht20_handle, measure_cmd, sizeof(measure_cmd)), + s_TAG, "unable to set mode for AHT20\n"); - vTaskDelay(pdMS_TO_TICKS(100)); + uint8_t read_measurement[7], read_bytes = 6;; +#ifdef CONFIG_AHT20_CHECK_CRC + read_bytes = 7; +#endif - ESP_RETURN_ON_ERROR(aht20_read_reg(handle, &status, 1), TAG, "I2C read/write error"); + bool busy = true; + while (busy) { + aht20_busy_status(aht20_handle, &busy); // wait for measurement + } - if ((status & BIT(AT581X_STATUS_Calibration_Enable)) && - (status & BIT(AT581X_STATUS_CRC_FLAG)) && - ((status & BIT(AT581X_STATUS_BUSY_INDICATION)) == 0)) { - ESP_RETURN_ON_ERROR(aht20_read_reg(handle, buf, 7), TAG, "I2C read/write error"); - ESP_RETURN_ON_ERROR((aht20_calc_crc(buf, 6) != buf[6]), TAG, "crc is error"); + ESP_RETURN_ON_ERROR(aht20_read_reg(aht20_handle, read_measurement, read_bytes), + s_TAG, "unable to read raw measurement"); - raw_data = buf[1]; - raw_data = raw_data << 8; - raw_data += buf[2]; - raw_data = raw_data << 8; - raw_data += buf[3]; - raw_data = raw_data >> 4; - *humidity_raw = raw_data; - *humidity = (float)raw_data * 100 / 1048576; +#ifdef CONFIG_AHT20_CHECK_CRC + ESP_RETURN_ON_FALSE((calc_CRC8(read_measurement, 6) == read_measurement[6]), + ESP_ERR_INVALID_RESPONSE, + s_TAG, "CRC match failed, invalid response received from AHT20"); +#endif - raw_data = buf[3] & 0x0F; - raw_data = raw_data << 8; - raw_data += buf[4]; - raw_data = raw_data << 8; - raw_data += buf[5]; - *temperature_raw = raw_data; - *temperature = (float)raw_data * 200 / 1048576 - 50; - return ESP_OK; - } else { - ESP_LOGI(TAG, "data is not ready"); - return ESP_ERR_NOT_FINISHED; + raw_read->humidity = ((read_measurement[1] << 16) | (read_measurement[2] << 8) | read_measurement[3]) >> 4; + raw_read->temperature = ((read_measurement[3] << 16) | (read_measurement[4] << 8) | read_measurement[5]) & 0xfffff; + + return ESP_OK; +} + +esp_err_t aht20_read_humidity(aht20_handle_t aht20, float_t *humidity) +{ + ESP_RETURN_ON_FALSE((aht20 != NULL), ESP_ERR_INVALID_ARG, s_TAG, "empty handle, initialize AHT20 handle"); + + aht20_dev_config_t * aht20_handle = (aht20_dev_config_t *) aht20; + + aht20_raw_reading_t raw_read; + ESP_RETURN_ON_ERROR(aht20_read_raw(aht20_handle, &raw_read), "", ""); + + *humidity = raw_read.humidity * 100.0 / 1024 / 1024; //Calculated humidity value + return ESP_OK; +} + +esp_err_t aht20_read_temperature(aht20_handle_t aht20, float_t *temperature) +{ + ESP_RETURN_ON_FALSE((aht20 != NULL), ESP_ERR_INVALID_ARG, s_TAG, "empty handle, initialize AHT20 handle"); + + aht20_dev_config_t * aht20_handle = (aht20_dev_config_t *) aht20; + + aht20_raw_reading_t raw_read; + ESP_RETURN_ON_ERROR(aht20_read_raw(aht20_handle, &raw_read), "", ""); + + *temperature = (raw_read.temperature * 200.0 / 1024 / 1024) - 50; //Calculated temperature value + + return ESP_OK; +} + +esp_err_t aht20_init(aht20_handle_t aht20) +{ + ESP_RETURN_ON_FALSE((aht20 != NULL), ESP_ERR_INVALID_ARG, s_TAG, "empty handle, initialize AHT20 handle"); + + aht20_dev_config_t * aht20_handle = (aht20_dev_config_t *) aht20; + vTaskDelay(20 / portTICK_PERIOD_MS); //time for AHT20 SCL to stabilize + + /***********************************************************************************/ + /** // This is undocumented in user manual + //The first time the power is turned on, read the status word at 0x71, determine whether the status word is 0x18, + //if it is not 0x18, reset the registers + **/ + uint8_t read_status; + + ESP_RETURN_ON_ERROR(aht20_read_reg(aht20_handle, &read_status, sizeof(read_status)), + s_TAG, "unable to read status"); + + if ((read_status & 0x18) != 0x18) { + ESP_RETURN_ON_ERROR(aht20_Start_Init(aht20_handle), + s_TAG, "reset failed, retry"); + vTaskDelay(10 / portTICK_PERIOD_MS); + } + /***********************************************************************************/ + + // initialize AHT20 + uint8_t aht20_init_cmd [] = { AHT20_INIT_REG, 0x08, 0x00 }; + + ESP_RETURN_ON_ERROR(aht20_write_reg(aht20_handle, aht20_init_cmd, sizeof(aht20_init_cmd)), + s_TAG, "unable to initialize AHT20\n"); + + ESP_LOGI(s_TAG, "AHT20 initialized\n"); + + return ESP_OK; + +} + +aht20_handle_t aht20_create(i2c_bus_handle_t bus_handle, uint8_t aht20_address) +{ + ESP_LOGI(s_TAG, "adding aht20 as device to bus\n"); + i2c_bus_device_handle_t dev_handle = i2c_bus_device_create(bus_handle, aht20_address, i2c_bus_get_current_clk_speed(bus_handle)); + ESP_RETURN_ON_FALSE((dev_handle != NULL), NULL, s_TAG, "unable to create device\n"); + ESP_LOGI(s_TAG, "device added to bus\n"); + + aht20_dev_config_t * my_aht20_handle = malloc(sizeof(aht20_dev_config_t)); + + ESP_RETURN_ON_FALSE((my_aht20_handle != NULL), NULL, s_TAG, "unable to allocate memory to initialize aht20 handle"); + + my_aht20_handle->i2c_dev = dev_handle; + return (aht20_handle_t) my_aht20_handle; +} + +esp_err_t aht20_remove(aht20_handle_t *aht20ptr) +{ + if (*aht20ptr == NULL) { + return ESP_ERR_INVALID_ARG; } + aht20_dev_config_t ** aht20_handle = (aht20_dev_config_t **) aht20ptr; + i2c_bus_device_delete(&((*aht20_handle)->i2c_dev)); + free(*aht20_handle); + *aht20_handle = NULL; // now AHT20 handle is not a dangling pointer + return ESP_OK; } -esp_err_t aht20_new_sensor(const aht20_i2c_config_t *i2c_conf, aht20_dev_handle_t *handle_out) +/*************************************************************************************************/ + +/******************************************** Sensor Hub *********************************************/ +#ifdef CONFIG_SENSOR_INCLUDED_HUMITURE + +static aht20_handle_t aht20 = NULL; +static bool is_init = false; + +esp_err_t humiture_aht20_init(i2c_bus_handle_t i2c_bus, uint8_t addr) { - ESP_LOGI(TAG, "%-15s: %d.%d.%d", CHIP_NAME, AHT20_VER_MAJOR, AHT20_VER_MINOR, AHT20_VER_PATCH); - ESP_LOGI(TAG, "%-15s: %1.1f - %1.1fV", "SUPPLY_VOLTAGE", SUPPLY_VOLTAGE_MIN, SUPPLY_VOLTAGE_MAX); - ESP_LOGI(TAG, "%-15s: %.2f - %.2f℃", "TEMPERATURE", TEMPERATURE_MIN, TEMPERATURE_MAX); + if (is_init || !i2c_bus) { + return ESP_FAIL; + } - ESP_RETURN_ON_FALSE(i2c_conf, ESP_ERR_INVALID_ARG, TAG, "invalid device config pointer"); - ESP_RETURN_ON_FALSE(handle_out, ESP_ERR_INVALID_ARG, TAG, "invalid device handle pointer"); + aht20 = aht20_create(i2c_bus, addr); - aht20_dev_t *handle = calloc(1, sizeof(aht20_dev_t)); - ESP_RETURN_ON_FALSE(handle, ESP_ERR_NO_MEM, TAG, "memory allocation for device handler failed"); + if (!aht20) { + return ESP_FAIL; + } - handle->i2c_port = i2c_conf->i2c_port; - handle->i2c_addr = i2c_conf->i2c_addr; + ESP_RETURN_ON_ERROR(aht20_init(aht20), s_TAG, ""); - *handle_out = handle; + is_init = true; return ESP_OK; } -esp_err_t aht20_del_sensor(aht20_dev_handle_t handle) +esp_err_t humiture_aht20_deinit(void) { - ESP_RETURN_ON_FALSE(handle, ESP_ERR_INVALID_ARG, TAG, "invalid device handle pointer"); + if (!is_init) { + return ESP_FAIL; + } - free(handle); + ESP_RETURN_ON_ERROR(aht20_remove(&aht20), s_TAG, ""); + is_init = false; return ESP_OK; } + +esp_err_t humiture_aht20_test(void) +{ + if (!is_init) { + return ESP_FAIL; + } + + return ESP_OK; +} + +esp_err_t humiture_aht20_acquire_humidity(float *h) +{ + if (!is_init) { + return ESP_FAIL; + } + + float humidity = 0; + esp_err_t ret = aht20_read_humidity(aht20, &humidity); + + if (ret == ESP_OK) { + *h = humidity; + return ESP_OK; + } + *h = 0; + return ESP_FAIL; +} + +esp_err_t humiture_aht20_acquire_temperature(float *t) +{ + if (!is_init) { + return ESP_FAIL; + } + + float temperature = 0; + esp_err_t ret = aht20_read_temperature(aht20, &temperature); + + if (ret == ESP_OK) { + *t = temperature; + return ESP_OK; + } + + *t = 0; + return ESP_FAIL; +} + +static humiture_impl_t aht20_impl = { + .init = humiture_aht20_init, + .deinit = humiture_aht20_deinit, + .test = humiture_aht20_test, + .acquire_humidity = humiture_aht20_acquire_humidity, + .acquire_temperature = humiture_aht20_acquire_temperature, +}; + +SENSOR_HUB_DETECT_FN(HUMITURE_ID, aht20, &aht20_impl); + +#endif + +/**********************************************************************************************/ diff --git a/components/sensors/humiture/aht20/idf_component.yml b/components/sensors/humiture/aht20/idf_component.yml index 786851a67..28813f2d4 100644 --- a/components/sensors/humiture/aht20/idf_component.yml +++ b/components/sensors/humiture/aht20/idf_component.yml @@ -1,4 +1,30 @@ -version: 1.0.0 +name: aht20 + +version: "1.1.0" + +author: Rohan Jeet + +description: AHT20 Temperature and Humidity Sensor I2C Driver for ESP-IDF + +issues: https://github.com/espressif/esp-iot-solution/issues + +repository: git://github.com/espressif/esp-iot-solution.git + +url: https://github.com/espressif/esp-iot-solution/tree/master/components/sensors/humiture/aht20 + +dependencies: + + i2c_bus: + public: true + override_path: "../../../i2c_bus" + + sensor_hub: + public: true + override_path: "../../sensor_hub" + + idf: ">=4.0" + + targets: - esp32 - esp32c2 @@ -7,13 +33,3 @@ targets: - esp32h2 - esp32s2 - esp32s3 -description: I2C driver for AHT20 humidity and temperature sensor -issues: https://github.com/espressif/esp-iot-solution/issues -repository: git://github.com/espressif/esp-iot-solution.git -url: https://github.com/espressif/esp-iot-solution/tree/master/components/sensors/humiture/aht20 -dependencies: - idf: '>=4.3' - cmake_utilities: "0.*" -sbom: - supplier: 'Organization: Espressif Systems (Shanghai) CO LTD' - originator: 'Organization: Espressif Systems (Shanghai) CO LTD' diff --git a/components/sensors/humiture/aht20/include/aht20.h b/components/sensors/humiture/aht20/include/aht20.h index 59d5b5bb1..981260f6c 100644 --- a/components/sensors/humiture/aht20/include/aht20.h +++ b/components/sensors/humiture/aht20/include/aht20.h @@ -1,79 +1,125 @@ /* - * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ -#pragma once +#ifndef AHT20_H +#define AHT20_H + +#include +#include +#include +#include +#include +#include +#include + +#include "esp_bit_defs.h" +#include "esp_log.h" +#include "esp_err.h" +#include "esp_check.h" + +#include "freertos/FreeRTOS.h" + +#include "i2c_bus.h" #ifdef __cplusplus extern "C" { #endif -#include "esp_types.h" -#include "esp_err.h" +#define AHT20_ADDRESS_LOW 0X38 /*!< AHT20 I2C ADDRESS LOW */ +#define AHT20_ADDRESS_HIGH 0X39 /*!< AHT20 I2C ADDRESS HIGH */ +#define AHT20_INIT_REG 0XBE /*!< initialize the AHT20 */ +#define AHT20_MEASURE_CYC 0xAC /*!< trigger measurement in cycle mode */ -#include "driver/i2c.h" +/** + * @brief AHT20 device handle + */ +typedef void * aht20_handle_t; -/* AHT20 address: CE pin low - 0x38, CE pin high - 0x39 */ -#define AHT20_ADDRRES_0 (0x38<<1) -#define AHT20_ADDRESS_1 (0x39<<1) +/** +* @brief soft reset AHT20 +* +* @param[in] aht20_handle AHT20 device handle +* +* @return +* - ESP_OK: successful reset +* - other error codes : failed to reset +* +*/ +esp_err_t aht20_reset(aht20_handle_t aht20_handle); /** - * @brief Type of AHT20 device handle - * - */ -typedef void *aht20_dev_handle_t; +* @brief get AHT20 humidity readings +* +* @param[in] aht20_handle AHT20 device handle +* +* @param[out] humidity AHT20 humidity reading +* +* @return +* - ESP_OK: successful read +* - other error codes : failed to read +* +*/ + +esp_err_t aht20_read_humidity(aht20_handle_t aht20_handle, float_t *humidity); /** - * @brief AHT20 I2C config struct - * - */ -typedef struct { - i2c_port_t i2c_port; /*!< I2C port used to connected AHT20 device */ - uint8_t i2c_addr; /*!< I2C address of AHT20 device, can be 0x38 or 0x39 according to A0 pin */ -} aht20_i2c_config_t; +* @brief get AHT20 temperature readings +* +* @param[in] aht20_handle AHT20 device handle +* +* @param[out] temperature AHT20 temperature reading +* +* @return +* - ESP_OK: successful read +* - other error codes : failed to read +* +*/ +esp_err_t aht20_read_temperature(aht20_handle_t aht20_handle, float_t *temperature); /** - * @brief Create new AHT20 device handle. - * - * @param[in] i2c_conf Config for I2C used by AHT20 - * @param[out] handle_out New AHT20 device handle - * @return - * - ESP_OK Device handle creation success. - * - ESP_ERR_INVALID_ARG Invalid device handle or argument. - * - ESP_ERR_NO_MEM Memory allocation failed. - * - */ -esp_err_t aht20_new_sensor(const aht20_i2c_config_t *i2c_conf, aht20_dev_handle_t *handle_out); +* @brief Initialize the AHT20 +* +* @param[in] aht20_handle AHT20 device handle +* +* @return +* - ESP_OK: successful initiailiztion +* - other error codes : failed to initialize +* +*/ +esp_err_t aht20_init(aht20_handle_t aht20_handle); /** - * @brief Delete AHT20 device handle. - * - * @param[in] handle AHT20 device handle - * @return - * - ESP_OK Device handle deletion success. - * - ESP_ERR_INVALID_ARG Invalid device handle or argument. - * - */ -esp_err_t aht20_del_sensor(aht20_dev_handle_t handle); +* @brief create an AHT20 device handle +* +* @param[in] bus_handle I2C master bus handle, with which this device will be associated +* +* @param[in] scl_speed_hz I2C communication speed used by this device +* +* @param[in] aht20_address I2C address of this device +* +* @return +* - NULL: Failed to create AHT20 device handle +* - other error codes : from the underlying i2c driver, check log +* +*/ +aht20_handle_t aht20_create(i2c_bus_handle_t bus_handle, uint8_t aht20_address); /** - * @brief read the temperature and humidity data - * - * @param[in] *handle points to an aht20 handle structure - * @param[out] *temperature_raw points to a raw temperature buffer - * @param[out] *temperature points to a converted temperature buffer - * @param[out] *humidity_raw points to a raw humidity buffer - * @param[out] *humidity points to a converted humidity buffer - * - * @return - * - ESP_OK Success - * - ESP_FAIL Fail - */ -esp_err_t aht20_read_temperature_humidity(aht20_dev_handle_t handle, - uint32_t *temperature_raw, float *temperature, - uint32_t *humidity_raw, float *humidity); +* @brief free the resources associated with AHT20 device +* +* @param[in] aht20_handler address of AHT20 device handle +* +* @return +* - ESP_ERR_INVALID_ARG: Invalid argument +* - ESP_OK : successful +*/ +esp_err_t aht20_remove(aht20_handle_t *aht20_handler); + #ifdef __cplusplus } -#endif +#endif // __cplusplus + +#endif // __AHT20_H diff --git a/components/sensors/humiture/aht20/priv_include/aht20_reg.h b/components/sensors/humiture/aht20/priv_include/aht20_reg.h deleted file mode 100644 index 478c5148a..000000000 --- a/components/sensors/humiture/aht20/priv_include/aht20_reg.h +++ /dev/null @@ -1,24 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD - * - * SPDX-License-Identifier: Apache-2.0 - */ - -#pragma once - -/** - * @brief chip information definition - */ -#define CHIP_NAME "ASAIR AHT20" /**< chip name */ -#define SUPPLY_VOLTAGE_MIN (2.2f) /**< chip min supply voltage */ -#define SUPPLY_VOLTAGE_MAX (5.5f) /**< chip max supply voltage */ -#define TEMPERATURE_MIN (-40.0f) /**< chip min operating temperature */ -#define TEMPERATURE_MAX (125.0f) /**< chip max operating temperature */ - -#define AHT20_START_MEASURMENT_CMD 0xAC /* start measurement command */ - -#define AT581X_STATUS_CMP_INT (2) /* 1 --Out threshold range; 0 --In threshold range */ -#define AT581X_STATUS_Calibration_Enable (3) /* 1 --Calibration enable; 0 --Calibration disable */ -#define AT581X_STATUS_CRC_FLAG (4) /* 1 --CRC ok; 0 --CRC failed */ -#define AT581X_STATUS_MODE_STATUS (5) /* 00 -NOR mode; 01 -CYC mode; 1x --CMD mode */ -#define AT581X_STATUS_BUSY_INDICATION (7) /* 1 --Equipment is busy; 0 --Equipment is idle */ diff --git a/components/sensors/humiture/aht20/test_apps/CMakeLists.txt b/components/sensors/humiture/aht20/test_apps/aht20_driver_test/CMakeLists.txt similarity index 87% rename from components/sensors/humiture/aht20/test_apps/CMakeLists.txt rename to components/sensors/humiture/aht20/test_apps/aht20_driver_test/CMakeLists.txt index 98e2ca8ee..fe5722940 100644 --- a/components/sensors/humiture/aht20/test_apps/CMakeLists.txt +++ b/components/sensors/humiture/aht20/test_apps/aht20_driver_test/CMakeLists.txt @@ -2,7 +2,9 @@ # in this exact order for cmake to work correctly cmake_minimum_required(VERSION 3.5) +set(COMPONENTS main) set(EXTRA_COMPONENT_DIRS "$ENV{IDF_PATH}/tools/unit-test-app/components" - "../../aht20") + "../../../aht20") + include($ENV{IDF_PATH}/tools/cmake/project.cmake) project(aht20_test) diff --git a/components/sensors/humiture/aht20/test_apps/aht20_driver_test/main/CMakeLists.txt b/components/sensors/humiture/aht20/test_apps/aht20_driver_test/main/CMakeLists.txt new file mode 100644 index 000000000..461a305c2 --- /dev/null +++ b/components/sensors/humiture/aht20/test_apps/aht20_driver_test/main/CMakeLists.txt @@ -0,0 +1,3 @@ +idf_component_register(SRCS "aht20_test_app.c" + INCLUDE_DIRS "." + PRIV_REQUIRES unity test_utils aht20) diff --git a/components/sensors/humiture/aht20/test_apps/main/Kconfig.projbuild b/components/sensors/humiture/aht20/test_apps/aht20_driver_test/main/Kconfig.projbuild similarity index 100% rename from components/sensors/humiture/aht20/test_apps/main/Kconfig.projbuild rename to components/sensors/humiture/aht20/test_apps/aht20_driver_test/main/Kconfig.projbuild diff --git a/components/sensors/humiture/aht20/test_apps/aht20_driver_test/main/aht20_test_app.c b/components/sensors/humiture/aht20/test_apps/aht20_driver_test/main/aht20_test_app.c new file mode 100644 index 000000000..78bfdfc8b --- /dev/null +++ b/components/sensors/humiture/aht20/test_apps/aht20_driver_test/main/aht20_test_app.c @@ -0,0 +1,134 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include "unity.h" +#include "esp_system.h" +#include "aht20.h" + +#define TEST_MEMORY_LEAK_THRESHOLD (-400) + +// I2C config +#define I2C_MASTER_SCL_IO CONFIG_I2C_MASTER_SCL +#define I2C_MASTER_SDA_IO CONFIG_I2C_MASTER_SDA +#define I2C_MASTER_NUM I2C_NUM_0 +#define I2C_MASTER_FREQ_HZ 100000 + +// Global handles +i2c_bus_handle_t my_i2c_bus_handle = NULL; +aht20_handle_t aht20_handle = NULL; + +/*******************************Memory Leak Checks****************************/ + +static size_t before_free_8bit; +static size_t before_free_32bit; +static size_t after_free_8bit; +static size_t after_free_32bit; + +static void check_leak(size_t before_free, size_t after_free, const char *type) +{ + ssize_t delta = after_free - before_free; + printf("MALLOC_CAP_%s: Before %u bytes free, After %u bytes free (delta %d)\n", type, before_free, after_free, delta); + TEST_ASSERT_MESSAGE(delta >= TEST_MEMORY_LEAK_THRESHOLD, "memory leak"); +} + +void setUp(void) +{ + before_free_8bit = heap_caps_get_free_size(MALLOC_CAP_8BIT); + before_free_32bit = heap_caps_get_free_size(MALLOC_CAP_32BIT); +} + +void tearDown(void) +{ + after_free_8bit = heap_caps_get_free_size(MALLOC_CAP_8BIT); + after_free_32bit = heap_caps_get_free_size(MALLOC_CAP_32BIT); + check_leak(before_free_8bit, after_free_8bit, "8BIT"); + check_leak(before_free_32bit, after_free_32bit, "32BIT"); +} + +/************************** Memory Leak Checks Completed********************/ + +/*******************************I2C Master Bus Initialization****************************/ +void i2c_master_init(void) +{ + const i2c_config_t conf = { + .mode = I2C_MODE_MASTER, + .sda_io_num = I2C_MASTER_SDA_IO, + .sda_pullup_en = GPIO_PULLUP_ENABLE, + .scl_io_num = I2C_MASTER_SCL_IO, + .scl_pullup_en = GPIO_PULLUP_ENABLE, + .master.clk_speed = I2C_MASTER_FREQ_HZ, + }; + + printf("Requesting I2C bus handle\n"); + my_i2c_bus_handle = i2c_bus_create(I2C_MASTER_NUM, &conf); + printf("I2C bus handle acquired\n"); +} +/*******************************I2C Master Bus Initialization Over****************************/ + +/*******************************AHT20 Device Initialization****************************/ +esp_err_t aht20_init_test() +{ + i2c_master_init(); + aht20_handle = aht20_create(my_i2c_bus_handle, AHT20_ADDRESS_LOW); + + printf("Initializing AHT20 sensor\n"); + while (aht20_init(aht20_handle) != ESP_OK) { + vTaskDelay(pdMS_TO_TICKS(100)); + } + printf("AHT20 initialized\n"); + + return ESP_OK; +} +/*******************************AHT20 Device Initialization Over****************************/ + +/*******************************AHT20 Device Deinitializtion****************************/ +void aht20_deinit_test(void) +{ + aht20_remove(&aht20_handle); + i2c_bus_delete(&my_i2c_bus_handle); +} +/*******************************AHT20 Device Deinitializtion Over****************************/ + +/*******************************AHT20 Read sensor****************************/ +void aht20_check_humidity(void) +{ + float_t humidity; + + TEST_ASSERT(ESP_OK == aht20_read_humidity(aht20_handle, &humidity)); + + printf("Humidity = %.2f%%\n", humidity); + +} + +void aht20_check_temprature(void) +{ + float_t temperature; + + TEST_ASSERT(ESP_OK == aht20_read_temperature(aht20_handle, &temperature)); + + printf("Temperature = %.2f°C\n", temperature); + +} +/*******************************AHT20 AHT20 Read sensor Over*****************************/ + +/*************************************Test Case**************************/ + +TEST_CASE("AHT20 Sensor", "[aht20][sensor]") +{ + aht20_init_test(); + aht20_check_humidity(); + vTaskDelay(pdMS_TO_TICKS(2000)); + aht20_check_temprature(); + aht20_deinit_test(); +} +/*************************************Test Case Over**************************/ + +void app_main(void) +{ + printf("\n=== AHT20 Sensor Test Menu ===\n"); + unity_run_menu(); // Run test selection menu in flash monitor +} diff --git a/components/sensors/humiture/aht20/test_apps/pytest_aht20.py b/components/sensors/humiture/aht20/test_apps/aht20_driver_test/pytest_aht20.py similarity index 80% rename from components/sensors/humiture/aht20/test_apps/pytest_aht20.py rename to components/sensors/humiture/aht20/test_apps/aht20_driver_test/pytest_aht20.py index 069fb610f..8db09196d 100644 --- a/components/sensors/humiture/aht20/test_apps/pytest_aht20.py +++ b/components/sensors/humiture/aht20/test_apps/aht20_driver_test/pytest_aht20.py @@ -6,10 +6,10 @@ - Build - . ${IDF_PATH}/export.sh - pip install idf_build_apps - - python tools/build_apps.py components/sensor/radar/at581x/test_apps -t esp32s3 + - python tools/build_apps.py components/sensors/humiture/aht20/test_apps -t esp32s3 - Test - pip install -r tools/requirements/requirement.pytest.txt - - pytest components/sensor/radar/at581x/test_apps --target esp32s3 + - pytest components/sensors/humiture/aht20/test_apps --target esp32s3 ''' import pytest diff --git a/components/sensors/humiture/aht20/test_apps/sdkconfig.defaults b/components/sensors/humiture/aht20/test_apps/aht20_driver_test/sdkconfig.defaults similarity index 100% rename from components/sensors/humiture/aht20/test_apps/sdkconfig.defaults rename to components/sensors/humiture/aht20/test_apps/aht20_driver_test/sdkconfig.defaults diff --git a/components/sensors/humiture/aht20/test_apps/main/CMakeLists.txt b/components/sensors/humiture/aht20/test_apps/main/CMakeLists.txt deleted file mode 100644 index 66cc48557..000000000 --- a/components/sensors/humiture/aht20/test_apps/main/CMakeLists.txt +++ /dev/null @@ -1,3 +0,0 @@ -idf_component_register(SRC_DIRS "." - PRIV_INCLUDE_DIRS "." - PRIV_REQUIRES unity test_utils aht20) diff --git a/components/sensors/humiture/aht20/test_apps/main/aht20_test.c b/components/sensors/humiture/aht20/test_apps/main/aht20_test.c deleted file mode 100644 index 68f76a3c8..000000000 --- a/components/sensors/humiture/aht20/test_apps/main/aht20_test.c +++ /dev/null @@ -1,104 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD - * - * SPDX-License-Identifier: Apache-2.0 - */ - -#include -#include "unity.h" -#include "driver/i2c.h" -#include "aht20.h" -#include "esp_system.h" -#include "esp_log.h" - -static const char *TAG = "aht20 test"; - -#define TEST_MEMORY_LEAK_THRESHOLD (-400) - -#define I2C_MASTER_SCL_IO CONFIG_I2C_MASTER_SCL /*!< gpio number for I2C master clock */ -#define I2C_MASTER_SDA_IO CONFIG_I2C_MASTER_SDA /*!< gpio number for I2C master data */ -#define I2C_MASTER_NUM I2C_NUM_0 /*!< I2C port number for master dev */ -#define I2C_MASTER_FREQ_HZ 100000 /*!< I2C master clock frequency */ - -static aht20_dev_handle_t aht20 = NULL; - -/** - * @brief i2c master initialization - */ -static void i2c_bus_init(void) -{ - const i2c_config_t i2c_conf = { - .mode = I2C_MODE_MASTER, - .sda_io_num = (gpio_num_t)I2C_MASTER_SDA_IO, - .sda_pullup_en = GPIO_PULLUP_DISABLE, - .scl_io_num = (gpio_num_t)I2C_MASTER_SCL_IO, - .scl_pullup_en = GPIO_PULLUP_DISABLE, - .master.clk_speed = I2C_MASTER_FREQ_HZ - }; - esp_err_t ret = i2c_param_config(I2C_MASTER_NUM, &i2c_conf); - TEST_ASSERT_EQUAL_MESSAGE(ESP_OK, ret, "I2C config returned error"); - - ret = i2c_driver_install(I2C_MASTER_NUM, i2c_conf.mode, 0, 0, 0); - TEST_ASSERT_EQUAL_MESSAGE(ESP_OK, ret, "I2C install returned error"); -} - -static void i2c_sensor_ath20_init(void) -{ - aht20_i2c_config_t i2c_conf = { - .i2c_port = I2C_MASTER_NUM, - .i2c_addr = AHT20_ADDRRES_0, - }; - - i2c_bus_init(); - aht20_new_sensor(&i2c_conf, &aht20); - TEST_ASSERT_NOT_NULL_MESSAGE(aht20, "AHT20 create returned NULL"); -} - -TEST_CASE("sensor aht20 test", "[aht20][iot][sensor]") -{ - esp_err_t ret = ESP_OK; - uint32_t temperature_raw; - uint32_t humidity_raw; - float temperature; - float humidity; - - i2c_sensor_ath20_init(); - - TEST_ASSERT(ESP_OK == aht20_read_temperature_humidity(aht20, &temperature_raw, &temperature, &humidity_raw, &humidity)); - ESP_LOGI(TAG, "%-20s: %2.2f %%", "humidity is", humidity); - ESP_LOGI(TAG, "%-20s: %2.2f degC", "temperature is", temperature); - - aht20_del_sensor(aht20); - ret = i2c_driver_delete(I2C_MASTER_NUM); - TEST_ASSERT_EQUAL(ESP_OK, ret); -} - -static size_t before_free_8bit; -static size_t before_free_32bit; - -static void check_leak(size_t before_free, size_t after_free, const char *type) -{ - ssize_t delta = after_free - before_free; - printf("MALLOC_CAP_%s: Before %u bytes free, After %u bytes free (delta %d)\n", type, before_free, after_free, delta); - TEST_ASSERT_MESSAGE(delta >= TEST_MEMORY_LEAK_THRESHOLD, "memory leak"); -} - -void setUp(void) -{ - before_free_8bit = heap_caps_get_free_size(MALLOC_CAP_8BIT); - before_free_32bit = heap_caps_get_free_size(MALLOC_CAP_32BIT); -} - -void tearDown(void) -{ - size_t after_free_8bit = heap_caps_get_free_size(MALLOC_CAP_8BIT); - size_t after_free_32bit = heap_caps_get_free_size(MALLOC_CAP_32BIT); - check_leak(before_free_8bit, after_free_8bit, "8BIT"); - check_leak(before_free_32bit, after_free_32bit, "32BIT"); -} - -void app_main(void) -{ - printf("AHT20 TEST \n"); - unity_run_menu(); -}