LoginSignup
3

More than 3 years have passed since last update.

Slack のメッセージと RaspberryPi の音声を相互変換する

Last updated at Posted at 2019-10-09

適切なタイトルをどうつけようか悩んだ。

Slack から Amazon Echo の Alexa を操作したいなぁと思って作った。
かれこれ作ってから1年くらい経ってしまったけど・・、ようやくアウトプット。

実現したいこと

  • Slack のメッセージを RaspberryPi のスピーカーから音声として出力する
  • RaspberryPi のマイクへの音声を Slack へメッセージとして送信する

先に結果

image.png

  • Slack のメッセージをスピーカー再生ができた
  • マイクの入力音声を、Slack のメッセージで通知できた

※ 相手が、Alexa を想定したイメージになっているが、実際に Amazon Echo は無いので、自分の声でテストしている

全体構成

Untitled (1).png

Slack to RaspberryPi

AWS を用いて構成する。
Slack の Webhook 受信に Amazon API Gateway を、
RaspberryPi との送受信には、AWS IoT Core を用いた。

MQTTトピック

Lambda → AWS IoT → RaspberryPi

下記のトピックを使用。
raspberrypi/request/#

テキスト→音声再生のトピックで通知。
raspberrypi/request/speak

RaspberryPi → AWS IoT → Lambda

下記のトピックを使用する。
raspberrypi/response

処理終了後 ACK メッセージを応答送信する。

RaspberryPi

python スクリプトをデーモン化し、systemd でサービス化。
RaspberryPi には、イヤホン端子スピーカを接続。

セットアップ

  • config を設定
  • AWS IoT Core の接続情報、SSL証明書を設定
  • AWS Amazon polly のアクセスキー/シークレットキーを設定

処理の流れ

  • paho ライブラリを使用して、AWS IoT Core へ MQTT subscribe
  • MQTT メッセージ受信
  • AWS SDK for Python boto3 を用いて、Amazon Polly でテキスト→音声変換
    • 変換後の音声ファイルは、ファイルシステムに一時保存
  • pyaudio ライブラリで、保存したmp3ファイルをスピーカー再生
    • 再生待ち処理をスレッド化、再生終了後にコールバック
  • 要求元の Slack 向けに、ACKメッセージを MQTT publish
  • 音声再生終了コールバック処理で、録音プロセス向けに、録音開始指示を MQTT publish(後述)

Paho - Pyton MQTT Client

下記を参考に。
MQTT with AWS IoT Platform using Python and Paho
https://iotbytes.wordpress.com/mqtt-with-aws-iot-using-python-and-paho/
https://github.com/pradeesi/AWS-IoT-with-Python-Paho

Amazon Polly

synthesize_speech でテキスト→音声変換
https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/polly.html#Polly.Client.synthesize_speech

        response = self.client.synthesize_speech(
            Text = text,
            OutputFormat = 'mp3',
            VoiceId = voice
        )

Text には、Slack から 受信したメッセージを設定する。

mp3 生成処理は、下記を参考に。
https://dev.classmethod.jp/cloud/aws/change-polly-voice-using-boto3/

mp3 再生

mp3 の再生は下記を参考に。
https://deviceplus.jp/hobby/raspberrypi_entry_012/

python からの再生は下記を参考に。
https://qiita.com/Nyanpy/items/cb4ea8dc4dc01fe56918

Tips:pygame の再生終了待ちをスレッドで行う

下記等を参考にした。
http://d.hatena.ne.jp/kadotanimitsuru/20090216/thread
https://www.raspberrypi.org/forums/viewtopic.php?t=46096

ソースコード

下記に。
https://github.com/nilesflow/AwsIoTSpeaker/

AWS IoT

セットアップ

  • RaspberryPi 用の things を作成
  • 証明書を作成
  • ダウンロードした証明書とroot証明書、エンドポイントを控えておく

Lambda

RaspberryPi と合わせて、Python で構築。
API Gateway からの要求を AWS IoT Core にMQTT送信。
応答メッセージを Slack へ返却するために、
Lambda 関数内でしばらく ACK 待ちを行っているのがポイント。
AWS IoT Core と 統合している訳ではないので、デプロイ時に MQTT 接続用のSSL証明書類をアップロードしている。
RaspberryPi からの通知でも同じ関数を使用しているので、処理分岐している。

セットアップ

  • Lambda 関数をコンソール、または
  • EC2 から AWS CLI でアップロード
  • 環境変数
    • SUBSCRIBE_HOST:AWS IoT Core のエンドポイント、yourhostname.iot.region.amazonaws.com
    • SUBSCRIBE_CAROOTFILE:同ルート証明書、certs/root-CA.crt
    • SUBSCRIBE_CERTFILE:同SSL証明書、certs/certificate.pem.crt
    • SUBSCRIBE_KEYFILE:同SSL証明書鍵、certs/private.pem.key

処理の流れ

  • API Gateway からリクエストを受信
  • 呼び出し元が、Slack か RaspberryPi を判定
  • paho ライブラリを使用して、AWS IoT Core へ MQTT subscribe (ACK 受信用)
    • トピックは、raspberrypi/response
  • paho ライブラリを使用して、AWS IoT Core へ MQTT publish
    • トピックは、raspberrypi/request/speak
  • ループ処理で sleep して、応答を待つ
  • paho ライブラリで MQTT 応答メッセージ受信
  • 正しい応答かどうかを確認
  • API Gateway へ 応答メッセージを返却
  • 応答メッセージは、slack 上にメッセージテキストとして表示される
    • 例えば、指定されたテキストを読み上げました。invoked xxxxxx

応答について

API Gateway 側でプロキシ統合を選んだ場合は、Lambda 側で実装するが、
API Gateway 側で吸収させるなら、既定の形式で返す必要がある。

下記等を参考。
https://qiita.com/taknuki/items/dd47d1c6d4190b52df9a#%E3%83%AC%E3%82%B9%E3%83%9D%E3%83%B3%E3%82%B9

ソースコード

API Gateway

Slack の Outgoing Webhooks を受信可能なエンドポイントを定義する。

  • リソース
    • /raspberrypi/{proxy+} でリソース定義
    • POST
  • メソッドリクエスト
    • 認可:AuthorizationRequestToken で Lammbda 関数を利用
    • リクエストの検証:クエリ文字列パラメータおよびヘッダーの検証
    • APIキーの必要性:false
    • URL クエリ文字列パラメータ:token を必須
    • HTTP リクエストヘッダー:Content-Type
  • 統合リクエスト
    • 統合タイプ:Lambda
    • Lambda プロキシ統合の使用:しない
    • Lambda 関数は、上述の関数を指定
    • URLパスパラメータ:method.request.path.proxy
    • マッピングテンプレート
      • リクエスト本文のパススルー:なし
      • application/x-www-form-urlencoded:ソースコード参照
        • Slack のPOSTパラメータから、Lambda の引数へのマッピングを行っている
  • 統合レスポンス
    • 200 のマッピングテンプレート
      • application/json:ステータスコードが 200 かどうかを判定する
        • Lambda の応答から、Slack への応答JSON形式へのマッピングしている
  • メソッドレスポンス

    • 応答を返却できるように下記のステータスコードを定義
      • 200
      • 400
      • 500
  • ステージ作成

    • 必要に応じて、devprod を作成
  • オーソライザー

    • AuthorizationRequestToken を作成
      • Lambda 関数:APIGateway-Authorization
        • ソースコード参照
      • IDソース:クエリ文字列:token
      • 認可のキャッシュ:有効
      • TTL(秒):300
  • カスタムドメイン名

    • 必要に応じて作成
    • ACM 証明書も配置する

ソースコード

https://github.com/nilesflow/RaspberryPiClient/tree/master/API_Gateway
https://github.com/nilesflow/RaspberryPiClient/tree/master/Lambda/APIGateway-Authorization

Slack Outgoing Webhooks

slack標準のOutgoing Webhookを使用。

セットアップ

  • チャンネル:対象のチャンネルを指定
  • 引き金となる言葉:「alexa,Alexa,ALEXA,アレクサ,あれくさ」等
  • URL:API Gateway のエンドポイントと token を指定
    • https://{your domain}/raspberrypi/speak?token={your token}
  • トークン:生成してURLパラメータに設定
  • 説明ラベル:任意
  • 名前をカスタマイズ:任意
  • アイコンをカスタマイズする:任意

参考

送信 POSTパラメータ等、下記。
https://api.slack.com/custom-integrations/outgoing-webhooks

RaspberryPi to Slack

Azure と AWS を用いて構成する。
RaspberryPi から Slack への通知には、同じく、AWS IoT Core を利用している。

MQTTトピック

RaspberryPi → AWS IoT → RaspberryPi

下記のトピックを使用。
raspberrypi/request/#

音声録音のトピックで通知。
音声再生デーモン処理終了時に発行される。
raspberrypi/request/listen

RaspberryPi → AWS IoT → Lambda

下記のトピックを使用する。
raspberrypi/response

テキスト変換処理終了後 通知メッセージを送信する。
raspberrypi/notify

Slack Incoming Webhooks

slack標準のIncoming Webhookを使用。

セットアップ

  • チャンネルへの投稿:対象の通知チャンネルを指定
  • Webhook URL:生成、後ほど Lambda に入力
  • 説明ラベル:任意
  • 名前をカスタマイズ:任意
  • アイコンをカスタマイズする:任意

参考

送信 JSON フォーマット等、下記。
https://api.slack.com/incoming-webhooks

Lambda

同じ関数を使用。

AWS IoT Core を通じた RaspberryPi からの MQTT メッセージを処理、
Slack Incoming Webhooks の URL へ送信する。

セットアップ

  • 同じ Lambda 関数を使用
  • 環境変数
    • SLACK_WEBHOOK_URL:Slack の Webhook URL、上述。

処理の流れ

  • AWS IoT Core から統合リクエストを受信
  • 呼び出し元が、Slack か RaspberryPi を判定
  • 指定の JSON 形式で、HTTP リクエスト送信

ソースコード

AWS IoT

セットアップ

  • ACT を作成:RaspberryPiNotification
  • ルールクエリステートメント:SELECT * FROM 'raspberrypi/notify'
  • アクション:上述の Lambda 関数を指定
  • エラーアクション:任意

Azure Speech Service

セットアップ

  • Azure ポータル
  • Cognitive Services から追加
  • Speech を選択
  • キーとエンドポイントを控えておく

RaspberryPi

python スクリプトをデーモン化し、systemd でサービス化。
RaspberryPi には、USBマイクを接続。

セットアップ

  • config を設定
  • AWS IoT Core の接続情報、SSL証明書を設定
  • Azure Speech Service のアクセスキーを設定

処理の流れ

  • paho ライブラリを使用して、AWS IoT Core へ MQTT subscribe
    • 接続認証のため、AWS IoT Core で発行したSSL証明書を配置
  • MQTT メッセージ受信
  • 音声録音処理
    • pyaudio ライブラリで音声録音
      • 音を検知したら録音データ取得
      • 無音が暫く続いたら、またはタイムアウトで終了
    • wav ファイルを生成
  • 音声→テキスト変換処理
    • cognitive-services の speech-service REST API をコール
      • wav ファイル → テキスト変換処理
  • 結果を MQTT publish で AWS IoT Core へ送信

H/W情報

$ arecord -l
**** ハードウェアデバイス CAPTURE のリスト ****
カード 1: Device [USB PnP Sound Device], デバイス 0: USB Audio [USB Audio]
  サブデバイス: 1/1
  サブデバイス #0: subdevice #0
$ lsusb
Bus 001 Device 004: ID 8086:0808 Intel Corp.
Bus 001 Device 003: ID 0424:ec00 Standard Microsystems Corp. SMSC9512/9514 Fast Ethernet Adapter
Bus 001 Device 002: ID 0424:9514 Standard Microsystems Corp. SMC9514 Hub
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
$ cat /proc/asound/modules
 0 snd_bcm2835
 1 snd_usb_audio
$ amixer -c 1 sget Mic
Simple mixer control 'Mic',0
Capabilities: cvolume cvolume-joined cswitch cswitch-joined
Capture channels: Mono
Limits: Capture 0 - 16
Mono: Capture 0 [0%] [0.00dB] [on]

$ amixer -c 1 sset Mic 60%
Simple mixer control 'Mic',0
Capabilities: cvolume cvolume-joined cswitch cswitch-joined
Capture channels: Mono
Limits: Capture 0 - 16
Mono: Capture 10 [62%] [14.88dB] [on]

$ amixer -c 1 sget Mic
Simple mixer control 'Mic',0
Capabilities: cvolume cvolume-joined cswitch cswitch-joined
Capture channels: Mono
Limits: Capture 0 - 16
Mono: Capture 10 [62%] [14.88dB] [on]

参考

音声録音処理は、下記処理を参考にさせていただいた。
https://qiita.com/mix_dvd/items/dc53926b83a9529876f7

Azure Cognitive Services は、
https://docs.microsoft.com/ja-jp/azure/cognitive-services/speech-service/rest-apis

ソースコード

参考

こちらの方が簡単そう・・。
https://qiita.com/miya236a/items/4f56f5b3dd3d3e6a3f8e

こちらの方とは似てる気がします・・。
https://hacknote.jp/archives/39454/

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3