読者です 読者をやめる 読者になる 読者になる

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には接続できないので別途購入する必要があります。ちなみに公式ケースにはケースに収まる短いケーブルが付属しています。私は知らずに長いケーブルを買ってしまいました。

Raspberry Pi Zero WからBluetoothでJBL Flip 3に音を出してみた

Raspberry Pi Zero WからBluetoothJBL Flip 3に音を出せるようにしてみました。

モジュールインストール

 $ sudo apt-get install pulseaudio-module-bluetooth bluez-tools

グループ設定

 $ sudo gpasswd -a pi pulse
 $ sudo gpasswd -a pi lp
 $ sudo gpasswd -a pulse lp
 $ sudo gpasswd -a pi audio
 $ sudo gpasswd -a pulse audio

Bluetoothの設定*1

 $ sudo sh -c "echo 'extra-arguments = --exit-idle-time=-1 --log-target=syslog' >> /etc/pulse/client.conf"
 $ sudo hciconfig hci0 up
 $ sudo hciconfig hci0 class 0x240414
 $ sudo reboot

ペアリング

 $ sudo bluetoothctl
 [NEW] Controller XX:XX:XX:XX:XX:XX RaspberryPi [default]
 [bluetooth]# agent KeyboardOnly
 Agent registered
 [bluetooth]# default-agent
 Default agent request successful
 [bluetooth]# scan on
 Discovery started
 [CHG] Controller  Discovering: yes
 [bluetooth]# pair XX:XX:XX:XX:XX:XX
 [agent] Enter PIN code: 0000  # 0000を入力
 [CHG] Device XX:XX:XX:XX:XX:XX Paired: yes
 Pairing successful
 [CHG] Device XX:XX:XX:XX:XX:XX Connected: no
 [CHG] Device XX:XX:XX:XX:XX:XX RSSI: -60
 [bluetooth]# trust XX:XX:XX:XX:XX:XX
 [bluetooth]# connect XX:XX:XX:XX:XX:XX
 [bluetooth]# exit

Pulseaudioの起動と設定

 $ pulseaudio --start

このコマンドで値を確認

 $ pacmd list-sinks

確認した値を引数として渡す

 $ pacmd set-default-sink bluez_sink.XX_XX_XX_XX_XX_XX

音量調整

 $ alsamixer

再生

 $ mplayer hoge.mp3

Raspberry Piのパワーがないためか、設定が悪いためか音質はあまりよくありませんでした。それとこの設定だけだと再起動すると設定が消えますが、今回は試験的に設定しただけなので設定を永続化する方法まで調べておりません。

参考にしたサイト

OpenWhiskからつぶやけないorz

前回作成した天気予報を取得する仕組みからTwitterにつぶやくプログラムを作ろうとしました。

Twitterの開発者用API取得

Twitterの開発者用のAPIを取得します。

Twitterにアクセスして、[Create New App]をクリックします。

[Name]、[Description]、[Website]への入力し、[Deveopper Aggreement]に同意して、[Create your Twiiter applicaiton] をクリックします。

[Cretae my access toke] をクリックして、アクセストークンを生成したら、[Consumer Key (API Key) ]、[Consumer Secret (API Secret)]、[Access Token]、[Access Token Secret]を控えておきます。

必要なモジュールのインストール

node.js、npmを事前にインストールしておきます。

TwitterAPIをコールするモジュールtwittを導入します。

$ npm install twitt

プログラム作成

index.jsという名前で以下のファイルを作成します。

var Twit = require('twit');
var client = new Twit({
    consumer_key: '先ほど控えた値を指定',
    consumer_secret: '先ほど控えた値を指定',
    access_token: '先ほど控えた値を指定',
    access_token_secret: '先ほど控えた値を指定'
});

function main(params) {
  msg=params.payload;
  //msg='ローカルでバッグ用メッセージ';
  client.post('statuses/update', { status: msg }, function(error, tweet, response) {
    if(error) throw error;
    console.log(tweet);     // Tweet body.
    console.log(response);  // Raw response object.
    return tweet;
  });
}

module.exports.main = main;
// ローカルで実行する場合は以下のコメントを外す
//main(params);

package.jsonの作成

package.jsonという名前で以下のファイルを作成します。

{
  "name": "TweetWeather",
  "main": "index.js",
  "dependencies" : {
    "twit" : "2.2.5"
  }
}

Bluemixへアクションを登録

作成したファイルをZIP化します。

$ zip -r action.zip *

[https://console.ng.bluemix.net/openwhisk/learn/cli:title]より、OpenWhiskのコマンドラインツールをダウンロードします。

プログラムをアップロードして、Bluemix上にアクションを作成します。ここではアクション名をTweetWeatherにしました。

先ほどダウンロードしたツールを次のように実行することでアクションを登録できます。

$ wsk action create TweetWeather –kind nodejs:6 action.zip

前回作成したシーケンスの最期にこのアクションをつなげると、日本語に翻訳した天気予報をtweetできます。と言いたいのですが、OpenWhiskからはなぜかTweetできませんでした。。。ローカルからだとつぶやけるのですが。

次のように意図的にエラーになる行を追加するとつぶやけるので、プログラムの問題だと思います。node.jsをよくわかっていないために正しいプログラムを書けていないようです。

    return tweet;
  });
  throw error;  //意図的にエラーになる行を追加
}

node.jsの勉強が必要ですね。

OpenWhiskで天気予報サービスを使ってみる

Bluemix上のOpenWhiskから天気予報(英語)を取得して、Watsonに翻訳させるサービスを作ってみました。

OpenWhiskでの開発

メニューから[OpenWhisk]を選択します。 f:id:ysysy:20170305215629p:plain

2通りの開発方法がありますが、今回は[ブラウザーで開発]を選択しました。 f:id:ysysy:20170305215956p:plain

経度、緯度を受付けるアクションを作成

[アクションの作成]を選択して、天気予報サービスに渡す経度と緯度を取得するアクションを作成します。

f:id:ysysy:20170305220438p:plain

名前をsetLocationとして、[アクションの作成]をクリック f:id:ysysy:20170305221445p:plain

次のコードを入力して、[ライブにする]をクリックします。 このコードでは、緯度と経度を受け取り、そのまま返却します。緯度、経度の入力がない場合は、デフォルト値として東京の緯度、経度を返却します。

function main(params) {
    var msg={};
    msg.latitude=params.latitude || "35.66";
    msg.longitude=params.longitude || "139.73";
    return msg;
}

[シーケンスにリンク]をクリックして、天気予報を取得する処理を次に追加します。 f:id:ysysy:20170305222821p:plain

天気予報を取得する処理の追加

[WEATHER]を選択します。 f:id:ysysy:20170305222928p:plain

[NEW BINDING]をクリックします。

f:id:ysysy:20170305223217p:plain

[サービス・インスタンスのプロビジョニング]をクリックします。 f:id:ysysy:20170305223346p:plain

[OK、準備ができました]をクリックします。 f:id:ysysy:20170305223505p:plain

[Weather Company Data]を選択します。

f:id:ysysy:20170305223702p:plain

画面下の[作成]をクリックします。

f:id:ysysy:20170305223827p:plain

この画面が表示されたらサービス・インスタンスの作成は完了です。元の画面に戻ります。 f:id:ysysy:20170305224040p:plain

[OK、新規サービス・インスタンス]をクリックします。 f:id:ysysy:20170305224159p:plain

作成したサービス・インスタンスが選択されていることを確認して、[構成の保存]をクリックします。画面上の名前の入力(今回はgetWeatherDataとしました。)も忘れずに! f:id:ysysy:20170305224648p:plain

[シーケンスに追加]をクリックします。

f:id:ysysy:20170305225624p:plain

[これは適切なようです]をクリックします。 f:id:ysysy:20170305232759p:plain

[シーケンス名]を入力して、[アクション・シーケンスの保存]をクリックします。 f:id:ysysy:20170305232900p:plain

[完了]をクリックします。

取得したデータを加工するアクションを作成

次に取得した天気予報データのうち、当日の"narrative"を抽出するアクションを作成します。

[アクションの作成]を選択します。 f:id:ysysy:20170305220438p:plain

アクションのは名前をconvertMessageとして作成しました。コードは次のようになります。取得したデータをWatsonに渡すため、メッセージを加工しています。

function main(params) {
    var msg={};
        // 翻訳先の言語は日本語
    msg.translateTo="ja";
        // 翻訳元の言語は英語
    msg.translateFrom="en";
    // 天気予報サービスから取得した当日の"narrative"を取得
    msg.payload=params.forecasts[0].narrative;
        // ログ出力用
    console.log(msg.payload);
    return msg;
}

作成した[マイ・シーケンス]を選び、[拡張]をクリックします。 f:id:ysysy:20170305233421p:plain

[MY ACTIONS]を選択します。 f:id:ysysy:20170305232227p:plain

[convertMessage]を選択して、[シーケンスに追加]をクリックします。 f:id:ysysy:20170305233733p:plain

[変更の保存]をクリックします。

翻訳サービス(WATSON TRANSLATOR)の追加

カタログに戻り[Watsonサービス]からサービスを作成します。 f:id:ysysy:20170305235805p:plain

[Language Translator]を選択し、[作成]をクリックします。 f:id:ysysy:20170305235941p:plain

OpenWhiskのマイ・シーケンスに戻り、[拡張]をクリックします。

[WATSON TRANSLATOR]を選択します。 f:id:ysysy:20170305232227p:plain

[translator]を選択します。

f:id:ysysy:20170306000611p:plain

[NEW BINDING]の横の青色のマークを選択して、[シーケンスに追加]をクリックします。 f:id:ysysy:20170306000447p:plain

[変更の保存]をクリックします。

シーケンスの実行

[このシーケンスを実行]をクリックします。

f:id:ysysy:20170306000910p:plain

[この値で実行]をクリックして、シーケンスを実行します。 f:id:ysysy:20170306001020p:plain

実行結果です。 f:id:ysysy:20170306001144p:plain

元の英語

Partly cloudy. Low 7C.

翻訳した日本語

一つには曇り。 低7C。

翻訳精度がいまいちですね。ちなみにGoogle翻訳だと

晴れときどき曇り。 低い7C。

こちらの方がマシですね。

感想

サーバーレスで天気予報や翻訳サービスなどを簡単に呼び出せるのはとても便利ですね。アイデア次第でサービスを組み合わせて、面白いサービスが作れそうです。本当は取得した結果をTwitterにつぶやくようにしたかったのですが、時間がなくできませんでした。また今度挑戦してみます。それにしてもBluemixのコンソールは遅いです。もう少し改善しないものでしょうか。。。

テキスト処理の備忘録

固定長ファイルを扱っていて、ファイルに本来あるべきでない非ASCII文字があり、ジョブが異常終了することがありました。その時の調査でよく使っていたコマンドのメモです。

指定したバイト長より大きな行を出力

この例では100バイトより大きな行を出力します。

$ awk 'length($0)>100 {print NR":"length($0)":"$0}' ファイル名 

ASCII以外の文字がある行を表示(該当の文字は色つきで表示)*1

$ grep --color='auto' -P -n "[\x80-\xFF]" ファイル名

指定した行を表示する(())

この例では10行目を表示します。

$ sed -n 10p ファイル名

この例では10-20行目を表示します。

$ sed -n 10,20p ファイル名

*1:grepによっては-Pをサポートしていません。サポートされていない場合の代替手段も知りたいのですが調べていないです。

DropboxへAPIを使ってアクセスしてみる

DropboxAPIを使ってみました。言語はPythonを選びました。

Dropbox for Pythonをインストール

pipコマンドでインストール

$ pip install dropbox

Dropboxへアクセスするアプリを作成

Dropboxにログインして、 https://www.dropbox.com/developers/apps/createにアクセスします。

以下の3項目を入力して、[Create app]をクリックします。

[1. Choose an API] -> [Dropbox API]を選択しました

[2. Choose the type of access you need] -> [App folder] *1を選択しました。

[3. Name your app] -> [AppSample1] 名前は好きな名前にしましょう。*2

メニューの[My apps]-[Settings]を表示して、[Generate access token]の[Generate]をクリックし、表示された値を控えます。

アクセスするコードを作成

# coding=utf-8   
import dropbox

dbx = dropbox.Dropbox('<先ほど控えたトークンを指定>')
dbx.users_get_current_account()
f = open('<アップロードするローカルファイルのパスを指定>', 'rb')
dbx.files_upload(f.read(),'<Dropbox上でのファイルパスを指定>')
f.close()

# アプリディレクトリにあるファイルの一覧を表示
for entry in dbx.files_list_folder('').entries:
    print(entry.name)

アップロード実行

作成したファイルを実行すると、Dropboxのルートに[アプリ]というフォルダが作成され、その下にデフォルトだとアプリ名と同じフォルダが作成され、その直下にファイルがアップロードされます。

*1:App folderを選択するとアプリ専用のフォルダ以外にはアクセスできません

*2:アプリ名は他の人が使っていると駄目なようなので、他とかぶらないようにしましょう。