71歳の... シリアル通信を介し、SD内のファイルを指定・操作をしてみた

　これまでのところ`SDcard`内のファイル操作といえば、あらかじめArduinoスケッチに書込んだものだけでした。今回は実行時において「動的」なファイル操作をしてみました。
　多くのサイトを参考にしましたが、いくつか挙げます。

https://docs.arduino.cc/libraries/sd/

https://github.com/espressif/arduino-esp32/blob/master/libraries/SD/examples/SD_Test/SD_Test.ino

https://hirocom777.hatenadiary.org/entry/2021/02/14/154415

https://docs.sunfounder.com/projects/esp32-starter-kit/ja/latest/arduino/basic_projects/ar_sd_read_write.html

　`Atduino IDE` 標準のシリアルモニターを介して行います（少なくとも`Tera Term`ではうまく動作しませんでした）。マイコン(`ESP32-S3`)に、

    コマンド・ファイル名を半角スペースで区切った文字列 "XX filename"
を送り、microSD内のそのファイルに対して次のような操作ができます。

| 操作|入力する文字列|
|-----|----|
|Directory／fileの一覧を表示|"ls"|
|指定Directory／fileの存在を調べる。もし、ディレクトリなら内部を調べる。|"ls dir/filename"|
|ファイルの作成|"cr filename"|
|ファイルに書込む|"wr filename"|
|ファイルの中身を表示させる|"re filename"|
|ファイルを削除する|"RM filename"|

その都度、「ファイル名」を指定して行うというのが今回のポイントです。
　環境／構成は以下です。

    １．Windows 11 Pro
    ２．Arduino IDE 2.3.6
        ただし、ボードマネージャーは、esp32 by Espressif Systems 3.0.0
    ３．ESP32-S3-DevKitC-1
        Arduino IDE 上では、ESP32S3 Dev Module
    ４．SPI接続 microSD スロットモジュール

ESP32-S3とmicroSDスロットのSPI接続は次のようにしました。

|ESP32-S3| microSDスロット|
|--------|---------------|
|10|CS|
|11|MOSI|
|12|CLK|
|13|MISO|
|3V3|3V3|
|GND|GND|

スケッチする上で重要であったこと、工夫したことを次にまとめます。

    １．SDのルートにあるファイルには、"/test.txt"のように頭に"/"が必要
    ２．SDカードの情報表示には、uint8_t cardType = SD.cardType()が必要
    ３．シリアル通信で文字列を受信させるには、
         while (Serial.available() == 0) { ; }  // １行入力(\n\r)まで待つ
         String line = Serial.readString();
        または、
         if (Serial.available()) {
           String data = Serial.readString();
           ...
         }
    ４．文字列比較で strcmp がうまく使えなかったので代わりに次のようにした
         if (line.length() > 3 && line.substring(0, 2) == "ls") {
        　 ...
         }
　スケッチ本体です。

```c:S3_SD_Console.ino
#include <string>
#include "SD.h"

// SD Card
#define SD_CS 10  // SD Card chip select
#define SPI_MOSI 11
#define SPI_MISO 13
#define SPI_SCK 12

const String _rt = "/";

void setup() {
  Serial.begin(115200);
  Serial.println(" ");
  Serial.println("Serial Console start...");
  SD.begin(SD_CS, SPI, 24000000, "/sd");

  uint8_t cardType = SD.cardType();
  Serial.println(" ");
  Serial.print("- SD Card Type: ");
  if (cardType == CARD_MMC) {
    Serial.println("MMC");
  } else if (cardType == CARD_SD) {
    Serial.println("SDSC");
  } else if (cardType == CARD_SDHC) {
    Serial.println("SDHC");
  } else {
    Serial.println("UNKNOWN");
  }

  uint64_t cardSize = SD.cardSize() / (1024 * 1024);
  Serial.printf("- SD Card Size: %lluMB\n", cardSize);

  File root = SD.open(_rt);
  // ルート内のみ表示
  Serial.println(" ");
  Serial.println("Files found in root directory -------------");  // Print the list of files found in the root directory
  while (File file = root.openNextFile()) {                       // Loop through all the files in the root directory
    Serial.print("  ");
    Serial.print(file.name());  // Print the filename
    Serial.print("\t");
    Serial.println(file.size());  // Print the filesize
    file.close();                 // Close the file
  }
  Serial.println("--- Listing root Dirs/Files done. ---------");
}

void loop() {
  while (Serial.available() == 0) { ; }  // １行入力(\n\r)まで待つ
  String line = Serial.readString();
  line.trim();

  if (line.length() == 2 && line.substring(0, 2) == "ls") {
    Serial.println("Listing all Dirs/Files. ----");
    File root = SD.open(_rt);
    printDirectory(root, 0);
    Serial.println(" --- Listing up done. ------");
  }

  if (line.length() > 3 && line.substring(0, 2) == "ls") {
    String fn = _rt + line.substring(3);
    if (SD.exists(fn)) {
      Serial.printf("%s exists.\n\r", fn);
      File fp = SD.open(fn);
      if (fp.isDirectory()) {
        while (File file = fp.openNextFile()) {  // Loop through all the files in the root directory
          Serial.print("  ");
          Serial.print(file.name());  // Print the filename
          if (file.isDirectory()) Serial.print("/");
          Serial.print("\t");
          Serial.println(file.size());  // Print the filesize
          file.close();                 // Close the file
        }
        Serial.println("** Listing Done **");
      }
    } else {
      Serial.printf("%s doesn't exist.\n\r", fn);
    }
  }

  if (line.length() > 3 && line.substring(0, 2) == "RM") {
    String fn = _rt + line.substring(3);
    if (SD.exists(fn)) {
      SD.remove(fn);
      Serial.printf("Removed file : %s.\n\r", fn);
    } else {
      Serial.printf("%s doesn't exist.\n\r", fn);
    }
  }

  if (line.length() > 3 && line.substring(0, 2) == "cr") {
    String fn = _rt + line.substring(3);
    File fc = SD.open(fn, FILE_WRITE);
    if (fc) {
      fc.close();
      Serial.printf("Created file : %s.\n\r", fn);
    }
  }

  if (line.length() > 3 && line.substring(0, 2) == "wr") {
    String fn = _rt + line.substring(3);
    Serial.printf("Write file : %s.", fn);
    File fw = SD.open(fn, FILE_APPEND);
    if (fw) {
      Serial.println("file opened. Type lines or EOF.");
      while (1) {
        if (Serial.available()) {
          String data = Serial.readString();
          if (data.substring(0, 3) == "EOF") {
            fw.close();
            break;
          }
          fw.println(data);  // 1行ごと書き込む
          Serial.println("Inputed a line >" + data);
        }
      }
      Serial.println("--- EOF Ended. file closed.");
    } else {
      Serial.println("file open refused.");
    }
  }

  if (line.substring(0, 2) == "re") {
    String fn = _rt + line.substring(3);
    if (SD.exists(fn)) {
      File fr = SD.open(fn);
      if (fr) {
        Serial.printf("\n\rReading %s begin ...\n\r", fn);
        // read from the file until there's nothing else in it:
        while (fr.available()) { Serial.write(fr.read()); }
        fr.close();  // close the file:
        Serial.printf("\n\r--- Reading %s done.\n\r", fn);
      } else {  // if the file didn't open, print an error:
        Serial.printf("\n\--- Reading %s error.\n\r", fn);
      }
    } else {
      Serial.printf("%s doesn't exist.\n\r", fn);
    }
  }
}

void printDirectory(File dir, int numTabs) {
  while (true) {
    File entry = dir.openNextFile();
    if (!entry) { break; }  // no more files

    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 {  // files have sizes, directories do not
      Serial.print("\t\t");
      Serial.println(entry.size(), DEC);
    }
    entry.close();
  }
}
```
　マイコンを始めた2013年頃、`mbed LPC1768`でSDカードのサンプルプログラムを動かし遊んでました。よくわからんけど、ただただ凄いなあと。あの頃から少し進歩でき嬉しいです。いろいろな皆様のおかげですね。最後までご覧いただき有難うございました。