เนื้อหาในบทความ
📖 บทนำ
การบันทึกข้อมูล (Data Logging) เป็นส่วนสำคัญของโปรเจกต์ IoT ไม่ว่าจะเป็นสถานีตรวจสอบสภาพอากาศ ระบบตรวจสอบอุณหภูมิ หรือการติดตามการใช้พลังงาน SD Card เป็นทางเลือกที่ยอดเยี่ยมสำหรับการบันทึกข้อมูลเนื่องจาก:
- 💾 ความจุขนาดใหญ่ (GB ถึง TB)
- 💵 ราคาถูก
- 🔌 ถอดปลั๊กได้ง่าย
- 💻 เข้ากันได้กับคอมพิวเตอร์ทุกเครื่อง
- 📊 ข้อมูลสามารถนำไปใช้ใน Excel ได้ทันที
ในบทความนี้คุณจะได้เรียนรู้วิธีการบันทึกข้อมูลเซ็นเซอร์ลง SD Card ในรูปแบบ CSV (Comma-Separated Values) ซึ่งเป็นรูปแบบมาตรฐานที่ Excel และ Google Sheets รองรับ ทำให้คุณสามารถวิเคราะห์ข้อมูล สร้างกราฟ และสร้างรายงานได้อย่างง่ายดาย
🤔 CSV คืออะไร?
CSV (Comma-Separated Values) เป็นรูปแบบไฟล์ข้อความที่เก็บข้อมูลตาราง โดยแต่ละค่าจะคั่นด้วยจุลภาค (comma) แต่ละบรรทัดแทนหนึ่งแถวของข้อมูล
Timestamp,Temperature,Humidity
2026-03-16 14:00:00,28.5,65.2
2026-03-16 14:01:00,28.7,64.8
2026-03-16 14:02:00,28.6,65.0
ข้อดีของ CSV:
- ✅ เปิดได้กับ Excel, Google Sheets, Numbers
- ✅ ไฟล์มีขนาดเล็ก
- ✅ แก้ไขได้ง่ายด้วย Text Editor
- ✅ เข้ากันได้กับภาษาโปรแกรมทุกภาษา
🔧 อุปกรณ์ที่ต้องใช้
Hardware
- ESP32 Development Board - ฿80-150
- MicroSD Card Module - ฿20-40
- MicroSD Card (4GB ขึ้นไป) - ฿50-150
- DHT22 Temperature & Humidity Sensor - ฿40-60
- Breadboard & Jumper Wires - ฿30-50
Software
- Arduino IDE หรือ PlatformIO
- Excel, Google Sheets หรือ Numbers
- Library: SD, FS, DHT sensor library
💡 เคล็ดลับ: SD Card Module ส่วนใหญ่ใช้ SPI protocol ซึ่ง ESP32 รองรับในตัว ตรวจสอบให้แน่ใจว่า SD Card ได้รับการจัดรูปแบบ (format) เป็น FAT32 หรือ exFAT
🔌 การต่อสาย
SD Card Module ต่อกับ ESP32
| SD Card Module | ESP32 |
|---|---|
| CS (Chip Select) | GPIO 5 |
| SCK (Clock) | GPIO 18 |
| MOSI (Master Out Slave In) | GPIO 23 |
| MISO (Master In Slave Out) | GPIO 19 |
| VCC | 3.3V |
| GND | GND |
DHT22 Sensor ต่อกับ ESP32
| DHT22 | ESP32 |
|---|---|
| VCC | 3.3V |
| Data | GPIO 4 |
| GND | GND |
⚠️ ข้อควรระวัง: ต่อตัวต้านทาน 10kΩ ระหว่าง VCC และ Data pin ของ DHT22 เพื่อดึงสัญญาณขึ้น (pull-up resistor)
💻 บันทึกข้อมูลเซ็นเซอร์เดียว
นี่คือโค้ดพื้นฐานสำหรับบันทึกข้อมูลอุณหภูมิจาก DHT22 ลง SD Card ในรูปแบบ CSV:
/**
* ESP32 SD Card CSV Data Logging - Single Sensor
* บันทึกข้อมูลอุณหภูมิและความชื้นลง SD Card
*/
#include <FS.h>
#include <SD.h>
#include <SPI.h>
#include <DHT.h>
// กำหนดขา GPIO
#define SD_CS 5
#define DHTPIN 4
#define DHTTYPE DHT22
// สร้างออบเจกต์ DHT
DHT dht(DHTPIN, DHTTYPE);
// ชื่อไฟล์ CSV
const char* filename = "/sensor_data.csv";
// ช่วงเวลาในการบันทึก (มิลลิวินาที)
const unsigned long LOG_INTERVAL = 60000; // 1 นาที
unsigned long lastLogTime = 0;
void setup() {
Serial.begin(115200);
// เริ่มต้นใช้งาน DHT
dht.begin();
// เริ่มต้นใช้งาน SD Card
Serial.println("กำลังเริ่มต้น SD Card...");
if (!SD.begin(SD_CS)) {
Serial.println("ไม่สามารถเริ่มต้น SD Card ได้!");
Serial.println("ตรวจสอบการต่อสายและ SD Card");
while (1) {
delay(1000);
}
}
Serial.println("SD Card เริ่มต้นสำเร็จ!");
// ตรวจสอบว่ามีไฟล์อยู่แล้วหรือไม่
if (!SD.exists(filename)) {
// สร้างไฟล์ใหม่และเขียนหัวตาราง
File file = SD.open(filename, FILE_WRITE);
if (file) {
file.println("Timestamp,Temperature,Humidity");
file.close();
Serial.println("สร้างไฟล์ CSV ใหม่และเขียนหัวตารางแล้ว");
} else {
Serial.println("ไม่สามารถสร้างไฟล์ได้!");
}
} else {
Serial.println("ไฟล์ CSV มีอยู่แล้ว กำลังเพิ่มข้อมูล...");
}
Serial.println("เริ่มต้นการบันทึกข้อมูล...");
Serial.println("Timestamp,Temperature,Humidity");
}
void loop() {
// ตรวจสอบเวลาที่จะบันทึกข้อมูล
unsigned long currentTime = millis();
if (currentTime - lastLogTime >= LOG_INTERVAL) {
lastLogTime = currentTime;
// อ่านค่าจาก DHT22
float temperature = dht.readTemperature();
float humidity = dht.readHumidity();
// ตรวจสอบว่าอ่านค่าสำเร็จหรือไม่
if (isnan(temperature) || isnan(humidity)) {
Serial.println("ไม่สามารถอ่านค่าจาก DHT22 ได้!");
return;
}
// สร้าง timestamp
String timestamp = getTimestamp();
// สร้างข้อมูล CSV
String csvData = timestamp + "," +
String(temperature, 1) + "," +
String(humidity, 1);
// เขียนข้อมูลลง SD Card
File file = SD.open(filename, FILE_APPEND);
if (file) {
file.println(csvData);
file.close();
Serial.print("บันทึกข้อมูล: ");
Serial.println(csvData);
} else {
Serial.println("ไม่สามารถเปิดไฟล์เพื่อเขียนข้อมูลได้!");
}
}
}
// ฟังก์ชันสร้าง timestamp
String getTimestamp() {
// หมายเหตุ: ESP32 มี RTC ภายใน แต่ต้องตั้งค่าเวลาครั้งแรก
// ในตัวอย่างนี้ใช้ millis() แทน
unsigned long seconds = millis() / 1000;
unsigned long minutes = seconds / 60;
unsigned long hours = minutes / 60;
unsigned long days = hours / 24;
return String(days) + "d " +
String(hours % 24) + "h " +
String(minutes % 60) + "m " +
String(seconds % 60) + "s";
} ✅ ผลลัพธ์: ข้อมูลจะถูกบันทึกในไฟล์ sensor_data.csv ทุกๆ 1 นาที ไฟล์สามารถนำไปเปิดใน Excel ได้ทันที
📊 บันทึกข้อมูลหลายเซ็นเซอร์
สำหรับโปรเจกต์ที่ซับซ้อนขึ้น เราสามารถบันทึกข้อมูลจากหลายเซ็นเซอร์พร้อมกันได้:
/**
* ESP32 SD Card CSV Data Logging - Multiple Sensors
* บันทึกข้อมูลหลายเซ็นเซอร์พร้อมกัน
*/
#include <FS.h>
#include <SD.h>
#include <SPI.h>
#include <DHT.h>
#include <Wire.h>
#include <Adafruit_BMP280.h>
// กำหนดขา GPIO
#define SD_CS 5
#define DHTPIN 4
#define DHTTYPE DHT22
#define LDR_PIN 34
// สร้างออบเจกต์เซ็นเซอร์
DHT dht(DHTPIN, DHTTYPE);
Adafruit_BMP280 bmp;
// ชื่อไฟล์ CSV
const char* filename = "/multi_sensor_data.csv";
const unsigned long LOG_INTERVAL = 30000; // 30 วินาที
unsigned long lastLogTime = 0;
void setup() {
Serial.begin(115200);
// เริ่มต้นใช้งานเซ็นเซอร์
dht.begin();
// เริ่มต้นใช้งาน BMP280 (Pressure Sensor)
if (!bmp.begin(0x76)) {
Serial.println("ไม่พบ BMP280!");
}
// ตั้งค่า LDR pin
pinMode(LDR_PIN, INPUT);
// เริ่มต้นใช้งาน SD Card
if (!SD.begin(SD_CS)) {
Serial.println("ไม่สามารถเริ่มต้น SD Card ได้!");
while (1) delay(1000);
}
// สร้างไฟล์ใหม่พร้อมหัวตาราง
if (!SD.exists(filename)) {
File file = SD.open(filename, FILE_WRITE);
if (file) {
// หัวตาราง CSV
file.println("Timestamp,Temperature,DHT_Humidity,BMP_Pressure,BMP_Altitude,Light_Level");
file.close();
Serial.println("สร้างไฟล์ CSV ใหม่แล้ว");
}
}
Serial.println("เริ่มต้นการบันทึกข้อมูลหลายเซ็นเซอร์...");
}
void loop() {
unsigned long currentTime = millis();
if (currentTime - lastLogTime >= LOG_INTERVAL) {
lastLogTime = currentTime;
// อ่านค่าจาก DHT22
float tempDHT = dht.readTemperature();
float humidity = dht.readHumidity();
// อ่านค่าจาก BMP280
float pressure = bmp.readPressure() / 100.0F; // hPa
float altitude = bmp.readAltitude(1013.25); // มิลลิเมตร
// อ่านค่าจาก LDR (Light Sensor)
int lightLevel = analogRead(LDR_PIN);
// ตรวจสอบความถูกต้องของข้อมูล
if (isnan(tempDHT) || isnan(humidity)) {
Serial.println("ข้อผิดพลาดในการอ่านค่าจาก DHT22!");
return;
}
// สร้างข้อมูล CSV
String csvData = getTimestamp() + "," +
String(tempDHT, 1) + "," +
String(humidity, 1) + "," +
String(pressure, 1) + "," +
String(altitude, 1) + "," +
String(lightLevel);
// เขียนข้อมูลลง SD Card
File file = SD.open(filename, FILE_APPEND);
if (file) {
file.println(csvData);
file.close();
Serial.print("บันทึกข้อมูล: ");
Serial.println(csvData);
}
}
}
String getTimestamp() {
unsigned long seconds = millis() / 1000;
return String(seconds) + "s";
}💡 เคล็ดลับ: คุณสามารถเพิ่มเซ็นเซอร์เพิ่มเติมได้ เช่น MQ Gas Sensor, Soil Moisture Sensor, หรือ Current Sensor โดยเพิ่มคอลัมน์ใน CSV
📂 อ่านข้อมูลใน Excel
วิธีที่ 1: เปิดไฟล์โดยตรง
- ถอด SD Card จาก ESP32
- ใส่ SD Card ลงในคอมพิวเตอร์ (ใช้ Card Reader)
- เปิด Windows Explorer หรือ Finder
- ดับเบิลคลิกไฟล์
sensor_data.csv - Excel จะเปิดไฟล์โดยอัตโนมัติ
วิธีที่ 2: นำเข้าข้อมูล (Import)
- เปิด Excel และสร้าง Workbook ใหม่
- ไปที่ Data → From Text/CSV
- เลือกไฟล์ CSV จาก SD Card
- Excel จะแสดงตัวอย่างข้อมูล ให้ตรวจสอบ
- คลิก Load เพื่อนำเข้าข้อมูล
Google Sheets
- ไปที่ Google Sheets
- คลิก File → Import
- ลากไฟล์ CSV มาวาง หรือคลิก Browse
- เลือก Replace spreadsheet หรือ Insert new sheet
- คลิก Import data
📈 การวิเคราะห์ข้อมูล
สร้างกราฟ (Charts)
- เลือกข้อมูลทั้งหมด (รวมหัวตาราง)
- ไปที่ Insert → Chart
- เลือกประเภทกราฟ:
- Line Chart: เหมาะกับการดูแนวโน้มตามเวลา
- Scatter Plot: เหมาะกับการดูความสัมพันธ์ระหว่างตัวแปร
- Bar Chart: เหมาะกับการเปรียบเทียบ
ฟังก์ชัน Excel ที่มีประโยชน์
| ฟังก์ชัน | คำอธิบาย | ตัวอย่าง |
|---|---|---|
| =AVERAGE(B2:B1000) | ค่าเฉลี่ย | อุณหภูมิเฉลี่ย |
| =MAX(B2:B1000) | ค่าสูงสุด | อุณหภูมิสูงสุด |
| =MIN(B2:B1000) | ค่าต่ำสุด | อุณหภูมิต่ำสุด |
| =STDEV(B2:B1000) | ส่วนเบี่ยงเบนมาตรฐาน | ความผันผวน |
| =CORREL(B2:B1000, C2:C1000) | สหสัมพันธ์ | ความสัมพันธ์ระหว่างอุณหภูมิและความชื้น |
ตัวอย่างการวิเคราะห์
💡 ตัวอย่าง: หากคุณมีข้อมูลอุณหภูมิและความชื้น คุณสามารถวิเคราะห์ได้ว่า:
- อุณหภูมิเฉลี่ยต่อวันคือเท่าไร
- ช่วงเวลาไหนที่อุณหภูมิสูงสุด
- อุณหภูมิและความชื้นมีความสัมพันธ์กันหรือไม่
🏠 โปรเจกต์ตัวอย่าง: สถานีตรวจสอบสภาพอากาศ
นี่คือโปรเจกต์สมบูรณ์ที่รวมทุกอย่างเข้าด้วยกัน:
ฟีเจอร์
- บันทึกข้อมูลทุก 5 นาที
- บันทึกข้อมูล 6 พารามิเตอร์: อุณหภูมิ, ความชื้น, ความดัน, ความสูง, แสง, ดัชนี UV
- สร้างไฟล์ใหม่ทุกวัน (เพื่อป้องกันไฟล์ใหญ่เกินไป)
- แสดงสถานะบน Serial Monitor
- ตรวจสอบพื้นที่ว่าง SD Card
/**
* ESP32 Weather Station with SD Card Logging
* สถานีตรวจสอบสภาพอากาศพร้อมบันทึกข้อมูล SD Card
*/
#include <FS.h>
#include <SD.h>
#include <SPI.h>
#include <DHT.h>
#include <Wire.h>
#include <Adafruit_BMP280.h>
#include <WiFi.h>
// กำหนดขา GPIO
#define SD_CS 5
#define DHTPIN 4
#define DHTTYPE DHT22
#define LDR_PIN 34
#define UV_PIN 35
DHT dht(DHTPIN, DHTTYPE);
Adafruit_BMP280 bmp;
const unsigned long LOG_INTERVAL = 300000; // 5 นาที
unsigned long lastLogTime = 0;
String getCurrentFilename() {
// สร้างชื่อไฟล์ตามวันที่: data_YYYYMMDD.csv
time_t now;
struct tm timeinfo;
// ใช้ NTP หรือ RTC ในการรับเวลาจริง
// ในตัวอย่างนี้ใช้เวลาจาก millis()
unsigned long days = millis() / (1000 * 60 * 60 * 24);
return "/data_day" + String(days) + ".csv";
}
void createNewFile(String filename) {
File file = SD.open(filename, FILE_WRITE);
if (file) {
file.println("Timestamp,Temperature,Humidity,Pressure,Altitude,Light,UV_Index");
file.close();
Serial.println("สร้างไฟล์ใหม่: " + filename);
}
}
void checkSDCardSpace() {
unsigned long totalBytes = SD.totalBytes();
unsigned long usedBytes = SD.usedBytes();
float usedPercent = (usedBytes * 100.0) / totalBytes;
Serial.print("พื้นที่ใช้: ");
Serial.print(usedPercent);
Serial.println("%");
if (usedPercent > 90) {
Serial.println("⚠️ แจ้งเตือน: SD Card เกือบเต็ม!");
}
}
void setup() {
Serial.begin(115200);
dht.begin();
if (!bmp.begin(0x76)) {
Serial.println("ไม่พบ BMP280!");
}
pinMode(LDR_PIN, INPUT);
pinMode(UV_PIN, INPUT);
if (!SD.begin(SD_CS)) {
Serial.println("ไม่สามารถเริ่มต้น SD Card ได้!");
while (1) delay(1000);
}
Serial.println("สถานีตรวจสอบสภาพอากาศเริ่มทำงาน!");
Serial.println("บันทึกข้อมูลทุก 5 นาที");
Serial.println("--------------------------------");
}
void loop() {
unsigned long currentTime = millis();
if (currentTime - lastLogTime >= LOG_INTERVAL) {
lastLogTime = currentTime;
// อ่านค่าจากเซ็นเซอร์ทั้งหมด
float temp = dht.readTemperature();
float humidity = dht.readHumidity();
float pressure = bmp.readPressure() / 100.0F;
float altitude = bmp.readAltitude(1013.25);
int light = analogRead(LDR_PIN);
int uv = analogRead(UV_PIN);
if (isnan(temp) || isnan(humidity)) {
Serial.println("ข้อผิดพลาดในการอ่านค่าจาก DHT22!");
return;
}
// สร้างชื่อไฟล์และตรวจสอบว่ามีไฟล์อยู่หรือไม่
String filename = getCurrentFilename();
if (!SD.exists(filename)) {
createNewFile(filename);
}
// สร้างข้อมูล CSV
String csvData = getTimestamp() + "," +
String(temp, 1) + "," +
String(humidity, 1) + "," +
String(pressure, 1) + "," +
String(altitude, 1) + "," +
String(light) + "," +
String(uv);
// เขียนข้อมูลลง SD Card
File file = SD.open(filename, FILE_APPEND);
if (file) {
file.println(csvData);
file.close();
// แสดงผลบน Serial Monitor
Serial.println("บันทึกข้อมูลสำเร็จ!");
Serial.print("อุณหภูมิ: ");
Serial.print(temp);
Serial.println("°C");
Serial.print("ความชื้น: ");
Serial.print(humidity);
Serial.println("%");
Serial.print("ความดัน: ");
Serial.print(pressure);
Serial.println(" hPa");
Serial.println("--------------------------------");
// ตรวจสอบพื้นที่ว่าง SD Card ทุกๆ 1 ชั่วโมง
static unsigned long lastCheck = 0;
if (currentTime - lastCheck > 3600000) {
checkSDCardSpace();
lastCheck = currentTime;
}
}
}
}
String getTimestamp() {
unsigned long seconds = millis() / 1000;
unsigned long minutes = seconds / 60;
unsigned long hours = minutes / 60;
return String(hours % 24) + ":" +
String(minutes % 60) + ":" +
String(seconds % 60);
}การวิเคราะห์ข้อมูล
เมื่อคุณมีข้อมูลหลายวัน คุณสามารถ:
- เปรียบเทียบสภาพอากาศระหว่างวัน
- ดูแนวโน้มระยะยาว
- หาช่วงเวลาที่ร้อน/เย็นที่สุด
- วิเคราะห์ความสัมพันธ์ระหว่างพารามิเตอร์ต่างๆ
🔧 ปัญหาที่พบบ่อยและวิธีแก้ไข
ปัญหา: SD Card initialization failed
อาการ: ESP32 แจ้งว่าไม่สามารถเริ่มต้น SD Card ได้
สาเหตุ: การต่อสายไม่ถูกต้อง, SD Card เสีย, หรือรูปแบบไฟล์ไม่รองรับ
วิธีแก้ไข:
- ตรวจสอบการต่อสาย (CS, SCK, MOSI, MISO, VCC, GND)
- ลองเปลี่ยน SD Card อื่น
- จัดรูปแบบ (format) SD Card เป็น FAT32
- ลองเปลี่ยนค่าความต้านทาน pull-up (ถ้ามี)
ปัญหา: ไม่สามารถเขียนข้อมูลลงไฟล์ได้
อาการ: ไฟล์เปิดได้แต่ไม่สามารถเขียนข้อมูลได้
สาเหตุ: SD Card เต็ม, ไฟล์เสีย, หรือ SD Card ถูกป้องกันไม่ให้เขียน (write-protected)
วิธีแก้ไข:
- ตรวจสอบพื้นที่ว่าง SD Card
- ตรวจสอบสวิตช์ lock บน SD Card
- ลบไฟล์เก่าๆ หรือใช้ SD Card ขนาดใหญ่ขึ้น
- ลองจัดรูปแบบ SD Card ใหม่
ปัญหา: ข้อมูลใน Excel ไม่เรียงอย่างถูกต้อง
อาการ: ข้อมูลทั้งหมดอยู่ในคอลัมน์เดียว
สาเหตุ: Excel ไม่รู้จักตัวคั่น (delimiter) หรือรูปแบบไฟล์
วิธีแก้ไข:
- ใช้ Data → From Text/CSV และเลือก delimiter เป็น Comma
- ตรวจสอบว่าข้อมูลมี comma เพียงพรรษ์และถูกต้อง
- ตรวจสอบว่าไม่มีอักขระพิเศษในข้อมูล
ปัญหา: Timestamp ไม่ถูกต้อง
อาการ: เวลาที่แสดงไม่ตรงกับเวลาจริง
สาเหตุ: ESP32 ไม่ได้รับเวลาจาก NTP หรือ RTC
วิธีแก้ไข:
- ใช้ NTP (Network Time Protocol) ในการรับเวลาจากอินเทอร์เน็ต
- ใช้ RTC (Real-Time Clock) module ภายใน/ภายนอก
- ตั้งค่าเวลาด้วยตนเองในโค้ด
🎉 สรุป
ในบทความนี้คุณได้เรียนรู้:
- วิธีการต่อ SD Card Module กับ ESP32
- การบันทึกข้อมูลเซ็นเซอร์ในรูปแบบ CSV
- การบันทึกข้อมูลหลายเซ็นเซอร์พร้อมกัน
- การนำเข้าและวิเคราะห์ข้อมูลใน Excel หรือ Google Sheets
- การสร้างกราฟและรายงานจากข้อมูล
- การแก้ปัญหาที่พบบ่อย
ตอนนี้คุณสามารถสร้างระบบบันทึกข้อมูลสำหรับโปรเจกต์ IoT ของคุณเองได้แล้ว!