Arduinoでエアコンの1時間タイマーを自作する

ArduinoでGPSロガーを自作する「タイムスタンプ対応版」

この記事は別ブログ(2021/04/01投稿)から移行したものです。

前回最低限の機能を持ったGPSロガーを作りましたが、今回は作成したログファイルの”作成日時”(更新日時も)の記録に対応しました。

所謂タイムスタンプというやつです。

通常ArduinoUnoでタイムスタンプに対応する方法で一般的なのはRTCを利用した方法ですが、
今回は電波さえ受信できれば時刻に狂いのないGPSが主人公ですのでこの日時データを利用しない手はないでしょう(取得できるのはUTC時刻なので少し厄介ですが)

前回のプログラムとは内容が変わって前回利用しなかった「TinyGPS++」ライブラリを利用します。
これによってプログラムもスッキリしますし時刻の取得も簡単に行えます。

今回のプログラムで軽く2分ほどログを記録しました。
作成日時も日本時間に合っていますし、更新日時も記録されています。
完璧です😎



回路図などは前回と全く同じです。

前回↓



プログラム

メインルーチン


#include <SoftwareSerial.h>
#include <TimeLib.h>
#include <SPI.h>
#include <SD.h>
#include <TinyGPS++.h>

SoftwareSerial mySerial(2,3); //rx=2 tx=3
TinyGPSPlus gps;
const int cs = 10;//SDカードCS
File logFile;
char fileName[16];
int fileNum = 0;
const int offset = 9;//TokyoのタイムゾーンはUTCに+9時間
int Year;
byte Month, Day, Hour, Minute, Second, hundredths;
unsigned long age;
char c;
bool newData;
bool newGPS;
String GGA;
String RMC;
int a;
int b;


void setup(){
  //Serial.begin(115200);
  INIT_GPS();//GPS初期化、設定
  INIT_SD();//SDカード初期化、準備
  Clear_Buf();//SoftwareSerial受信バッファクリア(これを行わないと上手く動かなかった)
}

void loop(){
  newData = false;
  a = -1;
  while(mySerial.available()){
    GGA = mySerial.readStringUntil('\n');//改行まで読み込む
    RMC = mySerial.readStringUntil('\n');//同上
    newData = true;
  }
   
  if(GGA != "" && newData == true){
    a = RMC.indexOf("V");//RMCセンテンスにステータス”V”が含まれているかチェック(含まれていなければ”-1”を返す)
    
    if(a==-1){//ステータスに”V”が含まれていないことを確認、SDカードへログ書き込み
      logFile = SD.open(fileName, FILE_WRITE);
      logFile.print(GGA.c_str());//String型はそのままでは書き込めないので".c_str()"でchar型に変換して書き込む
      logFile.print(RMC.c_str());
      logFile.close();//書き込み後は必ず閉じる
      newData = false;
    }
    else{//ステータスに"V"が含まれていたら記録しない
      newData = false;
    }
   }
}


GPS初期化関数


void INIT_GPS(void){//GPSの初期化と設定

  mySerial.begin(9600); //GPSと通信開始、GPSデフォルトの通信レート9600
  //以下2行はGPSの設定
  mySerial.print("$PMTK314,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0*28\r\n");//$GGA、RMCのみ送信
  mySerial.print("$PMTK225,0*2B\r\n");//間欠モードオフ(省エネモードオフ)

  while(1){
    newData = false;
    while(mySerial.available()){
      c = mySerial.read();
      if(gps.encode(c))//TinyGPS++のGPSデータ読み取り関数
      newData = true;
    }
    
    if(newData){
      if(gps.location.isValid()){//位置情報が更新されていたら初期化終了
        break;
      }
      else{//位置情報が得られなかった場合ループ
        newData = false;
      }
    }
  }
}


このGPS(GYSFDMAXB)の設定はSoftwareSerialの"mySerial.print"で以下のような設定用文字列を送信することで行います。
mySerial.print("$PMTK314,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0*28\r\n");

上記を例にして
”$PMTK314”は設定の項目。
PMTK314はGPSが送信するセンテンスの種類と頻度を設定できます。

後に続く”0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0”で実際にGPSが送信するセンテンスの種類と頻度を決めます。
左から順にGLL,RMC,VTG,GGA,GSA,GSV,飛ばして17個目がZDAです。
数字は0~5まで設定でき、送信の頻度を「0=送信しない」「1=毎回送信する」「2~5=2~5回に一回送信する」というふうに設定できます。
例のコマンドではRMCとGGAのみを毎回送信する設定になっています。
不要なセンテンスは排除。

*以降の2文字"28"は$と*で挟まれた"PMTK~,0,0"をXORし16進数表記にしたもの。
所謂チェックサムです。
便利な計算サイトがあるのでこちらを利用しました。

"\r\n"は改行コードです。
コマンドの送信は必ず改行コードで閉めます。


SDカード初期化


void INIT_SD(void){//SDカード初期化とログファイル作成、タイムスタンプ設定
  b = 0;
  newData = false;
  while(1){
    while (mySerial.available()){//GPSからシリアル通信データを受け取っていたら
      char c = mySerial.read();//バッファを読み込み
      if(gps.encode(c))//TinyGPS++でエンコード
      newData = true;
    }
    
    if (newData){
      b++;
      newData = false;
      
        if(b == 3){//GPSデータ受信4回目にデータを受け入れる(数回データを受信してからでないと上手くいかなかったので)
          //delay(50);
          //エンコードしたTinyGPS++の日時データをシステム時刻に設定
          setTime((gps.time.hour()), (gps.time.minute()), (gps.time.second()), (gps.date.day()), (gps.date.month()), (gps.date.year()));
          adjustTime(offset * SECS_PER_HOUR);//設定したシステム時刻(UTC時刻)をJST時刻(+9時間)に変換
          break;//日時の設定終わり
        }
    }
  }
  //SDカード初期化とログファイル作成
  SdFile::dateTimeCallback( &dateTime );//SDカードのタイムスタンプ用日時を返す関数
  if(!SD.begin(cs)){
  return;
  }
  
  String t;
  while(1){//ファイル名生成
    t = "GPS";
    if (fileNum < 10){
      t += "00";
    }
    else if(fileNum < 100){
      t += "0";
    }
    t += fileNum;
    t += ".LOG";
    t.toCharArray(fileName, 16);
    if(!SD.exists(fileName)) break;
    fileNum++;
  }
  
  // ログファイルに1行目を書き込む
  logFile=SD.open(fileName, FILE_WRITE);
  logFile.println("@適当なIDとか/ver1.0/wgs84");
  logFile.close();
  //SDカード初期化とタイムスタンプ設定おわり
}

SDカードの初期化ではまずTinyGPS++を利用しGPSによる正確な時刻を取得し、Arduinoのシステム時刻を設定します(setTime)
GPSから取得した時刻はUTC時刻なので"adjustTime"で時刻を9時間進め、JSTに合わせています。

あとはSDカードのタイムスタンプ用コールバック関数がシステム時刻から時刻を読み込むので、生成したログファイルの作成日時を自動的に記録してくれます。

バッファのクリア


void Clear_Buf(void){//バッファのクリア
  delay(500);
  while(mySerial.available()>0){//シリアル通信の受信バッファが空になるまで繰り返し読み込む
    char n = mySerial.read();
  }
}

void loop()に入る前にSoftwareSerialのバッファをクリア。


タイムスタンプ用コールバック関数


void dateTime(uint16_t* date, uint16_t* time){//SDカードのタイムスタンプ用日時を設定
  
  *date = FAT_DATE(year(), month(), day());//Arduinoのシステム日時を読み込む

  *time = FAT_TIME(hour(), minute(), second());
}

以上です。
基板製作はもう少しあとになりそう。