Raspberry Pi: измеряем влажность и температуру с помощью DHT11/DHT22

от автора

На Хабре уже публиковалась статья о подключении датчика температуры DS18B20 к Raspberry Pi. В нашем новом проекте, который строится на Raspberry Pi, понадобилось измерять не только температуру, но и влажность. Я расскажу, как подключить недорогие китайские датчики влажности к Raspberry Pi. Просмотрев несколько вариантов различных датчиков, остановился на двух наиболее массовых на рынке датчиков. Это DHT11, который привлек своей ценой $3 (с доставкой) и датчик DHT22 (около $10 с доставкой).

Основная разница между ними в диапазоне температур и точности измерения:

DHT11

  • Влажность 20-80% +- 5%
  • Температура 0-50 °С+- 2%
  • Данные считываются в целых единицах.

DHT22

  • Влажность 0-100% +- 5%
  • Температура -40-125 °С +- 0.5%
  • Данные считываются с точностью до десятых.

Подключение

Подключение к Raspberry Pi особой сложности не представляет: подключаем + от датчика к +5V на Raspberry Pi, "-" — к земле, и сигнал к одному из GPIO выводов.

Устанавливаем ПО

Оба датччика используют свой протокол вместо стандартизированного 1 wire, поэтому программное обеспечение для снятия показаний датчика тоже будет отличаться.

Сначала установим библиотеку на С для работы с GPIO www.open.com.au/mikem/bcm2835/index.html

wget http://www.open.com.au/mikem/bcm2835/bcm2835-1.15.tar.gz tar xzf bcm2835-1.15.tar.gz cd bcm2835-1.15/ ./configure make make install 

Для чтения данных с датчика за основу был взят файл на С Adafruit_DHT_Driver. Без внесения некоторых изменений работать с DHT22, этот код отказывался, пришлось немного изменить.

Поэтому привожу модифицированную версию.

Файл readDHT.c

//  How to access GPIO registers from C-code on the Raspberry-Pi //  Example program //  15-January-2012 //  Dom and Gert //   // Access from ARM Running Linux  #define BCM2708_PERI_BASE        0x20000000 #define GPIO_BASE                (BCM2708_PERI_BASE + 0x200000) /* GPIO controller */   #include <stdio.h> #include <string.h> #include <stdlib.h> #include <dirent.h> #include <fcntl.h> #include <assert.h> #include <unistd.h> #include <sys/mman.h> #include <sys/types.h> #include <sys/stat.h> #include <bcm2835.h> #include <unistd.h>  #define MAXTIMINGS 100  #define DHT11 11 #define DHT22 22 #define AM2302 22  int readDHT(int type, int pin);  int main(int argc, char **argv) {  if (!bcm2835_init())        return 1;   if (argc != 3) {        printf("usage: %s [11|22|2302] GPIOpin#\n", argv[0]);        printf("example: %s 2302 4 - Read from an AM2302 connected to GPIO #4\n", argv[0]);        return 2;  }  int type = 0;  if (strcmp(argv[1], "11") == 0) type = DHT11;  if (strcmp(argv[1], "22") == 0) type = DHT22;  if (strcmp(argv[1], "2302") == 0) type = AM2302;  if (type == 0) {        printf("Select 11, 22, 2303 as type!\n");        return 3;  }   int dhtpin = atoi(argv[2]);   if (dhtpin <= 0) {        printf("Please select a valid GPIO pin #\n");        return 3;  }    printf("Using pin #%d\n", dhtpin);  readDHT(type, dhtpin);  return 0;  } // main   int bits[250], data[100]; int bitidx = 0;  int readDHT(int type, int pin) {  int counter = 0;  int laststate = HIGH;  int j=0;  int i=0;  // Set GPIO pin to output  bcm2835_gpio_fsel(pin, BCM2835_GPIO_FSEL_OUTP);  bcm2835_gpio_write(pin, HIGH);  usleep(100);  bcm2835_gpio_write(pin, LOW);  usleep(20000);  bcm2835_gpio_fsel(pin, BCM2835_GPIO_FSEL_INPT);   data[0] = data[1] = data[2] = data[3] = data[4] = 0;  // read data!   for (i=0; i< MAXTIMINGS; i++) {     counter = 0;     while ( bcm2835_gpio_lev(pin) == laststate) {        counter++;        nanosleep(1);           // overclocking might change this?        if (counter == 100)          break;     }     laststate = bcm2835_gpio_lev(pin);     if (counter == 100) break;     bits[bitidx++] = counter;      if ((i>3) && (i%2 == 0)) {      // shove each bit into the storage bytes      data[j/8] <<= 1;      if (counter > 16)        data[j/8] |= 1;      j++;     }  }   #ifdef DEBUG  for (int i=3; i<bitidx; i+=2) {     printf("bit %d: %d\n", i-3, bits[i]);     printf("bit %d: %d (%d)\n", i-2, bits[i+1], bits[i+1] > 15);  } #endif   printf("Data (%d): 0x%x 0x%x 0x%x 0x%x 0x%x\n", j, data[0], data[1], data[2], data[3], data[4]);   if ((j >= 39) &&      (data[4] == ((data[0] + data[1] + data[2] + data[3]) & 0xFF)) ) {     // yay!     if (type == DHT11)  printf("Temp = %d *C, Hum = %d \%\n", data[2], data[0]);     if (type == DHT22) {  float f, h;        h = data[0] * 256 + data[1];        printf ("%s\n",h);        h /= 10;         f = (data[2] & 0x7F)* 256 + data[3];        f /= 10.0;        if (data[2] & 0x80)  f *= -1;        printf("Temp =  %.1f *C, Hum = %.1f \%\n", f, h);     }     return 1;  }   return 0; } 

Компилируем

gcc readDHT.c -lbcm2835 -lrt -o readDHT 

Пробуем считывать данные

./readDHT {тип датчика 11 или 22}  {номер GPIO вывода Raspberry PI} 

Например DHT11 подключен к GPIO4

root@raspberrypi /var/www/application/scripts/DHT # ./readDHT 11 4 Using pin #4 Data (40): 0x23 0x0 0x17 0x0 0x3a Temp = 23 *C, Hum = 35 % 

или DHT11 подключен к GPIO17

root@raspberrypi /var/www/application/scripts/DHT # ./readDHT 22 17 Using pin #17 Data (40): 0x1 0x75 0x0 0xea 0x60 Temp = 23.4 *C, Hum = 37.3 % 

При реализации вызова readDHT нужно учитывать, что если скорость обращения будет высокая ( чаще чем раз в секунду) вместо данных Вы будете получать CRC Error.

Сохранение данных с датчиков

Полученные данные нужно куда-то сохранять, приведу пример как можно сохранять данные в Google.docs. Т.к. мне ближе PHP, сохранение сделал на PHP с использованием Zend_Gdata_Spreadsheets.

imageПодготовим google docs. Создадим новую таблицу и дадим ей имя. Дадим имена колонкам первой datetime, второй temperature, третей humidity. Из адресной строки скопируем id нашей таблицы, по нему будет производиться обращение к таблице.

Устанавливаем php:

pi@raspberrypi ~ $ sudo apt-get php5 php5-curl unzip 

Создадим папку для нашего проекта:

pi@raspberrypi ~ $ mkdir /home/pi/dht 

Загрузим и распакуем Zend Framework:

pi@raspberrypi ~ $ mkdir /home/pi/dht/library pi@raspberrypi ~ $ cd /home/pi/dht/library pi@raspberrypi ~ $ wget http://packages.zendframework.com/releases/ZendFramework-1.12.0/ZendFramework-1.12.0-minimal.zip pi@raspberrypi ~ $ unzip ZendFramework-1.12.0-minimal.zip pi@raspberrypi ~ $ ln -s ZendFramework-1.12.0-minimal/library/Zend Zend 
DHTtoGoogleDocs.php

<?php  ini_set("include_path",get_include_path().':/home/pi/dht/library');  require_once 'Zend/Loader.php'; Zend_Loader::loadClass('Zend_Gdata_Spreadsheets'); Zend_Loader::loadClass('Zend_Gdata_ClientLogin');  define('GDATA_USER','googleusername'); define('GDATA_PASSWORD','google user password'); define('GDATA_SPREADSHEET_KEY','spreadsheetkey from url'); define('GDATA_WORKSHEET_ID','od6');  try {    $t = new Temperature_DHT();   // get data from sensor   $data = $t->getData(11,4);    $service = Zend_Gdata_Spreadsheets::AUTH_SERVICE_NAME;   $client = Zend_Gdata_ClientLogin::getHttpClient(GDATA_USER, GDATA_PASSWORD, $service);   $service = new Zend_Gdata_Spreadsheets($client);    // add row to spreadsheet   $row = array(           'datetime'=>date("Y-m-d H:i:s"),           'temperature'=>$data[0],           'humidity'=>$data[1],   );   $service->insertRow($row, GDATA_SPREADSHEET_KEY, GDATA_WORKSHEET_ID);  } catch (Exception $e) {   die( $e->getMessage() ); }    class Temperature_DHT  {        private $_maxFailCount=5;         public function getData($type, $pin)        {                 $count = 0;                 while ($count<=$this->_maxFailCount)                {                        $count++;                        $filename = '/home/pi/dht/readDHT';                         $out = exec   ("$filename $type $pin");                        if(preg_match("'^Temp = ([0-9\.]+) \*C, Hum = ([0-9\.]+) %'", $out,$result))                        {                                  return array($result[1],$result[2]);                        }                 }         } }  

Проверяем работу файла, запускаем:

pi@raspberrypi ~ $  php DHTtoGoogleDocs.php 

Осталось только поставить вызов «php DHTtoGoogleDocs.php» в cron.

В качестве эксперимента данные собирались почти месяц с интервалом в 10 минут в одном помещении и сохранялись в google docs, датчики были расположены рядом. Кому интересно можете посмотреть на разброс значений.
Из данных, которые собирали оба дачика, можно сказать, что если нужно просто понять меняется ли влажность, то датчика DHT11 вполне достаточно. Но, если Вы хотите, что бы отображаемое значение было близко к значению бытовой метеостанции, то лучше использовать DHT22.

ссылка на оригинал статьи http://habrahabr.ru/post/167459/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *