Arduinoを使って、GPS位置情報をイーサネットでPCに送る

GPSモジュール(GPS受信機)を利用すれば、陸上でも海上でも、GPS衛星からの信号を受信し、自身の現在位置を知ることができる。
GPS衛星からの信号には、日付・日時の情報も含まれているため、現在位置だけでなく現在時刻を知ることもできる。

図1 装置外観

本研究室では今回、Arduinoを用いてGPSモジュールから位置情報・時刻情報を読み取り、イーサネットでPCに送信する装置(図1)を開発した。
しかし、その程度のことは既に大勢の人々がやっていそうであるため、もう一工夫したいところである。
そこで、取得した位置情報(緯度・経度)データをただ表示するだけではなく、
Googleマップでその位置の地図を表示するURLリンクを生成し、緯度・経度データと合わせて表示するプログラムを作成した。

図2 装置概要図

以下の流れで開発を進めた。着手から完成までの所要日数は3日であった。

1.GPSモジュールから位置情報・時刻情報などが含まれたデータを取得する。
2.GPSモジュールから取得したデータをArduinoでデータ処理し、位置情報・時刻情報を取り出す。
3.位置情報データから、GoogleマップURLリンクを生成する。
4.Arduinoイーサネットシールドを使い、位置情報・時刻情報・GoogleマップURLをPCに送信する。

次に、上に挙げた各開発段階について、詳しく説明する。


1.GPSモジュールから位置情報・時刻情報などが含まれたデータを取得する。

今回使用したGPSモジュールは、高感度小型GPSモジュールである。(研究室に転がっていた物のため型番不明。説明書からStrawberry Linuxで2005年以降に販売されていたものだと推測できるが、2016年12月現在は販売していない模様。)
このGPSモジュールから出力されるデータのフォーマットは、NMEA-0183(GPSで最も一般的な通信フォーマット)に準拠している。
Arduinoとの通信は、TTLレベルのシリアル通信で行った。
Arduinoと通信を行う前に、GPSモジュールからデータが出力されているか確認するため、GPSモジュールとPCをUSB-シリアル変換ケーブルを使って直接接続した。
PC側ではTeraTermを使ってGPSモジュールから送られてくるデータ(リスト1)を確認した。

リスト1 GPSモジュールから送られてくるデータ


リスト1のように、NMEA-0183フォーマットのメッセージは、$GPで始まることがわかる。
$GPに続く3文字の記号が、メッセージの種類を表している。今回使用したGPSモジュールは、GSA, GSV, GGA, RMCの4種類のメッセージを出力する。
今回は簡単のため、緯度・経度情報、日付・時刻情報が含まれているRMCメッセージのみを使用した。
RMCメッセージの内容を以下の表1に示す。その他GSA,GSV,GGAメッセージの内容についてはここでは割愛する。

表1 RMCメッセージ


※説明書には、**度**分**秒**と書かれていたが、これは間違いであることがわかった。詳細は後述。


2.GPSモジュールから取得したデータをArduinoでデータ処理し、位置情報・時刻情報を取り出す。

Arduino UNOとGPSモジュールをシリアル接続した。今回使用したGPSモジュールの通信レートは4800bpsである。Arduino UNOのソフトウェアシリアル機能を用いて、GPSモジュールからのデータを受信する。GPSモジュールとの通信にハードウェアシリアルではなくソフトウェアシリアルを用いた理由は、ハードウェアシリアルをPCとの通信用として使用できるようにするためである。最終的にArduinoとPC間の通信はシリアルではなくイーサネットを使用するが、デバッグ時やプログラム書き込み時にはArduinoとPCをUSBケーブルで直接繋ぎ、シリアル通信を行う必要がある。

Arduinoが行うデータ処理の内容は以下の通りである。
(1) GPSモジュールから取得したデータを1メッセージごとに読み込む。
(2) RMCメッセージの場合、(3)以降の処理を行う。それ以外のメッセージは無視する。
(3) RMCメッセージに含まれる , (カンマ)の数を数える。抜け落ち無くメッセージを受信できていれば、RMCメッセージに含まれるカンマの数は12である。カンマの数が12であれば処理を継続。
(4) データのステータスがA(有効)になっているかどうか確認。有効の場合は処理を継続。
(5) 時刻(UTC)、日付情報を読み取る。
(6) 時刻(UTC)から日本時間(JST)を計算。日本時間における日付に関してはプログラムが複雑化するため計算しない。
(7) 緯度・経度情報を読み取る。
(8) 経度・緯度情報から、GoogleマップURLリンクを生成。(3章で述べる)

今回最初に作成したデータ処理プログラムには、大きな問題があった。それは、メッセージ内の各項目がカンマ区切りの書式になっているのにも関わらず、データ処理時に「各項目をカンマ区切りで取得する」という方法をとらず、「取得したい項目は、先頭から何文字目から何文字目までに書いてあるため、その部分を切り取る」という方法をとっていることである。これにより生じる不具合は、緯度や経度の桁が変わったときに正しいデータ処理を行えなくなることである。例えば、日本にいる場合は、北緯は20度から45度、東経は122度から153度の範囲内で変化し、緯度・経度の桁が変わることはないので問題は生じない。しかし、例えば英国スコットランドのアバディーンという町にいる場合は、北緯は57度だが西経は2度であり、日本と桁の違いが生じる。よって、最初に作成したプログラムの場合、海外で使用することは推奨されない。また、このプログラムを使用すると他の機種のGPSモジュールを使用した場合に一部データが正常に取得できないという問題が報告された。これについては、同じNMEA-0183フォーマットに準拠しているGPSモジュールでも、機種ごとにデータの表示桁数に違いがあるためであると考えている。

そこで、データ処理時に「各項目をカンマ区切りで取得する」という方法をとるようにプログラムを改良した。Arduinoでカンマ区切りのデータを分ける方法として、sscanf関数やstrtok関数を利用する方法がある。まず、sscanf関数を用いる方法を検討したが、Arduinoではsscanf関数にfloat型の指定%fが使えないことが判明し、プログラムがより複雑になってしまうため用いなかった。そこで今回はstrtok関数を利用した。strtok関数で文字列をカンマ区切りで切り出し、切り出した文字列を必要に応じてatof関数で実数にした。strtok関数の問題点としては、Arduinoのプログラムデータサイズが非常に大きくなってしまうということがあげられる。


3.位置情報データから、GoogleマップURLリンクを生成する。

GPSモジュールから位置情報や時刻情報を取得して、それらをただ表示するプログラムやGPSロガーを自作する例は、インターネットで調べれば何件も見つかる。

本装置を開発するにあたり、私が思ったことは、
・多くの人がやっていることと同じことをするのは面白くない!
・緯度・経度の数字だけ見てもそこがどこだかわからない!
ということである。

Googleマップは、特殊なソフトを必要とせず、インターネットに接続できる環境でインターネットブラウザがインストールされていれば誰でも閲覧できる地図である。これを使えば、位置情報を視覚的にわかりやすく認識することができる。
ちなみにGoogleマップでは、
http://maps.google.com/maps?q=緯度,経度
というURLにアクセスすると、その緯度経度座標にマーカーが立った地図が表示されるようになっている。つまり、GPSモジュールから取得した緯度経度情報をこのURLに当てはめてしまえば、現在位置にマーカーが立った地図をGoogleマップで表示するURLリンクを生成することができるのである。

「こんなの簡単!一瞬で完成! さあ、クリックしてみよう 現在地である江東区越中島 東京海洋大学越中島キャンパスが表示されるはず ポチ!」

図3 湘 南 台

この後、他の時間に取得したデータに切り替えてURLを生成しても、なぜか必ず約50km離れた神奈川県藤沢市湘南台になった。

調べてみると、原因がわかった。

今回の計算では、GPSモジュールから出力される緯度経度座標をそれぞれ単純に100で割っていた。すると、35.399721,139.475788 という座標になる。これをGoogleマップURLに当てはめると
http://maps.google.com/maps?q=35.399721,139.475788
となる。このリンクをクリックすると、現在地から約50km離れた湘南台の地図が表示される。

GPSモジュールの取扱説明書をよく読んでみると、
3539.9721, N は 北緯35度39分97秒21
13947.5578, E は 東経139度47分55秒78
と解釈するという記述があった。

一方、Googleマップについてよく調べてみると、GoogleマップのURLの座標は60進数ではなく10進数で表現しなければならないことがわかった。
つまり、GPSモジュールから出力されるNMEA-0183フォーマットの60進数表記の緯度経度座標を、Googleマップ用の10進数表記の緯度経度座標に変換する必要がある。

北緯35度39分97秒21を10進数に直すと、
35+(39/60)+(97.21/60/60)=35.67700278
東経139度47分55秒78を10進数に直すと、
139+(47/60)+(55.78/60/60)=139.7988278
これをGoogleマップURLに当てはめると
http://maps.google.com/maps?q=35.67700278,139.7988278
となる。このリンクをクリックすると、江東区深川2丁目付近の地図が表示される。

あれ・・・?

図4 約 1.5 km の 誤 差

湘南台と比べれば大きく近づいたものの、測定地点である江東区越中島 東京海洋大学越中島キャンパスからはまだ約1.5kmの誤差が生じた。
今回使用したGPSモジュールの説明書には、精度: 2m~25mと記載されている。1.5kmの誤差は、GPSの測定誤差とは考えにくい。

しばらく考えた結果、原因がわかった。

GPSモジュールの取扱説明書に書いてあった、
3539.9721, N は 北緯35度39分97秒21
13947.5578, E は 東経139度47分55秒78
と解釈するという方法は間違いだったのである。
冷静に考えれば、60進法表記において、97秒など存在しないはずである

正しくは、
3539.9721, N は 北緯35度39.9721分
13947.5578, E は 東経139度47.5578分
と解釈するのが正しいという結論に至った。

北緯35度39.9721分を10進数に直すと、
35+(39.9721/60)=35.66620167
東経139度47.5578分を10進数に直すと、
139+(47.5578/60)=139.79263
これをGoogleマップURLに当てはめると
http://maps.google.com/maps?q=35.66620167,139.79263
となる。このリンクをクリックすると、測定地点である江東区越中島 東京海洋大学越中島キャンパス 田原研究室の位置にピンの立った地図が表示される。

図5 正しい位置を示した地図

素晴らしい!

ちなみに、南半球の場合、GoogleマップURLの緯度の部分は負の値の座標になる。西半球の場合、経度の部分が負の値の座標になる。
日本にいる場合は、北半球・東半球のため、座標が負の値になることはないが、全世界に対応できるよう、南半球(S)の場合は緯度座標を-1倍、西半球(W)の場合は経度座標を-1倍し、URLが生成されるようにした。


4.Arduinoイーサネットシールドを使い、位置情報・時刻情報・GoogleマップURLをPCに送信する。

さて、ここまでできれば後はArduinoとPC間の通信をUSBからイーサネットに切り替えるだけである。
Arduino UNOにイーサネット機能を追加するため、Arduino Ethernet Shield R3を使用した。
ArduinoイーサネットシールドとPCはLANケーブル(クロスケーブル)で接続した。

Arduino電源投入後は待機状態になるようプログラムした。PC(TeraTerm)からArduinoに何かデータを送ると待機状態が解除され、GPSデータ処理を開始する。
PCとの接続が切れると、再び待機状態になる。

ArduinoからイーサネットでPCに送信されるデータのフォーマットは次のようにした。
, 西暦/月/日(UTC) , 時:分:秒(UTC) , 時:分:秒(JST) , 北緯 or 南緯 , 緯度(度) , 緯度(分) , 東経 or 西経 , 経度(度) , 経度(分) , GoogleマップURLリンク


, 2016/11/10 , 05:39:35 , 14:39:35 , N , 35 , 39.9652 , E , 139 , 47.5501 , http://maps.google.com/maps?q=35.666086,139.792501

実際に、PC側でTeraTermを起動し、イーサネットシールドのIPアドレスに接続すると図6の画面が表示される。

図6 TeraTerm表示画面

ご親切なことに、TeraTermはURLの文字列を自動で認識し、リンク化してくれる。よって、PCがインターネッツに接続されている状態であれば、TeraTerm上でURLをダブルクリックするだけでブラウザが起動し現在位置のGoogleマップが表示される。


以上により、Arduinoを用いてGPSモジュールから位置情報・時刻情報を読み取り、イーサネットでPCに送信する装置が完成した。


参考文献

1. 高感度小型GPSモジュール説明書

2. NMEA 0183 フォーマット 富山高等専門学校 航海科学研究室
http://www2.nc-toyama.ac.jp/~mkawai/lecture/radionav/nmea0183.html

3. Arduinoスケッチでカンマ区切りデータを複数に切り分ける
http://garchiving.com/comma-separated-by-arduino/

4. 緯度経度表記の変換
http://www.motohasi.net/GPS/PosConv.php

5. 緯度・経度の表現を60進法から10進法に変換する
http://lifelog.main.jp/wordpress/?p=146

6. 経度・緯度を60進法から10進法へ変換
http://blog.fkoji.com/2005/09210026.html

7. Googlemapをシンプルかつマーカーがちゃんと表示されるURLで指定するやり方
http://qiita.com/seito2014/items/0565b923a420d222d54e

8. Arduinoを使ってWebサーバーをつくろう! | Device Plus - デバプラ
http://deviceplus.jp/hobby/entry_006/




今回作成したGPS位置情報をイーサネットでPCに送るArduinoスケッチは、以下のリンクからダウンロードいただけます。

Arduinoスケッチ(GPS_Ethernet05.ino)(ZIPファイル)


2016年12月28日