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);
}
}
Home AssistantAdding 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"
Mounting & Final StepsI'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