John's Musings

Yeah, Me Neither

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.


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:




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.

// 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;

//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() {

  // We start by connecting to a WiFi network
  Serial.print("Connecting to ");

  while (WiFi.status() != WL_CONNECTED) {

  Serial.println("WiFi connected");
  Serial.println("IP address: ");

  // Setup a MQTT subscription
void callback(char* topic, byte* payload, unsigned int length) {
  Serial.print("Message arrived [");
  Serial.print("] ");
  for (int i = 0; i < length; i++) {

  // 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()) {

  Serial.print("Connecting to MQTT... ");

  uint8_t retries = 3;
  while ((ret = mqtt.connect()) != 0) { // connect will return 0 for connected
       Serial.println("Retrying MQTT connection in 5 seconds...");
       delay(5000);  // wait 5 seconds
       if (retries == 0) {
         // basically die and wait for WDT to reset me
         while (1);
  Serial.println("MQTT Connected!");

void setup() {
  pinMode(BUILTIN_LED, OUTPUT);     // Initialize the BUILTIN_LED pin as an output
  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.println(" done");
  Serial.println("SENSOR ACTIVE");


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.

     if(digitalRead(pirPin) == HIGH){
       digitalWrite(ledPin, LOW);   //the led visualizes the sensors output pin state
         //makes sure we wait for a transition to LOW before any further output is made:
         lockLow = false;            
         Serial.print("motion detected at ");
         Serial.println(" sec"); 
         takeLowTime = true;

     if(digitalRead(pirPin) == LOW){       
       digitalWrite(ledPin, HIGH);  //the led visualizes the sensors output pin state

        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){  
           //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");


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


    - platform: state

      entity_id: binary_sensor.pir_sensor

      to: 'on'


    condition: and


      - condition: state

        entity_id: binary_sensor.pir_sensor

        state: 'on'

      - condition: time

        after: '19:00:00'

        before: '07:00:00'


          - mon

          - tue

          - wed

          - thu

          - fri

          - sat

          - sun


    - service: switch.turn_on


        entity_id: switch.ge_12722_onoff_relay_switch_switch_8_0

automation 8:

  alias: Back Porch Light Off


    - platform: state

      entity_id: binary_sensor.pir_sensor

      to: 'off'


        minutes: 1


    - service: switch.turn_off


        entity_id: switch.ge_12722_onoff_relay_switch_switch_8_0