Схема с прошлого урока собрана и проверена: сенсор на пине 2, подтяжка 10 кОм между питанием и данными, на ножках ровно 5 В. Я вставил в IDE скетч из статьи, нажал Verify — и через две секунды получил красную строку: fatal error: DHT.h: No such file or directory. Схема рабочая, провода на месте, а скетч даже не начал собираться. Первая мысль была «перепутал пин», вторая — «датчик бракованный». Обе мимо, и IDE при этом честно подсветила настоящую проблему: компилятор сказал прямо — в проекте нет файла DHT.h, то есть библиотеки.
Библиотека — это чужой код, который умеет то, чего не умеет мой скетч: ронять линию данных на 18 миллисекунд, отмерять импульсы в 27 и 70 микросекунд, собирать из сорока бит два числа и проверять контрольную сумму. Писать это вручную — неделя работы ради одной температуры. Готовая библиотека Adafruit ставится за две минуты, а первый рабочий скетч занимает десять строк. В этом уроке мы поставим её правильно, увидим в Serial Monitor первые градусы и проценты и разберём все ошибки компиляции, которые встречаются на этом пути, — я собрал их в таблицу и за год с тех пор пятой не встретил.
Ставим библиотеку через Library Manager
В Arduino IDE откройте Manage Libraries: слева вверху иконка с книгами или меню Sketch — Include Library — Manage Libraries, горячие клавиши Ctrl+Shift+I. В строке поиска наберите dht sensor. Первой в списке выйдет DHT sensor library от Adafruit — берите именно её, с автором Adafruit, а не одноимённые клоны. Кнопка Install подтянет предложение доустановить зависимость Adafruit Unified Sensor — соглашайтесь, это не опция, а требование. Перед установкой закройте вкладки со скетчами: IDE иногда не подхватывает свежую библиотеку до перезапуска.
Зависимость обязательна по устройству библиотеки: внутри DHT.h зашит include, который тянет файл Adafruit_Sensor.h — общий каркас всех датчиков Adafruit. Нет каркаса — нет сборки, и компилятор уйдёт в ошибку, хотя про влажность в ней ни слова. У меня на момент урока стоят версии 1.4.4 и 1.1.14; если у вас заметно старее, обновитесь — в ветке 1.3.x я ловил подвисание чтения на DHT22. Проверить версии можно там же, в Manager Libraries: в строке каждой библиотеки справа стоит установленный номер.
ZIP-архивом с GitHub библиотеки я больше не ставлю: через Include Library .ZIP установка проходит, но обновляться потом неудобно, и через полгода ловишь баг, который давно починили. Manager Libraries показывает версии и зависимость сразу, поэтому для DHT это единственный путь, которым я пользуюсь. Как устроена работа с библиотеками в целом — от поиска до установки из архива — мы разбирали в отдельном уроке про библиотеки в IDE.
Первый скетч: десять строк
#include "DHT.h" #define DHTPIN 2#define DHTTYPE DHT11 // для DHT22 меняется только эта строка DHT dht(DHTPIN, DHTTYPE); void setup() { Serial.begin(9600); dht.begin(); // разбудить сенсор} void loop() { float t = dht.readTemperature(); // градусы Цельсия float h = dht.readHumidity(); // проценты влажности Serial.print("t = "); Serial.println(t); Serial.print("h = "); Serial.println(h); delay(2000); // DHT22 не отвечает чаще, чем раз в 2 секунды}Пройдём по строкам. Строка include с файлом DHT.h подключает библиотеку, которую мы только что поставили, — компилятор находит файл сам. Две строки #define — это подстановки: до сборки компилятор заменит DHTPIN на 2 и DHTTYPE на DHT11 по всему тексту скетча. Смысл не в экономии, а в удобстве: чтобы перевесить датчик на пин 7 или сменить модель, вы правите одну строку вверху, а не ищете число по всему файлу.
Строка DHT dht(DHTPIN, DHTTYPE) создаёт объект сенсора. Аргументы идут в жёстком порядке — сначала пин, потом модель; перепутать их местами проще простого, и компилятор не поможет: оба в его глазах числа. В setup() вызывается dht.begin() — функция настраивает пин данных и выдерживает паузу на пробуждение сенсора. Повторять begin в loop() не нужно и даже вредно, но об этом ниже.
В loop() живут две функции чтения: readTemperature() возвращает float в градусах Цельсия, readHumidity() — float в процентах относительной влажности. Печать парой имя=значение вроде t = 24.10 выбрана не для красоты: такие строки без переделок читает Serial Plotter, и показания сразу видны графиком. delay(2000) в конце — не мой каприз, а требование протокола: сенсор пересчитывает показания внутри корпуса раз в две секунды, более частый опрос возвращает мусор. Если строк с показаниями нет вовсе, откройте монитор порта и убедитесь, что плата жива и скетч запустился.
| Сообщение компилятора | Причина | Что делать |
|---|---|---|
| fatal error: DHT.h: No such file or directory | Библиотека не установлена | Manager Libraries — установить DHT sensor library |
| Adafruit_Sensor.h: No such file or directory | Не встала зависимость | Установить Adafruit Unified Sensor той же кнопкой |
| 'dht' was not declared in this scope | Объект не создан или назван иначе | Создать строкой DHT dht(пин, DHT11) до setup() |
| no matching function for call to 'DHT::DHT(int, int)' | Перепутан порядок аргументов | Первым пин, вторым модель: DHT dht(2, DHT11) |
| 'DHT11' was not declared in this scope | Установлен клон без нужных констант | Удалить клон, поставить библиотеку Adafruit |
Что делает dht.begin() и почему его зовут один раз
Теперь честно о begin. Он настраивает пин данных как выход, поднимает линию в высокий уровень и выдерживает паузу около секунды: сенсор после подачи питания не отвечает мгновенно, ему нужен стартовый прогрев. Отсюда два правила, которые я вывел из своих ночных отладок. Первое: не читайте датчик раньше, чем через секунду после begin, иначе первая строка в мониторе выйдет пустой или мусорной, а вы решите, что датчик мёртв. Второе: begin зовётся один раз. Мой знакомый месяц «перезагружал» зависающий сенсор вызовом begin каждые пять минут, а лечилось всё заменой макетной перемычки, которая болталась в колодке. Кстати, дымовой тест из фрагмента ниже появился именно после этой истории: одна проверка в setup экономит вечер угадываний.
Первый запуск я всегда делаю с открытой записью времени: если строки идут ровно через две секунды, тайминг опроса выстроен правильно, и большинство будущих историй с nan уже не случится. Расстояние между строками в мониторе — бесплатный осциллограф для скетчей с delay.
Для проверки живости удобно сделать один дымовой тест прямо в setup — фрагмент ниже дописывается в конец setup() из первого скетча:
// после dht.begin():delay(1500); // прогрев сенсора float t = dht.readTemperature(); // первое чтение — тестif (t > -40.0 && t < 80.0) { Serial.print("sensor ok, t = "); Serial.println(t);} else { Serial.println("sensor fail: проверь питание и провода");}- Смените DHT11 на DHT22 в #define и в конструкторе — скетч должен собраться без других правок. Это проверка, что вы поняли, где живёт тип датчика.
- Перевесьте датчик с пина 2 на пин 7, поменяв только DHTPIN. Если в реальной схеме забыли переткнуть провод, получите nan — и это тоже полезный опыт.
- Уберите delay(2000) совсем. В симуляторе ничего не сломается, а на реальном DHT11 опрос раз в миллисекунду превратит половину строк в nan: частоту задаёт железо, а не код. Заодно вспомните, что само чтение занимает у процессора 4–5 миллисекунд — на это время скетч стоит целиком.
- 1. Какая пара библиотек нужна для скетча с DHT от Adafruit?
- 2. Что произойдёт, если не вызвать dht.begin() в setup()?
- 3. Как правильно создать объект для DHT22 на пине 3?
- 4. Что возвращает dht.readHumidity()?
- 5. Компилятор пишет: 'dht' was not declared in this scope. Что проверить?
- Библиотека ставится через Manager Libraries: DHT sensor library от Adafruit плюс обязательная зависимость Adafruit Unified Sensor.
- Каркас скетча: DHT dht(пин, DHT11) до setup(), dht.begin() в setup() один раз, чтение в loop() не чаще раза в две секунды.
- readTemperature() возвращает градусы Цельсия, readHumidity() — проценты; обе возвращают float.
- Ошибки компиляции решаются в порядке таблицы: библиотека, зависимость, создание объекта, порядок аргументов.
- Печать парой имя=значение (t = 24.10) удобна и человеку, и Serial Plotter.
Если компилятор молчит, а в мониторе пошли пары t и h — поздравляю, у вас есть работающий термометр. Дальше дорога раздваивается. Хочется понять функции чтения до байта — двигайтесь в урок с разбором кода чтения: там Фаренгейт, округление и кэш библиотеки, из-за которого два вызова подряд возвращают одно и то же. Хочется сразу применить числа — в уроке про термостат с гистерезисом из тех же двух строк собирается вентилятор с границами включения. А если ваша ошибка компиляции в таблицу не уложилась — напишите мне, пополню список.
Частые вопросы по теме урока
- Какая пара библиотек нужна для скетча с DHT от Adafruit?
- Внутри DHT.h зашит include файла Adafruit_Sensor.h из каркасной библиотеки. Без Unified Sensor скетч с DHT не собирается. DHT sensor library и Adafruit Unified Sensor
- Что произойдёт, если не вызвать dht.begin() в setup()?
- Begin настраивает пин данных и время на пробуждение сенсора. Без него линия не в нужном состоянии, и сенсор не отвечает на стартовый импульс. Скетч соберётся, но датчик, скорее всего, не ответит — чтения вернут мусор или nan
- Как правильно создать объект для DHT22 на пине 3?
- Порядок аргументов жёсткий: сначала пин, потом модель. Компилятор не поймает перепутанные местами числа — оба выглядят как int. DHT dht(3, DHT22);
- Что возвращает dht.readHumidity()?
- readHumidity() возвращает float в процентах относительной влажности: DHT11 в пределах 20–80, DHT22 от 0 до 100. Проценты относительной влажности как float
- Компилятор пишет: 'dht' was not declared in this scope. Что проверить?
- Ошибка означает, что имени dht компилятор не знает: объект не создан, создан ниже по коду или назван иначе, чем используется. Создана ли строка DHT dht(...) до setup()