popnja’s blog

日記風からハード中心に変わりました

どこにもまとまった情報がなかったがラズパイ4+Ubuntu Server+GPS/1PPS+ntpd+USBシリアル変換を組み合わせた環境を構築した

私は、Raspberry Pi OSよりはUbuntu Serverの方が好きな人間です。ただし、ラズパイ4とUbuntu Serverの組み合わせでGPIOなどは使ったことはありません。でもやりたい。ラズパイ3は内蔵有線LANが遅いねん。Ubuntu Server 64bitが使いたいねん。ということでチャレンジ。

下準備

Ubuntu Server

まずはUbuntu Serverです。最近は、「Raspberry Pi Imager」で簡単にmicroSDにインストールできるようになったのでこちらを使っています。
www.raspberrypi.org
ubuntu.com
Ubuntu Server 20.04.3 LTS 64bit版」を今回は使用しています。
f:id:kaias1jp:20211004142939p:plain
f:id:kaias1jp:20211004142951p:plain
f:id:kaias1jp:20211004143000p:plain
作ったmicroSDカードをラズパイ4に挿してLANケーブルをつないだら電源ケーブルをつなぎます。DHCPIPアドレスが配布される環境なら何らかのIPアドレスが割り当てられるはずです。今回は最初からヘッドレスで動かしますので、パソコンからsshで接続します。
さて接続先ですが、人によってはどのIPアドレスかすぐわかる人もいるでしょう。私の場合はルーターの管理画面を見ればわかりますが、いちいち見に行くのは面倒なのでWindowsパソコン上で「NetEnum5」というソフトを使ってLANの中を検索します。
www.e-realize.com
このソフトを使ってLAN内を検索すると、こんな感じで結果が一覧で表示されます。
f:id:kaias1jp:20211004143800p:plain
今回はラズパイが目当てですので、「Raspberry Pi Trading Ltd」というベンダー名の機器でDHCPの範囲のものにあたりをつけます。
あとはsshクライアントで接続してください。ラズパイ4で動くUbuntu Serverの場合、初期ユーザー/パスワードは「ubuntu/ubuntu」ですが初回接続時にパスワード変更を要求されます。あとフィンガープリントがなんちゃらありますので、sshクライアントとIPアドレスの使用履歴によってはうまくいかないことがあります。つなぎ元がUbuntuの場合は、「~/.ssh/known_hosts」から該当する情報を削除するとよいかも。

USB-TTLシリアルケーブル

「GPIOピンに5本もつなぐのは面倒」「Ubuntu ServerでGPIOの使い方知らないのでなるべく回避したい」などあり、しばらく考えたら「1PPSはともかくGPS信号出力自身はシリアル通信なのだからUSB-TTLシリアルケーブルで受ければUSB化できるんじゃね?」という結論に至りました。そんなわけで手元にあったやつを利用。
f:id:kaias1jp:20211004150429p:plain
今使っているやつは、「PL2303」というチップを使っているようですね。まあ、よほど安いものじゃなければUbuntu Serverで認識すると思いますが。

1PPSの結線用ワイヤー

1PPSだけはGPIOに接続しないとだめそうなので、結線用のワイヤーが必要です。私の場合は電子工作用にジャンパワイヤーをいくつも持っていたので、それをつなげて長いものを作りました。

作業開始

作業するにあたって基本的な方針となったのが以下の2つのページです。
www.netfort.gr.jp
gato.intaa.net
「ラズパイ4+Ubuntu ServerでGPSなNTPサーバーを作る」みたいなページが見つからなかったんですよね。とは言え、どうすればいいかの基準は決まったので、以下に決定。

  • GPSモジュールのシリアル通信部分はUSB変換する
  • 1PPS信号はGPIOに入力する
  • gpsdとntpdの組み合わせではなくntpdのみでの構築を目指す

試行錯誤した結果なので、こんな感じになっています。特に、「gpsdを使わない」のは「ラズパイ4+Ubuntu Serverで1PPS信号を受けるようにするとなぜかgpsdがだんまりになり解決方法が見つからなかった」からなので、短時間で見つけた方法がこうだったということです。

GPSモジュールをUSB経由で接続する

GPSモジュールの「5V」「GND」「TXD」「RXD」をUSB TTLシリアルケーブルに接続します。
f:id:kaias1jp:20211004154336j:plain
たぶん配線はこんな感じのはず。

  • 5V:赤
  • GND:黒
  • TXD:白
  • RXD:緑

www.switch-science.com
ラズパイのGPIOのUARTにつなぐときと同じように、TXDとRXDはクロス接続します。
「動作確認はどうするべ」ということですが、一番簡単な方法はcatコマンドで生データを受ける方法です。ラズパイ4にUSB接続すると、「/dev/ttyUSB*(*は数字。たいてい0)」が生えてきます。そこをcatで覗きます。

sudo cat /dev/ttyUSB0

こんな感じで流れてきますね。

$GPGGA,064840.00,3530.61044,N,13932.69608,E,1,07,1.31,1.0,M,39.2,M,,*56

$GPGSA,A,3,18,10,05,12,23,13,24,,,,,,2.25,1.31,1.82*04

$GPGSV,3,1,11,05,22,143,25,10,25,316,29,12,23,161,42,13,24,073,26*70

$GPGSV,3,2,11,14,05,035,,15,54,066,,18,31,238,14,23,60,308,16*77

$GPGSV,3,3,11,24,83,314,18,25,05,185,12,28,17,047,13*4B

$GPGLL,3530.61044,N,13932.69608,E,064840.00,A,A*6E

$GPRMC,064841.00,A,3530.61045,N,13932.69612,E,0.227,,041021,,,A*7D

$GPVTG,,T,,M,0.227,N,0.420,K,A*22

$GPGGA,064841.00,3530.61045,N,13932.69612,E,1,07,1.31,0.6,M,39.2,M,,*5A

$GPGSA,A,3,18,10,05,12,23,13,24,,,,,,2.25,1.31,1.83*05

$GPGSV,3,1,12,05,22,143,26,10,25,316,29,12,23,161,42,13,24,073,27*71

$GPGSV,3,2,12,14,05,035,,15,54,066,,18,31,238,14,23,60,308,16*74

$GPGSV,3,3,12,24,83,314,17,25,05,185,12,28,17,047,15,32,00,281,*7B

$GPGLL,3530.61045,N,13932.69612,E,064841.00,A,A*65

本当にGPSとして機能しているか見る場合、ついでにGPS電波の受信状況を確認しながら設置場所を決める場合はgpsdとgpsmonを使用するのがよいです。gpsdに関してはここを参考にしました。
qiita.com
pps-gpioの部分は無視します。gpsdパッケージのインストールと「/etc/default/gpsd」の書き方だけです。

sudo apt update
sudo apt install gpsd gpsd-clients

他の記事には「python-gps」パッケージをインストールするように書かれていたりしますが、Ubuntu 20.04では上記2つのパッケージが依存パッケージとして「python3-gps」をインストールするようになっていますので不要です。

/etc/default/gpsd

# Default settings for the gpsd init script and the hotplug wrapper.

# Start the gpsd daemon automatically at boot time
START_DAEMON="true"

# Use USB hotplugging to add new USB devices automatically to the daemon
USBAUTO="false"

# Devices gpsd should collect to at boot time.
# They need to be read/writeable, either by user gpsd or the group dialout.
DEVICES="/dev/ttyUSB0"

# Other options you want to pass to gpsd
GPSD_OPTIONS="-n"

設定ファイルを書き換えたら、「sudo systemctl restart gpsd」でgpsdサービスを再起動させてから「gpsmon」コマンドを呼び出します。
f:id:kaias1jp:20211004160343p:plain
こんな感じの画面が表示されて、左上のリストがこんな感じになればOKです。
f:id:kaias1jp:20211004160426p:plain
この場所の一覧の各行のうち、「Y」の文字がいくつか表示されたら「GPS信号を受信できている」と判断してよいかとおもいます。右側の「LTP Pos:」というところに数字が出てきたら現在位置が取れてきています。ここまでくれば受信状況としては合格です。
作業が終わったら、この後は使わないのでgpsd関連のパッケージは削除します。

sudo apt remove gpsd gpsd-clients

1PPS信号をGPIOに入力する

次はPPSです。「/boot/firmware/overlays/」の中を見てみると面白いことがわかります。

/boot/firmware/overlays/pps-gpio.dtbo  /boot/firmware/overlays/pps-gpio.dtbo.bak

pps-gpioが使えるようですね。というわけで、config.txtの最後のところに以下の記述を追加します。
/boot/firmware/config.txt

dtoverlay=pps-gpio,gpiopin=18

「18」の所は、自分の環境で接続したピンに合わせて変更してください。
https://www.raspberrypi.org/documentation/computers/os.html#gpio-and-the-40-pin-header

「assert_falling_edge=true」というオプションを追加するという記事もありますが、これは使用しているGPSモジュールによっては必要な場合があるそうです。
mirahouse.jp
nezuku.hatenablog.com
太陽誘電製なのでつけました」ということを書いている記事が複数ありますので、ここらへんは調べてください。

あとは「/etc/modules」への追記も忘れずに。

/etc/modules

# /etc/modules: kernel modules to load at boot time.
#
# This file contains the names of kernel modules that should be loaded
# at boot time, one per line. Lines beginning with "#" are ignored.
pps-gpio

PPSの信号受信を確認するために「pps-tools」パッケージをインストールしておきます。

sudo apt install pps-tools

ここまで出来たら再起動。再起動出来たら、「/dev/pps0」が生えていることを確認してからppstestで出力を確認してみます。

sudo ppstest /dev/pps0

こんな感じで1秒ごとに行が追加されたらOKです。

trying PPS source "/dev/pps0"
found PPS source "/dev/pps0"
ok, found 1 source(s), now start fetching data...
source 0 - assert 1633332678.000000270, sequence: 67125 - clear  0.000000000, sequence: 0
source 0 - assert 1633332678.999998645, sequence: 67126 - clear  0.000000000, sequence: 0
source 0 - assert 1633332679.999997927, sequence: 67127 - clear  0.000000000, sequence: 0
source 0 - assert 1633332680.999996708, sequence: 67128 - clear  0.000000000, sequence: 0
source 0 - assert 1633332681.999999007, sequence: 67129 - clear  0.000000000, sequence: 0

ntpサーバーの環境を作る

ここまでで、「/dev/ttyUSB0」と「/dev/pps0」を使えばGPSとPPSの情報が取れそうな感じになっています。次はntpサーバーです。
今回は、「ntpd」を使用します。「chrony」もよさげなんですが、情報がありそうな「ntpd」を選びます。まずはパッケージをインストールします。

sudo apt install ntp

www.seekers.jp
次は、ntpdで使用するデバイスファイルを自動作成するようにします。「/etc/udev/rules.d/」にファイルを作ります。私は「10-gps.rules」にしました。

/etc/udev/rules.d/10-gps.rules

KERNEL=="ttyUSB0",MODE="0666",SYMLINK+="gps0"
KERNEL=="pps0",OWNER="root",GROUP="dialout",MODE="0666",SYMLINK+="gpspps0"

パーミッションを「0666」にしていますが、「0660」でよいのではないかという話もあります。これは、構築時に苦労したときにひとまずこうした結果を残しているだけです。
次に、ntpユーザをdialoutグループに追加します。

sudo usermod -aG dialout ntp

最後に、ntpdで使用する設定ファイルを書き換えます。追記部分はこちら。
/etc/ntp.conf

server 127.127.20.0 mode 17 minpoll 4 maxpoll 4 prefer
fudge 127.127.20.0 refid GPS
fudge 127.127.20.0 flag1 1 flag3 1

こちらは、以下の記事の「Generic NMEA GPS Receiver の設定について」という個所を参考にしています。
www.netfort.gr.jp
詳細は公式のこちらを。
www.eecis.udel.edu

デフォルトで入っているpoolの設定はコメントアウトします。
/etc/ntp.conf

# Use servers from the NTP Pool Project. Approved by Ubuntu Technical Board
# on 2011-02-08 (LP: #104525). See http://www.pool.ntp.org/join.html for
# more information.
#pool 0.ubuntu.pool.ntp.org iburst
#pool 1.ubuntu.pool.ntp.org iburst
#pool 2.ubuntu.pool.ntp.org iburst
#pool 3.ubuntu.pool.ntp.org iburst

# Use Ubuntu's ntp server as a fallback.
#pool ntp.ubuntu.com

さて、ntpdの設定までこぎつけました。あと少しだけやることがあります。必要かどうかは各自で判断してください。

DHCPIPアドレスを取得している場合の対応

最終的には固定IPアドレスにするかと思いますが、ひとまずDHCPを使っている場合に困らないように「/etc/dhcp/dhclient.conf」内のrequestの記述から「ntp-servers」を取り除きます。

/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, dhcp6.fqdn, dhcp6.sntp-servers,
        netbios-name-servers, netbios-scope, interface-mtu,
        rfc3442-classless-static-routes;
OS再起動時の時刻合わせ対応

これも、ntpdが上手く動いていたらいらないかもしれませんがやっておきます。
/etc/rc.local

#!/bin/sh
/bin/systemctl stop ntp.service
/bin/stty -F /dev/ttyUSB0 9600
/usr/sbin/ntpdate ntp.nict.jp
/bin/systemctl start ntp.service

「rc.localってUbuntu 20.04でも動くの?」という方はこちらを読んでください。
qiita.com
ntpdateコマンドはインストールされていないのでインストールしておきます。

sudo apt install ntpdate

これで、たぶんOS再起動時にはntp.nict.jpを使って時刻合わせをするはず。systemd-timesyncdサービスがなにかやっているかもしれないが、「ntpパッケージをインストールしたらsystemd-timesyncdは動かない」という話も聞いたので。本当はRTCを保持できるようにしたほうがよいでしょうね。
あ、rc.localのパーミッションはこうしています。

sudo chmod 700 /etc/rc.local
念のためにntpサービスを有効化

多分不要かと思うが念のため。

sudo systemctl enable ntp
なんかntpdが動かないと思ったらAppArmorを疑う

「journalctl -xe -u ntp」でログを見ると「refclock_open /dev/gps0: Permission denied」という赤い文字列がでてきて「ntpd exiting on signal 15 (Terminated)」という感じに止まっている場合があります。最初なにがなんだかわからずに色々調べました。そうしたら、ここらへんが出てきました。
jet-blog.com
qiita.com
kinneko.hatenadiary.org
www.linux-setting.tokyo
要するに、「AppArmorがntpdを守っているのでなんとかしないとこのままでは動かない」らしい。
筋の良い対応はこちらのようだが、たどり着く前に別の方法をやってしまった。
rohhie.net

sudo apt install apparmor-utils
sudo ln -s /etc/apparmor.d/usr.sbin.ntpd /etc/apparmor.d/disable
sudo apparmor_parser -R /etc/apparmor.d/usr.sbin.ntpd 

これで動くと「ntpq -p」の出力はこうなります。
f:id:kaias1jp:20211004174246p:plain
「oGPS_NMEA(0) .GPS. 」となっていればよいらしいです。なお、画像では比較用にntp.nict.jpもntp.confに登録していますが、本番運用ではいらないでしょう。
「ntpq -c rv」の結果はこんな感じ。
f:id:kaias1jp:20211004174620p:plain
読み方は調べてください。
qiita.com

ntpサーバーは動きました。あとはクライアントから問い合わせができるようにするだけです。

ntpサーバーをLAN内に公開する

ファイヤーウォールの制御は、ufwが楽だと思うので使います。

sudo ufw allow ssh
sudo ufw allow ntp
sudo ufw enable

ntpdでアクセス制限をかける場合は、ntp.confに書けばいいはずですが今回は何もしていません。ブロードキャストもしていません。
公開できると、クライアントから時刻の問い合わせができます。例えば、Windows11だとこんな感じ。
f:id:kaias1jp:20211004180455p:plain
f:id:kaias1jp:20211004180558p:plain
f:id:kaias1jp:20211004180747p:plain
f:id:kaias1jp:20211004180803p:plain
f:id:kaias1jp:20211004180813p:plain
ルーターDNS機能でntp.lanに紐づけしています。

さあ、みなさんもレッツトライ。