ESP32 ( ESP-WROOM-32 ) で micro SDHC メモリカードを使う場合の注意点

ESP32 ( ESP-WROOM-32 )

こんばんは。

今回は、ESP32 ( ESP-WROOM-32 ) で micro SDHC  メモリカードを使う場合の注意点を自分なりにまとめてみました。

Twitter上 で micro SDHC カードが読み込めないというツイートを見て、いろいろ調べたり、実験したりしてみました。
以下、Arduino IDE でコンパイルした場合で説明します。

私はアマチュアなので、SD カード規格にはあまり詳しくありません。
ですから、誤っていた場合はコメント欄等でご連絡いただけると助かります。

スポンサーリンク

SD ( SDHC ) カード規格について

SDHC ( SD ) カード規格についてはいろいろ調べてみたのですが、詳細な技術仕様書はなかなか拝見できませんでした。
それは、どうやら、SDアソシエーションという団体に入会しないと、詳細な技術仕様書が手に入らないようです。
その件については、以下のページを参照してください。

https://www.sdcard.org/jp/developers/overview/

要するに、企業がその団体に会費を払って入会しているみたいですね。
技術仕様書には恐らく、接続ピンをプルアップするべきなどと書いてあるのでしょうか・・・。

ザッと私なりに調べたところによると、SPIモードのような低速のデータ通信はライセンスが不要っぽく、HD動画などの高速大容量通信には、ライセンス料を払わないといけないみたいです。
(SPIモードでもライセンス必要かもしれません。現在調査中です。)

そもそも、マイコンの SD , SDHC ホストコントローラーを使って、SDカードスロット付きデバイスを販売する場合は、ライセンス料が必要みたいです。
販売に関しては、SPIモードも対象かどうかは良く分かりません。
MMC カードの場合はライセンス不要という情報もありました。
このライセンス料はかなり高額のようで、独りメーカーで販売しようとするにはハードルが高すぎますね。

MMCカードよりも SD カードの方が高速で、SPIモードのクロック最高周波数の理論値は 24MHz 程度らしいです。
でも、最近の micro SDHC カードでは、それ以上でも読み込める場合があります。
SDアソシエーション会員になれば、更に高速の通信が使えるみたいです。

現在の電子工作では、HD動画を再生したいということも有るかも知れないので、SPIモードでは限界を感じることもありますね。
高速化時代の電子工作家にとっては、これはなかなか厳しい現実ですね。

使用したデバイス等

では、実際に micro SDHC カードの読み書き実験に使ったものを紹介します。

ESP32-DevKitC

日本の電波法をクリアした技適認証済み Wi-Fi & Bluetooth マイコン ESP-WROOM-32 ( ESP32 )を搭載した開発ボードです。
現在、秋月電子通商さんや Amazon.co.jp で販売されています。

SparkFun micro SDカードスロット

micro SDカードスロットは、何も付属品が無いシンプルな物を使いました。

SparkFun マイクロSDカードスロット・ピッチ変換基板

ブレッドボード SAD-101 ( サンハヤト )

サンハヤトさんのこのブレッドボードは使用できるピン列が多く、複数連結できるのでお勧めです。

サンハヤト SAD-101 ニューブレッドボード
サンハヤト
¥529(2024/03/28 14:27時点)

その他、ジャンパーワイヤー等

動作確認済み micro SDHC カード

以下の micro SDHC カードは動作確認しました。
SD カードや SDXC カードは試していません。
他の種類や他メーカーのカードでは動作しない場合がありますのでご注意ください。

因みに、常識かと思いますが、静電気で一発で壊れますので、素手で触るときには十分ご注意ください。

Transcend micro SDHC カード 32GB Class10 UHS-I対応

旧モデル Transcend microSDHCカード 32GB Class10 UHS-I対応 TS32GUSDU1
トランセンドジャパン
¥1,390(2024/03/28 21:39時点)

Twitter の@robo8080さんによれば、これの 16GB は動作しなかったという情報がありました。
私は16GB は持ち合わせていないので、試しに 32GB を購入して確かめてみたら、特に問題無く動作しました。
これにはデータ転送中のエラーを修正する EEC 機能というものがあるらしく、それが影響するのかとも思いましたが、今のところ問題無しです。

Transcend microSDHCカード 8GB Class10

Transcend microSDHCカード 8GB Class10 UHS-I対応 Nintendo Switch 動作確認済 TS8GUSDU1
トランセンドジャパン
¥1,080(2024/03/28 11:30時点)

TOSHIBA microSDHCカード 8GB Class10 UHS-I対応

これは、SDカードアダプターがありませんのでご注意ください。

TDK microSDHCカード 4GB Class4 SDアダプター付き

SDHC カードの初期化(フォーマット)

現在販売している micro SDHC カードは殆ど FAT32 で初期化(フォーマット)されていると思いますので、そのままパソコンでファイル転送できると思います。
初期化されていない場合は、以下の記事を参照してください。

micro SD 、micro SDHC カードの初期化(フォーマット)方法

ただし、SDカードやSDXC カードはフォーマット方法が異なります。
SDカードは最大2GB までで、FAT16 または FAT12 です。
SDXC カードは32GB以上の大容量カードで、exFAT というシステムです。
当方では、SDHC カードしか試していませんので、ご了承ください。

カードの種類による容量の違いや、ファイルシステムの違いについては以下のサイトをご参照ください。

https://www.sdcard.org/jp/developers/overview/capacity/index.html

ESP32-DevKitC との接続

いろいろと調べた結果、SDカード規格によると、データ線は10kΩ前後でプルアップしなければいけないそうです。
ただ、その中でも MOSI ( Master Out Slave In )ラインはプルアップ抵抗が必須という情報がありました。

確かに、4GB の micro SDHC カードで、スピードクラスが class 4 の場合はプルアップしなくても読み込めましたが、8GB以上の class 10 のmicro SDHC カードは読み込めませんでした。
ということで、最低 MOSI ラインにはプルアップが必要だということが分かりました。

その他の、SLCK、MISO、CS ラインに必要かどうかはハッキリしたことは分かりません。
ただ、秋月電子通商さんで販売している以下の製品
http://akizukidenshi.com/catalog/g/gK-05818/
でも、電源ライン以外は全てプルアップしていますので、おそらく必要なのかと思われます。
MISO と MOSI だけで良いという情報もありました。

結局のところ、SDカード仕様書がネットで見当たらないので分かりませんが、エラーになった場合はデータ線全てをプルアップしてみてはいかがでしょうか。
私の環境の場合はMISO だけでOKでした。

サンプルスケッチの入力

では、まず、Arduino core for the ESP32 をインストールしていない方は以下のページを参照してください。

Arduino core for the ESP32 のインストール方法

また、重要なのは、今まで Arduino core for the ESP32 を使っていた方は、最新版をインストールし直してみて下さい。
古いライブラリでは micro SDHC カードを読み込まない場合があります

では、実際に Arduino IDE にスケッチを書きこんで、読み書きできるか試してみます。
サンプルスケッチで確認しても良いのですが、ここでは、同時に開くことが出来るファイル数を確認するためのスケッチを入力してみたいと思います。

【ソースコード】 (※無保証 ※PCの場合、ダブルクリックすればコード全体を選択できます)

#include <SD.h>

const uint8_t cs_SD = 5;

const char* fname1 ="/test1.txt";
const char* fname2 ="/test2.txt";
const char* fname3 ="/test3.txt";
const char* fname4 ="/test4.txt";
const char* fname5 ="/test5.txt";
const char* fname6 ="/test6.txt";
const char* fname7 ="/test7.txt";
const char* fname8 ="/test8.txt";
const char* fname9 ="/test9.txt";
const char* fname10 ="/test10.txt";

File f1, f2, f3, f4, f5, f6, f7, f8, f9, f10;

void setup() {
  Serial.begin(115200);
  SD.begin(cs_SD, SPI, 24000000, "/sd");

  Serial.println("\r\n--------正常な書き込み(ファイルは4つまで同時オープン)");
  f1 = SD.open(fname1, FILE_WRITE);
  f2 = SD.open(fname2, FILE_WRITE);
  f3 = SD.open(fname3, FILE_WRITE);
  f4 = SD.open(fname4, FILE_WRITE);
  serial_print_Fwrite(f1, 1, "このファイルはテスト1\r\n");
  serial_print_Fwrite(f2, 2, "このファイルはテスト2\r\n");
  serial_print_Fwrite(f3, 3, "このファイルはテスト3\r\n");
  serial_print_Fwrite(f4, 4, "このファイルはテスト4\r\n");
  f1.close(); f2.close(); f3.close(); f4.close();
  delay(1);
  
  f5 = SD.open(fname5, FILE_WRITE);
  f6 = SD.open(fname6, FILE_WRITE);
  f7 = SD.open(fname7, FILE_WRITE);
  f8 = SD.open(fname8, FILE_WRITE);
  serial_print_Fwrite(f5, 5, "このファイルはテスト5\r\n");
  serial_print_Fwrite(f6, 6, "このファイルはテスト6\r\n");
  serial_print_Fwrite(f7, 7, "このファイルはテスト7\r\n");
  serial_print_Fwrite(f8, 8, "このファイルはテスト8\r\n");
  f5.close(); f6.close(); f7.close(); f8.close();
  delay(1);
  
  f9 = SD.open(fname9, FILE_WRITE);
  f10 = SD.open(fname10, FILE_WRITE);
  serial_print_Fwrite(f9, 9, "このファイルはテスト9\r\n");
  serial_print_Fwrite(f10, 10, "このファイルはテスト10\r\n");
  f9.close();  f10.close();
  Serial.println("\r\n--------10ファイル読み込み同時オープン");
  
   Serial.println("");
  f1 = SD.open(fname1, FILE_READ);
  f2 = SD.open(fname2, FILE_READ);
  f3 = SD.open(fname3, FILE_READ);
  f4 = SD.open(fname4, FILE_READ);
  f5 = SD.open(fname5, FILE_READ);
  f6 = SD.open(fname6, FILE_READ);
  f7 = SD.open(fname7, FILE_READ);
  f8 = SD.open(fname8, FILE_READ);
  f9 = SD.open(fname9, FILE_READ);
  f10 = SD.open(fname10, FILE_READ);

  serial_print_Fread(f1, 1);
  serial_print_Fread(f2, 2);
  serial_print_Fread(f3, 3);
  serial_print_Fread(f4, 4);
  serial_print_Fread(f5, 5);
  serial_print_Fread(f6, 6);
  serial_print_Fread(f7, 7);
  serial_print_Fread(f8, 8);
  serial_print_Fread(f9, 9);
  serial_print_Fread(f10, 10);

  f1.close(); f2.close(); f3.close(); f4.close();  f5.close();
  f6.close(); f7.close(); f8.close(); f9.close();  f10.close();
  
  Serial.println("\r\n---------ファイルを4つ毎に読み込む");
  f1 = SD.open(fname1, FILE_READ);
  f2 = SD.open(fname2, FILE_READ);
  f3 = SD.open(fname3, FILE_READ);
  f4 = SD.open(fname4, FILE_READ);
  serial_print_Fread(f1, 1);
  serial_print_Fread(f2, 2);
  serial_print_Fread(f3, 3);
  serial_print_Fread(f4, 4);  
  f1.close(); f2.close(); f3.close(); f4.close();
  delay(1);
  
  f5 = SD.open(fname5, FILE_READ);
  f6 = SD.open(fname6, FILE_READ);
  f7 = SD.open(fname7, FILE_READ);
  f8 = SD.open(fname8, FILE_READ);
  serial_print_Fread(f5, 5);
  serial_print_Fread(f6, 6);
  serial_print_Fread(f7, 7);
  serial_print_Fread(f8, 8);
  f5.close(); f6.close(); f7.close(); f8.close();
  delay(1);

  f9 = SD.open(fname9, FILE_READ);
  f10 = SD.open(fname10, FILE_READ);  
  serial_print_Fread(f9, 9);
  serial_print_Fread(f10, 10);
  f9.close(); f10.close();
/*  
  Serial.println("\r\n------------ファイル削除");
  Delete_File(fname1);
  Delete_File(fname2);
  Delete_File(fname3);
  Delete_File(fname4);
  Delete_File(fname5);
  Delete_File(fname6);
  Delete_File(fname7);
  Delete_File(fname8);
  Delete_File(fname9);
  Delete_File(fname10);
*/ 
}

void serial_print_Fwrite(File ff, byte num, const char *message){
  if(ff.print(message)){
    Serial.printf("f%d Message wrote. Good!\r\n", num);
  }else{
    Serial.printf("f%d write failed\r\n", num);
  }
}

void serial_print_Fread(File ff, byte num){
  size_t len = ff.size();
  uint8_t buf[256];
  
  if (ff.read(buf, len)) {
    Serial.printf("f%d File found OK!!\r\n", num);
    for(int i=0; i<len; i++){
      Serial.write(buf[i]);
    }
  }else{
    Serial.printf("f%d File not found\r\n", num);
  }
}

void Delete_File(const char *fname){
  if(SD.remove(fname)){
      Serial.printf("%s File deleted\r\n", fname);
  } else {
      Serial.printf("%s Delete failed\r\n", fname);
  }
}

void loop() {
}

【解説】

◆1行目:
Arduino core for the ESP32 標準の SD ライブラリをインクルードします。

◆3行目:
micro SDHC カードの CS ( Chip Select )ピンを定義します。
GPIO #5 に接続しています。

◆5-14行:
micro SDHC カードに出力するファイル名を定義します。
ルートに書き込む場合は必ずスラッシュを入れてください。

◆16行:
ファイルハンドル名を定義

◆20行:
単なる、SD.begin(); でも良いのですが、その場合、周波数は1MHz で固定されてしまいます。
上で述べたように、SDカードのSPIモード通信の最大周波数を24MHz と変更する場合にはこうします。
CSピンは、”SS” としても良いです。
“SS” とした場合は、CSピンは自動的に GPIO #5 にアサインされます。

◆23-49行:
まず、最初に micro SDHC カードにファイルを書き込まなければいけないので、正常に書き込みできる方法にします。
Arduino core for the ESP32 ライブラリを使った場合、同時に開けるファイル数は4つまででしたので、この様に4つ書き込んだ後は必ずクローズしておきます。
クローズしないと次のファイルを開けません。

27-30行では、自作した関数121-127行で定義しています。
要するに、ファイル書き込めたかどうかをシリアルモニターに出力するものです。

◆53-76行:
実験のために、10個のファイルを同時に開いて読み込んでみます。
結果は後述します。

◆79-105行:
正常にファイルを読み込むためにはこのように記述します。
要するに、ファイルは4つまでしか同時に開けません。
書込みも同様です。

◆106-118行:
ファイルを削除したい場合は、このコメントを解除してください。

◆121-127行:
micro SDHC にファイルを書き込めたかどうかをシリアルモニターに出力するものです。

◆129-141行:
256バイトまでのファイルを読み込む関数です。
Arduino IDE 1.8.2 からUTF-8コードの日本語文字列をシリアルモニターに直接出力することができるようになりました。

◆143-149行:
micro SDHC カード内の指定ファイルを削除する関数です。

同時に開けるファイル数のチェック

では、スケッチをコンパイル書き込み実行させてみてください。

すると、シリアルモニターには以下のように表示されると思います。
因みに、シリアルモニターの転送レートは 115200 bps にしておいてください。

いかがでしょうか。

私は自作ライブラリ等で、micro SDHC カードからファイルを複数同時に開くことがあるのですが、4つまでしか開けないことを知らずに、延々とエラーに悩まされていたことがありました。
こうすると、Arduino core for the ESP32 標準ライブラリでは、同時に4つまでしか開けないことが分かりますね。
要注意です。
これについては、おそらく SD.h ライブラリを修正すれば開ける数は変えられると思いますが、あくまで標準使用ということで、そこは手を付けないでおきます。

因みに、SD.begin の周波数を 40MHz にしても、上記の動作確認済みカードの場合は読み込めました。
ただ、これは動作保証外だと思われます。

現在の Arduino core for the ESP32 では、ESP32 のCPU周波数が最大80MHz ですので、理論値では、SPI周波数は40MHz までクロックを出力できます。
しかし、いずれにしても安全動作はその4分の1周の20 MHz を最大周波数としておいた方が良いような気がします。
(自作ライブラリでは 40MHz としていますが・・・)
あとは、個人の判断でいろいろと試してみて下さい。

まとめ

まとめると、こんな感じになるでしょうか。

◆micro SDHC カードには相性がある。

◆ESP32 ( ESP-WROOM-32 )でmicro SDHC カードを使う場合には、MISO ピンは必ずプルアップしておく。
エラーがある場合はその他のデータ線もプルアップしておく。

◆SPIモードの周波数は最大20MHz を目安とした方が良さそう。

◆Arduino core for the ESP32 の SD.h ライブラリでは、同時に開けるファイル数は4つまで。

以上です。
何か間違い等ありましたら、コメント等でご連絡いただけると助かります。

早く Arduino core for the ESP32 がアップデートされて、240MHz まで使えるようになってほしいものです。
(実は、初めからCPUは 240MHz モードだったようです。失礼しました。)

では、今回はここまでです。
ではまた・・・。

Amazon.co.jp 当ブログのおすすめ

スイッチサイエンス ESPr Developer 32 Type-C SSCI-063647
スイッチサイエンス
¥2,420(2024/03/28 13:45時点)
ZEROPLUS ロジックアナライザ LAP-C(16032)
ZEROPLUS
¥22,504(2024/03/28 20:55時点)
Excelでわかるディープラーニング超入門
技術評論社
¥1,700(2024/03/28 09:40時点)

コメント

  1. chantai より:

    いつも参考にさせていただき助かっております。
    省電力化のためIO25pinをSDモジュールの電源として利用しているのですが、125MB SDは書き込みできますが、8GB SDHC(class4)や32GB SDHC(class10)では書き込みができなくなります。3.3V電源からの給電であればどのカードも書き込み出来ています。当面はセンサーデータ書き込みだけですので2GB未満のSD規格のカードを使おうと思っております。
    IOpinからの給電(degitalwrite High)では電流が小さく、SDHC以上では書き込みできなくなるような原因があるのでしょうか?原因についてお分かりになるならお教えいただきたいと思います。

    • mgo-tec mgo-tec より:

      chantai さん

      記事をご覧いただき、ありがとうございます。

      ESP32 ( ESP-WROOM-32 )の GPIO ピンからの給電は、まず無理だと思います。
      SDカードの規格では、最低でも 200mA 以上の電流が必要のようです。
      恐らく、読み書き時に瞬間的に大電流が必要なようです。

      基本的に、GPIO から引き込む電流は、最大 12mA 程度に抑えないと、ESP32 を破壊しかねません。
      それ以上の電流を流そうとすると、恐らくリミッターが作動するようですが、LED を光らせるだけでも、それに頼らずに12mA以下に抑えるように電流制限抵抗を入れた方が良いです。

      ですから、GPIO ピンから micor SD カードの電源を供給すると、電流制限がかかって SD カードのマイクロチップが起動しないものと考えられます。

      micro SD カードは大電流を必要とするので、余裕のある電源容量と、急な大電流でも電圧がドロップしない、高速応答高容量電源レギュレーターから給電した方が良いです。
      SDアソシエーション団体ページでは、未加入者でも観閲できる簡易版SD規格仕様書がダウンロードできますので、参照してみてください。
      https://www.sdcard.org/jp/developers/overview/index.html

      • Chantai より:

        返信いただきありがとうございます。
        当方、1ヵ月程度 単3電池4本駆動でdeepsleepして温度、土の水分をsd記録したくセンサー、sd記録電源の省力化をtestしておりました。これまで10日程度の稼働までしか達成できておらず、またこのやり方では本体が壊れることもあるというご指摘から他の方法を検討した方が良さそうです。しかし、素人レベルでアイデアありませんが。。重ねてすみませんが何か参考になる情報ございましたらお教えいただけないでしょうか?

        • mgo-tec mgo-tec より:

          なるほど。
          DeepSleep の単34本土壌センサーでね。
          正直、私は DeepSleep やバッテリー仕様回路は組んだことがありませんので詳しくは分かりませんが、
          まず、お聞きしたいのが、ESP-WROOM-32単体使用でしょうか?
          それとも、ESP32-DevKitC でしょうか?
          電源レギュレーター( LDO )は使用されていますでしょうか?

          ESP-WROOM-32(ESP32)は、起動時に大きな電流を必要とします。
          予想ですが、DeepSleep から目覚めた時にも電流が大きいと思われます。
          (Wi-Fi機能をOFFにしておく設定が必要)

          その時にバッテリー容量が低下していると、供給電流によって電圧降下が起きて、ESP32の動作可能電圧範囲を下回り、ESP32自身のメモリを破損する恐れがあります。
          長文記事ですが、以下の記事をご参照ください。
          ●ESP-WROOM-32 ( ESP32 )の消費電流を電流プローブ無しで測定してみました
          ●ESP-WROOM-32 (ESP32) の 電流 測定 その2
          ●ESP-WROOM-32 ( ESP32 ) のUSB電源突入電流(インラッシュカレント)を考える
          ●ESP-WROOM-32 ( ESP32 ) の保護機能付き電源強化対策の実験

          ですから、まず、高速応答、大容量のLDO電圧レギュレーターが必要です。
          ESP32-DevKitC の 3.3V出力端子ならばそれを経由しています。
          スイッチサイエンス製の ESPr Developer 32 はソフトスタート機能があり、更にUSB機器に優しい構造になっています。

          また、いくらLDOを介していても、バッテリーを使う場合はさらに要注意です。
          LDO の動作範囲電圧を下回らないような電源管理が必要です。

          その為には、M5stack回路にあるような、バッテリー電圧管理IC などの回路構成が必要だと思います。
          例えば、LDO の電圧動作範囲が 2.1V 以上だとすると、それ以下になったら電流供給を自動でストップするようなICです。

          ネットでは ESP8266 で1年間バッテリー駆動させた例が報告されているので調べてみて下さい。
          何分、私自身はバッテリー回路を組んだことが無いので、ここまでの説明しかできず、お役に立てずスミマセン。

  2. chantai より:

    度々お世話になります。
    ご紹介いただいたように電池駆動の省電力化は素人にはハードル高めですが、続けて検討していきたいと思います。
    また、そもそも内蔵の赤色LEDは常時OnのままでDeepSleepが上手く作動していないかもと疑問を持っております。1分ごとに復帰、センサー読取&SD書込み時に内蔵青色LED ONで動作を確認しておりますが。。。回路の通電チェックも必要に感じます。
    ESP32はamzonでKKHMF NodeMCU開発ボードを使用しております。
    電源レギュレータはあまり考えなく使用しておりませんでした。
    ご紹介のスイッチサイエンス製やM5Stackへの切替えや、他にやるべきことは多くあり、一つ一つクリアしていければと考えます。
    紹介いただいたサイトもよく勉強したいと思います。

    • mgo-tec mgo-tec より:

      KKHMF のボードは回路が公開されていないようなので、良く分からないのですが、恐らく通常の開発ボード上の電源LEDというものは、ボード内蔵のLDO出力の5Vまたは3.3Vから取っていて、USBを接続すれば点灯するように出来ている場合が多いです。
      あとは、USBシリアル通信しているときに点滅するLEDを備えているものが多いです。
      ですから、DeepSleepになっていても、電源が接続されていれば点灯すると思います。
      ということは、いくら DeepSleepを使っていても、LED点灯で電力消費し続けると思います。

      KKHMF のボードでも、3V3端子からmicroSDカードへ電源を供給すれば、ある程度安定した電圧が供給できると思います。
      ですが、おそらくスイッチサイエンス製のような高性能回路ではないかもしれませんので、何とも言えません。

      3V3端子から電源を取ってたとして、とりあえず microSDへの読み書きは可能でも、LDOやESP32の動作電圧範囲限界までバッテリー電圧が低下した場合は要注意です。
      バッテリーの電流供給能力や、LDOの性能によっては、2.5Vを下回っても危ないかも知れません。
      そこで、ESP32 や microSD が破損しないように早めにバッテリーを交換することができれば、バッテリー監視回路を組まなくても使えると言えば使えるかも知れません。

      いずれにしても、その辺は私も素人でそれ以上のことは全く分かりません。
      お役に立てず、スミマセン。

タイトルとURLをコピーしました