PR

Seeeduino XIAOとGPSモジュールで時刻を表示する

組み込みエンジニア
本記事はプロモーションが含まれています。

こんにちは、ENGかぴです。

GPSモジュールは現在地の情報や時刻情報などを衛星からの電波を受信してユーザーに通知するモジュールです。Seeeduino XIAOを使ってGPSモジュールから時刻と位置情報を取得してLCDに表示して動作確認したことをまとめています。

GPSモジュールはGPS受信機キット:AE-GYSFDMAXB(秋月電子)を使用しLCDはAQM1602XA-RN-GBW(秋月電子)を使用しています。GPSモジュール用のライブラリを追加して時刻情報を取得して動作確認したことを下記記事にまとめています。

ArduinoのライブラリでGPSモジュールで時刻を取得する

Seeeduino XIAOを使って動作確認を行ったことを下記リンクにまとめています。

Seeeduino XIAOで学べるソフト開発と標準ライブラリの使い方

GPSモジュールの使い方

GPSモジュールはSeeeduino XIAOのシリアル通信とGPSモジュールを配線するだけでデータを取得することができます。デフォルト設定では不要なデータも含まれてしまうため必要なデータだけが受信できるようにGPSモジュールの設定を行います。

GPSモジュールは秋月電子の「GPS受信機キット:AE-GYSFDMAXB」を使っているため下記リンクのデータシート(技術資料)を一部抜粋して説明します。

秋月電子ーGPS受信機キット:AE-GYSFDMAXB

GPSモジュールの電文(NMEAパケット)

GPSモジュールはメーカー問わず基本的にNMEAパケットフォーマットに従ってデータを送信するようになっています。NEMAパケットフォーマットは以下の通りです。

NMEAパケットフォーマット:GYSFDMAXB仕様書より抜粋
NMEAパケットフォーマット:GYSFDMAXB仕様書より抜粋

NMEAパケットは最後のCR・LFを除いて文字コード(アスキーコード)となります。各パラメータをまとめます。

パケット内容
Preamble1バイトの文字’$’
Talker ID4バイトの文字”PMTK”(メーカーによって異なる)
Packet Type3バイトの文字”000″から”999″(電文の番号)
Data Field任意の文字列で、Packet Typeによってデータ長が異なる。
設定項目が複数ある場合は”,”で区切る。
*1バイトの文字でData Fieldの終わり判定に使用する。
CHK1、CHK2Preambleと*までの間のチェックサム(EOR)値を2バイト文字で表現する。
CR、LF2バイトのバイナリデータCR:0x0D、LF:0x0Aとなる。
NMEAパケットの説明

GYSFDMAXBの仕様書ではCHK1、CHK2の説明にPreambleと*までのチェックサム値と記載されているため各バイトを加算して1バイトデータを文字列としてチェックサムとして計算してしまいそうですが、EORで計算する必要があります。

チェックサムが一致しない場合はGPSモジュール側で無効なパケットとして処理されてしまいます。

PR:わからないを放置せず、あなたにあった最低限のスキルを身に着けるコツを教える テックジム 「書けるが先で、理解が後」を体験しよう!

出力制御のフォーマット

GPSモジュールには各種データを出力する項目を選択する314パケットがあります。今回使用しているAE-GYSFDMAXBにおいてはDate Fieldは全部で19の設定項目がありますが、実装されている項目は以下の通りです。

種別内容設定番号
GPGLL位置情報(緯度、経度)を出力する。0
GPRMC位置情報と時刻(UTC時刻)、速度と方位を同時に出力する。1
GPVTG方位と速度を出力する。2
GPGGA位置情報(緯度、経度)、GPS測位状態、測位衛星数を出力する。3
GPGSAGPS衛星の使用衛星番号、各種DOP、動作モードを出力する。4
GPGSVGPS衛星の生成情報を出力する。5
GPZDA時刻(UTC時刻)と日付を出力する。17
GPSモジュール(GYSFDMAXB)に実装されている出力項目

GPSモジュールから日付と時刻を含む情報を取得したいので「314 PMTK_API_SET_NMEA_OUTPUT」の設定からGPZDA interval以外を出力しないように設定します。314フォーマットにおいて対象の番号の周期設定を変更することでインターバルのタイミングを任意に設定できます。

char buf314[]= "PMTK314,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0";
//正式なフォーマットは$,*checksum,CR,LFを追加する

例のように設定番号の0番と17番目以外のインターバルを0にすることでGPSモジュールからの情報を制限することができます。GPGLLとGPZDAはともに1にしているため1秒周期でGPGLLとGPZDAが出力されます。他の項目についても出力したい場合は0~5までの任意のタイミングに設定します。

電源ON以降(モジュールは電源ON後で最大1500ms準備時間が必要)にウェイトを置いた後で、314フォーマットによる出力制御設定を行います。

PR:スキマ時間で自己啓発!スマホで学べる人気のオンライン資格講座【スタディング】まずは気になる講座を無料で体験しよう!

時間データの受け取りタイミング

void loop(){

  if( digitalRead(PIN_DI1) == 0 ){ //LOWアクティブなので0で判定
    gpsok = true; //GPSのデータが確定
    timgpswait = GPS_WAIT;
  }

  if( timgpswait == TIME_UP ){ //1ppsが規定時間途絶えた
    timgpswait = TIME_OFF;
    gpsok = false; //GPSのデータが未確定とする
  }
}
/* 表示データ作成 */
void ShowData(void){

  if( gpsok ){ //GPSのデータが確定していれば以下の処理
    //LCDに時刻を表示する
  }
}

GPSモジュールは衛星を測位する前からシリアル通信でデータを通知します。データが確定していない場合のデータで時刻や測位情報として採用してしまうと不確定な情報が表示されるてしまいます。

GPSモジュールは測位が完了しデータが確定できた時1ppsピンからパルスを出力して通知する機能を持っています。1ppsピンの状態を監視してパルスが生成されていれば正常な情報であると判断してGPSモジュールの情報として処理するようにします。

【クリエイターズファクトリー】卒業がない!挫折する心配なし!Webスクール説明会申し込み

GPSモジュールからの受信データの受け入れ

/* RX function add */
void RxDataChk(){

  //受信データ数の算出
  if( rxgps.buf[rp] == '$' ){ //ヘッダーの確認
    flg = false;
    for(uint16_t i=0; i< rxsz; i++ ){
    if( rxgps.buf[rp] == '*' ){ //'*'までのデータ数を確認
      flg = true;
      sz = i+1;
      allsz = sz + 4; //チェックサムと制御文字を含めた全体を受信するため+4
    }
    if(++rp >= RING_SZ ){
      rp = 0;
    }
  }
  if( rxsz >= allsz && flg){
    //受信データを一時的に保管
    sum = sumbcc(&RxData[1], sz-2); //サム値の計算
    sprintf((char*)&buf[0],"%X", sum); //サム値をGPSと同じ文字に変換
    sum_h = buf[0];
    sum_l = buf[1];       
    if( RxData[sz] == sum_h && RxData[sz+1] == sum_l ){
      ControlSet(); //チェックサムで受け入れた後の処理
    }
  }else{
    if(++rxgps.rp >= RING_SZ ){
      rxgps.rp = 0;
    } 
  }
}

GPSモジュールから受信するデータもNMEAパケットに従っているためNMEAパケットの’$’から’*’までのデータを確認することでパケットタイプによるデータの管理ができます。

データ長はGPSモジュールから出力される種別に応じて異なるため、データ長の確認を行ってから必要なデータ数のみ一時的に受け入れチェックサム(EOR)の計算を行い、異常がなければデータを受け入れる処理を行います。

サム値の計算を行った後GPSモジュールが送信しているサム値と比較するために文字列に置き換えています。GPSモジュールからは16進数の数値が大文字でA~Fが送信されるためsprintf()内でて%Xを指定して大文字で変換しています。

大文字で変換していない場合は文字が不一致となるためサム値が一致していても違う文字として認識されるため注意が必要です。

サム値の比較の方法はGPSモジュールから受信したサム値を文字からバイナリデータに変換して比較する方法もあります。

void ControlSet(){

  if( RxData[0] =='$' && RxData[1] =='G'
   && RxData[2] =='P' && RxData[3] =='Z'
   && RxData[4] =='D' && RxData[5] =='A' ){
      //LCD表示用のデータ(時刻)の表示
  }
  else if( RxData[0] =='$' && RxData[1] =='G'
	&& RxData[2] =='P' && RxData[3] =='G'
	&& RxData[4] =='L' && RxData[5] =='L' ){
    //LCD表示用のデータ(位置情報)の表示
  }
}

受け入れたデータのパケットタイプから必要なパケットのデータであるかの確認を行い対象の処理を行います。上記の例では「$GPZDA」のパケットであることを判断してLCD用のデータを生成しています。

位置情報を示すパケットに対してデータを作成する場合は「$GPGLL」のパケットであることを確認して処理を追加します。

PR:あなたのキャリアのお供に「生涯学習のユーキャン」

GPSの情報の時間を補正する

void ControlSet(void){
  tm t;
  time_t tim;
  tm* ltim;

  year = strtol((char*)&date[0],NULL,10); //文字を10進数に置き換え
  mon = strtol((char*)&date[5],NULL,10);
  hour = strtol((char*)&date[11],NULL,10);

  t.tm_year = year - 1900;
  t.tm_mon = mon - 1; //0からカウントするので-1
  t.tm_hour = hour + 9; //日本:世界標準時から9時間ずれ

  tim = mktime(&t); //UNIX時間からの経過
  ltim = localtime(&tim); //ローカル時間に置き換え

  year = ltim->tm_year + 1900;
  month = ltim->tm_mon + 1;
  Serial.print(year); //年を表示
}

GPSモジュールの時刻と日時は世界標準時が基準であるため日本での時間は世界標準時+9時間となります。世界標準時で15:00を超えたとき日本時間に換算すると24時を超えることになり日付も更新する必要がありますが、うるう年が絡んだ時など日本時間に換算しようとしたとき少し面倒なのが欠点です。

日本時間に換算にする際にtime.hのmktime()やlocaltime()を使用すると煩雑な計算をしなくても時間の管理ができます。

date[]にGPSモジュールから取得した時刻データの文字列が格納していますが10進数に変換してtmの型の変数に格納します。年・月・日・時・分・秒をそれぞれをtmの型の変数に格納した後はmktime()関数で時刻の補正を行います。

mktime()はUNIX時間(1970年1月1日0時0分0秒)からの経過時間を時刻の桁上がりなどを考慮しての補正を行いtime_tの型で戻り値として格納できる関数です。

localtime()はtime_tの型のUNIX時間をtmの型(年・月・日・時・分・秒に分けてtmの型に変換する関数です。localtime()で変換した時刻データをLCDやシリアルモニタ表示用のデータとして加工すると日本時間に換算した結果が表示されます。

動作確認

GPSモジュール動作確認の回路図
GPSモジュール動作確認の回路図

GPSモジュールからの受信データをLCDに表示するための回路図です。初期画面はGPS-SeeeduinoとVER1.00を表示しておき、GPSモジュールの時刻データが確定した後は時刻を表示するようにしています。すでに確定している場合は最初で2秒の表示としています。

GPS受信機キット:AE-GYSFDMAXBのデータシートを確認すると330 PMTK_API_SET_DATUMを発行してTOKYO-Mに合わせると時刻調整できると考えていましたが、発行してもうまく時刻が補正されませんでした。他のAPIで補正することができるかもしれません。

時刻補正がなかったためtime.hを使用して時刻の補正を行いました。

GPSモジュールの時刻情報を表示
GPSモジュールの時刻情報を表示

時刻が表示されると日本時間に換算した時刻が表示されていることを確認しました。シリアルモニタでも同様の時刻が表示されていました。

シリアルモニタに表示していた位置情報をGoogle Mapで入力して確認しましたが100m程度ずれてしまうことがありました。

GPGLLフォーマットは経度と緯度が度分で取得できるので表示方法を加工すると表示できます。
例)1122.3456,N,15044.5566,Eの場合はGoogle Mapでは 11 22.3456,150 44.5566と入力します。

スポンサーリンク

ソースコード全体

ソースコードは記事作成時点において動作確認できていますが、使用しているライブラリの更新により動作が保証できなくなる可能性があります。また、ソースコードを使用したことによって生じた不利益などの一切の責任を負いかねます。参考資料としてお使いください。

リンクからZIPファイル形式のファイルをダウンロードし、任意の場所に展開していただくとテキストファイルが生成されます。

ソースコードをダウンロード

本記事はGPSモジュールから受信したシリアル通信のデータを判断して時刻表示を行っていますが、GPSモジュール用のライブラリを追加して時刻情報を表示する方法もあります。

ArduinoのライブラリでGPSモジュールで時刻を取得する

関連リンク

Arduinoのライブラリを使って動作確認を行ったことを下記リンクにまとめています。

Arduinoで学べるマイコンのソフト開発と標準ライブラリの使い方

Seeeduino XIAOで学べるソフト開発と標準ライブラリの使い方

ESP32-WROOM-32Eで学べるソフト開発と標準ライブラリの使い方

PR:技術系の通信教育講座ならJTEX

最後まで、読んでいただきありがとうございました。

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