Create a gist now

Instantly share code, notes, and snippets.

What would you like to do?
秋月電子 UART(シリアル)接続GPS(2016.2発売)をRaspberry Pi 3につなげて、GPS出力をgpsdで受ける

Raspberry Pi 3 B+ を NOOBS 1.92からセットアップ直後からスタートする。

利用するGPS製品ははこれ

GPS受信機キット 1PPS出力付き 「みちびき」対応: センサ一般 秋月電子通商 電子部品 ネット通販

Vcc GND Txd Rxd と 1pps出力の5線あるうち1pps出力以外の4線を利用する。

Vcc に 5V、TxdとRxdは3.3VでOK

製品購入状態では、UART(シリアル)9600bpsでNMEA 0183 フォーマットの測地情報 を吐き出すので、Raspberry Pi側でNMEAフォーマットを解析する gpsd を利用して位置情報 を取得する。

① Wi-Fi設定

NOOBS 1.92でセットアップした直後のRaspbian Jessieでは、GUIブートしますので、GUI画面で[Ctrl]+[Alt]+T (若しくは画面の上部にある黒い画面のコンピュータアイコンをダブルクリック)で、 LXTerminal を起動します。その中で、設定を入力していきます。

(GUIが起動したら、Ctrl-Alt-F1でtty1に降りてもOK)

RaspberryPi: raspi-config コマンドラインから設定(ノンイタラクティブ) - Qiitaより

pi@raspberrypi:~ $ sudo raspi-config nonint do_wifi_country JP
pi@raspberrypi:~ $ sudo wpa_cli wps_pbc
Selected interface 'wlan0'
OK
pi@raspberrypi:~ $

以降、他マシン( mDNS環境・Bonjour or Avahi 導入済みの環境)から ssh pi@raspberrypi.local でログインして設定する。(パスワードは raspberry ) mDNS環境じゃない場合は、ifconfigwlan0 に割り当てられた IPアドレスへ、他マシンからログインする。

② Timezone設定

Raspbian Jessieは systemd ベースなので、 sudo dpkg-reconfigure --frontend noninteractive tzdat ではなく、 sudo timedatectl set-timezone Asia/Tokyo で設定する。

pi@raspberrypi:~ $ timedatectl status
      Local time: Fri 2016-05-27 11:51:06 UTC
  Universal time: Fri 2016-05-27 11:51:06 UTC
        RTC time: n/a
       Time zone: Etc/UTC (UTC, +0000)
     NTP enabled: no
NTP synchronized: yes
 RTC in local TZ: no
      DST active: n/a
pi@raspberrypi:~ $ sudo timedatectl set-timezone Asia/Tokyo
pi@raspberrypi:~ $ timedatectl status
      Local time: Fri 2016-05-27 20:51:35 JST
  Universal time: Fri 2016-05-27 11:51:35 UTC
        RTC time: n/a
       Time zone: Asia/Tokyo (JST, +0900)
     NTP enabled: no
NTP synchronized: yes
 RTC in local TZ: no
      DST active: n/a
pi@raspberrypi:~ $

③ ロケール設定

Raspberry Piデフォルトでは en_GB.utf8 しか生成されていないので、 ja_JP.utf8 を生成する。 ロケールの設定は systemd スタイルのlocalectl で行う。 ついでに、 jfbterm を入れると同時に入る日本語フォントで、(GUIは使わないが) GUIの文字化けを防ぐ。 ついでに、GUI用の日本語変換 ibus-anthy も入れておく。

※256MB Raspberry Pi A+では、jfbterm ibus-anthy のインストールが非常に時間がかかるため、後回しにしてもよい。

pi@raspberrypi:~ $ sudo sed -i -e '/ja_JP.UTF-8/s/^# *//' /etc/locale.gen
pi@raspberrypi:~ $ sudo locale-gen
Generating locales (this might take a while)...
  ja_JP.UTF-8... done
Generation complete.
pi@raspberrypi:~ $ sudo localectl set-locale LANG=ja_JP.utf8
pi@raspberrypi:~ $ sudo apt-get -y install jfbterm ibus-anthy

④ キーボードレイアウト設定

これらも Debian スタイルではなくて、systemd スタイルで設定する。

pi@raspberrypi:~ $ localectl status
   System Locale: LANG=ja_JP.utf8
       VC Keymap: n/a
      X11 Layout: gb
       X11 Model: pc105
pi@raspberrypi:~ $ sudo localectl set-keymap jp106
pi@raspberrypi:~ $ localectl status
   System Locale: LANG=ja_JP.utf8
       VC Keymap: jp106
      X11 Layout: jp
       X11 Model: jp106
     X11 Options: terminate:ctrl_alt_bksp
pi@raspberrypi:~ $ 

CUIブート、piユーザ自動ログインに切り替え

RaspberryPi: raspi-config コマンドラインから設定(ノンイタラクティブ) - Qiitaより

pi@raspberrypi:~ $ sudo raspi-config nonint do_boot_behaviour B2

⑤ Raspberry Pi B+以降のUSB供給電流を上げる設定

pi@raspberrypi:~ $ cat << EOS | sudo tee -a  /boot/config.txt

max_usb_current=1
safe_mode_gpio=4
EOS

設定後、 reboot で有効になるが、次の設定で reboot するので、ここでは reboot を実行しない。

⑥ swapを使わないようにする

Rasbpian では dphys-swapfile というデーモンで swap ファイルを作って、システムに接続しているので、このデーモンを止めるとよい。

pi@raspberrypi:~ $ free
             total       used       free     shared    buffers     cached
Mem:        947748     140876     806872       6520      17336      64988
-/+ buffers/cache:      58552     889196
Swap:       102396          0     102396
pi@raspberrypi:~ $ sudo dphys-swapfile swapoff
pi@raspberrypi:~ $ free
             total       used       free     shared    buffers     cached
Mem:        947748     142100     805648       6520      17368      65532
-/+ buffers/cache:      59200     888548
Swap:            0          0          0
pi@raspberrypi:~ $ # sudo dphys-swapfile swapon  # swapを使いたくなったら
pi@raspberrypi:~ $ sudo systemctl stop dphys-swapfile
pi@raspberrypi:~ $ sudo systemctl disable dphys-swapfile
Synchronizing state for dphys-swapfile.service with sysvinit using update-rc.d...
Executing /usr/sbin/update-rc.d dphys-swapfile defaults
Executing /usr/sbin/update-rc.d dphys-swapfile disable
insserv: warning: current start runlevel(s) (empty) of script `dphys-swapfile' overrides LSB defaults (2 3 4 5).
insserv: warning: current stop runlevel(s) (2 3 4 5) of script `dphys-swapfile' overrides LSB defaults (empty).
pi@raspberrypi:~ $ 

⑦ Watchdog導入

Raspberry Piの暴走を検知するWatchdog timerを入れる。

http://machiawasepoint.com/raspberry-pi-jessie%E3%81%ABwatchdog%E3%82%92%E3%82%A4%E3%83%B3%E3%82%B9%E3%83%88%E3%83%BC%E3%83%AB/ を参照した。

pi@raspberrypi:~ $ sudo apt-get -y install watchdog
pi@raspberrypi:~ $ sudo sed -i -e '/\[Install\]/a WantedBy=multi-user.target' /lib/systemd/system/watchdog.service
#(これがないと watchdogをsystemctlでenableできない)

pi@raspberrypi:~ $ sudo systemctl enable watchdog
pi@raspberrypi:~ $ sudo modprobe bcm2835_wdt
# これで /dev/watchdog ができる

pi@raspberrypi:~ $ cat << 'EOS'| sudo tee -a /etc/watchdog.conf

max-load-1              = 24
watchdog-device = /dev/watchdog
watchdog-timeout = 10
EOS
pi@raspberrypi:~ $ sudo systemctl start watchdog

テストは後でやろう

pi@raspberrypi:~ $ :(){ :|:& };:
[1] 1239
pi@raspberrypi:~ $

1分ほどで1分足のLoad averageが24以上になっていることが検知され、自動でrebootがかかる。

SoCの機能のWatchdog Timerはテストできていないんじゃないですかね……

⑧ Raspberry Pi 3用のUART設定

Raspberry Pi 3は、 GPIO 14(TXD) / GPIO 15(RXD) は今までの /dev/ttyAM0 ではなくて /dev/ttyS0 で利用するので、注意。

pi@raspberrypi:~ $ cat << EOS | sudo tee -a  /boot/config.txt

core_freq=250
enable_uart=1
EOS

RaspberryPi: raspi-config コマンドラインから設定(ノンイタラクティブ) - Qiitaより

pi@raspberrypi:~ $ cat /boot/cmdline.txt
dwc_otg.lpm_enable=0 console=serial0,115200 console=tty1 root=/dev/mmcblk0p7 rootfstype=ext4 elevator=deadline fsck.repair=yes rootwait
pi@raspberrypi:~ $ grep -e enable_uart /boot/config.txt
enable_uart=1
pi@raspberrypi:~ $ sudo raspi-config nonint do_serial 1
pi@raspberrypi:~ $ cat /boot/cmdline.txt
dwc_otg.lpm_enable=0 console=tty1 root=/dev/mmcblk0p7 rootfstype=ext4 elevator=deadline fsck.repair=yes rootwait
pi@raspberrypi:~ $ grep -e enable_uart /boot/config.txt
enable_uart=0

/dev/ttyS0(/dev/ttyAMA0)はデバイスとして認識して欲しいので、/boot/config.txt に enable_uart=1 を入れます(raspi-configでは enable_uart=0 に設定されてしまいます)。

pi@raspberrypi:~ $ sudo sed -i -e 's/enable_uart=0/enable_uart=1/' /boot/config.txt

serial-getty@ttyS0.service を止め、UARTgetty を切り離します。

pi@raspberrypi:~ $ sudo systemctl stop serial-getty@ttyS0.service
pi@raspberrypi:~ $ sudo systemctl mask serial-getty@ttyS0.service

設定を有効にする( /dev/ttyS0getty から切り離す)ため reboot します。

/dev/gps0 の設定

reboot後、 /dev/ttyS0 の通信速度のデフォルト値を設定します。

pi@raspberrypi:~ $ sudo stty -F /dev/ttyS0 ispeed 9600

さらに、 GPSを /dev/gps0 でアクセスできるように、 udev にて シンボリックリンクを作るようにします。Raspberry 3以前では、UARTが /dev/ttyAMA0 となりますので、ここで KERNEL=="ttyAMA0" としておけば、以降デバイス名の際は吸収できます。

Raspberry Pi 3の場合:

pi@raspberrypi:~ $ sudo cat << 'EOS' | sudo tee /etc/udev/rules.d/40-selialgps.rules
KERNEL=="ttyS0", SYMLINK+="gps0"
EOS

Raspberry Pi 3より前の場合

pi@raspberrypi:~ $ sudo cat << 'EOS' | sudo tee /etc/udev/rules.d/40-selialgps.rules
KERNEL=="ttyAMA0", SYMLINK+="gps0"
EOS

/dev/gps0/dev/ttyS0 もしくは /dev/ttyAMA0 を kernelが認識するタイミング つまりboot時に作られるので、リブートします。

#sudo udevadm control --reload-rules しなくても良い
pi@raspberrypi:~ $ sudo reboot

gpsd をインストール

GPSから出てくる情報を解析する gpsd デーモンをインストールします。

pi@raspberrypi:~ $ sudo apt-get -y install gpsd gpsd-clients

gpsd の設定

本当は /dev/ttyS0 からGPSの情報が読めるかとか、 gpsd がそれを読み込めるかとか、さらに /dev/gps0 からGPS情報が 読み取れるかとか、試行錯誤するのだけれども、 試行錯誤した結果以下の設定で読めることが分かった。

pi@raspberrypi:~ $ cat << EOS | sudo tee -a  /etc/default/gpsd

USBAUTO="false"
DEVICES="/dev/gps0"
EOS

gpsd を起動

pi@raspberrypi:~ $ sudo systemctl start gpsd
pi@raspberrypi:~ $ sudo systemctl enable gpsd
Synchronizing state for gpsd.service with sysvinit using update-rc.d...
Executing /usr/sbin/update-rc.d gpsd defaults
Executing /usr/sbin/update-rc.d gpsd enable
pi@raspberrypi:~ $

cgps でモニタリング

pi@raspberrypi:~ $ cgps -s

⑭ ntpに組み込む

http://qiita.com/athlonz/items/fb1e5a7d8b1a4e073d8c http://nyanchew.com/jp/gps%E3%81%AEpps%E4%BF%A1%E5%8F%B7%E3%82%92%E4%BD%BF%E3%81%A3%E3%81%9F-stratum-1-ntp%E3%82%B5%E3%83%BC%E3%83%90%E3%81%AE%E4%BD%9C%E3%82%8A%E6%96%B9

モジュールの読み込みを設定する。

pi@raspberrypi:~ $ echo pps-gpio | sudo tee -a /etc/modules

モジュールの動作パラメータを設定する。

pi@raspberrypi:~ $ echo dtoverlay=pps-gpio,gpiopin=18 | sudo tee -a /boot/config.txt

リブート前。 pps_gpio pps_core が入っていない。

pi@raspberrypi:~ $ lsmod
Module                  Size  Used by
ppp_deflate             4520  0 
zlib_deflate           22039  1 ppp_deflate
bsd_comp                6079  0 
ppp_async               8326  1 
crc_ccitt               1732  1 ppp_async
ppp_generic            26985  7 bsd_comp,ppp_async,ppp_deflate
slhc                    5997  1 ppp_generic
cdc_acm                18718  2 
snd_bcm2835            23163  0 
snd_pcm                95473  1 snd_bcm2835
snd_timer              22396  1 snd_pcm
snd                    68368  3 snd_bcm2835,snd_timer,snd_pcm
i2c_bcm2708             5740  0 
bcm2835_gpiomem         3823  0 
bcm2835_wdt             4133  1 
uio_pdrv_genirq         3718  0 
uio                    10230  1 uio_pdrv_genirq
i2c_dev                 6578  2 
fuse                   89800  1 
ipv6                  367575  28 
pi@raspberrypi:~ $ sudo reboot
Connection to localhost closed by remote host.
Connection to localhost closed.

リブート後には、 pps_gpio pps_core が増えている。

pi@raspberrypi:~ $ lsmod
Module                  Size  Used by
ppp_deflate             4520  0 
zlib_deflate           22039  1 ppp_deflate
bsd_comp                6079  0 
ppp_async               8326  1 
crc_ccitt               1732  1 ppp_async
ppp_generic            26985  7 bsd_comp,ppp_async,ppp_deflate
slhc                    5997  1 ppp_generic
cdc_acm                18718  2 
snd_bcm2835            23163  0 
snd_pcm                95473  1 snd_bcm2835
snd_timer              22396  1 snd_pcm
snd                    68368  3 snd_bcm2835,snd_timer,snd_pcm
i2c_bcm2708             5740  0 
bcm2835_gpiomem         3823  0 
bcm2835_wdt             4133  1 
uio_pdrv_genirq         3718  0 
uio                    10230  1 uio_pdrv_genirq
pps_gpio                2993  0 
pps_core                8756  1 pps_gpio
i2c_dev                 6578  2 
fuse                   89800  1 
ipv6                  367575  28 
pi@raspberrypi:~ $ lsmod | grep pps
pps_gpio                2993  0 
pps_core                8756  1 pps_gpio
pi@raspberrypi:~ $ sudo sed -i -e '/^server [0-9].debian.pool.ntp.org/s/^/#/' /etc/ntp.conf 
pi@raspberrypi:~ $ cat << 'EOS' | sudo tee -a /etc/ntp.conf 

server ntp.nict.jp iburst prefer
server 127.127.22.0 minpoll 4 maxpoll 4
fudge 127.127.22.0 flag3 1 refid PPS
EOS
pi@raspberrypi:~ $ sudo systemctl restart ntp
pi@raspberrypi:~ $ ntpq -p

node.js v6系インストール

pi@raspberrypi:~ $ curl -sL https://deb.nodesource.com/setup_6.x | sudo -E bash -
pi@raspberrypi:~ $ sudo apt-get update
pi@raspberrypi:~ $ sudo apt-get install -y nodejs

注意:ARMv6系SoC採用のRaspberry Pi(1/1+/Zero)では、Node.js公式のDebian・Ubuntu系パッケージリポジトリは使用できない模様。

pi@raspberrypi:~ $ curl -sL https://deb.nodesource.com/setup_6.x | sudo -E bash -

## Installing the NodeSource Node.js v6.x repo...


## You appear to be running on ARMv6 hardware. Unfortunately this is not currently supported by the NodeSource Linux
 distributions. Please use the 'linux-armv6l' binary tarballs available directly from nodejs.org for Node.js v4 and
later.

node.jsgpsd クライアント制作

pi@raspberrypi:~ $ mkdir testapp
pi@raspberrypi:~ $ cd testapp
pi@raspberrypi:~/testapp $ npm install node-gpsd
pi@raspberrypi:~/testapp $ nano app.js
'use strict'

var gpsd = require('node-gpsd');

var gpsListener = new gpsd.Listener({
  port: 2947,
  hostname: 'localhost',
  logger:  {
    info: function() {
    },
    warn: console.warn,
    error: console.error
  },
  parse: true
});

function startGPS () {
  gpsListener.connect();
  gpsListener.watch();
  gpsListener.on('TPV', function (tpvData) {
    console.log(tpvData);
  });
}

startGPS();

実行

pi@raspberrypi:~/testapp $ node app.js

⑯ AWS-IOTのアプリを作る

git clone https://github.com/aws/aws-iot-device-sdk-js.git
cd aws-iot-device-sdk-js
npm install
cd aws-iot-device-sdk-js/
  • certificate.pem.crt
  • private.pem.key
  • public.pem.key

AWS-IoT で Raspberry Piとして登録した時に出来たそれを配置する

nano examples/device-example2.js
//node.js deps

//npm deps

//app deps
const deviceModule = require('..').device;
const cmdLineProcess = require('./lib/cmdline');
var gpsd = require('node-gpsd');
var execSync = require('child_process').execSync;

var deviceid = 0; // GPIOにつながったDIP SWで決定させるなど
console.log("DeviceId: " + deviceid);

//begin module

function processTest(args) {
   //
   // The device module exports an MQTT instance, which will attempt
   // to connect to the AWS IoT endpoint configured in the arguments.
   // Once connected, it will emit events which our application can
   // handle.
   //
   const device = deviceModule({
      keyPath: args.privateKey,
      certPath: args.clientCert,
      caPath: args.caCert,
      clientId: args.clientId,
      region: args.region,
      baseReconnectTimeMs: args.baseReconnectTimeMs,
      keepalive: args.keepAlive,
      protocol: args.Protocol,
      port: args.Port,
      host: args.Host,
      debug: args.Debug
   });

   const gpsListener = new gpsd.Listener({
      port: 2947,
      hostname: 'localhost',
      logger:  {
        info: function() {
        },
        warn: console.warn,
        error: console.error
      },
      parse: true
   });


   // 'topic_1' というトピック名で送信
   device.subscribe('topic_1');

   var getRaspberryPiTemperature = function() {
      // execSyncはnode v0.11からの機能
      var temperature = "" + execSync('vcgencmd measure_temp');
      // 出力形式は "59.6'C\n" となるので置換で単位と改行を取り除く
      temperature = temperature.replace(/'C\n/,'').split('=')[1];
      return temperature;
   };

   var startGPS = function() {
      gpsListener.connect();
      gpsListener.watch();
      gpsListener.on('TPV', function (tpvData) {

         //exec('vcgencmd measure_temp', function(err, stdout, stderr){
            temperature = getRaspberryPiTemperature();
            console.log(temperature);
         //});
         // var epoch = ((new Date).getTime()).toString().substring(10,-1);
         var epoch = Date.parse(tpvData.time).toString().substring(10,-1);
         device.publish('topic_1', JSON.stringify({
            deviceid: deviceid,
            timestamp: epoch,
            gpsinfo: tpvData,
            temperature: temperature
         }));
         //console.log(tpvData);
      });
   };
   startGPS();

   device
      .on('connect', function() {
         console.log('connect');
      });
   device
      .on('close', function() {
         console.log('close');
      });
   device
      .on('reconnect', function() {
         console.log('reconnect');
      });
   device
      .on('offline', function() {
         console.log('offline');
      });
   device
      .on('error', function(error) {
         console.log('error', error);
      });
   device
      .on('message', function(topic, payload) {
         console.log('message', topic, payload.toString());
      });

}

module.exports = cmdLineProcess;

if (require.main === module) {
   cmdLineProcess('connect to the AWS IoT service and publish/subscribe to topics using MQTT, test modes 1-2',
      process.argv.slice(2), processTest);
}
node examples/device-example2.js -g ap-northeast-1
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment