시간 millis() 함수로 시계 코딩(아두이노)

in #kr-arduino6 years ago

시간 millis() 함수로 시계 코딩(아두이노)



오늘은 millis() 함수를 이용한 시계 코딩을 실험 할 예정입니다. 지난 시간의 초를 시/분/초를 변환하거나 시/분/초를 초로 변환하는 코딩을 실험 하였습니다. 여기에, millis()함수와 전역 타이머변수를 이용하여 아두이노 시계 코딩을 만들어 보겠습니다. 시계 코딩의 결과는 시리얼모니터로 간단히 출력하는 실험이지만 나중에 외부 출력 부품을 이용하여 디지털 시계를 만들거나 아날로그 시계를 만들어 볼 예정입니다. 우선은 시계 코딩으로 어떻게 동작하는지 먼저 만들어 봐야 겠지요.

이제 본격적으로 실험을 해볼까요.

1. 시간



시간은 하루를 기준으로 24시간, 1시간은 60분, 1분은 60초로 이루어져 있습니다. 누구나 다 아는 기본 내용이지요. 시계는 하루 24시간을 표시하는 기계입니다. 즉, 24시간 이상의 시간이 흐르는 다시 리셋되어 시간이 0시부터 흐르게 됩니다. 가령, 25시가 입력되면 다음날 새벽 1시가 되겠죠. 이처럼 몇시간이 입력되었던 24시간을 기준으로 표시됩니다.

표시 => 전체시간 % 24 = 현재시간
예) 25%24 =1

몇시간이 입력되었던 날짜와 상관없이 24시간을 기준으로 표시만 하면 됩니다. 대충 시간은 어떤 느낌으로 출력해야 할지 아시겠지요. "23:59:59"가 되었을 때 1초가 흐르면 바로 "0:0:0"으로 리셋을 시키게 하여 24시간 기준으로 표시되게 하면 되겠죠.

2. 시간 입력



  // 시:분:초 입력
  if(Serial.available()){
    String inString = Serial.readStringUntil('\n');    
    int index1 = inString.indexOf(':'); 
    int index2 = inString.indexOf(':',index1+1);   
    int index3 = inString.length();
    int hour = inString.substring(0, index1).toInt();
    int min = inString.substring(index1+1,index2).toInt();
    int sec = inString.substring(index2+1,index3).toInt();
}

위 코딩은 지난시간에 시리얼모니터로 시/분/초를 입력한 값을 각각 hour, min, sec 변수에 저장하는 코딩입니다. 시간을 입력하고 그 시간문자열에서 시간들을 분리해 냅니다. 위 코딩 설명은 사전학습에서 이야기 했기 때문에 생략합니다.

3. 시간 동작


아두이노는 시간을 어떻게 흐르게 할까요. 아두이노 내부에 타이머가 있고 이 타이머시간값을 millis()함수를 사용하여 타이머시간값을 가져올 수 있고 이 값을 통해서 아두이노 시간을 흐르게 할 수 있습니다. 아두이노는 1초 단위의 시간을 구할 수 있습니다. delay(1000) 하고 시간변수를 +1씩 증가시키면 쉽게 1초단위로 시간값을 구할 수 있지만, 다른 부품들과 연동하기 위해서는 delay()함수를 될 수 있으면 쓰면 안됩니다. 그래서, millis()함수를 사용하는 데 다음 아래와 같이 코딩하면 됩니다.

if(millis()-timeVal>=1000){
  sec++; //1초증가
}

여기서, 시간을 외부로 부터 입력받아 세팅한 뒤에 시간이 흘러가게 해야 한다면 위 명령문에서 1초 증가를 변수에다 저장하여 시간을 돌리면 초, 분, 시 기준으로 아래 if문을 만들어 시간값이 증가하도록 코딩을 하면 됩니다.

if(sec==60){
  sec=0;
  min++; //1분증가
}
if(min==60){
  min=0;
  hour++; //1시간증가
}
if(hour==24){
  hour=0;  
}

이렇게 3개의 if문을 만들어서 시간값을 만들어 낼 수 있지만 이 방법으로 하지 않을 겁니다. 다른 방식으로 타이머변수에 현재시간으로 초기화하는 방법으로 코딩하겠습니다.

extern volatile unsigned long timer0_millis; //타이머변수
timer0_millis = ((long)hour*3600+min*60+sec)*1000;

이렇게, 시리얼모니터에서 시간을 입력 받은 hour, min, sec값을 초로 변환 한 값을 timer0_millis값으로 초기화 하면 어떤 현상이 발생할까요. timer0_millis 시간을 기준으로 타이머는 계속 흘러가기 때문에 millis()함수로 읽은 시간값은 입력된 시간을 기준으로 시간이 흘러가게 됩니다. 그렇게 되면 지난시간에 배운 millis()함수 값을 읽어서 시/분/초로 변환만 하면 현재 시간을 실시간으로 얻을 수 있습니다.

readTime = millis()/1000;
      
sec = readTime%60;
min = (readTime/60)%60;
hour = (readTime/(60*60))%24; 

이렇게 하면 시간을 알 수 있게 됩니다. 이게 바로 아두이노 시계가 되는 것이죠. 위의 if문으로 시간을 1초씩 증가해서 초, 분, 시를 증가시키든 방금했던 방식으로 아예 timer0_millis을 현재 시간으로 초기화 할지는 여러분이 선택만 하시면 됩니다.

두번째 timer0_millis변수로 초기화 하는 방법을 취하고 24시간 기준으로 timer0_millis를 리셋시키는 방법으로 코딩을 수정하면 다음과 같은 코딩이 됩니다.

readTime = millis()/1000;
      
if(millis()>=86400000){ //24시간 초기화
  timer0_millis=0;
}
sec = readTime%60;
min = (readTime/60)%60;
hour = (readTime/(60*60))%24; 

millis() 현재 시계의 시간값으로 타이머가 돌고 있습니다. 그 값이 24시간이 되면 0시로 리셋이 되어야 합니다.

1일 24시간 => 1x60x60x24 = 86400 x 1000(1초) = 86400000

위처럼, millis() 시간값이 86400000이 되면 1일이 됩니다. 24시간이라는 소리죠. 그러면 0시로 리셋을 해야 합니다. 간단히, timer0_millis 변수를 0으로 초기화 하면 0시부터 타이머가 돌게 됩니다. 이렇게 하면 24시간 기준으로 무한으로 아두이노 시계가 돌게 되겠죠.

그냥, 계속 millis()함수로 읽으면 되지 하실 분도 있을거에요. timer0_millis변수는 unsigned long 자료형입니다. 수치를 저장하는 값이 한계가 있습니다. 한달 조금 지나면 리셋이 되고 사실 그전에 시간에 대해 정확히 리셋 시점을 잡아줘야 합니다. 안그러면 리셋되는 시점에서 시간에 문제가 생길 수 있습니다. 편하게 24시간 기준으로 리셋을 시키는 것이 좋겠죠. 리셋 시점은 여러분들의 자유입니다.

4. 종합 소스


extern volatile unsigned long timer0_millis; //타이머변수
unsigned long timeVal; //이전시간
unsigned long readTime; //현재타이머시간
int hour, min, sec;
boolean state=false;

void setup()
{
  Serial.begin(9600);  
}

void loop()
{ 
  if(Serial.available()){
    String inString = Serial.readStringUntil('\n');    
    int index1 = inString.indexOf(':'); 
    int index2 = inString.indexOf(':',index1+1);   
    int index3 = inString.length();
    
    hour = inString.substring(0, index1).toInt();
    min = inString.substring(index1+1,index2).toInt();
    sec = inString.substring(index2+1,index3).toInt();
  
    
    timer0_millis = ((long)hour*3600+min*60+sec)*1000;
    state=true;
    timeVal=millis();
  }
  if(state==true){ //시리얼모니털 출력 시작
    
    if(millis()-timeVal>=1000){ //1초 단위 출력
     readTime = millis()/1000;
      
     if(millis()>=86400000){
       timer0_millis=0;
     }
     timeVal = millis();
   
     sec = readTime%60;
     min = (readTime/60)%60;
     hour = (readTime/(60*60))%24;       
      
     Serial.print(hour);
     Serial.print(" : ");
     Serial.print(min);
     Serial.print(" : ");
     Serial.println(sec);      
    }
  }
}

위 소스에서는 1초 단위로 출력하기 위해서

if(millis()-timeVal>=1000){ //1초 단위 출력
 timeVal = millis();
}

이 명령문을 사용했습니다. 이것은 1초 단위로 시리얼모니터로 출력하기 위한 락을 걸어놓은 느낌의 조건문입니다. 1초가 되었을 때 시간 값을 시리얼모니터로 출력을 할꺼야 하는 조건문으로 생각하세요. 위에서 설명했던 각각의 내용들을 전체 소스에 배치한 것 뿐이니깐 혹시 이해 안되면 위 설명을 다시 보시기 바랍니다.

5. 결과


시리얼모니터로 시간값을 입력하면 그 시간을 기준으로 타이머가 계속 흘러간다. 그래서 시계처럼 시간을 출력할 수 있다.

6. millis() 함수 대신에 timer0_millis 사용 가능


extern volatile unsigned long timer0_millis; //타이머변수

이렇게 선언하게 되면 구지 millis()함수를 사용안하고 timer0_millis로 시간값을 가져올 수 있습니다.

Serial.print(timer0_millis);

이렇게 한번 찍어보세요. millis()함수와 같은 시간값을 출력됩니다. 내부적으로 타이머변수 값이 갱신이 되는데 이 변수를 호출할 때 호출한 시점의 시간값을 담고 있습니다. timer0_millis를 millis()함수 대신해서 사용할 수 있습니다. 그런데 추천은 드리지 않습니다. 외부전역변수 자체가 하드웨어 내부에 사용하는 변수들을 건들게 되면 정상적으로 하드웨어가 동작하지 못하는 경우가 발생하기 때문에 특별한 목적이 없는 한 사용하지 않는 방향으로 코딩 하셨으면 합니다.

마무리


이렇게 해서 시리얼모니터로 입력한 시간값을 기준으로 아두이노의 시계는 동작하게 됩니다. 이걸 시리얼모니터로 출력했지만 다른 외부 출력 부품을 이용하면 아두이노 전용 시계가 만들어 지겠죠. 참고로 7-Segment Dispaly로 출력하면 디지털 시계가 되는 것이고 Stepper Motor로 회전 시키면 아날로그 시계가 됩니다. LED로 시계판을 만들면 LED 시계가 되겠죠. 이건 어떤 출력부품을 사용하느냐에 따라서 다양한 아두이노 시계를 만들 수 있게 됩니다.

한번 오늘 배운 시계 원리를 이용하여 어떤 아두이노 시계를 만들지 상상의 나래를 펼쳐 보세요.

Sort:  

(jjangjjangman 태그 사용시 댓글을 남깁니다.)
호출에 감사드립니다! 즐거운 스티밋하세요!

짱장맨 방문에 감사합니다.

상상 그만 할래요. 하나 만들어 주세요 ღ•̀ᴗ•̀ღ

4-Digit 7-Segment Display 부품으로 시계 만들까 고민중에 있네요. 온습도센서 부품 해먹은 거 고장 안났으면 온도계만들면 딱인데 뭐롤 만들까 고민중이네요.^^

serial 시계 보다는 LED digit시계가 활용성은 더 뛰어나겠죠

시계 코딩을 하는데 그 결과를 바로 다이렉트로 확인하기 위해서 시리얼시계 형태로 출력한거고요. 이 코딩을 기반으로 말씀하신 것처럼 7-Segment Display 부품으로 시간을 출력해도 되고요. 아니면 LED를 시계판 모양으로 만들어서 LED 시계를 하셔도 되고요. 그것도 아니면 Stepper Motor를 회전시켜서 시계로 표현하셔도 돼요.

Coin Marketplace

STEEM 0.29
TRX 0.12
JST 0.034
BTC 63247.38
ETH 3242.29
USDT 1.00
SBD 3.90