🚀

【電子工作】GPSで居場所情報を取得する

2022/10/01に公開

概要

サイクリングで居場所を特定するために、秋月電子のGPS受信機キットを使いました。

https://akizukidenshi.com/catalog/g/gK-09991/

初期起動

通常の使用では屋内にいても現在地情報は拾えますが、初期起動(コールドスタート)では屋外で人工衛星と接続するには屋外に出て接続する必要があります。
秋月の「よくある質問」にある通り、屋内でやると窓際でも接続できなかったので、必ず屋外に出ましょう。

【質問】 受信できないのですが不良品でしょうか?
[2019/07/03 17:57:16]
【回答】 初回起動(コールドスタート)時は接続までに時間がかかります。
標準値では1分以内ですが地形や天気、時間に左右されますので、 10分程度は時間がかかるものとご理解ください。
また同じ位置にいても上手く行かないことがありますので、 開けた場所を歩き回ると比較的早くスタートします。
少なくとも屋内では失敗します。

接続は5VとGNDだけで良いです。
LEDが弱く光っている状態から、1秒ごとに点灯する状態になったら初期起動は完了です
(私の場合は晴れた日に家の庭でやりまして、1分ほどで初期起動ができました。)

ESP32との接続

RXDは14番ピン
TXDは12番ピン
GNDはGND

5Vは手元のESP32 Devモジュールでは給電がうまく行かなかったので外部からとりました。

シリアル通信確認

私はplatformioを使っています。
シリアル通信をするためのライブラリ SoftwareSerialを追加して、以下のように記述します

main.cpp

#include <Arduino.h>
#include <SoftwareSerial.h>

SoftwareSerial mySerial(12, 14); // TX, RX

void setup() {
  Serial.begin(57600);  
  Serial.println("Hello");
  
  mySerial.begin(9600);
}

void loop() { // run over and over
 while (mySerial.available() > 0){
  char c = mySerial.read();
  Serial.print(c);
 }
}

解説

SoftwareSerialは、Serialクラスのシリアルポート以外のデジタルピンでシリアル通信を可能にするライブラリです。

コンストラクタではTXとRXピンを指定します。

SoftwareSerial(receivePin, transmitPin)

receivePinは発信機のTXと繋ぐほう、transmitPinは発信機のRXと繋ぐほうです。(GPSの使い方で出てくるサイトはこれを逆にしています。間違えないように!!)

取得結果

whileが1ループしたら一文字書き出しています。(1ループで1行でないことに注意!)

$GPGSV,4,1,13,32,86,342,32,194,79,163,,25,62,045,24,22,55,320,26*42
$GPGSV,4,2,13,31,40,294,,29,29,130,,10,27,205,,12,26,043,33*75
$GPGSV,4,3,13,26,16,227,,195,10,168,,23,07,181,,24,06,085,*48
$GPGSV,4,4,13,03,02,328,*43
$GPRMC,094405.640,V,,,,,10.51,177.20,011022,,,N*75
$GPVTG,177.20,T,,M,10.51,N,19.47,K,N*3F
$GPZDA,094405.640,01,10,2022,,*5A
$GPGGA,094407.000,3436.2891,N,13531.3637,E,1,5,1.88,19.6,M,34.4,M,,*6F
$GPGLL,3436.2891,N,13531.3637,E,094407.000,A,A*53
$GPGSA,A,3,32,25,22,10,12,,,,,,,,2.09,1.88,0.91*04
$GPGSV,4,1,13,32,86,342,32,194,79,163,,25,62,045,25,22,55,320,25*40
$GPGSV,4,2,13,31,40,294,,29,29,130,,10,27,205,19,12,26,043,32*7C
$GPGSV,4,3,13,26,16,227,,195,10,168,,23,07,181,,24,06,085,*48
$GPGSV,4,4,13,03,02,328,*43
$GPRMC,094407.000,A,3436.2891,N,13531.3637,E,1.31,56.63,011022,,,A*51
$GPVTG,56.63,T,,M,1.31,N,2.42,K,A*0C

この状態では読めないので、ライブラリを使ってエンコードします

TinyGPSPlusによるエンコード

こちらのライブラリを追加しましょう

main.cpp

#include <Arduino.h>
#include <TinyGPSPlus.h>
#include <SoftwareSerial.h>
 
SoftwareSerial mySerial(12, 14); // TX, RX

// The TinyGPSPlus object
TinyGPSPlus gps;

void displayInfo()
{
  Serial.print(F("Location: ")); 
  if (gps.location.isValid())
  {
    Serial.print(gps.location.lat(), 6);
    Serial.print(F(","));
    Serial.print(gps.location.lng(), 6);
  }
  else
  {
    Serial.print(F("INVALID"));
  }

  Serial.print(F("  Date/Time: "));
  if (gps.date.isValid())
  {
    Serial.print(gps.date.month());
    Serial.print(F("/"));
    Serial.print(gps.date.day());
    Serial.print(F("/"));
    Serial.print(gps.date.year());
  }
  else
  {
    Serial.print(F("INVALID"));
  }

  Serial.print(F(" "));
  if (gps.time.isValid())
  {
    if (gps.time.hour() < 10) Serial.print(F("0"));
    Serial.print(gps.time.hour());
    Serial.print(F(":"));
    if (gps.time.minute() < 10) Serial.print(F("0"));
    Serial.print(gps.time.minute());
    Serial.print(F(":"));
    if (gps.time.second() < 10) Serial.print(F("0"));
    Serial.print(gps.time.second());
    Serial.print(F("."));
    if (gps.time.centisecond() < 10) Serial.print(F("0"));
    Serial.print(gps.time.centisecond());
  }
  else
  {
    Serial.print(F("INVALID"));
  }

  Serial.println();
}
 
void setup() {
  Serial.begin(57600);
  Serial.println("Goodnight moon!");

  mySerial.begin(9600);
}
 
void loop() { // run over and over
 while (mySerial.available() > 0){
  char c = mySerial.read();
  gps.encode(c);
  if (gps.location.isUpdated()){
    // Serial.print("LAT="); Serial.println(gps.location.lat(), 6);
    // Serial.print("LONG="); Serial.println(gps.location.lng(), 6);
    // Serial.print("ALT="); Serial.println(gps.altitude.meters());
    displayInfo();
  }
 }
}

解説

先ほどではGPS受信機からシリアル通信で受け取ったデータ(char c = mySerial.read();の部分)をそのまま表示させていましたが、今回はgps.encode(c);でエンコードしています。

そうするとgpsオブジェクトにはエンコードされた居場所データが入っていますので、これをlocationやaltitudeなどのプロパティで読み取ってやれば、緯度経度や取得時間など読み取れます。

繰り返しになりますが、シリアル通信ではwhileが1ループで1文字を書き出します。
他のプログラムにコピーする時はwhileごとコピペしましょう。

platformio.ini

[env:esp32dev]
platform = espressif32
board = esp32dev
framework = arduino
lib_deps = 
	mikalhart/TinyGPSPlus@^1.0.3
	plerup/EspSoftwareSerial@^6.16.1
monitor_speed = 57600

Discussion