SDカードのファイルの操作(Arduino詳解)

Arduino詳解その31です。前回の記事はこちらです。 hirocom777.hatenadiary.org

ファイルを操作する

 前回はSDカード内のファイルやフォルダーの状態を把握しました。今回は、これらファイルやフォルダーを操作してみようと思います。

f:id:HiroCom777:20210220191125j:plain

ファイルを作成する方法は前々回にご紹介しましたが、削除するにはどうしたらいいのでしょうか。また、ファルダーを作ったり、削除したりする方法も説明します。

ファイルの削除

 まずはファイルの削除からです。以下のスケッチをご紹介します。

#include <SPI.h>
#include <SD.h>

void setup() {
  Serial.begin(9600);
  SD.begin(4);
  if (SD.remove("OLD_00.txt")) {
    Serial.println("OLD_00.txt削除しました");
  }
  if (SD.remove("FOLDER/OLD_01.txt")) {
    Serial.println("FOLDER/OLD_01.txt削除しました");
  }
  if (SD.remove("FOLDER/OLD_02.txt")) {
    Serial.println("FOLDER/OLD_02.txt削除しました");
  }
}

void loop() {
}

このスケッチは、SDカード内にある'OLD'で始まる名前の3つのファイルを削除します。削除に成功した場合には、シリアルモニターにメッセージを送ります。SDカード内はあらかじめPCで以下の様にファイルとフォルダーを作成しました。

D:.
│  NEW_00.txt
│  OLD_00.txt
│
└─FOLDER
        NEW_01.txt
        OLD_01.txt
        OLD_02.txt

 上のスケッチを書き込んでから、SDカードをセットしてシリアルモニターを開いてみると以下の様に表示しました。

OLD_00.txt削除しました
FOLDER/OLD_01.txt削除しました
FOLDER/OLD_02.txt削除しました

 SDカードの中をPCで再確認してみると以下の様になっています。

D:.
│  NEW_00.txt
│
└─FOLDER
        NEW_01.txt

 指定されたファイルが削除されています。  今回新しく出てきたものはこちらです。

* SD.remove()

 ファイルを削除します。カッコ内に削除するファイル名を指定します。ファイルを指定する場合、フォルダーを含んで指定することもできます。この場合フォルダー名とファイル名を'/'で区切ります。削除に成功した場合はtrue、失敗した場合はfalseを返します。

フォルダーを作成/削除する

 続いて、フォルダーの作成/削除方法を見てみましょう。以下のスケッチをご紹介します。

#include <SPI.h>
#include <SD.h>

void setup() {
  Serial.begin(9600);
  SD.begin(4);
  //ファルダ―削除
  if (SD.rmdir("OLD_1")) {
    Serial.println("OLD_1を削除しました");
  }
  if (SD.rmdir("OLD_2/OLD_3")) {
    Serial.println("OLD_2/OLD_3を削除しました");
  }
  if (SD.rmdir("OLD_2")) {
    Serial.println("OLD_2を削除しました");
  }
  //ファルダ―作成
  if (SD.mkdir("NEW_1")) {
    Serial.println("NEW_1を作成しました");
  }  
  if (SD.mkdir("NEW_2/NEW_3")) {
    Serial.println("NEW_2とNEW_2/NEW_3を作成しました");
  }
  if (SD.mkdir("NEW_2/NEW_4")) {
    Serial.println("NEW_2/NEW_4を作成しました");
  }
}

void loop() {
}

 こののスケッチは、SDカードの中にある3つのフォルダーを削除して新しいフォルダーを作成します。削除、作成に成功した場合には、シリアルモニターにメッセージを表示します。SDカードの中は、以下の様に設定しました。

D:.
├─OLD_1
└─OLD_2
    └─OLD_3

 上のスケッチを書き込んでから、SDカードをセットしてシリアルモニターを開いてみると以下の様に表示しました。

OLD_1を削除しました
OLD_2/OLD_3を削除しました
OLD_2を削除しました
NEW_1を作成しました
NEW_2とNEW_2/NEW_3を作成しました
NEW_2/NEW_4を作成しました

 SDカードの中をPCで再確認してみると以下の様になっています。

D:.
├─NEW_1
└─NEW_2
    ├─NEW_3
    └─NEW_4

 PCで作成されたファルダーは削除されて、新しいフォルダーができています。

* SD.mkdir()

 フォルダーを作成します。'/'で区切ることで、フォルダー下のフォルダーも作成できます。上のスケッチでは'NEW_2'とその下にある'NEW_2'を同時に作成しています。すでにあるフォルダーの下にフォルダーを作成する場合も指定方法は同じです。作成に成功した場合はtrue、失敗した場合はfalseを返します。

* SD.rmdir()

 フォルダーを削除します。'/'で区切ることで、フォルダー下のフォルダーも削除できます。削除するファルダーは空である必要があります。上の例では'OLD_2'は空ではないので、その下の'OLD_3'を削除した後に削除しています。削除に成功した場合はtrue、失敗した場合はfalseを返します。

SPI通信

 いかがでしょうか。これでSDカードの操作についてはひととおりご紹介しました。これで、データや設定を記録、読み出しする機能をガシェットに搭載できますね。ところで今回使用したSDカード用ライブラリなのですが、SPI通信を使用していると冒頭でご紹介しました。次回はSPI通信についてご説明したいと思います。お楽しみに!!

hirocom777.hatenadiary.org

Arduino UNO入門の連載記事はコチラからどうぞ!!

SDカードのファイル取り扱い(Arduino詳解)

Arduino詳解その30です。前回の記事はこちらです。 hirocom777.hatenadiary.org

ファイルとフォルダー

 前回はSDカードのファイルからデータを読み出してみました。今回は、SDカード上のファイルやフォルダーの取り扱いについて見てみようと思います。

f:id:HiroCom777:20210214150854j:plain

今回は、以下のスケッチをご紹介します。

#include <SPI.h>
#include <SD.h>

File root;

void setup() {
  //初期設定
  Serial.begin(9600);
  SD.begin(4);
  root = SD.open("/");
  //一番上(ルート)の中を表示
  printDirectory(root, 0);
}

void loop() {
}

void printDirectory(File dir, int numTabs) {
  while (true) {
    //フォルダー内の次のファイル
    File entry =  dir.openNextFile();
    if (! entry) {
      break;//オープンする内容が無ければループを抜ける
    }
    for (uint8_t i = 0; i < numTabs; i++) {
      Serial.print('\t');
    }
    //ファイル(フォルダー)名の表示
    Serial.print(entry.name());
    if (entry.isDirectory()) {
      //フォルダーならフォルダーの中を表示
      Serial.println("/");
      printDirectory(entry, numTabs + 1);
    } else {
      //ファイルサイズの表示
      Serial.print("\t\t");
      Serial.println(entry.size(), DEC);
    }
    entry.close();//終わったら呼び出し先に戻る
  }
}

このスケッチは、ArduinoIDEに標準でついてくるスケッチ例⇒SD⇒listfilesからコメント、補助的処理などを省いて簡略化したものです。SDカードのフォルダー、ファイルの配置状況を教えてくれます。さっそく実行してみましょう。今回はSDカードの内容を以下の様にしてみました。(Windows 10のPCで設定しました。)

D:.
│  TEXT_1.txt
│  TEXT_2.txt
│
├─FOLDER_1
│  │  TEXT_3.txt
│  │  TEXT_4.txt
│  │
│  └─FOLDER_2
│          TEXT_6.txt
│          TEXT_5.txt
│
└─FOLDER_3
        TEXT_8.txt
        TEXT_7.txt

実行してみる

SDカードをSDカードシールドに実装した状態で上記のスケッチを書き込み、実行してみてください。シリアルモニターを開いてみると・・・。僕の場合は以下の様に表示されました。

SYSTEM~1/
    WPSETT~1.DAT        12
    INDEXE~1        76
TEXT_1.TXT      0
FOLDER_1/
    FOLDER_2/
        TEXT_6.TXT      0
        TEXT_5.TXT      0
    TEXT_3.TXT      0
    TEXT_4.TXT      0
FOLDER_3/
    TEXT_8.TXT      0
    TEXT_7.TXT      0
TEXT_2.TXT      0

先頭の3行はWindowsが作成したシステムファイルですので無視してください。ちょっと順番が異なりますが、表示は合っていますね。ファイル名の後に書いてある数字はファイルサイズです。今回は空のファイルを配置したので全部ゼロになっています。

スケッチの解説

 それではスケッチの解説です。まずはsetupから。SD.open()は以前出てきました。ファイルをオープンするためのものですが、今回はカッコの中が"/"となっています。これは、ストレージ(今回の場合はSDカード)の一番上のフォルダーをさしています。SD.open()はフォルダーを開くこともできるのです。まずは一番上を開いてrootに割り当てています。その後にあるprintDirectory()って何なんでしょ?

printDirectory()

 printDirectory()は、フォルダーを指定してその中にあるファイル、フォルダーの一覧をシリアルモニターに表示する関数です。表示の対象がファイルの場合、ファイル名の後にファイルのサイズを表示します。表示の対象がフォルダーの場合、フォルダー名を表示した直後に表示フォルダーメイド体を指定して、さらにprintDirectory()を呼び出します。すると、そのファルダー内のファイル、フォルダーの一覧を表示して・・・となって、すべての階層についてファイル、フォルダーの一覧を表示すると言う訳です。いわゆる再帰というやつです。

 呼び出しの際にフォルダーとともに数値が指定されていますが、これは階層の深さによって表示の先頭に指定数のタブを配置することでインデント(段落)表示をしているんですね。表示する内容がなくなると、終了して呼び出し先に戻ります。

 今回新しく出てきたものはこちらです。

* FileObject.openNextFile()

 ファイルオブジェクトで指定したフォルダーに存在する次のファイル(フォルダー)をファイルオブジェクトの形で返します。対象がない場合は、falseを返します。カッコの中の引数は、FILE_READ(読込モード、ファイルの先頭から処理する)、FILE_WRITE(読込、書込モード、ファイルの最後から処理する)の2つを選択できます。省略するとFILE_READとなります。

* FileObject.isDirectory()

 ファイルオブジェクトがフォルダーかどうかを判定します。フォルダーならばtrue、そうでなければfalseを返します。

* FileObject.name()

 ファイルオブジェクトの名前を返します。

* FileObject.size()

 ファイルオブジェクトのサイズを返します。単位はバイトです。

SDカードの様子が分かった!!

 いかがでしたでしょうか?これでSDカードの内容を把握する方法がわかってきたと思います。次回も引き続きファイルやフォルダーの取り扱いについてご紹介しようと思います。お楽しみに!!

hirocom777.hatenadiary.org Arduino UNO入門の連載記事はコチラからどうぞ!!

意図しないデータ変換を防ぐ(CSVファイルをADOで扱う③)


CSVファイルをADOで扱う連載の3回目です。前回の記事はこちらになります

hirocom777.hatenadiary.org

意図しないデータ変換を防ぐ方法

 前回は、ADOを使ってCSVファイルを開くツール(以下CSV_ReaderADO)の概略について説明しました。思っていたよりもシンプルだったんじゃないですか?

f:id:HiroCom777:20210211213852j:plain

今回はCSV_ReaderADO、というよりADOを使ってCSVファイルを開くときに、意図しないデータ変換を防ぐ方法をご紹介したいと思います。CSV_ReaderADOはこちらからどうぞ!!

01_ADOでCSVを扱う.zip - Google ドライブ

ファイルを開いて比べてみる

Excelで開く

 CSVファイルをExcelで開いたときにデータが意図せず変換されてしまう問題は、よく見ることができます。今回は、以下のファイルを用意しました。

03_意図しないデータ変換を防ぐ.csv - Google ドライブ

テキストエディターで開くと内容は以下の様になっています。

ID,START,CLASS,NAME,COMMENT
001,2020-3,1-2,Sato,(1)
002,2021-5,2-5,Yamada,(A)
003,2019-12,13-4,Tanaka,

 まず、このファイルをExcelで開いてみましょう。すると、以下の様になりました。

ID START CLASS NAME COMMENT
1 Mar-20 1月2日 Sato -1
2 May-21 2月5日 Yamada (A)
3 Dec-19 4月13日 Tanaka

 一番上の項目名はいいとして、まず一番左のID列。3桁の数字で表しているはずが先頭のゼロが無くなっています。次のSTART列は開始の時期を年と月で表していると思うのですが、表示形式が変わってしまっています。次のCLASS列はクラス分けを表示したものらしいですが、こちらも日付表記に変換されています。それだけならいいのですが、3行目。'1-2'が'1月2日'、'2-5'が'2月5日'なのに、'13-4'が'4月13日'って何ですか?

 次のNAME列はいいとして、最後のCOMMENT列は1行目の'(1)'が'-1'に変わっています。そういえば、カッコで括った正の数値はマイナスを意味するんでしたっけ。2行目の'(A)'は'(A)'なんだよな。こっちは文字データと認識したみたいです。紛らわしい!!

CSV_ReaderADOで開く

それではCSV_ReaderADOで同じファイルを開いてみましょう。こんな感じになりました。

ID START CLASS NAME COMMENT
1 2020-3 1-2 Sato -1
2 2021-5 2-5 Yamada
3 2019-12 13-4 Tanaka

 あれ?ちょっと様子が違いますね。一番左のID列は同じように処理されていますが、START列とCLASS列は、変換されずに表記されています。問題は最後のCOMMENT列。1行目の'(1)'が'-1'になっているのは同じなのですが、2行目の表記は空っぽです。何なんでしょ?

ADOでCSVファイルを開くときに起こること

 僕なりに考察してみたんですが、ADOでCSVファイルを開くときには以下の処理が行われるのだと思います。

* 読み込んで数値と判断されるデータは数値として変換される。(というか、普通にExcelのセルにマニュアル入力したときの処理。)

  • 日付形式の認識と変換は実施されない。(文字データとして扱う)

  • 異なる形式のデータ(数値と文字列)が列内に存在すると上手く読み込めない。

ということは、数値として変換してほしくないデータは文字列型に固定(ダブルクォーテーションで括る)して、かつ同一列内のデータはデータ形式を統一すれば良いのではありませんか?

CSVファイルの作り方を修正

 それでは、最初のファイルをテキストエディターで修正してみましょう。ID列とCOMMENT列のデータをダブルクォーテーションで括って文字列型に固定します。先頭の項目名も文字列型なので同じようにしておきましょう。以下の様になりました。

"ID","START","CLASS","NAME","COMMENT"
"001",2020-3,1-2,Sato,"(1)"
"002",2021-5,2-5,Yamada,"(A)"
"003",2019-12,13-4,Tanaka,""

 CSV_ReaderADOで開いてみると・・・

ID START CLASS NAME COMMENT
001 2020-3 1-2 Sato (1)
002 2021-5 2-5 Yamada (A)
003 2019-12 3-4 Tanaka

 あ、ちゃんと開けていますね!! ちなみにこのファイルをExcelで開くとこうなりました。

ID START CLASS NAME COMMENT
1 Mar-20 1月2日 Sato -1
2 May-21 2月5日 Yamada (A)
3 Dec-19 3月4日 Tanaka

何も結果は変わっていませんね。

意図しないデータ変換を防ぐファイル形式

 つまり、データ変換を防ぎたい場合はダブルクォーテーションで括って文字列型に固定すればいいということです。極端なことを言えば、全部のデータを文字列型に固定してしまった方がいいと思います。後々予期しないデータが入力、編集されたりすると問題になるからです。今回ご紹介したファイルのデータをすべてダブルクォーテーションで括って文字列型に変換した場合でも、結果は同じことを確認しています。皆さんも試してみてください。

さらなる活用を考える

 いかがでしたでしょうか?ADOを使ってCSVファイルを開くって結構便利じゃないですか?次回はさらなる活用方法について考えてみます。お楽しみに!!

hirocom777.hatenadiary.org

CSVファイルをADOで扱う連載記事はコチラからどうぞ

中身と仕組みの説明(CSVファイルをADOで扱う②)


CSVファイルをADOで扱う連載の2回目です。前回の記事はこちらになります。

hirocom777.hatenadiary.org

CSV_ReaderADOの解説

 前回は、CSVファイルをADOで扱う事ができるという事をお話ししたうえで、CSVファイルを簡単にExcelシート上に表示するツール"CSV_ReaderADO***.xlsm"をご紹介しました。使ってみた感じはいかがでしょうか?今回はこのツールの解説をしたいと思います。ツールは以下からダウンロードして下さい。

01_ADOでCSVを扱う.zip - Google ドライブ

f:id:HiroCom777:20210209203433j:plain

ツールの解説

 前回もお話しした通り、このツールにはあらかじめADO(ADODB.Connectionオブジェクト)が組み込まれています。現在Excelで使用できるADOは複数あるのですが、ここではMicrosoft ActiveX Data Objects 2.8 Libraryを参照設定して使用します。VBEの画面からメニューのツール⇒参照設定で選択して設定してください。また、記述されているコードは起動時の操作画面で表示される”ファイル指定”ボタンのクリック時に実行されるプロシージャが1つ。標準モジュールにファンクションプロシージャが1つだけです。シンプルですね。それではコードの説明です。

”ファイル指定”ボタンのクリック時の処理

 まずは、ボタンのクリック時の処理です。以下にコードを示します。

Private Sub cmdFile_Click()
Dim fileName As String
Dim folderName As String
Dim outputBook As Workbook
Dim workRange As Range
  '読込ファイルの指定
  fileName = Application.GetOpenFilename("CSVファイル,*.csv")
  If fileName = "False" Then Exit Sub
  'データベースフォルダーの指定
  folderName = Left$(fileName, Len(fileName) - Len(Dir$(fileName)))
  '読込内容を新規ブックに出力
  Set outputBook = Workbooks.Add
  outputBook.Activate
  ReadCsvDataBase folderName, "SELECT * FROM [" & Dir$(fileName) & "]", outputBook.ActiveSheet.Range("A1")
  'セルのエラーチェックマーク消去
  For Each workRange In outputBook.ActiveSheet.UsedRange
    workRange.Errors(xlNumberAsText).Ignore = True
  Next
  'シート名を設定
  outputBook.ActiveSheet.Name = Replace$(UCase(Dir$(fileName)), ".CSV", "")
End Sub

 コードの大体の流れは、

  • ファイルオープンのコモンダイアログでCSVファイルを指定。
  • 新規のExcelブックを開いて出力先とする。
  • ReadCsvDataBaseプロシージャを使ってCSVファイルを読み込み表示。
  • 出力先ブックを修正(セルのエラーチェックマーク消去、シート名を拡張子なしのファイル名に設定)

となります。ReadCsvDataBaseプロシージャは標準モジュールに記述されています。これ以外はシンプルな処理ですね。

標準モジュールの処理

 標準モジュールに記述してあるプロシージャも1つだけ。上記で説明したReadCsvDataBaseプロシージャです。ReadCsvDataBaseプロシージャは引数としてCSVファイル、SQLコマンド、表示先のRange(左上原点)を指定すると、指定された条件でCSVファイルの内容を出力先に出力します。以下にコードを示します。

Public Function ReadCsvDataBase(folderName As String, sqlCmd As String, originRange As Range) As Long
On Error GoTo ErrorHandler

  'データベースを接続します
  Dim adoCn As Object
  Set adoCn = CreateObject("ADODB.Connection")
  adoCn.Provider = "Microsoft.ACE.OLEDB.12.0"
  adoCn.Properties("Extended Properties") = "Text;HDR=Yes;FMT=Delimited"
  adoCn.Open = folderName
  'レコードセットを開きます
  Dim adoRs As Object
  Set adoRs = CreateObject("ADODB.Recordset")
  adoRs.CursorLocation = adUseClient
  adoRs.Open sqlCmd, adoCn
  
  'フィールドリストを表示します
  Dim adoField As ADODB.Field
  Dim i As Long: i = 0
  For Each adoField In adoRs.Fields
    originRange.Offset(0, i).Value = adoField.Name
    i = i + 1
  Next
  'データを表示します
  originRange.Offset(1, 0).CopyFromRecordset adoRs
  ReadCsvDataBase = adoRs.RecordCount
  adoRs.Close
  adoCn.Close
ErrorHandler:
  If Err.Number <> 0 Then MsgBox "データベース接続に失敗しました " & Err.Number
End Function

 コードの大体の流れは、

  • CSVファイルにデータベース接続する。
  • 接続したデータベースからSQLコマンドを指定してレコードセットを開く。
  • データベースからフィールド名リストを取得して表示。
  • CSVファイルのデータ内容を取得して表示。
  • 取得したレコード数を返す。

となります。そう、ReadCsvDataBaseはファンクションプロシージャですので値を返します。取得したレコード数を返します。

ADOを使用したデータベースの接続、データアクセスについてなのですが、ここでは詳しく述べません。また、SQLコマンドはデータベースを制御するためのコマンドについても、詳しい説明は避けます。今回は、すべてのデータを取得する設定になっていると思ってください。

ここを詳しく説明していくと、少し話がそれてしまってつまらなくなってしまいそうだからです。両方とも、別に機会を設けて詳しく説明していきたいと思います。

CSVファイルの仕様

いかがでしたでしょうか。使用している技術に関してはともかく、シンプルなコードで構成されていることはわかっていただけたと思います。次回は、意図しないデータ変換を防ぐCSVファイルの仕様についてご説明したいと思います。お楽しみに!!

hirocom777.hatenadiary.org CSVファイルをADOで扱う連載記事はコチラからどうぞ

CSVファイルをADOで扱う(CSVファイルをADOで扱う①)


CSVファイルをADOで扱う

 皆さんこんにちは、HiroCom777です。僕は最近仕事でCSVファイルを使うことが多くなりました。Excelファイルなどと違って機能が少ない分シンプルで取り扱いが楽です。

f:id:HiroCom777:20210207124816j:plain

同時にCSVファイルをめぐっては、色々なトラブルを見ることがあります。代表的なのはExcelで開いたときにデータが意図せず変換されてしまうというもの。対策としてはテキストエディターを使用して開く、またExcelではPower Queryを使って開く等が紹介されていますが、どれもちょっとめんどくさいです。

ADOを使ってみては?

 僕は、以下でExcelファイルをデータベースとして使用する方法の連載記事をブログで書いています。 hirocom777.hatenadiary.org データベースというとOffice製品ではAccessが一般的ですが、実はExcelだけでも簡単なデータベースシステムならば構築することができるのです。SQLを使ったデータの絞り込みなどもできます。これは、ADODB.Connectionオブジェクト(以下、ADO。Excelに標準で搭載されている)をExcelに組み込むことでデータベースファイルにアクセスする機能を組み込むことで実現しています。この連載の一番最後に、ExcelファイルCSVファイルに置き換えた例をご紹介しています。 hirocom777.hatenadiary.org そう、ADOはCSVファイルも操作することができるんです。CSVファイルがデータベースになるんですよ。ADO、ちょっととっつきにくいのですが、この方法でCSVファイルを扱うことが出来れば以下のような効果があります。

高速に開くことができる

データベースですので速いです。大きなファイルでもかなり高速に扱うことができます。

文字化けなどのトラブルを避ける方法がある

CSVファイルをExcelで開いた際に、発生する文字化けのトラブルを回避する方法があります。開いた結果をExcelのシートに表示することもできます。

データベースの機能が使える

これが大きいのですが、SQLを使って複雑なデータ処理ができます。

 どうですか?確かにADOは最初のとっかかりが難しいのですが、それを越えてしまえば便利この上ないものです。この連載では、ExcelとADOを使って簡単に、正確に、そして便利に使う事を追求していきたいと思います。

簡単なツール

 今回は簡単なツール(マクロ入りのExcelファイル)を用意してみました。

01_ADOでCSVを扱う.zip - Google ドライブ

ダウンロードして解凍後に"CSV_ReaderADO***.xlsm"をマクロを有効にした形で開いてみてください。表示画面の”ファイル指定”ボタンを押すとファイル選択のダイアログが表示されますので任意のCSVファイルを選択してみましょう。

どうでしょうか、CSVファイルの内容が表示された新しいExcelファイルが表示されたと思います。意外と簡単に開けましたよね。このツールにはあらかじめADOが組み込まれています。

さて、開いた結果をよく見ていただきたいです。勿論開いたCSVファイルにもよるのですが、一部のデータは表記が変換されてしまっていると思います。実はCSVファイルの作り方次第で、この文字化けも防げるんです。

ツールの解説とポイント

と、言う訳で次回はこのツールの内容を解説しようと思います。お楽しみに!!

hirocom777.hatenadiary.org

CSVファイルをADOで扱う連載記事はコチラからどうぞ

SDカードからデータ読み出し(Arduino詳解)

Arduino詳解その29です。前回の記事はこちらです。
hirocom777.hatenadiary.org

ファイルに書き込む

 前回はSDカードにファイルを作って書き込んでみました。

f:id:HiroCom777:20210130174447j:plain
今回は、ファイルからデータを読み出してみたいと思います。以下のスケッチを用意しました。

#include <SPI.h>
#include <SD.h>

File myFile;
const int chipSelect = 4;

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

  myFile = SD.open("test.txt", FILE_READ);
  Serial.println("test.txt:");
  while (myFile.available()) {
    Serial.write(myFile.read());
  }
  myFile.close();
}

void loop() {
}

動かしてみる

 それでは動かしてみましょう。SDカードにファイルtext.txtを作って、何かテキストを入力してください。PCでもいいですし、前回のスケッチで作っ手もいいです。SDカードをSDカードシールドに実装した状態で上記のスケッチを書き込み、実行してみてください。シリアルモニターを開いてみると・・・。シリアルモニターには、ファイルの内容が表示されていると思います。

f:id:HiroCom777:20210130174652j:plain

スケッチの解説

 それではスケッチの解説です。今回も使用するライブラリも、前回と一緒です。今回はsetupだけ。以下が新しくでてきましたね。

FileObject.available()
 ファイルから読み取り可能なバイトがあるかどうかを確認します。読み取り可能なデータのバイト数を返します。

FileObject.read()
 ファイルからデータを1バイト読み込んで返します。データを読み込めない場合は-1を返します。

 つまり、今回ご紹介したスケッチは開いたファイルを確認ながら最後までデータを読み込んでシリアルモニターに表示するものです。簡単ですね!!

1行ずつ読みたい

 さて、上に紹介した例では1バイトずつデータを読み込んでいます。でも、前回ご紹介したデータの書込みでは文字データを1行ずつ書き込んでいました。1行ずつ読み込むにはどうしたらいいのでしょうか?以下のスケッチ例をご紹介します。

#include <SPI.h>
#include <SD.h>

File myFile;
const int chipSelect = 4;

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

  myFile = SD.open("test.txt", FILE_READ);
  myFile.setTimeout(100);

  Serial.println("test.txt:");
  while (myFile.available()) {
    for (int i = 1; myFile.available(); i++) {
      Serial.print(String(i) + ": ");
      Serial.println(myFile.readStringUntil('\n'));
    }
  }
  myFile.close();
}

void loop() {
}

 動かしてみると・・・、今度は先頭に番号がついて表示されましたね。1行ずつ読み込んでいる証拠です。(余談ですが、こんな感じのfor文はじめて書きました)

f:id:HiroCom777:20210130174937j:plain
 新しく出てきたものは以下になります。

FileObject.setTimeout()
 データ読込みの待ち時間を設定します。待ち時間はカッコ内の引数としてミリ秒単位で設定します(デフォルトでは1000ミリ秒=1秒)。

FileObject.readStringUntil()
 ファイルからデータを連続して読み込みます。引数として終了文字が指定できます。終了文字が検出されるかタイムアウトになると読込を終了します。今回は改行コード('/n’)を指定したため1行ごとに読込を終了しています。

 今回の場合タイムアウトを設定しなくても問題ないのですが、ファイルの最後が終了文字で終わらない場合に終了まで1秒時間がかかってしまいます。0.1秒程度に設定しておくのが無難でしょう。

数値を読み出したいときは?

 SDカードに数値を書き込むと、文字列に変換されて書き込みます。したがって、読み出すときには文字として読み出して数値に変換するってなるのが普通なのですが、数値として読めると便利ですよね。以下のスケッチを作ってみました。

#include <SPI.h>
#include <SD.h>

File myFile;
const int chipSelect = 4;

int dataInt;
float dataFloat;

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

  myFile = SD.open("test2.txt", FILE_WRITE); 
  myFile.println(7);
  myFile.println(1.23);
  myFile.close();

  myFile = SD.open("test2.txt", FILE_READ);
  myFile.setTimeout(100);
  dataInt = myFile.parseInt();
  dataFloat = myFile.parseFloat();
  Serial.println(dataInt + 1);
  Serial.println(dataFloat * 2);
  myFile.close();

  SD.remove("test2.txt"); 
}

void loop() {
}

 実行してシリアルモニターを見てみると・・・整数値8と浮動小数点値2.46が表示されます。このスケッチの動きは以下の様になります。
 

f:id:HiroCom777:20210130175155j:plain
 -新しく作成したファイルtext2.txtに整数値7と浮動小数点値1.23を書き込む
 -text2txtをクローズする
 -text2.txtを読み込みモードで開く
 -整数型の変数dataIntと、 浮動小数点型の変数dataFloatに読み込んだ値をセットする
 -変数dataIntに1を足した値(7 + 1 = 8)をシリアルモニターに返す
 -変数dataFloatに2を掛けた値(1.23 * 2 = 2.46)をシリアルモニターに返す
 -text2txtをクローズして削除する

計算して正しい値を表示できているので、数値として読み込めていることがわかると思います。新しく出てきたものはこちらです。

FileObject.parseInt()
 ファイルの読込み位置から最初の有効な整数値を返します。それ以前にある数値やマイナス記号でないデータは読み飛ばしてしまいます。数値が読み込めない場合やタイムアウトの場合は0を返して終了します。

FileObject.parseFloat()
 ファイルの読込み位置から最初の有効な浮動小数点値を返します。それ以前にある数値やマイナス記号などは読み飛ばしてしまいます。数値が読み込めない場合やタイムアウトの場合は0を返して終了します。

最後のSD.remove()はファイルを削除するのですが、こちらは次回説明したいと思います。

お次はファイルとフォルダーの取り扱い

 いかがでしたでしょうか?これでデータ読み書きの基本が出来たと思います。次回は、ファイルとフォルダーの取り扱いについてご紹介しようと思います。お楽しみに!!
hirocom777.hatenadiary.org



Arduino UNO入門の連載記事はコチラからどうぞ!!

SDカードへのデータ書き込み(Arduino詳解)

Arduino詳解その28です。前回の記事はこちらです。

hirocom777.hatenadiary.org

ファイルを作って書き込む

 前回はSDカードそのものについてご紹介しました。特徴や弱点なんかもわかってきました。

f:id:HiroCom777:20210125230800j:plain
今回は、いよいよArduinoUNOとSDカードシールドを使ってSDカードにアクセスしようと思います。まずはファイルを作って書き込んでみましょう。今回は以下のスケッチを用意しました。

#include <SPI.h>
#include <SD.h>

File myFile;
const int chipSelect = 4;

void setup() {
  Serial.begin(9600);
  SD.begin(chipSelect);
  myFile = SD.open("test.txt", FILE_WRITE);
  Serial.println("open test.txt");
}

void loop() {
  if (Serial.available()) {
    String data = Serial.readString();
    if (data != "end") {
      myFile.println(data);
      Serial.println("write data:" + data);
    } else {
      myFile.close();
      Serial.println("close test.txt");
      while (1);
    }
  }
}

動かす前の準備

 このスケッチは、ArduinoUNOに(SDカードシールド経由で)取り付けられているSDカードにファイルを作成してデータを書き込みます。最初に使用する前にSDカードが初期化(フォーマット)されている必要があります。基本的に購入した状態で初期化されているので、この作業は不要です。他で使いまわしたりして、中身が壊れているSDカードなどは初期化が必要になります。初期化はPCなどを使用して実行するのですが、あまり頻繁に実施するとSDカード自体の寿命を縮めますので注意してください。

動かしてみる

 それでは動かしてみましょう。SDカードをSDカードシールドに実装した状態で上記のスケッチを書き込んでください。書き込みが終わったら、シリアルモニターを開きましょう。シリアルモニターには、

open test.txt

と、表示されました。これで、text.txtファイルがSDカードに作成されました。次に、シリアルモニターの送信欄に何か文字を入力して送信してみてください。すると・・・

write data: + 入力文字

と、出たと思います。入力した文字をファイルに書き込んでいます。これを数回繰り返した後に、送信欄に"end"と入力して送信してください。今度は・・・

close test.txt

と、なりました。これでSDカード内のtext.txtファイル内に入力された内容が保存されました。PCに取り付けて内容を確認してみるとtext.txtファイルが確認できると思います。

スケッチの解説

 それではスケッチの解説です。今回も使用するライブラリは、最初に紹介したスケッチと一緒です。今回はファイルを操作しますので、以下が新しくでてきましたね

・File
 Fileは操作するファイルオブジェクトを作成します。今回のスケッチではmyFileをファイルオブジェクトとして設定しています。

 お次はsetupです。

・SD.begin()
 SDカードを初期化します。かっこの中はSDカードを選択するために使用するArduinoUNOの信号ピンです。使用するシールドによって異なることが有るので注意してください。また、初期化に成功するとtrue、失敗するとfalseを返します。

・SD.open()
 SDカード上のファイルを開きます。指定して名前のファイルが無い場合には、作成します。以下の様に記述します。
  SD.open(ファイル名, モード)
 ファイル名は8.3形式(ファイル名8文字以下、拡張子3文字)です。モードは、FILE_READ(読込モード、ファイルの先頭から処理する)、FILE_WRITE(読込、書込モード、ファイルの最後から処理する)の二つです。省略するとFILE_READとなります。
 処理に成功するとファイルオブジェクトを返します。失敗した場合はfalseを返します。

 loopでは、以下が新しく出てきましたね

FileObject.println()
 カッコの中のデータを最後に改行コードを付けて書き込みます。ファイルオブジェクト.printl()とすると、改行コードなしで書き込みます。また、数値は文字データに変換されて書き込みます。

FileObject.close()
 書き込んだ内容をSDカードに保存してファイルを閉じます。print(),println()で書き込んでもclose()を実施しないと保存されません。

お次は読み出し

 いかがでしたでしょうか?ファイルの書込みの基本は分かっていただけたと思います。次回は読み出しについて説明します。お楽しみに!!
hirocom777.hatenadiary.org


Arduino UNO入門の連載記事はコチラからどうぞ!!