【Bラボデジタルサイネージ18】部屋の明るさを表示する「BrightnessRModule」

![](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/BLabo-digital-signage

本記事は、[株式会社 函館ラボラトリ](http://www.hakolab.co.jp)が運営する「[Bラボ](http://www.hakolab.co.jp/b-lab/index.html)」における、大人向け「看板アプリ（デジタルサイネージ）を作るコース」用教材テキストです。

http://www.hakolab.co.jp/b-lab/index.html

1. [【Bラボデジタルサイネージ1】Processing+ラズパイでデジタルサイネージを作る！](https://qiita.com/ohayota/items/f4aa3e1b452060eb52ad)
2. [【Bラボデジタルサイネージ2】ラズパイ初期設定](https://qiita.com/ohayota/items/88fbcf177e6606311efb)
3. [【Bラボデジタルサイネージ3】準備プログラム作成＆機能実現の方針決定](https://qiita.com/ohayota/items/41a746d2258673726541)
4. [【Bラボデジタルサイネージ4】レイアウトの基準になるグリッドを表示する「GridModule」](https://qiita.com/ohayota/items/df704f523b5aa4dbb180)
5. [【Bラボデジタルサイネージ5】レイアウトの基準になる枠を表示する「PlaceholderModule」](https://qiita.com/ohayota/items/f52a10660009794afd9f)
6. [【Bラボデジタルサイネージ6】画像を全画面表示する「FullImageModule」](https://qiita.com/ohayota/items/4a9c8423459c251f4911)
7. [【Bラボデジタルサイネージ7】ページの自動切り替え](https://qiita.com/ohayota/items/9d2c28f9cbf2f559be31)
8. [【Bラボデジタルサイネージ8】設置されている場所の名前を表示する「LocationModule」](https://qiita.com/ohayota/items/046b361d945a709c4605)
9. [【Bラボデジタルサイネージ9】現在の時間を表示する「DateModule」](https://qiita.com/ohayota/items/6b80137e33bfb4172de6)
10. [【Bラボデジタルサイネージ10】ページ切り替えの時間が分かる「ProgressBarModule」](https://qiita.com/ohayota/items/9f3b6f9f32b7d93ef2a7)
11. [【Bラボデジタルサイネージ11】現在の表示中のページが分かる「PageControlModule」](https://qiita.com/ohayota/items/23acc993dca47286fffb)
12. [【Bラボデジタルサイネージ12】現在の天気を表示する「WeatherRModule」](https://qiita.com/ohayota/items/d9b1d21037dcedcfd78c)
13. [【Bラボデジタルサイネージ13】直近2件のバス時刻表を表示する「BusRModule」](https://qiita.com/ohayota/items/93d7c9b5328188b3c5b6)
14. [【Bラボデジタルサイネージ14】ごみ出しカレンダーを表示する「GomiRModule」](https://qiita.com/ohayota/items/40ed5db352a688637f83)
15. [【Bラボデジタルサイネージ15】（発展編）ツイートを表示する「TwitterRModule」](https://qiita.com/ohayota/items/6367fad7beaf9ad9ea1c)
16. [【Bラボデジタルサイネージ16】開店／閉店を表示する「OpenCloseRModule」](https://qiita.com/ohayota/items/284b5c7e7101f602016f)
17. [【Bラボデジタルサイネージ17】部屋の温度を表示する「TemperatureRModule」](https://qiita.com/ohayota/items/386c27a021601fb3ccfd)
18. **【Bラボデジタルサイネージ18】部屋の明るさを表示する「BrightnessRModule」（本記事）**
19. [【Bラボデジタルサイネージ19】起動画面を表示する「LaunchingScreenModule」](https://qiita.com/ohayota/items/0b6bdeda68c36707f762)
20. [【Bラボデジタルサイネージ20】RModuleの影を実装](https://qiita.com/ohayota/items/57242f18653ccf7eb83b)


# 作るもの

明るさのセンサ「CdSセル」を使い、明るさを計測できるようにします。

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



# 画像の準備

BrightnessRModuleの背景画像には、下の画像を使用します。

![background_bright.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/368192/795d68b4-b1fe-13c9-7bfd-115bfdf00eee.jpeg)

画像を2枚保存し、それぞれ「background_bright.jpg」、「background_not_bright.jpg」という名前をつけます。
（.png形式など別の形式の場合は、.jpg形式に変換してください。）

Processingの画面上のタブで、「スケッチ > スケッチフォルダーを開く」を選ぶと、開いている.pdeファイルが一覧で表示されます。
「data」フォルダの中に、新たに「brightness」フォルダを作ってください。
先ほど名前をつけて保存した「background_bright.jpg」「background_not_bright.jpg」を「data/brightness」フォルダの直下においてください。（ドラッグ＆ドロップ）

ここで新しく作った「data/brightness」フォルダの位置を示すパスを、定数として定義します。

``` java:DigitalSignage.pde
/* 略 */

final String AD_PATH = "ad/";
final String WEATHER_PATH = "weather/";
final String BUS_PATH = "bus/";
final String GOMI_PATH = "gomi/";
final String TWITTER_PATH = "twitter/";
final String OPENCLOSE_PATH = "openclose/"
final String TEMPERATURE_PATH = "temperature/";
final String BRIGHTNESS_PATH = "brightness/"; /* 追加 */
final String DUMMY_PATH = "dummy/";

/* 略 */
```

BrightnessRModuleの背景を保持する変数を定義します。

``` java:DigitalSignage.pde
/* 略 */

/* ここから追加 */
// BrightnessRModule
PGraphics brightnessBackgroundBright;
PGraphics brightnessBackgroundNotBright;
/* ここまで追加 */

/* 略 */
```

RModuleのリストに、BrightnessRModuleを表す「Brightness」を追加します。

``` java:DigitalSignage.pde
/* 略 */

enum RModule {
  Weather,
  Bus,
  Gomi,
  Twitter,
  OpenClose,
  Temperature,
  Brightness /* 追加 */
}

/* 略 */
```

`background.jpg`を用いて、モジュールの背景画像を生成します。
`initializeRModuleBackground()`内で実装します。

``` java:Initialize.pde
/* 略 */

void initializeRModuleBackground() {
  /* 中略 */
  temperatureBackground.rect(0, 0, w, h);
  temperatureBackground.endDraw();
  temperatureBackground.mask( sizeToModuleMask( moduleSize(module) ) );

  /* ここから追加 */
  module = RModule.Brightness;
  w = moduleWidth( moduleSize(module) );
  h = moduleHeight( moduleSize(module) );
  back = loadImage(BRIGHTNESS_PATH + "background_bright.jpg");
  brightnessBackgroundBright = createGraphics(w, h);
  brightnessBackgroundBright.beginDraw();
  brightnessBackgroundBright.colorMode(HSB, 360, 100, 100, 100);
  brightnessBackgroundBright.image( pImageCut(back, CENTER, CENTER, w, h) , 0, 0);
  brightnessBackgroundBright.fill(0, 0, 0, 40);
  brightnessBackgroundBright.noStroke();
  brightnessBackgroundBright.rect(0, 0, w, h);
  brightnessBackgroundBright.endDraw();
  brightnessBackgroundBright.mask( sizeToModuleMask( moduleSize(module) ) );
  
  back = loadImage(BRIGHTNESS_PATH + "background_not_bright.jpg");
  brightnessBackgroundNotBright = createGraphics(w, h);
  brightnessBackgroundNotBright.beginDraw();
  brightnessBackgroundNotBright.colorMode(HSB, 360, 100, 100, 100);
  brightnessBackgroundNotBright.image( pImageCut(back, CENTER, CENTER, w, h) , 0, 0);
  brightnessBackgroundNotBright.fill(0, 0, 0, 40);
  brightnessBackgroundNotBright.noStroke();
  brightnessBackgroundNotBright.rect(0, 0, w, h);
  brightnessBackgroundNotBright.endDraw();
  brightnessBackgroundNotBright.mask( sizeToModuleMask( moduleSize(module) ) );
  /* ここまで追加 */

/* 略 */
```


# BrightnessRModule用関数と変数の準備

新しいファイル`RM_Brightness.pde`を作ります。
BrightnessRModuleの描画用関数`drawBrightnessRModule()`、温度取得用の関数`updateBrightness()`を作ります。

``` java:RM_Brightness.pde
void drawBrightnessRModule(Area area) {
  
}

boolean updateBrightness() {
  return false;
}
```

値が更新されたことを示す変数、一定の基準値以上の明るさかを示す変数、明るさを0〜100%で示した変数を定義します。

``` java:DigitalSignage.pde
/* 略 */

// BrightnessRModule
SPI spi; /* 追加 */
boolean isBright = true; /* 追加 */
boolean isUpdatedBrightness = false; /* 追加 */
float brightnessRate = 0.0; /* 追加 */
PGraphics brightnessBackgroundBright;
PGraphics brightnessBackgroundNotBright;

/* 略 */
```

CdSセルからの値を取得するため、`SPI`という通信方式を使用します（本記事では詳細を割愛します）。

https://www.lapis-tech.com/jp/common/miconlp/tips/2-18-spiとは/


# 電子部品の配置

**電子部品を組み替えるときは、ラズパイの電源を切り、電源ケーブルをコンセントから抜いてください。**

以降、ラズパイと電子部品を接続するときのピンの番号は、下記サイトのGPIOピンの配置カードを参考にしてください。

http://herb.h.kobe-u.ac.jp/raspiinfo/gpio_pins_card.html

CdSセルと330Ω抵抗、ジャンパワイヤを下図のように配置します。

![IMG_2207.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/368192/d01eac74-47df-ac33-977b-50635c7d0af0.jpeg)

ラズパイで値を取得するには、CdSセルを直接つなぐだけでなく、A/D変換した値を取り出す必要があります。
そこで、A/D変換用の電子部品「MCP3002」を使います。

MCP3002とジャンパワイヤを下図のように配置してください。
（**MCP3002の上下を間違えないようにしてください。ショートして起動しなくなります。**）

![IMG_2208.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/368192/f22a4903-8a58-0a84-d9dd-a4c508b166cd.jpeg)

ラズパイのGPIOピンに接続するために、ジャンパワイヤを4本接続します。

- 左の上から1番目のピンは、19番ピン（GPIO10）に接続します。
- 左の上から2番目のピンは、21番ピン（GPIO09）に接続します。
- 左の上から3番目のピンは、23番ピン（GPIO11）に接続します。
- 右の上から4番目のピンは、24番ピン（GPIO08）に接続します。

![IMG_2209.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/368192/86f63e96-844c-9c20-3f96-3a73d38108d4.jpeg)

これで電子部品の配置が完了しました。


# CdSセルの値取得

関数`setup()`内で、SPI通信で入力を受け取るための設定をします。
接続されているデバイスのリストから、一番最初に出てくるデバイスの情報を`spi`という変数に入れておきます。

``` java:DigitalSignage.pde
/* 略 */

void setup() {
  frameRate(1);
  noCursor();
  colorMode(HSB, 360, 100, 100, 100);
  WHITE_COLOR = color(0, 0, 100);
  NEARLY_WHITE_COLOR = color(100, 2, 98);
  NEARLY_GREEN_COLOR = color(100, 5, 98);
  BLACK_COLOR = color(0, 0, 0);
  LIGHT_COLOR = color(0, 0, 80);
  GRAY_COLOR = color(0, 0, 50);
  GREEN_COLOR = color(150, 100, 60);
  
  textFont(createFont("NotoSansCJKjp-Bold", 32));
  
  background = pImageCut(loadImage("background.jpg"), CENTER, CENTER, width, height);
  
  GPIO.pinMode(SWITCH_PIN, GPIO.INPUT);
  i2c = new I2C(I2C.list()[0]);
  spi = new SPI(SPI.list()[0]); /* 追加 */
  spi.settings(500000, SPI.MSBFIRST, SPI.MODE0); /* 追加 */

  initialize();
}

/* 略 */
```

関数`updateBrightness()`内で、GPIOピンに送られてきた値を取得する処理を記述します。
MCP3002とのSPI通信を開始するとき、ラズパイからMCP3002に2byteの値を送ります。
その後に2byteの値が得られます。
2byteに含まれる16bitのうち、明るさの値としては、右から10bitの値（10進数になおすと、0〜1023）を使います。

得られた明るさの値が300以上のとき、「明るい」（`isBright: true`）という判定にします。

明るさの値は、割合（0〜100%）に換算します。

``` java:RM_Brightness.pde
boolean updateBrightness() {
  try {
    byte[] out = { byte(0x68), byte(0x00) };
    byte[] in = spi.transfer(out);
    int brightnessValue = ((in[0] << 8) + in[1]) & 0x3FF;
    isBright = (300 <= brightnessValue);
    brightnessRate = brightnessValue*100 / 1023.0;
    println("updateBrightness(): brightnessValue=" + brightnessValue,
              "brightnessRate=" + brightnessRate + "%",
              "isBright=" + isBright);
  } catch (Exception e) {
    return false;
  }
  
  return true;
}
```

関数`updateBrightness()`が実装できたので、この関数を呼び出すようにします。
温度センサから値を取得するタイミングは、以下の2つとします。

- デジタルサイネージを起動したとき
- 毎秒

``` java:Initialize.pde
void initialize() {
  initializeDate();
  initializeImage();
  initializeGrid();
  initializePlaceholder();
  initializeRModuleBackground();
  
  isUpdatedWeather = updateWeather();
  isUpdatedBus = updateBus();
  isUpdatedGomi = updateGomi();
  isUpdatedTwitter = updateTwitter();
  
  isUpdatedOpenClose = updateOpenClose();
  isUpdatedTemperature = updateTemperature();
  isUpdatedBrightness = updateBrightness(); /* 追加 */
}

/* 略 */
```

``` java:DigitalSignage.pde
/* 略 */

void updateDatas() {
  updateDate();
  isUpdatedOpenClose = updateOpenClose();
  isUpdatedTemperature = updateTemperature();
  isUpdatedBrightness = updateBrightness(); /* 追加 */

  /* 略 */
```

# BrightnessRModuleの描画

データが変数に代入できている状態になったので、画面上に表示するところまで実装します。

置換可能な**ReplaceableModule**を実装するとき、最低限定めなければいけないことは以下です。

- 描画時の基準となるエリア（Area.area1〜Area.area8のいずれか）
- 描画時のサイズ（Size.S〜Size.Lのいずれか）

まずはBrightnssRModuleの描画時の基準となるエリアについて説明します。
BrightnessRModuleの描画の基準になるのは、8つある表示エリアのうち`Area.area2`です。

![0038e.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/368192/fbc1ffa1-5fa7-67eb-caee-6d9a0d1efa53.png)

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

関数`moduleSize()`をBrightnessRModuleに対応させます。
BrightnessRModuleのサイズは`Size.S`です。

``` java:DigitalSignage.pde
Size moduleSize(RModule module) {
  if (module == RModule.Weather) return Size.M;
  if (module == RModule.Bus) return Size.L;
  if (module == RModule.Gomi) return Size.M;
  if (module == RModule.Twitter) return Size.L;
  if (module == RModule.OpenClose) return Size.M;
  if (module == RModule.Temperature) return Size.S;
  if (module == RModule.Brightness) return Size.S; /* 追加 */
  return Size.S;
}
```

次に関数`drawBrightnessRModule()`を実装します。
`Area.area2`の左上の座標、BrightnessRModuleの幅と高さを取得します。

``` java:RM_Brightness.pde
void drawBrightnessRModule(Area area) {
  RModule module = RModule.Brightness;
  Size size = moduleSize(module);
  
  int x = layoutGuideX(area);
  int y = layoutGuideY(area);
  int w = moduleWidth(size);
  int h = moduleHeight(size);

/* 略 */
```

モジュールの背景と、明るさの値を表示します。

``` java:RM_Brightness.pde
void drawBrightnessRModule(Area area) {
  RModule module = RModule.Brightness;
  Size size = moduleSize(module);
  
  int x = layoutGuideX(area);
  int y = layoutGuideY(area);
  int w = moduleWidth(size);
  int h = moduleHeight(size);
  
  /* ここから追加 */
  if (isBright) {
    image(brightnessBackgroundBright, x, y, w, h);
  } else {
    image(brightnessBackgroundNotBright, x, y, w, h);
  }

  if (isUpdatedBrightness) {
    drawText(LEFT, BASELINE, WHITE_COLOR, 32, "明るさ", x+50, y+50);
    // 温度表示
    drawText(CENTER, BASELINE, WHITE_COLOR, 96, int(brightnessRate)+"%", x+w/2, y+150);
  } else {
    fill(0, 0, 0, 50);
    noStroke();
    rect(x, y, w, h, MODULE_RECT_ROUND);
    
    drawText(CENTER, CENTER, WHITE_COLOR, 24, "BrightnessModule\nデータを取得できません", x+w/2, y+h/2);
  }
  /* ここまで追加 */
}
```

関数`drawBrightnessRModule()`が実装できたので、関数`drawModules()`内で呼び出します。

``` 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]);
  }
  drawDateModule();
  drawLocationModule();
  drawProgressBarModule();
  drawPageControlModule();
}

/* 略 */
```

実行してみると、明るさが表示されるようになっているはずです。
CdSセルを手で覆うと、値の変化が確認できるでしょう。

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


# 最後に

これで、部屋の明るさを表示する「BrightnessRModule」が実装できました。
ReplaceableModuleは、BrightnessRModuleをもって全て完成しました！
次は、**[起動画面を表示する「LaunchingScreenModule」](https://qiita.com/ohayota/items/0b6bdeda68c36707f762)**を作ってみましょう。

https://qiita.com/ohayota/items/0b6bdeda68c36707f762