LogiClover開発ブログ

LogiCloverは、趣味でFPGAを使った電子工作をしているサークルです。主に開発中の出来事や技術メモを投稿します

Raspberry Pi Zero Wに洗濯機を監視させる

f:id:logiclover:20181028004350j:plain

最近洗濯乾燥機を購入したのですが、乾燥の度合いによって残り時間が前後し表示されている数字があまり当てにならないため監視アプリケーションを作成しました。

設置

部材

すべてオンラインショップで調達

  • Raspberry Pi Zero W
    • コスパ最強、無線付き、最高では
  • Logicool Webcam C270
    • Amazonで安かった
    • CSI Cameraを使うとraspi専用アプリ化するのが嫌
  • 電源タップ
  • 3M コマンドタブ
    • 壁に貼ってはがせるやつ

設置

3Mのコマンドタブが最高すぎるので、すごく適当にひとまず固定します。

f:id:logiclover:20181028004436j:plain

RasPi 準備

OSを焼く

公式サイトからOSを落とします。GUIとかいらないのでRASPBIAN STRETCH LITEを選択。 ダウンロード後ddでSDカードにそのまま書き込みます

www.raspberrypi.org

sshを有効化

どうやら最新版はsshが無効化されているらしいのでルートにsshという名前でからファイルを作成。

無線LAN設定

初回起動後に無線設定をするのが面倒なので調べていたら(microHDMI+microUSBとか) 素晴らしいツールを作られている方がいたので使用します。

github.com

作成したwpa_supplicant.confをSDカード直下に配置

起動

無事立ち上がってssh経由でアクセスできることを確認します。また、すぐにパスワードを変えます。

実装

細かい話をするとgcfとかvision apiとかtesserct-ocrとか色々遠回りしたのですが、必要な箇所だけ書きます。

Pythonでやってもよかったのですが、気分でnode.jsを採用。

github.com

Webカメラの制御

opencvを使うか他の手法で悩みましたが、fswebcamを利用したnode-webcamを利用。 NodeWebcam.captureだけで写真を取って保存できるので最高。

ただし、awaitして使うと写真が保存されていないタイミングがあるっぽい挙動が見えたため以下のように実装。

const onCapture = (opts) => {
    return new Promise((resolve, reject) => {
        const identify = "capture"
        const filename = `${identify}.jpg`;
        const filepath = path.resolve(__dirname, filename);

        try {
            if (fs.existsSync(filepath)) {
                fs.unlinkSync(filepath);
            }
            NodeWebcam.capture(identify, opts, (err, result) => {
                // ファイル存在確認
                if (!fs.existsSync(filepath)) {
                    reject("capture failure");
                    return;
                }
                console.log('captured!')
                resolve(filename);
            });
        } catch (e) {
            console.error(e);
            reject(e);
        }
    });
};

optsは解像度やフォーマット等の書式設定、configに逃がしてある。

Slackに画像アップロード

非常に残念なことにデータのアップはWebhookではできないため、File Upload APIを利用。*1 面倒なのでyarn add @slack/clientを利用する。

        const web = new WebClient(slackToken);
        const result =
            await web.files.upload({
                filename: filename,
                file: fs.createReadStream(filepath),
                title: filename,
                channels: slackChannel,
                initial_comment: comment
            });
        if (result.ok) {
            console.log(`slack post! ${result.file.permalink}`);
        } else {
            console.error('slack post error', result.error);
        }

slackTokenにはSlack管理画面で取得したトークンを入れ、slackChannelには投稿したいチャンネルのIDを設定します。*2

定期実行

node-cronを使用。

    let count = 0;
    new CronJob(cronTime, async () => {
        console.log(`Job Start #${count++}`)
        const filename = await onCapture(cameraOption);
        const result = await onPost(filename, slackWebhookUrl, slackToken, slackChannel, slackComment);
        console.log('Done');
    }, null, true);

Dockerfile作成

fswebcamとかnode versionに依存すると最悪なので作成。

コンテナ内から実デバイス等いじるときは--privilegedするかデバイスを指定する必要があるらしい。

結果

うざいぐらい定期的にSlackに洗濯機写真が流せるようになりました。

f:id:logiclover:20181028004326j:plain

まとめ

最初は7segフォントのOCRするつもりでしたが、作っていてこれで要件が完全に満たせているのでこれで運用してみます。

*1:アップされているもののリンクを貼ってそれっぽく見せることはできる

*2:Webで開いてURL見るとわかります