Pull to refresh

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

DIY
На Хабре уже публиковалась статья о подключении датчика температуры 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 %

или DHT22 подключен к 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.
Tags:
Hubs:
Total votes 34: ↑27 and ↓7 +20
Views 116K
Comments Comments 33