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)
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!
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
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); 
 } 
} 
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
- 1x MQTT Binary Sensor for the PIR motion 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"
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!












Comments