บทความ: สอนใช้งาน SD Card กับ ESP32 เก็บข้อมูลแบบถาวร

เรียนรู้วิธีการเชื่อมต่อ SD Card กับ ESP32 และเขียนโปรแกรมเพื่อบันทึกข้อมูล sensor ลงไฟล์สำหรับ data logging และโปรเจกต์ IoT ของคุณ

📅 10 มีนาคม 2026⏱️ 15 นาที🎯 ระดับกลาง📂 Tutorial

📊 ภาพรวม Filesystem บน ESP32

ESP32 มีความสามารถในการจัดเก็บข้อมูลถาวรได้หลายวิธี ซึ่งเหมาะสำหรับโปรเจกต์ที่ต้องการบันทึกข้อมูล sensor, logs หรือการตั้งค่า ในบทความนี้เราจะเรียนรู้ 3 วิธีหลักๆ:

  • 💾SD Card: ใช้กับ Micro SD Card Module ความจุสูง (GB) เหมาะกับ data logging จำนวนมาก
  • 📁SPIFFS: Flash filesystem ในตัว ESP32 เอง ความจุเล็ก (KB-MB) เหมาะกับไฟล์ config
  • 🗂️LittleFS: รุ่นถัดไปของ SPIFFS เร็วกว่า มีประสิทธิภาพดีกว่า แนะนำให้ใช้

🔧 อุปกรณ์ที่ต้องใช้

Hardware

  • ✅ ESP32 Development Board (ทุกรุ่น)
  • ✅ Micro SD Card Module (SPI Interface)
  • ✅ Micro SD Card (แนะนำ 4GB-32GB, Class 10)
  • ✅ Jumper wires (Female-to-Female หรือ Female-to-Male)
  • ✅ Breadboard (ถ้าจำเป็น)

Software

  • ✅ Arduino IDE หรือ PlatformIO
  • ✅ Library: SD, FS, LittleFS

🔌 การต่อวงจร SD Card Module

SD Card Module ส่วนใหญ่ใช้ SPI Protocol ในการสื่อสาร ต่อกับ ESP32 ดังนี้:

SD Card ModuleESP32 Pin
CS (Chip Select)GPIO 5
SCK (Clock)GPIO 18
MOSI (Master Out Slave In)GPIO 23
MISO (Master In Slave Out)GPIO 19
VCC3.3V
GNDGND

⚠️ ข้อควรระวัง: บาง SD Card Module ต้องการ 5V สำหรับ VCC ให้ตรวจสอบ spec ของ module ที่ซื้อก่อนต่อวงจร

💾 การใช้งาน SD Card กับ ESP32

ตัวอย่างที่ 1: เขียนและอ่านไฟล์พื้นฐาน

โค้ดนี้สาธิตการเขียนและอ่านข้อมูลจาก SD Card เบื้องต้น:

#include "SD.h"
#include "SPI.h"

// กำหนดขา CS สำหรับ SD Card
#define SD_CS 5

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

  // เริ่มต้นใช้งาน SD Card
  if (!SD.begin(SD_CS)) {
    Serial.println("ไม่สามารถเริ่มต้น SD Card ได้!");
    return;
  }
  Serial.println("SD Card พร้อมใช้งาน!");

  // ตรวจสอบประเภทของ Card
  uint8_t cardType = SD.cardType();
  if (cardType == CARD_NONE) {
    Serial.println("ไม่พบ SD Card");
    return;
  }

  Serial.print("ประเภท SD Card: ");
  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");

  // แสดงข้อมูลขนาดของ SD Card
  uint64_t cardSize = SD.cardSize() / (1024 * 1024);
  Serial.printf("SD Card Size: %llu MB\n", cardSize);

  // เขียนข้อมูลลงไฟล์
  File file = SD.open("/test.txt", FILE_WRITE);
  if (file) {
    file.println("Hello, CynoIoT! SD Card ทำงานได้แล้ว");
    file.println("วันที่: 10/03/2026");
    file.close();
    Serial.println("เขียนข้อมูลลง test.txt สำเร็จ!");
  } else {
    Serial.println("ไม่สามารถเปิดไฟล์เพื่อเขียนได้");
  }

  // อ่านข้อมูลจากไฟล์
  file = SD.open("/test.txt");
  if (file) {
    Serial.println("\n--- ข้อมูลใน test.txt ---");
    while (file.available()) {
      Serial.write(file.read());
    }
    file.close();
  }

  // แสดงพื้นที่ว่างที่เหลือ
  uint64_t usedBytes = SD.usedBytes() / (1024 * 1024);
  uint64_t totalBytes = SD.totalBytes() / (1024 * 1024);
  Serial.printf("\nพื้นที่ใช้ไป: %llu MB / %llu MB\n", usedBytes, totalBytes);
}

void loop() {
  // ไม่ต้องทำอะไรใน loop
}

ตัวอย่างที่ 2: Data Logging สำหรับ Sensor

โค้ดนี้สาธิตการบันทึกข้อมูล sensor เป็นระยะ พร้อม timestamp:

#include "SD.h"
#include "SPI.h"

#define SD_CS 5
#define LED_PIN 2

// ชื่อไฟล์สำหรับ logging
const char* LOG_FILE = "/sensor_log.csv";

// ตัวแปรสำหรับจับเวลา
unsigned long lastLogTime = 0;
const unsigned long LOG_INTERVAL = 5000; // บันทึกทุก 5 วินาที

void setup() {
  Serial.begin(115200);
  pinMode(LED_PIN, OUTPUT);

  // เริ่มต้น SD Card
  if (!SD.begin(SD_CS)) {
    Serial.println("SD Card initialization failed!");
    while (1) {
      digitalWrite(LED_PIN, !digitalRead(LED_PIN));
      delay(100);
    }
  }

  Serial.println("SD Card พร้อมใช้งาน!");

  // สร้างไฟล์ CSV พร้อม header
  File file = SD.open(LOG_FILE, FILE_WRITE);
  if (file) {
    // เขียน header เฉพาะกรณีไฟล์ใหม่ (ไฟล์ว่าง)
    if (file.size() == 0) {
      file.println("Timestamp,Temperature,Humidity,Light");
      Serial.println("สร้างไฟล์ logging พร้อม header");
    }
    file.close();
  }
}

void loop() {
  unsigned long currentTime = millis();

  // บันทึกข้อมูลทุก 5 วินาที
  if (currentTime - lastLogTime >= LOG_INTERVAL) {
    lastLogTime = currentTime;

    // จำลองค่า sensor (ในการใช้งานจริง ให้อ่านจาก sensor จริง)
    float temperature = random(200, 350) / 10.0;  // 20.0 - 35.0 C
    float humidity = random(400, 800) / 10.0;      // 40.0 - 80.0 %
    int light = random(100, 1000);                 // Lux

    // สร้าง timestamp ง่ายๆ
    char timestamp[20];
    sprintf(timestamp, "%02d:%02d:%02d",
            (int)(currentTime / 3600000) % 24,
            (int)(currentTime / 60000) % 60,
            (int)(currentTime / 1000) % 60);

    // เปิดไฟล์เพื่อเขียน
    File file = SD.open(LOG_FILE, FILE_APPEND);
    if (file) {
      // เขียนข้อมูลในรูปแบบ CSV
      file.printf("%s,%.1f,%.1f,%d\n", timestamp, temperature, humidity, light);
      file.close();

      // กระพริบ LED แสดงว่าบันทึกสำเร็จ
      digitalWrite(LED_PIN, HIGH);
      delay(100);
      digitalWrite(LED_PIN, LOW);

      Serial.printf("[%s] บันทึก: %.1f°C, %.1f%%, %d lux\n",
                   timestamp, temperature, humidity, light);
    } else {
      Serial.println("ไม่สามารถเปิดไฟล์เพื่อเขียนได้!");
    }
  }
}

📁 SPIFFS vs LittleFS

นอกจาก SD Card แล้ว ESP32 ยังมี Flash filesystem ภายในสำหรับเก็บไฟล์ขนาดเล็ก เช่น ไฟล์ config, HTML สำหรับ web server หรือ certificates:

เปรียบเทียบ SPIFFS และ LittleFS

คุณสมบัติSPIFFSLittleFS
ความเร็วปานกลางเร็วกว่า ✅
ประสิทธิภาพพื้นที่ไม่ดี (มี waste)ดีกว่า ✅
ความเสถียรดีดีกว่า ✅
ความยาวไฟล์สูงสุด~2GB~2GB

💡 คำแนะนำ: สำหรับโปรเจกต์ใหม่ ให้ใช้ LittleFS เพราะมีประสิทธิภาพดีกว่า และ Espressif แนะนำให้ใช้แทน SPIFFS

ตัวอย่าง: ใช้งาน LittleFS

#include "LittleFS.h"

// ชื่อไฟล์ config
const char* CONFIG_FILE = "/config.json";

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

  // เริ่มต้น LittleFS
  if (!LittleFS.begin(true)) { // true = format ถ้ายังไม่ได้ mount
    Serial.println("LittleFS ล้มเหลว!");
    return;
  }
  Serial.println("LittleFS พร้อมใช้งาน!");

  // เขียนไฟล์ config
  writeFile(LittleFS, CONFIG_FILE, "{\"wifi_ssid\":\"MyWiFi\",\"interval\":5000}");

  // อ่านไฟล์ config
  String config = readFile(LittleFS, CONFIG_FILE);
  Serial.println("Config: " + config);

  // แสดงข้อมูล filesystem
  Serial.printf("Total: %d bytes\n", LittleFS.totalBytes());
  Serial.printf("Used: %d bytes\n", LittleFS.usedBytes());
}

void loop() {
  // ไม่ต้องทำอะไรใน loop
}

// ฟังก์ชันเขียนไฟล์
void writeFile(fs::FS &fs, const char *path, const char *message) {
  File file = fs.open(path, FILE_WRITE);
  if (!file) {
    Serial.println("ไม่สามารถเปิดไฟล์เพื่อเขียน");
    return;
  }
  if (file.print(message)) {
    Serial.println("เขียนไฟล์สำเร็จ");
  } else {
    Serial.println("เขียนไฟล์ล้มเหลว");
  }
  file.close();
}

// ฟังก์ชันอ่านไฟล์
String readFile(fs::FS &fs, const char *path) {
  File file = fs.open(path);
  if (!file || file.isDirectory()) {
    Serial.println("ไม่สามารถเปิดไฟล์เพื่ออ่าน");
    return String();
  }
  String content = file.readString();
  file.close();
  return content;
}

📊 โปรเจกต์จริง: Data Logger สำหรับ Smart Farm

นี่คือตัวอย่างโค้ดสมบูรณ์สำหรับโปรเจกต์ data logging จริง ที่รวม SD Card + DHT Sensor + Deep Sleep:

#include "SD.h"
#include "SPI.h"
#include "DHT.h"
#include "WiFi.h"

// กำหนดขาและค่าคงที่
#define SD_CS 5
#define DHTPIN 4
#define DHTTYPE DHT22
#define LED_PIN 2

// ตั้งค่า Deep Sleep (เวลานอน)
#define uS_TO_S_FACTOR 1000000  // แปลง microsecond เป็น second
#define TIME_TO_SLEEP  300      // นอน 5 นาที (300 วินาที)

DHT dht(DHTPIN, DHTTYPE);

const char* LOG_FILE = "/smartfarm_log.csv";

void setup() {
  Serial.begin(115200);
  pinMode(LED_PIN, OUTPUT);
  dht.begin();

  // แสดงสถานะ wake up
  esp_sleep_wakeup_cause_t wakeup_reason = esp_sleep_get_wakeup_cause();
  if (wakeup_reason == ESP_SLEEP_WAKEUP_TIMER) {
    Serial.println("ตื่นจาก Deep Sleep เพื่อบันทึกข้อมูล");
  } else {
    Serial.println("เริ่มต้นระบบ Smart Farm Data Logger");
  }

  // เริ่มต้น SD Card
  if (!SD.begin(SD_CS)) {
    Serial.println("SD Card initialization failed!");
    goToDeepSleep();
  }

  // อ่านค่า sensor
  float humidity = dht.readHumidity();
  float temperature = dht.readTemperature();

  // ตรวจสอบว่าอ่านค่าได้หรือไม่
  if (isnan(humidity) || isnan(temperature)) {
    Serial.println("อ่านค่าจาก DHT ล้มเหลว!");
    goToDeepSleep();
  }

  // สร้าง timestamp
  char timestamp[25];
  struct tm timeinfo;
  if (!getLocalTime(&timeinfo)) {
    // ถ้าไม่มี NTP ให้ใช้ millis()
    sprintf(timestamp, "%lu", millis() / 1000);
  } else {
    strftime(timestamp, sizeof(timestamp), "%Y-%m-%d %H:%M:%S", &timeinfo);
  }

  // บันทึกข้อมูล
  logData(timestamp, temperature, humidity);

  // แสดงสถานะบน Serial Monitor
  Serial.printf("[%s] Temp: %.1f°C, Humidity: %.1f%%\n",
               timestamp, temperature, humidity);

  // กระพริบ LED
  for (int i = 0; i < 3; i++) {
    digitalWrite(LED_PIN, HIGH);
    delay(100);
    digitalWrite(LED_PIN, LOW);
    delay(100);
  }

  // เข้าสู่โหมด Deep Sleep
  Serial.println("เข้าสู่ Deep Sleep 5 นาที...");
  goToDeepSleep();
}

void loop() {
  // ไม่ต้องทำอะไรใน loop เพราะใช้ Deep Sleep
}

void logData(const char* timestamp, float temp, float humidity) {
  File file = SD.open(LOG_FILE, FILE_APPEND);
  if (!file) {
    Serial.println("ไม่สามารถเปิดไฟล์ logging");
    return;
  }

  // สร้าง header ถ้าไฟล์ว่าง
  if (file.size() == 0) {
    file.println("Timestamp,Temperature,Humidity,Battery");
  }

  // อ่านแรงดันแบตเตอรี่ (ถ้ามีวงจรวัดแรงดัน)
  float battery = readBattery();

  // เขียนข้อมูล
  file.printf("%s,%.1f,%.1f,%.2f\n", timestamp, temp, humidity, battery);
  file.close();

  Serial.println("บันทึกข้อมูลลง SD Card สำเร็จ");
}

float readBattery() {
  // วงจรแบตเตอรี่ divider: 100k + 100k
  // ADC range: 0-4095, Reference: 3.3V
  int adcValue = analogRead(34); // GPIO34 สำหรับอ่านแรงดัน
  float voltage = (adcValue / 4095.0) * 3.3 * 2; // x2 เพราะ voltage divider
  return voltage;
}

void goToDeepSleep() {
  Serial.println("กำลังเข้าสู่ Deep Sleep...");
  esp_sleep_enable_timer_wakeup(TIME_TO_SLEEP * uS_TO_S_FACTOR);
  esp_deep_sleep_start();
}

🔧 แก้ปัญหาที่พบบ่อย

ปัญหา: SD Card initialization failed

อาการ: ขึ้นข้อความ "SD Card initialization failed!" บน Serial Monitor

สาเหตุ:

  • ต่อสายผิดขา
  • SD Card เสียหรือ format ไม่ถูกต้อง
  • ใช้แรงดันไฟผิด (บาง module ต้องการ 5V)
  • ขา SPI ไม่ตรงกับโค้ด

วิธีแก้:

  • ตรวจสอบการต่อสายทุกขา
  • ลอง format SD Card เป็น FAT32
  • เปลี่ยน SD Card ใบใหม่
  • ตรวจสอบว่า module ต้องการ 3.3V หรือ 5V

ปัญหา: เขียนไฟล์ไม่ได้หรือข้อมูลเสียหาย

สาเหตุ:

  • ปิดไฟล์ไม่ครบก่อน ESP32 รีเซ็ต
  • ถอด SD Card ขณะกำลังเขียนข้อมูล
  • พื้นที่เต็ม

วิธีแก้:

  • เรียก file.close() หลังเขียนทุกครั้ง
  • ตรวจสอบพื้นที่ว่างด้วย SD.usedBytes()
  • ใช้ FILE_APPEND แทน FILE_WRITE ถ้าต่อท้าย

ปัญหา: LittleFS ไม่ mount ได้

สาเหตุ: ยังไม่เคย format หรือ filesystem เสียหาย

วิธีแก้:

  • ใช้ LittleFS.begin(true) เพื่อ format ครั้งแรก
  • ระวัง: true จะลบข้อมูลทั้งหมด!

✅ สรุป

ในบทความนี้เราได้เรียนรู้:

  • การต่อ SD Card Module กับ ESP32 ผ่าน SPI
  • การอ่านและเขียนไฟล์พื้นฐานบน SD Card
  • สร้าง Data Logger สำหรับบันทึกข้อมูล sensor เป็น CSV
  • เปรียบเทียบ SPIFFS และ LittleFS สำหรับ flash filesystem
  • วิธีแก้ปัญหาที่พบบ่อยกับ SD Card

🚀 ถัดไป

© 2026 CynoIoT. สงวนลิขสิทธิ์.

ทำด้วย ❤️ สำหรับชุมชน IoT ไทย