RubicでGR-CITRUSのmrubyプログラミング事始め４[mrbgems編]

前回は自力でNTPに接続してUnixTimeを変換しましたが、今回は拡張ライブラリmrbgemsを使いたいと思います。GR-CITRUSのmrubyにmrbgemsを組み込むためにはクロスコンパイル環境をセットアップする必要があります。

#mrbgemsとは？
mrubyの拡張ライブラリです。Rubyのrubygemsに対してmrubyはmrbgemsが用意されています。しかし、rubyは最初から拡張ライブラリが豊富に使えるのに対して、mrubyは必要最低限という精神の元に作られているので最初からは使えません。使うためにはmrubyにmrbgemsを組み込んでコンパイルしなおさないといけません。

#環境構築の主な流れ
GR-CITRUSのmruby再構築には以下手順が必要となります。ここではWindows上で構築しました。


+ ビルド環境(Ruby)の準備
+ ビルド環境(Bison)の準備
+ ビルド環境(Mingw)の準備
+ RX用クロスコンパイラ準備
+ mrubyビルド＆GR-CITRUSファームウェアビルド

それと、[git](https://git-for-windows.github.io/)を使うので無い方はあらかじめインストールしておいてください。

#ビルド環境(Ruby)の準備
RubyInstallerを利用します。バージョンは、ここではrubyinstaller-2.3.1を利用しました。
rubyの実行ファイルへのパスを設定するチェックボックスをONにしてください。
http://rubyinstaller.org/

#ビルド環境(Bison)の準備
Bisonのセットアップは以下サイトより
Download「Complete package, except sources」の「Setup」を選択してください。
http://gnuwin32.sourceforge.net/packages/bison.htm

ここでは、bison-2.4.1-setupを利用しました。
インストール先は以下のように設定しました。

+ 「D:\tools\GnuWin32」


#ビルド環境(Mingw)の準備
mrubyビルド時とGR-CITRUS用ファームウェアビルド時に、Makeなどのビルドツールを使うため、Mingwをセットアップします。CygwinでもOKですが、ちっちゃい最低限のMingwの方が私は好き。(※もしCygwinなどでgccを入れてある場合は不要です。)

http://www.mingw.org/ からMingwをダウンロード（mingw-get-setup.exe）
![image](https://qiita-image-store.s3.amazonaws.com/0/151653/3dd9bd24-8c57-3667-b9d7-558d9d066876.png)

Exe実行でインストーラが起動する（インストール先はD:\tools\MinGW\としました）。

インストール終了後、パッケージの選択画面が出るので、「Basic Setup」のmigw32-base, mingw32-gcc-g++ の 2 つを選択(Mark for Installation)。
![lang_mingw_inst_select.png](https://qiita-image-store.s3.amazonaws.com/0/151653/a1fb1485-500a-62de-6790-02999248d4fd.png)

選択後、メニューの「Installation」⇒「Apply Changes」を選択
![image](https://qiita-image-store.s3.amazonaws.com/0/151653/e5f7b93e-5448-678e-a681-9195ca6999c0.png)



#RX用コンパイラをセットアップ
普通の方法だと

1. [GNU Tools](https://gcc-renesas.com/ja/)でユーザー登録
+ GNURXv1403-ELF.exeをダウンロード
+ Exe実行でインストーラが起動する。

なんですが、簡単なのでお勧めは[takjnさん](http://qiita.com/takjn/items/42fa8ad0c61a8840a9c2#_reference-7eace0853ad79f7e59fe)の紹介されているIDE for GRを利用する方法です。IDE for GRはArduino風にGR-CITRUSをプログラミングするソフトで中にRX用のコンパイラが入っており、そのまま利用できます。なので、そちらに習って（というかほとんどそのまま）その手順を行います。



##IDE for GRのダウンロード

+ がじぇっとるねさすのWebサイトから[IDE for GR 0.8.0](http://gadget.renesas.com/ja/product/ide4gr.html)をダウンロードします。
+ zipファイルの中身を任意のフォルダに解凍
+ ここでは「D:\tools\idegr\ide4gr-E0.8.0\」に解凍しました。

##GNU_RX_elf.batの修正

IDE for GR 解凍後の以下のファイルを修正します。
* D:\tools\idegr\ide4gr-E0.8.0\hardware\tools\gcc-rx\GNU_RX_elf.bat

Set Pathを修正し、BisonとMingwとRX用コンパイラのパスを追加します。

```GNU_RX_elf.bat
@Echo Off
REM Set PATH=C:\PROGRA~1\KPIT\GNURXV~2.03-\rx-elf/rx-elf\bin;C:\PROGRA~1\KPIT\GNURXV~2.03-\OtherU~1;C:\PROGRA~1\KPIT\GNURXV~2.03-\rx-elf/rx-elf\libexec\gcc\rx-elf\4.8-GNURX_v14.03;%PATH%
Set PATH=Set PATH=D:\tools\GnuWin32\bin;D:\tools\MinGW\bin;D:\tools\MinGW\msys\1.0\bin;D:\tools\idegr\ide4gr-E0.8.0\hardware\tools\gcc-rx\rx-elf\rx-elf\bin;D:\tools\idegr\ide4gr-E0.8.0\hardware\tools\gcc-rx\Other Utilities;D:\tools\idegr\ide4gr-E0.8.0\hardware\tools\gcc-rx\rx-elf\rx-elf\libexec\gcc\rx-elf\4.8-GNURX_v14.03;%PATH%


Set MAKE_MODE=unix

REM cd "C:\Program Files\KPIT\GNURXv14.03-ELF"

Cmd "cd\" /k
```

#mrubyビルド＆GR-CITRUSファームウェアビルド
mrubyのソースコードの取得

githubから落とします。

```
git clone https://github.com/mruby/mruby
```

ここでは

+ 「d:\work」に落としました。

##GR-CITRUS用のbuild_config.rbの準備
mrubyのmrubyフォルダ内にあるbuild_config.rbをtakjnさん公開の以下ファイルに書き換えます。
(takjnさん、ありがとうございます。利用させていただきます。)

https://github.com/takjn/mruby/blob/rx630/examples/targets/build_config_RX630.rb

※build_config_RX630.rbはtakjnさんがサンプルとして公開して下さっているものです。
公式のmrubyやGR-CITRUSとは一切の関係はありません。
自己責任での利用となることを注意してください。


##build_config.rbの修正

+ 上記で取得したbuild_config.rbのBIN_PATHを自身のRX用コンパイラのパスに修正します。
+ 追加するmrbgemsの設定をします。#gems from coreの項の最後に、「  conf.gem :core => "mruby-time"」を追加します。

```build_config.rb
#～～～～中略～～～～～～
  # Linux
  BIN_PATH = "D:/tools/idegr/ide4gr-E0.8.0/hardware/tools/gcc-rx/rx-elf/rx-elf/bin"

#～～～～中略～～～～～～
  #gems from core
  conf.gem :core => "mruby-sprintf"
  conf.gem :core => "mruby-print"
  conf.gem :core => "mruby-math"
  conf.gem :core => "mruby-enum-ext"
  conf.gem :core => "mruby-numeric-ext"
  conf.gem :core => "mruby-time" #←追加

```

##GR-CITRUSのソースを取得
githubから落とします。

```
git clone https://github.com/wakayamarb/wrbb-v2lib-firm/tree/master/firmware_develop
```
ここでは

+ 「d:\work」に落としました。


##makeファイルの修正

+ 「GNU_PATH := 」の部分を自身のRX用コンパイラのパスに書き換えます。


```makefile
#～～～～中略～～～～～～
GNU_PATH := D:/tools/idegr/ide4gr-E0.8.0/hardware/tools/gcc-rx/rx-elf/rx-elf/
```

##mrubyビルド

先に編集したバッチファイルGNU_RX_elf.batを実行します。

```
D:\tools\idegr\ide4gr-E0.8.0\hardware\tools\gcc-rx\GNU_RX_elf.bat
```


バッチファイル実行後に出たコマンドプロンプトを操作してbuild_config.rbのあるディレクトリに移動し、以下コマンドを実行します。

```
ruby ./minirake
```

コンパイルが成功すると以下のような表示が出て、mruby/buildフォルダにhostフォルダとRX630フォルダが作成されます。
（※2回目以降コンパイルする場合はbuildフォルダを空にしておく必要があります。前回コンパイル分が残っている場合は削除しておいてください。）

```
================================================
      Config Name: RX630
 Output Directory: build/RX630
    Included Gems:
             mruby-sprintf - standard Kernel#sprintf method
             mruby-print - standard print/puts/p
             mruby-math - standard Math module
             mruby-enum-ext - Enumerable module extension
             mruby-numeric-ext - Numeric class extension
             mruby-time - standard Time class
================================================
```

##GR-CITRUSのソースをビルド

+ mrubyをビルドしたフォルダの中にある、mruby\build\RX630\lib\libmruby.aをコピーします。
2. GR-CITRUSのソースのフォルダの中にあるwrbb-v2lib-firm\firmware\wrbb_mruby\libmruby.aファイルを上書きします。
3. firmwareフォルダに移動して、makeコマンドを実行します。

```
make
・・・・中略、正常終了で以下のような表示となる・・・・
rx-elf-objcopy -O binary ./gr_build/citrus_sketch.x  citrus_sketch.bin
rx-elf-objcopy -O srec -I elf32-rx-be-ns ./gr_build/citrus_sketch.x  ./gr_build/
citrus_sketch.mot
```

+ citrus_sketch.binファイルが更新されたのを確認。
+ GR-CITRUSのリセットボタンを押してUSBメモリとして認識させた後、citrus_sketch.binファイルをコピー

これでmruby-timeが使えるようになりました。

#mruby-timeのテスト
では、Rubicでmruby-timeを使ってUnix-Timeを変換してみましょう。

使い方は

```
t = Time.at(UnixTime)
```

だけですので実に簡単です。
NTPで時刻情報をとって、Unix-Timeを通常時刻に変換するのは以下のようになります。


```main.rb
#!mruby
SSID="あなたのSSID"
PASS="あなたのPASS"
NTP_PACKET_SIZE = 48
timeServer="132.163.4.101" #time-a.timefreq.bldrdoc.gov
#timeServer="129.6.15.28"  #time.nist.gov
timeZone = 9 #Tokyo
localPort = 8788 #local port to listen for UDP packets
sendPort = 123 # NTP requests are to port 123
pollIntv = 150      # poll every this many ms
maxPoll = 15        # poll up to this many times

Usb = Serial.new(0)

#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

#WiFi接続準備をする。
Usb.println "WiFi Ready"

Usb.println "WiFi Get Version"
Usb.println WiFi.version

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"
Usb.println WiFi.connect(SSID,PASS)

Usb.println "WiFi ipconfig"
Usb.println WiFi.ipconfig

Usb.println "WiFi multiConnect Set"
Usb.println WiFi.multiConnect 1

#NTP用要求パケットを作る
packetBuffer = Array.new(NTP_PACKET_SIZE , 0)
packetBuffer[0] = 0b11100011   # LI, Version, Mode
packetBuffer[1] = 0     # Stratum, or type of clock
packetBuffer[2] = 6     # Polling Interval
packetBuffer[3] = 0xEC  # Peer Clock Precision

binary = packetBuffer[0].chr
for num in 1..NTP_PACKET_SIZE-1 do
    binary += packetBuffer[num].chr
end

#NTPサーバーにUDP接続して要求パケットを送る
Usb.println "UDP OPEN"
Usb.println WiFi.udpOpen(1, timeServer, sendPort, localPort)
Usb.println "UDP SEND " + packetBuffer.length.to_s + " Data:" + packetBuffer.to_s
WiFi.send( 1,binary)

#NTPサーバーからの応答パケットを受け取る。
array = WiFi.recv 1
maxPoll.times do
  delay(pollIntv)    
  if (array[0] >= 0) then
      if (array.length == 48) then
          break
      end  
  end
  array = WiFi.recv 1   
end
WiFi.cClose(1)

#応答パケットが48バイトでなければ終了
if array.length != 48 then
    System.exit() 
end    

Usb.println array.to_s

#40から43バイト目に時刻情報が入っている
time = array[40]
for i in 1..3 do
  time = time << 8 | array[40+i]
end

#UNIXタイムは1970年1月1日0時から始まる
#1900年から1970年の70年を秒で表すと2208988800秒になる
UnixTime    = time - 2208988800 # convert NTP time to Unix time
UnixTimeJST = UnixTime + (timeZone * 60 * 60) #東京の時刻に変更する
Usb.println UnixTimeJST.to_s #1970年からの経過時間(秒)

t = Time.at(UnixTimeJST) #mruby-timeによる変換
Usb.println t.to_s
```

だいぶ簡単かつ、コードも短くなりました。mrbgemsすばらしい。
このほかにも[mrbgemsは色々ある](https://github.com/mruby/mgem-list)ので用途に応じて組み込んでみるのがいいと思います。


今回は以上となります。[次回は今までのことを組み合わせてIot鳩時計らしきものを作ります。](http://qiita.com/shimo_cchi/items/54d9109c39ae6151b272)

#おまけ
現状だとNTPから時刻をとっているのに、１～２分時間がずれると思います。
時刻の変換に正確性を求めなかった理由はUnixTimeの桁が大きいのでGR-CITRUSでは表現しきれなかったためです。mrubyはBigNumが使えないのでIntより大きい数字は暗黙的にfloatにキャストされる仕様です。
(参考)https://github.com/mruby/mruby/issues/2979#issuecomment-146308854

GR-CITRUSは実数の取扱いが標準だとFLOATであり精度が高くありません。
そのため、きちんとUnixTimeを取り扱うにはコンパイル時にbuild_config.rbの設定からMRB_USE_FLOATを外して実数の精度をDOUBLEにする必要があります。
（※通常より使用メモリ等大きくなりますので、自己責任でご利用ください。）

```build_config.rb
    #configuration for low memory environment
    #cc.defines << %w(MRB_USE_FLOAT) #無効にする
```

これでmrubyをコンパイルしなおします。
そのあと、GR-CITRUSも合わせて
wrbb-v2lib-firm-master\firmware\wrbb_mruby\include\mrbconf.h
のMRB_USE_FLOATを外します。

```mrbconf.h
/* configuration options: */
/* add -DMRB_USE_FLOAT to use float instead of double for floating point numbers */
//#define MRB_USE_FLOAT //無効にする
```

これで、GR-CITRUSのファームウェアをコンパイルすればOKです。