はじめに
こんにちはー!なたでです!
セカンドライフ技術系 Advent Calendar 2018の10日目の記事です。
今日は趣向を変えて、IoTにチャレンジします。具体的には、セカンドライフの中にインターホンのようなものを設置して、押したら自分の部屋で音が出るというものを作ってみたいと思います。
なお、今回いろいろ用意するものや前提知識が多く正直全てが書ききれておりません。また記事を補完する予定ですが、詳しく聞きたい場合はブログのコメントかTwitterかセカンドライフでお気軽に質問してください。
基本設計
IoTのイメージ
イメージとしては、以下のような感じです。
実現するためにはどうしたらよいでしょうか。今回は、以下のような作りで作りたいと思います。
セカンドライフ中で実行しているLSLのMono仮想マシン上で実行されているプログラムから、家の中にサーバーへアクセスして、これをトリガーに音を出そうと思います。
設計思想について
以下、上記のような構成にした理由や、細かな部分や用語について解説します。
LSLでサーバーへアクセスする方法
現状、セカンドライフから自宅のサーバーへアクセスする方法は2つあります。
- llHTTPRequest 外部のWebサーバーへアクセスできる
- llEmail 外部のメールサーバーへアクセスできる
今回は、llHTTPRequestを使用したいと思います。というのもllEmailの場合はメールサーバーを建てる必要が出てくるのですが、超面倒で一度挫折しており、ちょっと今回はやめます。メールサーバーをどうしても建てたい方は、「実践! CentOS 7 サーバー徹底構築」を読んでみましょう。(多分挫折します)
サーバーとは?
話が前後しますが、サーバーとは何か少し解説します。サーバーというのは、サーバーとクライアントの2つでセットの用語でして、一般的にサーバーが仕事をとりまとめていて、クライアントがそのサーバーに仕事をお願いしたり、結果を受け取ったりするそのような感じのものです。
サーバーとして動作させるためには、サーバー用としてのアプリケーションを常時実行する必要があります。有名なものに、Webサーバーとかメールサーバーとかあります。このブログも、Webサーバーが色々お仕事して、データを自分のPCにHTMLとして送り返しています。今回、2つのラズパイをWebサーバーとして動作させます。
Raspberry Pi(ラズパイ)とは?
Raspberry Pi は小型のパソコンです。WindowsやMacが動くようなCPUを使用したパソコンではなく、Androidスマホで利用しているARMと呼ばれるCPUを使った小さなパソコンです。ラズパイは、サーバー用途として利用したり、ピンから電気信号を操作したりして、色々なことができます。省エネで電気代があまりかからないため、常時起動が向いており、その辺に転がしておくだけで自宅サーバーを作れます。
今回は、私が昔購入して使っていなかった「Raspberry Pi B+」と「Raspberry Pi 2」を使用します。現在は「Raspberry Pi 3」など、高速な機種も出ています。古い機種のほうが消費電流が少ないメリットもありますが、あえて古いのを買おうとすると高くつくため、特に理由がない限り購入するのであれば「Raspberry Pi 3」がよいかと思います。
なぜラズパイを2つ設置する必要があるのか?
ラズパイが1つの構成でも、作ろうと思えば作れますが、今回は2つラズパイを利用することとしました。
スピーカーをつけている「Raspberry Pi B+」は家の中で持ち運びすることを考えています。インターネット固定回線から無線LANで飛ばしたいと考えていますが、電波が不安定になるため、これを外部に直接見せる常時起動用サーバーには向きません。そこで外部からデータを受け取る用のサーバーは有線回線で接続した「Raspberry Pi 2」がうけとり、ここを経由して無線LANで接続した「Raspberry Pi B+」にデータを渡そうと考えましたためです。
以下、メリットとデメリットを記載してみました。
常時起動用サーバーを作るメリット
- 必ず1つ常時起動用のサーバーを用意することで、ここではIoTとは関係ない定期実行処理(DDNSの更新処理等)や、常時起動させたい別の仕事を行わせられる。
- 常時起動用サーバーへポートを開いておけば、あとはスピーカー用ラズパイ、その他専用ラズパイに対して、処理を送るなど分担できる。
- スピーカー用ラズパイなどの電源が切れている場合でも、常時起動用サーバーが切れている状態を把握して、何かしら応答を返すような処理も作れる。
常時起動用サーバーを作るデメリット
- 常時起動させて行うような処理をするのでなければ、1つのみのほうがシンプル設計になる。
- ラズパイと電源をその分用意するため、お金がかかる。
下準備
用意するもの
基板や配線とかまとめるケース
今回、常時稼働ということと、色々なごちゃごちゃしているものをまとめる意味でケースを購入しました。難燃性なので、もし何かあっても安心です。
USB電源タップ
ラズパイに安定して与える電源です。将来他の機種を追加したり拡張することを見越して、USB電源の端子が多いものを選びました。電源も安すぎると燃えたりしそうで怖いため、日本の会社が販売しているものを選びました。
今回2Aのを選んでいます。ラズパイB+であれば問題ありませんが、ラズパイ3であれば、他のものも付けるかもなので、もうすこしアンペアがでるやつを探すといいかもしれません。
Raspberry Pi
ケースに入れる用のラズパイ1Bです。今回は不要ですが、今後拡張ができるようにT型拡張ボードもつけました。ラズパイ1Bには無線LANが内蔵していないので、USBドングルで接続しています。ラズパイ3であれば不要です。なおラズパイはmicroSDカードが必要です。microSDカードは、私は普通の民間用のを使用していますが、ドライブレコーダー用の方が少し高いですが耐久性があり安心です。また、セットアップ時にパソコンのディスプレイに接続しキーボード等で設定する必要があり、HDMIケーブル、USBキーボード、USBマウスが必要です。
スピーカー
音を出すためにラズパイの音声出力端子につけるため、DAISOのUSBミニスピーカーを購入しました。300円と安いですが非常に良い音がでます。USBケーブルをUSB電源タップに差し込み、ステレオミニプラグはラズパイの音声出力端子につけます。
ちなみにですが、6Wで5V使うということは、最大1.2A使うということになります。まあ、そんなに音量出さないので、あんまり使わないでしょう。ちなみに「自作☆改造☆修理の館(新館) – [高音質]ダイソーUSBミニスピーカー」を見ると待機電流は、0.04Aだそうです。
接続
後述のセットアップが終わったら、「Raspberry Pi B+」は、スピーカーと無線LANドングルを接続して、ケースの中に入れます。「RaspberryPi 2」は、家のネットワークの大元の機器に接続します。
サーバーのセットアップ
今回、2つのラズパイのセットアップが必要です。
- Raspberry Pi 2(有線接続)
- Raspberry Pi B+(WiFi+スピーカー付き)
今後、Raspberry Pi 2をサーバーA、Raspberry Pi B+をサーバーBと呼びたいと思います。
サーバーAとサーバーBともにOSはLinuxを選択します。なぜLinuxにするかというと、以下の理由があります。
- 無料で利用できるのでお得
- 世界中でLinuxを使用したサーバーが利用されているため安心感がある
- Windowsのように画面を使って何かアプリを操作したり実行することはないので、CUIがメイン操作のLinuxでも問題ない
ところで、RaspberryPi でLinuxを動かす場合に以下のようなLinuxディストリビューションを選べます。
Raspberry Pi ではRaspbianが主流になっています。Raspbianとは、Debian GNU/Linux をRaspberry Pi 向けにカスタマイズしたものであり、非常に使いやすいです。そのため何か理由がなければRaspbianを選択しましょう。とくにスピーカーを付ける方(サーバーBの方)、あるいは今後GPIOを操作する場合がある方は、必ずRaspbianをインストールしてください。
私は練習として、サーバーAにはCentOS 7をインストール。サーバーBにはRaspbianをインストールしました。
インストール方法については、以下を参照してください。
Raspbianを使用する場合は、上記の1.と2.を参照。CentOS を使用する場合は、上記の3.を参照。いくつか飛ばしていい章もありますが、両サーバーともに Node.js / npm までインストールしてください。
以下からは、セットアップが完了したサーバー上でNode.jsが実行できるようになっている状態で解説します。
サーバー側のプログラム
サーバーAとサーバーBともに、セットアップでインストールしたNode.jsを使用してサーバーを作成します。
プログラムの書き方
Node.jsとは
Node.jsは、一言でいうとJavaScriptをブラウザなしで単体で動かせる実行環境です。JavaScriptは、どのブラウザでも動作するという特徴があり、パソコンやらスマホやらあらゆるデバイスのブラウザで動作します。そのため、JavaScriptさえ覚えれば大体色々できてしまうので習得するならオススメの言語です。
今回は不要かもしれませんが、もしWindows環境でNode.jsのデバッグ環境をそろえたい場合は「JavaScript開発環境をNetBeansからVisual Studio Codeに切り替えた話」が参考になります。
Webサーバーを書くためには
Node.jsでWebサーバーを作る方法として、以下の2通りあります。
-
Node.js のコアモジュール http を使用する
- Node.js で動作するWebアプリケーションフレームワーク Express.js を使用する
今回やりたいことは非常にシンプルであるため、前者のhttpモジュールで作りたいと思います。例えば、POSTデータを受け取りたかったり、アクセス制限を制御したり、一般的なWebサーバーで利用されているような機能を使用したい場合は、後者のExpress.jsを使用するとよいでしょう。
データの受け取り方について
今回は、GETを使用して、URL上に含まれるデータを解析し音を鳴らしたいと思います。
例として、hoge.com
というアドレスを自宅サーバーに向けて、1234
が開いているウェブサーバーのポート番号としたときに、外部から以下のアドレスでアクセスしたとします。
このとき、pass=4567
などのパラメータなどがGETによって使用できる情報となります。セキュリティ的には色々と問題があるかと思いますが、今回はシンプルにしたいため、この方法でデータを取得したいと思います。
音の再生方法について
Raspbianをインストールしたラズパイのアナログ端子から音を出すことで実現させます。wavファイルであればaplay
。mp3ファイルであれば、mpg321
を使用して再生できます。
準備として、Raspbian上で以下のコマンドをうってください。右側の「1」がイヤホン端子で出力ということになります。
amixer cset numid=3 1
一度以下のコマンドで音が出るか確かめましょう。440Hzのサイン波が出ます。
speaker-test -t sine -f 440
wavファイルを再生する場合は、以下のコマンドで再生します。
aplay ./hoge.wav
mp3ファイルなら以下のコマンドでmpg321
をインストールして、再生できます。
sudo apt-get install mpg321 mpg321 ./hoge.mp3
常時稼働させる方法について
それぞれのサーバーで動かすには、node ./****Server.js
のようにプログラムをNode.jsで実行させるだけでよいのですが、常時実行が望ましいです。ここでは説明を省きますが、ラズパイを起動させたら上記のコマンドをバックグラウンドなどで自動実行させるようにしておくとよいでしょう。
ポート開放について
これも説明を省きますが、外部のインターネットからアクセスしたときに、サーバーAのポート62620へアクセスできるように、予めルーターなどの設定が必要です。ポート開放などでググってみてください。
グローバルIPアドレスについて
グローバルIPアドレスとは、外のインターネットから自分の家のIPアドレスを見たときのアドレスです。通常、グローバルIPアドレスは変動します。そのため、何かしら名前を付けて、そこに自分のIPアドレスを更新するという形をとります。詳細は、「Raspberry Pi はじめました (第5回) – DDNS編」の「外からアクセスできるようにするために、ドメインを作ろう」と「crontabを使ってみよう」の章を確認してください。
プログラム
サーバーB (192.168.1.11, 無線持ち運び可、スピーカー付き、Raspbian)
サーバーAからリクエストを受けたら音を再生するNode.js用スクリプトです。音声ファイルは1度再生すると、5秒間は無効タイムを作りました。音ファイルは./playSound.sh
のシェルスクリプトで再生します。
RoomServer.js
// WEB サーバー用 const exec = require("child_process").exec; const http = require("http"); const url = require("url"); const server = http.createServer(); // jsファイルが置いてある場所 const curdir = __dirname; // 連続再生防止 const soundTimeMS = 5000; let startTime = 0; // URL解析 const analysis = function(name, query) { if(name === "favicon.ico") { return 0; } if(name === "playSound") { const nowTime = new Date().getTime(); if(nowTime - startTime > soundTimeMS) { exec(curdir + "/playSound.sh"); startTime = new Date().getTime(); } } return 0; }; const onRequest = function(req, res) { const url_parse = url.parse(req.url, true); res.writeHead(200, {"Content-Type" : "text/plain"}); const filename = url_parse.path.split(/[\\/?#]/); if(filename.length > 1) { res.write(""+ analysis(filename[1], url_parse.query)); } res.end(); }; server.on("request", onRequest); server.listen(62620);
以下はシェルスクリプトplaySound.sh
の中身です。
playSound.sh
#!/bin/sh # シェルスクリプトがある場所をカレントディレクトリにする cd `dirname $0` # 音を再生する amixer cset numid=3 1 mpg321 ./crrect_answer3.mp3
単純にmp3ファイルを再生しているだけとなっています。音声ファイルは「ゲーム・ボタン音~フリー効果音・無料効果音素材」からお借りしました。
以下のコマンドで、サーバーを実行できます。
node ./RoomServer.js
実行中に、ブラウザで以下のURLを開くと音が出たら成功です。
サーバーA(192.168.1.10, 公開用常時起動、有線接続、CentOS7)
セカンドライフから受け取ったGETデータを調べて内容によってサーバーBにコマンドを実行させます。
MainServer.js
// WEB サーバー用 const exec = require("child_process").exec; const http = require("http"); const url = require("url"); const server = http.createServer(); // URL解析 const analysis = function(name, query) { if(name === "favicon.ico") { return 0; } if(name === "SecondLife") { if(query["type"] === "playSound") { exec("curl http://192.168.1.11:62620/playSound"); } } return 0; }; const onRequest = function(req, res) { const url_parse = url.parse(req.url, true); res.writeHead(200, {"Content-Type" : "text/plain"}); const filename = url_parse.path.split(/[\\/?#]/); if(filename.length > 1) { res.write(""+ analysis(filename[1], url_parse.query)); } res.end(); }; server.on("request", onRequest); server.listen(62620);
なお、CentOSを選択した場合は、内蔵されているファイアウォールでアクセスできません。そのため、以下のようにポートを設定します。
sudo firewall-cmd --permanent --add-port=5050/tcp sudo firewall-cmd --reload
開いているかどうかは、下記で確認できます。
sudo firewall-cmd --list-all-zones --permanent
サーバーを実行します。
node ./MainServer.js
実行中に、ブラウザで以下のURLを開くと音が出たら成功です。
最終確認
一応ここまで準備ができれば、外部のインターネットからアクセスするだけで音が出ます。携帯電話の回線を使ってスマホからテストして、音が出るかどうか確認してみましょう。
セカンドライフ上のオブジェクト
サーバーの準備が整いましたので、あとはLSLでサーバーにアクセスするプログラムを書きます。
string url="http://secondlife.hogehoge.com:62620/SecondLife?type=playSound"; default { state_entry() { } touch_start(integer total_number) { llHTTPRequest(url, [], ""); } http_response( key request_id, integer status, list metadata, string body ) { } }
これを家のドアベルとかに仕込めば完成です。
動作例
おわりに
おつかれさまです。今回のIoTとからめる記事はいかがでしたでしょうか。夢が広がる感じがしますよね!
今回、時間があったようでかなり時間がなく駆け足になってしまいました……。申し訳ございません。もっといろいろ説明するところはあるかもしれませんが、かなり長くなってしまいそうで、はしょっております。また時間があるときにでもこの記事を修正して、より分かりやすくしたいなーと思います。
それではまた!
コメント