本記事は、株式会社 函館ラボラトリが運営する「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の影を実装
作るもの
明るさのセンサ「CdSセル」を使い、明るさを計測できるようにします。
画像の準備
BrightnessRModuleの背景画像には、下の画像を使用します。
画像を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」フォルダの位置を示すパスを、定数として定義します。
/* 略 */
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の背景を保持する変数を定義します。
/* 略 */
/* ここから追加 */
// BrightnessRModule
PGraphics brightnessBackgroundBright;
PGraphics brightnessBackgroundNotBright;
/* ここまで追加 */
/* 略 */
RModuleのリストに、BrightnessRModuleを表す「Brightness」を追加します。
/* 略 */
enum RModule {
Weather,
Bus,
Gomi,
Twitter,
OpenClose,
Temperature,
Brightness /* 追加 */
}
/* 略 */
background.jpg
を用いて、モジュールの背景画像を生成します。
initializeRModuleBackground()
内で実装します。
/* 略 */
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()
を作ります。
void drawBrightnessRModule(Area area) {
}
boolean updateBrightness() {
return false;
}
値が更新されたことを示す変数、一定の基準値以上の明るさかを示す変数、明るさを0〜100%で示した変数を定義します。
/* 略 */
// BrightnessRModule
SPI spi; /* 追加 */
boolean isBright = true; /* 追加 */
boolean isUpdatedBrightness = false; /* 追加 */
float brightnessRate = 0.0; /* 追加 */
PGraphics brightnessBackgroundBright;
PGraphics brightnessBackgroundNotBright;
/* 略 */
CdSセルからの値を取得するため、SPI
という通信方式を使用します(本記事では詳細を割愛します)。
電子部品の配置
電子部品を組み替えるときは、ラズパイの電源を切り、電源ケーブルをコンセントから抜いてください。
以降、ラズパイと電子部品を接続するときのピンの番号は、下記サイトのGPIOピンの配置カードを参考にしてください。
CdSセルと330Ω抵抗、ジャンパワイヤを下図のように配置します。
ラズパイで値を取得するには、CdSセルを直接つなぐだけでなく、A/D変換した値を取り出す必要があります。
そこで、A/D変換用の電子部品「MCP3002」を使います。
MCP3002とジャンパワイヤを下図のように配置してください。
(MCP3002の上下を間違えないようにしてください。ショートして起動しなくなります。)
ラズパイのGPIOピンに接続するために、ジャンパワイヤを4本接続します。
- 左の上から1番目のピンは、19番ピン(GPIO10)に接続します。
- 左の上から2番目のピンは、21番ピン(GPIO09)に接続します。
- 左の上から3番目のピンは、23番ピン(GPIO11)に接続します。
- 右の上から4番目のピンは、24番ピン(GPIO08)に接続します。
これで電子部品の配置が完了しました。
CdSセルの値取得
関数setup()
内で、SPI通信で入力を受け取るための設定をします。
接続されているデバイスのリストから、一番最初に出てくるデバイスの情報をspi
という変数に入れておきます。
/* 略 */
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%)に換算します。
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つとします。
- デジタルサイネージを起動したとき
- 毎秒
void initialize() {
initializeDate();
initializeImage();
initializeGrid();
initializePlaceholder();
initializeRModuleBackground();
isUpdatedWeather = updateWeather();
isUpdatedBus = updateBus();
isUpdatedGomi = updateGomi();
isUpdatedTwitter = updateTwitter();
isUpdatedOpenClose = updateOpenClose();
isUpdatedTemperature = updateTemperature();
isUpdatedBrightness = updateBrightness(); /* 追加 */
}
/* 略 */
/* 略 */
void updateDatas() {
updateDate();
isUpdatedOpenClose = updateOpenClose();
isUpdatedTemperature = updateTemperature();
isUpdatedBrightness = updateBrightness(); /* 追加 */
/* 略 */
BrightnessRModuleの描画
データが変数に代入できている状態になったので、画面上に表示するところまで実装します。
置換可能なReplaceableModuleを実装するとき、最低限定めなければいけないことは以下です。
- 描画時の基準となるエリア(Area.area1〜Area.area8のいずれか)
- 描画時のサイズ(Size.S〜Size.Lのいずれか)
まずはBrightnssRModuleの描画時の基準となるエリアについて説明します。
BrightnessRModuleの描画の基準になるのは、8つある表示エリアのうちArea.area2
です。
関数moduleSize()
をBrightnessRModuleに対応させます。
BrightnessRModuleのサイズは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;
if (module == RModule.Brightness) return Size.S; /* 追加 */
return Size.S;
}
次に関数drawBrightnessRModule()
を実装します。
Area.area2
の左上の座標、BrightnessRModuleの幅と高さを取得します。
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);
/* 略 */
モジュールの背景と、明るさの値を表示します。
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()
内で呼び出します。
/* 略 */
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セルを手で覆うと、値の変化が確認できるでしょう。
最後に
これで、部屋の明るさを表示する「BrightnessRModule」が実装できました。
ReplaceableModuleは、BrightnessRModuleをもって全て完成しました!
次は、**起動画面を表示する「LaunchingScreenModule」**を作ってみましょう。