FFmpegを導入してHomeKitからwebカメラを使う

FFmpegは動画情報を変換したり転送するツールです。セキュリティカメラやビデオドアフォンを利用する上で必要になる場合が多いです。特にHomebridgeのプラグインなどのようにオープンソースのプロジェクトではほとんどFFmpegが利用されます。今回はMacUbuntuFFmpegをインストールして動作確認し、UbuntuのHomebridgeからUSBカメラを使ってみました。

Macにインストール

FFmpegって動画ファイル変換ソフトかと思ってたのですが、とても多機能なツールだったんですね。まずは手近なMacで使ってみました。MacFFmpegを使うためにはHomebrewを使います。Homebrewをインストールした後で、以下のコマンドでFFmpegをインストールできます。

% brew install ffmpeg

動作確認のためにバージョンを表示させました。

% ffmpeg -version

USBカメラ映像をMacで見る

次に、Macに接続された標準的なUSBカメラの映像を、FFmpegを使ってチェックします。Mac標準のPhoto Boothなどのアプリで確認できる映像をみようと思います。UVC(USB Video Class)という規格に対応したUSBカメラなら、Macの色々なアプリケーション、例えばMac標準のPhoto Boothなどで利用できます。Webカメラのほとんどが、またデジカメも一部がUVC規格です。試しに$15くらいのUSB接続顕微鏡を使ってみました。

検索してみると、macOSでは動画・音声の入出力に、avfoundationというフレームワークを使うようです。以下のコマンドでサポートされたデバイス名の一覧がわかるようです。

% ffmpeg -f avfoundation -list_devices true -i ""
[AVFoundation indev @ 0x7fd644104ec0] AVFoundation video devices:
[AVFoundation indev @ 0x7fd644104ec0] [0] USB2.0 Digital Camera
[AVFoundation indev @ 0x7fd644104ec0] [1] Capture screen 0
[AVFoundation indev @ 0x7fd644104ec0] AVFoundation audio devices:
[AVFoundation indev @ 0x7fd644104ec0] [0] USB2.0 Digital Camera
[AVFoundation indev @ 0x7fd644104ec0] [1] Built-in Input

接続したデバイスの名前は"USB2.0 Digital Camera"のようです。これがわかれば、ffmpegコマンドで動画データを扱えます。まずは、ffmpegコマンドの仲間の、ffplayというコマンドを使ってみます。ウィンドウが開いて動画が見られるので動作確認に適してます。

% ffplay -pixel_format uyvy422 -framerate 25 -f avfoundation -i "USB2.0 Digital Camera"

これで画像が出ました。写っているのは100円玉です。上記のコマンド例では、pixel_formatとframerateオプションを追加してます。これらのオプションを指定しないデフォルト設定ではエラーが出たので、エラーメッセージに合わせてこの2つを指定しました。

ffmpegコマンドは、基本的にはビデオフォーマット変換アプリなので、USBカメラから得た動画を、ファイルに保存します。以下のコマンドでQuickTimeで開けるファイルが完成します。キーボードのQを押すと動画取り込みを終了します。

% ffmpeg -pixel_format uyvy422 -framerate 25 -f avfoundation -i "USB2.0 Digital Camera" -pix_fmt yuv420p output2.mp4

結果のファイルをダブルクリックするとQuickTime Playerがファイルを開いてくれます。最後のパラメータはファイル名です。その前にpixel_formatを指定しています。こちらの指定は、出力フォーマットの指定です。これがなくても動画ファイルは作成されますが、その場合、QuickTime Playerでは再生できません。VLCなどの動作再生アプリなら開けます。QuickTime Plaayerで再生するためには、yuv420pに変換しておく必要がありました。

USBカメラ映像をUbuntuで見る

次に、Homebridgeを動かしているUbuntuマシンでもFFmpegを動かします。こちらはapt install ffmpegコマンドでインストールしました。UbuntuマシンにUSBカメラを接続すると、/dev/video0に接続されました。これをffmpegコマンドの入力に指定します。ただ、/dev/video0のユーザ:グループは、root:videoになっていて、それ以外のユーザは読み込みが許可されていませんので、変更する必要があります。今回は、自分のアカウント(以下の例ではfoo)と、Homebridgeを実行するときに使用されるアカウントhomebridgeを、videoグループに割り当てました。/etc/groupの該当する行を以下のように書き換えます。

video:x:44:foo,homebridge

こうして/dev/video0を読める設定にしておけば、以下のコマンドでUSBカメラの映像を動画ファイルに保存することができます。

% ffmpeg -i /dev/video0 -pix_fmt yuv420p output.mp4

UbuntuにもGUI環境をインストールしておけばffplayコマンドで確認できたのかもしれません。しかしサーバーとしてGUIなしで動かしているので、ファイルを転送してMacで動作確認しました。これでFFmpeg動作を確認できました。

HomebridgeでUSBカメラを使う

Apple HomeKitでは、セキュリティカメラやビデオドアフォン(英語ではビデオドアベルというらしいです)の映像を利用できます。HomebridgeではFFmpegで取得できる動画データを、HomeKitで利用できるように提供するプラグインが用意されています。その中の一つ、Homebridge Camera FFmpegを使用しました。

github.com

これをインストールして、ffmpegの設定をすると、コンフィグファイルに以下の記述が追記されました。名前はMicroscopeとしました。

{
    "name": "Camera FFmpeg",
    "cameras": [
        {
            "name": "Microscope",
            "videoConfig": {
                "source": "-i /dev/video0"
            }
        }
    ],
    "platform": "Camera-ffmpeg"
}

その結果、iPhoneMacのホーム.appの画面に、以下のように動画が現れます。

この画面には、最後に取得された動画が静止画として表示されてます。この画像をクリックすると、全画面表示になり、現在の動画が表示されます。

以上はUbuntuでHomebridgeを動かしている場合です。Raspberry Piへのインストール方法が以下のサイトで説明されていましたが、こちらも同じような手順でした。github.crookster.org

エラーへの対応

以上で、大体すんなり動くのですが、たまにエラーが出て、ライブ映像が表示できなくなる場合がありました。Homebridgeのwebページのログに、「デバイスがビジーである」というようなメッセージも出ます。/dev/video0が、他のプロセスに使用されていて使えないという意味です。このような場合は、fuserコマンドをルート権限で使用すると、様子がわかります。

$ sudo fuser /dev/video0
/dev/video0:          1257m

ライブ映像を表示していない時は、video0は誰も使用していないはずなのですが、プロセスIDが1257番のプロセスが使用し続けているようです。psコマンドで調べると、

$ ps -aux | grep 1257
homebri+    1257 99.5  0.1  66008  8236 ?        R    17:49   2:44 /var/lib/homebridge/node_modules/homebridge-camera-ffmpeg/node_modules/ffmpeg-for-homebridge/ffmpeg -i /dev/video0 -frames:v 1 -f image2 - -hide_banner -loglevel error

homebridgeが使っているようでした。試行錯誤して、動画が表示されなくて、homebridgeを再起動したりした場合に、発行済みのFFmpegプロセスがそのまま残ってしまうようです。この場合は、sudo kill 1257で中断することで正常に動くようになりました。

まとめ

USB接続カメラからの動画データをFFmpegを使って取り込み、HomeKitでカメラ画像として使用できることを確認しました。今回使用したHomebridge Camera FFmpegプラグインは、機能豊富で、MQTTブローカーとの連携機能もあります。MQTT経由でモーションセンサや呼び鈴ボタンのイベントを受け付けることができるので、センサ付きセキュリティカメラやビデオドアフォンとして機能させることができます。Homebridgeサーバに安価なUSBカメラを取り付けるだけでサーバ周辺の映像を世界中から確認できるのは便利かと思いました。

ただ、エラーで止まってしまったときの対応が面倒なので、あまり実用的ではないかもしれません。RTSPプロトコルを使ったIPカメラの方が使いやすいかと思います。そのうち試したいと思ってます。

Ledvanceの格安Zigbee電球をHomeKitで使う

いつも読ませていただいているブログでおすすめだった格安Zigbee電球を買って、Apple HomeKitに接続しました。Amazonで300円台で売っているLedvance SMART+の「調光調色」電球377円と「RGB」電球313円です。もしかしたら今は価格が変わっているかもしれませんが、破格の値段です。お買い得情報ありがとうございました。

     

発光色が任意に変えられるのがRGB電球の方で、「調光調色」電球は明るさと色温度が変えられる普通の電球です。RGB電球の方が普通電球よりも安いのも謎。ブログ情報ではZigbee2MQTTで使えたとのことでしたので、安心して買いました。

HomeKitのZigbee環境

というのも、HomebridgeでZigbee2MQTTを使っていたからです。使っているHomebridgeサーバは以下の構成です。

同じ構成でRaspberry Pi 4上でも問題なく動作してました。Raspberry Pi 4が起動しなくなり(熱が原因かな?)、NUC上のUbuntuに移行してます。

ペアリングを試みる

まずは電球をペアリングモードにします。最初に電源を入れると大体ペアリングモードになります。電球が点滅して色温度が変化します。一方で、すでにペアリングしてしまった電球は、当然ですが、電源を入れるだけではペアリングモードに入りません。Zigbee2MQTTのサイト情報によると、Ledvance社の製品は、5秒off、5秒onを5回繰り返すとペアリングモードに移行するらしいです。そこで、スマホでストップウォッチ画面を出して、5秒ごとにoff/onを繰り返してみました。すると、45秒後くらいから電球の点滅が始まり、ペアリングモードに入りました。Zigbee2MQTTを動かしているサーバのそばでペアリングモードに入れば、自動的にペアリングします。その様子を確認するために、mosquitto_subコマンドでペアリング状況をモニターしておきます。Zigbee2MQTTのconfiguration.yamlでは、MQTTトピックをzigbee2mqttと設定してあったので、これをサブスクライブすれば良いです。

mosquitto_sub -h localhost -t zigbee2mqtt/# -v

すると、今回購入したそれぞれの電球で、

zigbee2mqtt/bridge/logging {"level":"warning","message":"Received message from unsupported device with Zigbee model 'CLA60 TW Value' and manufacturer name 'LEDVANCE'"}
または
zigbee2mqtt/bridge/logging {"level":"warning","message":"Received message from unsupported device with Zigbee model 'CLA60 RGBW JP' and manufacturer name 'LEDVANCE'"}>

というメッセージが出ていました。どうやらどちらの電球もサポートされていないようです。未サポートですが、ペアリングはできたので、/opt/zigbee2mqtt/data/configuration.yamlには、デバイスのID番号が登録されてしまってました。番号ではわかりにくいので、とりあえずそれぞれの電球にLedvance WとLedvance RGBというfriendly_nameをつけるよう、configuration.yamlを書き換えておきました。

Zigbee2MQTTを更新する

Zigbee2MQTTのサイトで調べたら、サポートされているデバイスの情報は、/opt/zigbee2mqtt/node_modules/zigbee-herdsman-converters/devices の中のファイルの書かれているそうです。この中に、ledvance.jsというファイルがありましたが、確かにCLA60を含んだモデルはありませんでした。ところがgithubを見に行ったら、どちらのモデルも記述がありました。最新バージョンならばサポートされているようです。

実は、先立って以下の手順で、Zigbee2MQTTを最新版にしてありました。でもこれではまだ鮮度が不足だったようです。

www.zigbee2mqtt.io

追記:最近、別のマシンで試したところ、上記のアップデートで今回の電球のどちらもサポートされるようになってました。なので、すでに通常のアップデートで対応できるようです。以下の手順が本当に必要かどうかを、ledvance.jsで確認してください。)

そこでnode_modulesを手作業でアップデートすることにしました。以下のようにnode_modulesのあるディレクトリに移動して、updateしました。そのあと、npm outdatedするとパッケージの状態を知らせてくれます。

$ cd /opt/zigbee2mqtt/
$ npm update
$ npm outdated

この結果、どのパッケージもCurrentとWantedが同じで、必要なアップデートが完了したようでした。ただ、それでもLedvanceの電球は含まれませんでした。

npm outdatedの結果を見ると、Latestというバージョン番号もありました。zigbee-herdsman-convertersは(他のパッケージも同じですが)Currentよりも新しいバージョンがあるとのことでした。でもupdateではインストールされないようです。調べたところ、無理やり最新版にするためには、バージョン番号を指定してインストールする方法があるらしいです。そこで、最新版を指定してnpm installしました。zigbee-herdsman-convertersの他に、zigbee-herdsmanというパッケージもありましたので、念の為こちらもアップデートしました。

$ npm install zigbee-herdsman-converters@14.0.585
$ npm install zigbee-herdsman@0.14.46

この後は、以下のようにしてzigbee2mqttを再起動しました。

$ systemctl restart zigbee2mqtt.service

ペアリングをやり直す

再起動だけでは電球は認識されませんでした。ペアリングからやり直す必要があるようです。でも45秒off/onを繰り返すよりも簡単な方法が見つかりました。

Zigbee2MQTTは、デバイスが見つかると前述のconfiguration.yamlに書き込む他に、内部でのデータベースでデバイスを管理しているようです。なので、configuration.yamlからデバイスを削除しただけでは、デバイスは消えません。MQTT経由でコマンドを出すと、ようやくデバイス削除ができます。mosquittoを使う場合のコマンドは以下です。数字部分のIDは、configuration.yamlに書き込まれていますし、上記のmosquitto_subのログにも流れています。

mosquitto_pub -h localhost -t zigbee2mqtt/bridge/request/device/remove -m "0x9999999999999999"

この結果、電球の点滅がすぐに開始して、ペアリングが開始しました。

ホーム.appから使う

ペアリング後は、自動的にホーム.appに登録され、iPhoneMacから操作可能になります。以下はMac版ホーム.appのスクリーンショットです。

電球アイコンの部分をクリックするとon/offできます。それ以外のタイル部分をクリックすると、明るさと色を設定する画面が開きます。中央のスライダーで明るさを変更できます。下の6個の丸の一つをクリックするとプリセットされた色になります。

6個のプリセットボタンを選択すると、そこに「編集」の文字が現れます。もう一度クリックすると、「カラー」設定と、

色の「温度」設定が可能になります。

RGB電球では、「カラー」設定が表示されますが、RGB機能がない「調光調色」電球では、「温度」設定のみが表示されます。全体にシンプルでわかりやすいインタフェースかと思いました。

まとめ

Amazonで売られている格安Zigbee電球がHomeKitで動作することを確認しました。破格なので、まだ買えるようならおすすめです。Zigbeeはあまり流行ってないので売れなかったのかもしれません。PSEマーク技適マークも付いていて安心です。

RGBモデルは、価格がさらに安いです。重量は「調光調色」モデルが66g「RGB」モデルが138gと、RGBの方がかなり重いです。内部も詰まっていてお得感があります。冒頭の写真のように、変な色で光らせることもできるので、1個あれば楽しめます。ただ、色が変えられる照明の使い所が無いので、個人的には「調光調色」モデルの方が実用的かと思いました。

ESPHome を Apple HomeKit で使う

_images/logo-text.svg

ESPHomeは、ESP8266/ESP32の入出力ピンや、それに接続する各種センサ・アクチュエータ類などをコントロールするプログラムです。ESPチップのWiFi機能を使ってHome Assistantが動いているコンピュータに接続します。今回はESPHomeの機能を、HomeKitから使ってみます。そのためにHomebridgeとMosquittoを使います。

ESPHome

esphome.iok

こちらのブログで読ませていただいて、ESPHomeを知りました。yaml形式のテキストファイルに、以下のような記述(他にも書く必要はありますが)で、Home Assistant上にスイッチを表示して、GPIO 13番ピンに接続したLEDを点滅させることができるようです。

 switch:
  - platform: gpio
    pin: 13
    name: 'blue_led'

とても便利そうなので、是非ともApple HomeKitでも使ってみたいと思い、調べた結果が今回の内容です。

Homebridge用ESPHomeプラグインを試す

ESPHomeはHomeKit (HAP) をサポートしてないですし、多分これからもサポートしないような気がします。でもHomebridge経由ならなんとかなりそうかと思いました。実際にHomebridgeのプラグインを探してみると、ESPHomeをサポートするものがいくつかありました。それを一通り調べたり試したりしました。

その結果、うまく動きませんでした。開発が止まっているプラグインも多かったです。ESPHomeを使いたいならHome Assistantを使い、そこからHomeKitにブリッジするのが一般的のようでした。そのうち試したいと思います。

ESPHomeからMQTTを使う

ESPHomeのサイトのチュートリアルを見ていたら、MQTTでも機能するようです。通信プロトコルには、Home AssistantのAPIと、MQTTのどちらか、または両方が選べるようでした。これならHomebridgeからすぐに使えそうです。MQTTならデータの流れが簡単にモニターでいるので動作確認も楽そうです。YAMLの中に

api:

と書けば、Home Assistantが使うAPIが有効になり、

mqtt:
  broker: 192.168.xxx.xxx

などと書けば、MQTTブローカーが使えるようです。 なお、api:を指定したのに接続が無い場合は、15分ごとに再起動するらしいので、 使わない場合はapi:の行を削除するかコメントアウトしておいてくださいとのことです。

MQTTを選択することで、以下で説明するように、HomebridgeのMQTTThingプラグインで使用することができました。

ESPHomeを手動インストール

ESPHomeはPythonで書かれたプログラムですが、Home Assistantからはアドインとして使えるようです。なのでインストールもGUIベースで簡単です。Home Assistantを使わない場合は、コマンドラインでインストールします。以下、macOSで作業しました。

esphome.io

Pythonが必要です。Linuxはそのまま使えますが、MacWindowsの場合は、まずはPythonの環境を整えます。以下はMacの場合の手順です。

macOSには昔からpythonがインストール済みで、最近のmacOSにもpython3がすでにあってパスが通ってます。でもそのままでは動きません。Xcodeに含まれるコマンドライン開発ツールが必要です。

% which python3
/usr/bin/python3
% python3 --version
xcode-select: note: no developer tools were found at '/Applications/Xcode.app', requesting install. Choose an option in the dialog to download the command line developer tools.

開発ツールをインストールを促すダイアログが現れるかもしれませんが、それではうまくインストールできません。Xcode本体を入れておくと良いです。


XcodeApp Storeから無料でダウンロードできますし(Apple IDが必要ですが)、プログラミングには必須なので入れておきましょう。また、PythonからXcodeにアクセスする前にライセンスに同意しておく必要があります。コマンドラインからも可能ですが、Xcodeから同意する方が簡単です。単にXcodeを起動して、最初に現れるウィンドウで「同意する」ボタンを押すだけです。Xcodeがインストールされてライセンス同意されていればが、以下のように動作します。

% python3 --version
Python 3.8.9

pipが古いと後のESPHomeインストール時にエラーが出るので、これは更新しておきます。

% pip3 --version
pip 20.2.3
% pip3 install --upgrade pip
% pip3 --version
pip 22.2.1

いよいよesphomeをインストールします。pip3を使いました。

% pip3 install --user esphome

esphomeは~/Library/Python/3.8/bin/にインストールされます (3.8はPythonのバージョン番号です)。以下のように動作確認できます。

% ~/Library/Python/3.8/bin/esphome version
Version: 2022.6.2

フルパスをタイプしたくない場合は、以下のようにしてパスを貼っておくと良いです。

echo 'export PATH=$PATH:$HOME/Library/Python/3.8/bin' >> $HOME/.zshrc

ターミナルを一旦閉じて、再度開きます。そうすれば、esphomeがフルパスなしで実行できます。

% esphome version
Version: 2022.6.2

YAMLファイルを作る

YAMLファイルの簡単な雛形はwizardコマンドで作れます。以下のように入力します。

esphome wizard led.yaml

対話的に質問されるので適当に回答していくと、以下のようなled.yamlというファイルが出来上がりました。boardはesp32devです。

esphome:
  name: esp32

esp32:
  board: esp32dev
  framework:
    type: arduino

# Enable logging
logger:

# Enable Home Assistant API
api:
  password: ""

ota:
  password: ""

wifi:
  ssid: "XXXXXXXX"
  password: "xxxxxxxx"

  # Enable fallback hotspot (captive portal) in case wifi connection fails
  ap:
    ssid: "Esp32 Fallback Hotspot"
    password: "0e0VcHVTRV6K"

captive_portal:

apiとotaのパスワードを指定しなかったので""になってます。この行を丸ごと消しても大丈夫のようです。また、「Enable fallback hotspot」以下の行は、起動後1分以上WiFiに接続できない場合に、WiFi基地局として機能ための記述です。これに接続して、設定を変更することが可能です。わかりやすくするために、その部分を省略して、上で書いたMQTTとGPIOの設定を追加すると以下のようになります。

esphome:
  name: esp32

esp32:
  board: esp32dev
  framework:
    type: arduino

# Enable logging
logger:

# Enable Home Assistant API
#api:

ota:

wifi:
  ssid: "XXXXXXXX"
  password: "xxxxxxxx" 

mqtt:
broker: 192.168.xxx.xxx
switch:
- platform: gpio
pin: 13
name: 'blue_led'

太文字が追加した部分です。また、Home Assistantは使わないのでapi:をコメントアウトしました。

YMLAファイルを動かす

このファイルの設定で良いかどうかはconfigコマンドでチェックできます。

esphome config led.yaml

問題なければ、コンパイルして、ESP32にダウンロードして、起動させます。 以下のコマンドで全部やってくれます。

esphome run led.yaml 

最初のコンパイルでは、ESP32の開発環境をダウンロードするので時間がかかります。コンパイルが終了すると、ダウンロード方法を質問されます。USBシリアルポート経由か、WiFi経由かを選択します。初めてダウンロードする場合はUSBシリアルポート経由を選択します。次回以降は、WiFi経由でもアップロード可能になります。OTA (On The Air)という機能です。有線で接続する必要がないので、どこかに組み込んでしまったESP32のプログラム更新するときに便利です。

=============== [SUCCESS] Took 13.92 seconds ===============
INFO Successfully compiled program.
Found multiple options, please choose one:
  [1] /dev/cu.usbserial-0001 (CP2102 USB to UART Bridge Controller - CP2102 USB to UART Bridge Controller)
  [2] Over The Air (blueled.local)
(number): 1

1を指定します。するとダウンロード・実行されます。ESP32が稼働してます。ログのコメントが現れています。ログからMQTTが動作しているトピックがわかります。

[15:07:55][C][mqtt:074]:   Topic Prefix: 'blueled'
[15:07:55][C][mqtt:076]:   Log Topic: 'blueled/debug'
[15:07:55][C][mqtt:079]:   Availability: 'blueled/status'
[15:07:55][C][mqtt.switch:041]: MQTT Switch 'blue_led': 
[15:07:55][C][mqtt.switch:042]:   State Topic: 'blueled/switch/blue_led/state'
[15:07:55][C][mqtt.switch:042]:   Command Topic: 'blueled/switch/blue_led/command'

別のシェルウィンドウ(ターミナル)からmosquitto_subコマンドを動かしておくと、MQTTメッセージをチェックできます。

% mosquitto_sub -h 192.168.xxx.xxx -t blueled/# -v 
blueled/light/blue_led/state {"color_mode":"onoff","state":"OFF","color":{}}
blueled/status online
blueled/debug [C][mqtt.switch:042]:   Command Topic: 'blueled/switch/blue_led/command'
blueled/switch/blue_led/state OFF

これから、blueled/switch/blue_led/commandトピックに、ONかOFFを遅れば良さそうだとわかります。トピックの名前は、YAMLファイルに現れる名前の順番です。mosquitto_pubコマンドで試してみます。

% mosquitto_pub -h 192.168.xxx.xxx -t blueled/switch/blue_led/command -m ON
% mosquitto_pub -h 192.168.xxx.xxx -t blueled/switch/blue_led/command -m OFF

これで13番に接続したLEDが点灯、消灯します。

mosquitto_subコマンドの方にも結果が現れれています。

blueled/switch/blue_led/command ON
blueled/debug [D][switch:013]: 'blue_led' Turning ON.
blueled/debug [D][switch:037]: 'blue_led': Sending state ON
blueled/switch/blue_led/state ON
blueled/switch/blue_led/command OFF
blueled/debug [D][switch:017]: 'blue_led' Turning OFF.
blueled/debug [D][switch:037]: 'blue_led': Sending state OFF
blueled/switch/blue_led/state OFF

Homebridgeから使う

HomebridgeからMQTTを使うHomebridge Mqttthingプラグインを使用します。コンフィグに、以下のように追記します。

    {
    "type": "switch",
    "name": "Blue LED",
    "url": "mqtt://localhost:1883",
    "topics": {
        "getOn": "blueled/switch/blue_led/state",
        "setOn": "blueled/switch/blue_led/command"
    },
    "onValue": "on",
    "offValue": "off",
    "accessory": "mqttthing"
},

MQTTブローカーがHomebridgeと同じマシンで動いているので、urlはlocalhostになってます。Mqttthingのデフォルトのメッセージがtrueとfalseなので、これをESPHomeに合わせてonとoffに定義し直しています。これでiPhoneMacのホーム.appから操作できます。

   

まとめ

ESPHomeをApple HomeKitで使ってみました。MQTTプロトコルで使えば、Homebridgeのプラグインから使えました。YAMLの数行の記述でプログラミングが完成するので、簡単で良いです。またOTAの機能を使えばWiFi経由でプログラムを更新できるので便利です。

MQTTブローカーMosquittoを設置する

今回はMQTTブローカーをインストールします。MQTTがあると、スイッチと、センサ、リレー、サーボモータなどを接続したArduinoのようなコンピュータを、ネットワーク越しにIP接続したい時にとても便利です。先の記事では実験的に公衆MQTTサーバを使いましたが、外部から操作されては困るので、今回は自家用サーバを用意したいところです。そこで、Homebridgeがすでに動いているUbuntuコンピュータに、MosquittoというMQTTサーバをインストールします。

MQTTのある暮らし

前回の復習です。MQTT (Message Queueing Telemetry Transport)は、短いメッセージを効率よく伝える用作られたIoT向きのプロトコルです。MQTTブローカーというサーバを一つ用意して、それにデータ送信側・受信側が接続します。データを送信したい側(下の図では壁スイッチ)は、typeに適当な名前(例えばswitch)をつけて、送りたいデータ(例えば文字列off)をmessageとしてpublishします。データを受信したい側(下の図ではコンピュータ)は、同じtypeを指定してsubscribeしておくと、送信側からのデータがmessageとして送られます。この仲介をするのがMQTTブローカーです。

subscribeするコンピュータでは、Pythonなどのプログラムを動かしておいても良いですし、Homebridgeなどのサーバを動かしておいても良いです。なので、MQTTブローカーが逸般の誤家庭で動いていると、スマートホームを構築する上で色々便利です。今回はオープンソースのMosquittoというMQTTブローカーをインストールします。一番よく使われる有名なブローカーのようです。

mosquitto.org

Mosquittoをインストールする

Mosquittoは色々なコンピュータにインストール可能ですが、一般的にはRaspberry Pi OSやUbuntuなどのLinux系OSが多いかと思います。Homebridgeが動いているマシンにMosquittoもインストールしておけば、Homebridgeからはローカルマシンへのアクセスで利用できます。マシンの外から使わなければセキュリティ設定無しでも良いので簡単です。

Raspberry Pi OSやUbuntuなどには、aptコマンドでインストールできます。

sudo apt install mosquitto mosquitto-clients

これで終了ですので簡単です。MQTTブローカを起動するためには、

sudo systemctl start mosquitto

とします。止めるにはstartの代わりにstop, 再起動するにはrestartとします。なお、OS起動時に自動的にMQTTブローカが立ち上がる用に設定するには、

sudo systemctl enable mosquitto.service

としておきます。これでMQTTブローカーが動きます。デフォルト設定で動作させた場合、ユーザアカウント、パスワード無しです。ただしこのマシンの外部、つまりLAN上の他のマシンからの接続は受け付けません。動作の確認にはシェルコマンドのウィンドウを二つ開けて(Macからssh接続しているならターミナルウィンドウを2個開けてそれぞれssh接続します)、片方から以下のようにタイプします。

mosquitto_sub -h localhost -t test

mosquitto_subコマンドは、サブスクライブするコマンドです。-t引数でサブスクライブするタイプをtest、-h引数でブローカーのアドレスをlocalhostに指定してます。デフォルトではローカルからのアクセスしか許可されていないので、localhostを指定します。

別のシェルコマンドウィンドウから、以下のコマンドをタイプします。同じくMQTTブローカーに、タイプ名testで、helloというメッセージを送ってます。

mosquitto_pub -h localhost -t test -m hello

この結果、先のコマンドウィンドウに以下のようにhelloという文字が現れます。

mosquitto_sub -h localhost -t test
hello

このデフォルト設定では、コンピュータ外からの接続ができません。それでも、マルチタスクで動いているプログラム同士の通信ができます。例えばそのコンピュータでHomebridgeが動いていて、Homebridgeのプラグイン同士で通信する場合や、同じコンピュータ上の他のプログラムやデーモン同士で通信する場合はこれで十分です。

Mosquittoの追加設定

コンピュータの外部からの接続を受け付けるためには、デフォルト設定に対して、追加設定が必要になります。LANからの匿名アクセスを受け付ける場合、 /etc/mosquitto/mosquitto.conf の最後に次の2行を追加します。

allow_anonymous true
listener 1883

listenerは受付ポート番号です。これを指定するとLANにある他のコンピュータからIPアドレスを指定しての接続が可能になります。allow_anonymousは匿名での接続を許可するかどうかの設定です。デフォルトがfalseですが、ここではtrue(匿名接続を許可)に設定しています。上の例ならば、

mosquitto_sub -h 192.168.xxx.xxx -t test

というようにIPアドレスを指定してアクセスできるようになります。これで十分な気もしますが、匿名アクセスを禁止して、ユーザ名とパスワードを設定することもできます。その場合には、次のように記載します。

allow_anonymous false
password_file /etc/mosquitto/password.txt
listener 1883

ここで指定したpassword.txtというファイルは、ユーザと暗号化されたパスワードを記したファイルです。mosquitto_passwdコマンドで作成します。こちらに説明がありました。名前とパスワードをコロンで区切ったテキストファイルを用意して、

mosquitto_passwd -U password.txt

とタイプすれば、暗号化されたパスワードファイルを作ってくれます。これを適当な場所(ここでは/etc/mosquitto/)に置いておきます。先ほどのmosquitto_subコマンドの場合、

mosquitto_sub -h 192.168.xxx.xxx -t test -u USER -P PASSWORD

というようにして、-uでユーザ名、-Pでパスワードを指定してアクセスします。

Macにインストールする

Mosquittoは、Linux系のOSだけでなく、WindowsmacOSにもインストール可能です。WindowsmacOSを24時間稼働のサーバとして使う人は少ないかもしれません。でも、mosquitto_subやmosquitto_pubなどのコマンドは、MQTTの状態をモニターするのにあると便利なので、ぜひインストールしておきたいです。macOSの場合は、homebrewを使って以下のようにインストールします。

brew install mosquitto

これでmosquitto_subとmosquitto_pubコマンドが使えるようになります。

NASにインストールする

QNAPやSynologyのNASを使っている場合でしたら、Homebridgeの時と同様にDockerを使ってNASにMosquittoをインストールするのも良いと思います。NASとして24時間稼働しているので、無駄になりません。QNAPの場合は、Container Stationからインストールします。下はHomebridgeとMosquittoがすでに稼働しているときのスクリーンショットです。新規に「作成」する際の検索窓にMosquittoと入れれば、Dockerイメージが見つかります。

Mosquittoを使ってLチカ

ではインストールしたMosquittoブローカーを使ってみます。前回作ったMQTT経由でLEDを点滅するESP32のプログラムを、新しくLAN内に設置したMoaquittoブローカーを使うように書き直します。そしてMacなどからmosquitto_pubコマンドでLEDを点滅させます。

変更すべき箇所は、MQTTの設定部分です。xxxはMosquittoをインストールしたコンピュータのIPアドレスに合わせておいてください。

const char  MQTTADD[] = "192.168.xxx.xxx"; //Broker addr
const short MQTTPORT = 1883; //Broker port
const char  MQTTUSER[] = "";//Can be omitted if not needed
const char  MQTTPASS[] = "";//Can be omitted if not needed
const char  SUBTOPIC[] = "espLED/seton"; //sub topic
const char  PUBTOPIC[] = "espLED/geton"; //pub topic 
const char DBGTOPIC[] = "espLED/debug"; //debug topic  

Topic名の始まりはespLEDにしました。分かり易ければなんでも良いです。Mosquittoでユーザとパスワードを設定しない場合のプログラムで、MQTTUSERとMQTTPASSが空になってます。コンパイルしてESP32にダウンロードしておきます。シリアルモニターを見れば動作も確認できます。

一方で、Linux, Windows, MacなどからMosquittoのコマンドを使って操作します。

mosquitto_sub -h 192.168.xxx.xxx -t espLED/# -v

というコマンドを一つのコマンドシェルウィンドウから立ち上げておくと、動作を確認できて良いと思います。ESP32が起動した時から立ち上げてあれば、起動メッセージが見られます。

mosquitto_sub -h 192.168.xxx.xxx -t espLED/# -v
espLED/debug Connection established.

別のコマンドシェルからtrueやfalseをパブリッシュします。

mosquitto_pub -h 192.168.xxx.xxx -t espLED/seton -m true
mosquitto_pub -h 192.168.xxx.xxx -t espLED/seton -m false

するとLEDが点灯し、消灯するはずです。

モニターしているsubコマンドの方にも、以下のようにメッセージが現れるはずです。

mosquitto_sub -h 192.168.xxx.xxx -t espLED/# -v
espLED/debug Connection established.espLED/seton true
espLED/debug Message received.
espLED/geton true
espLED/seton false
espLED/debug Message received.
espLED/geton false

HomeKitからLチカする

前回の記事では、HomebridgeのHomebridge Mqttthingプラグインを使ってこのLEDをon/offしました。今回は、LAN内の新しいMosquittoブローカを使うように変更します。

公衆MQTTブローカーを指定していたHomebridgeのコンフィグを、以下のように変更して、今回設定したLAN内のMosquittoを使用するように設定します。

{
    "type": "lightbulb-OnOff",
    "name": "MQTT Light",
    "url": "mqtt://192.168.xxx.xxx:1883",
    "topics": {
        "getOn": "espLED/geton",
        "setOn": "espLED/seton"
    },
    "accessory": "mqttthing"
}

urlのxxxの部分は、Mosquittoブローカーのアドレスに合わせてください。Homebridgeが稼働している環境でMosquittoも動いているなら、"url": "mqtt://localhost:1833"と設定しても良いです。これで保存、再起動すれば、iPhoneMacのホーム.appからの操作で、LEDが点滅します。

   

まとめ

MQTTブローカーのMosquittoをサーバーにインストールして、これにESP 32を接続しました。前回の記事で紹介したLED点滅プログラムを、LAN内ブローカーを使う設定に変更して、Apple HomeKitからLEDを点滅させました。

公衆MQTTサーバでLチカする

スイッチやセンサの状態を送信したり、リレーやサーボモータの制御コマンドを受信するのに、MQTT (Message Queueing Telemetry Transport)というプロトコルが使われます。MQTTは、Home AssistantやHomebridgeでもサポートされていますし、Arduinoでも使えるので、スマートホーム自作に活用できます。通常は家の中にMQTTサーバー(MQTTの用語ではブローカーと呼ぶので以下ではブローカー)を用意して使用しますが、インターネット上にも試作・実験で誰でも使える公開ブローカーが用意されてます。

今回は、そんな公衆MQTTブローカーを使って、ESP 32に接続したLEDをHomeKitから点滅(Lチカ)させます。以下の経路で接続します:

  1. LEDを接続したESP 32
  2. ESP 32をMQTTに接続するライブラリ: EspMQTTClient
  3. Eclipse Projectの公衆MQTTブローカー
  4. Homebridge + MQTTプラグイン
  5. iPhoneMacのホーム.app

MQTTの使われ方

例えば、ArduinoやESP 32で無線スイッチを作って、人がon/offしたことをLAN経由で他のコンピュータに伝える仕組みを考えてみます。ソケット通信のライブラリを使って他のコンピュータにスイッチの状態を伝えるのが簡単で効率が良いのですが、プログラムを起動する手順が面倒です。Arduinoに簡単なWebサーバーのような応答をさせて、スイッチ状態をHTTPで送る方法もあります。下の写真はHTTP経由でLEDを点灯するLチカの例です。

https://i0.wp.com/randomnerdtutorials.com/wp-content/uploads/2018/07/esp32-we-server.jpg

( こちらから転載 )

この場合、コントロールするために普通のWebサーバやcurlコマンドも使えて便利です。でもHTTPは送りたいデータ以外にお約束のテキストを色々転送する必要があるので、せいぜい数バイトしか送る必要がないIoT的な用途には無駄(オーバーヘッド)が多いです。もともと制御用のプロトコルじゃないので、on/offの表現方法も自由過ぎて標準化が必要です。

そこでMQTTが使われます。短いメッセージを効率よく伝える用作られたIoT向きのプロトコルです。MQTTを使う場合、MQTTブローカーというサーバを別途用意して、それにデータ送信側と受信側がクライアントとして接続します。

手順には、雑誌や新聞の出版と購読をメタファーにした名前がついていて、サーバコンピュータは書籍取次業者(ブローカー)として機能します。データを送信したい側は、雑誌名・新聞名に相当するトピック (topic) に適当な名前をつけて、送りたいデータをメッセージ (message) として「出版 (publish) 」します。データを受信したい側は、雑誌名・新聞名であるtopicを指定して、「購読 (subscribe)」しておくと、送信側からデータがpublishされたときに、これをmessageとして受け取れます。

例えば、上の図のように、on/offスイッチ側がswitchというトピックを指定してoffという文字列をパブリッシュすると、switchをサブスクライブしている他のコンピュータにそれが伝わります。そのコンピュータでHomebridgeとMQTTプラグインが動いていれば、このon/offスイッチをHomeKitのアクセサリとして扱えます。

MQTTをインストールする

MQTTのソフトウェアを提供するオープンソースプロジェクトにはいろいろあるようですが、Mosquittoが定番の一つのようです。なのでこれを使います。Mosquitto一式をRaspberry Pi OSやUbuntuなどにインストールするには、

sudo apt install mosquitto mosquitto-clients

とします。MQTTクライアントのみで、ブローカを動かさないなら、mosquitto-clientsだけで良いです。Windows用もあります。個人的にはDOS窓はあまり好きではないので、WSLでUbuntuを使うのが良いのではと思います。その場合、Ubuntuのシェルウィンドウからaptコマンドで取り寄せます。

macOSの場合は、homebrewを使って以下のようにインストールします。

brew install mosquitto

これでMQTTブローカーにアクセスする2つのコマンド、mosquitto_subとmosquitto_pubコマンドが使えるようになります。

公衆MQTTブローカーを使ってみる

スマートホームで使うMQTTブローカーは、自宅で稼働させて、自家用に使うのが一般的だと思います。ただ、自前でサーバを建てたりメンテするのが煩わしいという人のために、インターネット上でMQTTブローカーを提供するサービスもあります。Mosquittoを作っているEclipse Fundationというところは、mqtt.eclipseprojects.ioというIPアドレスで、無料MQTTブローカーを提供してます。誰でもすぐに使えます。例えば、

mosquitto_sub -h mqtt.eclipseprojects.io -t #

とするとものすごい量のデータが流れてきます。コントロールCで止めてください。

-hがアドレス、-tがトピック名を表します。トピックの設定で#を使うと、全部の文字に合致する意味になるので、このブローカーに流れている全てのメッセージが表示されます。世界中の人たちがこのブローカーを使ってテストしている様子がわかります。

それっぽい名前のトピック、例えばtestとかsensorで始まるトピックに限定してみると、この記事を書いた時点では次のような情報が現れました(testの場合)。

mosquitto_sub -h mqtt.eclipseprojects.io -t test/# -v
test/dht11/temp_c 24.50
test/dht11/temp_f 76.10
test/dht11/humidity 53.00

トピックは任意の文字列なのですが、慣例としてスラッシュで区切って階層的に使います。-tで指定したtest/#というパラメータの意味は、test/で始まる全てのトピックという意味です。また-vオプションは、メッセージだけでなく、それのトピック名も表示するという意味です。その結果、誰かがtest/dht11/の名前で温度と湿度のデータを流しているようです。

ではこのMQTTブローカーに、何かデータを流してみましょう。他の人と被らないように、diysmarthomeというトピックをつけて流してみます。まずは、

mosquitto_sub -h mqtt.eclipseprojects.io -t diysmarthome/# -v

でサブスクライブしておきます。別のシェルウィンドウから、

mosquitto_pub -h mqtt.eclipseprojects.io -t diysmarthome -m hello
mosquitto_pub -h mqtt.eclipseprojects.io -t diysmarthome/sw -m on   
mosquitto_pub -h mqtt.eclipseprojects.io -t diysmarthome/sw -m off

とパブリッシュすると(-mオプションがメッセージを表します)、サブスクライブ側には、

mosquitto_sub -h mqtt.eclipseprojects.io -t diysmarthome/# -v 
diysmarthome hello
diysmarthome/sw on
diysmarthome/sw off

と表示されます。この公衆MQTTブローカーに流れる情報は、誰でも見ることができますし、トピックがバッティングする可能性もありますので、実際のスマートホームの制御には適してません。でも、動作チェックの目的なら十分に使えます。以下で試してみます。

HomebridgeでMQTTを使う

MQTTをHomeKitから使うために、HomebridgeにMQTTプラグインを追加します。HomegridgeにはMQTTを使うプラグインが多数あります。プラグインのページでmqttを検索すると30個見つかりました。

ここではHomebridge Mqttthingというプラグインを使ってみます。このプラグインで、HomeKitで定義されるほとんどのデバイスを作ることができ、その動作をMQTTトピック・メッセージに紐付け設定できます。このプラグインをインストールし、設定画面を開きます。アクセサリを追加するボタンを選び、Light bulb - on/offというアクセサリを作ってみます。名前はMQTT Lightにします。

URLには公衆MQTTブローカーのアドレスを入れておきます。ユーザ名やパスワードは不要なので空白にしておきます。

トピック名も決めておきます。タグで指定された名前そのままの、わかりやすい名前にしておきました。

この結果、configファイルには以下のような記述が書き込まれました。

{
    "type": "lightbulb-OnOff",
    "name": "MQTT Light",
    "url": "mqtt://mqtt.eclipseprojects.io",
    "topics": {
        "getOnline": "diysmarthome/getonline",
        "getOn": "diysmarthome/geton",
        "setOn": "diysmarthome/seton"
    },
    "accessory": "mqttthing"
}

Homebridgeを再起動すると、iPhoneMacのホーム.appにこのアクセサリが現れます。

このアクセサリを操作する前に、Ubuntu, Windows, Macなどのシェルウィンドウで以下のコマンドを動かしておきます。

mosquitto_sub -h mqtt.eclipseprojects.io -t diysmarthome/# -v

ここで、ホーム.appからこのアクセサリをタップまたはクリックすると、表示はオン・オフするのですが、

  --->  

同時に、実行中のmosquitto_subのコマンドの応答として、以下のような結果が表示されて、

diysmarthome/seton true
diysmarthome/seton false

MQTTブローカーにデータが流れていることがわかります。このプラグインでは、デフォルトではtrue、falseという文字列が流されます(設定で変更可能です)。ホーム.appの操作で、このアクセサリがオンになるとsetonトピックにtrueと言うメッセージが、オフになるとfaseが流れてきます。

では、先ほど設定したgetonの方も試してみます。別のシェルウィンドウから、

mosquitto_pub -h mqtt.eclipseprojects.io -t diysmarthome/geton -m true

とタイプしてみます。ホーム.appの表示がオンになります。また-mとしてfalseを送ると表示がオフになります。

  --->  

ここで実行したmosquitto_sub, mosquitto_pubコマンドに相当するプログラムをArduinoやESP 32の上に書けば、on/off型の電球の動きをするアクセサリが作れるわけです。

MQTTでLチカする

ここで作ったMQTT Lightに応答するアクセサリを、ESP 32を使って作ります。そのために、Arduino IDEにMQTTを扱うためのライブラリを導入します。Arduino IDEの「ツール」「ライブラリを管理...」からライブラリマネージャを開き、ここでespmqttで検索すると、ライブラリが見つかります。このEspMQTTClientライブラリを使います。

ライブラリのサイトで配布されているサンプルをもとに、以下のように、LEDを点滅させるプログラムを作りました。

#include "EspMQTTClient.h"

EspMQTTClient *client;

const int LED=13; //pin for LED
const char SSID[] = "XXXXXXXX"; //WiFi SSID
const char PASS[] = "xxxxxxxx"; //WiFi password
char CLIENTID[] = "diysmarthome_cv3a2JTs";//unique ID

const char  MQTTADD[] = "mqtt.eclipseprojects.io"; //Broker addr
const short MQTTPORT = 1883; //Broker port
const char  MQTTUSER[] = "";//Can be omitted if not needed
const char  MQTTPASS[] = "";//Can be omitted if not needed
const char  SUBTOPIC[] = "diysmarthome/seton"; //sub topic
const char  PUBTOPIC[] = "diysmarthome/geton"; //pub topic 
const char DBGTOPIC[] = "diysmarthome/debug"; //debug topic  

void setup() {
  pinMode(LED, OUTPUT);
  client = new EspMQTTClient(SSID,PASS,MQTTADD,MQTTUSER,MQTTPASS,CLIENTID,MQTTPORT); 
}

void onMessageReceived(const String& msg) {
  client->publish(DBGTOPIC, "Message received.");
  if(msg.compareTo("true")==0) {
    digitalWrite(LED, HIGH);
    client->publish(PUBTOPIC,"true");
  }
  else if(msg.compareTo("false")==0) {
    client->publish(PUBTOPIC,"false");
    digitalWrite(LED, LOW);
  }
}

void onConnectionEstablished() {
  client->subscribe(SUBTOPIC, onMessageReceived); //set callback 
  client->publish(DBGTOPIC, "Connection established.");
}

void loop() {
  client->loop();
  delay(100);//動作安定のため
}

CLIENTIDは、デバイス固有のIDにしておくべきらしいです。MACアドレスを使うことが多いらしいですが、ここではランダムな文字列を含ませておきました。

onConnectionEstablishedは、MQTTへのコネクションが成立したときに呼ばれる関数のようです。ここで、サブスクライブしたトピックにメッセージが到着したときに呼ばれる関数を、コールバック関数として登録します。

onMessageReceivedは、そのコールバック関数です。サブスクライブしたトピックにメッセージが流されると呼ばれます。メッセージの内容を判断してLEDを点滅してます。

メインloopの100ms遅延は、動作安定のために入れました。これを入れないでフルスピードでclient->loop()を呼び出すと、onConnectionEstablishedが何度も呼ばれてしまいました。インターネットの遅延に対して、接続できなかったと勘違いして再接続している様子でした。それで100ms遅延を入れました。

このプログラムをESP 32にダウンロードして動かすと、iPhoneMacのホームのボタン操作で、

 

  --->    --->  

 

LEDが点滅します(写真は使い回しです)。

mosquitto_subの命令を稼働させておけば、対応するトピックの情報が見られます。

mosquitto_sub -h mqtt.eclipseprojects.io -t diysmarthome/# -v
diysmarthome/debug Connection established.
diysmarthome/seton true
diysmarthome/debug Message received.
diysmarthome/geton true
diysmarthome/seton false
diysmarthome/debug Message received.
diysmarthome/geton false

デバッグのために、/degugと言うトピックを使いました。デバッグメッセージはシリアルポートに流すことが多いですが、こうすると、MQTTで動作状態を確認することができます。

このESP 32のLEDは、mosquitto_pubコマンドからもon/offできます。

% mosquitto_pub -h mqtt.eclipseprojects.io -t diysmarthome/seton -m true

% mosquitto_pub -h mqtt.eclipseprojects.io -t diysmarthome/seton -m false

LEDが点滅するだけでなく、ホーム.appの表示も同期します。

    --->  

まとめ

MQTTブローカーを開発しているEclipse Projectの公衆MQTTブローカーを使って、HomeKitの電球アクセサリを作りました。MQTTはいろいろなIoT機器や関連するサーバーが採用しています。データをモニターするのも楽なので、デバッグも捗ります。今回は公衆のブローカーを使いましたが、通常はLAN内に設置します。次は、スマートホームサーバを動かしているRaspberry PiUbuntuサーバにMQTTブローカーを設置します。