/*
제목 : 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(25, 6, NEO_GRB + NEO_KHZ800);
uint32_t color = strip.Color(255, 255, 255);
// secondsArray -> 모드 1에서 초를 표현하는 위치를 저장하는 변수(사각형 모서리 부분)
// color2 -> 모드 2에서 진자를 표현하는 위치를 저장하는 변수(가운데 기준 반원)
// rainbowRing -> 정오, 자정시 rainbowCycle 효과를 표현하는 위치를 저장하는 변수
int secondsArray[4] = {0, 4, 24, 20};
int color2[10] = {11, 16, 17, 18, 13, 13, 18, 17, 16, 11};
int rainbowRing[8] = {6, 7, 8, 13, 18, 17, 16, 11};
// 시간, 분을 표현하는 위치를 저장하는 변수
int timeLoc[24] = {2, 7, 3, 8, 9, 8, 14, 13, 19, 18, 23, 18, 17, 22, 21, 16, 15, 16, 10, 11, 5, 6, 1, 6};
// 시간을 표현하기 위해 시, 분 LED 색상을 정해줍니다.
uint32_t hoursColor = strip.Color(255, 187, 0);
uint32_t minutesColor = strip.Color(0, 255, 0);
// '초'를 표현하기 위해 변수를 선언하고 기본값을 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(3, INPUT);
pinMode(4, INPUT);
pinMode(11, OUTPUT);
}
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(40, 240, 40));
// 사각형 모서리에 위치한 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(0, 0, 0));
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(0, 0, 0));
strip.setPixelColor(12, strip.Color(40, 240, 40));
strip.setPixelColor(color2[count2], strip.Color(255, 255, 255));
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(11, 2793, 100);
delay(150);
tone(11, 2349, 100);
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(40, 240, 40));
strip.setPixelColor(7, strip.Color(40, 240, 40));
strip.setPixelColor(12, strip.Color(40, 240, 40));
strip.show();
// 타이머가 0초가됬을 경우 피에조 부저에서 소리가 나면서 전체 화면에 rainbowCycle 효과가 나옵니다.
if (remainSeconds == 0) {
for (int i = 0; i < 5; i++) {
tone(11, 1976, 100);
delay(200);
tone(11, 1568, 100);
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(0, 0, 0));
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 * 3, 0, WheelPos * 3);
}
if (WheelPos < 170) {
WheelPos -= 85;
return strip.Color(0, WheelPos * 3, 255 - WheelPos * 3);
}
WheelPos -= 170;
return strip.Color(WheelPos * 3, 255 - WheelPos * 3, 0);
}
// 모드 변경시 효과음을 표현하는 함수
void buzzerSetting(byte type) {
for (int i = type + 1; i > 0; i--) {
tone(11, 2047, 50);
delay(100);
}
}
nylee 2019-12-21 13:28:06
시간을 맟출 때 다운 박은 settime만 실행시키면 되는가요? 아니면 프로그램에서 고쳐야 하는지요.
자세한 설명 부탁 드립니다.