← All projects
Sensors Intermediate

A Temperature Logger on microSD: DHT22, DS3231 and a CSV File

Every 5 seconds the ESP32 writes the time, temperature and humidity to a CSV file on a memory card — no Wi-Fi, no cloud. The microSD module over SPI, the SD library, why paths start with "/", why close() matters, and opening the data in Excel.

Difficulty
Intermediate
Time
1 h
Category
Sensors
Updated

About the project

Not all data has to go to the internet. A sensor in a greenhouse, a cellar or a car can simply write its measurements to a microSD card. 8 GB holds decades of CSV lines. Later you put the card into a computer and open the file in Excel or Google Sheets. We build a logger: the DHT22 measures, the DS3231 keeps the exact time, and every 5 seconds a line is added to /log.csv.

What you need

  • ESP32 DevKit
  • microSD module (SPI) + a card up to 32 GB formatted as FAT32
  • DHT22 sensor
  • DS3231 clock with a cell
  • Libraries: SD (part of arduino-esp32), RTClib, DHT sensor library

Wiring

microSD module (hardware SPI, VSPI):

Module ESP32
VCC 5V (the blue module with a regulator)
GND GND
MISO GPIO19
MOSI GPIO23
SCK GPIO18
CS GPIO5

DS3231: SDA → GPIO21, SCL → GPIO22, VCC → 3V3, GND → GND. DHT22: DATA → GPIO27, VCC → 3V3.

The most common blue module has an AMS1117 regulator and a level shifter. It does not work from 3.3 V: power it from 5V. There are also modules without a regulator, and those, on the contrary, must run from 3.3 V only.

Rules of the SD library on the ESP32

  • A path always starts with "/": "/log.csv". Plain "log.csv" does not open on the ESP32.
  • Modes: FILE_READ (the default); FILE_WRITE, which overwrites the file from scratch; FILE_APPEND, which adds to the end. A logger needs APPEND.
  • if (f) checks whether the file opened. The card may have been removed, or the file may not exist.
  • close() is mandatory. The card writes in 512-byte sectors, and until the file is closed (or flush() is called) the data sits in a buffer. Cut the power and the data is lost.
  • Opening and closing the file for every line is fine for a logger that writes every few seconds. For hundreds of writes a second, keep the file open and flush() every 1–2 seconds.

Code

// microSD logger: DHT22 + DS3231 clock → /log.csv every 5 s
#include <SD.h>
#include <RTClib.h>
#include <DHT.h>

const int SD_CS = 5;
const unsigned long PERIOD_MS = 5000;
RTC_DS3231 rtc;
DHT dht(27, DHT22);
unsigned long lastLog = 0;

// the CSV header goes only into a new (or just deleted) file
void ensureHeader() {
  if (SD.exists("/log.csv")) return;
  File f = SD.open("/log.csv", FILE_WRITE);
  f.println("time,temperature,humidity");
  f.close();
}

void logLine() {
  float t = dht.readTemperature();
  float h = dht.readHumidity();
  if (isnan(t) || isnan(h)) return;
  DateTime now = rtc.now();
  char line[48];
  snprintf(line, sizeof line, "%04d-%02d-%02d %02d:%02d:%02d,%.1f,%.0f",
           now.year(), now.month(), now.day(), now.hour(), now.minute(), now.second(), t, h);
  ensureHeader();
  File f = SD.open("/log.csv", FILE_APPEND);
  if (!f) {
    Serial.println("Cannot open /log.csv");
    return;
  }
  f.println(line);
  f.close();   // only close() (or flush()) really writes the data to the card
  Serial.println(line);
}

void setup() {
  Serial.begin(115200);
  dht.begin();
  if (!rtc.begin()) Serial.println("DS3231 not found");
  else if (rtc.lostPower()) rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
  if (!SD.begin(SD_CS)) {
    Serial.println("Card mount failed: check the power and wiring");
    while (true) delay(1000);
  }
  ensureHeader();
  Serial.println("Logging to /log.csv. Commands: dump — show the file, clear — delete it");
}

void loop() {
  if (millis() - lastLog >= PERIOD_MS) {
    lastLog = millis();
    logLine();
  }
  if (Serial.available()) {
    String cmd = Serial.readStringUntil('\n');
    cmd.trim();
    if (cmd == "dump") {
      File f = SD.open("/log.csv");
      while (f.available()) Serial.write(f.read());
      f.close();
    } else if (cmd == "clear") {
      SD.remove("/log.csv");
      Serial.println("File deleted");
    }
  }
}

Opening it in Excel

The file looks like this:

time,temperature,humidity
2026-09-30 14:05:00,22.4,55
2026-09-30 14:05:05,22.5,55

With a European locale, Excel expects a semicolon as the separator and a comma as the decimal mark. Use "Data → From Text/CSV" and pick "comma" as the delimiter. Google Sheets opens the file as is.

Good to know

  • The card won't mount. Check the module's supply first, then the format: FAT32 (cards of 64 GB and larger ship as exFAT and need reformatting). Keep the SPI wires as short as possible.
  • The current while writing jumps to 80–100 mA. On a weak supply a write can reset the ESP32.
  • Endurance. Each sector survives thousands to tens of thousands of writes. One line every 5 seconds means years of service.
  • The simulator saves the card's files with the project (up to 256 KB), so the data survives a sketch restart, just like on a real card.