照明の色温度で空間の印象はがらりと変わる。
時間帯によって適した照明がある。
そんな話を聞いたので、時間帯で色が変わる照明をobnizを使って作ってみました。
自動で照明の色(色温度)が変わるのがプログラムのメイン機能ですが、ユーザーの操作による変更も可能。LEDの色変更のための赤・緑・青・黄ボタン、時間帯ごとの色を確認できるボタンを付けました。今後色を変更したり、ほかの色を付け足すことも考え、プログラミング時の確認用にカラーピッカーも入れてあります。
きっかけ
我が家のリビングルームの照明はPanasonicのシーリングライト。リモコンで色が変えられます。日中は「白い色」にして本などを読みやすく、夜は「暖かい色」(オレンジっぽい色)にしてリラックスできるように、といった感じ。でも、この「色」って何? と改めて思い、見つけたのがこの記事。
照明の色温度で冬のおうち時間を快適に。YAMAGIWAの開発者に聞く照明選びのコツ
光の色味を数値で表したものが色温度。時間帯によって色温度を変えることが、体内時計や生活リズムを整えるのを助けてくれる。ということのようです。
「時間によってリモコン操作をするのではなく、自動で照明の色温度を変えたい。」 というわけで、obnizを使って照明を作ることにしたわけです。
使ったもの
名前 | 概要 |
---|---|
obniz Board 1Y | https://obniz.io/ja/ |
X-Ring | WS2812B 搭載カラーLEDモジュール AliExpressで購入 約500円 |
ペンダントランプシェード | 紙製。イケアで購入 399円 |
照明用スタンド | イケアで購入 処分品 約1000円 |
口金変換アダプタ E17 → E26 | ヨドバシで購入 440円 |
セパラボディ E26 | ヨドバシで購入 165円 |
USB電源アダプタ | 100均店で購入 300円 |
最低限必要なのは最初の2つだけです。あとは雰囲気を盛り上げるためのもの、電源の確保を楽にするためのものです。
今回使用したカラーLEDは、Wemos X-Ringという12個のカラーLEDがリング状に配置された製品。配線用のピンソケットをはんだ付けして使います。テープLEDなら、はんだづけなしで使えるので、手軽に試したい方はそれでもOKです。
また、カラーLEDの光は思いのほか強烈です。直接見るのは目に悪いので、光を拡散させるものが必須。今回は市販の紙製ランプシェードを使いましたが、自分で作っても楽しいです。紙を筒状にしてLEDを囲むだけでも十分だと思います。
配線
Neopixelという名称でおなじみのWS2812B搭載カラーLEDは、電源(VCC)、GND、信号線(DIN)の3本だけで、複数のLEDをマイコンから制御できます。obnizのパーツライブラリにWS2812Bが用意されているので、簡単に使用可能となっています。
今回、obniz側は5V電源出力の「+」とGNDの「-」、信号用「io3」のピンを使用(信号用は任意)。X-Ring側はそれぞれ5V、GND、D3につなぎました。上の写真を見ればわかるかと思います。
obniz公式制作レシピ集にあったカラーLEDの例では、GNDを0ピン、VCCを2ピンからとっていますが、この方法だとブラウザを閉じた時点でLEDが消灯してしまいます。これでは普段使いの照明としてはいまいちです。「+」と「-」を使うと、これを回避することができます(これに気づくまでけっこう時間がかかってしまいました)。
プログラム
プログラムはブラウザアプリ(HTML/JavaScript)として作成します。
おもに以下の2つのパートで構成されます。
- ウェブブラウザでアクセスできるユーザーインターフェイス(ボタンでLEDのカラーを指定可能)
- 時間によってLEDカラーを変更するロジック
日の出・日の入り時刻を計算するライブラリ
obnizのパーツライブラリ「WS2812B」のほか、外部JavaScriptライブラリとして、日の出日の入り時刻を計算する「SunCalc」を使用。jsDelivr経由でロードしています。
以下のような形で計算できます。
例
let sunTime = SunCalc.getTimes(new Date(), 35.6809591 , 139.7673068);
例では東京駅の緯度・経度を指定しています。緯度・経度はGoogleで「地名 緯度 経度」検索するとDegree形式とDMS形式が出てくるので、ここではDegree形式を使います。
日の出、日の入り時刻の「時間」「分」をそれぞれ取得できます。
例
let sunriseHours = suntime.sunrise.getHours() ; // 日の出(時) let sunriseMinutes = suntime.sunrise.getMinutes(); // 日の出(分) let sunset Hours = suntime.sunset.getHours(); // 日の入り(時) let sunset Minutes = suntime.sunset.getMinutes(); // 日の入り(分)
現在時刻との比較のためにHours * 60 + Minutes として計算しています。
カラーLEDの色を指定する
obnizのパーツライブラリ「WS2812B」のサンプルと、ブラウザでカラーピッカーを使って色を指定する例は、obniz公式サイトで紹介されています。
スライダーに合わせて光るLEDの数を変える
HTMLのカラーピッカーでフルカラーLEDの色を変える
これらを参考にすれば、問題なくできます。手持ちのテープLEDで試したところ、すぐに色を指定して光らせることができました。
しかし、今回使用したX-RingというカラーLEDモジュールでは、どうも狙った色が出ません。青は問題ないのですが、赤と緑が逆になります。というわけで、指定した色の順番をRGBからGRBに変える処理を入れています。プログラムのlight関数に注釈を入れているので、一般的なカラーLEDモジュールの場合は適宜変更してください。
例
ledarry[i] = [r, g, b]; //RGB 順の場合 ledarry[i] = [g, r, b]; //GRB 順の場合
時間帯に合わせた色
ボタンで表示する色、時間帯に合わせた色は、JavaScirptオブジェクトcolorsで定義しています。もっと細かく時間を分けたい場合は適宜増やしてください(時刻を判定する部分も修正が必要です)。
名前 | 時間 | 色温度 |
---|---|---|
day(日中 ) | 0時~日の出 | 消灯 |
evening(夕方) | 日の出~日の入 | 6500K |
dark(夜) | 日の入~20:00 | 3000K |
night(夜) | 20:00~22:00 | 2000K |
midnight(深夜) | 22:00~0:00 | 1400K |
時間帯に合った色温度は最初の記事を参考にしました。日中は昼光色の6500K、夕方以降は暖かみを感じられるより低い色温度とし、そのあとは実際の色を見ながら気分で分けてしました(正しい分け方でありません)。また、名前はあまり考えずにつけてしまい、英語的にはおかしいと思いますが、とりあえずということで。
それぞれの色温度に対応するRGBの値は以下を使用しました。
実際にLEDを光らせてみると、「思った色味にならない」「LEDモジュールによって色味が違う」なんてこともあるので、環境などに合わせて修正が必要になるかと思います。上記設定はあくまでも参考程度に。
プログラムで数値指定、実行して結果を確認、という手順はけっこう面倒なので、ユーザーインターフェイスにカラーピッカーを用意しました。微調整などはこれを使えばいいでしょう。数値指定もすぐに反映できます。
コード全体
というわけで、コード全体は以下のとおり。
コード
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css">
<link rel="stylesheet" href="/css/starter-sample.css">
<script src="https://obniz.io/js/jquery-3.2.1.min.js"></script>
<script src="https://unpkg.com/obniz@3.14.0/obniz.js" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/gh/mourner/suncalc@master/suncalc.js"></script>
</head>
<style>
button {margin-bottom:8px;}
</style>
<body>
<div id="obniz-debug"></div>
<div class="led">
<h3 class="text-center">LED Light</h3>
<div id="now"></div>
<div id="sunrise"></div>
<div id="sunset"></div>
<button class="btn btn-outline-primary btn-color col-md-2" id="red">Red</button>
<button class="btn btn-outline-primary btn-color col-md-2" id="green">Green</button>
<button class="btn btn-outline-primary btn-color col-md-2" id="blue">Blue</button>
<button class="btn btn-outline-primary btn-color col-md-2" id="yellow">Yellow</button>
<br>
<button class="btn btn-outline-primary col-md-4" id="time">Show Time</button>
<button class="btn btn-outline-primary col-md-4" id="stop">Stop</button>
<div style="margin:2em 0 1em;">
<button class="btn btn-outline-primary col-md-4 period" id="day">DAY</button>
<button class="btn btn-outline-primary col-md-4 period" id="evening">EVENIG</button>
<button class="btn btn-outline-primary col-md-4 period" id="dark">DARK</button>
<button class="btn btn-outline-primary col-md-4 period" id="night">NIGHT</button>
<button class="btn btn-outline-primary col-md-4 period" id="midnight">MIDNIGHT</button>
</div>
<h3>Color Picker</h3>
<input type="color" value="#333300" id="colorPicker" style="width:100px;height:60px;">
<div class="form-row align-items-center mx-auto" style="margin:auto 1em;width:350px;">
<div class="col-auto">
<label class="sr-only" for="text5a">First Name</label>
<input type="text" class="form-control mb-2" id="hex" placeholder="Color(hex)">
</div>
<div class="col-auto">
<button id="submit" type="submit" class="btn btn-primary mb-2" style="width:100%">Submit</button>
</div>
</div>
</div>
<script>
'use strict'
let obniz = new Obniz("OBNIZ_ID_HERE");
const colors = {
"red" : [0x1F, 0x00, 0x00],
"blue" : [0x00, 0x00, 0x1F],
"green" : [0x00, 0x1F, 0x00],
"yellow" : [0x1F, 0x1F, 0x00],
"day" : [0xFF, 0xFF, 0xFF], // 6500K ffffff
"evening" : [0xFF, 0xB4, 0x6B], // 3000K ffb46b
"dark" : [0xFF, 0x89, 0x12], // 2000K ff8912
"night" : [0xFF, 0x65, 0x00], // 1400K ff6500
"midnight" : [0x00, 0x00, 0x00]
}
obniz.onconnect = async function () {
obniz.display.clear();
obniz.display.print("Hello World");
// パーツライブラリ WS2812B 電源は+ピン(5V出力)から供給するのでvcc、gndは指定しない。
const led = obniz.wired("WS2812B", {din: 3});
// 明るさ割合 0.04 to 1(最大)
const range = 1;
const max_num = 12; // LEDの個数を指定
let ledarry = new Array(max_num);
showTime();
// クラウド実行のアプリステータス
if (Obniz.App.isCloudRunning()) {
Obniz.App.done({
status: 'success',
text: `It's OK`
})
}
// LED点灯
function light(colorArray){
for(let i=0; i< max_num; i++){
const r = Math.floor(colorArray[0] * range);
const g = Math.floor(colorArray[1] * range);
const b = Math.floor(colorArray[2] * range);
// 製品によってRGBの並び順が異なるのでどちらかを
//ledarry[i] = [r, g, b]; //RGB
ledarry[i] = [g, r, b]; //GRB
}
led.rgbs(ledarry);
$("#hex").val(hex2digit(colorArray[0]) + hex2digit(colorArray[1]) + hex2digit(colorArray[2]));
}
// 時刻表示、LED点灯
async function showTime () {
obniz.display.clear();
// 時刻表示 日出/日没
const now = new Date();
const hour = now.getHours();
const min = now.getMinutes();
obniz.display.print("Now " + fill(hour) + ":" + fill(min));
const currentTime = hour * 60 + min;
const suntime = SunCalc.getTimes(new Date(), 35.6809591 , 139.7673068);
obniz.display.print(suntime.sunrise.getHours() + ":" + suntime.sunrise.getMinutes() +" / " + suntime.sunset.getHours() + ":" + suntime.sunset.getMinutes());
const sunrise = suntime.sunrise.getHours() * 60 + suntime.sunrise.getMinutes(); // 日の出
const sunset = suntime.sunset.getHours() * 60 + suntime.sunset.getMinutes(); // 日の入り
obniz.display.print(currentTime + " / " + sunrise + " / " + sunset);
// 現在時刻と日の出日の入り時刻を表示
$("#now").text("現在の時刻: " + fill(hour) + ":" + fill(min) + " (" + fill(currentTime, 4) + ")");
$("#sunset").text("日の入時刻: " + fill(suntime.sunset.getHours()) + ":" + fill(suntime.sunset.getMinutes()) + " ("+ fill(sunset, 4) + ")");
$("#sunrise").text("日の出時刻: " + fill(suntime.sunrise.getHours()) + ":" + fill(suntime.sunrise.getMinutes())+ " ("+ fill(sunrise, 4) + ")");
// 時刻で色を変更
if(currentTime < sunrise){ // 0時から日の出まで
light(colors["midnight"]);
console.log("nidnight");
}
else if(currentTime < sunset){ // 日中(日の出から日の入り)
light(colors["day"]);
console.log("day");
}
else if(currentTime < 20 * 60){ // 夕方(日の入り以降から20時まで)
light(colors["evening"]);
console.log("evenig");
}
else if(currentTime < 22 * 60){ // 夜(20時から22時まで)
light(colors["dark"]);
console.log("dark");
}
else { // 22時以降
light(colors["night"]);
console.log("night")
}
};
// Show Time ボタン
$('#time').click(function () {
showTime();
});
// Stop ボタン (消灯)
$('#stop').click(function () {
light([0x00, 0x00, 0x00]);
obniz.display.clear();
obniz.display.print("STOP");
});
// 色名 ボタン
$(".btn-color").click(function (){
const rgb = this.id;
light(colors[rgb]);
obniz.display.clear();
obniz.display.print(rgb);
console.log(rgb);
});
// 時間帯 ボタン
$(".period").click(function (){
const period = this.id;
light(colors[period]);
obniz.display.clear();
obniz.display.print(period);
console.log(period);
});
// カラーピッカー
$("#colorPicker").on("change", function(val) {
const colorString = $("#colorPicker").val();
const rgb = colorString2hex(colorString);
light([rgb[0], rgb[1], rgb[2]]);
});
$("#submit").click(function (){
const colorString = $("#hex").val();
console.log(colorString);
const rgb = colorString2hex(colorString);
light([rgb[0], rgb[1], rgb[2]]);
});
};
// カラーピッカー用色コード変換
function colorString2hex(hex) {
if (hex.slice(0, 1) == "#") hex = hex.slice(1);
$("#hex").val(hex.toUpperCase());
if (hex.length == 3)
hex =
hex.slice(0, 1) +
hex.slice(0, 1) +
hex.slice(1, 2) +
hex.slice(1, 2) +
hex.slice(2, 3) +
hex.slice(2, 3);
return [hex.slice(0, 2), hex.slice(2, 4), hex.slice(4, 6)].map(function(str) {
return parseInt(str, 16);
});
}
// 色コードの桁揃え
function hex2digit(num){
num = ("0" + num.toString(16)).slice(-2).toUpperCase();
return num;
}
// 時刻の桁揃え
function fill(num, def=2){
num = ("000" + num).slice(-def).toUpperCase();
return num;
}
</script>
</body>
</html>
インストール方法
単に上記プログラムを書いただけでは、ブラウザを起動している間しか動作しません。時間帯に合わせて色を変えるには、定期的にプログラムを実行させるための「クラウド実行」の設定が必要になります。
「アプリ開発」の一覧で、先に書いたプログラムの「アプリ設定」を開き、「ブラウザ実行」をチェック、さらに「クラウド実行」の以下の項目を設定します。
項目名 | 値 |
---|---|
Webhookで実行 | チェックなし |
時間で実行(タイムゾーン:+9:00 | every/10minutes |
デバイスがオンライン時に実行 | チェック |
デバイスのスイッチが押されたら実行 | チェック |
このあと「設定を更新」し、アプリ一覧に戻り、先に書いたアプリの「インストール」をクリック。デバイスを選択すれば、設定は完了です。
これで、10分ごとに起動して、現在時刻を判定して色を変えてくれるようになります。ただし、起動は10分ごとなので、「日の入りぴったりに色が変わる」といった感じにはなりません。最大10分は遅れてしまいます。
クラウド実行には、アプリの1日あたりの実行上限回数があるので、これ以上増やすには料金が必要となります。まあ、そこまで厳密にするほどのものでもないと思い、そのまま使っています。気になる方は回避策を編み出して、ぜひその結果を教えて下さい。
電源まわり
obnizの電源はUSBから取りますが、今回は照明スタンドに取り付けるために、いろいろ部品を買い足しました。
写真は左から照明スタンドのソケット、口金変換アダプタ、セパラボディ、USB電源アダプタ
今回使用した照明スタンドは、電源コードはついているのですが、出力はE17という電球を付けるためのソケットになっています。そのままでは当然USBに出力できません。
そこで見つけたのが、電球ソケットに差し込んでコンセントとして使用できるようにする「セパラボディ」という商品。しかし、このセパラボディの電球ソケットはE26というサイズ。スタンドのE17に変換する「口金変換アダプタ E17 → E26」もあわせて購入しました。
というわけで、
電球スタンド(E17)→口金変換アダプタ→セパラボディ→USBアダプタ
という変換に変換を重ねることで電源を確保しました。
最終的にこんな感じ↓でobnizとカラーLEDモジュール、照明スタンドが並ぶことになりました。
あとはこれにランプシェードをかぶせるだけで、冒頭の動画のようになります。
おわりに
obnizを触るのは今回が初めて。振り返ってみると、プログラミングの骨格は1日くらいでできたのですが、指定した時間ごとに起動するための設定を見つけるのに思いのほか時間がかかってしまいました。プログラム(アプリ)とデバイスの関係がわかったので、2作目以降は迷わずにできそうです。
ハードウェアを動かすためのプログラムとともに、それをPC/スマホから操作するためのユーザーインターフェイスも一緒に書けるのは、なんとも不思議な感じ。しかもJavaScriptで! それがとても楽! 今後はobinizならではの機能をより生かしたものを作ってみたいと思っています。
投稿者の人気記事
-
linclip
さんが
2021/05/14
に
編集
をしました。
(メッセージ: 初版)
-
linclip
さんが
2021/05/14
に
編集
をしました。
-
linclip
さんが
2021/05/15
に
編集
をしました。
(メッセージ: 記法修正)
-
linclip
さんが
2021/05/16
に
編集
をしました。
-
linclip
さんが
2022/01/23
に
編集
をしました。
ログインしてコメントを投稿する