Arduinoデータロガーシールドを使ってデータを記録しました

何を学習するの?

  • Arduino でセンサー値を SD カードに保存するとき、ファイルのタイムスタンプが 2000 年のまま変わらないという問題に悩んだことはありませんか。
  • 実は Arduino の標準 SD ライブラリは、ファイルシステムの作成日時・更新日時を更新しない仕様になっています。 そのため、PC に取り込んだときにファイルが時系列で並ばず、データ整理が非常に不便です。
  • この記事では、RTC(リアルタイムクロック)を活用して、 Arduino でもファイルに正しいタイムスタンプを付ける方法 を詳しく解説します。

単にデータを保存する方法と本記事の方法を比較します

項目一般的な Arduino ロガー本記事の方法
測定データのタイムスタンプ取得できる取得できる
ファイルのタイムスタンプ2000年など初期値のままRTC を使って正しい日時を付与
PC でのファイル管理時刻が狂って不便正しい時系列で並ぶ
長期観測不向き非常に便利

Arduino の SD ファイルが「2000年」になる理由

Arduino の SD ライブラリ(SD.h / SDFat)は、以下のような仕様になっています。

  • FAT ファイルシステムのタイムスタンプを自動更新しない
  • 作成日時・更新日時は FAT の初期値(2000/01/01)のまま
  • 測定データの時刻はデータとして残せても、ファイル自体の時刻は正しく残らない

つまり、どれだけ正確に RTC で測定時刻を記録しても、 ファイルのタイムスタンプは常に初期値のままです。

RTC を使ってファイルタイムスタンプを更新する仕組み

  • SDカードにデータを書き込む際にファイル情報として作成日時と更新日時を書き込む仕組みを組込みます。
— RTC を使ってファイルタイムスタンプを更新するフロー —

検討に使ったデータロガーシールド

  • SDカードとRTC(Real Time Clock)モジュールを含んだデータロガーシールドを購入しました。データロガーシールドで調べると多くはRTC(Real Time Clock)モジュールを含みます。データを測定・収集する場合に時間情報は重要だからと思います。
— データロガーシールドの外観と搭載モジュール —
  • シールドとして構成されていますので、Arduino UNO R3に挿入すればそれで接続は終了し配線の手間がありません。
— データロガーシールドとArduino UNO R3を接続 —
使用した製品と購入先は?
Arduino UNOR3 CPU Amazon [Arduino用のUNO R3 最終版スタータキット]
データロガーシールドメモリー、RTCAmazon [データロガーシールド]

スケッチに使用するライブラリの入手先

  • スケッチで使用する、ライブラリ(DS3231.h、DS3231.cpp)は[GitHub – jarzebski/Arduino-DS3231: DS3231 Real-Time-Clock]から取得しインストールしてください。
  • Arduno用のDS3231用のライブラリは何種類かが公開されていますが、この記事で使用しているライブラリは上記のライブラりであり、異なるライブラリを使用するとコンパイル時にエラーが発生します。

  • GitHubでライブラリをダウンロードする
    • 対象ライブラリのGitHubページを開く
    • 緑色の Code ボタンをクリック (リストの右上にある[<>Code▼]のボタンです。)
    • Download ZIP を選択
    • ZIPファイルがPCに保存される
    • ※「Source code (zip)」でもOKですが、基本は上の手順で問題ありません。
  • Arduino IDEにZIPライブラリをインストールする (Arduino IDE 1.x / 2.x 共通)
    • Arduino IDE を起動
    • メニューから
    • スケッチ → ライブラリをインクルード → .ZIP形式のライブラリをインストール
  • 先ほどダウンロードした ZIP ファイルを選択
    • IDE が自動的に展開してインストール完了
  • インストール確認
    • メニューのスケッチ → ライブラリをインクルード
    • 一覧の「インストール済み」セクションにライブラリ名が表示されていれば成功
  • 注意点(特にGitHub系でよくある)
    • ZIPの中にさらにフォルダ階層があるとIDEが認識しないことがある
      → libraryname/libraryname/*.h のように二重構造になっていないか確認
    • src フォルダがあるタイプはそのままでOK
    • リリースページに「Arduino用に整えたZIP」がある場合はそちらを使うと確実

RTC(実時間)モジュールに時刻を書き込みます

  • データロガーシールドのRTC(実時間)モジュールの時間はバッテリーを入れるだけでは不定の時間になっています。スケッチをコンパイルした時刻をRTC(実時間)モジュールに書き込みます。
////////////////////////////////////////////////////////
// RTC(実時間)モジュールの時間設定を行い、時間を画面へ表示する
////////////////////////////////////////////////////////

//ライブラリインクルード
#include <Wire.h>
#include <DS3231.h>

//実時間モジュール設定
DS3231 clock;
RTCDateTime dt;

//一般変数
int i;

void setup()
{
  Serial.begin(9600);

  //実時間モジュールの初期化
  Serial.println("Initialize RTC module");
  // Initialize DS3231
  clock.begin();

  // スケッチのコンパイル時の時刻をRTCモジュールに書き込む
  clock.setDateTime(__DATE__, __TIME__);  

  dt = clock.getDateTime();

  //一般変数の初期化
  i=0;
  
  delay(1000);  
  
  //シリアルログの最初の行に表示項目を表示する
  Serial.print("Count,");    
  Serial.print("Year,");
  Serial.print("Month,");
  Serial.print("Day,");
  Serial.print("Hour,");
  Serial.print("Minuts,");
  Serial.println("Second,");
   
}

void loop()
{
  //シリアルログの表示
  dt = clock.getDateTime();
  Serial.print(i);   Serial.print(",");  
  Serial.print(dt.year);   Serial.print(",");
  Serial.print(dt.month);  Serial.print(",");
  Serial.print(dt.day);    Serial.print(",");
  Serial.print(dt.hour);   Serial.print(",");
  Serial.print(dt.minute); Serial.print(",");
  Serial.println(dt.second); 

  i+=1; //iに1を加える
  
  delay(1000);   // 1秒の待ち時間
}

SDモジュールにデータを保存します

  • RTC(実時間)モジュールから日時と時刻を呼び出し、このデータをSDカードに書き込みます。
//////////////////////////////////////////////
// SDカードにデータを保存する
//////////////////////////////////////////////

//ライブラリインクルード
#include <Wire.h>
#include <DS3231.h>
#include "SPI.h" 
#include "SD.h"

//SDカード設定
File myFile;                //クラス指定
const int chipSelect = 10;  //チップセレクトピン指定

//実時間モジュール設定
DS3231 clock;
RTCDateTime dt;

//一般変数
int i;

//ファイル作成・更新時のタイムスタンプを記録するスケッチ------------------------------
char timestamp[30];

// call back for file timestamps
void dateTime(uint16_t *date, uint16_t *time) {
 sprintf(timestamp, "%02d:%02d:%02d %2d/%2d/%2d \n",dt.hour,dt.minute,dt.second,dt.month,dt.day,dt.year-2000);
 // return date using FAT_DATE macro to format fields
 *date = FAT_DATE(dt.year,dt.month,dt.day);
 // return time using FAT_TIME macro to format fields
 *time = FAT_TIME(dt.hour,dt.minute,dt.second);
}
//------------------------------------------------------------------------------

void setup()
{
  Serial.begin(9600);

  //実時間モジュールの初期化
  Serial.println("Initialize RTC module");
  // Initialize DS3231
  clock.begin();

  // スケッチのコンパイル時の時刻をRTCモジュールに書き込む
  // 一回書き込めば、再書き込みは不要なのでコメントアウトする
  // clock.setDateTime(__DATE__, __TIME__); 

  //SDカードの初期化
  Serial.println("Initializing SD card...");
  
  if (!SD.begin(chipSelect)) {
    Serial.println("Initialization failed or does not exist");
    while (1);
  }
  Serial.println("Initialization done.");
  Serial.println();

  dt = clock.getDateTime();

  //ファイル作成・更新時のタイムスタンプを記録するスケッチ------------------------------
  SdFile::dateTimeCallback(dateTime);
  sprintf(timestamp, "%02d:%02d:%02d %2d/%2d/%2d \n",dt.hour,dt.minute,dt.second,dt.month,dt.day,dt.year-2000);
  //------------------------------------------------------------------------------

  //一般変数の初期化
  i=0;
  
  delay(1000);  
  
  //シリアルログの最初の行に表示項目を表示する
  Serial.print("Count,");    
  Serial.print("Year,");
  Serial.print("Month,");
  Serial.print("Day,");
  Serial.print("Hour,");
  Serial.print("Minuts,");
  Serial.println("Second,");

  //ログデータの最初の行に記録項目を記入する
  myFile = SD.open("DATA_LOG.csv", FILE_WRITE); //SDカードをオープンする 
  myFile.print("Count,");   
  myFile.print("Year,");
  myFile.print("Month,");
  myFile.print("Day,");
  myFile.print("Hour,");
  myFile.print("Minuts,");
  myFile.println("Second");
  myFile.close();   //SDカードをクローズする   
}

void loop()
{
  //シリアルログの表示
  dt = clock.getDateTime();
  Serial.print(i);   Serial.print(",");  
  Serial.print(dt.year);   Serial.print(",");
  Serial.print(dt.month);  Serial.print(",");
  Serial.print(dt.day);    Serial.print(",");
  Serial.print(dt.hour);   Serial.print(",");
  Serial.print(dt.minute); Serial.print(",");
  Serial.println(dt.second); 

  //SDカードへの書き込み

  Serial.println("Write Data to SD Card...");

  myFile = SD.open("DATA_LOG.csv", FILE_WRITE); 
  myFile.print(i);   myFile.print(",");
  myFile.print(dt.year);   myFile.print(",");
  myFile.print(dt.month);  myFile.print(",");
  myFile.print(dt.day);    myFile.print(",");
  myFile.print(dt.hour);   myFile.print(",");
  myFile.print(dt.minute); myFile.print(",");
  myFile.println(dt.second);

  myFile.close();

  i+=1; //iに1を加える
  
  delay(1000);   // 1秒の待ち時間
}

スケッチの動作を確認します

  • シリアルモニタを使ってスケッチの動作を確認します。
— シリアルモニタの表示 —
  • SDカードには[DATA_LOG.CSV]が作られています。
  • ファイルの更新日時が現在の時間になっています。
— CSV内に保存されたデータ —

実際の使用例

まとめ

  • SDカードにデータを保存した時に、更新日時が2000年になっており使い勝手が悪いなと感じたことがこの記事の発端です。
  • Arduinoのホームページやインターネットを色々調べるとやっと方法が見つかりました。DS3231用のライブラリも色々あり、この目的に使えるライブラリはそれほど多くないことが解り、ライブラリやGitHubの勉強にもなりました。

ご質問、誤植の指摘などありましたら。「問い合わせ 」のページからお願いします。