코코아팹은 누구나 창의적 아이디어를 현실로 만들어 낼 수 있도록
만들고, 공유하고, 배울 수 있는 터전이
되고자 합니다.
아이디와 비밀번호를 잊으셨나요?아이디 / 비밀번호 찾기
코코아팹 회원이 아니신가요? 회원가입
2014-10-27 13:43:03
Real Time Clock 모듈은 전원만 유지되면 RTC 모듈에 설정된 시각에서 시작하여 계속 시간이 흐르고 있는 부품입니다. 이름 그대로 시계라고 할 수 있습니다.
이 것을 이용하여 특정 시각에 어떤일을 하던가, 일정한 시간 단위로 입력되는 값을 출력 / 저장 할 수 있습니다.
이번 컨텐츠에서는 RTC모듈을 세팅해 보고, 이를 가지고 간단한 LCD시계를 만들어 보겠습니다.
* Epoch Time
- 시간을 표현하는 방법으로 1970년 1월 1일 0시 0분 0초를 기준으로 1초에 1씩 증가한 숫자를 시간으로 환산하여 사용하는 방식입니다.
- Unix 기반 OS들이 사용하는 방식이라 Unix Time 이라고도 합니다.
NO | 부품명 | 수량 | 상세설명 |
1 | 오렌지보드 | 1 | 아두이노 |
2 | Tiny RTC V1.1 | 1 | RTC모듈 |
3 | Nokia 5110 LCD | 1 | LCD |
4 | 저항 10kΩ | 4 | 저항 |
5 | 저항 1kΩ | 1 | 저항 |
6 | 저항 330Ω | 1 | 저항 |
7 | 브레드보드 | 1 | 브레드보드 |
8 | 점퍼케이블 | 13 | 점퍼케이블 |
부품명 | 오렌지보드 | Tiny RTC | Nokia LCD | 브레드보드 |
파트 |
부품명 | 저항 10kΩ | 저항 1kΩ | 저항 330Ω | 점퍼케이블 |
파트 |
저항 연결
- 1번 핀 : 1kΩ
- 2~5번 핀 : 10kΩ
- 7번 핀 : 330Ω
아두이노 | RTC모듈 |
GND | GND |
5V | VCC |
A4 | SDA |
A5 | SCL |
LCD 연결 방법은 Nokia 5110 LCD 활용하기 를 참고 하시기 바랍니다.
/* * TimeRTCSet.pde * example code illustrating Time library with Real Time Clock. * * RTC clock is set in response to serial port time message * A Processing example sketch to set the time is inclided in the download */ #include <Time.h> #include <Wire.h> #include <DS1307RTC.h> #include <Arduino.h> #define RST 12 //12번 핀에 RST를 연결합니다. #define CE 11 //11번 핀에 CE를 연결합니다. #define DC 10 //10번 핀에 DC를 연결합니다. #define DIN 9 //9번 핀에 DIN을 연결합니다. #define CLK 8 //8번 핀에 CLK를 연결합니다. static const byte ASCII[][5] = // 아스키코드의 문자열을 나열합니다. { {0x00, 0x00, 0x00, 0x00, 0x00} // 20 ,{0x00, 0x00, 0x5f, 0x00, 0x00} // 21 ! ,{0x00, 0x07, 0x00, 0x07, 0x00} // 22 " ,{0x14, 0x7f, 0x14, 0x7f, 0x14} // 23 # ,{0x24, 0x2a, 0x7f, 0x2a, 0x12} // 24 $ ,{0x23, 0x13, 0x08, 0x64, 0x62} // 25 % ,{0x36, 0x49, 0x55, 0x22, 0x50} // 26 & ,{0x00, 0x05, 0x03, 0x00, 0x00} // 27 ' ,{0x00, 0x1c, 0x22, 0x41, 0x00} // 28 ( ,{0x00, 0x41, 0x22, 0x1c, 0x00} // 29 ) ,{0x14, 0x08, 0x3e, 0x08, 0x14} // 2a * ,{0x08, 0x08, 0x3e, 0x08, 0x08} // 2b + ,{0x00, 0x50, 0x30, 0x00, 0x00} // 2c , ,{0x08, 0x08, 0x08, 0x08, 0x08} // 2d - ,{0x00, 0x60, 0x60, 0x00, 0x00} // 2e . ,{0x20, 0x10, 0x08, 0x04, 0x02} // 2f / ,{0x3e, 0x51, 0x49, 0x45, 0x3e} // 30 0 ,{0x00, 0x42, 0x7f, 0x40, 0x00} // 31 1 ,{0x42, 0x61, 0x51, 0x49, 0x46} // 32 2 ,{0x21, 0x41, 0x45, 0x4b, 0x31} // 33 3 ,{0x18, 0x14, 0x12, 0x7f, 0x10} // 34 4 ,{0x27, 0x45, 0x45, 0x45, 0x39} // 35 5 ,{0x3c, 0x4a, 0x49, 0x49, 0x30} // 36 6 ,{0x01, 0x71, 0x09, 0x05, 0x03} // 37 7 ,{0x36, 0x49, 0x49, 0x49, 0x36} // 38 8 ,{0x06, 0x49, 0x49, 0x29, 0x1e} // 39 9 ,{0x00, 0x36, 0x36, 0x00, 0x00} // 3a : ,{0x00, 0x56, 0x36, 0x00, 0x00} // 3b ; ,{0x08, 0x14, 0x22, 0x41, 0x00} // 3c < ,{0x14, 0x14, 0x14, 0x14, 0x14} // 3d = ,{0x00, 0x41, 0x22, 0x14, 0x08} // 3e > ,{0x02, 0x01, 0x51, 0x09, 0x06} // 3f ? ,{0x32, 0x49, 0x79, 0x41, 0x3e} // 40 @ ,{0x7e, 0x11, 0x11, 0x11, 0x7e} // 41 A ,{0x7f, 0x49, 0x49, 0x49, 0x36} // 42 B ,{0x3e, 0x41, 0x41, 0x41, 0x22} // 43 C ,{0x7f, 0x41, 0x41, 0x22, 0x1c} // 44 D ,{0x7f, 0x49, 0x49, 0x49, 0x41} // 45 E ,{0x7f, 0x09, 0x09, 0x09, 0x01} // 46 F ,{0x3e, 0x41, 0x49, 0x49, 0x7a} // 47 G ,{0x7f, 0x08, 0x08, 0x08, 0x7f} // 48 H ,{0x00, 0x41, 0x7f, 0x41, 0x00} // 49 I ,{0x20, 0x40, 0x41, 0x3f, 0x01} // 4a J ,{0x7f, 0x08, 0x14, 0x22, 0x41} // 4b K ,{0x7f, 0x40, 0x40, 0x40, 0x40} // 4c L ,{0x7f, 0x02, 0x0c, 0x02, 0x7f} // 4d M ,{0x7f, 0x04, 0x08, 0x10, 0x7f} // 4e N ,{0x3e, 0x41, 0x41, 0x41, 0x3e} // 4f O ,{0x7f, 0x09, 0x09, 0x09, 0x06} // 50 P ,{0x3e, 0x41, 0x51, 0x21, 0x5e} // 51 Q ,{0x7f, 0x09, 0x19, 0x29, 0x46} // 52 R ,{0x46, 0x49, 0x49, 0x49, 0x31} // 53 S ,{0x01, 0x01, 0x7f, 0x01, 0x01} // 54 T ,{0x3f, 0x40, 0x40, 0x40, 0x3f} // 55 U ,{0x1f, 0x20, 0x40, 0x20, 0x1f} // 56 V ,{0x3f, 0x40, 0x38, 0x40, 0x3f} // 57 W ,{0x63, 0x14, 0x08, 0x14, 0x63} // 58 X ,{0x07, 0x08, 0x70, 0x08, 0x07} // 59 Y ,{0x61, 0x51, 0x49, 0x45, 0x43} // 5a Z ,{0x00, 0x7f, 0x41, 0x41, 0x00} // 5b [ ,{0x02, 0x04, 0x08, 0x10, 0x20} // 5c ¥ ,{0x00, 0x41, 0x41, 0x7f, 0x00} // 5d ] ,{0x04, 0x02, 0x01, 0x02, 0x04} // 5e ^ ,{0x40, 0x40, 0x40, 0x40, 0x40} // 5f _ ,{0x00, 0x01, 0x02, 0x04, 0x00} // 60 ` ,{0x20, 0x54, 0x54, 0x54, 0x78} // 61 a ,{0x7f, 0x48, 0x44, 0x44, 0x38} // 62 b ,{0x38, 0x44, 0x44, 0x44, 0x20} // 63 c ,{0x38, 0x44, 0x44, 0x48, 0x7f} // 64 d ,{0x38, 0x54, 0x54, 0x54, 0x18} // 65 e ,{0x08, 0x7e, 0x09, 0x01, 0x02} // 66 f ,{0x0c, 0x52, 0x52, 0x52, 0x3e} // 67 g ,{0x7f, 0x08, 0x04, 0x04, 0x78} // 68 h ,{0x00, 0x44, 0x7d, 0x40, 0x00} // 69 i ,{0x20, 0x40, 0x44, 0x3d, 0x00} // 6a j ,{0x7f, 0x10, 0x28, 0x44, 0x00} // 6b k ,{0x00, 0x41, 0x7f, 0x40, 0x00} // 6c l ,{0x7c, 0x04, 0x18, 0x04, 0x78} // 6d m ,{0x7c, 0x08, 0x04, 0x04, 0x78} // 6e n ,{0x38, 0x44, 0x44, 0x44, 0x38} // 6f o ,{0x7c, 0x14, 0x14, 0x14, 0x08} // 70 p ,{0x08, 0x14, 0x14, 0x18, 0x7c} // 71 q ,{0x7c, 0x08, 0x04, 0x04, 0x08} // 72 r ,{0x48, 0x54, 0x54, 0x54, 0x20} // 73 s ,{0x04, 0x3f, 0x44, 0x40, 0x20} // 74 t ,{0x3c, 0x40, 0x40, 0x20, 0x7c} // 75 u ,{0x1c, 0x20, 0x40, 0x20, 0x1c} // 76 v ,{0x3c, 0x40, 0x30, 0x40, 0x3c} // 77 w ,{0x44, 0x28, 0x10, 0x28, 0x44} // 78 x ,{0x0c, 0x50, 0x50, 0x50, 0x3c} // 79 y ,{0x44, 0x64, 0x54, 0x4c, 0x44} // 7a z ,{0x00, 0x08, 0x36, 0x41, 0x00} // 7b { ,{0x00, 0x00, 0x7f, 0x00, 0x00} // 7c | ,{0x00, 0x41, 0x36, 0x08, 0x00} // 7d } ,{0x10, 0x08, 0x08, 0x10, 0x08} // 7e ← ,{0x78, 0x46, 0x41, 0x46, 0x78} // 7f → }; void LcdWriteString(char *characters) // String 문자열을 LCD에 출력합니다. { while(*characters) LcdWriteCharacter(*characters++); } void LcdWriteCharacter(char character) // Character 문자를 LCD에 출력합니다. { for(int i=0; i<5; i++) LcdWriteData(ASCII[character - 0x20][i]); LcdWriteData(0x00); } void LcdWriteData(byte dat) { digitalWrite(DC, HIGH); digitalWrite(CE, LOW); shiftOut(DIN, CLK, MSBFIRST, dat); digitalWrite(CE, HIGH); } void LcdXY(int x, int y) // 커서의 위치를 지정합니다. { LcdWriteCmd(0x80 | x); // 커서의 가로위치를 지정합니다. LcdWriteCmd(0x40 | y); // 커서의 세로위치를 지정합니다. } void LcdWriteCmd(byte cmd) // LCD 명렁어를 보냅니다. { digitalWrite(DC, LOW); //커맨드 입력을 위해 LOW로 설정합니다. digitalWrite(CE, LOW); shiftOut(DIN, CLK, MSBFIRST, cmd); //시리얼 데이터를 LCD에 전송합니다. digitalWrite(CE, HIGH); } void setup() { pinMode(RST, OUTPUT); pinMode(CE, OUTPUT); pinMode(DC, OUTPUT); pinMode(DIN, OUTPUT); pinMode(CLK, OUTPUT); digitalWrite(RST, LOW); digitalWrite(RST, HIGH); // LCD와 연결된 각 핀들의 핀모드르 설정해 줍니다. LcdWriteCmd(0x21); // LCD 확장 명령 LcdWriteCmd(0xBF); LcdWriteCmd(0x04); LcdWriteCmd(0x14); LcdWriteCmd(0x20); LcdWriteCmd(0x0C); for(int i=0; i<504; i++) LcdWriteData(0x00); // LCD를 초기화 합니다 LcdXY(0,5); LcdWriteString("Hello KocoaFAB"); // LCD 맨 아래쪽에 Hell KocoaFAB을 띄웁니다. Serial.begin(9600); setSyncProvider(RTC.get); // 프로세싱에서 세팅한 현재 시간을 가져옵니다. if(timeStatus()!= timeSet) // 가져왔을 경우 확인 메세지를 시리얼 모니터에 출력합니다. Serial.println("Unable to sync with the RTC"); else // 가져오지 못했을 경우 실패 메세지를 시리얼 모니터에 출력합니다. Serial.println("RTC has set the system time"); } void loop() { if(Serial.available()) { time_t t = processSyncMessage(); // 프로세싱에서 동기화한 시간을 받습니다. if(t >0) { RTC.set(t); // 프로세싱에서 받은 시간을 RTC에 세팅해 줍니다. setTime(t); } } digitalClockDisplay(); // 세팅된 시간을 LCD에 출력해 줍니다. delay(1000); } void digitalClockDisplay(){ // 현재 시간을 LCD에 출력하는 함수 String hou = printDigits(hour()); String minu = printDigits(minute()); String sec = printDigits(second()); String sday = printDigits(day()); String smonth = printDigits(month()); String syear = printDigits(year()); // 시,분,초 / 일,월,년 을 String값으로 바꿔줍니다.(한자리일 경우 앞에 0을 붙여서 만들어줍니다.) String time = hou + ":" + minu + ":" + sec; char *timer = new char[time.length() + 1]; strcpy(timer, time.c_str()); // "시:분:초" 순으로 문장을 만들어 줍니다. String day = smonth + "/" + sday + "/" + syear; char * days = new char[day.length() + 1]; strcpy(days, day.c_str()); // "월/일/년" 순으로 문장을 만들어 줍니다. LcdXY(6,1); LcdWriteString(timer); // LCD 6,1 위치에서 부터 시간을 출력합니다. LcdXY(4,3); LcdWriteString(days); // LCD 4,3 위치에서 부터 날짜를 출력합니다. delete[] timer; delete[] days; } String printDigits(int digits){ // 시간이나 날짜가 한자리 일경우 앞에 0을 붙이는 함수 // utility function for digital clock display: prints preceding colon and leading 0 String temp = String(digits); if(digits < 10) temp = "0" + temp; return temp; } // 프로세싱에서 시리얼 포트를 통해 동기화한 시간을 세팅해 줍니다. #define TIME_MSG_LEN 11 // 동기화 메세지 길이 #define TIME_HEADER 'T' // 동기화 메세지 헤더('T') time_t processSyncMessage() { // 시리얼 포트를 통해 들어온 메세지를(프로세싱에서 보낸 시간) 받는 함수 while(Serial.available() >= TIME_MSG_LEN ){ char c = Serial.read() ; Serial.print(c); if( c == TIME_HEADER ) { time_t pctime = 0; for(int i=0; i < TIME_MSG_LEN -1; i++){ c = Serial.read(); if( c >= '0' && c <= '9'){ pctime = (10 * pctime) + (c - '0') ; // 받아온 글자를 숫자로 변환합니다. } } return pctime; } } return 0; }
/** * SyncArduinoClock. * * portIndex must be set to the port connected to the Arduino * * The current time is sent in response to request message from Arduino * or by clicking the display window * * The time message is 11 ASCII text characters; a header (the letter 'T') * followed by the ten digit system time (unix time) */ import processing.serial.*; import java.util.Date; import java.util.Calendar; import java.util.GregorianCalendar; public static final short portIndex = 4; // 연결된 포트번호와 맞는 index를 적어줍니다. public static final char TIME_HEADER = 'T'; public static final char TIME_REQUEST = 7; public static final char LF = 10; public static final char CR = 13; Serial myPort; void setup() { size(200, 200); println(Serial.list()); println(" Connecting to -> " + Serial.list()[portIndex]); myPort = new Serial(this,Serial.list()[portIndex], 9600); } void draw() { if ( myPort.available() > 0) { char val = char(myPort.read()); if(val == TIME_REQUEST){ long t = getTimeNow(); sendTimeMessage(TIME_HEADER, t); } else { if(val == LF) ; //igonore else if(val == CR) println(); else print(val); // echo everying but time request } } } void mousePressed() { // 실행후 나오는 화면을 클릭했을 경우 연결된 시리얼 포트로 시간을 전송합니다. sendTimeMessage( TIME_HEADER, getTimeNow()); } void sendTimeMessage(char header, long time) { // 연결된 시리얼 포트로 시간을 전송하는 함수 String timeStr = String.valueOf(time); myPort.write(header); // send header and time to arduino myPort.write(timeStr); } long getTimeNow(){ // 현재 시간을 받아옵니다. // java time is in ms, we want secs GregorianCalendar cal = new GregorianCalendar(); cal.setTime(new Date()); int tzo = cal.get(Calendar.ZONE_OFFSET); int dst = cal.get(Calendar.DST_OFFSET); long now = (cal.getTimeInMillis() / 1000) ; now = now + (tzo/1000) + (dst/1000); return now; } // 출처 : Time 라이브러리에 SyncArduinoClock 예제
아두이노
위의 소스를 사용하기 위해서는 Time.h 라이브러리가 추가로 필요합니다.
(라이브러리 사용법은 링크를 참고하시기 바랍니다.)
void digitalClockDisplay(){ // 현재 시간을 LCD에 출력하는 함수 String hou = printDigits(hour()); String minu = printDigits(minute()); String sec = printDigits(second()); String sday = printDigits(day()); String smonth = printDigits(month()); String syear = printDigits(year()); // 시,분,초 / 일,월,년 을 String값으로 바꿔줍니다.(한자리일 경우 앞에 0을 붙여서 만들어줍니다.) String time = hou + ":" + minu + ":" + sec; char *timer = new char[time.length() + 1]; strcpy(timer, time.c_str()); // "시:분:초" 순으로 문장을 만들어 줍니다. String day = smonth + "/" + sday + "/" + syear; char * days = new char[day.length() + 1]; strcpy(days, day.c_str()); // "월/일/년" 순으로 문장을 만들어 줍니다. LcdXY(6,1); LcdWriteString(timer); // LCD 6,1 위치에서 부터 시간을 출력합니다. LcdXY(4,3); LcdWriteString(days); // LCD 4,3 위치에서 부터 날짜를 출력합니다. delete[] timer; delete[] days; }
받아온 시간을 LCD화면에 출력하는 함수와 한자리 숫자 앞에 0을 붙여서 String 값으로 만들어 주는 함수 입니다.
받아온 시간을 각각 printDigits() 함수에 보내어 String값으로 바꿔 준 뒤, "시:분:초"와 "월/일/년" 으로 묶은 후 LCD에 출력합니다.
숫자값을 String으로 바꿔서 묶은 후 Char*[] 로 바꾸는 내용은 "아두이노 GPS모듈을 사용하여 LCD에 띄워봅시다." 에서 소프트웨어 설명 부분을 읽어 보시기 바랍니다.
String printDigits(int digits){ // 시간이나 날짜가 한자리 일경우 앞에 0을 붙이는 함수 // utility function for digital clock display: prints preceding colon and leading 0 String temp = String(digits); if(digits < 10) temp = "0" + temp; return temp; }
pinrtDigits()함수는 int값을 받아서 String값으로 바꿔주는 함수입니다. 또한 들어오는 인트값이 한자리일 경우(10 미만) 값 앞에 0을 붙여서 두자리로 만들어서 String값으로 리턴합니다.
// 프로세싱에서 시리얼 포트를 통해 동기화한 시간을 세팅해 줍니다. #define TIME_MSG_LEN 11 // 동기화 메세지 길이 #define TIME_HEADER 'T' // 동기화 메세지 헤더('T') time_t processSyncMessage() { // 시리얼 포트를 통해 들어온 메세지를(프로세싱에서 보낸 시간) 받는 함수 while(Serial.available() >= TIME_MSG_LEN ){ char c = Serial.read() ; Serial.print(c); if( c == TIME_HEADER ) { time_t pctime = 0; for(int i=0; i < TIME_MSG_LEN -1; i++){ c = Serial.read(); if( c >= '0' && c <= '9'){ pctime = (10 * pctime) + (c - '0') ; // 받아온 글자를 숫자로 변환합니다. } } return pctime; } } return 0; }
프로세싱에서 동기화한 Epoch time값을 시리얼 통신을 이용하여 받아 오는 함수 입니다. 시리얼 통신은 한자리씩 받아옴으로 숫자 하나씩 받아서 합칩니다.
이렇게 받아온 시간값을 loop문에서 RTC모듈 시간을 세팅해 줍니다.
위에 올린 프로세싱 소스를 쓰시는 분은 프로세싱 소스를 실행 시킨 후 나오는 화면을 클릭하면 컴퓨터의 시간으로 세팅이 됩니다.(아두이노와 연결 중이어야 합니다.)
따로 시간을 세팅하실 분은 시리얼 모니터에서 'T' + Epoch time값 을 써주시면 해당 시간으로 세팅이 됩니다. (ex : T1414390022)
(Epoch Time 으로 변환하는 방법은 http://www.epochconverter.com/ 과 같은 시간변화를 도와주는 홈페이지가 있습니다. 들어가셔서 원하시는 시간에 대한 Epoch Time 을 받아오시면 됩니다.)
(참고로 위에 있는 소스는 시리얼 모니터에 시간을 출력하지 않습니다. 밑에 사진은 Time 라이브러리에 예제 TimeRTCSet 에제를 실행 시킨 화면입니다.)
프로세싱 소스
public static final short portIndex = 4; // 연결된 포트번호와 맞는 index를 적어줍니다.
프로세싱을 실행 하면 밑의 빨간 네모안에 있는 내용이 나옵니다. 연결된 포트번호를 보시고 아두이노와 연결된 포트번호의 Index를 portIndex에 적어주시면 됩니다.
(위의 사진에서 아두이노와 연결된 포트번호는 COM38입니다. 5번째에 있으니 4번 index를 넣어주시면 됩니다. 인덱스 번호는 0번부터 시작(0, 1, 2, 3, 4))
프로세싱에서는 이 포트번호 설정만 해주시면 됩니다. 이 밑에 적는 내용은 그냥 참고로 알아두시면 편합니다.
void mousePressed() { // 실행후 나오는 화면을 클릭했을 경우 연결된 시리얼 포트로 시간을 전송합니다. sendTimeMessage( TIME_HEADER, getTimeNow()); } void sendTimeMessage(char header, long time) { // 연결된 시리얼 포트로 시간을 전송하는 함수 String timeStr = String.valueOf(time); myPort.write(header); // send header and time to arduino myPort.write(timeStr); } long getTimeNow(){ // 현재 시간을 받아옵니다. // java time is in ms, we want secs GregorianCalendar cal = new GregorianCalendar(); cal.setTime(new Date()); int tzo = cal.get(Calendar.ZONE_OFFSET); int dst = cal.get(Calendar.DST_OFFSET); long now = (cal.getTimeInMillis() / 1000) ; now = now + (tzo/1000) + (dst/1000); return now; }
mousePressed() 함수를 이용하여 화면을 클릭하면 getTimeNow()함수를 이용하여 현재시간을 받아서 sendTimeMessage()함수를 이용하여 시간값을 보냅니다.
getTimeNow()함수는 greogorianCalendar 라이브러리를 사용하여 현재 시간을 받고, sendTimeMessage()함수는 받은 시간앞에 헤더값('T')를 붙여서 연결된 시리얼 포트로 값을 전송합니다.
(아두이노에서 시간값 세팅하기 위해서는 'T' + Epoch Time 을 넣어야 합니다.)
kocoafabeditor
항상 진취적이고, 새로운 것을 추구하는 코코아팹 에디터입니다!