본문 바로가기

라즈베리파이 5

Raspberry pi 5 DHT11 온도 습도 센서 실습

반응형

 

또 한다. 한 것 또 하고 또 하고...

 

WiringPi GPIO 라이브러리는 라즈베리파이5에서도 잘 작동한다. 위대한 작품은 아주 꿋꿋하게 살아남는다.

 

 

1. 가장 유명한 GPIO 제어 라이브러리인 WiringPi 를 설치한다. Debian Package 로 설치한다. 가장 쉽다.

 

# fetch the source
$sudo apt install git
$git clone https://github.com/WiringPi/WiringPi.git
$cd WiringPi

# build the package
$./build debian
$mv debian-template/wiringpi_3.6_arm64.deb .

# install it
$sudo apt install ./wiringpi_3.6_arm64.deb 

 

위 과정을 따라하면 아래와 같은 화면으로 된다. 참고삼아 본다.

 

pi@raspberrypi:~ $ sudo apt install git
Reading package lists... Done
Building dependency tree... Done
Reading state information... Done
git is already the newest version (1:2.39.2-1.1).
0 upgraded, 0 newly installed, 0 to remove and 0 not upgraded.
pi@raspberrypi:~ $ git clone https://github.com/WiringPi/WiringPi.git
Cloning into 'WiringPi'...
remote: Enumerating objects: 2374, done.
remote: Counting objects: 100% (1251/1251), done.
remote: Compressing objects: 100% (367/367), done.
remote: Total 2374 (delta 952), reused 1082 (delta 856), pack-reused 1123
Receiving objects: 100% (2374/2374), 1010.53 KiB | 10.31 MiB/s, done.
Resolving deltas: 100% (1583/1583), done.
pi@raspberrypi:~ $ cd WiringPi
pi@raspberrypi:~/WiringPi $ ./build debian
version:3.6 architecture:arm64
[Compile] wiringPi.c
[Compile] wiringSerial.c
[Compile] wiringShift.c
[Compile] piHiPri.c
[Compile] piThread.c
[Compile] wiringPiSPI.c
[Compile] wiringPiI2C.c
[Compile] softPwm.c
[Compile] softTone.c
[Compile] mcp23008.c
[Compile] mcp23016.c
[Compile] mcp23017.c
[Compile] mcp23s08.c
[Compile] mcp23s17.c
[Compile] sr595.c
[Compile] pcf8574.c
[Compile] pcf8591.c
[Compile] mcp3002.c
[Compile] mcp3004.c
[Compile] mcp4802.c
[Compile] mcp3422.c
[Compile] max31855.c
[Compile] max5322.c
[Compile] ads1115.c
[Compile] sn3218.c
[Compile] bmp180.c
[Compile] htu21d.c
[Compile] ds18b20.c
[Compile] rht03.c
[Compile] drcSerial.c
[Compile] drcNet.c
[Compile] pseudoPins.c
[Compile] wpiExtensions.c
[Compile] wiringPiLegacy.c
[Link (Dynamic)]
[Install Headers: deb]
[Install Dynamic Lib: deb]
install -m 0755 -d                                                      /home/pi                             /WiringPi/debian-template/wiringPi/usr/lib
install -m 0755 libwiringPi.so.3.6                              /home/pi/WiringP                             i/debian-template/wiringPi/usr/lib/libwiringPi.so.3.6
ln -sf /home/pi/WiringPi/debian-template/wiringPi/usr/lib/libwiringPi.so.3.6   /                             home/pi/WiringPi/debian-template/wiringPi/usr/lib/libwiringPi.so
[Compile] ds1302.c
[Compile] maxdetect.c
[Compile] piNes.c
[Compile] gertboard.c
[Compile] piFace.c
[Compile] lcd128x64.c
[Compile] lcd.c
[Compile] scrollPhat.c
[Compile] piGlow.c
[Link (Dynamic)]
[Install Headers: deb]
[Install Dynamic Lib: deb]
install -m 0755 -d                                                      /home/pi                             /WiringPi/debian-template/wiringPi/usr/lib
install -m 0755 libwiringPiDev.so.3.6                           /home/pi/WiringP                             i/debian-template/wiringPi/usr/lib/libwiringPiDev.so.3.6
ln -sf /home/pi/WiringPi/debian-template/wiringPi/usr/lib/libwiringPiDev.so.3.6/                             home/pi/WiringPi/debian-template/wiringPi/usr/lib/libwiringPiDev.so
[Compile] gpio.c
[Compile] readall.c
[Link]
[Install: deb]
dpkg-deb: building package 'wiringpi' in 'wiringPi.deb'.
dpkg-name: info: moved 'wiringPi.deb' to './wiringpi_3.6_arm64.deb'
pi@raspberrypi:~/WiringPi $ mv debian-template/wiringpi-3.0-1.deb .

pi@raspberrypi:~/WiringPi $ mv debian-template/wiringpi_3.6_arm64.deb .


pi@raspberrypi:~/WiringPi $ sudo apt install ./wiringpi_3.6_arm64.deb
Reading package lists... Done
Building dependency tree... Done
Reading state information... Done
Note, selecting 'wiringpi' instead of './wiringpi_3.6_arm64.deb'
The following NEW packages will be installed:
  wiringpi
0 upgraded, 1 newly installed, 0 to remove and 0 not upgraded.
Need to get 0 B/65.2 kB of archives.
After this operation, 0 B of additional disk space will be used.
Get:1 /home/pi/WiringPi/wiringpi_3.6_arm64.deb wiringpi arm64 3.6 [65.2 kB]
Selecting previously unselected package wiringpi.
(Reading database ... 228617 files and directories currently installed.)
Preparing to unpack .../wiringpi_3.6_arm64.deb ...
Unpacking wiringpi (3.6) ...
Setting up wiringpi (3.6) ...
Processing triggers for man-db (2.11.2-2) ...
N: Download is performed unsandboxed as root as file '/home/pi/WiringPi/wiringpi                             _3.6_arm64.deb' couldn't be accessed by user '_apt'. - pkgAcquire::Run (13: Perm                             ission denied)
pi@raspberrypi:~/WiringPi $

 

다 설치하였다면 아래 명령어로 확인한다.

 

$gpio readall

 

아주 아름다운 화면을 볼 수 있다.

 

pi@raspberrypi:~/WiringPi $ gpio readall
 +-----+-----+---------+------+---+---Pi 5---+---+------+---------+-----+-----+
 | BCM | wPi |   Name  | Mode | V | Physical | V | Mode | Name    | wPi | BCM |
 +-----+-----+---------+------+---+----++----+---+------+---------+-----+-----+
 |     |     |    3.3v |      |   |  1 || 2  |   |      | 5v      |     |     |
 |   2 |   8 |   SDA.1 | ALT3 | 1 |  3 || 4  |   |      | 5v      |     |     |
 |   3 |   9 |   SCL.1 | ALT3 | 1 |  5 || 6  |   |      | 0v      |     |     |
 |   4 |   7 | GPIO. 7 |   -  | 0 |  7 || 8  | 0 |  -   | TxD     | 15  | 14  |
 |     |     |      0v |      |   |  9 || 10 | 0 |  -   | RxD     | 16  | 15  |
 |  17 |   0 | GPIO. 0 |   -  | 0 | 11 || 12 | 0 |  -   | GPIO. 1 | 1   | 18  |
 |  27 |   2 | GPIO. 2 |   -  | 0 | 13 || 14 |   |      | 0v      |     |     |
 |  22 |   3 | GPIO. 3 |   -  | 0 | 15 || 16 | 0 |  -   | GPIO. 4 | 4   | 23  |
 |     |     |    3.3v |      |   | 17 || 18 | 0 |  -   | GPIO. 5 | 5   | 24  |
 |  10 |  12 |    MOSI | ALT0 | 0 | 19 || 20 |   |      | 0v      |     |     |
 |   9 |  13 |    MISO | ALT0 | 0 | 21 || 22 | 0 |  -   | GPIO. 6 | 6   | 25  |
 |  11 |  14 |    SCLK | ALT0 | 0 | 23 || 24 | 1 | OUT  | CE0     | 10  | 8   |
 |     |     |      0v |      |   | 25 || 26 | 1 | OUT  | CE1     | 11  | 7   |
 |   0 |  30 |   SDA.0 |   IN | 1 | 27 || 28 | 1 | IN   | SCL.0   | 31  | 1   |
 |   5 |  21 | GPIO.21 |   -  | 0 | 29 || 30 |   |      | 0v      |     |     |
 |   6 |  22 | GPIO.22 |   -  | 0 | 31 || 32 | 0 |  -   | GPIO.26 | 26  | 12  |
 |  13 |  23 | GPIO.23 |   -  | 0 | 33 || 34 |   |      | 0v      |     |     |
 |  19 |  24 | GPIO.24 |   -  | 0 | 35 || 36 | 0 |  -   | GPIO.27 | 27  | 16  |
 |  26 |  25 | GPIO.25 |   -  | 0 | 37 || 38 | 0 |  -   | GPIO.28 | 28  | 20  |
 |     |     |      0v |      |   | 39 || 40 | 0 |  -   | GPIO.29 | 29  | 21  |
 +-----+-----+---------+------+---+----++----+---+------+---------+-----+-----+
 | BCM | wPi |   Name  | Mode | V | Physical | V | Mode | Name    | wPi | BCM |
 +-----+-----+---------+------+---+---Pi 5---+---+------+---------+-----+-----+
pi@raspberrypi:~/WiringPi $ gpio -V
2

 

 

온 습도 센서 연결은 아래와 같다.

 

 

 

코드는 아래와 같다.

 

#include <wiringPi.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>

#define MAXTIMINGS 83
#define DHTPIN 7

int dht11_dat[5] = {0, } ;

void read_dht11_dat()
{
  uint8_t laststate = HIGH ;
  uint8_t counter = 0 ;
  uint8_t j = 0, i ;
  uint8_t flag = HIGH ;
  uint8_t state = 0 ;
  float f ;
  dht11_dat[0] = dht11_dat[1] = dht11_dat[2] = dht11_dat[3] = dht11_dat[4] = 0 ;

  pinMode(DHTPIN, OUTPUT) ;
  digitalWrite(DHTPIN, LOW) ;
  delay(18) ;

  digitalWrite(DHTPIN, HIGH) ;
  delayMicroseconds(30) ;
  pinMode(DHTPIN, INPUT) ;

  for (i = 0; i < MAXTIMINGS; i++) {
    counter = 0 ;
    while ( digitalRead(DHTPIN) == laststate) { 
      counter++ ;
      delayMicroseconds(1) ;
      if (counter == 200) break ;
    }
    laststate = digitalRead(DHTPIN) ;
    if (counter == 200) break ; // if while breaked by timer, break for
    if ((i >= 4) && (i % 2 == 0)) {
      dht11_dat[j / 8] <<= 1 ;
      if (counter > 20) dht11_dat[j / 8] |= 1 ;
      j++ ;
    }
  }

  if ((j >= 40) && (dht11_dat[4] == ((dht11_dat[0] + dht11_dat[1] + dht11_dat[2] + dht11_dat[3]) & 0xff))) {
    printf("humidity = %d.%d %% Temperature = %d.%d *C \n", dht11_dat[0], dht11_dat[1], dht11_dat[2], dht11_dat[3]) ;
  }
  else printf("Data get failed\n") ;
}

int main(void)
{
  printf("dht11 Raspberry pi\n") ;
  if (wiringPiSetup() == -1) exit(1) ;

  while (1) {
    read_dht11_dat() ;
    delay(1000) ;
  }
  return 0 ;
}

 

 

코드를 입력했다면

컴파일은 $gcc -o 출력 실행파일이름 소스이름 -lwiringPi 하면 된다. dash 마이너스 영어 o 다. 대문자 P에 주의

 

$gcc -o dht11_cprog dht11_cprog.c -lwiringPi

 

실행은 점 슬래시 실행파일 이름이다.

 

$./dht11_cprog

 

맨 아래 실행 화면이다. data get failed는 무시한다.

 

코드 상세한 설명을 아래에 적는다.

 


코드 설명

uint8_t laststate = HIGH ;
uint8_t counter = 0 ;
uint8_t j = 0, i ;
uint8_t flag = HIGH ;
uint8_t state = 0 ;

이번에는 조금 특이하게 변수가 uint8_t 라고 지정되었습니다.

평소에 쓰던 char, int등과는 차이점이 있습니다.

uint8_t는 unsigned 8 bit type 변수를 의미합니다.

char, int가 word의 크기에 따라 달라지는데 이를 방지하기 위하여 쓰는 형식입니다.

user-defined type이라고 부르며 stdint.h에 정의되어 있습니다.

pinMode(DHTPIN, OUTPUT) ;
digitalWrite(DHTPIN, LOW) ;
delay(18) ;

digitalWrite(DHTPIN, HIGH) ;
delayMicroseconds(30) ;

DHT11은 여타 센서와 다르게 신호의 길이로 데이터를 내보냅니다.

처음에 신호선으로 LOW를 18ms동안, 그리고 20 ~ 40us동안 HIGH신호를 주면 start 신호입니다.
여기서는 중간값인 30us를 사용하였습니다.

자 이제 DHT11에서 신호를 출력하기 시작할 것입니다.

pinMode(DHTPIN, INPUT) ;

라즈베리는 받아야 하는 입장이므로 pinMode를 이용하여 INPUT으로 변경해줍니다.

for (i = 0; i < MAXTIMINGS; i++) {
counter = 0 ;
while ( digitalRead(DHTPIN) == laststate) {
counter++ ;
delayMicroseconds(1) ;
if (counter == 200) break ;
}
laststate = digitalRead(DHTPIN) ;
if (counter == 200) break ;
if ((i >= 4) && (i % 2 == 0)) {
dht11_dat[j / 8] <<= 1 ;
if (counter > 20) dht11_dat[j / 8] |= 1 ;
j++ ;
}
}

이번 소스코드의 핵심입니다.

먼저 MAXTIMINGS만큼 for문을 반복하며 값을 읽어들입니다.

이때 MAXTIMINGS는 83의 값을 가지는데 이는

40개의 데이터 비트 * 2(LOW, HIGH가 반복되어야 데이터 1개를 읽을 수 있으므로)

+ 3개의 처음 비트 - Response signal, pullup ready signal, 처음 start to transmit bit 해서 3개입니다.

while ( digitalRead(DHTPIN) == laststate) {

이 부분에서 laststate와 반대가 되었을 경우(비트가 HIGH에서 LOW로 혹은 LOW에서 HIGH로 바뀌었을 경우를 인식합니다.

1us단위로 count를 증가시켜 반전되는데 시간이 얼마나 걸렸는지 짐작합니다.

다만 최대 시간을 100us로 지정하여 이 시간동안 바뀌지 않을 경우
에러로 인식하고 break와 뒤에 나오는 if문을 이용하여 에러를 검출합니다.

(i >= 4) && (i % 2 == 0)

위 코드는 처음 3비트를 버린상태로, 그리고 짝수번째 비트인지를 확인합니다.

홀수번째는 LOW상태의 start to transmit bit이므로 필요가 없기 때문입니다.

조건이 부합할 때 i 를 8로 나눈곳에 데이터를 씁니다.

앞에서 비트가 반전될 때 까지 count를 측정하였는데 이게 20이상이면 1이라고 값을 씁니다.

0은 이미 초기화가 되어있으므로 무시합니다.

그러면 왜 20이냐구요?

데이터시트에 보면 26 ~ 28us정도를 data 0으로 지정하고 있습니다.

다만 count연산과 if문연산 속도가 있기에 26정도를 바로 써주게되면 지연이 일어나서 이미 늦어버리게 됩니다.

그렇기에 20이라는 값을 기준으로 비교해주는 것입니다.

if ((j >= 40) && (dht11_dat[4] == ((dht11_dat[0] + dht11_dat[1] + dht11_dat[2] + dht11_dat[3]) & 0xff))) {
printf("humidity = %d.%d %% Temperature = %d.%d *C \n", dht11_dat[0], dht11_dat[1], dht11_dat[2], dht11_dat[3]) ;
}
else printf("Data get failed\n") ;

값이 다 쓰였으면 0, 1, 2, 3번값을 더한 값과 마지막 4번값을 비교합니다.

마지막 4번째 8비트는 패리티 비트라고 부릅니다.

값이 잘 전송되었는지를 확인하기 위한 비트입니다.

이와 비교하여 잘 나왔다면 값 출력을, 아니라면 Data get failed라는 문장을 출력합니다.

main에서 1번씩 함수를 호출하여 실행하는 형태입니다.

코딩이 끝났으면 실행해 보도록 하겠습니다.

dht.c 라고 저장한 후 gcc를 이용하여 컴파일하고 실행합니다.

$gcc dht.c -o dht -lwiringPi
$sudo ./dht

 

 

동작 결과는 아래와 같다.

 

pi@raspberrypi:~/all_sensortest $ ./dht11_cprog
dht11 Raspberry pi
humidity = 60.0 % Temperature = 26.6 *C
Data get failed
humidity = 61.0 % Temperature = 26.2 *C
Data get failed
humidity = 62.0 % Temperature = 26.2 *C
Data get failed
humidity = 62.0 % Temperature = 26.2 *C
Data get failed
humidity = 62.0 % Temperature = 26.3 *C
Data get failed
^C
pi@raspberrypi:~/all_sensortest $

 

 

참고 문서

C언어로 구현한 DHT11 실습 코드

 

반응형

더욱 좋은 정보를 제공하겠습니다.~ ^^