Updated for 2019 China-US Young Maker Competition

I love using Home Assistant to monitor and automate my house. While I prefer using Z-Wave sensors and devices, openzwave's support for garage controls has been unstable at best in my experience. I therefore set out to create my own all-in-one multi-sensor and garage door controller with the following features:

Monitor temperature and humidity (Si7021 sensor)

Detect motion (PIR sensor)

Track whether the garage door is open/closed (magentic contact switch)

Control the garage door to open/close on command (relay)

Platform Choice

The Sparkfun ESP8266 Thing Dev board was the perfect choice for this project:

Easily programmable via USB

WiFi connectivity

Runs at 3.3V but also has ~5V available (via the VCC pin) for the PIR sensor and relay

I²C support for the Si7021 I2C temperature/humidity sensor

Community libraries available for MQTT and the Si7021 sensor

Onboard LED we can blink to show activity!

Wiring

The wiring is fairly straight-forward:

I've also described the connections below:

Si7021 Sensor

VIN (sensor) to 3V3 (ESP8266)

GND to GND

SCL to pin 14

SDA to pin 2

PIR Sensor

VCC (sensor) to VIN (ESP8266)

GND to GND

OUT to pin 16

Magnetic Contact Switch

One of the wires (switch) to GND (ESP8266)

The other wire to pin 13

Relay

VCC (relay) to VIN (ESP8266)

GND to GND

IN to pin 12

Code

The code is based off my MQTT Temperature and Humidity Monitor for Home Assistant:

#include <PubSubClient.h> #include <Adafruit_Si7021.h> #include <ESP8266WiFi.h> // START CONFIGURATION // Replace these with your WiFi credentials const char* ssid = "GetOffMyLAN"; const char* password = "correcthorsebatterystaple"; // Replace these with your MQTT connection info const char* mqtt_server = "192.168.1.12"; const char* mqtt_user = "garage"; const char* mqtt_password = "password123"; // Define which MQTT topics the data is published to #define humidity_topic "sensor/garage/humidity" #define temperature_topic "sensor/garage/temperature" #define garage_status_topic "sensor/garage/door-status" #define garage_command_topic "sensor/garage/door-command" #define motion_topic "sensor/garage/motion" // Pins #define pin_led 5 #define pin_door_status 13 #define pin_door_relay 12 #define pin_motion 16 // END CONFIGURATION WiFiClient espClient; PubSubClient client(espClient); Adafruit_Si7021 sensor; void setup() { // Configure the pins digitalWrite(pin_led, HIGH); digitalWrite(pin_door_relay, HIGH); pinMode(pin_led, OUTPUT); pinMode(pin_door_relay, OUTPUT); pinMode(pin_door_status, INPUT_PULLUP); pinMode(pin_motion, INPUT); Serial.begin(115200); delay(100); // We start by connecting to a WiFi network Serial.println(); Serial.println(); Serial.print("Connecting to "); Serial.println(ssid); WiFi.begin(ssid, password); // Blink the LED until we're connected int i = 0; while (WiFi.status() != WL_CONNECTED) { digitalWrite(pin_led, i % 2); delay(500); Serial.print("."); i++; } digitalWrite(pin_led, HIGH); Serial.println(""); Serial.println("WiFi connected"); Serial.println("IP address: "); Serial.println(WiFi.localIP()); Serial.println(""); // Connect to MQTT client.setServer(mqtt_server, 1883); client.setCallback(onMessageReceived); // Set up the climate sensor sensor = Adafruit_Si7021(); if (sensor.begin()) { Serial.println('Climate sensor ready'); } else { // TODO: We should probably reset the device if this happens Serial.println('SENSOR FAILED TO START'); } delay(50); digitalWrite(pin_led, HIGH); } // Attempts to reconnect to MQTT if we're ever disconnected void reconnect() { // Loop until we're reconnected while (!client.connected()) { digitalWrite(pin_led, LOW); Serial.print("Attempting MQTT connection..."); // Attempt to connect if (client.connect("GarageMultiSensor", mqtt_user, mqtt_password)) { Serial.println("connected"); client.subscribe(garage_command_topic); digitalWrite(pin_led, HIGH); delay(100); } else { Serial.print("failed, rc="); Serial.print(client.state()); Serial.println(" try again in 5 seconds"); // Wait 5 seconds before retrying delay(5000); } } } // Helps check whether any value change is significant enough to warrant a data push bool checkBound(float newValue, float prevValue, float maxDiff) { return !isnan(newValue) && (newValue < prevValue - maxDiff || newValue > prevValue + maxDiff); } long lastMsg = 0; float temp = 0.0; float hum = 0.0; float diff = 0.1; bool motion = false; bool doorOpen = false; void loop() { // Ensure we stay connected if (!client.connected()) { reconnect(); } // Let MQTT do its thing client.loop(); digitalWrite(pin_led, HIGH); bool sent = false; // Check the temp, humidity, and door state once per second long now = millis(); if (now - lastMsg > 1000) { lastMsg = now; // Read the values from the sensor float newTemp = sensor.readTemperature(); float newHum = sensor.readHumidity(); bool newDoorOpen = digitalRead(pin_door_status); // Check whether the temperature has changed if (checkBound(newTemp, temp, diff)) { temp = newTemp; Serial.print("New temperature:"); Serial.println(String(temp).c_str()); client.publish(temperature_topic, String(temp).c_str(), true); sent = true; } // Check whether the humidity has changed if (checkBound(newHum, hum, diff)) { hum = newHum; Serial.print("New humidity:"); Serial.println(String(hum).c_str()); client.publish(humidity_topic, String(hum).c_str(), true); sent = true; } if (doorOpen && !newDoorOpen) { doorOpen = false; Serial.println("Door closed"); client.publish(garage_status_topic, "closed", true); sent = true; } else if (!doorOpen && newDoorOpen) { doorOpen = true; Serial.println("Door open"); client.publish(motion_topic, "open", true); sent = true; } } // Constantly check for motion bool newMotion = digitalRead(pin_motion); if (motion && !newMotion) { motion = false; Serial.println("No more motion"); client.publish(motion_topic, "OFF", true); sent = true; } else if (!motion && newMotion) { motion = true; Serial.println("Motion detected"); client.publish(motion_topic, "ON", true); sent = true; } // If any data was sent (due to a change) then blink the blue LED if (sent) { sent = false; digitalWrite(pin_led, LOW); delay(100); digitalWrite(pin_led, HIGH); } } void onMessageReceived(char* topic, byte* payload, unsigned int length) { int i; char message_buff[100]; for(i=0; i<length; i++) { message_buff[i] = payload[i]; } message_buff[i] = '\0'; String msgString = String(message_buff); Serial.println("Payload: " + msgString); bool activateRelay = false; if (!doorOpen && msgString.equals("OPEN")) { activateRelay = true; } else if (doorOpen && msgString.equals("CLOSE")) { activateRelay = true; } if (activateRelay) { Serial.println("Toggling door state"); digitalWrite(pin_door_relay, LOW); delay(300); digitalWrite(pin_door_relay, HIGH); } }

Home Assistant

Adding the sensors to Home Assistant is very easy. First, ensure you've configured an MQTT broker. Once done you can add the following sensors:

2x MQTT Sensor for temperature and humidity

MQTT Sensor 1x MQTT Binary Sensor for the PIR motion sensor

MQTT Binary Sensor 1x MQTT Cover for the garage door status and control

Your configuration should look something like this:

sensor: - platform: mqtt state_topic: sensor/garage/temperature name: "Garage Temperature" unit_of_measurement: "°C" - platform: mqtt state_topic: sensor/basement/humidity name: "Garage Humidity" unit_of_measurement: "%" binary_sensor: - platform: mqtt state_topic: sensor/garage/motion name: "Garage Motion" cover: - platform: mqtt status_topic: sensor/garage/door-status payload_topic: sensor/garage/door-command name: "Garage Door"

Mounting & Final Steps

I've mounted this multi-sensor in an upper corner of my garage so that the PIR sensor points to the middle of the garage:

The magnetic reed switch is mounted on the door frame, with the magnet on the door, in order to detect the current state of the garage door:

While this is completely functional, I do hope to eventually design a simple PCB I can solder everything onto, and then design & 3D print a plastic enclosure to house the project!