Compare commits
10
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1bc908eb50 | ||
|
|
938eb7735d | ||
|
|
2da2d309ce | ||
|
|
8fe5aa7a03 | ||
|
|
07d8133d44 | ||
|
|
4cbd742102 | ||
|
|
8da6d528b2 | ||
|
|
b5b31ec7c4 | ||
|
|
5876c50229 | ||
|
|
6e763158d7 |
No files matched your search
@@ -1,31 +1,30 @@
|
|||||||
# ESP32 ESP-IDF zh_aht - Humidity & Temperature Sensor Component
|
# ESP32 ESP-IDF component for AHT series humidity & temperature sensor (I2C)
|
||||||
|
|
||||||
## Wiki
|
|
||||||
|
|
||||||
[EN](WIKI_EN.md) | [RU](WIKI_RU.md)
|
|
||||||
|
|
||||||
## Tested on
|
|
||||||
|
|
||||||
1. [ESP32 ESP-IDF v6.0.0](https://docs.espressif.com/projects/esp-idf/en/v6.0/esp32/index.html)
|
|
||||||
|
|
||||||
## SAST Tools
|
|
||||||
|
|
||||||
[PVS-Studio](https://pvs-studio.com/pvs-studio/?utm_source=website&utm_medium=github&utm_campaign=open_source) - static analyzer for C, C++, C#, and Java code.
|
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
||||||
1. Support of AHT10/AHT15/AHT20/AHT21/AHT25/AHT30/AHT40.
|
1. Support for the whole AHT family including AHT10, AHT15, AHT20, AHT21, AHT25, AHT30 and AHT40.
|
||||||
2. Support multiple sensors on one device with [zh_pca9548a](https://github.com/aZholtikov/zh_pca9548a).
|
2. Measurement of relative humidity in range 0 to 100 percent and temperature in range -50 to 150 degrees Celsius.
|
||||||
3. CRC8 data integrity verification.
|
3. Data integrity verification with CRC8 algorithm on every measurement frame.
|
||||||
4. Error statistics tracking.
|
4. Automatic sensor detection and initialization during setup.
|
||||||
|
5. Software reset of the sensor over I2C.
|
||||||
|
6. Tracking of I2C driver error statistics with a resettable counter.
|
||||||
|
7. I2C clock frequency up to 400000 Hz on addresses 0x38, 0x39 and 0x44.
|
||||||
|
8. Multiple sensors on one bus through external multiplexer zh_pca9548a.
|
||||||
|
|
||||||
|
## Attention
|
||||||
|
|
||||||
|
1. The component is not thread safe, one sensor handle must be served by a single task.
|
||||||
|
2. I2C bus is created by the user and the component does not manage it.
|
||||||
|
3. Statistics counters are global and shared between all sensor instances.
|
||||||
|
4. Read and reset functions contain blocking delays and cannot be called from a context without task switching.
|
||||||
|
|
||||||
## Using
|
## Using
|
||||||
|
|
||||||
In an existing project, run the following command to install the component:
|
In an existing project, run the following command to install the component:
|
||||||
|
|
||||||
```text
|
```bash
|
||||||
cd ../your_project/components
|
cd ../your_project/components
|
||||||
git clone https://github.com/aZholtikov/zh_aht.git
|
git clone https://github.com/aZholtikov/zh_aht
|
||||||
```
|
```
|
||||||
|
|
||||||
In the application, add the component:
|
In the application, add the component:
|
||||||
@@ -36,4 +35,197 @@ In the application, add the component:
|
|||||||
|
|
||||||
## Examples
|
## Examples
|
||||||
|
|
||||||
See Wiki [EN](WIKI_EN.md#usage-examples) | [RU](WIKI_RU.md#примеры-использования)
|
### Basic Example: Read Humidity and Temperature
|
||||||
|
|
||||||
|
```c
|
||||||
|
#include "zh_aht.h"
|
||||||
|
|
||||||
|
#define I2C_PORT (I2C_NUM_MAX - 1)
|
||||||
|
|
||||||
|
i2c_master_bus_handle_t i2c_bus_handle = NULL;
|
||||||
|
zh_aht_handle_t *aht_handle = NULL;
|
||||||
|
|
||||||
|
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_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;
|
||||||
|
esp_err_t ret = zh_aht_init(&config, &aht_handle);
|
||||||
|
if (ret != ESP_OK)
|
||||||
|
{
|
||||||
|
printf("AHT initialization error: %s\n", esp_err_to_name(ret));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
float humidity = 0.0;
|
||||||
|
float temperature = 0.0;
|
||||||
|
ret = zh_aht_read(&aht_handle, &humidity, &temperature);
|
||||||
|
if (ret == ESP_OK)
|
||||||
|
{
|
||||||
|
printf("Humidity: %.2f%%, Temperature: %.2f°C\n", humidity, temperature);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
printf("Read error: %s\n", esp_err_to_name(ret));
|
||||||
|
}
|
||||||
|
zh_aht_deinit(&aht_handle);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Read In A Loop Example
|
||||||
|
|
||||||
|
```c
|
||||||
|
#include "zh_aht.h"
|
||||||
|
|
||||||
|
#define I2C_PORT (I2C_NUM_MAX - 1)
|
||||||
|
|
||||||
|
i2c_master_bus_handle_t i2c_bus_handle = NULL;
|
||||||
|
zh_aht_handle_t *aht_handle = NULL;
|
||||||
|
|
||||||
|
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_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;
|
||||||
|
esp_err_t ret = zh_aht_init(&config, &aht_handle);
|
||||||
|
if (ret != ESP_OK)
|
||||||
|
{
|
||||||
|
printf("AHT initialization error: %s\n", esp_err_to_name(ret));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
float humidity = 0.0;
|
||||||
|
float temperature = 0.0;
|
||||||
|
for (int i = 0; i < 10; i++)
|
||||||
|
{
|
||||||
|
ret = zh_aht_read(&aht_handle, &humidity, &temperature);
|
||||||
|
if (ret == ESP_OK)
|
||||||
|
{
|
||||||
|
printf("Humidity: %.2f%%, Temperature: %.2f°C\n", humidity, temperature);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
printf("Read error: %s\n", esp_err_to_name(ret));
|
||||||
|
}
|
||||||
|
vTaskDelay(5000 / portTICK_PERIOD_MS);
|
||||||
|
}
|
||||||
|
zh_aht_deinit(&aht_handle);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Reset Sensor Example
|
||||||
|
|
||||||
|
```c
|
||||||
|
#include "zh_aht.h"
|
||||||
|
|
||||||
|
#define I2C_PORT (I2C_NUM_MAX - 1)
|
||||||
|
|
||||||
|
i2c_master_bus_handle_t i2c_bus_handle = NULL;
|
||||||
|
zh_aht_handle_t *aht_handle = NULL;
|
||||||
|
|
||||||
|
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_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;
|
||||||
|
esp_err_t ret = zh_aht_init(&config, &aht_handle);
|
||||||
|
if (ret != ESP_OK)
|
||||||
|
{
|
||||||
|
printf("AHT initialization error: %s\n", esp_err_to_name(ret));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ret = zh_aht_reset(&aht_handle);
|
||||||
|
if (ret == ESP_OK)
|
||||||
|
{
|
||||||
|
printf("Sensor reset ok\n");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
printf("Reset error: %s\n", esp_err_to_name(ret));
|
||||||
|
}
|
||||||
|
float humidity = 0.0;
|
||||||
|
float temperature = 0.0;
|
||||||
|
ret = zh_aht_read(&aht_handle, &humidity, &temperature);
|
||||||
|
if (ret == ESP_OK)
|
||||||
|
{
|
||||||
|
printf("Humidity: %.2f%%, Temperature: %.2f°C\n", humidity, temperature);
|
||||||
|
}
|
||||||
|
zh_aht_deinit(&aht_handle);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Error Statistics Example
|
||||||
|
|
||||||
|
```c
|
||||||
|
#include "zh_aht.h"
|
||||||
|
|
||||||
|
#define I2C_PORT (I2C_NUM_MAX - 1)
|
||||||
|
|
||||||
|
i2c_master_bus_handle_t i2c_bus_handle = NULL;
|
||||||
|
zh_aht_handle_t *aht_handle = NULL;
|
||||||
|
|
||||||
|
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_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;
|
||||||
|
esp_err_t ret = zh_aht_init(&config, &aht_handle);
|
||||||
|
if (ret != ESP_OK)
|
||||||
|
{
|
||||||
|
printf("AHT initialization error: %s\n", esp_err_to_name(ret));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
float humidity = 0.0;
|
||||||
|
float temperature = 0.0;
|
||||||
|
for (int i = 0; i < 10; i++)
|
||||||
|
{
|
||||||
|
zh_aht_read(&aht_handle, &humidity, &temperature);
|
||||||
|
}
|
||||||
|
const zh_aht_stats_t *stats = zh_aht_get_stats();
|
||||||
|
printf("I2C errors: %lu\n", (unsigned long)stats->i2c_driver_error);
|
||||||
|
zh_aht_reset_stats();
|
||||||
|
stats = zh_aht_get_stats();
|
||||||
|
printf("I2C errors after reset: %lu\n", (unsigned long)stats->i2c_driver_error);
|
||||||
|
zh_aht_deinit(&aht_handle);
|
||||||
|
}
|
||||||
|
```
|
||||||
-441
@@ -1,441 +0,0 @@
|
|||||||
# zh_aht — AHT Humidity & Temperature Sensor Component for ESP-IDF
|
|
||||||
|
|
||||||
## Table of Contents
|
|
||||||
|
|
||||||
- [Overview](#overview)
|
|
||||||
- [Features](#features)
|
|
||||||
- [Installation](#installation)
|
|
||||||
- [API Reference](#api-reference)
|
|
||||||
- [Usage Examples](#usage-examples)
|
|
||||||
- [Technical Specifications](#technical-specifications)
|
|
||||||
- [Error Codes](#error-codes)
|
|
||||||
- [Contributing](#contributing)
|
|
||||||
- [License](#license)
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Overview
|
|
||||||
|
|
||||||
`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.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Features
|
|
||||||
|
|
||||||
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 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)
|
|
||||||
8. **Minimal Overhead**: Low memory and CPU overhead
|
|
||||||
9. **Multiple Sensor Support**: Compatible with I2C multiplexer (zh_pca9548a)
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Installation
|
|
||||||
|
|
||||||
Navigate to your project's components directory:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
cd ../your_project/components
|
|
||||||
```
|
|
||||||
|
|
||||||
Clone the repository:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
git clone https://github.com/aZholtikov/zh_aht.git
|
|
||||||
```
|
|
||||||
|
|
||||||
In your application, include the header:
|
|
||||||
|
|
||||||
```c
|
|
||||||
#include "zh_aht.h"
|
|
||||||
```
|
|
||||||
|
|
||||||
The component will be automatically built with your project.
|
|
||||||
|
|
||||||
### Required menuconfig Settings
|
|
||||||
|
|
||||||
For correct operation, enable the following settings in menuconfig:
|
|
||||||
|
|
||||||
```text
|
|
||||||
I2C_ISR_IRAM_SAFE
|
|
||||||
I2C_MASTER_ISR_HANDLER_IN_IRAM
|
|
||||||
```
|
|
||||||
|
|
||||||
### Optional: Using with I2C Multiplexer (zh_pca9548a)
|
|
||||||
|
|
||||||
To use multiple AHT sensors on the same I2C bus, also install the [zh_pca9548a](https://github.com/aZholtikov/zh_pca9548a) component.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 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
|
|
||||||
|
|
||||||
**Description:**
|
|
||||||
|
|
||||||
Initialization configuration for AHT sensor (I2C bus, address, frequency).
|
|
||||||
|
|
||||||
**Fields (internal):**
|
|
||||||
|
|
||||||
| Field | Type | Description / Описание |
|
|
||||||
| ------- | ------ | ------------------------ |
|
|
||||||
| `i2c_handle` | `i2c_master_bus_handle_t` | Unique I2C bus handle. |
|
|
||||||
| `i2c_address` | `uint8_t` | Sensor I2C address (0x38, 0x39, or 0x44). |
|
|
||||||
| `i2c_frequency` | `uint32_t` | Sensor I2C frequency (max 400000 Hz). |
|
|
||||||
|
|
||||||
Use `ZH_AHT_INIT_CONFIG_DEFAULT()` macro to initialize with default values:
|
|
||||||
|
|
||||||
- `i2c_frequency`: 400000 Hz
|
|
||||||
- `i2c_address`: 0x38
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### zh_aht_handle_t Structure
|
|
||||||
|
|
||||||
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 / Описание |
|
|
||||||
|-------|------|------------------------|
|
|
||||||
| `dev_handle` | `i2c_master_dev_handle_t` | Pointer to I2C device handle. |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### zh_aht_stats_t Structure
|
|
||||||
|
|
||||||
**Description:**
|
|
||||||
|
|
||||||
Sensor error statistics structure.
|
|
||||||
|
|
||||||
**Fields (internal):**
|
|
||||||
|
|
||||||
| Field | Type | Description / Описание |
|
|
||||||
|-------|------|------------------------|
|
|
||||||
| `i2c_driver_error` | `uint32_t` | Number of I2C driver errors. |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### zh_aht_init()
|
|
||||||
|
|
||||||
Initializes the AHT sensor.
|
|
||||||
|
|
||||||
**Parameters:**
|
|
||||||
|
|
||||||
- `config` - Pointer to AHT initialization configuration structure
|
|
||||||
- `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_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 = NULL;
|
|
||||||
zh_aht_init_config_t config = ZH_AHT_INIT_CONFIG_DEFAULT();
|
|
||||||
config.i2c_handle = i2c_bus_handle;
|
|
||||||
zh_aht_init(&config, &aht_handle);
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### zh_aht_deinit()
|
|
||||||
|
|
||||||
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` - 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 pointer)
|
|
||||||
- `ESP_FAIL` - I2C device removal failed
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### zh_aht_read()
|
|
||||||
|
|
||||||
Reads humidity and temperature values from the sensor.
|
|
||||||
|
|
||||||
**Parameters:**
|
|
||||||
|
|
||||||
- `handle` - Double pointer to unique AHT handle (`zh_aht_handle_t **`). Must not be NULL
|
|
||||||
- `humidity` - Pointer to store humidity value (in %). Must not be NULL
|
|
||||||
- `temperature` - Pointer to store temperature value (in °C). Must not be NULL
|
|
||||||
|
|
||||||
**Returns:**
|
|
||||||
|
|
||||||
- `ESP_OK` - Success
|
|
||||||
- `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
|
|
||||||
|
|
||||||
**Note:** The function performs measurement and waits ~80ms for data ready.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### zh_aht_reset()
|
|
||||||
|
|
||||||
Resets the AHT sensor.
|
|
||||||
|
|
||||||
**Parameters:**
|
|
||||||
|
|
||||||
- `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 pointer)
|
|
||||||
- `ESP_FAIL` - I2C communication error
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### zh_aht_get_stats()
|
|
||||||
|
|
||||||
Gets error statistics since last reset.
|
|
||||||
|
|
||||||
**Returns:**
|
|
||||||
|
|
||||||
- Pointer to const `zh_aht_stats_t` structure (valid until `zh_aht_reset_stats()` is called)
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### zh_aht_reset_stats()
|
|
||||||
|
|
||||||
Resets all error statistics counters to zero.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Usage Examples
|
|
||||||
|
|
||||||
### Example: Single Sensor
|
|
||||||
|
|
||||||
```c
|
|
||||||
#include "zh_aht.h"
|
|
||||||
|
|
||||||
static const int I2C_PORT = I2C_NUM_MAX - 1;
|
|
||||||
static const int SDA_IO = GPIO_NUM_21;
|
|
||||||
static const int SCL_IO = GPIO_NUM_22;
|
|
||||||
|
|
||||||
static zh_aht_handle_t *aht_handle = NULL;
|
|
||||||
|
|
||||||
void app_main(void)
|
|
||||||
{
|
|
||||||
esp_log_level_set("zh_aht", ESP_LOG_ERROR);
|
|
||||||
|
|
||||||
i2c_master_bus_config_t bus_config = {
|
|
||||||
.clk_source = I2C_CLK_SRC_DEFAULT,
|
|
||||||
.i2c_port = I2C_PORT,
|
|
||||||
.scl_io_num = SCL_IO,
|
|
||||||
.sda_io_num = SDA_IO,
|
|
||||||
.glitch_ignore_cnt = 7,
|
|
||||||
.flags.enable_internal_pullup = true,
|
|
||||||
};
|
|
||||||
i2c_master_bus_handle_t bus_handle = NULL;
|
|
||||||
ESP_ERROR_CHECK(i2c_new_master_bus(&bus_config, &bus_handle));
|
|
||||||
|
|
||||||
zh_aht_init_config_t config = ZH_AHT_INIT_CONFIG_DEFAULT();
|
|
||||||
config.i2c_handle = bus_handle;
|
|
||||||
ESP_ERROR_CHECK(zh_aht_init(&config, &aht_handle));
|
|
||||||
|
|
||||||
float humidity = 0.0f;
|
|
||||||
float temperature = 0.0f;
|
|
||||||
|
|
||||||
for (;;)
|
|
||||||
{
|
|
||||||
esp_err_t err = zh_aht_read(&aht_handle, &humidity, &temperature);
|
|
||||||
if (err == ESP_OK)
|
|
||||||
{
|
|
||||||
printf("Humidity: %.2f%%\n", humidity);
|
|
||||||
printf("Temperature: %.2f°C\n", temperature);
|
|
||||||
const zh_aht_stats_t *stats = zh_aht_get_stats();
|
|
||||||
printf("I2C errors: %lu\n", stats->i2c_driver_error);
|
|
||||||
}
|
|
||||||
vTaskDelay(5000 / portTICK_PERIOD_MS);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Resource cleanup (not reached with for(;;))
|
|
||||||
// zh_aht_deinit(&aht_handle);
|
|
||||||
// i2c_del_master_bus(bus_handle);
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Example: Multiple Sensors via I2C Multiplexer (zh_pca9548a)
|
|
||||||
|
|
||||||
```c
|
|
||||||
#include "zh_pca9548a.h"
|
|
||||||
#include "zh_aht.h"
|
|
||||||
|
|
||||||
static const int I2C_PORT = I2C_NUM_MAX - 1;
|
|
||||||
static const int SDA_IO = GPIO_NUM_21;
|
|
||||||
static const int SCL_IO = GPIO_NUM_22;
|
|
||||||
|
|
||||||
static zh_pca9548a_handle_t pca9548a_handle = {0};
|
|
||||||
static zh_aht_handle_t *aht_handle_chan_0 = NULL;
|
|
||||||
static zh_aht_handle_t *aht_handle_chan_1 = NULL;
|
|
||||||
static zh_aht_handle_t *aht_handle_chan_2 = NULL;
|
|
||||||
|
|
||||||
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 bus_config = {
|
|
||||||
.clk_source = I2C_CLK_SRC_DEFAULT,
|
|
||||||
.i2c_port = I2C_PORT,
|
|
||||||
.scl_io_num = SCL_IO,
|
|
||||||
.sda_io_num = SDA_IO,
|
|
||||||
.glitch_ignore_cnt = 7,
|
|
||||||
.flags.enable_internal_pullup = true,
|
|
||||||
};
|
|
||||||
i2c_master_bus_handle_t bus_handle = NULL;
|
|
||||||
ESP_ERROR_CHECK(i2c_new_master_bus(&bus_config, &bus_handle));
|
|
||||||
|
|
||||||
// Initialize I2C multiplexer
|
|
||||||
zh_pca9548a_init_config_t pca_config = ZH_PCA9548A_INIT_CONFIG_DEFAULT();
|
|
||||||
pca_config.i2c_handle = bus_handle;
|
|
||||||
pca_config.i2c_address = 0x70;
|
|
||||||
ESP_ERROR_CHECK(zh_pca9548a_init(&pca_config, &pca9548a_handle));
|
|
||||||
|
|
||||||
// Initialize AHT sensors on different channels
|
|
||||||
zh_aht_init_config_t aht_config = ZH_AHT_INIT_CONFIG_DEFAULT();
|
|
||||||
aht_config.i2c_handle = bus_handle;
|
|
||||||
|
|
||||||
zh_pca9548a_set(&pca9548a_handle, ZH_PCA9548A_CHAN_NUM_0);
|
|
||||||
ESP_ERROR_CHECK(zh_aht_init(&aht_config, &aht_handle_chan_0));
|
|
||||||
|
|
||||||
zh_pca9548a_set(&pca9548a_handle, ZH_PCA9548A_CHAN_NUM_1);
|
|
||||||
ESP_ERROR_CHECK(zh_aht_init(&aht_config, &aht_handle_chan_1));
|
|
||||||
|
|
||||||
zh_pca9548a_set(&pca9548a_handle, ZH_PCA9548A_CHAN_NUM_2);
|
|
||||||
ESP_ERROR_CHECK(zh_aht_init(&aht_config, &aht_handle_chan_2));
|
|
||||||
|
|
||||||
float humidity = 0.0f;
|
|
||||||
float temperature = 0.0f;
|
|
||||||
|
|
||||||
for (;;)
|
|
||||||
{
|
|
||||||
// Read sensor on channel 0
|
|
||||||
zh_pca9548a_set(&pca9548a_handle, ZH_PCA9548A_CHAN_NUM_0);
|
|
||||||
zh_aht_read(&aht_handle_chan_0, &humidity, &temperature);
|
|
||||||
printf("Sensor 1. Humidity: %.2f%%\n", humidity);
|
|
||||||
printf("Sensor 1. Temperature: %.2f°C\n", temperature);
|
|
||||||
|
|
||||||
// Read sensor on channel 1
|
|
||||||
zh_pca9548a_set(&pca9548a_handle, ZH_PCA9548A_CHAN_NUM_1);
|
|
||||||
zh_aht_read(&aht_handle_chan_1, &humidity, &temperature);
|
|
||||||
printf("Sensor 2. Humidity: %.2f%%\n", humidity);
|
|
||||||
printf("Sensor 2. Temperature: %.2f°C\n", temperature);
|
|
||||||
|
|
||||||
// Read sensor on channel 2
|
|
||||||
zh_pca9548a_set(&pca9548a_handle, ZH_PCA9548A_CHAN_NUM_2);
|
|
||||||
zh_aht_read(&aht_handle_chan_2, &humidity, &temperature);
|
|
||||||
printf("Sensor 3. Humidity: %.2f%%\n", humidity);
|
|
||||||
printf("Sensor 3. Temperature: %.2f°C\n", temperature);
|
|
||||||
|
|
||||||
vTaskDelay(5000 / portTICK_PERIOD_MS);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Technical Specifications
|
|
||||||
|
|
||||||
| Parameter | Value |
|
|
||||||
| ----------- | ------- |
|
|
||||||
| **Humidity Range** | 0 - 100% |
|
|
||||||
| **Temperature Range** | -40 - +85°C. |
|
|
||||||
| **Humidity Resolution** | 0.01% |
|
|
||||||
| **Temperature Resolution** | 0.01°C |
|
|
||||||
| **I2C Address** | 0x38, 0x39, 0x44 |
|
|
||||||
| **I2C Frequency** | Up to 400 kHz |
|
|
||||||
| **Measurement Time** | ~80 ms |
|
|
||||||
| **Supported Models** | AHT10, AHT15, AHT20, AHT21, AHT25, AHT30, AHT40 |
|
|
||||||
| **ESP-IDF Version** | >= 5.0 |
|
|
||||||
| **Platform** | ESP32 series |
|
|
||||||
| **Language** | C (C99) |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Error Codes
|
|
||||||
|
|
||||||
| Error Code | Description |
|
|
||||||
| ------------ | ------------- |
|
|
||||||
| `ESP_OK` | Operation successful |
|
|
||||||
| `ESP_ERR_INVALID_ARG` | Invalid argument (NULL pointer or invalid configuration) |
|
|
||||||
| `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) |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Contributing
|
|
||||||
|
|
||||||
Contributions are welcome! To contribute:
|
|
||||||
|
|
||||||
1. Fork the repository
|
|
||||||
2. Create a feature branch (`git checkout -b feature/AmazingFeature`)
|
|
||||||
3. Commit your changes (`git commit -m 'Add some AmazingFeature'`)
|
|
||||||
4. Push to the branch (`git push origin feature/AmazingFeature`)
|
|
||||||
5. Open a Pull Request
|
|
||||||
|
|
||||||
Please ensure your code follows the existing style and includes appropriate documentation.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## License
|
|
||||||
|
|
||||||
This project is licensed under the Apache License, Version 2.0 - see the [LICENSE](LICENSE) file for details.
|
|
||||||
|
|
||||||
### Apache License, Version 2.0
|
|
||||||
|
|
||||||
Copyright (c) 2026 Alexey Zholtikov
|
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
you may not use this file except in compliance with the License.
|
|
||||||
You may obtain a copy of the License at
|
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software
|
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
See the License for the specific language governing permissions and
|
|
||||||
limitations under the License.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Additional Notes
|
|
||||||
|
|
||||||
- **I2C Pull-up Resistors**: Ensure proper pull-up resistors are connected to SDA and SCL lines
|
|
||||||
- **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 v4.0.0*
|
|
||||||
-441
@@ -1,441 +0,0 @@
|
|||||||
# zh_aht — Компонент датчика влажности и температуры AHT для ESP-IDF
|
|
||||||
|
|
||||||
## Содержание
|
|
||||||
|
|
||||||
- [Обзор](#обзор)
|
|
||||||
- [Возможности](#возможности)
|
|
||||||
- [Установка](#установка)
|
|
||||||
- [Справочник API](#справочник-api)
|
|
||||||
- [Примеры использования](#примеры-использования)
|
|
||||||
- [Технические характеристики](#технические-характеристики)
|
|
||||||
- [Коды ошибок](#коды-ошибок)
|
|
||||||
- [Вклад в проект](#вклад-в-проект)
|
|
||||||
- [Лицензия](#лицензия)
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Обзор
|
|
||||||
|
|
||||||
`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+.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Возможности
|
|
||||||
|
|
||||||
1. **Измерение влажности и температуры**: Считывание значений влажности от 0 до 100% и температуры от -40 до +85°C
|
|
||||||
2. **Интерфейс I2C**: Использует стандартный протокол I2C (макс. частота 400 кГц)
|
|
||||||
3. **Настраиваемый адрес I2C**: Поддерживает адреса 0x38, 0x39 и 0x44
|
|
||||||
4. **Поддержка нескольких моделей**: AHT10, AHT15, AHT20, AHT21, AHT25, AHT30, AHT40
|
|
||||||
5. **Статистика ошибок**: Встроенный счетчик ошибок драйвера I2C
|
|
||||||
6. **Низкое энергопотребление**: Работает с системой управления питанием ESP-IDF
|
|
||||||
7. **Потокобезопасность**: Использует драйвер I2C ESP-IDF (потокобезопасен)
|
|
||||||
8. **Минимальные накладные расходы**: Низкие накладные расходы на память и процессор
|
|
||||||
9. **Поддержка нескольких датчиков**: Совместим с I2C мультиплексатором (zh_pca9548a)
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Установка
|
|
||||||
|
|
||||||
Перейдите в каталог компонентов вашего проекта:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
cd ../ваш_проект/components
|
|
||||||
```
|
|
||||||
|
|
||||||
Клонируйте репозиторий:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
git clone https://github.com/aZholtikov/zh_aht.git
|
|
||||||
```
|
|
||||||
|
|
||||||
В вашем приложении подключите заголовочный файл:
|
|
||||||
|
|
||||||
```c
|
|
||||||
#include "zh_aht.h"
|
|
||||||
```
|
|
||||||
|
|
||||||
Компонент будет автоматически собран вместе с вашим проектом.
|
|
||||||
|
|
||||||
### Обязательные настройки в menuconfig
|
|
||||||
|
|
||||||
Для корректной работы компонента включите следующие настройки в menuconfig:
|
|
||||||
|
|
||||||
```text
|
|
||||||
I2C_ISR_IRAM_SAFE
|
|
||||||
I2C_MASTER_ISR_HANDLER_IN_IRAM
|
|
||||||
```
|
|
||||||
|
|
||||||
### Опционально: Использование с I2C мультиплексатором (zh_pca9548a)
|
|
||||||
|
|
||||||
Для использования нескольких датчиков AHT на одной шине I2C, также установите компонент [zh_pca9548a](https://github.com/aZholtikov/zh_pca9548a).
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Справочник API
|
|
||||||
|
|
||||||
Все функции в этой библиотеке используют двойной указатель (`zh_aht_handle_t **`) для дескриптора датчика, чтобы обеспечить правильное управление памятью и потокобезопасность.
|
|
||||||
|
|
||||||
### Структура zh_aht_init_config_t
|
|
||||||
|
|
||||||
**Описание:**
|
|
||||||
|
|
||||||
Конфигурация для инициализации датчика AHT (шина I2C, адрес, частота).
|
|
||||||
|
|
||||||
**Поля (внутренние):**
|
|
||||||
|
|
||||||
| Поле | Тип | Description / Описание |
|
|
||||||
| ------ | ----- | ------------------------ |
|
|
||||||
| `i2c_handle` | `i2c_master_bus_handle_t` | Уникальный дескриптор шины I2C. |
|
|
||||||
| `i2c_address` | `uint8_t` | Адрес устройства датчика I2C (0x38, 0x39 или 0x44). |
|
|
||||||
| `i2c_frequency` | `uint32_t` | Частота I2C датчика (макс. 400000 Гц). |
|
|
||||||
|
|
||||||
Используйте макрос `ZH_AHT_INIT_CONFIG_DEFAULT()` для инициализации значениями по умолчанию:
|
|
||||||
|
|
||||||
- `i2c_frequency`: 400000 Гц
|
|
||||||
- `i2c_address`: 0x38
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Структура zh_aht_handle_t
|
|
||||||
|
|
||||||
Структура объявляется как `typedef struct _zh_aht_handle_t zh_aht_handle_t;` и инкапсулирует детали внутренней реализации.
|
|
||||||
|
|
||||||
**Поля (внутренние):**
|
|
||||||
|
|
||||||
| Поле | Тип | Description / Описание |
|
|
||||||
|------|-----|------------------------|
|
|
||||||
| `dev_handle` | `i2c_master_dev_handle_t` | Указатель на дескриптор устройства I2C. |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Структура zh_aht_stats_t
|
|
||||||
|
|
||||||
**Описание:**
|
|
||||||
|
|
||||||
Структура статистики ошибок датчика.
|
|
||||||
|
|
||||||
**Поля (внутренние):**
|
|
||||||
|
|
||||||
| Поле | Тип | Description / Описание |
|
|
||||||
|------|-----|------------------------|
|
|
||||||
| `i2c_driver_error` | `uint32_t` | Количество ошибок драйвера I2C. |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### zh_aht_init()
|
|
||||||
|
|
||||||
Инициализирует датчик AHT.
|
|
||||||
|
|
||||||
**Параметры:**
|
|
||||||
|
|
||||||
- `config` - Указатель на структуру конфигурации инициализации AHT. Не должен быть NULL
|
|
||||||
- `handle` - Двойной указатель на уникальный дескриптор AHT (`zh_aht_handle_t **`). Должен быть NULL
|
|
||||||
|
|
||||||
**Возвращает:**
|
|
||||||
|
|
||||||
- `ESP_OK` - Успех
|
|
||||||
- `ESP_ERR_INVALID_ARG` - Неверный аргумент (NULL config или handle)
|
|
||||||
- `ESP_ERR_INVALID_STATE` - Датчик уже инициализирован
|
|
||||||
- `ESP_ERR_NO_MEM` - Ошибка выделения памяти
|
|
||||||
- `ESP_FAIL` - Ошибка инициализации устройства I2C
|
|
||||||
- `ESP_ERR_NOT_FOUND` - Датчик не отвечает
|
|
||||||
|
|
||||||
**Пример:**
|
|
||||||
|
|
||||||
```c
|
|
||||||
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);
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### zh_aht_deinit()
|
|
||||||
|
|
||||||
Деинициализирует датчик AHT и удаляет его из шины I2C. Память для handle автоматически освобождается, а указатель устанавливается в NULL.
|
|
||||||
|
|
||||||
**Параметры:**
|
|
||||||
|
|
||||||
- `handle` - Двойной указатель на уникальный дескриптор AHT (`zh_aht_handle_t **`). Не должен быть NULL
|
|
||||||
|
|
||||||
**Возвращает:**
|
|
||||||
|
|
||||||
- `ESP_OK` - Успех
|
|
||||||
- `ESP_ERR_INVALID_ARG` - Неверный аргумент (NULL handle)
|
|
||||||
- `ESP_FAIL` - Ошибка удаления устройства I2C
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### zh_aht_read()
|
|
||||||
|
|
||||||
Считывает значения влажности и температуры с датчика.
|
|
||||||
|
|
||||||
**Параметры:**
|
|
||||||
|
|
||||||
- `handle` - Двойной указатель на уникальный дескриптор AHT (`zh_aht_handle_t **`). Не должен быть NULL
|
|
||||||
- `humidity` - Указатель для хранения значения влажности (в %). Не должен быть NULL
|
|
||||||
- `temperature` - Указатель для хранения значения температуры (в °C). Не должен быть NULL
|
|
||||||
|
|
||||||
**Возвращает:**
|
|
||||||
|
|
||||||
- `ESP_OK` - Успех
|
|
||||||
- `ESP_ERR_INVALID_ARG` - Неверный аргумент (NULL handle, humidity или temperature)
|
|
||||||
- `ESP_FAIL` - Ошибка связи I2C
|
|
||||||
- `ESP_ERR_TIMEOUT` - Превышено время ожидания готовности данных
|
|
||||||
- `ESP_ERR_INVALID_CRC` - Неверная CRC контрольная сумма
|
|
||||||
|
|
||||||
**Примечание:** Функция выполняет измерение и ожидает ~80 мс готовности данных.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### zh_aht_reset()
|
|
||||||
|
|
||||||
Сбрасывает датчик AHT.
|
|
||||||
|
|
||||||
**Параметры:**
|
|
||||||
|
|
||||||
- `handle` - Двойной указатель на уникальный дескриптор AHT (`zh_aht_handle_t **`). Не должен быть NULL.
|
|
||||||
|
|
||||||
**Возвращает:**
|
|
||||||
|
|
||||||
- `ESP_OK` - Успех
|
|
||||||
- `ESP_ERR_INVALID_ARG` - Неверный аргумент (NULL handle)
|
|
||||||
- `ESP_FAIL` - Ошибка связи I2C
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### zh_aht_get_stats()
|
|
||||||
|
|
||||||
Получает статистику ошибок с момента последнего сброса.
|
|
||||||
|
|
||||||
**Возвращает:**
|
|
||||||
|
|
||||||
- Указатель на константную структуру `zh_aht_stats_t` (действителен до вызова `zh_aht_reset_stats()`)
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### zh_aht_reset_stats()
|
|
||||||
|
|
||||||
Сбрасывает все счётчики статистики ошибок, устанавливая их в ноль.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Usage Examples / Примеры использования
|
|
||||||
|
|
||||||
### Пример: Один датчик
|
|
||||||
|
|
||||||
```c
|
|
||||||
#include "zh_aht.h"
|
|
||||||
|
|
||||||
static const int I2C_PORT = I2C_NUM_MAX - 1;
|
|
||||||
static const int SDA_IO = GPIO_NUM_21;
|
|
||||||
static const int SCL_IO = GPIO_NUM_22;
|
|
||||||
|
|
||||||
static zh_aht_handle_t *aht_handle = NULL;
|
|
||||||
|
|
||||||
void app_main(void)
|
|
||||||
{
|
|
||||||
esp_log_level_set("zh_aht", ESP_LOG_ERROR);
|
|
||||||
|
|
||||||
i2c_master_bus_config_t bus_config = {
|
|
||||||
.clk_source = I2C_CLK_SRC_DEFAULT,
|
|
||||||
.i2c_port = I2C_PORT,
|
|
||||||
.scl_io_num = SCL_IO,
|
|
||||||
.sda_io_num = SDA_IO,
|
|
||||||
.glitch_ignore_cnt = 7,
|
|
||||||
.flags.enable_internal_pullup = true,
|
|
||||||
};
|
|
||||||
i2c_master_bus_handle_t bus_handle = NULL;
|
|
||||||
ESP_ERROR_CHECK(i2c_new_master_bus(&bus_config, &bus_handle));
|
|
||||||
|
|
||||||
zh_aht_init_config_t config = ZH_AHT_INIT_CONFIG_DEFAULT();
|
|
||||||
config.i2c_handle = bus_handle;
|
|
||||||
ESP_ERROR_CHECK(zh_aht_init(&config, &aht_handle));
|
|
||||||
|
|
||||||
float humidity = 0.0f;
|
|
||||||
float temperature = 0.0f;
|
|
||||||
|
|
||||||
for (;;)
|
|
||||||
{
|
|
||||||
esp_err_t err = zh_aht_read(&aht_handle, &humidity, &temperature);
|
|
||||||
if (err == ESP_OK)
|
|
||||||
{
|
|
||||||
printf("Влажность: %.2f%%\n", humidity);
|
|
||||||
printf("Температура: %.2f°C\n", temperature);
|
|
||||||
const zh_aht_stats_t *stats = zh_aht_get_stats();
|
|
||||||
printf("Ошибки I2C: %lu\n", stats->i2c_driver_error);
|
|
||||||
}
|
|
||||||
vTaskDelay(5000 / portTICK_PERIOD_MS);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Очистка ресурсов (не достигается в примере с for(;;))
|
|
||||||
// zh_aht_deinit(&aht_handle);
|
|
||||||
// i2c_del_master_bus(bus_handle);
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Пример: Несколько датчиков через I2C мультиплексор (zh_pca9548a)
|
|
||||||
|
|
||||||
```c
|
|
||||||
#include "zh_pca9548a.h"
|
|
||||||
#include "zh_aht.h"
|
|
||||||
|
|
||||||
static const int I2C_PORT = I2C_NUM_MAX - 1;
|
|
||||||
static const int SDA_IO = GPIO_NUM_21;
|
|
||||||
static const int SCL_IO = GPIO_NUM_22;
|
|
||||||
|
|
||||||
static zh_pca9548a_handle_t pca9548a_handle = {0};
|
|
||||||
static zh_aht_handle_t *aht_handle_chan_0 = NULL;
|
|
||||||
static zh_aht_handle_t *aht_handle_chan_1 = NULL;
|
|
||||||
static zh_aht_handle_t *aht_handle_chan_2 = NULL;
|
|
||||||
|
|
||||||
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 bus_config = {
|
|
||||||
.clk_source = I2C_CLK_SRC_DEFAULT,
|
|
||||||
.i2c_port = I2C_PORT,
|
|
||||||
.scl_io_num = SCL_IO,
|
|
||||||
.sda_io_num = SDA_IO,
|
|
||||||
.glitch_ignore_cnt = 7,
|
|
||||||
.flags.enable_internal_pullup = true,
|
|
||||||
};
|
|
||||||
i2c_master_bus_handle_t bus_handle = NULL;
|
|
||||||
ESP_ERROR_CHECK(i2c_new_master_bus(&bus_config, &bus_handle));
|
|
||||||
|
|
||||||
// Инициализация I2C мультиплексора
|
|
||||||
zh_pca9548a_init_config_t pca_config = ZH_PCA9548A_INIT_CONFIG_DEFAULT();
|
|
||||||
pca_config.i2c_handle = bus_handle;
|
|
||||||
pca_config.i2c_address = 0x70;
|
|
||||||
ESP_ERROR_CHECK(zh_pca9548a_init(&pca_config, &pca9548a_handle));
|
|
||||||
|
|
||||||
// Инициализация датчиков AHT на разных каналах
|
|
||||||
zh_aht_init_config_t aht_config = ZH_AHT_INIT_CONFIG_DEFAULT();
|
|
||||||
aht_config.i2c_handle = bus_handle;
|
|
||||||
|
|
||||||
zh_pca9548a_set(&pca9548a_handle, ZH_PCA9548A_CHAN_NUM_0);
|
|
||||||
ESP_ERROR_CHECK(zh_aht_init(&aht_config, &aht_handle_chan_0));
|
|
||||||
|
|
||||||
zh_pca9548a_set(&pca9548a_handle, ZH_PCA9548A_CHAN_NUM_1);
|
|
||||||
ESP_ERROR_CHECK(zh_aht_init(&aht_config, &aht_handle_chan_1));
|
|
||||||
|
|
||||||
zh_pca9548a_set(&pca9548a_handle, ZH_PCA9548A_CHAN_NUM_2);
|
|
||||||
ESP_ERROR_CHECK(zh_aht_init(&aht_config, &aht_handle_chan_2));
|
|
||||||
|
|
||||||
float humidity = 0.0f;
|
|
||||||
float temperature = 0.0f;
|
|
||||||
|
|
||||||
for (;;)
|
|
||||||
{
|
|
||||||
// Считывание с датчика на канале 0
|
|
||||||
zh_pca9548a_set(&pca9548a_handle, ZH_PCA9548A_CHAN_NUM_0);
|
|
||||||
zh_aht_read(&aht_handle_chan_0, &humidity, &temperature);
|
|
||||||
printf("Датчик 1. Влажность: %.2f%%\n", humidity);
|
|
||||||
printf("Датчик 1. Температура: %.2f°C\n", temperature);
|
|
||||||
|
|
||||||
// Считывание с датчика на канале 1
|
|
||||||
zh_pca9548a_set(&pca9548a_handle, ZH_PCA9548A_CHAN_NUM_1);
|
|
||||||
zh_aht_read(&aht_handle_chan_1, &humidity, &temperature);
|
|
||||||
printf("Датчик 2. Влажность: %.2f%%\n", humidity);
|
|
||||||
printf("Датчик 2. Температура: %.2f°C\n", temperature);
|
|
||||||
|
|
||||||
// Считывание с датчика на канале 2
|
|
||||||
zh_pca9548a_set(&pca9548a_handle, ZH_PCA9548A_CHAN_NUM_2);
|
|
||||||
zh_aht_read(&aht_handle_chan_2, &humidity, &temperature);
|
|
||||||
printf("Датчик 3. Влажность: %.2f%%\n", humidity);
|
|
||||||
printf("Датчик 3. Температура: %.2f°C\n", temperature);
|
|
||||||
|
|
||||||
vTaskDelay(5000 / portTICK_PERIOD_MS);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Технические характеристики
|
|
||||||
|
|
||||||
| Параметр | Значение |
|
|
||||||
| ---------- | ---------- |
|
|
||||||
| **Диапазон влажности** | 0 - 100% |
|
|
||||||
| **Диапазон температуры** | -40 - +85°C |
|
|
||||||
| **Разрешение измерения влажности** | 0.01% |
|
|
||||||
| **Разрешение измерения температуры** | 0.01°C |
|
|
||||||
| **Адрес I2C** | 0x38, 0x39, 0x44 |
|
|
||||||
| **Частота I2C** | До 400 кГц |
|
|
||||||
| **Время измерения** | ~80 мс |
|
|
||||||
| **Поддерживаемые модели** | AHT10, AHT15, AHT20, AHT21, AHT25, AHT30, AHT40 |
|
|
||||||
| **Версия ESP-IDF** | >= 5.0 |
|
|
||||||
| **Платформа** | Семейство ESP32 |
|
|
||||||
| **Язык** | C (C99) |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Коды ошибок
|
|
||||||
|
|
||||||
| Код ошибки | Описание |
|
|
||||||
| ------------ | ---------- |
|
|
||||||
| `ESP_OK` | Операция выполнена успешно |
|
|
||||||
| `ESP_ERR_INVALID_ARG` | Неверный аргумент (NULL указатель или неверная конфигурация) |
|
|
||||||
| `ESP_ERR_INVALID_STATE` | Датчик уже инициализирован |
|
|
||||||
| `ESP_ERR_NO_MEM` | Ошибка выделения памяти |
|
|
||||||
| `ESP_ERR_NOT_FOUND` | Датчик не отвечает |
|
|
||||||
| `ESP_ERR_TIMEOUT` | Превышено время ожидания готовности данных |
|
|
||||||
| `ESP_ERR_INVALID_CRC` | Неверная CRC контрольная сумма |
|
|
||||||
| `ESP_FAIL` | Общая ошибка (ошибка связи I2C) |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Вклад в проект
|
|
||||||
|
|
||||||
Вклад приветствуется! Чтобы внести свой вклад:
|
|
||||||
|
|
||||||
1. Сделайте форк репозитория
|
|
||||||
2. Создайте ветку функции (`git checkout -b feature/AmazingFeature`)
|
|
||||||
3. Закоммитьте ваши изменения (`git commit -m 'Add some AmazingFeature'`)
|
|
||||||
4. Отправьте в ветку (`git push origin feature/AmazingFeature`)
|
|
||||||
5. Откройте Pull Request
|
|
||||||
|
|
||||||
Пожалуйста, убедитесь, что ваш код следует существующему стилю и включает соответствующую документацию.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Лицензия
|
|
||||||
|
|
||||||
Этот проект лицензирован по лицензии Apache, версия 2.0 - см. файл [LICENSE](LICENSE) для подробной информации.
|
|
||||||
|
|
||||||
### Apache License, Version 2.0
|
|
||||||
|
|
||||||
Авторское право (c) 2026 Алексей Жолтиков
|
|
||||||
|
|
||||||
Лицензировано по лицензии Apache License, Version 2.0 (далее — "Лицензия");
|
|
||||||
вы не можете использовать этот файл, кроме случаев, предусмотренных Лицензией.
|
|
||||||
Копию Лицензии можно получить по адресу:
|
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
|
|
||||||
Если иное не требуется действующим законодательством или не согласовано в письменном виде,
|
|
||||||
программное обеспечение, распространяемое по Лицензии, распространяется на условиях "КАК ЕСТЬ",
|
|
||||||
БЕЗ КАКИХ-ЛИБО ГАРАНТИЙ, явных или подразумеваемых, включая, но не ограничиваясь, гарантии
|
|
||||||
ТОВАРНОГО СОСТОЯНИЯ, ПРИГОДНОСТИ ДЛЯ КОНКРЕТНОЙ ЦЕЛИ И НЕНАРУШЕНИЯ ПРАВ.
|
|
||||||
Смотрите Лицензию для получения конкретных прав и ограничений.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Дополнительные заметки
|
|
||||||
|
|
||||||
- **Подтягивающие резисторы I2C**: Убедитесь, что к линиям SDA и SCL подключены соответствующие подтягивающие резисторы
|
|
||||||
- **Время измерения**: Датчик требует ~80 мс для каждого измерения
|
|
||||||
- **I2C_ISR_IRAM_SAFE**: Для правильной работы включите `I2C_ISR_IRAM_SAFE` и `I2C_MASTER_ISR_HANDLER_IN_IRAM` в menuconfig
|
|
||||||
- **Потокобезопасность**: Компонент использует драйвер I2C ESP-IDF, который является потокобезопасным
|
|
||||||
- **Управление памятью**: Все функции API теперь используют двойные указатели для handle, что позволяет автоматически управлять памятью (освобождение при деинициализации, установка в NULL)
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
*Сгенерировано для zh_aht v4.0.0*
|
|
||||||
+13
-13
@@ -1,7 +1,7 @@
|
|||||||
/**
|
/**
|
||||||
* @file zh_aht.h
|
* @file zh_aht.h
|
||||||
*
|
*
|
||||||
* @brief Driver for AHT series temperature and humidity sensors (AHT10/AHT15/AHT20/AHT21/AHT25/AHT30/AHT40)
|
* @brief Driver for AHT series temperature and humidity sensors (AHT10/AHT15/AHT20/AHT21/AHT25/AHT30/AHT40).
|
||||||
*
|
*
|
||||||
* Provides I2C-based communication with AHT sensors including initialization,
|
* Provides I2C-based communication with AHT sensors including initialization,
|
||||||
* data reading with CRC validation, reset functionality, and error statistics.
|
* data reading with CRC validation, reset functionality, and error statistics.
|
||||||
@@ -11,9 +11,6 @@
|
|||||||
* - Automatic sensor initialization and probe detection
|
* - Automatic sensor initialization and probe detection
|
||||||
* - CRC8 data integrity verification
|
* - CRC8 data integrity verification
|
||||||
* - Error statistics tracking
|
* - Error statistics tracking
|
||||||
*
|
|
||||||
* @note Requires ESP-IDF v5.0+ with I2C master driver
|
|
||||||
* @note Enable I2C_ISR_IRAM_SAFE and I2C_MASTER_ISR_HANDLER_IN_IRAM in menuconfig
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
@@ -22,6 +19,8 @@
|
|||||||
#include "esp_log.h"
|
#include "esp_log.h"
|
||||||
#include "driver/i2c_master.h"
|
#include "driver/i2c_master.h"
|
||||||
#include "freertos/FreeRTOS.h"
|
#include "freertos/FreeRTOS.h"
|
||||||
|
#include "freertos/task.h"
|
||||||
|
#include "esp_heap_caps.h"
|
||||||
|
|
||||||
#define ZH_AHT_INIT_CONFIG_DEFAULT() \
|
#define ZH_AHT_INIT_CONFIG_DEFAULT() \
|
||||||
{ \
|
{ \
|
||||||
@@ -37,12 +36,12 @@ extern "C"
|
|||||||
* @brief Opaque handle for AHT sensor driver.
|
* @brief Opaque handle for AHT sensor driver.
|
||||||
*
|
*
|
||||||
* Incomplete type that encapsulates internal sensor state.
|
* Incomplete type that encapsulates internal sensor state.
|
||||||
* Users interact with this handle exclusively through the public API functions.c
|
* Users interact with this handle exclusively through the public API functions.
|
||||||
*/
|
*/
|
||||||
typedef struct _zh_aht_handle_t zh_aht_handle_t;
|
typedef struct _zh_aht_handle_t zh_aht_handle_t;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Configuration structure for AHT sensor initialization
|
* @brief Configuration structure for AHT sensor initialization.
|
||||||
*/
|
*/
|
||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
@@ -52,7 +51,7 @@ extern "C"
|
|||||||
} zh_aht_init_config_t;
|
} zh_aht_init_config_t;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Structure containing sensor error statistics
|
* @brief Structure containing sensor error statistics.
|
||||||
*/
|
*/
|
||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
@@ -60,7 +59,7 @@ extern "C"
|
|||||||
} zh_aht_stats_t;
|
} zh_aht_stats_t;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Initialize AHT sensor and establish I2C communication
|
* @brief Initialize AHT sensor and establish I2C communication.
|
||||||
*
|
*
|
||||||
* Allocates handle, configures I2C device, probes sensor presence,
|
* Allocates handle, configures I2C device, probes sensor presence,
|
||||||
* and sends initialization command if required.
|
* and sends initialization command if required.
|
||||||
@@ -77,7 +76,7 @@ extern "C"
|
|||||||
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 and release resources
|
* @brief Deinitialize AHT sensor and release resources.
|
||||||
*
|
*
|
||||||
* Removes I2C device and frees the sensor handle.
|
* Removes I2C device and frees the sensor handle.
|
||||||
*
|
*
|
||||||
@@ -85,11 +84,12 @@ extern "C"
|
|||||||
*
|
*
|
||||||
* @return ESP_OK on success
|
* @return ESP_OK on success
|
||||||
* @return ESP_ERR_INVALID_ARG if handle is NULL
|
* @return ESP_ERR_INVALID_ARG if handle is NULL
|
||||||
|
* @return ESP_FAIL on I2C communication errors
|
||||||
*/
|
*/
|
||||||
esp_err_t zh_aht_deinit(zh_aht_handle_t **handle);
|
esp_err_t zh_aht_deinit(zh_aht_handle_t **handle);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Read humidity and temperature from AHT sensor
|
* @brief Read humidity and temperature from AHT sensor.
|
||||||
*
|
*
|
||||||
* Sends read command, receives 7-byte response with CRC validation,
|
* Sends read command, receives 7-byte response with CRC validation,
|
||||||
* and converts raw data to engineering units.
|
* and converts raw data to engineering units.
|
||||||
@@ -107,7 +107,7 @@ extern "C"
|
|||||||
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 via I2C command
|
* @brief Reset AHT sensor via I2C command.
|
||||||
*
|
*
|
||||||
* Sends reset command and waits for sensor recovery.
|
* Sends reset command and waits for sensor recovery.
|
||||||
*
|
*
|
||||||
@@ -120,7 +120,7 @@ extern "C"
|
|||||||
esp_err_t zh_aht_reset(zh_aht_handle_t **handle);
|
esp_err_t zh_aht_reset(zh_aht_handle_t **handle);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Get pointer to sensor error statistics
|
* @brief Get pointer to sensor error statistics.
|
||||||
*
|
*
|
||||||
* Returns read-only pointer to global statistics structure.
|
* Returns read-only pointer to global statistics structure.
|
||||||
*
|
*
|
||||||
@@ -129,7 +129,7 @@ extern "C"
|
|||||||
const zh_aht_stats_t *zh_aht_get_stats(void);
|
const zh_aht_stats_t *zh_aht_get_stats(void);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Reset all error statistics to zero
|
* @brief Reset all error statistics to zero.
|
||||||
*
|
*
|
||||||
* Clears all error counters tracked in the global statistics structure.
|
* Clears all error counters tracked in the global statistics structure.
|
||||||
*/
|
*/
|
||||||
|
|||||||
+283
@@ -0,0 +1,283 @@
|
|||||||
|
# Описание компонента zh_aht
|
||||||
|
|
||||||
|
## Общие сведения
|
||||||
|
|
||||||
|
**zh_aht** — это компонент для ESP-IDF, реализующий драйвер цифровых датчиков влажности и температуры серии AHT (AHT10/AHT15/AHT20/AHT21/AHT25/AHT30/AHT40) по интерфейсу I2C.
|
||||||
|
|
||||||
|
Компонент предоставляет высокоуровневый API для инициализации датчика, чтения влажности и температуры с проверкой целостности данных, а также для отслеживания статистики ошибок связи.
|
||||||
|
|
||||||
|
## Основные возможности
|
||||||
|
|
||||||
|
1. **Поддержка всей серии AHT** — один драйвер работает со всеми датчиками семейства без различий в модели.
|
||||||
|
2. **Новый драйвер I2C Master** — использует API `i2c_master` (driver/i2c_master.h) вместо устаревшего i2c_driver.
|
||||||
|
3. **Автоматическая инициализация датчика** — проба устройства и отправка команды инициализации, если датчик не откалиброван.
|
||||||
|
4. **Проверка целостности данных (CRC8)** — каждый кадр данных проверяется алгоритмом CRC8 с полиномом `0x31`.
|
||||||
|
5. **Валидация состояния датчика** — контроль битов калибровки и готовности в ответе датчика.
|
||||||
|
6. **Статистика ошибок** — глобальный счётчик ошибок драйвера I2C с возможностью сброса.
|
||||||
|
7. **Внешнее управление шиной I2C** — шина создаётся пользователем, что позволяет подключать несколько устройств на одну шину.
|
||||||
|
8. **Поддержка нескольких датчиков** — через внешние расширители (например, мультиплексор `zh_pca9548a`).
|
||||||
|
9. **Скрытая реализация (opaque handle)** — внутреннее состояние недоступно пользователю, работа только через публичный API.
|
||||||
|
|
||||||
|
## Внутреннее устройство
|
||||||
|
|
||||||
|
### Структура `zh_aht_handle_t`
|
||||||
|
|
||||||
|
Дескриптор датчика — непрозрачный тип (`struct _zh_aht_handle_t`), полный определитель которого скрыт в `zh_aht.c`. Пользователь оперирует только указателем на него.
|
||||||
|
|
||||||
|
| Поле | Тип | Описание |
|
||||||
|
|------|-----|----------|
|
||||||
|
| `dev_handle` | `i2c_master_dev_handle_t` | Дескриптор I2C-устройства, через который выполняется вся связь с датчиком |
|
||||||
|
|
||||||
|
### Структура `zh_aht_init_config_t` (публичная)
|
||||||
|
|
||||||
|
Конфигурация инициализации, заполняется пользователем. Для значений по умолчанию используется макрос `ZH_AHT_INIT_CONFIG_DEFAULT()` (частота `400000` Гц, адрес `0x38`), при этом поле `i2c_handle` заполняется обязательно.
|
||||||
|
|
||||||
|
| Поле | Тип | Описание |
|
||||||
|
| ------ | ----- | ---------- |
|
||||||
|
| `i2c_handle` | `i2c_master_bus_handle_t` | Дескриптор I2C-шины, созданный пользователем |
|
||||||
|
| `i2c_address` | `uint8_t` | Адрес устройства на шине: `0x38`, `0x39` или `0x44` |
|
||||||
|
| `i2c_frequency` | `uint32_t` | Тактовая частота SCL, от `1` до `400000` Гц |
|
||||||
|
|
||||||
|
### Структура `zh_aht_stats_t` (публичная)
|
||||||
|
|
||||||
|
Глобальная статистика ошибок, доступна только на чтение через `zh_aht_get_stats()`.
|
||||||
|
|
||||||
|
| Поле | Тип | Описание |
|
||||||
|
|------|-----|----------|
|
||||||
|
| `i2c_driver_error` | `uint32_t` | Счётчик ошибок драйвера I2C (transmit/receive/remove device) |
|
||||||
|
|
||||||
|
### Команды датчика
|
||||||
|
|
||||||
|
Вспомогательные константы протокола AHT, расположенные в `zh_aht.c`:
|
||||||
|
|
||||||
|
| Константа | Значение | Описание |
|
||||||
|
| ----------- | ---------- | ---------- |
|
||||||
|
| `_aht_reset_command` | `0xBA` | Команда программного сброса датчика |
|
||||||
|
| `_aht_read_command` | `0xAC 0x33 0x00` | Последовательность запуска измерения и чтения данных |
|
||||||
|
| `_aht_init_command` | `0xBE 0x08 0x00` | Команда инициализации (калибровки) для AHT20 и новее |
|
||||||
|
| `status_cmd` | `0x71` | Команда чтения байта статуса датчика |
|
||||||
|
|
||||||
|
### Управление памятью
|
||||||
|
|
||||||
|
- Дескриптор выделяется одной операцией `heap_caps_calloc(1, sizeof(zh_aht_handle_t), MALLOC_CAP_8BIT)` при инициализации, поэтому все поля гарантированно обнулены.
|
||||||
|
- Освобождение — только через `heap_caps_free()` внутри `zh_aht_deinit()`, после чего указатель в копии вызывающего кода обнуляется.
|
||||||
|
- При любой ошибке инициализации уже выделенная память освобождается в макросах проверки ошибок (`ZH_ERROR_CHECK` с блоком `cleanup`), утечки исключены.
|
||||||
|
- Статистика ошибок `_stats` — статическая глобальная переменная, живущая всё время работы программы.
|
||||||
|
|
||||||
|
### Макросы обработки ошибок
|
||||||
|
|
||||||
|
Внутренние макросы `ZH_ERROR_CHECK` и `ZH_ERROR_CHECK_CONT` инкапсулируют типовую схему «проверка условия → логирование с именем функции, строкой и кодом ошибки → очистка ресурсов → возврат ошибки». Это устраняет дублирование кода обработки ошибок во всех функциях компонента.
|
||||||
|
|
||||||
|
## API (интерфейс)
|
||||||
|
|
||||||
|
### Инициализация и освобождение
|
||||||
|
|
||||||
|
| Функция | Описание |
|
||||||
|
|---------|----------|
|
||||||
|
| `zh_aht_init()` | Инициализация датчика: валидация конфигурации, выделение дескриптора, регистрация устройства на I2C-шине, проба датчика и отправка команды инициализации при необходимости. Указатель на дескриптор должен быть `NULL`. |
|
||||||
|
| `zh_aht_deinit()` | Удаление устройства с I2C-шины и освобождение дескриптора. Входной указатель обнуляется. |
|
||||||
|
|
||||||
|
### Чтение данных
|
||||||
|
|
||||||
|
| Функция | Описание |
|
||||||
|
|---------|----------|
|
||||||
|
| `zh_aht_read()` | Запуск измерения (команда `0xAC`), приём 7-байтного ответа, проверка статуса, CRC и преобразование сырых байтов во влажность (0–100 %) и температуру (−50…+150 °C). |
|
||||||
|
|
||||||
|
### Сброс датчика
|
||||||
|
|
||||||
|
| Функция | Описание |
|
||||||
|
|---------|----------|
|
||||||
|
| `zh_aht_reset()` | Отправка команды сброса `0xBA`, проверка статуса по `0x71` и повторная инициализация датчика, если бит калибровки сброшен. |
|
||||||
|
|
||||||
|
### Получение информации
|
||||||
|
|
||||||
|
| Функция | Описание |
|
||||||
|
|---------|----------|
|
||||||
|
| `zh_aht_get_stats()` | Возвращает указатель на глобальную структуру статистики ошибок `zh_aht_stats_t` (только для чтения). |
|
||||||
|
|
||||||
|
### Управление статистикой
|
||||||
|
|
||||||
|
| Функция | Описание |
|
||||||
|
|---------|----------|
|
||||||
|
| `zh_aht_reset_stats()` | Сбрасывает все счётчики ошибок статистики в ноль. |
|
||||||
|
|
||||||
|
## Потокобезопасность
|
||||||
|
|
||||||
|
Компонент **не является потокобезопасным**: механизмы синхронизации (мьютексы, критические секции, атомарные операции) не используются.
|
||||||
|
|
||||||
|
1. **Отсутствие защиты дескриптора** — одновременный вызов API-функций с одним дескриптором из нескольких задач может привести к некорректному обмену по I2C.
|
||||||
|
2. **Глобальная статистика `_stats`** — инкремент счётчика `i2c_driver_error` не атомарен. На практике на одной шине I2C операции передаются последовательно драйвером шины, но при многозадачном доступе к одному датчику синхронизацию должен обеспечить пользователь.
|
||||||
|
|
||||||
|
## Алгоритмы
|
||||||
|
|
||||||
|
### Преобразование сырых данных в инженерные единицы
|
||||||
|
|
||||||
|
Датчик возвращает 20-битные значения влажности и температуры в 7-байтном кадре:
|
||||||
|
|
||||||
|
- **Влажность**: байты `[1][2][3]` конкатенируются в 24-битное слово, старшие 4 бита отбрасываются (`>> 4`), затем `raw / 1048576 * 100` — результат в процентах.
|
||||||
|
- **Температура**: байты `[3][4][5]` конкатенируются в 24-битное слово, маскируются младшие 20 бит (`& 0xFFFFF`), затем `raw / 1048576 * 200 - 50` — результат в °C.
|
||||||
|
- Байт `[3]` используется обоими значениями: его младшие 4 бита содержат старшие биты температуры.
|
||||||
|
|
||||||
|
### Проверка состояния датчика в кадре
|
||||||
|
|
||||||
|
- Бит `0x08` байта статуса `[0]` — признак откалиброванности датчика; если он сброшен, возвращается `ESP_ERR_INVALID_STATE`.
|
||||||
|
- Бит `0x80` байта статуса `[0]` — флаг занятости/таймаута измерения; если он установлен, возвращается `ESP_ERR_TIMEOUT`.
|
||||||
|
|
||||||
|
### Алгоритм CRC8
|
||||||
|
|
||||||
|
- Начальное значение — `0xFF`, полином — `0x31`, без инвертирования результата.
|
||||||
|
- Для каждого байта данных: XOR с `crc`, затем 8 итераций сдвига влево с условным XOR полинома при выходе единицы за старший бит.
|
||||||
|
- Проверятся первые 6 байт кадра, 7-й байт — эталонная CRC от датчика.
|
||||||
|
|
||||||
|
### Процедура инициализации I2C-подключения
|
||||||
|
|
||||||
|
1. Регистрация устройства на шине (`i2c_master_bus_add_device`) с 7-битным адресом и заданной частотой.
|
||||||
|
2. Задержка 100 мс, затем проба датчика (`i2c_master_probe`) — при отсутствии ответа устройство удаляется с шины и возвращается `ESP_ERR_NOT_FOUND`.
|
||||||
|
3. Чтение байта статуса командой `0x71`.
|
||||||
|
4. Если бит калибровки `0x08` сброшен (характерно для AHT10/AHT15), отправляется команда инициализации `0xBE 0x08 0x00` и выдерживается 20 мс.
|
||||||
|
|
||||||
|
## Примеры использования
|
||||||
|
|
||||||
|
### Базовый пример
|
||||||
|
|
||||||
|
```c
|
||||||
|
#include "zh_aht.h"
|
||||||
|
|
||||||
|
zh_aht_handle_t *aht_handle = NULL;
|
||||||
|
|
||||||
|
void app_main(void)
|
||||||
|
{
|
||||||
|
// Создаём I2C-шину (шину создаёт пользователь)
|
||||||
|
i2c_master_bus_config_t i2c_bus_config = {
|
||||||
|
.clk_source = I2C_CLK_SRC_DEFAULT,
|
||||||
|
.i2c_port = I2C_NUM_MAX - 1,
|
||||||
|
.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 (;;)
|
||||||
|
{
|
||||||
|
// Читаем влажность и температуру
|
||||||
|
if (zh_aht_read(&aht_handle, &humidity, &temperature) == ESP_OK)
|
||||||
|
{
|
||||||
|
printf("Humidity: %.2f%%, Temperature: %.2f°C\n", humidity, temperature);
|
||||||
|
}
|
||||||
|
vTaskDelay(5000 / portTICK_PERIOD_MS);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Освобождение ресурсов
|
||||||
|
zh_aht_deinit(&aht_handle);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Обработка ошибок и сброс датчика
|
||||||
|
|
||||||
|
```c
|
||||||
|
#include "zh_aht.h"
|
||||||
|
|
||||||
|
static void read_sensor(zh_aht_handle_t **handle)
|
||||||
|
{
|
||||||
|
float humidity = 0.0;
|
||||||
|
float temperature = 0.0;
|
||||||
|
esp_err_t err = zh_aht_read(handle, &humidity, &temperature);
|
||||||
|
|
||||||
|
switch (err)
|
||||||
|
{
|
||||||
|
case ESP_OK:
|
||||||
|
printf("H: %.2f%%, T: %.2f°C\n", humidity, temperature);
|
||||||
|
break;
|
||||||
|
case ESP_ERR_INVALID_CRC:
|
||||||
|
// Повреждение данных — пробуем сбросить датчик
|
||||||
|
zh_aht_reset(handle);
|
||||||
|
break;
|
||||||
|
case ESP_ERR_TIMEOUT:
|
||||||
|
// Датчик не завершил измерение — читаем позже
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
printf("Read error: %s\n", esp_err_to_name(err));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Мониторинг статистики ошибок
|
||||||
|
|
||||||
|
```c
|
||||||
|
#include "zh_aht.h"
|
||||||
|
|
||||||
|
void monitor_errors(zh_aht_handle_t **handle)
|
||||||
|
{
|
||||||
|
// Получаем глобальную статистику (указатель всегда валиден)
|
||||||
|
const zh_aht_stats_t *stats = zh_aht_get_stats();
|
||||||
|
printf("I2C errors: %lu\n", (unsigned long)stats->i2c_driver_error);
|
||||||
|
|
||||||
|
// Пороговая реакция на накопленные ошибки
|
||||||
|
if (stats->i2c_driver_error > 10)
|
||||||
|
{
|
||||||
|
zh_aht_reset(handle); // сбрасываем датчик
|
||||||
|
zh_aht_reset_stats(); // обнуляем счётчики
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Логирование
|
||||||
|
|
||||||
|
Компонент использует ESP-IDF логирование с тегом `"zh_aht"`. Сообщения `INFO` сопровождаются ключевые операции, сообщения `ERROR` содержат имя файла, строку и имя кода ошибки.
|
||||||
|
|
||||||
|
```c
|
||||||
|
#include "esp_log.h"
|
||||||
|
|
||||||
|
// Уменьшить verbosity компонента до уровня ошибок
|
||||||
|
esp_log_level_set("zh_aht", ESP_LOG_ERROR);
|
||||||
|
```
|
||||||
|
|
||||||
|
## Ошибки
|
||||||
|
|
||||||
|
| Код | Описание |
|
||||||
|
| ----- | ---------- |
|
||||||
|
| `ESP_OK` | Операция выполнена успешно |
|
||||||
|
| `ESP_ERR_INVALID_ARG` | Неверный аргумент: `NULL`-указатель, недопустимый адрес/частота I2C или конфигурация |
|
||||||
|
| `ESP_ERR_INVALID_STATE` | Дескриптор уже инициализирован, либо датчик не откалиброван (бит `0x08` статуса) |
|
||||||
|
| `ESP_ERR_NO_MEM` | Не удалось выделить память под дескриптор |
|
||||||
|
| `ESP_ERR_NOT_FOUND` | Датчик не обнаружен на шине (проба не ответила) |
|
||||||
|
| `ESP_ERR_TIMEOUT` | Датчик не завершил измерение (флаг занятости в байте статуса) |
|
||||||
|
| `ESP_ERR_INVALID_CRC` | Проверка CRC8 кадра данных не прошла |
|
||||||
|
| `ESP_FAIL` | Ошибка обмена по I2C (transmit/receive/remove device) |
|
||||||
|
|
||||||
|
## Ограничения
|
||||||
|
|
||||||
|
1. **Потокобезопасность отсутствует** — компонент не использует мьютексы; один дескриптор должен обслуживаться одной задачей либо синхронизироваться пользователем.
|
||||||
|
2. **Допустимые адреса I2C жёстко заданы** — валидация принимает только `0x38`, `0x39`, `0x44`, так как это аппаратные адреса серии AHT.
|
||||||
|
3. **Частота I2C не выше 400 кГц** — датчики серии AHT не поддерживают Fast-mode Plus.
|
||||||
|
4. **Шина I2C создаётся пользователем** — компонент не управляет шиной, `i2c_handle` в конфигурации обязателен; без него `zh_aht_init()` вернёт `ESP_ERR_INVALID_ARG`.
|
||||||
|
5. **Статистика глобальна** — `_stats` общая для всех экземпляров датчиков: невозможно определить, какой именно датчик вызвал ошибку I2C.
|
||||||
|
6. **Блокирующие задержки** — `zh_aht_read()` содержит `vTaskDelay(80 мс)` на время измерения, `zh_aht_reset()` — до 30 мс; функции нельзя вызывать из контекста, где недопусто переключение задач.
|
||||||
|
7. **Необходим откалиброванный датчик** — если после инициализации и сброса бит калибровки `0x08` остаётся сброшенным, `zh_aht_read()` будет стабильно возвращать `ESP_ERR_INVALID_STATE`.
|
||||||
|
|
||||||
|
## Требования для сборки
|
||||||
|
|
||||||
|
- ESP-IDF версии `>=5.0` (используется новый API `driver/i2c_master.h`).
|
||||||
|
- Компонент `driver` (ESP-IDF 5.x) либо `esp_driver_i2c` (ESP-IDF 6.x) — выбирается автоматически в `CMakeLists.txt` по версии фреймворка.
|
||||||
|
- FreeRTOS (входит в состав ESP-IDF) — используется `vTaskDelay`.
|
||||||
|
|
||||||
|
## Установка
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd ваш_проект/components
|
||||||
|
git clone https://github.com/aZholtikov/zh_aht
|
||||||
|
```
|
||||||
|
|
||||||
|
```c
|
||||||
|
#include "zh_aht.h"
|
||||||
|
```
|
||||||
@@ -13,6 +13,13 @@ static const char *TAG = "zh_aht";
|
|||||||
return err; \
|
return err; \
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define ZH_ERROR_CHECK_CONT(cond, cleanup, msg, ...) \
|
||||||
|
if (!(cond)) \
|
||||||
|
{ \
|
||||||
|
ZH_LOGE(msg, ESP_FAIL, ##__VA_ARGS__); \
|
||||||
|
cleanup; \
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Handle structure for AHT sensor.
|
* @brief Handle structure for AHT sensor.
|
||||||
*
|
*
|
||||||
@@ -25,7 +32,7 @@ struct _zh_aht_handle_t
|
|||||||
|
|
||||||
static const uint8_t _aht_reset_command = 0xBA; /*!< Reset command for AHT sensor */
|
static const uint8_t _aht_reset_command = 0xBA; /*!< Reset command for AHT sensor */
|
||||||
static const uint8_t _aht_read_command[] = {0xAC, 0x33, 0x00}; /*!< Command sequence to initiate sensor data read */
|
static const uint8_t _aht_read_command[] = {0xAC, 0x33, 0x00}; /*!< Command sequence to initiate sensor data read */
|
||||||
static const uint8_t _aht_init_command[] = {0xBE, 0x08, 0x00}; /*!< Command sequence to configure sensor for normal operation */
|
static const uint8_t _aht_init_command[] = {0xBE, 0x08, 0x00}; /*!< Init command */
|
||||||
static zh_aht_stats_t _stats = {0}; /*!< Global statistics structure for error tracking */
|
static zh_aht_stats_t _stats = {0}; /*!< Global statistics structure for error tracking */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -66,7 +73,7 @@ static esp_err_t _zh_aht_i2c_init(const zh_aht_init_config_t *config, zh_aht_han
|
|||||||
*/
|
*/
|
||||||
static uint8_t _zh_aht_calc_crc(const uint8_t *buf, size_t len);
|
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) // -V2008
|
esp_err_t zh_aht_init(const zh_aht_init_config_t *config, zh_aht_handle_t **handle)
|
||||||
{
|
{
|
||||||
ZH_LOGI("AHT initialization begin.");
|
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(config != NULL && handle != NULL, ESP_ERR_INVALID_ARG, NULL, "AHT initialization failed. Invalid argument.");
|
||||||
@@ -90,16 +97,17 @@ esp_err_t zh_aht_deinit(zh_aht_handle_t **handle)
|
|||||||
return ESP_OK;
|
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)
|
||||||
{
|
{
|
||||||
ZH_LOGI("AHT read begin.");
|
ZH_LOGI("AHT read begin.");
|
||||||
ZH_ERROR_CHECK(handle != NULL && *handle != NULL && humidity != NULL && temperature != NULL, ESP_ERR_INVALID_ARG, NULL, "AHT read fail. Invalid argument.");
|
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};
|
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), 100) == ESP_OK, ESP_FAIL,
|
||||||
++_stats.i2c_driver_error, "AHT read fail. I2C driver error.");
|
++_stats.i2c_driver_error, "AHT read fail. I2C driver error.");
|
||||||
vTaskDelay(80 / portTICK_PERIOD_MS);
|
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), 100) == ESP_OK, ESP_FAIL,
|
||||||
++_stats.i2c_driver_error, "AHT read fail. I2C driver error.");
|
++_stats.i2c_driver_error, "AHT read fail. I2C driver error.");
|
||||||
|
ZH_ERROR_CHECK((sensor_data[0] & 0x08) != 0, ESP_ERR_INVALID_STATE, NULL, "AHT read fail. Sensor not calibrated.");
|
||||||
ZH_ERROR_CHECK((sensor_data[0] & 0x80) == 0, ESP_ERR_TIMEOUT, NULL, "AHT read fail. Timeout exceeded.");
|
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.");
|
ZH_ERROR_CHECK(_zh_aht_calc_crc(sensor_data, 6) == sensor_data[6], ESP_ERR_INVALID_CRC, NULL, "AHT read fail. Invalid CRC.");
|
||||||
*humidity = (((((uint32_t)sensor_data[1]) << 16) | (((uint32_t)sensor_data[2]) << 8) | (((uint32_t)sensor_data[3]) << 0)) >> 4) / 1048576.0 * 100.0;
|
*humidity = (((((uint32_t)sensor_data[1]) << 16) | (((uint32_t)sensor_data[2]) << 8) | (((uint32_t)sensor_data[3]) << 0)) >> 4) / 1048576.0 * 100.0;
|
||||||
@@ -112,9 +120,22 @@ esp_err_t zh_aht_reset(zh_aht_handle_t **handle)
|
|||||||
{
|
{
|
||||||
ZH_LOGI("AHT reset begin.");
|
ZH_LOGI("AHT reset begin.");
|
||||||
ZH_ERROR_CHECK(handle != NULL && *handle != NULL, ESP_ERR_INVALID_ARG, NULL, "AHT reset fail. Invalid argument.");
|
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,
|
ZH_ERROR_CHECK(i2c_master_transmit((*handle)->dev_handle, &_aht_reset_command, sizeof(_aht_reset_command), 100) == ESP_OK,
|
||||||
++_stats.i2c_driver_error, "AHT reset fail. I2C driver error.");
|
ESP_FAIL, ++_stats.i2c_driver_error, "AHT reset fail. I2C driver error (reset cmd).");
|
||||||
vTaskDelay(20 / portTICK_PERIOD_MS);
|
vTaskDelay(20 / portTICK_PERIOD_MS);
|
||||||
|
uint8_t status_cmd = 0x71;
|
||||||
|
uint8_t status = 0;
|
||||||
|
ZH_ERROR_CHECK(i2c_master_transmit((*handle)->dev_handle, &status_cmd, sizeof(status_cmd), 100) == ESP_OK, ESP_FAIL,
|
||||||
|
++_stats.i2c_driver_error, "AHT reset fail. I2C driver error (status cmd).");
|
||||||
|
vTaskDelay(10 / portTICK_PERIOD_MS);
|
||||||
|
ZH_ERROR_CHECK(i2c_master_receive((*handle)->dev_handle, &status, sizeof(status), 100) == ESP_OK, ESP_FAIL,
|
||||||
|
++_stats.i2c_driver_error, "AHT reset fail. I2C driver error (status read).");
|
||||||
|
if ((status & 0x08) == 0)
|
||||||
|
{
|
||||||
|
ZH_ERROR_CHECK(i2c_master_transmit((*handle)->dev_handle, _aht_init_command, sizeof(_aht_init_command), 100) == ESP_OK, ESP_FAIL,
|
||||||
|
++_stats.i2c_driver_error, "AHT reset fail. I2C driver error (init cmd AHT20+).");
|
||||||
|
vTaskDelay(20 / portTICK_PERIOD_MS);
|
||||||
|
}
|
||||||
ZH_LOGI("AHT reset completed successfully.");
|
ZH_LOGI("AHT reset completed successfully.");
|
||||||
return ESP_OK;
|
return ESP_OK;
|
||||||
}
|
}
|
||||||
@@ -134,12 +155,12 @@ void zh_aht_reset_stats(void)
|
|||||||
static esp_err_t _zh_aht_validate_config(const zh_aht_init_config_t *config)
|
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 || config->i2c_address == 0x44, 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_frequency <= 400000 && config->i2c_frequency > 0, ESP_ERR_INVALID_ARG, NULL, "Invalid I2C frequency.");
|
||||||
ZH_ERROR_CHECK(config->i2c_handle != NULL, ESP_ERR_INVALID_ARG, NULL, "Invalid I2C handle.");
|
ZH_ERROR_CHECK(config->i2c_handle != NULL, ESP_ERR_INVALID_ARG, NULL, "Invalid I2C handle.");
|
||||||
return ESP_OK;
|
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)
|
||||||
{
|
{
|
||||||
i2c_device_config_t aht_config = {
|
i2c_device_config_t aht_config = {
|
||||||
.dev_addr_length = I2C_ADDR_BIT_LEN_7,
|
.dev_addr_length = I2C_ADDR_BIT_LEN_7,
|
||||||
@@ -149,15 +170,19 @@ static esp_err_t _zh_aht_i2c_init(const zh_aht_init_config_t *config, zh_aht_han
|
|||||||
i2c_master_dev_handle_t _dev_handle = NULL;
|
i2c_master_dev_handle_t _dev_handle = NULL;
|
||||||
ZH_ERROR_CHECK(i2c_master_bus_add_device(config->i2c_handle, &aht_config, &_dev_handle) == ESP_OK, ESP_FAIL, NULL, "Add I2C device failed.");
|
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);
|
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_probe(config->i2c_handle, config->i2c_address, 100) == 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_CONT(i2c_master_bus_rm_device(_dev_handle) == ESP_OK, NULL, "I2C remove device failed.")}, "Sensor not connected or not responded.");
|
||||||
|
uint8_t status_cmd = 0x71;
|
||||||
uint8_t sensor_data = 0;
|
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_transmit(_dev_handle, &status_cmd, sizeof(status_cmd), 100) == 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_CONT(i2c_master_bus_rm_device(_dev_handle) == ESP_OK, NULL, "I2C remove device failed.")}, "I2C driver error (status cmd).");
|
||||||
|
vTaskDelay(10 / portTICK_PERIOD_MS);
|
||||||
|
ZH_ERROR_CHECK(i2c_master_receive(_dev_handle, &sensor_data, sizeof(sensor_data), 100) == ESP_OK, ESP_FAIL,
|
||||||
|
{ZH_ERROR_CHECK_CONT(i2c_master_bus_rm_device(_dev_handle) == ESP_OK, NULL, "I2C remove device failed.")}, "I2C driver error (status read).");
|
||||||
if ((sensor_data & 0x08) == 0)
|
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_transmit(_dev_handle, _aht_init_command, sizeof(_aht_init_command), 100) == 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_CONT(i2c_master_bus_rm_device(_dev_handle) == ESP_OK, NULL, "I2C remove device failed.")}, "I2C driver error (init cmd).");
|
||||||
vTaskDelay(20 / portTICK_PERIOD_MS);
|
vTaskDelay(20 / portTICK_PERIOD_MS);
|
||||||
}
|
}
|
||||||
handle->dev_handle = _dev_handle;
|
handle->dev_handle = _dev_handle;
|
||||||
|
|||||||
Reference in new issue
Block a user