From 1bc908eb5075c3ddf3a8f79364690f0c785e055d Mon Sep 17 00:00:00 2001 From: Alexey Zholtikov Date: Sat, 17 Oct 2026 21:36:38 +0300 Subject: [PATCH] doc: added wiki --- wiki_ru.md | 283 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 283 insertions(+) create mode 100644 wiki_ru.md diff --git a/wiki_ru.md b/wiki_ru.md new file mode 100644 index 0000000..d1258a6 --- /dev/null +++ b/wiki_ru.md @@ -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" +```