아두 이노 알람 시계 | [Ds3231Rtc] Arduino로 알람시계 만들기…^^ 168 개의 베스트 답변

당신은 주제를 찾고 있습니까 “아두 이노 알람 시계 – [DS3231RTC] Arduino로 알람시계 만들기…^^“? 다음 카테고리의 웹사이트 th.taphoamini.com 에서 귀하의 모든 질문에 답변해 드립니다: th.taphoamini.com/wiki. 바로 아래에서 답을 찾을 수 있습니다. 작성자 설계하는 아부지 Innoker 이(가) 작성한 기사에는 조회수 2,977회 및 좋아요 21개 개의 좋아요가 있습니다.

아두 이노 알람 시계 주제에 대한 동영상 보기

여기에서 이 주제에 대한 비디오를 시청하십시오. 주의 깊게 살펴보고 읽고 있는 내용에 대한 피드백을 제공하세요!

d여기에서 [DS3231RTC] Arduino로 알람시계 만들기…^^ – 아두 이노 알람 시계 주제에 대한 세부정보를 참조하세요

게시일 : 2021/04/24
#동수아부지 #Innoker #DS3231 #RTC #Arduino #알람시계 #DIY
반갑습니다.
동수아부지 Innoker 입니다.
이번 영상은 RTC 모듈을 이용하여 아두이노로 알람시계를 만드는 영상입니다.
부담 없는 영상으로 많은 시청 부탁드립니다.

블로그
: https://blog.naver.com/autofa
: https://blog.daum.net/autofa
아두이노 소스링크
: https://blog.naver.com/autofa/222321391665
회로도 링크
: https://blog.naver.com/autofa/222321391665
License of Music
Intro Music
📢BGM
✔️Track – Anwar Amr – Epicness
✔️theartistunion – https://theartistunion.com/tracks/af8a3c
✔️나눔뮤직 – https://tv.naver.com/v/10425657
Main Music
Song: Ikson – Tide (Vlog No Copyright Music)
Music promoted by Vlog No Copyright Music.
Video Link: https://youtu.be/8ZlG5pLQvcg​

아두 이노 알람 시계 주제에 대한 자세한 내용은 여기를 참조하세요.

[아두이노] 알람시계 프로젝트(RTC, LCD, 블루투스) – 시그널보내

[아두이노] 알람시계 프로젝트(RTC, LCD, 블루투스). 시그널보내 2021. 7. 1. 19:58. 두번째 프로젝트는 RTC모듈을 이용한 알람시계이다.

+ 여기를 클릭

Source: seo-dh-elec.tistory.com

Date Published: 4/4/2021

View: 5641

아두이노 말하는알람시계 예제 – DFPlayer – postpop

이전 글에서 아두이노를 이용한 시계를 코딩해 보았다. 이번에는 DFPlayer 사운드 모듈을 이용해 정시가 되면 시간을 스피커를 통해 알려주고, …

+ 더 읽기

Source: postpop.tistory.com

Date Published: 1/17/2022

View: 9272

주제와 관련된 이미지 아두 이노 알람 시계

주제와 관련된 더 많은 사진을 참조하십시오 [DS3231RTC] Arduino로 알람시계 만들기…^^. 댓글에서 더 많은 관련 이미지를 보거나 필요한 경우 더 많은 관련 기사를 볼 수 있습니다.

[DS3231RTC] Arduino로 알람시계 만들기...^^
[DS3231RTC] Arduino로 알람시계 만들기…^^

주제에 대한 기사 평가 아두 이노 알람 시계

  • Author: 설계하는 아부지 Innoker
  • Views: 조회수 2,977회
  • Likes: 좋아요 21개
  • Date Published: 2021. 4. 24.
  • Video Url link: https://www.youtube.com/watch?v=nv5wbDSv72w

[아두이노] 알람시계 프로젝트(RTC, LCD, 블루투스)

두번째 프로젝트는 RTC모듈을 이용한 알람시계이다.

RTC이란 Real Time Clock의 약자이며 쉽게 ‘시계’라고 생각하면 된다.

내부적으로 시간을 연산하는 프로그램을 갖고 있어서 초기값만 설정해 준 후 전원만 연결하면 실제시계와 똑같이 동작한다.

처음 RTC모듈을 접했다면 대부분 Serial화면으로 출력하는 법 먼저 배웠을 것이다.

그러나 아두이노는 제품을 만들 수 있는 장점이 있는데 왜 PC화면으로만 출력을 하는가?

보드에 RTC뿐만 아니라 LCD화면과 피에조 부저, LED, 블루투스 통신이 가능하도록 PC와 독립적인 알람시계를 만들어 보자!

준비물

RTC모듈, LCD패널(I2C), HC-06, 피에조부저, RLED, 9V배터리, 여러 점퍼선과 저항

설계 방법

1. RTC모듈에 초기값(현재시간), 알람을 울릴 시간을 프로그램으로 설정해준다.

2. LCD화면에 현재시간을 띄운다.

2-1) 첫째줄에 (연 / 월 / 일 / 요일)을 띄우고

2-2) 둘째줄에 (시 : 분 : 초)를 띄운다.

3. 알람시간이 되면 LED가 점등을 하고 피에조 부저를 울린다.

4. 블루투스 통신을 하여 스위치로 OFF할 수 있도록 한다.

5. 9V배터리를 연결하여 PC와 독립적인, 별개의 시계처럼 사용한다.

소스코드

#include #include #include #include #include #include virtuabotixRTC myRTC(22, 24, 26); LiquidCrystal_I2C lcd(0x27, 16, 2); int piez = 30; int led = 32; void setup() { Serial.begin(9600); lcd.init(); lcd.backlight(); myRTC.setDS1302Time(30, 11, 18, 5, 1, 7, 2021); //rtc 현재시간 설정 pinMode(piez, OUTPUT); //피에조 Serial.begin(9600); Serial1.begin(9600); //블루투스 시작 pinMode(led, OUTPUT); } void loop() { myRTC.updateTime(); lcd.setCursor(1, 0); lcd.print(myRTC.year); //연 lcd.print(“/”); lcd.print(myRTC.month); //월 lcd.print(“/”); lcd.print(myRTC.dayofmonth); //일 lcd.print(“/”); switch (myRTC.dayofweek) { //요일 case 1: lcd.print(“Sun”); break; case 2: lcd.print(“MON”); break; case 3: lcd.print(“TUE”); break; case 4: lcd.print(“WED”); break; case 5: lcd.print(“THU”); break; case 6: lcd.print(“FRI”); break; case 7: lcd.print(“SAT”); break; } lcd.setCursor(5, 1); lcd.print(myRTC.hours); lcd.print(“:”); lcd.print(myRTC.minutes); lcd.print(“:”); lcd.print(myRTC.seconds); if (myRTC.hours == 18 && myRTC.minutes == 11 && myRTC.seconds == 35) { //알람시간설정 tone(piez, 698); digitalWrite(led, HIGH); } if (Serial1.available()) { //블루투스로 데이터가 들어오면 char ch = Serial1.read(); //ch변수로 데이터 받기 Serial1.write(ch); //모니터 프로그램에 출력 switch (ch) { case ‘1’: noTone(piez); digitalWrite(led, LOW); break; case ‘2’: noTone(piez); digitalWrite(led, LOW); break; } } if (Serial.available()) { Serial1.write(Serial.read()); } delay(1000); lcd.clear(); }

사진 및 동영상

왼) 전원연결X 오) 전원연결O

결론 및 느낀점

이번 프로젝트는 일상생활에서 흔시 사용되는 모듈 3개를 융합시켜보았다.

시계, LCD화면, 블루투스 이것들은 우리가 평소에 사용하면서 그저 각각의 기능만 갖고있는 줄 알았지만

사실 전부 융합되어 있어서 결국엔 하나의 시스템을 갖추고 있다는 사실을 알게 되었다.

나는 단순히 3개의 모듈만 연결시키는것도 어려웠는데 PC, 스마트폰은 대체 얼마나 복잡하게 설계가 되어있기에

작은 공간안에 수많은 기능을 할 수 있는지 다시한번 놀랐다.

또한 이번 경험을 통해 임베디드에 살짝(?)가까워진 느낌도 들었고

우리가 사용하는 전자제품은 전부 센서의 조합인 것을 알게 되었다.

지금은 비록 단순한 알람시계를 만들었지만 더 나아가서 언젠간 고성능의 시제품을 개발할 수 있는날까지 끝까지 도전하자!!

아두이노 말하는알람시계 예제 – DFPlayer

이전 글에서 아두이노를 이용한 시계를 코딩해 보았다. 이번에는 DFPlayer 사운드 모듈을 이용해 정시가 되면 시간을 스피커를 통해 알려주고, 사용자 입력이 있을 경우(버튼 또는 센서) 현재의 시간을 알려주며, 설정한 시간이 되면 알람이 울리도록 해보자.

우선 앞선 예제에서 사용한 아두이노와 ESP01의 wifi를 이용한 시간 동기화를 그대로 사용하고 싶었으나 아래의 이유로 그렇게 할 수가 없었다.

아두이노 + DFPlayer + ESP01을 동시에 연결하기 위해서는 DFPlayer나 ESP01중 하나를 아두이노 Software Serial에 연결하고 다른 하나는 Hardware Serial에 연결해야만 한다.

1. Hardware Serial에 ESP01이 연결된 상태에서 Software Serial에 연결된 DFplayer가 불특정 하게 리셋되는 현상이 발생한다. 버튼을 이용해 DFplayer에 사운드 play 명령을 보내게 되면 play 되다 바로 리셋된다.

2. Hardware Serial에 ESP01이 연결된 상태에서는 시리얼 모니터에서 텍스트 입력해도 아두이노에서 수신이 안된다. 아두이노 RX핀에 연결된 블루투스 TX핀을 제거해야만 시리얼 모니터의 텍스트 입력을 아두이노가 수신할 수 있게 된다. 즉 알람 시간을 설정해야 하나 설정할 수가 없다. 다른 방법으로 설정한다고 해도 1번의 이유로 정상적인 결과가 나오지 않는다.

따라서 아두이노를 이용한 말하는 알람시계 예제에서는 ESP01을 사용하지 않고 진행하겠다. 시간 동기화는 시리얼 모니터 텍스트 명령을 통해 하거나 컴퓨터 연결 없이 독립된 외부 전원으로 기동 중일 때에는 블루투스 모듈을 Hardware Serial에 연결하고 안드로이드 앱을 이용하여 텍스트 명령으로 동기화시켜 주어야 한다.

아두이노 + DFPlayer + LCD 연결

DFPlayer의 전원을 3.3V에 연결해주고 GND는 브레드보드 공통에 연결하지 않고 아두이노 GND핀에 직접 연결해 주었다. 이전 글에서 사용했던 아두이노 시계 코드에서 ESP01 관련 코드를 제거한 아래 스케치를 업로드해주고 시리얼 모니터를 통해 텍스트 명령어를 입력하여 작동상태를 확인해보자. 시계 코드가 이해가 안 된다면 이전 글을 참조하기 바란다.

더보기 #include #include LiquidCrystal_I2C lcd(0x27, 16, 2); // Set the LCD address to 0x27 for a 16 chars and 2 line display int h = 12; // initial Time display is 12:59:45 PM int m = 59; uint8_t s = 45; bool meridian = true; uint8_t hm; bool now_am = true; //AM uint16_t yy = 2019; uint8_t mm = 1; uint8_t dd = 1; uint8_t week_num = 1; String week_day = “SUN”; bool adjust = false; bool display_t = false; unsigned long int start_time = 0; void setup() { Serial.begin(9600); lcd.begin(); lcd.backlight(); lcd.clear(); lcd.setCursor(0,0); // row, col의 좌표로 커서를 위치 col: 0 ~ 15 row: 0, 1 lcd.print(“Arduino clock”); } void loop() { cal_time(); adjust_time(); } void adjust_time() { if (Serial.available() > 0) { String temp = Serial.readStringUntil(‘

‘); if (temp == “dtime”) display_time(); else if (temp == “s”) { s = 0; start_time = millis(); display_t = true; } else if (temp == “mm”) { m++; display_t = true; } else if (temp == “m”) { m–; display_t = true; } else if (temp == “hh”) { h++; display_t = true; } else if (temp == “h”) { h–; display_t = true; } else if (temp == “24”) { meridian = !meridian; display_t = true; } else if (temp == “yy”) { yy++; display_t = true; } else if (temp == “y”) { yy–; display_t = true; } else if (temp == “mon”) { mm++; adjust = true; display_t = true; } else if (temp == “mon-“) { mm–; adjust = true; display_t = true; } else if (temp == “dd”) { dd++; adjust = true; display_t = true; } else if (temp == “d”) { dd–; adjust = true; display_t = true; } else if (temp == “ww”) { week_num++; adjust = true; display_t = true; } else if (temp == “w”) { week_num–; adjust = true; display_t = true; } else if (temp.startsWith(“tset”)) { String set = temp.substring(4, 6); h = set.toInt(); set = temp.substring(6, 8); m = set.toInt(); set = temp.substring(8, 10); s = set.toInt(); start_time = millis(); adjust = true; display_t = true; } else if (temp.startsWith(“dset”)) { String set = temp.substring(4, 8); yy = set.toInt(); set = temp.substring(8, 10); mm = set.toInt(); set = temp.substring(10, 12); dd = set.toInt(); adjust = true; display_t = true; } if (m >= 60) m = 0; else if (m < 0) m = 59; if (h >= 24) h = 0; else if (h < 0) h = 23; if (h < 12) now_am = true; else now_am = false; hm = h; if (meridian == true && hm == 12) { now_am = false; } else if (meridian == true && hm > 12) { hm = hm – 12; } cal_dd(); if (display_t == true) { display_time(); display_t = false; } } } bool change_millis = false; uint16_t millis_sec = 994; void cal_time() { if (millis() – start_time >= millis_sec) { // 시간 간격: 밀리초 start_time = millis(); // 상기 조건을 만족할때의 밀리초를 다시 start_time에 저장하여 조건 초기화 change_millis = ! change_millis; if (change_millis == true) millis_sec = 995; else millis_sec = 994; s++; if (s == 60) { s = 0; m++; } if(m == 60) { m = 0; h++; } if(h == 12) { now_am = false;} else if (h == 24) { h = 0; now_am = true; dd++; week_num++; cal_dd(); } hm = h; if(meridian == true && hm == 12) { now_am = false; } else if (meridian == true && hm > 12) { hm = hm – 12; } else now_am = true; display_time(); if (h == 1 && m == 1 && s == 10) { // NTP 동기화 시간 } } } void cal_dd() { uint8_t m_dds; switch (mm) { case 1: m_dds = 31; break; case 2: m_dds = 28; break; case 3: m_dds = 31; break; case 4: m_dds = 30; break; case 5: m_dds = 31; break; case 6: m_dds = 30; break; case 7: m_dds = 31; break; case 8: m_dds = 31; break; case 9: m_dds = 30; break; case 10: m_dds = 31; break; case 11: m_dds = 30; break; case 12: m_dds = 31; break; } if (adjust == false) { if (dd > m_dds) { dd = 1; mm++; } if (mm > 12) { mm = 1; yy++; } if (week_num > 7) week_num = 1; week_day_converter(); } else { if (dd > m_dds) dd = 1; else if (dd < 1) dd = m_dds; if (mm > 12) mm = 1; else if (mm < 1) mm = 12; if (week_num > 7) week_num = 1; else if (week_num < 1) week_num = 7; week_day_converter(); adjust = false; } } void week_day_converter() { switch (week_num) { case 1: week_day = "SUN"; break; case 2: week_day = "MON"; break; case 3: week_day = "TUE"; break; case 4: week_day = "WED"; break; case 5: week_day = "THU"; break; case 6: week_day = "FRI"; break; case 7: week_day = "SAT"; break; } } void display_time() { Serial.print(yy); Serial.print("."); if (mm < 10) { Serial.print("0"); Serial.print(mm); Serial.print("."); } else { Serial.print(mm); Serial.print("."); } if (dd < 10) { Serial.print("0"); Serial.print(dd); Serial.print(". "); } else { Serial.print(dd); Serial.print(". "); } Serial.print(week_day); Serial.print(" "); if (meridian == true) { if (now_am == true) Serial.print("AM "); else Serial.print("PM "); if (hm < 10) { Serial.print("0"); Serial.print(hm); Serial.print(":"); } else { Serial.print(hm); Serial.print(":"); } } else { if (h < 10) { Serial.print("0"); Serial.print(h); Serial.print(":"); } else { Serial.print(h); Serial.print(":"); } } if (m < 10) { Serial.print("0"); Serial.print(m); Serial.print(":"); } else { Serial.print(m); Serial.print(":"); } if (s < 10) { Serial.print("0"); Serial.println(s); } else Serial.println(s); lcd.setCursor(0,0); lcd.print(yy); lcd.print("."); if (mm < 10) { lcd.print("0"); lcd.print(mm); lcd.print("."); } else { lcd.print(mm); lcd.print("."); } if (dd < 10) { lcd.print("0"); lcd.print(dd); lcd.print(". "); } else { lcd.print(dd); lcd.print(". "); } lcd.print(week_day); lcd.print(" "); lcd.setCursor(0,1); if (meridian == true) { if (now_am == true) lcd.print("AM "); else lcd.print("PM "); if (hm < 10) { lcd.print("0"); lcd.print(hm); lcd.print(":"); } else { lcd.print(hm); lcd.print(":"); } } else { if (h < 10) { lcd.print("0"); lcd.print(h); lcd.print(":"); } else { lcd.print(h); lcd.print(":"); } } if (m < 10) { lcd.print("0"); lcd.print(m); lcd.print(":"); } else { lcd.print(m); lcd.print(":"); } if (s < 10) { lcd.print("0"); lcd.print(s); lcd.print(" "); } else { lcd.print(s); lcd.print(" "); } // 크리스탈 LCD에는 pritln 명령어 없음 } 시간을 말하는 동작을 구현하기 위해 DFPlayer의 SD 카드에 아래의 음성 예제 파일의 폴더 01, 02, ADVERT를 모두 선택 후 복사하여 붙여 넣기 해준다. 사용자 요청에 의해 재생되는 음성파일은 마이크로소프트의 기본 TTS 엔진인 혜미를 이용하여 제작되었으며, 정시를 알리는 음성파일은 삼성 피쳐폰 시절의 정시 알림 파일을 구해서 넣어놓았다. 알람의 끼워넣기 멘트용으로는 TTS 엔진 혜미로 제작된 것과 알람 소리샘 파일을 구해서 넣었음을 알린다. DFPlayer에 관한 이전 글 "DFplayer - 아두이노 사운드 모듈"을 읽었다는 전제하에 진행하겠다. DFPlayer를 이용해서 문장으로 연결된 음성 파일을 재생하는 방법으로는 일반 폴더 내에 있는 음성파일을 재생시키는 명령어를 사용하고 재생 종료 시 DFPlayer가 보내주는 재생 종료 코드를 받아 문장을 이루는 다음 연결 파일을 재생시키는 방법을 이용하면 좀 더 간단하게 구현할 수 있다. 하지만 이럴 경우에는 일반 폴더에 음성용 파일이 들어가 있어서 만약 일반 사운드 파일을 재생할 경우 음성파일도 재생목록에 포함되어 사운드 파일만 재생할 수가 없게 된다. 또한 사운드 파일이 재생되고 있을 때에는 종료를 시키고 음성파일을 재생해야 돼서 문장이 완료된 뒤에는 재생 중이던 사운드 파일을 이어서 재생할 수가 없다. 따라서 ADVERT폴더에 음성파일을 위치시키고 ADVERT 끼워넣기 재생 기능을 이용하여 구현하기로 한다. 하지만 ADVERT 끼워넣기 재생은 단점이 있는데 일반 파일 재생 명령과 달리 재생 종료 시 DFPlayer는 종료 코드를 송신해주지 않는다. 종료 코드를 받지 못한다면 연결된 문장을 만들기 위해 언제 다음 음성파일을 재생시켜야 할지 알 수 없게 되는데 이는 각 문장의 종료시간을 측정하여 해결하기로 하였다. 여기에서 문장의 종료 시간은 음성파일의 재생 종료 시간이 아니라 음성파일 내의 필요한 문장의 종료 시간을 의미한다. ADVERT 재생 명령은 ADVERT 파일이 재생 중일 때에도 새로운 ADVERT파일에 대해 연속하여 재생 명령을 줄 수 있고 잘 작동한다. 단, 앞선 ADVERT파일의 재생이 종료되기 전에 연이어 ADVERT 재생 명령을 해줘야만 DFPlaeyr는 ADVERT 재생이 한 번만 된 것으로 인식하고 ADVERT 종료 시 이전에 사운드 파일이 재생되고 있었다면 일시 정지되었던 시점부터 사운드 파일을 이어서 재생하게 된다. 시간 값에 따른 ADVERT 폴더 내 음성파일 넘버링 시간 멘트: 한시 ~ 열두 시 :: 1001 hour.mp3 ~ 1012 hour.mp3 :: 1150밀리 초 십 단위 멘트: 십 ~ 오십 :: 1110 min.mp3 ~ 1150 min.mp3 :: 1050밀리 초 분단위 문장: 일분입니다 ~ 구분입니다 :: 1201 min_sentence.mp3 ~ 1209 min_sentence.mp3 :: 1700밀리 초 십 분 단위 문장: 십 분입니다 ~ 오십 분입니다. :: 1210 min_sentence.mp3 ~ 1250 min_sentence.mp3 :: 1700밀리 초 오전/오후: 1301 AM.mp3, 1302 PM.mp3 :: 850밀리 초 알람 중 끼워넣기 문장: 1401 warning_sentence.mp3 ~ 1404 belsori-ceongug-ileonaseyo.mp3 정시 알림: 2101 k1.mp3 ~ 2112 k12.mp3 :: 2200밀리 초 사용자 정시 알림: 2201 w1.mp3 ~ 2212 w12.mp3 :: 2200밀리 초 오전 1시 21분: 1301 -> 1001 -> 1120 -> 1201

오후 1시 30분: 1302 -> 1001 -> 1230

아래와 같이 DFPlayer 제어를 위해 코드를 추가해 주었다.

#include #define DF_rxPin 2 // DFplayer RX -> arduino 2 #define DF_txPin 3 // DFplayer TX -> arduino 3 SoftwareSerial dfSerial(DF_txPin, DF_rxPin); // (RX, TX) // DFplayer command 변수 uint8_t df_command[] = {0x7E, 0xFF, 0x06, 0x00, 0x00, 0x00, 0x00, 0xEF}; void send_command(uint8_t cmd, uint8_t ack, uint8_t msb, uint8_t lsb) { df_command[3] = cmd; df_command[4] = ack; df_command[5] = msb; df_command[6] = lsb; for(int i = 0; i < sizeof(df_command); i++){ dfSerial.write(df_command[i]); } } // 볼륨 제어를 위한 변수 설정 uint8_t volume = 10; // setup() 함수 추가 dfSerial.begin(9600); // DFplayer Serial send_command(0x06, 0, 0, volume); // 볼륨 10 // 시리얼 모니터 이용 DFPlayer 제어를 위해 adjust_time() 함수에 추가 else if (temp == "test") speak_time(); else if (temp == "stop") send_command(0x16, 0, 0, 0); else if (temp == "play") send_command(0x0F, 0, 2, 6); else if (temp == "vv") { if(volume < 30) volume++; send_command(0x06, 0, 0, volume); } else if (temp == "v") { if(volume > 0) volume–; send_command(0x06, 0, 0, volume); } // 음성 문장 출력 사용자 함수 void speak_time() { send_command(0x06, 0, 0, 30); // 볼륨 30 send_command(0x0F, 0, 2, 255); delay(50); int ad_num = 1302; uint8_t ad_lsb = ad_num; uint8_t ad_msb = ad_num >> 8; send_command(0x13, 0, ad_msb, ad_lsb); delay(850); ad_num = 1005; ad_lsb = ad_num; ad_msb = ad_num >> 8; send_command(0x13, 0, ad_msb, ad_lsb); delay(1150); ad_num = 1130; ad_lsb = ad_num; ad_msb = ad_num >> 8; send_command(0x13, 0, ad_msb, ad_lsb); delay(1050); ad_num = 1203; ad_lsb = ad_num; ad_msb = ad_num >> 8; send_command(0x13, 0, ad_msb, ad_lsb); delay(1700); send_command(0x06, 0, 0, volume); }

더보기 #include #include LiquidCrystal_I2C lcd(0x27, 16, 2); // Set the LCD address to 0x27 for a 16 chars and 2 line display #include #define DF_rxPin 2 // DFplayer RX -> arduino 2 #define DF_txPin 3 // DFplayer TX -> arduino 3 SoftwareSerial dfSerial(DF_txPin, DF_rxPin); // (RX, TX) // DFplayer command 변수 uint8_t df_command[] = {0x7E, 0xFF, 0x06, 0x00, 0x00, 0x00, 0x00, 0xEF}; void send_command(uint8_t cmd, uint8_t ack, uint8_t msb, uint8_t lsb) { df_command[3] = cmd; df_command[4] = ack; df_command[5] = msb; df_command[6] = lsb; for(int i = 0; i < sizeof(df_command); i++){ dfSerial.write(df_command[i]); } } int h = 12; // initial Time display is 12:59:45 PM int m = 59; uint8_t s = 45; bool meridian = true; uint8_t hm; bool now_am = true; //AM uint16_t yy = 2019; uint8_t mm = 1; uint8_t dd = 1; uint8_t week_num = 1; String week_day = "SUN"; bool adjust = false; bool display_t = false; unsigned long int start_time = 0; uint8_t volume = 10; void setup() { Serial.begin(9600); dfSerial.begin(9600); // DFplayer Serial send_command(0x06, 0, 0, volume); // 볼륨 10 lcd.begin(); lcd.backlight(); lcd.clear(); lcd.setCursor(0,0); // row, col의 좌표로 커서를 위치 col: 0 ~ 15 row: 0, 1 lcd.print("Arduino clock"); } void loop() { cal_time(); adjust_time(); } void adjust_time() { if (Serial.available() > 0) { String temp = Serial.readStringUntil(‘

‘); if (temp == “dtime”) display_time(); else if (temp == “s”) { s = 0; start_time = millis(); display_t = true; } else if (temp == “mm”) { m++; display_t = true; } else if (temp == “m”) { m–; display_t = true; } else if (temp == “hh”) { h++; display_t = true; } else if (temp == “h”) { h–; display_t = true; } else if (temp == “24”) { meridian = !meridian; display_t = true; } else if (temp == “yy”) { yy++; display_t = true; } else if (temp == “y”) { yy–; display_t = true; } else if (temp == “mon”) { mm++; adjust = true; display_t = true; } else if (temp == “mon-“) { mm–; adjust = true; display_t = true; } else if (temp == “dd”) { dd++; adjust = true; display_t = true; } else if (temp == “d”) { dd–; adjust = true; display_t = true; } else if (temp == “ww”) { week_num++; adjust = true; display_t = true; } else if (temp == “w”) { week_num–; adjust = true; display_t = true; } else if (temp.startsWith(“tset”)) { String set = temp.substring(4, 6); h = set.toInt(); set = temp.substring(6, 8); m = set.toInt(); set = temp.substring(8, 10); s = set.toInt(); start_time = millis(); adjust = true; display_t = true; } else if (temp.startsWith(“dset”)) { String set = temp.substring(4, 8); yy = set.toInt(); set = temp.substring(8, 10); mm = set.toInt(); set = temp.substring(10, 12); dd = set.toInt(); adjust = true; display_t = true; } else if (temp == “test”) speak_time(); else if (temp == “stop”) send_command(0x16, 0, 0, 0); else if (temp == “play”) send_command(0x0F, 0, 2, 6); else if (temp == “vv”) { if(volume < 30) volume++; send_command(0x06, 0, 0, volume); } else if (temp == "v") { if(volume > 0) volume–; send_command(0x06, 0, 0, volume); } if (m >= 60) m = 0; else if (m < 0) m = 59; if (h >= 24) h = 0; else if (h < 0) h = 23; if (h < 12) now_am = true; else now_am = false; hm = h; if (meridian == true && hm == 12) { now_am = false; } else if (meridian == true && hm > 12) { hm = hm – 12; } cal_dd(); if (display_t == true) { display_time(); display_t = false; } } } void speak_time() { send_command(0x06, 0, 0, 30); // 볼륨 30 send_command(0x0F, 0, 2, 255); delay(50); int ad_num = 1302; uint8_t ad_lsb = ad_num; uint8_t ad_msb = ad_num >> 8; send_command(0x13, 0, ad_msb, ad_lsb); delay(850); ad_num = 1005; ad_lsb = ad_num; ad_msb = ad_num >> 8; send_command(0x13, 0, ad_msb, ad_lsb); delay(1150); ad_num = 1130; ad_lsb = ad_num; ad_msb = ad_num >> 8; send_command(0x13, 0, ad_msb, ad_lsb); delay(1050); ad_num = 1203; ad_lsb = ad_num; ad_msb = ad_num >> 8; send_command(0x13, 0, ad_msb, ad_lsb); delay(1700); send_command(0x06, 0, 0, volume); } bool change_millis = false; uint16_t millis_sec = 994; void cal_time() { if (millis() – start_time >= millis_sec) { // 시간 간격: 밀리초 start_time = millis(); // 상기 조건을 만족할때의 밀리초를 다시 start_time에 저장하여 조건 초기화 change_millis = ! change_millis; if (change_millis == true) millis_sec = 995; else millis_sec = 994; s++; if (s == 60) { s = 0; m++; } if(m == 60) { m = 0; h++; } if(h == 12) { now_am = false;} else if (h == 24) { h = 0; now_am = true; dd++; week_num++; cal_dd(); } hm = h; if(meridian == true && hm == 12) { now_am = false; } else if (meridian == true && hm > 12) { hm = hm – 12; } else now_am = true; display_time(); if (h == 1 && m == 1 && s == 10) { // NTP 동기화 시간 } } } void cal_dd() { uint8_t m_dds; switch (mm) { case 1: m_dds = 31; break; case 2: m_dds = 28; break; case 3: m_dds = 31; break; case 4: m_dds = 30; break; case 5: m_dds = 31; break; case 6: m_dds = 30; break; case 7: m_dds = 31; break; case 8: m_dds = 31; break; case 9: m_dds = 30; break; case 10: m_dds = 31; break; case 11: m_dds = 30; break; case 12: m_dds = 31; break; } if (adjust == false) { if (dd > m_dds) { dd = 1; mm++; } if (mm > 12) { mm = 1; yy++; } if (week_num > 7) week_num = 1; week_day_converter(); } else { if (dd > m_dds) dd = 1; else if (dd < 1) dd = m_dds; if (mm > 12) mm = 1; else if (mm < 1) mm = 12; if (week_num > 7) week_num = 1; else if (week_num < 1) week_num = 7; week_day_converter(); adjust = false; } } void week_day_converter() { switch (week_num) { case 1: week_day = "SUN"; break; case 2: week_day = "MON"; break; case 3: week_day = "TUE"; break; case 4: week_day = "WED"; break; case 5: week_day = "THU"; break; case 6: week_day = "FRI"; break; case 7: week_day = "SAT"; break; } } void display_time() { Serial.print(yy); Serial.print("."); if (mm < 10) { Serial.print("0"); Serial.print(mm); Serial.print("."); } else { Serial.print(mm); Serial.print("."); } if (dd < 10) { Serial.print("0"); Serial.print(dd); Serial.print(". "); } else { Serial.print(dd); Serial.print(". "); } Serial.print(week_day); Serial.print(" "); if (meridian == true) { if (now_am == true) Serial.print("AM "); else Serial.print("PM "); if (hm < 10) { Serial.print("0"); Serial.print(hm); Serial.print(":"); } else { Serial.print(hm); Serial.print(":"); } } else { if (h < 10) { Serial.print("0"); Serial.print(h); Serial.print(":"); } else { Serial.print(h); Serial.print(":"); } } if (m < 10) { Serial.print("0"); Serial.print(m); Serial.print(":"); } else { Serial.print(m); Serial.print(":"); } if (s < 10) { Serial.print("0"); Serial.println(s); } else Serial.println(s); lcd.setCursor(0,0); lcd.print(yy); lcd.print("."); if (mm < 10) { lcd.print("0"); lcd.print(mm); lcd.print("."); } else { lcd.print(mm); lcd.print("."); } if (dd < 10) { lcd.print("0"); lcd.print(dd); lcd.print(". "); } else { lcd.print(dd); lcd.print(". "); } lcd.print(week_day); lcd.print(" "); lcd.setCursor(0,1); if (meridian == true) { if (now_am == true) lcd.print("AM "); else lcd.print("PM "); if (hm < 10) { lcd.print("0"); lcd.print(hm); lcd.print(":"); } else { lcd.print(hm); lcd.print(":"); } } else { if (h < 10) { lcd.print("0"); lcd.print(h); lcd.print(":"); } else { lcd.print(h); lcd.print(":"); } } if (m < 10) { lcd.print("0"); lcd.print(m); lcd.print(":"); } else { lcd.print(m); lcd.print(":"); } if (s < 10) { lcd.print("0"); lcd.print(s); lcd.print(" "); } else { lcd.print(s); lcd.print(" "); } // 크리스탈 LCD에는 pritln 명령어 없음 } 상기 스케치를 아두이노에 업로드하고 시리얼 모니터의 전송 옵션을 새 줄로 변경한 다음 텍스트 입력 란에 "test"를 입력하고 엔터를 쳐보자. speak_time() 사용자 함수에 코딩된 대로 "오후 다섯 시 삼십 삼분입니다."라는 문장을 들을 수 있다. 세부 내용을 살펴보면 우선, 알림이므로 볼륨을 일정 값으로 지정해준다. 현재 30으로 세팅했다. ADVERT 폴더 내의 파일을 재생하기 위해서는 선행조건이 필요하게 되는데, 그 조건은 일반 폴더 내의 사운드 파일이 재생 중일 때에만 ADVERT 폴더내의 파일 재생 명령을 인식한다는 것이다. 즉, 일반 폴더 내의 사운드 파일이 재생 중일 때에는 바로 ADVERT 폴더 내의 사운드 파일을 ADVERT 재생 명령어로 재생할 수 있으나 사운드 파일이 재생 중이지 않을 때에는 ADVERT 재생 명령을 DFPlayer에 전송해도 아무런 반응을 하지 않게 된다. 따라서 사운드 파일이 재생 중이지 않을 때에는 묵음이 저장된 파일을 먼저 재생시킨 다음 ADVERT 폴더 내의 음성 파일을 재생시켜야만 정상적으로 재생할 수 있다. DFPlayer의 명령어 코드가 이해가 안 된다면 이전 글 "DFplayer - 아두이노 사운드 모듈"을 참조하기 바란다. speak_time() 함수의 send_command(0x0F, 0, 2, 255); 코드에 의해 폴더 2의 묵음이 저장된 사운드 파일 255번을 먼저 재생한 뒤 ADVERT폴더 내 음성파일이 재생되게 되는데 재생 순서는 1302 -> 1005 -> 1130 -> 1230 순서대로 재생되고 종료 후에는 이전 볼륨으로 돌아가게 된다. 하지만 사운드 파일이 재생 중일 경우에 묵음 파일 2번 폴더 255 파일을 재생하게 되면 재생 중이던 사운드 파일은 정지를 하게 되고 음성파일이 종료된 후에 재생중이던 파일이 이어서 재생되지 않는 문제가 발생한다. 이를 해결하기 위해서는 현재 사운드 파일이 재생 중인지 아닌지를 DFPlayer 모듈에 질의를 하고 그 결과에 따라 send_command(0x0F, 0, 2, 255); 코드를 실행시킬지 말지를 결정해줘야만 사운드 파일이 재생 중일 때는 일시 정지 후 음성파일을 출력하고 음성파일 재생이 완료되면 이어서 사운드 파일이 재생되며, 사운드 파일이 재생 중이지 않을 때에는 음성파일만 출력되는 작동을 하게 된다. 이를 구현하기 위해 아래 코드를 추가 및 수정해 주었다.

bool receive_status = false; // 질의 코드 0x42수신 여부 플래그 uint8_t status_count = 0; // 질의값 저장 인덱스용 카운터 uint8_t status_val[7]; // 질의값 저장 배열 bool played = false; // 질의결과 플래그 (재생중인지 아닌지) bool status_check = false; // 질의 수신 완료 플래그 bool speak = false; // 현재 상태 질의 시작 및 음성출력 시작 플래그 // adjust_time() 함수 내 test 실행코드 수정 else if (temp == “test”) speak = true; // loop() 함수내 추가 if (dfSerial.available()) { // 42 02 01 uint8_t temp = dfSerial.read(); if (temp == 0x42) receive_status = true; if (receive_status == true) { status_val[status_count] = temp; status_count++; if (temp == 0xEF) { // 질의값 수신종료 status_check = true; receive_status = false; status_count = 0; if (status_val[2] == 2 && status_val[3] == 1) played = true; // 인덱스 2 = 2 -> SD카드, 인덱스 3 = 1 재생중 else if (status_val[2] == 2 && status_val[3] == 0) played = false; // 인덱스 3 = 0 정지중 } } } void speak_time() { if (speak == true) { // 음성 출력 상태이면 send_command(0x42, 0, 0, 0); // DFplayer에 현재 상태 질의 speak = false; } if (status_check == true) { // 질의 및 수신이 완료된 상태이면 send_command(0x06, 0, 0, 30); // 볼륨 30 if (played == false) { // 질의에 대한 수신값이 재생중이 아닐때만 send_command(0x0F, 0, 2, 255); // 묵음 파일 재생 delay(50); } int ad_num = 1302; . . send_command(0x06, 0, 0, volume); status_check = false; } }

상기의 코드로는 연결된 음성의 재생에는 문제가 없으나, speak_time() 사용자 함수 내에 사용한 delay() 함수 때문에 시리얼 모니터에 표시되던 시계의 시간이 음성 출력 시작 시부터 종료 시까지 정지되는 문제가 발생하게 된다.

millis() 함수의 시간 값은 시리얼 모니터상 시계가 정지된 것과는 상관없이 계속 증가하고 있다. 하지만 speak_time() 함수 내에서 사용한 delay() 시간 4650 밀리초(약 4.7초) 동안에는 speak_time() 함수에서 빠져나가지 못해 loop() 함수가 작동하지 않게 되므로 millis() 함수를 이용한 초 증가 코드는 작동을 하지 않게 된다. if (millis() – start_time >= 1000) 조건을 만족하면, 1000밀리 초 이상이 되면 1초 증가하도록 짜인 코드 때문에 약 4.7초 뒤에 loop() 함수가 작동하게 되어도 초 증가는 1초만 계산되게 되어 3.7초 이상의 오차가 발생하게 될 것이다. 이를 해결하기 위해 speak_time() 함수 내의 delay() 함수를 millis() 함수를 이용하여 대체해 주기로 하자.

이전 글 아두이노 – 디지털 도어락 예제에서도 delay() 함수 대체 방법에 대해 설명했었다. 그때 적용한 코드는 LED의 순차점멸에 이용을 했었는데 그때의 코드에는 눈에 보이지 않는 에러를 내포하고 있었다. delay() 대체 사용자 함수가 loop() 함수에 위치하고 플래그에 의해 제어가 되는데, 플래그가 true가 되어 시간 값을 갖는 카운터가 동작을 할때 빠르게 반복하는 loop() 함수에의해 한번만 작동하는게 아니라 여러번 작동을 하게 된다. 당시에는 출력을 하는 코드가 LED를 켜고 끄는 코드였기에 계속 켜든 계속 끄던지 상관이 없었지만, DFplayer에 연속된 명령어를 보내게 되면 DFplayer가 정상적인 반응을 하지않게 된다. 따라서 delay() 대체 시간값을 갖는 카운터 함수가 실행될때 한번만 실행되도록 해주어야만 오류가 발생하지 않는다. 한번만 실행되도록 하기위해 시간값을 갖는 카운터 함수 내에 카운터와 관련되어 아래 조건을 걸어주었다.

if (sequence == 6) speak = true; else if (sequence == 21) speak = true; else if (sequence == 44) speak = true; else if (sequence == 65) speak = true; else if (sequence == 101) speak = true;

아래 코드는 speak_time() 함수내 delay() 함수를 대체하기 위해 추가하고 수정한 코드이다.

unsigned long int count_time = 0; bool delay_set = false; uint8_t sequence = 0; void delayCount() { // 시간값을 갖는 카운터 함수 if (delay_set == true) { if (millis() – count_time >= 50) { // 시간 간격: 밀리초 count_time = millis(); sequence++; // 시간값을 갖는 카운터 if (sequence == 6) speak = true; // 실행코드를 한번만 실행토록 하는 조건 else if (sequence == 23) speak = true; else if (sequence == 46) speak = true; else if (sequence == 67) speak = true; else if (sequence == 101) speak = true; } } } void speak_time() { if (speak == true) { if (sequence == 0 && status_check == false) { send_command(0x42, 0, 0, 0); sequence++; } if (status_check == true) { if (sequence == 1) { send_command(0x06, 0, 0, 30); // 볼륨 30 sequence++; } else if (sequence == 2 && played == true) { sequence = 5; delay_set = true; } else if (sequence == 2 && played == false) { send_command(0x0F, 0, 2, 255); sequence++; } else if (sequence == 3 && played == false) { delay_set = true; sequence++; } else if (sequence == 6) { int ad_num = 1302; uint8_t ad_lsb = ad_num; uint8_t ad_msb = ad_num >> 8; send_command(0x13, 0, ad_msb, ad_lsb); speak = false; } else if (sequence == 23) { int ad_num = 1005; uint8_t ad_lsb = ad_num; uint8_t ad_msb = ad_num >> 8; send_command(0x13, 0, ad_msb, ad_lsb); speak = false; } else if (sequence == 46) { int ad_num = 1130; uint8_t ad_lsb = ad_num; uint8_t ad_msb = ad_num >> 8; send_command(0x13, 0, ad_msb, ad_lsb); speak = false; } else if (sequence == 67) { int ad_num = 1203; uint8_t ad_lsb = ad_num; uint8_t ad_msb = ad_num >> 8; send_command(0x13, 0, ad_msb, ad_lsb); speak = false; } else if (sequence == 101) { Serial.println(“enter5”); send_command(0x06, 0, 0, volume); speak = false; status_check = false; delay_set = false; sequence = 0; } } } }

아래 코드를 업로드하고 시리얼 모니터에서 텍스트 명령어 “play, next, previous, stop”를 통해 사운드 파일을 제어하고 “test” 명령어를 통해 음성 출력을 테스트하고 아두이노 시계의 시간이 정지 없이 잘 표시되는지 살펴보자.

더보기 #include #include LiquidCrystal_I2C lcd(0x27, 16, 2); // Set the LCD address to 0x27 for a 16 chars and 2 line display #include #define DF_rxPin 2 // DFplayer RX -> arduino 2 #define DF_txPin 3 // DFplayer TX -> arduino 3 SoftwareSerial dfSerial(DF_txPin, DF_rxPin); // (RX, TX) // DFplayer command 변수 uint8_t df_command[] = {0x7E, 0xFF, 0x06, 0x00, 0x00, 0x00, 0x00, 0xEF}; void send_command(uint8_t cmd, uint8_t ack, uint8_t msb, uint8_t lsb) { df_command[3] = cmd; df_command[4] = ack; df_command[5] = msb; df_command[6] = lsb; for(int i = 0; i < sizeof(df_command); i++){ dfSerial.write(df_command[i]); } } int h = 12; // initial Time display is 12:59:45 PM int m = 59; uint8_t s = 45; bool meridian = true; uint8_t hm; bool now_am = true; //AM uint16_t yy = 2019; uint8_t mm = 1; uint8_t dd = 1; uint8_t week_num = 1; String week_day = "SUN"; bool adjust = false; bool display_t = false; unsigned long int start_time = 0; uint8_t volume = 10; bool receive_status = false; // 질의 코드 0x42수신 여부 플래그 uint8_t status_count = 0; // 질의값 저장 인덱스용 카운터 uint8_t status_val[7]; // 질의값 저장 배열 bool played = false; // 질의결과 플래그 (재생중인지 아닌지) bool status_check = false; // 질의 수신 완료 플래그 bool speak = false; // 현재 상태 질의 시작 및 음성출력 시작 플래그 unsigned long int count_time = 0; bool delay_set = false; uint8_t sequence = 0; void setup() { Serial.begin(9600); dfSerial.begin(9600); // DFplayer Serial send_command(0x06, 0, 0, volume); // 볼륨 10 lcd.begin(); lcd.backlight(); lcd.clear(); lcd.setCursor(0,0); // row, col의 좌표로 커서를 위치 col: 0 ~ 15 row: 0, 1 lcd.print("Arduino clock"); } void loop() { cal_time(); adjust_time(); delayCount(); speak_time(); if (dfSerial.available()) { // 42 02 01 uint8_t temp = dfSerial.read(); if (temp == 0x42) receive_status = true; if (receive_status == true) { status_val[status_count] = temp; status_count++; if (temp == 0xEF) { // 질의값 수신종료 status_check = true; receive_status = false; status_count = 0; if (status_val[2] == 2 && status_val[3] == 1) played = true; // 인덱스 2 = 2 -> SD카드, 인덱스 3 = 1 재생중 else if (status_val[2] == 2 && status_val[3] == 0) played = false; // 인덱스 3 = 0 정지중 } } } } void adjust_time() { if (Serial.available() > 0) { String temp = Serial.readStringUntil(‘

‘); if (temp == “dtime”) display_time(); else if (temp == “s”) { s = 0; start_time = millis(); display_t = true; } else if (temp == “mm”) { m++; display_t = true; } else if (temp == “m”) { m–; display_t = true; } else if (temp == “hh”) { h++; display_t = true; } else if (temp == “h”) { h–; display_t = true; } else if (temp == “24”) { meridian = !meridian; display_t = true; } else if (temp == “yy”) { yy++; display_t = true; } else if (temp == “y”) { yy–; display_t = true; } else if (temp == “mon”) { mm++; adjust = true; display_t = true; } else if (temp == “mon-“) { mm–; adjust = true; display_t = true; } else if (temp == “dd”) { dd++; adjust = true; display_t = true; } else if (temp == “d”) { dd–; adjust = true; display_t = true; } else if (temp == “ww”) { week_num++; adjust = true; display_t = true; } else if (temp == “w”) { week_num–; adjust = true; display_t = true; } else if (temp.startsWith(“tset”)) { String set = temp.substring(4, 6); h = set.toInt(); set = temp.substring(6, 8); m = set.toInt(); set = temp.substring(8, 10); s = set.toInt(); start_time = millis(); adjust = true; display_t = true; } else if (temp.startsWith(“dset”)) { String set = temp.substring(4, 8); yy = set.toInt(); set = temp.substring(8, 10); mm = set.toInt(); set = temp.substring(10, 12); dd = set.toInt(); adjust = true; display_t = true; } else if (temp == “test”) speak = true; else if (temp == “stop”) send_command(0x16, 0, 0, 0); else if (temp == “play”) send_command(0x0F, 0, 2, 6); else if (temp == “vv”) { if(volume < 30) volume++; send_command(0x06, 0, 0, volume); } else if (temp == "v") { if(volume > 0) volume–; send_command(0x06, 0, 0, volume); } else if (temp == “pause”) send_command(0x0E, 1, 0, 0); else if (temp == “previous”) send_command(0x02, 1, 0, 0); else if (temp == “next”) send_command(0x01, 1, 0, 0); else if (temp == “reset”) send_command(0x0C, 0, 0, 0); if (m >= 60) m = 0; else if (m < 0) m = 59; if (h >= 24) h = 0; else if (h < 0) h = 23; if (h < 12) now_am = true; else now_am = false; hm = h; if (meridian == true && hm == 12) { now_am = false; } else if (meridian == true && hm > 12) { hm = hm – 12; } cal_dd(); if (display_t == true) { display_time(); display_t = false; } } } void delayCount() { // 시간값을 갖는 카운터 함수 if (delay_set == true) { if (millis() – count_time >= 50) { // 시간 간격: 밀리초 count_time = millis(); sequence++; // 시간값을 갖는 카운터 if (sequence == 6) speak = true; // 실행코드를 한번만 실행토록 하는 조건 else if (sequence == 23) speak = true; else if (sequence == 46) speak = true; else if (sequence == 67) speak = true; else if (sequence == 101) speak = true; } } } void speak_time() { if (speak == true) { if (sequence == 0 && status_check == false) { send_command(0x42, 0, 0, 0); sequence++; } if (status_check == true) { if (sequence == 1) { send_command(0x06, 0, 0, 30); // 볼륨 30 sequence++; } else if (sequence == 2 && played == true) { sequence = 5; delay_set = true; } else if (sequence == 2 && played == false) { send_command(0x0F, 0, 2, 255); sequence++; } else if (sequence == 3 && played == false) { delay_set = true; sequence++; } else if (sequence == 6) { int ad_num = 1302; uint8_t ad_lsb = ad_num; uint8_t ad_msb = ad_num >> 8; send_command(0x13, 0, ad_msb, ad_lsb); speak = false; } else if (sequence == 23) { int ad_num = 1005; uint8_t ad_lsb = ad_num; uint8_t ad_msb = ad_num >> 8; send_command(0x13, 0, ad_msb, ad_lsb); speak = false; } else if (sequence == 46) { int ad_num = 1130; uint8_t ad_lsb = ad_num; uint8_t ad_msb = ad_num >> 8; send_command(0x13, 0, ad_msb, ad_lsb); speak = false; } else if (sequence == 67) { int ad_num = 1203; uint8_t ad_lsb = ad_num; uint8_t ad_msb = ad_num >> 8; send_command(0x13, 0, ad_msb, ad_lsb); speak = false; } else if (sequence == 101) { Serial.println(“enter5”); send_command(0x06, 0, 0, volume); speak = false; status_check = false; delay_set = false; sequence = 0; } } } } bool change_millis = false; uint16_t millis_sec = 994; void cal_time() { if (millis() – start_time >= millis_sec) { // 시간 간격: 밀리초 start_time = millis(); // 상기 조건을 만족할때의 밀리초를 다시 start_time에 저장하여 조건 초기화 change_millis = ! change_millis; if (change_millis == true) millis_sec = 995; else millis_sec = 994; s++; if (s == 60) { s = 0; m++; } if(m == 60) { m = 0; h++; } if(h == 12) { now_am = false;} else if (h == 24) { h = 0; now_am = true; dd++; week_num++; cal_dd(); } hm = h; if(meridian == true && hm == 12) { now_am = false; } else if (meridian == true && hm > 12) { hm = hm – 12; } else now_am = true; display_time(); if (h == 1 && m == 1 && s == 10) { // NTP 동기화 시간 } } } void cal_dd() { uint8_t m_dds; switch (mm) { case 1: m_dds = 31; break; case 2: m_dds = 28; break; case 3: m_dds = 31; break; case 4: m_dds = 30; break; case 5: m_dds = 31; break; case 6: m_dds = 30; break; case 7: m_dds = 31; break; case 8: m_dds = 31; break; case 9: m_dds = 30; break; case 10: m_dds = 31; break; case 11: m_dds = 30; break; case 12: m_dds = 31; break; } if (adjust == false) { if (dd > m_dds) { dd = 1; mm++; } if (mm > 12) { mm = 1; yy++; } if (week_num > 7) week_num = 1; week_day_converter(); } else { if (dd > m_dds) dd = 1; else if (dd < 1) dd = m_dds; if (mm > 12) mm = 1; else if (mm < 1) mm = 12; if (week_num > 7) week_num = 1; else if (week_num < 1) week_num = 7; week_day_converter(); adjust = false; } } void week_day_converter() { switch (week_num) { case 1: week_day = "SUN"; break; case 2: week_day = "MON"; break; case 3: week_day = "TUE"; break; case 4: week_day = "WED"; break; case 5: week_day = "THU"; break; case 6: week_day = "FRI"; break; case 7: week_day = "SAT"; break; } } void display_time() { Serial.print(yy); Serial.print("."); if (mm < 10) { Serial.print("0"); Serial.print(mm); Serial.print("."); } else { Serial.print(mm); Serial.print("."); } if (dd < 10) { Serial.print("0"); Serial.print(dd); Serial.print(". "); } else { Serial.print(dd); Serial.print(". "); } Serial.print(week_day); Serial.print(" "); if (meridian == true) { if (now_am == true) Serial.print("AM "); else Serial.print("PM "); if (hm < 10) { Serial.print("0"); Serial.print(hm); Serial.print(":"); } else { Serial.print(hm); Serial.print(":"); } } else { if (h < 10) { Serial.print("0"); Serial.print(h); Serial.print(":"); } else { Serial.print(h); Serial.print(":"); } } if (m < 10) { Serial.print("0"); Serial.print(m); Serial.print(":"); } else { Serial.print(m); Serial.print(":"); } if (s < 10) { Serial.print("0"); Serial.println(s); } else Serial.println(s); lcd.setCursor(0,0); lcd.print(yy); lcd.print("."); if (mm < 10) { lcd.print("0"); lcd.print(mm); lcd.print("."); } else { lcd.print(mm); lcd.print("."); } if (dd < 10) { lcd.print("0"); lcd.print(dd); lcd.print(". "); } else { lcd.print(dd); lcd.print(". "); } lcd.print(week_day); lcd.print(" "); lcd.setCursor(0,1); if (meridian == true) { if (now_am == true) lcd.print("AM "); else lcd.print("PM "); if (hm < 10) { lcd.print("0"); lcd.print(hm); lcd.print(":"); } else { lcd.print(hm); lcd.print(":"); } } else { if (h < 10) { lcd.print("0"); lcd.print(h); lcd.print(":"); } else { lcd.print(h); lcd.print(":"); } } if (m < 10) { lcd.print("0"); lcd.print(m); lcd.print(":"); } else { lcd.print(m); lcd.print(":"); } if (s < 10) { lcd.print("0"); lcd.print(s); lcd.print(" "); } else { lcd.print(s); lcd.print(" "); } // 크리스탈 LCD에는 pritln 명령어 없음 } 이제 음성 출력 코드를 시간에 맞춰 출력되도록 해보자. 시간에 맞추기 위해서는 고려해야 할 사항이 있다. 시간에 따라 음성파일의 번호를 계산하는 코드와 더불어 짧은 문장과 긴 문장을 구분하여 ADVERT 내 음성파일이 재생되도록 해야만 한다. 이를 구현하기 위해 아래의 speak_option() 사용자 함수를 작성해 주었다. bool short_sentence = false; // 짧은 문장, 긴문장 구분 플래그 bool sharp = false; // 정시알림 플래그 int am; // AM/PM ADVERT 음성파일 번호 저장 변수 int time_sharp; // 정시 알림 ADVERT 음성파일 번호 저장 변수 int hour_a; // 시 ADVERT 음성파일 번호 저장 변수 int min_w; // 십단위 ADVERT 음성파일 번호 저장 변수 int min_s; // 분단위 ADVERT 음성파일 번호 저장 변수 bool speak_finished = true; // 전체 음성 출력 종료 확인 플래그 void speak_option() { if (m == 0) { // 분이 0 이면 sharp = true; // 정시 if (h < 13) time_sharp = 2200 + h; // 시가 13이하 이면 음성파일 번호 2200 + h else time_sharp = 2200 + (h - 12); } else { sharp = false; // 정시 아님 if (h < 13) hour_a = 1000 + h; // 시가 13이하 이면 음성파일 번호 1000 + h else hour_a = 1000 + (h - 12); if (h < 12) am = 1301; // 오전이면 else am = 1302; int temp = m%10; // 분을 10으로 나눈 나머지가 if (temp == 0 || m < 10) { // 0 이거나 10분 보다 작으면 short_sentence = true; // 짧은 문장 min_s = 1200 + m; // 분단위 음성파일 번호 저장 } else { short_sentence = false; // 긴문장 min_w = 1100 + (m - temp); // 십단위 음성파일 번호 저장 min_s = 1200 + temp; // 분단위 음성파일 번호 저장 } } speak = true; // 상기 생성된 번호로 음성파일 출력 speak_finished = false; // 전체 음성 출력 종료 확인 플래그 } delayCount() 함수와 speak_time() 함수의 코드도 speak_option() 함수의 조건에 맞게 수정하였고 시간 값을 갖는 카운트를 이용한 시퀀스를 두 함수 모두 동시 변경(오류 방지용) 및 제어하기 위해 아래 변수를 추가하였으며, 계산된 ADVERT 음성파일 번호를 DFPlayer에 전송하도록 입력값을 갖는 사용자 함수 advert(int ad_num)를 추가해 주었다. uint8_t am_s = 6; // 오전, 오후 음성출력 시작값 uint8_t hour_a_s = 23; // 시 음성출력 시작값 uint8_t long_min_w_s = 46; // 긴문장 십단위 음성출력 시작값 uint8_t long_min_s_s = 67; // 긴문장 분단위 음성출력 시작값 uint8_t long_min_s_end = 100; // 긴문장 종료값 및 볼륨 조절 uint8_t short_min_s_s = 46; // 짧은 문장 분단위 음성출력 시작값 uint8_t short_min_s_end = 76; // 짧은 문장 종료값 및 볼륨 조절 uint8_t time_sharp_s = 6; // 정시 음성 출력 시작값 uint8_t time_sharp_end = 63; // 정시 음성 출력 종료값 및 볼륨 조절 void advert(int ad_num) { uint8_t ad_lsb = ad_num; uint8_t ad_msb = ad_num >> 8; send_command(0x13, 0, ad_msb, ad_lsb); }

cal_time() 함수에 의해 정시가 되었을 경우, 음성 출력을 위해 아래의 코드를 추가해 주었다.

if (speak_finished == true) { // 음성출력이 안되고 있을경우에 if (m == 0 && s == 0) { // 0분 0초가 되면 sharp = true; if (h < 13) time_sharp = 2100 + h; else time_sharp = 2100 + (h - 12); time_sharp_end = 48; // cal_time() 함수에 의해 시작된 음성 출력 종료값 설정 speak = true; } } 아래 스케치를 업로드하고 정시 음성 출력을 확인하고 test 명령을 통해 출력되는 음성 출력과 play 명령으로 사운드를 재생시킨 다음 test 명령을 통해 음성 출력이 잘되는지 확인해 보자. 더보기 #include #include LiquidCrystal_I2C lcd(0x27, 16, 2); // Set the LCD address to 0x27 for a 16 chars and 2 line display #include #define DF_rxPin 2 // DFplayer RX -> arduino 2 #define DF_txPin 3 // DFplayer TX -> arduino 3 SoftwareSerial dfSerial(DF_txPin, DF_rxPin); // (RX, TX) // DFplayer command 변수 uint8_t df_command[] = {0x7E, 0xFF, 0x06, 0x00, 0x00, 0x00, 0x00, 0xEF}; void send_command(uint8_t cmd, uint8_t ack, uint8_t msb, uint8_t lsb) { df_command[3] = cmd; df_command[4] = ack; df_command[5] = msb; df_command[6] = lsb; for(int i = 0; i < sizeof(df_command); i++){ dfSerial.write(df_command[i]); } } int h = 12; // initial Time display is 12:59:45 PM int m = 59; uint8_t s = 45; bool meridian = true; uint8_t hm; bool now_am = true; //AM uint16_t yy = 2019; uint8_t mm = 1; uint8_t dd = 1; uint8_t week_num = 1; String week_day = "SUN"; bool adjust = false; bool display_t = false; unsigned long int start_time = 0; uint8_t volume = 20; bool receive_status = false; // 질의 코드 0x42수신 여부 플래그 uint8_t status_count = 0; // 질의값 저장 인덱스용 카운터 uint8_t status_val[7]; // 질의값 저장 배열 bool played = false; // 질의결과 플래그 (재생중인지 아닌지) bool status_check = false; // 질의 수신 완료 플래그 unsigned long int count_time = 0; bool delay_set = false; uint8_t sequence = 0; bool speak = false; // 현재 상태 질의 시작 및 음성출력 시작 플래그 bool short_sentence = false; bool sharp = false; int am; int time_sharp; int hour_a; int min_w; int min_s; void setup() { Serial.begin(9600); dfSerial.begin(9600); // DFplayer Serial send_command(0x06, 0, 0, volume); // 볼륨 lcd.begin(); lcd.backlight(); lcd.clear(); lcd.setCursor(0,0); // row, col의 좌표로 커서를 위치 col: 0 ~ 15 row: 0, 1 lcd.print("Arduino clock"); } void loop() { cal_time(); adjust_time(); speak_time(); delayCount(); if (dfSerial.available()) { // 42 02 01 uint8_t temp = dfSerial.read(); if (temp == 0x42) receive_status = true; if (receive_status == true) { status_val[status_count] = temp; status_count++; if (temp == 0xEF) { status_check = true; receive_status = false; status_count = 0; if (status_val[2] == 2 && status_val[3] == 1) played = true; // 인덱스 2 = 2 -> SD카드, 인덱스 3 = 1 재생중 else if (status_val[2] == 2 && status_val[3] == 0) played = false; // 인덱스 3 = 0 정지중 } } } } void adjust_time() { if (Serial.available() > 0) { String temp = Serial.readStringUntil(‘

‘); if (temp == “dtime”) display_time(); else if (temp == “s”) { s = 0; start_time = millis(); display_t = true; } else if (temp == “mm”) { m++; display_t = true; } else if (temp == “m”) { m–; display_t = true; } else if (temp == “hh”) { h++; display_t = true; } else if (temp == “h”) { h–; display_t = true; } else if (temp == “24”) { meridian = !meridian; display_t = true; } else if (temp == “yy”) { yy++; display_t = true; } else if (temp == “y”) { yy–; display_t = true; } else if (temp == “mon”) { mm++; adjust = true; display_t = true; } else if (temp == “mon-“) { mm–; adjust = true; display_t = true; } else if (temp == “dd”) { dd++; adjust = true; display_t = true; } else if (temp == “d”) { dd–; adjust = true; display_t = true; } else if (temp == “ww”) { week_num++; adjust = true; display_t = true; } else if (temp == “w”) { week_num–; adjust = true; display_t = true; } else if (temp.startsWith(“tset”)) { String set = temp.substring(4, 6); h = set.toInt(); set = temp.substring(6, 8); m = set.toInt(); set = temp.substring(8, 10); s = set.toInt(); start_time = millis(); adjust = true; display_t = true; } else if (temp.startsWith(“dset”)) { String set = temp.substring(4, 8); yy = set.toInt(); set = temp.substring(8, 10); mm = set.toInt(); set = temp.substring(10, 12); dd = set.toInt(); adjust = true; display_t = true; } else if (temp == “test”) speak_option(); else if (temp == “stop”) send_command(0x16, 0, 0, 0); else if (temp == “play”) send_command(0x0F, 0, 2, 6); else if (temp == “pause”) send_command(0x0E, 1, 0, 0); else if (temp == “previous”) send_command(0x02, 1, 0, 0); else if (temp == “next”) send_command(0x01, 1, 0, 0); else if (temp == “vv”) { if(volume < 30) volume++; send_command(0x06, 0, 0, volume); } else if (temp == "v") { if(volume > 0) volume–; send_command(0x06, 0, 0, volume); } else if (temp == “reset”) send_command(0x0C, 0, 0, 0); if (m >= 60) m = 0; else if (m < 0) m = 59; if (h >= 24) h = 0; else if (h < 0) h = 23; if (h < 12) now_am = true; else now_am = false; hm = h; if(meridian == true && hm == 12) { now_am = false; } else if (meridian == true && hm > 12) { hm = hm – 12; } cal_dd(); if (display_t == true) { display_time(); display_t = false; } } } bool speak_finished = true; void speak_option() { if (m == 0 && s != 0) { sharp = true; if (h < 13) time_sharp = 2200 + h; else time_sharp = 2200 + (h - 12); } else { sharp = false; if (h < 13) hour_a = 1000 + h; else hour_a = 1000 + (h - 12); if (h < 12) am = 1301; else am = 1302; int temp = m%10; if (temp == 0 || m < 10) { short_sentence = true; min_s = 1200 + m; } else { short_sentence = false; min_w = 1100 + (m - temp); min_s = 1200 + temp; } } speak = true; speak_finished = false; } uint8_t am_s = 6; uint8_t hour_a_s = 23; uint8_t long_min_w_s = 46; uint8_t long_min_s_s = 67; uint8_t long_min_s_end = 100; uint8_t short_min_s_s = 46; uint8_t short_min_s_end = 76; uint8_t time_sharp_s = 6; uint8_t time_sharp_end = 63; void speak_time() { if (speak == true) { if (sequence == 0 && status_check == false) { send_command(0x42, 0, 0, 0); sequence++; } if (status_check == true) { if (sequence == 1) { send_command(0x06, 0, 0, 30); // 볼륨 30 sequence++; } else if (sequence == 2 && played == true) { sequence = 5; delay_set = true; } else if (sequence == 2 && played == false) { send_command(0x0F, 0, 2, 255); sequence++; } else if (sequence == 3 && played == false) { delay_set = true; sequence++; } if (sharp == false) { if (sequence == am_s) { advert(am); speak = false; } else if (sequence == hour_a_s) { advert(hour_a); speak = false; } if (short_sentence == false) { if (sequence == long_min_w_s) { advert(min_w); speak = false; } else if (sequence == long_min_s_s) { advert(min_s); speak = false; } else if (sequence == long_min_s_end) { send_command(0x06, 0, 0, volume); speak_finished = true; speak = false; status_check = false; sequence = 0; } } else { if (sequence == short_min_s_s) { advert(min_s); speak = false; } else if (sequence == short_min_s_end) { send_command(0x06, 0, 0, volume); speak_finished = true; speak = false; status_check = false; sequence = 0; } } } else { if (sequence == time_sharp_s) { advert(time_sharp); speak = false; } else if (sequence == time_sharp_end) { send_command(0x06, 0, 0, volume); speak_finished = true; speak = false; status_check = false; sequence = 0; time_sharp_end = 63; } } } } } void advert(int ad_num) { uint8_t ad_lsb = ad_num; uint8_t ad_msb = ad_num >> 8; send_command(0x13, 0, ad_msb, ad_lsb); } void delayCount() { if (delay_set == true) { if (millis() – count_time >= 50) { // 시간 간격: 밀리초 count_time = millis(); sequence++; if (sharp == false) { if (sequence == am_s) speak = true; else if (sequence == hour_a_s) speak = true; if (short_sentence == false) { if (sequence == long_min_w_s) speak = true; else if (sequence == long_min_s_s) speak = true; else if (sequence == long_min_s_end) { speak = true; delay_set = false; } } else { if (sequence == short_min_s_s) speak = true; else if (sequence == short_min_s_end) { speak = true; delay_set = false; } } } else { if (sequence == time_sharp_s) speak = true; else if (sequence == time_sharp_end) { speak = true; delay_set = false; } } } } } bool change_millis = false; uint16_t millis_sec = 994; void cal_time() { if (millis() – start_time >= millis_sec) { // 시간 간격: 밀리초 start_time = millis(); // 상기 조건을 만족할때의 밀리초를 다시 start_time에 저장하여 조건 초기화 change_millis = ! change_millis; if (change_millis == true) millis_sec = 995; else millis_sec = 994; s++; if (s == 60) { s = 0; m++; } if(m == 60) { m = 0; h++; } if(h == 12) { now_am = false;} else if (h == 24) { h = 0; now_am = true; dd++; week_num++; cal_dd(); } hm = h; if(meridian == true && hm == 12) { now_am = false; } else if (meridian == true && hm > 12) { hm = hm – 12; } else now_am = true; display_time(); if (speak_finished == true) { if (m == 0 && s == 0) { sharp = true; if (h < 13) time_sharp = 2100 + h; else time_sharp = 2100 + (h - 12); time_sharp_end = 48; speak = true; } } } } void cal_dd() { uint8_t m_dds; switch (mm) { case 1: m_dds = 31; break; case 2: m_dds = 28; break; case 3: m_dds = 31; break; case 4: m_dds = 30; break; case 5: m_dds = 31; break; case 6: m_dds = 30; break; case 7: m_dds = 31; break; case 8: m_dds = 31; break; case 9: m_dds = 30; break; case 10: m_dds = 31; break; case 11: m_dds = 30; break; case 12: m_dds = 31; break; } if (adjust == false) { if (dd > m_dds) { dd = 1; mm++; } if (mm > 12) { mm = 1; yy++; } if (week_num > 7) week_num = 1; week_day_converter(); } else { if (dd > m_dds) dd = 1; else if (dd < 1) dd = m_dds; if (mm > 12) mm = 1; else if (mm < 1) mm = 12; if (week_num > 7) week_num = 1; else if (week_num < 1) week_num = 7; week_day_converter(); adjust = false; } } void week_day_converter() { switch (week_num) { case 1: week_day = "SUN"; break; case 2: week_day = "MON"; break; case 3: week_day = "TUE"; break; case 4: week_day = "WED"; break; case 5: week_day = "THU"; break; case 6: week_day = "FRI"; break; case 7: week_day = "SAT"; break; } } void display_time() { Serial.print(yy); Serial.print("."); if (mm < 10) { Serial.print("0"); Serial.print(mm); Serial.print("."); } else { Serial.print(mm); Serial.print("."); } if (dd < 10) { Serial.print("0"); Serial.print(dd); Serial.print(". "); } else { Serial.print(dd); Serial.print(". "); } Serial.print(week_day); Serial.print(" "); if (meridian == true) { if (now_am == true) Serial.print("AM "); else Serial.print("PM "); if (hm < 10) { Serial.print("0"); Serial.print(hm); Serial.print(":"); } else { Serial.print(hm); Serial.print(":"); } } else { if (h < 10) { Serial.print("0"); Serial.print(h); Serial.print(":"); } else { Serial.print(h); Serial.print(":"); } } if (m < 10) { Serial.print("0"); Serial.print(m); Serial.print(":"); } else { Serial.print(m); Serial.print(":"); } if (s < 10) { Serial.print("0"); Serial.println(s); } else Serial.println(s); lcd.setCursor(0,0); lcd.print(yy); lcd.print("."); if (mm < 10) { lcd.print("0"); lcd.print(mm); lcd.print("."); } else { lcd.print(mm); lcd.print("."); } if (dd < 10) { lcd.print("0"); lcd.print(dd); lcd.print(". "); } else { lcd.print(dd); lcd.print(". "); } lcd.print(week_day); lcd.print(" "); lcd.setCursor(0,1); if (meridian == true) { if (now_am == true) lcd.print("AM "); else lcd.print("PM "); if (hm < 10) { lcd.print("0"); lcd.print(hm); lcd.print(":"); } else { lcd.print(hm); lcd.print(":"); } } else { if (h < 10) { lcd.print("0"); lcd.print(h); lcd.print(":"); } else { lcd.print(h); lcd.print(":"); } } if (m < 10) { lcd.print("0"); lcd.print(m); lcd.print(":"); } else { lcd.print(m); lcd.print(":"); } if (s < 10) { lcd.print("0"); lcd.print(s); lcd.print(" "); } else { lcd.print(s); lcd.print(" "); } // 크리스탈 LCD에는 pritln 명령어 없음 } 이제 알람 설정 코드를 추가하고 사용자 입력 버튼(또는 센서) 코드도 추가하여, 설정시간이 되면 알람이 울리고 버튼으로 알람을 정지시키고 또한 버튼으로 현재 시간을 출력할 수 있도록 해보자. 아울러 음성 출력이 되지않는 시간을 정하여 필요한 시간에만 음성출력이 되도록 해보자. uint8_t alarm_track_folder = 2; // 알람 사운드 파일 폴더 값 uint8_t alarm_track = 2; // 알람 사운드 파일 트랙 값 bool alarm_on = false; // 알람 작동 유무 설정 플래그 int alarm_h; // 알람 시 값 저장 변수 int alarm_m; // 알람 분 값 저장 변수 unsigned long int alarm_delay = 0; // 알람 경고 문구 제어용 딜레이 bool alarm_sentence = false; // 알람 경고 문구 시작 및 종료 플래그 uint8_t alarm_count = 0; // 알람 경고 문구 출력 시점 제어 bool alarm_time = false; // 현재 알람 출력중인지 여부 플래그 unsigned long int alarm_sequence_delay = 0; // 알람 기동용 딜레이 uint8_t alarm_sequence = 0; // 알람 기동용 시퀀스 값 bool alarm_send_f = false; // 알람 기동용 함수 시작 및 종료 플래그 bool alarm_stop = false; // 알람 정지 플래그 bool no_speak_sharp = false; // 정시알림 묵음여부 플래그 uint8_t no_speak_s = 0; // 묵음 시작 uint8_t no_speak_f = 0; // 묵음 종료 uint8_t pin = 7; // 버튼 핀 // adjust_time() 함수 추가 코드 else if (temp == "nsp") no_speak_sharp = !no_speak_sharp; else if (temp == "alarm") { alarm_on = !alarm_on; if (alarm_on) Serial.println("alarm on"); else Serial.println("alarm off"); } else if (temp.startsWith("aset")) { String set = temp.substring(4, 6); alarm_h = set.toInt(); set = temp.substring(6, 8); alarm_m = set.toInt(); Serial.print("alarm set: "); Serial.print(alarm_h); Serial.print(":"); Serial.println(alarm_m); } else if (temp.startsWith("setnsp")) { String set = temp.substring(6, 8); no_speak_s = set.toInt(); set = temp.substring(8, 10); no_speak_f = set.toInt(); Serial.print("no speak set: "); Serial.print(no_speak_s); Serial.print("~"); Serial.println(no_speak_f); } else if (temp == "test") speak_option(); else if (temp == "stop") { alarm_stop = true; // 알람 기동용 함수 내부 알람 정지 코드 alarm_time = false; // 알람 재생 정지 표시 alarm_sentence = false; // 알람 경고 출력 함수 정지 alarm_count = 0; // 알람 경고 카운터 초기화 alarm_send_f = true; // 알람 기동용 함수 시작 } // cal_time() 함수내 알람 출력 코드 if (no_speak_sharp == true) { // 정시 묵음 모드 일때 if (no_speak_s == no_speak_f) no_speak_sharp = false; // 묵음 시작값과 종료값이 같으면 정시 묵음 모드 아님 } if (alarm_on) { // 알람이 활성화 됐을때 if (alarm_h == h && alarm_m == m && s == 0){ 알람 시간이 되면 alarm_time = true; // 알람 시간 alarm_send_f = true; // 알람 기동 } } if (alarm_time != true) { // 알람 시간이 아닐 때 if (m == 0 && s == 0) { // 정시 이면 if (no_speak_sharp == false) { // 묵음 시간이 아니면 speak_sharp(); // 정시 출력 } else { if (no_speak_s > no_speak_f) { // 묵음 시작 시간이 종료 시간 보다 크면 if (h < no_speak_s && h > no_speak_f) { speak_sharp(); } } else if (no_speak_s < no_speak_f) { // 묵음 시작 시간이 종료 시간 보다 작으면 if (h > no_speak_s && h < no_speak_f) { speak_sharp(); } } } } } void speak_sharp() { if (speak_finished == true) { sharp = true; if (h < 13) time_sharp = 2100 + h; else time_sharp = 2100 + (h - 12); time_sharp_end = 48; speak = true; } } // 알람 기동용 함수 void alarm_send() { if (alarm_send_f == true) { if (millis() - alarm_sequence_delay >= 20) { // 시간 간격: 밀리초 alarm_sequence_delay = millis(); alarm_sequence++; if (alarm_stop == true) { if (alarm_sequence == 1) send_command(0x06, 0, 0, volume); // 이전 볼륨 복귀 else if (alarm_sequence == 2) send_command(0x16, 0, 0, 0); else if (alarm_sequence == 3) { send_command(0x15, 0, 0, 0); alarm_send_f = false; alarm_sequence = 0; alarm_stop = false; // 알람 시간 도달시 아래 코드로 진입하도록 초기화 } } else { // 알람 정지가 아니면 if (alarm_sequence == 1) send_command(0x16, 0, 0, 0); // 재생 정지 else if (alarm_sequence == 2) send_command(0x06, 0, 0, 30); // 볼륨 30 else if (alarm_sequence == 3) send_command(0x0F, 0, alarm_track_folder, alarm_track); // 폴더2 002번 사운드 파일 else if (alarm_sequence == 8) { send_command(0x19, 0, 0, 0); // 반복재생 alarm_sentence = true; alarm_count = 0; alarm_send_f = false; alarm_sequence = 0; } } } } } // 알람 중 끼워넣기 음성 출력 함수 void alarm_warning() { // 알람 경고문 if (alarm_sentence == true) { if (millis() – alarm_delay >= 1000) { // 시간 간격: 밀리초 alarm_delay = millis(); alarm_count++; if (alarm_count == 4) { advert(1401); } else if (alarm_count == 12) { advert(1402); } else if (alarm_count == 25) { advert(1403); } else if (alarm_count == 70) { advert(1404); alarm_sentence = false; alarm_count = 0; } } } } // loop() 함수 추가 코드 if (digitalRead(pin) == LOW) { // LOW – ON if (alarm_time == true) { // 알람이 울리고 있을 때 alarm_stop = true; // 알람 기동용 함수 내부 알람 정지 코드 alarm_time = false; // 알람 재생 정지 표시 alarm_sentence = false; // 알람 경고 출력 함수 정지 alarm_count = 0; // 알람 경고 카운터 초기화 alarm_send_f = true; // 알람 기동용 함수 시작 } else { // 알람이 울리지 않을 때 speak_option(); // 현재 시간 출력 } }

아래 스케치를 업로드하고 7번 핀에 점퍼선을 연결하고 GND 핀에도 점퍼선을 연결한 뒤에 버튼 대신 사용해서 테스트를 해보자. 우선 7번 핀과 GND핀을 점핑시켜보고 시간 출력이 잘 되는지 확인해 보자. 다음 알람 테스트를 위해 시리얼 모니터에 “alarm”을 입력하고 “aset1301″을 입력하여 13시 1분 알람 시간을 설정한다. 시간이 됐을 때 알람이 울리는지 확인해 보자. 정지 시에는 stop을 입력하거나 7번 핀과 GND핀을 점핑시켜도 정지하게 된다.

더보기 #include #include LiquidCrystal_I2C lcd(0x27, 16, 2); // Set the LCD address to 0x27 for a 16 chars and 2 line display #include #define DF_rxPin 2 // DFplayer RX -> arduino 2 #define DF_txPin 3 // DFplayer TX -> arduino 3 SoftwareSerial dfSerial(DF_txPin, DF_rxPin); // (RX, TX) // DFplayer command 변수 uint8_t df_command[] = {0x7E, 0xFF, 0x06, 0x00, 0x00, 0x00, 0x00, 0xEF}; void send_command(uint8_t cmd, uint8_t ack, uint8_t msb, uint8_t lsb) { df_command[3] = cmd; df_command[4] = ack; df_command[5] = msb; df_command[6] = lsb; for(int i = 0; i < sizeof(df_command); i++){ dfSerial.write(df_command[i]); } } int h = 12; // initial Time display is 12:59:45 PM int m = 59; uint8_t s = 45; bool meridian = true; uint8_t hm; bool now_am = true; //AM uint16_t yy = 2019; uint8_t mm = 1; uint8_t dd = 1; uint8_t week_num = 1; String week_day = "SUN"; bool adjust = false; bool display_t = false; unsigned long int start_time = 0; uint8_t volume = 20; bool receive_status = false; // 질의 코드 0x42수신 여부 플래그 uint8_t status_count = 0; // 질의값 저장 인덱스용 카운터 uint8_t status_val[7]; // 질의값 저장 배열 bool played = false; // 질의결과 플래그 (재생중인지 아닌지) bool status_check = false; // 질의 수신 완료 플래그 unsigned long int count_time = 0; bool delay_set = false; uint8_t sequence = 0; bool speak = false; // 현재 상태 질의 시작 및 음성출력 시작 플래그 bool short_sentence = false; bool sharp = false; int am; int time_sharp; int hour_a; int min_w; int min_s; uint8_t alarm_track_folder = 2; uint8_t alarm_track = 2; bool alarm_on = false; int alarm_h; int alarm_m; unsigned long int alarm_delay = 0; bool alarm_sentence = false; uint8_t alarm_count = 0; bool alarm_time = false; unsigned long int alarm_sequence_delay = 0; uint8_t alarm_sequence = 0; bool alarm_send_f = false; bool alarm_stop = false; bool no_speak_sharp = false; uint8_t no_speak_s = 0; uint8_t no_speak_f = 0; uint8_t pin = 7; void setup() { Serial.begin(9600); dfSerial.begin(9600); // DFplayer Serial pinMode(pin, INPUT_PULLUP); // pullup 저항 이용 - 버튼쪽에 저항 없음 OFF - HIHG, ON - LOW send_command(0x06, 0, 0, volume); // 볼륨 lcd.begin(); lcd.backlight(); lcd.clear(); lcd.setCursor(0,0); // row, col의 좌표로 커서를 위치 col: 0 ~ 15 row: 0, 1 lcd.print("Arduino clock"); } void loop() { cal_time(); adjust_time(); speak_time(); delayCount(); alarm_warning(); alarm_send(); if (dfSerial.available()) { // 42 02 01 uint8_t temp = dfSerial.read(); if (temp == 0x42) receive_status = true; if (receive_status == true) { status_val[status_count] = temp; status_count++; if (temp == 0xEF) { status_check = true; receive_status = false; status_count = 0; if (status_val[2] == 2 && status_val[3] == 1) played = true; // 인덱스 2 = 2 -> SD카드, 인덱스 3 = 1 재생중 else if (status_val[2] == 2 && status_val[3] == 0) played = false; // 인덱스 3 = 0 정지중 } } } if (digitalRead(pin) == LOW) { // LOW – ON if (alarm_time == true) { alarm_stop = true; alarm_time = false; alarm_sentence = false; alarm_count = 0; alarm_send_f = true; } else { speak_option(); } } } void adjust_time() { if (Serial.available() > 0) { String temp = Serial.readStringUntil(‘

‘); if (temp == “dtime”) display_time(); else if (temp == “s”) { s = 0; start_time = millis(); display_t = true; } else if (temp == “mm”) { m++; display_t = true; } else if (temp == “m”) { m–; display_t = true; } else if (temp == “hh”) { h++; display_t = true; } else if (temp == “h”) { h–; display_t = true; } else if (temp == “24”) { meridian = !meridian; display_t = true; } else if (temp == “yy”) { yy++; display_t = true; } else if (temp == “y”) { yy–; display_t = true; } else if (temp == “mon”) { mm++; adjust = true; display_t = true; } else if (temp == “mon-“) { mm–; adjust = true; display_t = true; } else if (temp == “dd”) { dd++; adjust = true; display_t = true; } else if (temp == “d”) { dd–; adjust = true; display_t = true; } else if (temp == “ww”) { week_num++; adjust = true; display_t = true; } else if (temp == “w”) { week_num–; adjust = true; display_t = true; } else if (temp == “nsp”) no_speak_sharp = !no_speak_sharp; else if (temp.startsWith(“tset”)) { String set = temp.substring(4, 6); h = set.toInt(); set = temp.substring(6, 8); m = set.toInt(); set = temp.substring(8, 10); s = set.toInt(); start_time = millis(); adjust = true; display_t = true; } else if (temp.startsWith(“dset”)) { String set = temp.substring(4, 8); yy = set.toInt(); set = temp.substring(8, 10); mm = set.toInt(); set = temp.substring(10, 12); dd = set.toInt(); adjust = true; display_t = true; } else if (temp == “alarm”) { alarm_on = !alarm_on; if (alarm_on) Serial.println(“alarm on”); else Serial.println(“alarm off”); } else if (temp.startsWith(“aset”)) { String set = temp.substring(4, 6); alarm_h = set.toInt(); set = temp.substring(6, 8); alarm_m = set.toInt(); Serial.print(“alarm set: “); Serial.print(alarm_h); Serial.print(“:”); Serial.println(alarm_m); } else if (temp.startsWith(“setnsp”)) { String set = temp.substring(6, 8); no_speak_s = set.toInt(); set = temp.substring(8, 10); no_speak_f = set.toInt(); Serial.print(“no speak set: “); Serial.print(no_speak_s); Serial.print(“~”); Serial.println(no_speak_f); } else if (temp == “test”) speak_option(); else if (temp == “stop”) { alarm_stop = true; alarm_time = false; alarm_sentence = false; alarm_count = 0; alarm_send_f = true; } else if (temp == “play”) send_command(0x0F, 0, 2, 6); else if (temp == “pause”) send_command(0x0E, 1, 0, 0); else if (temp == “previous”) send_command(0x02, 1, 0, 0); else if (temp == “next”) send_command(0x01, 1, 0, 0); else if (temp == “vv”) { if(volume < 30) volume++; send_command(0x06, 0, 0, volume); } else if (temp == "v") { if(volume > 0) volume–; send_command(0x06, 0, 0, volume); } else if (temp == “reset”) send_command(0x0C, 0, 0, 0); if (m >= 60) m = 0; else if (m < 0) m = 59; if (h >= 24) h = 0; else if (h < 0) h = 23; if (h < 12) now_am = true; else now_am = false; hm = h; if(meridian == true && hm == 12) { now_am = false; } else if (meridian == true && hm > 12) { hm = hm – 12; } cal_dd(); if (display_t == true) { display_time(); display_t = false; } } } bool speak_finished = true; void speak_option() { if (m == 0 && s != 0) { sharp = true; if (h < 13) time_sharp = 2200 + h; else time_sharp = 2200 + (h - 12); } else { sharp = false; if (h < 13) hour_a = 1000 + h; else hour_a = 1000 + (h - 12); if (h < 12) am = 1301; else am = 1302; int temp = m%10; if (temp == 0 || m < 10) { short_sentence = true; min_s = 1200 + m; } else { short_sentence = false; min_w = 1100 + (m - temp); min_s = 1200 + temp; } } speak = true; speak_finished = false; } uint8_t am_s = 6; uint8_t hour_a_s = 23; uint8_t long_min_w_s = 46; uint8_t long_min_s_s = 67; uint8_t long_min_s_end = 100; uint8_t short_min_s_s = 46; uint8_t short_min_s_end = 76; uint8_t time_sharp_s = 6; uint8_t time_sharp_end = 63; void speak_time() { if (speak == true) { if (sequence == 0 && status_check == false) { send_command(0x42, 0, 0, 0); sequence++; } if (status_check == true) { if (sequence == 1) { send_command(0x06, 0, 0, 30); // 볼륨 30 sequence++; } else if (sequence == 2 && played == true) { sequence = 5; delay_set = true; } else if (sequence == 2 && played == false) { send_command(0x0F, 0, 2, 255); sequence++; } else if (sequence == 3 && played == false) { delay_set = true; sequence++; } if (sharp == false) { if (sequence == am_s) { advert(am); speak = false; } else if (sequence == hour_a_s) { advert(hour_a); speak = false; } if (short_sentence == false) { if (sequence == long_min_w_s) { advert(min_w); speak = false; } else if (sequence == long_min_s_s) { advert(min_s); speak = false; } else if (sequence == long_min_s_end) { send_command(0x06, 0, 0, volume); speak_finished = true; speak = false; status_check = false; sequence = 0; } } else { if (sequence == short_min_s_s) { advert(min_s); speak = false; } else if (sequence == short_min_s_end) { send_command(0x06, 0, 0, volume); speak_finished = true; speak = false; status_check = false; sequence = 0; } } } else { if (sequence == time_sharp_s) { advert(time_sharp); speak = false; } else if (sequence == time_sharp_end) { send_command(0x06, 0, 0, volume); speak_finished = true; speak = false; status_check = false; sequence = 0; time_sharp_end = 63; } } } } } void advert(int ad_num) { uint8_t ad_lsb = ad_num; uint8_t ad_msb = ad_num >> 8; send_command(0x13, 0, ad_msb, ad_lsb); } void delayCount() { if (delay_set == true) { if (millis() – count_time >= 50) { // 시간 간격: 밀리초 count_time = millis(); sequence++; if (sharp == false) { if (sequence == am_s) speak = true; else if (sequence == hour_a_s) speak = true; if (short_sentence == false) { if (sequence == long_min_w_s) speak = true; else if (sequence == long_min_s_s) speak = true; else if (sequence == long_min_s_end) { speak = true; delay_set = false; } } else { if (sequence == short_min_s_s) speak = true; else if (sequence == short_min_s_end) { speak = true; delay_set = false; } } } else { if (sequence == time_sharp_s) speak = true; else if (sequence == time_sharp_end) { speak = true; delay_set = false; } } } } } bool change_millis = false; uint16_t millis_sec = 994; void cal_time() { if (millis() – start_time >= millis_sec) { // 시간 간격: 밀리초 start_time = millis(); // 상기 조건을 만족할때의 밀리초를 다시 start_time에 저장하여 조건 초기화 change_millis = ! change_millis; if (change_millis == true) millis_sec = 995; else millis_sec = 994; s++; if (s == 60) { s = 0; m++; } if(m == 60) { m = 0; h++; } if(h == 12) { now_am = false;} else if (h == 24) { h = 0; now_am = true; dd++; week_num++; cal_dd(); } hm = h; if(meridian == true && hm == 12) { now_am = false; } else if (meridian == true && hm > 12) { hm = hm – 12; } else now_am = true; display_time(); if (no_speak_sharp == true) { if (no_speak_s == no_speak_f) no_speak_sharp = false; } if (alarm_on) { if (alarm_h == h && alarm_m == m && s == 0){ alarm_time = true; alarm_send_f = true; } } if (alarm_time != true) { if (m == 0 && s == 0) { if (no_speak_sharp == false) { speak_sharp(); } else { if (no_speak_s > no_speak_f) { if (h < no_speak_s && h > no_speak_f) { speak_sharp(); } } else if (no_speak_s < no_speak_f) { if (h > no_speak_s && h < no_speak_f) { speak_sharp(); } } } } } } } void speak_sharp() { if (speak_finished == true) { sharp = true; if (h < 13) time_sharp = 2100 + h; else time_sharp = 2100 + (h - 12); time_sharp_end = 48; speak = true; } } void alarm_send() { if (alarm_send_f == true) { if (millis() - alarm_sequence_delay >= 20) { // 시간 간격: 밀리초 alarm_sequence_delay = millis(); alarm_sequence++; if (alarm_stop == true) { if (alarm_sequence == 1) send_command(0x06, 0, 0, volume); else if (alarm_sequence == 2) send_command(0x16, 0, 0, 0); else if (alarm_sequence == 3) { send_command(0x15, 0, 0, 0); alarm_send_f = false; alarm_sequence = 0; alarm_stop = false; } } else { if (alarm_sequence == 1) send_command(0x16, 0, 0, 0); else if (alarm_sequence == 2) send_command(0x06, 0, 0, 30); // 볼륨 30 else if (alarm_sequence == 3) send_command(0x0F, 0, alarm_track_folder, alarm_track); // 폴더2 002번 사운드 파일 else if (alarm_sequence == 8) { send_command(0x19, 0, 0, 0); // 반복재생 alarm_sentence = true; alarm_count = 0; alarm_send_f = false; alarm_sequence = 0; } } } } } void alarm_warning() { if (alarm_sentence == true) { if (millis() – alarm_delay >= 1000) { // 시간 간격: 밀리초 alarm_delay = millis(); alarm_count++; if (alarm_count == 4) { advert(1401); } else if (alarm_count == 12) { advert(1402); } else if (alarm_count == 25) { advert(1403); } else if (alarm_count == 70) { advert(1404); alarm_sentence = false; alarm_count = 0; } } } } void cal_dd() { uint8_t m_dds; switch (mm) { case 1: m_dds = 31; break; case 2: m_dds = 28; break; case 3: m_dds = 31; break; case 4: m_dds = 30; break; case 5: m_dds = 31; break; case 6: m_dds = 30; break; case 7: m_dds = 31; break; case 8: m_dds = 31; break; case 9: m_dds = 30; break; case 10: m_dds = 31; break; case 11: m_dds = 30; break; case 12: m_dds = 31; break; } if (adjust == false) { if (dd > m_dds) { dd = 1; mm++; } if (mm > 12) { mm = 1; yy++; } if (week_num > 7) week_num = 1; week_day_converter(); } else { if (dd > m_dds) dd = 1; else if (dd < 1) dd = m_dds; if (mm > 12) mm = 1; else if (mm < 1) mm = 12; if (week_num > 7) week_num = 1; else if (week_num < 1) week_num = 7; week_day_converter(); adjust = false; } } void week_day_converter() { switch (week_num) { case 1: week_day = "SUN"; break; case 2: week_day = "MON"; break; case 3: week_day = "TUE"; break; case 4: week_day = "WED"; break; case 5: week_day = "THU"; break; case 6: week_day = "FRI"; break; case 7: week_day = "SAT"; break; } } void display_time() { Serial.print(yy); Serial.print("."); if (mm < 10) { Serial.print("0"); Serial.print(mm); Serial.print("."); } else { Serial.print(mm); Serial.print("."); } if (dd < 10) { Serial.print("0"); Serial.print(dd); Serial.print(". "); } else { Serial.print(dd); Serial.print(". "); } Serial.print(week_day); Serial.print(" "); if (meridian == true) { if (now_am == true) Serial.print("AM "); else Serial.print("PM "); if (hm < 10) { Serial.print("0"); Serial.print(hm); Serial.print(":"); } else { Serial.print(hm); Serial.print(":"); } } else { if (h < 10) { Serial.print("0"); Serial.print(h); Serial.print(":"); } else { Serial.print(h); Serial.print(":"); } } if (m < 10) { Serial.print("0"); Serial.print(m); Serial.print(":"); } else { Serial.print(m); Serial.print(":"); } if (s < 10) { Serial.print("0"); Serial.println(s); } else Serial.println(s); lcd.setCursor(0,0); lcd.print(yy); lcd.print("."); if (mm < 10) { lcd.print("0"); lcd.print(mm); lcd.print("."); } else { lcd.print(mm); lcd.print("."); } if (dd < 10) { lcd.print("0"); lcd.print(dd); lcd.print(". "); } else { lcd.print(dd); lcd.print(". "); } lcd.print(week_day); lcd.print(" "); lcd.setCursor(0,1); if (meridian == true) { if (now_am == true) lcd.print("AM "); else lcd.print("PM "); if (hm < 10) { lcd.print("0"); lcd.print(hm); lcd.print(":"); } else { lcd.print(hm); lcd.print(":"); } } else { if (h < 10) { lcd.print("0"); lcd.print(h); lcd.print(":"); } else { lcd.print(h); lcd.print(":"); } } if (m < 10) { lcd.print("0"); lcd.print(m); lcd.print(":"); } else { lcd.print(m); lcd.print(":"); } if (s < 10) { lcd.print("0"); lcd.print(s); lcd.print(" "); } else { lcd.print(s); lcd.print(" "); } // 크리스탈 LCD에는 pritln 명령어 없음 } * 마지막 종료 문장 TTS 음성파일의 끝부분의 묵음 부분을 조금 더 길게 하는 게 좋을 거 같다. 이전 볼륨으로 돌아가는 명령 코드를 보내는 타이밍을 맞추기가 좀 더 쉬워질 것이다. 타이밍이 맞지 않으면 사운드 파일 재생 중 이어서 재생하기가 풀리게 된다. 현재 사용 중인 TTS 파일은 묵음 구간이 너무 짧아 ADVERT 재생 종료 전 이전 볼륨으로 돌아가는 명령어를 전송할 타이밍을 잡기가 쉽지 않았다. 말하는 알람시계는 완성되었다. 시리얼 모니터에서 알람을 설정하고 시간을 맞출 수 있으나, 컴퓨터 연결 없이 외부 전원을 이용할 경우에는 설정을 할 수 없게 된다. 다음 편에서는 블루투스 모듈을 연결하고 안드로이드 앱을 통해 시간 동기화 및 알람 설정을 하고 DFPlayer의 사운드 파일 제어를 해보겠다. 2019.0901 추가사항 상기코드로는 사운드 파일 재생중 현재 시간 출력 시 간헐적으로 오전/오후 음성을 건너 뛰는 현상이 발행하여 아래와 같이 음성출력 관련 변수 및 코드 speak_time(), delayCount()등을 수정해 주었다. 이전 코드는 시간 관련 음성 출력전에 알람용 볼륨 설정 명령을 DFPlayer에 보낸 후 50밀리 초 뒤에 음성 출력 명령을 보냈었지만, 이때 간헐적으로 DFPlayer에서 볼륨 설정 명령 때문에 음성 출력 명령을 제대로 처리하지 못하는 것같다. 이에 음성 출력 명령 을 보내고 100밀리 초 후에 볼륨 설정 명령을 보내도록 코드를 수정하였다. uint8_t am_s = 4; uint8_t vol_s = 6; uint8_t hour_a_s = 21; uint8_t long_min_w_s = 44; uint8_t long_min_s_s = 65; uint8_t long_min_s_end = 96; uint8_t short_min_s_s = 44; uint8_t short_min_s_end = 74; uint8_t time_sharp_s = 4; uint8_t time_sharp_end = 64; void speak_time() { if (speak == true) { if (sequence == 0 && status_check == false) { send_command(0x42, 0, 0, 0); sequence++; } else if (sequence == 1 && status_check == true) { if (played == false) { send_command(0x0F, 0, 2, 255); sequence++; // 2 delay_set = true; } else { delay_set = true; sequence = 3; } } else if (sequence >= am_s && sharp == false) { if (sequence == am_s) { advert(am); speak = false; } else if (sequence == vol_s) { send_command(0x06, 0, 0, time_volume); speak = false; } else if (sequence == hour_a_s) { advert(hour_a); speak = false; } if (short_sentence == false) { if (sequence == long_min_w_s) { advert(min_w); speak = false; } else if (sequence == long_min_s_s) { advert(min_s); speak = false; } else if (sequence == long_min_s_end) { send_command(0x06, 0, 0, volume); speak_finished = true; speak = false; status_check = false; sequence = 0; } } else { if (sequence == short_min_s_s) { advert(min_s); speak = false; } else if (sequence == vol_s) { send_command(0x06, 0, 0, time_volume); speak = false; } else if (sequence == short_min_s_end) { send_command(0x06, 0, 0, volume); speak_finished = true; speak = false; status_check = false; sequence = 0; } } } else if (sequence >= time_sharp_s && sharp == true) { if (sequence == time_sharp_s) { advert(time_sharp); speak = false; } else if (sequence == vol_s) { send_command(0x06, 0, 0, time_volume); speak = false; } else if (sequence == time_sharp_end) { send_command(0x06, 0, 0, volume); speak_finished = true; speak = false; status_check = false; sequence = 0; time_sharp_end = 64; } } } } void advert(int ad_num) { uint8_t ad_lsb = ad_num; uint8_t ad_msb = ad_num >> 8; send_command(0x13, 0, ad_msb, ad_lsb); } void delayCount() { if (delay_set == true) { if (millis() – count_time >= 50) { // 시간 간격: 밀리초 count_time = millis(); sequence++; if (sharp == false) { if (sequence == am_s) speak = true; else if (sequence == vol_s) speak = true; else if (sequence == hour_a_s) speak = true; if (short_sentence == false) { if (sequence == long_min_w_s) speak = true; else if (sequence == long_min_s_s) speak = true; else if (sequence == long_min_s_end) { speak = true; delay_set = false; } } else { if (sequence == short_min_s_s) speak = true; else if (sequence == short_min_s_end) { speak = true; delay_set = false; } } } else { if (sequence == time_sharp_s) speak = true; else if (sequence == vol_s) speak = true; else if (sequence == time_sharp_end) { speak = true; delay_set = false; } } } } }

2019.09.04 추가사항

12시 이후에 시간동기화 할 경우 오후 반영안되는 현상이 발견되어 cal_time() 함수 수정함.

if(meridian == true && hm == 12) { now_am = false; } else if (meridian == true && hm > 12) { hm = hm – 12; } else now_am = true;

수정 후

if(meridian == true && hm >= 12) { now_am = false; } else now_am = true; if (meridian == true && hm > 12) { hm = hm – 12; }

다음편 블루투스 연결 스마트폰 시스템 시간 동기화에서 수정된 전체 코드를 적용토록 하겠다.

2019.09.09 수정사항

speak_option() 함수와 speak_sharp()함수의 0시 음성출력을 위한 ADVERT 파일 번호 계산 오류를 수정 했으며, 묵음구간 적용이 안되어 cal_time() 함수의 묵음 구간 코드를 아래와 같이 수정해 주어야 한다.

speak_option() 함수

// 수정전 if (h < 13) time_sharp = 2200 + h; else time_sharp = 2200 + (h - 12); // 수정후 if (h == 0) time_sharp = 2212; else if (h < 13 && h > 0) time_sharp = 2200 + h; else time_sharp = 2200 + (h – 12); // 수정전 if (h < 13) hour_a = 1000 + h; else hour_a = 1000 + (h - 12); // 수정후 if (h == 0) hour_a = 1012; else if (h < 13 && h > 0) hour_a = 1000 + h; else hour_a = 1000 + (h – 12);

speak_sharp()함수

// 수정전 if (h < 13) time_sharp = 2100 + h; else time_sharp = 2100 + (h - 12); // 수정후 if (h == 0) time_sharp = 2112; else if (h < 13 && h > 0) time_sharp = 2100 + h; else time_sharp = 2100 + (h – 12);

cal_time() 함수

// 수정전 if (no_speak_sharp == true) { if (no_speak_s == no_speak_f) no_speak_sharp = false; } if (alarm_time != true) { if (m == 0 && s == 0) { if (no_speak_sharp == false) { speak_sharp(); } else { if (no_speak_s > no_speak_f) { if (h < no_speak_s && h > no_speak_f) { speak_sharp(); } } else if (no_speak_s < no_speak_f) { if (h > no_speak_s && h < no_speak_f) { speak_sharp(); } } } } } // 수정후 if (alarm_time != true) { if (m == 0 && s == 0) { if (no_speak_sharp == true) { if (no_speak_s == no_speak_f) { no_speak_sharp = false; speak_sharp(); } else { if (no_speak_s > no_speak_f) { if (h < no_speak_s && h > no_speak_f) { speak_sharp(); } } else if (no_speak_s < no_speak_f) { if (h < no_speak_s && h >= 0 || h > no_speak_f && h <= 24) { speak_sharp(); } } } } else { speak_sharp(); } } } 2019.09.09 수정사항 음성 출력 파일들의 묵음 구간을 늘려 놓았으며 그에 따라 아래 코드를 수정하여 음성이 종료되기 전에 볼륨이 변경되는 오류를 수정하였다. DFPlayer의 SD카드 파일을 모두 삭제하고 아래 파일의 폴더들을 모두 선택하여 붙여넣기 한뒤 아래 코드를 수정해 주면 특정 시간 출력시 음성이 종료되기전에 이전 볼륨으로 변경되던 오류가 수정되게 된다. // 음성 출력 변수 수정 64 -> 80 uint8_t time_sharp_end = 80; // speak_time() 함수 수정 64 -> 80 time_sharp_end = 80; // speak_sharp() 함수 수정 46 -> 48 time_sharp_end = 48;

관련 글

[arduino] – DFplayer – 아두이노 사운드 모듈

[arduino] – NodeMcu(ESP8266)에서 DFplayer를 제어하는 코드

[arduino] – ESP32(DevKit)로 DFplayer 제어하기

[arduino] – 안드로이드 앱 DFcontroller를 이용하여 DFplayer 제어

[arduino] – 아두이노시계 예제, ESP01 WiFi 이용 시간 동기화 하기

[arduino] – 아두이노 말하는알람시계 예제 – DFPlayer

[arduino] – 말하는알람시계 – 블루투스 연결 및 시간 동기화, DFPlayer 제어

[arduino] – NodeMcu – 말하는 알람시계, wifi이용 시간 동기화 및 DFPlayer 원격제어

키워드에 대한 정보 아두 이노 알람 시계

다음은 Bing에서 아두 이노 알람 시계 주제에 대한 검색 결과입니다. 필요한 경우 더 읽을 수 있습니다.

이 기사는 인터넷의 다양한 출처에서 편집되었습니다. 이 기사가 유용했기를 바랍니다. 이 기사가 유용하다고 생각되면 공유하십시오. 매우 감사합니다!

사람들이 주제에 대해 자주 검색하는 키워드 [DS3231RTC] Arduino로 알람시계 만들기…^^

  • DS3231
  • RTC
  • Arduino
  • 알람시계
  • DIY
[DS3231RTC] #Arduino로 #알람시계 #만들기…^^


YouTube에서 아두 이노 알람 시계 주제의 다른 동영상 보기

주제에 대한 기사를 시청해 주셔서 감사합니다 [DS3231RTC] Arduino로 알람시계 만들기…^^ | 아두 이노 알람 시계, 이 기사가 유용하다고 생각되면 공유하십시오, 매우 감사합니다.

See also  첼시 스쿼드 피파 4 | 급여 220 첼시 금액별 스쿼드 이렇게 맞추세요! 피파4 모든 답변

Leave a Comment