子どもの夜ふかし対策に照明が点いてたら消し続けるIoT装置を作った

# はじめに

私は保育園児のふたりの子どもがいるのですが、子どもたちの夜ふかしに日々悩まされていました。
21時～22時ぐらいに寝かしつけに入りはするのですが親が先に寝落ちしてしまい、子どもが寝室の照明をつけて夜中まで延々と遊び続けるという状況です。

せめて真っ暗闇なら親が寝落ちしたあともそこまで夜ふかししないだろうと思い寝室のブレーカーを落とすということも考えましたが、なぜかサーバールーム（クローゼット上部のルータが設置されている小さいスペース）とブレーカーが共用されており、このブレーカーを落とすと家中のネットワークが停止して夜間ジョブ（NASへのバックアップ等）に支障をきたしてしまうため断念しました。

そこで次の手段として考えたのが照明を消し続けるIoT装置の自作です。



# 仕様

以下のように仕様を考えました。

* 部屋の明るさを計測して照明が点いているかを判定
* 照明が点いていたら照明オフの赤外線信号を送信し続ける
* 上記の動作をデバイスのボタンを押してから1時間実行し続ける

図にすると以下のようなイメージです。
![image](https://user-images.githubusercontent.com/22117028/147377992-a04ebdff-6e44-45ff-83fd-e3471cf76a9a.png)



# obnizを使って実装

obnizというJavaScript/TypeScriptで実装できるデバイスがあり、私はIoTデバイスのプロトタイピングではまずこれを使って実装しています。
obnizは「obnizOS」というOSを書き込んだマイコンデバイスを「obniz Cloud」というクラウド環境を経由し「obniz SDK」にて実装したプログラムから制御する、というしくみです。
さらに「obniz Board」という専用マイコンデバイスもあり、過電流検知や高出力なプログラマブルピンとIoT入門者にとても優しい仕様のボードがあります。
![image](https://user-images.githubusercontent.com/22117028/147381368-5b709c7b-a057-4afd-8126-738f3cdeb1a8.png)
（https://obniz.com/ja/how_obniz_works より）

https://obniz.com/ja/products/obnizboard



## 赤外線モジュール

赤外線信号の取得や送信には赤外線モジュールが必要です。
obniz公式の[赤外線モジュール](https://store.obniz.com/ja/collections/official-electronics-parts/products/ir-module-cbr-ir002)があり、今回これを使用しました。
（[赤外線LED](https://akizukidenshi.com/catalog/g/gI-13204/)と[赤外線受信機](https://akizukidenshi.com/catalog/g/gI-06487/)を個別にそろえても大丈夫です）

https://store.obniz.com/ja/collections/official-electronics-parts/products/ir-module-cbr-ir002

obnizのすばらしい点として豊富なパーツライブリがありますが、さらに特筆したいのはパーツライブラリの動作をobnizのサイトから直接試せることです。
たとえば照明をオフにする赤外線信号は事前にリモコンから取得する必要がありますが、これは一度きりでいいのでわざわざプログラムを組むのは手間です。
しかし[パーツライブラリのサイト](https://obniz.io/ja/sdk/parts/IRModule/README.md)から赤外線信号受信のメソッドを直接実行すればプログラムの実装は不要です。
![image](https://user-images.githubusercontent.com/22117028/147385675-da8bc9a0-a30b-4b38-b497-4e40a1b4662d.png)
（https://obniz.io/ja/sdk/parts/IRModule/README.md より）



## 照度センサ

明るさの取得には照度センサを使用します。
私は手持ちの[CdSセル](https://akizukidenshi.com/catalog/g/gI-00110/)を使用しましたが、CdS（硫化カドミウム）は環境有害物質のため将来廃棄する際に簡単に捨てることができません。
新規に購入される場合は[フォトトランジスタ](https://akizukidenshi.com/catalog/g/gI-02325/)を使用した方がよいでしょう。

https://akizukidenshi.com/catalog/g/gI-00110/

https://akizukidenshi.com/catalog/g/gI-02325/



## obnizへの電子パーツの接続

写真、表を参考にobnizへパーツを接続してください。
![parts](https://user-images.githubusercontent.com/22117028/147385177-43d502a8-d969-4bdf-ae7f-3df4d03cc704.jpg)

|obnizのピン番号|パーツのピン|
|---|---|
|0|赤外線モジュール 5V|
|1|赤外線モジュール LED Anode|
|2|赤外線モジュール Sens Out|
|3|赤外線モジュール GND|
|10|照度センサ|
|11|照度センサ|



## 実装コード

ソースは`index.js`と`irSignal.js`の2ファイルを用意しました。
照明をオフにする赤外線信号のデータがそこそこ大きいため、そのデータ記載のためだけに`irSignal.js`を用意しています。

[赤外線モジュールのパーツライブラリサイト](https://obniz.io/ja/sdk/parts/IRModule/README.md)にて照明リモコンの照明オフ信号を取得し`irSignal.js`に記載します。

```js:irSignal.js
exports.irSignal = [1,1,1,1,1,1,1,1,1,...] // お使いの照明のリモコン信号に書き換え

```

信号データ以外の実装は`index.js`に記載します。
以下の折りたたみ内にソースコードを掲載しています。
`obnizId`をお持ちのobniz IDに書き換えてご利用ください。

<details><summary><b>╭╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╮<br>&nbsp;&nbsp;&nbsp;╏&nbsp;&nbsp;&nbsp;&nbsp;ソースコードを表示（折りたたみ）&nbsp;&nbsp;&nbsp;╏<br>&nbsp;&nbsp;&nbsp;╰╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╯</b></summary><div>

```js:index.js
const Obniz = require("obniz")
const { irSignal } = require("./irSignal")



const obnizId = "<your obniz ID>" // お持ちのobniz IDに書き換え

const irPin = { vcc: 0, send: 1, recv: 2, gnd: 3 }
const cdsPin = { signal:10, vcc:11 }

const brightnessThreshold = 400 // 照明オフ赤外線信号を飛ばす明るさのしきい値、ご利用の環境にあわせて調整してみてください
const intervalTime = 3000
const executionTime = 3600000



const obniz = new Obniz(obnizId)

let startTime = 0



const sleep = async ms => new Promise(r => setTimeout(r, ms))

const getBrightness = async ({ signal, vcc }) => {
	obniz[`io${vcc}`].drive("3v")
	obniz[`io${vcc}`].output(true)
	const voltage = await obniz[`ad${signal}`].getWait()
	const brightness = Math.round((voltage - 1.5) * 1000)
	return brightness
}

const offLight = async (irPin) => {
	await obniz.wired('IRModule', irPin).send(irSignal)
	await obniz.reset() // MEMO: obnizつけっぱだとPWMエラー出るのでリセット
}



const main = async () => {
	try {
		while (!(await obniz.connectWait({ timeout: 5 }))) { console.log("connect...") }
		console.log("[obniz Connected]")
		
		obniz.onclose = async () => await main() // 接続断時リトライ
		
		// ボタン押すとオン（オン時刻を保持）
		obniz.switch.onchange = state => {
			if (state === "push") {
				console.log("[button start]")
				startTime = Date.now()
			}
		}
		
		startTime = 0
		
		for (;;) {
			await sleep(intervalTime)
			
			// 時間チェック（オンから1時間）
			if (Date.now() - startTime > executionTime) startTime = 0
			if (startTime === 0) continue
			
			// 明るさチェック
			const brightness = await getBrightness(cdsPin)
			if (brightness < brightnessThreshold) continue
			
			// 照明オフ
			await offLight(irPin)
			console.log("[light off]", brightness)
		}
	} catch (e) {
		console.log("[main catch]", e.message)
		await main()
	}
}
main()
```

</div></details>



## 実行

obnizはデバイスだけでは動作せず、別途プログラムを実行する環境が必要です。
私は家で常時稼働しているRaspberry Piからobnizプログラムを実行しています。

他にも実行環境としての選択肢はクラウドもあります。
ワンタイム実行ならAWS LambdaといったFaaS、常時稼働ならHerokuといったPaaSの利用となるでしょう。
（今回のプログラムのケースですと常時稼働させるのでHerokuが適しています）

プログラム実行は以下のコマンドで行います。

```sh:パッケージインストール
npm i obniz
```

```sh:プログラム実行
node index.js
```

プログラム実行後、`[obniz Connected]`と表示されればobnizと正常に接続された状態です。
この状態でobnizのボタンを押すと1時間の間、明るさのしきい値（`brightnessThreshold`）を超えていれば照明オフの赤外線信号を送信し続けます。



# おわりに

この装置を導入したことで親が寝かしつけチャレンジ失敗時に子どもがエンドレス夜ふかしとなるケースは減少したように思います。

また子どもが照明がつかないと悟ると今度は寝室から抜け出すようになるのではという懸念もありましたが、今のところそういったことも発生していません。
おそらく親のいない部屋に行っても寂しいだけだからかなと思います。

今回作った装置は本当にちょっとしたものですが、子育ての課題を技術で解決するのは楽しいだけではなく達成感もあります。
実装に使用したobnizは私のようなJavaScriptぐらいしか書けないIoT初心者でも本当に手軽にIoTを始められます。
ぜひみなさまもソフトウェアだけでは限界がある課題に対してobnizを使ったIoTでのアプローチも試してみてください。