[회원 기고] 라즈베리파이를 활용한 서버실 온/습도 제어 관리



라즈베리파이를 활용한 서버실 온/습도 제어 관리




 항온 항습 관리 시스템을 사용할  있다면,  좋겠지만... 

고가의 항온/항습기를 도입하기 위한 비용 문제도 있지만 좁은 서버실에 항온/항습기를 도입하자면 배보다 배꼽이  커져 버리게 되어... 서버실 온/습도 관리에 어려움을 겪는 곳이 많으리라 생각됩니다. 


저렴한 비용에... 입맛대로 구성할  있는 직접 제작한 온/습도 제어 관리 시스템을 소개합니다. 

고가의 항온/항습기보다  많은 기능과 추가적인 다양한 기능들을 덧붙일  있는 유연성까지 갖추고 있는 아껴둔 자작 솔루션을 공개합니다. 


구성품 : 라즈베리 파이, DHT-22 온습도 센서, 릴레이, 10KΩ 저항, 전기 콘센트, 플러그, 땜납, 인두,... ( 전기가 공급되면 자동 작동하는 ) 가습기, 제습기, 냉방기, 온열기 활용 가능



[ 작업을 위한 구성품  도구 사진 ]




[제품 구성도]



앞의 제품 구성도를 유심히 보았다면... 

DHT22 온/습도 연결에 있어서 특이한 점을 찾을  있을 걸로 보이는데요. 


DHT22의 Vcc를 라즈베리 파이의 Vcc 핀이 아닌, Data처럼, GPIO 핀에 연결하였다는 점이 되겠는데... DHT22를 사용해  경험이 있다면, 아마도 잘못 연결한 사용법이라 생각할 수도 있을거라 생각되네요. 


DHT22를 실제 업무에 사용해  경우라면, DHT22가 안정성에 문제가 있어 다운되어 버리는 치명적인 결함이 있다는  파악하고 있지 않을까 싶은데... ( 사용하는 부품에 따라  시간에서 수개월까지 차이는 있었지만 오래 가도 1~2개월에  번은 반드시 다운이 되어 버리더군요. ) 


DHT22의 이런 치명적인 문제점을 해결하기 위해 고뇌한 끝에 찾아낸 혼자만의 비법(?)이라   있겠네요. 여러 전문가들에게 공식 질의를  보기도 했지만, 부품 제조사에서 명시한 규격 방식으로만 사용할 것을 권장할 뿐, 아무도 다른 해결 방안을 제시해 주지는 않더군요. 해외 사이트에서도 불안정한 문제점에 대한 내용은 간혹 찾을  있었지만, 어떤 사이트에서도 해결 방법을 제시해둔 곳은 없었고요. 


 당시 진행했던 프로젝트의 성패와 관련된 문제였기 때문에… 혼자서  주간 머리를 싸매고 고심 고심한 끝에… DHT22가 소모하는 전류량과 GPIO 포트가 수용할  있는 전체 전류량을 계산해서, 이렇게 DHT22 센서를    연결해도 작동하는  문제가 되지 않는다는 판단으로 결정한 독특한 연결 법이라   있겠고… 현재 10개 이상의 제품을 동일 방식으로 만들어 수년간 사용해 오고 있지만 다른 문제없이  작동하고 있는 상태이네요. 


DHT22의 불안정한 문제를 해결할  있었던 획기적인 기법으로 검증된 상황이 되겠네요. 



[온/습도 모니터링  관리용 GUI 인터페이스 화면]




[GUI 인터페이스 화면 설명]


 간단한 관리용 인터페이스 화면으로 특별히 설명하지 않아도 구성, 사용법을 추정할  있을거라 생각되겠는데요. 콘센터1  콘센터2에 가습기, 제습기, 난방기, 냉방기  어떤 제품이 연결되었는지, 작동  중지되기 위한 온/습도 범위에 대한 정보와 측정한 온/습도 정보가 저장될 경로 정보, 측정할 주기 시간을 설정한다거나, 측정한 온/습도 정보를 화면에 표시해 주고, 가습기, 제습기, 냉방기, 난방기가 작동/중지되는 값을 설정할  있게 해주는 GUI 인터페이스 프로그램이 되겠습니다. 


GUI Interface 화면은 파이썬으로 만든 프로그램이라… Linux, Window, MAC 등의 OS에 관계 없이 사용할  있다는  장점이라   있을  같고… Text file로 특정 폴더에 자동 생성되는 온/습도 파일을 표시해 주는 방식이기 때문에, 추가 프로그램을 덧붙여 온/습도 정보를 DB에 넣어 관리한다거나, SMS 또는 메일 등으로 알림을 보낸다거나 Kibana 등의 시각화 툴을 이용하여 멋지게 표현하는 것도 어렵지 않게 가능하리라 생각되고요. 


제작한 실물은 아래의 이미지와 같이 구성하였습니다. 케이블 연장을 유동적으로 길게 또는 짧게 사용할  있게 하기 위해 UTP 케이블과 I 커플러를 사용하여 만들었고요. 적정한 길이의 UTP 케이블을 간단하게 교체 연결만 한다면 가습기, 제습기 등의 기기를 원하는 위치에두고 작동시킬  있겠고요. 센서의 민감성으로 인해 DHT22와 라즈베리 파이와의 거리에는 제약이 있어, 문제가 생기지 않는 짧은 길이 ( 10~30cm )  고정시켰지만, 라즈베리파이와 릴레이 간의 거리는 수십 미터 정도로 충분히 길게해서 테스트해도 문제가 없음을 확인하였기 때문에,  공간의 장비실이라도 거리에 제한 없이 사용할  있을 걸로 생각되네요. 



[실제 제작한 구성품 사진]



[제/가습기  냉/난방기 하드웨어 제어 방식 설명] 


 전기 콘센트에 가습기, 제습기, 난방기, 냉방기 중, 2가지 종류의 기기를 릴레이로 제어되는 콘센트에 꽂아 두면, 온/습도에 따라 설정된 환경 값에 의해 기기를 작동시키거나 중지되게 하여 일정한 온/습도가 유지될  있게 됩니다. 


여름에는 제습기와 냉방기, 겨울에는 가습기와 난방기를 연결해 두고 온/습도가 자동 제어되게  수도 있겠고... 에어컨으로 온도를 관리하고 있어, 온도 제어가 필요하지 않다면 제습기와 가습기를 각각 연결해서 고습일  제습기가, 건조할때는 가습기가 작동되게  수도 있겠고요. 



[환경 구성을 위한 사전 설치 프로그램] 


- git에 올려진 WiringPI를 다운 받기 위해 git 설치 

pi@raspberrypi:~ $ sudo apt-get install git-core –y 


- 라즈베리파이에서 GPIO 핀을 사용하기 위해 WiringPI 설치 pi@raspberrypi:~ $ git clone https://github.com/WiringPi/WiringPi 


- 다운 받은 WiringPi Library를 Compile  설치. pi@raspberrypi:~ $ cd WiringPi pi@raspberrypi:~/WiringPi $ ./build 


[시스템 구성 파일] 


dht.c       : DHT22 센서로 부터 온/습도 정보 읽어 오는 C Source 

read_h_t.sh : 온/습도 정보를 읽고 기기를 제어하기 위한  스크립트 ( dht 이용 ) 

exe_h_t.sh  : read_h_t.sh 스크립트를 실행시켜 주는  스크립트 

get_stat.py  : 릴레이가 on 되어야 하는지 off 되어야 하는지 계산하는 파이썬 프로그램 

gui_h_t.py   :  GUI용 Interface program (파이썬으로 개발되어 리눅스, 윈도 모두 구동 가능 ) 

r-ctl.conf   : 환경 설정 파일 /etc/xdg/autostart/auto-gui-dht.desktop : Desktop 로긴시 자동으로 GUI program이 실행되게하는 단축 Link 


[소프트웨어 실행 방식] 


- 온/습도 Data 파일 생성 처리 

1. crontab에 의해 부팅시 또는 일정 주기로 exe_h_t.sh 실행됨 

2. exe_h_t.sh에 의해 read_h_t.sh 실행 상태를 점검하여, 실행되지 않고 있다면 read_h_t.sh 실행 

3. read_h_t.sh에서 r-ctl.conf 환경 설정 정보 기반으로 dht를 통해 일정 주기로 온/습도 정보 읽어와서 지정된 디렉터리에 온/습도 정보를 일자별 파일로 기록 남긴 후, get_stat.py를 통해 얻은 제어값으로 릴레이를 ON/OFF 처리함


-----------------------------------------------------------


GUI Interface Program 


1. Desktop 로긴시 /etc/xdg/autostart/auto-gui-dht.desktop에 의해 gui_h_t.py GUI 프로그램이 작동하여 read_h_t.sh에 의해 생성된 온/습도 정보 화면 표시, 환경 설정 파일 r-ctl.conf를 수정 지원 


[시스템을 구성하는 프로그램 소스 첨부] 


- dht.c : DHT22 센서로 부터 온/습도 정보 읽어 오는 C Source 

DHT-22로부터 온도/습도를 읽어오는 dht.c는 인터넷에 공개되어 있는 C 소스를 가져와서 일부 수정  컴파일하여 사용. 


인터넷으로부터 받은 받은 링크 주소: https://forums.raspberrypi.com/viewtopic.php?t=284053



/*

 *  dht.c:

 *    read temperature and humidity from DHT11 or DHT22 sensor

 */

 

#include <wiringPi.h>

#include <stdio.h>

#include <stdlib.h>

#include <stdint.h>

 

#define MAX_TIMINGS    85

#define DHT_PIN        24 

 

int data[5] = { 0, 0, 0, 0, 0 };

 

int read_dht_data()

{

    uint8_t laststate    = HIGH;

    uint8_t counter        = 0;

    uint8_t j            = 0, i;

 

    data[0] = data[1] = data[2] = data[3] = data[4] = 0;

 

    /* pull pin down for 18 milliseconds */

    pinMode( DHT_PIN, OUTPUT );

    digitalWrite( DHT_PIN, LOW );

    delay( 18 );

 

    /* prepare to read the pin */

    pinMode( DHT_PIN, INPUT );

 

    /* detect change and read data */

    for ( i = 0; i < MAX_TIMINGS; i++ )

    {

        counter = 0;

        while ( digitalRead( DHT_PIN ) == laststate )

        {

            counter++;

            delayMicroseconds( 1 );

            if ( counter == 255 )

            {

                break;

            }

        }

        laststate = digitalRead( DHT_PIN );

 

        if ( counter == 255 )

            break;

 

        /* ignore first 3 transitions */

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

        {

            /* shove each bit into the storage bytes */

            data[j / 8] <<= 1;

            if ( counter > 16 )

                data[j / 8] |= 1;

            j++;

        }

    }

 

    /*

     * check we read 40 bits (8bit x 5 ) + verify checksum in the last byte

     * print it out if data is good

     */

    if ( (j >= 40) &&

         (data[4] == ( (data[0] + data[1] + data[2] + data[3]) & 0xFF) ) )

    {

        float h = (float)((data[0] << 8) + data[1]) / 10;

        if ( h > 100 )

        {

            h = data[0];    // for DHT11

        }

        float c = (float)(((data[2] & 0x7F) << 8) + data[3]) / 10;

        if ( c > 125 )

        {

            c = data[2];    // for DHT11

        }

        if ( data[2] & 0x80 )

        {

            c = -c;

        }

 

        c -= 2.5;

 

        float f = c * 1.8f + 32;

        printf( "Humidity = %.1f %% Temperature = %.1f *C ( %.1f *F )", h, c, f );

        return 1;

    }else  {

        // printf( "Data not good, skip " );

        return 0;

    }

}

 

int main( void )

{

   // printf( "Raspberry Pi DHT11/DHT22 temperature/humidity test " );

 

    int cnt = 0;

 

    if ( wiringPiSetup() == -1 )

        exit( 1 );

 

    while ( 1 )

    {

        if( read_dht_data() ) break;

 

        if( ++ cnt > 5 ) {

          printf( "Error!!");

          return(1);

          break;

        }

        delay( 2000 ); /* wait 2 seconds before next read */

    }

 

    return(0);

}


소스 코드를 아래와 같이 compile하여, 컴파일된 dht 실행파일을 사용

pi@raspberrypi:~/dht $ gcc -o dht dht.c –lwiringPi 


- read_h_t.sh : 

1. 온/습도 정보를 읽어 오는 dht proram을 반복 실행하여 측정한 온/습도 기록 

2. 측정한 온/습도 정보를 기반으로 릴레이 제어 ( 제/가습기, 냉/난방기 제어 )



#!/bin/sh

scr_name=$(/usr/bin/readlink -f $0 )

#스크립트의 실행 경로 확인

scr_path=$(/usr/bin/dirname $scr_name)

 

#DHT 읽기중 오류 발생시 오류 발생 횟수 count를 위한 변수 초기화

err_cnt=0

 

#무한 반복하며 DHT로 부터 온/습도를 측정하여 그에 따른 처리를 함

while true

do

  ###/습도 읽어옴

  #DHT 전원 공급

  /usr/local/bin/gpio mode 22 out;/usr/local/bin/gpio write 22 1

  #DHT로 부터 온/습도 읽어 옴

  h_t=$($scr_path/dht)

  #측정한 습도값

  h=$(/usr/bin/echo $h_t|/usr/bin/cut -d " " -f 3 )

  #측정한 온도값

  t=$(/usr/bin/echo $h_t|/usr/bin/cut -d " " -f 7 )

  #DHT 전원 공급 차단(DHT 초기화 효과 - DHT 다운 증상 방지 효과)

  /usr/local/bin/gpio write 22 0

 

  #DHT로 측정값을 정상적으로 읽어 오지 못했을 경우

  if [ "$h_t" = "Error!!" ]; then

    err_cnt="expr $err_cnt + 1"   #오류 count 횟수 1증가 시킴

  else

    err_cnt=0 #DHT로 부터 값을 제대로 읽어 왔을 경우에는 오류 count를 초기화시킴

  fi

 

  연속 오류 횟수가 2번 이상 반복될 경우에는 라즈베리파이를 재부팅 시켜 오류 복구

  if [ $err_cnt -ge 2 ]; then

    /usr/bin/sudo /sbin/reboot

  fi

 

  #실행된 스크립트와 같은 경로에 있는 r-ctl.conf 환경 설정 파일로 부터

  #/습도값 기록할 data file 경로 확인

  dat_path=$(/usr/bin/cat $scr_path/r-ctl.conf | /usr/bin/grep -v ^# | /usr/bin/grep dat_path= |/usr/bin/cut -f 2 -d "=")

  #설정 파일에 지정한 디렉터리가 없을 경우에는 디렉터리를 새로 생성함

  [ ! -d "$dat_path" ] && /usr/bin/sudo /usr/bin/mkdir –p $dat_path;/usr/bin/sudo /usr/bin/chown pi $dat_path

 

  /습도 값을 기록할 파일명 ( 일자별로 파일 기록함 )

  o_file=$dat_path/$(/usr/bin/date +%Y%m%d).txt

 

  #일시  습도온도를 일자별 file에 기록함

  /usr/bin/echo $(/usr/bin/date +%Y%m%d_%H%M%S) 습도= $h ,  온도= $t >> $o_file

 

  ###relay 1에 대한 처리

  릴레이 1에 대한 설정값 가져옴

  r1=$(/usr/bin/cat $scr_path/r-ctl.conf | /usr/bin/grep -v ^# | /usr/bin/grep r1=)

  #첫번째 릴레이에 설정된 장치값 읽어옴( H: /가습기, T: /난방기 )

  r1_d=$(/usr/bin/echo $r1 | /usr/bin/cut -f 2 -d '='| /usr/bin/cut -f 1 -d ' ')

  #장치를 작동시킬 경계값

  r1_on=$(/usr/bin/echo $r1 | /usr/bin/cut -f 2 -d '='| /usr/bin/cut -f 2 -d ' ')

  #장치를 작동 중지 시킬 경계값

  r1_off=$(/usr/bin/echo $r1 | /usr/bin/cut -f 2 -d '='| /usr/bin/cut -f 3 -d ' ')

 

  if [ "$r1_d" = "H" ]; then    #릴레이1을 통해 가습기 또는 제습기를 제어할 경우라면

    r1_act=$($scr_path/get_stat.py $r1_on $r1_off $h ) #측정한 습도값을 전달하여 제어값 얻음

  fi

 

  if [ "$r1_d" = "T" ]; then    #릴레이1을 통해 냉방기 또는 난방기를 제어할 경우라면

    r1_act=$($scr_path/get_stat.py $r1_on $r1_off $t ) #측정한 온도값을 전달하여 제어값 얻음

  fi

 

  if [ "$r1_act" = "1" ]; then                        #릴레이1을 작동시킬때

     /usr/local/bin/gpio mode 28 out

     /usr/local/bin/gpio write 28 1

  fi

 

  if [ "$r1_act" = "0" ]; then                        #릴레이1을 작동 중지시킬때

     /usr/local/bin/gpio mode 28 out

     /usr/local/bin/gpio write 28 0

  fi

 

  ###relay 2에 대한 처리

  릴레이 2에 대한 설정값 가져옴

  r2=$(/usr/bin/cat $scr_path/r-ctl.conf | /usr/bin/grep -v ^# | /usr/bin/grep r2=)

  #첫번째 릴레이에 설정된 장치값 읽어옴(H:/제습기, T:/난방기)

  r2_d=$(/usr/bin/echo $r2 | /usr/bin/cut -f 2 -d '='| /usr/bin/cut -f 1 -d ' ')

  #장치를 작동시킬 경계값

  r2_on=$(/usr/bin/echo $r2 | /usr/bin/cut -f 2 -d '='| /usr/bin/cut -f 2 -d ' ')

  #장치를 작동 중지 시킬 경계값

  r2_off=$(/usr/bin/echo $r2 | /usr/bin/cut -f 2 -d '='| /usr/bin/cut -f 3 -d ' ')

 

  if [ "$r2_d" = "H" ]; then    #릴레이2을 통해 가습기 또는 제습기를 제어할 경우라면

    r2_act=$($scr_path/get_stat.py $r2_on $r2_off $h ) #측정한 습도값을 전달하여 제어값 얻음

  fi

 

  if [ "$r2_d" = "T" ]; then    #릴레이2을 통해 냉방기 또는 난방기를 제어할 경우라면

    r2_act=$($scr_path/get_stat.py $r2_on $r2_off $t ) #측정한 온도값을 전달하여 제어값 얻음

  fi

 

  if [ "$r2_act" = "1" ]; then                        #릴레이2을 작동시킬때

     /usr/local/bin/gpio mode 29 out

     /usr/local/bin/gpio write 29 1

  fi

 

  if [ "$r2_act" = "0" ]; then                        #릴레이2을 작동 중지시킬때

     /usr/local/bin/gpio mode 29 out

     /usr/local/bin/gpio write 29 0

  fi

 

  #설정된 시간(간격으로 반복되게 함

  /usr/bin/sleep $(/usr/bin/cat $scr_path/r-ctl.conf|/usr/bin/grep -v ^# | /usr/bin/grep r_sec= |/usr/bin/cut -f 2 -d "=")

done


- exe_h_t.sh  : 
   read_h_t.sh 스크립트가 자동 실행되어, 계속 작동할  있게 해주는 스크립트

   crontab에 의해 초기 부팅시  일정 시간 간격 실행되어 read_h_t.sh 실행 상태 체크


#!/usr/bin/sh

scr_name=$(/usr/bin/readlink -f $0 )

#스크립트의 실행 경로 확인

scr_path=$(/usr/bin/dirname $scr_name)

 

/usr/bin/ps -ef | /usr/bin/grep -v grep | /usr/bin/grep read_h_t.sh > /dev/null

if [ $? != 0 ]

then

  $scr_path/read_h_t.sh &

fi


crontab에 아래와 같이 등록 pi@raspberrypi:~/dht $ crontab –e


# m h  dom mon dow   command

켜질때마다 자동 실행되게

@reboot /home/pi/dht/exe_h_t.sh

매 시간마다 실행 상태 점검하여 미 작동시 실행될 수 있게

0 */1 * * * /home/pi/dht/exe_h_t.sh


- get_stat.py : 
릴레이가 on 되어야 하는지 off 되어야 하는지 계산하는 파이썬 프로그램 ( read_h_t.sh 에서 호출하여 사용함 )


#!/usr/bin/python3

#입력 Parameter: 작동시작 작동중지 측정된값

#출력 - 1: 작동, 0:중지, 2: 현상태유지, 9:오류

#판단을 위한 값을 입력 받아릴레이 작동 상태값 Return

import sys

if len(sys.argv) == 4: #전달받아야 할 인수 갯수가 자신의 이름 + 3개 인지 확인

   pass

else :        #전달 받아야할 인수 개수가 맞지 않을 경우

   print( 9 ) # 오류 표시

   quit()     종료

try:

   on=float(sys.argv[1])       #첫번째 인수는 on 

   off=float(sys.argv[2])       #두번째 인수는 off 

   value=float(sys.argv[3])    #세번째 인수는 측정값

except:       잘못된 값을 전달 받았을 경우

   print(9)   오류 표시

   quit()     종료

 

if on > off :     #냉방기제습기에 대한 처리

  if value > on :  작동되게 알림(측정값이 on 값보다 클때)

     print( 1 )

  elif value < off:# 작동 중지되게 알림(측정값이 off 값보다 작을때)

    print( 0 )

  else :           현상태 유지

    print( 2 )

else :           #난방기 또는 가습기

  if value < on :  작동되게 알림(측정값이 on 값보다 작을때)

    print( 1 )

  elif value > off : #작동 중지되게 알림(측정값이 off 값보다 클때 )

    print( 0 )

  else :             #그외의 상황에서는 현상태 유지

    print ( 2 )


- r-ctl.conf  : 

환경 설정 정보가 저장되어 있는 파일 기기 제어를 위해 read_h_t.sh  스크립트에서 사용 

설정 변경  제어를 위해 gui_h_t.py 파이썬 GUI 프로그램에서 사용


#####Relay 설정

구분 ON OFF

r1=H 40 50

r2=T 16 20

##### /습도 Data 저장 경로

dat_path=/home/pi/dht/data

##### /습도 측정 시간 간격()

r_sec=100


설정 파일에서 


r1 : 릴레이1에 대한 제어 설정  

r2 : 릴레이2에 대한 제어 설정  


릴레이 설정 변수에 H는 습도 제어, T는 온도 제어 

H/T 다음에  번째 숫자는 릴레이를 켜는(ON) 값,  번째 값은 릴레이를 끄는(OFF)  

r1=H 40 50  의미는 습도 40%에서 켜고, 50%에는 끄도록 설정 ( 가습기 ), 

제습기: H 다음에  값, 작은  순으로  값에서 작동시키고, 작은 값에서 중지 

난방기: T 다음에 작은 값,  값의 순으로, 작은 값에서 켜고,  값에서 중지 

냉방기: T 다음에  값, 작은  배치로  값에서 작동시키고, 작은 값에서 중지 


기준에 따라 조금씩의 차이는 있겠지만, 서버실의 온도는 16℃ ~ 28℃ 정도가 적정할  같고, 습도는 40% ~ 70% 정도가 적절할 걸로 보임. 


dat_path  측정한 온도/습도 자료를 저장할 경로에 대한 설정 

r_sec 값은 온/습도를 측정하는 반복 주기(초) 


- gui_h_t.py : 

온/습도 화면 표시  환경 설정 관리용 GUI 프로그램 read_h_t.sh에서 생성한 온/습도 파일 화면 표시 r-ctl.conf 환경 설정 파일 관리


#!/usr/bin/python3

from tkinter import *

import tkinter.ttk as ttk     콤보 박스

import os

from datetime import datetime

import time

def get_h_t():

    global conf_file_name #환경 설정 정보가 저장된 파일명

    global r_sec        #온습도 data를 반복 읽어 오는 주기

    global r1_f         #r1 relay에 대한 장치 구분값

    global r2_f         #r2 Relay에 대한 장치 구분값

    global r1_on        #r1 relay의 작동 시작 경계값

    global r2_on        #r2 relay의 작동 시작 경계값

    global r1_off       #r1 relay의 작동 중지 경계값

    global r2_off       #r2 relay의 작동 중지 경계값

    global dat_path     #측정한 온/습도 데이터가 저장되어 있는 경로

   

    global c_date       #측정한 일시

    global t            #측정된 온도 수치

    global h            #측정된 습도 수치

   

    스크립트가 위치하는 경로 확인

    (scr_path, scr_name)=os.path.split(os.path.abspath( __file__ ))

   

    환경 파일의 위치 확인

    conf_file_name = scr_path + "/r-ctl.conf"

   

    c_file = open( conf_file_name, "r" )

   

    for l in c_file.readlines():

        if l[0] == "#" :

            pass

        else :

            (e_name,e_val) = l.split('=')

            if e_name == "dat_path" :

                dat_path = e_val

            elif e_name == "r1" :

                r1_f=e_val.split()[0]    #Relay 1의 구분값

                r1_on=e_val.split()[1]   #Relay 1 ON 경계값

                r1_off=e_val.split()[2]  #Relay 2 OFF 경계값

            elif e_name == "r2" :

                r2_f=e_val.split()[0]    #Relay 2의 구분값

                r2_on=e_val.split()[1]   #Relay 2 ON 경계값

                r2_off=e_val.split()[2]  #Relay 2 OFF 경계값

            elif e_name == "r_sec" :

                r_sec = e_val

            else :

                pass

    c_file.close()

    #Data가 기록된 파일명 얻음

    dat_file=dat_path.rstrip(" ")+"/"+ datetime.today().strftime("%Y%m%d") + ".txt"

    #마지막 기록된 온/습도 Data 읽어옴

    d_file = open( dat_file, "r" )

    l_dat=d_file.readlines()[-1]

    d_file.close()

   

    c_date=datetime.strptime( l_dat.split()[0], "%Y%m%d_%H%M%S")   #측정한 일시

    h=l_dat.split()[2]  습도

    t=l_dat.split()[5]  온도

 

def get_d_name( gbn, on, off ):

    on=float(on)

    off=float(off)

    if gbn == "T" : #난방기 또는 냉방기

        if on > off :

            get_d_name = "냉방기"

        else :

            get_d_name = "난방기"

    else :

        if on > off:

            get_d_name = "제습기"

        else :

            get_d_name = "가습기"

    return get_d_name

   

def get_unit( gbn ) :

    if gbn == "T":

        return "˚C"

    else:

        return "%"

 

def r_check():

    try:   

        get_h_t()

       

        lbl_ctime.config( text = c_date )

        lbl_temp.config( text = t + "˚C")

        lbl_humi.config( text = h+'%' )

       

        combo_gbn1.set( get_d_name( r1_f, r1_on, r1_off ) )

        combo_gbn2.set( get_d_name( r2_f, r2_on, r2_off ) )

       

        ent_on1.delete( 0, END )

        ent_on1.insert( 0, r1_on )

       

        lbl_on1_unit.config( text=get_unit( r1_f ) )

       

        ent_off1.delete( 0, END )

        ent_off1.insert( 0, r1_off )

       

        lbl_off1_unit.config( text=get_unit( r1_f ) )

       

        ent_on2.delete( 0, END )

        ent_on2.insert( 0, r2_on )

       

        lbl_on2_unit.config( text=get_unit( r2_f ) )

       

        ent_off2.delete( 0, END )

        ent_off2.insert( 0, r2_off)

       

        lbl_off2_unit.config( text=get_unit( r2_f ) )

       

        ent_path.delete(0,END)

        ent_path.insert( 0, dat_path.rstrip())

       

        ent_rpt.delete( 0, END )

        ent_rpt.insert( 0, int(r_sec) )

    except :

        pass

   

    dht.after( int(r_sec) * 1000, r_check ) #지정된 시간 후 다시 실행되게 함

 

def get_dev_gbn( str ):

    if str == "가습기" or str == "제습기" :

        return "H"

    else :

        return "T"

   

def write_config() :

    c_file = open( conf_file_name, "r" )

    config_file=c_file.readlines()

    w_config_file=[]

    for l in config_file:

        if l[0] == "#" :

            w_config_file.append( l )

        else:

            (e_name,e_val) = l.split('=')

            if e_name == "dat_path" :

                w_config_file.append( e_name + "=" + ent_path.get() + " " )

            elif e_name == "r1" :

                w_config_file.append( e_name + "=" + get_dev_gbn( combo_gbn1.get() ) + " " + ent_on1.get() + " " + ent_off1.get() + " " )

            elif e_name == "r2" :

                w_config_file.append( e_name + "=" + get_dev_gbn( combo_gbn2.get() ) + " " + ent_on2.get() + " " + ent_off2.get() + " " )

            elif e_name == "r_sec" :

                w_config_file.append( e_name + "=" + ent_rpt.get() + " " )

    c_file.close()

   

    with open( conf_file_name, "w" ) as c_file:

        c_file.writelines( w_config_file )

dht=Tk()

dht.title( "온도/습도 현황" )

dht.geometry( "600x330+10+50")

i_x=20

i_y=20

o_x=200

o_y=30

p_x=i_x + 10

p_y=i_y

lbl_time_title=Label( dht, text="측정 일시" )

lbl_time_title.config(font=("Courier", 17, "bold"))

lbl_time_title.place(x=p_x + 70, y=p_y )

p_x=p_x+o_x + 100

lbl_temp_title=Label( dht, text="온도" )

lbl_temp_title.config(font=("Courier", 17, "bold"))

lbl_temp_title.place(x=p_x, y=p_y )

p_x=p_x + 150

lbl_humi_title=Label( dht, text="습도" )

lbl_humi_title.config(font=("Courier", 17, "bold"))

lbl_humi_title.place(x=p_x, y=p_y ) #, width=100, height=50 )

p_x=i_x

p_y=p_y+o_y

lbl_ctime=Label( dht, text="")

lbl_ctime.config(font=("Courier", 15 ) )

lbl_ctime.place(x=p_x, y=p_y )

p_x=p_x+o_x + 100

lbl_temp=Label( dht, text="")

lbl_temp.config(font=("Courier", 15 ) )

lbl_temp.place(x=p_x, y=p_y )

p_x=p_x + 150

lbl_humi=Label( dht, text="")

lbl_humi.config(font=("Courier", 15 ) )

lbl_humi.place(x=p_x, y=p_y )

p_x=i_x+120

p_y=p_y+60

lbl_gbn_title=Label( dht, text="장치구분" )

lbl_gbn_title.config(font=("Courier", 17, "bold"))

lbl_gbn_title.place(x=p_x, y=p_y ) #, width=100, height=50 )

p_x=p_x+130

lbl_on_title=Label( dht, text="가동시작값" )

lbl_on_title.config(font=("Courier", 17, "bold"))

lbl_on_title.place(x=p_x, y=p_y ) #, width=100, height=50 )

p_x=p_x+170

lbl_off_title=Label( dht, text="가동중지값" )

lbl_off_title.config(font=("Courier", 17, "bold"))

lbl_off_title.place(x=p_x, y=p_y ) #, width=100, height=50 )

p_x=i_x

p_y=p_y+o_y + 10

lbl_con1_title=Label( dht, text="콘센트1:" )

lbl_con1_title.config(font=("Courier", 17, "bold"))

lbl_con1_title.place(x=p_x, y=p_y ) #, width=100, height=50 )

p_x=p_x + 120

values = [ "가습기", "제습기", "난방기", "냉방기" ]

combo_gbn1 = ttk.Combobox( dht, height=4, width=8, values=values, state="readonly" )

combo_gbn1.config(font=("Courier", 15 ))

combo_gbn1.place( x=p_x, y=p_y )

p_x=p_x + 130

ent_on1 = Entry( dht, width=5 )

ent_on1.config(font=("Courier", 15 ))

ent_on1.place( x=p_x, y=p_y )

p_x=p_x + 70

lbl_on1_unit = Label( dht, text="")

lbl_on1_unit.config(font=("Courier", 15 ))

lbl_on1_unit.place( x=p_x, y=p_y )

p_x=p_x + 105

ent_off1 = Entry( dht, width=5 )

ent_off1.config(font=("Courier", 15 ))

ent_off1.place( x=p_x, y=p_y )

p_x=p_x + 70

lbl_off1_unit = Label( dht, text="")

lbl_off1_unit.config(font=("Courier", 15 ))

lbl_off1_unit.place( x=p_x, y=p_y )

p_x=i_x

p_y=p_y+o_y+10

lbl_con2_title=Label( dht, text="콘센트2:" )

lbl_con2_title.config(font=("Courier", 17, "bold"))

lbl_con2_title.place(x=p_x, y=p_y ) #, width=100, height=50 )

p_x=p_x + 120

#values = [ "가습기", "제습기", "난방기", "냉방기" ]

combo_gbn2 = ttk.Combobox( dht, height=4, width=8, values=values, state="readonly" )

combo_gbn2.config(font=("Courier", 15 ))

combo_gbn2.place( x=p_x, y=p_y )

p_x=p_x + 130

ent_on2 = Entry( dht, width=5 )

ent_on2.config(font=("Courier", 15 ))

ent_on2.place( x=p_x, y=p_y )

p_x=p_x + 70

lbl_on2_unit = Label( dht, text="")

lbl_on2_unit.config(font=("Courier", 15 ))

lbl_on2_unit.place( x=p_x, y=p_y )

p_x=p_x + 105

ent_off2 = Entry( dht, width=5 )

ent_off2.config(font=("Courier", 15 ))

ent_off2.place( x=p_x, y=p_y )

p_x=p_x + 70

lbl_off2_unit = Label( dht, text="")

lbl_off2_unit.config(font=("Courier", 15 ))

lbl_off2_unit.place( x=p_x, y=p_y )

p_x=i_x

p_y=p_y+o_y+10

lbl_path_title=Label( dht, text="저장경로:" )

lbl_path_title.config(font=("Courier", 17, "bold"))

lbl_path_title.place(x=p_x, y=p_y ) #, width=100, height=50 )

p_x=p_x + 120

ent_path = Entry( dht, width=33 )

ent_path.config(font=("Courier", 15 ))

ent_path.place( x=p_x, y=p_y )

p_x=i_x

p_y=p_y+o_y+10

lbl_rpt_title=Label( dht, text="측정주기:" )

lbl_rpt_title.config(font=("Courier", 17, "bold"))

lbl_rpt_title.place(x=p_x, y=p_y ) #, width=100, height=50 )

p_x=p_x + 120

ent_rpt = Entry( dht, width=5 )

ent_rpt.config(font=("Courier", 15 ))

ent_rpt.place( x=p_x, y=p_y )

p_x=p_x + 70

lbl_sec=Label( dht, text="()" )

lbl_sec.config(font=("Courier", 15))

lbl_sec.place(x=p_x, y=p_y )

p_x=p_x + 180

btn_save = Button( dht, width=8, text="설정저장", command=write_config )

btn_save.config(font=("Courier", 15 ))

btn_save.place( x=p_x, y=p_y )

dht.after( 0, r_check)

dht.mainloop()


- auto-gui-dht.desktop : 


GUI Interface gui_h_t.py를 Desktop 로긴시 자동 실행하는 데스크탑 링크 /etc/xdg/autostart/auto-gui-dht.desktop  저장


[Desktop Entry]

Type=Application

Name=Auto-GUI-DHT

Comment=GUI Humidity and Temperature

NoDisplay=false

Exec=/home/pi/dht/gui_h_t.sh

NotShowIn=GNOME;KDE;XFCE;




본 글은 "wansoo" 회원님께서 기고해 주셨습니다. 

기업의 IT운영에 대한 나만의 노하우나 경험이 있으시다면 [email protected]로 기고해 주세요  

선정되신 분께는 5만원 상당의 백화점 상품권을 보내드립니다. 



5개의 댓글이 있습니다.

3달 전

라즈베리파이로 할수 있는게 다양하네요.
잘 읽었습니다.

Reply

댓글 남기기

댓글을 남기기 위해서는 로그인이 필요합니다.

로그인 회원가입

3달 전

이런 정보 공유 좋아요... 공유 해주신 wansoo께도 감사드립니다.

Reply

댓글 남기기

댓글을 남기기 위해서는 로그인이 필요합니다.

로그인 회원가입

3달 전

좋은 정보 참고하겠습니다.

Reply

댓글 남기기

댓글을 남기기 위해서는 로그인이 필요합니다.

로그인 회원가입

3달 전

이 정도까지 디테일 하게 구성하는 줄을 몰랐습니다. !
좋은 정보 감사합니다 !

Reply

댓글 남기기

댓글을 남기기 위해서는 로그인이 필요합니다.

로그인 회원가입

1st 5stars

3달 전

온/습도 센서 DHT22를 제어하는 부분은 C 언어와 Shell Script를 이용해서 만들었고, GUI 부분과 계산에 복잡성이 있는 부분에 대해서만 Python을 이용하여 구현했습니다.

스크립트 중심으로 작업을 많이 하던 시기에 만들었던 시스템이라...
스크립트를 이용한 부분이 많습니다.
지금 새롭게 다시 만든다면 스크립트를 최소화하고 파이썬 중심으로 만들게 될 것 같네요.

서버실 항온/항습기를 이런 방식으로 구현할 수도 있다는데 참고하시면 도움될 것 같습니다.

Reply

댓글 남기기

댓글을 남기기 위해서는 로그인이 필요합니다.

로그인 회원가입

댓글 남기기

댓글을 남기기 위해서는 로그인이 필요합니다.

로그인 회원가입