本記事は、株式会社 函館ラボラトリが運営する「Bラボ」における、大人向け「看板アプリ(デジタルサイネージ)を作るコース」用教材テキストです。
- 【Bラボデジタルサイネージ1】Processing+ラズパイでデジタルサイネージを作る!
- 【Bラボデジタルサイネージ2】ラズパイ初期設定
- 【Bラボデジタルサイネージ3】準備プログラム作成&機能実現の方針決定
- 【Bラボデジタルサイネージ4】レイアウトの基準になるグリッドを表示する「GridModule」
- 【Bラボデジタルサイネージ5】レイアウトの基準になる枠を表示する「PlaceholderModule」
- 【Bラボデジタルサイネージ6】画像を全画面表示する「FullImageModule」
- 【Bラボデジタルサイネージ7】ページの自動切り替え
- 【Bラボデジタルサイネージ8】設置されている場所の名前を表示する「LocationModule」
- 【Bラボデジタルサイネージ9】現在の時間を表示する「DateModule」
- 【Bラボデジタルサイネージ10】ページ切り替えの時間が分かる「ProgressBarModule」
- 【Bラボデジタルサイネージ11】現在の表示中のページが分かる「PageControlModule」
- 【Bラボデジタルサイネージ12】現在の天気を表示する「WeatherRModule」
- 【Bラボデジタルサイネージ13】直近2件のバス時刻表を表示する「BusRModule」
- 【Bラボデジタルサイネージ14】ごみ出しカレンダーを表示する「GomiRModule」
- 【Bラボデジタルサイネージ15】(発展編)ツイートを表示する「TwitterRModule」
- 【Bラボデジタルサイネージ16】開店/閉店を表示する「OpenCloseRModule」
- 【Bラボデジタルサイネージ17】部屋の温度を表示する「TemperatureRModule」(本記事)
- 【Bラボデジタルサイネージ18】部屋の明るさを表示する「BrightnessRModule」
- 【Bラボデジタルサイネージ19】起動画面を表示する「LaunchingScreenModule」
- 【Bラボデジタルサイネージ20】RModuleの影を実装
作るもの
温度センサ「ADT7410」を使い、温度を計測をできるようにします。
画像の準備
TemperatureRModuleの背景画像には、下の画像を使用します。
画像を保存し、それぞれ「background.jpg」という名前をつけます。
(.png形式など別の形式の場合は、.jpg形式に変換してください。)
Processingの画面上のタブで、「スケッチ > スケッチフォルダーを開く」を選ぶと、開いている.pdeファイルが一覧で表示されます。
「data」フォルダの中に、新たに「temperature」フォルダを作ってください。
先ほど名前をつけて保存した「background.jpg」を「data/temperature」フォルダの直下においてください。(ドラッグ&ドロップ)
ここで新しく作った「data/temperature」フォルダの位置を示すパスを、定数として定義します。
/* 略 */
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 DUMMY_PATH = "dummy/";
/* 略 */
OpenCloseRModuleの背景を保持する変数を定義します。
/* 略 */
/* ここから追加 */
// TemperatureRModule
PGraphics temperatureBackground;
/* ここまで追加 */
/* 略 */
RModuleのリストに、OpenCloseRModuleを表す「OpenClose」を追加します。
/* 略 */
enum RModule {
  Weather,
  Bus,
  Gomi,
  Twitter,
  OpenClose,
  Temperature /* 追加 */
}
/* 略 */
background.jpgを用いて、モジュールの背景画像を生成します。
initializeRModuleBackground()内で実装します。
/* 略 */
void initializeRModuleBackground() {
  /* 中略 */
  openCloseBackgroundClose.rect(0, 0, w, h);
  openCloseBackgroundClose.endDraw();
  openCloseBackgroundClose.mask( sizeToModuleMask( moduleSize(module) ) );
  /* ここから追加 */
  module = RModule.Temperature;
  w = moduleWidth( moduleSize(module) );
  h = moduleHeight( moduleSize(module) );
  back = loadImage(TEMPERATURE_PATH + "background.jpg");
  temperatureBackground = createGraphics(w, h);
  temperatureBackground.beginDraw();
  temperatureBackground.colorMode(HSB, 360, 100, 100, 100);
  temperatureBackground.image( pImageCut(back, CENTER, CENTER, w, h) , 0, 0);
  temperatureBackground.fill(0, 0, 0, 40);
  temperatureBackground.noStroke();
  temperatureBackground.rect(0, 0, w, h);
  temperatureBackground.endDraw();
  temperatureBackground.mask( sizeToModuleMask( moduleSize(module) ) );
  /* ここまで追加 */
/* 略 */
TemperatureRModule用関数と変数の準備
新しいファイルRM_Temperature.pdeを作ります。
TemperatureRModuleの描画用関数drawTemperatureRModule()、温度取得用の関数updateTemperature()を作ります。
void drawTemperatureRModule(Area area) {
  
}
boolean updateTemperature() {
  return false;
}
温度(摂氏)を表す変数、値が更新されたことを示す変数を定義します。
/* 略 */
// TemperatureRModule
I2C i2c; /* 追加 */
boolean isUpdatedTemperature = false; /* 追加 */
float roomTempValue = 20.0; /* 追加 */
PGraphics temperatureBackground;
/* 略 */
温度センサの値を取得するため、I2Cという通信方式を使用します(本記事では詳細を割愛します)。
電子部品の配置
電子部品を組み替えるときは、ラズパイの電源を切り、電源ケーブルをコンセントから抜いてください。
以降、ラズパイと電子部品を接続するときのピンの番号は、下記サイトのGPIOピンの配置カードを参考にしてください。
まずブレッドボードへADT7410を配置します(4本のピンがはんだ付けされている前提です)。
ADT7410のピンの横には、3文字のアルファベットが印字されています。
「VDD」は、ブレッドボードの「+」に接続します。
「SCL」は、ラズパイの5番ピン(GPIO03、I2C)に接続します。
「SDA」は、ラズパイの3番ピン(GPIO02、I2C)に接続します。
「GND」は、ブレッドボードの「-」に接続します。
これで電子部品の配置が完了しました。
温度センサの値取得
関数setup()内で、I2Cで入力を受け取るための設定をします。
接続されているデバイスのリストから、一番最初に出てくるデバイスの情報をi2cという変数に入れておきます。
/* 略 */
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]); /* 追加 */
  
  initialize();
}
/* 略 */
関数updateTemperature()内で、GPIOピンに送られてきた値を取得する処理を記述します。
温度センサとの通信を開始するときにbeginTransmission()、終了するときにendTransmission()を実行します。
通信開始時には、接続されている温度センサのアドレス0x48を指定します。
温度センサから値を取得するときには、「取得する値がどのような値か」を設定するための情報を書き込むことが必要です。
下記サイトの「動作設定レジスタ詳細」を参考にして、今回設定すべき内容を以下の2つとします。
- 温度を、0.0078度の分解能(精密さ)で計測:bit7を1にする
- 計測する頻度は、1秒に1回:bit6を1にする
上記2つを設定するために書き込むべき値は、11000000と分かります。
この数は16進数表記にすると0xC0となるので、write()で0xC0を書き込みます。
boolean updateTemperature() {
  try {
    i2c.beginTransmission(0x48);
    i2c.write(0xC0);
    byte[] v = i2c.read(2);
    i2c.endTransmission();
  } catch (Exception e) {
    roomTempValue = 0.0;
    return false;
  }
  
  return true;
}
値0xC0を書き込んだ後、温度センサから16bit(2byte)の値を取得します。
この16bitの値をプログラムで変換し、摂氏の温度に変換します。
1byteの値が2つ、配列vに入っています。
16bitの値に変換するとき、v[0]に入っている8bitは上位8bit、v[1]に入っている8bitは下位8bitとして扱います。
v[0]の値は、8bitだけ左にビットシフトします。
変換した結果得られた値は、今回温度センサから取得した温度の分解能0.0078を掛けることで、摂氏の温度に変換することができます。
boolean updateTemperature() {
  try {
    i2c.beginTransmission(0x48);
    i2c.write(0xC0);
    byte[] v = i2c.read(2);
    i2c.endTransmission();
    
    /* ここから追加 */
    int temp = ((v[0] & 0x1F) * 256 + (v[1] & 0xFF));
    if (4096 <= temp) {
      temp -= 8192;
    }
  
    roomTempValue = temp * 0.0078;
    /* ここまで追加 */
  } catch (Exception e) {
    roomTempValue = 0.0;
    return false;
  }
  
  return true;
}
関数updateTemperature()が実装できたので、この関数を呼び出すようにします。
温度センサから値を取得するタイミングは、以下の2つとします。
- デジタルサイネージを起動したとき
- 毎秒
void initialize() {
  initializeDate();
  initializeImage();
  initializeGrid();
  initializePlaceholder();
  initializeRModuleBackground();
  
  isUpdatedWeather = updateWeather();
  isUpdatedBus = updateBus();
  isUpdatedGomi = updateGomi();
  isUpdatedTwitter = updateTwitter();
  
  isUpdatedOpenClose = updateOpenClose();
  isUpdatedTemperature = updateTemperature(); /* 追加 */
}
/* 略 */
/* 略 */
void updateDatas() {
  updateDate();
  isUpdatedOpenClose = updateOpenClose();
  isUpdatedTemperature = updateTemperature(); /* 追加 */
  /* 略 */
TemperatureRModuleの描画
データが変数に代入できている状態になったので、画面上に表示するところまで実装します。
置換可能なReplaceableModuleを実装するとき、最低限定めなければいけないことは以下です。
- 描画時の基準となるエリア(Area.area1〜Area.area8のいずれか)
- 描画時のサイズ(Size.S〜Size.Lのいずれか)
まずはTemperatureRModuleの描画時の基準となるエリアについて説明します。
TemperatureRModuleの描画の基準になるのは、8つある表示エリアのうちArea.area1です。
関数moduleSize()をTemperatureRModuleに対応させます。
TwitterRModuleのサイズはSize.Sです。
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;
  return Size.S;
}
次に関数drawTemperatureRModule()を実装します。
Area.area1の左上の座標、TemperatureRModuleの幅と高さを取得します。
void drawTemperatureRModule(Area area) {
  RModule module = RModule.Temperature;
  Size size = moduleSize(module);
  
  int x = layoutGuideX(area);
  int y = layoutGuideY(area);
  int w = moduleWidth(size);
  int h = moduleHeight(size);
/* 略 */
モジュールの背景と、温度の値を表示します。
void drawTemperatureRModule(Area area) {
  RModule module = RModule.Temperature;
  Size size = moduleSize(module);
  
  int x = layoutGuideX(area);
  int y = layoutGuideY(area);
  int w = moduleWidth(size);
  int h = moduleHeight(size);
  
  /* ここから追加 */
  image(temperatureBackground, x, y, w, h);
    
  // モジュールの名前表示
  drawText(LEFT, BASELINE, WHITE_COLOR, 32, "室温", x+50, y+50);
  
  if (isUpdatedTemperature) {
    // 温度表示
    drawText(CENTER, BASELINE, WHITE_COLOR, 96, nf(roomTempValue, 0, 1)+"℃", 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, "TemperatureModule\nデータを取得できません", x+w/2, y+h/2);
  }
  /* ここまで追加 */
}
関数drawTemperatureRModule()が実装できたので、関数drawModules()内で呼び出します。
/* 略 */
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) {
    drawFullImageModule(adImage[0]);
  }
  drawDateModule();
  drawLocationModule();
  drawProgressBarModule();
  drawPageControlModule();
}
/* 略 */
実行してみると、温度が表示されるようになっているはずです。
温度センサに指を触れると、徐々に温度が高くなっていく様子も確認できるでしょう。
最後に
これで部屋の温度を表示する「TemperatureRModule」が実装できました。
次は、**部屋の明るさを表示する「BrightnessRModule」**を作ってみましょう。






