Learning Goals 5 min
- Combine everything from L02-07 through L02-11 into one polished project — read, smooth, calibrate, map, threshold, alert.
- Build a small physical "product": a thermistor-based thermometer that prints temperature, lights an LED at warm, and beeps the buzzer at fever-level (37.5 °C).
- Tune the thresholds for your environment and write a one-paragraph "product spec" comment at the top of the sketch — the difference between a project and a sketch.
Warm-Up 10 min
So far Cluster B has been a series of single-skill lessons: read the ADC, smooth the readings, calibrate the sensor, map the range, convert to °C. Today you bolt them all together. The output is a small device on your desk that's genuinely useful — squeeze the thermistor between thumb and finger and the LED lights up; hold it long enough and the buzzer beeps.
Predict-the-spec
Before we code, picture the device. What thresholds make sense for a personal thermometer where you live?
One reasonable spec
- Below 24 °C → status "cool", no LED, no buzzer.
- 24–30 °C → status "normal", no LED, no buzzer.
- 30–37 °C → status "warm", LED on, no buzzer.
- ≥ 37.5 °C → status "fever", LED on, buzzer pulses every 2 seconds.
37.5 °C is a common fever threshold that doctors use. These bands work for skin-pinch readings (~32–34 °C from a healthy person) and rise convincingly into "warm" territory if the person actually has a temperature.
New Concept · From sensor to product 20 min
The full pipeline
Today's sketch is the cumulative Level 2 sensor pipeline. Read top to bottom:
- Read raw ADC at A0 (L02-07).
- Smooth with a running average of the last N samples (L02-08). N = 10 works well.
- Convert smoothed raw → resistance → Kelvin → °C (L02-11).
- Constrain the reported °C to a sensible band (e.g. 10–50 °C). Outside that, something has gone wrong; clip and label as "sensor error".
- Classify into one of four states: cool, normal, warm, fever.
- Act: set the LED, beep the buzzer, print the status.
L02-09's calibration isn't needed here — we're after a real temperature, not a relative one. The β-equation already calibrates the thermistor against absolute Celsius.
Hardware on top of L02-11
Add two outputs to the L02-11 wiring:
- LED on pin ~9, in series with a 220 Ω resistor — for the warm/fever state.
- Piezo buzzer on pin 8 (any digital pin), one leg to pin 8, other to GND — for the fever alarm.
The buzzer (non-blocking-ish)
We want the buzzer to chirp every 2 seconds while in fever state — but we don't want the chirp to block the temperature reading. The trick: use millis() to track when we last beeped, and only chirp if 2 seconds have passed since the last one:
unsigned long lastBeep = 0;
void maybeBeep(bool fever) {
if (!fever) return;
if (millis() - lastBeep >= 2000) {
tone(8, 880, 120); // 120 ms beep at A5 (880 Hz)
lastBeep = millis();
}
}tone(pin, freq, duration) from L01-14 plays a non-blocking tone — it returns immediately, and the buzzer plays for the requested milliseconds in the background. While it's beeping, your loop() keeps reading the thermistor. Clean.
Reading classification
An if/else if/else chain handles the four bands cleanly:
const char* statusFor(float t) {
if (t < 24) return "cool";
if (t < 30) return "normal";
if (t < 37.5) return "warm";
return "FEVER";
}Returning a const char* (a string pointer) is C++ shorthand for "here's a string label, don't copy it". Perfect for status names. The capitalised "FEVER" signals urgency in the Serial Monitor.
Worked Example · The full thermometer 20 min
Step 1 — wiring
Three blocks on the breadboard:
- Sensor divider: +5V → thermistor → A0 → 10 kΩ → GND.
- Warm-state LED: Pin 9 → 220 Ω → LED anode → LED cathode → GND.
- Fever buzzer: Pin 8 → piezo + → piezo − → GND.
Three pins (A0, 9, 8) plus +5V/GND. Tight, tidy, fits on a half-size breadboard with space for labels.
Step 2 — the sketch
Save as personal-thermometer.ino:
// L02-12: Personal Thermometer
// Reads an NTC thermistor on A0, prints °C every 250 ms,
// lights LED on pin 9 in "warm" range, chirps buzzer on pin 8
// every 2 s while in "fever" range (≥ 37.5 °C).
//
// Hardware:
// A0 ← NTC + 10kΩ divider (thermistor to +5V, 10k to GND)
// D9 → 220Ω → LED → GND
// D8 → piezo → GND
const int THERM = A0;
const int LED = 9;
const int BUZZ = 8;
// Thermistor constants
const float R_FIXED = 10000.0;
const float R0 = 10000.0;
const float T0_K = 298.15;
const float BETA = 3950.0;
// Status thresholds (°C)
const float COOL_MAX = 24;
const float NORM_MAX = 30;
const float WARM_MAX = 37.5;
// Smoothing
const int N = 10;
int samples[N];
int idx = 0;
long total = 0;
// Beep cadence
unsigned long lastBeep = 0;
const unsigned long BEEP_INTERVAL = 2000;
int smoothedRaw() {
total -= samples[idx];
samples[idx] = analogRead(THERM);
total += samples[idx];
idx = (idx + 1) % N;
return total / N;
}
float rawToCelsius(int raw) {
if (raw <= 0 || raw >= 1023) return -100; // sentinel for sensor error
float R = R_FIXED * (1023.0 / raw - 1.0);
float tK = 1.0 / (1.0/T0_K + (1.0/BETA) * log(R / R0));
return tK - 273.15;
}
const char* statusFor(float t) {
if (t < -50) return "SENSOR ERROR";
if (t < COOL_MAX) return "cool";
if (t < NORM_MAX) return "normal";
if (t < WARM_MAX) return "warm";
return "FEVER";
}
void setup() {
pinMode(LED, OUTPUT);
pinMode(BUZZ, OUTPUT);
Serial.begin(9600);
for (int i = 0; i < N; i++) samples[i] = analogRead(THERM);
for (int i = 0; i < N; i++) total += samples[i];
}
void loop() {
int raw = smoothedRaw();
float t = rawToCelsius(raw);
const char* st = statusFor(t);
bool warm = (t >= NORM_MAX) && (t < WARM_MAX);
bool fever = (t >= WARM_MAX);
digitalWrite(LED, (warm || fever) ? HIGH : LOW);
if (fever && millis() - lastBeep >= BEEP_INTERVAL) {
tone(BUZZ, 880, 120);
lastBeep = millis();
}
Serial.print(t, 1);
Serial.print(" °C ");
Serial.println(st);
delay(250);
}Step 3 — upload & test
Open Serial Monitor. With the thermistor in still room air, the steady output should look like:
26.3 °C normal 26.4 °C normal 26.3 °C normal
Pinch the thermistor between your fingers for 30 seconds → temperature climbs to ~32–34 °C, status flips to warm, LED lights up. Hold a few more seconds; for most people it stops at "warm".
To test "fever": cup the thermistor inside your closed fist (more skin contact, less airflow) for 90+ seconds, or briefly squeeze it under a warm tap. Once it reads ≥ 37.5 °C the buzzer chirps every 2 seconds and the Serial Monitor prints FEVER in caps.
Step 4 — final "ship-ready" touches
The top-of-file comment block is the "product spec". Anyone reading your sketch (a teacher, a future-you, a friend) should be able to glance at it and know what the project does without reading any code. Keep it short and focused: what does it do, what hardware does it need, what are the thresholds, what does each pin do. The hardware list in the comment is your debug instructions when something stops working in six months.
Basic 7 min
Goal: Adjust the thresholds for an outdoor build. Imagine you're running this on a tropical balcony — set COOL_MAX = 26, NORM_MAX = 32, WARM_MAX = 38. Note: the body-fever value rises with ambient, so above 38 you'd expect a real temperature.
Challenge 1 7 min
Goal: Add an RGB LED (from L02-05) and use it to show the status colour: blue = cool, green = normal, amber = warm, red = fever. Replace the single LED with three calls to setColour(r, g, b) based on status.
Challenge 2 6 min
Goal: Record a session. While the device runs, every 5 seconds print a CSV line: seconds_since_start, temperature_C, status. After running for 5 minutes you'll have ~60 lines you can paste into a spreadsheet and plot.
Challenge 3 · Drink-temperature checker 15 min
Turn the personal thermometer into a drink checker. The thermistor is taped to the outside of a mug. Change the worked example so it does this:
- Below 40 °C → status
too cold, LED off. - From 40 °C up to 60 °C → status
drink now, LED off. - 60 °C and above → status
TOO HOT, LED on. - The buzzer chirps once, at the moment the drink cools from
TOO HOTtodrink now.
Keep it ship-ready. Every threshold is a named const at the top. The top comment block describes the new device.
Test it fast: set the two thresholds to 28 and 31 while testing. Pinch the thermistor until it reads TOO HOT, then let go. Put 40 and 60 back when it works.
It works if the status passes through all three bands, the LED is on only while TOO HOT, and the buzzer chirps exactly once as it cools.
Recap 5 min
Cluster B done. The personal thermometer stitches together the six prior lessons: the ADC reads the divider voltage, the running average smooths it, the β-equation converts it to °C, map+constrain shape outputs, an if/else if chain classifies the band, the LED and buzzer carry the alert. The whole pipeline lives in helper functions — smoothedRaw, rawToCelsius, statusFor — so the main loop() reads like a recipe.
Next cluster (L02-13 onward) widens this same pattern to four more environmental sensors — DHT11, soil moisture, rain drop, piezo knock. Every one of them will use the read-smooth-convert-classify-act pipeline you just built.
- Pipeline
- A sequence of small steps that transforms data from raw → useful: read, smooth, convert, classify, act. Each step in its own helper function.
- Status string
- A short human-readable label (
"cool","warm","FEVER") used for logging and display, separate from the underlying number. const char*- A pointer to an immutable C string. Lightweight way to return a status label from a helper function.
- Non-blocking beep
- Using
tone()+ amillis()gate so the buzzer chirps periodically without freezing the main loop. - Sentinel value
- A magic value (here: -100) returned to signal "sensor error", distinct from any real temperature.
- Ship-ready
- The product mindset: tight spec, named constants, comment block at top, works without supervision. The difference between a sketch and a thing on your desk.
Extra Mission 5 min
Part 1 — Design your own thermometer product
Pick a place where temperature matters — a fridge, a bedroom at night, a greenhouse, a pet's bed. On paper, design a thermometer product for it.
Your design must include:
- A name and a one-sentence job for the product.
- Three or four status bands, with their °C thresholds.
- What the LED and buzzer do in each band, and how often the buzzer may sound.
- The top-of-file "product spec" comment, written out in full.
Part 2 — Make it
Change the worked example to your bands and outputs. Every threshold must be a named const. Leave it running for 5 minutes and save the Serial Monitor output.
Bring back next class: the uploaded sketch, a screenshot of the 5-minute log and a photo of the device in its place.