RubicでGR-CITRUSのmrubyプログラミング事始め５[インターネット鳩時計編]

この回で「RubicでGR-CITRUSのmrubyプログラミング事始め」は最後です。
今までのことを踏まえインターネット鳩時計を作ります。

#動作
動作は単純に

+ 起動時、NTPで時刻を取得
+ GR-CITRUSの時計を設定
+ １時間ごとに時刻のMP3ファイルをしゃべる

としたいと思います。単純！

#今回使うもの

+ GR-CITRUS
+ WA-MIKAN
+ MicroSDカード
+ 圧電素子かスピーカー
+ ブレッドボード
+ ブレッドボード・ジャンパーコード（オス－ワニグチ）×適当


#音声集め
まずは、時報の元となる音声素材を集めます。
以下のようなフリーで提供しているソフトやサイトがありますので利用させていただきます。

+ [TextTalk](http://choppli.123net.jp/textalk/)
+ [SofTalk](http://www35.atwiki.jp/softalk/pages/15.html)
とか
 
学術研究目的ならばNICTの杉浦孔明さんが開発されたrospeexなんていうのも試せます。

+ [rospeex](http://komeisugiura.jp/software/say.html)

K. Sugiura, Y. Shiga, H. Kawai, T. Misu and C. Hori: "A Cloud Robotics Approach towards Dialogue-Oriented Robot Speech," Advanced Robotics, Vol. 29, Issue 7, pp. 449-456, 2015.



有料のものならもVOICEROIDいいですね

+ [VOICEROID](https://www.amazon.co.jp/s/?_encoding=UTF8&camp=247&creative=7399&field-keywords=VOICEROID&linkCode=ur2&tag=amazon_product-22)

##音声ファイル作成
上記ソフトを使い以下セリフ用ファイルを用意しました。

|ファイル名|用途|セリフ|
|:---|:---:|:---:|
|0.mp3～23.mp3|時報用のmp3|「0時なのです」「15時でおやつの時間です」とか|
|now.mp3|時報用前置き用のmp3|「現在の時刻は」とか|

WA-MIKANのSDカード直下に以上のファイルを保存しておきます。

#スピーカーもしくは圧電素子とつなげる

[第2回](http://qiita.com/shimo_cchi/items/cfb16476ef42861b98b2)の時と同じように、GR－CITRUSと圧電素子（またはスピーカー）を接続します。

|CITRUSの端子番号|接続先|
|:---|:---:|
|0|圧電素子またはスピーカーの＋|
|GND|圧電素子またはスピーカーの―|

#Rubicに書き込み
Ruby使いじゃないのでにわかですが一応クラスっぽく書きました。
###mruby-time使用版
mrbgemsのmruby-timeを使っています。ない場合は[第4回の記事](http://qiita.com/shimo_cchi/items/645d814995c1902a2563)に従ってmruby-timeを入れるか、または次項のmruby-timeを使用しない版を使ってください。

```rb:main.rb
#!mruby
SSID="あなたのSSID"
PASS="あなたのPASS"
class Cts_ntp
 @@NTP_PACKET_SIZE = 48
 @@timeZone = 9 #Tokyo
 @@pollIntv = 150	# poll every this many ms
 @@maxPoll = 15		# poll up to this many times
 @@timeServer="129.6.15.28"  #time.nist.gov
#@@timeServer="132.163.4.101" #time-a.timefreq.bldrdoc.gov
 @@sendport = 123
 @@localport = 8788

 def initialize(params)
   @wifi          = params[:wifi] || WiFi
   @wifiport      = params[:wifiport] || 1
   @timeserver    = params[:timeserver] || @@timeServer
   @sendport      = params[:sendport] || @@sendport 
   @localport     = params[:localport] || @@localport
 end
 def wifi
  @wifi
 end
 def wifiport
  @wifiport
 end
 def timeserver
  @timeserver
 end
 def localport
  @localport
 end
 def sendport
  @sendport
 end

 def sendPacket
  packetBuffer = Array.new(@@NTP_PACKET_SIZE , 0)
  packetBuffer[0] = 0b11100011   # LI, Version, Mode , 0xE3
  packetBuffer[1] = 0     # Stratum, or type of clock
  packetBuffer[2] = 6     # Polling Interval
  packetBuffer[3] = 0xEC  # Peer Clock Precision
  packetBuffer[12]  = 49
  packetBuffer[13]  = 0x4E
  packetBuffer[14]  = 49
  packetBuffer[15]  = 52  
  binary = packetBuffer[0].chr
  for num in 1..@@NTP_PACKET_SIZE-1 do
      binary += packetBuffer[num].chr
  end
  @wifi.udpOpen(@wifiport, @timeserver, @sendport, @localport)
  @wifi.send(@wifiport,binary)
 end

 def receivePacket
  @array = @wifi.recv(@wifiport)
  @@maxPoll.times do
    delay(@@pollIntv)
    if (@array[0] != nil) then
        if (@array[0] >= 0) then
            if (@array.length == @@NTP_PACKET_SIZE) then
                break
            end  
        end
    end
    @array = @wifi.recv(@wifiport) 
  end

  if @array.length != @@NTP_PACKET_SIZE then
    "receive error"
  else
    time = @array[40]
    for i in 1..3 do
     time = time << 8 | @array[40+i]
    end
    @unixTime = time - 2208988800
    @unixTimeJST = @unixTime + (@@timeZone * 60 * 60)
    "receive ok"
  end
 end
 def unixTime
   @unixTime
 end
 def unixTimeJST
   @unixTimeJST
 end
 def packet
    @array
 end    
end

#Main

Usb = Serial.new(0)

#WiFi PART
#ESP8266を一度停止させる(リセットと同じ)
pinMode(5,1)
digitalWrite(5,0) # LOW:Disable
delay 500
digitalWrite(5,1) # LOW:Disable

if( System.useWiFi() == 0)then
  Usb.println "WiFi Card can't use."
  System.exit() 
end

Usb.println "WiFi Ready"
Usb.println "WiFi disconnect"
Usb.println WiFi.disconnect
Usb.println "WiFi Mode Setting"
Usb.println WiFi.setMode 3 #Station-Mode & SoftAPI-Mode
Usb.println "WiFi ipconfig"
Usb.println WiFi.ipconfig
Usb.println "WiFi connecting"
con=WiFi.connect(SSID,PASS)
Usb.println con
if (con != "WIFI CONNECTED\r\nWIFI GOT IP\r\n\r\nOK\r\n") then
  System.exit() 
end    
Usb.println "WiFi multiConnect Set"
Usb.println WiFi.multiConnect 1

#NTP PART
Usb.println "Ntp new"
ntp=Cts_ntp.new(wifi:WiFi,wifiport:1)
Usb.println "Send Packet"
ntp.sendPacket
Usb.println "Receive Packet"
Usb.println ntp.receivePacket
Usb.println ntp.packet.to_s
Usb.println ntp.unixTimeJST.to_s
WiFi.cClose(1)
#UNIX TIME
t = Time.at(ntp.unixTimeJST)
Usb.println t.to_s

#RTC PART
Usb.println "Rtc init"
Usb.println Rtc.init().to_s
Usb.println Rtc.setTime([t.year,t.month,t.day,t.hour,t.min,t.sec]).to_s

#LOOP PART
while true do
 ct=Rtc.getTime()
 Usb.println ct.to_s
 if (ct[4]==0 && ct[5]==0) then
    if( System.useMP3(3,4) == 0)then
      Usb.println "MP3 can't use."
      System.exit() 
    end
    f = ct[3].to_s + ".mp3" 
    Usb.println f
    3.times do
        led 1
        Usb.print MP3.play "now.mp3"
        delay(10)
        Usb.print MP3.play f
        led 0
        delay(100)
    end    
 end
 delay(10)
end
```

###mruby-timeを使用しない版

こちらはmruby-timeを使用しない版です。素のGR-CITRUSで動くと思います。

```rb:main.rb
#!mruby
SSID="あなたのSSID"
PASS="あなたのPASS"
class Cts_ntp
 @@NTP_PACKET_SIZE = 48
 @@timeZone = 9 #Tokyo
 @@pollIntv = 150	# poll every this many ms
 @@maxPoll = 15		# poll up to this many times
 @@timeServer="129.6.15.28"  #time.nist.gov
#@@timeServer="132.163.4.101" #time-a.timefreq.bldrdoc.gov
 @@sendport = 123
 @@localport = 8788

 def initialize(params)
   @wifi          = params[:wifi] || WiFi
   @wifiport      = params[:wifiport] || 1
   @timeserver    = params[:timeserver] || @@timeServer
   @sendport      = params[:sendport] || @@sendport 
   @localport     = params[:localport] || @@localport
 end
 def wifi
  @wifi
 end
 def wifiport
  @wifiport
 end
 def timeserver
  @timeserver
 end
 def localport
  @localport
 end
 def sendport
  @sendport
 end

 def sendPacket
  packetBuffer = Array.new(@@NTP_PACKET_SIZE , 0)
  packetBuffer[0] = 0b11100011   # LI, Version, Mode , 0xE3
  packetBuffer[1] = 0     # Stratum, or type of clock
  packetBuffer[2] = 6     # Polling Interval
  packetBuffer[3] = 0xEC  # Peer Clock Precision
  packetBuffer[12]  = 49
  packetBuffer[13]  = 0x4E
  packetBuffer[14]  = 49
  packetBuffer[15]  = 52  
  binary = packetBuffer[0].chr
  for num in 1..@@NTP_PACKET_SIZE-1 do
      binary += packetBuffer[num].chr
  end
  @wifi.udpOpen(@wifiport, @timeserver, @sendport, @localport)
  @wifi.send(@wifiport,binary)
 end

 def receivePacket
  @array = @wifi.recv(@wifiport)
  @@maxPoll.times do
    delay(@@pollIntv)
    if (@array[0] != nil) then
        if (@array[0] >= 0) then
            if (@array.length == @@NTP_PACKET_SIZE) then
                break
            end  
        end
    end
    @array = @wifi.recv(@wifiport) 
  end

  if @array.length != @@NTP_PACKET_SIZE then
    "receive error"
  else
    time = @array[40]
    for i in 1..3 do
     time = time << 8 | @array[40+i]
    end
    @unixTime = time - 2208988800
    @unixTimeJST = @unixTime + (@@timeZone * 60 * 60)
    "receive ok"
  end
 end
 def unixTime
   @unixTime
 end
 def unixTimeJST
   @unixTimeJST
 end
 def packet
    @array
 end    
end

class Cts_time
 def IsLeapYear(year)
  if ((year % 4)==0 && ((year % 100)!=0 || (year % 400)==0)) then
   return true
  else
   return false
  end
 end 
 def initialize(unixtime)
 # 閏年でない年の各月の日数
  dayofm  = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
  dayoflm = [31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
  monthstr = ["Jan", "Feb", "Mar", "Apr", "May", "Jun","Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
  weekstr = ["Sat","Sun", "Mon", "Tue", "Wed", "Thr", "Fri"] 
  # 日付の計算
  wt   =   unixtime
  @sec  =   wt % 60
  wt  -=   sec
  @min  = ( wt.div(60) ) % 60
  wt  -=   min
  @hour = ( wt.div(60*60)) % 24
  wt  -=   hour
  @day  = ( wt.div(60*60*24) ) 
  @year = 1970
  @month = 1

  while (@day>366) do
      if IsLeapYear(@year) then
          @day -= 366
      else
          @day -= 365
      end
      @year+=1
  end

  @day+=1 # 1 月 1 日は 0 だから

  while (1) do                                 
      if (IsLeapYear(@year)) then                # もし閏年なら 
          if (@day <= dayoflm[@month-1]) then     # 月の日数より day が少なければ
              break
          else                              #月の日数より day が多ければ 
              @day -= dayoflm[@month-1]       #月の日数を引き 
              @month+=1                      #月を 1 増やす
          end
      end
      if (!IsLeapYear(year)) then                  #もし閏年でなければ
          if (@day <= dayofm[@month-1]) then         #以下同上
              break
          else
              @day -= dayofm[@month-1]
              @month+=1
          end
      end
  end
  # Zeller's congruence
  zC = year/100
  zY = year%100
  zT = -2*zC+zC/4
  h=(@day+(26*(@month+1)/10)+zY+zY/4+zT) % 7
  @week=weekstr[h] 
  @weekno=h
  @today=@year.to_s + "/" + @month.to_s + "/" + @day.to_s + " " + @hour.to_s + ":" + @min.to_s + ":" + @sec.to_s + " " + monthstr[@month-1] +" "+@week
 end
 def today
   @today  
 end     
 def sec
   @sec.to_i
 end
 def min
   @min.to_i
 end
 def hour
   @hour.to_i
 end
 def day
   @day.to_i
 end
 def month
   @month
 end
 def year
   @year
 end 
 def week
  @week
 end
 def weekno
   @weekno.to_i
 end 
end

#Main

Usb = Serial.new(0)

#WiFi PART
#ESP8266を一度停止させる(リセットと同じ)
pinMode(5,1)
digitalWrite(5,0) # LOW:Disable
delay 500
digitalWrite(5,1) # LOW:Disable

if( System.useWiFi() == 0)then
  Usb.println "WiFi Card can't use."
  System.exit() 
end

Usb.println "WiFi Ready"
Usb.println "WiFi disconnect"
Usb.println WiFi.disconnect
Usb.println "WiFi Mode Setting"
Usb.println WiFi.setMode 3 #Station-Mode & SoftAPI-Mode
Usb.println "WiFi ipconfig"
Usb.println WiFi.ipconfig
Usb.println "WiFi connecting"
con=WiFi.connect(SSID,PASS)
Usb.println con
if (con != "WIFI CONNECTED\r\nWIFI GOT IP\r\n\r\nOK\r\n") then
  System.exit() 
end    
Usb.println "WiFi multiConnect Set"
Usb.println WiFi.multiConnect 1

#NTP PART
Usb.println "Ntp new"
ntp=Cts_ntp.new(wifi:WiFi,wifiport:1)
Usb.println "Send Packet"
ntp.sendPacket
Usb.println "Receive Packet"
Usb.println ntp.receivePacket
Usb.println ntp.packet.to_s
Usb.println ntp.unixTimeJST.to_s
WiFi.cClose(1)
#UNIX TIME
time=Cts_time.new(ntp.unixTimeJST)
Usb.println time.today

#RTC PART
Usb.println "Rtc init"
Usb.println Rtc.init().to_s
Usb.println Rtc.setTime([time.year,time.month,time.day,time.hour,time.min,time.sec]).to_s

#LOOP PART
while true do
 ct=Rtc.getTime()
 Usb.println ct.to_s
 if (ct[4]==0 && ct[5]==0) then
    if( System.useMP3(3,4) == 0)then
      Usb.println "MP3 can't use."
      System.exit() 
    end
    f = ct[3].to_s + ".mp3" 
    Usb.println f
    3.times do
        led 1
        Usb.print MP3.play "now.mp3"
        delay(10)
        Usb.print MP3.play f
        led 0
        delay(100)
    end    
 end
 delay(10)
end
```

#完成
<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">GR-Citrusで時報を喋らせてみました。音声はrospeexを利用させていただきました。<a href="https://twitter.com/hashtag/gr_citrus?src=hash">#gr_citrus</a> <a href="https://t.co/oKBdvf3lRe">pic.twitter.com/oKBdvf3lRe</a></p>&mdash; shimocchi (@shimo_cchi) <a href="https://twitter.com/shimo_cchi/status/809997638380597249">2016年12月17日</a></blockquote>
<script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>