개발자/스마트팜

Greenhouse 원격 관리시스템 소프트웨어 테스트 7

지구빵집 2020. 8. 21. 09:05
반응형

 

Greenhouse 원격 관리시스템 소프트웨어 테스트 7  

 

여기까지 잘 따라오셨다면 이미 다 한거나 다름이 없다. 하드웨어 연결은 소프트웨어 제어와 밀접하다고 생각하지만 사실은 물리적인 구성품이 때로는 소프트웨어 코드보다 훨씬 어렵고 시간도 오래 걸린다. 물리적인 부분은 되면 되는 거고 안 되면 안 되는 것이다. 소프트웨어는 코드 몇 줄에 테스트를 하고 문제점을 밝혀내지만 하드웨어는 몇 배나 힘든 과정을 거친다.

 

처음 작성한 개발 완료 보고서에는 소스코드를 넣지 않고 테스트 결과만을 표시했는 데 여기서는 소스코드와 테스트 결과, 꼭 필요한 원리와 간단한 설명을 포함하기로 한다. 이미 라즈베리파이 기반으로 리눅스를 사용하고 컴파일과 실행 등 기본적인 명령어를 다룰 수 있다고 생각하하기에 자세한 명령어 설명은 하지 않는다. 아직 갈 길이 멀다. 일모도원.

 

Greenhouse 원격관리 시스템을 구현하는 전체 포스팅은 아래와 같이 진행합니다. 참고하세요.

Greenhouse 원격관리시스템 개요 1

Greenhouse 원격관리시스템 Hardware 2

Greenhouse 원격 관리시스템 센서와 액츄에이터 3

Greenhouse 원격 관리시스템 액츄에이터 4

Greenhouse 원격 관리시스템 전원부 5

Greenhouse 원격 관리시스템 연결도 6

Greenhouse 원격 관리시스템 소프트웨어 테스트 7

Greenhouse 원격 관리시스템 서버프로그램과 카메라 8

Greenhouse 원격 관리시스템 안드로이드 앱 9

 

7.1 센서 테스트 프로그램
7.1.1 온 습도 센서 테스트 코드

 

여기서는 지면상 센서 테스트 프로그램 소스코드를 적지 않고, 소스코드 파일과 컴파일 과정 그리고 결과 화면만을 나타내기로 한다. 온 습도를 주기적으로 뿌려주는 테스트 프로그램 소스코드는 greenhouse 폴더의 greentemp.c 파일이다.

 

/*
 *      dht22.c:
 *	Simple test program to test the wiringPi functions
 *	Based on the existing dht11.c
 *	Amended by technion@lolware.net
 */

#include <wiringPi.h>

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <sys/types.h>
#include <unistd.h>

#define MAXTIMINGS 85
//static int DHTPIN = 21;
//static int DHTPIN = 7;

static int DHTPIN = 11;
static int dht22_dat[5] = {0,0,0,0,0};

static uint8_t sizecvt(const int read)
{
  /* digitalRead() and friends from wiringpi are defined as returning a value
  < 256. However, they are returned as int() types. This is a safety function */

  if (read > 255 || read < 0)
  {
    printf("Invalid data from wiringPi library\n");
    exit(EXIT_FAILURE);
  }
  return (uint8_t)read;
}

static int read_dht22_dat()
{
  uint8_t laststate = HIGH;
  uint8_t counter = 0;
  uint8_t j = 0, i;

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

  // pull pin down for 18 milliseconds
  pinMode(DHTPIN, OUTPUT);
  digitalWrite(DHTPIN, HIGH);
  delay(10);
  digitalWrite(DHTPIN, LOW);
  delay(18);
  // then pull it up for 40 microseconds
  digitalWrite(DHTPIN, HIGH);
  delayMicroseconds(40); 
  // prepare to read the pin
  pinMode(DHTPIN, INPUT);

  // detect change and read data
  for ( i=0; i< MAXTIMINGS; i++) {
    counter = 0;
    while (sizecvt(digitalRead(DHTPIN)) == laststate) {
      counter++;
      delayMicroseconds(1);
      if (counter == 255) {
        break;
      }
    }
    laststate = sizecvt(digitalRead(DHTPIN));

    if (counter == 255) break;

    // ignore first 3 transitions
    if ((i >= 4) && (i%2 == 0)) {
      // shove each bit into the storage bytes
      dht22_dat[j/8] <<= 1;
      if (counter > 16)
        dht22_dat[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) && 
      (dht22_dat[4] == ((dht22_dat[0] + dht22_dat[1] + dht22_dat[2] + dht22_dat[3]) & 0xFF)) ) {
        float t, h;
        h = (float)dht22_dat[0] * 256 + (float)dht22_dat[1];
        h /= 10;
        t = (float)(dht22_dat[2] & 0x7F)* 256 + (float)dht22_dat[3];
        t /= 10.0;
        if ((dht22_dat[2] & 0x80) != 0)  t *= -1;


    printf("Humidity = %.2f %% Temperature = %.2f *C \n", h, t );
    return 1;
  }
  else
  {
    printf("Data not good, skip\n");
    return 0;
  }
}

//int main (int argc, char *argv[])
int main (void)
{
  //여기에서 온도와 습도를 읽어옵니다.
  if (wiringPiSetup() == -1)
    exit(EXIT_FAILURE) ;
	
  if (setuid(getuid()) < 0)
  {
    perror("Dropping privileges failed\n");
    exit(EXIT_FAILURE);
  }

  while (read_dht22_dat() == 0) 
  {
     delay(1000); // wait 1sec to refresh 1초간 기다립니다.
  }


  return 0 ;
}

 

컴파일은
$gcc –o greentemp greentemp.c –l wiringPi

실행은
$sudo ./greentemp 실행하면 결과가 아래와 같이 출력된다. 

 

Humidity = 65.00 % Temperature = 25.00 *C
Humidity = 65.00 % Temperature = 25.00 *C
Humidity = 65.00 % Temperature = 25.00 *C
Humidity = 65.00 % Temperature = 25.00 *C

 

7.1.2 조도센서 테스트 코드
소스코드는 greenhouse 폴더의 greenlight.c 파일이다.

 

#include <stdio.h> 
#include <string.h> 
#include <errno.h> 
#include <wiringPi.h> 
#include <wiringPiSPI.h> 

#define CS_MCP3208 8 //GPIO 8 
#define SPI_CHANNEL 0 
#define SPI_SPEED 1000000 //1Mhz

// spi communication with Rpi and get sensor data 

int read_mcp3208_adc(unsigned char adcChannel) 
{
	unsigned char buff[3];
	int adcValue = 0;
	
	buff[0] = 0x06 | ((adcChannel & 0x07) >> 2);
	buff[1] = ((adcChannel & 0x07) << 6);
	buff[2] = 0x00;
	
	digitalWrite(CS_MCP3208, 0);
	wiringPiSPIDataRW(SPI_CHANNEL, buff, 3);
	
	buff[1] = 0x0f & buff[1];
	adcValue = (buff[1] << 8 ) | buff[2];
	
	digitalWrite(CS_MCP3208, 1);
	
	return adcValue;
}

int main(void) {

	unsigned char adcChannel_light = 0;

	int adcValue_light = 0;

	float vout_light;
	float vout_oftemp;
	float percentrh = 0;
	float supsiondo = 0;
	
	printf("start");

	
	if(wiringPiSetupGpio() == -1)
	{
		fprintf(stdout, "Unable to start wiringPi :%s\n", strerror(errno));
		return 1;
	}
	
	if(wiringPiSPISetup(SPI_CHANNEL, SPI_SPEED) == -1)
	{
		fprintf(stdout, "wiringPiSPISetup Failed :%s\n", strerror(errno));
		return 1;
	}
	
	pinMode(CS_MCP3208, OUTPUT);
	
	while(1)
	{
		adcValue_light = read_mcp3208_adc(adcChannel_light);
		
		//printf("Humiity = %u temparature = %u\n", adcValue_humi, adcValue_temp);
		printf("light sensor = %u\n", adcValue_light);
		
		delay(100);
	}
	return 0;
}

 

아래 코드는 서버 프로그램에서 조도 센서 값을 읽어오는 코드인데 위에 코드와 비교하는 의미에서 호출하는 코드만 나타낸다. 비교하여 참고하면 도움이 될 것이다.

 

int get_light_sensor(void)
{
	unsigned char adcChannel_light = 2;

	int adcValue_light = 0;

	float vout_light;
	float vout_oftemp;
	float percentrh = 0;
	float supsiondo = 0;
	
	printf("start");

	
	/*if(wiringPiSetupGpio() == -1)
	{
		fprintf(stdout, "Unable to start wiringPi :%s\n", strerror(errno));
		return 1;
	}*/
	
	if(wiringPiSPISetup(SPI_CHANNEL, SPI_SPEED) == -1)
	{
		fprintf(stdout, "wiringPiSPISetup Failed :%s\n", strerror(errno));
		return 1;
	}
	
	pinMode(CS_MCP3208, OUTPUT);
	
	
		adcValue_light = read_mcp3208_adc(adcChannel_light);
		
		//printf("Humiity = %u temparature = %u\n", adcValue_humi, adcValue_temp);
		printf("light sensor = %u\n", adcValue_light);
	
	if(adcValue_light < 100)
        return 0;
    else 
        return 1;

}

 

컴파일은

$gcc –o greenlight greenlight.c –l wiringPi

실행은

$sudo ./greenlight 실행하면 결과가 아래와 같이 출력된다.

 

밝을 때
light sensor = 2503
light sensor = 2504
light sensor = 2498
light sensor = 2507
light sensor = 2506
어두울 때
light sensor = 356
light sensor = 357
light sensor = 331
light sensor = 365
light sensor = 358

 

 

7.1.3 강우센서 테스트 코드
소스코드는 greenhouse 폴더의 greenrain.c 파일이다. 컴파일은 $gcc –o greenrain greenrain.c –l wiringPi 실행은 $sudo ./greenrain 실행하면 결과가 아래와 같이 출력된다. 비가 오지 않을경우 300~2500 이하의 값을 표시한다. 강우센서의 코드는 전체가 없기 때문에 강우센서에서 데이터를 읽어 일정 한 값 이상일 때 강우로 판단하는 코드를 아래에 나타낸다. 

 

int get_rain_sensor(void)
{
	unsigned char adcChannel_rain = 3;
	int adcValueofRain = 0;
	
	adcValueofRain = read_mcp3208_adc(adcChannel_rain);
	
	if(adcValueofRain > 2000){
		printf("It isn't raining\n");
		return 0;
	}else{
		printf("It is raining\n");
		return 1;
	}

	/*
	pinMode(RAINSEN, INPUT);
	while(1)
	{
		if(digitalRead(RAINSEN) == 0){
			printf("It is Raining\n");
			return 1;
		} else {
			printf("It isn't raining\n");
			return 0;
		}
	}*/
}

 

 

7.1.4 풍속계 테스트 코드
풍속계는 DC 24V 전원을 공급하여 아날로그 출력을 ADC 한 값을 얻는다. 상세한 센서 사용법과 출력값에 대한 풍속 데이터는 5장 센서 편의 풍속계를 참고하기 바란다. 마찬가지로 서버쪽에서 읽어 처리하는 프로그램 코드를 나타내었다

 

int get_wind_sensor(void)
{
	unsigned char adcChannel_wind = 2;
	int adcValueofwind = 0;
	float outvoltage = 0;

	pinMode(CS_MCP3208, OUTPUT);

	adcValueofwind = read_mcp3208_adc(adcChannel_wind);
	outvoltage = adcValueofwind * (5.0/1023.0);
	printf("wind output voltage  = %f\n", outvoltage);

	int Level = 6 * outvoltage; 
	printf("wind Level = %d\n", Level);

	return Level;
}

 

 

7.1.5 자동 수동 모드 입력 판단하는 소스코드
아래는 전면판의 자동 수동 선택 스위치의 값을 읽어서 자동 모드인지 수동 모드인지 판단하는 소스코드이다. 마찬가지로 서버 프로그램에서 읽어서 자동인지 수동인지를 판단하는 코드를 나타내었다. 이렇게 한 이유는 좌 우 회전 스위치에 풀 업 저항을 달아 아날로그 값을 읽어 하이상태와 로우 상태를 판단하였기 때문이다. 

 

int get_automanual()
{
	unsigned char adcChannel_auto = 1;
	int adcValueofauto = 0;

	pinMode(CS_MCP3208, OUTPUT);

	adcValueofauto = read_mcp3208_adc(adcChannel_auto);
	
	//printf("auto sensor = %u\n", adcValueofauto);
	
	if(adcValueofauto < 200)
		return 0;
	else
		return 1;
}

 

 

7.2 액츄에이터 테스트 프로그램
각 액츄에이터는 IO 보드 하드웨어 회로도 에서 SSR 을 제어함으로 팬, 전등, 워터펌프의 작동을 ON/OFF 시키는 구조이다. 개폐기는 특이하게 전원을 끊고 정회전, 역회전의 방향을 설정하고 다시 Relay의 출력을 제어하므로 열림과 닫힘이 이루어진다. 액츄에이터를 동작하기 위해 여기서는 할당된 GPIO 번호를 확인하고 워터펌프의 제어코드만을 살펴보면 다른 액츄에이터도 쉽게 제어할 수 가 있을 것이다. 워터펌프를 동작시키는 소스코드를 살펴보자.

 

#include <signal.h> //Signal 사용 헤더파일
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <stdlib.h> //exit() 사용 헤더파일

#include <wiringPi.h>

#define PUMP    0 // BCM_GPIO 5 // 할당된 GPIO wiringPi 번호

void sig_handler(int signo); // 마지막 종료 함수 선언

int main (void)
{
        signal(SIGINT, (void *)sig_handler);    //시그널 핸들러 함수

        if (wiringPiSetup () == -1)
        {
                fprintf(stdout, "Unable to start wiringPi: %s\n", strerror(errno));
                return 1 ;
        }

        pinMode (PUMP, OUTPUT) ;

        for (;;)
        {
                printf("here - pump on\n");
                digitalWrite (PUMP, 1) ; // On

                delay (2000) ; // ms 2초간 ON 상태를 유지

                digitalWrite (PUMP, 0) ; // Off

                delay (2000) ;

        }
        return 0 ;
}

void sig_handler(int signo)
{
    printf("process stop\n");
        digitalWrite (PUMP, 0) ; // Off

        exit(0);
}

  

가장 중요한 것은 각 액츄에이터에 할당된 GPIO 핀 번호를 확인하고, pinMode 함수로 출력으로 설정한다. 그리고 digitalWrite 함수로 0과 1을 씀으로 off/on 동작을 수행한다. 여기서 sig_handler 이벤트를 사용한 것은 CTRL-C 키를 눌러 프로그램을 종료해도 액츄에이터가 계속 동작하는 상황을 방지하기 위함이다. 위와 같은 방법으로 각 액츄에이터에 할당된 GPIO 번호를 제어하면 220V 전원이 연결된 SSR 을 통해 제어가 가능하다.

 

7.2.1 워터펌프 테스트 코드

 

int set_waterpump_active(const int _use)
{

	pinMode (PUMP, OUTPUT);
	digitalWrite (PUMP, _use) ;
	
}

 

7.2.2 FAN 테스트 코드

 

int set_fan_active(const int _use)
{

	pinMode (FAN, OUTPUT);
	digitalWrite (FAN, _use) ; 
	
}

 

7.2.3 개폐기 테스트 코드

개폐기의 열림 닫힘 코드를 따로 나타 내었고, 즉시 정지하는 코드를 추가로 나타낸다. 3개의 호출 함수로 개폐기 코드를 구성하였다.

 

void clockwise(int time)
{
	pinMode(FIRSTRELAY, OUTPUT);
	pinMode(SECONDRELAY, OUTPUT);

	digitalWrite(SECONDRELAY, 1);
	delay(10);
	digitalWrite(FIRSTRELAY, 1);
	delay(10);
	/*
	delay(time);

	digitalWrite(FIRSTRELAY, 0);
	delay(10);
	digitalWrite(SECONDRELAY, 0);
	*/
}

void unclockwise(int time)
{
	pinMode(FIRSTRELAY, OUTPUT);
	pinMode(SECONDRELAY, OUTPUT);
	
	digitalWrite(FIRSTRELAY, 1);
	delay(10);
	digitalWrite(SECONDRELAY, 0);
	delay(10);
	/*
	delay(time);

	digitalWrite(FIRSTRELAY, 0);
	delay(10);
	digitalWrite(SECONDRELAY, 0);
	*/
}

void openclosestop()
{
	pinMode(FIRSTRELAY, OUTPUT);
	pinMode(SECONDRELAY, OUTPUT);
	
	digitalWrite(FIRSTRELAY, 0);
	delay(10);
	digitalWrite(SECONDRELAY, 0);
	delay(10);
}

 

 

7.2.4 조명 테스트 코드 Greenhouse 원격관리 시스템의 조명 제어는 AC 220V를 공급하여 상시 조명, 식물 성장 조명, Bio 조명을 제어하는 기능을 한다. 수 십 개의 조명 장치를 채널 별로 나누어 제어를 하게 되지만 현재 시스템에서는 1ch 조명 수 개를 ON, OFF 하는 제어를 수행한다. 아래는 테스트 코드이다. 연결도와 SSR 제어 회로를 참고하여 테스트를 진행한다.  

 

int set_led_active(const int _use)
{

	pinMode(LED, OUTPUT);
	digitalWrite(LED, _use);
}

 

여기까지 스마트 Greenhouse에서 사용하는 센서와 액츄에이터 테스트를 실습했다. 라즈베리파이의 구동 소프트웨어는 복잡하지 않다. 이것이 전체적으로 합쳐졌을 때 잘 나누고 모듈화 하는 능력이 요구된다. 다음 포스팅에서는 여기까지 한 것들을 모두 합쳐서 원격관리 서버 프로그램을 검토할 것이다. 

 

여기까지 하고 놀러 나간다. 놀러나가지 않으면서 책을 보거나 빈둥빈둥 낭비하면서 꼭 놀러나간다고 하는 심뽀는 무언지 궁금하다. ^^ 노는 것도 낭비고 일하는 것도 낭비라면 낭비다. 생각보다 놀라울 정도로 정리를 잘 했다. 지금 시간 2020년 8월 11일 화요일 10시 33분.  

 

 

청계산이 바라보이는 상적동 사무실 옥상에서. 그리울까?

 

 

 

 

반응형