2019/01/06

Qiita はじめました

nikotan - Qiita

2018/07/07

体重計をインターネットにつないでスマートスピーカーと連携させてみた

体重計を改造して、測った体重を無線LAN経由でクラウドに記録できるようにしてみました。さらに、一台の体重計を家族で共有するために、スマートスピーカーで誰の体重かを知らせてあげられるようにしました。

体重計の改造

一般に、デジタル式の体重計では、ひずみゲージセンサを使った「ロードセル」というデバイスで重さを計測しています。このロードセル、単体でも入手できますが、体重計を作 るなら、市販の体重計をばらしてロードセルと筐体をそのまま流用してしまったほうが、手間もコストもかからず楽です。
というわけで、タニタのシンプルなデジタル式体重計(HD-661)を購入して分解・改造。ロードセルを使った重さの計測には、HX711というICチップを使ったロードセル用のADコンバータを、マイコンには無線LAN内蔵のESP-WROOM-02(ESP8266)を、体重の表示には4桁の7セグメントLEDロジックIC(シフトレジスタ)の74HC595を使います。体重計そのものとこれらの部品など含めて、部品代は総額3500円くらい。
過程はすっ飛ばして、出来上がった体重計はこんな感じです。



回路図は以下の通り。7セグメントLEDと74HC595の周辺の配線が多くて苦労しました。。。
(回路図を書くためにFritzingを初めて使ってみましたが、便利ですねこれ)

マイコン側はArduinoで体重を計測してHTTP POSTするプログラムを、クラウド側はAWSのAPI Gateway + Lambda + DynamoDBで計測した体重を記録するだけのWEBサービスを、それぞれ作りました。

スマートスピーカーとの連携

このままだと誰の体重かが分からないので、Alexa Skill Kitでカスタムスキルを作って、スマートスピーカー(Amazon Echo dot)に誰の体重を測ったのかを伝えられるようにしてみました。使う人とスマートスピーカーとのやり取りはこんな感じ(やり取りがわかりやすかったのでテスト用の画面で)。

これで、人ごとにデータを記録できるようになります。
実際のデータ記録先は、IoT向けのセンサデータなどの記録・分析用WEBサービスであるThingSpeak(運営しているのはMatlabのMathWorks)。グラフでログも確認できます。


というわけで、体重を毎日測って健康管理できるようになりました。

※今回のハードとソフトを作るにあたっては、以下の記事などを参考にしました。

2018/05/27

無線LAN&BluetoothモジュールESP-WROOM-32とBLEタグで誰かの在宅状況を見守ってみる

子供のランドセルとかに付けたBLE(Bluetooth Low Energy)タグのIDをモニタリングして、家から出発したり、家に帰ってきたことを検知してLINEに通知してくれる見守りセンサを作ってみました。

見守りセンサの方には、無線LANとBluetoothが一体になった小型モジュールで数百円で入手できてしまうESP32 (ESP-WROOM-32) を使います。前回のESP8266と同様、技適取得済みで安心、入手も容易です。
BLEタグの方には、例えばmamorioなどの紛失防止タグをそのまま使います。要は、BLEのadvertisingパケットを出し続けてくれてればOK。


苦労したポイントだけまとめておくと、、、
  • ESP32上で無線LANとBluetoothを同時に使うときのメモリ不足
    ⇒ Arduino IDEでのビルドオプション変更で解決
  • ESP32上で無線LANとBluetoothを同時に使うときの電源容量不足
    ⇒ 電源回路用のレギュレータ選定(ADP3338)で解決
  • 1台のESP32でカバーできる範囲が狭い
    ⇒ 複数台のモニタリング結果をクラウド連携で統合(AWS Lambda + DynamoDB + API Gateway)

見守りセンサ側のコード(ESP32上で動くArduinoコード)と、クラウド側のコード(AWS Lambda上で動くPythonコード)は、備忘録として以下に。
nikotan/BLETagMonitor_esp32.ino - GitHubGist
nikotan/BLETagMonitor_lambda.py - GitHubGist

実際しばらく使ってみると、在宅状況を把握できるだけで得られる安心もあるということが分かりました。


※今回のハードとソフトを作るにあたっては、以下の記事などを参考にしました。

2018/04/30

無線LANモジュールESP-WROOM-02と傾斜スイッチでポストへの投函を検知する電池駆動センサを作ってみた

個人でも安く容易に入手できて、かつ技適取得済みで安心の、ESP-WROOM-02(ESP8266)。恥ずかしながらつい先日教えてもらって初めてその存在を知ったので、使い方の勉強がてら動くものを作ってみました。

今回作ったのは、家のポストにモノが投函されたことを検知してIFTTT経由でLINE通知してくれる電池駆動のセンサ。仕上がりはこんな感じ。


我が家のポストは外にあって電源が取れない場所にあるので、ESP-WROOM-02の「deep sleep」機能(超低消費電力でのスリープ動作)を使って、乾電池での長期間駆動を目指します。似たようなことをやっているINTERNET Watchの記事などを参考に、以下の方針で作りました。
  • 普段はESP-WROOM-02はdeep sleepモードで寝かせとく
  • ポストにモノが投函されたことをESP-WROOM-02のリセットピンにつないだ傾斜スイッチで検知、deep sleepから起こす
  • 起きたら、自宅の無線LAN APにつないでIFTTTにWEBHOOK、通信終わったらすぐまたdeep sleepモードで寝かす

傾斜センサの部分は「型想い」と両面テープでポストのフタの内側へ、センサ本体と電池ケースは3Mの面ファスナーで着脱できるようにポストの内側へ、それぞれ固定しました。



ESP-WROOM-02上で動いているコード(Arduinoのコード)は以下に置いておきます。
nikotan/esp8266_PostMonitor.ino - GitHubGist

Arduinoのコードで動かせるってすばらしい。手軽にネットワーク機能を使ったデバイスを試作できて楽しいです。今回作ったものは今のところちゃんと動いていますが、どれくらい長期間駆動できるかはまだ不明。しばらく様子を見てみます。


※今回のハードとソフトを作るにあたっては、以下の記事などを参考にしました。

2018/04/22

Raspberry PiとWEBカメラで洗濯機の動作状況をモニタリングして、洗濯終了をLINEに通知するIoTボットを作ってみた

家の一階にある洗濯機で洗濯が終わったかどうかを、二階のリビングにいても分かるようにしたい!というわけで、タイトルのとおりです。

洗濯終了を判定するための手がかりとしては、洗濯終了時に鳴るビープ音、洗濯機の使用電力(電流)、などがありそうですが、今回は操作パネルの表示を使います。我が家の洗濯機には、操作パネルの中に洗濯終了時にだけ点灯する場所があったので、この場所の点灯状況で洗濯終了が自動判定できちゃいます。


Raspberry PiにつないだWEBカメラで操作パネルの特定位置の点灯状況を判定するだけなんですが、操作パネルをうまく写せるカメラの固定場所があまりない、というのが実は問題です。ちょっと離れた場所から監視せざるを得ないので、向きをざっくり合わせるだけでも使える、とか、カメラが多少動いたりしても使える、とかするための工夫が必要です。


基本的な方針は以下の通りですが、点灯状況を監視したい場所の近くにARマーカーを貼って、これを基準に監視場所の画像を切り出して点灯状況の判定をするようにしました。
  • ARマーカを点灯状況を監視すべき場所の近くに貼付
  • WEBカメラで撮影した画像からARマーカを検出
  • 検出したARマーカを基準に監視場所の画像を切り出して点灯判定
  • 点灯していると判定された場合にはIFTTT経由でLINEに「洗濯が終わっています」を通知
Raspberry Pi用のPythonコードは備忘録がてら以下に置きます。
nikotan/WashingMonitor

誤報対策で苦労しましたが、今は安定して動いてます。結構便利。


ARマーカーの検出のために使ったOpenCVのarucoライブラリや、arucoライブラリを使うためのRaspberry Pi上でのOpenCVのビルドや、IFTTTを使ったプッシュ通知などについては、以下記事を参考にしました。

2016/04/09

LINE BOT APIで翻訳ボットを作ってみた

巷で話題のLINE BOT API、面白そうなので、試しに翻訳ボット(送った文章を、日本語は英語に、日本語以外は日本語に、それぞれ自動翻訳して返すだけのLINEアカウント)を作ってみました。単純だけど反応良く動いてて楽しいです(*^^)v


翻訳のところはMicrosoft Translator Text APIを使って、webscript.ioで動かしてます。諸々、以下の記事などを参考にさせていただきました。

webscript.io上のコードはこちらに備忘録。

2015/09/06

OSX10.10.5にCaffe環境構築

久しぶりの週末プログラミングでDeep Learning触ってみました。
Deep Learningのフレームワークとしては、無難にCaffeを選択。
以下はmacbook airで動かすまでの手順メモです。

homebrewをインストール

$ ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"

pyenvをインストール

$ brew install pyenv
.bash_profileeval "$(pyenv init -)"を追加

Xcode command line toolsをインストール

pythonのインストール時に"Missing the OpenSSL lib?"とかエラー出たらインストール。
$ xcode-select --install

Anaconda Pythonをインストール

最新のバージョンを調べてインストール。
Anacondaは機械学習のライブラリ。
$ pyenv install -l
$ pyenv install 3.4.3
$ pyenv install anaconda-2.3.0
$ pyenv rehash
$ pyenv local anaconda-2.3.0
$ pyenv global anaconda-2.3.0
$ pyenv version

Caffeに必要な諸々とCaffeのビルド

Caffeのサイトにある手順通りでOK。
以上で環境構築完了。
Caffeのサイトにあるチュートリアルで動作を確認できます。

2012/05/28

PogoplugをOpenPogo化してOSX LionのTime Machineバックアップ先にして使ってます

Pogoplug(POGO-P25)を自宅で便利に使っていたんですが、ファイルコピーがとにかく遅い。あと、せっかくハードディスクをネットワークにつなげられてるんだから、Time Machineのバックアップ先にもしたい。

そこで、PogoplugをOpenPogo化してカスタマイズ。Netatalkで高速なNASにしたり、Time Machineのバックアップ先にしたり、2台のハードディスクをつないで二重化したりして、もう3ヶ月ほど連続で動かしていますが非常に安定しています。Pogoplugのもともとの機能は全てそのまま生かしたまま、Linuxボックスとして自由にカスタマイズできる、というのは超絶便利です。

というわけで、備忘録として、参考にしたサイトのリストをまとめておきます。

2012/03/11

裸族の二世帯住宅を OSX Lion で初期化する方法

fdiskを使ってマスターブートレコードを初期化することもできたので追記。pdiskを使った場合はAppleパーティションマップでのパーティション管理になるんだけど、この場合、linuxマシンで認識できないことがあった。マスターブートレコードにしておけばその心配がないっぽい。
[2012/3/12 追記]

裸族の二世帯住宅とは、センチュリーからかつて販売されていたRAIDできるHDDケース。使いやすいケースなんだけど、RAID0やRAID1で設定している場合にディスクユーティリティで初期化できない、というOSXユーザにとっては厄介な問題がある。ここなどによると、OSX 10.5以降のディスクユーティリティで起きるらしい。

というわけで、裸族の二世帯住宅を OSX Lion (10.7.x) で初期化(パーティションの切り直し&フォーマット)する方法を備忘録。
※お決まりですが、、、
あくまで自分用の備忘録です。もし参考にやってみようという方がいたら自己責任でどうぞ〜



[方法A] マスターブートレコード方式 (fdisk を使用)

(1) ターミナルから fdisk でマスターブートレコードを初期化

※例えば、HDDケースが /dev/disk1 として認識されている場合
$ fdisk -i /dev/disk1
fdisk: could not open MBR file /usr/standalone/i386/boot0: No such file or directory

-----------------------------------------------------
------ ATTENTION - UPDATING MASTER BOOT RECORD ------
-----------------------------------------------------

Do you wish to write new MBR and partition table? [n] y[enter]

(2) 続いて fdisk でパーティションを定義

※単一のパーティションで良ければ auto [style] が便利
$ fdisk -e /dev/disk1
fdisk: could not open MBR file /usr/standalone/i386/boot0: No such file or directory
Enter 'help' for information
fdisk: 1> auto hfs[enter]
fdisk:*1> p[enter]
Disk: /dev/disk1 geometry: 121601/255/63 [1953525169 sectors]
Offset: 0 Signature: 0xAA55
Starting Ending
#: id cyl hd sec - cyl hd sec [ start - size]
------------------------------------------------------------------------
*1: AF 0 1 1 - 1023 254 63 [ 63 - 1953525106] HFS+
2: 00 0 0 0 - 0 0 0 [ 0 - 0] unused
3: 00 0 0 0 - 0 0 0 [ 0 - 0] unused
4: 00 0 0 0 - 0 0 0 [ 0 - 0] unused
fdisk:*1> w[enter]
Writing MBR at offset 0.
fdisk: 1> q[enter]

(3) ディスクユーティリティか newfs_hfs/dev/disk1s1 を初期化



[方法B] Appleパーティションマップ方式(pdisk を使用)

(1) ターミナルから pdisk でパーティションマップを初期化

※例えば、HDDケースが /dev/disk2 として認識されている場合
$ pdisk /dev/disk2
Edit /dev/disk2 -
Command (? for help): i[enter]
map already exists
do you want to reinit? [n/y]: y[enter]
A physical block is 512 bytes: 2048[enter]
A logical block is 512 bytes: [enter]
size of 'device' is 1953525168 blocks (512 byte blocks): [enter]
new size of 'device' is 1953525168 blocks (512 byte blocks)
Command (? for help): p[enter]

Partition map (with 512 byte blocks) on '/dev/disk2'
#: type name length base ( size )
1: Apple_partition_map Apple 63 @ 1
2: Apple_Free Extra 1953525104 @ 64 (931.5G)

Device block size=2048, Number of Blocks=488381292 (931.5G)
DeviceType=0x0, DeviceId=0x0

Command (? for help): c[enter]
First block: 64[enter]
Length in blocks: 1953525104[enter]
Name of partition: Extra[enter]
Command (? for help): p[enter]

Partition map (with 512 byte blocks) on '/dev/disk2'
#: type name length base ( size )
1: Apple_partition_map Apple 63 @ 1
2: Apple_HFS Extra 1953525104 @ 64 (931.5G)

Device block size=2048, Number of Blocks=488381292 (931.5G)
DeviceType=0x0, DeviceId=0x0

Command (? for help): w[enter]
Writing the map destroys what was there before. Is that okay? [n/y]: y[enter]
The partition table has been altered!

Command (? for help): q[enter]

(2) ディスクユーティリティか newfs_hfs/dev/disk2s2 を初期化

2011/08/18

GR DIGITAL II で赤外線写真を試す


ケンコー・トキナーの PRO1D R72 (赤外線透過フィルター) を使った風景写真が面白い。撮影や加工のコツがまだまだ掴めないけど、適当にやってもそれなりに不思議な感じに仕上がる。

2011/08/16

OpenCVで顔検出してリアルタイムモザイク

もはや定番となってますが、顔検出のサンプルコード "samples/c/facedetect.cpp" をベースに、検出された顔の領域にモザイクをかけるようにしてみた。コードはgist:1148913へ。

本格的な画像処理や画像認識の機能をお手軽に試せてしまうの楽しい。
すごいぞ、OpenCV!;-)

モザイクかけるだけで何やら怪しい雰囲気になるなw

2011/08/15

OSX Lion (MacBook Air - Mid 2011) 上の Xcode4.1 で OpenCV2.2 を使えるようにする

MacBook Air (Mid 2011) で OpenCV を使ったコードを動かすまでのメモ。
手順は以下のとおり。

[1] Xcode をインストール

AppStore で検索&クリック。あとは待つだけ。

[2] MacPorts をインストール

公式サイトから Lion 用のイメージをダウンロードしてきてインストール。
インストールが終わったら、一応アップデートしておく。
$ sudo port selfupdate

[3] OpenCV をインストール

MacPorts を使ってしまえばコマンド一発。時間はかなりかかる。
$ sudo port install opencv

[4] Xcode のプロジェクト設定

ポイントは3つ。
  • 「Valid Architectures」を"x86_64"のみにする。
    ※"i386"を含めたままでは、ビルドでエラーが出た。
  • 「Header Search Paths」に"/opt/local/include"を追加。
  • 「Other Linker Flags」に必要なライブラリのパスを追加。例えば、以下の3つ。
    • "/opt/local/lib/libopencv_core.2.2.0.dylib"
    • "/opt/local/lib/libopencv_highgui.2.2.0.dylib"
    • "/opt/local/lib/libopencv_imgproc.2.2.0.dylib"
    ※ここで設定せずにプロジェクトに直接追加してしまってもOK

というわけで、C でこんなコードを書いて動かしてみると、、、
#include <opencv/cv.h>
#include <opencv/highgui.h>

IplImage* doCvGray(IplImage* in);
IplImage* doCanny(IplImage* in, double lowThresh, double highThresh, double aperture);

int main (int argc, const char * argv[])
{
  cvNamedWindow ("Capture", CV_WINDOW_AUTOSIZE);
  cvNamedWindow ("Edge", CV_WINDOW_AUTOSIZE);

  CvCapture *capture = cvCreateCameraCapture(0);
  assert(capture != NULL);

  IplImage *frame = 0;
  IplImage *out = 0;
  int c;

  while (1) {
    frame = cvQueryFrame (capture);

    out = doCvGray(frame);
    out = doCanny(out, 10, 100, 3);
    cvShowImage("Capture", frame);
    cvShowImage("Edge", out);
    cvReleaseImage(&out);

    c = cvWaitKey (2);
    if (c == '\x1b')
      break;
  }

  cvReleaseCapture (&capture);
  cvDestroyWindow ("Capture");
  cvDestroyWindow ("Edge");

  return 0;
}

IplImage* doCvGray(IplImage* in){
  IplImage* out = cvCreateImage(cvGetSize(in), IPL_DEPTH_8U, 1);
  cvCvtColor(in, out, CV_BGR2GRAY);
  return out;
};

IplImage* doCanny(IplImage* in, double lowThresh, double highThresh, double aperture){
  IplImage* out = cvCreateImage(cvGetSize(in), IPL_DEPTH_8U, 1);
  cvCanny(in, out, lowThresh, highThresh, aperture);
  return out;
};
はい、エッジ検出できておりますw

2011/05/21

Project Euler をやり始めてみた

数日前に存在を初めて知ったProject Euler
Project Euler is a series of challenging mathematical/computer programming problems that will require more than just mathematical insights to solve. Although mathematics will help you arrive at elegant and efficient methods, the use of a computer and programming skills will be required to solve most problems.
About Project Euler - Project Euler
要は、プログラミングで解く数学パズル、みたいなもの。WEB上に用意された沢山の問題を一つづつ解いていく。ちなみに、他のユーザが書いたコードを見ることもできて、ちょっと勉強になったりもする。
全部で300以上ある問題のうち、まだ10問しかクリアしてないけど、解き方を思いついたり、うまく一発で答えを当てられたときの気持よさはなかなかのもの。少しでもプログラミングできる人は是非お試しを。

さて、プログラミングにはどんな言語を使ってもいいんだけど、僕は結局Javaを選択して少しずつ問題を消化していこうかと。ちょっとだけクラスの設計もしたので、使ってみたかったgithubにリポジトリ作ってみた(こちら)。この設計に基づくと、記念すべき1問目、Problem1を解くSolverはこんな感じになる。
package euler;

public class Solver001 implements Solver
{
  public String solve()
  {
    int sum = sum(3, 1000) + sum(5, 1000) - sum(15, 1000);
    return Integer.toString(sum);
  }

  public int sum(int base, int max)
  {
    int n = (max - 1) / base;
    return base * n * (n+1) / 2;
  }
}
ま、あまり設計とか考えずにmain関数ベタ書きでよかったかも -_-;

まずは25問クリアして"Level1"になるのが目標です。

2011/04/23

iPhoneは基地局の情報を収集していた?

Appleが4/27付けで説明を公開した。
Apple Q&A on Location Data

要は、
  • ユーザの位置情報を収集はしていない
  • iPhoneに記録されている位置情報は、Appleが管理している基地局DBの一部(測位の高速化・高精度化のための端末側キャッシュとして利用)
  • 長期間に亘って記録していたのはソフトウェアのバグによるもので、近々アップデート予定(合わせて、キャッシュの母艦へのバックアップもやらないようにする)
とのこと。
基地局DBのキャッシュか、なるほど、って感じ。
[4/28追記]

iPhoneで知らぬ間に位置情報の履歴が記録されていた、ということが話題になっている。記録された履歴はiPhoneの同期先であるPCに保存され、アメリカの研究者がオープンソースで公開したiPhone Trackerというツールで可視化までできてしまう、というもの。

実際に上記のツールを使って僕のiPhoneで記録された位置情報の履歴を表示すると、こうなる。

個人的には非常に面白いデータで色々いじってみたくなるけど、プライバシ情報の扱い方という観点では改めて怖さも感じてしまう。

ま、ここではとりあえずそのあたりの問題は置いておいて、データを眺めてみる、、、むむ、何となく位置の履歴が自分の直感と合わない。例えば、もっと特定の場所に集中して位置情報が記録されててもいいはず。

そこで、こちらの記事を元にしてperlのコードを書いてみたりしつつ、母艦に保存されたsqliteのデータファイルの中身を確認してみた。見れば見るほど、このデータ、「位置情報の履歴を『逐一』記録したもの」というよりは「iPhoneが見つけた基地局の情報を記録したもの」って感じがしてくる。

というのも、、、
母艦に保存されたデータ(sqliteのデータベース)を開いてみると、位置情報が記録されているWifiLocationとCellLocationというテーブルの定義は以下のとおり。
CREATE TABLE WifiLocation (
MAC TEXT,
Timestamp FLOAT,
Latitude FLOAT,
Longitude FLOAT,
...(略)...
PRIMARY KEY (MAC)
);

CREATE TABLE CellLocation (
MCC INTEGER,
MNC INTEGER,
LAC INTEGER,
CI INTEGER,
Timestamp FLOAT,
Latitude FLOAT,
Longitude FLOAT,
...(略)...
PRIMARY KEY (MCC, MNC, LAC, CI)
);
文字通り、Timestampは時刻、Latitudeは緯度、Longitudeは経度を意味している。
ここで注目すべきは赤字で示したところ。PRIMARY KEY指定されてるWifiLocationのMACとCellLocationの(MCC,MNC,LAC,CI)MACは、おそらくWi-FiアクセスポイントのMACアドレス。一方の(MCC,MNC,LAC,CI)は、ケータイ基地局のIDに該当するようなものではないだろうか。要は、Wi-FiアクセスポイントMACアドレスとケータイ基地局IDは、それぞれのテーブルの中で重複が許されないってこと。

というわけで、これらのテーブルはあくまで「Wi-Fiアクセスポイント」および「ケータイ基地局」のリストを記録するためのもので、これらのテーブルにユーザの位置情報の履歴が全て記録されることはあり得ない。つまり、こういう形で位置情報の履歴を残している目的は、ユーザの位置を追跡するため、というよりは、基地局の位置を収集するため、という方が近いんじゃないかな。

さらに言えば、こうやって取得された基地局リストは、何らかの方法でやっぱりAppleが収集して、skyhookPlaceEngineなどと同じような測位用のデータベースを整備するために使っていた(もしくは、使おうとしていた)んじゃなかろうか。位置情報の履歴が記録され始めた時期(2010年の6月前後?)と、「Appleとskyhookの間の契約が終了→Appleが独自測位技術の利用を開始?」と報じられた時期(2010年の8月あたり)の関係も気になるところ。

ちなみに、見た限り、母艦上のデータに記録されている時刻(Timestamp)は、測位されたときの時刻ではなさそう。なので、ライフログとしてはあまり使えなさそう。あと、僕のデータにはアクセスポイントが15万件以上リストされていた。10ヶ月でそれだけの個数のアクセスポイントに遭遇していたのだとすると、ちょっと驚き。

2011/04/19

閲覧制限したBloggerブログのフィードを公開する方法(改)

以前書いた記事の方法ではあまりにややこしかったので、
もうちょっとだけシンプルな方法を用意してみた。

まず、目的は前回の記事と同じ。
Bloggerには閲覧者を制限する機能があって、プライベートなブログを作るには便利。なんだけど、ちょっとした不便がある。それは『閲覧制限をかけると記事やコメントのフィードが生成されない』というもの。ちょっと古いけど、こんな関連記事もチラホラ見かけられる。本文は特定の人以外に公開したくないけど、記事のタイトルやコメントが付いたことくらいはフィードリーダーで確認できたら便利だなぁ、という人、少しくらいはいそうだ。というより、僕が便利だ!w
Bloggerで閲覧制限したブログのフィードを公開する方法 - cyclic landscape

というわけで、今回用意したのはコレ。
Blogger Feed Creator


使い方は以下の通り。
  1. まず、Blogger Feed Creator ページ右上の"login"をクリックしてGoogleアカウントでログイン。Blogger Feed Creatorを初めて使う場合、Blogger Feed CreatorがBloggerへアクセスすることを許可するかどうか確認するためのページが表示される。内容を了解出来れば"アクセスを許可"。
  2. ログインしたGoogleアカウントで閲覧可能なBloggerブログの一覧が表示される。閲覧制限をかけたブログも一覧に表示されるはずなので、フィードを公開したいもののラジオボタンを"ON"に。
  3. これで、"posts"のURLをブログ記事のフィード(タイトルのみ)、"comments"のURLをコメントのフィード(コメント本文の先頭20文字のみ)として利用できる。
    ちなみに、Googleリーダ(などのPubSubHubbub対応フィードリーダ)でフィードを読む人は、一覧に表示されているメールアドレスを、Bloggerの更新通知先(ダッシュボードの「設定→メールとモバイル」と「設定→コメント」に設定項目あり)として登録しておくと、フィードの更新が早くなって便利。


今回は、slim3を使って作りました。
慣れてくると手放せません。

2010/03/22

Bloggerで閲覧制限したブログのフィードを公開する方法

この記事より簡単な方法を準備してみました。[2011.04.19]
閲覧制限したBloggerブログのフィードを公開する方法(改)


Bloggerには閲覧者を制限する機能があって、プライベートなブログを作るには便利。なんだけど、ちょっとした不便がある。それは『閲覧制限をかけると記事やコメントのフィードが生成されない』というもの。ちょっと古いけど、こんな関連記事もチラホラ見かけられる。本文は特定の人以外に公開したくないけど、記事のタイトルやコメントが付いたことくらいはフィードリーダーで確認できたら便利だなぁ、という人、少しくらいはいそうだ。というより、僕が便利だ!w

というわけで、方法を用意してみた。

やり方は以下の通り。
  1. hook2feedにGoogleアカウントでログインして「create new setting」する。

    「Feed Title」にはブログ名を、「URL」にはブログURLを、がオススメ。
  2. bmail2hookにGoogleアカウントでログインして「create new setting」する。

    「hook point URL」には、hook2feedで生成された「hook point URL」を入力すること。「Trim body?」で、本文をフィードにどれだけ載せるか、を設定できる。まったく載せたくなければ、「yes」「max length = 0」とすればよい。
  3. Bloggerの設定画面の「BlogSendアドレス」に、bmail2hookで生成されたメールアドレスを追加する。
  4. hook2feedで生成された「feed URL」を、使っているフィードリーダに追加する。
これで、設定したブログに記事を追加するとフィードが更新されるようになるので、閲覧制限したブログでも更新情報をフィードリーダで読めるようになると思う。

また、コメントの追加をフィードで知りたければ、同様の手順で作ったメールアドレスをBlogger設定画面の「コメント」→「コメント通知メール」という項目に追加すればいい。

というわけで、ちょっとややこしいけど、僕自身は便利に使えてるので満足ですっ!

2010/03/19

Bloggerの通知メールをWebHookに変換するWEBサービスを作ってみた

hook2feedと同様、これもまた「とある目的」のためにGAE for Javaで作ってみた。

bmail2hook - blogger's notification e-mails to web hooks

Googleアカウントでログイン後、WebHookの送信先URLを指定して「create new setting」すると、専用のメールアドレスが作成される。作成されたメールアドレスは以下のようにして使える。
  • 作成されたメールアドレスを、Bloggerの設定画面で新規記事や新規コメントの通知メール宛先に設定する
  • bmail2hookは、受信した通知メールから、記事のタイトル・URL・本文(の一部)を抽出して、指定したURLに対してWebHookする
これもまたhook2feedと同じく、「とある目的」以外の用途が思い付かない。困った。。。無理やり例を挙げるとするならば、、、HookHubとか経由してtwitterへ転送すれば、リアルタイムなtwitterへのブログ更新通知のできあがり、なんて如何?

ちなみに、bmail2hookとhook2feedで実現したかった「とある目的」については別記事にて。

2010/03/18

WebHookを溜めてRSSフィードを生成するWEBサービスを作ってみた

とある目的(については後日改めて)で使いたくて、GAE for Javaのお勉強がてら作ってみた。WebHooksの意義を考えると本末転倒な感じもするけど、深くは考えないことにする。。。

hook2feed - create feeds from web hooks

Googleアカウントでログインして「create new setting」すると、専用の2つのURL(「hook point URL」と「feed URL」)が作成される。hook2feedは、、、
  • 「hook point URL」の方に来たWebHook(HTTP POST)の履歴(最新20件分)を溜める
  • 「feed URL」の方で、WebHook履歴からRSSフィードを生成する
というように動く。
ちなみに、PubSubHubbubに一応対応していて、WebHookされたらhubにpublishするようになってる。PubSubHubbub対応のフィードリーダ(livedoorリーダなど)で見ると、リアルタイム性を体感できるはず。

さて、「とある目的」以外でどんな用途があるか、我ながらよく分からないけど、ブラウザで閲覧中のサイトタイトル&URLとダイアログボックスで入力したメモを「hook point URL」にWebHookしてフィード化するbookmarkletを作ってみた。以下のテキストボックスに「hook point URL」の末尾10桁のIDを入力してボタンを押すと、bookmarkletとして使えるリンクができる。
「hook point URL」のID(10桁) :
気付けば、まんま「あとで読む」なんだけど、役に立つシーンはあるかしら。他にも、HookHubを使って色んなWebHookをフィード化してみると、うれしいシーンが見つかる、かもしれない。

2010/03/06

GAE for Javaでquoted-printableなメールの受信

Google App Engine for Javaでメールを受信する処理を作ってたら、quoted-printableエンコーディングされたメール本文をデコードすると、デコード結果が途中で切れてしまう現象に遭遇した。調べてみると、quoted-printableエンコーディングでいうところの「Soft Line Breaks」がうまく認識されないことが原因だったので対策してみた。

まず、「Soft Line Breaks」については、RFCのquoted-printableエンコーディング規則の中で以下のように説明されている。要は、長すぎる行には適宜「=」+「改行」を入れて分割する、というエンコーディングが行われるとのこと。
Rule #5 (Soft Line Breaks):
The Quoted-Printable encoding REQUIRES that encoded lines be no more than 76 characters long. If longer lines are to be encoded with the Quoted-Printable encoding, 'soft' line breaks must be used. An equal sign as the last character on a encoded line indicates such a non-significant ('soft') line break in the encoded text.
RFC 1521: MIME Part One
RFCにはこう書いてあるのに、試してみると、JavaMail 1.4のMimeUtility.decode()でも、Commons Codec 1.4のQuotedPrintableCodec.decode()でも、Soft Line Breaksが正しく処理されず、最初のSoft Line Breakのところまででデコード結果が切れてしまう。で、なんでかなぁと思ってQuotedPrintableCodecのドキュメントをちゃんと読んでみると、きちんと明記されていた。
Note:
Rules #3, #4, and #5 of the quoted-printable spec are not implemented yet because the complete quoted-printable spec does not lend itself well into the byte[] oriented codec framework.
QuotedPrintableCodec (Commons Codec 1.4 API)
というわけで、自分でSoft Line Breaksを取り除いてやる必要がある。MimeMessageから取り出したMimeBodyPartをデコードする処理は、例えばこんな感じ。効率の悪いコードだけど、今のところ問題なく動いているようだ。
private String _decodeBody(MimeBodyPart bp)
{
  // parse header
  String contentType = null;
  String contentEncoding = null;
  String charset = null;
  try{
    contentType = bp.getContentType();
    contentEncoding = bp.getEncoding();
  }catch(MessagingException e){
  }
  String[] elems = contentType.split(";");
  for(String elem : elems){
    if(elem.trim().startsWith("charset=")){
      charset = elem.trim().substring("charset=".length());
    }
  }
  if(charset!=null){
    if(charset.startsWith("\"")) charset = charset.substring(1);
    if(charset.endsWith("\"")) charset = charset.substring(0, charset.length()-1);
  }
  // get inputstream
  InputStream in = null;
  try{
    in = bp.getRawInputStream();
  }catch(MessagingException e){
  }
  if(in==null) return "";
  // convert quoted-printable
  if(contentEncoding!=null && contentEncoding.equals("quoted-printable")){
    ByteArrayOutputStream baos = new ByteArrayOutputStream();
    int len;
    byte[] buffer = new byte[1024];
    try{
      while( (len=in.read(buffer, 0, buffer.length)) != -1 ){
        baos.write(buffer, 0, len);
      }
    }catch(IOException e){
    }
    byte[] b = baos.toByteArray();
    baos = new ByteArrayOutputStream();
    for(int j=0;j<b.length;j++){
      if(b[j]=='=' && j<b.length-1 && b[j+1]=='\n'){
        j++;
      }else{
        baos.write(b[j]);
      }
    }
    b = baos.toByteArray();
    in = new ByteArrayInputStream(b);
  }
  // decode
  if(in!=null && contentEncoding!=null){
    try{
      in = MimeUtility.decode(in, contentEncoding);
    }catch(MessagingException e){
    }
  }
  if(in==null) return "";
  // read body
  Reader r = null;
  if(charset!=null){
    try{
      r = new InputStreamReader(in, charset);
    }catch(UnsupportedEncodingException e){
    }
  }else{
    r = new InputStreamReader(in);
  }
  StringBuffer sb = new StringBuffer();
  BufferedReader br = null;
  try{
    br = new BufferedReader(r);
    String line = null;
    while( (line=br.readLine())!=null ){
      sb.append(line.trim());
      sb.append("\n");
    }
  }catch(IOException e){
  }finally{
    if(br!=null){
      try{
        br.close();
      }catch(IOException e){}
    }
  }
  return sb.toString();
}

2009/10/13

Everioの動画をMPEG4にお手軽変換できるワークフロー

動画撮影にはHDDビデオカメラのビクターEverio(GZ-MG77)を使っているのだが、これで撮れるMPEG2形式の動画ファイル、実はMacではそのまま扱えない。QuickTimeで再生できないし、当然、iMovieで使う事もできない。Apple純正のQuickTime用「MPEG2再生コンポーネント」なるものはあるが、EverioのMPEG2ファイルに関しては再生ができても音が出ないそうだ。Everioの音声コーデックに非対応ならしい。

というわけで、WindowsからMacに乗り換えて数ヶ月、動画を撮り貯めるばかりで、編集する事はもちろん、再生する事すらもできていなかったのだが、ようやく最近になって、このEverioの動画ファイルを扱える環境をMac上に整えた。と言っても、再生のためにVLCを、iMovieで読み込めるMPEG4への変換のためにffmpegXを、それぞれインストールしただけ。こんなに簡単ならもっと早く使い始めておけばよかった。

これらのツール、対応する動画/音声のフォーマットが豊富で、UIはシンプルで使いやすく、とにかく素晴らしく便利。そこで今回は、便利ついでにAutomatorでワークフローを作ってみた。
Automator画面
このワークフローでは、ffmpegXのアプリパッケージに含まれているffmpegというコマンドを利用している(っていうか、そもそもffmpegXはffmpegのGUIラッパーなので、ffmpegこそが本体なんだけど)。「選択されたFinder項目を取得」の後ろに、ffmpegを実行するための「シェルスクリプトを実行」をつないで、Finder用のプラグインとして保存。Finderで選択した動画ファイルをMPEG4に自動変換してくれる。
ちなみに、シェルスクリプトの中身を書き下すと以下のような感じ。
for f in "$@"
do
/Applications/ffmpegX.app/Contents/Resources/ffmpeg -i "$f" \
-vcodec mpeg4 -s 640x480 -aspect 4:3 -r 29.97 -b 4000k -qmin 2 -qmax 9 \
-acodec aac -ab 96k "$f".mp4
done
ffmpegにはかなり豊富なコマンドオプションが用意されているので、特に出力の各種パラメータについては色々とカスタマイズの余地がありそうだが、これで変換がお手軽にできるようになった。

ワークフローを作るのは初めてだったが、ちょー便利だな、これ。


[2010/03/22追記]
ちなみに、ワイド(16:9)で撮った動画を変換するために、
for f in "$@"
do
/Applications/ffmpegX.app/Contents/Resources/ffmpeg -i "$f" \
-vcodec mpeg4 -s 720x405 -aspect 16:9 -r 29.97 -b 4000k -qmin 2 -qmax 9 \
-acodec aac -ab 96k "$f".mp4
done
と、スクリプトの縦横比関連の指定("-s"と"-aspect")を変えたものも別途用意して使っている。