프로젝트

나도 메이커! 메이커스 여러분들의 작품/프로젝트를 공유하는 공간입니다.

타임라이트 (TIME-Light) 시계 키트 제작 메뉴얼

2018-11-05 11:02:33

 

타임라이트(TIME-Light) 시계 키트

LED 매트릭스로 만드는 나만의 시계 -'반짝이는 시간' 만들기-

 

 

인간의 삶에서 시간은 뗄 수 없습니다.

어려서부터 학습된 시간이라는 개념은 사람의 행동 대부분을 통제하고 있습니다.

정해진 시간에 맞춰서 등교와 출근을 하고, 식사를 하며 잠을 잡니다. 

 

시간에 대해 재미있는 사실 중 하나는 동물 중에서는 오직 인간만이 시간을 의식하고 있으며 나머지 동물들은 직관적인 시간만을 인식하고 있다는 사실입니다.

 

안타깝게도 우리는 시간의 통제 아래서 살아가는 것을 피할 수는 없지만 이러한 시간에 대한 인식과 개념은 결과적으로 세상을 더욱 정확하게 이해하고 서로간의 규칙을 정하는데 많은 도움을 준 것 또한 사실입니다.

 

최근에는 저도 이론적으로 정확하게 설명할 순 없지만 시간의 상대성을 극단적으로 보여준 '인터스텔라' 라는 영화도 있었죠(상대성 이론에 대해 자세한 내용은 묻지 마세요 다칩니다^^)

 

'TIME-Light 시계 키트'를 만들면서 평소에 생각하지 못했던 시간에 대해서 생각해 보는 것은 어떨까요?

 

이번 키트를 설계할 때, 가장 많이 고민했던 것은

'무엇을 표현할 수 있고, 확장성은 무궁한가?' 였습니다.

 

천편일률적인 결과물이 아니라, 표현하고 싶은 방식으로 시간을 표현하는 키트! 

 

그래서 5X5 LED 매트릭스를 통해서

별도의 납땜 없이도 반짝이는 모든 결과물을 표현할 수 있도록 설계했습니다.

 

메인 컨셉은 '시계'입니다. 가장 직관적으로 시와 분을 표현할 수 있도록 디자인했습니다.

물론 시간을 표현하는 방식, 컬러, 빛의 움직임 등은 코딩을 통해서 자유자재로 구현할 수 있습니다. 

 

그리고 MDF 페이스(앞면) 기본 격자무늬를 함께 제작했습니다.

격자무늬 페이스(앞면)으로는 온습도 센서를 활용해서 막대 그래프 모양으로 습도를 표현하거나, 

자신의 기분을 표현하는 캐릭터를 구현할 수도 있고, 친구들과 함께 협업을 통해서 글자를 움직이는 대형 판넬을 만들어 볼 수도 있습니다. 무엇을 표현하고자 하느냐에 따라 결과물이 달라지는 외관이 되는 것이죠!

 

아래 튜토리얼에서는 기본적으로 시계를 만들어 보는 가이드를 제공한 후,

응용할 수 있는 방법을 제안해 드리려고 합니다.

 

저희가 제안한 틀에 갖히지 마시고, 다양한 결과물이

탐라키트(애칭) Time-Light 시계키트를 통해서 구현되길 바랍니다. 

 

차근히 시작해 볼까요?

 

 

 

 

 

 

키트 구성품

NO 부품명 수량 상세설명
1 오렌지보드 1 아두이노 UNO 호환보드
2 네오 LED 매트릭스 1 5 x 5 네오픽셀 LED 모듈
3 시간 측정 모듈 1 Real Time Clock 모듈
4 버튼 2  
5 10K 저항 10 버튼과 함께 사용
6 피에조 스피커 1  
7 브레드보드  1 Mini 사이즈
8 점퍼케이블(암/수) 5 Female / Male Type
9 점퍼케이블(수/수) 15 Male / Male Type
10 MDF 외관 1 MDF
1 빛 확산판 1 아크릴

 

 

 

 

 

 

키트 조립하기(외관 및 회로 연결)

1. 외관 제작하기


 

 

 

2. 회로 연결하기

 

회로 연결을완료했다면 위 사진과 같이 거치대에 본체를 쏘옥!! 결합합니다.

 

 

 

 

소스코드 업로드하기

* 밑에 있는 소스 코드는 라이브러리가 포함되어 있지 않습니다. 코드를 받고 싶으신분은 아래 버튼을 이용해서 꼭 다운로드를 받으시길 바랍니다.

 

 

1.  RTC 모듈 시간 셋팅 코드

  - 시간정보가 저장되어 있지 않은 RTC 모듈에 시간을 저장(셋팅)하기 위해서 업로드하며, 1회만 업로드하여 사용합니다. 

  - 다운로드 받은 소스코드 중 1_Set_Time.ino 파일을 오렌지보드에 업로드합니다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
#include <Wire.h>
#include <TimeLib.h>
#include <DS1307RTC.h>
 
const char *monthName[12= {
  "Jan""Feb""Mar""Apr""May""Jun",
  "Jul""Aug""Sep""Oct""Nov""Dec"
};
 
tmElements_t tm;
 
void setup() {
  bool parse=false;
  bool config=false;
 
  // get the date and time the compiler was run
  if (getDate(__DATE__) && getTime(__TIME__)) {
    parse = true;
    // and configure the RTC with this info
    if (RTC.write(tm)) {
      config = true;
    }
  }
 
  Serial.begin(9600);
  while (!Serial) ; // wait for Arduino Serial Monitor
  delay(200);
  if (parse && config) {
    Serial.print("DS1307 configured Time=");
    Serial.print(__TIME__);
    Serial.print(", Date=");
    Serial.println(__DATE__);
  } else if (parse) {
    Serial.println("DS1307 Communication Error :-{");
    Serial.println("Please check your circuitry");
  } else {
    Serial.print("Could not parse info from the compiler, Time=\"");
    Serial.print(__TIME__);
    Serial.print("\", Date=\"");
    Serial.print(__DATE__);
    Serial.println("\"");
  }
}
 
void loop() {
}
 
bool getTime(const char *str)
{
  int Hour, Min, Sec;
 
  if (sscanf(str, "%d:%d:%d"&Hour, &Min, &Sec) != 3return false;                         
  tm.Hour = Hour;
  tm.Minute = Min;
  tm.Second = Sec;
  return true;
}
 
bool getDate(const char *str)
{
  char Month[12];
  int Day, Year;
  uint8_t monthIndex;
 
  if (sscanf(str, "%s %d %d", Month, &Day, &Year) != 3return false;
  for (monthIndex = 0; monthIndex < 12; monthIndex++) {
    if (strcmp(Month, monthName[monthIndex]) == 0break;
  }
  if (monthIndex >= 12return false;
  tm.Day = Day;
  tm.Month = monthIndex + 1;
  tm.Year = CalendarYrToTm(Year);
  return true;
}
 

 

- 업로드 후 아래 사진을 참고하여, 결과를 확인하세요.

 

2. 최종 소스 코드

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
/*
  제목 : TIME-Light 시계 만들기
  내용 : 네오 LED 매트릭스를 이용해 현재 시간을 보여주는 TIME-Light 시계 만들기
 
  이 소스코드는 kocoafab에서 작성하였습니다.
  소스코드 배포시에는 출처를 남겨주시기 바랍니다.
 
  E mail : contact@kocoa.or.kr
*/
 
#include <Wire.h>
#include "TimeLib.h"
#include "DS1307RTC.h"
#include "Adafruit_NeoPixel.h"
 
// 네오픽셀 LED 셋팅(네오 LED 매트릭스는 25개의 neoPixel을 사용합니다.)
Adafruit_NeoPixel strip = Adafruit_NeoPixel(256, NEO_GRB + NEO_KHZ800);
uint32_t color = strip.Color(255255255);
 
// secondsArray -> 모드 1에서 초를 표현하는 위치를 저장하는 변수(사각형 모서리 부분)
// color2 -> 모드 2에서 진자를 표현하는 위치를 저장하는 변수(가운데 기준 반원)
// rainbowRing -> 정오, 자정시 rainbowCycle 효과를 표현하는 위치를 저장하는 변수
int secondsArray[4= {042420};
int color2[10= {11161718131318171611};
int rainbowRing[8= {6781318171611};
 
// 시간, 분을 표현하는 위치를 저장하는 변수
int timeLoc[24= {27389814131918231817222116151610115616};
 
// 시간을 표현하기 위해 시, 분 LED 색상을 정해줍니다.
uint32_t hoursColor = strip.Color(2551870);
uint32_t minutesColor = strip.Color(02550);
 
// '초'를 표현하기 위해 변수를 선언하고 기본값을 0으로 지정해줍니다.
byte count = 0;
byte count2 = 0;
boolean colorFlag = 0;
 
// 현재 모드 번호를 저장하는 변수를 선언하고 시작 모드를 0으로 지정해줍니다.
byte mode = 0;
 
// 타이머에서 목표시간을 설정하는 변수를 선언해줍니다.
byte alarmTime = 0;
boolean alarmFlag = 0;
 
// 타이머에서 현재 시간(초)과 목표 시간까지 남은 시간(초)를 저장하는 변수를 선언해줍니다.
int checkHours = 0;
int checkMinutes = 0;
int remainMinutes = 0;
 
int checkSeconds = 0;
int remainSeconds = 0;
 
// 현재 시간에 맞게 LED 위치를 정하고 불을 켜주는 함수
void hoursFilter(int hourTime) {
  int h = (hourTime % 12* 2;
  strip.setPixelColor(timeLoc[h], hoursColor);
  strip.setPixelColor(timeLoc[h + 1], hoursColor);
  strip.show();
}
 
// 현재 분에 맞게 LED 위치를 정하고 불을 켜주는 함수
void minutesFilter(int minutesTime, int secondsTime) {
  int m = (minutesTime / 5* 2;
  strip.setPixelColor(timeLoc[m], minutesColor);
  strip.setPixelColor(timeLoc[m + 1], minutesColor);
 
  if (minutesTime == 0 && secondsTime == 0) {
    rainbowCycleRing(15);
    strip.clear();
  }
  strip.show();
}
 
void setup() {
  Serial.begin(9600);
 
  // 네오픽셀 LED를 시작하고 LED 밝기를 255로 설정합니다.
  strip.begin();
  strip.setBrightness(255);
 
  // 버튼 2개(3, 4번 핀)를 INPUT핀으로 설정해주고, 피에저 부저 1개(11번 핀)을 OUTPUT핀으로 설정해줍니다.
  pinMode(3INPUT);
  pinMode(4INPUT);
  pinMode(11OUTPUT);
}
 
void loop() {
  tmElements_t tm; // RTC 객체 선언
 
  if (RTC.read(tm)) { //RTC 모듈로 부터 데이터가 들어온다면
    int hours = tm.Hour; //시(hour) 데이터를 hours 변수에 담아줍니다.
    int minutes = tm.Minute; //분(minute) 데이터를 minutes 변수에 담아줍니다.
    int seconds = tm.Second; //초(second) 데이터를 seconds 변수에 담아줍니다.
 
    // 시, 분, 초를 시리얼 모니터에 출력해 줍니다.
    Serial.print(hours);
    Serial.print(":");
    Serial.print(minutes);
    Serial.print(":");
    Serial.println(seconds);
 
    // 5분에 한번씩 LED를 초기화 해줍니다.(5분 마다 새로운 화면을 표현해 줘야하기 떄문에 화면을 초기화 해줍니다.)
    if ((minutes % 5== 0 && seconds == 0) {
      strip.clear();
      strip.show();
    }
 
    if (!digitalRead(3)) {
      if (!digitalRead(4)) {
        // 양쪽 버튼을 같이 눌렀을 시 모드를 변경합니다.
        mode++;
 
        // 모드가 바뀔때 마다 LED를 초기화 해줍니다.
        strip.clear();
        strip.show();
 
        // 모드가 3을 넘어가면 다시 모드 0부터 시작합니다.
        if (mode > 3) {
          mode = 0;
          alarmTime = 0;
          alarmFlag = 0;
        }
 
        // 모드가 바뀔때 마다 피에조 부저로 바뀐 모드 번호만큼 소리를 냅니다.
        buzzerSetting(mode);
      }
    }
 
    // 모드 0 -> 일반 시계 + 사각형 모서리 부분에 초를 표현합니다.
    if (mode == 0) {
      secondsLight1(hours, minutes, seconds);
    }
 
    // 모드 1 -> 일반 시계 + 가운데 진자로 초 표현
    else if (mode == 1) {
      secondsLight2(hours, minutes, seconds);
    }
 
    // 모드 2 -> 12시 / 24시 기준 현재 남은 시간을 표현합니다.
    else if (mode == 2) {
      remainTime(hours);
    }
 
    // 모드 3 -> 타이머 기능(현재 초단위)
    else if (mode == 3) {
      alarm(hours, minutes, seconds);
    }
 
    strip.show();
  }
}
 
// LED를 제어하는 함수
// 함수 뒤에 들어오는 매개변수 값에 맞는 위치의 LED를 켜줍니다.
void lightOn(int i, int j) {
  strip.setPixelColor(i, color);
  strip.setPixelColor(j, color);
}
 
void lightOn(int i, int j, int k) {
  strip.setPixelColor(i, color);
  strip.setPixelColor(j, color);
  strip.setPixelColor(k, color);
}
 
// 모드 0 ~ 4까지 동작하는 함수 선언
// secondsLight1 -> Mode 0
void secondsLight1(int hours, int minutes, int seconds) {  // 외곽 4각형에 초를 표현합니다.
  hoursFilter(hours); // 시(hour) LED 출력합니다.
  minutesFilter(minutes, seconds); // 분(minute) LED 출력합니다.
 
  // 정가운데 LED를 켜줍니다.
  strip.setPixelColor(12,  strip.Color(4024040));
 
  // 사각형 모서리에 위치한 LED를 하나씩 dimming 해줍니다.
  for (int i = 0; i < 255; i += 5) {
    strip.setPixelColor(secondsArray[count], strip.Color(i, i, i));
    strip.show();
    delay(20);
  }
  strip.setPixelColor(secondsArray[count], strip.Color(000));
  strip.show();
 
  count++;
  if (count > 3) {
    count = 0;
    colorFlag = !colorFlag;
  }
}
 
// secondsLight2 -> Mode 1
void secondsLight2(int hours, int minutes, int seconds) { // 시계 가운데를 기준으로 아래 반원에 진자를 표현합니다.
  delay(200);
 
  hoursFilter(hours); // 시(hour) LED 출력합니다.
  minutesFilter(minutes, seconds); // 분(minute) LED 출력합니다.
 
  // 바로 전에 켰던 LED는 끄면서 다음 LED에 불을 켜줍니다.
  count2++;
  strip.setPixelColor(color2[count2 - 1], strip.Color(000));
  strip.setPixelColor(12,  strip.Color(4024040));
  strip.setPixelColor(color2[count2], strip.Color(255255255));
  strip.show();
 
  if (count2 >= 9) {
    count2 = 0;
  }
}
 
// remainTime -> Mode 2
void remainTime(int hours) {  // 12시(24시)까지 남은시간 표시합니다.
 
  // 오전일 경우 12시를 기준으로 현재 시간을 빼서 남은 시간을 구합니다.
  if (hours < 12) {
    hours = 12 - hours;
  }
  // 오후일 경우 24시를 기준으로 현재 시간을 빼서 남은 시간을 구합니다.
  else if (hours < 24) {
    hours = 24 - hours;
  }
 
  // 위에서 구한 남은 시간을 기준으로 그 이전에 있는 시간을 모두 켜줍니다.
  // ex : 12시 기준으로 2시간이 남았을 경우 1시 2시 모두 LED를 켜줍니다.
  for (int i = hours; hours >= 0; hours--) {
    hoursFilter(hours);
  }
  strip.show();
  delay(500);
}
 
// alarm -> mode 3
void alarm(int hours, int minutes, int seconds) {  // 타임타이머 기능을 사용합니다.(현재 초단위)
  // 오른쪽 버튼(4번핀)을 누를 경우 타이머의 셋팅할 시간이 증가합니다.(5초씩 증가)
  if (!digitalRead(4)) {
    alarmTime += 1;
    if (alarmTime > 12) {
      alarmTime = 0;
      strip.clear();
      strip.show();
    }
    hoursFilter(alarmTime);
    delay(200);
  }
 
  // 왼쪽 버튼(3번핀)을 누를 경우 현재 셋팅된 타이머만큼 시간을 측정합니다.
  // 타이머가 시작될 경우 피에조 부저에서 짧게 소리가 납니다.
  else if (!digitalRead(3)) {
    checkHours = hours;
    checkMinutes = minutes;
    tone(112793100);
    delay(150);
    tone(112349100);
    delay(150);
    alarmFlag = 1;
    checkSeconds = seconds + (alarmTime * 5);
    alarmTime = 0;
  }
 
  // 타이머가 시작되면 5초마다 LED가 여러번 깜빡이고, 그만큼 표시되는 시간이 줄어듭니다.
  if (alarmFlag == 1) {
    if (minutes != checkMinutes) {
      seconds += 60;
    }
 
    remainSeconds = checkSeconds - seconds;
 
    if (remainSeconds % 5 == 0) {
      strip.clear();
      strip.show();
      delay(50);
    }
 
    for (int i = remainSeconds; i > 0; i = i - 5) {
      minutesFilter(i, 1);
    }
 
    strip.setPixelColor(2, strip.Color(4024040));
    strip.setPixelColor(7, strip.Color(4024040));
    strip.setPixelColor(12, strip.Color(4024040));
    strip.show();
 
    // 타이머가 0초가됬을 경우 피에조 부저에서 소리가 나면서 전체 화면에 rainbowCycle 효과가 나옵니다.
    if (remainSeconds == 0) {
      for (int i = 0; i < 5; i++) {
        tone(111976100);
        delay(200);
        tone(111568100);
        delay(200);
      }
 
      rainbowCycle(25);
      alarmFlag = 0;
      mode = 0;
      strip.clear();
      strip.show();
    }
    delay(200);
  }
}
 
// 네오픽셀 rainbowCycle 효과를 표현하는 함수 1
void rainbowCycle(uint8_t wait) {
  uint16_t i, j;
 
  for (j = 0; j < 256; j++) { // 1 cycles of all colors on wheel(싸이클 올릴거면 횟수만큼 255에다 곱해주면됨)
    for (i = 0; i < strip.numPixels(); i++) {
      strip.setPixelColor(i, Wheel(((i * 256 / strip.numPixels()) + j) & 255));
    }
    strip.show();
    delay(wait);
  }
}
 
// 네오픽셀 rainbowCycle 효과를 표현하는 함수 2
void rainbowCycleRing(uint8_t wait) {
  uint16_t i, j;
  strip.setPixelColor(12, strip.Color(000));
  for (j = 0; j < 255 * 3; j += 55) { // 5 cycles of all colors on wheel(싸이클 올릴거면 횟수만큼 255에다 곱해주면됨)
    for (i = 0; i < 8; i++) {
      strip.setPixelColor(rainbowRing[i], Wheel(((i * 256 / strip.numPixels()) + j) & 255));
      delay(100);
      strip.show();
 
      // rainbowCycle중 버튼을 누르면
      if (!digitalRead(4)) {
        return (0);
      }
    }
    delay(wait);
  }
}
 
// 네오픽셀의 색깔을 정해주는 함수
uint32_t Wheel(byte WheelPos) {
  WheelPos = 255 - WheelPos;
  if (WheelPos < 85) {
    return strip.Color(255 - WheelPos * 30, WheelPos * 3);
  }
  if (WheelPos < 170) {
    WheelPos -= 85;
    return strip.Color(0, WheelPos * 3255 - WheelPos * 3);
  }
  WheelPos -= 170;
  return strip.Color(WheelPos * 3255 - WheelPos * 30);
}
 
// 모드 변경시 효과음을 표현하는 함수
void buzzerSetting(byte type) {
  for (int i = type + 1; i > 0; i--) {
    tone(11204750);
    delay(100);
  }
}
 

완성 모습 및 기능

 

 

 

 

 

TIME-Light 시계 키트 기능

시계의 기능은 총 3가지입니다. 

시계 뒷 면에 있는 두 개의 버튼을 동시에 눌러주면 기능이 변경됩니다.

 

시계 기능(2가지) 남은 하루 보기 스탑 워치 타이머

- 시와 분을 표현 / 면적이 적은 색이 시, 많은 색이

- 소스코드에서 입력한 알람 설정 시간에 알람 ON

- 매 시 정각에 에니메이션 효과

- 첫번째 : 모서리 네 개의 원을 통해 초를 표현

- 두번째 : 진자 애니메이션을 통해 초를 표현

- 오늘 하루의 남은 시간을 표현(12시간 /오전, 오후)

- 12시간마다 리셋

- 시간 측정(5초 단위 / 최대 60초 측정 가능)

- (사진 기준) 오른쪽 버튼 : 타이머 설정

- (사진 기준) 왼쪽 버튼 : 타이머 시작

 

 

 

 

 

타임라이트 시계 키트 활용하기

타임라이트 시계 키트는 나만의 페이스 디자인을 통해 다양한 프로젝트로 활용, 확장할 수 있습니다. 

쉽게 공작할 수 있는 우드락부터 레이저 컷팅, 3D 프린터까지 다양한 재료와 도구를 통해 나만의 페이스를 디자인해보는 것은 어떨까요?

 

 

 

 

- 우드락 활용하기

* 검은색 우드락 사용을 권장합니다. 

 

 

 

자료를 다운받아 도안을 1:1 사이즈로 출력합니다. 

자료를 출력한 후 우드락 위에 부착해 절취선을 따라 잘라 나만의 페이스를 디자인하고 제작합니다.  

 

 

 

 

- 레이저 커팅 활용하기

 

자료를 다운받아 백터 디자인 프로그램(일러스트레이터, 오토캐드, 코렐 등)으로 페이스를 디자인합니다.

디자인한 자료를 레이저 커팅기에 import하여 나만의 페이스를 제작합니다. 


 

 

 

- 3D 프린팅 활용하기

 

자료를 다운받아 3D 모델링 프로그램(스케치업, 퓨전, 라이노, 솔리드웍스 등)으로 페이스를 디자인합니다.

디자인한 자료를 3D 프린터에 import하여 나만의 페이스를 제작합니다. 

 

 

kocoafabeditor

코코아팹, kocoafab, 타임라이트 시계키트, TIME-Light 시계키트, 오렌지보드, Orange Board