Bラボデジタルサイネージ（短期ワークショップ用テキスト）

![](https://qiita-user-contents.imgix.net/https%3A%2F%2Fuser-images.githubusercontent.com%2F29194025%2F137117015-af05fe4f-7c32-4e38-876f-e153ff2f2f7d.png?ixlib=rb-4.0.0&auto=format&gif-q=60&q=75&w=1400&fit=max&s=2bbf46fb5c1d8dec16f2fcb5d089fde2)

https://github.com/ohayota/digital-signage-template

本記事は、[株式会社 函館ラボラトリ](http://www.hakolab.co.jp)が運営する「[Bラボ](http://www.hakolab.co.jp/b-lab/index.html)」における、大人向け「看板アプリ（デジタルサイネージ）を作るコース」用テキストです。
一からコードを書くわけではなく、あらかじめ用意したコードの一部を改変しながら、デジタルサイネージを作っていきましょう。

テンプレートプログラムは、下記GitHubリポジトリの`ShortTerm`ディレクトリに入っています。

https://github.com/ohayota/BLabo-digital-signage-sample/tree/main/ShortTerm


（11/29時点、本テキストは執筆中です）


# デジタルサイネージとは何か？

下記記事の「概要」をご参照ください。

https://qiita.com/ohayota/items/f4aa3e1b452060eb52ad#概要


# 必要なもの

下記記事の「必要なもの」をご参照ください。

https://qiita.com/ohayota/items/f4aa3e1b452060eb52ad#必要なもの


# 実装

あらかじめ途中まで実装した状態のプログラムを用意します（`ShortTerm`ディレクトリの中にあります）。
1つの機能をもつパーツ（モジュール）がすでに実装されていますが、現時点で実行しても各モジュールは表示されません。

https://github.com/ohayota/BLabo-digital-signage-sample

モジュールについての説明は、下記記事の「機能実現の方針」をご参照ください。

https://qiita.com/ohayota/items/41a746d2258673726541#機能実現の方針

ひとつひとつのモジュールが表示されるよう、プログラムを書いていきましょう。


# 電子部品の配置

モジュールのうち、下記のモジュールの機能を実現するために、ブレッドボード上に電子部品を配置します。

- 開店／閉店表示機能（OpenCloseRModule）用のスライドスイッチ
- 温度表示機能（TemperatureRModule）用の温度センサ
- 明るさ表示機能（BrightnessRModule）用の明るさセンサ

## スライドスイッチの配置

下記記事の「電子部品の配置」に従い、ブレッドボードに部品を配置してください。

https://qiita.com/ohayota/items/284b5c7e7101f602016f#電子部品の配置

## 温度センサの配置

下記記事の「電子部品の配置」に従い、ブレッドボードに部品を配置してください。

https://qiita.com/ohayota/items/386c27a021601fb3ccfd#電子部品の配置

## 明るさセンサの配置

下記記事の「電子部品の配置」に従い、ブレッドボードに部品を配置してください。

https://qiita.com/ohayota/items/76279927b4ef35eaf628#電子部品の配置


# モジュール表示、ページ切り替えの実装

1つの機能を持つ塊である「モジュール」の分類については、下記記事の「機能実現の方針」をご覧ください。

https://qiita.com/ohayota/items/41a746d2258673726541#機能実現の方針

これらのモジュールたちを画面に配置し、情報が表示されるようにします。


## モジュールの表示（1ページ目）

各モジュールを表示する関数は、`drawBusRModule()`のように、頭に「draw」とつく関数として用意されています。
モジュール表示用の関数は、いまは何も描画していない関数`drawModules()`内で呼び出します。

``` java:DigitalSignage.pde
void drawModules() {
  if (nowPageID == 0) {
    // ModuleやRModuleの描画用関数を呼び出す
  } else if (nowPageID == 1) {
    // ModuleやRModuleの描画用関数を呼び出す
  } else if (nowPageID == 2) {
    // ModuleやRModuleの描画用関数を呼び出す
  }
}
```

まずは1ページ目を作ってみましょう。
下図のような画面を作ります。

![0041.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/368192/afb4daa4-9ae8-ecb5-5d35-78d08df179ce.png)

### 「GridModule」でモジュール配置場所の基準線を表示

https://qiita.com/ohayota/items/df704f523b5aa4dbb180

![0003.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/368192/7d7f277c-c6dc-b500-8386-2b83b05d1144.png)

「GridModule」の描画用関数は、`M_Grid.pde`内の`drawGridModule()`として実装されています。
この関数を、`drawModules()`の`nowPageID=0`の部分で呼び出します。

``` java:DigitalSignage.pde
void drawModules() {
  if (nowPageID == 0) {
    drawGridModule(); /* 追加 */
  } else if (nowPageID == 1) {
    // ModuleやRModuleの描画用関数を呼び出す
  } else if (nowPageID == 2) {
    // ModuleやRModuleの描画用関数を呼び出す
  }
}
```


### 「PlaceholderModule」でモジュール配置場所に枠を表示

https://qiita.com/ohayota/items/f52a10660009794afd9f

![0008.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/368192/e07eb854-5d99-cba7-cfb8-1de632f0523a.png)

「PlaceholderModule」の描画用関数は、`M_Placeholder.pde`内の`drawPlaceholderModule()`として実装されています。
この関数を、`drawModules()`の`nowPageID=0`の部分で呼び出します。

``` java:DigitalSignage.pde
void drawModules() {
  if (nowPageID == 0) {
    drawGridModule();
    drawPlaceholderModule(); /* 追加 */
  } else if (nowPageID == 1) {
    // ModuleやRModuleの描画用関数を呼び出す
  } else if (nowPageID == 2) {
    // ModuleやRModuleの描画用関数を呼び出す
  }
}
```


### 「FullImageModule」で画面全体に画像を表示

https://qiita.com/ohayota/items/4a9c8423459c251f4911

![0006.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/368192/6fa2b0a6-7f02-c4bf-304c-b6959e1f443f.png)

「FullImageModule」の描画用関数は、`M_FullImage.pde`内の`drawFullImageModule()`として実装されています。
この関数を、`drawModules()`の`nowPageID=0`の部分で呼び出します。
かっこの中には、すでに読み込んである画像`background`を入れます。

``` java:DigitalSignage.pde
void drawModules() {
  if (nowPageID == 0) {
    drawFullImageModule(background); /* 追加 */
    drawGridModule();
    drawPlaceholderModule();
  } else if (nowPageID == 1) {
    // ModuleやRModuleの描画用関数を呼び出す
  } else if (nowPageID == 2) {
    // ModuleやRModuleの描画用関数を呼び出す
  }
}
```


### 「LocationModule」でデジタルサイネージ設置場所を表示

https://qiita.com/ohayota/items/046b361d945a709c4605

![0054.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/368192/824ffc46-ce30-7314-8b26-e9a1aacb17a9.png)

定数`LOCATION`に入っている文字列を表示します。

``` java:DigitalSignage.pde
// デジタルサイネージが設置されている場所や店舗などを書く。
final String LOCATION = "北海道函館市美原2丁目 / MIRAI BASE";
```

「LocationModule」の描画用関数は、`M_Location.pde`内の`drawLocationModule()`として実装されています。
この関数を、`drawModules()`の`nowPageID=0`の部分で呼び出します。

``` java:DigitalSignage.pde
void drawModules() {
  if (nowPageID == 0) {
    drawFullImageModule(background);
    drawGridModule();
    drawPlaceholderModule();
  } else if (nowPageID == 1) {
    // ModuleやRModuleの描画用関数を呼び出す
  } else if (nowPageID == 2) {
    // ModuleやRModuleの描画用関数を呼び出す
  }

  drawLocationModule(); /* 追加 */
}
```


### 「DateModule」で現在時刻を表示

https://qiita.com/ohayota/items/6b80137e33bfb4172de6

![0036.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/368192/d47a553d-ab7a-7358-3201-d4925223e932.png)

「DateModule」の描画用関数は、`M_Date.pde`内の`drawDateModule()`として実装されています。
この関数を、`drawModules()`の`nowPageID=0`の部分で呼び出します。

``` java:DigitalSignage.pde
void drawModules() {
  if (nowPageID == 0) {
    drawFullImageModule(background);
    drawGridModule();
    drawPlaceholderModule();
  } else if (nowPageID == 1) {
    // ModuleやRModuleの描画用関数を呼び出す
  } else if (nowPageID == 2) {
    // ModuleやRModuleの描画用関数を呼び出す
  }

  drawLocationModule();
  drawDateModule(); /* 追加 */
}
```


### 「ProgressBarModule」で次のページに切り替わるまでの時間を視覚化

https://qiita.com/ohayota/items/9f3b6f9f32b7d93ef2a7

![0038.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/368192/27a0c0be-92ff-3b97-f0fe-ff30d724a797.png)

「ProgressBarModule」の描画用関数は、`M_ProgressBar.pde`内の`drawProgressBarModule()`として実装されています。
この関数を、`drawModules()`の`nowPageID=0`の部分で呼び出します。

``` java:DigitalSignage.pde
void drawModules() {
  if (nowPageID == 0) {
    drawFullImageModule(background);
    drawGridModule();
    drawPlaceholderModule();
  } else if (nowPageID == 1) {
    // ModuleやRModuleの描画用関数を呼び出す
  } else if (nowPageID == 2) {
    // ModuleやRModuleの描画用関数を呼び出す
  }

  drawLocationModule();
  drawDateModule();
  drawProgressBarModule(); /* 追加 */
}
```


### 「PageControlModule」で現在表示中のページを視覚化

https://qiita.com/ohayota/items/23acc993dca47286fffb

![0020.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/368192/a2b5fa32-9d66-97c6-1975-34d39fa5c43c.png)

「PageControlModule」の描画用関数は、`M_PageControl.pde`内の`drawPageControlModule()`として実装されています。
この関数を、`drawModules()`の`nowPageID=0`の部分で呼び出します。

``` java:DigitalSignage.pde
void drawModules() {
  if (nowPageID == 0) {
    drawFullImageModule(background);
    drawGridModule();
    drawPlaceholderModule();
  } else if (nowPageID == 1) {
    // ModuleやRModuleの描画用関数を呼び出す
  } else if (nowPageID == 2) {
    // ModuleやRModuleの描画用関数を呼び出す
  }

  drawLocationModule();
  drawDateModule();
  drawProgressBarModule();
  drawPageControlModule(); /* 追加 */
}
```


### 「WeatherRModule」で現在の天気を表示

![0015.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/368192/bbba5465-6c2e-cad1-f346-05afe4c96d17.png)

https://qiita.com/ohayota/items/d9b1d21037dcedcfd78c

OpenWeatherのアカウントを取得していることが前提です。
下記記事の「必要な変数の定義」に従って、API Keyを取得します。

https://qiita.com/ohayota/items/d9b1d21037dcedcfd78c#必要な変数の定義

API Keyは、定数`WEATHER_API_KEY`へ代入してください。

``` java:DigitalSignage.pde
final String WEATHER_API_KEY = "取得したAPI Key";
```

WeatherRModuleを描画します。
描画時の基準となるエリアは`Area.area1`とします。

``` java:DigitalSignage.pde
void drawModules() {
  if (nowPageID == 0) {
    drawFullImageModule(background);
    drawGridModule();
    drawPlaceholderModule();
    drawWeatherRModule(Area.area1); /* 追加 */
  } else if (nowPageID == 1) {
    // ModuleやRModuleの描画用関数を呼び出す
  } else if (nowPageID == 2) {
    // ModuleやRModuleの描画用関数を呼び出す
  }

  drawLocationModule();
  drawDateModule();
  drawProgressBarModule();
  drawPageControlModule();
}
```


### 「BusRModule」でバス停の直近2件の時刻表を表示

![0023.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/368192/9fdb7e0b-010e-049f-f417-7e36d1fae0b2.png)

下記記事の「時刻表スプレッドシートの作成」の手順で得たURLを使い、バスの時刻表を取得します。
作成したAPIのURLを定数`BUS_API_URL`に代入します。

https://qiita.com/ohayota/items/93d7c9b5328188b3c5b6#時刻表スプレッドシートの作成

``` java:DigitalSignage.pde
final String BUS_API_URL = "取得したURL";
```

BusRModuleを描画します。
描画時の基準となるエリアは`Area.area3`とします。

``` java:DigitalSignage.pde
void drawModules() {
  if (nowPageID == 0) {
    drawFullImageModule(background);
    drawGridModule();
    drawPlaceholderModule();
    drawWeatherRModule(Area.area1);
    drawBusRModule(Area.area3); /* 追加 */
  } else if (nowPageID == 1) {
    // ModuleやRModuleの描画用関数を呼び出す
  } else if (nowPageID == 2) {
    // ModuleやRModuleの描画用関数を呼び出す
  }

  drawLocationModule();
  drawDateModule();
  drawProgressBarModule();
  drawPageControlModule();
}
```


### 「GomiRModule」で直近1週間のごみカレンダーを表示

![0004.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/368192/c7254053-68a1-d424-82ec-ce684e5e819e.png)

下記記事の「ごみカレンダースプレッドシートの作成」以降に従い、APIを作ってください。

https://qiita.com/ohayota/items/40ed5db352a688637f83

得られたAPIのURLを、定数`GOMI_API_URL`に代入します。

``` java:DigitalSignage.pde
final String GOMI_API_URL = "取得したURL";
```

GomiRModuleを描画します。
描画時の基準となるエリアは`Area.area5`とします。

``` java:DigitalSignage.pde
void drawModules() {
  if (nowPageID == 0) {
    drawFullImageModule(background);
    drawGridModule();
    drawPlaceholderModule();
    drawWeatherRModule(Area.area1);
    drawBusRModule(Area.area3);
    drawGomiRModule(Area.area5); /* 追加 */
  } else if (nowPageID == 1) {
    // ModuleやRModuleの描画用関数を呼び出す
  } else if (nowPageID == 2) {
    // ModuleやRModuleの描画用関数を呼び出す
  }

  drawLocationModule();
  drawDateModule();
  drawProgressBarModule();
  drawPageControlModule();
}
```


## ページ切り替えの実装

デジタルサイネージの画面を、10秒ごとに自動で切り替わるようにします。
下記記事の「ページ更新用の関数」に従い、関数`updatePageID()`を完成させてください。

https://qiita.com/ohayota/items/9d2c28f9cbf2f559be31#ページ更新用の関数


## モジュールの表示（2ページ目）

![0145.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/368192/a499d198-f9db-8534-5a9c-172b4481be67.png)


### 「TwitterRModule」で画像付きツイートを表示

![0013.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/368192/d9fd914f-1b34-12aa-1865-82bfbe421ac1.png)

Twitterアカウントを持っており、Developer Accountとしての申請がTwitter社に受理された状態が前提です。
下記記事の「ライブラリ導入」に従い、ライブラリ「Twitter4J」を導入してください。
Twitter APIで取得できる4つのキーを、下記記事「キーの取得」に従い取得してください。

https://qiita.com/ohayota/items/6367fad7beaf9ad9ea1c

各キーは定数に代入します。

``` java:DigitalSignage.pde
final String CONSUMER_KEY = "取得したAPI Key";
final String CONSUMER_KEY_SECRET = "取得したAPI Key Secret";
final String ACCESS_TOKEN = "取得したAccess Token";
final String ACCESS_TOKEN_SECRET = "取得したAccess Token Secret";
```

TwitterRModuleを描画します。
描画時の基準となるエリアは`Area.area3`とします。

``` java:DigitalSignage.pde
void drawModules() {
  if (nowPageID == 0) {
    drawFullImageModule(background);
    drawGridModule();
    drawPlaceholderModule();
    drawWeatherRModule(Area.area1);
    drawBusRModule(Area.area3);
    drawGomiRModule(Area.area5);
  } else if (nowPageID == 1) {
    drawFullImageModule(background);
    drawGridModule();
    drawPlaceholderModule();
    drawTwitterRModule(Area.area3); /* 追加 */
  } else if (nowPageID == 2) {
    // ModuleやRModuleの描画用関数を呼び出す
  }

  drawLocationModule();
  drawDateModule();
  drawProgressBarModule();
  drawPageControlModule();
}
```


### 「OpenCloseRModule」で開店／閉店を表示

![0189.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/368192/0143d64a-3b96-6a38-7bc3-cb2ea5d4e391.png)

![0010.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/368192/6725d799-ef5e-408e-e868-d9ef30014d49.png)

https://qiita.com/ohayota/items/284b5c7e7101f602016f

OpenCloseRModuleを描画します。
描画時の基準となるエリアは`Area.area5`とします。

``` java:DigitalSignage.pde
void drawModules() {
  if (nowPageID == 0) {
    drawFullImageModule(background);
    drawGridModule();
    drawPlaceholderModule();
    drawWeatherRModule(Area.area1);
    drawBusRModule(Area.area3);
    drawGomiRModule(Area.area5);
  } else if (nowPageID == 1) {
    drawFullImageModule(background);
    drawGridModule();
    drawPlaceholderModule();
    drawTwitterRModule(Area.area3);
    drawOpenCloseRModule(Area.area5); /* 追加 */
  } else if (nowPageID == 2) {
    // ModuleやRModuleの描画用関数を呼び出す
  }

  drawLocationModule();
  drawDateModule();
  drawProgressBarModule();
  drawPageControlModule();
}
```



### 「TemperatureRModule」で温度を表示

https://qiita.com/ohayota/items/386c27a021601fb3ccfd

![0011.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/368192/968bbcdb-4f8e-3c8a-1868-b6c75d9427fa.png)

TemperatureRModuleを描画します。
描画時の基準となるエリアは`Area.area1`とします。

``` java:DigitalSignage.pde
void drawModules() {
  if (nowPageID == 0) {
    drawFullImageModule(background);
    drawGridModule();
    drawPlaceholderModule();
    drawWeatherRModule(Area.area1);
    drawBusRModule(Area.area3);
    drawGomiRModule(Area.area5);
  } else if (nowPageID == 1) {
    drawFullImageModule(background);
    drawGridModule();
    drawPlaceholderModule();
    drawTemperatureRModule(Area.area1); /* 追加 */
    drawTwitterRModule(Area.area3);
    drawOpenCloseRModule(Area.area5);
  } else if (nowPageID == 2) {
    // ModuleやRModuleの描画用関数を呼び出す
  }

  drawLocationModule();
  drawDateModule();
  drawProgressBarModule();
  drawPageControlModule();
}
```


### 「BrightnessRModule」で明るさを表示

![0037.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/368192/6cd169b0-e54b-24dc-527d-96f6cd6224d0.png)

https://qiita.com/ohayota/items/76279927b4ef35eaf628

BrightnessRModuleを描画します。
描画時の基準となるエリアは`Area.area2`とします。

``` java:DigitalSignage.pde
void drawModules() {
  if (nowPageID == 0) {
    drawFullImageModule(background);
    drawGridModule();
    drawPlaceholderModule();
    drawWeatherRModule(Area.area1);
    drawBusRModule(Area.area3);
    drawGomiRModule(Area.area5);
  } else if (nowPageID == 1) {
    drawFullImageModule(background);
    drawGridModule();
    drawPlaceholderModule();
    drawTemperatureRModule(Area.area1);
    drawBrightnessRModule(Area.area2); /* 追加 */
    drawTwitterRModule(Area.area3);
    drawOpenCloseRModule(Area.area5);
  } else if (nowPageID == 2) {
    // ModuleやRModuleの描画用関数を呼び出す
  }

  drawLocationModule();
  drawDateModule();
  drawProgressBarModule();
  drawPageControlModule();
}
```


## 広告画像の表示

配列`adImage`に入っている画像を、`drawFullImageModule()`で描画します。

``` java:DigitalSignage.pde
void drawModules() {
  if (nowPageID == 0) {
    drawFullImageModule(background);
    drawGridModule();
    drawPlaceholderModule();
    drawWeatherRModule(Area.area1);
    drawBusRModule(Area.area3);
    drawGomiRModule(Area.area5);
  } else if (nowPageID == 1) {
    drawFullImageModule(background);
    drawGridModule();
    drawPlaceholderModule();
    drawTemperatureRModule(Area.area1);
    drawBrightnessRModule(Area.area2);
    drawTwitterRModule(Area.area3);
    drawOpenCloseRModule(Area.area5);
  } else if (nowPageID == 2) {
    drawFullImageModule(adImage[0]); /* 追加 */
  }

  drawLocationModule();
  drawDateModule();
  drawProgressBarModule();
  drawPageControlModule();
}
```


これでデジタルサイネージの実装は完了です。
もし余力があれば、各モジュールをカスタマイズしてみてください。

# デジタルサイネージを1から作ってみたい方へ

プログラムを段階的に書いていく形式の記事を用意しています（20本のシリーズです）。
下記の記事から読み始めてください。

https://qiita.com/ohayota/items/f4aa3e1b452060eb52ad