수요일, 3월 09, 2016

라즈베리파이2 와 8채널 릴레이를 이용한 전등(형광등) 컨트롤(Lighting control with Raspberry Pi2 & 8ch Relay) 만들기 - Part3

 2)  스위치 입력 모듈 제작 (c 언어- gcc)
      이번 페이지에는 c 언어와 wiringpi를 이용하여 스위치의 입력을 받는 module에 대하여 쓰고자 한다. 이전 글에서 밝힌 바와 같이 약간의 트릭이 필요한데 이 글의 끝 즈음에 나올 것이다.
     (기본적으로 wiringPi를 이용한 개발은 다른 블로거의 페이지 등에 많이 나와 있고 또한 가능하다면 원 제작자의 페이지(여기)를 통하여 정보를 얻는 것이 더욱 확실할 것이라 생각 한다.)



제일 먼저 해야 할 일은 버튼(혹은 스위치-버튼으로 통일 하겠다. 동작이 그러하니)과 Raspberry Pi의 GPIO간의 Bin bind가 계획 되어야 할 것 같다 하여 나는 다음과 같이 Bind 하였다.

INPUT(Button)
Btn No.GPIOPin NumberWiringPi ID
1238
2359
3477
417110
527132
622153
7101912
792113
OUTPUT(Relay)
Relay No.GPIOPin NumberWiringPi ID
114815
2151016
318121
423164
524185
625226
782410
872611
다소 복잡해 보이는데 GPIO를 지원하는 H/W와 Library가 서로 Pin define이 틀려서 이런 작업을 해 놓지 않으면 오히려 내가 해깔려 버리는 상황이 오기때문에 이렇게 
미리 정해 놔야 혼란이 없다.

자그럼 본격적으로 Code를 살펴보자.

#include
#include
#include
#include

// Button's WiringPi ID
#define BTN1 8
#define BTN2 9
#define BTN3 7
#define BTN4 0
#define BTN5 2
#define BTN6 3
#define BTN7 12
#define BTN8 13

// constants
#define SwitchCount 8
#define PREVENT_REACT_TICK    250

// global variables
int Buttons[ButtonCount] = {BTN1,BTN2,BTN3,BTN4,BTN5,BTN6,BTN7,BTN8};
int ButtonStatus[ButtonCount] = {1,}; // pull-up 저항을 사용 하므로 1인 상태가 버튼이 눌러지지 않은상태 임.

// Function front declear
void init(void);
void initDevice(void);
unsigned long GetTickCount(void);

///////////////////////////////////////////////////////////////////////////
// main 함수


///////////////////////////////////////////////////////////////////////////
int main(int argc, char* argv[])
{
int nLoop = 0;

if(init())
return 1;

while(1){
unsigned long curTick = GetTickCount();
for(nLoop = 0; nLoop < ButtonCount; nLoop++){
int CurButtonStauts = digitalRead(Buttons[nLoop]);
// Button click 검출
if(0 == ButtonStatus[nLoop] && 1 == CurButtonStauts && (0 == ulTick || (curTick-ulTick>PREVENT_REACT_TICK))){
printf("%d Button clicked\n", nLoop);
}// if
ButtonStatus[nLoop] = CurButtonStauts;
}// for
sleep(50); // Process가 CPU를 너무 만힝 잡아 먹지 않도록 적당히 동작해라.
}// while
return 0;
}


///////////////////////////////////////////////////////////////////////////
// wiringPi를 setup
///////////////////////////////////////////////////////////////////////////
int init(void)
{

if(wiringPiSetup() == -1){
printf("unable to setupwiringPi:%s\n", strerror(errno));
return 1;
}

initDevice();
return 0;
}

///////////////////////////////////////////////////////////////////////////
// 버튼에 할당된 GPIO들을 INPUT을 설정
///////////////////////////////////////////////////////////////////////////
void initDevice(void)
{
int nLoop =0;
for(nLoop = 0; nLoop < ButtonCount; nLoop++)
pinMode(Buttons[nLoop], INPUT);
}

///////////////////////////////////////////////////////////////////////////
// Linux에서 Win32의 getTickCount와 유사한 동작을 하도록 함.
// ※ 서핑을 통해 찾은 코드 조각 
///////////////////////////////////////////////////////////////////////////
unsigned long GetTickCount(void)
{
struct timeval gettick;
unsigned long tick;
int ret;
gettimeofday(&gettick, NULL);
tick = gettick.tv_sec*1000 + gettick.tv_usec/1000;
return tick;
}


시그널은 down edge /up edge 모두 들어오고 우리가 필요한 데이터는 버튼을 눌렀다 띄었을때의 데이터 이므로  down edge와 up edge의 모든 데이터가 필요하다.

 사람의 손이라는 것이 매우 투박하고 느리지만 PC(Raspberry Pi)의 신호 접수 속도는 사람 손의 속도에 비하면 번개와 같다. 또한 물리적인 운동이 회로상에서 발생되는 신호와 1:1로 매칭 되지 않는다. (실제 동작을 해보면 생각보다 많은 신호를 확인 할 수 있다.)

※ 정말로 더 명확하게 동작 하는 push button을 확인하고 싶으면 다음과 같은 로직이 적용되어야 할 것으로 보인다.
   - 아주 짧은 interval로 값을 확인 해당 값의 변화 추이에 따른 상태값의 변화
     ex) down 상태가 일정 시간 이상 되어야하거나 up 상태가 일정 시간 이상일 때문 입력으로 인식 하도록

본인이 사용한 방법은 단지 입력값이 무엇이 었든 회로로부터 한번에 한가지 신호만 들어 온다고 가정 하였기 때문에(회로도 그렇게 구성 했겠다.) 입력 변화의 인터벌 자체를 제한 하는 방법을 사용 한 것이다.
if(0 == ButtonStatus[nLoop] && 1 == CurButtonStauts && (0 == ulTick || (curTick-ulTick>PREVENT_REACT_TICK))){
이전 버튼의 상태가 0(pull-up 저항을 사용 했으므로 누른 상태)이고 현재 상태가 1(손을 땐 상태)이며 입력 변화가 0.25초이상의 간격이 존재할 경우 클릭으로 인식하라는 코드입니다.

3) 5V 8ch Relay 연동 및 control (c언어 -gcc)
  본인이 사용한 relay이다.
 생긴건 투박해도 동작 하는 모습을 보면 왠지 모르게 귀엽게 느껴지기도 한다.
 Pin assign은 사진에서 보는 중앙 부분을 볼때 왼쪽부터 GND/1/2/3/4/5/6/7/8/VCC으로 되어 있다.

인가 전압을 5V로 control은 Raspberry Pi에서 직접 넣었다. 라즈베리가 3.3V 동작인것을 감안하면 잘 안될것 같았는데 잘 되더라. 그리고 서핑한 결과 해외에서 사용해본 유저들의 리뷰를 보아서는 9개월 이상 이상없이 사용 중이다라는 리뷰가 있는 것을 보아서 크게 문제될 것은 없어 보인다. (상전 220v를 컨트롤 하는데 문제가 상기면 어쩌지 하는 걱정은 사실 지금도 있다.)

자 그럼 이놈을 어떻게 해볼까.
우선 계획은 순차적으로 켜지고 순차적으로 켜지고 다 키고 다 끄고 이정도의 동작이면 충분 할 것으로 생각 했다.

그럼 코드 조각을 살펴보자.
#include
#include
#include
#include

// reley's wiringPI ID
#define SW1 15
#define SW2 16
#define SW3 1
#define SW4 4
#define SW5 5
#define SW6 6
#define SW7 10
#define SW8 11

// constants
#define SwitchCount 8

// global variables
int Switchs[SwitchCount] = {SW1,SW2,SW3,SW4,SW5,SW6,SW7, SW8};
int SwitchStatus[SwitchCount]={0,};

// front function declare
void initDevice(void);
void SwitchON(int nIDX);
void SwitchOFF(int nIDX);
void SwitchToggle(int nIDX);
void TurnOffAllSwitch(void);
void TurnOnAllSwitch(void);

/////////////////////////////////////////////////////
// main function
/////////////////////////////////////////////////////
int main(int argc, char* argv[])
{
int nLoop =0;

if(init())
return 1;

// 순서대로 켜기
for( nLoop = 0; nLoop
SwitchON(nLoop);
sleep(1000);
}
// 순서대로 끄기
for( nLoop = 0; nLoop
SwitchOFF(nLoop);
sleep(1000);
}
// 모두 켜기
TurnOnAllSwitch();
sleep(1000);
// 모두 끄기
TurnOffAllSwitch();
sleep(1000);

// 니 맘대로 한번 해봐라.(20번)
for( nLoop = 0; nLoop < SwitchCount; nLoop++){
int nIdx = (int)(rand()%SwitchCount);
SwitchToggle(inIdx);
sleep(1000):
}

return 0;
}

/////////////////////////////////////////////////////
// wiringPI setup
/////////////////////////////////////////////////////
int init(void)
{
printf("Start Init Device\n");

if(wiringPiSetup() == -1){
printf("unable to setupwiringPi:%s\n", strerror(errno));
return 1;
}

initDevice();

printf("End Init Device\n");

return 0;
}

/////////////////////////////////////////////////////
// Relay를 control 할 GPIO를 OUTPUT으로 설정
/////////////////////////////////////////////////////
void initDevice(void)
{
for(nLoop = 0; nLoop < SwitchCount; nLoop++){
pinMode(Switchs[nLoop], OUTPUT);
// 일단 모두 꺼라(만약 켜 있던놈이 있을지도 모르니
SwitchOFF(nLoop);
}
}

/////////////////////////////////////////////////////
// 켜기
/////////////////////////////////////////////////////
void SwitchON(int nIDX)
{
digitalWrite(Switchs[nIDX], 0); // ON
SwitchStatus[nIDX]=1;
printf("Turn On Switch %d\n", nIDX);
}

/////////////////////////////////////////////////////
// 끄기
/////////////////////////////////////////////////////
void SwitchOFF(int nIDX)
{
digitalWrite(Switchs[nIDX], 1); // OFF
SwitchStatus[nIDX]=0;
printf("Turn OFF Switch %d\n", nIDX);
}

/////////////////////////////////////////////////////
// 토글링
/////////////////////////////////////////////////////
void SwitchToggle(int nIDX)
{
if(SwitchStatus[nIDX]==0)
SwitchON(nIDX);
else
SwitchOFF(nIDX);

printf("Toggle Switch %d\n", nIDX);
}

/////////////////////////////////////////////////////
// 모두 끄기
/////////////////////////////////////////////////////
void TurnOffAllSwitch(void)
{
int nLoop=0;
log_message(szLogPath,"Turn Off All Switch\n");
for(nLoop = 0; nLoop < SwitchCount; nLoop++){
SwitchOFF(nLoop);
}
}

/////////////////////////////////////////////////////
// 모두 켜기
/////////////////////////////////////////////////////
void TurnOnAllSwitch(void)
{
int nLoop=0;
log_message(szLogPath,"Turn Off All Switch\n");
for(nLoop = 0; nLoop < SwitchCount; nLoop++){
SwitchON(nLoop);
}
}

코드가 다소 난잡해 보인다.
하지만 끌때 0 켤때 1을 해당 GPIO에 write 해주기만 하면 된다.
결국 핵심은
/////////////////////////////////////////////////////
// 켜기
/////////////////////////////////////////////////////
void SwitchON(int nIDX)
{
digitalWrite(Switchs[nIDX], 0); // ON
SwitchStatus[nIDX]=1;
printf("Turn On Switch %d\n", nIDX);
}

/////////////////////////////////////////////////////
// 끄기
/////////////////////////////////////////////////////
void SwitchOFF(int nIDX)
{
digitalWrite(Switchs[nIDX], 1); // OFF
SwitchStatus[nIDX]=0;
printf("Turn OFF Switch %d\n", nIDX);
}
해당 GPIO에 끌때는 0 킬때는 1을 써준다.

이번 글은 여기까지 하도록 하겠습니다.

화요일, 3월 08, 2016

라즈베리파이2 와 8채널 릴레이를 이용한 전등(형광등) 컨트롤(Lighting control with Raspberry Pi2 & 8ch Relay) 만들기 - Part2


"1. Raspberry pi2의 설치와 설정"은 앞글에 나열한 링크로 대신 하고자 한다. 해당 링크를 따라 들어가 본다면 충분히 해볼만 하도록 쉽게 설명되어 있다.

자.. 그럼.. 20년전 배운 "디지털 공학" 수업 시간에 내가 수업을 잘 들었는지 아니면 그 시간에 잤는지 테스트해 볼 시간이 되었다.

2. 모듈 단위 개발 및 테스트
 1) 수제 push 스위치 제작
정말 회로등을 잘 아는 사람이라면 아마도 내가 한 짓(?)에 대하여 실소를 금치 못할 것이다.

"잘했네" 라고 말하는 사람은 아마 나랑 같은 실수를 하고 있는 사람 일 것이고, "초보자네" 라고 하는 사람이 대부분일 것이다. 무엇이 잘못 되었을까?

그리고 실재 내가 사용한 push button switch는 위에보이는 4pin 짜리가 아니고 멋모르고 사버린 6pin push button switch였다. 그건 이것이랑 또다른 상황에 봉착 하게 되는데 이미지가 없으므로 4pin을 기준으로 내가 했던 멍청한 행동에 대하여 말해 보고자 한다.

※ 일단 Raspberry Pi에 연결된 pin array는 문제 없다.(나는 시리얼 포트나 기타 외장 인터페이스를 사용 하지 않을 것이므로 어느 핀을 쓰던 상관이 없다. 물론 전원과 GND는 제외)

문제는 회로와 전기에 대한 기본 상식이 없었기 때문에 발생한 것이다. 처음 저렇게 만들어 놓고 입력 신호를 읽는데, 도무지 값이.. 틀린것이다. 왜일가?

"Floating Status"
나는 전기가 들어가면(3.3V) 1 안들어가면 0이라고 생각 하고 위와 같이 연결하였다.(물론 Raspberry Pi의 내부에 있는 pull down/up resister를 이용하면 된다는 자료는 찾긴 했지만 나의 귀차니즘은 끝내 모르면 미처버리는 내 성질을 이기지 못했다.  그래서 자료들을 찾아본 결과 스위치의 연결은 다음과 같은 기준으로 연결해야 한다는 결론을 얻었다.
OR

그리고 4 pin push button 및 6 pin push button의 회로도를 꼭 보고 확인해야 한다.

6pin push button(switch)를 기준으로 위의 회로에서 알수 있는 사항은 평상시 A/B, F/E가 연결 되어 있고 누르면 B/C, E/D가 연결 된다. 

4pin push button(switch)는 또 다르게 1/2,3/4가 항상 쌍으로 연결 되어 있고 누르면 1/2/3/4가 연결되는 형태를 갖는다.

그래서 나는 어떻게 했을까.(6Pin 기준)


Pull-up 저항을 기준으로 위와 같이 만들었다. 그런데 위와 같이 복잡하게 한 이유는 무엇일까?
 생각을 해보니 여러 신호가 한꺼번에 오면 내가 처리 하기 귀찮을 것 같기도 하고, 회로에 이상이 상기지나 않을까 하는 걱정도 되어서 위와 같이 만들었다. 위와 같이 만들면 어떤 일이 일어 날까? 한번에 하나의 입력만 받을 수 있게 된다.
즉, 2번 버튼을 누르면 3번이 죽고 1번을 누르면 2/3번이 죽는다.(엄밀히 말해서 죽는게 아니라 floating status가 되어 버린다고 해야 한다. 이 경우 값을 제대로 읽어 오지 않는다.

응용을 위장한 실패인데 아마 회로를 잘아는 사람이라면 위와 같이 만들어서는 안된다고 할지도 모르겠다. 결코 floating status가 좋은게 아니므로.

 ※floating status 가 도대체 먼가 해서 찾아 보았다. 
   - "기준 값이 없으므로 0도 1도 아닌 알수 없는 상태" 라고 설명한다. 
      그냥 모른다. 처리 하지 말아야 되는 값이다. 라고 생각 해버렸다.

그럼 4pin push button(switch)는 어떻게 해야 할가? 4pin push button(switch)는 해보지 않았다만 아래와 같이 하면 되지 않을까 싶다.

 이번 글은 여기 까지 하겠다.  다음 글은 아마 스위치로부터 값을 읽어 오는 부분일 것 같다 이부분에도 약간의 트릭(?)이 숨어 있다. 그럼 다음 글에서 보자.

※ 그리고 나의 오타에 대하여 사과한다.( 발견하는데로 바로바로 고치도록 하겠다.)

월요일, 3월 07, 2016

라즈베리파이2 와 8채널 릴레이를 이용한 전등(형광등) 컨트롤(Lighting control with Raspberry Pi2 & 8ch Relay) 만들기 - 시작

시작.

  나는 이 프로젝트를 가칭 "Intelligence Lighting Control System"이라고 부르고자 한다.
무언가 대단한 것 같지만 그냥 릴레이로 형광등을 켰다가 껐다 하는거다.

  내가 만든 혹은 사용한 코드는 깃허브나 기타 공유가 가능한 곳에 올려 놓을 생각이다.

  현재까지의 상태는 Pi와 릴레이 그리고 수작업으로 만든 push switch를 이용한 동작 및 웹을 이용한 동작까지 마무리된 상태이다. 하지만 이 블로그에는 처음 발생했던 문제부터 차근히 적어나아갈 예정이다.(예전 내가 한다고 했던 것들 중이 안한게 많아서 이번에는 이미 만들어 놨겠다. 못할 이유가 없기 때문에)

  개발에 필요한 Skill은 내가 아는 것을 기반으로 하였기 때문에 다른 언어로 하고자 하시는 분은 다른 자료를 찾는게 빠르지 싶다.

  GPIO 관련 컨트롤 - c언어(gcc)
  WEB Interface - HTML/CSS/PHP5

일단 전체 시스템의 구성은 아래와 같다.

내가 하고자 하는 것은
  "Office내의 전등들을 켜고 끄는 것이다. 그리고 그것이 푸시 스위치로 간단히 해결해야 하며, 때로는 외부에서도 컨트롤이 가능 해야한다." 라는 목표를 가지고  시스템 구성을 위와 같이 하였다.

 네트워크를 좀 아는 사람이라면 가능 할 것이라고 생각 할 것이고 어설프게 알고 있는 사람은 어떻게 저게 가능 하냐라고 물을 것이다. (근데 여기서 저 설명까지 하면 사족이 너무 길어지니 짧게 말하겠다. - 가능 하고 방법은 port forwarding으로 할 것이다.)

라즈베리 파이 설정등은 내가 참고한 사이트 링크로 대신하겠다. 물론! 해당 사이트 게시자에게 허락을 받아 놓은 상태는 절대 아니다 그냥 내맘대로 링크를 건다.(복사도 아니고 링크니까 문제 될게 없다고 생각한다.)

해야 할 일들을 처음에 나열해놓았고 내 블로그도 그 순서에 따라 진행해 나아갈 생각이다.

1. Raspberry pi2의 설치와 설정
   라즈베리안(라스베리파이용 리눅스 정도로만 알면 편하다.)의 설치 [여기]
   라즈베리파이 개발 환경 만들기[여기]
     - gcc 설치
     - apache 설치 [여기]
     - php5 설치
     - wiringpi 설치
  ※ 처음에 mysql을 이용할까 생각 했지만, 어짜피 전원이 나가면 전기스위치 상태가 초기화 되기 때문에 의미 없다고 생각 했고 DB에 업데이트 했다가 읽었다가 하는 Load가 내가 하려고하는 방법보다 더 많다고 생각해서 mysql 사용은 배제하였다.

2. 모듈 단위 개발 및 테스트
   1). 수제 push 스위치 제작 (우여 곡절이 참 많았다. 회로등에 대해서는 20년전에 배운 디지털 공학이 다인데 정말 순수하게 맨땅에 해딩하는 마인드로 차근차근 만들었다.)
   2) 스위치 입력 모듈 제작 (c 언어- gcc)
   3) 5V 8ch Relay 연동 및 control (c언어 -gcc)
3. 모듈 통합 및 통합 테스트
4. WEB 에서 사용할 Interface 제작 socket (c언어 -gcc)
5. WEB interface 재작
6. WEB 연동
7. 마무리 및 앞으로의 방향

자 그럼 다음 글에서 봅시다.