前回は自力で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を使うので無い方はあらかじめインストールしておいてください。
#ビルド環境(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)
Exe実行でインストーラが起動する(インストール先はD:\tools\MinGW\としました)。
インストール終了後、パッケージの選択画面が出るので、「Basic Setup」のmigw32-base, mingw32-gcc-g++ の 2 つを選択(Mark for Installation)。
選択後、メニューの「Installation」⇒「Apply Changes」を選択
#RX用コンパイラをセットアップ
普通の方法だと
- GNU Toolsでユーザー登録
- GNURXv1403-ELF.exeをダウンロード
- Exe実行でインストーラが起動する。
なんですが、簡単なのでお勧めはtakjnさんの紹介されているIDE for GRを利用する方法です。IDE for GRはArduino風にGR-CITRUSをプログラミングするソフトで中にRX用のコンパイラが入っており、そのまま利用できます。なので、そちらに習って(というかほとんどそのまま)その手順を行います。
##IDE for GRのダウンロード
- がじぇっとるねさすのWebサイトからIDE for GR 0.8.0をダウンロードします。
- 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用コンパイラのパスを追加します。
@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さん、ありがとうございます。利用させていただきます。)
※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"」を追加します。
#~~~~中略~~~~~~
# 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用コンパイラのパスに書き換えます。
#~~~~中略~~~~~~
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をコピーします。
- GR-CITRUSのソースのフォルダの中にあるwrbb-v2lib-firm\firmware\wrbb_mruby\libmruby.aファイルを上書きします。
- 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を通常時刻に変換するのは以下のようになります。
#!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は色々あるので用途に応じて組み込んでみるのがいいと思います。
今回は以上となります。次回は今までのことを組み合わせてIot鳩時計らしきものを作ります。
#おまけ
現状だとNTPから時刻をとっているのに、1~2分時間がずれると思います。
時刻の変換に正確性を求めなかった理由は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にする必要があります。
(※通常より使用メモリ等大きくなりますので、自己責任でご利用ください。)
#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を外します。
/* configuration options: */
/* add -DMRB_USE_FLOAT to use float instead of double for floating point numbers */
//#define MRB_USE_FLOAT //無効にする
これで、GR-CITRUSのファームウェアをコンパイルすればOKです。