28  Light and Proximity Sensor Labs

In 60 Seconds

Light sensors (BH1750) measure ambient illumination in lux, PIR sensors detect motion via infrared radiation changes, and ultrasonic sensors (HC-SR04) measure distance using sound wave time-of-flight. Each uses a different interface (I2C, digital GPIO, trigger/echo pins) and requires specific setup considerations like pull-up resistors for I2C and temperature compensation for ultrasonic distance calculations.

28.1 Learning Objectives

By the end of this chapter, you will be able to:

  • Interface light sensors: Configure BH1750 digital lux meter and LDR analog sensors
  • Build proximity detection: Use PIR motion sensors and HC-SR04 ultrasonic distance sensors
  • Compare touch sensing technologies: Differentiate capacitive and skin-inspired tactile sensor architectures
  • Diagnose I2C communication: Scan buses, address multiple sensors, and resolve communication issues
  • Apply circuit fundamentals: Use voltage dividers and RC filters in sensor circuits

Light sensors measure how bright it is (like the automatic brightness on your phone screen), PIR motion sensors detect when a person walks by (like the sensor that turns on a porch light), and ultrasonic distance sensors measure how far away an object is by bouncing sound waves off it (like a bat navigating in the dark). These are some of the most commonly used sensors in smart home and security projects.

28.2 Prerequisites

Required Knowledge:

Hardware Requirements:

  • ESP32 development board
  • BH1750 light sensor or LDR (photoresistor)
  • PIR motion sensor (HC-SR501)
  • HC-SR04 ultrasonic distance sensor
  • Resistors (10kOhm for voltage divider, 4.7kOhm for I2C)
  • Breadboard and jumper wires

28.3 Light Sensors

28.3.1 BH1750 (Digital Light Intensity)

The BH1750 is a digital ambient light sensor with spectral response close to the human eye, making it ideal for automatic brightness adjustment.

Specifications:

  • Range: 1-65535 lux
  • Interface: I2C
  • Resolution: 1 lux
  • I2C Address: 0x23 (ADDR pin LOW/floating) or 0x5C (ADDR pin HIGH)
  • Spectral response close to human eye
  • Power: 120uA active, 0.01uA power down

ESP32 Implementation:

#include <Wire.h>
#include <BH1750.h>

BH1750 lightMeter;

void setup() {
  Serial.begin(115200);
  Wire.begin();  // Uses default ESP32 I2C pins: SDA=GPIO21, SCL=GPIO22

  if (lightMeter.begin(BH1750::CONTINUOUS_HIGH_RES_MODE)) {
    Serial.println("BH1750 initialized");
  } else {
    Serial.println("Error initializing BH1750");
  }
}

void loop() {
  float lux = lightMeter.readLightLevel();

  Serial.print("Light: ");
  Serial.print(lux);
  Serial.print(" lux");

  // Classify light levels (approximate ranges)
  String classification;
  if (lux < 1) {
    classification = "Darkness (moonlight)";
  } else if (lux < 50) {
    classification = "Very dim (street lighting)";
  } else if (lux < 200) {
    classification = "Dim (hallway)";
  } else if (lux < 500) {
    classification = "Normal (home/office)";
  } else if (lux < 2000) {
    classification = "Bright (retail/overcast)";
  } else if (lux < 25000) {
    classification = "Very bright (daylight)";
  } else {
    classification = "Extremely bright (direct sun)";
  }

  Serial.print(" - ");
  Serial.println(classification);

  delay(1000);
}
Try It: Lux Level Explorer

Adjust the lux reading to see how the BH1750 classifies different light levels. Compare with the reference table below to understand real-world lighting conditions.

Learning Points: Light Sensing

Lux Reference Values:

Condition Lux Level
Full moon 0.1-1
Street lighting 10-50
Home lighting 150-300
Office 300-500
Overcast sky 1,000-2,000
Full daylight 10,000-25,000
Direct sunlight 100,000+

Real-World Applications:

  • Smart street lights: Dim when natural light is sufficient
  • Display backlighting: Adjust screen brightness based on ambient light
  • Greenhouse automation: Supplement natural light with artificial lighting
  • Energy efficiency: Reduce power when full brightness isnโ€™t needed

28.4 Proximity & Presence Sensors

28.4.1 PIR Motion Sensor (HC-SR501)

PIR (Passive Infrared) sensors detect motion by measuring changes in infrared radiation from warm bodies (humans, animals).

Specifications:

  • Detection Range: 3-7 meters (adjustable)
  • Detection Angle: 110 degree cone
  • Output: Digital HIGH when motion detected
  • Hold Time: ~2.5 to 200 seconds (adjustable via potentiometer)
  • Power: 5V, ~65ยตA quiescent
  • Trigger Modes: Single trigger or repeatable trigger

ESP32 Implementation:

#define PIR_PIN 13  // GPIO13 for PIR sensor

bool motionDetected = false;
unsigned long lastMotionTime = 0;

void setup() {
  Serial.begin(115200);
  pinMode(PIR_PIN, INPUT);
  pinMode(LED_BUILTIN, OUTPUT);

  Serial.println("PIR Motion Sensor Test");
  Serial.println("Warming up sensor (30-60 seconds)...");
  delay(60000);  // PIR needs 30-60s warm-up time
  Serial.println("Ready!");
}

void loop() {
  int pirState = digitalRead(PIR_PIN);

  if (pirState == HIGH) {
    if (!motionDetected) {
      motionDetected = true;
      lastMotionTime = millis();

      Serial.println("MOTION DETECTED!");
      Serial.print("Time: ");
      Serial.println(millis() / 1000);

      // Trigger action (turn on light, send alert, etc.)
      digitalWrite(LED_BUILTIN, HIGH);
    }
  } else {
    if (motionDetected) {
      unsigned long motionDuration = (millis() - lastMotionTime) / 1000;
      Serial.print("Motion ended. Duration: ");
      Serial.print(motionDuration);
      Serial.println(" seconds");

      motionDetected = false;
      digitalWrite(LED_BUILTIN, LOW);
    }
  }

  delay(100);
}
Try It: PIR Detection Zone Simulator

Configure the PIR sensor parameters to visualize its detection zone. Adjust mounting height, sensitivity, and hold time to see how they affect coverage.

Learning Points: PIR Sensors

Key Characteristics:

  • PIR sensors need a 30-60 second warm-up period after power-on
  • Output is digital (HIGH/LOW), making interfacing very simple
  • The sensor stays HIGH as long as motion is detected
  • Adjustable potentiometers control sensitivity and hold time

Real-World Applications:

  • Smart lighting: Turn on lights when someone enters a room
  • Security systems: Send alerts when motion detected while away
  • Energy saving: Power down devices when no one is present
  • Occupancy counting: Track room usage patterns

28.4.2 Ultrasonic Distance Sensor (HC-SR04)

Ultrasonic sensors measure distance by timing the echo return of a 40kHz sound pulse.

Specifications:

  • Range: 2cm to 400cm
  • Accuracy: +/-3mm
  • Measuring angle: 15 degrees
  • Trigger pulse: 10us
  • Power: 5V, 15mA

The HC-SR04 operates at 5V logic, but the ESP32 GPIO pins are 3.3V. The ECHO pin outputs a 5V HIGH signal that can damage the ESP32. Use a voltage divider (two resistors, e.g., 1kOhm + 2kOhm) on the ECHO line to reduce it to ~3.3V, or use a 3.3V-compatible variant like the HC-SR04P.

ESP32 Implementation:

#define TRIG_PIN 5
#define ECHO_PIN 18

void setup() {
  Serial.begin(115200);
  pinMode(TRIG_PIN, OUTPUT);
  pinMode(ECHO_PIN, INPUT);
}

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

  Serial.print("Distance: ");
  Serial.print(distance, 1);
  Serial.println(" cm");

  // Object detection
  if (distance > 0 && distance < 50) {
    Serial.println("Object detected nearby!");
  }

  delay(100);
}

float measureDistance() {
  // Send 10us pulse
  digitalWrite(TRIG_PIN, LOW);
  delayMicroseconds(2);
  digitalWrite(TRIG_PIN, HIGH);
  delayMicroseconds(10);
  digitalWrite(TRIG_PIN, LOW);

  // Measure echo pulse duration
  long duration = pulseIn(ECHO_PIN, HIGH, 30000);  // 30ms timeout

  // Calculate distance (speed of sound = 343 m/s at 20ยฐC)
  // distance = (duration * 0.0343) / 2
  float distance = duration * 0.0343 / 2.0;

  return distance;
}

Ultrasonic sensors measure distance using the time-of-flight formula where sound travels to the object and back.

$ d = $

where \(d\) is distance, \(t\) is echo pulse duration in microseconds, and \(v_{\text{sound}}\) is 343 m/s (0.0343 cm/ยตs) at 20ยฐC.

Worked example: The HC-SR04 measures an echo pulse duration of 1,750 ยตs. Calculate the distance:

$ d = = = 30.01 , $

Temperature compensation: At 0ยฐC, speed of sound is 331.3 m/s (3.5% slower), so the same 1,750 ยตs pulse would calculate as 29.0 cm instead of 30.0 cm, introducing 1 cm error per 30 cm distance without compensation.

Adjust the echo pulse duration and ambient temperature to see how they affect the calculated distance. Notice how temperature changes the speed of sound and therefore the distance measurement.

Learning Points: Ultrasonic Sensors

Key Concepts:

  • Time-of-Flight: distance = (time * speed_of_sound) / 2
  • Speed of Sound: 343 m/s at 20ยฐC (0.0343 cm/ยตs)
  • Temperature Compensation: Speed varies ~0.6 m/s per ยฐC
  • Blind zone: Cannot measure objects closer than 2cm

Real-World Applications:

  • Parking sensors: Alert drivers to obstacles
  • Robotics: Obstacle avoidance and navigation
  • Liquid level sensing: Measure tank fill levels
  • People counting: Detect presence at doorways

Temperature Compensation Formula:

float speed_of_sound = 331.3 + (0.606 * temp_celsius);  // m/s
float distance_cm = (pulse_duration_us * speed_of_sound) / 20000;
How It Works: HC-SR04 Ultrasonic Distance Measurement

The HC-SR04 uses time-of-flight measurement with these precise steps:

  1. Send trigger pulse - 10ยตs HIGH signal on TRIG pin activates the sensor
  2. Sensor emits ultrasound - 8 cycles of 40 kHz sound burst (inaudible to humans)
  3. Wait for echo - ECHO pin goes HIGH when sound is transmitted
  4. Measure echo duration - ECHO pin goes LOW when reflected sound returns
  5. Calculate distance - distance_cm = (echo_duration_ยตs ร— 0.0343) / 2

Why divide by 2? The sound travels to the object AND back, so the total time covers twice the distance.

28.5 Touch and Skin Sensing

Beyond light and proximity, touch sensors represent an emerging area of IoT sensing. Skin-inspired (or โ€œe-skinโ€) sensors mimic the multi-layer structure of human skin, using arrays of capacitive or piezoresistive sensing elements (โ€œtaxelsโ€) to detect pressure, texture, and even temperature. These sensors are particularly relevant for robotics, prosthetics, and human-computer interaction.

Diagram of skin-inspired touch sensing technology showing biomimetic multi-layer structure with tactile receptors, pressure sensing elements, and neural interface circuitry
Figure 28.1: Skin-inspired touch sensing technology
Layout of SKIN sensor array showing grid configuration with individual taxels, wiring matrix, and multiplexing circuitry for high-resolution touch sensing
Figure 28.2: SKIN sensor array configuration
Cross-section showing SKIN sensing mechanics with force application, material deformation, capacitance change, and electrical response characteristics
Figure 28.3: SKIN sensing mechanics and response
Detailed diagram of skin mechanics for tactile sensing including epidermis, dermis layers, mechanoreceptors distribution, and force transmission pathways
Figure 28.4: Skin mechanics and tactile sensing

For IoT applications, simpler capacitive touch sensors (like the ESP32โ€™s built-in touch pins) are commonly used for user interfaces, while advanced e-skin arrays remain primarily a research area.

28.6 I2C Sensors and Bus Management

I2C bus diagram showing ESP32 master connected to BH1750 light sensor and BMP280 pressure sensor via shared SDA and SCL lines with 4.7k pull-up resistors
Figure 28.5: I2C Communication Protocol: Master-Slave Transaction Sequence

I2C Bus Scanner:

#include <Wire.h>

#define I2C_SDA 21
#define I2C_SCL 22

void setup() {
  Serial.begin(115200);
  Wire.begin(I2C_SDA, I2C_SCL);

  // Scan I2C bus
  scanI2C();
}

void scanI2C() {
  Serial.println("Scanning I2C bus...");
  int devices = 0;

  for(byte address = 1; address < 127; address++) {
    Wire.beginTransmission(address);
    byte error = Wire.endTransmission();

    if (error == 0) {
      Serial.print("I2C device found at 0x");
      if (address < 16) Serial.print("0");
      Serial.println(address, HEX);
      devices++;
    }
  }

  Serial.print("Found ");
  Serial.print(devices);
  Serial.println(" devices");
}

void loop() {
  // Empty loop
}
Try It: I2C Address Explorer

Select common I2C sensors to see their addresses and check for conflicts. The I2C bus supports up to 127 device addresses (7-bit addressing).

28.7 Circuit Fundamentals for Sensors

28.7.1 Voltage Divider with LDR

For analog light sensors (LDR/photoresistor), use a voltage divider circuit:

VCC (3.3V) ----[LDR]----+----[10kOhm]---- GND
                        |
                      ADC Pin

How it works:

  • LDR resistance decreases with more light (~1kOhm bright, ~1MOhm dark)
  • Voltage at midpoint varies with light level
  • ADC reads this varying voltage

28.7.2 RC Low-Pass Filter

Filter high-frequency noise from sensor readings:

Sensor Output ----[10kOhm]----+---- ADC Pin
                              |
                         [100nF]
                              |
                             GND

Cutoff Frequency: f_c = 1 / (2 * PI * R * C) = 159 Hz

Try It: RC Low-Pass Filter Designer

Choose resistor and capacitor values to design a noise filter for your sensor. The cutoff frequency determines which signals pass through and which are filtered out.

Adjust the LDR resistance (which changes with light level) to see how the voltage at the ADC pin changes. The fixed resistor is 10kOhm and the supply is 3.3V.

Learning Points: Circuit Fundamentals

Voltage Divider Formula:

V_out = V_in * (R2 / (R1 + R2))

RC Filter Time Constant:

tau = R * C
5*tau = time to reach steady state

Why Use Pull-up Resistors for I2C: I2C uses open-drain outputs that can only pull LOW. Pull-up resistors (4.7k Ohm typical) pull the line HIGH when no device is transmitting.

28.8 Interactive Simulator: Sensor-Controlled Servo

Try it yourself! See how sensors control actuators for automated systems.

What This Simulates: An LDR light sensor controlling a servo motor to automatically adjust window blinds based on sunlight.

How to Use:

  1. Click Start Simulation
  2. Click on the LDR sensor to adjust light levels
  3. Watch the servo motor rotate as light changes
  4. Observe the Serial Monitor showing light levels and servo angles

Learning Points: Sensor-Actuator Integration

Control Logic:

High Light (bright) -> Angle 180 -> Blinds CLOSED (shade interior)
Medium Light        -> Angle 90  -> Blinds HALF (moderate light)
Low Light (dark)    -> Angle 0   -> Blinds OPEN (maximize daylight)

Real-World Applications:

  • Automated blinds/curtains based on time of day and sunlight
  • Solar panel tracking following sun position
  • Smart vents opening/closing based on temperature
  • Camera pan/tilt tracking objects detected by motion sensors

28.9 Knowledge Check

Key Takeaway

Light and proximity sensors each serve distinct purposes: BH1750 provides calibrated lux measurements for adaptive lighting, PIR sensors detect human presence via infrared changes (with a 30-60 second warm-up), and ultrasonic sensors measure distance using sound waves (requiring temperature compensation for outdoor use). Understanding voltage dividers and RC filters is essential for interfacing analog sensors reliably.

The Sensor Squad got three new members today!

First up is Lucy the Light Sensor (BH1750). โ€œI can tell you exactly how bright it is,โ€ Lucy said. โ€œOffice lighting is about 300-500 lux, but direct sunlight is over 100,000 lux! Smart buildings use me to dim the lights when the sun is shining โ€“ saving lots of energy!โ€

Next is Pete the PIR Sensor. โ€œI detect warm-blooded creatures!โ€ Pete announced. โ€œWhen a person walks by, the infrared heat pattern changes, and I send a HIGH signal. But you have to wait about a minute after I power on โ€“ I need my warm-up time!โ€

Finally, Ulti the Ultrasonic Sensor (HC-SR04) showed off: โ€œI shout a tiny โ€˜BEEPโ€™ too high for humans to hear, then listen for the echo bouncing back. The longer it takes, the farther away the object is! I use the speed of sound โ€“ 343 meters per second at room temperature.โ€

Bella the Battery asked, โ€œWhat happens when it is cold outside?โ€ Ulti admitted: โ€œSound travels slower in cold air, so I need a thermometer friend to help me calculate distance correctly!โ€

Common Mistake: PIR False Triggers from Heat Sources

The Problem: Your PIR motion sensor triggers constantly even when no one is present, or fails to detect people walking by.

Why It Happens: PIR sensors detect changes in infrared radiation, not absolute levels. Common causes of false triggers:

  1. Heating/AC vents: Warm or cold air blowing directly on the PIR causes continuous infrared fluctuations
  2. Sunlight through windows: Moving shadows or direct sunlight changes IR levels dramatically
  3. Pets: Dogs and cats are warm-blooded and trigger PIR sensors (not a โ€œfalseโ€ alarm, but unintended)
  4. Mounting height: PIR pointed at ground level has a small detection zone; mounted at 2-2.5m gives optimal 5-7m range

Real-World Example: A smart lighting system in an office hallway triggers lights every 30 seconds with no one present. Investigation reveals the PIR sensor is mounted directly below an AC vent. Cold air blowing on the sensor creates IR fluctuations that mimic human motion.

The Fix:

// Add filtering to reduce false triggers
const int PIR_PIN = 13;
const int MIN_TRIGGER_DURATION = 500; // Milliseconds
unsigned long triggerStartTime = 0;
bool confirmedMotion = false;

void setup() {
  Serial.begin(115200);
  pinMode(PIR_PIN, INPUT);
  pinMode(LED_BUILTIN, OUTPUT);
  delay(60000);  // Wait for PIR warm-up
}

void loop() {
  int pirState = digitalRead(PIR_PIN);

  if (pirState == HIGH) {
    if (triggerStartTime == 0) {
      triggerStartTime = millis(); // Start timing
    } else if (millis() - triggerStartTime > MIN_TRIGGER_DURATION) {
      // Confirmed motion only if PIR stays HIGH for 500ms
      if (!confirmedMotion) {
        confirmedMotion = true;
        Serial.println("CONFIRMED MOTION");
        digitalWrite(LED_BUILTIN, HIGH);
      }
    }
  } else {
    // PIR went LOW - reset
    triggerStartTime = 0;
    confirmedMotion = false;
    digitalWrite(LED_BUILTIN, LOW);
  }

  delay(50);
}

Prevention Checklist:

Key Insight: PIR sensors are binary (motion / no motion) and donโ€™t provide distance or identity. For more sophisticated detection, combine PIR with ultrasonic (distance) or camera (identity).

28.10 Hands-On Challenges

Goal: Read the BH1750 light sensor and display lux values.

Steps:

  1. Connect BH1750: VCCโ†’3.3V, GNDโ†’GND, SDAโ†’GPIO21, SCLโ†’GPIO22
  2. Add 4.7kฮฉ pull-up resistors on SDA and SCL (or use module with built-in resistors)
  3. Install library: #include <BH1750.h>
  4. Initialize in setup: lightMeter.begin()
  5. Read in loop: float lux = lightMeter.readLightLevel()

Expected result: 300-500 lux in office, 10,000+ lux outdoors

Goal: Turn on LED when ambient light drops below 50 lux.

Components: BH1750 + LED + 220ฮฉ resistor + ESP32

Challenge:

  • Read light every second
  • Use hysteresis (turn on at 50 lux, turn off at 80 lux) to prevent flickering
  • Add fade-in/fade-out using PWM

Code skeleton:

float lux = lightMeter.readLightLevel();
if (lux < 50 && !ledOn) {
    fadeIn(LED_PIN);
    ledOn = true;
} else if (lux > 80 && ledOn) {
    fadeOut(LED_PIN);
    ledOn = false;
}

Goal: Use 3 PIR sensors to detect which zone has motion, light only that zoneโ€™s LEDs.

Challenge:

  • Wire 3 PIR sensors (front, middle, back)
  • Control 3 LED strips independently
  • Implement timeout (lights off 30s after last motion)
  • Add ambient light override (donโ€™t activate if already bright)

Bonus: Log motion patterns to SD card for occupancy analysis

28.11 Concept Relationships

Core Concept Related Concepts Why It Matters
Lux Measurement Human Vision Response, Lighting Standards BH1750 spectral response matches human eye
PIR Motion Detection Infrared Changes, Warm-Up Period, False Triggers Detects heat signature changes, not absolute values
Ultrasonic ToF Speed of Sound, Temperature Compensation Accuracy depends on knowing sound velocity
I2C Pull-ups Open-Drain Logic, Signal Integrity Required for reliable I2C communication

28.12 Summary

This chapter covered light and proximity sensor implementation:

  • BH1750 provides calibrated lux measurements with spectral response matching human vision
  • PIR sensors detect motion via infrared radiation changes with simple digital output
  • Ultrasonic HC-SR04 measures distance using time-of-flight with temperature compensation needed
  • I2C bus scanning identifies connected sensors and verifies communication
  • Voltage dividers convert variable resistance (LDR) to measurable voltage
  • RC filters reduce high-frequency noise from analog sensor readings

28.13 See Also

Common Pitfalls

IR proximity sensors (VCNL4040, APDS-9960) can be overwhelmed by strong sunlight or incandescent lamps. In high-ambient-light environments, shield the sensor from direct light or use ultrasonic ranging instead.

If nothing is within the sensorโ€™s 4 m range, the echo pulse may never return. Without a timeout, pulseIn() blocks for up to 1 second. Always use pulseIn(ECHO, HIGH, 30000) with a 30 ms timeout to prevent main loop blocking.

A light-dependent resistor requires a fixed reference resistor. If the reference is mismatched to the LDRโ€™s operating range, the voltage swing across the full light range is compressed. Choose the fixed resistor near the geometric mean of the LDRโ€™s bright and dark resistance values.

When two HC-SR04 sensors cover overlapping areas, sensor Aโ€™s transmitted pulse can trigger sensor Bโ€™s echo detection, causing false distance readings. Trigger sensors sequentially with 50 ms gaps between triggers.

28.14 Whatโ€™s Next?

Topic Description
Best Practices & Labs Sensor wiring standards, debugging strategies, and deployment guidelines
Calibration Lab (Wokwi) Hands-on calibration techniques using simulated ESP32 environments
Temperature Sensor Labs Thermistor, DS18B20, and thermocouple implementation on ESP32
Motion & Environmental Sensors IMU, barometric pressure, and environmental sensor integration
Sensor Circuits & Signal Conditioning Amplification, filtering, and ADC interfacing for analog sensors