Pendahuluan — Data Tetap Tersimpan Tanpa WiFi

Seri 2 sudah mengajarkan kirim sensor ke broker MQTT (#7), histori di MySQL (#18), dan grafik Grafana (#19). Semua itu mengasumsikan WiFi atau jaringan tersedia.

Di lapangan — kebun, greenhouse, atau titik monitoring jauh dari router — koneksi bisa putus berjam-jam. Node deep sleep (#11) bangun sebentar, baca sensor, lalu tidur lagi; tanpa penyimpanan lokal, sampel hilang selamanya.

Artikel Tier 2 ini mengajarkan SD Card microSD lewat bus SPI (bukan I2C): tulis log CSV offline, lalu sinkron ke MQTT saat WiFi kembali — melengkapi arsitektur hybrid capstone #10, ESP8266 vs ESP32 (#36), dan persiapan capstone greenhouse (#39).

Prasyarat: Paham GPIO (#3), WiFi (#4), DHT22 (#5), dan idealnya sudah pakai NTP (#34) untuk timestamp akurat di setiap baris log.

Mengapa SD Card, Bukan Hanya Cloud?

SkenarioMQTT / cloud sajaSD Card + MQTT hybrid
WiFi mati 6 jamData hilangCSV tetap terisi
Deep sleep node bateraiReconnect lambatLog dulu, upload nanti
Audit lapanganButuh laptop + brokerCabut kartu → baca di PC
Debugging firmwareSulit trace event lokalFile teks mudah dibuka

SPI vs I2C — Jangan Campur

Di artikel I2C (#13), sensor BME280 dan OLED #14 pakai dua wire (SDA/SCL) + alamat perangkat. SD Card memakai SPI — protokol berbeda:

BusWireCocok untukArtikel Seri 2
I2CSDA, SCLSensor kecil, OLED#13 BME280
SPIMOSI, MISO, SCK, CSSD Card, display cepat, LoRa#37 (ini), #26 LoRa

SPI lebih cepat untuk burst data ke kartu memori — tepat untuk append ribuan baris CSV.

Dasar SPI — Empat Sinyal Utama

  • MOSI (Master Out Slave In) — ESP32 kirim data ke SD
  • MISO (Master In Slave Out) — SD kirim balik ke ESP32
  • SCK (Clock) — tick sinkron
  • CS / SS (Chip Select) — pilih perangkat di bus yang sama

Pro tip: Satu bus SPI bisa dipakai beberapa perangkat — asalkan masing-masing punya CS pin terpisah. Jangan dua perangkat aktif CS bersamaan.

Hardware — Modul SD Card microSD

  • Modul SD Card (holder microSD + level shifter 3,3 V)
  • ESP32 DevKit
  • microSD 4–32 GB (FAT32)
  • DHT22 untuk demo log suhu/kelembaban (#5)
  • Kabel jumper · breadboard

Wiring SPI — ESP32 ke Modul SD

Pin HSPI default yang umum di tutorial ESP32 (hindari bentrok dengan flash internal):

SD ModuleESP32 GPIOFungsi SPI
CSGPIO 5Chip Select
SCK / CLKGPIO 18Clock
MOSIGPIO 23Data ke SD
MISOGPIO 19Data dari SD
VCC3,3 VJangan 5 V langsung ke GPIO
GNDGNDCommon ground
DHT22 dataGPIO 4Digital — tidak bentrok SPI

Konflik Pin — Cek Sebelum Wiring

Fungsi lainGPIOBentrok SPI?
DHT22 (#5)4Tidak
Relay (#8)26Tidak
Soil ADC (#35)34, 35Tidak
Servo (#33)27Tidak
SD CS5Hindari jika board pakai flash pada GPIO 6–11

Format File CSV di Kartu

Satu file /sensor.csv dengan header — mudah diimpor ke Excel atau script Python (#18):

timestamp_iso,unix,temp_c,hum_pct
2026-07-02T14:30:00,1782977400,28.5,62.1

Timestamp ISO dan unix mengikuti konvensi NTP (#34) — contoh unix 1782977400 agar konsisten di seluruh seri.

Library & Include

Di Arduino IDE untuk ESP32, cukup:

  • SPI.h — bus SPI bawaan
  • SD.h — filesystem FAT di kartu
  • DHT sensor library — sama seperti #5

Sketch — Init SD & Tulis Baris Pertama

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

#define SD_CS     5
#define SD_SCK   18
#define SD_MISO  19
#define SD_MOSI  23
#define DHT_PIN   4
#define DHT_TYPE DHT22

#define LOG_FILE "/sensor.csv"

DHT dht(DHT_PIN, DHT_TYPE);

bool initSD() {
  SPI.begin(SD_SCK, SD_MISO, SD_MOSI, SD_CS);
  if (!SD.begin(SD_CS, SPI)) {
    Serial.println("SD init gagal");
    return false;
  }
  if (!SD.exists(LOG_FILE)) {
    File f = SD.open(LOG_FILE, FILE_WRITE);
    if (f) {
      f.println("timestamp_iso,unix,temp_c,hum_pct");
      f.close();
    }
  }
  return true;
}

void appendLog(float t, float h, time_t unixNow) {
  struct tm ti;
  localtime_r(&unixNow, &ti);
  char iso[25];
  strftime(iso, sizeof(iso), "%Y-%m-%dT%H:%M:%S", &ti);

  File f = SD.open(LOG_FILE, FILE_APPEND);
  if (!f) return;
  f.printf("%s,%ld,%.1f,%.1f\n", iso, (long)unixNow, t, h);
  f.close();
}

void setup() {
  Serial.begin(115200);
  dht.begin();
  delay(2000);
  initSD();
}

void loop() {
  float t = dht.readTemperature();
  float h = dht.readHumidity();
  if (!isnan(t) && !isnan(h)) {
    time_t now = 1782977400; // ganti dengan NTP (#34) di produksi
    appendLog(t, h, now);
    Serial.println("Baris ditulis ke SD");
  }
  delay(60000);
}

Sinkron ke MQTT Saat WiFi Kembali

Pola hybrid — log selalu ke SD; jika WiFi connect, baca baris terakhir (atau seluruh file) dan publish ke broker Mosquitto (#16):

// Setelah WiFi + mqtt.connected()
// Topic konsisten Seri 1: kodingindonesia/esp32/dht22/data
const char* TOPIC = "kodingindonesia/esp32/dht22/data";

void publishLastSample(float t, float h, long unixTs) {
  char payload[160];
  snprintf(payload, sizeof(payload),
    "{\"unix\":%ld,\"temp\":%.1f,\"hum\":%.1f,\"source\":\"sd_sync\"}",
    unixTs, t, h);
  mqtt.publish(TOPIC, payload);
}

Subscriber Python (#18) dan Grafana (#19) tidak perlu diubah — field JSON sama dengan node MQTT langsung.

Deep Sleep + Log Satu Sampel

Gabung dengan deep sleep (#11): di setiap wake, baca DHT → appendLog() → tidur lagi. Kartu SD tetap terisi meski WiFi tidak pernah aktif selama seminggu.

Baca Kartu di PC

  1. Cabut microSD dengan aman (unmount tidak ada di bare metal — minimal stop write dengan delay setelah f.close())
  2. Colok ke reader USB di laptop
  3. Buka sensor.csv di Excel/LibreOffice
  4. Opsional: impor ke MySQL dengan script Python mirip #18 — baca CSV bukan MQTT

PlatformIO — Dependency

Di PlatformIO (#29):

[env:esp32dev]
platform = espressif32
board = esp32dev
framework = arduino
lib_deps =
    adafruit/DHT sensor library@^1.4

Library SD dan SPI sudah bundled framework Arduino ESP32.

SD vs SPIFFS vs LittleFS

MediaKapasitasCocok untuk
SD Card (SPI)GBLog bulanan, cabut & baca di PC
SPIFFS / LittleFSMB (flash chip)Config kecil, web asset OTA (#15)
MQTT → DBUnlimited (server)Dashboard real-time #19

Keamanan & Best Practice

  • Jangan cabut kartu saat SD.open(..., FILE_APPEND) aktif — selalu close()
  • Kartu FAT32 — hindari power loss saat menulis (kapasitor kecil membantu)
  • Jangan simpan password WiFi di file teks di SD — pakai GANTI_* placeholder di firmware
  • Produksi: enkripsi kartu atau fisik secure enclosure

Estimasi Biaya (Indonesia)

KomponenHarga (Rp)
ESP32 DevKit35.000 – 55.000
Modul SD Card + holder15.000 – 30.000
microSD 8 GB25.000 – 45.000
DHT2235.000 – 55.000
Total~110.000 – 185.000

Checklist Sebelum Demo

  • ☐ Kartu diformat FAT32
  • ☐ Serial: SD init gagal tidak muncul
  • ☐ File /sensor.csv ada + header benar
  • ☐ Baris baru setiap interval baca DHT
  • ☐ Cabut kartu → buka CSV di PC → data terbaca
  • ☐ Opsional: WiFi sync publish ke kodingindonesia/esp32/dht22/data

FAQ Singkat

Bisa pakai I2C untuk SD Card?
Modul SD standar di pasaran pakai SPI — I2C jarang dan lambat untuk log intensif.
SD init gagal terus?
Cek wiring CS/SCK/MOSI/MISO, format FAT32, dan supply 3,3 V stabil.
Bentrok dengan BME280 I2C?
Tidak — I2C GPIO 21/22, SPI pakai 18/19/23/5 di sketch ini.
Perlu NTP?
Sangat disarankan — tanpa NTP, timestamp placeholder tidak akurat di lapangan (#34).
ESP8266 bisa logging SD?
Bisa terbatas — lihat perbandingan #36; ESP32 lebih nyaman untuk SPI + WiFi + MQTT.

Tips & Troubleshooting

  • File corrupt: Gunakan kartu berkualitas · hindari cabut saat write · coba SD.end() sebelum sleep panjang
  • 0 byte file: Pastikan FILE_APPEND dan flush / close dipanggil
  • DHT NaN di log: Sama seperti #5 — pull-up 10kΩ, delay(2000) setelah dht.begin()
  • MQTT sync dobel: Tandai baris sudah di-upload (kolom synced=1) atau simpan offset di NVS (#12)
  • Kartu penuh: Rotasi file harian /2026-07-02.csv atau hapus baris lama

MQTT Credentials Saat Sync

Saat node kembali online dan mengirim backlog, gunakan pola kredensial yang sama dengan artikel MQTT Seri 1:

  • Broker: 192.168.1.50 (Mosquitto #16)
  • User: kindo_esp32
  • Password: GANTI_PASSWORD_MQTT — jangan hardcode di repo
  • WiFi: GANTI_NAMA_WIFI / GANTI_PASSWORD_WIFI

Placeholder ini konsisten di seluruh seri — ganti saat flash firmware ke lapangan.

FreeRTOS & SD Write

Jika firmware memakai task terpisah (FreeRTOS #31), jangan tulis SD dari dua task bersamaan tanpa mutex. Pola aman: satu task loggerTask yang dequeue sample dari queue dan append ke kartu — hindari korupsi FAT saat WiFi task dan logger task bentrok.

Perbandingan dengan ESP-NOW & LoRa

ESP-NOW (#25) dan LoRa (#26) mengirim data ke gateway — tetap butuh penyimpanan jika gateway offline. SD di node sensor adalah buffer lokal pertama sebelum data mencapai gateway LoRa → MQTT (#28).

Rotasi File Harian

Untuk deployment jangka panjang, jangan biarkan satu sensor.csv membengkak tanpa batas. Pola umum:

  • /log/2026-07-02.csv — satu file per hari (butuh NTP #34 untuk nama file)
  • Hapus file >30 hari saat kartu >80% penuh
  • Simpan offset baris terakhir yang sudah di-MQTT-kan di NVS (#12)

Rotasi ini mencegah kartu 8 GB penuh di musim hujan saat node tidak pernah di-flash ulang.

Gabung Multi-Sensor di Satu Baris

Setelah paham DHT22, tambahkan kolom dari ADC soil & LDR (#35):

timestamp_iso,unix,temp_c,hum_pct,soil_pct,light_pct

Satu baris CSV = snapshot lengkap greenhouse — siap diimpor ke MySQL (#18) tanpa ubah skema MQTT.

Node-RED & Home Assistant

Setelah sync MQTT, flow Node-RED (#23) dan entitas Home Assistant (#21) membaca topic yang sama seperti node tanpa SD — field source: sd_sync membantu debug data yang tertunda.

Langkah Selanjutnya — Tier 2 Seri 2

Logging offline melengkapi pipeline MQTT — data aman di lapangan dan di cloud. Lanjut:

SPI dan SD Card membuka jalur data offline — lanjutkan di halaman artikel Koding Indonesia.