Files
zh_aht/wiki_ru.md
T
2026-10-17 21:36:38 +03:00

18 KiB
Raw Blame History

Описание компонента 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 мс.

Примеры использования

Базовый пример

#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);
}

Обработка ошибок и сброс датчика

#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;
    }
}

Мониторинг статистики ошибок

#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 содержат имя файла, строку и имя кода ошибки.

#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.

Установка

cd ваш_проект/components
git clone https://github.com/aZholtikov/zh_aht
#include "zh_aht.h"