Nervesを使って毎朝天気予報をRaspberry Pi 2にしゃべらせる(できた!)


(この記事は、「#NervesJP Advent Calendar 2019」の10日目です)
昨日はの「Nervesを使ってRaspberry Pi 2でLEDをチカチカさせる 〜クリスマスの飾り付けをしよう〜」です!こちらもぜひぜひ!

2020/11/04 追記

はじめに

  • Nervesを使って毎朝天気予報をRaspberry Pi 2にしゃべらせてみます
  • できました
    • NervesElixirのIoTでナウでヤングなクールなすごいやつです

まとめ

iex(1)> :os.cmd('espeak -ven+f5 -k5 -w /tmp/out.wav IwasWornToLoveElixir')
iex(2)> :os.cmd('aplay -q /tmp/out.wav')
  • これでできたも同然でしょう!
  • 天気予報を取得して、音声合成してファイルに保存して、aplayコマンドを実行するのみです

感謝

  • livedoor様のお天気Webサービス ありがとうございます!
  • docomo Developer support様の 音声合成【Powered by NTTテクノクロス】 ありがとうございます!
  • Nerves ありがとうございます!
  • Elixir ありがとうございます!
  • Erlang ありがとうございます!
    • あなたはまだ直接書いたことがありません
  • Raspberry Pi 2 ありがとうございます!
  • アドミラル・グレース・マーレー・ホッパー ありがとうございます!
  • アラン・チューリング ありがとうございます!
  • ジョン・フォン・ノイマン ありがとうございます!
  • 私の知識不足によりここにあげられていない数多くの発明があることでしょう
  • 感謝します!
  • 私にプログラミングの楽しさを教えてくれたすべてのコト、モノ、人に感謝します!
    • 一見何も関係なかったようにみえる出来事もきっと私がプログラムを少しだけ書けるようになったことに関係しているのでしょう
  • ひとつとして欠けると私には何もできなかったことでしょう
  • あなたがいて、私がいる。私がいてあなたがいる。すべてに感謝ーーすべてはつながっているーー縁起

作品

音声サンプル

iex(1)> c "audio_sample.exs"
iex(2)> AudioSample.create
  • とやってみてください。.wavファイルが2つできているので再生プレイヤーで再生してみてください。
  • macOS 10.14.6では再生(iTunes)できました
  • Elixirは、Elixir 1.9.4 (compiled with Erlang/OTP 22)を使いました

関連記事

対応内容

天気予報取得

  • livedoor様のお天気Webサービス を利用させていただいております
  • HTTP Getして返されたJSONから、"description" |> "text" とたどって値を取得しています

Weather.Forecast.text()

lib/weather/forecast.ex
defmodule Weather.Forecast do
  def text(city) do
    "http://weather.livedoor.com/forecast/webservice/json/v1?city=#{city}"
    |> HTTPoison.get!()
    |> Map.get(:body)
    |> Poison.decode!()
    |> Map.get("description")
    |> Map.get("text")
  end

音声合成

Torifuku.TextToSpeech.text_to_speech()

lib/torifuku/text_to_speech.ex
defmodule Torifuku.TextToSpeech do
  # Please refer to https://dev.smt.docomo.ne.jp/?p=docs.api.page&api_name=text_to_speech&p_name=api_7#tag01 .
  def text_to_speech(
        text,
        speaker_id \\ 1,
        style_id \\ 1,
        speech_rate \\ 1.0,
        power_rate \\ 1.0,
        voice_type \\ 1.0,
        audio_file_format \\ 2
      ) do
    url =
      "https://api.apigw.smt.docomo.ne.jp/crayon/v1/textToSpeech?APIKEY=#{
        Application.get_env(:hello_nerves, :text_to_speech_api_key)
      }"

    headers = [{"Content-Type", "application/json"}]

    json_data =
      %{
        Command: "AP_Synth",
        SpeakerID: speaker_id,
        StyleID: style_id,
        TextData: text,
        SpeechRate: speech_rate,
        PowerRate: power_rate,
        VoiceType: voice_type,
        AudioFileFormat: audio_file_format
      }
      |> Poison.encode!()

    HTTPoison.post!(url, json_data, headers) |> Map.get(:body)
  end
end

組み合わせる

  • 天気情報文字列の取得と音声データに変換 はできたのであとは部品を組み合わせます
  • もちろん |> で組み合わせましょう!
  • Nervesで動かす場合、/tmp配下にファイルを書き込めるようです
  • ファイルを書き込む位置と音声ファイルの再生につかうコマンドを if macroで場合わけしていますが、Nervesじゃないときの動作はmacOSでしか確認していないです

HelloNerves.sound_forecast

lib/hello_nerves.ex
defmodule HelloNerves do
  def sound_forecast(city) do
    content =
      Weather.Forecast.text(city)
      |> String.split()
      |> Enum.take(2)
      |> Enum.join()
      |> Torifuku.TextToSpeech.text_to_speech()

    if Application.get_env(:hello_nerves, :target) != :host do
      File.write("/tmp/output.wav", content)
      :os.cmd('aplay -q /tmp/output.wav')
    else
      # macOS
      File.write("output.wav", content)
      :os.cmd('afplay output.wav')
    end
  end

焼き込む

$ export MIX_TARGET=rpi2
$ export NERVES_NETWORK_SSID=ssid
$ export NERVES_NETWORK_PSK=secret
$ export TWITTER_CONSUMER_KEY=xxx
$ export TWITTER_CONSUMER_SECRET=yyy
$ export TWITTER_ACCESS_TOKEN=zzz
$ export TWITTER_ACCESS_TOKEN_SECRET=aaa
$ export TEXT_TO_SPEECH_API_KEY=ttt
$ mix deps.get
$ mix firmware
$ mix firmware.burn

WiFi越しにfirmware updateできます!

  • pojiroさんの作って学ぶNerves、BBBでCO2計測の記事で知りました!
    • ありがとうございます!
  • つい先日までこれを知らなくて、毎回、Raspberry Pi 2からmicroSDカードを抜いて、macに挿して焼いて、macから抜いて、Raspberry Pi 2に挿して電源ONを毎回繰り返していました
$ mix firmware.gen.script            # upload.shができています
$ mix firmware
$ ./upload.sh 192.168.1.10

次回

今年あらかじめ事前登録していた分はすべて書ききりました!