工作と競馬

電子工作、プログラミング、木工といった工作の記録記事、競馬に関する考察記事を掲載するブログ

タグ:赤外線リモコン

概要

Google HomeとIFTTTとIRKitで家電操作ができるか試し、無事動作することを確認した。


背景と目的

先日発売されたばかりのGoogle Homeを入手したが、残念ながら私の家には対応家電が1つもない。しかし、私の家にはIRKitがあり家電操作ができるようになっている。そこで、Google Homeの音声コマンドを活用し、家電操作をできるようにしてみる。


詳細

0.方法の検討

  • Google Homeは、IFTTTのGoogle Assistantチャンネルと連携し、音声コマンドをIFTTTのトリガとして利用できる
  • IRKitは、インターネット経由で赤外線発信を制御できる(IRKit Internet HTTP API)
  • IFTTTには、任意のURLにWebhookを送信するWebhookチャンネル(これは、少し前までMakerというチャンネルのアクションだった気がするが・・・名前変わった???)がある

したがって、

  • Google Assistantチャンネルをトリガーとし、音声コマンドでトリガーが発行されるように設定する
  • Webhookチャンネルをアクションとし、IRKit Internet HTTP APIにWebhookを送信する

となるAppletを作れば、家電操作ができそうだ。


1.IFTTTの設定

1.1 Google Assistantチャンネル

トリガーとしてGoogle Assistantチャンネルのトリガのうち、今回は一番左の"say a simple phrase"を選択する。

20171008134847

そして、下図のように音声コマンドと、応答音声、言語の設定をしてCreate triggerを押せば、設定完了。

20171008134836

1.2 Webhookチャンネル

次に、アクションとしてWebhookチャンネルのMake a web requestを選ぶ。

20171008135300

今回は、操作対象をテレビの電源とするため、あらかじめ読み取り済みのコード情報をBodyとして設定。なお、Content-TypeやBodyの記入の仕方はこちらを参照するとよい。

request


2.動作テスト

Google Homeに対して、"OK Google,テレビを消して"と呼びかけたところ、"わかりました"と応答音声が発せられ、テレビの電源が消えた!ちゃんと動いた!


まとめ

とりあえず自前の環境で既存の家電操作ができるようになった。今後、Googleから赤外線リモコンを学習できるデバイスも発売されるとのことだが、まずはこんな感じで音声操作ができることが確かめられてよかった。

概要

 当初の目的であるエアコンの電源を出先のスマホから操作可能となり、システムが完成した。


背景と目的

 前回までで、AWS上のLambdaからRaspberry Piへトピックをパブリッシュしてエアコンの電源を操作することができた。今回は、いよいよスマートフォン等から操作可能にし、システムを完成させる。


詳細

1.方法

 システムを完成させるには、

  • AWSのAPI Gatewayにて、操作情報を受け取りとLambdaの駆動をするWebエンドポイントを用意する
  • スマホ側の操作インターフェースとして、電源ボタン操作インターフェースとWebエンドポイントへの操作情報を送信機能を持つWebアプリを作成する

 が必要。それぞれ順にやっていく。

2.Webエンドポイントの作成

 API Gatewayにて、POSTメソッドを用意し、統合タイプを前回作成したLambda関数呼び出しとする。パラメータは、何でもよいが、Webアプリ側の実装が簡単なJSONでの受け渡しになるようにした。

3.操作用Webアプリ

 図3.1、3.2のように、電源ボタンの図をタップすると、ON/OFFが切り替わり、それに応じて2で作成したエンドポイントへリクエストを送るようになっている。

IMG_3363

 図3.1 OFF状態

IMG_3368

 図3.2 ON状態

4.動作確認

 最後に、動作確認を行った。スマホで、操作用Webアプリを開き、電源ボタンを押したところ、1秒程度で無事エアコンの電源が入った!結構素早い動きで驚いた。


 以上で、エアコンの電源をインターネット経由で操作することができるようになった。作業に要したのはおよそ2日だったが、出来上がったときはさすがにちょっと感動した。作ってよかった。

概要

 Raspberry PiがAWSから操作情報を受け取るためのプログラムの実装と、LambdaからRaspberry PiでON/OFF操作情報を送信するテストを行った。


背景と目的

 前回までで、疑似赤外線リモコンが完成した。今回は、Rapsberry PiがAWSから操作情報を受け取って疑似赤外線リモコンへ送信指示を出せるようにする。


詳細

1.方法

 AWSとRaspberry Piを連携させるには、AWS IoTを使う。操作情報を含むトピックをRaspberry Piがサブスクライブすればよさそう。

2.AWS IoTのコンソールでデバイスの作成

 デバイス作成は、AWS IoT上のコンソールのウイザードに従ってやった。コンソールのconnectから、Configuring a deviceのGet startedをクリックし、プラットフォームとSDKを選択する。プラットフォームはRaspbianなのでLinuxを選択。SDKは、いつの間にやらPythonができているのでPythonを選択。

SDK選択
図2.2 プラットフォームとSDKの画面

 次に、Thingの名前を決めてNext Stepに進むと、Download connect kitというものダウンロードボタンが現れるので、ダウンロード。

connection_kit
図2.3 connect kitのダウンロード画面

 次に、コマンドの指示がでるので、ダウンロードしたzipファイルをFTPでRaspberry Piに転送し、コマンドを実行。Start.shがうまく動けば、以下のようにコンソールに表示が出る。同時に、図2.5のようにAWSのコンソールにも表示が出る。

test
図2.4 コマンドを実行させた様子

command2
図2.5 通信がうまくいってるときのコンソール

 ここまでで、Raspberry PiとAWS IoTの接続ができた。

3.Raspberry Piで、トピックをサブスクライブする

 以下のリンク等を参考に、トピックをサブスクライブするためのコードを作成。

 最初、END_POINTというパラメータは何を指定すればいいか分からなかったが、こちらを見て、InteractのHTTPSという欄にあるRest API EndpointでOKだということが分かった。

# Import SDK packages
from AWSIoTPythonSDK.MQTTLib import AWSIoTMQTTClient

CLIENT_ID = "Thing名"
END_POINT = "xxx.iot.リージョン名.amazonaws.com"
PATH_CA = "配置先ディレクトリ/root-CA.crt"
PATH_KEY = "配置先ディレクトリ/Thing名.private.key"
PATH_CRT = "配置先ディレクトリ/Thing名.cert.pem"

# For certificate based connection
myMQTTClient = AWSIoTMQTTClient(CLIENT_ID)

# Configurations
myMQTTClient.configureEndpoint(END_POINT, 8883)
myMQTTClient.configureCredentials(PATH_CA, PATH_KEY, PATH_CRT)

# 接続
myMQTTClient.connect()

# サブスクライブ開始
myMQTTClient.subscribe("$aws/things/Thing名/shadow/update", 0, subscribe_callback)

# 受信待ち
while True:
    time.sleep(1)

 そして、subscribeメソッドの引数に渡したコールバック関数は、トピックが到着したときの処理を書けばよい。ここでは、詳細は省略するが、到着したペイロードの中に、ON/OFF操作情報があるとして、それに応じてGPIO18にパルスを送るようにしている。

def subscribe_callback(client, userdata, message):
    # データを受け取る
    payload = json.loads(message.payload)
    # payloadの内容に応じて、GPIO18にパルスを送る
    :
    :
4.Lambdaによる確認

 最後に、LambdaでAWS IoTのThing Shadowに、トピックをパブリッシュしてみた。実装は、boto3を使った。参考になったサイトは以下。payloadという辞書に、送信したいデータを入れる。

# coding: utf-8
import boto3
import json

iot = boto3.client("iot-data", リージョン名)

def lambda_handler(event, context):

    # 送信データを作成
    payload = {
        "state": {
            "desired": {
                キー : 値,
                :
            },
            "reported": {
                キー : 値,
                :
            }
        }
    }
    
    # パブリッシュする
    iot.publish(
        topic='$aws/things/Thing名/shadow/update',
        qos=0,
        payload=json.dumps(payload)
    )

    # TODO implement
    return {}

 この結果、正しくトピックが受信され、無事エアコンがON/OFFできた!

 というわけで、AWS IoTとRaspberry Piが接続され、操作情報を載せたトピックをパブリッシュすればエアコンの電源がON/OFFできるところまできた。次回は、最終段階であるAPI Gateway⇒Lambda部分を構築し、スマホの操作を完成させる。

概要

 疑似赤外線リモコンのソフトウェアコーディングと動作確認を行った。


背景と目的

 前回に続き、疑似赤外線リモコンのソフトウェアコーディングと動作確認を行う。


詳細

1.概要
  • TrinketというArduino互換マイコンを使い、GPIOから赤外LEDを駆動する。そのため、コーディングはArduino言語。Arduino開発環境セットアップは、以前行ったのでOK。
  • 私の家のエアコンのリモコンコードは、手持ちの赤外線リモコン受信モジュールとオシロスコープを使って調べたところ、家電協会フォーマットのようだ。(フォーマットは、こちらが詳しい。)
2.疑似赤外線リモコン ソフトウェアのコーディング

 まず、38kHzのサブキャリアとなるパルスを出力する関数。

// 38kHz, デューティ1/3のパルスを出力
// delayMicrosecondsの精度がいまいちなので26usになっていない
void output_pulse(){

  digitalWrite(PIN_DRIVE, HIGH);
  delayMicroseconds(4);
  digitalWrite(PIN_DRIVE, LOW);
  delayMicroseconds(7);
  
}

 次に、データ1ビット分を出力する関数。

// データビット1つを出力する
void output_databit(int high_or_low){

  int i;

  // HIGH期間
  for (i = 0; i < DATABIT_PULSE_COUNT; i++){
    output_pulse();
  }

  // LOW期間
  if(high_or_low){
    delayMicroseconds(DATABIT_PULSE_T * DATABIT_PULSE_COUNT * 3); // 3周期
  } else {
    delayMicroseconds(DATABIT_PULSE_T * DATABIT_PULSE_COUNT); // 1周期
  }
  
}

 リモコンコード先頭のリーダー部を出力する関数。

// リーダーを出力
void output_leader(){

  int i;
  
  // HIGH期間
  for (i = 0; i < DATABIT_PULSE_COUNT * 8; i++){
    output_pulse(); // 8周期 * 8T
  }

  // LOW期間
  delayMicroseconds(DATABIT_PULSE_T * DATABIT_PULSE_COUNT * 4); // 4T
}

 1バイト分のデータを出力する関数。

// データを出力
void output_data(byte databits, int bits_len){

  // 上位ビットからbits_len個を出力
  int i;
  for (i = 0; i < bits_len; i++){
    int x = (databits & B10000000) >> 7; // 最上位を取り出す
    output_databit(x);
    databits = databits << 1; // 1ビットシフト
  }
  
}

 リモコンコード1フレーム分を出力する関数。

// フレームを出力
// customercode = { 上位8bit, 下位8bit };
// parity = パリティ4bit + 0000;
// data_bytes = { data0, data1, ... };
void output_aeha_frame(byte customercode[], byte parity, byte data_bytes[], int data_len){

  int i, k;

  // リーダー
  output_leader();

  // カスタマーコード
  output_data(customercode[0], 8);
  output_data(customercode[1], 8);
  
  // パリティ
  output_data(parity, 4);

  // データ
  for(i = 0; i < data_len; i++){
    if (i == 0){
      k = 4;
    } else {
      k = 8;
    }
    output_data(data_bytes[i], k);
  }

  // ストップビット
  output_data(B10000000, 1);
  
}

 入力を検出する関数が以下。これは、Raspberry Piからの送信指示がGPIO#0に来たかを検出する。パルス幅5msecならON、パルス幅10msecならOFF、それ以外は無視という単純な送信指示パルスなので、ノイズによる誤動作もなくはないが、問題になったら改めるとして、とりあえずこれにする。

// 入力を検出
// 戻り値 : ONのときは1, OFFのときは0, 検出なしは-1
int detect_input(){

  int pw;
  int result;

  // いきなりHIGHのときは検出なし
  if (digitalRead(PIN_INPUT) == HIGH){
    return -1;
  }

  // 開始検出
  while (digitalRead(PIN_INPUT) == LOW) {
    delay(1);
  }

  // パルス幅をカウント
  int i;
  pw = 0;
  for(i = 0; i < 15; i++){
    if(digitalRead(PIN_INPUT) == HIGH) {
      pw += 1;  
    }
    delay(2);
  }
  if(4 <= pw && pw <= 6) {
    result = 1; // 5msecのときON
  } else if (9 <= pw && pw <= 11){
    result = 0; // 10msecのときOFF
  }
  
  return result;
  
}

 定数と初期設定たち。

const int DATABIT_PULSE_T = 26; // 26us
const int DATABIT_PULSE_COUNT = 15; // 26us*15周期
const int PIN_DRIVE = 1; // LED駆動
const int PIN_INPUT = 0; // 送信指示信号入力

// 送信データ
byte CC_FUJITSU[] = { B00101000, B11000110 }; // カスタマーコード
byte data_on[] = { B00000000, B00001000, B00001000, B01111111, B10010000, B00001100, B10000010, B00100000, B00000000, B00000000, B00000000, B00000000, B00000100, B11010110 };
byte data_off[] = { B00000000, B00001000, B00001000, B01000000, B10111111 };

boolean on_off = true;
 
// the setup routine runs once when you press reset:
void setup() {
  pinMode(PIN_DRIVE, OUTPUT);
  pinMode(PIN_INPUT, INPUT);
}

 メインのループ

// the loop routine runs over and over again forever:
void loop() {

  int input;

  // 入力検出
  input = detect_input();
  if (input == 1) {
    output_aeha_frame(CC_FUJITSU, B00000000, data_on, 14);
  } else if (input == 0){
    output_aeha_frame(CC_FUJITSU, B00000000, data_off, 5);
  } else {
    delay(100);
  }
  
}
3.動作確認

 動作確認は、Raspberry PiのGPIO18から送信指示のパルスを出力することで行った。
その結果、送信指示を受け取り正しいリモコンコードが発出され、エアコンの電源がON/OFFできた。

 ということで、リモコン部分は完成。次は、AWS側の設計・製作に入る。


要旨

 iTunesをPC外部から操作する方法に関する記事。USB対応PICマイコンPIC18F14K50と赤外線リモコン、自作Windowsアプリケーションを組み合わせた方法を実験し操作することができた。


背景と目的

 iTunesはPC上で操作することが多いが、PCと音楽を聴く場所が離れている場合などにはPCの外部から操作したいことがある。それにはiPhoneアプリのremoteが便利だ。同一のLANでつながれたPCであれば操作できる。しかし、それは当然ながらiPhone/iPodユーザーしか使用できない。そこで、iPhone/iPodユーザー以外でもPC外部からiTunesを操作できないかと考えてみたところ、PICマイコンを用いて操作するシステムが構築できそうなことに気づいた。そこで、実際にシステムを検討し操作してみたい。


詳細
1.システム構成

 図1は今回考案したシステム構成である。これは赤外線リモコンと毎度おなじみUSB対応マイコンPIC18F14K50、およびiTunes操作用自作PCアプリケーションを用いている。原理としては、まずユーザーが赤外線リモコンを操作すると赤外線信号を受信した受光モジュールの出力信号がPICのRC4ポートに入力され、PICがUSBにてPCへ受信信号の内容を伝える。そしてそれを受けたPC上のアプリケーションは受信内容に応じてiTunesに操作コマンドを送信するという仕組みである。

VBE3
図1.1 iTunesをPC外部から操作するためのシステム構成案

2.システムの作製
2.1 赤外線リモコンと受光モジュール

 赤外線リモコンは、オーディオプレーヤーの操作ということを考え手持ちのAVアンプであるSC-LX73(Pioneer)のリモコンを使用することにした。また、赤外線受光モジュールは、PL-IRM0101-3を用いる。これは秋月電子などで100円程度で手に入る。なお、SC-LX73のリモコンコードはどうやらNECフォーマット相当のようなので、PICの解析プログラムはNECフォーマットにあわせて書けばよいことがわかった。

2.2 赤外線受光モジュールの出力をPICで0/1パターンに変換

 PICで赤外線リモコンの受信信号を解析する場合には、通常受信コードを8ビットごとに変数に格納することが多いが今回は最終的な制御対象がPCであることからPICでいちいち処理せずPC側に任せてしまえばよい。そこで、受信した信号の1/0だけを判断してそのつどシリアルに文字列として1/0を送信するようなプログラムにした。プログラムは詳しくは載せないが、簡単に構成を述べると、タイマ割り込みによってNECフォーマットの最小時間単位0.56msのおよそ1/5の周期でタイマ割り込みを行い、1⇒0または0⇒1に変化するタイミングをよみとり1/0パターンを解析した。

2.3 PICからPCへの受信内容の送信/PCでの受信方法

 PICとPC間の通信は、毎度おなじみUSBによる仮想シリアル通信を用い、2.2で解析した1/0のパターンを送信する。なおPICからのシリアル送信については本ブログでは何度も取り上げているので、ここでは割愛する。一方、PC側のシリアルの受信であるが今回は自作アプリにて処理を記述する必要がある。今回はVB2008にてシリアルを受信するプログラムを記述し、正しくシリアルが受信できることを確認した。このPC側シリアル受信処理についても、Webの各所で取り上げられているので本ブログでは割愛する。

2.4 iTunes操作用自作アプリ

 今回のキモであるiTunesを別アプリから操作する部分は、iTunes Type Libraryというオブジェクトライブラリを使用することにした。具体的には、今回のアプリ開発環境であるVisual Basic 2008で図2.11のようにプロジェクト>参照の追加からiTunes Type Libraryにチェックを入れる。これにより、図2.12のように変数の型としてiTunesAppが参照できるようになりiTunesの画面上の操作は一通りプログラム上で扱えることになった。そこで、再生、一時停止、停止、トラック送り、トラック戻しのコマンドをそれぞれ以下のように書いてみた。そして、実際にiTunesがコマンドに反応して動作していることも確認できた。以上で、外部アプリケーションからiTunesを操作できるようになった。

    'iTunesをVB2008で操作するコマンドの例
    Private Sub iTunesCommandSample()

        Dim MyiTunes1 As New iTunesLib.iTunesApp

        MyiTunes1.Play() '再生
        MyiTunes1.Pause() '一時停止
        MyiTunes1.Stop() '停止
        MyiTunes1.NextTrack() 'トラック送り
        MyiTunes1.BackTrack() 'トラック戻し

    End Sub

VBE
図2.11 iTunes Type Libraryの参照

VBE2
図2.12 iTunesApp型オブジェクト変数を宣言

2.5 プログラム全体

 詳細説明は省くが、今回PC側アプリとして作成したコードを以下に示す。

Public Class Form1

    'iTunesAppオブジェクト
    Dim MyiTunes As New iTunesLib.iTunesApp

    'フォームのロード
    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load

        'シリアルポートのオープン
        SerialPort1.PortName = "COM9"
        'シリアルポートの通信速度指定
        SerialPort1.BaudRate = 19200
        'シリアルポートのパリティ指定
        SerialPort1.Parity = System.IO.Ports.Parity.None
        'シリアルポートのビット数指定
        SerialPort1.DataBits = 8
        'シリアルポートのストップビット指定
        SerialPort1.StopBits = System.IO.Ports.StopBits.One
        'シリアルポートのオープン
        SerialPort1.Open()

    End Sub

    'シリアルポートがデータを受信したら
    Private Sub SerialPort1_DataReceived(ByVal sender As Object, ByVal e As System.IO.Ports.SerialDataReceivedEventArgs) Handles SerialPort1.DataReceived

        Dim ir_code1 As String
        Dim ir_code2 As String
        Dim maker1 As Integer
        Dim maker2 As Integer
        Dim data1 As Integer
        Dim data2 As Integer

        '2回分の受信結果を得る(NECフォーマットでは同じコードを2回送るので)
        ir_code1 = SerialPort1.ReadLine()
        ir_code2 = SerialPort1.ReadLine()

        If ir_code1 = ir_code2 Then '2回の受信結果が同一か(NECフォーマットでは同じコードを2回送るので)
            If Mid(ir_code1, 1, 3) = "SLD" Then '先頭に"SLD"という文字列があるか(PIC送信側で先頭に"SLD"という文字列をつけているため)

                '0/1のパターンを分解
                maker1 = BinStr2Dec(Mid(ir_code1, 4, 8))
                maker2 = BinStr2Dec(Mid(ir_code1, 12, 8))
                data1 = BinStr2Dec(Mid(ir_code1, 20, 8))
                data2 = BinStr2Dec(Mid(ir_code1, 28, 8))

                'data1の内容で操作を決める
                Select Case data1
                    Case 232
                        MyiTunes.Play() '再生
                    Case 104
                        MyiTunes.Stop() '停止
                    Case 8
                        MyiTunes.NextTrack() 'トラック送り
                    Case 136
                        MyiTunes.BackTrack() 'トラック戻し
                End Select

            End If
        End If

    End Sub

    'フォームを閉じとき
    Private Sub Form1_FormClosed(ByVal sender As Object, ByVal e As System.Windows.Forms.FormClosedEventArgs) Handles Me.FormClosed

        ' シリアルポートのクローズ
        SerialPort1.Close()

    End Sub

    'バイナリの文字列を10進数に直す
    Private Function BinStr2Dec(ByVal a_str As String) As Integer

        Dim len_str As Integer
        Dim buf As Integer = 0

        len_str = Len(a_str)

        For i As Integer = 1 To len_str
            If Mid(a_str, i, 1) = "1" Then
                buf = buf + 2 ^ (len_str - i)
            End If
        Next i

        BinStr2Dec = buf

    End Function

End Class

3.結果

 2.5のプログラムを起動し、赤外線受光モジュールに向けてリモコンの再生ボタンを押したところ、iTunesの再生が開始した。他のボタンについても同様に押してみたところ、正しくiTunesを操作することができた。これにより、赤外線リモコンを用いてPC外部よりiTunesが操作できることがわかった。


まとめと今後の課題

 USB対応PICマイコンPIC18F14K50と赤外線リモコン、自作Windowsアプリケーションを組み合わせ、iTunesをPC外部から操作するシステムを構築し、正しく操作することができた。

このページのトップヘ