赤外線リモコン
IoTCapに付いている赤外線が意外に使えるので、しっかりとリモコン化してみよう。 まずは家にある各種リモコンのキャプチャだ。サンプルプログラムでキャプチャが可能。
sudo ./getIR2
としてから数秒以内にIoTCapに向けてリモコンボタンを押せば、その内容を表示してくれる。
照明器具
家の天井照明は、ほぼリモコン化されている。Panasonicのシーリングライトだ。
リモコン側にはチャンネルが3つ用意されており、複数台使用しても振り分けできるようになっている。それぞれのチャンネルですべてキャプチャしてみる。
| 機器 | 動作 | データ(CH1) | データ(CH2) | データ(CH3) |
|---|---|---|---|---|
| リビング シーリング ライト | 点灯(単純) | AEHA 40 44 82 9 45 36 | AEHA 40 44 82 9 53 60 | AEHA 40 44 82 9 61 52 |
| 消灯 | AEHA 40 44 82 9 47 38 | AEHA 40 44 82 9 55 62 | AEHA 40 44 82 9 63 54 | |
| 全灯(中間色) | AEHA 40 44 82 9 44 37 | AEHA 40 44 82 9 52 61 | AEHA 40 44 82 9 60 53 | |
| 昼白色 | AEHA 40 44 82 57 138 179 | AEHA 40 44 82 57 140 181 | AEHA 40 44 82 57 142 183 | |
| 電球色 | AEHA 40 44 82 57 139 178 | AEHA 40 44 82 57 141 180 | AEHA 40 44 82 57 143 182 | |
| エコナビ | AEHA 40 44 82 57 128 185 | AEHA 40 44 82 57 131 186 | AEHA 40 44 82 57 134 191 | |
| 常夜灯 | AEHA 40 44 82 9 46 39 | AEHA 40 44 82 9 54 63 | AEHA 40 44 82 9 62 55 | |
| 白く | AEHA 40 44 82 57 144 169 | AEHA 40 44 82 57 148 173 | AEHA 40 44 82 57 152 161 | |
| 赤く | AEHA 40 44 82 57 145 168 | AEHA 40 44 82 57 149 172 | AEHA 40 44 82 57 153 160 | |
| 明るく | AEHA 40 44 82 9 42 35 | AEHA 40 44 82 9 50 59 | AEHA 40 44 82 9 58 51 | |
| 暗く | AEHA 40 44 82 9 43 34 | AEHA 40 44 82 9 51 58 | AEHA 40 44 82 9 59 50 | |
| 環境設定 | AEHA 40 44 82 57 130 187 | AEHA 40 44 82 57 133 188 | AEHA 40 44 82 57 136 177 | |
| 明るさ調整 | AEHA 40 44 82 57 129 184 | AEHA 40 44 82 57 132 189 | AEHA 40 44 82 57 135 190 |
テレビ
次にテレビリモコンを取得
| 機器 | 動作 | データ | 動作 | データ |
|---|---|---|---|---|
| テレビ 基本ボタン | 電源 | NEC 32 64 191 18 237 | 決定 | NEC 32 64 191 61 194 |
| 地デジ | NEC 32 64 191 122 133 | 上 | NEC 32 64 191 62 193 | |
| BS | NEC 32 64 191 124 131 | 下 | NEC 32 64 191 63 192 | |
| CS | NEC 32 64 191 125 130 | 左 | NEC 32 64 191 95 160 | |
| 入力(UP) | NEC 32 64 191 58 197 | 右 | NEC 32 64 191 91 164 | |
| 入力(DOWN) | NEC 32 64 191 15 240 | メニュー | NEC 32 64 191 208 47 | |
| CH(UP) | NEC 32 64 191 27 228 | 番組表 | NEC 32 64 191 110 145 | |
| CH(DOWN) | NEC 32 64 191 31 224 | 戻る | NEC 32 64 191 59 196 | |
| 音量(UP) | NEC 32 64 191 26 229 | 終了 | NEC 32 64 191 60 195 | |
| 音量(DOWN) | NEC 32 64 191 30 225 | 上(再生) | NEC 32 64 190 32 223 | |
| 画面表示 | NEC 32 64 191 28 227 | 下(停止) | NEC 32 64 190 33 222 | |
| ミュート | NEC 32 64 191 16 239 | 左(巻戻) | NEC 32 64 190 34 221 | |
| クイック | NEC 32 64 191 39 216 | 右(早送) | NEC 32 64 190 35 220 |
各種チャンネルボタンはこちら
| 動作 | データ(地デジ) | データ(BS/CS) |
|---|---|---|
| CH01 | NEC 32 64 191 1 254 | NEC 32 64 191 97 158 |
| CH02 | NEC 32 64 191 2 253 | NEC 32 64 191 98 157 |
| CH03 | NEC 32 64 191 3 252 | NEC 32 64 191 99 156 |
| CH04 | NEC 32 64 191 4 251 | NEC 32 64 191 100 155 |
| CH05 | NEC 32 64 191 5 250 | NEC 32 64 191 101 154 |
| CH06 | NEC 32 64 191 6 249 | NEC 32 64 191 102 153 |
| CH07 | NEC 32 64 191 7 248 | NEC 32 64 191 103 152 |
| CH08 | NEC 32 64 191 8 247 | NEC 32 64 191 104 151 |
| CH09 | NEC 32 64 191 9 246 | NEC 32 64 191 105 150 |
| CH10 | NEC 32 64 191 10 245 | NEC 32 64 191 106 149 |
| CH11 | NEC 32 64 191 11 244 | NEC 32 64 191 107 148 |
| CH12 | NEC 32 64 191 12 243 | NEC 32 64 191 108 147 |
そのほかの特殊ボタン(その後増えたボタンも追加)
| 機器 | 動作 | データ |
|---|---|---|
| テレビ その他 | dデータ | NEC 32 67 188 20 235 |
| 青 | NEC 32 64 191 115 140 | |
| 赤 | NEC 32 64 191 116 139 | |
| 緑 | NEC 32 64 191 117 138 | |
| 黄 | NEC 32 64 191 118 137 | |
| 番組説明 | NEC 32 64 191 113 142 | |
| 早送り | NEC 32 64 190 46 209 | |
| 巻き戻し | NEC 32 64 190 44 211 | |
| 1つ次 | NEC 32 64 190 38 217 | |
| 1つ前 | NEC 32 64 190 39 216 | |
| 2画面 | NEC 32 64 191 41 214 | |
| 静止 | NEC 32 64 191 80 175 | |
| 音多切替 | NEC 32 64 191 19 236 | |
| 始めにジャンプ | NEC 32 64 190 71 184 | |
| 過去番組表 | NEC 32 64 190 53 202 | |
| ざんまい | NEC 32 64 190 76 179 | |
| 録画リスト | NEC 32 64 190 40 215 | |
| ボイス | NEC 32 64 190 69 186 | |
| 字幕 | NEC 32 67 188 82 173 | |
| まるごとch | NEC 32 64 191 38 217 | |
| シーン検索 | NEC 32 64 190 77 178 | |
| 設定 | NEC 32 64 191 208 47 | |
| 音声切替 | NEC 32 64 191 19 236 | |
| クリア音声 | NEC 32 64 190 70 185 | |
| 次みるTV | NEC 32 64 190 93 162 | |
| みるコレ | NEC 32 64 190 52 203 | |
| サブメニュー | NEC 32 64 191 39 216 | |
| 4K | NEC 32 64 190 124 131 | |
| スカパー | NEC 32 64 190 79 176 | |
| 4th-media | NEC 32 64 190 42 213 | |
| netflix | NEC 32 64 191 157 98 | |
| TUTAYA-TV | NEC 32 64 191 54 201 | |
| abemaTV | NEC 32 64 191 215 40 | |
| hulu | NEC 32 64 191 152 103 | |
| u-next | NEC 32 64 191 153 102 | |
| you tube | NEC 32 64 191 47 208 | |
| dTV | NEC 32 64 191 156 99 |
スポットクーラー
スポットクーラーがあったので拾ってみる
| 動作 | データ |
|---|---|
| 電源 | NEC 32 0 255 70 185 |
| モード | NEC 32 0 255 9 246 |
| 風量 | NEC 32 0 255 94 161 |
| おやすみ | NEC 32 0 255 74 181 |
| タイマー | NEC 32 0 255 66 189 |
| 上 | NEC 32 0 255 7 248 |
| 下 | NEC 32 0 255 12 243 |
ケース
ラズパイzero+IoTCap向けケース
ケースの作成を行う。案外赤外線がしっかりと飛んで、反応が良いので、裸のラズパイZW+IoTCAPの状態から、 ケースに入れたものにして、きちんと運用したいと思う。ケースは部屋の壁に取り付ける。テレビのちょうど真向いの壁の予定。
ラズパイZEROの寸法図は公式ページからドキュメントとしてたどれるので、 こちらを参照。 トップから行くには、「HELP→ドキュメント→ハードウェア→ラズパイ→Mechanical drawings→調べたいラズパイ」でたどればよい。

これを参考にしつつ、実寸を測定しながら、位置合わせの試作品を何個か作って、最終形が完成。 まずは、ケースの底側。IoTCAPとの接続ねじとGPIOのはんだ跡の逃がし穴を用意。周りのふちは石膏ボードの壁にホッチキスで取り付けるための枠。

そして、ケースの蓋側。こちらは、LCD表示エリア、BME280接続穴、PD受信穴、赤外線LED穴そしてスイッチ穴が開けてある。 表面実装のチップLEDは、案外明るく穴開けておくとかなりまぶしいので、薄い板で覆うようにした。これでも十分光っているのが外から見えた。

そして、本体をケースに収めた状態。左側にはミニカメラも装着済み。ここまでに試作品は10個近く作っており、 位置合わせはほぼOK。嵌合は相変わらずうまく作れず、なんとなく無理やりはまっている状態だ。 ここにBME280を取り付けたら、完成。

壁取付
壁への取り付けは、ホッチキスを使って止める。あらかじめコンセントにUSB電源を用意し、電源用USBケーブルを這わして、 届く位置を決定。また、テレビと部屋のシーリングライトが主な操作対象なので、それらに赤外線が照射可能な位置の壁と高さを決める。
そして、取り付け実施。ケースの蓋をした状態だと、ホッチキスの頭が当たって、きちんと取り付けできないことがわかり、 いったんケースは蓋や本体を取り外して、台座部分だけで壁への取り付けを行う。 本体は軽いので、ホッチキス止めでも十分支えてくれる。しかし何かぶつかったりなどで簡単に外れても困るので、 ある程度ホッチキスはたくさん打ち込んでおく。
壁に台座が取り付けられたら、本体と蓋を取り付ければ完了。本体と台座は両面テープで止めている。 ねじ止めしたいところだが、うまい機構が考え付かなかった。蓋がそこそこしっかりはまっているので、 台座を残して本体がポロリは無いはずだ。

将来機能として、カメラを装備。でもラズパイzeroには処理が重いので取り外した。
主目的のテレビと部屋の照明は、うまく反応してくれる。いろいろ活用できそうだ。
シャットダウンボタン
ボタンを活用して、シャットダウン機能を装備する。 とおもったが、サンプルに既に「poweroff」が存在。シェル版とpython版が用意されていた。
#! /usr/bin/python
# for IoTCAP
# (C)Copyright 2018 All rights reserved by Y.Onodera
### global
GPIO_SW1=22
import RPi.GPIO as GPIO
import time
import sys
import os
### function
def callbackSW1(pin):
i=0
while True:
time.sleep(0.1)
i+=1
if i > 50:
# sys.exit(0)
os.system('sudo poweroff')
break
if GPIO.input(GPIO_SW1) == 1:
break
### main routine
GPIO.setwarnings(False)
GPIO.setmode(GPIO.BCM)
GPIO.setup(GPIO_SW1, GPIO.IN, pull_up_down=GPIO.PUD_UP)
GPIO.add_event_detect(GPIO_SW1, GPIO.FALLING)
GPIO.add_event_callback(GPIO_SW1, callbackSW1)
try:
while True:
time.sleep(1)
except:
GPIO.cleanup() 5秒長押しするとシャットダウンが動作するというもの。 これを見ると、ボタン押下はイベント登録して、コールバックで動かしている。 pythonをあまり知らないので、こんな書き方があるのを知らなかった。
このままだと、5秒経過でのシャットダウンの開始が見えないので、 途中LEDを光らせて、シャットダウン受付中のレスポンスを返そうと思う。
#!/usr/bin/python
# coding:utf-8
import RPi.GPIO as GPIO
import time
import sys
import os
import smbus
#GPIO初期化
GPIO.setwarnings(False)
GPIO.setmode(GPIO.BCM)
#GPIO23pinを入力モードとし、pull up設定とします
GPIO_SW2 = 23
GPIO.setup(GPIO_SW2, GPIO.IN, pull_up_down=GPIO.PUD_UP)
#GPIO05(Blue)を出力モードとします
led_port=5
GPIO.setup(led_port, GPIO.OUT)
GPIO.output(led_port, GPIO.LOW)
#GPIO06(White)を出力モードとします
wh_port=6
GPIO.setup(wh_port, GPIO.OUT)
GPIO.output(wh_port, GPIO.LOW)
### function 長押し検知してシャットダウン処理
def callbackSW2(pin):
i=0
while True:
time.sleep(0.1)
i+=1
if i == 1:
GPIO.output(led_port, GPIO.LOW)
if i == 30:
GPIO.output(led_port, GPIO.HIGH)
if i == 35:
GPIO.output(led_port, GPIO.LOW)
if i == 40:
GPIO.output(led_port, GPIO.HIGH)
if i == 45:
GPIO.output(led_port, GPIO.LOW)
if i > 50:
GPIO.output(led_port, GPIO.HIGH)
# sys.exit(0)
os.system('sudo poweroff')
break
if GPIO.input(GPIO_SW2) == 1:
break
#GPIO23pinのボタン押下をイベント登録
GPIO.add_event_detect(GPIO_SW2, GPIO.FALLING)
GPIO.add_event_callback(GPIO_SW2, callbackSW2)
try:
while True:
time.sleep(0.2)
except:
GPIO.cleanup()これで、長押し3秒でLEDが点灯、3.5秒で消灯、4秒で点灯、4.5秒で消灯、そして5秒で点灯してシャットダウン動作に入る。 3回目の点滅が来たらシャットダウン開始だ。それ以前に指を離せば、セーフ。
時計表示
LCDがあるので、時計を表示させたい。時計表示もサンプルは用意されているが、「clock.sh」のシェルのみ。 内部ではシェルとCの処理を呼び出して、動作している。なので、これをpython化してみる。
python版の時計表示サンプルは無いが、初期化「initLCD.py」、カーソル移動「locateLCD.py」、表示「printLCD.py」は、 用意されているので、これらを組み合わせて、時計表示を作ってみよう。 また、上記シャットダウン処理に組み合わせることとする。
ということで、組み合わせて作ってみた。
#!/usr/bin/python
# coding:utf-8
import RPi.GPIO as GPIO
import time
import sys
import os
import smbus
import datetime
#LCD接続設定
bus_number=1
addr=0x3e
lcd_cmd=0x00
lcd_dat=0x40
#GPIO初期化
GPIO.setwarnings(False)
GPIO.setmode(GPIO.BCM)
#GPIO23pinを入力モードとし、pull up設定とします
GPIO_SW2 = 23
GPIO.setup(GPIO_SW2, GPIO.IN, pull_up_down=GPIO.PUD_UP)
#GPIO05(Blue)を出力モードとします
led_port=5
GPIO.setup(led_port, GPIO.OUT)
GPIO.output(led_port, GPIO.LOW)
#GPIO06(White)を出力モードとします
wh_port=6
GPIO.setup(wh_port, GPIO.OUT)
GPIO.output(wh_port, GPIO.LOW)
#LCD初期化(I2C使用)
bus=smbus.SMBus(bus_number)
time.sleep(0.1)
bus.write_i2c_block_data(addr, lcd_cmd, [0x38, 0x39, 0x14, 0x70, 0x56, 0x6c])
time.sleep(0.3)
bus.write_i2c_block_data(addr, lcd_cmd, [0x38, 0x0c, 0x01])
bus.write_i2c_block_data(addr, lcd_cmd, [0x05, 0x01])
### function 長押し検知してシャットダウン処理
def callbackSW2(pin):
i=0
while True:
time.sleep(0.1)
i+=1
if i == 1:
GPIO.output(led_port, GPIO.LOW)
if i == 30:
GPIO.output(led_port, GPIO.HIGH)
if i == 35:
GPIO.output(led_port, GPIO.LOW)
if i == 40:
GPIO.output(led_port, GPIO.HIGH)
if i == 45:
GPIO.output(led_port, GPIO.LOW)
if i > 50:
GPIO.output(led_port, GPIO.HIGH)
# sys.exit(0)
os.system('sudo poweroff')
break
if GPIO.input(GPIO_SW2) == 1:
break
#GPIO23pinのボタン押下をイベント登録
GPIO.add_event_detect(GPIO_SW2, GPIO.FALLING)
GPIO.add_event_callback(GPIO_SW2, callbackSW2)
try:
while True:
time.sleep(0.2)
# LCDカーソルをHomeポジションへ
bus.write_i2c_block_data(addr, lcd_cmd, [0x80])
# 現在日時
now = datetime.datetime.today().isoformat()
# LCD表示(時刻部)
for ix in range(11,19):
bus.write_i2c_block_data(addr, lcd_dat, [ord(now[ix])])
#LED点滅
if (ord(now[18]) % 2) == 0:
GPIO.output(wh_port, GPIO.LOW)
else:
GPIO.output(wh_port, GPIO.HIGH)
except:
GPIO.cleanup()1秒ごとにLEDも点滅させてみた。これで動いている感じが出る。本当は0.5秒点滅にしてみたかったが、 検知が難しそうなのと、pythonがそんなに早く動かないので、1秒単位での点滅として秒が奇数/偶数かで点灯/消灯させている。
常駐化
起動時に動作するように常駐化させてみる。まずはサービスファイルを作成。「etc下のsystemd/systemに作成する」
sudo nano iotcap.service
内容は、以下の感じとする。
[Unit]
Description =ShutdownButton and TimeDisplay by IoTCAP
[Service]
ExecStart=/usr/bin/python3 /home/{ユーザ名など}/IoTCAP/iotcap.py
Restart=no
Type=simple
[Install]
WantedBy=multi-user.target保存したら、まずはデーモン再読み込み。
sudo systemctl daemon-reload
認識確認。
systemctl list-unit-files –type=service | grep iotcap
iotcap.service disabled
では、一旦動かしてみる。
sudo systemctl start iotcap
うまく起動したかステータスを見てみよう。
systemctl status iotcap
● iotcap.service - ShutdownButton and TimeDisplay by IoTCAP
Loaded: loaded (iotcap.service; disabled; vendor preset: enabled)
Active: active (running) since Sun 2018-10-** **:**:** JST; 27s ago
Main PID: 1087 (python3)
CGroup: /system.slice/iotcap.service
mq1087 /usr/bin/python3 IoTCAP/iotcap.py
10月 ** **:**:** raspberrypi systemd[1]: Started ShutdownButton and TimeDisplay by IoTCAP.
IoTCAP側は時計が表示されて、LEDも点滅開始。systemctlのステータスも問題なしだ。 では、自動起動を設定しておこう。
sudo systemctl enable iotcap
Webサーバー化
このラズパイでWebサーバーを立てて、各種処理を起動出来れば、離れたクライアントからいろいろ操作ができる。 Nodejs使ってWebサーバを立てて、スマホからのリモート操作できるようにしてみよう。
APIサーバの準備
サーバ処理はexpressを使う。Expressのinit用にgeneratorをインストールする。
sudo npm install -g express-generator
インストールしたらプロジェクト作成。
express –ejs smarthome
作成したら、ミドルのインストールをしておこう
cd smarthome
npm install
基本的な準備完了。
では、app.jsを少し変更。apiへのルーティング作成。
var createError = require('http-errors');
var express = require('express');
var path = require('path');
var cookieParser = require('cookie-parser');
var logger = require('morgan');
var indexRouter = require('./routes/index');
var apiRouter = require('./routes/restapi');
var app = express();
// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'ejs');
app.use(logger('dev'));
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));
// cors
app.use(function(req, res, next){ // add
res.header("Access-Control-Allow-Origin","*");
res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
next();
});
app.use('/', indexRouter);
app.use('/api', apiRouter);
// catch 404 and forward to error handler
app.use(function(req, res, next) {
next(createError(404));
});
// error handler
app.use(function(err, req, res, next) {
// set locals, only providing error in development
res.locals.message = err.message;
res.locals.error = req.app.get('env') === 'development' ? err : {};
// render the error page
res.status(err.status || 500);
res.render('error');
});
module.exports = app;8行目で「apiRouter」を作成
21~26行目はCORS設定
29行目で「/api」をapiRouterに接続
併せて、route/users.jsファイルをroute/restapi.jsにリネームして内容を下記に変更。
var express = require('express');
var router = express.Router();
/* GET API */
router.get('/', function(req, res, next) {
var param = {"値":"サンプルAPI返却"};
res.header('Content-Type', 'application/json; charset=utf-8');
res.send(param);
});
module.exports = router;では、初回起動を行うが、その前にnodemonを入れておく。
npm install nodemon –save
これを入れて起動すれば、いちいち再起動しなくてもExpressを再起動してくれて便利だ。
npx nodemon ./bin/www
では、起動する。 まずは、ブラウザでアクセスしてみる。
http://raspberrypi.local:3000/
Expressの初期画面が出れば、とりあえずOK。index.js処理はできている様子(デフォルト版) では、API側も見てみよう。ブラウザでurlを少し変更
http://raspberrypi.local:3000/api
こんな結果が返ってくればOKだ。
{"値":"サンプルAPI返却"}
では、apiが呼び出されると、リモコン操作が動作するようにしてみよう。 まずは、送信処理を「/usr/local/bin」に置いておく。
sudo cp setIR2 /usr/local/bin/setIR2
apiで「/light/on」で部屋の明かり操作が呼び出されるようにする。 外部処理の呼び出しには、「child_process」を使用する。
var express = require('express');
var exec = require('child_process').exec;
var router = express.Router();
/* GET API */
router.get('/', function(req, res, next) {
var param = {"値":"サンプルAPI返却"};
res.header('Content-Type', 'application/json; charset=utf-8');
res.send(param);
});
router.get('/light/:id', function(req, res, next) {
if (req.params.id == 'on') {
// 照明点灯
exec('sudo /usr/local/bin/setIR2 AEHA 40 44 82 9 45 36', function (err, stdout, stderr) {
if (err) { console.log(err); }
});
} else if (req.params.id == 'off') {
// 照明消灯
exec('sudo /usr/local/bin/setIR2 AEHA 40 44 82 9 47 38', function (err, stdout, stderr) {
if (err) { console.log(err); }
});
}
res.header('Content-Type', 'application/json; charset=utf-8');
res.send({ status: '200', msg: 'OK' });
});
module.exports = router;2行目で「child_process」を準備
12行目で「/light/on」にアクセスがあったら「setIR2」をchild_processで呼び出す。onかoffを区別して点灯か消灯かを呼び出している。
これで、サーバにアクセスしてみると
http://raspberrypihome.local:3000/api/light/on
無事赤外線LEDから信号が飛んで、部屋の明かりが点灯した。これで問題なさそうだ。 あとは、各種コマンドに応じたapiを用意すればOK。
遠い昔は、寝るとき部屋の明かりを消すために長い紐を用意して、布団の中から明かりを消していた。そして今はリモコン化されているから、リモコンを手元に置いて消灯。そして今回はスマホから指示を出すことで消灯が可能に。
クライアント画面の準備
クライアント画面はvueで作ってみる。