Module 6: Sensors Deep Dive

Level: 🟡 Intermediate
Board: Arduino Uno
Prerequisites: Modules 1–5
Estimated time: 70–90 minutes
Goal: Work with a variety of sensors and learn to evaluate new ones independently.


What You'll Learn

Welcome to Intermediate. From this module forward, you won't always get complete wiring diagrams. You'll read datasheets and figure out the connections yourself. That's not cruelty, it's the single most valuable skill in electronics: the ability to work with any component you buy, not just the ones in a tutorial.

In this module, you'll work with four sensors (temperature/humidity, ultrasonic distance, motion, and soil moisture), learn to install and evaluate libraries, and implement noise filtering techniques that make your readings reliable.


6.1 Temperature & Humidity: DHT11 and DHT22

The Sensors

Spec DHT11 DHT22
Temperature range 0–50°C −40–80°C
Temperature accuracy ±2°C ±0.5°C
Humidity range 20–80% 0–100%
Humidity accuracy ±5% ±2–5%
Sample rate 1 reading/second 1 reading/2 seconds
Price ~$1 ~$3
Operating voltage 3.3V–5.5V 3.3V–5.5V
Current draw 2.5 mA 2.5 mA

Both use a single-wire proprietary protocol (not I2C or SPI). A pull-up resistor (10kΩ) is needed on the data line, though many breakout boards include one.

Wiring (from the datasheet)

Task: Look up the DHT22 datasheet. You'll find these pins:

Pin Function
1 VCC (3.3V–5.5V)
2 DATA
3 Not connected
4 GND

Connect VCC to 5V, GND to GND, DATA to a digital pin with a 10kΩ pull-up resistor to VCC. If using a breakout board, the pull-up is likely already on the board; check the schematic on the board's product page.

Installing the Library

  1. In the Arduino IDE: Sketch → Include Library → Manage Libraries
  2. Search for "DHT sensor library" by Adafruit
  3. Install it (it will also ask to install the "Adafruit Unified Sensor" dependency; install that too)

Reading Temperature and Humidity

/*
 * Module 6: DHT22 Temperature & Humidity
 *
 * Circuit (from datasheet):
 * - DHT22 pin 1 (VCC) → 5V
 * - DHT22 pin 2 (DATA) → digital pin 2 + 10kΩ pull-up to 5V
 * - DHT22 pin 4 (GND) → GND
 *
 * Library: Adafruit DHT Sensor Library
 * Board: Arduino Uno
 */

#include <DHT.h>

const int DHT_PIN = 2;
const int DHT_TYPE = DHT22;  // Change to DHT11 if using that sensor

DHT dht(DHT_PIN, DHT_TYPE);

void setup() {
  Serial.begin(9600);
  dht.begin();
  Serial.println("DHT22 sensor ready");
}

void loop() {
  // DHT22 needs 2 seconds between readings
  delay(2000);

  float humidity = dht.readHumidity();
  float tempC = dht.readTemperature();
  float tempF = dht.readTemperature(true);  // Fahrenheit

  // Check for read failures
  if (isnan(humidity) || isnan(tempC)) {
    Serial.println("ERROR: Failed to read from DHT sensor");
    Serial.println("Check: wiring, pull-up resistor, timing (2s min between reads)");
    return;
  }

  // Heat index (feels-like temperature)
  float heatIndex = dht.computeHeatIndex(tempC, humidity, false);

  Serial.print("Temp: ");
  Serial.print(tempC, 1);
  Serial.print("°C (");
  Serial.print(tempF, 1);
  Serial.print("°F)  Humidity: ");
  Serial.print(humidity, 1);
  Serial.print("%  Heat Index: ");
  Serial.print(heatIndex, 1);
  Serial.println("°C");
}

Evaluating the Library

When installing any library, check:

The Adafruit DHT library is well-maintained, widely used, and includes examples. Some alternatives exist (SimpleDHT, DHT_nonblocking); choose based on your needs.


6.2 Distance Measurement: HC-SR04 Ultrasonic Sensor

How It Works

The HC-SR04 sends an ultrasonic pulse (40 kHz) and measures how long it takes for the echo to return. Distance is calculated from the round-trip time.

Distance = (echo_duration × speed_of_sound) / 2
Speed of sound ≈ 343 m/s at 20°C = 0.0343 cm/µs

Datasheet Specs

Spec Value
Operating voltage 5V DC
Operating current 15 mA
Range 2 cm – 400 cm
Accuracy ±3 mm
Measuring angle 15° cone
Trigger pulse 10 µs HIGH

Wiring (4 pins)

HC-SR04 Pin Arduino Pin
VCC 5V
Trig Any digital pin (output)
Echo Any digital pin (input)
GND GND

Add a 0.1µF decoupling capacitor between VCC and GND, close to the sensor.

Code with Proper Error Handling

/*
 * Module 6: HC-SR04 Distance Measurement
 *
 * Circuit:
 * - VCC → 5V, GND → GND
 * - Trig → pin 10, Echo → pin 11
 * - 0.1µF cap across VCC/GND near sensor
 *
 * Board: Arduino Uno
 */

const int TRIG_PIN = 10;
const int ECHO_PIN = 11;

// Speed of sound varies with temperature
// At 20°C: 343.2 m/s = 0.03432 cm/µs
const float SPEED_OF_SOUND_CM_PER_US = 0.0343;

void setup() {
  Serial.begin(9600);
  pinMode(TRIG_PIN, OUTPUT);
  pinMode(ECHO_PIN, INPUT);
  Serial.println("HC-SR04 ready");
}

float measureDistanceCM() {
  // Ensure trigger is LOW
  digitalWrite(TRIG_PIN, LOW);
  delayMicroseconds(2);

  // Send 10µs trigger pulse
  digitalWrite(TRIG_PIN, HIGH);
  delayMicroseconds(10);
  digitalWrite(TRIG_PIN, LOW);

  // Measure echo duration (timeout: 30ms ≈ 500cm round trip)
  unsigned long duration = pulseIn(ECHO_PIN, HIGH, 30000);

  if (duration == 0) {
    return -1.0;  // No echo — object out of range or error
  }

  // Calculate distance
  float distance = (duration * SPEED_OF_SOUND_CM_PER_US) / 2.0;

  // Reject readings outside sensor's rated range
  if (distance < 2.0 || distance > 400.0) {
    return -1.0;
  }

  return distance;
}

void loop() {
  float distance = measureDistanceCM();

  if (distance < 0) {
    Serial.println("Out of range or no echo");
  } else {
    Serial.print("Distance: ");
    Serial.print(distance, 1);
    Serial.println(" cm");
  }

  delay(100);  // Minimum 60ms between measurements recommended
}

Temperature-Compensated Distance

For higher accuracy, adjust the speed of sound based on temperature:

// Speed of sound (m/s) ≈ 331.3 + (0.606 × temperature_celsius)
float speedOfSound = 331.3 + (0.606 * temperatureC);
float speedCmPerUs = speedOfSound / 10000.0;
float distance = (duration * speedCmPerUs) / 2.0;

If you have a DHT22, you already have temperature data; combine them for more accurate distance readings.


6.3 Motion Detection: PIR Sensor

How It Works

A PIR (Passive Infrared) sensor detects changes in infrared radiation, the kind emitted by warm bodies (humans, animals). It doesn't measure distance or direction; it just says "something warm moved."

Datasheet Specs (typical HC-SR501)

Spec Value
Operating voltage 4.5V–20V
Output Digital HIGH (3.3V) when motion detected
Detection range 3–7 meters (adjustable via potentiometer)
Detection angle ~120° cone
Delay time 0.3s–5 minutes (adjustable via potentiometer)
Warm-up time 30–60 seconds after power-on

Important PIR Behavior

Wiring

PIR Pin Arduino Pin
VCC 5V
OUT Any digital pin (input)
GND GND

Code

/*
 * Module 6: PIR Motion Detection
 *
 * Circuit:
 * - PIR VCC → 5V
 * - PIR OUT → pin 3
 * - PIR GND → GND
 *
 * Note: Allow 60 seconds warm-up after power-on.
 *
 * Board: Arduino Uno
 */

const int PIR_PIN = 3;
const int LED_PIN = 13;  // Built-in LED as indicator
const unsigned long WARMUP_TIME = 60000;  // 60 seconds

bool warmedUp = false;

void setup() {
  Serial.begin(9600);
  pinMode(PIR_PIN, INPUT);
  pinMode(LED_PIN, OUTPUT);

  Serial.println("PIR sensor warming up (60 seconds)...");
  Serial.println("Avoid moving near the sensor during warm-up.");
}

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

  // Wait for warm-up
  if (!warmedUp) {
    if (now < WARMUP_TIME) {
      // Blink LED during warm-up
      digitalWrite(LED_PIN, (now / 500) % 2);
      return;
    }
    warmedUp = true;
    Serial.println("PIR ready — monitoring for motion");
    digitalWrite(LED_PIN, LOW);
  }

  // Read PIR output
  int motionDetected = digitalRead(PIR_PIN);

  if (motionDetected == HIGH) {
    digitalWrite(LED_PIN, HIGH);
    Serial.print("[");
    Serial.print(now / 1000);
    Serial.println("s] Motion detected!");
  } else {
    digitalWrite(LED_PIN, LOW);
  }

  delay(100);
}

6.4 Soil Moisture Sensor

How It Works

A soil moisture sensor uses two probes that measure the electrical resistance (or capacitance, in capacitive models) of the soil between them. Wet soil conducts better than dry soil, so:

Recommendation: Capacitive sensors are preferred because they don't corrode over time. Resistive probes degrade because the DC current causes electrolysis. If you use resistive probes, only power them briefly when taking a reading to extend their life.

Wiring (capacitive v1.2)

Sensor Pin Arduino Pin
VCC 3.3V or 5V (check your model)
GND GND
AOUT Any analog pin (A0–A5)

Code with Calibration

/*
 * Module 6: Soil Moisture Sensor
 *
 * Circuit:
 * - Sensor VCC → 3.3V (check your sensor's rated voltage)
 * - Sensor GND → GND
 * - Sensor AOUT → A1
 *
 * Calibration required: measure dry air and water values to set range.
 *
 * Board: Arduino Uno
 */

const int MOISTURE_PIN = A1;

// --- CALIBRATION VALUES ---
// Step 1: Hold sensor in air → record the reading (DRY value)
// Step 2: Submerge sensor in water → record the reading (WET value)
// Replace these with YOUR measured values:
const int DRY_VALUE = 620;   // Reading in dry air
const int WET_VALUE = 310;   // Reading submerged in water

void setup() {
  Serial.begin(9600);
  Serial.println("Soil Moisture Sensor Ready");
  Serial.println("Calibration mode: raw values and percentage shown");
}

void loop() {
  int rawValue = analogRead(MOISTURE_PIN);

  // Map raw reading to 0–100% moisture
  int moisturePercent = map(rawValue, DRY_VALUE, WET_VALUE, 0, 100);
  moisturePercent = constrain(moisturePercent, 0, 100);

  Serial.print("Raw: ");
  Serial.print(rawValue);
  Serial.print("  Moisture: ");
  Serial.print(moisturePercent);
  Serial.print("% — ");

  // Interpret the reading
  if (moisturePercent < 20) {
    Serial.println("VERY DRY — water immediately");
  } else if (moisturePercent < 40) {
    Serial.println("DRY — water soon");
  } else if (moisturePercent < 70) {
    Serial.println("MOIST — good level");
  } else {
    Serial.println("WET — no watering needed");
  }

  delay(2000);
}

6.5 Calibration: Why Raw Readings Aren't Enough

Sensor readings are numbers, not units. A photoresistor reading of 450 doesn't mean "450 lumens." It means "450 out of 1023 on this particular voltage divider with this particular resistor in this particular lighting condition."

Calibration Process

1. Identify known reference points

2. Record raw readings at each reference point

Reference: 0°C → Raw reading: 213
Reference: 100°C → Raw reading: 847

3. Map raw readings to real units

float tempC = map(rawReading, 213, 847, 0, 100);

4. Verify intermediate values Measure at a known middle point (e.g., body temperature 37°C) and confirm the mapped value matches.

When Calibration Drifts

Sensors change over time (especially cheap ones). Temperature affects resistance. Humidity affects soil sensors. Plan to recalibrate periodically, and consider storing calibration values in EEPROM so they survive power cycles:

#include <EEPROM.h>

// Save calibration
EEPROM.put(0, dryValue);
EEPROM.put(4, wetValue);

// Load calibration
EEPROM.get(0, dryValue);
EEPROM.get(4, wetValue);

6.6 Noise Filtering: Making Readings Reliable

Raw sensor readings are noisy. Electrical interference, supply voltage fluctuations, and the ADC's own limitations cause readings to jump around even when the physical quantity is steady.

Moving Average Filter

The simplest approach: average the last N readings.

const int NUM_SAMPLES = 10;
int readings[NUM_SAMPLES];
int readIndex = 0;
long total = 0;

void setup() {
  // Initialize array
  for (int i = 0; i < NUM_SAMPLES; i++) {
    readings[i] = 0;
  }
}

int smoothedRead(int pin) {
  // Subtract the oldest reading
  total -= readings[readIndex];

  // Read the new value
  readings[readIndex] = analogRead(pin);

  // Add the new reading
  total += readings[readIndex];

  // Advance to next position
  readIndex = (readIndex + 1) % NUM_SAMPLES;

  // Return the average
  return total / NUM_SAMPLES;
}

Trade-off: More samples = smoother readings, but slower response to actual changes. 10 samples is a good starting point.

Median Filter

Takes the middle value of the last N readings. Better than averaging for rejecting sudden spikes (outliers).

int medianOfThree(int pin) {
  int a = analogRead(pin);
  delay(10);
  int b = analogRead(pin);
  delay(10);
  int c = analogRead(pin);

  // Sort and return middle value
  if (a > b) { int t = a; a = b; b = t; }  // a <= b
  if (b > c) { int t = b; b = c; c = t; }  // b <= c
  if (a > b) { int t = a; a = b; b = t; }  // a <= b
  return b;  // Middle value
}

When to Use Which

Filter Best for Weakness
Moving average Steady signals with small noise Lags behind rapid changes, affected by outliers
Median Signals with occasional spike noise Slower (needs multiple readings per call)
Exponential (EWMA) Real-time response with smoothing Requires tuning the weight parameter

Exponential Weighted Moving Average (EWMA)

Responds faster to recent values, less memory than full moving average:

float smoothed = 0;
const float ALPHA = 0.1;  // 0.0–1.0: lower = smoother, higher = more responsive

void loop() {
  int raw = analogRead(A0);
  smoothed = (ALPHA * raw) + ((1.0 - ALPHA) * smoothed);
  Serial.println(smoothed);
  delay(50);
}

Module Project: Environment Monitor

Objective

Build a multi-sensor environment monitor that reads temperature, humidity, and distance, displays calibrated values on the Serial Monitor, and triggers threshold alerts. You must source the wiring from the datasheets; the pin assignments below are hints, not instructions.

Components Needed

Component Quantity Notes
Arduino Uno 1
USB-B cable 1
Breadboard 1 Full-size
DHT22 sensor 1
HC-SR04 ultrasonic 1
10kΩ Resistor 1 Pull-up for DHT22 data line
0.1µF Ceramic Capacitor 1 Decoupling for HC-SR04
Red LED + 220Ω 1 each High-temp alert
Green LED + 220Ω 1 each Normal status
Jumper wires 10+

Pin Assignments (verify against datasheets)

Function Pin
DHT22 data D2
HC-SR04 Trig D10
HC-SR04 Echo D11
Red LED (alert) D8
Green LED (OK) D9

The Code

/*
 * Module 6 Project: Environment Monitor
 *
 * Reads temperature, humidity (DHT22) and distance (HC-SR04).
 * Displays calibrated values with noise filtering.
 * Alerts when thresholds are exceeded.
 *
 * WIRING: Source from datasheets. Suggested pins above for reference.
 *
 * Libraries: Adafruit DHT Sensor Library
 * Board: Arduino Uno
 */

#include <DHT.h>

// --- Pin definitions ---
const int DHT_PIN = 2;
const int TRIG_PIN = 10;
const int ECHO_PIN = 11;
const int ALERT_LED = 8;
const int OK_LED = 9;

// --- Sensor setup ---
DHT dht(DHT_PIN, DHT22);

// --- Thresholds ---
const float TEMP_HIGH_THRESHOLD = 30.0;   // °C
const float TEMP_LOW_THRESHOLD = 10.0;    // °C
const float HUMIDITY_HIGH_THRESHOLD = 70.0;
const float DISTANCE_CLOSE_THRESHOLD = 20.0; // cm

// --- Smoothing (EWMA) ---
float smoothedTemp = 0;
float smoothedHumidity = 0;
float smoothedDistance = 0;
const float ALPHA = 0.2;
bool firstReading = true;

// --- Timing ---
unsigned long lastDHTRead = 0;
const unsigned long DHT_INTERVAL = 2000;
unsigned long lastDistRead = 0;
const unsigned long DIST_INTERVAL = 200;
unsigned long lastPrint = 0;
const unsigned long PRINT_INTERVAL = 1000;

void setup() {
  Serial.begin(9600);
  dht.begin();
  pinMode(TRIG_PIN, OUTPUT);
  pinMode(ECHO_PIN, INPUT);
  pinMode(ALERT_LED, OUTPUT);
  pinMode(OK_LED, OUTPUT);

  Serial.println("Environment Monitor — Module 6");
  Serial.println("Warming up sensors...");
  Serial.println("-----");
  delay(2000);  // Initial DHT stabilization
}

float measureDistanceCM() {
  digitalWrite(TRIG_PIN, LOW);
  delayMicroseconds(2);
  digitalWrite(TRIG_PIN, HIGH);
  delayMicroseconds(10);
  digitalWrite(TRIG_PIN, LOW);

  unsigned long duration = pulseIn(ECHO_PIN, HIGH, 30000);
  if (duration == 0) return -1.0;

  // Temperature-compensated speed of sound
  float speedCmUs = (331.3 + (0.606 * smoothedTemp)) / 10000.0;
  float dist = (duration * speedCmUs) / 2.0;

  if (dist < 2.0 || dist > 400.0) return -1.0;
  return dist;
}

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

  // --- Read DHT22 (every 2s) ---
  if (now - lastDHTRead >= DHT_INTERVAL) {
    lastDHTRead = now;
    float t = dht.readTemperature();
    float h = dht.readHumidity();

    if (!isnan(t) && !isnan(h)) {
      if (firstReading) {
        smoothedTemp = t;
        smoothedHumidity = h;
      } else {
        smoothedTemp = (ALPHA * t) + ((1.0 - ALPHA) * smoothedTemp);
        smoothedHumidity = (ALPHA * h) + ((1.0 - ALPHA) * smoothedHumidity);
      }
    }
  }

  // --- Read distance (every 200ms) ---
  if (now - lastDistRead >= DIST_INTERVAL) {
    lastDistRead = now;
    float d = measureDistanceCM();

    if (d > 0) {
      if (firstReading) {
        smoothedDistance = d;
        firstReading = false;
      } else {
        smoothedDistance = (ALPHA * d) + ((1.0 - ALPHA) * smoothedDistance);
      }
    }
  }

  // --- Check thresholds ---
  bool alert = false;
  String alertReasons = "";

  if (smoothedTemp > TEMP_HIGH_THRESHOLD) {
    alert = true;
    alertReasons += "HIGH TEMP ";
  }
  if (smoothedTemp < TEMP_LOW_THRESHOLD && smoothedTemp > 0) {
    alert = true;
    alertReasons += "LOW TEMP ";
  }
  if (smoothedHumidity > HUMIDITY_HIGH_THRESHOLD) {
    alert = true;
    alertReasons += "HIGH HUMIDITY ";
  }
  if (smoothedDistance > 0 && smoothedDistance < DISTANCE_CLOSE_THRESHOLD) {
    alert = true;
    alertReasons += "PROXIMITY ";
  }

  // --- Update LEDs ---
  digitalWrite(ALERT_LED, alert ? HIGH : LOW);
  digitalWrite(OK_LED, alert ? LOW : HIGH);

  // --- Print status (every 1s) ---
  if (now - lastPrint >= PRINT_INTERVAL) {
    lastPrint = now;

    Serial.print("Temp: ");
    Serial.print(smoothedTemp, 1);
    Serial.print("°C  Humidity: ");
    Serial.print(smoothedHumidity, 1);
    Serial.print("%  Distance: ");

    if (smoothedDistance > 0) {
      Serial.print(smoothedDistance, 1);
      Serial.print("cm");
    } else {
      Serial.print("---");
    }

    if (alert) {
      Serial.print("  ⚠ ALERT: ");
      Serial.print(alertReasons);
    } else {
      Serial.print("  ✓ OK");
    }
    Serial.println();
  }
}

Circuit Diagram

<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 850 480" font-family="monospace" font-size="11">
  <!-- Arduino -->
  <rect x="30" y="80" width="140" height="320" fill="#1a7a8a" stroke="#333" stroke-width="2" rx="8"/>
  <text x="100" y="110" text-anchor="middle" fill="white" font-size="14" font-weight="bold">Arduino Uno</text>

  <!-- Pins -->
  <rect x="170" y="130" width="22" height="14" fill="#2a8a9a" stroke="white" stroke-width="1" rx="2"/>
  <text x="181" y="140" text-anchor="middle" fill="white" font-size="8">5V</text>
  <rect x="170" y="150" width="22" height="14" fill="#2a8a9a" stroke="white" stroke-width="1" rx="2"/>
  <text x="181" y="160" text-anchor="middle" fill="white" font-size="8">GND</text>
  <rect x="170" y="190" width="22" height="14" fill="#2a8a9a" stroke="white" stroke-width="1" rx="2"/>
  <text x="181" y="200" text-anchor="middle" fill="white" font-size="8">D2</text>
  <rect x="170" y="240" width="22" height="14" fill="#2a8a9a" stroke="white" stroke-width="1" rx="2"/>
  <text x="181" y="250" text-anchor="middle" fill="white" font-size="8">D8</text>
  <rect x="170" y="270" width="22" height="14" fill="#2a8a9a" stroke="white" stroke-width="1" rx="2"/>
  <text x="181" y="280" text-anchor="middle" fill="white" font-size="8">D9</text>
  <rect x="170" y="310" width="22" height="14" fill="#2a8a9a" stroke="white" stroke-width="1" rx="2"/>
  <text x="181" y="320" text-anchor="middle" fill="white" font-size="8">D10</text>
  <rect x="170" y="340" width="22" height="14" fill="#2a8a9a" stroke="white" stroke-width="1" rx="2"/>
  <text x="181" y="350" text-anchor="middle" fill="white" font-size="8">D11</text>

  <!-- === DHT22 === -->
  <rect x="350" y="100" width="110" height="60" fill="#4488aa" stroke="#333" stroke-width="2" rx="6"/>
  <text x="405" y="125" text-anchor="middle" fill="white" font-size="12" font-weight="bold">DHT22</text>
  <text x="405" y="145" text-anchor="middle" fill="white" font-size="9">Temp + Humidity</text>

  <!-- DHT connections -->
  <line x1="192" y1="137" x2="350" y2="115" stroke="red" stroke-width="1.5"/>
  <text x="270" y="118" font-size="8" fill="red">VCC (5V)</text>

  <line x1="192" y1="197" x2="350" y2="130" stroke="green" stroke-width="1.5"/>
  <text x="270" y="168" font-size="8" fill="green">DATA (D2)</text>

  <!-- Pull-up resistor notation -->
  <rect x="280" y="108" width="40" height="12" fill="none" stroke="#333" stroke-width="1" rx="2"/>
  <text x="300" y="117" text-anchor="middle" font-size="7">10kΩ↑</text>

  <line x1="192" y1="157" x2="350" y2="150" stroke="blue" stroke-width="1.5"/>

  <!-- === HC-SR04 === -->
  <rect x="350" y="220" width="110" height="60" fill="#6699cc" stroke="#333" stroke-width="2" rx="6"/>
  <text x="405" y="245" text-anchor="middle" fill="white" font-size="12" font-weight="bold">HC-SR04</text>
  <text x="405" y="265" text-anchor="middle" fill="white" font-size="9">Ultrasonic</text>

  <!-- HC-SR04 connections -->
  <line x1="260" y1="137" x2="260" y2="230" stroke="red" stroke-width="1.5"/>
  <line x1="260" y1="230" x2="350" y2="230" stroke="red" stroke-width="1.5"/>
  <text x="310" y="225" font-size="8" fill="red">VCC</text>

  <line x1="192" y1="317" x2="350" y2="250" stroke="orange" stroke-width="1.5"/>
  <text x="265" y="285" font-size="8" fill="orange">Trig (D10)</text>

  <line x1="192" y1="347" x2="350" y2="265" stroke="purple" stroke-width="1.5"/>
  <text x="265" y="315" font-size="8" fill="purple">Echo (D11)</text>

  <line x1="260" y1="157" x2="260" y2="270" stroke="blue" stroke-width="1"/>
  <line x1="260" y1="270" x2="350" y2="270" stroke="blue" stroke-width="1.5"/>

  <!-- 0.1uF cap -->
  <rect x="470" y="232" width="35" height="12" fill="none" stroke="#333" stroke-width="1" rx="2"/>
  <text x="487" y="241" text-anchor="middle" font-size="7">0.1µF</text>

  <!-- === ALERT LEDs === -->
  <rect x="350" y="350" width="110" height="55" fill="#f0f0f0" stroke="#999" stroke-width="1.5" rx="6"/>
  <text x="405" y="370" text-anchor="middle" font-size="10" font-weight="bold">Status LEDs</text>

  <line x1="192" y1="247" x2="350" y2="365" stroke="#cc0000" stroke-width="1.5"/>
  <text x="265" y="362" font-size="8" fill="#cc0000">Alert (D8)</text>

  <line x1="192" y1="277" x2="350" y2="385" stroke="#009900" stroke-width="1.5"/>
  <text x="265" y="385" font-size="8" fill="#009900">OK (D9)</text>

  <!-- Legend box -->
  <rect x="560" y="100" width="240" height="175" fill="#f8f8f8" stroke="#ccc" stroke-width="1" rx="6"/>
  <text x="680" y="120" text-anchor="middle" font-size="11" font-weight="bold">Threshold Alerts</text>
  <text x="575" y="140" font-size="9">Temp &gt; 30°C → Alert LED</text>
  <text x="575" y="158" font-size="9">Temp &lt; 10°C → Alert LED</text>
  <text x="575" y="176" font-size="9">Humidity &gt; 70% → Alert LED</text>
  <text x="575" y="194" font-size="9">Distance &lt; 20cm → Alert LED</text>
  <text x="575" y="218" font-size="9">All OK → Green LED</text>
  <text x="575" y="245" font-size="9" fill="#666">EWMA filter (α=0.2) on all readings</text>
  <text x="575" y="263" font-size="9" fill="#666">Temp-compensated distance calc</text>
</svg>

Circuit Schema (JSON)

{
  "module": 6,
  "project": "Environment Monitor",
  "board": "Arduino Uno",
  "schematic": {
    "components": [
      {
        "id": "U1",
        "type": "arduino_uno",
        "pins_used": {
          "5V": "net_vcc", "GND": "net_gnd",
          "D2": "net_dht_data", "D8": "net_alert_led",
          "D9": "net_ok_led", "D10": "net_trig", "D11": "net_echo"
        }
      },
      {
        "id": "DHT1",
        "type": "dht22",
        "operating_voltage": "3.3-5.5V",
        "current_draw": "2.5mA",
        "pins": { "vcc": "net_vcc", "data": "net_dht_data", "gnd": "net_gnd" }
      },
      {
        "id": "R_PULLUP",
        "type": "resistor",
        "value": "10000", "unit": "ohm",
        "purpose": "Pull-up for DHT22 data line",
        "pins": { "pin1": "net_vcc", "pin2": "net_dht_data" }
      },
      {
        "id": "US1",
        "type": "ultrasonic_sensor",
        "model": "HC-SR04",
        "current_draw": "15mA",
        "pins": { "vcc": "net_vcc", "trig": "net_trig", "echo": "net_echo", "gnd": "net_gnd" }
      },
      {
        "id": "C1",
        "type": "capacitor_ceramic",
        "value": "100", "unit": "nF",
        "purpose": "Decoupling for HC-SR04",
        "pins": { "pin1": "net_vcc", "pin2": "net_gnd" }
      },
      {
        "id": "R_ALERT",
        "type": "resistor", "value": "220", "unit": "ohm",
        "pins": { "pin1": "net_alert_led", "pin2": "net_alert_led_anode" }
      },
      {
        "id": "LED_ALERT",
        "type": "led", "color": "red",
        "pins": { "anode": "net_alert_led_anode", "cathode": "net_gnd" }
      },
      {
        "id": "R_OK",
        "type": "resistor", "value": "220", "unit": "ohm",
        "pins": { "pin1": "net_ok_led", "pin2": "net_ok_led_anode" }
      },
      {
        "id": "LED_OK",
        "type": "led", "color": "green",
        "pins": { "anode": "net_ok_led_anode", "cathode": "net_gnd" }
      }
    ],
    "nets": [
      { "name": "net_vcc", "nodes": ["U1.5V", "DHT1.vcc", "R_PULLUP.pin1", "US1.vcc", "C1.pin1"] },
      { "name": "net_gnd", "nodes": ["U1.GND", "DHT1.gnd", "US1.gnd", "C1.pin2", "LED_ALERT.cathode", "LED_OK.cathode"] },
      { "name": "net_dht_data", "nodes": ["U1.D2", "DHT1.data", "R_PULLUP.pin2"] },
      { "name": "net_trig", "nodes": ["U1.D10", "US1.trig"] },
      { "name": "net_echo", "nodes": ["U1.D11", "US1.echo"] },
      { "name": "net_alert_led", "nodes": ["U1.D8", "R_ALERT.pin1"] },
      { "name": "net_alert_led_anode", "nodes": ["R_ALERT.pin2", "LED_ALERT.anode"] },
      { "name": "net_ok_led", "nodes": ["U1.D9", "R_OK.pin1"] },
      { "name": "net_ok_led_anode", "nodes": ["R_OK.pin2", "LED_OK.anode"] }
    ],
    "power": {
      "source": "USB",
      "total_current_ma": 60,
      "note": "Well within USB 500mA limit"
    }
  },
  "code": { "filename": "module06_environment_monitor.ino", "language": "cpp" },
  "validation": {
    "expected_behavior": "Serial shows temp, humidity, distance with alerts. LEDs indicate status. EWMA smooths readings.",
    "datasheet_tasks": [
      "Verify DHT22 pin numbering matches your specific module (breakout boards may differ)",
      "Confirm HC-SR04 trigger pulse timing from its datasheet",
      "Check if your DHT22 breakout already includes the pull-up resistor"
    ],
    "common_mistakes": [
      "DHT reads NaN: missing pull-up resistor, wrong pin, reading too fast (<2s for DHT22)",
      "Distance always -1: trigger pulse too short, echo pin not set as INPUT",
      "Temperature reading seems wrong: check if you're reading Celsius vs. Fahrenheit",
      "Alert LED always on: thresholds set wrong for your environment"
    ]
  }
}

Experiments to Try

Experiment 1: Data Logger

Print readings as CSV format and use the Serial Monitor's copy function to paste into a spreadsheet:

Serial.print(millis() / 1000);
Serial.print(",");
Serial.print(smoothedTemp, 1);
Serial.print(",");
Serial.print(smoothedHumidity, 1);
Serial.print(",");
Serial.println(smoothedDistance, 1);

Experiment 2: Compare Filters

Run the moving average, median, and EWMA filters in parallel on the same sensor. Print all three to the Serial Plotter to see how they differ.

Experiment 3: Add the Soil Moisture Sensor

Extend the environment monitor with a soil moisture sensor on A1. Add a "DRY SOIL" alert and test with actual plant soil.


Self-Check: Module 6

Before moving to Module 7, make sure you can:


Key Terms Glossary

Term Definition
DHT22 Digital temperature and humidity sensor with single-wire protocol
HC-SR04 Ultrasonic distance sensor (2–400 cm range)
PIR sensor Passive Infrared motion detector. Senses warm bodies moving
Capacitive soil sensor Measures soil moisture via capacitance change (no corrosion)
Calibration Mapping raw sensor values to real-world units using known reference points
Moving average Smoothing filter that averages the last N readings
Median filter Takes the middle value of N readings, rejecting outliers
EWMA Exponential Weighted Moving Average. Responds faster to recent changes
Pull-up resistor Keeps a data line at a known HIGH state when not actively driven
Decoupling capacitor Small capacitor near a sensor's power pins to absorb voltage noise
pulseIn() Arduino function that measures the duration of a pulse on a pin
isnan() Checks if a value is Not-A-Number, used to detect failed sensor reads
EEPROM Non-volatile memory on the Arduino. Survives power cycles
Library Manager Arduino IDE tool for finding and installing code libraries

Previous: ← Module 5: Power Management Next: Module 7: Communication Protocols: How Components Talk →