【ラズパイ】こたつのスイッチを制御してみた

皆さんはこたつの消し忘れをしたことはないでしょうか?

私はとてもあります。

外出中、あれ?こたつの電源消したっけ?と不安になってしまいます。

外出先、スマホでこたつの電源をON、OFFできたらなということで

RaspberryPi(ラズパイ)とサーボモータを利用して、スマホから操作したいと思います。

開発環境

以下の流れで開発します。

スマホからのAPIをラズパイに送るため LINE Messaging API を使います。

ラズパイの中では、Node.jsで書かれたプログラムが動き、サーボモータを動かします。

サーボモータが動くことでコンセントのスイッチを切り替えることができるため、こたつをコンセントにつなげて、

こたつの電源を切り替えます。

このままでは、外出時、スマホとラズパイのやりとりができないので、ngrokというサービスを使って、外出先から操作できるようにします。

それでは、実装にあたり具体的な方法を紹介します。

事前準備

以下のものを用意します。

必ず必要というわけではなく、私はこれで実装したものを紹介します。

・RaspberryPi(ラズパイ)

【国内正規代理店品】Raspberry Pi4 ModelB 4GB ラズベリーパイ4 技適対応品【RS・OKdo版】

小型PCです。

・サーボモータ
Smraza デジタルRCサーボモーター 20kg高トルク 防水 サーボモデル メタルギア 防滴サーボ 1/10 1/8RCカー/オフロード車/ラジコンロボットカー用 (270°制御)

コンセントのスイッチを押す役割のサーボモータを準備します。

・スイッチ付きコンセント

ヤザワコーポレーション 省エネタップ 1個口 白 Y02F110WH 1個

サーボモータで制御するにあたりスイッチ付きのコンセントを用意します。

・デッキポストタイ

SIMPSON デッキポストタイ

サーボモータとコンセントを固定するために使いました。

本来は、柱をデッキの側根太または階段の桁に取り付けるコネクターだそうです。

ネットでも売っているのですが、明らかに高いのでホームセンターで買った方がよいです。

・木材

池川木材 もくレンガ 杉 木製 日本製 ナチュラル 約20×10×5cm

サーボモータとコンセントのスイッチと間を埋めるために使いました。

RaspberryPi内の作業

MessagingAPIからチャネルアクセストークンとチャンネルシークレットの取得

スマホで操作するにあたり、LINE MessagingAPIを使いました。

チャネルアクセストークンとチャネルシークレットを取得します。

https://account.line.biz/login?redirectUri=https%3A%2F%2Fdevelopers.line.biz%2Fconsole%2F

でLINEのアカウントを使って、コンソールにログインし、プロバイダーを作成します。

チャネル設定からMessagingAPIを選択します。

必要事項を書き、作成を押下します。

  • チャネルの種類 MessagingAPI
  • 会社・事業者の所在国・地域  日本
  • チャネル名 こたつ確認
  • チャネル説明 こたつの電源を確認します
  • 大業種 個人
  • 小業種 個人(IT・コンピュータ)

作成したチャネル設定からチャネル基本設定からチャネルシークレットをメモします。

Messaging API設定のタブを開き、チャネルアクセストークンをメモします。

RaspberryPiの作業ディレクトリにチャネルアクセストークンとチャネルシークレットを登録します。

.envファイルに以下のように記述します。

CHANNEL_SECRET=取得したチャネルシークレット
CHANNEL_ACCESS_TOKEN=取得したチャネルアクセストークン

Node.jsのインストール

今回はNode.jsでプログラムを書くため、Node.jsをインストールします。

Node.jsのバージョン管理をするため、nvm をインストールします。

$ curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.35.0/install.sh | bash

nvmが使えるか確認します。以下のようにバージョンが返ってくれば、使える状態です。

$ nvm --version
0.35.0

Node.jsをインストールします。(例:バージョン14)

$ nvm install v14

インストールが完了するとNode.jsが使えるようになります。

$ node -v
v14.0.0

スクリプト

作業ディレクトリで、次のコマンドを実行し開発環境を初期化します。

$ node init

必要なモジュールをインストールします。

$ npm install @line/bot-sdk axios dotenv express n obniz pigpio

サーボモータを制御するスクリプトを書きます。

require('dotenv').config();
const express = require('express');
const Gpio = require('pigpio').Gpio;
const line = require('@line/bot-sdk');
const app = express();
const port = 3000;

app.use(express.json());

const config = {
  channelAccessToken: process.env.CHANNEL_ACCESS_TOKEN,
  channelSecret: process.env.CHANNEL_SECRET
};

const client = new line.Client(config);
let onflg = false; // OFF

app.post('/moveServo',(req, res) => {
  Promise
    .all(req.body.events.map(handleEvent))
    .then((result) => res.json(result))
    .catch((err) => {
      console.error(err);
      res.status(500).end();
  });
});

function handleEvent(event) {
  const result = (event) => {
    if (event.type === 'message' && event.message.type === 'text') {
      switch (event.message.text) {
      case 'ON':
        onflg = true;
        return 105;
      case 'OFF':
        onflg = false;
        return 130;
      case 'こたつ確認':
        if (onflg) {
          return 'こたつの電源はONです';
        } else {
          return 'こたつの電源はOFFです';
        }
      }
    }
  }
  const resultValue = result(event);
  if (typeof resultValue === 'number') {
    moveServo(result);
  } else if (typeof resultValue === 'string') {
    const replyToken = event.replyToken;
    const response = {
      type: 'text',
      text: resultValue.toString()
    };
    return cline.replyMessage(replyToken, response);
  }
  return Promise.resolve(null);
};

const moveServo = (angle) => {
  const servoPin = 18; // GPIOピン番号
  const servo = new Gpio(servoPin, { mode: Gpio.OUTPUT });
  const servoMin = 500;
  const servoMax = 2500;
  const pulseWidth = Math.floor((angle / 180) * (servoMax - servoMin) + servoMin);
  servo.servoWrite(pulseWidth); 

  setTime(() => {
    servo.servoWrite(0);
    servo.digitalWrite(0);
    servo.pwmWrite(0);
    servo.hardwarePwmWrite(0, 0, 0);
  }, 1000);
};

app.listen(port, () => {
  console.log(`Server running on port ${port}`);
});

ngrokをインストール

https://dashboard.ngrok.com/でngrokを利用するため、アカウントを作成します。

https://dashboard.ngrok.com/get-started/your-authtokenで認証トークンをメモしておきます。

ローカルのサーバを外部公開するためにngrokをインストールします。

$ wget https://bin.equinox.io/c/bNyj1mQVY4c/ngrok-v3-stable-linux-arm64.tgz
$ sudo tar xvzf ./ngrok-v3-stable-linux-arm64.tgz -C /usr/local/bin

アカウント作成時にメモした認証トークンを登録します。

$ ngrok authtoken 認証トークン

以下のコマンドを実行します。

$ ngrok http 3000

これでポート番号3000でサーバを立てることができます。

表示されている、Forwarding のURLをメモしておきます。

Session Status             online
Account                    test(Plan: Free)
Update                     None
Version                    3.5.0
Region            Japan (JP)
Latency                    41ms
Web Interface              http://127.0.0.1:4040
Forwarding                 メモするURL -> http://localhost:3000

Connections               ttl   opn  rt1   rt5   p50   p90
                          25    0    0.00  0.00  5.19  8.31

MessageAPIのWebhook設定

https://developers.line.biz/console/でLINE Developersのコンソールにログインして、作成したチャネルのMessaging API設定のタブを開きます。

Webhook設定の項目のWebhook URLの項目にngrokでメモしたURLを入力します。
ngrokのURL/moveServo

また、Webhookの利用をONにしておきます。

リッチメニュー生成

スマホで操作するにあたり、LINEのリッチメニューを作成します。

https://manager.line.biz/から作成したアカウントにアクセスします。

左のリッチメニューからリッチメニュー作成を押下します。

表示設定に必要な情報を設定します。

テンプレートを選択します。

メニューで使用する画像は、新たに作りました。(下のボタン)

それぞれのボタンでどのような値をRaspberryPiへ送信するか設定します。

保存した際、リッチメニュー画面で以下のように表示されていれば、リッチメニュー生成完了です。

サーボモータ接続

サーボモータをRaspberryPiに接続します。

こちらの記事を参考にサーボモータをRaspberryPiに接続してください。

角度調整

スマホを使って、サーボモータを動かしてみて、コンセントのスイッチを変更できる角度を探ります。

適時、スクリプトの値を変えましょう。

34行目retrun 105;と37行目retrun 130;

サーボモータとコンセントを固定

サーボモータとコンセントをデッキポストタイを使って以下の画像のように固定します。

サーボモータとコンセントをスイッチが接触できるように木材を切り出し、

サーボモータの先端の穴を使い釘に刺します。

テスト

以下が動作テストしたものとなります。