Raspberry Piのカーネルが4.19になってから赤外線リモコン(lirc)の仕様が変わる

Raspberry Piのカーネルが4.19になって赤外線リモコン(lirc)の仕様が変わってから、台所の照明の電源がオン/オフが上手くできなかったが、やっと対処法が見つかった。
対処法が見つからない間は、sudo rpi-updateで4.14.98に戻していた。

4.19ではだめな例

os.system('irsend SEND_ONCE dshomei on')
sleep(0.5)
os.system('irsend SEND_ONCE dshomei on')

4.19でOKな例

for i in range(30):
    sleep(0.1)
    os.system('irsend SEND_ONCE dshomei on')

4.14ではirsend1回では失敗っすることがたまにあったので、2回実行していた。4.19では短い間隔で複数回送らないとだめなようだ。とりあえず30回送るようにしたら、今の所失敗していない。
この場合、トグル系のやつはどうしたらいいんだろうという気はするが、今はトグル系のものは制御してないからまあいいっか。

あと設定は以下のように変える必要がある。

# Uncomment this to enable the lirc-rpi module
# dtoverlay=lirc-rpi
# dtparam=gpio_in_pin=20
# dtparam=gpio_out_pin=21
dtoverlay=gpio-ir,gpio_pin=20
dtoverlay=gpio-ir-tx,gpio_pin=21

学習リモコン(ADRSIR)のスイッチを押すプログラム

ADRSIRの10個あるスイッチを押すサンプルプログラムがなかったので作ってみた。動作はNode-REDのリモコンサブフローの内容を参考にした。

下記のように実行すればスイッチ1が押せる。
./sw.py 1

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
#
# 学習リモコン(ADRSIR)のスイッチを押す動作
#
# 引数はスイッチ番号

import sys
from time import sleep
from RPi import GPIO

# スイッチ番号をGPIOのPIN番号に変換する
def getBcmFromSwitchNo(switchNo):
  bcm = -1
  if switchNo == '1':
    bcm = 4
  elif switchNo == '2':
    bcm = 17
  elif switchNo == '3':
    bcm = 27
  elif switchNo == '4':
    bcm = 18
  elif switchNo == '5':
    bcm = 5
  elif switchNo == '6':
    bcm = 6
  elif switchNo == '7':
    bcm = 13
  elif switchNo == '8':
    bcm = 12
  elif switchNo == '9':
    bcm = 22
  elif switchNo == '10':
    bcm = 23
  return bcm
    
if __name__ == '__main__':
  argvc = sys.argv
  argc = len(argvc)
  if argc == 2:
    GPIO.setwarnings(False) # warningsは無視
    GPIO.setmode(GPIO.BCM)
    bcm = getBcmFromSwitchNo(sys.argv[1])
    if bcm > 0:
      GPIO.setup(bcm, GPIO.OUT)
      GPIO.output(bcm, GPIO.LOW)
      sleep(0.002)
      GPIO.output(bcm, GPIO.HIGH)
      #GPIO.cleanup() #GPIOの全ての状態をもとに戻す  学習リモコンのNode-Redが実行中なのでクリーンアップはしない。

 

このページはこれらの動作を保証するものではありません。同様のことをトライされる場合は自己責任でお願致します。

学習リモコン(ADRSIR)で機器毎のコマンド管理とその送信プログラム

ADRSIRでボタン登録できるのは10個なので操作したい機器が増えてくると直ぐにパンクする。必然的に、それぞれの機器のリモコンデータを保持して、その中から任意のコマンドを実行したくなってくる。そこで、機器毎のリモコンデータ保持とコマンド送信ができるプログラムを作ってみた。

 
フォルダ構成はプログラムフォルダをルートとして、kiki、macro、utilsのサブフォルダを作成する前提で以下の例やコードを記述している。
  (プログラムフォルダ)
    kiki
    macro
    utils

【リモコンデータの保持方法】

各機器のリモコンデータ保持はyaml形式で保持することにした。
書式は下記の通り。
 機能名:リモコンコード
  ※リモコンコードはIR-remocon02-commandline.pyで読み込みコマンドで出力したコード。
   メーカー提供の「赤外線データ20171225.csv」から作成してもいいか。

例)
kiki/tv.yaml  このように機器毎にyamlファイルを用意する。

#
# テレビのリモコンデータ
#

電源: "0001A1001900130017003C001700140018003B001700130017003C001700140017003C0017003C001700140017003C001700130017003C001700140017003C0017001300170014001700130017003C0018003B0018003C0017001300170014001700130017003C0017003C0017001400170013001700140017003C0017003C0017003C001700CD034801A1001900130017003C001700140018003B001700130017003C001700140017003C0017003C001700130018003C001700130017003C001700140017003C0017001300170014001700130018003B0018003B0018003C0018001200170014001800120017003C0017003C0017001400170013001700140017003C0017003C0017003C001700CD034801A1001900130017003C001700140017003C001700130017003C001700140016003D0017003C001700130018003C001700130017003C001700140017003C0017001300170014001700130017003C0017003C0018003C0017001300170014001700130017003C0017003C0017001400170013001700140017003C0017003C0017003C001700CD034801A1001900130017003C001700140017003C001700130017003C001700140017003C0017003C001700130018003C001800120017003C001700140017003C0017001300170014001700130017003C0017003C0017003D0017001300170014001700130017003C0017003C0017001400170013001700140017003C0017003C0017003C001600CE034801A1001800140017003C001700140017003C001700130017003C001700140017003C0017003C001700130018003C001700130017003C001700140017003C0017001300170014001700130017003C0017003C0019003B0017001300170014001700130017003C0017003C0017001400170013001700140017003C0017003C0017003C0017004205"

 

【リモコンデータ管理プログラム】

要求のあった機器のリモコンデータ(yaml形式)を取得するプログラムは下記のようにした。

utils/cmdsbinder.py

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
#
# 各種機器のリモコンデータ管理プログラム
#

import yaml, codecs, os, inspect

# インストールルートディレクトリ取得 
irdir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))

# リモコンデータ格納ディレクトリ
kakunop = '/kiki/'

# リモコンデータ拡張子
ext = '.yaml'

# リモコンデータのロード
def load_remocon_data():
  # 呼び出し元の関数名をリモコンデータ名とする
  mname = inspect.currentframe().f_back.f_code.co_name
  return load_remocon_data_internal(mname)

# リモコンデータのロード
def load_remocon_data_internal(kiki):
  with codecs.open(irdir + kakunop + kiki + ext, 'r', 'utf-8') as fs:
    rdata = yaml.load(fs)
    return rdata

# 機器コード指定してリモコン情報取得
def get_rdata(kiki):
  return load_remocon_data_internal(kiki)

# テレビリモコン情報取得
def tv():
  return load_remocon_data()

# アンプリモコン情報取得
def amp():
  return load_remocon_data()

# ビデオ1リモコン情報取得
def video1():
  return load_remocon_data()

# ビデオ2リモコン情報取得
def video2():
  return load_remocon_data()

# HDMI切替器リモコン情報取得
def hdmik():
  return load_remocon_data()

 

【リモコンコマンド送信プログラム】

リモコンコマンドを送信するプログラムは下記のようにした。
IR-remocon02-commandline.pyの送信部分のみ切り出した。

utils/remocon.py

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
#
# リモコン送信プログラム
#

import smbus

# コネクションオブジェクトを取得
bus = smbus.SMBus(1)

# スレーブアドレス
SLAVE_ADDRESS = 0x52

# コマンド
# 送信データ長の書き込み
W2_data_num_write = 0x29
# 送信データの書き込み
W3_data_write = 0x39
# 送信データの送信開始
T1_trans_start = 0x59

# リモコンコマンド送信
def command_soshin(soshin_data):
  # 送信データ整形
  str_tmp = ""
  int_tmp = []
  for i in range(int(len(soshin_data) / 2)):
    str_tmp = soshin_data[i * 2] + soshin_data[i * 2 + 1]
    int_tmp.append(int(str_tmp, 16))

  # 送信データ長の書き込み
  data_num = int(len(int_tmp) / 4)
  data_numHL = [0x31, 0x32] # 初期化
  data_numHL[0] = int(data_num / 256)
  data_numHL[1] = int(data_num % 256)
  bus.write_i2c_block_data(SLAVE_ADDRESS, W2_data_num_write, data_numHL) 

  # 送信データの書き込み
  data = [0x31, 0x32, 0x33, 0x34] # 初期化
  for i in range(data_num):
    data[0] = int_tmp[i * 4 + 0]
    data[1] = int_tmp[i * 4 + 1]
    data[2] = int_tmp[i * 4 + 2]
    data[3] = int_tmp[i * 4 + 3]
    bus.write_i2c_block_data(SLAVE_ADDRESS, W3_data_write, data)

  # 送信データのリモコン送信開始
  null_data = [0x00] # ダミー
  bus.write_i2c_block_data(SLAVE_ADDRESS, T1_trans_start, null_data)

 

【マクロプログラム】

上の2つのプログラム利用したリモコンマクロ。
下記の例はテレビ、アンプ、ビデオをオンするマクロ。
これをNode-REDなどから呼び出して利用する。
状態をもったり細かい制御がしたくなったらこちらを利用していく予定。
今のところ下記の汎用のものでまかなえている。

例)
macro/m01_v1on.py

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
#
# ビデオ1系のオン/オフ マクロ
#

from time import sleep
import sys, os
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
from utils import cmdsbinder, remocon

# リモコン情報取得
tv = cmdsbinder.tv()
amp = cmdsbinder.amp()
video1 = cmdsbinder.video1()
hdmik = cmdsbinder.hdmik()

# ビデオ1とテレビとアンプを付け、切替器を入力1へ
remocon.command_soshin(tv['電源'])
sleep(0.5)
remocon.command_soshin(amp['電源'])
sleep(0.5)
remocon.command_soshin(video1['電源'])
sleep(0.5)
remocon.command_soshin(hdmik['入力1へ'])

 

【汎用コマンド送信プログラム】

こちらはNode-REDからの引数で任意のリモコンコマンドを送信する目的で作成したプログラム。
シェルとかから呼んでも問題ないはず。
特にプログラムで細かな制御をする必要のないものはマクロを作成するのではなく、これを利用すればいい。

utils/commander.py

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
#
# 汎用コマンド処理
#
# 引数を解釈して0.5秒おきにコマンドを送信する
#
# 引数は機器、コマンド、機器、コマンドと並んでいる前提
#

from time import sleep
import sys, os
import cmdsbinder, remocon

args = sys.argv
rdatas = {} # リモコン情報キャッシュ 

for i in range(1, len(args) - 1, 2):
  # 対応するリモコン情報取得
  kiki = args[i]
  if kiki in rdatas:
    rdata = rdatas[kiki]
  else:
    rdata = cmdsbinder.get_rdata(kiki)
    rdatas[kiki] = rdata

  # 対応するリモコン信号送信
  remocon.command_soshin(rdata[args[i + 1]])

  # 待機
  sleep(0.5)

 
・Node-REDから利用する場合

先ずは、injectノードを以下のようなJSON形式で作成する。
JSONの内容は機器(kiki)とコマンド(cmd)を持ったオブジェクトの配列。
例)

[
    {
        "kiki": "video1",
        "cmd": "電源"
    },
    {
        "kiki": "hdmik",
        "cmd": "入力1へ"
    }
]

 
次にfunctionノードを以下のようにして、上のJSONから引数生成する。
例)

var cmds = msg.payload;
var hikisu = "";
for(i = 0; i < cmds.length; i++){
    hikisu = hikisu + " '" + cmds[i].kiki + "'";
    hikisu = hikisu + " '" + cmds[i].cmd + "'";
}
msg.payload = hikisu;
return msg;

 
最後にexecノードでコマンドをutils/commander.py、引数をmsg.payloadにして実行すれば、上のJSONで指定したコマンドがリモコン送信される。

 

これを作って操作ボタンはそれほど利用する必要がなくなったので、7~10のボタンはリモコンデータ収集用のフリーのボタンとして位置付けるようになった。

各機器のリモコンデータを管理し、それらの中から任意のコマンドを実行できる仕組みを作らないと真の学習リモコンとはいえない。やっと学習リモコンらしくなってきた。改善したい点は、機器毎のyamlをもう少し簡単に対話形式で作成する機能を作ってみたい。

 

このページはこれらの動作を保証するものではありません。同様のことをトライされる場合は自己責任でお願致します。

Firebase Databaseとgoogle-home-notifierを使ったGoogle Homeの音声通知環境

Firebase Database(以下DB)とgoogle-home-notifierを使ってGoogle Homeの音声通知環境を作ってみた。まぁ、実際できてみるとどうやって使うかなという感じ。

【音声通知サービス プログラムのnodejsサンプル】

これをGoogle Homeがいるホームネットワーク内のどこかのPCで起動しておけば、DBの音声メッセージ用フィールドが更新されると、Google Homeに音声通知が流れる。このDBはREST APIでどこからでも更新できるので、ネットにつながる端末さえあれば、自宅に音声通知できる。あと、この構成の利点はngrokなどのホームネットワークを外部からアクセスできるようにするサービスを利用しなくて良いところか。


// 音声通知サービス

const firebase = require('firebase');
const googlehome = require('google-home-notifier');
const conf = require('./config.json');

// Firebase初期化
firebase.initializeApp(conf.firebase);
let ref;

// goodle homeデバイス定義 
googlehome.device(conf.deviceName, conf.language);

// 音声通知処理
const notify = snapshot => {
  const message = snapshot.val();
  if (message == '済') {
    // スルー
  }
  else {
    console.log(message);
    // 音声通知
    googlehome.notify(message, res => {
      console.log('<< notify OK >>');
      console.log(res);
      // 通知が終わったら音声メッセージ用フィールドをクリアーする
      ref.set('済');
    });
  }
};

// 音声メッセージ用フィールドの監視設定初期化
const kanshi = () => {
  // firebase.database().ref()だけでイベントループが始まる。
  // ref.offではループは止まらなかった。
  ref = firebase.database().ref(conf.dbUrl);
  ref.on('value', notify, error => errorlog('ref.on', error));
}

// 認証及び監視スタート
firebase.auth().signInWithEmailAndPassword(conf.email, conf.pwd)
.then(() => {
  console.log('<< auth OK >>');
  // 監視スタート
  kanshi();
})
.catch(error => errorlog('auth', error));

// エラー時処理
const errorlog = (stepLabel, error) => {
  console.log(`<< ${stepLabel} error >>`);
  console.log(`code: ${error.code}`);
  console.log(`message: ${error.message}`);
  console.log(`以下エラー全部===`);
  console.log(error);
  console.log(`================`);
  process.exit();
}

 
 
【config.json】

<XXX>となっているところは利用環境に合わせ設定する。

{
    "firebase": {
        "apiKey": "<API_KEY>",
        "authDomain": "<PROJECT_ID>.firebaseapp.com",
        "databaseURL": "https://<DATABASE_NAME>.firebaseio.com"
    },
    "email": "<EMAIL_ADDRESS>",
    "pwd": "<PASSWORD>",
    "dbUrl": "<PATH>/message",
    
    "deviceName": "Google Home",   
    "language": "ja"
}

 
 
【主要部分の説明】

  • google-home-notifierはホームページの使用例通りにしてみた。
    deviceNameだけでGoogle Homeを見つけるには「After “npm install”」の項を実施しないといけないところでハマった。デバイスの検索に成功するとフルのデバイス名が一覧される。Google Homeのデバイス名は”Google-Home-英数字の羅列”となっているようだ。本運用ではdeviceNameを”Google Home”からフルで指定したほうがいいかも。逆に全機にブロードキャスト的に使うこともできそう。
  • Firebaseの初期化は公式ガイドのここらへんを見て設定。nodejsで利用するときの読み書きはここらへんかな。nodejsをクライアントとして使う場合の説明が公式ガイドには見当たらなかった。ウェブと同じだろという認識なのかな。認証につてはEメールアドレス方式を利用。理由はこれが一番簡単だったから。
    あと、気になったのがFirebase Databaseって一旦ref()を実行するとイベントループを終了させる方法がないのか!API説明には見当たらなかった。process.exit()で終了させるしかなく、ちょっと気持ち悪い。
  • 通知後、音声メッセージ用フィールドを”済”にしている理由は、何らかの音声通知した印を用意しないと、再起動すると前回の音声通知を流してしまうため。履歴とったりする場合はまた違う方法があるかもしれない。

 

リンク及び参考にさせて頂いたURLの作者の皆様に感謝致します。
このページはこれらの動作を保証するものではありません。同様のことをトライされる場合は自己責任でお願致します。

Google Homeと学習リモコン(ADRSIR)で家電の音声操作

Google Homeで学習リモコンを使って家電を音声操作したくて以下を揃えた。

  • Raspberry Pi 3 Model B
  • ADRSIR [ラズベリー・パイ専用 学習リモコン基盤] ビット・トレード・ワン
    ダウンロード 左記からADRSIR関連のファイルをダウンロード&解凍し、先ず「I2C仕様-TOOL20171213.xlsx」を見ましょう。わたしはてっきり本体と同梱で同様の説明があると思って、どこに取扱説明書があるの?と一瞬途方にくれた。

 

色々設定して、シーリングライトの操作は「電気つけて」で「んーなぁ」といって電気をつけてくれるようになった!これぞ「メイドインアビス」!
もう少し、けだるい感じで発声してくれるといいんだけどね。

 

なお、登録フレーズ以外だと「このライトはまだ設定されてません。」というのが玉にキズ。基本コマンドを変更するいい方法はないものか?まあ、自分が言いそうなフレーズを全部IFTTTに登録すればいいのだが...Dialogflowなどを上手く使えないものかと試してみる予定。あと、エアコンをオンオフできるようにしてみたが、温度設定とかタイマーとかいろいろあって模索中。

ちなみに、応答速度は発声後だいたい4秒前後でまあギリ実用的かなというレベル。コマンドの中継をFirebaseではなくTwitterでやった場合は、もっと時間がかかりこりゃダメだ状態だった。

【それぞれの機器やサービスの接続方法は以下の通り】

Google Home => IFTTT =>  Firebase => Node-RED => 学習リモコン(ADRSIR)

※この接続を試す前にFirebaseにあたるところをTwitterでやってみたが、反応がとろすぎて却下。(まぁ、それ以外にもセキュリティーをどうするかなどなどTwitterでは問題は多いんだけど。)

【連携イメージ】

  • 音声コマンドをIFTTTを使ってFirebaseの音声操作コマンドを伝える項目として決めた特定のフィールドを更新する。
  • そのフィールドの更新をNode-REDで監視し、更新されたらそのコマンドに対応する処理を実行し、電気をつけたり消したりする。

【いろいろな疑問を解決するために参考にしたURLなど】

  • どうやってIFTTTからLANにある学習リモコン(Raspberry Pi)に操作を通知するか

これはngrokやdataplicityの紹介があって、それらを見ていたが、
ngrokは毎回アドレスが変わるからIFTTTで設定しづらいし無いなあ~とか思い、
dataplicityはちょっと今回やりたいことを考えたらオーバースペックすぎるな~とか。
そうこう考えているうちにFirebaseでできるような記事を見たので、それならGooogleアカウントは持ってるし新たにユーザー登録せずに無料で使えるなと思い、その線で調べた。結局、FirebaseとNode-REDが連携できると紹介していた以下の動画を見て、Firebaseで連携することに決定!これなら、Nodejsなどで簡易サーバーを作らなくて済む。

IoT Application using Node Red & Google Firebase | Raspberry Pi

セキュリティーは気になったので、この動画とは違うJSON Web Tokenでやった。
でも、それも既にFirebaseでは非推奨になっているようで、アクセストークンで対応する方法を、模索中。

上の経緯を踏まえて、IFTTTでのアプレット登録は、IFを「Google Assistant」にし、thenを「Webhooks」する方法にした。参考となるのは以下のようなものがあった。

GoogleHomeとiftttとDialogflowとfirebaseとLINEBOTとラズパイを使って子供と音声によるLINE交換をしてみる(後編)

  • Node-REDでどうやってFirebaseを利用するか?

取り敢えず、 node-red-contrib-firebaseをインストールしてみたが、RaspbianにデフォでインストールされているNode-REDでは、アイコンが出てこなかった!?なんで?これは-g付きでやったのでどのユーザーでも使えるはずだけど?古いから悪いのか、常用するユーザーidをデフォルトユーザーのpiから変えているのが悪いのかなど原因は分からなかった。なお、デフォルトユーザーpiを有効にする気はなので、それ以上調査はしなかった。

そこで、Node-RED関係を最新するため、Running on Raspberry Piを参考に常用するユーザーidでupdate-nodejs-and-noderedを実行。これで、Nodeなど結構最新になっていることが確認できた。またそのidのホームに.node-redディレクトリができていた。その状態で、node-red-contrib-firebaseを-gなし(テスト利用なので-gじゃなくていいかという理由)でインストールすると、右のタブにFirebase関係のアイコンが出てきた!

それで、特に問題なく動作した。

【Node-REDのフロー図】

  • 音声操作コマンドに対応するフィールドの更新を監視する。
  • 更新があると音声操作コマンドの内容を判定してリモコン操作。
  • 「リモコン◯」はメーカーサンプルのサブフローをそのまま利用。
  • コマンドクリアーは、操作コマンドの内容をそのままにしておくと、リブートした時に前回実行したコマンドが実行されてしまうので追加した。

【学習リモコン(ADRSIR)について】

  • 電源切り替えできるが、本体電源を利用する場合の、電源規格が見つからなかった。初心者的には明示して欲しい。5v,1Aがいいなど。micro-Bの規格範囲ならいいということか?Raspberry Piと利用する場合の電源説明もなかったので、ちょっと冷や冷やして電源投入した。
  • Raspberry Pi 3 Model Bとの組み合わせで5v,2.4Aの電源だとたまにリモコンが効かない「どじっこメイド」になる。5.2v,2.5A(sureface3用電源)の電源だと好調。
  • リモコンと受信機までの距離は3.5mぐらい(それ以上は未検証)は大丈夫なようだ。前提は5.2v,2.5A(sureface3用電源)の電源利用時。この送信できる距離は、リモコンを自作したサイトをみていると1mしか届かないとかいう記事がそこそこあったので、一番心配していた問題だった。今回の組み合わせでは問題にならなさそうでほっとした。12畳だったら真ん中あたりに置けば部屋中の機器を操作できそうかなという感じ。なお、上の項にあるように電源の出力が弱いと送信距離も減るということは考慮しておかないといけない。
  • 付属説明書に必要情報がない!電子工作のものってこんな感じなのか!!と思った。
    最低、ダウンロードでハード利用についての仕様やサンプルが入手できることを書いて欲しい。思わず「なぞなぞマンかよ!」と叫びたくなる。ここにADRSIRとの通信手順も書かれていた。
  • 欲を言えば「赤外線データの送信手順」もpythonでサンプルが欲しかった。操作したい機器が多いと、直接送信が一番いちばん手っ取り早いと思えるので。
    2018/01/14 追記 IR-remocon02-commandline.pyの中に利用されてないだけでメソッドはあった。
  • Node-REDのサンプルも、こうやって使ってみましょう的な説明があったほうがいい。
    リモコンいじり放題!!ラズパイ専用 学習リモコン基板で使えるソフトウェア3種詰め合わせ公開」からダウンロードして、Node-REDのサンプルっぽいのがあるなと思い、インポートしてみてやっと、こう使うのか!と分かった。「早く言ってよ~!」という感じだった。よくよく見てみると「I2C仕様-TOOL20171213.xlsx」のなかに画像があってコピーして使用してくださいとあったが。。。
    2018/01/13 追記 以下のようなURLを見つけた。
    この学習リモコンがすごい!2017 Sonyの高機能学習リモコンHUIS(ハウス)と同等機能を学習リモコン基板 “ADRSIR”で実現する

【Raspberry Piについて】

  • やりたことは、検索するとほぼ回答となる記事を見つけれられるので、いいなと感じた。

 

誰かがみたときに役にたちそうなブログネタなので、これ関連で投稿していくか。
まっ、ぼちぼち、いろいろ試してみよう。

「Google Assistant」のスキルとしたので、スマホからも指示できるのは「おまけ」か。

リンク及び参考にさせて頂いたURLの作者の皆様に感謝致します。
このページはこれらの動作を保証するものではありません。同様のことをトライされる場合は自己責任でお願致します。