- Плата Arduino. x1
- Компьютер(для написания и загрузке кода, а так же мы будем брать с него электричество и общаться с ним). x1
- Провод для соединения компьютера и платы. x1
- Датчик температуры и влажности DTH11.
- Провода. x3
- Есть схемы с внешним источником питания, но можно взять питание и с платы.
Схема:
Рассмотрим скетч, в котором мы будем управлять датчиком DTH11 на прямую, через передачу и приём сигнала(а так же его обработку) используя только стандартные функции.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 |
const int dataPin = 2; int temperature = -1; int humidity = -1; void setup(){ Serial.begin(9600); } int readDTH11(){ uint8_t recvBuffer[5]; uint8_t cnt = 7; uint8_t idx = 0; for (int i = 0; i<5; i++) recvBuffer[i] = 0; pinMode(dataPin, OUTPUT); digitalWrite(dataPin, LOW); delay(18); digitalWrite(dataPin, HIGH); delayMicroseconds (40); pinMode (dataPin, INPUT); pulseIn (dataPin, HIGH); unsigned int timeout = 10000; for (int i=0; i<40; i++) { timeout =10000; while (digitalRead(dataPin) == LOW){ if (timeout==0) return -1; timeout--; } unsigned long t = micros(); timeout =10000; while (digitalRead(dataPin) == HIGH){ if (timeout==0) return -1; timeout--; } if ((micros()-t)>40) recvBuffer[idx] |= (1 <<cnt); if (cnt == 0) { cnt = 7; idx++; } else cnt--; } humidity = recvBuffer[0]; temperature = recvBuffer[2]; uint8_t sum = recvBuffer[0]+recvBuffer[2]; if (recvBuffer[4] != sum) return -2; return 0; } void loop(){ int ret = readDTH11(); if(ret !=0) Serial.println(ret); Serial.print("Humidity: ");Serial.print(humidity);Serial.println(" %"); Serial.print("Temperature: ");Serial.print(temperature);Serial.println(" C"); delay(2000); } |
Сначала зададим глобальные переменные, которые будут использоваться во всё коде:
1 2 |
int temperature = -1; int humidity = -1; |
В переменной dataPin мы будем хранить значение номера штырька. Объявляется dataPin как константа (const) и подразумевается, что значение переменной не будет менятся.
1 |
const int dataPin = 2; |
Так же у нас будет использоваться две переменные со значением температуры и влажности (переменные целого типа):
1 2 |
int temperature = -1; int humidity = -1; |
В блоке Setup устанавливаем скорость взаимодействия компьютера и Arduino.
1 |
Serial.begin(115200); |
В нашем случае в значение 115200 бод
дальше мы объявляем функцию readDTH11() которая будет отдавать 0 когда удалось получить значение, -1 когда датчик не отвечает, и -2 когда не совпала контрольная сумма. Перед тем как вернуть 0 данная функция назначит глобальным переменным humidity и temperature значения влажности и температуры соответственно.
Рассмотрим её по подробнее:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
uint8_t recvBuffer[5]; uint8_t cnt = 7; uint8_t idx = 0; for (int i = 0; i<5; i++) recvBuffer[i] = 0; pinMode(dataPin, OUTPUT); digitalWrite(dataPin, LOW); delay(18); digitalWrite(dataPin, HIGH); delayMicroseconds (40); pinMode (dataPin, INPUT); pulseIn (dataPin, HIGH); unsigned int timeout = 10000; for (int i=0; i<40; i++) { timeout =10000; while (digitalRead(dataPin) == LOW){ if (timeout==0) return -1; timeout--; } unsigned long t = micros(); timeout =10000; while (digitalRead(dataPin) == HIGH){ if (timeout==0) return -1; timeout--; } if ((micros()-t)>40) recvBuffer[idx] |= (1 <<cnt); if (cnt == 0) { cnt = 7; idx++; } else cnt--; } humidity = recvBuffer[0]; temperature = recvBuffer[2]; uint8_t sum = recvBuffer[0]+recvBuffer[2]; if (recvBuffer[4] != sum) return -2; return 0; |
Задаём переменные которые будут использоваться в фунции:
1 2 3 |
uint8_t recvBuffer[5]; uint8_t cnt = 7; uint8_t idx = 0; |
Задаём массив recvBuffer целых 8 битных без знаковых чисел, с точной шириной. Длинной 5
1 |
uint8_t recvBuffer[5]; |
в которыё мы будем вносить данные со щётчика.
а так же переменные cnt = 7 в ней будет хранится номер текущего передаваемого бита.
и idx = 0 в котором будет хранится номер байта идущего со счётчика( или индекс массива)
Здесь же зануляем массив:
1 |
for (int i = 0; i<5; i++) recvBuffer[i] = 0; |
теперь мы готовы начать обмен данными…
1 |
pinMode(dataPin, OUTPUT); |
устанавливаем значение штырька в режим вывода.
1 |
digitalWrite(dataPin, LOW); |
устанавливаем значение LOW
1 |
delay(18); |
ждём 18 милисекунд
1 |
digitalWrite(dataPin, HIGH); |
включаем напряжение (устанавливаем режим HIGH)
1 |
delayMicroseconds (40); |
на 40 микросекунд
тем самым мы послали сигнал на сенсор, который говори, что мы готовы начать принимать данные.
1 |
pinMode (dataPin, INPUT); |
устанавливаем штырёк в режим приёма данных
1 |
pulseIn (dataPin, HIGH); |
Пропускаем первый сигнал. (LOW-HIGH);
1 |
unsigned int timeout = 10000; |
опередляем переменную timeout в которой будет хранится кол-во итераций циклов, что бы не попасть в бесконечную петлю.
Выставляем максимальное кол-во циклов, ожидания перехода из состояния штырька из LOW в HIGH.
1 2 3 4 |
while (digitalRead(dataPin) == LOW){ if (timeout==0) return -1; timeout--; } |
Непосредственно само ожидание, пока значение штырька равно LOW выполнять цикл, при этом каждый цикл значение timeout уменьшается на 1:
1 |
timeout--; |
а если щётчик достиг 0, то прерываем работу функции со значением -1
1 |
if (timeout==0) return -1; |
далее делаем замер времени:
1 |
unsigned long t = micros(); |
в переменной t — сохранится количество микросекунд пройденное с момента загрузки платы.
далее по принципу предыдущего цикла ждём, пока закончится идти сигнал HIGH
1 2 3 4 5 |
timeout =10000; while (digitalRead(dataPin) == HIGH){ if (timeout==0) return -1; timeout--; } |
Далее будет идти интересная строчка:
1 |
if ((micros()-t)>40) recvBuffer[idx] |= (1 <<cnt); |
Разберём её по подробнее:
(micros()-t)>40 здесь мы опять смотрим сколько прошло времени с момента запуска платы (micros()) и вычитаем из неё ранее сохранённое значение времени. если это значение больше 40 микросекунд, будем считать, что это 1. На этом шаге есть нюанс, раз в примерно 70 минут это значение сбрасывается. И если мы попали именно в этот промежуток времени то вместо 1 может получится 0.
Вы ражением (1 <<cnt), мы делаем побитовый сдвиг в лево иденицы на номер текущего бита. Например если cnt=7 (это первоначальное значение которое мы задали), то если у нас было &0000001(двоичная запись числа), то после операции 1<<7 у нас получится &1000000. Соответсвенно: (1<<6) = (&00000001<<6) = 0100000.
recvBuffer[idx] |= (1 <<cnt) можно записать, как recvBuffer[idx] =recvBuffer[idx]|(1 <<cnt)
Знак | является по битовым или на всякий случай:
a | b | aVb |
---|---|---|
1 | 1 | 1 |
1 | 0 | 1 |
0 | 1 | 1 |
0 | 0 | 0 |
побитовое или будет сравнивать двоичные записи данных чисел по битам и выполнять над ними операцию или, так например для 12|10=(&1100 | &1010)=&1110=14
ну и соответственно recvBuffer[idx] будет являться текущим байтом данных.
Соответственно строчка:
1 |
if ((micros()-t)>40) recvBuffer[idx] |= (1 <<cnt); |
делает следующую операцию, если время сигнала больше 40 микросекунд, то текущий бит буфера изменяется на 1 вместо 0. (не забываем, что в начале мы его обнулили).
Далее:
1 2 3 4 5 |
if (cnt == 0) { cnt = 7; idx++; } |
Если мы закончили заполнять байт, переходим к следующему. Если же мы не дошли до конца:
1 |
else cnt--; |
переходим к следующему биту(то есть предыдущему… 🙂
Здесь заканчивается основной цикл, тоесть мы получили 40 бит (for (int i=0; i<40; i++)).
После этого разбираем полученный буфер на значения, так:
1 |
humidity = recvBuffer[0]; |
В нулевом(первом) бите будет находится влажность мы передаём её в глобальную переменную humidity
1 |
temperature = recvBuffer[2]; |
Во втором (третьем) бите будет хранится значение температуры и мы её передаём в глобальную переменную temperature.
Далее проверяем контрольную сумму:
1 2 |
uint8_t sum = recvBuffer[0]+recvBuffer[2]; if (recvBuffer[4] != sum) return -2; |
Если recvBuffer[4] = recvBuffer[0]+recvBuffer[2] то всё хорошо. А если нет, функция закончится и выведет значение -2.
1 |
return 0; |
Если всё хорошо то выводим 0.
Ура! функция закончилась, осталось ей воспользоваться в главном цикле loop:
1 2 3 4 5 |
int ret = readDTH11(); if(ret !=0) Serial.println(ret); Serial.print("Влажность: ");Serial.print(humidity);Serial.println(" %"); Serial.print("Температура: ");Serial.print(temperature);Serial.println(" С"); delay(2000); |
1 |
int ret = readDTH11(); |
Здесь мы запускаем функцию readDTH11() и её вывод записываем в переменную ret.
1 |
if(ret !=0) Serial.println(ret); |
Если мы не дошли до конца функции и вывалились по середине, то выводим код ошибки на компьютер.
1 2 |
Serial.print("Humidity: ");Serial.print(humidity);Serial.println(" %"); Serial.print("Temperature: ");Serial.print(temperature);Serial.println(" C"); |
а так же выводим текущие значения влажности и температуры
1 |
delay(2000); |
Делаем задержку между замерами.. датчик не может делать замеры чаще чем раз в 2 секунды.
Скетч закончился.
Видео процесса:
Другие статьи по программированию плат Arduino можно посмотреть здесь.
Комментарии: