프로젝트

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

Dot Clock 만들기

2016-01-14 18:57:23

개요

 

여러분은 손목시계, 전자시계 혹은 휴대폰 중 어떤 시계를 사용하시나요? 

숫자로 나타내는 시계 말고 패턴으로 숫자를 나타내어서 사용하는 시계는 어떨까요?

최근에는 시계 하나도 인테리어 소품으로 많이 사용되고 있더라구요~!

휴대성보다는 인테리어용도를 더욱 살리는, LED를 이용한 시계를 만들어 보았습니다.

아래의 그림은 각각 1 7 3 9를 나타냅니다.

즉 17시 39분, 오후 5시 39 분이었답니다!

 

이 프로젝트는 instructables의 stregoi님의 프로젝트를 응용하여 만들었습니다.

Domino Clock 프로젝트 보기

 

 

 

관련 튜토리얼

 

Neo Pixel 사용하기

RTC 사용하기

한글 시계 만들기

 

 

부품목록

 
NO 부품명 수량 상세설명
1 오렌지보드 1 아두이노 우노
2 Tiny RTC V1.1 1 RTC 모듈
3 Neopixel led 44 strip

4

10KΩ 저항

1

10KΩ 저항

5 점퍼케이블 16 이상 점퍼케이블
6 스위치 1  
7 브레드보드 1 브레드보드

 

 
부품명 오렌지 보드 Strip Neo Pixel  스위치 Tiny RTC 10KΩ 저항 브레드 보드 점퍼 케이블
파트 x 1 x 44

x 1

x 1 x 1 x 1

x 16 이상

 

 

 

제작 과정

 

하드웨어 Making

 

* 가독성을 높이기 위해 브레드보드와 회로도에는 일부 점퍼케이블을 생략했습니다. 크기나 모양에 맞추어 점퍼케이블의 수는 늘리면 될 것 같습니다^^

브레드보드 레이아웃

1. 오렌지보드의 GND 핀을 브레드보드의 - 버스에 연결합니다.
2. 오렌지보드의 5V 핀을 브레드보드의 + 버스에 연결합니다.
3. DS1307RTC의 SDA, SCL, GND, 5V 을 오렌지 보드와 브레드 보드에 각각 연결합니다.
4. Neo Pixel strip LED의 GND는 브레드보드의 - 버스에, 5V는 + 버스에, DIN은 좌측부터 각각 11번, 9번, 5번, 3번 핀에 연결합니다. 
5. 스위치를 연결해 줍니다.
6. 스위치의 한쪽 끝이 연결 된 브레드보드를 + 버스와 연결합니다.
7. 반대쪽을 4번핀과 연결합니다. 
8. 8에서 연결된 브레드보드와 저항을 연결합니다.

9. 저항의 끝을 - 버스와 연결합니다.

 

 

회로도

 

 

 

 

소프트웨어 코딩

 

#include <Adafruit_NeoPixel.h>
#include <Time.h>
#include <Wire.h>
#include <DS1307RTC.h>

#define HourMonthPIN1 3
#define HourMonthPIN2 5
#define MinuteDayPIN1 9
#define MinuteDayPIN2 11// 3,5,9,11 번 핀에 NeoPixel을 연결

const int buttonPin = 4; // 4번 핀에 스위치를 연결
boolean buttonStatus; // 스위치의 상태를 읽어오기 위한 boolean 자료형으로 변수 선언
int Flag;

int color1[9] = {34,255,173,139,47,255,255,226,65};
int color2[9] = {27,0,0,34,48,88,235,121,199};
int color3[9] = {255,36,255,0,0,83,152,255,232}; // led의 색상을 나타내기 위한 배열

int lednum[9][9] =
{
  { 5, 0, 0, 0, 0, 0, 0, 0, 0 },
  { 4, 6, 0, 0, 0, 0, 0, 0, 0 },
  { 4, 5, 6, 0, 0, 0, 0, 0, 0 },
  { 1, 2, 8, 9, 0, 0, 0, 0, 0 },
  { 1, 2, 5, 8, 9, 0, 0, 0, 0 },
  { 1, 2, 4, 6, 8, 9, 0, 0, 0 },
  { 1, 2, 4, 5, 6, 8, 9, 0, 0 },
  { 0, 1, 2, 3, 7, 8, 9, 10, 0 },
  { 0, 1, 2, 3, 5, 7, 8, 9, 10 }
}; // 숫자의 형태를 나타내기 위한 배열

Adafruit_NeoPixel strip[4] =
{
  (Adafruit_NeoPixel(11, HourMonthPIN1, NEO_GRB + NEO_KHZ800)),
  (Adafruit_NeoPixel(11, HourMonthPIN2, NEO_GRB + NEO_KHZ800)),
  (Adafruit_NeoPixel(11, MinuteDayPIN1, NEO_GRB + NEO_KHZ800)),
  (Adafruit_NeoPixel(11, MinuteDayPIN2, NEO_GRB + NEO_KHZ800))
}; // LED 객체 선언

void setup() {
  Serial.begin(9600);
#if defined (__AVR_ATtiny85__)
  if (F_CPU == 16000000) clock_prescale_set(clock_div_1);
#endif

   pinMode(buttonPin, INPUT_PULLUP);
   
  for (int i = 0; i<4; i++)
  { strip[i].begin(); }

  for (int i = 0; i<4; i++)
  { strip[i].show();  }

  while (!Serial);
}

void loop() {
  Serial.print(digitalRead(buttonPin));
  tmElements_t tm; // RTC 객체 선언

  if(digitalRead(buttonPin)==HIGH)
  {
    if(Flag==0)
    {
      Flag = 1;
      buttonStatus = !buttonStatus;
      for(int k=0; k<4; k++)
    {
    LED_RESET (k);
    }
    }
  }
  else
  {
    Flag = 0;
  } // 스위치의 상태를 변환

  if (RTC.read(tm)) { //RTC 모듈로 부터 데이터가 들어온다면
   
    int hourFirst = tm.Hour / 10; // 시(hour) 데이터의 십의 자리를 hourFirst 변수에 담는다.
    int hourLast = tm.Hour % 10; // 시(hour) 데이터의 일의 자리를 hourLast 변수에 담는다.
    int minuteFirst = tm.Minute / 10; // 분(mintue) 데이터의 십의 자리를 minuteFirst 변수에 담는다.
    int minuteLast = tm.Minute % 10; // 분(mintue) 데이터의 일의 자리를 minuteLast 변수에 담는다.
    int seconds = tm.Second; // 초(second) 데이터를 seconds에 담는다.
    
    int monthFirst = tm.Month / 10; // 월(month) 데이터의 십의 자리를 monthFirst 변수에 담는다.
    int monthLast = tm.Month % 10; // 월(month) 데이터의 일의 자리를 monthLast 변수에 담는다.
    int dayFirst = tm.Day / 10; // 일(day) 데이터의 십의 자리를 dayFirst 변수에 담는다.
    int dayLast = tm.Day % 10; // 일(day) 데이터의 일의 자리를 dayLast 변수에 담는다.

    
    if(buttonStatus) // buttonStatus의 상태가 초기 값이라면
    {
        Filter(monthFirst, 0); Filter(monthLast, 1);// 월(hour) LED 출력
        Filter(dayFirst, 2); Filter(dayLast, 3);// 일(minute) LED 출력
        
        for (int i = 0; i<4; i++)
        { strip[i].show();  }

        if(hourFirst==0 && hourLast == 0 && minuteFirst == 0 && minuteLast == 0 && seconds == 0)
        {
        for(int k=0; k<4; k++)
        {
          LED_RESET(k);
        }
        delay(1000);
        } // 날짜가 변하는 시점인 hourFirst, hourLast, minuteFirst, minuteLast, seconds가 모두 0일 때에 LED 전체를 한번 reset 한다.
    }
    
    else // 그 외의 값에는
    {
      Filter(hourFirst, 0); Filter(hourLast, 1);// 시(hour) LED 출력
      Filter(minuteFirst, 2); Filter(minuteLast, 3);// 분(minute) LED 출력
      
      for (int i = 0; i<4; i++)
      { strip[i].show();  }
      
      if(seconds == 0)
      { LED_RESET (3);  
      delay(1000);} // 다음 minuteLast 출력을 위해 seconds가 0일때 마지막 LED를 reset 한다.
      
      if(minuteLast == 0 && seconds == 0)
      { LED_RESET (2);  
      delay(1000);} // 다음 minuteFirst 출력을 위해 seconds와 minuteLast가 0일때 세번째 LED를 reset 한다.
      
      if(minuteFirst == 0 && minuteLast == 0 && seconds == 0)
      { LED_RESET (1);  
      delay(1000);}  // 다음 hourLast 출력을 위해 seconds, minuteLast, minuteFirst가 0일때 두번째 LED를 reset 한다.
      
      if(hourLast == 0 && minuteFirst == 0 && minuteLast == 0 && seconds == 0)
      { LED_RESET (0);  
      delay(1000);}  // 다음 hourFirst 출력을 위해 seconds, minuteLast, minuteFirst,hourLast가 0일때 첫번째 LED를 reset 한다.
   
  }
}
}

void Filter(int timenum, int stripnum) // 각각의 자리에 필요한 숫자를 설정받는 함수
{
  if (timenum == 0)
  { LED_RESET(stripnum);  } // 각각의 자리가 0이라면 LED 전체를 RESET

  for (int t = 1; t < 10; t++) // 각각의 자리에 따라 Repeat 함수를 반복
  {
    if (timenum == t)
    { Repeat(timenum, stripnum);  }
  }
  
}

void Repeat(int clocknum, int stripnum) // 각각의 자리의 숫자를 받아 미리 선언된 패턴, 순서, 색에 따라 LED를 점등시켜주는 함수
{
  int i = clocknum - 1;

  for (int j = 0; j <= clocknum - 1; j++)
  {
    strip[stripnum].setPixelColor(lednum[i][j], color1[i], color2[i], color3[i]);
    strip[stripnum].show();
  }
} // 

void LED_RESET (int stripnum) // LED를 reset 하는 함수
{
for (int i = 0; i<11; i++)
      {
        strip[stripnum].setPixelColor(i, 0, 0, 0);
        strip[stripnum].show();
      }
}

 

 

스케치 설명

 

int color1[9] = {34,255,173,139,47,255,255,226,65};
int color2[9] = {27,0,0,34,48,88,235,121,199};
int color3[9] = {255,36,255,0,0,83,152,255,232}; // led의 색상을 나타내기 위한 배열

 

Klant님의 도움으로 Adobe Color CC라는 사이트를 알게 되어 각각 숫자에 따른 색상의 값을 미리 배열로 선언해두었습니다.

Adobe Color CC 바로 가기

Neo pixel LED 에서는 흰끼가 많은 파스텔톤 느낌의 색상보다는 원색에 가까운 계열의 색상이 더 선명하게 보였습니다.

<Adobe Color CC>

 

 

 

 

int lednum[9][9] =
{
  { 5, 0, 0, 0, 0, 0, 0, 0, 0 },
  { 4, 6, 0, 0, 0, 0, 0, 0, 0 },
  { 4, 5, 6, 0, 0, 0, 0, 0, 0 },
  { 1, 2, 8, 9, 0, 0, 0, 0, 0 },
  { 1, 2, 5, 8, 9, 0, 0, 0, 0 },
  { 1, 2, 4, 6, 8, 9, 0, 0, 0 },
  { 1, 2, 4, 5, 6, 8, 9, 0, 0 },
  { 0, 1, 2, 3, 7, 8, 9, 10, 0 },
  { 0, 1, 2, 3, 5, 7, 8, 9, 10 }
}; // 숫자의 형태를 나타내기 위한 배열

 

LED에서 패턴에 따라 숫자를 나타내기 위해 순서를 배열로 설정해 주었습니다.

1을 점등할 때는 한개의 숫자만 필요하지만 2차원 배열로 선언해 주었기 때문에

나머지 값들은 일단 0으로 설정하고 Repeat 함수에서 필요한 부분까지만 반복문을 이용하였습니다.

각각의 숫자에 따른 패턴은 아래와 같습니다. 

 

 

 

 

 

    int hourFirst = tm.Hour / 10; // 시(hour) 데이터의 십의 자리를 hourFirst 변수에 담는다.
    int hourLast = tm.Hour % 10; // 시(hour) 데이터의 일의 자리를 hourLast 변수에 담는다.
    int minuteFirst = tm.Minute / 10; // 분(mintue) 데이터의 십의 자리를 minuteFirst 변수에 담는다.
    int minuteLast = tm.Minute % 10; // 분(mintue) 데이터의 일의 자리를 minuteLast 변수에 담는다.
    int seconds = tm.Second; // 초(second) 데이터를 seconds에 담는다.
    
    int monthFirst = tm.Month / 10; // 월(month) 데이터의 십의 자리를 monthFirst 변수에 담는다.
    int monthLast = tm.Month % 10; // 월(month) 데이터의 일의 자리를 monthLast 변수에 담는다.
    int dayFirst = tm.Day / 10; // 일(day) 데이터의 십의 자리를 dayFirst 변수에 담는다.
    int dayLast = tm.Day % 10; // 일(day) 데이터의 일의 자리를 dayLast 변수에 담는다.

 

RTC 모듈에서는 시간과 날짜를 int 값으로 전달합니다. 전달 받은 값들을 일의자리와 십의 자리로 나눠서 출력하기 위한 변수를 선언해준 구간 입니다.

예를 들어 전달받은 시간이 23시 37분이라면 23을 10으로 나눠 몫과 나머지를 출력하여 각각 나타내었습니다.

여기서는 시간과 날짜를 출력 하였지만 RTC 모듈에서는 연도도 출력 할 수 있습니다.

연도를 출력 하고 싶다면 int yearFirst = tmYearToCalendar(tm.Year) / 1000; 처럼 각각의 자리에 맞는 연산을 실행해 주면 됩니다

이연정

RTC, 시계, LED