Preview build — page text is a draft awaiting human review. Code badges come from real compiles. The flasher is real but not yet hardware-tested; the Playground is still a preview.

Day 15 of 30 · about 41 minutes

Week-two project: a desk weather display

Today you build: A desk weather display with min/max and feels-like

  • Read 6 min
  • Wire DHT22 + LCD 8 min
  • Upload & test 6 min
  • Make it permanent 5 min
  • Try it 16 min

Community draft — report a mistake

Time to put week two together. No new parts — today is about combining what you know into a sketch that’s tidy enough to keep running on your desk.

Wiring

Day 10’s DHT22 and day 12’s LCD, together:

Part Pin Uno
DHT22 + / − / OUT 5V / GND / pin 2
LCD backpack VCC / GND / SDA / SCL 5V / GND / A4 / A5

Both share the Uno’s 5V and GND — use the breadboard’s power rails.

If day 12 found your LCD at 0x3F, change LCD_ADDRESS before uploading.

What the screen shows

  • Line 1: temperature and humidity, updated every 2.5 s.
  • Line 2: flips every 4 s between today’s low/high (since power-on) and the feels-like temperature.

What to notice in the code

  • Two millis() timers (day 7): one for reading the sensor, one for flipping the page. Neither blocks the other.
  • Small functions (day 9): printLine(), showNow(), showPage(). loop() reads like a summary.
  • printLine() pads every line to 16 characters with %-16s, so a short line overwrites a long one completely — no stray characters left behind.
  • dtostrf() turns a float into text. On the Uno, snprintf can’t do %f (it’s left out to save memory), which surprises everyone once.
  • Errors are shown, not hidden. A failed read puts “Sensor error” on screen instead of an old value that looks right.

Make it permanent

  • Power it from any 5 V phone charger through the USB port.
  • Keep the DHT22 away from the LCD and the Uno’s regulator — they warm up a little and the reading creeps up.

Try it

  • Add day 3’s button to reset the min/max.
  • Light day 7’s LED when humidity goes above 80 % — a monsoon mould warning for your cupboard.

You’re done when…

  • Line 1 shows something like 29.4°C RH 62% and updates every few seconds.
  • Line 2 alternates every 4 s between Lo28.9 Hi29.6 and Feels 31.2°C.
  • Breathing on the sensor pushes the humidity up and, if it warms the sensor, the Hi value too — and Lo/Hi survive the page flips.
  • Pulling the DATA wire shows Sensor error on line 1 within a few seconds.
  • Powered from a phone charger instead of the laptop, it starts on its own.

Next week: Wi-Fi. You’ll move to the ESP32.

Today's code

Verified — core avr 1.8.8, DHT sensor library 1.4.7, 3 Nov 2026Compile-only
lesson-desk-weather.inoArduino C++Open in PlaygroundOpen in Playground
// Day 15 — Week-two project: a desk weather display (Arduino Uno / Nano)
// DHT22 module: + → 5V, - → GND, OUT → pin 2 (10 k pull-up to 5V on a bare sensor).
// 16x2 LCD with I2C backpack: VCC → 5V, GND → GND, SDA → A4, SCL → A5. Address 0x27 (or 0x3F).
// Line 1: temperature and humidity now. Line 2 changes every 4 s: today's min/max, then "feels like".
#include <DHT.h>
#include <Wire.h>
#include <LiquidCrystal_I2C.h>

const uint8_t DHT_PIN = 2;
const uint8_t LCD_ADDRESS = 0x27;   // change to 0x3F if day 12's sketch found that instead
const unsigned long READ_EVERY = 2500;
const unsigned long PAGE_EVERY = 4000;

DHT dht(DHT_PIN, DHT22);
LiquidCrystal_I2C lcd(LCD_ADDRESS, 16, 2);

float tempNow = NAN, humNow = NAN;
float tempMin = 1000, tempMax = -1000;
unsigned long lastRead = 0, lastPage = 0;
int page = 0;

void printLine(int row, const char* text) {
  char padded[17];
  snprintf(padded, sizeof padded, "%-16s", text);   // pad with spaces to clear old text
  lcd.setCursor(0, row);
  lcd.print(padded);
}

// AVR snprintf has no %f, so dtostrf() turns a float into text first.
void showNow() {
  char t[8], line[17];
  dtostrf(tempNow, 4, 1, t);
  snprintf(line, sizeof line, "%s%cC  RH %d%%", t, (char)223, (int)(humNow + 0.5));  // 223 = ° on these LCDs
  printLine(0, line);
}

void showPage() {
  char a[8], b[8], line[17];
  if (page == 0) {
    dtostrf(tempMin, 4, 1, a);
    dtostrf(tempMax, 4, 1, b);
    snprintf(line, sizeof line, "Lo%s Hi%s", a, b);
  } else {
    dtostrf(dht.computeHeatIndex(tempNow, humNow, false), 4, 1, a);
    snprintf(line, sizeof line, "Feels %s%cC", a, (char)223);
  }
  printLine(1, line);
}

void setup() {
  Serial.begin(9600);
  dht.begin();
  lcd.init();
  lcd.backlight();
  printLine(0, "Desk weather");
  printLine(1, "Reading...");
}

void loop() {
  unsigned long now = millis();

  if (now - lastRead >= READ_EVERY) {
    lastRead = now;
    float h = dht.readHumidity();
    float t = dht.readTemperature();
    if (isnan(h) || isnan(t)) {
      printLine(0, "Sensor error");
      Serial.println(F("DHT22 read failed"));
      return;
    }
    tempNow = t;
    humNow = h;
    if (t < tempMin) tempMin = t;
    if (t > tempMax) tempMax = t;
    showNow();
    Serial.print(t, 1);
    Serial.print(F(" C, "));
    Serial.print(h, 0);
    Serial.println(F(" %"));
  }

  if (!isnan(tempNow) && now - lastPage >= PAGE_EVERY) {
    lastPage = now;
    showPage();
    page = 1 - page;   // flip between 0 and 1
  }
}

↑↓ to move · Enter to open