tatata’s blog(備忘録)

自分の備忘録として書いています。

ConCacheの利用方法

Elixir + PhoneixからREST APIを呼び出して取得したデータをConCacheを使ってキャッシュする方法を調べたので書いておく。

hexdocs.pm

はじめに

まずは、mix.exsにcon_cacheを追加して、mix deps.getする。

  defp deps do
    [
      {:con_cache, "~> 0.13"}, #この行を追加
    ]
  end

applicaition.exのchildrenに以下を追加

    children = [
      ...
      {ConCache,
        [name: :my_cache, ttl_check_interval: :timer.seconds(1), global_ttl: :timer.seconds(5)]},
     ...
     ]
名称 値サンプル 説明
name :my_cache ConCache管理するキャッシュ名
ttl_check_interval :timer.seconds(1) TTLチェック間隔
global_ttl :timer.seconds(5) キャッシュの有効期間

参照処理へ実装

元コードではREST APIを呼び出しているだけのControllerのコード

  def index(conn, _params) do
    users =
        res = HTTPoison.get!("http://localhost:3000/users")
        res.body
        |> Poison.decode!()

    render(conn, "index.html", users: users)
  end

上のコードを次のように修正。 ConCache.get_or_storeを使うことで、キャッシュにあればキャッシュを使用し、なければREST APIを呼び出している。

ConCache.get_or_storeの第1引数はキャッシュの名前、第2引数はキャッシュに登録するキー名、第3引数はキャッシュにない場合に実行される関数を指定する。

  def index(conn, _params) do
    users =
      ConCache.get_or_store(:my_cache, :my_key, fn ->
        res = HTTPoison.get!("http://localhost:3000/users")
        res.body
        |> Poison.decode!()
      end)

    render(conn, "index.html", users: users)
  end

以上でTTLが有効な間はキャッシュが利用され、REST API呼び出しが無駄に増えることを避けることができる。

更新(新規作成)処理へ実装

このままだと、元データが更新されてもキャッシュが更新されるまでは古い情報が表示されるため、データ更新時にキャッシュも更新する。

createの中でREAT APIの更新処理が成功した場合にConCache.update_existingを使ってキャッシュも更新している。

ConCache.update_existingはキャッシュに指定したキーがなければ追加、キーがあれば更新する。

第1引数と第2引数はConCache.get_or_storeと同じ。

第3引数の関数内でnew_valueに新しいキャッシュの内容を割り当て、{:ok, new_value}を返せば良い。

この例では、キャッシュにあるユーザーの一覧に、新規ユーザーを追加している。

  def create(conn, %{"user" => user_params}) do
    changeset = User.changeset(%User{}, user_params)

    req_body = user_params |> Poison.encode!()

    case HTTPoison.post("http://localhost:3000/users", req_body, [
           {"Content-Type", "application/json"}
         ]) do
      {:ok, %HTTPoison.Response{status_code: 200}} ->
        ConCache.update_existing(:my_cache, :my_key, fn old_value ->
          new_value = old_value ++ [user_params]
          {:ok, new_value}
        end)

        conn
        |> put_flash(:info, "User created!")
        |> redirect(to: Routes.user_path(conn, :index))

      _ ->
        conn
        |> render("new.html", changeset: changeset)
    end

ユーザー更新時はnewに同様の実装をする。

「サイバーセキュリティ レッドチーム実践ガイド」の「Chat Support Systems アプリケーション」仮想マシンをVirtualBoxで使う

サイバーセキュリティ レッドチーム実践ガイド | マイナビブックス - 第3章」で紹介されている「Chat Support Systems アプリケーション」の仮想マシンVMware用であり、そのままではVirtualBoxで使えないため、解決方法を書いておく。

具体的な問題点

VirtualBoxではネットワークインターフェースが認識されないためIPアドレスが割り当てられない。ログインして設定変更すれば良いのだが、パスワードは公開されていない。

解決策

メールすればパスワードを教えてもらえるらしい*1が面倒なので、以下の手順で解決した。

1. VirtulBoxの別の仮想マシンに仮想ディスクをアタッチする

別の仮想マシンのストレージ設定画面より、ディスクの追加アイコンをクリックする。 f:id:ysysy:20190313072908p:plain 「既存のディスクを選択」をクリックする。 f:id:ysysy:20190313072855p:plain 「Chat Support Systems アプリケーション」の仮想ディスクを選択する。 f:id:ysysy:20190313072829p:plain

2. 別の仮想マシンを起動して、アタッチした仮想ディスクをマウントする

アタッチされたディスクを確認する。/dev/sdbにアタッチされたことがわかる。

# dmesg | grep disk
[    3.067551] sd 2:0:0:0: [sdb] Attached SCSI disk
[    3.119617] sd 1:0:0:0: [sda] Attached SCSI disk

仮想ディスクのパーティションを確認する。/dev/sdb5がルートパーテションであると思われる。

# fdisk -l /dev/sdb
Disk /dev/sdb: 20 GiB, 21474836480 bytes, 41943040 sectors
Disk model: VBOX HARDDISK
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: dos
Disk identifier: 0x5fdb1238

Device     Boot   Start      End  Sectors  Size Id Type
/dev/sdb1  *       2048   999423   997376  487M 83 Linux
/dev/sdb2       1001470 41940991 40939522 19.5G  5 Extended
/dev/sdb5       1001472 41940991 40939520 19.5G 8e Linux LVM

ファイルマネージャー(この例ではFilesを使用)を起動してマウントする。 f:id:ysysy:20190313073356p:plain /media/root/f305128a-5d16-4dd0-a8be-4f83f63f622fにマウントされたことがわかる。

# df
Filesystem                        1K-blocks     Used Available Use% Mounted on
udev                                1000480        0   1000480   0% /dev
tmpfs                                204332     7012    197320   4% /run
/dev/sda1                          38958432 22889684  14060072  62% /
tmpfs                               1021644        8   1021636   1% /dev/shm
tmpfs                                  5120        0      5120   0% /run/lock
tmpfs                               1021644        0   1021644   0% /sys/fs/cgroup
tmpfs                                204328       16    204312   1% /run/user/130
tmpfs                                204328       36    204292   1% /run/user/0
/dev/mapper/LethalNodeJS--vg-root  18982780  2118044  15877396  12% /media/root/f305128a-5d16-4dd0-a8be-4f83f63f622f
3. ネットワーク設定を変更する

ネットワークの設定ファイルを確認するとens33になっているので、enp0s3に書き換える。

# vi /media/root/f305128a-5d16-4dd0-a8be-4f83f63f622f/etc/network/interfaces

変更前

# The primary network interface
auto ens33
iface ens33 inet dhcp

変更後

# The primary network interface
auto enp0s3
iface enp0s3 inet dhcp

OSを再起動するとIPアドレスが割り当てられたことがわかる。 f:id:ysysy:20190313223817p:plain

Amazon Echoで照明をオン・オフする 4(Alexa編)

今回は、AlexaスキルからLabmda経由で前回作成したSQSにメッセージを送って、照明をオン・オフする処理を作成する。

Lambda関数作成

Alexaスキルからの命令を受け取って、SQSにメッセージを投げるLambda関数を作成する。

f:id:ysysy:20180506190645p:plain

alexa-sdkをインストール

$ npm install --save alexa-sdk

次の2つのファイルを作成

index.js

const AWS = require('aws-sdk');
const Alexa = require('alexa-sdk');
let sqs = new AWS.SQS({region:'ap-northeast-1'});
let url = process.env.SQS_QUEUE_URL;
var state = '-1';
AWS.config.update({accessKeyId: 'KEY', secretAccessKey: 'SECRET'});

function changeState(state) {
        var params = {
        MessageBody: state,
        QueueUrl: url,
        DelaySeconds: 0,
        MessageAttributes: {
            LightStatus: {
              DataType: 'Number',
              StringValue: state
            },
        },
    };
    sqs.sendMessage(params, function(err, data) {
        if (err) console.log(err, err.stack);
        else     console.log(data);
    });
};

const handlers = {
    'turnOn' : function() {
        changeState('1');
        this.emit(':responseReady');
    },
    'turnOff' : function() {
        changeState('0');
        this.emit(':responseReady');
    }
};

exports.handler = function(event, context, callback) {
    const alexa = Alexa.handler(event, context, callback);
    alexa.appId = process.env.APP_ID // APP_ID is your skill id which can be found in the Amazon developer console where you create the skill.
    alexa.registerHandlers(handlers);
    alexa.execute();
};

package.json

{
  "name": "light",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "ISC",
  "dependencies": {
    "alexa-sdk": "^1.0.25"
  }
}

zipファイルにしてアップロードする

$ zip -r light.zip *

f:id:ysysy:20180506190651p:plain

環境変数の設定

APP_IDには後ほど作成するAlexaスキルのIDを、SQS_QUEUE_URLには前回作成したSQSキューのURLを設定する。

f:id:ysysy:20180506190654p:plain

Alexaスキル作成

Alexaコンソールにログインしてスキルを作成する。 ログインに使用するアカウントは、Amazon Echoを買ったamazon.co.jpのアカウントを使う。*1

developer.amazon.com

呼び出し名

「スキルの呼び出し名」にAlexaで呼び出すときの名前を設定する。ここでは、「ほげほげまん」とした。

f:id:ysysy:20180506185808p:plain

インテント

インテントには照明をオンにするときのフレーズを設定する。 この例では、「Alexa、ほげほげまんであかるくして」、「Alexa、ほげほげまんでつけて」と言うと照明がオンになる。

f:id:ysysy:20180506185812p:plain

同様に照明をオフにするときのフレーズを設定する。

f:id:ysysy:20180506185816p:plain

エンドポイント

ここに表示されている「スキルID」をLamda関数の環境変数APP_IDに設定する。 「デフォルトの地域」にLambda関数のARNを設定する f:id:ysysy:20180506185820p:plain

動作確認

照明オン・オフをAmazon Echoからできことを確認

照明オン

  • 「Alexa、ほげほげまんであかるくして」
  • 「Alexa、ほげほげまんでつけて」

照明オフ

  • 「Alexa、ほげほげまんでくらくして」
  • 「Alexa、ほげほげまんできって」
  • 「Alexa、ほげほげまんでけして」

Lambda関数で使ったnode.jsは詳しくないのでもっと良い方法があると思う。スキルの設定ももっとスマートな方法があるはず。でもこれで動くことは動く。

作成して時間が経っているので、記憶があやふやになり、あまり詳しく書けなかったのが反省。

*1:amazon.comのアカウントでログインしたら、Echoと関連づけられていなかった。

Amazon Echoで照明をオン・オフする 3(SQS編)

今回は、前回作成したプログラムを改良して、SQSからRaspberry Piにメッセージを取得して、照明をオン・オフする処理を作る。

SQSキュー作成

メッセージを入れるSQSのキューをAWSコンソールから作成

プログラム(turnOnOff.js)

SQSからメッセージを取得して、シリアルポート経由でArduinoに命令を送る常駐プログラムを作成

var AWS = require('aws-sdk');
var async = require('async');
var SerialPort = require('serialport');

// Raspberry Piのシリアルポートを指定
var port = new SerialPort('/dev/ttyACM0');

// 環境変数からAWSのアクセスキーを取得
let KEY = process.env.AWS_ACCESS_KEY;
// 環境変数からAWSのシークレットキーを取得
let SECRET = process.env.AWS_SECRET_KEY;
// 環境変数からSQSのキューURLを取得
let URL = process.env.SQS_QUEUE_URL;
// 認証情報をセット
AWS.config.update({accessKeyId: KEY, secretAccessKey: SECRET});

// SQSのリージョンを指定
var sqs = new AWS.SQS({region:'ap-northeast-1'});

// SQSの情報をセット
var params = {
  QueueUrl: URL,
  AttributeNames: [
    'ApproximateFirstReceiveTimestamp'
  ],
  MaxNumberOfMessages: 1,
  MessageAttributeNames: [
    'LightStatus' //メッセージの属性LightStatusの値で判断するため設定。任意の名前でOK
  ]
};

// 引数で渡ってきた値をシリアルポートに書く
function tunOnOff(state) {
  setTimeout(function() {
    port.write(state + "\n", function(err) {
      if (err) {
        return console.log('Error on write: ', err.message);
      }
      console.log('message written');
    });
  }, 2000);
};

// エラーが発生した場合はコンソールに出力
port.on('error', function(err) {
  console.log('Error: ', err.message);
});

// 処理開始
console.log('start');
setInterval(function() {
  sqs.receiveMessage(params, function(err, data) {
    if (err) {
      console.log(err, err.stack); // an error occurred
      console.log('error dayo');
    } else if (Array.isArray(data['Messages']) && data['Messages'].length > 0) {
      // SQSのメッセージの属性LightStatusが0の場合、シリアルポートに0を書き出す
      if (data['Messages'][0]['MessageAttributes']['LightStatus']['StringValue'] == '0') {
        tunOnOff('0');
      // SQSのメッセージの属性LightStatusが1の場合、シリアルポートに1を書き出す
      } else if (data['Messages'][0]['MessageAttributes']['LightStatus']['StringValue'] == '1') {
        tunOnOff('1');
      }

      // 取得したメッセージをキューから削除
      var deleteParams = {
        QueueUrl: URL,
        ReceiptHandle: data.Messages[0].ReceiptHandle
      };
      sqs.deleteMessage(deleteParams, function(err, data) {
        if (err) {
          console.log("Delete Error", err);
        };
      });
    }, 20000 );

環境変数設定

.bashrcに次の3行を追加

export AWS_ACCESS_KEY="<AWSのアクセスキー>"
export AWS_SECRET_KEY="<AWSのシークレットキー>"
export SQS_QUEUE_URL="<SQSのキューURL>"

動作確認

プログラム実行

Raspberry Piのコンソールよりプログラムを実行

$ node turnOnOff.js

SQSのキューにメッセージ送信

SQSのコンソールからメッセージ属性LightStatusを追加してメッセージを送信 f:id:ysysy:20180502162940p:plain

属性LightStatusの値が1だと照明がオンになり、0だと照明がオフになる。

node.jsは詳しくないので、ネットに転がっているコードを真似して作成したが、node.jsの非同期の考え方がいまいち理解できておらず、今回もコードがイケてない。コードを綺麗にしたいけど、時間がないのでこのまま。。。node-redでやればよかったかな。

次回はAlexaからSQSにメッセージを送信して、照明をオン・オフする。

Amazon Echoで照明をオン・オフする 2(Raspberry Pi編)

今回は、Raspberry Pi前回作成したArduinoに接続して、Raspbery PiからArduinoに照明のオン・オフを命令する処理を作る。

事前準備

  1. Raspberry PiArduinoをUSBで接続
  2. node.jsをインストール
  3. Node Serialportをインストール *1

www.npmjs.com

スクリプト作成

オンとオフで違うのはport.writeの引数(1または0)のみ

  • on.js (照明オン)
var SerialPort = require('serialport');
var port = new SerialPort('/dev/ttyACM0');

setTimeout(function() {
  port.write("1\n", function(err) {
    if (err) {
      return console.log('Error on write: ', err.message);
    }
    console.log('message written');
  });
}, 2000);


port.on('error', function(err) {
  console.log('Error: ', err.message);
})
  • off.js (照明オフ)
var SerialPort = require('serialport');
var port = new SerialPort('/dev/ttyACM0');

setTimeout(function() {
  port.write("0\n", function(err) {
    if (err) {
      return console.log('Error on write: ', err.message);
    }
    console.log('message written');
  });
}, 2000);


port.on('error', function(err) {
  console.log('Error: ', err.message);
})

動作確認

コマンドを実行して、照明をオン・オフできることを確認

# 照明オン
$ node on.js

# 照明オフ
$ node off.js

コードがイケてないけど、Raspberry Piから操作できるようになったのでよしとする。 次回は、SQSからメッセージを取得して、照明をオン・オフ。

*1:インストールにはまったけど、メモを取ってなかった

Amazon Echoで照明をオン・オフする 1(Arduino編)

Philips HueのようにWorks with Alexa認定商品だとAmazon Echoから照明のオン・オフができるけど、自宅の照明は対応していないので自分で作ってみた。

こんな感じで、Amazon EchoAWSRaspberry PiArduinoを組み合わせた。 f:id:ysysy:20180422142955p:plain

この記事ではまずArduinoから照明をオン・オフする機能について書く。

リモコンの信号解析

こちらの記事を参考にリモコン(Panasonic HK9327K)のボタンを押した時にどのような信号が出力されるか解析した。 deviceplus.jp

接続 f:id:ysysy:20180422143424p:plain

あまり覚えていないけど、Arduino-IRremoteのexampleにあるIRrecvDumpかIRrecvDumpV2をAruduinoに書き込んで、受光部に向けてリモコンのボタンを押すとシリアルモニタに信号が表示される。*1

github.com

# 調査結果
## 全灯ボタン
Encoding  : UNKNOWN
Code      : CA21A230 (32 bits)
Timing[83]:
     +3500, -1700     + 450, - 400     + 450, - 400     + 450, -1300
     + 450, -1250     + 450, - 400     + 450, -1300     + 450, - 400
     + 450, - 400     + 450, - 400     + 450, -1300     + 450, - 400
     + 450, - 400     + 450, -1300     + 450, - 400     + 450, -1250
     + 450, - 400     + 450, -1300     + 450, - 400     + 450, - 400
     + 450, -1300     + 450, - 400     + 450, - 400     + 450, - 400
     + 450, - 400     + 450, - 400     + 450, - 400     + 450, -1300
     + 450, -1300     + 450, - 400     + 450, -1250     + 450, - 400
     + 450, - 450     + 400, -1300     + 450, - 400     + 450, -1300
     + 450, - 400     + 450, - 400     + 450, -1300     + 450, - 400
     + 450, - 400     + 450
unsigned int  rawData[83] = {3500,1700, 450,400, 450,400, 450,1300, 450,1250, 450,400, 450,1300, 450,400, 450,400, 450,400, 450,1300, 450,400, 450,400, 450,1300, 450,400, 450,1250, 450,400, 450,1300, 450,400, 450,400, 450,1300, 450,400, 450,400, 450,400, 450,400, 450,400, 450,400, 450,1300, 450,1300, 450,400, 450,1250, 450,400, 450,450, 400,1300, 450,400, 450,1300, 450,400, 450,400, 450,1300, 450,400, 450,400, 450}; 

## お好みボタン
Encoding  : UNKNOWN
Code      : F6B92168 (32 bits)
Timing[83]:
     +3500, -1700     + 450, - 400     + 450, - 450     + 450, -1250
     + 450, -1300     + 450, - 400     + 450, -1250     + 450, - 450
     + 400, - 450     + 450, - 400     + 450, -1250     + 450, - 400
     + 450, - 450     + 400, -1300     + 450, - 400     + 450, -1300
     + 450, - 400     + 450, -1250     + 450, - 450     + 450, - 400
     + 450, -1250     + 450, - 400     + 450, - 400     + 450, - 450
     + 450, - 400     + 450, -1250     + 450, - 400     + 450, -1300
     + 450, -1300     + 400, - 450     + 450, -1250     + 450, - 400
     + 450, - 400     + 450, - 450     + 450, - 400     + 450, -1250
     + 450, - 400     + 450, - 400     + 450, -1300     + 450, - 400
     + 450, - 400     + 450
unsigned int  rawData[83] = {3500,1700, 450,400, 450,450, 450,1250, 450,1300, 450,400, 450,1250, 450,450, 400,450, 450,400, 450,1250, 450,400, 450,450, 400,1300, 450,400, 450,1300, 450,400, 450,1250, 450,450, 450,400, 450,1250, 450,400, 450,400, 450,450, 450,400, 450,1250, 450,400, 450,1300, 450,1300, 400,450, 450,1250, 450,400, 450,400, 450,450, 450,400, 450,1250, 450,400, 450,400, 450,1300, 450,400, 450,400, 450};

## 消灯ボタン
Timing[83]:
     +3500, -1700     + 450, - 400     + 450, - 400     + 450, -1300
     + 450, -1250     + 450, - 400     + 450, -1300     + 450, - 400
     + 450, - 400     + 450, - 400     + 450, -1300     + 450, - 400
     + 450, - 400     + 450, -1300     + 450, - 400     + 450, -1250
     + 450, - 450     + 450, -1250     + 450, - 400     + 450, - 400
     + 450, -1300     + 450, - 400     + 450, - 400     + 450, - 400
     + 450, - 400     + 450, -1300     + 450, -1300     + 400, -1300
     + 450, -1300     + 450, - 400     + 450, -1250     + 450, - 400
     + 450, - 450     + 450, - 400     + 450, -1250     + 450, -1300
     + 450, - 400     + 450, - 400     + 450, -1300     + 450, - 400
     + 450, - 400     + 450
unsigned int  rawData[83] = {3500,1700, 450,400, 450,400, 450,1300, 450,1250, 450,400, 450,1300, 450,400, 450,400, 450,400, 450,1300, 450,400, 450,400, 450,1300, 450,400, 450,1250, 450,450, 450,1250, 450,400, 450,400, 450,1300, 450,400, 450,400, 450,400, 450,400, 450,1300, 450,1300, 400,1300, 450,1300, 450,400, 450,1250, 450,400, 450,450, 450,400, 450,1250, 450,1300, 450,400, 450,400, 450,1300, 450,400, 450,400, 450}; 

信号出力

シリアルから1を入力すると「お好みボタン」、0を入力すると「消灯ボタン」を押した時と同じ信号を出力するようにする。 こちらの記事を参考に回路を作って動くには動いたけど、自分の部品構成ではこれで良いのかどうかよくわからず。抵抗の値が正しくないような気がする。。。

blogs.yahoo.co.jp

接続 f:id:ysysy:20180422154007p:plain

Arduinoに書き込むコードはこちら。いくつかのサイトを参考に作成。コードがいまいちだがひとまずOKとした。

#include <IRremote.h>

IRsend irsend;
const int KHZ = 38; // 38kHz carrier frequency for the NEC protocol
const unsigned int  IR_SIGNAL_ON[] = {3500,1700, 450,400, 450,450, 450,1250, 450,1300, 450,400, 450,1250, 450,450, 400,450, 450,400, 450,1250, 450,400, 450,450, 400,1300, 450,400, 450,1300, 450,400, 450,1250, 450,450, 450,400, 450,1250, 450,400, 450,400, 450,450, 450,400, 450,1250, 450,400, 450,1300, 450,1300, 400,450, 450,1250, 450,400, 450,400, 450,450, 450,400, 450,1250, 450,400, 450,400, 450,1300, 450,400, 450,400, 450};  // UNKNOWN F6B92168
const unsigned int  IR_SIGNAL_OFF[] = {3500,1700, 450,400, 450,400, 450,1300, 450,1250, 450,400, 450,1300, 450,400, 450,400, 450,400, 450,1300, 450,400, 450,400, 450,1300, 450,400, 450,1250, 450,450, 450,1250, 450,400, 450,400, 450,1300, 450,400, 450,400, 450,400, 450,400, 450,1300, 450,1300, 400,1300, 450,1300, 450,400, 450,1250, 450,400, 450,450, 450,400, 450,1250, 450,1300, 450,400, 450,400, 450,1300, 450,400, 450,400, 450};  // UNKNOWN 1B9C2A92

void setup() {
  Serial.begin(9600);
  // initialize digital pin LED_BUILTIN as an output.
  pinMode(LED_BUILTIN, OUTPUT);

}

void loop() {
  if (Serial.available() > 0) { // シリアル通信でデータが送られてくるまで待つ。
    char c = Serial.read(); // 一文字分データを取り出す。
    if (c == '1') { // 1が送られてきたらLEDを点灯させる。
      digitalWrite(LED_BUILTIN, HIGH);
      for (int i = 0; i < 3; i++) {
        irsend.sendRaw(IR_SIGNAL_ON, sizeof(IR_SIGNAL_ON) / sizeof(IR_SIGNAL_ON[0]), KHZ);
        delay(400);
      }
      
    } else if(c == '0') { // 0が送られてきたらLEDを消灯させる。
      digitalWrite(LED_BUILTIN, LOW);
      for (int i = 0; i < 3; i++) {
        irsend.sendRaw(IR_SIGNAL_OFF, sizeof(IR_SIGNAL_OFF) / sizeof(IR_SIGNAL_OFF[0]), KHZ);
        delay(400);
      }
    }
  }
  delay(1000);
}

動作確認

Arduinoに12VのACアダプタを接続して、シリアルモニタから1または0を入力して照明がオン・オフすることを確認する。*2

わかったこと

  • 赤外線LEDは940nmであること。他の波長ではダメ。
  • 赤外線LEDはパワーが弱いものだと動作しない。最初使っていたものはダメだったので、買い直した。
  • 今回使ったLEDは、ACアダプタから電源を供給する必要がある。
  • MOSFETを使うことで電流を増幅することができる。
  • V=IRの計算式はわかるが、実現したい回路を構成が今の知識ではよくわからない。
  • 誤った回路にするとパーツが壊れる。LEDが何個か壊れた。

次回は、Raspberry Piからのオン・オフ操作

*1:リモコンはPanasonic製なのにPanasonicとは認識されなかった。

*2:ACアダプタがないと電力が足りないため動作しない。

Raspberry Pi Zero Wにカメラをつけてみた

Raspberry Pi Zero Wにカメラをつけて、cronで定期的に部屋の中を撮影してみました。

Raspberry Pi Zero Wにカメラをつける

用意するもの

組み立てる

コネクタを使ってRaspberry Piとカメラを接続しケースに収めます。公式ケースはカメラがきれいに収まるようになっています。

FRISKと並べてみました。FRISKより一回り程度大きなサイズです。 f:id:ysysy:20170326215332j:plain

カメラを有効化する

rasp-configを起動します。

$ sudo raspi-config

「5 Interfacing Options」を選択します。 f:id:ysysy:20170326211005p:plain

「P1 Camera」を選択します。 f:id:ysysy:20170326211118p:plain

「Yes」を選択します。 f:id:ysysy:20170326211210p:plain

「Ok」を選択します。 f:id:ysysy:20170326211300p:plain

「Finish」を選択してraspi-configを終了します。 f:id:ysysy:20170326211404p:plain

Webサーバーを導入する

Webサーバーとしてnginxを導入

$ sudo apt-get install nginx

/etc/nginx/sites-available/defaultに60から62行目を追加して、ファイルの一覧を表示できるようにします。

     54         # deny access to .htaccess files, if Apache's document root
     55         # concurs with nginx's one
     56         #
     57         #location ~ /\.ht {
     58         #       deny all;
     59         #}
     60         location /pic {
     61           autoindex on;
     62         }
     63 }

nginxを再起動して設定を有効化します。

$ sudo service nginx restart

写真を保存するディレクトリを作成

$ sudo mkdir /var/www/html/pic
$ sudo chown pi.www-data

撮影用スクリプトを作成する

以下のスクリプトを作成します。今回はファイル名をpic.shとしました。

#!/bin/bash
/usr/bin/raspistill -w 480 -h 360 -ex auto -awb auto -rot 270 -o /var/www/html/pic/$(date +%Y%m%d%H%M).jpeg

スクリプトに実行権限を付与します。

chmod +x pic.sh

cronに登録する

5分ごとに撮影用スクリプトを実行するようcrontabに以下の一行を追加します。

*/5 * * * * /home/pi/pic.sh

ブラウザから確認

http::///pic にアクセスするとファイルの一覧が表示されます。 f:id:ysysy:20170326213946p:plain

感想

リビングに置いていると不在時の様子がわかるので、いつ誰が何をしていたかわかり意外に面白いです。当然、家族の許可がないとできませんが。。。知人に話したら不在時のペットの様子の確認に使ってみたいと言っておりました。確かに誰もいない部屋のペットがどんな行動をしているかは気になりますね。

*1:カメラにコネクタが付属していますが、Raspberry Pi Zeroには接続できないので別途購入する必要があります。ちなみに公式ケースにはケースに収まる短いケーブルが付属しています。私は知らずに長いケーブルを買ってしまいました。