ESP32The controller
DHT22Temperature + humidity
Arduino / C++Firmware & timing
Local dashboardWi-Fi + JSON
ESP32Example conversation

ESP32 Room Temperature Monitor

Build a resilient ESP32 room monitor with a DHT22, scheduled sampling, a local Wi-Fi API and clear fault states. Follow the student and TEP AI from the first connection to a testable system.

Intermediate24 messages12 project steps16 min read3,115 wordsReference design
Build something similar
01

Define a resilient system

S

Student

Question 1

I want to build an ESP32 room temperature monitor for my final-year project. A basic temperature display feels too simple. Can we make it technically stronger, show readings on my laptop, and demonstrate what happens when a sensor or the Wi-Fi connection fails?

TEP AI

Project guide

Yes. Let’s treat this as a small measurement system with explicit behavior, rather than starting with a collection of features. Our inputs are temperature and relative humidity from a DHT22. Our outputs are a serial diagnostic stream and a browser view on your local network. The interesting engineering work is deciding when a number is trustworthy, keeping the device responsive, and making failures visible.

We will build it in layers. First, prove that the board can read the sensor without networking. Then give acquisition its own schedule, keep a validated snapshot in memory, and let the HTTP endpoint return that snapshot. Finally, let the browser distinguish a fresh measurement from an old one or a lost connection. A web request must never directly trigger another sensor read.

For this example, choose a 2.5-second acquisition interval, a six-second freshness window, and a thirty-second limit for displaying a previous measurement as stale. Those are our project policies, not universal sensor specifications. We will also track consecutive failures and preserve raw readings alongside a filtered temperature. The result is a reference design you can explain, test and extend; the numerical outputs in this conversation remain illustrative until you collect your own measurements.

  • First milestone: validated temperature and humidity over USB serial.
  • Final milestone: a local dashboard that exposes freshness, failures and recovery.
First milestoneTemperature + humidity in Serial Monitor
02

Choose and place the components

S

Student

Question 2

I have an ESP32 development board, a bare four-pin DHT22, a breadboard and jumper wires. Do I need a display or an extra Wi-Fi module? Also, does placing the sensor next to the ESP32 affect the readings?

TEP AI

Project guide

You can use the ESP32’s built-in networking and your laptop as the display, so there is no need to add those two modules. Add a suitable USB data cable and a 10 kΩ pull-up resistor for the bare sensor wiring shown below. We will use an ESP32 board with an accessible GPIO4, but you must confirm the exact board’s pin labels and schematic before adopting that assignment.

Place the DHT22 where it measures the air you actually care about. A sensor directly beside a warm regulator, a processor or an enclosure wall may describe that local microclimate rather than the room. Start with short connections and keep the sensor separated from the board’s heat sources. This makes the first measurements easier to interpret before you experiment with longer leads or an enclosure.

For the project report, distinguish what you selected from what you verified. Record the board model, sensor variant, supply voltage, connection lengths and physical arrangement. A three-pin breakout may already include a pull-up and may use a different pin order; the four-pin diagram is not a substitute for inspecting it. Keep the first hardware setup simple so that a later change in behavior can be connected to a specific change in wiring, placement or firmware.

Technical reference: Adafruit: connecting a DHT sensor

03

Wire the sensor and explain the pull-up

S

Student

Question 3

Please show the connections. I understand power and ground, but why does the data wire need a resistor to the supply? Could I connect the sensor to 5 V and leave its data wire directly connected to the ESP32?

TEP AI

Project guide

Use the 3.3 V arrangement in this example: sensor pin 1 to 3V3, pin 2 to GPIO4, pin 4 to GND, and pin 3 unconnected. Place the 10 kΩ resistor between the data line and 3V3. Check the sensor’s documented orientation before counting pins, and disconnect power while moving wires. The pin labeled GPIO4 is the relevant signal; the fourth physical position on a board header is not necessarily GPIO4.

The pull-up gives the released data line a defined high level. The sensor and controller exchange timed digital pulses, so an undefined or slowly rising signal can prevent a valid transaction. As a simple circuit estimate, 3.3 V across 10 kΩ corresponds to about 0.33 mA when the line is pulled low. Cable capacitance and pull-up resistance also affect the rising edge. That is a reason to start with short wires, not a reason to choose the smallest resistor available.

Do not casually translate a 5 V Arduino diagram into a 3.3 V ESP32 connection. In this project, both the sensor supply and the data pull-up are 3.3 V. If a particular module requires another supply arrangement, inspect its internal pull-up and use an interface compatible with the board’s input limits. A common ground is essential: the receiver needs the same voltage reference as the device producing the signal.

Bare four-pin sensor: 1 → 3V3, 2 → GPIO4, 3 → unused, 4 → GND.

Reference: Adafruit DHT wiring guide. Use a 3.3 V data pull-up with the ESP32.

04

Establish the simplest known-good firmware

S

Student

Question 4

The wiring is ready. Should I begin with the web server code, or test the sensor by itself? I would like a small sketch where I can understand every line before adding the more complex parts.

TEP AI

Project guide

Start with the small serial sketch below. Install the Adafruit DHT sensor library and its Adafruit Unified Sensor dependency, select your actual ESP32 board and port, and set Serial Monitor to 115200 baud. The GPIO constant and the DHT22 constructor setting must agree with the physical connection and sensor type. Compile this version first, so a dependency or upload problem is separated from a networking problem.

The two measurements are checked before they are printed. A failed library read can produce NaN, which means “not a number”; it must not become an ordinary plotted value. Do not replace it with zero, because zero degrees or zero percent humidity would look like a valid observation. In this first milestone, a plain error message is enough to tell us that acquisition failed.

The two-second delay is deliberately simple here. It helps isolate the sensor test, but we will remove the long waiting period when the device also services browser requests. Keep a copy of this basic sketch even after the advanced version works. Returning to a small, known configuration is useful when you need to decide whether a later failure belongs to hardware, the acquisition layer or the web interface. The downloadable example is educational source, not a claim that this particular board has been physically tested.

esp32-room-monitor.ino
// AI Projects Lab — educational ESP32 + DHT22 example.
// Install Adafruit DHT sensor library and Adafruit Unified Sensor.
// Verify the GPIO label and sensor pinout for your actual board.
#include <DHT.h>

constexpr uint8_t SENSOR_GPIO = 4;
DHT roomSensor(SENSOR_GPIO, DHT22);

void setup() {
  Serial.begin(115200);
  roomSensor.begin();
}

void loop() {
  delay(2000);
  const float temperatureC = roomSensor.readTemperature();
  const float humidityPct = roomSensor.readHumidity();

  if (isnan(temperatureC) || isnan(humidityPct)) {
    Serial.println("No reading: check power, data pin and pull-up.");
    return;
  }

  Serial.print("Room: ");
  Serial.print(temperatureC, 1);
  Serial.print(" C | Humidity: ");
  Serial.print(humidityPct, 1);
  Serial.println(" %");
}

Basic serial milestone. Target-board compilation and hardware validation are still required.

05

Diagnose a failed reading systematically

S

Student

Question 5

The sketch uploads, but I keep getting the failed-reading message. I checked again: the data wire is connected to the pin marked 5, while the code says GPIO4. If I fix that, what should I check next if the error remains?

TEP AI

Project guide

That mismatch is a concrete first fault. With power disconnected, move the data wire to the board’s GPIO4, or deliberately change the constant to the GPIO you verified from the board documentation. Change one thing and test again. Do not simultaneously alter the pin, sensor type, read interval and library, because then a successful result will not tell you which assumption was wrong.

If the failure continues, inspect the complete signal path: sensor orientation, ground continuity, the 3.3 V supply at the sensor, and the pull-up connection. A breadboard power rail may be split in the middle. A jumper can look inserted while making poor contact. A multimeter is useful for static supply checks, while an oscilloscope or logic analyzer can help inspect digital timing if available. An I²C address scan will not diagnose this sensor’s single-data-wire protocol.

Then check software assumptions. Confirm DHT22 rather than DHT11, allow the required interval between transactions, and keep the network layer absent during this test. Adafruit’s implementation caches recent reads, so repeatedly calling its getters is not proof of repeated physical acquisitions. Write down the symptom, the exact intervention and the observed result. For this conversation, we can describe the expected recovery after correcting the pin, but your report should contain the actual serial output from your own hardware.

Technical reference: Adafruit DHT library: read timing and failure behavior

06

Schedule acquisition without a long delay

S

Student

Question 6

Now suppose the serial test is working. I want the browser to remain responsive while the ESP32 takes a reading every 2.5 seconds. Would a timer interrupt be better than delay(), and what happens when millis() rolls over?

TEP AI

Project guide

A cooperative loop is enough for this project. Let the loop repeatedly service network work, then check whether the next acquisition is due. Compare elapsed time using unsigned subtraction: uint32_t(now - lastSampleMs) >= SAMPLE_MS. This handles the ordinary millisecond-counter rollover when the loop is serviced regularly and the interval is short relative to the counter range. Avoid comparing now with a future timestamp by simply adding the interval.

When a sample is due, perform one acquisition and update the reference time. Do not run a catch-up loop that requests several immediate DHT transactions after the processor has been delayed. We want a sensible minimum spacing, not a burst of stale cached results. The reference firmware waits for the first interval after startup, which also gives the hardware time to settle.

A timer interrupt is not necessary here, and a timing-sensitive sensor transaction plus formatted output does not belong in an interrupt handler. The DHT library itself still performs a short synchronous exchange, and the simple web server can also wait on I/O. Therefore, call this a cooperative design, not a guarantee of hard real-time behavior. Measure service time on the board if responsiveness becomes a formal requirement. Only introduce tasks, queues or asynchronous networking after you can identify the blocking operation and the timing budget it violates.

Cooperative scheduling · excerpt
const uint32_t now = millis();
if (uint32_t(now - lastSampleMs) >= SAMPLE_MS) {
  lastSampleMs = now;
  acquire();
}
if (httpStarted) server.handleClient();
Elapsed-time scheduling · SAMPLE_MS = 2500

Technical reference: Arduino: millis() reference

07

Model fresh, stale and unavailable data

S

Student

Question 7

I do not want the dashboard to keep showing an old temperature as if everything is fine. Could we define the data structure and the failure states before implementing the API? What should happen before the very first successful reading?

TEP AI

Project guide

Keep measurement validity separate from communication success. An HTTP response can arrive correctly while the sensor is disconnected. Our snapshot therefore contains a state, a sequence number, the age of the last accepted sample, raw values, a filtered temperature and a consecutive-failure count. The sequence advances only when a new valid pair is accepted; repeated browser requests do not create new samples.

Before the first attempt, the state is starting. If an attempt fails and there has never been a valid pair, use unavailable. After a successful pair, use fresh while the most recent attempt succeeded and the sample is within our six-second window. A failed attempt immediately changes a previous valid measurement to stale. After thirty seconds without an accepted pair, the state is unavailable even though a historical value still exists in memory.

Only fresh values belong in the top-level current-reading fields. A previous pair can remain under lastGood, with its age clearly attached, so the interface can explain what was last observed. Use JSON null for values that are not currently available; NaN is not valid JSON. The age accumulator in the reference firmware saturates instead of wrapping back to a small value during a very long fault. This prevents an expired measurement from accidentally looking recent. The policy is intentionally explicit so we can test every transition without relying on the color of a status badge.

Fresh Current + validatedStale Historical, clearly labeledUnavailable No current measurement
/api/readings · illustrative stale response
{
  "state": "stale",
  "sequence": 12,
  "temperatureC": null,
  "humidityPct": null,
  "filteredC": null,
  "alarm": null,
  "consecutiveFailures": 1,
  "lastGood": {
    "temperatureC": 24.6,
    "humidityPct": 48.2,
    "ageMs": 2700
  }
}
JSON contract · example values, not hardware measurements
08

Add a local Wi-Fi API

S

Student

Question 8

That state model makes sense. Can you combine it with Wi-Fi and a small web interface? I also want to know whether losing the router should stop the sensor, and where the Wi-Fi password belongs in a project I might publish.

TEP AI

Project guide

The extended firmware below keeps acquisition independent of the network connection. Setup starts station mode and begins joining your network, but it does not sit in an endless connection loop. The main loop continues sampling. When Wi-Fi is available, it starts a small HTTP server; when the connection is lost, it stops that server while acquisition continues. Automatic reconnection is enabled, and the address is printed when service becomes available again.

The root route serves a minimal browser page, and /api/readings returns the current snapshot. That handler serializes memory; it never talks to the sensor. This is an important separation because opening several browser tabs must not change the physical read interval. Both routes send a no-store cache policy. The prototype uses a single simple server and bounded response content; it is not designed as a high-concurrency public service.

Replace the placeholder network credentials only in your local working copy. Before sharing the firmware, remove real credentials, network identifiers and private logs. For this classroom prototype, use a trusted local network and leave router port forwarding disabled. There is no authentication or TLS in this sample. If remote access becomes a requirement, redesign the boundary with an authenticated service and protected transport rather than placing this demonstration endpoint directly on the public internet.

esp32-room-monitor-network.ino
/* AI Projects Lab: ESP32 + DHT22 reference firmware.
 * Install Adafruit DHT sensor library and Adafruit Unified Sensor.
 * Open this file as its own Arduino sketch; do not combine it with the basic sketch.
 * Use a trusted local network. This HTTP demonstration has no authentication/TLS.
 * Configure and compile for your board; physical hardware testing is still required.
 */
#include <Arduino.h>
#include <DHT.h>
#include <WiFi.h>
#include <WebServer.h>
#include <math.h>
#include <stdint.h>
#include <string.h>

const char* WIFI_SSID = "YOUR_LOCAL_SSID";
const char* WIFI_PASSWORD = "YOUR_LOCAL_PASSWORD";
constexpr uint8_t SENSOR_GPIO = 4;
constexpr uint32_t SAMPLE_MS = 2500;
constexpr uint32_t FRESH_MS = 6000;
constexpr uint32_t EXPIRE_MS = 30000;
constexpr float ALPHA = 0.25f;

DHT roomSensor(SENSOR_GPIO, DHT22);
WebServer server(80);
bool httpStarted = false;
bool attempted = false, hasReading = false, latestOk = false, hot = false;
uint32_t lastSampleMs = 0, clockMs = 0, ageMs = 0;
uint32_t sequence = 0, consecutiveFailures = 0;
float temperatureC = NAN, humidityPct = NAN, filteredC = NAN;

// Advancing a bounded age avoids an old fault becoming recent at counter rollover.
// The loop must run regularly; no millis()-based method recovers arbitrary full wraps.
void advanceAge(uint32_t now) {
  const uint32_t elapsed = now - clockMs;
  clockMs = now;
  if (hasReading) {
    ageMs = elapsed > UINT32_MAX - ageMs ? UINT32_MAX : ageMs + elapsed;
  }
}

const char* measurementState() {
  if (!attempted) return "starting";
  if (!hasReading || ageMs > EXPIRE_MS) return "unavailable";
  if (latestOk && ageMs <= FRESH_MS) return "fresh";
  return "stale";
}

void acquire() {
  const float t = roomSensor.readTemperature();
  const float h = roomSensor.readHumidity();
  advanceAge(millis());
  attempted = true;
  latestOk = isfinite(t) && isfinite(h) && t >= -40.0f && t <= 80.0f &&
             h >= 0.0f && h <= 100.0f;
  if (!latestOk) {
    if (consecutiveFailures < UINT32_MAX) ++consecutiveFailures;
    return;  // Never replace a real reading with a fabricated zero.
  }
  if (!hasReading || ageMs > EXPIRE_MS) {
    filteredC = t;
    hot = false;
  } else {
    filteredC = ALPHA * t + (1.0f - ALPHA) * filteredC;
  }
  temperatureC = t;
  humidityPct = h;
  hasReading = true;
  ageMs = 0;
  consecutiveFailures = 0;
  ++sequence;
  if (filteredC >= 30.0f) hot = true;
  else if (filteredC <= 29.0f) hot = false;
}

String snapshotJson() {
  advanceAge(millis());
  const char* state = measurementState();
  const bool fresh = strcmp(state, "fresh") == 0;
  String result;
  result.reserve(420);
  result = "{\"state\":\"";
  result += state;
  result += "\",\"sequence\":" + String(sequence);
  result += ",\"temperatureC\":" + (fresh ? String(temperatureC, 1) : String("null"));
  result += ",\"humidityPct\":" + (fresh ? String(humidityPct, 1) : String("null"));
  result += ",\"filteredC\":" + (fresh ? String(filteredC, 2) : String("null"));
  result += ",\"alarm\":";
  result += fresh ? (hot ? "\"high\"" : "\"normal\"") : "null";
  result += ",\"consecutiveFailures\":" + String(consecutiveFailures);
  result += ",\"lastGood\":";
  if (hasReading) {
    result += "{\"temperatureC\":" + String(temperatureC, 1);
    result += ",\"humidityPct\":" + String(humidityPct, 1);
    result += ",\"ageMs\":" + String(ageMs) + "}";
  } else {
    result += "null";
  }
  return result + "}";
}

const char PAGE[] PROGMEM = R"HTML(<!doctype html>
<html lang="en"><meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>ESP32 room monitor</title>
<style>body{font:16px/1.6 system-ui;max-width:720px;margin:36px auto;padding:16px;background:#f1f8fc;color:#23445b}pre{padding:18px;border:1px solid #c8dfea;border-radius:12px;background:white;white-space:pre-wrap;overflow-wrap:anywhere}h1{color:#126f9c}</style>
<h1>ESP32 room monitor</h1>
<p id="state" role="status">Connecting to this device...</p>
<pre id="reading">Waiting for a snapshot.</pre>
<p>Educational local-network prototype. Values marked stale are historical.</p>
<script>
const state = document.querySelector('#state');
const reading = document.querySelector('#reading');
async function poll() {
  const controller = new AbortController();
  const timer = setTimeout(() => controller.abort(), 5000);
  try {
    const response = await fetch('/api/readings', {cache:'no-store', signal:controller.signal});
    if (!response.ok) throw new Error('HTTP ' + response.status);
    const snapshot = await response.json();
    state.textContent = 'Sensor: ' + snapshot.state + ' | Connection: available';
    reading.textContent = JSON.stringify(snapshot, null, 2);
  } catch {
    state.textContent = 'Connection unavailable — sensor state is unknown.';
    reading.textContent = 'No current snapshot. Check the device and local network.';
  } finally {
    clearTimeout(timer);
    setTimeout(poll, 2500);  // One outstanding request at a time.
  }
}
poll();
</script></html>)HTML";

void setup() {
  Serial.begin(115200);
  roomSensor.begin();
  WiFi.mode(WIFI_STA);
  WiFi.setAutoReconnect(true);
  WiFi.begin(WIFI_SSID, WIFI_PASSWORD);  // Acquisition does not wait for association.
  server.on("/", HTTP_GET, []() {
    server.sendHeader("Cache-Control", "no-store");
    server.send_P(200, "text/html; charset=utf-8", PAGE);
  });
  server.on("/api/readings", HTTP_GET, []() {
    server.sendHeader("Cache-Control", "no-store");
    server.send(200, "application/json", snapshotJson());
  });
  server.onNotFound([]() { server.send(404, "text/plain", "Not found"); });
  clockMs = lastSampleMs = millis();
}

void loop() {
  const uint32_t now = millis();
  advanceAge(now);
  if (uint32_t(now - lastSampleMs) >= SAMPLE_MS) {
    lastSampleMs = now;
    acquire();
    Serial.println(snapshotJson());
  }
  const bool connected = WiFi.status() == WL_CONNECTED;
  if (connected && !httpStarted) {
    server.begin();
    httpStarted = true;
    Serial.print("Local dashboard: http://");
    Serial.println(WiFi.localIP());
  } else if (!connected && httpStarted) {
    server.stop();
    httpStarted = false;
  }
  if (httpStarted) server.handleClient();
  delay(1);  // Yield briefly; the sensor interval is controlled by elapsed time.
}

Open this as a separate Arduino sketch. Replace the Wi-Fi placeholders locally and verify on your board.

Technical reference: Espressif: Wi-Fi station and reconnection API · Espressif: WebServer example

09

Make the browser honest about failures

S

Student

Question 9

If the browser polls regularly, how do I stop overlapping requests or a cached response? And if Wi-Fi disappears after one good response, should the page keep the last green “fresh” status forever?

TEP AI

Project guide

Use one polling cycle at a time. The embedded page waits for a request to finish, then schedules the next one. It also gives each request a finite timeout with AbortController. This prevents a slow or lost request from accumulating an unbounded queue of new requests. The browser asks for uncached data, and the server independently marks its responses as non-cacheable.

The browser displays sensor state and connection state as separate concepts. When a valid response arrives, it shows the firmware’s snapshot. If the request fails, the interface replaces the current-reading display with an explicit connection-unavailable message. It does not keep claiming that the last received value is fresh. A stale sensor response, on the other hand, means that communication worked but the measurement itself is not current. Those two failure modes lead you to different diagnostic steps.

Our included page is deliberately small: a status label and a readable JSON snapshot. A richer dashboard can turn those fields into cards and a chart, but it should retain the same semantics. Add a chart point only when the sample sequence changes. Do not fill a communication gap with invented measurements or interpret a reset sequence as a smooth continuation of the same session. Device time since boot is also not a calendar timestamp; a persistent history needs an explicit clock strategy and a boot or session identifier.

AcquireEvery 2.5 secondsValidate & storeA snapshot in memoryServe & displayIndependent requests
10

Separate smoothing from measurement accuracy

S

Student

Question 10

The readings may move slightly even when the room seems unchanged. Could we smooth the temperature and add a high-temperature indicator? I do not want the indicator rapidly switching on and off around one threshold.

TEP AI

Project guide

Keep the raw values and add a separate exponential moving average for presentation. In this example, filtered = 0.25 × raw + 0.75 × previousFiltered. Initialize it from the first valid sample instead of zero. At a 2.5-second sample interval, this setting intentionally adds visible lag. It reduces short-term variation in the displayed trend; it does not calibrate the sensor or establish that the absolute temperature is correct.

Do not update the filter with a failed read. After a gap long enough to make the previous measurement unavailable, reinitialize from the next valid value so a long-expired estimate is not blended into the recovery. If the signal jumps unexpectedly, first consider a real environmental change, sensor placement and connection quality. A filter should not conceal evidence needed to diagnose the system.

For a demonstration indicator, use hysteresis: enter the high-temperature state at 30 °C and leave it at 29 °C. Those thresholds are example configuration choices, not safety limits. The reference firmware applies them to the filtered temperature and publishes an alarm field only while the snapshot is fresh. A stale or unavailable measurement produces a null alarm, which means unknown, not normal. There is no heater, fan, relay or other actuator in this design. A real controller would need independent fault behavior and response-time requirements before it could make decisions from this signal.

Temperature smoothingfiltered = 0.25 × raw + 0.75 × previous

Example hysteresis: high at 30 °C · normal at 29 °C · unknown when not fresh.

11

Test faults, recovery and timing

S

Student

Question 11

How can I demonstrate that this is more than a sensor displaying numbers? I need a convincing validation section, including fault cases and evidence that the dashboard and firmware agree.

TEP AI

Project guide

Write the expected behavior before running each test. Begin with normal operation: record serial snapshots, fetch the endpoint and compare the sample sequence and values. Then request the endpoint faster than acquisition and verify that the sequence does not advance merely because requests arrive. Measure sample spacing and request latency separately; the cooperative design does not imply that either timing is perfectly constant.

Test sensor faults and network faults independently. For a disconnected-sensor test, power down before changing the wiring, then restart and observe the unavailable state. For a failure after valid operation, use a controlled test hook that substitutes an invalid sample, or a suitable isolated test arrangement, rather than casually disturbing a powered breadboard. Check the first failure, the thirty-second expiry and the first valid recovery. Restart the access point separately and confirm that serial acquisition continues while browser requests fail.

Finally, exercise edge cases in software: non-finite values, humidity outside the accepted range, repeated failed reads, the threshold deadband, and a millisecond-counter rollover near its maximum. The table below is a set of expected outcomes, not a record of physical tests already performed. In your report, add the firmware revision, configuration, observed output and pass/fail result for every case. If the observed behavior differs, keep the evidence and explain the correction; a documented fault investigation is stronger than a screenshot with an unexplained green label.

Validation plan · expected outcomes to verify on hardware
Test case Expected behavior Evidence to collect
First valid pair Fresh; sequence advances Serial + API agree
One failed acquisition Stale; current values and alarm null Failure count + lastGood age
No valid pair for > 30 s Unavailable; history stays labeled Expiry transition
Access point offline Acquisition continues; browser shows connection loss Serial continuity + browser state
Valid sample after expiry Fresh; filter resets; failures clear Recovery snapshot
Rapid repeated requests No extra sensor acquisitions Unchanged sequence between samples
12

Review the design and plan the next iteration

S

Student

Question 12

Can we finish with a clear explanation of what I have built, which parts I should demonstrate, and which improvements are worth leaving for a later version? I want the report to tell the engineering story, not just show screenshots.

TEP AI

Project guide

Your story starts with a requirement: deliver useful room measurements without disguising failures. The hardware layer provides a defined sensor connection. The acquisition layer reads at a controlled interval and validates the pair. The state layer distinguishes current information from history. The network layer publishes that state without controlling acquisition, and the browser communicates both sensor faults and connection loss. Each responsibility has a boundary that you can describe and test.

Demonstrate the basic serial sketch first, then the extended reference firmware. Explain the GPIO mismatch as a small debugging example, show why a failed measurement is not converted to zero, and compare fresh, stale and unavailable snapshots. Describe smoothing as a presentation choice with lag, and explain why an unknown alarm is different from a normal reading. Include your wiring diagram, source files, configuration and a validation table filled with your own observations.

For the next iteration, choose one extension with a clear reason: persistent logging, a better enclosure, a different sensor, authenticated remote access or a multi-device dashboard. Do not add all of them at once. The design and source here form an educational reference, with illustrative conversation and sample data; physical accuracy, network recovery and target-board compilation still need verification in your environment. Once you record those results, you will have a project report that explains both the successful path and the limits of the system, rather than simply asserting that the monitor works.

A complete reference design, ready for your validation

Two firmware milestones, a defined data contract, a local dashboard and a fault-test plan. Add your own hardware measurements to complete the engineering evidence.

Your idea can start here, too.

Use this example as a starting point for your own project.

Start my project

Curated example · The student dialogue and output are illustrative. No real user chat or physical test is represented.

Project details