ArduinoでのGPSモジュール(GMS7-CR6)からの日時取得

#はじめに
RandyHoo(<a href=https://twitter.com/Randy_Hoo_>@RandyHoo_</a>)と申します。
ニキシー管時計を作ったりしてる組み込みプログラマです。
![NixieClock.jpg](https://qiita-image-store.s3.amazonaws.com/0/167410/71247d3a-1a1b-6c53-cd27-1851a5d1205e.jpeg)
ニキシー管時計を作ってるときにハマった箇所なので展開します。

今回解説するコードは<a href=https://github.com/RandyHoo/Nixie_Clock>GitHub</a>にて公開しています。

#実装・解説
今回使用したGPSモジュールは、<a href=http://akizukidenshi.com/catalog/g/gM-08061/>GMS7-CR6</a>です。

```Nixcie_Clock.ino
#include <SoftwareSerial.h>
#include <string.h>
#include <stdlib.h>
#include <MsTimer2.h>
#include <Wire.h>

int CalenderArray[6] = {}; //0:Year1桁目, 1:Year2桁目, 2:Month1桁目, 3:Month2桁目, 4:Day1桁目, 5:Day2桁目

int ClockArray[6] = {}; //0:Hour1桁目, 1:Hour2桁目, 2:Min1桁目, 3:Min2桁目, 4:Sec1桁目, 5:Sec2桁目
boolean data_get_flag = false; //GPSデータ取得確認用フラグ

void loop() {
  data_get_flag = GPSDataCatch();//初期値設定用のデータ取得
}
```


```GPSDataCatch.ino
int GPSDataCatch(){
  String year_1; //現在の年1桁目
  String year_2; //現在の年2桁目
  String month_1; //現在の月1桁目
  String month_2; //現在の月2桁目
  String day_1; //現在の日1桁目
  String day_2; //現在の日2桁目
  String hour_1; //現在の時1桁目
  String hour_2; //現在の時2桁目
  String min_1; //現在の分1桁目
  String min_2; //現在の分2桁目
  String sec_1; //現在の秒1桁目
  String sec_2; //現在の秒2桁目

  char GPS_Rx_buf[256]; //受信用バッファ(255文字分)
  char GPS_Rx_count; //受信データカウンタ

  String want_frame = "$GPRMC"; //データを取り出したいフレーム
  String WantStatus = "A"; //ステータス判別用

  char data_read_count = 0; //データ読み出しカウンタ
  int data_get_flag = false; //データ取得結果
```


GPSから受信したデータを解析していきます。
文字変換を行うため、String型で扱います。

```GPSDataCatch.ino
  //Ackフレームを受信するまでループを回す
  do {
    if ( sGps.available() ) {
      GPS_Rx_buf[ GPS_Rx_count ] = sGps.read();
      GPS_Rx_count++;
    }
    //バッファが溢れそうなら
    if ( GPS_Rx_count > 250 ) {
      break;
    }
  } while ( GPS_Rx_buf[ GPS_Rx_count - 1 ] != 0x0A );
  GPS_Rx_buf[ GPS_Rx_count ] = '\0'; //一番最後の要素に改行を入れる
  String buf_str = GPS_Rx_buf; //Stringで処理するために変換
```

データは「,」で区切られているので、「,」の位置を取得し、必要なデータだけを取得していきます。
日時が取得できなかった場合にはこの関数の戻り値でfalseを返します。

```GPSDataCatch.ino
  //欲しいデータかどうか調べる
　//LogSample : $GPRMC,142152.000,A,3247.4679,N,12957.0161,E,0.00,36.32,250116,,,A*5A
  if ( want_frame == buf_str.substring( 0, 6 ) && WantStatus == buf_str.substring( 18, 19 ) ) {
    data_read_count = buf_str.indexOf( ",", data_read_count ); //1つ目の区切り文字を探す
    data_read_count++; //不要データなので次のデータへ

    data_read_count = buf_str.indexOf( ",", data_read_count ); //2つ目の区切り文字を探す(時刻)
    data_read_count++; //時計・カレンダーでは文字列位置を直指定するので次のデータへ
```

時刻はUTC(協定世界時)で取得するので、日本時間に修正します。
また、10以下の数字を「0X」表示するために変換処理を噛ませています。

```GPSDataCatch.ino
    String UTC_time = buf_str.substring( 7, 9 ); //時刻修正処理の為にHourを抜き出す
    int JP_time = atoi( UTC_time.c_str() ) + 9; //日本時間に修正
    //変換した際に24時以上になった場合
    if ( JP_time > 23 ) {
      JP_time -= 24;
    }
    String JP_time_str = String( JP_time ); //日本時間に差し換えるためにStringに変換
    //時刻が1桁だった場合
    if ( JP_time < 10) {
      String change_double_digit_calc = JP_time_str.substring( 0, 1 ); //1桁目を保持
      JP_time_str = "00"; //JP_time_strのデータ長を2に増設する
      char hoge_cddc = *change_double_digit_calc.c_str(); //1桁目に0を差し込むためにchar型に変換
      JP_time_str.setCharAt( 0, '0' ); //1桁目を0に差し替える
      JP_time_str.setCharAt( 1, hoge_cddc ); //2桁目に1桁目を差し込む
      String JP_time_str = String( JP_time ); //もう一度Stringに変換
    }
    
    String JP_time_first_digit = JP_time_str.substring( 0, 1 ); //1桁目を取り出す
    char hoge_1 = *JP_time_first_digit.c_str(); //char型に変換
    String JP_time_second_digit = JP_time_str.substring( 1, 2 ); //2桁目を取り出す
    char hoge_2 = *JP_time_second_digit.c_str(); //char型に変換
    buf_str.setCharAt( 7, hoge_1 ); //1桁目を差し替える
    buf_str.setCharAt( 8, hoge_2 ); //2桁目を差し替える
```

日時が取得出来たのでそれぞれint型に変換して配列に入れていきます。

```GPSDataCatch.ino
    //時1桁目を取得
    hour_1 = buf_str.substring( 7, 8 );
    ClockArray[0] = hour_1.toInt();
    //時2桁目を取得
    hour_2 = buf_str.substring( 8, 9 );
    ClockArray[1] = hour_2.toInt();
    //分1桁目を取得
    min_1 = buf_str.substring( 9, 10 );
    ClockArray[2] = min_1.toInt();
    //分2桁目を取得
    min_2 = buf_str.substring( 10, 11 );
    ClockArray[3] = min_2.toInt();
    //秒1桁目を取得
    sec_1 = buf_str.substring( 11, 12 );
    ClockArray[4] = sec_1.toInt();
    //秒2桁目を取得
    sec_2 = buf_str.substring( 12, 13 );
    ClockArray[5] = sec_2.toInt();

    //3～9つ目までの区切り文字をスキップする
    for (int i = 0; i < 7; i++) {
      data_read_count = buf_str.indexOf( ",", data_read_count ); //3つ目の区切り文字を探す
      data_read_count++; //不要データなので次のデータへ
    }

    //日1桁目を取得
    day_1 = buf_str.substring( data_read_count, data_read_count + 1 );
    CalenderArray[4] = day_1.toInt();
    data_read_count++;
    //日2桁目を取得
    day_2 = buf_str.substring( data_read_count, data_read_count + 1 );
    CalenderArray[5] = day_2.toInt();
    data_read_count++;
    //月1桁目を取得
    month_1 = buf_str.substring( data_read_count, data_read_count + 1 );
    CalenderArray[2] = month_1.toInt();
    data_read_count++;
    //月2桁目を取得
    month_2 = buf_str.substring( data_read_count, data_read_count + 1 );
    CalenderArray[3] = month_2.toInt();
    data_read_count++;
    //年1桁目を取得
    year_1 = buf_str.substring( data_read_count, data_read_count + 1 );
    CalenderArray[0] = year_1.toInt();
    data_read_count++;
    //年2桁目を取得
    year_2 = buf_str.substring( data_read_count, data_read_count + 1 );
    CalenderArray[1] = year_2.toInt();
```

以上です。