feat!: main structure of handle changed to double pointer

This commit is contained in:
aZholtikov committed 2026-07-07 13:48:37 +03:00
1 parent e8c79cf078
commit e534465f8c
8 files changed
+207 -274

No files matched your search

Binary file not shown.
+3 -106
View File
@@ -14,7 +14,7 @@
## Features
1. Support of AHT10/AHT15/AHT20/AHT21/AHT25/AHT30.
1. Support of AHT10/AHT15/AHT20/AHT21/AHT25/AHT30/AHT40.
2. Support some sensors on one device with [zh_pca9548a](https://github.com/aZholtikov/zh_pca9548a).
## Attention
@@ -41,109 +41,6 @@ In the application, add the component:
#include "zh_aht.h"
```
## Example
## Examples
Reading the sensor:
```c
#include "zh_aht.h"
#define I2C_PORT (I2C_NUM_MAX - 1)
zh_aht_handle_t aht_handle = {0};
void app_main(void)
{
esp_log_level_set("zh_aht", ESP_LOG_ERROR);
i2c_master_bus_config_t i2c_bus_config = {
.clk_source = I2C_CLK_SRC_DEFAULT,
.i2c_port = I2C_PORT,
.scl_io_num = GPIO_NUM_22,
.sda_io_num = GPIO_NUM_21,
.glitch_ignore_cnt = 7,
.flags.enable_internal_pullup = true,
};
i2c_master_bus_handle_t i2c_bus_handle = NULL;
i2c_new_master_bus(&i2c_bus_config, &i2c_bus_handle);
zh_aht_init_config_t config = ZH_AHT_INIT_CONFIG_DEFAULT();
config.i2c_handle = i2c_bus_handle;
zh_aht_init(&config, &aht_handle);
float humidity = 0.0;
float temperature = 0.0;
for (;;)
{
zh_aht_read(&aht_handle, &humidity, &temperature);
printf("Humidity %0.2f\n", humidity);
printf("Temperature %0.2f\n", temperature);
const zh_aht_stats_t *stats = zh_aht_get_stats();
printf("Number of i2c driver error: %ld.\n", stats->i2c_driver_error);
vTaskDelay(5000 / portTICK_PERIOD_MS);
}
}
```
3 sensor on one device:
```c
#include "zh_pca9548a.h"
#include "zh_aht.h"
#define I2C_PORT (I2C_NUM_MAX - 1)
zh_pca9548a_handle_t pca9548a_handle = {0};
zh_aht_handle_t aht_handle_chan_0 = {0};
zh_aht_handle_t aht_handle_chan_1 = {0};
zh_aht_handle_t aht_handle_chan_2 = {0};
void app_main(void)
{
esp_log_level_set("zh_pca9548a", ESP_LOG_ERROR);
esp_log_level_set("zh_aht", ESP_LOG_ERROR);
i2c_master_bus_config_t i2c_bus_config = {
.clk_source = I2C_CLK_SRC_DEFAULT,
.i2c_port = I2C_PORT,
.scl_io_num = GPIO_NUM_22,
.sda_io_num = GPIO_NUM_21,
.glitch_ignore_cnt = 7,
.flags.enable_internal_pullup = true,
};
i2c_master_bus_handle_t i2c_bus_handle = NULL;
i2c_new_master_bus(&i2c_bus_config, &i2c_bus_handle);
zh_pca9548a_init_config_t config = ZH_PCA9548A_INIT_CONFIG_DEFAULT();
config.i2c_handle = i2c_bus_handle;
config.i2c_address = 0x70;
zh_pca9548a_init(&config, &pca9548a_handle);
zh_aht_init_config_t aht_config = ZH_AHT_INIT_CONFIG_DEFAULT();
aht_config.i2c_handle = i2c_bus_handle;
zh_pca9548a_set(&pca9548a_handle, ZH_PCA9548A_CHAN_NUM_0);
zh_aht_init(&aht_config, &aht_handle_chan_0);
zh_pca9548a_set(&pca9548a_handle, ZH_PCA9548A_CHAN_NUM_1);
zh_aht_init(&aht_config, &aht_handle_chan_1);
zh_pca9548a_set(&pca9548a_handle, ZH_PCA9548A_CHAN_NUM_2);
zh_aht_init(&aht_config, &aht_handle_chan_2);
float humidity = 0.0;
float temperature = 0.0;
for (;;)
{
zh_pca9548a_set(&pca9548a_handle, ZH_PCA9548A_CHAN_NUM_0);
zh_aht_read(&aht_handle_chan_0, &humidity, &temperature);
printf("Sensor 1. Humidity %0.2f\n", humidity);
printf("Sensor 1. Temperature %0.2f\n", temperature);
humidity = 0.0;
temperature = 0.0;
zh_pca9548a_set(&pca9548a_handle, ZH_PCA9548A_CHAN_NUM_1);
zh_aht_read(&aht_handle_chan_1, &humidity, &temperature);
printf("Sensor 2. Humidity %0.2f\n", humidity);
printf("Sensor 2. Temperature %0.2f\n", temperature);
humidity = 0.0;
temperature = 0.0;
zh_pca9548a_set(&pca9548a_handle, ZH_PCA9548A_CHAN_NUM_2);
zh_aht_read(&aht_handle_chan_2, &humidity, &temperature);
printf("Sensor 3. Humidity %0.2f\n", humidity);
printf("Sensor 3. Temperature %0.2f\n", temperature);
humidity = 0.0;
temperature = 0.0;
vTaskDelay(5000 / portTICK_PERIOD_MS);
}
}
```
See Wiki [EN](WIKI_EN.md#usage-examples) | [RU](WIKI_RU.md#примеры-использования)
+38 -47
View File
@@ -16,7 +16,7 @@
## Overview
`zh_aht` is a lightweight ESP-IDF component for AHT10/AHT15/AHT20/AHT21/AHT25/AHT30 humidity and temperature sensors. It provides a simple API to read humidity values from 0 to 100% and temperature values from -40 to +85°C. The component supports multiple sensors on a single I2C bus using the [zh_pca9548a](https://github.com/aZholtikov/zh_pca9548a) I2C multiplexer.
`zh_aht` is a lightweight ESP-IDF component for AHT10/AHT15/AHT20/AHT21/AHT25/AHT30/AHT40 humidity and temperature sensors. It provides a simple API to read humidity values from 0 to 100% and temperature values from -40 to +85°C. The component supports multiple sensors on a single I2C bus using the [zh_pca9548a](https://github.com/aZholtikov/zh_pca9548a) I2C multiplexer.
The component is designed specifically for ESP32 microcontrollers and uses ESP-IDF v5.0+ I2C driver API.
@@ -26,8 +26,8 @@ The component is designed specifically for ESP32 microcontrollers and uses ESP-I
1. **Humidity and Temperature Measurement**: Reads humidity from 0 to 100% and temperature from -40 to +85°C
2. **I2C Interface**: Uses standard I2C protocol (400 kHz max frequency)
3. **Configurable I2C Address**: Supports both 0x38 and 0x39 addresses
4. **Multiple Model Support**: AHT10, AHT15, AHT20, AHT21, AHT25, AHT30
3. **Configurable I2C Address**: Supports 0x38, 0x39 and 0x44 addresses
4. **Multiple Model Support**: AHT10, AHT15, AHT20, AHT21, AHT25, AHT30, AHT40
5. **Error Statistics**: Built-in error counter for I2C driver errors
6. **Low Power**: Works with ESP-IDF power management
7. **Thread-Safe**: Uses ESP-IDF I2C driver (thread-safe)
@@ -75,13 +75,15 @@ To use multiple AHT sensors on the same I2C bus, also install the [zh_pca9548a](
## API Reference
All functions in this library use double pointer (`zh_aht_handle_t **`) for the sensor handler to allow for proper memory management and thread-safe operations.
### zh_aht_init_config_t Structure
```c
typedef struct
{
i2c_master_bus_handle_t i2c_handle; // Unique I2C bus handle
uint8_t i2c_address; // Sensor I2C address (0x38 or 0x39)
uint8_t i2c_address; // Sensor I2C address (0x38, 0x39, or 0x44)
uint32_t i2c_frequency; // Sensor I2C frequency (max 400000 Hz)
} zh_aht_init_config_t;
```
@@ -95,13 +97,13 @@ Use `ZH_AHT_INIT_CONFIG_DEFAULT()` macro to initialize with default values:
### zh_aht_handle_t Structure
```c
typedef struct
{
bool is_initialized; // Sensor initialization flag
i2c_master_dev_handle_t dev_handle; // Unique I2C device handle
} zh_aht_handle_t;
```
The structure is declared as `typedef struct _zh_aht_handle_t zh_aht_handle_t;` and encapsulates internal implementation details.
**Fields (internal):**
| Field | Type | Description |
|-------|------|-------------|
| `i2c_master_dev_handle_t` | `dev_handle` | Pointer to I2C device handle. |
---
@@ -123,18 +125,21 @@ Initializes the AHT sensor.
**Parameters:**
- `config` - Pointer to AHT initialization configuration structure
- `handle` - Pointer to unique AHT handle
- `handle` - Double pointer to unique AHT handle (`zh_aht_handle_t **`). If the pointer is NULL, memory will be allocated.
**Returns:**
- `ESP_OK` - Success
- `ESP_ERR_INVALID_ARG` - Invalid argument (NULL config or handle)
- `ESP_FAIL` - Initialization failed (configuration check or I2C device addition)
- `ESP_ERR_INVALID_STATE` - Sensor is already initialized
- `ESP_ERR_NO_MEM` - Memory allocation failed
- `ESP_FAIL` - Initialization I2C device failed
- `ESP_ERR_NOT_FOUND` - Sensor did not respond
**Example:**
```c
zh_aht_handle_t aht_handle = {0};
zh_aht_handle_t *aht_handle = NULL;
zh_aht_init_config_t config = ZH_AHT_INIT_CONFIG_DEFAULT();
config.i2c_handle = i2c_bus_handle;
zh_aht_init(&config, &aht_handle);
@@ -144,17 +149,16 @@ zh_aht_init(&config, &aht_handle);
### zh_aht_deinit()
Deinitializes the AHT sensor and removes it from the I2C bus.
Deinitializes the AHT sensor and removes it from the I2C bus. Memory for handle is automatically freed and the pointer is set to NULL.
**Parameters:**
- `handle` - Pointer to unique AHT handle
- `handle` - Double pointer to unique AHT handle (`zh_aht_handle_t **`). Must not be NULL.
**Returns:**
- `ESP_OK` - Success
- `ESP_ERR_INVALID_ARG` - Invalid argument (NULL handle)
- `ESP_ERR_INVALID_STATE` - Sensor not initialized
- `ESP_ERR_INVALID_ARG` - Invalid argument (NULL handle pointer)
- `ESP_FAIL` - I2C device removal failed
---
@@ -165,18 +169,17 @@ Reads humidity and temperature values from the sensor.
**Parameters:**
- `handle` - Pointer to unique AHT handle
- `handle` - Double pointer to unique AHT handle (`zh_aht_handle_t **`). Must not be NULL.
- `humidity` - Pointer to store humidity value (in %)
- `temperature` - Pointer to store temperature value (in °C)
**Returns:**
- `ESP_OK` - Success
- `ESP_ERR_INVALID_ARG` - Invalid argument (NULL handle, humidity or temperature)
- `ESP_ERR_NOT_FOUND` - Sensor not initialized
- `ESP_ERR_INVALID_ARG` - Invalid argument (NULL handle pointer or humidity pointer or temperature pointer)
- `ESP_FAIL` - I2C communication error
- `ESP_ERR_TIMEOUT` - Timeout waiting for data ready
- `ESP_ERR_INVALID_CRC` - Invalid CRC checksum
- `ESP_FAIL` - I2C communication error
**Note:** The function performs measurement and waits ~80ms for data ready.
@@ -188,13 +191,12 @@ Resets the AHT sensor.
**Parameters:**
- `handle` - Pointer to unique AHT handle
- `handle` - Double pointer to unique AHT handle (`zh_aht_handle_t **`). Must not be NULL.
**Returns:**
- `ESP_OK` - Success
- `ESP_ERR_INVALID_ARG` - Invalid argument (NULL handle)
- `ESP_ERR_NOT_FOUND` - Sensor not initialized
- `ESP_ERR_INVALID_ARG` - Invalid argument (NULL handle pointer)
- `ESP_FAIL` - I2C communication error
---
@@ -207,25 +209,12 @@ Gets error statistics since last reset.
- Pointer to the statistics structure
**Example:**
```c
const zh_aht_stats_t *stats = zh_aht_get_stats();
printf("I2C errors: %ld\n", stats->i2c_driver_error);
```
---
### zh_aht_reset_stats()
Resets error statistics counter.
**Example:**
```c
zh_aht_reset_stats();
```
---
## Usage Examples
@@ -237,7 +226,7 @@ zh_aht_reset_stats();
#define I2C_PORT (I2C_NUM_MAX - 1)
zh_aht_handle_t aht_handle = {0};
zh_aht_handle_t *aht_handle = NULL;
void app_main(void)
{
@@ -280,9 +269,9 @@ void app_main(void)
#define I2C_PORT (I2C_NUM_MAX - 1)
zh_pca9548a_handle_t pca9548a_handle = {0};
zh_aht_handle_t aht_handle_chan_0 = {0};
zh_aht_handle_t aht_handle_chan_1 = {0};
zh_aht_handle_t aht_handle_chan_2 = {0};
zh_aht_handle_t *aht_handle_chan_0 = NULL;
zh_aht_handle_t *aht_handle_chan_1 = NULL;
zh_aht_handle_t *aht_handle_chan_2 = NULL;
void app_main(void)
{
@@ -346,10 +335,10 @@ void app_main(void)
| **Temperature Range** | -40 - +85°C |
| **Humidity Resolution** | 0.01% |
| **Temperature Resolution** | 0.01°C |
| **I2C Address** | 0x38, 0x39 |
| **I2C Address** | 0x38, 0x39, 0x44 |
| **I2C Frequency** | Up to 400 kHz |
| **Measurement Time** | ~80 ms |
| **Supported Models** | AHT10, AHT15, AHT20, AHT21, AHT25, AHT30 |
| **Supported Models** | AHT10, AHT15, AHT20, AHT21, AHT25, AHT30, AHT40 |
| **ESP-IDF Version** | >= 5.0 |
| **Platform** | ESP32 series |
| **Language** | C (C99) |
@@ -362,8 +351,9 @@ void app_main(void)
|------------|-------------|
| `ESP_OK` | Operation successful |
| `ESP_ERR_INVALID_ARG` | Invalid argument (NULL pointer or invalid configuration) |
| `ESP_ERR_INVALID_STATE` | Sensor not initialized |
| `ESP_ERR_NOT_FOUND` | Sensor not initialized or not responding |
| `ESP_ERR_INVALID_STATE` | Sensor is already initialized |
| `ESP_ERR_NO_MEM` | Memory allocation failed |
| `ESP_ERR_NOT_FOUND` | Sensor did not respond |
| `ESP_ERR_TIMEOUT` | Timeout waiting for data ready |
| `ESP_ERR_INVALID_CRC` | Invalid CRC checksum |
| `ESP_FAIL` | General failure (I2C communication error) |
@@ -412,7 +402,8 @@ limitations under the License.
- **Measurement Time**: The sensor requires ~80ms for each measurement
- **I2C_ISR_IRAM_SAFE**: For correct operation, enable `I2C_ISR_IRAM_SAFE` and `I2C_MASTER_ISR_HANDLER_IN_IRAM` in menuconfig
- **Thread Safety**: The component uses ESP-IDF I2C driver which is thread-safe
- **Memory Management**: All API functions now use double pointers for handle, which allows automatic memory management (freeing on deinitialization, setting to NULL)
---
*Generated for zh_aht v3.4.0*
*Generated for zh_aht v4.0.0*
+35 -44
View File
@@ -16,7 +16,7 @@
## Обзор
`zh_aht` - это легковесный компонент ESP-IDF для датчиков влажности и температуры AHT10/AHT15/AHT20/AHT21/AHT25/AHT30. Он предоставляет простой API для считывания значений влажности от 0 до 100% и температуры от -40 до +85°C. Компонент поддерживает несколько датчиков на одной шине I2C с использованием I2C мультиплексатора [zh_pca9548a](https://github.com/aZholtikov/zh_pca9548a).
`zh_aht` - это легковесный компонент ESP-IDF для датчиков влажности и температуры AHT10/AHT15/AHT20/AHT21/AHT25/AHT30/AHT40. Он предоставляет простой API для считывания значений влажности от 0 до 100% и температуры от -40 до +85°C. Компонент поддерживает несколько датчиков на одной шине I2C с использованием I2C мультиплексатора [zh_pca9548a](https://github.com/aZholtikov/zh_pca9548a).
Компонент разработан специально для микроконтроллеров ESP32 и использует API драйвера I2C ESP-IDF v5.0+.
@@ -26,8 +26,8 @@
1. **Измерение влажности и температуры**: Считывание значений влажности от 0 до 100% и температуры от -40 до +85°C
2. **Интерфейс I2C**: Использует стандартный протокол I2C (макс. частота 400 кГц)
3. **Настраиваемый адрес I2C**: Поддерживает адреса 0x38 и 0x39
4. **Поддержка нескольких моделей**: AHT10, AHT15, AHT20, AHT21, AHT25, AHT30
3. **Настраиваемый адрес I2C**: Поддерживает адреса 0x38, 0x39 и 0x44
4. **Поддержка нескольких моделей**: AHT10, AHT15, AHT20, AHT21, AHT25, AHT30, AHT40
5. **Статистика ошибок**: Встроенный счетчик ошибок драйвера I2C
6. **Низкое энергопотребление**: Работает с системой управления питанием ESP-IDF
7. **Потокобезопасность**: Использует драйвер I2C ESP-IDF (потокобезопасен)
@@ -75,13 +75,15 @@ I2C_MASTER_ISR_HANDLER_IN_IRAM
## Справочник API
Все функции в этой библиотеке используют двойной указатель (`zh_aht_handle_t **`) для дескриптора датчика, чтобы обеспечить правильное управление памятью и потокобезопасность.
### Структура zh_aht_init_config_t
```c
typedef struct
{
i2c_master_bus_handle_t i2c_handle; // Уникальный дескриптор шины I2C
uint8_t i2c_address; // Адрес устройства датчика I2C (0x38 или 0x39)
uint8_t i2c_address; // Адрес устройства датчика I2C (0x38, 0x39 или 0x44)
uint32_t i2c_frequency; // Частота I2C датчика (макс. 400000 Гц)
} zh_aht_init_config_t;
```
@@ -95,13 +97,13 @@ typedef struct
### Структура zh_aht_handle_t
```c
typedef struct
{
bool is_initialized; // Флаг инициализации датчика
i2c_master_dev_handle_t dev_handle; // Уникальный дескриптор устройства I2C
} zh_aht_handle_t;
```
Структура объявляется как `typedef struct _zh_aht_handle_t zh_aht_handle_t;` и инкапсулирует детали внутренней реализации.
**Поля (внутренние):**
| Поле | Тип | Описание |
|------|-----|----------|
| `i2c_master_dev_handle_t` | `dev_handle` | Указатель на дескриптор устройства I2C. |
---
@@ -123,18 +125,21 @@ typedef struct
**Параметры:**
- `config` - Указатель на структуру конфигурации инициализации AHT
- `handle` - Указатель на уникальный дескриптор AHT
- `handle` - Двойной указатель на уникальный дескриптор AHT (`zh_aht_handle_t **`). Если указатель NULL, память будет выделена.
**Возвращает:**
- `ESP_OK` - Успех
- `ESP_ERR_INVALID_ARG` - Неверный аргумент (NULL config или handle)
- `ESP_FAIL` - Ошибка инициализации (проверка конфигурации или добавление устройства I2C)
- `ESP_ERR_INVALID_STATE` - Датчик уже инициализирован
- `ESP_ERR_NO_MEM` - Ошибка выделения памяти
- `ESP_FAIL` - Ошибка инициализации устройства I2C
- `ESP_ERR_NOT_FOUND` - Датчик не отвечает
**Пример:**
```c
zh_aht_handle_t aht_handle = {0};
zh_aht_handle_t *aht_handle = NULL;
zh_aht_init_config_t config = ZH_AHT_INIT_CONFIG_DEFAULT();
config.i2c_handle = i2c_bus_handle;
zh_aht_init(&config, &aht_handle);
@@ -144,17 +149,16 @@ zh_aht_init(&config, &aht_handle);
### zh_aht_deinit()
Деинициализирует датчик AHT и удаляет его из шины I2C.
Деинициализирует датчик AHT и удаляет его из шины I2C. Память для handle автоматически освобождается, а указатель устанавливается в NULL.
**Параметры:**
- `handle` - Указатель на уникальный дескриптор AHT
- `handle` - Двойной указатель на уникальный дескриптор AHT (`zh_aht_handle_t **`). Не должен быть NULL.
**Возвращает:**
- `ESP_OK` - Успех
- `ESP_ERR_INVALID_ARG` - Неверный аргумент (NULL handle)
- `ESP_ERR_INVALID_STATE` - Датчик не инициализирован
- `ESP_FAIL` - Ошибка удаления устройства I2C
---
@@ -165,7 +169,7 @@ zh_aht_init(&config, &aht_handle);
**Параметры:**
- `handle` - Указатель на уникальный дескриптор AHT
- `handle` - Двойной указатель на уникальный дескриптор AHT (`zh_aht_handle_t **`). Не должен быть NULL.
- `humidity` - Указатель для хранения значения влажности (в %)
- `temperature` - Указатель для хранения значения температуры (в °C)
@@ -173,10 +177,9 @@ zh_aht_init(&config, &aht_handle);
- `ESP_OK` - Успех
- `ESP_ERR_INVALID_ARG` - Неверный аргумент (NULL handle, humidity или temperature)
- `ESP_ERR_NOT_FOUND` - Датчик не инициализирован
- `ESP_FAIL` - Ошибка связи I2C
- `ESP_ERR_TIMEOUT` - Превышено время ожидания готовности данных
- `ESP_ERR_INVALID_CRC` - Неверная CRC контрольная сумма
- `ESP_FAIL` - Ошибка связи I2C
**Примечание:** Функция выполняет измерение и ожидает ~80 мс готовности данных.
@@ -188,13 +191,12 @@ zh_aht_init(&config, &aht_handle);
**Параметры:**
- `handle` - Указатель на уникальный дескриптор AHT
- `handle` - Двойной указатель на уникальный дескриптор AHT (`zh_aht_handle_t **`). Не должен быть NULL.
**Возвращает:**
- `ESP_OK` - Успех
- `ESP_ERR_INVALID_ARG` - Неверный аргумент (NULL handle)
- `ESP_ERR_NOT_FOUND` - Датчик не инициализирован
- `ESP_FAIL` - Ошибка связи I2C
---
@@ -207,25 +209,12 @@ zh_aht_init(&config, &aht_handle);
- Указатель на структуру статистики
**Пример:**
```c
const zh_aht_stats_t *stats = zh_aht_get_stats();
printf("Ошибки I2C: %ld\n", stats->i2c_driver_error);
```
---
### zh_aht_reset_stats()
Сбрасывает счетчик статистики ошибок.
**Пример:**
```c
zh_aht_reset_stats();
```
---
## Примеры использования
@@ -237,7 +226,7 @@ zh_aht_reset_stats();
#define I2C_PORT (I2C_NUM_MAX - 1)
zh_aht_handle_t aht_handle = {0};
zh_aht_handle_t *aht_handle = NULL;
void app_main(void)
{
@@ -280,9 +269,9 @@ void app_main(void)
#define I2C_PORT (I2C_NUM_MAX - 1)
zh_pca9548a_handle_t pca9548a_handle = {0};
zh_aht_handle_t aht_handle_chan_0 = {0};
zh_aht_handle_t aht_handle_chan_1 = {0};
zh_aht_handle_t aht_handle_chan_2 = {0};
zh_aht_handle_t *aht_handle_chan_0 = NULL;
zh_aht_handle_t *aht_handle_chan_1 = NULL;
zh_aht_handle_t *aht_handle_chan_2 = NULL;
void app_main(void)
{
@@ -346,10 +335,10 @@ void app_main(void)
| **Диапазон температуры** | -40 - +85°C |
| **Разрешение измерения влажности** | 0.01% |
| **Разрешение измерения температуры** | 0.01°C |
| **Адрес I2C** | 0x38, 0x39 |
| **Адрес I2C** | 0x38, 0x39, 0x44 |
| **Частота I2C** | До 400 кГц |
| **Время измерения** | ~80 мс |
| **Поддерживаемые модели** | AHT10, AHT15, AHT20, AHT21, AHT25, AHT30 |
| **Поддерживаемые модели** | AHT10, AHT15, AHT20, AHT21, AHT25, AHT30, AHT40 |
| **Версия ESP-IDF** | >= 5.0 |
| **Платформа** | Семейство ESP32 |
| **Язык** | C (C99) |
@@ -362,8 +351,9 @@ void app_main(void)
|------------|----------|
| `ESP_OK` | Операция выполнена успешно |
| `ESP_ERR_INVALID_ARG` | Неверный аргумент (NULL указатель или неверная конфигурация) |
| `ESP_ERR_INVALID_STATE` | Датчик не инициализирован |
| `ESP_ERR_NOT_FOUND` | Датчик не инициализирован или не отвечает |
| `ESP_ERR_INVALID_STATE` | Датчик уже инициализирован |
| `ESP_ERR_NO_MEM` | Ошибка выделения памяти |
| `ESP_ERR_NOT_FOUND` | Датчик не отвечает |
| `ESP_ERR_TIMEOUT` | Превышено время ожидания готовности данных |
| `ESP_ERR_INVALID_CRC` | Неверная CRC контрольная сумма |
| `ESP_FAIL` | Общая ошибка (ошибка связи I2C) |
@@ -412,7 +402,8 @@ void app_main(void)
- **Время измерения**: Датчик требует ~80 мс для каждого измерения
- **I2C_ISR_IRAM_SAFE**: Для правильной работы включите `I2C_ISR_IRAM_SAFE` и `I2C_MASTER_ISR_HANDLER_IN_IRAM` в menuconfig
- **Потокобезопасность**: Компонент использует драйвер I2C ESP-IDF, который является потокобезопасным
- **Управление памятью**: Все функции API теперь используют двойные указатели для handle, что позволяет автоматически управлять памятью (освобождение при деинициализации, установка в NULL)
---
*Сгенерировано для zh_aht v3.4.0*
*Сгенерировано для zh_aht v4.0.0*
+1 -1
View File
@@ -1,6 +1,6 @@
description: ESP32 ESP-IDF component for AHT10/AHT15/AHT20/AHT21/AHT25/AHT30 humidity & temperature sensor
url: https://github.com/aZholtikov/zh_aht
version: "3.4.0"
version: "4.0.0"
license: "Apache-2.0"
maintainers:
- "Alexey Zholtikov <alexey.zholtikov@yandex.ru>"
+74 -47
View File
@@ -1,5 +1,14 @@
/**
* @file zh_aht.h
*
* @brief ESP-IDF driver for AHT family sensors (AHT10, AHT15, AHT20, AHT21, AHT25, AHT30, AHT40).
*
* This module provides a high-level interface to control AHT family I2C temperature & humidity sensors.
* All devices (AHT10, AHT15, AHT20, AHT21, AHT25, AHT30, AHT40) share the same command set, data format, and CRC algorithm.
*
* @note Supported devices: AHT10, AHT15, AHT20, AHT21, AHT25, AHT30, AHT40, and compatible clones.
* @note Default I2C address: 0x38 (ADR tied to GND). Some variants (e.g., AHT20/30/40 on certain boards) may use 0x39 (ADR tied to VDD).
* @note Measurement range and accuracy differ between variants, but communication protocol is identical.
*/
#pragma once
@@ -9,100 +18,118 @@
#include "driver/i2c_master.h"
#include "freertos/FreeRTOS.h"
#ifdef __cplusplus
extern "C"
{
#endif
/**
* @brief AHT sensor initial default values.
* @brief Default AHT initialization configuration.
*
* @note I2C frequency: 400 kHz (fast mode — supported by all AHT variants).
* @note I2C address: 0x38 (ADR = GND). If ADR is pulled high (VDD), use 0x39. For AHT40 use 0x44.
*
* @warning AHT20/30 may be configured to 0x39 by hardware — check your board!
*/
#define ZH_AHT_INIT_CONFIG_DEFAULT() \
{ \
.i2c_frequency = 400000, \
.i2c_address = 0x38}
#ifdef __cplusplus
extern "C"
{
#endif
/**
* @brief Opaque handle type for AHT family sensor instance.
*/
typedef struct _zh_aht_handle_t zh_aht_handle_t;
/**
* @brief Structure for initial initialization of AHT sensor.
* @brief AHT sensor initialization configuration structure.
*/
typedef struct
{
i2c_master_bus_handle_t i2c_handle; /*!< Unique I2C bus handle. */
uint8_t i2c_address; /*!< Sensor I2C address. */
uint32_t i2c_frequency; /*!< Sensor I2C frequency. */
i2c_master_bus_handle_t i2c_handle; /*!< I2C bus handle (created via i2c_master_bus_create() or i2c_master_bus_alloc_handle()). */
uint8_t i2c_address; /*!< I2C address: 0x38 (ADR=GND) or 0x39 (ADR=VDD) or 0x44 (AHT40). */
uint32_t i2c_frequency; /*!< I2C clock frequency in Hz (≤ 400000). */
} zh_aht_init_config_t;
/**
* @brief AHT sensor handle.
* @brief Error statistics structure.
*/
typedef struct
{
bool is_initialized; /*!< Sensor initialization flag. */
i2c_master_dev_handle_t dev_handle; /*!< Unique I2C device handle. */
} zh_aht_handle_t;
/**
* @brief Structure for error statistics storage.
*/
typedef struct
{
uint32_t i2c_driver_error; /*!< Number of i2c driver error. */
uint32_t i2c_driver_error; /*!< Number of I2C errors in zh_aht_read()/zh_aht_reset(). */
} zh_aht_stats_t;
/**
* @brief Initialize AHT sensor.
* @brief Initialize an AHT family sensor and allocate handle.
*
* @param[in] config Pointer to AHT initialized configuration structure. Can point to a temporary variable.
* @param[out] handle Pointer to unique AHT handle.
* Validates configuration, allocates handle, registers device, probes sensor, and triggers calibration if needed.
*
* @attention I2C driver must be initialized first.
* @param[in] config Pointer to initialized configuration (must be non `NULL`, valid).
* @param[out] handle Double pointer to handle structure (`zh_aht_handle_t **`). On success, `*handle` points to newly allocated structure.
*
* @note Before initialize the sensor recommend initialize zh_aht_init_config_t structure with default values.
*
* @code zh_aht_init_config_t config = ZH_AHT_INIT_CONFIG_DEFAULT() @endcode
*
* @return ESP_OK if success or an error code otherwise.
* @return ESP_OK on success.
* @return ESP_ERR_INVALID_ARG if `config == NULL` or `handle == NULL`.
* @return ESP_ERR_INVALID_STATE if `*handle != NULL` (already initialized).
* @return ESP_ERR_NO_MEM if handle allocation failed.
* @return ESP_FAIL if I2C registration failed.
* @return ESP_ERR_NOT_FOUND if sensor did not respond.
*/
esp_err_t zh_aht_init(const zh_aht_init_config_t *config, zh_aht_handle_t *handle);
esp_err_t zh_aht_init(const zh_aht_init_config_t *config, zh_aht_handle_t **handle);
/**
* @brief Deinitialize AHT sensor.
* @brief Deinitialize the AHT sensor and release resources.
*
* @param[in] handle Pointer to unique AHT handle.
* Removes I2C device, frees handle, sets `*handle = NULL`.
*
* @return ESP_OK if success or an error code otherwise.
* @param[in,out] handle Double pointer to handle structure (`zh_aht_handle_t **`). Must not be `NULL`. Set to `NULL` on exit.
*
* @return ESP_OK on success.
* @return ESP_ERR_INVALID_ARG if `handle == NULL` or `*handle == NULL` (not initialized).
* @return ESP_FAIL if failed to remove I2C device.
*/
esp_err_t zh_aht_deinit(zh_aht_handle_t *handle);
esp_err_t zh_aht_deinit(zh_aht_handle_t **handle);
/**
* @brief Read AHT sensor.
* @brief Trigger measurement and read temperature & humidity.
*
* @param[in] handle Pointer to unique AHT handle.
* @param[out] humidity Pointer for AHT sensor reading data of humidity.
* @param[out] temperature Pointer for AHT sensor reading data of temperature.
* Sends read command, waits 80 ms, validates CRC/status, and converts to physical units.
*
* @return ESP_OK if success or an error code otherwise.
* @param[in] handle Double pointer to handle structure (`zh_aht_handle_t **`). Must not be `NULL`.
* @param[out] humidity Pointer to store humidity (%RH).
* @param[out] temperature Pointer to store temperature (°C).
*
* @return ESP_OK on success.
* @return ESP_ERR_INVALID_ARG if `handle == NULL` or `*handle == NULL` (not initialized) or `humidity == NULL` or `temperature == NULL`.
* @return ESP_FAIL if I2C error.
* @return ESP_ERR_TIMEOUT if sensor still busy.
* @return ESP_ERR_INVALID_CRC if CRC mismatch.
*/
esp_err_t zh_aht_read(zh_aht_handle_t *handle, float *humidity, float *temperature);
esp_err_t zh_aht_read(zh_aht_handle_t **handle, float *humidity, float *temperature);
/**
* @brief Reset AHT sensor.
* @brief Soft reset the AHT sensor.
*
* @param[in] handle Pointer to unique AHT handle.
* Sends reset command and waits 20 ms for recovery.
*
* @return ESP_OK if success or an error code otherwise.
* @param[in] handle Double pointer to handle structure (`zh_aht_handle_t **`). Must not be `NULL`.
*
* @return ESP_OK on success.
* @return ESP_ERR_INVALID_ARG if `handle == NULL` or `*handle == NULL` (not initialized).
* @return ESP_FAIL if I2C error.
*/
esp_err_t zh_aht_reset(zh_aht_handle_t *handle);
esp_err_t zh_aht_reset(zh_aht_handle_t **handle);
/**
* @brief Get error statistics.
* @brief Get pointer to internal error statistics.
*
* @return Pointer to the statistics structure.
* @return Pointer to static statistics structure.
*/
const zh_aht_stats_t *zh_aht_get_stats(void);
/**
* @brief Reset error statistics.
* @brief Reset error statistics counter.
*
* Sets `i2c_driver_error = 0`.
*/
void zh_aht_reset_stats(void);
+1 -1
View File
@@ -1 +1 @@
3.4.0
4.0.0
+55 -28
View File
@@ -13,47 +13,75 @@ static const char *TAG = "zh_aht";
return err; \
}
static const uint8_t _aht_reset_command = 0xBA;
static const uint8_t _aht_read_command[] = {0xAC, 0x33, 0x00};
static const uint8_t _aht_init_command[] = {0xBE, 0x08, 0x00};
/**
* @brief Opaque handle structure for the AHT family sensor (AHT10/AHT15/AHT20/AHT21/AHT25/AHT30/AHT40).
*
* This structure encapsulates the I2C device handle required to communicate with any sensor
* in the AHT family. All AHT sensors share the same I2C protocol, register map, and CRC algorithm.
*
* @note Supported devices: AHT10, AHT15, AHT20, AHT21, AHT25, AHT30, AHT40, and compatible clones.
* @note Default I2C addresses:
* - AHT10: 0x38 (ADR tied to GND)
* - AHT20/30: 0x38 (ADR tied to GND), 0x39 (ADR tied to VDD)
* - AHT40: 0x44
* @note Some variants may differ in measurement range and accuracy, but not in communication protocol.
*
* @note Do not instantiate or modify this structure directly. All interactions must go through
* the public API functions: zh_aht_init(), zh_aht_read(), zh_aht_deinit(), etc.
*/
struct _zh_aht_handle_t
{
i2c_master_dev_handle_t dev_handle; /*!< I2C device handle (ESP-IDF i2c_master API) used for all bus transactions. */
};
/**
* @brief AHT family command constants.
*
* @note All AHT family members (AHT10, AHT15, AHT20, AHT21, AHT25, AHT30, AHT40) support these commands.
* @note CRC algorithm, data format, and command structure are identical across family.
*/
static const uint8_t _aht_reset_command = 0xBA; /*!< Soft reset command (AHT family common). */
static const uint8_t _aht_read_command[] = {0xAC, 0x33, 0x00}; /*!< Trigger measurement and read data command. Format: 0xAC (measurement trigger) + 0x33 (normal mode) + 0x00 (unused byte). */
static const uint8_t _aht_init_command[] = {0xBE, 0x08, 0x00}; /*!< Self-calibration/initialization command (must be sent if CALIBRATED bit (status[3]) = 0). */
static zh_aht_stats_t _stats = {0};
static esp_err_t _zh_aht_validate_config(const zh_aht_init_config_t *config);
static esp_err_t _zh_aht_i2c_init(const zh_aht_init_config_t *config, zh_aht_handle_t *handle);
static esp_err_t _zh_aht_i2c_init(const zh_aht_init_config_t *config, zh_aht_handle_t **handle);
static uint8_t _zh_aht_calc_crc(const uint8_t *buf, size_t len);
esp_err_t zh_aht_init(const zh_aht_init_config_t *config, zh_aht_handle_t *handle)
esp_err_t zh_aht_init(const zh_aht_init_config_t *config, zh_aht_handle_t **handle) // -V2008
{
ZH_LOGI("AHT initialization begin.");
ZH_ERROR_CHECK(config != NULL && handle != NULL, ESP_ERR_INVALID_ARG, NULL, "AHT initialization failed. Invalid argument.");
ZH_ERROR_CHECK(_zh_aht_validate_config(config) == ESP_OK, ESP_FAIL, NULL, "AHT initialization failed. Initial configuration check failed.");
ZH_ERROR_CHECK(_zh_aht_i2c_init(config, handle) == ESP_OK, ESP_FAIL, NULL, "AHT initialization failed. Failed to add I2C device.");
handle->is_initialized = true;
ZH_ERROR_CHECK(_zh_aht_validate_config(config) == ESP_OK, ESP_ERR_INVALID_ARG, NULL, "AHT initialization failed. Invalid config.");
ZH_ERROR_CHECK(*handle == NULL, ESP_ERR_INVALID_STATE, NULL, "AHT initialization failed. AHT is already initialized.");
*handle = heap_caps_calloc(1, sizeof(zh_aht_handle_t), MALLOC_CAP_8BIT);
ZH_ERROR_CHECK(*handle != NULL, ESP_ERR_NO_MEM, NULL, "AHT initialization failed. Failed to allocate AHT handle.");
ZH_ERROR_CHECK(_zh_aht_i2c_init(config, handle) == ESP_OK, ESP_FAIL, heap_caps_free(*handle); *handle = NULL, "AHT initialization failed. I2C init failed.");
ZH_LOGI("AHT initialization completed successfully.");
return ESP_OK;
}
esp_err_t zh_aht_deinit(zh_aht_handle_t *handle)
esp_err_t zh_aht_deinit(zh_aht_handle_t **handle)
{
ZH_LOGI("AHT deinitialization started.");
ZH_ERROR_CHECK(handle != NULL, ESP_ERR_INVALID_ARG, NULL, "AHT deinitialization failed. Invalid argument.");
ZH_ERROR_CHECK(handle->is_initialized == true, ESP_ERR_INVALID_STATE, NULL, "AHT deinitialization failed. AHT not initialized.");
ZH_ERROR_CHECK(i2c_master_bus_rm_device(handle->dev_handle) == ESP_OK, ESP_FAIL, NULL, "AHT deinitialization failed. I2C remove device failed.");
handle->is_initialized = false;
ZH_ERROR_CHECK(handle != NULL && *handle != NULL, ESP_ERR_INVALID_ARG, NULL, "AHT deinitialization failed. Invalid argument.");
ZH_ERROR_CHECK(i2c_master_bus_rm_device((*handle)->dev_handle) == ESP_OK, ESP_FAIL, NULL, "AHT deinitialization failed. I2C remove device failed.");
heap_caps_free(*handle);
*handle = NULL;
ZH_LOGI("AHT deinitialization completed successfully.");
return ESP_OK;
}
esp_err_t zh_aht_read(zh_aht_handle_t *handle, float *humidity, float *temperature) // -V2008
esp_err_t zh_aht_read(zh_aht_handle_t **handle, float *humidity, float *temperature) // -V2008
{
ZH_LOGI("AHT read begin.");
ZH_ERROR_CHECK(humidity != NULL && temperature != NULL && handle != NULL, ESP_ERR_INVALID_ARG, NULL, "AHT read fail. Invalid argument.");
ZH_ERROR_CHECK(handle->is_initialized == true, ESP_ERR_NOT_FOUND, NULL, "AHT read fail. AHT not initialized.");
ZH_ERROR_CHECK(handle != NULL && *handle != NULL && humidity != NULL && temperature != NULL, ESP_ERR_INVALID_ARG, NULL, "AHT read fail. Invalid argument.");
uint8_t sensor_data[7] = {0};
ZH_ERROR_CHECK(i2c_master_transmit(handle->dev_handle, _aht_read_command, sizeof(_aht_read_command), 1000 / portTICK_PERIOD_MS) == ESP_OK, ESP_FAIL,
ZH_ERROR_CHECK(i2c_master_transmit((*handle)->dev_handle, _aht_read_command, sizeof(_aht_read_command), 1000 / portTICK_PERIOD_MS) == ESP_OK, ESP_FAIL,
++_stats.i2c_driver_error, "AHT read fail. I2C driver error.");
vTaskDelay(80 / portTICK_PERIOD_MS);
ZH_ERROR_CHECK(i2c_master_receive(handle->dev_handle, sensor_data, sizeof(sensor_data), 1000 / portTICK_PERIOD_MS) == ESP_OK, ESP_FAIL,
ZH_ERROR_CHECK(i2c_master_receive((*handle)->dev_handle, sensor_data, sizeof(sensor_data), 1000 / portTICK_PERIOD_MS) == ESP_OK, ESP_FAIL,
++_stats.i2c_driver_error, "AHT read fail. I2C driver error.");
ZH_ERROR_CHECK((sensor_data[0] & 0x80) == 0, ESP_ERR_TIMEOUT, NULL, "AHT read fail. Timeout exceeded.");
ZH_ERROR_CHECK(_zh_aht_calc_crc(sensor_data, 6) == sensor_data[6], ESP_ERR_INVALID_CRC, NULL, "AHT read fail. Invalid CRC.");
@@ -63,12 +91,11 @@ esp_err_t zh_aht_read(zh_aht_handle_t *handle, float *humidity, float *temperatu
return ESP_OK;
}
esp_err_t zh_aht_reset(zh_aht_handle_t *handle)
esp_err_t zh_aht_reset(zh_aht_handle_t **handle)
{
ZH_LOGI("AHT reset begin.");
ZH_ERROR_CHECK(handle != NULL, ESP_ERR_INVALID_ARG, NULL, "AHT reset fail. Invalid argument.");
ZH_ERROR_CHECK(handle->is_initialized == true, ESP_ERR_NOT_FOUND, NULL, "AHT reset fail. AHT not initialized.");
ZH_ERROR_CHECK(i2c_master_transmit(handle->dev_handle, &_aht_reset_command, sizeof(_aht_reset_command), 1000 / portTICK_PERIOD_MS) == ESP_OK, ESP_FAIL,
ZH_ERROR_CHECK(handle != NULL && *handle != NULL, ESP_ERR_INVALID_ARG, NULL, "AHT reset fail. Invalid argument.");
ZH_ERROR_CHECK(i2c_master_transmit((*handle)->dev_handle, &_aht_reset_command, sizeof(_aht_reset_command), 1000 / portTICK_PERIOD_MS) == ESP_OK, ESP_FAIL,
++_stats.i2c_driver_error, "AHT reset fail. I2C driver error.");
vTaskDelay(20 / portTICK_PERIOD_MS);
ZH_LOGI("AHT reset completed successfully.");
@@ -89,13 +116,13 @@ void zh_aht_reset_stats(void)
static esp_err_t _zh_aht_validate_config(const zh_aht_init_config_t *config)
{
ZH_ERROR_CHECK(config->i2c_address == 0x38 || config->i2c_address == 0x39, ESP_ERR_INVALID_ARG, NULL, "Invalid I2C address.");
ZH_ERROR_CHECK(config->i2c_address == 0x38 || config->i2c_address == 0x39 || config->i2c_address == 0x44, ESP_ERR_INVALID_ARG, NULL, "Invalid I2C address.");
ZH_ERROR_CHECK(config->i2c_frequency <= 400000, ESP_ERR_INVALID_ARG, NULL, "Invalid I2C frequency.");
ZH_ERROR_CHECK(config->i2c_handle != NULL, ESP_ERR_INVALID_ARG, NULL, "Invalid I2C handle.");
return ESP_OK;
}
static esp_err_t _zh_aht_i2c_init(const zh_aht_init_config_t *config, zh_aht_handle_t *handle) // -V2008
static esp_err_t _zh_aht_i2c_init(const zh_aht_init_config_t *config, zh_aht_handle_t **handle) // -V2008
{
i2c_device_config_t aht_config = {
.dev_addr_length = I2C_ADDR_BIT_LEN_7,
@@ -106,17 +133,17 @@ static esp_err_t _zh_aht_i2c_init(const zh_aht_init_config_t *config, zh_aht_han
ZH_ERROR_CHECK(i2c_master_bus_add_device(config->i2c_handle, &aht_config, &_dev_handle) == ESP_OK, ESP_FAIL, NULL, "Add I2C device failed.");
vTaskDelay(100 / portTICK_PERIOD_MS);
ZH_ERROR_CHECK(i2c_master_probe(config->i2c_handle, config->i2c_address, 1000 / portTICK_PERIOD_MS) == ESP_OK, ESP_ERR_NOT_FOUND,
ZH_ERROR_CHECK(i2c_master_bus_rm_device(_dev_handle) == ESP_OK, ESP_FAIL, NULL, "I2C remove device failed."), "Sensor not connected or not responded.");
{ZH_ERROR_CHECK(i2c_master_bus_rm_device(_dev_handle) == ESP_OK, ESP_FAIL, NULL, "I2C remove device failed.")}, "Sensor not connected or not responded.");
uint8_t sensor_data = 0;
ZH_ERROR_CHECK(i2c_master_receive(_dev_handle, &sensor_data, sizeof(sensor_data), 1000 / portTICK_PERIOD_MS) == ESP_OK, ESP_FAIL,
ZH_ERROR_CHECK(i2c_master_bus_rm_device(_dev_handle) == ESP_OK, ESP_FAIL, NULL, "I2C remove device failed."), "I2C driver error.");
{ZH_ERROR_CHECK(i2c_master_bus_rm_device(_dev_handle) == ESP_OK, ESP_FAIL, NULL, "I2C remove device failed.")}, "I2C driver error.");
if ((sensor_data & 0x08) == 0)
{
ZH_ERROR_CHECK(i2c_master_transmit(_dev_handle, _aht_init_command, sizeof(_aht_init_command), 1000 / portTICK_PERIOD_MS) == ESP_OK, ESP_FAIL,
ZH_ERROR_CHECK(i2c_master_bus_rm_device(_dev_handle) == ESP_OK, ESP_FAIL, NULL, "I2C remove device failed."), "I2C driver error.");
{ZH_ERROR_CHECK(i2c_master_bus_rm_device(_dev_handle) == ESP_OK, ESP_FAIL, NULL, "I2C remove device failed.")}, "I2C driver error.");
vTaskDelay(20 / portTICK_PERIOD_MS);
}
handle->dev_handle = _dev_handle;
(*handle)->dev_handle = _dev_handle;
return ESP_OK;
}