本記事は、株式会社 函館ラボラトリが運営する「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」**を作ってみましょう。