NanoPi NEOとGPSモジュールでNTPサーバ 簡易PPS編

NanoPi NEOピン配置
©FriendlyARM.

前回はGPSのMNEAだけでNTPサーバという内容だった。
今回はなるべく簡単にPPS信号を使って精度を高めようという記事。

先ず、GPSモジュールからのTime Pulse(1PPS)信号をNanoPiに受け取らせたい。
前回と同じNanoPi NEOのピン配置図を見るとGPIOという割にすべてのピンに何らかの役割がアサインされている。
取り敢えず今回はGPIO18番ピンを使いたいと思う。18番ピンは上の図の右端の下から4番めのPG9と書かれたところ。これは本来はUART1のフロー制御用のピン。そのままではPPS入力用としては使えないのでGPIOのアサイン制御を行う。
その際、Linux側からは物理的なピン番号は使わずLinux gpio番号を使う。
各GPIOピン番号に対応するLinux gpio番号は上のピン配置図の画像をクリックして表示されるWikiページに書いてある。linux gpio番号の求め方(計算方法)というものもあるようだが憶える意味がないのでWikiのページを見るだけで。

GPSモジュールと接続
GPSモジュールのPPS出力からNanoPi NEOの18番ピンを接続した。黄色の線。

# echo "201" > /sys/class/gpio/export
# echo "in" > /sys/class/gpio/gpio201/direction
# cat /sys/class/gpio/gpio201/direction  ←確認
in  ←inが表示されたらOK

/sys/class/gpio/export に対して制御したいLinux gpio番号の"201"を送信する。
Linux gpio201番を受信用(in)にする。今回は受信用だけど、信号を出力する用途ならinの代わりにoutを指定する。

書くまでもないことだが、デジタルなので信号の値は1 (Hi)、0 (Low)の2種類だけ。
現在のLinux gpio201番への入力の値を確認する。

$ cat /sys/class/gpio/gpio201/value
0      ←現在の値は0 (Low)

PPS信号を入力していると基本は0の筈だが連続して繰り返すと値が1になる瞬間に当たる筈。

今回は入力専用でしか使わないが、出力用にセットした場合は値をセット(信号を出力)できる。

$ echo 1 > /sys/class/gpio/gpio201/value #Hiを出力
$ echo 0 > /sys/class/gpio/gpio201/value #Lowを出力

PPS信号入力(出力)用としての利用をやめる時は下のように開放する。

# echo "201" > /sys/class/gpio/unexport

rpi_gpio_ntpdのインストール

rpi_gpio_ntpdはGPIOからの入力をNTPに渡してくれるツール。1秒ごとにピコンピコンと来るのをセンテンスにしてNTPに渡すのかな。rpi_gpio_ntpdは名前にrpiと付いているので元々はラズパイ用のツール。NanoPiシリーズでも使用できるとは書いてないけどOrangePiで使えるんだからNanoPiでもイケる。(GPIOを使える状態にしてから実行しないとダメみたいだけど)

rpi_gpio_ntpdのビルドには特に追加しなければならないパッケージは無いので以下3行の実行で終わる。(所要時間1分以内)

# git clone https://github.com/flok99/rpi_gpio_ntpd.git
# cd rpi_gpio_ntpd
# make install

rpi_gpio_ntpの使用方法

# /usr/local/bin/rpi_gpio_ntp -h
rpi_gpio_ntp v1.5, (C) 2013-2015 by folkert@vanheusden.com

-N x    x must be 0...3, it is the NTP shared memory unit number
-g x    gpio pin to listen on
-G x    explicit path to the gpio-pin-path, for special cases like the cubieboard1 (/sys.../gpio1_pg9 instead of /sys.../gpio1). Note: you need to "export" and configure the pin in this use-case by hand.
-d      debug mode
-F x    fudge factor (in microseconds)
-p x    when enabled, toggle GPIO pin x so that you can measure delays using a scope
-f      do not fork
-b      handle both on rising/falling but ignore falling
-P      use polling - for when the device does not support interrupts on gpio state changes
-i x    polling: how long shall we sleep (part of a second) and not poll for interrupts. e.g. 0.95

rpi_gpio_ntpの動作確認

先にgpioを念の為開放して入力モードにする。

# echo "201" > /sys/class/gpio/unexport         # ←開放
# echo "201" > /sys/class/gpio/export           # ←使用開始
# echo "in" > /sys/class/gpio/gpio201/direction # ←入力モードセット
# /usr/local/bin/rpi_gpio_ntp -g 201 -d
rpi_gpio_ntp v1.5, (C) 2013-2015 by folkert@vanheusden.com

NTP unit : 0
GPIO pin : 201
GPIO pout: -1
Fudge    : 0.000000000
"Fork into the background" disabled because of debug mode.
1489463315.000045988] interrupt #1, 0 wraps, offset 0.000046s nan/0.000046/nan
1489463316.000037482] interrupt #2, 0 wraps, offset 0.000037s 0.000037/0.000042/nan
1489463317.000006725] interrupt #3, 0 wraps, offset 0.000007s 0.000022/0.000030/nan
1489463318.000021552] interrupt #4, 0 wraps, offset 0.000022s 0.000022/0.000028/nan
1489463319.000007224] interrupt #5, 0 wraps, offset 0.000007s 0.000018/0.000024/nan
1489463320.000021080] interrupt #6, 0 wraps, offset 0.000021s 0.000019/0.000023/nan
1489463321.000023681] interrupt #7, 0 wraps, offset 0.000024s 0.000019/0.000023/0.000024
1489463322.000010403] interrupt #8, 0 wraps, offset 0.000010s 0.000017/0.000022/0.000024
1489463323.000028576] interrupt #9, 0 wraps, offset 0.000029s 0.000017/0.000023/0.000026
1489463324.000001205] interrupt #10, 0 wraps, offset 0.000001s 0.000015/0.000020/0.000026
1489463325.000010702] interrupt #11, 0 wraps, offset 0.000011s 0.000015/0.000020/0.000026
1489463326.000010529] interrupt #12, 0 wraps, offset 0.000011s 0.000014/0.000019/0.000026
1489463327.000020516] interrupt #13, 0 wraps, offset 0.000021s 0.000014/0.000019/0.000024
1489463328.000007457] interrupt #14, 0 wraps, offset 0.000007s 0.000013/0.000018/0.000024
1489463329.000026186] interrupt #15, 0 wraps, offset 0.000026s 0.000013/0.000019/0.000025
1489463330.000019952] interrupt #16, 0 wraps, offset 0.000020s 0.000013/0.000019/0.000024
1489463331.000023795] interrupt #17, 0 wraps, offset 0.000024s 0.000013/0.000019/0.000024
1489463332.000034301] interrupt #18, 0 wraps, offset 0.000034s 0.000013/0.000020/0.000025
1489463333.000014637] interrupt #19, 0 wraps, offset 0.000015s 0.000014/0.000020/0.000025
1489463334.000019262] interrupt #20, 0 wraps, offset 0.000019s 0.000014/0.000020/0.000025
1489463335.000019454] interrupt #21, 0 wraps, offset 0.000019s 0.000014/0.000020/0.000025
1489463336.000029841] interrupt #22, 0 wraps, offset 0.000030s 0.000014/0.000020/0.000026
1489463337.000002309] interrupt #23, 0 wraps, offset 0.000002s 0.000014/0.000019/0.000026
1489463338.000030650] interrupt #24, 0 wraps, offset 0.000031s 0.000014/0.000020/0.000026

停止させるのはCtrl+C
上にも書いたが、物理的なピン番号は18だが制御するのはLinux gpio番号の201。
使用した後はgpio201を開放する。

# echo "201" > /sys/class/gpio/unexport

ここまで、あくまでも確認なのでNTPdの運用時には関係ない。

ntpdのビルド (不要)

カーネルPPSを使用する場合はarmbianのパッケージでインストールしたntpdはPPSに非対応なので自分でビルドすることになるが、今回はrpi_gpio_ntpなのでビルドの必要は無い。でも、ビルドしたい場合は下

# apt-get install libcap-dev pps-tools
# /etc/init.d/ntp stop
# apt-get remove ntp
$ wget http://archive.ntp.org/ntp4/ntp-4.2.8p8.tar.gz
$ tar zxvf ntp-4.2.8p8.tar.gz
$ cd ntp-4.2.8p8
$ ./configure --enable-linuxcaps
$ make -j4
# make install

# ln -s /usr/local/sbin/ntpd /usr/sbin/ntpd
# ln -s /usr/local/bin/ntpdc /usr/bin/ntpdc      #必要なら
# ln -s /usr/local/bin/ntpq /usr/bin/ntpq          #必要なら
# ln -s /usr/local/bin/ntpsweep /usr/bin/ntpsweep  #必要なら
# ln -s /usr/local/bin/ntptrace /usr/bin/ntptrace  #必要なら

/etc/ntp.confにPPS用の設定を追記する。
このとき、カーネルPPSであれば127.127.22.*を指定するが、rpi_gpio_ntpを使用する場合は、NMEAと同じく127.127.28.*を使用する。前回(今回も引き続き)MNEA用として127.127.28.0を使用しているのでPPS用は1足して127.127.28.1を指定。

1
2
server 127.127.28.1 minpoll 4 maxpoll 4 prefer
fudge 127.127.28.1 refid PPS

準備完了

# /etc/init.d/ntp start                     # ←ntp開始

暫く待ってから確認。

$ ntpq -p
     remote           refid      st t when poll reach   delay   offset  jitter
==============================================================================
+SHM(0)          .NMEA.           0 l   10   16  377    0.000    1.471   0.153
*SHM(1)          .PPS.            0 l    9   16  377    0.000    0.001   0.006
+ntp1.jst.mfeed. 133.243.236.17   2 u   14   64  377   14.508    3.409   0.184
+ntp2.jst.mfeed. 133.243.236.17   2 u   15   64  377   13.886    2.908   0.229
+ntp3.jst.mfeed. 133.243.236.17   2 u   19   64  377   14.500    1.873   0.161

上手く行ったらシステム起動時に自動実行できるように/etc/rc.localに追記。

1
2
3
echo "201" > /sys/class/gpio/export
echo "in" > /sys/class/gpio/gpio201/direction
/usr/local/bin/rpi_gpio_ntp -N 1 -g 201

最後のexit 0より前に3行挿入する。
ただ、これだとちょっと雑過ぎて最初に大きな時間のズレが出るようなので要改良。ntpdateとか挟む方が良いかも。

カーネルPPSではないが精度はまぁまぁな感じ。でも、offsetの値に落ち着きがない印象。
やっぱりカーネルをビルドしようかしら?

2017年3月15日追記:
armbianで配布しているパッケージでカーネルを4.10.0に更新するとUART1が無効になる。そこで以下1行追記。

# vi /boot/armbianEnv.txt
overlays=sun8i-h3-uart1

ついでにPPS-GPIOモジュールを有効にしたい。こちらも1行追記

# vi /etc/modules
pps-gpio

システムを再起動する。

# lsmod
Module                  Size  Used by
sunxi_cir               3825  0 
cfg80211              192770  0 
rfkill                 10928  1 cfg80211
evdev                   9979  0 
sun8i_codec_analog     13766  0 
snd_soc_core          115473  1 sun8i_codec_analog
snd_pcm_dmaengine       4221  1 snd_soc_core
snd_pcm                70145  2 snd_pcm_dmaengine,snd_soc_core
sun8i_ths               3134  0 
gpio_keys               8517  0 
uio_pdrv_genirq         3354  0 
cpufreq_dt              3522  0 
uio                     8012  1 uio_pdrv_genirq
thermal_sys            43232  2 cpufreq_dt,sun8i_ths
pps_gpio                2833  0 
g_serial                3737  0 
libcomposite           34692  1 g_serial
fuse                   70718  1 

上の例では下から4行目にpps_gpioが表示されている。
ここまでは上手くいったが/dev/pps*が生えてこなくて詰んだ。
追記ここまで。

関連記事:

NanoPi NEOにGPSモジュールを繋いでNTPサーバ

NanoPi NEO
画像は使い回し

この記事ではroot(スーパーユーザー)で実行するコマンドは行頭に#、一般ユーザーで実行するコマンドは行頭に$を付けている。Linuxの流儀であれば su - でrootになって実行するよりはsudoで実行する。# hogehoge と書いてたら一般ユーザーで sudo hogehoge を実行みたいな。

正しい現在日時の設定・表示

まず、GPS云々以前にNanoPi NEOで正しく時刻表示できるようにする。

# dpkg-reconfigure tzdata

メニューが表示されるので先ずAsiaを選択、次にTokyoを選択。
armbianのdebian Jessieならこれだけ。

表示してみる

$ timedatectl status
      Local time: Wed 2017-03-11 11:26:41 JST
  Universal time: Wed 2017-03-11 02:26:41 UTC
        RTC time: Thu 1970-01-01 01:14:48
       Time zone: Asia/Tokyo (JST, +0900)
     NTP enabled: no
NTP synchronized: yes
 RTC in local TZ: no
      DST active: n/a

Local timeがJSTで表示されていてTime zoneがAsia/TokyoになっていればOK。
NanoPi NEOには時計のバックアップ(電池)が無いので一度電源が切れるとRTC timeが1970年1月1日0時に戻ってしまうが、これは無視で良い。どうしても気になるならRTCモジュールを買うなり自作するなりして追加するというのもアリだが、これからNTPやGPSで時刻を取るので正直あまり意味があるとは思えない。

RTC timeをどうしても1970年ホゲホゲから現在時刻に変更したいなら正しい時刻を取得した後に次を実行。

# hwclock -w

これでRTC timeがUniversal timeと同じ値になる。

2017年3月14日追記:
armbianのカーネルを3.4.113から4.10.0に(armbianで配布しているパッケージで)更新すると起動後のどこかのタイミングで自動的にRTC timeに時刻を入れてくれるようになるみたい。ただし、fake-hwclockを使っているようなので完全にUniversal timeに一致するわけではなく意味あるのか?という感じ。RTCをNTPに同期させるようにしてやるとほぼ正しい時刻になるのでそちらの方が良さげ。

日付と時刻が全然正しくないなら現在のNTPの状態を確認。

$ ntpq -p
     remote           refid      st t when poll reach   delay   offset  jitter
==============================================================================
*ntp1.jst.mfeed. 133.243.236.17   2 u  132  256  377   14.619    0.173   0.127

NTPの設定をした憶えはないが、DHCP有効ならありがたくも勝手に日本のMFEEDから取っている筈。固定IPならdebian.pool.ntp.orgから取ってる筈。
行頭に * (同期中)が表示されているのに日時が滅茶苦茶なら根本的に何かがおかしい。そうでなければ正しい日時を表示している筈。
行頭に * (同期中が付くのは1つの時刻ソースだけ)が表示されていない場合は次。NTPサーバに接続できてるならそうはならない筈だけど・・

日時表示がおかしい場合の仮対応

# apt-get install ntpdate   #必要なら
# /etc/init.d/ntp stop
# ntpdate ntp.jst.mfeed.ad.jp
# date
# /etc/init.d/ntp start
# date

ntpdを停止してntpdateで手動でntp.jst.mfeed.ad.jpから時刻を取得。
(または、ntpdate -u ntp.jst.mfeed.ad.jp ならNTPを再起動しなくても可)
dateで現在日時を確認。ここで正しい日時が表示されるならntpdを起動する。
1分ほど待ってから再度dateで現在日時を確認。
上では/etc/init.d ntp (start|stop|restart)を使っているが、systemctl (start|stop|restart) ntp.serviceでも可。
Time Zoneが変なら再度dpkg-reconfigure tzdataをやり直す。それでもダメならarmbian焼き直しが無難。

gpsd他のインストール

# apt-get install gpsd
# apt-get install gpsd-clients

もちろん纏めてapt-get install gpsd gpsd-clients でも可。

シリアルポートとgpsdの設定

GPSモジュール
1つ前の記事で購入したGPSモジュール

NanoPi NEOピン配置
©FriendlyARM.

今回は NanoPiのGPIO 8番ピンUART1_TXとGPSモジュールのRX、NanoPiのGPIO 10番ピンUART1_RXとGPSモジュールのTXを接続。念の為にNanoPiのGPIO 6番ピンGNDとGPSモジュールのGNDを接続。GPSモジュールへの電源はUSB供給。(ピンからも取れるけど)
これでNaniPiとGPSモジュールが通信できるようになっている筈。

上のピン配置図がわかりやすい。今回使ったGNDは右端の上から3番めのピン、UART1-TXは右端の上から4番めのピン、UART1-RXは右端の上から5番めのピン。
なお、ピンと書いているけど実際には基板にはスルーホールしかないのでピンヘッダかピンソケットを取り付けて配線する。ワイヤーをハンダ直付けでもいいけど・・

GPSモジュールと接続
GPSモジュールと接続した。上の写真では既にPPSも結線している(黄色の線)。

シリアルポートの確認
今回はUART0ではなくUART1を使用しているのでデバイスは/dev/ttyS1

$ cat /dev/ttyS1

$GPホニャララという行(NMEAセンテンス)が停まらずに出続ければ正常。Ctrl+Cで終了

すでにgpsdが動いている筈だが、GPSソースとしてttyS1を使用しない状態なので設定ごと停止&削除。

# systemctl stop gpsd.socket
# systemctl disable gpsd.socket

手動でttyS1を使用してgpsdを起動する。

# gpsd /dev/ttyS1 -F /var/run/gpsd.sock

GPSのデータ受信を確認(コマンド実行から表示まで数秒かかるかも)

$ cgps -s
または
$ gpsmon
cgps

これで正常にgpsdが動くことが確認できた。
そこで、gpsdがttyS1を使って自動起動できるようにする。

gpsd設定ファイル (おそらく新規作成)

# vi /etc/gpsd
1
2
3
4
START_DAEMON="true"
USBAUTO="false"
DEVICES="/dev/ttyS1"
GPSD_OPTIONS="-n"

4行目のGPSD_OPTIONSの-nを指定し忘れるとgpsd自体は正常に起動して例えばgpsmonとかcgpsなどは使えるが、NTPのソースとして使えない。
今回はNTPサーバが目的なので、gpsd経由で時刻を取るなら絶対必要なオプション。gpsdなんか使わず直で取るならどうでもいいけど。

# vi /lib/systemd/system/gpsd.service (1行だけ変更)
EnvironmentFile=-/etc/gpsd

FreeBSDだと/etc/defaults/hogeを/etc/hogeの設定行でオーバーライドという流儀だけどLinuxってそうならないんだっけ?
defaultって名前のディレクトリの存在する意味がないような・・
それなら /etc/gpsd 作成且つ /lib/systemd/system/gpsd.service の変更じゃなく、 /etc/default/gpsd を直変更のが簡単かも。

2017年3月14日追記:
armbianのカーネルを3.4.113から4.10.0に(armbianで配布しているパッケージで)更新した場合は以下2行を実行。(更新していないなら実行しない)

# systemctl enable gpsd.socket
# systemctl start gpsd.socket

次はカーネル更新の有無に関わらず自動起動のために必ず実行。

# ln -s /lib/systemd/system/gpsd.service /etc/systemd/system/multi-user.target.wants/

システムを再起動したときにgpsdが/dev/ttyS1を使用して正しく起動することを確認。(cgps -sまたはgpsmonで表示されればOK)

ntp.conf設定

(armbianのdebian jessieでは) DHCPが有効な場合はntpdは /etc/ntp.conf を読まずに /var/lib/ntp/ntp.conf.dhcp を読むようになっているが、 /var/lib/ntp/ntp.conf.dhcp はシステム起動時に上書きされてしまうので /var/lib/ntp/ntp.conf.dhcp を編集するのは意味がない。そこでDHCP有効でも /etc/ntp.conf を読むようにしたい。

# vi /etc/dhcp/dhclient.conf
request subnet-mask, broadcast-address, time-offset, routers,
        domain-name, domain-name-servers, domain-search, host-name,
        dhcp6.name-servers, dhcp6.domain-search,
        netbios-name-servers, netbios-scope, interface-mtu,
        rfc3442-classless-static-routes;

requestの行の最後から", ntp-servers"を削除。それ以外は触らない。

固定IPの場合は何も考えずに/etc/ntp.confを変更する。

# vi /etc/ntp.conf
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
driftfile /var/lib/ntp/ntp.drift
statsdir  /var/log/ntpstats/

server ntp1.jst.mfeed.ad.jp #210.173.160.27
server ntp2.jst.mfeed.ad.jp #210.173.160.57
server ntp3.jst.mfeed.ad.jp #210.173.160.87
#server ntp1.v6.mfeed.ad.jp #2001:3a0:0:2001::27:123
#server ntp2.v6.mfeed.ad.jp #2001:3a0:0:2005::57:123
#server ntp3.v6.mfeed.ad.jp #2001:3a0:0:2006::87:123

restrict -4 default ignore
restrict -6 default ignore

restrict 127.0.0.1
restrict ::1

restrict 192.168.0.0 mask 255.255.255.248 nomodify notrap
#restrict 2000:****:****:****:: mask ffff:ffff:ffff:ffff:: nomodify notrap

restrict -4 210.173.160.27 mask 255.255.255.255 nomodify notrap noquery
restrict -4 210.173.160.57 mask 255.255.255.255 nomodify notrap noquery
restrict -4 210.173.160.87 mask 255.255.255.255 nomodify notrap noquery
#restrict -6 2001:3a0:0:2001::27:123 nomodify notrap noquery
#restrict -6 2001:3a0:0:2005::57:123 nomodify notrap noquery
#restrict -6 2001:3a0:0:2006::87:123 nomodify notrap noquery

server 127.127.28.0 minpoll 4 maxpoll 4
fudge 127.127.28.0 refid NMEA

GPS関係は最後の2行だけ。
今回は時刻ソースをgpsdのNMEAにしたので127.127.28.0。「がとらぼ」の以前の記事ではgpsd経由じゃなくシリアルポート直取りのNMEAだったので127.127.20.0。
最終行のrefidの後は識別用の任意の文字列。ネットで検索して他所様のGPS & NTP関連のページを見るとこの値を"GPS"にしてる人が多いけど今回はGPSのNMEAとPPSの2つを時刻ソースとして使う予定で、ここで設定するのはNMEAなので"GPS"ではなく"NMEA"とした。前回は、この値を"GPS"にしているが、そちらの記事ではGPSのNMEAだけを時刻ソースとして使用したのでわざわざ"NMEA"と明示する必要がなかったから。

armbianインストール後に特に触っていなければdriftファイルがおそらく存在しないので作成する。すでに存在するなら下2行のコマンド実行は不要。

# touch /var/lib/ntp/ntp.drift
# chown ntp:ntp /var/lib/ntp/ntp.drift

ntpdを再起動して確認

# /etc/init.d/ntp restart

ntp再起動から30秒以上待ってからntpq -pで確認する。

$ ntpq -p
     remote           refid      st t when poll reach   delay   offset  jitter
==============================================================================
+ntp1.jst.mfeed. 133.243.236.17   2 u    5   64   37   15.058   -2.814   1.082
+ntp2.jst.mfeed. 133.243.236.17   2 u    4   64   37   14.191   -2.522   0.135
-ntp3.jst.mfeed. 133.243.236.17   2 u    7   64   37   14.111   -3.586   0.430
*SHM(0)          .NMEA.           0 l    4   16  377    0.000   -0.291   0.126

上の結果はNMEAとMFEEDの公開NTPサーバとのoffsetが200程度あったのでその分を反映(time1 0.200を設定追加)させている。

fudge 127.127.28.0 time1 0.200 refid NMEA

PPSをソースにせずNMEAだけというならNMEAの遅延の程度が不明なので、このようにNTPサーバ(上の例ならMFEED)の時刻に寄せてしまうというのもあり。
または、正確な時刻を得る環境があってNMEAの遅延を測定できるならその値を反映させる。
できればしばらくoffsetの様子を見ながら0.200なんてアバウトじゃなくもう少し細かく。

PPSは次の記事で。

関連記事:
Up