GPSモジュールから位置や時間等の情報をArduinoコマンドで取得して、シリアルモニタやM5Stackの液晶表示器に表示する方法を詳しく紹介します。
ライブラリを使用すれば簡単に位置情報は取得できますが、GPSモジュールから送られてくるのは単純な「文字列情報」のため、受信データ情報(センテンス)を理解すればライブラリを使用せずに、必要な情報だけを抽出して利用することもできるため、この方法についても後半で詳しく紹介します。
動作確認は「Arduino IDE」を使用して「コピペ」で書き込むだけです。
「Arduino IDE」の使用方法は、以下のリンクで詳しく紹介しています。
1.GPSとは
2.準備するもの
・GPSモジュール
・M5Stack BASIC
3.GPS受信データ確認
・データ確認用サンプルプログラム
・受信データ(シリアルモニタ&液晶表示)
4.ライブラリによるGPSデータ受信
・ライブラリ「TinyGPSPlus」について
・サンプルプログラム
・動作紹介
5.GPS受信データ詳細
・受信データ規格(NMEA 0183)について
・センテンス(受信データ文)詳細
6.ライブラリ無しで位置情報取得
・動作紹介
・サンプルプログラム
・プログラムの詳細
7.まとめ
1.GPSとは
GPSとは「Global Positioning System」の略です。日本語では「全地球測位システム」で、地球上の位置情報を測定するための仕組みです。
位置情報を測定するためには、地上から約2万km上空の衛星軌道を周回しているGPS衛星からの情報を利用します。
GPS衛星は地球の周りに30個以上あり、少なくとも4つの衛星からの電波を受信することで位置を測定します。(単独測位の場合)
各衛星からの距離を電波の到達時間から求め、3つの衛星までの距離から現在位置が求められます。
時間的なズレを4つ目の衛星で補正して、正確な位置が特定されます。
GPSは、航空機・船舶の航法、自動車のナビゲーション、モバイルアプリケーションの位置追跡など、さまざまな分野で広範に使用されています。また、災害時や緊急事態での救助活動にも重要な役割を果たしています。
2.準備するもの
今回使用した「GPSモジュール」は「M5Stack」社製の「UNIT-GPS」です。
「GPSモジュール」で受信したデータは同じく「M5Stack」社製の「M5Stack Basic」を使用して処理し、液晶表示器やシリアルモニタに表示させて確認を行います
・GPSモジュール
「UNIT-GPS」の外観は下写真のようになります。
電源電圧は 5Vで受信した位置情報は「シリアル通信(UART)」で取得することができます。
・M5Stack BASIC
「M5Stack Basic」の外観は下写真のようになります。
端子配列は下画像のようになり、今回は「GPSモジュール」との接続に「電源5V、0V、RX2、TX2」を使用します。
「M5Stack Basic」と「GPSモジュール(UNIT-GPS)」との接続は下写真のように行います。
購入は「Yahoo!ショッピング」が一番安いです。
・GROVEコネクタ配線
今回使用した「GPSモジュール」に付属の「GROVEコネクタ」配線には両端にコネクタがついているため「M5Stack Basic」にそのまま接続できません。
以下の4ピンケーブルを使用するとM5Stack Basic」側面の端子にそのまま接続できるため便利です。
3.GPS受信データ確認
まずは位置情報を受信した「GPSモジュール」からどんなデータが送信されてくるか確認してみましょう。
以下のサンプルプログラムを書き込んで実行することで「M5Stack Basic」の液晶表示器と「Arduino IDE」のシリアルモニタで受信したデータを確認することができます。
シリアルモニタの使い方は、以下のリンクで詳しく紹介しています。
・データ確認用サンプルプログラム
サンプルプログラムは以下になります。コピペで貼り付けて書き込んで実行してください。
※下コード(黒枠)内の右上角にある小さなアイコンのクリックでもコピーできます。
#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モジュール」が受信した位置情報のデータが表示され続けます。
同様にシリアルモニタにも下画像のように、位置情報のデータが表示され続けます。
4.ライブラリによるGPSデータ受信
「GPSモジュール」で受信したデータは、専用のライブラリ「TinyGPSPlus」を使用すると位置情報等を簡単に表示することができます。
・ライブラリ「TinyGPSPlus」について
「TinyGPSPLUS」は「Arduino IDE」で検索すると下画像の「Mikal Hart」さん作のものです。
・サンプルプログラム
サンプルプログラムは以下になります。コピペで貼り付けて書き込んで実行してください。
※下コード(黒枠)内の右上角にある小さなアイコンのクリックでもコピーできます。
#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"));
}
・動作紹介
「サンプルプログラム」を書き込んで実行したものは下画像のようになります。
「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)」が定義する、船舶やその他の海上および陸上の電子機器間でデータを共有するためのシリアル通信プロトコルです。
このプロトコルは、位置情報、速度、方向、気象情報など、さまざまなデータを交換するために使用されます。
・センテンス(受信データ文)詳細
GPSの「センテンス」とは「GPSモジュール」が衛星からデータを受信した時に出力される、位置情報や衛星の信号強度など、GPSデータの一部を指します。
これらの「センテンス」は特定の形式で表現され「NMEA 0183」フォーマットで提供されます。
各センテンスは「$」で始まり、カンマで区切られた文字列で構成されています。
例えば以下の「**GGA」センテンスは位置情報や高度などの基本的な情報を提供します。
他にも「**RMC」センテンスは、位置、速度、方向などの情報を提供し、「**ZDA」センテンスは、時刻と日付、「**GSV」センテンスは、衛星の詳細情報を提供します。
$** | 衛星システム名 | 地域/国 |
---|---|---|
GA | Galileo | Europe |
GB/BD | BDS (Beidou:北斗) | 中国 |
GI | NavIC (IRNSS) | インド |
GL | GLONASS | ロシア |
GN | GNSS(Global Navigation Satellite Systen) | 任意の組み合わせ |
GP | GPS(Global Positioning System) | アメリカ |
GQ | QZSS(準天頂衛星:みちびき) | 日本 |
以下から、今回使用した「GPSモジュール(UNIT-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 | $GNGGA | NMEAセンテンスの識別子 | **GGAは、GPS受信機が取得した位置修正の品質や精度情報を提供し、緯度、経度、高度、UTC時刻、精度や品質指標を含みます。 先頭のGxはTalker IDといい、以下のようになります。 GP=GPS/SBAS、GL=GLONASS、GA=Galileo、GB=BeiDou、GQ=QZSS、GN=任意の衛星の組み合わせ |
1 | 233341.000 | UTC時間 | UTC時間(時:分:秒)を示します。 233341.000 = 23時33分41秒 |
2 | 2958.75474 | 緯度 | 最初の2文字は度を表し、次の文字は分を表す。 2958.75474 = 29度58.75474分 |
3 | N | 緯度の方向 | N = 北緯、S = 南緯 |
4 | 12954.65000 | 経度 | 最初の3文字は度を表し、次の文字は分を表す。12954.65000 = 129度54.65000分 |
5 | E | 経度の方向 | E = 東経、W = 西経 |
6 | 1 | フィックスタイプ | 受信機が計測した衛星信号を基に、現在の位置の精度と信頼性を示す指標。 0 = 受信できないか無効。 1 = 単独測位(単独の衛星からの信号を使用して位置を特定)。 6 = 推定モード (推測航法) は NMEA2.3以降でのみ有効。 (2 : DGPS 測位)※非対応 (3 : GPS-PPS)※非対応 (4 : Real Time Kinematic. 固定整数を使用したRTKモード)※非対応 (5 : Float RTK. RTK モードで使用される衛星システム、浮動小数点整数)※非対応 (7 :マニュアル入力モード)※非対応 (8 :シミュレーションモード)※非対応 |
7 | 19 | 測位に使用する衛星の数 | 00 〜 24で示す。19 = 19個 |
8 | 0.7 | HDOP(Horizontal Dilution of Precision) | 水平精度係数で、値が小さいほど精度が高い。(1未満なら精度は高い) |
9 | 68.0 | 標高 | 標高(海抜)を示す。68.0 = 68.0メートル |
10 | M | 標高の単位 | Mで固定、メートルのみ。 |
11 | 0.0 | ジオイドの高さ | 0.0: ジオイドの高さ(地球の平均海水面を基準にした等重力面)を示す。 |
12 | M | ジオイドの高さの単位 | 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 | $GNGLL | NMEAセンテンスの識別子 | **GLLは、現在位置の地理的な情報を提供し、緯度、経度、UTC時刻、測位モードなどが含まれます。 |
1 | 2958.75474 | 緯度 | 最初の2文字は度を表し、次の文字は分を表す。 2958.75474 = 29度58.75474分 |
2 | N | 緯度の方向 | N = 北緯、S = 南緯 |
3 | 12954.65000 | 経度 | 最初の3文字は度を表し、次の文字は分を表す。 12954.65000 = 129度54.65000分 |
4 | E | 経度の方向 | E = 東経、W = 西経 |
5 | 233341.000 | UTC時間 | UTC時間(時:分:秒)を示します。 233341.000 = 23時33分41秒 |
6 | A | データの有効性を表す | A = データ有効、V = データ無効 |
7 | A | 測位モード | 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 | $GNRMC | NMEAセンテンスの識別子 | **RMCは位置情報や移動速度、進行方向、時刻、日付等の情報を提供します。 |
1 | 233344.000 | UTC時間 | UTC時間(時:分:秒)を示します。 233341.000 = 23時33分41秒 |
2 | A | 位置有効フラグ | A = データ有効 / V = 受信機警告、データ無効 |
3 | 2958.75474 | 緯度 | 最初の2文字は度を表し、次の文字は分を表す。 2958.75474 = 29度58.75474分 |
4 | N | 緯度の方向 | N = 北緯、S = 南緯 |
5 | 12954.65000 | 経度 | 最初の3文字は度を表し、次の文字は分を表す。 12954.65000 = 129度54.65000分 |
6 | E | 経度の方向 | E = 東経、W = 西経 |
7 | 0.00 | 移動速度 | ノット単位の移動速度 |
8 | 166.78 | 進行方向 | 真北からの角度。166.78 = 166.78度 |
9 | 140723 | 日付(YYMMDD形式) | 140723 = 23年07月14日 |
10 | (空白) | 磁気偏角 | データシートでは空白固定のため非対応? |
11 | (空白) | 磁気偏角方向 | データシートでは空白固定のため非対応? |
12 | A | 測位モード | 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 | $GNVTG | NMEAセンテンスの識別子 | **GSVは進行方向や速度に関する情報を提供します。 |
1 | 166.78 | 進行方向 | 真北からの角度。166.78 = 166.78度 |
2 | T | 進行方向の単位 | T = 真北(True North)固定 |
3 | (空白) | 磁北への向き | 磁北からの角度。(空白のためデータ無し) |
4 | M | 進行方向の単位 | M = 磁北(Magnetic North)固定 |
5 | 0.00 | 移動速度 | ノット単位の移動速度 |
6 | N | 地上速度の単位 | N = ノット |
7 | 0.00 | 移動速度 | キロメートル/時 単位の移動速度 |
8 | K | 地上速度の単位 | K = キロメートル/時(km/h) |
9 | A | 測位モード | A = 単独即位 E = 推定(Estimated)モード(推測航法) N = 無効 D = ディファレンシャルモード(補助基地局の差分データにより補正) (M = マニュアルインプット)※非対応 (S = シミュレーター)※非対応 |
10 | *2D | チェックサム(エラーチェック用) ※受信内容によって変わります | $ と * の間のすべての文字の XORを16進数で表しデータの整合性を確認。 ※データではないので*の前に「,」はありません。 |
**ZDA(時刻、日付)
$GNZDA,233344.000,14,07,2023,00,00*48
No | センテンス例 | 内容 | 詳細 |
---|---|---|---|
0 | $GNZDA | NMEAセンテンスの識別子 | **GSVは時刻と日付の情報を提供します。 |
1 | 233344.000 | UTC時間 | UTC時間(時:分:秒)を示します。233344.000 = 23時33分44秒 |
2 | 14 | 日 | 固定2桁(01 ~ 31) |
3 | 7 | 月 | 固定2桁(01 ~ 12) |
4 | 2023 | 年 | 固定4桁 |
5 | 00 | タイムゾーンの時間オフセット | サポートされていない(00固定) |
6 | 00 | タイムゾーンの分オフセット | サポートされていない(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 | $GPGSA | NMEAセンテンスの識別子 | **GSAはGNSS(Global Navigation Satellite System)の受信機の動作モードや精度、衛星接続情報を提供します。 |
1 | A | 動作モード | A = オートマチックモード M = マニュアルモード |
2 | 3 | FS番号 位置決め状態フラグ | 1 = 無効 2 = 2D位置決め 3 = 3D位置決め |
3〜14 | 02,03,08,14, 17,21,194,195, 199,,,, | 測位に使用される衛星番号 | 合計12個の利用可能な衛星が表示されます。 数値が12を超える場合は、最初の12のみが出力され、12未満の場合は空白になります。 |
15 | 1.3 | PDOP(Position Dilution of Precision) | 位置の測定の正確さで、低いほど正確(1で最小、10以上誤差大) |
16 | 0.7 | HDOP(Horizontal Dilution of Precision) | 水平方向の測定の正確さで、低いほど正確(1未満なら精度は高い) |
17 | 1.1 | VDOP(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 | $GPGSV | NMEAセンテンスの識別子 | **GSVは最大4つの可視衛星信号を出力します。 4つ以上の衛星を認識できる場合、新たにxxGSVメッセージを生成して表示します。 |
1 | 4 | メッセージ数 | 合計で4つのメッセージがあることを示す。 |
2 | 1 | メッセージの番号 | 4つのメッセージの1つ目であることを示す。 |
3 | 13 | 可視衛星の総数 | 歌詞衛星数が13の場合、13/4=3余り1となり4つのメッセージが表示される。 |
4〜7 | 01,,,28, | 1つ目の衛星情報 | 先頭から「衛星番号, 仰角(度), 方位角(度),信号強度(dBHz)」 ※空白はデータ無し |
8〜11 | 02,39,139,22, | 2つ目の衛星情報 | 〃 |
12〜15 | 03,20,251,28, | 3つ目の衛星情報 | 〃 |
16〜19 | 07,29,109,22 | 4つ目の衛星情報 | 〃 |
20 | *4C | チェックサム(エラーチェック用) ※受信内容によって変わります | $ と * の間のすべての文字の XORを16進数で表しデータの整合性を確認。 ※データではないので*の前に「,」はありません。 |
**TXT(情報や警告、エラー等のテキストメッセージ)
$GPTXT,01,01,01,ANTENNA OPEN*25
No | センテンス例 | 内容 | 詳細 |
---|---|---|---|
0 | $GPTXT | NMEAセンテンスの識別子 | **TXT追加のテキストメッセージや警告が含まれる場合に使用されます。 一般的に、デバイスからの状態やエラーメッセージが含まれることがあります。 |
1 | 01 | メッセージ総数 | 現在のメッセージ内のステートメントの総数 01 ~ 99 (メッセージが長すぎる場合複数の情報に分割して表示) |
2 | 01 | メッセージ番号 | 現在のメッセージ番号 01 ~ 99 |
3 | 01 | テキスト識別子 | 00 = エラーメッセージ 01 = 警告メッセージ 02 = 通知情報 07 = ユーザー情報 |
4 | ANTENNA OPEN | テキスト情報 | アンテナが開いていることを示す。 |
5 | *25 | チェックサム(エラーチェック用) ※受信内容によって変わります | $ と * の間のすべての文字の XORを16進数で表しデータの整合性を確認。 ※データではないので*の前に「,」はありません。 |
6.ライブラリ無しで位置情報取得
「GPSモジュール」から得られるデータは位置情報や時刻情報等を含む文字列データで「センテンス」と呼ばれます。
「センテンス」の内容がわかれば、位置情報等の必要な情報だけ抜き出して利用することができ、ライブラリを使用しなくても位置情報の確認を行うことができるため、以下からこの方法について詳しく紹介します。
・動作紹介
サンプルプログラムを書き込むと下画像のように各情報が表示されます。
・タイトルの下には日付と時刻を表示
・N:現在位置の北緯
・E:現在位置の東経
・Elevation:標高(ジオイドの補正なし)
・Direction:進行方向に対する北の角度
・Speed:時速
・サンプルプログラム
サンプルプログラムは以下になります。コピペで貼り付けて書き込んで実行してください。
※下コード(黒枠)内の右上角にある小さなアイコンのクリックでもコピーできます。
#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」を要素数として指定して「進行方向(方向マーカーの表示)、移動速度」を抽出し、液晶画面に表示します。
7.まとめ
「GPSモジュール」から位置や時間等の情報を取得して表示する方法を詳しく紹介しました。
「GPSモジュール」が受信した位置情報等のデータはシリアル通信で文字列として確認することができます。
ライブラリを使用すれば簡単に情報を取得できますが、受信データの構造を理解すればライブラリを使用せずに、必要な情報だけを抽出して利用することもできます。
受信データは「センテンス」と呼ばれ、「$」に続き「Talker ID」「センテンス識別子」で始まります。
「センテンス識別子」によって受信した情報の内容を確認できるため、「センテンス」の内容を理解して、利用する情報が一部で良い場合は、ライブラリを使用しない方法も検討してみましょう。
コメント