工業製造
産業用モノのインターネット | 工業材料 | 機器のメンテナンスと修理 | 産業プログラミング |
home  MfgRobots >> 工業製造 >  >> Manufacturing Technology >> 製造プロセス

TVシリーズ&アニメの放送日自動通知システム - Arduino Nano 33 IoTとRaspberry Piで構築

必要な部品と材料

JLCPCB カスタムPCB ×1
Arduino Nano 33 IoT ×1
Raspberry Pi 3 Model B / 4 Model B ×1
SparkFun Nokia 5110 LCDスクリーン ×1
シリアルMP3プレイヤーモジュール (OPEN-SMART) ×1
RGBディフューズド コモンアノードLED ×1
microSDカード ×2
抵抗 220Ω ×12
タクトスイッチ 6×6×5mm ×4
DCジャック 2.1mm 基板取付型 ×1

必要な工具と機器

アプリとオンラインサービス

このプロジェクトについて

概要

お気に入りの番組の放送日を追跡するWeb/モバイルアプリは数多く存在しますが、ハードウェア・ソフトウェアともにゼロから作り、様々な機能を持つ専用デバイスで通知を受け取りたいと考えました。

まず、PHPでWebアプリケーション「TVシリーズ/アニメ放送日トラッカー」を作成し、シリーズ/アニメのリストを作成して放送日情報を取得します。TVMAZE REST APIを利用し、放送日ごとにエピソード情報を正確に取得できます(煩雑なコーディングなしで)。このアプリを動かすため、Raspberry Pi上にLAMP(MariaDB)サーバーを構築しました。Raspberry Piを持たない、またはホストとして使いたくない方向けに、私のウェブサイトでユーザーフレンドリーなインターフェースを備えた無料版アプリも公開しています。ソースコードも以下で提供しています。

Webアプリとの情報連携後、新着エピソードがある場合、Arduino Nano 33 IoTを開発ボードとしてデータを収集します。アプリから返されるレスポンスをデリミタで分割して処理する方法を後述します。Nokia 5110スクリーンとプッシュボタンでメニューUIを実装し、機能とモードを管理します。

新着エピソードをオープニング曲付きで通知するため、シンプルなUARTコマンドで制御できるシリアルMP3プレイヤーモジュール(OPEN-SMART)を採用。さらにMP3プレイヤーモードを追加し、全オープニング曲を再生可能にすることで、部屋のインテリアとしても楽しめるデバイスになりました。

ブレッドボードで設計・テスト後、レトロな電話機形状のユニークなPCB「TVシリーズ/アニメ放送日通知機」をKiCadで設計し、見た目も使いやすさも兼ね備えたデバイスに仕上げました。

JLCPCB様にスポンサードいただきました。ありがとうございます。

TVシリーズ&アニメの放送日自動通知システム - Arduino Nano 33 IoTとRaspberry Piで構築

ステップ1: PCBの設計とはんだ付け

KiCadでPCBを設計。ガーバーファイルを添付しているので、JLCPCBで発注して同じプロジェクトを再現可能です。

TVシリーズ&アニメの放送日自動通知システム - Arduino Nano 33 IoTとRaspberry Piで構築

はんだごてでヘッダー、プッシュボタン、220Ω抵抗、DCジャック、5mmアノードRGB LEDを実装。

PCB上の部品構成:

TVシリーズ&アニメの放送日自動通知システム - Arduino Nano 33 IoTとRaspberry Piで構築
TVシリーズ&アニメの放送日自動通知システム - Arduino Nano 33 IoTとRaspberry Piで構築
TVシリーズ&アニメの放送日自動通知システム - Arduino Nano 33 IoTとRaspberry Piで構築

ステップ2: Raspberry PiでLAMP(MariaDB)Webサーバー構築

Raspberry Piプログラミング初心者は、公式サイトのよく説明された手順・チュートリアルを参照してください。公式チュートリアルはこちら

  1. ターミナルを開く(アクセサリ > ターミナル)
  2. Apache2インストール: sudo apt-get install apache2 -y
  3. ブラウザで https://localhost または hostname -I で表示されるIPアドレスにアクセスし、動作確認
  4. PHPインストール: sudo apt-get update && sudo apt-get install php -y
  5. MariaDBとPHP-MySQLインストール: sudo apt-get install mariadb-server php-mysql -y
  6. MySQLセキュアインストール実行: sudo mysql_secure_installation
    • rootパスワード設定: bot
    • 匿名ユーザー削除、リモートrootログイン無効、テストDB削除、権限テーブル再読み込み: すべてY
  7. データベース tvseries 作成:
    sudo mysql -uroot -p
    # パスワード: bot
    CREATE DATABASE tvseries;
    GRANT ALL PRIVILEGES ON tvseries.* TO 'root'@'localhost' IDENTIFIED BY 'bot';
    FLUSH PRIVILEGES;
    
  8. PHP cURL拡張インストール(TVMAZE APIアクセス用): sudo apt-get install php-curl
  9. Apache再起動: sudo service apache2 restart

ステップ3: TVMAZE REST APIでお気に入りシリーズ/アニメの情報取得

TVMAZEは無料で使いやすいREST APIを提供。番組名、日付、エピソード番号などで検索可能。TVMAZE REST API でエンドポイント確認可能。

「Episode by Date」オプションを使用し、指定番組の指定日付に放送されたエピソードを取得します。

該当エピソードがあれば詳細情報の配列が返り、なければHTTP 404が返ります。

本プロジェクトで追跡するシリーズ/アニメリスト例:

  1. One Piece - TV ID: 1505
  2. My Hero Academia - TV ID: 13615
  3. Westworld - TV ID: 1371
  4. The Simpsons - TV ID: 83
  5. The Late Late Show with James Corden - TV ID: 2831(毎日放送なのでテスト用)

ステップ4: Webアプリ「TVシリーズ/アニメ放送日トラッカー」開発(PHP)とシリーズテーブル作成

PHPでアプリを開発。コード解説は後述。PHPウェブ開発に慣れない方は、ダウンロードセクションの TV-Series-Anime-Episode-Tracker.zip をダウンロードし、Raspberry Piにアップロードするだけで利用可能です。

インストール手順

  1. TV-Series-Anime-Episode-Tracker.zip をダウンロード・解凍(index.php, query.php 含む)
  2. サーバーディレクトリに移動: sudo mv /home/pi/Downloads/TV-Series-Anime-Episode-Tracker/ /var/www/html/
  3. ブラウザで https://localhost/TV-Series-Anime-Episode-Tracker/ にアクセス

機能

  1. 初回アクセス時に series テーブルを自動作成(「Database Table Created!」表示)
  2. 2回目以降は「Database Table Found!」表示
  3. 各番組の情報(シリーズ名、TV ID、ステータス)をデータベースに登録:
    INSERT INTO `series` (`series_name`, `tv_id`, `status`) VALUES ('One Piece', '1505', 'Pending');
    
    ステータス変数で、指定日に新エピソードが放送された際の重複通知を防止
  4. query.php にアクセスすると、TVMAZE APIに問い合わせ、新着エピソードがあればArduinoでパース可能な形式で出力:
    %Series_Name%Season%Episode_Number%Episode_Name%
    %The Late Late Show%2020%91%Jason Schwartzman, Alicia Key%
    
  5. 新着なしの場合: No Entry Found!

コード解説

index.php
query.php

ステップ4.1: Raspberry PiなしでWebアプリを実行

私のウェブサイト(TheAmplituhedron)購読者向けに、Raspberry Pi不要で使える無料版アプリを提供。ユーザーフレンドリーなインターフェースでDB管理可能。利用希望の方はご連絡ください。

アプリケーションURL

ステップ5: Arduino Nano 33 IoTのセットアップ

Arduino Nano 33 IoT初心者も安心。Arduino IDEで簡単に使用可能。必要なドライバー(Arduino SAMD Core)はこちらで解説。

WiFi接続には WiFiNINAライブラリ が必要。

Nokia 5110スクリーン描画には LCD5110_Basicライブラリ を使用。

シリアルMP3プレイヤー通信にはArduino Nano 33 IoTの第2シリアルポート(Serial1、RX0/TX1ピン)を使用。

ステップ6: シリアルMP3プレイヤー(OPEN-SMART)へのコマンド送信

お気に入りシリーズ/アニメのオープニング曲プレイリストを作成:

  1. One Piece - Opening
  2. My Hero Academia - Opening
  3. Westworld - Opening
  4. The Simpsons - Opening
  5. The Late Late Show - Opening

プレイリストは opening_songs.zip でダウンロード可能。

OPEN-SMART製シリアルMP3プレイヤーモジュール(スピーカー付き)を使用。他ブランドではコマンドが異なる場合があります。

microSDカードはFAT16/FAT32でフォーマットし、01 フォルダを作成、曲名を 001xxx.mp3, 002xxx.mp3... の連番で保存。

ArduinoからUARTコマンド送信用関数 send_command_to_MP3_player を作成。第2シリアルポート(Serial1)経由でコマンドバイト列を送信。

コマンド形式: 7E で開始、EF で終了。第2バイトは間のバイト数。詳細はOPEN-SMARTマニュアル参照。

ステップ7: Arduino Nano 33 IoTのプログラミング

接続と調整

// 接続ピン配置
// Arduino Nano 33 IoT:
// Nokia 5110 Screen
// 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 (Nano用オプション、Nano 33 IoTでは不要)
// A4 --------------------------- SDA
// A5 --------------------------- SCL

コードアップロード後、すべての部品をヘッダー経由で接続。Arduino Nano用にDS3231 RTCモジュール接続スロットも用意(RTCでカウントダウンタイマー作成可能だが、Webアプリからデータ取得できず手動で放送日入力が必要)。

TVシリーズ&アニメの放送日自動通知システム - Arduino Nano 33 IoTとRaspberry Piで構築

スピーカーまたはヘッドホンをシリアルMP3プレイヤーに接続可能。付属スピーカーを使用。

TVシリーズ&アニメの放送日自動通知システム - Arduino Nano 33 IoTとRaspberry Piで構築
TVシリーズ&アニメの放送日自動通知システム - Arduino Nano 33 IoTとRaspberry Piで構築

モードと機能

1. WiFi接続中

「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 ローカルホスト(Raspberry Pi)接続試行。成功なら「Connected to the server!!!」、失敗なら「Connection Error!!!」

A.2 新着エピソード検出時:

色割り当て例:

終了ボタン押下まで表示・再生継続。各エピソード1回のみ通知(重複防止)。

A.3 終了ボタン押下後、TVアイコン表示しながら次回リクエスト待機

A.4 新着なし時: 「No Released Episode Detected :(」表示後、TVアイコン表示して待機

A.5 TVアイコン表示中に終了ボタンでメニュー復帰

B. MP3 Player モード

サブ機能として実装、部屋のアクセサリーとしても楽しめる

終了ボタンでメニュー復帰

C. Sleep モード

10秒後にスリープ突入、残り秒数表示。スリープ中はRGBでナイトライト風カラーパターン点灯。終了ボタンで復帰。

動画と結び

ホットグルーで本棚に固定。見事に動作します!

TVシリーズ&アニメの放送日自動通知システム - Arduino Nano 33 IoTとRaspberry Piで構築
TVシリーズ&アニメの放送日自動通知システム - Arduino Nano 33 IoTとRaspberry Piで構築

コード

Series_Release_Date_Notifier.ino (Arduino)

////////////////////////////////////////////////////
// TV Series / Anime New Episode
// Release Date Notifier
// -------------------------
// Arduino Nano 33 IoT
// by Kutluhan Aktar
////////////////////////////////////////////////////
// お気に入り番組の新着エピソードをオープニング曲付きで通知
// 補完Webアプリ「TV Series / Anime Release Date Tracker」をPHPで開発
// Raspberry Piをサーバーとして使用、またはTheAmplituhedron会員ならリアルタイムDBインターフェース利用可能
// 詳細: https://www.theamplituhedron.com/projects/TV-Series-Anime-New-Episode-Release-Date-Notifier/
// 無料版Webアプリ(会員向け): https://www.theamplituhedron.com/dashboard/TV-Series-Anime-Episode-Tracker/

#include <SPI.h>
#include <WiFiNINA.h>
#include <LCD5110_Basic.h>

char ssid[] = "[_SSID_]";
char pass[] = "[_PASSWORD_]";
int keyIndex = 0;
int status = WL_IDLE_STATUS;

// Raspberry PiのIPアドレス
IPAddress server(192, 168, 1, 22);
// TheAmplituhedron使用時は以下を使用
// char server[] = "www.theamplituhedron.com";

String application = "/TV-Series-Anime-Episode-Tracker/query.php";
// TheAmplituhedron使用時:
// String HEDRON = "[_HEDRON_]";
// String application = "/dashboard/TV-Series-Anime-Episode-Tracker/" + HEDRON;

WiFiClient client;
// WiFiSSLClient client; // SSL使用時

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 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

#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);
  pinMode(redPin, OUTPUT);
  pinMode(greenPin, OUTPUT);
  pinMode(bluePin, OUTPUT);
  adjustColor(0, 0, 0);
  
  myGLCD.InitLCD();
  myGLCD.setFont(SmallFont);
  Serial.begin(9600);
  Serial1.begin(9600);
  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);
    status = WiFi.begin(ssid, pass);
    delay(10000);
  }
  
  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) {
          if (client.connect(server, 80)) {
            Serial.println("connected to server");
            myGLCD.print("Connected to", 0, 8);
            myGLCD.print("the server!!!", 0, 16);
            client.println("GET " + application + " HTTP/1.1");
            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);
          
          String response = "";
          while (client.available()) {
            char c = client.read();
            response += c;
          }
          
          if(response != "" && response.endsWith("%")) {
            int delimiter = response.indexOf("%");
            int delimiter_1 = response.indexOf("%", delimiter + 1);
            int delimiter_2 = response.indexOf("%", delimiter_1 + 1);
            int delimiter_3 = response.indexOf("%", delimiter_2 + 1);
            int 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);
            }
            
            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);
            myGLCD.clrScr();
          }
          
          myGLCD.drawBitmap(8, 0, tv, 60, 48);
          delay(10 * 1000);
          myGLCD.clrScr();
          
          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);
          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);
          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) {
        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);
          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 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();
?>

カスタムパーツとエンクロージャ

回路図

TVシリーズ&アニメの放送日自動通知システム - Arduino Nano 33 IoTとRaspberry Piで構築
TVシリーズ&アニメの放送日自動通知システム - Arduino Nano 33 IoTとRaspberry Piで構築
TVシリーズ&アニメの放送日自動通知システム - Arduino Nano 33 IoTとRaspberry Piで構築
TVシリーズ&アニメの放送日自動通知システム - Arduino Nano 33 IoTとRaspberry Piで構築

製造プロセス

  1. LIRCでプロフェッショナルなRaspberry Piユニバーサルリモコンを構築
  2. Raspberry PiでRFをDCに変換: エンベロープ検波器の構築とトラブルシューティング
  3. Cycle Chaser: Raspberry Piで自転車をナイトタイムライトショーに変身
  4. Raspberry Piベースの泥棒検知器を構築 - ステップバイステップガイド
  5. Raspberry Pi、MCP9808、InfluxDB、Grafanaで作る家庭温度モニター
  6. Raspberry Piで構築するホームオートメーションシステム:リモート制御対応
  7. Raspberry Pi 3 B+ – アップデートされた機能、仕様、購入ガイド
  8. DIY Wall-E 風 Raspberry Pi CD ボックスロボット
  9. RasPiRobot Board V2:Raspberry Pi を強力なロボットコントローラーに拡張
  10. Windows IoT Core搭載RoverをRaspberry Pi 2で構築 - 初心者から上級者まで
  11. Schaffner、医療・民生用途向けIEC C14 PEM搭載の高性能FN 9262/9266 RFIフィルターを発表