たねやつの木

Photographs, Keyboards and Programming

【Node.js】スマートプラグ + Raspberry Piで水槽周りの電源管理(tp-link HS105)

こんばんは、たねやつです。

スマートアクアリウムに関しては結構久しぶりの更新になるのではないでしょうか??(笑)

CO2点火装置などに使用しているタイマー付きコンセントを流行りのIoTコンセント(スマートプラグ)に置き換えてRaspberry Piからオンオフを管理するようにしました。管理するといっても昼間にオンにして夜中はオフにするだけという単純なものです。

アクアリウム用だけでなくほかの家電制御にも割と汎用的に使える内容になっていると思います😎

前の記事

関連性はアクアリウム関係というだけですが前回は水温の取得ができるまでを進めました。

正直言って、tp-linkが配布しているマネジメント用のアプリを使うことで簡単に(Raspberry Pi無しで)スマートプラグの電源の管理やスケジューリングは行うことができます。しかし管理環境をRaspberry Pi上に集約することによって保守しやすくなると思います。

使用するもの

Raspberry Pi

どのバージョンでも大丈夫ですがRaspberry Pi Zero Wで動作確認しています。

秋葉原などの実店舗などで買うほうが安いです😏

tp-link HS105

ルーターやハブでtp-linkの他製品を使用している、Node.jsもモジュールを使用できるという点でこれを選択しました。今回は2つ使う(日中用電源、夜間用電源)のでセットのものを購入しました。日中だけでOKな場合は1つでも大丈夫です。

結構頻繁にセールを行っているので安いときに買うとお得です。

Node.js

今回はNode.jsで動作するモジュールを使用してスマートプラグの管理を行います。ので少しだけNode.jsとJavaScriptの知識が必要となるかもしれません。

Node.jsをRaspberry Piにインストールする方法は以下の私の記事でも紹介しています。

タイトルはZero用となっていますが普通の3BなどでもOKです!

構成

今回は二つのスマートプラグを使用します。なぜ2つ必要かというと日中用と夜間用で電源のオンオフを切り分けるためです。日中用のほうにはエアレーションCO2添加装置(電磁弁)水流用のポンプを接続し、夜間用にはエアレーションを接続します。

スマートプラグでは電源への配線を気にしなくてよい(というかそれ自体が電源)ので配置を考えるのは簡単ですね!今既に稼働しているプラグにそのままポンとつけるだけでほぼほぼ完了するかと思います。

一点注意が、おそらくほとんどの国内のタコ足配線ではHS105を二つ連続して挿すことができません(説明書にも注意書きとして書いてあります)。ので一つ置きに挿したりして工夫する必要があります。間のプラグは細いものであれば使用できます。

また、まずは純正のアプリを利用してWi-Fiの電波をつかむ必要があります。付属の手順に従って自宅のネットワークに参加させておいてください。(本末転倒な感じがしますが。。。(笑))

プログラム作成

プロジェクト作成

前回まで使用しているディレクトリを使用してもいいかもしれませんが、Node.jsのプロジェクトとして新たに作成します。

$ mkdir aqua
$ cd aqua
$ npm init

最初にプロジェクト名を聞かれます。今回は例としてaquaというプロジェクト名で進めていきます。その他の選択肢はデフォルトのまま進めていきます。

node-cron

スマートプラグのオンオフを定期的に実行させるために使用します。名前の通りLinuxのcronと同じようなことができます。先ほど作成したプロジェクト内に移動して、インストールを実行します。

$ npm install node-cron --save

--saveオプションを指定することによって、package.jsonというnpmプロジェクト管理用のファイルに「node-cronを使用している」という情報が追加されます。これによりgithubなどからcloneしてきたときにnpm installというコマンドを実行するだけでnode-cronをインストールすることができます。

tplink-smarthome-api

tplink-smarthome-apiというモジュールを使用して開発を進めていきます。これもnode-cronと同じ方法でインストーしておきます。

$ npm install tplink-smarthome-api --save

node-cronの動作確認

公式ドキュメントに簡単な実行例があるのでそれを参考にしてコマンドライン上で実行してみます。

$ node
> const cron = require('node-cron');
> cron.schedule('* * * * * *', () => console.log('test'));
test
test
...

二個目のコマンドを実行すると一秒ごとにtestと表示されます。日時の指定方法もcronと変わりないのでそのまま使えますね。複数のジョブも一つの処理にまとめても大丈夫そうです。

const cron = require('node-cron');
cron.schedule('* * * * * *', () => console.log('1'));
cron.schedule('*/2 * * * * *', () => console.log('2'));

これを実行するとコンソール上には112112112...と表示されます。

tplink-smarthome-apiの動作確認

コチラも公式の例を参考にして実行してみます。プロジェクト内に.jsのファイルを作成して内容を以下のようにします。

const { Client } = require('tplink-smarthome-api');

const ip = '';  // プラグのIPアドレスを指定

const client = new Client();
client.getDevice({host:ip}).then((device) => {
    device.getSysInfo().then(console.log);
    device.setPowerState(true);
});

そのあとにコンソール上でnode ファイル名で実行できます。成功すればログとスマートプラグの電源がオンになります。setPowerState(false)とすれば電源をオフにすることができます。

これで一通り必要な処理は完成しています。

二つをくっつける

この二つの処理をくっつけて、昼と夜で2つのスマートプラグのオンオフを切り替えるようにしたものが以下になります。新しくjsファイルを作成してそこに記述します。

const cron = require('node-cron');
const { Client } = require('tplink-smarthome-api');

/*
 * ┌────────────── second (optional)
 * │ ┌──────────── minute
 * │ │ ┌────────── hour
 * │ │ │ ┌──────── day of month
 * │ │ │ │ ┌────── month
 * │ │ │ │ │ ┌──── day of week
 * *  *  *  *  *  *
 */

const client = new Client();

const host = {
    daytime : '192.168.xx.xx',
    night   : '192.168.yy.yy',
};

// 日中
cron.schedule('0,10,20,30,40,50 * * * * *', () => {
    client.getDevice({host: host.daytime}).then((dev) => dev.setPowerState(true));
    client.getDevice({host: host.night}).then((dev) => dev.setPowerState(false));
});

// 夜間
cron.schedule('5,15,25,35,45,55 * * * * *', () => {
    client.getDevice({host: host.daytime}).then((dev) => dev.setPowerState(false));
    client.getDevice({host: host.night}).then((dev) => dev.setPowerState(true));
});

clientオブジェクトを使いまわしたりコールバック関数とPromiseが混在していますがとりあえず問題なく動いています。起動するにはnode [作成したファイル名]で起動します。

スマートプラグのIPアドレスは無線LANルーターなどから固定しておく必要があります。

この状態だと5秒おきに二つのスマートプラグのオンオフが入れ替わります。例えば08:00~18:00の間は日中用のプラグ、それ以外の時間には夜間用のプラグをオンにするのであれば、

// 日中
cron.schedule('0 0 8 * * *', () => { });

// 夜間
cron.schedule('0 0 18 * * *', () => { });

とします。これで一通り完了です!

後はデータベースへの状態を登録したりほかの処理と連携させることができます。スマートプラグ毎に処理を切り分けてコマンドラインからオンオフできるようにしてもいいかもしれません。

foreverでデーモン化

foreverというコマンドを利用することでバックグラウンドで実行し停止時に自動で再起動などしてくれます。以下のコマンドでインストール、起動できます。

$ sudo npm install -g forever
$ forever start [作成したファイル名]
もしくは
$ forever start --minUptime 1000 --spinSleepTime 1000 [作成したファイル名]

Raspberry Piの起動時と同時に実行する方法については以下の記事で紹介しています。

次の記事

スマートアクアリウム関係は未定ですがまだもう少し続くと思いますのでお楽しみに😎

Raspberry PiにSSH接続して最初にやることまとめ

個人メモ用にRaspberry PiにSSH接続してからやることをまとめておきます。SSH接続するまでは以下の記事にまとめています。

流れ

useradd

まずはpiでログインする。このユーザーは新しくsudoグループに所属しているユーザーを追加するまで使用する。

任意のユーザー名でユーザーを作成する。デフォルトで追加されているpiユーザーはセキュリティ強化のために後程削除する。

$ sudo useradd taneyats

途中にユーザーに関する情報(名前など)を登録するプロンプトが出てくるがエンター連打で問題ない。

この辺は直接Raspberry Piに接続して設定しているときにやっておいてもいいかもしれない。

usermod -aG

先ほど追加したユーザーをメインで使用するために、piの所属しているグループをそのまま使用する。groups ([username])コマンドで現在のユーザーの所属するグループ(usernameを指定した場合はそのユーザーの所属するグループ)を表示することができる。

(piでログイン)
$ groups
pi adm dialout cdrom sudo audio video plugdev games users input netdev gpio i2c spi

-Gオプションでグループを変更することができる。この時-aオプションを指定しないと変更前に所属しているグループから外されてしまうのでユーザーにグループを追加するときには-aGを使用する。

書式はusermod -aG group1,group2,.... usernameで複数のグループを追加できる。

$ sudo usermod adm,dialout,cdrom,sudo,audio,video,plugdev,games,users,input,netdev,gpio,i2c,spi taneyats
$ groups taneyats
taneyats adm dialout cdrom sudo audio video plugdev games users input netdev gpio i2c spi

sudoグループを追加することによってsudoコマンドを実行することができるようになる。権限を必要としない場合にはこのグループは外しておく。

passwd root

rootユーザーのパスワードを設定する。

$ sudo passwd root

userdel pi

先ほど追加したユーザーでsudoコマンドが実行できることを確認してからpiを削除する。sudoできるかどうかの確認もかねて一旦ターミナルの接続を切って、先ほど追加したユーザーでログインしなおす。削除対象のユーザーのプロセスが残っていると削除できない(ターミナルでpiにログインしたままではpiは削除できない)ので注意。

$ exit
...
> ターミナルで追加したユーザーでログイン実行
$ whoami
taneyats
$ sudo userdel pi
[sudo] password for taneyats:
$ su - pi
No passwd entry for user 'pi'

これでユーザー関係の初期設定は完了。

apt-get update / upgrade

各パッケージのアップデートを行う。

$ sudo apt-get update
...
$ sudo apt-get -y upgrade

たまに大きな変更があった場合のnoticeや設定を選択するためのプロンプトで止まっていることがあるのでたまにチェックする。

apt-get install

とりあえずvim git screen vsftpdぐらいは入れておく。

$ sudo apt-get install vim git screen vsftpd

デフォルトのエディタをvimに変更する

Linux側から開かれるデフォルトのエディタを変更する。sudo visudoやgitのコミット時にコメントが未設定の時に表示される。update-alternativesコマンドを使用する。初期セットアップ時以外にほぼほぼ使用することがないので結構構文を忘れる。。。どんなコマンドでも使い方を何となく忘れてしまったときには-h--helpオプションを指定して確認しよう。

$ sudo update-alternatives --config editor
There are 4 choices for the alternative editor (providing /usr/bin/editor).

  Selection    Path                Priority   Status
------------------------------------------------------------
* 0            /bin/nano            40        auto mode
  1            /bin/ed             -100       manual mode
  2            /bin/nano            40        manual mode
  3            /usr/bin/vim.basic   30        manual mode
  4            /usr/bin/vim.tiny    15        manual mode

Press <enter> to keep the current choice[*], or type selection number: 3
update-alternatives: using /usr/bin/vim.basic to provide /usr/bin/editor (editor) in manual mode

バックスペース時のベル音を消す

ターミナルで接続しているときにこれ以上消す文字が無いときにバックスペースを押すと、ビープ音が鳴るのが個人的には結構鬱陶しいので消す。/etc/inputrc内の10行目あたりにset bell-style noneという設定がコメントアウトされているので#を消して有効化する。次回ログイン時から有効になる。

ここで設定してもlessvim内では有効になっているのでそれも各自設定してあげる必要がある。これに関しては別記事でまとめてある。

Node.js

外部からスクリプトを取得してきてそれを使用してインストールする。aptで管理されているものではバージョンが古いので注意。自分はサーバーサイドの処理を可能な限りNode.jsで書くようにしています。

以下のドキュメントを参考。

Raspberry Pi Zeroの場合はCPUのアーキテクチャがarmv6lというものなので上のスクリプトでは導入できない。

以下の記事に切り分けたのでそちらを参照。

導入が完了したらとりあえずforeverあたりを入れておく。

PowerShell, Linuxでビープ音を消す方法

PowerShell(やcmd)コンソールで、入力文字が無いのにバックスペースキーを押したときになるポロロンという音(ビープ音)を消す方法についてです。音楽を聴いたりしているときや集中しているときにこれが鳴ると結構イライラしますよね(;'∀')

PowerShellからsshでLinuxマシンに接続したときにLinux側でも設定が必要なのでそちらについても記載しています。

Windows(PowerShell)での設定

ログイン時に実行されるプロファイル(Microsoft.PowerShell_profile.ps1)に設定を追加することで音を消すことができます。Linuxでの~/.bashrcのようなファイルのようです。

まずはPowerShellを管理者権限で実行します。アイコンを右クリックすると管理者として実行するというコマンドがあるのでそれを選択します。起動したら以下のコマンドを実行して設定ファイルを作成し、内容を追加します。以下のパスはWindows 10の場合です。

ni C:\Windows\System32\WindowsPowerShell\v1.0\Microsoft.PowerShell_profile.ps1 -Value "Set-PSReadlineOption -BellStyle None"

niコマンドはPowerShellでのNew-Itemコマンドのエイリアスです。Linuxでのtouchのようなものです。-Valueオプションで指定した文字列を内容としたファイルを作成できるので今回はそれを使用しています。

Microsoft.PowerShell_profile.ps1が既に存在している場合にはファイル作成時にエラーとなり上書きされることはありません。(私の環境では存在していませんでした。)

ni : ファイル 'C:\Windows\System32\WindowsPowerShell\v1.0\Microsoft.PowerShell_profile.ps1' は既に存在します。

これでコンソールを開きなおすとビープ音が消えます。

しかし、sshなどでLinuxマシンに接続すると再度ビープ音が発生します。PowerShell自体の設定で何とかなるかと思っていたのですが、音の根源から絶たなければならないようです・・・

Linuxでの設定

(Raspberry Pi上で動いているDebian stretchで確認しています。)

コマンドライン上のビープ音

/etc/inputrcという設定ファイルがあるのでそれを変更します。このファイルの変更にも管理者権限が必要です。

$ sudo vim /etc/inputrc
# do not bell on tab-completion
set bell-style none
# set bell-style visible

内容の20行目あたりにビープ音(ベル音)に関する設定がコメントアウトしてあります。set bell-style noneのほうのコメントアウトを解除します。

保存後マシンを再起動させると、コマンドライン上でのビープ音が消えます。inputrcだけを再度読み込ませることもできるかもしれません。

vimのビープ音

上記の設定だけでひと段落かと思いきや、どうやらvimlessコマンドで発生するビープ音は抑えきれないようです。。。

vimに関してですが~/.vimrcset visualbell追加することで消すことができます。

$ vim ~/.vimrc
syntax on   " (ほかの設定。。。)
set number  " 
...
set visualbell " ビープ音を消す

これで完了です。

【Node.js】foreverコマンドの基本的な使い方まとめ

079A9455.jpg

(過去に書いた記事を大きく修正してまとめなおしたものです。)

できること

node hoge.jsをバックグラウンドで実行、異常終了時に自動で再起動できるようになります。

Raspberry Piの起動時に実行されるスクリプトの中に追加すると起動と同時に実行されるようになります。

Foreverの導入

nodeプロジェクト外使用するコマンドなのでグローバルオプションを指定してインストールします。

$ sudo npm install -g forever

導入自体は以上で完了です。root権限で実行しないとアクセス拒否でエラーとなりインストールが完了しません。

実行する

nodeコマンドの代わりにforever startを使用します。今回の例ではExpressで作成したAPIサーバーを使用します。こちらのページで同様のモノを作成していますので参考にしていただければ幸いです。

$ cd myproj
$ forever start ./bin/www

各コマンド

実行しているスクリプトの一覧を確認

forever listで実行中のスクリプトを確認することができます。このコマンドを実行しているユーザで起動しているスクリプトのみ確認できます。

$ forever list
info:    Forever processes running
data:        uid  command   script forever pid  id logfile                      uptime
data:    [0] v0P9 npm start        3897    3903    /home/user/.forever/v0P9.log 2:0:4:1.657

実行時のコマンドやログが保存されている場所・起動からどれだけ時間が経過しているかなどを確認することができます。uidの前にある追番は次項の停止用コマンドにて使用することができます。

スクリプトの停止

forever listで確認したスクリプトの追番やuidを使用してforever stop [キーとなる値]で停止させることができます。よく間違えるのですがforever killではないので注意です。

$ forever stop 0
info:    Forever stopped process:
    uid  command   script forever pid  id logfile                      uptime
[0] v0P9 npm start        3897    3903    /home/user/.forever/v0P9.log 2:0:11:50.16

$ forever stop v0P9 でも可能

ユーザーのすべてのスクリプトを停止させるときにはforever stopallを使用することが可能です。一つしかスクリプトを実行していないときならばこのコマンドで停止させるのが楽です。

別ユーザーの実行しているスクリプトを確認・停止したいときにはsudo -u [実行しているユーザー] forever ...を使用します。

npm startを実行する

forever start hoge.jsで実行されるコマンドはnode hoge.jsと同義です。が、Expressなどを使用しているとすでに用意されている起動コマンド(プロジェクトルートにてnpm start)を使用した糸気があると思います。

forever start -c "[起動コマンド]" [プロジェクトルートのpath]を使用することで起動コマンドを指定することができます。

$ pwd 
/home/user/express-proj
$ forever start -c "npm start" ./

最後に指定している[プロジェクトルートのpath]ですが絶対パスで指定すると何故かうまく起動してくれません。。同様の現象はほかの方も一部報告されているようですが解決方法は見つかりませんでしたm(__)m

ですので実行する際にはプロジェクトルートまでcdするか現在のディレクトリからの相対パスを指定してあげてください。

一応以下の--workingDir [プロジェクトルートへのpath]オプションを指定することでスクリプトを実行するディレクトリを指定することができ、絶対パスっぽく指定することはできます。

$ forever start --workingDir /home/user/express-proj -c "npm start" ./ 

最後の./は何でもいいのですがここで指定しているディレクトリにログファイルが吐かれます。とりあえずこれで動いているからいいかな程度です。

環境変数を指定する

プロパティ名=値 forever start hoge.jsと実行することでnodeコマンドで実行するときと同じように環境変数を設定することができます。-cオプションなどと組み合わせて使うことができるのですがいずれの場合でもforeverの前に環境変数を指定してあげる必要があります。

$ PORT=9999 forever start -c "npm start" ./
$ forever start -c "PORT=9999 npm start" ./ ←起動しない

(Expressの場合)二つ目のコマンドで実行するとエラーとなり起動できません。foreverコマンド自体は成功しているように見えるので注意が必要です。

マシン起動時に自動で実行するようにする

forever自体とは関係はないのですが、マシンの起動時にスクリプトを実行させるようにする方法です。今回は上記のExpressで構築したサーバーをRaspberry Pi上で動かすような感じで運用していきます。

/etc/rc.localというファイルに書き込んだことが起動時にrootユーザー実行されます。

rc.localの中身はRaspberry Piだと以下のようになっています。

#!/bin/sh -e
#
# rc.local
#
# This script is executed at the end of each multiuser runlevel.
# Make sure that the script will "exit 0" on success or any other
# value on error.
#
# In order to enable or disable this script just change the execution
# bits.
#
# By default this script does nothing.

# Print the IP address
_IP=$(hostname -I) || true
if [ "$_IP" ]; then
  printf "My IP address is %s\n" "$_IP"
fi

exit 0

割り当てられたIPアドレスを表示する処理が書いてあります。HDMIでモニターに接続して起動したときに、ズラズラっと表示される中の最後のほうにこの処理によって出力される文字列があるはずです。

この処理とexit 0の間に今回のforeverでの起動処理を追加します。

# Print the IP address
_IP=$(hostname -I) || true
if [ "$_IP" ]; then
  printf "My IP address is %s\n" "$_IP"
fi

# Start Express API server 
sudo -u user /usr/bin/forever start --workingDir /home/user/express-proj/ -c "npm start"  ./

exit 0

上のほうでも書きましたが管理者権限でこの処理は実行されるためユーザーを変えるためにはsudo -u [ユーザー名]を使用します。Raspberry Piを再起動させて、問題なくサーバーが起動していることが確認出来たら完了です!

Google Home + IFTTT + Raspberry Piで家電を制御する

f:id:ibuquicallig:20190313171642p:plain

こんにちは、たねやつです。3記事目でこれがほぼほぼ最後の記事となります。

前の記事

準備

  • Google Homeの初期設定が完了していること
  • 前回記事までのExpressで作成したサーバーが起動していること
  • Expressが動いているサーバーへ外部からアクセスできること。 ドメインの取得、DNSへの登録、自宅に割り当てられたIPアドレスが変更された際にDNSへ再登録する仕組みが常時稼働させるために必要となります。

MyDNSを使用すれば毎回変化する自宅のIPアドレスをDNSサーバーに登録するのは簡単なのですが自宅で公開するためのセキュリティ面に関してはこの記事では触れません。

作業

IFTTTで設定

IFTTTに登録する部分は割愛します。登録フォームを使用しなくてもGoogleアカウントと連携することで簡単に登録できるようです。

左上のユーザーのドロップメニューを押してNew Appletを選択し、新規で登録します。thisの部分をクリックすると条件を指定する画面に移動します。そこでGoogle Assistantを選択し、次の画面でConnectをクリックします。

クリックすると初めて利用する場合、IFTTTとGoogleアカウントを紐づけるためのポップアップが出てきますので許可を押します。これで紐づけは完了です。

次に表示される画面は認識する語句の設定例について書いてあります。

f:id:ibuquicallig:20190314231415p:plain

数値や単語を含んだ文章(「おはよう」とツイートして)などが設定できるようです。今回使用するのはフレーズも単語も使用しない単純なものを使用します。(左上)

クリックすると認識する単語の設定に移ります。

f:id:ibuquicallig:20190314231419p:plain
項目 設定値
What do you want to say? Google Homeで認識する単語
What's another way to say it? (optional) 同上(必須ではない)
And another way? (optional) 同上(必須ではない)
What do you want the Assistant to say in response? Google Homeの返事
Language 日本語

という感じです。一つのコマンドに対して最大3パターンまで認識してくれるようです。IFTTTで連携する場合曖昧な命令に対しては反応してくれませんので注意してください!(「電気を消して」と設定して「電気消して」といっても反応しない)

Google Homeからのレスポンスの設定は空にしたままだとデフォルトの文言を発してきます(OK。アクションを実行します。)。レスポンスが必要ない場合は裏技的な感じになりますが¿を指定することで無音になります。¿は全角の?を変換することで入力できます。スペイン語の疑問文で使うやつですね(;'∀')

設定したらCreate triggerをクリックするとifの部分の設定が完了します。次にthatの設定を進めてきます。

thatをクリックしてWebhooksというサービスを選択します。これによりHTTPリクエストを送ったりすることができます。さらにMake a web requestをクリックして進めてきます。

f:id:ibuquicallig:20190314231411p:plain
項目 説明 値の例
URL HTTPリクエストを投げるURL http://example:5432/lightOn
Method HTTPメソッド GET
Content Type 取得できる値の種類 text/plain
Body ボディメッセージ (空)

今までで作成したExpressの処理はアクセスしたら処理を実行するだけのものだったのでメソッドはGETでOKです。認証としてPOSTで何かしらトークンをのっけて送るのもアリなのかもしれません(;'∀')

Create actionをクリックすると確認画面が表示されます。Receive notification when this Applet runsをオフにしておくといちいち通知が飛んで来ないようになります。

Finishを押して作成完了です。実際に話しかけてみて動作を確認しましょう!

ラグについて

一つ気になるのが発話してから実際にリモコン操作が行われるまでの 遅延です(;'∀')

私の環境では言い終わってから約6~7秒ほどかかります。Google Homeからの返事を空にしても約6秒かかります。Express内での処理で1秒ちょっとかかっているのでIFTTT側の処理(言語解析など?)で5秒ぐらいかかっている様子です。

GET /lightOff 200 1208.929 ms - 10
GET /lightOn 200 1713.517 ms - 10
GET /lightOff 200 1179.990 ms - 10
GET /lightOn 200 1727.170 ms - 10
GET /lightOff 200 1162.553 ms - 10

6秒の遅延といえばWEBページであれば致命的な大きさですがスマートスピーカーにおいては意外と気にならないものですね!もう少しレスポンスを改善しようかと思ったのですがこのままにしておきます。

最後に

Google HomeとIFTTT、Raspberry Piで動かすExpressによって安価に環境を作ることはできました!一番のネックは環境を外部にさらす部分かもしれませんが声で家電が操作できるようになるのはかなり便利ですね。正直こんなことしなくても対応している家電(Philips Fueなど)を購入すればもっと敷居は下がるのかもしれないですが。。。作ることに意義があるのです!(笑)

参考

https://www.reddit.com/r/ifttt/comments/9zu6to/google_home_no_response_option/

ExpressでBroadlink RM mini3(黒豆)を制御する

f:id:ibuquicallig:20190313171642p:plain

こんにちは、たねやつです。前回の記事から続きです。

今回はRaspberry Pi上にNode.jsでAPIサーバーを建てて、HTTPリクエストを受け取ると前回記事で作成したリモコン操作用のコマンドを実行するようにします。

前の記事

前提

Raspberry Pi上でコマンドを実行するところの処理を実装する以外はvimなどを使用するよりも、Windows上にインストールしたVSCodeなどを使用したほうが楽かもしれません。デバッグもできるので詰まったときの問題解消が行いやすいです。

準備

Node.jsをインストールする

既にRaspberry Pi上に環境がある場合は不要です。

$ sudo apt-get update
$ sudo apt-get install -y nodejs npm
...

nodejsだけではnodejs用のパッケージ管理マネージャーのnpmはインストールされないので追加します。数分かかります。完了後に以下コマンドで確認します。

$ nodejs -v
v8.11.1

この環境作成時点でのnode.jsの最新LTSバージョンはv10まで上がっているがRaspberry Pi用のレポジトリ(Debianのレポジトリも?)ではそこまで到達していません。特に最新版でなくとも今回は問題ないのでそのままv8を使用していきます!

次にnpmのバージョンを上げておきます。

$ sudo npm -g install npm
...
$ npm -v
6.9.0

再帰的なコマンドになるがこれでアップグレードできます。バージョンが最新のものになっていない場合にはRaspberry Pi自体の再起動が必要かもしれません。

Expressなどその他パッケージをnpmでインストール

次にNode.jsで使用するパッケージをnpmでインストールしていきます。ExpressとはWEBアプリケーションを作るためのフレームワーク(便利ツール)です。

例えばhttp://raspi.taneyats.com/getTimeというページにアクセスすると現在時刻だけを返すページを作ったりすることができます。これをうまくpython-broadlinkと組み合わせることによって特定のページにアクセス(HTTPリクエストを投げる)すると家の電気をつけるというようなことが可能になります。Linux上のコマンドを実行することになるので言語の壁を越えて(JavaScript - Python)処理を実行することができます。

さらに拡張させるとIFTTTなどからHTTPリクエストを投げることができるので、どんどんほかのアプリや装置(Google Homeなど)と連携することができます。次の記事ではこの連携について紹介いたします。

以下のコマンドでExpressなどをインストールします。

$ sudo npm -g install express express-generator forever
...
+ express-generator@4.16.0
+ express@4.16.4
+ forever@0.15.3
added 300 packages from 187 contributors in 82.425s

express-generatorはExpressでWEBアプリケーションを作るためのテンプレートを作成することができます。foreverはExpressには直接関係しませんがNode.jsで動くサーバーをRaspberry Pi起動時に自動で動作させたり、異例に停止した場合に自動でサーバーを再起動させたりすることができます。

プロジェクトの作成

express [プロジェクト名]でプロジェクトを作成してくれます。myprojの部分は適宜名前を変えてください。

$ express myproj
create : ...

change directory:
     $ cd myproj

   install dependencies:
     $ npm install

   run the app:
     $ DEBUG=myproj:* npm start

作成が完了したら表示されている通りコマンドを実行していき依存パッケージなどをインストールします。npm installをプロジェクト直下で実行するとそのプロジェクトを動かすために必要なほかのパッケージを拾ってきます。run the appのコマンドは単純にnpm startでも大丈夫です。

$ cd pyproj
$ npm install
...
$ npm start
> node ./bin/www

スタートできたらブラウザからhttp://Raspberry Piのアドレス:3000にアクセスしてみてください。ポート番号の:3000を忘れずにつけてください。余談ですがブラウザからアクセスしようとすると基本的には80番ポートにアクセスします。明示的に指定することによってアクセス先のポートを変更できます。

簡素なWelcome to Expressというページが表示されるはずです。Raspberry Piのコンソールに戻るとアクセスしたログが残っているはずです。

GET / 200 1414.590 ms - 170
GET /stylesheets/style.css 200 16.159 ms - 111
GET /favicon.ico 404 104.069 ms - 1032

これでテンプレートから作成は完了です。gitでソースを管理するのであればこの段階で初期コミットをしておくといいかもしれません。

$ cd myproj
$ git init
$ git remote add origin [リポジトリのURL]
$ echo "node_modules" > .gitignore
$ git add .
$ git commit -m "init commit"
$ git push -u origin master

プログラムの作成

Expressのルーティング

まずはExpressでのルーティングとコマンドの実行などについて触れておきます。プロジェクト内のapp.jsというファイルを開くと以下のようになっています。

var createError = require('http-errors');
//...

var indexRouter = require('./routes/index');
var usersRouter = require('./routes/users');

var app = express();

// ...

app.use('/', indexRouter);
app.use('/users', usersRouter);

// ...

var xxxRouterの部分で処理を変数に保持し、app.use()の部分で指定したURL(エンドポイント)に処理を割り当てます。ざっくりですがこのようになっています。

ですので新たに処理を追加したい場合にはこの2か所にの追加を行う必要があります。第一引数で指定しているエンドポイントにアクセスすると第二引数の処理が走ります。

http://hogehoge/userにアクセスするとuserRouterで指定したjavaScriptの処理が実行されます。一つ目の/は特にエンドポイントを指定せずにドメイン名やIPアドレスでアクセスしたときに表示されます。先ほど確認したWelcome to Expressの画面はまさにこの処理によって表示されたものとなります。

./routes/indexと記述している箇所が処理の本体になります。プロジェクト内を見てみると確かに/routes/index.jsというファイルがありますね!

var express = require('express');
var router = express.Router();

/* GET home page. */
router.get('/', function(req, res, next) {
  res.render('index', { title: 'Express' });
});

module.exports = router;

pug(jadeから改名)というHTML生成用の別のエンジンでHTML構文が生成されていますがここでhttp://Raspberry Piのアドレス:3000にアクセスしたときの処理が記述されています。

dateコマンドの結果を返すページを作成する

それでは早速簡単な処理を作ってみます。Node.jsを実行しているマシンでのdateコマンドを表示するだけのページを作ります。

まずはapp.jsにルーティング処理を追加します。

var createError = require('http-errors');
//...

var indexRouter = require('./routes/index');
var usersRouter = require('./routes/users');
// 以下を追加
var getDateRouter = require('./routes/getDate');

var app = express();

//...

app.use('/', indexRouter);
app.use('/users', usersRouter);
// 以下を追加
app.use('/getDate', getDateRouter);

既にテンプレートを生成した段階で参考になるものは存在しているのでそれに真似て書きます。変数名なんかは一新してしまってもいいかもしれません(ルーティング処理の処理であることは明白なので)

次にアクセス時の処理を追加します。/routes/index.jsと同じ場所にgetDate.jsという名前で作成します。作成するというよりもindex.jsを複製して名前を変えたほうが手間が省けます(゜_゜)

ファイルを開いて一旦index.js特有の処理を削除します。

var express = require('express');
var router = express.Router();

/* */
router.get('/', function(req, res, next) {
    
});

module.exports = router;

次にdateコマンドを実行する処理を書いていきます。Node.jsではLinux上のコマンドを実行するためにはchild_processというモジュールのexec()という関数を使うことで簡単に実行できます。外部のモジュールを使うためにはrequireで取得して変数に格納するなどして使用します。(pythonのimportのような感じです。)

requireの部分を足すとこんな感じです。

var express = require('express');
var exec = require('child_process').exec;
var router = express.Router();

router.get('/', function(req, res, next) {

});

module.exports = router;

exec()の第一引数には実行したいコマンドをStringで指定し、第二引数には結果などを保持したコールバック関数を指定します。コールバック関数に関しては以下を参考にしてください。

Promiseなどで処理の順序を制御するともっとすんなりしたコードになりますがとりあえずこのまま進めていきます。

コールバック関数の第一引数にはエラーオブジェクト(エラーかどうかを判定するために使うはず)、第二引数には標準出力(コマンドを実行し成功したときに表示される内容)、第三引数には標準エラー出力(コマンドを実行しエラーが発生したときに表示される内容)が渡ってきます。

今回はとりあえず動かすだけなのでエラー判定は行わず、標準出力だけを使用しページに表示します。最終的なソースはこんな感じになります。

var express = require('express');
var exec = require('child_process').exec;
var router = express.Router();

/** dateの実行結果を表示します */
router.get('/', function(req, res, next) {
  exec('date', function(err, stdout, stderr) {
    res.send(stdout);
  });
});

module.exports = router;

res.send()に指定したものが画面に表示されます。APIサーバーとして使用するときにはここにオブジェクト指定して返し、クライアント側でJSON.parse()して使用するという感じがよくある流れだと思います。

一点注意ですが、Windows上で実行するdateコマンドとLinux(Raspberry Pi)上で実行するものは若干異なります。

Windows上でNode.jsを動かしている場合はコマンドにdate /tと指定してください。オプションを指定せずに実行すると現在時刻変更用のプロンプトにより処理が完了せずページが表示できません。

> date
現在の日付: 2019/03/11
新しい日付を入力してください: (年-月-日) _
(ここで入力待ちとなり処理が完了しない)

Linux上で行う場合には問題ありません。




これでhttp://[Raspberry Piのアドレス]:3000/getDateにアクセスしてみるとアクセスしたときの現在日時(Windowsの場合現在日付)が表示されているはずです。

404 Not Foundが表示される場合はルーティングが間違っている場合があるかもしれません。app.jsの変数名やファイル名を確認してください。ブラウザの読み込みが完了しない場合にはgetDate.jsの処理が間違っていてres.send()にまで到達できていない可能性があります。

前回作成したRM mini3用のコマンドを実行する

ここからが本題となります!Linuxコマンドを実行することができたので前回コマンドラインから実行したコマンドで処理を作成してみましょう!

./broadlink_cli --device @dev --send @sig/light_on

こんな感じのコマンドを作成しましたね!(^^)! light_onでシーリングライトをONにする想定で進めていきます。ほかの家電の動作を制御する場合は適宜ファイル名や変数名などを置き換えて作成してください。

まずはpython-broadlinkへのシンボリックリンクを今回のExpressのプロジェクト内に作成して簡単にアクセスできるようにします。シンボリックはWindowsでのショートカットのようなものです。

$ ln -s [broadlink_cliがあるディレクトリの絶対パス] bl_cli
$ ls -la
...
lrwxrwxrwx  1 hoge hoge   24 Mar 11 20:24 bl_cli -> ../python-broadlink/cli/
...

相対パスでも大丈夫です。リンク名はbl_cliとしてコードに書く量をなるべく少なくしていきます。次にapp.jsに追記していきます。getDateを追加したときと同じなので簡単ですね!

var indexRouter = require('./routes/index');
var usersRouter = require('./routes/users');
var getDateRouter = require('./routes/getDate');
var lightOnRouter = require('./routes/lightOn');

app.use('/', indexRouter);
app.use('/users', usersRouter);
app.use('/getDate', getDateRouter);
app.use('/lightOn', lightOnRouter);

次にlightOn.jsを作成していきます。getDate.jsをコピーして使用します。

var express = require('express');
var exec = require('child_process').exec;
var router = express.Router();

var command = './bl_cli/broadlink_cli --device @bl_cli/dev --send @bl_cli/sig/light_on';
var str;

/** シーリングライトをONにします */
router.get('/', function(req, res, next) {
  exec(command, function(err, stdout, stderr) {
    if (err) {
        str = 'error occurred';
    } else {
        str = 'success';
    }
    res.send('str');
  });
});

module.exports = router;

コマンドを変数化して見やすくしています。またシンボリックリンクを使用する場合は適切にパスを追加してください。

エラーが発生している場合には画面上にerror occurredと表示します。表示しなくとも家電がシーリングライトが動作しているかどうかで判断はできますが。。。http://Raspberry Piのアドレス:3000/lightOnにブラウザからアクセスして結果を確認してください。

以上でシーリングライトをONにする処理は完成です。OFFにする処理もちょろっと変えるだけなのでパパっと作れるかと思います。

ポート番号を変更する

プロジェクト内のbin/www3000の部分を変更するとデフォルトのポート番号が変更できます。またはnpm start時に環境変数を設定することでも変更可能です。

var port = normalizePort(process.env.PORT || '3000');
app.set('port', port);

起動時にポート番号を指定する場合には、以下のコマンドで起動します。上記の変更した箇所よりもこちらのほうが優先されます。

$ PORT=xxxx npm start       ← Linux(Raspberry Pi)の場合
> $env:PORT=xxxx; npm start ← Windowsの場合

環境変数の指定はnpm startで実行されるコマンドに混ぜ込んでしまえば毎度指定する必要がなくなります。npm startで実際に実行されるコマンドはプロジェクト内のpackage.jsonというファイルに記載されていて、script.startという部分です。ここに上記のように変数を使いしてあげるとOKです。

最後に

ここまででURLにアクセスするだけでリモコン操作ができるようになりました。別のWEBページにこのURLにアクセスするボタンなどを置くことで(WEBサーバー側からは)Raspberry Piをまったく意識することなくリモコン操作できるようになります。

Expressを使用すると簡単に処理をルーティングすることができその名の通り特急でプログラムを作り上げることができるのが魅力ですね!exec()の部分をPromiseで実装することもできるモジュールもあるのですがとりあえずコールバック関数のまま実装しました。

次の記事

IFTTTを使用してGoogle HomeからAPIを実行できるようにします。これでようやくGoogle Homeからどんなリモコンでも操作できるようになります。

参考

https://github.com/foreverjs/forever/pull/627

https://www.gesource.jp/weblog/?p=8228

https://github.com/foreverjs/forever/issues/540

Broadlink RM mini3(黒豆)をRaspberry Piで動かす

f:id:ibuquicallig:20190313171642p:plain

こんにちは、たねやつです。

現在家で使っている赤外線送受信装置がIRKitというものなのですが、送受信のたびにLEDがチカチカしたり、夜中に謎の再起動のようなものが発生して眩しい!ので数年使用した今、新しいものに変えようと思います。

Amazonなどで見ていると2000円ちょっとでこんなものがありました。これでも少し値上げしているようです。ネットで見ても参考となるような情報がわんさかありましたので乗り換えてみようと思います。

ちなみに以下の商品もありますが、今回の目的には必要ない機能(アプリから操作できる)なので値段の安い黒いほうです。アプリから操作できるというよりは黒いほうは日本の正規販売代理店経由のモノではないのではじかれるという感じらしいです(;'∀')

Raspberry Piも家で絶賛稼働中なので外部からリクエストを受け取って赤外線操作できるようにする一連の記事です。

必要なもの

Raspberry Pi

もちろんこれが必要です。種類はどれでも大丈夫ですが安いのはRaspberry Pi Zero系です。ネットで買うよりも日本橋(大阪)や秋葉原で購入するほうが圧倒的に安いです(゜_゜)

スイッチサイエンスからでもリーズナブルに手に入れることができます(一人1個まで)

Broadlink RM mini3

今回はこれを使用しますが、Broadlink社のモノであればほとんど使用できるはずです。以下ページのgendevice()という関数に存在している製品であればOKです。

初期セットアップ用アプリ

(python-broadlinkからでも設定可能なようです。すでにこちらで進めてしまったので後日追記します)

RM mini3自体を自宅のWi-Fiに接続するためには純正アプリが必要となります。AndroidのAPKファイルであれば本家サイトから取得できるようです。野良APKも落ちていますが。。。

Download APK for Androidから取得できます。

アプリを起動して、ユーザー登録(必須。。)を済ませるとデバイスを追加することができるようになるので実行。手順に従ってRM mini3の起動 > アクセスポイントの認証情報の入力 > 接続と進めていく。

数回試してみていずれも失敗した(RM mini3が見つからない)が、接続済みデバイス一覧を見てみるとちゃんと登録されていました。もちろんルータ側の管理画面からも存在しているので使える状態になっている様子です。

どのタイミングで成功したのか不明ですが、おそらく接続に成功した時点で本体のLEDの激しい点滅が停止して緩やかに点滅するようになる用です。(要詳細確認)

若干心残りはありますがとりあえずこれで進めていきます。再起動のたびにIPアドレスが変わっても困るので、ルーター側の設定からDHCPで割り当てるアドレスを固定しておきます。同時に割り当てられたアドレスとMACアドレスも確認できるはずなのでメモしておきます。MACアドレスに関してはRM mini3本体底面にもシールで貼ってあるはずです。

python-broadlinkの導入

python-broadlinkをRaspberry Pi上に導入してコマンドラインからRM mini3を操作できるようにします。あとあとREST APIサーバーをNode.js + Expressで作るため言語を合わせたかったのですが、いい感じのパッケージを見つけることができずとりあえずpython-broadlinkを使用します。

gitリポジトリから取得します。gitがRaspberry Piに導入されていない場合はsudo apt-get install gitでインストールします。基本的な使い方は別記事でも紹介しています。

ユーザーのホーム直下での作業を想定しています。依存パッケージのインストールと既に接続しているRM mini3の検知まで行います。

$ git clone https://github.com/mjg59/python-broadlink.git
$ cd python-broadlink
$ sudo python setup.py install
...
$ cd cli
$ ./broadlink_discovery
Discovering...
###########################################
Unknown
# broadlink_cli --type 0x27c2 --host 192.168.11.xx --mac xxxxxxxxxxxx
Device file data (to be used with --device @filename in broadlink_cli) :
0x27c2 192.168.11.xx xxxxxxxxxxxx

無事にRM mini3が接続できていれば上のような感じでデバイスのIPアドレスとMACアドレスが表示されているはずです!

赤外線の受信 ~ 送信まで

(特にプログラミングを覚えたいとか仕組みがどうなっているかが気になるのでなければ、次の章のbroadlink_cliを使って送信などを実行まで飛ばしてもらって大丈夫です)

準備は整ったのでリモコンの信号を受信してそれを送信するコードを実行します。テスト的にコマンドライン上で対話的に進めていきます。対話モードでは一行ずコマンドを実行しながらあれこれできます。pythonコマンドで対話モードに入ります。>>>と表示されている間は対話モードになります。抜けるにはexit()と入力するかCtrl + Dを押します。

$ cd ~/python_broadlink
$ python
>>> import broadlink                            # モジュールのインポート
>>> devices = broadlink.discover(timeout=5)     # 変数にRM mini3の情報を格納
>>> print(devices)                      # 取得できているか確認
[<broadlink.rm instance at xxxxxxxx>]   # こんな感じであればOK
>>> devices[0].auth()                   # 認証が必要(らしい)
>>> devices[0].enter_learning()         # 記録モードにする
(ここでリモコンをRM mini3に向けて押してみる)
>>> ir_packet = devices[0].check_data() # 受信したデータを変数に格納
>>> print(ir_packet)                    # 内容の確認
             ))(() # なんかバイナリ?
>>> devices[0].send_data(ir_packet)     # 記録した内容で送信する

対話的にコードを実行していけるのが普段のコーディングではなかなか味わえなくて新鮮で楽しいですね!

途中で確認用のコードを挟んだりしていますがdevices[0].check_data()で取得できる値をdevices[0].send_data()の引数にわたしてあげると送信できます。複数台のRM mini3などが検知できる場合はdevices[]のインデックスを変えることによって動作させることができるはずです。

これでコマンドライン上からRM mini3での学習と送信ができました。この方法でスクリプトを書いていってもいいかもしれませんがより便利なbroadlink_cliというスクリプトが用意されています。

broadlink_cliを使って送信などを実行

このスクリプトを使用するとより簡単に、ワンラインで信号の記録や送信を行うことができます。まずはヘルプを見てみましょう。

$ cd ~/python_broadlink/cli
$ ./broadlink_cli -h
usage: broadlink_cli [-h] [--device DEVICE] [--type TYPE] [--host HOST]
                     [--mac MAC] [--temperature] [--energy] [--check]
                     [--checknl] [--turnon] [--turnoff] [--turnnlon]
                     [--turnnloff] [--switch] [--send] [--sensors] [--learn]
                     [--learnfile LEARNFILE] [--durations] [--convert]
                     [data [data ...]]

positional arguments:
  data                  Data to send or convert

optional arguments: (一部省略)
  -h, --help            show this help message and exit
  --device DEVICE       device definition as 'type host mac'
  --turnon              turn on device
  --turnoff             turn off device
  --send                send command
  --learn               learn command
  --learnfile LEARNFILE
                        learn command and save to specified file

コマンドラインのオプションでデバイスと信号を記録したファイルを指定して送信することができます。

デバイスを指定

まずデバイスの指定ですが、ちょっと前に実行したbroadlink_discoveryで表示される一番最後の行の値を使用します。

$ ./broadlink_discovery
Discovering...
###########################################
Unknown
# broadlink_cli --type 0x27c2 --host 192.168.11.xx --mac xxxxxxxxxxxx
Device file data (to be used with --device @filename in broadlink_cli) :
0x27c2 192.168.11.xx xxxxxxxxxxxx

0x27c2 192.168.11.xx xxxxxxxxxxxxの部分です。前から製品コードIPアドレスMACアドレスとなっています。これをbroadlink_cliと同じ場所にファイルとして保存します。vimなどで保存してもいいですがechoコマンドなどを使用してこちらもワンラインで行うことができます。

$ echo '0x27c2 192.168.11.xx xxxxxxxxxxxx' > device
$ ls -la
drwxr-xr-x 2 hoge hoge 4096 Mar  9 18:00 .
drwxr-xr-x 5 hoge hoge 4096 Mar  9 00:23 ..
-rwxr-xr-x 1 hoge hoge 5830 Mar  9 00:21 broadlink_cli
-rwxr-xr-x 1 hoge hoge 1055 Mar  9 00:21 broadlink_discovery
-rw-r--r-- 1 hoge hoge   34 Mar  9 18:00 device
-rw-r--r-- 1 hoge hoge 1716 Mar  9 00:21 README.md
$ cat device
0x27c2 192.168.11.xx xxxxxxxxxxxx

catなどでしっかり保存できているか確認します。ここで保存したファイルを./broadlink_cli --device @[ファイル名]で指定することで使用できます。ファイル名の前には@をつける必要があります。

信号をファイルに記録する

次に受信した信号を同じようにファイルに記録して使いまわすことができるようにします。--learnfile [ファイル名]オプションで受信した信号を指定したファイル名で保存することができます。--learnオプションでは標準出力(コンソール上)にそのまま信号を文字列を吐きます。

今回はシーリングライトをONにする信号を保存するのでlight_onという感じのファイル名にします。機器名_動作機器名.動作という感じにしておくと判別しやすいかと思います。

$ ./broadlink_cli --device @device --learnfile light_on
Learning... (ここでリモコンを押す)
Saving to light_on
$ cat light_on
26000201...

ファイルに出力されていることを確認してください。--deviceと同じくこのファイルは--send @[ファイル名]で指定すると送信できます。

信号をファイルから送信する

最後に今保存した信号を送信してみます。

$ ./broadlink_cli --device @device --send @light_on
(電気がつく)

標準出力は特にないのでコンソールには何も表示されないです。これで一連の保存~送信までの処理が完了しました!

信号ファイルはどんどん増えていくと思うのでcli内にディレクトリを一つ作成して管理したほうが運用しやすいかもしれません。

$ mkdir sig
$ mv light_on sig/
$ ./broadlink_cli --device @device --send @sig/light_on
(電気がつく)

最後に

これでRaspberry Pi上でRM mini3を制御できるようになりました!次はREST APIサーバーを作成してHTTPリクエストのみでコマンドを実行できるようにします。より簡単に言えばhttp://raspi.taneyats.com/light_onにブラウザからアクセスすれば家のライトがつくようにしようと思います。

次の記事

参考

https://qiita.com/canadie/items/3110a0f0ff4f4749e039

http://poohkids.com/raspberrypi/raspi_pipinstall/

https://qiita.com/Tadahiro_Yamamura/items/2cbcd272a96bb3761cc8

https://qiita.com/spiderx_jp/items/8f04019c83fabdc336d4

gitでファイルのファイルのバージョン管理を行う。(2 - Raspberry Piへ導入)

git_on_raspi

この記事では前回行ったWindows上でのgitコマンドの導入とおなじく、Raspberry Pi(Linux(Debian))への導入を行います。

前の記事

導入

パッケージの最新化

$ sudo apt-get update
$ sudo apt-get upgrade

パッケージリストを最新化しておきましょう。時間に余裕があればアップグレードも行います。

apt-get

$ sudo apt-get install git

でgitコマンドを使用可能になります。Windows比べるとぐっと楽ですね!

$ which git
/usr/bin/git

$ git --version
git version 2.11.0

インストールが成功しているかどうかは上記の2つのコマンドのいずれかでとりあえず判定できます。

git

基本コマンド

前回記事で使用したclone,add,commit,pushなどはすべてLinux上でも同じように使用できます。のでここでは割愛します。

commit時にはユーザー名などを設定していないと同じようにエラーとなるので設定してあげる必要があります。

> git config --global user.name "[ユーザー名]"
> git config --global user.email [メールアドレス]

設定するユーザー名はWindowsで設定したものと異なるものにしたほうがいいかもしれません。どの環境からコミットしたものなのかを判別するためにtaneyats@raspiとかtaneyats@winという感じにしてます。

pull

他ユーザーによってpushされた変更差分を取り込むためにはpullコマンドを使用します。cloneコマンドは一番最初にすべての最新ファイルを取得するために使用します。

Windows側からcommit.txtになんでもいいので文字列を追加してcommit,pushします。

# commit.txtを何かしら修正
> cd [commit.txtのあるフォルダ]
> git add .
> git commit -m "コメント"
> git push

初回push時に自動的にorginのmasterブランチにpushするようにしているのでgit pushだけでOKです。

リモートリポジトリに反映されていることが確認出来たら、Raspberry Pi側でpullを実行します。

$ cd [プロジェクト]
$ git pull
...
Fast-forward
 commit.txt | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

最新のソースを反映させるだけであれば以上のコマンドでOKです。例によって途中でユーザー認証が挟まれますが(>_<)

コマンド実行結果の最後にプラスマイナスで差分が表示されます。

ユーザー認証の自動化

パスワードをそのまま設定ファイルに書き込むことになるのであまりお勧めできませんが、push,pull実行時のユーザー認証を自動化させることができます。

.git/configというファイルに追記します。プロジェクトルート内にある.gitというディレクトリにはこのプロジェクトのgitの設定ファイルやログなどがあります。

configというファイルを開いてみるとこんな感じかと思います。

$ cd [project]/.git
$ vim config
[core]
        repositoryformatversion = 0
        filemode = true
        bare = false
        logallrefupdates = true
[remote "origin"]
        url = https://gitlab.com/username/git_test.git
        fetch = +refs/heads/*:refs/remotes/origin/*
[branch "master"]
        remote = origin
        merge = refs/heads/master
url = https://gitlab.com/username/git_test.git

の部分を

url = https://[ユーザー名]:[パスワード]@gitlab.com/username/git_test.git

という風にするとOKです。.gitディレクトリ内のファイルはcommit,push対象にはなりません。

最後に

以上でWindowsとRaspberry Pi上で基本的なgitコマンドを使用することができるようになりました。コンパイル不要なプログラム(JavaScriptやPHP)であればこれでWindowsで開発、テスト > Raspberry Pi上に反映という流れができます。FTPなどで転送するよりもバージョン管理もできるので便利ですね。

gitでファイルのファイルのバージョン管理を行う。(1 - 導入)

git_on_raspi

こんにちは、たねやつです。

Raspberry Piでいろいろなことを行う記事を書いているのですが、gitの使い方に関して特に記事にしてなかったので残しておきます。

バージョン管理だけでなくコンパイル不要なプログラム(JavaScript, PHPとか)であればそのまま開発 → 本番化作業としても使えるのでよく使っています。

gitとは

以下のページが詳しいです。

バージョン、世代管理が主な使い方かと思いますがリモートリポジトリ(保存先)を使用することでファイル共有としても使うことができ、こっちのほうが個人的にはうれしいですね。(笑)

VSCodeのような開発環境を使用するとほぼ2クリックでファイルの転送、更新が行えるので便利です。

導入

GitLabに登録

まずはソースを保管しておくためのリポジトリを作成します。今回はGitLabというサービスを使用します。より有名なリポジトリとしてはGitHubというものがありますが、GitLabであれば無料で外部に公開しないプライベートなプロジェクトを作成することができます。

人に見られたくないブログのドラフトや完全に個人で使用するようなアプリケーションの保管先として使うことができるようになります(;'∀')




以下のページにアクセスして基本事項を入力して登録できます。

登録後に設定したメールアドレスに対して認証が送られてくるので有効化するとGitLabを使用することができるようになります。

ログイン後

ログイン後にこんな感じの画面と、プロジェクトの一覧(現在は空)が表示されます。

プロジェクトの作成

先ほどの画面のNew Projectという緑色のボタンを押すと新たにプロジェクトを作成することができます。

プロジェクトの作成

こんな感じの画面が表示されます。今回はテストなのでgit_testという感じのプロジェクト名にしています。

Visibility Levelの部分をPrivateに設定するとほかの人からは参照できないプロジェクトにすることができます。

最後のInitialize repository with a READMEにチェックを入れるとREADME.mdを自動的に概要などから作成してくれます。プルできているかの確認用に使用したいのでチェックを入れるようにしてください。

Create Projectを押すとプロジェクトが作成されます。

作成後

画面のほかの部分を見てみるといろんなボタンがあっていろいろできそうですがひとまずこれで完了です。

Windows上でgitコマンドを使えるようにする

次にWindows上でgitコマンドを使用することができるようにします。以下のサイトからexeファイルを取得してインストールします。

https://git-scm.com/download/win

アクセスと同時にダウンロードが始まると思いますが始まらない場合はclick here to download manuallyをクリック。

既に自分の環境にインストールしているのでスクショが取れないのですがこの記事の流れが参考になります。

基本的にはデフォルトでOKです。ちなみに私はコンソールはMinTTYのほうを選択しています。




インストールが完了してPCを再起動させるとgitコマンドが使用できるようになります。Windowsキー + Rを押してcmdと入力してコマンドプロンプトを起動させて以下のコマンドを実行します。

> git --version
git version 2.18.0.windows.1

バージョンが表示されていれば正常にインストールできています。

もしくはエクスプローラー上で任意の場所(ホームなど)で右クリックメニューからGit Bash Hereを選択すると、現在いるフォルダでコマンドを実行できます。こっちのほうが便利ですね。

クローン

先ほど作成したプロジェクト(git_test)をPC上に持ってくるにはgit cloneというコマンドを使用します。ホームフォルダで今回は作業を行います。先ほどのcmdで起動したコマンドプロンプトの現在フォルダはおそらくホームフォルダになってると思うのでそのまま進めます。

以下のコマンドを実行してクローンを行います。

> git clone [プロジェクトのURL]
Cloning into 'git_test'...
Username for 'https://gitlab.com':
Password for 'https://username@gitlab.com':

remote: Enumerating objects: 3, done.
remote: Counting objects: 100% (3/3), done.
remote: Total 3 (delta 0), reused 0 (delta 0)
Unpacking objects: 100% (3/3), done.

> dir
...
2018/12/18  09:35    <DIR>          git_test
...

> cd git_test
> dir
2018/12/18  09:35    <DIR>          .
2018/12/18  09:35    <DIR>          ..
2018/12/18  09:35                33 README.md

プロジェクトのURLはプロジェクト作成後画面にあるCloneという青いボタンの中のClone with HTTPSのほうのURLを使用します。

Privateなプロジェクトに設定しているのでユーザー名などを聞かれますのでGitLabのアカウント情報を入力します。

完了したらdirコマンドでフォルダが存在しているかを確認します。

コミット

追加したファイルなどをコミットします。コミットすることによってgit上でファイルの状態を確定させ、変更などの管理できるようになります。

コミットするファイルを先ほどのクローンしたプロジェクトに作成します。

ファイルを追加


テスト用にcommit.txtというファイルを追加しました。

コミット前にコミットするファイルを選択するという動作が必要になります。git addというコマンドを使用します。

> git add .

もしくは

> git add commit.txt

上のコマンドでは現在のフォルダ以下のすべての変更ファイルをコミット対象に追加することができます。修正ファイルが膨大な場合に便利です。

下のコマンドでは指定したファイルのみをコミット対象に追加することができます。

コマンドを実行しても特に何も表示されません。。。git statusを実行することで現在addされているファイルを確認することができます。

> git status
On branch master
Your branch is up to date with 'origin/master'.

Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

        new file:   commit.txt




この状態でgit commit -m "コメント"を実行すると変更を確定します。コミット時には必ずコメントをつける必要があります。

> git commit -m "テスト"
[master xxxxxxx] テスト
 1 file changed, 1 insertion(+)
 create mode 100644 commit.txt

おそらく初回コミット実行時にユーザーネームとメールアドレスが設定されていないといった感じのエラーが表示されると思います。その場合は以下のコマンドを実行して設定します。

> git config --global user.name "[ユーザー名]"
> git config --global user.email [メールアドレス]

誰がコミットしたかわからないままだと複数人で開発しているときなんかにトレースできないですからね。。、。orz

これでプッシュを実行する準備ができました!

プッシュ

コミットしただけでは自分のPC上で変更を確定しただけなので、プッシュコマンドを実行してリポジトリ(GitLab)に反映させます。

初回プッシュ時に以下のコマンドで実行しておくと、次回からはgit pushだけでプッシュできるようになります。

> git push -u origin master
Enumerating objects: 4, done.
Counting objects: 100% (4/4), done.
Delta compression using up to 8 threads.
Compressing objects: 100% (2/2), done.
Writing objects: 100% (3/3), 313 bytes | 313.00 KiB/s, done.

...

Branch 'master' set up to track remote branch 'master' from 'origin'.

ここでもGitLabのアカウント情報を求められるはずですので入力してください。

最後まで完了したらGitLabのページを見てみましょう!

確認

プロジェクトのページを確認すると、、、

確認

しっかり新しいファイルが追加されていますね!以上でプッシュまでは完了です!

次の記事

次はRaspberry Pi上でgitコマンドを使えるようにするのと、pullコマンドについてです。

Raspberry Piで水温を取得する。(DS18B20)

IMG_20180505_154239_111

前の記事

カメラモジュールから画像を取得して任意のディレクトリに保存するまでを上記の記事で進めました。

目標

  • 防水の温度計を使用して水温を取得できるようになること。
  • 取得した値を読みやすいようにPythonで整形できるようになること。

必要なもの

Raspberry Pi

もちろんこれが必要。Zero Wを使用。

防水温度センサ (DS18B20)

水温を取得するために必要。

防水で無いもので同名のセンサもあります。(三脚のちっさいやつ)

今回は水槽内にほりこむ必要があるのでこのタイプを使用します。

線の色 役割
VCC
黄/白 データ
GND

のようです。

ほかの各センサをオンラインで購入したい場合や、防水ではなくより小型のDS1820を購入するには、アールエスコンポーネンツのECサイトが便利です。検索時にIFが1WireI2C、実装方法に表面実装またはTH実装等、自分の開発環境に合わせたセンサを絞り込むことが簡単にできるので使いやすいです。

アールエスコンポーネンツはRaspberry Piの日本正規総代理店でもありますのでRaspberry Piを使っている人にはおなじみの企業で安心ですね。

ほか電子工作部品

  • 半田こて一式
  • ブレッドボード
  • 抵抗1つ (4.7kΩ)
  • リード線

を使用します。

DS18B20のデータシート上では4.7kΩの抵抗器が必要なようですが、手元になかったので5.1kΩ で作成しました。値はちゃんと取得できています。

こんな感じのセットになっている商品がお手軽に部品を集めることができます。

LEDとかボタンもついているのでLチカとかも試せますね。

作業

温度センサの下準備

温度センサから出ている3本の線の皮膜を剥がし、中の銅線を露出させます。

皮膜を剥がすようの専用のニッパーのようなものもあるのですが、ハサミや普通のニッパーで丁寧にグルリと切り込みを入れてやれば、爪で千切ることもできます。

あまり力を入れすぎると中の銅線も一緒にちぎってしまいます。私は黒線でコレをやってしまいました(笑)

中には極細の銅線が数十本あります。ここにジャンパワイヤをはんだ付けしてブレッドボードに差し込めるようにします。そのままでは柔らかすぎて刺さりませんでした。

IMG_20180506_194540

はんだ付けした部分にはビニールテープなんかで巻いておきましょう。

1-Wireを有効にする

DS18B20を使用するには、1-Wireという規格のインターフェースを有効にする必要があります。

sudo raspi-configを実行して、Interfacing Options > 1-Wire > Yesで有効にできます。

実際には/boot/config.txtに以下の一行が追加されます。

dtoverlay=w1-gpio

作業を進めていて気づいたのですが、さらにもうちょっと値を追加する必要があります。

sudo vim /boot/config.txtで開いて以下のようにしてください。

dtoverlay=w1-gpio,pullup=on

pullup=onによってプルアップ電圧を設定するというカンジでしょうか...

プルアップ電圧とは下記参考によると、特殊な処理を行うときに通常の電力よりも多くの電力を使用する、そんなときに必要となる電圧のようです。
(from: https://www.maximintegrated.com/jp/app-notes/index.mvp/id/4255)

書き込み終わったらRaspiを再起動させましょう。

配線

water temp breadboard

IMG_20180506_194658

Fritzingというサービスを利用して回路を書いてみました。防水用のDS18B20のイメージがあれば良かったのですがありませんでした。

配線の色でそれぞれの線をどこに挿せばいいか確認してください。デフォルトではデータはGPIO4番のピンから受け取ることができるようです。

抵抗に関して、公式データシートや他の方の記事でも4.7kΩのものが使用されていましたが、手元になく5.1kΩのものを使用しました。

特に問題なく計れているようですが、もしかすると数値に若干狂いがあるかもしれません。

値を取得

それでは値を取得していきます。温度センサー本体にLEDとかついていなのでちゃんと動いてるかどうか心配ですね...(笑)

ちゃんと回路ができていて、値を取得できる状態にある場合、 以下の場所に28-で始まるディレクトリが現れます。

表示されない場合は、config.txtの内容と配線が正しいかどうか確認してみてください。

$ cd /sys/bus/w1/devices/
$ ls -la
total 0
drwxr-xr-x 2 root root 0 May  5 02:09 .
drwxr-xr-x 4 root root 0 May  5 02:09 ..
lrwxrwxrwx 1 root root 0 May  5 02:10 28-0517c41aecff -> ../../../devices/w1_bus_master1/28-0517c41aecff
lrwxrwxrwx 1 root root 0 May  5 02:09 w1_bus_master1 -> ../../../devices/w1_bus_master1

このディレクトリ名はセンサーに個別に振られている値のようで、再起動後や回路の再構築後にも同じディレクトリ名が現れます。

また複数個の同じセンサーをつなげても、このディレクトリ名で判別可能となるようです。

pythonなどの処理でディレクトリを決め打ちできるのでありがたい!

このディレクトリ内にあるw1_slaveというファイルにセンサーの取得した温度が格納されています。

$ cd 28-0517c41aecff
$ cat w1_slave
50 05 4b 46 7f ff 0c 10 1c : crc=1c YES
50 05 4b 46 7f ff 0c 10 1c t=85000

t=85000の値を1/1000したものが温度(℃)となります。

t=0となっている場合、温度センサーを指なんかで温めると反応し始める事があります。それでもダメな場合は回路を見直してみてください。

特に黄色のデータ線が正しく配線できているか確認してください。

t=85000?

。。。しかし85℃はおかしい(笑)指で温めても常に85℃となります(´・ω・`)

ダメ元で調べてみると、、ありました!!

要約すると、config.txtにpullup=onを追加すると大丈夫なようです!上の方にすでに追加したあの値ですね。

他にもRaspi側の電源供給のPINを変更したら治っただとかいろいろな情報が出てきました。詰まったときは検索すると意外とどうにでもなるもんです。

修正後の値取得

再度値を取得してみると...

$ cat w1_slave 
9e 01 4b 46 7f ff 0c 10 8a : crc=8a YES
9e 01 4b 46 7f ff 0c 10 8a t=25875

ちゃんとそれっぽい温度(25.8℃)で取得できました!ちなみにこの値はすでに水槽の中にほりこんだ時の値です。

アナログな水温計は25℃付近を指していたので、よりヒーターの近い方に投げ込んだので概ね正しい値が取得できていそうです。

コリドラスたちが興味津々に近づいてきたので水面からちょっと浮かせて設置したほうが良さそうですね。あまりもふもふされると体に悪そう。。orz

この形...もしかして

温度センサーでこの形と来たらまさに体温計ですね(笑)

IMG_20180505_154239_111

興味がてら体温も測ってみました(脇下で2分)

$ cat w1_slave
34 02 4b 46 7f ff 0c 10 5b : crc=5b YES
34 02 4b 46 7f ff 0c 10 5b t=35250

うーん、、どう頑張っても36℃を超えることはありませんでした(笑)

値をPythonで取得

Linux上のコマンドで値を取得することはできたので、今度はプログラムの中でも取得できるようにしましょう。

今回のスマート水槽構築に際しては、Pythonを使うという縛りで進めているのでいつものPHPは無しです。 DBに値を突っ込んでしまえば言語は関係ないですからね(゚∀゚)

こんな感じでコード書いてみました。ファイル名はget_water_temp.pyとしました。

import sys
import subprocess

### 定数
SENSOR_ID = "センサーのディレクトリ名(28-で始まるやつ)"
SENSOR_W1_SLAVE = "/sys/bus/w1/devices/" + SENSOR_ID + "/w1_slave"
ERR_VAL = 85000

def main():
  res = get_water_temp()
  if res is not None:
    temp_val = res.split("=")
    if temp_val[-1] == ERR_VAL:
      print "Got value:85000. Circuit is ok, but something wrong happens..."
      sys.exit(1)

    temp_val = round(float(temp_val[-1]) / 1000, 1)
    print temp_val
  else:
    print "cannot read the value."
    sys.exit(1)

def get_water_temp():
  try:
    res = subprocess.check_output(["cat", SENSOR_W1_SLAVE])
    return res
  except:
    return None

if __name__ == "__main__":
  main()

エラー処理等かなり適当です。いい感じにブラッシュアップしてあげてください。

cat w1_slaveで取得した生データを=で分割・配列に格納します。生データ内には=は一つだけある(t=85000)の部分だけなので、 いい感じに温度の値の部分だけを配列に格納できます。

pythonでは配列に格納されている値を後ろから取得することができます。val[-1]と負数を指定すると後ろから取得できます。 -1は最後尾に格納されている値を取得できます。

  • cat w1_slaveで生の値を取得できない時
    (ディレクトリが存在しない(=回路が以上でセンサーを認識できない)時)
  • 取得できる値が85000の時

にエラーを出力します。

0が取得できるときはエラーとはしていません。水温が0度になる可能性もなきにしもあらずなのですが、 いずれDB処理を追加するときに上記のエラーと合わせて有効フラグをfalseにする的な処理にします。

これでpython get_water_temp.pyをすると、小数部1桁(2桁目をround)した摂氏温度を取得できます。

$ python get_water_temp.py
23.3

以上で温度計が使えるようになりましたね!

最後に

プログラム内で値を取得することができるようになれば、データベースに格納してグラフ等を作成したり、 温度がある一定ラインを下回ったときに通知を飛ばす事ができるようになります。

この情報を使って水冷用のファンを回すとかやってみようと思います。

次の記事

参考

https://deviceplus.jp/hobby/raspberrypi_entry_018/

https://qiita.com/n0bisuke/items/ebcf5782638afcefd4f1