TV 시리즈 & 애니메이션 방영일 자동 알림 시스템 – 아두이노 & 라즈베리파이
부품 및 소모품
부품
수량
JLCPCB 커스텀 PCB
1
아두이노 나노 33 IoT (Arduino Nano 33 IoT)
1
라즈베리파이 3 모델 B 또는 4 모델 B
1
스파크펀 노키아 5110 LCD (SparkFun Nokia 5110 Screen)
1
시리얼 MP3 플레이어 (OPEN-SMART)
1
RGB 확산형 공통 애노드 LED
1
SD 카드 (플래시 메모리)
2
220옴 저항
12
6×6×5 푸시버튼 스위치
4
DC 파워 잭 2.1mm 배럴 타입 PCB 마운트
1
필요 도구 및 장비
앱 및 온라인 서비스
아두이노 IDE
KiCad (PCB 설계)
Notepad++
프로젝트 소개
개요
즐겨 보는 쇼의 방영일을 추적하는 웹/모바일 앱은 수없이 많지만, 하드웨어와 소프트웨어를 직접 만들어 내가 등록한 시리즈/애니메이션의 새 에피소드 방영을 알림받는 기기를 만들고 싶었습니다. 다양한 기능도 함께 구현했습니다.
먼저 PHP로 TV 시리즈/애니메이션 방영일 트래커 웹 애플리케이션을 개발해 시리즈/애니메이션 목록을 관리하고 방영일 정보를 수집했습니다. 이때 TVMAZE REST API 를 활용해 에피소드 정보를 방영일 기준으로 정확히 가져올 수 있었습니다. 라즈베리파이에 LAMP(마리아DB) 서버를 구축해 애플리케이션을 실행했습니다. 라즈베리파이가 없거나 호스트로 사용하고 싶지 않은 분들을 위해, 제 웹사이트에 사용자 친화적 인터페이스를 갖춘 무료 버전도 공개했습니다. 소스 코드도 아래에 있습니다.
웹 애플리케이션과의 데이터 전송이 완료되면, 새 에피소드 방영 시 아두이노 나노 33 IoT 를 개발 보드로 사용해 애플리케이션이 생성한 데이터를 수집합니다. 구분자(delimiter)를 이용해 애플리케이션이 전송한 응답을 파싱하는 방법은 아래에 설명되어 있습니다. 노키아 5110 화면과 푸시버튼으로 구현한 사용자 인터페이스(메뉴)를 통해 나노 33 IoT의 기능과 모드를 관리합니다.
마지막으로, 리스트에 있는 쇼의 새 에피소드 방영 시 오프닝 곡과 함께 알림을 받기 위해 시리얼 MP3 플레이어 모듈(OPEN-SMART) 을 사용했습니다. 간단한 UART 명령 체계 덕분에 구현이 쉬웠고, MP3 플레이어 모드를 추가해 모든 오프닝 곡을 재생할 수 있게 되어 방 안의 흥미로운 액세서리가 되었습니다 :)
브레드보드에서 설계를 완료하고 코드를 테스트한 후, 레트로 전화기 모양의 독특한 PCB(TV 시리즈/애니메이션 방영일 알림기 )를 설계해 완전히 새롭고 사용하기 쉬운 기기를 만들었습니다.
JLCPCB의 프로젝트 후원에 큰 감사를 드립니다.
1단계: TV 시리즈/애니메이션 방영일 알림기 PCB 설계 및 납땜
KiCad로 PCB를 설계했습니다. 거버 파일을 첨부했으니 JLCPCB에서 주문해 프로젝트를 재현할 수 있습니다 :)
납땜 인두로 헤더, 푸시버튼, 220Ω 저항, 파워 잭, 5mm 애노드 RGB LED를 부착했습니다.
PCB 부품 목록:
A1 (아두이노 나노 33 IoT용 헤더)
J1 (시리얼 MP3 플레이어용 헤더)
J2 (DS3231용 헤더 - 선택 사항)
J3 (노키아 5110 화면용 헤더)
J4 (파워 잭)
D1 (5mm 애노드 RGB LED)
K1, K2, K3, K4 (푸시버튼)
R1~R12 (220Ω 저항)
2단계: 라즈베리파이에 마리아DB 포함 LAMP 웹 서버 구축
라즈베리파이 프로그래밍 초보자는 공식 라즈베리파이 웹사이트 의 잘 설명된 지침과 튜토리얼을 참조하세요.
메뉴에서 Accessories > Terminal로 터미널 열기
아파치2 패키지 설치:
sudo apt-get install apache2 -y
크로미움 브라우저에서 https://localhost 접속해 서버 테스트. 라즈베리파이 IP 주소 사용도 가능:
hostname -I
PHP 패키지 설치 (업데이트 필수):
sudo apt-get update
sudo apt-get install php -y
마리아DB(MySQL 서버) 및 PHP-MySQL 패키지 설치:
sudo apt-get install mariadb-server php-mysql -y
마리아DB 보안 설치로 새 사용자 생성:
sudo mysql_secure_installation
Enter current password for root (enter for none): → 엔터
Set root password → Y, 비밀번호: bot
Remove anonymous users → Y
Disallow root login remotely → Y
Remove test database and access to it → Y
Reload privilege tables now → Y
완료 시 All done! and Thanks for using MariaDB! 메시지
TVMAZE API 데이터 저장을 위한 tvseries 데이터베이스 생성:
sudo mysql -uroot -p
-- 비밀번호: bot 입력
USE tvseries;
create database tvseries;
GRANT ALL PRIVILEGES ON tvseries.* TO 'root'@'localhost' IDENTIFIED BY 'bot';
FLUSH PRIVILEGES;
-- Ctrl+D로 종료
PHP에서 file_get_contents()로 TVMAZE API 호출을 위해 php-curl 설치:
sudo apt-get install php-curl
아파치 서버 재시작:
sudo service apache2 restart
3단계: TVMAZE REST API로 즐겨찾기 시리즈/애니메이션 정보 가져오기
TVMAZE는 무료이고 사용하기 쉬운 REST API를 제공합니다. 쇼 이름, 날짜, 에피소드 번호 등으로 검색 가능. TVMAZE REST API 에서 엔드포인트와 쿼리 확인 가능.
Episode by Date 옵션을 사용해 특정 날짜에 방영된 에피소드 정보를 가져옵니다.
URL: /shows/:id/episodesbydate?date=:date
날짜: ISO 8601 형식
예시: https://api.tvmaze.com/shows/1/episodesbydate?date=2013-07-01
해당 날짜에 방영된 에피소드가 있으면 전체 에피소드 정보 배열을 반환합니다.
없으면 HTTP 404 반환.
프로젝트에서 추적할 제가 좋아하는 시리즈/애니메이션 목록입니다. The Late Late Show 는 일일 방영이라 테스트용으로 추가했습니다.
One Piece – TV ID: 1505 – 링크
My Hero Academia – TV ID: 13615 – 링크
Westworld – TV ID: 1371 – 링크
The Simpsons – TV ID: 83 – 링크
The Late Late Show with James Corden – TV ID: 2831 – 링크
4단계: TV 시리즈/애니메이션 방영일 트래커 애플리케이션 개발 (PHP, 라즈베리파이 실행) 및 시리즈 테이블 생성
PHP로 애플리케이션을 개발했으며, 관심 있는 분들을 위해 아래 '코드 설명' 섹션에서 소스 코드를 설명했습니다.
PHP 웹 개발이 낯선 분은 TV-Series-Anime-Episode-Tracker.zip 을 다운로드해 라즈베리파이에 업로드하면 PHP 코딩 없이 사용 가능합니다.
설치 방법
애플리케이션(TV-Series-Anime-Episode-Tracker.zip, index.php와 query.php 포함) 다운로드
압축 해제
터미널로 서버 디렉토리(/var/www/html)에 이동 (아파치 서버는 보호된 위치):
sudo mv /home/pi/Downloads/TV-Series-Anime-Episode-Tracker/ /var/www/html/
브라우저에서 접속:
https://localhost/TV-Series-Anime-Episode-Tracker/
기능
최초 접속 시 series 테이블 자동 생성 및 Database Table Created! 알림
테이블 생성 성공 시 Database Table Found! 알림
각 쇼에 필요한 변수(시리즈명, TV ID, 상태) 삽입:
sudo mysql -uroot -p
-- 비밀번호: bot
USE tvseries;
INSERT INTO `series` (`series_name`, `tv_id`, `status`) VALUES ('One Piece', '1505', 'Pending');
status 변수는 새 에피소드 방영 시 중복 알림 방지용
query.php에서 TVMAZE API 요청으로 새 에피소드 방영 감지. 방영 에피소드 있으면 아두이노가 파싱 가능한 형식으로 1회만 출력:
%Series_Name%Season%Episode_Number%Episode_Name%
%The Late Late Show%2020%91%Jason Schwartzman, Alicia Key%
방영 에피소드 없으면 No Entry Found! 출력
코드 설명
1) index.php
DB 연결 설정: "localhost", "root", "bot", "tvseries"
checkDatabase(): series 테이블 존재 여부 확인
createTable(): 테이블 없으면 생성 후 사용자 알림
2) query.php
query 클래스: TVMAZE API 경로 정의
define_user(): 연결 설정 및 테이블명 가져오기
check_ep_release_date(): file_get_contents()로 현재 날짜 기준 새 에피소드 방영 여부 확인. 방영 시 DB status를 Pending에서 Released로 변경해 중복 방지
track_db_entries(): DB series 테이블의 각 쇼에 대해 check_ep_release_date() 실행
4.1단계: 라즈베리파이 없이 웹 애플리케이션 실행
내 웹사이트(TheAmplituhedron) 구독자를 위해 라즈베리파이 없이 호스팅 가능한 무료 버전을 개발했습니다. 사용자 친화적 인터페이스로 DB 관리 가능. 이 인터페이스를 프로젝트에 사용하려면 저에게 연락주세요.
애플리케이션 접속: https://www.theamplituhedron.com/dashboard/TV-Series-Anime-Episode-Tracker/
5단계: 아두이노 나노 33 IoT 설정
아두이노 나노 33 IoT 프로그래밍 초보자도 걱정 마세요. 아두이노 IDE로 간단히 사용 가능. 아두이노 SAMD 코어 드라이버 다운로드 필요.
와이파이 연결을 위해 WiFiNINA 라이브러리 다운로드.
노키아 5110 화면 텍스트/그래픽 출력을 위해 LCD5110_Basic 라이브러리 다운로드.
아두이노 나노 33 IoT의 두 번째 시리얼 포트(Serial1)로 시리얼 MP3 플레이어와 통신.
6단계: 시리얼 MP3 플레이어(OPEN-SMART) 명령 전송
프로젝트용 플레이리스트(즐겨찾기 시리즈/애니메이션 오프닝 곡):
One Piece - Opening
My Hero Academia - Opening
Westworld - Opening
The Simpsons - Opening
The Late Late Show - Opening
내 플레이리스트 opening_songs.zip 다운로드 가능.
아두이노 나노 33 IoT로 시리얼 MP3 플레이어 제어는 UART 명령만 알면 쉽습니다. OPEN-SMART 모듈(스피커 포함) 사용. 다른 브랜드면 아래 명령이 작동하지 않을 수 있습니다.
가이드 지침:
"본 모듈은 고품질 MP3 오디오 칩 기반 심플 MP3 플레이어 장치입니다. 8kHz~48kHz 샘플링 주파수의 MP3 및 WAV 포맷 지원. 온보드 TF 카드 소켓으로 오디오 파일 저장된 마이크로 SD 카드 장착 가능. MCU가 UART 포트로 명령 전송해 재생 상태 제어(곡 전환, 볼륨, 재생 모드 등). USB-UART 모듈로 디버깅 가능. 아두이노/AVR/ARM/PIC 호환."
"마이크로 SD 카드는 FAT16 또는 FAT32로 포맷하고 곡 파일 저장 필요. '01', '02' 폴더 생성 후 001xxx.mp3, 002xxx.mp3, 003xxx.mp3 형태로 곡 파일 저장."
즉, 01 폴더에 연속 번호로 곡 파일 저장: 001One Piece-Opening.mp3, 002My Hero Academia-Opening.mp3 등.
아두이노 나노 33 IoT로 UART 명령 전송을 위해 send_command_to_MP3_player 함수 생성. 요청 명령 길이에 따라(4, 5, 6바이트) 각 바이트를 두 번째 시리얼 포트(RX0, TX1 핀) Serial1로 전송.
명령은 바이트별로 구분: 0x7e. 모든 명령은 '7E'로 시작, 'EF'로 끝. 두 번째 바이트는 중간 바이트 수: '02', '03', '04'. 아래에서 OPEN-SMART 시리얼 MP3 플레이어 매뉴얼 다운로드해 전체 명령 확인 가능.
// 필수 MP3 플레이어 명령 정의
// 프로젝트 페이지에서 전체 명령 확인 가능:
// TF 카드 저장 장치 선택
static int8_t select_SD_card[] = {0x7e, 0x03, 0X35, 0x01, 0xef}; // 7E 03 35 01 EF
// /01/001xxx.mp3 재생
static int8_t play_song_1[] = {0x7e, 0x04, 0x41, 0x00, 0x01, 0xef}; // 7E 04 41 00 01 EF
// /01/002xxx.mp3 재생
static int8_t play_song_2[] = {0x7e, 0x04, 0x41, 0x00, 0x02, 0xef}; // 7E 04 41 00 02 EF
// /01/003xxx.mp3 재생
static int8_t play_song_3[] = {0x7e, 0x04, 0x41, 0x00, 0x03, 0xef}; // 7E 04 41 00 03 EF
// /01/004xxx.mp3 재생
static int8_t play_song_4[] = {0x7e, 0x04, 0x41, 0x00, 0x04, 0xef}; // 7E 04 41 00 04 EF
// /01/005xxx.mp3 재생
static int8_t play_song_5[] = {0x7e, 0x04, 0x41, 0x00, 0x05, 0xef}; // 7E 04 41 00 05 EF
// 재생
static int8_t play[] = {0x7e, 0x02, 0x01, 0xef}; // 7E 02 01 EF
// 일시정지
static int8_t pause[] = {0x7e, 0x02, 0x02, 0xef}; // 7E 02 02 EF
// 다음 곡
static int8_t next_song[] = {0x7e, 0x02, 0x03, 0xef}; // 7E 02 03 EF
// 이전 곡
static int8_t previous_song[] = {0x7e, 0x02, 0x04, 0xef}; // 7E 02 04 EF
7단계: 아두이노 나노 33 IoT 프로그래밍
필수 라이브러리 포함
와이파이 설정(SSID, 비밀번호) 정의
라즈베리파이 IP 주소 입력
애플리케이션 경로(query.php) 정의
이더넷 클라이언트 라이브러리 초기화
노키아 5110 화면 설정
화면 모드별 그래픽(TV, 음악) 정의
모노크롬 이미지 변환: Monochrome Image Converter
필수 MP3 플레이어 명령 정의
메뉴 옵션 및 모드 정의 (volatile boolean)
제어 버튼 및 RGB LED 핀 정의
시리얼 MP3 플레이어 모듈용 시리얼 통신 개시 (두 번째 포트 Serial1)
와이파이 네트워크 연결
시리얼 모니터와 노키아 5110 화면에서 연결 확인
read_buttons(): 제어 버튼(오른쪽, OK, 왼쪽, Exit) 입력 읽기
change_menu_options(): 오른쪽/왼쪽 버튼으로 옵션 번호 증감
interface(): 인터페이스(메뉴) 출력
Init Tracker 모드 선택 시 웹 애플리케이션 연결
수신 바이트 있으면 웹 애플리케이션 응답 가져오기
endsWith()로 응답에 퍼센트 기호(구분자) 포함 여부 확인
포함 시 미리 정의된 구분자 '%'로 응답 문자열 분할
서브스트링으로 정보 추출: Series_Name, Season, Episode, Episode_Name
정보 출력 및 해당 시리즈/애니메이션 오프닝 곡 재생(중지될 때까지)
Exit 버튼 눌릴 때까지 대기
빈 응답 시 No Released Episode Detected :( 출력
TV 아이콘 그리기 후 다음 요청까지 카운트
다음 요청까지 대기
MP3 Player mode 선택 시 음악 플레이어 아이콘 그리기. 제어 버튼으로 MP3 제어(오른쪽: 다음, 왼쪽: 이전, OK: 일시정지)
Sleep mode 선택 시 10초 후 슬립 모드 진입, 남은 초 표시
연결 및 조정
// 연결
// 아두이노 나노 33 IoT:
// 노키아 5110 화면
// D2 -------------------------- SCK (Clk)
// D3 -------------------------- MOSI (Din)
// D4 -------------------------- DC
// D5 -------------------------- RST
// D6 -------------------------- CS (CE)
// RGB
// D9 -------------------------- R
// D10 -------------------------- G
// D11 -------------------------- B
// LEFT_BUTTON
// A0 --------------------------- S
// OK_BUTTON
// A1 --------------------------- S
// RIGHT_BUTTON
// A2 --------------------------- S
// EXIT_BUTTON
// A3 --------------------------- S
// DS3231 (나노용 선택 사항, 나노 33 IoT엔 불필요)
// A4 --------------------------- SDA
// A5 --------------------------- SCL
코드 업로드 후 모든 필수 부품을 헤더로 보드에 장착했습니다.
아두이노 나노 교체 시 DS3231 RTC 모듈 연결 슬롯도 제공. 하지만 아두이노 나노 사용 시 웹 앱에서 데이터 가져오기 불가해 RTC로 카운트다운 타이머 만들려면 각 쇼의 방영일을 수동 입력해야 함.
참고: 시리얼 MP3 플레이어에 스피커 또는 헤드폰 연결해 사운드트랙 청취 가능. OPEN-SMART 제공 스피커 연결함.
모드 및 기능
1) 와이파이 연결 시도 중
Waiting... Attempting to connect to WiFi 표시
2) 연결 성공
Connected to WiFi!!! 메시지
3) 메뉴 인터페이스
사용 가능 모드: A.Init Tracker , B.MP3 Player , C.Sleep . 오른쪽/왼쪽 버튼으로 전환, OK 버튼으로 선택.
A. Init Tracker
A.1) 로컬호스트(라즈베리파이) 연결 시도. 성공 시 Connected to the server!!! , 실패 시 Connection Error!!!
A.2) 애플리케이션이 DB 등록 시리즈/애니메이션의 방영 에피소드 데이터 전송 시:
에피소드 정보 출력: 시리즈명, 시즌, 에피소드 번호, 에피소드명
해당 시리즈/애니메이션 오프닝 곡 재생
할당된 색상으로 RGB LED 점등
할당 색상 예시:
One Piece → 빨강
My Hero Academia → 초록
Westworld → 파랑
The Simpsons → 노랑
The Late Late Show → 보라
Exit 버튼 누를 때까지 정보 표시 및 색상 유지. 각 방영 에피소드당 1회만 알림(중복 방지).
A.3) Exit 버튼 누르면 TV 아이콘 그리기하며 다음 요청 대기.
A.4) 방영 에피소드 없으면 No Released Episode Detected :( 표시 후 TV 아이콘 그리기하며 다음 요청 대기.
A.5) TV 아이콘 표시 중 Exit 버튼 누르면 메뉴로 복귀.
B. MP3 Player
부수적이고 재미있는 기능으로 기기를 방 안의 흥미로운 액세서리로 변신.
B.1) 음악 노트 아이콘 그리기 후 제어 버튼으로 모든 오프닝 곡 재생:
오른쪽: 다음 곡
왼쪽: 이전 곡
OK: 일시정지
B.2) Exit 버튼 누르면 메뉴로 복귀.
C. Sleep
C.1) 10초 후 슬립 모드 진입, 남은 초 화면 출력.
C.2) 슬립 모드 유지 중 RGB에 나이트라이트용 컬러 패턴 적용.
C.3) Exit 버튼 누르면 메뉴로 복귀.
영상 및 결론
모든 단계 완료 후 글루건으로 책장에 고정. 훌륭하게 작동합니다 :)
코드
Series_Release_Date_Notifier.ino (아두이노)
////////////////////////////////////////////////////
// TV Series / Anime New Episode
// Release Date Notifier
// -------------------------
// Arduino Nano 33 IoT
// by Kutluhan Aktar
//
// ////////////////////////////////////////////////////
// 즐겨찾기 쇼의 새 에피소드 방영 시 오프닝 곡과 함께 알림 via Nano 33 IoT & 라즈베리파이.
// 이 프로젝트를 위한 검증 웹 앱을 PHP로 개발: TV Series / Anime Release Date Tracker.
// 라즈베리파이 서버 사용(프로젝트 튜토리얼 설명) 또는 TheAmplituhedron 실시간 DB 인터페이스 사용(구독자).
// 더 많은 정보: https://www.theamplituhedron.com/projects/TV-Series-Anime-New-Episode-Release-Date-Notifier/
// TheAmplituhedron 무료 웹 앱 버전 사용 가능(구독자): https://www.theamplituhedron.com/dashboard/TV-Series-Anime-Episode-Tracker/
//
// 연결
// 아두이노 나노 33 IoT:
// 노키아 5110 화면
// D2 --------------------------- SCK (Clk)
// D3 --------------------------- MOSI (Din)
// D4 --------------------------- DC
// D5 --------------------------- RST
// D6 --------------------------- CS (CE)
// RGB
// D9 --------------------------- R
// D10 --------------------------- G
// D11 --------------------------- B
// LEFT_BUTTON
// A0 --------------------------- S
// OK_BUTTON
// A1 --------------------------- S
// RIGHT_BUTTON
// A2 --------------------------- S
// EXIT_BUTTON
// A3 --------------------------- S
// DS3231 (나노용 선택 사항, 나노 33 IoT엔 불필요)
// A4 --------------------------- SDA
// A5 --------------------------- SCL
#include <SPI.h>
#include <WiFiNINA.h>
#include <LCD5110_Basic.h>
char ssid[] = "[_SSID_]"; // 네트워크 SSID
char pass[] = "[_PASSWORD_]"; // 네트워크 비밀번호
int keyIndex = 0; // 네트워크 키 인덱스(WEP만 필요)
int status = WL_IDLE_STATUS;
// TheAmplituhedron 호스팅 웹 앱 사용 시(구독자) 아래 주석 해제 후 동등 설정 사용
// 라즈베리파이 IP 주소 입력
IPAddress server(192, 168, 1, 22);
/*
// TheAmplituhedron용 네임 주소. 다른 호스트 서버 사용 시 변경.
char server[] = "www.theamplituhedron.com";
*/
// 라즈베리파이 내 애플리케이션 경로 정의
String application = "/TV-Series-Anime-Episode-Tracker/query.php";
/*
// TheAmplituhedron 호스트 서버 사용 시 본인의 hedron 정의:
String HEDRON = "[_HEDRON_]";
// 웹 애플리케이션 경로 정의. TheAmplituhedron 사용 시 hedron만 입력. 아니면 서버 내 경로 입력.
String application = "/dashboard/TV-Series-Anime-Episode-Tracker/" + HEDRON;
*/
// 이더넷 클라이언트 라이브러리 초기화
WiFiClient client;
/* WiFiSSLClient client; */
// 화면 설정
LCD5110 myGLCD(2,3,4,5,6);
extern uint8_t SmallFont[];
extern uint8_t MediumNumbers[];
// 화면 모드별 그래픽
extern uint8_t tv[];
extern uint8_t music[];
// 필수 MP3 플레이어 명령 정의
static int8_t select_SD_card[] = {0x7e, 0x03, 0X35, 0x01, 0xef};
static int8_t play_song_1[] = {0x7e, 0x04, 0x41, 0x00, 0x01, 0xef};
static int8_t play_song_2[] = {0x7e, 0x04, 0x41, 0x00, 0x02, 0xef};
static int8_t play_song_3[] = {0x7e, 0x04, 0x41, 0x00, 0x03, 0xef};
static int8_t play_song_4[] = {0x7e, 0x04, 0x41, 0x00, 0x04, 0xef};
static int8_t play_song_5[] = {0x7e, 0x04, 0x41, 0x00, 0x05, 0xef};
static int8_t play[] = {0x7e, 0x02, 0x01, 0xef};
static int8_t pause[] = {0x7e, 0x02, 0x02, 0xef};
static int8_t next_song[] = {0x7e, 0x02, 0x03, 0xef};
static int8_t previous_song[] = {0x7e, 0x02, 0x04, 0xef};
// 메뉴 옵션 및 모드 정의 (volatile boolean)
volatile boolean TV = false;
volatile boolean Music = false;
volatile boolean Sleep = false;
volatile boolean Activated = false;
// 제어 버튼 정의
#define B_Exit A3
#define B_Right A2
#define B_OK A1
#define B_Left A0
// RGB LED 핀 정의
#define redPin 9
#define greenPin 10
#define bluePin 11
// 데이터 홀더
int Right, OK, Left, Exit;
int selected = 0;
void setup() {
// 버튼
pinMode(B_Exit, INPUT);
pinMode(B_Right, INPUT);
pinMode(B_OK, INPUT);
pinMode(B_Left, INPUT);
// RGB
pinMode(redPin, OUTPUT);
pinMode(greenPin, OUTPUT);
pinMode(bluePin, OUTPUT);
adjustColor(0, 0, 0); // 검정(소등)
// 화면 초기화
myGLCD.InitLCD();
myGLCD.setFont(SmallFont);
Serial.begin(9600);
// 시리얼 MP3 플레이어 모듈용 시리얼 통신 개시 (두 번째 포트)
Serial1.begin(9600);
// SD 카드 선택
send_command_to_MP3_player(select_SD_card, 5);
// 와이파이 모듈 확인
if (WiFi.status() == WL_NO_MODULE) {
Serial.println("Communication with WiFi module failed!");
myGLCD.print("Connection Failed!", 0, 8);
while (true);
}
// 와이파이 네트워크 연결 시도
while (status != WL_CONNECTED) {
Serial.print("Attempting to connect to SSID: ");
Serial.println(ssid);
myGLCD.print("Waiting...", 0, 8);
myGLCD.print("Attempting to", 0, 16);
myGLCD.print("connect to", 0, 24);
myGLCD.print("WiFi !!!", 0, 32);
// WPA/WPA2 네트워크 연결. 오픈/WEP 네트워크 사용 시 이 줄 변경:
status = WiFi.begin(ssid, pass);
// 연결 대기 10초
delay(10000);
}
// 시리얼 모니터와 노키아 5110 화면에서 연결 확인
Serial.println("Connected to wifi");
myGLCD.clrScr();
myGLCD.print("Connected to", 0, 8);
myGLCD.print("WiFi!!!", 0, 16);
delay(2000);
myGLCD.clrScr();
}
void loop() {
read_buttons();
change_menu_options();
interface();
if(TV == true){
do{
myGLCD.invertText(true);
myGLCD.print("A.Init Tracker", 0, 16);
myGLCD.invertText(false);
delay(100);
if(OK == HIGH){
myGLCD.clrScr();
Activated = true;
while(Activated == true){
// 웹 애플리케이션 TV Series / Anime Release Date Tracker 연결
// TheAmplituhedron 호스트 사용 시 '80'을 '443'으로 변경
if (client.connect(server, 80)) {
Serial.println("connected to server");
myGLCD.print("Connected to", 0, 8);
myGLCD.print("the server!!!", 0, 16);
// HTTP 요청
client.println("GET " + application + " HTTP/1.1");
//client.println("Host: www.theamplituhedron.com");
client.println("Host: 192.168.1.22");
client.println("Connection: close");
client.println();
}else{
myGLCD.print("Connection", 0, 8);
myGLCD.print("Error!!!", 0, 16);
}
delay(2000); // 연결 후 2초 대기
// 수신 바이트 있으면 웹 앱 응답 가져오기
String response = "";
while (client.available()) {
char c = client.read();
response += c;
}
if(response != "" && response.endsWith("%")){
// 미리 정의된 구분자 '%'로 응답 문자열 분할
int delimiter, delimiter_1, delimiter_2, delimiter_3, delimiter_4;
delimiter = response.indexOf("%");
delimiter_1 = response.indexOf("%", delimiter + 1);
delimiter_2 = response.indexOf("%", delimiter_1 +1);
delimiter_3 = response.indexOf("%", delimiter_2 +1);
delimiter_4 = response.indexOf("%", delimiter_3 +1);
// 서브스트링으로 정보 추출
String Series_Name = response.substring(delimiter + 1, delimiter_1);
String Season = response.substring(delimiter_1 + 1, delimiter_2);
String Episode = response.substring(delimiter_2 + 1, delimiter_3);
String Episode_Name = response.substring(delimiter_3 + 1, delimiter_4);
// 정보 출력
myGLCD.clrScr();
myGLCD.print(Series_Name, 0, 0);
myGLCD.print(Season + " x " + Episode, 0, 16);
myGLCD.print(Episode_Name, 0, 32);
// 방영 시리즈/애니메이션 오프닝 곡 재생(중지될 때까지)
if(Series_Name == "One Piece") {
send_command_to_MP3_player(play_song_1, 6);
adjustColor(255,0,0);
}
if(Series_Name == "My Hero Academia") {
send_command_to_MP3_player(play_song_2, 6);
adjustColor(0,255,0);
}
if(Series_Name == "Westworld") {
send_command_to_MP3_player(play_song_3, 6);
adjustColor(0,0,255);
}
if(Series_Name == "The Simpsons") {
send_command_to_MP3_player(play_song_4, 6);
adjustColor(255,255,0);
}
if(Series_Name == "The Late Late Show") {
send_command_to_MP3_player(play_song_5, 6);
adjustColor(80,0,80);
}
// Exit 버튼 눌릴 때까지 대기...
volatile boolean song = true;
while(song == true){
read_buttons();
if(Exit == HIGH){
song = false;
send_command_to_MP3_player(pause, 4);
adjustColor(0,0,0);
}
}
myGLCD.clrScr();
}else{
// 방영 에피소드 없음
myGLCD.clrScr();
myGLCD.print("No Released", 0, 0);
myGLCD.print("Episode", 0, 16);
myGLCD.print("Detected :(", 0, 32);
delay(5000); // 5초 표시 대기
myGLCD.clrScr();
}
// TV 아이콘 그리기하며 다음 요청까지 카운트
myGLCD.drawBitmap(8, 0, tv, 60, 48);
delay(10 * 1000); // 다음 요청까지 대기
myGLCD.clrScr();
// Exit
read_buttons();
if(Exit == HIGH){
Activated = false;
myGLCD.clrScr();
}
}
}
}while(TV == false);
}
if(Music == true){
do{
myGLCD.invertText(true);
myGLCD.print("B.MP3 Player", 0, 24);
myGLCD.invertText(false);
delay(100);
if(OK == HIGH){
myGLCD.clrScr();
Activated = true;
while(Activated == true){
read_buttons();
// 음악 플레이어 아이콘 그리기
myGLCD.drawBitmap(8, 0, music, 60, 48);
// MP3 플레이어 제어
if(Right == true) send_command_to_MP3_player(next_song, 4);
if(Left == true) send_command_to_MP3_player(previous_song, 4);
if(OK == true) send_command_to_MP3_player(pause, 4);
// Exit
if(Exit == HIGH){
Activated = false;
myGLCD.clrScr();
send_command_to_MP3_player(pause, 4);
}
}
}
}while(Music == false);
}
if(Sleep == true){
do{
myGLCD.invertText(true);
myGLCD.print("C.Sleep", 0, 32);
myGLCD.invertText(false);
delay(100);
if(OK == HIGH){
// 10초 후 슬립 모드 진입
myGLCD.clrScr();
myGLCD.print("Entering", CENTER, 0);
myGLCD.print("Sleep Mode", CENTER, 8);
myGLCD.print("in", CENTER, 16);
myGLCD.print("Seconds", CENTER, 40);
// 남은 초 출력
myGLCD.setFont(MediumNumbers);
for (int s=10; s>=0; s--){
myGLCD.printNumI(s, CENTER, 24, 2, '0');
delay(1000);
}
myGLCD.enableSleep();
Activated = true;
while(Activated == true){
// 컬러 패턴
adjustColor(255,0,0); delay(500);
adjustColor(0,255,0); delay(500);
adjustColor(0,0,255); delay(500);
adjustColor(255,255,0); delay(500);
adjustColor(80,0,80); delay(500);
// Exit
read_buttons();
if(Exit == HIGH){
Activated = false;
myGLCD.clrScr();
myGLCD.disableSleep();
myGLCD.setFont(SmallFont);
adjustColor(0,0,0);
}
}
}
}while(Sleep == false);
}
}
void read_buttons(){
Right = digitalRead(B_Right);
OK = digitalRead(B_OK);
Left = digitalRead(B_Left);
Exit = digitalRead(B_Exit);
}
void send_command_to_MP3_player(int8_t command[], int len){
Serial.print("\nMP3 Command => ");
for(int i=0;i<len;i++){
Serial1.write(command[i]);
Serial.print(command[i], HEX);
}
delay(1000);
}
void interface(){
myGLCD.print("Menu Options :", 0, 0);
myGLCD.print("A.Init Tracker", 0, 16);
myGLCD.print("B.MP3 Player", 0, 24);
myGLCD.print("C.Sleep", 0, 32);
}
void change_menu_options(){
if(Right == true) selected++;
if(Left == true) selected--;
if(selected < 0) selected = 3;
if(selected > 3) selected = 1;
delay(100);
switch(selected){
case 1: TV = true; Music = false; Sleep = false; break;
case 2: TV = false; Music = true; Sleep = false; break;
case 3: TV = false; Music = false; Sleep = true; break;
}
}
void adjustColor(int red, int green, int blue){
red = 255 - red;
green = 255 - green;
blue = 255 - blue;
analogWrite(redPin, red);
analogWrite(greenPin, green);
analogWrite(bluePin, blue);
}
index.php (PHP)
<?php
$conn_database = mysqli_connect("localhost", "root", "bot", "tvseries");
function checkDatabase($table, $conn){
$sql = "SHOW TABLES FROM `tvseries` LIKE '$table'";
$check = mysqli_num_rows(mysqli_query($conn, $sql));
return ($check > 0) ? true : false;
}
function createTable($table_name, $conn){
if(!checkDatabase($table_name, $conn)){
$sql = "CREATE TABLE `$table_name`(
id int(11) AUTO_INCREMENT PRIMARY KEY NOT NULL,
series_name varchar(255) NOT NULL,
tv_id varchar(255) NOT NULL,
status varchar(255) NOT NULL
);";
if(mysqli_query($conn, $sql)){
echo "Database Table Created!";
}else{
echo "Error!";
}
}else{
echo "Database Table Found!";
}
}
createTable("series", $conn_database);
?>
query.php (PHP)
<?php
error_reporting(0);
class query{
const ROOT = "https://api.tvmaze.com/shows/";
const PATH = "/episodesbydate?date=";
public $conn, $table_name;
public function define_user($conn, $table_name){
$this->conn = $conn;
$this->table_name = $table_name;
}
private function check_ep_release_date($API_ID, $series_name, $status){
$date = date("Y-m-d");
$URL = self::ROOT.$API_ID.self::PATH.$date;
if($get_content = json_decode(file_get_contents((String)$URL))){
if($status != "Released"){
echo "%".$series_name."%".$get_content[0]->season."%".$get_content[0]->number."%".$get_content[0]->name."%";
$sql = "UPDATE `$this->table_name` SET `status`='Released' WHERE series_name='$series_name'";
mysqli_query($this->conn, $sql);
}
}else{
if($status != "Pending"){
$sql = "UPDATE `$this->table_name` SET `status`='Pending' WHERE series_name='$series_name'";
mysqli_query($this->conn, $sql);
}
}
}
public function track_db_entries(){
$sql = "SELECT * FROM `$this->table_name` ORDER BY `id` DESC";
if($result = mysqli_query($this->conn, $sql)){
$check = mysqli_num_rows($result);
if($check > 0){
while($row = mysqli_fetch_assoc($result)){
$this->check_ep_release_date($row['tv_id'], $row['series_name'], $row['status']);
}
}else{
echo "No Entry Found!";
}
}else{
echo "No Database Found!";
}
}
}
// 연결 설정 및 테이블명 정의
$conn_database = mysqli_connect("localhost", "root", "bot", "tvseries");
$table = "series";
// 각 등록 시리즈/애니메이션 방영일 확인 객체 생성
$q = new query();
$q->define_user($conn_database, $table);
$q->track_db_entries();
?>
커스텀 파트 및 인클로저
gerber_ycEfXzZq6h.zip
tv-series-anime-episode-tracker_gCfN0nFiA8.zip
opening_songs_y2LCEw79tg.zip
tv_Iz2isO8nGI.c
music_SA2xR86cZw.c
회로도