작품 소개
알람 시간을 설정해 두면 해당 시간에 알람(아기상어)이 울리면서 임의의 방향으로 방향을 바꾸면서 버튼을 누를 때까지 계속 도망다니는 로봇입니다.
본 작품은 무지개 지역 아동센터 교육 중 자유작품으로 아이들과 함께 기능을 구상하여 제작하였습니다.
주요 부품

1. RTC 모듈: 별도의 외부 전원 없이도 현재 시간을 저장하고 지속 갱신합니다.
2. 모터 드라이버: 바퀴 구동을 위한 DC 모터 2개의 방향 및 속도를 별도 제어합니다. 본 작품에서는 속도는 제어하지 않고 정지 및 최고 속도만 적용합니다.
3. 스위치: 로봇의 전체 전원을 켜거나 끕니다.
4. 스피커 모듈: 설정된 알람 시간이 되면 버튼을 누르기 전까지 아기상어 음악을 송출합니다.
5. 미니 브레드보드: 각 모듈에 전원이나 신호를 배분하기 위해 사용합니다.
6. 푸시버튼: 1개는 알람의 시간을 0~23까지 1시간 단위로 올립니다(23 다음은 다시 0으로). 나머지 1개는 알람의 분을 0~55까지 5분 단위로 올립니다(55 다음은 시간을 1 올리고 분은 다시 0으로).
7. LCD 모듈: 현재 시간을 1초에 1번 갱신하여 보여주고 세팅된 알람 시간도 보여줍니다.
8. 자동차 키트: 차체 및 모터 고정 부품, 수직 동력전달 기어박스 결함된 DC 모터 세트 x 2, 바퀴 x 2, 보조바퀴, 배터리 케이스, 기타 볼트 및 나사류
회로도

아두이노 | 액츄에이터 |
VIN | 배터리 +전원 입력 |
GND | 배터리 -전원 연결 |
5V | 브레드보드 +전원 출력 |
GND | 브레드보드 -전원 연결 |
D01 | 시간 버튼 |
D02 | 분 버튼 |
D03 | 스피커모듈_IN |
D04 | 모터드라이버_IN1 |
D05 | 모터드라이버_IN2 |
D06 | 모터드라이버_IN3 |
D07 | 모터드라이버_IN4 |
D08 | RTC모듈_RST |
D09 | RTC모듈_DAT |
D10 | RTC모듈_CLK |
SDA | LCD모듈_SDA |
SCL | LCD모듈_SCL |
하드웨어 제작/조립 방법
자동차 키트의 모터를 아래와 같이 지지대와 함께 볼트와 너트로 고정 해 주세요. 모터를 다 조립했으면 마지막 사진처럼 바퀴까지 조립 해 주세요.

다음은 모터가 있는 차체 아랫 면에 서포터가 부착되도록 지지대 4개를 달아 보조 바퀴를 조립합니다.

배터리 홀더를 아랫 부분 양 쪽 나사 홈을 통해 고정 해 줍니다.

차체 윗 면에 나머지 부품들을 글루건을 이용 해 아래와 같이(혹은 자유롭게) 고정 시켜 줍니다. 이 때 아두이노 보드의 케이블 연결 부분에 간섭이 일어나지 않도록 주의 해야 합니다. 부품 고정이 완료된 후에는 회로도와 같이 선을 연결 해 주세요.

스위치는 아래와 같이 핀이 차체 아래로 나오도록 고정한 후 전선을 연결합니다(납땜 추천).

코드
아래 코드를 실행하기 위해서는 다음 라이브러리의 설치가 필요합니다.
1. Rtc by Makuna by Michael C. Miller : RTC 모듈의 제어를 위해 필요합니다.
2. LiquidCrystal I2C : LCD 모듈의 제어를 위해 필요합니다.
3. ArduinoThread by Ivan Seidel : 1초에 한 번 LCD에 현재 시간을 표현하는 함수를 Thread 프로그램 화 하기 위해 필요합니다.
#include <Thread.h> #include <ThreeWire.h> #include <RtcDS1302.h> #include <LiquidCrystal_I2C.h> //------------------------------------------------------------------------------------- //시계관련 Thread clThread = Thread(); ThreeWire myWire(9,10,8); // IO(DAT), SCLK, CE(RST) 디지털 연결 번호 RtcDS1302<ThreeWire> Rtc(myWire); LiquidCrystal_I2C lcd(0x27,16,2); #define countof(a) (sizeof(a) / sizeof(a[0])) // 시계 모듈 년도, 달, 시간, 분, 초 받아오는 함수 RtcDateTime alm; bool b_almState = true; unsigned long int millTime = 0; //------------------------------------------------------------------------------------- //버튼관련 int BUTTON1 = 1; int buttonState1, buttonState1_old; int state1 = 0; int BUTTON2 = 2; int buttonState2; int state2 = 0; int alm_H = 0; int alm_M = 0; int alm_S = 0; //-------------------------------------------------------------------------------------- //모터관련 int Flag_FWDBWD; // 0:전진, 1:후진 int Flag_DIRECT; // 0:정방향, 1:좌회전, 2:우회전 #define M_IN1 4 // 모터제어 입력 핀 IN1을 Digital4번핀으로 설정 #define M_IN2 5 // 모터제어 입력 핀 IN2을 Digital5번핀으로 설정 #define M_IN3 6 // 모터제어 입력 핀 IN3을 Digital13번핀으로 설정 #define M_IN4 7 // 모터제어 입력 핀 IN4을 Digital12번핀으로 설정 //------------------------------------------------------------------------------------- //알람관련 // 코드 설명 https://postpop.tistory.com/98 // 12화음 이름 정의 typedef enum { NOTE_C, NOTE_Cs, NOTE_D, NOTE_Eb, NOTE_E, NOTE_F, NOTE_Fs, NOTE_G, NOTE_Gs, NOTE_A, NOTE_Bb, NOTE_B, NOTE_MAX } note_t; // 화음별 피에조 부저 진동수 정의 및 옥타브에 따른 값 변경 함수 double ledcWriteNote(note_t note, uint8_t octave){ const uint16_t noteFrequencyBase[12] = { // 1 2 3 4 5 6 7 8 9 10 11 12 // C C# D Eb E F F# G G# A Bb B 4186, 4435, 4699, 4978, 5274, 5588, 5920, 6272, 6645, 7040, 7459, 7902 }; if(octave > 8 || note >= NOTE_MAX){ return 0; } double noteFreq = (double)noteFrequencyBase[note] / (double)(1 << (8-octave)); return noteFreq; } // ---------------------------------------------------------------------- 아기상어 uint8_t Tempo = 220; float tempoRatio = float(Tempo)/60.0; uint8_t KeySign = 0; uint8_t octaveTemp = 4; float beatTime = 4/4; const note_t Mnote[] PROGMEM= { NOTE_D,NOTE_E,NOTE_D,NOTE_E,NOTE_D,NOTE_E,NOTE_D,NOTE_E,NOTE_D,NOTE_E,NOTE_D,NOTE_E,NOTE_D,NOTE_E,NOTE_D,NOTE_E, NOTE_G,NOTE_F,NOTE_G,NOTE_A,NOTE_G,NOTE_F,NOTE_D,NOTE_F,NOTE_G,NOTE_F,NOTE_G,NOTE_A,NOTE_G,NOTE_F,NOTE_D,NOTE_F,NOTE_G,NOTE_F,NOTE_G,NOTE_A,NOTE_G,NOTE_F,NOTE_D,NOTE_F,NOTE_A,NOTE_MAX,NOTE_MAX,NOTE_D,NOTE_E, NOTE_G,NOTE_G,NOTE_G,NOTE_G,NOTE_G,NOTE_MAX,NOTE_G,NOTE_G,NOTE_D,NOTE_E,NOTE_G,NOTE_MAX,NOTE_G,NOTE_G,NOTE_G,NOTE_MAX,NOTE_G,NOTE_G,NOTE_D,NOTE_E,NOTE_G,NOTE_MAX,NOTE_G,NOTE_G,NOTE_G,NOTE_MAX,NOTE_G,NOTE_G,NOTE_G,NOTE_G,NOTE_F,NOTE_F,NOTE_MAX,NOTE_MAX,NOTE_D,NOTE_E, NOTE_G,NOTE_G,NOTE_G,NOTE_G,NOTE_G,NOTE_MAX,NOTE_G,NOTE_G,NOTE_D,NOTE_E,NOTE_G,NOTE_MAX,NOTE_G,NOTE_G,NOTE_G,NOTE_MAX,NOTE_G,NOTE_G,NOTE_D,NOTE_E,NOTE_G,NOTE_MAX,NOTE_G,NOTE_G,NOTE_G,NOTE_MAX,NOTE_G,NOTE_G,NOTE_G,NOTE_G,NOTE_F,NOTE_F,NOTE_MAX,NOTE_MAX,NOTE_D,NOTE_E, NOTE_G,NOTE_G,NOTE_G,NOTE_G,NOTE_G,NOTE_MAX,NOTE_G,NOTE_G,NOTE_D,NOTE_E,NOTE_G,NOTE_MAX,NOTE_G,NOTE_G,NOTE_G,NOTE_MAX,NOTE_G,NOTE_G,NOTE_D,NOTE_E,NOTE_G,NOTE_MAX,NOTE_G,NOTE_G,NOTE_G,NOTE_MAX,NOTE_G,NOTE_G,NOTE_G,NOTE_G,NOTE_F,NOTE_F,NOTE_MAX,NOTE_MAX,NOTE_D,NOTE_D,NOTE_E, NOTE_G,NOTE_G,NOTE_G,NOTE_G,NOTE_G,NOTE_MAX,NOTE_G,NOTE_G,NOTE_D,NOTE_E,NOTE_G,NOTE_MAX,NOTE_G,NOTE_G,NOTE_G,NOTE_MAX,NOTE_G,NOTE_G,NOTE_D,NOTE_E,NOTE_G,NOTE_MAX,NOTE_G,NOTE_G,NOTE_G,NOTE_MAX,NOTE_G,NOTE_G,NOTE_G,NOTE_G,NOTE_F,NOTE_F,NOTE_MAX,NOTE_MAX,NOTE_D,NOTE_E, NOTE_G,NOTE_G,NOTE_G,NOTE_G,NOTE_G,NOTE_MAX,NOTE_G,NOTE_G,NOTE_D,NOTE_E,NOTE_G,NOTE_MAX,NOTE_G,NOTE_G,NOTE_G,NOTE_MAX,NOTE_G,NOTE_G,NOTE_D,NOTE_E,NOTE_G,NOTE_MAX,NOTE_G,NOTE_G,NOTE_G,NOTE_MAX,NOTE_G,NOTE_G,NOTE_G,NOTE_G,NOTE_F,NOTE_F,NOTE_MAX,NOTE_MAX,NOTE_D,NOTE_E, NOTE_G,NOTE_MAX,NOTE_G,NOTE_G,NOTE_G,NOTE_MAX,NOTE_G,NOTE_G,NOTE_D,NOTE_E,NOTE_G,NOTE_MAX,NOTE_G,NOTE_G,NOTE_G,NOTE_MAX,NOTE_G,NOTE_G,NOTE_D,NOTE_E,NOTE_G,NOTE_MAX,NOTE_G,NOTE_G,NOTE_G,NOTE_MAX,NOTE_G,NOTE_G,NOTE_G,NOTE_G,NOTE_F,NOTE_F,NOTE_MAX,NOTE_MAX,NOTE_D,NOTE_F, NOTE_A,NOTE_MAX,NOTE_A,NOTE_A,NOTE_A,NOTE_MAX,NOTE_A,NOTE_A,NOTE_E,NOTE_F,NOTE_A,NOTE_MAX,NOTE_A,NOTE_A,NOTE_A,NOTE_MAX,NOTE_A,NOTE_A,NOTE_E,NOTE_F,NOTE_A,NOTE_MAX,NOTE_A,NOTE_A,NOTE_A,NOTE_MAX,NOTE_A,NOTE_A,NOTE_A,NOTE_A,NOTE_G,NOTE_G,NOTE_MAX,NOTE_MAX,NOTE_E,NOTE_F, NOTE_A,NOTE_MAX,NOTE_A,NOTE_A,NOTE_A,NOTE_MAX,NOTE_A,NOTE_A,NOTE_E,NOTE_F,NOTE_A,NOTE_MAX,NOTE_A,NOTE_A,NOTE_A,NOTE_MAX,NOTE_A,NOTE_A,NOTE_E,NOTE_F,NOTE_A,NOTE_MAX,NOTE_A,NOTE_A,NOTE_A,NOTE_MAX,NOTE_A,NOTE_A,NOTE_A,NOTE_A,NOTE_G,NOTE_MAX,NOTE_MAX,NOTE_MAX,NOTE_E,NOTE_F, NOTE_A,NOTE_MAX,NOTE_A,NOTE_A,NOTE_A,NOTE_MAX,NOTE_A,NOTE_A,NOTE_E,NOTE_F,NOTE_A,NOTE_MAX,NOTE_A,NOTE_A,NOTE_A,NOTE_MAX,NOTE_A,NOTE_A,NOTE_E,NOTE_F,NOTE_A,NOTE_MAX,NOTE_A,NOTE_A,NOTE_B,NOTE_MAX,NOTE_B,NOTE_B,NOTE_B,NOTE_B,NOTE_A,NOTE_E,NOTE_F,NOTE_MAX,NOTE_E,NOTE_F,NOTE_A,NOTE_MAX,NOTE_MAX, }; const uint8_t Moct[] PROGMEM= { 3,3,3,3,3,3,3,3,4,4,4,4,5,5,5,5, 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4, 4,4,4,4,4,0,4,4,4,4,4,4,4,4,4,0,4,4,4,4,4,4,4,4,4,0,4,4,4,4,4,4,4,4,4,4, 4,4,4,4,4,0,4,4,4,4,4,4,4,4,4,0,4,4,4,4,4,4,4,4,4,0,4,4,4,4,4,4,4,4,4,4, 4,4,4,4,4,0,4,4,4,4,4,4,4,4,4,0,4,4,4,4,4,4,4,4,4,0,4,4,4,4,4,4,4,4,4,4,4, 4,4,4,4,4,0,4,4,4,4,4,4,4,4,4,0,4,4,4,4,4,4,4,4,4,0,4,4,4,4,4,4,4,4,4,4, 4,4,4,4,4,0,4,4,4,4,4,4,4,4,4,0,4,4,4,4,4,4,4,4,4,0,4,4,4,4,4,4,4,4,4,4, 4,4,4,4,4,0,4,4,4,4,4,4,4,4,4,0,4,4,4,4,4,4,4,4,4,0,4,4,4,4,4,4,4,4,4,4, 4,4,4,4,4,0,4,4,4,4,4,4,4,4,4,0,4,4,4,4,4,4,4,4,4,0,4,4,4,4,4,4,4,4,4,4, 4,4,4,4,4,0,4,4,4,4,4,4,4,4,4,0,4,4,4,4,4,4,4,4,4,0,4,4,4,4,4,4,4,4,4,4, 4,4,4,4,4,0,4,4,4,4,4,4,4,4,4,0,4,4,4,4,4,4,4,4,4,0,4,4,4,4,4,4,4,0,4,4,4,4,4, 0, }; const uint8_t localKey[] PROGMEM= { 0,2,0,2,0,2,0,2,0,2,0,2,0,2,0,2, 0,0,0,2,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,3,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,3,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,3,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,3,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,3,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,3,0,0,3,0, 2,0,2,2,2,0,2,2,2,0,2,0,2,2,2,0,2,2,2,0,2,0,2,2,2,0,2,2,2,2,0,0,0,0,2,0, 2,0,2,2,2,0,2,2,2,0,2,0,2,2,2,0,2,2,2,0,2,0,2,2,2,0,2,2,2,2,0,0,0,0,2,0, 2,0,2,2,2,0,2,2,2,0,2,0,2,2,2,0,2,2,2,0,2,0,2,2,2,0,2,2,2,2,2,2,0,0,2,0,2,0,0, 0, }; const uint8_t Mdur[] PROGMEM= { 16, 8,8,8,8,4,4,4,4,2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,4,4,4, 2,2,2,1,1,1,1,2,2,2,2,2,2,1,1,1,1,2,2,2,2,2,2,1,1,1,1,2,2,2,2,2,4,4,2,2, 2,2,2,1,1,1,1,2,2,2,2,2,2,1,1,1,1,2,2,2,2,2,2,1,1,1,1,2,2,2,2,2,4,4,2,2, 2,2,2,1,1,1,1,2,2,2,2,2,2,1,1,1,1,2,2,2,2,2,2,1,1,1,1,2,2,2,2,2,4,2,2,2,2, 2,2,2,1,1,1,1,2,2,2,2,2,2,1,1,1,1,2,2,2,2,2,2,1,1,1,1,2,2,2,2,2,4,4,2,2, 2,2,2,1,1,1,1,2,2,2,2,2,2,1,1,1,1,2,2,2,2,2,2,1,1,1,1,2,2,2,2,2,4,4,2,2, 2,2,2,1,1,1,1,2,2,2,2,2,2,1,1,1,1,2,2,2,2,2,2,1,1,1,1,2,2,2,2,2,4,4,2,2, 2,2,2,1,1,1,1,2,2,2,2,2,2,1,1,1,1,2,2,2,2,2,2,1,1,1,1,2,2,2,2,2,4,4,2,2, 2,2,2,1,1,1,1,2,2,2,2,2,2,1,1,1,1,2,2,2,2,2,2,1,1,1,1,2,2,2,2,2,4,4,2,2, 2,2,2,1,1,1,1,2,2,2,2,2,2,1,1,1,1,2,2,2,2,2,2,1,1,1,1,2,2,2,2,1,1,1,1,2,2,2,4, }; // ---------------------------------------------------------------------- uint8_t noteNum = 0; note_t keySignature(note_t note) { if (pgm_read_byte(&localKey[noteNum]) == 0) { if (KeySign == 1) { if (note == NOTE_B) note = NOTE_Bb; } else if (KeySign == 2) { if (note == NOTE_B) note = NOTE_Bb; else if (note == NOTE_E) note = NOTE_Eb; } else if (KeySign == 3) { if (note == NOTE_B) note = NOTE_Bb; else if (note == NOTE_E) note = NOTE_Eb; else if (note == NOTE_A) note = NOTE_Gs; } else if (KeySign == 4) { if (note == NOTE_B) note = NOTE_Bb; else if (note == NOTE_E) note = NOTE_Eb; else if (note == NOTE_A) note = NOTE_Gs; else if (note == NOTE_D) note = NOTE_Cs; } else if (KeySign == 5) { if (note == NOTE_B) note = NOTE_Bb; else if (note == NOTE_E) note = NOTE_Eb; else if (note == NOTE_A) note = NOTE_Gs; else if (note == NOTE_D) note = NOTE_Cs; else if (note == NOTE_G) note = NOTE_Fs; } else if (KeySign == 6) { if (note == NOTE_B) note = NOTE_Bb; else if (note == NOTE_E) note = NOTE_Eb; else if (note == NOTE_A) note = NOTE_Gs; else if (note == NOTE_D) note = NOTE_Cs; else if (note == NOTE_G) note = NOTE_Fs; else if (note == NOTE_C) { note = NOTE_B; octaveTemp -= 1; } } else if (KeySign == 7) { if (note == NOTE_B) note = NOTE_Bb; else if (note == NOTE_E) note = NOTE_Eb; else if (note == NOTE_A) note = NOTE_Gs; else if (note == NOTE_D) note = NOTE_Cs; else if (note == NOTE_G) note = NOTE_Fs; else if (note == NOTE_C) { note = NOTE_B; octaveTemp -= 1; } else if (note == NOTE_F) note = NOTE_E; } else if (KeySign == 8) { if (note == NOTE_F) note = NOTE_Fs; } else if (KeySign == 9) { if (note == NOTE_F) note = NOTE_Fs; else if (note == NOTE_C) note = NOTE_Cs; } else if (KeySign == 10) { if (note == NOTE_F) note = NOTE_Fs; else if (note == NOTE_C) note = NOTE_Cs; else if (note == NOTE_G) note = NOTE_Gs; } else if (KeySign == 11) { if (note == NOTE_F) note = NOTE_Fs; else if (note == NOTE_C) note = NOTE_Cs; else if (note == NOTE_G) note = NOTE_Gs; else if (note == NOTE_D) note = NOTE_Eb; } else if (KeySign == 12) { if (note == NOTE_F) note = NOTE_Fs; else if (note == NOTE_C) note = NOTE_Cs; else if (note == NOTE_G) note = NOTE_Gs; else if (note == NOTE_D) note = NOTE_Eb; else if (note == NOTE_A) note = NOTE_Bb; } else if (KeySign == 13) { if (note == NOTE_F) note = NOTE_Fs; else if (note == NOTE_C) note = NOTE_Cs; else if (note == NOTE_G) note = NOTE_Gs; else if (note == NOTE_D) note = NOTE_Eb; else if (note == NOTE_A) note = NOTE_Bb; else if (note == NOTE_E) note = NOTE_F; } else if (KeySign == 14) { if (note == NOTE_F) note = NOTE_Fs; else if (note == NOTE_C) note = NOTE_Cs; else if (note == NOTE_G) note = NOTE_Gs; else if (note == NOTE_D) note = NOTE_Eb; else if (note == NOTE_A) note = NOTE_Bb; else if (note == NOTE_E) note = NOTE_F; else if (note == NOTE_B) { note = NOTE_C; octaveTemp += 1; } } } else { // 1. natural if (pgm_read_byte(&localKey[noteNum]) == 2) { // 2. b if (note == NOTE_C) { note = NOTE_B; octaveTemp -= 1; } else if (note == NOTE_D) note = NOTE_Cs; else if (note == NOTE_E) note = NOTE_Eb; else if (note == NOTE_F) note = NOTE_E; else if (note == NOTE_G) note = NOTE_Fs; else if (note == NOTE_A) note = NOTE_Gs; else if (note == NOTE_B) note = NOTE_Bb; } else if (pgm_read_byte(&localKey[noteNum]) == 3) { // 2. # if (note == NOTE_C) note = NOTE_Cs; else if (note == NOTE_D) note = NOTE_Eb; else if (note == NOTE_E) note = NOTE_F; else if (note == NOTE_F) note = NOTE_Fs; else if (note == NOTE_G) note = NOTE_Gs; else if (note == NOTE_A) note = NOTE_Bb; else if (note == NOTE_B) { note = NOTE_C; octaveTemp += 1; } } else if (pgm_read_byte(&localKey[noteNum]) == 4) { // 4. bb if (note == NOTE_C) { note = NOTE_Bb; octaveTemp -= 1; } else if (note == NOTE_D) note = NOTE_C; else if (note == NOTE_E) note = NOTE_D; else if (note == NOTE_F) note = NOTE_Eb; else if (note == NOTE_G) note = NOTE_F; else if (note == NOTE_A) note = NOTE_G; else if (note == NOTE_B) note = NOTE_A; } else if (pgm_read_byte(&localKey[noteNum]) == 5) { // 5. ## if (note == NOTE_C) note = NOTE_D; else if (note == NOTE_D) note = NOTE_E; else if (note == NOTE_E) note = NOTE_Fs; else if (note == NOTE_F) note = NOTE_G; else if (note == NOTE_G) note = NOTE_A; else if (note == NOTE_A) note = NOTE_B; else if (note == NOTE_B) { note = NOTE_Cs; octaveTemp += 1; } } } return note; } // ---------------------------------------------------------------------- int beepPin = 3; bool beepMelody = false; unsigned long int beepTime = 0; //========================================================================================================== void setup() { // 초기화 //Serial.begin(9600);//xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx //lcd 초기화 lcd.init(); lcd.backlight(); lcd.setCursor(0,0); Rtc.Begin(); //Rtc.GetDateTime(); //RTC 모듈 최초 실행 시 현재 시간을 세팅해 주어야 함. 이후에는 아래 line 주석 처리 //RtcDateTime compiled = RtcDateTime(2024, 11, 23, 13, 27, 00); pinMode(BUTTON1, INPUT_PULLUP); pinMode(BUTTON2, INPUT_PULLUP); if (Rtc.GetIsWriteProtected()) { //KHS: RTC 모듈 쓰기 금지 확인 후 해제 lcd.println("RTC protected!"); Rtc.SetIsWriteProtected(false); } if (!Rtc.IsDateTimeValid()) { // Common Causes: // 1) first time you ran and the device wasn't running yet // 2) the battery on the device is low or even missing lcd.println("Lost DateTime!"); //Rtc.SetDateTime(compiled); } if (!Rtc.GetIsRunning()) { //KHS: RTC 모듈이 동작중이 아니라면 구동 lcd.println("RTC activated!"); Rtc.SetIsRunning(true); } pinMode(M_IN1, OUTPUT); // Digital4번핀 출력 핀 설정 pinMode(M_IN2, OUTPUT); // Digital5번핀 출력 핀 설정 pinMode(M_IN3, OUTPUT); // Digital13번핀 출력 핀 설정 pinMode(M_IN4, OUTPUT); // Digital12번핀 출력 핀 설정 clThread.onRun(currentTime); clThread.setInterval(1000); } void loop() { // 무한루프 //if(myThread.shouldRun()) clThread.run(); buttonState1 = digitalRead(BUTTON1); if(buttonState1 == HIGH) // { if(state1 == 0) { state1 = 1; } } else if(buttonState1 == LOW) //스위치 누름 상태 { if(state1 == 1) { if(beepMelody == 1) { if(alm_H == 0)alm_H = 23; else alm_H -= 1; stopAlarm(); } else { alm_H += 1; if(alm_H == 24) { alm_H = 0; } b_almState = 1; } alm = RtcDateTime(2024, 01, 01, alm_H, alm_M, alm_S); state1 = 0; delay(100); } } buttonState2 = digitalRead(BUTTON2); if(buttonState2 == HIGH) { if(state2 == 0) { state2 = 1; } } else if(buttonState2 == LOW) { if(state2 == 1) { if(beepMelody == 1) { if(alm_H == 0)alm_H = 23; else alm_H -= 1; stopAlarm(); } else { alm_M += 5; if(alm_M == 60) { alm_H += 1; alm_M = 0; } b_almState = 1; } alm = RtcDateTime(2024, 01, 01, alm_H, alm_M, alm_S); state2 = 0; delay(100); } } if(state1 == 0 && state2 == 0)b_almState = 0; if(b_almState == 0)lcdprintDateTime(2,alm); else lcdprintDateTime(1,alm); RtcDateTime ctm = Rtc.GetDateTime(); int Delt_H = abs(ctm.Hour() - alm.Hour()); int Delt_M = abs(ctm.Minute() - alm.Minute()); int Delt_S = abs(ctm.Second() - alm.Second()); if(Delt_H == 0 && Delt_M == 0 && Delt_S < 59) { beepMelody = 1; if (millis() - beepTime >= pgm_read_byte(&Mdur[noteNum])*(1000/(4*tempoRatio))) { beepTime = millis(); noTone(beepPin); octaveTemp = pgm_read_byte(&Moct[noteNum]); note_t KeyNote = keySignature(pgm_read_word(&Mnote[noteNum])); tone(beepPin, ledcWriteNote(KeyNote, octaveTemp)); // ledcWriteNote(uint8_t channel, note_t note, uint8_t octaveTemp); noteNum++; if (noteNum == sizeof(Mdur)) { // 초기화 noTone(beepPin); noteNum = 0; beepMelody = false; } } if(millis() - millTime >= 2500) { Flag_FWDBWD = random(0,2); Flag_DIRECT = random(0,3); millTime = millis(); } moveVehicle(); } if(!beepMelody)stopAlarm(); } void currentTime() { RtcDateTime now = Rtc.GetDateTime(); lcdprintDateTime(0,now); if (!now.IsValid()) { // Common Causes: // 1) the battery on the device is low or even missing and the power line was disconnected lcd.println("Lost DateTime!"); } } void lcdprintDateTime(int iPos, const RtcDateTime &dt) { char timeString[20] = {0,}; if(iPos == 0) { snprintf_P(timeString,countof(timeString),PSTR("%02u/%02u %02u:%02u:%02u") ,dt.Month(),dt.Day(),dt.Hour(),dt.Minute(),dt.Second()); lcd.setCursor(0,0); lcd.print(timeString); } else if(iPos == 1) { snprintf_P(timeString,countof(timeString),PSTR("ALARM %02u:%02u:%02u") ,dt.Hour(),dt.Minute(),dt.Second()); lcd.setCursor(0,1); lcd.print(timeString); } else { snprintf_P(timeString,countof(timeString),PSTR("NO ALARM ")); lcd.setCursor(0,1); lcd.print(timeString); } } void moveVehicle() { if(Flag_FWDBWD == 0) { if(millis() - millTime < 500) { if(Flag_DIRECT == 0) //직진 { // 모터 A 정회전 digitalWrite(M_IN1,1); // IN1번에 HIGH(motorA_vector가 0이면 LOW) digitalWrite(M_IN2,0); // IN2번에 LOW(motorA_vector가 0이면 HIGH) // 모터 B 정회전 digitalWrite(M_IN3,0); // IN3번에 HIGH(motorB_vector가 0이면 LOW) digitalWrite(M_IN4,1); // IN4번에 LOW(motorB_vector가 0이면 HIGH) } else if(Flag_DIRECT == 1) //죄회전 { // 모터 A 역회전 digitalWrite(M_IN1,0); // IN1번에 HIGH(motorA_vector가 0이면 LOW) digitalWrite(M_IN2,1); // IN2번에 LOW(motorA_vector가 0이면 HIGH) // 모터 B 정회전 digitalWrite(M_IN3,0); // IN3번에 HIGH(motorB_vector가 0이면 LOW) digitalWrite(M_IN4,1); // IN4번에 LOW(motorB_vector가 0이면 HIGH) } else if(Flag_DIRECT == 2) //우회전 { // 모터 A 정회전 digitalWrite(M_IN1,1); // IN1번에 HIGH(motorA_vector가 0이면 LOW) digitalWrite(M_IN2,0); // IN2번에 LOW(motorA_vector가 0이면 HIGH) // 모터 B 역회전 digitalWrite(M_IN3,1); // IN3번에 HIGH(motorB_vector가 0이면 LOW) digitalWrite(M_IN4,0); // IN4번에 LOW(motorB_vector가 0이면 HIGH) } } else { // 모터 A 정회전 digitalWrite(M_IN1,1); // IN1번에 HIGH(motorA_vector가 0이면 LOW) digitalWrite(M_IN2,0); // IN2번에 LOW(motorA_vector가 0이면 HIGH) // 모터 B 정회전 digitalWrite(M_IN3,0); // IN3번에 HIGH(motorB_vector가 0이면 LOW) digitalWrite(M_IN4,1); // IN4번에 LOW(motorB_vector가 0이면 HIGH) } } else { if(millis() - millTime < 500) { if(Flag_DIRECT == 0) //직진 { // 모터 A 역회전 digitalWrite(M_IN1,0); // IN1번에 HIGH(motorA_vector가 0이면 LOW) digitalWrite(M_IN2,1); // IN2번에 LOW(motorA_vector가 0이면 HIGH) // 모터 B 역회전 digitalWrite(M_IN3,1); // IN3번에 HIGH(motorB_vector가 0이면 LOW) digitalWrite(M_IN4,0); // IN4번에 LOW(motorB_vector가 0이면 HIGH) } else if(Flag_DIRECT == 1) //죄회전 { // 모터 A 정회전 digitalWrite(M_IN1,1); // IN1번에 HIGH(motorA_vector가 0이면 LOW) digitalWrite(M_IN2,0); // IN2번에 LOW(motorA_vector가 0이면 HIGH) // 모터 B 역회전 digitalWrite(M_IN3,1); // IN3번에 HIGH(motorB_vector가 0이면 LOW) digitalWrite(M_IN4,0); // IN4번에 LOW(motorB_vector가 0이면 HIGH) } else if(Flag_DIRECT == 2) //우회전 { // 모터 A 역회전 digitalWrite(M_IN1,0); // IN1번에 HIGH(motorA_vector가 0이면 LOW) digitalWrite(M_IN2,1); // IN2번에 LOW(motorA_vector가 0이면 HIGH) // 모터 B 정회전 digitalWrite(M_IN3,0); // IN3번에 HIGH(motorB_vector가 0이면 LOW) digitalWrite(M_IN4,1); // IN4번에 LOW(motorB_vector가 0이면 HIGH) } } else { // 모터 A 역회전 digitalWrite(M_IN1,0); // IN1번에 HIGH(motorA_vector가 0이면 LOW) digitalWrite(M_IN2,1); // IN2번에 LOW(motorA_vector가 0이면 HIGH) // 모터 B 역회전 digitalWrite(M_IN3,1); // IN3번에 HIGH(motorB_vector가 0이면 LOW) digitalWrite(M_IN4,0); // IN4번에 LOW(motorB_vector가 0이면 HIGH) } } } void stopAlarm() { beepMelody = 0; noTone(beepPin); // 모터 A 정지 digitalWrite(M_IN1,0); // IN1번에 HIGH(motorA_vector가 0이면 LOW) digitalWrite(M_IN2,0); // IN2번에 LOW(motorA_vector가 0이면 HIGH) // 모터 B 정지 digitalWrite(M_IN3,0); // IN3번에 HIGH(motorB_vector가 0이면 LOW) digitalWrite(M_IN4,0); // IN4번에 LOW(motorB_vector가 0이면 HIGH) }
트러블 슈팅 과정
업데이트 예정