2022年8月18日木曜日

ArduinoでSDカードを使用する

AdafruitのData Logger Shieldを使用して,ArduinoからSDカードへデータを保存した.


 Arduinoで測定したデータはシリアル通信経由でPC上に保存できるが,長期間にわたり記録する際などPCを使わずArduino単体で保存したいこともある.このような場合,SDカードシールドを使用することでArduinoから直接SDカードへ保存することができる.

 今回使用したSDカードシールドはAdafruitのData Logger Shieldで,RTC(Real Time Clock)機能が搭載されているのでデータの取得日時も保存できる.

 SDカードスロットは標準サイズの大きさで,FAT16またはFAT32形式のフォーマットに対応している.RTCにはPCF8523が使用されており,対応電池はCR1220相当である.詳しい仕様はこちらを参照のこと.


パッケージのインストール

 RTCを使用するにはRTClibというパッケージが必要なのでパッケージマネージャからインストールを行う.似たような名前がいくつかあるが,Adafruitが提供しているものをインストールする.


RTCの時刻合わせ

 Arduinoのサイトの手順に従い,RTCLibのサンプルスケッチpcf8523を用いてPCの時刻をRTCに設定する.シリアルモニタに表示されている時刻がPCと同じであれば成功.うまくいかない場合はrtc.adjust(DateTime(F(__DATE__), F(__TIME__)));をif文の外に出せば成功するかもしれない.

SDカードへの書き込み

 まず,SDカードが正常に認識されているかを確認するため,SDのサンプルスケッチにあるCardInfoを実行する.このとき,スケッチのコメントにあるように定数chipSelectを10に変更しておく.シリアルモニタに以下が表示されていれば成功.

 次に,実際にSDカードに書き込みテストを行う.テストにはサンプルスケッチのDataloggerを使用する.このスケッチではanalogReadで取得したアナログ入力A0-A2の値を,カンマ区切りでDATALOG.TXTという名前のファイルに保存する.

 CardInfoと同様にchipSelectの値を10に変更するのを忘れないこと.スケッチの実行は数秒間で十分.ArduinoからのSDカードの取り出しは本体の電源を切った後に行った方がいい.カードにDATALOG.TXTというファイルが存在すれば保存成功で,ファイルの中身は以下のようになっている.


 このスケッチではSDカードへのデータ保存は追記モードとなっており,記録を再開すると既存のデータの次の行からデータが追加される.また,ファイル形式はCSVもいけるようだ.CSVであればExcelでの利用が非常に楽になる.
 なお,SDカードへのデータ保存中はSDカード横の赤色LEDが点灯する.

センサデータを時刻と共にSDカードへ保存する

 例として,CTセンサで測定した交流電流値を測定時刻と共にSDカードへ保存してみる.CTセンサはこちらで解説したものを3つ使用し,アナログ入力A0-A2に接続した.スケッチはサンプルスケッチのDataloggerとpcf8523を流用しながら下のように作成した.
  1. //サンプルのSDにあるDataLoggerを流用して変更
  2. #include < spi.h >
  3. #include < sd .h >
  4. #include "RTClib.h"
  5. RTC_PCF8523 rtc;
  6. const int chipSelect = 10;
  7. //保存ファイル名
  8. const String fileName = "data.csv";
  9. //SDカードへの記録間隔[ms]
  10. const long loggingInterval = 2000;
  11. long nn;
  12. //SDカードとRTC関連の初期化処理.
  13. void setup() {
  14. nn = 0;
  15. // Open serial communications and wait for port to open:
  16. Serial.begin(9600);
  17. while (!Serial) {
  18. ; // wait for serial port to connect. Needed for native USB port only
  19. }
  20. // initialize the SD card
  21. Serial.print("Initializing SD card...");
  22. // make sure that the default chip select pin is set to
  23. // output, even if you don't use it:
  24. // see if the card is present and can be initialized:
  25. if (!SD.begin(chipSelect)) {
  26. Serial.println("Card failed, or not present");
  27. // don't do anything more:
  28. while (1);
  29. }
  30. Serial.println("card initialized.");
  31. //以降はRTClibサンプルのpcf8523を流用
  32. if (! rtc.begin()) {
  33. Serial.println("Couldn't find RTC");
  34. Serial.flush();
  35. while (1) delay(10);
  36. }
  37. //RTCが初期化されていない場合は接続先のPCの時間を設定する
  38. //うまく設定できない場合はrtc.adjust(DateTime(F(__DATE__), F(__TIME__)));をifの外に出す
  39. if (! rtc.initialized() || rtc.lostPower()) {
  40. Serial.println("RTC is NOT initialized, let's set the time!");
  41. // When time needs to be set on a new device, or after a power loss, the
  42. // following line sets the RTC to the date & time this sketch was compiled
  43. rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
  44. // This line sets the RTC with an explicit date & time, for example to set
  45. // January 21, 2014 at 3am you would call:
  46. // rtc.adjust(DateTime(2014, 1, 21, 3, 0, 0));
  47. //
  48. // Note: allow 2 seconds after inserting battery or applying external power
  49. // without battery before calling adjust(). This gives the PCF8523's
  50. // crystal oscillator time to stabilize. If you call adjust() very quickly
  51. // after the RTC is powered, lostPower() may still return true.
  52. }
  53. // When time needs to be re-set on a previously configured device, the
  54. // following line sets the RTC to the date & time this sketch was compiled
  55. // rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
  56. // This line sets the RTC with an explicit date & time, for example to set
  57. // January 21, 2014 at 3am you would call:
  58. // rtc.adjust(DateTime(2014, 1, 21, 3, 0, 0));
  59. // When the RTC was stopped and stays connected to the battery, it has
  60. // to be restarted by clearing the STOP bit. Let's do this to ensure
  61. // the RTC is running.
  62. rtc.start();
  63. }
  64. void loop() {
  65. // make a string for assembling the data to log:
  66. String dataString = "";
  67. float sensor;
  68. float vol;
  69. float cur;
  70. long n;
  71. n = millis();
  72. //delayの値が大きすぎると動かなくなるため,mills()とnn+loggingIntervalの差がloggingInterval以上か判定するif文で代用.
  73. //nnはif文内を実行ごとにloggingIntervalを加算して更新.
  74. if((n - nn) >= loggingInterval){
  75. nn += loggingInterval;
  76. //日付をyyyy/M/d h:m:s形式で取得
  77. DateTime now = rtc.now();
  78. dataString += String(now.year()) + "/" + String(now.month()) + "/" + String(now.day()) + " ";
  79. dataString += String(now.hour()) + ":" + String(now.minute()) + ":" + String(now.second()) + ",";
  80. //3つのCTセンサアンプ基板からA0-A2に入力された電圧を取得し,交流電流の実効値を計算.
  81. for (int analogPin = 0; analogPin < 3; analogPin++) {
  82. sensor = analogRead(analogPin);
  83. vol = sensor*5.0/1024.0;
  84. cur = vol*3000.0/(60.0*4.9);
  85. dataString += String(cur);
  86. if (analogPin < 2) {
  87. dataString += ",";
  88. }
  89. }
  90. //SDカードに記録
  91. // open the file. note that only one file can be open at a time,
  92. // so you have to close this one before opening another.
  93. File dataFile = SD.open(fileName, FILE_WRITE);
  94. // if the file is available, write to it:
  95. if (dataFile) {
  96. dataFile.println(dataString);
  97. dataFile.close();
  98. // print to the serial port too:
  99. Serial.println(dataString);
  100. }
  101. // if the file isn't open, pop up an error:
  102. else {
  103. Serial.println("error opening datalog.txt");
  104. }
  105. }
  106. }

 このスケッチでは2秒ごとにA0-A2の電圧を交流電流値に変換し,測定時刻と共にCSV形式のファイルに保存する.なお,測定間隔をdelay()で制御すると値が大きいとき処理が止まってしまう(40000ms以上で無理だった)ので,millis()とif文で代用した.
 また,setupでシリアル通信の疎通確認をしておりPCとの通信が確立しない限り処理が進まないと思うのだが,USBアダプタから電源を取ってもなぜか動いた.

保存されたファイルをエクセルで開くと以下のようになる(BCD列が電流値)


 










0 件のコメント:

コメントを投稿