IoTLabs

Nghiên cứu, Sáng tạo và Thử nghiệm

Series ESP32 & Cảm biến: Bài 21 – Đọc thẻ nhớ MicroSD: ghi log dữ liệu & theo dõi realtime

Thiết bị không phải lúc nào cũng online. Vì vậy ngoài realtime MQTT, bạn cần thêm khả năng ghi log vào thẻ nhớ MicroSD để:

  • Lưu dữ liệu khi mất WiFi/MQTT
  • Ghi lịch sử để debug
  • Đồng bộ lại khi online (sẽ làm sâu hơn ở các bài store-and-forward)

Bài này gồm 2 phần code:

  1. Ghi log CSV vào thẻ MicroSD
  2. Vừa ghi log vừa publish MQTT realtime lên IoTLabs Cloud

1. MicroSD trong IoT dùng để làm gì?

MicroSD thường dùng cho:

  • Data logger (nhiệt độ, độ ẩm, áp suất…)
  • Lưu event (RFID scan, door open…)
  • Lưu cấu hình/backup
  • Lưu “queue” dữ liệu để sync sau

👉 Đây là nền tảng để thiết bị chạy bền – không mất dữ liệu.

2. Chuẩn bị phần cứng & lưu ý quan trọng

Thiết bị

  • ESP32-C3 SuperMini
  • Module MicroSD (SPI)
  • Thẻ MicroSD (FAT32)

Lưu ý nguồn

  • MicroSD có thể “hút dòng” lúc ghi → nếu reset ngẫu nhiên:
    • dùng nguồn ổn định
    • dây ngắn, GND tốt
    • thêm tụ 100µF gần module (nếu cần)

3. Nối dây MicroSD (SPI)

MicroSD module thường có các chân: CS, SCK, MOSI, MISO, VCC, GND

Kết nối SPI (theo cùng nhóm với bài RC522 để dễ nhớ):

MicroSDESP32-C3
CSGPIO10
SCKGPIO6
MOSIGPIO7
MISOGPIO2
VCC3.3V
GNDGND

📌 Nếu bạn đang dùng RC522 cùng SPI: phải dùng CS khác nhau (mỗi thiết bị 1 CS).

4. Format log đề xuất (CSV)

Mỗi dòng = 1 bản ghi:

ts,temperature,humidity,note
1760000000,26.8,63.2,ok

CSV có lợi:

  • Mở bằng Excel nhanh
  • Debug dễ
  • Nhẹ và phù hợp IoT

5. Ví dụ 1: Ghi log vào MicroSD (CSV)

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

#define SD_CS 10

bool sdReady = false;

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

  SPI.begin(6, 2, 7, SD_CS); // SCK, MISO, MOSI, SS/CS

  if (!SD.begin(SD_CS)) {
    Serial.println("SD init failed");
    return;
  }

  sdReady = true;
  Serial.println("SD ready");

  // Tạo header nếu file chưa có
  if (!SD.exists("/log.csv")) {
    File f = SD.open("/log.csv", FILE_WRITE);
    if (f) {
      f.println("ts,temperature,humidity,note");
      f.close();
    }
  }
}

void loop() {
  if (!sdReady) {
    delay(2000);
    return;
  }

  long ts = millis() / 1000;           // demo: sau sẽ thay bằng RTC
  float temperature = random(200, 350) / 10.0;
  float humidity = random(400, 800) / 10.0;

  String line = String(ts) + "," +
                String(temperature, 1) + "," +
                String(humidity, 1) + ",ok";

  File f = SD.open("/log.csv", FILE_APPEND);
  if (f) {
    f.println(line);
    f.close();
    Serial.println("Wrote: " + line);
  } else {
    Serial.println("Open file failed");
  }

  delay(5000);
}

6. Chuẩn dữ liệu realtime MQTT (nhắc lại)

{
  "ts": 1760000000,
  "metrics": {
    "temperature": 26.8,
    "humidity": 63.2
  }
}

7. Ví dụ 2: Vừa ghi log MicroSD vừa publish MQTT realtime

#include <WiFi.h>
#include <WiFiClientSecure.h>
#include <PubSubClient.h>
#include <SPI.h>
#include <SD.h>

#define SD_CS 10

// ===== WIFI / MQTT CONFIG =====
const char* WIFI_SSID = "YOUR_WIFI";
const char* WIFI_PASS = "YOUR_PASS";

const char* MQTT_HOST = "mqtt.iotlabs.vn";
const int MQTT_PORT = 8883;
const char* MQTT_USER = "YOUR_MQTT_USER";
const char* MQTT_PASS = "YOUR_MQTT_PASS";

const char* MQTT_TOPIC =
  "iotlabs/<orgId>/devices/<deviceId>/telemetry";

WiFiClientSecure net;
PubSubClient mqtt(net);

bool sdReady = false;
unsigned long lastSend = 0;

void connectWiFi() {
  WiFi.begin(WIFI_SSID, WIFI_PASS);
  while (WiFi.status() != WL_CONNECTED) delay(500);
}

void connectMQTT() {
  net.setInsecure();
  mqtt.setServer(MQTT_HOST, MQTT_PORT);
  while (!mqtt.connected()) {
    mqtt.connect("esp32c3-sdlog", MQTT_USER, MQTT_PASS);
    delay(1000);
  }
}

void setupSD() {
  SPI.begin(6, 2, 7, SD_CS);

  if (!SD.begin(SD_CS)) {
    Serial.println("SD init failed");
    return;
  }
  sdReady = true;

  if (!SD.exists("/log.csv")) {
    File f = SD.open("/log.csv", FILE_WRITE);
    if (f) {
      f.println("ts,temperature,humidity");
      f.close();
    }
  }
  Serial.println("SD ready");
}

void appendCSV(long ts, float t, float h) {
  if (!sdReady) return;

  String line = String(ts) + "," + String(t, 1) + "," + String(h, 1);
  File f = SD.open("/log.csv", FILE_APPEND);
  if (f) {
    f.println(line);
    f.close();
  }
}

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

  setupSD();
  connectWiFi();
  connectMQTT();
}

void loop() {
  if (!mqtt.connected()) connectMQTT();
  mqtt.loop();

  if (millis() - lastSend > 5000) {
    lastSend = millis();

    long ts = millis() / 1000;  // demo: bài sau sẽ thay bằng DS3231
    float t = random(200, 350) / 10.0;
    float h = random(400, 800) / 10.0;

    // 1) Log vào SD
    appendCSV(ts, t, h);

    // 2) Publish realtime
    String payload = "{";
    payload += "\"ts\":" + String(ts) + ",";
    payload += "\"metrics\":{";
    payload += "\"temperature\":" + String(t, 1) + ",";
    payload += "\"humidity\":" + String(h, 1);
    payload += "}}";

    mqtt.publish(MQTT_TOPIC, payload.c_str());
    Serial.println(payload);
  }
}

8. Kinh nghiệm thực tế để log ổn định

  • Dùng FAT32, format thẻ trước khi test
  • Ghi theo chu kỳ (5–60s) thay vì ghi liên tục
  • Tránh mở/đóng file quá thường xuyên (bài tối ưu sau)
  • Nếu cần độ tin cậy cao:
    • thêm RTC DS3231 để timestamp “đúng”
    • chuẩn bị “queue file” để sync khi online

9. Tổng kết

Sau bài này, bạn đã:

  • Biết dùng MicroSD để ghi log dữ liệu
  • Kết hợp log offline + MQTT realtime
  • Có nền tảng để làm bước tiếp theo: store-and-forward

👉 Gợi ý mở rộng:

  • “Đồng bộ log MicroSD lên Cloud khi có mạng”
  • “Thiết kế queue file chống mất dữ liệu”