Как подключить датчик температуры и влажности DHT11 к Arduino
Датчик температуры и влажности DHT11 – популярный и дешёвый датчик, который можно использовать в довольно широком диапазоне температур и относительной влажности. Давайте посмотрим, как подключить его к Arduino и как считывать с него данные.
Нам понадобится:
- Arduino UNO или иная совместимая плата;
- модуль с датчиком температуры и влажности DHT11;
- соединительные провода (вот, например, хороший набор);
- макетная плата;
- логический анализатор (не обязательно);
- персональный компьютер со средой разработки Arduino IDE.
1Технические характеристики датчика температуры и влажности DHT11
Итак, датчик DHT11 имеет следующие характеристики:
- диапазон измеряемой относительной влажности – 20..90% с погрешностью до 5%,
- диапазон измеряемых температур – 0..50°C с погрешностью до 2°C;
- время реакции на изменения влажности – до 15 секунд, температуры – до 30 секунд;
- минимальный период опроса – 1 секунда.
Как видно, датчик DHT11 не отличается особой точностью, да и диапазон температур не охватывает отрицательные значения, что вряд ли подойдёт для наружных измерений в холодное время года при нашем климате. Однако малая стоимость, малый размер и простота работы с ним частично перекрывают эти недостатки. На рисунке приведён внешний вид датчика и его размеры в миллиметрах.
2Схема подключения датчика температуры и влажности DHT11
Рассмотрим схему подключения датчика температуры и влажности DHT11 к микроконтроллеру, в частности, к Arduino.
Давайте посмотрим, что показано на рисунке.
Обозначение на рисунке | Описание | Примечание |
---|---|---|
MCU | Микроконтроллер или одноплатный компьютер | Arduino / Raspberry Pi и др. |
DHT11 | Датчик температуры и влажности | Выводы 1Pin, 2Pin и 4Pin задействованы в схеме, один из выводов датчика – 3-ий пин 3Pin – ни к чему не подключается. |
DATA | Шина данных | Если длина соединительного кабеля от датчика к микроконтроллеру не превышает 20 метров, то эту шину рекомендуется подтянуть к питанию резистором 5,1 кОм; если больше 20 метров – то другой подходящий номинал (меньший). |
VDD | Питание датчика | Допустимы напряжения от ~3,0 до ~5,5 вольт постоянного тока; если используется питание ~3,3 В, то желательно использовать питающий провод не длиннее 20 см. |
Соберём рассмотренную схему. Я также по традиции включу в цепь логический анализатор, чтобы можно было изучить временную диаграмму информационного обмена с датчиком.
Сенсор DHT11 часто продаётся в виде готовой сборки с необходимой обвязкой – подтягивающими резистором и фильтрующим конденсатором (как на предыдущей фотографии). Для экспериментов с Arduino я рекомендую покупать именно такой.
3Считывание данных с сенсора DHT11 при помощи Arduino
Давайте пойдём таким путём: скачаем библиотеку для датчика DHT11 (также приложил её в конце статьи, т.к. у обновлённой библиотеки изменились вызываемые функции), установим её стандартным способом (распаковав в директорию \libraries\ среды разработки для Arduino).
Напишем вот такой простенький скетч. Он будет выводить в последовательный порт компьютера каждые 2 секунды сообщения об относительной влажности и температуре, считанные с датчика DHT11.
#include <dht11.h> // подключаем библиотеку dht11 sensor; // инициализация экземпляра датчика #define DHT11PIN 8 // вывод 8 будет шиной DATA void setup() { Serial.begin(9600); } void loop() { int chk = sensor.read(DHT11PIN); Serial.print("h="); Serial.print(sensor.humidity); Serial.print("%\t"); Serial.print("t="); Serial.print(sensor.temperature); Serial.println("C"); delay(2000); }
Загрузим этот скетч в Arduino. Подключимся к Arduino с помощью монитора COM-порта и увидим следующее:
Видно, что данные и о влажности, и о температуре считываются и выводятся в терминалку.
4Временная диаграмма информационного обмена датчика температуры и влажности DHT11 с микроконтроллером
С помощью временной диаграммы, полученной с логического анализатора, разберёмся, как осуществляется информационный обмен.
Для связи с микроконтроллером датчик температуры и влажности DHT11 использует однопроводный последовательный пакетный интерфейс. Один информационный пакет длительностью около 4 мс содержит: 1 бит запроса от микроконтроллера, 1 бит ответа датчика и 40 битов данных от датчика (16 битов информации о влажности, 16 битов информации о температуре и 8 проверочных битов). Давайте подробнее рассмотрим временную диаграмму информационного обмена Arduino с датчиком DHT11.
Из рисунка видно, что есть два типа импульсов: короткие и длинные. Короткие в данном протоколе обмена обозначают нули, длинные импульсы – единицы.
Итак, первые два импульса – это запрос Arduino к DHT11 и, соответственно, ответ датчика. Далее идут 16 бит влажности. Причём они разделены на байты, старший и младший, старший слева. То есть на нашем рисунке данные о влажности такие: 0001000000000000 = 00000000_00010000 = 0x10 = 16% относительной влажности.
Данные о температуре, аналогично: 0001011100000000 = 00000000_00010111 = 0x17 = 23 градуса Цельсия.
Контрольная сумма – это всего-навсего арифметическое суммирование 4-х полученных байтов данных:
00000000 +
00010000 +
00000000 +
00010111 =
00100111 в двоичной системе или 0 + 16 + 0 + 23 = 39 в десятичной.
5Работа с датчиком DHT11 без библиотеки
Теперь мы знаем достаточно для того чтобы написать собственную программу для работы с сенсором температуры и влажности DHT11 без использования сторонних библиотек. Напишем скетч, который будет опрашивать раз в секунду датчик и выводить в последовательный порт компьютера принятый пакет и данные о температуре, влажности, а также проверочный байт. На 13-ую ножку Arduino выведем контрольный сигнал и, подключившись в ней логическим анализатором, проверим, что мы верно считываем информацию от датчика.
Скетч для работы с DHT11 и Arduino без сторонних библиотек (разворачивается)
#define DHT11pin 8 // для подключения шины DATA сенсора DHT11 #define LEDpin 13 // используем для контроля const int NUM_READS = 500; // зависит от частоты кварца и подбирается экспериментально long readsCounter = 0; // счётчик циклов чтения int reads[NUM_READS]; // сырой массив считанных значений void setup() { Serial.begin(9600); pinMode(DHT11pin, INPUT); pinMode(LEDpin, OUTPUT); } void loop() { if (readsCounter < NUM_READS) { readSerialDHT11(); } else { delay(1000); // задержка ПЕРЕД запросом processDht11Data(); // обрабатываем данные из прошлого цикла initLink(); // инициализируем новый цикл обмена resetVals(); } } // Посылает импульс инициализации обмена с DHT11: void initLink() { pinMode(DHT11pin, OUTPUT); digitalWrite(DHT11pin, LOW); delay(15); pinMode(DHT11pin, INPUT); } // Читает данные датчика DHT11 и записывает в массив: void readSerialDHT11() { int sensorValue = digitalRead(DHT11pin); reads[readsCounter] = sensorValue; digitalWrite(LEDpin, sensorValue); // для проверки выводим на отдельную ножку readsCounter++; } // Обрабатывает массив данных за цикл с DHT11: void processDht11Data() { byte dht11Data[41] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; // обработанный массив (биты пакета) int zeroLen = 1; // минимальная длительность бита "0" int oneLen = 3 * zeroLen; // примерная длительность бита "1" int wrongData = 6 * zeroLen; // допуск по длительности для данных int currentBitLen = 0; // длительность текущего бита int bitPosition = 0; // позиция бита в пакете for (int i=1; i<NUM_READS-1; i++) { // пропускаем первый импульс-подтверждение if ((reads[i] == 0) && (reads[i+1] == 1)) { // если пришёл передний фронт // Принимаем решение: предыдущий бит - это "0" или "1": if ((currentBitLen >= zeroLen) && (currentBitLen <= oneLen)) { dht11Data[bitPosition] = 0; bitPosition++; } else if ((currentBitLen < oneLen) && (currentBitLen <= wrongData)) { dht11Data[bitPosition] = 1; bitPosition++; } currentBitLen = 0; } else { if (reads[i] == HIGH) { // при сигнале HIGH currentBitLen += 1; // считаем длительность текущего бита } } } for (int i=0; i<41; i++) { Serial.print(dht11Data[i]); Serial.print(" "); } Serial.println(); getHumidTemperatureParity(dht11Data); } // Получает данные о влажности, температуре из пакета DHT11 void getHumidTemperatureParity(byte data[]) { word humidity = 0; byte hLow = 0; byte hHi = 0; word temperature = 0; byte tLow = 0; byte tHi = 0; byte parity = 0; for (int i=1; i<9; i++){ //пропускаем первый импульс-подтверждение hLow = hLow | (data[i] << (8 - i)); } for (int i=9; i<17; i++){ hHi = hHi | (data[i] << (16 - i)); } for (int i=17; i<25; i++){ tLow = tLow | (data[i] << (24 - i)); } for (int i=25; i<33; i++){ tHi = tHi | (data[i] << (32 - i)); } for (int i=33; i<41; i++){ parity = parity | (data[i] << (40 - i)); } humidity = word(hHi, hLow); temperature = word(tHi, tLow); Serial.print("h="); Serial.print(humidity); Serial.print("%\t"); Serial.print("t="); Serial.print(temperature); Serial.print("C\t"); Serial.print("parity="); Serial.println(parity); } // Сбрасывает переменные в исходное состояние: void resetVals() { readsCounter = 0; }
Небольшая таблица даст дополнительные разъяснения к предлагаемому решению.
Функция | Назначение | Комментарий |
---|---|---|
initLink() | Посылает импульс инициализации обмена с DHT11. | В режиме INPUT цифровые выводы Arduino находятся в состоянии "1". Мы переводим их в режим OUTPUT и на 15 мс опускаем в "0", что служит сигналом начала обмена для датчика DHT11. |
readSerialDHT11() | Читает данные датчика DHT11 и записывает в массив. | Постоянно опрашиваем состояние линии DATA, записываем принятые данные в массив (до верхней границы массива), выводим для проверки на 13-ый пин Arduino принятые данные (это нужно было мне для отладки, в «чистовом» коде этот кусок можно убрать). |
processDht11Data() | Обрабатывает массив данных, принятых за один цикл (один пакет). | По передним фронтам принятых импульсов фиксируем факт пришедшего нового бита и считаем в условных единицах его длительность. По длительности оцениваем – это бит-единица или бит-ноль (в некоторых заданных пределах). Составляем пакет из 41 бита. |
getHumidTemperatureParity() | Получает данные о влажности, температуре из пакета. | Выделяем из 41 бита пакета данные о влажности, температуре и контрольную сумму. |
resetVals() | Сбрасывает переменные в исходное состояние. | При расширении программы здесь могут появиться ещё переменные, требующие сброса. |
Вот так будет выглядеть временная диаграмма. Здесь A – это сигнал в шине DATA датчика DHT11, B – считываемый сигнал.
Видно, что мы считываем данные с небольшой задержкой. Кроме того, мы начинаем читать данные с ответного сигнала и пропускаем запросный сигнал. В программе этот первый импульс мы не учитываем, т.к. он не относится непосредственно к данным. В выводе монитора COM-порта мы можем наблюдать аналогичную картину: длинная строка единиц и нулей – это наши пакеты, и видно, что все они начинаются с единичного бита, т.к. мы считываем пакеты, начиная с ответного бита.
Что мы не учли в этой программе, так это то, что минимальная длительность импульса бита-нуля может изменяться в некоторых пределах, а мы жёстко задали её значение в коде. По хорошему нужно ещё написать функцию getMinimalBitLen(), которая бы находила минимальную длительность (в условных единицах) одного бита-нуля в обрабатываемом пакете.
Кстати, я уже упоминал несколько раз в других статьях о чудесном устройстве для любителей электроники и изучения разнообразных протоколов – Flipper Zero. Недавно у Флиппера появилось приложение для чтения показаний датчиков температуры, в том числе и DHT11:
Download attachments:
- Библиотека DHT11 для Arduino (811 Downloads)
Поблагодарить автора:
Поделиться
Related items
6 comments
-
Горчаков Четверг, 04 Ноябрь 2021 04:19 Ссылка на комментарий
программа не компилируется в строке for (int i=1; i= zeroLen) && (currentBitLen
-
aave1 Четверг, 04 Ноябрь 2021 10:51 Ссылка на комментарий
Дело в том, что там знак "меньше" или "больше", который редактор воспринял как html тег, и часть кода после него спрятал. Я забыл заменить его спецсимволом. Поправил, теперь всё будет компилироваться. Спасибо большое за замечание!
-
Горчаков Четверг, 04 Ноябрь 2021 12:01 Ссылка на комментарий
Отлично! Как быстро вы среагировали.Замечательная работа.Премного благодарен.
-
Горчаков Четверг, 04 Ноябрь 2021 13:05 Ссылка на комментарий
Огромное спасибо за быструю реакцию.Теперь программа транслируется,однако выдает одни нули. Не можете ли подсказать почему?Может скорость поменять?
-
Горчаков Четверг, 04 Ноябрь 2021 13:11 Ссылка на комментарий
Датчик совершенно точно исправен, так как с библиотекой он выдает разумные значения 25 и 22.Значит что-то с программой не так. Правда у меня не совсем ардуино, а китайский клон wavgat Uno R3,там вроде какой-то другой процессор стоит.Вы писали, что скорость нужно подбирать,а как?
-
Горчаков Четверг, 04 Ноябрь 2021 13:17 Ссылка на комментарий
Кстати, я проверил как считается контрольная сумма, так вот иногда в дробной части проскакивает единица и тогда будет ошибка. Видимо все-таки нужно складывать все четыре байта, чтобы получить сумму и сравнивать ее с контрольной-тогда ошибок будет меньше.