Temperature data logger with an SD card on an Arduino Uno
A DS18B20, a DS3231 clock and a microSD card: every 5 seconds a dated temperature line goes into TEMPLOG.CSV, ready to open in a spreadsheet. The whole build, an Arduino Uno and 6 more parts, runs here in your browser on the firmware below; open it in the editor to change the wiring or the code and run it again.
Teaching? Assign it to your class in one click.
Intermediate Runs in your browser. Free, and no account needed.
The parts retail for about $36. On Mokxi they're free.
The note on the canvas
Word for word, as it sits beside the circuit in the editor.
Try it
1. Press Run. Every 5 seconds a dated line goes into TEMPLOG.CSV and the LED blinks.
2. Drag the DS18B20's slider and watch the next lines follow it.
3. Type d and Enter in the Uno's serial box to print the file back from the card.
4. Click TEMPLOG.CSV in the card's list to download it, then open it in a spreadsheet.
5. Click the card to pull it out: the next write fails and the logger says so. Put it back and press the Uno's reset button: the new lines go on the end of the same file.
The code
The firmware exactly as the editor opens it. Change a line there and press Run: it compiles in the browser.
// Temperature data logger: a DS18B20 thermometer, a DS3231 clock for the
// time stamps and a microSD card. Every five seconds one line goes into
// TEMPLOG.CSV, which opens in any spreadsheet.
//
// DS18B20 DQ pin 2, with a 4.7k pull-up from DQ to 5V; VDD 5V, GND
// DS3231 SDA A4, SCL A5, VCC 5V, GND
// microSD CS pin 10, MOSI 11, MISO 12, SCK 13, VCC 5V, GND
// LED pin 7 through 220 ohms: one blink for every line written
//
// THE FILE. The first time the card is used the sketch writes a header,
//
// date,time,temp_c
// 2026-11-01,09:00:05,21.50
//
// and after that one line per reading. The file is opened, written and
// closed every time, so pulling the card (or the power) loses at most the
// line being written. Leave the card in for days and the file keeps
// growing: at one line every five seconds that is about 450 KB a day.
//
// THE CLOCK. millis() forgets the time at every reset, and a log without
// real dates is hard to use. The DS3231 keeps the time on its own coin cell,
// so it is set once and then left alone. To set a new module, upload once
// with the rtc.adjust() line in setup() uncommented (it takes the time the
// sketch was compiled), then comment it out again and upload, or every reset
// would put the clock back to that moment. In Mokxi the clock starts at the
// time in its start property.
//
// THE SERIAL MONITOR (115200) shows every line as it is logged. Type d and
// Enter to print the whole file back from the card, which is how you check
// what is on it without taking it out.
//
// NO CARD? The logger still reads and prints every five seconds, and says
// so once. Put a card in and press reset to start logging. A card pulled out
// while it runs is noticed at the next write, and the same applies.
//
// Change INTERVAL_MS to log more or less often.
#include <SPI.h>
#include <SD.h>
#include <Wire.h>
#include <RTClib.h>
#include <OneWire.h>
#include <DallasTemperature.h>
const int SD_CS = 10;
const int LED = 7;
const unsigned long INTERVAL_MS = 5000;
const char *FILE_NAME = "TEMPLOG.CSV";
OneWire oneWire(2);
DallasTemperature sensors(&oneWire);
RTC_DS3231 rtc;
bool cardReady = false;
unsigned long lastLog = 0;
void print2(Print &out, int n) {
if (n < 10) out.print('0');
out.print(n);
}
// "2026-11-01,09:00:05,21.50" to wherever `out` points.
void writeLine(Print &out, const DateTime &t, float c) {
out.print(t.year());
out.print('-');
print2(out, t.month());
out.print('-');
print2(out, t.day());
out.print(',');
print2(out, t.hour());
out.print(':');
print2(out, t.minute());
out.print(':');
print2(out, t.second());
out.print(',');
out.println(c, 2);
}
// Start the card, and write the header if the file is new.
bool startCard() {
if (!SD.begin(SD_CS)) return false;
if (!SD.exists(FILE_NAME)) {
File file = SD.open(FILE_NAME, FILE_WRITE);
if (!file) return false;
file.println("date,time,temp_c");
file.close();
}
return true;
}
void dump() {
if (!cardReady) {
Serial.println("no card to read");
return;
}
File file = SD.open(FILE_NAME);
if (!file) {
Serial.println("could not open TEMPLOG.CSV");
return;
}
Serial.println("--- TEMPLOG.CSV ---");
while (file.available()) Serial.write(file.read());
file.close();
Serial.println("--- end ---");
}
void logReading() {
sensors.requestTemperatures();
float c = sensors.getTempCByIndex(0);
DateTime t = rtc.now();
if (c == DEVICE_DISCONNECTED_C) {
Serial.println("no DS18B20: check DQ on pin 2 and the 4.7k pull-up");
return;
}
writeLine(Serial, t, c);
if (!cardReady) return;
File file = SD.open(FILE_NAME, FILE_WRITE);
if (!file) {
Serial.println("could not write TEMPLOG.CSV: is the card still in? Press reset once it is.");
cardReady = false;
return;
}
writeLine(file, t, c);
file.close();
digitalWrite(LED, HIGH);
delay(100);
digitalWrite(LED, LOW);
}
void setup() {
Serial.begin(115200);
pinMode(LED, OUTPUT);
sensors.begin();
if (!rtc.begin()) Serial.println("no DS3231 at 0x68: check SDA and SCL");
// rtc.adjust(DateTime(F(__DATE__), F(__TIME__))); // once, to set the clock
cardReady = startCard();
if (cardReady) {
Serial.println("logging to TEMPLOG.CSV every 5 s (type d to print it)");
} else {
Serial.println("no SD card: check the card and CS on pin 10, then press reset. Readings go to the Serial Monitor only.");
}
}
void loop() {
while (Serial.available()) {
char c = Serial.read();
if (c == 'd' || c == 'D') dump();
}
if (millis() - lastLog >= INTERVAL_MS) {
lastLog = millis();
logReading();
}
}
Parts list
11 parts, plus the jumper wires. They retail for about $36; on Mokxi they're free.
How it is wired
17 connections, pin by pin, read from the circuit itself. Each line is one set of pins joined together, by a jumper wire or a breadboard strip.
- Arduino Uno R3 pin 13; microSD card module pin SCK
- Arduino Uno R3 pin 12; microSD card module pin MISO
- Arduino Uno R3 pin 11; microSD card module pin MOSI
- Arduino Uno R3 pin 10; microSD card module pin CS
- Arduino Uno R3 pin 7; Resistor, 220 Ω pin 1
- Arduino Uno R3 pin 2; Temperature sensor, DS18B20 pin DQ; Resistor, 4.7k Ω pin 1
- Ground: Arduino Uno R3 pin GND
- Arduino Uno R3 pin A4; Real-time clock, DS3231 pin SDA
- Arduino Uno R3 pin A5; Real-time clock, DS3231 pin SCL
- Ground: microSD card module pin GND
- 5 V: microSD card module pin VCC
- Resistor, 220 Ω pin 2; LED, green pin A
- Ground: LED, green pin C
- Ground: Temperature sensor, DS18B20 pin GND
- 5 V: Temperature sensor, DS18B20 pin VDD; Resistor, 4.7k Ω pin 2
- 5 V: Real-time clock, DS3231 pin VCC
- Ground: Real-time clock, DS3231 pin GND
Questions and answers
How often does it log, and can I change it?
Every 5 seconds. Change INTERVAL_MS at the top of the sketch. At one line every 5 s the file grows by about 450 KB a day.
How do I open the log in Excel or Google Sheets?
TEMPLOG.CSV is plain comma-separated text with a date,time,temp_c header, so a spreadsheet opens it directly. In Mokxi, click the file in the card’s list to download it.
Why use a DS3231 instead of millis()?
millis() starts again from zero at every reset and knows no date. The DS3231 keeps the date and time on its coin cell. Set it once by uploading with the rtc.adjust() line uncommented, then comment it out again.
What happens if the SD card is missing or pulled out?
Readings still go to the Serial Monitor and the sketch says the card is missing. The file is closed after every line, so at most the line being written is lost. Put the card back and press reset to carry on in the same file.
Change it and keep it
Open it in the editor, change the circuit or the code, and keep your version in a free account.