Arduino環境でSDカードモジュールを使う

it

Arduino環境上でRaspberry Pi Picoを使ってSDカードモジュールを使います。
ファイルの保存/読み取り、ファイルの存在確認や削除などの使い方について記載します。

記事の概要

本記事では、Arduino環境でSDモジュールを使うための内容を記載します。
SDカードモジュールの簡単な紹介と、使ってみた感想を記載しています。

Arduino環境でSDカードモジュールを使うための開発環境の作成と、開発をしていた時のトラブルや対処方法について記載しています。

ファイルの読み書き、ファイルの削除、ファイルとフォルダリストを作成するサンプルを作成し掲載しています。

SDカードモジュール

簡単紹介

MicroSDカードへのファイルの読み書き、ファイルリストの読み取りができます。
SDカードリーダモジュールにはPCB基板の形状の違うモデルがあり、製品によっては電源電圧が4.5V以上必要なものがあります。

当記事のSDカードリーダモジュールは代表でこのPCB基板のものを使用します。
ピン配置ではメモとして他にも所有するモデルのピン配置を掲載しますが、使用方法、使用するサンプルは同じものが使用できます。

◆入手

電子工作部品販売店、オンラインショップ、amazon、AliExpressなど入手性は高い。
購入の目安は\250~\300(送料込み)。
ロット売りなら\150ほどでも入手できそうです。

◆その他

2種類のマイクロSDカードリーダを所有しています。
機能や使い方は同じなので個々の優劣、比較は省略します。

◆関連記事

ピン配置

この記事では、小柄のPCB基板を「SDカードリーダモジュールS」、大柄のPCB基板を「SDカードリーダモジュールL」と識別します。

外観

使ってみた

◆組み立て

今回使用するSDカードリーダモジュールSではピンヘッダは未実装です。
SDカードリーダモジュールLではアングルタイプのピンヘッダが取り付けられていました。
使用したい状態に合わせてピンヘッダを取り付けます。

◆開発

ArduinoIDEのインストールと一緒にライブラリがインストールされます。
シンプルな命令セットで、ファイルへの読み書き、存在確認や削除などの操作できました。

4線式SPIで配線は少なくないです。
一部のマイコン基板では基板上にマイクロSDカードリーダモジュールが実装されているモデルがあり、配線不要で使用できるものもあります。

左から、
Marble Pico
XIAO ESP32-S3 Sense
ESP32-WROOM-BreakOut
ESP32-S3-WROOM-CAM
ESP32-WROVER LOLIN D32 PRO。
(XIAO ESP32-S3 Senseはカメラモジュールに搭載されていて、コネクタマウントするので配線は不要です)


◆用途

当記事では簡易温度ロガーを作りました。
他にもJPG画像を保存しておいて、読み取った画像を小型のLCDに表示したりできます。

準備

開発環境

Arduino環境でRaspberryPi Picoを使用します。
Arduino環境の準備はこちらの記事で紹介しています。

ボードライブラリ

今回Raspberry Pi Picoを使用します。
Arduino IDEのボードマネージャからRP2040用のライブラリのインストールとボードの選択をします。
ボードライブラリには「Raspberry Pi Pico/RP2040 by Earle F. Philhower, III」を使用します。
Generic RP2040またはRaspberry Pi Picoを使用します。

追加のボードマネージャのURLhttps://github.com/earlephilhower/arduino-pico/releases/download/global/package_rp2040_index.json
検索RP2040
ボードライブラリRaspberry Pi RP2040 Boards(x.x.x)※
選択するボードRaspberry Pi RP2040 Boards(x.x.x) > Raspberry Pi Pico
※動作確認はバージョン 4.1.1です

モジュールライブラリ

インストールは不要です。

SDカードモジュールを使用するためのライブラリは ArduinoIDEをインストールすることで含まれています。

トラブル

コンパイルエラー(ライブラリの競合)

◆症状

「Output」メッセージに競合が見つかったメッセージ(一部抜粋)が表示されます。
Multiple libraries were found for “SdFat.h”

インストールされているライブラリの不具合ではなく、後からインストールするライブラリとの競合が発生することがあります。
各モジュールライブラリをインストールするときに依存関係のインストールをすることがあります。
その依存関係に競合ライブラリが含まれていることがあります。

◆対処

競合ライブラリを除外します。

ArduinoIDEインストール時に一緒にインストールされるSDカードモジュールライブラリを使うことを前提にAdafruitライブラリ、「SdFat_-_Adafruit_Fork」が競合していることを前提にします。

作業は次の2ステップで行います。

1.Arduinoモジュールライブラリフォルダを開く

フォルダは以下のパスです。
[UserAccount]はコンピュータのアカウント名です。
C:\Users\[UserAccount]\Documents\Arduino\libraries

2.競合ライブラリを除外します

対象の競合ライブラリを「libraries」より上位のフォルダへ移動するか削除します。

◆補足

当方の作業中に確認した同現象が発生したライブラリです。
※ライブラリが悪ではありません。参考に記載します。

ライブラリ名競合依存ライブラリ名
PicoDVI-Adafruit Fork by Luke Wren(Wren6991)SdFat – Adafruit Fork by Bill Greiman

依存関係のインストール画面です。
目的のモジュールライブラリをコンパイルする以外に、インストール不要で動作する依存関係が混ざっていることがあります。
問題が起きなければ「INSTALL ALL」を選びますが、競合問題が発生する場合は「INSTALL WITHOUT DEPENDENCIES」を選択し、必要なものは個別でインストールします。

スケッチサンプル

ファイルへ保存(追記モード)

説明

Raspberry Pi Picoの内部温度を読み取りファイルへ保存します。

SDカードに “sample.txt”ファイルを作ります。
すでに存在する場合は、ファイルの最後に追記します。

1秒ごとに内部温度を読み取り、ファイルの末尾に追記します。
同じ内容をUSBシリアルに出力します。

スケッチ

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

#define SD_MOSI 19
#define SD_SCK  18
#define SD_CS   17
#define SD_MISO 16

#define FILENAME "sample.txt"
File myFile;
bool LEDState = false;

void setup()
{
  SPI.setTX(SD_MOSI);
  SPI.setSCK(SD_SCK);
  SPI.setRX(SD_MISO);
  SPI.setCS(SD_CS);

  if (SD.begin(SD_CS, SPI) == false)
  {
    Serial.println("initialization failed.");
    while (1);
  }
}

void loop()
{
  float Temp_degC = analogReadTemp();

  myFile = SD.open(FILENAME, FILE_WRITE);                       //追記モード
  Serial.printf("CPU Temperature = %f\n", Temp_degC);
  myFile.printf("CPU Temperature = %f\n", Temp_degC);
  myFile.close();
  delay(1000);

}

結果

シリアルモニタの結果とSDカードに記録されたファイルをメモ帳で開いた結果です。
温度の変化がほしかったので、Raspberry Pi PicoのCPUを指で温めてみました。

ファイルから読み出し

「ファイルへの保存(追記モード)」でSDカードに保存したファイルを読み取りモードで開きます。

読み取ったデータをUSBシリアルに送信します。
すべて読み取りが終わったら “–EOF–“をUSBシリアルに送信します。

スケッチ

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

#define SD_MOSI 19
#define SD_SCK  18
#define SD_CS   17
#define SD_MISO 16

#define FILENAME "sample.txt"
File myFile;

void setup()
{
  Serial.begin(115200);
  delay(1000);

  SPI.setTX(SD_MOSI);
  SPI.setSCK(SD_SCK);
  SPI.setRX(SD_MISO);
  SPI.setCS(SD_CS);

  if (SD.begin(SD_CS, SPI) == false)
  {
    Serial.println("initialization failed.");
    while (1);
  }

  //読み取りモードでファイルを開く
  myFile = SD.open(FILENAME, FILE_READ);
  if (myFile == false) 
  {
    Serial.println("open file failed.");
    while(1);
  }

  char buf = 0;

  while (myFile.available() != 0)
  {
    buf = myFile.read();
    
    //改行
    if(buf == 0x0d)
    {
      Serial.println("");        
    }
    else
    {
      Serial.write(buf);
    }
  }
  myFile.close();
  Serial.println("");
  Serial.println("--EOF--\n");

}

void loop()
{
}

結果

「ファイルへの保存(追記モード)」で保存したファイルの内容を読み出しました。

ファイルの存在確認と削除

SDカードカードから指定したファイルを削除します。

スケッチ

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

#define SD_MOSI 19
#define SD_SCK  18
#define SD_CS   17
#define SD_MISO 16

#define FILENAME "sample.txt"
 
void setup()
{
  bool bSDResult = false;
  Serial.begin(115200);
  delay(1000);
  Serial.print("");
 
  SPI.setTX(SD_MOSI);
  SPI.setSCK(SD_SCK);
  SPI.setRX(SD_MISO);
  SPI.setCS(SD_CS);
 
  bSDResult = SD.begin(SD_CS, SPI);
  if(bSDResult == true)
  {
    //ファイルの存在確認 : false = 無い / true = 有る
    if(SD.exists(FILENAME) == true)
    {
      SD.remove(FILENAME);      //ファイルの削除
Serial.println("削除成功");
    }
    else
    {
Serial.println("ファイルが見つかりません");
    }
  }
}
 
void loop()
{ 
}

結果

結果の出力はTeratermに表示させます。

SDカードにはsample.txt を作成します。
SDカードはSDカードモジュールに挿入してマイコンの電源を入れます。

Teraterm上には”削除成功”のメッセージが表示されました。
SDカードをパソコンで読み取らせるとsample.txtが削除されていました。

再びSDカードをSDカードモジュールに挿入して、マイコンからRESETにより再起動させます。
(SDカードにはsample.txtは削除されていて存在しません)
Teraterm上には”ファイルが見つかりません”と表示されました。

ファイルサイズとフォルダのリスト作成

SDカードカードの指定したフォルダからファイルとフォルダのサイズとリストを作成します。
フォルダ名は ‘/’で区切ります。

今回SDカードのルートフォルダを検索してリストを作成します。

スケッチ

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

#define SD_MOSI 19
#define SD_SCK  18
#define SD_CS   17
#define SD_MISO 16

File tFiles;

void setup()
{

  delay(500);
  Serial.begin(115200);
  while (!Serial) {
    ;
  }

  SPI.setTX(SD_MOSI);         //RP2040系 SPI0 ピン設定
  SPI.setSCK(SD_SCK);
  SPI.setRX(SD_MISO);
  SPI.setCS(SD_CS);

  if (SD.begin(SD_CS, SPI) == false)
  {
    Serial.println("initialization failed.");
    while (1);
  }
  tFiles = SD.open("/");      //ルートフォルダ(/)のファイルをすべて読み取る。

  File tNewFile;
  int iFileIndex = 1;

  while(1)
  {
    tNewFile = tFiles.openNextFile();      //ルートフォルダの見つかったリストがまだあるか?
    if(tNewFile == 0)break;

    //見つかったリストがファイルなら F, ディレクトリ(フォルダ)ならD を表示
    if(tNewFile.isDirectory() == true)
    {
      Serial.printf("D ");
    }
    else
    {
      Serial.printf("F ");
    }

    //見つかったインデックス、ファイル名、ファイルサイズを表示する
    Serial.printf("%02d : %s : %d\n", iFileIndex, tNewFile.name(), tNewFile.size());        //見つかった番号とファイル名をシリアル出力
    iFileIndex ++;
  }

  tNewFile.close();
  tFiles.close();

}

void loop()
{
}

結果

結果の出力はシリアルモニタに出力させます。

Windows上では、ファイルサイズをKB(キロバイト)で表示していますが、
ArduinoではByte(バイト)単位で読みだしています。

一番先頭に表示された System Volume Information ファイルのシステムに関するフォルダです。
無いものとして考えてください。

コメント

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