Чего только не реализовано на ESP32?
На этом микропоцессоре создано множество интернет и не только вещей. Вот и я уже очень давно хочу приобщиться к IOT, а также дополнительно прокачать себя в низкоуровневом программировании.
Однако ванильные Arduino‑скетчи, коими завалены все туториалы, меня не интересуют. Хочется «настоящего» программирования, сложностей, бессонных ночей, разборов документации и тому подобное...
Потому я выбрал путь изучения ESP32 с помощью C и ESP‑IDF.
С чего же начать? — конечно же метеостанция!
Это небольшой проект, которые покажет насколько ты вообще DIYщик, научит основам работы с ESP и её функциям.
Для своей метеостации я выбрал только основные датчики: давления, освещенности, температуры, влажности. Вот о последнем сегодня и пойдёт речь.
В качестве подопытного был приобретён популярный DHT22. Приобретался он сразу ��а плате с обвязкой (подтягивающий резистор, конденсатор и удобные пины для подключения).
Первый этап - подключение
Здесь всё просто: плюс к плюсу, минус к минусу, дату на любой цифровой GPIO. Главное чтобы GPIO умел работать как на ввод, так и на вывод. Питание можно брать как 3.3 В, так и 5 В.

Программирование
Опустим вопросы настройки IDE (в моём случае VS Code) и ESP-IDF. Перейдём сразу к сути и создадим новый пустой проект с благозвучным именем weather-station
Для работы с датчиком необходимо создать компонент, удобнее всего это делать через консоль:
idf.py create-component -C components dht22
У нас появится папка компонентов со следующей структурой:
components/dht22/ ├── include/ │ └── dht22.h ├── CMakeLists.txt └── dht22.c
Для начала определим заголовочный файл dht22.h
От датчика нам нужна только одна функция, которая получит на вход номер GPIO и «вернёт» нам значения влажности и температуры.
#pragma once #include <driver/gpio.h> #include <esp_log.h> esp_err_t dht22_read(gpio_num_t pin, float *temperature, float *humidity);
Можно было сделать возвращаемое значение void, но с ESP удобно возвращать стандартный тип ошибки esp_err_t. Это помогает потом понять что пошло не так, или наоборот — что всё работает отлично.
Для использования GPIO необходимо в CMakeList.txt подключить компонент esp_driver_gpio:
idf_component_register(SRCS "dht22.c" INCLUDE_DIRS "include" REQUIRES esp_driver_gpio)
А вот дальше начинается веселье... Нужно разобраться как работает датчик и реализовать это в коде.
Открываем документацию на датчик и видим следующее:

И чуть ниже расписано как это выглядит непосредственно на железе с указанием всех таймингов.

Чтож, приступаем к реализации!
Для работы с микросекундами самым простым вариантом будет использование стандартной функции задержки ets_delay_us(uint32_t us), которая входит в библиотеку rom/ets_sys.h.
А дальше всё по порядку:
Инициализация датчика (строки 17-30)
Подсчёт импульсов/бит (строки 32-47)
Проверка чексуммы (строки 50-53)
Перевод в человеческие значения (строки 55-67)
Получившийся код dht22.c приведён ниже:
#include "dht22.h" #include <driver/gpio.h> #include <rom/ets_sys.h> #include <esp_log.h> static int dht_wait_level(gpio_num_t pin, int level, uint32_t timeout_us) { while (gpio_get_level(pin) == level) { if (!timeout_us--) return -1; ets_delay_us(1); } return 0; } esp_err_t dht22_read(gpio_num_t pin, float *temperature, float *humidity) { uint8_t data[5] = {0}; // Start signal gpio_set_direction(pin, GPIO_MODE_INPUT_OUTPUT_OD); gpio_set_level(pin, 0); ets_delay_us(2000); // 2 ms low (0.8-20 ms) gpio_set_level(pin, 1); ets_delay_us(40); // 40 us high (20-200 us) // Response if (dht_wait_level(pin, 0, 80) < 0) return ESP_ERR_TIMEOUT; if (dht_wait_level(pin, 1, 80) < 0) return ESP_ERR_TIMEOUT; // Read 40 bits for (int i = 0; i < 40; i++) { // wait for low to high edge if (dht_wait_level(pin, 0, 60) < 0) return ESP_ERR_TIMEOUT; // measure high pulse uint32_t t = 0; while (gpio_get_level(pin)) { if (++t > 100) return ESP_ERR_TIMEOUT; ets_delay_us(1); } int byte = i / 8; data[byte] <<= 1; if (t > 40) // >40 us means bit = 1 data[byte] |= 1; } // checksum uint8_t sum = data[0] + data[1] + data[2] + data[3]; if ((sum & 0xFF) != data[4]) { return ESP_ERR_INVALID_CRC; } uint16_t raw_h = (data[0] << 8) | data[1]; uint16_t raw_t = (data[2] << 8) | data[3]; *humidity = raw_h / 10.0f; if (raw_t & 0x8000) { raw_t &= 0x7FFF; *temperature = -((float)raw_t / 10.0f); } else { *temperature = raw_t / 10.0f; } return ESP_OK; }
Теперь остаётся вызывать эту функцию из основного кода.
Для этого в main.c
определяем порт (я подключил к 23)
добавляем бесконечный цикл с вызовом функции
добавляем задержку между вызовами, чтобы дёргать датчик только раз в несколько секунд
выводим информацию — я сделал в консоль с помощью функции ESP_LOGI(tag, format,...)
#include <stdio.h> #include <freertos/FreeRTOS.h> #include <freertos/task.h> #include <esp_log.h> #include "dht22.h" #define DHT22_GPIO 23 void app_main(void) { float temperature, humidity = 0; while (true){ if (dht22_read(DHT22_GPIO, &temperature, &humidity) == ESP_OK) ESP_LOGI("main", "Temperature = %0.1f, Humidity = %0.1f", temperature, humidity); vTaskDelay(pdMS_TO_TICKS(2000)); } }
Билдим, прошиваем и смотрим на вывод:
I (2275) main: Temperature = 22.6, Humidity = 28.3 I (4275) main: Temperature = 22.6, Humidity = 28.4 I (6275) main: Temperature = 22.6, Humidity = 28.3
Всё заработало!
Датчик исправно работает и показывает температур и влажность в помещении. Сейчас зима, поэтому влажность низкая.
Первый шаг в IOT сделан! Дальше будет только интереснее!
