[아두이노] 6-Digit 7-Segment Display 시계 리모콘 제어

IOT/아두이노|2019. 6. 19. 09:00

[아두이노] 6-Digit 7-Segment Display 시계 리모콘 제어



6-Digit 7-Segment Display 지난 시간에 아두이노 시계를 제작하여 가상 실험을 하였습니다. 그런데 아두이노 시계의 초기값을 PC 시리얼모니터에서 시간을 입력하는게 좀 불편해 보여서 다른 외부 장치로 시간을 입력하는 방법이 없을까 고민하다가 무선으로 시간값을 입력하는 방법을 상상하다가 몇가지 상상한 방법 중에 리모콘으로 시간을 입력해볼까 하는 호기심이 생겨서 좋은 학습 자료가 될 것 같아서 리모콘으로 아두이노 시간값을 입력하는 실험하게 되었네요. 리모콘으로 시간을 입력하는데 그 입력된 시간을 리모콘이 누를때 숫자가 변경되고 그 값이 6-Digit 7-Segment Display 부품에 출력되게 해야 하는데 6개의 7-Segment Display을 각각 개별적으로 6개의 숫자를 갱신하게 할지 아니면 시/분/초 단위로 3개의 숫자로 리모콘으로 제어해서 3개의 숫자값을 출력해야 할지 아니면 다른 방법으로 시간 숫자를 제어할지 여러개를 고민하고 실험해 보았습니다. 그중 하나를 선택하여 post로 소개 합니다.

1. 6-Digit 7-Segment Display 회로도


  • 준비물 : 7-Segment Display 6개, IR Sensor 1개, IR remote 1개, 아두이노우노
  • 내용 : 7-Segment Display 부품의 핀 번호 순서에 맞게 아두이노우노에 연결하고, IR Sensor 핀을 2번핀에 연결하시오.

지난 시간의 회로도에서 스위치버턴핀으로 사용한 2번핀을 IR Sensor핀으로 사용했습니다. 수정 부분은 IR Sensor 핀만 변경해주시면 됩니다.


2. 코딩



리모콘 제어를 간단히 복습을 해 볼까요.


[IRemote]

  • 선언 :
#include <IRremote.h>
 
int IRpin = 2;  // IR Sensor Pin
IRrecv irrecv(IRpin);
decode_results results;
  • 초기세팅 : 리모콘 시작
void setup(){
  irrecv.enableIRIn(); // 리모콘 시작
}
  • 리모콘 읽기 : results.value으로 리모콘이 읽은 키 값이 저장되어 있음
void loop(){
  if (irrecv.decode(&results)){ //리모콘 누른값이 없다면 패스
      //results.value 로 리모콘이 눌린 값이 들어 있음.
        
        irrecv.resume(); // 다음값
  }
}

몇달 전 post의 내용인데 오랫만의 리모콘을 아두이노 시계에 적용합니다. 참고로 IRremote 라이브러리 설치는 사전학습에 가시면 나와 있으니깐 보시고 설치한 다음 사용하세요. 혹시 위와 같이 코딩했는데 작동을 안하면 라이브러리를 확인하시고 설치하시면 됩니다.

1) 리모콘 키값 찾기



위 그림의 리모콘의 버턴 4개를 사용할 예정입니다.

  • FUNC/STOP => 시간입력 ON/OFF
  • ST/REPT => 시간 입력 제어 위치
  • 세모 위/아래 => 숫자 증감/감소

가상시뮬레이터로 사전학습의 회로도와 키값 확인 소스를 누르시면 해당 results.value을 확인 할 수 있습니다.

위 코딩에서

Serial.println(results.value);

이렇게 해서 리모콘의 해당 키값을 시리얼모니터로 확인 한 다음 기록해 주세요.

#define STATE 16597183  //FUNC/STOP
#define SELECT_TIME 16609423 //ST/REPT
#define TIME_UP 16601263 //세모 UP
#define TIME_DOWN 16584943 //세모 DOWN

참고로, 실제로 실험하실 때는 리모콘 키값을 직접 누르시고 해당 키 값을 메모장에 기록했다가 위처럼 표현하시면 됩니다. 리모콘 종류에 따라 키 값이 다를 수 있음으로 꼭 사전에 키값을 확인하시고 코딩해 주세요.

2) 시간 입력 리모콘 제어


  • FUNC/STOP => 시간입력 ON/OFF

시간을 입력받을지 결정해야 합니다. 상태변수를 하나 만들어서 아래와 같이 IF 조건문을 만들면 됩니다.

if (irrecv.decode(&results)) //리모콘 누른값이 없다면 패스
    {
     if(results.value==STATE){
           output=!output;
     }
}

FUNC/STOP 리모콘 버턴을 누르면 output가 true or false 교대로 상태값을 갖게 됩니다. output가 true면 시간 입력이고 false면 입력된 시간이 흘러가게 하는 코딩을 하겠습니다.

if(리모콘키값==STATE){
  output=!output; 
  if(output==false){
    timer0_millis 시간 설정;
  }
  else{
    시간 전 입력 초기화;    
  } 
}
if(output==true){        
   시간 입력;
}

코딩을 하면,

 if (irrecv.decode(&results)) //리모콘 누른값이 없다면 패스
    {
     if(results.value==STATE){ //시간 입력 결정
       output=!output;       
       if(output==false){  //리모콘으로 시/분/초 입력한 값을 타이머변수에 저장;
         hour = clockVal[2];
         min = clockVal[1];
         sec = clockVal[0];
         timer0_millis = ((long)hour*3600+min*60+sec)*1000;         
       }
       else{  //시간 입력 전 입력 변수을 초기화
         selectVal=0;
         for(int i=0;i<3;i++){
           clockVal[i]=0;
         }         
       }       
     }
     if(output==true){ //시간 입력 제어
        시간입력;        
     }       
     irrecv.resume(); // 다음값
    }
}

clockVal[3]은 hour, min, sec의 값을 저장하게 됩니다. 그리고 selectVal은 시/분/초의 위치를 나타냅니다.

if(results.value==SELECT_TIME){ //시/분/초 위치 선택
  selectVal++;
  if(selectVal==3)selectVal=0;
}

ST/REPT를 누를 때 시/분/초가 selectVal이 0이면 sec이고, 1이면 min, 2이면 hour를 가리킵니다. selectVal가 3이 되면 다시 0으로 초기화됩니다. 해당 리모콘의 키 버턴을 통해 시/분/초 위치값을 지정할 수 있게 됩니다.

이제 세모 UP/DOWN 버턴을 통해서 시간값을 증감/감소를 제어를 해 볼까요.

if(results.value==TIME_UP) clockVal[selectVal]++;  //시간값 증가
else if(results.value==TIME_DOWN) clockVal[selectVal]--;  //시간값 감소

"clockVal[selectVal]++" or "clockVal[selectVal]--"로 시/분/초 값을 증가/감소를 시키게 됩니다. 여기서, 시간은 24시, 분은 60분, 초는 60초가 max 값입니다. 그래서 증가할 때 숫자는 시간은 24시를 넘지 말아야 하고 분/초는 60을 넘지 말아야 합니다.

그 부분을 IF문으로 표현을 하면 다음과 같습니다.

if(selectVal==2){ //hour 일때              
  if(clockVal[selectVal]==24)clockVal[selectVal]=0;
  else if(clockVal[selectVal]==-1)clockVal[selectVal]=23;
}
else{ //min, sec 일때
  if(clockVal[selectVal]==60)clockVal[selectVal]=0;
  else if(clockVal[selectVal]==-1)clockVal[selectVal]=59;         
}

selectVal가 2이면 시간이니깐 IF문에서 24가 되면 clockVal[selectVal]으로 0으로 만들고 감소할 때 -1이 되면 23으로 만들어 주면 0~23시간 조절하면 됩니다

그리고 그외 0~1이면 분/초로 60이 max이니깐 IF문으로 60이면 clockVal[selectVal]으로 0으로 만들고 감소할 때 -1이 되면 59으로 만들어 주면 0~59분/초를 조절하면 됩니다

3) 출력


  if(output==false){ //시간 출력
    readTime = millis()/1000;
    if(millis()>=86400000){
       timer0_millis=0;
    }
    sec = readTime%60;
    min = (readTime/60)%60;
    hour = (readTime/(60*60))%24; 

    segOutput(5,sec%10,0); //sec 1의 자리
    segOutput(4,sec/10,0); //sec 10의 자리
    segOutput(3,min%10,1); //min 1의 자리
    segOutput(2,min/10,0); //min 10의 자리
    segOutput(1,hour%10,1); //hour 1의 자리
    segOutput(0,hour/10,0); //hour 10의 자리      
  }
  else{ //시간 조정     
    segOutput(5,clockVal[0]%10,0); //sec 1의 자리
    segOutput(4,clockVal[0]/10,0); //sec 10의 자리
    segOutput(3,clockVal[1]%10,1); //min 1의 자리
    segOutput(2,clockVal[1]/10,0); //min 10의 자리
    segOutput(1,clockVal[2]%10,1); //hour 1의 자리
    segOutput(0,clockVal[2]/10,0); //hour 10의 자리      
  }

output가 false이면 시간을 출력하니깐 타이머변수를 세팅하면 millis()함수 현재 시간을 구하여 출력하면 됩니다. 지난시간의 출력 부분하고 같기 때문에 그대로 적용합니다. 하지만 시간을 입력할 때도 6-Digit 7-Segment Display 출력해야 하기 때문에 output가 true일때는 입력상황이니깐 그 입력에 대한 출력부분도 코딩해야 합니다. 입력시간은 clockVal[3] 배열변수에 저장되기 때문에 10의 자리와 1의 자리를 쪼개서 각 숫자를 해당 위치에 맞게 출력시키면 됩니다.

위의 표현한 코딩을 지난시간의 아두이노 시계에 합쳐 보도록 하겠습니다.

4) 종합 소스


#include <IRremote.h>

#define STATE 16597183
#define SELECT_TIME 16609423
#define TIME_UP 16601263
#define TIME_DOWN 16584943
  
const byte IRpin = 2;  
IRrecv irrecv(IRpin);
decode_results results;


//a,b,c,d,e,f,g 상태값
byte segValue[10][7] = {
   {1,1,1,1,1,1,0}, //0
   {0,1,1,0,0,0,0}, //1
   {1,1,0,1,1,0,1}, //2
   {1,1,1,1,0,0,1}, //3
   {0,1,1,0,0,1,1}, //4
   {1,0,1,1,0,1,1}, //5
   {1,0,1,1,1,1,1}, //6
   {1,1,1,0,0,0,0}, //7
   {1,1,1,1,1,1,1}, //8
   {1,1,1,1,0,1,1}  //9  
};

const byte segPin[8]={3,4,5,6,7,8,9,10}; //사용핀{a,b,c,d,e,f,g,dp} 순서대로임
const byte digitPin[6] = {A0,A1,A2,A3,A4,A5}; //segment 위치 핀

boolean output = false;//시간 출력형식 지정
byte selectVal = 0;
int clockVal[3]={0,0,0};

extern volatile unsigned long timer0_millis; //타이머변수
unsigned long readTime; //현재타이머시간
int hour, min, sec;


void setup() {
  Serial.begin(9600);  
  irrecv.enableIRIn(); // 리모콘 시작
 
  for(int i=0;i<10;i++){
    pinMode(segPin[i], OUTPUT);
  }
  for(int j=0;j<6;j++){
    pinMode(digitPin[j], OUTPUT);    
    digitalWrite(digitPin[j], HIGH); 
  }  
}

void loop() {
  IRremoteRead(); //리모콘 읽기
   
  if(output==false){ //시간 출력
    readTime = millis()/1000;
    if(millis()>=86400000){
         timer0_millis=0;
    }
    sec = readTime%60;
    min = (readTime/60)%60;
    hour = (readTime/(60*60))%24; 

    segOutput(5,sec%10,0); //sec 1의 자리
    segOutput(4,sec/10,0); //sec 10의 자리
    segOutput(3,min%10,1); //min 1의 자리
    segOutput(2,min/10,0); //min 10의 자리
    segOutput(1,hour%10,1); //hour 1의 자리
    segOutput(0,hour/10,0); //hour 10의 자리      
  }
  else{ //시간 조정     
    segOutput(5,clockVal[0]%10,0); //sec 1의 자리
    segOutput(4,clockVal[0]/10,0); //sec 10의 자리
    segOutput(3,clockVal[1]%10,1); //min 1의 자리
    segOutput(2,clockVal[1]/10,0); //min 10의 자리
    segOutput(1,clockVal[2]%10,1); //hour 1의 자리
    segOutput(0,clockVal[2]/10,0); //hour 10의 자리      
  }
}

void IRremoteRead(){
   if (irrecv.decode(&results)) //리모콘 누른값이 없다면 패스
    {
     if(results.value==STATE){ //시간 입력 결정
       output=!output;       
       if(output==false){ //리모콘으로 시/분/초 입력한 값을 타이머변수에 저장;
         hour = clockVal[2];
         min = clockVal[1];
         sec = clockVal[0];
         timer0_millis = ((long)hour*3600+min*60+sec)*1000;         
       }
       else{  //시간 입력 전 입력 변수을 초기화
         selectVal=0;
         for(int i=0;i<3;i++){
           clockVal[i]=0;
         }         
       }       
     }
     
     if(output==true){ //시간 입력 제어
       if(results.value==SELECT_TIME){ //시/분/초 위치 선택
         selectVal++;
         if(selectVal==3)selectVal=0;
       }
             
       if(results.value==TIME_UP)clockVal[selectVal]++; //시간값 증가
       else if(results.value==TIME_DOWN) clockVal[selectVal]--; //시간값 감소
 
       if(selectVal==2){ //hour 일때                 
         if(clockVal[selectVal]==24)clockVal[selectVal]=0;
         else if(clockVal[selectVal]==-1)clockVal[selectVal]=23;
       }
       else{ //min, sec 일때
         if(clockVal[selectVal]==60)clockVal[selectVal]=0;
         else if(clockVal[selectVal]==-1)clockVal[selectVal]=59;         
       }

     }   
     irrecv.resume(); // 다음값
    }
}

//LED 초기화
void segClear(){ 
  for(int i=0;i<8;i++){
    digitalWrite(segPin[i], LOW);        
  }
}
//LED 출력
void segOutput(int d, int Number, int dp){ 
  segClear();
  digitalWrite(digitPin[d], LOW); 
  for(int i=0;i<7;i++){
     digitalWrite(segPin[i], segValue[Number][i]);        
  }
  digitalWrite(segPin[7], dp);
  delayMicroseconds(1000);
  digitalWrite(digitPin[d], HIGH); 
}

지난시간의 아두이노 시계 소스를 이해 하셔야 오늘 리모콘 제어 부분을 이해할 수 있습니다. 단편적으로 설명하다 보니 아두이노시계 소스를 모른 상태에서 이해하시기가 좀 어려울 수 있겠네요. 사전학습을 미리 하시고 post를 읽어 주시기 바랍니다.

3. 결과


  • FUNC/STOP => 시간입력 ON/OFF
  • ST/REPT => 시간 입력 제어 위치
  • 세모 위/아래 => 숫자 증감/감소

FUNC/STOP 버턴을 누르면 시간을 입력 할 준비 상태가 됩니다. 세모 위/아래 바로 누르면 sec가 움직입니다. ST/REPT를 누르면 min 위치로 이동하고 다시 세모 위/아래를 누르면 min이 움직입니다. 또, ST/REPT를 누르면 hour 위치로 이동하고 다시 세모 위/아래를 누르면 hour이 움직입니다. 입력 할 시간 세팅이 끝나면 FUNC/STOP 버턴을 누르면 입력된 시간이 타이머변수에 세팅이 되고 그때부터 시간이 흘러가고 6-Digit 7-Segment Display에 현재 시간을 출력하게 됩니다.

한번 위의 순서대로 시간을 입력해 보세요.

아래 동영상은 회로도가 복잡해서 가상시뮬레이터를 실행하면 지연 렉이 발생합니다. 그래서 1초가 움직이는데 좀 오래 걸리는데 이게 잘못된 코딩은 아니고 지연 렉이니깐 감안하시고 보세요.


마무리


오늘은 가상시뮬레이터에서 가상으로 아두이노 시계를 만들었는데 거기에다가 리모콘을 연결하여 시간 입력을 실험 하였습니다. 약간 시간을 입력하는 로직이 좀 복잡할 수 있습니다. 혹시 위 post 이해가 안가지면 if문 단위로 가상 데이터를 넣어서 어떤 동작을 하는지 체크하시면서 테스트 해보시기 바랍니다.

참고로, 실제 구현 한것도 post에 올릴려고 했는데 IR Sensor를 찾지 못해서 실제로는 구현을 못해 봤네요. 만약 찾게 되면은 4-Digit 7-Segment Display 부품에 IR Sensor를 연결하여 실제로 실험한 post를 올리겠습니다.


댓글()