산업 제조
산업용 사물 인터넷 | 산업자재 | 장비 유지 보수 및 수리 | 산업 프로그래밍 |
home  MfgRobots >> 산업 제조 >  >> Manufacturing Technology >> 제조공정

7-세그먼트 어레이 시계

구성품 및 소모품

SparkFun Arduino Pro Mini 328 - 5V/16MHz
× 1
실시간 시계(RTC)
"Arduino용 DS1302 실시간 클록 모듈"을 구입하고 IC 소켓과 수정을 납땜 해제하십시오.
× 1
CR1220 배터리 및 홀더
"5pcs, 블랙 하우징 CR1220 CR1225 버튼 셀 배터리 소켓 홀더 BS-1220-1" 검색
× 1
커패시터 0.1uf 1206 SMD
× 1
커패시터 10uF 1206 SMD 세라믹
× 1
촉각 스위치, 상단 작동
버튼 상단이 있는 12mm 변형
× 4
3A 미니 DC-DC 강압 컨버터 모듈
× 1
0.28in 공통 음극 4자리 7 세그먼트 디스플레이
× 36
MAX7219 DIP IC
× 18
부저
× 1
머신 헤더
남성과 여성 헤더가 모두 필요합니다. 일반적으로 40개의 스트립으로 제공되므로 남성의 8개의 스트립이 필요합니다. 가공 핀 및 8개의 암 가공 소켓 스트립
× 1
DC 전원 소켓
"12V DC 전원 공급 장치 잭 소켓 암 패널 장착 커넥터 5.5 x 2.ODZ2" 검색
× 1

필요한 도구 및 기계

납땜 인두(일반)
3D 프린터(일반)

앱 및 온라인 서비스

Arduino IDE

이 프로젝트 정보

Hackaday.io를 탐색하는 동안 Frugha의 7-세그먼트 디스플레이 배열로 만든 아름다운 시계를 발견했습니다. 정말 만들고 싶었지만 Frugha의 버전은 0.56" 7-세그먼트 디스플레이를 사용하기 때문에 상당히 큽니다. 제 디자인은 0.28" 7-세그먼트 디스플레이를 사용하여 크기의 4분의 1에 불과합니다.

알람 시계와 시간, 날짜 및 알람을 설정하는 구성 화면을 통합하도록 소프트웨어가 다시 작성되었습니다.

동영상

구성 화면

시계는 4개의 버튼으로 제어됩니다. SELECT 버튼(맨 위)은 구성 화면을 표시합니다. 현재 선택 항목이 깜박입니다. 선택 버튼을 다시 누르면 다른 구성 섹션(즉, 시간, 날짜, 알람, 조정, 밝기, 날짜 형식 및 시계)이 순환됩니다.

메뉴 항목 중 하나가 선택된 상태에서 ENTER 버튼(위에서 아래로 두 번째)을 누르면 설정을 변경할 수 있습니다. 메뉴 항목에 대해 둘 이상의 설정이 있는 경우 ENTER 버튼이 설정 사이를 순환합니다. 활성 설정이 깜박입니다.

설정이 깜박이면 UP 및 DOWN 버튼(하단 두 개의 버튼)이 설정을 변경합니다.

날짜 화면

시계가 표시된 상태에서 ENTER 버튼(위에서 아래로 두 번째)을 누르면 날짜 화면이 5초간 표시됩니다.

구성 화면의 날짜 형식 섹션에서 날짜 형식(DD-MM 또는 MM-DD)과 사용된 글꼴을 설정할 수 있습니다.

디자인 고려 사항

보드 디자인을 간단히 하자면, 제 버전은 원래 버전에서 사용된 한 자리 숫자가 아닌 4자리 디스플레이를 사용합니다. 0.28" 디스플레이가 작기 때문에 MAX7219 칩을 별도의 보드에 올려야 했습니다. 간단히 배선하려면 기계가공된 암수 핀 헤더를 사용하여 보드를 연결하고,

보드를 상업적으로 만들고 싶거나 내가 한 것처럼 직접 만들고 싶다면 Eagle 파일이 포함되어 있습니다. 저는 토너법을 이용해서 만들었습니다.

칼럼 보드

모든 디스플레이를 수용하는 6개의 기둥 보드를 만들어야 합니다. 수 기계 헤더를 납땜할 때 중간 세트에서 시작하여 바깥쪽 가장자리까지 작업합니다. 성냥개비 또는 이와 유사한 것을 사용하여 소켓을 충분히 들어 올려 핀을 쉽게 납땜할 수 있습니다. 끝이 가는 작은 인두와 0.5mm 땜납을 사용하십시오.

열 모듈을 만든 후에는 무광 검정 페인트로 표시 테두리를 칠하는 것이 좋습니다. 이렇게 하면 디스플레이가 인접 항목과 완벽하게 정렬되지 않은 경우 흰색 가장자리가 표시되지 않습니다. 또한 마커 펜을 사용하여 각 열 모듈에 번호를 지정합니다. 이것은 MAX7219 마더보드에 삽입될 때 도움이 됩니다.

MAX7219 마더보드

마더보드와 컬럼 보드를 설계할 때 올바른 MAX7219 세그먼트 또는 숫자를 해당 디스플레이 핀에 매핑하지 않았습니다. PCB 레이아웃을 최대한 단순하게 유지하고 소프트웨어의 차이에 맞게 조정하고 싶었습니다.

암 가공 소켓의 경우 먼저 컬럼 보드의 수 핀에 배치한 다음 연결되어 있는 동안 어셈블리를 제자리에 납땜하는 것이 좋습니다. 이것은 열 핀이 해당 소켓과 정확히 정렬됨을 의미합니다. 제거해야 하는 경우 열 모듈에 번호를 지정하면 동일한 위치로 돌아갈 수 있습니다.

MPU 보드

마이크로프로세서 보드에는 다른 모든 구성 요소가 포함되어 있습니다. 4개의 버튼과 2개의 SMD 커패시터는 보드 후면에 장착되고 다른 모든 구성 요소는 보드 전면에 장착됩니다. 로우 프로파일을 유지하기 위해 일반적으로 Arduino Pro Mini에 납땜되는 FTDI 핀 헤더가 이제 PCB에 직접 납땜됩니다. 두 개의 와이어는 DTR 및 VCC 핀에서 PCB로 직접 연결됩니다.

사례 만들기

앞면과 뒷면에 대한 STL 파일이 포함되어 있습니다. 둘 다 0.2 레이어 높이와 테두리를 사용하여 인쇄되었습니다. 전면에는 제작판에만 닿는 지지대가 활성화되어 있습니다.

컬럼 모듈이 있는 MAX7219 마더보드는 전면에서 밀어 넣습니다. 조금 헐렁한 느낌이 든다면 마스킹 테이프를 한두 겹 더 발라주세요.

MPU 보드는 뒷면에 뜨겁게 접착되어 있습니다.

소프트웨어

소프트웨어를 사용하려면 Arduino IDE에 MD_MAX72XX 라이브러리가 설치되어 있어야 합니다. 테스트를 위해 Hardware_Test_V1 스케치를 제공했습니다. 단락이나 끊어진 배선을 확인할 수 있도록 모든 세그먼트에 불이 들어옵니다. 디스플레이가 작동하면 Clock_V4 스케치를 업로드하세요.

<섹션 클래스="섹션 컨테이너 섹션 축소 가능" id="코드">

코드

<울>
  • Clock_V4.ino
  • 숫자.h
  • Tunes.h
  • 버튼.h
  • Button.cpp
  • Hardware_Test_V1.zip
  • Clock_V4.inoC/C++
    <사전>/*--------------------------------------------- ------ * 7 Segment array clock * * Frugha의 시계 기반(https://hackaday.io/project/169632-7-segment-display-array-clock) * * John Bradnam의 수정(jbrad2089@ gmail.com) * - 하드웨어가 0.28" 7 세그먼트 디스플레이를 사용하도록 변경되었습니다. * - MAX7219 디바이스, 디지트 및 세그먼트 순서가 다른 경우에는 * 복잡한 PCB 보드 라우팅으로 변경되었습니다. * - 하드웨어는 Arduino Mini Pro, DS1302 RTC 및 4를 사용합니다. 푸시 버튼 * - 선택 가능한 알람 기능이 있는 알람 기능 추가 * - 밝기 설정 추가 * - 시간, 날짜, 알람, 음악 및 밝기를 설정하는 설정 화면 추가 * - 날짜 화면 추가(ENTER 버튼을 누르면 2초간 표시 * 업데이트 2020 -06-16 * - 구성 화면에 날짜 형식 옵션 추가 * - 날짜 화면 시간 초과를 2초에서 5초로 증가 * - 날짜 표시를 위한 구성 가능한 글꼴 선택 추가 *-------------- ------------------------------------*/ #include #include #포함 # include #include #include "button.h#include "Tunes.h#include "Digits.h#define HARDWARE_TYPE MD_MAX72XX::GENERIC_HW#define MAX_DEVICES 18#define LED_CLK 13 / / 또는 SCK(WHI)#define LED_DATA 11 // 또는 MOSI(BRN)#define LED_CS 10 // 또는 SS(YEL)#define SPEAKER 2#define SW_UP 3#define SW_DOWN 4#define RTC_CLK 5#define RTC_IO 6#define SW_ENTER 7#define RTC_CE 8#define SW_SELECT 9DS1302RTC rtc(RTC_CE, RTC_IO, RTC_CLK);// SPI 하드웨어 인터페이스MD_MAX72XX mx =MD_MAX72XX 핀 =MD_MAX72XX 핀(MAX_TYPE, LED_CS, MAX_DEVICES);// Ar LED_CLK, LED_CS, MAX_DEVICES);//EEPROM 처리#define EEPROM_ADDRESS 0#define EEPROM_MAGIC 0x0BAD0DADtypedef struct { uint32_t magic; 부울 알람; uint8_t분; uint8_t시간; 부울 형식12시간; uint8_t 밝기; uint8_t 조정; 부울 형식Dmy; bool squareFont;} EEPROM_DATA;EEPROM_DATA EepromData; //현재 EEPROM 설정 무효 clockButtonPressed(void);void enterButtonPressed(void);void downButtonPressed(void);void upButtonPressed(void);Button* clockButton;Button* enterButton;Button* downButton;Button* upButton;//동작 모드bool inSubMenu =false;#define SETUP_FLASH_RATE 200;부호 없는 긴 setupTimeout;bool setupDisplayState =false;enum ClockButtonModesEnum { CLOCK, TIME_SET, DATE_SET, ALARM_SET, TUNE_SET, BRIGHT_SET, TUNE_SET, BRIGHT_SET, FORMAT_SET };ClockButtonModesEnum clockMode =ClockButtonModesEnum timeSetMode =TIME_HOUR; ENUM DateSetMenuEnum {DATE_YEAR, DATE_MONTH, DATE_DAY} DateSetMenuEnum dateSetMode =DATE_YEAR; ENUM AlarmSetMenuEnum {ALARM_HOUR, ALARM_MIN, ALARM_STATE} AlarmSetMenuEnum alarmSetMode =ALARM_HOUR; ENUM FormatSetMenuEnum {DAY_MONTH, FONT_STYLE} FormatSetMenuEnum formatSetMode =DAY_MONTH; INT lastSeconds =-1;bool alarmRinging =거짓; //알람이 onbool일 때 참입니다. alarmCancelled =false; // userbool에 의해 알람이 취소됨 musicPlaying =false; // songbool을 재생하는 경우 true clockColon =false; //colonint8_t 표시/숨기기 dom[] ={31,28,31,30,31,30,31,31,30,31,30,31};tmElements_t newTime; //새로운 timevoid를 저장하는 데 사용 setup(){ Serial.begin(115200); //Eprom readEepromData(); //버튼 초기화 clockButton =new Button(SW_SELECT); enterButton =새 버튼(SW_ENTER); downButton =새 버튼(SW_DOWN); downButton->반복(downButtonPressed); upButton =새로운 버튼(SW_UP); upButton->반복(upButtonPressed); //사운드 초기화 pinMode(SPEAKER,OUTPUT); mx.begin(); mx.control(MD_MAX72XX::INTENSITY, EepromData.brightness); //0..MAX_INTENSITY mx.update(MD_MAX72XX::OFF); //자동 업데이트 없음 setSyncProvider(rtc.get); // RTC에서 시간을 가져오는 함수 if(timeStatus() !=timeSet) { Serial.println("RTC Sync Bad"); newTime.Year =CalendarYrToTm(2020); newTime.Month =5; newTime.Day =30; newTime.Hour =14; newTime.Minute =53; newTime.Second =0; time_t t =makeTime(newTime); 세트타임(t); rtc.set(t); if (rtc.set(t) !=0) { Serial.println("시간 설정 실패"); } } newTime.Year =달력YrToTm(연도()); newTime.Month =월(); newTime.Day =일(); newTime.Hour =시간(); newTime.Minute =분(); newTime.Second =초(); Serial.println("시간:" + String(newTime.Hour) + ":" + String(newTime.Minute) + ":" + String(newTime.Second)); Serial.println("날짜:" + String(newTime.Day) + "/" + String(newTime.Month) + "/" + String(tmYearToCalendar(newTime.Year))); 시계 모드 =시계; showTime(true);} 무효 루프(){ testButtons(); if (clockMode ==CLOCK) { 쇼타임(거짓); if (EepromData.alarm &&EepromData.hours ==hour() &&EepromData.minutes ==min()) { if (!alarmCancelled) { alarmRinging =true; playSong(멜로디[EepromData.tune]); } } else { 알람 취소 =거짓; 알람 울림 =거짓; } } else { showSetup(거짓); } 지연(100);}//----------------------------------------- ----------------------//버튼이 눌러졌는지 테스트void testButtons(){ //버튼 한 번 누르기 if (clockButton->Pressed()) { clockButtonPressed(); } if (enterButton->Pressed()) { enterButtonPressed(); } //버튼 핸들러가 반복 기능을 호출하기 때문에 눌린 결과를 확인할 필요가 없습니다. upButton->Pressed(); downButton->Pressed();}//---------------------------------------- -----------------------//CLOCK 처리 bttonvoid clockButtonPressed(){ if (cancelAlarm()) return; inSubMenu =거짓; clockMode =(clockMode ==FORMAT_SET) ? CLOCK :(ClockButtonModesEnum)((int)clockMode + 1); if (clockMode ==CLOCK) { Serial.println("시간 절약:" + String(newTime.Hour) + ":" + String(newTime.Minute)); Serial.println(" 보낸 사람:" + String(hour()) + ":" + String(분())); if (newTime.Year !=CalendarYrToTm(year()) || newTime.Month !=month() || newTime.Day !=day() || newTime.Hour !=hour() || newTime.Minute !=min()) { //시간 업데이트 Serial.println("RTC 업데이트 중"); newTime.Second =초(); time_t t =makeTime(newTime); 세트타임(t); rtc.set(t); if (rtc.set(t) !=0) { Serial.println("시간 설정 실패"); } } 쓰기이프롬데이터(); 쇼타임(참); } else { if (clockMode ==TIME_SET) { newTime.Year =CalendarYrToTm(year()); newTime.Month =월(); newTime.Day =일(); newTime.Hour =시간(); newTime.Minute =분(); newTime.Second =초(); Serial.println("로드 시간:" + String(hour()) + ":" + String(분())); } showSetup(참); }}//---------------------------------------------- ------------------//Enter bttonvoid enterButtonPressed(){ if (cancelAlarm()) return; if (clockMode !=CLOCK) { if (!inSubMenu) { timeSetMode =TIME_HOUR; 날짜 설정 모드 =DATE_YEAR; 알람 설정 모드 =ALARM_HOUR; 형식 설정 모드 =DAY_MONTH; inSubMenu =참; } else { 스위치(clockMode) { 경우 TIME_SET:timeSetMode =(timeSetMode ==TIME_FORMAT) ? TIME_HOUR :(TimeSetMenuEnum)((int)timeSetMode + 1); 부서지다; 케이스 DATE_SET:dateSetMode =(dateSetMode ==DATE_DAY) ? DATE_YEAR :(DateSetMenuEnum)((int)dateSetMode + 1); 부서지다; 경우 ALARM_SET:alarmSetMode =(alarmSetMode ==ALARM_STATE) ? ALARM_HOUR :(AlarmSetMenuEnum)((int)alarmSetMode + 1); 부서지다; 경우 FORMAT_SET:formatSetMode =(formatSetMode ==FONT_STYLE) ? DAY_MONTH :(FormatSetMenuEnum)((int)formatSetMode + 1); 부서지다; } } showSetup(참); } else { showDate(일(), 월(), 연도()); 지연(5000); }}//---------------------------------------------- ------------------//다운 핸들 bttonvoid downButtonPressed(){ if (cancelAlarm()) return; switch (clockMode) { 경우 TIME_SET:if (inSubMenu) { switch(timeSetMode) { 경우 TIME_HOUR:newTime.Hour =(newTime.Hour + 24 - 1) % 24; 부서지다; 경우 TIME_MIN:newTime.Minute =(newTime.Minute + 60 - 1) % 60; 부서지다; 케이스 TIME_FORMAT:EepromData.format12hr =!EepromData.format12hr; 부서지다; } showSetup(참); } 부서지다; 케이스 DATE_SET:if (inSubMenu) { switch(dateSetMode) { 케이스 DATE_YEAR:newTime.Year =((newTime.Year - 30 + 100) - 1) % 100 + 30; 부서지다; 케이스 DATE_MONTH:newTime.Month =((newTime.Month - 1 + 12) - 1) % 12 + 1; 부서지다; 케이스 DATE_DAY:uint8_t md =daysInMonth(newTime.Year, newTime.Month); newTime.Day =((newTime.Day - 1 + md) - 1) % md + 1; 부서지다; } showSetup(참); } 부서지다; case ALARM_SET:if (inSubMenu) { switch(alarmSetMode) { case ALARM_HOUR:EepromData.hours =(EepromData.hours + 24 - 1) % 24; 부서지다; 경우 ALARM_MIN:EepromData.minutes =(EepromData.minutes + 60 - 1) % 60; 부서지다; 경우 ALARM_STATE:EepromData.alarm =!EepromData.alarm; 부서지다; } showSetup(참); } 부서지다; 경우 TUNE_SET:EepromData.tune =(EepromData.tune + NUM_OF_MELODIES - 1) % NUM_OF_MELODIES; showSetup(참); 부서지다; 경우 BRIGHT_SET:EepromData.brightness =(EepromData.brightness + MAX_INTENSITY - 1) % MAX_INTENSITY; mx.control(MD_MAX72XX::INTENSITY, EepromData.brightness); //0..MAX_INTENSITY showSetup(true); 부서지다; case FORMAT_SET:if (inSubMenu) { switch (formatSetMode) { case DAY_MONTH:EepromData.formatDmy =!EepromData.formatDmy; 부서지다; 케이스 FONT_STYLE:EepromData.squareFont =!EepromData.squareFont; 부서지다; } } showSetup(참); 부서지다; }}//---------------------------------------------- ------------------//UP bttonvoid upButtonPressed(){ if (cancelAlarm()) return; switch (clockMode) { 경우 TIME_SET:if (inSubMenu) { switch(timeSetMode) { 경우 TIME_HOUR:newTime.Hour =(newTime.Hour + 1) % 24; 부서지다; 경우 TIME_MIN:newTime.Minute =(newTime.Minute + 1) % 60; 부서지다; 케이스 TIME_FORMAT:EepromData.format12hr =!EepromData.format12hr; 부서지다; } showSetup(참); } 부서지다; 케이스 DATE_SET:if (inSubMenu) { switch(dateSetMode) { 케이스 DATE_YEAR:newTime.Year =((newTime.Year - 30) + 1) % 100 + 30; 부서지다; 케이스 DATE_MONTH:newTime.Month =((newTime.Month - 1) + 1) % 12 + 1; 부서지다; 케이스 DATE_DAY:uint8_t md =daysInMonth(newTime.Year, newTime.Month); newTime.Day =(newTime.Day % md) + 1; 부서지다; } showSetup(참); } 부서지다; case ALARM_SET:if (inSubMenu) { switch(alarmSetMode) { case ALARM_HOUR:EepromData.hours =(EepromData.hours + 1) % 24; 부서지다; 경우 ALARM_MIN:EepromData.minutes =(EepromData.minutes + 1) % 60; 부서지다; 경우 ALARM_STATE:EepromData.alarm =!EepromData.alarm; 부서지다; } showSetup(참); } 부서지다; 사례 TUNE_SET:EepromData.tune =(EepromData.tune + 1) % NUM_OF_MELODIES; showSetup(참); 부서지다; 경우 BRIGHT_SET:EepromData.brightness =(EepromData.brightness + 1) % MAX_INTENSITY; mx.control(MD_MAX72XX::INTENSITY, EepromData.brightness); //0..MAX_INTENSITY showSetup(true); 부서지다; case FORMAT_SET:if (inSubMenu) { switch (formatSetMode) { case DAY_MONTH:EepromData.formatDmy =!EepromData.formatDmy; 부서지다; 케이스 FONT_STYLE:EepromData.squareFont =!EepromData.squareFont; 부서지다; } } showSetup(참); 부서지다; }}//---------------------------------------------- ------------------//튠불을 재생하는 경우 알람을 끕니다. cancelAlarm(){ if (musicPlaying) { musicPlaying =false; 알람 취소됨 =알람 울리는 중; true를 반환합니다. } 생산하다(); return false;}//-------------------------------------------- -------------------//설정 메뉴 표시 및 현재 항목 플래시 selectedvoid showSetup(bool force){ setupDisplayState =setupDisplayState | 힘; 힘 =힘 || (millis()> setupTimeout); if (강제) { setupTimeout =millis() + SETUP_FLASH_RATE; bool on =setupDisplayState; setupDisplayState =!setupDisplayState; mx.clear(); if (on || !(clockMode ==TIME_SET &&!inSubMenu)) displayString(0,7,"TINE"); if (on || !(clockMode ==TIME_SET &&inSubMenu &&timeSetMode ==TIME_HOUR)) displayNumber(0,13,newTime.Hour,2,true); if (on || !(clockMode ==TIME_SET &&inSubMenu &&timeSetMode ==TIME_MIN)) displayNumber(0,16,newTime.Minute,2,true); if (on || !(clockMode ==TIME_SET &&inSubMenu &&timeSetMode ==TIME_FORMAT)) displayString(0,19,(EepromData.format12hr) ? "12HR" :"24HR"); if (|| !(clockMode ==DATE_SET &&!inSubMenu)) displayString(1,7,"DATE"); if (|| !(clockMode ==DATE_SET &&inSubMenu &&dateSetMode ==DATE_YEAR)) displayNumber(1,13,tmYearToCalendar(newTime.Year),4,true); if (on || !(clockMode ==DATE_SET &&inSubMenu &&dateSetMode ==DATE_MONTH)) displayNumber(1,18,newTime.Month,2,true); if (|| !(clockMode ==DATE_SET &&inSubMenu &&dateSetMode ==DATE_DAY)) displayNumber(1,21,newTime.Day,2,true); if (on || !(clockMode ==ALARM_SET &&!inSubMenu)) displayString(2,6,"ALARN"); if (on || !(clockMode ==ALARM_SET &&inSubMenu &&alarmSetMode ==ALARM_HOUR)) displayNumber(2,13,EepromData.hours,2,true); if (on || !(clockMode ==ALARM_SET &&inSubMenu &&alarmSetMode ==ALARM_MIN)) displayNumber(2,16,EepromData.minutes,2,true); if (on || !(clockMode ==ALARM_SET &&inSubMenu &&alarmSetMode ==ALARM_STATE)) displayString(2,19,(EepromData.alarm) ? "ON" :"OFF"); if (on || !(clockMode ==TUNE_SET &&!inSubMenu)) displayString(3,7,"TUNE"); if (on || !(clockMode ==TUNE_SET &&inSubMenu)) { switch(EepromData.tune) { 경우 0:displayString(3,13,"ELISE"); 부서지다; 사례 1:displayString(3,13,"RANGE"); 부서지다; 사례 2:displayString(3,13,"SUNSHINE"); 부서지다; } } if (|| !(clockMode ==BRIGHT_SET &&!inSubMenu)) displayString(4,1,"밝기"); if (on || !(clockMode ==BRIGHT_SET &&inSubMenu)) displayNumber(4,13,EepromData.brightness,0,false); if (on || !(clockMode ==FORMAT_SET &&!inSubMenu)) displayString(5,0,"DATE ​​FORNAT"); if (on || !(clockMode ==FORMAT_SET &&inSubMenu &&formatSetMode ==DAY_MONTH)) displayString(5,13,(EepromData.formatDmy) ? "DD-NN" :"NN-DD"); if (on || !(clockMode ==FORMAT_SET &&inSubMenu &&formatSetMode ==FONT_STYLE)) displayString(5,19,(EepromData.squareFont) ? "FONT1" :"FONT2"); mx.update(); }}//---------------------------------------------- ------------------//변경된 경우 디스플레이에 현재 시간 표시// force - 변경되지 않은 경우에도 항상 시간 표시void showTime(bool force){ force =force || (lastSeconds !=초()); if (강제) { lastSeconds =두 번째(); showTime(시간(), 분(), 참, 참, (초() &0x01), (clockMode !=CLOCK || !EepromData.format12hr)); }}//---------------------------------------------- ------------------//디스플레이에 시간 표시// h - hour// m - 분// he - hour enable // me - min enable // ce - 콜론 활성화 무효 showTime(int h, int m, bool he, bool me, bool ce, bool f24){ mx.clear(); if (그) { if (!f24 &&h> 12) { h =h - 12; } if (h> 9 || f24) { displayLargeDigit(0, h / 10); } displayLargeDigit(5, h % 10); } if (나) { displayLargeDigit(13, m / 10); displayLargeDigit(18, m % 10); } if (ce) { displayLargeDigit(11, 10); } mx.update();}//---------------------------------------------------- ----------------------- 무효 showDate(int d, int m, int y){ #define XOFS 3 mx.clear(); if (EepromData.formatDmy) { displayDateDigit(XOFS+0, d / 10); displayDateDigit(XOFS+4, d % 10); displayDateDigit(XOFS+8, 10); //콜론 displayDateDigit(XOFS+12, m / 10); displayDateDigit(XOFS+16, m % 10); } else { displayDateDigit(XOFS+0, m / 10); displayDateDigit(XOFS+4, m % 10); displayDateDigit(XOFS+8, 10); //콜론 displayDateDigit(XOFS+12, d / 10); displayDateDigit(XOFS+16, d % 10); } displayNumber(5, 10, y, 0, 거짓); mx.update();}//----------------------------------------- ----------------------// 구성 설정에 따라 제곱 또는 작은 숫자 쓰기// x =0 ~ 23// v =큰 또는 제곱 숫자에 디스플레이 (0..10) 무효 displayDateDigit(uint8_t x, uint8_t v){ if (EepromData.squareFont) { displaySquareDigit(x, v); } else { displaySmallDigit(x, v); }}//---------------------------------------------- ------------------// 세그먼트 비트 마스크 쓰기// x =0 ~ 23// v =표시할 큰 숫자 (0..10)void displayLargeDigit(uint8_t x , uint8_t v){ if (x <24 &&v <11) { for (uint8_t row =0; row <6; row++) { for (uint8_t col =0; col <6; col++) { writePhysicalDigit(row, col + x, pgm_read_byte(&largeDigits[v][행][열]), 거짓); } } }}//-------------------------------------------- -------------------// 세그먼트 비트 마스크 쓰기// x =0 ~ 23// v =표시할 큰 숫자 (0..10)void displaySmallDigit( uint8_t x, uint8_t v){ if (x <24 &&v <11) { for (uint8_t row =0; row <5; row++) { for (uint8_t col =0; col <3; col++) { writePhysicalDigit(row, 열 + x, pgm_read_byte(&smallDigits[v][행][열]), 거짓); } } }}//-------------------------------------------- -------------------// 세그먼트 비트 마스크 쓰기// x =0 ~ 23// v =표시할 큰 숫자 (0..10)void displaySquareDigit( uint8_t col, uint8_t v){ //세그먼트 순서 defbca _ g uint8_t mask =ascii[v]; if (마스크 ==B00000000) { //하이픈 마스크 =B00000001; } if (mask &B00000100) { //세그 A writePhysicalDigit(0, col + 0, 0xff, false); writePhysicalDigit(0, 열 + 1, 0xff, 거짓); writePhysicalDigit(0, 열 + 2, 0xff, 거짓); } if (마스크 &B00010000) { //세그 B writePhysicalDigit(0, col + 2, 0xff, false); writePhysicalDigit(1, 열 + 2, 0xff, 거짓); writePhysicalDigit(2, 열 + 2, 0xff, 거짓); } if (마스크 &B00001000) { //세그 C writePhysicalDigit(2, col + 2, 0xff, false); writePhysicalDigit(3, 열 + 2, 0xff, 거짓); writePhysicalDigit(4, 열 + 2, 0xff, 거짓); } if (마스크 &B10000000) { //세그 D writePhysicalDigit(4, col + 0, 0xff, false); writePhysicalDigit(4, 열 + 1, 0xff, 거짓); writePhysicalDigit(4, 열 + 2, 0xff, 거짓); } if (마스크 &B01000000) { //세그 C writePhysicalDigit(2, col, 0xff, false); writePhysicalDigit(3, 열, 0xff, 거짓); writePhysicalDigit(4, 열, 0xff, 거짓); } if (마스크 &B00100000) { //세그 E writePhysicalDigit(0, col, 0xff, false); writePhysicalDigit(1, 열, 0xff, 거짓); writePhysicalDigit(2, 열, 0xff, 거짓); } if (마스크 &B00000001) { //세그 D writePhysicalDigit(2, col + 0, 0xff, false); writePhysicalDigit(2, 열 + 1, 0xff, 거짓); writePhysicalDigit(2, 열 + 2, 0xff, 거짓); }}//---------------------------------------------- ------------------// 문자열 쓰기suint8_t displayString(uint8_t row, uint8_t col, String s){ for (int i =0; i 0x5F) { c =0x3F; //공백 문자로 사용 } c =c - 0x30; writePhysicalDigit(행, 열, ascii[c], true); 열 =열 + 1; }}//---------------------------------------------- ------------------// 숫자 쓰기uint8_t displayNumber(uint8_t row, uint8_t col, int number, int padding, boolleadingZeros){ if (padding ==0) { padding =(숫자> 0) ? 바닥(log10(숫자)) + 1 :1; } 열 =열 + 패딩; 먼저 부울 =true; for (int i =0; i <패딩; i++) { col--; if (숫자 !=0 || 첫째) { writePhysicalDigit(행, 열, 아스키[숫자 % 10], 참); 숫자 =숫자 / 10; 첫 번째 =거짓; } else { writePhysicalDigit(행, 열, ascii[(leadingZeros) ? 0 :0x0F], 참); } }}//--------------------------------------------- ------------------// 세그먼트 비트 마스크 쓰기// 행 =0 ~ 5// col =0 ~ 23// v =세그먼트 마스크// 지우기 =true 모든 기존 패턴을 대체하고 false는 기존 무효와 OR OR이 됩니다. 0x01) ? 4:0); uint8_t dev =(열>> 2) * 3 + (행>> 1); uint16_t c =((uint16_t)dev <<3) | 디지트맵[파기]; if (! 지우기) { v =v | mx.getColumn(c); } mx.setColumn(c, v); }}//---------------------------------------------- ------------------//주어진 연도와 월의 날짜를 반환합니다. // 윤년이나 세기가 바뀌지 않는 한 2월은 28입니다.uint8_t daysInMonth(int y, int m) { 반환 dom[m - 1] + ((m ==2 &&(y % 4) ==0 &&(y % 100) !=0) ? 1 :0);}//------- -------------------------------------------------- ------//EepromData 구조를 EEPROMvoid writeEepromData(){ //이 함수는 EEPROM.update()를 사용하여 쓰기를 수행하므로 값이 변경되지 않은 경우 값을 다시 쓰지 않습니다. EEPROM.put(EEPROM_ADDRESS,EepromData);}//----------------------------------------------------- -------------------------//EEPROM에서 EepromData 구조를 읽고 필요한 경우 초기화합니다.void readEepromData(){ //Eprom EEPROM.get(EEPROM_ADDRESS, 이프롬데이터); //Serial.println("magic:" + String(EepromData.magic, 16) + ", 알람:" + String(EepromData.alarm) + ", 시간:" + String(EepromData.hours) + ":" + 문자열(EepromData.minutes) + ", 12시간:" + 문자열(EepromData.format12hr) + ", 밝기:" + 문자열(EepromData.brightness)); if (EepromData.magic !=EEPROM_MAGIC) { Serial.println("EEPROM 초기화 중..."); EepromData.magic =EEPROM_MAGIC; EepromData.alarm =거짓; EepromData.분 =30; EepromData.시간 =5; EepromData.format12hr =거짓; EepromData.brightness =8; EepromData.tune =0; EepromData.formatDmy =거짓; 쓰기이프롬데이터(); } Serial.println("알람:" + 문자열(EepromData.alarm) + ", 시간:" + 문자열(EepromData.hours) + ":" + 문자열(EepromData.minutes) + ", 12시간:" + 문자열(EepromData .format12hr) + ", 밝기:" + String(EepromData.brightness));}//-------------------------------------- ----------------------------------//멜로디 재생int playSong(const uint16_t* 멜로디){ //재생 END_OF_TUNE 음표가 나타날 때까지 멜로디의 각 음표 musicPlaying =true; int thisNote =0; uint16_t noteRaw =pgm_read_word(&멜로디[thisNote++]); 동안 (musicPlaying &¬eRaw !=END_OF_TUNE) { testButtons(); 생산하다(); 플레이노트(noteRaw); noteRaw =pgm_read_word(&멜로디[thisNote++]); } //while delay(50);}//-------------------------------------- -------------------------//Play a single notevoid playNote(uint16_t noteRaw){ // to calculate the note duration, take one second divided by the note type. // e.g. quarter note =1000 / 4, eighth note =1000/8, etc. uint16_t frequency =noteRaw &0x1FFF; uint16_t duration =(noteRaw &0xE000)>> 13; if (duration ==7) duration =8; uint16_t noteDuration =1800 / duration; if (frequency !=REST) { tone(SPEAKER, frequency, noteDuration); } // to distinguish the notes, set a minimum time between them. // the note's duration + 30% seems to work well:uint16_t pauseBetweenNotes =(noteDuration * 13) / 10; delay(pauseBetweenNotes); if (frequency !=REST) { // stop the tone playing:noTone(SPEAKER); }}
    Digits.hC/C++
    /*--------------------------------------------------- * 7 Segment array clock * define each digit in a 6x6 byte array *--------------------------------------------------*/ #pragma once//---------------------------------------------------// Standard MAX7219 wiring// Digit order 0,1,2,3,4,5/*// Segment order _ a b c d e f g#define _______ B00000000#define __cd___ B00011000#define _bcde_g B00111101#define abcdefg B01111111#define __cdefg B00011111#define __c____ B00010000#define ab__efg B01100111#define abcd_fg B01111011#define abcde_g B01111101#define ab_defg B01101111#define _bc____ B00110000#define a__defg B01001111#define ____ef_ B00000110#define ___defg B00001111#define abc__fg B01110011#define a___ef_ B01000110#define _b_____ B00100000#define ab___f_ B01100010#define _bcd___ B00111000#define a___ef_ B01000110#define ab_____ B01100000#define ____e__ B00000100#define __cde_g B00011101#define a____f_ B01000010#define a_cdefg B01011111#define ___de__ B00001100#define _____f_ B00000010#define ab___fg B01100011#define a__def_ B01001110#define __cde__ B00011100#define a___efg B01000111#define a__d___ B01001000#define abc____ B01110000#define _bc_ef_ B00110110#define ___def_ B00001110#define abc_ef_ B01110110#define _bcdef_ B00111110#define a__def_ B01001110#define abcd___ B01111000*///Segment order d e f b c a _ g#define _______ B00000000#define __cd___ B10001000#define _bcde_g B11011001#define abcdefg B11111101#define __cdefg B11101001#define __c____ B00001000#define ab__efg B01110101#define abcd_fg B10111101#define abcde_g B11011101#define ab_defg B11110101#define _bc____ B00011000#define a__defg B11100101#define ____ef_ B01100000#define ___defg B11100001#define abc__fg B00111101#define a___ef_ B01100100#define _b_____ B00010000#define ab___f_ B00110100#define _bcd___ B10011000#define a___ef_ B01100100#define ab_____ B00010100#define ____e__ B01000000#define __cde_g B11001001#define a____f_ B00100100#define a_cdefg B11101101#define ___de__ B11000000#define _____f_ B00100000#define ab___fg B00110101#define a__def_ B11100100#define __cde__ B11001000#define a___efg B01100101#define a__d___ B10000100#define abc____ B00011100#define _bc_ef_ B01111000#define ___def_ B11100000#define abc_ef_ B01111100#define _bcdef_ B11111000#define a__def_ B11100100#define abcd___ B10011100//Square Numbers//ASCII Character Set//Numbers 0 - 9//Letters A - Z//Segment order d e f b c a _ guint8_t ascii[] ={ B11111100, B00011000, B11010101, B10011101, B00111001, B10101101, B11101101, B00011100, B11111101, B10111101, B00000000, B00000000, B00000000, B00000001, B00000000, B00000000, B00000000, B01111101, B11101001, B11100100, B11011001, B11100101, B01100101, B10111101, B01111001, B00011000, B11011000, B00000000, B11100000, B00000000, B01001001, B11001001, B01110101, B00000000, B01000001, B10101101, B11100001, B11001000, B00000000, B00000000, B00000000, B10111001, B00000000, B11001100, B01010001, B10011100, B00000000, B10000000};//Digit sequence for each device (MAX7219)uint8_t digitMap[] ={5, 2, 6, 4, 1, 7, 3, 0};//------------------------------------------------------------// Digits using logical coordinates//------------------------------------------------------------const int8_t largeDigits[11][6][6] PROGMEM ={ { //0 { _______, _______, __cd___, _bcde_g, abcdefg, __cdefg }, { _______, __c____, abcdefg, ab__efg, abcd_fg, abcdefg }, { _______, abcde_g, ab_defg, _______, _bc____, a__defg }, { _______, abcdefg, ____ef_, __c____, abcdefg, ____ef_ }, { _______, abcdefg, __cdefg, abcdefg, a___efg, _______ }, { _______, abc__fg, abcdefg, ab__efg, _______, _______ } }, { //1 { _______, _______, _______, __c____, abcdefg, __cdefg }, { _______, _______, _______, abcdefg, abcdefg, ____ef_ }, { _______, _______, _bcde_g, abcdefg, a__defg, _______ }, { _______, _bc____, abcdefg, abcdefg, ____ef_, _______ }, { _______, abcdefg, abcdefg, a___ef_, _______, _______ }, { _b_____, abcdefg, abcdefg, _______, _______, _______ } }, { //2 { _______, _______, __cd___, _bcde_g, abcdefg, __cdefg }, { _______, _bc____, abcdefg, ab___f_, abcd_fg, abcdefg }, { _______, _______, _______, _bcd___, abcdefg, a___ef_ }, { _______, _______, _bcde_g, abcdefg, a___ef_, _______ }, { __c____, abcdefg, abcdefg, a___ef_, _______, _______ }, { _b_____, abc__fg, abcdefg, abcdefg, ab__efg, _______ } }, { //3 { _______, _______, __cd___, abcdefg, abcdefg, __cdefg }, { _______, _b_____, abcdefg, ab___f_, abcd_fg, abcdefg }, { _______, _______, __cd___, _bcde_g, abcdefg, a___ef_ }, { _______, _______, ab_____, abc__fg, abcdefg, ____e__ }, { _______, __cd___, __cde_g, _bcde_g, abcdefg, ____ef_ }, { _b_____, abc__fg, abcdefg, abcdefg, a____f_, _______ } }, { //4 { _______, _______, _bcd___, abcdefg, __c____, abcdefg }, { _______, __c____, abcdefg, a___ef_, abcde_g, a__defg }, { _______, abcde_g, abcdefg, _bcd___, abcdefg, ____ef_ }, { __c____, abcdefg, abcdefg, abcdefg, abcdefg, _______ }, { _______, _______, __c____, abcdefg, ____ef_, _______ }, { _______, _______, abcdefg, ab__efg, _______, _______ } }, { //5 { _______, _______, _bcde_g, abcdefg, abcdefg, abcdefg }, { _______, _bc____, abcdefg, a_cdefg, ___de__, _______ }, { _______, _______, abc__fg, abcdefg, abcdefg, ____ef_ }, { _______, _______, _______, _bc____, abcdefg, ____ef_ }, { _______, __cde_g, __cde_g, _bcde_g, abcdefg, _____f_ }, { _b_____, abcdefg, abcdefg, ab__efg, a____f_, _______ } }, { //6 { _______, _______, _______, __cd___, abcdefg, ____ef_ }, { _______, _______, _bcde_g, abcdefg, a____f_, _______ }, { _______, _bcd___, abcdefg, abcdefg, abcdefg, ____e__ }, { __c____, abcdefg, a___ef_, ab_____, abcdefg, ____ef_ }, { _bc____, abcdefg, __cdefg, _bcde_g, abcdefg, _______ }, { _______, abc__fg, abcdefg, ab__efg, _______, _______ } }, { //7 { _______, _bc____, abcdefg, abcdefg, abcdefg, __cdefg }, { _______, _b_____, ab___fg, ab___fg, abcdefg, abcdefg }, { _______, _______, _______, _bcde_g, abcdefg, a___ef_ }, { _______, _______, _bcde_g, abcdefg, ab__efg, _______ }, { _______, _bcde_g, abcdefg, a___ef_, _______, _______ }, { _b_____, abcdefg, abcdefg, _______, _______, _______ } }, { //8 { _______, _______, __cd___, abcdefg, abcdefg, __cdefg }, { _______, _bc____, abcdefg, ab___f_, abcd_fg, abcdefg }, { _______, _b_____, abcdefg, _bcde_g, abcdefg, a___ef_ }, { _______, _bcde_g, ab__efg, abc__fg, abcdefg, ____e__ }, { _bc____, abcdefg, __cde_g, _bcde_g, abcdefg, ____ef_ }, { _______, abc__fg, abcdefg, abcdefg, a____f_, _______ } }, { //9 { _______, _______, __cde_g, abcdefg, __cdefg, ___de__ }, { _______, _bcd___, abcdefg, ab___fg, abcd_fg, abcdefg }, { _______, abcdefg, a_cdefg, __cde__, abcdefg, a__defg }, { _______, ab_____, abcd_fg, abcdefg, abcdefg, _____f_ }, { _______, __cd___, abcdefg, ab__efg, _______, _______ }, { _b_____, abcdefg, a___ef_, _______, _______, _______ } }, { //Colon { _______, _______, _______, _______, _______, _______ }, { _______, __cde_g, _______, _______, _______, _______ }, { _______, _______, _______, _______, _______, _______ }, { _______, _______, _______, _______, _______, _______ }, { __cde_g, _______, _______, _______, _______, _______ }, { _______, _______, _______, _______, _______, _______ } }};const int8_t smallDigits[11][5][3] PROGMEM ={ { //0 { a___ef_, a__d___, abc____ }, { _bc_ef_, _______, _bc_ef_ }, { _bc_ef_, _______, _bc_ef_ }, { _bc_ef_, _______, _bc_ef_ }, { ___def_, a__d___, _bcd___ } }, { //1 { _______, abc_ef_, _______ }, { _______, _bc_ef_, _______ }, { _______, _bc_ef_, _______ }, { _______, _bc_ef_, _______ }, { _______, _bcdef_, _______ } }, { //2 { a__def_, a__d___, abc____ }, { _______, _______, _bc_ef_ }, { a___ef_, a__d___, _bcd___ }, { _bc_ef_, _______, _______ }, { ___def_, a__d___, abcd___ } }, { //3 { a__def_, a__d___, abc____ }, { _______, _______, _bc_ef_ }, { _______, a__def_, _bc____ }, { _______, _______, _bc_ef_ }, { a__def_, a__d___, _bcd___ } }, { //4 { abc_ef_, _______, abc_ef_ }, { _bc_ef_, _______, _bc_ef_ }, { ___def_, a__d___, _bc____ }, { _______, _______, _bc_ef_ }, { _______, _______, _bcdef_ } }, { //5 { a___ef_, a__d___, abcd___ }, { _bc_ef_, _______, _______ }, { ___def_, a__d___, abc____ }, { _______, _______, _bc_ef_ }, { a__def_, a__d___, _bcd___ } }, { //6 { a___ef_, a__d___, abcd___ }, { _bc_ef_, _______, _______ }, { ____ef_, a__d___, abc____ }, { _bc_ef_, _______, _bc_ef_ }, { ___def_, a__d___, _bcd___ } }, { //7 { a__def_, a__d___, abc____ }, { _______, _______, _bc_ef_ }, { _______, _bc_ef_, _______ }, { _______, _bc_ef_, _______ }, { _______, _bcdef_, _______ }, }, { //8 { a___ef_, a__d___, abc____ }, { _bc_ef_, _______, _bc_ef_ }, { ____ef_, a__d___, _bc____ }, { _bc_ef_, _______, _bc_ef_ }, { ___def_, a__d___, _bcd___ } }, { //9 { a___ef_, a__d___, abc____ }, { _bc_ef_, _______, _bc_ef_ }, { ___def_, a__d___, _bc____ }, { _______, _______, _bc_ef_ }, { a__def_, a__d___, _bcd___ } }, { //Hyphen { _______, _______, _______ }, { _______, _______, _______ }, { a__def_, a__d___, abcd___ }, { _______, _______, _______ }, { _______, _______, _______ } }};
    Tunes.hC/C++
    No preview (download only).
    Button.hC/C++
    /*Class:ButtonAuthor:John Bradnam ([email protected])Purpose:Arduino library to handle buttons*/#pragma once#include "Arduino.h"#define DEBOUNCE_DELAY 10//Repeat speed#define REPEAT_START_SPEED 500#define REPEAT_INCREASE_SPEED 50#define REPEAT_MAX_SPEED 50class Button{ public://Simple constructor Button(int pin); Button(int name, int pin); Button(int name, int pin, int analogLow, int analogHigh, bool activeLow =true); //Background function called when in a wait or repeat loop void Background(void (*pBackgroundFunction)()); //Repeat function called when button is pressed void Repeat(void (*pRepeatFunction)()); //Test if button is pressed bool IsDown(void); //Test whether button is pressed and released //Will call repeat function if one is provided bool Pressed(); //Return button state (HIGH or LOW) - LOW =Pressed int State(); //Return button name int Name(); private:int _name; int _pin; bool _range; int _low; int _high; bool _activeLow; void (*_repeatCallback)(void); void (*_backgroundCallback)(void);};
    Button.cppC/C++
    /*Class:ButtonAuthor:John Bradnam ([email protected])Purpose:Arduino library to handle buttons*/#include "Button.h"Button::Button(int pin){ _name =pin; _pin =pin; _range =false; _low =0; _high =0; _backgroundCallback =NULL; _repeatCallback =NULL; pinMode(_pin, INPUT_PULLUP);}Button::Button(int name, int pin){ _name =name; _pin =pin; _range =false; _low =0; _high =0; _backgroundCallback =NULL; _repeatCallback =NULL; pinMode(_pin, INPUT_PULLUP);}Button::Button(int name, int pin, int analogLow, int analogHigh, bool activeLow){ _name =name; _pin =pin; _range =true; _low =analogLow; _high =analogHigh; _activeLow =activeLow; _backgroundCallback =NULL; _repeatCallback =NULL; pinMode(_pin, INPUT);}//Set function to invoke in a delay or repeat loopvoid Button::Background(void (*pBackgroundFunction)()){ _backgroundCallback =pBackgroundFunction;}//Set function to invoke if repeat system requiredvoid Button::Repeat(void (*pRepeatFunction)()){ _repeatCallback =pRepeatFunction;} bool Button::IsDown(){ if (_range) { int value =analogRead(_pin); return (value>=_low &&value <_high); } else { return (digitalRead(_pin) ==LOW); }}//Tests if a button is pressed and released// returns true if the button was pressed and released// if repeat callback supplied, the callback is called while the key is pressedbool Button::Pressed(){ bool pressed =false; if (IsDown()) { unsigned long wait =millis() + DEBOUNCE_DELAY; while (millis() =time) { _repeatCallback(); unsigned long faster =speed - REPEAT_INCREASE_SPEED; if (faster>=REPEAT_MAX_SPEED) { speed =faster; } time =millis() + speed; } } pressed =true; } } return pressed;}//Return current button stateint Button::State(){ if (_range) { int value =analogRead(_pin); if (_activeLow) { return (value>=_low &&value <_high) ? LOW :HIGH; } else { return (value>=_low &&value <_high) ? HIGH :LOW; } } else { return digitalRead(_pin); }}//Return current button nameint Button::Name(){ return _name;}
    Hardware_Test_V1.zipC/C++
    Hardware test sketch - Turns on all segments and tests speaker
    No preview (download only).

    맞춤형 부품 및 인클로저

    회로도

    Schematic for each MAX7219 (repeated 18 times) Holds six 7 segment 4 digit displays (6 boards required) PCB files in Eagle format eagle_files_KPhUOj6Ezv.zip

    제조공정

    1. Arduino 관점 시계
    2. 단순 워드 클럭(Arduino)
    3. 이슬람 기도 시간이 있는 Arduino 시계
    4. 마스터 시계
    5. 단일 LED 매트릭스 Arduino 플립 시계
    6. Arduino Uno로 LED 매트릭스 제어
    7. eDOT - Arduino 기반 정밀 시계 및 기상 관측소
    8. Arduino가 있는 미니 레이더
    9. 실시간 시계가 있는 Arduino OLED 온도 디스플레이
    10. DS1302 RTC가 있는 간단한 알람 시계