ArduinoでAdafruitのGPSを使ってみる


このエントリーをはてなブックマークに追加

GPSモジュールはAdafruitのものを持っていたのでそれを使った

Overview | Adafruit Ultimate GPS | Adafruit Learning System

購入はスイッチサイエンスから行える

Adafruit Ultimate 66チャンネル10Hz GPSモジュール Version 3 - スイッチサイエンス

このGPSだとなかなか情報が多いのと、Adafruitが公式でライブラリなどを出してくれているのでやりやすいイメージ


で、ArduinoのTX, RXを使ってGPSモジュールを読み取る方法はいくつか書いてる人はけっこう出てくる、例えば下記

シリアルの基本的な使い方で読み取りはできる

が、そういえばAdafruitのArduinoライブラリを使ってコマンドとか送りたいのでちょくちょく調べていく

今回はライブラリの使い方サンプル例とチェックサムの計算

AdafruitのGPSライブラリ

下記githubからダウンロードできる

adafruit/Adafruit_GPS: An interrupt-based GPS library for no-parsing-required use

ライブラリのインストールの方法は下記などを参考に

GitHubにある ZIP形式ライブラリ のインストール方法 ( Arduino IDE ) | mgo-tec電子工作

zipファイルをダウンロードして、Arduino IDEを立ち上げて

メニューの スケッチ -> ライブラリをインクルード -> .zip形式のライブラリをインストール

でダウンロードしたライブラリを選択するだけ


試しに動かしてみる

しかしどうやってつかったらいいのかなと思ってたら

YouTubeにチュートリアルあげてくれてる方がいた

LESSON 22: Build an Arduino GPS Tracker

とりあえず、下記のコードをコンパイルして書き込んでやると、GPRMCとGPGGAのみのデータが出力されるようになる

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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
#include <Adafruit_GPS.h>
#include <SoftwareSerial.h>
SoftwareSerial mySerial(2, 3); // (RX, TX)
Adafruit_GPS GPS(&mySerial);
String NMEA1; //Variable for first NMEA sentence
String NMEA2; //Variable for second NMEA sentence
char c; // to read characters coming from the GPS
void setup() {
Serial.begin(115200); //turn on serial monitor
while(!Serial) {
;
}
Serial.println("Ready");
GPS.begin(9600); // turn on GPS at 9600 baud
GPS.sendCommand("$PGCMD,33,0*6D"); // turn off antenna update nuisance data
GPS.sendCommand(PMTK_SET_NMEA_UPDATE_10HZ); // set update rate to 10 Hz
GPS.sendCommand(PMTK_SET_NMEA_OUTPUT_RMCGGA); // request RMC and GGA Sentenes only
delay(1000);
}
void loop() {
readGPS();
}
void readGPS() {
while(!GPS.newNMEAreceived()) { // loop until you have a goo NMEA sentence
c=GPS.read();
}
GPS.parse(GPS.lastNMEA()); //parse that last good NMEA sentence
NMEA1=GPS.lastNMEA();
while(!GPS.newNMEAreceived()) { // loop until you have a goo NMEA sentence
c=GPS.read();
}
GPS.parse(GPS.lastNMEA()); //parse that last good NMEA sentence
NMEA2=GPS.lastNMEA();
Serial.println(NMEA1);
Serial.println(NMEA2);
}

で、sendCommand 関数でコマンドを送ってGPSの設定ができることがわかったので

今度は他のコマンドも送ろってみようと思ったが

下記公式のコマンドシート(pdf)を読んでみても、よくわからない部分があったので少しづつ調べていくことにする

PMTK command packet-Complete-A11


まずはチェックサムから

  • コマンドの最後はスターマーク(*)で、その後がチェックサムらしい
  • チェックサムのところの数値はどうやら16進数表記があてはまるらしいがそもそもどうやって算出するのか、公式のコマンドシートをみてみると
  • PMTK command packet-Complete-A11

    • 25ページ目にチェックサムの計算方法載ってた
    • ドルマーク($) と スター(*) の間のチェックサムを計算する仕様
      • 例えば、5ページ目にある test packet のコマンド $PMTK000*32<CR><LF> だと
        • “PMTK000” のチェックサムを計算する
        • チェックサムは “32”(16進数表記なので0x32のこと)
    • Xorで計算するらしい
    • 下記でASCIIから自動でチェックサムを計算してくれる
  • 計算の理論としては、それぞれのASCIIを2進数になおして、どんどんXORしていけばよい

  • PMTK000 を例にとると(0はASCIIコードでの0なので注意、10進数に直すと 48 になる), ASCII表記、10進数表記、2進数表記はそれぞれ
ASCII 10進数表記 2進数表記
P 80 1010000
M 77 1001101
T 84 1010100
K 75 1001011
0 48 0110000
0 48 0110000
0 48 0110000
  • となる
  • 上記の 0 は桁数をあわせて見やすくするために0を頭につけている(そのまま記載すると桁がずれるので)
  • これを順番にXORしていけばよい(たぶん、結果はちゃんと32になった)
  • 下記のようになる


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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
PとMのXORは
1010000
1001101
-------
0011101
さらに、この結果(0011101) と T のXORをとっていくと
0011101
1010100
-------
1001001
さらに、この結果(1001001) と K のXORをとる
1001001
1001011
-------
0000010
さらに、この結果と0のXORをとる
0000010
0110000
-------
0110010
さらに同様に0とのXORをとる
0110010
0110000
-------
0000010
さらに同様に0とのXORをとる
0000010
0110000
-------
0110010
最終的に 0110010 が出てきた
  • 2進数の 0110010 は 16進数に直すと 32 となる
  • よって、32がチェックサムとなる
  • みたいな感じでやれば、コマンドシートから送りたいコマンドを作ることができる


RubyでASCII文字列のチェックサムの計算

さきほどの、自動で出してくれるサイトで出してもいいが練習のためにRubyで計算させてみた

  • Rubyだと、 ^ でXORが計算できる
  • 結果は 10進数 で返ってくる
  • 例えば
1
2
3
4
5
6
7
8
9
10
11
> "P".ord ^ "M".ord
=> 29
> ("P".ord ^ "M".ord).to_s(2)
=> "11101"
> "P".ord ^ "M".ord ^ "T".ord
=> 73


  • rubyでチェックサム生成する(16進数)コードは単純には下記
  • 試しに変換する文字列は “PMTK000” としてる
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# encoding: utf-8
string = "PMTK000"
s = string.split("")
i = 2
acc = s[0].ord ^ s[1].ord
while i < s.length
acc = acc ^ s[i].ord
i += 1
end
puts acc.to_s(16)

もっといい方法がありそうだけどこれぐらいの長さならこれでよい

ひとまずこれでコマンドが送れるようになったので色々と試してみる


このエントリーをはてなブックマークに追加