74
Help us understand the problem. What are the problem?

More than 5 years have passed since last update.

posted at

updated at

家に大きめの信楽焼のタヌキが送られてきたので、Raspberry Pi仕込んでRails&Herokuでしゃべるタヌキにしたお話。

(ありがとうございます。)参考にさせていただきました。

引っ越し祝いに大きめのエジプト神像を送りつけられたのでラズパイを仕込んで喋れるようにした
http://qiita.com/sngazm/items/c27b7745fb68139213c2

こんな感じ

定刻イベント(毎時00分にランダムに登録されたメッセージを読み上げます。)

マザーテレサからのメッセージ
IMAGE ALT TEXT HERE

定刻イベント(毎時00分にランダムに登録されたメッセージを読み上げます。)

ドラえもんからのメッセージ
IMAGE ALT TEXT HERE

朝7:00に天気予報を読み上げる

IMAGE ALT TEXT HERE

お掃除音楽 夜8:00にドリフ盆回しを爆音で。(本体は30分ループです。)

IMAGE ALT TEXT HERE

1:準備したハードウェア

タヌキの中身
IMG_6033.JPG

・Raspberry Pi 2 Model B
・USBドングル(wifi)
・USBマウス(家にあるもの)
・UBSキーボード(家にあった)
・スピーカー
・MicroSDカード(16GB)
・HDMIケーブル
・Micro USBケーブル
新しく買ったものは、ラズパイとUSBドングルくらいでしたので、制作費としては、8000円くらいでした。全部Amazonで買えました。

1−2:ハードウェアのセットアップ

以下のサイトを参考にし、そのまま実行しました。動くようになりましたが、セットアップは時間がかかります。また注意事項としてMicroSDにNOOBSを入れてスロットに入れておかないと立ち上がりません。

・参考にしたサイト:https://liginc.co.jp/261317
・NOOBS(インストーラ):https://www.raspberrypi.org/downloads/noobs/

インストールに際し、特に苦慮したことはありませんでした。モニタは自宅のテレビを使用しました。(HDMI付)電源はIphoneとかの充電器を利用しましたが、一応規格が2.1A以上を推奨しているようなので要確認です。私は確認せずにテレビのUSBケーブルから給電してました。

2:仕組み

ラズパイ自体に色々仕込もうかと思いましたが、更新性がある方が面白いのでRailsで作って、Herokuに上げてそれをインターネットに接続されたラズパイが見に行く仕組みにしました。

TNK.png

3:しゃべる仕組み(Open Jtalk)を作ろう

ホルス神像様と同じくOpen Jtalkを採用させていただきました。

私はRubyを使うので、それで動く仕組みを組めないかと思いましたが、すでに先人が作ってくださいました。ありがとうございます。

open_jtalk-ruby
https://github.com/sunny4381/open_jtalk-ruby/blob/master/README.md

これをRailsのコントローラーに仕込みますと以下のような感じ。

posts/show.html.erb
<p>
  <%= @post.name %>
</p>

<p>
  <%= @post.body %>
</p>

<%= audio_tag("/a#{@post.id}.wav", :autoplay => true, :controls => true) %>
posts_controller.rb
def show


    time = Time.zone.now

    require 'open_jtalk'


    text ="どうもたぬきです。お便りのご紹介の時間だたぬ。"+@post.name+"さんからのメッセージです。"+@post.body+
    "   以上です。どうぞよろしくお願いいたぬます。"

      OpenJtalk.load(OpenJtalk::Config::Mei::NORMAL) do |openjtalk|
      header, data = openjtalk.synthesis(openjtalk.normalize_text(text))



      OpenJtalk::WaveFileWriter.save("public/a#{@post.id}.wav", header, data)




    end

うむしゃべる。

4-1:タイムスケジュールでしゃべるようにつくろう。

とりあえずはタイムスケジュールを組み時間になったらしゃべるようにします。
以下の感じで。

rate.png

私は定刻に飲む薬があるので、時間になったら教えてくれると助かります。
あと、子供が片付けをする時間にお片づけの曲を流したいので、それをセットします。その他はひとまず00分になったら、なんかしゃべるようにします。

まあ、この辺はほとんど鳩時計みたいなものですね。

4-2 使用したライブラリ

JAVASCRIPT で CRON ちっくに指定時間で実行
http://yurubu.org/javascript-cron/

これをルートのページに設置して、時間になったらメッセージ読み上げページに
移動する仕組みとしました。

posts/index.html.erb
script>


    var jsCron = {
        items:[],
        interval: null,
        parse: function(strUnix) {
            return strUnix.match(/^(\d+|\*) (\d+|\*) (\d+|\*) (\d+|\*) (\d|\*) +(\w+)/);
        },
        check: function() {
            var hoy = new Date();
            var test = [new Date(), hoy.getMinutes(), hoy.getHours(), hoy.getDate(), hoy.getMonth(), hoy.getDay()];

            for (var i in this.items) {
                var exec = 0;
                var t = this.parse(this.items[i][1]);
                for (var x in t)
                    if (t[x] && (t[x] == test[x] || t[x] == "*"))exec++;
                if (exec == 5 && this.items[i][0] == 0) {
                    eval(t[6]).call();
                    this.items[i][0] = 1;
                } else if (exec < 5 && this.items[i][0] == 1) {
                    this.items[i][0] = 0;
                }
            }
        },
        set: function(strUnix) {
            if (!/^(\d+|\*) (\d+|\*) (\d+|\*) (\d+|\*) (\d|\*) +(\w+)/.test(strUnix)) return new Error("Formato invalido");
            this.items.push([0, strUnix]);
        },
        init: function(seg) {
            var seg = seg || 1000;
            this.interval = setInterval("jsCron.check()", seg);
        }
    };
    jsCron.init();



    function nEffect() {
        audioElem = new Audio();
        audioElem.src = "/we.wav";
        audioElem.play();

    }
    // イベントの作成 (定刻メッセージ読み上げページへ移動)

    function mEffect() {
        { window.location.href = "/posts/<%=@randpost.id%>" ; };


    }

    // イベントの作成 (お天気予報読み上げページへ移動)

    function wEffect() {
        { window.location.href = "/posts/weather_news" ; };


    }

    // イベントの作成 (お薬アラーム読み上げページへ移動)

    function dEffect() {
        { window.location.href = "/posts/drug" ; };


    }

    // イベントの作成 (お掃除アラーム読み上げページへ移動)

    function cEffect() {
        { window.location.href = "/posts/clean_up" ; };


    }

//この辺で時間を指定してページを移動(分:時)

    jsCron.set("00 05 * * * mEffect()");
    jsCron.set("00 06 * * * mEffect()");
    jsCron.set("00 07 * * * wEffect()");
    jsCron.set("00 08 * * * mEffect()");
    jsCron.set("00 09 * * * mEffect()");
    jsCron.set("30 09 * * * mEffect()");
    jsCron.set("00 10 * * * mEffect()");
    jsCron.set("00 11 * * * mEffect()");
    jsCron.set("00 12 * * * mEffect()");
    jsCron.set("00 13 * * * mEffect()");
    jsCron.set("00 14 * * * mEffect()");
    jsCron.set("00 15 * * * mEffect()");
    jsCron.set("00 16 * * * mEffect()");
    jsCron.set("00 17 * * * mEffect()");
    jsCron.set("00 18 * * * mEffect()");
    jsCron.set("00 19 * * * mEffect()");
    jsCron.set("00 20 * * * mEffect()");
    jsCron.set("45 20 * * * cEffect()");
    jsCron.set("00 21 * * * mEffect()");
    jsCron.set("30 21 * * * dEffect()");
    jsCron.set("18 23 * * * mEffect()");
</script>

あまりニーズがあるかどうかはわからないので、ざっくりとメモ程度に作成しました。
もしご質問など頂けましたら回答させていただきます。

随時追記させていただきます。どうぞよろしくお願いいたします。

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Sign upLogin
74
Help us understand the problem. What are the problem?