Smart Garage Door Opener with Environmental Sensors

Introduction

This project’s goal is to connect my garage door opener to Home Assistant while also collecting environmental data. My garage door opener uses the Chamberlain Security+ 2.0 protocol, so, unfortunately, a simple relay for the door button wires does not work. Fortunately, Paul Wieland has developed and sells ratgdo shields that allow you to use an ESP8266 or ESP32 to control the garage door opener. Finally, I use the esphome-ratgdo custom component to use ESPHome to control the Garage Door Opener functions while also collecting data from various sensors.

The main focus of this post is to show how I used off-the-shelf components to collect environmental data in my garage rather than the ratgdo project, though I highly recommend the ratgdo shields! The air temperature, humidity, and pressure are all measured. The distance from the ceiling to below is collected. This distance determines whether or not the car is in the garage. The motion sensor automatically turns on the door opener lights when someone enters the garage. All the sensor modules use I2C communication and have Adafruit’s Stemma/SparkFun’s Qwiic connectors for easy development. To use only I2C sensors, I contributed implementations to the ESPHome project for the BMP581, Qwiic PIR (not accepted into the project yet), and Zio Ultrasonic sensors. Instead, I could have used the existing components for the PIR sensor and the ultrasonic distance sensor. However, this would require two more GPIOs on the ESP32. Using I2C sensors allow me to keep my wiring minimal, especially since the ESP32 and ratgdo shield are next to the garage door opener, and I wanted to mount the sensors in a more optimal position.

    Sensors mounted on garage ceiling with an RJ11 cable leading to the garage door opener and the ESP32 on a ratgdo shield.

Components

Sensors

The sensors are attached to the Swirly Mounting Grid using hex standoffs. The right-angle RJ11 breakout is similarly attached, with right-angle pins soldered into place. I used a female jump to Qwiic cable to connect to the first sensor. I then used regular Qwiic cables to connect each sensor to the next in a daisy chain. The cables pass through the mounting grid holes with care.

I used a heavy-duty wire cutter to cut off the excess mounting grid. I then placed the cut-off portion on the bottom of the other grid and used M3 screws to connect the two. This extension allowed me to use a 3M Medium Command Strip to attach the sensors to the garage ceiling. Without the extension, the screws from the standoffs extended too far for the Command strip to connect with the one on the ceiling.

    Sensors mounted on Adafruit Swirly before cutting.

    Underneath view of sensors mounted on Adafruit Swirly before cutting.

    Sensors on Adafruit Swirly after cutting excess.

    Excess Swirly material mounted to sensor board with M3 screws.

    Medium command strip applied to bottom of sensors mounted on Adafruit Swirly.

ESP32 on ratgdo

I soldered an RJ11 breakout directly to the LTC4311 I2C extender board. I used a Qwiic Cable to male jumper adapter to connect it to the ESP32. I soldered some headers on the ESP32 specifically for the I2C connection, as the ratgdo shield seems to have additional circuitry for the broken-out pins. The ratgdo shield is attached to the Garage door opener following the original instructions.

    RJ11 breakout board soldered directly to LTC4311 module.

    Another view of RJ11 breakout board soldered directly to LTC4311 module.

    ESP32 D1 mini on ratgdo shield, with wiring for I2C sensors.

    LTC4311 I2C extender connected to ESP32 D1 mini on ratgdo shield.

    Wires on garage door opener for ratgdo control.

ESPHome Configuration Highlights

I used the basic configuration from the ESPHome-Ratgdo project as a basis. I do not have any reed switches connected to the ratgdo shield, so I removed them from the configuration. Here are some of the highlights of my ESPHome configuration file.

Ultrasonic Sensor to Detect If Car is Present

The ultrasonic sensor measures the distance from the ceiling to whatever is below it. When the car is in the garage, the distance is much less than if not. The sensor measures distance in mm, but this measurement is noisy. This noise does not affect the car presence detection, so I convert mm to m using the multiply filter and keep only one decimal of accuracy. The sensor only updates the distance to HA every minute or if the distance changes by 0.1 m using the or, delta, and throttle filters.

A template binary sensor determines the car’s presence state. If the distance is more than 2m, then nothing is below the ceiling; i.e., the car is not in the garage. I use the invert filter to get the correct logic and the delayed_on_off filter to debounce any noise.


sensor:
  - platform: zio_ultrasonic	# included in ESPHome 2023.7
    name: "Distance"
	id: ultrasonic_distance
    update_interval: 1s
    unit_of_measurement: "m"
    accuracy_decimals: 1
    filters:
      - multiply: 0.001     # convert mm to m
      - or:
          - delta: 0.1      # publish if distance changes more than 0.1 meters
          - throttle: 60s	# publish every 60s

binary_sensor:
  # Car Presence from Distance Sensor
  - platform: analog_threshold
    name: "Car"
    sensor_id: ultrasonic_distance
    device_class: presence
    icon: mdi:car
    threshold: 2    # if the distance is more than 2m, then no car is present
    filters:
      - invert
      - delayed_on_off: 5s

Motion Sensor to Turn On/Off Lights

The Qwiic PIR Motion sensor is an internal component in ESPHome. If it detects motion, it turns on the garage door opener light using the ratgdo component. A copy sensor turns off the light after not observing any motion for 2.5 minutes. Finally, another copy sensor sends the motion sensor state to Home Assistant, with the delayed_on_off filter debouncing the signal.


external_components:
  - source: github://pr#5194
    components: [ qwiic_pir ]  

binary_sensor:
  # Qwiic PIR Motion Sensor
  - platform: qwiic_pir
    id: qwiic_pir_motion
    on_press:
      then:
        - light.turn_on: ${id_prefix}_light  	

  # Motion sensor filtered for internally turning light off
  - platform: copy
    source_id: qwiic_pir_motion
    id: motion_light_off_delay_filter
    filters:
      - delayed_off: 2.5min
    on_release:
      then:
        - light.turn_off: ratgdo_light 		

  # Motion sensor filtered for HA
  - platform: copy
    source_id: qwiic_pir_motion
    name: "Motion"  
    filters:
      - delayed_on_off:   # debounce the motion state
          time_on: 50ms
          time_off: 15s  

Environmental Sensors

The SHT45 is a very accurate temperature and humidity sensor. The BMP581 is a very accurate pressure sensor. The raw measurements are internal sensors. These measurements determine the absolute humidity and dew point in the garage. Copy sensors send the average value to Home Assistant every 15 seconds using the throttle_average filter.


external_components:
  - source: github://pr#4657	# included in ESPHome 2023.8 beta
    components: [ bmp581 ]    

sensor:
  # SHT45 Temperature and Humidity Sensor
  - platform: sht4x
    update_interval: 0.25s
    temperature:
      id: sht45_temperature
    humidity:
      id: sht45_humidity

  # BMP581 Pressure and Temperature Sensor
  - platform: bmp581
    update_interval: 0.25s
    address: 0x47
    temperature:
      id: bmp581_temperature
      oversampling: 8x
    pressure:
      id: bmp581_pressure
      oversampling: 128x

  # Absolute Humidity Sensor derived from temperature and humidity
  - platform: absolute_humidity
    temperature: sht45_temperature
    humidity: sht45_humidity
    id: absolute_humidity_sensor

  # Dew point Sensor computed using Magnus-Tetens formula (uncertain up to 0.35 degrees C)
  - platform: template
    id: dew_point
    update_interval: 1s
    device_class: temperature
    unit_of_measurement: "°C"
    accuracy_decimals: 1                                # formula is uncertain up to 0.35 degrees C = 0.63 degrees F for usual indoor temperatures
    lambda: |- 
        const float alpha = 6.112;                      // (hPa)
        const float beta = 17.62;
        const float lambda = 243.12;                    // (degrees C)

        float RH = id(sht45_humidity).state;               // Relative Humidity
        float T = id(sht45_temperature).state;                    // Temperature in (degrees C)

        float H = log( RH/100 ) + beta*T/(lambda+T);
        return (lambda)*H/(beta - H);  

  # SHT45 Sensor
  - platform: copy
    source_id: sht45_temperature
    name: "Temperature"
    accuracy_decimals: 1
    filters:
      - throttle_average: 15s
  - platform: copy
    source_id: sht45_humidity
    name: "Humidity"
    accuracy_decimals: 1
    filters:
      - throttle_average: 15s

    # BMP581 Pressure Sensor
  - platform: copy
    source_id: bmp581_pressure
    name: "Pressure"
    filters:
      - throttle_average: 15s
  - platform: copy
    source_id: bmp581_temperature
    name: "BMP581 Temperature"
    entity_category: diagnostic    
    filters:
      - throttle_average: 15s

  # Humidity Derived Sensors
  - platform: copy
    source_id: absolute_humidity_sensor
    name: "Absolute Humidity"
    filters:
      - throttle_average: 15s     
  - platform: copy
    source_id: dew_point
    name: "Dew Point"
    filters:
      - throttle_average: 15s