本棚に間接照明を取り付けていい感じに光らせた話―Arduino、Digisparkとウェーバー・フェヒナーの法則―

ところで、わが家には横幅が3メートルくらいの、すこし大きめの本棚があるんですが、これを作った時にどうしてだったか2cmくらい浮かせる設計にしていました。

f:id:at_you_key:20190322152422p:plain
浮遊感

僕はこういう隙間を見ると無性に光らせたくなってしまう人間なので、ここにLEDテープを貼って、間接照明にしてみようということになりました。

f:id:at_you_key:20190322152356p:plain
丁度良いスペースがあります

激安なLEDテープを取り付けてみた

まず必要なのはLEDテープです、Aliexpressで5m約300円 (激安!)のものを見つけました。

冗談みたいな値段ですが、綺麗にリールに巻かれて無事に届きました。 選んだ色はWarm Light(電球色)です。

f:id:at_you_key:20190322152252j:plain
写真は使用後ですが、こんなリール

両面テープがついているので、取り付けは簡単です。電源は12Vなので、どこの家でもあまっている12V2AのACアダプターを繋ぎました。

f:id:at_you_key:20190322152235j:plain
Chillな感じになりました

品質の方はどうなのかといえば、3ヶ月ほどつけっぱなしにしています、がいまのところ全く問題ないので、いい買い物でした。

かっこよくフェードさせたい

さて、このままではACアダプタの抜き差しでON/OFFするしかないので、少々オシャ感に欠けます。

そこで、なめらかなフェードを実装して、オシャ感を出していきたいと思います。ついでに明るさの調節もできるとなお良しです。

コントローラーはArduinoで作ろう

オシャなフェード以外にも、リモコン付けたいとか環境光センサー付けたいとか、高機能な調光器を探すとキリがないので、拡張性をブチ上げるためにコントローラーはArduinoで自作します。

Arduinoと書きましたが、ライトのフェードなんかにArduinoを使うのは大げさなので、Arduino環境で使えるちっちゃいマイコン「Digispark」のクローン*1を使ってみます。Aliexpressで200円くらいでした。

s.click.aliexpress.com

Digisparkの使い方については、このあたりを参考にしました。Arduino IDEで少し設定するだけで簡単に使えます。

qiita.com

digistump.com

回路はこんな感じ

5V駆動のDigisparkから、FETで12V電源をスイッチングさせる回路です。

とりあえずの操作用に、ピン2にプッシュボタンを1つ付けてみました。

f:id:at_you_key:20190322152337p:plain

材料はこれだけです。

  • Digispark
  • LEDテープ 12V 電球色 3m
  • プッシュボタン
  • FET 2SK2232
  • 抵抗 1MΩ
  • 12Vの電源

ちなみに、Digisparkには3端子レギュレーターが載っているので、今回のように12V電源を使う時は、VIN端子に繋ぐだけで降圧してくれるので楽ちんです。

”かっこいい” フェードとは?―ウェーバー・フェヒナーの法則

さて、ただフェードさせるだけなら、for文でカウントしてPWM出力すればOKですが、今回目指すのは"かっこいい"フェードです 。

どうすればかっこよくなるでしょうか?ウーム。

f:id:at_you_key:20190322141637p:plain

ところで、人間の5感の知覚というのは、刺激量の対数に比例するそうで、この法則を「ウェーバー・フェヒナーの法則」といいます。

f:id:at_you_key:20190322152105j:plain
法則の名前は二人のドイツ人学者から

雑に説明すると「刺激が強くなるほど感覚が鈍感になる」という法則です。

つまり、線形に明るさを変えてしまうと、見た目上は最初一気に明るくなって、その後はほとんど変化しないように見えてしまいます。

f:id:at_you_key:20190322152157j:plain
出力が線形の場合

そこで、コントローラーの出力を指数関数的に変化させることで、人間の目からは滑らかに変化して見えるようになります。

f:id:at_you_key:20190322152216j:plain
出力を指数変化にすると

それに、○○の法則ってなんだかかっこいいじゃないですか、よって今回はこれを”かっこいいフェード”と定義したいと思います。

とはいえ対数とか指数関数とか、数学に対してフワッとした理解しか持ち合わせていない僕にとっては、だいぶハードルの高いテーマです。 

すかさずGoogle先生に助けを求めたところ、ドンピシャな記事を見つけたので、こちらを参考にさせてもらいました。

qiita.com

実験してみる

とはいえ、はたしてそんなに変わるのかな?試しに光り方を比べてみたのがこちら。

なるほど...左が線形変化のフェード、右が指数変化のフェードです。 グラフと照らし合わせると一目瞭然ですね。

Arduinoスケッチ

※もっとシンプルな方法がありました。

www.creativity-ape.com

ボタンを1つしか付けていないので、こんな操作を想定してスケッチを書いてみました。

  1. 電源ON
  2. フェードして点灯
  3. ボタンを押すたびに徐々に(10ステップ)暗くなって消灯
  4. 消灯状態でボタンを押すと2番に戻って繰り返し

そうして出来上がったコードがこちら!

/*
本棚の下のLEDテープをかっこよくフェードさせるスケッチ。
*/

const int light_pin = 0; // LEDテープのコントロール
const int led_pin = 1; // Digisparkの内蔵LED
const int button_pin = 2; // プッシュボタン
const int max_value = 255; // PWM出力の最大値
const int fade_step = 10; // フェードのステップ数
const double C = 255.0; // 用いるLEDに依存する定数

void setup() {
  pinMode(light_pin, OUTPUT);
  pinMode(led_pin, OUTPUT);
  pinMode(button_pin, INPUT_PULLUP);

  // 初期状態は点灯
  fade(0, max_value);
}

void loop() {
  // 一旦ボタンを離してね
  button_wait(0);

  // fade_step刻みで暗くする
  for (int i = fade_step; i > 0; i--) {
    button_wait(1);
    fade(max_value / fade_step * i, max_value / fade_step * (i - 1));
  }

  // 一旦ボタンを離してね
  button_wait(0);
  button_wait(1);
  // リセット(点灯)
  fade(0, max_value);

}

void button_wait(boolean flag) {
  // ボタンが押/離されるまで待つ
  while (digitalRead(button_pin) == flag) {
    delay(100);
  }
}

void fade(int start_value, int end_value) {
  // 開始値をセット
  int current_value = start_value;


  // 目標値に達していないうちはループ
  while (current_value != end_value) {

    // 目標値より小さい
    if (current_value < end_value) {
      ++current_value;
    }

    // 目標値より大きい
    if (current_value > end_value) {
      --current_value;
    }

    // 明るさを出力
    analogWrite(light_pin, get_duty(current_value));
    delay(5);
  }
}

int get_duty(double ratio) {
  // Weber-Fechnerの法則
  return round(exp(log(255.0) - (1 - (ratio / 255.0)) * log(C))) - 1;
}

かっこよくなった!

オシャな感じになったんじゃないかと思います、満足したので今日はここまで。

*1:クローンはめちゃくちゃ安い代わりに、P5がResetピン設定になっていてI/Oとしては使えないので、そこだけ注意が必要です