GPSモジュールの使い方、簡単位置情報取得Arduinoコマンド(M5Stack使用)

GPSの使い方、Arduinoコマンドアイキャッチ

GPSモジュールから位置や時間等の情報をArduinoコマンドで取得して、シリアルモニタやM5Stackの液晶表示器に表示する方法を詳しく紹介します。

ライブラリを使用すれば簡単に位置情報は取得できますが、GPSモジュールから送られてくるのは単純な「文字列情報」のため、受信データ情報(センテンス)を理解すればライブラリを使用せずに、必要な情報だけを抽出して利用することもできるため、この方法についても後半で詳しく紹介します。


動作確認は「Arduino IDE」を使用して「コピペ」で書き込むだけです。
「Arduino IDE」の使用方法は、以下のリンクで詳しく紹介しています。

M5StackシリーズのためのArduino IDEのインストール方法と初期設定、使い方紹介
ArduinoIDEバージョン2のインストール方法から初期設定、スケッチ例の書き込み、コピペでの使い方まで詳しく紹介します。インストールはArduinoでも同じです。
スポンサーリンク

1.GPSとは

GPSとは「Global Positioning System」の略です。日本語では「全地球測位システム」で、地球上の位置情報を測定するための仕組みです。

位置情報を測定するためには、地上から約2万km上空の衛星軌道を周回しているGPS衛星からの情報を利用します。
GPS衛星は地球の周りに30個以上あり、少なくとも4つの衛星からの電波を受信することで位置を測定します。(単独測位の場合)

各衛星からの距離を電波の到達時間から求め、3つの衛星までの距離から現在位置が求められます。
時間的なズレを4つ目の衛星で補正して、正確な位置が特定されます。

これは「単独測位」の方法で、他にも2台以上の受信機を使う「相対測位」や地上の基準局を利用した「DPGS測位」「RTK-GPS測位」「ネットワークRTK-GPS測位」があります。

GPSは、航空機・船舶の航法、自動車のナビゲーション、モバイルアプリケーションの位置追跡など、さまざまな分野で広範に使用されています。また、災害時や緊急事態での救助活動にも重要な役割を果たしています。

スポンサーリンク

2.準備するもの

今回使用した「GPSモジュール」は「M5Stack」社製の「UNIT-GPS」です。
「GPSモジュール」で受信したデータは同じく「M5Stack」社製の「M5Stack Basic」を使用して処理し、液晶表示器やシリアルモニタに表示させて確認を行います

・GPSモジュール

「UNIT-GPS」の外観は下写真のようになります。

GPSの使い方、Arduinoコマンド
GPSの使い方、Arduinoコマンド
GPSの使い方、Arduinoコマンド
GPSの使い方、Arduinoコマンド

電源電圧は 5Vで受信した位置情報は「シリアル通信(UART)」で取得することができます。

・M5Stack BASIC

「M5Stack Basic」の外観は下写真のようになります。

M5Stack basicの外観

端子配列は下画像のようになり、今回は「GPSモジュール」との接続に「電源5V、0V、RX2、TX2」を使用します。

M5Stack basicの使い方

「M5Stack Basic」と「GPSモジュール(UNIT-GPS)」との接続は下写真のように行います。

GPSの使い方、結線方法
「UNIT-GPS」の「TXD」を「M5Stack Basic」の「RX2」へ、「RXD」を「TX2」へ接続します。

購入は「Yahoo!ショッピング」が一番安いです。

・GROVEコネクタ配線

今回使用した「GPSモジュール」に付属の「GROVEコネクタ」配線には両端にコネクタがついているため「M5Stack Basic」にそのまま接続できません。
以下の4ピンケーブルを使用するとM5Stack Basic」側面の端子にそのまま接続できるため便利です。

「GPSモジュール」に付属の配線とは「黄色」と「白色」の配線が逆になります。
スポンサーリンク

3.GPS受信データ確認

まずは位置情報を受信した「GPSモジュール」からどんなデータが送信されてくるか確認してみましょう。

以下のサンプルプログラムを書き込んで実行することで「M5Stack Basic」の液晶表示器と「Arduino IDE」のシリアルモニタで受信したデータを確認することができます。

シリアルモニタの使い方は、以下のリンクで詳しく紹介しています。

シリアル(UART)出力で内部データの表示(Arduinoプログラミング)
シリアル出力モニタとはプログラム内の値やデータをパソコンのモニタ上に表示させることができる機能で、ほとんどの開発環境にこの機能があります。表示だけではなく出力したデータを記録して保存したり、機器間でデータをやり取りする時にも使用されます。

・データ確認用サンプルプログラム

サンプルプログラムは以下になります。コピペで貼り付けて書き込んで実行してください。
※下コード(黒枠)内の右上角にある小さなアイコンのクリックでもコピーできます。

#include <M5Stack.h>

int y;  // カーソル「y座標」格納用

void setup() {  // 初期設定
  M5.begin();                   // 本体初期化
  M5.Power.begin();             // 電源管理を初期化
  Serial.begin(9600);           // USBシリアル通信を初期化(USBポートはRX=G3, TX=G1と共有)
  Serial2.begin(9600);          // シリアル通信2(GPSユニット用)を初期化(RX=16, TX=17)
  // 液晶タイトル表示
  M5.Lcd.setTextFont(4);        // フォントを設定
  M5.Lcd.drawCentreString("GPS Data Monitor", 160, 100, 4);  // テキスト中央表示(文字列, x座標, y座標, フォント番号)
  delay(2000);                  // タイトル表示時間
  M5.Lcd.fillScreen(BLACK);     // 背景色
  M5.Lcd.setCursor(0, 0, 2);    // LCDのカーソル位置をリセット
}

void loop() { // メイン処理
  M5.update();                  // 本体ボタン状態更新
  // GPSからの受信データ表示
  if (Serial2.available()) {    // GPSシリアルでデータが利用可能かチェック
    int ch = Serial2.read();    // GPSシリアルから文字を読み取る
    Serial.write(ch);           // 読み取った文字をUSBシリアルに書き込む
    M5.Lcd.print((char)ch);     // 液晶表示器にデータ表示
  }
  // 液晶表示更新
  y = M5.Lcd.getCursorY();        // 現在のカーソルの「y座標」取得
  if (y >= M5.Lcd.height() - 1) { // カーソルが画面の最下部に達した場合
    M5.Lcd.fillScreen(BLACK);     // 背景色
    M5.Lcd.setCursor(0, 0);       // LCDのカーソル位置をリセット
  }
  // データ出力一時停止
  if (M5.BtnA.wasPressed()) {         // Aボタンを押すと一時停止
    while (!M5.BtnB.wasPressed()) {M5.update();}  // Bボタンが押されるまで待機
  }
}

・受信データ(シリアルモニタ&液晶表示)

「サンプルプログラム」を書き込んで実行すると「M5Stack Basic」の液晶表示器に以下のように「GPSモジュール」が受信した位置情報のデータが表示され続けます。

位置情報については一部モザイク処理しています。
GPSの使い方、シリアルモニタで受信データ確認
「M5Stack Basic」本体の「ボタンA(左)」を押すとデータの更新が一時停止します。
「ボタンB(中央)」を押すとデータの更新が再開します。

同様にシリアルモニタにも下画像のように、位置情報のデータが表示され続けます。

GPSの使い方、Arduinoコマンド動作確認
受信したデータは文字列データで情報量も多いため一見よくわかりません。
このデータの詳細については後半で詳しく紹介しますが、ライブラリを使用すると簡単に位置情報等を取得することができるため、次からこの方法について紹介します。

4.ライブラリによるGPSデータ受信

「GPSモジュール」で受信したデータは、専用のライブラリ「TinyGPSPlus」を使用すると位置情報等を簡単に表示することができます。

・ライブラリ「TinyGPSPlus」について

「TinyGPSPLUS」は「Arduino IDE」で検索すると下画像の「Mikal Hart」さん作のものです。

GPSの使い方、ライブラリTinyGPSPlus

・サンプルプログラム

サンプルプログラムは以下になります。コピペで貼り付けて書き込んで実行してください。
※下コード(黒枠)内の右上角にある小さなアイコンのクリックでもコピーできます。

#include <M5Stack.h>
#include <TinyGPSPlus.h>  // GPS制御ライブラリ

static const uint32_t GPSBaud = 9600; // GPSモジュールのボーレートを9600に設定
TinyGPSPlus gps;  // TinyGPS++クラスのインスタンスを作成して、GPSデータを処理

// シリアルポート2に接続されたGPSモジュールとの通信のために、HardwareSerialオブジェクトであるssを作成
HardwareSerial ss(2);

// このカスタムバージョンのdelay()は、GPSオブジェクトが正しく機能することを保証します。
// 指定した時間(ミリ秒)だけ待機する
static void smartDelay(unsigned long ms) {
    unsigned long start = millis(); // 現在の時間をミリ秒単位で取得
    do {
      while (ss.available()) gps.encode(ss.read()); // シリアルポートからデータが読み込まれ、GPSオブジェクトにエンコードされる間、繰り返し処理を実行
    }
    while (millis() - start < ms);  // 経過時間が指定したミリ秒数未満である場合は、繰り返し処理を続ける(指定した時間が経過するまで待機)
}
// 初期設定 ----------------------------------------------------------------------
void setup() {
  M5.begin();           // 本体初期化
  M5.Power.begin();     // 電源管理を初期化
  ss.begin(GPSBaud);    // GPS受信データ通信シリアル初期化
}
// メイン ------------------------------------------------------------------------
void loop() {
  // 液晶表示初期化
  M5.Lcd.setCursor(0, 0);           // 表示座標指定
  M5.Lcd.setTextFont(4);            // 文字サイズ指定
  M5.Lcd.setTextColor(CYAN, BLACK); // 文字色, 背景色
  M5.Lcd.drawCentreString("< TinyGPSPlus TEST >\n", 160, 0, 4); // テキスト中央表示(文字列, x座標, y座標, フォント番号)

  // GPSデータ表示
  M5.Lcd.setCursor(0, 26);          // 表示座標指定
  M5.Lcd.setTextColor(WHITE, BLACK);                            // 文字色, 背景色
  M5.Lcd.printf("Satelites : %02d\n", gps.satellites.value());  // 衛星の数を表示する
  M5.Lcd.printf("HDOP      : %d \n", gps.hdop.value());         // HDOP値(水平方向の測定の正確さ)を表示する
  M5.Lcd.setTextColor(RED, BLACK);                              // 文字色, 背景色
  M5.Lcd.printf("N : %f  \n", gps.location.lat());              // 緯度を表示する
  M5.Lcd.setTextColor(GREEN, BLACK);                            // 文字色, 背景色
  M5.Lcd.printf("E : %f  \n", gps.location.lng());              // 経度を表示する
  M5.Lcd.setTextColor(WHITE, BLACK);                            // 文字色, 背景色
  M5.Lcd.printf("Location : %.3f  \n", gps.location.age());     // GPS位置情報の更新からの経過時間を表示する
  M5.Lcd.printf("Altitude   : %.1fm  \n", gps.altitude.meters()); // 高度をメートル単位で表示する
  M5.Lcd.printf("Course    : %.1f'  \n", gps.course.deg());       // 方位角を度数法で表示する
  M5.Lcd.printf("Speed     : %.1fkm  \n", gps.speed.kmph());      // 速度をキロメートル/時単位で表示する

  smartDelay(1000); // 遅延時間(カスタムディレイ)

  // 5秒以上経過してもGPSデータが受信されない場合はワイヤリングを確認するように表示する
  if (millis() > 5000 && gps.charsProcessed() < 10)
      M5.Lcd.println(F("No GPS data received: check wiring"));
}

・動作紹介

「サンプルプログラム」を書き込んで実行したものは下画像のようになります。

GPSの使い方、Arduinoコマンド動作確認

「M5Stack Basic」の液晶表示器には以下の情報が表示されます。

・Satelites:可視衛生の数
・HDOP:水平方向の測定の正確さ
・N:現在位置の北緯
・E:現在位置の東経
・Location:GPS位置情報の更新からの経過時間
・Altitude:標高
・Course:真北を基準とした方位角
・Speed:移動速度

ライブラリを使用すると簡単に表示できてしまいますが、実際にどんなデータを受信して、どう表示しているのかを知ることができません。
実際に受信したデータを確認してみると、意外と単純で、受信した文字データから、必要な情報だけ抜き出して利用するだけ
このデータについても、以下から詳しく紹介しているので、ライブラリを使用せずに位置情報等のデータを取得する方法にも挑戦してみましょう。

5.GPS受信データ詳細

「GPSモジュール」で受信したデータは上の「受信データ(シリアルモニタ&液晶表示)」で確認しました。
一見暗号のようで何のことやらさっぱりわかりませんが、受信データの内容は「NMEA 0183」という規格で決められています。

受信データは使用する「GPSモジュール」によって若干異なりますが、基本的な内容は同じです。

・受信データ規格(NMEA 0183)について

「NMEA 0183」は「National Marine Electronics Association(NMEA)」が定義する、船舶やその他の海上および陸上の電子機器間でデータを共有するためのシリアル通信プロトコルです。

このプロトコルは、位置情報、速度、方向、気象情報など、さまざまなデータを交換するために使用されます。

「NMEA 0183」は非常に一般的であり、GPS受信機、レーダー、ソナー、自動航法装置など、さまざまなナビゲーションおよび電子機器で広くサポートされています。

・センテンス(受信データ文)詳細

GPSの「センテンス」とは「GPSモジュール」が衛星からデータを受信した時に出力される、位置情報や衛星の信号強度など、GPSデータの一部を指します。
これらの「センテンス」は特定の形式で表現され「NMEA 0183」フォーマットで提供されます。

「センテンス」受信し解析することで、正確な位置情報や移動速度などの情報を取得できます。

各センテンスは「$」で始まり、カンマで区切られた文字列で構成されています。
例えば以下の「**GGA」センテンスは位置情報や高度などの基本的な情報を提供します。

GPSの使い方、受信データ(センテンス)構成


他にも「**RMC」センテンスは、位置、速度、方向などの情報を提供し、「**ZDA」センテンスは、時刻と日付、「**GSV」センテンスは、衛星の詳細情報を提供します。

「$」の後の「**」は「Talker ID」といい、データを受信した衛星システム名を表し、以下表のように、どの国の衛星を使用しているかがわかります。
$**衛星システム名地域/国
GAGalileoEurope
GB/BDBDS (Beidou:北斗)中国
GINavIC (IRNSS)インド
GLGLONASSロシア
GNGNSS(Global Navigation Satellite Systen)任意の組み合わせ
GPGPS(Global Positioning System)アメリカ
GQQZSS(準天頂衛星:みちびき)日本

以下から、今回使用した「GPSモジュール(UNIT-GPS)」で受信される主なセンテンスについて詳しく紹介していきます。

今回使用した「GPSモジュール(UNIT-GPS)」で受信した「センテンス」を例に紹介します。
「Talker ID」の部分は使用する「GPSモジュール」によって異なります。

**GGA(時刻、位置、その他GPSの情報)

センテンス例:
$GNGGA,233341.000,2958.75474,N,12954.65000,E,1,19,0.7,68.0,M,0.0,M,,*4A
Noセンテンス例内容詳細
0$GNGGANMEAセンテンスの識別子**GGAは、GPS受信機が取得した位置修正の品質や精度情報を提供し、緯度、経度、高度、UTC時刻、精度や品質指標を含みます。
先頭のGxはTalker IDといい、以下のようになります。 GP=GPS/SBAS、GL=GLONASS、GA=Galileo、GB=BeiDou、GQ=QZSS、GN=任意の衛星の組み合わせ
1233341.000UTC時間UTC時間(時:分:秒)を示します。
233341.000 = 23時33分41秒
22958.75474緯度最初の2文字は度を表し、次の文字は分を表す。
2958.75474 = 29度58.75474分
3N緯度の方向N = 北緯、S = 南緯
412954.65000経度最初の3文字は度を表し、次の文字は分を表す。12954.65000 = 129度54.65000分
5E経度の方向E = 東経、W = 西経
61フィックスタイプ受信機が計測した衛星信号を基に、現在の位置の精度と信頼性を示す指標。
 0 = 受信できないか無効。
 1 = 単独測位(単独の衛星からの信号を使用して位置を特定)。
 6 = 推定モード (推測航法) は NMEA2.3以降でのみ有効。
 (2 : DGPS 測位)※非対応
 (3 : GPS-PPS)※非対応
 (4 : Real Time Kinematic. 固定整数を使用したRTKモード)※非対応
 (5 : Float RTK. RTK モードで使用される衛星システム、浮動小数点整数)※非対応
 (7 :マニュアル入力モード)※非対応
 (8 :シミュレーションモード)※非対応
719測位に使用する衛星の数00 〜 24で示す。19 = 19個
80.7HDOP(Horizontal Dilution of Precision)水平精度係数で、値が小さいほど精度が高い。(1未満なら精度は高い)
968.0標高標高(海抜)を示す。68.0 = 68.0メートル
10M標高の単位Mで固定、メートルのみ。
110.0ジオイドの高さ0.0: ジオイドの高さ(地球の平均海水面を基準にした等重力面)を示す。
12Mジオイドの高さの単位Mで固定、メートルのみ。
13(空白)DGPS(Differential GPS)補正値(秒)精度向上のための補正値(差動基準局からの位置情報を補正)空白は未使用。
14(空白)DiffSta (Differential Station)値差動基準局ID。
15*4Aチェックサム(エラーチェック用) ※受信内容によって変わります$ と * の間のすべての文字の XORを16進数で表しデータの整合性を確認。
※データではないので*の前に「,」はありません。

**GLL(位置)

センテンス例:
$GNGLL,2958.75474,N,12954.65000,E,233341.000,A,A*4E
Noセンテンス例内容詳細
0$GNGLLNMEAセンテンスの識別子**GLLは、現在位置の地理的な情報を提供し、緯度、経度、UTC時刻、測位モードなどが含まれます。
12958.75474緯度最初の2文字は度を表し、次の文字は分を表す。
2958.75474 = 29度58.75474分
2N緯度の方向N = 北緯、S = 南緯
312954.65000経度最初の3文字は度を表し、次の文字は分を表す。
12954.65000 = 129度54.65000分
4E経度の方向E = 東経、W = 西経
5233341.000UTC時間UTC時間(時:分:秒)を示します。
233341.000 = 23時33分41秒
6Aデータの有効性を表すA = データ有効、V = データ無効
7A測位モードA = 自律モード E = 推定モード(推測航法) N = データ無効 D = ディファレンシャルモード(補助基地局の差分データにより補正)
8*4Eチェックサム(エラーチェック用) ※受信内容によって変わります$ と * の間のすべての文字の XORを16進数で表しデータの整合性を確認。
※データではないので*の前に「,」はありません。

**RMC(時刻、位置、日付)

センテンス例:
$GNRMC,233344.000,A,2958.75474,N,12954.65000,E,0.00,166.78,140723,,,A*71
Noセンテンス例内容詳細
0$GNRMCNMEAセンテンスの識別子**RMCは位置情報や移動速度、進行方向、時刻、日付等の情報を提供します。
1233344.000UTC時間UTC時間(時:分:秒)を示します。
233341.000 = 23時33分41秒
2A位置有効フラグA = データ有効 / V = 受信機警告、データ無効
32958.75474緯度最初の2文字は度を表し、次の文字は分を表す。
2958.75474 = 29度58.75474分
4N緯度の方向N = 北緯、S = 南緯
512954.65000経度最初の3文字は度を表し、次の文字は分を表す。
12954.65000 = 129度54.65000分
6E経度の方向E = 東経、W = 西経
70.00移動速度ノット単位の移動速度
8166.78進行方向真北からの角度。166.78 = 166.78度
9140723日付(YYMMDD形式)140723 = 23年07月14日
10(空白)磁気偏角データシートでは空白固定のため非対応?
11(空白)磁気偏角方向データシートでは空白固定のため非対応?
12A測位モードA = 単独即位
E = 推定(Estimated)モード(推測航法)
N = 無効
D = ディファレンシャルモード(補助基地局の差分データにより補正)
(M = マニュアルインプット)※非対応
(S = シミュレーター)※非対応
13*71チェックサム(エラーチェック用) ※受信内容によって変わります$ と * の間のすべての文字の XORを16進数で表しデータの整合性を確認。 ※データではないので*の前に「,」はありません。

**VTG(コースとスピード)

センテンス例:
$GNVTG,166.78,T,,M,0.00,N,0.00,K,A*2D
Noセンテンス例内容詳細
0$GNVTGNMEAセンテンスの識別子**GSVは進行方向や速度に関する情報を提供します。
1166.78進行方向真北からの角度。166.78 = 166.78度
2T進行方向の単位T = 真北(True North)固定
3(空白)磁北への向き磁北からの角度。(空白のためデータ無し)
4M進行方向の単位M = 磁北(Magnetic North)固定
50.00移動速度ノット単位の移動速度
6N地上速度の単位N = ノット
70.00移動速度キロメートル/時 単位の移動速度
8K地上速度の単位K = キロメートル/時(km/h)
9A測位モードA = 単独即位
E = 推定(Estimated)モード(推測航法)
N = 無効
D = ディファレンシャルモード(補助基地局の差分データにより補正)
(M = マニュアルインプット)※非対応
(S = シミュレーター)※非対応
10*2Dチェックサム(エラーチェック用) ※受信内容によって変わります$ と * の間のすべての文字の XORを16進数で表しデータの整合性を確認。 ※データではないので*の前に「,」はありません。

**ZDA(時刻、日付)

センテンス例:
$GNZDA,233344.000,14,07,2023,00,00*48
Noセンテンス例内容詳細
0$GNZDANMEAセンテンスの識別子**GSVは時刻と日付の情報を提供します。
1233344.000UTC時間UTC時間(時:分:秒)を示します。233344.000 = 23時33分44秒
214固定2桁(01 ~ 31)
37固定2桁(01 ~ 12)
42023固定4桁
500タイムゾーンの時間オフセットサポートされていない(00固定)
600タイムゾーンの分オフセットサポートされていない(00固定)
7*48チェックサム(エラーチェック用) ※受信内容によって変わります$ と * の間のすべての文字の XORを16進数で表しデータの整合性を確認。
※データではないので*の前に「,」はありません。

**GSA(受信機と接続衛星の情報)

センテンス例:
$GPGSA,A,3,02,03,08,14,17,21,194,195,199,,,,1.3,0.7,1.1*0E
Noセンテンス例内容詳細
0$GPGSANMEAセンテンスの識別子**GSAはGNSS(Global Navigation Satellite System)の受信機の動作モードや精度、衛星接続情報を提供します。
1A動作モードA = オートマチックモード
M = マニュアルモード
23FS番号 位置決め状態フラグ1 = 無効
2 = 2D位置決め
3 = 3D位置決め
3〜1402,03,08,14, 17,21,194,195, 199,,,,測位に使用される衛星番号合計12個の利用可能な衛星が表示されます。 数値が12を超える場合は、最初の12のみが出力され、12未満の場合は空白になります。
151.3PDOP(Position Dilution of Precision)位置の測定の正確さで、低いほど正確(1で最小、10以上誤差大)
160.7HDOP(Horizontal Dilution of Precision)水平方向の測定の正確さで、低いほど正確(1未満なら精度は高い)
171.1VDOP(Vertical Dilution of Precision)垂直方向の測定の正確さで、低いほど正確(1未満なら精度は高い)
18なしNMEA で定義された GNSS システムID今回確認できず(データシートではNMEA 4.1以降でのみ動作すると記載あり。)
1 = GPS システム
2 =GLONASS システム
4 =BDSシステム
19*0Eチェックサム(エラーチェック用) ※受信内容によって変わります$ と * の間のすべての文字の XORを16進数で表しデータの整合性を確認。
※データではないので*の前に「,」はありません。

**GSV(衛星情報詳細)

センテンス例:
$GPGSV,4,1,13,01,,,28,02,39,139,22,03,20,251,28,07,29,109,22*4C
Noセンテンス例内容詳細
0$GPGSVNMEAセンテンスの識別子**GSVは最大4つの可視衛星信号を出力します。 4つ以上の衛星を認識できる場合、新たにxxGSVメッセージを生成して表示します。
14メッセージ数合計で4つのメッセージがあることを示す。
21メッセージの番号4つのメッセージの1つ目であることを示す。
313可視衛星の総数歌詞衛星数が13の場合、13/4=3余り1となり4つのメッセージが表示される。
4〜701,,,28,1つ目の衛星情報先頭から「衛星番号, 仰角(度), 方位角(度),信号強度(dBHz)」
※空白はデータ無し
8〜1102,39,139,22,2つ目の衛星情報
12〜1503,20,251,28,3つ目の衛星情報
16〜1907,29,109,224つ目の衛星情報
20*4Cチェックサム(エラーチェック用) ※受信内容によって変わります$ と * の間のすべての文字の XORを16進数で表しデータの整合性を確認。
※データではないので*の前に「,」はありません。

**TXT(情報や警告、エラー等のテキストメッセージ)

センテンス例:
$GPTXT,01,01,01,ANTENNA OPEN*25
Noセンテンス例内容詳細
0$GPTXTNMEAセンテンスの識別子**TXT追加のテキストメッセージや警告が含まれる場合に使用されます。
一般的に、デバイスからの状態やエラーメッセージが含まれることがあります。
101メッセージ総数現在のメッセージ内のステートメントの総数 01 ~ 99  (メッセージが長すぎる場合複数の情報に分割して表示)
201メッセージ番号現在のメッセージ番号 01 ~ 99
301テキスト識別子00 = エラーメッセージ
01 = 警告メッセージ
02 = 通知情報
07 = ユーザー情報
4ANTENNA OPENテキスト情報アンテナが開いていることを示す。
5*25チェックサム(エラーチェック用) ※受信内容によって変わります$ と * の間のすべての文字の XORを16進数で表しデータの整合性を確認。
※データではないので*の前に「,」はありません。

6.ライブラリ無しで位置情報取得

「GPSモジュール」から得られるデータは位置情報や時刻情報等を含む文字列データで「センテンス」と呼ばれます。

「センテンス」の内容がわかれば、位置情報等の必要な情報だけ抜き出して利用することができ、ライブラリを使用しなくても位置情報の確認を行うことができるため、以下からこの方法について詳しく紹介します。

・動作紹介

サンプルプログラムを書き込むと下画像のように各情報が表示されます。

GPSの使い方、Arduinoコマンド動作確認

・タイトルの下には日付と時刻を表示
・N:現在位置の北緯
・E:現在位置の東経
・Elevation:標高(ジオイドの補正なし)
・Direction:進行方向に対する北の角度
・Speed:時速

「Dirention」については矢印が北の方向を指すことを想定して作りましたが、角度の受信情報の更新が遅く、受信データ自体が「0度」のまま変化しないことも多かったです・・・
原因不明で動作確認もうまくできないため参考使用例として確認をお願いします。

・サンプルプログラム

サンプルプログラムは以下になります。コピペで貼り付けて書き込んで実行してください。
※下コード(黒枠)内の右上角にある小さなアイコンのクリックでもコピーできます。

#include <M5Stack.h>

#define MAX_GPS_DATA_LEN 200  // GPS受信データ最大文字数
#define MAX_GPS_INFO 20       // GPS受信情報最大数

// 変数宣言
char gps_data_str[MAX_GPS_DATA_LEN] = ""; // GPS受信データ文字列格納配列
char *gps_info[MAX_GPS_INFO];             // GPS情報を格納する文字ポインタ配列

// 座標処理関数 ***********************************************************************
void getCoordinates(int num, int* degree, int* minute, int* second) {
  double coodinate = atof(gps_info[num]);                     // 受信データ文字列を浮動小数点数に変換して格納
  // 取得した座標をポインタで処理
  *degree = static_cast<int>(coodinate / 100);                // 「度」部分は整数部分を100で割った値を取得
  *minute = static_cast<int>((fmod(coodinate, 100.0)));  // 「分」部分は100で割った余りを60倍して取得
  *second = static_cast<int>((fmod(coodinate, 1.0)) * 60);    // 「秒」部分は小数点以下の部分を60倍した値を取得
}
// 方角マーカー表示関数 ****************************************************************
void drawTriangle(int x, int y, int angle) {
  // 三角形の頂点座標を計算します
  int x1 = x + cos((angle + 30) * PI / 180.0) * 20;
  int y1 = y + sin((angle + 30) * PI / 180.0) * 20;
  int x2 = x + cos((angle - 90) * PI / 180.0) * 35;
  int y2 = y + sin((angle - 90) * PI / 180.0) * 35;
  int x3 = x + cos((angle - 210) * PI / 180.0) * 20;
  int y3 = y + sin((angle - 210) * PI / 180.0) * 20;

  // 三角形を描画します
  M5.Lcd.fillTriangle(x1, y1, x2, y2, x3, y3, RED);
}

// 初期設定 --------------------------------------------------------------
void setup() {
  M5.begin();           // 本体初期化
  M5.Power.begin();     // 電源管理を初期化
  Serial.begin(9600);   // USBシリアル通信を初期化(USBポートはRX=3, TX=1と共有)
  Serial2.begin(9600);  // シリアル通信2(GPSユニット用)を初期化(RX=16, TX=17)

  // 初期画面表示
  M5.Lcd.setTextFont(4);                                  // フォント
  M5.Lcd.fillRect(0, 0, 320, 56, DARKGREEN);              // タイトル背景
  M5.Lcd.setTextColor(WHITE, DARKGREEN);                  // 文字色, 背景色
  M5.Lcd.drawCentreString("GPS Simple Test", 160, 4, 4);  // テキスト中央表示(文字列, x座標, y座標, フォント番号)
  M5.Lcd.drawCentreString("M5Stack  - RFID2 UNIT -", 160, 32, 4); // テキスト中央表示(文字列, x座標, y座標, フォント番号)
  M5.Lcd.fillRect(0, 57, 320, 2, WHITE);                  // 横線
  M5.Lcd.fillRect(0, 133, 320, 2, WHITE);                 // 横線
}
// メイン ----------------------------------------------------------------
void loop() {
  // GPS受信データ処理
  if (Serial2.available()) {  // GPSシリアルでデータが受信されたら
    int ch = Serial2.read();  // GPSシリアルから文字を読み取る
    Serial.write(ch);         // 読み取った文字をUSBシリアルに書き込む

    // GPS受信データ文字が「$」なら文字列から各情報を配列に分割して処理
    if ((char)ch == '$') {
      // GPSからの受信データ文字列をカンマで分割
      int i = 0;                                  // gps_info配列のインデックス変数
      char *token = strtok(gps_data_str, ",");    // カンマを区切り文字としてgps_data_strをトークンに分割
      while (token != NULL && i < MAX_GPS_INFO) { // トークンが存在する限りループ
        gps_info[i++] = token;                    // 現在のトークンをgps_info配列のインデックスiに代入し、iを+1
        token = strtok(NULL, ",");                // カンマを区切り文字として次のトークンを取得
      }
      // GPS受信データの識別子を指定して必要な情報を表示
      if (strcmp(gps_info[0], "$GNZDA") == 0) {         // 識別子が「GNZDA」なら
        // 年月日表示
        M5.Lcd.setCursor(0, 70);                                        // 表示座標指定
        M5.Lcd.setTextColor(DARKGREY, BLACK);                           // 文字色, 背景色
        M5.Lcd.printf("%s/%s/%s", gps_info[4], gps_info[3], gps_info[2]); // 年月日を表示
        // UTC時間から時刻表示
        double utc = atof(gps_info[1]);                                 // 受信データ文字列を浮動小数点数に変換して格納
        int hour = (int)(utc / 10000) + 9;                              // 時間を取得
        int minute = ((int)utc % 10000) / 100;                          // 分を取得
        int second = (int)utc % 100;                                    // 秒を取得
        M5.Lcd.setCursor(140, 70);                                      // 表示座標指定
        M5.Lcd.printf("%02d:%02d [%02d]\n", hour, minute, second);      // 時刻表示

      } else if (strcmp(gps_info[0], "$GNGGA") == 0) {  // 識別子が「GNGGA」なら
        // 北緯表示
        int degree, minute, second; // 座標の度分秒格納用変数
        getCoordinates(2, °ree, &minute, &second);                   // 座標処理関数を呼び出し
        M5.Lcd.setCursor(0, 100);                                       // 表示座標指定
        M5.Lcd.setTextColor(RED, BLACK);                                // 文字色, 背景色
        M5.Lcd.printf("N %02d' %02d' %02d\" ", degree, minute, second); // 北緯を表示
        // 東経表示
        getCoordinates(4, °ree, &minute, &second);                   // 座標処理関数を呼び出し
        M5.Lcd.setCursor(160, 100);                                     // 表示座標指定
        M5.Lcd.setTextColor(GREEN, BLACK);                              // 文字色, 背景色
        M5.Lcd.printf("E %03d' %02d' %02d\" ", degree, minute, second); // 東経を表示
        // 標高表示
        M5.Lcd.setCursor(0, 145);                                       // 表示座標指定
        M5.Lcd.setTextColor(WHITE, BLACK);                              // 文字色, 背景色
        M5.Lcd.printf("Elevation : %sm ", gps_info[9]);                 // 標高を表示

      } else if (strcmp(gps_info[0], "$GNVTG") == 0) {  // 識別子が「GNVTG」なら
        // 進行方向(真北からの右回り角度)
        static int angle = 360 - atoi(gps_info[1]);                     // 方向情報を整数に変換して左回り角度へ換算
        M5.Lcd.setCursor(0, 175);                                       // 表示座標指定
        M5.Lcd.printf("Direction : %d'    \n", angle);                  // 進行方向を表示
        // 方角マーカー表示クリア
        M5.Lcd.fillCircle(260, 185, 40, LIGHTGREY);                     // 方向マーカー表示円
        // 三角形を描画します
        drawTriangle(260, 185, angle);                                  // 方角マーカー表示関数呼び出し
        // 移動速度
        M5.Lcd.setCursor(0, 205);                                       // 表示座標指定
        M5.Lcd.printf("Speed :       %.1fkm\n", atof(gps_info[6]));     // 移動速度を表示
      }
      // GPS受信データ文字列リセット
      memset(gps_data_str, 0, sizeof(gps_data_str));
    }
    // GPS受信データ文字を文字列として結合
    int len = strlen(gps_data_str); // 文字列の長さを計算してlen変数に格納
    gps_data_str[len] = (char)ch;   // chをgps_data_str配列の末尾に追加
    gps_data_str[len + 1] = '\0';   // 文字列の終端にヌル文字を設定
  } 
}

・プログラムの詳細

「GPSモジュール」からデータを受信するには、まず51〜53行目」で「GPSモジュール」の通信端子を接続した「Serial2」の受信データを確認します。

受信データがあれば、データを1文字づつ取得して「シリアルモニタ」に出力します。

受信したデータが「$」でなければ「111〜114行目」で、受信した「文字(ch)」を「文字列配列(gps_data_str)」の末尾に追加して「文字列」として格納していきます。

受信したデータが「$」ならば「55〜63行目」のように「文字列配列(gps_data_str)」をカンマ区切りで、GPS情報を格納するポインタ配列「gps_info」に格納していきます。


次に「gps_info」の要素「0」に格納されている「センテンス識別子」を確認していきます。

「gps-info[0]」が「$GNZDA」なら「65〜76行目」のように、上で紹介したセンテンス詳細の「**ZDA(時刻、日付)」の表の「No」を要素数として指定して「日付、時刻」を抽出し、液晶画面に表示します。

「gps-info[0]」が「$GNGGA」なら「78〜93行目」のように、センテンス詳細の「**GGA(時刻、位置、その他GPSの情報)」の表の「No」を要素数として指定して「北緯、東経、標高」を抽出し、液晶画面に表示します。

「gps-info[0]」が「$GNVTG」なら「95〜106行目」のように、センテンス詳細の「**VTG(コースとスピード)」の表の「No」を要素数として指定して「進行方向(方向マーカーの表示)、移動速度」を抽出し、液晶画面に表示します。

以上のように「GPSモジュール」から受信した「文字列」データを、カンマ区切りで配列に格納し配列の要素数「0」で「センテンス識別子」を確認して、必要な情報を抽出することで、希望のデータを取得し利用することができます。

7.まとめ

「GPSモジュール」から位置や時間等の情報を取得して表示する方法を詳しく紹介しました。

「GPSモジュール」が受信した位置情報等のデータはシリアル通信で文字列として確認することができます。

ライブラリを使用すれば簡単に情報を取得できますが、受信データの構造を理解すればライブラリを使用せずに、必要な情報だけを抽出して利用することもできます。

受信データは「センテンス」と呼ばれ、「$」に続き「Talker ID」「センテンス識別子」で始まります。
「センテンス識別子」によって受信した情報の内容を確認できるため、「センテンス」の内容を理解して、利用する情報が一部で良い場合は、ライブラリを使用しない方法も検討してみましょう。

コメント

タイトルとURLをコピーしました