ArduinoのSDカードモジュール:RTCのタイムスタンプ付きCSVデータロガー
ArduinoのSDカードモジュールのガイドです。SPIの配線、FAT32フォーマット、RTCのタイムスタンプ付きCSVでのセンサー記録、ESP32のSD_MMC、Pico、initialization failedの直し方を解説します。
ArduinoのSDカードモジュールは、センサーで測った温度、湿度、距離、位置などの値を、microSDカードにファイルとして保存できる小さくて安価なボードです。パソコンをつながずに何日も、何か月も測定を続けるデータロガーをつくるいちばん簡単な方法でもあります。Arduinoが値を読み取り、モジュールがそれをカード上のCSVファイルに1行ずつ書き込みます。あとはカードをパソコンに挿して、Excelでデータをグラフにするだけです。モジュールはSPIで接続し、Arduino IDEに付属するSDライブラリを使えば数行のコードで動きます。
このガイドでは、モジュールのしくみ、5Vと3.3Vのモジュールの違い、Arduino Uno、Mega、ESP32、Raspberry Pi PicoでのSPIの配線、カードをFAT32でフォーマットする方法、ファイルの書き込みと読み込み、RTCモジュールを使った日付と時刻付きのCSVログの残し方を手順どおりに学びます。さらに、ESP32の高速なSD_MMCインターフェース、MicroPythonでPicoからSDカードを使う方法、そしてとてもよくある「initialization failed」(初期化失敗)エラーの直し方も扱います。

SDカードモジュールとは?どのように動くのか
SDカードは3.3 Vで動作し、2つの方法で通信できます。高速なネイティブのSDモードと、よりシンプルなSPIモードです。Arduinoのようなマイコンは、SPIでカードとやり取りします。モジュールの役目は、両者を電気的に合わせることです。レギュレーターが5 Vをカードに必要な3.3 Vに下げ、レベル変換チップ(ふつうは74LVC125A)がArduinoの5 Vの信号をカードが扱える3.3 Vに変換します。カードの中身は512バイトのブロックでできていて、ファイル、フォルダ、ファイル名といった考え方はFATファイルシステムが扱います。こうした面倒な処理はすべてSDライブラリが引き受けてくれるので、ファイルを開いて書き込み、閉じるだけで済みます。
| 項目 | 値 |
|---|---|
| インターフェース | SPI(MISO、MOSI、SCK、CS) |
| 電源(レギュレーター付きmicroSDモジュール) | 4.5〜5.5 V、ボード上に3.3 Vレギュレーター |
| 信号レベル | 3.3 Vと5 Vに対応(レベル変換チップのおかげ) |
| 対応カード | microSD(2 GBまで)とmicroSDHC(4〜32 GB) |
| ファイルシステム | FAT16またはFAT32 |
| 電流 | 待機時は数mA、書き込み時は最大100 mA |
| ファイル名(ArduinoのSDライブラリ) | 8.3形式:名前は8文字まで+拡張子は3文字 |
5Vと3.3VのSDカードモジュール:どちらを買えばいい?
| モジュールの種類 | 電源 | 信号レベル | 向いている用途 |
|---|---|---|---|
| レギュレーター付きmicroSDモジュール(74LVC125A) | 5 V | 3.3 Vと5 V | Uno、Nano、Mega。5 Vから給電するならESP32やPicoにも |
| 標準サイズのSDカードモジュール(+5と+3.3のピン) | 5 Vまたは3.3 V | 多くは本物のレベル変換回路がない | 3.3 Vのボード向け。5 Vの信号では不安定 |
| レギュレーターなしのmicroSDモジュール(スロットだけ) | 3.3 Vのみ | 3.3 Vのみ | ESP32、Raspberry Pi Pico |
| スロット内蔵のボード(ESP32-CAMなど) | ボードから給電 | 3.3 V | SD_MMCでそのまま使える |
Arduino Uno、Nano、Megaを使うなら、写真のようなレギュレーターとレベル変換チップ付きのmicroSDモジュールを選び、VCCを5Vピンにつなぎます。ESP32とPicoは3.3 Vで動作します。同じモジュールをこれらのボードでも使えますが、その場合も5 Vから給電してください(ESP32ではVIN、USB給電のPicoではVBUS)。レベル変換チップが3.3 Vで動いているので、マイコンに戻るMISOの信号はすでに3.3 Vになっています。レギュレーターなしのモジュールは3V3ピンに直接つなぎます。それを5 VのArduinoで使わなければならない場合は、MOSI、SCK、CSの各ラインに1 kΩ+2 kΩの分圧回路を入れるか、レベル変換モジュールを使ってください。抵抗値は分圧回路の計算ツールで確認できます。
SDカードモジュールの配線:SPIのピン
| モジュールのピン | Arduino Uno / Nano | Arduino Mega | ESP32 | Raspberry Pi Pico |
|---|---|---|---|---|
| GND | GND | GND | GND | GND |
| VCC | 5V | 5V | VIN(5 V) | VBUS(5 V) |
| MISO | D12 | D50 | GPIO 19 | GP16 |
| MOSI | D11 | D51 | GPIO 23 | GP19 |
| SCK | D13 | D52 | GPIO 18 | GP18 |
| CS | D10 | D53 | GPIO 5 | GP17 |
MISO、MOSI、SCKは、どのボードでもハードウェアSPIのピンにつなぐ必要があります。CS(チップセレクト)は、コードのSD.begin()に同じ番号を渡しさえすれば、どのデジタルピンでもかまいません。市販のシールドでは、CSはふつうD4かD10です。同じSPIバスに複数のデバイスをつなぐこともできます。MISO、MOSI、SCKは全デバイスで共有し、それぞれに専用のCSピンを割り当てます。SPIのしくみはSPIとは?で詳しく説明しています。ボードのどこにピンがあるかは、ピン配置ツールで確認できます。
SDカードの準備:FAT32でフォーマットする
- カードの容量:4〜32 GBのmicroSDHCカードを選びます。ArduinoのSDライブラリはSDSC(2 GBまで)とSDHCのカードに対応しています。64 GB以上のSDXCカードはexFATでフォーマットされて出荷されていて、このライブラリでは読めません。
- ファイルシステム:カードをFAT32でフォーマットします(2 GB以下のカードならFAT16でも動きます)。NTFSやexFATでは動きません。
- Windows:エクスプローラーでカードを右クリックして「フォーマット」を選び、ファイルシステムに「FAT32」、アロケーションユニットサイズは既定値を選びます。32 GBより大きいカードでは、WindowsにFAT32の選択肢が表示されません。
- macOS:「ディスクユーティリティ」でカードを選んで「消去」をクリックし、フォーマットに「MS-DOS (FAT)」、方式に「マスター・ブート・レコード」を選びます。
- いちばん確実な方法:SDアソシエーションの無料ツール「SD Card Formatter」(SDメモリカードフォーマッター)を使うと、公式の規格どおりにフォーマットでき、たいていのおかしなエラーが解消します。
- パーティションは1つ:カードのパーティションは1つだけにします。以前Raspberry Piやカメラで使ったカードには、複数のパーティションが残っていることがあります。フォーマットの前に削除してください。
最初のテスト:SDカードへの書き込みと読み込み
- フォーマットしたカードをモジュールに挿し、上の表のとおりにモジュールをArduinoに配線します。
- SDライブラリはArduino IDEに付属しているので、インストールするものはありません。スケッチの先頭にSPI.hとSD.hを追加するだけです。
- 下のコードを書き込み、シリアルモニタを9600ボーで開きます。
- 「tamam」(トルコ語で「OK」)とファイルの中身が表示されれば、カードと配線は問題ありません。ボードが起動するたびに、TEST.TXTに新しい行が1行追加されます。
- カードを抜いてパソコンに挿せば、TEST.TXTをどのテキストエディターでも開けます。
1#include <SPI.h>
2#include <SD.h>
3
4const int CS_PIN = 10; // modülün CS pini / module CS pin
5
6void setup() {
7 Serial.begin(9600);
8 Serial.print(F("SD kart baslatiliyor... "));
9 if (!SD.begin(CS_PIN)) { // kart başlatılamadı / card init failed
10 Serial.println(F("initialization failed"));
11 while (true); // burada dur / stop here
12 }
13 Serial.println(F("tamam"));
14
15 File dosya = SD.open("TEST.TXT", FILE_WRITE); // yoksa oluşturur, sona ekler / creates it, appends
16 if (dosya) {
17 dosya.println(F("Merhaba BlueGrays!"));
18 dosya.close(); // tamponu karta yazar / flushes the buffer to the card
19 Serial.println(F("TEST.TXT yazildi"));
20 } else {
21 Serial.println(F("TEST.TXT acilamadi")); // dosya açılamadı / could not open file
22 }
23
24 dosya = SD.open("TEST.TXT"); // okumak için aç / open for reading
25 if (dosya) {
26 Serial.print(F("Boyut (bayt): "));
27 Serial.println(dosya.size());
28 while (dosya.available()) {
29 Serial.write(dosya.read()); // içeriği seri porta yaz / print contents to serial
30 }
31 dosya.close();
32 }
33}
34
35void loop() {
36}| 関数 | はたらき |
|---|---|
| SD.begin(cs) | カードを初期化し、成功するとtrueを返す |
| SD.open("NAME.TXT", FILE_WRITE) | 書き込み用にファイルを開く。なければ作成し、あれば末尾に追記する |
| SD.open("NAME.TXT") | 読み込み用にファイルを開く(FILE_READ) |
| file.print() / println()(書き込み) | 文字列や数値をファイルに書き込む |
| file.flush() | ファイルを閉じずに、バッファのデータをカードに書き込む |
| file.close() | 残りのデータをカードに書き込んでファイルを閉じる |
| file.available() / read()(読み込み) | 読み残しのバイトがあるか確認する/1バイト読む |
| SD.exists() / SD.remove() / SD.mkdir()(ファイル操作) | ファイルがあるか確認する/ファイルを削除する/フォルダを作る |
RTCのタイムスタンプ付きでセンサーのデータをCSVに記録する
データロガーにいちばん実用的なファイル形式はCSV(カンマ区切り値)です。1行が1回の測定、1列が1つの値で、1行目には列の見出しを入れます。それぞれの値をいつ測ったのかを知るには、各行に日付と時刻を付ける必要があります。millis()はArduinoが動き始めてからの時間しかわからず、再起動のたびにリセットされます。そこで、コイン電池付きのRTC(リアルタイムクロック)モジュールを使います。RTCはArduinoの電源が切れている間も時間を数え続けます。DS1307は安価ですが1日に数秒ずれることがあり、DS3231は温度補償付きの水晶振動子のおかげで、ずれは1年に約1分程度です。両者の比較はRTC時計モジュールで紹介しています。

回路は次のとおりです。SDモジュールは上の表のとおりSPIのピンに、RTCモジュールはI2Cバスに(UnoではSDA = A4、SCL = A5、VCC = 5V)、DHT22センサーのデータピンはD2につなぎます。センサー単体を使う場合は、データピンとVCCの間に10 kΩの抵抗を入れてください。ライブラリマネージャからRTClib(Adafruit)とDHT sensor libraryをインストールします。センサーの配線の詳細はDHT11やDHT22温湿度センサーを参照してください。
1#include <SPI.h>
2#include <SD.h>
3#include <Wire.h>
4#include <RTClib.h>
5#include <DHT.h>
6
7const int CS_PIN = 10; // SD modülü CS / SD module CS
8const unsigned long ARALIK = 10000; // 10 saniyede bir kayıt / log every 10 seconds
9
10RTC_DS1307 rtc; // DS3231 için RTC_DS3231 / RTC_DS3231 for a DS3231
11DHT dht(2, DHT22); // veri pini D2, DHT11 de olur / data pin D2, DHT11 also works
12unsigned long sonKayit = 0;
13
14void setup() {
15 Serial.begin(9600);
16 dht.begin();
17 if (!rtc.begin()) {
18 Serial.println(F("RTC bulunamadi")); // RTC yok / RTC not found
19 while (true);
20 }
21 if (!rtc.isrunning()) { // saat hiç kurulmamış / clock never set
22 rtc.adjust(DateTime(F(__DATE__), F(__TIME__))); // derleme anına kur / set to compile time
23 }
24 if (!SD.begin(CS_PIN)) {
25 Serial.println(F("SD kart hatasi")); // SD başlatılamadı / SD init failed
26 while (true);
27 }
28 if (!SD.exists("VERI.CSV")) { // yeni dosyaya başlık satırı / header row for a new file
29 File f = SD.open("VERI.CSV", FILE_WRITE);
30 if (f) {
31 f.println(F("tarih,saat,sicaklik_C,nem_yuzde"));
32 f.close();
33 }
34 }
35}
36
37void loop() {
38 if (millis() - sonKayit < ARALIK) return; // zamanı gelmedi / not time yet
39 sonKayit = millis();
40
41 float t = dht.readTemperature();
42 float h = dht.readHumidity();
43 if (isnan(t) || isnan(h)) { // sensör okunamadı / sensor read failed
44 Serial.println(F("DHT okuma hatasi"));
45 return;
46 }
47
48 DateTime simdi = rtc.now();
49 char ts[8], hs[8], satir[48];
50 dtostrf(t, 1, 1, ts); // AVR'de sprintf %f desteklemez / AVR sprintf has no %f
51 dtostrf(h, 1, 1, hs);
52 snprintf(satir, sizeof(satir), "%04d-%02d-%02d,%02d:%02d:%02d,%s,%s",
53 simdi.year(), simdi.month(), simdi.day(),
54 simdi.hour(), simdi.minute(), simdi.second(), ts, hs);
55
56 File f = SD.open("VERI.CSV", FILE_WRITE); // sona ekle / append
57 if (f) {
58 f.println(satir);
59 f.close(); // her kayıtta kapat: güç kesilse de veri kalır / close every time so data survives a power cut
60 Serial.println(satir);
61 } else {
62 Serial.println(F("VERI.CSV acilamadi"));
63 }
64}このコードは10秒ごとに温度と湿度を読み取り、RTCから日付と時刻を取得して、2026-11-04,14:05:30,23.5,45.2のような行をVERI.CSVの末尾に追加します。記録のたびにファイルを閉じるので、停電してもそれまでに記録したデータは残ります。AVRボードではsprintf()が浮動小数点数(%f)に対応していないため、値はまずdtostrf()で文字列に変換します。タイミングにはdelay()ではなくmillis()を使っているので、loop()の中でほかの処理もできます。RTCが一度も設定されていなければ、コードをコンパイルした時刻に時計が合わせられます。DS3231を使う場合は、RTC_DS1307の代わりにRTC_DS3231と書き、isrunning()の代わりにlostPower()を確認するだけです。
どのくらいの容量を使うのでしょうか。1行は約31バイトです。10秒ごとに記録すると1日8,640行、約260 KBになり、1年でおよそ95 MBです。つまりこのペースなら、8 GBのカードで数十年もちます。本当の制約はカードではなく電源です。プロジェクトを電池で動かすなら、電池寿命計算ツールでどのくらいもつか事前に計算しておきましょう。
SDカードのデータをExcelで開く
- Arduinoの電源を外し、カードをモジュールから抜いてパソコンに挿します。
- VERI.CSVをパソコンにコピーします。カード上で直接作業するより、コピーで作業するほうが安全です。
- Excelで空白のブックを開き、「データ」>「テキストまたはCSVから」でファイルを選びます。
- プレビューのウィンドウで区切り記号に「コンマ」を選んで「読み込み」をクリックします。列が日付、時刻、温度、湿度に分かれます。
- 温度と湿度の列を選び、「挿入」タブから折れ線グラフを追加すると、時間とともにどう変化したかがわかります。
1// Excel'de ondalık ayırıcı virgülse / if Excel uses a decimal comma
2// Bu iki fonksiyonu kayıt kodunda loop()'un üstüne ekle / add these two functions above loop() in the logging sketch
3void noktayiVirgulYap(char *s) {
4 for (; *s; s++) {
5 if (*s == '.') *s = ','; // 23.5 -> 23,5
6 }
7}
8
9void excelSatiri(char *satir, size_t boyut, const DateTime &simdi, char *ts, char *hs) {
10 noktayiVirgulYap(ts);
11 noktayiVirgulYap(hs);
12 snprintf(satir, boyut, "%04d-%02d-%02d;%02d:%02d:%02d;%s;%s", // ayırıcı ; / separator ;
13 simdi.year(), simdi.month(), simdi.day(),
14 simdi.hour(), simdi.minute(), simdi.second(), ts, hs);
15}
16
17// loop() içinde snprintf satırının yerine / in loop(), replace the snprintf line with:
18// excelSatiri(satir, sizeof(satir), simdi, ts, hs);
19// Başlık satırı da / header row too: tarih;saat;sicaklik_C;nem_yuzdeカードを抜かずに、Arduinoをパソコンにつないだままで値をリアルタイムにExcelに送りたいなら、ArduinoのデータをExcelに送るで紹介しているData StreamerやPLX-DAQを使う方法を見てみてください。一方、パソコンから離れた現場で動かすプロジェクトには、SDカードが欠かせません。
ESP32でSDカードを使う(SPI)
ESP32のArduinoコアには専用のSDライブラリが付属していて、使い方はほぼ同じですが、重要な違いが3つあります。ファイルのパスは/から始まり("/olcumler.csv")、長いファイル名が使え、FILE_WRITEはファイルを最初から上書きします。追記するにはFILE_APPENDを使う必要があります。Unoのコードをそのまま移植すると、ボードが起動するたびに古いデータが消えてしまいます。デフォルトのSPIピンはSCK 18、MISO 19、MOSI 23で、CSにはふつうGPIO 5を使います。カードの種類と容量も、1行で読み取れます。
1#include <SPI.h>
2#include <SD.h>
3
4const int CS_PIN = 5; // VSPI: SCK 18, MISO 19, MOSI 23 / default VSPI pins
5const char *DOSYA = "/olcumler.csv"; // yol / ile başlar / path starts with /
6
7void setup() {
8 Serial.begin(115200);
9 if (!SD.begin(CS_PIN)) { // varsayılan SPI hızı 4 MHz / default SPI clock 4 MHz
10 Serial.println("Card Mount Failed");
11 return;
12 }
13 uint8_t tur = SD.cardType();
14 if (tur == CARD_NONE) {
15 Serial.println("Kart takili degil"); // kart yok / no card inserted
16 return;
17 }
18 Serial.printf("Kart tipi: %s\n", tur == CARD_SDHC ? "SDHC" : (tur == CARD_SD ? "SDSC" : "MMC"));
19 Serial.printf("Boyut: %llu MB\n", SD.cardSize() / (1024 * 1024));
20
21 if (!SD.exists(DOSYA)) { // uzun dosya adı serbest / long file names are fine
22 File f = SD.open(DOSYA, FILE_WRITE); // FILE_WRITE dosyayı sıfırlar / FILE_WRITE truncates
23 if (f) {
24 f.println("sure_s,isik_ham");
25 f.close();
26 }
27 }
28}
29
30void loop() {
31 File f = SD.open(DOSYA, FILE_APPEND); // ESP32'de sona ekleme / append on ESP32
32 if (f) {
33 f.printf("%lu,%d\n", millis() / 1000, analogRead(34)); // GPIO34'teki LDR / LDR on GPIO34
34 f.close();
35 }
36 delay(5000);
37}このコードは、GPIO 34につないだLDR(光センサー)の生の値(0〜4095)を5秒ごとに記録します。ESP32なら、正確な時刻のためにRTCモジュールを用意する必要さえありません。Wi-Fiに接続してNTPサーバーから時刻を取得し、getLocalTime()で各行にタイムスタンプを付けられます。配線が長い場合やカードが不安定な場合は、SPIの速度を下げてください。SD.begin(5, SPI, 1000000)なら1 MHzでカードを開始します。電池で動かすロガーでは、測定の合間にディープスリープを使うと、電池の持ちが何倍にもなります。
ESP32のSD_MMC:高速なSDインターフェースとESP32-CAM
ESP32にはSPIのほかに、カード本来の高速なプロトコルで通信するSDMMCコントローラーもあります。4ビットモードのSD_MMCライブラリはSPIより何倍も速く、写真や音声のような大きなファイルに最適です。無印ESP32ではピンが固定されていて、CLKがGPIO 14、CMDがGPIO 15、D0がGPIO 2、D1がGPIO 4、D2がGPIO 12、D3がGPIO 13です。これらのラインには10 kΩのプルアップ抵抗が必要です。1ビットモードではCLK、CMD、D0だけを使います。速度は落ちますが、GPIO 4、12、13が空きます。ESP32-CAMボードのmicroSDスロットはこれらのピンに配線されていて、1ビットモードを使えば、カードにアクセスするたびにGPIO 4のフラッシュLEDが点滅するのを防げます。
1#include "FS.h"
2#include "SD_MMC.h"
3
4void setup() {
5 Serial.begin(115200);
6 // 1-bit mod: yalnız CLK 14, CMD 15, D0 2 / 1-bit mode: only CLK 14, CMD 15, D0 2
7 if (!SD_MMC.begin("/sdcard", true)) {
8 Serial.println("SD_MMC baglanamadi"); // kart bağlanamadı / mount failed
9 return;
10 }
11 Serial.printf("Bos alan: %llu MB\n", (SD_MMC.totalBytes() - SD_MMC.usedBytes()) / (1024 * 1024));
12
13 File f = SD_MMC.open("/gunluk.txt", FILE_APPEND);
14 if (f) {
15 f.println("ESP32-CAM acildi"); // açılış kaydı / boot log line
16 f.close();
17 }
18}
19
20void loop() {
21}Raspberry Pi PicoでSDカードを使う(MicroPython)
MicroPythonでは、SDカードはmicropython-libのsdcard.pyドライバで扱います。パソコンにmpremoteがあればmpremote mip install sdcardでインストールするか、ThonnyでファイルをPicoにコピーします。カードはos.mount()で/sdフォルダとしてマウントされ、そのあとは通常のPythonのファイル操作(open、write)で使えます。下のコードは、Pico内蔵の温度センサーを読み取り、10秒ごとにCSVの行を書き込みます。Picoが初めてなら、まずRaspberry Pi Picoとは?を読んでください。
1from machine import Pin, SPI, ADC
2import os, time
3import sdcard # micropython-lib sürücüsü / micropython-lib driver
4
5spi = SPI(0, baudrate=1000000, sck=Pin(18), mosi=Pin(19), miso=Pin(16))
6sd = sdcard.SDCard(spi, Pin(17, Pin.OUT)) # CS = GP17; kart yoksa OSError / OSError if no card
7os.mount(os.VfsFat(sd), "/sd") # kart artık /sd klasörü / the card is now the /sd folder
8
9sensor = ADC(4) # dahili sıcaklık sensörü / internal temperature sensor
10DOSYA = "/sd/pico_log.csv"
11
12if "pico_log.csv" not in os.listdir("/sd"): # yeni dosyaya başlık / header for a new file
13 with open(DOSYA, "w") as f:
14 f.write("tarih,saat,sicaklik_C\n")
15
16while True:
17 v = sensor.read_u16() * 3.3 / 65535
18 t = 27 - (v - 0.706) / 0.001721 # RP2040 veri sayfası formülü / RP2040 datasheet formula
19 y, ay, g, sa, dk, sn = time.localtime()[:6]
20 with open(DOSYA, "a") as f: # "a" = sona ekle / append
21 f.write("{:04d}-{:02d}-{:02d},{:02d}:{:02d}:{:02d},{:.1f}\n".format(y, ay, g, sa, dk, sn, t))
22 print(sa, dk, sn, t)
23 time.sleep(10) # 10 saniye bekle / wait 10 secondsPicoには電池でバックアップされる時計がありません。Thonnyにつないでいるときはパソコンから時計が自動で設定されますが、単体で動かすと2021年1月1日から始まります。正確なタイムスタンプが必要なら、DS3231モジュールを追加するか、Pico WならNTPでインターネットから時刻を取得します。カードが挿さっていないか配線が間違っていると、ドライバはOSError: no SD card(SDカードがない)を出します。PicoをArduino IDE(arduino-picoコア)でプログラムする場合は、同じSDライブラリの方法が使えます。デフォルトのSPI0のピンは表と同じで、CSにはSD.begin(17)と書くだけです。
データの消失を防ぐ:電源、書き込み頻度、カードの寿命
- close()かflush()を呼ばないとカードには何も届かない:ライブラリはまず512バイトのバッファにデータをためます。ファイルを開いたままにしておく場合は、数行ごとにflush()を呼んでください。
- 書き込み中にカードを抜かない:書き込みの途中でカードを抜いたり電源を切ったりすると、ファイルシステムが壊れることがあります。記録を止めるボタンと、記録中であることを示すLEDを付けておくと安心です。
- 電源を安定させる:カードは書き込み時に最大100 mAを消費します。モジュールのVCCとGNDの間に10〜100 µFのコンデンサを入れると、電圧降下や不定期な書き込みエラーが減ります。
- 開閉を頻繁にしすぎない:1秒に何十回も測定する場合、1行ごとにファイルを開閉すると遅くなります。値をRAMにためて、10〜20行ずつまとめて書き込みましょう。
- カードの寿命:SDカードは内部で書き込みを別々のセルに分散させる(ウェアレベリング)ので、1分に数行書き込む程度のロガーなら、何年たってもカードは傷みません。それでも、有名メーカーのカードを選んでください。
- ファイルを分ける:日ごとに別のファイル(例:20261104.CSV)を開けばファイルが小さく保たれ、何か問題が起きても失うのはその日のデータだけで済みます。
SDカードモジュールを使ったプロジェクトのアイデア
- 気象観測ロガー:BME280で温度、湿度、気圧を1時間ごとに記録し、ひとつの季節全体をグラフにします。
- 植物の水やり記録:土壌水分センサーの値とポンプが動いた時刻を記録して、水やりのパターンを分析します。
- GPSルートロガー:NEO-6M GPSモジュールで自転車やハイキングのルートをCSVに保存し、地図に表示します。
- カードによる入退室記録:RFID RC522で読み取ったカードを、日付と時刻とともにファイルに書き込みます。
- 消費電力トラッカー:ACS712電流センサーで、機器が1日を通して流す電流を測って記録します。
- MP3プレーヤー:DFPlayer Miniは自分のmicroSDスロットから音声ファイルを再生します。Arduinoはコマンドを送るだけです。
よくある失敗と直し方
| 症状 | 考えられる原因 | 直し方 |
|---|---|---|
| エラー:「initialization failed」(初期化失敗) | カードが挿さっていない、配線が間違っている、またはCSピンがコードと合っていない | MISO、MOSI、SCKがハードウェアSPIのピンにつながっているか、SD.begin()のCS番号が正しいかを確認する |
| エラー:「initialization failed」(配線は正しい) | レギュレーター付きのモジュールを3.3 Vから給電している、または電源が弱い | モジュールを5 Vから給電し、配線を短くし、VCCとGNDの間にコンデンサを入れる |
| エラー:「Could not find FAT16/FAT32 partition」(パーティションが見つからない) | カードがexFATかNTFSでフォーマットされている。64 GB以上のSDXCカード | 32 GBまでのカードをFAT32でフォーマットする |
| ファイルが開けない(「error opening」) | ファイル名が8.3のルールに合っていない、または前のファイルを閉じていない | VERI.CSVのような短い名前を使い、すべてのファイルをclose()で閉じる |
| ファイルが空、または最後の数行が抜けている | close()かflush()を呼ぶ前に電源が切れた | 記録のたびにファイルを閉じるか、定期的にflush()を呼ぶ |
| ほかのSPIデバイスを追加するとSDカードが動かなくなる | ほかのデバイスのCSが浮いている、またはモジュールがMISOのラインを解放しない | setup()の最初ですべてのCSピンをOUTPUTかつHIGHにする。必要ならMISOを解放するモジュールを使う |
| Unoが固まる、または動作がおかしい | RAM不足:SDライブラリが約650バイト、OLEDディスプレイのバッファが1 KBを使う | 文字列をF()で囲む。必要ならMegaかESP32に移行する |
| ESP32で起動のたびに古いデータが消える | ファイルをFILE_WRITEで開いている | 末尾に追記するにはFILE_APPENDを使う |
| ESP32で「Card Mount Failed」(マウント失敗) | 電源、配線、高すぎるSPI速度、またはSD_MMCでのプルアップ抵抗の不足 | SPIの速度を1 MHzに下げる。SD_MMCでは1ビットモードを試す |
SDカードモジュールは、測るだけのプロジェクトを記録を残すデバイスに変えてくれます。数日分の実際のデータは、1回の測定値よりはるかに多くのことを教えてくれます。BlueGraysアプリでは、DHT22、DS1307 RTC、microSD、OLEDを使ったESP32の温度ロガーや、RTCを使ったデジタル時計、温湿度のプロジェクトを、回路図とコードつきで手順どおりに見られます。
よくある質問
Arduinoではどの容量のSDカードが使えますか?
標準のArduino SDライブラリは、FAT16またはFAT32でフォーマットされたSDSCとSDHCのカード、つまり32 GBまでのカードに対応しています。64 GB以上のSDXCカードはexFATで出荷されていて、このライブラリでは読めません。データロガーなら、4〜16 GBのカードで十分すぎるほどです。
SDカードで「initialization failed」と表示されるのはなぜですか?
よくある原因は、CSピンの間違い、MISO、MOSI、SCKがハードウェアSPIのピンにつながっていないこと、FAT32以外でフォーマットされたカード、そしてレギュレーター付きのモジュールを3.3 Vから給電していることです。まずCardInfoのスケッチ例を実行し、それから配線を短くしたり、モジュールを5 Vから給電したりしてみてください。
SDカードモジュールは3.3Vで動きますか?
レギュレーターなしのモジュールは3.3 Vで直接動作し、ESP32やPicoに向いています。レギュレーターとレベル変換チップ付きのmicroSDモジュールには4.5〜5.5 Vが必要で、3.3 Vから給電するとカードに十分な電圧が届きません。これらのモジュールは、ESP32ではVIN、PicoではVBUSから給電できます。
SDカードにあるArduinoのCSVファイルをExcelで開くには?
ファイルをパソコンにコピーし、Excelの「データ」>「テキストまたはCSVから」で、区切り記号に「コンマ」を選んで読み込みます。Excelが小数点にコンマを使う設定だと数値がおかしく見えることがあります。その場合は、列を「英語(米国)」のロケールで変換するか、コードで区切り記号にセミコロンを使ってください。日本語の設定なら、ふつうこの問題は起きません。
ArduinoはSDカードにどのくらいの頻度で書き込めますか?
1秒に数行書き込むのは問題ありませんが、1行ごとにファイルを開閉すると時間がかかります。速い測定では、データをRAMにためてまとめて書き込むか、ファイルを開いたままにして定期的にflush()を呼びます。1分に1回のようなゆっくりした記録なら、毎回開いて書き込んで閉じるのがいちばん安全な方法です。
SDカードモジュールとRFIDやTFTディスプレイは同時に使えますか?
はい。SPIデバイスはMISO、MOSI、SCKのラインを共有し、それぞれが専用のCSピンを使います。うまくいかないときは、setup()の最初ですべてのCSピンをOUTPUTかつHIGHにしてください。安価なmicroSDモジュールの中には、レベル変換チップがMISOのラインをまったく解放しないものがあります。その場合は解放するモジュールを使うか、ESP32なら2つめのSPIバスを使います。
この記事を読んだ後に作れるプロジェクト
全プロジェクトを見る
DHT11で温度と湿度を測定
部屋の温度は何度で、空気は湿っているでしょうか、それとも乾燥しているでしょうか?DHT11センサーが温度と湿度を測定し、Arduinoが2行のLCDディスプレイに値を表示して、数秒ごとに更新します。センサーに異常があればエラーメッセージが表示されます。自分だけの小さな気象観測装置を作る第一歩です。

ESP32温度データロガー(DHT22 + DS1307 RTC + microSD + OLED)
ESP32で日時付きの温度・湿度記録システムを作ります!DHT22で1分ごとに温度と湿度を読み取り、DS1307 RTCから取得した日時とともにmicroSDカードへCSV形式で記録します。OLEDには最新の温度を大きな文字で表示し、湿度のパーセント値、日時、総記録数も表示します。SDカードの/temp_log.csvファイルはExcel、Google Sheets、Pythonのpandasで開いて分析できます。温室の監視、家や部屋の温度管理、実験室のデータ収集、食品の保管環境や養蜂箱の監視に適しています。

ESP32とDS1307 RTCのデジタル時計(I2C + LCD)
このプロジェクトでは、DS1307リアルタイムクロック(RTC)モジュールを使って16x2 I2C LCDに時刻と日付を表示します。DS1307は電池でバックアップされるRTCで、ESP32の電源を切っても時刻を保持します。両方のモジュールが同じI2Cバス(SDA/SCL)を共有するため、2本の信号線だけで複数の機器を制御する方法を学べます。I2C通信プロトコルやRTCモジュールの動作原理、LCDの表示データを随時更新する方法を実践的に学ぶプロジェクトです。

ESP32 DHT22温湿度計(LCDディスプレイ付き)
DHT22センサーで周囲の温度と湿度を測定し、16x2 I2C LCDにリアルタイムで表示するプロジェクトです。Adafruit DHTライブラリを使い、1本の信号線を使うプロトコルでセンサーからデータを読み取ります。IoT気象観測装置やスマートホームの自動化、温室の環境監視システムの基礎となります。ESP32のGPIO 4ピンを介してDHT22と通信します。

ESP32のディープスリープで節電(OLED + RTC RAMカウンター)
ESP32のディープスリープモードは、CPUとほとんどの周辺機器を停止し、消費電流を約10 µAまで抑えます。電池で動くIoT機器に欠かせない機能です。このプロジェクトでは、タイマー(15秒)、外部ピン(EXT0、ボタンはGPIO 33)、タッチ(T0/GPIO 4)の3種類の復帰方法を使います。RTC_DATA_ATTRを付けた変数はディープスリープ中も保持され、起動回数、タイマーによる復帰回数、ボタンによる復帰回数を継続して記録できます。復帰するたびにSSD1306 OLEDディスプレイに復帰理由と回数を表示します。

ESP32とNTPでインターネットから正確な時刻を取得
ESP32をWi-Fiに接続し、NTP(Network Time Protocol)サーバーからミリ秒精度の現在時刻を取得します。configTzTime()関数でタイムゾーンを自動適用し、夏時間を採用する国ではPOSIX TZ文字列で夏時間と標準時間の切り替えも自動管理します。時刻はシリアルモニタと、必要に応じて接続するI2C LCDに表示します。configTzTime()がSNTPサービスを開始し、ネットワークにアクセスできる間は定期的に再同期します。

ドットマトリクス時計・カレンダー・気象ステーション(MAX7219、DS1307)
机や壁に置いて、時刻、日付、室内の温湿度を順番に表示する、文字がスクロールする表示板です。8x32のLEDディスプレイには、まず摂氏と華氏の温度、次に湿度、そして2つの点が1秒ごとに点滅する時計が表示されます。分が変わると、曜日と日付がスクロール表示されます。時計モジュールが時刻を保持し、DHT22センサーが温度と湿度を測定します。

DS1302 RTC時計をLCDディスプレイで表示
停電しても時刻を保持する小さな置き時計です。DS1302時計モジュールは専用の小さな電池で時刻を保持します。Arduinoが毎秒その情報を読み取り、LCDディスプレイの上段に時刻と日付を、下段に曜日を英語で表示します。自分だけの時計を手軽に作れます。
ほかの記事
BlueGraysアプリ
この回路をスマホでステップごとに作る
回路図、コード、部品リストを1つの画面で確認できます。Arduino、ESP32、Picoのプロジェクトを200件以上用意しています。

