2022-07-06(시간 설정하기)

유슬기·2022년 7월 6일
0

알람약상자

목록 보기
4/6

목표


사용자가 시간을 조정할 수 있도록 한다.

동작


버튼 2개로 제어한다.

  • 버튼1: 설정 진입, 변경할 변수 선택
  • 버튼2: 변수 값 변경

버튼1을 꾹 누르면 시간 설정에 진입한다. 이후 버튼 1로 변경할 변수(년도, 월, 일, …)를 선택하고 버튼 2로 해당 변수의 값을 조정한다.

코드


기존에 year, month, day, hour, minute, second를 각각 한 개의 변수에 담았었다. 이렇게 사용하다 보니 점점 하드 코딩이 많아지고 노가다가 많아져서 각 값들을 date 배열에 담았다.

배열 date는 6x2의 크기를 갖고 있으며 각 배열은 { 값, LCD에서의 위치 }를 나타낸다.

문제점:

  1. 설정에서 나가려고 버튼을 꾹 누르고 떼지 않을 시 설정 진입과 나가기가 살짝 구별이 잘 안 됨. 떼지 않아도 어느정도 누르고 있으면 setting 값이 바뀌기 때문임. 꾹 누른 후 떼었을 때 setting값이 바뀌도록 변경할까 고민.
  2. 아직 초단위는 설정해도 설정값은 무시되고 millis()에 의해서만 변경됨
#include <LiquidCrystal_I2C.h>

LiquidCrystal_I2C lcd(0x27, 16, 2);

extern volatile unsigned long timer0_millis;
unsigned long t;

int setBtnPin = 12; //설정 진입, 변경할 선택
int howLong = 0; //얼마나 꾹 누르고 있었는지
int setting = 0; //설정 진입여부

int changeBtnPin = 13; //시간 값 변경
int changeThing = 0; //date[changeThing][0]

int date[6][2] = {{ 2022, 0 }, //year (시간, 위치)
                  { 01, 5 }, //month
                  { 01, 8 }, //day
                  { 00, 11 }, //hour
                  { 00, 14 }, //minute
                  { 00, 17 } }; //second
            
void setup(){
  lcd.init();
  lcd.backlight();

  pinMode(setBtnPin, INPUT_PULLUP);
  pinMode(changeBtnPin, INPUT_PULLUP);
}

void loop(){
  
  t = millis()/1000;
  
  if (setting) {
    changeDate();
    outSetting();
  } else {
    updateDate();
    enterSetting();
  }

  if (t >= 60) timer0_millis = 0;

}

//설정 들어가기
void enterSetting() {
  int setBtn = digitalRead(setBtnPin);

  if (!setBtn) {
    howLong += 1;
    if (howLong >= 20) setting = 1; //꾹 눌렀으면 세팅 진입
  } else {
    howLong = 0;
  }
}

//설정 나가기
void outSetting() {
  int setBtn = digitalRead(setBtnPin);
  
  if (!setBtn) {
    howLong += 1;
    if (howLong >= 10) setting = 0;
  } else {
    howLong = 0;
  }
}

//설정에서 시간 변경
void changeDate() {
  lcd.cursor();
  lcd.blink();
  
  lcd.setCursor(date[changeThing][1], 0);

  int setBtn = digitalRead(setBtnPin);
  if (!setBtn) {
    changeThing += 1;
    if (changeThing == 6) changeThing = 0;
    delay(200);
  }

  int changeBtn = digitalRead(changeBtnPin);
  if (!changeBtn) {
    date[changeThing][0] += 1;
    dateLimit();
    delay(100);
    showDate();
  }
}

//시간의 흐름에 따라 현재 시간 갱신
void updateDate() {
  lcd.noCursor();
  
  date[5][0] = t; //t가 증가함에 따라 시간 갱신
  dateLimit();//각 시간 값 제한
  
  showDate();//갱신된 시간 보여주기
}

//각 시간 값 한계치 조정
void dateLimit() {
  //year = date[0], month = date[1], day = date[2]
  //hour = date[3], minute = date[4], second = date[5]

  if (date[5][0] >= 60) { //second
    if (!setting) date[4][0] += 1; //min
    date[5][0] = 0;
  }

  if (date[4][0] >= 60) { //minute
    if (!setting) date[3][0] += 1; //hour
    date[4][0] = 0;
  }

  if (date[3][0] >= 24) { //hour
    if (!setting) date[2][0] += 1; //day
    date[3][0] = 0;
  }

  //day
  if ((date[2][0] > 30 && (date[1][0] == 4 || date[1][0] == 6 || date[1][0] == 9 || date[1][0] == 11))
  || (date[2][0] > 31  && (date[1][0] == 1 || date[1][0] == 3 || date[1][0] == 5 || date[1][0] == 7 || date[1][0] == 8 || date[1][0] == 10 || date[1][0] == 12))
  || (date[0][0] % 4 && date[2][0] > 28 && date[1][0] == 2)
  || (!(date[0][0] % 4) && date[2][0] > 29 && date[1][0] == 2)) {
    if (!setting) date[1][0] += 1; //month
    date[2][0] = 1;
  }

  if (date[1][0] > 12) { //month
    if (!setting) date[0][0] += 1; //year
    date[1][0] = 1;
  }

  if (date[0][0] > 2100) { //year
    date[0][0] = 2022;
    date[1][0] = 1;
    date[2][0] = 1;
    date[3][0] = 0;
    date[4][0] = 0;
    date[5][0] = 0;
  }
  
}

//LCD에 시간 보여주기
void showDate() {
  
  //시간들
  for (int i = 0; i < 6; i ++) {
    lcdPrint(String(date[i][0]), date[i][1], 0);
  }
  
  //특수문자들
  lcdPrint("-", 4, 0);
  lcdPrint("-", 7, 0);
  lcdPrint(":", 13, 0);
  lcdPrint(":", 16, 0);
  
}

void lcdPrint(String str, int x, int y) { //LCD에 글자 쓰기

  if (str != ":" && str != "-" && str.length() < 2) str = "0" + str;

  for (int i = 0; i < str.length(); i ++) {
    lcd.setCursor(x + i, y);
    lcd.print(str[i]);
  }
  
}

자잘한 이야기..


코드 정리..

2022-07-05(현재 시간표시) 자잘한이야기.. 에서 포맷을 맞추려고 year을 제외한 나머지 변수가 길이가 2가 아니면 앞에 0을 붙였다. 이를 이번엔 좀 더 간단히 해결했다..

일단 기존엔 시간들을 다 합쳐서 한 번에 lcdPrint를 했는데 이제는 배열이 각 시간의 위치를 기억하고 있기 때문에 따로따로 lcdPrint를 한다. 그래서 lcdPrint에 들어오는 str의 길이가 2보다 작을 경우 앞에 0을 붙이도록 했다. 마찬가지로 year는 2022부터 2100까지만 받기 때문에 상관 없다. 그리고 시간을 구분하는 문자( “:”, “-”)는 0이 필요 없으니까 제외

노란색 배경에 굵은 글씨 - 바뀐 부분

void lcdPrint(String str, int x, int y) { //LCD에 글자 쓰기

  **if (str != ":" && str != "-" && str.length() < 2) str = "0" + str;**

  for (int i = 0; i < str.length(); i ++) {
    lcd.setCursor(x + i, y);
    lcd.print(str[i]);
  }
  
}

사용에 편리함 추가..

dateLimit() 함수는 2022-07-05(현재 시간표시)의 updateDate() 함수와 거의 비슷하다. 달라진 점은 배열의 값을 가져오고 변경한다는 점과 setting이 아닐 시에만 한계 값에 갔을 때 그 다음 값에 영향을 준다는 점이다.

60초가 되면 초는 0초가 되고 1분이 추가된다. 이를 세팅할 때도 적용하면 사용에 불편한 듯 하여 dateLimit()함수 안에 다음과 같은 조건을 각 시간마다 추가했다..

  if (date[5][0] >= 60) { //second
    **if (!setting)** date[4][0] += 1; //min
    date[5][0] = 0;
  }
profile
재밌는 걸 만들자!

0개의 댓글