/ #esp32 #home automation 

Build a Room Occupancy Tracker with ESP32 and Ultrasonic Sensors

I’ve been tinkering with smart home automation for years, always looking for clever ways to make my home more responsive. Commercial room occupancy sensors work fine, but they’re expensive and often limited in functionality. After one too many instances of my smart lights turning off while I was still reading in my favorite armchair, I decided to build my own room occupancy tracker using an ESP32 and ultrasonic sensors.

The beauty of this approach? You get precise, real-time occupancy data that integrates seamlessly with Home Assistant, all for under $15 in components. Let me show you how I built mine.

Why I Chose Ultrasonic Sensors Over PIR

Most commercial occupancy sensors use Passive Infrared (PIR) technology, which detects motion through heat changes. The problem? PIR sensors have a fatal flaw: if you stay still for too long, they think you’ve left the room. Not ideal for home offices or reading nooks.

Ultrasonic sensors work by emitting high-frequency sound waves and measuring how long they take to bounce back. By monitoring subtle changes in distance readings, we can detect stationary presence with remarkable accuracy. It’s like having a miniature sonar system for your room.

What You’ll Need

Before we dive in, here’s the shopping list:

  • ESP32 Development Board (Amazon | AliExpress) - The brains of the operation
  • HC-SR04 Ultrasonic Sensor (Amazon | AliExpress) - For distance measurement
  • Breadboard and Jumper Wires - For easy prototyping
  • Micro-USB Cable - For power and programming
  • 5V Power Supply - Optional for permanent installation
Pro Tip: The HC-SR04 operates at 5V, but the ESP32's GPIO pins are 3.3V. We'll use a simple voltage divider to protect your board—don't skip this!

Hardware Setup: Connecting the Dots

Let’s wire everything up. The HC-SR04 has four pins: VCC, Trig, Echo, and GND.

Here’s how to connect them to your ESP32:

  1. HC-SR04 VCC → ESP32 5V pin
  2. HC-SR04 GND → ESP32 GND pin
  3. HC-SR04 Trig → ESP32 GPIO 5 (via voltage divider)
  4. HC-SR04 Echo → ESP32 GPIO 18 (via voltage divider)

The Essential Voltage Divider

Since the HC-SR04’s Echo pin outputs 5V, we need to step it down to 3.3V for the ESP32. Create a simple voltage divider with two resistors:

  • Connect a 1kΩ resistor between Echo and GPIO 18
  • Connect a 2kΩ resistor between GPIO 18 and GND

This will reduce the 5V signal to approximately 3.3V, keeping your ESP32 safe from damage.

ESPHome Configuration: The Magic Sauce

Now for the software part. I’m using ESPHome because it makes deploying to ESP32 devices incredibly simple and integrates perfectly with Home Assistant.

Create a new ESPHome configuration file called room-occupancy.yaml:

esphome:
  name: room-occupancy-tracker
  platform: ESP32
  board: nodemcu-32s

wifi:
  ssid: "Your_WiFi_SSID"
  password: "Your_WiFi_Password"

# Enable logging for debugging
logger:

# Enable Home Assistant API
api:
  encryption:
    key: "your_encryption_key_here"

ota:
  password: "your_ota_password"

# Ultrasonic sensor configuration
sensor:
  - platform: ultrasonic
    trigger_pin: GPIO5
    echo_pin: GPIO18
    name: "Room Distance"
    id: room_distance
    update_interval: 1s
    unit_of_measurement: "cm"
    filters:
      - median:
          window_size: 5
          send_every: 3
      - throttle: 500ms
    on_value:
      then:
        - if:
            condition:
              - lambda: 'return (id(room_distance).state > 30.0) && (id(room_distance).state < 300.0);'
            then:
              - binary_sensor.template.publish:
                  id: room_occupied
                  state: ON
            else:
              - binary_sensor.template.publish:
                  id: room_occupied
                  state: OFF

# Binary sensor to represent occupancy
binary_sensor:
  - platform: template
    name: "Room Occupied"
    id: room_occupied
    device_class: occupancy

Let me break down the key parts:

  • The median filter smooths out erratic readings by taking the middle value of the last 5 measurements
  • throttle prevents the sensor from updating too frequently
  • The lambda function checks if the distance is within our “occupied” range (30cm to 300cm in this example)
  • The template binary sensor provides a clean ON/OFF occupancy state for Home Assistant

Calibration is Key

You’ll need to adjust the distance thresholds based on your room layout. Here’s how I calibrated mine:

# Alternative calibration method with multiple zones
sensor:
  - platform: ultrasonic
    # ... previous configuration
    on_value:
      then:
        - if:
            condition:
              - lambda: |-
                  static float empty_distance = 400.0; // Distance when room is empty
                  float current = id(room_distance).state;
                  return (abs(empty_distance - current) > 50.0) && (current < 350.0);                  
            then:
              - binary_sensor.template.publish:
                  id: room_occupied
                  state: ON
Installation Tip: Mount the sensor facing the area you want to monitor, about 2-2.5 meters high. Avoid pointing it directly at HVAC vents or windows, as air movement can cause false readings.

Home Assistant Integration

Once your ESP32 is running ESPHome, it should automatically appear in Home Assistant. If it doesn’t, check your ESPHome logs and make sure both devices are on the same network.

Create a simple automation to test your new sensor:

# In your Home Assistant automations.yaml
- alias: "Turn on lights when room occupied"
  trigger:
    platform: state
    entity_id: binary_sensor.room_occupied
    to: "on"
  action:
    service: light.turn_on
    entity_id: light.room_lights
    data:
      brightness: 255

- alias: "Turn off lights when room empty"
  trigger:
    platform: state
    entity_id: binary_sensor.room_occupied
    to: "off"
    for:
      minutes: 5
  action:
    service: light.turn_off
    entity_id: light.room_lights

Troubleshooting Common Issues

I’ve built several of these sensors, and here are the problems I’ve encountered:

Problem: Inconsistent readings or sensor timeout

  • Solution: Increase the update_interval to 2s and ensure your voltage divider is correctly wired. The HC-SR04 needs time between measurements.

Problem: False positives (says occupied when empty)

  • Solution: Adjust your distance thresholds and add a longer median filter window. Moving objects like ceiling fans can trigger the sensor.

Problem: ESP32 won’t connect to WiFi

  • Solution: Check your WiFi credentials and signal strength. The ESP32 can be fussy about 5GHz networks, so try 2.4GHz if you’re having issues.

Problem: Sensor readings are consistently wrong

  • Solution: Measure the actual empty-room distance and update your thresholds accordingly. Room geometry affects ultrasound reflection patterns.

Taking It Further

Once you have basic occupancy tracking working, here are some enhancements I’ve implemented:

Multiple Sensor Array: Place sensors at different heights and angles for more accurate whole-room coverage.

Presence Probability: Instead of simple ON/OFF, calculate a probability score based on multiple sensors and recent activity patterns.

Energy Monitoring Integration: Combine with smart plug energy data to distinguish between “person present” and “just electronics running.”

Time-based Sensitivity: Reduce sensitivity during nighttime hours to prevent false triggers from pets or house noises.

Frequently Asked Questions

Q: How accurate is ultrasonic occupancy detection compared to PIR? A: Ultrasonic is significantly better for detecting stationary presence but can be more susceptible to false triggers from air movement or vibrations. For most home use, it’s a worthwhile trade-off.

Q: Can I use multiple sensors in one room? A: Absolutely! You’ll need to configure them on different GPIO pins and potentially add delays between measurements to prevent interference.

Q: What’s the maximum range for reliable detection? A: The HC-SR04 works best within 2-4 meters. For larger spaces, consider using multiple sensors or upgrading to industrial-grade ultrasonic sensors.

Q: How does this compare to mmWave sensors? A: mmWave radar sensors (like the LD2410) are more accurate and can detect breathing, but they’re also more expensive and complex to set up. Ultrasonic strikes a great balance of cost and performance.

Q: Can I battery-power this setup? A: The ESP32 and HC-SR04 are relatively power-hungry. For battery operation, consider using deep sleep mode and only taking measurements periodically.

Building your own room occupancy tracker is not just about saving money—it’s about having complete control over your smart home’s behavior. The satisfaction of walking into a room and having the lights turn on automatically, knowing you built the system yourself, is hard to beat.

If you enjoyed this project, you might want to check out my other ESP32 tutorials like monitoring home temperature with ESP32 and Xiaomi sensors or setting up Zigbee2MQTT with Aqara sensors.

Happy building!