Scientists don't just look at a measurement once. They record it over hours or days and then look for patterns. In this capstone you build a small weather station that does exactly that. A DHT11 measures temperature and humidity, a photoresistor measures light, the LCD shows the live values, and every ten seconds the UNO adds a line to a file on an SD card. Afterwards you put the card in a computer and turn the file into a chart.
What you'll learn
- How to combine three libraries (LCD, DHT and SD) in one sketch
- How the SPI bus connects the SD card module
- How to write a CSV file that any spreadsheet program can open
- How to time two different jobs with
millis()instead ofdelay()
What you need
- UNO R3 board and USB cable (UNO R3 Starter Kit)
- 1602 LCD display, 10 kΩ potentiometer and 1 resistor, 220 Ω (UNO R3 Starter Kit)
- DHT11 temperature and humidity sensor (UNO R3 Starter Kit)
- Photoresistor module (45-in-1 Sensor Kit)
- SD card module (45-in-1 Sensor Kit)
- An SD or microSD card that fits your module, 32 GB or smaller, formatted as FAT32 (not included)
- Breadboard and jumper wires (UNO R3 Starter Kit)
Before you start
The LCD has 16 connections in a row. If yours has no pins, solder 16 pins from the 40-pin male header in the kit first, so it plugs into the breadboard. Ask an adult to help if you're new to soldering.
This is a bigger build than the earlier lessons, so don't wire everything at once. Add one part, test it, then add the next. When something stops working, you know it's the part you just added.
Wiring

| From | To |
|---|---|
| LCD VSS, RW and K | GND |
| LCD VDD | 5V |
| LCD V0 | Middle pin (W) of the potentiometer |
| LCD RS | UNO pin 9 |
| LCD E | UNO pin 8 |
| LCD D4, D5, D6, D7 | UNO pins 5, 4, 3, 2 |
| LCD A (backlight +) | 220 Ω resistor, then 5V |
| Potentiometer outer pins | 5V and GND |
| DHT11 S (signal) | UNO pin 7 |
| DHT11 + and − | 5V and GND |
| Photoresistor module S | UNO pin A0 |
| Photoresistor module + and − | 5V and GND |
| SD module CS | UNO pin 10 |
| SD module MOSI | UNO pin 11 |
| SD module MISO | UNO pin 12 |
| SD module SCK | UNO pin 13 |
| SD module VCC (or +5) | 5V |
| SD module GND | GND |
Sensor modules from different makers can label and order their pins differently. Always follow the labels printed on your module.
In the earlier LCD project, RS and E were on pins 12 and 11. Here they move to pins 9 and 8, because the SD card needs pins 11, 12 and 13. If your SD module has both a +5 and a +3.3 pin, use only the +5 pin. If it has only a 3.3 V pin, connect that to the UNO's 3V3 pin instead of 5V.
Build it in stages
- The screen. Wire the LCD and the potentiometer and upload the sketch. Even with nothing else connected you should see "Weather logger" and then "Sensor error". Turn the potentiometer until the text is sharp.
- The sensors. Add the DHT11 and the photoresistor module. The top line now shows temperature and humidity, and the bottom line shows the light level.
-
The SD card. Unplug the USB cable, add the SD module and push in the card. Plug the cable back in. The screen shows "SD card ready", and the counter
n=on the bottom line goes up by one every ten seconds.
The sketch
Install the DHT sensor library by Adafruit (and Adafruit Unified Sensor when asked). The LiquidCrystal, SPI and SD libraries are already built in.
#include <LiquidCrystal.h>
#include <DHT.h>
#include <SPI.h>
#include <SD.h>
// LCD pins: RS, E, D4, D5, D6, D7
LiquidCrystal lcd(9, 8, 5, 4, 3, 2);
DHT dht(7, DHT11);
const int LIGHT_PIN = A0;
const int SD_CS = 10;
const unsigned long SCREEN_EVERY = 2000; // update the screen every 2 s
const unsigned long LOG_EVERY = 10000; // save a line every 10 s
bool cardOk = false;
unsigned long lastScreen = 0;
unsigned long lastLog = 0;
unsigned int records = 0;
void setup() {
lcd.begin(16, 2);
dht.begin();
lcd.print(F("Weather logger"));
cardOk = SD.begin(SD_CS);
lcd.setCursor(0, 1);
if (cardOk) lcd.print(F("SD card ready"));
else lcd.print(F("No SD card!"));
// A new file starts with a line of column names
if (cardOk && !SD.exists("LOG.CSV")) {
File file = SD.open("LOG.CSV", FILE_WRITE);
if (file) {
file.println(F("seconds,temperature_C,humidity_pct,light_pct"));
file.close();
}
}
delay(2000);
}
void loop() {
unsigned long now = millis();
if (now - lastScreen < SCREEN_EVERY) return; // nothing to do yet
lastScreen = now;
float temperature = dht.readTemperature();
float humidity = dht.readHumidity();
int light = map(analogRead(LIGHT_PIN), 0, 1023, 100, 0); // bright = high %
lcd.clear();
if (isnan(temperature) || isnan(humidity)) {
lcd.print(F("Sensor error"));
return;
}
lcd.print(temperature, 1);
lcd.print(F("C "));
lcd.print(humidity, 0);
lcd.print(F("%"));
lcd.setCursor(0, 1);
lcd.print(F("L:"));
lcd.print(light);
lcd.print(F("% "));
if (cardOk) {
lcd.print(F("n="));
lcd.print(records);
} else {
lcd.print(F("no card"));
}
if (cardOk && now - lastLog >= LOG_EVERY) {
lastLog = now;
File file = SD.open("LOG.CSV", FILE_WRITE); // opens at the end of the file
if (file) {
file.print(now / 1000);
file.print(',');
file.print(temperature, 1);
file.print(',');
file.print(humidity, 0);
file.print(',');
file.println(light);
file.close(); // close = safely saved
records++;
} else {
cardOk = false; // card removed or full
}
}
}
Look at your data
- Let the station run for at least ten minutes. For a more interesting result, leave it overnight on a USB charger or power bank.
- Wait until the counter has just gone up, then unplug the power and take out the card.
- Put the card in a computer and open LOG.CSV with Excel, Google Sheets or LibreOffice Calc.
- Select the columns and insert a line chart. Can you see when the sun came up, or when someone opened a window?
How it works
The SD card module talks to the UNO over SPI, a fast bus with four wires: MOSI carries data to the card, MISO carries data back, SCK is the clock that keeps both sides in step, and CS (chip select) tells the card that the message is for it. On the UNO, SPI always uses pins 11, 12 and 13.
The file is a CSV file: plain text with one reading per line and commas between the values. Almost every spreadsheet program can open it. SD.open("LOG.CSV", FILE_WRITE) opens the file at its end, so new lines are added after the old ones, and close() makes sure the line is really written to the card. Because each line is saved and closed straight away, you lose at most one reading if the power is cut.
The first column is the number of seconds since the UNO started, taken from millis(). Two timers run side by side: the screen is refreshed every two seconds and the card is written every ten. Text inside F("…") is kept in the UNO's larger program memory. That matters here, because the SD library needs a big part of the UNO's small working memory.
Try this next
- Change
LOG_EVERYto 60000 to save one line per minute for a whole day. - Add a column for the highest temperature seen so far.
- Add the DS18B20 from the Sensor Kit as an outdoor probe (see Precise temperature with the DS18B20).
- Start a new file each time the UNO starts: look for the first name from LOG01.CSV to LOG99.CSV that doesn't exist yet.
Troubleshooting
- The screen lights up but shows only blocks or nothing: turn the potentiometer slowly from one end to the other.
- "No SD card!": check the four SPI wires and the power pin, and make sure the card is pushed in fully. Format the card as FAT32 on a computer. Cards larger than 32 GB don't work with this library.
- "Sensor error": check the DHT11 signal wire on pin 7 and its power pins.
-
The light value goes down when you shine a light on the sensor: swap
100, 0to0, 100in themap()line. - The file is empty or the last lines are missing: the power was cut while the UNO was writing. Unplug it just after the counter goes up.
- The UNO restarts or shows strange characters after adding the SD module: some SD modules draw a lot of current. Try another USB port or a powered USB hub.