MQTT PIR Motion Sensor with ESP8266 Feather Huzzah in Home Assistant
Because Motion Sensors for Home Automation cost so much money I decided to build one of my own from the prodigious spare parts box in Castle Hagensieker.
BOM
Adafruit Feather Huzzah - $16.95 plus shipping. I chose this because, a) I had one, and b) it has a battery attachment point.
Adafruit PIR Sensor - $9.95
Micro USB charger - You have one already don't you from an old cell phone?
Bear in mind you do NOT need these exact parts. You can go to eBay and buy the cheapest, from China, ESP8266 and PIR module and they will work fine.
Time to wire it up. Quite simple. One gotcha. Feather Huzzah is 3.3v and PIR module generally requires 5v. In my case I didn't need to modify anything but there is a voltage regulator on the PIR module. You tack solder a wire on it and use it instead of the red wire depicted below. I don't think you need it, but if you do you'll have to Google it. There are a dozen or more pages that show you how to do it. Mine just works.
PIR 5v to Feather Huzzah 3v = red
PIR GND to Feather Huzzah GND = black
PIR Signal to Feather Huzzah GPIO12 = yellow
Okay I'm assuming if you have an ESP8266 and Arduino you know how to set it up and ensure you have the proper libraries. If not read and follow this article. I could write the steps but they did it better.
Now make sure you have the following libraries installed:
Adafruit_MQTT.h
Adafruit_MQTT_Client.h"
Use this sketch making sure to fill out your wifi, and MQTT credentials.
/* Basic ESP8266 MQTT PIR sketch */ #include <ESP8266WiFi.h> #include "Adafruit_MQTT.h" #include "Adafruit_MQTT_Client.h" // Update these with values suitable for your network. /************************* WiFi Access Point *********************************/ #define WLAN_SSID "yourwifi" // Wi-Fi network name #define WLAN_PASS "your password" // Wi-Fi password /**************************** MQTT Broker ************************************/ #define AIO_SERVER "192.168.XX.XX" // MQTT broker IP #define AIO_SERVERPORT 1883 // MQTT broker port #define AIO_USERNAME "user" // MQTT username #define AIO_KEY "pass" // MQTT password #define AIO_CID "ESP-PIR-01" // MQTT client ID // Start a counter for serial logging and set the initial value to no motion int counter = 0; int previousReading = LOW; WiFiClient client; // Setup the MQTT client class by passing in the WiFi client and MQTT server and login details. Adafruit_MQTT_Client mqtt(&client, AIO_SERVER, AIO_SERVERPORT, AIO_USERNAME, AIO_KEY, AIO_CID); // Setup publish feeds - define topic name in parenthesis Adafruit_MQTT_Publish status = Adafruit_MQTT_Publish(&mqtt, AIO_CID "/feeds/motion"); Adafruit_MQTT_Publish motion_topic = Adafruit_MQTT_Publish(&mqtt, AIO_CID "/feeds/motion"); long lastMsg = 0; char msg[50]; int value = 0; ///////////////////////////// //VARS //the time we give the sensor to calibrate (10-60 secs according to the datasheet) int calibrationTime = 15; //the time when the sensor outputs a low impulse long unsigned int lowIn; //the amount of milliseconds the sensor has to be low //before we assume all motion has stopped long unsigned int pause = 5000; boolean lockLow = true; boolean takeLowTime; int pirPin = 12; // the digital pin connected to the PIR sensor's output int ledPin = 16; // the digital pin connected to built-in LED void MQTT_connect(); void setup_wifi() { delay(10); // We start by connecting to a WiFi network Serial.println(); Serial.print("Connecting to "); Serial.println(WLAN_SSID); WiFi.begin(WLAN_SSID, WLAN_PASS); while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); } Serial.println(""); Serial.println("WiFi connected"); Serial.println("IP address: "); Serial.println(WiFi.localIP()); } // Setup a MQTT subscription void callback(char* topic, byte* payload, unsigned int length) { Serial.print("Message arrived ["); Serial.print(topic); Serial.print("] "); for (int i = 0; i < length; i++) { Serial.print((char)payload[i]); } Serial.println(); // Switch on the LED if an 1 was received as first character if ((char)payload[0] == '1') { digitalWrite(BUILTIN_LED, HIGH); // Turn the LED on (Note that LOW is the voltage level // but actually the LED is on; this is because // it is active low on the ESP-01) } else { digitalWrite(BUILTIN_LED, LOW); // Turn the LED off by making the voltage HIGH } } void MQTT_connect() { int8_t ret; // Stop if already connected. if (mqtt.connected()) { return; } Serial.print("Connecting to MQTT... "); uint8_t retries = 3; while ((ret = mqtt.connect()) != 0) { // connect will return 0 for connected Serial.println(mqtt.connectErrorString(ret)); Serial.println("Retrying MQTT connection in 5 seconds..."); mqtt.disconnect(); delay(5000); // wait 5 seconds retries--; if (retries == 0) { // basically die and wait for WDT to reset me while (1); } } Serial.println("MQTT Connected!"); status.publish("online"); } void setup() { pinMode(BUILTIN_LED, OUTPUT); // Initialize the BUILTIN_LED pin as an output Serial.begin(115200); setup_wifi(); pinMode(pirPin, INPUT); pinMode(ledPin, OUTPUT); digitalWrite(pirPin, HIGH); //give the sensor some time to calibrate Serial.print("calibrating sensor "); for(int i = 0; i < calibrationTime; i++){ Serial.print("."); delay(500); } Serial.println(" done"); Serial.println("SENSOR ACTIVE"); delay(50); } void loop() { // Ensure the connection to the MQTT server is alive (this will make the first // connection and automatically reconnect when disconnected). See the MQTT_connect // function definition further below. MQTT_connect(); if(digitalRead(pirPin) == HIGH){ digitalWrite(ledPin, LOW); //the led visualizes the sensors output pin state if(lockLow){ motion_topic.publish("ON"); //makes sure we wait for a transition to LOW before any further output is made: lockLow = false; Serial.println("---"); Serial.print("motion detected at "); Serial.print(millis()/1000); Serial.println(" sec"); delay(50); } takeLowTime = true; } if(digitalRead(pirPin) == LOW){ digitalWrite(ledPin, HIGH); //the led visualizes the sensors output pin state if(takeLowTime){ lowIn = millis(); //save the time of the transition from high to LOW takeLowTime = false; //make sure this is only done at the start of a LOW phase } //if the sensor is low for more than the given pause, //we assume that no more motion is going to happen if(!lockLow && millis() - lowIn > pause){ motion_topic.publish("OFF"); //makes sure this block of code is only executed again after //a new motion sequence has been detected lockLow = true; Serial.print("motion ended at "); //output Serial.print((millis() - pause)/1000); Serial.println(" sec"); delay(50); } } }
Now hook up the Feather Huzzah and ensure you select the correct board and port. Then click the upload button which is the right arrow looking one.
Getting close. Once the uploads completes it will connect to your MQTT server and publish the topic "ESP-PIR-01/feeds/motion" You can then use an MQTT client, (I use MQTT.fx) to see if the sensor is reading ON and OFF
If it triggers on and off with motion then you're ready to configure Home Assistant as a binary sensor. Add this to your configuration.yaml file under binary_sensor:
- platform: mqtt
state_topic: "ESP-PIR-01/feeds/motion"
name: PIR Sensor
payload_on: "ON"
payload_off: "OFF"
qos: 0
device_class: motion
Once you restart Home Assistant it should show up as a binary sensor and indicate ON or OFF. Note that my PIR Sensor is the last one on top on the right.
Now that the binary sensor is installed and reads the motion state you can use it to trigger events in automation. For example this is going on the Back Porch. A motion trigger turns on the back porch light (which is actually a Z-Wave GE switch on the inside of the home). The following automations allow for this. And automation 7 stops the lights from coming on during daylight hours. Pretty smart, huh?
automation 7: alias: Back Porch Light On trigger: - platform: state entity_id: binary_sensor.pir_sensor to: 'on' condition: condition: and conditions: - condition: state entity_id: binary_sensor.pir_sensor state: 'on' - condition: time after: '19:00:00' before: '07:00:00' weekday: - mon - tue - wed - thu - fri - sat - sun action: - service: switch.turn_on data: entity_id: switch.ge_12722_onoff_relay_switch_switch_8_0 automation 8: alias: Back Porch Light Off trigger: - platform: state entity_id: binary_sensor.pir_sensor to: 'off' for: minutes: 1 action: - service: switch.turn_off data: entity_id: switch.ge_12722_onoff_relay_switch_switch_8_0
Enjoy.